[
  {
    "path": ".gitattributes",
    "content": "# Disable NL -> CRNL translation on Windows. This is necessary because the files on disk must\n# match the checksums in metadata.json.\nspec/fixtures/integration/application/module/environments/direnv/modules/nginx/README -text\nspec/fixtures/integration/application/module/environments/direnv/modules/nginx/metadata.json -text\nspec/fixtures/integration/application/module/environments/direnv/modules/nginx/Modulefile -text\nspec/fixtures/integration/application/module/environments/direnv/modules/nginx/manifests/init.pp -text\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  categories:\n    - title: Features & Enhancements\n      labels:\n        - enhancement\n    - title: Bug Fixes\n      labels:\n        - bug\n"
  },
  {
    "path": ".github/workflows/backport.yml",
    "content": "name: Backport merged pull request\non:\n  pull_request_target:\n    types: [labeled]\npermissions:\n  contents: write      # so it can comment\n  pull-requests: write # so it can create pull requests\njobs:\n  backport:\n    name: Backport merged pull request\n    runs-on: ubuntu-latest\n    # For security reasons, we don't want to checkout and run arbitrary code when\n    # using the pull_request_target trigger. So restrict this to cases where the\n    # backport label is applied to an already merged PR.\n    if: github.event.pull_request.merged && contains(github.event.label.name, 'backport')\n    steps:\n      - uses: actions/checkout@v4\n      - name: Create backport pull requests\n        uses: korthout/backport-action@v1\n"
  },
  {
    "path": ".github/workflows/checks.yaml",
    "content": "---\nname: Checks\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\npermissions:\n  contents: read\n\njobs:\n  checks:\n    name: ${{ matrix.cfg.check }}\n    strategy:\n      matrix:\n        cfg:\n          - {check: rubocop, os: ubuntu-latest, ruby: '3.1'}\n          - {check: warnings, os: ubuntu-latest, ruby: '3.1'}\n\n    runs-on: ${{ matrix.cfg.os }}\n    steps:\n      - name: Checkout current PR\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Install ruby version ${{ matrix.cfg.ruby }}\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.cfg.ruby }}\n\n      - name: Update rubygems and install gems\n        run: |\n          gem update --system --silent --no-document\n          bundle config set without packaging documentation\n          bundle install --jobs 4 --retry 3\n\n      - name: Run ${{ matrix.cfg.check }} check\n        run: bundle exec rake ${{ matrix.cfg.check }}\n"
  },
  {
    "path": ".github/workflows/jira.yml",
    "content": "---\nname: Export issue to Jira\n\non:\n  issues:\n    types: [labeled]\n\npermissions:\n  issues: write\n\njobs:\n  export:\n    uses: \"puppetlabs/phoenix-github-actions/.github/workflows/jira.yml@main\"\n    with:\n      jira-project: PUP\n      jira-base-url: ${{ vars.jira_base_url }}\n      jira-user-email: ${{ vars.jira_user_email }}\n    secrets:\n      jira-api-token: ${{ secrets.JIRA_ISSUES_ACTION }}\n"
  },
  {
    "path": ".github/workflows/mend.yaml",
    "content": "---\nname: Mend Monitor\non:\n  push:\n    branches:\n      - main\njobs:\n  mend_monitor:\n    if: ${{ github.repository_owner == 'puppetlabs' }}\n    runs-on: ubuntu-latest\n    name: Mend Monitor\n    steps:\n      - name: Checkout current PR\n        uses: actions/checkout@v4\n      - name: Setup Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: 3.1\n      - name: Create lock\n        run: bundle lock\n      - uses: actions/setup-java@v3\n        with:\n          distribution: 'temurin'\n          java-version: '17'\n      - name: Download Mend\n        run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar\n      - name: Run Mend\n        run: java -jar wss-unified-agent.jar\n        env:\n          WS_APIKEY: ${{ secrets.MEND_API_KEY }}\n          WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent\n          WS_USERKEY: ${{ secrets.MEND_TOKEN }}\n          WS_PRODUCTNAME: Puppet Agent\n          WS_PROJECTNAME: ${{ github.event.repository.name }}\n"
  },
  {
    "path": ".github/workflows/rspec_tests.yaml",
    "content": "---\nname: RSpec tests\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\npermissions:\n  contents: read\n\njobs:\n  rspec_tests:\n    name: ${{ matrix.cfg.os }}(ruby ${{ matrix.cfg.ruby }})\n    strategy:\n      matrix:\n        cfg:\n          - {os: ubuntu-latest, ruby: '3.1'}\n          - {os: ubuntu-20.04, ruby: '3.2'} # openssl 1.1.1\n          - {os: ubuntu-22.04, ruby: '3.2'} # openssl 3\n          - {os: ubuntu-22.04, ruby: '3.3'} # openssl 3 / latest Ruby\n          - {os: ubuntu-latest, ruby: 'jruby-9.4.3.0'}\n          - {os: windows-2019, ruby: '3.1'}\n          - {os: windows-2019, ruby: '3.2.5'} # openssl 3 & temporarily pinned to ruby 3.2.5, see PA-7108\n          - {os: windows-2019, ruby: '3.3'} # openssl 3 / latest Ruby\n\n    runs-on: ${{ matrix.cfg.os }}\n    env:\n      BUNDLE_SET: \"without packaging documentation\"\n    steps:\n      - name: Checkout current PR\n        uses: actions/checkout@v4\n\n      - name: Install ruby version ${{ matrix.cfg.ruby }}\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.cfg.ruby }}\n          bundler-cache: true\n\n      - name: Run tests on Windows\n        if: runner.os == 'Windows'\n        run: |\n          # https://github.com/ruby/ruby/pull/2791/files#diff-ff5ff976e81bebd977f0834e60416abbR97-R100\n          # Actions uses UTF8, causes test failures, similar to normal OS setup\n          $PSDefaultParameterValues['*:Encoding'] = 'utf8'\n          [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(\"IBM437\")\n          [Console]::InputEncoding  = [System.Text.Encoding]::GetEncoding(\"IBM437\")\n          $Env:LOG_SPEC_ORDER = 'true'\n\n          # debug information\n          chcp\n          Get-WinSystemLocale\n          Get-ChildItem Env: | % { Write-Output \"$($_.Key): $($_.Value)\"  }\n          # list current OpenSSL install\n          gem list openssl\n          ruby -ropenssl -e 'puts \"OpenSSL Version - #{OpenSSL::OPENSSL_VERSION}\"; puts \"OpenSSL Library Version - #{OpenSSL::OPENSSL_LIBRARY_VERSION}\"'\n          Get-Content Gemfile.lock\n          ruby -v\n          gem --version\n          bundle --version\n\n          # Run tests\n          bundle exec rake parallel:spec[2]\n\n      - name: Run tests on Linux\n        if: runner.os == 'Linux'\n        run: |\n          # debug information\n          gem list openssl\n          ruby -ropenssl -e 'puts \"OpenSSL Version - #{OpenSSL::OPENSSL_VERSION}\"; puts \"OpenSSL Library Version - #{OpenSSL::OPENSSL_LIBRARY_VERSION}\"'\n          cat Gemfile.lock\n          ruby -v\n          gem --version\n          bundle --version\n\n          if [[ ${{ matrix.cfg.ruby }} =~ \"jruby\" ]]; then\n            export _JAVA_OPTIONS='-Xmx1024m -Xms512m'\n\n            # workaround for PUP-10683\n            sudo apt remove rpm\n          fi\n\n          bundle exec rake parallel:spec[2]\n"
  },
  {
    "path": ".gitignore",
    "content": ".rspec\nresults\ntags\n.*.sw[op]\ntest.pp\n# YARD generated documentation\n.yardoc\n/doc\n# Now that there is a gemfile, RVM ignores rvmrc in parent directories, so a local one is required\n# to work around that.  Which we don't want committed, so we can ignore it here.\n/.rvmrc\n.bundle/\n.byebug_history\n/ext/packaging/\n/pkg/\nGemfile.lock\nGemfile.local\nGuardfile\npuppet-acceptance/\n/.project\n.ruby-version\n.ruby-gemset\n/acceptance/junit\n/acceptance/log\n/acceptance/.beaker\n# emacs backup files\n*~\n/*.samples\ncoverage/\n# Files and directory added by RubyMine IDE\n*.iml\n.rakeTasks\n.idea/\nspec_order.txt\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"benchmarks/full_catalog/puppetlabs-puppetserver_perf_control\"]\n\tpath = benchmarks/full_catalog/puppetlabs-puppetserver_perf_control\n\turl = https://github.com/puppetlabs/puppetlabs-puppetserver_perf_control\n"
  },
  {
    "path": ".mailmap",
    "content": "Adrien Thebo <adrien@puppetlabs.com> Adrien Thebo <adrien.thebo@gmail.com>\nAdrien Thebo <adrien@puppetlabs.com> adrienthebo <adrien@puppetlabs.com>\nAllen Ballman <aballman@gmail.com> ballman <ballman@980ebf18-57e1-0310-9a29-db15c13687c0>\nAndrew Shafer <andrew@puppetlabs.com> Andrew Shafer <andrew@luke-kaniess-macbook-pro-15.local>\nAndrew Shafer <andrew@puppetlabs.com> Andrew Shafer <andrew@reductivelabs.com>\nAndrew Shafer <andrew@puppetlabs.com> shafer <shafer@980ebf18-57e1-0310-9a29-db15c13687c0>\nAnselm Strauss <amsibamsi@gmail.com> Anselm Strauss <anselm@beastie.ttyv0.net>\nAnselm Strauss <amsibamsi@gmail.com> Anselm Strauss <anselm@silversurfer.l.ttyv0.net>\nBen Hengst <notbenh@cpan.org> ben hengst <notbenh@cpan.org>\nBen Hughes <ben@puppetlabs.com> Ben H <git@mumble.org.uk>\nBen Hughes <ben@puppetlabs.com> Ben Hughes <git@mumble.org.uk>\nBlake Barnett <blake@soulmachine.net> shadoi <shadoi@980ebf18-57e1-0310-9a29-db15c13687c0>\nBrice Figureau <brice-puppet@daysofwonder.com> Brice Figureau <brice@daysofwonder.com>\nBryan Kearney <bkearney@redhat.com> Bryan Kearney <bkearney@localhost.localdomain>\nCameron Thomas <cameron@puppetlabs.com> Cameron Thomas <cs.thomas.dev@gmail.com>\nCarl Caum <carl@puppetlabs.com> Carl Caum <carl@carlcaum.com>\nChris Price <chris@puppetlabs.com> Chris Price <cprice@explosivo.(none)>\nChris Price <chris@puppetlabs.com> cprice\nChristian Hofstaedtler <hofstaedtler@inqnet.at> Christian Hofstaedtler <ch+git@zeha.at>\nDan Bode <dan@puppetlabs.com> Dan Bode <bodepd@gmail.com>\nDan Bode <dan@puppetlabs.com> Dan Bode <dan@bodepd.com>\nDan Bode <dan@puppetlabs.com> Dan Bode <dan@reductivelabs.com>\nDaniel Pittman <daniel@puppetlabs.com> Daniel Pittman <daniel@rimspace.net>\nDavid Lutterkort <lutter@redhat.com> David Lutterkort <dlutter@redhat.com>\nDavid Lutterkort <lutter@redhat.com> lutter <lutter@1f5c1d6a-bddf-0310-8f58-fc49e503516a>\nDavid Lutterkort <lutter@redhat.com> lutter <lutter@980ebf18-57e1-0310-9a29-db15c13687c0>\nDavid Schmitt <david@dasz.at> David Schmitt <david@schmitt.edv-bus.at>\nDeepak Giridharagopal <deepak@puppetlabs.com> Deepak Giridharagopal <deepak@brownman.org>\nDominic Maraglia <dominic@puppetlabs.com> <dmaraglia@gmail.com>\nDonavan Miller <donavan@desinc.net> donavan <donavan@desinc.net>\nEric Sorenson <eric.sorenson@puppetlabs.com> Eric Sorenson <ahpook@gmail.com>\nEric Sorenson <eric.sorenson@puppetlabs.com> Eric Sorenson <eric@explosive.net>\nEric Sorenson <eric.sorenson@puppetlabs.com> Eric Sorenson <eric@leterel.apple.com>\nFrancois Deppierraz <francois@ctrlaltdel.ch> Francois Deppierraz <francois@slayer.ctrlaltdel.ch>\nGarrett Honeycutt <garrett@puppetlabs.com> Garrett Honeycutt <puppet-dev@garretthoneycutt.com>\nGary Larizza <gary@puppetlabs.com> Gary Larizza <ccshots@gmail.com>\nGreg Sutcliffe <greg.sutcliffe@gmail.com> Greg Sutcliffe <gsutcliffe@gmail.com>\nJames Turnbull <james@puppetlabs.com> James Turnbull <james@lovedthanlost.net>\nJames Turnbull <james@puppetlabs.com> James Turnbull <james@rhizome-lovedthanlost-net.local>\nJames Turnbull <james@puppetlabs.com> James Turnbull <james@ubuntu904.lovedthanlost.net>\nJames Turnbull <james@puppetlabs.com> root <root@absinthe.lovedthanlost.net>\nJeff McCune <jeff@puppetlabs.com> <jeff.mccune@northstarlabs.net>\nJeff McCune <jeff@puppetlabs.com> Jeffrey J McCune <jeff@northstarlabs.net>\nJeff McCune <jeff@puppetlabs.com> Jeffrey McCune <mccune.jeff@gmail.com>\nJeff McCune <jeff@puppetlabs.com> mccune <mccune@1f5c1d6a-bddf-0310-8f58-fc49e503516a>\nJeff McCune <jeff@puppetlabs.com> mccune <mccune@980ebf18-57e1-0310-9a29-db15c13687c0>\nJeff Weiss <jeff.weiss@puppetlabs.com> Jeff Weiss <jeff.a.weiss@gmail.com>\nJesse Wolfe <jesse@puppetlabs.com> Jesse Wolfe <jes5199@gmail.com>\nJos Boumans <jos@dwim.org> josb <josb@980ebf18-57e1-0310-9a29-db15c13687c0>\nJosh Cooper <josh@puppetlabs.com> Josh Cooper <josh+github@puppetlabs.com>\nJosh Cooper <josh@puppetlabs.com> joshcooper <josh+github@puppetlabs.com>\nJoshua Harlan Lifton <lifton@puppetlabs.com> lifton <lifton@puppetlabs.com>\nJustin Stoller <justin@puppetlabs.com> Justin Stoller <justin.stoller@gmail.com>\nKelsey Hightower <kelsey@puppetlabs.com> Kelsey Hightower <kelsey.hightower@gmail.com>\nKelsey Hightower <kelsey@puppetlabs.com> Kelsey Hightower <kelsey.hightower@puppetlabs.com>\nKen Barber <ken@puppetlabs.com> Ken Barber <ken@bob.sh>\nLuke Kanies <luke@puppetlabs.com> <luke@980ebf18-57e1-0310-9a29-db15c13687c0>\nLuke Kanies <luke@puppetlabs.com> Luke Kaines <luke@puppetlabs.com>\nLuke Kanies <luke@puppetlabs.com> Luke Kanies <luke@madstop.com>\nLuke Kanies <luke@puppetlabs.com> Luke Kanies <luke@reductivelabs.com>\nLuke Kanies <luke@puppetlabs.com> luke <luke@1f5c1d6a-bddf-0310-8f58-fc49e503516a>\nMarkus Roberts <markus@puppetlabs.com> Markus Roberts <Markus@reality.com>\nMarkus Roberts <markus@puppetlabs.com> Markus Roberts <markus@phage.local>\nMarkus Roberts <markus@puppetlabs.com> markus <markus@AVA-351181.(none)>\nMartin Englund <martin@englund.nu> Martin Englund <martin.englund@sun.com>\nMatt Palmer <mpalmer@hezmatt.org> mpalmer <mpalmer@980ebf18-57e1-0310-9a29-db15c13687c0>\nMatt Robinson <matt@puppetlabs.com> Matt Robinson <mattr@mattrobinson.net>\nMatthaus Owens <matthaus@puppetlabs.com> Matthaus Litteken <matthaus@puppetlabs.com>\nMatthaus Owens <matthaus@puppetlabs.com> Matthaus Litteken <mlitteken@gmail.com>\nMatthaus Owens <matthaus@puppetlabs.com> Matthaus Owens <mlitteken@gmail.com>\nMichael Stahnke <stahnma@puppetlabs.com> stahnma <stahnma@websages.com>\nMoses Mendoza <moses@puppetlabs.com> MosesMendoza <mendoza.moses@gmail.com>\nNan Liu <nan@puppetlabs.com> Nan Liu <nan.liu@gmail.com>\nNick Fagerlund <nick.fagerlund@puppetlabs.com> nfagerlund <nick.fagerlund@gmail.com>\nNick Fagerlund <nick.fagerlund@puppetlabs.com> nfagerlund <nick.fagerlund@puppetlabs.com>\nNigel Kersten <nigel@puppetlabs.com> Nigel Kersten <nigel@explanatorygap.net>\nNigel Kersten <nigel@puppetlabs.com> Nigel Kersten <nigelk@google.com>\nOhad Levy <ohadlevy@gmail.com> Ohad Levy <ohad.levy@infineon.com>\nPatrick Carlisle <patrick@puppetlabs.com> Patrick <patrick@puppetlabs.com>\nPatrick Carlisle <patrick@puppetlabs.com> pcarlisle <patrick@puppetlabs.com>\nPaul Lathrop <plathrop@digg.com> Paul Lathrop <paul@tertiusfamily.net>\nPaul Lathrop <plathrop@digg.com> Paul Lathrop <plathrop@debian.tertiusfamily.net>\nPeter Meier <peter.meier@immerda.ch> duritong <peter.meier@immerda.ch>\nPeter Meier <peter.meier@immerda.ch> mh <duritong@cronopios.org>\nPeter Mørch <peter@morch.com> peter <peter@morch.com>\nPieter van de Bruggen <pieter@puppetlabs.com> Pieter van de Bruggen <pvande@gmail.com>\nRahul Gopinath <rahul@puppetlabs.com> rahul <rahul@pe-solaris11-vm.(none)>\nRahul Gopinath <rahul@puppetlabs.com> rahul <rahul@puppetlabs.com>\nRahul Gopinath <rahul@puppetlabs.com> rahul <rahul@solaris11-master.(none)>\nRahul Gopinath <rahul@puppetlabs.com> vrthra <9@vrtra.net>\nRein Henrichs <rein@puppetlabs.com> Rein Henrichs <reinh@reinh.com>\nRudy Gevaert <rudy@webworm.org> rgevaert <rudy+github@webworm.org>\nRudy Gevaert <rudy@webworm.org> rgevaert <rudy.gevaert+github@ugent.be>\nRuss Allbery <rra@stanford.edu> Russ Allbery <rra@debian.org>\nSean E. Millichamp <sean@bruenor.org> Sean Millichamp <sean.millichamp@secure-24.com>\nSean E. Millichamp <sean@bruenor.org> Sean Millichamp <sean@bruenor.org>\nSteve McIintosh <stevemac@endpoint.com> steve mcintosh <stevemac@endpoint.com>\nThom May <thom@joost.com> Thom May <thom@clearairturbulence.org>\nThom May <thom@joost.com> Thom May <thom@virelais.nyc.joostas.com>\n"
  },
  {
    "path": ".noexec.yaml",
    "content": "--- \nexclude:\n - gem\n - rake\n - rspec\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "inherit_from: .rubocop_todo.yml\n\nrequire:\n  - rubocop-i18n\n  - rubocop-performance\n  - rubocop-rake\n  - rubocop-rspec\n\nAllCops:\n  TargetRubyVersion: 3.1\n  Include:\n    - 'lib/**/*.rb'\n    - 'ext/**/*.rb'\n  Exclude:\n    - '**/*.erb'\n    - 'acceptance/**/*'\n    - 'spec/**/*'\n    - 'tasks/**/*'\n    - 'ext/suse/puppet.spec'\n    - 'lib/puppet/vendor/**/*'\n    - 'lib/puppet/pops/model/ast.rb'\n    - 'lib/puppet/pops/parser/eparser.rb'\n\n# The formatting of defaults is unusual, so let's skip layout cops.\nLayout:\n  Exclude:\n    - 'lib/puppet/defaults.rb'\n\n# We don't mind when module and class keywords are aligned.\nLayout/IndentationWidth:\n  AllowedPatterns: ['^\\s*module']\n\nLayout/LineEndStringConcatenationIndentation:\n  Enabled: true\n\n# Enabling this cop will remove raising RuntimeErrors and cause spec test\n# failures\nStyle/RedundantException:\n  Exclude:\n    - 'lib/puppet/file_bucket/dipper.rb'\n    - 'lib/puppet/forge.rb'\n    - 'lib/puppet/module_tool/applications/unpacker.rb'\n    - 'lib/puppet/module_tool/local_tarball.rb'\n    - 'lib/puppet/module_tool/tar.rb'\n    - 'lib/puppet/pops/types/class_loader.rb'\n\n# Enabling this cop causes a plethora of failed rspec tests, mostly\n# Errno::ENAMETOOLONG and Puppet::Context::UndefinedBindingError: Unable to\n# lookup 'environments' errors\nStyle/RedundantSelfAssignment:\n  Exclude:\n    - 'lib/puppet/context.rb'\n\n# Enabling this cop causes failures in rb_tree_map_spec relating to important\n# function slike being unable to delete nodes and returning nil when the key\n# cannot be found\nStyle/PreferredHashMethods:\n  Enabled: false\n\n# Explicitly enables this cop new in 1.7\nLayout/SpaceBeforeBrackets:\n  Enabled: true\n\n# puppet uses symbol booleans in types and providers to work around long standing\n# bugs when trying to manage falsey pararameters and properties\nLint/BooleanSymbol:\n  Enabled: true\n  Exclude:\n    - 'lib/puppet/type.rb'\n    - 'lib/puppet/type/**/*.rb'\n    - 'lib/puppet/provider/**/*.rb'\n    - 'lib/puppet/reference/providers.rb'\n    - 'lib/puppet/parameter/value.rb'\n\nMetrics/AbcSize:\n  Enabled: false\n\nMetrics/BlockLength:\n  Enabled: false\n\nMetrics/BlockNesting:\n  Enabled: false\n\nMetrics/ClassLength:\n  Enabled: false\n\nMetrics/CyclomaticComplexity:\n  Enabled: false\n\nMetrics/MethodLength:\n  Enabled: false\n\nMetrics/ModuleLength:\n  Enabled: false\n\nMetrics/ParameterLists:\n  Enabled: false\n\nMetrics/PerceivedComplexity:\n  Enabled: false\n\nNaming/AccessorMethodName:\n  Enabled: false\n\nNaming/BinaryOperatorParameterName:\n  Enabled: false\n\nNaming/BlockParameterName:\n  Exclude:\n    - 'lib/puppet/util/windows/daemon.rb'\n    - 'lib/puppet/util/windows/user.rb'\n\nNaming/ClassAndModuleCamelCase:\n  Exclude:\n    - 'lib/puppet/ffi/windows/structs.rb'\n    - 'lib/puppet/pops/validation/checker4_0.rb'\n    - 'lib/puppet/pops/validation/validator_factory_4_0.rb'\n    - 'lib/puppet/util/windows/root_certs.rb'\n    - 'lib/puppet/util/windows/security.rb'\n    - 'lib/puppet/util/windows/user.rb'\n\nNaming/ConstantName:\n  Exclude:\n    - 'lib/puppet/graph/relationship_graph.rb'\n    - 'lib/puppet/indirector/hiera.rb'\n    - 'lib/puppet/provider/package/sun.rb'\n    - 'lib/puppet/resource/type.rb'\n    - 'lib/puppet/type/schedule.rb'\n    - 'lib/puppet/type/tidy.rb'\n    - 'lib/puppet/util.rb'\n    - 'lib/puppet/util/colors.rb'\n    - 'lib/puppet/util/execution.rb'\n    - 'lib/puppet/util/symbolic_file_mode.rb'\n    - 'lib/puppet/util/tagging.rb'\n    - 'lib/puppet/util/windows/adsi.rb'\n    - 'lib/puppet/util/windows/sid.rb'\n    - 'lib/puppet/util/yaml.rb'\n\nNaming/HeredocDelimiterNaming:\n  Enabled: false\n\n# Exclude existing violations to avoid breaking changes\nNaming/MemoizedInstanceVariableName:\n  Exclude:\n    - 'lib/puppet/module_tool/applications/installer.rb'\n    - 'lib/puppet/pops/types/type_factory.rb'\n    - 'lib/puppet/provider/package/portage.rb'\n    - 'lib/puppet/resource.rb'\n\nNaming/MethodName:\n  Exclude:\n    - 'lib/puppet/functions/**/*'\n    - 'lib/puppet/parser/ast/pops_bridge.rb'\n    - 'lib/puppet/pops/**/*'\n    - 'lib/puppet/util/windows/**/*'\n\nNaming/MethodParameterName:\n  Enabled: false\n\nNaming/PredicateName:\n  ForbiddenPrefixes: []\n\nNaming/RescuedExceptionsVariableName:\n  Enabled: false\n\nNaming/VariableName:\n  Exclude:\n    - 'ext/windows/service/daemon.rb'\n    - 'lib/puppet/agent.rb'\n    - 'lib/puppet/application/describe.rb'\n    - 'lib/puppet/pops/lookup/hiera_config.rb'\n    - 'lib/puppet/pops/validation/checker4_0.rb'\n    - 'lib/puppet/provider/package/pip.rb'\n    - 'lib/puppet/provider/package/windows/exe_package.rb'\n    - 'lib/puppet/provider/package/windows/msi_package.rb'\n    - 'lib/puppet/ssl/ssl_provider.rb'\n    - 'lib/puppet/util/windows/adsi.rb'\n    - 'lib/puppet/util/windows/daemon.rb'\n    - 'lib/puppet/util/windows/error.rb'\n    - 'lib/puppet/util/windows/user.rb'\n\nNaming/VariableNumber:\n  Enabled: false\n\nPerformance/AncestorsInclude: # new in 1.7\n  Enabled: true\n\nPerformance/BigDecimalWithNumericArgument: # new in 1.7\n  Enabled: true\n\nPerformance/ConcurrentMonotonicTime: # new in 1.12\n  Enabled: true\n\nPerformance/MapCompact: # new in 1.11\n  Enabled: true\n\nPerformance/RedundantSortBlock: # new in 1.7\n  Enabled: true\n\nPerformance/ReverseFirst: # new in 1.7\n  Enabled: true\n\nRSpec/BeEq: # new in 2.9.0\n  Enabled: true\n\nRSpec/BeNil: # new in 2.9.0\n  Enabled: true\n\nRSpec/ExcessiveDocstringSpacing: # new in 2.5\n  Enabled: true\n\nRSpec/IdenticalEqualityAssertion: # new in 2.4\n  Enabled: true\n\nRSpec/SubjectDeclaration: # new in 2.5\n  Enabled: true\n\nRSpec/VerifiedDoubleReference: # new in 2.10.0\n  Enabled: true\n\nRSpec/FactoryBot/SyntaxMethods: # new in 2.7\n  Enabled: true\n\nRSpec/Rails/AvoidSetupHook: # new in 2.4\n  Enabled: true\n\nStyle/AutoResourceCleanup:\n  Enabled: true\n\nStyle/CaseEquality:\n  Enabled: true\n\nStyle/CaseLikeIf:\n  Enabled: true\n\nStyle/ClassCheck:\n  Enabled: true\n\nStyle/ClassEqualityComparison:\n  Enabled: true\n\nStyle/ClassMethods:\n  Enabled: true\n\nStyle/CollectionCompact:\n  Enabled: true\n\nStyle/ColonMethodCall:\n  Enabled: true\n\nStyle/CombinableLoops:\n  Enabled: true\n  Exclude:\n    - 'lib/puppet/graph/relationship_graph.rb'\n    - 'lib/puppet/pops/types/ruby_generator.rb'\n    - 'lib/puppet/provider/service/init.rb'\n    - 'lib/puppet/util/rdoc/generators/puppet_generator.rb'\n\nStyle/ColonMethodDefinition:\n  Enabled: true\n\nStyle/DefWithParentheses:\n  Enabled: true\n\nStyle/Dir:\n  Enabled: true\n\nStyle/DocumentDynamicEvalDefinition:\n  Enabled: true\n\nStyle/DoubleCopDisableDirective:\n  Enabled: true\n\nStyle/EachForSimpleLoop:\n  Enabled: true\n\nStyle/EachWithObject:\n  Enabled: true\n\nStyle/EmptyBlockParameter:\n  Enabled: true\n\nStyle/EmptyCaseCondition:\n  Enabled: true\n\nStyle/EmptyLambdaParameter:\n  Enabled: true\n\nStyle/EmptyLiteral:\n  Enabled: true\n\nStyle/EvalWithLocation:\n  Enabled: true\n\nStyle/EvenOdd:\n  Enabled: true\n\nStyle/ExpandPathArguments:\n  Enabled: true\n\nStyle/FetchEnvVar:\n  Enabled: true\n\nStyle/FileRead:\n  Enabled: true\n\nStyle/FileWrite:\n  Enabled: true\n\nStyle/FloatDivision:\n  Enabled: true\n\nStyle/For:\n  Enabled: true\n\nStyle/FrozenStringLiteralComment:\n  Enabled: true\n\nStyle/GlobalStdStream:\n  Enabled: true\n\nStyle/GlobalVars:\n  Enabled: true\n\nStyle/HashAsLastArrayItem:\n  Enabled: true\n\nStyle/HashConversion:\n  Enabled: true\n\n# HashEachMethods does not guarantee the receiver is a Hash, so\n# this will not work since numerous puppet classes define #value\n# not #each_value\nStyle/HashEachMethods:\n  Enabled: false\n\nStyle/HashLikeCase:\n  Enabled: true\n  Exclude:\n    - 'lib/puppet/util/command_line/trollop.rb'\n\nStyle/HashTransformKeys:\n  Enabled: true\n\nStyle/HashTransformValues:\n  Enabled: true\n\nStyle/IdenticalConditionalBranches:\n  Enabled: true\n\nStyle/IfInsideElse:\n  Enabled: true\n\nStyle/IfUnlessModifierOfIfUnless:\n  Enabled: true\n\nStyle/IfWithBooleanLiteralBranches:\n  Enabled: true\n\nStyle/IfWithSemicolon:\n  Enabled: true\n\nStyle/InfiniteLoop:\n  Enabled: true\n\nStyle/InverseMethods:\n  Enabled: true\n\nStyle/KeywordParametersOrder:\n  Enabled: true\n\nStyle/Lambda:\n  Enabled: true\n\nStyle/LambdaCall:\n  Enabled: true\n\nStyle/LineEndConcatenation:\n  Enabled: true\n\nStyle/MapCompactWithConditionalBlock:\n  Enabled: true\n\nStyle/MapToHash:\n  Enabled: true\n\nStyle/MapToSet:\n  Enabled: true\n"
  },
  {
    "path": ".rubocop_todo.yml",
    "content": "# This configuration was generated by\n# `rubocop --auto-gen-config --no-offense-counts --no-auto-gen-timestamp`\n# using RuboCop version 1.28.0.\n# The point is for the user to remove these configuration records\n# one by one as the offenses are removed from the code base.\n# Note that changes in the inspected code, or installation of new\n# versions of RuboCop, may require this file to be generated again.\n\n# This cop supports safe auto-correction (--auto-correct).\nI18n/GetText/DecorateFunctionMessage:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nI18n/GetText/DecorateString:\n  Enabled: false\n\nI18n/GetText/DecorateStringFormattingUsingPercent:\n  Exclude:\n    - 'lib/puppet/provider/user/windows_adsi.rb'\n    - 'lib/puppet/transaction/resource_harness.rb'\n\nI18n/RailsI18n/DecorateString:\n  Enabled: false\n\nLint/AmbiguousAssignment: # new in 1.7\n  Enabled: false\n\nLint/AmbiguousOperatorPrecedence: # new in 1.21\n  Enabled: false\n\nLint/AmbiguousRange: # new in 1.19\n  Enabled: false\n\n# Configuration parameters: AllowedMethods.\n# AllowedMethods: enums\nLint/ConstantDefinitionInBlock:\n  Exclude:\n    - 'lib/puppet/face/config.rb'\n    - 'lib/puppet/face/help.rb'\n    - 'lib/puppet/face/node/clean.rb'\n    - 'lib/puppet/provider/package/aix.rb'\n    - 'lib/puppet/provider/package/apt.rb'\n    - 'lib/puppet/provider/package/gem.rb'\n    - 'lib/puppet/provider/package/pip.rb'\n    - 'lib/puppet/provider/package/yum.rb'\n    - 'lib/puppet/provider/service/upstart.rb'\n    - 'lib/puppet/provider/user/directoryservice.rb'\n    - 'lib/puppet/type/file.rb'\n    - 'lib/puppet/type/file/source.rb'\n    - 'lib/puppet/type/resources.rb'\n    - 'lib/puppet/type/schedule.rb'\n    - 'lib/puppet/type/tidy.rb'\n\nLint/ConstantOverwrittenInRescue: # new in 1.31\n  Enabled: false\n\nLint/DeprecatedConstants: # new in 1.8\n  Enabled: false\n\nLint/DuplicateBranch: # new in 1.3\n  Enabled: false\n\nLint/DuplicateMagicComment: # new in 1.37\n  Enabled: false\n\nLint/DuplicateMatchPattern: # new in 1.50\n  Enabled: false\n\nLint/DuplicateRegexpCharacterClassElement: # new in 1.1\n  Enabled: false\n\nLint/EmptyBlock: # new in 1.1\n  Enabled: false\n\nLint/EmptyClass: # new in 1.3\n  Enabled: false\n\nLint/EmptyInPattern: # new in 1.16\n  Enabled: false\n\nLint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21\n  Enabled: false\n\nLint/ItWithoutArgumentsInBlock: # new in 1.59\n  Enabled: false\n\nLint/LambdaWithoutLiteralBlock: # new in 1.8\n  Enabled: false\n\nLint/LiteralAssignmentInCondition: # new in 1.58\n  Enabled: false\n\nLint/MissingSuper:\n  Enabled: false\n\nLint/MixedCaseRange: # new in 1.53\n  Enabled: false\n\nLint/NestedMethodDefinition:\n  Exclude:\n    - 'lib/puppet/pops/types/p_binary_type.rb'\n    - 'lib/puppet/pops/types/p_init_type.rb'\n    - 'lib/puppet/pops/types/p_object_type.rb'\n    - 'lib/puppet/pops/types/p_sem_ver_range_type.rb'\n    - 'lib/puppet/pops/types/p_sem_ver_type.rb'\n    - 'lib/puppet/pops/types/p_sensitive_type.rb'\n    - 'lib/puppet/pops/types/p_timespan_type.rb'\n    - 'lib/puppet/pops/types/p_timestamp_type.rb'\n    - 'lib/puppet/pops/types/p_uri_type.rb'\n    - 'lib/puppet/pops/types/types.rb'\n    - 'lib/puppet/type.rb'\n\nLint/NonAtomicFileOperation: # new in 1.31\n  Enabled: false\n\nLint/NoReturnInBeginEndBlocks: # new in 1.2\n  Enabled: false\n\nLint/NumberedParameterAssignment: # new in 1.9\n  Enabled: false\n\nLint/OrAssignmentToConstant: # new in 1.9\n  Enabled: false\n\nLint/RedundantDirGlobSort: # new in 1.8\n  Enabled: false\n\nLint/RedundantRegexpQuantifiers: # new in 1.53\n  Enabled: false\n\n# Unsure how the changes in portage.rb from Lint/RedundantSplatExpansion impact\n# the code\nLint/RedundantSplatExpansion:\n  Exclude:\n    - 'lib/puppet/provider/package/portage.rb'\n\nLint/RefinementImportMethods: # new in 1.27\n  Enabled: false\n\nLint/RequireRangeParentheses: # new in 1.32\n  Enabled: false\n\nLint/RequireRelativeSelfPath: # new in 1.22\n  Enabled: false\n\nLint/RescueException:\n  Exclude:\n    - 'ext/windows/service/daemon.rb'\n    - 'lib/puppet/configurer/fact_handler.rb'\n    - 'lib/puppet/generate/type.rb'\n    - 'lib/puppet/settings.rb'\n    - 'lib/puppet/transaction/resource_harness.rb'\n    - 'lib/puppet/util.rb'\n    - 'lib/puppet/util/autoload.rb'\n    - 'lib/puppet/util/command_line/trollop.rb'\n    - 'util/rspec_grouper'\n\n# Configuration parameters: AllowComments, AllowNil.\nLint/SuppressedException:\n  Exclude:\n    - 'lib/puppet/application/face_base.rb'\n    - 'lib/puppet/ffi/windows/functions.rb'\n    - 'lib/puppet/forge/errors.rb'\n    - 'lib/puppet/functions/each.rb'\n    - 'lib/puppet/functions/filter.rb'\n    - 'lib/puppet/functions/map.rb'\n    - 'lib/puppet/functions/slice.rb'\n    - 'lib/puppet/pops/time/timespan.rb'\n    - 'lib/puppet/pops/types/iterable.rb'\n    - 'lib/puppet/pops/types/p_runtime_type.rb'\n    - 'lib/puppet/util/command_line.rb'\n    - 'lib/puppet/util/execution.rb'\n    - 'util/rspec_grouper'\n\nLint/SymbolConversion: # new in 1.9\n  Enabled: false\n\nLint/ToEnumArguments: # new in 1.1\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nLint/ToJSON:\n  Exclude:\n    - 'lib/puppet/module_tool/metadata.rb'\n    - 'lib/puppet/network/http/error.rb'\n    - 'lib/puppet/pops/serialization/json.rb'\n\nLint/TripleQuotes: # new in 1.9\n  Enabled: false\n\nLint/UnexpectedBlockArity: # new in 1.5\n  Enabled: false\n\nLint/UnmodifiedReduceAccumulator: # new in 1.1\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods.\nLint/UnusedMethodArgument:\n  Enabled: false\n\nLint/UselessRescue: # new in 1.43\n  Enabled: false\n\nLint/UselessRuby2Keywords: # new in 1.23\n  Enabled: false\n\nPerformance/BlockGivenWithExplicitBlock: # new in 1.9\n  Enabled: false\n\nPerformance/CollectionLiteralInLoop: # new in 1.8\n  Enabled: false\n\nPerformance/ConstantRegexp: # new in 1.9\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nPerformance/Count:\n  Exclude:\n    - 'lib/puppet/confine/any.rb'\n    - 'lib/puppet/confine/false.rb'\n    - 'lib/puppet/confine/true.rb'\n    - 'lib/puppet/graph/relationship_graph.rb'\n    - 'lib/puppet/provider.rb'\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\nPerformance/InefficientHashSearch:\n  Exclude:\n    - 'lib/puppet/face/node/clean.rb'\n    - 'lib/puppet/provider/nameservice/directoryservice.rb'\n    - 'lib/puppet/provider/user/directoryservice.rb'\n    - 'lib/puppet/resource.rb'\n    - 'lib/puppet/util/windows/adsi.rb'\n\nPerformance/MethodObjectAsBlock: # new in 1.9\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nPerformance/RedundantBlockCall:\n  Exclude:\n    - 'lib/puppet/application.rb'\n    - 'lib/puppet/context.rb'\n    - 'lib/puppet/file_bucket/file.rb'\n    - 'lib/puppet/functions/max.rb'\n    - 'lib/puppet/functions/min.rb'\n    - 'lib/puppet/gettext/stubs.rb'\n    - 'lib/puppet/network/http/api/server/v3.rb'\n    - 'lib/puppet/pal/pal_impl.rb'\n    - 'lib/puppet/pops/adaptable.rb'\n    - 'lib/puppet/pops/lookup/invocation.rb'\n    - 'lib/puppet/pops/model/factory.rb'\n    - 'lib/puppet/util.rb'\n\nPerformance/RedundantEqualityComparisonBlock: # new in 1.10\n  Enabled: false\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\n# Configuration parameters: MaxKeyValuePairs.\nPerformance/RedundantMerge:\n  Exclude:\n    - 'lib/puppet/x509/cert_provider.rb'\n\nPerformance/RedundantSplitRegexpArgument: # new in 1.10\n  Enabled: false\n\nPerformance/RedundantStringChars: # new in 1.7\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nPerformance/RegexpMatch:\n  Enabled: false\n\nPerformance/SortReverse: # new in 1.7\n  Enabled: false\n\nPerformance/Squeeze: # new in 1.7\n  Enabled: false\n\nPerformance/StringIdentifierArgument: # new in 1.13\n  Enabled: false\n\nPerformance/StringInclude: # new in 1.7\n  Enabled: false\n\nPerformance/Sum: # new in 1.8\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nPerformance/UnfreezeString:\n  Enabled: false\n\n# Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols.\n# SupportedStyles: inline, group\nStyle/AccessModifierDeclarations:\n  Exclude:\n    - 'lib/puppet/util/suidmanager.rb'\n    - 'lib/puppet/util/command_line/trollop.rb'\n    - 'lib/puppet/util/windows/monkey_patches/process.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: separated, grouped\nStyle/AccessorGrouping:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: prefer_alias, prefer_alias_method\nStyle/Alias:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: always, conditionals\nStyle/AndOr:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: percent_q, bare_percent\nStyle/BarePercentLiterals:\n  Exclude:\n    - 'lib/puppet/module_tool/metadata.rb'\n    - 'lib/puppet/node/environment.rb'\n    - 'lib/puppet/parameter/package_options.rb'\n    - 'lib/puppet/provider/package/dpkg.rb'\n    - 'lib/puppet/provider/package/gem.rb'\n    - 'lib/puppet/provider/package/rpm.rb'\n    - 'lib/puppet/provider/package/windows/package.rb'\n    - 'lib/puppet/settings.rb'\n    - 'lib/puppet/settings/base_setting.rb'\n    - 'lib/puppet/transaction/event.rb'\n    - 'lib/puppet/util/execution.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/BisectedAttrAccessor:\n  Exclude:\n    - 'lib/puppet/module.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.\n# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces\n# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object\n# FunctionalMethods: let, let!, subject, watch\n# IgnoredMethods: lambda, proc, it\nStyle/BlockDelimiters:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowOnConstant.\nStyle/CaseEquality:\n  Exclude:\n    - 'lib/puppet/indirector/terminus.rb'\n    - 'lib/puppet/interface/face_collection.rb'\n    - 'lib/puppet/module_tool/installed_modules.rb'\n    - 'lib/puppet/module_tool/shared_behaviors.rb'\n    - 'lib/puppet/util/command_line/puppet_option_parser.rb'\n    - 'lib/puppet/util/log/destination.rb'\n    - 'lib/puppet/util/multi_match.rb'\n    - 'lib/puppet/util/rdoc/generators/puppet_generator.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: nested, compact\nStyle/ClassAndModuleChildren:\n  Enabled: false\n\nStyle/ClassVars:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/ColonMethodCall:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, AllowInnerBackticks.\n# SupportedStyles: backticks, percent_x, mixed\nStyle/CommandLiteral:\n  Exclude:\n    - 'ext/windows/service/daemon.rb'\n    - 'lib/puppet/provider/nameservice/directoryservice.rb'\n    - 'lib/puppet/util/reference.rb'\n    - 'lib/puppet/util/terminal.rb'\n    - 'lib/puppet/util/windows/process.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: Keywords, RequireColon.\n# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE\nStyle/CommentAnnotation:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/CommentedKeyword:\n  Exclude:\n    - 'lib/puppet/util/command_line/trollop.rb'\n    - 'lib/puppet/util/rdoc/generators/puppet_generator.rb'\n    - 'lib/puppet/util/rdoc/generators/template/puppet/puppet.rb'\n    - 'lib/puppet/util/rpm_compare.rb'\n    - 'lib/puppet/util/windows/service.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.\n# SupportedStyles: assign_to_condition, assign_inside_condition\nStyle/ConditionalAssignment:\n  Enabled: false\n\n# Enabling this would require reworking Puppet's use of DateTime's #rfc2822, #httptime, and _strptime\nStyle/DateTime:\n  Enabled: false\n\n# Configuration parameters: AllowedConstants.\nStyle/Documentation:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: allowed_in_returns, forbidden\nStyle/DoubleNegation:\n  Exclude:\n    - 'lib/puppet/application/lookup.rb'\n    - 'lib/puppet/confine/boolean.rb'\n    - 'lib/puppet/http/service/compiler.rb'\n    - 'lib/puppet/parser/functions/fqdn_rand.rb'\n    - 'lib/puppet/pops/evaluator/evaluator_impl.rb'\n    - 'lib/puppet/pops/issue_reporter.rb'\n    - 'lib/puppet/pops/types/p_runtime_type.rb'\n    - 'lib/puppet/provider/package/apt.rb'\n    - 'lib/puppet/resource/status.rb'\n    - 'lib/puppet/type.rb'\n    - 'lib/puppet/util/feature.rb'\n    - 'lib/puppet/util/windows/adsi.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: empty, nil, both\nStyle/EmptyElse:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/EmptyLiteral:\n  Exclude:\n    - 'lib/puppet/parser/scope.rb'\n    - 'lib/puppet/pops/puppet_stack.rb'\n    - 'lib/puppet/pops/visitor.rb'\n    - 'lib/puppet/provider/package/portupgrade.rb'\n    - 'lib/puppet/provider/service/launchd.rb'\n    - 'lib/puppet/provider/user/directoryservice.rb'\n    - 'lib/puppet/type.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: compact, expanded\nStyle/EmptyMethod:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/Encoding:\n  Exclude:\n    - 'lib/puppet/face/module/install.rb'\n    - 'lib/puppet/face/module/list.rb'\n    - 'lib/puppet/face/module/upgrade.rb'\n    - 'lib/puppet/ffi/windows/structs.rb'\n    - 'lib/puppet/interface/action.rb'\n    - 'lib/puppet/module_tool.rb'\n    - 'lib/puppet/type.rb'\n    - 'lib/puppet/type/file.rb'\n    - 'lib/puppet/type/package.rb'\n    - 'lib/puppet/util/windows/service.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/ExplicitBlockArgument:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: format, sprintf, percent\nStyle/FormatString:\n  Enabled: false\n\n# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, IgnoredMethods.\n# SupportedStyles: annotated, template, unannotated\nStyle/FormatStringToken:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Applying the safe auto-correct results in util_spec failures.\nStyle/GlobalStdStream:\n  Exclude:\n    - 'lib/puppet/application/apply.rb'\n    - 'lib/puppet/application/script.rb'\n    - 'lib/puppet/face/epp.rb'\n    - 'lib/puppet/face/parser.rb'\n    - 'lib/puppet/util.rb'\n    - 'lib/puppet/util/command_line.rb'\n    - 'lib/puppet/util/windows/daemon.rb'\n\n# Configuration parameters: AllowedVariables.\nStyle/GlobalVars:\n  Exclude:\n    - 'lib/puppet/external/dot.rb'\n    - 'lib/puppet/test/test_helper.rb'\n    - 'lib/puppet/util/logging.rb'\n\n# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.\nStyle/GuardClause:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.\n# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys\n# SupportedShorthandSyntax: always, never, either\nStyle/HashSyntax:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/IfUnlessModifier:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# The auto-correct function introduces testing errors in the evaluating_parser_spec\nStyle/IfWithSemicolon:\n  Exclude:\n    - 'lib/puppet/pops/parser/evaluating_parser.rb'\n\n# This cop requires significant changes to testing, will require its own effort\nStyle/ImplicitRuntimeError:\n  Enabled: false\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\n# Configuration parameters: InverseMethods, InverseBlocks.\nStyle/InverseMethods:\n  Exclude:\n    - 'lib/puppet/face/catalog/select.rb'\n    - 'lib/puppet/graph/relationship_graph.rb'\n    - 'lib/puppet/parser/compiler.rb'\n    - 'lib/puppet/pops/loader/loader_paths.rb'\n    - 'lib/puppet/pops/types/ruby_generator.rb'\n    - 'lib/puppet/pops/validation.rb'\n    - 'lib/puppet/pops/validation/checker4_0.rb'\n    - 'lib/puppet/provider/package/pkg.rb'\n    - 'lib/puppet/provider/user/user_role_add.rb'\n    - 'lib/puppet/reference/providers.rb'\n    - 'lib/puppet/type/file.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: IgnoredMethods.\nStyle/MethodCallWithoutArgsParentheses:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline\nStyle/MethodDefParentheses:\n  Exclude:\n    - 'lib/puppet/pops/evaluator/evaluator_impl.rb'\n    - 'lib/puppet/pops/evaluator/relationship_operator.rb'\n    - 'lib/puppet/pops/issues.rb'\n    - 'lib/puppet/pops/label_provider.rb'\n    - 'lib/puppet/pops/model/factory.rb'\n    - 'lib/puppet/pops/model/model_label_provider.rb'\n    - 'lib/puppet/pops/model/model_tree_dumper.rb'\n    - 'lib/puppet/pops/model/tree_dumper.rb'\n    - 'lib/puppet/pops/parser/interpolation_support.rb'\n    - 'lib/puppet/pops/parser/parser_support.rb'\n    - 'lib/puppet/pops/utils.rb'\n    - 'lib/puppet/pops/validation.rb'\n    - 'lib/puppet/pops/validation/validator_factory_4_0.rb'\n    - 'lib/puppet/util/command_line/trollop.rb'\n\nStyle/MissingRespondToMissing:\n  Exclude:\n    - 'lib/puppet/module_tool/metadata.rb'\n    - 'lib/puppet/parser/scope.rb'\n    - 'lib/puppet/settings/alias_setting.rb'\n    - 'lib/puppet/util/command_line/trollop.rb'\n    - 'lib/puppet/util/feature.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: separated, grouped\nStyle/MixinGrouping:\n  Exclude:\n    - 'lib/puppet/util/rdoc/generators/puppet_generator.rb'\n\nStyle/MultilineBlockChain:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/MultilineIfModifier:\n  Exclude:\n    - 'lib/puppet/face/config.rb'\n    - 'lib/puppet/module_tool/applications/installer.rb'\n    - 'lib/puppet/module_tool/shared_behaviors.rb'\n    - 'lib/puppet/network/http/api/indirected_routes.rb'\n    - 'lib/puppet/pops/evaluator/access_operator.rb'\n    - 'lib/puppet/pops/loader/task_instantiator.rb'\n    - 'lib/puppet/pops/model/model_tree_dumper.rb'\n    - 'lib/puppet/provider/package/windows.rb'\n    - 'lib/puppet/provider/service/upstart.rb'\n    - 'lib/puppet/resource/catalog.rb'\n    - 'lib/puppet/type/file/content.rb'\n    - 'lib/puppet/type/user.rb'\n    - 'lib/puppet/util/execution.rb'\n    - 'lib/puppet/util/windows/com.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/MultilineIfThen:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: keyword, braces\nStyle/MultilineMemoization:\n  Exclude:\n    - 'lib/puppet/application.rb'\n    - 'lib/puppet/pops/types/types.rb'\n    - 'lib/puppet/type.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/MultilineTernaryOperator:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/MultilineWhenThen:\n  Exclude:\n    - 'lib/puppet/graph/simple_graph.rb'\n    - 'lib/puppet/interface/documentation.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowMethodComparison.\nStyle/MultipleComparison:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: literals, strict\nStyle/MutableConstant:\n  Enabled: false\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\n# Configuration parameters: EnforcedStyle, IgnoredMethods.\n# SupportedStyles: predicate, comparison\nStyle/NumericPredicate:\n  Enabled: false\n\n# Configuration parameters: AllowedMethods.\n# AllowedMethods: respond_to_missing?\nStyle/OptionalBooleanParameter:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: implicit, explicit\nStyle/RescueStandardError:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.\n# AllowedMethods: present?, blank?, presence, try, try!\nStyle/SafeNavigation:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowAsExpressionSeparator.\nStyle/Semicolon:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: only_raise, only_fail, semantic\nStyle/SignalException:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowIfMethodIsEmpty.\nStyle/SingleLineMethods:\n  Exclude:\n    - 'lib/puppet/ffi/windows/api_types.rb'\n    - 'lib/puppet/file_system/memory_file.rb'\n    - 'lib/puppet/graph/simple_graph.rb'\n    - 'lib/puppet/interface/action.rb'\n    - 'lib/puppet/parser/resource.rb'\n    - 'lib/puppet/pops/model/factory.rb'\n    - 'lib/puppet/pops/model/model_label_provider.rb'\n    - 'lib/puppet/pops/types/type_formatter.rb'\n    - 'lib/puppet/provider/nameservice/directoryservice.rb'\n    - 'lib/puppet/provider/service/freebsd.rb'\n    - 'lib/puppet/type.rb'\n    - 'lib/puppet/util/command_line/trollop.rb'\n    - 'lib/puppet/util/metaid.rb'\n    - 'lib/puppet/util/windows/com.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowModifier.\nStyle/SoleNestedConditional:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/StderrPuts:\n  Exclude:\n    - 'bin/puppet'\n    - 'lib/puppet/application/agent.rb'\n    - 'lib/puppet/application/apply.rb'\n    - 'lib/puppet/application/describe.rb'\n    - 'lib/puppet/application/device.rb'\n    - 'lib/puppet/application/face_base.rb'\n    - 'lib/puppet/application/filebucket.rb'\n    - 'lib/puppet/application/script.rb'\n    - 'lib/puppet/face/config.rb'\n    - 'lib/puppet/reference/type.rb'\n    - 'lib/puppet/util.rb'\n    - 'lib/puppet/util/command_line/trollop.rb'\n    - 'lib/puppet/util/rdoc/generators/puppet_generator.rb'\n    - 'lib/puppet/util/reference.rb'\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\n# Configuration parameters: Mode.\nStyle/StringConcatenation:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.\n# SupportedStyles: single_quotes, double_quotes\nStyle/StringLiterals:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, MinSize.\n# SupportedStyles: percent, brackets\nStyle/SymbolArray:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyle, AllowSafeAssignment.\n# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex\nStyle/TernaryParentheses:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: EnforcedStyleForMultiline.\n# SupportedStylesForMultiline: comma, consistent_comma, no_comma\nStyle/TrailingCommaInHashLiteral:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowNamedUnderscoreVariables.\nStyle/TrailingUnderscoreVariable:\n  Exclude:\n    - 'lib/puppet/indirector/file_server.rb'\n    - 'lib/puppet/module/plan.rb'\n    - 'lib/puppet/pops/evaluator/closure.rb'\n    - 'lib/puppet/pops/parser/parser_support.rb'\n    - 'lib/puppet/provider/group/windows_adsi.rb'\n    - 'lib/puppet/provider/package/zypper.rb'\n    - 'lib/puppet/provider/service/launchd.rb'\n    - 'lib/puppet/provider/user/pw.rb'\n    - 'lib/puppet/provider/user/windows_adsi.rb'\n    - 'lib/puppet/resource/type.rb'\n    - 'lib/puppet/util/windows/registry.rb'\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods.\n# 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\nStyle/TrivialAccessors:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/WhenThen:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\nStyle/WhileUntilModifier:\n  Exclude:\n    - 'lib/puppet/parser/scope.rb'\n    - 'lib/puppet/pops/parser/interpolation_support.rb'\n    - 'lib/puppet/pops/parser/locator.rb'\n    - 'lib/puppet/pops/parser/pn_parser.rb'\n    - 'lib/puppet/pops/types/p_object_type.rb'\n    - 'lib/puppet/util/windows/process.rb'\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\n# Configuration parameters: EnforcedStyle.\n# SupportedStyles: forbid_for_all_comparison_operators, forbid_for_equality_operators_only, require_for_all_comparison_operators, require_for_equality_operators_only\nStyle/YodaCondition:\n  Enabled: false\n\n# This cop supports unsafe auto-correction (--auto-correct-all).\nStyle/ZeroLengthPredicate:\n  Enabled: false\n\n# This cop supports safe auto-correction (--auto-correct).\n# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.\n# URISchemes: http, https\nLayout/LineLength:\n  Max: 582\n"
  },
  {
    "path": ".yardopts",
    "content": "--protected\n--private\n--verbose\n--markup markdown\n--readme README.md\n--tag status\n--transitive-tag status\n--tag comment\n--hide-tag comment\n--tag dsl:\"DSL\"\n--no-transitive-tag api\n--template-path yardoc/templates\n--files CO*.md,api/**/*.md\n--api public\n--api private\n--hide-void-return\n--exclude lib/puppet/vendor/\nlib/**/*.rb\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "# defaults\n* @puppetlabs/phoenix\n\n# PAL\n/lib/puppet/pal @puppetlabs/bolt\n\n# puppet module\n/lib/puppet/application/module.rb @puppetlabs/modules\n/lib/puppet/face/module @puppetlabs/modules\n/lib/puppet/forge @puppetlabs/modules\n/lib/puppet/module_tool @puppetlabs/modules\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Community Guidelines and Code of Conduct\n\nWe want to keep the Puppet communities awesome, and we need your help to keep it\nthat way. While we have specific guidelines for various tools (see links below),\nin general, you should:\n\n* **Be nice**: Be courteous, respectful and polite to fellow community members. No\n  offensive comments related to gender, gender identity or expression, sexual\n  orientation, disability, physical appearance, body size, race, religion; no\n  sexual images in public spaces, real or implied violence, intimidation,\n  oppression, stalking, following, harassing photography or recording, sustained\n  disruption of talks or other events, inappropriate physical contact, doxxing, or\n  unwelcome sexual attention will be tolerated. We like nice people way better\n  than mean ones!\n* **Encourage diversity and participation**: Make everyone in our community feel\n  welcome, regardless of their background, and do everything possible to encourage\n  participation in our community.\n* **Focus on constructive criticisms**: When offering suggestions, whether in online\n  discussions or as comments on a pull request, you should always use welcoming\n  and inclusive language. Be respectful of differing viewpoints and the fact that\n  others may not have the same experiences you do. Offer suggestions for\n  improvement, rather than focusing on mistakes. When others critique your work or\n  ideas, gracefully accept the criticisms and default to assuming good intentions.\n* **Keep it legal**: Basically, don't get us in trouble. Share only content that you\n  own, do not share private or sensitive information, and don't break the law.\n* **Stay on topic**: Keep conversation in a thread on topic, whether that's a pull\n  request or a Slack conversation or anything else. Make sure that you are posting\n  to the correct channel and remember that nobody likes spam.\n\n## Guideline violations --- 3 strikes method\n\nThe point of this section is not to find opportunities to punish people, but we\ndo need a fair way to deal with people who do harm to our community. Extreme\nviolations of a threatening, abusive, destructive, or illegal nature will be\naddressed immediately and are not subject to 3 strikes.\n\n* First occurrence: We'll give you a friendly, but public, reminder that the\n  behavior is inappropriate according to our guidelines.\n* Second occurrence: We'll send you a private message with a warning that any\n  additional violations will result in removal from the community.\n* Third occurrence: Depending on the violation, we might need to delete or ban\n  your account.\n\nNotes:\n\n* Obvious spammers are banned on first occurrence. If we don’t do this, we’ll\n  have spam all over the place.\n* Violations are forgiven after 6 months of good behavior, and we won’t hold a grudge.\n* People who are committing minor formatting / style infractions will get some\n  education, rather than hammering them in the 3 strikes process.\n\nContact conduct@puppet.com to report abuse or appeal violations. This email list\ngoes to Kara Sowles (kara at puppet.com) and Katie Abbott (katie dot abbott at\npuppet.com). In the case of appeals, we know that mistakes happen, and we’ll\nwork with you to come up with a fair solution if there has been a\nmisunderstanding.\n\n## Full text\n\nSee our [full community guidelines](https://puppet.com/community/community-guidelines),\ncovering Slack, IRC, events and other forms of community participation.\n\n## Credits\n\nCredit to [01.org](https://01.org/community/participation-guidelines) and\n[meego.com](http://wiki.meego.com/Community_guidelines), since they formed the\nstarting point for many of these guidelines.\n\nThe Event Code of Conduct is based on the [example policy from the Geek Feminism wiki](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment),\ncreated by the Ada Initiative and other volunteers. The [PyCon Code of Conduct](https://github.com/python/pycon-code-of-conduct) also served as inspiration.\n"
  },
  {
    "path": "Gemfile",
    "content": "source ENV['GEM_SOURCE'] || \"https://rubygems.org\"\n\ngemspec\n\ndef location_for(place, fake_version = nil)\n  if place.is_a?(String) && place =~ /^((?:git[:@]|https:)[^#]*)#(.*)/\n    [fake_version, { git: $1, branch: $2, require: false }].compact\n  elsif place.is_a?(String) && place =~ /^file:\\/\\/(.*)/\n    ['>= 0', { path: File.expand_path($1), require: false }]\n  else\n    [place, { require: false }]\n  end\nend\n\n# Make sure these gem requirements are in sync with the gempspec. Specifically,\n# the runtime_dependencies in puppet.gemspec match the runtime dependencies here\n# (like facter, semantic_puppet, and puppet-resource_api)\n\ngem \"facter\", *location_for(ENV['FACTER_LOCATION'] || [\"~> 4.3\"])\ngem \"semantic_puppet\", *location_for(ENV['SEMANTIC_PUPPET_LOCATION'] || [\"~> 1.0\"])\ngem \"puppet-resource_api\", *location_for(ENV['RESOURCE_API_LOCATION'] || [\"~> 1.5\"])\n\ngroup(:features) do\n  gem 'diff-lcs', '~> 1.3', require: false\n  gem \"hiera\", *location_for(ENV['HIERA_LOCATION']) if ENV.has_key?('HIERA_LOCATION')\n  gem 'hiera-eyaml', *location_for(ENV['HIERA_EYAML_LOCATION'])\n  gem 'hocon', '~> 1.0', require: false\n  # requires native libshadow headers/libs\n  #gem 'ruby-shadow', '~> 2.5', require: false, platforms: [:ruby]\n  gem 'minitar', '~> 0.9', require: false\n  gem 'msgpack', '~> 1.2', require: false\n  gem 'rdoc', ['~> 6.0', '< 6.4.0'], require: false, platforms: [:ruby]\n  # requires native augeas headers/libs\n  # gem 'ruby-augeas', require: false, platforms: [:ruby]\n  # requires native ldap headers/libs\n  # gem 'ruby-ldap', '~> 0.9', require: false, platforms: [:ruby]\n  gem 'puppetserver-ca', '~> 2.0', require: false\n  gem 'syslog', '~> 0.1.1', require: false, platforms: [:ruby]\n  gem 'CFPropertyList', ['>= 3.0.6', '< 4'], require: false\nend\n\ngroup(:test) do\n  # 1.16.0 - 1.16.2 are broken on Windows\n  gem 'ffi', '>= 1.15.5', '< 1.17.0', '!= 1.16.0', '!= 1.16.1', '!= 1.16.2', require: false\n  gem \"json-schema\", \"~> 2.0\", require: false\n  gem \"racc\", \"1.5.2\", require: false\n  gem \"rake\", *location_for(ENV['RAKE_LOCATION'] || '~> 13.0')\n  gem \"rspec\", \"~> 3.1\", require: false\n  gem \"rspec-expectations\", [\"~> 3.9\", \"!= 3.9.3\"]\n  gem \"rspec-its\", \"~> 1.1\", require: false\n  gem 'vcr', '~> 6.1', require: false\n  gem 'webmock', '~> 3.0', require: false\n  gem 'webrick', '~> 1.7', require: false\n  gem 'yard', require: false\n\n  gem 'rubocop', '~> 1.0', require: false, platforms: [:ruby]\n  gem 'rubocop-i18n', '~> 3.0', require: false, platforms: [:ruby]\n  gem 'rubocop-performance', '~> 1.0', require: false, platforms: [:ruby]\n  gem 'rubocop-rake', '~> 0.6', require: false, platforms: [:ruby]\n  gem 'rubocop-rspec', '~> 2.0', require: false, platforms: [:ruby]\nend\n\ngroup(:development, optional: true) do\n  gem 'memory_profiler', require: false, platforms: [:mri]\n  gem 'pry', require: false, platforms: [:ruby]\n  if RUBY_PLATFORM != 'java'\n    gem 'ruby-prof', '>= 0.16.0', require: false\n  end\nend\n\ngroup(:packaging) do\n  gem 'packaging', *location_for(ENV['PACKAGING_LOCATION'] || '~> 0.99')\nend\n\ngroup(:documentation, optional: true) do\n  gem 'gettext-setup', '~> 1.0', require: false, platforms: [:ruby]\n  gem 'ronn', '~> 0.7.3', require: false, platforms: [:ruby]\n  gem 'puppet-strings', require: false, platforms: [:ruby]\n  gem 'pandoc-ruby', require: false, platforms: [:ruby]\nend\n\nif File.exist? \"#{__FILE__}.local\"\n  eval(File.read(\"#{__FILE__}.local\"), binding)\nend\n\n# vim:filetype=ruby\n"
  },
  {
    "path": "Guardfile.example",
    "content": "# More info at https://github.com/guard/guard#readme\n\n# You'll need to make sure Guard, and any of its plugins are in your Gemfile.local\n#\n# Example:\n#  # Automatically run tests on file changes\n#  gem 'guard', require: false\n#  gem 'guard-rspec', require: false\n#  gem 'guard-bundler', require: false\n#  gem 'terminal-notifier-guard', require: false\n#\n# After running `bundle install`, you can run Guard via `bundle exec guard`\n# from the top of the repository checkout.\n\nnotification(:terminal_notifier, app_name: \"Puppet ::\", group: `pwd`.chomp) if `uname` =~ /Darwin/\n\n## Uncomment and set this to only include directories you want to watch\n# directories %w(app lib config test spec features) \\\n#  .select{|d| Dir.exist?(d) ? d : UI.warning(\"Directory #{d} does not exist\")}\n\n## Note: if you are using the `directories` clause above and you are not\n## watching the project directory ('.'), then you will want to move\n## the Guardfile to a watched dir and symlink it back, e.g.\n#\n#  $ mkdir config\n#  $ mv Guardfile config/\n#  $ ln -s config/Guardfile .\n#\n# and, you'll have to watch \"config/Guardfile\" instead of \"Guardfile\"\n\nguard :bundler do\n  require 'guard/bundler'\n  require 'guard/bundler/verify'\n  helper = Guard::Bundler::Verify.new\n\n  files = ['Gemfile', 'Gemfile.local']\n  files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }\n\n  # Assume files are symlinked from somewhere\n  files.each { |file| watch(helper.real_path(file)) }\nend\n\ndef file2specs(match)\n  file = match[0]\n  puts \"Lib file changed: #{file.inspect}\"\n  %w{spec/unit spec/integration}.collect { |d|\n    file.sub('lib/puppet', d).sub(\".rb\", \"_spec.rb\")\n  }.find_all { |f|\n    File.exist?(f)\n  }\nend\n\nrspec_options = {\n  cmd: \"bundle exec rspec\",\n  run_all: {\n    cmd: \"bundle exec parallel_rspec -o '--format progress \",\n    cmd_additional_args: \"'\"\n  },\n  all_after_pass: false\n}\nguard :rspec, rspec_options do\n  require \"guard/rspec/dsl\"\n  dsl = Guard::RSpec::Dsl.new(self)\n\n  # Feel free to open issues for suggestions and improvements\n\n  # RSpec files\n  rspec = dsl.rspec\n  watch(rspec.spec_helper) { rspec.spec_dir }\n  watch(rspec.spec_support) { rspec.spec_dir }\n  watch(rspec.spec_files)\n\n  # Ruby files\n  ruby = dsl.ruby\n  watch(ruby.lib_files) { |f| file2specs(f) }\nend\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Puppet\n\n![RSpec tests](https://github.com/puppetlabs/puppet/workflows/RSpec%20tests/badge.svg)\n[![Gem Version](https://badge.fury.io/rb/puppet.svg)](https://badge.fury.io/rb/puppet)\n[![Inline docs](https://inch-ci.org/github/puppetlabs/puppet.svg)](https://inch-ci.org/github/puppetlabs/puppet)\n\nPuppet, an automated administrative engine for your Linux, Unix, and Windows systems, performs\nadministrative tasks (such as adding users, installing packages, and updating server\nconfigurations) based on a centralized specification.\n\n## Documentation\n\nDocumentation for Puppet and related projects can be found online at the\n[Puppet Docs site](https://puppet.com/docs).\n\n### HTTP API\n\n[HTTP API Index](https://puppet.com/docs/puppet/latest/http_api/http_api_index.html)\n\n## Installation\n\nThe best way to run Puppet is with [Puppet Enterprise (PE)](https://puppet.com/products/puppet-enterprise/),\nwhich also includes orchestration features, a web console, and professional support.\nThe PE documentation is [available here.](https://puppet.com/docs/pe/latest)\n\nTo install an open source release of Puppet,\n[see the installation guide on the docs site.](https://puppet.com/docs/puppet/latest/installing_and_upgrading.html)\n\nIf you need to run Puppet from source as a tester or developer,\nsee the [Quick Start to Developing on Puppet](docs/quickstart.md) guide.\n\n## Developing and Contributing\n\nWe'd love to get contributions from you! For a quick guide to getting your\nsystem setup for developing, take a look at our [Quickstart\nGuide](https://github.com/puppetlabs/puppet/blob/main/docs/quickstart.md). Once you are up and running, take a look at the\n[Contribution Documents](https://github.com/puppetlabs/.github/blob/main/CONTRIBUTING.md) to see how to get your changes merged\nin.\n\nFor more complete docs on developing with Puppet, take a look at the\nrest of the [developer documents](https://github.com/puppetlabs/puppet/blob/main/docs/index.md).\n\n## Licensing\n\nSee [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\n\n## Support\n\nPlease log issues in this project's [GitHub Issues](https://github.com/puppetlabs/puppet/issues). A [mailing\nlist](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is\navailable for asking questions and getting help from others, or if you prefer chat, we also have a [Puppet Community slack.](https://puppetcommunity.slack.com/)\n\nWe use semantic version numbers for our releases and recommend that users stay\nas up-to-date as possible by upgrading to patch releases and minor releases as\nthey become available.\n\nBug fixes and ongoing development will occur in minor releases for the current\nmajor version. Security fixes will be backported to a previous major version on\na best-effort basis, until the previous major version is no longer maintained.\n\nFor example: If a security vulnerability is discovered in Puppet 8.1.1, we\nwould fix it in the 8 series, most likely as 8.1.2. Maintainers would then make\na best effort to backport that fix onto the latest Puppet 7 release.\n\nLong-term support, including security patches and bug fixes, is available for\ncommercial customers. Please see the following page for more details:\n\n[Puppet Enterprise Support Lifecycle](https://puppet.com/docs/puppet-enterprise/product-support-lifecycle/)\n"
  },
  {
    "path": "Rakefile",
    "content": "# frozen_string_literal: true\n\nrequire 'open3'\nrequire 'rake'\nrequire 'rubygems'\nrequire 'rubygems/package_task'\n\nif Rake.application.top_level_tasks.grep(/^(pl:|package:)/).any?\n  begin\n    require 'packaging'\n    Pkg::Util::RakeUtils.load_packaging_tasks\n  rescue LoadError => e\n    puts \"Error loading packaging rake tasks: #{e}\"\n  end\nend\n\nnamespace :package do\n  task :bootstrap do\n    puts 'Bootstrap is no longer needed, using packaging-as-a-gem'\n  end\n  task :implode do\n    puts 'Implode is no longer needed, using packaging-as-a-gem'\n  end\nend\n\ntask :default do\n  sh %{rake -T}\nend\n\nnamespace :pl_ci do\n  desc 'Build puppet gems'\n  task :gem_build, [:gemspec] do |t, args|\n    args.with_defaults(gemspec: 'puppet.gemspec')\n    stdout, stderr, status = Open3.capture3(<<~END)\n      gem build #{args.gemspec} --platform x86-mingw32 && \\\n      gem build #{args.gemspec} --platform x64-mingw32 && \\\n      gem build #{args.gemspec} --platform universal-darwin && \\\n      gem build #{args.gemspec}\n    END\n    if !status.exitstatus.zero?\n      puts \"Error building #{args.gemspec}\\n#{stdout} \\n#{stderr}\"\n      exit(1)\n    else\n      puts stdout\n    end\n  end\n\n  desc 'build the nightly puppet gems'\n  task :nightly_gem_build do\n    # this is taken from `rake package:nightly_gem`\n    extended_dot_version = %x{git describe --tags --dirty --abbrev=7}.chomp.tr('-', '.')\n\n    # we must create tempfile in the same directory as puppetg.gemspec, since\n    # it uses __dir__ to determine which files to include\n    require 'tempfile'\n    Tempfile.create('gemspec', __dir__) do |dst|\n      File.open('puppet.gemspec', 'r') do |src|\n        src.readlines.each do |line|\n          if line.match?(/version\\s*=\\s*['\"][0-9.]+['\"]/)\n            line = \"spec.version = '#{extended_dot_version}'\"\n          end\n          dst.puts line\n        end\n      end\n      dst.flush\n      Rake::Task['pl_ci:gem_build'].invoke(dst.path)\n    end\n  end\nend\n\ntask :spec do\n  ENV[\"LOG_SPEC_ORDER\"] = \"true\"\n  sh %{rspec #{ENV['TEST'] || ENV['TESTS'] || 'spec'}}\nend\n\ndesc 'run static analysis with rubocop'\ntask(:rubocop) do\n  require 'rubocop'\n  cli = RuboCop::CLI.new\n  exit_code = cli.run(%w(--display-cop-names --format simple))\n  raise \"RuboCop detected offenses\" if exit_code != 0\nend\n\ndesc \"verify that changed files are clean of Ruby warnings\"\ntask(:warnings) do\n  # This rake task looks at all files modified in this branch.\n  commit_range = 'HEAD^..HEAD'\n  ruby_files_ok = true\n  puts \"Checking modified files #{commit_range}\"\n  %x{git diff --diff-filter=ACM --name-only #{commit_range}}.each_line do |modified_file|\n    modified_file.chomp!\n    # Skip racc generated file as it can have many warnings that cannot be manually fixed\n    next if modified_file.end_with?(\"pops/parser/eparser.rb\")\n    next if modified_file.start_with?('spec/fixtures/', 'acceptance/fixtures/') || File.extname(modified_file) != '.rb'\n    puts modified_file\n\n    stdout, stderr, _ = Open3.capture3(\"ruby -wc \\\"#{modified_file}\\\"\")\n    unless stderr.empty?\n      ruby_files_ok = false\n      puts stderr\n    end\n    puts stdout\n  end\n  raise \"One or more ruby files contain warnings.\" unless ruby_files_ok\nend\n\nif Rake.application.top_level_tasks.grep(/^gettext:/).any?\n  begin\n    spec = Gem::Specification.find_by_name 'gettext-setup'\n    load \"#{spec.gem_dir}/lib/tasks/gettext.rake\"\n    GettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__)))\n  rescue LoadError\n    abort(\"Run `bundle install --with documentation` to install the `gettext-setup` gem.\")\n  end\nend\n"
  },
  {
    "path": "acceptance/.beaker.yml",
    "content": "---\nssh:\n  keys:\n    - id_rsa_acceptance\n    - ~/.ssh/id_rsa-acceptance\nxml:                         true\ntimesync:                    false\nrepo_proxy:                  true\nadd_el_extras:               false\n'master-start-curl-retries': 30\nlog_level:                   debug\npreserve_hosts:              onfail\nhelper:                      ./lib/helper.rb\noptions_file:                ./config/aio/options.rb\n"
  },
  {
    "path": "acceptance/.gitignore",
    "content": ".vagrant\nlocal_options.rb\npl-puppet-build.repo\npl-puppet-repos.rpm\nrepos.tar\nrepo-configs\nlog\nid_rsa-acceptance\nid_rsa-acceptance.pub\npreserved_config.yaml\nmerged_options.rb\ntmp\n"
  },
  {
    "path": "acceptance/Gemfile",
    "content": "# Specifies a gem mirror; duplicated in acceptance setup\n# to ensure a similar environment on acceptance hosts.\nsource ENV['GEM_SOURCE'] || 'https://rubygems.org'\n\ndef location_for(place, fake_version = nil)\n  if place.is_a?(String) && place =~ /^((?:git[:@]|https:)[^#]*)#(.*)/\n    [fake_version, { :git => $1, :branch => $2, :require => false }].compact\n  elsif place.is_a?(String) && place =~ /^file:\\/\\/(.*)/\n    ['>= 0', { :path => File.expand_path($1), :require => false }]\n  else\n    [place, { :require => false }]\n  end\nend\n\ngem \"beaker\", *location_for(ENV['BEAKER_VERSION'] || '~> 6.0')\ngem \"beaker-puppet\", *location_for(ENV['BEAKER_PUPPET_VERSION' || \"~> 4.0\"])\ngem \"beaker-hostgenerator\", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'] || \"~> 2\")\ngem \"beaker-abs\", *location_for(ENV['BEAKER_ABS_VERSION'] || \"~> 1.0\")\ngem \"beaker-vagrant\", *location_for(ENV['BEAKER_VAGRANT_VERSION'] || \"~> 0\")\ngem \"beaker-vmpooler\", *location_for(ENV['BEAKER_VMPOOLER_VERSION'] || \"~> 1.3\")\ngem \"beaker-vcloud\", *location_for(ENV['BEAKER_VCLOUD_VERSION'] || \"~> 1.0\")\ngem \"beaker-docker\", *location_for(ENV['BEAKER_DOCKER_VERSION'] || \"~> 0.5\")\ngem \"beaker-gke\", *location_for(ENV['BEAKER_GKE_VERSION'] || \"~> 0.0.3\")\ngem \"rake\", \">= 12.3.3\"\ngem \"httparty\", :require => false\ngem 'uuidtools', :require => false\n\ngroup(:test) do\n  gem \"rspec\", \"~> 2.14.0\", :require => false\n  gem \"mocha\", \"~> 0.10.5\", :require => false\nend\n\nif File.exist? \"#{__FILE__}.local\"\n  eval(File.read(\"#{__FILE__}.local\"), binding)\nend\n"
  },
  {
    "path": "acceptance/README.md",
    "content": "# Running Puppet Acceptance Tests\n\n\n## Table of Contents\n* [Setup](#setup)\n* [Quick Start](#quick-start)\n* [Configuration](#configuration)\n* [Running Tests](#running-tests)\n* [Writing Tests](#writing-tests)\n* [Getting Help](#getting-help)\n\n-------------\nAn important aside: Currently running acceptance tests that contain a specific\nchange is challenging unless you have access to infrastructure internal to the\nPuppet, Inc. network. This is a known issue, and we are working to make this a\nbetter experience for our community.\n\n-------------\n\n## Setup\n### Prerequisites\n* git\n* ruby\n* [bundler][]\n* a local clone of the puppet repo\n\nAll command examples in this readme assume you are working in the same directory\nthis README is in, `puppet/acceptance`.\n\n### Installation\nAll of the dependencies you need to run and develop tests are defined in\n`Gemfile`. To install them, run `bundle install --path .bundle/gems`. This\ncommand, as well all the command examples in this README, assume you are working\nin the acceptance directory. If you ever have issues with your runtime\ndependencies, you can update them with `bundle update` or start over fresh with\n`rm -rf .bundle/gems; bundle install --path .bundle/gems`.\n\nTo ensure installation was successful, you can run `bundle exec rake -T`. This\nshould return something along these lines:\n```\n$ bundle exec rake -T\nrake ci:help               # Print usage information\nrake ci:test:aio           # Run the acceptance tests using puppet-agent (AI...\nrake ci:test:gem           # Run the acceptance tests against puppet gem on ...\nrake ci:test:git           # Run the acceptance tests against a git checkout\nrake ci:test:quick         # Run a limited but representative subset of acce...\nrake clean                 # Remove any temporary products\nrake clobber               # Remove any generated files\n```\nTo get a detailed description of all of these tasks, run `bundle exec rake -D`.\n\n-------------\n## Quick Start\n### For community members\nCurrently, there isn't a good way for community members to run acceptance tests.\nThis is a known problem. We currently have multiple avenues we are exploring to\nmake running puppet acceptance tests easier for our community. In the meantime,\nwe apologize for the inconvenience.\n\n### For Puppet, Inc. employees\nIf you have access to infrastructure internal to the Puppet, Inc. network, then\nthe quickest way to get acceptance tests running is with vmpooler.\n\nTo test changes that are available on a branch on github.com:\n```\nbundle 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'\n```\nWhere `SHA` is the branch name, `RUNTIME_BRANCH` is the agent version stream,\nand `FORK` is the github fork where the branch lives.\n\nTo test changes that are available in a puppet-agent package on builds.delivery.puppetlabs.net:\n```\nbundle exec rake ci:test:aio OPTIONS='--preserve-hosts=always' SHA=9124b4e81ec0ac6394d3edc67d4ab71866869fd7 TESTS='tests/path/to/test.rb,tests/other/test.rb'\n```\n`SHA` is a sha or tag that exists on builds.delivery.puppetlabs.net/puppet-agent\n\nTo rerun a test on the hosts that have already been provisioned, use beaker subcommands:\n```\nbundle exec beaker exec tests/path/to/test.rb,tests/other/test.rb\n```\n\nAlways clean up after yourself when you are done:\n```\nbundle exec beaker destroy\n```\nThis will remove any provisioned hosts. Only run this once you are done with the\nhosts that have been checked out and provisioned for a given run.\n\n-------------\n\n## Configuration\n### Environment Variables\nA detailed description of the available environment variables can be found by\nrunning `bundle exec rake ci:help`. This will print a list of both required and\noptional environment variables with short descriptions on how they are used.\nPlease review all of these options as they will impact how your test servers\nare provisioned. This rake task is the most up to date source for this\ninformation. Please read through the available variables, their defaults, and\nwhat they do. They may impact your acceptance run in ways you do not expect.\n\n### Customizing Test Targets\nIf you are using the vmpooler hypervisor internal to Puppet, Inc. infrastructure,\nyou can customize the platforms to test on using the `HOSTS` environment variable.\nYou'll set the `HOSTS` environment variable to the host string you want to test,\nsuch as `HOSTS=redhat7-64ma-windows2012r2-64a`.\n\nFor a list of available `HOSTS` platforms and their exact naming structures,\ncheck 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\n`{platform}{version}-{architecture}{role/s}`. You will most often use either the\nagent (a) or master (m) role, but you can find a list of available roles in\n[beaker hostgenerator](https://github.com/puppetlabs/beaker-hostgenerator/blob/master/lib/beaker-hostgenerator/roles.rb).\nMultiple hosts in the string are separated with a dash(`-`). You must have at\nleast one agent and at least one master.\n\nBe careful not to confuse the different host string formats. We have different\ntools that expect the host string to be in different forms. For example,\n`packaging_platform` is specific to how [Vanagon](https://github.com/puppetlabs/vanagon)\nparses that string.\n\n### The Hosts File\nThe rake tasks that run acceptance will by default create a hosts file and\npopulate it using [beaker-hostgenerator][] using either the `HOSTS` environment\nvariable or the default host string (currently `redhat7-64ma-windows2012r2-64a`).\nThe automation assumes you are using the vmpooler hypervisor and a vmpooler\ninstance that is only available to Puppet, Inc. employees. If you want to\ncustomize the hypervisor or the vmpooler instance, you'll need to generate your own\nhosts file. You must pass in a valid host string to the `beaker-hostgenerator`\ncommand. See [Customizing Test Targets](#customizing-test-targets) for more\ninformation on how to construct a valid host string.\n\nTo customize the hypervisor, pass in `--hypervisor {hypervisor name}`. To set\nthe vmpooler instance, use `--global-config pooling_api={vmpooler uri}`. Only the\nvmpooler hypervisor uses the pooling_api key.\n\nThe host string that is passed in is the same that you would use with the\n`HOSTS` environment variable. See [Customizing Test Targets](#customizing-test-targets)\non how to format this string.\n\nTo have the automation recognize and use your custom hosts file, you'll need to\nset the `HOSTS` environment variable to the hosts file. In the above example, we\ncalled this file `hosts.yaml`, so we will set `HOSTS` to `hosts.yaml` when running\nall future beaker commands or rake tasks to run acceptance tests.\n\nFor example, if you were to run this command:\n```\nbundle exec beaker-hostgenerator redhat7-64ma-windows2012r2-64a --disable-default-role --osinfo-version 1 --hypervisor vmpooler --global-config pooling_api=http://customvmpooler/ > hosts.yaml\n```\nYou would generate a file called `hosts.yaml` that contains something like this:\n```\n---\nHOSTS:\n  redhat7-64-1:\n    platform: el-7-x86_64\n    packaging_platform: el-7-x86_64\n    template: redhat-7-x86_64\n    hypervisor: vmpooler\n    roles:\n    - master\n    - agent\n  windows2012r2-64-1:\n    platform: windows-2012r2-64\n    packaging_platform: windows-2012-x64\n    ruby_arch: x64\n    template: win-2012r2-x86_64\n    hypervisor: vmpooler\n    roles:\n    - agent\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  pooling_api: http://customvmpooler/\n```\nWe can then run the acceptance tests with:\n`bundle exec rake ci:test:aio HOSTS=hosts.yaml SHA={sha}`\n\n### Hypervisor Options\nThe hypervisor dictates where you will be running the acceptance tests. The beaker\nhypervisors take care of basic host setup so that you will have a consistent\nhost environment across every test run. You can find more details on the different\nhypervisor options in [the beaker repo](https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/README.md).\n\nHere, we will focus on vmpooler and docker, as those are the two we use most\noften internally. If you use a hypervisor other than abs, vagrant, vmpooler, or\ndocker, you'll have to add the gem to that hypervisor to `Gemfile.local` and run\n`bundle update` to install the new gems. You also have the ability to run tests\non a static host.\n\n#### VMPooler\n[VMPooler](https://github.com/puppetlabs/vmpooler) is the default hypervisor we\nuse. This is only available to Puppet, Inc. employees as it uses internal\ninfrastructure. If you have access to a similar setup, then you are welcome to\nuse this option with a few values changed. If you are using the Puppet internal\nvmpooler, then you can simply run the acceptance rake tasks. See\n[Customizing Test Targets](#customizing-test-targets) about how to use the\n`HOSTS` environment variable to customize the platforms you are running tests on.\n\nTo use a different vmpooler instance, use\n`--global-config pooling_api=http://customvmpooler/` when you use\n`beaker-hostgenerator` to generate `hosts.yaml`. Make sure you set `HOSTS` to\nthe hosts file you just generated so the automation can find that file. See\n[The Hosts File](#the-hosts-file) for more detail on the hosts file.\n\n#### Docker\nTo test with [the docker hypervisor](https://github.com/puppetlabs/beaker-docker),\nyou will want to generate a custom hosts file. You will also mostly likely need\nto manually edit the file. See [The Hosts File](#the-hosts-file) for more\ndetail on the hosts file.\n\nTo create a hosts file with a centos 7 master and a centos 7 agent, we can use\nthe following beaker-hostgenerator command\n`bundle exec beaker-hostgenerator centos7-64m-centos7-64a --disable-default-role --osinfo-version 1 --hypervisor docker  > hosts.yaml`\nWhich will produce a file called `hosts.yaml` that contains the following:\n```\n---\nHOSTS:\n  centos7-64-1:\n    docker_cmd:\n    - \"/sbin/init\"\n    image: centos:7\n    platform: centos-7-x86_64\n    packaging_platform: el-7-x86_64\n    docker_image_commands:\n    - cp /bin/true /sbin/agetty\n    - yum install -y crontabs initscripts iproute openssl sysvinit-tools tar wget\n      which ss\n    hypervisor: docker\n    roles:\n    - master\n  centos7-64-2:\n    docker_cmd:\n    - \"/sbin/init\"\n    image: centos:7\n    platform: centos-7-x86_64\n    packaging_platform: el-7-x86_64\n    docker_image_commands:\n    - cp /bin/true /sbin/agetty\n    - yum install -y crontabs initscripts iproute openssl sysvinit-tools tar wget\n      which ss\n    hypervisor: docker\n    roles:\n    - agent\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n\n```\nRun acceptance tests against pre-built puppet-agent packages with\n`bundle exec rake ci:test:aio SHA={sha|tag} TESTS=path/to/test.rb HOSTS=hosts.yaml`\n\nNote that if you are not running tests against the master branch and you are\ninstalling the latest puppetserver package, you will likely need to set `RELEASE_STREAM`\nto pick up the correct server version. Please see the section on [environment variables](#environment-variables)\nfor more information.\n\nWhen you generate your [hosts file](#the-hosts-file), [beaker-hostgenerator][] does\nits best to populate the values as logically as possible. You will likely want\nto update or modify them to suite your needs.\n\nWith `image`, [beaker-hostgenerator][] does its best to guess the most logical\nimage string based on the platform you are building. For the most part, this\nshould work without interference, but if you are using a custom docker image or\ndo not want the default, then you will have to manually update this string. Not\nevery string beaker-hostgenerator uses to populate this variable will be valid.\n\n`docker_image_commands` is automatically populated when generating the hosts\nfile with [beaker-hostgenerator][]. This has already been set for a handful of\nhost types, but may not be set for all.\n\n* TODO I only tried this once using a docker image that already had puppetserver\n    installed as the master host. The image I used took forever to provision,\n    so I gave up. If we want to continue down this route, we need to make sure\n    the setup steps can check if puppetserver has already been installed so that\n    we don't try to install it agian.\n* TODO There's something odd with `docker_mage_entrypoint` versus `docker_cmd`.\n    We should clarify the difference between these two values. I don't quite\n    understand what the difference is between them.\n* TODO These docker containers have to run in privileged mode (or systemd,\n    among possibly other things, won't function as we need them to). This is\n    not ideal if you're testing code that affects your OS (ie running docker on\n    linux without a docker machine in between the container and your laptop).\n    BE CAREFUL\n\n#### Static Hosts\nThis is not recommended unless you are familiar with how beaker and\nbeaker-puppet provision hosts.\n\nTo test on a server that's already been spun up or doesn't require a hypervisor,\nyou should set the name of the host to the FQDN of the server you want to use,\nthen remove the hypervisor and template settings. This is not recommended, and\nyou may run into issues with failures or overwritten configuration due to either\nbeaker provision steps or test provisioning steps.\n```\n---\nHOSTS:\n    azeqdqmk14mvu3g.delivery.puppetlabs.net:\n        platform: el-7-x86_64\n        packaging_platform: el-7-x86_64\n        roles:\n          - master\n```\n\n-------------\n\n## Running Tests\n### Testing with pre-built packages\n```\nbundle exec rake ci:test: SHA={sha|tag}\n```\n\nThis is the primary method that we use to run puppet acceptance tests. It\nrequires puppet-agent packages that have been built with the version of the\npuppet code that you want to test. As building packages usually takes quite a\nbit of time, this method requires some patience. You are required to set `SHA`\nwhen running acceptance tests against pre-built packages.\n\n#### Testing a specific version\nIf you are testing a specific version, `SHA` must be set to a value that exists\non the path `#{ENV['DEV_BUILDS_URL']}/puppet-agent/#{ENV['SHA']}`. Note that\nthis value corresponds to the puppet-agent package, not to puppet.\n`DEV_BUILDS_URL` defaults to the internal build server that is only accessible\nto Puppet, Inc. employees. The method called here depends on information written\nto a yaml file in that directory. Though you can override DEV_BUILDS_URL, the\nautomation here is very specific and likely will not work as you are expecting\nit to.\n\n```\nbundle exec rake ci:test:aio SHA=3cfbac6857c10efc5b1e02262cfd7b849bb9c4b2\n```\n```\nbundle exec rake ci:test:aio SHA=6.0.5\n```\n\n#### Testing Nightlies\nIf you do not have access to internal infrastructure, you can test against\npackages that have been made available on nightlies.puppet.com. Currently, you\ncannot specify a specific version. Instead, you have to use the latest shipped\npackage for the release stream you are interested in. To do this, `SHA` must be\nset to `latest`. If you want to modify the release stream you are testing,\n`RELEASE_STREAM` can be modified. It defaults to `puppet` which should\ncorrespond to the latest stream available. If you want to modify\n`RELEASE_STREAM`, set it to an available repo, such as `puppet5`.\n```\nbundle exec rake ci:test:aio SHA=latest RELEASE_STREAM=puppet5\n```\n\n### Testing with Git\n```\nbundle exec rake ci:test:git SHA={sha|tag|branch}\n```\n\n#### From a repo on a git server\nThough we primarily run acceptance tests against a built package, it is possible\nto run these tests with a git checkout. This is most useful when testing locally\nto speed up the feedback cycle.\n\nWhen testing from a github repo we need to unpack the appropriate\n[runtime archive](https://github.com/puppetlabs/puppet-runtime)\nfor the platform we are testing on. These pre-built archives are stored on an\ninternal server, and are currently only available to Puppet, Inc. employees.\nWith these archives, we get all of the runtime dependencies that are usually\nprovided as a part of the puppet agent package. This allows us to replicate\nthe runtime environment produced via a package install for the purpose of\nrunning acceptance tests.\n\nWhen testing with git, `SHA` can be set to any git artifact: a long sha, a short\nsha, a tag, a branch name, etc. What happens is that we write a gemfile with the\ndetails of the puppet repo, pointing to the artifact referenced with `SHA`. Then\nwhen we run `bundle install` on the testing host, bundler grabs puppet from\nwherever the gemfile points. If the git artifact referenced is not from the\npuppetlabs repo, you can use `FORK` to point to a different github namespace.\nLikewise, if the artifact you want to access is not available on `github.com`\nbut a custom git server, you can set `SERVER` to customize the git uri bundler\npulls from. For more details on these environment variables, run\n`bundle exec rake ci:help`.\n\nAs an example, if I have a development branch\n(`developent/master/major-feature`) that I'm working on and it only exists in my\nfork of puppet (`github.com/joeschmoe/puppet`), then I will run\n```\nbundle exec rake ci:test:git SHA=developent/master/major-feature FORK=joeschmoe\n```\n\nPlease note that any changes you want to test must be pushed up to your github\nserver. This is how we access the code to be tested.\n\n#### From a local repo\nIf you are testing with git and using the docker hypervisor, you can run tests\nagainst the puppet checkout on your local system. You need to update your hosts\nfile to add `mount_folders` to the docker host where you want the checkout of\npuppet to be available. Here, `host_path` is the path to puppet on your local\nmachine. The `container_path` is where puppet will end up on the docker image,\nso you can leave it as `/build/puppet`. Note that although `SHA` is required, it\nis never used in this workflow. For consistency, I would recommend setting `SHA`\nto your working branch name.\n\nWe still need access to our runtime dependencies when testing against a local\ngit checkout. When we are testing with the docker hypervisor, we assume that the\ndocker image you are using will have this. As of this writing (Jan. 2019), the\ndocker image you'll want to use for these tests is not public. The image is\ncalled `agent-runtime-{branch}`, where `{branch}` is the branch of puppet you\nare testing. This image includes everything we build as a part of [the runtime\narchive](https://github.com/puppetlabs/puppet-runtime). These components are\nnormally provided as a part of the puppet agent package.\n```\n---\nHOSTS:\n  debian8-64-1:\n    hypervisor: docker\n    docker_image_entrypoint: \"/sbin/init\"\n    image: pcr-internal.puppet.net/pe-and-platform/agent-runtime-master:201810110.17.gb5afc66\n    platform: debian-8-amd64\n    packaging_platform: debian-8-amd64\n    docker_image_commands:\n      - rm -f /usr/sbin/policy-rc.d\n      - systemctl mask getty@tty1.service getty-static.service\n      - apt-get update && apt-get install -y cron locales-all net-tools wget\n    mount_folders:\n      puppet:\n        host_path: ~/puppet\n        container_path: /build/puppet\n    roles:\n      - agent\n```\n\nFor more details on testing with docker, see [the docker section](#docker).\nRemember that `HOSTS` must be set to your hosts file for the automation to honor\nit.\n\n### Testing with Gems\nCurrently, running acceptance tests with gems is not working.\n```\nbundle exec rake ci:test:gem\n```\n\n### Rerunning Failed Tests\nThe 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.\n\nTo 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.\n```\nbundle exec rake ci:test:aio OPTIONS='--preserve-hosts=always' SHA=6.0.5\n```\nIf 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\n```\nbundle exec beaker exec tests/resource/package/yum.rb,tests/node/check_woy_cache_works.rb\n```\n\nThis should work regardless of which hypervisor or testing method you are using.\n\n-------------\n\n## Writing Tests\nRead more about writing beaker tests in beaker. Check out the [tutorials section](https://github.com/puppetlabs/beaker/tree/master/docs/tutorials)\nand [how to write a quick test](https://github.com/puppetlabs/beaker/blob/master/docs/tutorials/lets_write_a_test.md)\n\n-------------\n\n## Getting Help\n### On the web\n* [Puppet help messageboard](http://puppet.com/community/get-help)\n* [General GitHub documentation](http://help.github.com/)\n### On chat\n* Slack (slack.puppet.com) #testing, #puppet-dev, #windows\n\n[bundler]: https://rubygems.org/gems/bundler\n[rspec-puppet]: http://rspec-puppet.com/\n[rspec-puppet_docs]: http://rspec-puppet.com/documentation/\n[beaker]: https://github.com/puppetlabs/beaker\n[beaker-puppet]: https://github.com/puppetlabs/beaker-puppet\n[beaker-hostgenerator]: https://github.com/puppetlabs/beaker-hostgenerator\n"
  },
  {
    "path": "acceptance/Rakefile",
    "content": "require 'beaker-puppet'\n\nBeaker::DSL::Helpers::RakeHelpers.load_tasks\n\nnamespace :ci do\n  namespace :test do\n    desc <<-EOS\nRun a limited but representative subset of acceptance tests against puppet-agent\n(AIO) packages. This task is intended to reduce testing time on a per-commit\nbasis.\n\n  $ SHA=<full sha> bundle exec rake ci:test:quick\n\nSHA should be the full SHA for the puppet-agent package.\nEOS\n    task :quick => ['ci:check_env', 'ci:gen_hosts'] do\n      ENV['TESTS'] = get_test_sample.join(\",\")\n      Rake::Task[\"ci:test:aio\"].invoke\n    end\n\n    desc <<-EOS\nRun tests on docker quickly and easily. The docker container is set up to mount\nyour puppet directory in the container. This means you can edit code or test\nfiles and rerun tests without reconfiguring your test environment.\n\nDefaults to running all tests unless TESTS is set. TESTS is a comma seperated\nlist of test files to run.\n\n  $ bundle exec rake ci:test:docker TESTS='path/to/test.rb,path/to/another/test.rb'\n\nBy default, tests are run on a centos 7 host. To change the host, set HOSTS to\na valid host string according to beaker-hostgenerator requirements.\n\nAll tests marked with a server tag will be skipped.\n\nThis task skips all cleanup. Please be sure to run `bundle exec beaker destroy`\nto clean up docker containers used for testing.\nEOS\n    task :docker do\n      begin\n        ENV['HOSTS'] ||= 'centos7-64a'\n        ENV['SHA'] ||= `git rev-parse HEAD`.chomp\n        ENV['OPTIONS'] ||= '--preserve-hosts=always'\n        ENV['OPTIONS'] += ' --test-tag-exclude=server'\n        Rake::Task[\"ci:gen_hosts\"].invoke('docker')\n        hosts_file_content = YAML.load_file ENV['HOSTS']\n        hosts_file_content['HOSTS'].each do |host|\n          host[1]['mount_folders'] = {\n            'puppet' => {\n              'host_path' => \"#{File.dirname(__dir__)}\" ,\n              'container_path' => '/build/puppet'\n            }\n          }\n          host[1]['tag'] = 'acceptance_test_host'\n        end\n        File.open(ENV['HOSTS'], \"w\") { |f| f.write(YAML.dump(hosts_file_content)) }\n        Rake::Task[\"ci:test:git\"].invoke\n\n      ensure\n        puts <<-EOF\n\n\n************************\nYou can modify puppet code or tests and rerun tests without modifying your test\nenvironment.\n\nTo rerun a test or set of tests, pass a comma seperated list of tests to:\n\n  $ bundle exec beaker exec path/to/test.rb\n\n  or\n\n  $ bundle exec beaker exec path/to/test.rb,path/to/another/test.rb\n\n************************\nThis task skips all clean up so you can rerun tests. Don't forget to clean up\nafter yourself!\n\nTo clean up the docker containers used to run tests, run:\n\n  $ bundle exec beaker destroy\n\n************************\n\n\n        EOF\n      end\n    end\n  end\n\n  namespace :sync do\n    task :windows do\n      raise 'WIN_MACHINE environment variable is required' unless ENV['WIN_MACHINE']\n      win_machine = ENV['WIN_MACHINE'] + '.delivery.puppetlabs.net'\n      path = ENV['LIB_DIR'] || 'type' # 'lib/puppet' prefix is implicit.\n      dest_path = path.split('/')[0...-1].join\n      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}'\")\n    end\n  end\nend\n\ndef get_test_sample\n  # This set represents a reasonable sample of puppet acceptance tests,\n  # covering a wide range of features and code susceptible to regressions.\n  tests = [ 'tests/direct_puppet/cached_catalog_remediate_local_drift.rb',\n            'tests/resource/file/content_attribute.rb',\n            'tests/face/loadable_from_modules.rb',\n            'tests/language/functions_in_puppet_language.rb',\n            'tests/parser_functions/calling_all_functions.rb',\n            'tests/ticket_4622_filebucket_diff_test.rb',\n            'tests/pluginsync/4420_pluginfacts_should_be_resolvable_on_agent.rb',\n            'tests/ssl/puppet_cert_generate_and_autosign.rb',\n            'tests/resource/package/yum.rb',\n            'tests/resource/service/ticket_5024_systemd_enabling_masked_service.rb',\n            'tests/resource/service/puppet_service_management.rb'\n          ]\n\n  # Add any tests modified within the last two weeks to the list, excluding\n  # deleted ones. We can't rely on --diff-filter, because an acceptance\n  # test may be modified and then deleted in the same time range.\n  modified = `git log --name-only --pretty=\"format:\" --since 2.weeks ./tests`\n  tests += modified.split(\"\\n\").reject do |s|\n    s.empty?\n  end.collect do |s|\n    s.sub('acceptance/', '')\n  end.select do |s|\n    s =~ /\\.rb$/\n  end.find_all do |s|\n    File.exist?(s)\n  end\n\n  tests.uniq.sort\nend\n"
  },
  {
    "path": "acceptance/config/aio/options.rb",
    "content": "{\n  :type                        => 'aio',\n  'is_puppetserver'            => true,\n  'use-service'                => true, # use service scripts to start/stop stuff\n  'puppetservice'              => 'puppetserver',\n  'puppetserver-confdir'       => '/etc/puppetlabs/puppetserver/conf.d',\n  'puppetserver-config'        => '/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf',\n  :post_suite => [\n    'teardown/common/099_Archive_Logs.rb',\n  ],\n}\n"
  },
  {
    "path": "acceptance/config/gem/options.rb",
    "content": "{\n  # Use `git` so that we have a sane ruby environment\n  :type => 'git',\n}\n"
  },
  {
    "path": "acceptance/config/git/options.rb",
    "content": "{\n  :type                        => 'git',\n  :install                     => [\n    'puppet',\n  ],\n  'is_puppetserver'            => false,\n  'use-service'                => true, # use service scripts to start/stop stuff\n  'puppetservice'              => 'puppetserver',\n  'puppetserver-confdir'       => '/etc/puppetlabs/puppetserver/conf.d',\n  'puppetserver-config'        => '/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf'\n}\n"
  },
  {
    "path": "acceptance/config/nodes/aix-53-power.yaml",
    "content": "---\nHOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\n  pe-aix-53-acceptance:\n    roles:\n      - agent\n    platform: aix-5.3-power\n    hypervisor: none\n    vmhostname: pe-aix-53-acceptance.delivery.puppetlabs.net \nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/aix-61-power.yaml",
    "content": "---\nHOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\n  pe-aix-61-acceptance:\n    roles:\n      - agent\n    platform: aix-6.1-power\n    hypervisor: none\n    vmhostname: pe-aix-61-acceptance.delivery.puppetlabs.net\nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/aix-71-power.yaml",
    "content": "---\nHOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\n  pe-aix-71-acceptance:\n    roles:\n      - agent\n    platform: aix-7.1-power\n    hypervisor: none\n    vmhostname: pe-aix-71-acceptance.delivery.puppetlabs.net\nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/gem.yaml",
    "content": "---\nHOSTS:\n  win-2012r2-rubyx86:\n    roles:\n    - agent\n    platform: windows-2012r2-64\n    ruby_arch: x86\n    hypervisor: vmpooler\n    template: win-2012r2-x86_64\n  win-2012r2-rubyx64:\n    roles:\n    - agent\n    platform: windows-2012r2-64\n    ruby_arch: x64\n    hypervisor: vmpooler\n    template: win-2012r2-x86_64\n  osx-1010:\n    roles:\n    - agent\n    platform: osx-10.10-x86_64\n    hypervisor: vmpooler\n    template: osx-1010-x86_64\n  redhat-7:\n    roles:\n    - agent\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/huaweios-6-powerpc.yaml",
    "content": "---\nHOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\n  huawei-ce6850-2-debian-vm-eth0.ops.puppetlabs.net:\n    roles:\n      - agent\n    platform: huaweios-6-powerpc\n    hypervisor: none\nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-5-32ma-32da-32da",
    "content": "HOSTS:\n  centos-5-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-i386\n    template: centos-5-i386\n    hypervisor: vcloud\n  centos-5-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-i386\n    template: centos-5-i386\n    hypervisor: vcloud\n  centos-5-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-i386\n    template: centos-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-5-32mda",
    "content": "HOSTS:\n  centos-5-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-i386\n    template: centos-5-i386\n    hypervisor: vcloud\n  centos-5-i386-agent:\n    roles:\n      - agent\n    platform: el-5-i386\n    template: centos-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-5-64ma-64da-64da",
    "content": "HOSTS:\n  centos-5-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-x86_64\n    template: centos-5-x86_64\n    hypervisor: vcloud\n  centos-5-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-x86_64\n    template: centos-5-x86_64\n    hypervisor: vcloud\n  centos-5-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: centos-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-5-64mda",
    "content": "HOSTS:\n  centos-5-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: centos-5-x86_64\n    hypervisor: vcloud\n  centos-5-x86_64-agent:\n    roles:\n      - agent\n    platform: el-5-x86_64\n    template: centos-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-6-32ma-32da-32da",
    "content": "HOSTS:\n  centos-6-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-i386\n    template: centos-6-i386\n    hypervisor: vcloud\n  centos-6-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-i386\n    template: centos-6-i386\n    hypervisor: vcloud\n  centos-6-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-i386\n    template: centos-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-6-32mda",
    "content": "HOSTS:\n  centos-6-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-i386\n    template: centos-6-i386\n    hypervisor: vcloud\n  centos-6-i386-agent:\n    roles:\n      - agent\n    platform: el-6-i386\n    template: centos-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-6-64ma-64da-64da",
    "content": "HOSTS:\n  centos-6-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-x86_64\n    template: centos-6-x86_64\n    hypervisor: vcloud\n  centos-6-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-x86_64\n    template: centos-6-x86_64\n    hypervisor: vcloud\n  centos-6-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: centos-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-6-64mda",
    "content": "HOSTS:\n  centos-6-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: centos-6-x86_64\n    hypervisor: vcloud\n  centos-6-x86_64-agent:\n    roles:\n      - agent\n    platform: el-6-x86_64\n    template: centos-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/centos-6-64mda-sol-10-64a",
    "content": "HOSTS:\n  centos-6-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: centos-6-x86_64\n    hypervisor: vcloud\n  solaris-10-x86_64-agent:\n    roles:\n      - agent\n    platform: solaris-10-i386\n    template: solaris-10-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-6-32ma-32da-32da",
    "content": "HOSTS:\n  debian-6-i386-master:\n    roles:\n      - master\n      - agent\n    platform: debian-6-i386\n    template: debian-6-i386\n    hypervisor: vcloud\n  debian-6-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: debian-6-i386\n    template: debian-6-i386\n    hypervisor: vcloud\n  debian-6-i386-database:\n    roles:\n      - database\n      - agent\n    platform: debian-6-i386\n    template: debian-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-6-32mda",
    "content": "HOSTS:\n  debian-6-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: debian-6-i386\n    template: debian-6-i386\n    hypervisor: vcloud\n  debian-6-i386-agent:\n    roles:\n      - agent\n    platform: debian-6-i386\n    template: debian-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-6-64ma-64da-64da",
    "content": "HOSTS:\n  debian-6-amd64-master:\n    roles:\n      - master\n      - agent\n    platform: debian-6-amd64\n    template: debian-6-x86_64\n    hypervisor: vcloud\n  debian-6-amd64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: debian-6-amd64\n    template: debian-6-x86_64\n    hypervisor: vcloud\n  debian-6-amd64-database:\n    roles:\n      - database\n      - agent\n    platform: debian-6-amd64\n    template: debian-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-6-64mda",
    "content": "HOSTS:\n  debian-6-amd64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: debian-6-amd64\n    template: debian-6-x86_64\n    hypervisor: vcloud\n  debian-6-amd64-agent:\n    roles:\n      - agent\n    platform: debian-6-amd64\n    template: debian-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-7-32ma-32da-32da",
    "content": "HOSTS:\n  debian-7-i386-master:\n    roles:\n      - master\n      - agent\n    platform: debian-7-i386\n    template: debian-7-i386\n    hypervisor: vcloud\n  debian-7-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: debian-7-i386\n    template: debian-7-i386\n    hypervisor: vcloud\n  debian-7-i386-database:\n    roles:\n      - database\n      - agent\n    platform: debian-7-i386\n    template: debian-7-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-7-32mda",
    "content": "HOSTS:\n  debian-7-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: debian-7-i386\n    template: debian-7-i386\n    hypervisor: vcloud\n  debian-7-i386-agent:\n    roles:\n      - agent\n    platform: debian-7-i386\n    template: debian-7-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-7-64ma-64da-64da",
    "content": "HOSTS:\n  debian-7-amd64-master:\n    roles:\n      - master\n      - agent\n    platform: debian-7-amd64\n    template: debian-7-x86_64\n    hypervisor: vcloud\n  debian-7-amd64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: debian-7-amd64\n    template: debian-7-x86_64\n    hypervisor: vcloud\n  debian-7-amd64-database:\n    roles:\n      - database\n      - agent\n    platform: debian-7-amd64\n    template: debian-7-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-7-64mda",
    "content": "HOSTS:\n  debian-7-amd64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: debian-7-amd64\n    template: debian-7-x86_64\n    hypervisor: vcloud\n  debian-7-amd64-agent:\n    roles:\n      - agent\n    platform: debian-7-amd64\n    template: debian-7-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/debian-7-i386",
    "content": "HOSTS:\n  debian-7-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: debian-7-i386\n    template: debian-7-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-5-32ma-32da-32da",
    "content": "HOSTS:\n  oracle-5-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-i386\n    template: oracle-5-i386\n    hypervisor: vcloud\n  oracle-5-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-i386\n    template: oracle-5-i386\n    hypervisor: vcloud\n  oracle-5-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-i386\n    template: oracle-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-5-32mda",
    "content": "HOSTS:\n  oracle-5-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-i386\n    template: oracle-5-i386\n    hypervisor: vcloud\n  oracle-5-i386-agent:\n    roles:\n      - agent\n    platform: el-5-i386\n    template: oracle-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-5-64ma-64da-64da",
    "content": "HOSTS:\n  oracle-5-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-x86_64\n    template: oracle-5-x86_64\n    hypervisor: vcloud\n  oracle-5-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-x86_64\n    template: oracle-5-x86_64\n    hypervisor: vcloud\n  oracle-5-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: oracle-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-5-64mda",
    "content": "HOSTS:\n  oracle-5-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: oracle-5-x86_64\n    hypervisor: vcloud\n  oracle-5-x86_64-agent:\n    roles:\n      - agent\n    platform: el-5-x86_64\n    template: oracle-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-6-32ma-32da-32da",
    "content": "HOSTS:\n  oracle-6-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-i386\n    template: oracle-6-i386\n    hypervisor: vcloud\n  oracle-6-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-i386\n    template: oracle-6-i386\n    hypervisor: vcloud\n  oracle-6-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-i386\n    template: oracle-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-6-32mda",
    "content": "HOSTS:\n  oracle-6-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-i386\n    template: oracle-6-i386\n    hypervisor: vcloud\n  oracle-6-i386-agent:\n    roles:\n      - agent\n    platform: el-6-i386\n    template: oracle-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-6-64ma-64da-64da",
    "content": "HOSTS:\n  oracle-6-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-x86_64\n    template: oracle-6-x86_64\n    hypervisor: vcloud\n  oracle-6-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-x86_64\n    template: oracle-6-x86_64\n    hypervisor: vcloud\n  oracle-6-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: oracle-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/oracle-6-64mda",
    "content": "HOSTS:\n  oracle-6-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: oracle-6-x86_64\n    hypervisor: vcloud\n  oracle-6-x86_64-agent:\n    roles:\n      - agent\n    platform: el-6-x86_64\n    template: oracle-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-5-32ma-32da-32da",
    "content": "HOSTS:\n  redhat-5-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-i386\n    template: redhat-5-i386\n    hypervisor: vcloud\n  redhat-5-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-i386\n    template: redhat-5-i386\n    hypervisor: vcloud\n  redhat-5-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-i386\n    template: redhat-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-5-32mda",
    "content": "HOSTS:\n  redhat-5-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-i386\n    template: redhat-5-i386\n    hypervisor: vcloud\n  redhat-5-i386-agent:\n    roles:\n      - agent\n    platform: el-5-i386\n    template: redhat-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-5-64ma-64da-64da",
    "content": "HOSTS:\n  redhat-5-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-x86_64\n    template: redhat-5-x86_64\n    hypervisor: vcloud\n  redhat-5-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-x86_64\n    template: redhat-5-x86_64\n    hypervisor: vcloud\n  redhat-5-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: redhat-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-5-64mda",
    "content": "HOSTS:\n  redhat-5-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: redhat-5-x86_64\n    hypervisor: vcloud\n  redhat-5-x86_64-agent:\n    roles:\n      - agent\n    platform: el-5-x86_64\n    template: redhat-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-6-32ma-32da-32da",
    "content": "HOSTS:\n  redhat-6-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-i386\n    template: redhat-6-i386\n    hypervisor: vcloud\n  redhat-6-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-i386\n    template: redhat-6-i386\n    hypervisor: vcloud\n  redhat-6-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-i386\n    template: redhat-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-6-32mda",
    "content": "HOSTS:\n  redhat-6-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-i386\n    template: redhat-6-i386\n    hypervisor: vcloud\n  redhat-6-i386-agent:\n    roles:\n      - agent\n    platform: el-6-i386\n    template: redhat-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-6-64ma-64da-64da",
    "content": "HOSTS:\n  redhat-6-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-x86_64\n    template: redhat-6-x86_64\n    hypervisor: vcloud\n  redhat-6-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-x86_64\n    template: redhat-6-x86_64\n    hypervisor: vcloud\n  redhat-6-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: redhat-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/redhat-6-64mda",
    "content": "HOSTS:\n  redhat-6-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: redhat-6-x86_64\n    hypervisor: vcloud\n  redhat-6-x86_64-agent:\n    roles:\n      - agent\n    platform: el-6-x86_64\n    template: redhat-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-5-32ma-32da-32da",
    "content": "HOSTS:\n  scientific-5-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-i386\n    template: scientific-5-i386\n    hypervisor: vcloud\n  scientific-5-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-i386\n    template: scientific-5-i386\n    hypervisor: vcloud\n  scientific-5-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-i386\n    template: scientific-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-5-32mda",
    "content": "HOSTS:\n  scientific-5-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-i386\n    template: scientific-5-i386\n    hypervisor: vcloud\n  scientific-5-i386-agent:\n    roles:\n      - agent\n    platform: el-5-i386\n    template: scientific-5-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-5-64ma-64da-64da",
    "content": "HOSTS:\n  scientific-5-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-5-x86_64\n    template: scientific-5-x86_64\n    hypervisor: vcloud\n  scientific-5-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-5-x86_64\n    template: scientific-5-x86_64\n    hypervisor: vcloud\n  scientific-5-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: scientific-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-5-64mda",
    "content": "HOSTS:\n  scientific-5-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-5-x86_64\n    template: scientific-5-x86_64\n    hypervisor: vcloud\n  scientific-5-x86_64-agent:\n    roles:\n      - agent\n    platform: el-5-x86_64\n    template: scientific-5-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-6-32ma-32da-32da",
    "content": "HOSTS:\n  scientific-6-i386-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-i386\n    template: scientific-6-i386\n    hypervisor: vcloud\n  scientific-6-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-i386\n    template: scientific-6-i386\n    hypervisor: vcloud\n  scientific-6-i386-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-i386\n    template: scientific-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-6-32mda",
    "content": "HOSTS:\n  scientific-6-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-i386\n    template: scientific-6-i386\n    hypervisor: vcloud\n  scientific-6-i386-agent:\n    roles:\n      - agent\n    platform: el-6-i386\n    template: scientific-6-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-6-64ma-64da-64da",
    "content": "HOSTS:\n  scientific-6-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: el-6-x86_64\n    template: scientific-6-x86_64\n    hypervisor: vcloud\n  scientific-6-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: el-6-x86_64\n    template: scientific-6-x86_64\n    hypervisor: vcloud\n  scientific-6-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: scientific-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/scientific-6-64mda",
    "content": "HOSTS:\n  scientific-6-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: el-6-x86_64\n    template: scientific-6-x86_64\n    hypervisor: vcloud\n  scientific-6-x86_64-agent:\n    roles:\n      - agent\n    platform: el-6-x86_64\n    template: scientific-6-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/sles-11-32ma-32da-32da",
    "content": "HOSTS:\n  sles-11-i386-master:\n    roles:\n      - master\n      - agent\n    platform: sles-11-i386\n    template: sles-11-i386\n    hypervisor: vcloud\n  sles-11-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: sles-11-i386\n    template: sles-11-i386\n    hypervisor: vcloud\n  sles-11-i386-database:\n    roles:\n      - database\n      - agent\n    platform: sles-11-i386\n    template: sles-11-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/sles-11-32mda",
    "content": "HOSTS:\n  sles-11-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: sles-11-i386\n    template: sles-11-i386\n    hypervisor: vcloud\n  sles-11-i386-agent:\n    roles:\n      - agent\n    platform: sles-11-i386\n    template: sles-11-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/sles-11-64ma-64da-64da",
    "content": "HOSTS:\n  sles-11-x86_64-master:\n    roles:\n      - master\n      - agent\n    platform: sles-11-x86_64\n    template: sles-11-x86_64\n    hypervisor: vcloud\n  sles-11-x86_64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: sles-11-x86_64\n    template: sles-11-x86_64\n    hypervisor: vcloud\n  sles-11-x86_64-database:\n    roles:\n      - database\n      - agent\n    platform: sles-11-x86_64\n    template: sles-11-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/sles-11-64mda",
    "content": "HOSTS:\n  sles-11-x86_64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: sles-11-x86_64\n    template: sles-11-x86_64\n    hypervisor: vcloud\n  sles-11-x86_64-agent:\n    roles:\n      - agent\n    platform: sles-11-x86_64\n    template: sles-11-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/solaris-10-64a",
    "content": "HOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vcloud\n    template: redhat-7-x86_64\n  agent:\n    roles:\n      - agent\n    platform: solaris-10-x86_64\n    hypervisor: vcloud\n    template: solaris-10-x86_64\nCONFIG:\n  datastore: instance0\n  resourcepool: delivery/Quality Assurance/FOSS/Dynamic\n  folder: Delivery/Quality Assurance/FOSS/Dynamic\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/pe/solaris-11-64a",
    "content": "HOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vcloud\n    template: redhat-7-x86_64\n  agent:\n    roles:\n      - agent\n    platform: solaris-11-x86_64\n    hypervisor: vcloud\n    template: solaris-11-x86_64\nCONFIG:\n  datastore: instance0\n  resourcepool: delivery/Quality Assurance/FOSS/Dynamic\n  folder: Delivery/Quality Assurance/FOSS/Dynamic\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1004-32ma-32da-32da",
    "content": "HOSTS:\n  ubuntu-1004-i386-master:\n    roles:\n      - master\n      - agent\n    platform: ubuntu-10.04-i386\n    template: ubuntu-1004-i386\n    hypervisor: vcloud\n  ubuntu-1004-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: ubuntu-10.04-i386\n    template: ubuntu-1004-i386\n    hypervisor: vcloud\n  ubuntu-1004-i386-database:\n    roles:\n      - database\n      - agent\n    platform: ubuntu-10.04-i386\n    template: ubuntu-1004-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1004-32mda",
    "content": "HOSTS:\n  ubuntu-1004-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: ubuntu-10.04-i386\n    template: ubuntu-1004-i386\n    hypervisor: vcloud\n  ubuntu-1004-agent:\n    roles:\n      - agent\n    platform: ubuntu-10.04-i386\n    template: ubuntu-1004-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1004-64ma-64da-64da",
    "content": "HOSTS:\n  ubuntu-1004-amd64-master:\n    roles:\n      - master\n      - agent\n    platform: ubuntu-10.04-amd64\n    template: ubuntu-1004-x86_64\n    hypervisor: vcloud\n  ubuntu-1004-amd64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: ubuntu-10.04-amd64\n    template: ubuntu-1004-x86_64\n    hypervisor: vcloud\n  ubuntu-1004-amd64-database:\n    roles:\n      - database\n      - agent\n    platform: ubuntu-10.04-amd64\n    template: ubuntu-1004-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1004-64mda",
    "content": "HOSTS:\n  ubuntu-1004-amd64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: ubuntu-10.04-amd64\n    template: ubuntu-1004-x86_64\n    hypervisor: vcloud\n  ubuntu-1004-agent:\n    roles:\n      - agent\n    platform: ubuntu-10.04-amd64\n    template: ubuntu-1004-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1204-32ma-32da-32da",
    "content": "HOSTS:\n  ubuntu-1204-i386-master:\n    roles:\n      - master\n      - agent\n    platform: ubuntu-12.04-i386\n    template: ubuntu-1204-i386\n    hypervisor: vcloud\n  ubuntu-1204-i386-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: ubuntu-12.04-i386\n    template: ubuntu-1204-i386\n    hypervisor: vcloud\n  ubuntu-1204-i386-database:\n    roles:\n      - database\n      - agent\n    platform: ubuntu-12.04-i386\n    template: ubuntu-1204-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1204-32mda",
    "content": "HOSTS:\n  ubuntu-1204-i386:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: ubuntu-12.04-i386\n    template: ubuntu-1204-i386\n    hypervisor: vcloud\n  ubuntu-1204-agent:\n    roles:\n      - agent\n    platform: ubuntu-12.04-i386\n    template: ubuntu-1204-i386\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1204-64ma-64da-64da",
    "content": "HOSTS:\n  ubuntu-1204-amd64-master:\n    roles:\n      - master\n      - agent\n    platform: ubuntu-12.04-amd64\n    template: ubuntu-1204-x86_64\n    hypervisor: vcloud\n  ubuntu-1204-amd64-dashboard:\n    roles:\n      - dashboard\n      - agent\n    platform: ubuntu-12.04-amd64\n    template: ubuntu-1204-x86_64\n    hypervisor: vcloud\n  ubuntu-1204-amd64-database:\n    roles:\n      - database\n      - agent\n    platform: ubuntu-12.04-amd64\n    template: ubuntu-1204-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/pe/ubuntu-1204-64mda",
    "content": "HOSTS:\n  ubuntu-1204-amd64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: ubuntu-12.04-amd64\n    template: ubuntu-1204-x86_64\n    hypervisor: vcloud\n  ubuntu-1204-agent:\n    roles:\n      - agent\n    platform: ubuntu-12.04-amd64\n    template: ubuntu-1204-x86_64\n    hypervisor: vcloud\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  datastore: instance0\n  folder: Delivery/Quality Assurance/Enterprise/Dynamic\n  resourcepool: delivery/Quality Assurance/Enterprise/Dynamic\n"
  },
  {
    "path": "acceptance/config/nodes/solaris-10-sparc.yaml",
    "content": "---\nHOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\n  solaris-10-sparc:\n    roles:\n      - agent\n    platform: solaris-10-sparc\n    hypervisor: none\n    ip: 10.32.121.124\n    vmhostname: sol10-1.delivery.puppetlabs.net\nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/config/nodes/solaris-11-sparc.yaml",
    "content": "---\nHOSTS:\n  master:\n    roles:\n      - master\n    platform: el-7-x86_64\n    hypervisor: vmpooler\n    template: redhat-7-x86_64\n  solaris-11-sparc:\n    roles:\n      - agent\n    platform: solaris-11-sparc\n    hypervisor: none\n    ip: 10.32.114.245\n    vmhostname: sol11-1.delivery.puppetlabs.net\nCONFIG:\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n"
  },
  {
    "path": "acceptance/fixtures/MockInstaller.cs",
    "content": "/*\n\nThe MockInstaller is a C# class representing a stubbed exe installer. We will\ncompile this class into an installable .exe file.\n\nA MockInstaller _MUST_ come alongside a MockUninstaller, so we can uninstall the\nfake package from the system\n\n*/\nusing System;\n\npublic class MockInstaller\n{   public static void Main()\n   {\n        try\n        {\n            %{install_commands}\n        }\n        catch {\n            Environment.Exit(1003);\n        }\n        string keyName = \"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\";\n        Microsoft.Win32.RegistryKey key;\n        key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(keyName + \"\\\\%{package_display_name}\");\n        /*\n            Puppet deems an exe package 'installable' by identifying whether or not the following registry\n            values exist in the Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\PackageName key:\n\n            * DisplayName\n            * DisplayVersion\n            * UninstallString\n\n            So we must set those values in the registry for this to be an 'installable package' manageable by\n            puppet.\n         */\n        key.SetValue(\"DisplayName\", \"%{package_display_name}\");\n        key.SetValue(\"DisplayVersion\", \"1.0.0\");\n        key.SetValue(\"UninstallString\", @\"%{uninstaller_location}\");\n        key.Close();\n        Console.WriteLine(\"Installing...\");\n   }\n}\n"
  },
  {
    "path": "acceptance/fixtures/MockService.cs",
    "content": "/*\n\nThe MockService is a C# class representing a stubbed service. We will\ncompile this class into the service's .exe file.\n\nHere, we implement four methods:\n    * OnStart    -- called when SCM starts the service\n    * OnPause    -- called when SCM pauses the service\n    * OnContinue -- called when SCM resumes a paused service\n    * OnStop     -- called when SCM stops a service\n\nBefore calling one of these 'On' methods, the ServiceBase class sets\nthe service state to the corresponding PENDING state. The service state\nis in this PENDING state until the 'On' method is finished, whereby it is\nthen transitioned into the corresponding final state. Thus if we sleep for a\nfew seconds in the 'On' method, then note that SCM will report our service\nstate as being in the PENDING state while we're asleep. For example, if the\n'On' method is 'OnStart', the service state is set to START_PENDING before\ncalling 'OnStart', is START_PENDING while executing 'OnStart', and then is set\nto RUNNING after exiting 'OnStart'.\n\nWhen testing the Windows service provider, we really want to test to ensure\nthat it handles the state transitions correctly. For example, we want to\ncheck that:\n    * It waits for the appropriate PENDING state to finish\n    * It sets the service state to the appropriate final state\n\nThe reason we want to do this is because our service provider is communicating\nwith SCM directly, which does not care how the service implements these\ntransitions so long as it implements them. C#'s ServiceBase class implements\nthese state transitions for us. Thus by going to sleep in all of our 'On' methods,\nwe simulate transitioning to the corresponding PENDING state. When we wake-up\nand exit the 'On' method, we will transition to the appropriate final state.\n\nNOTE: Normally, you're supposed to have the service thread in a separate process.\nThe 'On' methods in this class would send signals to the service thread and then wait\nfor those signals to be processed. Sending and waiting for these signals is quite\nhard and unnecessary for our use-case, which is why our MockService does not have\nthe service thread.\n\n*/\n\nusing System;\nusing System.ServiceProcess;\n\npublic class MockService : ServiceBase {\n  public static void Main() {\n    System.ServiceProcess.ServiceBase.Run(new MockService());\n  }\n\n  public MockService() {\n    ServiceName = \"%{service_name}\";\n    CanStop = true;\n    CanPauseAndContinue = true;\n  }\n\n  private void StubPendingTransition(int seconds) {\n    RequestAdditionalTime(2000);\n    System.Threading.Thread.Sleep(seconds * 1000);\n  }\n\n  protected override void OnStart(string [] args) {\n    StubPendingTransition(%{start_sleep});\n  }\n\n  protected override void OnPause() {\n    StubPendingTransition(%{pause_sleep});\n  }\n\n  protected override void OnContinue() {\n    StubPendingTransition(%{continue_sleep});\n  }\n\n  protected override void OnStop() {\n    StubPendingTransition(%{stop_sleep});\n  }\n}\n"
  },
  {
    "path": "acceptance/fixtures/MockUninstaller.cs",
    "content": "/*\n\nThe MockUninstaller is a C# class representing a stubbed exe uninstaller. We will\ncompile this class into an usable .exe file.\n\nA MockInstaller _MUST_ come alongside a MockUninstaller, so we can uninstall the\nfake package from the system\n\n*/\nusing System;\n\npublic class MockInstaller\n{   public static void Main()\n   {\n        try\n        {\n            %{uninstall_commands}\n        }\n        catch {\n            Environment.Exit(1003);\n        }\n        string keyName = \"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\";\n        Console.WriteLine(\"Uninstalling...\");\n        /*\n            Remove the entire registry key created by the installer exe\n         */\n        using (Microsoft.Win32.RegistryKey _key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(keyName, true))\n        {\n            _key.DeleteSubKeyTree(\"%{package_display_name}\");\n        }\n   }\n}\n"
  },
  {
    "path": "acceptance/fixtures/debian-repo/Release",
    "content": "Archive: stable\nComponent: contrib\nOrigin: Puppet\nLabel: Puppet\nArchitecture: i386"
  },
  {
    "path": "acceptance/fixtures/el-repo/repodata/repomd.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<repomd xmlns=\"http://linux.duke.edu/metadata/repo\" xmlns:rpm=\"http://linux.duke.edu/metadata/rpm\">\n  <revision>1631716408</revision>\n  <data type=\"primary\">\n    <checksum type=\"sha256\">0427a2b1b650922e9e7359c09be8820caa3b1ae72efef4998fd7a50fbd3a858c</checksum>\n    <open-checksum type=\"sha256\">c838f308b3673ee45b64f5b4f594b1428ec76c7a09045ddbcafd37cddf6c9806</open-checksum>\n    <location href=\"repodata/0427a2b1b650922e9e7359c09be8820caa3b1ae72efef4998fd7a50fbd3a858c-primary.xml.gz\"/>\n    <timestamp>1631716408</timestamp>\n    <size>853</size>\n    <open-size>2885</open-size>\n  </data>\n  <data type=\"filelists\">\n    <checksum type=\"sha256\">12382dd1ca2ce49561d698430501e038a8694b64a5d69bdb7133bff1be5bd4ab</checksum>\n    <open-checksum type=\"sha256\">ee99cce50b52fdbe9986c16a1896b3f621fd678fa3e5731f153458a9b2770aa0</open-checksum>\n    <location href=\"repodata/12382dd1ca2ce49561d698430501e038a8694b64a5d69bdb7133bff1be5bd4ab-filelists.xml.gz\"/>\n    <timestamp>1631716408</timestamp>\n    <size>362</size>\n    <open-size>799</open-size>\n  </data>\n  <data type=\"other\">\n    <checksum type=\"sha256\">c274906bddc4277eb4a9f54ad0bfb833ae2c34209d2c8059ee187aa409886ead</checksum>\n    <open-checksum type=\"sha256\">537bf4c94bce5d5bf7a7ecca3e612028ba87b7bf9c2cf7c952ec902d6e3259ee</open-checksum>\n    <location href=\"repodata/c274906bddc4277eb4a9f54ad0bfb833ae2c34209d2c8059ee187aa409886ead-other.xml.gz\"/>\n    <timestamp>1631716408</timestamp>\n    <size>297</size>\n    <open-size>493</open-size>\n  </data>\n  <data type=\"primary_db\">\n    <checksum type=\"sha256\">68861daea8ff469f3418abd08697b408df11c8079b0b24178a4e2b4bd8a7102e</checksum>\n    <open-checksum type=\"sha256\">f50da72da990487e2a53e9414b4b5fe0b47357e7ddebb8ded8450ea268d1a1f2</open-checksum>\n    <location href=\"repodata/68861daea8ff469f3418abd08697b408df11c8079b0b24178a4e2b4bd8a7102e-primary.sqlite.bz2\"/>\n    <timestamp>1631716408</timestamp>\n    <size>2176</size>\n    <open-size>106496</open-size>\n    <database_version>10</database_version>\n  </data>\n  <data type=\"filelists_db\">\n    <checksum type=\"sha256\">4427b13c52edea24fc19776198a99611464b3c67f7828aeed8c5d20f3d8b1c02</checksum>\n    <open-checksum type=\"sha256\">6aa60e9b3cb91ce9240c648f2f72eeec9a9f29fa809e4fdb4d05de334e9d2f0d</open-checksum>\n    <location href=\"repodata/4427b13c52edea24fc19776198a99611464b3c67f7828aeed8c5d20f3d8b1c02-filelists.sqlite.bz2\"/>\n    <timestamp>1631716408</timestamp>\n    <size>973</size>\n    <open-size>28672</open-size>\n    <database_version>10</database_version>\n  </data>\n  <data type=\"other_db\">\n    <checksum type=\"sha256\">653202d291344674c0e6c2547647d09c2b0044ec96986b9c62f74dc49f15a3db</checksum>\n    <open-checksum type=\"sha256\">44c6e7b3d018f1c291708a368223ada428e13d5dd2ecc08c56321e074006a655</open-checksum>\n    <location href=\"repodata/653202d291344674c0e6c2547647d09c2b0044ec96986b9c62f74dc49f15a3db-other.sqlite.bz2\"/>\n    <timestamp>1631716408</timestamp>\n    <size>746</size>\n    <open-size>24576</open-size>\n    <database_version>10</database_version>\n  </data>\n</repomd>\n"
  },
  {
    "path": "acceptance/fixtures/manifest_large_exported_classes_node.pp",
    "content": "class foo ($bar) {\n  @@notify { 'foo': }\n}\n@@file { \"somedir/${name}_${munin_port_real}\":\n  ensure => present,\n  content => template(\"munin/defaultclient.erb\"),\n}\n# Collect all exported files\nFile <<||>>\n\n# Compile the munin.conf with a local header\nconcatenated_file { \"/etc/munin/munin.conf\":\n    dir => somedir,\n    header => \"/etc/munin/munin.conf.header\",\n}\nhosting_vserver_configuration {\n    \"erics\":\n        domain => \"orange.co\",\n        type => \"friend\",\n        context => 13,\n        ip => \"255.255.255.254\", prefix => 27,\n        admin_user => \"erict\", admin_user_name => \"hello, its me\",\n        admin_user_email => \"erict@orange.co\",\n        customer => \"hello? is it me?\",\n        admin_password => file(\"/etc/puppet/secrets/hosting/erict_passwd\"),\n}\nclass davids_black_co_at {\n    ## Create users for my parents and my grandmother\n    hosting::user {\n        rztt: realname => \"some other rztt\",\n            uid => 2001, admin => true;\n        same: realname => \"could be same\",\n            uid => 2002;\n        imapersontoodamnit: realname => \"some one else\",\n            uid => 2003;\n    }\n\n    # Install git.black.co.at\n    include git::daemon\n    include git::web\n    git::web::export { [manifests, \"puppet-trunk\"]: }\n\n    # Provision an additional mysql database on the database server\n    hosting::database { \"fogbugz\": type => mysql }\n    # Create another VirtualHost\n    apache2::site { \"local-fogbugz\":\n        source => \"puppet://$servername/files/hosting/erict/sites/local-fogbugz\"\n    }\n}\nnode backuppc {\n        # only use the smarthost\n        $mta = ssmtp\n        # this is a vserver on this host, so register correctly in nagios\n        $nagios_parent = \"orange.co\"\n        # I'm sharing an IP here, so those things have to have their own ports\n        $apache2_port = 8080\n        $munin_port = 5008\n        $munin_stats_port = 8667\n\n        # default configuration\n        include dbp\n\n        # configure the backuppc server\n        include backuppc::server\n}\n"
  },
  {
    "path": "acceptance/fixtures/sles-repo/repodata/repomd.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<repomd xmlns=\"http://linux.duke.edu/metadata/repo\" xmlns:rpm=\"http://linux.duke.edu/metadata/rpm\">\n <revision>1586872174</revision>\n<data type=\"filelists\">\n  <checksum type=\"sha\">57a44da7ea9c26d4f438d6ca5da3c561acfaabe1</checksum>\n  <open-checksum type=\"sha\">4de6823d4c547a4d0126368662a25086ebba0b48</open-checksum>\n  <location href=\"repodata/filelists.xml.gz\"/>\n  <timestamp>1586872175</timestamp>\n  <size>346</size>\n  <open-size>1143</open-size>\n</data>\n<data type=\"other\">\n  <checksum type=\"sha\">e8a80b739fad3deba689620da7947ea0e4bb8ec6</checksum>\n  <open-checksum type=\"sha\">438d8c0a6507319e488dabcb95a37e6e3a984086</open-checksum>\n  <location href=\"repodata/other.xml.gz\"/>\n  <timestamp>1586872175</timestamp>\n  <size>289</size>\n  <open-size>563</open-size>\n</data>\n<data type=\"primary\">\n  <checksum type=\"sha\">76eab89dc047884727aa27a4523e21728f356eb1</checksum>\n  <open-checksum type=\"sha\">803b916a92d58ee369063232de96c62125e1e483</open-checksum>\n  <location href=\"repodata/primary.xml.gz\"/>\n  <timestamp>1586872175</timestamp>\n  <size>817</size>\n  <open-size>3654</open-size>\n</data>\n</repomd>\n"
  },
  {
    "path": "acceptance/lib/acceptance_spec_helper.rb",
    "content": "require 'fileutils'\n\ndir = File.expand_path(File.dirname(__FILE__))\n$LOAD_PATH.unshift dir\n\nRSpec.configure do |config|\n  config.mock_with :mocha\nend\n"
  },
  {
    "path": "acceptance/lib/helper.rb",
    "content": "$LOAD_PATH << File.expand_path(File.dirname(__FILE__))\n\nrequire 'beaker-puppet'\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/agent_fqdn_utils.rb",
    "content": "module Puppet\n  module Acceptance\n    module AgentFqdnUtils\n\n      @@hostname_to_fqdn = {}\n\n      # convert from an Beaker::Host (agent) to the systems fqdn as returned by facter\n      def agent_to_fqdn(agent)\n        unless @@hostname_to_fqdn.has_key?(agent.hostname)\n          @@hostname_to_fqdn[agent.hostname] = on(agent, facter('networking.fqdn')).stdout.chomp\n        end\n        @@hostname_to_fqdn[agent.hostname]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/aix_util.rb",
    "content": "module Puppet\n  module Acceptance\n    module AixUtil\n      def to_kv_array(attributes)\n        attributes.map { |attribute, value| \"#{attribute}=#{value}\" }\n      end\n\n      def assert_object_attributes_on(host, object_get, object, expected_attributes)\n        host.send(object_get, object) do |result|\n          actual_attrs_kv_pairs = result.stdout.chomp.split(' ')[(1..-1)]\n          actual_attrs = actual_attrs_kv_pairs.map do |kv_pair|\n            attribute, value = kv_pair.split('=')\n            next nil unless value\n            [attribute, value]\n          end.compact.to_h\n\n          expected_attributes.each do |attribute, value|\n            attribute_str = \"attributes[#{object}][#{attribute}]\"\n            actual_value = actual_attrs[attribute]\n            assert_match(\n              /\\A#{value}\\z/,\n              actual_value,\n              \"EXPECTED: #{attribute_str} = \\\"#{value}\\\", ACTUAL:  #{attribute_str} = \\\"#{actual_value}\\\"\"\n            )\n          end\n        end\n      end\n\n      def assert_puppet_changed_object_attributes(result, object_resource, object, changed_attributes)\n        stdout = result.stdout.chomp\n        changed_attributes.each do |attribute, value|\n          prefix = /#{object_resource}\\[#{object}\\].*attributes changed.*/\n          attribute_str = \"attributes[#{object}][#{attribute}]\"\n    \n          assert_match(\n            /#{prefix}#{attribute}=#{value}/,\n            stdout,\n            \"Puppet did not indicate that #{attribute_str} changed to #{value}\"\n          )\n        end\n      end\n\n      def object_resource_manifest(object_resource, object, params)\n        params_str = params.map do |param, value|\n          value_str = value.to_s\n          value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n    \n          \"  #{param} => #{value_str}\"\n        end.join(\",\\n\")\n    \n        <<-MANIFEST\n#{object_resource} { '#{object}':\n  #{params_str}\n}\nMANIFEST\n      end\n\n      def run_attribute_management_tests(object_resource, id_property, initial_attributes, changed_attributes)\n        object_get = \"#{object_resource}_get\".to_sym\n        object_absent = \"#{object_resource}_absent\".to_sym\n        \n        name = \"obj\"\n        teardown do\n          agents.each { |agent| agent.send(object_absent, name) }\n        end\n\n        current_attributes = initial_attributes.dup\n\n        agents.each do |agent|\n          agent.send(object_absent, name)\n\n          # We extract the code for this step as a lambda because we will be checking\n          # for this case (1) Before the object has been created and (2) After the\n          # object has been created (towards the end). We do this because in (1), Puppet\n          # does not trigger the property setters after creating the object, while in (2)\n          # it does. These are two different scenarios that we want to check.\n          step_run_errors_when_property_is_passed_as_attribute = lambda do\n            manifest = object_resource_manifest(\n              object_resource,\n              name,\n              attributes: current_attributes.merge({ 'id' => '15' })\n            )\n     \n            apply_manifest_on(agent, manifest) do |result|\n              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}\")\n            end\n          end\n\n  \n          step \"Ensure that Puppet errors if a Puppet property is passed in as an AIX attribute when creating the #{object_resource}\" do\n            step_run_errors_when_property_is_passed_as_attribute.call\n          end\n      \n          step \"Ensure that the #{object_resource} can be created with the specified attributes\" do\n            manifest = object_resource_manifest(\n              object_resource,\n              name,\n              ensure: :present,\n              attributes: to_kv_array(current_attributes)\n            )\n\n            apply_manifest_on(agent, manifest)\n            assert_object_attributes_on(agent, object_get, name, current_attributes)\n          end\n\n          step \"Ensure that Puppet noops when the specified attributes are already set\" do\n            manifest = object_resource_manifest(\n              object_resource,\n              name,\n              attributes: to_kv_array(current_attributes)\n            )\n\n            apply_manifest_on(agent, manifest, catch_changes: true)\n          end\n\n          # Remember the changed attribute's old values\n          old_attributes = current_attributes.select { |k, _| changed_attributes.keys.include?(k) }\n\n          step \"Ensure that Puppet updates only the specified attributes and nothing else\" do\n            current_attributes = current_attributes.merge(changed_attributes)\n      \n            manifest = object_resource_manifest(\n              object_resource,\n              name,\n              attributes: to_kv_array(current_attributes)\n            )\n      \n            apply_manifest_on(agent, manifest) do |result|\n              assert_puppet_changed_object_attributes(\n                result,\n                object_resource.capitalize,\n                name,\n                changed_attributes\n              )\n            end\n            assert_object_attributes_on(agent, object_get, name, current_attributes)\n          end\n\n          step \"Ensure that Puppet accepts a hash for the attributes property\" do\n            # We want to see if Puppet will do something with the attributes property\n            # when we pass it in as a hash so that it does not just pass validation\n            # and end up noop-ing. Let's set one of our attributes back to its old\n            # value in order to simulate an actual change.\n            attribute = old_attributes.keys.first\n            old_value = old_attributes.delete(attribute)\n            current_attributes[attribute] = old_value\n\n            manifest = object_resource_manifest(\n              object_resource,\n              name,\n              attributes: current_attributes\n            )\n\n            apply_manifest_on(agent, manifest)\n            assert_object_attributes_on(agent, object_get, name, current_attributes)\n          end\n\n          step \"Ensure that `puppet resource #{object_resource}` outputs valid Puppet code\" do\n            on(agent, puppet(\"resource #{object_resource} #{name}\")) do |result|\n              manifest = result.stdout.chomp\n              apply_manifest_on(agent, manifest)\n            end\n          end\n\n          step \"Ensure that Puppet errors if a Puppet property is passed in as an AIX attribute after #{object_resource} has been created\" do\n            step_run_errors_when_property_is_passed_as_attribute.call\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/classifier_utils.rb",
    "content": "require 'httparty'\nrequire 'tempfile'\nrequire 'stringio'\nrequire 'uuidtools'\nrequire 'json'\nrequire 'pp'\n\nmodule Puppet\n  module Acceptance\n    module ClassifierUtils\n      DEFAULT_GROUP_ID = \"00000000-0000-4000-8000-000000000000\"\n      SSL_PORT = 4433\n      PREFIX = \"/classifier-api\"\n\n      # Keep track of our local tmpdirs for cleanup\n      def self.tmpdirs\n        @classifier_utils_tmpdirs ||= []\n      end\n\n      # PE creates a \"Production environment\" group during installation which\n      # all nodes are a member of by default.  This method just looks up this\n      # group and returns its uuid so that other methods may reference it.\n      def get_production_environment_group_uuid\n        step \"Get classifier groups so we can locate the 'Production environment' group\"\n        response = classifier_handle.get(\"/v1/groups\")\n        assert_equal(200, response.code, \"Unable to get classifer groups: #{response.body}\")\n\n        groups_json = response.body\n        groups = JSON.parse(groups_json)\n\n        if production_environment = groups.find { |g| g['name'] == 'Production environment' }\n          production_environment['id']\n        else\n          nil\n        end\n      end\n\n      # Create a Classifier Group which by default will apply to all of the passed\n      # nodes.  The Group will merge in the passed group_hash which will be converted\n      # into the json body for a Classifier PUT /v1/groups/:id request.\n      #\n      # A teardown body is registered to delete the created group at the end of the test.\n      #\n      # @returns String the created uuid for the group.\n      def create_group_for_nodes(nodes, group_hash)\n        group_uuid = UUIDTools::UUID.random_create()\n        response = nil\n\n        teardown do\n          step \"Deleting group #{group_uuid}\" do\n            response = classifier_handle.delete(\"/v1/groups/#{group_uuid}\")\n            assert_equal(204, response.code, \"Failed to delete group #{group_uuid}, #{response.code}:#{response.body}\")\n          end if response && response.code == 201\n        end\n\n        teardown do\n          step \"Cleaning up classifier certs on test host\" do\n            cleanup_local_classifier_certs\n          end\n        end\n\n        hostnames = nodes.map { |n| n.hostname }\n        step \"Add group #{group_uuid} for #{hostnames.join(\", \")}\"\n        rule = hostnames.inject([\"or\"]) do |r,name|\n          r << [\"~\", \"name\", name]\n          r\n        end\n        # In order to override the environment for test nodes, we need the\n        # groups we create to be a child of this \"Production environment\" group,\n        # otherwise we get a classification error from the conflicting groups.\n        parent = get_production_environment_group_uuid || Puppet::Acceptance::ClassifierUtils::DEFAULT_GROUP_ID \n        body = {\n          \"description\" => \"A classification group for the following acceptance test nodes: (#{hostnames.join(\", \")})\",\n          \"parent\" => parent,\n          \"rule\" => rule,\n          \"classes\" => {}\n        }.merge group_hash\n        response = classifier_handle.put(\"/v1/groups/#{group_uuid}\", :body => body.to_json)\n\n        assert_equal(201, response.code, \"Unexpected response code: #{response.code}, #{response.body}\")\n\n        return group_uuid\n      end\n\n      # Creates a group which allows the given nodes to specify their own environments.\n      # Will be torn down at the end of the test.\n      def classify_nodes_as_agent_specified(nodes)\n        create_group_for_nodes(nodes, {\n          \"name\" => \"Agent Specified Test Nodes\",\n          \"environment\" => \"agent-specified\",\n          \"environment_trumps\" => true,\n          \"description\" => \"The following acceptance suite nodes (#{nodes.map { |n| n.hostname }.join(\", \")}) expect to be able to specify their environment for tesing purposes.\",\n        })\n      end\n\n      def classify_nodes_as_agent_specified_if_classifer_present\n        classifier_node = false\n        begin\n          classifier_node = find_only_one(:classifier)\n        rescue Beaker::DSL::Outcomes::FailTest\n        end\n\n        if classifier_node || master.is_pe?\n          classify_nodes_as_agent_specified(agents)\n        end\n      end\n\n      def classifier_host\n        find_only_one(:classifier)\n      rescue Beaker::DSL::Outcomes::FailTest\n        # fallback to master since currently the sqautils genconfig does not recognize\n        # a classifier role.\n        master\n      end\n\n      def master_cert\n        @master_cert ||= on(master, \"cat `puppet config print hostcert`\", :silent => true).stdout\n      end\n\n      def master_key\n        @master_key ||= on(master, \"cat `puppet config print hostprivkey`\", :silent => true).stdout\n      end\n\n      def master_ca_cert_file\n        unless @ca_cert_file\n          ca_cert = on(master, \"cat `puppet config print localcacert`\", :silent => true).stdout\n          cert_dir = Dir.mktmpdir(\"pe_classifier_certs\")\n          Puppet::Acceptance::ClassifierUtils.tmpdirs << cert_dir\n\n          @ca_cert_file = File.join(cert_dir, \"cacert.pem\")\n          # RFC 1421 states PEM is 7-bit ASCII https://tools.ietf.org/html/rfc1421\n          File.open(@ca_cert_file, \"w:ASCII\") do |f|\n            f.write(ca_cert)\n          end\n        end\n        @ca_cert_file\n      end\n\n      def cleanup_local_classifier_certs\n        Puppet::Acceptance::ClassifierUtils.tmpdirs.each do |d|\n          FileUtils.rm_rf(d)\n        end\n      end\n\n      def clear_classifier_utils_cache\n        @master_cert = nil\n        @master_key = nil\n        @ca_cert_file = nil\n        @classifier_handle = nil\n      end\n\n      def classifier_handle(options = {})\n        unless @classifier_handle\n          server = options[:server] || classifier_host.reachable_name\n          port = options[:port] || SSL_PORT\n          prefix = options[:prefix] || PREFIX\n          cert = options[:cert] || master_cert\n          key = options[:key] || master_key\n          ca_cert_file = options[:ca_cert_file] || master_ca_cert_file\n          logger = options[:logger] || self.logger\n\n          # HTTParty performs a lot of configuration at the class level.\n          # This is inconvenient for our needs because we don't have the\n          # server/cert info available at the time the class is loaded.  I'm\n          # sidestepping this by generating an anonymous class on the fly when\n          # the test code actually requests a handle to the classifier.\n          @classifier_handle = Class.new do\n            include HTTParty\n            extend Classifier\n            @debugout = StringIO.new\n            @logger = logger\n            base_uri(\"https://#{server}:#{port}#{prefix}\")\n            debug_output(@debugout)\n            headers({'Content-Type' => 'application/json'})\n            pem(cert + key)\n            ssl_ca_file(ca_cert_file)\n          end\n        end\n        @classifier_handle\n      end\n\n      # Handle logging\n      module Classifier\n\n        [:head, :get, :post, :put, :delete].each do |method|\n          define_method(method) do |*args, &block|\n            log_output do\n              super(*args, &block)\n            end\n          end\n        end\n\n        private\n\n        # Ensure that the captured debugging output is logged to Beaker.\n        def log_output\n          yield\n        ensure\n          @debugout.rewind\n          @debugout.each_line { |l| @logger.info(l) }\n          @debugout.truncate(0)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/classifier_utils_spec.rb",
    "content": "require File.join(File.dirname(__FILE__),'../../acceptance_spec_helper.rb')\nrequire 'puppet/acceptance/classifier_utils'\nrequire 'stringio'\nrequire 'beaker'\n\nmodule ClassifierUtilsSpec\ndescribe 'ClassifierUtils' do\n\n  class ATestCase < Beaker::TestCase\n    include Puppet::Acceptance::ClassifierUtils\n    attr_accessor :logger, :hosts\n\n    def initialize\n      @logger = Logger.new\n      @hosts = []\n    end\n\n    def logger\n      @logger\n    end\n\n    def teardown\n    end\n\n    class Logger\n      attr_reader :destination\n\n      def initialize\n        @destination = StringIO.new\n      end\n\n      def info(log)\n        @destination << (log)\n      end\n    end\n  end\n\n  let(:testcase) { ATestCase.new }\n  let(:handle) { testcase.classifier_handle(\n      :server => 'foo',\n      :cert => 'cert',\n      :key => 'key',\n      :ca_cert_file => 'file'\n    )\n  }\n\n  it \"provides a handle to the classifier service\" do\n    handle.expects(:perform_request).with(Net::HTTP::Get, '/hi', {})\n    handle.get('/hi')\n  end\n\n  it \"logs output from the http connection attempt\" do\n    TCPSocket.expects(:open).raises('no-connection')\n    OpenSSL::X509::Certificate.expects(:new).with('certkey').returns(stub('cert'))\n    OpenSSL::PKey::RSA.expects(:new).with('certkey', nil).returns(stub('key'))\n    expect { handle.get('/hi') }.to raise_error('no-connection')\n    expect(testcase.logger.destination.string).to match(/opening connection to foo/)\n  end\n\n  it \"creates an agent-specified environment group for a passed set of nodes\" do\n    nodes = [\n      stub_everything('master', :hostname => 'abcmaster', :[] => ['master'] ),\n      stub_everything('agent', :hostname => 'defagent', :[] => ['agent'] ),\n    ]\n    testcase.hosts = nodes\n\n    uuid = nil\n    handle.expects(:perform_request).with do |method,url,body_hash|\n      expect(method).to eq(Net::HTTP::Put)\n      test_regex = %r{/v1/groups/(\\w+-\\w+-\\w+-\\w+-\\w+)}\n      md = test_regex.match(url)\n      expect(uuid = md[1]).to_not be_nil\n      expect(body_hash[:body]).to match(/environment[^:]*:[^:]*agent-specified/)\n    end.returns(\n        stub_everything('response', :code => 201))\n\n    expect(testcase.classify_nodes_as_agent_specified(nodes).to_s).to eq(uuid)\n  end\nend\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/common_utils.rb",
    "content": "module Puppet\n  module Acceptance\n    module BeakerUtils\n      # TODO: This should be added to Beaker\n      def assert_matching_arrays(expected, actual, message = \"\")\n        assert_equal(expected.sort, actual.sort, message)\n      end\n    end\n\n    module PackageUtils\n      def package_present(host, package, version = nil)\n          host.install_package(package, '', version)\n      end\n\n      def package_absent(host, package, cmdline_args = '', opts = {})\n          host.uninstall_package(package, cmdline_args, opts)\n      end\n    end\n\n    module CommandUtils\n      def ruby_command(host)\n        \"env PATH=\\\"#{host['privatebindir']}:${PATH}\\\" ruby\"\n      end\n      module_function :ruby_command\n\n      def gem_command(host, type='aio')\n        if type == 'aio'\n          if host['platform'] =~ /windows/\n            \"env PATH=\\\"#{host['privatebindir']}:${PATH}\\\" cmd /c gem\"\n          else\n            \"env PATH=\\\"#{host['privatebindir']}:${PATH}\\\" gem\"\n          end\n        else\n          on(host, 'which gem').stdout.chomp\n        end\n      end\n      module_function :gem_command\n    end\n\n    module ManifestUtils\n      def resource_manifest(resource, title, params = {})\n        params_str = params.map do |param, value|\n          # This is not quite correct for all parameter values,\n          # but it is good enough for most purposes.\n          value_str = value.to_s\n          value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n          \"  #{param} => #{value_str}\"\n        end.join(\",\\n\")\n\n        <<-MANIFEST\n#{resource} { '#{title}':\n  #{params_str}\n}\nMANIFEST\n      end\n\n      def file_manifest(path, params = {})\n        resource_manifest('file', path, params)\n      end\n\n      def user_manifest(username, params = {})\n        resource_manifest('user', username, params)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/environment_utils.rb",
    "content": "require 'puppet/acceptance/module_utils'\n\nmodule Puppet\n  module Acceptance\n    module EnvironmentUtils\n      include Puppet::Acceptance::ModuleUtils\n\n      # Generate puppet manifest for the creation of an environment with\n      # the given modulepath and manifest and env_name.  The created environment\n      # will have on testing_mod module, and manifest site.pp which includes it.\n      #\n      # @param options [Hash<Sym,String>]\n      # @option options [String] :modulepath Modules directory\n      # @option options [String] :manifest Manifest directory\n      # @option options [String] :env_name Environment name\n      # @return [String] Puppet manifest to create the environment files\n      def generate_environment(options)\n        modulepath = options[:modulepath]\n        manifestpath = options[:manifestpath]\n        env_name = options[:env_name]\n\n        <<-MANIFEST_SNIPPET\n          file {\n            ###################################################\n            # #{env_name}\n        #{generate_module(\"testing_mod\", env_name, modulepath)}\n\n            \"#{manifestpath}\":;\n            \"#{manifestpath}/site.pp\":\n              ensure => file,\n              mode => \"0640\",\n              content => '\n                notify { \"in #{env_name} site.pp\": }\n                include testing_mod\n              '\n            ;\n          }\n        MANIFEST_SNIPPET\n      end\n\n      # Generate one module's manifest code.\n      def generate_module(module_name, env_name, modulepath)\n        <<-MANIFEST_SNIPPET\n            \"#{modulepath}\":;\n            \"#{modulepath}/#{module_name}\":;\n            \"#{modulepath}/#{module_name}/manifests\":;\n\n            \"#{modulepath}/#{module_name}/manifests/init.pp\":\n              ensure => file,\n              mode => \"0640\",\n              content => 'class #{module_name} {\n                notify { \"include #{env_name} #{module_name}\": }\n              }'\n            ;\n        MANIFEST_SNIPPET\n      end\n\n      # Default, legacy, dynamic and directory environments\n      # using generate_manifest(), all rooted in testdir.\n      #\n      # @param [String] testdir path to the temp directory which will be the confdir all\n      #   the environments live in\n      # @return [String] Puppet manifest to generate all of the environment files.\n      def environment_manifest(testdir)\n        <<-MANIFEST\n          File {\n            ensure => directory,\n            owner => #{master.puppet['user']},\n            group => #{master.puppet['group']},\n            mode => \"0750\",\n          }\n\n          file { \"#{testdir}\": }\n\n        #{generate_environment(\n            :modulepath => \"#{testdir}/modules\",\n            :manifestpath => \"#{testdir}/manifests\",\n            :env_name => \"default environment\")}\n\n        #{generate_environment(\n            :modulepath => \"#{testdir}/testing-modules\",\n            :manifestpath => \"#{testdir}/testing-manifests\",\n            :env_name => \"legacy testing environment\")}\n\n          file {\n            \"#{testdir}/dynamic\":;\n            \"#{testdir}/dynamic/testing\":;\n          }\n\n        #{generate_environment(\n            :modulepath => \"#{testdir}/dynamic/testing/modules\",\n            :manifestpath => \"#{testdir}/dynamic/testing/manifests\",\n            :env_name => \"dynamic testing environment\")}\n\n          file {\n            \"#{testdir}/environments\":;\n            \"#{testdir}/environments/testing\":;\n          }\n\n        #{generate_environment(\n            :modulepath => \"#{testdir}/environments/testing/modules\",\n            :manifestpath => \"#{testdir}/environments/testing/manifests\",\n            :env_name => \"directory testing environment\")}\n\n          file {\n            \"#{testdir}/environments/testing_environment_conf\":;\n          }\n\n        #{generate_environment(\n            :modulepath => \"#{testdir}/environments/testing_environment_conf/nonstandard-modules\",\n            :manifestpath => \"#{testdir}/environments/testing_environment_conf/nonstandard-manifests\",\n            :env_name => \"directory testing with environment.conf\")}\n\n          file { \"#{testdir}/environments/testing_environment_conf/environment.conf\":\n            ensure => file,\n            mode => \"0640\",\n            content => '\n              modulepath = nonstandard-modules:$basemodulepath\n              manifest = nonstandard-manifests\n              config_version = local-version.sh\n            '\n          }\n\n          file {\n            \"#{testdir}/environments/testing_environment_conf/local-version.sh\":\n              ensure => file,\n              mode => \"0640\",\n              content => '#! /usr/bin/env bash\n              echo \"local testing_environment_conf\"'\n            ;\n          }\n\n          ###################\n          # Services\n\n          file {\n            \"#{testdir}/services\":;\n            \"#{testdir}/services/testing\":;\n        #{generate_module('service_mod',\n                            \"service testing environment\",\n                            \"#{testdir}/services/testing/modules\")}\n          }\n\n          #######################\n          # Config version script\n\n          file {\n            \"#{testdir}/static-version.sh\":\n              ensure => file,\n              mode => \"0640\",\n              content => '#! /usr/bin/env bash\n              echo \"static\"'\n            ;\n          }\n        MANIFEST\n      end\n\n      def get_directory_hash_from(host, path)\n        dir_hash = {}\n        on(host, \"ls #{path}\") do |result|\n          result.stdout.split.inject(dir_hash) do |hash,f|\n            hash[f] = \"#{path}/#{f}\"\n            hash\n          end\n        end\n        dir_hash\n      end\n\n      def safely_shadow_directory_contents_and_yield(host, original_path, new_path, &block)\n        original_files = get_directory_hash_from(host, original_path)\n        new_files = get_directory_hash_from(host, new_path)\n        conflicts = original_files.keys & new_files.keys\n\n        step \"backup original files\" do\n          conflicts.each do |c|\n            on(host, \"mv #{original_files[c]} #{original_files[c]}.bak\")\n          end\n        end\n\n        step \"shadow original files with temporary files\" do\n          new_files.each do |name,full_path_name|\n            on(host, \"cp -R #{full_path_name} #{original_path}/#{name}\")\n          end\n        end\n\n        new_file_list = new_files.keys.map { |name| \"#{original_path}/#{name}\" }.join(' ')\n        step \"open permissions to 755 on all temporary files copied into working dir and set ownership\" do\n          on(host, \"chown -R #{host.puppet['user']}:#{host.puppet['group']} #{new_file_list}\")\n          on(host, \"chmod -R 755 #{new_file_list}\")\n        end\n\n        if host.check_for_command(\"selinuxenabled\")\n          result = on(host, \"selinuxenabled\", :acceptable_exit_codes => [0,1])\n\n          if result.exit_code == 0\n            step \"mirror selinux contexts\" do\n              context = on(host, \"matchpathcon #{original_path}\").stdout.chomp.split(' ')[1]\n              on(host, \"chcon -R #{context} #{new_file_list}\")\n            end\n          end\n        end\n\n        yield\n\n      ensure\n        step \"clear out the temporary files\" do\n          files_to_delete = new_files.keys.map { |name| \"#{original_path}/#{name}\" }\n          on(host, \"rm -rf #{files_to_delete.join(' ')}\")\n        end\n        step \"move the shadowed files back to their original places\" do\n          conflicts.each do |c|\n            on(host, \"mv #{original_files[c]}.bak #{original_files[c]}\")\n          end\n        end\n      end\n\n      # Stand up a puppet master on the master node with the given master_opts\n      # using the passed envdir as the source of the puppet environment files,\n      # and passed confdir as the directory to use for the temporary\n      # puppet.conf. It then runs through a series of environment tests for the\n      # passed environment and returns a hashed structure of the results.\n      #\n      # @return [Hash<Beaker::Host,Hash<Sym,Beaker::Result>>] Hash of\n      #   Beaker::Hosts for each agent run keyed to a hash of Beaker::Result\n      #   objects keyed by each subtest that was performed.\n      def use_an_environment(environment, description, master_opts, envdir, confdir, options = {})\n        master_puppet_conf = master_opts.dup # shallow clone\n\n        results = {}\n        safely_shadow_directory_contents_and_yield(master, puppet_config(master, 'codedir', section: 'master'), envdir) do\n          config_print = options[:config_print]\n          directory_environments = options[:directory_environments]\n\n          with_puppet_running_on(master, master_puppet_conf, confdir) do\n            agents.each do |agent|\n              agent_results = results[agent] = {}\n\n              step \"puppet agent using #{description} environment\"\n              args = \"-t\", \"--server\", master\n              args << [\"--environment\", environment] if environment\n              # Test agents configured to use directory environments (affects environment\n              # loading on the agent, especially with regards to requests/node environment)\n              args << \"--environmentpath='$confdir/environments'\" if directory_environments && agent != master\n              on(agent, puppet(\"agent\", *args), :acceptable_exit_codes => (0..255)) do |result|\n                agent_results[:puppet_agent] = result\n              end\n\n              args = [\"--trace\"]\n              args << [\"--environment\", environment] if environment\n\n              step \"print puppet config for #{description} environment\"\n              on(master, puppet(*([\"config\", \"print\", \"basemodulepath\", \"modulepath\", \"manifest\", \"config_version\", config_print] + args)), :acceptable_exit_codes => (0..255)) do |result|\n                agent_results[:puppet_config] = result\n              end\n\n              step \"puppet apply using #{description} environment\"\n              on(master, puppet(*([\"apply\", '-e', '\"include testing_mod\"'] + args)), :acceptable_exit_codes => (0..255)) do |result|\n                agent_results[:puppet_apply] = result\n              end\n            end\n          end\n        end\n\n        return results\n      end\n\n      # For each Beaker::Host in the results Hash, generates a chart, comparing\n      # the expected exit code and regexp matches from expectations to the\n      # Beaker::Result.output for a particular command that was executed in the\n      # environment.  Outputs either 'ok' or text highlighting the errors, and\n      # returns false if any errors were found.\n      #\n      # @param [Hash<Beaker::Host,Hash<Sym,Beaker::Result>>] results\n      # @param [Hash<Sym,Hash{Sym => Integer,Array<Regexp>}>] expectations\n      # @return [Array] Returns an empty array of there were no failures, or an\n      #   Array of failed cases.\n      def review_results(results, expectations)\n        failed = []\n\n        results.each do |agent, agent_results|\n          divider = \"-\" * 79\n\n          logger.info divider\n          logger.info \"For: (#{agent.name}) #{agent}\"\n          logger.info divider\n\n          agent_results.each do |testname, execution_results|\n            expected_exit_code = expectations[testname][:exit_code]\n            match_tests = expectations[testname][:matches] || []\n            not_match_tests = expectations[testname][:does_not_match] || []\n            expect_failure = expectations[testname][:expect_failure]\n            notes = expectations[testname][:notes]\n\n            errors = []\n\n            if execution_results.exit_code != expected_exit_code\n              errors << \"To exit with an exit code of '#{expected_exit_code}', instead of '#{execution_results.exit_code}'\"\n            end\n\n            match_tests.each do |regexp|\n              if execution_results.output !~ regexp\n                errors << \"#{errors.empty? ? \"To\" : \"And\"} match: #{regexp}\"\n              end\n            end\n\n            not_match_tests.each do |regexp|\n              if execution_results.output =~ regexp\n                errors << \"#{errors.empty? ? \"Not to\" : \"And not\"} match: #{regexp}\"\n              end\n            end\n\n            error_msg = \"Expected the output:\\n#{execution_results.output}\\n#{errors.join(\"\\n\")}\" unless errors.empty?\n\n            case_failed = case\n              when errors.empty? && expect_failure then 'ok - failed as expected'\n              when errors.empty? && !expect_failure then 'ok'\n              else '*UNEXPECTED FAILURE*'\n            end\n            logger.info \"#{testname}: #{case_failed}\"\n            if case_failed == 'ok - failed as expected'\n              logger.info divider\n              logger.info \"Case is known to fail as follows:\\n#{execution_results.output}\\n\"\n            elsif case_failed == '*UNEXPECTED FAILURE*'\n              failed << \"Unexpected failure for #{testname}\"\n              logger.info divider\n              logger.info \"#{error_msg}\"\n            end\n\n            logger.info(\"------\\nNotes: #{notes}\") if notes\n            logger.info divider\n          end\n        end\n\n        return failed\n      end\n\n      def assert_review(review)\n        failures = []\n        review.each do |failed|\n          if !failed.empty?\n            problems = \"Problems in the output reported above:\\n  #{failed}\"\n            logger.warn(problems)\n            failures << problems\n          end\n        end\n        assert failures.empty?, \"Failed Review:\\n\\n#{failures.join(\"\\n\")}\\n\"\n      end\n\n      # generate a random string of 6 letters and numbers.  NOT secure\n      def random_string\n        [*('a'..'z'),*('0'..'9')].shuffle[0,8].join\n      end\n      private :random_string\n\n      # if the first test to call this has changed the environmentpath, this will cause trouble\n      #   maybe not the best idea to memoize this?\n      def environmentpath\n        @@memoized_environmentpath ||= master.puppet['environmentpath']\n      end\n      module_function :environmentpath\n\n      # create a tmpdir to hold a temporary environment bound by puppet environment naming restrictions\n      # symbolically link environment into environmentpath\n      # we can't use the temp_file utils in our own lib because host.tmpdir violates puppet's naming requirements\n      # in rare cases we want to do this on agents when testing things that use the default manifest\n      def mk_tmp_environment_with_teardown(host, environment)\n        # add the tmp_environment to a set to ensure no collisions\n        @@tmp_environment_set ||= Set.new\n        deadman = 100; loop_num = 0\n        while @@tmp_environment_set.include?(tmp_environment = environment.downcase + '_' + random_string) do\n          break if (loop_num = loop_num + 1) > deadman\n        end\n        @@tmp_environment_set << tmp_environment\n        tmpdir = File.join('','tmp',tmp_environment)\n        on host, \"mkdir -p #{tmpdir}/manifests #{tmpdir}/modules; chmod -R 755 #{tmpdir}\"\n\n        # register teardown to remove the link below\n        teardown do\n          on host, \"rm -rf #{File.join(environmentpath,tmp_environment)}\"\n        end\n\n        # WARNING: this won't work with filesync (symlinked environments are not supported)\n        on host, \"mkdir -p #{environmentpath}; ln -sf #{tmpdir} #{File.join(environmentpath,tmp_environment)}\"\n        return tmp_environment\n      end\n      module_function :mk_tmp_environment_with_teardown\n\n      # create sitepp in a tmp_environment as created by mk_tmp_environment_with_teardown\n      def create_sitepp(host, tmp_environment, file_content)\n        file_path = File.join('','tmp',tmp_environment,'manifests','site.pp')\n        create_remote_file(host, file_path, file_content)\n        on host, \"chmod -R 755 #{file_path}\"\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/environment_utils_spec.rb",
    "content": "require File.join(File.dirname(__FILE__),'../../acceptance_spec_helper.rb')\nrequire 'puppet/acceptance/environment_utils'\n\nmodule EnvironmentUtilsSpec\ndescribe 'EnvironmentUtils' do\n  class ATestCase\n    include Puppet::Acceptance::EnvironmentUtils\n\n    def step(str)\n      yield\n    end\n\n    def on(host, command, options = nil)\n      stdout = host.do(command, options)\n      yield TestResult.new(stdout) if block_given?\n    end\n  end\n\n  class TestResult\n    attr_accessor :stdout\n\n    def initialize(stdout)\n      self.stdout = stdout\n    end\n  end\n\n  class TestHost\n    attr_accessor :did, :directories, :attributes\n\n    def initialize(directories, attributes = {})\n      self.directories = directories\n      self.did = []\n      self.attributes = attributes\n    end\n\n    def do(command, options)\n      did << (options.nil? ? command : [command, options])\n      case command\n      when /^ls (.*)/ then directories[$1]\n      end\n    end\n\n    def [](param)\n      attributes[param]\n    end\n  end\n\n  let(:testcase) { ATestCase.new }\n  let(:host) { TestHost.new(directory_contents, 'user' => 'root', 'group' => 'puppet') }\n  let(:directory_contents) do\n    {\n      '/etc/puppetlabs/puppet' => 'foo bar baz widget',\n      '/tmp/dir'    => 'foo dingo bar thing',\n    }\n  end\n\n  it \"runs the block of code\" do\n    ran_code = false\n    testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') do\n      ran_code = true\n    end\n    expect(ran_code).to be true\n    expect(host.did).to eq([\n      \"ls /etc/puppetlabs/puppet\",\n      \"ls /tmp/dir\",\n      \"mv /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/foo.bak\",\n      \"mv /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/bar.bak\",\n      \"cp -R /tmp/dir/foo /etc/puppetlabs/puppet/foo\",\n      \"cp -R /tmp/dir/dingo /etc/puppetlabs/puppet/dingo\",\n      \"cp -R /tmp/dir/bar /etc/puppetlabs/puppet/bar\",\n      \"cp -R /tmp/dir/thing /etc/puppetlabs/puppet/thing\",\n      \"chown -R root:puppet /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"chmod -R 770 /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"rm -rf /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"mv /etc/puppetlabs/puppet/foo.bak /etc/puppetlabs/puppet/foo\",\n      \"mv /etc/puppetlabs/puppet/bar.bak /etc/puppetlabs/puppet/bar\"\n    ])\n  end\n\n  it \"backs up the original items that are shadowed by tmp items\" do\n    testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {}\n    expect(host.did.grep(%r{mv /etc/puppetlabs/puppet/\\w+ })).to eq([\n      \"mv /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/foo.bak\",\n      \"mv /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/bar.bak\",\n    ])\n  end\n\n  it \"copies in all the tmp items into the working dir\" do\n    testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {}\n    expect(host.did.grep(%r{cp})).to eq([\n      \"cp -R /tmp/dir/foo /etc/puppetlabs/puppet/foo\",\n      \"cp -R /tmp/dir/dingo /etc/puppetlabs/puppet/dingo\",\n      \"cp -R /tmp/dir/bar /etc/puppetlabs/puppet/bar\",\n      \"cp -R /tmp/dir/thing /etc/puppetlabs/puppet/thing\",\n    ])\n  end\n\n  it \"opens the permissions on all copied files to 770 and sets ownership based on host settings\" do\n    testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {}\n    expect(host.did.grep(%r{ch(mod|own)})).to eq([\n      \"chown -R root:puppet /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"chmod -R 770 /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n    ])\n  end\n\n  it \"deletes all the tmp items from the working dir\" do\n    testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {}\n    expect(host.did.grep(%r{rm})).to eq([\n      \"rm -rf /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n    ])\n  end\n\n  it \"replaces the original items that had been shadowed into the working dir\" do\n    testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {}\n    expect(host.did.grep(%r{mv /etc/puppetlabs/puppet/\\w+\\.bak})).to eq([\n      \"mv /etc/puppetlabs/puppet/foo.bak /etc/puppetlabs/puppet/foo\",\n      \"mv /etc/puppetlabs/puppet/bar.bak /etc/puppetlabs/puppet/bar\"\n    ])\n  end\n\n  it \"always cleans up, even if the code we yield to raises an error\" do\n    expect do\n      testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') do\n        raise 'oops'\n      end\n    end.to raise_error('oops')\n    expect(host.did).to eq([\n      \"ls /etc/puppetlabs/puppet\",\n      \"ls /tmp/dir\",\n      \"mv /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/foo.bak\",\n      \"mv /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/bar.bak\",\n      \"cp -R /tmp/dir/foo /etc/puppetlabs/puppet/foo\",\n      \"cp -R /tmp/dir/dingo /etc/puppetlabs/puppet/dingo\",\n      \"cp -R /tmp/dir/bar /etc/puppetlabs/puppet/bar\",\n      \"cp -R /tmp/dir/thing /etc/puppetlabs/puppet/thing\",\n      \"chown -R root:puppet /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"chmod -R 770 /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"rm -rf /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing\",\n      \"mv /etc/puppetlabs/puppet/foo.bak /etc/puppetlabs/puppet/foo\",\n      \"mv /etc/puppetlabs/puppet/bar.bak /etc/puppetlabs/puppet/bar\"\n    ])\n  end\nend\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/i18n_utils.rb",
    "content": "module Puppet\n  module Acceptance\n    module I18nUtils\n\n      # try to enable the locale's for a given language on the agent and return the preferred language name\n      #\n      # @param agent [string] the agent to check the locale configuration on\n      # @param language [string] the language attempt to configure if needed\n      # @return language [string] the language string to use on the agent node, will return nil if not available\n      def enable_locale_language(agent, language)\n        if agent['platform'] =~ /ubuntu/\n          on(agent, 'locale -a') do |locale_result|\n            if locale_result.stdout !~ /#{language}/\n              on(agent, \"locale-gen --lang #{language}\")\n            end\n          end\n        elsif agent['platform'] =~ /debian/\n          on(agent, 'locale -a') do |locale_result|\n            if locale_result.stdout !~ /#{language}/\n              on(agent, \"cp /etc/locale.gen /etc/locale.gen.orig ; sed -e 's/# #{language}/#{language}/' /etc/locale.gen.orig > /etc/locale.gen\")\n              on(agent, 'locale-gen')\n            end\n          end\n        end\n        return language_name(agent, language)\n      end\n\n      # figure out the preferred language string for the requested language if the language is configured on the system\n      def language_name(agent, language)\n        step \"PLATFORM #{agent['platform']}\"\n          on(agent, 'locale -a') do |locale_result|\n            [\"#{language}.utf8\", \"#{language}.UTF-8\", language].each do |lang|\n              return lang if locale_result.stdout =~ /#{lang}/\n            end\n          end\n        return nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/i18ndemo_utils.rb",
    "content": "module Puppet\nmodule Acceptance\n  module I18nDemoUtils\n\n    require 'puppet/acceptance/i18n_utils'\n    extend Puppet::Acceptance::I18nUtils\n\n    I18NDEMO_NAME = \"i18ndemo\"\n    I18NDEMO_MODULE_NAME = \"eputnam-#{I18NDEMO_NAME}\"\n\n    def configure_master_system_locale(language)\n      language = enable_locale_language(master, language)\n      fail_test(\"puppet server machine is missing #{language} locale. help...\") if language.nil?\n\n      on(master, \"localectl set-locale LANG=#{language}\")\n      on(master, \"service #{master['puppetservice']} restart\")\n    end\n\n    def reset_master_system_locale\n      language = language_name(master, 'en_US') || 'en_US'\n      on(master, \"localectl set-locale LANG=#{language}\")\n      on(master, \"service #{master['puppetservice']} restart\")\n    end\n\n    def install_i18n_demo_module(node, environment=nil)\n      env_options = environment.nil? ? '' : \"--environment #{environment}\"\n      on(node, puppet(\"module install #{I18NDEMO_MODULE_NAME} #{env_options}\"))\n    end\n\n    def uninstall_i18n_demo_module(node, environment=nil)\n      env_options = environment.nil? ? '' : \"--environment #{environment}\"\n      [I18NDEMO_MODULE_NAME, 'puppetlabs-stdlib', 'puppetlabs-translate'].each do |module_name|\n        on(node, puppet(\"module uninstall #{module_name} #{env_options}\"), :acceptable_exit_codes => [0,1])\n      end\n      var_dir = on(node, puppet('config print vardir')).stdout.chomp\n      on(node, \"rm -rf '#{File.join(var_dir, 'locales', 'ja')}' '#{File.join(var_dir, 'locales', 'fi')}'\")\n    end\n  end\nend\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/module_utils.rb",
    "content": "module Puppet\n  module Acceptance\n    module ModuleUtils\n\n      # Return an array of module paths for a given host.\n      #\n      # Example return value:\n      #\n      # [\n      #   \"/etc/puppetlabs/code/environments/production/modules\",\n      #   \"/etc/puppetlabs/code/modules\",\n      #   \"/opt/puppet/share/puppet/modules\",\n      # ]\n      #\n      # @param host [String] hostname\n      # @return [Array] paths for found modulepath\n      def get_modulepaths_for_host(host)\n        environment = on(host, puppet(\"config print environment\")).stdout.chomp\n        on(host, puppet(\"config print modulepath --environment #{environment}\")).stdout.chomp.split(host['pathseparator'])\n      end\n\n      # Return a string of the default (first) path in modulepath for a given host.\n      #\n      # Example return value:\n      #\n      #   \"/etc/puppetlabs/code/environments/production/modules\"\n      #\n      # @param host [String] hostname\n      # @return [String] first path for found modulepath\n      def get_default_modulepath_for_host(host)\n        get_modulepaths_for_host(host)[0]\n      end\n\n      # Return an array of paths to installed modules for a given host.\n      #\n      # Example return value:\n      #\n      # [\n      #   \"/opt/puppet/share/puppet/modules/apt\",\n      #   \"/opt/puppet/share/puppet/modules/auth_conf\",\n      #   \"/opt/puppet/share/puppet/modules/concat\",\n      # ]\n      #\n      # @param host [String] hostname\n      # @return [Array] paths for found modules\n      def get_installed_modules_for_host(host)\n        on(host, puppet('module list --render-as json')) do |result|\n          str  = result.stdout.lines.to_a.last\n          pat = /\\(([^()]+)\\)/\n          mods =  str.scan(pat).flatten\n          return mods\n        end\n      end\n\n      # Return a hash of array of paths to installed modules for a hosts.\n      # The individual hostnames are the keys of the hash. The only value\n      # for a given key is an array of paths for the found modules.\n      #\n      # Example return value:\n      #\n      # {\n      #   \"my_master\" =>\n      #     [\n      #       \"/opt/puppet/share/puppet/modules/apt\",\n      #       \"/opt/puppet/share/puppet/modules/auth_conf\",\n      #       \"/opt/puppet/share/puppet/modules/concat\",\n      #     ],\n      #   \"my_agent01\" =>\n      #     [\n      #       \"/opt/puppet/share/puppet/modules/apt\",\n      #       \"/opt/puppet/share/puppet/modules/auth_conf\",\n      #       \"/opt/puppet/share/puppet/modules/concat\",\n      #     ],\n      # }\n      #\n      # @param hosts [Array] hostnames\n      # @return [Hash] paths for found modules indexed by hostname\n      def get_installed_modules_for_hosts(hosts)\n        mods  = {}\n        hosts.each do |host|\n          mods[host] = get_installed_modules_for_host host\n        end\n        return mods\n      end\n\n      # Compare the module paths in given hashes and remove paths that\n      # are were not present in the first hash. The use case for this\n      # method is to remove any modules that were installed during the\n      # course of a test run.\n      #\n      # Installed module hashes would be gathered using the\n      # `get_+installed_module_for_hosts` command in the setup stage\n      # and teardown stages of a test. These hashes would be passed into\n      # this method in order to find modules installed during the test\n      # and delete them in order to return the SUT environments to their\n      # initial state.\n      #\n      # TODO: Enhance to take versions into account, so that upgrade/\n      # downgrade events during a test does not persist in the SUT\n      # environment.\n      #\n      # @param beginning_hash [Hash] paths for found modules indexed\n      #   by hostname. Taken in the setup stage of a test.\n      # @param ending_hash [Hash] paths for found modules indexed\n      #   by hostname. Taken in the teardown stage of a test.\n      def rm_installed_modules_from_hosts(beginning_hash, ending_hash)\n        ending_hash.each do |host, mod_array|\n          mod_array.each do |mod|\n            if ! beginning_hash[host].include? mod\n              on host, \"rm -rf '#{mod}'\"\n            end\n          end\n        end\n      end\n\n      # Convert a semantic version number string to an integer.\n      #\n      # Example return value given an input of '1.2.42':\n      #\n      #   10242\n      #\n      # @param semver [String] semantic version number\n      def semver_to_i( semver )\n        # semver assumed to be in format <major>.<minor>.<patch>\n        # calculation assumes that each segment is < 100\n        tmp = semver.split('.')\n        tmp[0].to_i * 10000 + tmp[1].to_i * 100 + tmp[2].to_i\n      end\n\n      # Compare two given semantic version numbers.\n      #\n      # Returns an integer indicating the relationship between the two:\n      #   0 indicates that both are equal\n      #   a value greater than 0 indicates that the semver1 is greater than semver2\n      #   a value less than 0 indicates that the semver1 is less than semver2\n      #\n      def semver_cmp( semver1, semver2 )\n        semver_to_i(semver1) - semver_to_i(semver2)\n      end\n\n      # Assert that a module was installed according to the UI..\n      #\n      # This is a wrapper to centralize the validation about how\n      # the UI responded that a module was installed.\n      # It is called after a call # to `on ( host )` and inspects\n      # STDOUT for specific content.\n      #\n      # @param stdout [String]\n      # @param module_author [String] the author portion of a module name\n      # @param module_name [String] the name portion of a module name\n      # @param module_verion [String] the version of the module to compare to\n      #     installed version\n      # @param compare_op [String] the operator for comparing the verions of\n      #     the installed module\n      def assert_module_installed_ui( stdout, module_author, module_name, module_version = nil, compare_op = nil )\n        valid_compare_ops = {'==' => 'equal to', '>' => 'greater than', '<' => 'less than'}\n        assert_match(/#{module_author}-#{module_name}/, stdout,\n              \"Notice that module '#{module_author}-#{module_name}' was installed was not displayed\")\n        if version\n          /#{module_author}-#{module_name} \\(.*v(\\d+\\.\\d+\\.\\d+)/ =~ stdout\n          installed_version = Regexp.last_match[1]\n          if valid_compare_ops.include? compare_op\n            assert_equal( true, semver_cmp(installed_version, module_version).send(compare_op, 0),\n              \"Installed version '#{installed_version}' of '#{module_name}' was not #{valid_compare_ops[compare_op]} '#{module_version}'\")\n          end\n        end\n      end\n\n      # Assert that a module is installed on disk.\n      #\n      # @param host [HOST] the host object to make the remote call on\n      # @param module_name [String] the name portion of a module name\n      # @param optional moduledir [String, Array] the path where the module should be, will\n      #        iterate over components of the modulepath by default.\n      def assert_module_installed_on_disk(host, module_name, moduledir=nil)\n        moduledir ||= get_modulepaths_for_host(host)\n        modulepath = moduledir.is_a?(Array) ? moduledir : [moduledir]\n        moduledir= nil\n\n        modulepath.each do |i|\n          # module directory should exist\n          if on(host, %Q{[ -d \"#{i}/#{module_name}\" ]}, :acceptable_exit_codes => (0..255)).exit_code == 0\n            moduledir = i\n          end\n        end\n        fail_test('module directory not found') unless moduledir\n\n        owner = ''\n        group = ''\n        on host, %Q{ls -ld \"#{moduledir}\"} do\n          listing = stdout.split(' ')\n          owner = listing[2]\n          group = listing[3]\n        end\n\n        # A module's files should have:\n        #     * a mode of 644 (755, if they're a directory)\n        #     * owner == owner of moduledir\n        #     * group == group of moduledir\n        on host, %Q{ls -alR \"#{moduledir}/#{module_name}\"} do\n          listings = stdout.split(\"\\n\")\n          listings = listings.grep(/^[bcdlsp-]/)\n          listings = listings.reject { |l| l =~ /\\.\\.$/ }\n\n          listings.each do |line|\n            fileinfo = parse_ls(line)\n            assert_equal owner, fileinfo[:owner]\n            assert_equal group, fileinfo[:group]\n\n            if fileinfo[:filetype] == 'd'\n              assert_equal 'rwx', fileinfo[:perms][:user]\n              assert_equal 'r-x', fileinfo[:perms][:group]\n              assert_equal 'r-x', fileinfo[:perms][:other]\n            else\n              assert_equal 'rw-', fileinfo[:perms][:user]\n              assert_equal 'r--', fileinfo[:perms][:group]\n              assert_equal 'r--', fileinfo[:perms][:other]\n            end\n          end\n        end\n      end\n\n      LS_REGEX = %r[(.)(...)(...)(...).?[[:space:]]+\\d+[[:space:]]+([[:word:]]+)[[:space:]]+([[:word:]]+).*[[:space:]]+([[:graph:]]+)$]\n\n      def parse_ls(line)\n        match = line.match(LS_REGEX)\n        if match.nil?\n          fail_test \"#{line.inspect} doesn't match ls output regular expression\"\n        end\n\n        captures = match.captures\n\n        {\n          :filetype => captures[0],\n          :perms => {\n            :user => captures[1],\n            :group => captures[2],\n            :other => captures[3],\n          },\n          :owner => captures[4],\n          :group => captures[5],\n          :file  => captures[6]\n        }\n      end\n      private :parse_ls\n\n      # Assert that a module is not installed on disk.\n      #\n      # @param host [HOST] the host object to make the remote call on\n      # @param module_name [String] the name portion of a module name\n      # @param optional moduledir [String, Array] the path where the module should be, will\n      #        iterate over components of the modulepath by default.\n      def assert_module_not_installed_on_disk(host, module_name, moduledir=nil)\n        moduledir ||= get_modulepaths_for_host(host)\n        modulepath = moduledir.is_a?(Array) ? moduledir : [moduledir]\n        moduledir= nil\n\n        modulepath.each do |i|\n          # module directory should not exist\n          on host, %Q{[ ! -d \"#{i}/#{module_name}\" ]}\n        end\n      end\n\n      # Create a simple directory environment and puppet.conf at :tmpdir.\n      #\n      # @note Also registers a teardown block to remove generated files.\n      #\n      # @param tmpdir [String] directory to contain all the\n      #   generated environment files\n      # @return [String] path to the new puppet configuration file defining the\n      #   environments\n      def generate_base_directory_environments(tmpdir)\n        puppet_conf = \"#{tmpdir}/puppet2.conf\"\n        dir_envs = \"#{tmpdir}/environments\"\n\n        step 'Configure a the direnv directory environment'\n        apply_manifest_on master, %Q{\n          File {\n            ensure => directory,\n            owner => #{master.puppet['user']},\n            group => #{master.puppet['group']},\n            mode => \"0750\",\n          }\n          file {\n            [\n              '#{dir_envs}',\n              '#{dir_envs}/direnv',\n            ]:\n          }\n\n          file {\n            '#{puppet_conf}':\n              ensure => file,\n              content => \"\n                [main]\n                environmentpath=#{dir_envs}\n              \"\n          }\n        }\n\n        return puppet_conf\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/puppet_type_test_tools.rb",
    "content": "require 'puppet/acceptance/environment_utils'\n\nmodule Puppet\n  module Acceptance\n    module PuppetTypeTestTools\n      include Puppet::Acceptance::EnvironmentUtils # for now, just for #random_string\n\n      # FIXME: yardocs\n      # TODO: create resource class which contains its manifest chunk, and assertions\n      #   can be an array or singular, holds the manifest and the assertion_code\n      #   has getter for the manifest\n      #   has #run_assertions(BeakerResult or string)\n      def generate_manifest(test_resources)\n        manifest = ''\n        test_resources = [test_resources].flatten # ensure it's an array so we enumerate properly\n        test_resources.each do |resource|\n          manifest << resource[:pre_code] + \"\\n\" if resource[:pre_code]\n          namevar = (resource[:parameters][:namevar] if resource[:parameters]) || \"#{resource[:type]}_#{random_string}\"\n          # ensure these are double quotes around the namevar incase users puppet-interpolate inside it\n          # FIXME: add test ^^\n          manifest << resource[:type] + '{\"' + namevar + '\":' if resource[:type]\n          if resource[:parameters]\n            resource[:parameters].each do |key,value|\n              next if key == :namevar\n              manifest << \"#{key} => #{value},\"\n            end\n          end\n          manifest << \"}\\n\" if resource[:type]\n        end\n        return manifest\n      end\n\n      def generate_assertions(test_resources)\n        assertion_code = ''\n        test_resources = [test_resources].flatten # ensure it's an array so we enumerate properly\n        test_resources.each do |resource|\n          if resource[:assertions]\n            resource[:assertions] = [resource[:assertions]].flatten # ensure it's an array so we enumerate properly\n            resource[:assertions].each do |assertion_type|\n              expect_failure = false\n              if assertion_type[:expect_failure]\n                expect_failure = true\n                assertion_code << \"expect_failure '#{assertion_type[:expect_failure][:message]}' do\\n\"\n                # delete the message\n                assertion_type[:expect_failure].delete(:message)\n                # promote the hash in expect_failure\n                assertion_type = assertion_type[:expect_failure]\n                assertion_type.delete(:expect_failure)\n              end\n\n              # ensure all the values are arrays\n              assertion_values = [assertion_type.values].flatten\n              assertion_values.each do |assertion_value|\n                # TODO: non matching asserts?\n                # TODO: non stdout? (support stdout, stderr, exit_code)\n                # TODO: what about checking resource state on host (non agent/apply #on use)?\n                if assertion_type.keys.first =~ /assert_match/\n                  assert_msg = 'found '\n                elsif assertion_type.keys.first =~ /refute_match/\n                  assert_msg = 'did not find '\n                else\n                  assert_msg = ''\n                end\n                if assertion_value.is_a?(String)\n                  matcher = \"\\\"#{assertion_value}\\\"\"\n                elsif assertion_value.is_a?(Regexp)\n                  matcher = assertion_value.inspect\n                else\n                  matcher = assertion_value\n                end\n                assertion_code << \"#{assertion_type.keys.first}(#{matcher}, result.stdout, '#{assert_msg}#{matcher}')\\n\"\n              end\n\n              assertion_code << \"end\\n\" if expect_failure\n            end\n          end\n        end\n        return assertion_code\n      end\n\n      Result = Struct.new(:stdout)\n      def run_assertions(assertions = '', result)\n        result_struct = Result.new\n        if result.respond_to? :stdout\n          result_struct.stdout = result.stdout\n        else\n          # handle results sent in as string\n          result_struct.stdout = result\n        end\n        result = result_struct\n\n        begin\n          eval(assertions)\n        rescue RuntimeError, SyntaxError => e\n          puts e\n          puts assertions\n          raise\n        end\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/puppet_type_test_tools_spec.rb",
    "content": "require File.join(File.dirname(__FILE__),'../../acceptance_spec_helper.rb')\nrequire 'puppet/acceptance/puppet_type_test_tools.rb'\nrequire 'beaker/dsl/assertions'\nrequire 'beaker/result'\n\nmodule Puppet\n  module Acceptance\n\n    describe 'PuppetTypeTestTools' do\n      include PuppetTypeTestTools\n      include Beaker::DSL::Assertions\n      include Beaker\n\n      context '#generate_manifest' do\n        it 'takes a single hash' do\n          expect(generate_manifest({:type => 'fake'})).to match(/^fake{\"fake_\\w{8}\":}$/)\n        end\n        it 'takes an array' do\n          expect(generate_manifest([{:type => 'fake'}])).to match(/^fake{\"fake_\\w{8}\":}$/)\n        end\n        it 'generates empty puppet code (assertion-only instance)' do\n          expect(generate_manifest({:fake => 'fake'})).to eql('')\n        end\n        it 'puts a namevar in the right place' do\n          expect(generate_manifest({:type => 'fake', :parameters =>\n                                    {:namevar => 'blah'}})).to match(/^fake{\"blah\":}$/)\n        end\n        it 'retains puppet code in a namevar' do\n          expect(generate_manifest({:type => 'fake', :parameters =>\n                                    {:namevar => \"half_${interpolated}_puppet_namevar\"}})).\n          to match(/^fake{\"half_\\${interpolated}_puppet_namevar\":}$/)\n        end\n        it 'places pre_code before the type' do\n          expect(generate_manifest({:type => 'fake', :pre_code => '$some = puppet_code'})).\n            to match(/^\\$some = puppet_code\\nfake{\"fake_\\w{8}\":}$/m)\n        end\n        it 'places multiple, arbitrary parameters' do\n          expect(generate_manifest({:type => 'fake', :parameters =>\n                                    {:someprop => \"function(call)\", :namevar => \"blah\", :someparam => 2}})).\n          to match(/^fake{\"blah\":someprop => function\\(call\\),someparam => 2,}$/)\n        end\n      end\n\n      context '#generate_assertions' do\n        it 'takes a single hash' do\n          expect(generate_assertions({:assertions => {:fake => 'matcher'}}))\n            .to match(/^fake\\(\"matcher\", result\\.stdout, '\"matcher\"'\\)$/)\n        end\n        it 'takes an array' do\n          expect(generate_assertions([{:assertions => {:fake => 'matcher'}}]))\n            .to match(/^fake\\(\"matcher\", result\\.stdout, '\"matcher\"'\\)$/)\n        end\n        it 'generates empty assertions (puppet-code only instance)' do\n          expect(generate_assertions({:type => 'no assertions'})).to eql('')\n        end\n        it 'generates arbitrary assertions' do\n          expect(generate_assertions({:assertions => [{:fake => 'matcher'},\n                                                      {:other => 'othermatch'}]}))\n            .to match(/^fake\\(\"matcher\", result\\.stdout, '\"matcher\"'\\)\\nother\\(\"othermatch\", result.stdout, '\"othermatch\"'\\)$/m)\n        end\n        it 'can give a regex to assertions' do\n          expect(generate_assertions({:assertions => {:fake => /matcher/}}))\n            .to match(/^fake\\(\\/matcher\\/, result\\.stdout, '\\/matcher\\/'\\)$/)\n        end\n        it 'allows multiple of one assertion type' do\n          expect(generate_assertions({:assertions => {:fake => ['matcher','othermatch']}}))\n            .to match(/^fake\\(\"matcher\", result\\.stdout, '\"matcher\"'\\)\\nfake\\(\"othermatch\", result.stdout, '\"othermatch\"'\\)$/)\n        end\n        it 'allows multiple assertion_types with multiple values' do\n          expect(generate_assertions({:assertions => [{:fake => ['matcher','othermatch']},\n                                                      {:fake2 => ['matcher2','othermatch2']}]}))\n            .to match(/^fake\\(\"matcher\", result\\.stdout, '\"matcher\"'\\)\\nfake\\(\"othermatch\", result.stdout, '\"othermatch\"'\\)\\nfake2\\(\"matcher2\", result.stdout, '\"matcher2\"'\\)\\nfake2\\(\"othermatch2\", result.stdout, '\"othermatch2\"'\\)\\n$/)\n        end\n        context 'expect_failure' do\n          it 'generates arbitrary assertion' do\n            expect(generate_assertions({:assertions => {:expect_failure => {:fake => 'matcher'}}}))\n              .to match(/^expect_failure '' do\\nfake\\(.*\\)\\nend$/)\n          end\n          it 'allows multiple of one assertion type' do\n            expect(generate_assertions({:assertions => {:expect_failure => {:fake => ['matcher','othermatch']}}}))\n              .to match(/^expect_failure '' do\\nfake\\(.*\\)\\nfake\\(.*\\)\\nend$/)\n          end\n          it 'allows multiple assertion_types' do\n            pending 'ack! requires recursion :-('\n            #expect(generate_assertions({:assertions => {:expect_failure => [{:fake => 'matcher'},{:fake2 => 'matcher2'}]}}))\n              #.to match(/^expect_failure '' do\\nfake\\(.*\\)\\nfake2\\(.*\\)\\nend$/)\n          end\n          it 'allows multiple assertion_types with an expect_failure on one' do\n            expect(generate_assertions({:assertions => [{:expect_failure => {:fake => 'matcher'}}, {:fake2 => 'matcher2'}]}))\n              .to match(/^expect_failure '' do\\nfake\\(.*\\)\\nend\\nfake2\\(.*\\)$/)\n          end\n          it 'allows custom expect_failure messages' do\n            expect(generate_assertions({:assertions => {:expect_failure => {:fake => 'matcher', :message => 'oh noes, this should fail but pass'}}}))\n              .to match(/^expect_failure 'oh noes, this should fail but pass' do\\nfake\\(.*\\)\\nend$/)\n          end\n        end\n        it 'allow custom assertion messages'\n      end\n\n      context 'run_assertions' do\n      #def run_assertions(assertions = '', result)\n        it 'takes a string result' do\n          expect(run_assertions('assert_match(\"yes please\", result.stdout)', 'yes please')).to be true\n        end\n        let(:result) {Beaker::Result.new('host','command')}\n        it 'takes a beaker \"type\" Result' do\n          result.stdout = 'yes please'\n          expect(run_assertions('assert_match(\"yes please\", result.stdout)', result)).to be true\n        end\n        it 'runs a bunch of assertions' do\n          result.stdout = 'yes please'\n          expect(run_assertions(\"assert_match('yes please', result.stdout)\\nrefute_match('blah', result.stdout)\", result)).to be false\n        end\n        it 'fails assertions' do\n          pending 'why doesnt this work?'\n          result.stdout = 'yes please'\n          expect(run_assertions('assert_match(\"blah\", result.stdout)', result)).to raise_error\n        end\n        context 'exceptions' do\n          #rescue RuntimeError, SyntaxError => e\n          it 'puts the assertion code, raises error' do\n            pending 'why doesnt this work?'\n            expect(run_assertions('assert_match(\"blah\") }', result)).to raise_error\n          end\n        end\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/rpm_util.rb",
    "content": "module Puppet\n  module Acceptance\n    module RpmUtils\n      # Utilities for creating a basic rpm package and using it in tests\n      @@defaults = {:repo => '/tmp/rpmrepo', :pkg => 'mypkg', :publisher => 'tstpub.lan', :version => '1.0'}\n      @@setup_packages = {}\n\n      def rpm_provider(agent)\n        has_dnf = on(agent, 'which dnf', :acceptable_exit_codes => [0,1]).exit_code\n        has_dnf == 0 ? 'dnf' : 'yum'\n      end\n\n      def setup(agent)\n        @@setup_packages[agent] ||= {}\n        cmd = rpm_provider(agent)\n        required_packages = %w[createrepo curl rpm-build]\n        required_packages.each do |pkg|\n          pkg_installed = (on agent, \"#{cmd} list installed #{pkg}\", :acceptable_exit_codes => (0..255)).exit_code == 0\n          # We need a newer OpenSSH for the newer OpenSSL that curl installs\n          # RE-16677\n          on(agent, 'dnf upgrade -y openssh') if (agent.platform.start_with?('el-9') && pkg == 'curl')\n          # package not present, so perform a new install\n          if !pkg_installed\n            on agent, \"#{cmd} install -y #{pkg}\"\n          # package is present, but has not yet attempted an upgrade\n          # note that this may influence YUM cache behavior\n          elsif !@@setup_packages[agent].has_key?(pkg)\n            # first pass, always attempt an upgrade to latest version\n            # fixes Fedora 25 curl compat with python-pycurl for instance\n            on agent, \"#{cmd} upgrade -y #{pkg}\"\n          end\n\n          @@setup_packages[agent][pkg] = true\n        end\n      end\n\n      def clean_rpm(agent, o={})\n        cmd = rpm_provider(agent)\n        o = @@defaults.merge(o)\n        on agent, \"rm -rf #{o[:repo]}\", :acceptable_exit_codes => (0..255)\n        on agent, \"#{cmd} remove -y #{o[:pkg]}\", :acceptable_exit_codes => (0..255)\n        on agent, \"rm -f /etc/yum.repos.d/#{o[:publisher]}.repo\", :acceptable_exit_codes => (0..255)\n      end\n\n      def setup_rpm(agent, o={})\n        setup(agent)\n        o = @@defaults.merge(o)\n        on agent, \"mkdir -p #{o[:repo]}/{RPMS,SRPMS,BUILD,SOURCES,SPECS}\"\n        on agent, \"echo '%_topdir #{o[:repo]}' > ~/.rpmmacros\"\n        on agent, \"createrepo #{o[:repo]}\"\n        on agent, \"cat <<EOF > /etc/yum.repos.d/#{o[:publisher]}.repo\n[#{o[:publisher]}]\nname=#{o[:publisher]}\nbaseurl=file://#{o[:repo]}/\nenabled=1\ngpgcheck=0\nEOF\n\"\n      end\n\n      def send_rpm(agent, o={})\n        setup(agent)\n        o = @@defaults.merge(o)\n        on agent, \"mkdir -p #{o[:repo]}/#{o[:pkg]}-#{o[:version]}/usr/bin\"\n        on agent, \"cat <<EOF > #{o[:repo]}/#{o[:pkg]}\n#!/bin/bash\necho Hello World\nEOF\n\"\n        pkg_name = \"#{o[:pkg]}-#{o[:version]}\"\n        on agent, \"install -m 755 #{o[:repo]}/#{o[:pkg]} #{o[:repo]}/#{pkg_name}/usr/bin\"\n        on agent, \"tar -zcvf #{o[:repo]}/SOURCES/#{pkg_name}.tar.gz -C #{o[:repo]} #{pkg_name}\"\n        on agent, \"cat <<EOF > #{o[:repo]}/SPECS/#{o[:pkg]}.spec\n# Don't try fancy stuff like debuginfo, which is useless on binary-only packages. Don't strip binary too\n# Be sure buildpolicy set to do nothing\n%define        __spec_install_post %{nil}\n%define          debug_package %{nil}\n%define        __os_install_post %{_dbpath}/brp-compress\n\nSummary: A very simple toy bin rpm package\nName: #{o[:pkg]}\nVersion: #{o[:version]}\nRelease: 1\nEpoch: #{o[:epoch] || 0}\nBuildArch: noarch\nLicense: GPL+\nGroup: Development/Tools\nSOURCE0 : %{name}-%{version}.tar.gz\nURL: https://www.puppetlabs.com/\n\nBuildRoot: %{_topdir}/BUILD/%{name}-%{version}-%{release}-root\n\n%description\n%{summary}\n\n%prep\n%setup -q\n\n%build\n# Empty section.\n\n%install\nrm -rf %{buildroot}\nmkdir -p  %{buildroot}\n\n# in builddir\ncp -a * %{buildroot}\n\n\n%clean\nrm -rf %{buildroot}\n\n%files\n%defattr(-,root,root,-)\n%{_bindir}/*\n\n%changelog\n* Mon Dec 01 2014  Michael Smith <michael.smith@puppetlabs.com> #{o[:version]}-1\n- First Build\n\nEOF\n\"\n        on agent, \"rpmbuild -ba #{o[:repo]}/SPECS/#{o[:pkg]}.spec\"\n        on agent, \"createrepo --update #{o[:repo]}\"\n\n        cmd = rpm_provider(agent)\n        # DNF requires a cache reset to make local repositories accessible.\n        if cmd == 'dnf'\n          on agent, \"dnf clean metadata\"\n        end\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/service_utils.rb",
    "content": "require 'puppet/acceptance/common_utils'\n\nmodule Puppet\n  module Acceptance\n    module ServiceUtils\n\n      # Return whether a host supports the systemd provider.\n      # @param host [String] hostname\n      # @return [Boolean] whether the systemd provider is supported.\n      def supports_systemd?(host)\n        # The Windows MSI doesn't put Puppet in the Ruby vendor or site dir, so loading it fails.\n        return false if host.platform.variant == 'windows'\n        ruby = Puppet::Acceptance::CommandUtils.ruby_command(host)\n        suitable = on(host, \"#{ruby} -e \\\"require 'puppet'; puts Puppet::Type.type(:service).provider(:systemd).suitable?\\\"\" ).stdout.chomp\n        suitable == \"true\" ? true : false\n      end\n\n      # Construct manifest ensuring service status.\n      # @param service [String] name of the service\n      # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys.\n      # @return [String] a manifest\n      def service_manifest(service, status)\n        ensure_status = \"ensure => '#{status[:ensure]}',\" if status[:ensure]\n        enable_status = \"enable => '#{status[:enable]}',\" if status[:enable]\n        %Q{\n          service { '#{service}':\n            #{ensure_status}\n            #{enable_status}\n          }\n        }\n      end\n\n      # Alter the state of a service using puppet apply and assert that a change was logged.\n      # Assumes the starting state is not the desired state.\n      # @param host [String] hostname.\n      # @param service [String] name of the service.\n      # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys.\n      # @return None\n      def ensure_service_change_on_host(host, service, status)\n        # the process of creating the service will also start it\n        # to avoid a flickering test from the race condition, this test will ensure\n        # that the exit code is either\n        #   2 => something changed, or\n        #   0 => no change needed\n        apply_manifest_on(host, service_manifest(service, status), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(/Service\\[#{service}\\]\\/ensure: ensure changed '\\w+' to '#{status[:ensure]}'/, result.stdout, 'Service status change failed') if status[:ensure]\n          assert_match(/Service\\[#{service}\\]\\/enable: enable changed '\\w+' to '#{status[:enable]}'/, result.stdout, 'Service enable change failed') if status[:enable]\n        end\n      end\n\n      # Ensure the state of a service using puppet apply and assert that no change was logged.\n      # Assumes the starting state is the ensured state.\n      # @param host [String] hostname.\n      # @param service [String] name of the service.\n      # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys.\n      # @return None\n      def ensure_service_idempotent_on_host(host, service, status)\n        # ensure idempotency\n        apply_manifest_on(host, service_manifest(service, status)) do |result|\n          refute_match(/Service\\[#{service}\\]\\/ensure/, result.stdout, 'Service status not idempotent') if status[:ensure]\n          refute_match(/Service\\[#{service}\\]\\/enable/, result.stdout, 'Service enable not idempotent') if status[:enable]\n        end\n      end\n\n      # Alter the state of a service using puppet apply, assert that it changed and change is idempotent.\n      # Can set 'ensure' and 'enable'. Assumes the starting state is not the desired state.\n      # @param host [String] hostname.\n      # @param service [String] name of the service.\n      # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys.\n      # @param block [Proc] optional: block to verify service state\n      # @return None\n      def ensure_service_on_host(host, service, status, &block)\n        ensure_service_change_on_host(host, service, status)\n        assert_service_status_on_host(host, service, status, &block)\n        ensure_service_idempotent_on_host(host, service, status)\n        assert_service_status_on_host(host, service, status, &block)\n      end\n\n      # Checks that the ensure and/or enable status of a service are as expected.\n      # @param host [String] hostname.\n      # @param service [String] name of the service.\n      # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys.\n      # @param block [Proc] optional: block to verify service state\n      # @return None\n      def assert_service_status_on_host(host, service, status, &block)\n        ensure_status = \"ensure.+=> '#{status[:ensure]}'\" if status[:ensure]\n        enable_status = \"enable.+=> '#{status[:enable]}'\" if status[:enable]\n\n        on(host, puppet_resource('service', service)) do |result|\n          assert_match(/'#{service}'.+#{ensure_status}.+#{enable_status}/m, result.stdout, \"Service status does not match expectation #{status}\")\n        end\n\n        # Verify service state on the system using a custom block\n        if block\n          yield block\n        end\n      end\n\n      # Refreshes a service.\n      # @param host [String] hostname.\n      # @param service [String] name of the service to refresh.\n      # @return None\n      def refresh_service_on_host(host, service)\n        refresh_manifest = %Q{\n          service { '#{service}': }\n\n          notify { 'Refreshing #{service}':\n            notify => Service['#{service}'],\n          }\n        }\n\n        apply_manifest_on(host, refresh_manifest)\n      end\n\n      # Runs some common acceptance tests for nonexistent services.\n      # @param service [String] name of the service\n      # @return None\n      def run_nonexistent_service_tests(service)\n        step \"Verify that a nonexistent service is considered stopped, disabled and no logonaccount is reported\" do\n          on(agent, puppet_resource('service', service)) do |result|\n            { enable: false, ensure: :stopped }.each do |property, value|\n              assert_match(/#{property}.*#{value}.*$/, result.stdout, \"Puppet does not report #{property}=#{value} for a non-existent service\")\n            end\n            refute_match(/logonaccount\\s+=>/, result.stdout, \"Puppet reports logonaccount for a non-existent service\")\n          end\n        end\n      \n        step \"Verify that stopping and disabling a nonexistent service is a no-op\" do\n          manifest =  service_manifest(service, ensure: :stopped, enable: false)\n          apply_manifest_on(agent, manifest, catch_changes: true)\n        end\n\n        [\n          [ :enabling,  [ :enable, true     ]],\n          [ :starting,  [ :ensure, :running ]]\n        ].each do |operation, (property, value)|\n          manifest = service_manifest(service, property => value)\n\n          step \"Verify #{operation} a non-existent service prints an error message but does not fail the run without detailed exit codes\" do\n            apply_manifest_on(agent, manifest) do |result|\n              assert_match(/Error:.*#{service}.*$/, result.stderr, \"Puppet does not error when #{operation} a non-existent service.\")\n            end\n          end\n        \n          step \"Verify #{operation} a non-existent service with detailed exit codes correctly returns an error code\" do\n            apply_manifest_on(agent, manifest, :acceptable_exit_codes => [4])\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/solaris_util.rb",
    "content": "module Puppet\n  module Acceptance\n    module IPSUtils\n      def clean(agent, o={})\n        o = {:repo => '/var/tstrepo', :pkg => 'mypkg', :publisher => 'tstpub.lan'}.merge(o)\n        on agent, \"rm -rf %s||:\" % o[:repo]\n        on agent, \"rm -rf /tst||:\"\n        on agent, \"pkg uninstall %s||:\" % o[:pkg]\n        on agent, \"pkg unset-publisher %s ||:\" % o[:publisher]\n      end\n      def setup(agent, o={})\n        o = {:repo => '/var/tstrepo', :publisher => 'tstpub.lan'}.merge(o)\n        on agent, \"mkdir -p %s\" % o[:repo]\n        on agent, \"pkgrepo create %s\" % o[:repo]\n        on agent, \"pkgrepo set -s %s publisher/prefix=%s\" % [o[:repo], o[:publisher]]\n        on agent, \"pkgrepo -s %s refresh\" % o[:repo]\n      end\n      def setup_fakeroot(agent, o={})\n        o = {:root=>'/opt/fakeroot'}.merge(o)\n        on agent, \"rm -rf %s\" % o[:root]\n        on agent, \"mkdir -p %s/tst/usr/bin\" % o[:root]\n        on agent, \"mkdir -p %s/tst/etc\" % o[:root]\n        on agent, \"echo dummy > %s/tst/usr/bin/x\" % o[:root]\n        on agent, \"echo val > %s/tst/etc/y\" % o[:root]\n      end\n      def setup_fakeroot2(agent, o={})\n        o = {:root=>'/opt/fakeroot'}.merge(o)\n        on agent, \"rm -rf %s\" % o[:root]\n        on agent, \"mkdir -p %s/tst2/usr/bin\" % o[:root]\n        on agent, \"mkdir -p %s/tst2/etc\" % o[:root]\n        on agent, \"echo dummy > %s/tst2/usr/bin/x\" % o[:root]\n        on agent, \"echo val > %s/tst2/etc/y\" % o[:root]\n      end\n      def send_pkg2(agent, o={})\n        o = {:repo=>'/var/tstrepo', :root=>'/opt/fakeroot', :publisher=>'tstpub.lan', :pkg=>'mypkg2@0.0.1', :pkgdep => 'mypkg@0.0.1'}.merge(o)\n        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]]\n        on agent, \"echo depend type=require fmri=%s >> /tmp/%s.p5m\" % [o[:pkgdep], o[:pkg]]\n        on agent, \"pkgsend publish -d %s -s %s /tmp/%s.p5m\" % [o[:root], o[:repo], o[:pkg]]\n        on agent, \"pkgrepo refresh -p %s -s %s\" % [o[:publisher], o[:repo]]\n        on agent, \"pkg refresh\"\n        on agent, \"pkg list -g %s\" % o[:repo]\n      end\n\n      def send_pkg(agent, o={})\n        o = {:repo=>'/var/tstrepo', :root=>'/opt/fakeroot', :publisher=>'tstpub.lan', :pkg=>'mypkg@0.0.1'}.merge(o)\n        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]]\n        on agent, \"pkgsend publish -d %s -s %s /tmp/%s.p5m\" % [o[:root], o[:repo], o[:pkg]]\n        on agent, \"pkgrepo refresh -p %s -s %s\" % [o[:publisher], o[:repo]]\n        on agent, \"pkg refresh\"\n      end\n      def set_publisher(agent, o={})\n        o = {:repo=>'/var/tstrepo', :publisher=>'tstpub.lan'}.merge(o)\n        on agent, \"pkg set-publisher -g %s %s\" % [o[:repo], o[:publisher]]\n        on agent, \"pkg refresh\"\n      end\n    end\n    module SMFUtils\n      def clean(agent, o={})\n        o = {:service => 'tstapp'}.merge(o)\n        on(agent, \"svcs -l %s\" % o[:service], acceptable_exit_codes: [0, 1]) do |result|\n          next if result.stdout =~ /doesn't match/\n          lines = result.stdout.chomp.lines\n          instances = lines.select { |line| line =~ /^fmri/ }.map { |line| line.split(' ')[1].chomp }\n          instances.each do |instance|\n            on agent, \"svcadm disable %s ||:\" % instance\n            on agent, \"svccfg delete %s ||:\" % instance\n          end\n        end\n        on agent, \"rm -rf /var/svc/manifest/application/%s.xml ||:\" % o[:service]\n        on agent, \"rm -f /opt/bin/%s ||:\" % o[:service]\n      end\n      def setup(agent, o={})\n        setup_methodscript(agent, o)\n      end\n\n      def setup_methodscript(agent, o={})\n        o = {:service => 'tstapp'}.merge(o)\n        on agent, \"mkdir -p /opt/bin\"\n        create_remote_file agent, '/lib/svc/method/%s' % o[:service], %[\n#!/usr/bin/sh\n. /lib/svc/share/smf_include.sh\ncase \"$1\" in\n  start) /opt/bin/%s ;;\n  stop)\n      ctid=`svcprop -p restarter/contract $SMF_FMRI`\n      if [ -n \"$ctid\" ]; then\n        smf_kill_contract $ctid TERM\n      fi\n  ;;\n  *) echo \"Usage: $0 { start | stop }\" ; exit 1 ;;\nesac\nexit $SMF_EXIT_OK\n        ] % ([o[:service]] * 4)\n        create_remote_file agent, ('/opt/bin/%s' % o[:service]), %[\n#!/usr/bin/sh\ncleanup() {\n  rm -f /tmp/%s.pidfile; exit 0\n}\n\ntrap cleanup INT TERM\ntrap '' HUP\n(while :; do sleep 1;  done) & echo $! > /tmp/%s.pidfile\n        ] % ([o[:service]] * 2)\n        on agent, \"chmod 755 /lib/svc/method/%s\" % o[:service]\n        on agent, \"chmod 755 /opt/bin/%s\" % o[:service]\n        on agent, \"mkdir -p /var/svc/manifest/application\"\n        create_remote_file agent, ('/var/smf-%s.xml' % o[:service]),\n%[<?xml version=\"1.0\"?>\n<!DOCTYPE service_bundle SYSTEM \"/usr/share/lib/xml/dtd/service_bundle.dtd.1\">\n<service_bundle type='manifest' name='%s:default'>\n  <service name='application/tstapp' type='service' version='1'>\n  <create_default_instance enabled='false' />\n  <method_context> <method_credential user='root' group='root' /> </method_context>\n  <exec_method type='method' name='start' exec='/lib/svc/method/%s start' timeout_seconds=\"60\" />\n  <exec_method type='method' name='stop' exec='/lib/svc/method/%s stop' timeout_seconds=\"60\" />\n  <exec_method type='method' name='refresh' exec='/lib/svc/method/%s refresh' timeout_seconds=\"60\" />\n  <stability value='Unstable' />\n  <template>\n    <common_name> <loctext xml:lang='C'>Dummy</loctext> </common_name>\n    <documentation>\n      <manpage title='tstapp' section='1m' manpath='/usr/share/man' />\n    </documentation>\n  </template>\n</service>\n</service_bundle>\n        ] % ([o[:service]] * 4)\n        on agent, \"svccfg -v validate /var/smf-%s.xml\" % o[:service]\n        on agent, \"echo > /var/svc/log/application-%s:default.log\" % o[:service]\n        return (\"/var/smf-%s.xml\" % o[:service]), (\"/lib/svc/method/%s\" % o[:service])\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/static_catalog_utils.rb",
    "content": "module Puppet\n  module Acceptance\n    module StaticCatalogUtils\n\n      # Adds code-id-command and code-content-command scripts\n      # to the server and updates puppetserver.conf. This is\n      # necessary for testing static catalogs.\n      # @param master [String] the host running puppetserver.\n      # @param scriptdir [String] the path to the directory where the scripts should be placed.\n      def setup_puppetserver_code_id_scripts(master, scriptdir)\n        code_id_command = <<EOF\n#! /bin/bash\n\necho -n 'code_version_1'\nEOF\n\n        code_content_command = <<EOF\n#! /bin/bash\n\nif [ \\\\\\$2 == 'code_version_1' ] ; then\n  echo -n 'code_version_1'\nelse\n  echo -n 'newer_code_version'\nfi\nEOF\n        apply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nfile { '#{scriptdir}/code_id.sh':\n  ensure => file,\n  content => \"#{code_id_command}\",\n  mode => \"0755\",\n}\n\nfile { '#{scriptdir}/code_content.sh':\n  ensure => file,\n  content => \"#{code_content_command}\",\n  mode => \"0755\",\n}\nMANIFEST\n\n        puppetserver_config = \"#{master['puppetserver-confdir']}/puppetserver.conf\"\n        on master, \"cp #{puppetserver_config} #{scriptdir}/puppetserver.conf.bak\"\n        versioned_code_settings = {\"versioned-code\" => {\"code-id-command\" => \"#{scriptdir}/code_id.sh\", \"code-content-command\" => \"#{scriptdir}/code_content.sh\"}}\n        modify_tk_config(master, puppetserver_config, versioned_code_settings)\n      end\n\n      def cleanup_puppetserver_code_id_scripts(master, scriptdir)\n        # These are -f so we don't bail on the teardown if for some reason they didn't get laid down\n        on master, \"rm -f #{scriptdir}/code_id.sh\"\n        on master, \"rm -f #{scriptdir}/code_content.sh\"\n        puppetserver_config = \"#{master['puppetserver-confdir']}/puppetserver.conf\"\n        on master, \"cp #{scriptdir}/puppetserver.conf.bak #{puppetserver_config}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/temp_file_utils.rb",
    "content": "module Puppet\n  module Acceptance\n    module TempFileUtils\n      RWXR_XR_X = '0755'\n      PUPPET_CODEDIR_PERMISSIONS = RWXR_XR_X\n\n      # Return the name of the root user, as appropriate for the platform.\n      def root_user(host)\n        case host['platform']\n        when /windows/\n          'Administrator'\n        else\n          'root'\n        end\n      end\n\n      # Return the name of the root group, as appropriate for the platform.\n      def root_group(host)\n        case host['platform']\n        when /windows/\n          'Administrators'\n        when /aix/\n          'system'\n        when /osx|bsd/\n          'wheel'\n        else\n          'root'\n        end\n      end\n\n      # Create a file on the host.\n      # Parameters:\n      # [host] the host to create the file on\n      # [file_path] the path to the file to be created\n      # [file_content] a string containing the contents to be written to the file\n      # [options] a hash containing additional behavior options.  Currently supported:\n      # * :mkdirs (default false) if true, attempt to create the parent directories on the remote host before writing\n      #       the file\n      # * :owner (default 'root') the username of the user that the file should be owned by\n      # * :group (default 'puppet') the name of the group that the file should be owned by\n      # * :mode (default '644') the mode (file permissions) that the file should be created with\n      def create_test_file(host, file_rel_path, file_content, options = {})\n\n        # set default options\n        options[:mkdirs] ||= false\n        options[:mode] ||= \"755\"\n        unless options[:owner]\n          if host['roles'].include?('master') then\n            options[:owner] = host.puppet['user']\n          else\n            options[:owner] = root_user(host)\n          end\n        end\n        unless options[:group]\n          if host['roles'].include?('master') then\n            options[:group] = host.puppet['group']\n          else\n            options[:group] = root_group(host)\n          end\n        end\n\n        file_path = get_test_file_path(host, file_rel_path)\n\n        mkdirs(host, File.dirname(file_path)) if (options[:mkdirs] == true)\n        create_remote_file(host, file_path, file_content)\n\n        #\n        # NOTE: we need these chown/chmod calls because the acceptance framework connects to the nodes as \"root\", but\n        #  puppet 'master' runs as user 'puppet'.  Therefore, in order for puppet master to be able to read any files\n        #  that we've created, we have to carefully set their permissions\n        #\n\n        chown(host, options[:owner], options[:group], file_path)\n        chmod(host, options[:mode], file_path)\n\n      end\n\n\n      # Given a relative path, returns an absolute path for a test file.  Basically, this just prepends the\n      # a unique temp dir path (specific to the current test execution) to your relative path.\n      def get_test_file_path(host, file_rel_path)\n        initialize_temp_dirs unless @host_test_tmp_dirs\n\n        File.join(@host_test_tmp_dirs[host.name], file_rel_path)\n      end\n\n\n      # Check for the existence of a temp file for the current test; basically, this just calls file_exists?(),\n      # but prepends the path to the current test's temp dir onto the file_rel_path parameter.  This allows\n      # tests to be written using only a relative path to specify file locations, while still taking advantage\n      # of automatic temp file cleanup at test completion.\n      def test_file_exists?(host, file_rel_path)\n        file_exists?(host, get_test_file_path(host, file_rel_path))\n      end\n\n      def file_exists?(host, file_path)\n        host.execute(\"test -f \\\"#{file_path}\\\"\",\n                     :acceptable_exit_codes => [0, 1])  do |result|\n          return result.exit_code == 0\n        end\n      end\n\n      def dir_exists?(host, dir_path)\n        host.execute(\"test -d \\\"#{dir_path}\\\"\",\n                     :acceptable_exit_codes => [0, 1])  do |result|\n          return result.exit_code == 0\n        end\n      end\n\n      def link_exists?(host, link_path)\n        host.execute(\"test -L \\\"#{link_path}\\\"\",\n                     :acceptable_exit_codes => [0, 1])  do |result|\n          return result.exit_code == 0\n        end\n      end\n\n      def file_contents(host, file_path)\n        host.execute(\"cat \\\"#{file_path}\\\"\") do |result|\n          return result.stdout\n        end\n      end\n\n      def tmpdir(host, basename)\n        host_tmpdir = host.tmpdir(basename)\n        # we need to make sure that the puppet user can traverse this directory...\n        chmod(host, \"755\", host_tmpdir)\n        host_tmpdir\n      end\n\n      def mkdirs(host, dir_path)\n        on(host, \"mkdir -p #{dir_path}\")\n      end\n\n      def chown(host, owner, group, path)\n        on(host, \"chown #{owner}:#{group} #{path}\")\n      end\n\n      def chmod(host, mode, path)\n        on(host, \"chmod #{mode} #{path}\")\n      end\n\n      # Returns an array containing the owner, group and mode of\n      # the file specified by path. The returned mode is an integer\n      # value containing only the file mode, excluding the type, e.g\n      # S_IFDIR 0040000\n      def stat(host, path)\n        require File.join(File.dirname(__FILE__),'common_utils.rb')\n        ruby = Puppet::Acceptance::CommandUtils.ruby_command(host)\n        owner = on(host, \"#{ruby} -e 'require \\\"etc\\\"; puts (Etc.getpwuid(File.stat(\\\"#{path}\\\").uid).name)'\").stdout.chomp\n        group = on(host, \"#{ruby} -e 'require \\\"etc\\\"; puts (Etc.getgrgid(File.stat(\\\"#{path}\\\").gid).name)'\").stdout.chomp\n        mode  = on(host, \"#{ruby} -e 'puts (File.stat(\\\"#{path}\\\").mode & 07777)'\").stdout.chomp.to_i\n\n        [owner, group, mode]\n      end\n\n      def initialize_temp_dirs()\n        # pluck this out of the test case environment; not sure if there is a better way\n        @cur_test_file = @path\n        @cur_test_file_shortname = File.basename(@cur_test_file, File.extname(@cur_test_file))\n\n        # we need one list of all of the hosts, to assist in managing temp dirs.  It's possible\n        # that the master is also an agent, so this will consolidate them into a unique set\n        @all_hosts = Set[master, *agents]\n\n        # now we can create a hash of temp dirs--one per host, and unique to this test--without worrying about\n        # doing it twice on any individual host\n        @host_test_tmp_dirs = Hash[@all_hosts.map do |host| [host.name, tmpdir(host, @cur_test_file_shortname)] end ]\n      end\n\n      def remove_temp_dirs()\n        @all_hosts.each do |host|\n          on(host, \"rm -rf #{@host_test_tmp_dirs[host.name]}\")\n        end\n      end\n\n      # a silly variable for keeping track of whether or not all of the tests passed...\n      @all_tests_passed = false\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/windows_utils/package_installer.rb",
    "content": "module Puppet\n  module Acceptance\n    module WindowsUtils\n      # Sets up a mock installer on the host.\n\n      def create_mock_package(host, tmpdir, config = {}, installer_file = 'MockInstaller.cs', uninstaller_file = 'MockUninstaller.cs')\n        installer_exe_path = \"#{tmpdir}/#{config[:name].gsub(/\\s+/, '')}Installer.exe\".gsub('/', '\\\\')\n        uninstaller_exe_path = \"#{tmpdir}/#{config[:name].gsub(/\\s+/, '')}Uninstaller.exe\".gsub('/', '\\\\')\n        tranformations = {\n          package_display_name: config[:name],\n          uninstaller_location: uninstaller_exe_path,\n          install_commands:     config[:install_commands],\n          uninstall_commands:   config[:uninstall_commands]\n        }\n\n        [\n          { source: installer_file, destination: installer_exe_path },\n          { source: uninstaller_file, destination: uninstaller_exe_path },\n        ].each do |exe|\n          fixture_path = File.join(\n            File.dirname(__FILE__),\n            '..',\n            '..',\n            '..',\n            '..',\n            'fixtures',\n            exe[:source]\n          )\n          code = File.read(fixture_path) % tranformations\n          build_mock_exe(host, exe[:destination], code)\n        end\n        # If the registry key still exists from a previous package install, then delete it.\n        teardown do\n          if package_installed?(host, config[:name])\n            on host, powershell(\"\\\"Remove-Item HKLM:\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\#{config[:name]}\\\"\")\n          end\n        end\n        # return the installer path for tests to use as the source: attribute\n        installer_exe_path\n      end\n\n      def build_mock_exe(host, destination, code)\n        # Make a source file containing the code on the SUT, the source file\n        # will be the same location/name as the destination exe but with the .cs\n        # extension\n        source_path_on_host = destination.gsub(/\\.exe$/, '.cs')\n        create_remote_file(host, source_path_on_host.gsub('\\\\', '/'), code)\n        # Create the installer.exe file by compiling the copied over C# code\n        # with PowerShell\n        create_installer_exe = \"\\\"Add-Type\"\\\n          \" -TypeDefinition (Get-Content #{source_path_on_host} | Out-String)\"\\\n          \" -Language CSharp\"\\\n          \" -OutputAssembly #{destination}\"\\\n          \" -OutputType ConsoleApplication\\\"\"\n        on host, powershell(create_installer_exe)\n      end\n\n      def package_installed?(host, name)\n        # A successfully installed mock package will have created a registry key under\n        # HKLM:\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall. Simply checking\n        # for that key should suffice as an indicator that the installer completed\n        test_key = \"\\\"Test-Path HKLM:\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\#{name}\\\"\"\n        on(host, powershell(test_key)) do |result|\n          return result.stdout.chomp == 'True'\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/windows_utils/service.rb",
    "content": "module Puppet\n  module Acceptance\n    module WindowsUtils\n      # Sets up a mock service on the host. The methodology here is a simplified\n      # version of what's described in https://msdn.microsoft.com/en-us/magazine/mt703436.aspx\n      def setup_service(host, config = {}, service_file = 'MockService.cs')\n        config[:name] ||= \"Mock Service\"\n        config[:display_name] ||= \"#{config[:name]} (Puppet Acceptance Tests)\"\n        config[:description] ||= \"Service created solely for acceptance testing the Puppet Windows Service provider\"\n\n        # Create a temporary directory to store the service's C# source code +\n        # its .exe file.\n        tmpdir = host.tmpdir(\"mock_service\")\n\n        # Copy-over the C# code\n        code_fixture_path = File.join(\n          File.dirname(__FILE__),\n          '..',\n          '..',\n          '..',\n          '..',\n          'fixtures',\n          service_file\n        )\n        code = File.read(code_fixture_path) % {\n          service_name: config[:name],\n          start_sleep: config[:start_sleep],\n          pause_sleep: config[:pause_sleep],\n          continue_sleep: config[:continue_sleep],\n          stop_sleep: config[:stop_sleep]\n        }\n        code_path_unix = \"#{tmpdir}/source.cs\"\n        code_path_win = code_path_unix.gsub('/', '\\\\')\n        create_remote_file(host, code_path_unix, code)\n\n        # Create the service.exe file by compiling the copied over C# code\n        # with PowerShell\n        service_exe_path_win = \"#{tmpdir}/#{config[:name]}.exe\".gsub('/', '\\\\')\n        create_service_exe = \"\\\"Add-Type\"\\\n          \" -TypeDefinition (Get-Content #{code_path_win} | Out-String)\"\\\n          \" -Language CSharp\"\\\n          \" -OutputAssembly #{service_exe_path_win}\"\\\n          \" -OutputType ConsoleApplication\"\\\n          \" -ReferencedAssemblies 'System.ServiceProcess'\\\"\"\n        on host, powershell(create_service_exe)\n\n        # Now register the service with SCM\n        register_service_with_scm = \"\\\"New-Service\"\\\n          \" #{config[:name]}\"\\\n          \" #{service_exe_path_win}\"\\\n          \" -DisplayName '#{config[:display_name]}'\"\\\n          \" -Description '#{config[:description]}'\"\\\n          \" -StartupType Automatic\\\"\"\n        on host, powershell(register_service_with_scm)\n\n        # Ensure that our service is deleted after the tests\n        teardown { delete_service(host, config[:name]) }\n      end\n\n      def delete_service(host, name)\n        # Check if our service has already been deleted. If so, then we\n        # have nothing else to do.\n        begin\n          on host, powershell(\"Get-Service #{name}\")\n        rescue Beaker::Host::CommandFailure\n          return\n        end\n\n        # Ensure that our service process is killed. We cannot do a Stop-Service here\n        # b/c there's a chance that our service could be in a pending state (e.g.\n        # \"PausePending\", \"ContinuePending\"). If this is the case, then Stop-Service\n        # will fail.\n        on host, powershell(\"\\\"Get-Process #{name} -ErrorAction SilentlyContinue | Stop-Process -Force\\\" | exit 0\")\n\n        # Now remove our service. We use sc.exe because older versions of PowerShell\n        # may not have the Remove-Service commandlet.\n        on agent, \"sc.exe delete #{name}\"\n      end\n\n      # Config should be a hash of <property> => <expected value>\n      def assert_service_properties_on(host, name, properties = {})\n        properties.each do |property, expected_value|\n          # We need to get the underlying WMI object for the service since that\n          # object contains all of our service properties. The one returned by\n          # Get-Service only has these properties for newer versions of PowerShell.\n          get_property_value = \"\\\"Get-WmiObject -Class Win32_Service\"\\\n            \" | Where-Object { \\\\$_.name -eq '#{name}' }\"\\\n            \" | ForEach-Object { \\\\$_.#{property} }\\\"\"\n\n          on(host, powershell(get_property_value)) do |result|\n            actual_value = result.stdout.chomp\n\n            property_str = \"#{name}[#{property}]\"\n            assert_match(expected_value, actual_value, \"EXPECTED: #{property_str} = #{expected_value}, ACTUAL: #{property_str} = #{actual_value}\")\n          end\n        end\n      end\n\n      def assert_service_startmode_delayed(host, name)\n        get_delayed_service = \"\\\"Get-ChildItem HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Services\"\\\n          \" | Where-Object { \\\\$_.Property -Contains 'DelayedAutoStart' -And \\\\$_.PsChildName -Like '#{name}' }\"\\\n          \" | Select-Object -ExpandProperty PSChildName\\\"\"\n\n        on(host, powershell(get_delayed_service)) do |result|\n          svc = result.stdout.chomp\n          assert(!svc.empty?, \"Service #{name} does not exist or is not a delayed service\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/lib/puppet/acceptance/windows_utils.rb",
    "content": "require 'puppet/acceptance/common_utils'\n\nmodule Puppet\n  module Acceptance\n    module WindowsUtils\n      require 'puppet/acceptance/windows_utils/service.rb'\n      require 'puppet/acceptance/windows_utils/package_installer.rb'\n\n      def profile_base(agent)\n        ruby = Puppet::Acceptance::CommandUtils.ruby_command(agent)\n        getbasedir = <<'END'\nputs ENV['USERPROFILE'].match(/(.*)\\\\\\\\[^\\\\\\\\]*/)[1]\nEND\n        on(agent, \"#{ruby} -e \\\"#{getbasedir}\\\"\").stdout.chomp\n      end\n\n      # Checks whether the account with the given username has the given password on a host\n      def assert_password_matches_on(host, username, password, msg = nil)\n        script = <<-PS1\n  Add-Type -AssemblyName System.DirectoryServices.AccountManagement\n  $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $env:COMPUTERNAME)\n  $ctx.ValidateCredentials(\"#{username}\", \"#{password}\")\n        PS1\n        result = execute_powershell_script_on(host, script) \n        assert_match(/True/, result.stdout.strip, msg)\n      end\n\n      def deny_administrator_access_to(host, filepath)\n        # we need to create a fake directory in the user's tempdir with powershell because the ACL\n        # perms set down by cygwin when making tempdirs makes the ACL unusable. Thus we create a\n        # tempdir using powershell and pull its' ACL as a starting point for the new ACL.\n        script = <<-PS1\n  mkdir -Force $env:TMP\\\\fake-dir-for-acl\n  $acl = Get-ACL $env:TMP\\\\fake-dir-for-acl\n  rm -Force $env:TMP\\\\fake-dir-for-acl\n  $ar = New-Object system.security.accesscontrol.filesystemaccessrule(\"Administrator\",\"FullControl\",\"Deny\")\n  $acl.SetAccessRule($ar)\n  Set-ACL #{filepath} $acl\n        PS1\n        execute_powershell_script_on(host, script)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/pending/ticket_11860_exec_should_not_override_locale.rb",
    "content": "test_name \"#11860: exec resources should not override system locale\"\n\n#######################################################################################\n#                                   NOTE\n#######################################################################################\n#\n# This test won't run properly until the test agent nodes have the Spanish language\n# pack installed on them.  On an ubuntu system, this can be done with the following:\n#\n#  apt-get install language-pack-es-base\n#\n# Also, this test depends on the following pull requests:\n#\n#  https://github.com/puppetlabs/puppet-acceptance/pull/123\n#  https://github.com/puppetlabs/facter/pull/159\n#\n#######################################################################################\n\ntemp_file_name = \"/tmp/11860_exec_should_not_override_locale.txt\"\nlocale_string = \"es_ES.UTF-8\"\n\n\nstep \"Check value of LANG environment variable\"\n\n# in this step we are going to run an \"exec\" block that writes the value of the LANG\n#  environment variable to a file.  We need to verify that exec's are no longer\n#  forcefully setting this var to 'C'.\n\n\ntest_LANG_manifest = <<HERE\nexec {\"es_ES locale print LANG environment variable\":\n      command =>  \"/usr/bin/printenv LANG > #{temp_file_name}\",\n}\nHERE\n\n# apply the manifest.\n#\n# note that we are passing in an extra :environment argument, which will cause the\n# framework to temporarily set this variable before executing the puppet command.\n# this lets us know what value we should be looking for as the output of the exec.\n\napply_manifest_on agents, test_LANG_manifest, :environment => {:LANG => locale_string}\n\n# cat the temp file and make sure it contained the correct value.\non(agents, \"cat #{temp_file_name}\").each do |result|\n  assert_equal(locale_string, \"#{result.stdout.chomp}\", \"Unexpected result for host '#{result.host}'\")\nend\n\n\n\nstep \"Check for locale-specific output of cat command\"\n\n# in this step we are going to run an \"exec\" block that runs the \"cat\" command.  The command\n# is intentionally invalid, because we are going to run it using a non-standard locale and\n# we want to confirm that the error message is in the correct language.\n\ntest_cat_manifest = <<HERE\nexec {\"es_ES locale invalid cat command\":\n      command =>  \"/bin/cat SOME_FILE_THAT_DOESNT_EXIST > #{temp_file_name} 2>&1\",\n      returns => 1,\n}\nHERE\n\n# apply the manifest, again passing in the extra :environment argument to set our locale.\napply_manifest_on agents, test_cat_manifest, :environment => {:LANG => locale_string}\n\n# cat the output file and ensure that the error message is in spanish\non(agents, \"cat #{temp_file_name}\").each do |result|\n  assert_equal(\"/bin/cat: SOME_FILE_THAT_DOESNT_EXIST: No existe el fichero o el directorio\",\n               \"#{result.stdout.chomp}\", \"Unexpected result for host '#{result.host}'\")\nend\n\n\nstep \"cleanup\"\n\n# remove the temp file\non agents, \"rm -f #{temp_file_name}\"\n\n\n\n"
  },
  {
    "path": "acceptance/pending/ticket_4149_parseonly_should_not_fail.rb",
    "content": "test_name \"#4149: parseonly should do the right thing\"\n\nstep \"test with a manifest with syntax errors\"\nmanifest = 'class someclass { notify { \"hello, world\" } }'\napply_manifest_on(agents, manifest, :parseonly => true, :acceptable_exit_codes => [1]) {\n  stdout =~ /Could not parse for .*: Syntax error/ or\n    fail_test(\"didn't get a reported systax error\")\n}\n\nstep \"test with a manifest with correct syntax\"\napply_manifest_on agents,'class someclass { notify(\"hello, world\") }', :parseonly => true\n\n# REVISIT: This tests the current behaviour, which is IMO not actually the\n# correct behaviour.  On the other hand, if we change this we might\n# unexpectedly break things out in the wild, so better to be warned than to be\n# surprised by it. --daniel 2010-12-22\nstep \"test with a class with an invalid attribute\"\napply_manifest_on agents, 'file { \"/tmp/whatever\": fooble => 1 }', :parseonly => true\n"
  },
  {
    "path": "acceptance/pending/ticket_4151_defined_function_should_not_return_true_for_unrealized_virtual_resources.rb",
    "content": "test_name \"#4151: defined function should not return true for unrealized virtual resources\"\n\n# Jeff McCune <jeff@puppetlabs.com>\n# 2010-07-06\n#\n# This script is expected to exit non-zero if ticket 4151 has not been\n# fixed.\n#\n# The expected behavior is for defined() to only return true if a virtual\n# resource has been realized.\n#\n# This test creates a virtual resource, does NOT realize it, then calls\n# the defined() function against it.  If defined returns true, there will\n# be an error since Notify[\"goodbye\"] will require a resource which has\n# not been realized.\n\n\nmanifest1 =  %q{\n    @notify { \"hello\": }\n    if (defined(Notify[\"hello\"])) { $requires = [ Notify[\"hello\"] ] }\n    notify { \"goodbye\": require => $requires }\n}\n\napply_manifest_on(agents, manifest1)\n"
  },
  {
    "path": "acceptance/pending/ticket_5027_warn_on_dynamic_scope.rb",
    "content": "test_name \"#5027: Issue warnings when using dynamic scope\"\n\nstep \"Apply dynamic scoping manifest on agents\"\napply_manifest_on agents, %q{\n  $foo = 'foo_value'\n\n  class a {\n      $bar = 'bar_value'\n\n      include b\n  }\n\n  class b inherits c {\n      notify { $baz: } # should not generate a warning -- inherited from class c\n      notify { $bar: } # should generate a warning -- uses dynamic scoping\n      notify { $foo: } # should not generate a warning -- comes from top scope\n  }\n\n  class c {\n      $baz = 'baz_value'\n  }\n\n  include a\n}\n\nstep \"Verify deprecation warning\"\nfail_test \"Deprecation warning not issued\" unless\n  stdout.include? 'warning: Dynamic lookup of $bar will not be supported in future versions. Use a fully-qualified variable name or parameterized classes.'\n"
  },
  {
    "path": "acceptance/pending/ticket_5224_exec_should_unset_user_env_vars.rb",
    "content": "test_name \"#5224: exec resources should unset user-related environment variables\"\n\n#######################################################################################\n#                                   NOTE\n#######################################################################################\n#\n# This test depends on the following pull requests:\n#\n#  https://github.com/puppetlabs/puppet-acceptance/pull/123\n#\n# because it needs to be able to set some environment variables for the duration of\n# the puppet commands.  Shouldn't be moved out of 'pending' until after that has been\n# merged.\n#\n#######################################################################################\n\n\ntemp_file_name = \"/tmp/5224_exec_should_unset_user_env_vars.txt\"\nsentinel_string = \"Abracadabra\"\n\n\n# these should match up with the value of Puppet::Util::POSIX_USER_ENV_VARS,\n# but I don't have access to that from here, so this is unfortunately hard-coded\n# (cprice 2012-01-27)\nPOSIX_USER_ENV_VARS = ['HOME', 'USER', 'LOGNAME']\n\n\n\nstep \"Check value of user-related environment variables\"\n\n# in this step we are going to run some \"exec\" blocks that writes the value of the\n# user-related environment variables to a file.  We need to verify that exec's are\n# unsetting these vars.\n\n\ntest_printenv_manifest = <<HERE\nexec {\"print %s environment variable\":\n      command =>  \"/usr/bin/printenv %s > #{temp_file_name}\",\n}\nHERE\n\n# loop over the vars that we care about; these should match up with the value of Puppet::Util::POSIX_USER_ENV_VARS,\n# but I don't have access to that from here, so this is unfortunately hard-coded (cprice 2012-01-27)\nPOSIX_USER_ENV_VARS.each do |var|\n\n  # apply the manifest.\n  #\n  # note that we are passing in an extra :environment argument, which will cause the\n  # framework to temporarily set this variable before executing the puppet command.\n  # this lets us know what value we should be looking for as the output of the exec.\n\n  apply_manifest_on agents, test_printenv_manifest % [var, var], :environment => {var => sentinel_string}\n\n  # cat the temp file and make sure it contained the correct value.\n  on(agents, \"cat #{temp_file_name}\").each do |result|\n    assert_equal(\"\", \"#{result.stdout.chomp}\", \"Unexpected result for host '#{result.host}', environment var '#{var}'\")\n  end\nend\n\n\n\n\nstep \"Check value of user-related environment variables when they are provided as part of the exec resource\"\n\n# in this step we are going to run some \"exec\" blocks that write the value of the\n# user-related environment variables to a file.  However, this time, the manifest\n# explicitly overrides these variables in the \"environment\" section, so we need to\n# be sure that we are respecting these overrides.\n\ntest_printenv_with_env_overrides_manifest = <<HERE\nexec {\"print %s environment variable\":\n      command =>  \"/usr/bin/printenv %s > #{temp_file_name}\",\n      environment => [\"%s=#{sentinel_string}\", \"FOO=bar\"]\n}\nHERE\n\n# loop over the vars that we care about;\nPOSIX_USER_ENV_VARS.each do |var|\n\n  # apply the manifest.\n  #\n  # note that we are passing in an extra :environment argument, which will cause the\n  # framework to temporarily set this variable before executing the puppet command.\n  # this lets us know what value we should be looking for as the output of the exec.\n\n  apply_manifest_on agents, test_printenv_with_env_overrides_manifest % [var, var, var],\n                    :environment => {var => sentinel_string}\n\n  # cat the temp file and make sure it contained the correct value.\n  on(agents, \"cat #{temp_file_name}\").each do |result|\n    assert_equal(sentinel_string, \"#{result.stdout.chomp}\",\n                 \"Unexpected result for host '#{result.host}', environment var '#{var}'\")\n  end\nend\n\n\n\n\n\n\n\n\nstep \"cleanup\"\n\n# remove the temp file\non agents, \"rm -f #{temp_file_name}\"\n\n\n\n"
  },
  {
    "path": "acceptance/pending/ticket_6928_puppet_master_parse_fails.rb",
    "content": "test_name \"#6928: Puppet --parseonly should return deprication message\"\n\n# Create good and bad formatted manifests\nstep \"Master: create valid, invalid formatted manifests\"\ncreate_remote_file(master, '/tmp/good.pp', %w{notify{good:}} )\ncreate_remote_file(master, '/tmp/bad.pp', 'notify{bad:')\n\nstep \"Master: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning\"\non master, puppet_master( %w{--parseonly /tmp/bad.pp} ), :acceptable_exit_codes => [ 1 ]\n  fail_test \"Deprecation warning not issued for --parseonly\" unless\n    stdout.include? '--parseonly has been removed. Please use \\'puppet parser validate <manifest>\\''\n\nstep \"Agents: create valid, invalid formatted manifests\"\nagents.each do |host|\n  create_remote_file(host, '/tmp/good.pp', %w{notify{good:}} )\n  create_remote_file(host, '/tmp/bad.pp', 'notify{bad:')\nend\n\nstep \"Agents: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning\"\nagents.each do |host|\n  on(host, \"puppet --parseonly /tmp/bad.pp}\", :acceptable_exit_codes => [ 1 ]) do\n    fail_test \"Deprecation warning not issued for --parseonly\" unless\n      stdout.include? '--parseonly has been removed. Please use \\'puppet parser validate <manifest>\\''\n  end\nend\n\nstep \"Test Face for ‘parser validate’ with good manifest -- should pass\"\nagents.each do |host|\n  on(host, \"puppet parser validate /tmp/good.pp\", :acceptable_exit_codes => [ 0 ])\nend\n\nstep \"Test Face for ‘parser validate’ with bad manifest -- should fail\"\nagents.each do |host|\n  on(host, \"puppet parser validate /tmp/bad.pp\", :acceptable_exit_codes => [ 1 ]) do\n    fail_test \"Bad manifest detection failed\" unless\n      stderr.include? 'Could not run: Could not parse for environment production'\n  end\nend\n"
  },
  {
    "path": "acceptance/teardown/common/099_Archive_Logs.rb",
    "content": "require 'date'\n\ndef file_glob(host, path)\n  result = on(host, \"ls #{path}\", :acceptable_exit_codes => [0, 2])\n  return [] if result.exit_code != 0\n  return result.stdout.strip.split(\"\\n\")\nend\n\n# This test is prefixed with zzz so it will hopefully run last.\ntest_name 'Backup puppet logs and app data on all hosts' do\n  today = Date.today().to_s\n  # truncate the job name so it only has the name-y part and no parameters\n  job_name = (ENV['JOB_NAME'] || 'unknown_jenkins_job')\n               .sub(/[A-Z0-9_]+=.*$/, '')\n               .gsub(/[\\/,.]/, '_')[0..200]\n  archive_name = \"#{job_name}__#{ENV['BUILD_ID']}__#{today}__sut-files.tgz\"\n  archive_root = \"SUT_#{today}\"\n\n  hosts.each do |host|\n    step(\"Capturing log errors for #{host}\") do\n      case host[:platform]\n      when /windows/\n        # on Windows, all of the desired data (including logs) is in the data dir\n        puppetlabs_data = 'C:/ProgramData/PuppetLabs'\n        archive_file_from(host, puppetlabs_data, {}, archive_root, archive_name)\n\n        # Note: Windows `ls` uses absolute paths for all matches when an absolute path is supplied.\n        tempdir = 'C:/Windows/TEMP'\n        file_glob(host, File.join(tempdir, 'install-puppet-*.log')).each do |install_log|\n          archive_file_from(host, install_log, {}, archive_root, archive_name)\n        end\n        file_glob(host, File.join(tempdir, 'puppet-*-installer.log')).each do |install_log|\n          archive_file_from(host, install_log, {}, archive_root, archive_name)\n        end\n      else\n        puppetlabs_logdir = '/var/log/puppetlabs'\n        grep_for_alerts = if host[:platform] =~ /solaris/\n                            \"egrep -i 'warn|error|fatal'\"\n                          elsif host[:platform] =~ /aix/\n                            \"grep -iE -B5 -A10 'warn|error|fatal'\"\n                          else\n                            \"grep -i -B5 -A10 'warn\\\\|error\\\\|fatal'\"\n                          end\n\n        ## If there are any PL logs, try to echo all warning, error, and fatal\n        ## messages from all PL logs to the job's output\n        on(host, <<-GREP_FOR_ALERTS, :accept_all_exit_codes => true )\n  if [ -d #{puppetlabs_logdir} ] && [ -n \"$(find #{puppetlabs_logdir} -name '*.log*')\" ]; then\n    for log in $(find #{puppetlabs_logdir} -name '*.log*'); do\n      # grep /dev/null only to get grep to print filenames, since -H is not in POSIX spec for grep\n      #{grep_for_alerts} $log /dev/null;\n      echo \"\"\n    done\n  fi\n  GREP_FOR_ALERTS\n\n        step(\"Archiving logs for #{host} into #{archive_name} (muzzling everything but :warn or higher beaker logs...)\") do\n          ## turn the logger off to avoid getting hundreds of lines of scp progress output\n          previous_level = @logger.log_level\n          @logger.log_level = :warn\n\n          pxp_cache = '/opt/puppetlabs/pxp-agent/spool'\n          puppetlabs_data = '/etc/puppetlabs'\n\n          version_lookup_result = on(host, \"cat /opt/puppetlabs/puppet/VERSION\", :accept_all_exit_codes => true)\n\n          # If we can't find a VERSION file, chances are puppet wasn't\n          # installed and these paths aren't present.  Beaker's\n          # archive_file_from() will fail if it can't find the file, and we\n          # want to proceed...\n          if version_lookup_result.exit_code == 0\n            agent_version = version_lookup_result.output.strip\n            archive_file_from(host, pxp_cache, {}, archive_root, archive_name) unless version_is_less(agent_version, \"1.3.2\")\n            archive_file_from(host, puppetlabs_data, {}, archive_root, archive_name)\n            archive_file_from(host, puppetlabs_logdir, {}, archive_root, archive_name)\n          end\n\n          syslog_dir = '/var/log'\n          syslog_name = 'messages'\n          if host[:platform] =~ /ubuntu|debian/\n            syslog_name = 'syslog'\n          elsif host[:platform] =~ /solaris/\n            syslog_dir = '/var/adm'\n            # Next few lines are for debugging POOLER-200, once that is resolved this can be removed\n            @logger.log_level = previous_level\n            on(host, 'egrep -i \\'reboot after panic\\' /var/adm/messages', :acceptable_exit_codes => [0,1,2])\n            @logger.log_level = :warn\n          elsif host[:platform] =~ /osx/\n            syslog_name = \"system.log\"\n          elsif host[:platform] =~ /fedora/\n            on(host, \"journalctl --no-pager > /var/log/messages\")\n          elsif host[:platform] =~ /aix/\n            on(host, \"alog -o -t console > /var/log/messages\")\n          end\n\n          syslog_path = File.join(syslog_dir, syslog_name)\n          if host.file_exist?(syslog_path)\n            archive_file_from(host, syslog_path, {}, archive_root, archive_name)\n          end\n\n          ## turn the logger back on in case someone else wants to log things\n          @logger.log_level = previous_level\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/agent/agent_disable_lockfile.rb",
    "content": "test_name \"C4553 - agent --disable/--enable functionality should manage the agent lockfile properly\"\ntag 'audit:integration', # lockfile uses the standard `vardir` location to store/query lockfile.\n                         # The validation of the `vardir` at the OS level\n                         # should be accomplished in another test.\n    'audit:high',\n    'audit:refactor'     # This test should not require a master. Remove the use of `with_puppet_running_on`.\n\n#\n# This test is intended to ensure that puppet agent --enable/--disable\n#  work properly, both in terms of complying with our public \"API\" around\n#  lockfile semantics ( http://links.puppet.com/agent_lockfiles ), and\n#  in terms of actually restricting or allowing new agent runs to begin.\n#\n\nrequire 'puppet/acceptance/temp_file_utils'\n\nextend Puppet::Acceptance::TempFileUtils\n\ninitialize_temp_dirs()\n@all_tests_passed = false\n\n\n###############################################################################\n# BEGIN TEST LOGIC\n###############################################################################\n\nteardown do\n  if @all_tests_passed then\n    remove_temp_dirs()\n  end\n  agents.each do |agent|\n    on(agent, puppet('agent', \"--enable\"))\n  end\nend\n\ntuples = [\n    [\"reason not specified\", false],\n    [\"I'm busy; go away.'\", true]\n]\n\nwith_puppet_running_on(master, {}) do\n  tuples.each do |expected_message, explicitly_specify_message|\n    step \"disable the agent; specify message? '#{explicitly_specify_message}', message: '#{expected_message}'\" do\n      agents.each do |agent|\n        if (explicitly_specify_message)\n          on(agent, puppet('agent', \"--disable \\\"#{expected_message}\\\"\"))\n        else\n          on(agent, puppet('agent', \"--disable\"))\n        end\n\n        agent_disabled_lockfile = \"#{agent.puppet['vardir']}/state/agent_disabled.lock\"\n        unless file_exists?(agent, agent_disabled_lockfile) then\n          fail_test(\"Failed to create disabled lock file '#{agent_disabled_lockfile}' on agent '#{agent}'\")\n        end\n        lock_file_content = file_contents(agent, agent_disabled_lockfile)\n\n        # This is a hack; we should parse the JSON into a hash, but I don't\n        # think I have a library available from the acceptance test framework\n        # that I can use to do that.  So I'm falling back to <gasp> regex.\n        lock_file_content_regex = /\"disabled_message\"\\s*:\\s*\"#{expected_message}\"/\n        unless lock_file_content =~ lock_file_content_regex\n          fail_test(\"Disabled lock file contents invalid; expected to match '#{lock_file_content_regex}', got '#{lock_file_content}' on agent '#{agent}'\")\n        end\n      end\n    end\n\n    step \"attempt to run the agent (message: '#{expected_message}')\" do\n      agents.each do |agent|\n        on(agent, puppet('agent', \"--test\"), :acceptable_exit_codes => [1]) do |result|\n          disabled_regex = /administratively disabled.*'#{expected_message}'/\n          unless result.stdout =~ disabled_regex\n            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'\n          end\n        end\n      end\n    end\n\n    step \"enable the agent (message: '#{expected_message}')\" do\n      agents.each do |agent|\n\n        agent_disabled_lockfile = \"#{agent.puppet['vardir']}/state/agent_disabled.lock\"\n        on(agent, puppet('agent', \"--enable\"))\n        if file_exists?(agent, agent_disabled_lockfile) then\n          fail_test(\"Failed to remove disabled lock file '#{agent_disabled_lockfile}' on agent '#{agent}'\")\n        end\n      end\n    end\n\n    step \"verify that we can run the agent (message: '#{expected_message}')\" do\n      agents.each do |agent|\n        on(agent, puppet('agent', \"--test\"))\n      end\n    end\n  end # tuples block\nend # with_puppet_running_on block\n\n@all_tests_passed = true\n"
  },
  {
    "path": "acceptance/tests/agent/agent_fails_with_unknown_resource.rb",
    "content": "test_name \"agent run should fail if it finds an unknown resource type\" do\n  tag 'audit:high',\n      'audit:integration'\n\n  require 'puppet/acceptance/common_utils'\n\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  step \"agent should fail when it can't find a resource\" do\n    vendor_modules_path = master.tmpdir('vendor_modules')\n    tmp_environment = mk_tmp_environment_with_teardown(master, 'tmp')\n\n    site_pp_content = <<-SITEPP\n      define foocreateresource($one) {\n        $msg = 'hello'\n        notify { $name: message => $msg }\n      }\n      class example($x) {\n        if $x == undef or $x == [] or $x == '' {\n          notice 'foo'\n          return()\n        }\n        notice 'bar'\n      }\n      node default {\n        class { example: x => [] }\n        create_resources('foocreateresource', {'blah'=>{'one'=>'two'}})\n        mycustomtype{'foobar':}\n      }\n    SITEPP\n    manifests_path = \"/tmp/#{tmp_environment}/manifests\"\n    on(master, \"mkdir -p '#{manifests_path}'\")\n    create_remote_file(master, \"#{manifests_path}/site.pp\", site_pp_content)\n\n    custom_type_content = <<-CUSTOMTYPE\n      Puppet::Type.newtype(:mycustomtype) do\n        @doc = \"Create a new mycustomtype thing.\"\n\n        newparam(:name, :namevar => true) do\n          desc \"Name of mycustomtype instance\"\n        end\n\n        def refresh\n        end\n      end\n    CUSTOMTYPE\n    type_path = \"#{vendor_modules_path}/foo/lib/puppet/type\"\n    on(master, \"mkdir -p '#{type_path}'\")\n    create_remote_file(master, \"#{type_path}/mycustomtype.rb\", custom_type_content)\n\n    on(master, \"chmod -R 750 '#{vendor_modules_path}' '/tmp/#{tmp_environment}'\")\n    on(master, \"chown -R #{master.puppet['user']}:#{master.puppet['group']} '#{vendor_modules_path}' '/tmp/#{tmp_environment}'\")\n\n    master_opts = {\n      'main' => {\n        'environment' => tmp_environment,\n        'vendormoduledir' => vendor_modules_path\n       }\n    }\n\n    with_puppet_running_on(master, master_opts) do\n      agents.each do |agent|\n        teardown do\n          agent.rm_rf(vendor_modules_path)\n        end\n\n        # override vendormoduledir in case agent and server are on the same host\n        agent_dir = get_test_file_path(agent, 'vendormodulepath')\n        on(agent, puppet('agent', '-t', '--environment', tmp_environment, '--vendormoduledir', agent_dir), acceptable_exit_codes: [1]) do |result|\n          assert_match(/Error: Failed to apply catalog: Resource type 'Mycustomtype' was not found/, result.stderr)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/agent/agent_parses_json_catalog.rb",
    "content": "test_name \"C99978: Agent parses a JSON catalog\"\n\ntag 'risk:high',\n    'audit:high',        # tests defined catalog format\n    'audit:integration', # There is no OS specific risk here.\n    'server',\n    'catalog:json'\n\nrequire 'puppet/acceptance/common_utils'\nrequire 'json'\n\nstep \"Agent parses a JSON catalog\" do\n  agents.each do |agent|\n    # Path to a ruby binary\n    ruby = Puppet::Acceptance::CommandUtils.ruby_command(agent)\n  \n    # Refresh the catalog\n    on(agent, puppet(\"agent --test\"))\n\n    # The catalog file should be parseable JSON\n    json_catalog = File.join(agent.puppet['client_datadir'], 'catalog',\n                             \"#{agent.puppet['certname']}.json\")\n    on(agent, \"cat #{json_catalog} | #{ruby} -rjson -e 'JSON.parse(STDIN.read)'\")\n\n    # Can the agent parse it as JSON?\n    on(agent, puppet(\"catalog find --terminus json > /dev/null\"))\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/agent/fallback_to_cached_catalog.rb",
    "content": "test_name \"fallback to the cached catalog\"\n\ntag 'audit:high',\n    'audit:integration', # This test is not OS sensitive.\n    'audit:refactor'     # A catalog fixture can be used for this test. Remove the usage of `with_puppet_running_on`.\n\nstep \"run agents once to cache the catalog\" do\n  with_puppet_running_on master, {} do\n    on(agents, puppet(\"agent -t\"))\n  end\nend\n\nstep \"run agents again, verify they use cached catalog\" do\n  agents.each do |agent|\n    # can't use --test, because that will set usecacheonfailure=false\n    # We use a server that the agent can't possibly talk to in order\n    # to guarantee that no communication can take place.\n    on(agent, puppet(\"agent --onetime --no-daemonize --server puppet.example.com --verbose\")) do |result|\n      assert_match(/Using cached catalog/, result.stdout) unless agent['locale'] == 'ja'\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/agent/last_run_summary_report.rb",
    "content": "test_name \"The 'last_run_summary.yaml' report has the right location and permissions\" do\n  tag 'audit:high'\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n  \n  agents.each do |agent|\n    skip_test('This test does not work on Windows in japanese') if agent['platform'] =~ /windows/ && agent['locale'] == 'ja'\n\n    custom_publicdir = agent.tmpdir('custom_public_dir')\n\n    statedir = on(agent, puppet('config print statedir')).stdout.chomp\n    fail_test(\"The 'statedir' config is not set!\") if statedir.empty?\n\n    publicdir = on(agent, puppet('config print publicdir')).stdout.chomp\n    fail_test(\"The 'publicdir' config is not set!\") if publicdir.empty?\n\n    teardown do\n      agent.rm_rf(custom_publicdir)\n      agent.rm_rf(\"#{publicdir}/*\") unless publicdir.empty?\n      on(agent, puppet(\"config set publicdir #{publicdir}\"))\n    end\n\n    step \"Check if '#{publicdir}' was created during puppet installation\" do\n      on(agent, \"ls #{publicdir}\", :acceptable_exit_codes => [0])\n    end\n\n    step \"Check if '#{publicdir}' has '0755' permissions\" do\n      if agent['platform'] =~ /windows/\n        on(agent, \"icacls #{publicdir}\") do |result|\n          # Linux 'Owner' permissions class equivalent\n          assert_match(/BUILTIN\\\\Administrators:.*\\(F\\)/, result.stdout)\n\n          # Known issue on Windows: 'C:\\ProgramData\\PuppetLabs\\puppet' permissions are inherited\n          # by its subfolders and it does not have any permissions for 'Everyone' (see 'PuppetAppDir'\n          # in 'puppet-agent/resources/windows/wix/appdatafiles.wxs')\n          # Below line should be added when solution is found:\n          # assert_match(/Everyone:.*\\(RX\\)/, result.stdout)\n        end\n      else\n        on(agent, \"ls -al #{publicdir}\") do |result|\n          assert_match(/rwxr-xr-x.+\\.$/, result.stdout)\n        end\n      end\n    end\n\n    step \"Create the 'last_run_summary.yaml' report file by applying catalog\" do\n      on(agent, puppet('agent -t')) do |result|\n        assert_match('Applied catalog', result.stdout)\n      end\n    end\n\n    step \"Check if the 'last_run_summary.yaml' report file created has '0640' permissions\" do\n      if agent['platform'] =~ /windows/\n        on(agent, \"icacls #{File.join(publicdir, 'last_run_summary.yaml')}\") do |result|\n          # Linux 'Owner' premissions class equivalent\n          assert_match('Administrator:(R,W', result.stdout)\n          # Linux 'Group' permissions class equivalent\n          assert_match('None:(R)', result.stdout)\n          # Linux 'Public' permissions class equivalent\n          assert_match('Everyone:(Rc,S,RA)', result.stdout)\n          # According to icacls docs:\n          # Rc = Read control\n          # S  = Synchronize\n          # RA = Read attributes\n          # More at https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/icacls\n        end\n      else\n        on(agent, \"ls -al #{publicdir}\") do |result|\n          assert_match(/rw-r-----.+last_run_summary\\.yaml$/, result.stdout)\n        end\n      end\n    end\n\n    step \"Check that '#{statedir}' exists and has no 'last_run_summary.yaml' file\" do\n      on(agent, \"ls #{statedir}\",:acceptable_exit_codes => [0]) do |result|\n        refute_match(/last_run_summary.yaml/, result.stdout)\n      end\n    end\n    \n    step \"Check that 'publicdir' can be reconfigured\" do\n      on(agent, puppet(\"config set publicdir #{custom_publicdir}\"))\n      on(agent, puppet('config print publicdir')) do |result|\n        assert_match(custom_publicdir, result.stdout)\n      end\n    end\n\n    step \"Create a new 'last_run_summary.yaml' report file by applying catalog\" do\n      on(agent, puppet('agent -t')) do |result|\n        assert_match('Applied catalog', result.stdout)\n      end\n    end\n\n    step \"Check if the 'last_run_summary.yaml' report file was created in the new location and still has '0640' permissions\" do\n      if agent['platform'] =~ /windows/\n        on(agent, \"icacls #{File.join(custom_publicdir, 'last_run_summary.yaml')}\") do |result|\n          # Linux 'Owner' premissions class equivalent\n          assert_match('Administrator:(R,W', result.stdout)\n          # Linux 'Group' permissions class equivalent\n          assert_match('None:(R)', result.stdout)\n          # Linux 'Public' permissions class equivalent\n          assert_match('Everyone:(Rc,S,RA)', result.stdout)\n          # According to icacls docs:\n          # Rc = Read control\n          # S  = Synchronize\n          # RA = Read attributes\n          # More at https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/icacls\n        end\n      else\n        on(agent, \"ls -al #{custom_publicdir}\") do |result|\n          assert_match(/rw-r-----.+last_run_summary\\.yaml$/, result.stdout)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/aix/aix_package_provider.rb",
    "content": "test_name \"aix package provider should work correctly\" do\n\n  tag 'audit:high',\n      'audit:acceptance'  # OS specific by definition.\n\n  confine :to, :platform => /aix/\n\n  dir = \"/tmp/aix-packages-#{$$}\"\n\n  def assert_package_version(package, expected_version)\n    # The output of lslpp is a colon-delimited list like:\n    # sudo:sudo.rte:1.8.6.4: : :C: :Configurable super-user privileges runtime: : : : : : :0:0:/:\n    # We want the version, so grab the third field\n    on(hosts, \"lslpp -qLc #{package} | cut -f3 -d:\") do |result|\n      actual_version = result.stdout.chomp\n      assert_equal(expected_version, actual_version, \"Installed package version #{actual_version} does not match expected version #{expected_version}\")\n    end\n  end\n\n  def get_package_manifest(package, version, sourcedir)\n    <<-MANIFEST\n    package { '#{package}':\n      ensure   => '#{version}',\n      provider => aix,\n      source   => '#{sourcedir}',\n    }\n    MANIFEST\n  end\n\n  package = 'sudo.rte'\n  version1 = '1.7.10.4'\n  version2 = '1.8.6.4'\n\n  teardown do\n    on hosts, \"rm -rf #{dir}\"\n    on hosts, \"installp -u #{package}\"\n  end\n\n  step \"download packages to use for test\" do\n    on hosts, \"mkdir -p #{dir}\"\n    on hosts, \"curl https://artifactory.delivery.puppetlabs.net/artifactory/generic_enterprise__local/misc/sudo.#{version1}.aix51.lam.bff > #{dir}/sudo.#{version1}.aix51.lam.bff\"\n    on hosts, \"curl https://artifactory.delivery.puppetlabs.net/artifactory/generic_enterprise__local/misc/sudo.#{version2}.aix51.lam.bff > #{dir}/sudo.#{version2}.aix51.lam.bff\"\n  end\n\n  step \"install the older version of package\" do\n    apply_manifest_on(hosts, get_package_manifest(package, version1, dir), :catch_failures => true)\n  end\n\n  step \"verify package is installed and at the correct version\" do\n    assert_package_version package, version1\n  end\n\n  step \"install a newer version of the package\" do\n    apply_manifest_on(hosts, get_package_manifest(package, version2, dir), :catch_failures => true)\n  end\n\n  step \"verify package is installed and at the newer version\" do\n    assert_package_version package, version2\n  end\n\n  step \"test that downgrading fails by trying to install an older version of the package\" do\n    apply_manifest_on(hosts, get_package_manifest(package, version1, dir), :acceptable_exit_codes => [4,6]) do |res|\n      assert_match(/aix package provider is unable to downgrade packages/, res.stderr, \"Didn't get an error about downgrading packages\")\n    end\n  end\n\n  step \"uninstall the package\" do\n    apply_manifest_on(hosts, get_package_manifest(package, 'absent', dir), :catch_failures => true)\n  end\n\n  step \"verify the package is gone\" do\n    on hosts, \"lslpp -qLc #{package}\", :acceptable_exit_codes => [1]\n  end\n\n  step \"install the older version of package\" do\n    apply_manifest_on(hosts, get_package_manifest(package, version1, dir), :catch_failures => true)\n  end\n\n  step \"verify package is installed and at the correct version\" do\n    assert_package_version package, version1\n  end\n\n  step \"install latest version of the package\" do\n    apply_manifest_on(hosts, get_package_manifest(package, 'latest', dir), :catch_failures => true)\n  end\n\n  step \"verify package is installed and at the correct version\" do\n    assert_package_version package, version2\n  end\n\n  step \"PUP-7818 remove a package without defining the source metaparameter\" do\n    manifest = get_package_manifest(package, 'latest', dir)\n    manifest = manifest + \"package { 'nonexistant_example_package.rte': ensure => absent, }\"\n    apply_manifest_on(hosts, manifest, :catch_failures => true)\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/aix/nim_package_provider.rb",
    "content": "test_name \"NIM package provider should work correctly\"\n\ntag 'audit:high',\n    'audit:acceptance'  # OS specific by definition\n\n# nim test is slow, confine to only aix 7.2 and recent puppet versions\nconfine :to, :platform => \"aix\" do |aix|\n  version = on(aix, 'puppet --version').stdout\n  version &&\n    Gem::Version.new(version) > Gem::Version.new('6.4.0') &&\n    on(aix, 'facter os.release.full').stdout == '7.2'\nend\n\nteardown do\n    test_apply('cdrecord', 'absent', '')\n    test_apply('puppet.test.rte', 'absent', '')\nend\n\ndef assert_package_version(package, expected_version)\n  # The output of lslpp is a colon-delimited list like:\n  # sudo:sudo.rte:1.8.6.4: : :C: :Configurable super-user privileges runtime: : : : : : :0:0:/:\n  # We want the version, so grab the third field\n  on(hosts, \"lslpp -qLc #{package} | cut -f3 -d:\") do |result|\n    actual_version = result.stdout.chomp\n    assert_equal(expected_version, actual_version, \"Installed package version #{actual_version} does not match expected version #{expected_version}\")\n  end\nend\n\ndef get_manifest(package, ensure_value)\n  <<MANIFEST\npackage {'#{package}':\n  ensure   => '#{ensure_value}',\n  source   => 'lpp_custom',\n  provider => nim,\n}\nMANIFEST\nend\n\ndef test_apply(package_name, ensure_value, expected_version)\n  manifest = get_manifest(package_name, ensure_value)\n  on hosts, puppet_apply([\"--detailed-exitcodes\", \"--verbose\"]),\n     {:stdin => manifest, :acceptable_exit_codes => [2]}\n\n  step \"validate installed package version\" do\n    assert_package_version package_name, expected_version\n  end\n\n  step \"run again to ensure idempotency\" do\n    on hosts, puppet_apply([\"--detailed-exitcodes\", \"--verbose\"]),\n       {:stdin => manifest, :acceptable_exit_codes => [0]}\n  end\n\n  step \"validate installed package version\" do\n    assert_package_version package_name, expected_version\n  end\nend\n\n# These two packages live in an LPP source on the NIM master. Details\n# on our nim masters are available at\n# https://confluence.puppetlabs.com/display/OPS/IBM+Power+LPARs\npackage_types = {\n    \"RPM\" => {\n        :package_name    => \"cdrecord\",\n        :old_version     => '1.9-6',\n        :new_version     => '1.9-9'\n    },\n    \"BFF\" => {\n        :package_name    => \"puppet.test.rte\",\n        :old_version     => '1.0.0.0',\n        :new_version     => '2.0.0.0'\n    }\n}\n\nstep \"Setup: ensure test packages are not installed\" do\n  pkgs = ['cdrecord', 'puppet.test.rte']\n  pkgs.each do |pkg|\n    on hosts, puppet_apply([\"--detailed-exitcodes\", \"--verbose\"]),\n       {:stdin => get_manifest(pkg, 'absent'), :acceptable_exit_codes => [0,2]}\n  end\nend\n\npackage_types.each do |package_type, details|\n  step \"install a #{package_type} package via 'ensure=>present'\" do\n    package_name = details[:package_name]\n    version = details[:new_version]\n    test_apply(package_name, 'present', version)\n  end\n\n  step \"uninstall a #{package_type} package via 'ensure=>absent'\" do\n    package_name = details[:package_name]\n    version = ''\n    test_apply(package_name, 'absent', version)\n  end\n\n  step \"install a #{package_type} package via 'ensure=><OLD_VERSION>'\" do\n    package_name = details[:package_name]\n    version = details[:old_version]\n    test_apply(package_name, version, version)\n  end\n\n  step \"upgrade a #{package_type} package via 'ensure=><NEW_VERSION>'\" do\n    package_name = details[:package_name]\n    version = details[:new_version]\n    test_apply(package_name, version, version)\n  end\n\n  step \"attempt to downgrade a #{package_type} package via 'ensure=><OLD_VERSION>'\" do\n    package_name = details[:package_name]\n    version = details[:old_version]\n\n    manifest = get_manifest(package_name, version)\n    on(hosts, puppet_apply(\"--verbose\", \"--detailed-exitcodes\"),\n       { :stdin => manifest,\n         :acceptable_exit_codes => [4,6] }) do |result|\n\n        assert_match(/NIM package provider is unable to downgrade packages/, result.stderr, \"Didn't get an error about downgrading packages\")\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/allow_arbitrary_node_name_fact_for_agent.rb",
    "content": "test_name \"node_name_fact should be used to determine the node name for puppet agent\"\n\ntag 'audit:high',\n    'audit:integration',  # Tests that the server properly overrides certname with node_name fact.\n                          # Testing of passenger master is no longer needed.\n    'server'\n\nsuccess_message = \"node_name_fact setting was correctly used to determine the node name\"\n\ntestdir = master.tmpdir(\"nodenamefact\")\nnode_names = []\n\non agents, facter('kernel') do |result|\n  node_names << result.stdout.chomp\nend\n\nnode_names.uniq!\n\nstep \"Prepare for custom tk-auth rules\" do\n  on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.conf /etc/puppetlabs/puppetserver/conf.d/auth.bak'\n  modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => false}})\nend\n\nteardown do\n  modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => true}})\n  on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.bak /etc/puppetlabs/puppetserver/conf.d/auth.conf'\n  on master, \"service #{master['puppetservice']} reload\"\nend\n\nstep \"Setup tk-auth rules\" do\n  tka_header = <<-HEADER\nauthorization: {\n    version: 1\n    rules: [\n        {\n            match-request: {\n                path: \"/puppet/v3/file\"\n                type: path\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs file\"\n        },\n    HEADER\n\n  tka_node_rules = node_names.map do |node_name|\n    <<-NODE_RULES\n        {\n            match-request: {\n                path: \"/puppet/v3/catalog/#{node_name}\"\n                type: path\n                method: [get, post]\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs catalog #{node_name}\"\n        },\n        {\n            match-request: {\n                path: \"/puppet/v3/node/#{node_name}\"\n                type: path\n                method: get\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs node #{node_name}\"\n        },\n        {\n            match-request: {\n                path: \"/puppet/v3/report/#{node_name}\"\n                type: path\n                method: put\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs report #{node_name}\"\n        },\n      NODE_RULES\n  end\n\n  tka_footer = <<-FOOTER\n        {\n          match-request: {\n            path: \"/\"\n            type: path\n          }\n          deny: \"*\"\n          sort-order: 999\n          name: \"puppetlabs deny all\"\n        }\n    ]\n}\n    FOOTER\n\n  tk_auth = [tka_header, tka_node_rules, tka_footer].flatten.join(\"\\n\")\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n      file { '/etc/puppetlabs/puppetserver/conf.d/auth.conf':\n        ensure => file,\n        mode => '0644',\n        content => '#{tk_auth}',\n      }\n    MANIFEST\nend\n\nstep \"Setup site.pp for node name based classification\" do\n\n  site_manifest = <<-SITE_MANIFEST\nnode default {\n  notify { \"false\": }\n}\n\nnode #{node_names.map { |name| %Q[\"#{name}\"] }.join(\", \")} {\n  notify { \"#{success_message}\": }\n}\nSITE_MANIFEST\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n    $directories = [\n      '#{testdir}',\n      '#{testdir}/environments',\n      '#{testdir}/environments/production',\n      '#{testdir}/environments/production/manifests',\n    ]\n\n    file { $directories:\n      ensure => directory,\n      mode => '0755',\n    }\n\n    file { '#{testdir}/environments/production/manifests/manifest.pp':\n      ensure => file,\n      mode => '0644',\n      content => '#{site_manifest}',\n    }\n  MANIFEST\nend\n\nstep \"Ensure nodes are classified based on the node name fact\" do\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{testdir}/environments\",\n    },\n    'master' => {\n      'node_terminus'   => 'plain',\n    },\n  }\n\n  with_puppet_running_on(master, master_opts, testdir) do\n    on(agents, puppet('agent', \"--no-daemonize --verbose --onetime --node_name_fact kernel\")) do |result|\n      assert_match(/defined 'message'.*#{success_message}/, result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/allow_arbitrary_node_name_for_agent.rb",
    "content": "test_name \"node_name_value should be used as the node name for puppet agent\"\n\ntag 'audit:high',\n    'audit:integration',  # Tests that the server properly overrides certname with node_name fact.\n                          # Testing of passenger master is no longer needed.\n    'server'\n\nsuccess_message = \"node_name_value setting was correctly used as the node name\"\ntestdir = master.tmpdir('nodenamevalue')\n\nstep \"Prepare for custom tk-auth rules\" do\n  on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.conf /etc/puppetlabs/puppetserver/conf.d/auth.bak'\n  modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => false}})\nend\n\nteardown do\n  on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.bak /etc/puppetlabs/puppetserver/conf.d/auth.conf'\n  modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => true}})\n  on master, \"service #{master['puppetservice']} reload\"\nend\n\nstep \"Setup tk-auth rules\" do\n  tk_auth = <<-TK_AUTH\nauthorization: {\n    version: 1\n    rules: [\n        {\n            match-request: {\n                path: \"/puppet/v3/file\"\n                type: path\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs file\"\n        },\n        {\n            match-request: {\n                path: \"/puppet/v3/catalog/specified_node_name\"\n                type: path\n                method: [get, post]\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs catalog\"\n        },\n        {\n            match-request: {\n                path: \"/puppet/v3/node/specified_node_name\"\n                type: path\n                method: get\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs node\"\n        },\n        {\n            match-request: {\n                path: \"/puppet/v3/report/specified_node_name\"\n                type: path\n                method: put\n            }\n            allow: \"*\"\n            sort-order: 500\n            name: \"puppetlabs report\"\n        },\n        {\n          match-request: {\n            path: \"/\"\n            type: path\n          }\n          deny: \"*\"\n          sort-order: 999\n          name: \"puppetlabs deny all\"\n        }\n    ]\n}\n    TK_AUTH\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n      file { '/etc/puppetlabs/puppetserver/conf.d/auth.conf':\n        ensure => file,\n        mode => '0644',\n        content => '#{tk_auth}',\n      }\n    MANIFEST\nend\n\nstep \"Setup site.pp for node name based classification\" do\n\n  site_manifest = <<-SITE_MANIFEST\nnode default {\n  notify { \"false\": }\n}\nnode specified_node_name {\n  notify { \"#{success_message}\": }\n}\n  SITE_MANIFEST\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n    $directories = [\n      '#{testdir}',\n      '#{testdir}/environments',\n      '#{testdir}/environments/production',\n      '#{testdir}/environments/production/manifests',\n    ]\n\n    file { $directories:\n      ensure => directory,\n      mode => '0755',\n    }\n\n    file { '#{testdir}/environments/production/manifests/manifest.pp':\n      ensure => file,\n      mode => '0644',\n      content => '#{site_manifest}',\n    }\n  MANIFEST\nend\n\nstep \"Ensure nodes are classified based on the node name fact\" do\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{testdir}/environments\",\n    },\n    'master' => {\n      'node_terminus'   => 'plain',\n    },\n  }\n  with_puppet_running_on(master, master_opts, testdir) do\n    on(agents, puppet('agent', \"-t --node_name_value specified_node_name\"), :acceptable_exit_codes => [0,2]) do |result|\n      assert_match(/defined 'message'.*#{success_message}/, result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/apply/classes/parameterized_classes.rb",
    "content": "test_name \"parametrized classes\"\n\ntag 'audit:high',\n    'audit:unit'   # This should be covered at the unit layer.\n\n########################################################################\nstep \"should allow param classes\"\nmanifest = %q{\nclass x($y, $z) {\n  notice(\"${y}-${z}\")\n}\nclass {x: y => '1', z => '2'}\n}\n\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"inclusion after parameterization failed\" unless result.stdout.include? \"1-2\"\nend\n\n########################################################################\n# REVISIT: This was ported from the old set of tests, but I think that\n# the desired behaviour has recently changed.  --daniel 2010-12-23\nstep \"should allow param class post inclusion\"\nmanifest = %q{\nclass x($y, $z) {\n  notice(\"${y}-${z}\")\n}\nclass {x: y => '1', z => '2'}\ninclude x\n}\n\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"inclusion after parameterization failed\" unless result.stdout.include? \"1-2\"\nend\n\n########################################################################\nstep \"should allow param classes defaults\"\nmanifest = %q{\nclass x($y, $z='2') {\n  notice(\"${y}-${z}\")\n}\nclass {x: y => '1'}\n}\n\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"the default didn't apply as expected\" unless result.stdout.include? \"1-2\"\nend\n\n########################################################################\nstep \"should allow param class defaults to be overridden\"\nmanifest = %q{\nclass x($y, $z='2') {\n  notice(\"${y}-${z}\")\n}\nclass {x: y => '1', z => '3'}\n}\n\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"the override didn't happen as we expected\" unless result.stdout.include? \"1-3\"\nend\n"
  },
  {
    "path": "acceptance/tests/apply/classes/should_allow_param_override.rb",
    "content": "test_name \"should allow param override\"\n\ntag 'audit:high',\n    'audit:unit'   # This should be covered at the unit layer.\n\nmanifest = %q{\nclass parent {\n  notify { 'msg':\n    message => parent,\n  }\n}\nclass child inherits parent {\n  Notify['msg'] {message => 'child'}\n}\ninclude parent\ninclude child\n}\n\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"parameter override didn't work\" unless\n        result.stdout.include? \"defined 'message' as 'child'\"\nend\n\n"
  },
  {
    "path": "acceptance/tests/apply/classes/should_allow_param_undef_override.rb",
    "content": "test_name \"should allow overriding a parameter to undef in inheritence\"\n\ntag 'audit:high',\n    'audit:unit'   # This should be covered at the unit layer.\n\nagents.each do |agent|\n  dir = agent.tmpdir('class_undef_override')\n  out = File.join(dir, 'class_undef_override_out')\n  source = File.join(dir, 'class_undef_override_test')\n\nmanifest = %Q{\n  class parent {\n    file { 'test':\n      path   => '#{out}',\n      source => '#{source}',\n    }\n  }\n  class child inherits parent {\n    File['test'] {\n      source  => undef,\n      content => 'hello new world!',\n    }\n  }\n  include parent\n  include child\n}\n\n  step \"prepare the target file on all systems\"\n  on(agent, \"echo 'hello world!' > #{out}\")\n  step \"apply the manifest\"\n  apply_manifest_on(agent, manifest)\n  step \"verify the file content\"\n  on(agent, \"cat #{out}\") do |result|\n    fail_test \"the file was not touched\" if result.stdout.include? \"hello world!\"\n    fail_test \"the file was not updated\" unless result.stdout.include? \"hello new world\"\n  end\n\n  on(agent, \"rm -rf #{dir}\")\nend\n"
  },
  {
    "path": "acceptance/tests/apply/classes/should_include_resources_from_class.rb",
    "content": "test_name \"resources declared in a class can be applied with include\"\n\ntag 'audit:high',\n    'audit:unit'   # This should be covered at the unit layer.\n\nmanifest = %q{\nclass x {\n  notify{'a':}\n}\ninclude x\n}\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"the resource did not apply\" unless result.stdout.include?(\"defined 'message' as 'a'\")\nend\n"
  },
  {
    "path": "acceptance/tests/apply/classes/should_not_auto_include_resources_from_class.rb",
    "content": "test_name \"resources declared in classes are not applied without include\"\n\ntag 'audit:high',\n    'audit:unit'   # This should be covered at the unit layer.\n\nmanifest = %q{ class x { notify { 'test': message => 'never invoked' } } }\napply_manifest_on(agents, manifest) do |result|\n    fail_test \"found the notify despite not including it\" if\n        result.stdout.include? \"never invoked\"\nend\n"
  },
  {
    "path": "acceptance/tests/catalog_with_binary_data.rb",
    "content": "test_name \"C100300: Catalog containing binary data is applied correctly\" do\n  require 'puppet/acceptance/common_utils'\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  tag 'risk:high',\n      'server'\n\n  test_num        = 'c100300'\n  tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*'))\n  agent_tmp_dirs  = {}\n  agents.each do |agent|\n    agent_tmp_dirs[agent_to_fqdn(agent)] = agent.tmpdir(tmp_environment)\n  end\n\n  teardown do\n    step 'remove all test files on agents' do\n      agents.each {|agent| on(agent, \"rm -r '#{agent_tmp_dirs[agent_to_fqdn(agent)]}'\", :accept_all_exit_codes => true)}\n    end\n\n    # Remove all traces of the last used environment\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n\n    # note - master teardown is registered by #mk_tmp_environment_with_teardown\n  end\n\n  step \"Create module with binary data file on master\" do\n    on(master, \"mkdir -p '#{environmentpath}/#{tmp_environment}/modules/#{test_num}'/{manifests,files}\")\n    master_module_manifest    = \"#{environmentpath}/#{tmp_environment}/modules/#{test_num}/manifests/init.pp\"\n    master_module_binary_file = \"#{environmentpath}/#{tmp_environment}/modules/#{test_num}/files/binary_data\"\n\n    create_remote_file(master, master_module_binary_file, \"\\xC0\\xFF\")\n    on(master, \"chmod 644 '#{master_module_binary_file}'\")\n\n    manifest = <<-MANIFEST\n      class #{test_num}(\n      ) {\n        \\$test_path = \\$facts['networking']['fqdn'] ? #{agent_tmp_dirs}\n        file { '#{test_num}':\n          path   => \"\\$test_path/#{test_num}\",\n          content => binary_file('#{test_num}/binary_data'),\n          ensure => present,\n        }\n      }\n    MANIFEST\n    create_remote_file(master, master_module_manifest, manifest)\n    on(master, \"chmod 644 '#{master_module_manifest}'\")\n  end\n\n  step \"Create site.pp to classify nodes to include module\" do\n    site_pp_file = \"#{environmentpath}/#{tmp_environment}/manifests/site.pp\"\n    site_pp      = <<-SITE_PP\n      node default {\n        include #{test_num}\n      }\n    SITE_PP\n    create_remote_file(master, site_pp_file, site_pp)\n    on(master, \"chmod 644 '#{site_pp_file}'\")\n  end\n\n  step \"start the master\" do\n    with_puppet_running_on(master, {}) do\n\n      step \"run puppet and ensure that binary data was correctly applied\" do\n        agents.each do |agent|\n          on(agent, puppet('agent', '--test', \"--environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n          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|\n            assert_match(/c0\\nff/, res.stdout, 'Binary file did not contain originally specified data')\n          end\n        end\n      end\n\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/direct_puppet/cached_catalog_remediate_local_drift.rb",
    "content": "require 'puppet/acceptance/static_catalog_utils'\nextend Puppet::Acceptance::StaticCatalogUtils\n\ntest_name \"PUP-5122: Puppet remediates local drift using code_id and content_uri\" do\n\n  tag 'audit:high',\n      'audit:acceptance',\n      'audit:refactor',  # use mk_tmp_environment_with_teardown helper for environment construction\n      'server'\n\n\n  skip_test 'requires puppetserver installation' if @options[:type] != 'aio'\n\n  basedir = master.tmpdir(File.basename(__FILE__, '.*'))\n  module_dir = \"#{basedir}/environments/production/modules\"\n\n  master_opts = {\n   'main' => {\n      'environmentpath' => \"#{basedir}/environments\"\n    }\n  }\n\n  step \"Add versioned-code parameters to puppetserver.conf and ensure the server is running\" do\n    setup_puppetserver_code_id_scripts(master, basedir)\n  end\n\n  teardown do\n    cleanup_puppetserver_code_id_scripts(master, basedir)\n    on master, \"rm -rf #{basedir}\"\n\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"Create a module and a file with content representing the first code_id version\" do\n    apply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile {\n  '#{basedir}':;\n  '#{basedir}/environments':;\n  '#{basedir}/environments/production':;\n  '#{basedir}/environments/production/manifests':;\n  '#{module_dir}':;\n  '#{module_dir}/foo':;\n  '#{module_dir}/foo/files':;\n}\nMANIFEST\n  end\n\n  with_puppet_running_on master, master_opts, basedir do\n    agents.each do |agent|\n      agent_test_file_path = agent.tmpfile('foo_file')\n\n      step \"Add test file resource to site.pp on master with agent-specific file path\" do\n        apply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nFile {\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile { \"#{basedir}/environments/production/manifests/site.pp\" :\n  ensure => file,\n  mode => \"0640\",\n  content => \"node default {\n  file { '#{agent_test_file_path}' :\n    ensure => file,\n    source => 'puppet:///modules/foo/foo.txt'\n  }\n}\",\n}\n\nfile { \"#{module_dir}/foo/files/foo.txt\" :\n  ensure => file,\n  content => \"code_version_1\",\n  mode => \"0640\",\n}\nMANIFEST\n      end\n\n      step \"agent: #{agent}: Initial run: create the file with code version 1 and cache the catalog\"\n      on(agent, puppet(\"agent\", \"-t\"), :acceptable_exit_codes => [0,2])\n\n      # When there is no drift, there should be no request made to the server\n      # for file metadata or file content.  A puppet run depending on\n      # a non-server will fail if such a request is made.  Verify the agent\n      # sends a report.\n\n      step \"Remove existing reports from server reports directory\"\n      on(master, \"rm -rf /opt/puppetlabs/server/data/puppetserver/reports/#{agent.node_name}/*\")\n      r = on(master, \"ls /opt/puppetlabs/server/data/puppetserver/reports/#{agent.node_name} | wc -l\").stdout.chomp\n      assert_equal(r, '0', \"reports directory should be empty!\")\n\n      step \"Verify puppet run without drift does not make file request from server\"\n      r = on(agent, puppet(\"agent\",\n        \"--use_cached_catalog\",\n        \"--server\", \"no_such_host\",\n        \"--report_server\", master.hostname,\n        \"--onetime\",\n        \"--no-daemonize\",\n        \"--detailed-exitcodes\",\n        \"--verbose\"\n      )).stderr\n      assert_equal(r, \"\", \"Fail: Did agent try to contact server?\")\n\n      step \"Verify report was delivered to server\"\n      r = on(master, \"ls /opt/puppetlabs/server/data/puppetserver/reports/#{agent.node_name} | wc -l\").stdout.chomp\n      assert_equal(r, '1', \"Reports directory should have one file\")\n\n      step \"agent: #{agent}: Remove the test file to simulate drift\"\n      on(agent, \"rm -rf #{agent_test_file_path}\")\n\n      step \"Alter the source file on the master to simulate a code update\"\n      apply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nfile { \"#{module_dir}/foo/files/foo.txt\" :\n  ensure => file,\n  mode => \"0640\",\n  content => \"code_version_2\",\n}\nMANIFEST\n\n      step \"Run agent again using --use_cached_catalog and ensure content from the first code_id is used\"\n      on(agent, puppet(\"agent\", \"-t\", \"--use_cached_catalog\"), :acceptable_exit_codes => [0,2])\n      on(agent, \"cat #{agent_test_file_path}\") do |result|\n        assert_equal('code_version_1', result.stdout)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/direct_puppet/catalog_uuid_correlates_catalogs_with_reports.rb",
    "content": "test_name \"PUP-5872: catalog_uuid correlates catalogs with reports\" do\n\n  tag 'audit:high',\n      'audit:acceptance',\n      'audit:refactor'    # remove dependence on server by adding a\n                          # catalog and report fixture to validate against.\n\n  master_reportdir = create_tmpdir_for_user(master, 'reportdir')\n\n  def remove_reports_on_master(master_reportdir, agent_node_name)\n    on(master, \"rm -rf #{master_reportdir}/#{agent_node_name}/*\")\n  end\n\n  def get_catalog_uuid_from_cached_catalog(host, agent_vardir, agent_node_name)\n    cache_catalog_uuid = nil\n    on(host, \"cat #{agent_vardir}/client_data/catalog/#{agent_node_name}.json\") do |result|\n      cache_catalog_uuid = result.stdout.match(/\"catalog_uuid\":\"([a-z0-9\\-]*)\",/)[1]\n    end\n    cache_catalog_uuid\n  end\n\n  def get_catalog_uuid_from_report(master_reportdir, agent_node_name)\n    report_catalog_uuid = nil\n    on(master, \"cat #{master_reportdir}/#{agent_node_name}/*\") do |result|\n      report_catalog_uuid = result.stdout.match(/catalog_uuid: '?([a-z0-9\\-]*)'?/)[1]\n    end\n    report_catalog_uuid\n  end\n\n  with_puppet_running_on(master, :master => { :reportdir => master_reportdir, :reports => 'store' }) do\n    agents.each do |agent|\n      agent_vardir = agent.tmpdir(File.basename(__FILE__, '.*'))\n\n      step \"agent: #{agent}: Initial run to retrieve a catalog and generate the first report\" do\n        on(agent, puppet(\"agent\", \"-t\", \"--vardir #{agent_vardir}\"), :acceptable_exit_codes => [0,2])\n      end\n\n      cache_catalog_uuid = get_catalog_uuid_from_cached_catalog(agent, agent_vardir, agent.node_name)\n\n      step \"agent: #{agent}: Ensure the catalog and report share the same catalog_uuid\" do\n        report_catalog_uuid = get_catalog_uuid_from_report(master_reportdir, agent.node_name)\n        assert_equal(cache_catalog_uuid, report_catalog_uuid, \"catalog_uuid found in cached catalog, #{cache_catalog_uuid} did not match report #{report_catalog_uuid}\")\n      end\n\n      step \"cleanup reports on master\" do\n        remove_reports_on_master(master_reportdir, agent.node_name)\n      end\n\n      step \"Run with --use_cached_catalog and ensure catalog_uuid in the new report matches the cached catalog\" do\n        on(agent, puppet(\"agent\", \"--onetime\", \"--no-daemonize\", \"--use_cached_catalog\", \"--vardir #{agent_vardir}\"), :acceptance_exit_codes => [0,2])\n        report_catalog_uuid = get_catalog_uuid_from_report(master_reportdir, agent.node_name)\n        assert_equal(cache_catalog_uuid, report_catalog_uuid, \"catalog_uuid found in cached catalog, #{cache_catalog_uuid} did not match report #{report_catalog_uuid}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/direct_puppet/static_catalog_env_control.rb",
    "content": "test_name \"Environment control of static catalogs\"\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # use mk_tmp_environment_with_teardown helper for environment construction\n    'server'\n\nskip_test 'requires puppetserver to test static catalogs' if @options[:type] != 'aio'\n\nrequire 'json'\n\n@testroot = master.tmpdir(File.basename(__FILE__, '/*'))\n@coderoot = \"#{@testroot}/code\"\n@confdir = master['puppetserver-confdir']\n@master_opts = {\n  'main' => {\n    'environmentpath' => \"#{@coderoot}/environments\",\n  },\n}\n@production_files = {}\n@canary_files = {}\n@agent_manifests = {}\n@catalog_files = {}\nagents.each do |agent|\n  hn = agent.node_name\n  resdir = agent.tmpdir('results')\n  @production_files[hn] = \"#{resdir}/prod_hello_from_puppet_uri\"\n  @canary_files[hn] = \"#{resdir}/can_hello_from_puppet_uri\"\n  @catalog_files[hn] = \"#{on(agent, puppet('config', 'print', 'client_datadir')).stdout.chomp}/catalog/#{hn}.json\"\n  @agent_manifests[hn] = <<MANIFESTAGENT\nfile { '#{@coderoot}/environments/production/modules/hello/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"class hello {\n  notice('hello from production-hello')\n  file { '#{resdir}' :\n    ensure => directory,\n    mode => '0755',\n  }\n  file { '#{resdir}/prod_hello_from_puppet_uri' :\n    ensure => file,\n    mode => '0644',\n    source => 'puppet:///modules/hello/hello_msg',\n  }\n}\",\n}\n\nfile { '#{@coderoot}/environments/canary/modules/can_hello/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class can_hello {\n  notice(\"hello from production-hello\")\n  file { \"#{resdir}\":\n    ensure => directory,\n    mode => \"0755\",\n  }\n  file { \"#{resdir}/can_hello_from_puppet_uri\" :\n    ensure => file,\n    mode => \"0644\",\n    source => \"puppet:///modules/can_hello/hello_msg\",\n  }\n}',\n}\nMANIFESTAGENT\nend\n\n# The code_content script needs to return the correct content whose checksum\n# matches the metadata contained in the static catalog.\nPRODUCTION_CONTENT = \"Hello message from production/hello module, content from source attribute.\".freeze\nCANARY_CONTENT = \"Hello message from canary/can_hello module, content from source attribute.\".freeze\n\n@manifest = <<MANIFEST\nFile {\n  ensure => directory,\n  mode => \"0755\",\n}\n\nfile {\n  '#{@testroot}':;\n  '#{@coderoot}':;\n  '#{@coderoot}/environments':;\n  '#{@coderoot}/environments/production':;\n  '#{@coderoot}/environments/production/manifests':;\n  '#{@coderoot}/environments/production/modules':;\n  '#{@coderoot}/environments/production/modules/hello':;\n  '#{@coderoot}/environments/production/modules/hello/manifests':;\n  '#{@coderoot}/environments/production/modules/hello/files':;\n\n  '#{@coderoot}/environments/canary':;\n  '#{@coderoot}/environments/canary/manifests':;\n  '#{@coderoot}/environments/canary/modules':;\n  '#{@coderoot}/environments/canary/modules/can_hello':;\n  '#{@coderoot}/environments/canary/modules/can_hello/manifests':;\n  '#{@coderoot}/environments/canary/modules/can_hello/files':;\n\n}\n\nfile { '#{@coderoot}/code_id.sh' :\n  ensure => file,\n  mode => \"0755\",\n  content => '#! /bin/bash\necho \"code_version_1\"\n',\n}\n\nfile { '#{@coderoot}/code_content.sh' :\n  ensure => file,\n  mode => \"0755\",\n  content => '#! /bin/bash\n# script arguments:\n#  $1 environment\n#  $2 code_id\n#  $3 path relative to mount\n# use echo -n to omit newline\nif [ $1 == \"production\" ] ; then\n  echo -n \"#{PRODUCTION_CONTENT}\"\nelse\n  echo -n \"#{CANARY_CONTENT}\"\nfi\n',\n}\n\nfile { '#{@coderoot}/environments/production/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\n',\n}\n\nfile { '#{@coderoot}/environments/canary/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\nstatic_catalogs = false\n',\n}\n\nfile { '#{@coderoot}/environments/production/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include hello\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/canary/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include can_hello\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/production/modules/hello/files/hello_msg':\n  ensure => file,\n  mode => \"0644\",\n  content => \"#{PRODUCTION_CONTENT}\",\n}\n\nfile { '#{@coderoot}/environments/canary/modules/can_hello/files/hello_msg':\n  ensure => file,\n  mode => \"0644\",\n  content => \"#{CANARY_CONTENT}\",\n}\nMANIFEST\n\nteardown do\n  on(master, \"mv #{@confdir}/puppetserver.conf.bak #{@confdir}/puppetserver.conf\")\n  on(master, \"rm -rf #{@testroot}\")\n  agents.each do |agent|\n    on(agent, puppet('config print lastrunfile')) do |command_result|\n      agent.rm_rf(command_result.stdout)\n    end\n  end\nend\n\nstep 'apply main manifest, static_catalogs unspecified in global scope, unspecified in production environment, disabled in canary environment'\non(\n  master,\n  \"cp #{@confdir}/puppetserver.conf #{@confdir}/puppetserver.conf.bak\"\n)\napply_manifest_on(master, @manifest, :catch_failures => true)\n\nstep \"Add versioned-code parameters to puppetserver.conf and ensure the server is running\"\npuppetserver_config = \"#{master['puppetserver-confdir']}/puppetserver.conf\"\non master, \"cp #{puppetserver_config} #{@coderoot}/puppetserver.conf.bak\"\nversioned_code_settings = {\n  \"jruby-puppet\" => {\n    \"master-code-dir\" => @coderoot\n  },\n  \"versioned-code\" => {\n    \"code-id-command\" => \"#{@coderoot}/code_id.sh\",\n     \"code-content-command\" => \"#{@coderoot}/code_content.sh\"\n  }\n}\nmodify_tk_config(master, puppetserver_config, versioned_code_settings)\n\nstep 'start puppet server'\nwith_puppet_running_on master, @master_opts, @coderoot do\n  agents.each do |agent|\n    hn = agent.node_name\n\n    apply_manifest_on(master, @agent_manifests[hn], :catch_failures => true)\n\n    step 'agent gets a production catalog, should be static catalog by default'\n    on(\n      agent,\n      puppet(\n        'agent',\n        '-t',\n        '--environment', 'production'\n      ),\n      :acceptable_exit_codes => [0, 2]\n    )\n\n    step 'verify production environment'\n    r = on(agent, \"cat #{@catalog_files[hn]}\")\n    catalog_content = JSON.parse(r.stdout)\n    assert_equal(\n      catalog_content['environment'],\n      'production',\n      'catalog for unexpectected environment'\n    )\n\n    step 'verify static catalog by finding metadata section in catalog'\n    assert(\n      catalog_content['metadata'] && catalog_content['metadata'][@production_files[hn]],\n      'metadata section of catalog not found'\n    )\n\n    step 'agent gets a canary catalog, static catalog should be disabled'\n    on(\n      agent,\n      puppet(\n        'agent',\n        '-t',\n        '--environment', 'canary'\n      ),\n      :acceptable_exit_codes => [0, 2]\n    )\n\n    step 'verify canary environment'\n    r = on(agent, \"cat #{@catalog_files[hn]}\")\n    catalog_content = JSON.parse(r.stdout)\n    assert_equal(\n      catalog_content['environment'],\n      'canary',\n      'catalog for unexpectected environment'\n    )\n\n    step 'verify not static catalog by absence of metadata section in catalog'\n    assert_nil(\n      catalog_content['metadata'],\n      'unexpected metadata section found in catalog'\n    )\n\n  end\nend\n\nstep 'enable static catalog for canary environment'\n@static_canary_manifest = <<MANIFEST2\nfile { '#{@coderoot}/environments/canary/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\nstatic_catalogs = true\n',\n}\nMANIFEST2\napply_manifest_on(master, @static_canary_manifest, :catch_failures => true)\n\nstep 'disable global static catalog setting'\n@master_opts = {\n  'master' => {\n    'static_catalogs' => false\n  },\n  'main' => {\n    'environmentpath' => \"#{@coderoot}/environments\",\n  },\n}\n\nstep 'bounce server for static catalog disable setting to take effect.'\nwith_puppet_running_on master, @master_opts, @coderoot do\n  agents.each do |agent|\n    hn = agent.node_name\n\n    apply_manifest_on(master, @agent_manifests[hn], :catch_failures => true)\n\n    step 'agent gets a production catalog, should not be a static catalog'\n    on(\n      agent,\n      puppet(\n        'agent',\n        '-t',\n        '--environment', 'production'\n      ),\n      :acceptable_exit_codes => [0, 2]\n    )\n\n    step 'verify production environment'\n    r = on(agent, \"cat #{@catalog_files[hn]}\")\n    catalog_content = JSON.parse(r.stdout)\n    assert_equal(\n      catalog_content['environment'],\n      'production',\n      'catalog for unexpectected environment'\n    )\n\n    step 'verify production environment, not static catalog'\n    assert_nil(\n      catalog_content['metadata'],\n      'unexpected metadata section found in catalog'\n    )\n\n    step 'agent gets a canary catalog, static catalog should be enabled'\n    on(\n      agent,\n      puppet(\n        'agent',\n        '-t',\n        '--environment', 'canary'\n      ),\n      :acceptable_exit_codes => [0, 2]\n    )\n\n    step 'verify canary catalog'\n    r = on(agent, \"cat #{@catalog_files[hn]}\")\n    catalog_content = JSON.parse(r.stdout)\n    assert_equal(\n      catalog_content['environment'],\n      'canary',\n      'catalog for unexpectected environment'\n    )\n\n    step 'verify canary static catalog'\n    assert(\n      catalog_content['metadata'] && catalog_content['metadata'][@canary_files[hn]],\n      'metadata section of catalog not found'\n    )\n\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/direct_puppet/supports_utf8.rb",
    "content": "test_name \"C97172: static catalogs support utf8\" do\n\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  tag 'audit:high',\n      'audit:acceptance',\n      'audit:refactor'  # Review for agent side UTF validation.\n\n  app_type = File.basename(__FILE__, '.*')\n  tmp_environment   = mk_tmp_environment_with_teardown(master, app_type)\n\n  tmp_file = {}\n  agents.each do |agent|\n    tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment)\n  end\n\n  teardown do\n    # Remove all traces of the last used environment\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n\n    step 'clean out produced resources' do\n      agents.each do |agent|\n        if tmp_file.has_key?(agent_to_fqdn(agent)) && !tmp_file[agent_to_fqdn(agent)].empty?\n          on(agent, \"rm -f '#{tmp_file[agent_to_fqdn(agent)]}'\")\n        end\n      end\n    end\n  end\n\n  file_contents     = 'Mønti Pythøn ik den Hølie Gräilen, yër? € ‰ ㄘ 万 竹 Ü Ö'\n  step 'create site.pp with utf8 chars' do\n    manifest = <<MANIFEST\nfile { '#{environmentpath}/#{tmp_environment}/manifests/site.pp':\n  ensure => file,\n  content => '\n\\$test_path = \\$facts[\"networking\"][\"fqdn\"] ? #{tmp_file}\nfile { \\$test_path:\n  content => @(UTF8)\n    #{file_contents}\n    | UTF8\n}\n  ',\n}\nMANIFEST\n    apply_manifest_on(master, manifest, :catch_failures => true)\n  end\n\n  step 'run agent(s)' do\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n        config_version = ''\n        config_version_matcher = /configuration version '(\\d+)'/\n        on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"),\n           :acceptable_exit_codes => 2).stdout do |result|\n          config_version = result.match(config_version_matcher)[1]\n        end\n        on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |result|\n          assert_equal(file_contents, result, 'file contents did not match accepted')\n        end\n\n        on(agent, \"rm -f '#{tmp_file[agent_to_fqdn(agent)]}'\")\n        on(agent, puppet(\"agent -t --environment '#{tmp_environment}' --use_cached_catalog\"),\n           :acceptable_exit_codes => 2).stdout do |result|\n          assert_match(config_version_matcher, result, 'agent did not use cached catalog')\n          second_config_version = result.match(config_version_matcher)[1]\n          asset_equal(config_version, second_config_version, 'config version should have been the same')\n        end\n        on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |result|\n          assert_equal(file_contents, result, 'file contents did not match accepted')\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/environment/broken_unassigned_environment_handled_gracefully.rb",
    "content": "test_name 'PUP-3755 Test an un-assigned broken environment'\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',     # Use mk_tmp_environment_with_teardown helper\n    'server'\n\nteardown do\n  agents.each do |agent|\n    on(agent, puppet('config print lastrunfile')) do |command_result|\n      agent.rm_rf(command_result.stdout)\n    end\n  end\nend\n\nstep 'setup environments'\n\ntestdir = create_tmpdir_for_user(master, 'confdir')\nenvironment = 'debug'\nmanifest = <<-MANIFEST\n  File {\n    ensure => directory,\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n    mode => \"0750\",\n  }\n\n  file { \"#{testdir}\":;\n    \"#{testdir}/environments\":;\n    \"#{testdir}/environments/production\":;\n    \"#{testdir}/environments/production/manifests\":;\n    \"#{testdir}/environments/production/modules\":;\n    \"#{testdir}/environments/#{environment}\":;\n    \"#{testdir}/environments/#{environment}/manifests\":;\n    \"#{testdir}/environments/#{environment}/modules\":;\n  }\n  # broken envioronment\n  file { \"#{testdir}/environments/production/manifests/site.pp\":\n    ensure  => file,\n    content => 'import \"/tmp/bogus/*.pp\"'\n  }\n  file { \"#{testdir}/environments/#{environment}/manifests/site.pp\":\n    ensure  => file,\n    content => 'node default{\\nnotify{\"you win\":}\\n}'\n  }\nMANIFEST\n\napply_manifest_on(master, manifest, :catch_failures => true)\n\nstep 'run agents, ensure no one complains about the other environment'\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\"\n  }\n}\n\nwith_puppet_running_on(master, master_opts, testdir) do\n  agents.each do |agent|\n    on(agent, puppet('agent',\n                     \"--test --environment #{environment}\"),\n       :acceptable_exit_codes => (0..255)) do |result|\n      assert_match(/you win/, result.stdout, 'agent did not pickup newly classified environment.')\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/can_enumerate_environments.rb",
    "content": "test_name \"Can enumerate environments via an HTTP endpoint\"\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\nconfine :except, :platform => /osx/ # see PUP-4820\n\ndef server_port(agent)\n  setting_on(agent, \"agent\", \"serverport\")\nend\n\ndef setting_on(host, section, name)\n  on(host, puppet(\"config\", \"print\", name, \"--section\", section)).stdout.chomp\nend\n\ndef full_path(host, path)\n  if host['platform'] =~ /win/\n    on(host, \"cygpath '#{path}'\").stdout.chomp\n  else\n    path\n  end\nend\n\ndef curl_master_from(agent, path, headers = '', &block)\n  url = \"https://#{master}:#{server_port(agent)}#{path}\"\n  cert_path = full_path(agent, setting_on(agent, \"agent\", \"hostcert\"))\n  key_path = full_path(agent, setting_on(agent, \"agent\", \"hostprivkey\"))\n  curl_base = \"curl --tlsv1 -sg --cert \\\"#{cert_path}\\\" --key \\\"#{key_path}\\\" -k -H '#{headers}'\"\n\n  on agent, \"#{curl_base} '#{url}'\", &block\nend\n\nmaster_user = puppet_config(master, 'user', section: 'master')\nenvironments_dir = create_tmpdir_for_user master, \"environments\"\napply_manifest_on(master, <<-MANIFEST)\nFile {\n  ensure => directory,\n  owner => #{master_user},\n  group => #{master.puppet['group']},\n  mode => \"0770\",\n}\n\nfile {\n  \"#{environments_dir}\":;\n  \"#{environments_dir}/env1\":;\n  \"#{environments_dir}/env2\":;\n}\nMANIFEST\n\nmaster_opts =  {\n  :master => {\n    :environmentpath => environments_dir\n  }\n}\nif master.is_pe?\n  master_opts[:master][:basemodulepath] = master['sitemoduledir']\nend\n\nwith_puppet_running_on(master, master_opts) do\n  step \"Ensure that an unauthenticated client cannot access the environments list\" do\n    on(master, \"curl --tlsv1 -ksv https://#{master}:#{server_port(master)}/puppet/v3/environments\", :acceptable_exit_codes => [0,7]) do |result|\n      assert_match(/< HTTP\\/1\\.\\d 403/, result.stderr)\n    end\n  end\n\n  step \"Ensure that an authenticated client can retrieve the list of environments\" do\n    curl_master_from(master, '/puppet/v3/environments') do |result|\n      data = JSON.parse(result.stdout)\n      assert_equal([\"env1\", \"env2\", \"production\"], data[\"environments\"].keys.sort)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/custom_type_provider_from_same_environment.rb",
    "content": "test_name 'C59122: ensure provider from same env as custom type' do\nrequire 'puppet/acceptance/environment_utils'\nextend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:integration',  # This behavior is specific to the master to 'do the right thing'\n    'server'\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  file_correct    = \"#{tmp_environment}-correct.txt\"\n  file_wrong      = \"#{tmp_environment}-wrong.txt\"\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n  fq_prod_environmentpath = \"#{environmentpath}/production\"\n\n  teardown do\n    step 'clean out production env' do\n      on(master, \"rm -rf #{fq_prod_environmentpath}/modules/*\",         :accept_all_exit_codes => true)\n      on(master, \"rm     #{fq_prod_environmentpath}/manifests/site.pp\", :accept_all_exit_codes => true)\n    end\n    step 'clean out file resources' do\n      on(hosts, \"rm #{file_correct} #{file_wrong}\", :accept_all_exit_codes => true)\n    end\n\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create a custom type and provider in each of production and #{tmp_environment}\" do\n    type_name               = 'test_custom_type'\n    provider_name           = 'universal'\n    type_content            = <<TYPE\n      Puppet::Type.newtype(:#{type_name}) do\n        @doc = \"Manage a file (the simple version).\"\n        ensurable\n        newparam(:name) do\n          desc \"The full path to the file.\"\n        end\n      end\nTYPE\n\n    def provider_content(file_file_content, type_name, provider_name)\n      return <<PROVIDER\n        Puppet::Type.type(:#{type_name}).provide(:#{provider_name}) do\n          desc \"#{provider_name} file mgmt, yo\"\n          def create\n            File.open(@resource[:name], \"w\") { |f| f.puts \"#{file_file_content}!\" }\n          end\n          def destroy\n            File.unlink(@resource[:name])\n          end\n          def exists?\n            File.exist?(@resource[:name])\n          end\n        end\nPROVIDER\n    end\n\n    manifest = <<MANIFEST\nFile { ensure => directory }\nfile {\n       '#{fq_tmp_environmentpath}/modules/simple_type':;\n       '#{fq_tmp_environmentpath}/modules/simple_type/lib':;\n       '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet':;\n       '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/type/':;\n       '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/provider/':;\n       '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}':;\n       '#{fq_prod_environmentpath}/modules':;\n       '#{fq_prod_environmentpath}/modules/simple_type':;\n       '#{fq_prod_environmentpath}/modules/simple_type/lib':;\n       '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet':;\n       '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/type/':;\n       '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/provider/':;\n       '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}':;\n}\nfile { '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/type/#{type_name}.rb':\n  ensure => file,\n    content => '#{type_content}',\n}\nfile { '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/type/#{type_name}.rb':\n  ensure => file,\n    content => '#{type_content}',\n}\nfile { '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}/#{provider_name}.rb':\n  ensure => file,\n    content => '#{provider_content('correct', type_name, provider_name)}',\n}\nfile { '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}/#{provider_name}.rb':\n  ensure => file,\n    content => '#{provider_content('wrong', type_name, provider_name)}',\n}\nfile { '#{fq_tmp_environmentpath}/manifests/site.pp':\n  ensure => file,\n    content => 'node default { #{type_name}{\"#{file_correct}\": ensure=>present} }',\n}\nfile { '#{fq_prod_environmentpath}/manifests': }\nfile { '#{fq_prod_environmentpath}/manifests/site.pp':\n  ensure => file,\n    content => 'node default { #{type_name}{\"#{file_wrong}\": ensure=>present} }',\n}\nMANIFEST\n    apply_manifest_on(master, manifest, :catch_failures => true)\n  end\n\n  step \"run agent in #{tmp_environment}, ensure it finds the correct provider\" do\n    with_puppet_running_on(master,{}) do\n      agents.each do |agent|\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\"),\n          :accept_all_exit_codes => true) do |result|\n          assert_equal(2, result.exit_code, 'agent did not exit with the correct code of 2')\n          assert_match(/#{file_correct}/, result.stdout, 'agent did not ensure the correct file')\n          assert(agent.file_exist?(file_correct), 'puppet did not create the file')\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/environment/directory_environment_production_created_master.rb",
    "content": "test_name 'ensure production environment created by master if missing'\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\ntestdir = create_tmpdir_for_user master, 'prod-env-created'\n\nstep 'make environmentpath'\nmaster_user = puppet_config(master, 'user', section: 'master')\napply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  owner => #{master_user},\n  group => #{master.puppet['group']},\n  mode => '0640',\n}\n\nfile {\n  \"#{testdir}\":;\n  \"#{testdir}/environments\":;\n}\nMANIFEST\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n  }\n}\n\nstep 'run master; ensure production environment created'\nwith_puppet_running_on(master, master_opts, testdir) do\n  on(master, \"test -d '#{testdir}/environments/production'\")\n\n  step 'ensure catalog returned from production env with no changes'\n  agents.each do |agent|\n    on(agent, puppet(\"agent -t --environment production --detailed-exitcodes\")) do |result|\n      # detailed-exitcodes produces a 0 when no changes are made.\n      assert_equal(0, result.exit_code)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/enc_nonexistent_directory_environment.rb",
    "content": "test_name \"Master should produce error if enc specifies a nonexistent environment\" do\n  require 'puppet/acceptance/classifier_utils.rb'\n  extend Puppet::Acceptance::ClassifierUtils\n\n  tag 'audit:high',\n      'audit:unit',\n      'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  testdir = create_tmpdir_for_user(master, 'nonexistent_env')\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  owner  => #{master.puppet['user']},\n  group  => #{master.puppet['group']},\n  mode   => '0755',\n}\n\nfile {\n  \"#{testdir}\":;\n  \"#{testdir}/environments\":;\n  \"#{testdir}/environments/production\":;\n  \"#{testdir}/environments/production/manifests\":;\n  \"#{testdir}/environments/production/manifests/site.pp\":\n    ensure  => file,\n    mode => '0644',\n    content => 'notify { \"In the production environment\": }';\n}\n  MANIFEST\n\n  if master.is_pe?\n    group = {\n        'name'               => 'Environment Does Not Exist',\n        'description'        => 'Classify our test agent nodes in an environment that does not exist.',\n        'environment'        => 'doesnotexist',\n        'environment_trumps' => true,\n    }\n    create_group_for_nodes(agents, group)\n  else\n    apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n    file { \"#{testdir}/enc.rb\":\n      ensure  => file,\n      mode    => '0775',\n      content => '#!#{master['privatebindir']}/ruby\n        puts \"environment: doesnotexist\"\n      ';\n    }\n    MANIFEST\n  end\n\n  master_opts           = {\n      'main' => {\n          'environmentpath' => \"#{testdir}/environments\",\n      }\n  }\n  master_opts['master'] = {\n      'node_terminus'  => 'exec',\n      'external_nodes' => \"#{testdir}/enc.rb\",\n  } if !master.is_pe?\n\n  with_puppet_running_on(master, master_opts, testdir) do\n    agents.each do |agent|\n      on(agent, puppet(\"agent -t --verbose\"), :acceptable_exit_codes => [1]) do |result|\n        unless agent['locale'] == 'ja'\n          assert_match(/Could not find a directory environment named 'doesnotexist'/, result.stderr, \"Errors when nonexistent environment is specified\")\n        end\n        refute_match(/In the production environment/, result.stdout, \"Executed manifest from production environment\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/environment_scenario-bad.rb",
    "content": "test_name 'Test behavior of directory environments when environmentpath is set to a non-existent directory' do\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n  require 'puppet/acceptance/classifier_utils'\n  extend Puppet::Acceptance::ClassifierUtils\n\n  tag 'audit:high',\n      'audit:unit', # The error responses for the agent should be covered by Ruby unit tests.\n      # The server 404/400 response should be covered by server integration tests.\n      'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  classify_nodes_as_agent_specified_if_classifer_present\n\n  step 'setup environments'\n\n  testdir                = create_tmpdir_for_user(master, 'confdir')\n  puppet_conf_backup_dir = create_tmpdir_for_user(master, \"puppet-conf-backup-dir\")\n\n  apply_manifest_on(master, environment_manifest(testdir), :catch_failures => true)\n\n  step 'Test' do\n    env_path    = '/doesnotexist'\n    master_opts = {\n        'main' => {\n            'environmentpath' => \"#{env_path}\",\n        }\n    }\n    env         = 'testing'\n\n    results = use_an_environment(env, 'bad environmentpath', master_opts, testdir, puppet_conf_backup_dir, :directory_environments => true)\n\n    expectations = {\n        :puppet_config           => {\n            :exit_code => 0,\n            :matches   => [%r{basemodulepath = /etc/puppetlabs/code/modules:/opt/puppetlabs/puppet/modules},\n                           %r{modulepath =},\n                           %r{manifest =},\n                           %r{config_version =}],\n        },\n        :puppet_apply            => {\n            :exit_code => 1,\n            :matches   => [%r{Could not find a directory environment named '#{env}' anywhere in the path.*#{env_path}}],\n        },\n        :puppet_agent            => {\n            :exit_code => 0,\n        },\n    }\n\n    agents.each do |host|\n      unless host['locale'] == 'ja'\n        expectations[:puppet_agent][:matches] = [%r{Environment '#{env}' not found on server, skipping initial pluginsync.},\n                                                 %r{Local environment: '#{env}' doesn't match server specified environment 'production', restarting agent run with environment 'production'}]\n      end\n    end\n\n    assert_review(review_results(results, expectations))\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/feature_branch_configured_environment.rb",
    "content": "test_name \"Agent should use set environment after running with specified environment\" do\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  tag 'audit:high',\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  tmp_environment = mk_tmp_environment_with_teardown(master, 'special')\n  agents.each do |agent|\n    on(agent, puppet(\"agent -t --environment #{tmp_environment}\")) do |result|\n      assert_match(/Info: Using environment 'special_\\w+'/, result.stdout)\n    end\n\n    on(agent, puppet('agent -t')) do |result|\n      assert_match(/Info: Using environment 'production'/, result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/should_find_existing_production_environment.rb",
    "content": "test_name \"should find existing production environment\"\ntag 'audit:medium'\n\nrequire 'puppet/acceptance/i18ndemo_utils'\nextend Puppet::Acceptance::I18nDemoUtils\n\nagents.each do |agent|\n  path_separator = agent.platform_defaults[:pathseparator]\n  initial_environment = on(agent, puppet(\"config print environment\")).stdout.chomp\n  initial_environment_paths = on(agent, puppet(\"config print environmentpath\")).stdout.chomp.split(path_separator)\n\n  default_environment_path = ''\n  custom_environment_path = agent.tmpdir('custom_environment')\n\n  teardown do\n    step 'uninstall the module' do\n      uninstall_i18n_demo_module(master)\n      uninstall_i18n_demo_module(agent)\n    end\n\n    step 'Remove custom environment paths' do\n      environment_paths = on(agent, puppet(\"config print environmentpath\")).stdout.chomp\n      environment_paths.split(path_separator).each do |path|\n        agent.rm_rf(path) unless initial_environment_paths.include?(path)\n      end\n\n      agent.rm_rf(custom_environment_path)\n    end\n\n    step 'Reset environment settings' do\n      on(agent, puppet(\"config set environmentpath #{initial_environment_paths.join(path_separator)}\"))\n\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n\n      if initial_environment == 'production'\n        on(agent, puppet(\"config delete environment\"))\n      else\n        on(agent, puppet(\"config set environment #{initial_environment}\"))\n      end\n\n      on(agent, puppet(\"agent -t\"))\n    end\n  end\n\n  step 'Ensure a clean environment with default settings' do\n    step 'Remove the lastrunfile which contains the last used agent environment' do\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n\n    step 'Change to the default environment setting' do\n      on(agent, puppet(\"config delete environment\"))\n      on(agent, puppet(\"config print environment\")) do |result|\n        assert_match('production', result.stdout, \"Default environment is not 'production' as expected\")\n      end\n    end\n\n    step 'Change to the default environmentpath setting and remove production folder' do\n      on(agent, puppet(\"config delete environmentpath\"))\n      default_environment_path = on(agent, puppet(\"config print environmentpath\")).stdout.chomp\n      agent.rm_rf(\"#{default_environment_path}/production\")\n    end\n\n    step 'Apply changes and expect puppet to create the production folder back' do\n      on(agent, puppet(\"agent -t\"))\n      on(agent, \"ls #{default_environment_path}\") do |result|\n        assert_match('production', result.stdout, \"Default environment folder was not generated in last puppet run\")\n      end\n    end\n  end\n\n  step 'Install a module' do\n    install_i18n_demo_module(master)\n  end\n\n  step 'Expect output from the custom fact of the module' do\n    on(agent, puppet(\"agent -t\"), :acceptable_exit_codes => [0, 2]) do |result|\n      assert_match(/Error:.*i18ndemo/, result.stderr)\n    end\n  end\n\n  step 'Add a custom environment path before the current one' do\n    current_environment_path = on(agent, puppet(\"config print environmentpath\")).stdout.chomp\n    on(agent, puppet(\"config set environmentpath '#{custom_environment_path}#{path_separator}#{current_environment_path}'\"))\n  end\n\n  step 'Expect the module to still be found' do\n    on(agent, puppet(\"agent -t\"), :acceptable_exit_codes => [0, 2]) do |result|\n      assert_match(/Error:.*i18ndemo/, result.stderr)\n    end\n  end\n\n  step 'Expect no production environment folder changes' do\n    on(agent, \"ls #{custom_environment_path}\") do |result|\n      refute_match(/production/, result.stdout)\n    end\n\n    on(agent, \"ls #{default_environment_path}\") do |result|\n      assert_match('production', result.stdout)\n    end\n  end\n\n  step 'Remove production folder' do\n    agent.rm_rf(\"#{default_environment_path}/production\")\n  end\n\n  step 'Expect production environment folder to be recreated in the custom path' do\n    on(agent, puppet(\"agent -t\"), :acceptable_exit_codes => [0, 2]) do |result|\n      step 'Expect the module to be gone on the server node' do\n        refute_match(/Error:.*i18ndemo/, result.stderr)\n      end if agent == master\n\n      step 'Expect the production environment, along with the module, to be synced back on the agent node' do\n        assert_match(/Error:.*i18ndemo/, result.stderr)\n      end if agent != master\n    end\n\n    on(agent, \"ls #{custom_environment_path}\") do |result|\n      assert_match('production', result.stdout, \"Default environment folder was not generated in last puppet run\")\n    end\n\n    on(agent, \"ls #{default_environment_path}\") do |result|\n      refute_match(/production/, result.stdout)\n    end\n  end\n\n  step 'Set back to just default environmentpath setting' do\n    on(agent, puppet(\"config delete environmentpath\"))\n  end\n\n  step 'Expect production environment folder to be found in both paths but use the default one' do\n    on(agent, puppet(\"agent -t\"), :acceptable_exit_codes => [0, 2]) do |result|\n      step 'Expect the module to be gone' do\n        refute_match(/Error:.*i18ndemo/, result.stderr)\n      end if agent == master\n    end\n\n    on(agent, \"ls #{default_environment_path}\") do |result|\n      assert_match('production', result.stdout, \"Default environment folder was not generated in last puppet run\")\n    end\n\n    on(agent, \"ls #{custom_environment_path}\") do |result|\n      assert_match('production', result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_agent_environment_when_enc_doesnt_specify.rb",
    "content": "test_name \"Agent should use agent environment if there is an enc that does not specify the environment\" do\n  require 'puppet/acceptance/classifier_utils'\n  extend Puppet::Acceptance::ClassifierUtils\n\n  tag 'audit:high',\n      'audit:integration',\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  classify_nodes_as_agent_specified_if_classifer_present\n\n  testdir = create_tmpdir_for_user(master, 'use_agent_env')\n\n  create_remote_file(master, \"#{testdir}/enc.rb\", <<END)\n#!#{master['privatebindir']}/ruby\nputs <<YAML\nparameters:\nYAML\nEND\n  on(master, \"chmod 755 '#{testdir}/enc.rb'\")\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0770\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/production/manifests':;\n    '#{testdir}/environments/more_different/':;\n    '#{testdir}/environments/more_different/manifests':;\n  }\n  file { '#{testdir}/environments/production/manifests/site.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"production environment\": }',\n  }\n  file { '#{testdir}/environments/more_different/manifests/more_different.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"more_different_string\": }',\n  }\n  MANIFEST\n\n  master_opts = {\n      'main' => {\n          'environmentpath' => \"#{testdir}/environments\",\n          'node_terminus'   => 'exec',\n          'external_nodes'  => \"#{testdir}/enc.rb\",\n      },\n  }\n\n  with_puppet_running_on(master, master_opts, testdir) do\n\n    agents.each do |agent|\n      run_agent_on(agent, \"--no-daemonize --onetime --verbose --environment more_different\") do |result|\n        assert_match(/more_different_string/, result.stdout, \"Did not find more_different_string from \\\"more_different\\\" environment\")\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_agent_environment_when_no_enc.rb",
    "content": "test_name \"Agent should use agent environment if there is no enc-specified environment\" do\n\n  tag 'audit:high',\n      'audit:integration',\n      'audit:refactor', # This can be combined with use_agent_environment_when_enc_doesnt_specify test\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  testdir = create_tmpdir_for_user(master, 'use_agent_env')\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0770\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/production/manifests':;\n    '#{testdir}/environments/more_different/':;\n    '#{testdir}/environments/more_different/manifests':;\n  }\n  file { '#{testdir}/environments/production/manifests/site.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"production environment\": }',\n  }\n  file { '#{testdir}/environments/more_different/manifests/more_different.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"more_different_string\": }',\n  }\n  MANIFEST\n\n  master_opts = {\n      'main'   => {\n          'environmentpath' => \"#{testdir}/environments\",\n      },\n      'master' => {\n          'node_terminus' => 'plain'\n      },\n  }\n\n  with_puppet_running_on(master, master_opts, testdir) do\n\n    agents.each do |agent|\n      run_agent_on(agent, \"--no-daemonize --onetime --verbose --environment more_different\") do |result|\n        assert_match(/more_different_string/, result.stdout, \"Did not find more_different_string from \\\"more_different\\\" environment\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_enc_environment.rb",
    "content": "test_name 'Agent should use environment given by ENC and only compile a catalog once' do\n  require 'puppet/acceptance/classifier_utils.rb'\n  extend Puppet::Acceptance::ClassifierUtils\n\n  tag 'audit:high',\n      'audit:integration',\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  testdir = create_tmpdir_for_user(master, 'use_enc_env')\n\n  if master.is_pe?\n    group = {\n        'name'               => 'Special Environment',\n        'description'        => 'Classify our test agent nodes in the special environment.',\n        'environment'        => 'special',\n        'environment_trumps' => true,\n    }\n    create_group_for_nodes(agents, group)\n  else\n\n    create_remote_file(master, \"#{testdir}/enc.rb\", <<END)\n#!#{master['privatebindir']}/ruby\nputs <<YAML\nparameters:\nenvironment: special\nYAML\nEND\n    on(master, \"chmod 755 '#{testdir}/enc.rb'\")\n\n  end\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0770\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/production/manifests':;\n    '#{testdir}/environments/special/':;\n    '#{testdir}/environments/special/manifests':;\n  }\n  file { '#{testdir}/environments/production/manifests/site.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"production environment\": }',\n  }\n  file { '#{testdir}/environments/special/manifests/different.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"expected_string\": }',\n  }\n  MANIFEST\n\n  master_opts           = {\n      'main' => {\n          'environmentpath' => \"#{testdir}/environments\",\n      },\n  }\n  master_opts['master'] = {\n      'node_terminus'  => 'exec',\n      'external_nodes' => \"#{testdir}/enc.rb\",\n  } if !master.is_pe?\n\n  with_puppet_running_on(master, master_opts, testdir) do\n\n    agents.each do |agent|\n      run_agent_on(agent, \"--no-daemonize --onetime --verbose\") do |result|\n        assert_match(/expected_string/, result.stdout, \"Did not find expected_string from \\\"special\\\" environment\")\n        caching_catalog_message_count = result.stdout.split(/Info: Caching catalog for/).length - 1\n        assert_equal(caching_catalog_message_count, 1, 'Should only compile and cache the catalog once during the run')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_enc_environment_for_files.rb",
    "content": "test_name \"Agent should use environment given by ENC for fetching remote files\" do\n\n  tag 'audit:high',\n      'audit:integration',\n      'audit:refactor', # This test should be rolled into use_enc_environment\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  testdir = create_tmpdir_for_user(master, 'respect_enc_test')\n\n  create_remote_file(master, \"#{testdir}/enc.rb\", <<END)\n#!#{master['privatebindir']}/ruby\nputs <<YAML\nparameters:\nenvironment: special\nYAML\nEND\n  on(master, \"chmod 755 '#{testdir}/enc.rb'\")\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0770\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/special/':;\n    '#{testdir}/environments/special/manifests':;\n    '#{testdir}/environments/special/modules':;\n    '#{testdir}/environments/special/modules/amod':;\n    '#{testdir}/environments/special/modules/amod/files':;\n  }\n  file { '#{testdir}/environments/special/modules/amod/files/testy':\n    ensure => file,\n    mode => \"0640\",\n    content => 'special_environment',\n  }\n  MANIFEST\n\n  master_opts = {\n      'main'   => {\n          'environmentpath'     => \"#{testdir}/environments\",\n          'environment_timeout' => 0,\n      },\n      'master' => {\n          'node_terminus'  => 'exec',\n          'external_nodes' => \"#{testdir}/enc.rb\",\n      },\n  }\n\n  with_puppet_running_on(master, master_opts, testdir) do\n    agents.each do |agent|\n      atmp = agent.tmpdir('respect_enc_test')\n      teardown do\n        on(agent, \"rm -rf '#{atmp}'\")\n      end\n\n      logger.debug \"agent: #{agent} \\tagent.tmpdir => #{atmp}\"\n\n      create_remote_file(master, \"#{testdir}/environments/special/manifests/different.pp\", <<END)\nfile { \"#{atmp}/special_testy\":\n  source => \"puppet:///modules/amod/testy\",\n}\nEND\n      on(master, \"chmod 644 '#{testdir}/environments/special/manifests/different.pp'\")\n\n      run_agent_on(agent, \"--no-daemonize --onetime --verbose --trace\")\n\n      on(agent, \"cat '#{atmp}/special_testy'\") do |result|\n        assert_match(/special_environment/,\n                     result.stdout,\n                     \"The file from environment 'special' was not found\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_enc_environment_for_pluginsync.rb",
    "content": "test_name \"Agent should use environment given by ENC for pluginsync\" do\n\n  tag 'audit:high',\n      'audit:integration',\n      'audit:refactor', # This test should be rolled into use_enc_environment\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  testdir = create_tmpdir_for_user(master, 'respect_enc_test')\n\n  create_remote_file(master, \"#{testdir}/enc.rb\", <<END)\n#!#{master['privatebindir']}/ruby\nputs <<YAML\nparameters:\nenvironment: special\nYAML\nEND\n  on(master, \"chmod 755 #{testdir}/enc.rb\")\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0770\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/special/':;\n    '#{testdir}/environments/special/modules':;\n    '#{testdir}/environments/special/modules/amod':;\n    '#{testdir}/environments/special/modules/amod/lib':;\n    '#{testdir}/environments/special/modules/amod/lib/puppet':;\n  }\n  file { '#{testdir}/environments/special/modules/amod/lib/puppet/foo.rb':\n    ensure => file,\n    mode => \"0640\",\n    content => \"#special_version\",\n  }\n  MANIFEST\n\n  master_opts = {\n      'main'   => {\n          'environmentpath' => \"#{testdir}/environments\",\n      },\n      'master' => {\n          'node_terminus'  => 'exec',\n          'external_nodes' => \"#{testdir}/enc.rb\"\n      },\n  }\n\n  with_puppet_running_on(master, master_opts, testdir) do\n\n    agents.each do |agent|\n      agent_vardir = agent.puppet['vardir']\n      teardown do\n        on(agent, \"rm -rf '#{agent_vardir}/lib'\")\n      end\n\n      run_agent_on(agent, \"-t --no-daemonize --onetime\")\n      on(agent, \"cat '#{agent_vardir}/lib/puppet/foo.rb'\") do |result|\n        assert_match(/#special_version/, result.stdout, \"The plugin from environment 'special' was not synced\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_environment_from_environmentpath.rb",
    "content": "test_name \"Use environments from the environmentpath\" do\n  require 'puppet/acceptance/classifier_utils'\n  extend Puppet::Acceptance::ClassifierUtils\n\n  tag 'audit:high',\n      'audit:integration',\n      'server'\n\n  classify_nodes_as_agent_specified_if_classifer_present\n\n  testdir = create_tmpdir_for_user(master, 'use_environmentpath')\n\n  def generate_environment(path_to_env, environment)\n    <<-EOS\n  \"#{path_to_env}/#{environment}\":;\n  \"#{path_to_env}/#{environment}/manifests\":;\n  \"#{path_to_env}/#{environment}/modules\":;\n    EOS\n  end\n\n  def generate_module_content(module_name, options = {})\n    base_path   = options[:base_path]\n    environment = options[:environment]\n    env_path    = options[:env_path]\n\n    path_to_module = [base_path, env_path, environment, \"modules\"].compact.join(\"/\")\n    module_info    = \"module-#{module_name}\"\n    module_info << \"-from-#{environment}\" if environment\n\n    <<-EOS\n  \"#{path_to_module}/#{module_name}\":;\n  \"#{path_to_module}/#{module_name}/manifests\":;\n  \"#{path_to_module}/#{module_name}/files\":;\n  \"#{path_to_module}/#{module_name}/templates\":;\n  \"#{path_to_module}/#{module_name}/lib\":;\n  \"#{path_to_module}/#{module_name}/lib/facter\":;\n\n  \"#{path_to_module}/#{module_name}/manifests/init.pp\":\n    ensure => file,\n    mode => \"0640\",\n    content => 'class #{module_name} {\n      notify { \"template-#{module_name}\": message => template(\"#{module_name}/our_template.erb\") }\n      file { \"$agent_file_location/file-#{module_info}\": source => \"puppet:///modules/#{module_name}/data\" }\n    }'\n  ;\n  \"#{path_to_module}/#{module_name}/lib/facter/environment_fact_#{module_name}.rb\":\n    ensure => file,\n    mode => \"0640\",\n    content => \"Facter.add(:environment_fact_#{module_name}) { setcode { 'environment fact from #{module_info}' } }\"\n  ;\n  \"#{path_to_module}/#{module_name}/files/data\":\n    ensure => file,\n    mode => \"0640\",\n    content => \"data file from #{module_info}\"\n  ;\n  \"#{path_to_module}/#{module_name}/templates/our_template.erb\":\n    ensure => file,\n    mode => \"0640\",\n    content => \"<%= @environment_fact_#{module_name} %>\"\n  ;\n    EOS\n  end\n\n  def generate_site_manifest(path_to_manifest, *modules_to_include)\n    <<-EOS\n  \"#{path_to_manifest}/site.pp\":\n    ensure => file,\n    mode => \"0640\",\n    content => \"#{modules_to_include.map {|m| \"include #{m}\"}.join(\"\\n\")}\"\n  ;\n    EOS\n  end\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n  mode => \"0770\",\n}\n\nfile {\n  \"#{testdir}\":;\n  \"#{testdir}/base\":;\n  \"#{testdir}/additional\":;\n  \"#{testdir}/modules\":;\n#{generate_environment(\"#{testdir}/base\", \"shadowed\")}\n  #{generate_environment(\"#{testdir}/base\", \"onlybase\")}\n  #{generate_environment(\"#{testdir}/additional\", \"shadowed\")}\n\n  #{generate_module_content(\"atmp\",\n                            :base_path   => testdir,\n                            :env_path    => 'base',\n                            :environment => 'shadowed')}\n  #{generate_site_manifest(\"#{testdir}/base/shadowed/manifests\", \"atmp\", \"globalmod\")}\n\n  #{generate_module_content(\"atmp\",\n                            :base_path   => testdir,\n                            :env_path    => 'base',\n                            :environment => 'onlybase')}\n  #{generate_site_manifest(\"#{testdir}/base/onlybase/manifests\", \"atmp\", \"globalmod\")}\n\n  #{generate_module_content(\"atmp\",\n                            :base_path   => testdir,\n                            :env_path    => 'additional',\n                            :environment => 'shadowed')}\n  #{generate_site_manifest(\"#{testdir}/additional/shadowed/manifests\", \"atmp\", \"globalmod\")}\n\n# And one global module (--modulepath setting)\n#{generate_module_content(\"globalmod\", :base_path => testdir)}\n  \"#{testdir}/additional/production\":;\n  \"#{testdir}/additional/production/manifests\":;\n#{generate_site_manifest(\"#{testdir}/additional/production/manifests\", \"globalmod\")}\n}\n  MANIFEST\n\n  def run_with_environment(agent, environment, options = {})\n    expected_exit_code = options[:expected_exit_code] || 2\n\n    step \"running an agent in environment '#{environment}'\"\n    atmp = agent.tmpdir(\"use_environmentpath_#{environment}\")\n\n    teardown do\n      on(agent, \"rm -rf '#{atmp}'\")\n    end\n\n    agent_config = [\n        \"-t\"\n    ]\n    agent_config << '--environment' << environment if environment\n    # This to test how the agent behaves when using the directory environment\n    # loaders (which will not load an environment if it does not exist)\n    agent_config << \"--environmentpath='$confdir/environments'\" if agent != master\n    agent_config << {\n        'ENV' => { \"FACTER_agent_file_location\" => atmp },\n    }\n\n    on(agent,\n       puppet(\"agent\", *agent_config),\n       :acceptable_exit_codes => [expected_exit_code]) do |result|\n\n      yield atmp, result\n    end\n  end\n\n  master_opts = {\n      'master' => {\n          'environmentpath' => \"#{testdir}/additional:#{testdir}/base\",\n          'basemodulepath'  => \"#{testdir}/modules\",\n      }\n  }\n  if master.is_pe?\n    master_opts['master']['basemodulepath'] << \":#{master['sitemoduledir']}\"\n  end\n\n  with_puppet_running_on(master, master_opts, testdir) do\n    agents.each do |agent|\n      run_with_environment(agent, \"shadowed\") do |tmpdir, catalog_result|\n        [\"module-atmp-from-shadowed\", \"module-globalmod\"].each do |expected|\n          assert_match(/environment fact from #{expected}/, catalog_result.stdout)\n        end\n\n        [\"module-atmp-from-shadowed\", \"module-globalmod\"].each do |expected|\n          on(agent, \"cat '#{tmpdir}/file-#{expected}'\") do |file_result|\n            assert_match(/data file from #{expected}/, file_result.stdout)\n          end\n        end\n      end\n\n      run_with_environment(agent, \"onlybase\") do |tmpdir, catalog_result|\n        [\"module-atmp-from-onlybase\", \"module-globalmod\"].each do |expected|\n          assert_match(/environment fact from #{expected}/, catalog_result.stdout)\n        end\n\n        [\"module-atmp-from-onlybase\", \"module-globalmod\"].each do |expected|\n          on(agent, \"cat '#{tmpdir}/file-#{expected}'\") do |file_result|\n            assert_match(/data file from #{expected}/, file_result.stdout)\n          end\n        end\n      end\n\n      run_with_environment(agent, \"production\", :expected_exit_code => 2) do |tmpdir, catalog_result|\n        refute_match(/module-atmp/, catalog_result.stdout, \"module-atmp was included despite the default environment being loaded\")\n\n        assert_match(/environment fact from module-globalmod/, catalog_result.stdout)\n\n        on(agent, \"cat '#{tmpdir}/file-module-globalmod'\") do |file_result|\n          assert_match(/data file from module-globalmod/, file_result.stdout)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/use_last_server_specified_environment.rb",
    "content": "test_name \"Agent should use the last server-specified environment if server is authoritative\" do\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  tag 'audit:high',\n      'server'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  testdir = create_tmpdir_for_user(master, 'use_enc_env')\n\n  create_remote_file(master, \"#{testdir}/enc.rb\", <<END)\n#!#{master['privatebindir'] || '/opt/puppetlabs/puppet/bin'}/ruby\nputs <<YAML\nparameters:\nenvironment: special\nYAML\nEND\n  on(master, \"chmod 755 '#{testdir}/enc.rb'\")\n\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0770\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/production/manifests':;\n    '#{testdir}/environments/special/':;\n    '#{testdir}/environments/special/manifests':;\n  }\n  file { '#{testdir}/environments/production/manifests/site.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"production environment\": }',\n  }\n  file { '#{testdir}/environments/special/manifests/different.pp':\n    ensure => file,\n    mode => \"0640\",\n    content => 'notify { \"special environment\": }',\n  }\n  MANIFEST\n\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{testdir}/environments\",\n    },\n  }\n  master_opts['master'] = {\n    'node_terminus'  => 'exec',\n    'external_nodes' => \"#{testdir}/enc.rb\",\n  } if !master.is_pe?\n\n  with_puppet_running_on(master, master_opts, testdir) do\n    agents.each do |agent|\n      step 'ensure the lastrunfile is absent for the first run' do\n        on(agent, puppet('config print lastrunfile')) do |command_result|\n          agent.rm_rf(command_result.stdout)\n        end\n      end\n\n      step 'first run: agent makes a node request to get the environment' do\n        run_agent_on(agent, '--no-daemonize --onetime --debug') do |result|\n          assert_match(/Local environment: 'production' doesn't match server specified node environment 'special', switching agent to 'special'/, result.stdout)\n          assert_match(/Debug: HTTP GET .*\\/puppet\\/v3\\/node/, result.stdout)\n          assert_match(/Notice: special environment/, result.stdout)\n        end\n      end\n\n      step 'second run: agent uses the environment from lastrunfile' do\n        run_agent_on(agent, '--no-daemonize --onetime --debug') do |result|\n          assert_match(/Debug: Successfully loaded last environment from the lastrunfile/, result.stdout)\n          assert_match(/Notice: special environment/, result.stdout)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/environment/variables_refreshed_each_compilation.rb",
    "content": "test_name 'C98115 compilation should get new values in variables on each compilation' do\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  confine :except, :platform => /^(aix|osx|solaris)/\n\n  tag 'audit:high',\n      'audit:integration',\n      'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_type               = File.basename(__FILE__, '.*')\n  tmp_environment        = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath = \"#{environmentpath}/#{tmp_environment}\"\n\n  create_remote_file(master, \"#{fq_tmp_environmentpath}/environment.conf\", <<-CONF)\n    environment_timeout = unlimited\n  CONF\n  # the module function loading logic is different from inside a single manifest\n  #   we exercise both here\n  on(master, \"mkdir -p '#{fq_tmp_environmentpath}'/modules/custom_time/{manifests,functions,facts.d}\")\n  create_remote_file(master, \"#{fq_tmp_environmentpath}/modules/custom_time/manifests/init.pp\", <<-FILE)\n    class custom_time {\n      $t = custom_time::my_system_time()\n\n      notify { 'custom time':\n        message => \"module_${t}_module\",\n      }\n    }\n  FILE\n  create_remote_file(master, \"#{fq_tmp_environmentpath}/modules/custom_time/functions/my_system_time.pp\", <<-FILE)\n    function custom_time::my_system_time() {\n      $facts['custom_time']\n    }\n  FILE\n  create_sitepp(master, tmp_environment, <<-SITE)\n    function bar() {\n      $facts['custom_time']\n    }\n    class foo::bar {\n      notify { \"local_${bar()}_local\": }\n    }\n    include foo::bar\n    include custom_time\n  SITE\n  create_remote_file(master, \"#{fq_tmp_environmentpath}/modules/custom_time/facts.d/custom_time.sh\", <<-FILE)\n#!/bin/bash\n\n\nif [[ `uname` == 'Darwin' ]]; then\n  echo -n \"custom_time=$(date +%s)\"\nelse\n  echo -n \"custom_time=$(date +%s%N)\"\nfi\n  FILE\n\n  on(master, \"chmod -R 0777 '#{fq_tmp_environmentpath}/'\")\n\n  windows_fact_location = \"#{fq_tmp_environmentpath}/modules/custom_time/facts.d/custom_time.ps1\"\n  create_remote_file(master, windows_fact_location, <<-FILE)\necho \"custom_time=$(get-date -format HHmmssffffff)\"\n  FILE\n\n  on(master, \"chmod -R 0666 '#{windows_fact_location}'\")\n\n\n  step \"run agent in #{tmp_environment}, ensure it increments the customtime with each run\" do\n    with_puppet_running_on(master, {}) do\n      local_custom_time_pattern  = 'local_(\\d+)_local'\n      module_custom_time_pattern = 'module_(\\d+)_module'\n      agents.each do |agent|\n        # ensure our custom facts have been synced\n        on(agent,\n           puppet(\"agent -t --environment '#{tmp_environment}'\"),\n           :accept_all_exit_codes => true)\n\n        local_custom_time1 = module_custom_time1 = nil\n        local_custom_time2 = module_custom_time2 = nil\n\n        on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"),\n           :accept_all_exit_codes => [2]) do |result|\n          assert_match(/Notice: #{local_custom_time_pattern}/, result.stdout, 'first custom time was not as expected')\n          assert_match(/Notice: #{module_custom_time_pattern}/, result.stdout, 'first module uptime was not as expected')\n\n          local_custom_time1  = result.stdout.match(/Notice: #{local_custom_time_pattern}/)[1].to_i\n          module_custom_time1 = result.stdout.match(/Notice: #{module_custom_time_pattern}/)[1].to_i\n        end\n\n        sleep 1\n\n        on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"),\n           :accept_all_exit_codes => [2]) do |result|\n          assert_match(/Notice: #{local_custom_time_pattern}/, result.stdout, 'second custom time was not as expected')\n          assert_match(/Notice: #{module_custom_time_pattern}/, result.stdout, 'second module uptime was not as expected')\n\n          local_custom_time2  = result.stdout.match(/Notice: #{local_custom_time_pattern}/)[1].to_i\n          module_custom_time2 = result.stdout.match(/Notice: #{module_custom_time_pattern}/)[1].to_i\n        end\n\n        assert(local_custom_time2 > local_custom_time1, 'local custom time did not change as expected if at all')\n        assert(module_custom_time2 > module_custom_time1, 'module custom time did not change as expected if at all')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/face/4654_facts_face.rb",
    "content": "test_name \"Puppet facts face should resolve custom and external facts\"\n\ntag 'audit:high',\n    'audit:integration'   # The facter acceptance tests should be acceptance.\n                          # However, the puppet face merely needs to interact with libfacter.\n                          # So, this should be an integration test.\n#\n# This test is intended to ensure that custom and external facts present\n# on the agent are resolved and displayed by the puppet facts face.\n#\ncustom_fact = <<CFACT\nFacter.add('custom_fact') do\n  setcode do\n    'foo'\n  end\nend\nCFACT\n\nunix_external_fact = <<EFACT\n#!/bin/sh\necho 'external_fact=bar'\nEFACT\n\nwin_external_fact = <<EFACT\n@echo off\necho external_fact=bar\nEFACT\n\nagents.each do |agent|\n  if agent['platform'] =~ /windows/\n    external_fact = win_external_fact\n    ext = '.bat'\n  else\n    external_fact = unix_external_fact\n    ext = '.sh'\n  end\n\n  step \"Create custom and external facts in their default directories on the agent\"\n\n  teardown do\n    on agent, \"rm -rf #{agent.puppet['plugindest']}/facter\"\n    on agent, \"rm -rf #{agent.puppet['pluginfactdest']}/external#{ext}\"\n  end\n\n  on agent, puppet('apply'), :stdin => <<MANIFEST\n  file { \"#{agent.puppet['plugindest']}/facter\":\n    ensure => directory,\n  }\n\n  file { \"#{agent.puppet['plugindest']}/facter/custom.rb\":\n    ensure  => file,\n    content => \"#{custom_fact}\",\n  }\n\n  file { \"#{agent.puppet['pluginfactdest']}/external#{ext}\":\n    ensure  => file,\n    mode    => \"0755\",\n    content => \"#{external_fact}\",\n  }\nMANIFEST\n\n  step \"Agent #{agent}: custom_fact and external_fact should be present in the output of `puppet facts`\"\n  on agent, puppet('facts') do |result|\n    assert_match(/\"custom_fact\": \"foo\"/, result.stdout, \"custom_fact did not match expected output\")\n    assert_match(/\"external_fact\": \"bar\"/, result.stdout, \"external_fact did not match expected output\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/face/loadable_from_modules.rb",
    "content": "test_name \"Exercise loading a face from a module\"\n\n# Because the module tool does not work on windows, we can't run this test there\nconfine :except, :platform => 'windows'\n\ntag 'audit:high',\n    'audit:acceptance',    # This has been OS sensitive.\n    'audit:refactor'       # Remove the confine against windows and refactor to\n                           # accommodate the Windows platform.\n\nrequire 'puppet/acceptance/temp_file_utils'\nextend Puppet::Acceptance::TempFileUtils\ninitialize_temp_dirs\n\nmetadata_json_file = <<-FILE\n{\n  \"name\": \"puppetlabs-helloworld\",\n  \"version\": \"0.0.1\",\n  \"author\": \"Puppet Labs\",\n  \"summary\": \"Nginx Module\",\n  \"license\": \"Apache Version 2.0\",\n  \"source\": \"https://github.com/puppetlabs/puppetlabs-nginx\",\n  \"project_page\": \"https://github.com/puppetlabs/puppetlabs-nginx\",\n  \"issues_url\": \"https://github.com/puppetlabs/puppetlabs-nginx\",\n  \"dependencies\": [\n    {\"name\":\"puppetlabs-stdlub\",\"version_requirement\":\">= 1.0.0\"}\n  ]\n}\nFILE\n\nagents.each do |agent|\n\n  if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n    puts \"Module build, loading and installing not supported on fips enabled platforms\"\n    next\n  end\n\n  environmentpath = get_test_file_path(agent, 'environments')\n  dev_modulepath = \"#{environmentpath}/dev/modules\"\n  module_base_dir = \"#{dev_modulepath}/helloworld\"\n\n  teardown do\n    on agent, \"rm -rf #{module_base_dir}\"\n  end\n\n  # make sure that we use the modulepath from the dev environment\n  puppetconf = get_test_file_path(agent, 'puppet.conf')\n  on agent, puppet(\"config\", \"set\", \"environmentpath\", environmentpath, \"--section\", \"main\", \"--config\", puppetconf)\n  on agent, puppet(\"config\", \"set\", \"environment\", \"dev\", \"--section\", \"user\", \"--config\", puppetconf)\n\n  mkdirs agent, module_base_dir\n  create_remote_file(agent, \"#{module_base_dir}/metadata.json\", metadata_json_file)\n  mkdirs agent, \"#{module_base_dir}/lib/puppet/application\"\n  mkdirs agent, \"#{module_base_dir}/lib/puppet/face\"\n\n  # copy application, face, and utility module\n  create_remote_file(agent, \"#{module_base_dir}/lib/puppet/application/helloworld.rb\", <<'EOM')\nrequire 'puppet/face'\nrequire 'puppet/application/face_base'\n\nclass Puppet::Application::Helloworld < Puppet::Application::FaceBase\nend\nEOM\n\n  create_remote_file(agent, \"#{module_base_dir}/lib/puppet/face/helloworld.rb\", <<'EOM')\nPuppet::Face.define(:helloworld, '0.1.0') do\n  summary \"Hello world face\"\n  description \"This is the hello world face\"\n\n  action 'actionprint' do\n    summary \"Prints hello world from an action\"\n    when_invoked do |options|\n      puts \"Hello world from an action\"\n    end\n  end\n\n  action 'moduleprint' do\n    summary \"Prints hello world from a required module\"\n    when_invoked do |options|\n      require 'puppet/helloworld.rb'\n      Puppet::Helloworld.print\n    end\n  end\nend\nEOM\n\n  create_remote_file(agent, \"#{module_base_dir}/lib/puppet/helloworld.rb\", <<'EOM')\nmodule Puppet::Helloworld\n  def print\n    puts \"Hello world from a required module\"\n  end\n  module_function :print\nend\nEOM\n\n  on(agent, puppet('help', '--config', puppetconf)) do |result|\n    assert_match(/helloworld\\s*Hello world face/, result.stdout, \"Face missing from list of available subcommands\")\n  end\n\n  on(agent, puppet('help', 'helloworld', '--config', puppetconf)) do |result|\n    assert_match(/This is the hello world face/, result.stdout, \"Descripion help missing\")\n    assert_match(/moduleprint\\s*Prints hello world from a required module/, result.stdout, \"help for moduleprint action missing\")\n    assert_match(/actionprint\\s*Prints hello world from an action/, result.stdout, \"help for actionprint action missing\")\n  end\n\n  on(agent, puppet('helloworld', 'actionprint', '--config', puppetconf)) do |result|\n    assert_match(/^Hello world from an action$/, result.stdout, \"face did not print hello world\")\n  end\n\n  on(agent, puppet('helloworld', 'moduleprint', '--config', puppetconf)) do |result|\n    assert_match(/^Hello world from a required module$/, result.stdout, \"face did not load module to print hello world\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/face/parser_validate.rb",
    "content": "test_name 'parser validate' do\n\ntag 'audit:high',\n    'audit:unit'   # Parser validation should be core to ruby\n                   # and platform agnostic.\n\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  app_type = File.basename(__FILE__, '.*')\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  agents.each do |agent|\n    skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr'\n\n    step 'manifest with parser function call' do\n      if agent.platform !~ /windows/\n        tmp_environment   = mk_tmp_environment_with_teardown(agent, app_type)\n\n        create_sitepp(agent, tmp_environment, <<-SITE)\nfunction validate_this() {\n  notice('hello, puppet')\n}\nvalidate_this()\n        SITE\n        on(agent, puppet(\"parser validate --environment #{tmp_environment}\"), :pty => true) # default manifest\n      end\n\n      # manifest with Type aliases\n      create_test_file(agent, \"#{app_type}.pp\", <<-PP)\nfunction validate_this() {\n  notice('hello, puppet')\n}\nvalidate_this()\ntype MyInteger = Integer\nnotice 42 =~ MyInteger\n      PP\n      tmp_manifest = get_test_file_path(agent, \"#{app_type}.pp\")\n      on(agent, puppet(\"parser validate #{tmp_manifest}\"))\n    end\n\n    step 'manifest with bad syntax' do\n      create_test_file(agent, \"#{app_type}_broken.pp\", \"notify 'hello there'\")\n      tmp_manifest = get_test_file_path(agent, \"#{app_type}_broken.pp\")\n      on(agent, puppet(\"parser validate #{tmp_manifest}\"), :accept_all_exit_codes => true) do |result|\n        assert_equal(result.exit_code, 1, 'parser validate did not exit with 1 upon parse failure')\n        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\\)/\n        assert_match(expected, result.output, \"parser validate did not output correctly: '#{result.output}'. expected: '#{expected.to_s}'\") unless agent['locale'] == 'ja'\n      end\n    end\n\n    step '(large) manifest with exported resources' do\n      fixture_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures/manifest_large_exported_classes_node.pp')\n      create_test_file(agent, \"#{app_type}_exported.pp\", File.read(fixture_path))\n      tmp_manifest = get_test_file_path(agent, \"#{app_type}_exported.pp\")\n      on(agent, puppet(\"parser validate #{tmp_manifest}\"))\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/enable_option_disable_i18n.rb",
    "content": "test_name 'Verify that disable_i18n can be set to true and have translations disabled' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n\n  step \"configure server locale to #{language}\" do\n    configure_master_system_locale(language)\n  end\n\n  tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*'))\n\n  step 'install a i18ndemo module' do\n    install_i18n_demo_module(master, tmp_environment)\n  end\n\n  disable_i18n_default_master = master.puppet['disable_i18n']\n  teardown do\n    step 'resetting the server locale' do\n      on(master, puppet(\"config set disable_i18n #{ disable_i18n_default_master }\"))\n      reset_master_system_locale\n    end\n    step 'uninstall the module' do\n      agents.each do |agent|\n        uninstall_i18n_demo_module(agent)\n      end\n      uninstall_i18n_demo_module(master)\n    end\n  end\n\n  agents.each do |agent|\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n      on(master, puppet(\"config set disable_i18n false\"))\n      reset_master_system_locale\n    end\n\n    step 'expect #{language} translation for a custom type' do\n      site_pp_content = <<-PP\n        node default {\n          i18ndemo_type { '12345': }\n        }\n      PP\n      create_sitepp(master, tmp_environment, site_pp_content)\n      on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result|\n        assert_match(/Error: .* \\w+-i18ndemo type: 値は有12345効な値ではありません/, result.stderr, 'missing error from invalid value for custom type param')\n      end\n    end\n\n    step 'disable i18n' do\n      on(agent, puppet(\"config set disable_i18n true\"))\n      on(master, puppet(\"config set disable_i18n true\"))\n      reset_master_system_locale\n    end\n\n    step 'expect no #{language} translation for a custom type' do\n      site_pp_content = <<-PP\n        node default {\n          i18ndemo_type { '12345': }\n        }\n      PP\n      create_sitepp(master, tmp_environment, site_pp_content)\n      on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result|\n        assert_match(/Error: .* Value 12345 is not a valid value for i18ndemo_type\\:\\:name/, result.stderr)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_agent.rb",
    "content": "test_name 'C100565: puppet agent with module should translate messages' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  skip_test('i18n test module uses deprecated function; update module to resume testing.')\n  # function validate_absolute_path used https://github.com/eputnam/eputnam-i18ndemo/blob/621d06d/manifests/init.pp#L15\n\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n  disable_i18n_default_master = master.puppet['disable_i18n']\n\n  step 'enable i18n on master' do\n    on(master, puppet(\"config set disable_i18n false\"))\n  end\n\n  step \"configure server locale to #{language}\" do\n    configure_master_system_locale(language)\n  end\n\n  tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*'))\n\n  step 'install a i18ndemo module' do\n    install_i18n_demo_module(master, tmp_environment)\n  end\n\n  teardown do\n    step 'resetting the server locale' do\n      on(master, puppet(\"config set disable_i18n #{ disable_i18n_default_master }\"))\n      reset_master_system_locale\n    end\n    step 'uninstall the module' do\n      agents.each do |agent|\n        uninstall_i18n_demo_module(agent)\n      end\n      uninstall_i18n_demo_module(master)\n    end\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    type_path = agent.tmpdir('provider')\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      agent.rm_rf(type_path)\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step \"Run puppet agent of a module with language #{agent_language} and verify the translations\" do\n\n      step 'verify custom fact translations' do\n        site_pp_content_1 = <<-PP\n          node default {\n            class { 'i18ndemo':\n              filename => '#{type_path}'\n            }\n          }\n        PP\n\n        create_sitepp(master, tmp_environment, site_pp_content_1)\n        on(agent, puppet(\"agent -t --no-disable_i18n --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(/.*\\w+-i18ndemo fact: これは\\w+-i18ndemoからのカスタムファクトからのレイズです/, result.stderr, 'missing translation for raise from ruby fact')\n        end\n      end unless agent['platform'] =~ /ubuntu-16.04/ # Condition to be removed after FACT-2799 gets resolved\n\n      step 'verify custom type translations' do\n        site_pp_content_2 = <<-PP\n          node default {\n            i18ndemo_type { 'hello': }\n          }\n        PP\n\n        create_sitepp(master, tmp_environment, site_pp_content_2)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result|\n          assert_match(/Warning:.*\\w+-i18ndemo type: 良い値/, result.stderr, 'missing warning from custom type')\n        end\n\n        site_pp_content_3 = <<-PP\n          node default {\n            i18ndemo_type { '12345': }\n          }\n        PP\n        create_sitepp(master, tmp_environment, site_pp_content_3)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result|\n          assert_match(/Error: .* \\w+-i18ndemo type: 値は有12345効な値ではありません/, result.stderr, 'missing error from invalid value for custom type param')\n        end\n      end\n\n      step 'verify custom provider translation' do\n        site_pp_content_4 = <<-PP\n          node default {\n            i18ndemo_type { 'hello': \n              ensure => present, \n              dir => '#{type_path}',\n            }\n          }\n        PP\n        create_sitepp(master, tmp_environment, site_pp_content_4)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language)) do |result|\n          assert_match(/Warning:.*\\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, result.stderr, 'missing translated provider message')\n        end\n      end\n\n      step 'verify function string translation' do\n        site_pp_content_5 = <<-PP\n          node default {\n            notify { 'happy': \n              message => happyfuntime('happy') \n            }\n          }\n        PP\n        create_sitepp(master, tmp_environment, site_pp_content_5)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 2) do |result|\n          assert_match(/Notice: --\\*\\w+-i18ndemo function: それは楽しい時間です\\*--/, result.stdout, 'missing translated notice message')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_agent_cached_catalog.rb",
    "content": "test_name 'C100566: puppet agent with module should translate messages when using a cached catalog' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n  disable_i18n_default_master = master.puppet['disable_i18n']\n\n  step 'enable i18n on master' do\n    on(master, puppet(\"config set disable_i18n false\"))\n  end\n\n  step \"configure server locale to #{language}\" do\n    configure_master_system_locale(language)\n  end\n\n  tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*'))\n\n  step 'install a i18ndemo module' do\n    install_i18n_demo_module(master, tmp_environment)\n  end\n\n  teardown do\n    step 'resetting the server locale' do\n      on(master, puppet(\"config set disable_i18n #{ disable_i18n_default_master }\"))\n      reset_master_system_locale\n    end\n    step 'uninstall the module' do\n      agents.each do |agent|\n        uninstall_i18n_demo_module(agent)\n      end\n      uninstall_i18n_demo_module(master)\n    end\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    type_path = agent.tmpdir('provider')\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      agent.rm_rf(type_path)\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    unresolved_server = 'puppet.unresolved.host.example.com'\n\n    step \"Run puppet apply of a module with language #{agent_language} and verify the translations using the cached catalog\" do\n      step 'verify custom fact translations' do\n        site_pp_content_1 = <<-PP\n          node default {\n            class { 'i18ndemo':\n              filename => '#{type_path}'\n            }\n          }\n        PP\n        create_sitepp(master, tmp_environment, site_pp_content_1)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(/.*\\w+-i18ndemo fact: これは\\w+-i18ndemoからのカスタムファクトからのレイズです/, result.stderr, 'missing translation for raise from ruby fact')\n        end\n        on(agent, puppet(\"agent -t --environment #{tmp_environment} --use_cached_catalog\", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(/.*\\w+-i18ndemo fact: これは\\w+-i18ndemoからのカスタムファクトからのレイズです/, result.stderr, 'missing translation for raise from ruby fact when using cached catalog')\n        end\n      end\n\n      step 'verify custom provider translation' do\n        site_pp_content_2 = <<-PP\n          node default {\n            i18ndemo_type { 'hello': \n              ensure => present, \n              dir => '#{type_path}',\n            }\n          }\n        PP\n        create_sitepp(master, tmp_environment, site_pp_content_2)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(/Warning:.*\\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, result.stderr, 'missing translated provider message')\n        end\n        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|\n          assert_match(/Warning:.*\\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, result.stderr, 'missing translated provider message when using cached catalog')\n        end\n      end\n\n      step 'verify function string translation' do\n        site_pp_content_3 = <<-PP\n          node default {\n            notify { 'happy': \n              message => happyfuntime('happy') \n            }\n          }\n        PP\n        create_sitepp(master, tmp_environment, site_pp_content_3)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(/Notice: --\\*\\w+-i18ndemo function: それは楽しい時間です\\*--/, result.stdout, 'missing translated notice message')\n        end\n        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|\n          assert_match(/Notice: --\\*\\w+-i18ndemo function: それは楽しい時間です\\*--/, result.stdout, 'missing translated notice message when using cached catalog')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_agent_with_multiple_environments.rb",
    "content": "test_name 'C100575: puppet agent with different modules in different environments should translate based on their module' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n\n  app_type_1        = File.basename(__FILE__, '.*') + \"_env_1\"\n  app_type_2        = File.basename(__FILE__, '.*') + \"_env_2\"\n  tmp_environment_1 = mk_tmp_environment_with_teardown(master, app_type_1)\n  tmp_environment_2 = mk_tmp_environment_with_teardown(master, app_type_2)\n  full_path_env_1 = File.join('/tmp', tmp_environment_1)\n  full_path_env_2 = File.join('/tmp', tmp_environment_2)\n  tmp_po_file = master.tmpfile('tmp_po_file')\n\n  disable_i18n_default_master = master.puppet['disable_i18n']\n  step 'enable i18n on master' do\n    on(master, puppet(\"config set disable_i18n false\"))\n  end\n\n  step 'install a i18ndemo module' do\n    install_i18n_demo_module(master, tmp_environment_1)\n    install_i18n_demo_module(master, tmp_environment_2)\n  end\n\n  step \"configure server locale to #{language}\" do\n    configure_master_system_locale(language)\n  end\n\n  teardown do\n    on(master, \"rm -f '#{tmp_po_file}'\")\n    step 'uninstall the module' do\n      agents.each do |agent|\n        uninstall_i18n_demo_module(agent)\n      end\n      uninstall_i18n_demo_module(master)\n    end\n    step 'resetting the server locale' do\n      on(master, puppet(\"config set disable_i18n #{ disable_i18n_default_master }\"))\n      reset_master_system_locale\n    end\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    env_1_po_file = File.join(full_path_env_1, 'modules', I18NDEMO_NAME, 'locales', 'ja', \"#{I18NDEMO_MODULE_NAME}.po\")\n    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}\")\n    env_2_po_file = File.join(full_path_env_2, 'modules', I18NDEMO_NAME, 'locales', 'ja', \"#{I18NDEMO_MODULE_NAME}.po\")\n    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}\")\n    on(master, \"chmod a+r '#{env_1_po_file}' '#{env_2_po_file}'\")\n\n    step 'verify function string translation' do\n      site_pp_content = <<-PP\n          node default {\n            notify { 'happy':\n              message => happyfuntime('happy')\n            }\n          }\n      PP\n      create_sitepp(master, tmp_environment_1, site_pp_content)\n      on(agent, puppet(\"agent -t --environment #{tmp_environment_1}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 2) do |result|\n        assert_match(/Notice: --\\*(ENV_1:)?ENV_1:\\w+-i18ndemo function: それは楽しい時間です\\*--/, result.stdout, 'missing translated notice message for environment 1')\n      end\n\n      create_sitepp(master, tmp_environment_2, site_pp_content)\n      on(agent, puppet(\"agent -t --environment #{tmp_environment_2}\", 'ENV' => shell_env_language), :acceptable_exit_codes => 2) do |result|\n        assert_match(/Notice: --\\*(ENV_2:)?ENV_2:\\w+-i18ndemo function: それは楽しい時間です\\*--/, result.stdout, 'missing translated notice message for environment 2')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_apply.rb",
    "content": "test_name 'C100567: puppet apply of module should translate messages' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    # REMIND - It was noted that skipping tests on certain platforms sometimes causes\n    # beaker to mark the test as a failed even if the test succeeds on other targets. \n    # Hence we just print a message and skip w/o telling beaker about it.\n    if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n      puts \"Module build, loading and installing is not supported on fips enabled platforms\"\n      next\n    end\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    type_path = agent.tmpdir('provider')\n    step 'install a i18ndemo module' do\n      install_i18n_demo_module(agent)\n    end\n\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      uninstall_i18n_demo_module(agent)\n      on(agent, \"rm -rf '#{type_path}'\")\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step \"Run puppet apply of a module with language #{agent_language} and verify the translations\" do\n      step 'verify custom fact translations' do\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': filename => '#{type_path}' }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/.*\\w+-i18ndemo fact: これは\\w+-i18ndemoからのカスタムファクトからのレイズです/, apply_result.stderr, 'missing translation for raise from ruby fact')\n        end\n      end unless agent['platform'] =~ /ubuntu-16.04/ # Condition to be removed after FACT-2799 gets resolved\n\n      step 'verify custom translations' do\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { 'hello': }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          assert_match(/Warning:.*\\w+-i18ndemo type: 良い値/, apply_result.stderr, 'missing warning from custom type')\n        end\n\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { '12345': }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          assert_match(/Error: .* \\w+-i18ndemo type: 値は有12345効な値ではありません/, apply_result.stderr, 'missing error from invalid value for custom type param')\n        end\n      end\n\n      step 'verify custom provider translation' do\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Warning:.*\\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, apply_result.stderr, 'missing translated provider message')\n        end\n      end\n\n      step 'verify function string translation' do\n        on(agent, puppet(\"apply -e \\\"notify { 'happy': message => happyfuntime('happy') }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Notice: --\\*\\w+-i18ndemo function: それは楽しい時間です\\*--/, apply_result.stdout, 'missing translated notice message')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_apply_module_lang.rb",
    "content": "test_name 'C100574: puppet apply using a module should translate messages in a language not supported by puppet' do\n\n  confine :except, :platform => /^windows/ # Can't print Finish on an English or Japanese code page\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language='fi_FI'\n\n  agents.each do |agent|\n    # REMIND - It was noted that skipping tests on certain platforms sometimes causes\n    # beaker to mark the test as a failed even if the test succeeds on other targets. \n    # Hence we just print a message and skip w/o telling beaker about it.\n    if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n      puts \"Module build, loading and installing is not supported on fips enabled platforms\"\n      next\n    end\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    type_path = agent.tmpdir('provider')\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      uninstall_i18n_demo_module(agent)\n      on(agent, \"rm -rf '#{type_path}'\")\n    end\n\n    step 'install a i18ndemo module' do\n      install_i18n_demo_module(agent)\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step \"Run puppet apply of a module with language #{agent_language} and verify default english returned\" do\n      step 'verify custom fact message translated and applied catalog message not translatated' do\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': filename => '#{type_path}' }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/i18ndemo_fact: tämä on korotus mukautetusta tosiasiasta \\w+-i18ndemo/,\n                       apply_result.stderr, 'missing translated message for raise from ruby fact')\n          assert_match(/Notice: Applied catalog in [0-9.]+ seconds/, apply_result.stdout, 'missing untranslated message for catalog applied')\n        end\n      end unless agent['platform'] =~ /ubuntu-16.04/ # Condition to be removed after FACT-2799 gets resolved\n\n      step 'verify warning translated from init.pp' do\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': filename => '#{type_path}' }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Warning: .*I18ndemo-tiedoston luominen/, apply_result.stderr, 'missing translated warning from init.pp')\n        end\n\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': param1 => false }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          assert_match(/Error: .* tiedostoa ei voitu luoda./, apply_result.stderr, 'missing translated message for fail from init.pp')\n        end\n      end\n\n      step 'verify custom type messages translated' do\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { 'hello': }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          assert_match(/Warning: .* Hyvä arvo i18ndemo_type::name/, apply_result.stderr, 'missing translated warning from custom type')\n        end\n\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { '12345': }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          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')\n        end\n      end\n\n      step 'verify custom provider translation' do\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Warning: .* Onko i18ndemo_type olemassa\\?/, apply_result.stderr, 'missing translated provider message')\n        end\n      end\n\n      step 'verify function string translation' do\n        on(agent, puppet(\"apply -e \\\"notify { 'happy': message => happyfuntime('happy') }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Notice: --\\*SE ON HAUSKAA AIKAA\\*--/, apply_result.stdout, 'missing translated notice message')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_apply_unsupported_lang.rb",
    "content": "test_name 'C100568: puppet apply of module for an unsupported language should fall back to english' do\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  unsupported_language='hu_HU'\n  shell_env_language = { 'LANGUAGE' => unsupported_language, 'LANG' => unsupported_language }\n\n  agents.each do |agent|\n    # REMIND - It was noted that skipping tests on certain platforms sometimes causes\n    # beaker to mark the test as a failed even if the test succeeds on other targets. \n    # Hence we just print a message and skip w/o telling beaker about it.\n    if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n      puts \"Module build, loading and installing is not supported on fips enabled platforms\"\n      next\n    end\n\n    type_path = agent.tmpdir('provider')\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      uninstall_i18n_demo_module(agent)\n      on(agent, \"rm -rf '#{type_path}'\")\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step 'install a i18ndemo module' do\n      install_i18n_demo_module(agent)\n    end\n\n    step \"Run puppet apply of a module with language #{unsupported_language} and verify default english returned\" do\n      step 'verify custom fact messages not translatated' do\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': filename => '#{type_path}' }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          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')\n        end\n      end\n\n      step 'verify warning not translated from init.pp' do\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': filename => '#{type_path}' }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Warning:.*Creating an i18ndemo file/, apply_result.stderr, 'missing untranslated warning from init.pp')\n        end\n\n        on(agent, puppet(\"apply -e \\\"class { 'i18ndemo': param1 => false }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          assert_match(/Error:.*Failed to create/, apply_result.stderr, 'missing untranslated message for fail from init.pp')\n        end\n      end\n\n      step 'verify custom type messages not translated' do\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { 'hello': }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          assert_match(/Warning:.*Good value for i18ndemo_type::name/, apply_result.stderr, 'missing untranslated warning from custom type')\n        end\n\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { '12345': }\\\"\", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result|\n          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')\n        end\n      end\n\n      step 'verify custom provider translation' do\n        on(agent, puppet(\"apply -e \\\"i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Warning:.* Does i18ndemo_type exist\\?/, apply_result.stderr, 'missing untranslated provider message')\n        end\n      end\n\n      step 'verify function string translation' do\n        on(agent, puppet(\"apply -e \\\"notify { 'happy': message => happyfuntime('happy') }\\\"\", 'ENV' => shell_env_language)) do |apply_result|\n          assert_match(/Notice: --\\*IT'S HAPPY FUN TIME\\*--/, apply_result.stdout, 'missing untranslated notice message')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_describe.rb",
    "content": "test_name 'C100576: puppet describe with module type translates message' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    # REMIND - It was noted that skipping tests on certain platforms sometimes causes\n    # beaker to mark the test as a failed even if the test succeeds on other targets. \n    # Hence we just print a message and skip w/o telling beaker about it.\n    if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n      puts \"Module build, loading and installing is not supported on fips enabled platforms\"\n      next\n    end\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      uninstall_i18n_demo_module(agent)\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step 'install a i18ndemo module' do\n      install_i18n_demo_module(agent)\n    end\n\n    step \"Run puppet describe from a module with language #{agent_language} and verify the translations\" do\n      on(agent, puppet('describe i18ndemo_type', 'ENV' => shell_env_language)) do |result|\n        assert_match(/\\w+-i18ndemo type: dirパラメータは、検査するディレクトリパスをとります/, result.stdout, 'missing translation of dir parameter from i18ndemo_type')\n        assert_match(/\\w+-i18ndemo type: nameパラメータには、大文字と小文字の変数A-Za-zが使用されます/, result.stdout, 'missing translation of name parameter from i18ndemo_type')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_face.rb",
    "content": "test_name 'C100573: puppet application/face with module translates messages' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    # REMIND - It was noted that skipping tests on certain platforms sometimes causes\n    # beaker to mark the test as a failed even if the test succeeds on other targets. \n    # Hence we just print a message and skip w/o telling beaker about it.\n    if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n      puts \"Module build, loading and installing is not supported on fips enabled platforms\"\n      next\n    end\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      uninstall_i18n_demo_module(agent)\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step 'install a i18ndemo module' do\n      install_i18n_demo_module(agent)\n    end\n\n    step \"Run puppet i18ndemo (face/application from a module with language #{agent_language} and verify the translations\" do\n      step 'puppet --help contains i18ndemo summary translation' do\n        on(agent, puppet('--help', 'ENV' => shell_env_language)) do |result|\n          assert_match(/\\s*i18ndemo\\s+\\w+-i18ndemo face: I18ndemoモジュールの人形の顔の例/, result.stdout, 'missing translation of i18ndemo help summary')\n        end\n      end\n\n      step 'puppet i18ndemo --help contains test_face summary' do\n        on(agent, puppet('i18ndemo --help', 'ENV' => shell_env_language)) do |result|\n          assert_match(/\\s*test_face\\s+\\w+-i18ndemo face: test_faceアクションのヘルプの要約/, result.stdout, 'missing translation of i18ndemo face help summary')\n        end\n      end\n\n      step 'puppet i18ndemo test_face contains translated warning' do\n        on(agent, puppet('i18ndemo', 'ENV' => shell_env_language)) do |result|\n          assert_match(/Warning: \\w+-i18ndemo face: i18ndemo test_faceが呼び出されました/, result.stderr, 'missing translation of Warning message from i18ndemo face')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/modules/puppet_resource.rb",
    "content": "test_name 'C100572: puppet resource with module translates messages' do\n  confine :except, :platform => /^solaris/ # translation not supported\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  require 'puppet/acceptance/i18ndemo_utils'\n  extend Puppet::Acceptance::I18nDemoUtils\n\n  language = 'ja_JP'\n\n  agents.each do |agent|\n    skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja'\n\n    # REMIND - It was noted that skipping tests on certain platforms sometimes causes\n    # beaker to mark the test as a failed even if the test succeeds on other targets. \n    # Hence we just print a message and skip w/o telling beaker about it.\n    if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n      puts \"Module build, loading and installing is not supported on fips enabled platforms\"\n      next\n    end\n\n    agent_language = enable_locale_language(agent, language)\n    skip_test(\"test machine is missing #{agent_language} locale. Skipping\") if agent_language.nil?\n    shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language }\n\n    disable_i18n_default_agent = agent.puppet['disable_i18n']\n    teardown do\n      on(agent, puppet(\"config set disable_i18n #{ disable_i18n_default_agent }\"))\n      uninstall_i18n_demo_module(agent)\n    end\n\n    step 'enable i18n' do\n      on(agent, puppet(\"config set disable_i18n false\"))\n    end\n\n    step 'install a i18ndemo module' do\n      install_i18n_demo_module(agent)\n    end\n\n    step \"Run puppet resource for a module with language #{agent_language} and verify the translations\" do\n      step 'puppet resource i18ndemo_type information contains translation' do\n        on(agent, puppet('resource i18ndemo_type', 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result|\n          assert_match(/Warning: Puppet::Type::I18ndemo_type::ProviderRuby: \\w+-i18ndemo type: i18ndemo_typeからの警告メッセージ/, result.stderr, 'missing translation of resource i18ndemo_type information')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/i18n/translation_fallback.rb",
    "content": "test_name 'C100560: puppet agent run output falls back to english when language not available' do\n  # No confines because even on non-translation supported OS' we should still fall back to english\n\n  tag 'audit:medium',\n      'audit:acceptance'\n\n  agents.each do |agent|\n    step 'Run Puppet apply with language Hungarian and check the output' do\n      unsupported_language='hu_HU'\n      on(agent, puppet(\"agent -t\",\n                       'ENV' => {'LANG' => unsupported_language, 'LANGUAGE' => ''})) do |apply_result|\n        assert_match(/Applying configuration version '[^']*'/, apply_result.stdout,\n                     'agent run should default to english translation')\n        assert_match(/Applied catalog in [0-9.]* seconds/, apply_result.stdout,\n                     'agent run should default to english translation')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/language/binary_data_type.rb",
    "content": "test_name 'C98346: Binary data type' do\n  require 'puppet/acceptance/puppet_type_test_tools.rb'\n  extend Puppet::Acceptance::PuppetTypeTestTools\n\n  tag 'audit:high',\n      'audit:integration', # Tests that binary data is retains integrity\n                           # between server and agent transport/application.\n                           # The weak link here is final ruby translation and\n                           # should not be OS sensitive.\n      'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_type               = File.basename(__FILE__, '.*')\n  tmp_environment        = mk_tmp_environment_with_teardown(master, app_type)\n\n  tmp_filename_win = tmp_filename_else = ''\n  agents.each do |agent|\n    # ugh... this won't work with more than two agents of two types\n    if agent.platform =~ /32$/\n      tmp_filename_win = \"C:\\\\cygwin\\\\tmp\\\\#{tmp_environment}.txt\"\n    else\n      tmp_filename_win = \"C:\\\\cygwin64\\\\tmp\\\\#{tmp_environment}.txt\"\n    end\n    tmp_filename_else = \"/tmp/#{tmp_environment}.txt\"\n    on(agent, \"echo 'old content' > '/tmp/#{tmp_environment}.txt'\")\n  end\n  # create a fake module files... file for binary_file()\n  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\\\"}'\"))\n\n  base64_relaxed = Base64.encode64(\"invasionfromspace#{random_string}\").strip\n  base64_strict  = Base64.strict_encode64(\"invasion from space #{random_string}\\n\")\n  base64_urlsafe = Base64.urlsafe_encode64(\"invasion from-space/#{random_string}\\n\")\n\n  test_resources = [\n      { :type       => 'notify', :parameters => { :namevar => \"1:$hell\" }, :pre_code => \"$hell = Binary('hello','%b')\",\n        :assertions => { :assert_match => 'Notice: 1:hell' } },\n      { :type       => 'notify', :parameters => { :namevar => \"2:$relaxed\" }, :pre_code => \"$relaxed = Binary('#{base64_relaxed}')\",\n        :assertions => { :assert_match => \"Notice: 2:#{base64_relaxed}\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"3:$cHVwcGV0\" }, :pre_code => \"$cHVwcGV0 = Binary('cHVwcGV0')\",\n        :assertions => { :assert_match => 'Notice: 3:cHVwcGV0' } },\n      { :type       => 'notify', :parameters => { :namevar => \"4:$strict\" }, :pre_code => \"$strict = Binary('#{base64_strict}')\",\n        :assertions => { :assert_match => \"Notice: 4:#{base64_strict}\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"5:$urlsafe\" }, :pre_code => \"$urlsafe = Binary('#{base64_urlsafe}')\",\n        :assertions => { :assert_match => \"Notice: 5:#{base64_urlsafe}\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"6:$byte_array\" }, :pre_code => \"$byte_array = Binary([67,68])\",\n        :assertions => { :assert_match => \"Notice: 6:Q0Q=\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"7:${empty_array}empty\" }, :pre_code => \"$empty_array = Binary([])\",\n        :assertions => { :assert_match => \"Notice: 7:empty\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"8:${relaxed[1]}\" },\n        :assertions => { :assert_match => \"Notice: 8:bg==\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"9:${relaxed[1,3]}\" },\n        :assertions => { :assert_match => \"Notice: 9:bnZh\" } },\n      { :type       => 'notify', :parameters => { :namevar => \"A:${utf8}\" }, :pre_code => '$utf8=String(Binary([0xF0, 0x9F, 0x91, 0x92]),\"%s\")',\n        :assertions => { :assert_match => 'Notice: A:\\\\xF0\\\\x9F\\\\x91\\\\x92' } },\n      { :type       => 'notify', :parameters => { :namevar => \"B:${type($bin_file)}\" }, :pre_code => '$bin_file=binary_file(\"empty/blah.txt\")',\n        :assertions => { :assert_match => 'Notice: B:Binary' } },\n      { :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}' }\",\n        :assertions => { :assert_match => /#{base64_relaxed}/ } },\n  ]\n\n  sitepp_content = generate_manifest(test_resources)\n  assertion_code = generate_assertions(test_resources)\n\n  create_sitepp(master, tmp_environment, sitepp_content)\n\n  step \"run agent in #{tmp_environment}, run all assertions\" do\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n        on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => [2]) do |result|\n          run_assertions(assertion_code, result)\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/language/exported_resources.rb",
    "content": "test_name \"C94788: exported resources using a yaml terminus for storeconfigs\" do\nrequire 'puppet/acceptance/environment_utils'\nextend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',     # This could be a component of a larger workflow scenario.\n    'server'\n\n  skip_test 'requires puppetserver to service restart' if @options[:type] != 'aio'\n\n  app_type = File.basename(__FILE__, '.*')\n  tmp_environment   = mk_tmp_environment_with_teardown(master, app_type)\n  exported_username = 'er0ck'\n\n  teardown do\n    step 'stop puppet server' do\n      on(master, \"service #{master['puppetservice']} stop\")\n    end\n    step 'remove cached agent json catalogs from the master' do\n      on(master, \"rm -f #{File.join(master.puppet['yamldir'],'catalog','*')}\",\n         :accept_all_exit_codes => true)\n    end\n    on(master, \"mv #{File.join('','tmp','puppet.conf')} #{master.puppet['confdir']}\",\n       :accept_all_exit_codes => true)\n    step 'clean out collected resources' do\n      on(hosts, puppet_resource(\"user #{exported_username} ensure=absent\"), :accept_all_exit_codes => true)\n    end\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  storeconfigs_backend_name = 'json_storeconfigs'\n  step 'create a yaml storeconfigs terminus in the modulepath' do\n    moduledir = File.join(environmentpath,tmp_environment,'modules')\n    terminus_class_name = 'JsonStoreconfigs'\n    manifest = <<MANIFEST\nFile {\n  ensure => directory,\n}\nfile {\n  '#{moduledir}':;\n  '#{moduledir}/yaml_terminus':;\n  '#{moduledir}/yaml_terminus/lib':;\n  '#{moduledir}/yaml_terminus/lib/puppet':;\n  '#{moduledir}/yaml_terminus/lib/puppet/indirector':;\n  '#{moduledir}/yaml_terminus/lib/puppet/indirector/catalog':;\n  '#{moduledir}/yaml_terminus/lib/puppet/indirector/facts':;\n  '#{moduledir}/yaml_terminus/lib/puppet/indirector/node':;\n  '#{moduledir}/yaml_terminus/lib/puppet/indirector/resource':;\n}\nfile { '#{moduledir}/yaml_terminus/lib/puppet/indirector/catalog/#{storeconfigs_backend_name}.rb':\n  ensure => file,\n  content => '\n    require \"puppet/indirector/catalog/yaml\"\n    class Puppet::Resource::Catalog::#{terminus_class_name} < Puppet::Resource::Catalog::Yaml\n      def save(request)\n        raise ArgumentError.new(\"You can only save objects that respond to :name\") unless request.instance.respond_to?(:name)\n        file = path(request.key)\n        basedir = File.dirname(file)\n        # This is quite likely a bad idea, since we are not managing ownership or modes.\n        Dir.mkdir(basedir) unless Puppet::FileSystem.exist?(basedir)\n        begin\n          # We cannot dump anonymous modules in yaml, so dump to json\n          File.open(file, \"w\") { |f| f.write request.instance.to_json }\n        rescue TypeError => detail\n          Puppet.err \"Could not save \\#{self.name} \\#{request.key}: \\#{detail}\"\n        end\n      end\n      def find(request)\n        nil\n      end\n    end\n  ',\n}\nfile { '#{moduledir}/yaml_terminus/lib/puppet/indirector/facts/#{storeconfigs_backend_name}.rb':\n  ensure => file,\n  content => '\n    require \"puppet/indirector/facts/yaml\"\n    class Puppet::Node::Facts::#{terminus_class_name} < Puppet::Node::Facts::Yaml\n      def find(request)\n        nil\n      end\n    end\n  ',\n}\nfile { '#{moduledir}/yaml_terminus/lib/puppet/indirector/node/#{storeconfigs_backend_name}.rb':\n  ensure => file,\n  content => '\n    require \"puppet/indirector/node/yaml\"\n    class Puppet::Node::#{terminus_class_name} < Puppet::Node::Yaml\n      def find(request)\n        nil\n      end\n    end\n  ',\n}\nfile { '#{moduledir}/yaml_terminus/lib/puppet/indirector/resource/#{storeconfigs_backend_name}.rb':\n  ensure => file,\n  content => '\n    require \"puppet/indirector/yaml\"\n    require \"puppet/resource/catalog\"\n    class Puppet::Resource::#{terminus_class_name} < Puppet::Indirector::Yaml\n      desc \"Read resource instances from cached catalogs\"\n      def search(request)\n        catalog_dir = File.join(Puppet.run_mode.server? ? Puppet[:yamldir] : Puppet[:clientyamldir], \"catalog\", \"*\")\n        results = Dir.glob(catalog_dir).collect { |file|\n          catalog = Puppet::Resource::Catalog.convert_from(:json, File.read(file))\n          if catalog.name == request.options[:host]\n            next\n          end\n          catalog.resources.select { |resource|\n            resource.type == request.key && resource.exported\n          }.map! { |res|\n            data_hash = res.to_data_hash\n            parameters = data_hash[\"parameters\"].map do |name, value|\n              Puppet::Parser::Resource::Param.new(:name => name, :value => value)\n            end\n            attrs = {:parameters => parameters, :scope => request.options[:scope]}\n            result = Puppet::Parser::Resource.new(res.type, res.title, attrs)\n            result.collector_id = \"\\#{catalog.name}|\\#{res.type}|\\#{res.title}\"\n            result\n          }\n        }.flatten.compact\n        results\n      end\n    end\n  ',\n}\n# all the filtering is taken care of in the terminii\n#   so any tests on filtering belong with puppetdb or pe\nfile { '#{environmentpath}/#{tmp_environment}/manifests/site.pp':\n  ensure => file,\n  content => '\n    node \"#{master.hostname}\" {\n      @@user{\"#{exported_username}\": ensure => present,}\n    }\n    node \"default\" {\n      # collect resources on all nodes (puppet prevents collection on same node)\n      User<<| |>>\n    }\n  ',\n}\nMANIFEST\n    apply_manifest_on(master, manifest, :catch_failures => true)\n  end\n\n  # must specify environment in puppet.conf for it to pickup the terminus code in an environment module\n  #   but we have to bounce the server to pickup the storeconfigs... config anyway\n  # we can't use with_puppet_running_on here because it uses puppet resource to bounce the server\n  #   puppet resource tries to use yaml_storeconfig's path() which doesn't exist\n  #   and fails back to yaml which indicates an attempted directory traversal and fails.\n  #   we could implemnt path() properly, but i'm just going to start the server the old fashioned way\n  #  and... config set is broken and doesn't add a main section\n  step 'turn on storeconfigs, start puppetserver the old fashioned way' do\n    on(master, \"cp #{File.join(master.puppet['confdir'],'puppet.conf')} #{File.join('','tmp')}\")\n    on(master, \"echo [main] >> #{File.join(master.puppet['confdir'],'puppet.conf')}\")\n    on(master, \"echo environment=#{tmp_environment} >> #{File.join(master.puppet['confdir'],'puppet.conf')}\")\n    on(master, puppet('config set storeconfigs true --section main'))\n    on(master, puppet(\"config set storeconfigs_backend #{storeconfigs_backend_name} --section main\"))\n    on(master, \"service #{master['puppetservice']} restart\")\n    step 'run the master agent to export the resources' do\n      on(master, puppet(\"agent -t --environment #{tmp_environment}\"))\n    end\n    agents.each do |agent|\n      next if agent == master\n      step 'run the agents to collect exported resources' do\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\"),\n           :acceptable_exit_codes => 2)\n        on(agent, puppet_resource(\"user #{exported_username}\"), :accept_all_exit_codes => true) do |result|\n          assert_match(/present/, result.stdout, 'collected resource not found')\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/language/functions_in_puppet_language.rb",
    "content": "test_name 'Puppet executes functions written in the Puppet language'\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',     # use mk_tmp_environment_with_teardown helper for environment construction\n    'server'\n\nteardown do\n  on master, 'rm -rf /etc/puppetlabs/code/modules/jenny'\n  on master, 'rm -rf /etc/puppetlabs/code/environments/tommy'\n  on master, 'rm -rf /etc/puppetlabs/code/environments/production/modules/one'\n  on master, 'rm -rf /etc/puppetlabs/code/environments/production/modules/three'\n\n  agents.each do |agent|\n    on(agent, puppet('config print lastrunfile')) do |command_result|\n      agent.rm_rf(command_result.stdout)\n    end\n  end\nend\n\nstep 'Create some functions' do\n\n  manifest = <<-EOF\n    File {\n      ensure => 'present',\n      owner => 'root',\n      group => 'root',\n      mode => '0644',\n    }\n\n    file {['/etc/puppetlabs/',\n      '/etc/puppetlabs/code/',\n      '/etc/puppetlabs/code/modules/',\n      '/etc/puppetlabs/code/modules/jenny',\n      '/etc/puppetlabs/code/modules/jenny/functions',\n      '/etc/puppetlabs/code/modules/jenny/functions/nested',\n      '/etc/puppetlabs/code/environments',\n      '/etc/puppetlabs/code/environments/production',\n      '/etc/puppetlabs/code/environments/production/modules',\n      '/etc/puppetlabs/code/environments/production/modules/one',\n      '/etc/puppetlabs/code/environments/production/modules/one/functions',\n      '/etc/puppetlabs/code/environments/production/modules/one/manifests',\n      '/etc/puppetlabs/code/environments/production/modules/three',\n      '/etc/puppetlabs/code/environments/production/modules/three/functions',\n      '/etc/puppetlabs/code/environments/production/modules/three/manifests',\n      '/etc/puppetlabs/code/environments/tommy',\n      '/etc/puppetlabs/code/environments/tommy/modules',\n      '/etc/puppetlabs/code/environments/tommy/modules/two',\n      '/etc/puppetlabs/code/environments/tommy/modules/two/functions',\n      ]:\n      ensure => directory,\n      mode => '0755',\n    }\n\n    # \"Global\" functions, no env\n    file { '/etc/puppetlabs/code/modules/jenny/functions/mini.pp':\n      content => 'function jenny::mini($a, $b) {if $a <= $b {$a} else {$b}}',\n      require => File['/etc/puppetlabs/code/modules/jenny/functions'],\n    }\n    file { '/etc/puppetlabs/code/modules/jenny/functions/nested/maxi.pp':\n      content => 'function jenny::nested::maxi($a, $b) {if $a >= $b {$a} else {$b}}',\n      require => File['/etc/puppetlabs/code/modules/jenny/functions/nested'],\n    }\n\n    # Module \"one\", \"production\" env\n    file { '/etc/puppetlabs/code/environments/production/modules/one/functions/foo.pp':\n      content => 'function one::foo() {\"This is the one::foo() function in the production environment\"}',\n      require => File['/etc/puppetlabs/code/environments/production/modules/one/functions'],\n    }\n    file { '/etc/puppetlabs/code/environments/production/modules/one/manifests/init.pp':\n      content => 'class one { }',\n      require => File['/etc/puppetlabs/code/environments/production/modules/one/manifests'],\n    }\n\n    # Module \"three\", \"production\" env\n    file { '/etc/puppetlabs/code/environments/production/modules/three/functions/baz.pp':\n      content => 'function three::baz() {\"This is the three::baz() function in the production environment\"}',\n      require => File['/etc/puppetlabs/code/environments/production/modules/three/functions'],\n    }\n    file { '/etc/puppetlabs/code/environments/production/modules/three/manifests/init.pp':\n      content => 'class three { }',\n      require => File['/etc/puppetlabs/code/environments/production/modules/three/functions'],\n    }\n\n    # Module \"two\", \"tommy\" env\n    file { '/etc/puppetlabs/code/environments/tommy/modules/two/functions/bar.pp':\n      content => 'function two::bar() {\"This is the two::bar() function in the tommy environment\"}',\n      require => File['/etc/puppetlabs/code/environments/tommy/modules/two/functions'],\n    }\n    EOF\n  apply_manifest_on(master, manifest, {:catch_failures => true, :acceptable_exit_codes => [0,1]})\nend\n\nmanifest = <<-MANIFEST\n  notice 'jenny::mini(1, 2) =', jenny::mini(1,2)\n  notice 'jenny::nested::maxi(1, 2) =', jenny::nested::maxi(1,2)\n  notice 'one::foo() =', one::foo()\n  require 'one'; notice 'three::baz() =', three::baz()\nMANIFEST\n\nrc = apply_manifest_on(master, manifest, {:accept_all_exit_codes => true,})\n\nstep 'Call a global function' do\n  fail_test 'Failed to call a \"global\" function' \\\n    unless  rc.stdout.include?('jenny::mini(1, 2) = 1')\n  end\n\nstep 'Call a global nested function' do\n  fail_test 'Failed to call a \"global\" nested function' \\\n    unless rc.stdout.include?('jenny::nested::maxi(1, 2) = 2')\n  end\n\nstep 'Call an env-specific function' do\n  fail_test 'Failed to call a function defined in the current environment' \\\n    unless rc.stdout.include?('This is the one::foo() function in the production environment')\n  end\n\nstep 'Call a function defined in an un-included module' do\n  fail_test 'Failed to call a function defined in an un-required module' \\\n    unless rc.stdout.include?('This is the three::baz() function in the production environment')\nend\n\nmanifest = <<-MANIFEST.strip\n  notice \"two::bar() =\", two::bar()\nMANIFEST\n\n# This should fail\nstep 'Call a function not defined in the current environment' do\n  rc = on master, puppet(\"apply -e '#{manifest}' --environment production\"), {:accept_all_exit_codes => true,}\n  fail_test 'Should not be able to call a function not defined in the current environment' \\\n    unless rc.stderr.include?(\"Error: Evaluation Error: Unknown function: 'two::bar'\")\nend\n\nstep 'Call an env-specific function in a non-default environment' do\n  rc = on master, puppet(\"apply -e '#{manifest}' --environment tommy\")\n  fail_test 'Failed to call env-specific function from that environment' \\\n    unless rc.stdout.include?('This is the two::bar() function in the tommy environment')\nend\n\n"
  },
  {
    "path": "acceptance/tests/language/objects_in_catalog.rb",
    "content": "test_name 'C99627: can use Object types in the catalog and apply/agent' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor'     # The use of apply on a reference system should\n                         # be adequate to test puppet. Running this in\n                         # context of server/agent should not be necessary.\n\n  manifest = <<-PP\ntype Mod::Foo = Object[{\n  attributes => {\n    'name' => String,\n    'size' => Integer[0, default]\n  }\n}]\ndefine mod::foo_notifier(Mod::Foo $foo) {\n   notify { $foo.name: }\n}\nclass mod {\n  mod::foo_notifier { xyz:\n    foo => Mod::Foo('A foo', 42)\n  }\n}\ninclude mod\n  PP\n\n  agents.each do |agent|\n    # This is currently only expected to work with apply as the custom data type\n    # definition will not be present on the agent to deserialize properly\n\n    step \"apply manifest on agent #{agent.hostname} and assert notify output\" do\n      apply_manifest_on(agent, manifest) do |result|\n        assert(result.exit_code == 0, \"agent didn't exit properly: (#{result.exit_code})\")\n        assert_match(/A foo/, result.stdout, 'agent didn\\'t notify correctly')\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/language/pcore_generate_env_isolation.rb",
    "content": "test_name 'C98345: ensure puppet generate assures env. isolation' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  tmp_environment2 = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n  fq_tmp_environmentpath2  = \"#{environmentpath}/#{tmp_environment2}\"\n\n  type_name = 'conflicting'\n  relative_type_dir  = 'modules/conflict/lib/puppet/type'\n  relative_type_path = \"#{relative_type_dir}/#{type_name}.rb\"\n  step 'create custom type in two environments' do\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/#{relative_type_dir}\")\n    on(master, \"mkdir -p #{fq_tmp_environmentpath2}/#{relative_type_dir}\")\n\n    custom_type1 = <<-END\n    Puppet::Type.newtype(:#{type_name}) do\n      newparam :name, :namevar => true\n    END\n    custom_type2 =  \"#{custom_type1}\"\n    custom_type2 << \"      newparam :other\\n\"\n    custom_type1 << \"    end\\n\"\n    custom_type2 << \"    end\\n\"\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/#{relative_type_path}\",  custom_type1)\n    create_remote_file(master, \"#{fq_tmp_environmentpath2}/#{relative_type_path}\", custom_type2)\n\n    site_pp1 = <<-PP\n    notify{$environment:}\n    #{type_name}{\"somename\":}\n    PP\n    site_pp2 = <<-PP\n    notify{$environment:}\n    #{type_name}{\"somename\": other => \"uhoh\"}\n    PP\n    create_sitepp(master, tmp_environment,  site_pp1)\n    create_sitepp(master, tmp_environment2, site_pp2)\n  end\n\n  on master, \"chmod -R 755 /tmp/#{tmp_environment}\"\n  on master, \"chmod -R 755 /tmp/#{tmp_environment2}\"\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      on(agent, puppet(\"agent -t --environment #{tmp_environment}\"),\n         :acceptable_exit_codes => 2)\n      step 'run agent in environment with type with an extra parameter. try to use this parameter' do\n        on(agent, puppet(\"agent -t --environment #{tmp_environment2}\"),\n           :accept_all_exit_codes => true) do |result|\n          unless agent['locale'] == 'ja'\n            assert_match(\"Error: no parameter named 'other'\", result.output,\n                         'did not produce environment isolation issue as expected')\n          end\n        end\n      end\n    end\n\n    step 'generate pcore files' do\n      on(master, puppet(\"generate types --environment #{tmp_environment}\"))\n      on(master, puppet(\"generate types --environment #{tmp_environment2}\"))\n    end\n\n    agents.each do |agent|\n      step 'rerun agents after generate, ensure proper runs' do\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\"),\n           :acceptable_exit_codes => 2)\n        on(agent, puppet(\"agent -t --environment #{tmp_environment2}\"),\n           :acceptable_exit_codes => 2)\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/language/pcore_resource_types_should_have_precedence_over_ruby.rb",
    "content": "test_name 'C98097 - generated pcore resource types should be loaded instead of ruby for custom types' do\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',    # use `mk_tmp_environment_with_teardown` helper to build environment\n    'server'\n\n  environment = 'production'\n  step 'setup - install module with custom ruby resource type' do\n    #{{{\n    testdir = master.tmpdir('c98097')\n    codedir = \"#{testdir}/codedir\"\n\n    site_manifest_content =<<EOM\nnode default {\n  notice(mycustomtype{\"foobar\":})\n}\nEOM\n\n    custom_type_content =<<EOM\nPuppet::Type.newtype(:mycustomtype) do\n  @doc = \"Create a new mycustomtype thing.\"\n\n  newparam(:name, :namevar => true) do\n    desc \"Name of mycustomtype instance\"\n    $stderr.puts \"this indicates that we are running ruby code and should not be seen when running generated pcore resource\"\n  end\n\n  def refresh\n  end\n\nend\nEOM\n\n    apply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode   => \"0755\",\n}\n\nfile {[\n  '#{codedir}',\n  '#{codedir}/environments',\n  '#{codedir}/environments/#{environment}',\n  '#{codedir}/environments/#{environment}/manifests',\n  '#{codedir}/environments/#{environment}/modules',\n  '#{codedir}/environments/#{environment}/modules/mymodule',\n  '#{codedir}/environments/#{environment}/modules/mymodule/manifests',\n  '#{codedir}/environments/#{environment}/modules/mymodule/lib',\n  '#{codedir}/environments/#{environment}/modules/mymodule/lib/puppet',\n  '#{codedir}/environments/#{environment}/modules/mymodule/lib/puppet/type'\n  ]:\n}\n\nfile { '#{codedir}/environments/#{environment}/manifests/site.pp':\n  ensure => file,\n  content => '#{site_manifest_content}',\n}\n\nfile { '#{codedir}/environments/#{environment}/modules/mymodule/lib/puppet/type/mycustomtype.rb':\n  ensure => file,\n  content => '#{custom_type_content}',\n}\nMANIFEST\n\n    conf_opts = {\n      'main' => {\n        'environmentpath' => \"#{codedir}/environments\"\n      }\n    }\n\n    backup_file = backup_the_file(master, puppet_config(master, 'confdir', section: 'master'), testdir, 'puppet.conf')\n    lay_down_new_puppet_conf master, conf_opts, testdir\n\n    teardown do\n      restore_puppet_conf_from_backup( master, backup_file )\n      # See PUP-6995\n      on(master, \"rm -f #{puppet_config(master, 'yamldir', section: 'master')}/node/*.yaml\")\n\n      agents.each do |agent|\n        on(agent, puppet('config print lastrunfile')) do |command_result|\n          agent.rm_rf(command_result.stdout)\n        end\n      end\n    end\n    #}}}\n\n    catalog_results = {}\n    catalog_results[master.hostname] = { 'ruby_cat' => '', 'pcore_cat' => '' }\n\n    step 'compile catalog using ruby resource' do\n      on master, puppet('catalog', 'find', master.hostname) do |result|\n        assert_match(/running ruby code/, result.stderr)\n        catalog_results[master.hostname]['ruby_cat'] = JSON.parse(result.stdout.sub(/^[^{]+/,''))\n      end\n    end\n\n    step 'generate pcore type from ruby type' do\n      on master, puppet('generate', 'types', '--environment', environment)\n    end\n\n    step 'compile catalog and make sure that ruby code is NOT executed' do\n      on master, puppet('catalog', 'find', master.hostname) do |result|\n        refute_match(/running ruby code/, result.stderr)\n        catalog_results[master.hostname]['pcore_cat'] = JSON.parse(result.stdout.sub(/^[^{]+/,''))\n      end\n    end\n\n    step 'ensure that the resources created in the catalog using ruby and pcore are the same' do\n      assert_equal(catalog_results[master.hostname]['ruby_cat']['resources'], catalog_results[master.hostname]['pcore_cat']['resources'])\n    end\n\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/language/resource_refs_with_nested_arrays.rb",
    "content": "test_name \"#7681: Allow using array variables in resource references\"\n\ntag 'audit:high',\n    'audit:unit'\n\nagents.each do |agent|\n  test_manifest = <<MANIFEST\n$exec_names = [\"first\", \"second\"]\nexec { \"first\":\n  command => \"#{agent.echo('the first command')}\",\n  path => \"#{agent.path}\",\n  logoutput => true,\n}\nexec { \"second\":\n  command => \"#{agent.echo('the second command')}\",\n  path => \"#{agent.path}\",\n  logoutput => true,\n}\nexec { \"third\":\n  command => \"#{agent.echo('the final command')}\",\n  path => \"#{agent.path}\",\n  logoutput => true,\n  require => Exec[$exec_names],\n}\nMANIFEST\n\n  apply_manifest_on(agent, test_manifest) do |result|\n    assert_match(/Exec\\[third\\].*the final command/, result.stdout)\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/language/sensitive_data_type.rb",
    "content": "test_name 'C98120, C98077: Sensitive Data is redacted on CLI, logs, reports' do\n  require 'puppet/acceptance/puppet_type_test_tools.rb'\n  extend Puppet::Acceptance::PuppetTypeTestTools\n\ntag 'audit:high',\n    'audit:acceptance',   # Tests that sensitive data is retains integrity\n                          # between server and agent transport/application.\n                          # Leaving at acceptance layer due to validate\n                          # written logs.\n    'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n\n  tmp_filename_win = tmp_filename_else = ''\n  agents.each do |agent|\n    # ugh... this won't work with more than two agents of two types\n    if agent.platform =~ /32$/\n      tmp_filename_win  = \"C:\\\\cygwin\\\\tmp\\\\#{tmp_environment}.txt\"\n    else\n      tmp_filename_win  = \"C:\\\\cygwin64\\\\tmp\\\\#{tmp_environment}.txt\"\n    end\n    tmp_filename_else = \"/tmp/#{tmp_environment}.txt\"\n    on agent, \"echo 'old content' > /tmp/#{tmp_environment}.txt\"\n  end\n\n  # first attempts at a reasonable table driven test.  needs API work\n  # FIXME:\n  #   expand this to other resource types, make parameters arbitrary, make assertions arbitrary\n  # FIXME: add context messaging to each instance\n  notify_redacted = 'Sensitive \\[value redacted\\]'\n  file_redacted   = 'changed \\[redacted\\] to \\[redacted\\]'\n  test_resources = [\n    {:type => 'notify', :parameters => {:namevar => \"1:${Sensitive.new('sekrit1')}\"},\n       :assertions => [{:refute_match => 'sekrit1'}, {:assert_match => \"1:#{notify_redacted}\"}]},\n    {:type => 'notify', :parameters => {:namevar => \"2:${Sensitive.new($meh2)}\"}, :pre_code => '$meh2=\"sekrit2\"',\n       :assertions => [{:refute_match => 'sekrit2'}, {:assert_match => \"2:#{notify_redacted}\"}]},\n    {:type => 'notify', :parameters => {:namevar => \"3:meh\", :message => '\"3:${Sensitive.new(\\'sekrit3\\')}\"'},\n       :assertions => [{:refute_match => 'sekrit3'}, {:assert_match => \"3:#{notify_redacted}\"}]},\n    {:type => 'notify', :parameters => {:namevar => \"4:meh\", :message => \"Sensitive.new($meh4)\"}, :pre_code => '$meh4=\"sekrit4\"',\n       :assertions => [{:refute_match => 'sekrit4'}, {:assert_match => file_redacted}]},\n    {:type => 'notify', :parameters => {:namevar => \"5:meh\", :message => \"$meh5\"}, :pre_code => '$meh5=Sensitive.new(\"sekrit5\")',\n      :assertions => [{:refute_match => 'sekrit5'}, {:assert_match => file_redacted}]},\n    {:type => 'notify', :parameters => {:namevar => \"6:meh\", :message => '\"6:${meh6}\"'}, :pre_code => '$meh6=Sensitive.new(\"sekrit6\")',\n       :assertions => [{:refute_match => 'sekrit6'}, {:assert_match => \"6:#{notify_redacted}\"}]},\n    {:type => 'notify', :parameters => {:namevar => \"7:${Sensitive('sekrit7')}\"},\n       :assertions => [{:refute_match => 'sekrit7'}, {:assert_match => \"7:#{notify_redacted}\"}]},\n    # unwrap(), these should be en-clair\n    {:type => 'notify', :parameters => {:namevar => \"8:${unwrap(Sensitive.new('sekrit8'))}\"},\n       :assertions => {:assert_match => \"8:sekrit8\"}},\n    {:type => 'notify', :parameters => {:namevar => \"9:meh\", :message => '\"9:${unwrap(Sensitive.new(\\'sekrit9\\'))}\"'},\n       :assertions => {:assert_match => \"9:sekrit9\"}},\n    {:type => 'notify', :parameters => {:namevar => \"A:meh\", :message => '\"A:${unwrap($mehA)}\"'}, :pre_code => '$mehA=Sensitive.new(\"sekritA\")',\n       :assertions => {:assert_match => \"A:sekritA\"}},\n    {:type => 'notify', :parameters => {:namevar => \"B:meh\", :message => '\"B:${$mehB.unwrap}\"'}, :pre_code => '$mehB=Sensitive.new(\"sekritB\")',\n       :assertions => {:assert_match => \"B:sekritB\"}},\n    {:type => 'notify', :parameters => {:namevar => \"C:meh\", :message => '\"C:${$mehC.unwrap |$unwrapped| { \"blk_${unwrapped}_blk\" } } nonblk_${mehC}_nonblk\"'}, :pre_code => '$mehC=Sensitive.new(\"sekritC\")',\n       :assertions => {:assert_match => [\"C:blk_sekritC_blk\", \"nonblk_#{notify_redacted}_nonblk\"]}},\n    # for --show_diff\n    {: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}' }\",\n       :assertions => [{:refute_match => 'sekritD'}, {:assert_match => /#{tmp_environment}\\.txt..content. #{file_redacted}/}]},\n\n  ]\n\n  sitepp_content = generate_manifest(test_resources)\n  assertion_code = generate_assertions(test_resources)\n\n  # Make a copy of the full set of 'test_resources' but filtered down to include\n  # only the assertions of type ':refute_match'.  So for example, where the\n  # 'test_resources' array might have an entry like this...\n  #\n  #  {:type => 'notify', ...\n  #   :assertions => [{:refute_match => 'sekrit1'},\n  #                   {:assert_match => \"1:#{notify_redacted}\"}]}\n  #\n  # ... the ':assert_match' entry would be filtered out in the new\n  # 'refutation_resources' array, producing:\n  #\n  #  {:type => 'notify', ...\n  #   :assertions => [{:refute_match => 'sekrit1'}]}\n  #\n  # This is done so that when validating the log output, we can refute the\n  # existence of any of the sensitive info in the log without having to\n  # assert that redacted info is in the log.  The redacted info appears in\n  # the console output from the Puppet agent run - by virtue of including a\n  # '--debug' flag on the agent command line - whereas the redacted info is not\n  # expected to be piped into the log.\n\n  refutation_resources = test_resources.collect do |assertion_group|\n    refutation_group = assertion_group.clone\n    refutation_group[:assertions] = assertion_group[:assertions].select do |assertion|\n      assertion.has_key?(:refute_match)\n    end\n    refutation_group\n  end\n  refutation_code = generate_assertions(refutation_resources)\n\n  create_sitepp(master, tmp_environment, sitepp_content)\n\n  step \"run agent in #{tmp_environment}, run all assertions\" do\n    with_puppet_running_on(master,{}) do\n      agents.each do |agent|\n        # redirect logging to a temp location to avoid platform specific syslogs\n        on(agent, puppet(\"agent -t --debug --trace --show_diff --environment #{tmp_environment}\"), :accept_all_exit_codes => true) do |result|\n          assert_equal(result.exit_code, 2,'puppet agent run failed')\n\n          run_assertions(assertion_code, result) unless agent['locale'] == 'ja'\n\n          step \"assert no redacted data in log\" do\n            run_assertions(refutation_code, result)\n          end\n        end\n\n        # don't do this before the agent log scanning, above. it will skew the results\n        step \"assert no redacted data in vardir\" do\n          # no recursive grep in solaris :facepalm:\n          on(agent, \"find #{agent.puppet['vardir']} -type f | xargs grep sekrit\", :accept_all_exit_codes => true) do |result|\n            refute_match(/sekrit(1|2|3|6|7)/, result.stdout, 'found redacted data we should not have')\n            #TODO: if/when this is fixed, we should just be able to eval(assertion_code_ in this result block also!\n            expect_failure 'file resource contents will end up in the cached catalog en-clair' do\n              refute_match(/sekritD/, result.stdout, 'found redacted file data we should not have')\n            end\n          end\n        end\n\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/language/server_set_facts.rb",
    "content": "test_name 'C64667: ensure server_facts is set and error if any value is overwritten by an agent' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:acceptance', # Validating server/client interaction\n    'server'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n\n  step 'ensure $server_facts exist' do\n    create_sitepp(master, tmp_environment, <<-SITE)\n      notify{\"abc$server_facts\":}\n    SITE\n\n    master_opts = {}\n    with_puppet_running_on(master, master_opts) do\n      agents.each do |agent|\n        on(agent, puppet(\"agent -t --environment #{tmp_environment}\"),\n           :acceptable_exit_codes => 2) do |result|\n          assert_match(/abc{serverversion/, result.stdout,\n                       \"#{agent}: $server_facts should have some stuff\" )\n        end\n      end\n    end\n  end\n\n  step 'ensure puppet issues a warning if an agent overwrites a server fact' do\n    agents.each do |agent|\n      on(agent, puppet(\"agent -t\",\n                       'ENV' => { 'FACTER_server_facts' => 'overwrite' }),\n        :acceptable_exit_codes => 1) do |result|\n          # Do not perform this check on non-English hosts\n          unless agent['locale'] == 'ja'\n            assert_match(/Error.*Attempt to assign to a reserved variable name: 'server_facts'/,\n                         result.stderr, \"#{agent}: $server_facts should error if overwritten\" )\n          end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/loader/autoload_from_resource_type_decl.rb",
    "content": "test_name 'C100303: Resource type statement triggered auto-loading works both with and without generated types' do\n  tag 'risk:high'\n\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  # create the file and make sure its empty and accessible by everyone\n  def empty_execution_log_file(host, path)\n    create_remote_file(host, path, '')\n    on(host, \"chmod 777 '#{path}'\")\n  end\n\n  app_type               = File.basename(__FILE__, '.*')\n  tmp_environment        = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath = \"#{environmentpath}/#{tmp_environment}\"\n\n  relative_type_dir  = 'modules/one/lib/puppet/type'\n  relative_type_path = \"#{relative_type_dir}/type_tst.rb\"\n\n  execution_log = {}\n  execution_log[agent_to_fqdn(master)] = master.tmpfile('master_autoload_resource')\n  agents.each do |agent|\n    execution_log[agent_to_fqdn(agent)] = agent.tmpfile('agent_autoload_resource')\n  end\n\n  teardown do\n    on(master, \"rm -f '#{execution_log[agent_to_fqdn(master)]}'\")\n    agents.each do |agent|\n      on(agent, \"rm -f '#{execution_log[agent_to_fqdn(agent)]}'\")\n\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step 'create custom type' do\n    on(master, \"mkdir -p '#{fq_tmp_environmentpath}/#{relative_type_dir}'\")\n\n    # create a custom type that will write out to a different file on each agent\n    # this way we can verify whether the newtype code was executed on each system\n    custom_type = <<-END\n    Puppet::Type.newtype(:type_tst) do\n      newparam(:name, :namevar => true) do\n        fqdn = Facter.value('networking.fqdn')\n        if fqdn == '#{agent_to_fqdn(master)}'\n          File.open(\"#{execution_log[agent_to_fqdn(master)]}\", 'a+') { |f| f.puts(\"found_type_tst: \" + Time.now.to_s) }\n        end\n    END\n    agents.each do |agent|\n      custom_type << <<-END\n        if fqdn == '#{agent_to_fqdn(agent)}'\n          File.open(\"#{execution_log[agent_to_fqdn(agent)]}\", 'a+') { |f| f.puts(\"found_type_tst: \" + Time.now.to_s) }\n        end\n      END\n    end\n    custom_type << <<-END\n        Puppet.notice(\"found_type_tst\")\n      end\n    end\n    END\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/#{relative_type_path}\", custom_type)\n\n    site_pp = <<-PP\n    Resource['type_tst'] { 'found_type': }\n    PP\n    create_sitepp(master, tmp_environment, site_pp)\n  end\n  on(master, \"chmod -R 755 '/tmp/#{tmp_environment}'\")\n\n  # when the agent does its run, the newtype is executed on both the agent and master nodes\n  # so we should see a message in the execution log file on the agent and the master\n  agents.each do |agent|\n    with_puppet_running_on(master, {}) do\n\n      empty_execution_log_file(master, execution_log[agent_to_fqdn(master)])\n      empty_execution_log_file(agent, execution_log[agent_to_fqdn(agent)])\n\n      on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\")) do |puppet_result|\n        assert_match(/\\/File\\[.*\\/type_tst.rb\\]\\/ensure: defined content as/, puppet_result.stdout,\n                     'Expected to see defined content message for type: type_tst')\n        assert_match(/Notice: found_type_tst/, puppet_result.stdout, 'Expected to see the notice from the new type: type_tst')\n      end\n\n      on(master, \"cat '#{execution_log[agent_to_fqdn(master)]}'\") do |cat_result|\n        assert_match(/found_type_tst:/, cat_result.stdout,\n                     \"Expected to see execution log entry on master #{agent_to_fqdn(master)}\")\n      end\n      on(agent, \"cat '#{execution_log[agent_to_fqdn(agent)]}'\") do |cat_result|\n        assert_match(/found_type_tst:/, cat_result.stdout,\n                     \"Expected to see execution log entry on agent #{agent_to_fqdn(agent)}\")\n      end\n    end\n  end\n\n  # when generating the pcore the newtype should only be run on the master node\n  step 'generate pcore files' do\n    # start with an empty execution log\n    empty_execution_log_file(master, execution_log[agent_to_fqdn(master)])\n    agents.each do |agent|\n      empty_execution_log_file(agent, execution_log[agent_to_fqdn(agent)])\n    end\n\n    on(master, puppet(\"generate types --environment '#{tmp_environment}'\")) do |puppet_result|\n      assert_match(/Notice: Generating '\\/.*\\/type_tst\\.pp' using 'pcore' format/, puppet_result.stdout,\n                   'Expected to see Generating message for type: type_tst')\n      assert_match(/Notice: found_type_tst/, puppet_result.stdout, 'Expected to see log entry on master ')\n    end\n\n    # we should see a log entry on the master node\n    on(master, \"cat '#{execution_log[agent_to_fqdn(master)]}'\") do |cat_result|\n      assert_match(/found_type_tst:/, cat_result.stdout,\n                   \"Expected to see execution log entry on master #{agent_to_fqdn(master)}\")\n    end\n\n    # we should not see any log entries on any of the agent nodes\n    agents.each do |agent|\n      next if agent == master\n      on(agent, \"cat '#{execution_log[agent_to_fqdn(agent)]}'\") do |cat_result|\n        assert_empty(cat_result.stdout.chomp, \"Expected execution log file to be empty on agent node #{agent_to_fqdn(agent)}\")\n      end\n    end\n  end\n\n  empty_execution_log_file(master, execution_log[agent_to_fqdn(master)])\n  agents.each do |agent|\n    next if agent == master\n    empty_execution_log_file(agent, execution_log[agent_to_fqdn(agent)])\n\n    # this test is relying on the beaker helper with_puppet_running_on() to restart the server\n    # Compilation should now work using the generated types,\n    # so we should only see a log entry on the agent node and nothing on the master node\n    with_puppet_running_on(master, {}) do\n      on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"),\n         :acceptable_exit_codes => 0) do |puppet_result|\n        assert_match(/Notice: found_type_tst/, puppet_result.stdout, 'Expected to see output from new type: type_tst')\n      end\n    end\n\n    on(agent, \"cat '#{execution_log[agent_to_fqdn(agent)]}'\") do |cat_result|\n      assert_match(/found_type_tst:/, cat_result.stdout,\n                   \"Expected to see an execution log entry on agent #{agent_to_fqdn(agent)}\")\n    end\n  end\n\n  on(master, \"cat '#{execution_log[agent_to_fqdn(master)]}'\") do |cat_result|\n    assert_empty(cat_result.stdout.chomp, \"Expected master execution log to be empty #{agent_to_fqdn(master)}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/loader/func4x_loadable_from_modules.rb",
    "content": "test_name \"Exercise a module with 4x function and 4x system function\"\n\n# Purpose:\n# Test that a packed puppet can call a 4x system function, and that a 4x function in\n# a module can be called.\n#\n# Method:\n# * Manually construct a very simple module with a manifest that creates a file.\n# * The file has content that depends on logic that calls both a system function (reduce), and\n#   a function supplied in the module (helloworld::mul10).\n# * The module is manually constructed to allow the test to also run on Windows where the module tool\n#   is not supported.\n# * The module is included by calling 'include' from 'puppet apply'.\n# * Puppet apply is executed to generate the file with the content.\n# * The generated contents is asserted.\n\n# TODO: The test can be improved by adding yet another module that calls the function in helloworld.\n# TODO: The test can be improved to also test loading of a non namespaced function\n\nrequire 'puppet/acceptance/temp_file_utils'\nextend Puppet::Acceptance::TempFileUtils\n\ntag 'audit:high',\n    'audit:unit'    # This should be covered adequately by unit tests\n\ninitialize_temp_dirs\n\nagents.each do |agent|\n  # The modulepath to use in environment 'dev'\n  envs_path = get_test_file_path(agent, 'environments')\n  dev_modulepath = get_test_file_path(agent, 'environments/dev/modules')\n  target_path = get_test_file_path(agent, 'output')\n  mkdirs agent, target_path\n\n  # make sure that we use the modulepath from the dev environment\n  puppetconf = get_test_file_path(agent, 'puppet.conf')\n  on agent, puppet(\"config\", \"set\", \"environmentpath\", envs_path, \"--section\", \"main\", \"--config\", puppetconf)\n  on agent, puppet(\"config\", \"set\", \"environment\", \"dev\", \"--section\", \"user\", \"--config\", puppetconf)\n\n  # Where the functions in the written modules should go\n  helloworld_functions = 'helloworld/lib/puppet/functions/helloworld'\n  # Clean out the module that will be written to ensure no interference from a previous run\n  on agent, \"rm -rf #{File.join(dev_modulepath, 'helloworld')}\"\n  mkdirs agent, File.join(dev_modulepath, helloworld_functions)\n\n  # Write a module\n  # Write the function helloworld::mul10, that multiplies its argument by 10\n  create_remote_file(agent, File.join(dev_modulepath, helloworld_functions, \"mul10.rb\"), <<'SOURCE')\nPuppet::Functions.create_function(:'helloworld::mul10') do\n  def mul10(x)\n    x * 10\n  end\nend\nSOURCE\n\n  # Write a manifest that calls a 4x function (reduce), and calls a function defined in the module\n  # (helloworld::mul10).\n  #\n  mkdirs agent, File.join(dev_modulepath, \"helloworld\", \"manifests\")\n  create_remote_file(agent, File.join(dev_modulepath, \"helloworld\", \"manifests\", \"init.pp\"), <<SOURCE)\nclass helloworld {\n  file { \"#{target_path}/result.txt\":\n    ensure => 'file',\n    mode => '0666',\n    content => [1,2,3].reduce(\"Generated\") |$memo, $n| {\n      \"${memo}, ${n} => ${helloworld::mul10($n)}\"\n    }\n  }\n}\nSOURCE\n\n  # Run apply to generate the file with the output\n  on(agent, puppet('apply', '-e', \"'include helloworld'\", '--config', puppetconf))\n\n  # Assert that the file was written with the generated content\n  on(agent, \"cat #{File.join(target_path, 'result.txt')}\") do |result|\n    assert_match(/^Generated, 1 => 10, 2 => 20, 3 => 30$/, result.stdout, \"Generated the wrong content\")\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/loader/resource_triggers_autoload.rb",
    "content": "test_name 'C100296: can auto-load defined types using a Resource statement' do\n  tag 'risk:high'\n\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  app_type               = File.basename(__FILE__, '.*')\n  tmp_environment        = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath = \"#{environmentpath}/#{tmp_environment}\"\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  relative_define_type_dir    = 'modules/one/manifests'\n  relative_define_type_1_path = \"#{relative_define_type_dir}/tst1.pp\"\n  relative_define_type_2_path = \"#{relative_define_type_dir}/tst2.pp\"\n  step 'create custom type in two environments' do\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/#{relative_define_type_dir}\")\n\n    define_type_1 = <<-END\n    define one::tst1($var) {\n      notify { \"tst1: ${var}\": }\n    }\n    END\n    define_type_2 = <<-END\n    define one::tst2($var) {\n      notify { \"tst2: ${var}\": }\n    }\n    END\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/#{relative_define_type_1_path}\", define_type_1)\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/#{relative_define_type_2_path}\", define_type_2)\n\n    site_pp = <<-PP\n    each(['tst1', 'tst2']) |$nr| {\n      Resource[\"one::${nr}\"] { \"some_title_${nr}\": var => \"Define found one::${nr}\" }\n    }\n    PP\n    create_sitepp(master, tmp_environment, site_pp)\n  end\n\n  on(master, \"chmod -R 755 /tmp/#{tmp_environment}\")\n\n  with_puppet_running_on(master, {}) do\n    agents.each do |agent|\n      on(agent, puppet(\"agent -t --environment #{tmp_environment}\"),\n         :acceptable_exit_codes => 2) do |puppet_result|\n        assert_match(/Notice: tst1: Define found one::tst1/, puppet_result.stdout, 'Expected to see output from define notify')\n        assert_match(/Notice: tst2: Define found one::tst2/, puppet_result.stdout, 'Expected to see output from define notify')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/config3_interpolation.rb",
    "content": "test_name 'C99578: lookup should allow interpolation in hiera3 configs' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',  # This test specifically tests interpolation on the master.\n                       # Recommend adding an additonal test that validates\n                       # lookup in a masterless setup.\n    'server'\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n  master_confdir = puppet_config(master, 'confdir', section: 'master')\n\n  hiera_conf_backup = master.tmpfile('C99578-hiera-yaml')\n\n  step \"backup global hiera.yaml\" do\n    on(master, \"cp -a #{master_confdir}/hiera.yaml #{hiera_conf_backup}\", :acceptable_exit_codes => [0,1])\n  end\n\n  teardown do\n    on(master, \"mv #{hiera_conf_backup} #{master_confdir}/hiera.yaml\", :acceptable_exit_codes => [0,1])\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create hiera configs in #{tmp_environment} and global\" do\n    step \"create global hiera.yaml and module data\" do\n      create_remote_file(master, \"#{master_confdir}/hiera.yaml\", <<-HIERA)\n---\n:backends:\n  - \"yaml\"\n:hierarchy:\n  - \"%{calling_class_path}\"\n  - \"%{calling_class}\"\n  - \"%{calling_module}\"\n  - \"common\"\n      HIERA\n\n      on(master, \"mkdir -p #{fq_tmp_environmentpath}/hieradata/\")\n      on(master, \"mkdir -p #{fq_tmp_environmentpath}/modules/some_mod/manifests\")\n      create_remote_file(master, \"#{fq_tmp_environmentpath}/modules/some_mod/manifests/init.pp\", <<-PP)\nclass some_mod {\n  notify { \"${lookup('environment_key')}\": }\n}\n      PP\n\n      create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/some_mod.yaml\", <<-YAML)\n---\nenvironment_key: \"env value\"\n      YAML\n\n      create_sitepp(master, tmp_environment, <<-SITE)\ninclude some_mod\n      SITE\n\n      on(master, \"chmod -R 775 #{fq_tmp_environmentpath}\")\n      on(master, \"chmod -R 775 #{master_confdir}\")\n    end\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step \"agent lookup\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment} --debug\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          assert_match(/env value/, result.stdout,\n                       \"agent lookup didn't find correct key\")\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/config5_interpolation.rb",
    "content": "test_name 'C99578: hiera5 lookup config with interpolated scoped nested variables' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',  # This test specifically tests interpolation on the master.\n                       # Recommend adding an additonal test that validates\n                       # lookup in a masterless setup.\n    'server'\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type + '1')\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create environment hiera5.yaml and environment data\" do\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hiera.yaml\", <<-HIERA)\n---\nversion: 5\ndefaults:\n  datadir: 'hieradata'\n  data_hash: yaml_data\nhierarchy:\n  - name: \"Global settings\"\n    path: \"global.yaml\"\n  - name: \"Role specific settings\"\n    paths:\n      - \"roles/%{::roles.0}.yaml\"\n  - name: \"Other Role specific settings\"\n    paths:\n      - \"roles/%{roles2.0}.yaml\"\n  - name: \"scoped variable\"\n    paths:\n      - \"roles/%{::myclass::myvar.0}.yaml\"\n  - name: \"nested hash variable\"\n    paths:\n      - \"roles/%{::hash_array.key1.0}.yaml\"\n    HIERA\n\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/hieradata/roles\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/global.yaml\", <<-YAML)\nroles:\n  - test1\nroles2:\n  - test2\ndata:\n  - \"from global\"\n    YAML\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/roles/test1.yaml\", <<-YAML)\ndata:\n  - 'from test1'\n    YAML\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/roles/test2.yaml\", <<-YAML)\ndata:\n  - 'from test2'\n    YAML\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/roles/test3.yaml\", <<-YAML)\ndata:\n  - 'from test3'\n    YAML\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/roles/test4.yaml\", <<-YAML)\ndata:\n  - 'from test4'\n    YAML\n\n    create_sitepp(master, tmp_environment, <<-SITE)\nclass myclass {\n  $myvar = ['test3']\n}\ninclude myclass\n\n$hash_array = {key1 => ['test4']}\n\n$roles = lookup('roles')\n$data = lookup('data', Array[String], 'unique')\nnotify{\"data: ${data}\":}\n$hiera_array_data = hiera_array('data')\nnotify{\"hiera_array_data: ${hiera_array_data}\":}\n\n$roles2 = lookup('roles2')\n$data2 = lookup('data', Array[String], 'unique')\nnotify{\"data2: ${data2}\":}\n$hiera_array_data2 = hiera_array('data')\nnotify{\"hiera_array_data2: ${hiera_array_data2}\":}\n    SITE\n\n    on(master, \"chmod -R 775 #{fq_tmp_environmentpath}\")\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step \"agent lookups: #{agent.hostname}, hiera5\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          assert_match(/data: \\[from global, from test1/, result.stdout,\n                       \"agent lookup didn't interpolate with hiera value\")\n          assert_match(/hiera_array_data: \\[from global, from test1/, result.stdout,\n                       \"agent hiera_array didn't interpolate with hiera value\")\n\n          assert_match(/data2: \\[from global, from test1, from test2/, result.stdout,\n                       \"agent lookup didn't interpolate non-global scope with hiera value\")\n          assert_match(/hiera_array_data2: \\[from global, from test1, from test2/, result.stdout,\n                       \"agent hiera_array didn't interpolate non-global scope with hiera value\")\n\n          assert_match(/data2: \\[from global, from test1, from test2, from test3/, result.stdout,\n                       \"agent lookup didn't interpolate class scope with hiera value\")\n          assert_match(/hiera_array_data2: \\[from global, from test1, from test2, from test3/, result.stdout,\n                       \"agent hiera_array didn't interpolate class scope with hiera value\")\n\n          assert_match(/data2: \\[from global, from test1, from test2, from test3, from test4\\]/, result.stdout,\n                       \"agent lookup didn't interpolate nested hashes with hiera value\")\n          assert_match(/hiera_array_data2: \\[from global, from test1, from test2, from test3, from test4\\]/, result.stdout,\n                       \"agent hiera_array didn't interpolate nested hashes with hiera value\")\n        end\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/hiera3_custom_backend.rb",
    "content": "test_name 'C99630: hiera v3 custom backend' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n  require 'puppet/acceptance/temp_file_utils.rb'\n  extend Puppet::Acceptance::TempFileUtils\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # Master is not needed for this test. Refactor\n                       # to use puppet apply with a local module tree.\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n  puppetserver_config = \"#{master['puppetserver-confdir']}/puppetserver.conf\"\n  existing_loadpath = read_tk_config_string(on(master, \"cat #{puppetserver_config}\").stdout.strip)['jruby-puppet']['ruby-load-path'].first\n  confdir = puppet_config(master, 'confdir', section: 'master')\n\n  hiera_conf_backup = master.tmpfile('C99629-hiera-yaml')\n\n  step \"backup global hiera.yaml\" do\n    on(master, \"cp -a #{confdir}/hiera.yaml #{hiera_conf_backup}\", :acceptable_exit_codes => [0,1])\n  end\n\n  teardown do\n    step 'delete custom backend, restore default hiera config' do\n      on(master, \"rm #{existing_loadpath}/hiera/backend/custom_backend.rb\", :acceptable_exit_codes => [0,1])\n      on(master, \"mv #{hiera_conf_backup} #{confdir}/hiera.yaml\", :acceptable_exit_codes => [0,1])\n      on(master, \"/opt/puppetlabs/server/bin/puppetserver gem uninstall --executables --force hiera\")\n      on(master, \"/opt/puppetlabs/puppet/bin/gem uninstall --executables --force hiera\")\n    end\n\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"install hiera v3 gem\" do\n    # for puppet agent <-> server, hiera must be installed using puppetserver's gem command\n    on(master, \"/opt/puppetlabs/server/bin/puppetserver gem install --no-document hiera\")\n    # for puppet lookup, hiera must be installed using puppet's gem command\n    on(master, \"/opt/puppetlabs/puppet/bin/gem install --no-document hiera\")\n  end\n\n  step \"create hiera v5 config and v3 custom backend\" do\n    on(master, \"cp #{confdir}/hiera.yaml /tmp\")\n    create_remote_file(master, \"#{confdir}/hiera.yaml\", <<-HIERA)\n---\nversion: 5\nhierarchy:\n  - name: Test\n    hiera3_backend: custom\n    HIERA\n    on(master, \"chmod -R #{PUPPET_CODEDIR_PERMISSIONS} #{confdir}\")\n\n    on(master, \"mkdir -p #{existing_loadpath}/hiera/backend/\")\n    custom_backend_rb = <<-RB\nclass Hiera\n  module Backend\n    class Custom_backend\n      def lookup(key, scope, order_override, resolution_type, context)\n        return 'custom value' unless (key == 'lookup_options')\n      end\n    end\n  end\nend\n    RB\n    create_remote_file(master, \"#{existing_loadpath}/hiera/backend/custom_backend.rb\", custom_backend_rb)\n    on(master, \"chmod #{PUPPET_CODEDIR_PERMISSIONS} #{existing_loadpath}/hiera/backend/custom_backend.rb\")\n  end\n\n  step \"create site.pp which calls lookup on our keys\" do\n    create_sitepp(master, tmp_environment, <<-SITE)\n      notify { \"${lookup('anykey')}\": }\n    SITE\n    on(master, \"chmod -R #{PUPPET_CODEDIR_PERMISSIONS} #{fq_tmp_environmentpath}\")\n  end\n\n  step 'assert lookups using lookup subcommand on the master' do\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", '--explain', 'anykey'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/custom value/, result.stdout,\n                   \"lookup subcommand didn't find correct key\")\n    end\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step \"agent manifest lookup on #{agent.hostname}\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          assert_match(/custom value/, result.stdout,\n                       \"agent lookup didn't find correct key\")\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/lookup.rb",
    "content": "test_name \"Lookup data using the agnostic lookup function\" do\n  # pre-docs:\n  # https://puppet-on-the-edge.blogspot.com/2015/01/puppet-40-data-in-modules-and.html\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # Master is not needed for this test. Refactor\n                       # to use puppet apply with a local module tree.\n                       # Use mk_tmp_environment_with_teardown to create environment.\n    'server'\n\n  testdir = master.tmpdir('lookup')\n\n  module_name                     = \"data_module\"\n  module_name2                    = \"other_module\"\n  hash_name                       = \"hash_name\"\n  array_key                       = \"array_key\"\n\n  env_data_implied_key            = \"env_data_implied\"\n  env_data_implied_value          = \"env_implied_a\"\n  env_data_key                    = \"env_data\"\n  env_data_value                  = \"env_a\"\n  env_hash_key                    = \"env_hash_key\"\n  env_hash_value                  = \"env_class_a\"\n  env_array_value0                = \"env_array_a\"\n  env_array_value1                = \"env_array_b\"\n\n  module_data_implied_key         = \"module_data_implied\"\n  module_data_implied_value       = \"module_implied_b\"\n  module_data_key                 = \"module_data\"\n  module_data_value               = \"module_b\"\n  module_data_value_other         = \"other_module_b\"\n  module_hash_key                 = \"module_hash_key\"\n  module_hash_value               = \"module_class_b\"\n  module_array_value0             = \"module_array_a\"\n  module_array_value1             = \"module_array_b\"\n\n  env_data_override_implied_key   = \"env_data_override_implied\"\n  env_data_override_implied_value = \"env_override_implied_c\"\n  env_data_override_key           = \"env_data_override\"\n  env_data_override_value         = \"env_override_c\"\n\n  hiera_data_implied_key          = \"apache_server_port_implied\"\n  hiera_data_implied_value        = \"8080\"\n  hiera_data_key                  = \"apache_server_port\"\n  hiera_data_value                = \"9090\"\n  hiera_hash_key                  = \"hiera_hash_key\"\n  hiera_hash_value                = \"hiera_class_c\"\n  hiera_array_value0              = \"hiera_array_a\"\n  hiera_array_value1              = \"hiera_array_b\"\n\n  automatic_data_key              = \"automatic_data_key\"\n  automatic_data_value            = \"automatic_data_value\"\n  automatic_default_value         = \"automatic_default_value\"\n\n  def mod_manifest_entry(module_name = nil, testdir, module_data_implied_key,\n                         module_data_implied_value, module_data_key,\n                         module_data_value, hash_name, module_hash_key,\n                         module_hash_value, array_key, module_array_value0,\n                         module_array_value1)\n    if module_name\n      module_files_manifest = <<PP\n      # the function to provide data for this module\n      file { '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/functions/#{module_name}/data.rb':\n        ensure => file,\n        content => \"\n          Puppet::Functions.create_function(:'#{module_name}::data') do\n            def data()\n              { '#{module_name}::#{module_data_implied_key}' => '#{module_data_implied_value}',\n                '#{module_name}::#{module_data_key}' => '#{module_data_value}',\n                '#{module_name}::#{hash_name}' => {'#{module_hash_key}' => '#{module_hash_value}'},\n                '#{module_name}::#{array_key}' => ['#{module_array_value0}', '#{module_array_value1}']\n              }\n            end\n          end\n        \",\n        mode => \"0640\",\n      }\nPP\n      module_files_manifest\n    end\n  end\n\n  def mod_manifest_metadata_json(module_name = nil, testdir)\n    if module_name\n      <<PPmetadata\n      file { '#{testdir}/environments/production/modules/#{module_name}/metadata.json':\n        ensure => file,\n        content => '\n{\n  \"name\": \"tester-#{module_name}\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [\n  ],\n  \"data_provider\": \"function\"\n}\n        ',\n        mode => \"0644\",\n      }\n      file { '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/bindings':\n        ensure => absent,\n\tforce  => true,\n      }\nPPmetadata\n    end\n  end\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  module_manifest1 = mod_manifest_entry(module_name, testdir, module_data_implied_key,\n                         module_data_implied_value, module_data_key, module_data_value,\n                         hash_name, module_hash_key, module_hash_value, array_key,\n                         module_array_value0, module_array_value1)\n  module_manifest2 = mod_manifest_entry(module_name2, testdir, module_data_implied_key,\n                         module_data_implied_value, module_data_key, module_data_value_other,\n                         hash_name, module_hash_key, module_hash_value, array_key,\n                         module_array_value0, module_array_value1)\n  metadata_manifest1 = mod_manifest_metadata_json(module_name, testdir)\n  metadata_manifest2 = mod_manifest_metadata_json(module_name2, testdir)\n\n  apply_manifest_on(master, <<-PP, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile {\n  '#{testdir}':;\n  '#{testdir}/hieradata':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/manifests':;\n  '#{testdir}/environments/production/modules':;\n  '#{testdir}/environments/production/lib':;\n  '#{testdir}/environments/production/lib/puppet':;\n  '#{testdir}/environments/production/lib/puppet/functions':;\n  '#{testdir}/environments/production/lib/puppet/functions/environment':;\n  '#{testdir}/environments/production/modules/#{module_name}':;\n  '#{testdir}/environments/production/modules/#{module_name}/manifests':;\n  '#{testdir}/environments/production/modules/#{module_name}/lib':;\n  '#{testdir}/environments/production/modules/#{module_name}/lib/puppet':;\n  '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/bindings':;\n  '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/bindings/#{module_name}':;\n  '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/functions':;\n  '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/functions/#{module_name}':;\n  '#{testdir}/environments/production/modules/#{module_name2}':;\n  '#{testdir}/environments/production/modules/#{module_name2}/manifests':;\n  '#{testdir}/environments/production/modules/#{module_name2}/lib':;\n  '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet':;\n  '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/bindings':;\n  '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/bindings/#{module_name2}':;\n  '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/functions':;\n  '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/functions/#{module_name2}':;\n}\n\nfile { '#{testdir}/hiera.yaml':\n  ensure  => file,\n  content => '---\n    :backends:\n      - \"yaml\"\n    :logger: \"console\"\n    :hierarchy:\n      - \"global\"\n\n    :yaml:\n      :datadir: \"#{testdir}/hieradata\"\n  ',\n  mode => \"0640\",\n}\n\nfile { '#{testdir}/hieradata/global.yaml':\n  ensure  => file,\n  content => \"---\n    #{hiera_data_key}: #{hiera_data_value}\n    #{module_name}::#{hiera_data_implied_key}: #{hiera_data_implied_value}\n    #{module_name}::#{hash_name}:\n        #{hiera_hash_key}: #{hiera_hash_value}\n    #{module_name}::#{array_key}:\n        - #{hiera_array_value0}\n        - #{hiera_array_value1}\n    #{module_name}::#{automatic_data_key}: #{automatic_data_value}\n  \",\n  mode => \"0640\",\n}\n\nfile { '#{testdir}/environments/production/environment.conf':\n  ensure => file,\n  content => '\n    environment_timeout = 0\n    # for this environment, provide our own function to supply data to lookup\n    # implies a ruby function in <environment>/lib/puppet/functions/environment/data.rb\n    #   named environment::data()\n    environment_data_provider = \"function\"\n  ',\n  mode => \"0640\",\n}\n\n# the function to provide data for this environment\nfile { '#{testdir}/environments/production/lib/puppet/functions/environment/data.rb':\n  ensure => file,\n  content => \"\n    Puppet::Functions.create_function(:'environment::data') do\n      def data()\n        { '#{module_name}::#{env_data_implied_key}' => '#{env_data_implied_value}',\n          '#{module_name}::#{env_data_override_implied_key}' => '#{env_data_override_implied_value}',\n          '#{env_data_key}' => '#{env_data_value}',\n          '#{module_name}::#{hash_name}' => {'#{env_hash_key}' => '#{env_hash_value}'},\n          '#{env_data_override_key}' => '#{env_data_override_value}',\n          '#{module_name}::#{array_key}' => ['#{env_array_value0}', '#{env_array_value1}']\n        }\n      end\n    end\n  \",\n  mode => \"0640\",\n}\n\n# place module file segments here\n#{module_manifest1}\n# same key, different module and values\n#{module_manifest2}\n\nfile { '#{testdir}/environments/production/modules/#{module_name}/manifests/init.pp':\n  ensure => file,\n  content => '\n    class #{module_name}($#{env_data_implied_key},\n                         $#{module_data_implied_key},\n                         $#{env_data_override_implied_key},\n                         $#{hiera_data_implied_key},\n                         $#{automatic_data_key}=$#{automatic_default_value}) {\n      # lookup data from the environment function databinding\n      notify { \"#{env_data_implied_key} $#{env_data_implied_key}\": }\n      $lookup_env = lookup(\"#{env_data_key}\")\n      notify { \"#{env_data_key} $lookup_env\": }\n\n      # lookup data from the module databinding\n      notify { \"#{module_data_implied_key} $#{module_data_implied_key}\": }\n      $lookup_module = lookup(\"#{module_name}::#{module_data_key}\")\n      notify { \"#{module_data_key} $lookup_module\": }\n\n      # lookup data from another modules databinding\n      $lookup_module2 = lookup(\"#{module_name2}::#{module_data_key}\")\n      notify { \"#{module_data_key} $lookup_module2\": }\n\n      # ensure env can override module\n      notify { \"#{env_data_override_implied_key} $#{env_data_override_implied_key}\": }\n      $lookup_override = lookup(\"#{env_data_override_key}\")\n      notify { \"#{env_data_override_key} $lookup_override\": }\n\n      # should fall-back to hiera global.yaml data\n      notify { \"#{hiera_data_implied_key} $#{hiera_data_implied_key}\": }\n      $lookup_port = lookup(\"#{hiera_data_key}\")\n      notify { \"#{hiera_data_key} $lookup_port\": }\n\n      # should be able to merge hashes across sources\n      #   this mimicks/covers behavior for including classes\n      $lookup_hash = lookup(\"#{module_name}::#{hash_name}\",Hash[String,String],\\\\'hash\\\\')\n      notify { \"#{hash_name} $lookup_hash\": }\n\n      # should be able to make an array across sources\n      #   this mimicks/covers behavior for including classes\n      $lookup_array = lookup(\"#{module_name}::#{array_key}\",Array[String],\\\\'unique\\\\')\n      notify { \"yep\": message => \"#{array_key} $lookup_array\" }\n\n      # automatic data lookup of parametrized class\n      notify { \"#{automatic_data_key} $#{automatic_data_key}\": }\n    }',\n  mode => \"0640\",\n}\n\nfile { '#{testdir}/environments/production/manifests/site.pp':\n  ensure => file,\n  content => \"\n      node default {\n        include #{module_name}\n    }\",\n  mode => \"0640\",\n}\nPP\n\n  apply_manifest_on(master, <<-PP, :catch_failures => true)\n#{metadata_manifest1}\n#{metadata_manifest2}\n  PP\n\n\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{testdir}/environments\",\n      'hiera_config' => \"#{testdir}/hiera.yaml\",\n    },\n  }\n  with_puppet_running_on master, master_opts, testdir do\n    step \"Lookup string data, binding specified in metadata.json\" do\n      agents.each do |agent|\n        on(agent, puppet('agent', \"-t\"), :acceptable_exit_codes => [0, 2]) do |result|\n          assert_match(\"#{env_data_implied_key} #{env_data_implied_value}\", result.stdout)\n          assert_match(\"#{env_data_key} #{env_data_value}\", result.stdout)\n\n          assert_match(\"#{module_data_implied_key} #{module_data_implied_value}\", result.stdout)\n          assert_match(\"#{module_data_key} #{module_data_value}\", result.stdout)\n\n          assert_match(\"#{module_data_key} #{module_data_value_other}\", result.stdout)\n\n          assert_match(\"#{env_data_override_implied_key} #{env_data_override_implied_value}\", result.stdout)\n          assert_match(\"#{env_data_override_key} #{env_data_override_value}\", result.stdout)\n\n          assert_match(\"#{hiera_data_implied_key} #{hiera_data_implied_value}\", result.stdout)\n          assert_match(\"#{hiera_data_key} #{hiera_data_value}\", result.stdout)\n\n          assert_match(\"#{hash_name} {#{module_hash_key} => #{module_hash_value}, #{env_hash_key} => #{env_hash_value}, #{hiera_hash_key} => #{hiera_hash_value}}\", result.stdout)\n\n          assert_match(\"#{array_key} [#{hiera_array_value0}, #{hiera_array_value1}, #{env_array_value0}, #{env_array_value1}, #{module_array_value0}, #{module_array_value1}]\", result.stdout)\n\n          assert_match(\"#{automatic_data_key} #{automatic_data_value}\", result.stdout)\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/lookup_rich_values.rb",
    "content": "test_name 'C99044: lookup should allow rich data as values' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # Master is not needed for this test. Refactor\n                       # to use puppet apply with a local environment.\n    'server'\n\n  # The following two lines are required for the puppetserver service to\n  # start correctly. These should be removed when PUP-7102 is resolved.\n  confdir = puppet_config(master, 'confdir', section: 'master')\n  on(master, \"chown puppet:puppet #{confdir}/hiera.yaml\")\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n\n  sensitive_value_rb = 'foot, no mouth'\n  sensitive_value_pp = 'toe, no step'\n  sensitive_value_pp2 = 'toe, no module'\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create ruby lookup function in #{tmp_environment}\" do\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/lib/puppet/functions/environment\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hiera.yaml\", <<-HIERA)\n---\nversion: 5\nhierarchy:\n  - name: Test\n    data_hash: rich_data_test\n  - name: Test2\n    data_hash: some_mod::rich_data_test2\n  - name: Test3\n    data_hash: rich_data_test3\n  HIERA\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/lib/puppet/functions/rich_data_test.rb\", <<-FUNC)\nPuppet::Functions.create_function(:rich_data_test) do\n  def rich_data_test(options, context)\n    rich_type_instance = Puppet::Pops::Types::PSensitiveType::Sensitive.new(\"#{sensitive_value_rb}\")\n    {\n      'environment_key' => rich_type_instance,\n    }\n  end\nend\n    FUNC\n  end\n\n  step \"create puppet language lookup function in #{tmp_environment} module\" do\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/modules/some_mod/functions\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/modules/some_mod/functions/rich_data_test2.pp\", <<-FUNC)\nfunction some_mod::rich_data_test2($options, $context) {\n  {\n    \"environment_key2\" => Sensitive('#{sensitive_value_pp}'),\n  }\n}\n    FUNC\n    on(master, \"chmod -R a+rw #{fq_tmp_environmentpath}\")\n  end\n\n  step \"C99571: create puppet language lookup function in #{tmp_environment}\" do\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/functions\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/functions/rich_data_test3.pp\", <<-FUNC)\nfunction rich_data_test3($options, $context) {\n  {\n    \"environment_key3\" => Sensitive('#{sensitive_value_pp2}'),\n  }\n}\n    FUNC\n    on(master, \"chmod -R a+rw #{fq_tmp_environmentpath}\")\n  end\n\n  step \"create site.pp which calls lookup on our keys\" do\n    create_sitepp(master, tmp_environment, <<-SITE)\n      notify { \"${unwrap(lookup('environment_key'))}\": }\n      notify { \"${unwrap(lookup('environment_key2'))}\": }\n      notify { \"${unwrap(lookup('environment_key3'))}\": }\n    SITE\n  end\n\n  step 'assert lookups using lookup subcommand' do\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"lookup subcommand using ruby function didn't exit properly: (#{result.exit_code})\")\n      assert_match(sensitive_value_rb, result.stdout,\n                   \"lookup subcommand using ruby function didn't find correct key\")\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key2'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"lookup subcommand using puppet function in module didn't exit properly: (#{result.exit_code})\")\n      assert_match(sensitive_value_pp, result.stdout,\n                   \"lookup subcommand using puppet function in module didn't find correct key\")\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key3'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"lookup subcommand using puppet function didn't exit properly: (#{result.exit_code})\")\n      assert_match(sensitive_value_pp2, result.stdout,\n                   \"lookup subcommand using puppet function didn't find correct key\")\n    end\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step \"agent lookup in ruby function\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup using ruby function didn't exit properly: (#{result.exit_code})\")\n          assert_match(sensitive_value_rb, result.stdout,\n                       \"agent lookup using ruby function didn't find correct key\")\n          assert_match(sensitive_value_pp, result.stdout,\n                       \"agent lookup using puppet function in module didn't find correct key\")\n          assert_match(sensitive_value_pp2, result.stdout,\n                       \"agent lookup using puppet function didn't find correct key\")\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/merge_strategies.rb",
    "content": "test_name 'C99903: merge strategies' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # Master is not needed for this test. Refactor\n                       # to use puppet apply with a local module tree.\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type + '1')\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n  tmp_environment2 = mk_tmp_environment_with_teardown(master, app_type + '2')\n  fq_tmp_environmentpath2  = \"#{environmentpath}/#{tmp_environment2}\"\n\n  master_confdir = puppet_config(master, 'confdir', section: 'master')\n  hiera_conf_backup = master.tmpfile(app_type)\n\n  teardown do\n    step \"restore default global hiera.yaml\" do\n      on(master, \"mv #{hiera_conf_backup} #{master_confdir}/hiera.yaml\", :acceptable_exit_codes => [0,1])\n    end\n\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create global hiera.yaml and environment data\" do\n    step \"backup global hiera.yaml\" do\n      on(master, \"cp -a #{master_confdir}/hiera.yaml #{hiera_conf_backup}\")\n    end\n\n    create_remote_file(master, \"#{master_confdir}/hiera.yaml\", <<-HIERA)\n---\n:backends:\n  - yaml\n:yaml:\n  :datadir: \"/etc/puppetlabs/code/environments/%{::environment}/hieradata\"\n:hierarchy:\n  - \"host\"\n  - \"roles\"\n  - \"profiles\"\n  - \"%{facts.os.name}\"\n  - \"%{facts.os.family}\"\n  - \"%{facts.kernel}\"\n  - \"common\"\n:merge_behavior: deeper\n:deep_merge_options:\n  :merge_hash_arrays: true\nHIERA\n    on(master, \"chown puppet:puppet #{master_confdir}/hiera.yaml\")\n\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/hieradata/\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/host.yaml\", <<-YAML)\n---\nprofiles:\n  webserver:\n    apache:\n      httpd:\n        modules:\n          - mpm_prefork\n          - php\n          - ssl\narrayed_hash:\n  the_hash:\n    - array1:\n        key1: val1\n        key2: val2\narray:\n  - foo\nYAML\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/profiles.yaml\", <<-YAML)\nprofiles:\n  webserver:\n    apache:\n      httpd:\n        modules:\n          - auth_kerb\n          - authnz_ldap\n          - cgid\n          - php\n          - status\narray:\n  - bar\nYAML\n\n    create_sitepp(master, tmp_environment, <<-SITE)\nnotify { \"hiera_hash: ${hiera_hash ('profiles')['webserver']['apache']['httpd']['modules']}\": }\nnotify { \"lookup1: ${lookup ('profiles')['webserver']['apache']['httpd']['modules']}\": }\nnotify { \"lookup1b: ${lookup ({'name' => 'profiles', 'merge' => 'deep'})['webserver']['apache']['httpd']['modules']}\": }\nnotify { \"hiera_merge_hash: ${hiera_hash ('arrayed_hash')}\": }\nnotify { \"lookup_arrayed_hash: ${lookup ({'name' => 'arrayed_hash', 'merge' => {'strategy' => 'deep', 'merge_hash_arrays' => true}})}\": }\nnotify { \"hiera-array: ${hiera ('array')}\": }\nnotify { \"hiera_array: ${hiera_array ('array')}\": }\nnotify { \"lookup-array: ${lookup ('array')}\": }\n    SITE\n\n    on(master, \"chmod -R 775 #{fq_tmp_environmentpath}\")\n  end\n\n  step \"create another environment, hiera5 config and environment data: #{tmp_environment2}\" do\n    create_remote_file(master, \"#{fq_tmp_environmentpath2}/hiera.yaml\", <<-HIERA)\n---\nversion: 5\nhierarchy:\n  - name: \"%{environment}/host\"\n    data_hash: yaml_data\n    path: \"hieradata/host.yaml\"\n  - name: \"%{environment}/profiles\"\n    data_hash: yaml_data\n    path: \"hieradata/profiles.yaml\"\nHIERA\n\n    on(master, \"mkdir -p #{fq_tmp_environmentpath2}/hieradata/\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath2}/hieradata/host.yaml\", <<-YAML)\n---\nprofiles:\n  webserver:\n    apache:\n      httpd:\n        modules:\n          - mpm_prefork\n          - php\n          - ssl\narrayed_hash:\n  the_hash:\n    - array1:\n        key1: val1\n        key2: val2\narray:\n  - foo\nlookup_options:\n  'profiles':\n    merge:\n      strategy: deep\nYAML\n\n    create_remote_file(master, \"#{fq_tmp_environmentpath2}/hieradata/profiles.yaml\", <<-YAML)\nprofiles:\n  webserver:\n    apache:\n      httpd:\n        modules:\n          - auth_kerb\n          - authnz_ldap\n          - cgid\n          - php\n          - status\narray:\n  - bar\nlookup_options:\n  'profiles':\n    merge:\n      strategy: deep\nYAML\n\n    create_sitepp(master, tmp_environment2, <<-SITE)\nnotify { \"hiera_hash: ${hiera_hash ('profiles')['webserver']['apache']['httpd']['modules']}\": }\nnotify { \"lookup2: ${lookup ('profiles')['webserver']['apache']['httpd']['modules']}\": }\nnotify { \"lookup2b: ${lookup ({'name' => 'profiles', 'merge' => 'first'})['webserver']['apache']['httpd']['modules']}\": }\nnotify { \"hiera_merge_hash: ${hiera_hash ('arrayed_hash')}\": }\nnotify { \"lookup_arrayed_hash: ${lookup ({'name' => 'arrayed_hash', 'merge' => {'strategy' => 'deep', 'merge_hash_arrays' => true}})}\": }\nnotify { \"hiera-array: ${hiera ('array')}\": }\nnotify { \"hiera_array: ${hiera_array ('array')}\": }\nnotify { \"lookup-array: ${lookup ('array')}\": }\n    SITE\n\n    on(master, \"chmod -R 775 #{fq_tmp_environmentpath2}\")\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step \"agent lookups #{agent.hostname}, hiera3\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          # hiera_hash will honor old global merge strategies, which were a bad idea\n          assert_match(/hiera_hash: \\[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\\]/, result.stdout,\n                       \"1: agent hiera_hash didn't find correct key\")\n          # so, lookup doesn't honor them except on a by-key or by-lookup basis\n          assert_match(/lookup1: \\[mpm_prefork, php, ssl\\]/, result.stdout,\n                       \"1: agent lookup didn't find correct key\")\n          assert_match(/lookup1b: \\[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\\]/, result.stdout,\n                       \"1b: agent lookup didn't find correct key\")\n          assert_match(/hiera_merge_hash: {the_hash => \\[{array1 => {key1 => val1, key2 => val2}}\\]}/, result.stdout,\n                       \"agent hiera_hash 1 merge_hash_arrays didn't work properly\")\n          assert_match(/lookup_arrayed_hash: {the_hash => \\[{array1 => {key1 => val1, key2 => val2}}\\]}/, result.stdout,\n                       \"agent lookup 1 deep merge with merge_hash_arrays didn't work properly\")\n          assert_match(/hiera-array: \\[foo\\]/, result.stdout,\n                       \"hiera() lookup of an array with deeper should be merged\")\n          assert_match(/hiera_array: \\[foo, bar\\]/, result.stdout,\n                       \"hiera_array() lookup of an array should be merged\")\n          assert_match(/lookup-array: \\[foo\\]/, result.stdout,\n                       \"lookup() lookup of an array should default to first\")\n        end\n      end\n      step \"agent lookups #{agent.hostname}, hiera5\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment2}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          assert_match(/hiera_hash: \\[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\\]/, result.stdout,\n                       \"2: agent hiera_hash didn't find correct key\")\n          assert_match(/lookup2: \\[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\\]/, result.stdout,\n                       \"2: agent lookup didn't find correct key\")\n          assert_match(/lookup2b: \\[mpm_prefork, php, ssl\\]/, result.stdout,\n                       \"2b: agent lookup didn't find correct key\")\n          assert_match(/hiera_merge_hash: {the_hash => \\[{array1 => {key1 => val1, key2 => val2}}\\]}/, result.stdout,\n                       \"agent hiera_hash 2 merge_hash_arrays didn't work properly\")\n          assert_match(/lookup_arrayed_hash: {the_hash => \\[{array1 => {key1 => val1, key2 => val2}}\\]}/, result.stdout,\n                       \"agent lookup 2 deep merge with merge_hash_arrays didn't work properly\")\n          assert_match(/hiera-array: \\[foo\\]/, result.stdout,\n                       \"hiera() 2 lookup in hiera5 of an array should default to first\")\n          assert_match(/hiera_array: \\[foo, bar\\]/, result.stdout,\n                       \"hiera_array() 2 lookup of an array should be merged\")\n          assert_match(/lookup-array: \\[foo\\]/, result.stdout,\n                       \"lookup() 2 lookup in hiera5 of an array should default to first\")\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/v3_config_and_data.rb",
    "content": "test_name 'C99629: hiera v5 can use v3 config and data' do\n  require 'puppet/acceptance/environment_utils.rb'\n  extend Puppet::Acceptance::EnvironmentUtils\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # Master is not needed for this test. Refactor\n                       # to use puppet apply with a local module tree.\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n\n  hiera_conf_backup = master.tmpfile('C99629-hiera-yaml')\n\n  step \"create hiera v3 global config and data\" do\n    confdir = puppet_config(master, 'confdir', section: 'master')\n\n    step \"backup global hiera.yaml\" do\n      on(master, \"cp -a #{confdir}/hiera.yaml #{hiera_conf_backup}\", :acceptable_exit_codes => [0,1])\n    end\n\n    teardown do\n      step \"restore global hiera.yaml\" do\n        on(master, \"mv #{hiera_conf_backup} #{confdir}/hiera.yaml\", :acceptable_exit_codes => [0,1])\n      end\n\n      agents.each do |agent|\n        on(agent, puppet('config print lastrunfile')) do |command_result|\n          agent.rm_rf(command_result.stdout)\n        end\n      end\n    end\n\n    step \"create global hiera.yaml and module data\" do\n      create_remote_file(master, \"#{confdir}/hiera.yaml\", <<-HIERA)\n---\n:backends:\n  - \"yaml\"\n  - \"json\"\n  - \"hocon\"\n:hierarchy:\n  - \"somesuch\"\n  - \"common\"\n      HIERA\n\n      on(master, \"mkdir -p #{fq_tmp_environmentpath}/hieradata/\")\n      create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/somesuch.yaml\", <<-YAML)\n---\nenvironment_key1: \"env value1\"\nenvironment_key3: \"env value3\"\n      YAML\n      create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/somesuch.json\", <<-JSON)\n{\n  \"environment_key1\" : \"wrong value\",\n  \"environment_key2\" : \"env value2\"\n}\n      JSON\n      step \"C99628: add hocon backend and data\" do\n        create_remote_file(master, \"#{fq_tmp_environmentpath}/hieradata/somesuch.conf\", <<-HOCON)\nenvironment_key4 = \"hocon value\",\n        HOCON\n      end\n\n      create_sitepp(master, tmp_environment, <<-SITE)\nnotify { \"${lookup('environment_key1')}\": }\nnotify { \"${lookup('environment_key2')}\": }\nnotify { \"${lookup('environment_key3')}\": }\nnotify { \"${lookup('environment_key4')}\": }\n      SITE\n\n      on(master, \"chmod -R 775 #{fq_tmp_environmentpath}\")\n      on(master, \"chmod -R 775 #{confdir}\")\n    end\n  end\n\n  step 'assert lookups using lookup subcommand' do\n    step 'assert lookup --explain using lookup subcommand' do\n      on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key1 --explain'), :accept_all_exit_codes => true) do |result|\n        assert(result.exit_code == 0, \"1: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n        assert_match(/env value1/, result.stdout,\n                     \"1: lookup subcommand didn't find correct key\")\n        assert_match(/hiera configuration version 3/, result.stdout,\n                     \"hiera config version not reported properly\")\n        assert_match(/#{fq_tmp_environmentpath}\\/hieradata\\/somesuch\\.yaml/, result.stdout,\n                     \"hiera hierarchy abs path not reported properly\")\n        assert_match(/path: \"somesuch\"/, result.stdout,\n                     \"hiera hierarchy path not reported properly\")\n      end\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key2'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"2: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/env value2/, result.stdout,\n                   \"2: lookup subcommand didn't find correct key\")\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key3'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"3: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/env value3/, result.stdout,\n                   \"3: lookup subcommand didn't find correct key\")\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key4'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"4: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/hocon value/, result.stdout,\n                   \"4: lookup subcommand didn't find correct key\")\n    end\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step \"agent lookup\" do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          assert_match(/env value1/, result.stdout,\n                       \"1: agent lookup didn't find correct key\")\n          assert_match(/env value2/, result.stdout,\n                       \"2: agent lookup didn't find correct key\")\n          assert_match(/env value3/, result.stdout,\n                       \"3: agent lookup didn't find correct key\")\n          assert_match(/hocon value/, result.stdout,\n                       \"4: agent lookup didn't find correct key\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/lookup/v4_hieradata_with_v5_configs.rb",
    "content": "test_name 'C99572: v4 hieradata with v5 configs' do\n  require 'puppet/acceptance/puppet_type_test_tools.rb'\n  extend Puppet::Acceptance::PuppetTypeTestTools\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor',  # Master is not needed for this test. Refactor\n                       # to use puppet apply with a local module tree.\n\n  app_type        = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n  fq_tmp_environmentpath  = \"#{environmentpath}/#{tmp_environment}\"\n\n  confdir = puppet_config(master, 'confdir', section: 'master')\n  hiera_conf_backup = master.tmpfile('C99572-hiera-yaml')\n\n  step \"backup global hiera.yaml\" do\n    on(master, \"cp -a #{confdir}/hiera.yaml #{hiera_conf_backup}\", :acceptable_exit_codes => [0,1])\n  end\n\n  teardown do\n    step \"restore global hiera.yaml\" do\n      on(master, \"mv #{hiera_conf_backup} #{confdir}/hiera.yaml\", :acceptable_exit_codes => [0,1])\n    end\n\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create global hiera.yaml and data\" do\n    create_remote_file(master, \"#{confdir}/hiera.yaml\", <<-HIERA)\n---\nversion: 5\nhierarchy:\n  - name: \"%{environment}\"\n    data_hash: yaml_data\n    path: \"%{environment}.yaml\"\n  - name: common\n    data_hash: yaml_data\n    path: \"common.yaml\"\n    HIERA\n    on(master, \"chmod 755 #{confdir}/hiera.yaml\")\n    create_remote_file(master, \"#{confdir}/#{tmp_environment}.yaml\", <<-YAML)\n---\nenvironment_key: environment_key-global_env_file\nglobal_key: global_key-global_env_file\n    YAML\n    create_remote_file(master, \"#{confdir}/common.yaml\", <<-YAML)\n---\nenvironment_key: environment_key-global_common_file\nglobal_key: global_key-global_common_file\n    YAML\n  end\n\n  step \"create environment hiera.yaml and data\" do\n    on(master, \"mkdir -p #{fq_tmp_environmentpath}/data\")\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/hiera.yaml\", <<-HIERA)\n---\nversion: 5\nhierarchy:\n  - name: \"%{environment}\"\n    data_hash: yaml_data\n    path: \"%{environment}.yaml\"\n  - name: common\n    data_hash: yaml_data\n    path: \"common.yaml\"\n  - name: hocon\n    data_hash: hocon_data\n    path: \"common.conf\"\n  HIERA\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/data/#{tmp_environment}.yaml\", <<-YAML)\n---\nenvironment_key: \"environment_key-env_file\"\n    YAML\n    create_remote_file(master, \"#{fq_tmp_environmentpath}/data/common.yaml\", <<-YAML)\n---\nenvironment_key: \"environment_key-common_file\"\nglobal_key: \"global_key-common_file\"\n    YAML\n    step \"C99628: add hocon backend and data\" do\n      create_remote_file(master, \"#{fq_tmp_environmentpath}/data/common.conf\", <<-HOCON)\nenvironment_key2 = \"hocon value\",\n      HOCON\n    end\n\n    create_sitepp(master, tmp_environment, <<-SITE)\n      notify { \"${lookup('environment_key')}\": }\n      notify { \"${lookup('global_key')}\": }\n      notify { \"${lookup('environment_key2')}\": }\n    SITE\n    on(master, \"chmod -R 755 #{fq_tmp_environmentpath}\")\n  end\n\n  step 'assert lookups using lookup subcommand' do\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"1: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/environment_key-env_file/, result.stdout,\n                   'lookup environment_key subcommand didn\\'t find correct key')\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'global_key'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"2: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/global_key-common_file/, result.stdout,\n                   'lookup global_key subcommand didn\\'t find correct key')\n    end\n    on(master, puppet('lookup', \"--environment #{tmp_environment}\", 'environment_key2'), :accept_all_exit_codes => true) do |result|\n      assert(result.exit_code == 0, \"3: lookup subcommand didn't exit properly: (#{result.exit_code})\")\n      assert_match(/hocon value/, result.stdout,\n                   'lookup environment_key2 subcommand didn\\'t find correct key')\n    end\n  end\n\n  with_puppet_running_on(master,{}) do\n    agents.each do |agent|\n      step 'agent lookup' do\n        on(agent, puppet('agent', \"-t --environment #{tmp_environment}\"),\n           :accept_all_exit_codes => true) do |result|\n          assert(result.exit_code == 2, \"agent lookup didn't exit properly: (#{result.exit_code})\")\n          assert_match(/global_key-common_file/m, result.stdout,\n                       'agent lookup didn\\'t find global key')\n          assert_match(/environment_key-env_file/m, result.stdout,\n                       'agent lookup didn\\'t find environment_key')\n          assert_match(/hocon value/m, result.stdout,\n                       'agent lookup didn\\'t find environment_key2')\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/modulepath.rb",
    "content": "test_name 'Supports vendored modules' do\n  tag 'risk:high'\n\n  # beacon custom type emits a message so we can tell where the\n  # type was loaded from, e.g. vendored, global, and whether the\n  # type was loaded locally or pluginsynced from the master.\n  def beacon_type(message)\n    return <<END\n    Puppet::Type.newtype(:beacon) do\n      newparam(:name,  :namevar => true)\n      newproperty(:message) do\n        def sync; true; end\n        def retrieve; :absent; end\n        def insync?(is); false; end\n        defaultto { \"#{message}\" }\n      end\n    end\nEND\n  end\n\n  def global_modules(host)\n    if host.platform =~ /windows/\n      '/cygdrive/c/ProgramData/PuppetLabs/code/modules'\n    else\n      '/etc/puppetlabs/code/modules'\n    end\n  end\n\n  def vendor_modules(host)\n    if host.platform =~ /windows/\n      # escape spaces\n      \"/cygdrive/c/Program\\\\ Files/Puppet\\\\ Labs/Puppet/puppet/vendor_modules\"\n    else\n      '/opt/puppetlabs/puppet/vendor_modules'\n    end\n  end\n\n  teardown do\n    hosts.each do |host|\n      on(host, \"rm -rf #{vendor_modules(host)}/beacon\")\n      on(host, \"rm -rf #{global_modules(host)}/beacon\")\n\n      libdir = host.puppet['vardir']\n      on(host, \"rm -rf #{libdir}\")\n    end\n\n    on(master, \"rm -rf /etc/puppetlabs/code/environments/production/modules/beacon\")\n    on(master, \"rm -f /etc/puppetlabs/code/environments/production/manifests/site.pp\")\n  end\n\n  step 'delete libdir' do\n    hosts.each do |host|\n      on(host, \"rm -rf #{host.puppet['libdir']}\")\n    end\n  end\n\n  step 'create vendored module with a custom type' do\n    hosts.each do |host|\n      vendor_dir = vendor_modules(host)\n      on(host, \"mkdir -p #{vendor_dir}/beacon/lib/puppet/type\")\n\n      # unescape, because net-scp escapes\n      vendor_dir.gsub!(/\\\\/, '')\n      create_remote_file(host, \"#{vendor_dir}/beacon/lib/puppet/type/beacon.rb\", beacon_type(\"vendored module from #{host}\"))\n    end\n  end\n\n  step 'vendored modules work locally' do\n    hosts.each do |host|\n      on(host, puppet(\"apply -e \\\"beacon { 'ping': }\\\"\")) do |result|\n        assert_match(/defined 'message' as 'vendored module from #{host}'/, result.stdout)\n      end\n    end\n  end\n\n  step 'vendored modules can be excluded' do\n    hosts.each do |host|\n      on(host, puppet(\"describe --vendormoduledir '' beacon\"), accept_all_exit_codes: true) do |result|\n        assert_match(/Unknown type beacon/, result.stdout)\n      end\n    end\n  end\n\n  step 'global modules override vendored modules' do\n    agents.each do |agent|\n      # skip the agent on the master, as we don't want to install the\n      # global module on the master until later\n      next if agent == master\n\n      global_dir = global_modules(agent)\n      on(agent, \"mkdir -p #{global_dir}/beacon/lib/puppet/type\")\n\n      # global_dir doesn't have spaces, so don't need to escape\n      create_remote_file(agent, \"#{global_dir}/beacon/lib/puppet/type/beacon.rb\", beacon_type(\"global module from #{agent}\"))\n\n      on(agent, puppet(\"apply -e \\\"beacon { 'ping': }\\\"\")) do |result|\n        assert_match(/defined 'message' as 'global module from #{agent}'/, result.stdout)\n      end\n    end\n  end\n\n  step \"prepare server\" do\n    create_remote_file(master, \"/etc/puppetlabs/code/environments/production/manifests/site.pp\", \"beacon { 'ping': }\")\n    on(master, \"chown -R puppet:puppet /etc/puppetlabs/code/environments/production/manifests/site.pp\")\n    on(master, \"chown -R puppet:puppet #{vendor_modules(master)}\")\n  end\n\n  with_puppet_running_on(master, {}) do\n    step \"agent doesn't pluginsync the vendored module, instead using its local vendored module\" do\n      agents.each do |agent|\n        on(agent, puppet(\"agent -t\"), :acceptable_exit_codes => [0,2]) do |result|\n          assert_match(/defined 'message' as 'vendored module from #{agent}'/, result.stdout)\n        end\n      end\n    end\n\n    step \"agent downloads and uses newly installed global module from the server\" do\n      global_dir = global_modules(master)\n      on(master, \"mkdir -p #{global_dir}/beacon/lib/puppet/type\")\n      create_remote_file(master, \"#{global_dir}/beacon/lib/puppet/type/beacon.rb\", beacon_type(\"server module from #{master}\"))\n      on(master, \"chown -R puppet:puppet #{global_dir}\")\n\n      agents.each do |agent|\n        on(agent, puppet(\"agent -t\"), :acceptable_exit_codes => [0,2]) do |result|\n          assert_match(/defined 'message' as 'server module from #{master}'/, result.stdout)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ordering/master_agent_application.rb",
    "content": "test_name \"Puppet applies resources without dependencies in file order over the network\"\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\ntestdir = master.tmpdir('application_order')\n\napply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n  File {\n    ensure => directory,\n    mode => \"0750\",\n    owner => #{master.puppet['user']},\n    group => #{master.puppet['group']},\n  }\n  file {\n    '#{testdir}':;\n    '#{testdir}/environments':;\n    '#{testdir}/environments/production':;\n    '#{testdir}/environments/production/manifests':;\n    '#{testdir}/environments/production/manifests/site.pp':\n      ensure => file,\n      mode => \"0640\",\n      content => '\nnotify { \"first\": }\nnotify { \"second\": }\nnotify { \"third\": }\nnotify { \"fourth\": }\nnotify { \"fifth\": }\nnotify { \"sixth\": }\nnotify { \"seventh\": }\nnotify { \"eighth\": }\n      ';\n  }\nMANIFEST\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n   }\n}\n\nwith_puppet_running_on(master, master_opts) do\n  agents.each do |agent|\n    on(agent, puppet('agent', \"--no-daemonize --onetime --verbose\")) do |result|\n      if result.stdout !~ /Notice: first.*Notice: second.*Notice: third.*Notice: fourth.*Notice: fifth.*Notice: sixth.*Notice: seventh.*Notice: eighth/m\n        fail_test \"Output did not include the notify resources in the correct order\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/calling_all_functions.rb",
    "content": "test_name 'Calling all functions.. test in progress!'\n\ntag 'audit:high',\n    'audit:acceptance'\n\n# create single manifest calling all functions\nstep 'Apply manifest containing all function calls'\ndef manifest_call_each_function_from_array(functions)\n  manifest = ''\n  # use index to work around puppet's immutable variables\n  # use variables so we can concatenate strings\n  functions.each_with_index do |function,index|\n    if function[:rvalue]\n      manifest << \"$pre#{index} = \\\"sayeth #{function[:name].capitalize}: Scope(Class[main]): \\\" \"\n      manifest << \"$output#{index} = #{function[:name]}(#{function[:args]}) \"\n      manifest << \"#{function[:lambda]} notice \\\"${pre#{index}}${output#{index}}\\\"\\n\"\n    else\n      manifest << \"$pre#{index} = \\\"sayeth #{function[:name].capitalize}: \\\" \"\n      manifest << \"notice \\\"${pre#{index}}\\\"\\n\"\n      manifest << \"#{function[:name]}(#{function[:args]}) \"\n      manifest << \"#{function[:lambda]}\\n\"\n    end\n  end\n  manifest\nend\n\n\nagents.each do |agent|\n  testdir = agent.tmpdir('calling_all_functions')\n  # if agent[\"platform\"] =~ /win/\n  #   generator = {:args => '\"c:/windows/system32/tasklist.exe\"', :expected => /\\nImage Name/}\n  # else\n  #   generator = {:args => '\"/bin/date\"',                        :expected => /\\w\\w\\w.*?\\d\\d:\\d\\d\\:\\d\\d/}\n  # end\n\n  # create list of 3x functions and args\n  # notes: hiera functions are well tested elsewhere, included for completeness\n  #   special cases: contain (call this from call_em_all)\n  #   do fail last because it errors out\n\n  functions_3x = [\n    {:name => :alert,            :args => '\"consider yourself on alert\"',      :lambda => nil, :expected => 'consider yourself on alert', :rvalue => false},\n    {:name => :binary_file,      :args => '\"call_em_all/rickon.txt\"',          :lambda => nil, :expected => '', :rvalue => true},\n    #{:name => :break,            :args => '',                                  :lambda => nil, :expected => '', :rvalue => false},\n    # this is explicitly called from call_em_all module which is included below\n    #{:name => :contain,          :args => 'call_em_all',                       :lambda => nil, :expected => '', :rvalue => true},\n    # below doens't instance the resource. no output\n    {:name => :create_resources, :args => 'notify, {\"w\"=>{message=>\"winter is coming\"}}',      :lambda => nil, :expected => '', :rvalue => false},\n    {:name => :crit,             :args => '\"consider yourself critical\"',      :lambda => nil, :expected => 'consider yourself critical', :rvalue => false},\n    {:name => :debug,            :args => '\"consider yourself bugged\"',        :lambda => nil, :expected => '', :rvalue => false}, # no output expected unless run with debug\n    {:name => :defined,          :args => 'File[\"/tmp\"]',                      :lambda => nil, :expected => 'false', :rvalue => true},\n    {:name => :dig,              :args => '[100]',                             :lambda => nil, :expected => '[100]', :rvalue => true},\n    # Expect sha256 hash value for the digest\n    {:name => :digest,           :args => '\"Sansa\"',                           :lambda => nil, :expected => '4ebf3a5527313f06c7965749d7764c15cba6fe86da11691ca9bd0ce448563979', :rvalue => true},\n    {:name => :emerg,            :args => '\"consider yourself emergent\"',      :lambda => nil, :expected => 'consider yourself emergent', :rvalue => false},\n    {:name => :err,              :args => '\"consider yourself in err\"',        :lambda => nil, :expected => 'consider yourself in err', :rvalue => false},\n    {:name => :file,             :args => '\"call_em_all/rickon.txt\"',          :lambda => nil, :expected => 'who?', :rvalue => true},\n    {:name => :fqdn_rand,        :args => '100000',                            :lambda => nil, :expected => /Fqdn_rand: Scope\\(Class\\[main\\]\\): \\d{1,5}/, :rvalue => true},\n    # generate requires a fully qualified exe; which requires specifics for windows vs posix\n    #{:name => :generate,         :args => generator[:args],                    :lambda => nil, :expected => generator[:expected], :rvalue => true},\n    {:name => :hiera_array,      :args => 'date,default_array',                :lambda => nil, :expected => 'default_array', :rvalue => true},\n    {:name => :hiera_hash,       :args => 'date,default_hash',                 :lambda => nil, :expected => 'default_hash', :rvalue => true},\n    {:name => :hiera_include,    :args => 'date,call_em_all',                  :lambda => nil, :expected => '', :rvalue => false},\n    {:name => :hiera,            :args => 'date,default_date',                 :lambda => nil, :expected => 'default_date', :rvalue => true},\n    {:name => :include,          :args => 'call_em_all',                       :lambda => nil, :expected => '', :rvalue => false},\n    {:name => :info,             :args => '\"consider yourself informed\"',      :lambda => nil, :expected => '', :rvalue => false}, # no ouput unless in debug mode\n    {:name => :inline_template,  :args => '\\'empty<%= @x %>space\\'',           :lambda => nil, :expected => 'emptyspace', :rvalue => true},\n    # test the living life out of this thing in lookup.rb, and it doesn't allow for a default value\n    #{:name => :lookup,           :args => 'date,lookup_date',                  :lambda => nil, :expected => '', :rvalue => true},  # well tested elsewhere\n    {:name => :sha256,           :args => '\"Bran\"',                            :lambda => nil, :expected => '824264f7f73d6026550b52a671c50ad0c4452af66c24f3784e30f515353f2ce0', :rvalue => true},\n    # Integer.new\n    {:name => :Integer,          :args => '\"100\"',                             :lambda => nil, :expected => '100', :rvalue => true},\n    {:name => :notice,           :args => '\"consider yourself under notice\"',  :lambda => nil, :expected => 'consider yourself under notice', :rvalue => false},\n    {:name => :realize,          :args => 'User[arya]',                        :lambda => nil, :expected => '', :rvalue => false},  # TODO: create a virtual first\n    {:name => :regsubst,         :args => '\"Cersei\",\"Cer(\\\\\\\\w)ei\",\"Daenery\\\\\\\\1\"',:lambda => nil, :expected => 'Daenerys', :rvalue => true},\n    # explicitly called in call_em_all; implicitly called by the include above\n    #{:name => :require,          :args => '[4,5,6]',                          :lambda => nil, :expected => '', :rvalue => true},\n    # 4x output contains brackets around scanf output\n    {:name => :scanf,            :args => '\"Eddard Stark\",\"%6s\"',              :lambda => nil, :expected => '[Eddard]', :rvalue => true},\n    {:name => :sha1,             :args => '\"Sansa\"',                           :lambda => nil, :expected => '4337ce5e4095e565d51e0ef4c80df1fecf238b29', :rvalue => true},\n    {:name => :shellquote,       :args => '[\"-1\", \"--two\"]',                   :lambda => nil, :expected => '-1 --two', :rvalue => true},\n    # 4x output contains brackets around split output and commas btwn values\n    {:name => :split,            :args => '\"9,8,7\",\",\"',                       :lambda => nil, :expected => '[9, 8, 7]', :rvalue => true},\n    {:name => :sprintf,          :args => '\"%b\",\"123\"',                        :lambda => nil, :expected => '1111011', :rvalue => true},\n    {:name => :step,             :args => '[100,99],1',                        :lambda => nil, :expected => 'Iterator[Integer]-Value', :rvalue => true},\n    # explicitly called in call_em_all\n    #{:name => :tag,              :args => '[4,5,6]',                          :lambda => nil, :expected => '', :rvalue => true},\n    {:name => :tagged,           :args => '\"yer_it\"',                          :lambda => nil, :expected => 'false', :rvalue => true},\n    {:name => :template,         :args => '\"call_em_all/template.erb\"',        :lambda => nil, :expected => 'no defaultsno space', :rvalue => true},\n    {:name => :type,             :args => '42',                                :lambda => nil, :expected => 'Integer[42, 42]', :rvalue => true},\n    {:name => :versioncmp,       :args => '\"1\",\"2\"',                           :lambda => nil, :expected => '-1', :rvalue => true},\n    {:name => :warning,          :args => '\"consider yourself warned\"',        :lambda => nil, :expected => 'consider yourself warned', :rvalue => false},\n    # do this one last or it will not allow the others to run.\n    {:name => :fail,             :args => '\"Jon Snow\"',                        :lambda => nil, :expected => /Error:.*Jon Snow/, :rvalue => false},\n  ]\n\n  puppet_version = on(agent, puppet('--version')).stdout.chomp\n\n  functions_4x = [\n    {:name => :assert_type,      :args => '\"String[1]\", \"Valar morghulis\"',    :lambda => nil, :expected => 'Valar morghulis', :rvalue => true},\n    {:name => :each,             :args => '[1,2,3]',                           :lambda => '|$x| {$x}', :expected => '[1, 2, 3]', :rvalue => true},\n    {:name => :epp,              :args => '\"call_em_all/template.epp\",{x=>droid}', :lambda => nil, :expected => 'This is the droid you are looking for!', :rvalue => true},\n    {:name => :filter,           :args => '[4,5,6]',                           :lambda => '|$x| {true}', :expected => '[4, 5, 6]', :rvalue => true},\n    # find_file() called by binary_file\n    #{:name => :find_file,           :args => '[4,5,6]',                           :lambda => '|$x| {true}', :expected => '[4, 5, 6]', :rvalue => true},\n    {:name => :inline_epp,       :args => '\\'<%= $x %>\\',{x=>10}',             :lambda => nil, :expected => '10', :rvalue => true},\n    #{:name => :lest,             :args => '100',                               :lambda => '\"100\"', :expected => '100', :rvalue => true},\n    {:name => :map,              :args => '[7,8,9]',                           :lambda => '|$x| {$x * $x}', :expected => '[49, 64, 81]', :rvalue => true},\n    {:name => :match,            :args => '\"abc\", /b/',                        :lambda => nil, :expected => '[b]', :rvalue => true},\n    #{:name => :next,             :args => '100',                               :lambda => nil, :expected => '100', :rvalue => true},\n    {:name => :reduce,           :args => '[4,5,6]',                           :lambda => '|$sum, $n| { $sum+$n }', :expected => '15', :rvalue => true},\n    #{:name => :return,           :args => '100',                               :lambda => nil, :expected => '100', :rvalue => true},\n    {:name => :reverse_each,     :args => '[100,99]',                          :lambda => nil, :expected => 'Iterator[Integer]-Value', :rvalue => true},\n    #         :reuse,:recycle\n    {:name => :slice,            :args => '[1,2,3,4,5,6], 2',                  :lambda => nil, :expected => '[[1, 2], [3, 4], [5, 6]]', :rvalue => true},\n    {:name => :strftime,         :args => 'Timestamp(\"4216-09-23T13:14:15.123 UTC\"), \"%C\"',    :lambda => nil, :expected => '42', :rvalue => true},\n    {:name => :then,             :args => '100',                               :lambda => '|$x| {$x}', :expected => '100', :rvalue => true},\n    {:name => :with,             :args => '1, \"Catelyn\"',                      :lambda => '|$x, $y| {\"$x, $y\"}', :expected => '1, Catelyn', :rvalue => true},\n  ]\n\n  module_manifest = <<PP\nFile {\n  ensure => directory,\n}\nfile {\n  '#{testdir}':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/modules':;\n  '#{testdir}/environments/production/modules/tagged':;\n  '#{testdir}/environments/production/modules/tagged/manifests':;\n  '#{testdir}/environments/production/modules/contained':;\n  '#{testdir}/environments/production/modules/contained/manifests':;\n  '#{testdir}/environments/production/modules/required':;\n  '#{testdir}/environments/production/modules/required/manifests':;\n  '#{testdir}/environments/production/modules/call_em_all':;\n  '#{testdir}/environments/production/modules/call_em_all/manifests':;\n  '#{testdir}/environments/production/modules/call_em_all/templates':;\n  '#{testdir}/environments/production/modules/call_em_all/files':;\n}\nfile { '#{testdir}/environments/production/modules/tagged/manifests/init.pp':\n  ensure  => file,\n  content => 'class tagged {\n    notice tagged\n    tag     yer_it\n    }',\n}\nfile { '#{testdir}/environments/production/modules/required/manifests/init.pp':\n  ensure  => file,\n  content => 'class required {\n    notice required\n    }',\n}\nfile { '#{testdir}/environments/production/modules/contained/manifests/init.pp':\n  ensure  => file,\n  content => 'class contained {\n    notice contained\n    }',\n}\nfile { '#{testdir}/environments/production/modules/call_em_all/manifests/init.pp':\n  ensure  => file,\n  content => 'class call_em_all {\n    notice call_em_all\n    contain contained\n    require required\n    tag     yer_it\n    }',\n}\nfile { '#{testdir}/environments/production/modules/call_em_all/files/rickon.txt':\n  ensure  => file,\n  content => 'who?',\n}\nfile { '#{testdir}/environments/production/modules/call_em_all/templates/template.epp':\n  ensure  => file,\n  content => 'This is the <%= $x %> you are looking for!',\n}\nfile { '#{testdir}/environments/production/modules/call_em_all/templates/template.erb':\n  ensure  => file,\n  content => 'no defaults<%= @x %>no space',\n}\nPP\n\n  apply_manifest_on(agent, module_manifest, :catch_failures => true)\n\n  scope = 'Scope(Class[main]):'\n  # apply the 4x function manifest with future parser\n  puppet_apply_options = {:modulepath => \"#{testdir}/environments/production/modules/\",\n     :acceptable_exit_codes => 1}\n  puppet_apply_options[:future_parser] = true if puppet_version =~ /\\A3\\./\n  apply_manifest_on(agent, manifest_call_each_function_from_array(functions_4x), puppet_apply_options) do |result|\n       functions_4x.each do |function|\n         expected = \"#{function[:name].capitalize}: #{scope} #{function[:expected]}\"\n         unless agent['locale'] == 'ja'\n           assert_match(expected, result.output,\n                        \"#{function[:name]} output didn't match expected value\")\n         end\n       end\n     end\n\n   file_path = agent.tmpfile('apply_manifest.pp')\n\n   create_remote_file(agent, file_path, manifest_call_each_function_from_array(functions_3x))\n\n   trusted_3x = puppet_version =~ /\\A3\\./ ? '--trusted_node_data ' : ''\n   on(agent, puppet(\"apply #{trusted_3x} --color=false  --modulepath #{testdir}/environments/production/modules/ #{file_path}\"),\n      :acceptable_exit_codes => 1 ) do |result|\n        functions_3x.each do |function|\n          # append the function name to the matcher so it's more expressive\n          if function[:expected].is_a?(String)\n            if function[:name] == :fail\n              expected = function[:expected]\n            elsif function[:name] == :crit\n              expected = \"#{function[:name].capitalize}ical: #{scope} #{function[:expected]}\"\n            elsif function[:name] == :emerg\n              expected = \"#{function[:name].capitalize}ency: #{scope} #{function[:expected]}\"\n            elsif function[:name] == :err\n              expected = \"#{function[:name].capitalize}or: #{scope} #{function[:expected]}\"\n            elsif function[:expected] == ''\n              expected = \"#{function[:name].capitalize}: #{function[:expected]}\"\n            else\n              expected = \"#{function[:name].capitalize}: #{scope} #{function[:expected]}\"\n            end\n          elsif function[:expected].is_a?(Regexp)\n            expected = function[:expected]\n          else\n            raise 'unhandled function expectation type (we allow String or Regexp)'\n          end\n\n          unless agent['locale'] == 'ja'\n            assert_match(expected, result.output, \"#{function[:name]} output didn't match expected value\")\n          end\n        end\n     end\n\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/hiera/lookup_data.rb",
    "content": "test_name \"Lookup data using the hiera parser function\"\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor'    # Master is not required for this test. Replace with agents.each\n\ntestdir = master.tmpdir('hiera')\n\nstep 'Setup'\n\napply_manifest_on(master, <<-PP, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile {\n  '#{testdir}':;\n  '#{testdir}/hieradata':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/manifests':;\n  '#{testdir}/environments/production/modules':;\n}\n\nfile { '#{testdir}/hiera.yaml':\n  ensure  => file,\n  content => '---\n    :backends:\n      - \"yaml\"\n    :logger: \"console\"\n    :hierarchy:\n      - \"%{environment}\"\n      - \"global\"\n\n    :yaml:\n      :datadir: \"#{testdir}/hieradata\"\n  ',\n  mode => \"0640\",\n}\n\nfile { '#{testdir}/hieradata/global.yaml':\n  ensure  => file,\n  content => \"---\n    port: 8080\n  \",\n  mode => \"0640\",\n}\n\nfile {\n  '#{testdir}/environments/production/modules/apache':;\n  '#{testdir}/environments/production/modules/apache/manifests':;\n}\n\nfile { '#{testdir}/environments/production/modules/apache/manifests/init.pp':\n  ensure => file,\n  content => '\n    class apache {\n      $port = hiera(\"port\")\n\n      notify { \"port from hiera\":\n        message => \"apache server port: ${port}\"\n      }\n    }',\n  mode => \"0640\",\n}\n\nfile { '#{testdir}/environments/production/manifests/site.pp':\n  ensure => file,\n  content => \"\n    node default {\n      include apache\n    }\",\n  mode => \"0640\",\n}\nPP\n\nstep \"Try to lookup string data\"\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n    'hiera_config' => \"#{testdir}/hiera.yaml\",\n  },\n}\n\nwith_puppet_running_on master, master_opts, testdir do\n  agents.each do |agent|\n    on(agent, puppet('agent', \"-t\"), :acceptable_exit_codes => [2]) do |result|\n      assert_match('apache server port: 8080', result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/hiera_array/lookup_data.rb",
    "content": "test_name \"Lookup data using the hiera_array parser function\"\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor'    # Master is not required for this test. Replace with agents.each\n\ntestdir = master.tmpdir('hiera')\n\nstep 'Setup'\n\napply_manifest_on(master, <<-PP, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile {\n  '#{testdir}':;\n  '#{testdir}/hieradata':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/manifests':;\n  '#{testdir}/environments/production/modules':;\n}\n\nfile { '#{testdir}/hiera.yaml':\n  ensure  => file,\n  content => '---\n    :backends:\n      - \"yaml\"\n    :logger: \"console\"\n    :hierarchy:\n      - \"%{environment}\"\n      - \"global\"\n\n    :yaml:\n      :datadir: \"#{testdir}/hieradata\"\n  ',\n  mode => \"0640\";\n}\n\nfile { '#{testdir}/hieradata/global.yaml':\n  ensure  => file,\n  content => \"---\n    port: '8080'\n    ntpservers: ['global.ntp.puppetlabs.com']\n  \",\n  mode => \"0640\";\n}\n\nfile { '#{testdir}/hieradata/production.yaml':\n  ensure  => file,\n  content => \"---\n    ntpservers: ['production.ntp.puppetlabs.com']\n  \",\n  mode => \"0640\";\n}\n\nfile {\n  '#{testdir}/environments/production/modules/ntp':;\n  '#{testdir}/environments/production/modules/ntp/manifests':;\n}\n\nfile { '#{testdir}/environments/production/modules/ntp/manifests/init.pp':\n  ensure => file,\n  content => '\n    class ntp {\n      $ntpservers = hiera_array(\"ntpservers\")\n\n      define print {\n        $server = $name\n        notify { \"ntpserver ${server}\": }\n      }\n\n      ntp::print { $ntpservers: }\n    }',\n  mode => \"0640\";\n}\n\nfile { '#{testdir}/environments/production/manifests/site.pp':\n  ensure => file,\n  content => \"\n    node default {\n      include ntp\n    }\",\n  mode => \"0640\";\n}\nPP\n\nstep \"Try to lookup array data\"\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n    'hiera_config' => \"#{testdir}/hiera.yaml\",\n  },\n}\n\nwith_puppet_running_on master, master_opts, testdir do\n  agents.each do |agent|\n    on(agent, puppet('agent', \"-t\"), :acceptable_exit_codes => [2]) do |result|\n      assert_match('ntpserver global.ntp.puppetlabs.com', result.stdout)\n      assert_match('ntpserver production.ntp.puppetlabs.com', result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/hiera_hash/lookup_data.rb",
    "content": "test_name \"Lookup data using the hiera_hash parser function\"\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor'    # Master is not required for this test. Replace with agents.each\n\ntestdir = master.tmpdir('hiera')\n\nstep 'Setup'\n\napply_manifest_on(master, <<-PP, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\nfile {\n  '#{testdir}':;\n  '#{testdir}/hieradata':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/manifests':;\n  '#{testdir}/environments/production/modules':;\n}\n\nfile { '#{testdir}/hiera.yaml':\n  ensure  => file,\n  content => '---\n    :backends:\n      - \"yaml\"\n    :logger: \"console\"\n    :hierarchy:\n      - \"%{environment}\"\n      - \"global\"\n\n    :yaml:\n      :datadir: \"#{testdir}/hieradata\"\n  ',\n  mode => \"0640\";\n}\n\nfile { '#{testdir}/hieradata/global.yaml':\n  ensure  => file,\n  content => \"---\n    database_user:\n      name: postgres\n      uid: 500\n      gid: 500\n  \",\n  mode => \"0640\";\n}\n\nfile { '#{testdir}/hieradata/production.yaml':\n  ensure  => file,\n  content => \"---\n    database_user:\n      shell: '/bin/bash'\n  \",\n  mode => \"0640\";\n}\n\nfile {\n  '#{testdir}/environments/production/modules/ntp/':;\n  '#{testdir}/environments/production/modules/ntp/manifests':;\n}\n\nfile { '#{testdir}/environments/production/modules/ntp/manifests/init.pp':\n  ensure => file,\n  content => 'class ntp {\n    $database_user = hiera_hash(\"database_user\")\n\n    notify { \"the database user\":\n      message => \"name: ${database_user[\"name\"]} shell: ${database_user[\"shell\"]}\"\n    }\n  }',\n  mode => \"0640\";\n}\n\nfile { '#{testdir}/environments/production/manifests/site.pp':\n  ensure => file,\n  content => \"\n    node default {\n      include ntp\n    }\",\n  mode => \"0640\";\n}\nPP\n\nstep \"Try to lookup hash data\"\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n    'hiera_config' => \"#{testdir}/hiera.yaml\",\n  },\n}\n\nwith_puppet_running_on master, master_opts, testdir do\n  agents.each do |agent|\n    on(agent, puppet('agent', \"-t\"), :acceptable_exit_codes => [2]) do |result|\n      assert_match(\"name: postgres shell: /bin/bash\", result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/hiera_in_templates.rb",
    "content": "test_name \"Calling Hiera function from inside templates\"\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor'    # Master is not required for this test. Replace with agents.each\n\n@module_name = \"hieratest\"\n@coderoot = master.tmpdir(\"#{@module_name}\")\n\n@msg_default = 'message from default.yaml'\n@msg_production = 'message from production.yaml'\n@msg1os = 'message1 from {osfamily}.yaml'\n@msg2os = 'message2 from {osfamily}.yaml'\n@msg_fqdn = 'messsage from {fqdn}.yaml'\n\n@k1 = 'key1'\n@k2 = 'key2'\n@k3 = 'key3'\n\n@hval2p = 'hash_value2 from production.yaml'\n@hval3p = 'hash_value3 from production.yaml'\n@hval1os = 'hash_value1 from {osfamily}.yaml'\n@hval2os = 'hash_value2 from {osfamily}.yaml'\n\n@h_m_call = \"hiera\\\\('message'\\\\)\"\n@h_h_call = \"hiera\\\\('hash_value'\\\\)\"\n@h_i_call = \"hiera\\\\('includes'\\\\)\"\n@ha_m_call = \"hiera_array\\\\('message'\\\\)\"\n@ha_i_call = \"hiera_array\\\\('includes'\\\\)\"\n@hh_h_call = \"hiera_hash\\\\('hash_value'\\\\)\"\n\n@mod_default_msg = 'This file created by mod_default.'\n@mod_osfamily_msg = 'This file created by mod_osfamily.'\n@mod_production_msg = 'This file created by mod_production.'\n@mod_fqdn_msg = 'This file created by mod_fqdn.'\n\n@master_opts = {\n  'main' => {\n    'environmentpath' => \"#{@coderoot}/environments\",\n    'hiera_config' => \"#{@coderoot}/hiera.yaml\",\n  },\n}\n\n\ndef create_environment(osfamilies, tmp_dirs)\n  envroot = \"#{@coderoot}/environments\"\n  production = \"#{envroot}/production\"\n  modroot = \"#{production}/modules\"\n  moduledir = \"#{modroot}/#{@module_name}\"\n  hieradir = \"#{@coderoot}/hieradata\"\n\n  osfamily_yamls = \"\"\n  osfamilies.each do |osf|\n    new_yaml = <<NEW_YAML\nfile {\"#{hieradir}/#{osf}.yaml\":\n  content => \"\n---\nmessage: [\n  '#{@msg1os}',\n  '#{@msg2os}',\n]\nincludes: '#{@module_name}::mod_osfamily'\nhash_value:\n  #{@k1}: '#{@hval1os}'\n  #{@k2}: '#{@hval2os}'\n\"\n}\nNEW_YAML\n    osfamily_yamls += new_yaml\n  end\n  environ = <<ENV\n\nFile {\n  ensure => file,\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n  mode   => \"0644\",\n}\n\nfile {\n  [\n    \"#{@coderoot}\",\n    \"#{envroot}\",\n    \"#{production}\",\n    \"#{production}/modules\",\n    \"#{production}/manifests\",\n    \"#{hieradir}\",\n    \"#{moduledir}\",\n    \"#{moduledir}/examples\",\n    \"#{moduledir}/manifests\",\n    \"#{moduledir}/templates\",\n  ] :\n  ensure => directory,\n}\n\nfile { '#{production}/manifests/site.pp':\n  ensure => file,\n  content => \"\nnode default {\n  \\\\$msgs = hiera_array('message')\n  notify {\\\\$msgs:}\n  class {'#{@module_name}':\n    result_dir => hiera('result_dir')[\\\\$facts['networking']['hostname']],\n  }\n}\n\",\n}\n\n\nfile {\"#{@coderoot}/hiera.yaml\":\n  content => \"\n---\n:backends:\n  - yaml\n\n:yaml:\n  :datadir: #{@coderoot}/hieradata\n\n:hierarchy:\n  - \\\\\"%{clientcert}\\\\\"\n  - \\\\\"%{environment}\\\\\"\n  - \\\\\"%{os.family}\\\\\"\n  - \\\\\"default\\\\\"\n\"\n}\n\nfile {\"#{hieradir}/default.yaml\":\n  content => \"\n---\nmessage: '#{@msg_default}'\nincludes: '#{@module_name}::mod_default'\nresult_dir:\n#{tmp_dirs}\n\"\n}\n\n#{osfamily_yamls}\n\n\nfile {\"#{hieradir}/production.yaml\":\n  content => \"\n---\nmessage: '#{@msg_production}'\nincludes: '#{@module_name}::mod_production'\nhash_value:\n  #{@k2}: '#{@hval2p}'\n  #{@k3}: '#{@hval3p}'\n\"\n}\n\nfile {\"#{hieradir}/#{$fqdn}.yaml\":\n  content => \"\n---\nmessage: '#{@msg_fqdn}'\nincludes: '#{@module_name}::mod_fqdn'\n\"\n}\n\nfile {\"#{moduledir}/examples/init.pp\":\n  content => \"\ninclude #{@module_name}\n\"\n}\n\nfile { \"#{moduledir}/manifests/init.pp\":\n  content => \"\nclass #{@module_name} (\n  \\\\$result_dir,\n) {\n  file { \\\\$result_dir:\n    ensure => directory,\n    mode   => '0755',\n  }\n  file {\\\\\\\"\\\\\\${result_dir}/#{@module_name}_results_epp\\\\\\\":\n    ensure  => file,\n    mode  => '0644',\n    content => epp('#{@module_name}/hieratest_results_epp.epp'),\n  }\n  file {\\\\\\\"\\\\\\${result_dir}/#{@module_name}_results_erb\\\\\\\":\n    ensure  => file,\n    mode  => '0644',\n    content => template('#{@module_name}/hieratest_results_erb.erb'),\n  }\n}\n\"\n}\n\nfile { \"#{moduledir}/manifests/mod_default.pp\":\n  content => \"\nclass #{@module_name}::mod_default {\n  \\\\$result_dir = hiera('result_dir')[\\\\$facts['networking']['hostname']]\n  notify{\\\\\"module mod_default invoked.\\\\\\\\n\\\\\":}\n  file {\\\\\\\"\\\\\\${result_dir}/mod_default\\\\\\\":\n    ensure  => 'file',\n    mode    => '0644',\n    content => \\\\\\\"#{@mod_default_msg}\\\\\\\\n\\\\\\\",\n  }\n}\n\"\n}\n\nfile { \"#{moduledir}/manifests/mod_osfamily.pp\":\n  content => \"\nclass #{@module_name}::mod_osfamily {\n  \\\\$result_dir = hiera('result_dir')[\\\\$facts['networking']['hostname']]\n  notify{\\\\\"module mod_osfamily invoked.\\\\\\\\n\\\\\":}\n  file {\\\\\\\"\\\\\\${result_dir}/mod_osfamily\\\\\\\":\n    ensure  => 'file',\n    mode    => '0644',\n    content => \\\\\\\"#{@mod_osfamily_msg}\\\\\\\\n\\\\\\\",\n  }\n}\n\"\n}\n\nfile { \"#{moduledir}/manifests/mod_production.pp\":\n  content => \"\nclass #{@module_name}::mod_production {\n  \\\\$result_dir = hiera('result_dir')[\\\\$facts['networking']['hostname']]\n  notify{\\\\\"module mod_production invoked.\\\\\\\\n\\\\\":}\n  file {\\\\\\\"\\\\\\${result_dir}/mod_production\\\\\\\":\n    ensure  => 'file',\n    mode    => '0644',\n    content => '#{@mod_production_msg}',\n  }\n}\n\"\n}\n\nfile { \"#{moduledir}/manifests/mod_fqdn.pp\":\n  content => \"\nclass #{@module_name}::mod_fqdn {\n  \\\\$result_dir = hiera('result_dir')[\\\\$facts['networking']['hostname']]\n  notify{\\\\\"module mod_fqdn invoked.\\\\\\\\n\\\\\":}\n  file {\\\\\\\"\\\\\\${result_dir}/mod_fqdn\\\\\\\":\n    ensure  => 'file',\n    mode    => '0644',\n    content => \\\\\\\"#{@mod_fqdn_msg}\\\\\\\\n\\\\\\\",\n  }\n}\n\"\n}\n\nfile { \"#{moduledir}/templates/hieratest_results_epp.epp\":\n  content => \"\nhiera('message'): <%= hiera('message') %>\nhiera('hash_value'): <%= hiera('hash_value') %>\nhiera('includes'): <%= hiera('includes') %>\nhiera_array('message'): <%= hiera_array('message') %>\nhiera_array('includes'): <%= hiera_array('includes') %>\nhiera_hash('hash_value'): <%= hiera_hash('hash_value') %>\nhiera_include('includes'): <%= hiera_include('includes') %>\n\"\n}\n\nfile { \"#{moduledir}/templates/hieratest_results_erb.erb\":\n  content => \"\nhiera('message'): <%= scope().call_function('hiera', ['message']) %>\nhiera('hash_value'): <%= scope().call_function('hiera', ['hash_value']) %>\nhiera('includes'): <%= scope().call_function('hiera', ['includes']) %>\nhiera_array('message'): <%= scope().call_function('hiera_array', ['message']) %>\nhiera_array('includes'): <%= scope().call_function('hiera_array', ['includes']) %>\nhiera_hash('hash_value'): <%= scope().call_function('hiera_hash', ['hash_value']) %>\n\"\n}\n\nENV\n  environ\nend\n\ndef find_osfamilies\n  family_hash = {}\n  agents.each do |agent|\n    res = on(agent, facter(\"os.family\"))\n    osf = res.stdout.chomp\n    family_hash[osf] = 1\n  end\n  family_hash.keys\nend\n\ndef find_tmp_dirs\n  tmp_dirs = \"\"\n  host_to_result_dir = {}\n  agents.each do |agent|\n    h = on(agent, facter(\"networking.hostname\")).stdout.chomp\n    t = agent.tmpdir(\"#{@module_name}_results\")\n    tmp_dirs += \"  #{h}: '#{t}'\\n\"\n    host_to_result_dir[h] = t\n  end\n  result = {\n    'tmp_dirs' => tmp_dirs,\n    'host_to_result_dir' => host_to_result_dir\n  }\n  result\nend\n\n\nstep 'Setup'\n\nwith_puppet_running_on master, @master_opts, @coderoot do\n  res = find_tmp_dirs\n  tmp_dirs = res['tmp_dirs']\n  host_to_result_dir = res['host_to_result_dir']\n  env_manifest = create_environment(find_osfamilies, tmp_dirs)\n  apply_manifest_on(master, env_manifest, :catch_failures => true)\n  agents.each do |agent|\n    resultdir = host_to_result_dir[on(agent, facter(\"networking.hostname\")).stdout.chomp]\n    step \"Applying catalog to agent: #{agent}. result files in #{resultdir}\"\n    on(\n      agent,\n      puppet('agent', \"-t\"),\n      :acceptable_exit_codes => [2]\n    )\n\n    step \"####### Verifying hiera calls from erb template #######\"\n    r1 = on(agent, \"cat #{resultdir}/hieratest_results_erb\")\n    result = r1.stdout\n\n    step \"Verifying hiera() call #1.\"\n    assert_match(\n      /#{@h_m_call}: #{@msg_production}/,\n      result,\n      \"#{@h_m_call} failed. Expected: '#{@msg_production}'\"\n    )\n\n    step \"Verifying hiera() call #2.\"\n    assert_match(\n      /#{@h_h_call}.*\\\"#{@k3}\\\"=>\\\"#{@hval3p}\\\"/,\n      result,\n      \"#{@h_h_call} failed. Expected: '\\\"#{@k3}\\\"=>\\\"#{@hval3p}\\\"'\"\n    )\n\n    step \"Verifying hiera() call #3.\"\n    assert_match(\n      /#{@h_h_call}.*\\\"#{@k2}\\\"=>\\\"#{@hval2p}\\\"/,\n      result,\n      \"#{@h_h_call} failed. Expected: '\\\"#{@k2}\\\"=>\\\"#{@hval2p}\\\"'\"\n    )\n\n    step \"Verifying hiera() call #4.\"\n    assert_match(\n      /#{@h_i_call}: #{@module_name}::mod_production/,\n      result,\n      \"#{@h_i_call} failed.  Expected:'#{@module_name}::mod_production'\"\n    )\n\n    step \"Verifying hiera_array() call. #1\"\n    assert_match(\n/#{@ha_m_call}: \\[\\\"#{@msg_production}\\\", \\\"#{@msg1os}\\\", \\\"#{@msg2os}\\\", \\\"#{@msg_default}\\\"\\]/,\n      result,\n      \"#{@ha_m_call} failed. Expected: '[\\\"#{@msg_production}\\\", \\\"#{@msg1os}\\\", \\\"#{@msg2os}\\\", \\\"#{@msg_default}\\\"]'\"\n    )\n\n    step \"Verifying hiera_array() call. #2\"\n    assert_match(\n/#{@ha_i_call}: \\[\\\"#{@module_name}::mod_production\\\", \\\"#{@module_name}::mod_osfamily\\\", \\\"#{@module_name}::mod_default\\\"\\]/,\n      result,\n      \"#{@ha_i_call} failed. Expected: '[\\\"#{@module_name}::mod_production\\\", \\\"#{@module_name}::mod_osfamily\\\", \\\"#{@module_name}::mod_default\\\"]'\"\n    )\n\n    step \"Verifying hiera_hash() call. #1\"\n    assert_match(\n      /#{@hh_h_call}:.*\\\"#{@k3}\\\"=>\\\"#{@hval3p}\\\"/,\n      result,\n      \"#{@hh_h_call} failed. Expected: '\\\"#{@k3}\\\"=>\\\"#{@hval3p}\\\"'\"\n    )\n\n    step \"Verifying hiera_hash() call. #2\"\n    assert_match(\n      /#{@hh_h_call}:.*\\\"#{@k2}\\\"=>\\\"#{@hval2p}\\\"/,\n      result,\n      \"#{@hh_h_call} failed. Expected: '\\\"#{@k2}\\\"=>\\\"#{@hval2p}\\\"'\"\n    )\n\n    step \"Verifying hiera_hash() call. #3\"\n    assert_match(\n      /#{@hh_h_call}:.*\\\"#{@k1}\\\"=>\\\"#{@hval1os}\\\"/,\n      result,\n      \"#{@hh_h_call} failed.  Expected: '\\\"#{@k1}\\\"=>\\\"#{@hval1os}\\\"'\"\n    )\n\n    r2 = on(agent, \"cat #{resultdir}/mod_default\")\n    result = r2.stdout\n    step \"Verifying hiera_include() call. #1\"\n    assert_match(\n      \"#{@mod_default_msg}\",\n      result,\n      \"#{@hi_i_call} failed.  Expected: '#{@mod_default_msg}'\"\n    )\n\n    r3 = on(agent, \"cat #{resultdir}/mod_osfamily\")\n    result = r3.stdout\n    step \"Verifying hiera_include() call. #2\"\n    assert_match(\n      \"#{@mod_osfamily_msg}\",\n      result,\n      \"#{@hi_i_call} failed.  Expected: '#{@mod_osfamily_msg}'\"\n    )\n\n    r4 = on(agent, \"cat #{resultdir}/mod_production\")\n    result = r4.stdout\n    step \"Verifying hiera_include() call. #3\"\n    assert_match(\n      \"#{@mod_production_msg}\",\n      result,\n      \"#{@hi_i_call} failed.  Expected: '#{@mod_production_msg}'\"\n    )\n\n    step \"####### Verifying hiera calls from epp template #######\"\n    r5 = on(agent, \"cat #{resultdir}/hieratest_results_epp\")\n    result = r5.stdout\n\n    step \"Verifying hiery() call #1.\"\n    assert_match(\n      /#{@h_m_call}: #{@msg_production}/,\n      result,\n      \"#{@hi_m_call} failed.  Expected '#{@msg_production}'\"\n    )\n\n    step \"Verifying hiera() call #2.\"\n    assert_match(\n      /#{@h_h_call}.*#{@k3} => #{@hval3p}/,\n      result,\n      \"#{@h_h_call} failed.  Expected '#{@k3} => #{@hval3p}'\"\n    )\n\n    step \"Verifying hiera() call #3.\"\n      assert_match(/#{@h_h_call}.*#{@k2} => #{@hval2p}/,\n      result,\n      \"#{@h_h_call} failed.  Expected '#{@k2} => #{@hval2p}'\"\n    )\n\n    step \"Verifying hiera() call #4.\"\n    assert_match(\n      /#{@h_i_call}: #{@module_name}::mod_production/,\n      result,\n      \"#{@h_i_call} failed.  Expected: '#{@module_name}::mod_production'\"\n    )\n\n    step \"Verifying hiera_array() call. #1\"\n    assert_match(\n/#{@ha_m_call}: \\[#{@msg_production}, #{@msg1os}, #{@msg2os}, #{@msg_default}\\]/,\n      result,\n      \"#{@ha_m_call} failed.  Expected: '[#{@msg_production}, #{@msg1os}, #{@msg2os}, #{@msg_default}]'\"\n    )\n\n    step \"Verifying hiera_array() call. #2\"\n    assert_match(\n/#{@ha_i_call}: \\[#{@module_name}::mod_production, #{@module_name}::mod_osfamily, #{@module_name}::mod_default\\]/,\n      result,\n      \"#{@ha_i_call} failed.  Expected: '[#{@module_name}::mod_production, #{@module_name}::mod_osfamily, #{@module_name}::mod_default'\"\n    )\n\n    step \"Verifying hiera_hash() call. #1\"\n    assert_match(\n      /#{@hh_h_call}:.*#{@k3} => #{@hval3p}/,\n      result,\n      \"#{@hh_h_call} failed.  Expected: '{@k3} => #{@hval3p}'\"\n    )\n\n    step \"Verifying hiera_hash() call. #2\"\n    assert_match(\n      /#{@hh_h_call}:.*#{@k2} => #{@hval2p}/,\n      result,\n      \"#{@hh_h_call} failed. Expected '#{@k2} => #{@hval2p}'\",\n    )\n\n    step \"Verifying hiera_hash() call. #3\"\n    assert_match(\n      /#{@hh_h_call}:.*#{@k1} => #{@hval1os}/,\n      result,\n      \"#{@hh_h_call}: failed.  Expected: '#{@k1} => #{@hval1os}'\"\n    )\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/no_exception_in_reduce_with_bignum.rb",
    "content": "test_name 'C97760: Integer in reduce() should not cause exception' do\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  tag 'audit:high',\n      'audit:unit'\n\n  # Remove all traces of the last used environment\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_type = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, app_type)\n\n  step 'On master, create site.pp with integer' do\n    create_sitepp(master, tmp_environment, <<-SITEPP)\n$data = [\n{\n\"certname\"=>\"xxxxxxxxx.some.domain\",\n\"parameters\"=>{\n    \"admin_auth_keys\"=>{\n        \"keyname1\"=>{\n            \"key\"=>\"ABCDEF\",\n            \"options\"=>[\"from=\\\\\"10.0.0.0/8\\\\\"\"]\n        },\n        \"keyname2\"=>{\n            \"key\"=>\"ABCDEF\",\n        },\n        \"keyname3\"=>{\n            \"key\"=>\"ABCDEF\",\n            \"options\"=>[\"from=\\\\\"10.0.0.0/8\\\\\"\"],\n            \"type\"=>\"ssh-xxx\"\n        },\n        \"keyname4\"=>{\n            \"key\"=>\"ABCDEF\",\n            \"options\"=>[\"from=\\\\\"10.0.0.0/8\\\\\"\"]\n        }\n    },\n    \"admin_user\"=>\"ertxa\",\n    \"admin_hosts\"=>[\"1.2.3.4\",\n        \"1.2.3.4\",\n        \"1.2.3.4\"],\n    \"admin_password\"=>\"ABCDEF\",\n    \"sshd_ports\"=>[22,\n        22, 24],\n    \"sudo_no_password_all\"=>false,\n    \"sudo_no_password_commands\"=>[],\n    \"sshd_config_template\"=>\"cfauth/sshd_config.epp\",\n    \"sudo_env_keep\"=>[]\n},\n\"exported\"=>false},\n]\n$data_reduced = $data.reduce({}) |$m, $r|{\n    $cn = $r['certname']\n    notice({ $cn => $r['parameters'] })\n}\nSITEPP\n  end\n\n  with_puppet_running_on(master, {}) do\n    agents.each do |agent|\n      on(agent, puppet(\"agent -t --environment #{tmp_environment}\"))\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/parser_functions/puppet_lookup_cmd.rb",
    "content": "test_name \"Puppet Lookup Command\"\n\ntag 'audit:high',\n    'audit:acceptance',\n    'audit:refactor'   # Master is not required for this test. Replace with agents.each\n                       # Wrap steps in blocks in accordance with Beaker style guide\n\n# doc:\n# https://puppet.com/docs/puppet/latest/hiera_automatic.html\n\n@module_name = \"puppet_lookup_command_test\"\n\n### @testroot = \"/etc/puppetlabs\"\n@testroot = master.tmpdir(\"#{@module_name}\")\n\n@coderoot = \"#{@testroot}/code\"\n@confdir = \"#{@testroot}/puppet\"\n\n@node1 = 'node1.example.org'\n@node2 = 'node2.example.org'\n\n@master_opts = {\n  'main' => {\n    'environmentpath' => \"#{@coderoot}/environments\",\n    'hiera_config' => \"#{@coderoot}/hiera.yaml\",\n  },\n}\n\n@manifest = <<MANIFEST\nFile {\n  ensure => directory,\n  mode => \"0755\",\n}\n\nfile {\n  '#{@confdir}':;\n  '#{@coderoot}':;\n  '#{@coderoot}/hieradata':;\n  '#{@coderoot}/environments':;\n\n##### default environment, production\n  '#{@coderoot}/environments/production':;\n  '#{@coderoot}/environments/production/data':;\n  '#{@coderoot}/environments/production/functions':;\n  '#{@coderoot}/environments/production/functions/environment':;\n  '#{@coderoot}/environments/production/lib':;\n  '#{@coderoot}/environments/production/lib/puppet':;\n  '#{@coderoot}/environments/production/lib/puppet/functions':;\n  '#{@coderoot}/environments/production/lib/puppet/functions/environment':;\n  '#{@coderoot}/environments/production/manifests':;\n  '#{@coderoot}/environments/production/modules':;\n\n#   module mod1 hiera\n  '#{@coderoot}/environments/production/modules/mod1':;\n  '#{@coderoot}/environments/production/modules/mod1/manifests':;\n  '#{@coderoot}/environments/production/modules/mod1/data':;\n  '#{@coderoot}/environments/production/modules/mod1/functions':;\n  '#{@coderoot}/environments/production/modules/mod1/lib':;\n  '#{@coderoot}/environments/production/modules/mod1/lib/puppet':;\n  '#{@coderoot}/environments/production/modules/mod1/lib/puppet/functions':;\n  '#{@coderoot}/environments/production/modules/mod1/lib/puppet/functions/mod1':;\n\n#   module mod2 ruby function\n  '#{@coderoot}/environments/production/modules/mod2':;\n  '#{@coderoot}/environments/production/modules/mod2/manifests':;\n  '#{@coderoot}/environments/production/modules/mod2/data':;\n  '#{@coderoot}/environments/production/modules/mod2/functions':;\n  '#{@coderoot}/environments/production/modules/mod2/lib':;\n  '#{@coderoot}/environments/production/modules/mod2/lib/puppet':;\n  '#{@coderoot}/environments/production/modules/mod2/lib/puppet/functions':;\n  '#{@coderoot}/environments/production/modules/mod2/lib/puppet/functions/mod2':;\n\n#   module mod3 puppet function\n  '#{@coderoot}/environments/production/modules/mod3':;\n  '#{@coderoot}/environments/production/modules/mod3/manifests':;\n  '#{@coderoot}/environments/production/modules/mod3/data':;\n  '#{@coderoot}/environments/production/modules/mod3/functions':;\n  '#{@coderoot}/environments/production/modules/mod3/not-lib':;\n  '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet':;\n  '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet/functions':;\n  '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet/functions/mod3':;\n\n#   module mod4 none\n  '#{@coderoot}/environments/production/modules/mod4':;\n  '#{@coderoot}/environments/production/modules/mod4/manifests':;\n  '#{@coderoot}/environments/production/modules/mod4/data':;\n  '#{@coderoot}/environments/production/modules/mod4/functions':;\n  '#{@coderoot}/environments/production/modules/mod4/lib':;\n  '#{@coderoot}/environments/production/modules/mod4/lib/puppet':;\n  '#{@coderoot}/environments/production/modules/mod4/lib/puppet/functions':;\n  '#{@coderoot}/environments/production/modules/mod4/lib/puppet/functions/mod4':;\n\n##### env1 hiera\n  '#{@coderoot}/environments/env1':;\n  '#{@coderoot}/environments/env1/data':;\n  '#{@coderoot}/environments/env1/functions':;\n  '#{@coderoot}/environments/env1/functions/environment':;\n  '#{@coderoot}/environments/env1/lib':;\n  '#{@coderoot}/environments/env1/lib/puppet':;\n  '#{@coderoot}/environments/env1/lib/puppet/functions':;\n  '#{@coderoot}/environments/env1/lib/puppet/functions/environment':;\n  '#{@coderoot}/environments/env1/manifests':;\n  '#{@coderoot}/environments/env1/modules':;\n\n#   module mod1 hiera\n  '#{@coderoot}/environments/env1/modules/mod1':;\n  '#{@coderoot}/environments/env1/modules/mod1/manifests':;\n  '#{@coderoot}/environments/env1/modules/mod1/data':;\n  '#{@coderoot}/environments/env1/modules/mod1/functions':;\n  '#{@coderoot}/environments/env1/modules/mod1/lib':;\n  '#{@coderoot}/environments/env1/modules/mod1/lib/puppet':;\n  '#{@coderoot}/environments/env1/modules/mod1/lib/puppet/functions':;\n  '#{@coderoot}/environments/env1/modules/mod1/lib/puppet/functions/mod1':;\n\n#   module mod2 ruby function\n  '#{@coderoot}/environments/env1/modules/mod2':;\n  '#{@coderoot}/environments/env1/modules/mod2/manifests':;\n  '#{@coderoot}/environments/env1/modules/mod2/data':;\n  '#{@coderoot}/environments/env1/modules/mod2/functions':;\n  '#{@coderoot}/environments/env1/modules/mod2/lib':;\n  '#{@coderoot}/environments/env1/modules/mod2/lib/puppet':;\n  '#{@coderoot}/environments/env1/modules/mod2/lib/puppet/functions':;\n  '#{@coderoot}/environments/env1/modules/mod2/lib/puppet/functions/mod2':;\n\n#   module mod3 puppet function\n  '#{@coderoot}/environments/env1/modules/mod3':;\n  '#{@coderoot}/environments/env1/modules/mod3/manifests':;\n  '#{@coderoot}/environments/env1/modules/mod3/data':;\n  '#{@coderoot}/environments/env1/modules/mod3/functions':;\n  '#{@coderoot}/environments/env1/modules/mod3/not-lib':;\n  '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet':;\n  '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet/functions':;\n  '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet/functions/mod3':;\n\n#   module mod4 none\n  '#{@coderoot}/environments/env1/modules/mod4':;\n  '#{@coderoot}/environments/env1/modules/mod4/manifests':;\n  '#{@coderoot}/environments/env1/modules/mod4/data':;\n  '#{@coderoot}/environments/env1/modules/mod4/functions':;\n  '#{@coderoot}/environments/env1/modules/mod4/lib':;\n  '#{@coderoot}/environments/env1/modules/mod4/lib/puppet':;\n  '#{@coderoot}/environments/env1/modules/mod4/lib/puppet/functions':;\n  '#{@coderoot}/environments/env1/modules/mod4/lib/puppet/functions/mod4':;\n\n\n##### env2 ruby function\n  '#{@coderoot}/environments/env2':;\n  '#{@coderoot}/environments/env2/data':;\n  '#{@coderoot}/environments/env2/functions':;\n  '#{@coderoot}/environments/env2/functions/environment':;\n  '#{@coderoot}/environments/env2/lib':;\n  '#{@coderoot}/environments/env2/lib/puppet':;\n  '#{@coderoot}/environments/env2/lib/puppet/functions':;\n  '#{@coderoot}/environments/env2/lib/puppet/functions/environment':;\n  '#{@coderoot}/environments/env2/manifests':;\n  '#{@coderoot}/environments/env2/modules':;\n\n#   module mod1 hiera\n  '#{@coderoot}/environments/env2/modules/mod1':;\n  '#{@coderoot}/environments/env2/modules/mod1/manifests':;\n  '#{@coderoot}/environments/env2/modules/mod1/data':;\n  '#{@coderoot}/environments/env2/modules/mod1/functions':;\n  '#{@coderoot}/environments/env2/modules/mod1/lib':;\n  '#{@coderoot}/environments/env2/modules/mod1/lib/puppet':;\n  '#{@coderoot}/environments/env2/modules/mod1/lib/puppet/functions':;\n  '#{@coderoot}/environments/env2/modules/mod1/lib/puppet/functions/mod1':;\n\n#   module mod2 ruby function\n  '#{@coderoot}/environments/env2/modules/mod2':;\n  '#{@coderoot}/environments/env2/modules/mod2/manifests':;\n  '#{@coderoot}/environments/env2/modules/mod2/data':;\n  '#{@coderoot}/environments/env2/modules/mod2/functions':;\n  '#{@coderoot}/environments/env2/modules/mod2/lib':;\n  '#{@coderoot}/environments/env2/modules/mod2/lib/puppet':;\n  '#{@coderoot}/environments/env2/modules/mod2/lib/puppet/functions':;\n  '#{@coderoot}/environments/env2/modules/mod2/lib/puppet/functions/mod2':;\n\n#   module mod3 puppet function\n  '#{@coderoot}/environments/env2/modules/mod3':;\n  '#{@coderoot}/environments/env2/modules/mod3/manifests':;\n  '#{@coderoot}/environments/env2/modules/mod3/data':;\n  '#{@coderoot}/environments/env2/modules/mod3/functions':;\n  '#{@coderoot}/environments/env2/modules/mod3/not-lib':;\n  '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet':;\n  '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet/functions':;\n  '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet/functions/mod3':;\n\n#   module mod4 none\n  '#{@coderoot}/environments/env2/modules/mod4':;\n  '#{@coderoot}/environments/env2/modules/mod4/manifests':;\n  '#{@coderoot}/environments/env2/modules/mod4/data':;\n  '#{@coderoot}/environments/env2/modules/mod4/functions':;\n  '#{@coderoot}/environments/env2/modules/mod4/lib':;\n  '#{@coderoot}/environments/env2/modules/mod4/lib/puppet':;\n  '#{@coderoot}/environments/env2/modules/mod4/lib/puppet/functions':;\n  '#{@coderoot}/environments/env2/modules/mod4/lib/puppet/functions/mod4':;\n\n\n##### env3 puppet function\n  '#{@coderoot}/environments/env3':;\n  '#{@coderoot}/environments/env3/data':;\n  '#{@coderoot}/environments/env3/functions':;\n  '#{@coderoot}/environments/env3/functions/environment':;\n  '#{@coderoot}/environments/env3/not-lib':;\n  '#{@coderoot}/environments/env3/not-lib/puppet':;\n  '#{@coderoot}/environments/env3/not-lib/puppet/functions':;\n  '#{@coderoot}/environments/env3/not-lib/puppet/functions/environment':;\n  '#{@coderoot}/environments/env3/manifests':;\n  '#{@coderoot}/environments/env3/modules':;\n\n#   module mod1 hiera\n  '#{@coderoot}/environments/env3/modules/mod1':;\n  '#{@coderoot}/environments/env3/modules/mod1/manifests':;\n  '#{@coderoot}/environments/env3/modules/mod1/data':;\n  '#{@coderoot}/environments/env3/modules/mod1/functions':;\n  '#{@coderoot}/environments/env3/modules/mod1/lib':;\n  '#{@coderoot}/environments/env3/modules/mod1/lib/puppet':;\n  '#{@coderoot}/environments/env3/modules/mod1/lib/puppet/functions':;\n  '#{@coderoot}/environments/env3/modules/mod1/lib/puppet/functions/mod1':;\n\n#   module mod2 ruby function\n  '#{@coderoot}/environments/env3/modules/mod2':;\n  '#{@coderoot}/environments/env3/modules/mod2/manifests':;\n  '#{@coderoot}/environments/env3/modules/mod2/data':;\n  '#{@coderoot}/environments/env3/modules/mod2/functions':;\n  '#{@coderoot}/environments/env3/modules/mod2/lib':;\n  '#{@coderoot}/environments/env3/modules/mod2/lib/puppet':;\n  '#{@coderoot}/environments/env3/modules/mod2/lib/puppet/functions':;\n  '#{@coderoot}/environments/env3/modules/mod2/lib/puppet/functions/mod2':;\n\n#   module mod3 puppet function\n  '#{@coderoot}/environments/env3/modules/mod3':;\n  '#{@coderoot}/environments/env3/modules/mod3/manifests':;\n  '#{@coderoot}/environments/env3/modules/mod3/data':;\n  '#{@coderoot}/environments/env3/modules/mod3/functions':;\n  '#{@coderoot}/environments/env3/modules/mod3/not-lib':;\n  '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet':;\n  '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet/functions':;\n  '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet/functions/mod3':;\n\n#   module mod4 none\n  '#{@coderoot}/environments/env3/modules/mod4':;\n  '#{@coderoot}/environments/env3/modules/mod4/manifests':;\n  '#{@coderoot}/environments/env3/modules/mod4/data':;\n  '#{@coderoot}/environments/env3/modules/mod4/functions':;\n  '#{@coderoot}/environments/env3/modules/mod4/lib':;\n  '#{@coderoot}/environments/env3/modules/mod4/lib/puppet':;\n  '#{@coderoot}/environments/env3/modules/mod4/lib/puppet/functions':;\n  '#{@coderoot}/environments/env3/modules/mod4/lib/puppet/functions/mod4':;\n\n\n##### env4 none\n  '#{@coderoot}/environments/env4':;\n  '#{@coderoot}/environments/env4/data':;\n  '#{@coderoot}/environments/env4/functions':;\n  '#{@coderoot}/environments/env4/functions/environment':;\n  '#{@coderoot}/environments/env4/lib':;\n  '#{@coderoot}/environments/env4/lib/puppet':;\n  '#{@coderoot}/environments/env4/lib/puppet/functions':;\n  '#{@coderoot}/environments/env4/lib/puppet/functions/environment':;\n  '#{@coderoot}/environments/env4/manifests':;\n  '#{@coderoot}/environments/env4/modules':;\n\n#   module mod1 hiera\n  '#{@coderoot}/environments/env4/modules/mod1':;\n  '#{@coderoot}/environments/env4/modules/mod1/manifests':;\n  '#{@coderoot}/environments/env4/modules/mod1/data':;\n  '#{@coderoot}/environments/env4/modules/mod1/functions':;\n  '#{@coderoot}/environments/env4/modules/mod1/lib':;\n  '#{@coderoot}/environments/env4/modules/mod1/lib/puppet':;\n  '#{@coderoot}/environments/env4/modules/mod1/lib/puppet/functions':;\n  '#{@coderoot}/environments/env4/modules/mod1/lib/puppet/functions/mod1':;\n\n#   module mod2 ruby function\n  '#{@coderoot}/environments/env4/modules/mod2':;\n  '#{@coderoot}/environments/env4/modules/mod2/manifests':;\n  '#{@coderoot}/environments/env4/modules/mod2/data':;\n  '#{@coderoot}/environments/env4/modules/mod2/functions':;\n  '#{@coderoot}/environments/env4/modules/mod2/lib':;\n  '#{@coderoot}/environments/env4/modules/mod2/lib/puppet':;\n  '#{@coderoot}/environments/env4/modules/mod2/lib/puppet/functions':;\n  '#{@coderoot}/environments/env4/modules/mod2/lib/puppet/functions/mod2':;\n\n#   module mod3 puppet function\n  '#{@coderoot}/environments/env4/modules/mod3':;\n  '#{@coderoot}/environments/env4/modules/mod3/manifests':;\n  '#{@coderoot}/environments/env4/modules/mod3/data':;\n  '#{@coderoot}/environments/env4/modules/mod3/functions':;\n  '#{@coderoot}/environments/env4/modules/mod3/not-lib':;\n  '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet':;\n  '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet/functions':;\n  '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet/functions/mod3':;\n\n#   module mod4 none\n  '#{@coderoot}/environments/env4/modules/mod4':;\n  '#{@coderoot}/environments/env4/modules/mod4/manifests':;\n  '#{@coderoot}/environments/env4/modules/mod4/data':;\n  '#{@coderoot}/environments/env4/modules/mod4/functions':;\n  '#{@coderoot}/environments/env4/modules/mod4/lib':;\n  '#{@coderoot}/environments/env4/modules/mod4/lib/puppet':;\n  '#{@coderoot}/environments/env4/modules/mod4/lib/puppet/functions':;\n  '#{@coderoot}/environments/env4/modules/mod4/lib/puppet/functions/mod4':;\n}\n\n## Global data provider config (hiera)\nfile { '#{@coderoot}/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  :backends:\n    - \"yaml\"\n  :logger: \"console\"\n  :hierarchy:\n    - \"global\"\n\n  :yaml:\n    :datadir: \"#{@coderoot}/hieradata\"\n',\n}\n\n## facts file\nfile { '#{@coderoot}/facts.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  my_data_key: \"my_data_value\"\n',\n}\n\nfile { '#{@coderoot}/hieradata/global.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  global_key: \"global-hiera provided value for key\"\n  another_global_key: \"global-hiera provided value for key\"\n  mod1::global_key: \"global-hiera provided value for key\"\n  mod2::global_key: \"global-hiera provided value for key\"\n  mod3::global_key: \"global-hiera provided value for key\"\n  mod4::global_key: \"global-hiera provided value for key\"\n',\n}\n\n\n## Evironment data provider configuration\nfile { '#{@coderoot}/environments/production/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\n',\n}\n\nfile { '#{@coderoot}/environments/env1/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\nenvironment_data_provider = \"hiera\"\n',\n}\n\nfile { '#{@coderoot}/environments/env2/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\nenvironment_data_provider = \"function\"\n',\n}\n\nfile { '#{@coderoot}/environments/env3/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\nenvironment_data_provider = \"function\"\n',\n}\n\nfile { '#{@coderoot}/environments/env4/environment.conf':\n  ensure => file,\n  mode => \"0644\",\n  content => 'environment_timeout = 0\nenvironment_data_provider = \"none\"\n',\n}\n\n# Environment hiera data provider\nfile { '#{@coderoot}/environments/production/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/production/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  global_key: \"env-production hiera provided value\"\n  environment_key: \"env-production hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env1/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env1/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  global_key: \"env-env1 hiera provided value\"\n  environment_key: \"env-env1 hiera provided value\"\n',\n}\n\n\nfile { '#{@coderoot}/environments/env2/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env2/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  global_key: \"env-env1 hiera provided value\"\n  environment_key: \"env-env1 hiera provided value\"\n',\n}\n\n\nfile { '#{@coderoot}/environments/env3/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env3/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  global_key: \"env-env1 hiera provided value\"\n  environment_key: \"env-env1 hiera provided value\"\n',\n}\n\n\nfile { '#{@coderoot}/environments/env4/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env4/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  global_key: \"env-env1 hiera provided value\"\n  environment_key: \"env-env1 hiera provided value\"\n',\n}\n\n# Environment ruby function data provider\nfile { '#{@coderoot}/environments/production/lib/puppet/functions/environment/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    {\n      'environment_key': 'env-production-ruby-function data() provided value',\n      'global_key': 'env-production-ruby-function data () provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env1/lib/puppet/functions/environment/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    {\n      'environment_key' => 'env-env1-ruby-function data() provided value',\n      'global_key' => 'env-env1-ruby-function data () provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env2/lib/puppet/functions/environment/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    {\n      'environment_key' => 'env-env2-ruby-function data() provided value',\n      'global_key' => 'env-env2-ruby-function data () provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env3/not-lib/puppet/functions/environment/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    {\n      'environment_key' => 'env-env3-ruby-function data() provided value',\n      'global_key' => 'env-env3-ruby-function data () provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env4/lib/puppet/functions/environment/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    {\n      'environment_key' => 'env-env4-ruby-function data() provided value',\n      'global_key' => 'env-env4-ruby-function data () provided value',\n    }\n  end\nend\n\",\n}\n\n# Environment puppet function data provider\nfile { '#{@coderoot}/environments/production/functions/environment/data.pp':\n  ensure => file,\n  mode => \"0755\",\n  content => 'function environment::data() {\n  {\n    \"environment_key\" => \"env-production-puppet-function data() provided value\",\n    \"global_key\" => \"env-production-puppet-function data() provided value\",\n  }\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/functions/environment/data.pp':\n  ensure => file,\n  mode => \"0755\",\n  content => 'function environment::data() {\n  {\n    \"environment_key\" => \"env-env1-puppet-function data() provided value\",\n    \"global_key\" => \"env-env1-puppet-function data() provided value\",\n  }\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/functions/environment/data.pp':\n  ensure => file,\n  mode => \"0755\",\n  content => 'function environment::data() {\n  {\n    \"environment_key\" => \"env-env2-puppet-function data() provided value\",\n    \"global_key\" => \"env-env2-puppet-function data() provided value\",\n  }\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/functions/environment/data.pp':\n  ensure => file,\n  mode => \"0755\",\n  content => 'function environment::data() {\n  {\n    \"environment_key\" => \"env-env3-puppet-function data() provided value\",\n    \"global_key\" => \"env-env3-puppet-function data() provided value\",\n  }\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/functions/environment/data.pp':\n  ensure => file,\n  mode => \"0755\",\n  content => 'function environment::data() {\n  {\n    \"environment_key\" => \"env-env4-puppet-function data() provided value\",\n    \"global_key\" => \"env-env4-puppet-function data() provided value\",\n  }\n}\n',\n}\n\n\n## Module data provider configuration\n# Module hiera data provider\nfile { '#{@coderoot}/environments/production/modules/mod1/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod1/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod1::module_key\": \"module-production-mod1-hiera provided value\"\n  \"mod1::global_key\": \"module-production-mod1-hiera provided value\"\n  \"environment_key\": \"module-production-mod1-hiera provided value\"\n  \"global_key\": \"module-production-mod1-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod2/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod2/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod2::module_key\": \"module-production-mod2-hiera provided value\"\n  \"mod2::global_key\": \"module-production-mod2-hiera provided value\"\n  \"environment_key\": \"module-production-mod2-hiera provided value\"\n  \"global_key\": \"module-production-mod2-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod3/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod3/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod3::module_key\": \"module-production-mod3-hiera provided value\"\n  \"mod3::global_key\": \"module-production-mod3-hiera provided value\"\n  \"environment_key\": \"module-production-mod3-hiera provided value\"\n  \"global_key\" => \"module-production-mod3-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod4/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod4/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod4::module_key\": \"module-production-mod4-hiera provided value\"\n  \"mod4::global_key\": \"module-production-mod4-hiera provided value\"\n  \"environment_key\": \"module-production-mod4-hiera provided value\"\n  \"global_key\": \"module-production-mod4-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod1/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod1/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod1::module_key\": \"module-env1-mod1-hiera provided value\"\n  \"global_key\": \"module-env1-mod1-hiera provided value\"\n  \"environment_key\": \"module-env1-mod1-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod2/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod2/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod2::module_key\": \"module-env1-mod2-hiera provided value\"\n  \"global_key\": \"module-env1-mod2-hiera provided value\"\n  \"environment_key\": \"module-env1-mod2-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod3/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod3/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod3::module_key\": \"module-env1-mod3-hiera provided value\"\n  \"global_key\": \"module-env1-mod3-hiera provided value\"\n  \"environment_key\": \"module-env1-mod3-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod4/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod4/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod4::module_key\": \"module-env1-mod4-hiera provided value\"\n  \"global_key\": \"module-env1-mod4-hiera provided value\"\n  \"environment_key\": \"module-env1-mod4-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod1/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod1/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod1::module_key\": \"module-env2-mod1-hiera provided value\"\n  \"global_key\": \"module-env2-mod1-hiera provided value\"\n  \"environment_key\": \"module-env2-mod1-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod2/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod2/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod2::module_key\": \"module-env2-mod2-hiera provided value\"\n  \"global_key\": \"module-env2-mod2-hiera provided value\"\n  \"environment_key\": \"module-env2-mod2-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod3/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod3/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod3::module_key\": \"module-env2-mod3-hiera provided value\"\n  \"global_key\": \"module-env2-mod3-hiera provided value\"\n  \"environment_key\": \"module-env2-mod3-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod4/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod4/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod4::module_key\": \"module-env2-mod4-hiera provided value\"\n  \"global_key\": \"module-env2-mod4-hiera provided value\"\n  \"environment_key\": \"module-env2-mod4-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod1/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod1/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod1::module_key\": \"module-env3-mod1-hiera provided value\"\n  \"global_key\": \"module-env3-mod1-hiera provided value\"\n  \"environment_key\": \"module-env3-mod1-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod2/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod2/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod2::module_key\": \"module-env3-mod2-hiera provided value\"\n  \"global_key\": \"module-env3-mod2-hiera provided value\"\n  \"environment_key\": \"module-env3-mod2-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod3/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod3/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod3::module_key\": \"module-env3-mod3-hiera provided value\"\n  \"global_key\": \"module-env3-mod3-hiera provided value\"\n  \"environment_key\": \"module-env3-mod3-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod4/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod4/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod4::module_key\": \"module-env3-mod4-hiera provided value\"\n  \"global_key\": \"module-env3-mod4-hiera provided value\"\n  \"environment_key\": \"module-env3-mod4-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod1/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod1/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod1::module_key\": \"module-env4-mod1-hiera provided value\"\n  \"global_key\": \"module-env4-mod1-hiera provided value\"\n  \"environment_key\": \"module-env4-mod1-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod2/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod2/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod2::module_key\": \"module-env4-mod2-hiera provided value\"\n  \"global_key\": \"module-env4-mod2-hiera provided value\"\n  \"environment_key\": \"module-env4-mod2-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod3/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod3/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod3::module_key\": \"module-env4-mod3-hiera provided value\"\n  \"global_key\": \"module-env4-mod3-hiera provided value\"\n  \"environment_key\": \"module-env4-mod3-hiera provided value\"\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod4/hiera.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 4\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod4/data/common.yaml':\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  \"mod4::module_key\": \"module-env4-mod4-hiera provided value\"\n  \"global_key\": \"module-env4-mod4-hiera provided value\"\n  \"environment_key\": \"module-env4-mod4-hiera provided value\"\n',\n}\n\n# Module ruby function data provider\nfile { '#{@coderoot}/environments/production/modules/mod1/lib/puppet/functions/mod1/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod1::data') do\n  def data()\n    {\n      'mod1::module_key' => 'module-production-mod1-ruby-function provided value',\n      'mod1::global_key' => 'module-production-mod1-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod2/lib/puppet/functions/mod2/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod2::data') do\n  def data()\n    {\n      'mod2::module_key' => 'module-production-mod2-ruby-function provided value',\n      'mod2::global_key' => 'module-production-mod2-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet/functions/mod3/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod3::data') do\n  def data()\n    {\n      'mod3::module_key' => 'module-production-mod3-ruby-function provided value',\n      'mod3::global_key' => 'module-production-mod3-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod4/lib/puppet/functions/mod4/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod4::data') do\n  def data()\n    {\n      'mod4::module_key' => 'module-production-mod4-ruby-function provided value',\n      'mod4::global_key' => 'module-production-mod4-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod1/lib/puppet/functions/mod1/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod1::data') do\n  def data()\n    {\n      'mod1::module_key' => 'module-env1-mod1-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod2/lib/puppet/functions/mod2/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod2::data') do\n  def data()\n    {\n      'mod2::module_key' => 'module-env1-mod2-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet/functions/mod3/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod3::data') do\n  def data()\n    {\n      'mod3::module_key' => 'module-env1-mod3-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod4/lib/puppet/functions/mod4/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod4::data') do\n  def data()\n    {\n      'mod4::module_key' => 'module-env1-mod4-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod1/lib/puppet/functions/mod1/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod1::data') do\n  def data()\n    {\n      'mod1::module_key' => 'module-env2-mod1-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod2/lib/puppet/functions/mod2/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod2::data') do\n  def data()\n    {\n      'mod2::module_key' => 'module-env2-mod2-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet/functions/mod3/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod3::data') do\n  def data()\n    {\n      'mod3::module_key' => 'module-env2-mod3-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod4/lib/puppet/functions/mod4/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod4::data') do\n  def data()\n    {\n      'mod4::module_key' => 'module-env2-mod4-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod1/lib/puppet/functions/mod1/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod1::data') do\n  def data()\n    {\n      'mod1::module_key' => 'module-env3-mod1-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod2/lib/puppet/functions/mod2/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod2::data') do\n  def data()\n    {\n      'mod2::module_key' => 'module-env3-mod2-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet/functions/mod3/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod3::data') do\n  def data()\n    {\n      'mod3::module_key' => 'module-env3-mod3-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod4/lib/puppet/functions/mod4/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod4::data') do\n  def data()\n    {\n      'mod4::module_key' => 'module-env3-mod4-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod1/lib/puppet/functions/mod1/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod1::data') do\n  def data()\n    {\n      'mod1::module_key' => 'module-env4-mod1-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod2/lib/puppet/functions/mod2/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod2::data') do\n  def data()\n    {\n      'mod2::module_key' => 'module-env4-mod2-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet/functions/mod3/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod3::data') do\n  def data()\n    {\n      'mod3::module_key' => 'module-env4-mod3-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod4/lib/puppet/functions/mod4/data.rb':\n  ensure => file,\n  mode => \"0644\",\n  content => \"Puppet::Functions.create_function(:'mod4::data') do\n  def data()\n    {\n      'mod4::module_key' => 'module-env4-mod4-ruby-function provided value',\n    }\n  end\nend\n\",\n}\n\n# Module puppet function data provider\nfile {  '#{@coderoot}/environments/production/modules/mod1/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod1::data() {\n  {\n    'mod1::module_key' => 'module-production-mod1-puppet-function provided value',\n    'mod1::global_key' => 'module-production-mod1-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/production/modules/mod2/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod2::data() {\n  {\n    'mod2::module_key' => 'module-production-mod2-puppet-function provided value',\n    'mod2::global_key' => 'module-production-mod2-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/production/modules/mod3/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod3::data() {\n  {\n    'mod3::module_key' => 'module-production-mod3-puppet-function provided value',\n    'mod3::global_key' => 'module-production-mod3-puppet-funtion provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/production/modules/mod4/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod4::data() {\n  {\n    'mod4::module_key' => 'module-production-mod4-puppet-function provided value',\n    'mod4::global_key' => 'module-production-mod4-puppet-funtion provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env1/modules/mod1/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod1::data() {\n  {\n    'mod1::module_key' => 'module-env1-mod1-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env1/modules/mod2/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod2::data() {\n  {\n    'mod2::module_key' => 'module-env1-mod2-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env1/modules/mod3/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod3::data() {\n  {\n    'mod3::module_key' => 'module-env1-mod3-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env1/modules/mod4/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod4::data() {\n  {\n    'mod4::module_key' => 'module-env1-mod4-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env2/modules/mod1/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod1::data() {\n  {\n    'mod1::module_key' => 'module-env2-mod1-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env2/modules/mod2/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod2::data() {\n  {\n    'mod2::module_key' => 'module-env2-mod2-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env2/modules/mod3/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod3::data() {\n  {\n    'mod3::module_key' => 'module-env2-mod3-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env2/modules/mod4/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod4::data() {\n  {\n    'mod4::module_key' => 'module-env2-mod4-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env3/modules/mod1/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod1::data() {\n  {\n    'mod1::module_key' => 'module-env3-mod1-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env3/modules/mod2/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod2::data() {\n  {\n    'mod2::module_key' => 'module-env3-mod2-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env3/modules/mod3/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod3::data() {\n  {\n    'mod3::module_key' => 'module-env3-mod3-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env3/modules/mod4/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod4::data() {\n  {\n    'mod4::module_key' => 'module-env3-mod4-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env4/modules/mod1/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod1::data() {\n  {\n    'mod1::module_key' => 'module-env4-mod1-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env4/modules/mod2/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod2::data() {\n  {\n    'mod2::module_key' => 'module-env4-mod2-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env4/modules/mod3/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod3::data() {\n  {\n    'mod3::module_key' => 'module-env4-mod3-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile {  '#{@coderoot}/environments/env4/modules/mod4/functions/data.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"function mod4::data() {\n  {\n    'mod4::module_key' => 'module-env4-mod4-puppet-function provided value',\n  }\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/production/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include mod1\n  include mod2\n  include mod3\n  include mod4\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/env1/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include mod1\n  include mod2\n  include mod3\n  include mod4\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/env2/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include mod1\n  include mod2\n  include mod3\n  include mod4\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/env3/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include mod1\n  include mod2\n  include mod3\n  include mod4\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/env4/manifests/site.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => \"node default {\n  include mod1\n  include mod2\n  include mod2\n  include mod2\n}\n\",\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod1/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod1 {\n  notice(\"hello from production-mod1\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod2/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod2 {\n  notice(\"hello from production-mod2\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod3/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod3 {\n  notice(\"hello from production-mod3\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod4/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod4 {\n  notice(\"hello from production-mod4\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod1/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod1 {\n  notice(\"hello from env1-mod1\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod2/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod2 {\n  notice(\"hello from env1-mod2\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod3/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod3 {\n  notice(\"hello from env1-mod3\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod4/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod4 {\n  notice(\"hello from env1-mod4\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod1/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod1 {\n  notice(\"hello from env2-mod1\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod2/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod2 {\n  notice(\"hello from env2-mod2\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod3/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod3 {\n  notice(\"hello from env2-mod3\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod4/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod4 {\n  notice(\"hello from env2-mod4\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod1/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod1 {\n  notice(\"hello from env3-mod1\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod2/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod2 {\n  notice(\"hello from env3-mod2\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod3/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod3 {\n  notice(\"hello from env3-mod3\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod4/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod4 {\n  notice(\"hello from env3-mod4\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod1/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod1 {\n  notice(\"hello from env4-mod1\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod2/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod2 {\n  notice(\"hello from env4-mod2\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod3/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod3 {\n  notice(\"hello from env4-mod3\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod4/manifests/init.pp':\n  ensure => file,\n  mode => \"0644\",\n  content => 'class mod4 {\n  notice(\"hello from env4-mod4\")\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod1/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod1\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"hiera\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod2/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod2\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod3/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod3\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/production/modules/mod4/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod1\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"none\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod1/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod1\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"hiera\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod2/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod2\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod3/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod3\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env1/modules/mod4/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod4\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"none\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod1/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod1\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"hiera\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod2/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod2\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod3/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod3\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env2/modules/mod4/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod4\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"none\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod1/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod1\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"hiera\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod2/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod2\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod3/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod3\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env3/modules/mod4/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod4\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"none\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod1/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod1\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"hiera\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod2/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod2\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod3/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod3\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"function\"\n}\n',\n}\n\nfile { '#{@coderoot}/environments/env4/modules/mod4/metadata.json':\n  ensure => file,\n  mode => \"0644\",\n  content => '{\n  \"name\": \"tester-mod4\",\n  \"version\": \"0.1.0\",\n  \"author\": \"tester\",\n  \"summary\": null,\n  \"license\": \"Apache-2.0\",\n  \"source\": \"\",\n  \"project_page\": null,\n  \"issues_url\": null,\n  \"dependencies\": [],\n  \"data_provider\": \"none\"\n}\n',\n}\nMANIFEST\n\n  @env1puppetconfmanifest = <<MANI1\nfile { '#{@confdir}/puppet.conf' :\n  ensure => file,\n  mode => \"0664\",\n  content => \"[server]\nvardir = /opt/puppetlabs/server/data/puppetserver\nlogdir = /var/log/puppetlabs/puppetserver\nrundir = /var/run/puppetlabs/puppetserver\npidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid\ncodedir = #{@coderoot}\n\n[main]\nenvironmentpath = #{@coderoot}/environments\nhiera_config = #{@coderoot}/hiera.yaml\nenvironment = env1\nserver = #{master.connection.hostname}\n\",\n}\nMANI1\n\n  @env2puppetconfmanifest = <<MANI2\nfile { '#{@confdir}/puppet.conf' :\n  ensure => file,\n  mode => \"0664\",\n  content => \"[server]\nvardir = /opt/puppetlabs/server/data/puppetserver\nlogdir = /var/log/puppetlabs/puppetserver\nrundir = /var/run/puppetlabs/puppetserver\npidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid\ncodedir = #{@coderoot}\n\n[main]\nenvironmentpath = #{@coderoot}/environments\nhiera_config = #{@coderoot}/hiera.yaml\nenvironment = env2\nserver = #{master.connection.hostname}\n\",\n}\nMANI2\n\n  @env3puppetconfmanifest = <<MANI3\nfile { '#{@confdir}/puppet.conf' :\n  ensure => file,\n  mode => \"0664\",\n  content => \"[server]\nvardir = /opt/puppetlabs/server/data/puppetserver\nlogdir = /var/log/puppetlabs/puppetserver\nrundir = /var/run/puppetlabs/puppetserver\npidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid\ncodedir = #{@coderoot}\n\n[main]\nenvironmentpath = #{@coderoot}/environments\nhiera_config = #{@coderoot}/hiera.yaml\nenvironment = env3\nserver = #{master.connection.hostname}\n\",\n}\nMANI3\n\n  @env4puppetconfmanifest = <<MANI4\nfile { '#{@confdir}/puppet.conf' :\n  ensure => file,\n  mode => \"0664\",\n  content => \"[server]\nvardir = /opt/puppetlabs/server/data/puppetserver\nlogdir = /var/log/puppetlabs/puppetserver\nrundir = /var/run/puppetlabs/puppetserver\npidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid\ncodedir = #{@coderoot}\n\n[main]\nenvironmentpath = #{@coderoot}/environments\nhiera_config = #{@coderoot}/hiera.yaml\nenvironment = env4\nserver = #{master.connection.hostname}\n\",\n}\nMANI4\n\n  @encmanifest = <<MANIENC\n## enc\nfile { '#{@coderoot}/enc.rb' :\n  ensure => file,\n  mode => \"0755\",\n  content => \"#!#{master['privatebindir']}/ruby\nnodename = ARGV.shift\nnode2env = {\n  '#{@node1}' => \\\\\\\"---\\\\\\\\n  environment: env2\\\\\\\\n\\\\\\\",\n  '#{@node2}' => \\\\\\\"---\\\\\\\\n  environment: env3\\\\\\\\n\\\\\\\",\n}\nputs (\\\\\\\"\\#{node2env[nodename]}\\\\\\\" ||'')\n\",\n}\nfile { '#{@confdir}/puppet.conf' :\n  ensure => file,\n  mode => \"0664\",\n  content => \"[server]\nvardir = /opt/puppetlabs/server/data/puppetserver\nlogdir = /var/log/puppetlabs/puppetserver\nrundir = /var/run/puppetlabs/puppetserver\npidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid\ncodedir = #{@coderoot}\n\n[server]\nnode_terminus = exec\nexternal_nodes = #{@coderoot}/enc.rb\n\n[main]\nenvironmentpath = #{@coderoot}/environments\nhiera_config = #{@coderoot}/hiera.yaml\nserver = #{master.connection.hostname}\n\",\n}\nMANIENC\n\nteardown do\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/certs/#{@node1}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/certs/#{@node2}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/ca/signed/#{@node1}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/ca/signed/#{@node2}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/private_keys/#{@node1}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/private_keys/#{@node2}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/public_keys/#{@node1}.pem\")\n  on(master, \"rm -f /etc/puppetlabs/puppet/ssl/public_keys/#{@node2}.pem\")\nend\n\nstep 'apply main manifest'\napply_manifest_on(master, @manifest, :catch_failures => true)\n\nstep 'start puppet server'\nwith_puppet_running_on master, @master_opts, @coderoot do\n\n  step \"global_key\"\n  rg = on(master, puppet('lookup', 'global_key'))\n  result = rg.stdout\n  assert_match(\n    /global-hiera/,\n    result,\n    \"global_key lookup failed, expected 'global-hiera'\"\n  )\n\n  step \"production environment_key not provided\"\n  on(master, puppet('lookup', 'enviroment_key'), :acceptable_exit_codes => [1])\n\n  step \"environment_key from environment env1\"\n  re1 = on(master, puppet('lookup', '--environment env1', 'environment_key'))\n  result = re1.stdout\n  assert_match(\n    /env-env1 hiera/,\n    result,\n    \"env1 environment_key lookup failed, expected 'env-env1 hiera'\"\n  )\n\n  step \"environment_key from environment env2\"\n  re2 = on(master, puppet('lookup', '--environment env2', 'environment_key'))\n  result = re2.stdout\n  assert_match(\n    /env-env2-ruby-function/,\n    result,\n    \"env2 environment_key lookup failed, expected 'env-env2-puppet-function'\"\n  )\n\n  step \"environment_key from environment env3\"\n  re3 = on(master, puppet('lookup', '--environment env3', 'environment_key'))\n  result = re3.stdout\n  assert_match(\n    /env-env3-puppet-function/,\n    result,\n    \"env3 environment_key lookup failed, expected 'env-env2-ruby-function data() provided value'\"\n  )\n\n  step \"environment_key from environment env4\"\n  on(master, puppet('lookup', '--environment env4', 'environment_key'), :acceptable_exit_codes => [1])\n\n  step \"production mod1 module_key\"\n  repm1 = on(master, puppet('lookup', 'mod1::module_key'))\n  result = repm1.stdout\n  assert_match(\n    /module-production-mod1-hiera/,\n    result,\n    \"production mod1 module_key lookup failed, expected 'module-production-mod1-hiera'\"\n  )\n\n  step \"production mod2 module_key\"\n  repm2 = on(master, puppet('lookup', 'mod2::module_key'))\n  result = repm2.stdout\n  assert_match(\n    /module-production-mod2-ruby-function/,\n    result,\n    \"production mod2 module_key lookup failed, expected 'module-production-mod2-ruby-function'\"\n  )\n\n  step \"production mod3 module_key\"\n  repm3 = on(master, puppet('lookup', 'mod3::module_key'))\n  result = repm3.stdout\n  assert_match(\n    /module-production-mod3-puppet-function/,\n    result,\n    \"production mod3 module_key lookup failed, expected 'module-production-mod3-puppet-function'\"\n  )\n\n  step \"production mod4 module_key\"\n  on(master, puppet('lookup', 'mod4::module_key'), :acceptable_exit_codes => [1])\n\n  step \"env1 mod1 module_key\"\n  re1m1 = on(master, puppet('lookup', '--environment env1', 'mod1::module_key'))\n  result = re1m1.stdout\n  assert_match(\n    /module-env1-mod1-hiera/,\n    result,\n    \"env1 mod1 module_key lookup failed, expected 'module-env1-mod1-hiera'\"\n  )\n\n  step \"env1 mod2 module_key\"\n  re1m2 = on(master, puppet('lookup', '--environment env1', 'mod2::module_key'))\n  result = re1m2.stdout\n  assert_match(\n    /module-env1-mod2-ruby-function/,\n    result,\n    \"env1 mod2 module_key lookup failed, expected 'module-env1-mod2-ruby-function'\"\n  )\n\n  step \"env1 mod3 module_key\"\n  re1m3 = on(master, puppet('lookup', '--environment env1', 'mod3::module_key'))\n  result = re1m3.stdout\n  assert_match(\n    /module-env1-mod3-puppet-function/,\n    result,\n    \"env1 mod3 module_key lookup failed, expected 'module-env1-mod3-puppet-function'\"\n  )\n\n  step \"env1 mod4 module_key\"\n  on(master, puppet('lookup', '--environment env1', 'mod4::module_key'), :acceptable_exit_codes => [1])\n\n  step \"env2 mod1 module_key\"\n  re2m1 = on(master, puppet('lookup', '--environment env2', 'mod1::module_key'))\n  result = re2m1.stdout\n  assert_match(\n    /module-env2-mod1-hiera/,\n    result,\n    \"env2 mod1 module_key lookup failed, expected 'module-env2-mod1-hiera'\"\n  )\n\n  step \"env2 mod2 module_key\"\n  re2m2 = on(master, puppet('lookup', '--environment env2', 'mod2::module_key'))\n  result = re2m2.stdout\n  assert_match(\n    /module-env2-mod2-ruby-function/,\n    result,\n    \"env2 mod2 module_key lookup failed, expected 'module-env2-mod2-ruby-function'\"\n  )\n\n  step \"env2 mod3 module_key\"\n  re2m3 = on(master, puppet('lookup', '--environment env2', 'mod3::module_key'))\n  result = re2m3.stdout\n  assert_match(\n    /module-env2-mod3-puppet-function/,\n    result,\n    \"env2 mod3 module_key lookup failed, expected 'module-env2-mod3-puppet-function'\"\n  )\n\n  step \"env2 mod4 module_key\"\n  on(master, puppet('lookup', '--environment env2', 'mod4::module_key'), :acceptable_exit_codes => [1])\n\n  step \"env3 mod1 module_key\"\n  re3m1 = on(master, puppet('lookup', '--environment env3', 'mod1::module_key'))\n  result = re3m1.stdout\n  assert_match(\n    /module-env3-mod1-hiera/,\n    result,\n    \"env3 mod1 module_key lookup failed, expected 'module-env3-mod1-hiera'\"\n  )\n\n  step \"env3 mod2 module_key\"\n  re3m2 = on(master, puppet('lookup', '--environment env3', 'mod2::module_key'))\n  result = re3m2.stdout\n  assert_match(\n    /module-env3-mod2-ruby-function/,\n    result,\n    \"env3 mod2 module_key lookup failed, expected 'module-env3-mod2-ruby-function'\"\n  )\n\n  step \"env3 mod3 module_key\"\n  re3m3 = on(master, puppet('lookup', '--environment env3', 'mod3::module_key'))\n  result = re3m3.stdout\n  assert_match(\n    /module-env3-mod3-puppet-function/,\n    result,\n    \"env3 mod3 module_key lookup failed, expected 'module-env3-mod3-puppet-function'\"\n  )\n\n  step \"env3 mod4 module_key\"\n#   re3m4 = on(master, puppet('lookup', '--environment env3', 'mod4::module_key'), :acceptable_exit_codes => [1])\n\n  step \"env4 mod1 module_key\"\n  re4m1 = on(master, puppet('lookup', '--environment env4', 'mod1::module_key'))\n  result = re4m1.stdout\n  assert_match(\n    /module-env4-mod1-hiera/,\n    result,\n    \"env4 mod2 environent_key lookup failed, expected 'module-env4-hiera'\"\n  )\n\n\n  step \"env4 mod2 module_key\"\n  re4m2 = on(master, puppet('lookup', '--environment env4', 'mod2::module_key'))\n  result = re4m2.stdout\n  assert_match(\n    /module-env4-mod2-ruby-function/,\n    result,\n    \"env4 mod2 environent_key lookup failed, expected 'module-env4-mod2-ruby-function'\"\n  )\n\n  step \"env4 mod3 module_key\"\n  re4m3 = on(master, puppet('lookup', '--environment env4', 'mod3::module_key'))\n  result = re4m3.stdout\n  assert_match(\n    /module-env4-mod3-puppet-function/,\n    result,\n    \"env4 mod3 module_key lookup failed, expected 'module-env4-mod3-puppet-function'\"\n  )\n\n  step \"env4 mod4 module_key\"\n  on(master, puppet('lookup', '--environment env4', 'mod4::module_key'), :acceptable_exit_codes => [1])\n\n  step \"global key explained\"\n  rxg = on(master, puppet('lookup', '--explain', 'global_key'))\n  result = rxg.stdout\n  assert_match(\n    /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*Found key.*global-hiera/,\n    result,\n    \"global_key explained failed, expected /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*Found key.*global-hiera/\"\n  )\n\n  step \"environment env1 environment_key explained\"\n  rxe1 = on(master, puppet('lookup', '--explain', '--environment env1', 'environment_key'))\n  result = rxe1.stdout\n  assert_match(\n    /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key/,\n    result,\n    \"environment env1 enviroment_key lookup failed, expected /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key/\"\n  )\n  assert_match(\n    /common.*\\s*.*env-env1 hiera/,\n    result,\n    \"environment env1 enviroment_key lookup failed, expected /common.*\\s*.*env-env1 hiera/\"\n  )\n\n  step \"environment env2 environment_key explained\"\n  rxe2 = on(master, puppet('lookup', '--explain', '--environment env2', 'environment_key'))\n  result = rxe2.stdout\n  assert_match(\n    /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key/,\n    result,\n    \"environment env2 enviroment_key lookup failed, expected /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key/\"\n  )\n  assert_match(\n    /eprecated API function.*\\s*.*env-env2-ruby-function/,\n    result,\n    \"environment env2 enviroment_key lookup failed, expected /eprecated API function.*\\s*.*env-env2-ruby-function/\"\n  )\n\n  step \"environment env3 environment_key explained\"\n  rxe3 = on(master, puppet('lookup', '--explain', '--environment env3', 'environment_key'))\n  result = rxe3.stdout\n  assert_match(\n    /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key/,\n    result,\n    \"environment env3 enviroment_key lookup failed, expected /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key/\"\n  )\n  assert_match(\n    /eprecated API function.*\\s*.*env-env3-puppet-function/,\n    result,\n    \"environment env3 enviroment_key lookup failed, expected /eprecated API function.*\\s*.*env-env3-puppet-function/\"\n  )\n\n  step \"environment env4 environment_key explained\"\n  rxe4 = on(master, puppet('lookup', '--explain', '--environment env4', 'environment_key'))\n  result = rxe4.stdout\n  assert_match(\n    /Global Data Provider.*\\s*Using.*\\s*Hier.*\\s*Path.*\\s*Orig.*\\s*No such key.*environment_key/,\n    result,\n    \"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.*/\"\n  )\n\n  step \"environment env1 mod4::module_key explained\"\n  rxe1m4 = on(master, puppet('lookup', '--explain', '--environment env1', 'mod4::module_key'))\n  result = rxe1m4.stdout\n  assert_match(\n    /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.*/,\n    result,\n    \"environment env1 mod4::module_key lookup explained failed.\"\n  )\n\n  step \"environment env2 mod3::module_key explained\"\n  rxe2m3 = on(master, puppet('lookup', '--explain', '--environment env2', 'mod3::module_key'))\n  result = rxe2m3.stdout\n  assert_match(\n    /Global Data Provider.*Using configuration.*Hierarchy entry.*Path.*No such key/m,\n    result,\n    \"global env2 mod3::module_key lookup --explain had correct output\"\n  )\n  assert_match(\n    /Environment Data Provider.*Deprecated.*No such key/m,\n    result,\n    \"environment env2 mod3::module_key lookup --explain had correct output\"\n  )\n  assert_match(\n    /Module.*Data Provider.*Deprecated API function \"mod3::data\".*Found key.*module-env2-mod3-puppet-function provided value/m,\n    result,\n    \"module env2 mod3::module_key lookup --explain had correct output\"\n  )\n\n  step \"environment env3 mod2::module_key explained\"\n  rxe3m2 = on(master, puppet('lookup', '--explain', '--environment env3', 'mod2::module_key'))\n  result = rxe3m2.stdout\n  assert_match(\n    /Global Data Provider.*Using configuration.*Hierarchy entry.*Path.*No such key/m,\n    result,\n    \"global env2 mod3::module_key lookup --explain had correct output\"\n  )\n  assert_match(\n    /Environment Data Provider.*Deprecated.*No such key/m,\n    result,\n    \"environment env2 mod3::module_key lookup --explain had correct output\"\n  )\n  assert_match(\n    /Module.*Data Provider.*Deprecated API function \"mod2::data\".*Found key.*module-env3-mod2-ruby-function provided value/m,\n    result,\n    \"module env2 mod3::module_key lookup --explain had correct output\"\n  )\n\n  step \"environment env4 mod1::module_key explained\"\n  rxe4m1 = on(master, puppet('lookup', '--explain', '--environment env4', 'mod1::module_key'))\n  result = rxe4m1.stdout\n  assert_match(\n    /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/,\n    result,\n    \"environment env4 mod1::module_key lookup failed.\"\n  )\n\n  step 'apply env1 puppet.conf manifest'\n  apply_manifest_on(master, @env1puppetconfmanifest, :catch_failures => true)\n\n  step \"puppet.conf specified environment env1 environment_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'environment_key'))\n  result = r.stdout\n  assert_match(\n    /env-env1 hiera/,\n    result,\n    \"puppet.conf specified environment env1, environment_key lookup failed, expected /env-env1 hiera/\"\n  )\n\n  step \"puppet.conf specified environment env1 mod4::module_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'mod4::module_key'), :acceptable_exit_codes => [1])\n\n  step 'apply env2 puppet.conf manifest'\n  apply_manifest_on(master, @env2puppetconfmanifest, :catch_failures => true)\n\n  step \"puppet.conf specified environment env2 environment_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'environment_key'))\n  result = r.stdout\n  assert_match(\n    /env-env2-ruby-function/,\n    result,\n    \"puppet.conf specified environment env2, environment_key lookup failed, expected /env-env2-ruby-function/\"\n  )\n\n  step \"puppet.conf specified environment env2 mod3::module_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'mod3::module_key'))\n  result = r.stdout\n  assert_match(\n    /module-env2-mod3-puppet-function/,\n    result,\n    \"puppet.conf specified environment env2 mod3::module_key lookup failed, expeccted /module-env2-mod3-puppet-function/\"\n  )\n\n  step 'apply env3 puppet.conf manifest'\n  apply_manifest_on(master, @env3puppetconfmanifest, :catch_failures => true)\n\n  step \"puppet.conf specified environment env3 environment_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'environment_key'))\n  result = r.stdout\n  assert_match(\n    /env-env3-puppet-function/,\n    result,\n    \"puppet.conf specified environment env1, environment_key lookup failed, expected /env-env3-puppet-function/\"\n  )\n\n  step \"puppet.conf specified environment env3 mod2::module_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'mod2::module_key'))\n  result = r.stdout\n  assert_match(\n    /module-env3-mod2-ruby-function/,\n    result,\n    \"puppet.conf specified environment env2 mod3::module_key lookup failed, expeccted /module-env3-mod2-ruby-function/\"\n  )\n\n  step 'apply env4 puppet.conf manifest'\n  apply_manifest_on(master, @env4puppetconfmanifest, :catch_failures => true)\n\n  step \"puppet.conf specified environment env4 environment_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'environment_key'), :acceptable_exit_codes => [1])\n\n  step \"puppet.conf specified environment env4 mod1::module_key\"\n  r = on(master, puppet('lookup', \"--confdir #{@confdir}\", 'mod1::module_key'))\n  result = r.stdout\n  assert_match(\n    /module-env4-mod1-hiera/,\n    result,\n    \"puppet.conf specified environment env4 mod1::module_key lookup failed, expeccted /module-env4-mod1-hiera/\"\n  )\n\n  step 'apply enc manifest'\n  apply_manifest_on(master, @encmanifest, :catch_failures => true)\n\n  step \"--compile uses environment specified in ENC\"\n  r = on(master, puppet('lookup', '--compile', \"--node #{@node1}\", \"--confdir #{@confdir}\", \"--facts #{@coderoot}/facts.yaml\", 'environment_key'))\n  result = r.stderr\n  assert_match(\n    /CA is not available/,\n    result,\n    \"lookup in ENC specified environment failed\"\n  )\n\n  step \"handle certificate\"\n  on(master, \"puppetserver ca generate --certname #{@node1}\")\n  on(master, \"puppetserver ca generate --certname #{@node2}\")\n  on(master, \"mkdir -p #{@testroot}/puppet/ssl/certs\")\n  on(master, \"mkdir -p #{@testroot}/puppet/ssl/private_keys\")\n  on(master, \"cp -a /etc/puppetlabs/puppet/ssl/certs/ca.pem #{@testroot}/puppet/ssl/certs\")\n  on(master, \"cp -a /etc/puppetlabs/puppet/ssl/crl.pem #{@testroot}/puppet/ssl\")\n  on(master, \"cp -a /etc/puppetlabs/puppet/ssl/private_keys/#{master.connection.hostname}.pem #{@testroot}/puppet/ssl/private_keys\")\n  on(master, \"cp -a /etc/puppetlabs/puppet/ssl/certs/#{master.connection.hostname}.pem #{@testroot}/puppet/ssl/certs\")\n\n  step \"--compile uses environment specified in ENC\"\n  r = on(master, puppet('lookup', '--compile', \"--node #{@node1}\", \"--confdir #{@confdir}\", \"--facts #{@coderoot}/facts.yaml\", 'environment_key'))\n  result = r.stdout\n  assert_match(\n    /env-env2-ruby-function/,\n    result,\n    \"lookup in ENC specified environment failed\"\n  )\n\n  step \"without --compile does not use environment specified in ENC\"\n  r = on(master, puppet('lookup', \"--node #{@node1}\", \"--confdir #{@confdir}\", \"--facts #{@coderoot}/facts.yaml\", 'environment_key'))\n  result = r.stdout\n  assert_match(\n    /env-production hiera provided value/,\n    result,\n    \"lookup in production environment failed\"\n  )\n\n  step \"lookup fails when there are no facts available\"\n  r = on(master, puppet('lookup', '--compile', \"--node #{@node1}\", \"--confdir #{@confdir}\", 'environment_key'), :acceptable_exit_codes => [1])\n  result = r.stderr\n  assert_match(\n    /No facts available/,\n    result,\n    \"Expected to raise when there were no facts available.\"\n  )\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/3935_pluginsync_should_follow_symlinks.rb",
    "content": "test_name \"pluginsync should not error when modulepath is a symlink and no modules have plugin directories\"\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\nstep \"Create a modulepath directory which is a symlink and includes a module without facts.d or lib directories\"\nbasedir = master.tmpdir(\"symlink_modulepath\")\n\ntarget           =  \"#{basedir}/target_dir\"\ntest_module_dir  =  \"#{target}/module1\"\nlink_dest        =  \"#{basedir}/link_dest\"\nmodulepath       =  \"#{link_dest}\"\nmodulepath << \"#{master['sitemoduledir']}\" if master.is_pe?\n\napply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode   => \"0750\",\n  owner  => #{master.puppet['user']},\n  group  => #{master.puppet['group']},\n}\n\nfile {\n  '#{basedir}':;\n  '#{target}':;\n  '#{test_module_dir}':;\n}\n\nfile { '#{link_dest}':\n  ensure => link,\n  target => '#{target}',\n}\nMANIFEST\n\nmaster_opts = {\n  'main' => {\n    'basemodulepath' => \"#{modulepath}\"\n  }\n}\n\nwith_puppet_running_on master, master_opts, basedir do\n  agents.each do |agent|\n    on(agent, puppet('agent', \"-t\")) do |result|\n      refute_match(/Could not retrieve information from environment production source\\(s\\) puppet:\\/\\/\\/pluginfacts/, result.stderr)\n      refute_match(/Could not retrieve information from environment production source\\(s\\) puppet:\\/\\/\\/plugins/, result.stderr)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/4420_pluginfacts_should_be_resolvable_on_agent.rb",
    "content": "test_name \"Pluginsync'ed external facts should be resolvable on the agent\" do\n  tag 'audit:high',\n      'audit:integration'\n\n#\n# This test is intended to ensure that external facts downloaded onto an agent via\n# pluginsync are resolvable. In Linux, the external fact should have the same\n# permissions as its source on the master.\n#\n\n  step \"Create a codedir with a manifest and test module with external fact\"\n  codedir = master.tmpdir('4420-codedir')\n\n  site_manifest_content = <<EOM\nnode default {\n  include mymodule\n  notify { \"foo is ${foo}\": }\n}\nEOM\n\n  unix_fact = <<EOM\n#!/bin/sh\necho \"foo=bar\"\nEOM\n\n  win_fact = <<EOM\n@echo off\necho foo=bar\nEOM\n\n  apply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode   => \"0755\",\n  owner  => #{master.puppet['user']},\n  group  => #{master.puppet['group']},\n}\n\nfile {\n  '#{codedir}':;\n  '#{codedir}/environments':;\n  '#{codedir}/environments/production':;\n  '#{codedir}/environments/production/manifests':;\n  '#{codedir}/environments/production/modules':;\n  '#{codedir}/environments/production/modules/mymodule':;\n  '#{codedir}/environments/production/modules/mymodule/manifests':;\n  '#{codedir}/environments/production/modules/mymodule/facts.d':;\n}\n\nfile { '#{codedir}/environments/production/manifests/site.pp':\n  ensure => file,\n  content => '#{site_manifest_content}',\n}\n\nfile { '#{codedir}/environments/production/modules/mymodule/manifests/init.pp':\n  ensure => file,\n  content => 'class mymodule {}',\n}\n\nfile { '#{codedir}/environments/production/modules/mymodule/facts.d/unix_external_fact.sh':\n  ensure  => file,\n  mode    => '755',\n  content => '#{unix_fact}',\n}\nfile { '#{codedir}/environments/production/modules/mymodule/facts.d/win_external_fact.bat':\n  ensure  => file,\n  mode    => '644',\n  content => '#{win_fact}',\n}\nMANIFEST\n\n  master_opts = {\n      'main' => {\n          'environmentpath' => \"#{codedir}/environments\"\n      }\n  }\n\n  with_puppet_running_on(master, master_opts, codedir) do\n    agents.each do |agent|\n      factsd         = agent.tmpdir('facts.d')\n      pluginfactdest = agent.tmpdir('facts.d')\n\n      teardown do\n        on(master, \"rm -rf '#{codedir}'\")\n        on(agent, \"rm -rf '#{factsd}' '#{pluginfactdest}'\")\n      end\n\n      step \"Pluginsync the external fact to the agent and ensure it resolves correctly\" do\n        on(agent, puppet('agent', '-t', '--pluginfactdest', factsd), :acceptable_exit_codes => [2]) do |result|\n          assert_match(/foo is bar/, result.stdout)\n        end\n      end\n      step \"Use plugin face to download to the agent\" do\n        on(agent, puppet('plugin', 'download', '--pluginfactdest', pluginfactdest)) do |result|\n          assert_match(/Downloaded these plugins: .*external_fact/, result.stdout) unless agent['locale'] == 'ja'\n        end\n      end\n\n      step \"Ensure it resolves correctly\" do\n        on(agent, puppet('apply', '--pluginfactdest', pluginfactdest, '-e', \"'notify { \\\"foo is ${foo}\\\": }'\")) do |result|\n          assert_match(/foo is bar/, result.stdout)\n        end\n      end\n      # Linux specific tests\n      next if agent['platform'] =~ /windows/\n\n      step \"In Linux, ensure the pluginsync'ed external fact has the same permissions as its source\" do\n        on(agent, puppet('resource', \"file '#{factsd}/unix_external_fact.sh'\")) do |result|\n          assert_match(/0755/, result.stdout)\n        end\n      end\n      step \"In Linux, ensure puppet apply uses the correct permissions\" do\n        test_source = File.join('/', 'tmp', 'test')\n        on(agent, puppet('apply', \"-e \\\"file { '#{test_source}': ensure => file, mode => '0456' }\\\"\"))\n\n        { 'source_permissions => use,'    => /0456/,\n          'source_permissions => ignore,' => /0644/,\n          ''                              => /0644/\n        }.each do |source_permissions, mode|\n          on(agent, puppet('apply', \"-e \\\"file { '/tmp/test_target': ensure => file, #{source_permissions} source => '#{test_source}' }\\\"\"))\n          on(agent, puppet('resource', \"file /tmp/test_target\")) do |result|\n            assert_match(mode, result.stdout)\n          end\n\n          on(agent, \"rm -f /tmp/test_target\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/4847_pluginfacts_should_be_resolvable_from_applications.rb",
    "content": "test_name \"Pluginsync'ed custom facts should be resolvable during application runs\" do\n\n  tag 'audit:high',\n      'audit:integration'\n\n  #\n  # This test is intended to ensure that custom facts downloaded onto an agent via\n  # pluginsync are resolvable by puppet applications besides agent/apply.\n  #\n\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  tmp_environment         = mk_tmp_environment_with_teardown(master, 'resolve')\n  master_module_dir       = \"#{environmentpath}/#{tmp_environment}/modules/module_name\"\n  master_type_dir         = \"#{master_module_dir}/lib/puppet/type\"\n  master_module_type_file = \"#{master_type_dir}/test4847.rb\"\n  master_provider_dir     = \"#{master_module_dir}/lib/puppet/provider/test4847\"\n  master_provider_file    = \"#{master_provider_dir}/only.rb\"\n  master_facter_dir       = \"#{master_module_dir}/lib/facter\"\n  master_facter_file      = \"#{master_facter_dir}/foo.rb\"\n  on(master, \"mkdir -p '#{master_type_dir}' '#{master_provider_dir}' '#{master_facter_dir}'\")\n\n  teardown do\n    on(master, \"rm -rf '#{master_module_dir}'\")\n\n    # Remove all traces of the last used environment\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  test_type = <<-TYPE\n      Puppet::Type.newtype(:test4847) do\n        newparam(:name, :namevar => true)\n      end\n  TYPE\n  create_remote_file(master, master_module_type_file, test_type)\n\n  test_provider = <<-PROVIDER\n      Puppet::Type.type(:test4847).provide(:only) do\n        def self.instances\n          warn \"fact foo=\\#{Facter.value('foo')}\"\n          []\n        end\n      end\n  PROVIDER\n  create_remote_file(master, master_provider_file, test_provider)\n\n  foo_fact_content = <<-FACT_FOO\n      Facter.add('foo') do\n        setcode do\n          'bar'\n        end\n      end\n  FACT_FOO\n  create_remote_file(master, master_facter_file, foo_fact_content)\n  on(master, \"chmod 755 '#{master_module_type_file}' '#{master_provider_file}' '#{master_facter_file}'\")\n\n  with_puppet_running_on(master, {}) do\n    agents.each do |agent|\n      on(agent, puppet(\"agent -t --environment #{tmp_environment}\"))\n      on(agent, puppet('resource test4847')) do |result|\n        assert_match(/fact foo=bar/, result.stderr)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/7316_apps_should_be_available_via_pluginsync.rb",
    "content": "test_name 'the pluginsync functionality should sync app definitions, and they should be runnable afterwards' do\n\n  tag 'audit:high',\n      'audit:integration'\n\n  #\n  # This test is intended to ensure that pluginsync syncs app definitions to the agents.\n  # Further, the apps should be runnable on the agent after the sync has occurred.\n  #\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  tmp_environment   = mk_tmp_environment_with_teardown(master, 'app')\n  master_module_dir = \"#{environmentpath}/#{tmp_environment}/modules\"\n  on(master, \"mkdir -p '#{master_module_dir}'\")\n\n  teardown do\n    on(master, \"rm -rf '#{master_module_dir}'\")\n\n    # Remove all traces of the last used environment\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  app_name   = \"superbogus\"\n  app_desc   = \"a simple application for testing application delivery via plugin sync\"\n  app_output = \"Hello from the #{app_name} application\"\n\n  master_module_file_content = <<-HERE\nrequire 'puppet/application'\n\nclass Puppet::Application::#{app_name.capitalize} < Puppet::Application\n\n  def help\n    <<-HELP\n\npuppet-#{app_name}(8) -- #{app_desc}\n========\n    HELP\n  end\n\n  def main()\n    puts(\"#{app_output}\")\n  end\nend\n  HERE\n\n  # here we create a custom app, which basically doesn't do anything except\n  # for print a hello-world message\n  #\n  master_module_app_path = \"#{master_module_dir}/#{app_name}/lib/puppet/application\"\n  master_module_app_file = \"#{master_module_app_path}/#{app_name}.rb\"\n  on(master, \"mkdir -p '#{master_module_app_path}'\")\n  create_remote_file(master, master_module_app_file, master_module_file_content)\n  on(master, \"chmod 755 '#{master_module_app_file}'\")\n\n  step \"start the master\" do\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n\n        agent_lib_dir         = agent.tmpdir('agent_lib_sync')\n        agent_module_app_file = \"#{agent_lib_dir}/puppet/application/#{app_name}.rb\"\n        teardown do\n          on(agent, \"rm -rf '#{agent_lib_dir}'\")\n        end\n\n        # the module files shouldn't exist on the agent yet because they haven't been synced\n        step \"verify that the module files don't exist on the agent path\" do\n          if file_exists?(agent, agent_module_app_file)\n            fail_test(\"app file already exists on agent: '#{agent_module_app_file}'\")\n          end\n        end\n\n        step \"run the agent\" do\n          on(agent, puppet(\"agent --libdir='#{agent_lib_dir}' --test --environment '#{tmp_environment}'\")) do |result|\n            refute_match(\n              /The \\`source_permissions\\` parameter is deprecated/,\n              result.stderr,\n              \"pluginsync should not get a deprecation warning for source_permissions\")\n          end\n        end\n\n        step \"verify that the module files were synced down to the agent\" do\n          unless file_exists?(agent, agent_module_app_file)\n            fail_test(\"The app file we expect was not not synced to agent: '#{agent_module_app_file}'\")\n          end\n        end\n\n        step \"verify that the application shows up in help\" do\n          on(agent, PuppetCommand.new(:help, \"--libdir='#{agent_lib_dir}'\")) do |result|\n            assert_match(/^\\s+#{app_name}\\s+#{app_desc}/, result.stdout)\n          end\n        end\n\n        step \"verify that we can run the application\" do\n          on(agent, PuppetCommand.new(:\"#{app_name}\", \"--libdir='#{agent_lib_dir}'\")) do |result|\n            assert_match(/^#{app_output}/, result.stdout)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/7316_faces_with_app_stubs_should_be_available_via_pluginsync.rb",
    "content": "test_name \"the pluginsync functionality should sync app definitions, and they should be runnable afterwards\"\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\n#\n# This test is intended to ensure that pluginsync syncs face definitions to the agents.\n# Further, the face should be runnable on the agent after the sync has occurred.\n#\n# (NOTE: When this test is passing, it should resolve both #7316 re: verifying that apps/faces can\n#  be run on the agent node after a plugin sync, and #6753 re: being able to run a face without\n#  having a placeholder stub file in the \"applications\" directory.)\n#\n\nrequire 'puppet/acceptance/temp_file_utils'\n\nextend Puppet::Acceptance::TempFileUtils\n\ninitialize_temp_dirs()\nall_tests_passed = false\n\n###############################################################################\n# BEGIN TEST LOGIC\n###############################################################################\n\n# create some vars to point to the directories that we're going to point the master/agents at\nenvironments_dir = \"environments\"\nmaster_module_dir = \"#{environments_dir}/production/modules\"\nagent_lib_dir = \"agent_lib\"\n\napp_name = \"superbogus\"\napp_desc = \"a simple %1$s for testing %1$s delivery via plugin sync\"\napp_output = \"Hello from the #{app_name} %s\"\n\nmaster_module_face_content = <<-HERE\nPuppet::Face.define(:#{app_name}, '0.0.1') do\n  copyright \"Puppet Labs\", 2011\n  license   \"Apache 2 license; see COPYING\"\n\n  summary \"#{app_desc % \"face\"}\"\n\n  action(:foo) do\n    summary \"a test action defined in the test face in the main puppet lib dir\"\n\n    default\n    when_invoked do |*args|\n      puts \"#{app_output % \"face\"}\"\n    end\n  end\n\nend\nHERE\n\nmaster_module_app_content = <<-HERE\nrequire 'puppet/application/face_base'\n\nclass Puppet::Application::#{app_name.capitalize} < Puppet::Application::FaceBase\nend\n\nHERE\n\n# this begin block is here for handling temp file cleanup via an \"ensure\" block\n# at the very end of the test.\nbegin\n\n  # here we create a custom app, which basically doesn't do anything except for\n  # print a hello-world message\n  agent_module_face_file = \"#{agent_lib_dir}/puppet/face/#{app_name}.rb\"\n  master_module_face_file = \"#{master_module_dir}/#{app_name}/lib/puppet/face/#{app_name}.rb\"\n\n  agent_module_app_file = \"#{agent_lib_dir}/puppet/application/#{app_name}.rb\"\n  master_module_app_file = \"#{master_module_dir}/#{app_name}/lib/puppet/application/#{app_name}.rb\"\n\n  # copy all the files to the master\n  step \"write our simple module out to the master\" do\n    create_test_file(master, master_module_app_file, master_module_app_content, :mkdirs => true)\n    create_test_file(master, master_module_face_file, master_module_face_content, :mkdirs => true)\n  end\n\n  step \"verify that the app file exists on the master\" do\n    unless test_file_exists?(master, master_module_app_file) then\n      fail_test(\"Failed to create app file '#{get_test_file_path(master, master_module_app_file)}' on master\")\n    end\n    unless test_file_exists?(master, master_module_face_file) then\n      fail_test(\"Failed to create face file '#{get_test_file_path(master, master_module_face_file)}' on master\")\n    end\n  end\n\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{get_test_file_path(master, environments_dir)}\",\n    }\n  }\n\n  step \"start the master\" do\n    with_puppet_running_on master, master_opts do\n\n      # the module files shouldn't exist on the agent yet because they haven't been synced\n      step \"verify that the module files don't exist on the agent path\" do\n        agents.each do |agent|\n            if test_file_exists?(agent, agent_module_app_file) then\n              fail_test(\"app file already exists on agent: '#{get_test_file_path(agent, agent_module_app_file)}'\")\n            end\n            if test_file_exists?(agent, agent_module_app_file) then\n              fail_test(\"face file already exists on agent: '#{get_test_file_path(agent, agent_module_face_file)}'\")\n            end\n        end\n      end\n\n      step \"run the agent\" do\n        agents.each do |agent|\n          on(agent, puppet('agent',\n                           \"--libdir=\\\"#{get_test_file_path(agent, agent_lib_dir)}\\\" \",\n                           \"--trace --test\")\n          )\n        end\n      end\n\n    end\n  end\n\n  step \"verify that the module files were synced down to the agent\" do\n    agents.each do |agent|\n      unless test_file_exists?(agent, agent_module_app_file) then\n        fail_test(\"The app file we expected was not synced to the agent: '#{get_test_file_path(agent, agent_module_app_file)}'\")\n      end\n      unless test_file_exists?(agent, agent_module_face_file) then\n        fail_test(\"The face file we expected was not not synced to the agent: '#{get_test_file_path(agent, agent_module_face_file)}'\")\n      end\n    end\n  end\n\n  step \"verify that the application shows up in help\" do\n    agents.each do |agent|\n      on(agent, PuppetCommand.new(:help, \"--libdir=\\\"#{get_test_file_path(agent, agent_lib_dir)}\\\"\")) do |result|\n        assert_match(/^\\s+#{app_name}\\s+#{app_desc % \"face\"}/, result.stdout)\n      end\n    end\n  end\n\n  step \"verify that we can run the application\" do\n    agents.each do |agent|\n      on(agent, PuppetCommand.new(:\"#{app_name}\", \"--libdir=\\\"#{get_test_file_path(agent, agent_lib_dir)}\\\"\")) do |result|\n        assert_match(/^#{app_output % \"face\"}/, result.stdout)\n      end\n    end\n  end\n\n  step \"clear out the libdir on the agents in preparation for the next test\" do\n    agents.each do |agent|\n      on(agent, \"rm -rf #{get_test_file_path(agent, agent_lib_dir)}/*\")\n    end\n  end\n\n  all_tests_passed = true\n\nensure\n  ##########################################################################################\n  # Clean up all of the temp files created by this test.  It would be nice if this logic\n  # could be handled outside of the test itself; I envision a stanza like this one appearing\n  # in a very large number of the tests going forward unless it is handled by the framework.\n  ##########################################################################################\n  if all_tests_passed then\n    remove_temp_dirs()\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/feature/pluginsync_should_sync_features.rb",
    "content": "test_name \"the pluginsync functionality should sync feature and function definitions\" do\n\n  tag 'audit:high',\n      'audit:integration'\n\n  #\n  # This test is intended to ensure that pluginsync syncs feature definitions to\n  # the agents.  It checks the feature twice; once to make sure that it gets\n  # loaded successfully during the run in which it was synced, and once to ensure\n  # that it still gets loaded successfully during the subsequent run (in which it\n  # should not be synced because the files haven't changed.)\n  #\n\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  module_name = 'superbogus'\n  tmp_environment = mk_tmp_environment_with_teardown(master, 'sync')\n  master_module_dir = \"#{environmentpath}/#{tmp_environment}/modules\"\n  master_module_type_path = \"#{master_module_dir}/#{module_name}/lib/puppet/type/\"\n  master_module_feature_path = \"#{master_module_dir}/#{module_name}/lib/puppet/feature\"\n  master_module_function_path = \"#{master_module_dir}/#{module_name}/lib/puppet/functions\"\n  master_module_function_path_namespaced = \"#{master_module_dir}/#{module_name}/lib/puppet/functions/superbogus\"\n  on(master, \"mkdir -p '#{master_module_dir}'\")\n  on(master, \"mkdir -p '#{master_module_type_path}' '#{master_module_feature_path}' '#{master_module_function_path}' #{master_module_function_path_namespaced}\")\n\n  master_module_type_file = \"#{master_module_type_path}/#{module_name}.rb\"\n  master_module_type_content = <<-HERE\n    module Puppet\n      Type.newtype(:#{module_name}) do\n        newparam(:name) do\n          isnamevar\n        end\n    \n        newproperty(:testfeature) do\n          def sync\n            Puppet.info(\"The value of the #{module_name} feature is: \\#{Puppet.features.#{module_name}?}\")\n          end\n          def retrieve\n            :absent\n          end\n          def insync?(is)\n            false\n          end\n        end\n      end\n    end\n  HERE\n  create_remote_file(master, master_module_type_file, master_module_type_content)\n\n  master_module_feature_file = \"#{master_module_feature_path}/#{module_name}.rb\"\n  master_module_feature_content = <<-HERE\n    Puppet.features.add(:#{module_name}) do\n      Puppet.info(\"#{module_name} feature being queried\")\n      true\n    end\n  HERE\n  create_remote_file(master, master_module_feature_file, master_module_feature_content)\n  on(master, \"chmod 755 '#{master_module_type_file}' '#{master_module_feature_file}'\")\n\n  master_module_function_file = \"#{master_module_function_path}/bogus_function.rb\"\n  master_module_function_content = <<-HERE\n    Puppet::Functions.create_function(:bogus_function) do\n      dispatch :bogus_function do\n      end\n      def bogus_function()\n        three = call_function('round', 3.14)\n        hostname = `facter hostname`\n        \"Three is \\#{three}. bogus_function reporting hostname is \\#{hostname}\"\n      end\n    end\n  HERE\n  create_remote_file(master, master_module_function_file, master_module_function_content)\n  on(master, \"chmod 755 '#{master_module_function_file}' '#{master_module_function_file}'\")\n\n  master_module_namespaced_function_file = \"#{master_module_function_path_namespaced}/bogus_function2.rb\"\n  master_module_namespaced_function_content = <<-HERE\n    Puppet::Functions.create_function(:'superbogus::bogus_function2') do\n      dispatch :bogus_function2 do\n      end\n      def bogus_function2()\n        four = call_function('round', 4.14)\n        hostname = `facter hostname`\n        \"Four is \\#{four}. bogus_function reporting hostname is \\#{hostname}\"\n      end\n    end\n  HERE\n  create_remote_file(master, master_module_namespaced_function_file, master_module_namespaced_function_content)\n  on(master, \"chmod 755 '#{master_module_namespaced_function_file}'\")\n\n  site_pp = <<-HERE\n    #{module_name} { \"This is the title of the #{module_name} type instance in site.pp\":\n      testfeature => \"Hi.  I'm setting the testfeature property of #{module_name} here in site.pp\",\n    }\n    notify { module_function:\n        message => Deferred('bogus_function', [])\n    }\n    notify { module_function2:\n        message => Deferred('superbogus::bogus_function2', [])\n    }\n  HERE\n  create_sitepp(master, tmp_environment, site_pp)\n\n  # These master opts should not be necessary whence content negotation for\n  # Puppet 6.0.0 is completed, and this should just be removed.\n  master_opts = {\n    'master' => {\n      'rich_data' => 'true'\n    }\n  }\n\n  step 'start the master' do\n    with_puppet_running_on(master, master_opts) do\n      agents.each do |agent|\n        agent_lib_dir             = agent.tmpdir('libdir')\n        agent_module_type_file    = \"#{agent_lib_dir}/puppet/type/#{module_name}.rb\"\n        agent_module_feature_file = \"#{agent_lib_dir}/puppet/feature/#{module_name}.rb\"\n        agent_module_function_file = \"#{agent_lib_dir}/puppet/functions/bogus_function.rb\"\n        agent_module_namespaced_function_file = \"#{agent_lib_dir}/puppet/functions/superbogus/bogus_function2.rb\"\n\n        facter_hostname = fact_on(agent, 'hostname')\n        step \"verify that the module files don't exist on the agent path\" do\n          [agent_module_type_file, agent_module_feature_file, agent_module_function_file].each do |file_path|\n            if file_exists?(agent, file_path)\n              fail_test(\"file should not exist on the agent yet: '#{file_path}'\")\n            end\n          end\n        end\n\n        step 'run the agent and verify that it loaded the feature' do\n          on(agent, puppet(\"agent -t --libdir='#{agent_lib_dir}' --rich_data --environment '#{tmp_environment}'\"),\n             :acceptable_exit_codes => [2]) do |result|\n            assert_match(/The value of the #{module_name} feature is: true/, result.stdout,\n                         \"Expected agent stdout to include confirmation that the feature was 'true'\")\n            assert_match(/Three is 3. bogus_function reporting hostname is #{facter_hostname}/, result.stdout,\n                         \"Expect the agent stdout to run bogus_function and report hostname\")\n            assert_match(/Four is 4. bogus_function reporting hostname is #{facter_hostname}/, result.stdout,\n              \"Expect the agent stdout to run bogus_function and report hostname\")\n          end\n        end\n\n        step 'verify that the module files were synced down to the agent' do\n          [agent_module_type_file, agent_module_feature_file, agent_module_function_file, agent_module_namespaced_function_file].each do |file_path|\n            unless file_exists?(agent, file_path)\n              fail_test(\"Expected file to exist on the agent now: '#{file_path}'\")\n            end\n          end\n        end\n\n        step 'run the agent again with a cached catalog' do\n          on(agent, puppet(\"agent -t --libdir='#{agent_lib_dir}' --use_cached_catalog --rich_data --environment '#{tmp_environment}'\"), :acceptable_exit_codes => [2]) do |result|\n            assert_match(/The value of the #{module_name} feature is: true/, result.stdout,\n                         \"Expected agent stdout to include confirmation that the feature was 'true'\")\n            assert_match(/Three is 3. bogus_function reporting hostname is #{facter_hostname}/, result.stdout,\n              \"Expect the agent stdout to run bogus_function and report hostname\")\n            assert_match(/Four is 4. bogus_function reporting hostname is #{facter_hostname}/, result.stdout,\n              \"Expect the agent stdout to run bogus_function and report hostname\")\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/pluginsync/files_earlier_in_modulepath_take_precendence.rb",
    "content": "test_name \"earlier modules take precendence over later modules in the modulepath\"\n\ntag 'audit:high',\n    'audit:integration',\n    'server'\n\nstep \"Create some modules in the modulepath\"\nbasedir = master.tmpdir(\"module_precedence\")\n\nmodule_dir1 = \"#{basedir}/environments/production/modules1\"\nmodule_dir2 = \"#{basedir}/modules2\"\nmodulepath = \"#{module_dir1}:#{module_dir2}\"\nmodulepath << \":#{master['sitemoduledir']}\" if master.is_pe?\n\napply_manifest_on(master, <<MANIFEST, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile {\n  '#{basedir}':;\n  '#{module_dir2}':;\n  '#{module_dir2}/a':;\n  '#{module_dir2}/a/lib':;\n  '#{basedir}/environments':;\n  '#{basedir}/environments/production':;\n  '#{module_dir1}':;\n  '#{module_dir1}/a':;\n  '#{module_dir1}/a/lib':;\n}\n\nfile { '#{basedir}/environments/production/environment.conf':\n  ensure => file,\n  content => \"modulepath='#{modulepath}'\",\n  mode => \"0640\",\n}\n\nfile { \"mod1\":\n  ensure => file,\n  path => \"#{module_dir1}/a/lib/foo.rb\",\n  content => \"'from the first module'\",\n  mode => \"0640\",\n}\n\nfile { \"mod2\":\n  ensure => file,\n  path => \"#{module_dir2}/a/lib/foo.rb\",\n  content => \"'from the second module'\",\n  mode => \"0640\",\n}\nMANIFEST\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{basedir}/environments\",\n  }\n}\n\nwith_puppet_running_on master, master_opts, basedir do\n  agents.each do |agent|\n    on(agent, puppet('agent', \"-t\"))\n    on(agent, \"cat \\\"#{agent.puppet['vardir']}/lib/foo.rb\\\"\") do |result|\n      assert_match(/from the first module/, result.stdout, \"The synced plugin was not found or the wrong version was synced\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/apt_install_package_with_range.rb",
    "content": "test_name \"apt can install range if package is not installed\" do\n  confine :to, :platform => /debian|ubuntu/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = \"helloworld\"\n  available_package_versions = ['1.0-1', '1.19-1', '2.0-1']\n  repo_fixture_path = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures', 'debian-repo')\n\n  agents.each do |agent|\n    scp_to(agent, repo_fixture_path, '/tmp')\n\n    file_manifest = resource_manifest('file', '/etc/apt/sources.list.d/tmp.list', ensure: 'present', content: 'deb [trusted=yes] file:/tmp/debian-repo ./')\n    apply_manifest_on(agent, file_manifest)\n\n    on(agent, 'apt-get update')\n\n    teardown do\n      package_absent(agent, package, '--force-yes')\n      file_manifest = resource_manifest('file', '/etc/apt/sources.list.d/tmp.list', ensure: 'absent')\n      apply_manifest_on(agent, file_manifest)\n      on(agent, 'rm -rf /tmp/debian-repo')\n      on(agent, 'apt-get update')\n    end\n\n    step \"Ensure that package is installed first if not present\" do\n      package_manifest = resource_manifest('package', package, ensure: \"<=#{available_package_versions[1]}\")\n      apply_manifest_on(agent, package_manifest)\n      installed_package_version = on(agent.name, \"apt-cache policy #{package} | sed -n -e 's/Installed: //p'\").stdout\n      assert_match(available_package_versions[1], installed_package_version)\n    end\n\n    step \"Ensure that package is updated\" do\n      package_manifest = resource_manifest('package', package, ensure: \">#{available_package_versions[1]}\")\n      apply_manifest_on(agent, package_manifest)\n      installed_package_version = on(agent.name, \"apt-cache policy #{package} | sed -n -e 's/Installed: //p'\").stdout\n      assert_match(available_package_versions[2], installed_package_version)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/dnfmodule_enable_only.rb",
    "content": "test_name \"dnfmodule can change flavors\" do\n  confine :to, :platform => /el-8-x86_64/  # only el/centos 8 have the appstream repo\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  without_profile = '389-ds'\n  with_profile = 'swig'\n\n  agents.each do |agent|\n    skip_test('appstream repo not present') unless on(agent, 'dnf repolist').stdout.include?('appstream')\n    teardown do\n      apply_manifest_on(agent, resource_manifest('package', without_profile, ensure: 'absent', provider: 'dnfmodule'))\n      apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'absent', provider: 'dnfmodule'))\n    end\n  end\n\n  step \"Enable module with no default profile: #{without_profile}\" do\n    apply_manifest_on(agent, resource_manifest('package', without_profile, ensure: 'present', provider: 'dnfmodule'), expect_changes: true)\n    on(agent, \"dnf module list --enabled | grep #{without_profile}\")\n  end\n\n  step \"Ensure idempotency for: #{without_profile}\" do\n    apply_manifest_on(agent, resource_manifest('package', without_profile, ensure: 'present', provider: 'dnfmodule'), catch_changes: true)\n  end\n\n  step \"Enable module with a profile: #{with_profile}\" do\n    apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'present', enable_only: true, provider: 'dnfmodule'), expect_changes: true)\n    on(agent, \"dnf module list --enabled | grep #{with_profile}\")\n  end\n\n  step \"Ensure idempotency for: #{with_profile}\" do\n    apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'present', enable_only: true, provider: 'dnfmodule'), catch_changes: true)\n  end\n\n  step \"Install a flavor for: #{with_profile}\" do\n    apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'present', flavor: 'common', provider: 'dnfmodule'), expect_changes: true)\n    on(agent, \"dnf module list --installed | grep #{with_profile}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/dnfmodule_ensure_versionable.rb",
    "content": "test_name \"dnfmodule is versionable\" do\n  confine :to, :platform => /el-8-x86_64/  # only el/centos 8 have the appstream repo\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n\n  package = \"postgresql\"\n\n  agents.each do |agent|\n    skip_test('appstream repo not present') unless on(agent, 'dnf repolist').stdout.include?('appstream')\n    teardown do\n      apply_manifest_on(agent, resource_manifest('package', package, ensure: 'absent', provider: 'dnfmodule'))\n    end\n  end\n\n  step \"Ensure we get the newer version by default\" do\n    apply_manifest_on(agent, resource_manifest('package', package, ensure: 'present', provider: 'dnfmodule'))\n    on(agent, 'postgres --version') do |version|\n      assert_match('postgres (PostgreSQL) 10', version.stdout, 'package version not correct')\n    end\n  end\n\n  step \"Ensure we get a specific version if we want it\" do\n    apply_manifest_on(agent, resource_manifest('package', package, ensure: '9.6', provider: 'dnfmodule'))\n    on(agent, 'postgres --version') do |version|\n      assert_match('postgres (PostgreSQL) 9.6', version.stdout, 'package version not correct')\n    end\n  end\n\n  step \"Ensure we can disable a package\" do\n    apply_manifest_on(agent, resource_manifest('package', package, ensure: :disabled, provider: 'dnfmodule'))\n    on(agent, \"dnf module list | grep #{package}\") do |output|\n      output.stdout.each_line do |line|\n        assert_match(\"\\[x\\]\", line, 'package not disabled')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/dnfmodule_manages_flavors.rb",
    "content": "test_name \"dnfmodule can change flavors\" do\n  confine :to, :platform => /el-8-x86_64/  # only el/centos 8 have the appstream repo\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = \"postgresql\"\n\n  agents.each do |agent|\n    skip_test('appstream repo not present') unless on(agent, 'dnf repolist').stdout.include?('appstream')\n    teardown do\n      apply_manifest_on(agent, resource_manifest('package', package, ensure: 'absent', provider: 'dnfmodule'))\n    end\n  end\n\n  step \"Install the client #{package} flavor\" do\n    apply_manifest_on(agent, resource_manifest('package', package, ensure: 'present', flavor: 'client', provider: 'dnfmodule'))\n    on(agent, \"dnf module list --installed | grep #{package} | sed -E 's/\\\\[d\\\\] //g'\") do |output|\n      assert_match('client [i]', output.stdout, 'installed flavor not correct')\n    end\n  end\n\n  step \"Install the server #{package} flavor\" do\n    apply_manifest_on(agent, resource_manifest('package', package, ensure: 'present', flavor: 'server', provider: 'dnfmodule'))\n    on(agent, \"dnf module list --installed | grep #{package} | sed -E 's/\\\\[d\\\\] //g'\") do |output|\n      assert_match('server [i]', output.stdout, 'installed flavor not correct')\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/dpkg_ensure_latest_virtual_packages.rb",
    "content": "test_name \"dpkg ensure latest with allow_virtual set to true, the virtual package should detect and install a real package\" do\n  confine :to, :platform => /debian/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  pkg = \"rubygems\"\n\n  agents.each do |agent|\n    ruby_present = on(agent, 'dpkg -s ruby', accept_all_exit_codes: true).exit_code == 0\n\n    teardown do\n      if ruby_present\n        apply_manifest_on(agent, resource_manifest('package', 'ruby', ensure: 'present'))\n      else\n        apply_manifest_on(agent, resource_manifest('package', 'ruby', ensure: 'absent'))\n      end\n    end\n\n    step \"Uninstall system ruby if already present\" do\n      apply_manifest_on(agent, resource_manifest('package', 'ruby', ensure: 'absent')) if ruby_present\n    end\n\n    step \"Ensure latest should install ruby instead of rubygems when allow_virtual is set to true\" do\n      package_manifest_with_allow_virtual = resource_manifest('package', pkg, ensure: 'latest', allow_virtual: true)\n      apply_manifest_on(agent, package_manifest_with_allow_virtual, expect_changes: true)\n\n      output = on(agent, \"dpkg-query -W --showformat='${Status} ${Package} ${Version} [${Provides}]\\n' \").output\n      lines = output.split(\"\\n\")\n      matched_line = lines.find { |package| package.match(/[\\[ ](#{Regexp.escape(pkg)})[\\],]/)}\n\n      package_line_info = matched_line.split\n      real_package_name = package_line_info[3]\n      real_package_installed_version = package_line_info[4]\n\n      installed_version = on(agent, \"apt-cache policy #{real_package_name} | sed -n -e 's/Installed: //p'\").stdout.strip\n      assert_match(real_package_installed_version, installed_version)\n    end\n\n    step \"Ensure latest should not install ruby package if it's already installed and exit code should be 0\" do\n      package_manifest_with_allow_virtual = resource_manifest('package', pkg, ensure: 'latest', allow_virtual: true)\n      apply_manifest_on(agent, package_manifest_with_allow_virtual, :catch_changes => true)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/dpkg_hold_true_package_is_latest.rb",
    "content": "test_name \"dpkg ensure hold package is latest installed\" do\n  confine :to, :platform => /debian-9-amd64/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n\n  package = \"nginx\"\n\n  agents.each do |agent|\n    teardown do\n      package_absent(agent, package, '--force-yes')\n    end\n  end\n\n  step\"Ensure that package is installed first if not present\" do\n    expected_package_version = on(agent.name, \"apt-cache policy #{package} | sed -n -e 's/Candidate: //p'\").stdout\n    package_manifest = resource_manifest('package', package, mark: \"hold\")\n\n    apply_manifest_on(agent, package_manifest) do |result|\n      installed_package_version = on(agent.name, \"apt-cache policy #{package} | sed -n -e 's/Installed: //p'\").stdout\n      assert_match(expected_package_version, installed_package_version)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/dpkg_hold_true_should_preserve_version.rb",
    "content": "test_name \"dpkg ensure hold package should preserve version if package is already installed\" do\n  confine :to, :platform => /debian-9-amd64/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = \"openssl\"\n\n  step \"Ensure hold should lock to specific installed version\" do\n    existing_installed_version = on(agent.name, \"dpkg -s #{package} | sed -n -e 's/Version: //p'\").stdout\n    existing_installed_version.delete!(' ')\n\n    package_manifest_hold = resource_manifest('package', package, mark: \"hold\")\n    apply_manifest_on(agent, package_manifest_hold) do\n      installed_version = on(agent.name, \"apt-cache policy #{package} | sed -n -e 's/Installed: //p'\").stdout\n      installed_version.delete!(' ')\n      assert_match(existing_installed_version, installed_version)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/gem.rb",
    "content": "test_name \"gem provider should install and uninstall\" do\n  confine :to, :template => /centos-7-x86_64|redhat-7-x86_64/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = 'colorize'\n\n  agents.each do |agent|\n    # On a Linux host with only the 'agent' role, the puppet command fails when another Ruby is installed earlier in the PATH:\n    #\n    # [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\": }'\n    # Activating bundler (2.0.2) failed:\n    # Could not find 'bundler' (= 2.0.2) among 5 total gem(s)\n    # To install the version of bundler this project requires, run `gem install bundler -v '2.0.2'`\n    #\n    # Magically, the puppet command succeeds on a Linux host with both the 'master' and 'agent' roles.\n    #\n    # Puppet's Ruby makes a fine target. Unfortunately, it's first in the PATH on Windows: PUP-6134.\n    # Also, privatebindir isn't a directory on Windows, it's a PATH:\n    # https://github.com/puppetlabs/beaker-puppet/blob/master/lib/beaker-puppet/install_utils/aio_defaults.rb\n    #\n    # These tests depend upon testing being confined to /centos-7-x86_64|redhat-7-x86_64/.\n    if agent['roles'].include?('master')\n      original_path = agent.get_env_var('PATH').split('=').last\n\n      # https://github.com/puppetlabs/puppet-agent/blob/master/resources/files/puppet-agent.sh\n      puppet_agent_sh_path = '/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/opt/puppetlabs/bin'\n\n      system_gem_command = '/usr/bin/gem'\n\n      teardown do\n        step \"Teardown: Uninstall System Ruby, and reset PATH\" do\n          package_absent(agent, 'ruby')\n          agent.clear_env_var('PATH')\n          agent.add_env_var('PATH', original_path)\n        end\n      end\n      \n      step \"Setup: Install System Ruby, and set PATH to place System Ruby ahead of Puppet Ruby\" do\n        package_present(agent, 'ruby')\n        agent.add_env_var('PATH', puppet_agent_sh_path)\n      end\n    \n      step \"Install a gem package in System Ruby\" do\n        package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'gem' } )\n        apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n          list = on(agent, \"#{system_gem_command} list\").stdout\n          assert_match(/#{package} \\(/, list)\n        end\n        on(agent, \"#{system_gem_command} uninstall #{package}\")\n      end\n\n      step \"Uninstall a gem package in System Ruby\" do\n        on(agent, \"/usr/bin/gem install #{package}\")\n        package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'gem' } )\n        apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n          list = on(agent, \"#{system_gem_command} list\").stdout\n          refute_match(/#{package} \\(/, list)\n        end\n        on(agent, \"#{system_gem_command} uninstall #{package}\")\n      end\n      \n      step \"Uninstall System Ruby, and reset PATH\" do\n        package_absent(agent, 'ruby')\n        agent.add_env_var('PATH', original_path)\n      end\n    end\n\n    puppet_gem_command = \"#{agent['privatebindir']}/gem\"\n\n    step \"Install a gem package with a target command\" do\n      package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'gem', command: puppet_gem_command } )\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        list = on(agent, \"#{puppet_gem_command} list\").stdout\n        assert_match(/#{package} \\(/, list)\n      end\n      on(agent, \"#{puppet_gem_command} uninstall #{package}\")\n    end\n\n    step \"Install a gem package in a certain min max range\" do\n      package_manifest1 = resource_manifest('package', package, { ensure: '>0.5 <0.7', provider: 'gem' } )\n      package_manifest2 = resource_manifest('package', package, { ensure: '>0.7 <0.8.1',  provider: 'gem' } )\n\n      # Install package (with version between 0.5 and 0.7)\n      apply_manifest_on(agent, package_manifest1, :expect_changes => true) do\n        list = on(agent, \"#{puppet_gem_command} list\").stdout\n        assert_match(/#{package} \\((0.6.0)\\)/, list)\n      end\n\n      # Reapply same manifest and expect no changes\n      apply_manifest_on(agent, package_manifest1, :catch_changes => true)\n\n      # Install besides existing package (with version between 0.7 and 0.8.1) and expect changes\n      apply_manifest_on(agent, package_manifest2, :expect_changes => true) do\n        list = on(agent, \"#{puppet_gem_command} list\").stdout\n        assert_match(/#{package} \\((0.8.0, 0.6.0)\\)/, list)\n      end\n      on(agent, \"#{puppet_gem_command} uninstall #{package} --all\")\n    end\n\n    step \"Uninstall a gem package with a target command\" do\n      on(agent, \"#{puppet_gem_command} install #{package}\")\n      package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'gem', command: puppet_gem_command } )\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        list = on(agent, \"#{puppet_gem_command} list\").stdout\n        refute_match(/#{package} \\(/, list)\n      end\n      on(agent, \"#{puppet_gem_command} uninstall #{package}\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/pip.rb",
    "content": "test_name \"pip provider should install, use install_options with latest, and uninstall\" do\n  confine :to, :template => /centos/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = 'colorize'\n  pip_command = 'pip'\n\n  teardown do\n    on(agent, \"#{pip_command} uninstall #{package} --disable-pip-version-check --yes\", :accept_all_exit_codes => true)\n  end\n\n  agents.each do |agent|\n    step \"Setup: Install EPEL Repository, Python and Pip\" do\n      package_present(agent, 'epel-release')\n      if agent.platform =~ /el-8/\n        package_present(agent, 'python2')\n        package_present(agent, 'python2-pip')\n        pip_command = 'pip2'\n      else\n        package_present(agent, 'python')\n        package_present(agent, 'python-pip')\n      end\n    end\n\n    step \"Ensure presence of a pip package\" do\n      package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'pip' } )\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        list = on(agent, \"#{pip_command} list --disable-pip-version-check\").stdout\n        assert_match(/#{package} \\(/, list)\n      end\n      on(agent, \"#{pip_command} uninstall #{package} --disable-pip-version-check --yes\")\n    end\n\n    step \"Install a pip package using version range\" do\n      package_manifest1 = resource_manifest('package', package, { ensure: '<=1.1.0', provider: 'pip' } )\n      package_manifest2 = resource_manifest('package', package, { ensure: '<1.0.4',  provider: 'pip' } )\n\n      # Make a fresh package install (with version lower than or equal to 1.1.0)\n      apply_manifest_on(agent, package_manifest1, :expect_changes => true) do\n        list = on(agent, \"#{pip_command} list --disable-pip-version-check\").stdout\n        match = list.match(/#{package} \\((.+)\\)/)\n        installed_version = match[1] if match\n        assert_match(installed_version, '1.1.0')\n      end\n\n      # Reapply same manifest and expect no changes\n      apply_manifest_on(agent, package_manifest1, :catch_changes => true)\n\n      # Reinstall over existing package (with version lower than 1.0.4) and expect changes (to be 1.0.3)\n      apply_manifest_on(agent, package_manifest2, :expect_changes => true) do\n        list = on(agent, \"#{pip_command} list --disable-pip-version-check\").stdout\n        match = list.match(/#{package} \\((.+)\\)/)\n        installed_version = match[1] if match\n        assert_match(installed_version, '1.0.3')\n      end\n\n      on(agent, \"#{pip_command} uninstall #{package} --disable-pip-version-check --yes\")\n    end\n\n    step \"Ensure latest with pip uses install_options\" do\n      on(agent, \"#{pip_command} install #{package} --disable-pip-version-check\")\n      package_manifest = resource_manifest('package', package, { ensure: 'latest', provider: 'pip', install_options: { '--index' => 'https://pypi.python.org/simple' } } )\n      result = apply_manifest_on(agent, package_manifest, { :catch_failures => true, :debug => true } )\n      assert_match(/--index=https:\\/\\/pypi.python.org\\/simple/, result.stdout)\n      on(agent, \"#{pip_command} uninstall #{package} --disable-pip-version-check --yes\")\n    end\n\n    step \"Uninstall a pip package\" do\n      on(agent, \"#{pip_command} install #{package} --disable-pip-version-check\")\n      package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'pip' } )\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        list = on(agent, \"#{pip_command} list --disable-pip-version-check\").stdout\n        refute_match(/#{package} \\(/, list)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/puppetserver_gem.rb",
    "content": "test_name \"puppetserver_gem provider should install and uninstall\" do\n  tag 'audit:high',\n      'server'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  skip_test 'puppetserver_gem is only suitable on server nodes' unless master\n\n  package = 'world_airports'\n\n  teardown do\n    # Ensure the gem is uninstalled if anything goes wrong\n    # TODO maybe execute this only if something fails, as it takes time\n    on(master, \"puppetserver gem uninstall #{package}\")\n  end\n\n  step \"Installing a gem executes without error\" do\n    package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'puppetserver_gem' } )\n    apply_manifest_on(master, package_manifest, catch_failures: true) do\n      list = on(master, \"puppetserver gem list\").stdout\n      assert_match(/#{package} \\(/, list)\n    end\n\n    # Run again for idempotency\n    apply_manifest_on(master, package_manifest, catch_changes: true)\n  end\n\n  step \"Uninstalling a gem executes without error\" do\n    package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'puppetserver_gem' } )\n    apply_manifest_on(master, package_manifest, catch_failures: true) do\n      list = on(master, \"puppetserver gem list\").stdout\n      refute_match(/#{package} \\(/, list)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/rpm_ensure_install_multiversion_package.rb",
    "content": "test_name \"rpm should install packages with multiple versions\" do\n  confine :to, :platform => /redhat|centos|el|fedora/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = \"kernel-devel-puppet\"\n  repo_fixture_path = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures', 'el-repo')\n\n  repo_content = <<-REPO\n[local]\nname=EL-releasever - test packages\nbaseurl=file:///tmp/el-repo\nenabled=1\ngpgcheck=0\nprotect=1\nREPO\n\n  agents.each do |agent|\n    initially_installed_versions = []\n    scp_to(agent, repo_fixture_path, '/tmp')\n\n    file_manifest = resource_manifest('file', '/etc/yum.repos.d/local.repo', ensure: 'present', content: repo_content)\n    apply_manifest_on(agent, file_manifest)\n\n    teardown do\n      on(agent, 'rm -rf /tmp/el-repo')\n      on(agent, 'rm -f /etc/yum.repos.d/local.repo')\n\n      available_versions = on(agent, \"yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | awk '{print $2}'\").stdout\n      initially_installed_versions.each do |version|\n        if available_versions.include? version\n          package_manifest = resource_manifest('package', package, ensure: version, install_only: true)\n          apply_manifest_on(agent, package_manifest, :catch_failures => true)\n        end\n      end\n    end\n\n    step \"Uninstall package versions for clean setup\" do\n      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\")\n\n      package_manifest = resource_manifest('package', package, ensure: 'absent', install_only: true)\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        remaining_installed_versions = on(agent, \"yum --showduplicates list #{package} | sed -e '1,/Installed Packages/ d' -e '/Available Packages/,$ d' | awk '{print $2}'\").stdout\n        assert(remaining_installed_versions.empty?)\n      end\n\n      available_versions = on(agent, \"yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | awk '{print $2}'\").stdout.split(\"\\n\")\n      if available_versions.size < 2\n        skip_test \"we need at least two package versions to perform the multiversion rpm test\"\n      end\n    end\n\n    step \"Ensure oldest version of multiversion package is installed\" do\n      oldest_version = on(agent, \"yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | head -1 | awk '{print $2}'\").stdout.strip\n      package_manifest = resource_manifest('package', package, ensure: oldest_version, install_only: true)\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        installed_version = on(agent, \"rpm -q #{package}\").stdout\n        assert_match(oldest_version, installed_version)\n      end\n    end\n\n    step \"Ensure newest package multiversion package in installed\" do\n      newest_version = on(agent, \"yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | tail -1 | awk '{print $2}'\").stdout.strip\n      package_manifest = resource_manifest('package', package, ensure: newest_version, install_only: true)\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        installed_version = on(agent, \"rpm -q #{package}\").stdout\n        assert_match(newest_version, installed_version)\n      end\n    end\n\n    step \"Ensure rpm will uninstall multiversion package\" do\n      package_manifest = resource_manifest('package', package, ensure: 'absent', install_only: true)\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        remaining_installed_versions = on(agent, \"yum --showduplicates list #{package} | sed -e '1,/Installed Packages/ d' -e '/Available Packages/,$ d' | awk '{print $2}'\").stdout\n        assert(remaining_installed_versions.empty?)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/yum_semantic_versioning.rb",
    "content": "test_name \"yum provider should use semantic versioning for ensuring desired version\" do\n  confine :to, :platform => /el-7/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = 'yum-utils'\n\n  lower_package_version = '1.1.31-34.el7'\n  middle_package_version = '1.1.31-42.el7'\n  higher_package_version = '1.1.31-45.el7'\n\n  agents.each do |agent|\n    yum_command = 'yum'\n\n    step \"Setup: Skip test if box already has the package installed\" do\n      on(agent, \"rpm -q #{package}\", :acceptable_exit_codes => [1,0]) do |result|\n        skip_test \"package #{package} already installed on this box\" unless result.output =~ /package #{package} is not installed/\n      end\n    end\n\n    step \"Setup: Skip test if package versions are not available\" do\n      on(agent, \"yum list #{package} --showduplicates\", :acceptable_exit_codes => [1,0]) do |result|\n        versions_available = [lower_package_version, middle_package_version, higher_package_version].all? {\n          |needed_versions| result.output.include? needed_versions }\n        skip_test \"package #{package} versions not available on the box\" unless versions_available\n      end\n    end\n\n    step \"Using semantic versioning to downgrade to a desired version <= X\" do\n      on(agent, \"#{yum_command} install #{package} -y\")\n      package_manifest = resource_manifest('package', package, { ensure: \"<=#{lower_package_version}\", provider: 'yum' } )\n      apply_manifest_on(agent, package_manifest, :catch_failures => true) do\n        installed_version = on(agent, \"rpm -q #{package}\").stdout\n        assert_match(/#{lower_package_version}/, installed_version)\n      end\n      # idempotency test\n      package_manifest = resource_manifest('package', package, { ensure: \"<=#{lower_package_version}\", provider: 'yum' } )\n      apply_manifest_on(agent, package_manifest, :catch_changes => true)\n      on(agent, \"#{yum_command} remove #{package} -y\")\n    end\n\n    step \"Using semantic versioning to ensure a version >X <=Y\" do\n      on(agent, \"#{yum_command} install #{package} -y\")\n      package_manifest = resource_manifest('package', package, { ensure: \">#{lower_package_version} <=#{higher_package_version}\", provider: 'yum' } )\n      apply_manifest_on(agent, package_manifest) do\n        installed_version = on(agent, \"rpm -q #{package}\").stdout\n        assert_match(/#{higher_package_version}/, installed_version)\n      end\n      on(agent, \"#{yum_command} remove #{package} -y\")\n    end\n\n    step \"Using semantic versioning to install a version >X <Y\" do\n      package_manifest = resource_manifest('package', package, { ensure: \">#{lower_package_version} <#{higher_package_version}\", provider: 'yum' } )\n      # installing a version >X <Y will install the highet version in between\n      apply_manifest_on(agent, package_manifest) do\n        installed_version = on(agent, \"rpm -q #{package}\").stdout\n        assert_match(/#{middle_package_version}/, installed_version)\n      end\n      on(agent, \"#{yum_command} remove #{package} -y\")\n    end\n\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/provider/package/zypper_install_package_with_range.rb",
    "content": "test_name \"zypper can install range if package is not installed\" do\n  confine :to, :platform => /sles/\n  tag 'audit:high'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  package = \"helloworld\"\n  available_package_versions = ['1.0-2', '1.19-2', '2.0-2']\n  repo_fixture_path = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures', 'sles-repo')\n  repo_content = <<-REPO\n[local]\nname=local - test packages\nbaseurl=file:///tmp/sles-repo\nenabled=1\ngpgcheck=0\nREPO\n\n  agents.each do |agent|\n    scp_to(agent, repo_fixture_path, '/tmp')\n\n    file_manifest = resource_manifest('file', '/etc/zypp/repos.d/local.repo', ensure: 'present', content: repo_content)\n    apply_manifest_on(agent, file_manifest)\n\n    teardown do\n      package_absent(agent, package, '--force-yes')\n      file_manifest = resource_manifest('file', '/etc/zypp/repos.d/local.repo', ensure: 'absent')\n      apply_manifest_on(agent, file_manifest)\n      on(agent, 'rm -rf /tmp/sles-repo')\n    end\n\n    step \"Ensure that package is installed first if not present\" do\n      package_manifest = resource_manifest('package', package, ensure: \"<=#{available_package_versions[1]}\")\n      apply_manifest_on(agent, package_manifest)\n      installed_package_version = on(agent, \"rpm -q #{package}\").stdout\n      assert_match(available_package_versions[1], installed_package_version)\n    end\n\n    step \"Ensure that package is updated\" do\n      package_manifest = resource_manifest('package', package, ensure: \">#{available_package_versions[1]}\")\n      apply_manifest_on(agent, package_manifest)\n      installed_package_version = on(agent, \"rpm -q #{package}\").stdout\n      assert_match(available_package_versions[2], installed_package_version)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/reports/agent_sends_json_report_for_cached_catalog.rb",
    "content": "test_name \"C100533: Agent sends json report for cached catalog\" do\n\n  tag 'risk:high',\n      'audit:high',\n      'audit:integration',\n      'server'\n\n  with_puppet_running_on(master, :main => {}) do\n    expected_format = 'json'\n\n    step \"Perform agent run to ensure that catalog is cached\" do\n      agents.each do |agent|\n        on(agent, puppet('agent', '-t'), :acceptable_exit_codes => [0,2])\n      end\n    end\n\n    step \"Ensure agent sends #{expected_format} report for cached catalog\" do\n      agents.each do |agent|\n        on(agent, puppet('agent', '-t',\n                         '--http_debug'), :acceptable_exit_codes => [0,2]) do |res|\n          # Expected content-type should be in the headers of the\n          # HTTP report payload being PUT to the server by the agent.\n          unless res.stderr =~ /<- \"PUT \\/puppet\\/v[3-9]\\/report.*Content-Type: .*\\/#{expected_format}/\n            fail_test(\"Report was not submitted in #{expected_format} format\")\n          end\n        end\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/reports/cached_catalog_status_in_report.rb",
    "content": "test_name \"PUP-5867: The report specifies whether a cached catalog was used, and if so, why\" do\n  tag 'audit:high',\n      'audit:integration',\n      'server'\n\n  master_reportdir = create_tmpdir_for_user(master, 'report_dir')\n\n  teardown do\n    on(master, \"rm -rf #{master_reportdir}\")\n  end\n\n  def remove_reports_on_master(master_reportdir, agent_node_name)\n    on(master, \"rm -rf #{master_reportdir}/#{agent_node_name}/*\")\n  end\n\n  with_puppet_running_on(master, :master => { :reportdir => master_reportdir, :reports => 'store' }) do\n    agents.each do |agent|\n      step \"cached_catalog_status should be 'not used' when a new catalog is retrieved\" do\n        step \"Initial run: cache a newly retrieved catalog\" do\n          on(agent, puppet(\"agent\", \"-t\"), :acceptable_exit_codes => [0,2])\n        end\n\n        step \"Run again and ensure report indicates that the cached catalog was not used\" do\n          on(agent, puppet(\"agent\", \"--onetime\", \"--no-daemonize\"), :acceptable_exit_codes => [0, 2])\n          on(master, \"cat #{master_reportdir}/#{agent.node_name}/*\") do |result|\n            assert_match(/cached_catalog_status: not_used/, result.stdout, \"expected to find 'cached_catalog_status: not_used' in the report\")\n          end\n          remove_reports_on_master(master_reportdir, agent.node_name)\n        end\n      end\n\n      step \"Run with --use_cached_catalog and ensure report indicates cached catalog was explicitly requested\" do\n        on(agent, puppet(\"agent\", \"--onetime\", \"--no-daemonize\", \"--use_cached_catalog\"), :acceptable_exit_codes => [0, 2])\n        on(master, \"cat #{master_reportdir}/#{agent.node_name}/*\") do |result|\n          assert_match(/cached_catalog_status: explicitly_requested/, result.stdout, \"expected to find 'cached_catalog_status: explicitly_requested' in the report\")\n        end\n        remove_reports_on_master(master_reportdir, agent.node_name)\n      end\n\n      step \"On a run which fails to retrieve a new catalog, ensure report indicates cached catalog was used on failure\" do\n        on(agent, puppet(\"agent\", \"--onetime\", \"--no-daemonize\", \"--report_server #{master}\", \"--server nonexist\"), :acceptable_exit_codes => [0, 2])\n        on(master, \"cat #{master_reportdir}/#{agent.node_name}/*\") do |result|\n          assert_match(/cached_catalog_status: on_failure/, result.stdout, \"expected to find 'cached_catalog_status: on_failure' in the report\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/reports/corrective_change_new_resource.rb",
    "content": "test_name \"C98092 - a new resource should not be reported as a corrective change\" do\n\n  require 'yaml'\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  tag 'audit:high',\n      'audit:integration',\n      'audit:refactor',    # Uses a server currently but is testing agent report\n      'broken:images'\n\n  test_file_name  = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, test_file_name)\n  tmp_file        = {}\n\n  agents.each do |agent|\n    tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment)\n  end\n\n  teardown do\n    step 'clean out produced resources' do\n      agents.each do |agent|\n        if tmp_file.has_key?(agent_to_fqdn(agent)) && tmp_file[agent_to_fqdn(agent)] != ''\n          on(agent, \"rm '#{tmp_file[agent_to_fqdn(agent)]}'\", :accept_all_exit_codes => true)\n        end\n\n\n        on(agent, puppet('config print lastrunfile')) do |command_result|\n          agent.rm_rf(command_result.stdout)\n        end\n      end\n    end\n  end\n\n  step 'create file resource - site.pp to verify corrective change flag' do\n    file_contents = 'this is a test'\n    manifest      = <<-MANIFEST\n    file { '#{environmentpath}/#{tmp_environment}/manifests/site.pp':\n      ensure => file,\n      content => '\n    \\$test_path = \\$facts[\"networking\"][\"fqdn\"] ? #{tmp_file}\n    file { \\$test_path:\n      content => @(UTF8)\n        #{file_contents}\n        | UTF8\n    }\n      ',\n    }\n    MANIFEST\n    apply_manifest_on(master, manifest, :catch_failures => true)\n  end\n\n  step 'run agent(s)' do\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n        #Run agent once to create new File resource\n        step 'Run agent once to create new File resource' do\n          on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n        end\n\n        #Verify the file resource is created\n        step 'Verify the file resource is created' do\n          on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |file_result|\n            assert_equal(file_contents, file_result, 'file contents did not match accepted')\n          end\n        end\n      end\n    end\n  end\n\n  # Open last_run_report.yaml\n  step 'Check report' do\n    agents.each do |agent|\n      on(agent, puppet('config print statedir')) do |command_result|\n        report_path = command_result.stdout.chomp + '/last_run_report.yaml'\n        on(agent, \"cat '#{report_path}'\").stdout do |report_contents|\n\n          yaml_data = YAML::parse(report_contents)\n          # Remove any Ruby class tags from the yaml\n          yaml_data.root.each do |o|\n            if o.respond_to?(:tag=) and o.tag != nil and o.tag.start_with?(\"!ruby\")\n              o.tag = nil\n            end\n          end\n          report_yaml = yaml_data.to_ruby\n\n          file_resource_details = report_yaml[\"resource_statuses\"][\"File[#{tmp_file[agent_to_fqdn(agent)]}]\"]\n          assert(file_resource_details.has_key?(\"corrective_change\"), 'corrective_change key is missing')\n          corrective_change_value = file_resource_details[\"corrective_change\"]\n          assert_equal(false, corrective_change_value, 'corrective_change flag should be false')\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/reports/corrective_change_outside_puppet.rb",
    "content": "test_name \"C98093 - a resource changed outside of Puppet will be reported as a corrective change\" do\n\n  require 'yaml'\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  tag 'audit:high',\n      'audit:integration',\n      'audit:refactor',    # Uses a server currently, but is testing agent report\n      'broken:images'\n\n  test_file_name  = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, test_file_name)\n  tmp_file        = {}\n\n\n  agents.each do |agent|\n    tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment)\n  end\n\n  teardown do\n    step 'clean out produced resources' do\n      agents.each do |agent|\n        if tmp_file.has_key?(agent_to_fqdn(agent)) && tmp_file[agent_to_fqdn(agent)] != ''\n          on(agent, \"rm '#{tmp_file[agent_to_fqdn(agent)]}'\", :accept_all_exit_codes => true)\n        end\n\n        on(agent, puppet('config print lastrunfile')) do |command_result|\n          agent.rm_rf(command_result.stdout)\n        end\n      end\n    end\n  end\n\n  step 'create file resource - site.pp to verify corrective change flag' do\n    file_contents = 'this is a test'\n    manifest      = <<-MANIFEST\n      file { '#{environmentpath}/#{tmp_environment}/manifests/site.pp':\n        ensure => file,\n        content => '\n      \\$test_path = \\$facts[\"networking\"][\"fqdn\"] ? #{tmp_file}\n      file { \\$test_path:\n        content => @(UTF8)\n          #{file_contents}\n          | UTF8\n      }\n        ',\n      }\n    MANIFEST\n    apply_manifest_on(master, manifest, :catch_failures => true)\n  end\n\n  step 'run agent(s)' do\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n        #Run agent once to create new File resource\n        step 'Run agent once to create new File resource' do\n          on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n        end\n\n        #Verify the file resource is created\n        step 'Verify the file resource is created' do\n          on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |file_result|\n            assert_equal(file_contents, file_result, 'file contents did not match accepted')\n          end\n        end\n\n        #Delete the file\n        step 'Delete the file' do\n          on(agent, \"rm '#{tmp_file[agent_to_fqdn(agent)]}'\", :accept_all_exit_codes => true)\n        end\n\n        #Run agent to correct the file's absence\n        step 'Run agent to correct the files absence' do\n          on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n        end\n\n        #Verify the file resource is created\n        step 'Verify the file resource is created' do\n          on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |file_result|\n            assert_equal(file_contents, file_result, 'file contents did not match accepted')\n          end\n        end\n      end\n    end\n  end\n\n  # Open last_run_report.yaml\n  step 'Check report' do\n    agents.each do |agent|\n      on(agent, puppet('config print statedir')) do |command_result|\n        report_path = command_result.stdout.chomp + '/last_run_report.yaml'\n        on(agent, \"cat '#{report_path}'\").stdout do |report_contents|\n\n          yaml_data = YAML::parse(report_contents)\n          # Remove any Ruby class tags from the yaml\n          yaml_data.root.each do |o|\n            if o.respond_to?(:tag=) and o.tag != nil and o.tag.start_with?(\"!ruby\")\n              o.tag = nil\n            end\n          end\n          report_yaml = yaml_data.to_ruby\n\n          file_resource_details = report_yaml[\"resource_statuses\"][\"File[#{tmp_file[agent_to_fqdn(agent)]}]\"]\n          assert(file_resource_details.has_key?(\"corrective_change\"), 'corrective_change key is missing')\n          corrective_change_value = file_resource_details[\"corrective_change\"]\n          assert_equal(true, corrective_change_value, 'corrective_change flag should be true')\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/reports/corrective_change_via_puppet.rb",
    "content": "test_name \"C98094 - a resource changed via Puppet manifest will not be reported as a corrective change\" do\n\n  require 'yaml'\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  tag 'audit:high',\n      'audit:integration',\n      'audit:refactor',    # Uses a server currently, but is testing agent report\n      'broken:images',\n      'server'\n\n  test_file_name = File.basename(__FILE__, '.*')\n  tmp_environment = mk_tmp_environment_with_teardown(master, test_file_name)\n  tmp_file = {}\n\n  original_test_data = 'this is my original important data'\n  modified_test_data = 'this is my modified important data'\n\n  agents.each do |agent|\n    tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment)\n  end\n\n  teardown do\n    # Remove all traces of the last used environment\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n\n    step 'clean out produced resources' do\n      agents.each do |agent|\n        if tmp_file.has_key?(agent_to_fqdn(agent)) && tmp_file[agent_to_fqdn(agent)] != ''\n          on(agent, \"rm '#{tmp_file[agent_to_fqdn(agent)]}'\", :accept_all_exit_codes => true)\n        end\n      end\n    end\n  end\n\n  def create_manifest_for_file_resource(file_resource, file_contents, environment_name)\n    manifest = <<-MANIFEST\n      file { '#{environmentpath}/#{environment_name}/manifests/site.pp':\n        ensure => file,\n        content => '\n      \\$test_path = \\$facts[\"networking\"][\"fqdn\"] ? #{file_resource}\n      file { \\$test_path:\n        content => @(UTF8)\n          #{file_contents}\n          | UTF8\n      }\n        ',\n      }\n    MANIFEST\n    apply_manifest_on(master, manifest, :catch_failures => true)\n  end\n\n  step 'create file resource in site.pp' do\n    create_manifest_for_file_resource(tmp_file, original_test_data, tmp_environment)\n  end\n\n  step 'run agent(s) to create the new resource' do\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n        step 'Run agent once to create new File resource' do\n          on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n        end\n\n        step 'Verify the file resource is created' do\n          on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |file_contents|\n            assert_equal(original_test_data, file_contents, 'file contents did not match expected contents')\n          end\n        end\n      end\n\n      step 'Change the manifest for the resource' do\n        create_manifest_for_file_resource(tmp_file, modified_test_data, tmp_environment)\n      end\n\n      agents.each do |agent|\n        step 'Run agent a 2nd time to change the File resource' do\n          on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n        end\n\n        step 'Verify the file resource is created' do\n          on(agent, \"cat '#{tmp_file[agent_to_fqdn(agent)]}'\").stdout do |file_contents|\n            assert_equal(modified_test_data, file_contents, 'file contents did not match expected contents')\n          end\n        end\n      end\n    end\n  end\n\n  # Open last_run_report.yaml\n  step 'Check report' do\n    agents.each do |agent|\n      on(agent, puppet('config print statedir')) do |command_result|\n        report_path = command_result.stdout.chomp + '/last_run_report.yaml'\n        on(agent, \"cat '#{report_path}'\").stdout do |report_contents|\n\n          yaml_data = YAML::parse(report_contents)\n          # Remove any Ruby class tags from the yaml\n          yaml_data.root.each do |o|\n            if o.respond_to?(:tag=) and o.tag != nil and o.tag.start_with?(\"!ruby\")\n              o.tag = nil\n            end\n          end\n          report_yaml           = yaml_data.to_ruby\n          file_resource_details = report_yaml[\"resource_statuses\"][\"File[#{tmp_file[agent_to_fqdn(agent)]}]\"]\n          assert(file_resource_details.has_key?(\"corrective_change\"), 'corrective_change key is missing')\n          corrective_change_value = file_resource_details[\"corrective_change\"]\n          assert_equal(false, corrective_change_value, 'corrective_change flag for the changed resource should be false')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/reports/submission.rb",
    "content": "test_name \"Report submission\"\n\ntag 'audit:high',\n    'audit:integration'\n\nif master.is_pe?\n  require \"time\"\n\n  def puppetdb\n    hosts.detect { |h| h['roles'].include?('database') }\n  end\n\n  def sleep_until_queue_empty(timeout=60)\n    metric = \"org.apache.activemq:BrokerName=localhost,Type=Queue,Destination=com.puppetlabs.puppetdb.commands\"\n    queue_size = nil\n\n    begin\n      Timeout.timeout(timeout) do\n        until queue_size == 0\n          result = on(puppetdb, %Q{curl http://localhost:8080/v3/metrics/mbean/#{CGI.escape(metric)}})\n          if md = /\"?QueueSize\"?\\s*:\\s*(\\d+)/.match(result.stdout.chomp)\n            queue_size = Integer(md[1])\n          end\n          sleep 1\n        end\n      end\n    rescue Timeout::Error\n      raise \"Queue took longer than allowed #{timeout} seconds to empty\"\n    end\n  end\n\n  def query_last_report_time_on(agent)\n    time_query_script = <<-EOS\n      require \"net/http\"\n      require \"json\"\n\n      puppetdb_url = URI(\"http://localhost:8080/v3/reports\")\n      puppetdb_url.query = CGI.escape(%Q{query=[\"=\",\"certname\",\"#{agent}\"]})\n      result = Net::HTTP.get(puppetdb_url)\n      json = JSON.load(result)\n      puts json.first[\"receive-time\"]\n    EOS\n    on(puppetdb, \"#{master[:privatebindir]}/ruby -e '#{time_query_script}'\").output.chomp\n  end\n\n  last_times = {}\n\n  agents.each do |agent|\n    last_times[agent] = query_last_report_time_on(agent)\n  end\n\n  with_puppet_running_on(master, {}) do\n    agents.each do |agent|\n      on(agent, puppet('agent', \"-t\"))\n\n      sleep_until_queue_empty\n\n      current_time = Time.parse(query_last_report_time_on(agent))\n      last_time = Time.parse(last_times[agent])\n\n      assert(current_time > last_time, \"Most recent report time #{current_time} is not newer than last report time #{last_time}\")\n    end\n  end\n\nelse\n\n  testdir = create_tmpdir_for_user master, 'report_submission'\n\n  teardown do\n    on master, \"rm -rf #{testdir}\"\n  end\n\n  with_puppet_running_on(master, :main => { :reportdir => testdir, :reports => 'store' }) do\n    agents.each do |agent|\n      on(agent, puppet('agent', \"-t\"))\n\n      on master, \"grep -q #{agent.node_name} #{testdir}/*/*\"\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/accept_array_commands.rb",
    "content": "test_name \"Be able to execute array commands\" do\n  tag 'audit:high',\n      'audit:acceptance'\n\n  agents.each do |agent|\n    if agent.platform =~ /windows/\n      cmd = ['C:\\Windows\\System32\\cmd.exe', '/c', 'echo', '*']\n    else\n      cmd = ['/bin/echo', '*']\n    end\n\n    exec_manifest = <<~MANIFEST\n      exec { \"test exec\":\n        command => #{cmd},\n        logoutput => true,\n      }\n    MANIFEST\n\n    apply_manifest_on(agent, exec_manifest) do |output|\n      assert_match('Notice: /Stage[main]/Main/Exec[test exec]/returns: *', output.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/accept_multi-line_commands.rb",
    "content": "test_name \"Be able to execute multi-line commands (#9996)\"\nconfine :except, :platform => 'windows'\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  temp_file_name = agent.tmpfile('9996-multi-line-commands')\n\n  test_manifest = <<HERE\nexec { \"test exec\":\n      command =>  \"/bin/echo '#Test' > #{temp_file_name};\n                   /bin/echo 'bob' >> #{temp_file_name};\"\n}\nHERE\n\n  expected_results = <<HERE\n#Test\nbob\nHERE\n\n  on(agent, \"rm -f #{temp_file_name}\")\n\n  apply_manifest_on agent, test_manifest\n\n  on(agent, \"cat #{temp_file_name}\") do |result|\n    assert_equal(expected_results, result.stdout, \"Unexpected result for host '#{agent}'\")\n  end\n\n  on(agent, \"rm -f #{temp_file_name}\")\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_accept_large_output.rb",
    "content": "test_name \"tests that puppet correctly captures large and empty output.\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  testfile = agent.tmpfile('should_accept_large_output')\n\n  # Generate >64KB file to exceed pipe buffer.\n  lorem_ipsum = <<EOF\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna\naliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint\noccaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nEOF\n  create_remote_file(agent, testfile, lorem_ipsum*1024)\n\n  apply_manifest_on(agent, \"exec {'cat #{testfile}': path => ['/bin', '/usr/bin', 'C:/cygwin32/bin', 'C:/cygwin64/bin', 'C:/cygwin/bin'], logoutput => true}\") do |result|\n    fail_test \"didn't seem to run the command\" unless\n      result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja'\n    fail_test \"didn't print output correctly\" unless\n      result.stdout.lines.select {|line| line =~ /\\/returns:/}.count == 4097\n  end\n\n  apply_manifest_on(agent, \"exec {'echo': path => ['/bin', '/usr/bin', 'C:/cygwin32/bin', 'C:/cygwin64/bin', 'C:/cygwin/bin'], logoutput => true}\") do |result|\n    fail_test \"didn't seem to run the command\" unless\n      result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja'\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_not_run_command_creates.rb",
    "content": "test_name \"should not run command creates\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  touch      = agent.tmpfile('touched')\n  donottouch = agent.tmpfile('not-touched')\n\nmanifest = %Q{\n  exec { \"test#{Time.new.to_i}\": command => '#{agent.touch(donottouch)}', creates => \"#{touch}\"}\n}\n\n  step \"prepare the agents for the test\"\n  on agent, \"touch #{touch} && rm -f #{donottouch}\"\n\n  step \"test using puppet apply\"\n  apply_manifest_on(agent, manifest) do |result|\n    fail_test \"looks like the thing executed, which it shouldn't\" if\n      result.stdout.include? 'executed successfully'\n  end\n\n  step \"verify the file didn't get created\"\n  on agent, \"test -f #{donottouch}\", :acceptable_exit_codes => [1]\n\n  step \"prepare the agents for the second part of the test\"\n  on agent, \"touch #{touch} ; rm -f #{donottouch}\"\n\n  step \"test using puppet resource\"\n  on(agent, puppet_resource('exec', \"test#{Time.new.to_i}\",\n                   \"command='#{agent.touch(donottouch)}'\",\n                   \"creates='#{touch}'\")) do |result|\n    fail_test \"looks like the thing executed, which it shouldn't\" if\n      result.stdout.include? 'executed successfully'\n  end\n\n  step \"verify the file didn't get created the second time\"\n  on agent, \"test -f #{donottouch}\", :acceptable_exit_codes => [1]\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_run_bad_command.rb",
    "content": "test_name \"tests that puppet can run badly written scripts that fork and inherit descriptors\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\ndef sleepy_daemon_script(agent)\n  if agent['platform'] =~ /win/\n    # Windows uses a shorter sleep, because it's expected to wait until the end.\n    return <<INITSCRIPT\necho hello\nstart /b ping.exe 127.0.0.1 -n 1\nINITSCRIPT\n  else\n    return <<INITSCRIPT\necho hello\n/bin/sleep 60 &\nINITSCRIPT\n  end\nend\n\n# TODO: taken from pxp-agent, find common home\ndef stop_sleep_process(targets, accept_no_pid_found = false)\n  targets = [targets].flatten\n\n  targets.each do |target|\n    case target['platform']\n    when /osx/\n      command = \"ps -e -o pid,comm | grep sleep | sed 's/^[^0-9]*//g' | cut -d\\\\  -f1\"\n    when /win/\n      command = \"cmd.exe /C WMIC path win32_process WHERE Name=\\\\\\\"PING.EXE\\\\\\\" get ProcessId | egrep -o '[0-9]+\\\\s*$'\"\n    else\n      command = \"ps -ef | grep 'bin/sleep ' | grep -v 'grep' | grep -v 'true' | sed 's/^[^0-9]*//g' | cut -d\\\\  -f1\"\n    end\n\n    # A failed test may leave an orphaned sleep process, handle multiple matches.\n    pids = nil\n    on(target, command, accept_all_exit_codes: accept_no_pid_found) do |output|\n      pids = output.stdout.chomp.split\n      if pids.empty? && !accept_no_pid_found\n        raise(\"Did not find a pid for a sleep process on #{target}\")\n      end\n    end\n\n    pids.each do |pid|\n      target['platform'] =~ /win/ ?\n        on(target, \"taskkill /F /pid #{pid}\") :\n        on(target, \"kill -s TERM #{pid}\")\n    end\n  end\nend\n\nteardown do\n  # On Windows, Puppet waits until the sleep process exits before exiting\n  stop_sleep_process(agents.select {|agent| agent['platform'] =~ /win/}, true)\n  # Requiring a sleep process asserts that Puppet exited before the sleep process.\n  stop_sleep_process(agents.reject {|agent| agent['platform'] =~ /win/})\nend\n\nagents.each do |agent|\n  ext = if agent['platform'] =~ /win/ then '.bat' else '' end\n  daemon = agent.tmpfile('sleepy_daemon') + ext\n  create_remote_file(agent, daemon, sleepy_daemon_script(agent))\n  on(agent, \"chmod +x #{daemon}\")\n\n  apply_manifest_on(agent, \"exec {'#{daemon}': logoutput => true}\") do |result|\n    fail_test \"didn't seem to run the command\" unless\n      result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja'\n  end\nend\n\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_run_command.rb",
    "content": "test_name \"tests that puppet correctly runs an exec.\"\n# original author: Dan Bode  --daniel 2010-12-23\n\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\ndef before(agent)\n  step \"file to be touched should not exist.\"\n  agent.tmpfile('test-exec')\nend\n\ndef after(agent, touched)\n  step \"checking the output worked\"\n  on agent, \"test -f #{touched}\"\n\n  step \"clean up the system\"\n  on agent, \"rm -f #{touched}\"\nend\n\nagents.each do |agent|\n  touched = before(agent)\n  apply_manifest_on(agent, \"exec {'test': command=>'#{agent.touch(touched)}'}\") do |result|\n    fail_test \"didn't seem to run the command\" unless\n      result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja'\n  end\n  after(agent, touched)\n\n  touched = before(agent)\n  on(agent, puppet_resource('-d', 'exec', 'test', \"command='#{agent.touch(touched)}'}\")) do |result|\n    fail_test \"didn't seem to run the command\" unless\n      result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja'\n  end\n  after(agent, touched)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_run_command_as_user.rb",
    "content": "test_name \"The exec resource should be able to run commands as a different user\" do\n  confine :except, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::BeakerUtils\n\n  def random_username\n    \"pl#{rand(999999).to_i}\"\n  end\n\n  def exec_resource_manifest(params = {})\n    default_params = {\n      :logoutput => true,\n      :path      => '/usr/bin:/usr/sbin:/bin:/sbin',\n      :command   => 'echo Hello'\n    }\n    params = default_params.merge(params)\n\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"'#{value_str}'\" if value.is_a?(String)\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\nexec { 'run_test_command':\n  #{params_str}\n}\nMANIFEST\n  end\n\n  agents.each do |agent|\n    username = random_username\n\n    # Create our user. Ensure that we start with a clean slate.\n    agent.user_absent(username)\n    agent.user_present(username)\n    teardown { agent.user_absent(username) }\n\n    tmpdir = agent.tmpdir(\"forbidden\")\n    on(agent, \"chmod 700 #{tmpdir}\")\n\n    step \"Runs the command even when the user doesn't have permissions to access the pwd\" do\n      # Can't use apply_manifest_on here because that does not take the :cwd\n      # as an option.\n      tmpfile = agent.tmpfile(\"exec_user_perms_manifest\")\n      create_remote_file(agent, tmpfile, exec_resource_manifest(user: username))\n      on(agent, \"cd #{tmpdir} && puppet apply #{tmpfile} --detailed-exitcodes\", acceptable_exit_codes: [0, 2])\n    end\n\n    step \"Runs the command even when the user doesn't have permission to access the specified cwd\" do\n      apply_manifest_on(agent, exec_resource_manifest(user: username, cwd: tmpdir), catch_failures: true)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_run_command_in_cwd.rb",
    "content": "test_name \"The Exec resource should run commands in the specified cwd\" do\n  tag 'audit:high',\n      'audit:acceptance'\n  confine :except, :platform => /debian-12-amd64/ # PUP-12020\n\n  require 'puppet/acceptance/windows_utils'\n  extend Puppet::Acceptance::WindowsUtils\n\n  # Useful utility that converts a string literal\n  # to a regex. We do a lot of assertions on file\n  # paths here that we need to escape, so this is\n  # a nice way of making the code more readable.\n  def to_regex(str)\n    Regexp.new(Regexp.escape(str))\n  end\n\n  def exec_resource_manifest(command, params = {})\n    default_params = {\n      :command   => command\n    }\n    params = default_params.merge(params)\n\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      # Single quote the strings in case our value is a Windows\n      # path\n      value_str = \"'#{value_str}'\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\n  exec { 'run_test_command':\n  #{params_str}\n  }\n  MANIFEST\n  end\n\n  def assert_file_on(host, filepath, failure_comment)\n    if host.platform =~ /windows/\n      cmd = \"cmd.exe /c \\\"type #{filepath.gsub('/', '\\\\')}\\\"\"\n    else\n      cmd = \"test -f #{filepath}\"\n    end\n    on(host, cmd, :acceptable_exit_codes => [0, 1]) do |result|\n      assert_equal(0, result.exit_code, failure_comment)\n    end\n  end\n\n  agents.each do |agent|\n    testdir = agent.tmpdir(\"mock_testdir\")\n    if agent.platform =~ /windows/\n      path = 'C:\\Windows\\System32'\n      echo_to = 'cmd.exe /c echo testing >'\n      cat = 'cmd.exe /c type'\n      non_existant_dir = 'C:\\does_not_exist'\n      origin_working_dir = on(agent, 'cmd.exe /c echo %CD%').stdout.chomp\n    else\n      path = '/usr/bin:/usr/sbin:/bin:/sbin'\n      echo_to = 'echo testing >'\n      cat = 'cat'\n      non_existant_dir = '/does_not_exist'\n      origin_working_dir = on(agent, 'pwd').stdout.chomp\n    end\n\n    step \"clean current working directory\" do\n      on(agent, \"rm -f cwd_test*\")\n    end\n\n    step \"Defaults to the current directory if the CWD option is not provided\" do\n      apply_manifest_on(agent, exec_resource_manifest(\"#{echo_to} cwd_test1\", {:path => path}), :catch_failures => true)\n      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')\n    end\n\n    step \"Runs the command in the user specified CWD\" do\n      apply_manifest_on(agent, exec_resource_manifest(\"#{echo_to} cwd_test2\", {:cwd => testdir, :path => path}), :catch_failures => true)\n      assert_file_on(agent, File.join(testdir, 'cwd_test2'), 'Exec did not create file in test directory, exec resource not using :cwd given')\n    end\n\n    step \"Errors if the user specified CWD does not exist\" do\n      apply_manifest_on(agent, exec_resource_manifest(\"#{echo_to} cwd_test3\", {cwd: non_existant_dir, :path => path}), :expect_failures => true) do |result|\n        assert_equal(4, result.exit_code, \"Exec manifest still executed with non-existant :cwd\")\n      end\n    end\n\n    # \"onlyif\" testing will require some form of runnable test in the testdir for the\n    # onlyif clause to actually execute. The runnable test we will use is attempting to\n    # 'cat' an unqualified file that will only exist in the testdir\n    create_remote_file(agent, File.join(testdir, 'testdir_onlyif.txt'), 'testing')\n\n    step 'Runs a \"check\" command (:onlyif or :unless) in the user specified CWD' do\n      apply_manifest_on(agent, exec_resource_manifest(\"#{echo_to} cwd_test4\", {cwd: testdir, :path => path, :onlyif => \"#{cat} testdir_onlyif.txt\"}), :expect_changes => true)\n      assert_file_on(agent, File.join(testdir, 'cwd_test4'), 'Exec did not create file in test directory, exec resource not using :cwd given')\n    end\n\n    step 'Does not run the exec if the \"check\" command (:onlyif or :unless) fails' do\n      apply_manifest_on(agent, exec_resource_manifest(\"#{echo_to} cwd_test5\", {cwd: testdir, :path => path, :onlyif => \"foobar\"}), :expect_failures => true) do |result|\n        assert_equal(4, result.exit_code, \"Exec manifest still executed with failed :onlyif clause\")\n      end\n    end\n\n    tmpdir_noaccess = agent.tmpdir(\"mock_dir\")\n    create_remote_file(agent, File.join(tmpdir_noaccess, 'noaccess.txt'), 'foobar')\n    username = \"pl#{rand(999999).to_i}\"\n\n    # The next two steps set up to test running with a CWD that the user does not have access to.\n    # The setup for the test creates 1. a new user and 2. a new directory that the new user does\n    # not have access to.\n    step \"Setup user for 'no access' test\" do\n      agent.user_present(username)\n      if agent.platform =~ /solaris/\n        # for some reason applications of 'user_present' on solaris 10 don't manage the homedir correctly, so just\n        # force a puppet apply to manage the user\n        on agent, puppet_resource('user', username, \"ensure=present managehome=true home=/export/home/#{username}\")\n        # we need to create the user directory ourselves in order for solaris users to successfully login\n        on(agent, \"mkdir /export/home/#{username} && chown -R #{username} /export/home/#{username}\")\n      elsif agent.platform =~ /osx/\n        # we need to create the user directory ourselves in order for macos users to successfully login\n        on(agent, \"mkdir /Users/#{username} && chown -R #{username}:80 /Users/#{username}\")\n      elsif agent.platform =~ /debian|ubuntu|sles/\n        # we need to create the user directory ourselves in order for deb users to successfully login\n        on(agent, \"mkdir /home/#{username} && chown -R #{username} /home/#{username}\")\n      end\n      teardown { agent.user_absent(username) }\n    end\n\n    tmpdir_noaccess = agent.tmpdir(\"mock_noaccess\")\n    create_remote_file(agent, File.join(tmpdir_noaccess, 'noaccess.txt'), 'foobar')\n\n    step \"Setup restricted access directory for 'no access' test\" do\n      if agent.platform =~ /windows/\n        deny_administrator_access_to(agent, tmpdir_noaccess)\n        deny_administrator_access_to(agent, File.join(tmpdir_noaccess, 'noaccess.txt'))\n      else\n        if agent.platform =~ /osx/\n          # This is a little nuts, but on MacOS the tmpdir returned from agent.tmpdir is located in\n          # a directory that users other than root can't even access, i.e. other users won't have access\n          # to either the noaccess dir itself (which we want) _or the tmpdir root it's located in_. This is\n          # a problem since it will look to puppet like the noacceess dir doesn't exist at all, and so we\n          # can't count on any reliaable failure since we want a return indicating no access, not a missing directory.\n          #\n          # To get around this for MacOS platforms we simply use the new user's homedir as the 'tmpdir' and\n          # put the noaccess dir there.\n          on(agent, \"mkdir /Users/#{username}/noaccess_test && cp #{tmpdir_noaccess}/noaccess.txt /Users/#{username}/noaccess_test && chmod -R 600 /Users/#{username}/noaccess_test\")\n          tmpdir_noaccess = \"/Users/#{username}/noaccess_test\"\n        end\n        # remove permissions for all other users other than root, which should force puppet to fail when running as another user\n        on(agent, \"chmod -R 600 #{tmpdir_noaccess}\")\n      end\n    end\n\n    step \"Errors if the user does not have access to the specified CWD\" do\n      manifest_path = agent.tmpfile('apply_manifest.pp')\n      create_remote_file(agent, manifest_path, exec_resource_manifest(\"#{cat} noaccess.txt\", {:cwd => tmpdir_noaccess, :path => path}))\n      if agent.platform =~ /windows/\n        on(agent, \"cmd.exe /c \\\"puppet apply #{manifest_path} --detailed-exitcodes\\\"\", :acceptable_exit_codes => [4]) do |result|\n          assert_equal(4, result.exit_code, \"Exec manifest still executed inside restricted directory\", )\n        end\n      elsif agent.platform =~ /osx/\n        # 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.\n        on(agent, \"cp #{manifest_path} /Users/#{username}/noaccess_manifest.pp && chown #{username}:80 /Users/#{username}/noaccess_manifest.pp\")\n        on(agent, \"su - #{username} -c \\\"/opt/puppetlabs/bin/puppet apply /Users/#{username}/noaccess_manifest.pp --detailed-exitcodes\\\"\", :acceptable_exit_codes => [4]) do |result|\n          assert_equal(4, result.exit_code, \"Exec manifest still executed inside restricted directory\")\n        end\n      else\n        on(agent, \"chown #{username} #{manifest_path}\")\n        if agent.platform =~ /solaris|aix/\n          on(agent, \"su - #{username} -c \\\"/opt/puppetlabs/bin/puppet apply #{manifest_path} --detailed-exitcodes\\\"\", :acceptable_exit_codes => [4]) do |result|\n            assert_equal(4, result.exit_code, \"Exec manifest still executed inside restricted directory\")\n          end\n        else\n          on(agent, \"su #{username} -c \\\"/opt/puppetlabs/bin/puppet apply #{manifest_path} --detailed-exitcodes\\\"\", :acceptable_exit_codes => [4]) do |result|\n            assert_equal(4, result.exit_code, \"Exec manifest still executed inside restricted directory\")\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_set_environment_variables.rb",
    "content": "test_name \"The Exec resource should set user-specified environment variables\" do\n  tag 'audit:high',\n      'audit:acceptance'\n\n  # Would be nice to parse the actual values from puppet_output,\n  # but that would require some complicated matching since\n  # puppet_output contains other stuff.\n  def assert_env_var_values(puppet_output, expected_values)\n    expected_values.each do |env_var, value|\n      assert_match(/#{env_var}=#{value}/, puppet_output, \"Expected '#{env_var}=#{value}' to be printed as part of the output!\")\n    end\n  end\n\n  agents.each do |agent|\n    # Calculate some top-level variables/functions we\n    # will need for our tests.\n    unless agent.platform =~ /windows/\n      path = '/usr/bin:/usr/sbin:/bin:/sbin'\n      print_env_vars = lambda do |*env_vars|\n        env_vars_str = env_vars.map do |env_var|\n          \"#{env_var}=$#{env_var}\"\n        end.join(\" \")\n\n        \"echo #{env_vars_str}\"\n      end\n    else\n      # Powershell's directory is dependent on what version of Powershell is\n      # installed on the system (e.g. v1.0, v2.0), so we need to programmatically\n      # calculate the executable's directory to add to our PATH variable.\n      powershell_path = on(agent, \"cmd.exe /c where powershell.exe\").stdout.chomp\n      *powershell_dir, _ = powershell_path.split('\\\\')\n      powershell_dir = powershell_dir.join('\\\\')\n\n      path = \"C:\\Windows\\System32;#{powershell_dir}\"\n      print_env_vars = lambda do |*env_vars|\n        env_vars_str = env_vars.map do |env_var|\n          \"#{env_var}=$env:#{env_var}\"\n        end\n\n        \"powershell.exe \\\"Write-Host -NoNewLine #{env_vars_str}\\\"\"\n      end\n    end\n\n    # Easier to read than a def. The def. would require us\n    # to specify the host as a param. in order to get the path\n    # and print_cwd command, which is unnecessary clutter.\n    exec_resource_manifest = lambda do |params = {}|\n      default_params = {\n        :logoutput => true,\n        :path      => path\n      }\n      params = default_params.merge(params)\n\n      params_str = params.map do |param, value|\n        value_str = value.to_s\n        # Single quote the strings in case our value is a Windows\n        # path\n        value_str = \"'#{value_str}'\" if value.is_a?(String)\n\n        \"  #{param} => #{value_str}\"\n      end.join(\",\\n\")\n\n      <<-MANIFEST\n  exec { 'run_test_command':\n    #{params_str}\n  }\nMANIFEST\n    end\n\n    step 'Passes the user-specified environment variables into the command' do\n      manifest = exec_resource_manifest.call(\n        command: print_env_vars.call('ENV_VAR_ONE', 'ENV_VAR_TWO'),\n        environment: ['ENV_VAR_ONE=VALUE_ONE', 'ENV_VAR_TWO=VALUE_TWO']\n      )\n\n      apply_manifest_on(agent, manifest) do |result|\n        assert_env_var_values(result.stdout, ENV_VAR_ONE: 'VALUE_ONE', ENV_VAR_TWO: 'VALUE_TWO')\n      end\n    end\n\n    step \"Temporarily overrides previously set environment variables\" do\n      manifest = exec_resource_manifest.call(\n        command: print_env_vars.call('ENV_VAR_ONE'),\n        environment: ['ENV_VAR_ONE=VALUE_OVERRIDE']\n      )\n\n      apply_manifest_on(agent, manifest, environment: { 'ENV_VAR_ONE' => 'VALUE' }) do |result|\n        assert_env_var_values(result.stdout, ENV_VAR_ONE: 'VALUE_OVERRIDE')\n      end\n    end\n\n    step \"Temporarily overrides previously set environment variables even if the passed-in value is empty\" do\n      manifest = exec_resource_manifest.call(\n        command: print_env_vars.call('ENV_VAR_ONE'),\n        environment: ['ENV_VAR_ONE=']\n      )\n\n      apply_manifest_on(agent, manifest, environment: { 'ENV_VAR_ONE' => 'VALUE' }) do |result|\n        assert_env_var_values(result.stdout, ENV_VAR_ONE: '')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/exec/should_set_path.rb",
    "content": "test_name \"the path statement should work to locate commands\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  file = agent.tmpfile('touched-should-set-path')\n\n  step \"clean up the system for the test\"\n  on agent, \"rm -f #{file}\"\n\n  step \"invoke the exec resource with a path set\"\n  on(agent, puppet_resource('exec', 'test',\n                   \"command='#{agent.touch(file, false)}'\", \"path='#{agent.path}'\"))\n\n  step \"verify that the files were created\"\n  on agent, \"test -f #{file}\"\n\n  step \"clean up the system after testing\"\n  on agent, \"rm -f #{file}\"\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/ascii_diff_output_content_attribute.rb",
    "content": "test_name \"ASCII Diff Output of Content Attribute\" do\n  tag 'audit:high',\n      'audit:acceptance'\n\n  sha256 = Digest::SHA256.new\n  agents.each do |agent|\n    step 'When handling ASCII files' do\n      target = agent.tmpfile('content_ASCII_file_test')\n      initial_text = 'Initial Text'\n      initial_text_sha_checksum = sha256.hexdigest(initial_text)\n      updated_text = 'Updated Text'\n      updated_text_sha_checksum = sha256.hexdigest(updated_text)\n      on agent, puppet('config', 'set', 'diff', 'diff')\n\n      step 'Ensure the test environment is clean' do\n        on agent, \"rm -f #{target}\"\n      end\n\n      teardown do\n        on agent, \"rm -f #{target}\"\n      end\n\n      step 'Create ASCII file using content' do\n        manifest = \"file { '#{target}': content => '#{initial_text}', ensure => present , checksum => 'sha256'}\"\n\n        on(agent, puppet('apply'), :stdin => manifest) do |result|\n          assert_match(/ensure: defined content as '{sha256}#{initial_text_sha_checksum}'/, result.stdout, \"#{agent}: checksum of ASCII file not matched\")\n        end\n      end\n\n      step 'Update existing ASCII file content' do\n        manifest = \"file { '#{target}': content => '#{updated_text}', ensure => present , checksum => 'sha256'}\"\n\n        on(agent, puppet('apply','--show_diff'), :stdin => manifest) do |result|\n          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\")\n          assert_match(/^- ?#{initial_text}$/, result.stdout, \"#{agent}: initial text not found in diff\")\n          assert_match(/^\\+ ?#{updated_text}$/, result.stdout, \"#{agent}: updated text not found in diff\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/bin_diff_output_content_attribute.rb",
    "content": "test_name \"Binary Diff Output of Content Attribute\" do\n  tag 'audit:high',\n      'audit:acceptance'\n\n  # cannot test binary diff on windows2012r2_ja-64-1\n  # 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\n  # Error: Could not send report: anchor value must contain alphanumerical characters only\n  confine :except, {}, hosts.select { |host| host[:platform]=~ /windows/ && host[:locale] == 'ja' }\n\n  sha256 = Digest::SHA256.new\n  agents.each do |agent|\n    step 'When handling binary files' do\n      target = agent.tmpfile('content_binary_file_test')\n      initial_bin_data = \"\\xc7\\xd1\\xfc\\x84\"\n      initial_base64_data = Base64.encode64(initial_bin_data).chomp\n      initial_sha_checksum = sha256.hexdigest(initial_bin_data)\n      updated_bin_data = \"\\xc7\\xd1\\xfc\\x85\"\n      updated_base64_data = Base64.encode64(updated_bin_data).chomp\n      updated_sha_checksum = sha256.hexdigest(updated_bin_data)\n      on(agent, puppet('config', 'set', 'diff', 'diff'))\n\n      agent_default_external_encoding=nil\n      on(agent, \"#{ruby_command(agent)} -e \\\"puts Encoding.default_external\\\"\") do |result|\n        agent_default_external_encoding = result.stdout.chomp\n      end\n\n      if agent_default_external_encoding && agent_default_external_encoding != Encoding.default_external\n        begin\n          initial_bin_data=initial_bin_data.force_encoding(agent_default_external_encoding).encode(Encoding.default_external)\n          updated_bin_data=updated_bin_data.force_encoding(agent_default_external_encoding).encode(Encoding.default_external)\n        rescue Encoding::InvalidByteSequenceError\n          #depending on agent_default_external_encoding, the conversion may fail, but this should not be a problem\n        end\n      end\n\n      teardown do\n        on agent, \"rm -f #{target}\"\n      end\n\n      step 'Ensure the test environment is clean' do\n        on agent, \"rm -f #{target}\"\n      end\n\n      step 'Create binary file using content' do\n        manifest = \"file { '#{target}': content => Binary('#{initial_base64_data}'), ensure => present , checksum => 'sha256'}\"\n\n        on(agent, puppet('apply'), :stdin => manifest) do |result|\n          assert_match(/ensure: defined content as '{sha256}#{initial_sha_checksum}'/, result.stdout, \"#{agent}: checksum of binary file not matched\")\n        end\n      end\n\n      step 'Update existing binary file content' do\n        manifest = \"file { '#{target}': content => Binary('#{updated_base64_data}'), ensure => present , checksum => 'sha256'}\"\n\n        on(agent, puppet('apply','--show_diff'), :stdin => manifest) do |result|\n          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\")\n          refute_match(/content: Received a Log attribute with invalid encoding:/, result.stdout, \"#{agent}: Received a Log attribute with invalid encoding\")\n          if initial_bin_data.valid_encoding? && updated_bin_data.valid_encoding?\n            assert_match(/^- ?#{initial_bin_data}$/, result.stdout, \"#{agent}: initial utf-8 data not found in binary diff\")\n            assert_match(/^\\+ ?#{updated_bin_data}$/, result.stdout, \"#{agent}: updated utf-8 data not found in binary diff\")\n          else\n            assert_match(/Binary files #{target} and .* differ/, result.stdout, \"#{agent}: Binary file diff notice not matched\")\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/content_attribute.rb",
    "content": "test_name \"Content Attribute\"\ntag 'audit:high',\n    'audit:refactor',   # Use block stype test_name\n    'audit:acceptance'\n\nagents.each do |agent|\n  target = agent.tmpfile('content_file_test')\n\n  step \"Ensure the test environment is clean\"\n  on agent, \"rm -f #{target}\"\n\n  step \"Content Attribute: using raw content\"\n\n  checksums_fips = ['sha256', 'sha256lite']\n  checksums_no_fips = ['sha256', 'sha256lite', 'md5', 'md5lite']\n\n  if on(agent, facter(\"fips_enabled\")).stdout =~ /true/\n    checksums = checksums_fips\n  else\n    checksums = checksums_no_fips\n  end\n\n  manifest = \"file { '#{target}': content => 'This is the test file content', ensure => present }\"\n  manifest += checksums.collect {|checksum_type|\n    \"file { '#{target+checksum_type}': content => 'This is the test file content', ensure => present, checksum => #{checksum_type} }\"\n  }.join(\"\\n\")\n  apply_manifest_on(agent, manifest) do |result|\n    checksums.each do |checksum_type|\n      refute_match(/content changed/, result.stdout, \"#{agent}: shouldn't have overwrote #{target+checksum_type}\")\n    end\n  end\n\n  on(agent, \"cat #{target}\") do |result|\n    assert_match(/This is the test file content/, result.stdout, \"File content not matched on #{agent}\") unless agent['locale'] == 'ja'\n  end\n\n  step \"Content Attribute: illegal timesteps\"\n  ['mtime', 'ctime'].each do |checksum_type|\n    manifest = \"file { '#{target+checksum_type}': content => 'This is the test file content', ensure => present, checksum => #{checksum_type} }\"\n    apply_manifest_on(agent, manifest, :acceptable_exit_codes => [1]) do |result|\n      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'\n    end\n  end\n\n  step \"Ensure the test environment is clean\"\n  on(agent, \"rm -f #{target}\")\n\n  step \"Content Attribute: using a checksum from filebucket\"\n  on(agent, \"echo 'This is the checksum file contents' > #{target}\")\n\n  step \"Backup file into the filebucket\"\n  on(agent, puppet_filebucket(\"backup --local #{target}\"))\n\n  step \"Modify file to force apply to retrieve file from local clientbucket\"\n  on(agent, \"echo 'This is the modified file contents' > #{target}\")\n\n  dir = on(agent, puppet_filebucket(\"--configprint clientbucketdir\")).stdout.chomp\n\n  sha256_manifest = %Q|\n    filebucket { 'local':\n      path => '#{dir}',\n    }\n\n    file { '#{target}':\n      ensure  => present,\n      content => '{sha256}3b9238769b033b48073267b8baea00fa51c598dc14081da51f2e510c37c46a28',\n      backup  => local,\n    }\n  |\n\n  step \"Applying Manifest on Agent\"\n  apply_manifest_on agent, sha256_manifest\n\n  step \"Validate filebucket checksum file contents\"\n  on(agent, \"cat #{target}\") do |result|\n    assert_match(/This is the checksum file content/, result.stdout, \"File content not matched on #{agent}\") unless agent['locale'] == 'ja'\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/handle_fifo_files.rb",
    "content": "test_name \"should be able to handle fifo files\"\ntag 'audit:high',\n    'audit:acceptance'\nconfine :except, :platform => /windows/\n\ndef ensure_content_to_file_manifest(file_path, ensure_value)\n  return <<-MANIFEST\n  file { \"#{file_path}\":\n    ensure => #{ensure_value},\n    content => \"Hello World\"\n  }\n  MANIFEST\nend\n\nagents.each do |agent|\n  tmp_path = agent.tmpdir(\"tmpdir\")\n  fifo_path = \"#{tmp_path}/myfifo\"\n\n  teardown do\n    agent.rm_rf(tmp_path)\n  end\n\n  step \"create fifo\" do\n    on(agent, \"mkfifo #{fifo_path}\")\n  end\n\n  step \"check that fifo got created\" do\n    on(agent, \"ls -l #{fifo_path}\") do |result|\n      assert(result.stdout.start_with?('p'))\n    end\n  end\n\n  step \"puppet ensures given fifo is present\" do\n    apply_manifest_on(agent, ensure_content_to_file_manifest(fifo_path, 'present'), :acceptable_exit_codes => [2]) do |result|\n      assert_match(/Warning: .+ Ensure set to :present but file type is fifo so no content will be synced/, result.stderr)\n    end\n  end\n\n  step \"check that given file is still a fifo\" do\n    on(agent, \"ls -l #{fifo_path}\") do |result|\n      assert(result.stdout.start_with?('p'))\n    end\n  end\n\n  step \"puppet ensures given fifo is a regular file\" do\n    apply_manifest_on(agent, ensure_content_to_file_manifest(fifo_path, 'file'), :acceptable_exit_codes => [0]) do |result|\n      assert_match(/Notice: .+\\/myfifo\\]\\/ensure: defined content as '{/, result.stdout)\n      refute_match(/Warning: .+ Ensure set to :present but file type is fifo so no content will be synced/, result.stderr)\n    end\n  end\n\n  step \"check that given fifo is now a regular file\" do\n    on(agent, \"ls -l #{fifo_path}\") do |result|\n      assert(result.stdout.start_with?('-'))\n    end\n  end\n\n  step \"check that given file now has desired content\" do\n    on(agent, \"cat #{fifo_path}\") do |result|\n      assert_equal('Hello World', result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/handle_fifo_files_when_recursing.rb",
    "content": "test_name \"should be able to handle fifo files when recursing\"\ntag 'audit:high',\n    'audit:acceptance'\nconfine :except, :platform => /windows/\n\ndef ensure_owner_recursively_manifest(path, owner_value)\n  return <<-MANIFEST\n  file { \"#{path}\":\n    ensure  => present,\n    recurse => true,\n    owner   => #{owner_value}\n  }\n  MANIFEST\nend\n\nagents.each do |agent|\n  initial_owner = ''\n  random_user = \"pl#{rand(999).to_i}\"\n\n  tmp_path = agent.tmpdir(\"tmpdir\")\n  fifo_path = \"#{tmp_path}/myfifo\"\n\n  teardown do\n    agent.rm_rf(tmp_path)\n  end\n\n  step \"create fifo file\" do\n    on(agent, \"mkfifo #{fifo_path}\")\n    on(agent, puppet(\"resource user #{random_user} ensure=absent\"))\n  end\n\n  step \"check that fifo file got created\" do\n    on(agent, \"ls -l #{fifo_path}\") do |result|\n      assert(result.stdout.start_with?('p'))\n      initial_owner = result.stdout.split[2]\n    end\n  end\n\n  step \"create a new user\" do\n    on(agent, puppet(\"resource user #{random_user} ensure=present\"))\n  end\n\n  step \"puppet ensures '#{random_user}' as owner of path\" do\n    apply_manifest_on(agent, ensure_owner_recursively_manifest(tmp_path, random_user), :acceptable_exit_codes => [0]) do |result|\n      assert_match(/#{tmp_path}\\]\\/owner: owner changed '#{initial_owner}' to '#{random_user}'/, result.stdout)\n      refute_match(/Error: .+ Failed to generate additional resources using ‘eval_generate’: Cannot manage files of type fifo/, result.stderr)\n    end\n  end\n\n  step \"check that given file is still a fifo\" do\n    on(agent, \"ls -l #{fifo_path}\") do |result|\n      assert(result.stdout.start_with?('p'))\n    end\n  end\n\n  step \"check ownership of fifo file\" do\n    on(agent, \"ls -l #{fifo_path}\") do |result|\n      user = result.stdout.split[2]\n      assert_equal(random_user, user)\n    end\n  end\n\n  step \"check ownership of tmp folder\" do\n    on(agent, \"ls -ld #{tmp_path}\") do |result|\n      user = result.stdout.split[2]\n      assert_equal(random_user, user)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/should_create_directory.rb",
    "content": "test_name \"should create directory\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  target = agent.tmpfile(\"create-dir\")\n  teardown do\n    step \"clean up after the test run\" do\n      on(agent, \"rm -rf #{target}\")\n    end\n  end\n\n  step \"verify we can create a directory\" do\n    on(agent, puppet_resource(\"file\", target, 'ensure=directory'))\n  end\n\n  step \"verify the directory was created\" do\n    on(agent, \"test -d #{target}\")\n  end\n\n  dir_manifest = agent.tmpfile(\"dir-resource\")\n  create_remote_file(agent, dir_manifest, <<-PP)\n    $dir='#{target}'\n    $same_dir='#{target}/'\n    file {$dir:\n      ensure => directory,\n    }\n    file { $same_dir:\n      ensure => directory,\n    }\n  PP\n\n  step \"verify we can't create same dir resource with a trailing slash\" do\n    options = {:acceptable_exit_codes => [1]}\n    on(agent, puppet_apply(\"--noop #{dir_manifest}\"), options) do |result|\n      unless agent['locale'] == 'ja'\n        assert_match('Cannot alias File', result.output,\n                     'duplicate directory resources did not fail properly')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/should_create_empty.rb",
    "content": "test_name \"should create empty file for 'present'\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  target = agent.tmpfile(\"empty\")\n\n  step \"clean up the system before we begin\"\n  on(agent, \"rm -rf #{target}\")\n\n  step \"verify we can create an empty file\"\n  on(agent, puppet_resource(\"file\", target, 'ensure=present'))\n\n  step \"verify the target was created\"\n  on(agent, \"test -f #{target} && test ! -s #{target}\")\n\n  step \"clean up after the test run\"\n  on(agent, \"rm -rf #{target}\")\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/should_create_symlink.rb",
    "content": "test_name \"should create symlink\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\ndef message\n  'hello world'\nend\n\ndef reset_link_and_target(agent, link, target)\n  step \"clean up the system before we begin\"\n  on agent, \"rm -rf #{target} #{link}\"\n  on agent, \"echo '#{message}' > #{target}\"\nend\n\ndef verify_symlink(agent, link, target)\n  step \"verify the symlink was created\"\n  on(agent, \"test -L #{link} && test -f #{link}\")\n  step \"verify the symlink points to a file\"\n  on(agent, \"test -f #{target}\")\n\n  step \"verify the content is identical on both sides\"\n  on(agent, \"cat #{link}\") do |result|\n    fail_test \"link missing content\" unless result.stdout.include?(message)\n  end\n  on(agent, \"cat #{target}\") do |result|\n    fail_test \"target missing content\" unless result.stdout.include?(message)\n  end\nend\n\nagents.each do |agent|\n  if agent.platform.variant == 'windows'\n    # symlinks are supported only on Vista+ (version 6.0 and higher)\n    on(agent, facter('kernelmajversion')) do |result|\n      skip_test \"Test not supported on this platform\" if result.stdout.chomp.to_f < 6.0\n    end\n  end\n\n  link_file = agent.tmpfile(\"symlink-link\")\n  target_file = agent.tmpfile(\"symlink-target\")\n  link_dir = agent.tmpdir(\"dir_symlink-link\")\n  target_dir = agent.tmpdir(\"dir-symlink-target\")\n\n  reset_link_and_target(agent, link_file, target_file)\n  reset_link_and_target(agent, link_dir, target_dir)\n\n  step \"verify we can create a symlink with puppet resource\"\n  on(agent, puppet_resource(\"file\", \"#{link_file}\", \"ensure=#{target_file}\"))\n  verify_symlink(agent, link_file, target_file)\n  reset_link_and_target(agent, link_file, target_file)\n\n  step \"verify that 'links => manage' preserves a symlink\"\n  apply_manifest_on(agent, \"file { '#{link_file}': ensure => link, target => '#{target_file}', links => manage }\")\n  verify_symlink(agent, link_file, target_file)\n  reset_link_and_target(agent, link_file, target_file)\n\n  step \"verify that 'links => manage' and 'recurse => true' preserves links in a directory\"\n  on(agent, puppet_resource(\"file\", target_dir, \"ensure=directory\"))\n  reset_link_and_target(agent, link_dir, \"#{target_dir}/symlink-target\")\n  apply_manifest_on(agent, \"file { '#{link_dir}': ensure => directory, target => '#{target_dir}', links => manage, recurse => true }\")\n  verify_symlink(agent, \"#{link_dir}/symlink-target\", \"#{target_dir}/symlink-target\")\n\n  step \"clean up after the test run\"\n  on agent, \"rm -rf #{target_file} #{link_file} #{target_dir} #{link_dir}\"\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/should_default_mode.rb",
    "content": "test_name \"file resource: set default modes\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\ndef regexp_mode(mode)\n  Regexp.new(\"mode\\s*=>\\s*'0?#{mode}'\")\nend\n\nagents.each do |agent|\n  step \"setup\"\n  parent = agent.tmpdir('default-mode-parent')\n  on(agent, \"rm -rf #{parent}\")\n\n  step \"puppet should set execute bit on readable directories\"\n  on(agent, puppet_resource(\"file\", parent, \"ensure=directory\", \"mode=0644\")) do |result|\n    assert_match(regexp_mode(755), result.stdout)\n  end\n\n  step \"include execute bit on newly created directories\"\n  dir = \"#{parent}/dir\"\n  on(agent, \"mkdir #{dir} && cd #{dir} && cd ..\")\n\n  step \"exclude execute bit from newly created files\"\n  file = \"#{parent}/file.txt\"\n  on(agent, \"echo foobar > #{file}\")\n  on(agent, \"#{file}\", :acceptable_exit_codes => (1..255)) do |result|\n    refute_match(/foobar/, result.stdout)\n  end\n\n  step \"set execute bit on file if explicitly specified\"\n  file_750 = \"#{parent}/file_750.txt\"\n  on(agent, puppet_resource(\"file\", file_750, \"ensure=file\", \"mode=0750\")) do |result|\n    assert_match(regexp_mode(750), result.stdout)\n  end\n\n  step \"don't set execute bit if directory not readable\"\n  dir_600 = \"#{parent}/dir_600\"\n  on(agent, puppet_resource(\"file\", dir_600, \"ensure=directory\", \"mode=0600\")) do |result|\n    assert_match(regexp_mode(700), result.stdout) # readable by owner, but not group\n  end\n\n  on(agent, \"rm -rf #{parent}\")\nend\n\n"
  },
  {
    "path": "acceptance/tests/resource/file/should_remove_dir.rb",
    "content": "test_name \"should remove directory, but force required\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  target = agent.tmpdir(\"delete-dir\")\n\n  step \"clean up the system before we begin\"\n  on(agent, \"rm -rf #{target} ; mkdir -p #{target}\")\n\n  step \"verify we can't remove a directory without 'force'\"\n  on(agent, puppet_resource(\"file\", target, 'ensure=absent')) do |result|\n    fail_test \"didn't tell us that force was required\" unless\n      result.stdout.include? \"Not removing directory; use 'force' to override\" unless agent['locale'] == 'ja'\n  end\n\n  step \"verify the directory still exists\"\n  on(agent, \"test -d #{target}\")\n\n  step \"verify we can remove a directory with 'force'\"\n  on(agent, puppet_resource(\"file\", target, 'ensure=absent', 'force=true'))\n\n  step \"verify that the directory is gone\"\n  on(agent, \"test -d #{target}\", :acceptable_exit_codes => [1])\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/should_remove_file.rb",
    "content": "test_name \"should remove file\"\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n  target = agent.tmpfile('delete-file')\n\n  step \"clean up the system before we begin\"\n  on agent, \"rm -rf #{target} && touch #{target}\"\n\n  step \"verify we can remove a file\"\n  on(agent, puppet_resource(\"file\", target, 'ensure=absent'))\n\n  step \"verify that the file is gone\"\n  on agent, \"test -e #{target}\", :acceptable_exit_codes => [1]\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/source_attribute.rb",
    "content": "test_name \"The source attribute\" do\n  require 'puppet/acceptance/module_utils'\n  extend Puppet::Acceptance::ModuleUtils\n\n  tag 'audit:high',\n      'audit:acceptance',\n      'server'\n\n  @target_file_on_windows = 'C:/windows/temp/source_attr_test'\n  @target_file_on_nix     = '/tmp/source_attr_test'\n  @target_dir_on_windows  = 'C:/windows/temp/source_attr_test_dir'\n  @target_dir_on_nix      = '/tmp/source_attr_test_dir'\n\n  # In case any of the hosts happens to be fips enabled we limit to the lowest\n  # common denominator.\n  checksums_fips = [nil, 'sha256', 'sha256lite', 'ctime', 'mtime']\n  checksums_no_fips = [nil, 'sha256', 'sha256lite', 'md5', 'md5lite', 'ctime', 'mtime']\n\n  fips_host_present = hosts.any? { |host| on(host, facter(\"fips_enabled\")).stdout =~ /true/ }\n\n  if fips_host_present\n    checksums = checksums_fips\n  else\n    checksums = checksums_no_fips\n  end\n\n  orig_installed_modules = get_installed_modules_for_hosts hosts\n  teardown do\n    rm_installed_modules_from_hosts orig_installed_modules, (get_installed_modules_for_hosts hosts)\n    hosts.each do |host|\n      file_to_rm = host['platform'] =~ /windows/ ? @target_file_on_windows : @target_file_on_nix\n      dir_to_rm = host['platform'] =~ /windows/ ? @target_dir_on_windows : @target_dir_on_nix\n\n      checksums.each do |checksum_type|\n        on(host, \"rm #{file_to_rm}#{checksum_type}\", :acceptable_exit_codes => [0,1])\n        on(host, \"rm -r #{dir_to_rm}#{checksum_type}\", :acceptable_exit_codes => [0,1])\n      end\n    end\n  end\n\n  step \"Setup - create environment and test module\"\n  # set directories\n  testdir = master.tmpdir('file_source_attr')\n  env_dir = \"#{testdir}/environments\"\n  prod_dir = \"#{env_dir}/production\"\n  manifest_dir = \"#{prod_dir}/manifests\"\n  manifest_file = \"#{prod_dir}/manifests/site.pp\"\n  module_dir = \"#{prod_dir}/modules\"\n  test_module_dir = \"#{module_dir}/source_test_module\"\n  test_module_manifests_dir = \"#{test_module_dir}/manifests\"\n  test_module_files_dir = \"#{test_module_dir}/files\"\n  mod_manifest_file = \"#{test_module_manifests_dir}/init.pp\"\n  mod_source_file = \"#{test_module_files_dir}/source_file\"\n  mod_source_dir = \"#{test_module_files_dir}/source_dir\"\n  mod_source_dir_file = \"#{mod_source_dir}/source_dir_file\"\n\n  mod_source = ' the content is present'\n\n  def mod_manifest_entry(checksum_type = nil)\n    checksum = if checksum_type then \"checksum => #{checksum_type},\" else \"\" end\n    manifest = <<-EOF\n    $target_file#{checksum_type} = $::kernel ? {\n      \\\\'windows\\\\' => \\\\'#{@target_file_on_windows}#{checksum_type}\\\\',\n      default   => \\\\'#{@target_file_on_nix}#{checksum_type}\\\\'\n    }\n\n    file { $target_file#{checksum_type}:\n      source => \\\\'puppet:///modules/source_test_module/source_file\\\\',\n      #{checksum}\n      ensure => present\n    }\n\n    $target_dir#{checksum_type} = $::kernel ? {\n      \\\\'windows\\\\' => \\\\'#{@target_dir_on_windows}#{checksum_type}\\\\',\n      default   => \\\\'#{@target_dir_on_nix}#{checksum_type}\\\\'\n    }\n\n    file { $target_dir#{checksum_type}:\n      source => \\\\'puppet:///modules/source_test_module/source_dir\\\\',\n      #{checksum}\n      ensure => directory,\n      recurse => true\n    }\n  EOF\n    manifest\n  end\n\n  mod_manifest = <<-EOF\n  class source_test_module {\n  #{checksums.collect { |checksum_type| mod_manifest_entry(checksum_type) }.join(\"\\n\")}\n  }\n  EOF\n\n  env_manifest = <<-EOF\n  filebucket { \\\\'main\\\\':\n    server => \\\\'#{master}\\\\',\n    path   => false,\n  }\n\n  File { backup => \\\\'main\\\\' }\n\n  node default {\n    include source_test_module\n  }\n  EOF\n\n  # apply manifests to setup environment and modules\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n    File {\n      ensure => directory,\n      mode => '0755',\n    }\n\n    file {\n      '#{testdir}':;\n      '#{env_dir}':;\n      '#{prod_dir}':;\n      '#{manifest_dir}':;\n      '#{module_dir}':;\n      '#{test_module_dir}':;\n      '#{test_module_manifests_dir}':;\n      '#{test_module_files_dir}':;\n    }\n\n    file { '#{mod_manifest_file}':\n      ensure => file,\n      mode => '0644',\n      content => '#{mod_manifest}',\n    }\n\n    file { '#{mod_source_file}':\n      ensure => file,\n      mode => '0644',\n      content => '#{mod_source}',\n    }\n\n    file { '#{mod_source_dir}':\n      ensure => directory,\n      mode => '0755'\n    }\n\n    file { '#{mod_source_dir_file}':\n      ensure => file,\n      mode => '0644',\n      content => '#{mod_source}',\n    }\n\n    file { '#{manifest_file}':\n      ensure => file,\n      mode => '0644',\n      content => '#{env_manifest}',\n    }\n  MANIFEST\n\n  step \"When using a puppet:/// URI with a master/agent setup\"\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{env_dir}\",\n    },\n  }\n  with_puppet_running_on(master, master_opts, testdir) do\n    agents.each do |agent|\n      # accept an exit code of 2 which is returned if there are changes\n      step \"create file the first run\"\n      on(agent, puppet('agent', \"--test\"), :acceptable_exit_codes => [0,2]) do\n        file_to_check = agent['platform'] =~ /windows/ ? @target_file_on_windows : @target_file_on_nix\n        dir_to_check = agent['platform'] =~ /windows/ ? @target_dir_on_windows : @target_dir_on_nix\n\n        checksums.each do |checksum_type|\n          on agent, \"cat #{file_to_check}#{checksum_type}\" do |result|\n            assert_match(/the content is present/, result.stdout, \"Result file not created #{checksum_type}\")\n          end\n\n          on agent, \"cat #{dir_to_check}#{checksum_type}/source_dir_file\" do |result|\n            assert_match(/the content is present/, result.stdout, \"Result file not created #{checksum_type}\")\n          end\n        end\n      end\n\n      step \"second run should not update file\"\n      on(agent, puppet('agent', \"--test\"), :acceptable_exit_codes => [0,2]) do |result|\n        refute_match(/content changed.*(md5|sha256)/, result.stdout, \"Shouldn't have overwritten any files\")\n\n        # When using ctime/mtime, the agent compares the values from its\n        # local file with the values on the master to determine if the\n        # file is insync or not. If during the first run, the agent\n        # creates the files, and the resulting ctime/mtime are still\n        # behind the times on the master, then the 2nd agent run will\n        # consider the file to not be insync, and will update it\n        # again. This process will repeat until the agent updates the\n        # file, and the resulting ctime/mtime are after the values on\n        # the master, at which point it will have converged.\n        if result.stdout =~ /content changed.*ctime/\n          Log.warn \"Agent did not converge using ctime\"\n        end\n\n        if result.stdout =~ /content changed.*mtime/\n          Log.warn \"Agent did not converge using mtime\"\n        end\n      end\n    end\n\n=begin\n    # Disable flaky test until PUP-4115 is addressed.\n    step \"touch files and verify they're updated with ctime/mtime\"\n    # wait until we're not at the mtime of files on the agents\n    # this could be done cross-platform using Puppet, but a single puppet query is unlikely to be less than a second,\n    # and iterating over all agents would be much slower\n    sleep(1)\n\n    on master, \"touch #{mod_source_file} #{mod_source_dir_file}\"\n    agents.each do |agent|\n      on(agent, puppet('agent', \"--test\"), :acceptable_exit_codes => [0,2]) do\n        file_to_check = agent['platform'] =~ /windows/ ? @target_file_on_windows : @target_file_on_nix\n        dir_to_check = agent['platform'] =~ /windows/ ? @target_dir_on_windows : @target_dir_on_nix\n        ['ctime', 'mtime'].each do |time_type|\n          assert_match(/File\\[#{file_to_check}#{time_type}\\]\\/content: content changed/, stdout, \"Should have updated files\")\n          assert_match(/File\\[#{dir_to_check}#{time_type}\\/source_dir_file\\]\\/content: content changed/, stdout, \"Should have updated files\")\n        end\n      end\n    end\n=end\n  end\n\n  # TODO: Add tests for puppet:// URIs with multi-master/agent setups.\n  step \"When using puppet apply\"\n  agents.each do |agent|\n    step \"Setup testing local file sources\"\n\n    # create one larger manifest with all the files so we don't have to run\n    # puppet apply per each checksum_type\n    localsource_testdir = agent.tmpdir('local_source_file_test')\n    source = \"#{localsource_testdir}/source_mod/files/source\"\n    on agent, \"mkdir -p #{File.dirname(source)}\"\n    # don't put a 'z' in this content\n    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.'\n    create_remote_file agent, source, source_content\n\n    local_apply_manifest = \"\"\n    target = {}\n    checksums.each do |checksum_type|\n      target[checksum_type] = \"#{localsource_testdir}/target#{checksum_type}\"\n      checksum = if checksum_type then \"checksum => #{checksum_type},\" else \"\" end\n      local_apply_manifest.concat(\"file { '#{target[checksum_type]}': source => '#{source}', ensure => present, #{checksum} }\\n\")\n    end\n\n    apply_manifest_on agent, local_apply_manifest\n\n    checksums.each do |checksum_type|\n      step \"Using a local file path. #{checksum_type}\"\n      on(agent, \"cat #{target[checksum_type]}\") do |result|\n        assert_match(/Yay, this is the local file./, result.stdout, \"FIRST: File contents not matched on #{agent}\")\n      end\n    end\n\n    step \"second run should not update any files\"\n    apply_manifest_on(agent, local_apply_manifest) do |result|\n      refute_match(/content changed/, result.stdout, \"Shouldn't have overwrote any files\")\n    end\n\n    # changes in source file producing updates is tested elsewhere\n    step \"subsequent run should not update file using <checksum>lite if only after byte 512 is changed\"\n    byte_after_md5lite = 513\n    source_content[byte_after_md5lite] = 'z'\n    create_remote_file agent, source, source_content\n\n    if fips_host_present\n      apply_manifest_on(agent, \"file { '#{localsource_testdir}/targetsha256lite': source => '#{source}', ensure => present, checksum => sha256lite }\") do |result|\n        refute_match(/(content changed|defined content)/, result.stdout, \"Shouldn't have overwrote any files\")\n      end\n    else\n      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|\n        refute_match(/(content changed|defined content)/, result.stdout, \"Shouldn't have overwrote any files\")\n      end\n    end\n\n    local_module_manifest = \"\"\n    checksums.each do |checksum_type|\n      on agent, \"rm -rf #{target[checksum_type]}\"\n      checksum = if checksum_type then \"checksum => #{checksum_type},\" else \"\" end\n      local_module_manifest.concat(\"file { '#{target[checksum_type]}': source => 'puppet:///modules/source_mod/source', ensure => present, #{checksum} }\\n\")\n    end\n\n    localsource_test_manifest = agent.tmpfile('local_source_test_manifest')\n    create_remote_file agent, localsource_test_manifest, local_module_manifest\n    on agent, puppet( %{apply --modulepath=#{localsource_testdir} #{localsource_test_manifest}} )\n\n    checksums.each do |checksum_type|\n      step \"Using a puppet:/// URI with checksum type: #{checksum_type}\"\n      on(agent, \"cat #{target[checksum_type]}\") do |result|\n        assert_match(/Yay, this is the local file./, result.stdout, \"FIRST: File contents not matched on #{agent}\")\n      end\n    end\n\n    step \"second run should not update any files using apply with puppet:/// URI source\"\n    on(agent, puppet( %{apply --modulepath=#{localsource_testdir} #{localsource_test_manifest}} )) do |result|\n      refute_match(/content changed/, result.stdout, \"Shouldn't have overwrote any files\")\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/symbolic_modes.rb",
    "content": "test_name 'file resource: symbolic modes' do\n  confine :except, :platform => /^windows/\n  confine :to, {}, hosts.select {|host| !host[:roles].include?('master')}\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  class FileSymlink\n    attr_reader :mode, :path, :start_mode, :symbolic_mode\n\n    def initialize(base_dir, file_type, symbolic_mode, mode, start_mode=nil)\n      @base_dir      = base_dir\n      @file_type     = file_type\n      @symbolic_mode = symbolic_mode\n      @mode          = mode\n      @start_mode    = start_mode\n\n      if @start_mode.nil?\n        @path= \"#{@base_dir}/#{@file_type}_#{@symbolic_mode}_#{@mode.to_s(8)}\"\n      else\n        @path= \"#{@base_dir}/#{@file_type}_#{@symbolic_mode}_#{@start_mode.to_s(8)}_#{@mode.to_s(8)}\"\n      end\n    end\n\n    # does the mode of the file/directory change from start_mode to puppet apply\n    def mode_changes?\n      ! @start_mode.nil? && @start_mode != @mode\n    end\n\n    def get_manifest\n      \"file { #{@path.inspect}: ensure => '#{@file_type}', mode => '#{@symbolic_mode}' }\"\n    end\n  end\n\n  class BaseTest\n    include Beaker::DSL::Assertions\n\n    def initialize(testcase, agent, base_dir)\n      @testcase       = testcase\n      @agent          = agent\n      @base_dir       = base_dir\n      @file_list      = []\n      @directory_list = []\n    end\n\n    def assert_mode(agent, path, expected_mode)\n      permissions = @testcase.stat(agent, path)\n      assert_equal(expected_mode, permissions[2], \"'#{path}' current mode #{permissions[2].to_s(8)} doesn't match expected mode #{expected_mode.to_s(8)}\")\n    end\n\n    def manifest\n      manifest_array = (@file_list + @directory_list).map {|x| x.get_manifest}\n      @testcase.step(manifest_array)\n      manifest_array.join(\"\\n\")\n    end\n\n    def puppet_reapply\n      @testcase.apply_manifest_on(@agent, manifest) do |apply_result|\n        refute_match(/mode changed/, apply_result.stdout, \"reapplied the symbolic mode change\")\n        (@file_list + @directory_list).each do |file|\n          refute_match(/#{Regexp.escape(file.path)}/, apply_result.stdout, \"Expected to not see '#{file.path}' in 'puppet apply' output\")\n        end\n      end\n    end\n  end\n\n  class CreateTest < BaseTest\n\n    def symlink_file(symbolic_mode, mode)\n      @file_list << FileSymlink.new(@base_dir, 'file', symbolic_mode, mode)\n    end\n\n    def symlink_directory(symbolic_mode, mode)\n      @directory_list << FileSymlink.new(@base_dir, 'directory', symbolic_mode, mode)\n    end\n\n    def puppet_apply\n      apply_result = @testcase.apply_manifest_on(@agent, manifest).stdout\n      (@file_list + @directory_list).each do |file|\n        assert_match(/File\\[#{Regexp.escape(file.path)}\\]\\/ensure: created/, apply_result, \"Failed to create #{file.path}\")\n        assert_mode(@agent, file.path, file.mode)\n      end\n    end\n  end\n\n  class ModifyTest < BaseTest\n\n    def symlink_file(symbolic_mode, start_mode, mode)\n      @file_list << FileSymlink.new(@base_dir, 'file', symbolic_mode, mode, start_mode)\n    end\n\n    def symlink_directory(symbolic_mode, start_mode, mode)\n      @directory_list << FileSymlink.new(@base_dir, 'directory', symbolic_mode, mode, start_mode)\n    end\n\n    def create_starting_state\n      files       = @file_list.collect {|x| \"'#{x.path}'\" }\n      directories = @directory_list.collect {|x| \"'#{x.path}'\" }\n\n      @testcase.on(@agent, \"touch #{files.join(' ')}\")\n      @testcase.on(@agent, \"mkdir -p #{directories.join(' ')}\")\n      @testcase.on(@agent, \"chown symuser:symgroup #{files.join(' ')} #{directories.join(' ')}\")\n      cmd_list = []\n      (@file_list + @directory_list).each do |file|\n        cmd_list << \"chmod #{file.start_mode.to_s(8)} '#{file.path}'\"\n      end\n      @testcase.on(@agent, cmd_list.join(' && '))\n    end\n\n    def puppet_apply\n      @testcase.step(manifest)\n      apply_result = @testcase.apply_manifest_on(@agent, manifest).stdout\n      @testcase.step(apply_result)\n      (@file_list + @directory_list).each do |file|\n        if file.mode_changes?\n          assert_match(/File\\[#{Regexp.escape(file.path)}.* mode changed '#{'%04o' % file.start_mode}'.* to '#{'%04o' % file.mode}'/,\n                       apply_result, \"couldn't set mode to #{file.symbolic_mode}\")\n        else\n          refute_match(/#{Regexp.escape(file.path)}.*mode changed/, apply_result, \"reapplied the symbolic mode change for file #{file.path}\")\n        end\n        assert_mode(@agent, file.path, file.mode)\n      end\n    end\n  end\n\n# For your reference:\n# 4000    the set-user-ID-on-execution bit\n# 2000    the set-group-ID-on-execution bit\n# 1000    the sticky bit\n# 0400    Allow read by owner.\n# 0200    Allow write by owner.\n# 0100    For files, allow execution by owner.  For directories, allow the\n#         owner to search in the directory.\n# 0040    Allow read by group members.\n# 0020    Allow write by group members.\n# 0010    For files, allow execution by group members.  For directories, allow\n#         group members to search in the directory.\n# 0004    Allow read by others.\n# 0002    Allow write by others.\n# 0001    For files, allow execution by others.  For directories allow others\n#         to search in the directory.\n#\n# On Solaris 11 (from man chmod):\n#\n# 20#0    Set group ID on execution if # is 7, 5, 3, or 1.\n#         Enable mandatory locking if # is 6, 4, 2, or 0.\n#         ...\n#         For directories, the set-gid bit can\n#         only be set or cleared by using symbolic mode.\n\n# From https://www.gnu.org/software/coreutils/manual/html_node/Symbolic-Modes.html#Symbolic-Modes\n# Users\n# u  the user who owns the file;\n# g  other users who are in the file's group;\n# o  all other users;\n# a  all users; the same as 'ugo'.\n#\n# Operations\n# + to add the permissions to whatever permissions the users already have for the file;\n# - to remove the permissions from whatever permissions the users already have for the file;\n# = to make the permissions the only permissions that the users have for the file.\n#\n# Permissions\n# r the permission the users have to read the file;\n# w the permission the users have to write to the file;\n# x the permission the users have to execute the file, or search it if it is a directory.\n# s the meaning depends on which user (uga) the permission is associated with:\n#     to set set-user-id-on-execution, use 'u' in the users part of the symbolic mode and 's' in the permissions part.\n#     to set set-group-id-on-execution, use 'g' in the users part of the symbolic mode and 's' in the permissions part.\n#     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.\n# 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.\n# X execute/search permission is affected only if the file is a directory or already had execute permission.\n#\n# Note we do not currently support the Solaris (l) permission:\n# l mandatory file and record locking refers to a file's ability to have its reading or writing\n#     permissions locked while a program is accessing that file.\n#\n  agents.each do |agent|\n    is_solaris = agent['platform'].include?('solaris')\n\n    on(agent, puppet('resource user symuser ensure=present'))\n    on(agent, puppet('resource group symgroup ensure=present'))\n    base_dir_create = agent.tmpdir('symbolic-modes-create_test')\n    base_dir_modify = agent.tmpdir('symbolic-modes-modify_test')\n\n    teardown do\n      on(agent, puppet('resource user symuser ensure=absent'))\n      on(agent, puppet('resource group symgroup ensure=absent'))\n      on(agent, \"rm -rf '#{base_dir_create}' '#{base_dir_modify}'\")\n    end\n\n    create_test = CreateTest.new(self, agent, base_dir_create)\n    create_test.symlink_file('u=r', 00444)\n    create_test.symlink_file('u=w', 00244)\n    create_test.symlink_file('u=x', 00144)\n    create_test.symlink_file('u=rw', 00644)\n    create_test.symlink_file('u=rwx', 00744)\n    create_test.symlink_file('u=rwxt', 01744)\n    create_test.symlink_file('u=rwxs', 04744)\n    create_test.symlink_file('u=rwxts', 05744)\n\n    create_test.symlink_file('ug=r', 00444)\n    create_test.symlink_file('ug=rw', 00664)\n    create_test.symlink_file('ug=rwx', 00774)\n    create_test.symlink_file('ug=rwxt', 01774)\n    create_test.symlink_file('ug=rwxs', 06774)\n    create_test.symlink_file('ug=rwxts', 07774)\n\n    create_test.symlink_file('ugo=r', 00444)\n    create_test.symlink_file('ugo=rw', 00666)\n    create_test.symlink_file('ugo=rwx', 00777)\n    create_test.symlink_file('ugo=rwxt', 01777)\n    #create_test.symlink_file('ugo=rwxs', 06777)  ## BUG, puppet creates 07777\n    create_test.symlink_file('ugo=rwxts', 07777)\n\n    create_test.symlink_file('u=rwx,go=rx', 00755)\n    create_test.symlink_file('u=rwx,g=rx,o=r', 00754)\n    create_test.symlink_file('u=rwx,g=rx,o=', 00750)\n    create_test.symlink_file('a=rwx', 00777)\n\n    create_test.symlink_file('u+r', 00644)\n    create_test.symlink_file('u+w', 00644)\n    create_test.symlink_file('u+x', 00744)\n    create_test.symlink_directory('u=r', 00455)\n    create_test.symlink_directory('u=w', 00255)\n    create_test.symlink_directory('u=x', 00155)\n    create_test.symlink_directory('u=rw', 00655)\n    create_test.symlink_directory('u=rwx', 00755)\n    create_test.symlink_directory('u=rwxt', 01755)\n    create_test.symlink_directory('u=rwxs', 04755)\n    create_test.symlink_directory('u=rwxts', 05755)\n\n    create_test.symlink_directory('ug=r', 00445)\n    create_test.symlink_directory('ug=rw', 00665)\n    create_test.symlink_directory('ug=rwx', 00775)\n    create_test.symlink_directory('ug=rwxt', 01775)\n    create_test.symlink_directory('ug=rwxs', 06775)\n    create_test.symlink_directory('ug=rwxts', 07775)\n\n    create_test.symlink_directory('ugo=r', 00444)\n    create_test.symlink_directory('ugo=rw', 00666)\n    create_test.symlink_directory('ugo=rwx', 00777)\n    create_test.symlink_directory('ugo=rwxt', 01777)\n    #create_test.symlink_directory('ugo=rwxs', 06777)  ## BUG, puppet creates 07777\n    create_test.symlink_directory('ugo=rwxts', 07777)\n\n    create_test.symlink_directory('u=rwx,go=rx', 00755)\n    create_test.symlink_directory('u=rwx,g=rx,o=r', 00754)\n    create_test.symlink_directory('u=rwx,g=rx,o=', 00750)\n    create_test.symlink_directory('a=rwx', 00777)\n\n    create_test.symlink_directory('u+r', 00755)\n    create_test.symlink_directory('u+w', 00755)\n    create_test.symlink_directory('u+x', 00755)\n    create_test.puppet_apply()\n    create_test.puppet_reapply()\n\n    modify_test = ModifyTest.new(self, agent, base_dir_modify)\n    modify_test.symlink_file('u+r', 00200, 00600)\n    modify_test.symlink_file('u+r', 00600, 00600)\n    modify_test.symlink_file('u+w', 00500, 00700)\n    modify_test.symlink_file('u+w', 00400, 00600)\n    modify_test.symlink_file('u+x', 00700, 00700)\n    modify_test.symlink_file('u+x', 00600, 00700)\n    modify_test.symlink_file('u+X', 00100, 00100)\n    modify_test.symlink_file('u+X', 00200, 00200)\n    modify_test.symlink_file('u+X', 00410, 00510)\n    modify_test.symlink_file('a+X', 00600, 00600)\n    modify_test.symlink_file('a+X', 00700, 00711)\n\n    modify_test.symlink_file('u+s', 00744, 04744)\n    modify_test.symlink_file('g+s', 00744, 02744)\n    modify_test.symlink_file('u+t', 00744, 01744)\n\n    modify_test.symlink_file('u-r', 00200, 00200)\n    modify_test.symlink_file('u-r', 00600, 00200)\n    modify_test.symlink_file('u-w', 00500, 00500)\n    modify_test.symlink_file('u-w', 00600, 00400)\n    modify_test.symlink_file('u-x', 00700, 00600)\n    modify_test.symlink_file('u-x', 00600, 00600)\n\n    modify_test.symlink_file('u-s', 04744, 00744)\n    modify_test.symlink_file('g-s', 02744, 00744)\n    modify_test.symlink_file('u-t', 01744, 00744)\n\n    modify_test.symlink_directory('u+r', 00200, 00600)\n    modify_test.symlink_directory('u+r', 00600, 00600)\n    modify_test.symlink_directory('u+w', 00500, 00700)\n    modify_test.symlink_directory('u+w', 00400, 00600)\n    modify_test.symlink_directory('u+x', 00700, 00700)\n    modify_test.symlink_directory('u+x', 00600, 00700)\n    modify_test.symlink_directory('u+X', 00100, 00100)\n    modify_test.symlink_directory('u+X', 00200, 00300)\n    modify_test.symlink_directory('u+X', 00410, 00510)\n    modify_test.symlink_directory('a+X', 00600, 00711)\n    modify_test.symlink_directory('a+X', 00700, 00711)\n\n    modify_test.symlink_directory('u+s', 00744, 04744)\n    modify_test.symlink_directory('g+s', 00744, 02744)\n    modify_test.symlink_directory('u+t', 00744, 01744)\n\n    modify_test.symlink_directory('u-r', 00200, 00200)\n    modify_test.symlink_directory('u-r', 00600, 00200)\n    modify_test.symlink_directory('u-w', 00500, 00500)\n    modify_test.symlink_directory('u-w', 00600, 00400)\n    modify_test.symlink_directory('u-x', 00700, 00600)\n    modify_test.symlink_directory('u-x', 00600, 00600)\n\n    modify_test.symlink_directory('u-s', 04744, 00744)\n    # using chmod 2744 on a directory to set the start_mode fails on Solaris\n    modify_test.symlink_directory('g-s', 02744, 00744) unless is_solaris\n    modify_test.symlink_directory('u-t', 01744, 00744)\n    modify_test.create_starting_state\n    modify_test.puppet_apply\n    modify_test.puppet_reapply\n\n    # these raise\n    # test.assert_raises('')\n    # test.assert_raises(' ')\n    # test.assert_raises('u=X')\n    # test.assert_raises('u-X')\n    # test.assert_raises('+l')\n    # test.assert_raises('-l')\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/ticket_6448_file_with_utf8_source.rb",
    "content": "test_name 'Ensure a file resource can have a UTF-8 source attribute, content, and path when served via a module' do\n  tag 'audit:high',\n      'broken:images',\n      'audit:acceptance',\n      'server'\n\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  require 'puppet/acceptance/agent_fqdn_utils'\n  extend Puppet::Acceptance::AgentFqdnUtils\n\n  tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*'))\n  agent_tmp_dirs  = {}\n\n  agents.each do |agent|\n    agent_tmp_dirs[agent_to_fqdn(agent)] = agent.tmpdir(tmp_environment)\n  end\n\n  teardown do\n    # note - master teardown is registered by #mk_tmp_environment_with_teardown\n    step 'remove all test files on agents' do\n      agents.each do |agent|\n        on(agent, \"rm -r '#{agent_tmp_dirs[agent_to_fqdn(agent)]}'\", :accept_all_exit_codes => true)\n        on(agent, puppet('config print lastrunfile')) do |command_result|\n          agent.rm_rf(command_result.stdout)\n        end\n      end\n    end\n  end\n\n  step 'create unicode source file served via module on master' do\n    # 静 \\u9759 0xE9 0x9D 0x99 http://www.fileformat.info/info/unicode/char/9759/index.htm\n    # 的 \\u7684 0xE7 0x9A 0x84 http://www.fileformat.info/info/unicode/char/7684/index.htm\n    # ☃ \\2603 0xE2 0x98 0x83 http://www.fileformat.info/info/unicode/char/2603/index.htm\n    setup_module_on_master = <<-MASTER_MANIFEST\n      File {\n        ensure => directory,\n        mode => \"0755\",\n      }\n\n      file {\n        '#{environmentpath}/#{tmp_environment}/modules/utf8_file_module':;\n        '#{environmentpath}/#{tmp_environment}/modules/utf8_file_module/files':;\n      }\n\n      file { '#{environmentpath}/#{tmp_environment}/modules/utf8_file_module/files/\\u9759\\u7684':\n        ensure => file,\n        content => \"\\u2603\"\n      }\n    MASTER_MANIFEST\n    apply_manifest_on(master, setup_module_on_master, :expect_changes => true)\n  end\n\n  step 'create a site.pp on master containing a unicode file resource' do\n    site_pp_contents = <<-SITE_PP\n      \\$test_path = \\$facts['networking']['fqdn'] ? #{agent_tmp_dirs}\n      file { \"\\$test_path/\\uff72\\uff67\\u30d5\\u30eb\":\n        ensure => present,\n        source => \"puppet:///modules/utf8_file_module/\\u9759\\u7684\",\n      }\n    SITE_PP\n\n    create_site_pp = <<-CREATE_SITE_PP\n      file { \"#{environmentpath}/#{tmp_environment}/manifests/site.pp\":\n        ensure => file,\n        content => @(UTF8)\n          #{site_pp_contents}\n        | UTF8\n      }\n    CREATE_SITE_PP\n    apply_manifest_on(master, create_site_pp, :expect_changes => true)\n  end\n\n  step 'ensure agent can manage unicode file resource' do\n    # イ \\uff72 0xEF 0xBD 0xB2 http://www.fileformat.info/info/unicode/char/ff72/index.htm\n    # ァ \\uff67 0xEF 0xBD 0xA7 http://www.fileformat.info/info/unicode/char/ff67/index.htm\n    # フ \\u30d5 0xE3 0x83 0x95 http://www.fileformat.info/info/unicode/char/30d5/index.htm\n    # ル \\u30eb 0xE3 0x83 0xAB http://www.fileformat.info/info/unicode/char/30eb/index.htm\n\n    with_puppet_running_on(master, {}) do\n      agents.each do |agent|\n        on(agent, puppet(\"agent -t --environment '#{tmp_environment}'\"), :acceptable_exit_codes => 2)\n\n        on(agent, \"cat '#{agent_tmp_dirs[agent_to_fqdn(agent)]}/\\uff72\\uff67\\u30d5\\u30eb'\") do |result|\n          assert_match(\"\\u2603\", result.stdout, \"managed UTF-8 file contents '#{result.stdout}' did not match expected value '\\u2603'\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/file/ticket_7680-follow-symlinks.rb",
    "content": "test_name \"#7680: 'links => follow' should use the file source content\"\n\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\nagents.each do |agent|\n\n  step \"Create file content\"\n  real_source = agent.tmpfile('follow_links_source')\n  dest        = agent.tmpfile('follow_links_dest')\n  symlink     = agent.tmpfile('follow_links_symlink')\n\n  on agent, \"echo 'This is the real content' > #{real_source}\"\n  if agent['platform'].include?('windows')\n    # cygwin ln doesn't behave properly, fallback to mklink,\n    # but that requires backslashes, that need to be escaped,\n    # and the link cannot exist prior.\n    on agent, \"rm -f #{symlink}\"\n    on agent, \"cmd /c mklink #{symlink.gsub('/', '\\\\\\\\\\\\\\\\')} #{real_source.gsub('/', '\\\\\\\\\\\\\\\\')}\"\n  else\n    on agent, \"ln -sf #{real_source} #{symlink}\"\n  end\n\n  manifest = <<-MANIFEST\n    file { '#{dest}':\n      ensure => file,\n      source => '#{symlink}',\n      links  => follow,\n    }\n  MANIFEST\n  apply_manifest_on(agent, manifest, :trace => true)\n\n  on(agent, \"cat #{dest}\") do |result|\n    assert_match(/This is the real content/, result.stdout)\n  end\n\n  step \"Cleanup\"\n  [real_source, dest, symlink].each do |file|\n    on agent, \"rm -f '#{file}'\"\n  end\nend\n\n\n"
  },
  {
    "path": "acceptance/tests/resource/file/ticket_8740_should_not_enumerate_root_directory.rb",
    "content": "test_name \"#8740: should not enumerate root directory\"\n\nconfine :except, :platform => 'windows'\nconfine :except, :platform => 'osx'\n\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:acceptance'\n\ntarget = \"/test-socket-#{$$}\"\n\nrequire 'puppet/acceptance/common_utils'\nextend Puppet::Acceptance::CommandUtils\n\nagents.each do |agent|\n  step \"clean up the system before we begin\"\n  on(agent, \"rm -f #{target}\")\n\n  step \"create UNIX domain socket\"\n  on(agent, \"#{ruby_command(agent)} -e \\\"require 'socket'; UNIXServer::new('#{target}').close\\\"\")\n\n  step \"query for all files, which should return nothing\"\n  on(agent, puppet_resource('file'), :acceptable_exit_codes => [1]) do |result|\n    assert_match(%r{Listing all file instances is not supported.  Please specify a file or directory, e.g. puppet resource file /etc}, result.stderr)\n  end\n\n  [\"/\", \"/etc\"].each do |file|\n    step \"query '#{file}' directory, which should return single entry\"\n    on(agent, puppet_resource('file', file)) do |result|\n      files = result.stdout.scan(/^file \\{ '([^']+)'/).flatten\n\n      assert_equal(1, files.size, \"puppet returned multiple files: #{files.join(', ')}\")\n      assert_match(file, files[0], \"puppet did not return file\")\n    end\n  end\n\n  step \"query file that does not exist, which should report the file is absent\"\n  on(agent, puppet_resource('file', '/this/does/notexist')) do |result|\n    assert_match(/ensure\\s+=>\\s+'absent'/, result.stdout)\n  end\n\n  step \"remove UNIX domain socket\"\n  on(agent, \"rm -f #{target}\")\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_create.rb",
    "content": "test_name \"should create a group\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure the group does not exist\"\n  agent.group_absent(name)\n\n  step \"create the group\"\n  on agent, puppet_resource('group', name, 'ensure=present')\n\n  step \"verify the group exists\"\n  agent.group_get(name)\n\n  step \"delete the group\"\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_destroy.rb",
    "content": "test_name \"should destroy a group\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure the group is present\"\n  agent.group_present(name)\n\n  step \"delete the group\"\n  on agent, puppet_resource('group', name, 'ensure=absent')\n\n  step \"verify the group was deleted\"\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_manage_attributes_aix.rb",
    "content": "test_name \"should correctly manage the attributes property for the Group (AIX only)\" do\n  confine :to, :platform => /aix/\n  \n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n  \n  require 'puppet/acceptance/aix_util'\n  extend Puppet::Acceptance::AixUtil\n\n  initial_attributes = {\n    'admin' => true\n  }\n  changed_attributes = {\n    'admin' => false\n  }\n\n  run_attribute_management_tests('group', :gid, initial_attributes, changed_attributes)\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_manage_members.rb",
    "content": "test_name \"should correctly manage the members property for the Group resource\" do\n  # These are the only platforms whose group providers manage the members\n  # property\n  confine :to, :platform => /windows|osx|aix|^el-|fedora/\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::BeakerUtils\n\n  def random_name\n    \"pl#{rand(999999).to_i}\"\n  end\n\n  def group_manifest(user, params)\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\ngroup { '#{user}':\n  #{params_str}\n}\nMANIFEST\n  end\n\n  def members_of(host, group)\n    case host['platform']\n    when /windows/\n      # More verbose than 'net localgroup <group>', but more programmatic\n      # because it does not require us to parse stdout\n      get_group_members = <<-PS1\n# Adapted from https://github.com/RamblingCookieMonster/PowerShell/blob/master/Get-ADGroupMembers.ps1\nfunction Get-Members([string] $group) {\n  $ErrorActionPreference = 'Stop'\n\n  Add-Type -AssemblyName 'System.DirectoryServices.AccountManagement' -ErrorAction Stop\n  $contextType = [System.DirectoryServices.AccountManagement.ContextType]::Machine\n  $groupObject = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity(\n    $contextType,\n    $group\n  )\n\n  if (-Not $groupObject) {\n    throw \"Could not find the group '$group'!\"\n  }\n\n  $members = $groupObject.GetMembers($false) | ForEach-Object { \"'$($_.Name)'\" }\n  write-output \"[$([string]::join(',', $members))]\"\n}\n\nGet-Members #{group}\nPS1\n      Kernel.eval(\n        execute_powershell_script_on(host, get_group_members).stdout.chomp \n      )\n    else\n      # This reads the group members from the /etc/group file\n      get_group_members = <<-RUBY\nrequire 'etc'\n\ngroup_struct = nil\nEtc.group do |g|\n  if g.name == '#{group}'\n    group_struct = g\n    break\n  end\nend\n\nunless group_struct\n  raise \"Could not find the group '#{group}'!\"\nend\n\nputs(group_struct.mem.to_s)\nRUBY\n\n      script_path = \"#{host.tmpfile(\"get_group_members\")}.rb\"\n      create_remote_file(host, script_path, get_group_members)\n\n      # The setup step should have already set :privatebindir on the\n      # host. We only include the default here to make this routine\n      # work for local testing, which sometimes skips the setup step.\n      privatebindir = host.has_key?(:privatebindir) ? host[:privatebindir] : '/opt/puppetlabs/puppet/bin'\n\n      result = on(host, \"#{privatebindir}/ruby #{script_path}\")\n      Kernel.eval(result.stdout.chomp)\n    end\n  end\n\n  agents.each do |agent|\n    users = 6.times.collect { random_name }\n    users.each { |user| agent.user_absent(user) }\n\n    group = random_name\n    agent.group_absent(group)\n    teardown { agent.group_absent(group) }\n\n    step 'Creating the Users' do\n      users.each do |user|\n        agent.user_present(user)\n        teardown { agent.user_absent(user) }\n      end\n    end\n\n    group_members = [users[0], users[1]]\n\n    step 'Ensure that the group is created with the specified members' do\n      manifest = group_manifest(group, members: group_members)\n      apply_manifest_on(agent, manifest)\n      assert_matching_arrays(group_members, members_of(agent, group), \"The group was not successfully created with the specified members!\")\n    end\n\n    step \"Verify that Puppet errors when one of the members does not exist\" do\n      manifest = group_manifest(group, members: ['nonexistent_member'])\n      apply_manifest_on(agent, manifest, :acceptable_exit_codes => [0, 1]) do |result|\n        assert_match(/Error:.*#{group}/, result.stderr, \"Puppet fails to report an error when one of the members in the members property does not exist\")\n      end\n    end\n\n    step \"Verify that Puppet noops when the group's members are already set after creating the group\" do\n      manifest = group_manifest(group, members: group_members)\n      apply_manifest_on(agent, manifest, catch_changes: true)\n      assert_matching_arrays(group_members, members_of(agent, group), \"The group's members somehow changed despite Puppet reporting a noop\")\n    end\n\n    step \"Verify that Puppet enforces minimum user membership when auth_membership == false\" do\n      new_members = [users[2], users[4]]\n\n      manifest = group_manifest(group, members: new_members, auth_membership: false)\n      apply_manifest_on(agent, manifest)\n\n      group_members += new_members\n      assert_matching_arrays(group_members, members_of(agent, group), \"Puppet fails to enforce minimum user membership when auth_membership == false\")\n    end\n\n    step \"Verify that Puppet noops when the group's members are already set after enforcing minimum user membership\" do\n      manifest = group_manifest(group, members: group_members)\n      apply_manifest_on(agent, manifest, catch_changes: true)\n      assert_matching_arrays(group_members, members_of(agent, group), \"The group's members somehow changed despite Puppet reporting a noop\")\n    end\n\n    # Run some special, platform-specific tests. If these get too large, then\n    # we should consider placing them in a separate file.\n    case agent['platform']\n    when /windows/\n      domain = on(agent, 'hostname').stdout.chomp.upcase\n\n      step \"(Windows) Verify that Puppet prints each group member as DOMAIN\\\\<user>\" do\n        new_members = [users[3]]\n\n        manifest = group_manifest(group, members: new_members, auth_membership: false)\n        apply_manifest_on(agent, manifest) do |result|\n          group_members += new_members\n\n          stdout = result.stdout.chomp\n\n          group_members.each do |user|\n            assert_match(/#{domain}\\\\#{user}/, stdout, \"Puppet fails to print the group member #{user} as #{domain}\\\\#{user}\")\n          end\n        end\n      end\n\n      step \"(Windows) Verify that `puppet resource` prints each group member as DOMAIN\\\\<user>\" do\n        on(agent, puppet('resource', 'group', group)) do |result|\n          stdout = result.stdout.chomp\n\n          group_members.each do |user|\n            assert_match(/#{domain}\\\\#{user}/, stdout, \"`puppet resource` fails to print the group member #{user} as #{domain}\\\\#{user}\")\n          end\n        end\n      end\n    when /aix/\n      step \"(AIX) Verify that Puppet accepts a comma-separated list of members for backwards compatibility\" do\n        new_members = [users[3], users[5]]\n\n        manifest = group_manifest(group, members: new_members.join(','), auth_membership: false)\n        apply_manifest_on(agent, manifest)\n\n        group_members += new_members\n        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\")\n      end\n    end\n\n    step \"Verify that Puppet enforces inclusive user membership when auth_membership == true\" do\n      group_members = [users[0]]\n\n      manifest = group_manifest(group, members: group_members, auth_membership: true)\n      apply_manifest_on(agent, manifest)\n      assert_matching_arrays(group_members, members_of(agent, group), \"Puppet fails to enforce inclusive group membership when auth_membership == true\")\n    end\n\n    step \"Verify that Puppet noops when the group's members are already set after enforcing inclusive user membership\" do\n      manifest = group_manifest(group, members: group_members)\n      apply_manifest_on(agent, manifest, catch_changes: true)\n      assert_matching_arrays(group_members, members_of(agent, group), \"The group's members somehow changed despite Puppet reporting a noop\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_modify_gid.rb",
    "content": "test_name \"should modify gid of existing group\"\nconfine :except, :platform => 'windows'\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nname = \"pl#{rand(999999).to_i}\"\ngid1  = (rand(989999).to_i + 10000)\ngid2  = (rand(989999).to_i + 10000)\n\nagents.each do |agent|\n  # AIX group provider returns quoted gids\n  step \"ensure that the group exists with gid #{gid1}\"\n  on(agent, puppet_resource('group', name, 'ensure=present', \"gid=#{gid1}\")) do |result|\n    fail_test \"missing gid notice\" unless result.stdout =~ /gid +=> +'?#{gid1}'?/\n  end\n\n  step \"ensure that we can modify the GID of the group to #{gid2}\"\n  on(agent, puppet_resource('group', name, 'ensure=present', \"gid=#{gid2}\")) do |result|\n    fail_test \"missing gid notice\" unless result.stdout =~ /gid +=> +'?#{gid2}'?/\n  end\n\n  step \"verify that the GID changed\"\n  gid_output = agent.group_gid(name).to_i\n  fail_test \"gid #{gid_output} does not match expected value of: #{gid2}\" unless gid_output == gid2\n\n  step \"clean up the system after the test run\"\n  on(agent, puppet_resource('group', name, 'ensure=absent'))\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_not_create_existing.rb",
    "content": "test_name \"group should not create existing group\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nname = \"gr#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure the group exists on the target node\"\n  agent.group_present(name)\n\n  step \"verify that we don't try and create the existing group\"\n  on(agent, puppet_resource('group', name, 'ensure=present')) do |result|\n    fail_test \"looks like we created the group\" if\n      result.stdout.include? \"/Group[#{name}]/ensure: created\"\n  end\n\n  step \"clean up the system after the test run\"\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_not_destroy_unexisting.rb",
    "content": "test_name \"should not destroy a group that doesn't exist\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nname = \"test-group-#{Time.new.to_i}\"\n\nstep \"verify the group does not already exist\"\nagents.each do |agent|\n  agent.group_absent(name)\nend\n\nstep \"verify that we don't remove the group when it doesn't exist\"\non(agents, puppet_resource('group', name, 'ensure=absent')) do |result|\n  fail_test \"it looks like we tried to remove the group\" if\n    result.stdout.include? \"/Group[#{name}]/ensure: removed\"\nend\n\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_query.rb",
    "content": "test_name \"test that we can query and find a group that exists.\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr'\n\n  step \"ensure that our test group exists\"\n  agent.group_present(name)\n\n  step \"query for the resource and verify it was found\"\n  on(agent, puppet_resource('group', name)) do |result|\n    fail_test \"didn't find the group #{name}\" unless result.stdout.include? 'present'\n  end\n\n  step \"clean up the group we added\"\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/group/should_query_all.rb",
    "content": "test_name \"should query all groups\"\n\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:integration' # Does not modify system running test\n\nagents.each do |agent|\n  skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr'\n  step \"query natively\"\n\n  groups = agent.group_list\n\n  fail_test(\"No groups found\") unless groups\n\n  step \"query with puppet\"\n  on(agent, puppet_resource('group')) do |result|\n    result.stdout.each_line do |line|\n      name = ( line.match(/^group \\{ '([^']+)'/) or next )[1]\n\n      unless groups.delete(name)\n        fail_test \"group #{name} found by puppet, not natively\"\n      end\n    end\n  end\n\n  if groups.length > 0 then\n    fail_test \"#{groups.length} groups found natively, not puppet: #{groups.join(', ')}\"\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/common_package_name_in_different_providers.rb",
    "content": "test_name \"ticket 1073: common package name in two different providers should be allowed\" do\n\n  confine :to, {:platform => /(?:centos|el-|fedora)/}, agents\n  # Skipping tests if facter finds this is an ec2 host, PUP-7774\n  agents.each do |agent|\n    skip_test('Skipping EC2 Hosts') if fact_on(agent, 'ec2_metadata')\n  end\n\n  # Upgrade the AlmaLinux release package for newer keys until our image is updated (RE-16096)\n  agents.each do |agent|\n    on(agent, 'dnf -y upgrade almalinux-release') if fact_on(agent, 'os.name') == 'AlmaLinux'\n  end\n\n  tag 'audit:high',\n      'audit:acceptance' # Uses a provider that depends on AIO packaging\n\n  require 'puppet/acceptance/rpm_util'\n  extend Puppet::Acceptance::RpmUtils\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::CommandUtils\n\n  rpm_options = {:pkg => 'guid', :version => '1.0'}\n\n  teardown do\n    step \"cleanup\"\n    agents.each do |agent|\n      clean_rpm agent, rpm_options\n    end\n  end\n\n  step \"Verify gem and ruby-devel on fedora-22 and above if not aio\" do\n    if @options[:type] != 'aio' then\n      agents.each do |agent|\n        if agent[:platform] =~ /fedora-2[2-9]/ then\n          unless check_for_package agent, 'rubygems'\n            install_package agent, 'rubygems'\n          end\n          unless check_for_package agent, 'ruby-devel'\n            install_package agent, 'ruby-devel'\n          end\n        end\n      end\n    end\n  end\n\n  def gem_provider\n    if @options[:type] == 'aio'\n      'puppet_gem'\n    else\n      'gem'\n    end\n  end\n\n  def verify_state(hosts, pkg, state, match)\n    hosts.each do |agent|\n      cmd = rpm_provider(agent)\n      # Note yum lists packages as <name>.<arch>\n      on(agent, \"#{cmd} list installed\") do |result|\n        method(match).call(/^#{pkg}\\./, result.stdout)\n      end\n\n      on(agent, \"#{gem_command(agent, @options[:type])} list --local\") do |result|\n        method(match).call(/^#{pkg} /, result.stdout)\n      end\n    end\n  end\n\n  def verify_present(hosts, pkg)\n    verify_state(hosts, pkg, '(?!purged|absent)[^\\']+', :assert_match)\n  end\n\n  def verify_absent(hosts, pkg)\n    verify_state(hosts, pkg, '(?:purged|absent)', :refute_match)\n  end\n\n  # Setup repo and package\n  agents.each do |agent|\n    clean_rpm agent, rpm_options\n    setup_rpm agent, rpm_options\n    send_rpm agent, rpm_options\n  end\n\n  verify_absent agents, 'guid'\n\n  # Test error trying to install duplicate packages\n  collide1_manifest = <<-MANIFEST\n    package {'guid': ensure => installed}\n    package {'other-guid': name => 'guid', ensure => present}\n  MANIFEST\n\n  apply_manifest_on(agents, collide1_manifest, :acceptable_exit_codes => [1]) do |result|\n    assert_match(/Error while evaluating a Resource Statement, Cannot alias Package\\[other-guid\\] to \\[nil, \"guid\", nil\\]/, \"#{result.host}: #{result.stderr}\")\n  end\n\n  verify_absent agents, 'guid'\n\n  gem_source = if ENV['GEM_SOURCE'] then \"source => '#{ENV['GEM_SOURCE']}',\" else '' end\n  collide2_manifest = <<-MANIFEST\n    package {'guid': ensure => '0.1.0', provider => #{gem_provider}, #{gem_source}}\n    package {'other-guid': name => 'guid', ensure => installed, provider => #{gem_provider}, #{gem_source}}\n  MANIFEST\n\n  apply_manifest_on(agents, collide2_manifest, :acceptable_exit_codes => [1]) do |result|\n    assert_match(/Error while evaluating a Resource Statement, Cannot alias Package\\[other-guid\\] to \\[nil, \"guid\", \"#{gem_provider}\"\\]/, \"#{result.host}: #{result.stderr}\")\n  end\n\n  verify_absent agents, 'guid'\n\n  # Test successful parallel installation\n  install_manifest = <<-MANIFEST\n    package {'guid': ensure => installed}\n\n    package {'gem-guid':\n      provider => #{gem_provider},\n      name => 'guid',\n      ensure => installed,\n      #{gem_source}\n    }\n  MANIFEST\n\n  apply_manifest_on(agents, install_manifest) do |result|\n    assert_match('Package[guid]/ensure: created', \"#{result.host}: #{result.stdout}\")\n    assert_match('Package[gem-guid]/ensure: created', \"#{result.host}: #{result.stdout}\")\n  end\n\n  verify_present agents, 'guid'\n\n  # Test removal\n  remove_manifest = <<-MANIFEST\n    package {'gem-guid':\n      provider => #{gem_provider},\n      name => 'guid',\n      ensure => absent,\n      #{gem_source}\n    }\n\n    package {'guid': ensure => absent}\n  MANIFEST\n\n  apply_manifest_on(agents, remove_manifest) do |result|\n    assert_match('Package[guid]/ensure: removed', \"#{result.host}: #{result.stdout}\")\n    assert_match('Package[gem-guid]/ensure: removed', \"#{result.host}: #{result.stdout}\")\n  end\n\n  verify_absent agents, 'guid'\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/does_not_exist.rb",
    "content": "# Redmine (#22529)\ntest_name \"Puppet returns only resource package declaration when querying an uninstalled package\" do\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done at the integration (or unit) layer though\n                         # actual changing of resources could irreparably damage a\n                         # host running this, or require special permissions.\n\n  agents.each do |agent|\n\n    step \"test puppet resource package\" do\n      on(agent, puppet('resource', 'package', 'not-installed-on-this-host')) do |result|\n        assert_match(/package.*not-installed-on-this-host.*\\n.*ensure.*(?:absent|purged).*\\n.*provider/, result.stdout)\n      end\n    end\n\n  end\n\n  # Until #3707 is fixed and purged rpm/yum packages no longer give spurious creation notices\n  # Also skipping solaris, windows whose providers do not have purgeable implemented.\n  confine_block(:to, :platform => /debian|ubuntu/) do\n    agents.each do |agent|\n      step \"test puppet apply\" do\n        on(agent, puppet('apply', '-e', %Q|\"package {'not-installed-on-this-host': ensure => purged }\"|)) do |result|\n          refute_match(/warning/i, result.stdout)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/basic_tests.rb",
    "content": "test_name \"Package:IPS basic tests\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: clean slate\"\n  clean agent\n\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  set_publisher agent\n\n  step \"IPS: basic ensure we are clean\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>absent}')\n  on(agent, \"pkg list -v mypkg\", :acceptable_exit_codes => [1]) do\n    refute_match( /mypkg@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: basic - it should create\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: check it was created\"\n  on(agent, puppet(\"resource package mypkg\")) do\n    assert_match( /ensure\\s+=> '0\\.0\\.1[,:]?.*'/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: do not upgrade until latest is mentioned\"\n  send_pkg agent,:pkg => 'mypkg@0.0.2'\n  apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do\n    refute_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: verify it was not upgraded\"\n  on(agent, puppet(\"resource package mypkg\")) do\n    assert_match( /ensure\\s+=> '0\\.0\\.1[,:]?.*'/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ask to be latest\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>latest}')\n\n  step \"IPS: ensure it was upgraded\"\n  on(agent, puppet(\"resource package mypkg\")) do\n    assert_match( /ensure\\s+=> '0\\.0\\.2[,:]?.*'/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: when there are more than one option, choose latest.\"\n  send_pkg agent,:pkg => 'mypkg@0.0.3'\n  send_pkg agent,:pkg => 'mypkg@0.0.4'\n  apply_manifest_on(agent, 'package {mypkg : ensure=>latest}')\n  on(agent, puppet(\"resource package mypkg\")) do\n    assert_match( /ensure\\s+=> '0\\.0\\.4[,:]?.*'/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ensure removed.\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>absent}')\n  on(agent, \"pkg list -v mypkg\", :acceptable_exit_codes => [1]) do\n    refute_match( /mypkg@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_be_holdable.rb",
    "content": "test_name \"Package:IPS versionable\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent, :pkg => 'mypkg2'\n    clean agent, :pkg => 'mypkg'\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  setup_fakeroot2 agent\n  send_pkg2 agent, :pkg => 'mypkg2@0.0.1'\n  set_publisher agent\n  step \"IPS: basic - it should create a specific version and install dependent package\"\n  apply_manifest_on(agent, 'package {mypkg2 : ensure=>\"0.0.1\"}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg2\" do\n    assert_match( /mypkg2@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: it should upgrade current and dependent package\"\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.2'\n  setup_fakeroot2 agent\n  send_pkg2 agent, :pkg => 'mypkg2@0.0.2', :pkgdep => 'mypkg@0.0.2'\n  apply_manifest_on(agent, 'package {mypkg2 : ensure=>\"0.0.2\"}') do\n    assert_match( /changed/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.2/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg2\" do\n    assert_match( /mypkg2@0.0.2/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: it should not upgrade current and dependent package if dependent package is hold\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"present\", mark=>\"hold\", provider=>\"pkg\"}') do\n    assert_match( //, result.stdout, \"err: #{agent}\")\n  end\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.3'\n  setup_fakeroot2 agent\n  send_pkg2 agent, :pkg => 'mypkg2@0.0.3', :pkgdep => 'mypkg@0.0.3'\n  apply_manifest_on(agent, 'package {mypkg2 : ensure=>\"0.0.2\"}') do\n    refute_match( /changed/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.2/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg2\" do\n    assert_match( /mypkg2@0.0.2/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: it should upgrade if hold was released.\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.3\", provider=>\"pkg\"}') do\n    assert_match( //, result.stdout, \"err: #{agent}\")\n  end\n  apply_manifest_on(agent, 'package {mypkg2 : ensure=>\"0.0.3\"}') do\n    assert_match( /changed/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.3/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg2\" do\n    assert_match( /mypkg2@0.0.3/, result.stdout, \"err: #{agent}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_be_idempotent.rb",
    "content": "test_name \"Package:IPS idempotency\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  set_publisher agent\n\n  step \"IPS: it should create\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: should be idempotent (present)\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do\n    refute_match( /created/, result.stdout, \"err: #{agent}\")\n    refute_match( /changed/, result.stdout, \"err: #{agent}\")\n  end\n  send_pkg agent, :pkg => 'mypkg@0.0.2'\n\n  step \"IPS: ask for latest version\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>latest}')\n\n  step \"IPS: ask for latest version again: should be idempotent (latest)\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') do\n    refute_match( /created/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ask for specific version\"\n  send_pkg agent,:pkg => 'mypkg@0.0.3'\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.3\"}') do\n    assert_match( /changed/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ask for specific version again: should be idempotent (version)\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.3\"}') do\n    refute_match( /created/, result.stdout, \"err: #{agent}\")\n    refute_match( /changed/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ensure removed.\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>absent}')\n  on(agent, \"pkg list -v mypkg\", :acceptable_exit_codes => [1]) do\n    refute_match( /mypkg/, result.stdout, \"err: #{agent}\")\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_be_updatable.rb",
    "content": "test_name \"Package:IPS test for updatable (update, latest)\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  set_publisher agent\n\n  step \"IPS: basic - it should create\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ask to be latest\"\n  send_pkg agent, :pkg => 'mypkg@0.0.2'\n  apply_manifest_on(agent, 'package {mypkg : ensure=>latest}')\n\n  step \"IPS: ensure it was upgraded\"\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.2/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: when there are more than one option, choose latest.\"\n  send_pkg agent,:pkg => 'mypkg@0.0.3'\n  send_pkg agent,:pkg => 'mypkg@0.0.4'\n  apply_manifest_on(agent, 'package {mypkg : ensure=>latest}')\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.4/, result.stdout, \"err: #{agent}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_be_updateable_and_unholdable_at_same_time.rb",
    "content": "test_name \"Package:IPS test for updatable holded package\" do\n  confine :to, :platform => 'solaris-11'\n\n  tag 'audit:high'\n\n  require 'puppet/acceptance/solaris_util'\n  extend Puppet::Acceptance::IPSUtils\n\n  agents.each do |agent|\n    teardown do\n      clean agent\n    end\n\n    step \"IPS: setup\" do\n      setup agent\n      setup_fakeroot agent\n      send_pkg agent, :pkg => 'mypkg@0.0.1'\n      set_publisher agent\n    end\n\n    step \"IPS: it should create and hold in same manifest\" do\n      apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.1\", mark=>hold}') do |result|\n        assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n      end\n    end\n\n    step \"IPS: it should update and unhold in same manifest\" do\n      send_pkg agent, :pkg => 'mypkg@0.0.2'\n      apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.2\", mark=>\"none\"}')\n    end\n\n    step \"IPS: ensure it was upgraded\" do\n      on agent, \"pkg list -v mypkg\" do |result|\n        assert_match( /mypkg@0.0.2/, result.stdout, \"err: #{agent}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_be_versionable.rb",
    "content": "test_name \"Package:IPS versionable\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  send_pkg agent, :pkg => 'mypkg@0.0.2'\n  set_publisher agent\n  step \"IPS: basic - it should create a specific version\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.1\"}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list mypkg\" do\n    assert_match( /0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: it should upgrade if asked for next version\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.2\"}') do\n    assert_match( /ensure changed/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list mypkg\" do\n    refute_match( /0.0.1/, result.stdout, \"err: #{agent}\")\n    assert_match( /0.0.2/, result.stdout, \"err: #{agent}\")\n  end\n  step \"IPS: it should downpgrade if asked for previous version\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"0.0.1\"}') do\n    assert_match( /ensure changed/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list mypkg\" do\n    refute_match( /0.0.2/, result.stdout, \"err: #{agent}\")\n    assert_match( /0.0.1/, result.stdout, \"err: #{agent}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_create.rb",
    "content": "test_name \"Package:IPS basic tests\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: clean slate\"\n  clean agent\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  set_publisher agent\n  step \"IPS: basic - it should create\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n  step \"IPS: check it was created\"\n  on(agent, puppet(\"resource package mypkg\")) do\n    assert_match( /ensure\\s+=> '0\\.0\\.1[,:]?.*'/, result.stdout, \"err: #{agent}\")\n  end\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_query.rb",
    "content": "test_name \"Package:IPS query\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  set_publisher agent\n  step \"IPS: basic - it should create\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>\"present\"}') do\n    assert_match( /ensure: created/, result.stdout, \"err: #{agent}\")\n  end\n\n  on(agent, puppet(\"resource package mypkg\")) do\n    assert_match( /0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n\n  on(agent, puppet(\"resource package\")) do\n    assert_match( /0.0.1/, result.stdout, \"err: #{agent}\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/ips/should_remove.rb",
    "content": "test_name \"Package:IPS basic tests\"\nconfine :to, :platform => 'solaris-11'\n\ntag 'audit:medium',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nrequire 'puppet/acceptance/solaris_util'\nextend Puppet::Acceptance::IPSUtils\n\nteardown do\n  step \"cleanup\"\n  agents.each do |agent|\n    clean agent\n  end\nend\n\n\nagents.each do |agent|\n  step \"IPS: setup\"\n  setup agent\n  setup_fakeroot agent\n  send_pkg agent, :pkg => 'mypkg@0.0.1'\n  set_publisher agent\n  on agent, \"pkg install mypkg\"\n  on agent, \"pkg list -v mypkg\" do\n    assert_match( /mypkg@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n\n  step \"IPS: ensure removed.\"\n  apply_manifest_on(agent, 'package {mypkg : ensure=>absent}')\n\n  on(agent, \"pkg list -v mypkg\", :acceptable_exit_codes => [1]) do\n    refute_match( /mypkg@0.0.1/, result.stdout, \"err: #{agent}\")\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/windows.rb",
    "content": "test_name \"Windows Package Provider\" do\n  confine :to, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/windows_utils'\n  extend Puppet::Acceptance::WindowsUtils\n\n  def package_manifest(name, params, installer_source)\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\npackage { '#{name}':\n  source => '#{installer_source}',\n  #{params_str}\n}\nMANIFEST\n  end\n\n  mock_package = {\n    :name => \"MockPackage\"\n  }\n\n  agents.each do |agent|\n    tmpdir = agent.tmpdir(\"mock_installer\")\n    installer_location = create_mock_package(agent, tmpdir, mock_package)\n\n    step 'Verify that ensure = present installs the package' do\n      apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :present}, installer_location))\n      assert(package_installed?(agent, mock_package[:name]), 'Package succesfully installed')\n    end\n\n    step 'Verify that ensure = absent removes the package' do\n      apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :absent}, installer_location))\n      assert_equal(false, package_installed?(agent, mock_package[:name]), 'Package successfully Uninstalled')\n    end\n\n    tmpdir = agent.tmpdir(\"mock_installer\")\n    mock_package[:name] = \"MockPackageWithFile\"\n    mock_package[:install_commands] = 'System.IO.File.ReadAllLines(\"install.txt\");'\n    installer_location = create_mock_package(agent, tmpdir, mock_package)\n\n    # Since we didn't add the install.txt package the installation should fail with code 1004\n    step 'Verify that ensure = present fails when an installer fails with a non-zero exit code' do\n      apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :present}, installer_location)) do |result|\n        assert_match(/#{mock_package[:name]}/, result.stderr, 'Windows package provider did not fail when the package install failed')\n      end\n    end\n\n    step 'Verify that ensure = present installs a package that requires additional resources' do\n      create_remote_file(agent, \"#{tmpdir}/install.txt\", 'foobar')\n      apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :present}, installer_location))\n      assert(package_installed?(agent, mock_package[:name]), 'Package succesfully installed')\n    end\n\n    step 'Verify that ensure = absent removes the package that required additional resources' do\n      apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :absent}, installer_location))\n      assert_equal(false, package_installed?(agent, mock_package[:name]), 'Package successfully Uninstalled')\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/package/yum.rb",
    "content": "test_name \"test the yum package provider\" do\n\n  confine :to, {:platform => /(?:centos|el-|fedora)/}, agents\n  # Skipping tests if facter finds this is an ec2 host, PUP-7774\n  agents.each do |agent|\n    skip_test('Skipping EC2 Hosts') if fact_on(agent, 'ec2_metadata')\n  end\n\n  # Upgrade the AlmaLinux release package for newer keys until our image is updated (RE-16096)\n  agents.each do |agent|\n    on(agent, 'dnf -y upgrade almalinux-release') if fact_on(agent, 'os.name') == 'AlmaLinux'\n  end\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done at the integration (or unit) layer though\n                         # actual changing of resources could irreparably damage a\n                         # host running this, or require special permissions.\n\n  require 'puppet/acceptance/rpm_util'\n  extend Puppet::Acceptance::RpmUtils\n\n  epoch_rpm_options    = {:pkg => 'epoch', :version => '1.1', :epoch => '1'}\n  no_epoch_rpm_options = {:pkg => 'guid', :version => '1.0'}\n\n  teardown do\n    step \"cleanup\"\n    agents.each do |agent|\n      clean_rpm agent, epoch_rpm_options\n      clean_rpm agent, no_epoch_rpm_options\n    end\n  end\n\n  def verify_state(hosts, pkg, state, match)\n    hosts.each do |agent|\n      cmd = rpm_provider(agent)\n      # Note yum and dnf list packages as <name>.<arch>\n      on(agent, \"#{cmd} list installed\") do |result|\n        method(match).call(/^#{pkg}\\./, result.stdout)\n      end\n    end\n  end\n\n  def verify_present(hosts, pkg)\n    verify_state(hosts, pkg, '(?!purged|absent)[^\\']+', :assert_match)\n  end\n\n  def verify_absent(hosts, pkg)\n    verify_state(hosts, pkg, '(?:purged|absent)', :refute_match)\n  end\n\n  step \"Managing a package which does not include an epoch in its version\" do\n    step 'Setup repo and package'\n    agents.each do |agent|\n      clean_rpm agent, no_epoch_rpm_options\n      setup_rpm agent, no_epoch_rpm_options\n      send_rpm agent, no_epoch_rpm_options\n    end\n\n    step 'Installing a known package succeeds' do\n      verify_absent agents, 'guid'\n      apply_manifest_on(agents, 'package {\"guid\": ensure => installed}') do |result|\n        assert_match('Package[guid]/ensure: created', \"#{result.host}: #{result.stdout}\")\n      end\n    end\n\n    step 'Removing a known package succeeds' do\n      verify_present agents, 'guid'\n      apply_manifest_on(agents, 'package {\"guid\": ensure => absent}') do |result|\n        assert_match('Package[guid]/ensure: removed', \"#{result.host}: #{result.stdout}\")\n      end\n    end\n\n    step 'Installing a specific version of a known package succeeds' do\n      verify_absent agents, 'guid'\n      apply_manifest_on(agents, 'package {\"guid\": ensure => \"1.0\"}') do |result|\n        assert_match('Package[guid]/ensure: created', \"#{result.host}: #{result.stdout}\")\n      end\n    end\n\n    step 'Removing a specific version of a known package succeeds' do\n      verify_present agents, 'guid'\n      apply_manifest_on(agents, 'package {\"guid\": ensure => absent}') do |result|\n        assert_match('Package[guid]/ensure: removed', \"#{result.host}: #{result.stdout}\")\n      end\n    end\n\n    step 'Installing a non-existent version of a known package fails' do\n      verify_absent agents, 'guid'\n      apply_manifest_on(agents, 'package {\"guid\": ensure => \"1.1\"}') do |result|\n        refute_match(/Package\\[guid\\]\\/ensure: created/, \"#{result.host}: #{result.stdout}\")\n        assert_match(\"Package[guid]/ensure: change from 'purged' to '1.1' failed\", \"#{result.host}: #{result.stderr}\")\n      end\n      verify_absent agents, 'guid'\n    end\n\n    step 'Installing a non-existent package fails' do\n      verify_absent agents, 'not_a_package'\n      apply_manifest_on(agents, 'package {\"not_a_package\": ensure => present}') do |result|\n        refute_match(/Package\\[not_a_package\\]\\/ensure: created/, \"#{result.host}: #{result.stdout}\")\n        assert_match(\"Package[not_a_package]/ensure: change from 'purged' to 'present' failed\", \"#{result.host}: #{result.stderr}\")\n      end\n      verify_absent agents, 'not_a_package'\n    end\n\n    step 'Removing a non-existent package succeeds' do\n      verify_absent agents, 'not_a_package'\n      apply_manifest_on(agents, 'package {\"not_a_package\": ensure => absent}') do |result|\n        refute_match(/Package\\[not_a_package\\]\\/ensure/, \"#{result.host}: #{result.stdout}\")\n        assert_match('Applied catalog', \"#{result.host}: #{result.stdout}\")\n      end\n      verify_absent agents, 'not_a_package'\n    end\n\n    step 'Installing a known package using source succeeds' do\n      verify_absent agents, 'guid'\n      apply_manifest_on(agents, \"package { 'guid': ensure => installed, install_options => '--nogpgcheck', source=>'/tmp/rpmrepo/RPMS/noarch/guid-1.0-1.noarch.rpm' }\") do |result|\n        assert_match('Package[guid]/ensure: created', \"#{result.host}: #{result.stdout}\")\n      end\n    end\n  end\n\n  ### Epoch tests ###\n  agents.each do |agent|\n    step \"Managing a package which includes an epoch in its version\" do\n      step \"Setup repo and package\" do\n        clean_rpm agent, no_epoch_rpm_options\n        setup_rpm agent, epoch_rpm_options\n        send_rpm agent, epoch_rpm_options\n      end\n\n      step 'Installing a known package with an epoch succeeds' do\n        verify_absent [agent], 'epoch'\n        apply_manifest_on(agent, 'package {\"epoch\": ensure => installed}') do |result|\n          assert_match('Package[epoch]/ensure: created', \"#{result.host}: #{result.stdout}\")\n        end\n      end\n\n      step 'Removing a known package with an epoch succeeds' do\n        verify_present [agent], 'epoch'\n        apply_manifest_on(agent, 'package {\"epoch\": ensure => absent}') do |result|\n          assert_match('Package[epoch]/ensure: removed', \"#{result.host}: #{result.stdout}\")\n        end\n      end\n\n      step \"Installing a specific version of a known package with an epoch succeeds when epoch and arch are specified\" do\n        verify_absent [agent], 'epoch'\n        apply_manifest_on(agent, \"package {'epoch': ensure => '1:1.1-1.noarch'}\") do |result|\n          assert_match('Package[epoch]/ensure: created', \"#{result.host}: #{result.stdout}\")\n        end\n\n        apply_manifest_on(agent, \"package {'epoch': ensure => '1:1.1-1.noarch'}\") do |result|\n          refute_match(/epoch/, result.stdout)\n        end\n      end\n\n      if rpm_provider(agent) == 'dnf'\n        # Yum requires the arch to be specified whenever epoch is specified. This step is only\n        # expected to work in DNF.\n        step \"Installing a specific version of a known package with an epoch succeeds when epoch is specified and arch is not\" do\n          step \"Remove the package\" do\n            apply_manifest_on(agent, 'package {\"epoch\": ensure => absent}')\n            verify_absent [agent], 'epoch'\n          end\n\n          apply_manifest_on(agent, 'package {\"epoch\": ensure => \"1:1.1-1\"}') do |result|\n            assert_match('Package[epoch]/ensure: created', \"#{result.host}: #{result.stdout}\")\n          end\n\n          apply_manifest_on(agent, 'package {\"epoch\": ensure => \"1:1.1-1\"}') do |result|\n            refute_match(/epoch/, result.stdout)\n          end\n\n          apply_manifest_on(agent, \"package {'epoch': ensure => '1:1.1-1.noarch'}\") do |result|\n            refute_match(/epoch/, result.stdout)\n          end\n        end\n      end\n\n      if rpm_provider(agent) == 'yum'\n        step \"Installing a specified version of a known package with an epoch succeeds without epoch or arch provided\" do\n          # Due to a bug in DNF, epoch is required. This step is only expected to work in Yum.\n          # See https://bugzilla.redhat.com/show_bug.cgi?id=1286877\n          step \"Remove the package\" do\n            apply_manifest_on(agent, 'package {\"epoch\": ensure => absent}')\n            verify_absent [agent], 'epoch'\n          end\n\n          apply_manifest_on(agent, 'package {\"epoch\": ensure => \"1.1-1\"}') do |result|\n            assert_match('Package[epoch]/ensure: created', \"#{result.host}: #{result.stdout}\")\n          end\n\n          apply_manifest_on(agent, 'package {\"epoch\": ensure => \"1.1-1\"}') do |result|\n            refute_match(/epoch/, result.stdout)\n          end\n\n          apply_manifest_on(agent, \"package {'epoch': ensure => '1:1.1-1.noarch'}\") do |result|\n            refute_match(/epoch/, result.stdout)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/AIX_service_provider.rb",
    "content": "test_name 'AIX Service Provider Testing'\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nconfine :to, :platform =>  'aix'\n\nrequire 'puppet/acceptance/service_utils'\nextend Puppet::Acceptance::ServiceUtils\n\nsloth_daemon_script = <<SCRIPT\n#!/usr/bin/env sh\nwhile true; do sleep 1; done\nSCRIPT\n\ndef lsitab_assert_enable(host, service, expected_status)\n  case expected_status\n  when true\n    expected_output = service\n  when false\n    expected_output = ''\n  else\n    raise \"This test doesn't know what to do with an expected enable status of #{expected_status}\"\n  end\n\n  on(host, \"lsitab #{service} | cut -f 1 -d :\") do |result|\n    actual_output = result.stdout.chomp\n    assert_equal(expected_output, actual_output,\n      \"Service doesn't actually have enabled = #{expected_status}\")\n  end\nend\n\ndef lssrc_assert_status(host, service, expected_status)\n  case expected_status\n  when true\n    expected_output = 'active'\n  when false\n    expected_output = 'inoperative'\n  else\n    raise \"This test doesn't know what to do with an expected status of #{expected_status}\"\n  end\n\n  # sometimes there's no group or PID which messes up the condense to a single\n  # delimiter\n  on(host, \"lssrc -s #{service} | tr -s ' ' ':' | tail -1 | cut -f 3- -d :\") do |result|\n    actual_output = result.stdout.chomp\n    assert_match(/#{expected_output}\\Z/, actual_output,\n        \"Service is not actually #{expected_status}\")\n  end\nend\n\nteardown do\n  agents.each do |agent|\n    on(agent, \"rmssys -s sloth_daemon\", :allowable_exit_codes => [0,1])\n  end\nend\n\nagents.each do |agent|\n\n  run_nonexistent_service_tests('nonexistent_service')\n\n  step \"Setup on #{agent}\"\n  sloth_daemon_path = agent.tmpfile(\"sloth_daemon.sh\")\n  create_remote_file(agent, sloth_daemon_path, sloth_daemon_script)\n  on(agent, \"chmod +x #{sloth_daemon_path}\")\n  on(agent, \"mkssys -s sloth_daemon -p #{sloth_daemon_path} -u 0 -S -n 15 -f 9\")\n\n  # Creating the service may also start it. Stop service before beginning the test.\n  on(agent, puppet_resource('service', 'sloth_daemon', 'ensure=stopped', 'enable=false'))\n\n  ## Query\n  step \"Verify the service exists on #{agent}\"\n  on(agent, puppet_resource('service', 'sloth_daemon')) do |result|\n    assert_match(/sloth_daemon/, result.stdout, \"Couldn't find service sloth_daemon\")\n  end\n\n  ## Start the service\n  step \"Start the service on #{agent}\"\n  ensure_service_on_host(agent, 'sloth_daemon', {:ensure => 'running'}) do\n    lssrc_assert_status(agent, 'sloth_daemon', true)\n  end\n\n  ## Stop the service\n  step \"Stop the service on #{agent}\"\n  ensure_service_on_host(agent, 'sloth_daemon', {:ensure => 'stopped'}) do\n    lssrc_assert_status(agent, 'sloth_daemon', false)\n  end\n\n  ## Enable the service\n  step \"Enable the service on #{agent}\"\n  ensure_service_on_host(agent, 'sloth_daemon', {:enable => 'true'}) do\n    lsitab_assert_enable(agent, 'sloth_daemon', true)\n  end\n\n  ## Disable the service\n  step \"Disable the service on #{agent}\"\n  ensure_service_on_host(agent, 'sloth_daemon', {:enable => 'false'}) do\n    lsitab_assert_enable(agent, 'sloth_daemon', false)\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/init_on_systemd.rb",
    "content": "test_name 'SysV on default Systemd Service Provider Validation' do\n\n  confine :to, :platform => /el-[6-8]|centos|fedora-(2[0-9])/ do |h|\n    result = on(h, 'which systemctl', :acceptable_exit_codes => [0, 1])\n    result.stdout =~ /systemctl/\n  end\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done at the integration (or unit) layer though\n                         # actual changing of resources could irreparably damage a\n                         # host running this, or require special permissions.\n\n  require 'puppet/acceptance/service_utils'\n  extend Puppet::Acceptance::ServiceUtils\n\n  svc = 'puppetize'\n  initd_location = \"/etc/init.d/#{svc}\"\n  pidfile = \"/var/run/#{svc}.pid\"\n\n  # Some scripts don't have status command.\n  def initd_file(svc, pidfile, initd_location, status)\n    <<INITD\n#!/bin/bash\n# #{svc} daemon\n# chkconfig: 2345 20 80\n# description: #{svc} daemon\n\nDESC=\"#{svc} daemon\"\nPIDFILE=#{pidfile}\nSCRIPTNAME=#{initd_location}\n\ncase \"$1\" in\nstart)\n    PID=`/usr/bin/#{svc} 120 > /dev/null 2>&1 & echo $!`\n    if [ -z $PID ]; then\n        echo \"Failed to start\"\n    else\n        echo $PID > $PIDFILE\n        echo \"Started\"\n    fi\n;;\n\n#{if status then \"status)\" else \"status-ignored)\" end}\n    if [ -f $PIDFILE ]; then\n        PID=`cat $PIDFILE`\n        if [ -z \"`ps axf | grep ${PID} | grep -v grep `\" ]; then\n            printf \"Process dead but pidfile exists\"\n            exit 2\n        else\n            echo \"Running\"\n        fi\n    else\n        echo \"Service not running\"\n        exit 3\n    fi\n;;\n\nstop)\n    PID=`cat $PIDFILE`\n    if [ -f $PIDFILE ]; then\n        kill -TERM $PID\n        rm -f $PIDFILE\n    else\n        echo \"pidfile not found\"\n    fi\n;;\n\nrestart)\n    $0 stop\n    $0 start\n;;\n\n*)\n    echo \"Usage: $0 (start|stop|restart)\"\n    exit 1\nesac\n\nexit 0\nINITD\n  end\n\n  def assert_service_status(agent, pidfile, expected_running)\n    on(agent, \"ps -p `cat #{pidfile}`\", :acceptable_exit_codes => (expected_running ? [0] : [1]))\n  end\n\n  agents.each do |agent|\n    sleep_bin = on(agent, 'which sleep').stdout.chomp\n\n    step \"Create initd script with status command\" do\n      create_remote_file agent, initd_location, initd_file(svc, pidfile, initd_location, true)\n      apply_manifest_on agent, <<MANIFEST\nfile {'/usr/bin/#{svc}': ensure => link, target => '#{sleep_bin}', }\nfile {'#{initd_location}': ensure => file, mode   => '0755', }\nMANIFEST\n      on(agent, \"chkconfig --add #{svc}\")\n      on(agent, \"chkconfig #{svc}\", :acceptable_exit_codes => [0])\n      on(agent, \"service #{svc} status\", :acceptable_exit_codes => [3])\n    end\n\n    step \"Verify the service exists on #{agent}\" do\n      assert_service_status_on_host(agent, svc, {:ensure => 'stopped', :enable => 'true'}) do\n        assert_service_status(agent, pidfile, false)\n      end\n    end\n\n    step \"Start the service on #{agent}\" do\n      ensure_service_on_host(agent, svc, {:ensure => 'running'}) do\n        assert_service_status(agent, pidfile, true)\n      end\n    end\n\n    step \"Disable the service on #{agent}\" do\n      ensure_service_on_host(agent, svc, {:enable => 'false'}) do\n        assert_service_status(agent, pidfile, true)\n      end\n    end\n\n    step \"Stop the service on #{agent}\" do\n      ensure_service_on_host(agent, svc, {:ensure => 'stopped'}) do\n        assert_service_status(agent, pidfile, false)\n      end\n    end\n\n    step \"Enable the service on #{agent}\" do\n      ensure_service_on_host(agent, svc, {:enable => 'true'}) do\n        assert_service_status(agent, pidfile, false)\n      end\n    end\n\n    step \"Create initd script without status command\" do\n      create_remote_file agent, initd_location, initd_file(svc, pidfile, initd_location, false)\n      apply_manifest_on agent, <<MANIFEST\nfile {'/usr/bin/#{svc}': ensure => link, target => '#{sleep_bin}', }\nfile {'#{initd_location}': ensure => file, mode   => '0755', }\nMANIFEST\n      on(agent, \"chkconfig --add #{svc}\")\n      on(agent, \"chkconfig #{svc}\", :acceptable_exit_codes => [0])\n      on(agent, \"service #{svc} status\", :acceptable_exit_codes => [1])\n    end\n\n    step \"Verify the service exists on #{agent}\" do\n      assert_service_status_on_host(agent, svc, {:ensure => 'stopped', :enable => 'true'}) do\n        assert_service_status(agent, pidfile, false)\n      end\n    end\n\n    # The following are implemented differently because currently the Redhat provider can't tell when\n    # a service is running if it doesn't implement the status command. However it can still manage it.\n    step \"Start the service on #{agent}\" do\n      ensure_service_change_on_host(agent, svc, {:ensure => 'running'})\n      assert_service_status(agent, pidfile, true)\n      ensure_service_idempotent_on_host(agent, svc, {:ensure => 'running'})\n      assert_service_status(agent, pidfile, true)\n    end\n\n    step \"Disable the service on #{agent}\" do\n      ensure_service_change_on_host(agent, svc, {:enable => 'false'})\n      assert_service_status(agent, pidfile, true)\n      ensure_service_idempotent_on_host(agent, svc, {:enable => 'false'})\n      assert_service_status(agent, pidfile, true)\n    end\n\n    step \"Stop the service on #{agent}\" do\n      ensure_service_change_on_host(agent, svc, {:ensure => 'stopped'})\n      assert_service_status(agent, pidfile, false)\n      ensure_service_idempotent_on_host(agent, svc, {:ensure => 'stopped'})\n      assert_service_status(agent, pidfile, false)\n    end\n\n    step \"Enable the service on #{agent}\" do\n      ensure_service_change_on_host(agent, svc, {:enable => 'true'})\n      assert_service_status(agent, pidfile, false)\n      ensure_service_idempotent_on_host(agent, svc, {:enable => 'true'})\n      assert_service_status(agent, pidfile, false)\n    end\n\n    teardown do\n      on(agent, \"service #{svc} stop\", :accept_any_exit_code => true)\n      on(agent, \"chkconfig --del #{svc}\")\n      on(agent, \"rm /usr/bin/#{svc} #{initd_location}\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/launchd_provider.rb",
    "content": "test_name 'Mac OS X launchd Provider Testing'\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nconfine :to, {:platform => /osx/}, agents\n\nrequire 'puppet/acceptance/service_utils'\nextend Puppet::Acceptance::ServiceUtils\n\nsloth_daemon_script = <<SCRIPT\n#!/usr/bin/env sh\nwhile true; do sleep 1; done\nSCRIPT\n\nsvc = 'com.puppetlabs.sloth'\nlaunchd_script_path = \"/Library/LaunchDaemons/#{svc}.plist\"\n\ndef launchctl_assert_status(host, service, expect_running)\n  on(host, 'launchctl list') do |result|\n    if expect_running\n      assert_match(/#{service}/, result.stdout, 'Service was not found in launchctl list')\n    else\n      refute_match(/#{service}/, result.stdout, 'Service was not expected in launchctl list')\n    end\n  end\nend\n\nagents.each do |agent|\n  step \"Setup on #{agent}\"\n  sloth_daemon_path = agent.tmpfile(\"sloth_daemon.sh\")\n  create_remote_file(agent, sloth_daemon_path, sloth_daemon_script)\n\n  launchd_script = <<SCRIPT\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n        <key>Label</key>\n        <string>#{svc}</string>\n        <key>Program</key>\n        <string>#{sloth_daemon_path}</string>\n        <key>RunAtLoad</key>\n        <true/>\n</dict>\n</plist>\nSCRIPT\n  create_remote_file(agent, launchd_script_path, launchd_script)\n\n  teardown do\n    on agent, puppet_resource('service', 'com.puppetlabs.sloth', 'ensure=stopped', 'enable=true')\n    on agent, \"rm #{sloth_daemon_path} #{launchd_script_path}\"\n  end\n\n  step \"Verify the service exists on #{agent}\"\n  assert_service_status_on_host(agent, svc, {:ensure => 'stopped', :enable => 'true'}) do\n    launchctl_assert_status(agent, svc, false)\n  end\n\n  step \"Start the service on #{agent}\"\n  ensure_service_on_host(agent, svc, {:ensure => 'running'}) do\n    launchctl_assert_status(agent, svc, true)\n  end\n\n  step \"Disable the service on #{agent}\"\n  ensure_service_on_host(agent, svc, {:enable => 'false'}) do\n    launchctl_assert_status(agent, svc, true)\n  end\n\n  step \"Stop the service on #{agent}\"\n  ensure_service_on_host(agent, svc, {:ensure => 'stopped'}) do\n    launchctl_assert_status(agent, svc, false)\n  end\n\n  step \"Enable the service on #{agent}\"\n  ensure_service_on_host(agent, svc, {:enable => 'true'}) do\n    launchctl_assert_status(agent, svc, false)\n  end\n\n  # switching from stopped to running should output the correct status of the service and not 'absent'\n  step \"Start the service on #{agent} when service is stopped, and check output\" do\n    on agent, puppet_resource('service', svc, 'ensure=stopped')\n    on agent, puppet_resource('service', svc, 'ensure=running')\n    assert_service_status_on_host(agent, svc, {:ensure => 'running'})\n  end\n\n  # switching from running to stopped should output the correct status of the service and not 'absent'\n  step \"Stop the service on #{agent} when service is running, and check output\" do\n    on agent, puppet_resource('service', svc, 'ensure=running')\n    on agent, puppet_resource('service', svc, 'ensure=stopped')\n    assert_service_status_on_host(agent, svc, {:ensure => 'stopped'})\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/puppet_service_management.rb",
    "content": "test_name \"The Puppet service should be manageable with Puppet\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n    'audit:acceptance' # uses services from a running puppet-agent install\n#\n# This test is intended to ensure that the Puppet service can\n# be directly managed by Puppet. See PUP-5053, PUP-5257, and RE-5574 for\n# more context around circumstances that this can fail.\n#\n\nskip_test 'requires puppet service scripts from AIO agent package' if @options[:type] != 'aio'\n\nrequire 'puppet/acceptance/service_utils'\nextend Puppet::Acceptance::ServiceUtils\n\n# Set service status before running other 'ensure' operations on it\ndef set_service_initial_status(host, service, status)\n  step \"Establishing precondition: #{service}: ensure => #{status}\"\n  ensure_service_on_host(host, service, {'ensure' => status})\nend\n\n# We want to test Puppet in the following conditions:\n# 1) Starting, stopping and refreshing while the service is initially stopped\n# 2) Starting, stopping and refreshing while the service is initially running\nagents.each do |agent|\n\n  ['puppet'].each do |service|\n    # --- service management using `puppet apply` --- #\n    step \"#{service} service management using `puppet apply`\"\n    set_service_initial_status(agent, service, 'stopped')\n    step \"Starting the #{service} service: it should be running\"\n    ensure_service_on_host(agent, service, {'ensure' => 'running'})\n\n    step \"Stopping the #{service} service: it should be stopped\"\n    ensure_service_on_host(agent, service, {'ensure' => 'stopped'})\n\n    ['stopped', 'running'].each do |status|\n      step \"Refreshing the #{service} service while it is #{status}: it should be #{status}\"\n      set_service_initial_status(agent, service, status)\n      refresh_service_on_host(agent, service)\n      assert_service_status_on_host(agent, service, {'ensure' => status}) # Status should not change after refresh\n\n      # --- service management using `puppet resource` --- #\n      step \"#{service} service management using `puppet resource`\"\n      step \"Starting the #{service} service while it is #{status}: it should be running\"\n      set_service_initial_status(agent, service, status)\n      on(agent, puppet_resource('service', service, 'ensure=running'))\n      assert_service_status_on_host(agent, service, {'ensure' => 'running'}) # Status should always be 'running' after starting\n\n      step \"Stopping the #{service} service while it is #{status}: it should be stopped\"\n      set_service_initial_status(agent, service, status)\n      on(agent, puppet_resource('service', service, 'ensure=stopped'))\n      assert_service_status_on_host(agent, service, {'ensure' => 'stopped'}) # Status should always be 'stopped' after stopping\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/puppet_service_runs_puppet.rb",
    "content": "require 'puppet/acceptance/service_utils'\nextend Puppet::Acceptance::ServiceUtils\n\ntest_name 'Starting the puppet service should successfully run puppet' do\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  skip_test 'requires a server node to run puppet agent -t' unless master\n\n  agents.each do |agent|\n    statedir = on(agent, puppet('config', 'print', 'statedir')).stdout.chomp\n    last_run_report = \"#{statedir}/last_run_report.yaml\"\n\n    teardown do\n      on(agent, puppet_resource('file', last_run_report, 'ensure=absent'))\n    end\n\n    step 'Ensure last_run_report.yaml is absent' do\n      on(agent, puppet_resource('file', last_run_report, 'ensure=absent'))\n    end\n\n    step 'Ensure stop puppet service' do\n      on(agent, puppet_resource('service', 'puppet', 'ensure=stopped'))\n      assert_service_status_on_host(agent, 'puppet', {'ensure' => 'stopped'})\n    end\n\n    step 'Ensure start puppet service' do\n      on(agent, puppet_resource('service', 'puppet', 'ensure=running'))\n      assert_service_status_on_host(agent, 'puppet', {'ensure' => 'running'})\n    end\n\n    retry_params = {:max_retries => 30,\n                    :retry_interval => 2}\n\n    step 'Ensure last_run_report.yaml is created' do\n      retry_on(agent, \"test -e #{last_run_report}\", retry_params)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/service_enable_linux.rb",
    "content": "test_name 'SysV and Systemd Service Provider Validation'\n\ntag 'audit:high',\n    'audit:refactor',  # Investigate merging with init_on_systemd.rb\n                       # Use block style `test_name`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nconfine :to, :platform => /el-|centos|fedora|debian|sles|ubuntu-v/\n# osx covered by launchd_provider.rb\n# ubuntu-[a-u] upstart covered by ticket_14297_handle_upstart.rb\n\npackage_name = {'el'     => 'httpd',\n                'centos' => 'httpd',\n                'fedora' => 'httpd',\n                'debian' => 'apache2',\n                'sles'   => 'apache2',\n                'ubuntu' => 'cron', # See https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1447807\n}\n\nagents.each do |agent|\n  platform = agent.platform.variant\n  osname = on(agent, facter('os.name')).stdout.chomp\n  majrelease = on(agent, facter('os.release.major')).stdout.chomp.to_i\n\n  init_script_systemd = \"/usr/lib/systemd/system/#{package_name[platform]}.service\"\n  symlink_systemd     = \"/etc/systemd/system/multi-user.target.wants/#{package_name[platform]}.service\"\n\n  start_runlevels     = [\"2\", \"3\", \"4\", \"5\"]\n  kill_runlevels      = [\"0\", \"1\", \"6\"]\n  if platform == 'sles'\n    start_runlevels   = [\"3\", \"5\"]\n    kill_runlevels    = [\"3\", \"5\"]\n  elsif platform == 'ubuntu'\n    start_runlevels   = [\"2\", \"3\", \"4\", \"5\"]\n    kill_runlevels    = [\"2\", \"3\", \"4\", \"5\"]\n  end\n\n  manifest_uninstall_package = %Q{\n    package { '#{package_name[platform]}':\n      ensure => absent,\n    }\n  }\n  manifest_install_package = %Q{\n    package { '#{package_name[platform]}':\n      ensure => present,\n    }\n  }\n  manifest_service_enabled = %Q{\n    service { '#{package_name[platform]}':\n      enable => true,\n    }\n  }\n  manifest_service_disabled = %Q{\n    service { '#{package_name[platform]}':\n      enable => false,\n    }\n  }\n\n  teardown do\n    apply_manifest_on(agent, manifest_uninstall_package)\n  end\n\n  step \"installing #{package_name[platform]}\"\n  apply_manifest_on(agent, manifest_install_package, :catch_failures => true)\n\n  step \"ensure enabling service creates the start & kill symlinks\"\n  # amazon linux is based on el: v1 uses a 4-digit year for its majrelease\n  # and is based on el-6 (sysV). v2 returns a single-digit version for its\n  # majrelease and is based on el-7 (systemd).\n  is_sysV = ((platform == 'centos' || platform == 'el') && osname != 'Amazon' && majrelease < 7) ||\n             (osname == 'Amazon' && majrelease > 2010) ||\n             platform == 'debian' || platform == 'ubuntu' ||\n             (platform == 'sles' && majrelease < 12)\n  apply_manifest_on(agent, manifest_service_disabled, :catch_failures => true)\n  apply_manifest_on(agent, manifest_service_enabled, :catch_failures => true) do\n    if is_sysV\n      # debian platforms using sysV put rc runlevels directly in /etc/\n      on agent, \"ln -s /etc/ /etc/rc.d\", :accept_all_exit_codes => true\n      rc_symlinks = on(agent, \"find /etc/ -name *#{package_name[platform]}\", :accept_all_exit_codes => true).stdout\n      start_runlevels.each do |runlevel|\n        assert_match(/rc#{runlevel}\\.d\\/S\\d\\d#{package_name[platform]}/, rc_symlinks, \"did not find start symlink for #{package_name[platform]} in runlevel #{runlevel}\")\n        assert_match(/\\/etc(\\/rc\\.d)?\\/init\\.d\\/#{package_name[platform]}/, rc_symlinks, \"did not find #{package_name[platform]} init script\")\n      end\n\n      # Temporary measure until the Ubuntu SysV bugs are fixed. The cron service doesn't keep kill symlinks around while\n      # the service is enabled, unlike Apache2.\n      unless platform == 'ubuntu'\n        kill_runlevels.each do |runlevel|\n          assert_match(/rc#{runlevel}\\.d\\/K\\d\\d#{package_name[platform]}/, rc_symlinks, \"did not find kill symlink for #{package_name[platform]} in runlevel #{runlevel}\")\n        end\n      end\n    else\n      rc_symlinks = on(agent, \"ls #{symlink_systemd} #{init_script_systemd}\", :accept_all_exit_codes => true).stdout\n      assert_match(\"#{symlink_systemd}\",     rc_symlinks, \"did not find #{symlink_systemd}\")\n      assert_match(\"#{init_script_systemd}\", rc_symlinks, \"did not find #{init_script_systemd}\")\n    end\n  end\n\n  step \"ensure disabling service removes start symlinks\"\n  apply_manifest_on(agent, manifest_service_disabled, :catch_failures => true) do\n    if is_sysV\n      rc_symlinks = on(agent, \"find /etc/ -name *#{package_name[platform]}\", :accept_all_exit_codes => true).stdout\n      # sles removes rc.d symlinks\n      if platform != 'sles'\n        (start_runlevels + kill_runlevels).each do |runlevel|\n          assert_match(/rc#{runlevel}\\.d\\/K\\d\\d#{package_name[platform]}/, rc_symlinks, \"did not find kill symlink for #{package_name[platform]} in runlevel #{runlevel}\")\n        end\n      end\n    else\n      rc_symlinks = on(agent, \"ls #{symlink_systemd}\", :accept_all_exit_codes => true).stdout\n      refute_match(\"#{symlink_systemd}\",     rc_symlinks, \"should not have found #{symlink_systemd}\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/should_not_change_the_system.rb",
    "content": "test_name \"`puppet resource service` should list running services without calling dangerous init scripts\"\n\ntag 'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:integration' # Doesn't change the system it runs on\n\nconfine :except, :platform => 'windows'\nconfine :except, :platform => 'solaris'\n\n# For each script in /etc/init.d, the init service provider will call\n# the script with the `status` argument, except for blacklisted\n# scripts that are known to be dangerous, e.g. /etc/init.d/reboot.sh\n# The first execution of `puppet resource service` will enumerate\n# all services, and we want to check that puppet enumerates at\n# least one service. We use ssh because our tests run over ssh, so it\n# must be present.\n\nagents.each do |agent|\n  service_name = case agent['platform']\n                 when /osx/\n                   \"com.openssh.sshd\"\n                 else\n                   \"ssh[^']*\"\n                 end\n\n  step \"list running services and make sure ssh reports running\"\n  on(agent, puppet('resource service')) do |result|\n    assert_match(/service { '#{service_name}':\\n\\s*ensure\\s*=>\\s*'(?:true|running)'/, result.stdout, \"ssh is not running\")\n  end\n\n  step \"list running services again and make sure ssh is still running\"\n  on(agent, puppet('resource service')) do |result|\n    assert_match(/service { '#{service_name}':\\n\\s*ensure\\s*=>\\s*'(?:true|running)'/, result.stdout, \"ssh is no longer running\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/should_query_all.rb",
    "content": "test_name \"should query all services\"\n\ntag 'audit:high',\n    'audit:refactor',   # Investigate combining with should_not_change_the_system.rb\n                        # Use block style `test_name`\n    'audit:integration' # Doesn't change the system it runs on\n\nagents.each do |agent|\n  step \"query with puppet\"\n  on(agent, puppet_resource('service'), :accept_all_exit_codes => true) do |result|\n    assert_equal(result.exit_code, 0, \"'puppet resource service' should have an exit code of 0\")\n    assert(/^service/ =~ result.stdout, \"'puppet resource service' should present service details\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/smf_basic_tests.rb",
    "content": "test_name \"SMF: basic tests\" do\n  confine :to, :platform => 'solaris'\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done at the integration (or unit) layer though\n                         # actual changing of resources could irreparably damage a\n                         # host running this, or require special permissions.\n\n  require 'puppet/acceptance/solaris_util'\n  extend Puppet::Acceptance::SMFUtils\n\n  require 'puppet/acceptance/service_utils'\n  extend Puppet::Acceptance::ServiceUtils\n\n  def assert_svcs_info_matches_on(agent, service, info_hash)\n    info_hash.merge({ 'next_state' => 'none' })\n    on(agent, \"svcs -l #{service}\") do |result|\n      info_hash.each do |key, value|\n        escaped_key, escaped_value = Regexp.escape(key), Regexp.escape(value)\n\n        assert_match(\n          /^#{escaped_key}.*#{escaped_value}$/,\n          result.stdout,\n          \"`svcs -l #{service}` does not indicate that #{key} = #{value} on #{agent}\"\n        )\n      end\n    end\n  end\n\n  teardown do\n    agents.each do |agent|\n      clean agent, :service => 'tstapp'\n    end\n  end\n\n  agents.each do |agent|\n    clean agent, :service => 'tstapp'\n\n    # Run the tests for a non-existent service first\n    run_nonexistent_service_tests('tstapp')\n\n    manifest, _ = setup agent, :service => 'tstapp'\n\n    step \"Ensure that the service is created with a manifest\" do\n      apply_manifest_on(agent, 'service {tstapp : enable=>true, manifest=>\"%s\", ensure=>\"running\"}' % manifest) do |result|\n        assert_match( /ensure changed 'stopped' to 'running'/, result.stdout, \"Failed to create, enable and start the service on #{agent}\")\n      end\n    end\n\n    step \"Ensure that the SMF provider is idempotent -- it does not create services again\" do\n      apply_manifest_on(agent, 'service {tstapp : enable=>true, manifest=>\"%s\"}' % manifest, :catch_changes => true)\n    end\n\n    step \"Ensure you can query the service with the ral\" do\n      on(agent, puppet(\"resource service tstapp\")) do |result|\n        { ensure: 'running', enable: true, provider: 'smf' }.each do |property, value|\n          assert_match(/#{property}.*#{value}.*$/, result.stdout, \"Puppet does not report #{property}=#{value} for tstapp service\")\n        end\n      end\n    end\n\n    step \"Verify that ensure can be syncd. without changing the service's enabled? status\" do\n      on(agent, puppet(\"resource service tstapp ensure=stopped\"))\n      assert_svcs_info_matches_on(agent, 'application/tstapp', { 'enabled' => 'false (temporary)', 'state' => 'disabled' })\n    end\n\n    step \"Ensure that when syncing only enable, the service's current status is preserved\" do\n      # Mark the service as maint using svcadm\n      on(agent, 'svcadm mark -I maintenance tstapp')\n      on(agent, puppet(\"resource service tstapp enable=false\"))\n      assert_svcs_info_matches_on(agent, 'application/tstapp', { 'enabled' => 'false', 'state' => 'maintenance' })\n    end\n\n    step \"enable == true and ensure == stopped stop the service, but enable it to start again upon reboot\" do\n      on(agent, puppet(\"resource service tstapp enable=true ensure=stopped\"))\n      assert_svcs_info_matches_on(agent, 'application/tstapp', { 'enabled' => 'false (temporary)', 'state' => 'disabled' })\n    end\n\n    step \"enable == false and ensure == running start the service, but disable it upon reboot\" do\n      on(agent, puppet(\"resource service tstapp enable=false ensure=running\"))\n      assert_svcs_info_matches_on(agent, 'application/tstapp', { 'enabled' => 'true (temporary)', 'state' => 'online' })\n    end\n\n    step \"Verify that puppet will noop on the service if enable + ensure are already synced\" do\n      apply_manifest_on(agent, 'service {tstapp : enable=>false, ensure=>\"running\"}' % manifest, :catch_changes => true)\n      assert_svcs_info_matches_on(agent, 'application/tstapp', { 'enabled' => 'true (temporary)', 'state' => 'online' })\n    end\n\n    step \"Ensure that pupppet fails when multiple instances of the service resource exist\" do\n      # Add a second service instance.\n      on(agent, 'svccfg -s application/tstapp add second')\n      apply_manifest_on(agent, 'service {tstapp : enable=>true}') do |result|\n        assert_match(/Error:.*'tstapp' matches multiple FMRIs/, result.stderr, \"Puppet fails to output an error message when multiple FMRIs of a given service exist\")\n      end\n      on(agent, 'svccfg delete svc:/application/tstapp:second')\n    end\n\n    if agent['platform'] =~ /11/\n      step \"SMF: unset the general/complete property to mark the service as an incomplete service\" do\n        fmri = on(agent, \"svcs -H -o fmri tstapp\").stdout.chomp\n        on(agent, \"svccfg -s #{fmri} delprop general/complete\")\n      end\n\n      step \"Verify that an incomplete service is considered stopped and disabled\" do\n        on(agent, puppet_resource('service', 'tstapp')) do |result|\n          { enable: false, ensure: :stopped }.each do |property, value|\n            assert_match(/#{property}.*#{value}.*$/, result.stdout, \"Puppet does not report #{property}=#{value} for an incomplete service\")\n          end\n        end\n      end\n\n      step \"Verify that stopping and disabling an incomplete service is a no-op\" do\n        manifest =  service_manifest('tstapp', ensure: :stopped, enable: false)\n        apply_manifest_on(agent, manifest, catch_changes: true)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/systemd_resource_shows_correct_output.rb",
    "content": "require 'puppet/acceptance/service_utils'\nextend Puppet::Acceptance::ServiceUtils\n\ntest_name 'systemd service shows correct output when queried with \"puppet resource\"' do\n\n  tag 'audit:high'\n\n  skip_test 'requires puppet service script from AIO agent package' if @options[:type] != 'aio'\n\n  package_name = 'puppet'\n\n  # This test ensures that 'puppet resource' output matches the system state\n  confine :to, {}, agents.select { |agent| supports_systemd?(agent) }\n\n  agents.each do |agent|\n    initial_state = on(agent, puppet_resource('service', package_name)).stdout\n\n    teardown do\n      apply_manifest_on(agent, initial_state)\n    end\n\n    step \"Setting ensure=stopped and enable=true\" do\n      on(agent, puppet_resource('service', package_name, 'ensure=stopped', 'enable=true'))\n    end\n\n    step \"Expect reported status to match system state\" do\n      on(agent, puppet_resource('service', package_name, 'ensure=stopped', 'enable=true')) do |result|\n        assert_match(/ensure\\s*=>\\s*'stopped'/, result.stdout, \"Expected '#{package_name}' service to appear as stopped\")\n        assert_match(/enable\\s*=>\\s*'true'/, result.stdout, \"Expected '#{package_name}' service to appear as enabled\")\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "acceptance/tests/resource/service/ticket_5024_systemd_enabling_masked_service.rb",
    "content": "require 'puppet/acceptance/service_utils'\nextend Puppet::Acceptance::ServiceUtils\n\ntest_name 'Systemd masked services are unmasked before attempting to start'\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done at the integration (or unit) layer though\n                       # actual changing of resources could irreparably damage a\n                       # host running this, or require special permissions.\n\nskip_test \"requires AIO install to require 'puppet'\" if @options[:type] != 'aio'\n\n# This test in intended to ensure that a service which was previously marked\n# as masked and then set to enabled will first be unmasked.\nconfine :to, {}, agents.select { |agent| supports_systemd?(agent) }\npackage_name = {'el'     => 'httpd',\n                'centos' => 'httpd',\n                'fedora' => 'httpd',\n                'amazon' => 'httpd',\n                'sles'   => 'apache2',\n                'debian' => 'cron', # apache2 does not create systemd service symlinks in Debian\n                'ubuntu' => 'cron', # See https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1447807\n}\n\nagents.each do |agent|\n  platform = agent.platform.variant\n  init_script_systemd = \"/usr/lib/systemd/system/#{package_name[platform]}.service\"\n\n  if agent['platform'] =~ /(ubuntu)/\n    version = on(agent, facter('os.release.full')).stdout.chomp.to_i\n    if version < 24\n      init_script_systemd = \"/lib/systemd/system/#{package_name[platform]}.service\"\n    end\n  elsif agent['platform'] =~ /debian/\n    init_script_systemd = \"/lib/systemd/system/#{package_name[platform]}.service\"\n  end\n\n  symlink_systemd = \"/etc/systemd/system/multi-user.target.wants/#{package_name[platform]}.service\"\n  masked_symlink_systemd = \"/etc/systemd/system/#{package_name[platform]}.service\"\n\n  manifest_uninstall_package = %Q{\n    package { '#{package_name[platform]}':\n      ensure => absent,\n    }\n  }\n  manifest_install_package = %Q{\n    package { '#{package_name[platform]}':\n      ensure => present,\n    }\n  }\n  manifest_service_masked = %Q{\n    service { '#{package_name[platform]}':\n      enable => mask,\n      ensure => stopped,\n    }\n  }\n  manifest_service_enabled = %Q{\n    service { '#{package_name[platform]}':\n      enable => true,\n      ensure => running,\n    }\n  }\n\n  teardown do\n    if platform == 'sles'\n      on agent, 'zypper remove -y apache2 apache2-prefork libapr1 libapr-util1'\n    else\n      apply_manifest_on(agent, manifest_uninstall_package)\n    end\n  end\n\n  step \"Installing #{package_name[platform]}\"\n  apply_manifest_on(agent, manifest_install_package, :catch_failures => true)\n\n  step \"Masking the #{package_name[platform]} service\"\n  apply_manifest_on(agent, manifest_service_masked, :catch_failures => true)\n  on(agent, puppet_resource('service', package_name[platform])) do |result|\n    assert_match(/ensure.+=> 'stopped'/, result.stdout, \"Expected #{package_name[platform]} service to be stopped\")\n    assert_match(/enable.+=> 'false'/, result.stdout, \"Expected #{package_name[platform]} service to be masked\")\n    on(agent, \"readlink #{masked_symlink_systemd}\") do |readlink_result|\n      assert_equal('/dev/null', readlink_result.stdout.chomp, \"Expected service symlink to point to /dev/null\")\n    end\n  end\n\n  step \"Enabling the #{package_name[platform]} service\"\n  apply_manifest_on(agent, manifest_service_enabled, :catch_failures => true)\n  on(agent, puppet_resource('service', package_name[platform])) do |result|\n    assert_match(/ensure.+=> 'running'/, result.stdout, \"Expected #{package_name[platform]} service to be running\")\n    assert_match(/enable.+=> 'true'/, result.stdout, \"Expected #{package_name[platform]} service to be enabled\")\n    on(agent, \"readlink #{symlink_systemd}\") do |readlink_result|\n      assert_equal(init_script_systemd, readlink_result.stdout.chomp, \"Expected service symlink to point to systemd init script\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/windows.rb",
    "content": "test_name \"Windows Service Provider\" do\n  confine :to, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/windows_utils'\n  extend Puppet::Acceptance::WindowsUtils\n\n  require 'puppet/acceptance/service_utils'\n  extend Puppet::Acceptance::ServiceUtils\n\n  def service_manifest(name, params)\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\nservice { '#{name}':\n  #{params_str}\n}\nMANIFEST\n  end\n\n  mock_service_nofail = {\n    :name => \"mock_service_nofail\",\n    :start_sleep => 0,\n    :pause_sleep => 0,\n    :continue_sleep => 0,\n    :stop_sleep => 0,\n  }\n\n  # Since the default timeout for service operations is\n  # 30 seconds, waiting for 40 should ensure that the service\n  # operation will fail with a default timeout.\n  mock_service_long_start_stop = {\n    :name => \"mock_service_long_start_stop\",\n    :start_sleep => 40,\n    :pause_sleep => 0,\n    :continue_sleep => 0,\n    :stop_sleep => 40,\n  }\n\n  new_user = \"tempUser#{rand(999999).to_i}\"\n  fresh_user = \"freshUser#{rand(999999).to_i}\"\n\n  fresh_user_manifest = <<-MANIFEST\n    user { '#{fresh_user}':\n      ensure => present,\n      password => 'freshUserPassword#123',\n      roles => 'SeServiceLogonRight'\n    }\n\n    service { '#{mock_service_nofail[:name]}':\n      logonaccount => '#{fresh_user}',\n      logonpassword => 'freshUserPassword#123',\n      require => User['#{fresh_user}']\n    }\n  MANIFEST\n\n  teardown do\n    delete_service(agent, mock_service_nofail[:name])\n    delete_service(agent, mock_service_long_start_stop[:name])\n    on(agent, puppet(\"resource user #{new_user} ensure=absent\"))\n    on(agent, puppet(\"resource user #{fresh_user} ensure=absent\"))\n  end\n\n  agents.each do |agent|\n    local_service_locale_name = agent['locale'] == 'fr' ? 'AUTORITE NT\\SERVICE LOCAL' : 'NT AUTHORITY\\LOCAL SERVICE'\n\n    run_nonexistent_service_tests(mock_service_nofail[:name])\n    setup_service(agent, mock_service_nofail, 'MockService.cs')\n\n    step 'Verify that enable = false disables the service' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: false))\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartMode: 'Disabled')\n    end\n\n    step 'Verify that enable = manual indicates that the service can be started on demand' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: :manual))\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartMode: 'Manual')\n    end\n\n    step 'Verify that enable = delayed indicates that the service start mode is correctly set' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: :delayed))\n      assert_service_startmode_delayed(agent, mock_service_nofail[:name])\n    end\n\n    step 'Verify that enable = true indicates that the service is started automatically upon reboot' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: true))\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartMode: 'Auto')\n    end\n\n    step 'Verify that enable noops if the enable property is already synced' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: true), catch_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartMode: 'Auto')\n    end\n\n    step 'Verify that we can start the service' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :running))\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Running')\n    end\n\n    step 'Verify that we can change logonaccount, for an already running service, using user created in the same manifest' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem')\n      apply_manifest_on(agent, fresh_user_manifest, expect_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: fresh_user)\n    end\n\n    step 'Verify that running the same manifest twice causes no more changes' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: fresh_user)\n      apply_manifest_on(agent, fresh_user_manifest, catch_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: fresh_user)\n    end\n\n    step 'Verify that we can change logonaccount, for an already running service, using SID' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: fresh_user)\n      on(agent, puppet(\"resource service #{mock_service_nofail[:name]} logonaccount=S-1-5-19\")) do |result|\n        assert_match(/Service\\[#{mock_service_nofail[:name]}\\]\\/logonaccount: logonaccount changed '.\\\\#{fresh_user}' to '#{Regexp.escape(local_service_locale_name)}'/, result.stdout)\n        refute_match(/Transitioning the #{mock_service_nofail[:name]} service from SERVICE_RUNNING to SERVICE_STOPPED/, result.stdout, \n          \"Expected no service restarts since ensure isn't being managed as 'running'.\")\n        refute_match(/Successfully started the #{mock_service_nofail[:name]} service/, result.stdout)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: local_service_locale_name)\n    end\n\n    step 'Verify that logonaccount noops if the logonaccount property is already synced' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: 'S-1-5-19'), catch_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: local_service_locale_name)\n    end\n\n    step 'Verify that setting logonaccount fails if input is invalid' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: 'InvalidUser'), :acceptable_exit_codes => [1]) do |result|\n        assert_match(/\"InvalidUser\" is not a valid account/, result.stderr)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: local_service_locale_name)\n    end\n\n    step 'Verify that the service restarts if it is already running, logonaccount is different from last run and ensure is set to running' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: local_service_locale_name)\n      on(agent, puppet(\"resource service #{mock_service_nofail[:name]} logonaccount=LocalSystem ensure=running --debug\")) do |result|\n        assert_match(/Service\\[#{mock_service_nofail[:name]}\\]\\/logonaccount: logonaccount changed '#{Regexp.escape(local_service_locale_name)}' to 'LocalSystem'/, result.stdout)\n        assert_match(/Transitioning the #{mock_service_nofail[:name]} service from SERVICE_RUNNING to SERVICE_STOPPED/, result.stdout)\n        assert_match(/Successfully started the #{mock_service_nofail[:name]} service/, result.stdout)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem')\n    end\n\n    step 'Verify that there are no restarts if logonaccount does not change, even though ensure is managed as running' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem')\n      on(agent, puppet(\"resource service #{mock_service_nofail[:name]} logonaccount=LocalSystem ensure=running --debug\")) do |result|\n        refute_match(/Service\\[#{mock_service_nofail[:name]}\\]\\/logonaccount: logonaccount changed/, result.stdout)\n        refute_match(/Service\\[#{mock_service_nofail[:name]}\\]\\/ensure: ensure changed/, result.stdout)\n        refute_match(/Transitioning the #{mock_service_nofail[:name]} service from SERVICE_RUNNING to SERVICE_STOPPED/, result.stdout)\n        refute_match(/Successfully started the #{mock_service_nofail[:name]} service/, result.stdout)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem')\n    end\n\n    step \"Create a new user named #{new_user}\" do\n      on(agent, puppet(\"resource user #{new_user} ensure=present password=firstPassword#123\")) do |result|\n        assert_match(/User\\[#{new_user}\\]\\/ensure: created/, result.stdout)\n      end\n    end\n\n    step 'Verify that a user without the `Logon As A Service` right cannot be managed as the logonaccount of a service' do\n      apply_manifest_on(agent, service_manifest(mock_service_long_start_stop[:name], logonaccount: new_user), :acceptable_exit_codes => [1]) do |result|\n        assert_match(/#{new_user}\" is missing the 'Log On As A Service' right./, result.stderr)\n      end\n    end\n\n    step \"Grant #{new_user} the `Logon As A Service` right\" do\n      on(agent, puppet(\"resource user #{new_user} roles='SeServiceLogonRight'\")) do |result|\n        assert_match(/User\\[#{new_user}\\]\\/roles: roles changed  to 'SeServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step 'Verify that setting logonpassword fails if input is invalid' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: new_user, logonpassword: 'wrongPass'), :acceptable_exit_codes => [1]) do |result|\n        assert_match(/The given password is invalid for user '\\.\\\\#{new_user}'/, result.stderr)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem')\n    end\n\n    step \"Verify that #{new_user} can be set as logonaccount and service is still running\" do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem')\n      on(agent, puppet(\"resource service #{mock_service_nofail[:name]} logonaccount=#{new_user} logonpassword=firstPassword#123 ensure=running --debug\")) do |result|\n        assert_match(/Service\\[#{mock_service_nofail[:name]}\\]\\/logonaccount: logonaccount changed 'LocalSystem' to '.\\\\#{new_user}'/, result.stdout)\n        assert_match(/Transitioning the #{mock_service_nofail[:name]} service from SERVICE_RUNNING to SERVICE_STOPPED/, result.stdout)\n        assert_match(/Successfully started the #{mock_service_nofail[:name]} service/, result.stdout)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n    end\n\n    step \"Change password for #{new_user} and verify that service state isn't yet affected by this\" do\n      on(agent, puppet(\"resource user #{new_user} ensure=present password=secondPassword#123\")) do |result|\n        assert_match(/User\\[#{new_user}\\]\\/password: changed \\[redacted\\] to \\[redacted\\]/, result.stdout)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n    end\n\n    step 'Verify that setting logonpassword fails when using old password and service remains running' do\n      apply_manifest_on(agent, service_manifest(mock_service_long_start_stop[:name], logonaccount: new_user, logonpassword: 'firstPassword#123'), :acceptable_exit_codes => [1]) do |result|\n        assert_match(/The given password is invalid for user/, result.stderr)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n    end\n\n    step 'Verify that setting the new logonpassword does not report any changes' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: new_user, logonpassword: 'secondPassword#123'), catch_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n    end\n\n    step 'Verify that we can still stop the service' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :stopped), expect_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Stopped')\n    end\n\n    step 'Verify that the new logonpassword has actually been set by succesfully restarting the service' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Stopped')\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :running), expect_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Running')\n    end\n\n    step \"Deny #{new_user} the `Logon As A Service` right\" do\n      on(agent, puppet(\"resource user #{new_user} roles='SeDenyServiceLogonRight'\")) do |result|\n        assert_match(/User\\[#{new_user}\\]\\/roles: roles changed SeServiceLogonRight to 'SeDenyServiceLogonRight,SeServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step 'Verify that we can still stop the service' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Running')\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :stopped), expect_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Stopped')\n    end\n\n    step 'Verify that the service cannot be started anymore because of the denied `Logon As A Service` right' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :running), :acceptable_exit_codes => [4], catch_changes: true) do |result|\n        assert_match(/Failed to start the service/, result.stderr)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Stopped')\n    end\n\n    step 'Verify that a user with `Logon As A Service` right denied will raise error when managing it as logonaccount for a service' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: new_user), :acceptable_exit_codes => [1, 4]) do |result|\n        assert_match(/#{new_user}\\\" has the 'Log On As A Service' right set to denied./, result.stderr)\n      end\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: new_user, State: 'Stopped')\n    end\n\n    step \"Grant back #{new_user} the `Logon As A Service` right for our subsequent tests\" do\n      on(agent, puppet(\"resource user #{new_user} roles='SeServiceLogonRight' role_membership=inclusive\")) do |result|\n        assert_match(/User\\[#{new_user}\\]\\/roles: roles changed SeServiceLogonRight,SeDenyServiceLogonRight to 'SeServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step 'Verify that ensure noops if the ensure property is already synced' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :stopped), catch_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Stopped')\n    end\n\n    step 'Verify that we can change logonaccount for a stopped service' do\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Stopped', StartName: new_user)\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: local_service_locale_name), expect_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Stopped', StartName: local_service_locale_name)\n    end\n\n    step 'Verify that logonaccount noops if the logonaccount property is already synced' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], logonaccount: local_service_locale_name), catch_changes: true)\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: local_service_locale_name)\n    end\n\n    step 'Verify that we can query the service with the RAL' do\n      on(agent, puppet(\"resource service #{mock_service_nofail[:name]}\")) do |result|\n        assert_match( /enable\\s+=>\\s+'true'/, result.stdout, \"Failed to query the service with the RAL on #{agent}\")\n      end\n    end\n\n    step 'Disable the service and change logonaccount back to `LocalSystem` in preparation for our subsequent tests' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: false, logonaccount: 'LocalSystem'))\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartName: 'LocalSystem', StartMode: 'Disabled')\n    end\n\n    step 'Verify that starting a disabled service fails if the enable property is not managed' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :running)) do |result|\n        assert_match(/#{mock_service_nofail[:name]}/, result.stderr, 'Windows service provider is able to start a disabled service without managing the enable property')\n      end\n    end\n\n    step 'Verify that enable = false, ensure = running leaves the service disabled and in the running state' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: false, ensure: :running))\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartMode: 'Disabled', State: 'Running')\n    end\n\n    step 'Stop the service to prepare for our subsequent tests' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :stopped))\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Stopped')\n    end\n\n    step 'Verify that enable = true, ensure = running leaves the service enabled and in the running state' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], enable: true, ensure: :running))\n      assert_service_properties_on(agent, mock_service_nofail[:name], StartMode: 'Auto', State: 'Running')\n    end\n\n    step 'Pause the service to prepare for the next test' do\n      on(agent, powershell(\"Suspend-Service #{mock_service_nofail[:name]}\"))\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Paused')\n    end\n\n    step 'Verify that Puppet can resume a paused service' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :running))\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Running')\n    end\n\n    step 'Pause the service (again) to prepare for the next test' do\n      on(agent, powershell(\"Suspend-Service #{mock_service_nofail[:name]}\"))\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Paused')\n    end\n\n    step 'Verify that Puppet can stop a paused service' do\n      apply_manifest_on(agent, service_manifest(mock_service_nofail[:name], ensure: :stopped))\n      assert_service_properties_on(agent, mock_service_nofail[:name], State: 'Stopped')\n    end\n\n    # delete the service so it doesn't interfere with subsequent tests\n    delete_service(agent, mock_service_nofail[:name])\n\n    setup_service(agent, mock_service_long_start_stop, 'MockService.cs')\n\n    step 'Verify that starting a service fails if the service does not start by the expiration of the wait hint' do\n      apply_manifest_on(agent, service_manifest(mock_service_long_start_stop[:name], ensure: :running)) do |result|\n        assert_match(/#{mock_service_long_start_stop[:name]}/, result.stderr, 'No progress made on service operation and dwWaitHint exceeded')\n      end\n    end\n\n    # delete and recreate the service so it doesn't interfere with subsequent tests\n    delete_service(agent, mock_service_long_start_stop[:name])\n    setup_service(agent, mock_service_long_start_stop, 'MockService.cs')\n\n    step 'Verify that starting a service works if the service has a long start and a long timeout' do\n      apply_manifest_on(agent, service_manifest(mock_service_long_start_stop[:name], ensure: :running, timeout: 45))\n      assert_service_properties_on(agent, mock_service_long_start_stop[:name], State: 'Running')\n    end\n\n    step 'Start the Service to prepare for subsequent test' do\n      apply_manifest_on(agent, service_manifest(mock_service_long_start_stop[:name], enable: true, ensure: :running))\n    end\n\n    step 'Verify that stopping a service fails if the service does not stop by the expiration of the wait hint' do\n      apply_manifest_on(agent, service_manifest(mock_service_long_start_stop[:name], ensure: :stopped)) do |result|\n        assert_match(/#{mock_service_long_start_stop[:name]}/, result.stderr, 'No progress made on service operation and dwWaitHint exceeded')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/service/windows_mixed_utf8.rb",
    "content": "# coding: utf-8\ntest_name \"Windows Service Provider With Mixed UTF-8 Service Names\" do\n  confine :to, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/windows_utils'\n  extend Puppet::Acceptance::WindowsUtils\n\n  def service_manifest(name, params)\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\nservice { '#{name}':\n  #{params_str}\n}\nMANIFEST\n  end\n\n  [\n    # different UTF-8 widths\n    # 1-byte A\n    # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n    # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n    # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n    {\n      :name => \"A\\u06FF\\u16A0\\u{2070E}\",\n      :start_sleep => 0,\n      :pause_sleep => 0,\n      :continue_sleep => 0,\n      :stop_sleep => 0,\n    }\n  ].each do |mock_service|\n    agents.each do |agent|\n      setup_service(agent, mock_service, 'MockService.cs')\n\n      step 'Verify that enable = false disables the service' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], enable: false))\n        assert_service_properties_on(agent, mock_service[:name], StartMode: 'Disabled')\n      end\n\n      step 'Verify that enable = manual indicates that the service can be started on demand' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], enable: :manual))\n        assert_service_properties_on(agent, mock_service[:name], StartMode: 'Manual')\n      end\n\n      step 'Verify that enable = delayed indicates that the service start mode is correctly set' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], enable: :delayed))\n        assert_service_startmode_delayed(agent, mock_service[:name])\n      end\n\n      step 'Verify that enable = true indicates that the service is started automatically upon reboot' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], enable: true))\n        assert_service_properties_on(agent, mock_service[:name], StartMode: 'Auto')\n      end\n\n      step 'Verify that we can start the service' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], ensure: :running))\n        assert_service_properties_on(agent, mock_service[:name], State: 'Running')\n      end\n\n      step 'Verify idempotence' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], ensure: :running))\n        assert_service_properties_on(agent, mock_service[:name], State: 'Running')\n      end\n\n      step 'Verify that we can stop the service' do\n        apply_manifest_on(agent, service_manifest(mock_service[:name], ensure: :stopped))\n        assert_service_properties_on(agent, mock_service[:name], State: 'Stopped')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/tidy/resources_should_be_non_isomorphic.rb",
    "content": "# This test is to verify multi tidy resources with same path but\n# different matches should not cause error as found in the bug PUP-6508\ntest_name \"PUP-6655 - C98145 tidy resources should be non-isomorphic\" do\n  tag 'audit:high',\n      'audit:integration'\n\n  agents. each do |agent|\n    dir = agent.tmpdir('tidy-test-dir')\n    on(agent, \"mkdir -p #{dir}\")\n\n    files = %w{file1.txt file2.doc}\n    on(agent, \"touch #{dir}/{#{files.join(',')}}\")\n\n    manifest = <<-MANIFEST\ntidy {'tidy-resource1':\n  path  => \"#{dir}\",\n  matches => \"*.txt\",\n  recurse => true,\n}\ntidy {'tidy-resource2':\n  path  => \"#{dir}\",\n  matches => \"*.doc\",\n  recurse => true,\n}\nMANIFEST\n\n    step \"Ensure the newly created files are present:\" do\n      present = files.map {|file| \"-f #{File.join(dir, file)}\"}.join(' -a ')\n      on(agent, \"[ #{present} ]\")\n    end\n\n    step \"Create multiple tidy resources with same path\" do\n      apply_manifest_on(agent, manifest) do |result|\n        refute_match(/Error:/, result.stderr, \"Unexpected error was detected\")\n      end\n    end\n\n    step \"Verify that the files are actually removed successfully:\" do\n      present = files.map {|file| \"-f #{File.join(dir, file)}\"}.join(' -o ')\n      on(agent, \"[ #{present} ]\", :acceptable_exit_codes => [1])\n    end\n\n    teardown do\n      on(agent, puppet(\"apply -e \\\"file{'#{dir}': ensure => absent, force => true}\\\"\"))\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/tidy/should_remove_old_files.rb",
    "content": "test_name \"Tidying files by date\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:integration'\n\nagents.each do |agent|\n  step \"Create a directory of old and new files\"\n  dir = agent.tmpdir('tidy-test')\n  on agent, \"mkdir -p #{dir}\"\n\n  # YYMMddhhmm, so 03:04 Jan 2 1970\n  old = %w[one two three four five]\n  new = %w[a b c d e]\n\n  on agent, \"touch -t 7001020304 #{dir}/{#{old.join(',')}}\"\n  on agent, \"touch #{dir}/{#{new.join(',')}}\"\n\n  step \"Run a tidy resource to remove the old files\"\n\nmanifest = <<-MANIFEST\n  tidy { \"#{dir}\":\n    age     => '1d',\n    recurse => true,\n  }\nMANIFEST\n\n  apply_manifest_on agent, manifest\n\n  step \"Ensure the old files are gone\"\n\n  old_test = old.map {|name| \"-f #{File.join(dir, name)}\"}.join(' -o ')\n\n  on agent, \"[ #{old_test} ]\", :acceptable_exit_codes => [1]\n\n  step \"Ensure the new files are still present\"\n\n  new_test = new.map {|name| \"-f #{File.join(dir, name)}\"}.join(' -a ')\n\n  on agent, \"[ #{new_test} ]\"\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/osx_10.4_should_fail_when_modify_home.rb",
    "content": "test_name \"should not modify the home directory of an user on OS X >= 10.14\" do\n  confine :to, :platform => /osx/\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::BeakerUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  user = \"pl#{rand(999999).to_i}\"\n\n  agents.each do |agent|\n    teardown do\n      agent.user_absent(user)\n    end\n\n    step \"ensure the user is present\" do\n      agent.user_present(user)\n    end\n\n    step \"verify the error message is correct\" do\n      expected_error = /OS X version [0-9\\.]+ does not allow changing home using puppet/\n      user_manifest = resource_manifest('user', user, ensure: 'present', home: \"/opt/#{user}\")\n\n      apply_manifest_on(agent, user_manifest) do |result|\n        assert_match(\n          expected_error,\n          result.stderr,\n          \"Puppet fails to report an error when changing home directory on OS X >= 10.14\"\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/osx_10.4_should_fail_when_modify_uid.rb",
    "content": "test_name \"should not modify the uid of an user on OS X >= 10.14\" do\n  confine :to, :platform => /osx/\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::BeakerUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  user = \"pl#{rand(999999).to_i}\"\n\n  agents.each do |agent|\n    teardown do\n      agent.user_absent(user)\n    end\n\n    step \"ensure the user is present\" do\n      agent.user_present(user)\n    end\n\n    step \"verify the error message is correct\" do\n      expected_error = /OS X version [0-9\\.]+ does not allow changing uid using puppet/\n      user_manifest = resource_manifest('user', user, ensure: 'present', uid: rand(999999))\n\n      apply_manifest_on(agent, user_manifest) do |result|\n        assert_match(\n          expected_error,\n          result.stderr,\n          \"Puppet fails to report an error when changing uid on OS X >= 10.14\"\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_allow_managed_macos_users_to_login.rb",
    "content": "test_name \"should allow managed macOS users to login\" do\n\n  confine :to, :platform => /osx/\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n\n  # Two different test cases, with additional environment setup inbetween,\n  # were added in the same file to save on test run time (managing macOS users\n  # takes around a minute or so because when retrieving plist data from all users with\n  # `dscl readall` we also receive thousands of bytes of user avatar image)\n  agents.each do |agent|\n    teardown do\n      on(agent, puppet(\"resource\", \"user\", 'testuser', \"ensure=absent\"))\n    end\n\n    # Checking if we can create a user with password, salt and iterations\n    step \"create the test user with password and salt\" do\n      # The password is 'helloworld'\n      apply_manifest_on(agent, <<-MANIFEST, :catch_failures => true)\n          user { 'testuser':\n            ensure => present,\n            password => '6ce97688468f231845d9d982f1f10832ca0c6c728a77bac51c548af99ebd9b9c62bcba15112a0c7a7e34effbb2e92635650c79c51517d72b083a4eb2a513f51ad1f8ea9556cef22456159c341d8bcd382a91708afaf253c2b727d4c6cd3d29cc26011d5d511154037330ecea0263b1be8c1c13086d029c57344291bd37952b56',\n            salt       => '377e8b60e5fdfe509cad188d5b1b9e40e78b418f8c3f0127620ea69d4c32789c',\n            iterations => 40000,\n          }\n        MANIFEST\n    end\n\n    step \"verify the password was set correctly and is able to log in\" do\n      on(agent, \"dscl /Local/Default -authonly testuser helloworld\", :acceptable_exit_codes => 0)\n    end\n\n    unless agent['platform'] =~ /^osx-1[1-9]/\n      skip_test \"AuthenticationAuthority field fix test is not valid for macOS before Big Sur (11.0)\"\n    end\n\n    # Setting up environment to mimic situation on macOS 11 BigSur\n    # Prior to macOS BigSur, `dscl . -create` was populating more fields with\n    # default values, including AuthenticationAuthority which contains the\n    # ShadowHash type\n    # Withouth this field, login is not allowed\n    step \"remove AuthenticationAuthority field from user\" do\n      on(agent, \"dscl /Local/Default -delete Users/testuser AuthenticationAuthority\", :acceptable_exit_codes => 0)\n    end\n\n    step \"expect user without AuthenticationAuthority to not be able to log in\" do\n      on(agent, \"dscl /Local/Default -authonly testuser helloworld\", :acceptable_exit_codes => (1..255))\n    end\n\n    # Expecting Puppet to pick up the missing field and add it to\n    # make the user usable again\n    step \"change password with different salt and expect AuthenticationAuthority field to be readded\" do\n      # The password is still 'helloworld' but with different salt\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n          user { 'testuser':\n            ensure => present,\n            password   => '7e8e82542a0c06595e99bfe10c6bd219d19dede137c5c2f84bf10d98d83b77302d3c9c7f7e3652d420f562613f582ab62b26a52b9b26d0d032efbd486fd865b3ba4fd8a3512137681ce87d190f8fa7848d941c6080c588528dcb682c763c040ff54992dce204c3e5dda973e7b36f7f50a774e55e99fe4c8ed6b6464614838c13',\n            salt       => '8005b8855a187086a3b59eff925a611ec61d2d66d2e786b7598fe0a0b4b8ffba',\n            iterations => 40000\n          }\n        MANIFEST\n        \n        assert_match(/User 'testuser' is missing the 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash/, result.stdout)\n        assert_match(/Adding 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash to user 'testuser'/, result.stdout)\n      end\n    end\n\n    step \"verify the password was set correctly and is able to log in\" do\n      on(agent, \"dscl /Local/Default -authonly testuser helloworld\", :acceptable_exit_codes => 0)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_create.rb",
    "content": "test_name \"should create a user\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure the user and group do not exist\"\n  agent.user_absent(name)\n  agent.group_absent(name)\n\n  step \"create the user\"\n  on agent, puppet_resource('user', name, 'ensure=present')\n\n  step \"verify the user exists\"\n  agent.user_get(name)\n\n  case agent['platform']\n  when /sles/, /solaris/, /windows/, /osx/, /aix/\n    # no private user groups by default\n  else\n    agent.group_get(name)\n  end\n\n  step \"delete the user, and group, if any\"\n  agent.user_absent(name)\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_create_modify_with_password.rb",
    "content": "# frozen_string_literal: true\n\ntest_name 'should create a user with password and modify the password' do\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n  # require changing the system running the test\n  # in ways that might require special permissions\n  # or be harmful to the system running the test\n  \n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::ManifestUtils\n\n  name = \"pl#{rand(999_999).to_i}\"\n  initial_password = 'test1'\n  modified_password = 'test2'\n\n  agents.each do |agent|\n    teardown { agent.user_absent(name) }\n\n    step 'ensure the user does not exist' do\n      user_manifest = resource_manifest('user', name, { ensure: 'absent', provider: 'useradd' } )\n      apply_manifest_on(agent, user_manifest) do |result|\n        skip_test 'Useradd provider not present on this host' if result.stderr =~ /Provider useradd is not functional on this host/\n      end\n    end\n\n    step 'create the user with password' do\n      apply_manifest_on(agent, <<-MANIFEST, catch_failures: true)\n          user { '#{name}':\n            ensure => present,\n            password => '#{initial_password}',\n          }\n        MANIFEST\n    end\n\n    step 'verify the password was set correctly' do\n      on(agent, puppet('resource', 'user', name), acceptable_exit_codes: 0) do |result|\n        assert_match(/password\\s*=>\\s*'#{initial_password}'/, result.stdout, 'Password was not set correctly')\n      end\n    end\n\n    step 'modify the user with a different password' do\n      # There is a known issue with SSSD and Red Hat 8, this is a temporary workaround until a permanent fix is\n      # implemented in our images. See ITHELP-100250\n      # https://access.redhat.com/solutions/7031304\n      if agent['platform'] == 'el-8-ppc64le'\n        on(agent, 'systemctl stop sssd; rm -f /var/lib/sss/db/*; systemctl start sssd', acceptable_exit_codes: 0)\n      end\n\n      apply_manifest_on(agent, <<-MANIFEST, catch_failures: true)\n\t    user { '#{name}':\n\t      ensure => present,\n\t      password => '#{modified_password}',\n\t    }\n\tMANIFEST\n    end\n\n    step 'verify the password was set correctly' do\n      on(agent, \"puppet resource user #{name}\", acceptable_exit_codes: 0) do |result|\n        assert_match(/password\\s*=>\\s*'#{modified_password}'/, result.stdout, 'Password was not changed correctly')\n      end\n    end\n\n    step 'Verify idempotency when setting the same password' do\n      apply_manifest_on(agent, <<-MANIFEST, expect_changes: false)\n\t    user { '#{name}':\n\t      ensure => present,\n\t      password => '#{modified_password}',\n\t    }\n\tMANIFEST\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_create_with_expiry_absent.rb",
    "content": "test_name \"verifies that puppet resource creates a user and assigns the correct expiry date when absent\" do\n  confine :except, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  user = \"pl#{rand(999999).to_i}\"\n\n  teardown do\n    step \"cleanup\"\n    agents.each do |host|\n      on(host, puppet_resource('user', user, 'ensure=absent'))\n    end\n  end\n  \n  agents.each do |host|\n    step \"user should not exist\"\n    on(host, puppet_resource('user', user, 'ensure=absent'), :acceptable_exit_codes => [0])\n  \n    step \"create user with expiry=absent\"\n    on(host, puppet_resource('user', user, 'ensure=present', 'expiry=absent'), :acceptable_exit_codes => [0])\n  \n    step \"verify the user exists and expiry is not set (meaning never expire)\"\n    on(host, puppet_resource('user', user)) do |result|\n      assert_match(/ensure.*=> 'present'/, result.stdout)\n      refute_match(/expiry.*=>/, result.stdout)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_create_with_gid.rb",
    "content": "test_name \"verifies that puppet resource creates a user and assigns the correct group\"\nconfine :except, :platform => 'windows'\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nuser = \"pl#{rand(999999).to_i}\"\ngroup = \"gp#{rand(999999).to_i}\"\n\nagents.each do |host|\n  step \"user should not exist\"\n  host.user_absent(user)\n\n  step \"group should exist\"\n  host.group_present(group)\n\n  step \"create user with group\"\n  on(host, puppet_resource('user', user, 'ensure=present', \"gid=#{group}\"))\n\n  step \"verify the group exists and find the gid\"\n  group_gid = host.group_gid(group)\n\n  step \"verify that the user has that as their gid\"\n  host.user_get(user) do |result|\n    if host['platform'] =~ /aix/\n        match = result.stdout.match(/pgrp=([^\\s\\\\]+)/)\n        user_gid = match ? host.group_gid(match[1]) : nil\n    else\n        user_gid = result.stdout.split(':')[3]\n    end\n\n    fail_test \"expected gid #{group_gid} but got: #{user_gid}\" unless group_gid == user_gid\n  end\n\n  step \"clean up after the test is done\"\n  on(host, puppet_resource('user', user, 'ensure=absent'))\n  on(host, puppet_resource('group', group, 'ensure=absent'))\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_destroy.rb",
    "content": "test_name \"should delete a user\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure the user is present\"\n  agent.user_present(name)\n\n  step \"delete the user\"\n  on agent, puppet_resource('user', name, 'ensure=absent')\n\n  step \"verify the user was deleted\"\n  fail_test \"User #{name} was not deleted\" if agent.user_list.include? name\n\n  step \"delete the user, if any\"\n  agent.user_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_destroy_with_managehome.rb",
    "content": "test_name \"should delete a user with managehome=true\" do\n  confine :except, :platform => /osx/\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  agents.each do |agent|\n    home = ''\n    name = \"pl#{rand(999999).to_i}\"\n\n    teardown do\n      agent.user_absent(name)\n    end\n\n    step \"ensure the user is present\" do\n      agent.user_present(name)\n    end\n\n    step \"get home directory path\" do\n      on(agent, puppet_resource('user', name)) do |result|\n        info = result.stdout.match(/home\\s+=>\\s+'(.+)',/)\n        home = info[1] if info\n      end\n    end\n\n    step \"delete the user with managehome=true\" do\n      on(agent, puppet_resource('user', name, ['ensure=absent', 'managehome=true']))\n    end\n\n    step \"verify the user was deleted\" do\n      fail_test \"User '#{name}' was not deleted\" if agent.user_list.include?(name)\n    end\n\n    step \"verify the home directory was deleted\" do\n      skip_test(\"managehome parameter on Windows is not behaving as expected. See PUP-11202\") if agent['platform'] =~ /windows/\n      on(agent, \"test -d #{home}\", :acceptable_exit_codes => [1])\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_manage_attributes_aix.rb",
    "content": "test_name \"should correctly manage the attributes property for the User resource (AIX only)\" do\n  confine :to, :platform => /aix/\n  \n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n  \n  require 'puppet/acceptance/aix_util'\n  extend Puppet::Acceptance::AixUtil\n\n  initial_attributes = {\n    'nofiles'       => 10000,\n    'fsize'         => 100000,\n    'data'          => 60000,\n  }\n  changed_attributes = {\n    'nofiles' => -1,\n    'data' => 40000\n  }\n\n  run_attribute_management_tests('user', :uid, initial_attributes, changed_attributes)\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_manage_groups.rb",
    "content": "test_name \"should correctly manage the groups property for the User resource\" do\n  # NOTE: These tests run for only some of our supported platforms.\n  # We should eventually update them to work with all of our\n  # supported platforms where managing the groups property makes\n  # sense.\n\n  confine :except, :platform => /windows/\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::BeakerUtils\n\n  def random_name\n    \"pl#{rand(999999).to_i}\"\n  end\n\n  def user_manifest(user, params)\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\nuser { '#{user}':\n  #{params_str}\n}\nMANIFEST\n  end\n\n  def groups_of(host, user)\n    # The setup step should have already set :privatebindir on the\n    # host. We only include the default here to make this routine\n    # work for local testing, which sometimes skips the setup step.\n    privatebindir = host.has_key?(:privatebindir) ? host[:privatebindir] : '/opt/puppetlabs/puppet/bin'\n\n    # This bit of code reads the user's groups from the /etc/group file.\n    result = on(host, \"#{privatebindir}/ruby -e \\\"require 'puppet'; puts(Puppet::Util::POSIX.groups_of('#{user}').to_s)\\\"\")\n    Kernel.eval(result.stdout.chomp)\n  end\n\n  agents.each do |agent|\n    groups = 5.times.collect { random_name }\n    groups.each { |group| agent.group_absent(group) }\n\n    # We want to ensure that Beaker destroys the user first before\n    # the groups. Otherwise the teardown step will fail b/c we will\n    # be trying to remove the user's primary group before removing\n    # the user.\n    user = random_name\n    agent.user_absent(user)\n    teardown { agent.user_absent(user) }\n\n    step 'Creating the Groups' do\n      groups.each do |group|\n        agent.group_present(group)\n        teardown { agent.group_absent(group) }\n      end\n    end\n\n    user_groups = [groups[0], groups[1]]\n    primary_group = groups[2]\n\n    step 'Ensure that the user is created with the specified groups' do\n      # We use inclusive membership to ensure that the user's only a member\n      # of our groups and no other group.\n      manifest = user_manifest(user, groups: user_groups, gid: primary_group, membership: :inclusive)\n      apply_manifest_on(agent, manifest)\n      assert_matching_arrays(user_groups, groups_of(agent, user), \"The user was not successfully created with the specified groups!\")\n    end\n\n    step \"Verify that Puppet errors when one of the groups does not exist\" do\n      manifest = user_manifest(user, groups: ['nonexistent_group'])\n      apply_manifest_on(agent, manifest) do |result|\n        assert_match(/Error:.*#{user}/, result.stderr, \"Puppet fails to report an error when one of the groups in the groups property does not exist\")\n      end\n    end\n\n    primary_group = groups[3]\n    step \"Verify that modifying the primary group does not change the user's groups\" do\n      manifest = user_manifest(user, gid: primary_group)\n      apply_manifest_on(agent, manifest)\n      assert_matching_arrays(user_groups, groups_of(agent, user), \"The user's groups changed after modifying the primary group\")\n    end\n\n    step \"Verify that Puppet noops when the user's groups are already set\" do\n      manifest = user_manifest(user, groups: user_groups)\n      apply_manifest_on(agent, manifest, catch_changes: true)\n      assert_matching_arrays(user_groups, groups_of(agent, user), \"The user's groups somehow changed despite Puppet reporting a noop\")\n    end\n\n    step \"Verify that Puppet enforces minimum group membership\" do\n      new_groups = [groups[2], groups[4]]\n\n      manifest = user_manifest(user, groups: new_groups, membership: :minimum)\n      apply_manifest_on(agent, manifest)\n\n      user_groups += new_groups\n      assert_matching_arrays(user_groups, groups_of(agent, user), \"Puppet fails to enforce minimum group membership\")\n    end\n\n    if agent['platform'] =~ /osx/\n      skip_test \"User provider on OSX fails to enforce inclusive group membership, so we will skip that test until this is fixed. See PUP-9160.\"\n    end\n\n    step \"Verify that Puppet enforces inclusive group membership\" do\n      user_groups = [groups[0]]\n\n      manifest = user_manifest(user, groups: user_groups, membership: :inclusive)\n      apply_manifest_on(agent, manifest)\n      assert_matching_arrays(user_groups, groups_of(agent, user), \"Puppet fails to enforce inclusive group membership\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_manage_purge_ssh_keys.rb",
    "content": "test_name 'should manage purge_ssh_keys' do\n  tag 'audit:high',\n      'audit:acceptance'\n\n  # MODULES-11236\n  skip_test('This test does not work on Windows nor macOS') if agent['platform'] =~ /windows/ || agent['platform'] =~ /osx/\n\n  name = \"usr#{rand(9999).to_i}\"\n\n  agents.each do |agent|\n    home = agent.tmpdir(name)\n    authorized_keys_path = \"#{home}/.ssh/authorized_keys\"\n\n    teardown do\n      agent.rm_rf(home)\n      on(agent, puppet_resource('user', \"#{name}\", 'ensure=absent'))\n    end\n\n    step \"create user #{name} with ssh keys purged\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n          user {'#{name}':\n            ensure => present,\n            home => '#{home}',\n            purge_ssh_keys => true\n          }\n        MANIFEST\n      end\n\n      on(agent, puppet(\"resource user #{name} --to_yaml\")) do |result|\n        resource = YAML.load(result.stdout)\n        assert_match('present', resource['user'][name]['ensure'])\n      end\n    end\n\n    step \"ensure home ownership\" do\n      on(agent, \"chown -R #{name} #{home}\")\n    end\n\n    step \"add ssh keys\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n        ssh_authorized_key { '#{name}@example.com':\n          ensure => present,\n          user   => '#{name}',\n          type   => 'ssh-rsa',\n          key    => 'my-key'\n        }\n        MANIFEST\n      end\n\n      on(agent, \"cat #{authorized_keys_path}\") do |result|\n        assert_match(/ssh-rsa my-key #{name}@example.com/, result.stdout)\n      end\n    end\n\n    step \"purge ssh keys\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n          user {'#{name}':\n            purge_ssh_keys => true\n          }\n        MANIFEST\n      end\n\n      on(agent, \"cat #{authorized_keys_path}\") do |result|\n        refute_match(/ssh-rsa my-key #{name}@example.com/, result.stdout)\n      end\n    end\n\n    step \"add ssh keys\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n        ssh_authorized_key { '#{name}@example.com':\n          ensure => present,\n          user   => '#{name}',\n          type   => 'ssh-rsa',\n          key    => 'my-key'\n        }\n        MANIFEST\n      end\n\n      on(agent, \"cat #{authorized_keys_path}\") do |result|\n        assert_match(/ssh-rsa my-key #{name}@example.com/, result.stdout)\n      end\n    end\n\n    step \"purge ssh keys when purge_ssh_keys has relative path to home\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n          user {'#{name}':\n            purge_ssh_keys => '~/.ssh/authorized_keys'\n          }\n        MANIFEST\n\n      end\n\n      on(agent, \"cat #{authorized_keys_path}\") do |result|\n        refute_match(/ssh-rsa my-key #{name}@example.com/, result.stdout)\n      end\n    end\n\n    step \"add ssh keys\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n        ssh_authorized_key { '#{name}@example.com':\n          ensure => present,\n          user   => '#{name}',\n          type   => 'ssh-rsa',\n          key    => 'my-key'\n        }\n        MANIFEST\n      end\n\n      on(agent, \"cat #{authorized_keys_path}\") do |result|\n        assert_match(/ssh-rsa my-key #{name}@example.com/, result.stdout)\n      end\n    end\n\n    step \"purge ssh keys when purge_ssh_keys has absolute path\" do\n      apply_manifest_on(agent, <<-MANIFEST, { :catch_failures => true, :debug => true }) do |result|\n          user {'#{name}':\n            purge_ssh_keys => '#{authorized_keys_path}'\n          }\n        MANIFEST\n      end\n\n      on(agent, \"cat #{authorized_keys_path}\") do |result|\n        refute_match(/ssh-rsa my-key #{name}@example.com/, result.stdout)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_manage_roles_on_windows.rb",
    "content": "test_name \"Manage roles for a Windows user\" do\n  confine :to, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'puppet/acceptance/windows_utils'\n  extend Puppet::Acceptance::WindowsUtils\n\n  def user_manifest(name, params)\n    params_str = params.map do |param, value|\n      value_str = value.to_s\n      value_str = \"\\\"#{value_str}\\\"\" if value.is_a?(String)\n\n      \"  #{param} => #{value_str}\"\n    end.join(\",\\n\")\n\n    <<-MANIFEST\nuser { '#{name}':\n  #{params_str}\n}\nMANIFEST\n  end\n\n  newUser = \"tempUser#{rand(999999).to_i}\"\n\n  teardown do\n    on(agent, puppet(\"resource user #{newUser} ensure=absent\")) do |result|\n      assert_match(/User\\[#{newUser}\\]\\/ensure: removed/, result.stdout)\n    end\n  end\n\n  agents.each do |agent|\n    step \"Create a new user named #{newUser}\" do\n      apply_manifest_on(agent, user_manifest(newUser, ensure: :present), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/ensure: created/, result.stdout)\n      end\n    end\n\n    step \"Verify that a new user has no roles\" do\n      on(agent, puppet(\"resource user #{newUser}\")) do |result|\n        refute_match(/roles\\s+=>/, result.stdout)\n      end\n    end\n\n    step \"Verify that puppet can grant #{newUser} a right\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeServiceLogonRight']), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/roles: roles changed  to 'SeServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step \"Verify that puppet can grant #{newUser} a privilege also\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeBackupPrivilege']), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/roles: roles changed SeServiceLogonRight to 'SeBackupPrivilege,SeServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step \"Verify that puppet can not grant #{newUser} an invalid role\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['InvalidRoleName']), :acceptable_exit_codes => [4], catch_changes: true) do |result|\n        assert_match(/Calling `LsaAddAccountRights` returned 'Win32 Error Code 0x00000521'. One or more of the given rights\\/privilleges are incorrect./, result.stderr)\n      end\n    end\n\n    step \"Verify that puppet can remove all of #{newUser}'s roles when managing :roles as an empty array and :role_membership as inclusive\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: [], role_membership: :inclusive), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/roles: roles changed SeBackupPrivilege,SeServiceLogonRight to ''/, result.stdout)\n      end\n    end\n\n    step \"Verify that puppet can grant #{newUser} more than one right at the same time\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeDenyServiceLogonRight', 'SeDenyBatchLogonRight']), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/roles: roles changed  to 'SeDenyBatchLogonRight,SeDenyServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step \"Verify that :role_membership managed as minimum just appends given role to existing ones\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeBackupPrivilege'], role_membership: :minimum), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/roles: roles changed SeDenyServiceLogonRight,SeDenyBatchLogonRight to 'SeBackupPrivilege,SeDenyBatchLogonRight,SeDenyServiceLogonRight'/, result.stdout)\n      end\n    end\n\n    step \"Verify that :roles noops when #{newUser} already has given role while managing :role_membership as minimum\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeBackupPrivilege'], role_membership: :minimum), catch_changes: true) do |result|\n        refute_match(/User\\[#{newUser}\\]\\/roles: roles changed/, result.stdout)\n      end\n    end\n\n    step \"Verify that while not managing :role_membership, the behaviour remains the same, with noop from :roles when #{newUser} already has the given role\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeBackupPrivilege']), catch_changes: true) do |result|\n        refute_match(/User\\[#{newUser}\\]\\/roles: roles changed/, result.stdout)\n      end\n    end\n\n    step \"Verify that while managing :role_membership as inclusive, #{newUser} remains only with the given roles\" do\n      apply_manifest_on(agent, user_manifest(newUser, roles: ['SeBackupPrivilege', 'SeServiceLogonRight'], role_membership: :inclusive), expect_changes: true) do |result|\n        assert_match(/User\\[#{newUser}\\]\\/roles: roles changed SeBackupPrivilege,SeDenyServiceLogonRight,SeDenyBatchLogonRight to 'SeBackupPrivilege,SeServiceLogonRight'/, result.stdout)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_manage_shell.rb",
    "content": "test_name \"should manage user shell\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nname = \"pl#{rand(999999).to_i}\"\n\nconfine :except, :platform => 'windows'\n\nagents.each do |agent|\n  step \"ensure the user and group do not exist\"\n  agent.user_absent(name)\n  agent.group_absent(name)\n\n  step \"create the user with shell\"\n  shell = '/bin/sh'\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"shell=#{shell}\"])\n\n  step \"verify the user shell matches the managed shell\"\n  agent.user_get(name) do |result|\n    fail_test \"didn't set the user shell for #{name}\" unless result.stdout.include? shell\n  end\n\n  step \"modify the user with shell\"\n\n  # We need to use an allowed shell in AIX, as according to `/etc/security/login.cfg`\n  if agent['platform'] =~ /aix/\n    shell = '/bin/ksh'\n  else\n    shell = '/bin/bash'\n  end\n\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"shell=#{shell}\"])\n\n  step \"verify the user shell matches the managed shell\"\n  agent.user_get(name) do |result|\n    fail_test \"didn't set the user shell for #{name}\" unless result.stdout.include? shell\n  end\n\n  step \"delete the user, and group, if any\"\n  agent.user_absent(name)\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_modify.rb",
    "content": "test_name \"should modify a user\"\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure the user is present\"\n  agent.user_present(name)\n\n  step \"modify the user\"\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"comment=comment#{name}\"])\n\n  step \"verify the user was modified\"\n  agent.user_get(name) do |result|\n    fail_test \"didn't modify the user #{name}\" unless result.stdout.include? \"comment#{name}\"\n  end\n\n  step \"delete the user\"\n  agent.user_absent(name)\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_modify_gid.rb",
    "content": "test_name \"verify that we can modify the gid\"\nconfine :except, :platform => 'windows'\nconfine :except, :platform => /aix/ # PUP-5358\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nuser = \"u#{rand(99999).to_i}\"\ngroup1 = \"#{user}o\"\ngroup2 = \"#{user}n\"\n\nagents.each do |host|\n  step \"ensure that the groups both exist\"\n  on(host, puppet_resource('group', group1, 'ensure=present'))\n  on(host, puppet_resource('group', group2, 'ensure=present'))\n\n  step \"ensure the user exists and has the old group\"\n  on(host, puppet_resource('user', user, 'ensure=present', \"gid=#{group1}\"))\n\n  step \"verify that the user has the correct gid\"\n  group_gid1 = host.group_gid(group1)\n  host.user_get(user) do |result|\n    user_gid1 = result.stdout.split(':')[3]\n\n    fail_test \"didn't have the expected old GID #{group_gid1}, but got: #{user_gid1}\" unless group_gid1 == user_gid1\n  end\n\n  step \"modify the GID of the user\"\n  on(host, puppet_resource('user', user, 'ensure=present', \"gid=#{group2}\"))\n\n  step \"verify that the user has the updated gid\"\n  group_gid2 = host.group_gid(group2)\n  host.user_get(user) do |result|\n    user_gid2 = result.stdout.split(':')[3]\n\n    fail_test \"didn't have the expected old GID #{group_gid}, but got: #{user_gid2}\" unless group_gid2 == user_gid2\n  end\n\n  step \"ensure that we remove the things we made\"\n  on(host, puppet_resource('user',  user,   'ensure=absent'))\n  on(host, puppet_resource('group', group1, 'ensure=absent'))\n  on(host, puppet_resource('group', group2, 'ensure=absent'))\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_modify_gid_forcelocal.rb",
    "content": "test_name \"verify that we can modify the gid with forcelocal\" do\n  confine :to, :platform => /el|fedora/ # PUP-5358\n\n  require 'puppet/acceptance/common_utils'\n  extend Puppet::Acceptance::PackageUtils\n  extend Puppet::Acceptance::ManifestUtils\n\n  tag 'audit:high'\n\n  user = \"u#{rand(99999).to_i}\"\n  group1 = \"#{user}o\"\n  group2 = \"#{user}n\"\n\n  agents.each do |host|\n    teardown do\n      on(host, puppet_resource('user',  user,   'ensure=absent'))\n      on(host, puppet_resource('group', group1, 'ensure=absent'))\n      on(host, puppet_resource('group', group2, 'ensure=absent'))\n    end\n\n    step \"ensure that the groups both exist\" do\n      on(host, puppet_resource('group', group1, 'ensure=present'))\n      on(host, puppet_resource('group', group2, 'ensure=present'))\n    end\n\n    step \"ensure the user exists and has the old group\" do\n      apply_manifest_on(agent, resource_manifest('user', user, ensure: 'present', gid: group1, forcelocal: true))\n    end\n\n    step \"verify that the user has the correct gid\" do\n      group_gid1 = host.group_gid(group1)\n      host.user_get(user) do |result|\n        user_gid1 = result.stdout.split(':')[3]\n\n        fail_test \"didn't have the expected old GID #{group_gid1}, but got: #{user_gid1}\" unless group_gid1 == user_gid1\n      end\n    end\n\n    step \"modify the GID of the user\" do\n      apply_manifest_on(agent, resource_manifest('user', user, ensure: 'present', gid: group2, forcelocal: true), expect_changes: true)\n    end\n\n    step \"verify that the user has the updated gid\" do\n      group_gid2 = host.group_gid(group2)\n      host.user_get(user) do |result|\n        user_gid2 = result.stdout.split(':')[3]\n\n        fail_test \"didn't have the expected old GID #{group_gid}, but got: #{user_gid2}\" unless group_gid2 == user_gid2\n      end\n    end\n\n    step \"run again for idempotency\" do\n      apply_manifest_on(agent, resource_manifest('user', user, ensure: 'present', gid: group2, forcelocal: true), catch_changes: true)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_modify_when_not_managing_home.rb",
    "content": "test_name \"should modify a user when no longer managing home (#20726)\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nrequire 'puppet/acceptance/windows_utils'\nextend Puppet::Acceptance::WindowsUtils\n\nname = \"pl#{rand(999999).to_i}\"\npw = \"Passwrd-#{rand(999999).to_i}\"[0..11]\n\ndef get_home_dir(host, user_name)\n  home_dir = nil\n  on host, puppet_resource('user', user_name) do |result|\n    home_dir = result.stdout.match(/home\\s*=>\\s*'([^']+)'/m)[1]\n  end\n  home_dir\nend\n\nagents.each do |agent|\n  home_prop = nil\n  case agent['platform']\n  when /windows/\n    # Sadly Windows ADSI won't tell us the default home directory\n    # for a user. You can get it via WMI Win32_UserProfile, but that\n    # doesn't exist in a base 2003 install. So we simply specify an\n    # initial home directory, that matches what the default will be.\n    # This way we are guaranteed that `puppet resource user name`\n    # will include the home directory in its output.\n    home_prop = \"home='#{profile_base(agent)}\\\\#{name}'\"\n  when /solaris/\n    pending_test(\"managehome needs work on solaris\")\n  when /osx/\n    skip_test(\"OSX doesn't support managehome\")\n    # we don't get here\n  end\n\n  teardown do\n    step \"delete the user\"\n    agent.user_absent(name)\n    agent.group_absent(name)\n  end\n\n  step \"ensure the user is present with managehome\"\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"managehome=true\", \"password=#{pw}\", home_prop].compact)\n\n  step \"find the current home dir\"\n  home_dir = get_home_dir(agent, name)\n  on agent, \"test -d '#{home_dir}'\"\n\n  step \"modify the user\"\n  new_home_dir = \"#{home_dir}_foo\"\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"home='#{new_home_dir}'\"]) do |result|\n    found_home_dir = result.stdout.match(/home\\s*=>\\s*'([^']+)'/m)[1]\n    assert_equal new_home_dir, found_home_dir, \"Failed to change home property of user\"\n  end\n\n  step \"verify that home directory still exists since we did not specify managehome\"\n  on agent, \"test -d '#{home_dir}'\"\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_modify_while_managing_home.rb",
    "content": "test_name \"should modify a user without changing home directory (pending #19542)\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nrequire 'puppet/acceptance/windows_utils'\nextend Puppet::Acceptance::WindowsUtils\n\nname = \"pl#{rand(999999).to_i}\"\npw = \"Passwrd-#{rand(999999).to_i}\"[0..11]\n\ndef get_home_dir(host, user_name)\n  home_dir = nil\n  on host, puppet_resource('user', user_name) do |result|\n    home_dir = result.stdout.match(/home\\s*=>\\s*'([^']+)'/m)[1]\n  end\n  home_dir\nend\n\nagents.each do |agent|\n  home_prop = nil\n  case agent['platform']\n  when /windows/\n    home_prop = \"home='#{profile_base(agent)}\\\\#{name}'\"\n  when /solaris/\n    pending_test(\"managehome needs work on solaris\")\n  when /osx/\n    skip_test(\"OSX doesn't support managehome\")\n    # we don't get here\n  end\n\n  teardown do\n    step \"delete the user\"\n    agent.user_absent(name)\n    agent.group_absent(name)\n  end\n\n  step \"ensure the user is present with managehome\"\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"managehome=true\", \"password=#{pw}\", home_prop].compact)\n\n  step \"find the current home dir\"\n  home_dir = get_home_dir(agent, name)\n\n  step \"modify the user\"\n  on agent, puppet_resource('user', name, [\"ensure=present\", \"managehome=true\", \"home='#{home_dir}_foo'\"]) do |result|\n    # SHOULD: user resource output should contain the new home directory\n    pending_test \"when #19542 is reimplemented correctly\"\n  end\n  # SHOULD: old home directory should not exist in the filesystem\n  # SHOULD: new home directory should exist in the filesystem\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_not_create_existing.rb",
    "content": "test_name \"tests that user resource will not add users that already exist.\" do\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  user  = \"u#{rand(999999).to_i}\"\n  group = \"g#{rand(999999).to_i}\"\n\n  teardown do\n    agents.each do |agent|\n      agent.user_absent(user)\n      agent.group_absent(group)\n    end\n  end\n\n  step \"Setup: Create test user and group\" do\n    agents.each do |agent|\n      agent.user_present(user)\n      agent.group_present(group)\n    end\n  end\n\n  step \"verify that we don't try to create a user account that already exists\" do\n    agents.each do |agent|\n      on(agent, puppet_resource('user', user, 'ensure=present')) do |result|\n        fail_test \"tried to create '#{user}' user\" if result.stdout.include? 'created'\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_not_destroy_unexisting.rb",
    "content": "test_name \"ensure that puppet does not report removing a user that does not exist\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nname = \"pl#{rand(999999).to_i}\"\n\nstep \"verify that user #{name} does not exist\"\nagents.each do |agent|\n  agent.user_absent(name)\nend\n\nstep \"ensure absent doesn't try and do anything\"\non(agents, puppet_resource('user', name, 'ensure=absent')) do |result|\n  fail_test \"tried to remove the user, apparently\" if result.stdout.include? 'removed'\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_not_modify_disabled.rb",
    "content": "test_name 'PUP-6586 Ensure puppet does not continually reset password for disabled user' do\n\n  confine :to, :platform => 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  name = \"pl#{rand(99999).to_i}\"\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet_resource('user', \"#{name}\", 'ensure=absent'))\n    end\n  end\n\n  manifest = <<-MANIFEST\nuser {'#{name}':\n  ensure    => present,\n  password  => 'P@ssword!',\n}\nMANIFEST\n\n  agents.each do |agent|\n    step \"create user #{name} with puppet\" do\n      apply_manifest_on(agent, manifest, :catch_failures => true)\n    end\n\n    step \"disable user #{name}\" do\n      on(agent, \"net user #{name} /ACTIVE:NO\", :acceptable_exit_codes => 0)\n    end\n\n    step \"test that password is not reset by puppet\" do\n      # :catch_changes will fail the test if there were changes during\n      # run execution\n      apply_manifest_on(agent, manifest, :catch_changes => true)\n    end\n  end\nend\n\n\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_purge.rb",
    "content": "test_name \"should purge a user\" do\n  # Until purging works on AIX, Solaris, and OSX. See PUP-9188\n  confine :except, :platform => /^aix/\n  confine :except, :platform => /^solaris/\n  confine :except, :platform => /^osx/\n  tag 'audit:high',\n      'audit:acceptance'\n\n  agents.each do |agent|\n    unmanaged = \"unmanaged-#{rand(999999).to_i}\"\n    managed = \"managed-#{rand(999999).to_i}\"\n    step \"ensure that the unmanaged and managed users do not exist\" do\n      agent.user_absent(unmanaged)\n      agent.user_absent(managed)\n    end\n\n    step \"create the unmanaged user\" do\n      on agent, puppet_resource('user', unmanaged, 'ensure=present')\n    end\n\n    step \"verify the user exists\" do\n      assert(agent.user_list.include?(unmanaged), \"Unmanaged user was not created\")\n    end\n\n    step \"create the managed user and purge unmanaged users\" do\n      manifest = %Q|\n      user {'#{managed}':\n        ensure => present\n      }\n      resources { 'user':\n        purge => true,\n        unless_system_user => true\n      }|\n      apply_manifest_on(agent, manifest)\n    end\n\n    step \"verify the unmanaged user is purged\" do\n      assert(!agent.user_list.include?(unmanaged), \"Unmanaged user was not purged\")\n    end\n\n    step \"verify managed user is not purged\" do\n      assert(agent.user_list.include?(managed), \"Managed user was purged\")\n    end\n\n    step \"verify system user is not purged\" do\n      if agent['platform'] =~ /windows/\n        win_admin_user = agent['locale'] == 'fr' ? \"Administrateur\" : \"Administrator\"\n        assert(agent.user_list.include?(win_admin_user), \"System user (Administrator) was purged\")\n      else\n        assert(agent.user_list.include?(\"root\"), \"System user (root) was purged\")\n      end\n    end\n\n    teardown do\n      agent.user_absent(unmanaged)\n      agent.user_absent(managed)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_query.rb",
    "content": "test_name \"test that we can query and find a user that exists.\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:acceptance' # Could be done as integration tests, but would\n                       # require changing the system running the test\n                       # in ways that might require special permissions\n                       # or be harmful to the system running the test\n\nname = \"pl#{rand(999999).to_i}\"\n\nagents.each do |agent|\n  step \"ensure that our test user exists\"\n  agent.user_present(name)\n\n  step \"query for the resource and verify it was found\"\n  on(agent, puppet_resource('user', name)) do |result|\n    fail_test \"didn't find the user #{name}\" unless result.stdout.include? 'present'\n  end\n\n  step \"clean up the user and group we added\"\n  agent.user_absent(name)\n  agent.group_absent(name)\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/should_query_all.rb",
    "content": "test_name \"should query all users\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_run`\n    'audit:integration'\n\nagents.each do |agent|\n  next if agent == master\n  skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr'\n\n  step \"query natively\"\n  users = agent.user_list\n\n  fail_test(\"No users found\") unless users\n\n  step \"query with puppet\"\n  on(agent, puppet_resource('user')) do |result|\n    result.stdout.each_line do |line|\n      name = ( line.match(/^user \\{ '([^']+)'/) or next )[1]\n\n      unless users.delete(name)\n        fail_test \"user #{name} found by puppet, not natively\"\n      end\n    end\n  end\n\n  if users.length > 0 then\n    fail_test \"#{users.length} users found natively, not puppet: #{users.join(', ')}\"\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/resource/user/utf8_user_comments.rb",
    "content": "# Ensure a user can be managed with UTF-8 comment value. The comment value is\n# normally used for \"Full Name\" so this is important in a UTF-8 context.\n# We should be able to:\n# - create a new user with a UTF-8 comment\n# - modify an existing UTF-8 comment to a new UTF-8 comment\n# - modify an existing UTF-8 comment to an ASCII comment\n# - create a new user with an ASCII comment\n# - modify an existing ASCII comment to a UTF-8 one\n# Where applicable, we should be able to do this in different locales\ntest_name 'PUP-6777 Manage users with UTF-8 comments' do\n\n  tag 'audit:high',\n      'audit:acceptance' # Could be done as integration tests, but would\n                         # require changing the system running the test\n                         # in ways that might require special permissions\n                         # or be harmful to the system running the test\n\n  # AIX providers are separate from most other platforms,\n  # and have not been made unicode-aware yet.\n  confine :except, :platform => /aix/\n\n  user0 = \"foo#{rand(99999).to_i}\"\n  user1 = \"bar#{rand(99999).to_i}\"\n  user2 = \"baz#{rand(99999).to_i}\"\n  user3 = \"qux#{rand(99999).to_i}\"\n  user4 = \"ris#{rand(99999).to_i}\"\n  osx_agents = agents.select { |a| a[:platform] =~ /osx/ } || []\n  windows_agents = agents.select { |a| a[:platform] =~ /windows/ } || []\n\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  mixed_utf8_0 = \"A\\u06FF\"\n  reported_mixed_utf8_0 = '\\'A\\u{6FF}\\''\n  mixed_utf8_1 = \"\\u16A0\\u{2070E}\"\n  reported_mixed_utf8_1 = '\\'\\u{16A0}\\u{2070E}\\''\n\n  teardown do\n    # remove user on all agents\n    teardown_manifest = <<-EOF\n      user { ['#{user0}','#{user1}','#{user2}','#{user3}','#{user4}']: ensure => absent }\n    EOF\n    apply_manifest_on(agents, teardown_manifest, :environment => {:LANG => \"en_US.UTF-8\"})\n  end\n\n  step \"ensure user can be created with UTF-8 comment (with UTF-8 environment on *nix)\" do\n    create_user = <<-EOF\n      user { '#{user0}':\n        ensure  => present,\n        comment => '#{mixed_utf8_0}',\n      }\n    EOF\n    # Note setting LANG='<encoding>' environment like this has no effect on Windows agents.\n    apply_manifest_on(agents, create_user, :expect_changes => true, :environment => {:LANG => \"en_US.UTF-8\"})\n  end\n\n  step \"ensure UTF-8 comment can be changed (with UTF-8 environment on *nix)\" do\n    set_comment_utf8 = <<-EOF\n      user { '#{user0}':\n        comment => '#{mixed_utf8_1}',\n      }\n    EOF\n    # Note setting LANG='<encoding>' environment like this has no effect on Windows agents.\n    apply_manifest_on(agents, set_comment_utf8, :expect_changes => true, :environment => {:LANG => \"en_US.UTF-8\"}) do |result|\n      assert_match(/changed #{reported_mixed_utf8_0} to #{reported_mixed_utf8_1}/, result.stdout, \"failed to modify UTF-8 user comment in UTF-8 environment\")\n    end\n  end\n\n  # *NIX and OSX should also work with ISO-8859-1 (at least, let's make sure we don't regress)\n  step \"ensure user can be created with UTF-8 comment (with ISO-8859-1 environment on *nix)\" do\n    create_user = <<-EOF\n      user { '#{user1}':\n        ensure  => present,\n        comment => '#{mixed_utf8_0}',\n      }\n    EOF\n    # Since LANG=<'encoding'> has no effect, this test is redundant on Windows - exclude it.\n    apply_manifest_on(agents - windows_agents, create_user, :expect_changes => true, :environment => {:LANG => \"en_US.ISO8859-1\"})\n  end\n\n  step \"ensure UTF-8 comment can be changed (with ISO-8859-1 environment on *nix)\" do\n    set_comment_utf8 = <<-EOF\n      user { '#{user1}':\n        comment => '#{mixed_utf8_1}',\n      }\n    EOF\n    # Since LANG=<'encoding'> has no effect, this test is redundant on Windows - exclude it.\n    apply_manifest_on(agents - windows_agents, set_comment_utf8, :expect_changes => true, :environment => {:LANG => \"en_US.ISO8859-1\"}) do |result|\n      assert_match(/changed #{reported_mixed_utf8_0} to #{reported_mixed_utf8_1}/, result.stdout, \"failed to modify UTF-8 user comment in ISO-8859-1 environment\")\n    end\n  end\n\n  step \"ensure user can be created with UTF-8 comment (with POSIX locale on *nix)\" do\n    create_user = <<-EOF\n      user { '#{user2}':\n        ensure  => present,\n        comment => '#{mixed_utf8_0}',\n      }\n    EOF\n    # OS X is known broken in POSIX locale with UTF-8 chars on OS X, so exclude OS X here.\n    # Also since LANG=<'encoding'> has no effect, this test is redundant on Windows - exclude it.\n    apply_manifest_on(agents - osx_agents - windows_agents, create_user, :expect_changes => true, :environment => {:LANG => \"POSIX\"})\n  end\n\n  step \"ensure UTF-8 comment can be modifed (with POSIX locale on *nix)\" do\n    set_comment_utf8 = <<-EOF\n      user { '#{user2}':\n        ensure  => present,\n        comment => '#{mixed_utf8_1}',\n      }\n    EOF\n    # OS X is known broken in POSIX locale with UTF-8 chars on OS X, so exclude OS X here.\n    # Also since LANG=<'encoding'> has no effect, this test is redundant on Windows - exclude it.\n    apply_manifest_on(agents - osx_agents - windows_agents, set_comment_utf8, :expect_changes => true, :environment => {:LANG => \"POSIX\"}) do |result|\n      assert_match(/changed #{reported_mixed_utf8_0} to #{reported_mixed_utf8_1}/, result.stdout, \"failed to modify UTF-8 user comment with POSIX environment\")\n    end\n  end\n\n  step \"ensure user can be created with ASCII comment (with POSIX locale on *nix)\" do\n    create_user = <<-EOF\n      user { '#{user3}':\n        ensure => present,\n        comment => 'bar',\n      }\n    EOF\n\n    # While setting LANG='<encoding>' environment like this has no effect on\n    # Windows agents, we still want to run this and the following tests on Windows\n    # because we want to ensure we can set create/modify ASCII comments to UTF-8\n    # and back.\n    # OS X is known broken in POSIX locale with UTF-8 chars on OS X, so exclude OS X here.\n    apply_manifest_on(agents - osx_agents, create_user, :expect_changes => true, :environment => {:LANG => \"POSIX\"})\n  end\n\n\n  # This test is important because of ruby's Etc.getpwnam behavior which returns\n  # strings in current locale if compatible - make sure we can get a system\n  # value in POSIX and compare it to incoming from puppet in UTF-8.\n  step \"ensure ASCII comment can be modified to UTF-8 comment (with POSIX locale on *nix)\" do\n    set_comment_utf8 = <<-EOF\n      user { '#{user3}':\n        comment => '#{mixed_utf8_0}',\n      }\n    EOF\n    # While setting LANG='<encoding>' environment like this has no effect on\n    # Windows agents, we still want to run this and the following tests on Windows\n    # because we want to ensure we can set create/modify ASCII comments to UTF-8\n    # and back.\n    # OS X is known broken in POSIX locale with UTF-8 chars on OS X, so exclude OS X here.\n    apply_manifest_on(agents - osx_agents, set_comment_utf8, :expect_changes => true, :environment => {:LANG => \"POSIX\"}) do |result|\n      assert_match(/changed 'bar' to #{reported_mixed_utf8_0}/, result.stdout, \"failed to modify user ASCII comment to UTF-8 comment with POSIX locale\")\n    end\n  end\n\n  step \"create another user with UTF-8 comment (with POSIX locale on *nix)\" do\n    create_user = <<-EOF\n      user { '#{user4}':\n        ensure => present,\n        comment => '#{mixed_utf8_0}',\n      }\n    EOF\n    # While setting LANG='<encoding>' environment like this has no effect on\n    # Windows agents, we still want to run this and the following tests on Windows\n    # because we want to ensure we can set create/modify ASCII comments to UTF-8\n    # and back.\n    # OS X is known broken in POSIX locale with UTF-8 chars on OS X, so exclude OS X here.\n    apply_manifest_on(agents - osx_agents, create_user, :expect_changes => true, :environment => {:LANG => \"POSIX\"})\n  end\n\n\n  step \"ensure UTF-8 comment can be modified to ASCII comment (with POSIX locale on *nix)\" do\n    set_comment_ascii = <<-EOF\n      user { '#{user4}':\n        comment => 'bar',\n      }\n    EOF\n    # While setting LANG='<encoding>' environment like this has no effect on\n    # Windows agents, we still want to run this on Windows because we want to\n    # ensure we can set create/modify ASCII comments to UTF-8 and back.\n    # OS X is known broken in POSIX locale with UTF-8 chars, so exclude OS X here\n    apply_manifest_on(agents - osx_agents, set_comment_ascii, :expect_changes => true, :environment => {:LANG => \"POSIX\"}) do |result|\n      assert_match(/changed #{reported_mixed_utf8_0} to 'bar'/, result.stdout, \"failed to modify user UTF-8 comment to ASCII comment with POSIX locale\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/security/cve-2013-1640_facter_string.rb",
    "content": "# Setting a custom fact to \"string\" will overwrite a local variable during\n# template compilation on the master allowing remote code execution by\n# any authenticated client.\ntest_name \"CVE 2013-1640 Remote Code Execution\" do\n\n  tag 'audit:high',       # low risk, high (security) impact\n      'audit:integration'\n\n  confine :except, :platform => 'windows'\n\n  on agents, %q[ FACTER_string=\"<%= %x{ /bin/echo hax0rd }  %>\" ] +\n             %q[ puppet apply -e ] +\n             %q[ 'notice(inline_template(\"<%= \\\"I am Safe\\\" %>\"))' ] do |test|\n\n    assert_match(/I am Safe/, test.stdout)\n    refute_match(/hax0rd/, test.stdout)\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/security/cve-2013-1652_improper_query_params.rb",
    "content": "require 'json'\n\ntest_name \"CVE 2013-1652 Improper query parameter validation\" do\n  confine :except, :platform => 'windows'\n  confine :except, :platform => /osx/ # see PUP-4820\n  confine :except, :platform => /aix/\n\n  tag 'audit:high',        # risk low, high (security) impact\n      'audit:integration',\n      'server'\n\n  with_puppet_running_on master, {} do\n    # Ensure each agent has a signed cert\n    on agents, puppet('agent', \"-t\")\n\n    agents.each do |agent|\n      next if agent['roles'].include?( 'master' )\n\n      certname = on(agent, puppet('agent', \"--configprint certname\")).stdout.chomp\n\n      payload = \"https://#{master}:8140/puppet/v3/catalog/#{certname}\" +\n                \"?environment=production&use_node=\" +\n                \"---%20!ruby/object:Puppet::Node%0A%20%20\" +\n                \"name:%20#{master}%0A%20%20classes:%20\\[\\]%0A%20%20\" +\n                \"parameters:%20%7B%7D%0A%20%20facts:%20%7B%7D\"\n\n      cert_path = on(agent, puppet('agent', \"--configprint hostcert\")).stdout.chomp\n      key_path = on(agent, puppet('agent', \"--configprint hostprivkey\")).stdout.chomp\n      curl_base = \"curl --tlsv1 -g --cert \\\"#{cert_path}\\\" --key \\\"#{key_path}\\\" -k -H 'Accept: application/json'\"\n\n      curl_call =  \"#{curl_base} '#{payload}'\"\n\n      step \"Attempt to retrieve another nodes catalog\" do\n        on agent, curl_call do |test|\n          begin\n            res = JSON.parse( test.stdout )\n            fail_test( \"Retrieved catalog for #{master} from #{agent}\" ) if\n              res['data'] && res['data']['name'] == master.name\n          rescue JSON::ParserError\n            # good, continue\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/security/cve-2013-1652_poison_other_node_cache.rb",
    "content": "test_name \"CVE 2013-1652 Poison node cache\" do\n\n  tag 'audit:high',        # low risk, high (security) impact\n      'audit:integration', # master side only\n      'server'\n\n  step \"Determine suitability of the test\" do\n    skip_test( \"This test will only run on Puppet 3.x\" ) if\n      on(master, puppet('--version')).stdout =~ /\\A2\\./\n  end\n\n  with_puppet_running_on( master, {} ) do\n    # Ensure agent has a signed cert\n    on master, puppet('agent', '-t' )\n\n    certname = on(\n      master, puppet('agent', \"--configprint certname\")).stdout.chomp\n    cert_path = on(\n      master, puppet('agent', \"--configprint hostcert\")).stdout.chomp\n    key_path = on(\n      master, puppet('agent', \"--configprint hostprivkey\")).stdout.chomp\n\n    curl_base = \"curl --tlsv1 -g --cert \\\"#{cert_path}\\\" \" +\n                \"--key \\\"#{key_path}\\\" -k -H 'Accept: application/json'\"\n\n    step \"Attempt to poison the master's node cache\" do\n      yamldir = puppet_config(master, 'yamldir', section: 'master')\n      exploited = \"#{yamldir}/node/you.lose.yaml\"\n      on master, \"rm -rf #{exploited}\"\n      on master, \"rm -rf #{yamldir}/node/*\"\n      payload2 = \"https://#{master}:8140/puppet/v3/node/#{certname}?environment=production\" +\n                 \"&instance=---+%21ruby%2Fobject%3APuppet%3A%3ANode%0A+classes\" +\n                 \"%3A%0A+-+foo%0A+name%3A+you.lose%0A+parameters\" +\n                 \"%3A+%7B%7D%0A+time%3A+2013-02-28+15%3A12%3A30.367008+-08%3A00\"\n\n      on master, \"#{curl_base} '#{payload2}'\"\n\n      fail_test( \"Found exploit file #{exploited}\" ) if\n        on( master, \"[ ! -f #{exploited} ]\",\n           :acceptable_exit_codes => [0,1] ).exit_code == 1\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/security/cve-2013-2275_report_acl.rb",
    "content": "test_name \"(#19531) report save access control\"\n\ntag 'audit:high',        # low risk, high (security) impact\n    'audit:refactor',    # Use block style `test_name`\n    'audit:integration', # issue completely on the server side\n    'server'\n\nstep \"Verify puppet only allows saving reports from the node matching the certificate\"\n\nfake_report = <<-EOYAML\n--- !ruby/object:Puppet::Transaction::Report\n  host: mccune\n  metrics: {}\n  logs: []\n  puppet_version: \"2.7.20\"\n  status: failed\n  report_format: 3\nEOYAML\n\nwith_puppet_running_on(master, {}) do\n  submit_fake_report_cmd = [\n    \"curl --tlsv1 -k -X PUT\",\n    \"--cacert \\\"$(puppet config print --section server cacert)\\\"\",\n    \"--cert \\\"$(puppet config print --section server hostcert)\\\"\",\n    \"--key \\\"$(puppet config print --section server hostprivkey)\\\"\",\n    \"-H 'Content-Type: text/yaml'\",\n    \"-d '#{fake_report}'\",\n    \"\\\"https://#{master}:8140/puppet/v3/report/mccune?environment=production\\\"\",\n  ].join(\" \")\n\n  on(master, submit_fake_report_cmd, :acceptable_exit_codes => [0]) do |result|\n    msg = \"(#19531) (CVE-2013-2275) Puppet Server accepted a report for a node that does not match the certname\"\n    assert_match(/Forbidden request/, result.stdout, msg)\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/security/cve-2013-4761_injection_of_class_names_loading_code.rb",
    "content": "test_name \"CVE 2013-4761 Injection of bad class names causing code loading\" do\n  confine :except, :platform => 'windows'\n\n  tag 'audit:high',        # low risk, high (security) impact\n      'audit:integration', # issue is completely on the master side\n      'server'\n\n  testdir = create_tmpdir_for_user master, 'class-names-injection'\n  exploit_path = \"#{testdir}/exploit.rb\"\n  exploited_path = \"#{testdir}/exploited\"\n\n  # @return [String] path to the manifest file\n  def create_exploit_manifest(path, exploit_path_expression)\n    apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n      File {\n        ensure => directory,\n        mode => \"0770\",\n        owner => #{master.puppet['user']},\n        group => #{master.puppet['group']},\n      }\n\n      file {\n        '#{path}/environments':;\n        '#{path}/environments/production':;\n        '#{path}/environments/production/manifests':;\n        '#{path}/environments/production/manifests/site.pp':\n          ensure => file,\n          content => '\n$enc_data = \"#{exploit_path_expression}\"\ninclude $enc_data\n',\n          mode => \"0640\",\n      }\n    MANIFEST\n  end\n\n  def should_not_be_able_to_exploit(exploited_path)\n    agents.each do |agent|\n      next if agent['roles'].include?('master')\n\n      step \"Request a catalog to trigger the exploit\" do\n        on agent, puppet('agent', '-t'), :acceptable_exit_codes => [1]\n      end\n\n      step \"Check that the exploit marker was not created\" do\n        on master, \"test ! -e #{exploited_path}\"\n      end\n    end\n  end\n\n  step \"Create exploit file\" do\n    create_remote_file(master, exploit_path, <<-EXPLOIT)\n    ::File.open('#{exploited_path}', 'w') { |f| f.puts(\"exploited\") }\n    EXPLOIT\n\n    on(master, \"chmod 777 #{exploit_path}\")\n  end\n\n  master_opts = {\n    'main' => {\n      'environmentpath' => \"#{testdir}/environments\",\n    },\n  }\n\n  with_puppet_running_on(master, master_opts) do\n    step \"Class name is not interpreted as an absolute path\" do\n      create_exploit_manifest(testdir, 'tmp::exploit')\n      should_not_be_able_to_exploit(exploited_path)\n    end\n\n    step \"Class name cannot be used for a directory traversal out of the module path\" do\n      # This is just a guess about how far back we need to go...\n      traversal_exploit_expression = \"#{'::..' * 20}#{exploit_path.gsub(File::SEPARATOR,'::')}\"\n      create_exploit_manifest(testdir, traversal_exploit_expression)\n\n      should_not_be_able_to_exploit(exploited_path)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ssl/autosign_command.rb",
    "content": "test_name \"autosign command and csr attributes behavior (#7243,#7244)\" do\n  skip_test \"Test requires at least one non-master agent\" if hosts.length == 1\n\n  tag 'audit:high',        # cert/ca core behavior\n      'audit:integration',\n      'server'             # Ruby implementation is deprecated\n\n  def assert_key_generated(name, stdout)\n    assert_match(/Creating a new RSA SSL key for #{name}/, stdout, \"Expected agent to create a new SSL key for autosigning\")\n  end\n\n  testdirs = {}\n  test_certnames = []\n\n  step \"Generate tmp dirs on all hosts\" do\n    hosts.each do |host|\n      testdirs[host] = host.tmpdir('autosign_command')\n      on(host, \"chmod 755 #{testdirs[host]}\")\n    end\n  end\n\n  hostname = master.execute('facter hostname')\n  fqdn = master.execute('facter fqdn')\n\n  teardown do\n    step \"clear test certs\"\n    master_opts = {\n      'main' => { 'server' => fqdn },\n      'master' => { 'dns_alt_names' => \"puppet,#{hostname},#{fqdn}\" }\n    }\n    with_puppet_running_on(master, master_opts) do\n      on(master,\n         \"puppetserver ca clean --certname #{test_certnames.join(',')}\",\n         :acceptable_exit_codes => [0,24])\n    end\n  end\n\n  step \"Step 1: ensure autosign command can approve CSRs\" do\n\n    # Our script needs to consume stdin (SERVER-1116)\n    # We should revert back to /bin/true once resolved\n    autosign_true_script = <<-EOF\n!#/bin/bash\nwhile read line\ndo\n    : # noop command\ndone\nexit 0\nEOF\n\n    autosign_true_script_path = \"#{testdirs[master]}/mytrue\"\n    create_remote_file(master, autosign_true_script_path, autosign_true_script)\n    on(master, \"chmod 777 #{autosign_true_script_path}\")\n\n    master_opts = {\n      'master' => {\n        'autosign' => autosign_true_script_path,\n        'dns_alt_names' => \"puppet,#{hostname},#{fqdn}\"\n      }\n    }\n    with_puppet_running_on(master, master_opts) do\n      agents.each do |agent|\n        next if agent == master\n\n        test_certnames << (certname = \"#{agent}-autosign\")\n        on(agent, puppet(\"agent --test\",\n                  \"--waitforcert 0\",\n                  \"--ssldir\", \"'#{testdirs[agent]}/ssldir-autosign'\",\n                  \"--certname #{certname}\"), :acceptable_exit_codes => [0,2]) do |result|\n          unless agent['locale'] == 'ja'\n            assert_key_generated(agent, result.stdout)\n            assert_match(/Downloaded certificate for #{agent}/, result.stdout, \"Expected certificate to be autosigned\")\n          end\n        end\n      end\n    end\n  end\n\n  step \"Step 2: ensure autosign command can reject CSRs\" do\n\n    # Our script needs to consume stdin (SERVER-1116)\n    # We should revert back to /bin/false once resolved\n    autosign_false_script = <<-EOF\n!#/bin/bash\nwhile read line\ndo\n    : # noop command\ndone\nexit 1\nEOF\n\n    autosign_false_script_path = \"#{testdirs[master]}/myfalse\"\n    create_remote_file(master, autosign_false_script_path, autosign_false_script)\n    on(master, \"chmod 777 #{autosign_false_script_path}\")\n\n    master_opts = {\n      'master' => {\n        'autosign' => autosign_false_script_path,\n        'dns_alt_names' => \"puppet,#{hostname},#{fqdn}\"\n      }\n    }\n    with_puppet_running_on(master, master_opts) do\n      agents.each do |agent|\n        next if agent == master\n\n        test_certnames << (certname = \"#{agent}-reject\")\n        on(agent, puppet(\"agent --test\",\n                        \"--waitforcert 0\",\n                        \"--ssldir\", \"'#{testdirs[agent]}/ssldir-reject'\",\n                        \"--certname #{certname}\"), :acceptable_exit_codes => [1]) do |result|\n          unless agent['locale'] == 'ja'\n            assert_key_generated(agent, result.stdout)\n            assert_match(/Certificate for #{agent}-reject has not been signed yet/, result.stdout, \"Expected certificate to not be autosigned\")\n          end\n        end\n      end\n    end\n  end\n\n  autosign_inspect_csr_path = \"#{testdirs[master]}/autosign_inspect_csr.rb\"\n  step \"Step 3: setup an autosign command that inspects CSR attributes\" do\n    autosign_inspect_csr = <<-END\n#!/usr/bin/env ruby\nrequire 'openssl'\n\ndef unwrap_attr(attr)\n  set = attr.value\n  str = set.value.first\n  str.value\nend\n\ncsr_text = STDIN.read\ncsr = OpenSSL::X509::Request.new(csr_text)\npassphrase = csr.attributes.find { |a| a.oid == '1.3.6.1.4.1.34380.2.1' }\n# And here we jump hoops to unwrap ASN1's Attr Set Str\nif unwrap_attr(passphrase) == 'my passphrase'\n  exit 0\nend\nexit 1\n    END\n    create_remote_file(master, autosign_inspect_csr_path, autosign_inspect_csr)\n    on master, \"chmod 777 #{testdirs[master]}\"\n    on master, \"chmod 777 #{autosign_inspect_csr_path}\"\n  end\n\n  agent_csr_attributes = {}\n  step \"Step 4: create attributes for inclusion on csr on agents\" do\n    csr_attributes = <<-END\ncustom_attributes:\n  1.3.6.1.4.1.34380.2.0: hostname.domain.com\n  1.3.6.1.4.1.34380.2.1: my passphrase\n  1.3.6.1.4.1.34380.2.2: # system IPs in hex\n    - 0xC0A80001 # 192.168.0.1\n    - 0xC0A80101 # 192.168.1.1\n    END\n\n    agents.each do |agent|\n      agent_csr_attributes[agent] = \"#{testdirs[agent]}/csr_attributes.yaml\"\n      create_remote_file(agent, agent_csr_attributes[agent], csr_attributes)\n    end\n  end\n\n  step \"Step 5: successfully obtain a cert\" do\n    master_opts = {\n      'master' => {\n        'autosign' => autosign_inspect_csr_path,\n        'dns_alt_names' => \"puppet,#{hostname},#{fqdn}\"\n      },\n    }\n    with_puppet_running_on(master, master_opts) do\n      agents.each do |agent|\n        next if agent == master\n\n        step \"attempting to obtain cert for #{agent}\"\n        test_certnames << (certname = \"#{agent}-attrs\")\n        on(agent, puppet(\"agent --test\",\n                         \"--waitforcert 0\",\n                         \"--ssldir\", \"'#{testdirs[agent]}/ssldir-attrs'\",\n                         \"--csr_attributes '#{agent_csr_attributes[agent]}'\",\n                         \"--certname #{certname}\"), :acceptable_exit_codes => [0,2]) do |result|\n          assert_key_generated(agent, result.stdout) unless agent['locale'] == 'ja'\n          assert_match(/Downloaded certificate for #{agent}/, result.stdout, \"Expected certificate to be autosigned\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ssl/certificate_extensions.rb",
    "content": "require 'puppet/acceptance/temp_file_utils'\nextend Puppet::Acceptance::TempFileUtils\n\ntest_name \"certificate extensions available as trusted data\" do\n  skip_test \"Test requires at least one non-master agent\" if hosts.length == 1\n\n  tag 'audit:high',        # ca/cert core functionality\n      'audit:integration',\n      'server'             # Ruby implementation is deprecated\n\n  initialize_temp_dirs\n\n  agent_certnames = []\n  hostname = master.execute('facter networking.hostname')\n  fqdn = master.execute('facter networking.fqdn')\n\n  teardown do\n    step \"Cleanup the test agent certs\"\n    master_config = {\n      'main' => { 'server' => fqdn },\n      'master' => { 'dns_alt_names' => \"puppet,#{hostname},#{fqdn}\" }\n    }\n\n    with_puppet_running_on(master, master_config) do\n      on(master,\n         \"puppetserver ca clean --certname #{agent_certnames.join(',')}\",\n         :acceptable_exit_codes => [0,24])\n    end\n  end\n\n  environments_dir = get_test_file_path(master, \"environments\")\n  master_config = {\n    'main' => {\n      'environmentpath' => environments_dir,\n    },\n    'master' => {\n      'autosign' => true,\n      'dns_alt_names' => \"puppet,#{hostname},#{fqdn}\",\n    }\n  }\n\n  csr_attributes = YAML.dump({\n    'extension_requests' => {\n      # registered puppet extensions\n      'pp_uuid' => 'b5e63090-5167-11e3-8f96-0800200c9a66',\n      'pp_instance_id' => 'i-3fkva',\n      # private (arbitrary) extensions\n      '1.3.6.1.4.1.34380.1.2.1' => 'db-server', # node role\n      '1.3.6.1.4.1.34380.1.2.2' => 'webops' # node group\n    }\n  })\n\n  step \"Generate a production environment manifest to dump trusted data\"\n  apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)\n    File {\n      ensure => directory,\n      mode => \"0770\",\n      owner => #{master.puppet['user']},\n      group => #{master.puppet['group']},\n    }\n    file {\n      '#{environments_dir}':;\n      '#{environments_dir}/production':;\n      '#{environments_dir}/production/manifests':;\n      '#{environments_dir}/production/manifests/site.pp':\n        ensure => file,\n        content => '\n          file { \"$test_dir/trusted.yaml\":\n            ensure => file,\n            content => inline_template(\"<%= YAML.dump(@trusted) %>\")\n          }\n          ',\n        mode => \"0640\",\n    }\n  MANIFEST\n\n  with_puppet_running_on(master, master_config) do\n    agents.each do |agent|\n      next if agent == master\n\n      step \"Create agent csr_attributes.yaml on #{agent}\"\n      agent_csr_attributes = get_test_file_path(agent, \"csr_attributes.yaml\")\n      agent_ssldir = get_test_file_path(agent, \"ssldir\")\n      create_remote_file(agent, agent_csr_attributes, csr_attributes)\n\n      agent_certname = \"#{agent}-extensions\"\n      agent_certnames << agent_certname\n\n      step \"Check in as #{agent_certname}\"\n      on(agent, puppet(\"agent\", \"--test\",\n                       \"--waitforcert\", 0,\n                       \"--csr_attributes\", agent_csr_attributes,\n                       \"--certname\", agent_certname,\n                       \"--ssldir\", agent_ssldir,\n                       'ENV' => { \"FACTER_test_dir\" => get_test_file_path(agent, \"\") }),\n        :acceptable_exit_codes => [0, 2])\n\n      trusted_data = YAML.load(on(agent, \"cat #{get_test_file_path(agent, 'trusted.yaml')}\").stdout)\n      agent_hostname, agent_domain = agent_certname.split('.', 2)\n\n      step \"Verify trusted data\"\n      assert_equal({\n          'authenticated' => 'remote',\n          'certname' => agent_certname,\n          'extensions' => {\n            'pp_uuid' => 'b5e63090-5167-11e3-8f96-0800200c9a66',\n            'pp_instance_id' => 'i-3fkva',\n            '1.3.6.1.4.1.34380.1.2.1' => 'db-server',\n            '1.3.6.1.4.1.34380.1.2.2' => 'webops'\n          },\n          'hostname' => agent_hostname,\n          'domain' => agent_domain,\n          'external' => {}\n        },\n        trusted_data)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ssl/trusted_external_facts.rb",
    "content": "test_name \"trusted external fact test\" do\n  require 'puppet/acceptance/environment_utils'\n  extend Puppet::Acceptance::EnvironmentUtils\n\n  ### HELPERS ###\n\n  SEPARATOR=\"<TRUSTED_JSON>\"\n  def parse_trusted_json(puppet_output)\n    trusted_json = puppet_output.split(SEPARATOR)[1]\n    if trusted_json.nil?\n      raise \"Puppet output does not contain the expected '#{SEPARATOR}<trusted_json>#{SEPARATOR}' output\"\n    end\n    JSON.parse(trusted_json)\n  rescue => e\n    raise \"Failed to parse the trusted JSON: #{e}\"\n  end\n\n  ### END HELPERS ###\n\n  tag 'audit:high',        # external facts\n    'server'\n\n  skip_test 'requires a master for serving module content' if master.nil?\n\n  testdir = master.tmpdir('trusted_external_facts')\n  on(master, \"chmod 755 #{testdir}\")\n  tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*'))\n\n  teardown do\n    on(master, \"rm -r '#{testdir}'\", :accept_all_exit_codes => true)\n\n    # Remove all traces of the last used environment\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  step \"create 'external' module referencing trusted hash\" do\n    on(master, \"mkdir -p #{environmentpath}/#{tmp_environment}/modules/external/manifests\")\n    master_module_manifest = \"#{environmentpath}/#{tmp_environment}/modules/external/manifests/init.pp\"\n    manifest = <<MANIFEST\nclass external {\n  $trusted_json = inline_template('<%= @trusted.to_json %>')\n  notify { 'trusted facts':\n    message => \"#{SEPARATOR}${trusted_json}#{SEPARATOR}\"\n  }\n}\nMANIFEST\n    create_remote_file(master, master_module_manifest, manifest)\n    on(master, \"chmod 644 '#{master_module_manifest}'\")\n  end\n\n  step \"create site.pp to classify nodes to include module\" do\n    site_pp_file = \"#{environmentpath}/#{tmp_environment}/manifests/site.pp\"\n    site_pp      = <<-SITE_PP\nnode default {\n  include external\n}\n    SITE_PP\n    create_remote_file(master, site_pp_file, site_pp)\n    on(master, \"chmod 644 '#{site_pp_file}'\")\n  end\n\n  step \"when trusted_external_command is a file\" do\n    external_trusted_fact_script_path = \"#{testdir}/external_facts.sh\"\n\n    step \"create the file\" do\n      external_trusted_fact_script = <<EOF\n#!/bin/bash\nCERTNAME=$1\nprintf '{\"doot\":\"%s\"}\\n' \"$CERTNAME\"\nEOF\n      create_remote_file(master, external_trusted_fact_script_path, external_trusted_fact_script)\n      on(master, \"chmod 777 #{external_trusted_fact_script_path}\")\n    end\n\n    step \"start the master and perform the test\" do\n      master_opts = {\n        'main' => {\n          'trusted_external_command' => external_trusted_fact_script_path\n        }\n      }\n\n      with_puppet_running_on(master, master_opts) do\n        agents.each do |agent|\n          on(agent, puppet(\"agent\", \"-t\", \"--environment\", tmp_environment), :acceptable_exit_codes => [0,2]) do |res|\n            trusted_hash = parse_trusted_json(res.stdout)\n            assert_includes(trusted_hash, 'external', \"Trusted fact hash contains external key\")\n            assert_equal(agent.to_s, trusted_hash['external']['doot'], \"trusted facts contains certname\")\n          end\n        end\n      end\n    end\n  end\n\n  step \"when trusted_external_command is a directory\" do\n    dir_path = \"#{testdir}/commands\"\n    executable_files = {\n      'no_extension' => <<EOF,\n#!/bin/bash\nCERTNAME=$1\nprintf '{\"no_extension_key\":\"%s\"}\\n' \"$CERTNAME\"\nEOF\n\n      'shell.sh' => <<EOF,\n#!/bin/bash\nCERTNAME=$1\nprintf '{\"shell_key\":\"%s\"}\\n' \"$CERTNAME\"\nEOF\n\n      'ruby.rb' => <<EOF,\n#!#{master[:privatebindir]}/ruby\nrequire 'json'\nCERTNAME=ARGV[0]\ndata = { \"ruby_key\" => CERTNAME }\nprint data.to_json\nEOF\n    }\n\n    step \"create the directory\" do\n      on(master, \"mkdir #{dir_path}\")\n      on(master, \"chmod 755 #{dir_path}\")\n\n      executable_files.each do |filename, content|\n        filepath = \"#{dir_path}/#{filename}\"\n        create_remote_file(master, filepath, content)\n        on(master, \"chmod 777 #{filepath}\")\n      end\n\n      # Create a non-executable file and an executable child-directory\n      # to ensure that these cases are skipped during external data\n      # retrieval\n\n      create_remote_file(master, \"#{dir_path}/non_executable_file\", \"foo\")\n\n      executable_child_dir = \"#{dir_path}/child_dir\"\n      on(master, \"mkdir #{executable_child_dir}\")\n      on(master, \"chmod 777 #{executable_child_dir}\")\n    end\n\n    master_opts = {\n      'main' => {\n        'trusted_external_command' => dir_path\n      }\n    }\n\n    step \"start the master and perform the test\" do\n      with_puppet_running_on(master, master_opts) do\n        agents.each do |agent|\n          on(agent, puppet(\"agent\", \"-t\", \"--environment\", tmp_environment), :acceptable_exit_codes => [0,2]) do |res|\n            trusted_hash = parse_trusted_json(res.stdout)\n            assert_includes(trusted_hash, 'external', \"Trusted fact hash contains external key\")\n\n\n            external_keys = [\n              'no_extension',\n              'shell',\n              'ruby'\n            ]\n            assert_equal(external_keys.sort, trusted_hash['external'].keys.sort, \"trusted['external'] does not contain <basename> keys of all executable files\")\n\n            external_keys.each do |key|\n              expected_data = { \"#{key}_key\" => agent.to_s }\n              data = trusted_hash['external'][key]\n              assert_equal(expected_data, data, \"trusted['external'][#{key}] does not contain #{key}'s data\")\n            end\n          end\n        end\n      end\n    end\n\n    step \"when there's more than executable <basename> script\" do\n      step \"create the conflicting file\" do\n        filepath = \"#{dir_path}/shell.rb\"\n        create_remote_file(master, filepath, executable_files['shell.sh'])\n        on(master, \"chmod 777 #{filepath}\")\n      end\n\n      step \"start the master and perform the test\" do\n        with_puppet_running_on(master, master_opts) do\n          agents.each do |agent|\n            on(agent, puppet(\"agent\", \"-t\", \"--environment\", tmp_environment), :acceptable_exit_codes => [1]) do |res|\n              assert_match(/.*shell.*#{Regexp.escape(dir_path)}/, res.stderr)\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_1334_clientbucket_corrupted.rb",
    "content": "test_name 'C99977 corrupted clientbucket' do\n\n  tag 'audit:high',\n      'audit:integration'\n\n  agents.each do |agent|\n    tmpfile = agent.tmpfile('c99977file')\n    unmanaged_content = \"unmanaged\\n\"\n    \n    unmanaged_sha = Digest::SHA256.hexdigest(unmanaged_content)\n\n    managed_content = \"managed\\n\"\n    manifest = \"file { '#{tmpfile}': content => '#{managed_content}', backup => 'puppet' }\"\n\n    step 'create unmanaged file' do\n      create_remote_file(agent, tmpfile, unmanaged_content)\n    end\n\n    step 'manage file' do\n      apply_manifest_on(agent, manifest)\n    end\n\n    step 'corrupt clientbucket of file' do\n      if agent['platform'] =~ /windows/\n        vardir = 'C:/ProgramData/PuppetLabs/puppet/cache'\n      else\n        vardir = '/opt/puppetlabs/puppet/cache'\n      end\n      clientbucket_base = \"#{vardir}/clientbucket\"\n      sha_array = unmanaged_sha.scan(/\\w/)\n      clientbucket_path = clientbucket_base\n      (0..7).each do |i|\n        clientbucket_path = \"#{clientbucket_path}/#{sha_array[i]}\"\n      end\n      clientbucket_path = \"#{clientbucket_path}/#{unmanaged_sha}\"\n\n      contents_path = \"#{clientbucket_path}/contents\"\n      paths_path = \"#{clientbucket_path}/paths\"\n\n      create_remote_file(agent, contents_path, \"corrupted\\n\")\n      create_remote_file(agent, paths_path, \"corrupted\\n\")\n    end\n\n    step 'reset file to pre-managed state' do\n      create_remote_file(agent, tmpfile, unmanaged_content)\n    end\n\n    step 'manage file again' do\n      apply_manifest_on(agent, manifest) do |result|\n        assert_match(/Warning: Existing backup does not match its expected sum, .*Overwriting corrupted backup/, result.stderr) unless agent['locale'] == 'ja'\n        on(agent, \"cat #{tmpfile}\") do |r2|\n          assert_equal(managed_content, r2.stdout)\n        end\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_13948_lib_dir_hook_should_be_called_on_initialization.rb",
    "content": "test_name \"the $libdir setting hook is called on startup\"\n\nrequire 'puppet/acceptance/temp_file_utils'\n\nextend Puppet::Acceptance::TempFileUtils\n\ntag 'audit:high',      # tests basic custom module/pluginsync handling?\n    'audit:refactor',    # Use block style `test_namme`\n    'audit:integration',\n    'server'\n\ninitialize_temp_dirs()\nall_tests_passed = false\n\n###############################################################################\n# BEGIN TEST LOGIC\n###############################################################################\n\n# create some vars to point to the directories that we're going to point the master/agents at\nmaster_module_dir = \"master_modules\"\nagent_var_dir = \"agent_var\"\nagent_lib_dir = \"#{agent_var_dir}/lib\"\n\napp_name = \"superbogus\"\napp_desc = \"a simple %1$s for testing %1$s delivery via plugin sync\"\napp_output = \"Hello from the #{app_name} %s\"\n\nmaster_module_face_content = <<-HERE\nPuppet::Face.define(:#{app_name}, '0.0.1') do\n  copyright \"Puppet Labs\", 2011\n  license   \"Apache 2 license; see COPYING\"\n\n  summary \"#{app_desc % \"face\"}\"\n\n  action(:foo) do\n    summary \"a test action defined in the test face in the main puppet lib dir\"\n\n    default\n    when_invoked do |*args|\n      puts \"#{app_output % \"face\"}\"\n    end\n  end\n\nend\nHERE\n\nmaster_module_app_content = <<-HERE\nrequire 'puppet/application/face_base'\n\nclass Puppet::Application::#{app_name.capitalize} < Puppet::Application::FaceBase\nend\n\nHERE\n\n# this begin block is here for handling temp file cleanup via an \"ensure\" block\n# at the very end of the test.\nbegin\n\n  # here we create a custom app, which basically doesn't do anything except for\n  # print a hello-world message\n  agent_module_face_file = \"#{agent_lib_dir}/puppet/face/#{app_name}.rb\"\n  master_module_face_file = \"#{master_module_dir}/#{app_name}/lib/puppet/face/#{app_name}.rb\"\n\n  agent_module_app_file = \"#{agent_lib_dir}/puppet/application/#{app_name}.rb\"\n  master_module_app_file = \"#{master_module_dir}/#{app_name}/lib/puppet/application/#{app_name}.rb\"\n\n  # copy all the files to the master\n  step \"write our simple module out to the master\" do\n    create_test_file(master, master_module_app_file, master_module_app_content, :mkdirs => true)\n    create_test_file(master, master_module_face_file, master_module_face_content, :mkdirs => true)\n  end\n\n  step \"verify that the app file exists on the master\" do\n    unless test_file_exists?(master, master_module_app_file) then\n      fail_test(\"Failed to create app file '#{get_test_file_path(master, master_module_app_file)}' on master\")\n    end\n    unless test_file_exists?(master, master_module_face_file) then\n      fail_test(\"Failed to create face file '#{get_test_file_path(master, master_module_face_file)}' on master\")\n    end\n  end\n\n  step \"start the master\" do\n    basemodulepath = \"#{get_test_file_path(master, master_module_dir)}\"\n    if master.is_pe?\n      basemodulepath << \":#{master['sitemoduledir']}\"\n    end\n    master_opts = {\n      'main' => {\n        'basemodulepath' => basemodulepath,\n      },\n      'master' => {\n        'node_terminus' => 'plain',\n      },\n    }\n\n    with_puppet_running_on master, master_opts do\n\n      # the module files shouldn't exist on the agent yet because they haven't been synced\n      step \"verify that the module files don't exist on the agent path\" do\n        agents.each do |agent|\n            if test_file_exists?(agent, agent_module_app_file) then\n              fail_test(\"app file already exists on agent: '#{get_test_file_path(agent, agent_module_app_file)}'\")\n            end\n            if test_file_exists?(agent, agent_module_face_file) then\n              fail_test(\"face file already exists on agent: '#{get_test_file_path(agent, agent_module_face_file)}'\")\n            end\n        end\n      end\n\n      step \"run the agent\" do\n        agents.each do |agent|\n\n          step \"capture the existing ssldir, in case the default package puppet.conf sets it within vardir (rhel...)\"\n          agent_ssldir = on(agent, puppet('agent --configprint ssldir')).stdout.chomp\n\n          on(agent, puppet('agent',\n                           \"--vardir=\\\"#{get_test_file_path(agent, agent_var_dir)}\\\" \",\n                           \"--ssldir=\\\"#{agent_ssldir}\\\" \",\n                           \"--trace  --test\")\n          )\n        end\n      end\n\n    end\n  end\n\n  step \"verify that the module files were synced down to the agent\" do\n    agents.each do |agent|\n      unless test_file_exists?(agent, agent_module_app_file) then\n        fail_test(\"Expected app file not synced to agent: '#{get_test_file_path(agent, agent_module_app_file)}'\")\n      end\n      unless test_file_exists?(agent, agent_module_face_file) then\n        fail_test(\"Expected face file not synced to agent: '#{get_test_file_path(agent, agent_module_face_file)}'\")\n      end\n    end\n  end\n\n  step \"verify that the application shows up in help\" do\n    agents.each do |agent|\n      on(agent, PuppetCommand.new(:help, \"--vardir=\\\"#{get_test_file_path(agent, agent_var_dir)}\\\"\")) do |result|\n        assert_match(/^\\s+#{app_name}\\s+#{app_desc % \"face\"}/, result.stdout)\n      end\n    end\n  end\n\n  step \"verify that we can run the application\" do\n    agents.each do |agent|\n      on(agent, PuppetCommand.new(:\"#{app_name}\", \"--vardir=\\\"#{get_test_file_path(agent, agent_var_dir)}\\\"\")) do |result|\n        assert_match(/^#{app_output % \"face\"}/, result.stdout)\n      end\n    end\n  end\n\n  step \"clear out the libdir on the agents in preparation for the next test\" do\n    agents.each do |agent|\n      on(agent, \"rm -rf '#{get_test_file_path(agent, agent_lib_dir)}/*'\")\n    end\n  end\n\n  all_tests_passed = true\n\nensure\n  ##########################################################################################\n  # Clean up all of the temp files created by this test.  It would be nice if this logic\n  # could be handled outside of the test itself; I envision a stanza like this one appearing\n  # in a very large number of the tests going forward unless it is handled by the framework.\n  ##########################################################################################\n  if all_tests_passed then\n    remove_temp_dirs()\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_15560_managehome.rb",
    "content": "test_name \"#15560: Manage home directories\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_namme`\n                       # refactor to be OS agnostic and added to the resource/user\n                       # tests. managehome is currently not covered there.\n    'audit:acceptance'\n\nconfine :to, :platform => 'windows'\n\nusername = \"pl#{rand(99999).to_i}\"\n\nmanifest_present = <<-EOM\nuser { '#{username}':\n  ensure     => present,\n  managehome => true,\n  password   => 'Password123!!',\n}\nEOM\n\nmanifest_absent = <<-EOM\nuser { '#{username}':\n  ensure     => absent,\n  managehome => true,\n}\nEOM\n\nagents.each do |host|\n  on(host, puppet_apply, :stdin => manifest_present)\n\n  deleteable_profile = true\n\n  version = on(host, facter('os.release.full')).stdout.chomp\n  if version =~ /^5\\.[012]|2003/\n    homedir = \"C:/Documents and Settings/#{username}\"\n    deleteable_profile = false\n  else\n    homedir = \"C:/Users/#{username}\"\n  end\n\n  on(host, \"test -d '#{homedir}'\")\n\n  on(host, puppet_apply, :stdin => manifest_absent)\n\n  if deleteable_profile\n    on(host, \"test ! -d '#{homedir}'\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_2280_refresh_fail_should_fail_run.rb",
    "content": "test_name 'C100297 - A resource triggered by a refresh that fails should be reported as a failure when using --detailed-exitcodes' do\n\n  tag 'audit:high',\n      'audit:integration' # Service type interaction with --detailed-exitcodes\n\n  manifest =<<EOS\n    exec{'true':\n      command => 'true',\n      path => ['/bin', '/usr/bin'],\n    }\n\n    exec{'false':\n      command => 'false',\n      path => ['/bin', '/usr/bin'],\n      refreshonly => true,\n      subscribe => Exec['true'],\n    }\n\n    exec{'require_echo':\n      command => 'echo \"This should not happen due to a failed requirement.\"',\n      path => ['/bin', '/usr/bin'],\n      logoutput => true,\n      require => Exec['false'],\n    }\nEOS\n\n  agents.each do |agent|\n    step 'Apply manifest with fail on refresh. Ensure that this results in a failed dependency' do\n      apply_manifest_on(agent, manifest, :expect_failures => true) do |res|\n        refute_match(/require_echo.*returns: executed successfully/, res.stdout)\n        assert_match(/require_echo.*Skipping because of failed dependencies/, res.stderr) unless agent['locale'] == 'ja'\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_2455_on_solaris_init_provider_should_start_service_in_own_smf_contract.rb",
    "content": "test_name \"(PUP-2455) Service provider should start Solaris init service in its own SMF contract\"\n\ntag 'audit:high',\n    'audit:refactor',  # Use block style `test_name`\n                       # Use mk_tmp_environment_with_teardown\n                       # Combine with Service resource tests\n    'audit:acceptance' # Service provider functionality\n\nskip_test unless agents.any? {|agent| agent['platform'] =~ /solaris/ }\n\nteardown do\n  agents.each do |agent|\n    on(agent, puppet('config print lastrunfile')) do |command_result|\n      agent.rm_rf(command_result.stdout)\n    end\n  end\nend\n\nsleepy_daemon_initscript = <<INITSCRIPT\n#!/usr/bin/bash\nFIXTURESERVICE=\"/tmp/sleepy_daemon\"\nstart(){\n    $FIXTURESERVICE &\n}\n\nstop(){\n    FIXTUREPID=`ps -ef | grep \"$FIXTURESERVICE\" | grep -v grep | awk '{print $2}'`\n    if [ \"x$FIXTUREPID\" != \"x\" ]; then\n      kill -9 ${FIXTUREPID}\n    fi\n}\n\nstatus(){\n    FIXTUREPID=`ps -ef | grep \"$FIXTURESERVICE\" | grep -v grep | awk '{print $2}'`\n    if [ \"x$FIXTUREPID\" = \"x\" ]; then\n      exit 1\n    else\n      exit 0\n    fi\n}\ncase \"$1\" in\n    start)\n        start\n        ;;\n    stop)\n        stop\n        ;;\n    status)\n        status\n        ;;\nesac\nINITSCRIPT\n\nstep \"Setup fixture service manifest on master\"\n\ntestdir = master.tmpdir('solaris_services_in_own_smf_contract')\n\ntest_manifest = <<MANIFEST\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\nfile { '#{testdir}': }\nfile { '#{testdir}/environments': }\nfile { '#{testdir}/environments/production': }\nfile { '#{testdir}/environments/production/manifests': }\nfile { '#{testdir}/environments/production/manifests/site.pp':\n  ensure  => file,\n  content => '\n    node default {\n      service { \"sleepy_daemon\":\n      provider => \"init\",\n      enable   => \"true\",\n      ensure   => \"running\",\n      status   => \"/etc/init.d/sleepy_daemon status\",\n      }\n    }\n  ',\n}\nMANIFEST\n\napply_manifest_on master, test_manifest\n\nstep \"Start master\"\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n  }\n}\n\nwith_puppet_running_on master, master_opts, testdir do\n\n  agents.each do |agent|\n\n    fixture_service = 'sleepy_daemon'\n    fixture_service_stop = '/etc/init.d/sleepy_daemon stop'\n\n    next unless agent['platform'] =~ /solaris/\n\n    step \"Setup fixture service on #{agent}\"\n    sleepy_daemon_script = <<SCRIPT\n#!#{agent['privatebindir']}/ruby\nwhile true\n  sleep (2)\nend\nSCRIPT\n    sleepy_daemon_path = \"/tmp/sleepy_daemon\"\n    sleepy_daemon_initscript_path = \"/etc/init.d/sleepy_daemon\"\n    create_remote_file(agent, sleepy_daemon_path, sleepy_daemon_script)\n    create_remote_file(agent, sleepy_daemon_initscript_path, sleepy_daemon_initscript)\n    on(agent, \"chmod +x #{sleepy_daemon_path} #{sleepy_daemon_initscript_path}\")\n\n    step \"Start the fixture service on #{agent} \"\n    on(agent, puppet(\"resource service #{fixture_service} provider=init ensure=stopped\"))\n    on(agent, puppet(\"resource service #{fixture_service} provider=init ensure=running\")) do |result|\n      assert_match(/ensure changed 'stopped' to 'running'/, result.stdout, \"The fixture service #{fixture_service} is not in a testable state on #{agent}.\")\n    end\n\n    step \"Verify whether the fixture process is alone in its SMF contract on #{agent}\"\n    service_ctid = on(agent, \"sleep 10;ps -eo ctid,args | grep #{fixture_service} | grep -v grep | awk '{print $1}'\").stdout.chomp.to_i\n    number_in_contract = on(agent, \"pgrep -c #{service_ctid} | wc -l\").stdout.chomp.to_i\n    assert(number_in_contract == 1, \"The fixture process #{fixture_service} is not alone in its SMF contract on #{agent}.\")\n\n    if agent.is_pe?\n\n      step \"Stop puppet on #{agent}\"\n      on(agent, \"svcadm disable pe-puppet;sleep 70;svcadm disable pe-puppet\")\n\n      step \"Stop fixture service on #{agent}\"\n      on(agent, \"#{fixture_service_stop}\")\n\n      step \"Enable puppet service on #{agent}\"\n      on(agent, \"svcadm enable pe-puppet;sleep 10\") do\n        puppet_ctid = on(agent, \"svcs -Ho CTID pe-puppet | awk '{print $1}'\").stdout.chomp.to_i\n        service_ctid = on(agent, \"ps -eo ctid,args | grep #{fixture_service} | grep -v grep | awk '{print $1}'\").stdout.chomp.to_i\n\n        step \"Compare SMF contract ids for puppet and #{fixture_service} on #{agent}\"\n        unless ( puppet_ctid != \"0\" and service_ctid != \"0\" ) then\n          fail_test(\"SMF contract ids should not equal zero.\")\n        end\n\n        assert(service_ctid != puppet_ctid, \"Service is in the same SMF contract as puppet on #{agent}.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_5477_master_not_dectect_sitepp.rb",
    "content": "# In 2.6, compile does not fail when site.pp does not exist.\n#\n# However, if a catalog is compiled when site.pp does not exist,\n# puppetmaster does not detect when site.pp is created. This requires a restart\n#\ntest_name \"Ticket 5477, Puppet Master does not detect newly created site.pp file\"\n\ntag 'audit:high',\n    'audit:integration',\n    'audit:refactor',     # Use block style `test_name`\n    'server'\n\ntestdir = master.tmpdir('missing_site_pp')\nmanifest_file = \"#{testdir}/environments/production/manifests/site.pp\"\n\napply_manifest_on(master, <<-PP, :catch_failures => true)\nFile {\n  ensure => directory,\n  mode => \"0750\",\n  owner => #{master.puppet['user']},\n  group => #{master.puppet['group']},\n}\n\nfile {\n  '#{testdir}':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/manifests':;\n}\nPP\n\nmaster_opts = {\n  'main' => {\n    'environmentpath' => \"#{testdir}/environments\",\n    'filetimeout' => 1,\n    'environment_timeout' => 0,\n  }\n}\n\nwith_puppet_running_on master, master_opts, testdir do\n  # Run test on Agents\n  step \"Agent: agent --test\"\n  on(agents, puppet('agent', \"-t\"), :acceptable_exit_codes => [0,2])\n\n  # Create a new site.pp\n  step \"Master: create basic site.pp file\"\n  create_remote_file master, manifest_file, \"notify{ticket_5477_notify:}\"\n\n  on master, \"chmod 644 #{manifest_file}\"\n\n  sleep 3\n\n  step \"Agent: puppet agent --test\"\n\n  agents.each do |host|\n    on(host, puppet('agent', \"-t\"), :acceptable_exit_codes => [2]) do |result|\n      assert_match(/ticket_5477_notify/, result.stdout, \"#{host}: Site.pp not detected on Puppet Master\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_5592_hiera_lookup_when_param_undef.rb",
    "content": "test_name 'Ensure hiera lookup occurs if class param is undef' do\n\n  tag 'audit:high',\n      'audit:unit'    # basic auto lookup functionality\n\n  agents.each do |agent|\n\n    testdir = agent.tmpdir('undef')\n\n    step 'Setup - create hiera data file and test module' do\n\n##{{{\n      manifest =<<-PP\nFile {\n  ensure => directory,\n  mode => \"0750\",\n}\n\nfile {\n  '#{testdir}':;\n  '#{testdir}/hieradata':;\n  '#{testdir}/environments':;\n  '#{testdir}/environments/production':;\n  '#{testdir}/environments/production/modules':;\n}\n\nfile { '#{testdir}/hiera.yaml':\n  ensure  => file,\n  content => '---\n    :backends:\n      - \"yaml\"\n    :hierarchy:\n      - \"global\"\n    :yaml:\n      :datadir: \"#{testdir}/hieradata\"\n  ',\n  mode => \"0640\",\n}\n\nfile { '#{testdir}/hieradata/global.yaml':\n  ensure  => file,\n  content => \"test::my_param: 'hiera lookup value'\",\n  mode => \"0640\",\n}\n\nfile {\n  '#{testdir}/environments/production/modules/test':;\n  '#{testdir}/environments/production/modules/test/manifests':;\n}\n\nfile { '#{testdir}/environments/production/modules/test/manifests/init.pp':\n  ensure => file,\n  content => '\n    class test (\n      $my_param = \"class default value\",\n    ) {\n      notice($my_param)\n    }',\n  mode => \"0640\",\n}\nPP\n#}}}\n\n      apply_manifest_on(agent, manifest, :catch_failures => true)\n    end\n\n    step 'Invoke class with undef param and verify hiera value was applied' do\n      on(agent, puppet('apply', \"-e 'class {\\\"test\\\": my_param => undef }'\", \"--modulepath=#{testdir}/environments/production/modules\", \"--hiera_config=#{testdir}/hiera.yaml\" ), :acceptable_exit_codes => [0,2]) do |result|\n        assert_match('hiera lookup value', result.stdout)\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_6541_invalid_filebucket_files.rb",
    "content": "test_name \"#6541: file type truncates target when filebucket cannot retrieve hash\"\n\ntag 'audit:high',\n    'audit:integration', # file type and file bucket interop\n    'audit:refactor'     # look into combining with ticket_4622_filebucket_diff_test.rb\n                         # Use block style `test_run`\n\nagents.each do |agent|\n  target=agent.tmpfile('6541-target')\n\n  on(agent, \"rm -rf \\\"#{agent.puppet['vardir']}/*bucket\\\"\")\n\n  step \"write zero length file\"\n  manifest = \"file { '#{target}': content => '' }\"\n  apply_manifest_on(agent, manifest)\n\n  step \"overwrite file, causing zero-length file to be backed up\"\n  manifest = \"file { '#{target}': content => 'some text', backup => 'puppet' }\"\n  apply_manifest_on(agent, manifest)\n\n  test_name \"verify invalid hashes should not change the file\"\n\n  manifest = \"file { '#{target}': content => '{sha256}notahash' }\"\n\n  apply_manifest_on(agent, manifest) do |result|\n    refute_match(/content changed/, result.stdout, \"#{agent}: shouldn't have overwrote the file\")\n  end\n\n  test_name \"verify valid but unbucketed hashes should not change the file\"\n  manifest = \"file { '#{target}': content => '{md5}13ad7345d56b566a4408ffdcd877bc78' }\"\n  apply_manifest_on(agent, manifest) do |result|\n    refute_match(/content changed/, result.stdout, \"#{agent}: shouldn't have overwrote the file\")\n  end\n\n  test_name \"verify that an empty file can be retrieved from the filebucket\"\n  manifest = \"file { '#{target}': content => '{sha256}e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', backup => 'puppet' }\"\n\n  apply_manifest_on(agent, manifest) do |result|\n    assert_match(/content changed '\\{sha256\\}b94f6f125c79e3a5ffaa826f584c10d52ada669e6762051b826b55776d05aed2' to '\\{sha256\\}e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'/, result.stdout, \"#{agent}: shouldn't have overwrote the file\")\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb",
    "content": "test_name \"#6857: redact password hashes when applying in noop mode\"\n\ntag 'audit:high',\n    'audit:refactor',    # Use block style `test_name`\n    'audit:integration'\n\nrequire 'puppet/acceptance/common_utils'\nextend Puppet::Acceptance::CommandUtils\n\nhosts_to_test = agents.reject do |agent|\n  if agent['platform'].match(/(?:ubuntu|centos|debian|el-|fedora)/)\n    result = on(agent, \"#{ruby_command(agent)} -e \\\"require 'shadow' or raise\\\"\", :acceptable_exit_codes => [0,1])\n    result.exit_code != 0\n  else\n    # Non-linux platforms do not rely on ruby-libshadow for password management\n    # and so we don't reject them from testing\n    false\n  end\nend\nskip_test \"No suitable hosts found\" if hosts_to_test.empty?\n\nusername = \"pl#{rand(99999).to_i}\"\n\nteardown do\n  step \"Teardown: Ensure test user is removed\"\n  hosts_to_test.each do |host|\n    on agent, puppet('resource', 'user', username, 'ensure=absent')\n    on agent, puppet('resource', 'group', username, 'ensure=absent')\n  end\nend\n\nadduser_manifest = <<MANIFEST\nuser { '#{username}':\n  ensure   => 'present',\n  password => 'Apassw0rd!',\n}\nMANIFEST\n\nchangepass_manifest = <<MANIFEST\nuser { '#{username}':\n  ensure   => 'present',\n  password => 'Anewpassw0rd!',\n  noop     => true,\n}\nMANIFEST\n\nhosts_to_test.each do |host|\n  apply_manifest_on(host, adduser_manifest )\n  apply_manifest_on(host, changepass_manifest ) do |result|\n    assert_match( /current_value \\[redacted\\], should be \\[redacted\\]/ , \"#{result.host}: #{result.stdout}\" ) unless host['locale'] == 'ja'\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_6907_use_provider_in_same_run_it_becomes_suitable.rb",
    "content": "test_name \"providers should be useable in the same run they become suitable\"\n\ntag 'audit:high',       # autoloader, core puppet agent run functionality\n    'audit:refactor',    # Use block style `test_name`\n    'audit:integration' # does not require packages, probably implicitly assumed in many other places\n\nagents.each do |agent|\n  dir = agent.tmpdir('provider-6907')\n\n  on agent, \"mkdir -p #{dir}/lib/puppet/{type,provider/test6907}\"\n  on agent, \"cat > #{dir}/lib/puppet/type/test6907.rb\", :stdin => <<TYPE\nPuppet::Type.newtype(:test6907) do\n  newparam(:name, :namevar => true)\n\n  newproperty(:file)\nend\nTYPE\n\n  on agent, \"cat > #{dir}/lib/puppet/provider/test6907/only.rb\", :stdin => <<PROVIDER\nPuppet::Type.type(:test6907).provide(:only) do\n  # The name of the file is chosen to be *.exe so it works on windows and *nix\n  # because windows inspects the PATHEXT environment variable in 1.9.3 and later.\n  commands :anything => \"#{dir}/must_exist.exe\"\n  require 'fileutils'\n\n  def file\n    'not correct'\n  end\n\n  def file=(value)\n    FileUtils.touch(value)\n  end\nend\nPROVIDER\n\n  on agent, puppet_apply(\"--libdir #{dir}/lib --trace\"), :stdin => <<MANIFEST\n  test6907 { \"test-6907\":\n    file => \"#{dir}/test_file\",\n  }\n\n  # The name of the file is chosen to be *.exe so it works on windows and *nix\n  # because windows inspects the PATHEXT environment variable in 1.9.3 and later.\n  file { \"#{dir}/must_exist.exe\":\n    ensure => file,\n    mode => \"0755\",\n  }\nMANIFEST\n\n  on agent, \"ls #{dir}/test_file\"\nend\n"
  },
  {
    "path": "acceptance/tests/ticket_9862_puppet_runs_without_service_user_or_group_present.rb",
    "content": "test_name \"#9862: puppet runs without service user or group present\"\n\ntag 'audit:high',     # startup/configuration, high impact, low risk\n    'audit:refactor',    # Use block style `test_name`\n    'audit:integration' # could easily be acceptance, not package dependant,\n                        # but changing a person running the tests users and\n                        # groups can be very onerous\n\n# puppet doesn't try to manage ownership on windows.\nconfine :except, :platform => 'windows'\n\nrequire 'puppet/acceptance/temp_file_utils'\nextend Puppet::Acceptance::TempFileUtils\ninitialize_temp_dirs\n\ndef assert_ownership(agent, location, expected_user, expected_group)\n  permissions = stat(agent, location)\n  assert_equal(expected_user, permissions[0], \"Owner #{permissions[0]} does not match expected #{expected_user}\")\n  assert_equal(expected_group, permissions[1], \"Group #{permissions[1]} does not match expected #{expected_group}\")\nend\n\ndef missing_directory_for(agent, dir)\n  agent_dir = get_test_file_path(agent, dir)\n  on(agent, \"rm -rf #{agent_dir}\")\n  agent_dir\nend\n\nteardown do\n  agents.each do |agent|\n    step \"ensure puppet resets it's user/group settings\"\n    on(agent, puppet('apply', '-e', '\"notify { puppet_run: }\"'))\n    on(agent, \"find \\\"#{agent.puppet['vardir']}\\\" -user exist_u\", {:acceptable_exit_codes => [0, 1]}) do |result|\n      assert_equal('', result.stdout)\n    end\n    on(agent, puppet('resource', 'user', 'exist_u', 'ensure=absent'))\n    on(agent, puppet('resource', 'group', 'exist_g', 'ensure=absent'))\n  end\nend\n\nstep \"when the user and group are missing\"\nagents.each do |agent|\n  logdir = missing_directory_for(agent, 'log')\n\n  on(agent, puppet('apply',\n                   '-e', '\"notify { puppet_run: }\"',\n                   '--logdir', logdir,\n                   '--user', 'missinguser',\n                   '--group', 'missinggroup')) do |result|\n\n    assert_match(/puppet_run/, result.stdout)\n    assert_ownership(agent, logdir, root_user(agent), root_group(agent))\n  end\nend\n\nstep \"when the user and group exist\"\nagents.each do |agent|\n  logdir = missing_directory_for(agent, 'log')\n\n  on(agent, puppet('resource', 'user', 'exist_u', 'ensure=present'))\n  on(agent, puppet('resource', 'group', 'exist_g', 'ensure=present'))\n\n  on(agent, puppet('apply',\n                   '-e', '\"notify { puppet_run: }\"',\n                   '--logdir', logdir,\n                   '--user', 'exist_u',\n                   '--group', 'exist_g')) do |result|\n\n    assert_match(/puppet_run/, result.stdout)\n    assert_ownership(agent, logdir, 'exist_u', 'exist_g')\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/utf8/utf8-in-catalog.rb",
    "content": "test_name 'utf-8 characters in cached catalog' do\n\n  tag 'audit:high', # utf-8 is high impact in general\n      'audit:integration', # not package dependent but may want to vary platform by LOCALE/encoding\n      'audit:refactor', # use mk_tmp_environment_with_teardown\n      'server'\n\n  utf8chars     = \"\\u20ac\\u2030\\u3118\\u4e07\\u7af9\\u00dc\\u00d6\"\n  file_content  = \"This is the file content. file #{utf8chars}\"\n  codedir       = master.tmpdir(\"code\")\n  on(master, \"rm -rf '#{codedir}'\")\n  env_dir = \"#{codedir}/environments\"\n  agents.each do |agent|\n\n    step \"agent name: #{agent.hostname}, platform: #{agent.platform}\"\n    agent_vardir = agent.tmpdir(\"agent_vardir\")\n    agent_file   = agent.tmpfile(\"file\" + utf8chars)\n    teardown do\n      on(agent, \"rm -rf '#{agent_vardir}' '#{agent_file}'\")\n\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n\n    step \"Apply manifest\" do\n      on(agent, \"rm -rf '#{agent_file}'\", :environment => { :LANG => \"en_US.UTF-8\" })\n\n      master_manifest = <<PP\nFile {\n  ensure => directory,\n  mode => \"0755\",\n}\n\nfile {\n  '#{codedir}/':;\n  '#{codedir}/environments':;\n  '#{codedir}/environments/production':;\n  '#{codedir}/environments/production/manifests':;\n}\n\nfile { '#{env_dir}/production/manifests/site.pp' :\n  ensure => file,\n  mode => '0644',\n  content => '\n    file { \"#{agent_file}\" :\n      ensure => file,\n      mode => \"0644\",\n      content => \"#{file_content}\n    \",\n    }\n  ',\n}\nPP\n\n      apply_manifest_on(master, master_manifest, {:acceptable_exit_codes => [0, 2],\n                                                  :catch_failures => true, :environment => { :LANG => \"en_US.UTF-8\" }})\n    end\n\n    master_opts = {\n        'main'  => {\n            'environmentpath' => \"#{env_dir}\",\n        },\n        'agent' => {\n            'use_cached_catalog' => 'true'\n        }\n    }\n\n    with_puppet_running_on(master, master_opts, codedir) do\n      step \"apply utf-8 catalog\" do\n        on(agent, puppet(\"agent -t --vardir '#{agent_vardir}'\"),\n           { :acceptable_exit_codes => [2], :environment => { :LANG => \"en_US.UTF-8\" } })\n      end\n\n      step \"verify cached catalog\" do\n        catalog_file_name = \"#{agent_vardir}/client_data/catalog/#{agent.node_name}.json\"\n\n        on(agent, \"cat '#{catalog_file_name}'\", :environment => { :LANG => \"en_US.UTF-8\" }) do |result|\n          assert_match(/#{agent_file}/, result.stdout, \"cached catalog does not contain expected agent file name\")\n          assert_match(/#{file_content}/, result.stdout, \"cached catalog does not contain expected file content\")\n        end\n      end\n\n      step \"apply cached catalog\" do\n        on(agent, puppet(\"resource file '#{agent_file}' ensure=absent\"), :environment => { :LANG => \"en_US.UTF-8\" })\n        on(agent, puppet(\"catalog apply --vardir '#{agent_vardir}' --terminus json\"), :environment => { :LANG => \"en_US.UTF-8\" })\n        on(agent, \"cat '#{agent_file}'\", :environment => { :LANG => \"en_US.UTF-8\" }) do |result|\n          assert_match(/#{utf8chars}/, result.stdout, \"result stdout did not contain \\\"#{utf8chars}\\\"\")\n        end\n      end\n    end\n  end\nend \n \n"
  },
  {
    "path": "acceptance/tests/utf8/utf8-in-file-resource.rb",
    "content": "test_name 'utf-8 characters in resource title and param values' do\n\n  tag 'audit:high',       # utf-8 is high impact in general\n      'audit:integration' # not package dependent but may want to vary platform by LOCALE/encoding\n\n  confine :except, :platform => [\n    'windows',    # PUP-6983\n    'aix',        # PUP-7194\n  ]   \n\n  # utf8chars = \"€‰ㄘ万竹ÜÖ\"\n  utf8chars = \"\\u20ac\\u2030\\u3118\\u4e07\\u7af9\\u00dc\\u00d6\"\n  agents.each do |agent|\n    puts \"agent name: #{agent.node_name}, platform: #{agent.platform}\"\n    agent_file = agent.tmpfile(\"file\" + utf8chars) \n    teardown do\n      on(agent, \"rm -rf #{agent_file}\")\n    end\n    # remove this file, so puppet can create it and not merely correct\n    # its drift.\n    on(agent, \"rm -rf #{agent_file}\", :environment => {:LANG => \"en_US.UTF-8\"})\n\n    manifest =\n<<PP\n\nfile { \"#{agent_file}\" :\n  ensure => file,\n  mode => \"0644\",\n  content => \"This is the file content. file #{utf8chars} \n\",\n}\n\nPP\n\n    step \"Apply manifest\" do\n      apply_manifest_on(\n        agent,\n        manifest,\n        {\n          :acceptable_exit_codes => [2],\n          :catch_failures => true, \n          :environment => {:LANG => \"en_US.UTF-8\"}\n        }\n      )\n      on(agent, \"cat #{agent_file}\", :environment => {:LANG => \"en_US.UTF-8\"}) do |result|\n        assert_match(/#{utf8chars}/, result.stdout, \"result stdout did not contain \\\"#{utf8chars}\\\"\")\n      end\n    end\n\n    step \"Drift correction\" do\n      on(\n        agent,\n        \"echo '' > #{agent_file}\",\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      apply_manifest_on(\n        agent,\n        manifest,\n        {\n          :acceptable_exit_codes => [2],\n          :catch_failures => true, \n          :environment => {:LANG => \"en_US.UTF-8\"}\n        }\n      )\n      on(agent, \"cat #{agent_file}\", :environment => {:LANG => \"en_US.UTF-8\"}) do |result|\n        assert_match(/#{utf8chars}/, result.stdout, \"result stdout did not contain \\\"#{utf8chars}\\\"\")\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "acceptance/tests/utf8/utf8-in-function-args.rb",
    "content": "test_name 'utf-8 characters in function parameters' do\n  confine :except, :platform => /debian-12-amd64/ # PUP-12020\n\n  tag 'audit:high',\n      'audit:integration', # not package dependent but may want to vary platform by LOCALE/encoding\n      'audit:refactor'     # if keeping, use mk_tmp_environment_with_teardown\n\n  confine :except, :platform => [\n    'windows',      # PUP-6983\n    'aix',          # PUP-7194\n  ]\n\n  teardown do\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n\n  # utf8chars = \"€‰ㄘ万竹ÜÖ\"\n  utf8chars = \"\\u20ac\\u2030\\u3118\\u4e07\\u7af9\\u00dc\\u00d6\"\n  agents.each do |agent|\n    step 'alert' do\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\" \"'alert(\\\"alert #{utf8chars}\\\")'\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(\n        /#{utf8chars}/,\n        result.stderr,\n        \"Did not find the utf8 chars.\"\n      )\n    end\n\n    step 'assert_type' do\n      on(\n        agent,\n        puppet(\n          \"apply\", \"-e\", \"'notice(assert_type(String, \\\"#{utf8chars}\\\"))'\"\n        ),\n        {\n          :environment => {:LANG => \"en_US.UTF-8\"},\n          :acceptable_exit_codes => [0],\n        }\n      )\n      on(\n        agent,\n        puppet(\"apply\", \"-e 'notice(assert_type(Float, \\\"#{utf8chars}\\\"))'\"),\n        {\n          :environment => {:LANG => \"en_US.UTF-8\"},\n          :acceptable_exit_codes => [1],\n        }\n      )\n    end\n\n    step 'filter' do\n      puppet_cmd = \"'\n        [$a] = [[\\\"abc\\\", \\\"#{utf8chars}\\\", \\\"100\\\"]];\n        [$f] = [filter($a) |$p| {$p =~ /#{utf8chars}/}];\n        notice(\\\"f = $f\\\")\n      '\"\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", puppet_cmd),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(/#{utf8chars}/, result.stdout, \"filter() failed.\")\n    end\n\n    agent_lookup_test_dir = agent.tmpdir(\"lookup_test_dir\")\n\n    mod_name = \"lookup_module\"\n    mod_key = \"#{mod_name}::mod_key_#{utf8chars}\"\n    mod_val = \"mod_val_#{utf8chars}\"\n    env_key = \"env_key_#{utf8chars}\"\n    env_val = \"env_val_#{utf8chars}\"\n    array_key = \"array_key_with_utf8_#{utf8chars}\"\n    array_val_2 = \"array value 2 with utf8 #{utf8chars}\"\n    scalar_key = \"scalar_key_with_utf8_#{utf8chars}\"\n    scalar_val = \"scalar value with utf8 #{utf8chars}\"\n    non_key = \"non_key_#{utf8chars}\"\n\n    step 'apply hiera/lookup manifest' do\n      # I want the banner in the output but\n      # some results: orig_hiera_config,\n      # orig_environmentpath from operations\n      # here are used later, so I don't want\n      # them local to a step block.\n    end\n    lookup_manifest =\n\n<<LOOKUP_MANIFEST\n\nFile {\n  ensure => directory,\n  mode => \"0755\",\n}\n\nfile {\n  \"#{agent_lookup_test_dir}\" :;\n  \"#{agent_lookup_test_dir}/hiera_data\" :;\n  \"#{agent_lookup_test_dir}/environments\" :;\n  \"#{agent_lookup_test_dir}/environments/production\" :;\n  \"#{agent_lookup_test_dir}/environments/production/data\" :;\n  \"#{agent_lookup_test_dir}/environments/production/manifests\" :;\n  \"#{agent_lookup_test_dir}/environments/production/modules\" :;\n  \"#{agent_lookup_test_dir}/environments/production/modules/#{mod_name}\" :;\n  \"#{agent_lookup_test_dir}/environments/production/modules/#{mod_name}/manifests\" :;\n  \"#{agent_lookup_test_dir}/environments/production/modules/#{mod_name}/data\" :;\n}\n\nfile { \"#{agent_lookup_test_dir}/environments/production/modules/#{mod_name}/hiera.yaml\" :\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n  version: 5\n',\n}\n\nfile { \"#{agent_lookup_test_dir}/environments/production/modules/#{mod_name}/data/common.yaml\" :\n  ensure => \"file\",\n  mode => \"0644\",\n  content => '---\n  #{mod_key}: #{mod_val}\n',\n}\n\nfile { \"#{agent_lookup_test_dir}/environments/production/environment.conf\" :\n  ensure => file,\n  mode => \"0644\",\n  content => '\n# environment_data_provider = \"hiera\"\n'\n}\n\nfile { \"#{agent_lookup_test_dir}/environments/production/hiera.yaml\" :\n  ensure => file,\n  mode => \"0644\",\n  content => '\n---\n  version: 5\n'\n}\n\nfile { \"#{agent_lookup_test_dir}/environments/production/data/common.yaml\" :\n  ensure => file,\n  mode => \"0644\",\n  content => '\n---\n  #{env_key} : #{env_val}\n',\n}\n\nfile { \"#{agent_lookup_test_dir}/hiera.yaml\" :\n  ensure => file,\n  mode => \"0644\",\n  content => '---\n:backends:\n  - yaml\n:hierarchy:\n  - common\n:yaml:\n  :datadir: #{agent_lookup_test_dir}/hiera_data\n',\n}\n\nfile { \"#{agent_lookup_test_dir}/hiera_data/common.yaml\" :\n  ensure => file,\n  mode => \"0644\",\n  content => '\n#{array_key} :\n    - \"array value 1\"\n    - \"#{array_val_2}\"\n#{scalar_key} : \"#{scalar_val}\"\n',\n}\n\nLOOKUP_MANIFEST\n    apply_manifest_on(\n      agent, lookup_manifest, :environment => {:LANG => \"en_US.UTF-8\"}\n    )\n    result = on(\n      agent,\n      puppet(\"config\", \"print hiera_config\"),\n      :environment => {:LANG => \"en_US.UTF-8\"}\n    )\n    orig_hiera_config = result.stdout.chomp\n\n    result = on(\n      agent,\n      puppet(\"config\", \"print environmentpath\"),\n      :environment => {:LANG => \"en_US.UTF-8\"}\n    )\n    orig_environmentpath = result.stdout.chomp\n\n    on(\n      agent,\n      puppet(\n        \"config\",\n        \"set hiera_config #{agent_lookup_test_dir}/hiera.yaml\"\n      ),\n      :environment => {:LANG => \"en_US.UTF-8\"}\n    )\n    on(\n      agent,\n      puppet(\n        \"config\", \"set environmentpath #{agent_lookup_test_dir}/environments\"\n      ),\n      :environment => {:LANG => \"en_US.UTF-8\"}\n    )\n\n    step 'hiera' do\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", \"'notice(hiera(\\\"#{array_key}\\\"))'\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(/#{array_val_2}/, result.stdout, \"hiera array lookup\")\n\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", \"'notice(hiera(\\\"#{scalar_key}\\\"))'\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(/#{scalar_val}/, result.stdout, \"hiera scalar lookup\")\n\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", \"'notice(hiera(\\\"#{non_key}\\\"))'\"),\n        {\n          :acceptable_exit_codes => (0..255),\n          :environment => {:LANG => \"en_US.UTF-8\"}\n        }\n      )\n      assert_match(\n        /did not find a value for the name '#{non_key}'/,\n        result.stderr,\n        \"hiera non_key lookup\"\n      )\n    end\n\n    step 'lookup' do\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", \"'notice(lookup(\\\"#{env_key}\\\"))'\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(\n        /#{env_val}/,\n        result.stdout,\n        \"env lookup failed for '#{env_key}'\"\n      )\n\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", \"'notice(lookup(\\\"#{mod_key}\\\"))'\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(\n        /#{mod_val}/,\n        result.stdout,\n        \"module lookup failed for '#{mod_key}'\"\n      )\n\n      on(\n        agent,\n        puppet(\"config\", \"set hiera_config #{orig_hiera_config}\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      on(\n        agent,\n        puppet(\"config\", \"set environmentpath #{orig_environmentpath}\"),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n    end\n\n    step 'dig' do\n      hash_string = \"{\n        a => {\n          b => [\n            {\n              x => 10,\n              y => 20,\n            },\n            {\n              x => 100,\n              y => \\\"dig_result = #{utf8chars}\\\"\n            },\n          ]\n        }\n      }\"\n      puppet_cmd = \"'\n        [$v] = [#{hash_string}];\n        [$dig_result] = [dig($v, a, b, 1, y)];\n        notice($dig_result)\n      '\"\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", puppet_cmd),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(\n        /dig_result = #{utf8chars}/,\n        result.stdout,\n        \"dig() test failed.\"\n      )\n    end\n\n    step 'match' do\n      strings = [\n        \"string1_#{utf8chars}\",\n        \"string2_#{utf8chars}\",\n        \"string3_no-utf8\",\n        \"string4_no-utf8\"\n      ]\n      puppet_cmd = \"'\n        [$vec] = [#{strings}];\n        [$found] = [match($vec, /#{utf8chars}/)];\n        notice($found)\n      '\"\n      result = on(\n        agent,\n        puppet(\"apply\", \"-e\", puppet_cmd),\n        :environment => {:LANG => \"en_US.UTF-8\"}\n      )\n      assert_match(\n        /[[#{utf8chars}], [#{utf8chars}], , ]/,\n        result.stdout,\n        \"match() result unexpected\"\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/utf8/utf8-in-puppet-describe.rb",
    "content": "test_name 'utf-8 characters in module doc string, puppet describe' do\n\n  tag 'audit:high',      # utf-8 is high impact in general, puppet describe low risk?\n      'audit:integration', # not package dependent but may want to vary platform by LOCALE/encoding\n      'audit:refactor'     # if keeping, use mk_tmp_environment_with_teardown\n                           # remove with_puppet_running_on unless pluginsync is absolutely necessary\n                           # (if it is, add 'server' tag\n\n  # utf8chars = \"€‰ㄘ万竹ÜÖ\"\n  utf8chars = \"\\u20ac\\u2030\\u3118\\u4e07\\u7af9\\u00dc\\u00d6\"\n\n  master_mod_dir = master.tmpdir(\"describe_master\")\n  on(master, \"chmod -R 755 #{master_mod_dir}\");\n  teardown do\n    on(master, \"rm -rf #{master_mod_dir}\")\n\n    agents.each do |agent|\n      on(agent, puppet('config print lastrunfile')) do |command_result|\n        agent.rm_rf(command_result.stdout)\n      end\n    end\n  end\n  master_manifest = \n<<MASTER_MANIFEST\n\nFile {\n  ensure => directory,\n  mode => \"0755\",\n}\n\nfile {\n  '#{master_mod_dir}/code':;\n  '#{master_mod_dir}/code/environments':;\n  '#{master_mod_dir}/code/environments/production':;\n  '#{master_mod_dir}/code/environments/production/modules':;\n  '#{master_mod_dir}/code/environments/production/modules/master_mytype_module':;\n  '#{master_mod_dir}/code/environments/production/modules/master_mytype_module/lib':;\n  '#{master_mod_dir}/code/environments/production/modules/master_mytype_module/lib/puppet':;\n  '#{master_mod_dir}/code/environments/production/modules/master_mytype_module/lib/puppet/type':;\n}\n\nfile { '#{master_mod_dir}/code/environments/production/modules/master_mytype_module/lib/puppet/type/master_mytype.rb' :\n  ensure => file,\n  mode => '0755',\n  content => '\nPuppet::Type.newtype(:master_mytype) do\n  @doc = \"Testing to see if puppet handles describe blocks correctly\nwhen they contain utf8 characters, such as #{utf8chars}\n\"\n  newparam(:name) do\n    isnamevar\n    desc \" name parameter for mytype, also with some utf8 chars #{utf8chars}\"\n  end\nend\n',\n}\n\nMASTER_MANIFEST\n\n  step \"Apply master manifest\" do\n    apply_manifest_on(master, master_manifest)\n  end\n  master_opts = {\n    'main' => {\n       'environmentpath' => \"#{master_mod_dir}/code/environments\",\n    }\n  }\n\n  step \"Start puppet server\"\n  with_puppet_running_on(master, master_opts, master_mod_dir) do\n    agents.each do |agent|\n      puts \"agent name: #{agent.hostname}, platform: #{agent.platform}\"\n      step \"Run puppet agent for plugin sync\" do \n        on(\n          agent, puppet(\"agent\", \"-t\"),\n          :acceptable_exit_codes => [0, 2]\n        )\n      end\n\n      step \"Puppet describe for master-hosted mytype\" do \n        on(agent, puppet(\"describe\", \"master_mytype\")) do |result|\n          assert_match(\n            /master_mytype.*such as #{utf8chars}/m,\n            result.stdout,\n  \"Main description of master_mytype did not match utf8 chars, '#{utf8chars}'\"\n          )\n\n          assert_match(\n            /name parameter.*chars #{utf8chars}/,\n            result.stdout,\n            \"Name parameter description of master_mytype did not match utf8 chars, '#{utf8chars}'\"\n          )\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/utf8/utf8-recursive-copy.rb",
    "content": "test_name \"PUP-8735: UTF-8 characters are preserved after recursively copying directories\" do\n\n  tag 'audit:high', # utf-8 is high impact in general\n      'audit:integration' # not package dependent but may want to vary platform by LOCALE/encoding\n\n  # Translation is not supported on these platforms:\n  confine :except, :platform => /^solaris/\n\n  # for file_exists?\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  # for enable_locale_language\n  require 'puppet/acceptance/i18n_utils'\n  extend Puppet::Acceptance::I18nUtils\n\n  agents.each do |host|\n    filename = \"Fișier\"\n    content = <<-CONTENT\n閑けさや\n岩にしみいる\n蝉の声\n    CONTENT\n\n    workdir = host.tmpdir(\"tmp#{rand(999999).to_i}\")\n    source_dir = \"#{workdir}/Adresář\"\n    target_dir = \"#{workdir}/目录\"\n\n    manifest = %Q|\nfile { [\"#{workdir}\", \"#{source_dir}\"]:\n  ensure => directory,\n}\n\nfile { \"#{source_dir}/#{filename}\":\n  ensure  => file,\n  content => \"#{content}\",\n}\n\nfile { \"#{source_dir}/#{filename}_Copy\":\n  ensure => file,\n  source =>  \"#{source_dir}/#{filename}\",\n}\n\nfile { \"#{target_dir}\":\n  ensure  => directory,\n  source  => \"#{source_dir}\",\n  recurse => remote,\n  replace => true,\n}|\n\n    step \"Ensure the en_US locale is enabled (and skip this test if not)\" do\n      if enable_locale_language(host, 'en_US').nil?\n        skip_test(\"Host #{host} is missing the en_US locale. Skipping this test.\")\n      end\n    end\n\n    step \"Create and recursively copy a directory with UTF-8 filenames and contents\" do\n      apply_manifest_on(host, manifest, environment: {'LANGUAGE' => 'en_US', 'LANG' => 'en_US'})\n    end\n\n    step \"Ensure that the files' names and their contents are preserved\" do\n      [\"#{target_dir}/#{filename}\", \"#{target_dir}/#{filename}_Copy\"]. each do |filepath|\n        assert(file_exists?(host, filepath), \"Expected the UTF-8 directory's files to be recursivly copied, but they were not\")\n        assert(file_contents(host, filepath) == content, \"Expected the contents of the copied UTF-8 files to be preserved, but they were not\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/windows/PA-2191_windows_nocodepage_utf8_fallback.rb",
    "content": "test_name 'PA-2191 - winruby fallsback to UTF8 for invalid CodePage' do\n  confine :to, platform: 'windows'\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  agents.each do |host|\n\n    initial_chcp_code = on(host, 'cmd.exe /c chcp').stdout.delete('^0-9')\n\n    teardown do\n      on(host, \"cmd.exe /c chcp #{initial_chcp_code}\")\n    end\n\n    step 'set an invalid Code Page and check if puppet can run' do\n      on(host, 'cmd.exe /c chcp 720')\n      begin\n        on(host, puppet('--version'), acceptable_exit_codes: [0])\n      rescue StandardError\n        fail_test('Code Page 720 is invalid')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/windows/PUP-9719_windows_system_first_pa_run.rb",
    "content": "# frozen_string_literal: true\n\ntest_name 'PUP-9719 Windows First Agent run as SYSTEM sets cache file permissions correctly' do\n  tag 'risk:high',\n      'audit:high',\n      'audit:integration'\n\n  confine :to, platform: 'windows'\n\n  require 'puppet/acceptance/temp_file_utils'\n  extend Puppet::Acceptance::TempFileUtils\n\n  agents.each do |agent|\n    publicdir = on(agent, puppet('config print publicdir')).stdout.chomp\n    client_datadir = on(agent, puppet('config print client_datadir')).stdout.chomp\n\n    teardown do\n      on agent, 'schtasks /delete /tn PuppetSystemRun /F'\n      on agent, \"rm -rf #{publicdir}/*\"              unless publicdir.empty?\n      on agent, \"rm -rf #{client_datadir}/catalog/*\" unless client_datadir.empty?\n    end\n\n    step 'Clean the public and catalog directories first' do\n      fail_test(\"The publicdir config is not set!\")      if publicdir.empty?\n      fail_test(\"The client_datadir config is not set!\") if client_datadir.empty?\n\n      on agent, \"rm -rf #{publicdir}/*\"\n      on agent, \"rm -rf #{client_datadir}/catalog/*\"\n    end\n\n    step 'Create and run a scheduled task on System Account.' do\n      date_format = if agent['locale'] == 'ja'\n                      '%Y/%m/%d'\n                    else\n                      '%m/%d/%Y'\n                    end\n      on agent, %Q(\n        schtasks /create /tn PuppetSystemRun /RL HIGHEST /RU SYSTEM /F /SC ONCE /SD \\\n        #{(Date.today + 1).strftime(date_format)} /ST 23:59 /TR \\\n        'cmd /c \\\"%ProgramFiles%\\\\Puppet Labs\\\\Puppet\\\\bin\\\\puppet.bat\\\" agent -t'\n      )\n      on agent, 'schtasks /run /tn PuppetSystemRun'\n    end\n\n    step 'Wait for Puppet Agent run to complete' do\n      last_puppet_run = File.join(publicdir, 'last_run_summary.yaml')\n      trymax = 10\n      try = 1\n      last_wait = 2\n      wait = 3\n      file_found = false\n      while try <= trymax\n        if file_exists?(agent, last_puppet_run)\n          logger.info('Puppet run has completed')\n          file_found = true\n          break\n        end\n        @logger.warn \"Wait for Puppet (SYSTEM) run to complete, Try #{try}, Trying again in #{wait} seconds\"\n        sleep wait\n        (last_wait, wait) = wait, last_wait + wait\n        try += 1\n      end\n      fail_test(\"Puppet Run (SYSTEM) didn't complete\") unless file_found\n    end\n\n    step \"Test that normal PA run under Administrator doesn't fail.\" do\n      on agent, 'cmd /c puppet agent -t --detailed-exitcodes', acceptable_exit_codes: [0]\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/windows/QA-506_windows_exit_codes_test.rb",
    "content": "test_name \"Windows Exec `exit_code` Parameter Acceptance Test\"\n\ntag 'risk:high',\n    'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:integration' # exec resource succeeds when the `exit_code` parameter\n                        # is given a windows specific exit code and a exec\n                        # returns that exit code, ie. it either correctly matches\n                        # exit_code parameter to returned exit code, or ignores both (;\n\nconfine :to, :platform => 'windows'\n\npass_exitcode_manifest = <<-MANIFEST\nwinexitcode::execute { '0':\n  exit_code => 0\n}\nMANIFEST\n\nupper_8bit_boundary_manifest = <<-MANIFEST\nwinexitcode::execute { '255':\n  exit_code => 255\n}\nMANIFEST\n\ncross_8bit_boundary_manifest = <<-MANIFEST\nwinexitcode::execute { '256':\n  exit_code => 256\n}\nMANIFEST\n\nupper_32bit_boundary_manifest = <<-MANIFEST\nwinexitcode::execute { '4294967295':\n  exit_code => 4294967295\n}\nMANIFEST\n\ncross_32bit_boundary_manifest = <<-MANIFEST\nwinexitcode::execute { '4294967296':\n  exit_code => 0\n}\nMANIFEST\n\nnegative_boundary_manifest = <<-MANIFEST\nwinexitcode::execute { '-1':\n  exit_code => 4294967295\n}\nMANIFEST\n\nstep \"Install Custom Module for Testing\"\n\nagents.each do |agent|\n  if (on(agent, puppet(\"--version\")).stdout.split('.')[0].to_i < 4)\n    module_path_config_property = \"confdir\"\n  else\n    module_path_config_property = \"codedir\"\n  end\n\n  native_modules_path = on(agent, puppet(\"config print #{module_path_config_property}\")).stdout.gsub('C:', '/cygdrive/c').strip\n\n  #Check to see if we are running on Windows 2003. Do a crazy hack to get around SCP issues.\n  if (on(agent, facter(\"os.release.major\")).stdout =~ /2003/)\n    on(agent, \"ln -s #{native_modules_path.gsub(/ /, '\\ ')} /tmp/puppet_etc\")\n    modules_path = '/tmp/puppet_etc'\n  else\n    modules_path = native_modules_path\n  end\n\n  #Create the modules directory if it doesn't exist\n  if on(agent, \"test ! -d #{modules_path}/modules\", :acceptable_exit_codes => [0,1]).exit_code == 0\n    on(agent, \"mkdir -p #{modules_path}/modules\")\n  end\n\n  # copy custom module.\n  scp_to(agent, File.expand_path(File.join(File.dirname(__FILE__), \"winexitcode\")), \"#{modules_path}/modules\")\nend\n\nagents.each do |agent|\n  step \"Verify '0' is a Valid Exit Code\"\n\n  #Apply the manifest and verify Puppet returns success.\n  on(agent, puppet('apply', '--debug'), :stdin => pass_exitcode_manifest)\n\n  step \"Verify Unsigned 8bit Upper Boundary\"\n\n  #Apply the manifest and verify Puppet returns success.\n  on(agent, puppet('apply', '--debug'), :stdin => upper_8bit_boundary_manifest)\n\n  step \"Verify Unsigned 8bit Cross Boundary\"\n\n  #Apply the manifest and verify Puppet returns success.\n  on(agent, puppet('apply', '--debug'), :stdin => cross_8bit_boundary_manifest)\n\n  step \"Verify Unsigned 32bit Upper Boundary\"\n\n  #Apply the manifest and verify Puppet returns success.\n  on(agent, puppet('apply', '--debug'), :stdin => upper_32bit_boundary_manifest)\n\n  step \"Verify Unsigned 32bit Cross Boundary\"\n\n  #Apply the manifest and verify Puppet returns success.\n  on(agent, puppet('apply', '--debug'), :stdin => cross_32bit_boundary_manifest)\n\n  step \"Verify Negative Exit Code Rollover Boundary\"\n\n  #Apply the manifest and verify Puppet returns success.\n  on(agent, puppet('apply', '--debug'), :stdin => negative_boundary_manifest)\nend\n"
  },
  {
    "path": "acceptance/tests/windows/QA-760_win_dash_dot_file_test.rb",
    "content": "test_name \"QA-760 - Windows Files Containing '-' and '.'\"\n\ntag 'risk:high',\n    'audit:high',\n    'audit:refactor',   # Use block style `test_name`\n    'audit:integration'\n\nconfine(:to, :platform => 'windows')\n\ntemp_folder = <<-MANIFEST\nfile { 'c:/temp':\n  ensure => directory\n}\nMANIFEST\n\ndash_dot_file = <<-MANIFEST\nfile { 'c:/temp/dash-dot-%s.file':\n  ensure  => file,\n  content => \"The file has new content: %s!\",\n}\nMANIFEST\n\nstep \"Generate Manifest\"\n\nfirst_run_manifest = \"\"\nsecond_run_manifest = \"\"\n\nfor i in 1..100\n  first_run_manifest += \"#{dash_dot_file}\\n\" % [i,i]\n  second_run_manifest += \"#{dash_dot_file}\\n\" % [i,-i]\nend\n\nstep \"Create Temp Folder\"\n\nagents.each do |agent|\n  on(agent, puppet('apply', '--debug'), :stdin => temp_folder)\nend\n\nstep \"Create Dash Dot File 100 Times\"\n\nagents.each do |agent|\n  on(agent, puppet('apply', '--debug'), :stdin => first_run_manifest)\nend\n\nstep \"Update Dash Dot File 100 Times\"\n\nagents.each do |agent|\n  on(agent, puppet('apply', '--debug'), :stdin => second_run_manifest)\nend\n"
  },
  {
    "path": "acceptance/tests/windows/enable_password_changes_special_users.rb",
    "content": "test_name 'Puppet should change passwords for disabled, expired, or locked out Windows user accounts' do\n\n  tag 'audit:high',\n      'audit:acceptance'\n\n  require 'date'\n  require 'puppet/acceptance/windows_utils'\n\n  extend Puppet::Acceptance::WindowsUtils\n  confine :to, platform: 'windows'\n\n  def random_username\n    \"pl#{rand(999999).to_i}\"\n  end\n\n  def change_password_manifest(username)\n    return <<-MANIFEST\n    user { '#{username}':\n      ensure   => 'present',\n      password => '#{NEW_PASSWORD}'\n    }\n    MANIFEST\n  end\n\n  INITIAL_PASSWORD='iP@ssword'\n  NEW_PASSWORD=\"Password-#{rand(999999).to_i}\"\n\n  agents.each do |host|\n    disabled_username = random_username\n\n    step \"Create a disabled user account\" do\n      on(host, \"cmd.exe /c net user #{disabled_username} #{INITIAL_PASSWORD} /active:no /add\")\n    end\n\n    step \"Change the disabled user account's password with puppet\" do\n      apply_manifest_on(host, change_password_manifest(disabled_username))\n    end\n\n    step \"Enabling the user account as the AccountManagement context can't verify disabled users\" do\n      on(host, \"cmd.exe /c net user #{disabled_username} /active:yes\")\n    end\n\n    step \"Ensure the password was changed\" do\n      assert_password_matches_on(host, disabled_username, NEW_PASSWORD, \"Expected the disabled user account's password to be changed\")\n    end\n\n    expired_username = random_username\n\n    step \"Create an expired user account\" do\n      date_format = host[\"locale\"] == \"ja\" ? \"%y/%m/%d\" : \"%m/%d/%y\"\n      on(host, \"cmd.exe /c net user #{expired_username} #{INITIAL_PASSWORD} /expires:#{(Date.today - 1).strftime(date_format)} /add\")\n    end\n\n    step \"Change the expired user's password with puppet\" do\n      apply_manifest_on(host, change_password_manifest(expired_username))\n    end\n\n    step \"Make expired user valid, as AccountManagement context can't verify expired user credentials\" do\n      date_format = host[\"locale\"] == \"ja\" ? \"%y/%m/%d\" : \"%m/%d/%y\"\n      on(host, \"cmd.exe /c net user #{expired_username} /expires:#{(Date.today + 1).strftime(date_format)}\")\n      on(host, \"cmd.exe /c net user #{expired_username} /active:yes\")\n    end\n\n    step \"Ensure the password was changed\" do\n      assert_password_matches_on(host, expired_username, NEW_PASSWORD, \"Expected the expired user account's password to be changed\")\n    end\n\n    locked_username = random_username\n\n    step \"Create a user account, lower the account lockout threshold, and lock the new account by using the wrong password\" do\n      on(host, \"cmd.exe /c net user #{locked_username} #{INITIAL_PASSWORD} /add\")\n      on(host, \"cmd.exe /c net accounts /lockoutthreshold:1\")\n      on(host, \"cmd.exe /c runas /user:#{locked_username} hostname.exe\", accept_all_exit_codes: true)\n    end\n\n    step \"Change the locked account's password with puppet\" do\n      apply_manifest_on(host, change_password_manifest(locked_username))\n    end\n\n    step \"Unlock the account(set it as active) as AccountManagement context can't verify credentials of locked out accounts\" do\n      on(host, \"cmd.exe /c net user #{locked_username} /active:yes\")\n    end\n\n    step \"Ensure the password was changed\" do\n      assert_password_matches_on(host, locked_username, NEW_PASSWORD, \"Expected the locked out user account's password to be changed\")\n    end\n\n    teardown do\n      on(host, \"cmd.exe /c net accounts /lockoutthreshold:0\")\n      host.user_absent(disabled_username)\n      host.user_absent(expired_username)\n      host.user_absent(locked_username)\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/windows/service_manager_integration.rb",
    "content": "test_name 'Test agent state via service control manager' do\n\n  tag 'audit:integration'\n\n  confine :to, platform: 'windows'\n\n  teardown do\n    agents.each do |agent|\n      state = query_agent_state(agent)\n      if state != \"STOPPED\"\n        stop_puppet_windows_daemon(agent)\n        ensure_agent_state(agent, \"STOPPED\")\n      end\n    end\n  end\n\n  def query_agent_state(host)\n    on(host, 'sc query puppet').stdout.match(/STATE.+\\s{1}(\\w+)/)[1]\n  end\n\n  def start_puppet_windows_daemon(host)\n    on(host, 'sc start puppet')\n  end\n\n  def stop_puppet_windows_daemon(host)\n    on(host, 'sc stop puppet')\n  end\n\n  def ensure_agent_state(host, state)\n    retry_attempts = 0\n    while retry_attempts < 5\n      return if state == query_agent_state(host)\n      retry_attempts += 1\n      sleep 1\n    end\n    fail_test \"State not #{state} after 5 tries\"\n  end\n\n  step 'store initial state' do\n\n    agents.each do |agent|\n      initial_state = query_agent_state(agent)\n      assert_match(\"STOPPED\", initial_state, \"agent daemon should initially be stopped\")\n\n      start_puppet_windows_daemon(agent)\n      ensure_agent_state(agent, \"RUNNING\")\n      stop_puppet_windows_daemon(agent)\n      ensure_agent_state(agent, \"STOPPED\")\n    end\n  end\nend\n"
  },
  {
    "path": "acceptance/tests/windows/winexitcode/manifests/execute.pp",
    "content": "define winexitcode::execute (\n  $exit_code = 0,\n) {\n\ninclude winexitcode\n\n  exec { \"testcommand_${exit_code}\":\n    command   => \"c:\\\\Windows\\\\System32\\\\cmd.exe /c c:\\\\tmp\\\\test.bat ${title}\",\n    returns   => $exit_code,\n    logoutput => true,\n    require   => Class['winexitcode'],\n  }\n}\n"
  },
  {
    "path": "acceptance/tests/windows/winexitcode/manifests/init.pp",
    "content": "class winexitcode {\n  file { \"c:\\\\tmp\":\n    ensure  => directory,\n    mode    => '0660',\n    owner   => 'Administrator',\n    group   => 'Administrators',\n  }\n  ->\n  file { \"c:\\\\tmp\\\\test.bat\":\n    ensure  => file,\n    mode    => '0660',\n    owner   => 'Administrator',\n    group   => 'Administrators',\n    content => 'exit /b %1',\n  }\n}\n"
  },
  {
    "path": "acceptance/vmpooler.cfg",
    "content": "---\nHOSTS:\n  ubuntu1404-64:\n    roles:\n      - master\n      - dashboard\n      - database\n      - agent\n    platform: ubuntu-14.04-amd64\n    template: ubuntu-1404-x86_64\n    hypervisor: vmpooler\n  ubuntu1404-64-agent:\n    roles:\n      - agent\n    platform: ubuntu-14.04-amd64\n    template: ubuntu-1404-x86_64\n    hypervisor: vmpooler\nCONFIG:\n  nfs_server: none\n  consoleport: 443\n  pooling_api: http://vmpooler.delivery.puppetlabs.net/\n\n"
  },
  {
    "path": "api/docs/http_api_index.md",
    "content": "A Puppet server provides several services via HTTP API, and the Puppet\nagent application uses those services to resolve a node's credentials, retrieve\na configuration catalog, retrieve file data, and submit reports.\n\nIn general, these APIs aren't designed for use by tools other than Puppet agent.\nThis is gradually changing, although we expect external use of these APIs to\nremain low for the foreseeable future. The server ignores any parameters it isn't expecting.\n\nV1/V2 HTTP APIs (Removed)\n---------------\n\nThe V1 and V2 APIs were removed in Puppet 4.0.0. The routes that were previously\nunder `/` or `/v2.0` can now be found under the [`/puppet/v3`](#puppet-v3-http-api)\nAPI or [`/puppet-ca/v1`](#ca-v1-http-api) API.\n\nStarting with version 2.1, the Puppet Server 2.x series provides both the\ncurrent and previous API endpoints, and can serve nodes running Puppet agent 3.x\nand 4.x. However, Rack masters, WEBrick masters, and Puppet Server 2.0 cannot\nserve nodes running Puppet 3.x.\n\nPuppet and Puppet CA APIs\n------------------\n\nBeginning with Puppet 4, Puppet's HTTP API has been split into two APIs, which\nare versioned separately. There is now an API for configuration-related services\nand a separate one for the certificate authority (CA).\n\nAll configuration endpoints are prefixed with `/puppet`, while all CA endpoints are\nprefixed with `/puppet-ca`. All endpoints are explicitly versioned: the prefix\nis always immediately followed by a string such as `/v3` (a directory separator,\nthe letter `v`, and the version number of the API).\n\n### Authorization\n\nAs of Puppet 7, support for legacy auth.conf is removed. Puppet Server 7\nenforces all authorization using its `auth.conf`. See\nhttps://puppet.com/docs/puppetserver/latest/config_file_auth.html for more\ndetails.\n\nPuppet V3 HTTP API\n------------------\n\nThe Puppet agent application uses several network services to manage systems.\nThese services are all grouped under the `/puppet` API. Other tools can access\nthese services and use the Puppet server's data for other purposes.\n\nThe V3 API contains endpoints of two types: those that are based on dispatching\nto Puppet's internal \"indirector\" framework, and those that are not (namely the\n[environment endpoints](#environment-endpoints)).\n\nEvery HTTP endpoint that dispatches to the indirector follows the form:\n`/puppet/v3/:indirection/:key?environment=:environment` where:\n\n* `:environment` is the name of the environment that should be in effect for\n  the request. Not all endpoints need an environment, but the query\n  parameter must always be specified.\n* `:indirection` is the indirection to dispatch the request to.\n* `:key` is the \"key\" portion of the indirection call.\n\nUsing this API requires significant understanding of how Puppet's internal\nservices are structured, but the following documents attempt to specify what is\navailable and how to interact with it.\n\n### Configuration Management Services\n\nThese services are all directly used by the Puppet agent application, in order\nto manage the configuration of a node.\n\nThese endpoints accept payload formats formatted as JSON (MIME type\n`application/json`) except for `File Content` and `File Bucket File` which\nalways use `application/octet-stream`.\n\n* [Facts](./http_facts.md)\n* [Catalog](./http_catalog.md)\n* [Node](./http_node.md)\n* [File Bucket File](./http_file_bucket_file.md)\n* [File Content](./http_file_content.md)\n* [File Metadata](./http_file_metadata.md)\n* [Report](./http_report.md)\n\n### Informational Services\n\nThese services are not directly used by Puppet agent, but may be used by other\ntools.\n\n* [Status](./http_status.md)\n\n### Environments Endpoint\n\nThe `/puppet/v3/environments` endpoint is different as it will only accept payloads\nformatted as JSON and respond with JSON (MIME type of `application/json`).\n\n* [Environments](./http_environments.md)\n\n### Puppet Server-specific endpoints\n\nPuppet Server adds additional `/puppet/v3/` endpoints:\n\n* [Static File Content](https://puppet.com/docs/puppetserver/latest/puppet-api/v3/static_file_content.md)\n* [Environment Classes](https://puppet.com/docs/puppetserver/latest/puppet-api/v3/environment_classes.md)\n\n#### Error Responses\n\nThe `environments` endpoint will respond to error conditions in a uniform manner\nand use standard HTTP response code to signify those errors.\n\n* When the client submits a malformed request, the API will return a 400 Bad\n  Request response.\n* When the client is not authorized, the API will return a 403 Not Authorized\n  response.\n* When the client attempts to use an HTTP method that is not permissible for\n  the endpoint, the API will return a 405 Method Not Allowed response.\n* When the client asks for a response in a format other than JSON, the API will\n  return a 406 Unacceptable response.\n* When the server encounters an unexpected error during the handling of a\n  request, it will return a 500 Server Error response.\n* When the server is unable to find an endpoint handler for an http request,\n  it will return a 404 Not Found response\n\nAll error responses will contain a body, except when it is a HEAD request. The\nerror responses will uniformly be a JSON object with the following properties:\n\n* `message`: (`String`) A human readable message explaining the error.\n* `issue_kind`: (`String`) A unique label to identify the error class.\n\nA [JSON schema for the error objects](../schemas/error.json) is also available.\n\nCA V1 HTTP API\n--------------\n\nThe certificate authority (CA) API contains all of the endpoints supporting Puppet's public key infrastructure (PKI) system. This endpoint is now handled entirely through Puppet Server. See Puppet Server's [HTTP API](https://puppet.com/docs/puppetserver/latest/http_api_index.md) docs for detailed information.\n\nSerialization Formats\n---------------------\n\nPuppet sends messages using `JSON` or as binary data. Not all\nREST services support all of the formats.\n\n* [JSON](https://tools.ietf.org/html/rfc7159)\n* [PSON](./pson.md) was supported in earlier versions of Puppet, but is no longer for performance reasons.\n\n`YAML` was supported in earlier versions of Puppet, but is no longer for security reasons.\n"
  },
  {
    "path": "api/docs/http_catalog.md",
    "content": "Catalog\n=============\n\nThe `catalog` endpoint returns a catalog for the specified node name given the provided facts.\n\nFind\n----\n\nRetrieve a catalog.\n\n    POST /puppet/v3/catalog/:nodename\n    GET /puppet/v3/catalog/:nodename?environment=:environment\n\n### Supported HTTP Methods\n\nPOST, GET\n\n### Supported Response Formats\n\n`application/json`\n\n### Notes\n\nThe POST and GET methods are functionally equivalent. Both provide the 3 parameters specified below: the POST in the\nrequest body, the GET in the query string.\n\nPuppet originally used GET; POST was added because some web servers have a maximum URI length of\n1024 bytes (which is easily exceeded with the `facts` parameter).\n\nThe examples below use the POST method.\n\n### Parameters\n\nFour parameters should be provided to the POST or GET:\n\n- `environment`: the environment name.\n- `facts_format`: must be `application/json`.\n- `facts`: serialized JSON of the facts hash. Since facts can contain `&`, which\n  is also the HTTP query parameter delimiter, facts are doubly-escaped.\n- `transaction_uuid`: a transaction uuid identifying the entire transaction (shows up in the report as well).\n\nTwo optional parameters are required for static catalogs:\n- `static_catalog`: a boolean requesting a\n[static catalog](https://puppet.com/docs/puppet/latest/static_catalogs.html) if available; should always\nbe `true`.\n- `checksum_type`: a dot-separated list of checksum types supported by the agent, for use in file resources of a static\ncatalog. The order signifies preference, highest first.\n\nOptional parameters that may be provided to the POST or GET:\n\n- `configured_environment`: the environment configured on the client. May be\n  provided to notify an ENC that the client requested a specific environment\n  which might differ from what the client believes is its current environment.\n- `job_id`: which orchestration job triggered this catalog request.\n\n### Example Response\n\n#### Catalog found\n\n    POST /puppet/v3/catalog/elmo.mydomain.com\n\n    environment=env&configured_environment=canary_env&facts_format=application%2Fjson&facts=%257B%2522name%2522%253A%2522elmo.mydomain.com%2522%252C%2522values%2522%253A%257B%2522architecture%2522%253A%2522x86_64%2522%257D%257D&transaction_uuid=aff261a2-1a34-4647-8c20-ff662ec11c4c\n\n    HTTP 200 OK\n    Content-Type: application/json\n\n    {\n      \"tags\": [\n        \"settings\",\n        \"multi_param_class\",\n        \"class\"\n      ],\n      \"name\": \"elmo.mydomain.com\",\n      \"version\": 1377473054,\n      \"code_id\": null,\n      \"catalog_uuid\": \"827a74c8-cf98-44da-9ff7-18c5e4bee41e\",\n      \"catalog_format\": 1,\n      \"environment\": \"production\",\n      \"resources\": [\n        {\n          \"type\": \"Stage\",\n          \"title\": \"main\",\n          \"tags\": [\n            \"stage\"\n          ],\n          \"exported\": false,\n          \"parameters\": {\n            \"name\": \"main\"\n          }\n        },\n        {\n          \"type\": \"Class\",\n          \"title\": \"Settings\",\n          \"tags\": [\n            \"class\",\n            \"settings\"\n          ],\n          \"exported\": false\n        },\n        {\n          \"type\": \"Class\",\n          \"title\": \"main\",\n          \"tags\": [\n            \"class\"\n          ],\n          \"exported\": false,\n          \"parameters\": {\n            \"name\": \"main\"\n          }\n        },\n        {\n          \"type\": \"Class\",\n          \"title\": \"Multi_param_class\",\n          \"tags\": [\n            \"class\",\n            \"multi_param_class\"\n          ],\n          \"line\": 10,\n          \"exported\": false,\n          \"parameters\": {\n            \"one\": \"hello\",\n            \"two\": \"world\"\n          }\n        },\n        {\n          \"type\": \"Notify\",\n          \"title\": \"foo\",\n          \"tags\": [\n            \"notify\",\n            \"foo\",\n            \"class\",\n            \"multi_param_class\"\n          ],\n          \"line\": 4,\n          \"exported\": false,\n          \"parameters\": {\n            \"message\": \"One is hello, two is world\"\n          }\n        }\n      ],\n      \"edges\": [\n        {\n          \"source\": \"Stage[main]\",\n          \"target\": \"Class[Settings]\"\n        },\n        {\n          \"source\": \"Stage[main]\",\n          \"target\": \"Class[main]\"\n        },\n        {\n          \"source\": \"Stage[main]\",\n          \"target\": \"Class[Multi_param_class]\"\n        },\n        {\n          \"source\": \"Class[Multi_param_class]\",\n          \"target\": \"Notify[foo]\"\n        }\n      ],\n      \"classes\": [\n        \"settings\",\n        \"multi_param_class\"\n      ]\n    }\n\n#### Static Catalog found\n\n~~~\nPOST /puppet/v3/catalog/elmo.mydomain.com\n\nenvironment=env&configured_environment=canary_env&facts_format=application%2Fjson&facts=%7B%22name%22%3A%22elmo.mydomain.com%22%2C%22values%22%3A%7B%22architecture%22%3A%22x86_64%22%7D&transaction_uuid=aff261a2-1a34-4647-8c20-ff662ec11c4c&static_catalog=true&checksum_type=sha256.md5\n\nHTTP 200 OK\nContent-Type: application/json\n\n{\n  \"tags\": [\n    \"settings\",\n    \"multi_param_class\",\n    \"class\"\n  ],\n  \"name\": \"elmo.mydomain.com\",\n  \"version\": 1377473054,\n  \"code_id\": \"arbitrary_code_id_string\",\n  \"catalog_uuid\": \"827a74c8-cf98-44da-9ff7-18c5e4bee41e\",\n  \"catalog_format\": 1,\n  \"environment\": \"production\",\n  \"resources\": [\n    {\n      \"type\": \"Stage\",\n      \"title\": \"main\",\n      \"tags\": [\n        \"stage\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Settings\",\n      \"tags\": [\n        \"class\",\n        \"settings\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"main\",\n      \"tags\": [\n        \"class\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Multi_param_class\",\n      \"tags\": [\n        \"class\",\n        \"multi_param_class\"\n      ],\n      \"line\": 10,\n      \"exported\": false,\n      \"parameters\": {\n        \"one\": \"hello\",\n        \"two\": \"world\"\n      }\n    },\n    {\n      \"type\": \"Notify\",\n      \"title\": \"foo\",\n      \"tags\": [\n        \"notify\",\n        \"foo\",\n        \"class\",\n        \"multi_param_class\"\n      ],\n      \"line\": 4,\n      \"exported\": false,\n      \"parameters\": {\n        \"message\": \"One is hello, two is world\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"/tmp/foo\",\n      \"tags\": [\n        \"file\",\n        \"class\"\n      ],\n      \"line\": 12,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"source\": \"puppet:///modules/a_module/foo\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"/tmp/bar\",\n      \"tags\": [\n        \"file\",\n        \"class\"\n      ],\n      \"line\": 16,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"source\": \"puppet:///modules/a_module/bar\",\n        \"recurse\", \"true\"\n      }\n  ],\n  \"edges\": [\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Settings]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[main]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Multi_param_class]\"\n    },\n    {\n      \"source\": \"Class[Multi_param_class]\",\n      \"target\": \"Notify[foo]\"\n    },\n    {\n      \"source\": \"Class[Main]\",\n      \"target\": \"File[/tmp/foo]\"\n    }\n  ],\n  \"classes\": [\n    \"settings\",\n    \"multi_param_class\"\n  ]\n  \"metadata\": {\n    \"/tmp/foo\": {\n      \"checksum\": {\n        \"type\": \"sha256\",\n        \"value\": \"{sha256}5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03\"\n      },\n      \"content_uri\": \"puppet:///modules/a_module/files/foo\",\n      \"destination\": null,\n      \"group\": 20,\n      \"links\": \"manage\",\n      \"mode\": 420,\n      \"owner\": 501,\n      \"path\": \"/etc/puppetlabs/code/environments/production/modules/a_module/files/foo.txt\",\n      \"relative_path\": null,\n      \"source\": \"puppet:///modules/a_module/foo\",\n      \"type\": \"file\"\n    }\n  },\n  \"recursive_metadata\": {\n    \"/tmp/bar\": {\n      \"puppet:///modules/a_module/bar\": [\n        {\n          \"checksum\": {\n            \"type\": \"ctime\",\n            \"value\": \"{ctime}2016-02-19 17:38:36 -0800\"\n          },\n          \"content_uri\": \"puppet:///modules/a_module/files/bar\",\n          \"destination\": null,\n          \"group\": 20,\n          \"links\": \"manage\",\n          \"mode\": 420,\n          \"owner\": 501,\n          \"path\": \"/etc/puppetlabs/code/environments/production/modules/a_module/files/bar\",\n          \"relative_path\": \".\",\n          \"source\": null,\n          \"type\": \"directory\"\n        },\n        {\n          \"checksum\": {\n            \"type\": \"sha256\",\n            \"value\": \"{sha256}962dbd7362c34a20baac8afd13fba734d3d51cc2944477d96ee05a730e5edcb7\"\n          },\n          \"content_uri\": \"puppet:///modules/a_module/files/bar/baz\",\n          \"destination\": null,\n          \"group\": 20,\n          \"links\": \"manage\",\n          \"mode\": 420,\n          \"owner\": 501,\n          \"path\": \"/etc/puppetlabs/code/environments/production/modules/a_module/files/bar\",\n          \"relative_path\": \"baz\",\n          \"source\": null,\n          \"type\": \"file\"\n        }\n      ]\n    }\n  }\n}\n~~~\n\nSchema\n------\n\nIn the POST request body (or the GET query), the facts parameter should conform\nto [the facts schema.](../schemas/facts.json)\n\nA catalog response body conforms to\n[the catalog schema.](../schemas/catalog.json)\n"
  },
  {
    "path": "api/docs/http_certificate.md",
    "content": "Certificate\n=============\n\nThe `certificate` endpoint returns the certificate for the specified name,\nwhich might be either a standard certname or `ca`.\n\nUnder Puppet Server's CA service, the `environment` parameter is ignored and can\nbe omitted. Under a Rack or WEBrick Puppet master, `environment` is required and\nmust be a valid environment, but it has no effect on the response.\n\nFind\n----\n\nGet a certificate.\n\n    GET /puppet-ca/v1/certificate/:nodename?environment=:environment\n\n\n### Supported HTTP Methods\n\nGET\n\n### Supported Response Formats\n\n`text/plain`\n\nThe returned certificate is always in the `.pem` format.\n\n### Parameters\n\nNone\n\n### Responses\n\n#### Certificate found\n\n    GET /puppet-ca/v1/certificate/elmo.mydomain.com?environment=env\n\n    HTTP 200 OK\n    Content-Type: text/plain\n\n    -----BEGIN CERTIFICATE-----\n    MIIFujCCA6KgAwIBAgIBATANBgkqhkiG9w0BAQsFADBiMWAwXgYDVQQDDFdQdXBw\n    ZXQgQ0EgZ2VuZXJhdGVkIG9uIGRoY3A1MC5reWxvLmJhY2tsaW5lLnB1cHBldGxh\n    YnMubmV0IGF0IDIwMTMtMDYtMjQgMTY6MzA6MTcgLTA3MDAwHhcNMTMwNjIzMjMz\n    MDE5WhcNMTgwNjIzMjMzMDE5WjBiMWAwXgYDVQQDDFdQdXBwZXQgQ0EgZ2VuZXJh\n    dGVkIG9uIGRoY3A1MC5reWxvLmJhY2tsaW5lLnB1cHBldGxhYnMubmV0IGF0IDIw\n    MTMtMDYtMjQgMTY6MzA6MTcgLTA3MDAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n    ggIKAoICAQDABq1lmzccjuRmnCdXvTmdeXJGb9S8r8+I+G6fkHTa1WKDSob9PZpS\n    eXJtanbl0zNws9yBt1Dko2zhKDKctBRWf5CT42nDxBZPY7SaD7KaCzb07g9wfWgU\n    BOb/6smyl/iySEmQzzFLRgZbo5A9WLiy/UdyQim1faakevRme2Xi/l/i0TKbpu27\n    DhCS+E8aC8Bvaj0ph0T+TzYphTR76pP5Kps6G7Jyk/HFYrVXnY44X2PEt2mgkEXp\n    xHCbU+qCFMtTLMG+ZArA/noM3I/O6W5LhLSzApjut/M7UdMlpZ45PGDrsvf2R306\n    NcOh+zbbkhxuIaGqaxeaenYzbOlA3gXhZvYaV6EKjXNtm7BslpsvhLi0U+CWyb3C\n    qRkpex0MgxJgxoqViJ4TDVA+EmztOnK86+G4HGeJqTPQloYO/Td1wMT1Txh9T5Ue\n    Wctw/g+4o22EyJQRo+vxxzHNRIfe7EHAerMUtLT5u9MJeQb9N1iUR2ATNAN+QiB2\n    KEqyc9eMapK6QUZFV23Xvbdup1WCrgsWXBqyRWKV7x0sc9Wv8RMRKEFYaBeHEVXU\n    m0hGgF34Z8Rzphq2H1FjkLD+xbtGOjrA1Mb2De81Hfvrf18497X5UMPtsuzOt/XU\n    PHbbSCy+05J7VNZ/gaiGqgpHfcG5yiqCdj1LIzhFuuvm+fADPxK38wIDAQABo3sw\n    eTA3BglghkgBhvhCAQ0EKhYoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5hbCBD\n    ZXJ0aWZpY2F0ZTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n    HQ4EFgQUEhn/MqSDtuxg12klWosCGenxf1cwDQYJKoZIhvcNAQELBQADggIBAH1G\n    L3FG/keKlGqs70PxxvR1wCo4VM3K/C+5uxnzm1MHEAd96nhtwE6YSkUe+XgDiXfC\n    +NXS2C4TeTQAEo6grREapWDjhJvrhrgqTZmb4lTKzb91II3/VGYzG5UXxID262zy\n    QLoX/IBN/xDJ5ds0wF2adUbnHUssEGGljgngewH/7kjeW/L5iL+USXZnKHPSggjM\n    RAEjlucE/rDqDNoxhOS4K2PjseFm7krW4cZ0gNmxdrhc7OhmJ56dH92F4M9jn7Qy\n    EqxWB304U/aMcO3NJxTQc7AreL/pUtjtI6hxM4miHbjSh6RfNBqhzRyJvxA6gc6g\n    m3kumdw04KZFSs/6fPFFbI60i5K+vioB4CnUWpj+3Z+OnDEvhQJEACR1JC8A67Ih\n    x+GDlbHLU1BWonwZzSMJz+ABXV3dwIrOSFHI0UmDXg+cIdZ+SaL93qMjUVU4v9nu\n    gR9yJGMqNuzLjgfbD/KGCEEAITKBwPvCVd//OMlWVrXr7vvt+yo6STIlTJxABJDp\n    CSLyHUtT++CsPXsPADxgRctpIbh1eMFEivkK9Oy+W/CZYIZnARVysUpMWg7TkXqx\n    mSCXy9ZXLWqU/ssVhbLS9vFVa5pvxcyfiRpsFg0XZsx8mnZP6OaWcL8FjF+/NwNP\n    tg1+DuYTn+d54OHi/GZEnvutgrDZyrJDrrb/Czm9\n    -----END CERTIFICATE-----\n\n#### Certificate not found\n\n    GET /puppet-ca/v1/certificate/certificate_does_not_exist?environment=env\n\n    HTTP 404 Not Found\n    Content-Type: text/plain\n\n    Not Found: Could not find certificate certificate_does_not_exist\n\n#### No Certificate name given\n\n    GET /puppet-ca/v1/certificate?environment=env\n\n    HTTP/1.1 400 Bad Request\n    Content-Type: text/plain\n\n    No request key specified in /puppet-ca/v1/certificate\n\n#### Master is not a CA\n\n    GET /puppet/v1/certificate/valid_certificate?environment=env\n\n    HTTP/1.1 400 Bad Request\n    Content-Type: text/plain\n\n    this master is not a CA\n\n\nSchema\n------\n\nA `certificate` response body is not structured data according to any standard scheme such as\njson/pson/yaml, so no schema is applicable.\n"
  },
  {
    "path": "api/docs/http_certificate_request.md",
    "content": "Certificate Request\n=============\n\nThe `certificate_request` endpoint submits a Certificate Signing Request (CSR)\nto the master. The master must be configured to be a CA. The returned\nCSR is always in the `.pem` format.\n\nUnder Puppet Server's CA service, the `environment` parameter is ignored and can\nbe omitted. Under a Rack or WEBrick Puppet master, `environment` is required and\nmust be a valid environment, but it has no effect on the response.\n\nFind\n----\n\nGet a submitted CSR\n\n    GET /puppet-ca/v1/certificate_request/:nodename?environment=:environment\n    Accept: text/plain\n\nSave\n----\n\nSubmit a CSR\n\n    PUT /puppet-ca/v1/certificate_request/:nodename?environment=:environment\n    Content-Type: text/plain\n\nNote: The `:nodename` must match the Common Name on the submitted CSR.\n\nNote: Although the `Content-Type` is sent as `text/plain` the content is\nspecifically a CSR in PEM format.\n\nSearch\n----\n\n**Note:** The plural `certificate_requests` endpoint is a legacy feature. Puppet\nServer doesn't support it, and we don't plan to add support in the future.\n\nList submitted CSRs\n\n    GET /puppet-ca/v1/certificate_requests/:ignored_pattern?environment=:environment\n    Accept: text/plain\n\nThe `:ignored_pattern` parameter is not used, but must still be provided.\n\nDestroy\n----\n\nDelete a submitted CSR\n\n    DELETE /puppet-ca/v1/certificate_request/:nodename?environment=:environment\n    Accept: text/plain\n\n### Supported HTTP Methods\n\nThe default configuration only allows requests that result in a Find and a\nSave. You need to modify Puppet Server's `auth.conf` in order to allow clients to use Search and\nDestroy actions. It is not recommended that you change the default settings.\n\nGET, PUT, DELETE\n\n### Supported Response Formats\n\n`text/plain`\n\nThe returned CSR is always in the `.pem` format.\n\n### Parameters\n\nNone\n\n### Examples\n\n#### CSR found\n\n    GET /puppet-ca/v1/certificate_request/agency?environment=env\n\n    HTTP/1.1 200 OK\n    Content-Type: text/plain\n\n    -----BEGIN CERTIFICATE REQUEST-----\n    MIIBnzCCAQwCAQAwYzELMAkGA1UEBhMCVUsxDzANBgNVBAgTBkxvbmRvbjEPMA0G\n    A1UEBxMGTG9uZG9uMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx\n    DzANBgNVBAMTBmFnZW5jeTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxSCr\n    FKUKjVGFPuQ0iGM9mZKw94sOIgGohqrHH743kPvjsId3d38Qk+H+1DbVf42bQY0W\n    kAVcwNDqmBnx0lOtQ0oeGnbbwlJFjhqXr8jFEljPrc9S2/IIILDf/FeYWw9lRiOV\n    LoU6ZfCIBfq6v4D4KX3utRbOoELNyBeT6VA1ufMCAwEAAaAAMAkGBSsOAwIPBQAD\n    gYEAno7O1jkR56TNMe1Cw/eyQUIaniG22+0kmoftjlcMYZ/IKCOz+HRgnDtBPf8j\n    O5nt0PQN8YClW7Xx2U8ZTvBXn/UEKMtCBkbF+SULiayxPgfyKy/axinfutEChnHS\n    ZtUMUBLlh+gGFqOuH69979SJ2QmQC6FNomTkYI7FOHD/TG0=\n    -----END CERTIFICATE REQUEST-----\n\n#### CSR not found\n\n    GET /puppet-ca/v1/certificate_request/does_not_exist?environment=env\n\n    HTTP/1.1 404 Not Found\n    Content-Type: text/plain\n\n    Not Found: Could not find certificate_request does_not_exist\n\n#### No node name given\n\n    GET /puppet-ca/v1/certificate_request?environment=env\n\n    HTTP/1.1 400 Bad Request\n    Content-Type: text/plain\n\n    No request key specified in /puppet-ca/v1/certificate_request\n\n#### Delete a CSR that exists\n\n    DELETE /puppet-ca/v1/certificate_request/agency?environment=production\n    Accept: s\n\n    HTTP/1.1 200 OK\n    Content-Type: text/plain\n\n    1\n\n#### Delete a CSR that does not exists\n\n    DELETE /puppet-ca/v1/certificate_request/missing?environment=production\n    Accept: s\n\n    HTTP/1.1 200 OK\n    Content-Type: text/plain\n\n    false\n\n#### Retrieve all CSRs\n\n     GET /puppet-ca/v1/certificate_requests/ignored?environment=production\n     Accept: s\n\n     HTTP/1.1 200 OK\n     Content-Type: text/plain\n\n     -----BEGIN CERTIFICATE REQUEST-----\n     MIIBnzCCAQwCAQAwYzELMAkGA1UEBhMCVUsxDzANBgNVBAgTBkxvbmRvbjEPMA0G\n     A1UEBxMGTG9uZG9uMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx\n     DzANBgNVBAMTBmFnZW5jeTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxSCr\n     FKUKjVGFPuQ0iGM9mZKw94sOIgGohqrHH743kPvjsId3d38Qk+H+1DbVf42bQY0W\n     kAVcwNDqmBnx0lOtQ0oeGnbbwlJFjhqXr8jFEljPrc9S2/IIILDf/FeYWw9lRiOV\n     LoU6ZfCIBfq6v4D4KX3utRbOoELNyBeT6VA1ufMCAwEAAaAAMAkGBSsOAwIPBQAD\n     gYEAno7O1jkR56TNMe1Cw/eyQUIaniG22+0kmoftjlcMYZ/IKCOz+HRgnDtBPf8j\n     O5nt0PQN8YClW7Xx2U8ZTvBXn/UEKMtCBkbF+SULiayxPgfyKy/axinfutEChnHS\n     ZtUMUBLlh+gGFqOuH69979SJ2QmQC6FNomTkYI7FOHD/TG0=\n     -----END CERTIFICATE REQUEST-----\n\n     ---\n     -----BEGIN CERTIFICATE REQUEST-----\n     MIIBnjCCAQsCAQAwYjELMAkGA1UEBhMCVUsxDzANBgNVBAgTBkxvbmRvbjEPMA0G\n     A1UEBxMGTG9uZG9uMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx\n     DjAMBgNVBAMTBWFnZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1tucK\n     enT1CkDPgsCU/0e2cbzRsiKF8yHH7+ntF6Q3d9ZCaZWJ00mj0+YmiYrnum+KAikE\n     45Iaf9vaUV3CPsDVrUPOI8kYehiv868ZhP3nxblE6iuNBK+Fdv9GN/vKQrmL5iRE\n     bIrOM3/lxpS7SpidGdA6EIVlS3604bwLY4xHNQIDAQABoAAwCQYFKw4DAg8FAAOB\n     gQAXH0YFuidPqB6P2MyPEEGZ3rzozINBx/oXvGptXI60Zy5mgH6iAkrZfi57pEzP\n     jFoO2JRaFxTJC1FVpc4zR1K6sq4h3fIMwqppJRX+5wJNKyhU61eY2gR2O/rAJzw4\n     wcUKf9JhoE7/p1cUulIIIq7t/ibCvf0LYSFwGqTwGqN2TQ==\n     -----END CERTIFICATE REQUEST-----\n\nThe CSR PEMs are separated by \"\\n---\\n\"\n\nSchema\n------\n\nA `certificate_request` response body is not structured data according to any\nstandard scheme such as json/pson/yaml, so no schema is applicable.\n"
  },
  {
    "path": "api/docs/http_certificate_revocation_list.md",
    "content": "Certificate Revocation List\n===========================\n\nThe `certificate_revocation_list` endpoint retrieves a Certificate Revocation List (CRL)\nfrom the master. The master must be configured to be a CA. The returned\nCRL is always in the `.pem` format.\n\nUnder Puppet Server's CA service, the `environment` parameter is ignored and can\nbe omitted. Under a Rack or WEBrick Puppet master, `environment` is required and\nmust be a valid environment, but it has no effect on the response.\n\nThe `:nodename` should always be `ca`, due to Puppet Server's default\n`auth.conf`. (You can use a different `:nodename` if you change the auth rules,\nbut it will have no effect on the response.)\n\nFind\n----\n\nGet the submitted CRL\n\n    GET /puppet-ca/v1/certificate_revocation_list/:nodename?environment=:environment\n    Accept: text/plain\n\n### Supported HTTP Methods\n\nGET\n\n### Supported Response Formats\n\n`text/plain`\n\nThe returned CRL is always in the `.pem` format.\n\n### Parameters\n\nNone\n\n### Examples\n\nSince the returned CRL always looks similar to the human eye, the successful examples are each followed by an openssl\ndecoding of the CRL PEM file.\n\n#### Empty revocation list\n\n    GET /puppet-ca/v1/certificate_revocation_list/ca?environment=env\n\n    HTTP/1.1 200 OK\n    Content-Type: text/plain\n\n    -----BEGIN X509 CRL-----\n    MIICdzBhAgEBMA0GCSqGSIb3DQEBBQUAMB8xHTAbBgNVBAMMFFB1cHBldCBDQTog\n    bG9jYWxob3N0Fw0xMzA3MTYyMDQ4NDJaFw0xODA3MTUyMDQ4NDNaoA4wDDAKBgNV\n    HRQEAwIBADANBgkqhkiG9w0BAQUFAAOCAgEAqyBJOy3dtCOcrb0Fu7ZOOiDQnarg\n    IzXUV/ug1dauPEVyURLNNr+CJrr89QZnU/71lqgpWTN/J47mO/lffMSPjmINE+ng\n    XzOffm0qCG2+gNyaOBOdEmQTLdHPIXvcm7T+wEqc7XFW2tjEdpEubZgweruU/+DB\n    RX6/PhFbalQ0bKcMeFLzLAD4mmtBaQCJISmUUFWx1pyCS6pgBtQ1bNy3PJPN2PNW\n    YpDf3DNZ16vrAJ4a4SzXLXCoONw0MGxZcS6/hctJ75Vz+dTMrArKwckytWgQS/5e\n    c/1/wlMZn4xlho+EcIPMPfCB5hW1qzGU2WjUakTVxzF4goamnfFuKbHKEoXVOo9C\n    3dEQ9un4Uyd1xHxj8WvQck79In5/S2l9hdqp4eud4BaYB6tNRKxlUntSCvCNriR2\n    wrDNsMuQ5+KJReG51vM0OzzKmlScgIHaqbVeNFZI9X6TpsO2bLEZX2xyqKw4xrre\n    OIEZRoJrmX3VQ/4u9hj14Qbt72/khYo6z/Fckc5zVD+dW4fjP2ztVTSPzBqIK3+H\n    zAgewYW6cJ6Aan8GSl3IfRqj6WlOubWj8Gr1U0dOE7SkBX6w/X61uqsHrOyg/E/Z\n    0Wcz/V+W5iZxa4Spm0x4sfpNzf/bNmjTe4M2MXyn/hXx5MdHf/HZdhOs/lzwKUGL\n    kEwcy38d6hYtUjs=\n    -----END X509 CRL-----\n\n    > openssl crl -inform PEM -in empty.crl -text -noout\n    Certificate Revocation List (CRL):\n            Version 2 (0x1)\n            Signature Algorithm: sha1WithRSAEncryption\n            Issuer: /CN=Puppet CA: localhost\n            Last Update: Jul 16 20:48:42 2013 GMT\n            Next Update: Jul 15 20:48:43 2018 GMT\n            CRL extensions:\n                X509v3 CRL Number:\n                    0\n    No Revoked Certificates.\n        Signature Algorithm: sha1WithRSAEncryption\n            ab:20:49:3b:2d:dd:b4:23:9c:ad:bd:05:bb:b6:4e:3a:20:d0:\n            9d:aa:e0:23:35:d4:57:fb:a0:d5:d6:ae:3c:45:72:51:12:cd:\n            36:bf:82:26:ba:fc:f5:06:67:53:fe:f5:96:a8:29:59:33:7f:\n            27:8e:e6:3b:f9:5f:7c:c4:8f:8e:62:0d:13:e9:e0:5f:33:9f:\n            7e:6d:2a:08:6d:be:80:dc:9a:38:13:9d:12:64:13:2d:d1:cf:\n            21:7b:dc:9b:b4:fe:c0:4a:9c:ed:71:56:da:d8:c4:76:91:2e:\n            6d:98:30:7a:bb:94:ff:e0:c1:45:7e:bf:3e:11:5b:6a:54:34:\n            6c:a7:0c:78:52:f3:2c:00:f8:9a:6b:41:69:00:89:21:29:94:\n            50:55:b1:d6:9c:82:4b:aa:60:06:d4:35:6c:dc:b7:3c:93:cd:\n            d8:f3:56:62:90:df:dc:33:59:d7:ab:eb:00:9e:1a:e1:2c:d7:\n            2d:70:a8:38:dc:34:30:6c:59:71:2e:bf:85:cb:49:ef:95:73:\n            f9:d4:cc:ac:0a:ca:c1:c9:32:b5:68:10:4b:fe:5e:73:fd:7f:\n            c2:53:19:9f:8c:65:86:8f:84:70:83:cc:3d:f0:81:e6:15:b5:\n            ab:31:94:d9:68:d4:6a:44:d5:c7:31:78:82:86:a6:9d:f1:6e:\n            29:b1:ca:12:85:d5:3a:8f:42:dd:d1:10:f6:e9:f8:53:27:75:\n            c4:7c:63:f1:6b:d0:72:4e:fd:22:7e:7f:4b:69:7d:85:da:a9:\n            e1:eb:9d:e0:16:98:07:ab:4d:44:ac:65:52:7b:52:0a:f0:8d:\n            ae:24:76:c2:b0:cd:b0:cb:90:e7:e2:89:45:e1:b9:d6:f3:34:\n            3b:3c:ca:9a:54:9c:80:81:da:a9:b5:5e:34:56:48:f5:7e:93:\n            a6:c3:b6:6c:b1:19:5f:6c:72:a8:ac:38:c6:ba:de:38:81:19:\n            46:82:6b:99:7d:d5:43:fe:2e:f6:18:f5:e1:06:ed:ef:6f:e4:\n            85:8a:3a:cf:f1:5c:91:ce:73:54:3f:9d:5b:87:e3:3f:6c:ed:\n            55:34:8f:cc:1a:88:2b:7f:87:cc:08:1e:c1:85:ba:70:9e:80:\n            6a:7f:06:4a:5d:c8:7d:1a:a3:e9:69:4e:b9:b5:a3:f0:6a:f5:\n            53:47:4e:13:b4:a4:05:7e:b0:fd:7e:b5:ba:ab:07:ac:ec:a0:\n            fc:4f:d9:d1:67:33:fd:5f:96:e6:26:71:6b:84:a9:9b:4c:78:\n            b1:fa:4d:cd:ff:db:36:68:d3:7b:83:36:31:7c:a7:fe:15:f1:\n            e4:c7:47:7f:f1:d9:76:13:ac:fe:5c:f0:29:41:8b:90:4c:1c:\n            cb:7f:1d:ea:16:2d:52:3b\n\n#### One-item revocation list\n\n    GET /puppet-ca/v1/certificate_revocation_list/ca?environment=env\n\n    HTTP/1.1 200 OK\n    Content-Type: text/plain\n\n    -----BEGIN X509 CRL-----\n    MIICnDCBhQIBATANBgkqhkiG9w0BAQUFADAfMR0wGwYDVQQDDBRQdXBwZXQgQ0E6\n    IGxvY2FsaG9zdBcNMTMxMDA3MTk0ODQwWhcNMTgxMDA2MTk0ODQxWjAiMCACAQUX\n    DTEzMTAwNzE5NDg0MVowDDAKBgNVHRUEAwoBAaAOMAwwCgYDVR0UBAMCAQEwDQYJ\n    KoZIhvcNAQEFBQADggIBALrh49WNdmrJOPCRntD1nxCObmqZgl8ZwTv7TO9VkmCG\n    Ksvo8zR2aTIOH9VUKqWrE0squhtFJXl8dxL4PR1RiLbmhO7dp+NHdu8ejTQpoOTp\n    h69xbQFT3oHcIdn2cBGrLJQcZgXsiswT0KJ8nuw6eDO93yXDrguSUdou99M99wTw\n    2nn1kUQKW9b0vUI7t2ADF5U8/DES+1IrvBq2IEHmg4+ekZRCxeJMuqd1R13gymcJ\n    osSPbRgIjCli6zD3aK4Nq5OMMpVLV/VVPwyQb4GwW4Wj5iyNAp8d/EAqtZ21ZHUi\n    nvuXmRtUWHJwfi40D5T2GQXxuUjB4pnh8cFq7f89iUvqoCwFo7nRIacrrweNFMYD\n    GxVJVMfz4PkP66ckIPQ5Uuey92dg5p2w4b2cp8NstxMdgcc3KAF483ItKA8uIDuU\n    1dbzw1v2k5qUjoImueHwKolbLmPyYmvFp7hbnV+WpFbvGjyIfW3BMankDEv4ig0L\n    MCw6n2GKv1hSWM6Mrk8Ja1yYOFLsjI0RoVCZsf1iNiRT28haldXVTPyNtct9mGAv\n    6az5W/nyixIPrrHubTx28zhmuHZx6y3hQMCLmuYOT+e7F/eFsYXVEjuJjxjr33uA\n    O/ii4EkTls1gzvonOtoBoGElzQAogrZI3HXCwFYvU2whLKr9cwv5bpRkUfPCMQ4n\n    -----END X509 CRL-----\n\n    > openssl crl -inform PEM -in 1revoked.crl -text -noout\n    Certificate Revocation List (CRL):\n            Version 2 (0x1)\n            Signature Algorithm: sha1WithRSAEncryption\n            Issuer: /CN=Puppet CA: localhost\n            Last Update: Oct  7 19:48:40 2013 GMT\n            Next Update: Oct  6 19:48:41 2018 GMT\n            CRL extensions:\n                X509v3 CRL Number:\n                    1\n    Revoked Certificates:\n        Serial Number: 05\n            Revocation Date: Oct  7 19:48:41 2013 GMT\n            CRL entry extensions:\n                X509v3 CRL Reason Code:\n                    Key Compromise\n        Signature Algorithm: sha1WithRSAEncryption\n            ba:e1:e3:d5:8d:76:6a:c9:38:f0:91:9e:d0:f5:9f:10:8e:6e:\n            6a:99:82:5f:19:c1:3b:fb:4c:ef:55:92:60:86:2a:cb:e8:f3:\n            34:76:69:32:0e:1f:d5:54:2a:a5:ab:13:4b:2a:ba:1b:45:25:\n            79:7c:77:12:f8:3d:1d:51:88:b6:e6:84:ee:dd:a7:e3:47:76:\n            ef:1e:8d:34:29:a0:e4:e9:87:af:71:6d:01:53:de:81:dc:21:\n            d9:f6:70:11:ab:2c:94:1c:66:05:ec:8a:cc:13:d0:a2:7c:9e:\n            ec:3a:78:33:bd:df:25:c3:ae:0b:92:51:da:2e:f7:d3:3d:f7:\n            04:f0:da:79:f5:91:44:0a:5b:d6:f4:bd:42:3b:b7:60:03:17:\n            95:3c:fc:31:12:fb:52:2b:bc:1a:b6:20:41:e6:83:8f:9e:91:\n            94:42:c5:e2:4c:ba:a7:75:47:5d:e0:ca:67:09:a2:c4:8f:6d:\n            18:08:8c:29:62:eb:30:f7:68:ae:0d:ab:93:8c:32:95:4b:57:\n            f5:55:3f:0c:90:6f:81:b0:5b:85:a3:e6:2c:8d:02:9f:1d:fc:\n            40:2a:b5:9d:b5:64:75:22:9e:fb:97:99:1b:54:58:72:70:7e:\n            2e:34:0f:94:f6:19:05:f1:b9:48:c1:e2:99:e1:f1:c1:6a:ed:\n            ff:3d:89:4b:ea:a0:2c:05:a3:b9:d1:21:a7:2b:af:07:8d:14:\n            c6:03:1b:15:49:54:c7:f3:e0:f9:0f:eb:a7:24:20:f4:39:52:\n            e7:b2:f7:67:60:e6:9d:b0:e1:bd:9c:a7:c3:6c:b7:13:1d:81:\n            c7:37:28:01:78:f3:72:2d:28:0f:2e:20:3b:94:d5:d6:f3:c3:\n            5b:f6:93:9a:94:8e:82:26:b9:e1:f0:2a:89:5b:2e:63:f2:62:\n            6b:c5:a7:b8:5b:9d:5f:96:a4:56:ef:1a:3c:88:7d:6d:c1:31:\n            a9:e4:0c:4b:f8:8a:0d:0b:30:2c:3a:9f:61:8a:bf:58:52:58:\n            ce:8c:ae:4f:09:6b:5c:98:38:52:ec:8c:8d:11:a1:50:99:b1:\n            fd:62:36:24:53:db:c8:5a:95:d5:d5:4c:fc:8d:b5:cb:7d:98:\n            60:2f:e9:ac:f9:5b:f9:f2:8b:12:0f:ae:b1:ee:6d:3c:76:f3:\n            38:66:b8:76:71:eb:2d:e1:40:c0:8b:9a:e6:0e:4f:e7:bb:17:\n            f7:85:b1:85:d5:12:3b:89:8f:18:eb:df:7b:80:3b:f8:a2:e0:\n            49:13:96:cd:60:ce:fa:27:3a:da:01:a0:61:25:cd:00:28:82:\n            b6:48:dc:75:c2:c0:56:2f:53:6c:21:2c:aa:fd:73:0b:f9:6e:\n            94:64:51:f3:c2:31:0e:27\n\n#### No node name given\n\n    GET /puppet-ca/v1/certificate_revocation_list?environment=env\n\n    HTTP/1.1 400 Bad Request\n    Content-Type: text/plain\n\n    No request key specified in /puppet-ca/v1/certificate_revocation_list\n\nSchema\n------\n\nA `certificate_revocation_list` response body is not structured data according to any\nstandard scheme such as json/pson/yaml, so no schema is applicable.\n"
  },
  {
    "path": "api/docs/http_certificate_status.md",
    "content": "Certificate Status\n===============\n\nThe `certificate status` endpoint allows a client to read or alter the\nstatus of a certificate or pending certificate request. It is only\nuseful on the CA.\n\nUnder Puppet Server's CA service, the `environment` parameter is ignored and can\nbe omitted. Under a Rack or WEBrick Puppet master, `environment` is required and\nmust be a valid environment, but it has no effect on the response.\n\nFind\n----\n\n    GET /puppet-ca/v1/certificate_status/:certname?environment=:environment\n    Accept: application/json, text/pson\n\nRetrieve information about the specified certificate. Similar to `puppet\ncert --list :certname`.\n\nSearch\n-----\n\n    GET /puppet-ca/v1/certificate_statuses/:any_key?environment=:environment\n    Accept: application/json, text/pson\n\nRetrieve information about all known certificates. Similar to `puppet\ncert --list --all`. A key is required but is ignored.\n\nSave\n----\n\n    PUT /puppet-ca/v1/certificate_status/:certname?environment=:environment\n    Content-Type: text/pson\n\nChange the status of the specified certificate. The desired state\nis sent in the body of the PUT request as a one-item PSON hash; the two\nallowed complete hashes are `{\"desired_state\":\"signed\"}` (for signing a\ncertificate signing request; similar to `puppet cert --sign`) and\n`{\"desired_state\":\"revoked\"}` (for revoking a certificate; similar to\n`puppet cert --revoke`).\n\nNote that revoking a certificate will not clean up other info about the\nhost - see the DELETE request for more information.\n\nDelete\n-----\n\n    DELETE /puppet-ca/v1/certificate_status/:hostname?environment=:environment\n    Accept: application/json, text/pson\n\nCause the certificate authority to discard all SSL information regarding\na host (including any certificates, certificate requests, and keys).\nThis does not revoke the certificate if one is present; if you wish to\nemulate the behavior of `puppet cert --clean`, you must PUT a\n`desired_state` of `revoked` before deleting the host’s SSL information.\n\nIf the deletion was successful, it returns a string listing the deleted\nclasses like\n\n    \"Deleted for myhost: Puppet::SSL::Certificate, Puppet::SSL::Key\"\n\nOtherwise it returns\n\n    \"Nothing was deleted\"\n\n### Supported HTTP Methods\n\nThis endpoint is disabled in the default configuration. It is\nrecommended to be careful with this endpoint, as it can allow control\nover the certificates used by the puppet master.\n\nGET, PUT, DELETE\n\n\n### Supported Response Formats\n\n`application/json`, `text/pson`, `pson`\n\nThis endpoint can produce yaml as well, but the returned data is\nincomplete.\n\n### Examples\n\n#### Certificate information\n\n    GET /puppet-ca/v1/certificate_status/mycertname?environment=env\n\n    HTTP/1.1 200 OK\n    Content-Type: text/pson\n\n    {\n      \"name\":\"mycertname\",\n      \"state\":\"signed\",\n      \"fingerprint\":\"A6:44:08:A6:38:62:88:5B:32:97:20:49:8A:4A:4A:AD:65:C3:3E:A2:4C:30:72:73:02:C5:F3:D4:0E:B7:FC:2F\",\n      \"fingerprints\":{\n        \"default\":\"A6:44:08:A6:38:62:88:5B:32:97:20:49:8A:4A:4A:AD:65:C3:3E:A2:4C:30:72:73:02:C5:F3:D4:0E:B7:FC:2F\",\n        \"SHA1\":\"77:E6:5A:7E:DD:83:78:DC:F8:51:E3:8B:12:71:F4:57:F1:C2:34:AE\",\n        \"SHA256\":\"A6:44:08:A6:38:62:88:5B:32:97:20:49:8A:4A:4A:AD:65:C3:3E:A2:4C:30:72:73:02:C5:F3:D4:0E:B7:FC:2F\",\n        \"SHA512\":\"CA:A0:8C:B9:FE:9D:C2:72:18:57:08:E9:4B:11:B7:BC:4E:F7:52:C8:9C:76:03:45:B4:B6:C5:D2:DC:E8:79:43:D7:71:1F:5C:97:FA:B2:F3:ED:AE:19:BD:A9:3B:DB:9F:A5:B4:8D:57:3F:40:34:29:50:AA:AA:0A:93:D8:D7:54\"\n      },\n      \"dns_alt_names\":[\"DNS:puppet\",\"DNS:mycertname\"]\n    }\n\n\n#### Revoking a certificate\n\n    PUT /puppet-ca/v1/certificate_status/mycertname?environment=production HTTP/1.1\n    Content-Type: text/pson\n    Content-Length: 27\n\n    {\"desired_state\":\"revoked\"}\n\nThis has no meaningful return value.\n\n\n#### Deleting the certificate information\n\n    DELETE /puppet-ca/v1/certificate_status/mycertname?environment=production HTTP/1.1\n\nGets the response:\n\n    \"Deleted for mycertname: Puppet::SSL::Certificate, Puppet::SSL::Key\"\n\nSchema\n-----\n\nFind and search operations return objects which\nconform to [the host schema.](../schemas/host.json)\n"
  },
  {
    "path": "api/docs/http_environments.md",
    "content": "Environments\n============\n\nThe `environments` endpoint allows for enumeration of the environments known to the master. Each environment contains information\nabout itself like its modulepath, manifest directory, environment timeout, and the config version.\nThis endpoint is by default accessible to any client with a valid certificate, though this may be changed in Puppet Server's `auth.conf`.\n\nGet\n---\n\nGet the list of known environments.\n\n    GET /puppet/v3/environments\n\n### Supported Response Formats\n\n`application/json`\n\n### Parameters\n\nNone\n\n### Example Request & Response\n\n    GET /puppet/v3/environments\n\n    HTTP 200 OK\n    Content-Type: application/json\n\n    {\n      \"search_paths\": [\"/etc/puppetlabs/code/environments\"]\n      \"environments\": {\n        \"production\": {\n          \"settings\": {\n            \"modulepath\": [\"/etc/puppetlabs/code/environments/production/modules\", \"/etc/puppetlabs/code/environments/development/modules\"],\n            \"manifest\": [\"/etc/puppetlabs/code/environments/production/manifests\"]\n            \"environment_timeout\": 180,\n            \"config_version\": \"/version/of/config\"\n          }\n        }\n      }\n    }\n\nThe `environment_timeout` attribute could also be the string \"unlimited\".\n\nSchema\n------\n\nAn environments response body conforms to\n[the environments schema.](../schemas/environments.json)\n"
  },
  {
    "path": "api/docs/http_facts.md",
    "content": "Facts\n=====\n\nThe `facts` endpoint allows setting the facts for the specified node name.\n\nSave\n----\n\nStore facts for a node. The request body should contain JSON-formatted facts.\n\n    PUT /puppet/v3/facts/:nodename?environment=:environment\n\n### Supported HTTP Methods\n\nPUT\n\n### Supported Format(s)\n\n`application/json`\n\n### Parameters\n\nNone\n\n### Example\n\n* Note: list of facts was shortened for readability.\n* Note: JSON was formatted for readability.\n\n    PUT /puppet/v3/facts/elmo.mydomain.com?environment=env\n    Content-Type: application/json\n\n    {\n      \"name\": \"elmo.mydomain.com\",\n      \"values\": {\n        \"architecture\": \"x86_64\",\n        \"kernel\": \"Darwin\",\n        \"domain\": \"local\",\n        \"macaddress\": \"70:11:24:8c:33:a9\",\n        \"osfamily\": \"Darwin\",\n        \"operatingsystem\": \"Darwin\",\n        \"facterversion\": \"1.7.2\",\n        \"fqdn\": \"elmo.mydomain.com\",\n      },\n      \"timestamp\": \"2013-09-09 15:49:27 -0700\",\n      \"expiration\": \"2013-09-09 16:19:27 -0700\"\n    }\n\n    HTTP/1.1 200 OK\n    Content-Type: application/json\n\nSchema\n------\n\nThe representation of facts contained in a PUT body, should adhere to\n[the facts schema.](../schemas/facts.json)\n"
  },
  {
    "path": "api/docs/http_file_bucket_file.md",
    "content": "File Bucket File\n=============\n\nThe `file_bucket_file` endpoint manages the contents of files in the\nfile bucket. All access to files is managed with the md5 checksum of the\nfile contents, represented as `:md5`. Where used, `:filename` means the\nfull absolute path of the file on the client system. This is usually\noptional and used as an error check to make sure correct file is\nretrieved. The environment is required in all requests but ignored, as\nthe file bucket does not distinguish between environments.\n\nFind\n----\n\nRetrieve the contents of a file.\n\n    GET /puppet/v3/file_bucket_file/:md5?environment=:environment\n    GET /puppet/v3/file_bucket_file/:md5/:original_path?environment=:environment\n\nThis will return the contents of the file if it's present. If\n`:original_path` is provided then the contents will only be sent if the\nfile was uploaded with the same path at some point.\n\nHead\n----\n\nCheck if a file is present in the filebucket\n\n    HEAD /puppet/v3/file_bucket_file/:md5?environment=:environment\n    HEAD /puppet/v3/file_bucket_file/:md5/:original_path?environment=:environment\n\nThis behaves identically to find, only returning headers.\n\nSave\n----\n\nSave a file to the filebucket\n\n    PUT /puppet/v3/file_bucket_file/:md5?environment=:environment\n    PUT /puppet/v3/file_bucket_file/:md5/:original_path?environment=:environment\n\nThe body should contain the file contents. This saves the file using the\nmd5 sum of the file contents. If `:original_path` is provided, it adds\nthe path to a list for the given file. If the md5 sum in the request is\nincorrect, the file will be instead saved under the correct checksum.\n\n### Supported HTTP Methods\n\nGET, HEAD, PUT\n\n### Supported Response Formats\n\n`application/octet-stream`\n\n### Parameters\n\nNone\n\n### Examples\n\n#### Saving a file\n\n    > PUT /puppet/v3/file_bucket_file/md5/eb61eead90e3b899c6bcbe27ac581660//home/user/myfile.txt?environment=production HTTP/1.1\n\n    > Content-Type: application/octet-stream\n    > Content-Length: 24\n\n    > This is the file content\n\n\n    < HTTP/1.1 200 OK\n\n#### Retrieving a file\n\n    > GET /puppet/v3/file_bucket_file/md5/4949e56d376cc80ce5387e8e89a75396//home/user/myfile.txt?environment=production HTTP/1.1\n    > Accept: application/octet-stream\n\n\n    < HTTP/1.1 200 OK\n    < Content-Length: 24\n\n    < This is the file content\n\n#### Wrong file name\n\n    > GET /puppet/v3/file_bucket_file/md5/4949e56d376cc80ce5387e8e89a75396//home/user/wrong_name?environment=production HTTP/1.1\n    > Accept: application/octet-stream\n\n\n    < HTTP/1.1 404 Not Found\n    <\n    < Not Found: Could not find file_bucket_file md5/4949e56d376cc80ce5387e8e89a75396/home/user/wrong_name\n\nSchema\n------\n\nA `file_bucket_file` response body is not structured data according to any standard scheme such as\njson/yaml, so no schema is applicable.\n"
  },
  {
    "path": "api/docs/http_file_content.md",
    "content": "File Content\n=============\n\nThe `file_content` endpoint returns the contents of the specified file.\n\nFind\n----\n\nGet a file.\n\n    GET /puppet/v3/file_content/:mount_point/:name\n\nThe endpoint path includes a `:mount_point` which can be one of the following types:\n\n* Custom file serving mounts as specified in fileserver.conf --- see [the docs on configuring mount points](https://puppet.com/docs/puppet/latest/file_serving.html).\n* `modules/<MODULE>` --- a semi-magical mount point which allows access to the `files` subdirectory of `<MODULE>` --- see [the docs on file serving](https://puppet.com/docs/puppet/latest/file_serving.html).\n* `plugins` --- a highly magical mount point which merges the `lib`  directory of every module together. Used for syncing plugins; not intended for general consumption. Per-module sub-paths can not be specified.\n* `pluginfacts` --- a highly magical mount point which merges the `facts.d` directory of every module together. Used for syncing external facts; not intended for general consumption. Per-module sub-paths can not be specified.\n* `tasks/<MODULE>` --- a semi-magical mount point which allows access to files in the `tasks` subdirectory of `<MODULE>` --- see the [the docs on file serving](https://puppet.com/docs/puppet/latest/file_serving.html).\n\n`:name` is the path to the file within the `:mount_point` that is requested.\n\n### Supported HTTP Methods\n\nGET\n\n### Supported Response Formats\n\n`application/octet-stream`\n\n### Parameters\n\nNone\n\n### Notes\n\n### Responses\n\n#### File found\n\n    GET /puppet/v3/file_content/modules/example/my_file?environment=env\n    Accept: application/octet-stream\n\n    HTTP/1.1 200 OK\n    Content-Type: application/octet-stream\n    Content-Length: 16\n\n    this is my file\n\n\n#### File not found\n\n    GET /puppet/v3/file_content/modules/example/not_found?environment=env\n    Accept: application/octet-stream\n\n    HTTP/1.1 404 Not Found\n    Content-Type: text/plain\n\n    Not Found: Could not find file_content modules/example/not_found\n\n#### No file name given\n\n    GET /puppet/v3/file_content?environment=env\n\n    HTTP/1.1 400 Bad Request\n    Content-Type: text/plain\n\n    No request key specified in /puppet/v3/file_content/\n\nSchema\n------\n\nA `file_content` response body is not structured data according to any standard scheme such as\njson/pson/yaml, so no schema is applicable.\n"
  },
  {
    "path": "api/docs/http_file_metadata.md",
    "content": "File Metadata\n=============\n\nThe `file_metadata` endpoint returns select metadata for a single file or many files. There are find and search variants\nof the endpoint; the search variant has a trailing 's' so is actually `file_metadatas`.\n\nAlthough the term 'file' is used generically in the endpoint name and documentation, each returned item can be one of\nthe following three types:\n\n* File\n* Directory\n* Symbolic link\n\nThe endpoint path includes a `:mount` which can be one of the following types:\n\n* Custom file serving mounts as specified in fileserver.conf --- see [the docs on configuring mount points](https://puppet.com/docs/puppet/latest/file_serving.html).\n* `modules/<MODULE>` --- a semi-magical mount point which allows access to the `files` subdirectory of `<MODULE>` --- see [the docs on file serving](https://puppet.com/docs/puppet/latest/file_serving.html).\n* `plugins` --- a highly magical mount point which merges the `lib`  directory of every module together. Used for syncing plugins; not intended for general consumption. Per-module sub-paths can not be specified.\n* `pluginfacts` --- a highly magical mount point which merges the `facts.d` directory of every module together. Used for syncing external facts; not intended for general consumption. Per-module sub-paths can not be specified.\n* `tasks/<MODULE>` --- a semi-magical mount point which allows access to files in the `tasks` subdirectory of `<MODULE>` --- see the [the docs on file serving](https://puppet.com/docs/puppet/latest/file_serving.html).\n\nNote: JSON responses in the examples below are pretty-printed for readability.\n\nFind\n----\n\nGet file metadata for a single file\n\n    GET /puppet/v3/file_metadata/:mount/path/to/file?environment=:environment\n\n### Supported HTTP Methods\n\nGET\n\n### Supported Response Formats\n\n`application/json`\n\n### Parameters\n\nOptional parameters to GET:\n\n* `links` -- either `manage` (default) or `follow`. See examples in Search below.\n* `checksum_type` -- the checksum type to calculate the checksum value for the result metadata; one of `md5` (default), `md5lite`, `sha256`, `sha256lite`, `mtime`, `ctime`, and `none`.\n* `source_permissions` -- whether (and how) Puppet should copy owner, group, and mode permissions; one of\n  * `ignore` (the default) will never apply the owner, group, or mode from the source when managing a file. When creating new files without explicit permissions, the permissions they receive will depend on platform-specific behavior. On POSIX, Puppet will use the umask of the user it is running as. On Windows, Puppet will use the default DACL associated with the user it is running as.\n  * `use` will cause Puppet to apply the owner, group, and mode from the source to any files it is managing.\n  * `use_when_creating` will only apply the owner, group, and mode from the source when creating a file; existing files will not have their permissions overwritten.\n\n### Example Response\n\n#### File metadata found for a file\n\n    GET /puppet/v3/file_metadata/modules/example/just_a_file.txt?environment=env\n\n    HTTP/1.1 200 OK\n    Content-Type: application/json\n\n    {\n        \"checksum\": {\n            \"type\": \"md5\",\n            \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n        },\n        \"destination\": null,\n        \"group\": 20,\n        \"links\": \"manage\",\n        \"mode\": 420,\n        \"owner\": 501,\n        \"path\": \"/etc/puppetlabs/code/modules/example/files/just_a_file.txt\",\n        \"relative_path\": null,\n        \"type\": \"file\"\n    }\n\n#### File metadata found for a directory\n\n    GET /puppet/v3/file_metadata/modules/example/subdirectory?environment=env\n\n    HTTP/1.1 200 OK\n    Content-Type: application/json\n\n    {\n        \"checksum\": {\n            \"type\": \"ctime\",\n            \"value\": \"{ctime}2013-10-01 13:16:10 -0700\"\n        },\n        \"destination\": null,\n        \"group\": 20,\n        \"links\": \"manage\",\n        \"mode\": 493,\n        \"owner\": 501,\n        \"path\": \"/etc/puppetlabs/code/modules/example/files/subdirectory\",\n        \"relative_path\": null,\n        \"type\": \"directory\"\n    }\n\n#### File metadata found for a link ignoring source permissions\n\n    GET /puppet/v3/file_metadata/modules/example/link_to_file.txt?environment=env&source_permissions=ignore\n\n    HTTP/1.1 200 OK\n    Content-Type: application/json\n\n    {\n        \"checksum\": {\n            \"type\": \"md5\",\n            \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n        },\n        \"destination\": \"/etc/puppetlabs/code/modules/example/files/just_a_file.txt\",\n        \"group\": 20,\n        \"links\": \"manage\",\n        \"mode\": 420,\n        \"owner\": 501,\n        \"path\": \"/etc/puppetlabs/code/modules/example/files/link_to_file.txt\",\n        \"relative_path\": null,\n        \"type\": \"link\"\n    }\n\n#### File not found\n\n    GET /puppet/v3/file_metadata/modules/example/does_not_exist?environment=env\n\n    HTTP/1.1 404 Not Found\n\n    Not Found: Could not find file_metadata modules/example/does_not_exist\n\nSearch\n------\n\nGet a list of metadata for multiple files\n\n    GET /puppet/v3/file_metadatas/foo.txt?environment=env\n\n### Supported HTTP Methods\n\nGET\n\n### Supported Response Formats\n\n`application/json`\n\n### Parameters\n\n* `recurse` -- should always be set to `yes`; unfortunately the default is `no`, which causes a search to behave like a find operation.\n* `ignore` -- file or directory regex to ignore; can be repeated.\n* `links` -- either `manage` (default) or `follow`. See examples below.\n* `checksum_type` -- the checksum type to calculate the checksum value for the result metadata; one of `md5` (default), `md5lite`, `sha256`, `sha256lite`, `mtime`, `ctime`, and `none`.\n* `source_permissions` -- whether (and how) Puppet should copy owner, group, and mode permissions; one of\n  * `ignore` (the default) will never apply the owner, group, or mode from the source when managing a file. When creating new files without explicit permissions, the permissions they receive will depend on platform-specific behavior. On POSIX, Puppet will use the umask of the user it is running as. On Windows, Puppet will use the default DACL associated with the user it is running as.\n  * `use` will cause Puppet to apply the owner, group, and mode from the source to any files it is managing.\n  * `use_when_creating` will only apply the owner, group, and mode from the source when creating a file; existing files will not have their permissions overwritten.\n\n### Example Response\n\n#### Basic search\n\n    GET /puppet/v3/file_metadatas/modules/example?environment=env&recurse=yes\n\n    HTTP 200 OK\n    Content-Type: application/json\n\n    [\n        {\n            \"checksum\": {\n                \"type\": \"ctime\",\n                \"value\": \"{ctime}2013-10-01 13:15:59 -0700\"\n            },\n            \"destination\": null,\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 493,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \".\",\n            \"type\": \"directory\"\n        },\n        {\n            \"checksum\": {\n                \"type\": \"md5\",\n                \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n            },\n            \"destination\": null,\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 420,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \"just_a_file.txt\",\n            \"type\": \"file\"\n        },\n        {\n            \"checksum\": {\n                \"type\": \"md5\",\n                \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n            },\n            \"destination\": \"/etc/puppetlabs/code/modules/example/files/just_a_file.txt\",\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 493,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \"link_to_file.txt\",\n            \"type\": \"link\"\n        },\n        {\n            \"checksum\": {\n                \"type\": \"ctime\",\n                \"value\": \"{ctime}2013-10-01 13:15:59 -0700\"\n            },\n            \"destination\": null,\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 493,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \"subdirectory\",\n            \"type\": \"directory\"\n        },\n        {\n            \"checksum\": {\n                \"type\": \"md5\",\n                \"value\": \"{md5}d41d8cd98f00b204e9800998ecf8427e\"\n            },\n            \"destination\": null,\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 420,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \"subdirectory/another_file.txt\",\n            \"type\": \"file\"\n        }\n    ]\n\n#### Search ignoring 'sub*' and links = manage\n\n    GET /puppet/v3/file_metadatas/modules/example?environment=env&recurse=true&ignore=sub*&links=manage\n\n    HTTP 200 OK\n    Content-Type: application/json\n\n    [\n        {\n            \"checksum\": {\n                \"type\": \"ctime\",\n                \"value\": \"{ctime}2013-10-01 13:15:59 -0700\"\n            },\n            \"destination\": null,\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 493,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \".\",\n            \"type\": \"directory\"\n        },\n        {\n            \"checksum\": {\n                \"type\": \"md5\",\n                \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n            },\n            \"destination\": null,\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 420,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \"just_a_file.txt\",\n            \"type\": \"file\"\n        },\n        {\n            \"checksum\": {\n                \"type\": \"md5\",\n                \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n            },\n            \"destination\": \"/etc/puppetlabs/code/modules/example/files/just_a_file.txt\",\n            \"group\": 20,\n            \"links\": \"manage\",\n            \"mode\": 493,\n            \"owner\": 501,\n            \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n            \"relative_path\": \"link_to_file.txt\",\n            \"type\": \"link\"\n        }\n    ]\n\n#### Search ignoring \"sub*\" and links = follow\n\nThis example is identical to the above example, except for the links parameter. The resulting JSON, then,\nis identical to the above example, except for:\n\n* the \"links\" field is set to \"follow\" rather than \"manage\" in all metadata objects\n* in the \"link_to_file.txt\" metadata:\n    * for \"manage\" the \"destination\" field is the link destination; for \"follow\", it's null\n    * for \"manage\" the \"type\" field is \"link\"; for \"follow\" it's \"file\"\n    * for \"manage\" the \"mode\", \"owner\" and \"group\" fields are the link's values; for \"follow\" the destination's values\n\n~~~\nGET /puppet/v3/file_metadatas/modules/example?environment=env&recurse=true&ignore=sub*&links=follow\n\nHTTP 200 OK\nContent-Type: application/json\n\n[\n    {\n        \"checksum\": {\n            \"type\": \"ctime\",\n            \"value\": \"{ctime}2013-10-01 13:15:59 -0700\"\n        },\n        \"destination\": null,\n        \"group\": 20,\n        \"links\": \"follow\",\n        \"mode\": 493,\n        \"owner\": 501,\n        \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n        \"relative_path\": \".\",\n        \"type\": \"directory\"\n    },\n    {\n        \"checksum\": {\n            \"type\": \"md5\",\n            \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n        },\n        \"destination\": null,\n        \"group\": 20,\n        \"links\": \"follow\",\n        \"mode\": 420,\n        \"owner\": 501,\n        \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n        \"relative_path\": \"just_a_file.txt\",\n        \"type\": \"file\"\n    },\n    {\n        \"checksum\": {\n            \"type\": \"md5\",\n            \"value\": \"{md5}d0a10f45491acc8743bc5a82b228f89e\"\n        },\n        \"destination\": null,\n        \"group\": 20,\n        \"links\": \"follow\",\n        \"mode\": 420,\n        \"owner\": 501,\n        \"path\": \"/etc/puppetlabs/code/modules/example/files\",\n        \"relative_path\": \"link_to_file.txt\",\n        \"type\": \"file\"\n    }\n]\n~~~\n\nSchema\n------\n\nThe file metadata response body conforms to\n[the `file_metadata` schema.](../schemas/file_metadata.json)\n\nSample Module\n-------------\n\nThe examples above use this (faux) module:\n\n    /etc/puppetlabs/code/modules/example/\n      files/\n        just_a_file.txt\n        link_to_file.txt -> /etc/puppetlabs/code/modules/example/files/just_a_file.txt\n        subdirectory/\n          another_file.txt\n"
  },
  {
    "path": "api/docs/http_node.md",
    "content": "Node\n====\n\nThe `node` endpoint is used by the puppet agent to get basic information\nabout a node. The returned information includes the node name and\nenvironment, and optionally any classes set by an External Node\nClassifier and a hash of parameters which may include the node's facts.\nThe returned node may have a different environment from the one given in\nthe request if Puppet is configured with an ENC.\n\nFind\n----\n\nRetrieve data for a node\n\n    GET /puppet/v3/node/:certname?environment=:environment&transaction_uuid=:transaction_uuid&configured_environment=:environment\n\n\n### Supported HTTP Methods\n\nGET\n\n### Supported Response Formats\n\n`application/json`\n\n### Parameters\n\nOne parameter should be provided to the GET:\n\n- `transaction_uuid`: a transaction uuid identifying the entire transaction (shows up in the report as well)\n\nAn optional parameter can be provided to the GET to notify a node classifier that the client requested a specific\nenvironment, which might differ from what the client believes is its current environment:\n\n- `configured_environment`: the environment configured on the client\n\n### Examples\n\n    > GET /puppet/v3/node/mycertname?environment=production&transaction_uuid=aff261a2-1a34-4647-8c20-ff662ec11c4c&configured_environment=production HTTP/1.1\n    > Accept: application/json\n\n    < HTTP/1.1 200 OK\n    < Content-Type: application/json\n    < Content-Length: 4630\n\n    {\n      \"name\":\"thinky.corp.puppetlabs.net\",\n      \"parameters\":{\n        \"architecture\":\"amd64\",\n        \"kernel\":\"Linux\",\n        \"blockdevices\":\"sda,sr0\",\n        \"clientversion\":\"3.3.1\",\n        \"clientnoop\":\"false\",\n        \"environment\":\"production\",\n        ...\n      },\n      \"environment\":\"production\"\n    }\n\nSchema\n------\n\nA node response body conforms to\n[the node schema.](../schemas/node.json)\n"
  },
  {
    "path": "api/docs/http_report.md",
    "content": "Report\n======\nThis document describes the Puppet master's report endpoint and the schema for\nReport Format 12 in technical terms. Also see the\n[documentation](https://puppet.com/docs/puppet/latest/format_report.html).\n\nThe `report` endpoint allows clients to send reports to the master via `http`\nor `https`. Once received by the master they are processed by the *report\nprocessors* configured to be triggered when a report is received. As an\nexample, storing reports in PuppetDB is handled by one such report processor.\n\nSave\n----\nThe http(s) endpoint for sending reports to the master is:\n\n    PUT /puppet/v3/report/:nodename?environment=:environment\n\n### Supported HTTP Methods\n\nPUT\n\n### Supported Format(s)\n\n`application/json`\n\n### Parameters\n\nNone\n\n### Content\n\nThe content of a report is typically generated by the Puppet Runtime and consists of a JSON serialization of `Puppet::Transaction::Report` object which in turn contains a structure of objects with of the following runtime types:\n\n* `Puppet::Util::Log`\n* `Puppet::Util::Metric`\n* `Puppet::Resource::Status`\n* `Puppet::Transaction::Event`\n\nThis JSON serialization is compliant with the endpoint's report JSON schema.\n\nExample\n-------\nHere is an example of a PUT request. (Note that the content-length is not correct as the\nexample is formatted for readability)\n\n    PUT /puppet/v3/report/kermit.com?environment=production HTTP/1.0\n    Content-Type: application/json\n    Content-Length: 1428\n\n    {\"host\"=>\"kermit.com\",\n     \"time\"=>\"2013-09-12T03:50:59.009301000+02:00\",\n     \"configuration_version\"=>1357986,\n     \"transaction_uuid\"=>\"df34516e-4050-402d-a166-05b03b940749\",\n     \"code_id\"=>null,\n     \"job_id\"=>null,\n     \"catalog_uuid\"=>\"827a74c8-cf98-44da-9ff7-18c5e4bee41e\",\n     \"catalog_format\"=>1,\n     \"report_format\"=>9,\n     \"puppet_version\"=>\"5.0.0\",\n     \"status\"=>\"unchanged\",\n     \"transaction_completed\"=>true,\n     \"noop\"=>false,\n     \"noop_pending\"=>false,\n     \"environment\"=>\"test_environment\",\n     \"logs\"=>\n      [{\"level\"=>\"warning\",\n        \"message\"=>\"log message\",\n        \"source\"=>\"Puppet\",\n        \"tags\"=>[\"warning\"],\n        \"time\"=>\"2013-09-12T03:50:59.009328000+02:00\",\n        \"file\"=>nil,\n        \"line\"=>nil}],\n     \"metrics\"=>\n      {\"resources\"=>\n        {\"name\"=>\"resources\",\n         \"label\"=>\"Resources\",\n         \"values\"=>\n          [[\"total\", \"Total\", 1],\n           [\"skipped\", \"Skipped\", 0],\n           [\"failed\", \"Failed\", 0],\n           [\"failed_to_restart\", \"Failed to restart\", 0],\n           [\"restarted\", \"Restarted\", 0],\n           [\"changed\", \"Changed\", 1],\n           [\"out_of_sync\", \"Out of sync\", 0],\n           [\"scheduled\", \"Scheduled\", 0]]},\n       \"time\"=>\n        {\"name\"=>\"time\",\n         \"label\"=>\"Time\",\n         \"values\"=>[[\"timing\", \"Timing\", 4], [\"total\", \"Total\", 4]]},\n       \"changes\"=>\n        {\"name\"=>\"changes\", \"label\"=>\"Changes\", \"values\"=>[[\"total\", \"Total\", 0]]},\n       \"events\"=>\n        {\"name\"=>\"events\",\n         \"label\"=>\"Events\",\n         \"values\"=>\n          [[\"total\", \"Total\", 0],\n           [\"failure\", \"Failure\", 0],\n           [\"success\", \"Success\", 0]]}},\n     \"resource_statuses\"=>\n      {\"Notify[a resource]\"=>\n        {\"title\"=>\"a resource\",\n         \"file\"=>nil,\n         \"line\"=>nil,\n         \"resource\"=>\"Notify[a resource]\",\n         \"resource_type\"=>\"Notify\",\n         \"provider_used\"=>nil,\n         \"containment_path\"=>[\"Notify[a resource]\"],\n         \"evaluation_time\"=>nil,\n         \"tags\"=>[\"notify\"],\n         \"time\"=>\"2013-09-12T03:50:59.009238000+02:00\",\n         \"failed\"=>false,\n         \"changed\"=>true,\n         \"out_of_sync\"=>false,\n         \"skipped\"=>false,\n         \"change_count\"=>0,\n         \"out_of_sync_count\"=>0,\n         \"events\"=>[]}},\n      \"cached_catalog_status\"=> \"not_used\"}\n\nSchema\n------\n\nThe sent report objects must conform to\n[the report schema.](../schemas/report.json)\n"
  },
  {
    "path": "api/docs/pson.md",
    "content": "PSON was moved to https://github.com/puppetlabs/puppet-pson\n"
  },
  {
    "path": "api/schemas/catalog.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"Catalog\",\n    \"description\": \"A puppet resource catalog\",\n    \"type\": \"object\",\n    \"properties\": {\n        \"tags\": {\n            \"description\": \"Tags: regex is from https://puppet.com/docs/puppet/latest/lang_reserved.html\",\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"string\",\n                \"pattern\": \"\\\\A[[:alnum:]_][[:alnum:]_:.-]*\\\\Z\"\n            }\n        },\n        \"name\": {\n            \"type\": \"string\"\n        },\n        \"version\": {\n            \"type\": [\"string\", \"integer\"]\n        },\n        \"code_id\": {\n            \"type\": [\"string\", \"null\"]\n        },\n        \"catalog_uuid\": {\n            \"type\": \"string\"\n        },\n        \"catalog_format\": {\n            \"type\": \"integer\"\n        },\n        \"environment\": {\n            \"type\": \"string\"\n        },\n        \"resources\": {\n            \"description\": \"The array of resources in the catalog\",\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"object\",\n                \"properties\": {\n                   \"type\": {\n                       \"type\": \"string\"\n                   },\n                    \"title\": {\n                        \"type\": \"string\"\n                    },\n                    \"line\": {\n                        \"type\": \"integer\"\n                    },\n                    \"kind\": {\n                        \"type\": \"string\"\n                    },\n                    \"file\": {\n                        \"type\": \"string\"\n                    },\n                    \"exported\": {\n                        \"type\": \"boolean\"\n                    },\n                    \"sensitive_parameters\": {\n                        \"description\": \"An optional list of resource parameters to be treated as sensitive.\",\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\"\n                        }\n                    },\n                    \"tags\": {\n                        \"description\": \"Tags: regex is from https://puppet.com/docs/puppet/latest/lang_reserved.html\",\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\",\n                            \"pattern\": \"\\\\A[[:alnum:]_][[:alnum:]_:.-]*\\\\Z\"\n                        }\n                    },\n                    \"parameters\": {\n                        \"description\": \"Parameters: regex is from https://puppet.com/docs/puppet/latest/lang_reserved.html\",\n                        \"type\": \"object\",\n                        \"patternProperties\": {\n                            \"^[a-z][a-z0-9_]*$\": {}\n                        },\n                        \"additionalProperties\": false\n                    }\n                },\n                \"required\": [\"type\", \"title\", \"tags\", \"exported\"],\n                \"additionalProperties\": false\n            }\n        },\n        \"edges\": {\n            \"description\": \"An array of the containment relationships in the catalog.\",\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"source\": {\n                        \"description\": \"Containing object\",\n                        \"type\": \"string\"\n                    },\n                    \"target\": {\n                        \"description\": \"Contained object\",\n                        \"type\": \"string\"\n                    }\n                },\n                \"required\": [\"source\", \"target\"],\n                \"additionalProperties\": false\n            }\n\n        },\n        \"classes\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"string\"\n            }\n        },\n        \"metadata\": {\n            \"description\": \"A mapping of non-recursive File resources to their required metadata\",\n            \"type\": \"object\",\n            \"patternProperties\": {\n                \"^.+$\": {\n                    \"$ref\": \"#/definitions/file_metadata\"\n                }\n            }\n        },\n        \"recursive_metadata\": {\n            \"description\": \"A mapping of recursive File resources to a hash of sources and arrays of associated metadata for that resource\",\n            \"type\": \"object\",\n            \"patternProperties\": {\n                \"^.+$\": {\n                    \"type\": \"object\",\n                    \"patternProperties\": {\n                        \"^.+$\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/file_metadata\"\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"required\": [\"tags\", \"name\", \"version\", \"code_id\", \"catalog_uuid\", \"catalog_format\", \"environment\", \"resources\", \"edges\", \"classes\"],\n    \"additionalProperties\": false,\n\n    \"definitions\" : {\n        \"file_metadata\": {\n            \"description\": \"Metadata about a file, directory, or symbolic link; as described in file_metadata.json\",\n            \"type\":        \"object\",\n            \"properties\": {\n                \"path\": {\n                    \"type\": \"string\"\n                },\n                \"relative_path\": {\n                    \"oneOf\": [{\"type\": \"string\"}, {\"type\": \"null\"}]\n                },\n                \"links\": {\n                    \"enum\": [\"manage\", \"follow\"]\n                },\n                \"owner\": {\n                    \"type\": \"integer\"\n                },\n                \"group\": {\n                    \"type\": \"integer\"\n                },\n                \"mode\": {\n                    \"type\": \"integer\"\n                },\n                \"type\": {\n                    \"enum\": [\"file\", \"directory\", \"link\"]\n                },\n                \"destination\": {\n                    \"oneOf\": [{\"type\": \"string\"}, {\"type\": \"null\"}]\n                },\n                \"checksum\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"type\": {\n                            \"enum\": [\"md5\", \"sha256\", \"ctime\"]\n                        },\n                        \"value\": {\n                            \"type\": \"string\"\n                        }\n                    },\n                    \"required\": [\"type\", \"value\"],\n                    \"additionalProperties\": false\n                },\n                \"source\": {\n                    \"type\": \"string\"\n                },\n                \"content_uri\": {\n                    \"type\": \"string\"\n                }\n            },\n            \"required\": [\"path\", \"relative_path\", \"links\", \"owner\", \"group\", \"mode\", \"type\", \"destination\", \"checksum\"],\n            \"additionalProperties\": false\n        }\n    }\n}\n"
  },
  {
    "path": "api/schemas/environments.json",
    "content": "{\n  \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n  \"title\":       \"Environment Enumeration\",\n  \"description\": \"An enumeration of environments and their settings\",\n  \"type\":        \"object\",\n  \"properties\": {\n    \"search_paths\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"minItems\": 1,\n      \"description\": \"An array of the paths where the master looked for environments.\"\n    },\n    \"environments\": {\n      \"type\": \"object\",\n      \"patternProperties\": {\n        \"^[a-z0-9_]+$\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"settings\" : {\n              \"type\": \"object\",\n              \"properties\": {\n                \"manifest\": { \"type\": \"string\" },\n                \"modulepath\": {\n                  \"type\": \"array\",\n                  \"items\": { \"type\": \"string\" }\n                },\n                \"config_version\": { \"type\": \"string\" },\n                \"environment_timeout\": { \"type\": [\"integer\", \"string\"] }\n              },\n              \"required\": [\"modulepath\", \"manifest\", \"environment_timeout\", \"config_version\"],\n              \"oneOf\": [\n                {\n                  \"title\": \"numeric timeout\",\n                  \"properties\": {\n                    \"environment_timeout\": {\n                      \"type\": \"integer\",\n                      \"minimum\": 0\n                    }\n                  }\n                },\n                {\n                  \"title\": \"unlimited timeout\",\n                  \"properties\": {\n                    \"environment_timeout\": {\n                      \"type\": \"string\",\n                      \"enum\": [\"unlimited\"]\n                    }\n                  }\n                }\n              ]\n            }\n          },\n          \"required\": [\"settings\"]\n        }\n      }\n    }\n  },\n  \"required\": [\"search_paths\", \"environments\"]\n}\n"
  },
  {
    "path": "api/schemas/error.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"HTTP Error Response Object\",\n    \"description\": \"A description of the error encountered when attempting to service an HTTP request.\",\n    \"type\":        \"object\",\n    \"properties\": {\n        \"message\": {\n            \"description\": \"A human-readable message explaining the error\",\n            \"type\": \"string\"\n        },\n        \"issue_kind\": {\n            \"description\": \"A unique label to identify the error class\",\n            \"type\": \"string\"\n        }\n    },\n    \"required\": [\"message\", \"issue_kind\"],\n    \"additionalProperties\": false\n}\n"
  },
  {
    "path": "api/schemas/facts.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"Catalog facts\",\n    \"description\": \"Facts parameter in a POST to retrieve a catalog\",\n    \"type\":        \"object\",\n    \"properties\": {\n        \"name\": {\n            \"type\": \"string\"\n        },\n        \"values\": {\n            \"description\": \"The facts for the specified node\",\n            \"type\": \"object\",\n            \"patternProperties\": {\n                \"^[a-z][a-z0-9_]*$\": {}\n            },\n            \"additionalProperties\": false\n        },\n        \"timestamp\": {\n            \"description\": \"When the facts were gathered; NB doesn't adhere to json 'date-time' format.\",\n            \"type\": \"string\"\n        },\n        \"expiration\": {\n            \"description\": \"When the facts will expire; NB doesn't adhere to json 'date-time' format\",\n            \"type\": \"string\"\n        }\n    },\n    \"required\": [\"name\", \"values\", \"timestamp\", \"expiration\"],\n    \"additionalProperties\": false\n}\n"
  },
  {
    "path": "api/schemas/file_metadata.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"File Metadata\",\n    \"description\": \"Metadata about a file, directory, or symbolic link\",\n    \"type\":        \"object\",\n    \"properties\": {\n        \"path\": {\n            \"type\": \"string\"\n        },\n        \"relative_path\": {\n            \"oneOf\": [{\"type\": \"string\"}, {\"type\": \"null\"}]\n        },\n        \"links\": {\n            \"enum\": [\"manage\", \"follow\"]\n        },\n        \"owner\": {\n            \"oneOf\": [{\"type\": \"string\"}, {\"type\": \"integer\"}]\n        },\n        \"group\": {\n            \"oneOf\": [{\"type\": \"string\"}, {\"type\": \"integer\"}]\n        },\n        \"mode\": {\n            \"type\": \"integer\"\n        },\n        \"type\": {\n            \"enum\": [\"file\", \"directory\", \"link\"]\n        },\n        \"destination\": {\n            \"oneOf\": [{\"type\": \"string\"}, {\"type\": \"null\"}]\n        },\n        \"checksum\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"type\": {\n                    \"enum\": [\"md5\", \"sha256\", \"ctime\"]\n                },\n                \"value\": {\n                    \"type\": \"string\"\n                }\n            },\n            \"required\": [\"type\", \"value\"],\n            \"additionalProperties\": false\n        },\n        \"source\": {\n            \"type\": \"string\"\n        },\n        \"content_uri\": {\n            \"type\": \"string\"\n        }\n    },\n    \"required\": [\"path\", \"relative_path\", \"links\", \"owner\", \"group\", \"mode\", \"type\", \"destination\", \"checksum\"],\n    \"additionalProperties\": false\n}\n"
  },
  {
    "path": "api/schemas/host.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"Host\",\n    \"description\": \"SSL Host information\",\n    \"type\":        \"object\",\n    \"properties\": {\n        \"name\": {\n            \"type\": \"string\"\n        },\n        \"state\": {\n            \"type\": \"string\",\n            \"enum\": [\n                \"requested\",\n                \"signed\",\n                \"revoked\"\n            ]\n        },\n        \"desired_state\": {\n            \"type\": \"string\",\n            \"enum\": [\n                \"signed\",\n                \"revoked\"\n            ]\n        },\n        \"fingerprint\": {\n            \"type\": \"string\"\n        },\n        \"fingerprints\": {\n            \"type\": \"object\",\n            \"patternProperties\": {\n                \"^[A-Za-z0-9_]*$\": {}\n            },\n            \"additionalProperties\": false\n        },\n        \"dns_alt_names\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"string\"\n            }\n        }\n    },\n    \"required\": [\"name\", \"state\", \"fingerprint\", \"fingerprints\", \"dns_alt_names\"],\n    \"additionalProperties\": false\n}\n"
  },
  {
    "path": "api/schemas/json-meta-schema.json",
    "content": "{\n    \"id\": \"http://json-schema.org/draft-04/schema#\",\n    \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n    \"description\": \"Core schema meta-schema\",\n    \"definitions\": {\n        \"schemaArray\": {\n            \"type\": \"array\",\n            \"minItems\": 1,\n            \"items\": { \"$ref\": \"#\" }\n        },\n        \"positiveInteger\": {\n            \"type\": \"integer\",\n            \"minimum\": 0\n        },\n        \"positiveIntegerDefault0\": {\n            \"allOf\": [ { \"$ref\": \"#/definitions/positiveInteger\" }, { \"default\": 0 } ]\n        },\n        \"simpleTypes\": {\n            \"enum\": [ \"array\", \"boolean\", \"integer\", \"null\", \"number\", \"object\", \"string\" ]\n        },\n        \"stringArray\": {\n            \"type\": \"array\",\n            \"items\": { \"type\": \"string\" },\n            \"minItems\": 1,\n            \"uniqueItems\": true\n        }\n    },\n    \"type\": \"object\",\n    \"properties\": {\n        \"id\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n        },\n        \"$schema\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n        },\n        \"title\": {\n            \"type\": \"string\"\n        },\n        \"description\": {\n            \"type\": \"string\"\n        },\n        \"default\": {},\n        \"multipleOf\": {\n            \"type\": \"number\",\n            \"minimum\": 0,\n            \"exclusiveMinimum\": true\n        },\n        \"maximum\": {\n            \"type\": \"number\"\n        },\n        \"exclusiveMaximum\": {\n            \"type\": \"boolean\",\n            \"default\": false\n        },\n        \"minimum\": {\n            \"type\": \"number\"\n        },\n        \"exclusiveMinimum\": {\n            \"type\": \"boolean\",\n            \"default\": false\n        },\n        \"maxLength\": { \"$ref\": \"#/definitions/positiveInteger\" },\n        \"minLength\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\n        \"pattern\": {\n            \"type\": \"string\",\n            \"format\": \"regex\"\n        },\n        \"additionalItems\": {\n            \"anyOf\": [\n                { \"type\": \"boolean\" },\n                { \"$ref\": \"#\" }\n            ],\n            \"default\": {}\n        },\n        \"items\": {\n            \"anyOf\": [\n                { \"$ref\": \"#\" },\n                { \"$ref\": \"#/definitions/schemaArray\" }\n            ],\n            \"default\": {}\n        },\n        \"maxItems\": { \"$ref\": \"#/definitions/positiveInteger\" },\n        \"minItems\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\n        \"uniqueItems\": {\n            \"type\": \"boolean\",\n            \"default\": false\n        },\n        \"maxProperties\": { \"$ref\": \"#/definitions/positiveInteger\" },\n        \"minProperties\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\n        \"required\": { \"$ref\": \"#/definitions/stringArray\" },\n        \"additionalProperties\": {\n            \"anyOf\": [\n                { \"type\": \"boolean\" },\n                { \"$ref\": \"#\" }\n            ],\n            \"default\": {}\n        },\n        \"definitions\": {\n            \"type\": \"object\",\n            \"additionalProperties\": { \"$ref\": \"#\" },\n            \"default\": {}\n        },\n        \"properties\": {\n            \"type\": \"object\",\n            \"additionalProperties\": { \"$ref\": \"#\" },\n            \"default\": {}\n        },\n        \"patternProperties\": {\n            \"type\": \"object\",\n            \"additionalProperties\": { \"$ref\": \"#\" },\n            \"default\": {}\n        },\n        \"dependencies\": {\n            \"type\": \"object\",\n            \"additionalProperties\": {\n                \"anyOf\": [\n                    { \"$ref\": \"#\" },\n                    { \"$ref\": \"#/definitions/stringArray\" }\n                ]\n            }\n        },\n        \"enum\": {\n            \"type\": \"array\",\n            \"minItems\": 1,\n            \"uniqueItems\": true\n        },\n        \"type\": {\n            \"anyOf\": [\n                { \"$ref\": \"#/definitions/simpleTypes\" },\n                {\n                    \"type\": \"array\",\n                    \"items\": { \"$ref\": \"#/definitions/simpleTypes\" },\n                    \"minItems\": 1,\n                    \"uniqueItems\": true\n                }\n            ]\n        },\n        \"allOf\": { \"$ref\": \"#/definitions/schemaArray\" },\n        \"anyOf\": { \"$ref\": \"#/definitions/schemaArray\" },\n        \"oneOf\": { \"$ref\": \"#/definitions/schemaArray\" },\n        \"not\": { \"$ref\": \"#\" }\n    },\n    \"dependencies\": {\n        \"exclusiveMaximum\": [ \"maximum\" ],\n        \"exclusiveMinimum\": [ \"minimum\" ]\n    },\n    \"default\": {}\n}\n"
  },
  {
    "path": "api/schemas/node.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"Node\",\n    \"description\": \"A Puppet node object\",\n    \"type\":        \"object\",\n    \"properties\": {\n        \"environment\": {\n            \"type\": \"string\"\n        },\n        \"name\": {\n            \"type\": \"string\"\n        },\n        \"classes\": {\n            \"type\": \"array\",\n            \"items\": { \"type\": \"string\" }\n        },\n        \"parameters\": {\n            \"type\": \"object\"\n        }\n    },\n    \"required\": [\"name\", \"environment\"],\n    \"additionalProperties\": false\n}\n"
  },
  {
    "path": "api/schemas/report.json",
    "content": "{\n    \"$schema\":     \"http://json-schema.org/draft-04/schema#\",\n    \"title\":       \"Report\",\n    \"description\": \"A Puppet Report\",\n    \"type\":        \"object\",\n\n    \"additionalProperties\": false,\n\n    \"required\": [\n        \"host\",\n        \"time\",\n        \"resource_statuses\",\n        \"configuration_version\",\n        \"report_format\",\n        \"puppet_version\",\n        \"status\",\n        \"transaction_completed\",\n        \"noop\",\n        \"noop_pending\",\n        \"environment\"\n    ],\n\n    \"properties\": {\n        \"host\": {\n            \"description\": \"The host that generated the report\",\n            \"type\":        \"string\"\n        },\n\n        \"time\": {\n            \"description\": \"When the run began. In ISO 8601 format with 9 characters second-fragment\",\n            \"type\":        \"string\"\n        },\n\n        \"logs\": {\n            \"description\": \"Zero or more occurrences of Log objects\",\n            \"type\":        \"array\",\n            \"items\": {\n                 \"type\": \"object\",\n                 \"$ref\": \"#/definitions/log\"\n            }\n        },\n\n        \"metrics\": {\n            \"description\": \"Hash of metric category to data for that category\",\n            \"type\":        \"object\",\n\n            \"properties\": {\n                \"time\": {\n                    \"type\": \"object\",\n                    \"$ref\": \"#/definitions/time_metrics\"\n                },\n\n                \"resources\": {\n                    \"type\": \"object\",\n                    \"$ref\": \"#/definitions/resources_metrics\"\n                },\n\n                \"events\": {\n                    \"type\": \"object\",\n                    \"$ref\": \"#/definitions/events_metrics\"\n                },\n\n                \"changes\": {\n                    \"type\": \"object\",\n                    \"$ref\": \"#/definitions/changes_metrics\"\n                }\n            },\n\n            \"additionalProperties\": false,\n            \"minProperties\":        1\n        },\n\n        \"resource_statuses\": {\n            \"description\":       \"Object with one property per resource-name having type as described by #report/status.json schema type\",\n            \"type\": \"object\",\n            \"patternProperties\": {\n                \".*\": {\n                    \"type\": \"object\",\n                    \"$ref\": \"#/definitions/status\"\n                }\n            },\n            \"additionalProperties\": false\n        },\n\n        \"configuration_version\": {\n            \"description\": \"The configuration version of the Puppet run. This is a String if the user has specified their own versioning scheme, otherwise an Integer representing seconds since the epoch.\",\n            \"anyOf\":       [\n                { \"type\": \"integer\", \"description\": \"seconds since the epoch\" },\n                { \"type\": \"string\",  \"description\": \"custom versioning scheme\" }\n            ]\n        },\n\n        \"transaction_uuid\": {\n            \"description\": \"A UUID covering the transaction. The query parameters for the catalog retrieval will have included the same UUID.\",\n            \"type\":        \"string\"\n        },\n\n        \"code_id\": {\n            \"description\": \"The id of the code input to the compiler.\",\n            \"type\":        \"string\"\n        },\n\n        \"job_id\": {\n            \"description\": \"The id of the job that this transaction is a part of.\",\n            \"type\":        [\"string\", \"null\"]\n        },\n\n        \"catalog_uuid\": {\n            \"description\": \"A UUID covering a specific catalog.\",\n            \"type\":        \"string\"\n        },\n\n        \"cached_catalog_status\": {\n            \"description\": \"Whether a cached catalog was used, and if so, why it was used. Enumerator with one of the values:\\n\\n* `not_used`, if a cached catalog was not used.\\n* `explicitly_requested`, if a cached catalog was used because the use_cached_catalog option was set.\\n* `on_failure`, if a cached catalog was used because the usecacheonfailure setting was set and the agent failed to download a new catalog\",\n            \"enum\": [\n              \"not_used\",\n              \"explicitly_requested\",\n              \"on_failure\"\n            ]\n        },\n\n        \"server_used\": {\n            \"description\": \"The name of the server that was used to compile the catalog. If failover occurred, this will hold the first server successfully contacted. If this run had no server (e.g. a `puppet apply` run), this field will be blank\",\n            \"type\": \"string\"\n        },\n\n        \"report_format\": {\n            \"description\": \"The report format version documented by this schema\",\n            \"type\":        \"integer\",\n            \"enum\":        [\"12\", 12]\n        },\n\n        \"puppet_version\": {\n            \"description\": \"The version of the Puppet Agent the report is for.\",\n            \"type\":        \"string\"\n        },\n\n        \"status\": {\n            \"description\": \"Report status, enumerator with one of the values:\\n\\n* `failed`, if run failed\\n* `changed`, if something changed\\n* `unchanged`, if nothing changed from the previous run\\n\",\n            \"enum\": [\n                \"failed\",\n                \"changed\",\n                \"unchanged\"\n            ]\n        },\n\n        \"transaction_completed\": {\n                    \"description\": \"Whether the transaction completed (e.g. didn't have an unrescued exception).\",\n                    \"type\":        \"boolean\"\n                },\n\n        \"noop\": {\n            \"description\": \"Whether the puppet run was started in noop mode\",\n            \"type\":        \"boolean\"\n        },\n\n        \"noop_pending\": {\n            \"description\": \"Whether there are changes that we decided not to apply because of noop\",\n            \"type\":        \"boolean\"\n        },\n\n        \"environment\": {\n            \"description\": \"The name of the environment that was used for the puppet run (e.g. \\\"production\\\").\",\n            \"type\":        \"string\"\n        },\n\n        \"corrective_change\": {\n            \"description\": \"True if a change or noop event in this report was caused by an unexpected change to the system between Puppet runs.\",\n            \"type\":        \"boolean\"\n        }\n    },\n\n    \"definitions\" : {\n        \"log\" : {\n            \"properties\": {\n\n                \"file\": {\n                    \"description\": \"The pathname of the manifest file which triggered the log message.\",\n                    \"oneOf\":       [\n                        {\"type\": \"string\"},\n                        {\"type\": \"null\"}\n                    ]\n                },\n\n                \"line\": {\n                    \"description\": \"The line number in the manifest file which triggered the log message.\",\n                    \"oneOf\":       [\n                        {\"type\": \"string\"},\n                        {\"type\": \"null\"}\n                    ]\n                },\n\n                \"level\": {\n                    \"description\": \"The severity of the message.\",\n                    \"enum\": [\n                        \"debug\",\n                        \"info\",\n                        \"notice\",\n                        \"warning\",\n                        \"err\",\n                        \"alert\",\n                        \"emerg\",\n                        \"crit\"\n                    ]\n                },\n\n                \"message\": {\n                    \"description\": \"The message itself.\",\n                    \"type\":        \"string\"\n                },\n\n                \"source\": {\n                    \"description\": \"The origin of the log message. This could be a resource, a property of a resource, or the string 'Puppet'.\",\n                    \"type\":        \"string\"\n                },\n\n                \"tags\": {\n                    \"description\": \"The strings with which the source is tagged\",\n                    \"type\":        \"array\",\n                    \"items\":       { \"type\": \"string\" }\n                },\n\n                \"time\": {\n                    \"description\": \"When the message was sent. In ISO 8601 format with 9 characters second-fragment\",\n                    \"type\":        \"string\"\n                }\n            },\n\n            \"required\": [\n                \"level\",\n                \"message\",\n                \"source\",\n                \"tags\",\n                \"time\"\n            ],\n            \"additionalProperties\": false\n        },\n\n        \"time_metrics\":    {\n             \"description\": \"A Metric in the `time` category\",\n             \"type\":        \"object\",\n             \"properties\": {\n\n                \"name\": {\n                    \"description\": \"The name of the metric category ('time')\",\n                    \"enum\":        [\"time\"]\n                },\n\n                \"label\": {\n                    \"description\": \"The name in title form\",\n                    \"enum\":        [\"Time\"]\n                },\n\n                \"values\": {\n                    \"description\": \"The metric values in the 'time' category contains one entry per resource type in the catalog for\\nwhich there is at least one resource and the values named:\\n\\n* `config_retrieval`\\n* `total`\\n\\n\",\n                    \"type\":        \"array\",\n                    \"items\": {\n                        \"description\": \"Each entry in `values` is an array with 3 slots for `name`, `label` and `value`\",\n                        \"type\":        \"array\",\n                        \"items\": [\n                            {\n                                \"description\": \"The name of the value (the name of a resource type, `config_retrieval`, or `total`\",\n                                \"type\":        \"string\"\n                            },\n                            {\n                                \"description\": \"The name in title form\",\n                                \"type\":        \"string\"\n                            },\n                            {\n                                \"description\": \"The value\",\n                                \"type\":        \"number\"\n                            }\n                        ]\n                    }\n                }\n            },\n\n            \"required\":             [ \"name\", \"label\", \"values\"],\n            \"additionalProperties\": false\n        },\n\n        \"resources_metrics\": {\n            \"description\": \"A Metric in the `resources` category\",\n            \"type\":        \"object\",\n            \"properties\": {\n\n                \"name\": {\n                    \"description\": \"The name of the metric category ('resources')\",\n                    \"enum\":        [\"resources\"]\n                },\n\n                \"label\": {\n                    \"description\": \"The name in title form\",\n                    \"enum\":        [\"Resources\"]\n                },\n\n                \"values\": {\n                    \"description\": \"The metric values in the 'resources' category\",\n                    \"type\":        \"array\",\n                    \"items\": {\n                        \"description\": \"Each entry in `values` is an array with 3 slots for `name`, `label` and `value`\",\n                        \"type\":        \"array\",\n                        \"items\": [\n                            {\n                                \"description\": \"The name of the value\",\n                                \"enum\": [\n                                    \"failed\",\n                                    \"out_of_sync\",\n                                    \"changed\",\n                                    \"total\",\n                                    \"skipped\",\n                                    \"failed_to_restart\",\n                                    \"restarted\",\n                                    \"scheduled\",\n                                    \"corrective_change\"\n                                ]\n                            },\n                            {\n                                \"description\": \"The name in title form\",\n                                \"enum\": [\n                                    \"Failed\",\n                                    \"Out of sync\",\n                                    \"Changed\",\n                                    \"Total\",\n                                    \"Skipped\",\n                                    \"Failed to restart\",\n                                    \"Restarted\",\n                                    \"Scheduled\",\n                                    \"Corrective change\"\n                                ]\n                            },\n                            {\n                                \"description\": \"The value\",\n                                \"type\":        \"integer\"\n                            }\n                        ]\n                    }\n                }\n            },\n            \"required\":             [ \"name\", \"label\", \"values\"],\n            \"additionalProperties\": false\n        },\n\n        \"events_metrics\": {\n            \"description\": \"A Metric in the `events` category\",\n            \"type\":        \"object\",\n            \"properties\": {\n\n                \"name\": {\n                    \"description\": \"The name of the metric category ('events')\",\n                    \"enum\":        [\"events\"]\n                },\n\n                \"label\": {\n                    \"description\": \"The name in title form\",\n                    \"enum\":        [\"Events\"]\n                },\n\n                \"values\": {\n                    \"description\": \"The metric values in the 'events' category. The entry named `total` is always present, the others are present only if their value is non zero.\",\n                    \"type\":        \"array\",\n                    \"items\": {\n                        \"description\": \"Each entry in `values` is an array with 3 slots for `name`, `label` and `value`.\",\n                        \"type\":        \"array\",\n                        \"items\": [\n                            {\n                                \"description\": \"The name of the value\",\n                                \"enum\": [\n                                    \"success\",\n                                    \"failure\",\n                                    \"audit\",\n                                    \"noop\",\n                                    \"total\"\n                                ]\n                            },\n                            {\n                                \"description\": \"The name in title form\",\n                                \"enum\":  [\n                                    \"Success\",\n                                    \"Failure\",\n                                    \"Audit\",\n                                    \"Noop\",\n                                    \"Total\"\n                                ]\n                            },\n                            {\n                                \"description\": \"The value\",\n                                \"type\":        \"integer\"\n                            }\n                        ]\n                    }\n                }\n           },\n\n            \"required\":             [ \"name\", \"label\", \"values\"],\n            \"additionalProperties\": false\n        },\n\n        \"changes_metrics\": {\n            \"description\": \"A Metric in the `changes` category\",\n            \"type\":        \"object\",\n            \"properties\": {\n\n                \"name\": {\n                    \"description\": \"The name of the metric category ('changes')\",\n                    \"enum\":        [\"changes\"]\n                },\n\n                \"label\": {\n                    \"description\": \"The name in title form\",\n                    \"enum\":        [\"Changes\"]\n                },\n\n                \"values\": {\n                    \"description\": \"The metric value in the 'changes' category is called `total` - there is only one total.\",\n                    \"type\":        \"array\",\n                    \"items\": {\n                        \"description\": \"Each entry in `values` is an array with 3 slots for `name`, `label` and `value`.\",\n                        \"type\":        \"array\",\n                        \"items\": [\n                            {\n                                \"description\": \"The name of the value\",\n                                \"enum\":        [\"total\" ]\n                            },\n                            {\n                                \"description\": \"The name in title form\",\n                                \"enum\":        [\"Total\"]\n                            },\n                            {\n                                \"description\": \"The value\",\n                                \"type\":        \"integer\"\n                            }\n                        ]\n                    }\n                }\n            },\n            \"required\":             [ \"name\", \"label\", \"values\"],\n            \"additionalProperties\": false\n        },\n\n        \"status\": {\n            \"description\": \"A Status entry for a resource in a report\",\n            \"type\":        \"object\",\n\n            \"properties\": {\n\n                \"resource_type\": {\n                    \"description\": \"The type name of the resource, capitalized\",\n                    \"type\":        \"string\",\n                    \"pattern\":     \"^[A-Z].*$\"\n                },\n\n                \"title\": {\n                    \"description\": \"The title of the resource.\",\n                    \"type\":        \"string\"\n                },\n\n                \"resource\": {\n                    \"description\": \"The resource name, in the form Type[title]. **Deprecated**. This is always the same string as the name of the property where this Status object is the value.\",\n                    \"type\":        \"string\"\n                },\n\n                \"provider_used\": {\n                    \"description\": \"The provider name used by the resource\",\n                    \"oneOf\":       [\n                        {\"type\": \"string\"},\n                        {\"type\": \"null\"}\n                    ]\n                },\n\n                \"file\": {\n                    \"description\": \"The pathname of the manifest file which declared the resource.\",\n                    \"oneOf\":       [\n                        {\"type\": \"string\"},\n                        {\"type\": \"null\"}\n                    ]\n                },\n\n                \"line\": {\n                    \"description\": \"The line number in the manifest file which declared the resource.\",\n                    \"oneOf\":       [\n                        {\"type\": \"string\"},\n                        {\"type\": \"null\"}\n                    ]\n                },\n\n                \"evaluation_time\": {\n                    \"description\": \"The amount of time, in seconds, taken to evaluate the resource.\",\n                    \"oneOf\":       [\n                        {\"type\": \"number\"},\n                        {\"type\": \"null\"}\n                    ]\n                },\n\n                \"change_count\": {\n                    \"description\": \"The number of properties which changed.\",\n                    \"type\":        \"integer\"\n                },\n\n                \"out_of_sync_count\": {\n                    \"description\": \"The number of properties which were out of sync.\",\n                    \"type\":        \"integer\"\n                },\n\n                \"tags\": {\n                    \"description\": \"The strings with which the resource is tagged\",\n                    \"type\":        \"array\",\n                    \"items\":      { \"type\": \"string\" }\n                },\n\n                \"time\": {\n                    \"description\": \"The time the resource was evaluated. In ISO 8601 format with 9 characters second-fragment\",\n                    \"type\":        \"string\"\n                },\n\n                \"events\": {\n                    \"description\": \"the Puppet::Transaction::Event objects for the resource\",\n                    \"type\":        \"array\",\n                    \"items\":       { \"$ref\": \"#/definitions/event\" }\n                },\n\n                \"out_of_sync\": {\n                    \"description\": \"True if out_of_sync_count > 0, otherwise false. **Deprecated**\",\n                    \"type\":        \"boolean\"\n                },\n\n                \"changed\": {\n                    \"description\": \"True if change_count > 0, otherwise false. **Deprecated**\",\n                    \"type\":        \"boolean\"\n                },\n\n                \"skipped\": {\n                    \"description\": \"True if the resource was skipped, otherwise false.\",\n                    \"type\":        \"boolean\"\n                },\n\n                \"failed\": {\n                    \"description\": \"True if Puppet experienced an error while evaluating this resource, otherwise false. **Deprecated**\",\n                    \"type\":        \"boolean\"\n                },\n\n                \"failed_to_restart\": {\n                    \"description\": \"True if Puppet experienced an error while trying to restart this resource (For example, when a Service resource has been notified from another resource), otherwise false.\",\n                    \"type\":        \"boolean\"\n                },\n\n                \"containment_path\": {\n                    \"description\": \"An array of strings; each element represents a container (type or class) that, together, make up the path of the resource in the catalog.\",\n                    \"type\":        \"array\",\n                    \"items\":       { \"type\": \"string\" }\n                },\n\n                \"corrective_change\": {\n                    \"description\": \"True if a change or noop event on this resource was caused by an unexpected change to the system between Puppet runs.\",\n                    \"type\":        \"boolean\"\n                }\n            },\n\n            \"required\": [\n                \"resource_type\",\n                \"title\",\n                \"change_count\",\n                \"out_of_sync_count\",\n                \"tags\",\n                \"events\",\n                \"skipped\",\n                \"containment_path\",\n                \"corrective_change\"\n            ],\n            \"additionalProperties\":   false\n        },\n        \"event\": {\n            \"description\": \"An Event in a Report\",\n            \"type\":        \"object\",\n            \"properties\": {\n\n                \"audited\": {\n                    \"description\": \"True if this property is being audited, otherwise false. True in report of `inspect` kind.\",\n                    \"type\":        \"boolean\"\n                },\n\n                \"property\": {\n                    \"description\": \"The property for which the event occurred.\",\n                    \"oneOf\": [\n                        { \"type\": \"string\" },\n                        { \"type\": \"null\" }\n                    ]\n                },\n\n                \"previous_value\": {\n                    \"description\": \"The value of the property before the change (if any) was applied.\",\n                    \"oneOf\": [\n                        { \"type\": \"string\" },\n                        { \"type\": \"array\" },\n                        { \"type\": \"object\" },\n                        { \"type\": \"null\" }\n                    ]\n                },\n\n                \"desired_value\": {\n                    \"description\": \"the value specified in the manifest.\",\n                    \"oneOf\": [\n                        { \"type\": \"string\" },\n                        { \"type\": \"array\" },\n                        { \"type\": \"object\" },\n                        { \"type\": \"null\" }\n                    ]\n                },\n\n                \"historical_value\": {\n                    \"description\": \"The audited value from a previous run of Puppet, if known. Absent in reports of `inspect` kind.\",\n                    \"oneOf\": [\n                        { \"type\": \"string\" },\n                        { \"type\": \"array\" },\n                        { \"type\": \"object\" },\n                        { \"type\": \"null\" }\n                    ]\n                },\n\n                \"message\": {\n                    \"description\": \"The log message generated by this event.\",\n                    \"type\":        \"string\"\n                },\n\n                \"name\": {\n                    \"description\": \"The name of the event.\",\n                    \"type\":        \"string\"\n                },\n\n                \"redacted\": {\n                  \"description\": \"Whether this event has been redacted\",\n                  \"type\": \"boolean\"\n                },\n\n                \"status\": {\n                    \"description\": \"One of the following strings:\\n\\n* `success` - property was out of sync, and was successfully changed to be in sync.\\n* `failure`- property was out of sync, and couldn't be changed to be in sync due to an error.\\n* `noop` - property was out of sync, and wasn't changed due to noop mode.\\n* `audit` - property was in sync, and was being audited.\\n\\ndepending on the type of the event. Always `audit` in reports of `inspect` kind.\\n\",\n                    \"enum\": [\n                        \"success\",\n                        \"failure\",\n                        \"noop\",\n                        \"audit\"\n                    ]\n                },\n                \"time\": {\n                    \"description\": \"The time at which the property was evaluated. In ISO 8601 format with 9 characters second-fragment\",\n                    \"type\":        \"string\"\n                },\n\n                \"corrective_change\": {\n                    \"description\": \"True if this event was caused by an unexpected change to the system between Puppet runs.\",\n                    \"type\":        \"boolean\"\n                }\n            },\n            \"required\": [\n                \"audited\",\n                \"property\",\n                \"message\",\n                \"name\",\n                \"status\",\n                \"time\",\n                \"corrective_change\"\n            ],\n\n            \"additionalProperties\": false\n        }\n    }\n}\n"
  },
  {
    "path": "benchmarks/catalog_memory/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\n# For memory debugging - if the core_ext is not loaded, things break inside mass\n# require 'mass'\nrequire 'objspace'\n\n# Only runs for Ruby > 2.1.0, and must do this early since ObjectSpace.trace_object_allocations_start must be called\n# as early as possible.\n#\nRUBYVER_ARRAY = RUBY_VERSION.split(\".\").collect {|s| s.to_i }\nRUBYVER = (RUBYVER_ARRAY[0] << 16 | RUBYVER_ARRAY[1] << 8 | RUBYVER_ARRAY[2])\nif RUBYVER < (2 << 16 | 1 << 8 | 0)\n  puts \"catalog_memory requires Ruby version >= 2.1.0 to run. Skipping\"\n  exit(0)\nend\n\nObjectSpace.trace_object_allocations_start\n\nclass Benchmarker\n  include FileUtils\n\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n    @@first_counts = nil\n    @@first_refs = nil\n    @@count = 0\n  end\n\n  def setup\n  end\n\n  def run(args=nil)\n    unless @initialized\n      require 'puppet'\n      config = File.join(@target, 'puppet.conf')\n      Puppet.initialize_settings(['--config', config])\n      @initialized = true\n    end\n    @@count += 1\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    # Mimic what apply does (or the benchmark will in part run for the *root* environment)\n    Puppet.push_context({:current_environment => env},'current env for benchmark')\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n    Puppet.pop_context\n    GC.start\n    sleep(2)\n    counted = ObjectSpace.count_objects({})\n    if @@first_counts && @@count == 10\n      diff = @@first_counts.merge(counted) {|k, base_v, new_v| new_v - base_v }\n      puts \"Count of objects TOTAL = #{diff[:TOTAL]}, FREE = #{diff[:FREE]}, T_OBJECT = #{diff[:T_OBJECT]}, T_CLASS = #{diff[:T_CLASS]}\"\n      changed = diff.reject {|k,v| v == 0}\n      puts \"Number of changed classes = #{changed}\"\n      GC.start\n      # Find references to leaked Objects\n      leaked_instances = ObjectSpace.each_object.reduce([]) {|x, o| x << o.object_id; x } - @@first_refs\n      File.open(\"diff.json\", \"w\") do |f|\n        leaked_instances.each do |id|\n          o = ObjectSpace._id2ref(id)\n          f.write(ObjectSpace.dump(o)) if !o.nil?\n        end\n      end\n      # Output information where bound objects where instantiated\n      map_of_allocations = leaked_instances.reduce(Hash.new(0)) do |memo, x|\n        o = ObjectSpace._id2ref(x)\n        class_path = ObjectSpace.allocation_class_path(o)\n        class_path = class_path.nil? ? ObjectSpace.allocation_sourcefile(o) : class_path\n        if !class_path.nil?\n          method = ObjectSpace.allocation_method_id(o)\n          source_line = ObjectSpace.allocation_sourceline(o)\n          memo[\"#{class_path}##{method}-#{source_line}\"] += 1\n        end\n        memo\n      end\n      map_of_allocations.sort_by {|k, v| v}.reverse_each {|k,v| puts \"#{v} #{k}\" }\n      # Dump the heap for further analysis\n      GC.start\n      ObjectSpace.dump_all(output: File.open('heap.json','w'))\n    elsif @@count == 1\n      # Set up baseline and output info for first run\n      @@first_counts = counted\n      @@first_refs = ObjectSpace.each_object.reduce([]) {|x, o| x << o.object_id; x }\n      diff = @@first_counts\n      puts \"Count of objects TOTAL = #{diff[:TOTAL]}, FREE = #{diff[:FREE]}, T_OBJECT = #{diff[:T_OBJECT]}, T_CLASS = #{diff[:T_CLASS]}\"\n    end\n\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'empty_catalog')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n    File.join(environment, 'manifests', 'site.pp'),{})\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/catalog_memory/description",
    "content": "Benchmark scenario: Runs an empty catalog and dumps the state of the memory after all runs and a diff between first and last run\nBenchmark target: catalog compilation memory consumption / leak\nParser: Future\nRequires: Ruby 2.1.0\n\n"
  },
  {
    "path": "benchmarks/catalog_memory/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = '0'\n"
  },
  {
    "path": "benchmarks/catalog_memory/site.pp.erb",
    "content": "notice('hello world')"
  },
  {
    "path": "benchmarks/defined_types/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n  end\n\n  def run(args=nil)\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'defined_types')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => @size)\n\n    @size.times do |i|\n      module_name = \"module#{i}\"\n      module_base = File.join(environment, 'modules', module_name)\n      manifests = File.join(module_base, 'manifests')\n\n      mkdir_p(manifests)\n\n      File.open(File.join(module_base, 'metadata.json'), 'w') do |f|\n        JSON.dump({\n          \"types\" => [],\n          \"source\" => \"\",\n          \"author\" => \"Defined Types Benchmark\",\n          \"license\" => \"Apache 2.0\",\n          \"version\" => \"1.0.0\",\n          \"description\" => \"Defined Types benchmark module #{i}\",\n          \"summary\" => \"Just this benchmark module, you know?\",\n          \"dependencies\" => [],\n        }, f)\n      end\n\n      render(File.join(templates, 'module', 'testing.pp.erb'),\n             File.join(manifests, 'testing.pp'),\n             :name => module_name)\n    end\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/defined_types/description",
    "content": "Benchmark scenario: heavy use of defined types\nBenchmark target: catalog compilation\n\n"
  },
  {
    "path": "benchmarks/defined_types/module/testing.pp.erb",
    "content": "define <%= name %>::testing {\n  notify { \"in <%= name %>: $title\": }\n}\n"
  },
  {
    "path": "benchmarks/defined_types/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\n"
  },
  {
    "path": "benchmarks/defined_types/site.pp.erb",
    "content": "<% size.times do |i| %>\n  module<%= i %>::testing { \"first\": }\n  module<%= i %>::testing{ \"second\": }\n<% end %>\n"
  },
  {
    "path": "benchmarks/dependency_loading/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n    @@benchmark_count ||= 0\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n  end\n\n  def run(args=nil)\n    envs = Puppet.lookup(:environments)\n\n    50.times do\n    envs.clear('benchmarking')\n      node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n      Puppet::Resource::Catalog.indirection.find('testing', :use_node => node)\n    end\n  end\n\n  def benchmark_count\n    bc = @@benchmark_count\n    @@benchmark_count += 1\n    bc\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    modules = File.join(environment, 'modules')\n    templates = File.join('benchmarks', 'dependency_loading')\n\n    mkdir_p(modules)\n    mkdir_p(File.join(environment, 'manifests'))\n\n    module_count = @size * 2\n    modula = 10\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => module_count, :modula => modula)\n\n    module_count.times do |i|\n      module_name = \"module#{i}\"\n      module_base = File.join(modules, module_name)\n      manifests = File.join(module_base, 'manifests')\n      global_module_functions = File.join(module_base, 'lib', 'puppet', 'functions')\n      module_functions = File.join(global_module_functions, module_name)\n\n      mkdir_p(manifests)\n      mkdir_p(module_functions)\n\n      File.open(File.join(module_base, 'metadata.json'), 'w') do |f|\n        JSON.dump({\n          'name' => \"tester-#{module_name}\",\n          'author' => 'Puppet Labs tester',\n          'license' => 'Apache 2.0',\n          'version' => '1.0.0',\n          'summary' => 'Benchmark module',\n          'dependencies' => i > 0 ? dependency_to(i - 1) : [],\n          'source' => ''\n        }, f)\n      end\n\n      if (i + 1) % modula == 0\n        render(File.join(templates, 'module', 'init.pp.erb'),\n               File.join(manifests, 'init.pp'),\n               :name => module_name, :other => \"module#{i - 1}\")\n      else\n        render(File.join(templates, 'module', 'init.pp_no_call.erb'),\n          File.join(manifests, 'init.pp'),\n          :name => module_name)\n      end\n\n      function_template = File.join(templates, 'module', 'function.erb')\n      render(function_template, File.join(module_functions, \"f#{module_name}.rb\"), :name => module_name)\n      global_function_template = File.join(templates, 'module', 'global_function.erb')\n      render(global_function_template, File.join(global_module_functions, \"f#{module_name}.rb\"), :name => module_name)\n    end\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def dependency_to(n)\n    [ {'name' => \"tester-module#{n}\", 'version_requirement' => '1.0.0'} ]\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    site.filename = erb_file\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/dependency_loading/description",
    "content": "Benchmark scenario: call of local and global functions in modules appointed by dependencies\nBenchmark target: module loaders\n\nA large number of modules are generated. Each module contains a global and a local function.\nA subset of the modules declares a dependency to another module in their metadata.json and\nhas an init.pp which contains calls to the global and local function that resides in the\nmodule appointed by the dependency.\n\nThe objective is to measure the difference between having loaders traversing dependencies\nfirst or disregard the dependencies altogether (traverse all).\n"
  },
  {
    "path": "benchmarks/dependency_loading/module/function.erb",
    "content": "Puppet::Functions.create_function(:'<%= name %>::f<%= name %>') do\n  def f<%= name %>\n    '<%= name %>::f<%= name %>'\n  end\nend\n"
  },
  {
    "path": "benchmarks/dependency_loading/module/global_function.erb",
    "content": "Puppet::Functions.create_function(:'f<%= name %>') do\n  def f<%= name %>\n    'global f<%= name %>'\n  end\nend\n"
  },
  {
    "path": "benchmarks/dependency_loading/module/init.pp.erb",
    "content": "class <%= name %> {\n  # Call local and global function defined in <other>\n  $v = [ <%= other %>::f<%= other %>(), f<%= other %>() ]\n}\n"
  },
  {
    "path": "benchmarks/dependency_loading/module/init.pp_no_call.erb",
    "content": "class <%= name %> {\n}\n"
  },
  {
    "path": "benchmarks/dependency_loading/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/dependency_loading/site.pp.erb",
    "content": "<% size.times do |i| %><% if (i + 1) % modula == 0 %>include module<%= i %>\n<% end %><% end %>\n"
  },
  {
    "path": "benchmarks/empty_catalog/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n  end\n\n  def setup\n  end\n\n  def run(args=nil)\n    unless @initialized\n      require 'puppet'\n      config = File.join(@target, 'puppet.conf')\n      Puppet.initialize_settings(['--config', config])\n      @initialized = true\n    end\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    # Mimic what apply does (or the benchmark will in part run for the *root* environment)\n    Puppet.push_context({:current_environment => env},'current env for benchmark')\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'empty_catalog')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n    File.join(environment, 'manifests', 'site.pp'),{})\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/empty_catalog/description",
    "content": "Benchmark scenario: an empty catalog (only one call to log a message) shows the setup time for env / compiler\nBenchmark target: catalog compilation overhead\nParser: Future\n\n"
  },
  {
    "path": "benchmarks/empty_catalog/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = '0'\n"
  },
  {
    "path": "benchmarks/empty_catalog/site.pp.erb",
    "content": "notice('hello world')"
  },
  {
    "path": "benchmarks/evaluations/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n    @micro_benchmarks = {}\n    @parsecount = 100\n    @evalcount = 100\n  end\n\n  def setup\n    require 'puppet'\n    require 'puppet/pops'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n    manifests = File.join('benchmarks', 'evaluations', 'manifests')\n    Dir.foreach(manifests) do |f|\n      if f =~ /^(.*)\\.pp$/\n        @micro_benchmarks[$1] = File.read(File.join(manifests, f))\n      end\n    end\n    # Run / Evaluate the common puppet logic\n    @env = Puppet.lookup(:environments).get('benchmarking')\n    @node = Puppet::Node.new(\"testing\", :environment => @env)\n    @parser  = Puppet::Pops::Parser::EvaluatingParser.new\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @scope = @compiler.topscope\n\n    # Perform a portion of what a compile does (just enough to evaluate the site.pp logic)\n    @compiler.catalog.environment_instance = @compiler.environment\n    @compiler.send(:evaluate_main)\n\n    # Then pretend we are running as part of a compilation\n    Puppet.push_context(@compiler.context_overrides, \"Benchmark masquerading as compiler configured context\")\n  end\n\n  def run(args = {})\n    details = args[:detail] || 'all'\n    measurements = []\n    @micro_benchmarks.each do |name, source|\n      # skip if all but the wanted if a single benchmark is wanted\n      match = details.match(/#{name}(?:[\\._\\s](parse|eval))?$/)\n      next unless details == 'all' || match\n      # if name ends with .parse or .eval only do that part, else do both parts\n      ending = match ? match[1] : nil # parse, eval or nil ending\n      unless ending == 'eval'\n        measurements << Benchmark.measure(\"#{name} parse\") do\n          1..@parsecount.times { @parser.parse_string(source, name) }\n        end\n      end\n      unless ending == 'parse'\n        model = @parser.parse_string(source, name)\n        measurements << Benchmark.measure(\"#{name} eval\") do\n          1..@evalcount.times do\n            begin\n              # Run each in a local scope\n              scope_memo = @scope.ephemeral_level\n              @scope.new_ephemeral(true)\n              @parser.evaluate(@scope, model)\n            ensure\n              # Toss the created local scope\n              @scope.pop_ephemerals(scope_memo)\n            end\n          end\n        end\n      end\n    end\n    measurements\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'evaluations')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n    File.join(environment, 'manifests', 'site.pp'),{})\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n\n    # Generate one module with a 3x function and a 4x function (namespaces)\n    module_name = \"module1\"\n    module_base = File.join(environment, 'modules', module_name)\n    manifests = File.join(module_base, 'manifests')\n    mkdir_p(manifests)\n    functions_3x = File.join(module_base, 'lib', 'puppet', 'parser', 'functions')\n    functions_4x = File.join(module_base, 'lib', 'puppet', 'functions')\n    mkdir_p(functions_3x)\n    mkdir_p(functions_4x)\n\n    File.open(File.join(module_base, 'metadata.json'), 'w') do |f|\n      JSON.dump({\n        \"types\" => [],\n        \"source\" => \"\",\n        \"author\" => \"Evaluations Benchmark\",\n        \"license\" => \"Apache 2.0\",\n        \"version\" => \"1.0.0\",\n        \"description\" => \"Evaluations Benchmark module 1\",\n        \"summary\" => \"Module with supporting logic for evaluations benchmark\",\n        \"dependencies\" => [],\n      }, f)\n    end\n\n    render(File.join(templates, 'module', 'init.pp.erb'),\n           File.join(manifests, 'init.pp'),\n           :name => module_name)\n\n    render(File.join(templates, 'module', 'func3.rb.erb'),\n           File.join(functions_3x, 'func3.rb'),\n           :name => module_name)\n\n    # namespaced function\n    mkdir_p(File.join(functions_4x, module_name))\n    render(File.join(templates, 'module', 'module1_func4.rb.erb'),\n           File.join(functions_4x, module_name, 'func4.rb'),\n           :name => module_name)\n\n    # non namespaced\n    render(File.join(templates, 'module', 'func4.rb.erb'),\n           File.join(functions_4x, 'func4.rb'),\n           :name => module_name)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\n\nend\n"
  },
  {
    "path": "benchmarks/evaluations/benchmarker_task.rb",
    "content": "# Helper class that is used by the Rake task generator.\n# Currently only supports defining arguments that are passed to run\n# (The rake task generator always passes :warm_up_runs as an Integer when profiling).\n# Other benchmarks, and for regular runs that wants arguments must specified them\n# as an Array of symbols.\n#\nclass BenchmarkerTask\n  def self.run_args\n    [:detail]\n  end\nend"
  },
  {
    "path": "benchmarks/evaluations/description",
    "content": "Benchmark scenario: evaluates a select set of time critical expressions\nBenchmark target: measuring individual use cases of evaluation\nParser: Future\n\nEvaluations:\n* fcall_3x - calls sprintf 20x times\n* fcall_4x - calls assert_type 20x times (is heavier than sprintf, have no similar simple 4x function)\n* interpolation - does 20x interpolations of variying length\n* var_absolute - references a top scope variable 20x times with absolute reference\n* var_relative - references a top scope variable 20x times with non absolute reference\n* var_class_absolute - references a class variable 20x times with absolute reference\n* var_class_relative - references a class variable 20x times with non absolute reference\n\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/assert_type.pp",
    "content": "$tmp = [\nassert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1),\nassert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1),\nassert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1),\nassert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1), assert_type(Integer,1),\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/fcall_3x.pp",
    "content": "$tmp = [\nfunc3(x,y), func3(x,y), func3(x,y), func3(x,y), func3(x,y),\nfunc3(x,y), func3(x,y), func3(x,y), func3(x,y), func3(x,y),\nfunc3(x,y), func3(x,y), func3(x,y), func3(x,y), func3(x,y),\nfunc3(x,y), func3(x,y), func3(x,y), func3(x,y), func3(x,y),\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/fcall_4x.pp",
    "content": "$tmp = [\nfunc4(x,y), func4(x,y), func4(x,y), func4(x,y), func4(x,y),\nfunc4(x,y), func4(x,y), func4(x,y), func4(x,y), func4(x,y),\nfunc4(x,y), func4(x,y), func4(x,y), func4(x,y), func4(x,y),\nfunc4(x,y), func4(x,y), func4(x,y), func4(x,y), func4(x,y),\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/fcall_ns4x.pp",
    "content": "$tmp = [\nmodule1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y),\nmodule1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y),\nmodule1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y),\nmodule1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y), module1::func4(x,y),\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/interpolation.pp",
    "content": "$tmp = [ \"...$x...\",\n  \"...$x...$x\",\n  \"...$x...$x...\",\n  \"...$x...$x...$x...\",\n  \"...$x...$x...$x...$x\",\n  \"...$x...$x...$x...$x...\",\n  \"...$x...$x...$x...$x...$x\",\n  \"...$x...$x...$x...$x...$x...\",\n  \"...$x...$x...$x...$x...$x...$x\",\n  \"...$x...$x...$x...$x...$x...$x...\",\n]"
  },
  {
    "path": "benchmarks/evaluations/manifests/var_absolute.pp",
    "content": "$tmp = [ $::x, $::x, $::x, $::x, $::x, $::x, $::x, $::x, $::x, $::x,\n  $::x, $::x, $::x, $::x, $::x, $::x, $::x, $::x, $::x, $::x,\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/var_class_absolute.pp",
    "content": "$tmp = [ $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a,\n   $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a,\n  $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a,\n $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a, $::testing::param_a,\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/var_class_relative.pp",
    "content": "$tmp = [ $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a,\n   $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a,\n  $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a,\n $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a, $testing::param_a,\n]\n"
  },
  {
    "path": "benchmarks/evaluations/manifests/var_relative.pp",
    "content": "$tmp = [$x, $x, $x, $x, $x, $x, $x, $x, $x, $x,\n $x, $x, $x, $x, $x, $x, $x, $x, $x, $x,\n]\n"
  },
  {
    "path": "benchmarks/evaluations/module/func3.rb.erb",
    "content": "Puppet::Parser::Functions::newfunction(:func3,\n  :arity => 2,\n  :doc => \"Blah blah, this is a lot of documentation that the ruby parser must deal with\n  because documentation is part of what is loaded at runtime. Some functions have\n  very little documentation, and some have quite a lot. This simulates documentation\n  that is slightly longer than the shortest ones.\") do |vals|\n    # produces nil\nend\n"
  },
  {
    "path": "benchmarks/evaluations/module/func4.rb.erb",
    "content": "# Blah blah, this is a lot of documentation that the ruby parser must deal with\n# because documentation is part of what is loaded at runtime. Some functions have\n# very little documentation, and some have quite a lot. This simulates documentation\n# that is slightly longer than the shortest ones.\n#\nPuppet::Functions.create_function(:func4) do\n  def func4(x,y)\n  end\nend"
  },
  {
    "path": "benchmarks/evaluations/module/init.pp.erb",
    "content": "# empty init (for now)"
  },
  {
    "path": "benchmarks/evaluations/module/module1_func4.rb.erb",
    "content": "# Blah blah, this is a lot of documentation that the ruby parser must deal with\n# because documentation is part of what is loaded at runtime. Some functions have\n# very little documentation, and some have quite a lot. This simulates documentation\n# that is slightly longer than the shortest ones.\n#\nPuppet::Functions.create_function(:'<%= name %>::func4') do\n  def func4(x,y)\n  end\nend"
  },
  {
    "path": "benchmarks/evaluations/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = '0'\nstrict_variables = true\n"
  },
  {
    "path": "benchmarks/evaluations/site.pp.erb",
    "content": "# Common setup done once for all micro benchmarks\n#\nclass testing {\n  $param_a = 10\n  $param_b = 20\n}\ninclude testing\n$x = 'aaaaaaaa'\n\n\n"
  },
  {
    "path": "benchmarks/fq_var_lookup/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n  end\n\n  def run(args=nil)\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'fq_var_lookup')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => @size)\n\n\n    module_name = \"tst_generate\"\n    module_base = File.join(environment, 'modules', module_name)\n    manifests = File.join(module_base, 'manifests')\n\n    mkdir_p(manifests)\n\n    File.open(File.join(module_base, 'metadata.json'), 'w') do |f|\n      JSON.dump({\n        \"types\" => [],\n        \"source\" => \"\",\n        \"author\" => \"tst_generate Benchmark\",\n        \"license\" => \"Apache 2.0\",\n        \"version\" => \"1.0.0\",\n        \"description\" => \"Qualified variable lookup benchmark module 1\",\n        \"summary\" => \"Just this benchmark module, you know?\",\n        \"dependencies\" => [],\n      }, f)\n\n    render(File.join(templates, 'module', 'params.pp.erb'),\n           File.join(manifests, 'params.pp'),\n           :name => module_name)\n\n    render(File.join(templates, 'module', 'badclass.pp.erb'),\n           File.join(manifests, 'badclass.pp'),\n           :size => @size)\n\n    end\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/fq_var_lookup/description",
    "content": "Benchmark scenario: many qualified variable lookups without leading ::.\nBenchmark target: catalog compilation.\n\n"
  },
  {
    "path": "benchmarks/fq_var_lookup/module/badclass.pp.erb",
    "content": "class tst_generate::badclass {\n  include tst_generate::params\n\n  group { \"${tst_generate::params::ugroup}\":\n    ensure => present,\n  }\n\n  user { \"${tst_generate::params::user}\":\n    ensure => present,\n    gid    => $tst_generate::params::ugroup,\n  }\n\n  file { \"${tst_generate::params::basedir}\":\n    ensure  => directory,\n    owner   => $tst_generate::params::user,\n    group   => $tst_generate::params::ugroup,\n  }\n\n<% (1..500).to_a.each do |i| %>\n  file{ \"${tst_generate::params::basedir}/file_<%= i %>\":\n    ensure  => file,\n    content => \"This is a test file.\\n\",\n    owner   => $tst_generate::params::user,\n    group   => $tst_generate::params::ugroup,\n    require => File[\"${tst_generate::params::basedir}\"],\n  }\n\n<% end %>\n}\n"
  },
  {
    "path": "benchmarks/fq_var_lookup/module/params.pp.erb",
    "content": "class tst_generate::params {\n  $basedir = '/tmp/type_collection_tst'\n  $user = 'tstusr'\n  $ugroup = 'tstugroup'\n}\n"
  },
  {
    "path": "benchmarks/fq_var_lookup/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\n"
  },
  {
    "path": "benchmarks/fq_var_lookup/site.pp.erb",
    "content": "include tst_generate::badclass\n"
  },
  {
    "path": "benchmarks/full_catalog/Gemfile",
    "content": "source ENV['GEM_SOURCE'] || \"https://rubygems.org\"\n\ngem \"r10k\", '~> 2.6'\n"
  },
  {
    "path": "benchmarks/full_catalog/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\nrequire 'bundler'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size='medium')\n    @target = target\n    @size = 'large'\n  end\n\n  def check_submodule\n    submodule = File.join('benchmarks', 'full_catalog', 'puppetlabs-puppetserver_perf_control')\n    unless File.exist?(File.join(submodule, 'Puppetfile'))\n      raise RuntimeError, 'The perf control repo is not readable. Make sure to initialize submodules by running: git submodule update --init --recursive'\n    end\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    environment = File.join(@target, 'environments', 'perf_control')\n    Puppet.initialize_settings(['--config', config])\n    FileUtils.cd(@target) do\n      Bundler::with_clean_env do\n        system(\"bundle install\")\n        system(\"bundle exec r10k puppetfile install --puppetfile #{File.join(environment, 'Puppetfile')} --moduledir #{File.join(environment, 'modules')} --config r10k.yaml\")\n      end\n    end\n\n    # Loading the base system affects the first run a lot so burn one run here\n    run\n  end\n\n  def run(args=nil)\n    # Probably could pare down these facts a lot\n    facts = Puppet::Node::Facts.new(\"testing\", {\n      'pe_concat_basedir'         => '/tmp/file',\n      'platform_symlink_writable' => true,\n      'pe_build'                  => '2016.4.4',\n      'identity' => {\n        'privileged' => true,\n      },\n      'mountpoints' => {\n        '/tmp' => {\n          'options' => ['rw'],\n        }\n      },\n      'puppet_files_dir_present' => true,\n      'puppetversion' => '4.10.4',\n      'aio_agent_version' => '1.10.4',\n      'aio_agent_build'   => '1.10.4',\n      'memory'           => {\n        'system'        => {\n          'total_bytes' => 8589934592,\n        }\n      },\n      'memorysize' => '8 GiB',\n      'osfamily' => 'RedHat',\n      'selinux' => false,\n      'operatingsystem' => 'CentOS',\n      'operatingsystemrelease' => '6.8',\n      'path' => '/tmp/staging/',\n      'processorcount' => '2',\n      'fake_domain' => 'pgtomcat.mycompany.org',\n      'function' => 'app',\n      'group' => 'pgtomcat',\n      'stage' => 'prod',\n      'staging_http_get' => 'curl',\n      'whereami' => 'portland',\n      'hostname' => 'pgtomcat',\n      'fqdn' => 'pgtomcat.mycompany.org',\n      'lsbdistcodename' => 'n/a',\n      'processor0' => 'AMD Ryzen 7 1700 Eight-Core Processor',\n    })\n\n    env = Puppet.lookup(:environments).get('perf_control')\n    node = Puppet::Node.indirection.find(\"testing\", :environment => env, :facts => facts)\n\n    Puppet.push_context({:current_environment => env}, 'current env for benchmark')\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n    Puppet.pop_context\n    Puppet.lookup(:environments).clear('perf_control')\n  end\n\n  def generate\n    check_submodule\n    templates = File.join('benchmarks', 'full_catalog')\n    source = File.join(templates, \"puppetlabs-puppetserver_perf_control\")\n    environment = File.join(@target, 'environments', 'perf_control')\n    mkdir_p(File.join(@target, 'environments'))\n    FileUtils.cp_r(source, environment)\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :codedir => File.join(@target, 'environments'),\n           :target => @target)\n\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => @size)\n\n    render(File.join(templates, 'hiera.yaml.erb'),\n           File.join(@target, 'hiera.yaml'),\n           :datadir => File.join(environment, 'hieradata'))\n\n    FileUtils.cp(File.join(templates, 'Gemfile'), @target)\n    FileUtils.cp(File.join(templates, 'r10k.yaml'), @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/full_catalog/description",
    "content": ""
  },
  {
    "path": "benchmarks/full_catalog/hiera.yaml.erb",
    "content": "---\n:backends:\n  - yaml\n:yaml:\n  :datadir: \"<%= datadir %>\"\n:hierarchy:\n# This hierarchy is based on the one from the puppetlabs ops team.\n  - \"nodes/%{fake_domain}/%{fqdn}\"\n  - \"nodes/%{fake_domain}/%{hostname}\"\n  - \"domains/%{fake_domain}/groups/%{group}\"\n  - \"domains/%{fake_domain}/stages/%{stage}\"\n  - \"domains/%{fake_domain}\"\n  - \"groups/%{group}/%{function}/%{stage}\"\n  - \"groups/%{group}/%{function}\"\n  - \"groups/%{group}/%{whereami}/%{stage}\"\n  - \"groups/%{group}/%{stage}\"\n  - \"groups/%{group}\"\n  - \"location/%{whereami}/%{stage}\"\n  - \"location/%{whereami}\"\n  - \"os/%{osfamily}/%{lsbdistcodename}\"\n  - \"os/%{osfamily}\"\n  - \"modules/%{module_name}\"\n  - \"stages/%{stage}\"\n  - \"common\"\n"
  },
  {
    "path": "benchmarks/full_catalog/puppet.conf.erb",
    "content": "environmentpath = <%= codedir %>\nconfdir = <%= target %>\n"
  },
  {
    "path": "benchmarks/full_catalog/r10k.yaml",
    "content": ":cachedir: '/tmp/full_catalog_r10k_cache'\n"
  },
  {
    "path": "benchmarks/full_catalog/site.pp.erb",
    "content": "node 'default' {\n  include ::role::by_size::<%= size %>\n}\n"
  },
  {
    "path": "benchmarks/function_loading/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n    @@benchmark_count ||= 0\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n  end\n\n  def run(args=nil)\n    envs = Puppet.lookup(:environments)\n\n    envs.clear('benchmarking')\n#    # Uncomment to get some basic memory statistics\n#    ObjectSpace.garbage_collect\n#    result = {}\n#    ObjectSpace.count_objects(result)\n#    puts \"C(#{result[:T_CLASS]}) O(#{result[:T_OBJECT]}) F(#{result[:FREE]}) #{result[:T_CLASS] + result[:T_OBJECT]}\"\n    node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n    Puppet::Resource::Catalog.indirection.find('testing', :use_node => node)\n  end\n\n  def benchmark_count\n    bc = @@benchmark_count\n    @@benchmark_count += 1\n    bc\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    modules = File.join(environment, 'modules')\n    env_functions = File.join(environment, 'lib', 'puppet', 'functions', 'environment')\n    templates = File.join('benchmarks', 'function_loading')\n\n    mkdir_p(modules)\n    mkdir_p(env_functions)\n    mkdir_p(File.join(environment, 'manifests'))\n\n    module_count = @size / 5\n    function_count = @size * 3\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => module_count,\n           :function_count => function_count)\n\n    env_function_template = File.join(templates, 'env_function.erb')\n    function_count.times { |n| render(env_function_template, File.join(env_functions, \"f#{n}.rb\"), :n => n) }\n\n    module_count.times do |i|\n      module_name = \"module#{i}\"\n      module_base = File.join(modules, module_name)\n      manifests = File.join(module_base, 'manifests')\n      module_functions = File.join(module_base, 'lib', 'puppet', 'functions', module_name)\n\n      mkdir_p(manifests)\n      mkdir_p(module_functions)\n\n      File.open(File.join(module_base, 'metadata.json'), 'w') do |f|\n        JSON.dump({\n          'name' => \"tester-#{module_name}\",\n          'author' => 'Puppet Labs tester',\n          'license' => 'Apache 2.0',\n          'version' => '1.0.0',\n          'summary' => 'Benchmark module',\n          'dependencies' => dependencies_for(i),\n          'source' => ''\n        }, f)\n      end\n\n      render(File.join(templates, 'module', 'init.pp.erb'),\n             File.join(manifests, 'init.pp'),\n             :name => module_name, :mc => i, :function_count => function_count)\n\n      function_template = File.join(templates, 'module', 'function.erb')\n      function_count.times do |n|\n        render(function_template,\n               File.join(module_functions, \"f#{n}.rb\"),\n               :name => module_name, :n => n)\n      end\n    end\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def dependencies_for(n)\n    return [] if n == 0\n    Array.new(n) do |m|\n      {'name' => \"tester-module#{m}\", 'version_requirement' => '1.0.0'}\n    end\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    site.filename = erb_file\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/function_loading/description",
    "content": "Benchmark scenario: many functions spread across many modules.\nBenchmark target: function loading and call overhead\n\nA number of modules are generated. The environment, and each module holds a large number of functions (several 100s).\nFuctions call each other and cause deep recursion (and on demand loading as they are called.\nCalls are made across modules, and to the environment.\n"
  },
  {
    "path": "benchmarks/function_loading/env_function.erb",
    "content": "Puppet::Functions.create_function(:'environment::f<%= n %>') do\n  def f<%= n %>\n    <% if n > 0 %>call_function(:'environment::f<%= n-1 %>')<% else %>'environment::f<%= n %>'<% end %>\n  end\nend\n"
  },
  {
    "path": "benchmarks/function_loading/module/function.erb",
    "content": "Puppet::Functions.create_function(:'<%= name %>::f<%= n %>') do\n  def f<%= n %>\n    '<%= name %>::f<%= n %>'\n  end\nend\n"
  },
  {
    "path": "benchmarks/function_loading/module/init.pp.erb",
    "content": "class <%= name %> {\n<% mc.times do |m| %>\n  $v<%= m %> = [\n  <% (function_count - 1).times do |n| %>    <%= name %>::f<%= n %>(),\n  <% end %>\n    module<%= m %>::f<%= function_count - 1 %>()\n  ]\n<% end %>\n}\n"
  },
  {
    "path": "benchmarks/function_loading/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\nparser = future\n"
  },
  {
    "path": "benchmarks/function_loading/site.pp.erb",
    "content": "environment::f<%= function_count() - 1 %>()\n<% size.times do |i| %>include module<%= i %>\n<% end %>\n\n"
  },
  {
    "path": "benchmarks/hiera_conf_interpol/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 100 ? size : 100\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    envs = Puppet.lookup(:environments)\n    @node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n  end\n\n  def run(args=nil)\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @compiler.compile do |catalog|\n      scope = @compiler.topscope\n      50.times { |index| scope[\"d#{index}\"] = \"dir_#{index}\" }\n      @size.times do\n        100.times do |index|\n          invocation = Puppet::Pops::Lookup::Invocation.new(scope)\n          Puppet::Pops::Lookup.lookup('a', nil, nil, true, nil, invocation)\n        end\n      end\n      catalog\n    end\n  end\n\n  def generate\n    env_dir = File.join(@target, 'environments', 'benchmarking')\n    hiera_yaml = File.join(@target, 'hiera.yaml')\n    datadir = File.join(@target, 'data')\n\n    mkdir_p(env_dir)\n    File.open(hiera_yaml, 'w') do |f|\n      f.puts('version: 5')\n      f.puts('hierarchy:')\n      50.times do |index|\n        5.times do |repeat|\n          f.puts(\"  - name: Entry_#{index}_#{repeat}\")\n          f.puts(\"    path: \\\"%{::d#{index}}/data_#{repeat}\\\"\")\n        end\n      end\n    end\n\n    dir_0_dir = File.join(datadir, 'dir_0')\n    mkdir_p(dir_0_dir)\n\n    data_0_yaml = File.join(dir_0_dir, 'data_0.yaml')\n    File.open(data_0_yaml, 'w') do |f|\n      f.puts('a: value a')\n    end\n\n    templates = File.join('benchmarks', 'hiera_conf_interpol')\n\n    render(File.join(templates, 'puppet.conf.erb'),\n      File.join(@target, 'puppet.conf'),\n      :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/hiera_conf_interpol/description",
    "content": "Benchmark scenario: Many lookups using a Hiera configuration with many interpolations\nBenchmark target: hiera lookup.\n"
  },
  {
    "path": "benchmarks/hiera_conf_interpol/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\ncodedir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/hiera_env_lookup/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 100 ? size : 100\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    envs = Puppet.lookup(:environments)\n    @node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n  end\n\n  def run(args=nil)\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @compiler.compile do |catalog|\n      scope = @compiler.topscope\n      scope['confdir'] = 'test'\n      @size.times do\n        100.times do |index|\n          invocation = Puppet::Pops::Lookup::Invocation.new(scope)\n          Puppet::Pops::Lookup.lookup(\"x#{index}\", nil, nil, true, nil, invocation)\n        end\n\n        100.times do\n          invocation = Puppet::Pops::Lookup::Invocation.new(scope)\n          Puppet::Pops::Lookup.lookup(\"h1.h2.h3.k0\", nil, nil, true, nil, invocation)\n        end\n      end\n      catalog\n    end\n  end\n\n  def generate\n    # $codedir/\n    #   environments/benchmarking/\n    #     hiera.yaml\n    #     data/\n    #       test/data.yaml\n    #       common.yaml\n    #\n    env_dir = File.join(@target, 'environments', 'benchmarking')\n    hiera_yaml = File.join(env_dir, 'hiera.yaml')\n    datadir = File.join(env_dir, 'data')\n    datadir_test = File.join(datadir, 'test')\n    test_data_yaml = File.join(datadir_test, 'data.yaml')\n    common_yaml = File.join(datadir, 'common.yaml')\n\n    mkdir_p(datadir_test)\n\n    File.open(hiera_yaml, 'w') do |f|\n      f.puts(<<-YAML)\nversion: 5\ndefaults:\n  datadir: data\n  data_hash: yaml_data\nhierarchy:\n  - name: Common\n    path: common.yaml\n  - name: Configured\n    path: test/data.yaml\nYAML\n    end\n\n    File.open(common_yaml, 'w') do |f|\n      100.times do |index|\n        f.puts(\"a#{index}: value a#{index}\")\n        f.puts(\"b#{index}: value b#{index}\")\n        f.puts(\"c#{index}: value c#{index}\")\n        f.puts(\"cbm#{index}: \\\"%{hiera('a#{index}')}, %{hiera('b#{index}')}, %{hiera('c#{index}')}\\\"\")\n      end\n    end\n\n    File.open(test_data_yaml, 'w') do |f|\n      100.times { |index| f.puts(\"x#{index}: \\\"%{hiera('cbm#{index}')}\\\"\")}\n\n      f.puts(<<-YAML)\nh1:\n  h2:\n    h3:\nYAML\n      100.times { |index| f.puts(<<-YAML) }\n      k#{index}: v#{index}\nYAML\n    end\n\n    templates = File.join('benchmarks', 'hiera_env_lookup')\n\n    render(File.join(templates, 'puppet.conf.erb'),\n      File.join(@target, 'puppet.conf'),\n      :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/hiera_env_lookup/description",
    "content": "Benchmark scenario: Many lookups using a Hiera 5 configuration in an environment\nBenchmark target: hiera lookup.\n"
  },
  {
    "path": "benchmarks/hiera_env_lookup/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\ncodedir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/hiera_function/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 100 ? size : 100\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    envs = Puppet.lookup(:environments)\n    @node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n  end\n\n  def run(args=nil)\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @compiler.compile do |catalog|\n      scope = @compiler.topscope\n      scope['confdir'] = 'test'\n      @size.times do\n        100.times do |index|\n          hiera_func = @compiler.loaders.puppet_system_loader.load(:function, 'hiera')\n          hiera_func.call(scope, \"x#{index}\")\n        end\n      end\n      catalog\n    end\n  end\n\n  def generate\n    env_dir = File.join(@target, 'environments', 'benchmarking')\n    hiera_yaml = File.join(@target, 'hiera.yaml')\n    datadir = File.join(@target, 'data')\n    datadir_test = File.join(datadir, 'test')\n    test_data_yaml = File.join(datadir_test, 'data.yaml')\n    common_yaml = File.join(datadir, 'common.yaml')\n\n    mkdir_p(env_dir)\n    mkdir_p(datadir)\n    mkdir_p(datadir_test)\n\n    File.open(hiera_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\n:backends: yaml\n:yaml:\n   :datadir: #{datadir}\n:hierarchy:\n   - \"%{confdir}/data\"\n   - common\n:logger: noop\n      YAML\n    end\n\n    File.open(common_yaml, 'w') do |f|\n      100.times do |index|\n        f.puts(\"a#{index}: value a#{index}\")\n        f.puts(\"b#{index}: value b#{index}\")\n        f.puts(\"c#{index}: value c#{index}\")\n        f.puts(\"cbm#{index}: \\\"%{hiera('a#{index}')}, %{hiera('b#{index}')}, %{hiera('c#{index}')}\\\"\")\n      end\n    end\n\n    File.open(test_data_yaml, 'w') do |f|\n      100.times { |index| f.puts(\"x#{index}: \\\"%{hiera('cbm#{index}')}\\\"\")}\n    end\n\n    templates = File.join('benchmarks', 'hiera_function')\n\n    render(File.join(templates, 'puppet.conf.erb'),\n      File.join(@target, 'puppet.conf'),\n      :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/hiera_function/description",
    "content": "Benchmark scenario: Many lookups using hiera function\nBenchmark target: hiera lookup.\n"
  },
  {
    "path": "benchmarks/hiera_function/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\ncodedir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/hiera_global_lookup/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 100 ? size : 100\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    envs = Puppet.lookup(:environments)\n    @node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n  end\n\n  def run(args=nil)\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @compiler.compile do |catalog|\n      scope = @compiler.topscope\n      scope['confdir'] = 'test'\n      @size.times do\n        100.times do |index|\n          invocation = Puppet::Pops::Lookup::Invocation.new(scope)\n          Puppet::Pops::Lookup.lookup(\"x#{index}\", nil, nil, true, nil, invocation)\n        end\n\n        100.times do\n          invocation = Puppet::Pops::Lookup::Invocation.new(scope)\n          Puppet::Pops::Lookup.lookup(\"h1.h2.h3.k0\", nil, nil, true, nil, invocation)\n        end\n      end\n      catalog\n    end\n  end\n\n  def generate\n    # $codedir/\n    #   environments/benchmarking/\n    #   hiera.yaml\n    #   data/\n    #     test/data.yaml\n    #     common.yaml\n    #\n    env_dir = File.join(@target, 'environments', 'benchmarking')\n    hiera_yaml = File.join(@target, 'hiera.yaml')\n    datadir = File.join(@target, 'data')\n    datadir_test = File.join(datadir, 'test')\n    test_data_yaml = File.join(datadir_test, 'data.yaml')\n    common_yaml = File.join(datadir, 'common.yaml')\n\n    mkdir_p(env_dir)\n    mkdir_p(datadir_test)\n\n    File.open(hiera_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\nversion: 5\ndefaults:\n  datadir: data\n  data_hash: yaml_data\nhierarchy:\n  - name: Configured\n    path: test/data.yaml\n  - name: Common\n    path: common.yaml\nYAML\n    end\n\n    File.open(common_yaml, 'w') do |f|\n      100.times do |index|\n        f.puts(\"a#{index}: value a#{index}\")\n        f.puts(\"b#{index}: value b#{index}\")\n        f.puts(\"c#{index}: value c#{index}\")\n        f.puts(\"cbm#{index}: \\\"%{hiera('a#{index}')}, %{hiera('b#{index}')}, %{hiera('c#{index}')}\\\"\")\n      end\n    end\n\n    File.open(test_data_yaml, 'w') do |f|\n      100.times { |index| f.puts(\"x#{index}: \\\"%{hiera('cbm#{index}')}\\\"\")}\n\n      f.puts(<<-YAML)\nh1:\n  h2:\n    h3:\nYAML\n      100.times { |index| f.puts(<<-YAML) }\n      k#{index}: v#{index}\nYAML\n    end\n\n    templates = File.join('benchmarks', 'hiera_global_lookup')\n\n    render(File.join(templates, 'puppet.conf.erb'),\n      File.join(@target, 'puppet.conf'),\n      :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/hiera_global_lookup/description",
    "content": "Benchmark scenario: Many lookups using a global Hiera 3 configuration\nBenchmark target: hiera lookup.\n"
  },
  {
    "path": "benchmarks/hiera_global_lookup/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\ncodedir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/hiera_include/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 100 ? size : 100\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    envs = Puppet.lookup(:environments)\n    @node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n  end\n\n  def run(args=nil)\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @compiler.compile do |catalog|\n      scope = @compiler.topscope\n      scope['confdir'] = 'test'\n      @size.times do\n        100.times do |index|\n          hiera_func = @compiler.loaders.puppet_system_loader.load(:function, 'hiera_include')\n          hiera_func.call(scope, 'common_entry')\n        end\n      end\n      catalog\n    end\n  end\n\n  def generate\n    env_dir = File.join(@target, 'environments', 'benchmarking')\n    manifests_dir = File.join(env_dir, 'manifests')\n    dummy_class_manifest = File.join(manifests_dir, 'foo.pp')\n    hiera_yaml = File.join(@target, 'hiera.yaml')\n    datadir = File.join(@target, 'data')\n    common_yaml = File.join(datadir, 'common.yaml')\n    groups_yaml = File.join(datadir, 'groups.yaml')\n\n    mkdir_p(env_dir)\n    mkdir_p(manifests_dir)\n    mkdir_p(datadir)\n\n    File.open(hiera_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\n:backends: yaml\n:yaml:\n   :datadir: #{datadir}\n:hierarchy:\n   - common\n   - groups\n:logger: noop\n      YAML\n    end\n\n    File.open(groups_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\npuppet:\n  staff:\n    groups:\n      YAML\n\n      0.upto(50).each do |i|\n        f.puts(\"      group#{i}:\")\n        0.upto(125).each do |j|\n          f.puts(\"        - user#{j}\")\n        end\n      end\n    end\n\n    File.open(dummy_class_manifest, 'w') do |f|\n      f.puts(\"class dummy_class { }\")\n    end\n\n    File.open(common_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n        common_entry:\n          - dummy_class\n      YAML\n    end\n\n    templates = File.dirname(File.realpath(__FILE__))\n\n    render(File.join(templates, 'puppet.conf.erb'),\n      File.join(@target, 'puppet.conf'),\n      :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/hiera_include/description",
    "content": "Benchmark scenario: Large nested hierarchy dataset\nBenchmark target: hiera include.\n"
  },
  {
    "path": "benchmarks/hiera_include/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\ncodedir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/hiera_include_one/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 1000 ? size : 1000\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    envs = Puppet.lookup(:environments)\n    @node = Puppet::Node.new('testing', :environment => envs.get('benchmarking'))\n  end\n\n  def run(args=nil)\n    @size.times do\n      @compiler = Puppet::Parser::Compiler.new(@node)\n      @compiler.compile do |catalog|\n        scope = @compiler.topscope\n        scope['confdir'] = 'test'\n          hiera_func = @compiler.loaders.puppet_system_loader.load(:function, 'hiera_include')\n          hiera_func.call(scope, 'common_entry')\n        catalog\n      end\n    end\n  end\n\n  def generate\n    env_dir = File.join(@target, 'environments', 'benchmarking')\n    manifests_dir = File.join(env_dir, 'manifests')\n    dummy_class_manifest = File.join(manifests_dir, 'foo.pp')\n    hiera_yaml = File.join(@target, 'hiera.yaml')\n    datadir = File.join(@target, 'data')\n    common_yaml = File.join(datadir, 'common.yaml')\n    groups_yaml = File.join(datadir, 'groups.yaml')\n\n    mkdir_p(env_dir)\n    mkdir_p(manifests_dir)\n    mkdir_p(datadir)\n\n    File.open(hiera_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\n:backends: yaml\n:yaml:\n   :datadir: #{datadir}\n:hierarchy:\n   - common\n   - groups\n:logger: noop\n      YAML\n    end\n\n    File.open(groups_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\npuppet:\n  staff:\n    groups:\n      YAML\n\n      0.upto(50).each do |i|\n        f.puts(\"      group#{i}:\")\n        0.upto(125).each do |j|\n          f.puts(\"        - user#{j}\")\n        end\n      end\n    end\n\n    File.open(dummy_class_manifest, 'w') do |f|\n      f.puts(\"class dummy_class { }\")\n    end\n\n    File.open(common_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n        common_entry:\n          - dummy_class\n      YAML\n    end\n\n    templates = File.dirname(File.realpath(__FILE__))\n\n    render(File.join(templates, 'puppet.conf.erb'),\n      File.join(@target, 'puppet.conf'),\n      :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/hiera_include_one/description",
    "content": "Benchmark scenario: Large nested hierarchy dataset, many compilations, one lookup per compile\nBenchmark target: hiera include.\n"
  },
  {
    "path": "benchmarks/hiera_include_one/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\ncodedir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = 0\n"
  },
  {
    "path": "benchmarks/legacy_hiera_lookup/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @hiera_yaml = File.join(target, 'hiera.yaml')\n    @size = size > 100 ? size : 100\n  end\n\n  def setup\n    require 'puppet'\n    require 'hiera'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n    Hiera.logger = 'noop'\n    @hiera = ::Hiera.new(:config => @hiera_yaml)\n  end\n\n  def run(args=nil)\n    @size.times do\n      100.times do |index|\n        @hiera.lookup(\"x#{index}\", nil, { 'confdir' => 'test' })\n      end\n    end\n  end\n\n  def generate\n    datadir = File.join(@target, 'data')\n    datadir_test = File.join(datadir, 'test')\n    test_data_yaml = File.join(datadir_test, 'data.yaml')\n    common_yaml = File.join(datadir, 'common.yaml')\n\n    mkdir_p(datadir)\n    mkdir_p(datadir_test)\n\n    File.open(@hiera_yaml, 'w') do |f|\n      f.puts(<<-YAML)\n---\n:backends: yaml\n:yaml:\n   :datadir: #{datadir}\n:hierarchy:\n   - \"%{confdir}/data\"\n   - common\n:logger: noop\n      YAML\n    end\n\n    File.open(common_yaml, 'w') do |f|\n      100.times do |index|\n        f.puts(\"a#{index}: value a#{index}\")\n        f.puts(\"b#{index}: value b#{index}\")\n        f.puts(\"c#{index}: value c#{index}\")\n        f.puts(\"cbm#{index}: \\\"%{hiera('a#{index}')}, %{hiera('b#{index}')}, %{hiera('c#{index}')}\\\"\")\n      end\n    end\n\n    File.open(test_data_yaml, 'w') do |f|\n      100.times { |index| f.puts(\"x#{index}: \\\"%{hiera('cbm#{index}')}\\\"\")}\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/legacy_hiera_lookup/description",
    "content": "Benchmark scenario: Lookups that uses raw Hiera 3 (the legacy implementation)\nBenchmark target: legacy hiera lookup.\n"
  },
  {
    "path": "benchmarks/many_environments/benchmarker.rb",
    "content": "require 'fileutils'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size > 1000 ? size : 1000\n  end\n\n  def setup\n    require 'puppet'\n    @config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', @config])\n  end\n\n  def run(args=nil)\n    Puppet.settings.clear\n    environment_loaders = Puppet.lookup(:environments)\n    environment_loaders.clear_all\n    environment_loaders.get!(\"anenv#{@size/2}\")\n  end\n\n  def generate\n    environments = File.join(@target, 'environments')\n    puppet_conf = File.join(@target, 'puppet.conf')\n\n    File.open(puppet_conf, 'w') do |f|\n      f.puts(<<-EOF)\n        environmentpath=#{environments}\n      EOF\n    end\n\n    @size.times do |i|\n      mkdir_p(File.join(environments, \"anenv#{i}\"))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/many_environments/description",
    "content": "Benchmark scenario: many directory environments.\nBenchmark target: environment lookup.\n"
  },
  {
    "path": "benchmarks/many_modules/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n  end\n\n  def run(args=nil)\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'many_modules')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => @size)\n\n    @size.times do |i|\n      module_name = \"module#{i}\"\n      module_base = File.join(environment, 'modules', module_name)\n      manifests = File.join(module_base, 'manifests')\n      locales = File.join(module_base, 'locales')\n\n      mkdir_p(manifests)\n      mkdir_p(locales)\n\n      File.open(File.join(module_base, 'metadata.json'), 'w') do |f|\n        JSON.dump({\n          \"name\" => \"module#{i}\",\n          \"types\" => [],\n          \"source\" => \"\",\n          \"author\" => \"ManyModules Benchmark\",\n          \"license\" => \"Apache 2.0\",\n          \"version\" => \"1.0.0\",\n          \"description\" => \"Many Modules benchmark module #{i}\",\n          \"summary\" => \"Just this benchmark module, you know?\",\n          \"dependencies\" => [],\n        }, f)\n      end\n\n      File.open(File.join(locales, 'config.yaml'), 'w') do |f|\n        f.puts(\n          {\"gettext\"=>\n            {\"project_name\"=>\"module#{i}\",\n            \"package_name\"=>\"module#{i}\",\n            \"default_locale\"=>\"en\",\n            \"bugs_address\"=>\"docs@puppet.com\",\n            \"copyright_holder\"=>\"Puppet, Inc.\",\n            \"comments_tag\"=>\"TRANSLATOR\",\n            \"source_files\"=>[\"./lib/**/*.rb\"]}}.to_yaml\n          )\n      end\n\n      roles = 0.upto(10).to_a\n\n      render(File.join(templates, 'module', 'init.pp.erb'),\n             File.join(manifests, 'init.pp'),\n             :name => module_name,\n             :roles => roles\n            )\n\n      render(File.join(templates, 'module', 'internal.pp.erb'),\n             File.join(manifests, 'internal.pp'),\n             :name => module_name)\n\n      roles.each do |j|\n        render(File.join(templates, 'module', 'role.pp.erb'),\n               File.join(manifests, \"role#{j}.pp\"),\n               :name => module_name,\n               :index => j)\n      end\n    end\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/many_modules/description",
    "content": "Benchmark scenario: many manifests spread across many modules.\nBenchmark target: catalog compilation.\n\n"
  },
  {
    "path": "benchmarks/many_modules/module/init.pp.erb",
    "content": "class <%= name %> {\n  class { \"<%= name %>::internal\": }\n  <% roles.each do |role| %>\n    class { \"<%= name %>::role<%= role %>\": }\n  <% end %>\n}\n"
  },
  {
    "path": "benchmarks/many_modules/module/internal.pp.erb",
    "content": "class <%= name %>::internal {\n  notify { \"<%= name %>::internal\": }\n}\n"
  },
  {
    "path": "benchmarks/many_modules/module/role.pp.erb",
    "content": "class <%= name %>::role<%= index %> {\n  notify { \"<%= name %>::role<%= index %>\": }\n}\n"
  },
  {
    "path": "benchmarks/many_modules/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\n"
  },
  {
    "path": "benchmarks/many_modules/site.pp.erb",
    "content": "<% size.times do |i| %>\n  include module<%= i %>\n<% end %>\n"
  },
  {
    "path": "benchmarks/missing_type_caching/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n    Puppet[:always_retry_plugins] = false\n  end\n\n  def run(args=nil)\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'missing_type_caching')\n    test_module_dir = File.join(environment, 'modules', 'testmodule',\n                                'manifests')\n\n    mkdir_p(File.join(environment, 'modules'))\n    @size.times.each do |i|\n      mkdir_p(File.join(environment, 'modules', \"mymodule_#{i}\", 'lib',\n                        'puppet', 'type'))\n      mkdir_p(File.join(environment, 'modules', \"mymodule_#{i}\", 'manifests'))\n    end\n    mkdir_p(File.join(environment, 'manifests'))\n\n    mkdir_p(test_module_dir)\n\n\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => @size)\n\n    render(File.join(templates, 'module', 'testmodule.pp.erb'),\n           File.join(test_module_dir, 'init.pp'),\n           :name => \"foo\")\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/missing_type_caching/description",
    "content": "Benchmark scenario: heavy use of defined types found in init.pp\nBenchmark target: catalog compilation\n\n"
  },
  {
    "path": "benchmarks/missing_type_caching/module/testmodule.pp.erb",
    "content": "define testmodule {\n  notify { \"in <%= name %>: $title\": }\n}\n"
  },
  {
    "path": "benchmarks/missing_type_caching/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\n"
  },
  {
    "path": "benchmarks/missing_type_caching/site.pp.erb",
    "content": "if true {\n<% size.times do |i| %>\n  testmodule { \"foo<%= i %>\": }\n<% end %>\n}\n"
  },
  {
    "path": "benchmarks/serialization/benchmarker.rb",
    "content": "require 'puppet'\n\nclass Benchmarker\n  def initialize(target, size)\n    @size = size\n    @direction = ENV['SER_DIRECTION'] == 'generate' ? :generate : :parse\n    @format = ENV['SER_FORMAT'] == 'pson' ? :pson : :json\n\n    puts \"Benchmarker #{@direction} #{@format}\"\n  end\n\n  def setup\n  end\n\n  def generate\n    path = File.expand_path(File.join(__FILE__, '../catalog.json'))\n    puts \"Using catalog #{path}\"\n\n    @data = File.read(path)\n    @catalog = JSON.parse(@data)\n  end\n\n  def run(args=nil)\n    0.upto(@size) do |i|\n      # This parses a catalog from JSON data, which is a combination of parsing\n      # the data into a JSON hash, and the parsing the hash into a Catalog. It's\n      # interesting to see just how slow that latter process is:\n      #\n      #   Puppet::Resource::Catalog.convert_from(:json, @data)\n      #\n      # However, for this benchmark, we're just testing how long JSON vs PSON\n      # parsing and generation are, where we default to parsing JSON.\n      #\n      if @direction == :generate\n        if @format == :pson\n          PSON.dump(@catalog)\n        else\n          JSON.dump(@catalog)\n        end\n      else\n        if @format == :pson\n          PSON.parse(@data)\n        else\n          JSON.parse(@data)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/serialization/catalog.json",
    "content": "{\n  \"tags\": [\n    \"pe_repo\",\n    \"pe_repo::platform::el_7_x86_64\",\n    \"platform\",\n    \"el_7_x86_64\",\n    \"puppet_enterprise\",\n    \"puppet_enterprise::license\",\n    \"license\",\n    \"puppet_enterprise::profile::agent\",\n    \"profile\",\n    \"agent\",\n    \"puppet_enterprise::profile::amq::broker\",\n    \"amq\",\n    \"broker\",\n    \"puppet_enterprise::profile::certificate_authority\",\n    \"certificate_authority\",\n    \"puppet_enterprise::profile::console\",\n    \"console\",\n    \"puppet_enterprise::profile::master\",\n    \"master\",\n    \"puppet_enterprise::profile::master::mcollective\",\n    \"mcollective\",\n    \"puppet_enterprise::profile::mcollective::agent\",\n    \"puppet_enterprise::profile::mcollective::peadmin\",\n    \"peadmin\",\n    \"puppet_enterprise::profile::orchestrator\",\n    \"orchestrator\",\n    \"puppet_enterprise::profile::puppetdb\",\n    \"puppetdb\",\n    \"settings\",\n    \"default\",\n    \"puppet_enterprise::params\",\n    \"params\",\n    \"puppet_enterprise::symlinks\",\n    \"symlinks\",\n    \"puppet_enterprise::pxp_agent\",\n    \"pxp_agent\",\n    \"puppet_enterprise::pxp_agent::service\",\n    \"service\",\n    \"puppet_enterprise::amq\",\n    \"puppet_enterprise::packages\",\n    \"packages\",\n    \"puppet_enterprise::repo\",\n    \"repo\",\n    \"puppet_enterprise::stages\",\n    \"stages\",\n    \"puppet_enterprise::repo::config\",\n    \"config\",\n    \"puppet_enterprise::amq::config\",\n    \"puppet_enterprise::amq::service\",\n    \"puppet_enterprise::amq::certs\",\n    \"certs\",\n    \"puppet_enterprise::profile::master::classifier\",\n    \"classifier\",\n    \"puppet_enterprise::profile::master::auth_conf\",\n    \"auth_conf\",\n    \"puppet_enterprise::master::tk_authz\",\n    \"tk_authz\",\n    \"puppet_enterprise::profile::master::puppetdb\",\n    \"puppet_enterprise::master\",\n    \"puppet_enterprise::master::puppetserver\",\n    \"puppetserver\",\n    \"pe_r10k::package\",\n    \"pe_r10k\",\n    \"package\",\n    \"puppet_enterprise::master::file_sync_disabled\",\n    \"file_sync_disabled\",\n    \"puppet_enterprise::profile::controller\",\n    \"controller\",\n    \"puppet_enterprise::cli_config\",\n    \"cli_config\",\n    \"puppet_enterprise::profile::console::certs\",\n    \"puppet_enterprise::profile::console::console_services_config\",\n    \"console_services_config\",\n    \"puppet_enterprise::console_services\",\n    \"console_services\",\n    \"puppet_enterprise::profile::console::proxy\",\n    \"proxy\",\n    \"pe_nginx\",\n    \"pe_nginx::params\",\n    \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n    \"nginx_conf\",\n    \"puppet_enterprise::profile::console::proxy::http_redirect\",\n    \"http_redirect\",\n    \"puppet_enterprise::mcollective::service\",\n    \"puppet_enterprise::mcollective::server\",\n    \"server\",\n    \"puppet_enterprise::mcollective::server::plugins\",\n    \"plugins\",\n    \"puppet_enterprise::mcollective::server::logs\",\n    \"logs\",\n    \"puppet_enterprise::mcollective::server::certs\",\n    \"puppet_enterprise::mcollective::server::facter\",\n    \"facter\",\n    \"puppet_enterprise::mcollective::cleanup\",\n    \"cleanup\",\n    \"puppet_enterprise::puppetdb\",\n    \"puppet_enterprise::puppetdb::database_ini\",\n    \"database_ini\",\n    \"puppet_enterprise::puppetdb::jetty_ini\",\n    \"jetty_ini\",\n    \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n    \"rbac_consumer_conf\",\n    \"puppet_enterprise::puppetdb::config_ini\",\n    \"config_ini\",\n    \"puppet_enterprise::puppetdb::service\",\n    \"puppet_enterprise::certs::puppetdb_whitelist\",\n    \"puppetdb_whitelist\",\n    \"pe_concat::setup\",\n    \"pe_concat\",\n    \"setup\",\n    \"pe_staging\",\n    \"node\",\n    \"class\"\n  ],\n  \"name\": \"agent.demo.com\",\n  \"version\": 1492108311,\n  \"code_id\": null,\n  \"catalog_uuid\": \"c85cdf7e-f56d-4fc7-b513-3a00532cee91\",\n  \"catalog_format\": 2,\n  \"environment\": \"production\",\n  \"resources\": [\n    {\n      \"type\": \"Stage\",\n      \"title\": \"main\",\n      \"tags\": [\n        \"stage\",\n        \"main\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Settings\",\n      \"tags\": [\n        \"class\",\n        \"settings\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Main\",\n      \"tags\": [\n        \"class\",\n        \"main\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Node\",\n      \"title\": \"default\",\n      \"tags\": [\n        \"node\",\n        \"default\",\n        \"class\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Params\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::params\",\n        \"puppet_enterprise\",\n        \"params\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"certificate_authority_host\": \"agent.demo.com\",\n        \"console_host\": \"agent.demo.com\",\n        \"database_host\": \"agent.demo.com\",\n        \"mcollective_middleware_hosts\": [\n          \"agent.demo.com\"\n        ],\n        \"pcp_broker_host\": \"agent.demo.com\",\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppet_master_host\": \"agent.demo.com\",\n        \"certificate_authority_port\": 8140,\n        \"puppet_master_port\": 8140,\n        \"console_port\": 443,\n        \"api_port\": \"4433\",\n        \"puppetdb_port\": 8081,\n        \"database_port\": 5432,\n        \"puppetdb_database_name\": \"pe-puppetdb\",\n        \"puppetdb_database_user\": \"pe-puppetdb\",\n        \"classifier_database_name\": \"pe-classifier\",\n        \"classifier_database_super_user\": \"pe-classifier\",\n        \"classifier_database_read_user\": \"pe-classifier-read\",\n        \"classifier_database_write_user\": \"pe-classifier-write\",\n        \"classifier_service_regular_db_user\": \"pe-classifier-write\",\n        \"classifier_service_migration_db_user\": \"pe-classifier\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"activity_database_name\": \"pe-activity\",\n        \"activity_database_super_user\": \"pe-activity\",\n        \"activity_database_read_user\": \"pe-activity-read\",\n        \"activity_database_write_user\": \"pe-activity-write\",\n        \"activity_service_migration_db_user\": \"pe-activity\",\n        \"activity_service_regular_db_user\": \"pe-activity-write\",\n        \"activity_url_prefix\": \"\\/activity-api\",\n        \"rbac_database_name\": \"pe-rbac\",\n        \"rbac_database_super_user\": \"pe-rbac\",\n        \"rbac_database_read_user\": \"pe-rbac-read\",\n        \"rbac_database_write_user\": \"pe-rbac-write\",\n        \"rbac_service_migration_db_user\": \"pe-rbac\",\n        \"rbac_service_regular_db_user\": \"pe-rbac-write\",\n        \"rbac_url_prefix\": \"\\/rbac-api\",\n        \"orchestrator_url_prefix\": \"\\/orchestrator\",\n        \"orchestrator_database_name\": \"pe-orchestrator\",\n        \"orchestrator_database_super_user\": \"pe-orchestrator\",\n        \"orchestrator_database_read_user\": \"pe-orchestrator-read\",\n        \"orchestrator_database_write_user\": \"pe-orchestrator-write\",\n        \"orchestrator_service_migration_db_user\": \"pe-orchestrator\",\n        \"orchestrator_service_regular_db_user\": \"pe-orchestrator-write\",\n        \"orchestrator_port\": 8143,\n        \"pglogical_keepalives_idle\": 30,\n        \"pglogical_keepalives_count\": 2,\n        \"database_ssl\": true,\n        \"database_cert_auth\": true,\n        \"license_key_path\": \"\\/etc\\/puppetlabs\\/license.key\",\n        \"send_analytics_data\": true,\n        \"mcollective_middleware_port\": 61613,\n        \"mcollective_middleware_user\": \"mcollective\",\n        \"mcollective_middleware_password\": \"password123\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"manage_symlinks\": true,\n        \"pcp_broker_port\": 8142,\n        \"puppetdb_sync_interval_minutes\": 2,\n        \"ha_enabled_replicas\": [\n          \n        ]\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_repo\",\n      \"tags\": [\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"base_path\": \"https:\\/\\/pm.puppetlabs.com\\/puppet-agent\",\n        \"repo_dir\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\",\n        \"prefix\": \"\\/packages\",\n        \"master\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_repo::Platform::El_7_x86_64\",\n      \"tags\": [\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"pe_repo\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"agent_version\": \"1.10.0\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::License\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::license\",\n        \"puppet_enterprise\",\n        \"license\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Agent\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::agent\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"manage_symlinks\": true,\n        \"pxp_enabled\": true,\n        \"pcp_broker_host\": \"agent.demo.com\",\n        \"pcp_broker_port\": 8142,\n        \"manage_puppet_conf\": false,\n        \"server_list\": [\n          \n        ],\n        \"pcp_broker_ws_uris\": [\n          \n        ],\n        \"pcp_broker_list\": [\n          \n        ],\n        \"package_inventory_enabled\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Amq::Broker\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"amq\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"decrease_network_consumer_priority\": true,\n        \"duplex\": false,\n        \"dynamic_only\": true,\n        \"excluded_collectives\": [\n          \n        ],\n        \"heap_mb\": \"512\",\n        \"included_collectives\": [\n          \n        ],\n        \"keystore_password\": \"password123\",\n        \"truststore_password\": \"password123\",\n        \"network_ttl\": \"2\",\n        \"openwire_protocol\": \"ssl\",\n        \"openwire_port\": 61616,\n        \"openwire_transport_options\": {\n          \"transport.enabledProtocols\": \"TLSv1,TLSv1.1,TLSv1.2\"\n        },\n        \"queue_conduit_subscriptions\": false,\n        \"stomp_protocol\": \"stomp+ssl\",\n        \"stomp_transport_options\": {\n          \"transport.enabledProtocols\": \"TLSv1,TLSv1.1,TLSv1.2\"\n        },\n        \"stomp_port\": 61613,\n        \"stomp_user\": \"mcollective\",\n        \"stomp_password\": \"password123\",\n        \"topic_conduit_subscriptions\": true,\n        \"enable_web_console\": false,\n        \"store_usage\": \"1gb\",\n        \"temp_usage\": \"1gb\",\n        \"activemq_hubname\": \"agent.demo.com\",\n        \"use_dedicated_task_runner\": false,\n        \"enable_gc_logging\": true,\n        \"java_args\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Master\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"ca_host\": \"agent.demo.com\",\n        \"ca_port\": 8140,\n        \"certname\": \"agent.demo.com\",\n        \"classifier_client_certname\": \"agent.demo.com\",\n        \"classifier_host\": \"agent.demo.com\",\n        \"classifier_port\": \"4433\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"console_client_certname\": \"agent.demo.com\",\n        \"console_host\": \"agent.demo.com\",\n        \"console_server_certname\": \"agent.demo.com\",\n        \"orchestrator_client_certname\": \"agent.demo.com\",\n        \"master_of_masters_certname\": \"agent.demo.com\",\n        \"enable_ca_proxy\": true,\n        \"facts_terminus\": \"puppetdb\",\n        \"java_args\": {\n          \"Xmx\": \"2048m\",\n          \"Xms\": \"2048m\"\n        },\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"manage_symlinks\": true,\n        \"metrics_graphite_enabled\": false,\n        \"metrics_graphite_host\": \"graphite\",\n        \"metrics_graphite_port\": 2003,\n        \"metrics_graphite_update_interval_seconds\": 60,\n        \"metrics_jmx_enabled\": true,\n        \"metrics_server_id\": \"peglisan-latest\",\n        \"profiler_enabled\": true,\n        \"file_sync_enabled\": \"automatic\",\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppetdb_port\": 8081,\n        \"code_manager_auto_configure\": false,\n        \"check_for_updates\": true,\n        \"send_analytics_data\": true,\n        \"replication_mode\": \"none\",\n        \"provisioned_replicas\": [\n          \n        ],\n        \"ssl_listen_port\": 8140\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Certificate_authority\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"client_whitelist\": [\n          \n        ]\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Console\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"ca_host\": \"agent.demo.com\",\n        \"certname\": \"agent.demo.com\",\n        \"database_host\": \"agent.demo.com\",\n        \"database_port\": 5432,\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"master_host\": \"agent.demo.com\",\n        \"master_port\": 8140,\n        \"master_certname\": \"agent.demo.com\",\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppetdb_port\": 8081,\n        \"listen_address\": \"127.0.0.1\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"console_ssl_listen_port\": 443,\n        \"console_services_listen_port\": \"4430\",\n        \"console_services_ssl_listen_port\": \"4431\",\n        \"console_services_api_listen_port\": \"4432\",\n        \"console_services_api_ssl_listen_port\": \"4433\",\n        \"console_services_plaintext_status_enabled\": false,\n        \"console_services_plaintext_status_port\": 8123,\n        \"activity_url_prefix\": \"\\/activity-api\",\n        \"activity_database_name\": \"pe-activity\",\n        \"activity_database_migration_user\": \"pe-activity\",\n        \"activity_database_user\": \"pe-activity-write\",\n        \"classifier_database_name\": \"pe-classifier\",\n        \"classifier_database_migration_user\": \"pe-classifier\",\n        \"classifier_database_user\": \"pe-classifier-write\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"classifier_synchronization_period\": 600,\n        \"classifier_prune_threshold\": 7,\n        \"classifier_node_check_in_storage\": false,\n        \"rbac_database_name\": \"pe-rbac\",\n        \"rbac_database_migration_user\": \"pe-rbac\",\n        \"rbac_database_user\": \"pe-rbac-write\",\n        \"rbac_url_prefix\": \"\\/rbac-api\",\n        \"rbac_token_maximum_lifetime\": \"10y\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"delayed_job_workers\": 2,\n        \"disable_live_management\": true,\n        \"migrate_db\": false,\n        \"whitelisted_certnames\": [\n          \n        ],\n        \"java_args\": {\n          \"Xmx\": \"256m\",\n          \"Xms\": \"256m\"\n        },\n        \"proxy_read_timeout\": 120,\n        \"pcp_timeout\": 5,\n        \"display_local_time\": false,\n        \"send_analytics_data\": true,\n        \"replication_mode\": \"none\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Master::Mcollective\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Mcollective::Agent\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"mcollective\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"activemq_brokers\": [\n          \"agent.demo.com\"\n        ],\n        \"allow_no_actionpolicy\": \"1\",\n        \"collectives\": [\n          \"mcollective\"\n        ],\n        \"main_collective\": \"mcollective\",\n        \"manage_metadata_cron\": true,\n        \"mco_fact_cache_time\": 300,\n        \"mco_identity\": \"agent.demo.com\",\n        \"mco_loglevel\": \"info\",\n        \"mco_registerinterval\": 600,\n        \"randomize_activemq\": false,\n        \"stomp_password\": \"password123\",\n        \"stomp_port\": 61613,\n        \"stomp_user\": \"mcollective\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Mcollective::Peadmin\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"mcollective\",\n        \"peadmin\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"activemq_brokers\": [\n          \"agent.demo.com\"\n        ],\n        \"collectives\": [\n          \"mcollective\"\n        ],\n        \"create_user\": true,\n        \"home_dir\": \"\\/var\\/lib\\/peadmin\",\n        \"mco_loglevel\": \"info\",\n        \"main_collective\": \"mcollective\",\n        \"randomize_activemq\": false,\n        \"stomp_password\": \"password123\",\n        \"stomp_port\": 61613,\n        \"stomp_user\": \"mcollective\",\n        \"manage_symlinks\": true\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Orchestrator\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"database_host\": \"agent.demo.com\",\n        \"database_port\": 5432,\n        \"database_name\": \"pe-orchestrator\",\n        \"database_user\": \"pe-orchestrator-write\",\n        \"database_migration_user\": \"pe-orchestrator\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8143,\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"master_host\": \"agent.demo.com\",\n        \"master_port\": 8140,\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppetdb_port\": 8081,\n        \"classifier_host\": \"agent.demo.com\",\n        \"classifier_port\": \"4433\",\n        \"classifier_prefix\": \"\\/classifier-api\",\n        \"rbac_host\": \"agent.demo.com\",\n        \"rbac_port\": \"4433\",\n        \"rbac_prefix\": \"\\/rbac-api\",\n        \"activity_host\": \"agent.demo.com\",\n        \"activity_port\": \"4433\",\n        \"activity_prefix\": \"\\/activity-api\",\n        \"console_host\": \"agent.demo.com\",\n        \"console_services_host\": \"agent.demo.com\",\n        \"console_services_port\": \"4433\",\n        \"pcp_listen_port\": 8142,\n        \"pcp_accept_consumers\": 4,\n        \"pcp_delivery_consumers\": 4,\n        \"run_service\": true,\n        \"java_args\": {\n          \"Xmx\": \"192m\",\n          \"Xms\": \"192m\"\n        }\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Puppetdb\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"database_host\": \"agent.demo.com\",\n        \"master_certname\": \"agent.demo.com\",\n        \"whitelisted_certnames\": [\n          \n        ],\n        \"certname\": \"agent.demo.com\",\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"database_name\": \"pe-puppetdb\",\n        \"database_port\": 5432,\n        \"database_user\": \"pe-puppetdb\",\n        \"gc_interval\": \"60\",\n        \"node_purge_ttl\": \"0s\",\n        \"node_ttl\": \"7d\",\n        \"report_ttl\": \"14d\",\n        \"listen_address\": \"127.0.0.1\",\n        \"listen_port\": \"8080\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8081,\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"java_args\": {\n          \"Xmx\": \"256m\",\n          \"Xms\": \"256m\"\n        },\n        \"sync_whitelist\": [\n          \n        ],\n        \"rbac_host\": \"agent.demo.com\",\n        \"rbac_port\": \"4433\",\n        \"rbac_prefix\": \"\\/rbac-api\"\n      }\n    },\n    {\n      \"type\": \"Pe_anchor\",\n      \"title\": \"puppet_enterprise:barrier:ca\",\n      \"tags\": [\n        \"pe_anchor\",\n        \"puppet_enterprise:barrier:ca\",\n        \"class\",\n        \"puppet_enterprise\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/init.pp\",\n      \"line\": 228,\n      \"exported\": false\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"create repo_dir\",\n      \"tags\": [\n        \"exec\",\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/init.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"mkdir -p \\/opt\\/puppetlabs\\/server\\/data\\/packages\",\n        \"creates\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\",\n        \"path\": \"\\/sbin\\/:\\/bin\\/\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/init.pp\",\n      \"line\": 51,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"require\": \"Exec[create repo_dir]\",\n        \"backup\": false,\n        \"mode\": \"644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/init.pp\",\n      \"line\": 51,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"require\": \"Exec[create repo_dir]\",\n        \"mode\": \"0755\",\n        \"backup\": false,\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/current\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/init.pp\",\n      \"line\": 60,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\",\n        \"backup\": false,\n        \"mode\": \"644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/GPG-KEY-puppetlabs\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/init.pp\",\n      \"line\": 66,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"puppet:\\/\\/\\/modules\\/pe_repo\\/GPG-KEY-puppetlabs\",\n        \"backup\": false,\n        \"ensure\": \"file\",\n        \"mode\": \"644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/GPG-KEY-puppet\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_repo\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/init.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"puppet:\\/\\/\\/modules\\/pe_repo\\/GPG-KEY-puppet\",\n        \"backup\": false,\n        \"ensure\": \"file\",\n        \"mode\": \"644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Pe_repo::El\",\n      \"title\": \"el-7-x86_64\",\n      \"tags\": [\n        \"pe_repo::el\",\n        \"pe_repo\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/platform\\/el_7_x86_64.pp\",\n      \"line\": 6,\n      \"exported\": false,\n      \"parameters\": {\n        \"agent_version\": \"1.10.0\",\n        \"pe_version\": \"2017.2.0-rc1-424-ge1372a3\",\n        \"installer_build\": \"el-7-x86_64\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Symlinks\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"manage_symlinks\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\",\n      \"tags\": [\n        \"file\",\n        \"pe-agent-symlinks\",\n        \"pe-mco-symlinks\",\n        \"pe-master-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 31,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"replace\": false,\n        \"tag\": [\n          \"pe-agent-symlinks\",\n          \"pe-mco-symlinks\",\n          \"pe-master-symlinks\"\n        ],\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\",\n      \"tags\": [\n        \"file\",\n        \"pe-agent-symlinks\",\n        \"pe-mco-symlinks\",\n        \"pe-master-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 31,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"replace\": false,\n        \"tag\": [\n          \"pe-agent-symlinks\",\n          \"pe-mco-symlinks\",\n          \"pe-master-symlinks\"\n        ],\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\\/facter\",\n      \"tags\": [\n        \"file\",\n        \"pe-agent-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/bin\\/facter\",\n        \"tag\": \"pe-agent-symlinks\",\n        \"require\": \"File[\\/usr\\/local\\/bin]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\\/puppet\",\n      \"tags\": [\n        \"file\",\n        \"pe-agent-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/bin\\/puppet\",\n        \"tag\": \"pe-agent-symlinks\",\n        \"require\": \"File[\\/usr\\/local\\/bin]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\\/pe-man\",\n      \"tags\": [\n        \"file\",\n        \"pe-agent-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 51,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/bin\\/pe-man\",\n        \"tag\": \"pe-agent-symlinks\",\n        \"require\": \"File[\\/usr\\/local\\/bin]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\\/hiera\",\n      \"tags\": [\n        \"file\",\n        \"pe-agent-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 58,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/bin\\/hiera\",\n        \"tag\": \"pe-agent-symlinks\",\n        \"require\": \"File[\\/usr\\/local\\/bin]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\\/mco\",\n      \"tags\": [\n        \"file\",\n        \"pe-mco-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/bin\\/mco\",\n        \"tag\": \"pe-mco-symlinks\",\n        \"require\": \"File[\\/usr\\/local\\/bin]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/usr\\/local\\/bin\\/r10k\",\n      \"tags\": [\n        \"file\",\n        \"pe-master-symlinks\",\n        \"class\",\n        \"puppet_enterprise::symlinks\",\n        \"puppet_enterprise\",\n        \"symlinks\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/symlinks.pp\",\n      \"line\": 72,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/bin\\/r10k\",\n        \"tag\": \"pe-master-symlinks\",\n        \"require\": \"File[\\/usr\\/local\\/bin]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Pxp_agent\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::pxp_agent\",\n        \"puppet_enterprise\",\n        \"pxp_agent\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/agent.pp\",\n      \"line\": 48,\n      \"exported\": false,\n      \"parameters\": {\n        \"broker_ws_uri\": [\n          \"wss:\\/\\/agent.demo.com:8142\\/pcp2\\/\"\n        ],\n        \"pcp_version\": \"2\",\n        \"enabled\": true,\n        \"pxp_loglevel\": \"info\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/pxp-agent\\/pxp-agent.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::pxp_agent\",\n        \"puppet_enterprise\",\n        \"pxp_agent\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/pxp_agent.pp\",\n      \"line\": 63,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"{\\\"broker-ws-uris\\\":[\\\"wss:\\/\\/agent.demo.com:8142\\/pcp2\\/\\\"],\\\"pcp-version\\\":\\\"2\\\",\\\"ssl-key\\\":\\\"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\\\",\\\"ssl-cert\\\":\\\"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\\\",\\\"ssl-ca-cert\\\":\\\"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\\\",\\\"loglevel\\\":\\\"info\\\"}\",\n        \"mode\": \"0660\",\n        \"notify\": \"Service[pxp-agent]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Pxp_agent::Service\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::pxp_agent::service\",\n        \"puppet_enterprise\",\n        \"pxp_agent\",\n        \"service\",\n        \"puppet_enterprise::pxp_agent\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/pxp_agent.pp\",\n      \"line\": 69,\n      \"exported\": false,\n      \"parameters\": {\n        \"enabled\": true\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pxp-agent\",\n      \"tags\": [\n        \"service\",\n        \"pxp-agent\",\n        \"class\",\n        \"puppet_enterprise::pxp_agent::service\",\n        \"puppet_enterprise\",\n        \"pxp_agent\",\n        \"puppet_enterprise::pxp_agent\",\n        \"puppet_enterprise::profile::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/pxp_agent\\/service.pp\",\n      \"line\": 4,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": true,\n        \"enable\": true,\n        \"hasrestart\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/state\\/package_inventory_enabled\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::agent\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/agent.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Amq\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 90,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Packages\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Repo\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::repo\",\n        \"puppet_enterprise\",\n        \"repo\",\n        \"puppet_enterprise::packages\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Stages\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::stages\",\n        \"puppet_enterprise\",\n        \"stages\",\n        \"puppet_enterprise::repo\",\n        \"repo\",\n        \"puppet_enterprise::packages\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Stage\",\n      \"title\": \"pe_setup\",\n      \"tags\": [\n        \"stage\",\n        \"pe_setup\",\n        \"class\",\n        \"puppet_enterprise::stages\",\n        \"puppet_enterprise\",\n        \"stages\",\n        \"puppet_enterprise::repo\",\n        \"repo\",\n        \"puppet_enterprise::packages\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/stages.pp\",\n      \"line\": 13,\n      \"exported\": false,\n      \"parameters\": {\n        \"before\": \"Stage[main]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Repo::Config\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::repo::config\",\n        \"puppet_enterprise\",\n        \"repo\",\n        \"config\",\n        \"puppet_enterprise::repo\",\n        \"puppet_enterprise::packages\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/repo.pp\",\n      \"line\": 29,\n      \"exported\": false,\n      \"parameters\": {\n        \"stage\": \"pe_setup\",\n        \"master\": \"agent.demo.com\",\n        \"manage\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-bundler\",\n      \"tags\": [\n        \"package\",\n        \"pe-bundler\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 23,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-java\",\n      \"tags\": [\n        \"package\",\n        \"pe-java\",\n        \"pe-master-packages\",\n        \"pe-puppetdb-packages\",\n        \"pe-activemq-packages\",\n        \"pe-console-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 33,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": [\n          \"pe-master-packages\",\n          \"pe-puppetdb-packages\",\n          \"pe-activemq-packages\",\n          \"pe-console-packages\"\n        ],\n        \"before\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-puppetdb\",\n      \"tags\": [\n        \"package\",\n        \"pe-puppetdb\",\n        \"pe-puppetdb-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-puppetdb-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-console-services\",\n      \"tags\": [\n        \"package\",\n        \"pe-console-services\",\n        \"pe-console-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 46,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-console-packages\",\n        \"before\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-client-tools\",\n      \"tags\": [\n        \"package\",\n        \"pe-client-tools\",\n        \"pe-controller-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 50,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-controller-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-license\",\n      \"tags\": [\n        \"package\",\n        \"pe-license\",\n        \"pe-master-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-master-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-puppet-license-cli\",\n      \"tags\": [\n        \"package\",\n        \"pe-puppet-license-cli\",\n        \"pe-master-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-master-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-puppetdb-termini\",\n      \"tags\": [\n        \"package\",\n        \"pe-puppetdb-termini\",\n        \"pe-master-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-master-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-console-services-termini\",\n      \"tags\": [\n        \"package\",\n        \"pe-console-services-termini\",\n        \"pe-master-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-master-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-puppetserver\",\n      \"tags\": [\n        \"package\",\n        \"pe-puppetserver\",\n        \"pe-master-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-master-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-modules\",\n      \"tags\": [\n        \"package\",\n        \"pe-modules\",\n        \"pe-master-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-master-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-orchestration-services\",\n      \"tags\": [\n        \"package\",\n        \"pe-orchestration-services\",\n        \"pe-orchestrator-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-orchestrator-packages\",\n        \"before\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-activemq\",\n      \"tags\": [\n        \"package\",\n        \"pe-activemq\",\n        \"pe-activemq-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 69,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": \"pe-activemq-packages\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-puppet-enterprise-release\",\n      \"tags\": [\n        \"package\",\n        \"pe-puppet-enterprise-release\",\n        \"pe-activemq-packages\",\n        \"pe-console-packages\",\n        \"pe-master-packages\",\n        \"pe-puppetdb-packages\",\n        \"class\",\n        \"puppet_enterprise::packages\",\n        \"puppet_enterprise\",\n        \"packages\",\n        \"puppet_enterprise::amq\",\n        \"amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/packages.pp\",\n      \"line\": 73,\n      \"exported\": false,\n      \"parameters\": {\n        \"tag\": [\n          \"pe-activemq-packages\",\n          \"pe-console-packages\",\n          \"pe-master-packages\",\n          \"pe-puppetdb-packages\"\n        ],\n        \"before\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n        \"ensure\": \"latest\",\n        \"allow_virtual\": true\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Amq::Config\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq.pp\",\n      \"line\": 19,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"require\": \"Package[pe-activemq]\",\n        \"amq_confdir\": \"\\/etc\\/puppetlabs\\/activemq\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/credentials.properties\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/jetty.xml\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/log4j.properties\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/jetty-realm.properties\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Beans\",\n      \"title\": \"agent.demo.com - beans\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::beans\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"beans\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config.pp\",\n      \"line\": 20,\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Amq::Service\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::amq::service\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"service\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq.pp\",\n      \"line\": 24,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pe-activemq\",\n      \"tags\": [\n        \"service\",\n        \"pe-activemq\",\n        \"class\",\n        \"puppet_enterprise::amq::service\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/service.pp\",\n      \"line\": 8,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true,\n        \"subscribe\": \"Class[Puppet_enterprise::Amq::Config]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/sysconfig\\/pe-activemq\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"amq\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 135,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"content\": \"# Centrally managed by Puppet version 4.10.0\\n\\nACTIVEMQ_OPTS_MEMORY=\\\"-Xms512m -Xmx512m -Dorg.apache.activemq.UseDedicatedTaskRunner=false -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:\\/var\\/log\\/puppetlabs\\/activemq\\/activemq_gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=16 -XX:GCLogFileSize=64m\\\"\\n\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0755\",\n        \"notify\": \"Service[pe-activemq]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Broker\",\n      \"title\": \"remove default localhost\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::broker\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"broker\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 146,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"localhost\",\n        \"broker_ensure\": \"absent\",\n        \"additional_attributes\": {\n          \n        },\n        \"data_directory\": \"${activemq.base}\\/data\",\n        \"persistent\": false,\n        \"use_jmx\": true,\n        \"destination_purge_schedule\": 300000\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Broker\",\n      \"title\": \"agent.demo.com\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::broker\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"broker\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 151,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"additional_attributes\": {\n          \n        },\n        \"broker_ensure\": \"present\",\n        \"data_directory\": \"${activemq.base}\\/data\",\n        \"persistent\": false,\n        \"use_jmx\": true,\n        \"destination_purge_schedule\": 300000\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Amq::Certs\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::amq::certs\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"certs\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 155,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"keystore_password\": \"password123\",\n        \"truststore_password\": \"password123\"\n      }\n    },\n    {\n      \"type\": \"Pe_java_ks\",\n      \"title\": \"puppetca:truststore\",\n      \"tags\": [\n        \"pe_java_ks\",\n        \"puppetca:truststore\",\n        \"class\",\n        \"puppet_enterprise::amq::certs\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"certs\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/certs.pp\",\n      \"line\": 26,\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"puppetca\",\n        \"target\": \"\\/etc\\/puppetlabs\\/activemq\\/broker.ts\",\n        \"ensure\": \"latest\",\n        \"certificate\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"password\": \"password123\",\n        \"trustcacerts\": true,\n        \"require\": \"Package[pe-activemq]\",\n        \"path\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/bin\",\n          \"\\/opt\\/puppetlabs\\/server\\/bin\",\n          \"\\/usr\\/bin\",\n          \"\\/bin\",\n          \"\\/usr\\/sbin\",\n          \"\\/sbin\"\n        ],\n        \"notify\": \"Class[Puppet_enterprise::Amq::Service]\",\n        \"password_fail_reset\": true\n      }\n    },\n    {\n      \"type\": \"Pe_java_ks\",\n      \"title\": \"agent.demo.com:keystore\",\n      \"tags\": [\n        \"pe_java_ks\",\n        \"agent.demo.com:keystore\",\n        \"class\",\n        \"puppet_enterprise::amq::certs\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"certs\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/certs.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"agent.demo.com\",\n        \"target\": \"\\/etc\\/puppetlabs\\/activemq\\/broker.ks\",\n        \"ensure\": \"latest\",\n        \"certificate\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"private_key\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"password\": \"password123\",\n        \"require\": \"Pe_java_ks[puppetca:truststore]\",\n        \"path\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/bin\",\n          \"\\/opt\\/puppetlabs\\/server\\/bin\",\n          \"\\/usr\\/bin\",\n          \"\\/bin\",\n          \"\\/usr\\/sbin\",\n          \"\\/sbin\"\n        ],\n        \"notify\": \"Class[Puppet_enterprise::Amq::Service]\",\n        \"password_fail_reset\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/broker.ts\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::certs\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"certs\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/certs.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"require\": [\n          \"Pe_java_ks[puppetca:truststore]\",\n          \"Pe_java_ks[agent.demo.com:keystore]\"\n        ],\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/activemq\\/broker.ks\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::amq::certs\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"certs\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/certs.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"pe-activemq\",\n        \"mode\": \"0640\",\n        \"require\": [\n          \"Pe_java_ks[puppetca:truststore]\",\n          \"Pe_java_ks[agent.demo.com:keystore]\"\n        ],\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Ssl_context\",\n      \"title\": \"agent.demo.com-ssl-context\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::ssl_context\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"ssl_context\",\n        \"agent.demo.com-ssl-context\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 161,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"keystore_password\": \"password123\",\n        \"truststore_password\": \"password123\",\n        \"additional_attributes\": {\n          \n        },\n        \"context_ensure\": \"present\",\n        \"keystore_path\": \"file:${activemq.base}\\/conf\\/broker.ks\",\n        \"truststore_path\": \"file:${activemq.base}\\/conf\\/broker.ts\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Management_context\",\n      \"title\": \"agent.demo.com - managementContext\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::management_context\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"management_context\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 252,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"additional_attributes\": {\n          \n        },\n        \"create_connector\": false,\n        \"context_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry\",\n      \"title\": \"agent.demo.com-topic->\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::destination_policy_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"destination_policy_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 256,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"topic\",\n        \"target_destination\": \">\",\n        \"enable_producer_flow_control\": false,\n        \"memory_limit\": \"5mb\",\n        \"additional_attributes\": {\n          \n        },\n        \"additional_policies\": {\n          \n        },\n        \"enable_gc_inactive_destinations\": false,\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry\",\n      \"title\": \"agent.demo.com-queue->\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::destination_policy_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"destination_policy_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 264,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"queue\",\n        \"target_destination\": \">\",\n        \"enable_producer_flow_control\": false,\n        \"memory_limit\": \"20mb\",\n        \"additional_attributes\": {\n          \n        },\n        \"additional_policies\": {\n          \n        },\n        \"enable_gc_inactive_destinations\": false,\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry\",\n      \"title\": \"agent.demo.com-queue-*.reply.>\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::destination_policy_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"destination_policy_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 272,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"queue\",\n        \"target_destination\": \"*.reply.>\",\n        \"enable_gc_inactive_destinations\": true,\n        \"inactive_timout_before_gc\": 300000,\n        \"additional_attributes\": {\n          \n        },\n        \"additional_policies\": {\n          \n        },\n        \"enable_producer_flow_control\": true,\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Transport_connector\",\n      \"title\": \"agent.demo.com-openwire-transport\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::transport_connector\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"transport_connector\",\n        \"agent.demo.com-openwire-transport\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 280,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"transport_name\": \"openwire\",\n        \"transport_uri\": \"ssl:\\/\\/0.0.0.0:61616\",\n        \"transport_options\": {\n          \"transport.enabledProtocols\": \"TLSv1,TLSv1.1,TLSv1.2\"\n        },\n        \"additional_attributes\": {\n          \n        },\n        \"connector_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Transport_connector\",\n      \"title\": \"agent.demo.com-stomp-transport\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::transport_connector\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"transport_connector\",\n        \"agent.demo.com-stomp-transport\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 287,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"transport_name\": \"stomp+ssl\",\n        \"transport_uri\": \"stomp+ssl:\\/\\/0.0.0.0:61613\",\n        \"transport_options\": {\n          \"transport.enabledProtocols\": \"TLSv1,TLSv1.1,TLSv1.2\"\n        },\n        \"additional_attributes\": {\n          \n        },\n        \"connector_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Statistics_broker_plugin\",\n      \"title\": \"agent.demo.com-statisticsBrokerPlugin\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::statistics_broker_plugin\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"statistics_broker_plugin\",\n        \"agent.demo.com-statisticsbrokerplugin\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 294,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"plugin_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Timestamping_broker_plugin\",\n      \"title\": \"agent.demo.com-timeStampingBrokerPlugin\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::timestamping_broker_plugin\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"timestamping_broker_plugin\",\n        \"agent.demo.com-timestampingbrokerplugin\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 298,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"zero_expiration_override\": 30000,\n        \"future_only\": false,\n        \"plugin_ensure\": \"present\",\n        \"ttl_ceiling\": 0\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Simple_authentication_user\",\n      \"title\": \"agent.demo.com-simple_auth_user-mcollective\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::simple_authentication_user\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"simple_authentication_user\",\n        \"agent.demo.com-simple_auth_user-mcollective\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 303,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"groups\": \"mcollective,admins,everyone\",\n        \"password\": \"password123\",\n        \"username\": \"mcollective\",\n        \"user_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry\",\n      \"title\": \"agent.demo.com-authorization-queue->\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 310,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"queue\",\n        \"target_destination\": \">\",\n        \"write\": \"admins\",\n        \"read\": \"admins\",\n        \"admin\": \"admins\",\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry\",\n      \"title\": \"agent.demo.com-authorization-topic->\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 319,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"topic\",\n        \"target_destination\": \">\",\n        \"write\": \"admins\",\n        \"read\": \"admins\",\n        \"admin\": \"admins\",\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry\",\n      \"title\": \"agent.demo.com-authorization-queue-mcollective.>\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 328,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"queue\",\n        \"target_destination\": \"mcollective.>\",\n        \"write\": \"mcollective\",\n        \"read\": \"mcollective\",\n        \"admin\": \"mcollective\",\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry\",\n      \"title\": \"agent.demo.com-authorization-topic-mcollective.>\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 337,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"topic\",\n        \"target_destination\": \"mcollective.>\",\n        \"write\": \"mcollective\",\n        \"read\": \"mcollective\",\n        \"admin\": \"mcollective\",\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry\",\n      \"title\": \"agent.demo.com-authorization-topic-ActiveMQ.Advisory.>\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 346,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"destination_type\": \"topic\",\n        \"target_destination\": \"ActiveMQ.Advisory.>\",\n        \"write\": \"everyone\",\n        \"read\": \"everyone\",\n        \"admin\": \"everyone\",\n        \"entry_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::Web_console\",\n      \"title\": \"agent.demo.com - web console - false\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::web_console\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"web_console\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 355,\n      \"exported\": false,\n      \"parameters\": {\n        \"console_ensure\": \"false\",\n        \"console_config_file\": \"jetty.xml\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Amq::Config::System_usage\",\n      \"title\": \"agent.demo.com - systemusage\",\n      \"tags\": [\n        \"puppet_enterprise::amq::config::system_usage\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"system_usage\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/amq\\/broker.pp\",\n      \"line\": 359,\n      \"exported\": false,\n      \"parameters\": {\n        \"brokername\": \"agent.demo.com\",\n        \"memory_usage\": \"200mb\",\n        \"store_usage\": \"1gb\",\n        \"temp_usage\": \"1gb\",\n        \"usage_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"certificate-authority-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 149,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"puppetserver\",\n        \"namespace\": \"puppetlabs.services.ca.certificate-authority-service\",\n        \"service\": \"certificate-authority-service\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.reporters.graphite.host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.reporters.graphite.host\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 170,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.reporters.graphite.host\",\n        \"value\": \"graphite\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf]\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.reporters.graphite.port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.reporters.graphite.port\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 177,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.reporters.graphite.port\",\n        \"value\": 2003,\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf]\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.reporters.graphite.update-interval-seconds\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.reporters.graphite.update-interval-seconds\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 184,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.reporters.graphite.update-interval-seconds\",\n        \"value\": 60,\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf]\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 196,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"certificate-authority.proxy-config.proxy-target-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"certificate-authority.proxy-config.proxy-target-url\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 201,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n        \"setting\": \"certificate-authority.proxy-config.proxy-target-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:8140\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"certificate-authority.proxy-config.ssl-opts.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"certificate-authority.proxy-config.ssl-opts.ssl-cert\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 208,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n        \"setting\": \"certificate-authority.proxy-config.ssl-opts.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"certificate-authority.proxy-config.ssl-opts.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"certificate-authority.proxy-config.ssl-opts.ssl-key\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 215,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n        \"setting\": \"certificate-authority.proxy-config.ssl-opts.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"certificate-authority.proxy-config.ssl-opts.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"certificate-authority.proxy-config.ssl-opts.ssl-ca-cert\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 222,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n        \"setting\": \"certificate-authority.proxy-config.ssl-opts.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Master::Classifier\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::master::classifier\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"classifier\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 232,\n      \"exported\": false,\n      \"parameters\": {\n        \"classifier_host\": \"agent.demo.com\",\n        \"classifier_port\": \"4433\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"node_terminus\": \"classifier\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"node_terminus\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"node_terminus\",\n        \"class\",\n        \"puppet_enterprise::profile::master::classifier\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"classifier\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/classifier.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"section\": \"master\",\n        \"setting\": \"node_terminus\",\n        \"value\": \"classifier\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppet\\/classifier.yaml\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master::classifier\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"classifier\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/classifier.pp\",\n      \"line\": 31,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"- server: agent.demo.com\\n  port: 4433\\n  prefix: \\/classifier-api\\n\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 247,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0644\",\n        \"content\": \"-----BEGIN X509 CRL-----\\nMIIC5zCB0AIBATANBgkqhkiG9w0BAQUFADBtMWswaQYDVQQDDGJQdXBwZXQgRW50\\nZXJwcmlzZSBDQSBnZW5lcmF0ZWQgb24gcGVnbGlzYW4tbGF0ZXN0LmVuZy5wdXBw\\nZXRsYWJzLm5ldCBhdCArMjAxNy0wNC0wNSAxMToyODoyNCAtMDcwMBcNMTcwNDA1\\nMTgyODM1WhcNMjIwNDA0MTgyODM2WqAvMC0wHwYDVR0jBBgwFoAUxEkp7bbPB8\\/8\\njSG0o1Bp7jfvlrAwCgYDVR0UBAMCAQAwDQYJKoZIhvcNAQEFBQADggIBACEbxx3l\\n2S2EU07RjoTDauMakvqbCZBfGcC5vWI25hbka3JnvcR+7P\\/YtvvXpwFTfiBWiuE5\\nxtG1fuRR\\/3aYAeCpbxGvovzFN+7ScjuMRlgLznwOP\\/Q7HzflmlxgAxKFDm+pgZmi\\ngYJi5QBCILbgckjQ\\/z\\/j8dCypTLjzDloDQ+SzgdB7wBW0GmorvvUCR+0+M6A4Z1L\\nXBekjvsWLXsdMY86zuMHxP8iV07PywssY+Lpye4ChWse7NYuKFqM0Xd1MaqSuJt\\/\\nDBl9OTbNuJtzvnbrRQiKzaygtym5utIE1ctdfF\\/j0CdvM0q+JqGTOPmhRoEMHZVp\\nSdkDFIREBramGyLfCWK2oa865hLEj1GIN7BD8KgZRvrno93BfSvXLR8u3ABpzsW1\\ngxCehY5pJj+Zxj\\/37\\/YJ7eNwWiNXF1BmlXvmFVrP0s\\/0axe2CYvSmEVN3X\\/oUGpM\\nGVTDRePbfwewPaZQh5EiWxvW69JXUzeNFlYdia+rQfHWJZ4Gq9HGLMi6sMA5KLfA\\n8D9isghacsxp4yjN8LVw\\/7m6TzleYXc4k\\/Yo3DXkaR2IhiXwHZGr6\\/ylqISbuq5x\\nGO+CYxlVtxLFfeXBglXB2zSAXObR2\\/mog7UwDhPhZ9vUXIMZ5FZ54W4UL9JPhwGB\\nwIWM97zKDyGZYNNxaDhAXurrpr6WTNHRPbu5\\n-----END X509 CRL-----\\n\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 257,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"recurse\": true,\n        \"require\": \"Package[pe-puppetserver]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 266,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"module_groups\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"module_groups\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 276,\n      \"exported\": false,\n      \"parameters\": {\n        \"section\": \"main\",\n        \"setting\": \"module_groups\",\n        \"value\": \"base+pe_only\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"codedir_setting\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"codedir_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 291,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"codedir\",\n        \"section\": \"main\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"environmentpath_setting\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"environmentpath_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 312,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"environmentpath\",\n        \"section\": \"main\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Master::Auth_conf\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 321,\n      \"exported\": false,\n      \"parameters\": {\n        \"console_client_certname\": \"agent.demo.com\",\n        \"classifier_client_certname\": \"agent.demo.com\",\n        \"orchestrator_client_certname\": \"agent.demo.com\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Master::Tk_authz\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/auth_conf.pp\",\n      \"line\": 15,\n      \"exported\": false,\n      \"parameters\": {\n        \"console_client_certname\": \"agent.demo.com\",\n        \"classifier_client_certname\": \"agent.demo.com\",\n        \"orchestrator_client_certname\": \"agent.demo.com\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"allow_header_cert_info\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 16,\n      \"exported\": false,\n      \"parameters\": {\n        \"version\": 1,\n        \"allow_header_cert_info\": false,\n        \"replace\": false,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs catalog\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"^\\/puppet\\/v3\\/catalog\\/([^\\/]+)$\",\n        \"match_request_type\": \"regex\",\n        \"match_request_method\": [\n          \"get\",\n          \"post\"\n        ],\n        \"allow\": \"$1\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs catalog\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs certificate\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet-ca\\/v1\\/certificate\\/\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow_unauthenticated\": true,\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs certificate\",\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs crl\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 46,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet-ca\\/v1\\/certificate_revocation_list\\/ca\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow_unauthenticated\": true,\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs crl\",\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs csr\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet-ca\\/v1\\/certificate_request\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": [\n          \"get\",\n          \"put\"\n        ],\n        \"allow_unauthenticated\": true,\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs csr\",\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs environments\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 63,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/environments\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow\": \"*\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs environments\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs environment\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/environment\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow\": \"agent.demo.com\",\n        \"sort_order\": 510,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs environment\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs environment classes\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 79,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/environment_classes\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow\": \"agent.demo.com\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs environment classes\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs file\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 92,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/file\",\n        \"match_request_type\": \"path\",\n        \"allow\": \"*\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs file\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs node\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"^\\/puppet\\/v3\\/node\\/([^\\/]+)$\",\n        \"match_request_type\": \"regex\",\n        \"match_request_method\": \"get\",\n        \"allow\": \"$1\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs node\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs report\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 109,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"^\\/puppet\\/v3\\/report\\/([^\\/]+)$\",\n        \"match_request_type\": \"regex\",\n        \"match_request_method\": \"put\",\n        \"allow\": \"$1\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs report\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs resource type\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 117,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/resource_type\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow\": [\n          \"agent.demo.com\",\n          \"agent.demo.com\",\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs resource type\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs status\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 127,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/status\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow_unauthenticated\": true,\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs status\",\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs static file content\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 135,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/v3\\/static_file_content\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow\": \"*\",\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs static file content\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs experimental\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet\\/experimental\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"get\",\n        \"allow_unauthenticated\": true,\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs experimental\",\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs deny all\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/tk_authz.pp\",\n      \"line\": 155,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/\",\n        \"match_request_type\": \"path\",\n        \"deny\": \"*\",\n        \"sort_order\": 999,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"require\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"puppetlabs deny all\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Master::Puppetdb\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 329,\n      \"exported\": false,\n      \"parameters\": {\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppetdb_port\": 8081,\n        \"facts_terminus\": \"puppetdb\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"command_broadcast\": true,\n        \"sticky_read_failover\": true,\n        \"include_unchanged_resources\": true,\n        \"soft_write_failure\": false,\n        \"report_processor_ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"mode\": \"0644\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb.conf_server_urls\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb.conf_server_urls\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 45,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf\",\n        \"section\": \"main\",\n        \"setting\": \"server_urls\",\n        \"value\": \"https:\\/\\/agent.demo.com:8081\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb.conf_command_broadcast\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb.conf_command_broadcast\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 53,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf\",\n        \"setting\": \"command_broadcast\",\n        \"value\": true,\n        \"ensure\": \"present\",\n        \"section\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb.conf_include_unchanged_resources\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb.conf_include_unchanged_resources\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 59,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf\",\n        \"setting\": \"include_unchanged_resources\",\n        \"value\": true,\n        \"ensure\": \"present\",\n        \"section\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb.conf_soft_write_failure\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb.conf_soft_write_failure\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf\",\n        \"setting\": \"soft_write_failure\",\n        \"value\": false,\n        \"ensure\": \"present\",\n        \"section\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb.conf_sticky_read_failover\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb.conf_sticky_read_failover\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf\",\n        \"setting\": \"sticky_read_failover\",\n        \"value\": true,\n        \"ensure\": \"present\",\n        \"section\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"storeconfigs\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"storeconfigs\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"section\": \"master\",\n        \"setting\": \"storeconfigs\",\n        \"value\": true,\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"storeconfigs_backend\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"storeconfigs_backend\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 87,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"section\": \"master\",\n        \"setting\": \"storeconfigs_backend\",\n        \"value\": \"puppetdb\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"reports_puppetdb\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"reports_puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 94,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"section\": \"master\",\n        \"setting\": \"reports\",\n        \"subsetting\": \"puppetdb\",\n        \"subsetting_separator\": \",\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppet\\/routes.yaml\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/puppetdb.pp\",\n      \"line\": 104,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"content\": \"master:\\n      facts:\\n        terminus: puppetdb\\n        cache: yaml\\n\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0444\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Master\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 349,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"static_files\": {\n          \"\\/packages\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\"\n        },\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"java_args\": {\n          \"Xmx\": \"2048m\",\n          \"Xms\": \"2048m\"\n        },\n        \"metrics_server_id\": \"peglisan-latest\",\n        \"metrics_jmx_enabled\": true,\n        \"metrics_graphite_enabled\": false,\n        \"profiler_enabled\": true,\n        \"manage_symlinks\": true,\n        \"code_manager_auto_configure\": false,\n        \"puppetserver_jruby_puppet_master_code_dir\": \"\\/etc\\/puppetlabs\\/code\",\n        \"ssl_listen_port\": 8140\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Master::Puppetserver\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"static_files\": {\n          \"\\/packages\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\"\n        },\n        \"java_args\": {\n          \"Xmx\": \"2048m\",\n          \"Xms\": \"2048m\"\n        },\n        \"metrics_server_id\": \"peglisan-latest\",\n        \"metrics_jmx_enabled\": true,\n        \"metrics_graphite_enabled\": false,\n        \"profiler_enabled\": true,\n        \"code_manager_auto_configure\": false,\n        \"puppetserver_jruby_puppet_master_code_dir\": \"\\/etc\\/puppetlabs\\/code\",\n        \"puppetserver_webserver_ssl_port\": 8140,\n        \"jruby_max_requests_per_instance\": 10000,\n        \"base_puppet_admin_certs\": [\n          \"agent.demo.com\"\n        ],\n        \"puppet_admin_certs\": [\n          \n        ],\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"puppetserver_webserver_ssl_host\": \"0.0.0.0\",\n        \"puppetserver_jruby_puppet_gem_home\": \"\\/opt\\/puppetlabs\\/server\\/data\\/puppetserver\\/jruby-gems\",\n        \"puppetserver_jruby_puppet_master_conf_dir\": \"\\/etc\\/puppetlabs\\/puppet\",\n        \"puppetserver_jruby_puppet_master_var_dir\": \"\\/opt\\/puppetlabs\\/server\\/data\\/puppetserver\",\n        \"puppetserver_jruby_puppet_master_run_dir\": \"\\/var\\/run\\/puppetlabs\\/puppetserver\",\n        \"puppetserver_jruby_puppet_master_log_dir\": \"\\/var\\/log\\/puppetlabs\\/puppetserver\",\n        \"puppetserver_jruby_puppet_ruby_load_path\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/lib\\/ruby\\/vendor_ruby\",\n          \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/lib\"\n        ],\n        \"service_stop_retries\": 60,\n        \"start_timeout\": 300,\n        \"pre_commit_hook_commands\": [\n          \"\\/opt\\/puppetlabs\\/server\\/bin\\/generate-puppet-types.rb\"\n        ]\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"puppetserver:master jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 116,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"container\": \"puppetserver\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"pe-master-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-master-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 121,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.enterprise.services.master.master-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"pe-master-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"request-handler-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"request-handler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 125,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.services.request-handler.request-handler-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"request-handler-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"jruby-puppet-pooled-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"jruby-puppet-pooled-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 128,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.services.jruby.jruby-puppet-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"jruby-puppet-pooled-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"jruby-pool-manager-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"jruby-pool-manager-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 132,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"jruby-pool-manager-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"metrics-puppet-profiler-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"metrics-puppet-profiler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 136,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.enterprise.services.puppet-profiler.puppet-profiler-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"metrics-puppet-profiler-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"metrics-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"metrics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 140,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"metrics-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"puppet-server-config-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet-server-config-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.services.config.puppet-server-config-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"puppet-server-config-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"puppet-admin-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet-admin-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 148,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.services.puppet-admin.puppet-admin-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"puppet-admin-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"webrouting-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"webrouting-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 152,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"webrouting-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"pe-legacy-routes-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-legacy-routes-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 156,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.enterprise.services.legacy-routes.pe-legacy-routes-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"pe-legacy-routes-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"status-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"status-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 160,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.status.status-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"status-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"authorization-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"authorization-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 164,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.authorization.authorization-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"authorization-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"scheduler-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"scheduler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 168,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"scheduler-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"pe-jruby-metrics-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-jruby-metrics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 172,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.enterprise.services.jruby.pe-jruby-metrics-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"pe-jruby-metrics-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"analytics-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"analytics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 176,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.enterprise.services.analytics.analytics-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"analytics-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 185,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.client-auth\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.client-auth\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 189,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.client-auth\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.ssl-host\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 194,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-host\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.ssl-port\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 199,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-port\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.ssl-cert\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 204,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-cert\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.ssl-key\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 209,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-key\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.ssl-ca-cert\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 214,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-ca-cert\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.ssl-crl-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.ssl-crl-path\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 219,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-crl-path\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.access-log-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.access-log-config\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 224,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.access-log-config\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.max-threads\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.max-threads\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 229,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.max-threads\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.static-content\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.static-content\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 234,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.static-content\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings\",\n      \"title\": \"puppet-server\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 240,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"puppetserver\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8140,\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"access_log_config\": \"\\/etc\\/puppetlabs\\/puppetserver\\/request-logging.xml\",\n        \"default_server\": true,\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"certname\": \"agent.demo.com\",\n        \"ssl_cert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"ssl_key\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"container_service\": \"puppet-server\",\n        \"client_auth\": \"want\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"webserver.puppet-server.static-content\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"webserver.puppet-server.static-content\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 257,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.puppet-server.static-content\",\n        \"type\": \"array\",\n        \"value\": [\n          {\n            \"resource\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n            \"path\": \"\\/packages\",\n            \"follow-links\": true\n          }\n        ],\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 265,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/pe-master-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 269,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.enterprise.services.master.master-service\\/pe-master-service\\\"\",\n        \"value\": \"\\/puppet\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/pe-legacy-routes-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 277,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.enterprise.services.legacy-routes.pe-legacy-routes-service\\/pe-legacy-routes-service\\\"\",\n        \"value\": \"\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/legacy-routes-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 282,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.services.legacy-routes.legacy-routes-service\\/legacy-routes-service\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/certificate-authority-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 288,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.services.ca.certificate-authority-service\\/certificate-authority-service\\\"\",\n        \"value\": \"\\/puppet-ca\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/reverse-proxy-ca-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 293,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.enterprise.services.reverse-proxy.reverse-proxy-ca-service\\/reverse-proxy-ca-service\\\"\",\n        \"value\": \"\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/puppet-admin-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 298,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.services.puppet-admin.puppet-admin-service\\/puppet-admin-service\\\"\",\n        \"value\": \"\\/puppet-admin-api\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/status-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 303,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\\"\",\n        \"value\": \"\\/status\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service\\/remove-master-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 310,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.services.master.master-service\\/master-service\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 316,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.ruby-load-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.ruby-load-path\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 319,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.ruby-load-path\",\n        \"type\": \"array\",\n        \"value\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/lib\\/ruby\\/vendor_ruby\",\n          \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/lib\"\n        ],\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"os-settings.remove\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"os-settings.remove\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 327,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"os-settings\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.gem-home\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.gem-home\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 332,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.gem-home\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/puppetserver\\/jruby-gems\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.master-conf-dir\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.master-conf-dir\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 337,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.master-conf-dir\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.master-code-dir\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.master-code-dir\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 342,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.master-code-dir\",\n        \"value\": \"\\/etc\\/puppetlabs\\/code\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.master-var-dir\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.master-var-dir\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 347,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.master-var-dir\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/puppetserver\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.master-run-dir\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.master-run-dir\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 352,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.master-run-dir\",\n        \"value\": \"\\/var\\/run\\/puppetlabs\\/puppetserver\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.master-log-dir\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.master-log-dir\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 357,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.master-log-dir\",\n        \"value\": \"\\/var\\/log\\/puppetlabs\\/puppetserver\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.borrow-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.borrow-timeout\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 369,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.borrow-timeout\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.max-active-instances\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.max-active-instances\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 382,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.max-active-instances\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.max-requests-per-instance\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.max-requests-per-instance\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 389,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.max-requests-per-instance\",\n        \"value\": 10000,\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.use-legacy-auth-conf\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.use-legacy-auth-conf\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 396,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.use-legacy-auth-conf\",\n        \"value\": false,\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics-service\\/metrics-webservice\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 414,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\\"\",\n        \"value\": \"\\/metrics\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"jruby-puppet.environment-class-cache-enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"jruby-puppet.environment-class-cache-enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 434,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"jruby-puppet.environment-class-cache-enabled\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"profiler.enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"profiler.enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 441,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"profiler.enabled\",\n        \"value\": true,\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"pe-puppetserver.puppet-code-repo\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"pe-puppetserver.puppet-code-repo\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 447,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"pe-puppetserver.puppet-code-repo\",\n        \"value\": \"puppet-code\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"pe-puppetserver.pre-commit-hook-commands\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"pe-puppetserver.pre-commit-hook-commands\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 454,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"pe-puppetserver.pre-commit-hook-commands\",\n        \"type\": \"array\",\n        \"value\": [\n          \"\\/opt\\/puppetlabs\\/server\\/bin\\/generate-puppet-types.rb\"\n        ],\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppet-admin\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppet-admin\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 462,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"puppet-admin\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs environment cache\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 473,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet-admin-api\\/v1\\/environment-cache\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"delete\",\n        \"allow\": [\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"rule_name\": \"puppetlabs environment cache\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs jruby pool\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 482,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_path\": \"\\/puppet-admin-api\\/v1\\/jruby-pool\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": \"delete\",\n        \"allow\": [\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"rule_name\": \"puppetlabs jruby pool\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"http-client.ssl-protocols\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"http-client.ssl-protocols\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 491,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"http-client.ssl-protocols\",\n        \"type\": \"array\",\n        \"value\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"http-client.cipher-suites\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"http-client.cipher-suites\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 505,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"http-client.cipher-suites\",\n        \"type\": \"array\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"http-client.idle-timeout-milliseconds\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"http-client.idle-timeout-milliseconds\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 519,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"http-client.idle-timeout-milliseconds\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"http-client.connect-timeout-milliseconds\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"http-client.connect-timeout-milliseconds\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 532,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf\",\n        \"setting\": \"http-client.connect-timeout-milliseconds\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 543,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf#global.logging-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 546,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf\",\n        \"setting\": \"global.logging-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetserver\\/logback.xml\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf#global.hostname\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 551,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf\",\n        \"setting\": \"global.hostname\",\n        \"value\": \"agent.demo.com\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.certs.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.certs.ssl-cert\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 556,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf\",\n        \"setting\": \"global.certs.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.certs.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.certs.ssl-key\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 561,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf\",\n        \"setting\": \"global.certs.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.certs.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.certs.ssl-ca-cert\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 566,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf\",\n        \"setting\": \"global.certs.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 577,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 580,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.enabled\",\n        \"ensure\": \"absent\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.server-id\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.server-id\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 585,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.server-id\",\n        \"value\": \"peglisan-latest\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.registries.puppetserver.reporters.jmx.enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.registries.puppetserver.reporters.jmx.enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 591,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.registries.puppetserver.reporters.jmx.enabled\",\n        \"value\": true,\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.registries.default.reporters.jmx.enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.registries.default.reporters.jmx.enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 598,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.registries.default.reporters.jmx.enabled\",\n        \"value\": true,\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.registries.puppetserver.reporters.graphite.enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.registries.puppetserver.reporters.graphite.enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 604,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.registries.puppetserver.reporters.graphite.enabled\",\n        \"value\": false,\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.registries.puppetserver.metrics-allowed\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.registries.puppetserver.metrics-allowed\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 616,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.registries.puppetserver.metrics-allowed\",\n        \"type\": \"array\",\n        \"ensure\": \"absent\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.reporters.graphite.enabled\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.reporters.graphite.enabled\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 629,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.reporters.graphite.enabled\",\n        \"ensure\": \"absent\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"metrics.reporters.jmx\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"metrics.reporters.jmx\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 636,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf\",\n        \"setting\": \"metrics.reporters.jmx\",\n        \"ensure\": \"absent\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/rbac-consumer.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 646,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"rbac-consumer.api-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"rbac-consumer.api-url\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 649,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/rbac-consumer.conf\",\n        \"setting\": \"rbac-consumer.api-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/activity-consumer.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 655,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity-consumer.api-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity-consumer.api-url\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 659,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/activity-consumer.conf\",\n        \"setting\": \"activity-consumer.api-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\\/activity-api\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Java_args\",\n      \"title\": \"puppetserver\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 681,\n      \"exported\": false,\n      \"parameters\": {\n        \"java_args\": {\n          \"Xmx\": \"2048m\",\n          \"Xms\": \"2048m\"\n        },\n        \"enable_gc_logging\": true,\n        \"container\": \"puppetserver\",\n        \"initconfdir\": \"\\/etc\\/sysconfig\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Init_defaults\",\n      \"title\": \"puppetserver\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 686,\n      \"exported\": false,\n      \"parameters\": {\n        \"user\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"service_stop_retries\": 60,\n        \"start_timeout\": 300,\n        \"container\": \"puppetserver\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"fileserver.conf remove [files]\",\n      \"tags\": [\n        \"augeas\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 695,\n      \"exported\": false,\n      \"parameters\": {\n        \"changes\": [\n          \"remove files\"\n        ],\n        \"onlyif\": \"match files size > 0\",\n        \"incl\": \"\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\",\n        \"load_path\": \"\\/opt\\/puppetlabs\\/puppet\\/share\\/augeas\\/lenses\\/dist\",\n        \"lens\": \"PuppetFileserver.lns\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Pe_service\",\n      \"title\": \"puppetserver\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/puppetserver.pp\",\n      \"line\": 707,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"puppetserver\",\n        \"ensure\": \"running\",\n        \"enable\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/log\\/puppetlabs\\/puppet\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 76,\n      \"exported\": false,\n      \"parameters\": {\n        \"mode\": \"0640\",\n        \"backup\": false,\n        \"ensure\": \"directory\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/pe_build\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 82,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"content\": \"2017.2.0-rc1-424-ge1372a3\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver puppetconf certname\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 95,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"certname\",\n        \"value\": \"agent.demo.com\",\n        \"section\": \"master\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver puppetconf always_cache_features\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 103,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"always_cache_features\",\n        \"section\": \"master\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver puppetconf user\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 109,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"user\",\n        \"value\": \"pe-puppet\",\n        \"section\": \"main\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver puppetconf group\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 115,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"group\",\n        \"value\": \"pe-puppet\",\n        \"section\": \"main\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver static catalogs\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 127,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"static_catalogs\",\n        \"section\": \"main\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetconf environment_timeout setting\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master.pp\",\n      \"line\": 139,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"environment_timeout\",\n        \"value\": \"0\",\n        \"section\": \"main\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/share\\/puppet_enterprise\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 386,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0755\",\n        \"require\": \"Package[pe-puppet-enterprise-release]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"Ensure public dir \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n      \"tags\": [\n        \"exec\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 399,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"mkdir -p \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n        \"path\": \"\\/sbin\\/:\\/bin\\/\",\n        \"unless\": \"ls \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Fileserver_conf\",\n      \"title\": \"pe_packages\",\n      \"tags\": [\n        \"puppet_enterprise::fileserver_conf\",\n        \"puppet_enterprise\",\n        \"fileserver_conf\",\n        \"pe_packages\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 409,\n      \"exported\": false,\n      \"parameters\": {\n        \"mountpoint\": \"pe_packages\",\n        \"path\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n        \"require\": \"Exec[Ensure public dir \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public]\",\n        \"allow\": \"*\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_r10k::Package\",\n      \"tags\": [\n        \"class\",\n        \"pe_r10k::package\",\n        \"pe_r10k\",\n        \"package\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-r10k\",\n      \"tags\": [\n        \"package\",\n        \"pe-r10k\",\n        \"class\",\n        \"pe_r10k::package\",\n        \"pe_r10k\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_r10k\\/manifests\\/package.pp\",\n      \"line\": 3,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"latest\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Master::File_sync_disabled\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 489,\n      \"exported\": false\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"versioned-code-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"versioned-code-service\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/file_sync_disabled.pp\",\n      \"line\": 18,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.services.versioned-code-service.versioned-code-service\",\n        \"container\": \"puppetserver\",\n        \"service\": \"versioned-code-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/file-sync.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/file_sync_disabled.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/versioned-code.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/file_sync_disabled.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs file sync api\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/file_sync_disabled.pp\",\n      \"line\": 66,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"rule_name\": \"puppetlabs file sync api\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        },\n        \"sort_order\": 200\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs file sync repo\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/file_sync_disabled.pp\",\n      \"line\": 70,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"rule_name\": \"puppetlabs file sync repo\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        },\n        \"sort_order\": 200\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/opt-out\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 501,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/analytics-opt-out\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master.pp\",\n      \"line\": 512,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Controller\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"manage_puppet_code\": true,\n        \"manage_orchestrator\": true,\n        \"code_manager_url\": \"https:\\/\\/agent.demo.com:8170\\/code-manager\",\n        \"orchestrator_url\": \"https:\\/\\/agent.demo.com:8143\",\n        \"rbac_url\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"puppetdb_urls\": [\n          \"https:\\/\\/agent.demo.com:8081\"\n        ]\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"require\": \"Package[pe-client-tools]\",\n        \"ensure\": \"directory\",\n        \"mode\": \"0755\",\n        \"recurse\": false,\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/ssl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"require\": \"Package[pe-client-tools]\",\n        \"ensure\": \"directory\",\n        \"mode\": \"0755\",\n        \"recurse\": false,\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/ssl\\/certs\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"require\": \"Package[pe-client-tools]\",\n        \"ensure\": \"directory\",\n        \"mode\": \"0755\",\n        \"recurse\": false,\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/ssl\\/certs\\/ca.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"require\": \"Package[pe-client-tools]\",\n        \"ensure\": \"present\",\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"mode\": \"0444\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/orchestrator.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0444\",\n        \"content\": \"{\\\"options\\\":{\\\"service-url\\\":\\\"https:\\/\\/agent.demo.com:8143\\\"}}\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/puppet-code.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 64,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0444\",\n        \"content\": \"{\\\"service-url\\\":\\\"https:\\/\\/agent.demo.com:8170\\/code-manager\\\"}\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/puppet-access.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 79,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0444\",\n        \"content\": \"{\\\"service-url\\\":\\\"https:\\/\\/agent.demo.com:4433\\/rbac-api\\\",\\\"certificate-file\\\":\\\"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\\\"}\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/puppetdb.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::controller\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 94,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0444\",\n        \"content\": \"{\\\"puppetdb\\\":{\\\"server_urls\\\":[\\\"https:\\/\\/agent.demo.com:8081\\\"],\\\"cacert\\\":\\\"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\\\"}}\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Cli_config\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::cli_config\",\n        \"puppet_enterprise\",\n        \"cli_config\",\n        \"puppet_enterprise::profile::controller\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/controller.pp\",\n      \"line\": 102,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\",\n        \"user\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0444\",\n        \"require\": \"Package[pe-client-tools]\",\n        \"additional_services\": [\n          \n        ],\n        \"additional_nodes\": [\n          \n        ]\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::cli_config\",\n        \"puppet_enterprise\",\n        \"cli_config\",\n        \"puppet_enterprise::profile::controller\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/cli_config.pp\",\n      \"line\": 194,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0444\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\\/services\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::cli_config\",\n        \"puppet_enterprise\",\n        \"cli_config\",\n        \"puppet_enterprise::profile::controller\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/cli_config.pp\",\n      \"line\": 201,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"services\",\n        \"value\": [\n          {\n            \"type\": \"classifier\",\n            \"url\": \"https:\\/\\/agent.demo.com:4433\\/classifier-api\",\n            \"server\": \"agent.demo.com\",\n            \"port\": \"4433\",\n            \"prefix\": \"classifier-api\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:4433\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"classifier-service\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"Classifier\"\n          },\n          {\n            \"type\": \"rbac\",\n            \"url\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n            \"server\": \"agent.demo.com\",\n            \"port\": \"4433\",\n            \"prefix\": \"rbac-api\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:4433\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"rbac-service\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"RBAC\"\n          },\n          {\n            \"type\": \"activity\",\n            \"url\": \"https:\\/\\/agent.demo.com:4433\\/activity-api\",\n            \"server\": \"agent.demo.com\",\n            \"port\": \"4433\",\n            \"prefix\": \"activity-api\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:4433\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"activity-service\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"Activity Service\"\n          },\n          {\n            \"type\": \"master\",\n            \"url\": \"https:\\/\\/agent.demo.com:8140\\/\",\n            \"server\": \"agent.demo.com\",\n            \"port\": 8140,\n            \"prefix\": \"\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:8140\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"pe-master\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"Puppet Server\"\n          },\n          {\n            \"type\": \"orchestrator\",\n            \"url\": \"https:\\/\\/agent.demo.com:8143\\/orchestrator\",\n            \"server\": \"agent.demo.com\",\n            \"port\": 8143,\n            \"prefix\": \"orchestrator\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:8143\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"orchestrator-service\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"Orchestrator\"\n          },\n          {\n            \"type\": \"pcp-broker\",\n            \"url\": \"wss:\\/\\/agent.demo.com:8142\\/pcp\",\n            \"server\": \"agent.demo.com\",\n            \"port\": 8142,\n            \"prefix\": \"pcp\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:8143\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"broker-service\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"PCP Broker\"\n          },\n          {\n            \"type\": \"pcp-broker\",\n            \"url\": \"wss:\\/\\/agent.demo.com:8142\\/pcp2\",\n            \"server\": \"agent.demo.com\",\n            \"port\": 8142,\n            \"prefix\": \"pcp2\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:8143\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"broker-service\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"PCP Broker v2\"\n          },\n          {\n            \"type\": \"puppetdb\",\n            \"url\": \"https:\\/\\/agent.demo.com:8081\\/pdb\",\n            \"server\": \"agent.demo.com\",\n            \"port\": 8081,\n            \"prefix\": \"pdb\",\n            \"status_url\": \"https:\\/\\/agent.demo.com:8081\\/status\",\n            \"status_prefix\": \"status\",\n            \"status_key\": \"puppetdb-status\",\n            \"node_certname\": \"agent.demo.com\",\n            \"display_name\": \"PuppetDB\"\n          }\n        ],\n        \"type\": \"array\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\\/nodes\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::cli_config\",\n        \"puppet_enterprise\",\n        \"cli_config\",\n        \"puppet_enterprise::profile::controller\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/cli_config.pp\",\n      \"line\": 207,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"nodes\",\n        \"value\": [\n          {\n            \"role\": \"primary_master\",\n            \"display_name\": \"Primary Master\",\n            \"certname\": \"agent.demo.com\"\n          }\n        ],\n        \"type\": \"array\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\\/certs\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::cli_config\",\n        \"puppet_enterprise\",\n        \"cli_config\",\n        \"puppet_enterprise::profile::controller\",\n        \"profile\",\n        \"controller\",\n        \"puppet_enterprise::profile::master\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/cli_config.pp\",\n      \"line\": 213,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"certs\",\n        \"value\": {\n          \"ca-cert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\"\n        },\n        \"type\": \"hash\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/client-tools\\/services.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"certificate-authority.certificate-status\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"certificate-authority.certificate-status\",\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/certificate_authority.pp\",\n      \"line\": 31,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n        \"setting\": \"certificate-authority.certificate-status\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"puppetlabs certificate status\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/certificate_authority.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"match_request_path\": \"\\/puppet-ca\\/v1\\/certificate_status\",\n        \"match_request_type\": \"path\",\n        \"match_request_method\": [\n          \"get\",\n          \"put\",\n          \"delete\"\n        ],\n        \"allow\": [\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 500,\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"rule_name\": \"puppetlabs certificate status\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"certificate-authority.proxy-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"certificate-authority.proxy-config\",\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/certificate_authority.pp\",\n      \"line\": 61,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf\",\n        \"setting\": \"certificate-authority.proxy-config\",\n        \"ensure\": \"absent\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Fileserver_conf\",\n      \"title\": \"pe_modules\",\n      \"tags\": [\n        \"puppet_enterprise::fileserver_conf\",\n        \"puppet_enterprise\",\n        \"fileserver_conf\",\n        \"pe_modules\",\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/certificate_authority.pp\",\n      \"line\": 72,\n      \"exported\": false,\n      \"parameters\": {\n        \"mountpoint\": \"pe_modules\",\n        \"path\": \"\\/opt\\/puppetlabs\\/server\\/share\\/installer\\/modules\",\n        \"allow\": \"*\",\n        \"before\": [\n          \"Pe_anchor[puppet_enterprise:barrier:ca]\"\n        ]\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Console::Certs\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"certs\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 183,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"require\": \"Package[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs\",\n      \"title\": \"pe-console-services::server_cert\",\n      \"tags\": [\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/certs.pp\",\n      \"line\": 16,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"container\": \"console-services\",\n        \"cert_dir\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\",\n        \"append_ca\": false,\n        \"before\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf]\",\n        \"make_pk8_cert\": true,\n        \"group\": \"pe-console-services\",\n        \"owner\": \"pe-console-services\",\n        \"ssl_dir\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Console::Console_services_config\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 208,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"listen_address\": \"127.0.0.1\",\n        \"listen_port\": \"4430\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": \"4431\",\n        \"api_listen_port\": \"4432\",\n        \"api_ssl_listen_port\": \"4433\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"activity_url_prefix\": \"\\/activity-api\",\n        \"rbac_url_prefix\": \"\\/rbac-api\",\n        \"status_proxy_enabled\": false,\n        \"status_proxy_port\": 8123,\n        \"replication_mode\": \"none\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"tk_jetty_request_header_max_size\": 65536\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 91,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.host\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 96,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.host\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.port\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.port\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.ssl-host\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 106,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-host\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.ssl-port\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-port\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.ssl-cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 116,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-cert\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.ssl-key\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 121,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-key\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.ssl-ca-cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 126,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.ssl-ca-cert\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings\",\n      \"title\": \"console\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 132,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 4431,\n        \"ssl_cert\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ssl_key\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"access_log_config\": \"\\/etc\\/puppetlabs\\/console-services\\/request-logging.xml\",\n        \"tk_jetty_request_header_max_size\": 65536,\n        \"default_server\": true,\n        \"client_auth\": \"none\",\n        \"certname\": \"agent.demo.com\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"container_service\": \"console\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.host\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 147,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.console.host\",\n        \"value\": \"127.0.0.1\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.port\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 152,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.console.port\",\n        \"value\": \"4430\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings\",\n      \"title\": \"api\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 158,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 4433,\n        \"ssl_cert\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ssl_key\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"access_log_config\": \"\\/etc\\/puppetlabs\\/console-services\\/request-logging-api.xml\",\n        \"certname\": \"agent.demo.com\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"container_service\": \"api\",\n        \"client_auth\": \"want\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.host\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 170,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.api.host\",\n        \"value\": \"127.0.0.1\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.port\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 175,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.api.port\",\n        \"value\": \"4432\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.status-proxy\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.status-proxy\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 186,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"webserver.status-proxy\",\n        \"value\": {\n          \"host\": \"0.0.0.0\",\n          \"port\": 8123\n        },\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.activity.services\\/activity-service\\\"\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 193,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.activity.services\\/activity-service\\\"\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/activity-api\",\n          \"server\": \"api\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.rbac.services.http.api\\/rbac-http-api-service\\\"\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 200,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.rbac.services.http.api\\/rbac-http-api-service\\\"\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/rbac-api\",\n          \"server\": \"api\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.pe-console-ui.service\\/pe-console-ui-service\\\".pe-console-app\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 207,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.pe-console-ui.service\\/pe-console-ui-service\\\".pe-console-app\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/\",\n          \"server\": \"console\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.pe-console-auth-ui.service\\/pe-console-auth-ui-service\\\".authn-app\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 214,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.pe-console-auth-ui.service\\/pe-console-auth-ui-service\\\".authn-app\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/auth\",\n          \"server\": \"console\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.classifier.main\\/classifier-service\\\"\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 221,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.classifier.main\\/classifier-service\\\"\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/classifier-api\",\n          \"server\": \"api\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\\"\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 228,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\\"\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/status\",\n          \"server\": \"api\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-proxy-service\\/status-proxy-service\\\"\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 235,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-proxy-service\\/status-proxy-service\\\"\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/status\",\n          \"server\": \"status-proxy\"\n        },\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\\"\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 243,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\\"\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"route\": \"\\/metrics\",\n          \"server\": \"api\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"status-proxy\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"status-proxy\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 250,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"status-proxy\",\n        \"type\": \"hash\",\n        \"value\": {\n          \"proxy-target-url\": \"https:\\/\\/127.0.0.1:4433\\/status\",\n          \"ssl-opts\": {\n            \"ssl-cert\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n            \"ssl-key\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n            \"ssl-ca-cert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\"\n          }\n        },\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.remove-rbac-ui-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"web-router-service.remove-rbac-ui-service\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 261,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.rbac-ui.service\\/rbac-ui-service\\\"\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.remove-helpers-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"web-router-service.remove-helpers-service\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 267,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.proxy.services.proxy\\/helpers-service\\\"\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"web-router-service.remove-classifier-ui-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"web-router-service.remove-classifier-ui-service\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 273,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.classifier-ui.service\\/classifier-ui-service\\\"\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/global.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 287,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.logging-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.logging-config\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 290,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.logging-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/console-services\\/logback.xml\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.version-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.version-path\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 295,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.version-path\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/pe_version\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.login-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.login-path\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 300,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.login-path\",\n        \"value\": \"\\/auth\\/login\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"global.replication-mode\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"global.replication-mode\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/console_services_config.pp\",\n      \"line\": 305,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.replication-mode\",\n        \"value\": \"none\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Activity\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 226,\n      \"exported\": false,\n      \"parameters\": {\n        \"database_host\": \"agent.demo.com\",\n        \"database_port\": 5432,\n        \"database_name\": \"pe-activity\",\n        \"database_user\": \"pe-activity-write\",\n        \"database_migration_user\": \"pe-activity\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8&sslcert=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"rbac_host\": \"127.0.0.1\",\n        \"rbac_port\": \"4432\",\n        \"rbac_url_prefix\": \"\\/rbac-api\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"container\": \"console-services\",\n        \"maximum_pool_size\": 10,\n        \"pool_timeout\": 30,\n        \"pool_check_timeout\": 5,\n        \"group\": \"pe-console-services\",\n        \"user\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Rbac\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 240,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"database_host\": \"agent.demo.com\",\n        \"database_port\": 5432,\n        \"database_name\": \"pe-rbac\",\n        \"database_user\": \"pe-rbac-write\",\n        \"database_migration_user\": \"pe-rbac\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8&sslcert=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"token_maximum_lifetime\": \"10y\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"container\": \"console-services\",\n        \"maximum_pool_size\": 10,\n        \"pool_timeout\": 30,\n        \"pool_check_timeout\": 5,\n        \"group\": \"pe-console-services\",\n        \"user\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 257,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"group\": \"pe-console-services\",\n        \"owner\": \"pe-console-services\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs::Whitelist_entry\",\n      \"title\": \"rbac cert whitelist entry: agent.demo.com\",\n      \"tags\": [\n        \"puppet_enterprise::certs::whitelist_entry\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"whitelist_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 287,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Classifier\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 295,\n      \"exported\": false,\n      \"parameters\": {\n        \"master_host\": \"agent.demo.com\",\n        \"master_port\": 8140,\n        \"database_host\": \"agent.demo.com\",\n        \"database_port\": 5432,\n        \"database_name\": \"pe-classifier\",\n        \"database_user\": \"pe-classifier-write\",\n        \"database_migration_user\": \"pe-classifier\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8&sslcert=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"client_certname\": \"agent.demo.com\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"synchronization_period\": 600,\n        \"prune_days_threshold\": 7,\n        \"node_check_in_storage\": false,\n        \"notify\": \"Service[pe-console-services]\",\n        \"container\": \"console-services\",\n        \"group\": \"pe-console-services\",\n        \"user\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Console_services\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 313,\n      \"exported\": false,\n      \"parameters\": {\n        \"client_certname\": \"agent.demo.com\",\n        \"master_host\": \"agent.demo.com\",\n        \"classifier_host\": \"127.0.0.1\",\n        \"classifier_port\": \"4432\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppetdb_port\": 8081,\n        \"rbac_host\": \"127.0.0.1\",\n        \"rbac_port\": \"4432\",\n        \"activity_host\": \"127.0.0.1\",\n        \"activity_port\": \"4432\",\n        \"activity_url_prefix\": \"\\/activity-api\",\n        \"orchestrator_host\": \"agent.demo.com\",\n        \"orchestrator_port\": 8143,\n        \"orchestrator_url_prefix\": \"\\/orchestrator\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"java_args\": {\n          \"Xmx\": \"256m\",\n          \"Xms\": \"256m\"\n        },\n        \"status_proxy_enabled\": false,\n        \"pcp_timeout\": 5,\n        \"service_alert_timeout\": 5000,\n        \"display_local_time\": false,\n        \"session_maximum_lifetime\": \"\",\n        \"replication_mode\": \"none\",\n        \"proxy_idle_timeout\": 60,\n        \"master_port\": 8140,\n        \"service_stop_retries\": 60,\n        \"start_timeout\": 300,\n        \"pcp_broker_host\": \"agent.demo.com\",\n        \"pcp_broker_port\": 8142\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Console_services\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/console_services.pp\",\n      \"line\": 43,\n      \"exported\": false,\n      \"parameters\": {\n        \"client_certname\": \"agent.demo.com\",\n        \"proxy_idle_timeout\": 60,\n        \"master_host\": \"agent.demo.com\",\n        \"master_port\": 8140,\n        \"orchestrator_host\": \"agent.demo.com\",\n        \"orchestrator_port\": 8143,\n        \"orchestrator_url_prefix\": \"\\/orchestrator\",\n        \"classifier_host\": \"127.0.0.1\",\n        \"classifier_port\": \"4432\",\n        \"classifier_url_prefix\": \"\\/classifier-api\",\n        \"puppetdb_host\": \"agent.demo.com\",\n        \"puppetdb_port\": 8081,\n        \"rbac_host\": \"127.0.0.1\",\n        \"rbac_port\": \"4432\",\n        \"activity_host\": \"127.0.0.1\",\n        \"activity_port\": \"4432\",\n        \"activity_url_prefix\": \"\\/activity-api\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"status_proxy_enabled\": false,\n        \"pcp_timeout\": 5,\n        \"service_alert_timeout\": 5000,\n        \"display_local_time\": false,\n        \"session_maximum_lifetime\": \"\",\n        \"replication_mode\": \"none\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"container\": \"console-services\",\n        \"group\": \"pe-console-services\",\n        \"rbac_url_prefix\": \"\\/rbac-api\",\n        \"user\": \"pe-console-services\",\n        \"status_services\": [\n          \n        ]\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Java_args\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/console_services.pp\",\n      \"line\": 74,\n      \"exported\": false,\n      \"parameters\": {\n        \"java_args\": {\n          \"Xmx\": \"256m\",\n          \"Xms\": \"256m\"\n        },\n        \"enable_gc_logging\": true,\n        \"container\": \"console-services\",\n        \"initconfdir\": \"\\/etc\\/sysconfig\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Init_defaults\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/console_services.pp\",\n      \"line\": 79,\n      \"exported\": false,\n      \"parameters\": {\n        \"service_stop_retries\": 60,\n        \"start_timeout\": 300,\n        \"container\": \"console-services\",\n        \"user\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Pe_service\",\n      \"title\": \"console-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/console_services.pp\",\n      \"line\": 84,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"ensure\": \"running\",\n        \"enable\": true\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Console::Proxy\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console.pp\",\n      \"line\": 341,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"trapperkeeper_proxy_listen_address\": \"127.0.0.1\",\n        \"trapperkeeper_proxy_listen_port\": \"4430\",\n        \"proxy_read_timeout\": 120,\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 443,\n        \"replication_mode\": \"none\",\n        \"require\": \"Class[Puppet_enterprise::Profile::Console::Certs]\",\n        \"dhparam_file\": \"\\/etc\\/puppetlabs\\/nginx\\/dhparam_puppetproxy.pem\",\n        \"ssl_ciphers\": [\n          \"ECDHE-RSA-AES128-GCM-SHA256\",\n          \"ECDHE-ECDSA-AES128-GCM-SHA256\",\n          \"ECDHE-RSA-AES256-GCM-SHA384\",\n          \"ECDHE-ECDSA-AES256-GCM-SHA384\",\n          \"DHE-RSA-AES128-GCM-SHA256\",\n          \"DHE-DSS-AES128-GCM-SHA256\",\n          \"kEDH+AESGCM\",\n          \"ECDHE-RSA-AES128-SHA256\",\n          \"ECDHE-ECDSA-AES128-SHA256\",\n          \"ECDHE-RSA-AES128-SHA\",\n          \"ECDHE-ECDSA-AES128-SHA\",\n          \"ECDHE-RSA-AES256-SHA384\",\n          \"ECDHE-ECDSA-AES256-SHA384\",\n          \"ECDHE-RSA-AES256-SHA\",\n          \"ECDHE-ECDSA-AES256-SHA\",\n          \"DHE-RSA-AES128-SHA256\",\n          \"DHE-RSA-AES128-SHA\",\n          \"DHE-DSS-AES128-SHA256\",\n          \"DHE-RSA-AES256-SHA256\",\n          \"DHE-DSS-AES256-SHA\",\n          \"DHE-RSA-AES256-SHA\",\n          \"ECDHE-RSA-DES-CBC3-SHA\",\n          \"ECDHE-ECDSA-DES-CBC3-SHA\",\n          \"AES128-GCM-SHA256\",\n          \"AES256-GCM-SHA384\",\n          \"AES128-SHA256\",\n          \"AES256-SHA256\",\n          \"AES128-SHA\",\n          \"AES256-SHA\",\n          \"AES\",\n          \"CAMELLIA\",\n          \"DES-CBC3-SHA\",\n          \"!aNULL\",\n          \"!eNULL\",\n          \"!EXPORT\",\n          \"!DES\",\n          \"!RC4\",\n          \"!MD5\",\n          \"!PSK\",\n          \"!aECDH\",\n          \"!EDH-DSS-DES-CBC3-SHA\",\n          \"!EDH-RSA-DES-CBC3-SHA\",\n          \"!KRB5-DES-CBC3-SHA\"\n        ],\n        \"ssl_prefer_server_ciphers\": \"on\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"ssl_session_cache\": \"shared:SSL:50m\",\n        \"ssl_session_timeout\": \"1d\",\n        \"ssl_verify_client\": \"off\",\n        \"ssl_verify_depth\": 1,\n        \"nginx_gzip\": \"on\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_nginx\",\n      \"tags\": [\n        \"class\",\n        \"pe_nginx\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_nginx::Params\",\n      \"tags\": [\n        \"class\",\n        \"pe_nginx::params\",\n        \"pe_nginx\",\n        \"params\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Package\",\n      \"title\": \"pe-nginx\",\n      \"tags\": [\n        \"package\",\n        \"pe-nginx\",\n        \"class\",\n        \"pe_nginx\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/init.pp\",\n      \"line\": 19,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"latest\"\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pe-nginx\",\n      \"tags\": [\n        \"service\",\n        \"pe-nginx\",\n        \"class\",\n        \"pe_nginx\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/init.pp\",\n      \"line\": 24,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true,\n        \"require\": \"Package[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 75,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0644\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"require\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/nginx\\/dhparam_puppetproxy.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 85,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"source\": \"puppet:\\/\\/\\/modules\\/puppet_enterprise\\/console\\/dhparam_puppetproxy.pem\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0644\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Console::Proxy::Nginx_conf\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 94,\n      \"exported\": false,\n      \"parameters\": {\n        \"gzip\": \"on\",\n        \"nginx_config_file\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"gzip_comp_level\": 5,\n        \"gzip_min_length\": 256,\n        \"gzip_proxied\": \"any\",\n        \"gzip_vary\": \"on\",\n        \"gzip_types\": [\n          \"application\\/atom+xml\",\n          \"application\\/javascript\",\n          \"application\\/json\",\n          \"application\\/ld+json\",\n          \"application\\/manifest+json\",\n          \"application\\/rss+xml\",\n          \"application\\/vnd.geo+json\",\n          \"application\\/vnd.ms-fontobject\",\n          \"application\\/x-font-ttf\",\n          \"application\\/x-web-app-manifest+json\",\n          \"application\\/xhtml+xml\",\n          \"application\\/xml\",\n          \"font\\/opentype\",\n          \"image\\/bmp\",\n          \"image\\/svg+xml\",\n          \"image\\/x-icon\",\n          \"text\\/cache-manifest\",\n          \"text\\/css\",\n          \"text\\/plain\",\n          \"text\\/vcard\",\n          \"text\\/vnd.rim.location.xloc\",\n          \"text\\/vtt\",\n          \"text\\/x-component\",\n          \"text\\/x-cross-domain-policy\",\n          \"text\\/javascript\"\n        ]\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"gzip\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/nginx_conf.pp\",\n      \"line\": 58,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_ensure\": \"present\",\n        \"value\": \"on\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"directive_name\": \"gzip\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"gzip_comp_level\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_comp_level\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/nginx_conf.pp\",\n      \"line\": 68,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_ensure\": \"present\",\n        \"value\": 5,\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"directive_name\": \"gzip_comp_level\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"gzip_min_length\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_min_length\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/nginx_conf.pp\",\n      \"line\": 73,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_ensure\": \"present\",\n        \"value\": 256,\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"directive_name\": \"gzip_min_length\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"gzip_proxied\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_proxied\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/nginx_conf.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_ensure\": \"present\",\n        \"value\": \"any\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"directive_name\": \"gzip_proxied\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"gzip_vary\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_vary\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/nginx_conf.pp\",\n      \"line\": 83,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_ensure\": \"present\",\n        \"value\": \"on\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"directive_name\": \"gzip_vary\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"gzip_types\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_types\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/nginx_conf.pp\",\n      \"line\": 90,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_ensure\": \"present\",\n        \"value\": \"application\\/atom+xml\\napplication\\/javascript\\napplication\\/json\\napplication\\/ld+json\\napplication\\/manifest+json\\napplication\\/rss+xml\\napplication\\/vnd.geo+json\\napplication\\/vnd.ms-fontobject\\napplication\\/x-font-ttf\\napplication\\/x-web-app-manifest+json\\napplication\\/xhtml+xml\\napplication\\/xml\\nfont\\/opentype\\nimage\\/bmp\\nimage\\/svg+xml\\nimage\\/x-icon\\ntext\\/cache-manifest\\ntext\\/css\\ntext\\/plain\\ntext\\/vcard\\ntext\\/vnd.rim.location.xloc\\ntext\\/vtt\\ntext\\/x-component\\ntext\\/x-cross-domain-policy\\ntext\\/javascript\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"directive_name\": \"gzip_types\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"server_name\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"server_name\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 104,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"agent.demo.com\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"server_name\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"listen\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"listen\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 112,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"443 ssl\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"listen\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_certificate\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_certificate\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 119,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_certificate\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_certificate_key\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_certificate_key\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 123,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_certificate_key\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_crl\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_crl\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 127,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_crl\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_prefer_server_ciphers\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_prefer_server_ciphers\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 131,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"on\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_prefer_server_ciphers\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_ciphers\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_ciphers\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 135,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_ciphers\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_protocols\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_protocols\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 139,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"TLSv1 TLSv1.1 TLSv1.2\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_protocols\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_dhparam\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_dhparam\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 143,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"\\/etc\\/puppetlabs\\/nginx\\/dhparam_puppetproxy.pem\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_dhparam\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_verify_client\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_verify_client\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 147,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"off\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_verify_client\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_verify_depth\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_verify_depth\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 151,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": 1,\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_verify_depth\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_session_timeout\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_session_timeout\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 155,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"1d\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_session_timeout\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"ssl_session_cache\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_session_cache\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 159,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"shared:SSL:50m\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"ssl_session_cache\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_pass\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"proxy_pass\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 165,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"http:\\/\\/127.0.0.1:4430\",\n        \"location_context\": \"\\/\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"proxy_pass\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_redirect\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"proxy_redirect\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 170,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": \"http:\\/\\/127.0.0.1:4430 \\/\",\n        \"location_context\": \"\\/\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"proxy_redirect\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_read_timeout\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"proxy_read_timeout\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 175,\n      \"exported\": false,\n      \"parameters\": {\n        \"value\": 120,\n        \"location_context\": \"\\/\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"directive_name\": \"proxy_read_timeout\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_set_header x-ssl-subject\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 181,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"proxy_set_header\",\n        \"value\": \"X-SSL-Subject $ssl_client_s_dn\",\n        \"location_context\": \"\\/\",\n        \"replace_value\": false,\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_set_header x-client-dn\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 188,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"proxy_set_header\",\n        \"value\": \"X-Client-DN $ssl_client_s_dn\",\n        \"location_context\": \"\\/\",\n        \"replace_value\": false,\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_set_header x-client-verify\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 195,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"proxy_set_header\",\n        \"value\": \"X-Client-Verify $ssl_client_verify\",\n        \"location_context\": \"\\/\",\n        \"replace_value\": false,\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"proxy_set_header x-forwarded-for\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 202,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"proxy_set_header\",\n        \"value\": \"X-Forwarded-For $proxy_add_x_forwarded_for\",\n        \"location_context\": \"\\/\",\n        \"replace_value\": false,\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"server_context\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Profile::Console::Proxy::Http_redirect\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy.pp\",\n      \"line\": 210,\n      \"exported\": false,\n      \"parameters\": {\n        \"ssl_listen_port\": 443,\n        \"enable_http_redirect\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/http_redirect.pp\",\n      \"line\": 18,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"mode\": \"0644\",\n        \"notify\": \"Service[pe-nginx]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"http_redirect-server_name\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"http_redirect-server_name\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/http_redirect.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"server_name\",\n        \"value\": \"agent.demo.com\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"http_redirect-listen\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"http_redirect-listen\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/http_redirect.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"listen\",\n        \"value\": \"80\",\n        \"require\": \"Pe_nginx::Directive[server_name]\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Pe_nginx::Directive\",\n      \"title\": \"http_redirect-return\",\n      \"tags\": [\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"http_redirect-return\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/console\\/proxy\\/http_redirect.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"directive_name\": \"return\",\n        \"value\": \"301 https:\\/\\/$server_name$request_uri\",\n        \"require\": \"Pe_nginx::Directive[server_name]\",\n        \"directive_ensure\": \"present\",\n        \"target\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n        \"server_context\": \"agent.demo.com\",\n        \"replace_value\": true\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Service\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::service\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"service\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"mcollective\",\n      \"tags\": [\n        \"service\",\n        \"mcollective\",\n        \"class\",\n        \"puppet_enterprise::mcollective::service\",\n        \"puppet_enterprise\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/service.pp\",\n      \"line\": 6,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/credentials\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"master\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/mcollective.pp\",\n      \"line\": 36,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"password123\",\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0600\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Master::Keypair\",\n      \"title\": \"pe-internal-mcollective-servers\",\n      \"tags\": [\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-mcollective-servers\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/mcollective.pp\",\n      \"line\": 56,\n      \"exported\": false,\n      \"parameters\": {\n        \"keypair_name\": \"pe-internal-mcollective-servers\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Master::Keypair\",\n      \"title\": \"pe-internal-puppet-console-mcollective-client\",\n      \"tags\": [\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-puppet-console-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/mcollective.pp\",\n      \"line\": 56,\n      \"exported\": false,\n      \"parameters\": {\n        \"keypair_name\": \"pe-internal-puppet-console-mcollective-client\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Master::Keypair\",\n      \"title\": \"pe-internal-peadmin-mcollective-client\",\n      \"tags\": [\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-peadmin-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/master\\/mcollective.pp\",\n      \"line\": 56,\n      \"exported\": false,\n      \"parameters\": {\n        \"keypair_name\": \"pe-internal-peadmin-mcollective-client\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\/mcollective-public.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"mcollective\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/mcollective\\/agent.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Server\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/mcollective\\/agent.pp\",\n      \"line\": 51,\n      \"exported\": false,\n      \"parameters\": {\n        \"activemq_brokers\": [\n          \"agent.demo.com\"\n        ],\n        \"allow_no_actionpolicy\": \"1\",\n        \"collectives\": [\n          \"mcollective\"\n        ],\n        \"main_collective\": \"mcollective\",\n        \"manage_metadata_cron\": true,\n        \"mco_fact_cache_time\": 300,\n        \"mco_identity\": \"agent.demo.com\",\n        \"mco_loglevel\": \"info\",\n        \"mco_registerinterval\": 600,\n        \"randomize_activemq\": false,\n        \"stomp_password\": \"password123\",\n        \"stomp_port\": 61613,\n        \"stomp_user\": \"mcollective\",\n        \"activemq_heartbeat_interval\": 120,\n        \"max_hbrlck_fails\": 0\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Server::Plugins\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::server::plugins\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"plugins\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/mcollective\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::plugins\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"plugins\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/plugins.pp\",\n      \"line\": 13,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/mcollective\\/plugins\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::plugins\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"plugins\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/plugins.pp\",\n      \"line\": 15,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/mcollective\\/plugins\\/mcollective\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::plugins\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"plugins\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/plugins.pp\",\n      \"line\": 21,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"recurse\": \"remote\",\n        \"source\": \"puppet:\\/\\/\\/modules\\/puppet_enterprise\\/mcollective\\/plugins\",\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Server::Logs\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::server::logs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"logs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/log\\/puppetlabs\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::logs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"logs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/logs.pp\",\n      \"line\": 13,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/log\\/puppetlabs\\/mcollective.log\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::logs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"logs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/logs.pp\",\n      \"line\": 17,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/log\\/puppetlabs\\/mcollective-audit.log\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::logs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"logs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/logs.pp\",\n      \"line\": 17,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Server::Certs\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/ca.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 41,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/agent.demo.com.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 45,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/agent.demo.com.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/mcollective-private.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 53,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"-----BEGIN RSA PRIVATE KEY-----\\nSOMETHINGSOMETHING\\n-----END RSA PRIVATE KEY-----\\n\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/mcollective-public.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 59,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtzwjy9nfwSdNPHU2J9IO\\niRrbKgNqD4J\\/QRjpjE5GcJ5GQ+wqqRquKs5BhL9vATotjkClFQs\\/a1uiuDLV3BrM\\nmt2z3jsd\\/OPoma2H5OErZGD+9q+lytdPuLEm0J8gTFJ26pp0I8fLnTW\\/1IpNCVFw\\n5mswAkSKxK4pDGBX9vlWgwT84bvYMfU7J0AJdyEb7VxVGZTuk4m+ICTZ5gwWGLUQ\\n8pOX7h72jS76tqckDbRzAViSwvvzraQgGoRnpazTaKh5Q9eNN45nH9couAFkqU8m\\n4AooMHC4sr9RCIFx1r7+Xqg6OsRxe+9bvtXo6AfD+7oyyt8bO3PoqJzPK3KM51EI\\nzY4Qx2OsGw2XHXVfEHgAVD3u3D0UrIoH+1GFU8oTDf+CGu0r8\\/IUVj3fG7\\/XOjc6\\nrc4oGC9TvSISBTXpngxaYx1Al+AUI3vQl3BqIEmU805dxZvyJ3c9uKMXku9yhvib\\n5\\/7EpyHUEZ8Wfbj3IK0Wnopu7ZO31nch5I\\/qmE0HUzxKUOEjnbkOsCOwPH9K+V1r\\nra7Oya94+OTFq17fYhhH9qVpDnWG0HoKKTGgJbj6f5noyq2PCzDN\\/qzP4XaNERSE\\nrNHpPglncyOz26LgASb2\\/4hn0bdoJ3gTuc++O+dNMwDuVQN39PMiXp48MmgcxDYy\\nbIe8xC4JSs8x8SnSnvVoLzECAwEAAQ==\\n-----END PUBLIC KEY-----\\n\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\/puppet-dashboard-public.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\/peadmin-public.pem\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"certs\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/certs.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwJWaN7cdOi4HXt7NRxZu\\nmSW8fVPcf4AEm\\/cSqTzEUxUE4dWf9kGfqhJWzrB0oSKzAXwCI7YIEcpxt2KnUm8B\\n9I7cLP67I2wFFHJsT41NZRqNZo5qlc19GQw4AxBT2F4ZIRmTTysE6uwCpF05dQCI\\nM\\/IHHSQgWnTn4rBMzIRxvDq1FcGM\\/95wxHbZBWBxI0P93h7Oq8XdZdErczwzYC5B\\nfzAxE0iM9mF17CM2YnkmMMBpXhzl\\/rg0JPZ0mNWBUR8XCGiq33KSRvcBLBvBN9\\/Q\\nZuWFSeX1ZpcrCJOheFLXq6I2aOv99GH6MoyKN8Ix\\/cyz\\/i\\/FmugKm4l0VEBXOG\\/P\\nDOyAMkPOPncQ9dUdY9uhtOt52Vf\\/0mjCy1o96qkv1ZXYe+vrBsKK\\/Gn9KuSV2nIO\\n4\\/9pMYPg3WC2EjTFmkV\\/+DNQGILgEjJHQF7UNWXRcJqjVJ2GoVBwX3\\/e8fnwsv6b\\nfa3oAQr\\/cExI8PRoDUCBP3djfoHCqswaXdjaQxkYGaM1c2X8wdxa5P0I6aixsAk4\\ngSWSvr9I0GH2z6gZXUo12JDW67so4OcI\\/klmUp+u2ichB521aPafScrHjm7hU+w8\\nb2isugsbtzWDQneH3nfOQAhJwNuSwqLXu8bGXILTKUaat0IsjpdQiRrORZ4tt8BO\\nG++CcoLqFsZH1wfRXjrG+rECAwEAAQ==\\n-----END PUBLIC KEY-----\\n\",\n        \"backup\": false,\n        \"mode\": \"0660\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"notify\": \"Service[mcollective]\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Server::Facter\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::server::facter\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"facter\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server.pp\",\n      \"line\": 46,\n      \"exported\": false,\n      \"parameters\": {\n        \"manage_metadata_cron\": true,\n        \"mco_facter_interval\": 15,\n        \"mco_facter_interval_offset\": 11,\n        \"mco_facter_cron_minute\": [\n          11,\n          26,\n          41,\n          56\n        ],\n        \"mco_facter_windows_start_time\": 13\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/bin\\/refresh-mcollective-metadata\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::facter\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"facter\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/facter.pp\",\n      \"line\": 74,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"#!\\/opt\\/puppetlabs\\/puppet\\/bin\\/ruby\\n\\nrequire \\\"yaml\\\"\\nrequire 'puppet'\\nrequire 'puppet\\/face'\\n\\nPuppet.initialize_settings\\nfacts = Puppet::Face[:facts, '0.0.1'].find()\\nfacts_y = facts.values.to_yaml\\n\\nFile.open('\\/etc\\/puppetlabs\\/mcollective\\/facts.yaml.new', 'w', :encoding => Encoding::UTF_8) do |f|\\n  f.puts facts_y\\nend\\n\\nFile.rename('\\/etc\\/puppetlabs\\/mcollective\\/facts.yaml.new', '\\/etc\\/puppetlabs\\/mcollective\\/facts.yaml')\\n\",\n        \"before\": [\n          \"Cron[pe-mcollective-metadata]\",\n          \"Exec[bootstrap mcollective metadata]\"\n        ],\n        \"backup\": false,\n        \"mode\": \"0775\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"bootstrap mcollective metadata\",\n      \"tags\": [\n        \"exec\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::facter\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"facter\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/facter.pp\",\n      \"line\": 79,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"\\/opt\\/puppetlabs\\/puppet\\/bin\\/refresh-mcollective-metadata >>\\/var\\/log\\/puppetlabs\\/mcollective-metadata-cron.log 2>&1\",\n        \"creates\": \"\\/etc\\/puppetlabs\\/mcollective\\/facts-bootstrapped\",\n        \"require\": \"File[\\/opt\\/puppetlabs\\/puppet\\/bin\\/refresh-mcollective-metadata]\",\n        \"before\": \"Service[mcollective]\"\n      }\n    },\n    {\n      \"type\": \"Cron\",\n      \"title\": \"pe-mcollective-metadata\",\n      \"tags\": [\n        \"cron\",\n        \"pe-mcollective-metadata\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::facter\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"facter\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/facter.pp\",\n      \"line\": 87,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"\\/opt\\/puppetlabs\\/puppet\\/bin\\/refresh-mcollective-metadata >>\\/var\\/log\\/puppetlabs\\/mcollective-metadata-cron.log 2>&1\",\n        \"user\": \"root\",\n        \"minute\": [\n          11,\n          26,\n          41,\n          56\n        ],\n        \"require\": \"File[\\/opt\\/puppetlabs\\/puppet\\/bin\\/refresh-mcollective-metadata]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/facts-bootstrapped\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server::facter\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"facter\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server\\/facter.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"require\": \"Exec[bootstrap mcollective metadata]\",\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0775\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/server.cfg\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::server\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/server.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"\\n# Centrally managed by Puppet version 4.10.0\\n# https:\\/\\/puppet.com\\/docs\\/mcollective\\/current\\/configure\\/server.html\\n\\n# Connector settings (required):\\n# -----------------------------\\nconnector = activemq\\ndirect_addressing = 1\\n\\n# ActiveMQ connector settings:\\nplugin.activemq.randomize = false\\nplugin.activemq.pool.size = 1\\nplugin.activemq.pool.1.host = agent.demo.com\\nplugin.activemq.pool.1.port = 61613\\nplugin.activemq.pool.1.user = mcollective\\nplugin.activemq.pool.1.password = password123\\nplugin.activemq.pool.1.ssl = true\\nplugin.activemq.pool.1.ssl.ca = \\/etc\\/puppetlabs\\/mcollective\\/ssl\\/ca.cert.pem\\nplugin.activemq.pool.1.ssl.cert = \\/etc\\/puppetlabs\\/mcollective\\/ssl\\/agent.demo.com.cert.pem\\nplugin.activemq.pool.1.ssl.key = \\/etc\\/puppetlabs\\/mcollective\\/ssl\\/agent.demo.com.private_key.pem\\nplugin.activemq.heartbeat_interval = 120\\nplugin.activemq.max_hbrlck_fails = 0\\n\\n# Security plugin settings (required):\\n# -----------------------------------\\nsecurityprovider           = ssl\\n\\n# SSL plugin settings:\\nplugin.ssl_server_private  = \\/etc\\/puppetlabs\\/mcollective\\/ssl\\/mcollective-private.pem\\nplugin.ssl_server_public   = \\/etc\\/puppetlabs\\/mcollective\\/ssl\\/mcollective-public.pem\\nplugin.ssl_client_cert_dir = \\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\nplugin.ssl_serializer      = yaml\\n\\n# Facts, identity, and classes (recommended):\\n# ------------------------------------------\\nfactsource = yaml\\nplugin.yaml = \\/etc\\/puppetlabs\\/mcollective\\/facts.yaml\\nfact_cache_time = 300\\n\\nidentity = agent.demo.com\\n\\nclassesfile = \\/opt\\/puppetlabs\\/puppet\\/cache\\/state\\/classes.txt\\n\\n# Registration (recommended):\\n# -----------------------\\nregistration = Meta\\nregisterinterval = 600\\n\\n# Subcollectives (optional):\\n# -------------------------\\nmain_collective = mcollective\\ncollectives     = mcollective\\n\\n# Auditing (optional):\\n# -------------------\\nplugin.rpcaudit.logfile = \\/var\\/log\\/puppetlabs\\/mcollective-audit.log\\nrpcaudit = 1\\nrpcauditprovider = logfile\\n\\n# Authorization (optional):\\n# ------------------------\\nplugin.actionpolicy.allow_unconfigured = 1\\nrpcauthorization = 1\\nrpcauthprovider = action_policy\\n\\n# Logging:\\n# -------\\nlogfile  = \\/var\\/log\\/puppetlabs\\/mcollective.log\\nloglevel = info\\n\\n# Platform defaults:\\n# -----------------\\ndaemonize = 1\\nlibdir = \\/opt\\/puppet\\/libexec\\/mcollective:\\/opt\\/puppetlabs\\/mcollective\\/plugins\\n\\n# Puppet Agent plugin configuration:\\n# ---------------------------------\\nplugin.puppet.splay = true\\nplugin.puppet.splaylimit = 120\\nplugin.puppet.signal_daemon = 0\\nplugin.puppet.command = \\/opt\\/puppetlabs\\/bin\\/puppet agent\\nplugin.puppet.config  = \\/etc\\/puppetlabs\\/puppet\\/puppet.conf\\n\\n\",\n        \"mode\": \"0660\",\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Mcollective::Cleanup\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/discovery.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/package.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/package.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppet.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppet.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppetral.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppetral.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/rpcutil.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/rpcutil.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/service.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/service.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/average.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/average.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/boolean_summary.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/boolean_summary.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/sum.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/sum.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/summary.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/summary.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/completion.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/facts.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/find.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/help.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/inventory.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/package.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/ping.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/plugin.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/puppet.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/rpc.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/service.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/audit\\/logfile.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/activemq.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/activemq.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/rabbitmq.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/rabbitmq.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/agent_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/agent_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/collective_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/collective_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fact_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fact_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fstat_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fstat_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/puppet_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/puppet_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/resource_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/resource_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/service_data.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/service_data.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/flatfile.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/flatfile.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/mc.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/mc.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/stdin.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/stdin.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/facts\\/yaml_facts.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/registration\\/meta.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/security\\/sshkey.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/package\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppet_agent_mgr\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/service\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/actionpolicy.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppet_agent_mgr.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppet_server_address_validation.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppetrunner.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_resource_validator.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_resource_validator.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_server_address_validator.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_server_address_validator.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_tags_validator.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_tags_validator.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_variable_validator.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_variable_validator.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/service_name.ddl\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/service_name.rb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::mcollective::cleanup\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"cleanup\",\n        \"puppet_enterprise::mcollective::server\",\n        \"server\",\n        \"puppet_enterprise::profile::mcollective::agent\",\n        \"profile\",\n        \"agent\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/cleanup.pp\",\n      \"line\": 88,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"force\": true,\n        \"notify\": \"Service[mcollective]\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/mcollective\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"mcollective\",\n        \"peadmin\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/mcollective\\/peadmin.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/etc\\/puppetlabs\\/mcollective\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Mcollective::Client\",\n      \"title\": \"peadmin\",\n      \"tags\": [\n        \"puppet_enterprise::mcollective::client\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"peadmin\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/mcollective\\/peadmin.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"activemq_brokers\": [\n          \"agent.demo.com\"\n        ],\n        \"cert_name\": \"peadmin\",\n        \"keypair_name\": \"pe-internal-peadmin-mcollective-client\",\n        \"create_user\": true,\n        \"home_dir\": \"\\/var\\/lib\\/peadmin\",\n        \"logfile\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/client.log\",\n        \"mco_loglevel\": \"info\",\n        \"main_collective\": \"mcollective\",\n        \"stomp_password\": \"password123\",\n        \"stomp_port\": 61613,\n        \"stomp_user\": \"mcollective\",\n        \"collectives\": [\n          \"mcollective\"\n        ],\n        \"manage_symlinks\": true,\n        \"randomize_activemq\": false,\n        \"client_name\": \"peadmin\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Orchestrator\",\n      \"title\": \"orchestration-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 99,\n      \"exported\": false,\n      \"parameters\": {\n        \"database_host\": \"agent.demo.com\",\n        \"database_name\": \"pe-orchestrator\",\n        \"database_user\": \"pe-orchestrator-write\",\n        \"database_migration_user\": \"pe-orchestrator\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"master_url\": \"https:\\/\\/agent.demo.com:8140\",\n        \"puppetdb_url\": \"https:\\/\\/agent.demo.com:8081\",\n        \"classifier_url\": \"https:\\/\\/agent.demo.com:4433\\/classifier-api\",\n        \"console_services_url\": \"https:\\/\\/agent.demo.com:4433\",\n        \"rbac_url\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"activity_url\": \"https:\\/\\/agent.demo.com:4433\\/activity-api\",\n        \"console_url\": \"https:\\/\\/agent.demo.com\",\n        \"pcp_brokers\": [\n          \n        ],\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"client_certname\": \"agent.demo.com\",\n        \"container\": \"orchestration-services\",\n        \"database_port\": 5432,\n        \"user\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker\",\n      \"title\": \"orchestration-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 120,\n      \"exported\": false,\n      \"parameters\": {\n        \"accept_consumers\": 4,\n        \"delivery_consumers\": 4,\n        \"controller_uris\": [\n          \"wss:\\/\\/agent.demo.com:8143\\/server\"\n        ],\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"container\": \"orchestration-services\",\n        \"user\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services webrouting-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 127,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\",\n        \"service\": \"webrouting-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker metrics-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 133,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\",\n        \"service\": \"metrics-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker metrics-webservice\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 139,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\",\n        \"service\": \"metrics-webservice\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker scheduler-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\",\n        \"service\": \"scheduler-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker status-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 151,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.status.status-service\",\n        \"service\": \"status-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 159,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/authorization.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 168,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.authorization.version\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.authorization.version\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 172,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"setting\": \"authorization.version\",\n        \"value\": 1,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 178,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/global.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 188,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0644\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.global.logging-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.global.logging-config\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 198,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.logging-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/logback.xml\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.global.certs.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.global.certs.ssl-ca-cert\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 204,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.certs.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.global.certs.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.global.certs.ssl-cert\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 210,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.certs.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.global.certs.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.global.certs.ssl-key\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 216,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/global.conf\",\n        \"setting\": \"global.certs.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings\",\n      \"title\": \"pcp-broker\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 222,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8142,\n        \"ssl_cert\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ssl_key\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"certname\": \"agent.demo.com\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"container_service\": \"pcp-broker\",\n        \"client_auth\": \"want\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.web-router-service.broker-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.web-router-service.broker-service\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 231,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.pcp.broker.service\\/broker-service\\\"\",\n        \"value\": {\n          \"v1\": {\n            \"route\": \"\\/pcp\",\n            \"server\": \"pcp-broker\"\n          },\n          \"v2\": {\n            \"route\": \"\\/pcp2\",\n            \"server\": \"pcp-broker\"\n          },\n          \"metrics\": {\n            \"route\": \"\\/\",\n            \"server\": \"pcp-broker\"\n          }\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings\",\n      \"title\": \"orchestrator\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 250,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8143,\n        \"ssl_cert\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ssl_key\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"access_log_config\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/request-logging.xml\",\n        \"default_server\": true,\n        \"certname\": \"agent.demo.com\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"hostcrl\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"container_service\": \"orchestrator\",\n        \"client_auth\": \"want\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.web-router-service.orchestrator-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.web-router-service.orchestrator-service\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 261,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.orchestrator.service\\/orchestrator-service\\\"\",\n        \"value\": {\n          \"route\": \"\\/orchestrator\\/v1\",\n          \"server\": \"orchestrator\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.web-router-service.orchestrator-dispatch-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.web-router-service.orchestrator-dispatch-service\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 267,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.orchestrator.dispatch\\/orchestrator-dispatch-service\\\"\",\n        \"value\": {\n          \"route\": \"\\/server\",\n          \"server\": \"orchestrator\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.web-router-service.status-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.web-router-service.status-service\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 273,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\\"\",\n        \"value\": {\n          \"route\": \"\\/status\",\n          \"server\": \"orchestrator\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.web-router-service.metrics-webservice\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.web-router-service.metrics-webservice\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 279,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/web-routes.conf\",\n        \"setting\": \"web-router-service.\\\"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\\"\",\n        \"value\": {\n          \"route\": \"\\/metrics\",\n          \"server\": \"orchestrator\"\n        },\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs\",\n      \"title\": \"agent.demo.com\",\n      \"tags\": [\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 285,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"cert_dir\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\",\n        \"container\": \"orchestration-services\",\n        \"make_pk8_cert\": true,\n        \"group\": \"pe-orchestration-services\",\n        \"owner\": \"pe-orchestration-services\",\n        \"ssl_dir\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\",\n        \"append_ca\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Java_args\",\n      \"title\": \"orchestration-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 292,\n      \"exported\": false,\n      \"parameters\": {\n        \"java_args\": {\n          \"Xmx\": \"192m\",\n          \"Xms\": \"192m\"\n        },\n        \"enable_gc_logging\": true,\n        \"container\": \"orchestration-services\",\n        \"initconfdir\": \"\\/etc\\/sysconfig\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Pe_service\",\n      \"title\": \"orchestration-services\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/orchestrator.pp\",\n      \"line\": 297,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"container\": \"orchestration-services\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Puppetdb\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/puppetdb.pp\",\n      \"line\": 92,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"cert_whitelist_path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"database_name\": \"pe-puppetdb\",\n        \"database_port\": 5432,\n        \"database_host\": \"agent.demo.com\",\n        \"database_user\": \"pe-puppetdb\",\n        \"gc_interval\": \"60\",\n        \"node_purge_ttl\": \"0s\",\n        \"node_ttl\": \"7d\",\n        \"report_ttl\": \"14d\",\n        \"listen_address\": \"127.0.0.1\",\n        \"listen_port\": \"8080\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8081,\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"java_args\": {\n          \"Xmx\": \"256m\",\n          \"Xms\": \"256m\"\n        },\n        \"rbac_url\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"service_stop_retries\": 60,\n        \"start_timeout\": 300,\n        \"read_maximum_pool_size\": 25,\n        \"command_processing_threads\": 2,\n        \"concurrent_writes\": 2\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Puppetdb::Database_ini\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 117,\n      \"exported\": false,\n      \"parameters\": {\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"database_host\": \"agent.demo.com\",\n        \"database_name\": \"pe-puppetdb\",\n        \"database_port\": 5432,\n        \"database_user\": \"pe-puppetdb\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"gc_interval\": \"60\",\n        \"node_purge_ttl\": \"0s\",\n        \"node_ttl\": \"7d\",\n        \"report_ttl\": \"14d\",\n        \"write_maximum_pool_size\": 25\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Puppetdb::Shared_database_settings\",\n      \"title\": \"database\",\n      \"tags\": [\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/database_ini.pp\",\n      \"line\": 38,\n      \"exported\": false,\n      \"parameters\": {\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"database_host\": \"agent.demo.com\",\n        \"database_name\": \"pe-puppetdb\",\n        \"database_port\": 5432,\n        \"database_user\": \"pe-puppetdb\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"maximum_pool_size\": 25,\n        \"section\": \"database\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_gc_interval\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_gc_interval\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/database_ini.pp\",\n      \"line\": 58,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"gc-interval\",\n        \"value\": \"60\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"section\": \"database\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_node_ttl\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_node_ttl\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/database_ini.pp\",\n      \"line\": 63,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"node-ttl\",\n        \"value\": \"7d\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"section\": \"database\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_node_purge_ttl\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_node_purge_ttl\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/database_ini.pp\",\n      \"line\": 68,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"node-purge-ttl\",\n        \"value\": \"0s\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"section\": \"database\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_report_ttl\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_report_ttl\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/database_ini.pp\",\n      \"line\": 73,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"report-ttl\",\n        \"value\": \"14d\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"section\": \"database\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Puppetdb::Shared_database_settings\",\n      \"title\": \"read-database\",\n      \"tags\": [\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"read-database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 133,\n      \"exported\": false,\n      \"parameters\": {\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"database_host\": \"agent.demo.com\",\n        \"database_name\": \"pe-puppetdb\",\n        \"database_port\": 5432,\n        \"database_user\": \"pe-puppetdb\",\n        \"database_properties\": \"?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"maximum_pool_size\": 25,\n        \"section\": \"read-database\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Puppetdb::Jetty_ini\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"cert_whitelist_path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"listen_address\": \"127.0.0.1\",\n        \"listen_port\": \"8080\",\n        \"ssl_listen_address\": \"0.0.0.0\",\n        \"ssl_listen_port\": 8081,\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ssl_dir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\",\n        \"ssl_protocols\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"tk_jetty_request_header_max_size\": 65536\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 34,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_host\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_host\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 60,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"host\",\n        \"value\": \"127.0.0.1\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_port\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_port\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"port\",\n        \"value\": \"8080\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_sslhost\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_sslhost\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"ssl-host\",\n        \"value\": \"0.0.0.0\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_sslport\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_sslport\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 76,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"ssl-port\",\n        \"value\": 8081,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_ssl_key\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_ssl_key\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 81,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_ssl_cert\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_ssl_cert\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 86,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_ssl_ca_cert\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_ssl_ca_cert\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 91,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_client_auth\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_client_auth\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 96,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"client-auth\",\n        \"value\": \"want\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_ssl_protocols\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_ssl_protocols\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"ssl-protocols\",\n        \"value\": \"TLSv1,TLSv1.1,TLSv1.2\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb-certificate-whitelist\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb-certificate-whitelist\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 106,\n      \"exported\": false,\n      \"parameters\": {\n        \"section\": \"puppetdb\",\n        \"setting\": \"certificate-whitelist\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist]\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb_request_header_max_size\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppetdb_request_header_max_size\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::jetty_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"jetty_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/jetty_ini.pp\",\n      \"line\": 121,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"request-header-max-size\",\n        \"value\": 65536,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini\",\n        \"section\": \"jetty\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Puppetdb::Rbac_consumer_conf\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"rbac_consumer_conf\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 155,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"localcacert\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"rbac_url\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"ssl_dir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"rbac_consumer_conf\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/rbac_consumer_conf.pp\",\n      \"line\": 19,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetdb_rbac_consumer_ssl_key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetdb_rbac_consumer_ssl_key\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"rbac_consumer_conf\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/rbac_consumer_conf.pp\",\n      \"line\": 34,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"global.certs.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetdb_rbac_consumer_ssl_cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetdb_rbac_consumer_ssl_cert\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"rbac_consumer_conf\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/rbac_consumer_conf.pp\",\n      \"line\": 39,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"global.certs.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetdb_rbac_consumer_ssl_ca_cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetdb_rbac_consumer_ssl_ca_cert\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"rbac_consumer_conf\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/rbac_consumer_conf.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"global.certs.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetdb_rbac_consumer_api_url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetdb_rbac_consumer_api_url\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"rbac_consumer_conf\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/rbac_consumer_conf.pp\",\n      \"line\": 50,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"rbac-consumer.api-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Puppetdb::Config_ini\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::puppetdb::config_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"config_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 169,\n      \"exported\": false,\n      \"parameters\": {\n        \"confdir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\",\n        \"command_processing_threads\": 2,\n        \"concurrent_writes\": 2\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/config.ini\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::config_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"config_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/config_ini.pp\",\n      \"line\": 16,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"config.ini threads command-processing section\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::config_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"config_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/config_ini.pp\",\n      \"line\": 31,\n      \"exported\": false,\n      \"parameters\": {\n        \"section\": \"command-processing\",\n        \"setting\": \"threads\",\n        \"value\": 2,\n        \"notify\": \"Service[pe-puppetdb]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/config.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/config.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"config.ini concurrent-writes command-processing section\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::config_ini\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"config_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/config_ini.pp\",\n      \"line\": 38,\n      \"exported\": false,\n      \"parameters\": {\n        \"section\": \"command-processing\",\n        \"setting\": \"concurrent-writes\",\n        \"value\": 2,\n        \"notify\": \"Service[pe-puppetdb]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/config.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/config.ini]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Puppetdb::Service\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::puppetdb::service\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"service\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 175,\n      \"exported\": false\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Pe_service\",\n      \"title\": \"puppetdb\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::service\",\n        \"service\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/service.pp\",\n      \"line\": 7,\n      \"exported\": false,\n      \"parameters\": {\n        \"service_subscribe\": [\n          \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n          \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n          \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n          \"Class[Puppet_enterprise::Puppetdb::Config_ini]\",\n          \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\"\n        ],\n        \"container\": \"puppetdb\",\n        \"ensure\": \"running\",\n        \"enable\": true\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/log\\/puppetlabs\\/puppetdb\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 177,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/log\\/puppetlabs\\/puppetdb\\/puppetdb.log\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 185,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Java_args\",\n      \"title\": \"puppetdb\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 193,\n      \"exported\": false,\n      \"parameters\": {\n        \"java_args\": {\n          \"Xmx\": \"256m\",\n          \"Xms\": \"256m\"\n        },\n        \"require\": \"Package[pe-puppetdb]\",\n        \"enable_gc_logging\": true,\n        \"container\": \"puppetdb\",\n        \"initconfdir\": \"\\/etc\\/sysconfig\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Init_defaults\",\n      \"title\": \"puppetdb\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb.pp\",\n      \"line\": 199,\n      \"exported\": false,\n      \"parameters\": {\n        \"service_stop_retries\": 60,\n        \"start_timeout\": 300,\n        \"container\": \"puppetdb\",\n        \"user\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs\",\n      \"title\": \"pe-puppetdb\",\n      \"tags\": [\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/puppetdb.pp\",\n      \"line\": 116,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"container\": \"puppetdb\",\n        \"cert_dir\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\",\n        \"make_pk8_cert\": true,\n        \"group\": \"pe-puppetdb\",\n        \"owner\": \"pe-puppetdb\",\n        \"ssl_dir\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\",\n        \"append_ca\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/puppetdb.pp\",\n      \"line\": 123,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"group\": \"pe-puppetdb\",\n        \"owner\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Puppet_enterprise::Certs::Puppetdb_whitelist\",\n      \"tags\": [\n        \"class\",\n        \"puppet_enterprise::certs::puppetdb_whitelist\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"puppetdb_whitelist\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/profile\\/puppetdb.pp\",\n      \"line\": 138,\n      \"exported\": false,\n      \"parameters\": {\n        \"cert_whitelist_path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n        \"certnames\": [\n          \"agent.demo.com\"\n        ],\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs::Whitelist_entry\",\n      \"title\": \"puppet_enterprise::certs::puppetdb_whitelist entry: agent.demo.com\",\n      \"tags\": [\n        \"puppet_enterprise::certs::whitelist_entry\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"whitelist_entry\",\n        \"class\",\n        \"puppet_enterprise::certs::puppetdb_whitelist\",\n        \"puppetdb_whitelist\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/puppetdb_whitelist.pp\",\n      \"line\": 53,\n      \"exported\": false,\n      \"parameters\": {\n        \"certname\": \"agent.demo.com\",\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64.repo\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::el\",\n        \"pe_repo\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/el.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"[pe_repo]\\nname=Puppet Labs PE Packages \\\\$releasever - \\\\$basearch\\nbaseurl=https:\\/\\/agent.demo.com:8140\\/packages\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64\\nenabled=1\\ngpgcheck=1\\nsslverify=False\\nproxy=_none_\\ngpgkey=https:\\/\\/agent.demo.com:8140\\/packages\\/GPG-KEY-puppetlabs\\n       https:\\/\\/agent.demo.com:8140\\/packages\\/GPG-KEY-puppet\\n\",\n        \"backup\": false,\n        \"ensure\": \"file\",\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64.bash\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::el\",\n        \"pe_repo\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/el.pp\",\n      \"line\": 24,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"#!\\/bin\\/bash -e\\n\\nPUPPET_CONF_DIR=\\\"\\/etc\\/puppetlabs\\/puppet\\\"\\nPUPPET_BIN_DIR=\\\"\\/opt\\/puppetlabs\\/puppet\\/bin\\\"\\nexport OLD_PUPPET_BIN_DIR=\\\"\\/opt\\/puppet\\/bin\\\"\\n\\nfail() { echo >&2 \\\"$@\\\"; exit 1; }\\ncmd()  { hash \\\"$1\\\" >&\\/dev\\/null; } # portable 'which'\\n\\npuppet_installed() {\\n  if [ -x \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" -o -x \\\"${OLD_PUPPET_BIN_DIR}\\/puppet\\\" ]; then\\n    return 0\\n  else\\n    return 1\\n  fi\\n}\\n\\npxp_present() {\\n    if cmd \\\"${PUPPET_BIN_DIR}\\/pxp-agent\\\"; then\\n        return 1\\n    else\\n        return 0\\n    fi\\n}\\n\\n# Echo back either the AIO puppet bin dir path, or PE 3.x pe-agent\\n# puppet bin dir.\\npuppet_bin_dir() {\\n    if [ -e \\\"${PUPPET_BIN_DIR?}\\\" ]; then\\n        t_puppet_bin_dir=\\\"${PUPPET_BIN_DIR?}\\\"\\n    else\\n        t_puppet_bin_dir=\\/opt\\/puppet\\/bin\\n    fi\\n    echo \\\"${t_puppet_bin_dir?}\\\"\\n}\\n\\n\\n### The following variable and function (upgrading_from_38x)\\n### Need to be declared after the `puppet_bin_dir` function\\n### due to the way bash handles function parsing\\n# Are we upgradeing from 3.8.x or 2015.2+ ?\\nORIGINAL_PUPPET_BIN_DIR=$(puppet_bin_dir)\\n\\nupgrading_from_38x() {\\n    [ \\\"${ORIGINAL_PUPPET_BIN_DIR?}\\\" = \\\"${OLD_PUPPET_BIN_DIR?}\\\" ]\\n}\\n\\nmktempfile() {\\n  if cmd mktemp; then\\n    if [ \\\"osx\\\" = \\\"${PLATFORM_NAME}\\\" ]; then\\n      mktemp -t installer\\n    else\\n      mktemp\\n    fi\\n  else\\n    echo \\\"\\/tmp\\/puppet-enterprise-installer.XXX-${RANDOM}\\\"\\n  fi\\n}\\n\\ncustom_puppet_configuration() {\\n  # Parse optional pre-installation configuration of Puppet settings via\\n  # command-line arguments. Arguments should be of the form\\n  #\\n  #   <section>:<setting>=<value>\\n  #\\n  # There are four valid section settings in puppet.conf: \\\"main\\\", \\\"master\\\",\\n  # \\\"agent\\\", \\\"user\\\". If you provide valid setting and value for one of these\\n  # four sections, it will end up in <confdir>\\/puppet.conf.\\n  #\\n  # There are two sections in csr_attributes.yaml: \\\"custom_attributes\\\" and\\n  # \\\"extension_requests\\\". If you provide valid setting and value for one\\n  # of these two sections, it will end up in <confdir>\\/csr_attributes.yaml.\\n  #\\n  # note:Custom Attributes are only present in the CSR, while Extension\\n  # Requests are both in the CSR and included as X509 extensions in the\\n  # signed certificate (and are thus available as \\\"trusted facts\\\" in Puppet).\\n  #\\n  # Regex is authoritative for valid sections, settings, and values.  Any input\\n  # that fails regex will trigger this script to fail with error message.\\n  regex='^(main|master|agent|user|custom_attributes|extension_requests):([^=]+)=(.*)$'\\n  declare -a attr_array\\n  declare -a extn_array\\n\\n  for entry in \\\"$@\\\"; do\\n    if ! [[ $entry =~ $regex ]]; then\\n      fail \\\"Unable to interpret argument: '${entry}'. Expected '<section>:<setting>=<value>' matching regex: '${regex}'\\\"\\n    else\\n      section=${BASH_REMATCH[1]}\\n      setting=${BASH_REMATCH[2]}\\n      value=${BASH_REMATCH[3]}\\n      case $section in\\n        custom_attributes)\\n          # Store the entry in attr_array for later addition to csr_attributes.yaml\\n          attr_array=(\\\"${attr_array[@]}\\\" \\\"${setting}: '${value}'\\\")\\n          ;;\\n        extension_requests)\\n          # Store the entry in extn_array for later addition to csr_attributes.yaml\\n          extn_array=(\\\"${extn_array[@]}\\\" \\\"${setting}: '${value}'\\\")\\n          ;;\\n        *)\\n          # Set the specified entry in puppet.conf\\n          \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set \\\"$setting\\\" \\\"$value\\\" --section \\\"$section\\\"\\n      esac\\n    fi\\n  done\\n\\n  # If the the length of the attr_array or extn_array is greater than zero, it\\n  # means we have settings, so we'll create the csr_attributes.yaml file.\\n  if [[ ${#attr_array[@]} -gt 0 || ${#extn_array[@]} -gt 0 ]]; then\\n    mkdir -p \\\"${PUPPET_CONF_DIR}\\\"\\n    echo '---' > \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n\\n    if [[ ${#attr_array[@]} -gt 0 ]]; then\\n      echo 'custom_attributes:' >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      for ((i = 0; i < ${#attr_array[@]}; i++)); do\\n        echo \\\"  ${attr_array[i]}\\\" >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      done\\n    fi\\n\\n    if [[ ${#extn_array[@]} -gt 0 ]]; then\\n      echo 'extension_requests:' >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      for ((i = 0; i < ${#extn_array[@]}; i++)); do\\n        echo \\\"  ${extn_array[i]}\\\" >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      done\\n    fi\\n  fi\\n}\\n\\nensure_link() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource file \\\"${1?}\\\" ensure=link target=\\\"${2?}\\\"\\n}\\n\\nensure_agent_links() {\\n  target_path=\\\"\\/usr\\/local\\/bin\\\"\\n\\n  if mkdir -p \\\"${target_path}\\\" && [ -w \\\"${target_path}\\\" ]; then\\n    for bin in facter puppet pe-man hiera; do\\n      ensure_link \\\"${target_path}\\/${bin}\\\" \\\"${PUPPET_BIN_DIR?}\\/${bin}\\\"\\n    done\\n  else\\n    echo \\\"!!! WARNING: ${target_path} is inaccessible; unable to create convenience symlinks for puppet, hiera, facter and pe-man.  These executables may be found in ${pe_path?}.\\\" 1>&2\\n  fi\\n}\\n\\n# Detected existing installation? Return y if true, else n\\nis_upgrade() {\\n  if puppet_installed; then\\n    echo \\\"y\\\"\\n  else\\n    echo \\\"n\\\"\\n  fi\\n}\\n\\n# Sets server, certname and any custom puppet.conf flags passed in to the script\\npuppet_config_set() {\\n  # puppet config set does not create the [main] section if it does not exist\\n  # and does not use it if it has no settings (PUP-4755); and augeas does not\\n  # consider puppet.conf parseable with settings floating outside of a section\\n  puppet_conf=\\\"$(\\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config print confdir)\\/puppet.conf\\\"\\n  if ! grep '\\\\[main\\\\]' \\\"${puppet_conf}\\\"; then\\n    t_surgery='yes'\\n    cat >> \\\"${puppet_conf}\\\" <<EOF\\n[main]\\nplace=holder\\nEOF\\n  fi\\n\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set server agent.demo.com --section main\\n\\n  if [ \\\"${t_surgery}\\\" = 'yes' ]; then\\n    t_platform_test=\\\"x$(uname -s)\\\"\\n    if [ \\\"${t_platform_test}\\\" = \\\"xDarwin\\\" ]; then\\n      sed -i '' '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\"\\n    elif [ \\\"${t_platform_test}\\\" = \\\"xSunOS\\\" -o \\\"${t_platform_test}\\\" = \\\"xAIX\\\" ]; then\\n      sed '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\" > \\\"${puppet_conf}.new\\\" && mv \\\"${puppet_conf}.new\\\" \\\"${puppet_conf}\\\"\\n    else\\n      sed -i '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\"\\n    fi\\n  fi\\n\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set certname $(\\\"${PUPPET_BIN_DIR?}\\/facter\\\" fqdn | \\\"${PUPPET_BIN_DIR?}\\/ruby\\\" -e 'puts STDIN.read.downcase') --section main\\n  custom_puppet_configuration \\\"$@\\\"\\n\\n  # To ensure the new config settings take place and to work around differing OS behaviors on recieving a service start command while running\\n  # (on nix it triggers a puppet run, on osx it does nothing), restart the service by stopping and starting it again\\n  restart_puppet_agent\\n}\\n\\nrestart_puppet_agent() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=stopped\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=running enable=true\\n}\\n\\nstart_puppet_agent() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=running enable=true\\n}\\n\\nstop_puppet_agent() {\\n  \\\"$(puppet_bin_dir)\\/puppet\\\" resource service puppet ensure=stopped\\n  wait_for_puppet_lock\\n}\\n\\nmcollective_service_name() {\\n  if upgrading_from_38x; then\\n    t_mco_service_name='pe-mcollective'\\n  else\\n    t_mco_service_name='mcollective'\\n  fi\\n  echo \\\"${t_mco_service_name}\\\"\\n}\\n\\nmcollective_status() {\\n  output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource service \\\"$(mcollective_service_name)\\\" 2> \\/dev\\/null)\\n  case ${output?} in\\n    *ensure*=\\\\>*running*)\\n      echo \\\"running\\\";;\\n    *)\\n      echo \\\"stopped\\\";;\\n  esac\\n}\\n\\npxp_agent_status() {\\n  output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource service pxp-agent 2> \\/dev\\/null)\\n  case ${output?} in\\n    *ensure*=\\\\>*running*)\\n      echo \\\"running\\\";;\\n    *)\\n      echo \\\"stopped\\\";;\\n  esac\\n}\\n\\nensure_service() {\\n    pkg=${1?}\\n    state=${2?}\\n    \\\"$(puppet_bin_dir)\\/puppet\\\" resource service \\\"${pkg?}\\\" ensure=\\\"${state?}\\\"\\n    return $?\\n}\\n\\nwait_for_puppet_lock() {\\n  t_puppet_run_lock=`$(puppet_bin_dir)\\/puppet config print agent_catalog_run_lockfile`\\n  while [ -f \\\"${t_puppet_run_lock?}\\\" ]; do\\n      echo \\\"Waiting for Agent run lock ${t_puppet_run_lock?} to clear...\\\"\\n      sleep 10\\n  done\\n}\\n\\n# In version 7.10.0 curl introduced the -k flag and performs peer\\n# certificate validation by default. If peer validation is performed by\\n# default the -k flag is necessary for this script to work. However, if curl\\n# is older than 7.10.0 the -k flag does not exist. This function will return\\n# the correct invocation of curl depending on the version installed.\\ncurl_no_peer_verify() {\\n  curl_ver_regex='curl ([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)'\\n  [[ \\\"$(curl -V 2>\\/dev\\/null)\\\" =~ $curl_ver_regex ]]\\n  curl_majv=\\\"${BASH_REMATCH[1]-7}\\\"  # Default to 7  if no match\\n  curl_minv=\\\"${BASH_REMATCH[2]-10}\\\" # Default to 10 if no match\\n  if [[ \\\"$curl_majv\\\" -eq 7 && \\\"$curl_minv\\\" -le 9 ]] || [[ \\\"$curl_majv\\\" -lt 7 ]]; then\\n    curl_invocation=\\\"curl\\\"\\n  else\\n    curl_invocation=\\\"curl -k\\\"\\n  fi\\n\\n  $curl_invocation \\\"$@\\\"\\n}\\n\\n# Uses curl, or if not present, wget to download file from passed http url to a\\n# temporary location.\\n#\\n# Arguments\\n# 1. The url to download\\n# 2. The file to save it as\\n#\\n# Returns 0 or 1 if download fails.\\ndownload_from_url() {\\n    local t_url=\\\"${1?}\\\"\\n    local t_file=\\\"${2?}\\\"\\n\\n    if cmd curl; then\\n        # curl on AIX doesn't support -k, but it's the default behavior\\n        if [ \\\"$PLATFORM_NAME\\\" = \\\"aix\\\" ]; then\\n          CURL=\\\"curl_no_peer_verify\\\"\\n        else\\n          CURL=\\\"curl -k\\\"\\n        fi\\n        t_http_code=\\\"$($CURL --tlsv1 -sLo \\\"${t_file?}\\\" \\\"${t_url?}\\\" --write-out %{http_code} || fail \\\"curl failed to get ${t_url?}\\\")\\\"\\n    elif cmd wget; then\\n        # wget on AIX doesn't support SSL\\n        [ \\\"$PLATFORM_NAME\\\" = \\\"aix\\\" ] && fail \\\"Unable to download installation materials without curl\\\"\\n\\n        # Run wget and use awk to figure out the HTTP status.\\n        t_http_code=\\\"$(wget --secure-protocol=TLSv1 -O \\\"${t_file?}\\\" --no-check-certificate -S \\\"${t_url?}\\\" 2>&1 | awk '\\/HTTP\\\\\\/1.1\\/ { printf $2 }')\\\"\\n        if [ -z \\\"${t_file?}\\\" ]; then\\n            fail \\\"wget failed to get ${t_url?}\\\"\\n        fi\\n    else\\n        fail \\\"Unable to download installation materials without curl or wget\\\"\\n    fi\\n\\n    if [ \\\"${t_http_code?}\\\" == \\\"200\\\" ]; then\\n        return 0\\n    else\\n        return 1\\n    fi\\n}\\n\\nsupported_platform() {\\n  # Because of differences between how regex works between bash versions this\\n  # regex MUST be in a variable and passed into the test below UNQUOTED.\\n  t_supported_platform_regex='(el-(4|5|6|7)-(i386|x86_64|s390x))|(debian-(6|7|8)-(i386|amd64))|(ubuntu-(10\\\\.04|12\\\\.04|14\\\\.04|15\\\\.04|15\\\\.10|16\\\\.04)-(i386|amd64))|(sles-(10|11|12)-(i386|x86_64|s390x))|(fedora-(2[2-5])-(i386|x86_64))|(solaris-(10|11)-(i386|sparc))|(aix-(5\\\\.3|6\\\\.1|7\\\\.1)-power)|(osx-10\\\\.(9|10|11|12)-x86_64)'\\n  [[ \\\"${1?}\\\" =~ ${t_supported_platform_regex:?} ]]\\n}\\n\\nrun_agent_install_from_url() {\\n    t_agent_install_url=\\\"${1?}\\\"\\n\\n    t_install_file=$(mktempfile)\\n    if ! download_from_url \\\"${t_agent_install_url?}\\\" \\\"${t_install_file?}\\\"; then\\n        if supported_platform \\\"${PLATFORM_TAG?}\\\"; then\\n            fail \\\"The agent packages needed to support ${PLATFORM_TAG} are not present on your master. \\\\\\n    To add them, apply the pe_repo::platform::$(echo \\\"${PLATFORM_TAG?}\\\" | tr - _ | tr -dc '[:alnum:]_') class to your master node and then run Puppet. \\\\\\n    The required agent packages should be retrieved when puppet runs on the master, after which you can run the install.bash script again.\\\"\\n        else\\n            fail \\\"This method of agent installation is not supported for ${PLATFORM_TAG?} in Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\\"\\n        fi\\n    fi\\n\\n    bash \\\"${t_install_file?}\\\" \\\"${@: 2}\\\" || fail \\\"Error running install script ${t_install_file?}\\\"\\n}\\n\\n\\ninstall_agent() {\\n  doing_upgrade=$(is_upgrade)\\n\\n  cat <<REPO > \\/etc\\/yum.repos.d\\/pe_repo.repo\\n[pe_repo]\\nname=Puppet Labs PE Packages \\\\$releasever - \\\\$basearch\\nbaseurl=https:\\/\\/agent.demo.com:8140\\/packages\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64\\nenabled=1\\ngpgcheck=1\\nsslverify=False\\nproxy=_none_\\ngpgkey=https:\\/\\/agent.demo.com:8140\\/packages\\/GPG-KEY-puppetlabs\\n       https:\\/\\/agent.demo.com:8140\\/packages\\/GPG-KEY-puppet\\n\\nREPO\\n\\n  # To work around PE-13973, race condition leading to two mco services\\n  # running on upgrade, stop mco before upgrade, and then start it post\\n  # upgrade if mco was already running.\\n  if [ y = \\\"${doing_upgrade}\\\" ]; then\\n    mco_status=$(mcollective_status)\\n    pxp_status=$(pxp_agent_status)\\n    ensure_service \\\"$(mcollective_service_name)\\\" stopped\\n    ensure_service pxp-agent stopped\\n  fi\\n\\n  yum clean all --disablerepo=\\\"*\\\" --enablerepo=pe_repo\\n\\n  # PE-13928 Use update if already installed\\n  if yum list installed puppet-agent; then\\n    yum update -y puppet-agent\\n  else\\n    yum install -y puppet-agent\\n  fi\\n\\n  if [ ! y = \\\"${doing_upgrade}\\\" ]; then\\n    puppet_config_set \\\"$@\\\"\\n    start_puppet_agent\\n  else\\n    ensure_service mcollective \\\"${mco_status}\\\"\\n    ensure_service pxp-agent \\\"${pxp_status}\\\"\\n  fi\\n\\n  ensure_agent_links\\n}\\n\\nremove_repo_file() {\\n  rm -f '\\/etc\\/yum.repos.d\\/pe_repo.repo'\\n}\\n\\ntrap remove_repo_file EXIT\\n\\ninstall_agent \\\"$@\\\"\\n\",\n        \"backup\": false,\n        \"ensure\": \"file\",\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Pe_repo::Repo\",\n      \"title\": \"el-7-x86_64 2017.2.0-rc1-424-ge1372a3\",\n      \"tags\": [\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/el.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"agent_version\": \"1.10.0\",\n        \"installer_build\": \"el-7-x86_64\",\n        \"pe_version\": \"2017.2.0-rc1-424-ge1372a3\",\n        \"tarball_creates\": \"repodata\",\n        \"tarball_strip\": \"5\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"amq_augeas_base_beans_config\",\n      \"tags\": [\n        \"augeas\",\n        \"amq_augeas_base_beans_config\",\n        \"puppet_enterprise::amq::config::beans\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"beans\",\n        \"class\",\n        \"puppet_enterprise::amq::config\",\n        \"puppet_enterprise::amq\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/beans.pp\",\n      \"line\": 16,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"changes\": [\n          \"set #comment '\\n    Centrally managed by Puppet version 4.10.0\\n    For details on this file, visit https:\\/\\/puppet.com\\/docs\\/mcollective\\/current\\/deploy\\/middleware\\/activemq.html\\n'\",\n          \"set beans\\/#attribute\\/xmlns 'http:\\/\\/www.springframework.org\\/schema\\/beans'\",\n          \"set beans\\/#attribute\\/xmlns:amq 'http:\\/\\/activemq.apache.org\\/schema\\/core'\",\n          \"set beans\\/#attribute\\/xmlns:xsi 'http:\\/\\/www.w3.org\\/2001\\/XMLSchema-instance'\",\n          \"set beans\\/#attribute\\/xsi:schemaLocation 'http:\\/\\/www.springframework.org\\/schema\\/beans\\n    http:\\/\\/www.springframework.org\\/schema\\/beans\\/spring-beans-2.0.xsd\\n    http:\\/\\/activemq.apache.org\\/schema\\/core\\n    http:\\/\\/activemq.apache.org\\/schema\\/core\\/activemq-core.xsd\\n    http:\\/\\/activemq.apache.org\\/camel\\/schema\\/spring\\n    http:\\/\\/activemq.apache.org\\/camel\\/schema\\/spring\\/camel-spring.xsd'\",\n          \"set beans\\/bean[#attribute\\/class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer']\\/#attribute\\/class 'org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'\",\n          \"set beans\\/bean[#attribute\\/class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer']\\/property\\/#attribute\\/name 'locations'\",\n          \"set beans\\/bean[#attribute\\/class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer']\\/property\\/#attribute\\/name 'locations'\",\n          \"set beans\\/bean[#attribute\\/class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer']\\/property[#attribute\\/name='locations']\\/value\\/#text 'file:${activemq.base}\\/conf\\/credentials.properties'\"\n        ],\n        \"require\": \"File[\\/etc\\/puppetlabs\\/activemq\\/activemq.xml]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"localhost: AMQ broker: remove default localhost\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::broker\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"broker\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/broker.pp\",\n      \"line\": 68,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='localhost']\",\n        \"changes\": \"rm \\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='localhost']\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ broker: agent.demo.com\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::broker\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"broker\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/broker.pp\",\n      \"line\": 68,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\",\n        \"changes\": [\n          \"set #attribute\\/brokerName 'agent.demo.com'\",\n          \"set #attribute\\/xmlns 'http:\\/\\/activemq.apache.org\\/schema\\/core'\",\n          \"set #attribute\\/dataDirectory '${activemq.base}\\/data'\",\n          \"set #attribute\\/persistent 'false'\",\n          \"set #attribute\\/useJmx 'true'\",\n          \"set #attribute\\/schedulePeriodForDestinationPurge '300000'\"\n        ],\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ sslContext: agent.demo.com-ssl-context\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::ssl_context\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"ssl_context\",\n        \"agent.demo.com-ssl-context\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/ssl_context.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/sslContext\\/sslContext\",\n        \"changes\": [\n          \"set #attribute\\/keyStorePassword 'password123'\",\n          \"set #attribute\\/keyStore 'file:${activemq.base}\\/conf\\/broker.ks'\",\n          \"set #attribute\\/trustStorePassword 'password123'\",\n          \"set #attribute\\/trustStore 'file:${activemq.base}\\/conf\\/broker.ts'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ managementContext: agent.demo.com - managementContext\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::management_context\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"management_context\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/management_context.pp\",\n      \"line\": 56,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/managementContext\\/managementContext\",\n        \"changes\": [\n          \"set #attribute\\/createConnector 'false'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ destinationPolicyEntry: agent.demo.com-topic->\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::destination_policy_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"destination_policy_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/destination_policy_entry.pp\",\n      \"line\": 95,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/destinationPolicy\\/policyMap\\/policyEntries\\/policyEntry[#attribute\\/topic='>']\",\n        \"changes\": [\n          \"set #attribute\\/topic '>'\",\n          \"set #attribute\\/producerFlowControl 'false'\",\n          \"set #attribute\\/gcInactiveDestinations 'false'\",\n          \"set #attribute\\/memoryLimit '5mb'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ destinationPolicyEntry: agent.demo.com-queue->\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::destination_policy_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"destination_policy_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/destination_policy_entry.pp\",\n      \"line\": 95,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/destinationPolicy\\/policyMap\\/policyEntries\\/policyEntry[#attribute\\/queue='>']\",\n        \"changes\": [\n          \"set #attribute\\/queue '>'\",\n          \"set #attribute\\/producerFlowControl 'false'\",\n          \"set #attribute\\/gcInactiveDestinations 'false'\",\n          \"set #attribute\\/memoryLimit '20mb'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ destinationPolicyEntry: agent.demo.com-queue-*.reply.>\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::destination_policy_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"destination_policy_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/destination_policy_entry.pp\",\n      \"line\": 95,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/destinationPolicy\\/policyMap\\/policyEntries\\/policyEntry[#attribute\\/queue='*.reply.>']\",\n        \"changes\": [\n          \"set #attribute\\/queue '*.reply.>'\",\n          \"set #attribute\\/producerFlowControl 'true'\",\n          \"set #attribute\\/gcInactiveDestinations 'true'\",\n          \"set #attribute\\/inactiveTimoutBeforeGC '300000'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ transportConnector: agent.demo.com-openwire-transport\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::transport_connector\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"transport_connector\",\n        \"agent.demo.com-openwire-transport\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/transport_connector.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/transportConnectors\\/transportConnector[#attribute\\/name='openwire']\",\n        \"changes\": [\n          \"set #attribute\\/name 'openwire'\",\n          \"set #attribute\\/uri 'ssl:\\/\\/0.0.0.0:61616?transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ transportConnector: agent.demo.com-stomp-transport\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::transport_connector\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"transport_connector\",\n        \"agent.demo.com-stomp-transport\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/transport_connector.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/transportConnectors\\/transportConnector[#attribute\\/name='stomp+ssl']\",\n        \"changes\": [\n          \"set #attribute\\/name 'stomp+ssl'\",\n          \"set #attribute\\/uri 'stomp+ssl:\\/\\/0.0.0.0:61613?transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ statisticsBrokerPlugin: agent.demo.com-statisticsBrokerPlugin\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::statistics_broker_plugin\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"statistics_broker_plugin\",\n        \"agent.demo.com-statisticsbrokerplugin\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/statistics_broker_plugin.pp\",\n      \"line\": 23,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/statisticsBrokerPlugin\",\n        \"changes\": \"set \\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/statisticsBrokerPlugin ''\",\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ timeStampingBrokerPlugin: agent.demo.com-timeStampingBrokerPlugin\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::timestamping_broker_plugin\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"timestamping_broker_plugin\",\n        \"agent.demo.com-timestampingbrokerplugin\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/timestamping_broker_plugin.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/timeStampingBrokerPlugin\",\n        \"changes\": [\n          \"set #attribute\\/futureOnly 'false'\",\n          \"set #attribute\\/ttlCeiling '0'\",\n          \"set #attribute\\/zeroExpirationOverride '30000'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ simpleAuthentication user: agent.demo.com-simple_auth_user-mcollective\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::simple_authentication_user\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"simple_authentication_user\",\n        \"agent.demo.com-simple_auth_user-mcollective\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/simple_authentication_user.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/simpleAuthenticationPlugin\\/users\\/authenticationUser[#attribute\\/username='mcollective']\",\n        \"changes\": [\n          \"set #attribute\\/username 'mcollective'\",\n          \"set #attribute\\/password 'password123'\",\n          \"set #attribute\\/groups 'mcollective,admins,everyone'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-queue->\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/authorization_plugin_entry.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/authorizationPlugin\\/map\\/authorizationMap\\/authorizationEntries\\/authorizationEntry[#attribute\\/queue='>']\",\n        \"changes\": [\n          \"set #attribute\\/queue '>'\",\n          \"set #attribute\\/admin 'admins'\",\n          \"set #attribute\\/read 'admins'\",\n          \"set #attribute\\/write 'admins'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-topic->\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/authorization_plugin_entry.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/authorizationPlugin\\/map\\/authorizationMap\\/authorizationEntries\\/authorizationEntry[#attribute\\/topic='>']\",\n        \"changes\": [\n          \"set #attribute\\/topic '>'\",\n          \"set #attribute\\/admin 'admins'\",\n          \"set #attribute\\/read 'admins'\",\n          \"set #attribute\\/write 'admins'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-queue-mcollective.>\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/authorization_plugin_entry.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/authorizationPlugin\\/map\\/authorizationMap\\/authorizationEntries\\/authorizationEntry[#attribute\\/queue='mcollective.>']\",\n        \"changes\": [\n          \"set #attribute\\/queue 'mcollective.>'\",\n          \"set #attribute\\/admin 'mcollective'\",\n          \"set #attribute\\/read 'mcollective'\",\n          \"set #attribute\\/write 'mcollective'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-topic-mcollective.>\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/authorization_plugin_entry.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/authorizationPlugin\\/map\\/authorizationMap\\/authorizationEntries\\/authorizationEntry[#attribute\\/topic='mcollective.>']\",\n        \"changes\": [\n          \"set #attribute\\/topic 'mcollective.>'\",\n          \"set #attribute\\/admin 'mcollective'\",\n          \"set #attribute\\/read 'mcollective'\",\n          \"set #attribute\\/write 'mcollective'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-topic-ActiveMQ.Advisory.>\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::authorization_plugin_entry\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"authorization_plugin_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/authorization_plugin_entry.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/plugins\\/authorizationPlugin\\/map\\/authorizationMap\\/authorizationEntries\\/authorizationEntry[#attribute\\/topic='ActiveMQ.Advisory.>']\",\n        \"changes\": [\n          \"set #attribute\\/topic 'ActiveMQ.Advisory.>'\",\n          \"set #attribute\\/admin 'everyone'\",\n          \"set #attribute\\/read 'everyone'\",\n          \"set #attribute\\/write 'everyone'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"AMQ webConsole: agent.demo.com - web console - false\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::web_console\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"web_console\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/web_console.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/import[#attribute\\/resource='jetty.xml']\",\n        \"changes\": \"rm \\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/import[#attribute\\/resource='jetty.xml']\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"agent.demo.com: AMQ systemUsage: agent.demo.com - systemusage\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::amq::config::system_usage\",\n        \"puppet_enterprise\",\n        \"amq\",\n        \"config\",\n        \"system_usage\",\n        \"class\",\n        \"puppet_enterprise::profile::amq::broker\",\n        \"profile\",\n        \"broker\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/amq\\/config\\/system_usage.pp\",\n      \"line\": 43,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\",\n        \"lens\": \"ActiveMQ_XML.lns\",\n        \"context\": \"\\/files\\/etc\\/puppetlabs\\/activemq\\/activemq.xml\\/beans\\/broker[#attribute\\/brokerName='agent.demo.com']\\/systemUsage\\/systemUsage\",\n        \"changes\": [\n          \"set memoryUsage\\/memoryUsage\\/#attribute\\/limit '200mb'\",\n          \"set storeUsage\\/storeUsage\\/#attribute\\/limit '1gb'\",\n          \"set tempUsage\\/tempUsage\\/#attribute\\/limit '1gb'\"\n        ],\n        \"require\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n        \"notify\": \"Service[pe-activemq]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n      \"tags\": [\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 22,\n      \"exported\": false,\n      \"parameters\": {\n        \"notify\": \"Exec[pe-puppetserver service full restart]\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"mode\": \"0644\",\n        \"warn\": false,\n        \"force\": false,\n        \"backup\": \"puppet\",\n        \"replace\": true,\n        \"order\": \"alpha\",\n        \"ensure_newline\": false\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver certificate-authority-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.ca.certificate-authority-service\\/certificate-authority-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/init.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"replace\": false,\n        \"ensure\": \"present\",\n        \"mode\": \"0644\",\n        \"warn\": false,\n        \"force\": false,\n        \"backup\": \"puppet\",\n        \"order\": \"alpha\",\n        \"ensure_newline\": false\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"00_header_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/init.pp\",\n      \"line\": 14,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"content\": \"authorization: {\\n  rules: []\\n\",\n        \"order\": \"10\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"99_footer_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/init.pp\",\n      \"line\": 21,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"content\": \"}\\n\",\n        \"order\": \"10\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"authorization.version.\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/init.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"setting\": \"authorization.version\",\n        \"value\": 1,\n        \"require\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"authorization.allow-header-cert-info.\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/init.pp\",\n      \"line\": 34,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"setting\": \"authorization.allow-header-cert-info\",\n        \"value\": false,\n        \"require\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs catalog\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"^\\/puppet\\/v3\\/catalog\\/([^\\/]+)$\",\n            \"type\": \"regex\",\n            \"query-params\": {\n              \n            },\n            \"method\": [\n              \"get\",\n              \"post\"\n            ]\n          },\n          \"allow\": \"$1\",\n          \"name\": \"puppetlabs catalog\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs certificate\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet-ca\\/v1\\/certificate\\/\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow-unauthenticated\": true,\n          \"name\": \"puppetlabs certificate\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs crl\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet-ca\\/v1\\/certificate_revocation_list\\/ca\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow-unauthenticated\": true,\n          \"name\": \"puppetlabs crl\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs csr\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet-ca\\/v1\\/certificate_request\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": [\n              \"get\",\n              \"put\"\n            ]\n          },\n          \"allow-unauthenticated\": true,\n          \"name\": \"puppetlabs csr\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs environments\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/environments\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow\": \"*\",\n          \"name\": \"puppetlabs environments\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs environment\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/environment\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow\": \"agent.demo.com\",\n          \"name\": \"puppetlabs environment\",\n          \"sort-order\": 510\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs environment classes\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/environment_classes\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow\": \"agent.demo.com\",\n          \"name\": \"puppetlabs environment classes\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs file\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/file\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            }\n          },\n          \"allow\": \"*\",\n          \"name\": \"puppetlabs file\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs node\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"^\\/puppet\\/v3\\/node\\/([^\\/]+)$\",\n            \"type\": \"regex\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow\": \"$1\",\n          \"name\": \"puppetlabs node\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs report\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"^\\/puppet\\/v3\\/report\\/([^\\/]+)$\",\n            \"type\": \"regex\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"put\"\n          },\n          \"allow\": \"$1\",\n          \"name\": \"puppetlabs report\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs resource type\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/resource_type\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow\": [\n            \"agent.demo.com\",\n            \"agent.demo.com\",\n            \"agent.demo.com\"\n          ],\n          \"name\": \"puppetlabs resource type\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs status\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/status\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow-unauthenticated\": true,\n          \"name\": \"puppetlabs status\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs static file content\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/v3\\/static_file_content\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow\": \"*\",\n          \"name\": \"puppetlabs static file content\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs experimental\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet\\/experimental\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"get\"\n          },\n          \"allow-unauthenticated\": true,\n          \"name\": \"puppetlabs experimental\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs deny all\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            }\n          },\n          \"deny\": \"*\",\n          \"name\": \"puppetlabs deny all\",\n          \"sort-order\": 999\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver jetty9-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\\/jetty9-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver pe-master-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-master-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.enterprise.services.master.master-service\\/pe-master-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver request-handler-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"request-handler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.request-handler.request-handler-service\\/request-handler-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver jruby-puppet-pooled-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"jruby-puppet-pooled-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.jruby.jruby-puppet-service\\/jruby-puppet-pooled-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver jruby-pool-manager-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"jruby-pool-manager-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service\\/jruby-pool-manager-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver metrics-puppet-profiler-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"metrics-puppet-profiler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.enterprise.services.puppet-profiler.puppet-profiler-service\\/metrics-puppet-profiler-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver metrics-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"metrics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver puppet-server-config-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet-server-config-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.config.puppet-server-config-service\\/puppet-server-config-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver puppet-admin-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet-admin-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.puppet-admin.puppet-admin-service\\/puppet-admin-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver webrouting-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"webrouting-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\\/webrouting-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver pe-legacy-routes-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-legacy-routes-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.enterprise.services.legacy-routes.pe-legacy-routes-service\\/pe-legacy-routes-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver status-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"status-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver authorization-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"authorization-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.authorization.authorization-service\\/authorization-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver scheduler-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"scheduler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\\/scheduler-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver pe-jruby-metrics-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-jruby-metrics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.enterprise.services.jruby.pe-jruby-metrics-service\\/pe-jruby-metrics-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver analytics-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"analytics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.enterprise.services.analytics.analytics-service\\/analytics-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.client-auth\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.client-auth\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.client-auth\",\n        \"value\": \"want\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-host\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.ssl-host\",\n        \"value\": \"0.0.0.0\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-port\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.ssl-port\",\n        \"value\": 8140,\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-crl-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-crl-path\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.puppet-server.ssl-crl-path\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"ensure\": \"present\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.access-log-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.access-log-config\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 67,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.puppet-server.access-log-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppetserver\\/request-logging.xml\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.max-threads\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.max-threads\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.puppet-server.max-threads\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.request-header-max-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.request-header-max-size\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 89,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.puppet-server.request-header-max-size\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.default-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.default-server\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.puppet-server.default-server\",\n        \"value\": true,\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"puppetserver.webserver.puppet-server.ssl-protocols\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"puppetserver.webserver.puppet-server.ssl-protocols\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"puppet-server\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.puppet-server.ssl-protocols\",\n        \"type\": \"array\",\n        \"value\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Service[pe-puppetserver]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs environment cache\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet-admin-api\\/v1\\/environment-cache\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"delete\"\n          },\n          \"allow\": [\n            \"agent.demo.com\"\n          ],\n          \"name\": \"puppetlabs environment cache\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs jruby pool\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet-admin-api\\/v1\\/jruby-pool\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": \"delete\"\n          },\n          \"allow\": [\n            \"agent.demo.com\"\n          ],\n          \"name\": \"puppetlabs jruby pool\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'Xmx'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xmx\",\n        \"value\": \"2048m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'Xms'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xms\",\n        \"value\": \"2048m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'XX:+PrintGCDetails'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDetails\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'XX:+PrintGCDateStamps'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDateStamps\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'Xloggc:'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xloggc:\",\n        \"value\": \"\\/var\\/log\\/puppetlabs\\/puppetserver\\/puppetserver_gc.log\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'XX:+UseGCLogFileRotation'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+UseGCLogFileRotation\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'XX:NumberOfGCLogFiles='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:NumberOfGCLogFiles=\",\n        \"value\": \"16\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetserver_'XX:GCLogFileSize='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:GCLogFileSize=\",\n        \"value\": \"64m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf java_bin\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"JAVA_BIN\",\n        \"value\": \"\\\"\\/opt\\/puppetlabs\\/server\\/bin\\/java\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf user\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"USER\",\n        \"value\": \"pe-puppet\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf group\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 30,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"GROUP\",\n        \"value\": \"pe-puppet\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf install_dir\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"INSTALL_DIR\",\n        \"value\": \"\\\"\\/opt\\/puppetlabs\\/server\\/apps\\/puppetserver\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf config\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"CONFIG\",\n        \"value\": \"\\\"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf bootstrap_config\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 45,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"BOOTSTRAP_CONFIG\",\n        \"value\": \"\\\"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf service_stop_retries\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 50,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"SERVICE_STOP_RETRIES\",\n        \"value\": 60,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetserver initconf start_timeout\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"START_TIMEOUT\",\n        \"value\": 300,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetserver\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetserver]\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pe-puppetserver\",\n      \"tags\": [\n        \"service\",\n        \"pe-puppetserver\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 10,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true,\n        \"restart\": \"service pe-puppetserver reload\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe-puppetserver service full restart\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"puppetserver\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"service pe-puppetserver restart\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"refreshonly\": true,\n        \"before\": \"Service[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"fileserver.conf pe_packages\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::fileserver_conf\",\n        \"puppet_enterprise\",\n        \"fileserver_conf\",\n        \"pe_packages\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/fileserver_conf.pp\",\n      \"line\": 18,\n      \"exported\": false,\n      \"parameters\": {\n        \"changes\": [\n          \"set \\/files\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\\/pe_packages\\/path \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\",\n          \"set \\/files\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\\/pe_packages\\/allow *\"\n        ],\n        \"incl\": \"\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\",\n        \"load_path\": \"\\/opt\\/puppetlabs\\/puppet\\/share\\/augeas\\/lenses\\/dist\",\n        \"lens\": \"PuppetFileserver.lns\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"puppetserver versioned-code-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"versioned-code-service\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.services.versioned-code-service.versioned-code-service\\/versioned-code-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-puppetserver service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs file sync api\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"undef\",\n            \"type\": \"undef\",\n            \"query-params\": {\n              \n            }\n          },\n          \"allow-unauthenticated\": false,\n          \"name\": \"puppetlabs file sync api\",\n          \"sort-order\": 200\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs file sync repo\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"undef\",\n            \"type\": \"undef\",\n            \"query-params\": {\n              \n            }\n          },\n          \"allow-unauthenticated\": false,\n          \"name\": \"puppetlabs file sync repo\",\n          \"sort-order\": 200\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-puppetlabs certificate status\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/puppet-ca\\/v1\\/certificate_status\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            },\n            \"method\": [\n              \"get\",\n              \"put\",\n              \"delete\"\n            ]\n          },\n          \"allow\": [\n            \"agent.demo.com\"\n          ],\n          \"name\": \"puppetlabs certificate status\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"fileserver.conf pe_modules\",\n      \"tags\": [\n        \"augeas\",\n        \"puppet_enterprise::fileserver_conf\",\n        \"puppet_enterprise\",\n        \"fileserver_conf\",\n        \"pe_modules\",\n        \"class\",\n        \"puppet_enterprise::profile::certificate_authority\",\n        \"profile\",\n        \"certificate_authority\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/fileserver_conf.pp\",\n      \"line\": 18,\n      \"exported\": false,\n      \"parameters\": {\n        \"changes\": [\n          \"set \\/files\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\\/pe_modules\\/path \\/opt\\/puppetlabs\\/server\\/share\\/installer\\/modules\",\n          \"set \\/files\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\\/pe_modules\\/allow *\"\n        ],\n        \"incl\": \"\\/etc\\/puppetlabs\\/puppet\\/fileserver.conf\",\n        \"load_path\": \"\\/opt\\/puppetlabs\\/puppet\\/share\\/augeas\\/lenses\\/dist\",\n        \"lens\": \"PuppetFileserver.lns\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0600\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"before\": \"Exec[pe-console-services service full restart]\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"mode\": \"0400\",\n        \"before\": \"Exec[pe-console-services service full restart]\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"mode\": \"0400\",\n        \"before\": \"Exec[pe-console-services service full restart]\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.public_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 75,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/public_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"mode\": \"0400\",\n        \"before\": \"Exec[pe-console-services service full restart]\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs::Pk8_cert\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 82,\n      \"exported\": false,\n      \"parameters\": {\n        \"pem_file\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"mode\": \"0400\",\n        \"container\": \"console-services\",\n        \"require\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs]\",\n        \"pk8_file\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.client-auth\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.client-auth\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.client-auth\",\n        \"value\": \"none\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-host\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.ssl-host\",\n        \"value\": \"0.0.0.0\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-port\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.ssl-port\",\n        \"value\": 4431,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.ssl-cert\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.ssl-key\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-crl-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-crl-path\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.console.ssl-crl-path\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.access-log-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.access-log-config\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 67,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.console.access-log-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/console-services\\/request-logging.xml\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.max-threads\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.max-threads\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.console.max-threads\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.request-header-max-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.request-header-max-size\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 89,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.console.request-header-max-size\",\n        \"value\": 65536,\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.default-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.default-server\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.console.default-server\",\n        \"value\": true,\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.console.ssl-protocols\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.console.ssl-protocols\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"console\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.console.ssl-protocols\",\n        \"type\": \"array\",\n        \"value\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.client-auth\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.client-auth\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.client-auth\",\n        \"value\": \"want\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-host\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.ssl-host\",\n        \"value\": \"0.0.0.0\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-port\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.ssl-port\",\n        \"value\": 4433,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.ssl-cert\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.ssl-key\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-crl-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-crl-path\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.api.ssl-crl-path\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.access-log-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.access-log-config\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 67,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.api.access-log-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/console-services\\/request-logging-api.xml\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.max-threads\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.max-threads\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.api.max-threads\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.request-header-max-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.request-header-max-size\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 89,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.api.request-header-max-size\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.default-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.default-server\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.api.default-server\",\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.webserver.api.ssl-protocols\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.webserver.api.ssl-protocols\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"api\",\n        \"class\",\n        \"puppet_enterprise::profile::console::console_services_config\",\n        \"profile\",\n        \"console\",\n        \"console_services_config\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.api.ssl-protocols\",\n        \"type\": \"array\",\n        \"value\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"notify\": \"Service[pe-console-services]\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 34,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.rbac-base-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.rbac-base-url\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity.conf\",\n        \"setting\": \"activity.rbac-base-url\",\n        \"value\": \"http:\\/\\/127.0.0.1:4432\\/rbac-api\\/v1\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.cors-origin-pattern\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.cors-origin-pattern\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity.conf\",\n        \"setting\": \"activity.cors-origin-pattern\",\n        \"value\": \".*\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 54,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.subprotocol\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.subprotocol\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.subprotocol\",\n        \"value\": \"postgresql\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.subname\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.subname\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 62,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.subname\",\n        \"value\": \"\\/\\/agent.demo.com:5432\\/pe-activity?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8&sslcert=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.user\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 67,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.user\",\n        \"value\": \"pe-activity-write\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.migration-user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.migration-user\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 72,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.migration-user\",\n        \"value\": \"pe-activity\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.maximum-pool-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.maximum-pool-size\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 91,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.maximum-pool-size\",\n        \"value\": 10,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.connection-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.connection-timeout\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 98,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.connection-timeout\",\n        \"value\": 30000,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"activity.database.connection-check-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"activity.database.connection-check-timeout\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 105,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf\",\n        \"setting\": \"activity.database.connection-check-timeout\",\n        \"value\": 5000,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:activity activity-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.activity.services\",\n        \"service\": \"activity-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:activity jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/activity.pp\",\n      \"line\": 117,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.certificate-whitelist\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.certificate-whitelist\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.certificate-whitelist\",\n        \"value\": \"\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.token-private-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.token-private-key\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.token-private-key\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.token-public-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.token-public-key\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.token-public-key\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.token-maximum-lifetime\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.token-maximum-lifetime\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 63,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.token-maximum-lifetime\",\n        \"value\": \"10y\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.password-reset-expiration\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.password-reset-expiration\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 76,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.password-reset-expiration\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.session-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.session-timeout\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 83,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.session-timeout\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.token-auth-lifetime\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.token-auth-lifetime\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 96,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.token-auth-lifetime\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.ds-trust-chain\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.ds-trust-chain\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 109,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.ds-trust-chain\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.failed-attempts-lockout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.failed-attempts-lockout\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 122,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf\",\n        \"setting\": \"rbac.failed-attempts-lockout\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 135,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.subprotocol\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.subprotocol\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 138,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.subprotocol\",\n        \"value\": \"postgresql\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.subname\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.subname\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 143,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.subname\",\n        \"value\": \"\\/\\/agent.demo.com:5432\\/pe-rbac?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8&sslcert=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.user\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 148,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.user\",\n        \"value\": \"pe-rbac-write\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.migration-user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.migration-user\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 153,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.migration-user\",\n        \"value\": \"pe-rbac\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.maximum-pool-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.maximum-pool-size\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 172,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.maximum-pool-size\",\n        \"value\": 10,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.connection-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.connection-timeout\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 179,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.connection-timeout\",\n        \"value\": 30000,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.rbac.database.connection-check-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.rbac.database.connection-check-timeout\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 186,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf\",\n        \"setting\": \"rbac.database.connection-check-timeout\",\n        \"value\": 5000,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:rbac rbac-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 192,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.rbac\",\n        \"service\": \"rbac-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:rbac rbac-storage-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 198,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.storage.permissioned\",\n        \"service\": \"rbac-storage-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:rbac rbac-http-api-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 204,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.http.api\",\n        \"service\": \"rbac-http-api-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:rbac rbac-authn-middleware\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 210,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.http.middleware\",\n        \"service\": \"rbac-authn-middleware\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:rbac activity-reporting-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 216,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.activity.services\",\n        \"service\": \"activity-reporting-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:rbac jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/rbac.pp\",\n      \"line\": 222,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Pe_file_line\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist:agent.demo.com\",\n      \"tags\": [\n        \"pe_file_line\",\n        \"puppet_enterprise::certs::whitelist_entry\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"whitelist_entry\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/whitelist_entry.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist\",\n        \"line\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.puppet-master\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.puppet-master\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.puppet-master\",\n        \"value\": \"https:\\/\\/agent.demo.com:8140\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.ssl-key\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 60,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.ssl-cert\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.synchronization-period\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.synchronization-period\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.synchronization-period\",\n        \"value\": 600,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.prune-days-threshold\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.prune-days-threshold\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 76,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.prune-days-threshold\",\n        \"value\": 7,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.node-check-in-storage\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.node-check-in-storage\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 81,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf\",\n        \"setting\": \"classifier.node-check-in-storage\",\n        \"value\": false,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier-database.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 93,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"backup\": false,\n        \"mode\": \"0640\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"classifier.database.subprotocol\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"classifier.database.subprotocol\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 96,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier-database.conf\",\n        \"setting\": \"classifier.database.subprotocol\",\n        \"value\": \"postgresql\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.database.subname\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.database.subname\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier-database.conf\",\n        \"setting\": \"classifier.database.subname\",\n        \"value\": \"\\/\\/agent.demo.com:5432\\/pe-classifier?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8&sslcert=\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.database.user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.database.user\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 106,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier-database.conf\",\n        \"setting\": \"classifier.database.user\",\n        \"value\": \"pe-classifier-write\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.classifier.database.migration-user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.classifier.database.migration-user\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier-database.conf\",\n        \"setting\": \"classifier.database.migration-user\",\n        \"value\": \"pe-classifier\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:classifier classifier-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 130,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.classifier.main\",\n        \"service\": \"classifier-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:classifier activity-reporting-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 136,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.activity.services\",\n        \"service\": \"activity-reporting-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:classifier jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/classifier.pp\",\n      \"line\": 142,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 74,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.assets-dir\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.assets-dir\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 81,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.assets-dir\",\n        \"value\": \"dist\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.puppet-master\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.puppet-master\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 86,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.puppet-master\",\n        \"value\": \"https:\\/\\/agent.demo.com:8140\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.rbac-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.rbac-server\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 91,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.rbac-server\",\n        \"value\": \"http:\\/\\/127.0.0.1:4432\\/rbac-api\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.classifier-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.classifier-server\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 96,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.classifier-server\",\n        \"value\": \"http:\\/\\/127.0.0.1:4432\\/classifier-api\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.activity-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.activity-server\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.activity-server\",\n        \"value\": \"http:\\/\\/127.0.0.1:4432\\/activity-api\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.orchestrator-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.orchestrator-server\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 106,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.orchestrator-server\",\n        \"value\": \"https:\\/\\/agent.demo.com:8143\\/orchestrator\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.display-local-time\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.display-local-time\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.display-local-time\",\n        \"value\": false,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.session-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.session-timeout\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 123,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.session-timeout\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.session-maximum-lifetime\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.session-maximum-lifetime\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 137,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.session-maximum-lifetime\",\n        \"value\": \"\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.puppetdb-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.puppetdb-server\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 147,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.puppetdb-server\",\n        \"value\": \"https:\\/\\/agent.demo.com:8081\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.certs.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.certs.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 152,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.certs.ssl-key\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.certs.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.certs.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 157,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.certs.ssl-cert\",\n        \"value\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.certs.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.certs.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 162,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.certs.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.proxy-idle-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.proxy-idle-timeout\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 175,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.proxy-idle-timeout\",\n        \"value\": 60,\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.license-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.license-key\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 182,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.license-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/license.key\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.pcp-request-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.pcp-request-timeout\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 188,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.pcp-request-timeout\",\n        \"value\": 5,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.service-alert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.service-alert\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 280,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.service-alert\",\n        \"value\": [\n          {\n            \"type\": \"activity\",\n            \"url\": \"https:\\/\\/agent.demo.com:4433\",\n            \"replication_mode\": \"none\"\n          },\n          {\n            \"type\": \"classifier\",\n            \"url\": \"https:\\/\\/agent.demo.com:4433\",\n            \"replication_mode\": \"none\"\n          },\n          {\n            \"type\": \"master\",\n            \"url\": \"https:\\/\\/agent.demo.com:8140\",\n            \"replication_mode\": \"none\"\n          },\n          {\n            \"type\": \"orchestrator\",\n            \"url\": \"https:\\/\\/agent.demo.com:8143\",\n            \"replication_mode\": \"none\"\n          },\n          {\n            \"type\": \"puppetdb\",\n            \"url\": \"https:\\/\\/agent.demo.com:8081\",\n            \"replication_mode\": \"none\"\n          },\n          {\n            \"type\": \"rbac\",\n            \"url\": \"https:\\/\\/agent.demo.com:4433\",\n            \"replication_mode\": \"none\"\n          }\n        ],\n        \"type\": \"array\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.service-alert-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.service-alert-timeout\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 287,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.service-alert-timeout\",\n        \"value\": 5000,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"console-services.console.no-longer-reporting-cutoff\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"console-services.console.no-longer-reporting-cutoff\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 298,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf\",\n        \"setting\": \"console.no-longer-reporting-cutoff\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console webrouting-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 305,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\",\n        \"service\": \"webrouting-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 311,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.rbac\",\n        \"service\": \"rbac-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-authn-middleware\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 316,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.http.middleware\",\n        \"service\": \"rbac-authn-middleware\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-consumer-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 321,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.consumer\",\n        \"service\": \"rbac-consumer-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-status-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 327,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.status\",\n        \"service\": \"rbac-status-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-storage-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 333,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.storage.permissioned\",\n        \"service\": \"rbac-storage-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-authn-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 339,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.authn\",\n        \"service\": \"rbac-authn-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console rbac-authz-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 345,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.rbac.services.authz\",\n        \"service\": \"rbac-authz-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console pe-console-ui-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 356,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.pe-console-ui.service\",\n        \"service\": \"pe-console-ui-service\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console pe-console-auth-ui-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 363,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.pe-console-auth-ui.service\",\n        \"service\": \"pe-console-auth-ui-service\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 370,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console status-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 376,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.status.status-service\",\n        \"service\": \"status-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console scheduler-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 383,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\",\n        \"service\": \"scheduler-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console metrics-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 397,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\",\n        \"service\": \"metrics-service\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"console-services:console metrics-webservice\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/console_services.pp\",\n      \"line\": 403,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"console-services\",\n        \"namespace\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\",\n        \"service\": \"metrics-webservice\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'Xmx'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xmx\",\n        \"value\": \"256m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'Xms'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xms\",\n        \"value\": \"256m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'XX:+PrintGCDetails'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDetails\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'XX:+PrintGCDateStamps'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDateStamps\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'Xloggc:'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xloggc:\",\n        \"value\": \"\\/var\\/log\\/puppetlabs\\/console-services\\/console-services_gc.log\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'XX:+UseGCLogFileRotation'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+UseGCLogFileRotation\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'XX:NumberOfGCLogFiles='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:NumberOfGCLogFiles=\",\n        \"value\": \"16\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-console-services_'XX:GCLogFileSize='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:GCLogFileSize=\",\n        \"value\": \"64m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf java_bin\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"JAVA_BIN\",\n        \"value\": \"\\\"\\/opt\\/puppetlabs\\/server\\/bin\\/java\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf user\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"USER\",\n        \"value\": \"pe-console-services\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf group\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 30,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"GROUP\",\n        \"value\": \"pe-console-services\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf install_dir\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"INSTALL_DIR\",\n        \"value\": \"\\\"\\/opt\\/puppetlabs\\/server\\/apps\\/console-services\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf config\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"CONFIG\",\n        \"value\": \"\\\"\\/etc\\/puppetlabs\\/console-services\\/conf.d\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf bootstrap_config\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 45,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"BOOTSTRAP_CONFIG\",\n        \"value\": \"\\\"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf service_stop_retries\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 50,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"SERVICE_STOP_RETRIES\",\n        \"value\": 60,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"console-services initconf start_timeout\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"START_TIMEOUT\",\n        \"value\": 300,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-console-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pe-console-services\",\n      \"tags\": [\n        \"service\",\n        \"pe-console-services\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 10,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true,\n        \"restart\": \"service pe-console-services reload\",\n        \"require\": \"Package[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe-console-services service full restart\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"service pe-console-services restart\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"refreshonly\": true,\n        \"before\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for gzip\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\\/http\",\n        \"changes\": [\n          \"set gzip 'on'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for gzip_comp_level\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_comp_level\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\\/http\",\n        \"changes\": [\n          \"set gzip_comp_level '5'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for gzip_min_length\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_min_length\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\\/http\",\n        \"changes\": [\n          \"set gzip_min_length '256'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for gzip_proxied\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_proxied\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\\/http\",\n        \"changes\": [\n          \"set gzip_proxied 'any'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for gzip_vary\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_vary\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\\/http\",\n        \"changes\": [\n          \"set gzip_vary 'on'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for gzip_types\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"gzip_types\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"nginx_conf\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/nginx.conf\\/http\",\n        \"changes\": [\n          \"set gzip_types 'application\\/atom+xml\\napplication\\/javascript\\napplication\\/json\\napplication\\/ld+json\\napplication\\/manifest+json\\napplication\\/rss+xml\\napplication\\/vnd.geo+json\\napplication\\/vnd.ms-fontobject\\napplication\\/x-font-ttf\\napplication\\/x-web-app-manifest+json\\napplication\\/xhtml+xml\\napplication\\/xml\\nfont\\/opentype\\nimage\\/bmp\\nimage\\/svg+xml\\nimage\\/x-icon\\ntext\\/cache-manifest\\ntext\\/css\\ntext\\/plain\\ntext\\/vcard\\ntext\\/vnd.rim.location.xloc\\ntext\\/vtt\\ntext\\/x-component\\ntext\\/x-cross-domain-policy\\ntext\\/javascript'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for server_name\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"server_name\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set server_name 'agent.demo.com'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for listen\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"listen\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set listen '443 ssl'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_certificate\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_certificate\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_certificate '\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_certificate_key\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_certificate_key\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_certificate_key '\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_crl\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_crl\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_crl '\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_prefer_server_ciphers\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_prefer_server_ciphers\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_prefer_server_ciphers 'on'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_ciphers\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_ciphers\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_protocols\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_protocols\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_protocols 'TLSv1 TLSv1.1 TLSv1.2'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_dhparam\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_dhparam\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_dhparam '\\/etc\\/puppetlabs\\/nginx\\/dhparam_puppetproxy.pem'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_verify_client\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_verify_client\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_verify_client 'off'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_verify_depth\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_verify_depth\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_verify_depth '1'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_session_timeout\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_session_timeout\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_session_timeout '1d'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for ssl_session_cache\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"ssl_session_cache\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set ssl_session_cache 'shared:SSL:50m'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_pass\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"proxy_pass\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_pass 'http:\\/\\/127.0.0.1:4430'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_redirect\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"proxy_redirect\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_redirect 'http:\\/\\/127.0.0.1:4430 \\/'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_read_timeout\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"proxy_read_timeout\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_read_timeout '120'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_set_header x-ssl-subject\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_set_header[.='X-SSL-Subject $ssl_client_s_dn'] 'X-SSL-Subject $ssl_client_s_dn'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_set_header x-client-dn\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_set_header[.='X-Client-DN $ssl_client_s_dn'] 'X-Client-DN $ssl_client_s_dn'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_set_header x-client-verify\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_set_header[.='X-Client-Verify $ssl_client_verify'] 'X-Client-Verify $ssl_client_verify'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for proxy_set_header x-forwarded-for\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf\\/server[server_name='agent.demo.com']\\/location[#uri='\\/']\",\n        \"changes\": [\n          \"set #uri '\\/'\",\n          \"set proxy_set_header[.='X-Forwarded-For $proxy_add_x_forwarded_for'] 'X-Forwarded-For $proxy_add_x_forwarded_for'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for http_redirect-server_name\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"http_redirect-server_name\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set server_name 'agent.demo.com'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for http_redirect-listen\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"http_redirect-listen\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set listen '80'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"Augeas\",\n      \"title\": \"pe_nginx::directive for http_redirect-return\",\n      \"tags\": [\n        \"augeas\",\n        \"pe_nginx::directive\",\n        \"pe_nginx\",\n        \"directive\",\n        \"http_redirect-return\",\n        \"class\",\n        \"puppet_enterprise::profile::console::proxy::http_redirect\",\n        \"puppet_enterprise\",\n        \"profile\",\n        \"console\",\n        \"proxy\",\n        \"http_redirect\",\n        \"puppet_enterprise::profile::console::proxy\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_nginx\\/manifests\\/directive.pp\",\n      \"line\": 145,\n      \"exported\": false,\n      \"parameters\": {\n        \"incl\": \"\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\",\n        \"lens\": \"Nginx.lns\",\n        \"context\": \"\\/files\\/\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf\\/server[server_name='agent.demo.com']\",\n        \"changes\": [\n          \"set return '301 https:\\/\\/$server_name$request_uri'\"\n        ],\n        \"notify\": \"Service[pe-nginx]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-mcollective-servers.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-mcollective-servers.cert.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-mcollective-servers\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 30,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/pe-internal-mcollective-servers.pem\",\n        \"content\": \"-----BEGIN CERTIFICATE-----\\nMIIFzzCCA7egAwIBAgIBAzANBgkqhkiG9w0BAQsFADBtMWswaQYDVQQDDGJQdXBw\\nZXQgRW50ZXJwcmlzZSBDQSBnZW5lcmF0ZWQgb24gcGVnbGlzYW4tbGF0ZXN0LmVu\\nZy5wdXBwZXRsYWJzLm5ldCBhdCArMjAxNy0wNC0wNSAxMToyODoyNCAtMDcwMDAe\\nFw0xNzA0MDQxODI5MTVaFw0yMjA0MDQxODI5MTVaMCoxKDAmBgNVBAMMH3BlLWlu\\ndGVybmFsLW1jb2xsZWN0aXZlLXNlcnZlcnMwggIiMA0GCSqGSIb3DQEBAQUAA4IC\\nDwAwggIKAoICAQC3PCPL2d\\/BJ008dTYn0g6JGtsqA2oPgn9BGOmMTkZwnkZD7Cqp\\nGq4qzkGEv28BOi2OQKUVCz9rW6K4MtXcGsya3bPeOx384+iZrYfk4StkYP72r6XK\\n10+4sSbQnyBMUnbqmnQjx8udNb\\/Uik0JUXDmazACRIrErikMYFf2+VaDBPzhu9gx\\n9TsnQAl3IRvtXFUZlO6Tib4gJNnmDBYYtRDyk5fuHvaNLvq2pyQNtHMBWJLC+\\/Ot\\npCAahGelrNNoqHlD1403jmcf1yi4AWSpTybgCigwcLiyv1EIgXHWvv5eqDo6xHF7\\n71u+1ejoB8P7ujLK3xs7c+ionM8rcoznUQjNjhDHY6wbDZcddV8QeABUPe7cPRSs\\nigf7UYVTyhMN\\/4Ia7Svz8hRWPd8bv9c6NzqtzigYL1O9IhIFNemeDFpjHUCX4BQj\\ne9CXcGogSZTzTl3Fm\\/Indz24oxeS73KG+Jvn\\/sSnIdQRnxZ9uPcgrRaeim7tk7fW\\ndyHkj+qYTQdTPEpQ4SOduQ6wI7A8f0r5XWutrs7Jr3j45MWrXt9iGEf2pWkOdYbQ\\negopMaAluPp\\/mejKrY8LMM3+rM\\/hdo0RFISs0ek+CWdzI7PbouABJvb\\/iGfRt2gn\\neBO5z747500zAO5VA3f08yJenjwyaBzENjJsh7zELglKzzHxKdKe9WgvMQIDAQAB\\no4G8MIG5MDcGCWCGSAGG+EIBDQQqDChQdXBwZXQgUnVieS9PcGVuU1NMIEludGVy\\nbmFsIENlcnRpZmljYXRlMA4GA1UdDwEB\\/wQEAwIFoDAgBgNVHSUBAf8EFjAUBggr\\nBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH\\/BAIwADAdBgNVHQ4EFgQUzri5Vvfz\\nynlzvgry0OlWPtenr0UwHwYDVR0jBBgwFoAUxEkp7bbPB8\\/8jSG0o1Bp7jfvlrAw\\nDQYJKoZIhvcNAQELBQADggIBAM9sB0\\/BgLFTlrY2Lgsn5wJMLICZc0qRnEXDJ4gN\\nnOPFfjCzuEjo2+Mmrv29YGUJyzUw+RFw1cAmagvIus+0BSkp\\/yYvcJ+Ubo2nZlw0\\n8XbnU4Pvj8znUqJWCtcXJ2oWkJOfodLTtUNbS7EjIXmuirKw6XlD03qbfOetA8IA\\nDS4WDI0\\/ZUzwkOt9CRJVDU6P3EG\\/Y76ZXO7YEdPdY8tazGbEoxnk6DhzaCebpH1U\\nWFjmuFB8riN87CTbyXBYZoSsAOK5ETCAnXRz6KR5GnHWySxpAHuOBPKpqUtlq0V8\\n9R7dScLPJvNz5WKMyM3Ft3tcpRaC8zegzb3\\/ZqJhwmVmeLGgJ9bgpsWeLTxGk+pC\\ntoNe280LYJSmc\\/IXxPfIQuiOydlRlbmBCWN8mkH0Wd1ZAe3x9sX2BN+Qz4eg\\/yi5\\nPuQW7qCfPpdb77nCPm18NTx4tOjs0I8Aqd10hQDFDRxbcnRiNu8WI4qsAXRDNln7\\nNsHk4w9JdO0djmBwQB0ZCQD5pciBGmdtQvMvGfKhWSZTzItng8YW9zKh++nET3+N\\nyPZ6fxkaOa+ipm+lQxXmwE0ER4pdaUMvC+b0ZMNZH4tkfwC8w\\/FGNgtghbqj3Ymp\\nRRTEcqsKbRdlTG4YfwNeMCcp88HweE1XxOkVVBbqBiQS+\\/cx4Ma6SQ64jjyKrQPi\\nRiuj\\n-----END CERTIFICATE-----\\n\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-mcollective-servers.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-mcollective-servers.private_key.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-mcollective-servers\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/pe-internal-mcollective-servers.pem\",\n        \"content\": \"-----BEGIN RSA PRIVATE KEY-----\\nSOMETHINGSOMETHING\\n-----END RSA PRIVATE KEY-----\\n\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-mcollective-servers.public_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-mcollective-servers.public_key.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-mcollective-servers\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/public_keys\\/pe-internal-mcollective-servers.pem\",\n        \"content\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtzwjy9nfwSdNPHU2J9IO\\niRrbKgNqD4J\\/QRjpjE5GcJ5GQ+wqqRquKs5BhL9vATotjkClFQs\\/a1uiuDLV3BrM\\nmt2z3jsd\\/OPoma2H5OErZGD+9q+lytdPuLEm0J8gTFJ26pp0I8fLnTW\\/1IpNCVFw\\n5mswAkSKxK4pDGBX9vlWgwT84bvYMfU7J0AJdyEb7VxVGZTuk4m+ICTZ5gwWGLUQ\\n8pOX7h72jS76tqckDbRzAViSwvvzraQgGoRnpazTaKh5Q9eNN45nH9couAFkqU8m\\n4AooMHC4sr9RCIFx1r7+Xqg6OsRxe+9bvtXo6AfD+7oyyt8bO3PoqJzPK3KM51EI\\nzY4Qx2OsGw2XHXVfEHgAVD3u3D0UrIoH+1GFU8oTDf+CGu0r8\\/IUVj3fG7\\/XOjc6\\nrc4oGC9TvSISBTXpngxaYx1Al+AUI3vQl3BqIEmU805dxZvyJ3c9uKMXku9yhvib\\n5\\/7EpyHUEZ8Wfbj3IK0Wnopu7ZO31nch5I\\/qmE0HUzxKUOEjnbkOsCOwPH9K+V1r\\nra7Oya94+OTFq17fYhhH9qVpDnWG0HoKKTGgJbj6f5noyq2PCzDN\\/qzP4XaNERSE\\nrNHpPglncyOz26LgASb2\\/4hn0bdoJ3gTuc++O+dNMwDuVQN39PMiXp48MmgcxDYy\\nbIe8xC4JSs8x8SnSnvVoLzECAwEAAQ==\\n-----END PUBLIC KEY-----\\n\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-puppet-console-mcollective-client.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-puppet-console-mcollective-client.cert.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-puppet-console-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 30,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/pe-internal-puppet-console-mcollective-client.pem\",\n        \"content\": \"\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-puppet-console-mcollective-client.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-puppet-console-mcollective-client.private_key.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-puppet-console-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/pe-internal-puppet-console-mcollective-client.pem\",\n        \"content\": \"\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-puppet-console-mcollective-client.public_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-puppet-console-mcollective-client.public_key.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-puppet-console-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/public_keys\\/pe-internal-puppet-console-mcollective-client.pem\",\n        \"content\": \"\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-peadmin-mcollective-client.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-peadmin-mcollective-client.cert.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-peadmin-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 30,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/pe-internal-peadmin-mcollective-client.pem\",\n        \"content\": \"-----BEGIN CERTIFICATE-----\\nMIIF1jCCA76gAwIBAgIBBDANBgkqhkiG9w0BAQsFADBtMWswaQYDVQQDDGJQdXBw\\nZXQgRW50ZXJwcmlzZSBDQSBnZW5lcmF0ZWQgb24gcGVnbGlzYW4tbGF0ZXN0LmVu\\nZy5wdXBwZXRsYWJzLm5ldCBhdCArMjAxNy0wNC0wNSAxMToyODoyNCAtMDcwMDAe\\nFw0xNzA0MDQxODI5MjBaFw0yMjA0MDQxODI5MjBaMDExLzAtBgNVBAMMJnBlLWlu\\ndGVybmFsLXBlYWRtaW4tbWNvbGxlY3RpdmUtY2xpZW50MIICIjANBgkqhkiG9w0B\\nAQEFAAOCAg8AMIICCgKCAgEAwJWaN7cdOi4HXt7NRxZumSW8fVPcf4AEm\\/cSqTzE\\nUxUE4dWf9kGfqhJWzrB0oSKzAXwCI7YIEcpxt2KnUm8B9I7cLP67I2wFFHJsT41N\\nZRqNZo5qlc19GQw4AxBT2F4ZIRmTTysE6uwCpF05dQCIM\\/IHHSQgWnTn4rBMzIRx\\nvDq1FcGM\\/95wxHbZBWBxI0P93h7Oq8XdZdErczwzYC5BfzAxE0iM9mF17CM2Ynkm\\nMMBpXhzl\\/rg0JPZ0mNWBUR8XCGiq33KSRvcBLBvBN9\\/QZuWFSeX1ZpcrCJOheFLX\\nq6I2aOv99GH6MoyKN8Ix\\/cyz\\/i\\/FmugKm4l0VEBXOG\\/PDOyAMkPOPncQ9dUdY9uh\\ntOt52Vf\\/0mjCy1o96qkv1ZXYe+vrBsKK\\/Gn9KuSV2nIO4\\/9pMYPg3WC2EjTFmkV\\/\\n+DNQGILgEjJHQF7UNWXRcJqjVJ2GoVBwX3\\/e8fnwsv6bfa3oAQr\\/cExI8PRoDUCB\\nP3djfoHCqswaXdjaQxkYGaM1c2X8wdxa5P0I6aixsAk4gSWSvr9I0GH2z6gZXUo1\\n2JDW67so4OcI\\/klmUp+u2ichB521aPafScrHjm7hU+w8b2isugsbtzWDQneH3nfO\\nQAhJwNuSwqLXu8bGXILTKUaat0IsjpdQiRrORZ4tt8BOG++CcoLqFsZH1wfRXjrG\\n+rECAwEAAaOBvDCBuTA3BglghkgBhvhCAQ0EKgwoUHVwcGV0IFJ1YnkvT3BlblNT\\nTCBJbnRlcm5hbCBDZXJ0aWZpY2F0ZTAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH\\/\\nBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB\\/wQCMAAwHQYDVR0OBBYE\\nFItiZpcN62zhX97Xmj3kAMqipvdfMB8GA1UdIwQYMBaAFMRJKe22zwfP\\/I0htKNQ\\nae4375awMA0GCSqGSIb3DQEBCwUAA4ICAQC35ChVXYhhDfeKu3Kjs9S8Eo\\/19Xr3\\nZf6mkRCn0\\/TE6P3fOJweDNjJ2GKrtcvV6uNw06gvtYu4dQKnTezbYwjT9ngeSehC\\n1y4abbvoymGKpHO2T7QxSt6N0wrTzbwgYO6r7Z9KwY5CV0GurwVGgwMgl40QAi9j\\nY3BNheEjIE4iZp6s7kHjWNkzZr9Z54YIxl3V9\\/QNHkvYf66vYv50Yj4GEcKAeCl6\\nldX6mvPvRoLm9ygak1iURNWeRVjkoNo9vPvudt8A5CQzC\\/MeYu18z8pCSne\\/oxZG\\ndlBVbZvU0OkiHMktcUCf3zfXBhvynnWspcaH0+\\/POutjjec7hkriCaFxKTmvwpT5\\n27www6uXs4Ig70YGRKs4GEdnYjleFB2QYqNgYCVWNkyC2uvi7v8VUPbC8TY8wFKb\\n07A6SVovoJ8rBJ\\/d58Jhk8eCe9lnbOn4wK4AVSRVQkqvnumlg1ZVlNIpfBJcvqSO\\nKHqcwax8PjqZFoPLDZiYaPx7Gw25PvpyM7WqOYz1BbP3y8R1i2D0kVfOdkYYhf4P\\njkpxyoluwx7Gvya+GHHApfbkKhW1u8+HZzoMofw1XaSzKwXHGz2OYcxdezK4wc6Z\\nNn02pwPdai6XUau586SJhR1xd3EayaKU6QIpmOfrAullQYPCuH8267VB\\/22pTpOK\\nq2f3MnhaHgzS1w==\\n-----END CERTIFICATE-----\\n\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-peadmin-mcollective-client.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-peadmin-mcollective-client.private_key.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-peadmin-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/pe-internal-peadmin-mcollective-client.pem\",\n        \"content\": \"-----BEGIN RSA PRIVATE KEY-----\\nSOMETHINGSOMETHING\\n-----END RSA PRIVATE KEY-----\\n\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"pe-internal-peadmin-mcollective-client.public_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"pe-internal-peadmin-mcollective-client.public_key.pem\",\n        \"puppet_enterprise::master::keypair\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"keypair\",\n        \"pe-internal-peadmin-mcollective-client\",\n        \"class\",\n        \"puppet_enterprise::profile::master::mcollective\",\n        \"profile\",\n        \"mcollective\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/master\\/keypair.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/public_keys\\/pe-internal-peadmin-mcollective-client.pem\",\n        \"content\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwJWaN7cdOi4HXt7NRxZu\\nmSW8fVPcf4AEm\\/cSqTzEUxUE4dWf9kGfqhJWzrB0oSKzAXwCI7YIEcpxt2KnUm8B\\n9I7cLP67I2wFFHJsT41NZRqNZo5qlc19GQw4AxBT2F4ZIRmTTysE6uwCpF05dQCI\\nM\\/IHHSQgWnTn4rBMzIRxvDq1FcGM\\/95wxHbZBWBxI0P93h7Oq8XdZdErczwzYC5B\\nfzAxE0iM9mF17CM2YnkmMMBpXhzl\\/rg0JPZ0mNWBUR8XCGiq33KSRvcBLBvBN9\\/Q\\nZuWFSeX1ZpcrCJOheFLXq6I2aOv99GH6MoyKN8Ix\\/cyz\\/i\\/FmugKm4l0VEBXOG\\/P\\nDOyAMkPOPncQ9dUdY9uhtOt52Vf\\/0mjCy1o96qkv1ZXYe+vrBsKK\\/Gn9KuSV2nIO\\n4\\/9pMYPg3WC2EjTFmkV\\/+DNQGILgEjJHQF7UNWXRcJqjVJ2GoVBwX3\\/e8fnwsv6b\\nfa3oAQr\\/cExI8PRoDUCBP3djfoHCqswaXdjaQxkYGaM1c2X8wdxa5P0I6aixsAk4\\ngSWSvr9I0GH2z6gZXUo12JDW67so4OcI\\/klmUp+u2ichB521aPafScrHjm7hU+w8\\nb2isugsbtzWDQneH3nfOQAhJwNuSwqLXu8bGXILTKUaat0IsjpdQiRrORZ4tt8BO\\nG++CcoLqFsZH1wfRXjrG+rECAwEAAQ==\\n-----END PUBLIC KEY-----\\n\",\n        \"backup\": false,\n        \"owner\": \"pe-puppet\",\n        \"group\": \"pe-puppet\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Mcollective::Client::User\",\n      \"title\": \"peadmin\",\n      \"tags\": [\n        \"puppet_enterprise::mcollective::client::user\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"user\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client.pp\",\n      \"line\": 61,\n      \"exported\": false,\n      \"parameters\": {\n        \"home_dir\": \"\\/var\\/lib\\/peadmin\",\n        \"user_name\": \"peadmin\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Mcollective::Client::Certs\",\n      \"title\": \"peadmin\",\n      \"tags\": [\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client.pp\",\n      \"line\": 68,\n      \"exported\": false,\n      \"parameters\": {\n        \"client_name\": \"peadmin\",\n        \"keypair_name\": \"pe-internal-peadmin-mcollective-client\",\n        \"destination_dir\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/client.log\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"peadmin\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"backup\": false,\n        \"mode\": \"0600\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"peadmin\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"# This file managed by Puppet 4.10.0\\nmain_collective = mcollective\\ncollectives     = mcollective\\nlibdir          = \\/opt\\/puppet\\/libexec\\/mcollective:\\/opt\\/puppetlabs\\/mcollective\\/plugins\\nlogfile         = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/client.log\\nloglevel        = info\\n\\nsecurityprovider = ssl\\nplugin.ssl_client_private = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/peadmin-private.pem\\nplugin.ssl_client_public  = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/peadmin-public.pem\\nplugin.ssl_server_public  = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/mcollective-public.pem\\nplugin.ssl_serializer     = yaml\\n\\nconnector = activemq\\nplugin.activemq.randomize = false\\nplugin.activemq.pool.size = 1\\nplugin.activemq.pool.1.host = agent.demo.com\\nplugin.activemq.pool.1.port = 61613\\nplugin.activemq.pool.1.user = mcollective\\nplugin.activemq.pool.1.password = password123\\nplugin.activemq.pool.1.ssl = true\\nplugin.activemq.pool.1.ssl.ca = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/ca.cert.pem\\nplugin.activemq.pool.1.ssl.cert = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/agent.demo.com.cert.pem\\nplugin.activemq.pool.1.ssl.key = \\/var\\/lib\\/peadmin\\/.mcollective.d\\/agent.demo.com.private_key.pem\\n\\n# Facts\\nfactsource = yaml\\nplugin.yaml = \\/etc\\/puppetlabs\\/mcollective\\/facts.yaml\\ndirect_addressing = 1\\n\\n\\n\\n\",\n        \"backup\": false,\n        \"mode\": \"0600\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/mcollective\\/client.cfg\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"peadmin\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client.pp\",\n      \"line\": 109,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"backup\": false,\n        \"mode\": \"0600\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 29,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.master-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.master-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.master-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:8140\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.puppetdb-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.puppetdb-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 46,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.puppetdb-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:8081\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.classifier-service\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.classifier-service\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.classifier-service\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\\/classifier-api\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.console-services-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.console-services-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 58,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.console-services-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.rbac-consumer.api-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.rbac-consumer.api-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 64,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"rbac-consumer.api-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\\/rbac-api\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.activity-consumer.api-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.activity-consumer.api-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 70,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"activity-consumer.api-url\",\n        \"value\": \"https:\\/\\/agent.demo.com:4433\\/activity-api\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.pcp-broker-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.pcp-broker-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 77,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.pcp-broker-url\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.console-url\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.console-url\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 83,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.console-url\",\n        \"value\": \"https:\\/\\/agent.demo.com\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 89,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 95,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.pcp-timeout\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.pcp-timeout\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 123,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.pcp-timeout\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.global-concurrent-compiles\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.global-concurrent-compiles\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 130,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.global-concurrent-compiles\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.job-prune-threshold\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.job-prune-threshold\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 137,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.job-prune-threshold\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.database.subname\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.database.subname\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.database.subname\",\n        \"value\": \"\\/\\/agent.demo.com:5432\\/pe-orchestrator?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.database.user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.database.user\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 150,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.database.user\",\n        \"value\": \"pe-orchestrator-write\",\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.orchestrator.database.migration-user\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.orchestrator.database.migration-user\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 155,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf\",\n        \"setting\": \"orchestrator.database.migration-user\",\n        \"value\": \"pe-orchestrator\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"dispatch: allow pcp-brokers\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 175,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"match_request_type\": \"path\",\n        \"match_request_path\": \"\\/server\",\n        \"allow\": [\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 500,\n        \"ensure\": \"present\",\n        \"rule_name\": \"dispatch: allow pcp-brokers\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        }\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:orchestrator orchestrator-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 187,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.orchestrator.service\",\n        \"service\": \"orchestrator-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:orchestrator orchestrator-dispatch-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 192,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.orchestrator.dispatch\",\n        \"service\": \"orchestrator-dispatch-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:orchestrator jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 197,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:orchestrator remote-rbac-consumer-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 202,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.rbac-client.services.rbac\",\n        \"service\": \"remote-rbac-consumer-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:orchestrator remote-activity-reporter\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/orchestrator.pp\",\n      \"line\": 207,\n      \"exported\": false,\n      \"parameters\": {\n        \"container\": \"orchestration-services\",\n        \"namespace\": \"puppetlabs.rbac-client.services.activity\",\n        \"service\": \"remote-activity-reporter\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/pcp-broker.conf\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 12,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"mode\": \"0640\",\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.pcp-broker.accept-consumers\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.pcp-broker.accept-consumers\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/pcp-broker.conf\",\n        \"setting\": \"pcp-broker.accept-consumers\",\n        \"value\": 4,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.pcp-broker.delivery-consumers\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.pcp-broker.delivery-consumers\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 31,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/pcp-broker.conf\",\n        \"setting\": \"pcp-broker.delivery-consumers\",\n        \"value\": 4,\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.pcp-broker.controller-uris\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.pcp-broker.controller-uris\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/pcp-broker.conf\",\n        \"setting\": \"pcp-broker.controller-uris\",\n        \"type\": \"array\",\n        \"value\": [\n          \"wss:\\/\\/agent.demo.com:8143\\/server\"\n        ],\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.pcp-broker.controller-whitelist\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.pcp-broker.controller-whitelist\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 44,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/pcp-broker.conf\",\n        \"setting\": \"pcp-broker.controller-whitelist\",\n        \"type\": \"array\",\n        \"value\": [\n          \"http:\\/\\/puppetlabs.com\\/inventory_request\",\n          \"http:\\/\\/puppetlabs.com\\/rpc_blocking_request\",\n          \"http:\\/\\/puppetlabs.com\\/rpc_non_blocking_request\"\n        ],\n        \"ensure\": \"present\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"pxp commands\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 61,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_type\": \"path\",\n        \"match_request_query_params\": {\n          \"message_type\": [\n            \"http:\\/\\/puppetlabs.com\\/rpc_non_blocking_request\",\n            \"http:\\/\\/puppetlabs.com\\/rpc_blocking_request\"\n          ]\n        },\n        \"allow\": [\n          \"agent.demo.com\",\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 400,\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"match_request_path\": \"\\/pcp-broker\\/send\",\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"pxp commands\",\n        \"allow_unauthenticated\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"inventory request\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 77,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_type\": \"path\",\n        \"match_request_query_params\": {\n          \"message_type\": [\n            \"http:\\/\\/puppetlabs.com\\/inventory_request\"\n          ]\n        },\n        \"allow\": [\n          \"agent.demo.com\",\n          \"agent.demo.com\"\n        ],\n        \"sort_order\": 400,\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"match_request_path\": \"\\/pcp-broker\\/send\",\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"inventory request\",\n        \"allow_unauthenticated\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"multi-cast with destination_report\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 93,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_type\": \"path\",\n        \"match_request_query_params\": {\n          \"targets\": [\n            \"pcp:\\/\\/*\\/agent\",\n            \"pcp:\\/\\/*\\/*\"\n          ],\n          \"destination_report\": \"true\"\n        },\n        \"allow\": [\n          \n        ],\n        \"sort_order\": 399,\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"match_request_path\": \"\\/pcp-broker\\/send\",\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"multi-cast with destination_report\",\n        \"allow_unauthenticated\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"pcp-broker message\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 107,\n      \"exported\": false,\n      \"parameters\": {\n        \"match_request_type\": \"path\",\n        \"match_request_query_params\": {\n          \"message_type\": [\n            \"http:\\/\\/puppetlabs.com\\/associate_request\",\n            \"http:\\/\\/puppetlabs.com\\/rpc_provisional_response\",\n            \"http:\\/\\/puppetlabs.com\\/rpc_blocking_response\",\n            \"http:\\/\\/puppetlabs.com\\/rpc_non_blocking_response\",\n            \"http:\\/\\/puppetlabs.com\\/rpc_error_message\"\n          ]\n        },\n        \"allow\": \"*\",\n        \"sort_order\": 420,\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"match_request_path\": \"\\/pcp-broker\\/send\",\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"ensure\": \"present\",\n        \"rule_name\": \"pcp-broker message\",\n        \"allow_unauthenticated\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization::Rule\",\n      \"title\": \"pcp messages\",\n      \"tags\": [\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 123,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"match_request_path\": \"\\/pcp-broker\\/send\",\n        \"notify\": \"Service[pe-orchestration-services]\",\n        \"rule_name\": \"pcp messages\",\n        \"allow_unauthenticated\": false,\n        \"match_request_query_params\": {\n          \n        },\n        \"sort_order\": 200\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker broker-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 131,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.pcp.broker.service\",\n        \"service\": \"broker-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker authorization-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 136,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.authorization.authorization-service\",\n        \"service\": \"authorization-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker jetty9-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 141,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\",\n        \"service\": \"jetty9-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg\",\n      \"title\": \"orchestration-services:pcp-broker webrouting-service\",\n      \"tags\": [\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pcp_broker.pp\",\n      \"line\": 146,\n      \"exported\": false,\n      \"parameters\": {\n        \"namespace\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\",\n        \"service\": \"webrouting-service\",\n        \"container\": \"orchestration-services\",\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n      \"tags\": [\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 22,\n      \"exported\": false,\n      \"parameters\": {\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"mode\": \"0644\",\n        \"warn\": false,\n        \"force\": false,\n        \"backup\": \"puppet\",\n        \"replace\": true,\n        \"order\": \"alpha\",\n        \"ensure_newline\": false\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services webrouting-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\\/webrouting-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services metrics-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services metrics-webservice\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services scheduler-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\\/scheduler-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services status-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.client-auth\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.client-auth\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.client-auth\",\n        \"value\": \"want\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-host\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.ssl-host\",\n        \"value\": \"0.0.0.0\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-port\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.ssl-port\",\n        \"value\": 8142,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-crl-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-crl-path\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.pcp-broker.ssl-crl-path\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.access-log-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.access-log-config\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 67,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.pcp-broker.access-log-config\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.max-threads\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.max-threads\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.pcp-broker.max-threads\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.request-header-max-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.request-header-max-size\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 89,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.pcp-broker.request-header-max-size\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.default-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.default-server\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.pcp-broker.default-server\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.pcp-broker.ssl-protocols\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.pcp-broker.ssl-protocols\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"pcp-broker\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.pcp-broker.ssl-protocols\",\n        \"type\": \"array\",\n        \"value\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.client-auth\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.client-auth\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 27,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.client-auth\",\n        \"value\": \"want\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-host\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-host\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.ssl-host\",\n        \"value\": \"0.0.0.0\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-port\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-port\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 37,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.ssl-port\",\n        \"value\": 8143,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-ca-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-ca-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.ssl-ca-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-cert\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-cert\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.ssl-cert\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-key\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-key\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 52,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.ssl-key\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-crl-path\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-crl-path\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"webserver.orchestrator.ssl-crl-path\",\n        \"value\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.access-log-config\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.access-log-config\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 67,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.orchestrator.access-log-config\",\n        \"value\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/request-logging.xml\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.max-threads\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.max-threads\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 78,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.orchestrator.max-threads\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.request-header-max-size\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.request-header-max-size\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 89,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"setting\": \"webserver.orchestrator.request-header-max-size\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.default-server\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.default-server\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 100,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.orchestrator.default-server\",\n        \"value\": true,\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_hocon_setting\",\n      \"title\": \"orchestration-services.webserver.orchestrator.ssl-protocols\",\n      \"tags\": [\n        \"pe_hocon_setting\",\n        \"orchestration-services.webserver.orchestrator.ssl-protocols\",\n        \"puppet_enterprise::trapperkeeper::webserver_settings\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"webserver_settings\",\n        \"orchestrator\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/webserver_settings.pp\",\n      \"line\": 111,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"setting\": \"webserver.orchestrator.ssl-protocols\",\n        \"type\": \"array\",\n        \"value\": [\n          \"TLSv1\",\n          \"TLSv1.1\",\n          \"TLSv1.2\"\n        ],\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0600\",\n        \"backup\": false,\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"before\": \"Exec[pe-orchestration-services service full restart]\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"before\": \"Exec[pe-orchestration-services service full restart]\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"before\": \"Exec[pe-orchestration-services service full restart]\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.public_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 75,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/public_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"before\": \"Exec[pe-orchestration-services service full restart]\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs::Pk8_cert\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 82,\n      \"exported\": false,\n      \"parameters\": {\n        \"pem_file\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"mode\": \"0400\",\n        \"container\": \"orchestration-services\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl]\",\n        \"pk8_file\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'Xmx'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xmx\",\n        \"value\": \"192m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'Xms'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xms\",\n        \"value\": \"192m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'XX:+PrintGCDetails'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDetails\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'XX:+PrintGCDateStamps'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDateStamps\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'Xloggc:'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xloggc:\",\n        \"value\": \"\\/var\\/log\\/puppetlabs\\/orchestration-services\\/orchestration-services_gc.log\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'XX:+UseGCLogFileRotation'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+UseGCLogFileRotation\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'XX:NumberOfGCLogFiles='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:NumberOfGCLogFiles=\",\n        \"value\": \"16\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-orchestration-services_'XX:GCLogFileSize='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:GCLogFileSize=\",\n        \"value\": \"64m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-orchestration-services\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pe-orchestration-services\",\n      \"tags\": [\n        \"service\",\n        \"pe-orchestration-services\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 10,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true,\n        \"restart\": \"service pe-orchestration-services reload\",\n        \"require\": \"Package[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe-orchestration-services service full restart\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"service pe-orchestration-services restart\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"refreshonly\": true,\n        \"before\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 19,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"[database]-puppetdb_psdatabase_username\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"username\",\n        \"value\": \"pe-puppetdb\",\n        \"section\": \"database\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"[database]-puppetdb_subname\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"subname\",\n        \"value\": \"\\/\\/agent.demo.com:5432\\/pe-puppetdb?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"section\": \"database\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"[database]-maximum-pool-size\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::database_ini\",\n        \"database_ini\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"maximum-pool-size\",\n        \"value\": 25,\n        \"section\": \"database\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"read-database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 19,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0640\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"[read-database]-puppetdb_psdatabase_username\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"read-database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"username\",\n        \"value\": \"pe-puppetdb\",\n        \"section\": \"read-database\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"[read-database]-puppetdb_subname\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"read-database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"subname\",\n        \"value\": \"\\/\\/agent.demo.com:5432\\/pe-puppetdb?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem&sslkey=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8&sslcert=\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n        \"section\": \"read-database\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"[read-database]-maximum-pool-size\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::puppetdb::shared_database_settings\",\n        \"puppet_enterprise\",\n        \"puppetdb\",\n        \"shared_database_settings\",\n        \"read-database\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/puppetdb\\/shared_database_settings.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"maximum-pool-size\",\n        \"value\": 25,\n        \"section\": \"read-database\",\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini\",\n        \"ensure\": \"present\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini]\"\n      }\n    },\n    {\n      \"type\": \"Service\",\n      \"title\": \"pe-puppetdb\",\n      \"tags\": [\n        \"service\",\n        \"pe-puppetdb\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::service\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 10,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"running\",\n        \"enable\": true,\n        \"hasrestart\": true,\n        \"restart\": \"service pe-puppetdb reload\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"subscribe\": [\n          \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n          \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n          \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n          \"Class[Puppet_enterprise::Puppetdb::Config_ini]\",\n          \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\"\n        ]\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe-puppetdb service full restart\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::trapperkeeper::pe_service\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pe_service\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb::service\",\n        \"service\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/pe_service.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"service pe-puppetdb restart\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"refreshonly\": true,\n        \"before\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'Xmx'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xmx\",\n        \"value\": \"256m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'Xms'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xms\",\n        \"value\": \"256m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'XX:+PrintGCDetails'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDetails\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'XX:+PrintGCDateStamps'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+PrintGCDateStamps\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'Xloggc:'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-Xloggc:\",\n        \"value\": \"\\/var\\/log\\/puppetlabs\\/puppetdb\\/puppetdb_gc.log\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'XX:+UseGCLogFileRotation'\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:+UseGCLogFileRotation\",\n        \"value\": \"\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'XX:NumberOfGCLogFiles='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:NumberOfGCLogFiles=\",\n        \"value\": \"16\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_subsetting\",\n      \"title\": \"pe-puppetdb_'XX:GCLogFileSize='\",\n      \"tags\": [\n        \"pe_ini_subsetting\",\n        \"puppet_enterprise::trapperkeeper::java_args\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"java_args\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/java_args.pp\",\n      \"line\": 49,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"subsetting\": \"-XX:GCLogFileSize=\",\n        \"value\": \"64m\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"setting\": \"JAVA_ARGS\",\n        \"quote_char\": \"\\\"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf java_bin\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"JAVA_BIN\",\n        \"value\": \"\\\"\\/opt\\/puppetlabs\\/server\\/bin\\/java\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf user\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"USER\",\n        \"value\": \"pe-puppetdb\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf group\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 30,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"GROUP\",\n        \"value\": \"pe-puppetdb\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf install_dir\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"INSTALL_DIR\",\n        \"value\": \"\\\"\\/opt\\/puppetlabs\\/server\\/apps\\/puppetdb\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf config\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"CONFIG\",\n        \"value\": \"\\\"\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf bootstrap_config\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 45,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"BOOTSTRAP_CONFIG\",\n        \"value\": \"\\\"\\/etc\\/puppetlabs\\/puppetdb\\/bootstrap.cfg\\\"\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf service_stop_retries\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 50,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"SERVICE_STOP_RETRIES\",\n        \"value\": 60,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_ini_setting\",\n      \"title\": \"puppetdb initconf start_timeout\",\n      \"tags\": [\n        \"pe_ini_setting\",\n        \"puppet_enterprise::trapperkeeper::init_defaults\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"init_defaults\",\n        \"puppetdb\",\n        \"class\",\n        \"puppet_enterprise::puppetdb\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/init_defaults.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"setting\": \"START_TIMEOUT\",\n        \"value\": 300,\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/sysconfig\\/pe-puppetdb\",\n        \"key_val_separator\": \"=\",\n        \"section\": \"\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Exec[pe-puppetdb service full restart]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 35,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0600\",\n        \"backup\": false,\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"before\": \"Exec[pe-puppetdb service full restart]\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"before\": \"Exec[pe-puppetdb service full restart]\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"before\": \"Exec[pe-puppetdb service full restart]\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.public_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 75,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/public_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"before\": \"Exec[pe-puppetdb service full restart]\",\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"Puppet_enterprise::Certs::Pk8_cert\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs.pp\",\n      \"line\": 82,\n      \"exported\": false,\n      \"parameters\": {\n        \"pem_file\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0400\",\n        \"container\": \"puppetdb\",\n        \"require\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/ssl]\",\n        \"pk8_file\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8\"\n      }\n    },\n    {\n      \"type\": \"Pe_file_line\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist:agent.demo.com\",\n      \"tags\": [\n        \"pe_file_line\",\n        \"puppet_enterprise::certs::whitelist_entry\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"whitelist_entry\",\n        \"class\",\n        \"puppet_enterprise::certs::puppetdb_whitelist\",\n        \"puppetdb_whitelist\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/whitelist_entry.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist\",\n        \"line\": \"agent.demo.com\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 40,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"755\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 57,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Pe_staging::Deploy\",\n      \"title\": \"puppet-agent-el-7-x86_64.tar.gz\",\n      \"tags\": [\n        \"pe_staging::deploy\",\n        \"pe_staging\",\n        \"deploy\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 62,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"https:\\/\\/pm.puppetlabs.com\\/puppet-agent\\/2017.2.0-rc1-424-ge1372a3\\/1.10.0\\/repos\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"staging_path\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"target\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\",\n        \"creates\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/repodata\",\n        \"strip\": \"5\",\n        \"require\": [\n          \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public]\",\n          \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0]\"\n        ],\n        \"mode\": \"0755\"\n      }\n    },\n    {\n      \"type\": \"Pe_repo::Set_owner_group_permissions\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\",\n      \"tags\": [\n        \"pe_repo::set_owner_group_permissions\",\n        \"pe_repo\",\n        \"set_owner_group_permissions\",\n        \"pe_repo::repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 73,\n      \"exported\": false,\n      \"parameters\": {\n        \"file_mode\": \"0644\",\n        \"dir_mode\": \"0755\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"target_dir\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 81,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"755\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/install.bash\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 93,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"content\": \"#! \\/bin\\/bash\\nset -u\\nset -e\\n\\n\\nPUPPET_CONF_DIR=\\\"\\/etc\\/puppetlabs\\/puppet\\\"\\nPUPPET_BIN_DIR=\\\"\\/opt\\/puppetlabs\\/puppet\\/bin\\\"\\nexport OLD_PUPPET_BIN_DIR=\\\"\\/opt\\/puppet\\/bin\\\"\\n\\nfail() { echo >&2 \\\"$@\\\"; exit 1; }\\ncmd()  { hash \\\"$1\\\" >&\\/dev\\/null; } # portable 'which'\\n\\npuppet_installed() {\\n  if [ -x \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" -o -x \\\"${OLD_PUPPET_BIN_DIR}\\/puppet\\\" ]; then\\n    return 0\\n  else\\n    return 1\\n  fi\\n}\\n\\npxp_present() {\\n    if cmd \\\"${PUPPET_BIN_DIR}\\/pxp-agent\\\"; then\\n        return 1\\n    else\\n        return 0\\n    fi\\n}\\n\\n# Echo back either the AIO puppet bin dir path, or PE 3.x pe-agent\\n# puppet bin dir.\\npuppet_bin_dir() {\\n    if [ -e \\\"${PUPPET_BIN_DIR?}\\\" ]; then\\n        t_puppet_bin_dir=\\\"${PUPPET_BIN_DIR?}\\\"\\n    else\\n        t_puppet_bin_dir=\\/opt\\/puppet\\/bin\\n    fi\\n    echo \\\"${t_puppet_bin_dir?}\\\"\\n}\\n\\n\\n### The following variable and function (upgrading_from_38x)\\n### Need to be declared after the `puppet_bin_dir` function\\n### due to the way bash handles function parsing\\n# Are we upgradeing from 3.8.x or 2015.2+ ?\\nORIGINAL_PUPPET_BIN_DIR=$(puppet_bin_dir)\\n\\nupgrading_from_38x() {\\n    [ \\\"${ORIGINAL_PUPPET_BIN_DIR?}\\\" = \\\"${OLD_PUPPET_BIN_DIR?}\\\" ]\\n}\\n\\nmktempfile() {\\n  if cmd mktemp; then\\n    if [ \\\"osx\\\" = \\\"${PLATFORM_NAME}\\\" ]; then\\n      mktemp -t installer\\n    else\\n      mktemp\\n    fi\\n  else\\n    echo \\\"\\/tmp\\/puppet-enterprise-installer.XXX-${RANDOM}\\\"\\n  fi\\n}\\n\\ncustom_puppet_configuration() {\\n  # Parse optional pre-installation configuration of Puppet settings via\\n  # command-line arguments. Arguments should be of the form\\n  #\\n  #   <section>:<setting>=<value>\\n  #\\n  # There are four valid section settings in puppet.conf: \\\"main\\\", \\\"master\\\",\\n  # \\\"agent\\\", \\\"user\\\". If you provide valid setting and value for one of these\\n  # four sections, it will end up in <confdir>\\/puppet.conf.\\n  #\\n  # There are two sections in csr_attributes.yaml: \\\"custom_attributes\\\" and\\n  # \\\"extension_requests\\\". If you provide valid setting and value for one\\n  # of these two sections, it will end up in <confdir>\\/csr_attributes.yaml.\\n  #\\n  # note:Custom Attributes are only present in the CSR, while Extension\\n  # Requests are both in the CSR and included as X509 extensions in the\\n  # signed certificate (and are thus available as \\\"trusted facts\\\" in Puppet).\\n  #\\n  # Regex is authoritative for valid sections, settings, and values.  Any input\\n  # that fails regex will trigger this script to fail with error message.\\n  regex='^(main|master|agent|user|custom_attributes|extension_requests):([^=]+)=(.*)$'\\n  declare -a attr_array\\n  declare -a extn_array\\n\\n  for entry in \\\"$@\\\"; do\\n    if ! [[ $entry =~ $regex ]]; then\\n      fail \\\"Unable to interpret argument: '${entry}'. Expected '<section>:<setting>=<value>' matching regex: '${regex}'\\\"\\n    else\\n      section=${BASH_REMATCH[1]}\\n      setting=${BASH_REMATCH[2]}\\n      value=${BASH_REMATCH[3]}\\n      case $section in\\n        custom_attributes)\\n          # Store the entry in attr_array for later addition to csr_attributes.yaml\\n          attr_array=(\\\"${attr_array[@]}\\\" \\\"${setting}: '${value}'\\\")\\n          ;;\\n        extension_requests)\\n          # Store the entry in extn_array for later addition to csr_attributes.yaml\\n          extn_array=(\\\"${extn_array[@]}\\\" \\\"${setting}: '${value}'\\\")\\n          ;;\\n        *)\\n          # Set the specified entry in puppet.conf\\n          \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set \\\"$setting\\\" \\\"$value\\\" --section \\\"$section\\\"\\n      esac\\n    fi\\n  done\\n\\n  # If the the length of the attr_array or extn_array is greater than zero, it\\n  # means we have settings, so we'll create the csr_attributes.yaml file.\\n  if [[ ${#attr_array[@]} -gt 0 || ${#extn_array[@]} -gt 0 ]]; then\\n    mkdir -p \\\"${PUPPET_CONF_DIR}\\\"\\n    echo '---' > \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n\\n    if [[ ${#attr_array[@]} -gt 0 ]]; then\\n      echo 'custom_attributes:' >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      for ((i = 0; i < ${#attr_array[@]}; i++)); do\\n        echo \\\"  ${attr_array[i]}\\\" >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      done\\n    fi\\n\\n    if [[ ${#extn_array[@]} -gt 0 ]]; then\\n      echo 'extension_requests:' >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      for ((i = 0; i < ${#extn_array[@]}; i++)); do\\n        echo \\\"  ${extn_array[i]}\\\" >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      done\\n    fi\\n  fi\\n}\\n\\nensure_link() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource file \\\"${1?}\\\" ensure=link target=\\\"${2?}\\\"\\n}\\n\\nensure_agent_links() {\\n  target_path=\\\"\\/usr\\/local\\/bin\\\"\\n\\n  if mkdir -p \\\"${target_path}\\\" && [ -w \\\"${target_path}\\\" ]; then\\n    for bin in facter puppet pe-man hiera; do\\n      ensure_link \\\"${target_path}\\/${bin}\\\" \\\"${PUPPET_BIN_DIR?}\\/${bin}\\\"\\n    done\\n  else\\n    echo \\\"!!! WARNING: ${target_path} is inaccessible; unable to create convenience symlinks for puppet, hiera, facter and pe-man.  These executables may be found in ${pe_path?}.\\\" 1>&2\\n  fi\\n}\\n\\n# Detected existing installation? Return y if true, else n\\nis_upgrade() {\\n  if puppet_installed; then\\n    echo \\\"y\\\"\\n  else\\n    echo \\\"n\\\"\\n  fi\\n}\\n\\n# Sets server, certname and any custom puppet.conf flags passed in to the script\\npuppet_config_set() {\\n  # puppet config set does not create the [main] section if it does not exist\\n  # and does not use it if it has no settings (PUP-4755); and augeas does not\\n  # consider puppet.conf parseable with settings floating outside of a section\\n  puppet_conf=\\\"$(\\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config print confdir)\\/puppet.conf\\\"\\n  if ! grep '\\\\[main\\\\]' \\\"${puppet_conf}\\\"; then\\n    t_surgery='yes'\\n    cat >> \\\"${puppet_conf}\\\" <<EOF\\n[main]\\nplace=holder\\nEOF\\n  fi\\n\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set server agent.demo.com --section main\\n\\n  if [ \\\"${t_surgery}\\\" = 'yes' ]; then\\n    t_platform_test=\\\"x$(uname -s)\\\"\\n    if [ \\\"${t_platform_test}\\\" = \\\"xDarwin\\\" ]; then\\n      sed -i '' '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\"\\n    elif [ \\\"${t_platform_test}\\\" = \\\"xSunOS\\\" -o \\\"${t_platform_test}\\\" = \\\"xAIX\\\" ]; then\\n      sed '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\" > \\\"${puppet_conf}.new\\\" && mv \\\"${puppet_conf}.new\\\" \\\"${puppet_conf}\\\"\\n    else\\n      sed -i '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\"\\n    fi\\n  fi\\n\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set certname $(\\\"${PUPPET_BIN_DIR?}\\/facter\\\" fqdn | \\\"${PUPPET_BIN_DIR?}\\/ruby\\\" -e 'puts STDIN.read.downcase') --section main\\n  custom_puppet_configuration \\\"$@\\\"\\n\\n  # To ensure the new config settings take place and to work around differing OS behaviors on recieving a service start command while running\\n  # (on nix it triggers a puppet run, on osx it does nothing), restart the service by stopping and starting it again\\n  restart_puppet_agent\\n}\\n\\nrestart_puppet_agent() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=stopped\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=running enable=true\\n}\\n\\nstart_puppet_agent() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=running enable=true\\n}\\n\\nstop_puppet_agent() {\\n  \\\"$(puppet_bin_dir)\\/puppet\\\" resource service puppet ensure=stopped\\n  wait_for_puppet_lock\\n}\\n\\nmcollective_service_name() {\\n  if upgrading_from_38x; then\\n    t_mco_service_name='pe-mcollective'\\n  else\\n    t_mco_service_name='mcollective'\\n  fi\\n  echo \\\"${t_mco_service_name}\\\"\\n}\\n\\nmcollective_status() {\\n  output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource service \\\"$(mcollective_service_name)\\\" 2> \\/dev\\/null)\\n  case ${output?} in\\n    *ensure*=\\\\>*running*)\\n      echo \\\"running\\\";;\\n    *)\\n      echo \\\"stopped\\\";;\\n  esac\\n}\\n\\npxp_agent_status() {\\n  output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource service pxp-agent 2> \\/dev\\/null)\\n  case ${output?} in\\n    *ensure*=\\\\>*running*)\\n      echo \\\"running\\\";;\\n    *)\\n      echo \\\"stopped\\\";;\\n  esac\\n}\\n\\nensure_service() {\\n    pkg=${1?}\\n    state=${2?}\\n    \\\"$(puppet_bin_dir)\\/puppet\\\" resource service \\\"${pkg?}\\\" ensure=\\\"${state?}\\\"\\n    return $?\\n}\\n\\nwait_for_puppet_lock() {\\n  t_puppet_run_lock=`$(puppet_bin_dir)\\/puppet config print agent_catalog_run_lockfile`\\n  while [ -f \\\"${t_puppet_run_lock?}\\\" ]; do\\n      echo \\\"Waiting for Agent run lock ${t_puppet_run_lock?} to clear...\\\"\\n      sleep 10\\n  done\\n}\\n\\n# In version 7.10.0 curl introduced the -k flag and performs peer\\n# certificate validation by default. If peer validation is performed by\\n# default the -k flag is necessary for this script to work. However, if curl\\n# is older than 7.10.0 the -k flag does not exist. This function will return\\n# the correct invocation of curl depending on the version installed.\\ncurl_no_peer_verify() {\\n  curl_ver_regex='curl ([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)'\\n  [[ \\\"$(curl -V 2>\\/dev\\/null)\\\" =~ $curl_ver_regex ]]\\n  curl_majv=\\\"${BASH_REMATCH[1]-7}\\\"  # Default to 7  if no match\\n  curl_minv=\\\"${BASH_REMATCH[2]-10}\\\" # Default to 10 if no match\\n  if [[ \\\"$curl_majv\\\" -eq 7 && \\\"$curl_minv\\\" -le 9 ]] || [[ \\\"$curl_majv\\\" -lt 7 ]]; then\\n    curl_invocation=\\\"curl\\\"\\n  else\\n    curl_invocation=\\\"curl -k\\\"\\n  fi\\n\\n  $curl_invocation \\\"$@\\\"\\n}\\n\\n# Uses curl, or if not present, wget to download file from passed http url to a\\n# temporary location.\\n#\\n# Arguments\\n# 1. The url to download\\n# 2. The file to save it as\\n#\\n# Returns 0 or 1 if download fails.\\ndownload_from_url() {\\n    local t_url=\\\"${1?}\\\"\\n    local t_file=\\\"${2?}\\\"\\n\\n    if cmd curl; then\\n        # curl on AIX doesn't support -k, but it's the default behavior\\n        if [ \\\"$PLATFORM_NAME\\\" = \\\"aix\\\" ]; then\\n          CURL=\\\"curl_no_peer_verify\\\"\\n        else\\n          CURL=\\\"curl -k\\\"\\n        fi\\n        t_http_code=\\\"$($CURL --tlsv1 -sLo \\\"${t_file?}\\\" \\\"${t_url?}\\\" --write-out %{http_code} || fail \\\"curl failed to get ${t_url?}\\\")\\\"\\n    elif cmd wget; then\\n        # wget on AIX doesn't support SSL\\n        [ \\\"$PLATFORM_NAME\\\" = \\\"aix\\\" ] && fail \\\"Unable to download installation materials without curl\\\"\\n\\n        # Run wget and use awk to figure out the HTTP status.\\n        t_http_code=\\\"$(wget --secure-protocol=TLSv1 -O \\\"${t_file?}\\\" --no-check-certificate -S \\\"${t_url?}\\\" 2>&1 | awk '\\/HTTP\\\\\\/1.1\\/ { printf $2 }')\\\"\\n        if [ -z \\\"${t_file?}\\\" ]; then\\n            fail \\\"wget failed to get ${t_url?}\\\"\\n        fi\\n    else\\n        fail \\\"Unable to download installation materials without curl or wget\\\"\\n    fi\\n\\n    if [ \\\"${t_http_code?}\\\" == \\\"200\\\" ]; then\\n        return 0\\n    else\\n        return 1\\n    fi\\n}\\n\\nsupported_platform() {\\n  # Because of differences between how regex works between bash versions this\\n  # regex MUST be in a variable and passed into the test below UNQUOTED.\\n  t_supported_platform_regex='(el-(4|5|6|7)-(i386|x86_64|s390x))|(debian-(6|7|8)-(i386|amd64))|(ubuntu-(10\\\\.04|12\\\\.04|14\\\\.04|15\\\\.04|15\\\\.10|16\\\\.04)-(i386|amd64))|(sles-(10|11|12)-(i386|x86_64|s390x))|(fedora-(2[2-5])-(i386|x86_64))|(solaris-(10|11)-(i386|sparc))|(aix-(5\\\\.3|6\\\\.1|7\\\\.1)-power)|(osx-10\\\\.(9|10|11|12)-x86_64)'\\n  [[ \\\"${1?}\\\" =~ ${t_supported_platform_regex:?} ]]\\n}\\n\\nrun_agent_install_from_url() {\\n    t_agent_install_url=\\\"${1?}\\\"\\n\\n    t_install_file=$(mktempfile)\\n    if ! download_from_url \\\"${t_agent_install_url?}\\\" \\\"${t_install_file?}\\\"; then\\n        if supported_platform \\\"${PLATFORM_TAG?}\\\"; then\\n            fail \\\"The agent packages needed to support ${PLATFORM_TAG} are not present on your master. \\\\\\n    To add them, apply the pe_repo::platform::$(echo \\\"${PLATFORM_TAG?}\\\" | tr - _ | tr -dc '[:alnum:]_') class to your master node and then run Puppet. \\\\\\n    The required agent packages should be retrieved when puppet runs on the master, after which you can run the install.bash script again.\\\"\\n        else\\n            fail \\\"This method of agent installation is not supported for ${PLATFORM_TAG?} in Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\\"\\n        fi\\n    fi\\n\\n    bash \\\"${t_install_file?}\\\" \\\"${@: 2}\\\" || fail \\\"Error running install script ${t_install_file?}\\\"\\n}\\n\\n# Sets PLATFORM_NAME to a value that PE expects\\n#\\n# Arguments:\\n# PLATFORM_NAME\\n# RELEASE_FILE\\n#\\n# Side-effect:\\n# Modifies PLATFORM_NAME\\nfunction sanitize_platform_name() {\\n    # Sanitize name for unusual platforms\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        redhatenterpriseserver | redhatenterpriseclient | redhatenterpriseas | redhatenterprisees | enterpriseenterpriseserver | redhatenterpriseworkstation | redhatenterprisecomputenode | oracleserver)\\n            PLATFORM_NAME=rhel\\n            ;;\\n        enterprise*)\\n            PLATFORM_NAME=centos\\n            ;;\\n        scientific | scientifics | scientificsl)\\n            PLATFORM_NAME=rhel\\n            ;;\\n        oracle | ol)\\n            PLATFORM_NAME=rhel\\n            ;;\\n        'suse linux')\\n            PLATFORM_NAME=sles\\n            ;;\\n        amazonami)\\n            PLATFORM_NAME=amazon\\n            ;;\\n    esac\\n\\n    if [ -r \\\"${RELEASE_FILE:-}\\\" ] && grep -E \\\"Cumulus Linux\\\" \\\"${RELEASE_FILE}\\\" &> \\/dev\\/null; then\\n        PLATFORM_NAME=cumulus\\n    fi\\n}\\n\\n# Sets PLATFORM_RELEASE to a value that PE expects\\n#\\n# Arguments:\\n# PLATFORM_NAME\\n# PLATFORM_RELEASE\\n#\\n# Side-effect:\\n# Modifies PLATFORM_RELEASE\\nfunction sanitize_platform_release() {\\n    # Sanitize release for unusual platforms\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        centos | rhel | sles)\\n            # Platform uses only number before period as the release,\\n            # e.g. \\\"CentOS 5.5\\\" is release \\\"5\\\"\\n            PLATFORM_RELEASE=$(echo -n \\\"${PLATFORM_RELEASE?}\\\" | cut -d. -f1)\\n            ;;\\n        debian)\\n            # Platform uses only number before period as the release,\\n            # e.g. \\\"Debian 6.0.1\\\" is release \\\"6\\\"\\n            PLATFORM_RELEASE=$(echo -n \\\"${PLATFORM_RELEASE?}\\\" | cut -d. -f1)\\n            if [ ${PLATFORM_RELEASE} = \\\"testing\\\" ] ; then\\n                PLATFORM_RELEASE=7\\n            fi\\n            ;;\\n        cumulus)\\n            PLATFORM_RELEASE=$(echo -n \\\"${PLATFORM_RELEASE?}\\\" | cut -d'.' -f'1,2')\\n            ;;\\n    esac\\n}\\n\\n##############################################################################\\n# We need to know what the PE platform tag is for this node, which requires\\n# digging through a bunch of data to extract it.  This is currently the best\\n# mechanism available to do this, which is copied from the PE\\n# installer itself.\\nif [ -z \\\"${PLATFORM_NAME:-\\\"\\\"}\\\" ] || [ -z \\\"${PLATFORM_RELEASE:-\\\"\\\"}\\\" ]; then\\n    # https:\\/\\/www.freedesktop.org\\/software\\/systemd\\/man\\/os-release.html#Description\\n    # Try \\/etc\\/os-release first, then \\/usr\\/lib\\/os-release, then legacy pre-systemd methods\\n    if [ -f \\\"\\/etc\\/os-release\\\" ] || [ -f \\\"\\/usr\\/lib\\/os-release\\\" ]; then\\n        if [ -f \\\"\\/etc\\/os-release\\\" ]; then\\n            RELEASE_FILE=\\\"\\/etc\\/os-release\\\"\\n        else\\n            RELEASE_FILE=\\\"\\/usr\\/lib\\/os-release\\\"\\n        fi\\n        PLATFORM_NAME=$(source \\\"${RELEASE_FILE}\\\"; echo -n \\\"${ID}\\\")\\n        sanitize_platform_name\\n        PLATFORM_RELEASE=$(source \\\"${RELEASE_FILE}\\\"; echo -n \\\"${VERSION_ID}\\\")\\n        sanitize_platform_release\\n    # Try identifying using lsb_release.  This takes care of Ubuntu\\n    # (lsb-release is part of ubuntu-minimal).\\n    elif type lsb_release > \\/dev\\/null 2>&1; then\\n        t_prepare_platform=`lsb_release -icr 2>&1`\\n\\n        PLATFORM_NAME=\\\"$(echo -n \\\"${t_prepare_platform?}\\\" | grep -E '^Distributor ID:' | cut -s -d: -f2 | sed 's\\/[[:space:]]\\/\\/' | tr '[[:upper:]]' '[[:lower:]]')\\\"\\n        sanitize_platform_name\\n\\n        # Release\\n        PLATFORM_RELEASE=\\\"$(echo -n \\\"${t_prepare_platform?}\\\" | grep -E '^Release:' | cut -s -d: -f2 | sed 's\\/[[:space:]]\\/\\/g')\\\"\\n        sanitize_platform_release\\n    elif [ \\\"x$(uname -s)\\\" = \\\"xDarwin\\\" ]; then\\n        PLATFORM_NAME=\\\"osx\\\"\\n        # sw_vers returns something like 10.9.2, but we only want 10.9 so chop off the end\\n        t_platform_release=\\\"$(\\/usr\\/bin\\/sw_vers -productVersion | cut -d'.' -f1,2)\\\"\\n        PLATFORM_RELEASE=\\\"${t_platform_release?}\\\"\\n    # Test for Solaris.\\n    elif [ \\\"x$(uname -s)\\\" = \\\"xSunOS\\\" ]; then\\n        PLATFORM_NAME=\\\"solaris\\\"\\n        t_platform_release=\\\"$(uname -r)\\\"\\n        # JJM We get back 5.10 but we only care about the right side of the decimal.\\n        PLATFORM_RELEASE=\\\"${t_platform_release##*.}\\\"\\n    elif [ \\\"x$(uname -s)\\\" = \\\"xAIX\\\" ] ; then\\n        PLATFORM_NAME=\\\"aix\\\"\\n        t_platform_release=\\\"$(oslevel | cut -d'.' -f1,2 | sed -e 's\\/7\\\\.2\\/7\\\\.1\\/')\\\"\\n        PLATFORM_RELEASE=\\\"${t_platform_release}\\\"\\n\\n        # Test for RHEL variant. RHEL, CentOS, OEL\\n    elif [ -f \\/etc\\/redhat-release ] && [ -r \\/etc\\/redhat-release ] && [ -s \\/etc\\/redhat-release ]; then\\n        # Oracle Enterprise Linux 5.3 and higher identify the same as RHEL\\n        if grep -qi 'red hat enterprise' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME=rhel\\n        elif grep -qi 'centos' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME=centos\\n        elif grep -qi 'scientific' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME=rhel\\n        elif grep -qi 'fedora' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME='fedora'\\n        fi\\n        # Release - take first digits after ' release ' only.\\n        PLATFORM_RELEASE=\\\"$(sed 's\\/.*\\\\ release\\\\ \\\\([[:digit:]]\\\\+\\\\).*\\/\\\\1\\/g;q' \\/etc\\/redhat-release)\\\"\\n    # Test for Debian releases\\n    elif [ -f \\/etc\\/debian_version ] && [ -r \\/etc\\/debian_version ] && [ -s \\/etc\\/debian_version ]; then\\n        t_prepare_platform__debian_version_file=\\\"\\/etc\\/debian_version\\\"\\n        t_prepare_platform__debian_version=`cat \\/etc\\/debian_version`\\n\\n        if cat \\\"${t_prepare_platform__debian_version_file?}\\\" | grep -E '^[[:digit:]]' > \\/dev\\/null; then\\n            PLATFORM_NAME=debian\\n            PLATFORM_RELEASE=\\\"$(echo -n \\\"${t_prepare_platform__debian_version?}\\\" | sed 's\\/\\\\..*\\/\\/')\\\"\\n        elif cat \\\"${t_prepare_platform__debian_version_file?}\\\" | grep -E '^wheezy' > \\/dev\\/null; then\\n            PLATFORM_NAME=debian\\n            PLATFORM_RELEASE=\\\"7\\\"\\n        fi\\n    elif [ -f \\/etc\\/SuSE-release ] && [ -r \\/etc\\/SuSE-release ]; then\\n        t_prepare_platform__suse_version=`cat \\/etc\\/SuSE-release`\\n\\n        if echo -n \\\"${t_prepare_platform__suse_version?}\\\" | grep -E 'Enterprise Server'; then\\n            PLATFORM_NAME=sles\\n            t_version=`\\/bin\\/cat \\/etc\\/SuSE-release | grep VERSION | sed 's\\/^VERSION = \\\\(\\\\d*\\\\)\\/\\\\1\\/' `\\n            t_patchlevel=`cat \\/etc\\/SuSE-release | grep PATCHLEVEL | sed 's\\/^PATCHLEVEL = \\\\(\\\\d*\\\\)\\/\\\\1\\/' `\\n            PLATFORM_RELEASE=\\\"${t_version}\\\"\\n        fi\\n    elif [ -f \\/etc\\/system-release ]; then\\n        if grep -qi 'amazon linux' \\/etc\\/system-release; then\\n            PLATFORM_NAME=amazon\\n            PLATFORM_RELEASE=6\\n        else\\n            fail \\\"$(cat \\/etc\\/system-release) is not a supported platform for Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\n                    Please visit http:\\/\\/links.puppetlabs.com\\/puppet_enterprise_${PE_LINK_VER?}_platform_support to request support for this platform.\\\"\\n\\n        fi\\n    elif [ -z \\\"${PLATFORM_NAME:-\\\"\\\"}\\\" ]; then\\n        fail \\\"$(uname -s) is not a supported platform for Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\n            Please visit http:\\/\\/links.puppetlabs.com\\/puppet_enterprise_${PE_LINK_VER?}_platform_support to request support for this platform.\\\"\\n    fi\\nfi\\n\\nif [ -z \\\"${PLATFORM_NAME:-\\\"\\\"}\\\" ] || [ -z \\\"${PLATFORM_RELEASE:-\\\"\\\"}\\\" ]; then\\n    fail \\\"Unknown platform\\\"\\nfi\\n\\n# Architecture\\nif [ -z \\\"${PLATFORM_ARCHITECTURE:-\\\"\\\"}\\\" ]; then\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        solaris | aix )\\n            PLATFORM_ARCHITECTURE=\\\"$(uname -p)\\\"\\n            if [ \\\"${PLATFORM_ARCHITECTURE}\\\" = \\\"powerpc\\\" ] ; then\\n                PLATFORM_ARCHITECTURE='power'\\n            fi\\n            ;;\\n        *)\\n            PLATFORM_ARCHITECTURE=\\\"`uname -m`\\\"\\n            ;;\\n    esac\\n\\n    case \\\"${PLATFORM_ARCHITECTURE?}\\\" in\\n        x86_64)\\n            case \\\"${PLATFORM_NAME?}\\\" in\\n                ubuntu | debian )\\n                    PLATFORM_ARCHITECTURE=amd64\\n                    ;;\\n            esac\\n            ;;\\n        i686)\\n            PLATFORM_ARCHITECTURE=i386\\n            ;;\\n        ppc)\\n            PLATFORM_ARCHITECTURE=powerpc\\n            ;;\\n    esac\\nfi\\n\\n# Tag\\nif [ -z \\\"${PLATFORM_TAG:-\\\"\\\"}\\\" ]; then\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        # Enterprise linux (centos & rhel) share the same packaging\\n        # Amazon linux is similar enough for our packages\\n        rhel | centos | amazon )\\n            PLATFORM_TAG=\\\"el-${PLATFORM_RELEASE?}-${PLATFORM_ARCHITECTURE?}\\\"\\n            ;;\\n        *)\\n            PLATFORM_TAG=\\\"${PLATFORM_NAME?}-${PLATFORM_RELEASE?}-${PLATFORM_ARCHITECTURE?}\\\"\\n            ;;\\n    esac\\nfi\\n\\n# This is the end of the code copied from the upstream installer.\\n##############################################################################\\n\\n\\nrun_agent_install_from_url \\\"https:\\/\\/agent.demo.com:8140\\/packages\\/2017.2.0-rc1-424-ge1372a3\\/${PLATFORM_TAG}.bash\\\" \\\"$@\\\"\\n\\n# ...and we should be good.\\nexit 0\\n\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/upgrade.bash\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\",\n        \"content\": \"#! \\/bin\\/bash\\nset -u\\nset -e\\n\\n\\nPUPPET_CONF_DIR=\\\"\\/etc\\/puppetlabs\\/puppet\\\"\\nPUPPET_BIN_DIR=\\\"\\/opt\\/puppetlabs\\/puppet\\/bin\\\"\\nexport OLD_PUPPET_BIN_DIR=\\\"\\/opt\\/puppet\\/bin\\\"\\n\\nfail() { echo >&2 \\\"$@\\\"; exit 1; }\\ncmd()  { hash \\\"$1\\\" >&\\/dev\\/null; } # portable 'which'\\n\\npuppet_installed() {\\n  if [ -x \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" -o -x \\\"${OLD_PUPPET_BIN_DIR}\\/puppet\\\" ]; then\\n    return 0\\n  else\\n    return 1\\n  fi\\n}\\n\\npxp_present() {\\n    if cmd \\\"${PUPPET_BIN_DIR}\\/pxp-agent\\\"; then\\n        return 1\\n    else\\n        return 0\\n    fi\\n}\\n\\n# Echo back either the AIO puppet bin dir path, or PE 3.x pe-agent\\n# puppet bin dir.\\npuppet_bin_dir() {\\n    if [ -e \\\"${PUPPET_BIN_DIR?}\\\" ]; then\\n        t_puppet_bin_dir=\\\"${PUPPET_BIN_DIR?}\\\"\\n    else\\n        t_puppet_bin_dir=\\/opt\\/puppet\\/bin\\n    fi\\n    echo \\\"${t_puppet_bin_dir?}\\\"\\n}\\n\\n\\n### The following variable and function (upgrading_from_38x)\\n### Need to be declared after the `puppet_bin_dir` function\\n### due to the way bash handles function parsing\\n# Are we upgradeing from 3.8.x or 2015.2+ ?\\nORIGINAL_PUPPET_BIN_DIR=$(puppet_bin_dir)\\n\\nupgrading_from_38x() {\\n    [ \\\"${ORIGINAL_PUPPET_BIN_DIR?}\\\" = \\\"${OLD_PUPPET_BIN_DIR?}\\\" ]\\n}\\n\\nmktempfile() {\\n  if cmd mktemp; then\\n    if [ \\\"osx\\\" = \\\"${PLATFORM_NAME}\\\" ]; then\\n      mktemp -t installer\\n    else\\n      mktemp\\n    fi\\n  else\\n    echo \\\"\\/tmp\\/puppet-enterprise-installer.XXX-${RANDOM}\\\"\\n  fi\\n}\\n\\ncustom_puppet_configuration() {\\n  # Parse optional pre-installation configuration of Puppet settings via\\n  # command-line arguments. Arguments should be of the form\\n  #\\n  #   <section>:<setting>=<value>\\n  #\\n  # There are four valid section settings in puppet.conf: \\\"main\\\", \\\"master\\\",\\n  # \\\"agent\\\", \\\"user\\\". If you provide valid setting and value for one of these\\n  # four sections, it will end up in <confdir>\\/puppet.conf.\\n  #\\n  # There are two sections in csr_attributes.yaml: \\\"custom_attributes\\\" and\\n  # \\\"extension_requests\\\". If you provide valid setting and value for one\\n  # of these two sections, it will end up in <confdir>\\/csr_attributes.yaml.\\n  #\\n  # note:Custom Attributes are only present in the CSR, while Extension\\n  # Requests are both in the CSR and included as X509 extensions in the\\n  # signed certificate (and are thus available as \\\"trusted facts\\\" in Puppet).\\n  #\\n  # Regex is authoritative for valid sections, settings, and values.  Any input\\n  # that fails regex will trigger this script to fail with error message.\\n  regex='^(main|master|agent|user|custom_attributes|extension_requests):([^=]+)=(.*)$'\\n  declare -a attr_array\\n  declare -a extn_array\\n\\n  for entry in \\\"$@\\\"; do\\n    if ! [[ $entry =~ $regex ]]; then\\n      fail \\\"Unable to interpret argument: '${entry}'. Expected '<section>:<setting>=<value>' matching regex: '${regex}'\\\"\\n    else\\n      section=${BASH_REMATCH[1]}\\n      setting=${BASH_REMATCH[2]}\\n      value=${BASH_REMATCH[3]}\\n      case $section in\\n        custom_attributes)\\n          # Store the entry in attr_array for later addition to csr_attributes.yaml\\n          attr_array=(\\\"${attr_array[@]}\\\" \\\"${setting}: '${value}'\\\")\\n          ;;\\n        extension_requests)\\n          # Store the entry in extn_array for later addition to csr_attributes.yaml\\n          extn_array=(\\\"${extn_array[@]}\\\" \\\"${setting}: '${value}'\\\")\\n          ;;\\n        *)\\n          # Set the specified entry in puppet.conf\\n          \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set \\\"$setting\\\" \\\"$value\\\" --section \\\"$section\\\"\\n      esac\\n    fi\\n  done\\n\\n  # If the the length of the attr_array or extn_array is greater than zero, it\\n  # means we have settings, so we'll create the csr_attributes.yaml file.\\n  if [[ ${#attr_array[@]} -gt 0 || ${#extn_array[@]} -gt 0 ]]; then\\n    mkdir -p \\\"${PUPPET_CONF_DIR}\\\"\\n    echo '---' > \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n\\n    if [[ ${#attr_array[@]} -gt 0 ]]; then\\n      echo 'custom_attributes:' >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      for ((i = 0; i < ${#attr_array[@]}; i++)); do\\n        echo \\\"  ${attr_array[i]}\\\" >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      done\\n    fi\\n\\n    if [[ ${#extn_array[@]} -gt 0 ]]; then\\n      echo 'extension_requests:' >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      for ((i = 0; i < ${#extn_array[@]}; i++)); do\\n        echo \\\"  ${extn_array[i]}\\\" >> \\\"${PUPPET_CONF_DIR}\\/csr_attributes.yaml\\\"\\n      done\\n    fi\\n  fi\\n}\\n\\nensure_link() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource file \\\"${1?}\\\" ensure=link target=\\\"${2?}\\\"\\n}\\n\\nensure_agent_links() {\\n  target_path=\\\"\\/usr\\/local\\/bin\\\"\\n\\n  if mkdir -p \\\"${target_path}\\\" && [ -w \\\"${target_path}\\\" ]; then\\n    for bin in facter puppet pe-man hiera; do\\n      ensure_link \\\"${target_path}\\/${bin}\\\" \\\"${PUPPET_BIN_DIR?}\\/${bin}\\\"\\n    done\\n  else\\n    echo \\\"!!! WARNING: ${target_path} is inaccessible; unable to create convenience symlinks for puppet, hiera, facter and pe-man.  These executables may be found in ${pe_path?}.\\\" 1>&2\\n  fi\\n}\\n\\n# Detected existing installation? Return y if true, else n\\nis_upgrade() {\\n  if puppet_installed; then\\n    echo \\\"y\\\"\\n  else\\n    echo \\\"n\\\"\\n  fi\\n}\\n\\n# Sets server, certname and any custom puppet.conf flags passed in to the script\\npuppet_config_set() {\\n  # puppet config set does not create the [main] section if it does not exist\\n  # and does not use it if it has no settings (PUP-4755); and augeas does not\\n  # consider puppet.conf parseable with settings floating outside of a section\\n  puppet_conf=\\\"$(\\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config print confdir)\\/puppet.conf\\\"\\n  if ! grep '\\\\[main\\\\]' \\\"${puppet_conf}\\\"; then\\n    t_surgery='yes'\\n    cat >> \\\"${puppet_conf}\\\" <<EOF\\n[main]\\nplace=holder\\nEOF\\n  fi\\n\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set server agent.demo.com --section main\\n\\n  if [ \\\"${t_surgery}\\\" = 'yes' ]; then\\n    t_platform_test=\\\"x$(uname -s)\\\"\\n    if [ \\\"${t_platform_test}\\\" = \\\"xDarwin\\\" ]; then\\n      sed -i '' '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\"\\n    elif [ \\\"${t_platform_test}\\\" = \\\"xSunOS\\\" -o \\\"${t_platform_test}\\\" = \\\"xAIX\\\" ]; then\\n      sed '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\" > \\\"${puppet_conf}.new\\\" && mv \\\"${puppet_conf}.new\\\" \\\"${puppet_conf}\\\"\\n    else\\n      sed -i '\\/^place=holder$\\/ d' \\\"${puppet_conf}\\\"\\n    fi\\n  fi\\n\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" config set certname $(\\\"${PUPPET_BIN_DIR?}\\/facter\\\" fqdn | \\\"${PUPPET_BIN_DIR?}\\/ruby\\\" -e 'puts STDIN.read.downcase') --section main\\n  custom_puppet_configuration \\\"$@\\\"\\n\\n  # To ensure the new config settings take place and to work around differing OS behaviors on recieving a service start command while running\\n  # (on nix it triggers a puppet run, on osx it does nothing), restart the service by stopping and starting it again\\n  restart_puppet_agent\\n}\\n\\nrestart_puppet_agent() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=stopped\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=running enable=true\\n}\\n\\nstart_puppet_agent() {\\n  \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" resource service puppet ensure=running enable=true\\n}\\n\\nstop_puppet_agent() {\\n  \\\"$(puppet_bin_dir)\\/puppet\\\" resource service puppet ensure=stopped\\n  wait_for_puppet_lock\\n}\\n\\nmcollective_service_name() {\\n  if upgrading_from_38x; then\\n    t_mco_service_name='pe-mcollective'\\n  else\\n    t_mco_service_name='mcollective'\\n  fi\\n  echo \\\"${t_mco_service_name}\\\"\\n}\\n\\nmcollective_status() {\\n  output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource service \\\"$(mcollective_service_name)\\\" 2> \\/dev\\/null)\\n  case ${output?} in\\n    *ensure*=\\\\>*running*)\\n      echo \\\"running\\\";;\\n    *)\\n      echo \\\"stopped\\\";;\\n  esac\\n}\\n\\npxp_agent_status() {\\n  output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource service pxp-agent 2> \\/dev\\/null)\\n  case ${output?} in\\n    *ensure*=\\\\>*running*)\\n      echo \\\"running\\\";;\\n    *)\\n      echo \\\"stopped\\\";;\\n  esac\\n}\\n\\nensure_service() {\\n    pkg=${1?}\\n    state=${2?}\\n    \\\"$(puppet_bin_dir)\\/puppet\\\" resource service \\\"${pkg?}\\\" ensure=\\\"${state?}\\\"\\n    return $?\\n}\\n\\nwait_for_puppet_lock() {\\n  t_puppet_run_lock=`$(puppet_bin_dir)\\/puppet config print agent_catalog_run_lockfile`\\n  while [ -f \\\"${t_puppet_run_lock?}\\\" ]; do\\n      echo \\\"Waiting for Agent run lock ${t_puppet_run_lock?} to clear...\\\"\\n      sleep 10\\n  done\\n}\\n\\n# In version 7.10.0 curl introduced the -k flag and performs peer\\n# certificate validation by default. If peer validation is performed by\\n# default the -k flag is necessary for this script to work. However, if curl\\n# is older than 7.10.0 the -k flag does not exist. This function will return\\n# the correct invocation of curl depending on the version installed.\\ncurl_no_peer_verify() {\\n  curl_ver_regex='curl ([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)'\\n  [[ \\\"$(curl -V 2>\\/dev\\/null)\\\" =~ $curl_ver_regex ]]\\n  curl_majv=\\\"${BASH_REMATCH[1]-7}\\\"  # Default to 7  if no match\\n  curl_minv=\\\"${BASH_REMATCH[2]-10}\\\" # Default to 10 if no match\\n  if [[ \\\"$curl_majv\\\" -eq 7 && \\\"$curl_minv\\\" -le 9 ]] || [[ \\\"$curl_majv\\\" -lt 7 ]]; then\\n    curl_invocation=\\\"curl\\\"\\n  else\\n    curl_invocation=\\\"curl -k\\\"\\n  fi\\n\\n  $curl_invocation \\\"$@\\\"\\n}\\n\\n# Uses curl, or if not present, wget to download file from passed http url to a\\n# temporary location.\\n#\\n# Arguments\\n# 1. The url to download\\n# 2. The file to save it as\\n#\\n# Returns 0 or 1 if download fails.\\ndownload_from_url() {\\n    local t_url=\\\"${1?}\\\"\\n    local t_file=\\\"${2?}\\\"\\n\\n    if cmd curl; then\\n        # curl on AIX doesn't support -k, but it's the default behavior\\n        if [ \\\"$PLATFORM_NAME\\\" = \\\"aix\\\" ]; then\\n          CURL=\\\"curl_no_peer_verify\\\"\\n        else\\n          CURL=\\\"curl -k\\\"\\n        fi\\n        t_http_code=\\\"$($CURL --tlsv1 -sLo \\\"${t_file?}\\\" \\\"${t_url?}\\\" --write-out %{http_code} || fail \\\"curl failed to get ${t_url?}\\\")\\\"\\n    elif cmd wget; then\\n        # wget on AIX doesn't support SSL\\n        [ \\\"$PLATFORM_NAME\\\" = \\\"aix\\\" ] && fail \\\"Unable to download installation materials without curl\\\"\\n\\n        # Run wget and use awk to figure out the HTTP status.\\n        t_http_code=\\\"$(wget --secure-protocol=TLSv1 -O \\\"${t_file?}\\\" --no-check-certificate -S \\\"${t_url?}\\\" 2>&1 | awk '\\/HTTP\\\\\\/1.1\\/ { printf $2 }')\\\"\\n        if [ -z \\\"${t_file?}\\\" ]; then\\n            fail \\\"wget failed to get ${t_url?}\\\"\\n        fi\\n    else\\n        fail \\\"Unable to download installation materials without curl or wget\\\"\\n    fi\\n\\n    if [ \\\"${t_http_code?}\\\" == \\\"200\\\" ]; then\\n        return 0\\n    else\\n        return 1\\n    fi\\n}\\n\\nsupported_platform() {\\n  # Because of differences between how regex works between bash versions this\\n  # regex MUST be in a variable and passed into the test below UNQUOTED.\\n  t_supported_platform_regex='(el-(4|5|6|7)-(i386|x86_64|s390x))|(debian-(6|7|8)-(i386|amd64))|(ubuntu-(10\\\\.04|12\\\\.04|14\\\\.04|15\\\\.04|15\\\\.10|16\\\\.04)-(i386|amd64))|(sles-(10|11|12)-(i386|x86_64|s390x))|(fedora-(2[2-5])-(i386|x86_64))|(solaris-(10|11)-(i386|sparc))|(aix-(5\\\\.3|6\\\\.1|7\\\\.1)-power)|(osx-10\\\\.(9|10|11|12)-x86_64)'\\n  [[ \\\"${1?}\\\" =~ ${t_supported_platform_regex:?} ]]\\n}\\n\\nrun_agent_install_from_url() {\\n    t_agent_install_url=\\\"${1?}\\\"\\n\\n    t_install_file=$(mktempfile)\\n    if ! download_from_url \\\"${t_agent_install_url?}\\\" \\\"${t_install_file?}\\\"; then\\n        if supported_platform \\\"${PLATFORM_TAG?}\\\"; then\\n            fail \\\"The agent packages needed to support ${PLATFORM_TAG} are not present on your master. \\\\\\n    To add them, apply the pe_repo::platform::$(echo \\\"${PLATFORM_TAG?}\\\" | tr - _ | tr -dc '[:alnum:]_') class to your master node and then run Puppet. \\\\\\n    The required agent packages should be retrieved when puppet runs on the master, after which you can run the install.bash script again.\\\"\\n        else\\n            fail \\\"This method of agent installation is not supported for ${PLATFORM_TAG?} in Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\\"\\n        fi\\n    fi\\n\\n    bash \\\"${t_install_file?}\\\" \\\"${@: 2}\\\" || fail \\\"Error running install script ${t_install_file?}\\\"\\n}\\n\\n# Sets PLATFORM_NAME to a value that PE expects\\n#\\n# Arguments:\\n# PLATFORM_NAME\\n# RELEASE_FILE\\n#\\n# Side-effect:\\n# Modifies PLATFORM_NAME\\nfunction sanitize_platform_name() {\\n    # Sanitize name for unusual platforms\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        redhatenterpriseserver | redhatenterpriseclient | redhatenterpriseas | redhatenterprisees | enterpriseenterpriseserver | redhatenterpriseworkstation | redhatenterprisecomputenode | oracleserver)\\n            PLATFORM_NAME=rhel\\n            ;;\\n        enterprise*)\\n            PLATFORM_NAME=centos\\n            ;;\\n        scientific | scientifics | scientificsl)\\n            PLATFORM_NAME=rhel\\n            ;;\\n        oracle | ol)\\n            PLATFORM_NAME=rhel\\n            ;;\\n        'suse linux')\\n            PLATFORM_NAME=sles\\n            ;;\\n        amazonami)\\n            PLATFORM_NAME=amazon\\n            ;;\\n    esac\\n\\n    if [ -r \\\"${RELEASE_FILE:-}\\\" ] && grep -E \\\"Cumulus Linux\\\" \\\"${RELEASE_FILE}\\\" &> \\/dev\\/null; then\\n        PLATFORM_NAME=cumulus\\n    fi\\n}\\n\\n# Sets PLATFORM_RELEASE to a value that PE expects\\n#\\n# Arguments:\\n# PLATFORM_NAME\\n# PLATFORM_RELEASE\\n#\\n# Side-effect:\\n# Modifies PLATFORM_RELEASE\\nfunction sanitize_platform_release() {\\n    # Sanitize release for unusual platforms\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        centos | rhel | sles)\\n            # Platform uses only number before period as the release,\\n            # e.g. \\\"CentOS 5.5\\\" is release \\\"5\\\"\\n            PLATFORM_RELEASE=$(echo -n \\\"${PLATFORM_RELEASE?}\\\" | cut -d. -f1)\\n            ;;\\n        debian)\\n            # Platform uses only number before period as the release,\\n            # e.g. \\\"Debian 6.0.1\\\" is release \\\"6\\\"\\n            PLATFORM_RELEASE=$(echo -n \\\"${PLATFORM_RELEASE?}\\\" | cut -d. -f1)\\n            if [ ${PLATFORM_RELEASE} = \\\"testing\\\" ] ; then\\n                PLATFORM_RELEASE=7\\n            fi\\n            ;;\\n        cumulus)\\n            PLATFORM_RELEASE=$(echo -n \\\"${PLATFORM_RELEASE?}\\\" | cut -d'.' -f'1,2')\\n            ;;\\n    esac\\n}\\n\\n##############################################################################\\n# We need to know what the PE platform tag is for this node, which requires\\n# digging through a bunch of data to extract it.  This is currently the best\\n# mechanism available to do this, which is copied from the PE\\n# installer itself.\\nif [ -z \\\"${PLATFORM_NAME:-\\\"\\\"}\\\" ] || [ -z \\\"${PLATFORM_RELEASE:-\\\"\\\"}\\\" ]; then\\n    # https:\\/\\/www.freedesktop.org\\/software\\/systemd\\/man\\/os-release.html#Description\\n    # Try \\/etc\\/os-release first, then \\/usr\\/lib\\/os-release, then legacy pre-systemd methods\\n    if [ -f \\\"\\/etc\\/os-release\\\" ] || [ -f \\\"\\/usr\\/lib\\/os-release\\\" ]; then\\n        if [ -f \\\"\\/etc\\/os-release\\\" ]; then\\n            RELEASE_FILE=\\\"\\/etc\\/os-release\\\"\\n        else\\n            RELEASE_FILE=\\\"\\/usr\\/lib\\/os-release\\\"\\n        fi\\n        PLATFORM_NAME=$(source \\\"${RELEASE_FILE}\\\"; echo -n \\\"${ID}\\\")\\n        sanitize_platform_name\\n        PLATFORM_RELEASE=$(source \\\"${RELEASE_FILE}\\\"; echo -n \\\"${VERSION_ID}\\\")\\n        sanitize_platform_release\\n    # Try identifying using lsb_release.  This takes care of Ubuntu\\n    # (lsb-release is part of ubuntu-minimal).\\n    elif type lsb_release > \\/dev\\/null 2>&1; then\\n        t_prepare_platform=`lsb_release -icr 2>&1`\\n\\n        PLATFORM_NAME=\\\"$(echo -n \\\"${t_prepare_platform?}\\\" | grep -E '^Distributor ID:' | cut -s -d: -f2 | sed 's\\/[[:space:]]\\/\\/' | tr '[[:upper:]]' '[[:lower:]]')\\\"\\n        sanitize_platform_name\\n\\n        # Release\\n        PLATFORM_RELEASE=\\\"$(echo -n \\\"${t_prepare_platform?}\\\" | grep -E '^Release:' | cut -s -d: -f2 | sed 's\\/[[:space:]]\\/\\/g')\\\"\\n        sanitize_platform_release\\n    elif [ \\\"x$(uname -s)\\\" = \\\"xDarwin\\\" ]; then\\n        PLATFORM_NAME=\\\"osx\\\"\\n        # sw_vers returns something like 10.9.2, but we only want 10.9 so chop off the end\\n        t_platform_release=\\\"$(\\/usr\\/bin\\/sw_vers -productVersion | cut -d'.' -f1,2)\\\"\\n        PLATFORM_RELEASE=\\\"${t_platform_release?}\\\"\\n    # Test for Solaris.\\n    elif [ \\\"x$(uname -s)\\\" = \\\"xSunOS\\\" ]; then\\n        PLATFORM_NAME=\\\"solaris\\\"\\n        t_platform_release=\\\"$(uname -r)\\\"\\n        # JJM We get back 5.10 but we only care about the right side of the decimal.\\n        PLATFORM_RELEASE=\\\"${t_platform_release##*.}\\\"\\n    elif [ \\\"x$(uname -s)\\\" = \\\"xAIX\\\" ] ; then\\n        PLATFORM_NAME=\\\"aix\\\"\\n        t_platform_release=\\\"$(oslevel | cut -d'.' -f1,2 | sed -e 's\\/7\\\\.2\\/7\\\\.1\\/')\\\"\\n        PLATFORM_RELEASE=\\\"${t_platform_release}\\\"\\n\\n        # Test for RHEL variant. RHEL, CentOS, OEL\\n    elif [ -f \\/etc\\/redhat-release ] && [ -r \\/etc\\/redhat-release ] && [ -s \\/etc\\/redhat-release ]; then\\n        # Oracle Enterprise Linux 5.3 and higher identify the same as RHEL\\n        if grep -qi 'red hat enterprise' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME=rhel\\n        elif grep -qi 'centos' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME=centos\\n        elif grep -qi 'scientific' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME=rhel\\n        elif grep -qi 'fedora' \\/etc\\/redhat-release; then\\n            PLATFORM_NAME='fedora'\\n        fi\\n        # Release - take first digits after ' release ' only.\\n        PLATFORM_RELEASE=\\\"$(sed 's\\/.*\\\\ release\\\\ \\\\([[:digit:]]\\\\+\\\\).*\\/\\\\1\\/g;q' \\/etc\\/redhat-release)\\\"\\n    # Test for Debian releases\\n    elif [ -f \\/etc\\/debian_version ] && [ -r \\/etc\\/debian_version ] && [ -s \\/etc\\/debian_version ]; then\\n        t_prepare_platform__debian_version_file=\\\"\\/etc\\/debian_version\\\"\\n        t_prepare_platform__debian_version=`cat \\/etc\\/debian_version`\\n\\n        if cat \\\"${t_prepare_platform__debian_version_file?}\\\" | grep -E '^[[:digit:]]' > \\/dev\\/null; then\\n            PLATFORM_NAME=debian\\n            PLATFORM_RELEASE=\\\"$(echo -n \\\"${t_prepare_platform__debian_version?}\\\" | sed 's\\/\\\\..*\\/\\/')\\\"\\n        elif cat \\\"${t_prepare_platform__debian_version_file?}\\\" | grep -E '^wheezy' > \\/dev\\/null; then\\n            PLATFORM_NAME=debian\\n            PLATFORM_RELEASE=\\\"7\\\"\\n        fi\\n    elif [ -f \\/etc\\/SuSE-release ] && [ -r \\/etc\\/SuSE-release ]; then\\n        t_prepare_platform__suse_version=`cat \\/etc\\/SuSE-release`\\n\\n        if echo -n \\\"${t_prepare_platform__suse_version?}\\\" | grep -E 'Enterprise Server'; then\\n            PLATFORM_NAME=sles\\n            t_version=`\\/bin\\/cat \\/etc\\/SuSE-release | grep VERSION | sed 's\\/^VERSION = \\\\(\\\\d*\\\\)\\/\\\\1\\/' `\\n            t_patchlevel=`cat \\/etc\\/SuSE-release | grep PATCHLEVEL | sed 's\\/^PATCHLEVEL = \\\\(\\\\d*\\\\)\\/\\\\1\\/' `\\n            PLATFORM_RELEASE=\\\"${t_version}\\\"\\n        fi\\n    elif [ -f \\/etc\\/system-release ]; then\\n        if grep -qi 'amazon linux' \\/etc\\/system-release; then\\n            PLATFORM_NAME=amazon\\n            PLATFORM_RELEASE=6\\n        else\\n            fail \\\"$(cat \\/etc\\/system-release) is not a supported platform for Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\n                    Please visit http:\\/\\/links.puppetlabs.com\\/puppet_enterprise_${PE_LINK_VER?}_platform_support to request support for this platform.\\\"\\n\\n        fi\\n    elif [ -z \\\"${PLATFORM_NAME:-\\\"\\\"}\\\" ]; then\\n        fail \\\"$(uname -s) is not a supported platform for Puppet Enterprise v2017.2.0-rc1-424-ge1372a3\\n            Please visit http:\\/\\/links.puppetlabs.com\\/puppet_enterprise_${PE_LINK_VER?}_platform_support to request support for this platform.\\\"\\n    fi\\nfi\\n\\nif [ -z \\\"${PLATFORM_NAME:-\\\"\\\"}\\\" ] || [ -z \\\"${PLATFORM_RELEASE:-\\\"\\\"}\\\" ]; then\\n    fail \\\"Unknown platform\\\"\\nfi\\n\\n# Architecture\\nif [ -z \\\"${PLATFORM_ARCHITECTURE:-\\\"\\\"}\\\" ]; then\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        solaris | aix )\\n            PLATFORM_ARCHITECTURE=\\\"$(uname -p)\\\"\\n            if [ \\\"${PLATFORM_ARCHITECTURE}\\\" = \\\"powerpc\\\" ] ; then\\n                PLATFORM_ARCHITECTURE='power'\\n            fi\\n            ;;\\n        *)\\n            PLATFORM_ARCHITECTURE=\\\"`uname -m`\\\"\\n            ;;\\n    esac\\n\\n    case \\\"${PLATFORM_ARCHITECTURE?}\\\" in\\n        x86_64)\\n            case \\\"${PLATFORM_NAME?}\\\" in\\n                ubuntu | debian )\\n                    PLATFORM_ARCHITECTURE=amd64\\n                    ;;\\n            esac\\n            ;;\\n        i686)\\n            PLATFORM_ARCHITECTURE=i386\\n            ;;\\n        ppc)\\n            PLATFORM_ARCHITECTURE=powerpc\\n            ;;\\n    esac\\nfi\\n\\n# Tag\\nif [ -z \\\"${PLATFORM_TAG:-\\\"\\\"}\\\" ]; then\\n    case \\\"${PLATFORM_NAME?}\\\" in\\n        # Enterprise linux (centos & rhel) share the same packaging\\n        # Amazon linux is similar enough for our packages\\n        rhel | centos | amazon )\\n            PLATFORM_TAG=\\\"el-${PLATFORM_RELEASE?}-${PLATFORM_ARCHITECTURE?}\\\"\\n            ;;\\n        *)\\n            PLATFORM_TAG=\\\"${PLATFORM_NAME?}-${PLATFORM_RELEASE?}-${PLATFORM_ARCHITECTURE?}\\\"\\n            ;;\\n    esac\\nfi\\n\\n# This is the end of the code copied from the upstream installer.\\n##############################################################################\\n\\n\\n\\n# Backup the old hiera.yaml, because rpm, for example will remove it\\n# when pe-hiera package is uninstalled\\nbackup_hiera_yaml() {\\n    t_timestamp=$(date +'%Y%m%d%H%M%s')\\n    t_hiera_yaml_backup=\\\"\\/etc\\/puppetlabs\\/puppet\\/hiera.yaml-${t_timestamp?}.bak\\\"\\n    echo \\\"Backing up hiera.yaml to ${t_hiera_yaml_backup?}\\\"\\n    cp \\/etc\\/puppetlabs\\/puppet\\/hiera.yaml \\\"${t_hiera_yaml_backup?}\\\"\\n    HIERA_YAML_BACKUP=${t_hiera_yaml_backup?}\\n    # The pe-migration-code.rb script will look for this environment variable.\\n    export HIERA_YAML_BACKUP\\n}\\n\\n# Backup the old puppet.conf so that it is available for trouble shooting.\\nbackup_puppet_conf() {\\n    t_timestamp=$(date +'%Y%m%d%H%M%s')\\n    t_puppet_conf_backup=\\\"\\/etc\\/puppetlabs\\/puppet\\/puppet.conf-${t_timestamp?}.bak\\\"\\n    echo \\\"Backing up puppet.conf to ${t_puppet_conf_backup?}\\\"\\n    cp \\/etc\\/puppetlabs\\/puppet\\/puppet.conf \\\"${t_puppet_conf_backup?}\\\"\\n}\\n\\ncheck_package() {\\n    pkg=\\\"${1?}\\\"\\n    output=$(\\\"$(puppet_bin_dir)\\/puppet\\\" resource package \\\"${pkg?}\\\" 2> \\/dev\\/null)\\n    case ${output?} in\\n        *ensure*=\\\\>*purged*)\\n            return 1;;\\n        *ensure*=\\\\>*absent*)\\n            return 1;;\\n        *)\\n            return 0;;\\n    esac\\n}\\n\\n# If you need to force the code migration during a compile master upgrade (say\\n# on a second running of the upgrade script, which won't detect that it was\\n# upgradiang from 3.8.x if puppet-agent has already been installed), then you\\n# can supply FORCE_PE_CODE_MIGRATION=1 in your environment to ensure that the\\n# code migration script runs to update puppet.conf and hiera.yaml.  But you\\n# will also need to set HIERA_YAML_BACKUP to the location of your 3.8.x\\n# hiera.yaml file if one was being used.\\nrun_pe_code_migration() {\\n    t_code_migration_url=\\\"${1?}\\\"\\n\\n    if upgrading_from_38x || [ \\\"${FORCE_PE_CODE_MIGRATION:-false}\\\" != \\\"false\\\" ]; then\\n        echo \\\" * Preparing to run script to update puppet.conf and hiera.yaml\\\"\\n        t_install_file=$(mktempfile)\\n        if ! download_from_url \\\"${t_code_migration_url?}\\\" \\\"${t_install_file?}\\\"; then\\n            fail \\\"Unable to retrieve code migration script from '${t_code_migration_url?}'\\\"\\n        fi\\n\\n        \\\"${PUPPET_BIN_DIR?}\\/ruby\\\" \\\"${t_install_file?}\\\" || fail \\\"Error running code migration script: ${t_install_file?}\\\"\\n    else\\n        return 0\\n    fi\\n}\\n\\n# Discover services\\nt_38x_services=(pe-puppet pe-mcollective)\\nt_compile_or_amq_services=(pe-puppetserver pe-activemq)\\nt_puppet_agent_services=(puppet mcollective pxp-agent)\\nt_shutdown_services=()\\n\\nif [ -e \\\"$(puppet_bin_dir)\\/puppet\\\" ]; then\\n    for service in \\\"${t_compile_or_amq_services[@]?}\\\"; do\\n        if check_package ${service?}; then\\n            t_shutdown_services+=(${service?})\\n        fi\\n    done\\n\\n    if upgrading_from_38x; then\\n        for service in \\\"${t_38x_services[@]?}\\\"; do\\n            ensure_service \\\"${service?}\\\" stopped\\n            if [ \\\"${service?}\\\" == \\\"pe-puppet\\\" ]; then\\n                wait_for_puppet_lock\\n            fi\\n        done\\n    else\\n        for service in \\\"${t_puppet_agent_services[@]?}\\\"; do\\n            if [ \\\"${service?}\\\" == \\\"puppet\\\" ]; then\\n                stop_puppet_agent\\n            elif [ \\\"${service?}\\\" == \\\"pxp-agent\\\" ]; then\\n                if pxp_present; then\\n                    ensure_service \\\"${service?}\\\" stopped\\n                fi\\n            else\\n                ensure_service \\\"${service?}\\\" stopped\\n            fi\\n        done\\n    fi\\n\\n    for service in \\\"${t_shutdown_services[@]?}\\\"; do\\n        ensure_service \\\"${service?}\\\" stopped\\n    done\\nfi\\n\\n# Before upgrading packages on 3.8.x\\nif upgrading_from_38x; then\\n    backup_hiera_yaml\\n    backup_puppet_conf\\n    echo \\\"Moving old puppet enterprise modules aside to \\/opt\\/puppet\\/share\\/puppet\\/modules.bak\\\"\\n    mv \\/opt\\/puppet\\/share\\/puppet\\/modules \\/opt\\/puppet\\/share\\/puppet\\/modules.bak\\nfi\\n\\nt_remote_installer_url=\\\"https:\\/\\/agent.demo.com:8140\\/packages\\/2017.2.0-rc1-424-ge1372a3\\\"\\n\\nrun_agent_install_from_url \\\"${t_remote_installer_url?}\\/install.bash\\\" \\\"$@\\\"\\n\\n# Package installation may have re-started the service\\nstop_puppet_agent\\n\\n# Run puppet to get most up to date version of the node.  Do not use -t,\\n# because there will be changes, and we don't want to halt the script here\\n# because Puppet returns a 2 (we are running set -e)\\nFACTER_pe_build=2017.2.0-rc1-424-ge1372a3 \\\"${PUPPET_BIN_DIR?}\\/puppet\\\" agent --onetime --verbose --no-daemonize --no-usecacheonfailure --no-splay --show_diff\\n\\n# Expects:\\n# * HIERA_YAML_BACKUP\\nrun_pe_code_migration \\\"${t_remote_installer_url?}\\/pe-code-migration.rb\\\"\\n\\n# Remove unnecessary logrotate.d configuration\\nrm -f \\/etc\\/logrotate.d\\/pe-puppetserver\\n\\n# Start services that have been previously shutdown\\nfor service in \\\"${t_shutdown_services[@]?}\\\" \\\"${t_puppet_agent_services[@]?}\\\"; do\\n    ensure_service \\\"${service?}\\\" running\\ndone\\n\\n# ...and we should be good.\\nexit 0\\n\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64\",\n      \"tags\": [\n        \"file\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/repo.pp\",\n      \"line\": 112,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"link\",\n        \"target\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\",\n        \"backup\": false,\n        \"mode\": \"0644\",\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_concat::Setup\",\n      \"tags\": [\n        \"class\",\n        \"pe_concat::setup\",\n        \"pe_concat\",\n        \"setup\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_concat::setup\",\n        \"pe_concat\",\n        \"setup\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/setup.pp\",\n      \"line\": 56,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"mode\": \"0755\",\n        \"source\": \"puppet:\\/\\/\\/modules\\/pe_concat\\/concatfragments.sh\",\n        \"backup\": false,\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_concat::setup\",\n        \"pe_concat\",\n        \"setup\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/setup.pp\",\n      \"line\": 62,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0755\",\n        \"backup\": false,\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_concat::setup\",\n        \"pe_concat\",\n        \"setup\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/setup.pp\",\n      \"line\": 62,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0755\",\n        \"backup\": false,\n        \"owner\": \"root\",\n        \"group\": \"root\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 149,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"force\": true,\n        \"ignore\": [\n          \".svn\",\n          \".git\",\n          \".gitignore\"\n        ],\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"purge\": true,\n        \"recurse\": true,\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 159,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat.out\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 164,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 169,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0644\",\n        \"replace\": true,\n        \"alias\": \"pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n        \"source\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat.out\",\n        \"backup\": \"puppet\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg\",\n      \"tags\": [\n        \"exec\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 187,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\\"\",\n        \"alias\": \"pe_concat_\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\",\n        \"notify\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"subscribe\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg]\",\n        \"unless\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\\" -t\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"require\": [\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat]\"\n        ]\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver certificate-authority-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"certificate-authority-service\",\n        \"class\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.ca.certificate-authority-service\\/certificate-authority-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver certificate-authority-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 149,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"force\": true,\n        \"ignore\": [\n          \".svn\",\n          \".git\",\n          \".gitignore\"\n        ],\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"purge\": true,\n        \"recurse\": true,\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 159,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat.out\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 164,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 169,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0644\",\n        \"replace\": false,\n        \"alias\": \"pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"source\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat.out\",\n        \"backup\": \"puppet\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n      \"tags\": [\n        \"exec\",\n        \"pe_concat\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 187,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\\"\",\n        \"alias\": \"pe_concat_\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\",\n        \"notify\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n        \"subscribe\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf]\",\n        \"unless\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\\" -t\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"require\": [\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat]\"\n        ]\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments\\/10_00_header__etc_puppetlabs_puppetserver_conf.d_auth.conf\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"authorization: {\\n  rules: []\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_00_header_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments\\/10_99_footer__etc_puppetlabs_puppetserver_conf.d_auth.conf\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"pe_puppet_authorization\",\n        \"class\",\n        \"puppet_enterprise::master::tk_authz\",\n        \"puppet_enterprise\",\n        \"master\",\n        \"tk_authz\",\n        \"puppet_enterprise::profile::master::auth_conf\",\n        \"profile\",\n        \"auth_conf\",\n        \"puppet_enterprise::profile::master\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"}\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_99_footer_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver jetty9-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\\/jetty9-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver jetty9-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver pe-master-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-master-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.enterprise.services.master.master-service\\/pe-master-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver pe-master-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver request-handler-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"request-handler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.request-handler.request-handler-service\\/request-handler-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver request-handler-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver jruby-puppet-pooled-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"jruby-puppet-pooled-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.jruby.jruby-puppet-service\\/jruby-puppet-pooled-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver jruby-puppet-pooled-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver jruby-pool-manager-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"jruby-pool-manager-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service\\/jruby-pool-manager-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver jruby-pool-manager-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver metrics-puppet-profiler-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"metrics-puppet-profiler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.enterprise.services.puppet-profiler.puppet-profiler-service\\/metrics-puppet-profiler-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver metrics-puppet-profiler-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver metrics-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"metrics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver metrics-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver puppet-server-config-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet-server-config-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.config.puppet-server-config-service\\/puppet-server-config-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver puppet-server-config-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver puppet-admin-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet-admin-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.puppet-admin.puppet-admin-service\\/puppet-admin-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver puppet-admin-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver webrouting-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"webrouting-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\\/webrouting-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver webrouting-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver pe-legacy-routes-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-legacy-routes-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.enterprise.services.legacy-routes.pe-legacy-routes-service\\/pe-legacy-routes-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver pe-legacy-routes-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver status-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"status-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver status-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver authorization-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"authorization-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.authorization.authorization-service\\/authorization-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver authorization-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver scheduler-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"scheduler-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\\/scheduler-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver scheduler-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver pe-jruby-metrics-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"pe-jruby-metrics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.enterprise.services.jruby.pe-jruby-metrics-service\\/pe-jruby-metrics-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver pe-jruby-metrics-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver analytics-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"analytics-service\",\n        \"class\",\n        \"puppet_enterprise::master::puppetserver\",\n        \"master\",\n        \"puppetserver\",\n        \"puppet_enterprise::master\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.enterprise.services.analytics.analytics-service\\/analytics-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver analytics-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n        \"group\": \"pe-puppet\",\n        \"require\": \"Package[pe-puppetserver]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver versioned-code-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"versioned-code-service\",\n        \"class\",\n        \"puppet_enterprise::master::file_sync_disabled\",\n        \"master\",\n        \"file_sync_disabled\",\n        \"puppet_enterprise::profile::master\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.services.versioned-code-service.versioned-code-service\\/versioned-code-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_puppetserver versioned-code-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/pk8_cert.pp\",\n      \"line\": 22,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"openssl pkcs8 -topk8 -inform PEM -outform DER -in \\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem -out \\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8 -nocrypt\",\n        \"path\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/bin\",\n          \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\"\n        ],\n        \"onlyif\": \"test ! -e '\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8' -o '\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8' -ot '\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem'\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"pe-console-services::server_cert\",\n        \"pe-console-services\",\n        \"server_cert\",\n        \"class\",\n        \"puppet_enterprise::profile::console::certs\",\n        \"profile\",\n        \"console\",\n        \"puppet_enterprise::profile::console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/pk8_cert.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\",\n        \"mode\": \"0400\",\n        \"before\": \"Exec[pe-console-services service full restart]\",\n        \"backup\": false,\n        \"require\": \"Package[pe-console-services]\",\n        \"notify\": \"Service[pe-console-services]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n      \"tags\": [\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 22,\n      \"exported\": false,\n      \"parameters\": {\n        \"notify\": \"Exec[pe-console-services service full restart]\",\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"mode\": \"0644\",\n        \"warn\": false,\n        \"force\": false,\n        \"backup\": \"puppet\",\n        \"replace\": true,\n        \"order\": \"alpha\",\n        \"ensure_newline\": false\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services activity-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.activity.services\\/activity-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services jetty9-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\\/jetty9-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.rbac\\/rbac-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-storage-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.storage.permissioned\\/rbac-storage-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-http-api-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.http.api\\/rbac-http-api-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-authn-middleware\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.http.middleware\\/rbac-authn-middleware\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services activity-reporting-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.activity.services\\/activity-reporting-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services classifier-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.classifier.main\\/classifier-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services webrouting-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\\/webrouting-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-consumer-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.consumer\\/rbac-consumer-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-status-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.status\\/rbac-status-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-authn-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.authn\\/rbac-authn-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services rbac-authz-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac.services.authz\\/rbac-authz-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services pe-console-ui-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.pe-console-ui.service\\/pe-console-ui-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services pe-console-auth-ui-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.pe-console-auth-ui.service\\/pe-console-auth-ui-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services status-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services scheduler-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\\/scheduler-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services metrics-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"console-services metrics-webservice\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-console-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"User\",\n      \"title\": \"peadmin\",\n      \"tags\": [\n        \"user\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client::user\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/user.pp\",\n      \"line\": 16,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"shell\": \"\\/bin\\/bash\",\n        \"comment\": \"peadmin\",\n        \"home\": \"\\/var\\/lib\\/peadmin\",\n        \"password\": \"!!\",\n        \"membership\": \"minimum\"\n      }\n    },\n    {\n      \"type\": \"Group\",\n      \"title\": \"peadmin\",\n      \"tags\": [\n        \"group\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client::user\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"user\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/user.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::user\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"user\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/user.pp\",\n      \"line\": 29,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0701\",\n        \"backup\": false,\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.bashrc.custom\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::user\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"user\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/user.pp\",\n      \"line\": 36,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"mode\": \"0600\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Pe_file_line\",\n      \"title\": \"peadmin:path\",\n      \"tags\": [\n        \"pe_file_line\",\n        \"peadmin:path\",\n        \"puppet_enterprise::mcollective::client::user\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"user\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/user.pp\",\n      \"line\": 43,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/var\\/lib\\/peadmin\\/.bashrc.custom\",\n        \"line\": \"export PATH=\\\"\\/opt\\/puppetlabs\\/puppet\\/bin:${PATH}\\\"\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 42,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0701\",\n        \"backup\": false,\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/ca.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 47,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/ca.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/agent.demo.com.cert.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 51,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/certs\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/agent.demo.com.private_key.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 55,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/peadmin-private.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 59,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"-----BEGIN RSA PRIVATE KEY-----\\nSOMETHINGSOMETHING\\n-----END RSA PRIVATE KEY-----\\n\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/peadmin-public.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 65,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwJWaN7cdOi4HXt7NRxZu\\nmSW8fVPcf4AEm\\/cSqTzEUxUE4dWf9kGfqhJWzrB0oSKzAXwCI7YIEcpxt2KnUm8B\\n9I7cLP67I2wFFHJsT41NZRqNZo5qlc19GQw4AxBT2F4ZIRmTTysE6uwCpF05dQCI\\nM\\/IHHSQgWnTn4rBMzIRxvDq1FcGM\\/95wxHbZBWBxI0P93h7Oq8XdZdErczwzYC5B\\nfzAxE0iM9mF17CM2YnkmMMBpXhzl\\/rg0JPZ0mNWBUR8XCGiq33KSRvcBLBvBN9\\/Q\\nZuWFSeX1ZpcrCJOheFLXq6I2aOv99GH6MoyKN8Ix\\/cyz\\/i\\/FmugKm4l0VEBXOG\\/P\\nDOyAMkPOPncQ9dUdY9uhtOt52Vf\\/0mjCy1o96qkv1ZXYe+vrBsKK\\/Gn9KuSV2nIO\\n4\\/9pMYPg3WC2EjTFmkV\\/+DNQGILgEjJHQF7UNWXRcJqjVJ2GoVBwX3\\/e8fnwsv6b\\nfa3oAQr\\/cExI8PRoDUCBP3djfoHCqswaXdjaQxkYGaM1c2X8wdxa5P0I6aixsAk4\\ngSWSvr9I0GH2z6gZXUo12JDW67so4OcI\\/klmUp+u2ichB521aPafScrHjm7hU+w8\\nb2isugsbtzWDQneH3nfOQAhJwNuSwqLXu8bGXILTKUaat0IsjpdQiRrORZ4tt8BO\\nG++CcoLqFsZH1wfRXjrG+rECAwEAAQ==\\n-----END PUBLIC KEY-----\\n\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/var\\/lib\\/peadmin\\/.mcollective.d\\/mcollective-public.pem\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::mcollective::client::certs\",\n        \"puppet_enterprise\",\n        \"mcollective\",\n        \"client\",\n        \"certs\",\n        \"peadmin\",\n        \"puppet_enterprise::mcollective::client\",\n        \"class\",\n        \"puppet_enterprise::profile::mcollective::peadmin\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/mcollective\\/client\\/certs.pp\",\n      \"line\": 71,\n      \"exported\": false,\n      \"parameters\": {\n        \"content\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtzwjy9nfwSdNPHU2J9IO\\niRrbKgNqD4J\\/QRjpjE5GcJ5GQ+wqqRquKs5BhL9vATotjkClFQs\\/a1uiuDLV3BrM\\nmt2z3jsd\\/OPoma2H5OErZGD+9q+lytdPuLEm0J8gTFJ26pp0I8fLnTW\\/1IpNCVFw\\n5mswAkSKxK4pDGBX9vlWgwT84bvYMfU7J0AJdyEb7VxVGZTuk4m+ICTZ5gwWGLUQ\\n8pOX7h72jS76tqckDbRzAViSwvvzraQgGoRnpazTaKh5Q9eNN45nH9couAFkqU8m\\n4AooMHC4sr9RCIFx1r7+Xqg6OsRxe+9bvtXo6AfD+7oyyt8bO3PoqJzPK3KM51EI\\nzY4Qx2OsGw2XHXVfEHgAVD3u3D0UrIoH+1GFU8oTDf+CGu0r8\\/IUVj3fG7\\/XOjc6\\nrc4oGC9TvSISBTXpngxaYx1Al+AUI3vQl3BqIEmU805dxZvyJ3c9uKMXku9yhvib\\n5\\/7EpyHUEZ8Wfbj3IK0Wnopu7ZO31nch5I\\/qmE0HUzxKUOEjnbkOsCOwPH9K+V1r\\nra7Oya94+OTFq17fYhhH9qVpDnWG0HoKKTGgJbj6f5noyq2PCzDN\\/qzP4XaNERSE\\nrNHpPglncyOz26LgASb2\\/4hn0bdoJ3gTuc++O+dNMwDuVQN39PMiXp48MmgcxDYy\\nbIe8xC4JSs8x8SnSnvVoLzECAwEAAQ==\\n-----END PUBLIC KEY-----\\n\",\n        \"backup\": false,\n        \"mode\": \"0400\",\n        \"owner\": \"peadmin\",\n        \"group\": \"peadmin\",\n        \"show_diff\": false\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-dispatch: allow pcp-brokers\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/server\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \n            }\n          },\n          \"allow\": [\n            \"agent.demo.com\"\n          ],\n          \"name\": \"dispatch: allow pcp-brokers\",\n          \"sort-order\": 500\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services orchestrator-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.orchestrator.service\\/orchestrator-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services orchestrator-dispatch-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.orchestrator.dispatch\\/orchestrator-dispatch-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services jetty9-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\\/jetty9-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services remote-rbac-consumer-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac-client.services.rbac\\/remote-rbac-consumer-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services remote-activity-reporter\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.rbac-client.services.activity\\/remote-activity-reporter\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-pxp commands\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/pcp-broker\\/send\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \"message_type\": [\n                \"http:\\/\\/puppetlabs.com\\/rpc_non_blocking_request\",\n                \"http:\\/\\/puppetlabs.com\\/rpc_blocking_request\"\n              ]\n            }\n          },\n          \"allow\": [\n            \"agent.demo.com\",\n            \"agent.demo.com\"\n          ],\n          \"name\": \"pxp commands\",\n          \"sort-order\": 400\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-inventory request\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/pcp-broker\\/send\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \"message_type\": [\n                \"http:\\/\\/puppetlabs.com\\/inventory_request\"\n              ]\n            }\n          },\n          \"allow\": [\n            \"agent.demo.com\",\n            \"agent.demo.com\"\n          ],\n          \"name\": \"inventory request\",\n          \"sort-order\": 400\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-multi-cast with destination_report\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/pcp-broker\\/send\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \"targets\": [\n                \"pcp:\\/\\/*\\/agent\",\n                \"pcp:\\/\\/*\\/*\"\n              ],\n              \"destination_report\": \"true\"\n            }\n          },\n          \"allow\": [\n            \n          ],\n          \"name\": \"multi-cast with destination_report\",\n          \"sort-order\": 399\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-pcp-broker message\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/pcp-broker\\/send\",\n            \"type\": \"path\",\n            \"query-params\": {\n              \"message_type\": [\n                \"http:\\/\\/puppetlabs.com\\/associate_request\",\n                \"http:\\/\\/puppetlabs.com\\/rpc_provisional_response\",\n                \"http:\\/\\/puppetlabs.com\\/rpc_blocking_response\",\n                \"http:\\/\\/puppetlabs.com\\/rpc_non_blocking_response\",\n                \"http:\\/\\/puppetlabs.com\\/rpc_error_message\"\n              ]\n            }\n          },\n          \"allow\": \"*\",\n          \"name\": \"pcp-broker message\",\n          \"sort-order\": 420\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_puppet_authorization_hocon_rule\",\n      \"title\": \"rule-pcp messages\",\n      \"tags\": [\n        \"pe_puppet_authorization_hocon_rule\",\n        \"pe_puppet_authorization::rule\",\n        \"pe_puppet_authorization\",\n        \"rule\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_puppet_authorization\\/manifests\\/rule.pp\",\n      \"line\": 80,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"absent\",\n        \"path\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf\",\n        \"value\": {\n          \"match-request\": {\n            \"path\": \"\\/pcp-broker\\/send\",\n            \"type\": \"undef\",\n            \"query-params\": {\n              \n            }\n          },\n          \"allow-unauthenticated\": false,\n          \"name\": \"pcp messages\",\n          \"sort-order\": 200\n        }\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services broker-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.pcp.broker.service\\/broker-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"Pe_concat::Fragment\",\n      \"title\": \"orchestration-services authorization-service\",\n      \"tags\": [\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/trapperkeeper\\/bootstrap_cfg.pp\",\n      \"line\": 28,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"content\": \"puppetlabs.trapperkeeper.services.authorization.authorization-service\\/authorization-service\\n\",\n        \"order\": \"10\",\n        \"ensure\": \"present\",\n        \"notify\": \"Exec[pe-orchestration-services service full restart]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 149,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"force\": true,\n        \"ignore\": [\n          \".svn\",\n          \".git\",\n          \".gitignore\"\n        ],\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n        \"purge\": true,\n        \"recurse\": true,\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 159,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat.out\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 164,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 169,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0644\",\n        \"replace\": true,\n        \"alias\": \"pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n        \"source\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat.out\",\n        \"backup\": \"puppet\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg\",\n      \"tags\": [\n        \"exec\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 187,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\\"\",\n        \"alias\": \"pe_concat_\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\",\n        \"notify\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n        \"subscribe\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg]\",\n        \"unless\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\\" -t\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"require\": [\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat]\"\n        ]\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services webrouting-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\\/webrouting-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services webrouting-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services metrics-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services metrics-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services metrics-webservice\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services metrics-webservice\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services scheduler-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\\/scheduler-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services scheduler-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services status-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services status-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/pk8_cert.pp\",\n      \"line\": 22,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"openssl pkcs8 -topk8 -inform PEM -outform DER -in \\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem -out \\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8 -nocrypt\",\n        \"path\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/bin\",\n          \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\"\n        ],\n        \"onlyif\": \"test ! -e '\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8' -o '\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8' -ot '\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem'\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"agent.demo.com\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/pk8_cert.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-orchestration-services\",\n        \"group\": \"pe-orchestration-services\",\n        \"mode\": \"0400\",\n        \"before\": \"Exec[pe-orchestration-services service full restart]\",\n        \"backup\": false,\n        \"require\": \"Package[pe-orchestration-services]\",\n        \"notify\": \"Service[pe-orchestration-services]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"exec\",\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/pk8_cert.pp\",\n      \"line\": 22,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"openssl pkcs8 -topk8 -inform PEM -outform DER -in \\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem -out \\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8 -nocrypt\",\n        \"path\": [\n          \"\\/opt\\/puppetlabs\\/puppet\\/bin\",\n          \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\"\n        ],\n        \"onlyif\": \"test ! -e '\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8' -o '\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8' -ot '\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem'\",\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8\",\n      \"tags\": [\n        \"file\",\n        \"puppet_enterprise::certs::pk8_cert\",\n        \"puppet_enterprise\",\n        \"certs\",\n        \"pk8_cert\",\n        \"puppet_enterprise::certs\",\n        \"pe-puppetdb\",\n        \"class\",\n        \"puppet_enterprise::profile::puppetdb\",\n        \"profile\",\n        \"puppetdb\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/puppet_enterprise\\/manifests\\/certs\\/pk8_cert.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"owner\": \"pe-puppetdb\",\n        \"group\": \"pe-puppetdb\",\n        \"mode\": \"0400\",\n        \"before\": \"Exec[pe-puppetdb service full restart]\",\n        \"backup\": false,\n        \"require\": \"Package[pe-puppetdb]\",\n        \"notify\": \"Service[pe-puppetdb]\"\n      }\n    },\n    {\n      \"type\": \"Pe_staging::File\",\n      \"title\": \"puppet-agent-el-7-x86_64.tar.gz\",\n      \"tags\": [\n        \"pe_staging::file\",\n        \"pe_staging\",\n        \"file\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_staging::deploy\",\n        \"deploy\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_staging\\/manifests\\/deploy.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"source\": \"https:\\/\\/pm.puppetlabs.com\\/puppet-agent\\/2017.2.0-rc1-424-ge1372a3\\/1.10.0\\/repos\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"target\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"subdir\": \"pe_repo\",\n        \"unless\": \"[ -e '\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/repodata' ]\"\n      }\n    },\n    {\n      \"type\": \"Pe_staging::Extract\",\n      \"title\": \"puppet-agent-el-7-x86_64.tar.gz\",\n      \"tags\": [\n        \"pe_staging::extract\",\n        \"pe_staging\",\n        \"extract\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_staging::deploy\",\n        \"deploy\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_staging\\/manifests\\/deploy.pp\",\n      \"line\": 32,\n      \"exported\": false,\n      \"parameters\": {\n        \"target\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\",\n        \"source\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"mode\": \"0755\",\n        \"subdir\": \"pe_repo\",\n        \"strip\": \"5\",\n        \"creates\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/repodata\",\n        \"require\": \"Pe_staging::File[puppet-agent-el-7-x86_64.tar.gz]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"Set perms of \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0 directories to 0755\",\n      \"tags\": [\n        \"exec\",\n        \"pe_repo::set_owner_group_permissions\",\n        \"pe_repo\",\n        \"set_owner_group_permissions\",\n        \"pe_repo::repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/set_owner_group_permissions.pp\",\n      \"line\": 9,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"find \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/ -type d ! -perm 0755 -exec chmod -c 0755 {} \\\\;\",\n        \"onlyif\": \"find \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/ -type d ! -perm 0755 | grep '.*'\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"logoutput\": true,\n        \"loglevel\": \"info\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"Set perms of \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0 contents to 0644\",\n      \"tags\": [\n        \"exec\",\n        \"pe_repo::set_owner_group_permissions\",\n        \"pe_repo\",\n        \"set_owner_group_permissions\",\n        \"pe_repo::repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/set_owner_group_permissions.pp\",\n      \"line\": 17,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"find \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/ -type f ! -perm 0644 -exec chmod -c 0644 {} \\\\;\",\n        \"onlyif\": \"find \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/ -type f ! -perm 0644 | grep '.*'\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"logoutput\": true,\n        \"loglevel\": \"info\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"Set user\\/group of \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0 contents to root:root\",\n      \"tags\": [\n        \"exec\",\n        \"pe_repo::set_owner_group_permissions\",\n        \"pe_repo\",\n        \"set_owner_group_permissions\",\n        \"pe_repo::repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_repo\\/manifests\\/set_owner_group_permissions.pp\",\n      \"line\": 25,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"find \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/ \\\\( ! -user root -or ! -group root \\\\) -exec chown root:root -c {} \\\\;\",\n        \"onlyif\": \"find \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/ \\\\( ! -user root -or ! -group root \\\\) | grep '.*'\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"logoutput\": true,\n        \"loglevel\": \"info\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 144,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 149,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"mode\": \"0750\",\n        \"force\": true,\n        \"ignore\": [\n          \".svn\",\n          \".git\",\n          \".gitignore\"\n        ],\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"purge\": true,\n        \"recurse\": true,\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 159,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat.out\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 164,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0640\",\n        \"backup\": false,\n        \"owner\": \"pe-console-services\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 169,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"present\",\n        \"mode\": \"0644\",\n        \"replace\": true,\n        \"alias\": \"pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n        \"source\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat.out\",\n        \"backup\": \"puppet\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg\",\n      \"tags\": [\n        \"exec\",\n        \"pe_concat\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/init.pp\",\n      \"line\": 187,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\\"\",\n        \"alias\": \"pe_concat_\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\",\n        \"notify\": \"File[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"subscribe\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg]\",\n        \"unless\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh -o \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat.out\\\" -d \\\"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\\" -t\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"require\": [\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments]\",\n          \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat]\"\n        ]\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services activity-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.activity.services\\/activity-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services activity-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services jetty9-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::activity\",\n        \"activity\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\\/jetty9-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services jetty9-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.rbac\\/rbac-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-storage-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.storage.permissioned\\/rbac-storage-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-storage-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-http-api-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.http.api\\/rbac-http-api-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-http-api-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-authn-middleware\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.http.middleware\\/rbac-authn-middleware\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-authn-middleware\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services activity-reporting-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::rbac\",\n        \"rbac\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.activity.services\\/activity-reporting-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services activity-reporting-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services classifier-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::classifier\",\n        \"classifier\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.classifier.main\\/classifier-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services classifier-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n        \"group\": \"pe-console-services\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services webrouting-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webrouting.webrouting-service\\/webrouting-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services webrouting-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-consumer-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.consumer\\/rbac-consumer-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-consumer-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-status-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.status\\/rbac-status-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-status-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-authn-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.authn\\/rbac-authn-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-authn-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-authz-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac.services.authz\\/rbac-authz-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services rbac-authz-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services pe-console-ui-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.pe-console-ui.service\\/pe-console-ui-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services pe-console-ui-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services pe-console-auth-ui-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.pe-console-auth-ui.service\\/pe-console-auth-ui-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services pe-console-auth-ui-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services status-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services status-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services scheduler-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.scheduler.scheduler-service\\/scheduler-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services scheduler-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services metrics-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services metrics-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services metrics-webservice\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::console_services\",\n        \"console_services\",\n        \"console-services\",\n        \"class\",\n        \"puppet_enterprise::console_services\",\n        \"puppet_enterprise::profile::console\",\n        \"profile\",\n        \"console\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_console-services metrics-webservice\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services orchestrator-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.orchestrator.service\\/orchestrator-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services orchestrator-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services orchestrator-dispatch-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.orchestrator.dispatch\\/orchestrator-dispatch-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services orchestrator-dispatch-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services jetty9-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.webserver.jetty9-service\\/jetty9-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services jetty9-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services remote-rbac-consumer-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac-client.services.rbac\\/remote-rbac-consumer-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services remote-rbac-consumer-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services remote-activity-reporter\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::orchestrator\",\n        \"orchestrator\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.rbac-client.services.activity\\/remote-activity-reporter\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services remote-activity-reporter\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services broker-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.pcp.broker.service\\/broker-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services broker-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services authorization-service\",\n      \"tags\": [\n        \"file\",\n        \"pe_concat::fragment\",\n        \"pe_concat\",\n        \"fragment\",\n        \"puppet_enterprise::trapperkeeper::bootstrap_cfg\",\n        \"puppet_enterprise\",\n        \"trapperkeeper\",\n        \"bootstrap_cfg\",\n        \"puppet_enterprise::trapperkeeper::pcp_broker\",\n        \"pcp_broker\",\n        \"orchestration-services\",\n        \"class\",\n        \"puppet_enterprise::profile::orchestrator\",\n        \"profile\",\n        \"orchestrator\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_concat\\/manifests\\/fragment.pp\",\n      \"line\": 113,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"file\",\n        \"owner\": \"root\",\n        \"mode\": \"0640\",\n        \"content\": \"puppetlabs.trapperkeeper.services.authorization.authorization-service\\/authorization-service\\n\",\n        \"backup\": false,\n        \"replace\": true,\n        \"alias\": \"pe_concat_fragment_orchestration-services authorization-service\",\n        \"notify\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Pe_staging\",\n      \"tags\": [\n        \"class\",\n        \"pe_staging\",\n        \"pe_staging::file\",\n        \"file\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_staging::deploy\",\n        \"deploy\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"path\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\",\n        \"owner\": \"0\",\n        \"group\": \"0\",\n        \"mode\": \"0755\"\n      }\n    },\n    {\n      \"type\": \"File\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\",\n      \"tags\": [\n        \"file\",\n        \"class\",\n        \"pe_staging\",\n        \"pe_staging::file\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_staging::deploy\",\n        \"deploy\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_staging\\/manifests\\/init.pp\",\n      \"line\": 20,\n      \"exported\": false,\n      \"parameters\": {\n        \"ensure\": \"directory\",\n        \"owner\": \"0\",\n        \"group\": \"0\",\n        \"mode\": \"0755\",\n        \"backup\": false\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz\",\n      \"tags\": [\n        \"exec\",\n        \"pe_staging::file\",\n        \"pe_staging\",\n        \"file\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_staging::deploy\",\n        \"deploy\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_staging\\/manifests\\/file.pp\",\n      \"line\": 101,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"curl  -f -L -o puppet-agent-el-7-x86_64.tar.gz https:\\/\\/pm.puppetlabs.com\\/puppet-agent\\/2017.2.0-rc1-424-ge1372a3\\/1.10.0\\/repos\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"path\": \"\\/opt\\/puppetlabs\\/puppet\\/bin:\\/usr\\/local\\/bin:\\/usr\\/bin:\\/bin\",\n        \"cwd\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\",\n        \"creates\": \"\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz\",\n        \"logoutput\": \"on_failure\",\n        \"unless\": \"[ -e '\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/repodata' ]\"\n      }\n    },\n    {\n      \"type\": \"Exec\",\n      \"title\": \"extract puppet-agent-el-7-x86_64.tar.gz\",\n      \"tags\": [\n        \"exec\",\n        \"pe_staging::extract\",\n        \"pe_staging\",\n        \"extract\",\n        \"puppet-agent-el-7-x86_64.tar.gz\",\n        \"pe_staging::deploy\",\n        \"deploy\",\n        \"pe_repo::repo\",\n        \"pe_repo\",\n        \"repo\",\n        \"pe_repo::el\",\n        \"el\",\n        \"el-7-x86_64\",\n        \"class\",\n        \"pe_repo::platform::el_7_x86_64\",\n        \"platform\",\n        \"el_7_x86_64\",\n        \"node\",\n        \"default\"\n      ],\n      \"file\": \"\\/opt\\/puppetlabs\\/puppet\\/modules\\/pe_staging\\/manifests\\/extract.pp\",\n      \"line\": 104,\n      \"exported\": false,\n      \"parameters\": {\n        \"command\": \"tar xzf \\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz --strip=5\",\n        \"path\": \"\\/usr\\/local\\/sbin:\\/usr\\/local\\/bin:\\/usr\\/sbin:\\/usr\\/bin:\\/sbin\",\n        \"cwd\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\",\n        \"creates\": \"\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0\\/repodata\",\n        \"logoutput\": \"on_failure\"\n      }\n    }\n  ],\n  \"edges\": [\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Settings]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Main]\"\n    },\n    {\n      \"source\": \"Class[Main]\",\n      \"target\": \"Node[default]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Params]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_repo]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_repo::Platform::El_7_x86_64]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::License]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Agent]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Master]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Certificate_authority]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Console]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Master::Mcollective]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Mcollective::Agent]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Mcollective::Peadmin]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Orchestrator]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise]\",\n      \"target\": \"Pe_anchor[puppet_enterprise:barrier:ca]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo]\",\n      \"target\": \"Exec[create repo_dir]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/current]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/GPG-KEY-puppetlabs]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/GPG-KEY-puppet]\"\n    },\n    {\n      \"source\": \"Class[Pe_repo::Platform::El_7_x86_64]\",\n      \"target\": \"Pe_repo::El[el-7-x86_64]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Symlinks]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin\\/facter]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin\\/puppet]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin\\/pe-man]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin\\/hiera]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin\\/mco]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Symlinks]\",\n      \"target\": \"File[\\/usr\\/local\\/bin\\/r10k]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Pxp_agent]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Pxp_agent]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/pxp-agent\\/pxp-agent.conf]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Pxp_agent::Service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Pxp_agent::Service]\",\n      \"target\": \"Service[pxp-agent]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Agent]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/state\\/package_inventory_enabled]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Amq]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Packages]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Repo]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Stages]\"\n    },\n    {\n      \"source\": \"Stage[pe_setup]\",\n      \"target\": \"Class[Puppet_enterprise::Repo::Config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-bundler]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-java]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-console-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-client-tools]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-license]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-puppet-license-cli]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-puppetdb-termini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-console-services-termini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-puppetserver]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-modules]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-orchestration-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-activemq]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Packages]\",\n      \"target\": \"Package[pe-puppet-enterprise-release]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Amq::Config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/activemq.xml]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/credentials.properties]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/jetty.xml]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/log4j.properties]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/jetty-realm.properties]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Config]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Beans[agent.demo.com - beans]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Amq::Service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Service]\",\n      \"target\": \"Service[pe-activemq]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"File[\\/etc\\/sysconfig\\/pe-activemq]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Broker[remove default localhost]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Amq::Certs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Certs]\",\n      \"target\": \"Pe_java_ks[puppetca:truststore]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Certs]\",\n      \"target\": \"Pe_java_ks[agent.demo.com:keystore]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/broker.ts]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Amq::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/activemq\\/broker.ks]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Ssl_context[agent.demo.com-ssl-context]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Management_context[agent.demo.com - managementContext]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry[agent.demo.com-topic->]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry[agent.demo.com-queue->]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry[agent.demo.com-queue-*.reply.>]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Transport_connector[agent.demo.com-openwire-transport]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Transport_connector[agent.demo.com-stomp-transport]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Statistics_broker_plugin[agent.demo.com-statisticsBrokerPlugin]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Timestamping_broker_plugin[agent.demo.com-timeStampingBrokerPlugin]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Simple_authentication_user[agent.demo.com-simple_auth_user-mcollective]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-queue->]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-topic->]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-queue-mcollective.>]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-topic-mcollective.>]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-topic-ActiveMQ.Advisory.>]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::Web_console[agent.demo.com - web console - false]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Amq::Broker]\",\n      \"target\": \"Puppet_enterprise::Amq::Config::System_usage[agent.demo.com - systemusage]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[certificate-authority-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[metrics.reporters.graphite.host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[metrics.reporters.graphite.port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[metrics.reporters.graphite.update-interval-seconds]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/ca.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[certificate-authority.proxy-config.proxy-target-url]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[certificate-authority.proxy-config.ssl-opts.ssl-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[certificate-authority.proxy-config.ssl-opts.ssl-key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_hocon_setting[certificate-authority.proxy-config.ssl-opts.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Master::Classifier]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Classifier]\",\n      \"target\": \"Pe_ini_setting[node_terminus]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Classifier]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppet\\/classifier.yaml]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppet\\/ssl\\/crl.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppet\\/ssl\\/private_keys\\/agent.demo.com.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_ini_setting[module_groups]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_ini_setting[codedir_setting]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Pe_ini_setting[environmentpath_setting]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Master::Auth_conf]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Master::Tk_authz]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs catalog]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs certificate]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs crl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs csr]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs environments]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs environment]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs environment classes]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs file]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs node]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs report]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs resource type]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs status]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs static file content]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs experimental]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Tk_authz]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs deny all]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppet\\/puppetdb.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb.conf_server_urls]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb.conf_command_broadcast]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb.conf_include_unchanged_resources]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb.conf_soft_write_failure]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb.conf_sticky_read_failover]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[storeconfigs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_setting[storeconfigs_backend]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[reports_puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppet\\/routes.yaml]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Master]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Master::Puppetserver]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[puppetserver:master jetty9-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[pe-master-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[request-handler-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[jruby-puppet-pooled-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[jruby-pool-manager-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[metrics-puppet-profiler-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[metrics-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[puppet-server-config-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[puppet-admin-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[webrouting-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[pe-legacy-routes-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[status-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[authorization-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[scheduler-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[pe-jruby-metrics-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[analytics-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/webserver.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.client-auth]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.ssl-host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.ssl-port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.ssl-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.ssl-key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.ssl-crl-path]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.access-log-config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.max-threads]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.static-content]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[webserver.puppet-server.static-content]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/web-routes.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/pe-master-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/pe-legacy-routes-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/legacy-routes-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/certificate-authority-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/reverse-proxy-ca-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/puppet-admin-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/status-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[web-router-service\\/remove-master-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/pe-puppet-server.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.ruby-load-path]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[os-settings.remove]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.gem-home]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.master-conf-dir]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.master-code-dir]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.master-var-dir]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.master-run-dir]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.master-log-dir]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.borrow-timeout]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.max-active-instances]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.max-requests-per-instance]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.use-legacy-auth-conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics-service\\/metrics-webservice]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[jruby-puppet.environment-class-cache-enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[profiler.enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[pe-puppetserver.puppet-code-repo]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[pe-puppetserver.pre-commit-hook-commands]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[puppet-admin]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs environment cache]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs jruby pool]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[http-client.ssl-protocols]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[http-client.cipher-suites]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[http-client.idle-timeout-milliseconds]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[http-client.connect-timeout-milliseconds]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf#global.logging-config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/global.conf#global.hostname]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[global.certs.ssl-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[global.certs.ssl-key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[global.certs.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/metrics.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.server-id]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.registries.puppetserver.reporters.jmx.enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.registries.default.reporters.jmx.enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.registries.puppetserver.reporters.graphite.enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.registries.puppetserver.metrics-allowed]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.reporters.graphite.enabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[metrics.reporters.jmx]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/rbac-consumer.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[rbac-consumer.api-url]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/activity-consumer.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Pe_hocon_setting[activity-consumer.api-url]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Augeas[fileserver.conf remove [files]]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::Puppetserver]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Pe_service[puppetserver]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"File[\\/var\\/log\\/puppetlabs\\/puppet]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/pe_build]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"Pe_ini_setting[puppetserver puppetconf certname]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"Pe_ini_setting[puppetserver puppetconf always_cache_features]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"Pe_ini_setting[puppetserver puppetconf user]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"Pe_ini_setting[puppetserver puppetconf group]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"Pe_ini_setting[puppetserver static catalogs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master]\",\n      \"target\": \"Pe_ini_setting[puppetconf environment_timeout setting]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/share\\/puppet_enterprise]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Exec[Ensure public dir \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"Puppet_enterprise::Fileserver_conf[pe_packages]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_r10k::Package]\"\n    },\n    {\n      \"source\": \"Class[Pe_r10k::Package]\",\n      \"target\": \"Package[pe-r10k]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Master::File_sync_disabled]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::File_sync_disabled]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[versioned-code-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::File_sync_disabled]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/file-sync.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::File_sync_disabled]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/versioned-code.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::File_sync_disabled]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs file sync api]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Master::File_sync_disabled]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs file sync repo]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/opt-out]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/analytics-opt-out]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Controller]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/ssl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/ssl\\/certs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/ssl\\/certs\\/ca.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/orchestrator.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/puppet-code.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/puppet-access.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Controller]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/puppetdb.conf]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Cli_config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Cli_config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/client-tools\\/services.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Cli_config]\",\n      \"target\": \"Pe_hocon_setting[\\/etc\\/puppetlabs\\/client-tools\\/services.conf\\/services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Cli_config]\",\n      \"target\": \"Pe_hocon_setting[\\/etc\\/puppetlabs\\/client-tools\\/services.conf\\/nodes]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Cli_config]\",\n      \"target\": \"Pe_hocon_setting[\\/etc\\/puppetlabs\\/client-tools\\/services.conf\\/certs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Certificate_authority]\",\n      \"target\": \"Pe_hocon_setting[certificate-authority.certificate-status]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Certificate_authority]\",\n      \"target\": \"Pe_puppet_authorization::Rule[puppetlabs certificate status]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Certificate_authority]\",\n      \"target\": \"Pe_hocon_setting[certificate-authority.proxy-config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Certificate_authority]\",\n      \"target\": \"Puppet_enterprise::Fileserver_conf[pe_modules]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Console::Certs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Certs]\",\n      \"target\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/webserver.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.ssl-host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.ssl-port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.ssl-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.ssl-key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.status-proxy]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.activity.services\\/activity-service\\\"]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.rbac.services.http.api\\/rbac-http-api-service\\\"]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.pe-console-ui.service\\/pe-console-ui-service\\\".pe-console-app]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.pe-console-auth-ui.service\\/pe-console-auth-ui-service\\\".authn-app]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.classifier.main\\/classifier-service\\\"]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-service\\/status-service\\\"]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.trapperkeeper.services.status.status-proxy-service\\/status-proxy-service\\\"]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.\\\"puppetlabs.trapperkeeper.services.metrics.metrics-service\\/metrics-webservice\\\"]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[status-proxy]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.remove-rbac-ui-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.remove-helpers-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[web-router-service.remove-classifier-ui-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/global.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[global.logging-config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[global.version-path]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[global.login-path]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Console_services_config]\",\n      \"target\": \"Pe_hocon_setting[global.replication-mode]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console]\",\n      \"target\": \"Puppet_enterprise::Certs::Whitelist_entry[rbac cert whitelist entry: agent.demo.com]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Console_services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Console_services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Console_services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Console_services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Console_services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Pe_service[console-services]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_nginx]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_nginx::Params]\"\n    },\n    {\n      \"source\": \"Class[Pe_nginx]\",\n      \"target\": \"Package[pe-nginx]\"\n    },\n    {\n      \"source\": \"Class[Pe_nginx]\",\n      \"target\": \"Service[pe-nginx]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/proxy.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/nginx\\/dhparam_puppetproxy.pem]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\",\n      \"target\": \"Pe_nginx::Directive[gzip]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\",\n      \"target\": \"Pe_nginx::Directive[gzip_comp_level]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\",\n      \"target\": \"Pe_nginx::Directive[gzip_min_length]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\",\n      \"target\": \"Pe_nginx::Directive[gzip_proxied]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\",\n      \"target\": \"Pe_nginx::Directive[gzip_vary]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Nginx_conf]\",\n      \"target\": \"Pe_nginx::Directive[gzip_types]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[server_name]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[listen]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_certificate]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_certificate_key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_crl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_prefer_server_ciphers]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_ciphers]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_protocols]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_dhparam]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_verify_client]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_verify_depth]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_session_timeout]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[ssl_session_cache]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_pass]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_redirect]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_read_timeout]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_set_header x-ssl-subject]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_set_header x-client-dn]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_set_header x-client-verify]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy]\",\n      \"target\": \"Pe_nginx::Directive[proxy_set_header x-forwarded-for]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Http_redirect]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Http_redirect]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/nginx\\/conf.d\\/http_redirect.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Http_redirect]\",\n      \"target\": \"Pe_nginx::Directive[http_redirect-server_name]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Http_redirect]\",\n      \"target\": \"Pe_nginx::Directive[http_redirect-listen]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Console::Proxy::Http_redirect]\",\n      \"target\": \"Pe_nginx::Directive[http_redirect-return]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Service]\",\n      \"target\": \"Service[mcollective]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Mcollective]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/credentials]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Mcollective]\",\n      \"target\": \"Puppet_enterprise::Master::Keypair[pe-internal-mcollective-servers]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Mcollective]\",\n      \"target\": \"Puppet_enterprise::Master::Keypair[pe-internal-puppet-console-mcollective-client]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Master::Mcollective]\",\n      \"target\": \"Puppet_enterprise::Master::Keypair[pe-internal-peadmin-mcollective-client]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Mcollective::Agent]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\/mcollective-public.pem]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Server]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Server::Plugins]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Plugins]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/mcollective]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Plugins]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/mcollective\\/plugins]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Plugins]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/mcollective\\/plugins\\/mcollective]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Server::Logs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Logs]\",\n      \"target\": \"File[\\/var\\/log\\/puppetlabs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Logs]\",\n      \"target\": \"File[\\/var\\/log\\/puppetlabs\\/mcollective.log]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Logs]\",\n      \"target\": \"File[\\/var\\/log\\/puppetlabs\\/mcollective-audit.log]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/ca.cert.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/agent.demo.com.cert.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/agent.demo.com.private_key.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/mcollective-private.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/mcollective-public.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\/puppet-dashboard-public.pem]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Certs]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/ssl\\/clients\\/peadmin-public.pem]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Server::Facter]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Facter]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/bin\\/refresh-mcollective-metadata]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Facter]\",\n      \"target\": \"Exec[bootstrap mcollective metadata]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Facter]\",\n      \"target\": \"Cron[pe-mcollective-metadata]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server::Facter]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/facts-bootstrapped]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Server]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/server.cfg]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/discovery.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/package.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/package.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppet.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppet.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppetral.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/puppetral.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/rpcutil.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/rpcutil.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/service.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/agent\\/service.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/average.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/average.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/boolean_summary.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/boolean_summary.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/sum.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/sum.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/summary.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/aggregate\\/summary.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/completion.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/facts.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/find.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/help.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/inventory.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/package.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/ping.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/plugin.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/puppet.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/rpc.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/application\\/service.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/audit\\/logfile.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/activemq.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/activemq.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/rabbitmq.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/connector\\/rabbitmq.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/agent_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/agent_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/collective_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/collective_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fact_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fact_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fstat_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/fstat_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/puppet_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/puppet_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/resource_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/resource_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/service_data.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/data\\/service_data.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/flatfile.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/flatfile.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/mc.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/mc.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/stdin.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/discovery\\/stdin.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/facts\\/yaml_facts.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/registration\\/meta.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/security\\/sshkey.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/package]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppet_agent_mgr]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/actionpolicy.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppet_agent_mgr.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppet_server_address_validation.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/util\\/puppetrunner.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_resource_validator.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_resource_validator.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_server_address_validator.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_server_address_validator.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_tags_validator.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_tags_validator.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_variable_validator.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/puppet_variable_validator.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/service_name.ddl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Mcollective::Cleanup]\",\n      \"target\": \"File[\\/opt\\/puppet\\/libexec\\/mcollective\\/mcollective\\/validator\\/service_name.rb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Mcollective::Peadmin]\",\n      \"target\": \"File[\\/etc\\/mcollective]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Mcollective::Peadmin]\",\n      \"target\": \"Puppet_enterprise::Mcollective::Client[peadmin]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services webrouting-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker metrics-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker metrics-webservice]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker scheduler-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker status-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/authorization.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.authorization.version]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/webserver.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/global.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.global.logging-config]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.global.certs.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.global.certs.ssl-cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.global.certs.ssl-key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.web-router-service.broker-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.web-router-service.orchestrator-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.web-router-service.orchestrator-dispatch-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.web-router-service.status-service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.web-router-service.metrics-webservice]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Certs[agent.demo.com]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Orchestrator]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Pe_service[orchestration-services]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Puppetdb]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Puppetdb::Database_ini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n      \"target\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[database]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_gc_interval]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_node_ttl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_node_purge_ttl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Database_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_report_ttl]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb]\",\n      \"target\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/jetty.ini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_host]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_port]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_sslhost]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_sslport]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_ssl_key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_ssl_cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_ssl_ca_cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_client_auth]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_ssl_protocols]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb-certificate-whitelist]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Jetty_ini]\",\n      \"target\": \"Pe_ini_setting[puppetdb_request_header_max_size]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/rbac_consumer.conf]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n      \"target\": \"Pe_hocon_setting[puppetdb_rbac_consumer_ssl_key]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n      \"target\": \"Pe_hocon_setting[puppetdb_rbac_consumer_ssl_cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n      \"target\": \"Pe_hocon_setting[puppetdb_rbac_consumer_ssl_ca_cert]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Rbac_consumer_conf]\",\n      \"target\": \"Pe_hocon_setting[puppetdb_rbac_consumer_api_url]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Puppetdb::Config_ini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Config_ini]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/config.ini]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Config_ini]\",\n      \"target\": \"Pe_ini_setting[config.ini threads command-processing section]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Config_ini]\",\n      \"target\": \"Pe_ini_setting[config.ini concurrent-writes command-processing section]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Puppetdb::Service]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb::Service]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Pe_service[puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb]\",\n      \"target\": \"File[\\/var\\/log\\/puppetlabs\\/puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb]\",\n      \"target\": \"File[\\/var\\/log\\/puppetlabs\\/puppetdb\\/puppetdb.log]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Puppetdb]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Puppetdb]\",\n      \"target\": \"Puppet_enterprise::Certs[pe-puppetdb]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Profile::Puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Puppet_enterprise::Certs::Puppetdb_whitelist]\"\n    },\n    {\n      \"source\": \"Class[Puppet_enterprise::Certs::Puppetdb_whitelist]\",\n      \"target\": \"Puppet_enterprise::Certs::Whitelist_entry[puppet_enterprise::certs::puppetdb_whitelist entry: agent.demo.com]\"\n    },\n    {\n      \"source\": \"Pe_repo::El[el-7-x86_64]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64.repo]\"\n    },\n    {\n      \"source\": \"Pe_repo::El[el-7-x86_64]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64.bash]\"\n    },\n    {\n      \"source\": \"Pe_repo::El[el-7-x86_64]\",\n      \"target\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Beans[agent.demo.com - beans]\",\n      \"target\": \"Augeas[amq_augeas_base_beans_config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Broker[remove default localhost]\",\n      \"target\": \"Augeas[localhost: AMQ broker: remove default localhost]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Broker[agent.demo.com]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ broker: agent.demo.com]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Ssl_context[agent.demo.com-ssl-context]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ sslContext: agent.demo.com-ssl-context]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Management_context[agent.demo.com - managementContext]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ managementContext: agent.demo.com - managementContext]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry[agent.demo.com-topic->]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ destinationPolicyEntry: agent.demo.com-topic->]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry[agent.demo.com-queue->]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ destinationPolicyEntry: agent.demo.com-queue->]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Destination_policy_entry[agent.demo.com-queue-*.reply.>]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ destinationPolicyEntry: agent.demo.com-queue-*.reply.>]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Transport_connector[agent.demo.com-openwire-transport]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ transportConnector: agent.demo.com-openwire-transport]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Transport_connector[agent.demo.com-stomp-transport]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ transportConnector: agent.demo.com-stomp-transport]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Statistics_broker_plugin[agent.demo.com-statisticsBrokerPlugin]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ statisticsBrokerPlugin: agent.demo.com-statisticsBrokerPlugin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Timestamping_broker_plugin[agent.demo.com-timeStampingBrokerPlugin]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ timeStampingBrokerPlugin: agent.demo.com-timeStampingBrokerPlugin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Simple_authentication_user[agent.demo.com-simple_auth_user-mcollective]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ simpleAuthentication user: agent.demo.com-simple_auth_user-mcollective]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-queue->]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-queue->]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-topic->]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-topic->]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-queue-mcollective.>]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-queue-mcollective.>]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-topic-mcollective.>]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-topic-mcollective.>]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Authorization_plugin_entry[agent.demo.com-authorization-topic-ActiveMQ.Advisory.>]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ authorizationPlugin entry: agent.demo.com-authorization-topic-ActiveMQ.Advisory.>]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::Web_console[agent.demo.com - web console - false]\",\n      \"target\": \"Augeas[AMQ webConsole: agent.demo.com - web console - false]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Amq::Config::System_usage[agent.demo.com - systemusage]\",\n      \"target\": \"Augeas[agent.demo.com: AMQ systemUsage: agent.demo.com - systemusage]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[certificate-authority-service]\",\n      \"target\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[certificate-authority-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver certificate-authority-service]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"Pe_concat::Fragment[00_header_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"Pe_concat::Fragment[99_footer_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"Pe_hocon_setting[authorization.version.\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"Pe_hocon_setting[authorization.allow-header-cert-info.\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs catalog]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs catalog]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs certificate]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs certificate]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs crl]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs crl]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs csr]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs csr]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs environments]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs environments]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs environment]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs environment]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs environment classes]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs environment classes]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs file]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs file]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs node]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs node]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs report]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs report]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs resource type]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs resource type]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs status]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs status]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs static file content]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs static file content]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs experimental]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs experimental]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs deny all]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs deny all]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[puppetserver:master jetty9-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[pe-master-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver pe-master-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[request-handler-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver request-handler-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[jruby-puppet-pooled-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver jruby-puppet-pooled-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[jruby-pool-manager-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver jruby-pool-manager-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[metrics-puppet-profiler-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver metrics-puppet-profiler-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[metrics-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver metrics-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[puppet-server-config-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver puppet-server-config-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[puppet-admin-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver puppet-admin-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[webrouting-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver webrouting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[pe-legacy-routes-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver pe-legacy-routes-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[status-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[authorization-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver authorization-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[scheduler-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver scheduler-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[pe-jruby-metrics-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver pe-jruby-metrics-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[analytics-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver analytics-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.client-auth]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-host]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-port]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-crl-path]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.access-log-config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.max-threads]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.request-header-max-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.default-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[puppet-server]\",\n      \"target\": \"Pe_hocon_setting[puppetserver.webserver.puppet-server.ssl-protocols]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs environment cache]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs environment cache]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs jruby pool]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs jruby pool]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'Xmx']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'Xms']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'XX:+PrintGCDetails']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'XX:+PrintGCDateStamps']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'Xloggc:']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'XX:+UseGCLogFileRotation']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'XX:NumberOfGCLogFiles=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetserver]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetserver_'XX:GCLogFileSize=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf java_bin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf group]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf install_dir]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf bootstrap_config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf service_stop_retries]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetserver]\",\n      \"target\": \"Pe_ini_setting[puppetserver initconf start_timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[puppetserver]\",\n      \"target\": \"Service[pe-puppetserver]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[puppetserver]\",\n      \"target\": \"Exec[pe-puppetserver service full restart]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Fileserver_conf[pe_packages]\",\n      \"target\": \"Augeas[fileserver.conf pe_packages]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[versioned-code-service]\",\n      \"target\": \"Pe_concat::Fragment[puppetserver versioned-code-service]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs file sync api]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs file sync api]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs file sync repo]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs file sync repo]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[puppetlabs certificate status]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-puppetlabs certificate status]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Fileserver_conf[pe_modules]\",\n      \"target\": \"Augeas[fileserver.conf pe_modules]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.public_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-console-services::server_cert]\",\n      \"target\": \"Puppet_enterprise::Certs::Pk8_cert[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.client-auth]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-host]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-port]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-crl-path]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.access-log-config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.max-threads]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.request-header-max-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.default-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[console]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.console.ssl-protocols]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.client-auth]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-host]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-port]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-crl-path]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.access-log-config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.max-threads]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.request-header-max-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.default-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[api]\",\n      \"target\": \"Pe_hocon_setting[console-services.webserver.api.ssl-protocols]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.rbac-base-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.cors-origin-pattern]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/activity-database.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.subprotocol]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.subname]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.migration-user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.maximum-pool-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.connection-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Pe_hocon_setting[activity.database.connection-check-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:activity activity-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Activity[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:activity jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.certificate-whitelist]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.token-private-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.token-public-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.token-maximum-lifetime]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.password-reset-expiration]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.session-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.token-auth-lifetime]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.ds-trust-chain]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.failed-attempts-lockout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/rbac-database.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.subprotocol]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.subname]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.migration-user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.maximum-pool-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.connection-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.rbac.database.connection-check-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-storage-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-http-api-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-authn-middleware]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac activity-reporting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Rbac[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Whitelist_entry[rbac cert whitelist entry: agent.demo.com]\",\n      \"target\": \"Pe_file_line[\\/etc\\/puppetlabs\\/console-services\\/rbac-certificate-whitelist:agent.demo.com]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.puppet-master]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.synchronization-period]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.prune-days-threshold]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.node-check-in-storage]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/classifier-database.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[classifier.database.subprotocol]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.database.subname]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.database.user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.classifier.database.migration-user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:classifier classifier-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:classifier activity-reporting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Classifier[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:classifier jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/conf.d\\/console.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.assets-dir]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.puppet-master]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.rbac-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.classifier-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.activity-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.orchestrator-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.display-local-time]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.session-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.session-maximum-lifetime]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.puppetdb-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.certs.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.certs.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.certs.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.proxy-idle-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.license-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.pcp-request-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.service-alert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.service-alert-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Pe_hocon_setting[console-services.console.no-longer-reporting-cutoff]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console webrouting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-authn-middleware]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-consumer-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-storage-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-authn-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-authz-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console pe-console-ui-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console pe-console-auth-ui-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console scheduler-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console metrics-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Console_services[console-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console metrics-webservice]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'Xmx']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'Xms']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'XX:+PrintGCDetails']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'XX:+PrintGCDateStamps']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'Xloggc:']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'XX:+UseGCLogFileRotation']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'XX:NumberOfGCLogFiles=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[console-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-console-services_'XX:GCLogFileSize=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf java_bin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf group]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf install_dir]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf bootstrap_config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf service_stop_retries]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[console-services]\",\n      \"target\": \"Pe_ini_setting[console-services initconf start_timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[console-services]\",\n      \"target\": \"Service[pe-console-services]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[console-services]\",\n      \"target\": \"Exec[pe-console-services service full restart]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[gzip]\",\n      \"target\": \"Augeas[pe_nginx::directive for gzip]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[gzip_comp_level]\",\n      \"target\": \"Augeas[pe_nginx::directive for gzip_comp_level]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[gzip_min_length]\",\n      \"target\": \"Augeas[pe_nginx::directive for gzip_min_length]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[gzip_proxied]\",\n      \"target\": \"Augeas[pe_nginx::directive for gzip_proxied]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[gzip_vary]\",\n      \"target\": \"Augeas[pe_nginx::directive for gzip_vary]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[gzip_types]\",\n      \"target\": \"Augeas[pe_nginx::directive for gzip_types]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[server_name]\",\n      \"target\": \"Augeas[pe_nginx::directive for server_name]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[listen]\",\n      \"target\": \"Augeas[pe_nginx::directive for listen]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_certificate]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_certificate]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_certificate_key]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_certificate_key]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_crl]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_crl]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_prefer_server_ciphers]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_prefer_server_ciphers]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_ciphers]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_ciphers]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_protocols]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_protocols]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_dhparam]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_dhparam]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_verify_client]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_verify_client]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_verify_depth]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_verify_depth]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_session_timeout]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_session_timeout]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[ssl_session_cache]\",\n      \"target\": \"Augeas[pe_nginx::directive for ssl_session_cache]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_pass]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_pass]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_redirect]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_redirect]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_read_timeout]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_read_timeout]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_set_header x-ssl-subject]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_set_header x-ssl-subject]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_set_header x-client-dn]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_set_header x-client-dn]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_set_header x-client-verify]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_set_header x-client-verify]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[proxy_set_header x-forwarded-for]\",\n      \"target\": \"Augeas[pe_nginx::directive for proxy_set_header x-forwarded-for]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[http_redirect-server_name]\",\n      \"target\": \"Augeas[pe_nginx::directive for http_redirect-server_name]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[http_redirect-listen]\",\n      \"target\": \"Augeas[pe_nginx::directive for http_redirect-listen]\"\n    },\n    {\n      \"source\": \"Pe_nginx::Directive[http_redirect-return]\",\n      \"target\": \"Augeas[pe_nginx::directive for http_redirect-return]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-mcollective-servers]\",\n      \"target\": \"File[pe-internal-mcollective-servers.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-mcollective-servers]\",\n      \"target\": \"File[pe-internal-mcollective-servers.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-mcollective-servers]\",\n      \"target\": \"File[pe-internal-mcollective-servers.public_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-puppet-console-mcollective-client]\",\n      \"target\": \"File[pe-internal-puppet-console-mcollective-client.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-puppet-console-mcollective-client]\",\n      \"target\": \"File[pe-internal-puppet-console-mcollective-client.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-puppet-console-mcollective-client]\",\n      \"target\": \"File[pe-internal-puppet-console-mcollective-client.public_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-peadmin-mcollective-client]\",\n      \"target\": \"File[pe-internal-peadmin-mcollective-client.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-peadmin-mcollective-client]\",\n      \"target\": \"File[pe-internal-peadmin-mcollective-client.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Master::Keypair[pe-internal-peadmin-mcollective-client]\",\n      \"target\": \"File[pe-internal-peadmin-mcollective-client.public_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client[peadmin]\",\n      \"target\": \"Puppet_enterprise::Mcollective::Client::User[peadmin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client[peadmin]\",\n      \"target\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/client.log]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client[peadmin]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/mcollective\\/client.cfg]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/orchestrator.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.master-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.puppetdb-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.classifier-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.console-services-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.rbac-consumer.api-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.activity-consumer.api-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.pcp-broker-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.console-url]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.pcp-timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.global-concurrent-compiles]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.job-prune-threshold]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.database.subname]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.database.user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.orchestrator.database.migration-user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Pe_puppet_authorization::Rule[dispatch: allow pcp-brokers]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator orchestrator-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator orchestrator-dispatch-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator remote-rbac-consumer-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Orchestrator[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator remote-activity-reporter]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/conf.d\\/pcp-broker.conf]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.pcp-broker.accept-consumers]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.pcp-broker.delivery-consumers]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.pcp-broker.controller-uris]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.pcp-broker.controller-whitelist]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_puppet_authorization::Rule[pxp commands]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_puppet_authorization::Rule[inventory request]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_puppet_authorization::Rule[multi-cast with destination_report]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_puppet_authorization::Rule[pcp-broker message]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Pe_puppet_authorization::Rule[pcp messages]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker broker-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker authorization-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pcp_broker[orchestration-services]\",\n      \"target\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker webrouting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services webrouting-service]\",\n      \"target\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services webrouting-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services webrouting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker metrics-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services metrics-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker metrics-webservice]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services metrics-webservice]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker scheduler-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services scheduler-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker status-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.client-auth]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-host]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-port]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-crl-path]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.access-log-config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.max-threads]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.request-header-max-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.default-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[pcp-broker]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.pcp-broker.ssl-protocols]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.client-auth]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-host]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-port]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-ca-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-cert]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-key]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-crl-path]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.access-log-config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.max-threads]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.request-header-max-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.default-server]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Webserver_settings[orchestrator]\",\n      \"target\": \"Pe_hocon_setting[orchestration-services.webserver.orchestrator.ssl-protocols]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[agent.demo.com]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[agent.demo.com]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[agent.demo.com]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[agent.demo.com]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.public_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[agent.demo.com]\",\n      \"target\": \"Puppet_enterprise::Certs::Pk8_cert[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'Xmx']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'Xms']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'XX:+PrintGCDetails']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'XX:+PrintGCDateStamps']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'Xloggc:']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'XX:+UseGCLogFileRotation']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'XX:NumberOfGCLogFiles=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[orchestration-services]\",\n      \"target\": \"Pe_ini_subsetting[pe-orchestration-services_'XX:GCLogFileSize=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[orchestration-services]\",\n      \"target\": \"Service[pe-orchestration-services]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[orchestration-services]\",\n      \"target\": \"Exec[pe-orchestration-services service full restart]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[database]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/database.ini]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[database]\",\n      \"target\": \"Pe_ini_setting[[database]-puppetdb_psdatabase_username]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[database]\",\n      \"target\": \"Pe_ini_setting[[database]-puppetdb_subname]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[database]\",\n      \"target\": \"Pe_ini_setting[[database]-maximum-pool-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/conf.d\\/read_database.ini]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\",\n      \"target\": \"Pe_ini_setting[[read-database]-puppetdb_psdatabase_username]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\",\n      \"target\": \"Pe_ini_setting[[read-database]-puppetdb_subname]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Puppetdb::Shared_database_settings[read-database]\",\n      \"target\": \"Pe_ini_setting[[read-database]-maximum-pool-size]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[puppetdb]\",\n      \"target\": \"Service[pe-puppetdb]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Pe_service[puppetdb]\",\n      \"target\": \"Exec[pe-puppetdb service full restart]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'Xmx']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'Xms']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'XX:+PrintGCDetails']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'XX:+PrintGCDateStamps']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'Xloggc:']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'XX:+UseGCLogFileRotation']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'XX:NumberOfGCLogFiles=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Java_args[puppetdb]\",\n      \"target\": \"Pe_ini_subsetting[pe-puppetdb_'XX:GCLogFileSize=']\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf java_bin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf user]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf group]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf install_dir]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf bootstrap_config]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf service_stop_retries]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Init_defaults[puppetdb]\",\n      \"target\": \"Pe_ini_setting[puppetdb initconf start_timeout]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/ssl]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-puppetdb]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.public_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs[pe-puppetdb]\",\n      \"target\": \"Puppet_enterprise::Certs::Pk8_cert[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Whitelist_entry[puppet_enterprise::certs::puppetdb_whitelist entry: agent.demo.com]\",\n      \"target\": \"Pe_file_line[\\/etc\\/puppetlabs\\/puppetdb\\/certificate-whitelist:agent.demo.com]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"Pe_staging::Deploy[puppet-agent-el-7-x86_64.tar.gz]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"Pe_repo::Set_owner_group_permissions[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/install.bash]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/upgrade.bash]\"\n    },\n    {\n      \"source\": \"Pe_repo::Repo[el-7-x86_64 2017.2.0-rc1-424-ge1372a3]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_concat::Setup]\"\n    },\n    {\n      \"source\": \"Class[Pe_concat::Setup]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin\\/concatfragments.sh]\"\n    },\n    {\n      \"source\": \"Class[Pe_concat::Setup]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat]\"\n    },\n    {\n      \"source\": \"Class[Pe_concat::Setup]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/bin]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments.concat.out]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\",\n      \"target\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver certificate-authority-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver certificate-authority-service]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments.concat.out]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[00_header_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments\\/10_00_header__etc_puppetlabs_puppetserver_conf.d_auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[99_footer_\\/etc\\/puppetlabs\\/puppetserver\\/conf.d\\/auth.conf]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_conf.d_auth.conf\\/fragments\\/10_99_footer__etc_puppetlabs_puppetserver_conf.d_auth.conf]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver jetty9-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver jetty9-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver pe-master-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver pe-master-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver request-handler-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver request-handler-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver jruby-puppet-pooled-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver jruby-puppet-pooled-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver jruby-pool-manager-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver jruby-pool-manager-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver metrics-puppet-profiler-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver metrics-puppet-profiler-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver metrics-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver metrics-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver puppet-server-config-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver puppet-server-config-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver puppet-admin-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver puppet-admin-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver webrouting-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver webrouting-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver pe-legacy-routes-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver pe-legacy-routes-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver status-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver status-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver authorization-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver authorization-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver scheduler-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver scheduler-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver pe-jruby-metrics-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver pe-jruby-metrics-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver analytics-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver analytics-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[puppetserver versioned-code-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_puppetserver_bootstrap.cfg\\/fragments\\/10_puppetserver versioned-code-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Pk8_cert[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8]\",\n      \"target\": \"Exec[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Pk8_cert[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/console-services\\/certs\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:activity activity-service]\",\n      \"target\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:activity activity-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services activity-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:activity jetty9-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-storage-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-storage-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-http-api-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-http-api-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac rbac-authn-middleware]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-authn-middleware]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:rbac activity-reporting-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services activity-reporting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:classifier classifier-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services classifier-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console webrouting-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services webrouting-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-consumer-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-consumer-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-status-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-authn-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-authn-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console rbac-authz-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services rbac-authz-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console pe-console-ui-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services pe-console-ui-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console pe-console-auth-ui-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services pe-console-auth-ui-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console status-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console scheduler-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services scheduler-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console metrics-service]\",\n      \"target\": \"Pe_concat::Fragment[console-services metrics-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[console-services:console metrics-webservice]\",\n      \"target\": \"Pe_concat::Fragment[console-services metrics-webservice]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::User[peadmin]\",\n      \"target\": \"User[peadmin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::User[peadmin]\",\n      \"target\": \"Group[peadmin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::User[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::User[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.bashrc.custom]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::User[peadmin]\",\n      \"target\": \"Pe_file_line[peadmin:path]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/ca.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/agent.demo.com.cert.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/agent.demo.com.private_key.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/peadmin-private.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/peadmin-public.pem]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Mcollective::Client::Certs[peadmin]\",\n      \"target\": \"File[\\/var\\/lib\\/peadmin\\/.mcollective.d\\/mcollective-public.pem]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[dispatch: allow pcp-brokers]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-dispatch: allow pcp-brokers]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator orchestrator-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services orchestrator-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator orchestrator-dispatch-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services orchestrator-dispatch-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator jetty9-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services jetty9-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator remote-rbac-consumer-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services remote-rbac-consumer-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:orchestrator remote-activity-reporter]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services remote-activity-reporter]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[pxp commands]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-pxp commands]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[inventory request]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-inventory request]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[multi-cast with destination_report]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-multi-cast with destination_report]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[pcp-broker message]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-pcp-broker message]\"\n    },\n    {\n      \"source\": \"Pe_puppet_authorization::Rule[pcp messages]\",\n      \"target\": \"Pe_puppet_authorization_hocon_rule[rule-pcp messages]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker broker-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services broker-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Trapperkeeper::Bootstrap_cfg[orchestration-services:pcp-broker authorization-service]\",\n      \"target\": \"Pe_concat::Fragment[orchestration-services authorization-service]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments.concat.out]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\",\n      \"target\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/orchestration-services\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services webrouting-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services webrouting-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services metrics-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services metrics-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services metrics-webservice]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services metrics-webservice]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services scheduler-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services scheduler-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services status-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services status-service]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Pk8_cert[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8]\",\n      \"target\": \"Exec[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Pk8_cert[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/orchestration-services\\/ssl\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Pk8_cert[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8]\",\n      \"target\": \"Exec[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Puppet_enterprise::Certs::Pk8_cert[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/puppetdb\\/ssl\\/agent.demo.com.private_key.pk8]\"\n    },\n    {\n      \"source\": \"Pe_staging::Deploy[puppet-agent-el-7-x86_64.tar.gz]\",\n      \"target\": \"Pe_staging::File[puppet-agent-el-7-x86_64.tar.gz]\"\n    },\n    {\n      \"source\": \"Pe_staging::Deploy[puppet-agent-el-7-x86_64.tar.gz]\",\n      \"target\": \"Pe_staging::Extract[puppet-agent-el-7-x86_64.tar.gz]\"\n    },\n    {\n      \"source\": \"Pe_repo::Set_owner_group_permissions[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0]\",\n      \"target\": \"Exec[Set perms of \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0 directories to 0755]\"\n    },\n    {\n      \"source\": \"Pe_repo::Set_owner_group_permissions[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0]\",\n      \"target\": \"Exec[Set perms of \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0 contents to 0644]\"\n    },\n    {\n      \"source\": \"Pe_repo::Set_owner_group_permissions[\\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0]\",\n      \"target\": \"Exec[Set user\\/group of \\/opt\\/puppetlabs\\/server\\/data\\/packages\\/public\\/2017.2.0-rc1-424-ge1372a3\\/el-7-x86_64-1.10.0 contents to root:root]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments.concat.out]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n      \"target\": \"File[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat[\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\",\n      \"target\": \"Exec[pe_concat_\\/etc\\/puppetlabs\\/console-services\\/bootstrap.cfg]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services activity-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services activity-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services jetty9-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services jetty9-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-storage-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-storage-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-http-api-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-http-api-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-authn-middleware]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-authn-middleware]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services activity-reporting-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services activity-reporting-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services classifier-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services classifier-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services webrouting-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services webrouting-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-consumer-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-consumer-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-status-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-status-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-authn-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-authn-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services rbac-authz-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services rbac-authz-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services pe-console-ui-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services pe-console-ui-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services pe-console-auth-ui-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services pe-console-auth-ui-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services status-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services status-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services scheduler-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services scheduler-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services metrics-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services metrics-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[console-services metrics-webservice]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_console-services_bootstrap.cfg\\/fragments\\/10_console-services metrics-webservice]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services orchestrator-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services orchestrator-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services orchestrator-dispatch-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services orchestrator-dispatch-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services jetty9-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services jetty9-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services remote-rbac-consumer-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services remote-rbac-consumer-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services remote-activity-reporter]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services remote-activity-reporter]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services broker-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services broker-service]\"\n    },\n    {\n      \"source\": \"Pe_concat::Fragment[orchestration-services authorization-service]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/puppet\\/cache\\/pe_concat\\/_etc_puppetlabs_orchestration-services_bootstrap.cfg\\/fragments\\/10_orchestration-services authorization-service]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Pe_staging]\"\n    },\n    {\n      \"source\": \"Class[Pe_staging]\",\n      \"target\": \"File[\\/opt\\/puppetlabs\\/server\\/data\\/staging]\"\n    },\n    {\n      \"source\": \"Pe_staging::File[puppet-agent-el-7-x86_64.tar.gz]\",\n      \"target\": \"Exec[\\/opt\\/puppetlabs\\/server\\/data\\/staging\\/pe_repo-puppet-agent-1.10.0\\/puppet-agent-el-7-x86_64.tar.gz]\"\n    },\n    {\n      \"source\": \"Pe_staging::Extract[puppet-agent-el-7-x86_64.tar.gz]\",\n      \"target\": \"Exec[extract puppet-agent-el-7-x86_64.tar.gz]\"\n    }\n  ],\n  \"classes\": [\n    \"pe_repo\",\n    \"pe_repo::platform::el_7_x86_64\",\n    \"puppet_enterprise\",\n    \"puppet_enterprise::license\",\n    \"puppet_enterprise::profile::agent\",\n    \"puppet_enterprise::profile::amq::broker\",\n    \"puppet_enterprise::profile::certificate_authority\",\n    \"puppet_enterprise::profile::console\",\n    \"puppet_enterprise::profile::master\",\n    \"puppet_enterprise::profile::master::mcollective\",\n    \"puppet_enterprise::profile::mcollective::agent\",\n    \"puppet_enterprise::profile::mcollective::peadmin\",\n    \"puppet_enterprise::profile::orchestrator\",\n    \"puppet_enterprise::profile::puppetdb\",\n    \"settings\",\n    \"default\",\n    \"puppet_enterprise::params\",\n    \"puppet_enterprise\",\n    \"pe_repo\",\n    \"pe_repo::platform::el_7_x86_64\",\n    \"puppet_enterprise::license\",\n    \"puppet_enterprise::profile::agent\",\n    \"puppet_enterprise::symlinks\",\n    \"puppet_enterprise::pxp_agent\",\n    \"puppet_enterprise::pxp_agent::service\",\n    \"puppet_enterprise::profile::amq::broker\",\n    \"puppet_enterprise::amq\",\n    \"puppet_enterprise::packages\",\n    \"puppet_enterprise::repo\",\n    \"puppet_enterprise::stages\",\n    \"puppet_enterprise::repo::config\",\n    \"puppet_enterprise::amq::config\",\n    \"puppet_enterprise::amq::service\",\n    \"puppet_enterprise::amq::certs\",\n    \"puppet_enterprise::profile::master\",\n    \"puppet_enterprise::profile::master::classifier\",\n    \"puppet_enterprise::profile::master::auth_conf\",\n    \"puppet_enterprise::master::tk_authz\",\n    \"puppet_enterprise::profile::master::puppetdb\",\n    \"puppet_enterprise::master\",\n    \"puppet_enterprise::master::puppetserver\",\n    \"pe_r10k::package\",\n    \"puppet_enterprise::master::file_sync_disabled\",\n    \"puppet_enterprise::profile::controller\",\n    \"puppet_enterprise::cli_config\",\n    \"puppet_enterprise::profile::certificate_authority\",\n    \"puppet_enterprise::profile::console\",\n    \"puppet_enterprise::profile::console::certs\",\n    \"puppet_enterprise::profile::console::console_services_config\",\n    \"puppet_enterprise::console_services\",\n    \"puppet_enterprise::profile::console::proxy\",\n    \"pe_nginx\",\n    \"pe_nginx::params\",\n    \"puppet_enterprise::profile::console::proxy::nginx_conf\",\n    \"puppet_enterprise::profile::console::proxy::http_redirect\",\n    \"puppet_enterprise::profile::master::mcollective\",\n    \"puppet_enterprise::mcollective::service\",\n    \"puppet_enterprise::profile::mcollective::agent\",\n    \"puppet_enterprise::mcollective::server\",\n    \"puppet_enterprise::mcollective::server::plugins\",\n    \"puppet_enterprise::mcollective::server::logs\",\n    \"puppet_enterprise::mcollective::server::certs\",\n    \"puppet_enterprise::mcollective::server::facter\",\n    \"puppet_enterprise::mcollective::cleanup\",\n    \"puppet_enterprise::profile::mcollective::peadmin\",\n    \"puppet_enterprise::profile::orchestrator\",\n    \"puppet_enterprise::profile::puppetdb\",\n    \"puppet_enterprise::puppetdb\",\n    \"puppet_enterprise::puppetdb::database_ini\",\n    \"puppet_enterprise::puppetdb::jetty_ini\",\n    \"puppet_enterprise::puppetdb::rbac_consumer_conf\",\n    \"puppet_enterprise::puppetdb::config_ini\",\n    \"puppet_enterprise::puppetdb::service\",\n    \"puppet_enterprise::certs::puppetdb_whitelist\",\n    \"pe_concat::setup\",\n    \"pe_staging\"\n  ]\n}\n"
  },
  {
    "path": "benchmarks/serialization/description",
    "content": "Benchmark scenario: Serializing catalogs in JSON vs PSON\nBenchmark target: overhead of ruby PSON/JSON serialization\n\n"
  },
  {
    "path": "benchmarks/system_startup/benchmarker.rb",
    "content": "class Benchmarker\n  def initialize(target, size)\n  end\n\n  def setup\n  end\n\n  def generate\n  end\n\n  def run(args=nil)\n    # Just running help is probably a good proxy of a full startup.\n    # Simply asking for the version might also be good, but it would miss all\n    # of the app searching and loading parts\n    `puppet help`\n  end\nend\n"
  },
  {
    "path": "benchmarks/system_startup/description",
    "content": "Benchmark scenario: running puppet commands from the CLI\nBenchmark target: overhead of loading puppet\n"
  },
  {
    "path": "benchmarks/type_inference/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = size\n  end\n\n  def setup\n  end\n\n  def run(args=nil)\n    unless @initialized\n      require 'puppet'\n      config = File.join(@target, 'puppet.conf')\n      Puppet.initialize_settings(['--config', config])\n      @initialized = true\n    end\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    # Mimic what apply does (or the benchmark will in part run for the *root* environment)\n    Puppet.push_context({:current_environment => env},'current env for benchmark')\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'type_inference')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(\n        File.join(templates, 'site.pp.erb'),\n        File.join(environment, 'manifests', 'site.pp'),\n        :size => @size)\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/type_inference/description",
    "content": "Benchmark scenario: a resource is defined with typed arguments\nBenchmark target: type inference overhead\nParser: Future\n\n"
  },
  {
    "path": "benchmarks/type_inference/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\nenvironment_timeout = '0'\n"
  },
  {
    "path": "benchmarks/type_inference/site.pp.erb",
    "content": "$vr = { 'mode' => 'read', 'path' => ['foo', 'fee' ] }\n$vw = { 'mode' => 'write', 'path' => ['biz', 'baz' ] }\n$v1 = { 'key' => 'a', 'value' => $vr }\n$v2 = { 'key' => 3, 'value' => $vw }\n$val = [ $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2, $v1, $v2 ]\n\n<% (size * 5).times do |i| %>\neach($val) | Struct[{key=>Variant[Integer[1,3],Enum[a,b,c]], value=>Struct[{mode=>Enum[read,write,update],path=>Array[String]}]}] $v | {  }\n# each($val) | $v | {  }\n$val.each | Struct[{key=>Variant[Integer[1,3],Enum[a,b,c]], value=>Struct[{mode=>Enum[read,write,update],path=>Array[String]}]}] $v | {  }\n<% end %>\n"
  },
  {
    "path": "benchmarks/virtual_collection/benchmarker.rb",
    "content": "require 'erb'\nrequire 'ostruct'\nrequire 'fileutils'\nrequire 'json'\n\nclass Benchmarker\n  include FileUtils\n\n  def initialize(target, size)\n    @target = target\n    @size = 200\n  end\n\n  def setup\n    require 'puppet'\n    config = File.join(@target, 'puppet.conf')\n    Puppet.initialize_settings(['--config', config])\n  end\n\n  def run(args=nil)\n    env = Puppet.lookup(:environments).get('benchmarking')\n    node = Puppet::Node.new(\"testing\", :environment => env)\n    Puppet::Resource::Catalog.indirection.find(\"testing\", :use_node => node)\n  end\n\n  def generate\n    environment = File.join(@target, 'environments', 'benchmarking')\n    templates = File.join('benchmarks', 'virtual_collection')\n\n    mkdir_p(File.join(environment, 'modules'))\n    mkdir_p(File.join(environment, 'manifests'))\n\n    render(File.join(templates, 'site.pp.erb'),\n           File.join(environment, 'manifests', 'site.pp'),\n           :size => @size)\n\n    render(File.join(templates, 'puppet.conf.erb'),\n           File.join(@target, 'puppet.conf'),\n           :location => @target)\n  end\n\n  def render(erb_file, output_file, bindings)\n    site = ERB.new(File.read(erb_file))\n    File.open(output_file, 'w') do |fh|\n      fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding }))\n    end\n  end\nend\n"
  },
  {
    "path": "benchmarks/virtual_collection/description",
    "content": "Benchmark scenario: heavy use of virtual collection\nBenchmark target: catalog compilation\nParser: Future\n\n"
  },
  {
    "path": "benchmarks/virtual_collection/puppet.conf.erb",
    "content": "confdir = <%= location %>\nvardir = <%= location %>\nenvironmentpath = <%= File.join(location, 'environments') %>\n\n"
  },
  {
    "path": "benchmarks/virtual_collection/site.pp.erb",
    "content": "<% size.times do |i| %>\n  @notify { \"name<%= i %>\":}\n<% end %>\n\n<% size.times do |i| %>\n  Notify <| title == \"name<%= i %>\" |>\n<% end %>\n"
  },
  {
    "path": "bin/puppet",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nbegin\n  require 'puppet/util/command_line'\n  Puppet::Util::CommandLine.new.execute\nrescue LoadError => e\n  $stderr.puts e.message\n  exit(1)\nend\n"
  },
  {
    "path": "conf/environment.conf",
    "content": "# Each environment can have an environment.conf file. Its settings will only\n# affect its own environment. See docs for more info:\n# https://puppet.com/docs/puppet/latest/config_file_environment.html\n\n# Any unspecified settings use default values; some of those defaults are based\n# on puppet.conf settings.\n\n# If these settings include relative file paths, they'll be resolved relative to\n# this environment's directory.\n\n# Allowed settings and default values:\n\n# modulepath = ./modules:$basemodulepath\n# manifest = (default_manifest from puppet.conf, which defaults to ./manifests)\n# config_version = (no script; Puppet will use the time the catalog was compiled)\n# environment_timeout = (environment_timeout from puppet.conf, which defaults to 0)\n    # Note: unless you have a specific reason, we recommend only setting\n    # environment_timeout in puppet.conf.\n"
  },
  {
    "path": "conf/fileserver.conf",
    "content": "# fileserver.conf\n\n# Puppet automatically serves PLUGINS and FILES FROM MODULES: anything in\n# <module name>/files/<file name> is available to authenticated nodes at\n# puppet:///modules/<module name>/<file name>. You do not need to edit this\n# file to enable this.\n\n# MOUNT POINTS\n\n# If you need to serve files from a directory that is NOT in a module,\n# you must create a static mount point in this file:\n#\n# [extra_files]\n#   path /etc/puppetlabs/puppet/files\n#\n# In the example above, anything in /etc/puppetlabs/puppet/files/<file name>\n# would be available to authenticated nodes at puppet:///extra_files/<file name>.\n#\n# Mount points may also use three placeholders as part of their path:\n#\n# %H - The node's certname.\n# %h - The portion of the node's certname before the first dot. (Usually the\n#      node's short hostname.)\n# %d - The portion of the node's certname after the first dot. (Usually the\n#      node's domain name.)\n\n# PERMISSIONS\n\n# The ability to set permissions - for example, using the allow, allow_ip, or\n# deny directives - has been removed from fileserver.conf. Instead, you can\n# control file access in Puppet Server's auth.conf file. See the documentation\n# at https://puppet.com/docs/puppetserver/latest/config_file_auth.html.\n"
  },
  {
    "path": "conf/hiera.yaml",
    "content": "---\n# Hiera 5 Global configuration file\n\nversion: 5\n\n# defaults:\n#   data_hash: yaml_data\n# hierarchy:\n#  - name: Common\n#    data_hash: yaml_data\nhierarchy: []\n"
  },
  {
    "path": "conf/puppet.conf",
    "content": "# This file can be used to override the default puppet settings.\n# See the following links for more details on what settings are available:\n# - https://puppet.com/docs/puppet/latest/config_important_settings.html\n# - https://puppet.com/docs/puppet/latest/config_about_settings.html\n# - https://puppet.com/docs/puppet/latest/config_file_main.html\n# - https://puppet.com/docs/puppet/latest/configuration.html\n"
  },
  {
    "path": "docs/catalogs.md",
    "content": "# Two Types of Catalogs\n\nWhen working on subsystems of Puppet that deal with the catalog it is important\nto be aware of the two different types of Catalogs.\n\nThe two different types of catalogs becomes relevant when writing spec tests\nbecause we frequently need to wire up a fake catalog so that we can exercise\ntypes, providers, or termini that filter the catalog.\n\nThe two different types of catalogs are so-called \"resource\" catalogs and \"RAL\"\n(resource abstraction layer) catalogs.  At a high level, the resource catalog\nis the in-memory object we serialize and transfer around the network.  The\ncompiler terminus is expected to produce a resource catalog.  The agent takes a\nresource catalog and converts it into a RAL catalog.  The RAL catalog is what\nis used to apply the configuration model to the system.\n\nResource dependency information is most easily obtained from a RAL catalog by\nwalking the graph instance produced by the `relationship_graph` method.\n\n### Resource Catalog\n\nIf you're writing spec tests for something that deals with a catalog \"server\nside,\" a new catalog terminus for example, then you'll be dealing with a\nresource catalog.  You can produce a resource catalog suitable for spec tests\nusing something like this:\n\n```ruby\nlet(:catalog) do\n  catalog = Puppet::Resource::Catalog.new(\"node-name-val\") # NOT certname!\n  rsrc = Puppet::Resource.new(\"file\", \"sshd_config\",\n    :parameters => {\n      :ensure => 'file',\n      :source => 'puppet:///modules/filetest/sshd_config',\n    }\n  )\n  rsrc.file = 'site.pp'\n  rsrc.line = 21\n  catalog.add_resource(rsrc)\nend\n```\n\nThe resources in this catalog may be accessed using `catalog.resources`.\nResource dependencies are not easily walked using a resource catalog however.\nTo walk the dependency tree convert the catalog to a RAL catalog as described\nin\n\n### RAL Catalog\n\nThe resource catalog may be converted to a RAL catalog using `catalog.to_ral`.\nThe RAL catalog contains `Puppet::Type` instances instead of `Puppet::Resource`\ninstances as is the case with the resource catalog.\n\nOne very useful feature of the RAL catalog are the methods to work with\nresource relationships.  For example:\n\n    irb> catalog = catalog.to_ral\n    irb> graph = catalog.relationship_graph\n    irb> pp graph.edges\n    [{ Notify[alpha] => File[/tmp/file_20.txt] },\n     { Notify[alpha] => File[/tmp/file_21.txt] },\n     { Notify[alpha] => File[/tmp/file_22.txt] },\n     { Notify[alpha] => File[/tmp/file_23.txt] },\n     { Notify[alpha] => File[/tmp/file_24.txt] },\n     { Notify[alpha] => File[/tmp/file_25.txt] },\n     { Notify[alpha] => File[/tmp/file_26.txt] },\n     { Notify[alpha] => File[/tmp/file_27.txt] },\n     { Notify[alpha] => File[/tmp/file_28.txt] },\n     { Notify[alpha] => File[/tmp/file_29.txt] },\n     { File[/tmp/file_20.txt] => Notify[omega] },\n     { File[/tmp/file_21.txt] => Notify[omega] },\n     { File[/tmp/file_22.txt] => Notify[omega] },\n     { File[/tmp/file_23.txt] => Notify[omega] },\n     { File[/tmp/file_24.txt] => Notify[omega] },\n     { File[/tmp/file_25.txt] => Notify[omega] },\n     { File[/tmp/file_26.txt] => Notify[omega] },\n     { File[/tmp/file_27.txt] => Notify[omega] },\n     { File[/tmp/file_28.txt] => Notify[omega] },\n     { File[/tmp/file_29.txt] => Notify[omega] }]\n\nIf the `relationship_graph` method is throwing exceptions at you, there's a\ngood chance the catalog is not a RAL catalog.\n\n## Settings Catalog ##\n\nBe aware that Puppet creates a mini catalog and applies this catalog locally to\nmanage file resource from the settings.  This behavior made it difficult and\ntime consuming to track down a race condition in\n[PUP-1070](https://tickets.puppetlabs.com/browse/PUP-1070).\n\nEven more surprising, the `File[puppetdlockfile]` resource is only added to the\nsettings catalog if the file exists on disk.  This caused the race condition as\nit will exist when a separate process holds the lock while applying the\ncatalog.\n\nIt may be sufficient to simply be aware of the settings catalog and the\npotential for race conditions it presents.  An effective way to be reasonably\nsure and track down the problem is to wrap the File.open method like so:\n\n```ruby\n# We're wrapping ourselves around the File.open method.\n# As described at: https://goo.gl/lDsv6\nclass File\n  WHITELIST = [ /pidlock.rb:39/ ]\n\n  class << self\n    alias xxx_orig_open open\n  end\n\n  def self.open(name, *rest, &block)\n    # Check the whitelist for any \"good\" File.open calls against the #\n    puppetdlock file\n    white_listed = caller(0).find do |line|\n      JJM_WHITELIST.find { |re| re.match(line) }\n    end\n\n    # If you drop into IRB here, take a look at your caller, it might be\n    # the ghost in the machine you're looking for.\n    binding.pry if name =~ /puppetdlock/ and not white_listed\n    xxx_orig_open(name, *rest, &block)\n  end\nend\n```\n\nThe settings catalog is populated by the `Puppet::Util::Settings#to\\_catalog`\nmethod.\n"
  },
  {
    "path": "docs/environment_convergence.md",
    "content": "# Environment Convergence\n\nThe term \"environment\" as used in this document refers to a directory on the server, such as `/etc/puppetlabs/code/environments/<name>`, containing puppet manifests, hiera data, custom facts, etc.\n\nAt the beginning of an agent run, the agent and server negotiate which environment to use. It is important for the agent and server to use the same environment during the run, because the manifest may reference facts that must be downloaded and evaluated on the agent. If they are mismatched, then compilation can fail.\n\nFor security reasons, puppet defaults to **server-specified environments**. This means the server always decides which environment to assign to each agent. This is important because we don't want an agent to request a catalog from an arbitrary environment, as the server might include a class containing sensitive data.\n\nHowever, there are cases during [iterative development](https://puppet.com/docs/pe/latest/environment_based_testing.html) where it is necessary for a trusted user to be able to run the agent against an environment contained in a feature branch. For example, `puppet agent -t --environment <feature>`. If the code works as intended, then it provides confidence that the feature branch code can be merged. We refer to this workflow as an **agent-specified environment**.\n\n## Agent and Server Negotiation\n\nThe server decides which environment an agent should use by asking its node terminus to classify the node. Terminus just means there's an implementation of an interface that knows how to perform CRUD operations on `Puppet::Node` objects. The terminus is responsible for returning a node object, including the environment that the node is assigned to.\n\nPE ships with a `classifier` node terminus that communicates with the PE classifier via REST. So by default, PE uses server-specified environments.\n\nHowever, open source defaults to a `plain` node terminus, which means it falls back to agent-specified environments.\n\nWhen the agent negotiates with the server, the server may respond in one of the following ways:\n\n  1. Tell the agent to switch to the server-specified environment.\n  2. Allow the agent to continue using its current agent-specified environment.\n  3. Fail the run due to a classification conflict. Conflicts can arise if the agent is assigned to multiple environments at the same time, as can happen if the agent's facts match multiple rules in the classifier.\n  4. Fail the request because the agent's requested environment doesn't exist on the server.\n\n## Convergence\n\nIf the agent starts off in the correct environment at the start of its run and it uses that environment for the duration of the run, then the agent and server environments are **synchronized**.\n\nThe process of trying to synchronize environments is referred to as **environment convergence**.\n\n## Server-specified environments\n\nWhen running in a server-specified context, i.e. the server always decides which environment to use, then the agent's run should result in one of the following outcomes (ignoring networking issues, etc):\n\n  1. If the agent's environment is synchronized with the server, then the agent uses that environment for the duration of the run.\n  2. If the environments are not synchronized, then the server will attempt to switch the agent to the server-specified environment. The agent will retry its run using the server-specified environment, up to some maximum retry limit.\n  3. If the environment does not converge after N retries, then the agent fails the run. This can happen if classification rules cause the environment to flap from environment A to B to A, etc.\n\nThere are several reasons why environments may not be synchronized:\n\n1. The agent has never run before or its cache directory was deleted.\n2. The agent was configured to use a different environment on the command line or puppet.conf than it used last time.\n3. Classification changed on the server.\n4. A fact's value changed on the agent and classification is based on the fact.\n5. The environment was updated on the server, such as modifying a fact used in classification.\n6. The environment was deleted on the server.\n\nIn all cases, the server will attempt to switch the agent to a \"known good\" environment. For example, if agents are configured to use an environment, but it is deleted from the server, then we want those agents to reconverge to a **new** server-specified environment. This self-healing property is important when running agents at scale.\n\n## Agent-specified environments\n\nWhen running in an agent-specified context, i.e. the server **allows** the agent to decide, then the agent's run may result in one of the following outcomes (again ignoring networking issues):\n\n1. If the environment exists on the server, then the agent will use that for the duration of the run, even if it needs to download facts and plugins from the server.\n2. If the environment doesn't exist on the server:\n  1. By default, switch to the server-specified environment, typically the value of `Puppet[:environment]` on the server.\n  2. Otherwise, if `strict_enviroment_mode` is enabled, then fail the run, because the user wants to strictly use the requested environment.\n\n## Last Used Environment\n\nPrior to 6.25.0 and 7.10.0, the agent used to make a node request at the beginning of the run to determine which environment to start off in. This was changed in [PUP-10216](https://tickets.puppet.com/browse/PUP-10216) so the agent will start off in the environment it used last time. This information is stored in `Puppet[:lastrunfile]`. Doing so eliminates the agent's node request and several requests among server, classifier, puppetdb and postgres. \n\nThe old behavior can be enabled using the `Puppet[:use_last_environment]=false` setting or specifying `--no-use_last_environment` on the command line.\n\n## Flow Diagram\n\nThe following flow diagram shows how the agent converges its environment. The green lines trace the happy path:\n\n[![](https://mermaid.ink/img/pako:eNqlVt9v2jAQ_les7LWVRqm0jYdNVUufWlQB3R4CqjznEqw6NrMdWgT93-dfwSHA0IAHuJzv--783XHKKiEig6SXTHjOxBuZYanR-G7CkfmMtH26vPyObmdAXm8xmcHqWQFyVmZ-NGai-PHhw2OQxazHsoL1g8CZc6XWagGne3H3mClYD0xZA1xCOgQl2AKQdSDrCagNsyvwaHgd4KJ_YkazEcgFyD5fUCl4CVyvGjbqv1OlVX21_QBXbu7KdXcYaUmJfjSZGheLTpd6ra0shuSRqhJrMkv7UgqJfhnOAjXIp__gCDkFY0D0PSZaHS3TpX1iVUG5WnISyBsMjvlJKB2aY5T8U4HSaABvrYZFGkfNTUDuKAjmyIwQLwBxU-wLN4K_LDCrtotNwwNyTzVpTB1HznRzLriCVV3MqCIEIINsa-jqsHhTK7BVF9RWV4PvMLjRTTdcZhjZb0xe49gjwU3dlFUSapqYLBZwK7jpgh5ilgYTaYGGNw8tLVtYnz_2e-VNZO063fY07AzU3qDGvYZgjqwu715JZB1L9EBLqlvCuMCYZCzEI-bL4A-Da5zIelFwTw8w-Ap2JnBH6a07uZO6kWF5HOvnLlseNoo9obwIhftuDoRGN8ysPshC5du3dLM4hLmQOjYsKL1ztkmxD9W8S6Oszcm-fRCvPD3M05w3DxiLo3MX59PV-jzPsAa_qb19YFfvpHHwm_mcLVsVHhKpkSpiU_fdSuZ9EZ-OgGfBDiH-wcXcCR72rv9mlJvFuWSAPiOlpXiF3qc87154-_KNZnrWu56_B0evkAC8Db06HXp9OvTr6dBvp0M7Z-jU6ZyBPUPkzhkqX305Hds9Q6vuGVp1_0ur5CIpQZaYZuYNb2W5JomeQQmTpGfMDHJcMT0xL38fJrRyf8x-RrWQSc9tp4sEV1qMzLreOHzUHcWFxGXwfvwFBtGZ8Q)](https://mermaid.live/edit#pako:eNqlVt9v2jAQ_les7LWVRqm0jYdNVUufWlQB3R4CqjznEqw6NrMdWgT93-dfwSHA0IAHuJzv--783XHKKiEig6SXTHjOxBuZYanR-G7CkfmMtH26vPyObmdAXm8xmcHqWQFyVmZ-NGai-PHhw2OQxazHsoL1g8CZc6XWagGne3H3mClYD0xZA1xCOgQl2AKQdSDrCagNsyvwaHgd4KJ_YkazEcgFyD5fUCl4CVyvGjbqv1OlVX21_QBXbu7KdXcYaUmJfjSZGheLTpd6ra0shuSRqhJrMkv7UgqJfhnOAjXIp__gCDkFY0D0PSZaHS3TpX1iVUG5WnISyBsMjvlJKB2aY5T8U4HSaABvrYZFGkfNTUDuKAjmyIwQLwBxU-wLN4K_LDCrtotNwwNyTzVpTB1HznRzLriCVV3MqCIEIINsa-jqsHhTK7BVF9RWV4PvMLjRTTdcZhjZb0xe49gjwU3dlFUSapqYLBZwK7jpgh5ilgYTaYGGNw8tLVtYnz_2e-VNZO063fY07AzU3qDGvYZgjqwu715JZB1L9EBLqlvCuMCYZCzEI-bL4A-Da5zIelFwTw8w-Ap2JnBH6a07uZO6kWF5HOvnLlseNoo9obwIhftuDoRGN8ysPshC5du3dLM4hLmQOjYsKL1ztkmxD9W8S6Oszcm-fRCvPD3M05w3DxiLo3MX59PV-jzPsAa_qb19YFfvpHHwm_mcLVsVHhKpkSpiU_fdSuZ9EZ-OgGfBDiH-wcXcCR72rv9mlJvFuWSAPiOlpXiF3qc87154-_KNZnrWu56_B0evkAC8Db06HXp9OvTr6dBvp0M7Z-jU6ZyBPUPkzhkqX305Hds9Q6vuGVp1_0ur5CIpQZaYZuYNb2W5JomeQQmTpGfMDHJcMT0xL38fJrRyf8x-RrWQSc9tp4sEV1qMzLreOHzUHcWFxGXwfvwFBtGZ8Q)\n"
  },
  {
    "path": "docs/http.md",
    "content": "# HTTP Client\n\n<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->\n**Table of Contents**\n\n- [HTTP Client](#http-client)\n    - [Problems](#problems)\n        - [REST Client](#rest-client)\n        - [Persistent connections](#persistent-connections)\n        - [Routing](#routing)\n        - [Inconsistencies](#inconsistencies)\n        - [Error Handling](#error-handling)\n        - [SSL Trust Stores](#ssl-trust-stores)\n    - [Proposal](#proposal)\n        - [Goals](#goals)\n        - [Non-Goals](#non-goals)\n    - [Design](#design)\n        - [Classes](#classes)\n            - [Client](#client)\n            - [Connection Pool](#connection-pool)\n            - [Route](#route)\n            - [Service](#service)\n            - [Resolvers](#resolvers)\n            - [Session](#session)\n        - [Routing](#routing-1)\n            - [DNS SRV](#dns-srv)\n            - [Server List](#server-list)\n            - [Default Puppet Settings](#default-puppet-settings)\n        - [Generic HTTP(S) Requests](#generic-https-requests)\n        - [Puppetserver](#puppetserver)\n\n<!-- markdown-toc end -->\n\n## Problems\n\nThis is a proposal to restructure the HTTP client code in puppet to solve the\nfollowing problems.\n\n### REST Client\n\nIt's difficult to use puppet as a library to call our own REST APIs due to the\ncoupling of puppet's http code with the indirector. As a result, users have\ncreated REST clients, but they don't behave the same way our agent does, such as\nserialization and deserialization of rich data, `server_list` for high\navailability, and JSON to PSON content negotiation, etc.\n\nIt would be beneficial to the puppet ecosystem to have an REST client that's\nreusable by more than the agent.\n\n### Persistent connections\n\nPersistent HTTP connections allow puppet to establish an HTTP(S) connection once\nand reuse it for multiple HTTP requests. This avoids making a new TCP connection\nand SSL handshake for each request. This is important for pluginsync, due to the\nlarge number of individual GET requests. However, persistent connections are not\nenabled by default, and must be opted into, as was recently done for `puppet\ndevice` and `puppet plugin download`. More than likely, other applications\nshould be using persistent connections, but aren't.\n\n### Routing\n\nPuppet supports 3 ways of routing connections: DNS SRV records, server list, and\nstatic puppet settings. However, some routing methods are not consistently\napplied. For example, `puppet plugin download` and `puppet report upload` don't\nobserve server list.\n\nOnce a route has been determined, puppet stores the last used server and port in\nPuppet's context system, but it's more of a hack than anything. As a result,\nit's difficult to know how the last used server and port were set and when to\ninvalidate them.\n\n### Inconsistencies\n\n`Puppet::Network::HTTP::Connection` supports two ways of making GET and POST\nrequests, but they don't behave consistently when handling HTTP redirects, the\n`Retry-After` header, server and proxy authentication, and exception handling.\n\n### Error Handling\n\nThe `Puppet::Network::HttpPool` and related classes don't specify which\nexceptions can be raised. Instead they pass through whatever exceptions ruby\nraises. Everything from `SocketError` to `SystemCallError` to\n`OpenSSL::SSL::SSLError` to `Net::ProtocolError` and `TimeoutError`. As a\nresult, it's hard for clients to build higher level abstractions.\n\n### SSL Trust Stores\n\nPuppet only trusts the puppet PKI when connecting to puppet infrastructure, but\nneeds to additionally trust the system cert store for requests like PMT and\ndownloading files from https sources. However, the current API doesn't allow the\ncaller to do that, which is why `Puppet::Util::HttpProxy#request_with_redirects`\nduplicates the logic from`Puppet::Network::HTTP::Connection#request_with_redirects`.\n\n## Proposal\n\nIn order to solve these problems, I propose creating an HTTP client in puppet\nwith the following goals:\n\n### Goals\n\n* Implement a REST client in puppet capable of serializing/deserializing puppet\n  objects like Catalog, Report, etc.\n* Reuse the existing networking code as much as possible, such as\n  `Puppet::Network::HTTP::Pool`, but restructure it with a clear API.\n* Always use persistent connections unless the caller explicitly opts out.\n* Handle server resolution (via DNS SRV, etc) in a consistent way.\n* Define an exception hierarchy for the API so that `Net::HTTP` specific\n  exceptions don't leak out.\n* Make it possible to use the system trust store for a single HTTPS request.\n\n### Non-Goals\n\n* Ruby's builtin `Net::HTTP` library is fairly buggy, however, we're not switching\n  away from it right now. We may in the future, but it's out of scope.\n* Serialization of puppet domain objects requires pops, rich data and loaders.\n  As a result, creating a standalone puppet-http gem is out of scope.\n\n## Design\n\n### Classes\n\n![classes](./httpclient.png)\n\n#### Client\n\nHas a pool of persistent HTTP connections and creates HTTP sessions. Closes\npersistent connections when its close method is called.\n\nHas low-level HTTP methods, such as `get`, `post`, etc which take the path,\nheaders, options, and allow the caller to stream the request and response body.\nReturns `Puppet::HTTP::Response` with the response code, etc.\n\n#### Connection Pool\n\nMaintains the pool of persistent `Net::HTTP` connections, keeping track of when\nidle connections expire. The `with_connection` method takes a block, which\nensures borrowed connections are always returned to the pool.\n\n#### Route\n\nDefines a route to a REST service. Includes the API prefix, DNS SRV service name,\nand puppet server and port settings for that service.\n\n#### Service\n\nRepresents an instance of a puppet web service. Includes the URL used to connect\nto the service, such as `https://puppet:8140/puppet/v3`. There are four\nservices: `ca`, `report`, `fileserver`, and the default `puppet`.\n\nThe `ca` and `report` services handle certs and reports, respectively. The\n`fileserver` service handles puppet file metadata and content requests, such as\npluginsync and file resources with `source => 'puppet://'`. The `puppet` service\nhandles nodes, facts, and catalogs, and is also the fallback for the other three\nservices.\n\nEach service is responsible for serializing/deserializing the HTTP entity into a\ndomain object. It uses the existing `Puppet::Network::Format` code to do so.\n\n#### Resolvers\n\nEach resolver represents a different strategy for resolving a service name into\na list of candidate servers and ports.\n\n#### Session\n\nRepresents an HTTP session through which services may be connected to and accessed.\n\nHas a `Session#route_to` method to route to a web service based on the requested\nservice name and client configuration:\n\n```ruby\nclient = Puppet::HTTP::Client.new\nsession = client.create_session\nservice = session.route_to(:ca)\ncert = service.get_certificate('foo')\nputs \"Retrieved cert #{cert.subject.to_utf8} from #{service.url}\"\n```\n\nThe `Session#route_to(:ca)` method (above) returns an instance of\n`Puppet::HTTP::Service::Ca`, which has methods appropriate for that type of\nservice. All services extend `Puppet::HTTP::Service`.\n\n### Routing\n\nIf an explicit server and port are specified on the command line or\nconfiguration, such as `puppet agent -t --server foo.example.com`, then the\n`Session#route_to` method will always return a `Service` with that host and port.\n\nOtherwise, the session will walk the list of resolvers in priority order:\n\n* DNS SRV\n* Server list\n* Puppet server/port settings\n\nIf the `route_to` method attempts to connect to a service, but it results in an\nexception, such as \"connection refused\", then the session will attempt the next\nservice.\n\nIf the caller successfully uses a service, then the session will return the same\nservice the next time `route_to` is called again.\n\n#### DNS SRV\n\nThe DNS SRV resolver performs an SRV lookup, and randomly selects one of the\ntargets based on the weight of each entry in the SRV record. A target with\nweight 2 would be twice as likely to be chosen as a target with weight 1.\n\n```ruby\nclient = Puppet::HTTP::Client.new(use_srv: true, srv_domain: 'puppet.example.com')\nsession = client.create_session\nservice = session.route_to(:ca)\n# service.url is \"https://compiler1.puppet.example.com:8140\"\n```\n\n#### Server List\n\nThe server list resolver selects the first available server using puppetserver's\nsimple status endpoint. This applies when routing requests to the `:puppet`\nservice, as well as any service whose server and port are the same as the\n`:puppet` service. For example, when `:ca_server` and `:report_server` have not\nbeen overridden.\n\n```ruby\nclient = Puppet::HTTP::Client.new(server_list: ['compiler1', 'compiler2'])\nsession = client.create_session\nservice = session.route_to(:puppet)\n# service.url is \"https://compiler1:8140\"\n```\n\n#### Puppet Settings\n\nThe resolver selects a route based on the puppet settings for that service:\n\n| service    | server setting | port setting |\n|------------|----------------|--------------|\n| ca         | ca_server      | ca_port      |\n| fileserver | server         | serverport   |\n| report     | report_server  | report_port  |\n| puppet     | server         | serverport   |\n\nFor example, `route_to(:report)` would use `Puppet[:report_server]` and\n`Puppet[:report_port]`.\n\n#### Example: CA Service Routing\n\nThere are some variations in how the different services are routed. Here is a\nvisual of how the CA service is routed. We have to preserve some [interesting behavior](https://github.com/puppetlabs/puppet/blob/master/lib/puppet/http/client.rb#L243-L249)\nwith this service, but otherwise the flow is similar to that of other services.\n\n![httpcaroute](./httpcaroute.png)\n\n### Generic HTTP(S) Requests\n\nPuppet agents support downloading file content from 3rd party file servers,\nwhich reduces load on the compiler. The `Client` will provide a low-level API\nfor making `GET` requests for an arbitrary URL, and streaming the response body.\n\nPuppet only trusts the puppet PKI for its REST requests. However, it should be\npossible to additionally trust the system store when making HTTPS requests:\n\n```ruby\nclient = Puppet::HTTP::Client.new\nresponse = client.get(\"https://artifactory.example.com/java.tar.gz\", options: { include_system_store: true })\nresponse.read_body do |data|\n  puts \"Read #{data.bytes}\"\nend\n```\n\n### Puppetserver\n\nPuppet ruby code running in puppetserver sometimes make outbound connections\nsuch as the [puppetdb\nterminus](https://github.com/puppetlabs/puppetdb/blob/6.5.0/puppet/lib/puppet/util/puppetdb/http.rb#L138),\nPE classifier terminus, and ['http' report\nprocessor](https://github.com/puppetlabs/puppet/blob/6.7.0/lib/puppet/reports/http.rb#L32).\nCurrently, puppetserver registers its own http client class, so that it can\nperform the HTTP request using Apache HttpClient.\n\nIn order to preserve this capability, puppetserver should have a way of\noverriding the `get` and `post` methods of `Puppet::HTTP::Client` to call the\nApache HttpClient instead.\n\nOne way might be to create an adapter that overrides Puppet's implementation and\ndelegates to [puppetserver's\nclient](https://github.com/puppetlabs/puppetserver/blob/f718994c0f32f8c697daa662ec4074e4596350fc/src/ruby/puppetserver-lib/puppet/server/http_client.rb#L23):\n\n```ruby\nclass Puppet::Server::HttpClientAdapter < Puppet::HTTP::Client\n  def initialize(http_client)\n    super\n    @http_client = http_client\n  end\n\n  def get(url, headers={}, options={})\n    @http_client.get(url, headers, options)\n  end\n\n  # etc\nend\n```\n\nAnd register it with puppet:\n\n```ruby\nPuppet.push_context(http_client: HttpClientAdapter.new(Puppet::Server::HttpClient.new))\n\n```\n"
  },
  {
    "path": "docs/index.md",
    "content": "# Puppet Developer Documentation\n\nSetting up and running tests\n\n* [Quickstart Guide](quickstart.md)\n* [RSpec Tutorial](rspec_tutorial.md)\n* [Running acceptance tests](../acceptance/README.md)\n\nDeveloper References\n\n* [Profiling and Benchmarking](profiling.md)\n* [Various Catalog Forms](catalogs.md)\n* [Windows](windows.md)\n* [Unicode and you](unicode.md)\n* [Working on the parser](parser_work.md)\n* [Indirector](indirector.md)\n* [HTTP](http.md)\n* [Environment Convergence](environment_convergence.md)\n* [Settings](settings.md)\n"
  },
  {
    "path": "docs/indirector.md",
    "content": "# Indirector\n\n> This document describes Puppet's indirector subsystem, but it has a number of limitations described below. As a result, don't introduce any new indirections or termini.\n\nPuppet's indirector supports pluggable backends (termini) for a variety of key-value stores (indirections). Each indirection type corresponds to a particular Ruby class (the \"Indirected Class\" below) and values are instances of that class. Each instance's key is available from its name method. The termini can be local (e.g., on-disk files) or remote (e.g., using a REST interface to talk to a puppet master).\n\nAn indirector has five methods, which are mapped into HTTP verbs for the REST interface:\n\n* `find(key)` - get a single value (mapped to GET or POST with a singular endpoint)\n* `search(key)` - get a list of matching values (mapped to GET with a plural endpoint)\n* `head(key)` - return true if the key exists (mapped to HEAD)\n* `destroy(key)` - remove the key and value (mapped to DELETE)\n* `save(instance)` - write the instance to the store, using the instance's name as the key (mapped to PUT)\n\nThese methods are available via the indirection class method on the indirected classes. For example, the following:\n\n```ruby\ncatalog  = Puppet::Resource::Catalog.indirection.find('foo.example.com')\n```\n\nWill retrieve the catalog for the node `foo.example.com` based on the currently configured catalog terminus. If the terminus is the compiler, a new catalog will be compiled. If the terminus is `json`, it will be loaded from disk.\n\nAt startup, each indirection is configured with a terminus. In most cases, this is the default terminus defined by the indirected class, but it can be overridden by the application or face, or overridden with the route_file configuration. The available termini differ for each indirection, and are listed below.\n\nIndirections can also have a cache, represented by a second terminus. This is a write-through cache: modifications are written both to the cache and to the primary terminus. Values fetched from the terminus are written to the cache.\n\n## Interaction with REST\nREST endpoints have the form /{prefix}/{version}/{indirection}/{key}?environment={environment}, where the indirection can be singular or plural, following normal English spelling rules. However, like most things in the English language, there are [exceptions](https://github.com/puppetlabs/puppet/blob/359ca36977e7a096385f6ea9cc0a10c03df5a7e9/lib/puppet/network/http/api/indirected_routes.rb#L269-L287). On the server side, REST responses are generated from the locally-configured endpoints.\n\n## Indirections and Termini\nBelow is the list of all indirections, their associated terminus classes, and how you select between them.\n\nIn general, the appropriate terminus class is selected by the application for you (e.g., puppet agent would always use the rest terminus for most of its indirected classes), but some classes are tunable via normal settings. These will have terminus setting documentation listed with them.\n\n### catalog\nIndirected Class: `Puppet::Resource::Catalog`\nTerminus Setting: `catalog_terminus`\n\n`compiler` terminus\nCompiles catalogs on demand using Puppet's compiler.\n\n`json` terminus\nStore catalogs as flat files, serialized using JSON.\n\n`msgpack` terminus\nStore catalogs as flat files, serialized using MessagePack.\n\n`rest` terminus\nFind resource catalogs over HTTP via REST.\n\n`store_configs` terminus\nPart of the \"storeconfigs\" feature. Should not be directly set by end users.\n\n`yaml` terminus\nStore catalogs as flat files, serialized using YAML.\n\n### data_binding\nWhere to find external data bindings.\n\nIndirected Class: `Puppet::DataBinding`\nTerminus Setting: `data_binding_terminus`\n\n`hiera` terminus\nRetrieve data using Hiera.\n\n`none` terminus\nA Dummy terminus that always throws :no_such_key for data lookups.\n\n### facts\nIndirected Class: `Puppet::Node::Facts`\nTerminus Setting: `facts_terminus`\n\n`facter` terminus\nRetrieve facts from Facter. This provides a somewhat abstract interface between Puppet and Facter. It's only somewhat abstract because it always returns the local host's facts, regardless of what you attempt to find.\n\n`memory` terminus\nKeep track of facts in memory but nowhere else. This is used for one-time compiles, such as what the stand-alone puppet does. To use this terminus, you must load it with the data you want it to contain.\n\n`network_device` terminus\nRetrieve facts from a network device.\n\n`store_configs` terminus\nPart of the \"storeconfigs\" feature. Should not be directly set by end users.\n\n`yaml` terminus\nStore client facts as flat files, serialized using YAML, or return deserialized facts from disk.\n\n### file_bucket_file\nIndirected Class: `Puppet::FileBucket::File`\n\n`file` terminus\nStore files in a directory set based on their checksums.\n\n`rest` terminus\nThis is a REST based mechanism to send/retrieve file to/from the filebucket\n\n`selector` terminus\nSelect the terminus based on the request\n\n### file_content\nIndirected Class: `Puppet::FileServing::Content`\n\n`file` terminus\nRetrieve file contents from disk.\n\n`file_server` terminus\nRetrieve file contents using Puppet's fileserver.\n\n`http` terminus\nRetrieve file contents from a remote HTTP server.\n\n`rest` terminus\nRetrieve file contents via a REST HTTP interface.\n\n`selector` terminus\nSelect the terminus based on the request\n\n### file_metadata\nIndirected Class: `Puppet::FileServing::Metadata`\n\n`file` terminus\nRetrieve file metadata directly from the local filesystem.\n\n`file_server` terminus\nRetrieve file metadata using Puppet's fileserver.\n\n`http` terminus\nRetrieve file metadata from a remote HTTP server.\n\n`rest` terminus\nRetrieve file metadata via a REST HTTP interface.\n\n`selector` terminus\nSelect the terminus based on the request\n\n### node\nWhere to find node information. A node is composed of its name, its facts, and its environment.\n\nIndirected Class: `Puppet::Node`\nTerminus Setting: `node_terminus`\n\n`exec` terminus\nCall an external program to get node information. See the External Nodes page for more information.\n\n`ldap` terminus\nSearch in LDAP for node configuration information. See the LDAP Nodes page for more information. This will first search for whatever the certificate name is, then (if that name contains a .) for the short name, then default.\n\n`memory` terminus\nKeep track of nodes in memory but nowhere else. This is used for one-time compiles, such as what the stand-alone puppet does. To use this terminus, you must load it with the data you want it to contain; it is only useful for developers and should generally not be chosen by a normal user.\n\n`msgpack` terminus\nStore node information as flat files, serialized using MessagePack, or deserialize stored MessagePack nodes.\n\n`plain` terminus\nAlways return an empty node object. Assumes you keep track of nodes in flat file manifests. You should use it when you don't have some other, functional source you want to use, as the compiler will not work without a valid node terminus.\n\nNote that class is responsible for merging the node's facts into the node instance before it is returned.\n\n`rest` terminus\nGet a node via REST. Puppet agent uses this to allow the puppet master to override its environment.\n\n`store_configs` terminus\nPart of the \"storeconfigs\" feature. Should not be directly set by end users.\n\n`yaml` terminus\nStore node information as flat files, serialized using YAML, or deserialize stored YAML nodes.\n\n### report\nIndirected Class: `Puppet::Transaction::Report`\n\n`msgpack` terminus\nStore last report as a flat file, serialized using MessagePack.\n\n`processor` terminus\nPuppet's report processor. Processes the report with each of the report types listed in the ‘reports' setting.\n\n`rest` terminus\nGet server report over HTTP via REST.\n\n`yaml` terminus\nStore last report as a flat file, serialized using YAML.\n\n### resource\nIndirected Class: `Puppet::Resource`\n`ral` terminus\nManipulate resources with the resource abstraction layer. Only used internally.\n\n`store_configs` terminus\nPart of the \"storeconfigs\" feature. Should not be directly set by end users.\n\n`rest` terminus\nGet puppet master's status via REST. Useful because it tests the health of both the web server and the indirector.\n\n## Limitations\n\nHere are specific issues with the indirector:\n\n* The indirector relies on mutable global state, such as the list of indirections and which termini and caches are currently in use.\n* Termini can be configured in settings, code or `routes.yaml`, but not all termini can be configured the same way. For example, there is a `node_terminus` setting, but no `report_terminus`. There's a `catalog_cache_terminus`, but no `fact_cache_terminus`.\n* There can only be one termini for an indirection at any one time. However, some applications like `puppet catalog find` need to make a REST request and then write the catalog to disk, which [means the terminus is changed at runtime](https://github.com/puppetlabs/puppet/blob/359ca36977e7a096385f6ea9cc0a10c03df5a7e9/lib/puppet/face/catalog.rb#L134-L143).\n* The indirector maintains an optional terminus cache, which is often used to overcome the previous limitation. For example, `puppet agent` downloads the catalog using the `rest` terminus, but saves the cached catalog as a side effect.\n* The caller is aware of the cache, and [sometimes bypasses it](https://github.com/puppetlabs/puppet/blob/359ca36977e7a096385f6ea9cc0a10c03df5a7e9/lib/puppet/configurer.rb#L297) or [forces it to be used](https://github.com/puppetlabs/puppet/blob/359ca36977e7a096385f6ea9cc0a10c03df5a7e9/lib/puppet/configurer.rb#L457).\n* It's not clear when objects are cleared from the cache.\n* If an exception occurs when calling the terminus, the caller can't tell whether it occurred in the terminus or the cache terminus.\n* Exceptions from the remote side of the `rest` terminus, don't propagate back. So if `find` returns nil, the caller doesn't know if it the resource doesn't exist, or an exception occurred for a different reason, such as the environment doesn't exist. The `fail_on_404` option was created to handle that case, but is meaningless for non-REST termini.\n* Can't process the HTTP response header to do something intelligent, e.g. to switch facts to JSON instead of PSON\n* Doesn't support streaming as objects must be fully loaded in memory.\n* Concrete termini, e.g. `Puppet::Node::Facts::Rest`, must inherit from an abstract terminus like `Puppet::Indirector::REST`, `Puppet::Indirector::Code`, etc. Also concrete termini can't inherit from other concrete termini.\n"
  },
  {
    "path": "docs/parser_work.md",
    "content": "Working on Parser Logic\n===\n\nThis document contains advice related to doing grammar / parser work.\n\nFrom Grammar to Ruby\n---\nThe grammar is described in a `.ra` (racc) file. For the \"future parser\", this is in\nlib/puppet/pops/parser/egrammar.ra and it is combined with the parser_support.rb file in the\nsame directory and processed by race. The output is the resulting parser (in eparser.rb).\n\nNever modify the `parser.rb` by hand.\n\nMerge conflicts\n---\nSimply touch the `egrammar.ra` (unless it was changed by resolving merge conflicts), and\nthen rebuild the parser by running make in the same directory.\n\nThe resulting `eparser.rb` should be checked in.\n\nThe eparser.rb and Racc runtime\n---\nIf you look inside the `eparser.rb` file, you see several tables and a set of methods.\nThe tables are used by the racc runtime (written n C and part of Ruby), and it calls back to\nthe methods that implement the actions that were expressed in the grammar.\n\nNote that the file contains source file/line references to the grammar file, thus ensuring\nthat runtime exceptions appear to come from the grammar file (as they should).\n\nGrammar Ambiguities\n---\nIf you are working with grammar changes, you may run into ambiguity problems. There are two kinds of conflicts:\n\n* shift/reduce\n* reduce/reduce\n\nBot of these conflicts mean that racc can not determine what to do when the sequence of source tokens have made it reach a particular state.\n\nA \"shift\" can be read as \"tell me more\", and \"reduce\" as \"got it\". So a shift/reduce is an ambiguity\nwhere the grammar expresses that it is both ok to accept the state as complete, or to continue and build something more elaborate. A reduce/reduce, is trickier, since this means we reached a state\nwhere there appears to be no difference between completing one of multiple choices.\n\nThere are several reasons to why shift/reduce, and reduce/reduce conflicts occur:\n\n* The language is truly ambiguous, i.e. there is no way to differentiate between two or more\n  choices. This is poorly design language feature and it can only be fixed completely by changing\n  the language. It is however possible to make such a problem less of an issue by hardcoding\n  a decision and thus blocking one interpretation of the input from occurring. When doing so, the\n  trick is to make this happen in a very dark corner of the programming language; i.e. in\n  a sequence that is of little practical use. In all cases, avoid having ambiguities in the\n  language.\n\n* Racc only performs one token look-ahead over rule boundaries, any lookahead beyond that must\n  take place in one and the same rule!\n  To resolve these, you can introduce additional states, you can roll up rules into a larger rule, or\n  you can assign precedence to rules.\n  * \"flattening\" the grammar means  that you spell out a sequence of tokens even if it would\n    be less repetition to refer to a rule. Remember, breaking up sequences into rules is not\n    just syntactic, it changes how the parser works.\n  * adding states, means that you break up sequences into pieces that are unambiguous; thus making\n    it possible for the grammar to reduce them. Unfortunately this makes the grammar quite abstract\n    and hard to read.\n  * Assigning precedence to rules can solve a shift/reduce. The decision with the highest precedence\n    will win. When doing this for rules, great care must be taken as it may mean that certain rules\n    can never be triggered.\n  \n* The language is an expression language and racc can not on its own determine the priority\n  of operators - e.g. in 1 + 2 * 3, should the addition or the multiplication be performed first?\n  Issues of this kind are easy to solve by giving operators a precedence.\n\nAs a rule of thumb, do not try to implement all semantics of the language in the grammar. It\nis far better to make the grammar parse non sensical input and then validate the result than\ntrying to capture all semantics via grammar rules. This makes the grammar simpler and there\nare far fewer grammar conflicts to deal with.\n\n### How Racc signals Ambiguities\n\nWhen the grammar (.ra file) is processed racc outputs information about unused/useless rules\nand the number of shift/reduce, and reduce/reduce conflicts. It will still produce a parser, so\nyou must pay attention to this output. If you see conflicts it means that certain parts of\nthe grammar may be unreachable (racc has built in defaults that **may** be what you want, but\nit is most often by accident).\n\nWhen an ambiguity is reported. You need to generate a more detailed report. You do that by running\nthe makefile target `egrammar.output`. This produces a file with the same name. At the top of this\nfile, you will find a more detailed report of which states/rules that are involved in the ambiguity.\n\nIt may for instance say:\n\n     state 168 contains one shift/reduce conflict\n     \nTo find what this means, you search for \"state 168\", it is probably mentioned in several places\nwith a \"goto state 168\", search until you find the state itself. There you find a description\nof that exact state; how it got there, and what racc considers at that point.\n\nHere is a simple example:\n\n    state 66\n\n       7) syntactic_statements : syntactic_statements syntactic_statement _\n       9) syntactic_statement : syntactic_statement _ COMMA expression\n\n      COMMA         shift, and go to state 68\n      $default      reduce using rule 7 (syntactic_statements)\n\nThe current state is shown with an `_`, thus we are looking at the state where the parser\nhas seen a `syntactic_statement`. We see below, that if it sees a COMMA, it will shift to\nstate 68 (to deal with the expression), and if not, it will reduce rule 7 (it will add one\nsyntactic_statement to the list of syntactic_statements).\n\nIf there is a conflict of a token/rule, it will be listed multiple times in the decision table.\nSay if there was a conflict on the COMMA, it may be shown as:\n\n      COMMA         shift, and go to state 68\n      COMMA         reduce using rule 666 (the_trouble_rule)\n      $default      reduce using rule 7 (syntactic_statements)\n\n\n### How to find where the problem is\n\nEach conflicting token/rule-pair is displayed in the output (as shown above), thus if the\nsame COMMA is involved in 3 conflicts, you will see 6 entries. The valuable piece of information is the name of the reduction rule in conflict. At this point, try to manually construct the sequence of input tokens that would lead up to the ambiguity. \nIt may be that the problem in the grammar is \"before\" reaching the ambiguity\non the COMMA. Once you understand the sequence, you need to apply reasoning to find the resolution\nof the problem.\n\nIf that proves to be hard, and your grammar produces a viable parser, you can build a\ndebugging parser, and turn on debugging output in the runtime. This gives you a trace of\nwhat the parser decides when it parses the input. This is sometimes easier than manually\nfollowing the state changes using only the .output file. Often, you need both, because the trace\nonly tells you which of the alternatives that it took, not what the alternatives were.\n\nAnd yes, this is extremely tedious and time consuming. You will most certainly want to run this\non as little source input as possible to avoid having your head explode.\n\n### Generating a Debugging Parser\n\nTo generate a debugging parser, run the make target `egrammar.debug`. This creates an\neparser.rb (it overwrites the non-debugging variant). (**Do not check in this parser**, it is\nmuch slower than the non debugging variant).\n\n### Turning on Debug output\n\nTo turn on debug output, you need to set an instance variable. You do this in `parser_support.rb`\nin the `_parse()` method. Simply change the line that by default reads:\n\n    @yydebug = false\n    \nto\n\n    @yydebug = true\n    \nAgain, **Do not check in this change**.\n\nNote that the @yydebug=true does nothing unless the parser is build for debugging - i.e. you\ndo not have to change it while you are switching from regular to non debugging version.\n\n### Running with debugging on\n\nWhen you run with debugging turned on, the trace will be printed to stdout, and each\ndecision; reading a token, shifting to another rule/state, and reduction of rules\nis printed out.\n\nArmed with that output and the .output file, you can now manually step through the grammar.\n\n### Limiting the scope\n\nSometimes it is just impossible to figure out what is going wrong in a complex grammar.\nYou can try reducing the grammar by simply commenting out large sections of the grammar. Repeat this\nfor as long as the problem occurs. When you removed the problem, revert that change, then continue elsewhere until you have the smallest possible reproducer\n\nFixing Problems\n---\n\n### Precedence\n\nPrecedence is expressed in a table at the beginning of the grammar. It lists the precedence\nfrom high (at the top) to low (at the bottom), and for each token (real or pseudo token)\nthe associativity (`left`, `right` `nonasoc`) is expressed before a token (or list of tokens).\n\ne.g.\n\n    prechigh\n      left  HIGH\n      nonassoc UMINUS\n      left  TIMES DIV MODULO\n      left  MINUS PLUS\n      right EQUALS\n      left  LOW\n    preclow\n\nThe associativity tells racc how to group input with the same precedence; i.e. should 1 + 2 + 3 be treated as (1 + 2) + 3, or 1 + (2 + 3). A nonassoc means that racc does not allow this multiple\ntimes in a row, e.g. an unary minus can not occur in a sequence and --1 is an error.\n\nThe example above shows two pseudo tokens HIGH and LOW that can be used in the grammar to\nmake a rule have a certain precedence. \n\nWe can now express an otherwise ambiguous grammar like this:\n\n    expr\n      : expr PLUS  expr\n      | expr MINUS expr\n      | expr TIMES expr\n      | expr DIV   expr\n      |      MINUS expr =UMINUS\n      \n### \"Decent Precedence\"\n\nOptionally, we can deal with precedence by grouping the expressions having the same precedence\n\n    expr\n      : mulexp             # to higher precedence\n      | expr PLUS mulexp\n      | expr MINUS mulexp\n      \n    mulexp\n      : primary            # to higher precedence\n      | mulexp TIMES primary\n      | mulexp DIV primary\n\n    primary\n      : NUMBER\n      \nThis has the same effect as setting the precedence and associativity in the precedence\ntable. \n\nI named this \"Decent Precedence\" since this mimics the behavior of a \"recursive decent parser\",\nthe type of parser that is usually written by hand.\n\n### Assigning the precedence\n\nThe precedence of a rule can be assigned like this:\n\n    |  MINUS expression  =UMINUS\n\nThis means that the lexer delivers a MINUS token, and when that is followed by an\nexpression, the result is an UMINUS operation. If we did not assign =UMINUS, the rule\nwould be given the precedence of MINUS.\n\nOther options for fixing problems\n---\n    \n### Creating look ahead / look behind in the lexer\n\nSometimes it is possible to solve an issue by doing a bit more work in the lexer. As an example,\nthe puppet grammar has LBRACK and LISTSTART tokens that are issued for the input '['. The lexer\ncan differentiate between the tokens - a LISTSTART occurs if at the beginning of the input, or after whitespace. This helps making input such as $a[1] and $a [1] non ambiguous before fed to the grammar\n(where it is impossible to differentiate between them due to whitespace tokens not being part of\nthe information sent to the parser). (This is actually an example of \"look behind\").\n\nBeware that any lookahead in the lexer is very expensive since it visits each and every\ncharacter in the source file. Look-behinds are cheap in comparison.\n  \n  \nLiterature\n===\n\nThere is almost no documentation for racc. Luckily, it is a Ruby port of Yacc, and almost everything that is described for Yacc also applies to Racc (with the major exception that Racc uses rules\nwritten in Ruby, and that the runtime methods are slightly different).\n\nThe best book on the topic is \"O'Reilly Yacc & Lex\". If you want to learn more about parsers, see \"Compilers, Principles, Techniques and Tools\" (Aho et.al), also known as 'the dragon book').\n\n\n"
  },
  {
    "path": "docs/profiling.md",
    "content": "# Profiling Puppet\n\nPuppet is a beast. Puppet is at times a very *slow* beast. Maybe we can find\nwhat is making it slow and fix it.\n\n## Coarse Grained Profiling\n\nThere is a built-in system of profiling that can be used to identify some slow\nspots. This can only work with code that is explicitly instrumented, which, at\nthe time of this writing, is primarily the compiler. To enable profiling there\nare several options:\n\n* To profile every request on the master add `--profile` to your master's\n  startup.\n* To profile a single run for an agent add `--profile` to your agent's options\n  for that run.\n* To profile a masterless run add `--profile` to your `puppet apply` options.\n\nThe timing information will be output to the logs and tagged with the word\n\"PROFILE\".\n\nFor the agent there is actually a second system: evaltrace. You can enable this\non the agent by passing it `--evaltrace`. Timing information for each resource\nwill be output to the logs.\n\n## Using a Ruby Profiler\n\nFor much finer grained profiling, you'll want to use\n[ruby-prof](https://rubygems.org/gems/ruby-prof). Once you have the gem\ninstalled you can either modify the code to profile a certain section (using\nRubyProf.profile) or run the master with ruby-prof by adding `use\nRack::RubyProf, :path => '/temp/profile'` to the config.ru for your master.\n\n## Running the Benchmarks\n\nPuppet has a number of benchmark scenarios to pinpoint problems in specific,\nknown, use cases. The benchmark scenarios live in the `benchmarks` directory.\n\nTo run a scenario you do:\n\n    bundle exec rake benchmark:<scenario_name>\n\n## Profiling Benchmarks\n\nYou can also run `heap_dump`, `memory_profile` or `profile` tasks for each\nscenario. You'll first need to run `bundle install --with development` to\ninstall the prerequisite gems.\n\nThe `heap_dump` task generates a heap dump with object allocation\ntracing enabled.\n\nThe `memory_profile` task generates a memory profile listing retained memory\nby file and location.\n\nThe `profile` task generates a calltrace of the benchmark scenario:\n\n    bundle exec rake benchmark:<scenario_name>:profile\n\nThe calltrace file is viewable with\n[kcachegrind](http://kcachegrind.sourceforge.net/html/Home.html).\n"
  },
  {
    "path": "docs/quickstart.md",
    "content": "# Quick Start to Developing on Puppet\n\nBefore diving into the code, you should first take the time to make sure you\nhave an environment where you can run puppet as a developer. In a nutshell you\nneed: the puppet codebase, ruby versions, and dependencies. Once you've got all\nof that in place you can make sure that you have a working development system\nby running the puppet spec tests.\n\n## The Puppet Codebase\n\nIn order to contribute to puppet you'll need to have a GitHub account. Once you\nhave your account, fork the puppetlabs/puppet repo, and clone it onto your\nlocal machine. The [GitHub docs have a good\nexplanation](https://help.github.com/articles/fork-a-repo) of how to do all of\nthis.\n\n## Ruby versions\n\nPuppet needs to work across a variety of ruby versions. Popular ways of making\nsure you have access to the various versions of ruby are to use either\n[rbenv](https://github.com/sstephenson/rbenv) or [rvm](https://rvm.io/). You can\nread up on the linked sites for how to get them installed on your system.\n\n## Dependencies\n\nMake sure you have [bundler](http://bundler.io/) installed. This should be as\nsimple as:\n\n    $ gem install bundler\n\nNow you can get all of the dependencies using:\n\n    $ bundle install --path .bundle\n\nOnce this is done, you can interact with puppet through bundler using `bundle\nexec <command>` which will ensure that `<command>` is executed in the context\nof puppet's dependencies.\n\nFor example to run the specs:\n\n    $ bundle exec rake spec\n\nTo run puppet itself (for a resource lookup say):\n\n    $ bundle exec puppet resource host localhost\n\nTo apply a test manifest:\n\n    $ bundle exec puppet apply -e 'notify { \"hello world\": }'\n\n## Running Spec Tests\n\nPuppet projects use a common convention of using Rake to run unit tests.\nThe tests can be run with the following rake task:\n\n    $ bundle exec rake spec\n\nTo run a single file's worth of tests (much faster!), give the filename:\n\n    $ bundle exec rake spec TEST=spec/unit/file_system_spec.rb\n\nTo run a single test or group of tests, give the filename and line number:\n\n    $ bundle exec rake spec TEST=spec/unit/file_system_spec.rb:42\n\nTo run all tests in parallel, process count is the number of processes to use when running the tests:\n\n    $ bundle exec rake parallel:spec[process_count]\n\nWhen tests fail, it is often useful to capture Puppet's log of a test\nrun. The test harness pays attention to two environment variables that can\nbe used to send logs to a file, and to adjust the log level:\n\n* `PUPPET_TEST_LOG`: when set, must be an absolute path to a file. Puppet's\n  log messages will be sent to that file. Note that the log file will\n  contain lots of spurious warnings `Unable to set ownership of log file`\n  - you can safely ignore them.\n* `PUPPET_TEST_LOG_LEVEL`: change the log level to adjust how much detail\n  is captured. It defaults to `notice`; useful values include `info` and\n  `debug`.\n\n## Running Acceptance Tests\n\nFor details on how to run puppet acceptance tests, please see the [acceptance readme](../acceptance/README.md)"
  },
  {
    "path": "docs/rspec_tutorial.md",
    "content": "# A brief introduction to testing in Puppet\n\nPuppet relies heavily on automated testing to ensure that Puppet behaves as\nexpected and that new features don't interfere with existing behavior. There are\nthree primary sets of tests that Puppet uses: _unit tests_, _integration tests_,\nand _acceptance tests_.\n\n- - -\n\nUnit tests are used to test the individual components of Puppet to ensure that\nthey function as expected in isolation. Unit tests are designed to hide the\nactual system implementations and provide canned information so that only the\nintended behavior is tested, rather than the targeted code and everything else\nconnected to it. Unit tests should never affect the state of the system that's\nrunning the test.\n\n- - -\n\nIntegration tests serve to test different units of code together to ensure that\nthey interact correctly. While individual methods might perform correctly, when\nused with the rest of the system they might fail, so integration tests are a\nhigher level version of unit tests that serve to check the behavior of\nindividual subsystems.\n\nAll of the unit and integration tests for Puppet are kept in the spec/ directory.\n\n- - -\n\nAcceptance tests are used to test high level behaviors of Puppet that deal with\na number of concerns and aren't easily tested with normal unit tests. Acceptance\ntests function by changing system state and checking the system after\nthe fact to make sure that the intended behavior occurred. Because of this\nacceptance tests can be destructive, so the systems being tested should be\nthrowaway systems.\n\nAll of the acceptance tests for Puppet are kept in the acceptance/tests/\ndirectory. Running the acceptance tests is much more involved than running the\nspec tests. Information about how to run them can be found in the [acceptance\ntesting documentation](https://github.com/puppetlabs/puppet/blob/master/acceptance/README.md).\n\n## Testing dependency version requirements\n\nPuppet is only compatible with a specific version of RSpec. If you are not\nusing Bundler to install the required test libraries you must ensure that you\nare using the right library version. Using an unsupported version of RSpec will\nprobably display many spurious failures. The supported version of RSpec can be\nfound in the project Gemfile.\n\n## Puppet Continuous integration\n\n  * GitHub Actions (spec tests only): https://github.com/puppetlabs/puppet/actions\n  * Jenkins (spec and acceptance tests): https://jenkins.puppetlabs.com/view/Puppet%20FOSS/\n\n## RSpec\n\nPuppet uses RSpec to perform unit and integration tests. RSpec handles a number\nof concerns to make testing easier:\n\n  * Executing examples and ensuring the actual behavior matches the expected behavior (examples)\n  * Grouping tests (describe and contexts)\n  * Setting up test environments and cleaning up afterwards (before and after blocks)\n  * Isolating tests (mocks and stubs)\n\n#### Examples and expectations\n\nAt the most basic level, RSpec provides a framework for executing tests (which\nare called examples) and ensuring that the actual behavior matches the expected\nbehavior (which are done with expectations)\n\n```ruby\n# This is an example; it sets the test name and defines the test to run\nspecify \"one equals one\" do\n  # add an expectation that left and right arguments are equal\n  expect(1).to eq(1)\nend\n\n# Examples can be declared with either 'it' or 'specify'\nit \"one doesn't equal two\" do\n  expect(1).to_not eq(2)\nend\n```\n\nGood examples generally do as little setup as possible and only test one or two\nthings; it makes tests easier to understand and easier to debug.\n\nMore complete documentation on expectations is available at https://www.relishapp.com/rspec/rspec-expectations/docs\n\nNote Puppet supports the [RSpec 3](http://rspec.info/blog/2013/07/the-plan-for-rspec-3/)\nAPI, so please do not use RSpec 2 \"should\" syntax like `1.should == 1`.\n\n### Example groups\n\nExample groups are fairly self explanatory; they group similar examples into a\nset.\n\n```ruby\ndescribe \"the number one\" do\n\n  it \"is larger than zero\" do\n    expect(1).to be > 0\n  end\n\n  it \"is an odd number\" do\n    expect(1).to be_odd # calls 1.odd?\n  end\n\n  it \"is not nil\" do\n    expect(1).to be\n  end\nend\n```\n\nExample groups have a number of uses that we'll get into later, but one of the\nsimplest demonstrations of what they do is how they help to format\ndocumentation:\n\n```\nrspec ex.rb --format documentation\n\nthe number one\n  is larger than zero\n  is an odd number\n  is not nil\n\nFinished in 0.00516 seconds\n3 examples, 0 failures\n```\n\n### Setting up and tearing down tests\n\nExamples may require some setup before they can run, and might need to clean up\nafterwards. `before` and `after` blocks can be used before this, and can be\nused inside of example groups to limit how many examples they affect.\n\n```ruby\n\ndescribe \"something that could warn\" do\n  before :each do\n    # Disable warnings for this test\n    $VERBOSE = nil\n  end\n\n  after :each do\n    # Enable warnings afterwards\n    $VERBOSE = true\n  end\n\n  it \"doesn't generate a warning\" do\n    MY_CONSTANT = 1\n    # reassigning a constant normally prints out 'warning: already initialized constant FOO'\n    MY_CONSTANT = 2\n  end\nend\n```\n\n### Setting up helper data\n\nSome examples may require setting up data before hand and making it available to\ntests. RSpec provides helper methods with the `let` method call that can be used\ninside of tests.\n\n```ruby\ndescribe \"a helper object\" do\n  # This creates an array with three elements that we can retrieve in tests. A\n  # new copy will be made for each test.\n  let(:my_helper) do\n    ['foo', 'bar', 'baz']\n  end\n\n  it \"is an array\" do\n    expect(my_helper).to be_a_kind_of Array\n  end\n\n  it \"has three elements\" do\n    expect(my_helper.size).to eq(3)\n  end\nend\n```\n\nLike `before` blocks, helper objects like this are used to avoid doing a lot of\nsetup in individual examples and share setup between similar tests.\n\n### Isolating tests with stubs\n\nRSpec allows you to provide fake data during testing to make sure that\nindividual tests are only running the code being tested. You can stub out entire\nobjects, or just stub out individual methods on an object. When a method is\nstubbed the method itself will never be called.\n\nThe RSpec Mocks documentation can be found at https://relishapp.com/rspec/rspec-mocks/v/3-8/docs/\n\n```ruby\ndescribe \"stubbing a method on an object\" do\n  let(:my_helper) do\n    ['foo', 'bar', 'baz']\n  end\n\n  it 'has three items before being stubbed' do\n    expect(my_helper.size).to eq(3)\n  end\n\n  describe 'when stubbing the size' do\n    before :each do\n      allow(my_helper).to receive(:size).and_return(10)\n    end\n\n    it 'has the stubbed value for size' do\n      expect(my_helper.size).to eq(10)\n    end\n  end\nend\n```\n\nEntire objects can be stubbed as well.\n\n```ruby\ndescribe \"stubbing an object\" do\n  let(:my_helper) do\n    double(:not_an_array, :size => 10)\n  end\n\n  it 'has the stubbed size'\n    expect(my_helper.size).to eq(10)\n  end\nend\n```\n\n### Adding expectations with mocks\n\nIt's possible to combine the concepts of stubbing and expectations so that a\nmethod has to be called for the test to pass (like an expectation), and can\nreturn a fixed value (like a stub).\n\n```ruby\ndescribe \"mocking a method on an object\" do\n  let(:my_helper) do\n    ['foo', 'bar', 'baz']\n  end\n\n  describe \"when mocking the size\" do\n    before :each do\n      expect(my_helper).to receive(:size).and_return(10)\n    end\n\n    it \"adds an expectation that a method was called\" do\n      my_helper.size\n    end\n  end\nend\n```\n\nLike stubs, entire objects can be mocked.\n\n```ruby\ndescribe \"mocking an object\" do\n  let(:my_helper) do\n    double(:not_an_array)\n  end\n\n  before :each do\n    expect(not_an_array).to receive(:size).and_return(10)\n  end\n\n  it \"adds an expectation that the method was called\" do\n    not_an_array.size\n  end\nend\n```\n### Writing tests without side effects\n\nWhen properly written each test should be able to run in isolation, and tests\nshould be able to be run in any order. This makes tests more reliable and allows\na single test to be run if only that test is failing, instead of running all\n17000+ tests each time something is changed. However, there are a number of ways\nthat can make tests fail when run in isolation or out of order.\n\n#### Narrowing down a spec test with side effects\n\nIf you do have a test that passes in isolation but fails when run as part of\na full spec run, you can often narrow down the culprit by a two-step process.\nFirst, run:\n\n```\nbundle exec rake spec\n```\n\nwhich should generate a spec_order.txt file.\n\nSecond, run:\n\n```\nutil/binary_search_specs.rb <full path to failing spec>\n```\n\nAnd it will (usually) tell you the test that makes the failing spec fail.\n\nThe 'usually' caveat is because there can be spec failures that require\nspecific ordering between > 2 spec files, and this tool only handles the\ncase for 2 spec files. The > 2 case is rare and if you suspect you're in\nthat boat, there isn't an established best practice, other than to ask\nfor help on IRC or the mailing list.\n\n#### Using instance variables\n\nPuppet has a number of older tests that use `before` blocks and instance\nvariables to set up fixture data, instead of `let` blocks. These can retain\nstate between tests, which can lead to test failures when tests are run out of\norder.\n\n```ruby\n# test.rb\nRSpec.configure do |c|\n  c.mock_with :rspec\nend\n\ndescribe \"fixture data\" do\n  describe \"using instance variables\" do\n\n    # BAD\n    before :all do\n      # This fixture will be created only once and will retain the `foo` stub\n      # between tests.\n      @fixture = double('test data')\n    end\n\n    it \"can be stubbed\" do\n      allow(@fixture).to receive(:foo).and_return(:bar)\n      expect(@fixture.foo).to eq(:bar)\n    end\n\n    it \"does not keep state between tests\" do\n      # The foo stub was added in the previous test and shouldn't be present\n      # in this test.\n      expect { @fixture.foo }.to raise_error\n    end\n  end\n\n  describe \"using `let` blocks\" do\n\n    # GOOD\n    # This will be recreated between tests so that state isn't retained.\n    let(:fixture) { double('test data') }\n\n    it \"can be stubbed\" do\n      allow(fixture).to receive(:foo).and_return(:bar)\n      expect(fixture.foo).to eq(:bar)\n    end\n\n    it \"does not keep state between tests\" do\n      # since let blocks are regenerated between tests, the foo stub added in\n      # the previous test will not be present here.\n      expect { fixture.foo }.to raise_error\n    end\n  end\nend\n```\n\n```\nbundle exec rspec test.rb -fd\n\nfixture data\n  using instance variables\n    can be stubbed\n    should not keep state between tests (FAILED - 1)\n  using `let` blocks\n    can be stubbed\n    should not keep state between tests\n\nFailures:\n\n  1) fixture data using instance variables should not keep state between tests\n     Failure/Error: expect { @fixture.foo }.to raise_error\n       expected Exception but nothing was raised\n     # ./test.rb:17:in `block (3 levels) in <top (required)>'\n\nFinished in 0.00248 seconds\n4 examples, 1 failure\n\nFailed examples:\n\nrspec ./test.rb:16 # fixture data using instance variables should not keep state between tests\n```\n\n### RSpec references\n\n  * RSpec core docs: https://www.relishapp.com/rspec/rspec-core/docs\n  * RSpec guidelines with Ruby: http://betterspecs.org/\n\n"
  },
  {
    "path": "docs/settings.md",
    "content": "Settings\n========\n\n<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->\n**Table of Contents**\n\n- [Definitions](#definitions)\n- [Sections](#sections)\n- [Initialization](#initialization)\n- [Dashes & Underscores](#dashes--underscores)\n- [Setting Types](#setting-types)\n- [Setting Values](#setting-values)\n    - [Duration & TTL Settings](#duration--ttl-settings)\n    - [File/Directory Settings](#filedirectory-settings)\n- [Hooks](#hooks)\n- [Run Mode](#run-mode)\n    - [Preferred Run Mode](#preferred-run-mode)\n- [Precedence](#precedence)\n- [File Watcher](#file-watcher)\n- [RSpec](#rspec)\n\n<!-- markdown-toc end -->\n\nPuppet can be configured via settings. All settings are defined in\n`lib/puppet/defaults.rb`. All settings have a type, value, description, etc.\nSettings can come from multiple sources such as the command line, configuration\nfile, programmatically, etc, and are looked up in a specific order, so that the\ncommand line takes precedence over what's specified in puppet.conf.\n\nPuppet settings can be looked up using `Puppet[:name]` and set using\n`Puppet[:name] = 'value'`.\n\n# Definitions\n\nSettings are defined using `Puppet::Settings#define_settings`. The method takes\na section name, setting name and a hash describing the setting, e.g. its type,\ndescription, etc.\n\n# Sections\n\nPuppet settings can be specified in INI file format based on a section, for example:\n\n```inifile\n[main]\nstrict=true\n```\n\nComments and whitespace are ignored. A setting may be configured in any section,\neven if it wasn't defined in that section. For example, the `strict` setting is\ndefined in `main`:\n\n```ruby\nsettings.define_setting(:main, strict: { ... })\n```\n\nBut it can be configured in any section, so this is legal:\n\n```inifile\n[server]\nstrict = true\n```\n\nThe purpose of the section name is when applying a settings catalog, see\n'File/Directory Settings' below.\n\nPuppet predefines section names like `main`, `user`, `agent` and `server`. Only\nthese sections are allowed in `puppet.conf`.\n\n# Initialization\n\nThe entry point for initializing settings is `Puppet.initialize_settings`. It is\npossible to pass in command line arguments, as well as inject dependencies, such\nas an alternate facter implementation.\n\nPuppet initializes its settings in three phases: global options, loading its\n`puppet.conf`, and application options.\n\nFirst, puppet parses command line arguments using our vendored trollop library.\nAny argument with the same name as a puppet setting is automatically set. The\nargument and its optional value are \"consumed\" and unknown arguments are\nignored. Puppet handles boolean arguments specially, so it's possible to pass\n`--onetime` or `--no-onetime`, and puppet will set the value to `true` or\n`false`, respectively.\n\nSecond, puppet loads `puppet.conf` from a predefined location depending on\nwhether it's running privileged or not. See\n[https://github.com/puppetlabs/puppet-specifications/blob/master/file_paths.md](https://github.com/puppetlabs/puppet-specifications/blob/master/file_paths.md\n). Assuming puppet is running an application like `puppet agent`, then\nthe application parses unconsumed arguments using Ruby's builtin\n`OptionParser`.\n\nThird, the application parses any application-specific options using the same\n`OptionParser` instance from above. If the application defines an option with\nthe same name as a setting, the application's option handler will be called\nlast, so it \"wins\".\n\n# Dashes & Underscores\n\nPuppet settings are always defined using underscores, but application options\nshould always be defined using dashes, e.g. `option(\"--job-id ID\")`\n\nAs long as you're using Ruby 2.5 or above, `OptionParser` will automatically\nconvert underscores to dashes, so your option handler will always be called\neven if the setting is specified using underscores in `puppet.conf` or on the\ncommand line.\n\n# Setting Types\n\nBy default, settings are assumed to contain a string value. It is possible to\nspecify another type when the setting is defined, such as `:type => :integer`. Each\ntype maps to a subclass of `Puppet::Settings::BaseSetting`. In general, try to\nreuse an existing type instead of creating one subclass for every setting.\n\nWhen creating a new setting type, you may need to implement the `munge` method\nto convert the external representation (the string \"42\") to its internal\nrepresentation (the integer 42).\n\nYou may also want to implement the `print` method, which is invoked when running\n`puppet config print <name>`.\n\n# Setting Values\n\nPuppet defines several \"root\" settings that must be defined, such as `confdir`.\nThese settings default to directories based on whether puppet is running as a\nprivileged user and is running on Windows or not.\n\nNon-root settings may be defined in terms of other settings. For example, the\n`ssldir` setting's value is defined to be `\"$confdir/ssl\"`. So in order to\nresolve the value of the `ssldir`, puppet will recursively resolve `confdir`.\nPuppet supports multiple levels of recursion, but will raise if it detects a\ncycle.\n\n## Duration & TTL Settings\n\nPuppet's duration and ttl-based settings assume the value is specified in\nseconds unless units are specified, such as `5m`, `1h`, etc.\n\n## File/Directory Settings\n\nThe `file` and `directory` settings are handled specially, because puppet will\ncompile an internal \"settings\" catalog and apply it, to ensure they match the\ndesired state. So whenever `Puppet.settings.use(:main, etc)` is called, then all\nfile and directory-based settings in the `main`, etc sections will be added to\nthe settings catalog.\n\nIt is possible to specify the `owner` and/or `group` for these types of\nsettings. The special `service` account means use whatever user/group puppet is\nconfigured to run under, as specified as `Puppet[:user]`/`Puppet[:group]`. For\nexample, when puppet is a library within puppetserver, `Puppet[:user]` is set\nto the `puppet` user. This way puppetserver, not running as root, can access\nfiles that puppet creates.\n\nIt is also possible for a user to specify `owner`, `group` or `mode` metadata in\n`puppet.conf` by appending a hash after the value:\n\n```inifile\nssldir = \"$confdir/ssl\" { owner=root,group=root,mode=0750 }\n```\n\nSee also the `settings_catalog` and `manage_internal_file_permissions` settings,\nwhich can disable these behaviors.\n\n# Hooks\n\nIt is possible to add a hook to a setting. The hook will be called at various\ntimes whenever the value is set. The hook may be called multiple times. Hook\nbehavior is confusing and surprising! If you must define a new hook, use\n`on_initialize_and_write`. The other types of hooks won't be called if the\nsetting is defined in a section that doesn't match the current run_mode.\n\nIf a setting's default value interpolates another base setting, then the hook\nwill **not** be called if the base setting changes. So try to avoid mixing hooks\nand interpolated default values.\n\n# Run Mode\n\nPuppet can be configured to run in different \"modes\". The default run mode is\n`:user`, but can be switched to `:agent` or `:server`. If the run mode is\nswitched, then it changes how settings are resolved. For example, given\n`puppet.conf` containing:\n\n```inifile\n[server]\nnode_terminus=exec\n```\nThen calling `Puppet[:node_terminus]` will return either `nil` or `exec`\ndepending on the current run mode.\n\n## Preferred Run Mode\n\nSettings and run mode have a circular dependency. We need to know the run mode\nin order to load settings. However, puppet applications are defined in modules.\nSo we need to resolve the `modulepath` setting to find the application, and then\nthe application can change the run mode.\n\nTo break the dependency, puppet's preferred run mode is the mode it initially\nstarts in, though it may change later on.\n\n# Precedence\n\nPuppet settings can be defined in multiple sources (command line, puppet.conf,\netc). When looking up a value, puppet searches based on the precedence of each\nsource, roughly in order of high to low:\n\n* memory\n* command line\n* current environment.conf\n* section for the current run mode\n* main section\n* defaults\n\nIt is important to note that both the current environment and run mode change\nhow the value is resolved.\n\n# File Watcher\n\nWhen running as a daemon, puppet will watch its `puppet.conf` and reload its\nconfiguration if it changes.\n\n# RSpec\n\nTo avoid order-dependent test failures, puppet's rspec tests create unique\n\"root\" directories for each rspec example. For example, you can safely mutate\nsettings in a test `Puppet[:strict] = true` or modify the contents of the\n`confdir` without affecting other tests.\n"
  },
  {
    "path": "docs/unicode.md",
    "content": "# UTF-8 Handling #\n\nNow that Puppet only supports Ruby 1.9+, developers should be aware\nof how Ruby handles Strings and Regexp objects. Specifically, every \ninstance of these two classes will have an encoding attribute determined\nin a number of ways.\n\n * If the source file has an encoding specified in the magic comment at the\n   top, the instance will take on that encoding.\n * Otherwise, the encoding will be determined by the LC\\_LANG or LANG\n   environment variables.\n * Otherwise, the encoding will default to ASCII-8BIT\n\n## Encodings of Regexp and String instances ##\n\nIn general, please be aware that Ruby regular expressions need to be\ncompatible with the encoding of a string being used to match them.  If they are\nnot compatible you can expect to receive an error such as:\n\n    Encoding::CompatibilityError: incompatible encoding regexp match (ASCII-8BIT\n    regexp with UTF-8 string)\n\nIn addition, some escape sequences are only valid if the regular expression is\nmarked as an ASCII-8BIT object. If the regular expression is not marked as\nASCII-8BIT, you can get an error such as:\n\n    SyntaxError: (irb):7: invalid multibyte escape: /\\xFF/\n\nThis error is particularly common when serializing a string to other\nrepresentations like JSON or YAML.  To resolve the problem you can explicitly\nmark the regular expression as ASCII-8BIT using the /n flag:\n\n    \"a\" =~ /\\342\\230\\203/n\n\nFinally, any time you're thinking of a string as an array of bytes rather than\nan array of characters, common when escaping a string, you should work with\neverything in ASCII-8BIT.  Changing the encoding will not change the data\nitself and allow the Regexp and the String to deal with bytes rather than\ncharacters.\n"
  },
  {
    "path": "docs/windows.md",
    "content": "# Windows\n\nIf you'd like to run Puppet from source on Windows platforms, follow the [Quickstart](./quickstart.md) to using bundler and installing the necessary gems on Windows.\n\nYou will need to install Ruby on Windows from [rubyinstaller.org](http://rubyinstaller.org)\nor from [Choclately](https://chocolatey.org/packages/ruby).\n\n    C:\\> cd C:\\work\\puppet\n    C:\\work\\puppet> gem install bundler\n    C:\\work\\puppet> bundle install --path .bundle\n    C:\\work\\puppet> bundle exec puppet --version\n    4.7.1\n\nWhen writing a test that cannot possibly run on Windows, e.g. there is\nno mount type on windows, do the following:\n\n    describe Puppet::MyClass, :unless => Puppet::Util::Platform.windows? do\n      ..\n    end\n\nIf the test doesn't currently pass on Windows, e.g. due to on going porting, then use an rspec conditional pending block:\n\n    pending(\"porting to Windows\", :if => Puppet::Util::Platform.windows?) do\n      <example1>\n    end\n\n    pending(\"porting to Windows\", :if => Puppet::Util::Platform.windows?) do\n      <example2>\n    end\n\nThen run the test as:\n\n    C:\\work\\puppet> bundle exec rspec spec/path/to/test.rb\n\n## Common Issues ##\n\n * Don't assume file paths start with '/', as that is not a valid path on\n   Windows.  Use Puppet::Util.absolute\\_path? to validate that a path is fully\n   qualified.\n\n * Use File.expand\\_path('/tmp') in tests to generate a fully qualified path\n   that is valid on POSIX and Windows.  In the latter case, the current working\n   directory will be used to expand the path.\n\n * Always use binary mode when performing file I/O, unless you explicitly want\n   Ruby to translate between unix and dos line endings.  For example, opening an\n   executable file in text mode will almost certainly corrupt the resulting\n   stream, as will occur when using:\n\n     IO.open(path, 'r') { |f| ... }\n     IO.read(path)\n\n   If in doubt, specify binary mode explicitly:\n\n     IO.open(path, 'rb')\n\n * Don't assume file paths are separated by ':'.  Use `File::PATH_SEPARATOR`\n   instead, which is ':' on POSIX and ';' on Windows.\n\n * On Windows, `File::SEPARATOR` is '/', and `File::ALT_SEPARATOR` is '\\'.  On\n   POSIX systems, `File::ALT_SEPARATOR` is nil.  In general, use '/' as the\n   separator as most Windows APIs, e.g. CreateFile, accept both types of\n   separators.\n\n * Don't use waitpid/waitpid2 if you need the child process' exit code,\n   as the child process may exit before it has a chance to open the\n   child's HANDLE and retrieve its exit code.  Use Puppet::Util::Execution.execute.\n\n * Don't assume 'C' drive.  Use environment variables to look these up:\n\n    \"#{ENV['windir']}/system32/netsh.exe\"\n"
  },
  {
    "path": "examples/enc/regexp_nodes/classes/databases",
    "content": "db\\d{2}\nmysql\n"
  },
  {
    "path": "examples/enc/regexp_nodes/classes/webservers",
    "content": "^(web|www)\nleterel\n"
  },
  {
    "path": "examples/enc/regexp_nodes/environment/development",
    "content": "^dev-\nprod-canary\n"
  },
  {
    "path": "examples/enc/regexp_nodes/parameters/service/prod",
    "content": "\\d{3}$\n"
  },
  {
    "path": "examples/enc/regexp_nodes/parameters/service/qa",
    "content": "^qa-\n^qa2-\n^qa3-\n"
  },
  {
    "path": "examples/enc/regexp_nodes/parameters/service/sandbox",
    "content": "^dev-\n"
  },
  {
    "path": "examples/enc/regexp_nodes/regexp_nodes.rb",
    "content": "#!/usr/bin/env ruby\n\n# = Synopsis\n# This is an external node classifier script, after\n# https://puppet.com/docs/puppet/latest/lang_write_functions_in_puppet.html\n#\n# = Usage\n# regexp_nodes.rb <host>\n#\n# = Description\n# This classifier implements filesystem autoloading: It looks through classes,\n# parameters, and environment subdirectories, looping through each file it\n# finds.  Each file's contents are a regexp-per-line which, if they match the\n# hostname passed to the program as ARGV[0], sets a class, parameter value\n# or environment named the same thing as the file itself. At the end, the\n# resultant data structure is returned back to the puppet master process as\n# yaml.\n#\n# = Caveats\n# Since the files are read in directory order, multiple matches for a given\n# hostname in the parameters/ and environment/ subdirectories will return the\n# last-read value.  (Multiple classes/ matches don't cause a problem; the\n# class is either incuded or it isn't)\n#\n# Unmatched hostnames in any of the environment/ files will cause 'production'\n# to be emitted; be aware of the complexity surrounding the interaction between\n# ENC and environments as discussed in https://projects.puppetlabs.com/issues/3910\n#\n# = Examples\n# Based on the example files in the classes/ and parameters/ subdirectories\n# in the distribution, classes/database will set the 'database' class for\n# hosts matching %r{db\\d{2}} (that is, 'db' followed by two digits) or with\n# 'mysql' anywhere in the hostname.  Similarly, hosts beginning with 'www' or\n# 'web' or the hostname 'leterel' (my workstation) will be assigned the\n# 'webserver' class.\n#\n# Under parameters/ there is one subdirectory 'service' which\n# sets the a parameter called 'service' to the value 'prod' for production\n# hosts (whose hostnames always end with a three-digit code), 'qa' for\n# anything that starts with 'qa-' 'qa2-' or 'qa3-', and 'sandbox' for any\n# development machines whose hostnames start with 'dev-'.\n#\n# In the environment/ subdirectory, any hosts matching '^dev-' and a single\n# production host which serves as 'canary in the coal mine' will be put into\n# the development environment\n#\n# = Author\n# Eric Sorenson <eric@explosive.net>\n\n# we need yaml or there's not much point in going on\nrequire 'yaml'\n\n# Sets are like arrays but automatically de-duplicate elements\nrequire 'set'\n\n# set up some syslog logging\nrequire 'syslog'\nSyslog.open('extnodes', Syslog::LOG_PID | Syslog::LOG_NDELAY, Syslog::LOG_DAEMON)\n# change this to LOG_UPTO(Sysslog::LOG_DEBUG) if you want to see everything\n# but remember your syslog.conf needs to match this or messages will be filtered\nSyslog.mask = Syslog::LOG_UPTO(Syslog::LOG_INFO)\n\n# Helper method to log to syslog; we log at level debug if no level is specified\n# since those are the most frequent calls to this method\ndef log(message,level=:debug)\n  Syslog.send(level,message)\nend\n\n\n# set our workingdir to be the directory we're executed from, regardless\n# of parent's cwd, symlinks, etc. via handy Pathname.realpath method\nrequire 'pathname'\np = Pathname.new(File.dirname(__FILE__))\nWORKINGDIR = \"#{p.realpath}\"\n\n# This class holds all the methods for creating and accessing the properties\n# of an external node. There are really only two public methods: initialize\n# and a special version of to_yaml\n\nclass ExternalNode\n  # Make these instance variables get/set-able with eponymous methods\n  attr_accessor :classes, :parameters, :environment, :hostname\n\n  # initialize takes three arguments:\n  # hostname:: usually passed in via ARGV[0] but it could be anything\n  # classdir:: directory under WORKINGDIR to look for files named after\n  # classes\n  # parameterdir:: directory under WORKINGDIR to look for directories to set\n  # parameters\n  def initialize(hostname, classdir = 'classes/', parameterdir = 'parameters/', environmentdir = 'environment/')\n    # instance variables that contain the lists of classes and parameters\n    @classes = Set.new\n    @parameters = Hash.new(\"unknown\")  # sets a default value of \"unknown\"\n    @environment = \"production\"\n\n    self.parse_argv(hostname)\n    self.match_classes(\"#{WORKINGDIR}/#{classdir}\")\n    self.match_parameters(\"#{WORKINGDIR}/#{parameterdir}\")\n    self.match_environment(\"#{WORKINGDIR}/#{environmentdir}\")\n  end\n\n  # private method called by initialize which sanity-checks our hostname.\n  # good candidate for overriding in a subclass if you need different checks\n  def parse_argv(hostname)\n    if hostname =~ /^([-\\w]+?)\\.([-\\w\\.]+)/  # non-greedy up to the first . is hostname\n      @hostname = $1\n    elsif hostname =~ /^([-\\w]+)$/     # sometimes puppet's @name is just a name\n      @hostname = hostname\n      log(\"got shortname for [#{hostname}]\")\n    else\n      log(\"didn't receive parsable hostname, got: [#{hostname}]\",:err)\n      exit(1)\n    end\n  end\n\n  # to_yaml massages a copy of the object and outputs clean yaml so we don't\n  # feed weird things back to puppet []<\n  def to_yaml\n    classes = self.classes.to_a\n    if self.parameters.empty? # otherwise to_yaml prints \"parameters: {}\"\n      parameters = nil\n    else\n      parameters = self.parameters\n    end\n    ({ 'classes' => classes, 'parameters' => parameters, 'environment' => environment}).to_yaml\n  end\n\n  # Private method that expects an absolute path to a file and a string to\n  # match - it returns true if the string was matched by any of the lines in\n  # the file\n  def matched_in_patternfile?(filepath, matchthis)\n\n    patternlist = []\n\n    begin\n      File.open(filepath).each do |l|\n        l.chomp!\n\n        next if l =~ /^$/\n        next if l =~ /^#/\n\n        if l =~ /^\\s*(\\S+)/\n          m = Regexp.last_match\n          log(\"found a non-comment line, transforming [#{l}] into [#{m[1]}]\")\n          l.gsub!(l,m[1])\n        else\n          next l\n        end\n\n        pattern = %r{#{l}}\n        patternlist <<  pattern\n        log(\"appending [#{pattern}] to patternlist for [#{filepath}]\")\n      end\n    rescue StandardError\n      log(\"Problem reading #{filepath}: #{$!}\",:err)\n      exit(1)\n    end\n\n    log(\"list of patterns for #{filepath}: #{patternlist}\")\n\n    if matchthis =~ Regexp.union(patternlist)\n      log(\"matched #{$~} in #{matchthis}, returning true\")\n      return true\n\n    else  # hostname didn't match anything in patternlist\n      log(\"#{matchthis} unmatched, returning false\")\n      return nil\n    end\n\n  end # def\n\n  # private method - takes a path to look for files, iterates through all\n  # readable, regular files it finds, and matches this instance's @hostname\n  # against each line; if any match, the class will be set for this node.\n  def match_classes(fullpath)\n    Dir.foreach(fullpath) do |patternfile|\n      filepath = \"#{fullpath}/#{patternfile}\"\n      next unless File.file?(filepath) and\n        File.readable?(filepath)\n        next if patternfile =~ /^\\./\n      log(\"Attempting to match [#{@hostname}] in [#{filepath}]\")\n      if matched_in_patternfile?(filepath,@hostname)\n        @classes << patternfile.to_s\n        log(\"Appended #{patternfile} to classes instance variable\")\n      end\n    end\n  end\n\n  # match_environment is similar to match_classes but it overwrites\n  # any previously set value (usually just the default, 'production')\n  # with a match\n  def match_environment(fullpath)\n    Dir.foreach(fullpath) do |patternfile|\n      filepath = \"#{fullpath}/#{patternfile}\"\n      next unless File.file?(filepath) and\n        File.readable?(filepath)\n        next if patternfile =~ /^\\./\n      log(\"Attempting to match [#{@hostname}] in [#{filepath}]\")\n      if matched_in_patternfile?(filepath,@hostname)\n        @environment = patternfile.to_s\n        log(\"Wrote #{patternfile} to environment instance variable\")\n      end\n    end\n  end\n\n\n  # Parameters are handled slightly differently; we make another level of\n  # directories to get the parameter name, then use the names of the files\n  # contained in there for the values of those parameters.\n  #\n  # ex: cat ./parameters/service/production\n  # ^prodweb\n  # would set parameters[\"service\"] = \"production\" for prodweb001\n  def match_parameters(fullpath)\n    Dir.foreach(fullpath) do |parametername|\n\n      filepath = \"#{fullpath}/#{parametername}\"\n      next if File.basename(filepath) =~ /^\\./   # skip over dotfiles\n\n      next unless File.directory?(filepath) and\n        File.readable?(filepath)        # skip over non-directories\n\n      log(\"Considering contents of #{filepath}\")\n\n      Dir.foreach(\"#{filepath}\") do |patternfile|\n        secondlevel = \"#{filepath}/#{patternfile}\"\n        log(\"Found parameters patternfile at #{secondlevel}\")\n        next unless File.file?(secondlevel) and\n          File.readable?(secondlevel)\n        log(\"Attempting to match [#{@hostname}] in [#{secondlevel}]\")\n        if matched_in_patternfile?(secondlevel, @hostname)\n          @parameters[ parametername.to_s ] = patternfile.to_s\n          log(\"Set @parameters[#{parametername}] = #{patternfile}\")\n        end\n      end\n    end\n  end\n\nend\n\n# Logic for local hacks that don't fit neatly into the autoloading model can\n# happen as we initialize a subclass\nclass MyExternalNode < ExternalNode\n\n  def initialize(hostname, classdir = 'classes/', parameterdir = 'parameters/')\n\n    super\n\n    # Set \"hostclass\" parameter based on hostname,\n    # stripped of leading environment prefix and numeric suffix\n    if @hostname =~ /^(\\w*?)-?(\\D+)(\\d{2,3})$/\n      match = Regexp.last_match\n\n      hostclass = match[2]\n      log(\"matched hostclass #{hostclass}\")\n      @parameters[ \"hostclass\" ] = hostclass\n    else\n      log(\"couldn't figure out class from #{@hostname}\",:warning)\n\n    end\n  end\n\nend\n\n\n# Here we begin actual execution by calling methods defined above\n\nmynode = MyExternalNode.new(ARGV[0])\n\nputs mynode.to_yaml\n"
  },
  {
    "path": "examples/hiera/README.md",
    "content": "A working demo of Hiera with YAML backend.\n======================================================\n\nThis demo consists of:\n\n- A **NTP** module that has defaults for *pool.ntp.org* servers\n- A **YAML** data source in the *data/* directory where users can override data in yaml files\n- A **Users** module that has a few manifests that simply notify that they are being included\n- In Hiera data files a key called **classes** that decides what to include on a node\n\nBelow various usage scenarios can be tested using this module.\n\nThe examples below assume you:\n- Have the puppet-agent already installed\n- You have this repository cloned from github\n- Are running these commands from within the *examples/hiera* directory as cwd.\n\nModule from forge with module defaults\n--------------------------------------\n\n- Comment out lines 6-8 of [data/common.yaml](data/common.yaml#L6-8) to avoid overrides used further in the example\n- Run a `puppet apply` to create a */tmp/ntp.conf* file containing the two *pool.ntp.org* addresses\n- The *users::common* class should also be present in your catalog\n\n```shell\n$ sed -i '6,8 s/^/#/' data/common.yaml\n$ puppet apply site.pp --hiera_config=hiera.yaml --modulepath=modules\nNotice: Compiled catalog for node.corp.com in environment production in 0.04 seconds\nNotice: Adding users::common\nNotice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'\nNotice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/ensure: defined content as '{sha256}949c7247dbe0870258c921418cc8b270afcc57e1aa6f9d9933f306009ede60d0'\nNotice: Applied catalog in 0.02 seconds\n$ cat /tmp/ntp.conf\nserver 1.pool.ntp.org\nserver 2.pool.ntp.org\n```\n\nSite wide override data in _data::common_\n-----------------------------------------\n\n- Remove the comments on lines 6-8 of [data/common.yaml](data/common.yaml#L6-8)\n- Run a `puppet apply` to update */tmp/ntp.conf* to contain the two *ntp.example.com* addresses\n- The *users::common* class should also be present in your catalog\n\n```shell\n$ sed -i '6,8 s/^#//' data/common.yaml\n$ puppet apply site.pp --hiera_config=hiera.yaml --modulepath=modules\nNotice: Compiled catalog for node.corp.com in environment production in 0.04 seconds\nNotice: Adding users::common\nNotice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'\nNotice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/content: content changed '{sha256}949c7247dbe0870258c921418cc8b270afcc57e1aa6f9d9933f306009ede60d0' to '{sha256}28ced955a8ed9efd7514b2364fe378ba645ab947f26e8c0b4d84e8368f1257a0'\nNotice: Applied catalog in 0.02 seconds\n$ cat /tmp/ntp.conf\nserver ntp1.example.com\nserver ntp2.example.com\n```\n\nFact driven overrides for location=dc1\n--------------------------------------\n\n- Override the location fact to `dc1` to demonstrate *data/dc1.yaml* overrides the *ntp::config::ntpservers* values in *data/common.yaml*\n- `dc1` nodes will\n  - have the *users::common* and *users::dc1* in their catalogs\n  - */tmp/ntp.conf* will contain the two *ntp.dc1.example.com* addresses\n- Show that the nodes in `dc2` would use the site-wide defaults\n\n```shell\n$ FACTER_location=dc1 puppet apply site.pp --hiera_config=hiera.yaml --modulepath=modules\nNotice: Compiled catalog for node.corp.com in environment production in 0.04 seconds\nNotice: Adding users::dc1\nNotice: /Stage[main]/Users::Dc1/Notify[Adding users::dc1]/message: defined 'message' as 'Adding users::dc1'\nNotice: Adding users::common\nNotice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'\nNotice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/content: content changed '{sha256}28ced955a8ed9efd7514b2364fe378ba645ab947f26e8c0b4d84e8368f1257a0' to '{sha256}39227f1cf8d09623d2e66b6622af2e8db01ab26f77a5a2e6d6e058d0977f369b'\nNotice: Applied catalog in 0.02 seconds\n$ cat /tmp/ntp.conf\nserver ntp1.dc1.example.com\nserver ntp2.dc1.example.com\n```\n\nNow simulate a machine in `dc2`, because there is no data for `dc2` it uses the site wide defaults and\ndoes not include the *users::dc1* class anymore\n\n```shell\n$ FACTER_location=dc2 puppet apply site.pp --hiera_config=hiera.yaml --modulepath=modules\nNotice: Compiled catalog for node.corp.com in environment production in 0.04 seconds\nNotice: Adding users::common\nNotice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'\nNotice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/content: content changed '{sha256}39227f1cf8d09623d2e66b6622af2e8db01ab26f77a5a2e6d6e058d0977f369b' to '{sha256}28ced955a8ed9efd7514b2364fe378ba645ab947f26e8c0b4d84e8368f1257a0'\nNotice: Applied catalog in 0.02 seconds\n$ cat /tmp/ntp.conf\nserver ntp1.example.com\nserver ntp2.example.com\n```\n\nYou could create override data in the following places for a machine in *location=dc2*, they will be searched in this order and the first one with data will match.\n\n- file data/dc2.yaml\n- file data/&lt;environment&gt;.yaml\n- file data/common.yaml\n\nIn this example due to the presence of *common.yaml* that declares *ntpservers* the classes will never be searched, it will have precedence.\n"
  },
  {
    "path": "examples/hiera/data/common.yaml",
    "content": "---\nclasses:\n  - users::common\n  - ntp::config\n\nntp::config::ntpservers:\n  - 'ntp1.example.com'\n  - 'ntp2.example.com'\n\nlookup_options:\n  classes:\n    merge: unique\n"
  },
  {
    "path": "examples/hiera/data/dc1.yaml",
    "content": "---\nntp::config::ntpservers:\n  - 'ntp1.dc1.example.com'\n  - 'ntp2.dc1.example.com'\nclasses:\n  - users::dc1\n"
  },
  {
    "path": "examples/hiera/hiera.yaml",
    "content": "---\nversion: 5\ndefaults:\n  datadir: data\n  data_hash: yaml_data\n\nhierarchy:\n  - name: 'Per Location'\n    path: \"%{facts.location}.yaml\"\n\n  - name: 'Per Environment'\n    path: \"%{facts.environment}.yaml\"\n\n  - name: 'Common Data'\n    path: 'common.yaml'\n"
  },
  {
    "path": "examples/hiera/modules/ntp/data/common.yaml",
    "content": "---\nntp::config::ntpservers:\n  - '1.pool.ntp.org'\n  - '2.pool.ntp.org'\n"
  },
  {
    "path": "examples/hiera/modules/ntp/hiera.yaml",
    "content": "---\nversion: 5\ndefaults:\n  datadir: data\n  data_hash: yaml_data\n\nhierarchy:\n  - name: 'Common Data'\n    path: 'common.yaml'\n"
  },
  {
    "path": "examples/hiera/modules/ntp/manifests/config.pp",
    "content": "# @summary Manage /tmp/ntp.conf file\n#\n# Given an array of ntpservers, manage the /tmp/ntp.conf file\n#\n# @example\n#   include ntp::config\n#\n# @param ntpservers\n#   An array of ntpserver(s) that should be present in the conf file\nclass ntp::config(\n  Array[String[1], 1] $ntpservers = undef,\n) {\n\n  file { '/tmp/ntp.conf':\n    content => epp('ntp/ntp.conf.epp')\n  }\n\n}\n"
  },
  {
    "path": "examples/hiera/modules/ntp/templates/ntp.conf.epp",
    "content": "<% $ntp::config::ntpservers.each |$server| { -%>\nserver <%= $server %>\n<% } -%>\n"
  },
  {
    "path": "examples/hiera/modules/users/manifests/common.pp",
    "content": "# @summary Notify to demonstrate users::common in catalog\n#\n# A common Class that all examples should include\n#\n# @example\n#   include users::common\nclass users::common {\n  notify { 'Adding users::common': }\n}\n"
  },
  {
    "path": "examples/hiera/modules/users/manifests/dc1.pp",
    "content": "# @summary Notify to demonstrate users::dc1 in catalog\n#\n# A Class that should be present in dc1 node(s) catalog\n#\n# @example\n#   include users::dc1\nclass users::dc1 {\n  notify { 'Adding users::dc1': }\n}\n"
  },
  {
    "path": "examples/hiera/site.pp",
    "content": "node default {\n  include lookup('classes')\n}\n"
  },
  {
    "path": "examples/nagios/check_puppet.rb",
    "content": "#!/usr/bin/env ruby\n\nrequire 'optparse'\nrequire 'sys/proctable'\ninclude Sys\n\nclass CheckPuppet\n\n  VERSION = '0.1'\n  script_name = File.basename($0)\n\n  # default options\n  OPTIONS = {\n    :statefile => \"/opt/puppetlabs/puppet/cache/state/state.yaml\",\n    :process   => \"puppet\",\n    :interval  => 30,\n  }\n\n  OptionParser.new do |o|\n    o.set_summary_indent('  ')\n    o.banner =    \"Usage: #{script_name} [OPTIONS]\"\n    o.define_head \"The check_puppet Nagios plug-in checks that specified Puppet process is running and the state file is no older than specified interval.\"\n      o.separator   \"\"\n      o.separator   \"Mandatory arguments to long options are mandatory for short options too.\"\n\n\n        o.on(\n          \"-s\", \"--statefile=statefile\", String, \"The state file\",\n\n    \"Default: #{OPTIONS[:statefile]}\") { |op| OPTIONS[:statefile] = op }\n\n      o.on(\n        \"-p\", \"--process=processname\", String, \"The process to check\",\n\n    \"Default: #{OPTIONS[:process]}\")   { |op| OPTIONS[:process] = op }\n\n      o.on(\n        \"-i\", \"--interval=value\", Integer,\n\n    \"Default: #{OPTIONS[:interval]} minutes\")  { |op| OPTIONS[:interval] = op }\n\n    o.separator \"\"\n    o.on_tail(\"-h\", \"--help\", \"Show this help message.\") do\n      puts o\n      exit\n    end\n\n    o.parse!(ARGV)\n  end\n\n  def check_proc\n\n    unless ProcTable.ps.find { |p| p.name == OPTIONS[:process]}\n      @proc = 2\n    else\n      @proc = 0\n    end\n\n  end\n\n  def check_state\n\n    # Set variables\n    curt = Time.now\n    intv = OPTIONS[:interval] * 60\n\n    # Check file time\n    begin\n      @modt = File.mtime(\"#{OPTIONS[:statefile]}\")\n    rescue\n      @file = 3\n    end\n\n    diff = (curt - @modt).to_i\n\n    if diff > intv\n      @file = 2\n    else\n      @file = 0\n    end\n\n  end\n\n  def output_status\n\n    case @file\n    when 0\n      state = \"state file status okay updated on \" + @modt.strftime(\"%m/%d/%Y at %H:%M:%S\")\n    when 2\n      state = \"state fille is not up to date and is older than #{OPTIONS[:interval]} minutes\"\n    when 3\n      state = \"state file status unknown\"\n    end\n\n    case @proc\n    when 0\n      process = \"process #{OPTIONS[:process]} is running\"\n    when 2\n      process = \"process #{OPTIONS[:process]} is not running\"\n    end\n\n    case\n    when (@proc == 2 or @file == 2)\n      status = \"CRITICAL\"\n      exitcode = 2\n    when (@proc == 0 and @file == 0)\n      status = \"OK\"\n      exitcode = 0\n    else\n      status = \"UNKNOWN\"\n      exitcode = 3\n    end\n\n    puts \"PUPPET #{status}: #{process}, #{state}\"\n    exit(exitcode)\n  end\nend\n\ncp = CheckPuppet.new\ncp.check_proc\ncp.check_state\ncp.output_status\n\n"
  },
  {
    "path": "ext/README.md",
    "content": "# `ext/` directory details \nThis directory contains files used internally when packaging [puppet](https://github.com/puppetlabs/puppet) and [puppet-agent](https://github.com/puppetlabs/puppet-agent)\nWhat follows is a more detailed description of each directory/file:\n* `debian/` - init scripts for puppet (used for Debian-based platforms that do not support systemd)\n* `hiera/hiera.yaml` - installed to `$codedir/environments/production`as a default Hiera configuration file\n* `osx/puppet.plist` - puppet launchd plist for macOS\n* `redhat/` -  init scripts for puppet (used for EL-based platforms that do not support systemd)\n* `solaris/smf/` - service manifests for Solaris 11\n* `suse/client.init` - init script for puppet (used for SUSE-based platforms that do not support systemd) \n* `systemd/puppet.service` - systemd unit file for puppet\n* `windows/` - the puppet daemon for Windows, and other useful `.bat` helper wrappers\n* `build_defaults.yaml` - information pertaining to the puppetlabs build automation\n* `project_data.yaml` - information used when packaging the puppet gem"
  },
  {
    "path": "ext/build_defaults.yaml",
    "content": "---\npackager: 'puppetlabs'\n\n# These are the build targets used by the packaging repo. Uncomment to allow use.\n#final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-el-7-x86_64'\n#cows: 'base-lucid-i386.cow base-precise-i386.cow base-squeeze-i386.cow base-trusty-i386.cow base-wheezy-i386.cow'\npbuild_conf: '/etc/pbuilderrc'\n\nbuild_gem: TRUE\nbuild_dmg: FALSE\nsign_tar: FALSE\nyum_host: 'yum.puppetlabs.com'\nyum_repo_path: '/opt/repository/yum/'\napt_signing_server: 'apt.puppetlabs.com'\napt_repo_url: 'http://apt.puppetlabs.com'\napt_repo_path: '/opt/repository/incoming'\ntar_host: 'downloads.puppetlabs.com'\nnonfinal_gem_path: '/opt/repository-nightlies/downloads/gems/puppet8-nightly'\n"
  },
  {
    "path": "ext/debian/puppet.default",
    "content": "# Defaults for puppet - sourced by /etc/init.d/puppet\n\n# Startup options\nDAEMON_OPTS=\"\"\n"
  },
  {
    "path": "ext/debian/puppet.init",
    "content": "#! /bin/sh\n### BEGIN INIT INFO\n# Provides:          puppet\n# Required-Start:    $network $named $remote_fs $syslog\n# Required-Stop:     $network $named $remote_fs $syslog\n# Should-Start:      puppet\n# Default-Start:     2 3 4 5\n# Default-Stop:      0 1 6\n### END INIT INFO\n\nDAEMON=/opt/puppetlabs/puppet/bin/puppet\nDAEMON_OPTS=\"\"\nNAME=\"agent\"\nPROCNAME=\"puppet\"\nDESC=\"puppet agent\"\nPIDFILE=\"/var/run/puppetlabs/${NAME}.pid\"\n\ntest -x $DAEMON || exit 0\n\n[ -r /etc/default/puppet ] && . /etc/default/puppet\n\n. /lib/lsb/init-functions\n\nreload_puppet_agent() {\n    start-stop-daemon --stop --quiet --signal HUP --pidfile $PIDFILE --name $PROCNAME\n}\n\nstart_puppet_agent() {\n    start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON -- $NAME $DAEMON_OPTS\n}\n\nstop_puppet_agent() {\n    start-stop-daemon --stop --retry TERM/10/KILL/5 --quiet --oknodo --pidfile $PIDFILE --name $PROCNAME\n    rm -f \"$PIDFILE\"\n}\n\nrestart_puppet_agent() {\n    log_begin_msg \"Restarting $DESC\"\n    stop_puppet_agent\n    start_puppet_agent\n    log_end_msg $?\n}\n\nstatus_puppet_agent() {\n    if (type status_of_proc > /dev/null 2>&1) ; then\n        status_of_proc -p \"${PIDFILE}\" \"${DAEMON}\" \"${NAME}\"\n    else\n        status_of_proc() {\n            local pidfile daemon name status\n\n            pidfile=\n            OPTIND=1\n            while getopts p: opt ; do\n                case \"$opt\" in\n                    p)  pidfile=\"$OPTARG\";;\n                esac\n            done\n            shift $(($OPTIND - 1))\n\n            if [ -n \"$pidfile\" ]; then\n                pidfile=\"-p $pidfile\"\n            fi\n            daemon=\"$1\"\n            name=\"$2\"\n            status=\"0\"\n            pidofproc $pidfile $daemon >/dev/null || status=\"$?\"\n            if [ \"$status\" = 0 ]; then\n                log_success_msg \"$name is running\"\n                return 0\n            elif [ \"$status\" = 4 ]; then\n                log_failure_msg \"could not access PID file for $name\"\n                return $status\n            else\n                log_failure_msg \"$name is not running\"\n                return $status\n            fi\n        }\n        status_of_proc -p \"${PIDFILE}\" \"${DAEMON}\" \"${NAME}\"\n    fi\n}\n\ncase \"$1\" in\n    start)\n        log_begin_msg \"Starting $DESC\"\n        start_puppet_agent\n        log_end_msg $?\n    ;;\n    stop)\n        log_begin_msg \"Stopping $DESC\"\n        stop_puppet_agent\n        log_end_msg $?\n    ;;\n    reload)\n        log_begin_msg \"Reloading $DESC\"\n        reload_puppet_agent\n        log_end_msg $?\n    ;;\n    status)\n        status_puppet_agent\n    ;;\n    restart|force-reload)\n        restart_puppet_agent\n    ;;\n    condrestart)\n        if status_puppet_agent >/dev/null 2>&1; then\n            restart_puppet_agent\n        fi\n    ;;\n    *)\n        echo \"Usage: $0 {start|stop|status|restart|condrestart|force-reload|reload}\" >&2\n        exit 1\n    ;;\nesac\n"
  },
  {
    "path": "ext/hiera/hiera.yaml",
    "content": "---\nversion: 5\ndefaults:\n  # The default value for \"datadir\" is \"data\" under the same directory as the hiera.yaml\n  # file (this file)\n  # When specifying a datadir, make sure the directory exists.\n  # See https://puppet.com/docs/puppet/latest/environments_about.html for further details on environments.\n  # datadir: data\n  # data_hash: yaml_data\nhierarchy:\n  - name: \"Per-node data (yaml version)\"\n    path: \"nodes/%{::trusted.certname}.yaml\"\n  - name: \"Other YAML hierarchy levels\"\n    paths:\n      - \"common.yaml\"\n"
  },
  {
    "path": "ext/osx/puppet.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n        <key>EnvironmentVariables</key>\n        <dict>\n                <key>LANG</key>\n                <string>en_US.UTF-8</string>\n        </dict>\n        <key>Label</key>\n        <string>puppet</string>\n        <key>KeepAlive</key>\n        <true/>\n        <key>ProgramArguments</key>\n        <array>\n                <string>/opt/puppetlabs/bin/puppet</string>\n                <string>agent</string>\n                <string>--verbose</string>\n                <string>--no-daemonize</string>\n                <string>--logdest</string>\n                <string>console</string>\n        </array>\n        <key>RunAtLoad</key>\n        <true/>\n        <key>StandardErrorPath</key>\n        <string>/var/log/puppetlabs/puppet/puppet.log</string>\n        <key>StandardOutPath</key>\n        <string>/var/log/puppetlabs/puppet/puppet.log</string>\n        <key>SessionCreate</key>\n        <true />\n</dict>\n</plist>\n"
  },
  {
    "path": "ext/project_data.yaml",
    "content": "---\nproject: 'puppet'\ngem_rdoc_options:\n  - --title\n  - \"Puppet - Configuration Management\"\n  - --main\n  - README.md\n  - --line-numbers\n# Array of files to include when building source tarballs\nfiles:\n  - '[A-Z]*'\n  - install.rb\n  - bin\n  - lib\n  - conf\n  - man\n  - examples\n  - ext\n  - tasks\n  - locales\n"
  },
  {
    "path": "ext/redhat/client.init",
    "content": "#!/bin/bash\n# puppet        Init script for running the puppet client daemon\n#\n# Author:       Duane Griffin <d.griffin@psenterprise.com>\n#               David Lutterkort <dlutter@redhat.com>\n#\n# chkconfig: - 98 02\n#\n# description: Enables periodic system configuration checks through puppet.\n# processname: puppet\n# config: /etc/sysconfig/puppet\n\n# Source function library.\n. /etc/rc.d/init.d/functions\n\n[ -f /etc/sysconfig/puppet ] && . /etc/sysconfig/puppet\nlockfile=/var/lock/subsys/puppet\npiddir=/var/run/puppetlabs\npidfile=\"${piddir}/agent.pid\"\npuppetd=/opt/puppetlabs/puppet/bin/puppet\npid=$(cat \"$pidfile\" 2> /dev/null)\nRETVAL=0\n\nPUPPET_OPTS=\"agent \"\n\n# Determine if we can use the -p option to daemon, killproc, and status.\n# RHEL < 5 can't.\nif status | grep -q -- '-p' 2>/dev/null; then\n    daemonopts=\"--pidfile $pidfile\"\n    pidopts=\"-p $pidfile\"\n    USEINITFUNCTIONS=true\nfi\n\n# Figure out if the system just booted. Let's assume\n# boot doesn't take longer than 5 minutes\n## Not used for now\n##[ -n \"$INIT_VERSION\" ] && PUPPET_OPTS=\"${PUPPET_OPTS} --fullrun\"\n\nstart() {\n    echo -n $\"Starting puppet agent: \"\n    mkdir -p $piddir\n    daemon $daemonopts $puppetd ${PUPPET_OPTS} ${PUPPET_EXTRA_OPTS}\n    RETVAL=$?\n    echo\n        [ $RETVAL = 0 ] && touch ${lockfile}\n        return $RETVAL\n}\n\nstop() {\n    echo -n $\"Stopping puppet agent: \"\n    if [ \"$USEINITFUNCTIONS\" = \"true\" ]; then\n      killproc $pidopts $puppetd\n      RETVAL=$?\n    else\n      if [ -n \"${pid}\" ]; then\n        kill -TERM $pid >/dev/null 2>&1\n        RETVAL=$?\n      fi\n    fi\n    echo\n    [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}\n    return $RETVAL\n}\n\nreload() {\n    echo -n $\"Reloading puppet agent: \"\n    if [ \"$USEINITFUNCTIONS\" = \"true\" ]; then\n      killproc $pidopts $puppetd -HUP\n      RETVAL=$?\n    else\n      if [ -n \"${pid}\" ]; then\n        kill -HUP $pid >/dev/null 2>&1\n        RETVAL=$?\n      else\n        RETVAL=0\n      fi\n    fi\n    echo\n    return $RETVAL\n}\n\nrotate() {\n    echo -n $\"Reopening log files for puppet agent: \"\n    killproc $pidopts $puppetd -USR2\n    RETVAL=$?\n    echo\n    return $RETVAL\n}\n\nrestart() {\n    stop\n    start\n}\n\nrh_status() {\n    base=puppet\n    if [ \"$USEINITFUNCTIONS\" = \"true\" ]; then\n      status $pidopts $puppetd\n      RETVAL=$?\n      return $RETVAL\n    else\n      if [ -n \"${pid}\" ]; then\n        if `ps -p $pid | grep $pid > /dev/null 2>&1`; then\n          echo \"${base} (pid ${pid}) is running...\"\n          RETVAL=0\n          return $RETVAL\n        fi\n      fi\n      if [ -f \"${pidfile}\" ] ; then\n        echo \"${base} dead but pid file exists\"\n        RETVAL=1\n        return $RETVAL\n      fi\n      if [ -f \"${lockfile}\" ]; then\n        echo \"${base} dead but subsys locked\"\n        RETVAL=2\n        return $RETVAL\n      fi\n      echo \"${base} is stopped\"\n      RETVAL=3\n      return $RETVAL\n    fi\n}\n\nrh_status_q() {\n    rh_status >/dev/null 2>&1\n}\n\ngenconfig() {\n    echo -n $\"Generate puppet agent configuration: \"\n    $puppetd ${PUPPET_OPTS} ${PUPPET_EXTRA_OPTS} --genconfig\n}\n\ncase \"$1\" in\n    start)\n        start\n    ;;\n    stop)\n        stop\n    ;;\n    restart)\n        restart\n    ;;\n    rotate)\n        rotate\n    ;;\n    reload|force-reload)\n        reload\n    ;;\n    condrestart|try-restart)\n        rh_status_q || exit 0\n        restart\n    ;;\n    status)\n        rh_status\n    ;;\n    once)\n        shift\n        $puppetd ${PUPPET_OPTS} --onetime ${PUPPET_EXTRA_OPTS} $@\n        ;;\n    genconfig)\n        genconfig\n    ;;\n    *)\n        echo $\"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart|rotate|once|genconfig}\"\n        exit 1\nesac\n\nexit $RETVAL\n"
  },
  {
    "path": "ext/redhat/client.sysconfig",
    "content": "# You may specify parameters to the puppet client here\n#PUPPET_EXTRA_OPTS=--waitforcert=500\n"
  },
  {
    "path": "ext/solaris/smf/puppet",
    "content": "#!/sbin/sh\n\n. /lib/svc/share/smf_include.sh\n\n[ -z \"${SMF_FMRI}\" ] && exit \"$SMF_EXIT_ERR\"\n\nCONF_FILE=/etc/puppetlabs/puppet/puppet.conf\n[ ! -f \"${CONF_FILE}\" ] && exit \"$SMF_EXIT_ERR_CONFIG\"\n\nPUPPET=/opt/puppetlabs/bin/puppet\n\ncase \"$1\" in\nstart)\n  exec \"$PUPPET\" agent\n  ;;\n\nstop)\n  # stop sends sigterm first followed by sigkill\n  # smf_kill_contract <CTID> TERM 1 30\n  # sends sigterm to all process in ctid and will continue\n  # to do so for 30 seconds with interval of 5 seconds\n  # smf_kill_contract <CTID> KILL 1\n  # continues until all processes are killed.\n  # svcs -p <fmri> lists all processes in the contract.\n  # http://bnsmb.de/solaris/My_Little_SMF_FAQ.html\n  ctid=`svcprop -p restarter/contract \"$SMF_FMRI\"`\n  if [ -n \"$ctid\" ]; then\n    smf_kill_contract \"$ctid\" TERM 1 30\n    ret=$?\n    [ \"$ret\" -eq 1 ] && exit \"$SMF_EXIT_ERR_FATAL\"\n\n    if [ \"$ret\" -eq 2 ] ; then\n      smf_kill_contract \"$ctid\" KILL 1\n      [ $? -ne 0 ] && exit \"$SMF_EXIT_ERR_FATAL\"\n    fi\n  fi\n  ;;\n*)\n  echo \"Usage: $0 {start|stop}\";\n  exit \"$SMF_EXIT_ERR_FATAL\"\n  ;;\nesac\nexit \"$SMF_EXIT_OK\"\n\n"
  },
  {
    "path": "ext/solaris/smf/puppet.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service_bundle SYSTEM \"/usr/share/lib/xml/dtd/service_bundle.dtd.1\">\n<!-- Original puppet manifest: Luke Kanies - puppetlabs.com -->\n<service_bundle type=\"manifest\" name=\"puppet\">\n\n  <service name=\"network/puppet\" type=\"service\" version=\"1\">\n\n    <create_default_instance enabled=\"false\"/>\n    <single_instance/>\n\n    <dependency name=\"config-file\" grouping=\"require_all\" restart_on=\"none\" type=\"path\">\n      <service_fmri value=\"file:///etc/puppetlabs/puppet/puppet.conf\"/>\n    </dependency>\n\n    <dependency name=\"loopback\" grouping=\"require_all\" restart_on=\"error\" type=\"service\">\n      <service_fmri value=\"svc:/network/loopback:default\"/>\n    </dependency>\n\n    <dependency name=\"physical\" grouping=\"require_all\" restart_on=\"error\" type=\"service\">\n      <service_fmri value=\"svc:/network/physical:default\"/>\n    </dependency>\n\n    <dependency name=\"fs-local\" grouping=\"require_all\" restart_on=\"none\" type=\"service\">\n      <service_fmri value=\"svc:/system/filesystem/local\"/>\n    </dependency>\n\n    <exec_method type=\"method\" name=\"start\" exec=\"/lib/svc/method/puppet start\" timeout_seconds=\"60\"/>\n\n    <exec_method type=\"method\" name=\"stop\" exec=\"/lib/svc/method/puppet stop\" timeout_seconds=\"60\"/>\n\n    <exec_method type=\"method\" name=\"refresh\" exec=\":kill\" timeout_seconds=\"60\"/>\n\n  <stability value=\"Evolving\"/>\n\n    <template>\n      <common_name>\n        <loctext xml:lang=\"C\">Puppet Agent Daemon</loctext>\n      </common_name>\n      <documentation>\n        <manpage title=\"puppet\" section=\"1\"/>\n        <doc_link name=\"puppetlabs.com\" uri=\"https://puppetlabs.com/puppet/introduction\"/>\n      </documentation>\n    </template>\n  </service>\n\n</service_bundle>\n"
  },
  {
    "path": "ext/suse/client.init",
    "content": "#!/bin/bash\n# puppet        Init script for running the puppet client daemon\n#\n# Author:       Duane Griffin <d.griffin@psenterprise.com>\n#               David Lutterkort <dlutter@redhat.com>\n#               Martin Vuk <martin.vuk@fri.uni-lj.si> (SuSE support)\n#\n# chkconfig: - 98 02\n#\n# description: Enables periodic system configuration checks through puppet.\n# processname: puppet\n# config: /etc/sysconfig/puppet\n\n### BEGIN INIT INFO\n# Provides: puppet\n# Required-Start: $local_fs $remote_fs $network $syslog\n# Should-Start: puppet\n# Required-Stop: $local_fs $remote_fs $network $syslog\n# Should-Stop: puppet\n# Default-Start: 3 5\n# Default-Stop: 0 1 2 6\n# Short-Description: puppet\n# Description: Enables periodic system configuration checks through puppet.\n### END INIT INFO\n\n# Shell functions sourced from /etc/rc.status:\n#      rc_check         check and set local and overall rc status\n#      rc_status        check and set local and overall rc status\n#      rc_status -v     ditto but be verbose in local rc status\n#      rc_status -v -r  ditto and clear the local rc status\n#      rc_failed        set local and overall rc status to failed\n#      rc_reset         clear local rc status (overall remains)\n#      rc_exit          exit appropriate to overall rc status\n[ -f /etc/rc.status ] && . /etc/rc.status\n[ -f /etc/sysconfig/puppet ] && . /etc/sysconfig/puppet\nlockfile=/var/lock/subsys/puppet\npidfile=/var/run/puppetlabs/agent.pid\npuppetd=/opt/puppetlabs/puppet/bin/puppet\n\nPUPPET_OPTS=\"agent\"\n\n# First reset status of this service\nrc_reset\n\n# Return values acc. to LSB for all commands but status:\n# 0 - success\n# 1 - misc error\n# 2 - invalid or excess args\n# 3 - unimplemented feature (e.g. reload)\n# 4 - insufficient privilege\n# 5 - program not installed\n# 6 - program not configured\n#\n# Note that starting an already running service, stopping\n# or restarting a not-running service as well as the restart\n# with force-reload (in case signalling is not supported) are\n# considered a success.\n\ncase \"$1\" in\n    start)\n        echo -n \"Starting puppet services.\"\n        ## Start daemon with startproc(8). If this fails\n        ## the echo return value is set appropriate.\n\n        ## This accounts for the behavior of startproc on sles 10.\n        ## Check to see if a running process matches the contents\n        ## of the pidfile, and do nothing if true. Otherwise\n        ## force a process start\n        if [ -f \"${pidfile}\" ]; then\n            PID=$(cat \"$pidfile\")\n            if [ \"$PID\" -eq $(pgrep -f \"$puppetd\") ] ; then\n                rc_status -v\n                rc_exit\n            fi\n        fi\n        startproc -f -w -p \"${pidfile}\" \"${puppetd}\" \"${PUPPET_OPTS}\" ${PUPPET_EXTRA_OPTS} && touch \"${lockfile}\"\n        # Remember status and be verbose\n        rc_status -v\n        ;;\n    stop)\n        echo -n \"Shutting down puppet:\"\n        ## Stop daemon with killproc(8) and if this fails\n        ## set echo the echo return value.\n\n        killproc -QUIT -p \"${pidfile}\" \"${puppetd}\" && rm -f \"${lockfile}\" \"${pidfile}\"\n\n        # Remember status and be verbose\n        rc_status -v\n        ;;\n    try-restart|condrestart)\n        ## Stop the service and if this succeeds (i.e. the\n        ## service was running before), start it again.\n        $0 status >/dev/null &&  $0 restart\n\n        # Remember status and be quiet\n        rc_status\n        ;;\n    restart)\n        ## Stop the service and regardless of whether it was\n        ## running or not, start it again.\n        $0 stop\n        $0 start\n\n        # Remember status and be quiet\n        rc_status\n        ;;\n    reload|force-reload)\n        # Reload the service by sending the HUP signal\n        echo -n \"Reloading puppet service: \"\n        killproc -HUP -p \"${pidfile}\" \"${puppetd}\"\n        rc_status -v\n        ;;\n    status)\n        echo -n \"Checking for service puppetd: \"\n        ## Check status with checkproc(8), if process is running\n        ## checkproc will return with exit status 0.\n\n        # Status has a slightly different for the status command:\n        # 0 - service running\n        # 1 - service dead, but /var/run/  pid  file exists\n        # 2 - service dead, but /var/lock/ lock file exists\n        # 3 - service not running\n\n        # NOTE: checkproc returns LSB compliant status values.\n        if [ -f \"${pidfile}\" ]; then\n            checkproc -p \"${pidfile}\" \"${puppetd}\"\n            rc_status -v\n        else\n            rc_failed 3\n            rc_status -v\n        fi\n        ;;\n    once)\n        shift\n        $puppetd \"${PUPPET_OPTS}\" --onetime ${PUPPET_EXTRA_OPTS} $@\n        ;;\n    *)\n        echo \"Usage: $0 {start|stop|status|try-restart|condrestart|restart|force-reload|reload|once}\"\n        exit 1\nesac\nrc_exit\n"
  },
  {
    "path": "ext/systemd/puppet.service",
    "content": "#\n# Local settings can be configured without being overwritten by package upgrades, for example\n# if you want to increase puppet open-files-limit to 10000,\n# you need to increase systemd's LimitNOFILE setting, so create a file named\n# \"/etc/systemd/system/puppet.service.d/limits.conf\" containing:\n# [Service]\n# LimitNOFILE=10000\n# You can confirm it worked by running systemctl daemon-reload\n# then running systemctl show puppet | grep LimitNOFILE\n#\n[Unit]\nDescription=Puppet agent\nDocumentation=man:puppet-agent(8)\nWants=basic.target\nAfter=basic.target network.target network-online.target\n\n[Service]\nEnvironmentFile=-/etc/sysconfig/puppetagent\nEnvironmentFile=-/etc/sysconfig/puppet\nEnvironmentFile=-/etc/default/puppet\nExecStart=/opt/puppetlabs/puppet/bin/puppet agent $PUPPET_EXTRA_OPTS --no-daemonize\nExecReload=/bin/kill -HUP $MAINPID\nKillMode=process\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ext/windows/puppet_interactive.bat",
    "content": "@echo off\r\nSETLOCAL\r\necho Running Puppet agent on demand ...\r\ncd \"%~dp0\"\r\ncall puppet.bat agent --test %*\r\nPAUSE\r\n"
  },
  {
    "path": "ext/windows/puppet_shell.bat",
    "content": "@echo off\r\nSETLOCAL\r\nif exist \"%~dp0environment.bat\" (\r\n  call \"%~dp0environment.bat\" %0 %*\r\n) else (\r\n  SET \"PATH=%~dp0;%PATH%\"\r\n)\r\nREM Display Ruby version\r\nruby.exe -v\r\n"
  },
  {
    "path": "ext/windows/run_puppet_interactive.bat",
    "content": "@echo Running puppet on demand ...\r\n@echo off\r\nSETLOCAL\r\nif exist \"%~dp0environment.bat\" (\r\n  call \"%~dp0environment.bat\" %0 %*\r\n) else (\r\n  SET \"PATH=%~dp0;%PATH%\"\r\n)\r\nelevate.exe \"%~dp0puppet_interactive.bat\"\r\n"
  },
  {
    "path": "ext/windows/service/daemon.bat",
    "content": "@echo off\nSETLOCAL\n\ncall \"%~dp0..\\bin\\environment.bat\" %0 %*\n\nruby -rubygems \"%~dp0daemon.rb\" %*"
  },
  {
    "path": "ext/windows/service/daemon.rb",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire 'fileutils'\nrequire 'puppet/util/windows/daemon'\n\n# This file defines utilities for logging to eventlog. While it lives inside\n# Puppet, it is completely independent and loads no other parts of Puppet, so we\n# can safely require *just* it.\nrequire 'puppet/util/windows/eventlog'\n\n# monkey patches ruby Process to add .create method\nrequire 'puppet/util/windows/monkey_patches/process'\n\nclass WindowsDaemon < Puppet::Util::Windows::Daemon\n  CREATE_NEW_CONSOLE = 0x00000010\n\n  @run_thread = nil\n  @LOG_TO_FILE = false\n  @loglevel = 0\n  LOG_FILE =  File.expand_path(File.join(ENV.fetch('ALLUSERSPROFILE', nil), 'PuppetLabs', 'puppet', 'var', 'log', 'windows.log'))\n  LEVELS = [:debug, :info, :notice, :warning, :err, :alert, :emerg, :crit]\n  LEVELS.each do |level|\n    define_method(\"log_#{level}\") do |msg|\n      log(msg, level)\n    end\n  end\n\n  def service_init\n  end\n\n  def service_main(*argsv)\n    argsv = (argsv << ARGV).flatten.compact\n    args = argsv.join(' ')\n    @loglevel = LEVELS.index(argsv.index('--debug') ? :debug : :notice)\n\n    @LOG_TO_FILE = (argsv.index('--logtofile') ? true : false)\n\n    if @LOG_TO_FILE\n      FileUtils.mkdir_p(File.dirname(LOG_FILE))\n      args = args.gsub(\"--logtofile\", \"\")\n    end\n\n    base_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))\n    load_env(base_dir)\n\n    # The puppet installer registers a 'Puppet' event source.  For the moment events will be logged with this key, but\n    # it may be a good idea to split the Service and Puppet events later so it's easier to read in the windows Event Log.\n    #\n    # Example code to register an event source;\n    # eventlogdll =  File.expand_path(File.join(basedir, 'puppet', 'ext', 'windows', 'eventlog', 'puppetres.dll'))\n    # if (File.exist?(eventlogdll))\n    #   Win32::EventLog.add_event_source(\n    #      'source' => \"Application\",\n    #      'key_name' => \"Puppet Agent\",\n    #      'category_count' => 3,\n    #      'event_message_file' => eventlogdll,\n    #      'category_message_file' => eventlogdll\n    #   )\n    # end\n\n    puppet = File.join(base_dir, 'puppet', 'bin', 'puppet')\n    ruby = File.join(base_dir, 'puppet', 'bin', 'ruby.exe')\n    ruby_puppet_cmd = \"\\\"#{ruby}\\\" \\\"#{puppet}\\\"\"\n\n    unless File.exist?(puppet)\n      log_err(\"File not found: '#{puppet}'\")\n      return\n    end\n    log_debug(\"Using '#{puppet}'\")\n\n    cmdline_debug = argsv.index('--debug') ? :debug : nil\n    @loglevel = parse_log_level(ruby_puppet_cmd, cmdline_debug)\n    log_notice('Service started')\n\n    service = self\n    @run_thread = Thread.new do\n      while service.running?\n        runinterval = service.parse_runinterval(ruby_puppet_cmd)\n\n        if service.state == RUNNING or service.state == IDLE\n          service.log_notice(\"Executing agent with arguments: #{args}\")\n          pid = Process.create(:command_line => \"#{ruby_puppet_cmd} agent --onetime #{args}\", :creation_flags => CREATE_NEW_CONSOLE).process_id\n          service.log_debug(\"Process created: #{pid}\")\n        else\n          service.log_debug(\"Service is paused.  Not invoking Puppet agent\")\n        end\n\n        service.log_debug(\"Service worker thread waiting for #{runinterval} seconds\")\n        sleep(runinterval)\n        service.log_debug('Service worker thread woken up')\n      end\n    rescue Exception => e\n      service.log_exception(e)\n    end\n    @run_thread.join\n  rescue Exception => e\n    log_exception(e)\n  ensure\n    log_notice('Service stopped')\n  end\n\n  def service_stop\n    log_notice('Service stopping / killing worker thread')\n    @run_thread.kill if @run_thread\n  end\n\n  def service_pause\n    log_notice('Service pausing')\n  end\n\n  def service_resume\n    log_notice('Service resuming')\n  end\n\n  def service_shutdown\n    log_notice('Host shutting down')\n  end\n\n  # Interrogation handler is just for debug.  Can be commented out or removed entirely.\n  # def service_interrogate\n  #   log_debug('Service is being interrogated')\n  # end\n\n  def log_exception(e)\n    log_err(e.message)\n    log_err(e.backtrace.join(\"\\n\"))\n  end\n\n  def log(msg, level)\n    if LEVELS.index(level) >= @loglevel\n      if @LOG_TO_FILE\n        # without this change its possible that we get Encoding errors trying to write UTF-8 messages in current codepage\n        File.open(LOG_FILE, 'a:UTF-8') { |f| f.puts(\"#{Time.now} Puppet (#{level}): #{msg}\") }\n      end\n\n      native_type, native_id = Puppet::Util::Windows::EventLog.to_native(level)\n      report_windows_event(native_type, native_id, msg.to_s)\n    end\n  end\n\n  def report_windows_event(type, id, message)\n    eventlog = nil\n    eventlog = Puppet::Util::Windows::EventLog.open(\"Puppet\")\n    eventlog.report_event(\n      :event_type => type, # EVENTLOG_ERROR_TYPE, etc\n      :event_id => id, # 0x01 or 0x02, 0x03 etc.\n      :data => message # \"the message\"\n    )\n  rescue Exception\n    # Ignore all errors\n  ensure\n    unless eventlog.nil?\n      eventlog.close\n    end\n  end\n\n  # Parses runinterval.\n  #\n  # @param puppet_path [String] The file path for the Puppet executable.\n  # @return runinterval [Integer] How often to do a Puppet run, in seconds.\n  def parse_runinterval(puppet_path)\n    begin\n      runinterval = %x(#{puppet_path} config --section agent --log_level notice print runinterval).chomp\n      if runinterval == ''\n        runinterval = 1800\n        log_err(\"Failed to determine runinterval, defaulting to #{runinterval} seconds\")\n      else\n        # Use Kernel#Integer because to_i will return 0 with non-numeric strings.\n        runinterval = Integer(runinterval)\n      end\n    rescue Exception => e\n      log_exception(e)\n      runinterval = 1800\n    end\n\n    runinterval\n  end\n\n  def parse_log_level(puppet_path, cmdline_debug)\n    begin\n      loglevel = \"notice\"\n      unless loglevel && respond_to?(\"log_#{loglevel}\")\n        loglevel = :notice\n        log_err(\"Failed to determine loglevel, defaulting to #{loglevel}\")\n      end\n    rescue Exception => e\n      log_exception(e)\n      loglevel = :notice\n    end\n\n    LEVELS.index(cmdline_debug || loglevel.to_sym)\n  end\n\n  private\n\n  def load_env(base_dir)\n    # ENV that uses backward slashes\n    ENV['FACTER_env_windows_installdir'] = base_dir.tr('/', '\\\\')\n    ENV['PL_BASEDIR'] = base_dir.tr('/', '\\\\')\n    ENV['PUPPET_DIR'] = File.join(base_dir, 'puppet').tr('/', '\\\\')\n    ENV['OPENSSL_CONF'] = File.join(base_dir, 'puppet', 'ssl', 'openssl.cnf').tr('/', '\\\\')\n    ENV['SSL_CERT_DIR'] = File.join(base_dir, 'puppet', 'ssl', 'certs').tr('/', '\\\\')\n    ENV['SSL_CERT_FILE'] = File.join(base_dir, 'puppet', 'ssl', 'cert.pem').tr('/', '\\\\')\n    ENV['Path'] = [\n      File.join(base_dir, 'puppet', 'bin'),\n      File.join(base_dir, 'bin')\n    ].join(';').tr('/', '\\\\') + ';' + ENV.fetch('Path', nil)\n\n    # ENV that uses forward slashes\n    ENV['RUBYLIB'] = \"#{File.join(base_dir, 'puppet', 'lib')};#{ENV.fetch('RUBYLIB', nil)}\"\n  rescue => e\n    log_exception(e)\n  end\nend\n\nif __FILE__ == $PROGRAM_NAME\n  WindowsDaemon.mainloop\nend\n"
  },
  {
    "path": "install.rb",
    "content": "#! /usr/bin/env ruby\n#--\n# Copyright 2004 Austin Ziegler <ruby-install@halostatue.ca>\n#   Install utility. Based on the original installation script for rdoc by the\n#   Pragmatic Programmers.\n#\n# This program is free software. It may be redistributed and/or modified under\n# the terms of the GPL version 2 (or later) or the Ruby licence.\n#\n# Usage\n# -----\n# In most cases, if you have a typical project layout, you will need to do\n# absolutely nothing to make this work for you. This layout is:\n#\n#   bin/    # executable files -- \"commands\"\n#   lib/    # the source of the library\n#\n# The default behaviour:\n# 1) Build Rdoc documentation from all files in bin/ (excluding .bat and .cmd),\n#    all .rb files in lib/, ./README, ./ChangeLog, and ./Install.\n# 2) Build ri documentation from all files in bin/ (excluding .bat and .cmd),\n#    and all .rb files in lib/. This is disabled by default on Microsoft Windows.\n# 3) Install commands from bin/ into the Ruby bin directory. On Windows, if a\n#    if a corresponding batch file (.bat or .cmd) exists in the bin directory,\n#    it will be copied over as well. Otherwise, a batch file (always .bat) will\n#    be created to run the specified command.\n# 4) Install all library files ending in .rb from lib/ into Ruby's\n#    site_lib/version directory.\n#\n#++\n\nrequire 'rbconfig'\nrequire 'find'\nrequire 'fileutils'\nrequire 'tempfile'\nrequire 'optparse'\nrequire 'ostruct'\n\nPREREQS = %w{openssl facter cgi}\nMIN_FACTER_VERSION = 1.5\n\nInstallOptions = OpenStruct.new\n\ndef glob(list)\n  g = list.map { |i| Dir.glob(i) }\n  g.flatten!\n  g.compact!\n  g\nend\n\ndef do_configs(configs, target, strip = 'conf/')\n  Dir.mkdir(target) unless File.directory? target\n  configs.each do |cf|\n    ocf = File.join(InstallOptions.config_dir, cf.gsub(/#{strip}/, ''))\n    FileUtils.install(cf, ocf, mode: 0644, preserve: true, verbose: true)\n  end\nend\n\ndef do_bins(bins, target, strip = 's?bin/')\n  Dir.mkdir(target) unless File.directory? target\n  bins.each do |bf|\n    obf = bf.gsub(/#{strip}/, '')\n    install_binfile(bf, obf, target)\n  end\nend\n\ndef do_libs(libs, strip = 'lib/')\n  libs.each do |lf|\n    next if File.directory? lf\n    olf = File.join(InstallOptions.site_dir, lf.sub(/^#{strip}/, ''))\n    op = File.dirname(olf)\n    FileUtils.makedirs(op, mode: 0755, verbose: true)\n    FileUtils.chmod(0755, op)\n    FileUtils.install(lf, olf, mode: 0644, preserve: true, verbose: true)\n  end\nend\n\ndef do_man(man, strip = 'man/')\n  man.each do |mf|\n    omf = File.join(InstallOptions.man_dir, mf.gsub(/#{strip}/, ''))\n    om = File.dirname(omf)\n    FileUtils.makedirs(om, mode: 0755, verbose: true)\n    FileUtils.chmod(0755, om)\n    FileUtils.install(mf, omf, mode: 0644, preserve: true, verbose: true)\n    # Solaris does not support gzipped man pages. When called with\n    # --no-check-prereqs/without facter the default gzip behavior still applies\n    unless $osname == \"Solaris\"\n      gzip = %x{which gzip}\n      gzip.chomp!\n      %x{#{gzip} -f #{omf}}\n    end\n  end\nend\n\ndef do_locales(locale, strip = 'locales/')\n  locale.each do |lf|\n    next if File.directory? lf\n    olf = File.join(InstallOptions.locale_dir, lf.sub(/^#{strip}/, ''))\n    op = File.dirname(olf)\n    FileUtils.makedirs(op, mode: 0755, verbose: true)\n    FileUtils.chmod(0755, op)\n    FileUtils.install(lf, olf, mode: 0644, preserve: true, verbose: true)\n  end\nend\n\n# Verify that all of the prereqs are installed\ndef check_prereqs\n  PREREQS.each { |pre|\n    begin\n      require pre\n      if pre == \"facter\"\n        # to_f isn't quite exact for strings like \"1.5.1\" but is good\n        # enough for this purpose.\n        facter_version = Facter.version.to_f\n        if facter_version < MIN_FACTER_VERSION\n          puts \"Facter version: #{facter_version}; minimum required: #{MIN_FACTER_VERSION}; cannot install\"\n          exit(-1)\n        end\n      end\n    rescue LoadError\n      puts \"Could not load #{pre}; cannot install\"\n      exit(-1)\n    end\n  }\nend\n\n##\n# Prepare the file installation.\n#\ndef prepare_installation\n  InstallOptions.configs = true\n  InstallOptions.check_prereqs = true\n  InstallOptions.batch_files = true\n\n  ARGV.options do |opts|\n    opts.banner = \"Usage: #{File.basename($0)} [options]\"\n    opts.separator \"\"\n    opts.on('--[no-]configs', 'Prevents the installation of config files', 'Default off.') do |ontest|\n      InstallOptions.configs = ontest\n    end\n    opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir|\n      InstallOptions.destdir = destdir\n    end\n    opts.on('--configdir[=OPTIONAL]', 'Installation directory for config files', 'Default /etc/puppetlabs/puppet') do |configdir|\n      InstallOptions.configdir = configdir\n    end\n    opts.on('--codedir[=OPTIONAL]', 'Installation directory for code files', 'Default /etc/puppetlabs/code') do |codedir|\n      InstallOptions.codedir = codedir\n    end\n    opts.on('--vardir[=OPTIONAL]', 'Installation directory for var files', 'Default /opt/puppetlabs/puppet/cache') do |vardir|\n      InstallOptions.vardir = vardir\n    end\n    opts.on('--publicdir[=OPTIONAL]', 'Installation directory for public files such as the `last_run_summary.yaml` report', 'Default /opt/puppetlabs/puppet/public') do |publicdir|\n      InstallOptions.publicdir = publicdir\n    end\n    opts.on('--rundir[=OPTIONAL]', 'Installation directory for state files', 'Default /var/run/puppetlabs') do |rundir|\n      InstallOptions.rundir = rundir\n    end\n    opts.on('--logdir[=OPTIONAL]', 'Installation directory for log files', 'Default /var/log/puppetlabs/puppet') do |logdir|\n      InstallOptions.logdir = logdir\n    end\n    opts.on('--bindir[=OPTIONAL]', 'Installation directory for binaries', 'overrides RbConfig::CONFIG[\"bindir\"]') do |bindir|\n      InstallOptions.bindir = bindir\n    end\n    opts.on('--localedir[=OPTIONAL]', 'Installation directory for locale information', 'Default /opt/puppetlabs/puppet/share/locale') do |localedir|\n      InstallOptions.localedir = localedir\n    end\n    opts.on('--ruby[=OPTIONAL]', 'Ruby interpreter to use with installation', 'overrides ruby used to call install.rb') do |ruby|\n      InstallOptions.ruby = ruby\n    end\n    opts.on('--sitelibdir[=OPTIONAL]', 'Installation directory for libraries', 'overrides RbConfig::CONFIG[\"sitelibdir\"]') do |sitelibdir|\n      InstallOptions.sitelibdir = sitelibdir\n    end\n    opts.on('--mandir[=OPTIONAL]', 'Installation directory for man pages', 'overrides RbConfig::CONFIG[\"mandir\"]') do |mandir|\n      InstallOptions.mandir = mandir\n    end\n    opts.on('--[no-]check-prereqs', 'Prevents validation of prerequisite libraries', 'Default on') do |prereq|\n      InstallOptions.check_prereqs = prereq\n    end\n    opts.on('--no-batch-files', 'Prevents installation of batch files for windows', 'Default off') do |batch_files|\n      InstallOptions.batch_files = false\n    end\n    opts.on('--quick', 'Performs a quick installation. Only the', 'installation is done.') do |quick|\n      InstallOptions.configs = true\n      warn \"--quick is deprecated. Use --configs\"\n    end\n    opts.separator(\"\")\n    opts.on_tail('--help', \"Shows this help text.\") do\n      $stderr.puts opts\n      exit\n    end\n\n    opts.parse!\n  end\n\n  # Mac OS X 10.5 and higher declare bindir\n  # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin\n  # which is not generally where people expect executables to be installed\n  # These settings are appropriate defaults for all OS X versions.\n  if RUBY_PLATFORM =~ /^universal-darwin[\\d\\.]+$/\n    RbConfig::CONFIG['bindir'] = \"/usr/bin\"\n  end\n\n  # Here we only set $osname if we have opted to check for prereqs.\n  # Otherwise facter won't be guaranteed to be present.\n  if InstallOptions.check_prereqs\n    check_prereqs\n    $osname = Facter.value('os.name')\n  end\n\n  if not InstallOptions.configdir.nil?\n    configdir = InstallOptions.configdir\n  elsif $osname == \"windows\"\n    configdir = File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"etc\")\n  else\n    configdir = \"/etc/puppetlabs/puppet\"\n  end\n\n  if not InstallOptions.codedir.nil?\n    codedir = InstallOptions.codedir\n  elsif $osname == \"windows\"\n    codedir = File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"code\")\n  else\n    codedir = \"/etc/puppetlabs/code\"\n  end\n\n  if not InstallOptions.vardir.nil?\n    vardir = InstallOptions.vardir\n  elsif $osname == \"windows\"\n    vardir = File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"cache\")\n  else\n    vardir = \"/opt/puppetlabs/puppet/cache\"\n  end\n\n  if not InstallOptions.publicdir.nil?\n    publicdir = InstallOptions.publicdir\n  elsif $osname == \"windows\"\n    publicdir = File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"public\")\n  else\n    publicdir = \"/opt/puppetlabs/puppet/public\"\n  end\n\n  if not InstallOptions.rundir.nil?\n    rundir = InstallOptions.rundir\n  elsif $osname == \"windows\"\n    rundir = File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"var\", \"run\")\n  else\n    rundir = \"/var/run/puppetlabs\"\n  end\n\n  if not InstallOptions.logdir.nil?\n    logdir = InstallOptions.logdir\n  elsif $osname == \"windows\"\n    logdir = File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"var\", \"log\")\n  else\n    logdir = \"/var/log/puppetlabs/puppet\"\n  end\n\n  if not InstallOptions.bindir.nil?\n    bindir = InstallOptions.bindir\n  else\n    bindir = RbConfig::CONFIG['bindir']\n  end\n\n  if not InstallOptions.localedir.nil?\n    localedir = InstallOptions.localedir\n  else\n    if $osname == \"windows\"\n      localedir = File.join(ENV['PROGRAMFILES'], \"Puppet Labs\", \"Puppet\", \"puppet\", \"share\", \"locale\")\n    else\n      localedir = \"/opt/puppetlabs/puppet/share/locale\"\n    end\n  end\n\n  if not InstallOptions.sitelibdir.nil?\n    sitelibdir = InstallOptions.sitelibdir\n  else\n    sitelibdir = RbConfig::CONFIG[\"sitelibdir\"]\n    if sitelibdir.nil?\n      sitelibdir = $LOAD_PATH.find { |x| x =~ /site_ruby/ }\n      if sitelibdir.nil?\n        version = [RbConfig::CONFIG[\"MAJOR\"], RbConfig::CONFIG[\"MINOR\"]].join(\".\")\n        sitelibdir = File.join(RbConfig::CONFIG[\"libdir\"], \"ruby\", version, \"site_ruby\")\n      elsif sitelibdir !~ Regexp.quote(version)\n        sitelibdir = File.join(sitelibdir, version)\n      end\n    end\n  end\n\n  if not InstallOptions.mandir.nil?\n    mandir = InstallOptions.mandir\n  else\n    mandir = RbConfig::CONFIG['mandir']\n  end\n\n  # This is the new way forward\n  if not InstallOptions.destdir.nil?\n    destdir = InstallOptions.destdir\n  else\n    destdir = ''\n  end\n\n  configdir = join(destdir, configdir)\n  codedir = join(destdir, codedir)\n  vardir = join(destdir, vardir)\n  publicdir = join(destdir, publicdir)\n  rundir = join(destdir, rundir)\n  logdir = join(destdir, logdir)\n  bindir = join(destdir, bindir)\n  localedir = join(destdir, localedir)\n  mandir = join(destdir, mandir)\n  sitelibdir = join(destdir, sitelibdir)\n\n  FileUtils.makedirs(configdir) if InstallOptions.configs\n  FileUtils.makedirs(codedir)\n  FileUtils.makedirs(bindir)\n  FileUtils.makedirs(mandir)\n  FileUtils.makedirs(sitelibdir)\n  FileUtils.makedirs(vardir)\n  FileUtils.makedirs(publicdir)\n  FileUtils.makedirs(rundir)\n  FileUtils.makedirs(logdir)\n  FileUtils.makedirs(localedir)\n\n  InstallOptions.site_dir = sitelibdir\n  InstallOptions.codedir = codedir\n  InstallOptions.config_dir = configdir\n  InstallOptions.bin_dir = bindir\n  InstallOptions.man_dir = mandir\n  InstallOptions.var_dir = vardir\n  InstallOptions.public_dir = publicdir\n  InstallOptions.run_dir = rundir\n  InstallOptions.log_dir = logdir\n  InstallOptions.locale_dir = localedir\nend\n\n##\n# Join two paths. On Windows, dir must be converted to a relative path,\n# by stripping the drive letter, but only if the basedir is not empty.\n#\ndef join(basedir, dir)\n  return \"#{basedir}#{dir[2..-1]}\" if $osname == \"windows\" and basedir.length > 0 and dir.length > 2\n\n  \"#{basedir}#{dir}\"\nend\n\n##\n# Install file(s) from ./bin to RbConfig::CONFIG['bindir']. Patch it on the way\n# to insert a #! line; on a Unix install, the command is named as expected\n# (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under\n# windows, we add an '.rb' extension and let file associations do their stuff.\ndef install_binfile(from, op_file, target)\n  tmp_file = Tempfile.new('puppet-binfile')\n\n  if not InstallOptions.ruby.nil?\n    ruby = InstallOptions.ruby\n  else\n    ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])\n  end\n\n  File.open(from) do |ip|\n    File.open(tmp_file.path, \"w\") do |op|\n      op.puts \"#!#{ruby}\" unless $osname == \"windows\"\n      contents = ip.readlines\n      contents.shift if contents[0] =~ /^#!/\n      op.write contents.join\n    end\n  end\n\n  if $osname == \"windows\" && InstallOptions.batch_files\n    installed_wrapper = false\n\n    unless File.extname(from) =~ /\\.(cmd|bat)/\n      if File.exist?(\"#{from}.bat\")\n        FileUtils.install(\"#{from}.bat\", File.join(target, \"#{op_file}.bat\"), mode: 0755, preserve: true, verbose: true)\n        installed_wrapper = true\n      end\n\n      if File.exist?(\"#{from}.cmd\")\n        FileUtils.install(\"#{from}.cmd\", File.join(target, \"#{op_file}.cmd\"), mode: 0755, preserve: true, verbose: true)\n        installed_wrapper = true\n      end\n\n      if not installed_wrapper\n        tmp_file2 = Tempfile.new('puppet-wrapper')\n        cwv = <<-EOS\n@echo off\nSETLOCAL\nif exist \"%~dp0environment.bat\" (\n  call \"%~dp0environment.bat\" %0 %*\n) else (\n  SET \"PATH=%~dp0;%PATH%\"\n)\nruby.exe -S -- puppet %*\nEOS\n        File.open(tmp_file2.path, \"w\") { |cw| cw.puts cwv }\n        FileUtils.install(tmp_file2.path, File.join(target, \"#{op_file}.bat\"), mode: 0755, preserve: true, verbose: true)\n\n        tmp_file2.unlink\n      end\n    end\n  end\n  FileUtils.install(tmp_file.path, File.join(target, op_file), mode: 0755, preserve: true, verbose: true)\n  tmp_file.unlink\nend\n\n# Change directory into the puppet root so we don't get the wrong files for install.\nFileUtils.cd File.dirname(__FILE__) do\n  # Set these values to what you want installed.\n  configs = glob(%w{conf/puppet.conf conf/hiera.yaml})\n  bins  = glob(%w{bin/*})\n  man   = glob(%w{man/man[0-9]/*})\n  libs  = glob(%w{lib/**/*})\n  locales = glob(%w{locales/**/*})\n\n  prepare_installation\n\n  if $osname == \"windows\"\n    windows_bins = glob(%w{ext/windows/*bat})\n  end\n\n  do_configs(configs, InstallOptions.config_dir) if InstallOptions.configs\n  do_bins(bins, InstallOptions.bin_dir)\n  do_bins(windows_bins, InstallOptions.bin_dir, 'ext/windows/') if $osname == \"windows\" && InstallOptions.batch_files\n  do_libs(libs)\n  do_locales(locales)\n  do_man(man) unless $osname == \"windows\"\nend\n"
  },
  {
    "path": "lib/hiera/puppet_function.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera_puppet'\n\n# Provides the base class for the puppet functions hiera, hiera_array, hiera_hash, and hiera_include.\n# The actual function definitions will call init_dispatch and override the merge_type and post_lookup methods.\n#\n# @see hiera_array.rb, hiera_include.rb under lib/puppet/functions for sample usage\n#\nclass Hiera::PuppetFunction < Puppet::Functions::InternalFunction\n  def self.init_dispatch\n    dispatch :hiera_splat do\n      scope_param\n      param 'Tuple[String, Any, Any, 1, 3]', :args\n    end\n\n    dispatch :hiera_no_default do\n      scope_param\n      param 'String', :key\n    end\n\n    dispatch :hiera_with_default do\n      scope_param\n      param 'String', :key\n      param 'Any',   :default\n      optional_param 'Any', :override\n    end\n\n    dispatch :hiera_block1 do\n      scope_param\n      param 'String',              :key\n      block_param 'Callable[1,1]', :default_block\n    end\n\n    dispatch :hiera_block2 do\n      scope_param\n      param 'String',              :key\n      param 'Any',                 :override\n      block_param 'Callable[1,1]', :default_block\n    end\n  end\n\n  def hiera_splat(scope, args)\n    hiera(scope, *args)\n  end\n\n  def hiera_no_default(scope, key)\n    post_lookup(scope, key, lookup(scope, key, nil, false, nil))\n  end\n\n  def hiera_with_default(scope, key, default, override = nil)\n    post_lookup(scope, key, lookup(scope, key, default, true, override))\n  end\n\n  def hiera_block1(scope, key, &default_block)\n    post_lookup(scope, key, lookup(scope, key, nil, false, nil, &default_block))\n  end\n\n  def hiera_block2(scope, key, override, &default_block)\n    post_lookup(scope, key, lookup(scope, key, nil, false, override, &default_block))\n  end\n\n  def lookup(scope, key, default, has_default, override, &default_block)\n    unless Puppet[:strict] == :off\n      # TRANSLATORS 'lookup' is a puppet function and should not be translated\n      message = _(\"The function '%{class_name}' is deprecated in favor of using 'lookup'.\") % { class_name: self.class.name }\n      message += ' ' + _(\"See https://puppet.com/docs/puppet/%{minor_version}/deprecated_language.html\") %\n                       { minor_version: Puppet.minor_version }\n      Puppet.warn_once('deprecations', self.class.name, message)\n    end\n    lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {})\n    adapter = lookup_invocation.lookup_adapter\n    lookup_invocation.set_hiera_xxx_call\n    lookup_invocation.set_global_only unless adapter.global_only? || adapter.has_environment_data_provider?(lookup_invocation)\n    lookup_invocation.set_hiera_v3_location_overrides(override) unless override.nil? || override.is_a?(Array) && override.empty?\n    Puppet::Pops::Lookup.lookup(key, nil, default, has_default, merge_type, lookup_invocation, &default_block)\n  end\n\n  def merge_type\n    :first\n  end\n\n  def post_lookup(scope, key, result)\n    result\n  end\nend\n"
  },
  {
    "path": "lib/hiera/scope.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\nclass Hiera\n  class Scope\n    extend Forwardable\n\n    CALLING_CLASS = 'calling_class'\n    CALLING_CLASS_PATH = 'calling_class_path'\n    CALLING_MODULE = 'calling_module'\n    MODULE_NAME = 'module_name'\n\n    CALLING_KEYS = [CALLING_CLASS, CALLING_CLASS_PATH, CALLING_MODULE].freeze\n    EMPTY_STRING = ''\n\n    attr_reader :real\n\n    def initialize(real)\n      @real = real\n    end\n\n    def [](key)\n      case key\n      when CALLING_CLASS\n        ans = find_hostclass(@real)\n      when CALLING_CLASS_PATH\n        ans = find_hostclass(@real).gsub(/::/, '/')\n      when CALLING_MODULE\n        ans = safe_lookupvar(MODULE_NAME)\n      else\n        ans = safe_lookupvar(key)\n      end\n      ans == EMPTY_STRING ? nil : ans\n    end\n\n    # This method is used to handle the throw of :undefined_variable since when\n    # strict variables is not in effect, missing handling of the throw leads to\n    # a more expensive code path.\n    #\n    def safe_lookupvar(key)\n      reason = catch :undefined_variable do\n        return @real.lookupvar(key)\n      end\n\n      case Puppet[:strict]\n      when :off\n        # do nothing\n      when :warning\n        Puppet.warn_once(Puppet::Parser::Scope::UNDEFINED_VARIABLES_KIND, _(\"Variable: %{name}\") % { name: key },\n                         _(\"Undefined variable '%{name}'; %{reason}\") % { name: key, reason: reason })\n      when :error\n        raise ArgumentError, _(\"Undefined variable '%{name}'; %{reason}\") % { name: key, reason: reason }\n      end\n      nil\n    end\n    private :safe_lookupvar\n\n    def exist?(key)\n      CALLING_KEYS.include?(key) || @real.exist?(key)\n    end\n\n    def include?(key)\n      CALLING_KEYS.include?(key) || @real.include?(key)\n    end\n\n    def catalog\n      @real.catalog\n    end\n\n    def resource\n      @real.resource\n    end\n\n    def compiler\n      @real.compiler\n    end\n\n    def find_hostclass(scope)\n      if scope.source and scope.source.type == :hostclass\n        scope.source.name.downcase\n      elsif scope.parent\n        find_hostclass(scope.parent)\n      else\n        nil\n      end\n    end\n    private :find_hostclass\n\n    # This is needed for type conversion to work\n    def_delegators :@real, :call_function\n  end\nend\n"
  },
  {
    "path": "lib/hiera_puppet.rb",
    "content": "# frozen_string_literal: true\n\nPuppet.features.hiera?\nrequire 'hiera/scope'\nrequire_relative 'puppet'\n\nmodule HieraPuppet\n  module_function\n\n  def lookup(key, default, scope, override, resolution_type)\n    scope = Hiera::Scope.new(scope)\n\n    answer = hiera.lookup(key, default, scope, override, resolution_type)\n\n    if answer.nil?\n      raise Puppet::ParseError, _(\"Could not find data item %{key} in any Hiera data file and no default supplied\") % { key: key }\n    end\n\n    answer\n  end\n\n  def parse_args(args)\n    # Functions called from Puppet manifests like this:\n    #\n    #   hiera(\"foo\", \"bar\")\n    #\n    # Are invoked internally after combining the positional arguments into a\n    # single array:\n    #\n    #   func = function_hiera\n    #   func([\"foo\", \"bar\"])\n    #\n    # Functions called from templates preserve the positional arguments:\n    #\n    #   scope.function_hiera(\"foo\", \"bar\")\n    #\n    # Deal with Puppet's special calling mechanism here.\n    if args[0].is_a?(Array)\n      args = args[0]\n    end\n\n    if args.empty?\n      raise Puppet::ParseError, _(\"Please supply a parameter to perform a Hiera lookup\")\n    end\n\n    key      = args[0]\n    default  = args[1]\n    override = args[2]\n\n    [key, default, override]\n  end\n\n  def hiera\n    @hiera ||= Hiera.new(:config => hiera_config)\n  end\n\n  def hiera_config\n    config = {}\n\n    config_file = hiera_config_file\n    if config_file\n      config = Hiera::Config.load(config_file)\n    end\n\n    config[:logger] = 'puppet'\n    config\n  end\n\n  def hiera_config_file\n    hiera_config = Puppet.settings[:hiera_config]\n    if Puppet::FileSystem.exist?(hiera_config)\n      hiera_config\n    else\n      Puppet.warning _(\"Config file %{hiera_config} not found, using Hiera defaults\") % { hiera_config: hiera_config }\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/agent/disabler.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/json_lockfile'\n\n# This module is responsible for encapsulating the logic for\n#  \"disabling\" the puppet agent during a run; in other words,\n#  keeping track of enough state to answer the question\n#  \"has the puppet agent been administratively disabled?\"\n#\n# The implementation involves writing a lockfile with JSON\n#  contents, and is considered part of the public Puppet API\n#  because it used by external tools such as mcollective.\n#\n# For more information, please see docs on the website.\n#  http://links.puppet.com/agent_lockfiles\nmodule Puppet::Agent::Disabler\n  DISABLED_MESSAGE_JSON_KEY = \"disabled_message\"\n\n  # Let the daemon run again, freely in the filesystem.\n  def enable\n    Puppet.notice _(\"Enabling Puppet.\")\n    disable_lockfile.unlock\n  end\n\n  # Stop the daemon from making any catalog runs.\n  def disable(msg = nil)\n    data = {}\n    Puppet.notice _(\"Disabling Puppet.\")\n    unless msg.nil?\n      data[DISABLED_MESSAGE_JSON_KEY] = msg\n    end\n    disable_lockfile.lock(data)\n  end\n\n  def disabled?\n    disable_lockfile.locked?\n  end\n\n  def disable_message\n    data = disable_lockfile.lock_data\n    return nil if data.nil?\n    if data.has_key?(DISABLED_MESSAGE_JSON_KEY)\n      return data[DISABLED_MESSAGE_JSON_KEY]\n    end\n\n    nil\n  end\n\n  def disable_lockfile\n    @disable_lockfile ||= Puppet::Util::JsonLockfile.new(Puppet[:agent_disabled_lockfile])\n\n    @disable_lockfile\n  end\n  private :disable_lockfile\nend\n"
  },
  {
    "path": "lib/puppet/agent/locker.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/pidlock'\nrequire_relative '../../puppet/error'\n\n# This module is responsible for encapsulating the logic for \"locking\" the\n# puppet agent during a catalog run; in other words, keeping track of enough\n# state to answer the question \"is there a puppet agent currently applying a\n# catalog?\"\n#\n# The implementation involves writing a lockfile whose contents are simply the\n# PID of the running agent process.  This is considered part of the public\n# Puppet API because it used by external tools such as mcollective.\n#\n# For more information, please see docs on the website.\n#  http://links.puppet.com/agent_lockfiles\nmodule Puppet::Agent::Locker\n  # Yield if we get a lock, else raise Puppet::LockError. Return\n  # value of block yielded.\n  def lock\n    if lockfile.lock\n      begin\n        yield\n      ensure\n        lockfile.unlock\n      end\n    else\n      fail Puppet::LockError, _('Failed to acquire lock')\n    end\n  end\n\n  def running?\n    lockfile.locked?\n  end\n\n  def lockfile_path\n    @lockfile_path ||= Puppet[:agent_catalog_run_lockfile]\n  end\n\n  def lockfile\n    @lockfile ||= Puppet::Util::Pidlock.new(lockfile_path)\n\n    @lockfile\n  end\n  private :lockfile\nend\n"
  },
  {
    "path": "lib/puppet/agent.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/application'\nrequire_relative '../puppet/error'\nrequire_relative '../puppet/util/at_fork'\n\nrequire 'timeout'\n\n# A general class for triggering a run of another\n# class.\nclass Puppet::Agent\n  require_relative 'agent/locker'\n  include Puppet::Agent::Locker\n\n  require_relative 'agent/disabler'\n  include Puppet::Agent::Disabler\n\n  require_relative '../puppet/util/splayer'\n  include Puppet::Util::Splayer\n\n  # Special exception class used to signal an agent run has timed out.\n  class RunTimeoutError < Exception # rubocop:disable Lint/InheritException\n  end\n\n  attr_reader :client_class, :client, :should_fork\n\n  def initialize(client_class, should_fork = true)\n    @should_fork = can_fork? && should_fork\n    @client_class = client_class\n  end\n\n  def can_fork?\n    Puppet.features.posix? && RUBY_PLATFORM != 'java'\n  end\n\n  def needing_restart?\n    Puppet::Application.restart_requested?\n  end\n\n  # Perform a run with our client.\n  def run(client_options = {})\n    if disabled?\n      log_disabled_message\n      return\n    end\n\n    result = nil\n    wait_for_lock_deadline = nil\n    block_run = Puppet::Application.controlled_run do\n      # splay may sleep for awhile when running onetime! If not onetime, then\n      # the job scheduler splays (only once) so that agents assign themselves a\n      # slot within the splay interval.\n      do_splay = client_options.fetch(:splay, Puppet[:splay])\n      if do_splay\n        splay(do_splay)\n\n        if disabled?\n          log_disabled_message\n          break\n        end\n      end\n\n      # waiting for certs may sleep for awhile depending on onetime, waitforcert and maxwaitforcert!\n      # this needs to happen before forking so that if we fail to obtain certs and try to exit, then\n      # we exit the main process and not the forked child.\n      ssl_context = wait_for_certificates(client_options)\n\n      result = run_in_fork(should_fork) do\n        with_client(client_options[:transaction_uuid], client_options[:job_id]) do |client|\n          client_args = client_options.merge(:pluginsync => Puppet::Configurer.should_pluginsync?)\n          begin\n            # lock may sleep for awhile depending on waitforlock and maxwaitforlock!\n            lock do\n              if disabled?\n                log_disabled_message\n                nil\n              else\n                # NOTE: Timeout is pretty heinous as the location in which it\n                # throws an error is entirely unpredictable, which means that\n                # it can interrupt code blocks that perform cleanup or enforce\n                # sanity. The only thing a Puppet agent should do after this\n                # error is thrown is die with as much dignity as possible.\n                Timeout.timeout(Puppet[:runtimeout], RunTimeoutError) do\n                  Puppet.override(ssl_context: ssl_context) do\n                    client.run(client_args)\n                  end\n                end\n              end\n            end\n          rescue Puppet::LockError\n            now = Time.now.to_i\n            wait_for_lock_deadline ||= now + Puppet[:maxwaitforlock]\n\n            if Puppet[:waitforlock] < 1\n              Puppet.notice _(\"Run of %{client_class} already in progress; skipping  (%{lockfile_path} exists)\") % { client_class: client_class, lockfile_path: lockfile_path }\n              nil\n            elsif now >= wait_for_lock_deadline\n              Puppet.notice _(\"Exiting now because the maxwaitforlock timeout has been exceeded.\")\n              nil\n            else\n              Puppet.info _(\"Another puppet instance is already running; --waitforlock flag used, waiting for running instance to finish.\")\n              Puppet.info _(\"Will try again in %{time} seconds.\") % { time: Puppet[:waitforlock] }\n              sleep Puppet[:waitforlock]\n              retry\n            end\n          rescue RunTimeoutError => detail\n            Puppet.log_exception(detail, _(\"Execution of %{client_class} did not complete within %{runtimeout} seconds and was terminated.\") %\n              { client_class: client_class, runtimeout: Puppet[:runtimeout] })\n            nil\n          rescue StandardError => detail\n            Puppet.log_exception(detail, _(\"Could not run %{client_class}: %{detail}\") % { client_class: client_class, detail: detail })\n            nil\n          ensure\n            Puppet.runtime[:http].close\n          end\n        end\n      end\n      true\n    end\n    Puppet.notice _(\"Shutdown/restart in progress (%{status}); skipping run\") % { status: Puppet::Application.run_status.inspect } unless block_run\n    result\n  end\n\n  def stopping?\n    Puppet::Application.stop_requested?\n  end\n\n  def run_in_fork(forking = true)\n    return yield unless forking or Puppet.features.windows?\n\n    atForkHandler = Puppet::Util::AtFork.get_handler\n\n    atForkHandler.prepare\n\n    begin\n      child_pid = Kernel.fork do\n        atForkHandler.child\n        $0 = _(\"puppet agent: applying configuration\")\n        begin\n          exit(yield || 1)\n        rescue NoMemoryError\n          exit(254)\n        end\n      end\n    ensure\n      atForkHandler.parent\n    end\n\n    exit_code = Process.waitpid2(child_pid)\n    exit_code[1].exitstatus\n  end\n\n  private\n\n  # Create and yield a client instance, keeping a reference\n  # to it during the yield.\n  def with_client(transaction_uuid, job_id = nil)\n    begin\n      @client = client_class.new(transaction_uuid, job_id)\n    rescue StandardError => detail\n      Puppet.log_exception(detail, _(\"Could not create instance of %{client_class}: %{detail}\") % { client_class: client_class, detail: detail })\n      return\n    end\n    yield @client\n  ensure\n    @client = nil\n  end\n\n  def wait_for_certificates(options)\n    waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : Puppet[:waitforcert])\n    sm = Puppet::SSL::StateMachine.new(waitforcert: waitforcert, onetime: Puppet[:onetime])\n    sm.ensure_client_certificate\n  end\n\n  def log_disabled_message\n    Puppet.notice _(\"Skipping run of %{client_class}; administratively disabled (Reason: '%{disable_message}');\\nUse 'puppet agent --enable' to re-enable.\") % { client_class: client_class, disable_message: disable_message }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/agent.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/daemon'\nrequire_relative '../../puppet/util/pidlock'\nrequire_relative '../../puppet/agent'\nrequire_relative '../../puppet/configurer'\nrequire_relative '../../puppet/ssl/oids'\n\nclass Puppet::Application::Agent < Puppet::Application\n  run_mode :agent\n\n  def app_defaults\n    super.merge({\n                  :catalog_terminus => :rest,\n                  :catalog_cache_terminus => :json,\n                  :node_terminus => :rest,\n                  :facts_terminus => :facter,\n                })\n  end\n\n  def preinit\n    # Do an initial trap, so that cancels don't get a stack trace.\n    Signal.trap(:INT) do\n      $stderr.puts _(\"Cancelling startup\")\n      exit(0)\n    end\n\n    {\n      :waitforcert => nil,\n      :detailed_exitcodes => false,\n      :verbose => false,\n      :debug => false,\n      :setdest => false,\n      :enable => false,\n      :disable => false,\n      :fqdn => nil,\n      :serve => [],\n      :digest => 'SHA256',\n      :graph => true,\n      :fingerprint => false,\n      :sourceaddress => nil,\n      :start_time => Time.now,\n    }.each do |opt, val|\n      options[opt] = val\n    end\n\n    @argv = ARGV.dup\n  end\n\n  option(\"--disable [MESSAGE]\") do |message|\n    options[:disable] = true\n    options[:disable_message] = message\n  end\n\n  option(\"--enable\")\n  option(\"--debug\", \"-d\")\n  option(\"--fqdn FQDN\", \"-f\")\n  option(\"--test\", \"-t\")\n  option(\"--verbose\", \"-v\")\n\n  option(\"--fingerprint\")\n  option(\"--digest DIGEST\")\n\n  option(\"--sourceaddress IP_ADDRESS\")\n\n  option(\"--detailed-exitcodes\") do |_arg|\n    options[:detailed_exitcodes] = true\n  end\n\n  option(\"--logdest DEST\", \"-l DEST\") do |arg|\n    handle_logdest_arg(arg)\n  end\n\n  option(\"--waitforcert WAITFORCERT\", \"-w\") do |arg|\n    options[:waitforcert] = arg.to_i\n  end\n\n  option(\"--job-id ID\") do |arg|\n    options[:job_id] = arg\n  end\n\n  def summary\n    _(\"The puppet agent daemon\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-agent(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Retrieves the client configuration from the Puppet master and applies it to\n      the local host.\n\n      This service may be run as a daemon, run periodically using cron (or something\n      similar), or run interactively for testing purposes.\n\n\n      USAGE\n      -----\n      puppet agent [--certname <NAME>] [-D|--daemonize|--no-daemonize]\n        [-d|--debug] [--detailed-exitcodes] [--digest <DIGEST>] [--disable [MESSAGE]] [--enable]\n        [--fingerprint] [-h|--help] [-l|--logdest syslog|eventlog|<ABS FILEPATH>|console]\n        [--serverport <PORT>] [--noop] [-o|--onetime] [--sourceaddress <IP_ADDRESS>] [-t|--test]\n        [-v|--verbose] [-V|--version] [-w|--waitforcert <SECONDS>]\n\n\n      DESCRIPTION\n      -----------\n      This is the main puppet client. Its job is to retrieve the local\n      machine's configuration from a remote server and apply it. In order to\n      successfully communicate with the remote server, the client must have a\n      certificate signed by a certificate authority that the server trusts;\n      the recommended method for this, at the moment, is to run a certificate\n      authority as part of the puppet server (which is the default). The\n      client will connect and request a signed certificate, and will continue\n      connecting until it receives one.\n\n      Once the client has a signed certificate, it will retrieve its\n      configuration and apply it.\n\n\n      USAGE NOTES\n      -----------\n      'puppet agent' does its best to find a compromise between interactive\n      use and daemon use. If you run it with no arguments and no configuration, it\n      goes into the background, attempts to get a signed certificate, and retrieves\n      and applies its configuration every 30 minutes.\n\n      Some flags are meant specifically for interactive use --- in particular,\n      'test', 'tags' and 'fingerprint' are useful.\n\n      '--test' runs once in the foreground with verbose logging, then exits.\n      It also exits if it can't get a valid catalog. `--test` includes the\n      '--detailed-exitcodes' option by default and exits with one of the following\n      exit codes:\n\n      * 0: The run succeeded with no changes or failures; the system was already in\n           the desired state.\n      * 1: The run failed, or wasn't attempted due to another run already in progress.\n      * 2: The run succeeded, and some resources were changed.\n      * 4: The run succeeded, and some resources failed.\n      * 6: The run succeeded, and included both changes and failures.\n\n      '--tags' allows you to specify what portions of a configuration you want\n      to apply. Puppet elements are tagged with all of the class or definition\n      names that contain them, and you can use the 'tags' flag to specify one\n      of these names, causing only configuration elements contained within\n      that class or definition to be applied. This is very useful when you are\n      testing new configurations --- for instance, if you are just starting to\n      manage 'ntpd', you would put all of the new elements into an 'ntpd'\n      class, and call puppet with '--tags ntpd', which would only apply that\n      small portion of the configuration during your testing, rather than\n      applying the whole thing.\n\n      '--fingerprint' is a one-time flag. In this mode 'puppet agent' runs\n      once and displays on the console (and in the log) the current certificate\n      (or certificate request) fingerprint. Providing the '--digest' option\n      allows you to use a different digest algorithm to generate the fingerprint.\n      The main use is to verify that before signing a certificate request on\n      the master, the certificate request the master received is the same as\n      the one the client sent (to prevent against man-in-the-middle attacks\n      when signing certificates).\n\n      '--skip_tags' is a flag used to filter resources. If this is set, then\n      only resources not tagged with the specified tags will be applied.\n      Values must be comma-separated.\n\n\n      OPTIONS\n      -------\n\n      Note that any Puppet setting that's valid in the configuration file is also a\n      valid long argument. For example, 'server' is a valid setting, so you can\n      specify '--server <servername>' as an argument. Boolean settings accept a '--no-'\n      prefix to turn off a behavior, translating into '--setting' and '--no-setting'\n      pairs, such as `--daemonize` and `--no-daemonize`.\n\n      See the configuration file documentation at\n      https://puppet.com/docs/puppet/latest/configuration.html for the\n      full list of acceptable settings. A commented list of all settings can also be\n      generated by running puppet agent with '--genconfig'.\n\n      * --certname:\n        Set the certname (unique ID) of the client. The master reads this\n        unique identifying string, which is usually set to the node's\n        fully-qualified domain name, to determine which configurations the\n        node will receive. Use this option to debug setup problems or\n        implement unusual node identification schemes.\n        (This is a Puppet setting, and can go in puppet.conf.)\n\n      * --daemonize:\n        Send the process into the background. This is the default.\n        (This is a Puppet setting, and can go in puppet.conf. Note the special 'no-'\n        prefix for boolean settings on the command line.)\n\n      * --no-daemonize:\n        Do not send the process into the background.\n        (This is a Puppet setting, and can go in puppet.conf. Note the special 'no-'\n        prefix for boolean settings on the command line.)\n\n      * --debug:\n        Enable full debugging.\n\n      * --detailed-exitcodes:\n        Provide extra information about the run via exit codes; works only if '--test'\n        or '--onetime' is also specified. If enabled, 'puppet agent' uses the\n        following exit codes:\n\n        0: The run succeeded with no changes or failures; the system was already in\n        the desired state.\n\n        1: The run failed, or wasn't attempted due to another run already in progress.\n\n        2: The run succeeded, and some resources were changed.\n\n        4: The run succeeded, and some resources failed.\n\n        6: The run succeeded, and included both changes and failures.\n\n      * --digest:\n        Change the certificate fingerprinting digest algorithm. The default is\n        SHA256. Valid values depends on the version of OpenSSL installed, but\n        will likely contain MD5, MD2, SHA1 and SHA256.\n\n      * --disable:\n        Disable working on the local system. This puts a lock file in place,\n        causing 'puppet agent' not to work on the system until the lock file\n        is removed. This is useful if you are testing a configuration and do\n        not want the central configuration to override the local state until\n        everything is tested and committed.\n\n        Disable can also take an optional message that will be reported by the\n        'puppet agent' at the next disabled run.\n\n        'puppet agent' uses the same lock file while it is running, so no more\n        than one 'puppet agent' process is working at a time.\n\n        'puppet agent' exits after executing this.\n\n      * --enable:\n        Enable working on the local system. This removes any lock file,\n        causing 'puppet agent' to start managing the local system again\n        However, it continues to use its normal scheduling, so it might\n        not start for another half hour.\n\n        'puppet agent' exits after executing this.\n\n      *  --evaltrace:\n        Logs each resource as it is being evaluated. This allows you to interactively\n        see exactly what is being done. (This is a Puppet setting, and can go in\n        puppet.conf. Note the special 'no-' prefix for boolean settings on the command line.)\n\n      * --fingerprint:\n        Display the current certificate or certificate signing request\n        fingerprint and then exit. Use the '--digest' option to change the\n        digest algorithm used.\n\n      * --help:\n        Print this help message\n\n      * --job-id:\n        Attach the specified job id to the catalog request and the report used for\n        this agent run. This option only works when '--onetime' is used.  When using\n        Puppet Enterprise this flag should not be used as the orchestrator sets the\n        job-id for you and it must be unique.\n\n      * --logdest:\n        Where to send log messages. Choose between 'syslog' (the POSIX syslog\n        service), 'eventlog' (the Windows Event Log), 'console', or the path to a log\n        file. If debugging or verbosity is enabled, this defaults to 'console'.\n        Otherwise, it defaults to 'syslog' on POSIX systems and 'eventlog' on Windows.\n        Multiple destinations can be set using a comma separated list\n        (eg: `/path/file1,console,/path/file2`)\"\n\n        A path ending with '.json' will receive structured output in JSON format. The\n        log file will not have an ending ']' automatically written to it due to the\n        appending nature of logging. It must be appended manually to make the content\n        valid JSON.\n\n        A path ending with '.jsonl' will receive structured output in JSON Lines\n        format.\n\n      * --masterport:\n        The port on which to contact the Puppet Server.\n        (This is a Puppet setting, and can go in puppet.conf.\n        Deprecated in favor of the 'serverport' setting.)\n\n      * --noop:\n        Use 'noop' mode where the daemon runs in a no-op or dry-run mode. This\n        is useful for seeing what changes Puppet would make without actually\n        executing the changes.\n        (This is a Puppet setting, and can go in puppet.conf. Note the special 'no-'\n        prefix for boolean settings on the command line.)\n\n      * --onetime:\n        Run the configuration once. Runs a single (normally daemonized) Puppet\n        run. Useful for interactively running puppet agent when used in\n        conjunction with the --no-daemonize option.\n        (This is a Puppet setting, and can go in puppet.conf. Note the special 'no-'\n        prefix for boolean settings on the command line.)\n\n      * --serverport:\n        The port on which to contact the Puppet Server.\n        (This is a Puppet setting, and can go in puppet.conf.)\n\n      * --sourceaddress:\n        Set the source IP address for transactions. This defaults to automatically selected.\n        (This is a Puppet setting, and can go in puppet.conf.)\n\n      * --test:\n        Enable the most common options used for testing. These are 'onetime',\n        'verbose', 'no-daemonize', 'no-usecacheonfailure', 'detailed-exitcodes',\n        'no-splay', and 'show_diff'.\n\n      * --trace\n        Prints stack traces on some errors. (This is a Puppet setting, and can go in\n        puppet.conf. Note the special 'no-' prefix for boolean settings on the command line.)\n\n      * --verbose:\n        Turn on verbose reporting.\n\n      * --version:\n        Print the puppet version number and exit.\n\n      * --waitforcert:\n        This option only matters for daemons that do not yet have certificates\n        and it is enabled by default, with a value of 120 (seconds). This\n        causes 'puppet agent' to connect to the server every 2 minutes and ask\n        it to sign a certificate request. This is useful for the initial setup\n        of a puppet client. You can turn off waiting for certificates by\n        specifying a time of 0.\n        (This is a Puppet setting, and can go in puppet.conf.)\n\n      * --write_catalog_summary\n        After compiling the catalog saves the resource list and classes list to the node\n        in the state directory named classes.txt and resources.txt\n        (This is a Puppet setting, and can go in puppet.conf.)\n\n      EXAMPLE\n      -------\n          $ puppet agent --server puppet.domain.com\n\n\n      DIAGNOSTICS\n      -----------\n\n      Puppet agent accepts the following signals:\n\n      * SIGHUP:\n        Restart the puppet agent daemon.\n      * SIGINT and SIGTERM:\n        Shut down the puppet agent daemon.\n      * SIGUSR1:\n        Immediately retrieve and apply configurations from the puppet master.\n      * SIGUSR2:\n        Close file descriptors for log files and reopen them. Used with logrotate.\n\n      AUTHOR\n      ------\n      Luke Kanies\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def run_command\n    if options[:fingerprint]\n      fingerprint\n    else\n      # It'd be nice to daemonize later, but we have to daemonize before\n      # waiting for certificates so that we don't block\n      daemon = daemonize_process_when(Puppet[:daemonize])\n\n      # Setup signal traps immediately after daemonization so we clean up the daemon\n      daemon.set_signal_traps\n\n      log_config if Puppet[:daemonize]\n\n      # Each application is responsible for pushing loaders onto the context.\n      # Use the current environment that has already been established, though\n      # it may change later during the configurer run.\n      env = Puppet.lookup(:current_environment)\n      Puppet.override(current_environment: env,\n                      loaders: Puppet::Pops::Loaders.new(env, true)) do\n        if Puppet[:onetime]\n          onetime(daemon)\n        else\n          main(daemon)\n        end\n      end\n    end\n  end\n\n  def log_config\n    # skip also config reading and parsing if debug is not enabled\n    return unless Puppet::Util::Log.sendlevel?(:debug)\n\n    Puppet.settings.stringify_settings(:agent, :all).each_pair do |k, v|\n      next if k.include?(\"password\") || v.to_s.empty?\n\n      Puppet.debug(\"Using setting: #{k}=#{v}\")\n    end\n  end\n\n  # rubocop:disable Lint/RedundantStringCoercion\n  def fingerprint\n    Puppet::Util::Log.newdestination(:console)\n    cert_provider = Puppet::X509::CertProvider.new\n    client_cert = cert_provider.load_client_cert(Puppet[:certname])\n    if client_cert\n      puts Puppet::SSL::Digest.new(options[:digest].to_s, client_cert.to_der).to_s\n    else\n      csr = cert_provider.load_request(Puppet[:certname])\n      if csr\n        puts Puppet::SSL::Digest.new(options[:digest].to_s, csr.to_der).to_s\n      else\n        $stderr.puts _(\"Fingerprint asked but neither the certificate, nor the certificate request have been issued\")\n        exit(1)\n      end\n    end\n  rescue => e\n    Puppet.log_exception(e, _(\"Failed to generate fingerprint: %{message}\") % { message: e.message })\n    exit(1)\n  end\n  # rubocop:enable Lint/RedundantStringCoercion\n\n  def onetime(daemon)\n    begin\n      exitstatus = daemon.agent.run({ :job_id => options[:job_id], :start_time => options[:start_time], :waitforcert => options[:waitforcert] })\n    rescue => detail\n      Puppet.log_exception(detail)\n    end\n\n    daemon.stop(:exit => false)\n\n    if !exitstatus\n      exit(1)\n    elsif options[:detailed_exitcodes] then\n      exit(exitstatus)\n    else\n      exit(0)\n    end\n  end\n\n  def main(daemon)\n    Puppet.notice _(\"Starting Puppet client version %{version}\") % { version: Puppet.version }\n    daemon.start\n  end\n\n  # Enable all of the most common test options.\n  def setup_test\n    Puppet.settings.handlearg(\"--no-usecacheonfailure\")\n    Puppet.settings.handlearg(\"--no-splay\")\n    Puppet.settings.handlearg(\"--show_diff\")\n    Puppet.settings.handlearg(\"--no-daemonize\")\n    options[:verbose] = true\n    Puppet[:onetime] = true\n    options[:detailed_exitcodes] = true\n  end\n\n  def setup\n    raise ArgumentError, _(\"The puppet agent command does not take parameters\") unless command_line.args.empty?\n\n    setup_test if options[:test]\n\n    setup_logs\n\n    exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?\n\n    Puppet::SSL::Oids.register_puppet_oids\n\n    if options[:fqdn]\n      Puppet[:certname] = options[:fqdn]\n    end\n\n    Puppet.settings.use :main, :agent, :ssl\n\n    Puppet::Transaction::Report.indirection.terminus_class = :rest\n    # we want the last report to be persisted locally\n    Puppet::Transaction::Report.indirection.cache_class = :yaml\n\n    if Puppet[:catalog_cache_terminus]\n      Puppet::Resource::Catalog.indirection.cache_class = Puppet[:catalog_cache_terminus]\n    end\n\n    # In fingerprint mode we don't need to set up the whole agent\n    unless options[:fingerprint]\n      setup_agent\n    end\n  end\n\n  private\n\n  def enable_disable_client(agent)\n    if options[:enable]\n      agent.enable\n    elsif options[:disable]\n      agent.disable(options[:disable_message] || 'reason not specified')\n    end\n    exit(0)\n  end\n\n  def setup_agent\n    agent = Puppet::Agent.new(Puppet::Configurer, !(Puppet[:onetime]))\n\n    enable_disable_client(agent) if options[:enable] or options[:disable]\n\n    @agent = agent\n  end\n\n  def daemonize_process_when(should_daemonize)\n    daemon = Puppet::Daemon.new(@agent, Puppet::Util::Pidlock.new(Puppet[:pidfile]))\n    daemon.argv = @argv\n\n    daemon.daemonize if should_daemonize\n\n    daemon\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/apply.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/configurer'\nrequire_relative '../../puppet/util/profiler/aggregate'\n\nclass Puppet::Application::Apply < Puppet::Application\n  require_relative '../../puppet/util/splayer'\n  include Puppet::Util::Splayer\n\n  option(\"--debug\", \"-d\")\n  option(\"--execute EXECUTE\", \"-e\") do |arg|\n    options[:code] = arg\n  end\n  option(\"--loadclasses\", \"-L\")\n  option(\"--test\", \"-t\")\n  option(\"--verbose\", \"-v\")\n  option(\"--use-nodes\")\n  option(\"--detailed-exitcodes\")\n\n  option(\"--write-catalog-summary\") do |arg|\n    Puppet[:write_catalog_summary] = arg\n  end\n\n  option(\"--catalog catalog\", \"-c catalog\") do |arg|\n    options[:catalog] = arg\n  end\n\n  option(\"--logdest LOGDEST\", \"-l\") do |arg|\n    handle_logdest_arg(arg)\n  end\n\n  option(\"--parseonly\") do |_args|\n    puts \"--parseonly has been removed. Please use 'puppet parser validate <manifest>'\"\n    exit 1\n  end\n\n  def summary\n    _(\"Apply Puppet manifests locally\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-apply(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Applies a standalone Puppet manifest to the local system.\n\n\n      USAGE\n      -----\n      puppet apply [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]\n        [-e|--execute] [--detailed-exitcodes] [-L|--loadclasses]\n        [-l|--logdest syslog|eventlog|<ABS FILEPATH>|console] [--noop]\n        [--catalog <catalog>] [--write-catalog-summary] <file>\n\n\n      DESCRIPTION\n      -----------\n      This is the standalone puppet execution tool; use it to apply\n      individual manifests.\n\n      When provided with a modulepath, via command line or config file, puppet\n      apply can effectively mimic the catalog that would be served by puppet\n      master with access to the same modules, although there are some subtle\n      differences. When combined with scheduling and an automated system for\n      pushing manifests, this can be used to implement a serverless Puppet\n      site.\n\n      Most users should use 'puppet agent' and 'puppet master' for site-wide\n      manifests.\n\n\n      OPTIONS\n      -------\n      Any setting that's valid in the configuration\n      file is a valid long argument for puppet apply. For example, 'tags' is a\n      valid setting, so you can specify '--tags <class>,<tag>'\n      as an argument.\n\n      See the configuration file documentation at\n      https://puppet.com/docs/puppet/latest/configuration.html for the\n      full list of acceptable parameters. You can generate a commented list of all\n      configuration options by running puppet with\n      '--genconfig'.\n\n      * --debug:\n        Enable full debugging.\n\n      * --detailed-exitcodes:\n        Provide extra information about the run via exit codes. If enabled, 'puppet\n        apply' will use the following exit codes:\n\n        0: The run succeeded with no changes or failures; the system was already in\n        the desired state.\n\n        1: The run failed.\n\n        2: The run succeeded, and some resources were changed.\n\n        4: The run succeeded, and some resources failed.\n\n        6: The run succeeded, and included both changes and failures.\n\n      * --help:\n        Print this help message\n\n      * --loadclasses:\n        Load any stored classes. 'puppet agent' caches configured classes\n        (usually at /etc/puppetlabs/puppet/classes.txt), and setting this option causes\n        all of those classes to be set in your puppet manifest.\n\n      * --logdest:\n        Where to send log messages. Choose between 'syslog' (the POSIX syslog\n        service), 'eventlog' (the Windows Event Log), 'console', or the path to a log\n        file. Defaults to 'console'.\n        Multiple destinations can be set using a comma separated list\n        (eg: `/path/file1,console,/path/file2`)\"\n\n        A path ending with '.json' will receive structured output in JSON format. The\n        log file will not have an ending ']' automatically written to it due to the\n        appending nature of logging. It must be appended manually to make the content\n        valid JSON.\n\n        A path ending with '.jsonl' will receive structured output in JSON Lines\n        format.\n\n      * --noop:\n        Use 'noop' mode where Puppet runs in a no-op or dry-run mode. This\n        is useful for seeing what changes Puppet will make without actually\n        executing the changes.\n\n      * --execute:\n        Execute a specific piece of Puppet code\n\n      * --test:\n        Enable the most common options used for testing. These are 'verbose',\n        'detailed-exitcodes' and 'show_diff'.\n\n      * --verbose:\n        Print extra information.\n\n      * --catalog:\n        Apply a JSON catalog (such as one generated with 'puppet master --compile'). You can\n        either specify a JSON file or pipe in JSON from standard input.\n\n      * --write-catalog-summary\n        After compiling the catalog saves the resource list and classes list to the node\n        in the state directory named classes.txt and resources.txt\n\n      EXAMPLE\n      -------\n          $ puppet apply -e 'notify { \"hello world\": }'\n          $ puppet apply -l /tmp/manifest.log manifest.pp\n          $ puppet apply --modulepath=/root/dev/modules -e \"include ntpd::server\"\n          $ puppet apply --catalog catalog.json\n\n\n      AUTHOR\n      ------\n      Luke Kanies\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def app_defaults\n    super.merge({\n                  :default_file_terminus => :file_server,\n                  :write_catalog_summary => false\n                })\n  end\n\n  def run_command\n    if options[:catalog]\n      apply\n    else\n      main\n    end\n  ensure\n    if @profiler\n      Puppet::Util::Profiler.remove_profiler(@profiler)\n      @profiler.shutdown\n    end\n  end\n\n  def apply\n    if options[:catalog] == \"-\"\n      text = $stdin.read\n    else\n      text = Puppet::FileSystem.read(options[:catalog], :encoding => 'utf-8')\n    end\n    env = Puppet.lookup(:environments).get(Puppet[:environment])\n    Puppet.override(:current_environment => env, :loaders => create_loaders(env)) do\n      catalog = read_catalog(text)\n      apply_catalog(catalog)\n    end\n  end\n\n  def main\n    # rubocop:disable Layout/ExtraSpacing\n    manifest          = get_manifest() # Get either a manifest or nil if apply should use content of Puppet[:code]\n    splay                              # splay if needed\n    facts             = get_facts()    # facts or nil\n    node              = get_node()     # node or error\n    apply_environment = get_configured_environment(node, manifest)\n    # rubocop:enable Layout/ExtraSpacing\n\n    # TRANSLATORS \"puppet apply\" is a program command and should not be translated\n    Puppet.override({ :current_environment => apply_environment, :loaders => create_loaders(apply_environment) }, _(\"For puppet apply\")) do\n      configure_node_facts(node, facts)\n\n      # Allow users to load the classes that puppet agent creates.\n      if options[:loadclasses]\n        file = Puppet[:classfile]\n        if Puppet::FileSystem.exist?(file)\n          unless FileTest.readable?(file)\n            $stderr.puts _(\"%{file} is not readable\") % { file: file }\n            exit(63)\n          end\n          node.classes = Puppet::FileSystem.read(file, :encoding => 'utf-8').split(/\\s+/)\n        end\n      end\n\n      begin\n        # Compile the catalog\n        starttime = Time.now\n\n        # When compiling, the compiler traps and logs certain errors\n        # Those that do not lead to an immediate exit are caught by the general\n        # rule and gets logged.\n        #\n        catalog =\n          begin\n            Puppet::Resource::Catalog.indirection.find(node.name, :use_node => node)\n          rescue Puppet::Error\n            # already logged and handled by the compiler, including Puppet::ParseErrorWithIssue\n            exit(1)\n          end\n\n        # Resolve all deferred values and replace them / mutate the catalog\n        Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(node.facts, catalog, apply_environment, Puppet[:preprocess_deferred])\n\n        # Translate it to a RAL catalog\n        catalog = catalog.to_ral\n\n        catalog.finalize\n\n        catalog.retrieval_duration = Time.now - starttime\n\n        # We accept either the global option `--write_catalog_summary`\n        # corresponding to the new setting, or the application option\n        # `--write-catalog-summary`. The latter is needed to maintain backwards\n        # compatibility.\n        #\n        # Puppet settings parse global options using PuppetOptionParser, but it\n        # only recognizes underscores, not dashes.\n        # The base application parses app specific options using ruby's builtin\n        # OptionParser. As of ruby 2.4, it will accept either underscores or\n        # dashes, but prefer dashes.\n        #\n        # So if underscores are used, the PuppetOptionParser will parse it and\n        # store that in Puppet[:write_catalog_summary]. If dashes are used,\n        # OptionParser will parse it, and set Puppet[:write_catalog_summary]. In\n        # either case, settings will contain the correct value.\n        if Puppet[:write_catalog_summary]\n          catalog.write_class_file\n          catalog.write_resource_file\n        end\n\n        exit_status = apply_catalog(catalog)\n\n        if !exit_status\n          exit(1)\n        elsif options[:detailed_exitcodes] then\n          exit(exit_status)\n        else\n          exit(0)\n        end\n      rescue => detail\n        Puppet.log_exception(detail)\n        exit(1)\n      end\n    end\n  end\n\n  # Enable all of the most common test options.\n  def setup_test\n    Puppet.settings.handlearg(\"--no-splay\")\n    Puppet.settings.handlearg(\"--show_diff\")\n    options[:verbose] = true\n    options[:detailed_exitcodes] = true\n  end\n\n  def setup\n    setup_test if options[:test]\n\n    exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?\n\n    handle_logdest_arg(Puppet[:logdest])\n    Puppet::Util::Log.newdestination(:console) unless options[:setdest]\n\n    Signal.trap(:INT) do\n      $stderr.puts _(\"Exiting\")\n      exit(1)\n    end\n\n    Puppet.settings.use :main, :agent, :ssl\n\n    if Puppet[:catalog_cache_terminus]\n      Puppet::Resource::Catalog.indirection.cache_class = Puppet[:catalog_cache_terminus]\n    end\n\n    # we want the last report to be persisted locally\n    Puppet::Transaction::Report.indirection.cache_class = :yaml\n\n    set_log_level\n\n    if Puppet[:profile]\n      @profiler = Puppet::Util::Profiler.add_profiler(Puppet::Util::Profiler::Aggregate.new(Puppet.method(:info), \"apply\"))\n    end\n  end\n\n  private\n\n  def create_loaders(env)\n    # Ignore both 'cached_puppet_lib' and pcore resource type loaders\n    Puppet::Pops::Loaders.new(env, false, false)\n  end\n\n  def read_catalog(text)\n    facts = get_facts()\n    node = get_node()\n    configured_environment = get_configured_environment(node)\n\n    # TRANSLATORS \"puppet apply\" is a program command and should not be translated\n    Puppet.override({ :current_environment => configured_environment }, _(\"For puppet apply\")) do\n      configure_node_facts(node, facts)\n\n      # NOTE: Does not set rich_data = true automatically (which would ensure always reading catalog with rich data\n      # on (seemingly the right thing to do)), but that would remove the ability to test what happens when a\n      # rich catalog is processed without rich_data being turned on.\n      format = Puppet::Resource::Catalog.default_format\n      begin\n        catalog = Puppet::Resource::Catalog.convert_from(format, text)\n      rescue => detail\n        raise Puppet::Error, _(\"Could not deserialize catalog from %{format}: %{detail}\") % { format: format, detail: detail }, detail.backtrace\n      end\n      # Resolve all deferred values and replace them / mutate the catalog\n      Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(node.facts, catalog, configured_environment, Puppet[:preprocess_deferred])\n\n      catalog.to_ral\n    end\n  end\n\n  def apply_catalog(catalog)\n    configurer = Puppet::Configurer.new\n    configurer.run(:catalog => catalog, :pluginsync => false)\n  end\n\n  # Returns facts or nil\n  #\n  def get_facts\n    facts = nil\n    unless Puppet[:node_name_fact].empty?\n      # Collect our facts.\n      facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value])\n      raise _(\"Could not find facts for %{node}\") % { node: Puppet[:node_name_value] } unless facts\n\n      Puppet[:node_name_value] = facts.values[Puppet[:node_name_fact]]\n      facts.name = Puppet[:node_name_value]\n    end\n    facts\n  end\n\n  # Returns the node or raises and error if node not found.\n  #\n  def get_node\n    node = Puppet::Node.indirection.find(Puppet[:node_name_value])\n    raise _(\"Could not find node %{node}\") % { node: Puppet[:node_name_value] } unless node\n\n    node\n  end\n\n  # Returns either a manifest (filename) or nil if apply should use content of Puppet[:code]\n  #\n  def get_manifest\n    manifest = nil\n    # Set our code or file to use.\n    if options[:code] or command_line.args.length == 0\n      Puppet[:code] = options[:code] || STDIN.read\n    else\n      manifest = command_line.args.shift\n      raise _(\"Could not find file %{manifest}\") % { manifest: manifest } unless Puppet::FileSystem.exist?(manifest)\n\n      Puppet.warning(_(\"Only one file can be applied per run.  Skipping %{files}\") % { files: command_line.args.join(', ') }) if command_line.args.size > 0\n    end\n    manifest\n  end\n\n  # Returns a configured environment, if a manifest is given it overrides what is configured for the environment\n  # specified by the node (or the current_environment found in the Puppet context).\n  # The node's resolved environment is modified  if needed.\n  #\n  def get_configured_environment(node, manifest = nil)\n    configured_environment = node.environment || Puppet.lookup(:current_environment)\n\n    apply_environment = manifest ?\n      configured_environment.override_with(:manifest => manifest) :\n      configured_environment\n\n    # Modify the node descriptor to use the special apply_environment.\n    # It is based on the actual environment from the node, or the locally\n    # configured environment if the node does not specify one.\n    # If a manifest file is passed on the command line, it overrides\n    # the :manifest setting of the apply_environment.\n    node.environment = apply_environment\n    apply_environment\n  end\n\n  # Mixes the facts into the node, and mixes in server facts\n  def configure_node_facts(node, facts)\n    node.merge(facts.values) if facts\n    # Add server facts so $server_facts[environment] exists when doing a puppet apply\n    node.add_server_facts({})\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/catalog.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/indirection_base'\n\nclass Puppet::Application::Catalog < Puppet::Application::IndirectionBase\nend\n"
  },
  {
    "path": "lib/puppet/application/config.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\n\nclass Puppet::Application::Config < Puppet::Application::FaceBase\n  environment_mode :not_required\nend\n"
  },
  {
    "path": "lib/puppet/application/describe.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\n\nclass Formatter\n  def initialize(width)\n    @width = width\n  end\n\n  def wrap(txt, opts)\n    return \"\" unless txt && !txt.empty?\n\n    work = (opts[:scrub] ? scrub(txt) : txt)\n    indent = opts[:indent] || 0\n    textLen = @width - indent\n    patt = Regexp.new(\"\\\\A(.{0,#{textLen}})[ \\n]\")\n    prefix = \" \" * indent\n\n    res = []\n\n    while work.length > textLen\n      if work =~ patt\n        res << ::Regexp.last_match(1)\n        work.slice!(0, ::Regexp.last_match(0).length)\n      else\n        res << work.slice!(0, textLen)\n      end\n    end\n    res << work if work.length.nonzero?\n    prefix + res.join(\"\\n#{prefix}\")\n  end\n\n  def header(txt, sep = \"-\")\n    \"\\n#{txt}\\n\" + sep * txt.size\n  end\n\n  private\n\n  def scrub(text)\n    # For text with no carriage returns, there's nothing to do.\n    return text if text !~ /\\n/\n\n    # If we can match an indentation, then just remove that same level of\n    # indent from every line.\n    if text =~ /^(\\s+)/\n      indent = ::Regexp.last_match(1)\n      text.gsub(/^#{indent}/, '')\n    else\n      text\n    end\n  end\nend\n\nclass TypeDoc\n  def initialize\n    @format = Formatter.new(76)\n    @types = {}\n    Puppet::Type.loadall\n    Puppet::Type.eachtype { |type|\n      next if type.name == :component\n\n      @types[type.name] = type\n    }\n  end\n\n  def list_types\n    puts \"These are the types known to puppet:\\n\"\n    @types.keys.sort_by(&:to_s).each do |name|\n      type = @types[name]\n      s = type.doc.gsub(/\\s+/, \" \")\n      if s.empty?\n        s = \".. no documentation ..\"\n      else\n        n = s.index(\".\") || s.length\n        if n > 45\n          s = s[0, 45] + \" ...\"\n        else\n          s = s[0, n]\n        end\n      end\n      printf \"%-15s - %s\\n\", name, s\n    end\n  end\n\n  def format_type(name, opts)\n    name = name.to_sym\n    unless @types.has_key?(name)\n      puts \"Unknown type #{name}\"\n      return\n    end\n    type = @types[name]\n    puts @format.header(name.to_s, \"=\")\n    puts @format.wrap(type.doc, :indent => 0, :scrub => true) + \"\\n\\n\"\n\n    puts @format.header(\"Parameters\")\n    if opts[:parameters]\n      format_attrs(type, [:property, :param])\n    else\n      list_attrs(type, [:property, :param])\n    end\n\n    if opts[:meta]\n      puts @format.header(\"Meta Parameters\")\n      if opts[:parameters]\n        format_attrs(type, [:meta])\n      else\n        list_attrs(type, [:meta])\n      end\n    end\n\n    if type.providers.size > 0\n      puts @format.header(\"Providers\")\n      if opts[:providers]\n        format_providers(type)\n      else\n        list_providers(type)\n      end\n    end\n  end\n\n  # List details about attributes\n  def format_attrs(type, attrs)\n    docs = {}\n    type.allattrs.each do |name|\n      kind = type.attrtype(name)\n      docs[name] = type.attrclass(name).doc if attrs.include?(kind) && name != :provider\n    end\n\n    docs.sort { |a, b|\n      a[0].to_s <=> b[0].to_s\n    }.each { |name, doc|\n      print \"\\n- **#{name}**\"\n      if type.key_attributes.include?(name) and name != :name\n        puts \" (*namevar*)\"\n      else\n        puts \"\"\n      end\n      puts @format.wrap(doc, :indent => 4, :scrub => true)\n    }\n  end\n\n  # List the names of attributes\n  def list_attrs(type, attrs)\n    params = []\n    type.allattrs.each do |name|\n      kind = type.attrtype(name)\n      params << name.to_s if attrs.include?(kind) && name != :provider\n    end\n    puts @format.wrap(params.sort.join(\", \"), :indent => 4)\n  end\n\n  def format_providers(type)\n    type.providers.sort_by(&:to_s).each { |prov|\n      puts \"\\n- **#{prov}**\"\n      puts @format.wrap(type.provider(prov).doc, :indent => 4, :scrub => true)\n    }\n  end\n\n  def list_providers(type)\n    list = type.providers.sort_by(&:to_s).join(\", \")\n    puts @format.wrap(list, :indent => 4)\n  end\nend\n\nclass Puppet::Application::Describe < Puppet::Application\n  banner \"puppet describe [options] [type]\"\n\n  option(\"--short\", \"-s\", \"Only list parameters without detail\") do |_arg|\n    options[:parameters] = false\n  end\n\n  option(\"--providers\", \"-p\")\n  option(\"--list\", \"-l\")\n  option(\"--meta\", \"-m\")\n\n  def summary\n    _(\"Display help about resource types\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-describe(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Prints help about Puppet resource types, providers, and metaparameters.\n\n\n      USAGE\n      -----\n      puppet describe [-h|--help] [-s|--short] [-p|--providers] [-l|--list] [-m|--meta]\n\n\n      OPTIONS\n      -------\n      * --help:\n        Print this help text\n\n      * --providers:\n        Describe providers in detail for each type\n\n      * --list:\n        List all types\n\n      * --meta:\n        List all metaparameters\n\n      * --short:\n        List only parameters without detail\n\n\n      EXAMPLE\n      -------\n          $ puppet describe --list\n          $ puppet describe file --providers\n          $ puppet describe user -s -m\n\n\n      AUTHOR\n      ------\n      David Lutterkort\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def preinit\n    options[:parameters] = true\n  end\n\n  def main\n    doc = TypeDoc.new\n\n    if options[:list]\n      doc.list_types\n    else\n      options[:types].each { |name| doc.format_type(name, options) }\n    end\n  end\n\n  def setup\n    options[:types] = command_line.args.dup\n    handle_help(nil) unless options[:list] || options[:types].size > 0\n    $stderr.puts \"Warning: ignoring types when listing all types\" if options[:list] && options[:types].size > 0\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/device.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/configurer'\nrequire_relative '../../puppet/util/network_device'\nrequire_relative '../../puppet/ssl/oids'\n\nclass Puppet::Application::Device < Puppet::Application\n  run_mode :agent\n\n  attr_accessor :args, :agent, :host\n\n  def app_defaults\n    super.merge({\n                  :catalog_terminus => :rest,\n                  :catalog_cache_terminus => :json,\n                  :node_terminus => :rest,\n                  :facts_terminus => :network_device,\n                })\n  end\n\n  def preinit\n    # Do an initial trap, so that cancels don't get a stack trace.\n    Signal.trap(:INT) do\n      $stderr.puts _(\"Cancelling startup\")\n      exit(0)\n    end\n\n    {\n      :apply => nil,\n      :waitforcert => nil,\n      :detailed_exitcodes => false,\n      :verbose => false,\n      :debug => false,\n      :centrallogs => false,\n      :setdest => false,\n      :resource => false,\n      :facts => false,\n      :target => nil,\n      :to_yaml => false,\n    }.each do |opt, val|\n      options[opt] = val\n    end\n\n    @args = {}\n  end\n\n  option(\"--centrallogging\")\n  option(\"--debug\", \"-d\")\n  option(\"--resource\", \"-r\")\n  option(\"--facts\", \"-f\")\n  option(\"--to_yaml\", \"-y\")\n  option(\"--verbose\", \"-v\")\n\n  option(\"--detailed-exitcodes\") do |_arg|\n    options[:detailed_exitcodes] = true\n  end\n\n  option(\"--libdir LIBDIR\") do |arg|\n    options[:libdir] = arg\n  end\n\n  option(\"--apply MANIFEST\") do |arg|\n    options[:apply] = arg.to_s\n  end\n\n  option(\"--logdest DEST\", \"-l DEST\") do |arg|\n    handle_logdest_arg(arg)\n  end\n\n  option(\"--waitforcert WAITFORCERT\", \"-w\") do |arg|\n    options[:waitforcert] = arg.to_i\n  end\n\n  option(\"--port PORT\", \"-p\") do |arg|\n    @args[:Port] = arg\n  end\n\n  option(\"--target DEVICE\", \"-t\") do |arg|\n    options[:target] = arg.to_s\n  end\n\n  def summary\n    _(\"Manage remote network devices\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-device(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Retrieves catalogs from the Puppet master and applies them to remote devices.\n\n      This subcommand can be run manually; or periodically using cron,\n      a scheduled task, or a similar tool.\n\n\n      USAGE\n      -----\n        puppet device [-h|--help] [-v|--verbose] [-d|--debug]\n                      [-l|--logdest syslog|<file>|console] [--detailed-exitcodes]\n                      [--deviceconfig <file>] [-w|--waitforcert <seconds>]\n                      [--libdir <directory>]\n                      [-a|--apply <file>] [-f|--facts] [-r|--resource <type> [name]]\n                      [-t|--target <device>] [--user=<user>] [-V|--version]\n\n\n      DESCRIPTION\n      -----------\n      Devices require a proxy Puppet agent to request certificates, collect facts,\n      retrieve and apply catalogs, and store reports.\n\n\n      USAGE NOTES\n      -----------\n      Devices managed by the puppet-device subcommand on a Puppet agent are\n      configured in device.conf, which is located at $confdir/device.conf by default,\n      and is configurable with the $deviceconfig setting.\n\n      The device.conf file is an INI-like file, with one section per device:\n\n      [<DEVICE_CERTNAME>]\n      type <TYPE>\n      url <URL>\n      debug\n\n      The section name specifies the certname of the device.\n\n      The values for the type and url properties are specific to each type of device.\n\n      The optional debug property specifies transport-level debugging,\n      and is limited to telnet and ssh transports.\n\n      See https://puppet.com/docs/puppet/latest/config_file_device.html for details.\n\n\n      OPTIONS\n      -------\n      Note that any setting that's valid in the configuration file is also a valid\n      long argument. For example, 'server' is a valid configuration parameter, so\n      you can specify '--server <servername>' as an argument.\n\n      * --help, -h:\n        Print this help message\n\n      * --verbose, -v:\n        Turn on verbose reporting.\n\n      * --debug, -d:\n        Enable full debugging.\n\n      * --logdest, -l:\n        Where to send log messages. Choose between 'syslog' (the POSIX syslog\n        service), 'console', or the path to a log file. If debugging or verbosity is\n        enabled, this defaults to 'console'. Otherwise, it defaults to 'syslog'.\n        Multiple destinations can be set using a comma separated list\n        (eg: `/path/file1,console,/path/file2`)\"\n\n        A path ending with '.json' will receive structured output in JSON format. The\n        log file will not have an ending ']' automatically written to it due to the\n        appending nature of logging. It must be appended manually to make the content\n        valid JSON.\n\n      * --detailed-exitcodes:\n        Provide transaction information via exit codes. If this is enabled, an exit\n        code of '1' means at least one device had a compile failure, an exit code of\n        '2' means at least one device had resource changes, and an exit code of '4'\n        means at least one device had resource failures. Exit codes of '3', '5', '6',\n        or '7' means that a bitwise combination of the preceding exit codes happened.\n\n      * --deviceconfig:\n        Path to the device config file for puppet device.\n        Default: $confdir/device.conf\n\n      * --waitforcert, -w:\n        This option only matters for targets that do not yet have certificates\n        and it is enabled by default, with a value of 120 (seconds).  This causes\n        +puppet device+ to poll the server every 2 minutes and ask it to sign a\n        certificate request.  This is useful for the initial setup of a target.\n        You can turn off waiting for certificates by specifying a time of 0.\n\n      * --libdir:\n        Override the per-device libdir with a local directory. Specifying a libdir also\n        disables pluginsync. This is useful for testing.\n\n        A path ending with '.jsonl' will receive structured output in JSON Lines\n        format.\n\n      * --apply:\n        Apply a manifest against a remote target. Target must be specified.\n\n      * --facts:\n        Displays the facts of a remote target. Target must be specified.\n\n      * --resource:\n        Displays a resource state as Puppet code, roughly equivalent to\n        `puppet resource`.  Can be filtered by title. Requires --target be specified.\n\n      * --target:\n        Target a specific device/certificate in the device.conf. Doing so will perform a\n        device run against only that device/certificate.\n\n      * --to_yaml:\n        Output found resources in yaml format, suitable to use with Hiera and\n        create_resources.\n\n      * --user:\n        The user to run as.\n\n\n      EXAMPLE\n      -------\n            $ puppet device --target remotehost --verbose\n\n      AUTHOR\n      ------\n      Brice Figureau\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011-2018 Puppet Inc., LLC\n      Licensed under the Apache 2.0 License\n    HELP\n  end\n\n  def main\n    if options[:resource] and !options[:target]\n      raise _(\"resource command requires target\")\n    end\n    if options[:facts] and !options[:target]\n      raise _(\"facts command requires target\")\n    end\n\n    unless options[:apply].nil?\n      raise _(\"missing argument: --target is required when using --apply\") if options[:target].nil?\n      raise _(\"%{file} does not exist, cannot apply\") % { file: options[:apply] } unless File.file?(options[:apply])\n    end\n    libdir = Puppet[:libdir]\n    vardir = Puppet[:vardir]\n    confdir = Puppet[:confdir]\n    ssldir = Puppet[:ssldir]\n    certname = Puppet[:certname]\n\n    env = Puppet::Node::Environment.remote(Puppet[:environment])\n    returns = Puppet.override(:current_environment => env, :loaders => Puppet::Pops::Loaders.new(env)) do\n      # find device list\n      require_relative '../../puppet/util/network_device/config'\n      devices = Puppet::Util::NetworkDevice::Config.devices.dup\n      if options[:target]\n        devices.select! { |key, _value| key == options[:target] }\n      end\n      if devices.empty?\n        if options[:target]\n          raise _(\"Target device / certificate '%{target}' not found in %{config}\") % { target: options[:target], config: Puppet[:deviceconfig] }\n        else\n          Puppet.err _(\"No device found in %{config}\") % { config: Puppet[:deviceconfig] }\n          exit(1)\n        end\n      end\n      devices.collect do |_devicename, device|\n        # TODO when we drop support for ruby < 2.5 we can remove the extra block here\n\n        device_url = URI.parse(device.url)\n        # Handle nil scheme & port\n        scheme = \"#{device_url.scheme}://\" if device_url.scheme\n        port = \":#{device_url.port}\" if device_url.port\n\n        # override local $vardir and $certname\n        Puppet[:ssldir] = ::File.join(Puppet[:deviceconfdir], device.name, 'ssl')\n        Puppet[:confdir] = ::File.join(Puppet[:devicedir], device.name)\n        Puppet[:libdir] = options[:libdir] || ::File.join(Puppet[:devicedir], device.name, 'lib')\n        Puppet[:vardir] = ::File.join(Puppet[:devicedir], device.name)\n        Puppet[:certname] = device.name\n        ssl_context = nil\n\n        # create device directory under $deviceconfdir\n        Puppet::FileSystem.dir_mkpath(Puppet[:ssldir]) unless Puppet::FileSystem.dir_exist?(Puppet[:ssldir])\n\n        # this will reload and recompute default settings and create device-specific sub vardir\n        Puppet.settings.use :main, :agent, :ssl\n\n        # Workaround for PUP-8736: store ssl certs outside the cache directory to prevent accidental removal and keep the old path as symlink\n        optssldir = File.join(Puppet[:confdir], 'ssl')\n        Puppet::FileSystem.symlink(Puppet[:ssldir], optssldir) unless Puppet::FileSystem.exist?(optssldir)\n\n        unless options[:resource] || options[:facts] || options[:apply]\n          # Since it's too complicated to fix properly in the default settings, we workaround for PUP-9642 here.\n          # See https://github.com/puppetlabs/puppet/pull/7483#issuecomment-483455997 for details.\n          # This has to happen after `settings.use` above, so the directory is created and before `setup_host` below, where the SSL\n          # routines would fail with access errors\n          if Puppet.features.root? && !Puppet::Util::Platform.windows?\n            user = Puppet::Type.type(:user).new(name: Puppet[:user]).exists? ? Puppet[:user] : nil\n            group = Puppet::Type.type(:group).new(name: Puppet[:group]).exists? ? Puppet[:group] : nil\n            Puppet.debug(\"Fixing perms for #{user}:#{group} on #{Puppet[:confdir]}\")\n            FileUtils.chown(user, group, Puppet[:confdir]) if user || group\n          end\n\n          ssl_context = setup_context\n\n          unless options[:libdir]\n            Puppet.override(ssl_context: ssl_context) do\n              Puppet::Configurer::PluginHandler.new.download_plugins(env) if Puppet::Configurer.should_pluginsync?\n            end\n          end\n        end\n\n        # this inits the device singleton, so that the facts terminus\n        # and the various network_device provider can use it\n        Puppet::Util::NetworkDevice.init(device)\n\n        if options[:resource]\n          type, name = parse_args(command_line.args)\n          Puppet.info _(\"retrieving resource: %{resource} from %{target} at %{scheme}%{url_host}%{port}%{url_path}\") % { resource: type, target: device.name, scheme: scheme, url_host: device_url.host, port: port, url_path: device_url.path }\n          resources = find_resources(type, name)\n          if options[:to_yaml]\n            data = resources.map do |resource|\n              resource.prune_parameters(:parameters_to_include => @extra_params).to_hiera_hash\n            end.inject(:merge!)\n            text = YAML.dump(type.downcase => data)\n          else\n            text = resources.map do |resource|\n              resource.prune_parameters(:parameters_to_include => @extra_params).to_manifest.force_encoding(Encoding.default_external)\n            end.join(\"\\n\")\n          end\n          (puts text)\n          0\n        elsif options[:facts]\n          Puppet.info _(\"retrieving facts from %{target} at %{scheme}%{url_host}%{port}%{url_path}\") % { resource: type, target: device.name, scheme: scheme, url_host: device_url.host, port: port, url_path: device_url.path }\n          remote_facts = Puppet::Node::Facts.indirection.find(name, :environment => env)\n          # Give a proper name to the facts\n          remote_facts.name = remote_facts.values['clientcert']\n          renderer = Puppet::Network::FormatHandler.format(:console)\n          puts renderer.render(remote_facts)\n          0\n        elsif options[:apply]\n          # avoid reporting to server\n          Puppet::Transaction::Report.indirection.terminus_class = :yaml\n          Puppet::Resource::Catalog.indirection.cache_class = nil\n\n          require_relative '../../puppet/application/apply'\n          begin\n            Puppet[:node_terminus] = :plain\n            Puppet[:catalog_terminus] = :compiler\n            Puppet[:catalog_cache_terminus] = nil\n            Puppet[:facts_terminus] = :network_device\n            Puppet.override(:network_device => true) do\n              Puppet::Application::Apply.new(Puppet::Util::CommandLine.new('puppet', [\"apply\", options[:apply]])).run_command\n            end\n          end\n        else\n          Puppet.info _(\"starting applying configuration to %{target} at %{scheme}%{url_host}%{port}%{url_path}\") % { target: device.name, scheme: scheme, url_host: device_url.host, port: port, url_path: device_url.path }\n\n          overrides = {}\n          overrides[:ssl_context] = ssl_context if ssl_context\n          Puppet.override(overrides) do\n            configurer = Puppet::Configurer.new\n            configurer.run(:network_device => true, :pluginsync => false)\n          end\n        end\n      rescue => detail\n        Puppet.log_exception(detail)\n        # If we rescued an error, then we return 1 as the exit code\n        1\n      ensure\n        Puppet[:libdir] = libdir\n        Puppet[:vardir] = vardir\n        Puppet[:confdir] = confdir\n        Puppet[:ssldir] = ssldir\n        Puppet[:certname] = certname\n      end\n    end\n\n    if !returns or returns.compact.empty?\n      exit(1)\n    elsif options[:detailed_exitcodes]\n      # Bitwise OR the return codes together, puppet style\n      exit(returns.compact.reduce(:|))\n    elsif returns.include? 1\n      exit(1)\n    else\n      exit(0)\n    end\n  end\n\n  def parse_args(args)\n    type = args.shift or raise _(\"You must specify the type to display\")\n    Puppet::Type.type(type) or raise _(\"Could not find type %{type}\") % { type: type }\n    name = args.shift\n\n    [type, name]\n  end\n\n  def find_resources(type, name)\n    key = [type, name].join('/')\n\n    if name\n      [Puppet::Resource.indirection.find(key)]\n    else\n      Puppet::Resource.indirection.search(key, {})\n    end\n  end\n\n  def setup_context\n    waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : Puppet[:waitforcert])\n    sm = Puppet::SSL::StateMachine.new(waitforcert: waitforcert)\n    sm.ensure_client_certificate\n  end\n\n  def setup\n    setup_logs\n\n    Puppet::SSL::Oids.register_puppet_oids\n\n    # setup global device-specific defaults; creates all necessary directories, etc\n    Puppet.settings.use :main, :agent, :device, :ssl\n\n    if options[:apply] || options[:facts] || options[:resource]\n      Puppet::Util::Log.newdestination(:console)\n    else\n      args[:Server] = Puppet[:server]\n      if options[:centrallogs]\n        logdest = args[:Server]\n\n        logdest += \":\" + args[:Port] if args.include?(:Port)\n        Puppet::Util::Log.newdestination(logdest)\n      end\n\n      Puppet::Transaction::Report.indirection.terminus_class = :rest\n\n      if Puppet[:catalog_cache_terminus]\n        Puppet::Resource::Catalog.indirection.cache_class = Puppet[:catalog_cache_terminus].intern\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/doc.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\n\nclass Puppet::Application::Doc < Puppet::Application\n  run_mode :server\n\n  attr_accessor :unknown_args, :manifest\n\n  def preinit\n    { :references => [], :mode => :text, :format => :to_markdown }.each do |name, value|\n      options[name] = value\n    end\n    @unknown_args = []\n    @manifest = false\n  end\n\n  option(\"--all\", \"-a\")\n  option(\"--outputdir OUTPUTDIR\", \"-o\")\n  option(\"--verbose\", \"-v\")\n  option(\"--debug\", \"-d\")\n  option(\"--charset CHARSET\")\n\n  option(\"--format FORMAT\", \"-f\") do |arg|\n    method = \"to_#{arg}\"\n    require_relative '../../puppet/util/reference'\n    if Puppet::Util::Reference.method_defined?(method)\n      options[:format] = method\n    else\n      raise _(\"Invalid output format %{arg}\") % { arg: arg }\n    end\n  end\n\n  option(\"--mode MODE\", \"-m\") do |arg|\n    require_relative '../../puppet/util/reference'\n    if Puppet::Util::Reference.modes.include?(arg) or arg.intern == :rdoc\n      options[:mode] = arg.intern\n    else\n      raise _(\"Invalid output mode %{arg}\") % { arg: arg }\n    end\n  end\n\n  option(\"--list\", \"-l\") do |_arg|\n    require_relative '../../puppet/util/reference'\n    refs = Puppet::Util::Reference.references(Puppet.lookup(:current_environment))\n    puts refs.collect { |r| Puppet::Util::Reference.reference(r).doc }.join(\"\\n\")\n    exit(0)\n  end\n\n  option(\"--reference REFERENCE\", \"-r\") do |arg|\n    options[:references] << arg.intern\n  end\n\n  def summary\n    _(\"Generate Puppet references\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-doc(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Generates a reference for all Puppet types. Largely meant for internal\n      Puppet Inc. use. (Deprecated)\n\n\n      USAGE\n      -----\n      puppet doc [-h|--help] [-l|--list]\n        [-r|--reference <reference-name>]\n\n\n      DESCRIPTION\n      -----------\n      This deprecated command generates a Markdown document to stdout\n      describing all installed Puppet types or all allowable arguments to\n      puppet executables. It is largely meant for internal use and is used to\n      generate the reference document available on the Puppet Inc. web site.\n\n      For Puppet module documentation (and all other use cases) this command\n      has been superseded by the \"puppet-strings\"\n      module - see https://github.com/puppetlabs/puppetlabs-strings for more information.\n\n      This command (puppet-doc) will be removed once the\n      puppetlabs internal documentation processing pipeline is completely based\n      on puppet-strings.\n\n      OPTIONS\n      -------\n\n      * --help:\n        Print this help message\n\n      * --reference:\n        Build a particular reference. Get a list of references by running\n        'puppet doc --list'.\n\n\n      EXAMPLE\n      -------\n          $ puppet doc -r type > /tmp/type_reference.markdown\n\n\n      AUTHOR\n      ------\n      Luke Kanies\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def handle_unknown(opt, arg)\n    @unknown_args << { :opt => opt, :arg => arg }\n    true\n  end\n\n  def run_command\n    [:rdoc].include?(options[:mode]) ? send(options[:mode]) : other\n  end\n\n  def rdoc\n    exit_code = 0\n    files = []\n    unless @manifest\n      env = Puppet.lookup(:current_environment)\n      files += env.modulepath\n      files << ::File.dirname(env.manifest) if env.manifest != Puppet::Node::Environment::NO_MANIFEST\n    end\n    files += command_line.args\n    Puppet.info _(\"scanning: %{files}\") % { files: files.inspect }\n\n    Puppet.settings[:document_all] = options[:all] || false\n    begin\n      require_relative '../../puppet/util/rdoc'\n      if @manifest\n        Puppet::Util::RDoc.manifestdoc(files)\n      else\n        options[:outputdir] = \"doc\" unless options[:outputdir]\n        Puppet::Util::RDoc.rdoc(options[:outputdir], files, options[:charset])\n      end\n    rescue => detail\n      Puppet.log_exception(detail, _(\"Could not generate documentation: %{detail}\") % { detail: detail })\n      exit_code = 1\n    end\n    exit exit_code\n  end\n\n  def other\n    text = ''.dup\n    with_contents = options[:references].length <= 1\n    exit_code = 0\n    require_relative '../../puppet/util/reference'\n    options[:references].sort_by(&:to_s).each do |name|\n      section = Puppet::Util::Reference.reference(name)\n      raise _(\"Could not find reference %{name}\") % { name: name } unless section\n\n      begin\n        # Add the per-section text, but with no ToC\n        text += section.send(options[:format], with_contents)\n      rescue => detail\n        Puppet.log_exception(detail, _(\"Could not generate reference %{name}: %{detail}\") % { name: name, detail: detail })\n        exit_code = 1\n        next\n      end\n    end\n\n    text += Puppet::Util::Reference.footer unless with_contents # We've only got one reference\n\n    puts text\n\n    exit exit_code\n  end\n\n  def setup\n    # sole manifest documentation\n    if command_line.args.size > 0\n      options[:mode] = :rdoc\n      @manifest = true\n    end\n\n    if options[:mode] == :rdoc\n      setup_rdoc\n    else\n      setup_reference\n    end\n\n    setup_logging\n  end\n\n  def setup_reference\n    if options[:all]\n      # Don't add dynamic references to the \"all\" list.\n      require_relative '../../puppet/util/reference'\n      refs = Puppet::Util::Reference.references(Puppet.lookup(:current_environment))\n      options[:references] = refs.reject do |ref|\n        Puppet::Util::Reference.reference(ref).dynamic?\n      end\n    end\n\n    options[:references] << :type if options[:references].empty?\n  end\n\n  def setup_rdoc\n    # consume the unknown options\n    # and feed them as settings\n    if @unknown_args.size > 0\n      @unknown_args.each do |option|\n        # force absolute path for modulepath when passed on commandline\n        if option[:opt] == \"--modulepath\"\n          option[:arg] = option[:arg].split(::File::PATH_SEPARATOR).collect { |p| ::File.expand_path(p) }.join(::File::PATH_SEPARATOR)\n        end\n        Puppet.settings.handlearg(option[:opt], option[:arg])\n      end\n    end\n  end\n\n  def setup_logging\n    Puppet::Util::Log.level = :warning\n\n    set_log_level\n\n    Puppet::Util::Log.newdestination(:console)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/epp.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\nrequire_relative '../../puppet/face'\n\nclass Puppet::Application::Epp < Puppet::Application::FaceBase\nend\n"
  },
  {
    "path": "lib/puppet/application/face_base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/face'\nrequire 'optparse'\n\nclass Puppet::Application::FaceBase < Puppet::Application\n  option(\"--debug\", \"-d\") do |_arg|\n    set_log_level(:debug => true)\n  end\n\n  option(\"--verbose\", \"-v\") do |_|\n    set_log_level(:verbose => true)\n  end\n\n  option(\"--render-as FORMAT\") do |format|\n    self.render_as = format.to_sym\n  end\n\n  option(\"--help\", \"-h\") do |_arg|\n    if action && !@is_default_action\n      # Only invoke help on the action if it was specified, not if\n      # it was the default action.\n      puts Puppet::Face[:help, :current].help(face.name, action.name)\n    else\n      puts Puppet::Face[:help, :current].help(face.name)\n    end\n    exit(0)\n  end\n\n  attr_reader :render_as\n  attr_accessor :face, :action, :type, :arguments\n\n  def render_as=(format)\n    @render_as = Puppet::Network::FormatHandler.format(format)\n    @render_as or raise ArgumentError, _(\"I don't know how to render '%{format}'\") % { format: format }\n  end\n\n  def render(result, args_and_options)\n    hook = action.when_rendering(render_as.name)\n\n    if hook\n      # when defining when_rendering on your action you can optionally\n      # include arguments and options\n      if hook.arity > 1\n        result = hook.call(result, *args_and_options)\n      else\n        result = hook.call(result)\n      end\n    end\n\n    render_as.render(result)\n  end\n\n  def preinit\n    super\n    Signal.trap(:INT) do\n      $stderr.puts _(\"Cancelling Face\")\n      exit(0)\n    end\n  end\n\n  def parse_options\n    # We need to parse enough of the command line out early, to identify what\n    # the action is, so that we can obtain the full set of options to parse.\n\n    # REVISIT: These should be configurable versions, through a global\n    # '--version' option, but we don't implement that yet... --daniel 2011-03-29\n    @type = Puppet::Util::ConstantInflector.constant2file(self.class.name.to_s.sub(/.+:/, '')).to_sym\n    @face = Puppet::Face[@type, :current]\n\n    # Now, walk the command line and identify the action.  We skip over\n    # arguments based on introspecting the action and all, and find the first\n    # non-option word to use as the action.\n    action_name = nil\n    index       = -1\n    until action_name or (index += 1) >= command_line.args.length\n      item = command_line.args[index]\n      if item =~ /^-/\n        option = @face.options.find do |name|\n          item =~ /^-+#{name.to_s.gsub(/[-_]/, '[-_]')}(?:[ =].*)?$/\n        end\n        if option\n          option = @face.get_option(option)\n          # If we have an inline argument, just carry on.  We don't need to\n          # care about optional vs mandatory in that case because we do a real\n          # parse later, and that will totally take care of raising the error\n          # when we get there. --daniel 2011-04-04\n          if option.takes_argument? and !item.index('=')\n            index += 1 unless\n              option.optional_argument? and command_line.args[index + 1] =~ /^-/\n          end\n        else\n          option = find_global_settings_argument(item)\n          if option\n            unless Puppet.settings.boolean? option.name\n              # As far as I can tell, we treat non-bool options as always having\n              # a mandatory argument. --daniel 2011-04-05\n              # ... But, the mandatory argument will not be the next item if an = is\n              # employed in the long form of the option. --jeffmccune 2012-09-18\n              index += 1 unless item =~ /^--#{option.name}=/\n            end\n          else\n            option = find_application_argument(item)\n            if option\n              index += 1 if option[:argument] and !(option[:optional])\n            else\n              raise OptionParser::InvalidOption, item.sub(/=.*$/, '')\n            end\n          end\n        end\n      else\n        # Stash away the requested action name for later, and try to fetch the\n        # action object it represents; if this is an invalid action name that\n        # will be nil, and handled later.\n        action_name = item.to_sym\n        @action = Puppet::Face.find_action(@face.name, action_name)\n        @face   = @action.face if @action\n      end\n    end\n\n    if @action.nil?\n      @action = @face.get_default_action()\n      if @action\n        @is_default_action = true\n      else\n        # First try to handle global command line options\n        # But ignoring invalid options as this is a invalid action, and\n        # we want the error message for that instead.\n        begin\n          super\n        rescue OptionParser::InvalidOption\n        end\n\n        face   = @face.name\n        action = action_name.nil? ? 'default' : \"'#{action_name}'\"\n        msg = _(\"'%{face}' has no %{action} action.  See `puppet help %{face}`.\") % { face: face, action: action }\n\n        Puppet.err(msg)\n        Puppet::Util::Log.force_flushqueue()\n\n        exit false\n      end\n    end\n\n    # Now we can interact with the default option code to build behaviour\n    # around the full set of options we now know we support.\n    @action.options.each do |o|\n      o = @action.get_option(o) # make it the object.\n      self.class.option(*o.optparse) # ...and make the CLI parse it.\n    end\n\n    # ...and invoke our parent to parse all the command line options.\n    super\n  end\n\n  def find_global_settings_argument(item)\n    Puppet.settings.each do |_name, object|\n      object.optparse_args.each do |arg|\n        next unless arg =~ /^-/\n\n        # sadly, we have to emulate some of optparse here...\n        pattern = /^#{arg.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/\n        pattern.match item and return object\n      end\n    end\n    nil                  # nothing found.\n  end\n\n  def find_application_argument(item)\n    self.class.option_parser_commands.each do |options, _function|\n      options.each do |option|\n        next unless option =~ /^-/\n\n        pattern = /^#{option.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/\n        next unless pattern.match(item)\n\n        return {\n          :argument => option =~ /[ =]/,\n          :optional => option =~ /[ =]\\[/\n        }\n      end\n    end\n    nil                  # not found\n  end\n\n  def setup\n    Puppet::Util::Log.newdestination :console\n\n    @arguments = command_line.args\n\n    # Note: because of our definition of where the action is set, we end up\n    # with it *always* being the first word of the remaining set of command\n    # line arguments.  So, strip that off when we construct the arguments to\n    # pass down to the face action. --daniel 2011-04-04\n    # Of course, now that we have default actions, we should leave the\n    # \"action\" name on if we didn't actually consume it when we found our\n    # action.\n    @arguments.delete_at(0) unless @is_default_action\n\n    # We copy all of the app options to the end of the call; This allows each\n    # action to read in the options.  This replaces the older model where we\n    # would invoke the action with options set as global state in the\n    # interface object.  --daniel 2011-03-28\n    @arguments << options\n\n    # If we don't have a rendering format, set one early.\n    self.render_as ||= @action.render_as || :console\n  end\n\n  def main\n    status = false\n\n    # Call the method associated with the provided action (e.g., 'find').\n    unless @action\n      puts Puppet::Face[:help, :current].help(@face.name)\n      raise _(\"%{face} does not respond to action %{arg}\") % { face: face, arg: arguments.first }\n    end\n\n    # We need to do arity checking here because this is generic code\n    # calling generic methods – that have argument defaulting.  We need to\n    # make sure we don't accidentally pass the options as the first\n    # argument to a method that takes one argument.  eg:\n    #\n    #   puppet facts find\n    #   => options => {}\n    #      @arguments => [{}]\n    #   => @face.send :bar, {}\n    #\n    #   def face.bar(argument, options = {})\n    #   => bar({}, {})  # oops!  we thought the options were the\n    #                   # positional argument!!\n    #\n    # We could also fix this by making it mandatory to pass the options on\n    # every call, but that would make the Ruby API much more annoying to\n    # work with; having the defaulting is a much nicer convention to have.\n    #\n    # We could also pass the arguments implicitly, by having a magic\n    # 'options' method that was visible in the scope of the action, which\n    # returned the right stuff.\n    #\n    # That sounds attractive, but adds complications to all sorts of\n    # things, especially when you think about how to pass options when you\n    # are writing Ruby code that calls multiple faces.  Especially if\n    # faces are involved in that. ;)\n    #\n    # --daniel 2011-04-27\n    if (arity = @action.positional_arg_count) > 0\n      unless (count = arguments.length) == arity then\n        raise ArgumentError, n_(\"puppet %{face} %{action} takes %{arg_count} argument, but you gave %{given_count}\", \"puppet %{face} %{action} takes %{arg_count} arguments, but you gave %{given_count}\", arity - 1) % { face: @face.name, action: @action.name, arg_count: arity - 1, given_count: count - 1 }\n      end\n    end\n\n    if @face.deprecated?\n      Puppet.deprecation_warning(_(\"'puppet %{face}' is deprecated and will be removed in a future release\") % { face: @face.name })\n    end\n\n    result = @face.send(@action.name, *arguments)\n    puts render(result, arguments) unless result.nil?\n    status = true\n\n  # We need an easy way for the action to set a specific exit code, so we\n  # rescue SystemExit here; This allows each action to set the desired exit\n  # code by simply calling Kernel::exit.  eg:\n  #\n  #   exit(2)\n  #\n  # --kelsey 2012-02-14\n  rescue SystemExit => detail\n    status = detail.status\n  rescue => detail\n    Puppet.log_exception(detail)\n    Puppet.err _(\"Try 'puppet help %{face} %{action}' for usage\") % { face: @face.name, action: @action.name }\n  ensure\n    exit status\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/facts.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/indirection_base'\n\nclass Puppet::Application::Facts < Puppet::Application::IndirectionBase\n  # Allows `puppet facts` actions to be run against environments that\n  # don't exist locally, such as using the `--environment` flag to make a REST\n  # request to a specific environment on a master. There is no way to set this\n  # behavior per-action, so it must be set for the face as a whole.\n  environment_mode :not_required\nend\n"
  },
  {
    "path": "lib/puppet/application/filebucket.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\n\nclass Puppet::Application::Filebucket < Puppet::Application\n  environment_mode :not_required\n\n  option(\"--bucket BUCKET\", \"-b\")\n  option(\"--debug\", \"-d\")\n  option(\"--fromdate FROMDATE\", \"-f\")\n  option(\"--todate TODATE\", \"-t\")\n  option(\"--local\", \"-l\")\n  option(\"--remote\", \"-r\")\n  option(\"--verbose\", \"-v\")\n\n  attr_reader :args\n\n  def summary\n    _(\"Store and retrieve files in a filebucket\")\n  end\n\n  def digest_algorithm\n    Puppet.default_digest_algorithm\n  end\n\n  def help\n    <<~HELP\n\n      puppet-filebucket(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      A stand-alone Puppet filebucket client.\n\n\n      USAGE\n      -----\n      puppet filebucket <mode> [-h|--help] [-V|--version] [-d|--debug]\n        [-v|--verbose] [-l|--local] [-r|--remote] [-s|--server <server>]\n        [-f|--fromdate <date>] [-t|--todate <date>] [-b|--bucket <directory>]\n        <file> <file> ...\n\n      Puppet filebucket can operate in three modes, with only one mode per call:\n\n      backup:\n        Send one or more files to the specified file bucket. Each sent file is\n        printed with its resulting #{digest_algorithm} sum.\n\n      get:\n        Return the text associated with an #{digest_algorithm} sum. The text is printed to\n        stdout, and only one file can be retrieved at a time.\n\n      restore:\n        Given a file path and an #{digest_algorithm} sum, store the content associated with\n        the sum into the specified file path. You can specify an entirely new\n        path to this argument; you are not restricted to restoring the content\n        to its original location.\n\n      diff:\n        Print a diff in unified format between two checksums in the filebucket\n        or between a checksum and its matching file.\n\n      list:\n        List all files in the current local filebucket. Listing remote\n        filebuckets is not allowed.\n\n      DESCRIPTION\n      -----------\n      This is a stand-alone filebucket client for sending files to a local or\n      central filebucket.\n\n      Note that 'filebucket' defaults to using a network-based filebucket\n      available on the server named 'puppet'. To use this, you'll have to be\n      running as a user with valid Puppet certificates. Alternatively, you can\n      use your local file bucket by specifying '--local', or by specifying\n      '--bucket' with a local path.\n\n      **Important**: When you enable and use the backup option, and by extension\n      the filebucket resource, you must ensure that sufficient disk space is\n      available for the file backups. Generally, you can provide the disk space\n      by using one of the following two options:\n\n        - Use a `find` command and `crontab` entry to retain only the last X days\n        of file backups. For example:\n\n        ```shell\n        find /opt/puppetlabs/server/data/puppetserver/bucket -type f -mtime +45 -atime +45 -print0 | xargs -0 rm\n        ```\n\n        - Restrict the directory to a maximum size after which the oldest items are removed.\n\n\n      OPTIONS\n      -------\n      Note that any setting that's valid in the configuration\n      file is also a valid long argument. For example, 'ssldir' is a valid\n      setting, so you can specify '--ssldir <directory>' as an\n      argument.\n\n      See the configuration file documentation at\n      https://puppet.com/docs/puppet/latest/configuration.html for the\n      full list of acceptable parameters. A commented list of all\n      configuration options can also be generated by running puppet with\n      '--genconfig'.\n\n      * --bucket:\n        Specify a local filebucket path. This overrides the default path\n        set in '$clientbucketdir'.\n\n      * --debug:\n        Enable full debugging.\n\n      * --fromdate:\n        (list only) Select bucket files from 'fromdate'.\n\n      * --help:\n        Print this help message.\n\n      * --local:\n        Use the local filebucket. This uses the default configuration\n        information and the bucket located at the '$clientbucketdir'\n        setting by default. If '--bucket' is set, puppet uses that\n        path instead.\n\n      * --remote:\n        Use a remote filebucket. This uses the default configuration\n        information and the bucket located at the '$bucketdir' setting\n        by default.\n\n      * --server_list:\n        A list of comma separated servers; only the first entry is used for file storage.\n        This setting takes precidence over `server`.\n\n      * --server:\n        The server to use for file storage. This setting is only used if `server_list`\n        is not set.\n\n      * --todate:\n        (list only) Select bucket files until 'todate'.\n\n      * --verbose:\n        Print extra information.\n\n      * --version:\n        Print version information.\n\n      EXAMPLES\n      --------\n          ## Backup a file to the filebucket, then restore it to a temporary directory\n          $ puppet filebucket backup /etc/passwd\n          /etc/passwd: 429b225650b912a2ee067b0a4cf1e949\n          $ puppet filebucket restore /tmp/passwd 429b225650b912a2ee067b0a4cf1e949\n\n          ## Diff between two files in the filebucket\n          $ puppet filebucket -l diff d43a6ecaa892a1962398ac9170ea9bf2 7ae322f5791217e031dc60188f4521ef\n          1a2\n          > again\n\n          ## Diff between the file in the filebucket and a local file\n          $ puppet filebucket -l diff d43a6ecaa892a1962398ac9170ea9bf2 /tmp/testFile\n          1a2\n          > again\n\n          ## Backup a file to the filebucket and observe that it keeps each backup separate\n          $ puppet filebucket -l list\n          d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n\n          $ echo again >> /tmp/TestFile\n\n          $ puppet filebucket -l backup /tmp/TestFile\n          /tmp/TestFile: 7ae322f5791217e031dc60188f4521ef\n\n          $ puppet filebucket -l list\n          d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n          7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n          ## List files in a filebucket within date ranges\n          $ puppet filebucket -l -f 2015-01-01 -t 2015-01-11 list\n          <Empty Output>\n\n          $ puppet filebucket -l -f 2015-05-10 list\n          d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n          7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n          $ puppet filebucket -l -f \"2015-05-11 09:30:00\" list\n          7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n          $ puppet filebucket -l -t \"2015-05-11 09:30:00\" list\n          d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n\n          ## Manage files in a specific local filebucket\n          $ puppet filebucket -b /tmp/TestBucket backup /tmp/TestFile2\n          /tmp/TestFile2: d41d8cd98f00b204e9800998ecf8427e\n          $ puppet filebucket -b /tmp/TestBucket list\n          d41d8cd98f00b204e9800998ecf8427e 2015-05-11 09:33:22 /tmp/TestFile2\n\n          ## From a Puppet Server, list files in the server bucketdir\n          $ puppet filebucket -b $(puppet config print bucketdir --section server) list\n          d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n          7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n      AUTHOR\n      ------\n      Luke Kanies\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def run_command\n    @args = command_line.args\n    command = args.shift\n    return send(command) if %w[get backup restore diff list].include? command\n\n    help\n  end\n\n  def get\n    digest = args.shift\n    out = @client.getfile(digest)\n    print out\n  end\n\n  def backup\n    raise _(\"You must specify a file to back up\") unless args.length > 0\n\n    args.each do |file|\n      unless Puppet::FileSystem.exist?(file)\n        $stderr.puts _(\"%{file}: no such file\") % { file: file }\n        next\n      end\n      unless FileTest.readable?(file)\n        $stderr.puts _(\"%{file}: cannot read file\") % { file: file }\n        next\n      end\n      digest = @client.backup(file)\n      puts \"#{file}: #{digest}\"\n    end\n  end\n\n  def list\n    fromdate = options[:fromdate]\n    todate = options[:todate]\n    out = @client.list(fromdate, todate)\n    print out\n  end\n\n  def restore\n    file = args.shift\n    digest = args.shift\n    @client.restore(file, digest)\n  end\n\n  def diff\n    raise Puppet::Error, _(\"Need exactly two arguments: filebucket diff <file_a> <file_b>\") unless args.count == 2\n\n    left = args.shift\n    right = args.shift\n    if Puppet::FileSystem.exist?(left)\n      # It's a file\n      file_a = left\n      checksum_a = nil\n    else\n      file_a = nil\n      checksum_a = left\n    end\n    if Puppet::FileSystem.exist?(right)\n      # It's a file\n      file_b = right\n      checksum_b = nil\n    else\n      file_b = nil\n      checksum_b = right\n    end\n    if (checksum_a || file_a) && (checksum_b || file_b)\n      Puppet.info(_(\"Comparing %{checksum_a} %{checksum_b} %{file_a} %{file_b}\") % { checksum_a: checksum_a, checksum_b: checksum_b, file_a: file_a, file_b: file_b })\n      print @client.diff(checksum_a, checksum_b, file_a, file_b)\n    else\n      raise Puppet::Error, _(\"Need exactly two arguments: filebucket diff <file_a> <file_b>\")\n    end\n  end\n\n  def setup\n    Puppet::Log.newdestination(:console)\n\n    @client = nil\n    @server = nil\n\n    Signal.trap(:INT) do\n      $stderr.puts _(\"Cancelling\")\n      exit(1)\n    end\n\n    if options[:debug]\n      Puppet::Log.level = :debug\n    elsif options[:verbose]\n      Puppet::Log.level = :info\n    end\n\n    exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?\n\n    require_relative '../../puppet/file_bucket/dipper'\n    begin\n      if options[:local] or options[:bucket]\n        path = options[:bucket] || Puppet[:clientbucketdir]\n        @client = Puppet::FileBucket::Dipper.new(:Path => path)\n      else\n        session = Puppet.lookup(:http_session)\n        api = session.route_to(:puppet)\n\n        @client = Puppet::FileBucket::Dipper.new(Server: api.url.host, Port: api.url.port)\n      end\n    rescue => detail\n      Puppet.log_exception(detail)\n      exit(1)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/generate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\n\n# The Generate application.\nclass Puppet::Application::Generate < Puppet::Application::FaceBase\nend\n"
  },
  {
    "path": "lib/puppet/application/help.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\n\nclass Puppet::Application::Help < Puppet::Application::FaceBase\n  environment_mode :not_required\nend\n"
  },
  {
    "path": "lib/puppet/application/indirection_base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\n\nclass Puppet::Application::IndirectionBase < Puppet::Application::FaceBase\nend\n"
  },
  {
    "path": "lib/puppet/application/lookup.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/pops'\nrequire_relative '../../puppet/node'\nrequire_relative '../../puppet/node/server_facts'\nrequire_relative '../../puppet/parser/compiler'\n\nclass Puppet::Application::Lookup < Puppet::Application\n  RUN_HELP = _(\"Run 'puppet lookup --help' for more details\").freeze\n  DEEP_MERGE_OPTIONS = '--knock-out-prefix, --sort-merged-arrays, and --merge-hash-arrays'\n  TRUSTED_INFORMATION_FACTS = %w[hostname domain fqdn clientcert].freeze\n\n  run_mode :server\n\n  # Options for lookup\n  option('--merge TYPE') do |arg|\n    options[:merge] = arg\n  end\n\n  option('--debug', '-d')\n\n  option('--verbose', '-v')\n\n  option('--render-as FORMAT') do |format|\n    options[:render_as] = format.downcase.to_sym\n  end\n\n  option('--type TYPE_STRING') do |arg|\n    options[:type] = arg\n  end\n\n  option('--compile', '-c')\n\n  option('--knock-out-prefix PREFIX_STRING') do |arg|\n    options[:prefix] = arg\n  end\n\n  option('--sort-merged-arrays')\n\n  option('--merge-hash-arrays')\n\n  option('--explain')\n\n  option('--explain-options')\n\n  option('--default VALUE') do |arg|\n    options[:default_value] = arg\n  end\n\n  # not yet supported\n  option('--trusted')\n\n  # Options for facts/scope\n  option('--node NODE_NAME') do |arg|\n    options[:node] = arg\n  end\n\n  option('--facts FACT_FILE') do |arg|\n    options[:fact_file] = arg\n  end\n\n  def app_defaults\n    super.merge({\n                  :facts_terminus => 'yaml'\n                })\n  end\n\n  def setup_logs\n    # This sets up logging based on --debug or --verbose if they are set in `options`\n    set_log_level\n\n    # This uses console for everything that is not a compilation\n    Puppet::Util::Log.newdestination(:console)\n  end\n\n  def setup_terminuses\n    require_relative '../../puppet/file_serving/content'\n    require_relative '../../puppet/file_serving/metadata'\n\n    Puppet::FileServing::Content.indirection.terminus_class = :file_server\n    Puppet::FileServing::Metadata.indirection.terminus_class = :file_server\n\n    Puppet::FileBucket::File.indirection.terminus_class = :file\n  end\n\n  def setup\n    setup_logs\n\n    exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?\n\n    if options[:node]\n      Puppet::Util.skip_external_facts do\n        Puppet.settings.use :main, :server, :ssl, :metrics\n      end\n    else\n      Puppet.settings.use :main, :server, :ssl, :metrics\n    end\n\n    setup_terminuses\n  end\n\n  def summary\n    _(\"Interactive Hiera lookup\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-lookup(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Does Hiera lookups from the command line.\n\n      Since this command needs access to your Hiera data, make sure to run it on a\n      node that has a copy of that data. This usually means logging into a Puppet\n      Server node and running 'puppet lookup' with sudo.\n\n      The most common version of this command is:\n\n      'puppet lookup <KEY> --node <NAME> --environment <ENV> --explain'\n\n      USAGE\n      -----\n      puppet lookup [--help] [--type <TYPESTRING>] [--merge first|unique|hash|deep]\n        [--knock-out-prefix <PREFIX-STRING>] [--sort-merged-arrays]\n        [--merge-hash-arrays] [--explain] [--environment <ENV>]\n        [--default <VALUE>] [--node <NODE-NAME>] [--facts <FILE>]\n        [--compile]\n        [--render-as s|json|yaml|binary|msgpack] <keys>\n\n      DESCRIPTION\n      -----------\n      The lookup command is a CLI for Puppet's 'lookup()' function. It searches your\n      Hiera data and returns a value for the requested lookup key, so you can test and\n      explore your data. It is a modern replacement for the 'hiera' command.\n      Lookup uses the setting for global hiera.yaml from puppet's config,\n      and the environment to find the environment level hiera.yaml as well as the\n      resulting modulepath for the environment (for hiera.yaml files in modules).\n      Hiera usually relies on a node's facts to locate the relevant data sources. By\n      default, 'puppet lookup' uses facts from the node you run the command on, but\n      you can get data for any other node with the '--node <NAME>' option. If\n      possible, the lookup command will use the requested node's real stored facts\n      from PuppetDB; if PuppetDB isn't configured or you want to provide arbitrary\n      fact values, you can pass alternate facts as a JSON or YAML file with '--facts\n      <FILE>'.\n\n      If you're debugging your Hiera data and want to see where values are coming\n      from, use the '--explain' option.\n\n      If '--explain' isn't specified, lookup exits with 0 if a value was found and 1\n      otherwise. With '--explain', lookup always exits with 0 unless there is a major\n      error.\n\n      You can provide multiple lookup keys to this command, but it only returns a\n      value for the first found key, omitting the rest.\n\n      For more details about how Hiera works, see the Hiera documentation:\n      https://puppet.com/docs/puppet/latest/hiera_intro.html\n\n      OPTIONS\n      -------\n\n      * --help:\n        Print this help message.\n\n      * --explain\n        Explain the details of how the lookup was performed and where the final value\n        came from (or the reason no value was found).\n\n      * --node <NODE-NAME>\n        Specify which node to look up data for; defaults to the node where the command\n        is run. Since Hiera's purpose is to provide different values for different\n        nodes (usually based on their facts), you'll usually want to use some specific\n        node's facts to explore your data. If the node where you're running this\n        command is configured to talk to PuppetDB, the command will use the requested\n        node's most recent facts. Otherwise, you can override facts with the '--facts'\n        option.\n\n      * --facts <FILE>\n        Specify a .json or .yaml file of key => value mappings to override the facts\n        for this lookup. Any facts not specified in this file maintain their\n        original value.\n\n      * --environment <ENV>\n        Like with most Puppet commands, you can specify an environment on the command\n        line. This is important for lookup because different environments can have\n        different Hiera data. This environment will be always be the one used regardless\n        of any other factors.\n\n      * --merge first|unique|hash|deep:\n        Specify the merge behavior, overriding any merge behavior from the data's\n        lookup_options. 'first' returns the first value found. 'unique' appends\n        everything to a merged, deduplicated array. 'hash' performs a simple hash\n        merge by overwriting keys of lower lookup priority. 'deep' performs a deep\n        merge on values of Array and Hash type. There are additional options that can\n        be used with 'deep'.\n\n      * --knock-out-prefix <PREFIX-STRING>\n        Can be used with the 'deep' merge strategy. Specifies a prefix to indicate a\n        value should be removed from the final result.\n\n      * --sort-merged-arrays\n        Can be used with the 'deep' merge strategy. When this flag is used, all\n        merged arrays are sorted.\n\n      * --merge-hash-arrays\n        Can be used with the 'deep' merge strategy. When this flag is used, hashes\n        WITHIN arrays are deep-merged with their counterparts by position.\n\n      * --explain-options\n        Explain whether a lookup_options hash affects this lookup, and how that hash\n        was assembled. (lookup_options is how Hiera configures merge behavior in data.)\n\n      * --default <VALUE>\n        A value to return if Hiera can't find a value in data. For emulating calls to\n        the 'lookup()' function that include a default.\n\n      * --type <TYPESTRING>:\n        Assert that the value has the specified type. For emulating calls to the\n        'lookup()' function that include a data type.\n\n      * --compile\n        Perform a full catalog compilation prior to the lookup. If your hierarchy and\n        data only use the $facts, $trusted, and $server_facts variables, you don't\n        need this option; however, if your Hiera configuration uses arbitrary\n        variables set by a Puppet manifest, you might need this option to get accurate\n        data. No catalog compilation takes place unless this flag is given.\n\n      * --render-as s|json|yaml|binary|msgpack\n        Specify the output format of the results; \"s\" means plain text. The default\n        when producing a value is yaml and the default when producing an explanation\n        is s.\n\n      EXAMPLE\n      -------\n        To look up 'key_name' using the Puppet Server node's facts:\n        $ puppet lookup key_name\n\n        To look up 'key_name' using the Puppet Server node's arbitrary variables from a manifest, and\n        classify the node if applicable:\n        $ puppet lookup key_name --compile\n\n        To look up 'key_name' using the Puppet Server node's facts, overridden by facts given in a file:\n        $ puppet lookup key_name --facts fact_file.yaml\n\n        To look up 'key_name' with agent.local's facts:\n        $ puppet lookup --node agent.local key_name\n\n        To get the first value found for 'key_name_one' and 'key_name_two'\n        with agent.local's facts while merging values and knocking out\n        the prefix 'foo' while merging:\n        $ puppet lookup --node agent.local --merge deep --knock-out-prefix foo key_name_one key_name_two\n\n        To lookup 'key_name' with agent.local's facts, and return a default value of\n        'bar' if nothing was found:\n        $ puppet lookup --node agent.local --default bar key_name\n\n        To see an explanation of how the value for 'key_name' would be found, using\n        agent.local's facts:\n        $ puppet lookup --node agent.local --explain key_name\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2015 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n\n    HELP\n  end\n\n  def main\n    keys = command_line.args\n\n    if (options[:sort_merged_arrays] || options[:merge_hash_arrays] || options[:prefix]) && options[:merge] != 'deep'\n      raise _(\"The options %{deep_merge_opts} are only available with '--merge deep'\\n%{run_help}\") % { deep_merge_opts: DEEP_MERGE_OPTIONS, run_help: RUN_HELP }\n    end\n\n    use_default_value = !options[:default_value].nil?\n    merge_options = nil\n\n    merge = options[:merge]\n    unless merge.nil?\n      strategies = Puppet::Pops::MergeStrategy.strategy_keys\n      unless strategies.include?(merge.to_sym)\n        strategies = strategies.map { |k| \"'#{k}'\" }\n        raise _(\"The --merge option only accepts %{strategies}, or %{last_strategy}\\n%{run_help}\") % { strategies: strategies[0...-1].join(', '), last_strategy: strategies.last, run_help: RUN_HELP }\n      end\n\n      if merge == 'deep'\n        merge_options = { 'strategy' => 'deep',\n                          'sort_merged_arrays' => !options[:sort_merged_arrays].nil?,\n                          'merge_hash_arrays' => !options[:merge_hash_arrays].nil? }\n\n        if options[:prefix]\n          merge_options['knockout_prefix'] = options[:prefix]\n        end\n\n      else\n        merge_options = { 'strategy' => merge }\n      end\n    end\n\n    explain_data = !!options[:explain]\n    explain_options = !!options[:explain_options]\n    only_explain_options = explain_options && !explain_data\n    if keys.empty?\n      if only_explain_options\n        # Explain lookup_options for lookup of an unqualified value.\n        keys = Puppet::Pops::Lookup::GLOBAL\n      else\n        raise _('No keys were given to lookup.')\n      end\n    end\n    explain = explain_data || explain_options\n\n    # Format defaults to text (:s) when producing an explanation and :yaml when producing the value\n    format = options[:render_as] || (explain ? :s : :yaml)\n    renderer = Puppet::Network::FormatHandler.format(format)\n    raise _(\"Unknown rendering format '%{format}'\") % { format: format } if renderer.nil?\n\n    generate_scope do |scope|\n      lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, explain ? Puppet::Pops::Lookup::Explainer.new(explain_options, only_explain_options) : nil)\n      begin\n        type = options.include?(:type) ? Puppet::Pops::Types::TypeParser.singleton.parse(options[:type], scope) : nil\n        result = Puppet::Pops::Lookup.lookup(keys, type, options[:default_value], use_default_value, merge_options, lookup_invocation)\n        puts renderer.render(result) unless explain\n      rescue Puppet::DataBinding::LookupError => e\n        lookup_invocation.report_text { e.message }\n        exit(1) unless explain\n      end\n      puts format == :s ? lookup_invocation.explainer.explain : renderer.render(lookup_invocation.explainer.to_hash) if explain\n    end\n    exit(0)\n  end\n\n  def generate_scope\n    if options[:node]\n      node = options[:node]\n    else\n      node = Puppet[:node_name_value]\n\n      # If we want to lookup the node we are currently on\n      # we must returning these settings to their default values\n      Puppet.settings[:facts_terminus] = 'facter'\n    end\n\n    fact_file = options[:fact_file]\n\n    if fact_file\n      if fact_file.end_with?('.json')\n        given_facts = Puppet::Util::Json.load_file(fact_file)\n      elsif fact_file.end_with?('.yml', '.yaml')\n        given_facts = Puppet::Util::Yaml.safe_load_file(fact_file)\n      else\n        given_facts = Puppet::Util::Json.load_file_if_valid(fact_file)\n        given_facts ||= Puppet::Util::Yaml.safe_load_file_if_valid(fact_file)\n      end\n\n      unless given_facts.instance_of?(Hash)\n        raise _(\"Incorrectly formatted data in %{fact_file} given via the --facts flag (only accepts yaml and json files)\") % { fact_file: fact_file }\n      end\n\n      if TRUSTED_INFORMATION_FACTS.any? { |key| given_facts.key? key }\n        unless TRUSTED_INFORMATION_FACTS.all? { |key| given_facts.key? key }\n          raise _(\"When overriding any of the %{trusted_facts_list} facts with %{fact_file} \"\\\n                  \"given via the --facts flag, they must all be overridden.\") % { fact_file: fact_file, trusted_facts_list: TRUSTED_INFORMATION_FACTS.join(',') }\n        end\n      end\n    end\n\n    if node.is_a?(Puppet::Node)\n      node.add_extra_facts(given_facts) if given_facts\n    else # to allow unit tests to pass a node instance\n      facts = retrieve_node_facts(node, given_facts)\n      ni = Puppet::Node.indirection\n      tc = ni.terminus_class\n      if options[:compile]\n        if tc == :plain\n          node = ni.find(node, facts: facts, environment: Puppet[:environment])\n        else\n          begin\n            service = Puppet.runtime[:http]\n            session = service.create_session\n            cert = session.route_to(:ca)\n\n            _, x509 = cert.get_certificate(node)\n            cert = OpenSSL::X509::Certificate.new(x509)\n            Puppet::SSL::Oids.register_puppet_oids\n            trusted = Puppet::Context::TrustedInformation.remote(true, facts.values['certname'] || node, Puppet::SSL::Certificate.from_instance(cert))\n            Puppet.override(trusted_information: trusted) do\n              node = ni.find(node, facts: facts, environment: Puppet[:environment])\n            end\n          rescue\n            Puppet.warning _(\"CA is not available, the operation will continue without using trusted facts.\")\n            node = ni.find(node, facts: facts, environment: Puppet[:environment])\n          end\n        end\n      else\n        ni.terminus_class = :plain\n        node = ni.find(node, facts: facts, environment: Puppet[:environment])\n        ni.terminus_class = tc\n      end\n    end\n    node.environment = Puppet[:environment] if Puppet.settings.set_by_cli?(:environment)\n    node.add_server_facts(Puppet::Node::ServerFacts.load)\n    Puppet[:code] = 'undef' unless options[:compile]\n    compiler = Puppet::Parser::Compiler.new(node)\n    if options[:node]\n      Puppet::Util.skip_external_facts do\n        compiler.compile { |catalog| yield(compiler.topscope); catalog }\n      end\n    else\n      compiler.compile { |catalog| yield(compiler.topscope); catalog }\n    end\n  end\n\n  def retrieve_node_facts(node, given_facts)\n    facts = Puppet::Node::Facts.indirection.find(node, :environment => Puppet.lookup(:current_environment))\n\n    facts = Puppet::Node::Facts.new(node, {}) if facts.nil?\n    facts.add_extra_values(given_facts) if given_facts\n\n    if facts.values.empty?\n      raise _(\"No facts available for target node: %{node}\") % { node: node }\n    end\n\n    facts\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/module.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\n\nclass Puppet::Application::Module < Puppet::Application::FaceBase\nend\n"
  },
  {
    "path": "lib/puppet/application/node.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/indirection_base'\n\nclass Puppet::Application::Node < Puppet::Application::IndirectionBase\nend\n"
  },
  {
    "path": "lib/puppet/application/parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\nrequire_relative '../../puppet/face'\n\nclass Puppet::Application::Parser < Puppet::Application::FaceBase\nend\n"
  },
  {
    "path": "lib/puppet/application/plugin.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/face_base'\nclass Puppet::Application::Plugin < Puppet::Application::FaceBase\n  environment_mode :not_required\nend\n"
  },
  {
    "path": "lib/puppet/application/report.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application/indirection_base'\n\nclass Puppet::Application::Report < Puppet::Application::IndirectionBase\nend\n"
  },
  {
    "path": "lib/puppet/application/resource.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\n\nclass Puppet::Application::Resource < Puppet::Application\n  environment_mode :not_required\n\n  attr_accessor :host, :extra_params\n\n  def preinit\n    @extra_params = [:provider]\n  end\n\n  option(\"--debug\", \"-d\")\n  option(\"--verbose\", \"-v\")\n  option(\"--edit\", \"-e\")\n  option(\"--to_yaml\", \"-y\")\n  option('--fail', '-f')\n\n  option(\"--types\", \"-t\") do |_arg|\n    env = Puppet.lookup(:environments).get(Puppet[:environment]) || create_default_environment\n    types = []\n    Puppet::Type.typeloader.loadall(env)\n    Puppet::Type.eachtype do |t|\n      next if t.name == :component\n\n      types << t.name.to_s\n    end\n    puts types.sort\n    exit(0)\n  end\n\n  option(\"--param PARAM\", \"-p\") do |arg|\n    @extra_params << arg.to_sym\n  end\n\n  def summary\n    _(\"The resource abstraction layer shell\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-resource(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Uses the Puppet RAL to directly interact with the system.\n\n\n      USAGE\n      -----\n      puppet resource [-h|--help] [-d|--debug] [-v|--verbose] [-e|--edit]\n        [-p|--param <parameter>] [-t|--types] [-y|--to_yaml] <type>\n        [<name>] [<attribute>=<value> ...]\n\n\n      DESCRIPTION\n      -----------\n      This command provides simple facilities for converting current system\n      state into Puppet code, along with some ability to modify the current\n      state using Puppet's RAL.\n\n      By default, you must at least provide a type to list, in which case\n      puppet resource will tell you everything it knows about all resources of\n      that type. You can optionally specify an instance name, and puppet\n      resource will only describe that single instance.\n\n      If given a type, a name, and a series of <attribute>=<value> pairs,\n      puppet resource will modify the state of the specified resource.\n      Alternately, if given a type, a name, and the '--edit' flag, puppet\n      resource will write its output to a file, open that file in an editor,\n      and then apply the saved file as a Puppet transaction.\n\n\n      OPTIONS\n      -------\n      Note that any setting that's valid in the configuration\n      file is also a valid long argument. For example, 'ssldir' is a valid\n      setting, so you can specify '--ssldir <directory>' as an\n      argument.\n\n      See the configuration file documentation at\n      https://puppet.com/docs/puppet/latest/configuration.html for the\n      full list of acceptable parameters. A commented list of all\n      configuration options can also be generated by running puppet with\n      '--genconfig'.\n\n      * --debug:\n        Enable full debugging.\n\n      * --edit:\n        Write the results of the query to a file, open the file in an editor,\n        and read the file back in as an executable Puppet manifest.\n\n      * --help:\n        Print this help message.\n\n      * --param:\n        Add more parameters to be outputted from queries.\n\n      * --types:\n        List all available types.\n\n      * --verbose:\n        Print extra information.\n\n      * --to_yaml:\n        Output found resources in yaml format, suitable to use with Hiera and\n        create_resources.\n\n      * --fail:\n        Fails and returns an exit code of 1 if the resource could not be modified.\n\n      EXAMPLE\n      -------\n      This example uses `puppet resource` to return a Puppet configuration for\n      the user `luke`:\n\n          $ puppet resource user luke\n          user { 'luke':\n           home => '/home/luke',\n           uid => '100',\n           ensure => 'present',\n           comment => 'Luke Kanies,,,',\n           gid => '1000',\n           shell => '/bin/bash',\n           groups => ['sysadmin','audio','video','puppet']\n          }\n\n\n      AUTHOR\n      ------\n      Luke Kanies\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def main\n    # If the specified environment does not exist locally, fall back to the default (production) environment\n    env = Puppet.lookup(:environments).get(Puppet[:environment]) || create_default_environment\n\n    Puppet.override(\n      current_environment: env,\n      loaders: Puppet::Pops::Loaders.new(env),\n      stringify_rich: true\n    ) do\n      type, name, params = parse_args(command_line.args)\n\n      raise _(\"Editing with Yaml output is not supported\") if options[:edit] and options[:to_yaml]\n\n      resources = find_or_save_resources(type, name, params)\n\n      if options[:to_yaml]\n        data = resources.map do |resource|\n          resource.prune_parameters(:parameters_to_include => @extra_params).to_hiera_hash\n        end.inject(:merge!)\n        text = YAML.dump(type.downcase => data)\n      else\n        text = resources.map do |resource|\n          resource.prune_parameters(:parameters_to_include => @extra_params).to_manifest.force_encoding(Encoding.default_external)\n        end.join(\"\\n\")\n      end\n\n      options[:edit] ?\n        handle_editing(text) :\n        (puts text)\n    end\n  end\n\n  def setup\n    Puppet::Util::Log.newdestination(:console)\n    set_log_level\n  end\n\n  private\n\n  def local_key(type, name)\n    [type, name].join('/')\n  end\n\n  def handle_editing(text)\n    require 'tempfile'\n    # Prefer the current directory, which is more likely to be secure\n    # and, in the case of interactive use, accessible to the user.\n    tmpfile = Tempfile.new('x2puppet', Dir.pwd, :encoding => Encoding::UTF_8)\n    begin\n      # sync write, so nothing buffers before we invoke the editor.\n      tmpfile.sync = true\n      tmpfile.puts text\n\n      # edit the content\n      system(ENV.fetch(\"EDITOR\", nil) || 'vi', tmpfile.path)\n\n      # ...and, now, pass that file to puppet to apply.  Because\n      # many editors rename or replace the original file we need to\n      # feed the pathname, not the file content itself, to puppet.\n      system('puppet apply -v ' + tmpfile.path)\n    ensure\n      # The temporary file will be safely removed.\n      tmpfile.close(true)\n    end\n  end\n\n  def parse_args(args)\n    type = args.shift or raise _(\"You must specify the type to display\")\n    Puppet::Type.type(type) or raise _(\"Could not find type %{type}\") % { type: type }\n    name = args.shift\n    params = {}\n    args.each do |setting|\n      if setting =~ /^(\\w+)=(.+)$/\n        params[::Regexp.last_match(1)] = ::Regexp.last_match(2)\n      else\n        raise _(\"Invalid parameter setting %{setting}\") % { setting: setting }\n      end\n    end\n\n    [type, name, params]\n  end\n\n  def create_default_environment\n    Puppet.debug(\"Specified environment '#{Puppet[:environment]}' does not exist on the filesystem, defaulting to 'production'\")\n    Puppet[:environment] = :production\n    basemodulepath = Puppet::Node::Environment.split_path(Puppet[:basemodulepath])\n    modulepath = Puppet[:modulepath]\n    modulepath = (modulepath.nil? || modulepath.empty?) ? basemodulepath : Puppet::Node::Environment.split_path(modulepath)\n    Puppet::Node::Environment.create(Puppet[:environment], modulepath, Puppet::Node::Environment::NO_MANIFEST)\n  end\n\n  def find_or_save_resources(type, name, params)\n    key = local_key(type, name)\n\n    Puppet.override(stringify_rich: true) do\n      if name\n        if params.empty?\n          [Puppet::Resource.indirection.find(key)]\n        else\n          resource = Puppet::Resource.new(type, name, :parameters => params)\n\n          # save returns [resource that was saved, transaction log from applying the resource]\n          save_result, report = Puppet::Resource.indirection.save(resource, key)\n          status = report.resource_statuses[resource.ref]\n          raise \"Failed to manage resource #{resource.ref}\" if status&.failed? && options[:fail]\n\n          [save_result]\n        end\n      else\n        if type == \"file\"\n          raise _(\"Listing all file instances is not supported.  Please specify a file or directory, e.g. puppet resource file /etc\")\n        end\n\n        Puppet::Resource.indirection.search(key, {})\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/script.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/configurer'\nrequire_relative '../../puppet/util/profiler/aggregate'\nrequire_relative '../../puppet/parser/script_compiler'\n\nclass Puppet::Application::Script < Puppet::Application\n  option(\"--debug\", \"-d\")\n  option(\"--execute EXECUTE\", \"-e\") do |arg|\n    options[:code] = arg\n  end\n  option(\"--test\", \"-t\")\n  option(\"--verbose\", \"-v\")\n\n  option(\"--logdest LOGDEST\", \"-l\") do |arg|\n    handle_logdest_arg(arg)\n  end\n\n  def summary\n    _(\"Run a puppet manifests as a script without compiling a catalog\")\n  end\n\n  def help\n    <<~HELP\n\n      puppet-script(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Runs a puppet language script without compiling a catalog.\n\n\n      USAGE\n      -----\n      puppet script [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]\n        [-e|--execute]\n        [-l|--logdest syslog|eventlog|<FILE>|console] [--noop]\n        <file>\n\n\n      DESCRIPTION\n      -----------\n      This is a standalone puppet script runner tool; use it to run puppet code\n      without compiling a catalog.\n\n      When provided with a modulepath, via command line or config file, puppet\n      script can load functions, types, tasks and plans from modules.\n\n      OPTIONS\n      -------\n      Note that any setting that's valid in the configuration\n      file is also a valid long argument. For example, 'environment' is a\n      valid setting, so you can specify '--environment mytest'\n      as an argument.\n\n      See the configuration file documentation at\n      https://puppet.com/docs/puppet/latest/configuration.html for the\n      full list of acceptable parameters. A commented list of all\n      configuration options can also be generated by running puppet with\n      '--genconfig'.\n\n      * --debug:\n        Enable full debugging.\n\n      * --help:\n        Print this help message\n\n\n      * --logdest:\n        Where to send log messages. Choose between 'syslog' (the POSIX syslog\n        service), 'eventlog' (the Windows Event Log), 'console', or the path to a log\n        file. Defaults to 'console'.\n        Multiple destinations can be set using a comma separated list\n        (eg: `/path/file1,console,/path/file2`)\"\n\n        A path ending with '.json' will receive structured output in JSON format. The\n        log file will not have an ending ']' automatically written to it due to the\n        appending nature of logging. It must be appended manually to make the content\n        valid JSON.\n\n        A path ending with '.jsonl' will receive structured output in JSON Lines\n        format.\n\n      * --noop:\n        Use 'noop' mode where Puppet runs in a no-op or dry-run mode. This\n        is useful for seeing what changes Puppet will make without actually\n        executing the changes. Applies to tasks only.\n\n      * --execute:\n        Execute a specific piece of Puppet code\n\n      * --verbose:\n        Print extra information.\n\n      EXAMPLE\n      -------\n          $ puppet script -l /tmp/manifest.log manifest.pp\n          $ puppet script --modulepath=/root/dev/modules -e 'notice(\"hello world\")'\n\n\n      AUTHOR\n      ------\n      Henrik Lindberg\n\n\n      COPYRIGHT\n      ---------\n      Copyright (c) 2017 Puppet Inc., LLC Licensed under the Apache 2.0 License\n\n    HELP\n  end\n\n  def app_defaults\n    super.merge({\n                  :default_file_terminus => :file_server,\n                })\n  end\n\n  def run_command\n    if Puppet.features.bolt?\n      Puppet.override(:bolt_executor => Bolt::Executor.new) do\n        main\n      end\n    else\n      raise _(\"Bolt must be installed to use the script application\")\n    end\n  end\n\n  def main\n    # The tasks feature is always on\n    Puppet[:tasks] = true\n\n    # Set the puppet code or file to use.\n    if options[:code] || command_line.args.length == 0\n      Puppet[:code] = options[:code] || STDIN.read\n    else\n      manifest = command_line.args.shift\n      raise _(\"Could not find file %{manifest}\") % { manifest: manifest } unless Puppet::FileSystem.exist?(manifest)\n\n      Puppet.warning(_(\"Only one file can be used per run. Skipping %{files}\") % { files: command_line.args.join(', ') }) if command_line.args.size > 0\n    end\n\n    unless Puppet[:node_name_fact].empty?\n      # Collect the facts specified for that node\n      facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value])\n      raise _(\"Could not find facts for %{node}\") % { node: Puppet[:node_name_value] } unless facts\n\n      Puppet[:node_name_value] = facts.values[Puppet[:node_name_fact]]\n      facts.name = Puppet[:node_name_value]\n    end\n\n    # Find the Node\n    node = Puppet::Node.indirection.find(Puppet[:node_name_value])\n    raise _(\"Could not find node %{node}\") % { node: Puppet[:node_name_value] } unless node\n\n    configured_environment = node.environment || Puppet.lookup(:current_environment)\n\n    apply_environment = manifest ?\n      configured_environment.override_with(:manifest => manifest) :\n      configured_environment\n\n    # Modify the node descriptor to use the special apply_environment.\n    # It is based on the actual environment from the node, or the locally\n    # configured environment if the node does not specify one.\n    # If a manifest file is passed on the command line, it overrides\n    # the :manifest setting of the apply_environment.\n    node.environment = apply_environment\n\n    # TRANSLATION, the string \"For puppet script\" is not user facing\n    Puppet.override({ :current_environment => apply_environment }, \"For puppet script\") do\n      # Merge in the facts.\n      node.merge(facts.values) if facts\n\n      # Add server facts so $server_facts[environment] exists when doing a puppet script\n      # SCRIPT TODO: May be needed when running scripts under orchestrator. Leave it for now.\n      #\n      node.add_server_facts({})\n\n      begin\n        # Compile the catalog\n\n        # When compiling, the compiler traps and logs certain errors\n        # Those that do not lead to an immediate exit are caught by the general\n        # rule and gets logged.\n        #\n        begin\n          # support the following features when evaluating puppet code\n          # * $facts with facts from host running the script\n          # * $settings with 'settings::*' namespace populated, and '$settings::all_local' hash\n          # * $trusted as setup when using puppet apply\n          # * an environment\n          #\n\n          # fixup trusted information\n          node.sanitize()\n\n          compiler = Puppet::Parser::ScriptCompiler.new(node.environment, node.name)\n          topscope = compiler.topscope\n\n          # When scripting the trusted data are always local, but set them anyway\n          topscope.set_trusted(node.trusted_data)\n\n          # Server facts are always about the local node's version etc.\n          topscope.set_server_facts(node.server_facts)\n\n          # Set $facts for the node running the script\n          facts_hash = node.facts.nil? ? {} : node.facts.values\n          topscope.set_facts(facts_hash)\n\n          # create the $settings:: variables\n          topscope.merge_settings(node.environment.name, false)\n\n          compiler.compile()\n        rescue Puppet::Error\n          # already logged and handled by the compiler, including Puppet::ParseErrorWithIssue\n          exit(1)\n        end\n\n        exit(0)\n      rescue => detail\n        Puppet.log_exception(detail)\n        exit(1)\n      end\n    end\n  ensure\n    if @profiler\n      Puppet::Util::Profiler.remove_profiler(@profiler)\n      @profiler.shutdown\n    end\n  end\n\n  def setup\n    exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?\n\n    handle_logdest_arg(Puppet[:logdest])\n    Puppet::Util::Log.newdestination(:console) unless options[:setdest]\n\n    Signal.trap(:INT) do\n      $stderr.puts _(\"Exiting\")\n      exit(1)\n    end\n\n    # TODO: This skips applying the settings catalog for these settings, but\n    # the effect of doing this is unknown. It may be that it only works if there is a puppet\n    # installed where a settings catalog have already been applied...\n    # This saves 1/5th of the startup time\n\n    #    Puppet.settings.use :main, :agent, :ssl\n\n    # When running a script, the catalog is not relevant, and neither is caching of it\n    Puppet::Resource::Catalog.indirection.cache_class = nil\n\n    # we do not want the last report to be persisted\n    Puppet::Transaction::Report.indirection.cache_class = nil\n\n    set_log_level\n\n    if Puppet[:profile]\n      @profiler = Puppet::Util::Profiler.add_profiler(Puppet::Util::Profiler::Aggregate.new(Puppet.method(:info), \"script\"))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application/ssl.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/application'\nrequire_relative '../../puppet/ssl/oids'\n\nclass Puppet::Application::Ssl < Puppet::Application\n  run_mode :agent\n\n  def summary\n    _(\"Manage SSL keys and certificates for puppet SSL clients\")\n  end\n\n  def help\n    <<~HELP\n      puppet-ssl(8) -- #{summary}\n      ========\n\n      SYNOPSIS\n      --------\n      Manage SSL keys and certificates for SSL clients needing\n      to communicate with a puppet infrastructure.\n\n      USAGE\n      -----\n      puppet ssl <action> [-h|--help] [-v|--verbose] [-d|--debug] [--localca] [--target CERTNAME]\n\n\n      OPTIONS\n      -------\n\n      * --help:\n        Print this help message.\n\n      * --verbose:\n        Print extra information.\n\n      * --debug:\n        Enable full debugging.\n\n      * --localca\n        Also clean the local CA certificate and CRL.\n\n      * --target CERTNAME\n        Clean the specified device certificate instead of this host's certificate.\n\n      ACTIONS\n      -------\n\n      * bootstrap:\n        Perform all of the steps necessary to request and download a client\n        certificate. If autosigning is disabled, then puppet will wait every\n        `waitforcert` seconds for its certificate to be signed. To only attempt\n        once and never wait, specify a time of 0. Since `waitforcert` is a\n        Puppet setting, it can be specified as a time interval, such as 30s,\n        5m, 1h.\n\n      * submit_request:\n        Generate a certificate signing request (CSR) and submit it to the CA. If\n        a private and public key pair already exist, they will be used to generate\n        the CSR. Otherwise, a new key pair will be generated. If a CSR has already\n        been submitted with the given `certname`, then the operation will fail.\n\n      * generate_request:\n        Generate a certificate signing request (CSR). If a private and public key\n        pair exist, they will be used to generate the CSR. Otherwise a new key\n        pair will be generated.\n\n      * download_cert:\n        Download a certificate for this host. If the current private key matches\n        the downloaded certificate, then the certificate will be saved and used\n        for subsequent requests. If there is already an existing certificate, it\n        will be overwritten.\n\n      * verify:\n        Verify the private key and certificate are present and match, verify the\n        certificate is issued by a trusted CA, and check revocation status.\n\n      * clean:\n        Remove the private key and certificate related files for this host. If\n        `--localca` is specified, then also remove this host's local copy of the\n        CA certificate(s) and CRL bundle. if `--target CERTNAME` is specified, then\n        remove the files for the specified device on this host instead of this host.\n\n       * show:\n        Print the full-text version of this host's certificate.\n    HELP\n  end\n\n  option('--target CERTNAME') do |arg|\n    options[:target] = arg.to_s\n  end\n  option('--localca')\n  option('--verbose', '-v')\n  option('--debug', '-d')\n\n  def initialize(command_line = Puppet::Util::CommandLine.new)\n    super(command_line)\n\n    @cert_provider = Puppet::X509::CertProvider.new\n    @ssl_provider = Puppet::SSL::SSLProvider.new\n    @machine = Puppet::SSL::StateMachine.new\n    @session = Puppet.runtime[:http].create_session\n  end\n\n  def setup_logs\n    set_log_level(options)\n    Puppet::Util::Log.newdestination(:console)\n  end\n\n  def main\n    if command_line.args.empty?\n      raise Puppet::Error, _(\"An action must be specified.\")\n    end\n\n    if options[:target]\n      # Override the following, as per lib/puppet/application/device.rb\n      Puppet[:certname] = options[:target]\n      Puppet[:confdir]  = File.join(Puppet[:devicedir], Puppet[:certname])\n      Puppet[:vardir]   = File.join(Puppet[:devicedir], Puppet[:certname])\n      Puppet.settings.use(:main, :agent, :device)\n    else\n      Puppet.settings.use(:main, :agent)\n    end\n\n    Puppet::SSL::Oids.register_puppet_oids\n    Puppet::SSL::Oids.load_custom_oid_file(Puppet[:trusted_oid_mapping_file])\n\n    certname = Puppet[:certname]\n    action = command_line.args.first\n    case action\n    when 'submit_request'\n      ssl_context = @machine.ensure_ca_certificates\n      if submit_request(ssl_context)\n        cert = download_cert(ssl_context)\n        unless cert\n          Puppet.info(_(\"The certificate for '%{name}' has not yet been signed\") % { name: certname })\n        end\n      end\n    when 'download_cert'\n      ssl_context = @machine.ensure_ca_certificates\n      cert = download_cert(ssl_context)\n      unless cert\n        raise Puppet::Error, _(\"The certificate for '%{name}' has not yet been signed\") % { name: certname }\n      end\n    when 'generate_request'\n      generate_request(certname)\n    when 'verify'\n      verify(certname)\n    when 'clean'\n      possible_extra_args = command_line.args.drop(1)\n      unless possible_extra_args.empty?\n        raise Puppet::Error, _(<<~END) % { args: possible_extra_args.join(' ') }\n          Extra arguments detected: %{args}\n          Did you mean to run:\n            puppetserver ca clean --certname <name>\n          Or:\n            puppet ssl clean --target <name>\n        END\n      end\n\n      clean(certname)\n    when 'bootstrap'\n      unless Puppet::Util::Log.sendlevel?(:info)\n        Puppet::Util::Log.level = :info\n      end\n      @machine.ensure_client_certificate\n      Puppet.notice(_(\"Completed SSL initialization\"))\n    when 'show'\n      show(certname)\n    else\n      raise Puppet::Error, _(\"Unknown action '%{action}'\") % { action: action }\n    end\n  end\n\n  def show(certname)\n    password = @cert_provider.load_private_key_password\n    ssl_context = @ssl_provider.load_context(certname: certname, password: password)\n    puts ssl_context.client_cert.to_text\n  end\n\n  def submit_request(ssl_context)\n    key = @cert_provider.load_private_key(Puppet[:certname])\n    unless key\n      key = create_key(Puppet[:certname])\n      @cert_provider.save_private_key(Puppet[:certname], key)\n    end\n\n    csr = @cert_provider.create_request(Puppet[:certname], key)\n    route = create_route(ssl_context)\n    route.put_certificate_request(Puppet[:certname], csr, ssl_context: ssl_context)\n    @cert_provider.save_request(Puppet[:certname], csr)\n    Puppet.notice _(\"Submitted certificate request for '%{name}' to %{url}\") % { name: Puppet[:certname], url: route.url }\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 400\n      raise Puppet::Error, _(\"Could not submit certificate request for '%{name}' to %{url} due to a conflict on the server\") % { name: Puppet[:certname], url: route.url }\n    else\n      raise Puppet::Error.new(_(\"Failed to submit certificate request: %{message}\") % { message: e.message }, e)\n    end\n  rescue => e\n    raise Puppet::Error.new(_(\"Failed to submit certificate request: %{message}\") % { message: e.message }, e)\n  end\n\n  def generate_request(certname)\n    key = @cert_provider.load_private_key(certname)\n    unless key\n      key = create_key(certname)\n      @cert_provider.save_private_key(certname, key)\n    end\n\n    csr = @cert_provider.create_request(certname, key)\n    @cert_provider.save_request(certname, csr)\n    Puppet.notice _(\"Generated certificate request in '%{path}'\") % { path: @cert_provider.to_path(Puppet[:requestdir], certname) }\n  rescue => e\n    raise Puppet::Error.new(_(\"Failed to generate certificate request: %{message}\") % { message: e.message }, e)\n  end\n\n  def download_cert(ssl_context)\n    key = @cert_provider.load_private_key(Puppet[:certname])\n\n    # try to download cert\n    route = create_route(ssl_context)\n    Puppet.info _(\"Downloading certificate '%{name}' from %{url}\") % { name: Puppet[:certname], url: route.url }\n\n    _, x509 = route.get_certificate(Puppet[:certname], ssl_context: ssl_context)\n    cert = OpenSSL::X509::Certificate.new(x509)\n    Puppet.notice _(\"Downloaded certificate '%{name}' with fingerprint %{fingerprint}\") % { name: Puppet[:certname], fingerprint: fingerprint(cert) }\n\n    # verify client cert before saving\n    @ssl_provider.create_context(\n      cacerts: ssl_context.cacerts, crls: ssl_context.crls, private_key: key, client_cert: cert\n    )\n    @cert_provider.save_client_cert(Puppet[:certname], cert)\n    @cert_provider.delete_request(Puppet[:certname])\n    cert\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      nil\n    else\n      raise Puppet::Error.new(_(\"Failed to download certificate: %{message}\") % { message: e.message }, e)\n    end\n  rescue => e\n    raise Puppet::Error.new(_(\"Failed to download certificate: %{message}\") % { message: e.message }, e)\n  end\n\n  def verify(certname)\n    password = @cert_provider.load_private_key_password\n    ssl_context = @ssl_provider.load_context(certname: certname, password: password)\n\n    # print from root to client\n    ssl_context.client_chain.reverse.each_with_index do |cert, i|\n      digest = Puppet::SSL::Digest.new('SHA256', cert.to_der)\n      if i == ssl_context.client_chain.length - 1\n        Puppet.notice(\"Verified client certificate '#{cert.subject.to_utf8}' fingerprint #{digest}\")\n      else\n        Puppet.notice(\"Verified CA certificate '#{cert.subject.to_utf8}' fingerprint #{digest}\")\n      end\n    end\n  end\n\n  def clean(certname)\n    # make sure cert has been removed from the CA\n    if certname == Puppet[:ca_server]\n      cert = nil\n\n      begin\n        ssl_context = @machine.ensure_ca_certificates\n        route = create_route(ssl_context)\n        _, cert = route.get_certificate(certname, ssl_context: ssl_context)\n      rescue Puppet::HTTP::ResponseError => e\n        if e.response.code.to_i != 404\n          raise Puppet::Error.new(_(\"Failed to connect to the CA to determine if certificate %{certname} has been cleaned\") % { certname: certname }, e)\n        end\n      rescue => e\n        raise Puppet::Error.new(_(\"Failed to connect to the CA to determine if certificate %{certname} has been cleaned\") % { certname: certname }, e)\n      end\n\n      if cert\n        raise Puppet::Error, _(<<~END) % { certname: certname }\n          The certificate %{certname} must be cleaned from the CA first. To fix this,\n          run the following commands on the CA:\n            puppetserver ca clean --certname %{certname}\n            puppet ssl clean\n        END\n      end\n    end\n\n    paths = {\n      'private key' => Puppet[:hostprivkey],\n      'public key' => Puppet[:hostpubkey],\n      'certificate request' => Puppet[:hostcsr],\n      'certificate' => Puppet[:hostcert],\n      'private key password file' => Puppet[:passfile]\n    }\n    if options[:localca]\n      paths['local CA certificate'] = Puppet[:localcacert]\n      paths['local CRL'] = Puppet[:hostcrl]\n    end\n    paths.each_pair do |label, path|\n      if Puppet::FileSystem.exist?(path)\n        Puppet::FileSystem.unlink(path)\n        Puppet.notice _(\"Removed %{label} %{path}\") % { label: label, path: path }\n      end\n    end\n  end\n\n  private\n\n  def fingerprint(cert)\n    Puppet::SSL::Digest.new(nil, cert.to_der)\n  end\n\n  def create_route(ssl_context)\n    @session.route_to(:ca, ssl_context: ssl_context)\n  end\n\n  def create_key(certname)\n    if Puppet[:key_type] == 'ec'\n      Puppet.info _(\"Creating a new EC SSL key for %{name} using curve %{curve}\") % { name: certname, curve: Puppet[:named_curve] }\n      OpenSSL::PKey::EC.generate(Puppet[:named_curve])\n    else\n      Puppet.info _(\"Creating a new SSL key for %{name}\") % { name: certname }\n      OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/application.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'optparse'\nrequire_relative '../puppet/util/command_line'\nrequire_relative '../puppet/util/constant_inflector'\nrequire_relative '../puppet/error'\nrequire_relative '../puppet/application_support'\n\nmodule Puppet\n# Defines an abstract Puppet application.\n#\n# # Usage\n#\n# To create a new application extend `Puppet::Application`. Derived applications\n# must implement the `main` method and should implement the `summary` and\n# `help` methods in order to be included in `puppet help`, and should define\n# application-specific options. For example:\n#\n# ```\n# class Puppet::Application::Example < Puppet::Application\n#\n#   def summary\n#     \"My puppet example application\"\n#   end\n#\n#   def help\n#     <<~HELP\n#     puppet-example(8) -- #{summary}\n#     ...\n#     HELP\n#   end\n#\n#   # define arg with a required option\n#   option(\"--arg ARGUMENT\") do |v|\n#     options[:arg] = v\n#   end\n#\n#   # define arg with an optional option\n#   option(\"--maybe [ARGUMENT]\") do |v|\n#     options[:maybe] = v\n#   end\n#\n#   # define long and short arg\n#   option(\"--all\", \"-a\")\n#\n#   def initialize(command_line = Puppet::Util::CommandLine.new)\n#     super\n#     @data = {}\n#   end\n#\n#   def main\n#     # call action\n#     send(@command_line.args.shift)\n#   end\n#\n#   def read\n#     # read action\n#   end\n#\n#   def write\n#     # write action\n#   end\n#\n# end\n# ```\n#\n# Puppet defines the following application lifecycle methods that are called in\n# the following order:\n#\n# * {#initialize}\n# * {#initialize_app_defaults}\n# * {#preinit}\n# * {#parse_options}\n# * {#setup}\n# * {#main}\n#\n# ## Execution state\n# The class attributes/methods of Puppet::Application serve as a global place to set and query the execution\n# status of the application: stopping, restarting, etc.  The setting of the application status does not directly\n# affect its running status; it's assumed that the various components within the application will consult these\n# settings appropriately and affect their own processing accordingly.  Control operations (signal handlers and\n# the like) should set the status appropriately to indicate to the overall system that it's the process of\n# stopping or restarting (or just running as usual).\n#\n# So, if something in your application needs to stop the process, for some reason, you might consider:\n#\n# ```\n#  def stop_me!\n#    # indicate that we're stopping\n#    Puppet::Application.stop!\n#    # ...do stuff...\n#  end\n# ```\n#\n# And, if you have some component that involves a long-running process, you might want to consider:\n#\n# ```\n#  def my_long_process(giant_list_to_munge)\n#    giant_list_to_munge.collect do |member|\n#      # bail if we're stopping\n#      return if Puppet::Application.stop_requested?\n#      process_member(member)\n#    end\n#  end\n# ```\n# @abstract\n# @api public\nclass Application\n  require_relative '../puppet/util'\n  include Puppet::Util\n\n  DOCPATTERN = ::File.expand_path(::File.dirname(__FILE__) + \"/util/command_line/*\")\n  CommandLineArgs = Struct.new(:subcommand_name, :args)\n\n  @loader = Puppet::Util::Autoload.new(self, 'puppet/application')\n\n  class << self\n    include Puppet::Util\n\n    attr_accessor :run_status\n\n    def clear!\n      self.run_status = nil\n    end\n\n    # Signal that the application should stop.\n    # @api public\n    def stop!\n      self.run_status = :stop_requested\n    end\n\n    # Signal that the application should restart.\n    # @api public\n    def restart!\n      self.run_status = :restart_requested\n    end\n\n    # Indicates that Puppet::Application.restart! has been invoked and components should\n    # do what is necessary to facilitate a restart.\n    # @api public\n    def restart_requested?\n      :restart_requested == run_status\n    end\n\n    # Indicates that Puppet::Application.stop! has been invoked and components should do what is necessary\n    # for a clean stop.\n    # @api public\n    def stop_requested?\n      :stop_requested == run_status\n    end\n\n    # Indicates that one of stop! or start! was invoked on Puppet::Application, and some kind of process\n    # shutdown/short-circuit may be necessary.\n    # @api public\n    def interrupted?\n      [:restart_requested, :stop_requested].include? run_status\n    end\n\n    # Indicates that Puppet::Application believes that it's in usual running run_mode (no stop/restart request\n    # currently active).\n    # @api public\n    def clear?\n      run_status.nil?\n    end\n\n    # Only executes the given block if the run status of Puppet::Application is clear (no restarts, stops,\n    # etc. requested).\n    # Upon block execution, checks the run status again; if a restart has been requested during the block's\n    # execution, then controlled_run will send a new HUP signal to the current process.\n    # Thus, long-running background processes can potentially finish their work before a restart.\n    def controlled_run(&block)\n      return unless clear?\n\n      result = block.call\n      Process.kill(:HUP, $PID) if restart_requested?\n      result\n    end\n\n    # used to declare code that handle an option\n    def option(*options, &block)\n      long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\\[no-\\])?([^ =]+).*$/, '\\1').tr('-', '_')\n      fname = \"handle_#{long}\".intern\n      if block_given?\n        define_method(fname, &block)\n      else\n        define_method(fname) do |value|\n          self.options[long.to_s.to_sym] = value\n        end\n      end\n      option_parser_commands << [options, fname]\n    end\n\n    def banner(banner = nil)\n      @banner ||= banner\n    end\n\n    def option_parser_commands\n      @option_parser_commands ||= (\n        superclass.respond_to?(:option_parser_commands) ? superclass.option_parser_commands.dup : []\n      )\n      @option_parser_commands\n    end\n\n    # @return [Array<String>] the names of available applications\n    # @api public\n    def available_application_names\n      # Use our configured environment to load the application, as it may\n      # be in a module we installed locally, otherwise fallback to our\n      # current environment (*root*). Once we load the application the\n      # current environment will change from *root* to the application\n      # specific environment.\n      environment = Puppet.lookup(:environments).get(Puppet[:environment]) ||\n                    Puppet.lookup(:current_environment)\n      @loader.files_to_load(environment).map do |fn|\n        ::File.basename(fn, '.rb')\n      end.uniq\n    end\n\n    # Finds the class for a given application and loads the class. This does\n    # not create an instance of the application, it only gets a handle to the\n    # class. The code for the application is expected to live in a ruby file\n    # `puppet/application/#{name}.rb` that is available on the `$LOAD_PATH`.\n    #\n    # @param application_name [String] the name of the application to find (eg. \"apply\").\n    # @return [Class] the Class instance of the application that was found.\n    # @raise [Puppet::Error] if the application class was not found.\n    # @raise [LoadError] if there was a problem loading the application file.\n    # @api public\n    def find(application_name)\n      begin\n        require @loader.expand(application_name.to_s.downcase)\n      rescue LoadError => e\n        Puppet.log_and_raise(e, _(\"Unable to find application '%{application_name}'. %{error}\") % { application_name: application_name, error: e })\n      end\n\n      class_name = Puppet::Util::ConstantInflector.file2constant(application_name.to_s)\n\n      clazz = try_load_class(class_name)\n\n      ################################################################\n      #### Begin 2.7.x backward compatibility hack;\n      ####  eventually we need to issue a deprecation warning here,\n      ####  and then get rid of this stanza in a subsequent release.\n      ################################################################\n      if clazz.nil?\n        class_name = application_name.capitalize\n        clazz = try_load_class(class_name)\n      end\n      ################################################################\n      #### End 2.7.x backward compatibility hack\n      ################################################################\n\n      if clazz.nil?\n        raise Puppet::Error, _(\"Unable to load application class '%{class_name}' from file 'puppet/application/%{application_name}.rb'\") % { class_name: class_name, application_name: application_name }\n      end\n\n      clazz\n    end\n\n    # Given the fully qualified name of a class, attempt to get the class instance.\n    # @param [String] class_name the fully qualified name of the class to try to load\n    # @return [Class] the Class instance, or nil? if it could not be loaded.\n    def try_load_class(class_name)\n      const_defined?(class_name) ? const_get(class_name) : nil\n    end\n    private :try_load_class\n\n    # Return an instance of the specified application.\n    #\n    # @param [Symbol] name the lowercase name of the application\n    # @return [Puppet::Application] an instance of the specified name\n    # @raise [Puppet::Error] if the application class was not found.\n    # @raise [LoadError] if there was a problem loading the application file.\n    # @api public\n    def [](name)\n      find(name).new\n    end\n\n    # Sets or gets the run_mode name. Sets the run_mode name if a mode_name is\n    # passed. Otherwise, gets the run_mode or a default run_mode\n    # @api public\n    def run_mode(mode_name = nil)\n      if mode_name\n        Puppet.settings.preferred_run_mode = mode_name\n      end\n\n      return @run_mode if @run_mode and !mode_name\n\n      require_relative '../puppet/util/run_mode'\n      @run_mode = Puppet::Util::RunMode[mode_name || Puppet.settings.preferred_run_mode]\n    end\n\n    # Sets environment_mode name. When acting as a compiler, the environment mode\n    # should be `:local` since the directory must exist to compile the catalog.\n    # When acting as an agent, the environment mode should be `:remote` since\n    # the Puppet[:environment] setting refers to an environment directoy on a remote\n    # system. The `:not_required` mode is for cases where the application does not\n    # need an environment to run.\n    #\n    # @param mode_name [Symbol] The name of the environment mode to run in. May\n    #   be one of `:local`, `:remote`, or `:not_required`. This impacts where the\n    #   application looks for its specified environment. If `:not_required` or\n    #   `:remote` are set, the application will not fail if the environment does\n    #   not exist on the local filesystem.\n    # @api public\n    def environment_mode(mode_name)\n      raise Puppet::Error, _(\"Invalid environment mode '%{mode_name}'\") % { mode_name: mode_name } unless [:local, :remote, :not_required].include?(mode_name)\n\n      @environment_mode = mode_name\n    end\n\n    # Gets environment_mode name. If none is set with `environment_mode=`,\n    # default to :local.\n    # @return [Symbol] The current environment mode\n    # @api public\n    def get_environment_mode\n      @environment_mode || :local\n    end\n\n    # This is for testing only\n    # @api public\n    def clear_everything_for_tests\n      @run_mode = @banner = @run_status = @option_parser_commands = nil\n    end\n  end\n\n  attr_reader :options, :command_line\n\n  # Every app responds to --version\n  # See also `lib/puppet/util/command_line.rb` for some special case early\n  # handling of this.\n  option(\"--version\", \"-V\") do |_arg|\n    puts Puppet.version\n    exit(0)\n  end\n\n  # Every app responds to --help\n  option(\"--help\", \"-h\") do |_v|\n    puts help\n    exit(0)\n  end\n\n  # Initialize the application receiving the {Puppet::Util::CommandLine} object\n  # containing the application name and arguments.\n  #\n  # @param command_line [Puppet::Util::CommandLine] An instance of the command line to create the application with\n  # @api public\n  def initialize(command_line = Puppet::Util::CommandLine.new)\n    @command_line = CommandLineArgs.new(command_line.subcommand_name, command_line.args.dup)\n    @options = {}\n  end\n\n  # Now that the `run_mode` has been resolved, return default settings for the\n  # application. Note these values may be overridden when puppet's configuration\n  # is loaded later.\n  #\n  # @example To override the facts terminus:\n  #   def app_defaults\n  #     super.merge({\n  #       :facts_terminus => 'yaml'\n  #     })\n  #   end\n  #\n  # @return [Hash<String, String>] default application settings\n  # @api public\n  def app_defaults\n    Puppet::Settings.app_defaults_for_run_mode(self.class.run_mode).merge(\n      :name => name\n    )\n  end\n\n  # Initialize application defaults. It's usually not necessary to override this method.\n  # @return [void]\n  # @api public\n  def initialize_app_defaults\n    Puppet.settings.initialize_app_defaults(app_defaults)\n  end\n\n  # The preinit block is the first code to be called in your application, after\n  # `initialize`, but before option parsing, setup or command execution. It is\n  # usually not necessary to override this method.\n  # @return [void]\n  # @api public\n  def preinit\n  end\n\n  # Call in setup of subclass to deprecate an application.\n  # @return [void]\n  # @api public\n  def deprecate\n    @deprecated = true\n  end\n\n  # Return true if this application is deprecated.\n  # @api public\n  def deprecated?\n    @deprecated\n  end\n\n  # Execute the application. This method should not be overridden.\n  # @return [void]\n  # @api public\n  def run\n    # I don't really like the names of these lifecycle phases.  It would be nice to change them to some more meaningful\n    # names, and make deprecated aliases.  --cprice 2012-03-16\n\n    exit_on_fail(_(\"Could not get application-specific default settings\")) do\n      initialize_app_defaults\n    end\n\n    Puppet::ApplicationSupport.push_application_context(self.class.run_mode, self.class.get_environment_mode)\n\n    exit_on_fail(_(\"Could not initialize\"))                { preinit }\n    exit_on_fail(_(\"Could not parse application options\")) { parse_options }\n    exit_on_fail(_(\"Could not prepare for execution\"))     { setup }\n\n    if deprecated?\n      Puppet.deprecation_warning(_(\"`puppet %{name}` is deprecated and will be removed in a future release.\") % { name: name })\n    end\n\n    exit_on_fail(_(\"Could not configure routes from %{route_file}\") % { route_file: Puppet[:route_file] }) { configure_indirector_routes }\n    exit_on_fail(_(\"Could not log runtime debug info\"))                       { log_runtime_environment }\n    exit_on_fail(_(\"Could not run\"))                                          { run_command }\n  end\n\n  # This method must be overridden and perform whatever action is required for\n  # the application. The `command_line` reader contains the actions and\n  # arguments.\n  # @return [void]\n  # @api public\n  def main\n    raise NotImplementedError, _(\"No valid command or main\")\n  end\n\n  # Run the application. By default, it calls {#main}.\n  # @return [void]\n  # @api public\n  def run_command\n    main\n  end\n\n  # Setup the application. It is usually not necessary to override this method.\n  # @return [void]\n  # @api public\n  def setup\n    setup_logs\n  end\n\n  # Setup logging. By default the `console` log destination will only be created\n  # if `debug` or `verbose` is specified on the command line. Override to customize\n  # the logging behavior.\n  # @return [void]\n  # @api public\n  def setup_logs\n    handle_logdest_arg(Puppet[:logdest]) unless options[:setdest]\n\n    unless options[:setdest]\n      if options[:debug] || options[:verbose]\n        Puppet::Util::Log.newdestination(:console)\n      end\n    end\n\n    set_log_level\n\n    Puppet::Util::Log.setup_default unless options[:setdest]\n  end\n\n  def set_log_level(opts = nil)\n    opts ||= options\n    if opts[:debug]\n      Puppet::Util::Log.level = :debug\n    elsif opts[:verbose] && !Puppet::Util::Log.sendlevel?(:info)\n      Puppet::Util::Log.level = :info\n    end\n  end\n\n  def handle_logdest_arg(arg)\n    return if arg.nil?\n\n    logdest = arg.split(',').map!(&:strip)\n    Puppet[:logdest] = arg\n\n    logdest.each do |dest|\n      Puppet::Util::Log.newdestination(dest)\n      options[:setdest] = true\n    rescue => detail\n      Puppet.log_and_raise(detail, _(\"Could not set logdest to %{dest}.\") % { dest: arg })\n    end\n  end\n\n  def configure_indirector_routes\n    Puppet::ApplicationSupport.configure_indirector_routes(name.to_s)\n  end\n\n  # Output basic information about the runtime environment for debugging\n  # purposes.\n  #\n  # @param extra_info [Hash{String => #to_s}] a flat hash of extra information\n  #   to log. Intended to be passed to super by subclasses.\n  # @return [void]\n  # @api public\n  def log_runtime_environment(extra_info = nil)\n    runtime_info = {\n      'puppet_version' => Puppet.version,\n      'ruby_version' => RUBY_VERSION,\n      'run_mode' => self.class.run_mode.name\n    }\n    unless Puppet::Util::Platform.jruby_fips?\n      runtime_info['openssl_version'] = \"'#{OpenSSL::OPENSSL_VERSION}'\"\n      runtime_info['openssl_fips'] = OpenSSL::OPENSSL_FIPS\n    end\n    runtime_info['default_encoding'] = Encoding.default_external\n    runtime_info.merge!(extra_info) unless extra_info.nil?\n\n    Puppet.debug 'Runtime environment: ' + runtime_info.map { |k, v| k + '=' + v.to_s }.join(', ')\n  end\n\n  # Options defined with the `option` method are parsed from settings and the command line.\n  # Refer to {OptionParser} documentation for the exact format. Options are parsed as follows:\n  #\n  # * If the option method is given a block, then it will be called whenever the option is encountered in the command-line argument.\n  # * If the option method has no block, then the default option handler will store the argument in the `options` instance variable.\n  # * If a given option was not defined by an `option` method, but it exists as a Puppet setting:\n  #   * if `unknown` was used with a block, it will be called with the option name and argument.\n  #   * if `unknown` wasn't used, then the option/argument is handed to Puppet.settings.handlearg for\n  #     a default behavior.\n  #  * The `-h` and `--help` options are automatically handled by the command line before creating the application.\n  #\n  # Options specified on the command line override settings. It is usually not\n  # necessary to override this method.\n  # @return [void]\n  # @api public\n  def parse_options\n    # Create an option parser\n    option_parser = OptionParser.new(self.class.banner)\n\n    # Here we're building up all of the options that the application may need to handle.  The main\n    # puppet settings defined in \"defaults.rb\" have already been parsed once (in command_line.rb) by\n    # the time we get here; however, our app may wish to handle some of them specially, so we need to\n    # make the parser aware of them again.  We might be able to make this a bit more efficient by\n    # re-using the parser object that gets built up in command_line.rb.  --cprice 2012-03-16\n\n    # Add all global options to it.\n    Puppet.settings.optparse_addargs([]).each do |option|\n      option_parser.on(*option) do |arg|\n        handlearg(option[0], arg)\n      end\n    end\n\n    # Add options that are local to this application, which were\n    # created using the \"option()\" metaprogramming method.  If there\n    # are any conflicts, this application's options will be favored.\n    self.class.option_parser_commands.each do |options, fname|\n      option_parser.on(*options) do |value|\n        # Call the method that \"option()\" created.\n        send(fname, value)\n      end\n    end\n\n    # Scan command line.  We just hand any exceptions to our upper levels,\n    # rather than printing help and exiting, so that we can meaningfully\n    # respond with context-sensitive help if we want to. --daniel 2011-04-12\n    option_parser.parse!(command_line.args)\n  end\n\n  def handlearg(opt, val)\n    opt, val = Puppet::Settings.clean_opt(opt, val)\n    send(:handle_unknown, opt, val) if respond_to?(:handle_unknown)\n  end\n\n  # this is used for testing\n  def self.exit(code)\n    exit(code)\n  end\n\n  def name\n    self.class.to_s.sub(/.*::/, \"\").downcase.to_sym\n  end\n\n  # Return the text to display when running `puppet help`.\n  # @return [String] The help to display\n  # @api public\n  def help\n    _(\"No help available for puppet %{app_name}\") % { app_name: name }\n  end\n\n  # The description used in top level `puppet help` output\n  # If left empty in implementations, we will attempt to extract\n  # the summary from the help text itself.\n  # @return [String]\n  # @api public\n  def summary\n    \"\"\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/application_support.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/node/environment'\nrequire_relative '../puppet/file_system'\nrequire_relative '../puppet/indirector'\n\nmodule Puppet\n  module ApplicationSupport\n    # Pushes a Puppet Context configured with a remote environment for an agent\n    # (one that exists at the master end), and a regular environment for other\n    # modes. The configuration is overridden with options from the command line\n    # before being set in a pushed Puppet Context.\n    #\n    # @param run_mode [Puppet::Util::RunMode] Puppet's current Run Mode.\n    # @param environment_mode [Symbol] optional, Puppet's\n    #   current Environment Mode. Defaults to :local\n    # @return [void]\n    # @api private\n    def self.push_application_context(run_mode, environment_mode = :local)\n      Puppet.push_context_global(Puppet.base_context(Puppet.settings), \"Update for application settings (#{run_mode})\")\n      # This use of configured environment is correct, this is used to establish\n      # the defaults for an application that does not override, or where an override\n      # has not been made from the command line.\n      #\n      configured_environment_name = Puppet[:environment]\n      if run_mode.name == :agent\n        configured_environment = Puppet::Node::Environment.remote(configured_environment_name)\n      elsif environment_mode == :not_required\n        configured_environment =\n          Puppet.lookup(:environments).get(configured_environment_name) || Puppet::Node::Environment.remote(configured_environment_name)\n      else\n        configured_environment = Puppet.lookup(:environments).get!(configured_environment_name)\n      end\n      configured_environment = configured_environment.override_from_commandline(Puppet.settings)\n\n      # Setup a new context using the app's configuration\n      Puppet.push_context({ :current_environment => configured_environment },\n                          \"Update current environment from application's configuration\")\n    end\n\n    # Reads the routes YAML settings from the file specified by Puppet[:route_file]\n    # and resets indirector termini for the current application class if listed.\n    #\n    # For instance, PE uses this to set the master facts terminus\n    # to 'puppetdb' and its cache terminus to 'yaml'.\n    #\n    # @param application_name [String] The name of the current application.\n    # @return [void]\n    # @api private\n    def self.configure_indirector_routes(application_name)\n      route_file = Puppet[:route_file]\n      if Puppet::FileSystem.exist?(route_file)\n        routes = Puppet::Util::Yaml.safe_load_file(route_file, [Symbol])\n        if routes[\"server\"] && routes[\"master\"]\n          Puppet.warning(\"Route file #{route_file} contains both server and master route settings.\")\n        elsif routes[\"server\"] && !routes[\"master\"]\n          routes[\"master\"] = routes[\"server\"]\n        elsif routes[\"master\"] && !routes[\"server\"]\n          routes[\"server\"] = routes[\"master\"]\n        end\n        application_routes = routes[application_name]\n        Puppet::Indirector.configure_routes(application_routes) if application_routes\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/coercion.rb",
    "content": "# frozen_string_literal: true\n\n# Various methods used to coerce values into a canonical form.\n#\n# @api private\nmodule Puppet::Coercion\n  # Try to coerce various input values into boolean true/false\n  #\n  # Only a very limited subset of values are allowed. This method does not try\n  # to provide a generic \"truthiness\" system.\n  #\n  # @param value [Boolean, Symbol, String]\n  # @return [Boolean]\n  # @raise\n  # @api private\n  def self.boolean(value)\n    # downcase strings\n    if value.respond_to? :downcase\n      value = value.downcase\n    end\n\n    case value\n    when true, :true, 'true', :yes, 'yes' # rubocop:disable Lint/BooleanSymbol\n      true\n    when false, :false, 'false', :no, 'no' # rubocop:disable Lint/BooleanSymbol\n      false\n    else\n      fail('expected a boolean value')\n    end\n  end\n\n  # Return the list of acceptable boolean values.\n  #\n  # This is limited to lower-case, even though boolean() is case-insensitive.\n  #\n  # @return [Array]\n  # @raise\n  # @api private\n  def self.boolean_values\n    %w[true false yes no]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/compilable_resource_type.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet'\n# The CompilableResourceType module should be either included in a class or used as a class extension\n# to mark that the instance used as the 'resource type' of a resource instance\n# is an object that is compatible with Puppet::Type's API wrt. compiling.\n# Puppet Resource Types written in Ruby use a meta programmed Ruby Class as the type. Those classes\n# are subtypes of Puppet::Type. Meta data (Pcore/puppet language) based resource types uses instances of\n# a class instead.\n#\nmodule Puppet::CompilableResourceType\n  # All 3.x resource types implemented in Ruby using Puppet::Type respond true.\n  # Other kinds of implementations should reimplement and return false.\n  def is_3x_ruby_plugin?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/concurrent/lock.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/concurrent/synchronized'\n\nmodule Puppet\nmodule Concurrent\n# A simple lock that at the moment only does any locking on jruby\nclass Lock\n  include Puppet::Concurrent::Synchronized\n  def synchronize\n    yield\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/concurrent/synchronized.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Concurrent\n# Including Puppet::Concurrent::Synchronized into a class when running on JRuby\n# causes all of its instance methods to be synchronized on the instance itself.\n# When running on MRI it has no effect.\nif RUBY_PLATFORM == 'java'\n  require 'jruby/synchronized'\n  Synchronized = JRuby::Synchronized\nelse\n  module Synchronized; end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/concurrent/thread_local_singleton.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  module Concurrent\n    module ThreadLocalSingleton\n      def singleton\n        key = (name + \".singleton\").intern\n        thread = Thread.current\n        value = thread.thread_variable_get(key)\n        if value.nil?\n          value = new\n          thread.thread_variable_set(key, value)\n        end\n        value\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/concurrent.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Concurrent\nend\n"
  },
  {
    "path": "lib/puppet/configurer/downloader.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/configurer'\nrequire_relative '../../puppet/resource/catalog'\n\nclass Puppet::Configurer::Downloader\n  attr_reader :name, :path, :source, :ignore\n\n  # Evaluate our download, returning the list of changed values.\n  def evaluate\n    Puppet.info _(\"Retrieving %{name}\") % { name: name }\n\n    files = []\n    begin\n      catalog.apply do |trans|\n        unless Puppet[:ignore_plugin_errors]\n          # Propagate the first failure associated with the transaction. The any_failed?\n          # method returns the first resource status that failed or nil, not a boolean.\n          first_failure = trans.any_failed?\n          if first_failure\n            event = (first_failure.events || []).first\n            detail = event ? event.message : 'unknown'\n            raise Puppet::Error, _(\"Failed to retrieve %{name}: %{detail}\") % { name: name, detail: detail }\n          end\n        end\n\n        trans.changed?.each do |resource|\n          yield resource if block_given?\n          files << resource[:path]\n        end\n      end\n    rescue Puppet::Error => detail\n      if Puppet[:ignore_plugin_errors]\n        Puppet.log_exception(detail, _(\"Could not retrieve %{name}: %{detail}\") % { name: name, detail: detail })\n      else\n        raise detail\n      end\n    end\n    files\n  end\n\n  def initialize(name, path, source, ignore = nil, environment = nil, source_permissions = :ignore)\n    @name = name\n    @path = path\n    @source = source\n    @ignore = ignore\n    @environment = environment\n    @source_permissions = source_permissions\n  end\n\n  def file\n    unless @file\n      args = default_arguments.merge(:path => path, :source => source)\n      args[:ignore] = ignore.split if ignore\n      @file = Puppet::Type.type(:file).new(args)\n    end\n    @file\n  end\n\n  def catalog\n    unless @catalog\n      @catalog = Puppet::Resource::Catalog.new(\"PluginSync\", @environment)\n      @catalog.host_config = false\n      @catalog.add_resource(file)\n    end\n    @catalog\n  end\n\n  private\n\n  def default_arguments\n    defargs = {\n      :path => path,\n      :recurse => true,\n      :links => :follow,\n      :source => source,\n      :source_permissions => @source_permissions,\n      :tag => name,\n      :purge => true,\n      :force => true,\n      :backup => false,\n      :noop => false,\n      :max_files => -1\n    }\n    unless Puppet::Util::Platform.windows?\n      defargs[:owner] = Process.uid\n      defargs[:group] = Process.gid\n    end\n    defargs\n  end\nend\n"
  },
  {
    "path": "lib/puppet/configurer/fact_handler.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/facts/facter'\n\nrequire_relative '../../puppet/configurer'\nrequire_relative '../../puppet/configurer/downloader'\n\n# Break out the code related to facts.  This module is\n# just included into the agent, but having it here makes it\n# easier to test.\nmodule Puppet::Configurer::FactHandler\n  def find_facts\n    # This works because puppet agent configures Facts to use 'facter' for\n    # finding facts and the 'rest' terminus for caching them.  Thus, we'll\n    # compile them and then \"cache\" them on the server.\n\n    facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value], :environment => Puppet::Node::Environment.remote(@environment))\n    unless Puppet[:node_name_fact].empty?\n      Puppet[:node_name_value] = facts.values[Puppet[:node_name_fact]]\n      facts.name = Puppet[:node_name_value]\n    end\n    facts\n  rescue SystemExit, NoMemoryError\n    raise\n  rescue Exception => detail\n    message = _(\"Could not retrieve local facts: %{detail}\") % { detail: detail }\n    Puppet.log_exception(detail, message)\n    raise Puppet::Error, message, detail.backtrace\n  end\n\n  def facts_for_uploading\n    encode_facts(find_facts)\n  end\n\n  def encode_facts(facts)\n    # facts = find_facts\n\n    # NOTE: :facts specified as parameters are URI encoded here,\n    # then  encoded for a second time depending on their length:\n    #\n    # <= 1024 characters sent via query string of a HTTP GET, additionally query string encoded\n    # > 1024 characters sent in POST data, additionally x-www-form-urlencoded\n    # so it's only important that encoding method here return original values\n    # correctly when CGI.unescape called against it (in compiler code)\n    if Puppet[:preferred_serialization_format] == \"pson\"\n      { :facts_format => :pson, :facts => Puppet::Util.uri_query_encode(facts.render(:pson)) }\n    else\n      { :facts_format => 'application/json', :facts => Puppet::Util.uri_query_encode(facts.render(:json)) }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/configurer/plugin_handler.rb",
    "content": "# frozen_string_literal: true\n\n# Break out the code related to plugins.  This module is\n# just included into the agent, but having it here makes it\n# easier to test.\nrequire_relative '../../puppet/configurer'\n\nclass Puppet::Configurer::PluginHandler\n  SUPPORTED_LOCALES_MOUNT_AGENT_VERSION = Gem::Version.new(\"5.3.4\")\n\n  def download_plugins(environment)\n    source_permissions = Puppet::Util::Platform.windows? ? :ignore : :use\n\n    plugin_downloader = Puppet::Configurer::Downloader.new(\n      \"plugin\",\n      Puppet[:plugindest],\n      Puppet[:pluginsource],\n      Puppet[:pluginsignore],\n      environment\n    )\n    plugin_fact_downloader = Puppet::Configurer::Downloader.new(\n      \"pluginfacts\",\n      Puppet[:pluginfactdest],\n      Puppet[:pluginfactsource],\n      Puppet[:pluginsignore],\n      environment,\n      source_permissions\n    )\n\n    result = []\n    result += plugin_fact_downloader.evaluate\n    result += plugin_downloader.evaluate\n\n    unless Puppet[:disable_i18n]\n      # until file metadata/content are using the rest client, we need to check\n      # both :server_agent_version and the session to see if the server supports\n      # the \"locales\" mount\n      server_agent_version = Puppet.lookup(:server_agent_version) { \"0.0\" }\n      locales = Gem::Version.new(server_agent_version) >= SUPPORTED_LOCALES_MOUNT_AGENT_VERSION\n      unless locales\n        session = Puppet.lookup(:http_session)\n        locales = session.supports?(:fileserver, 'locales') || session.supports?(:puppet, 'locales')\n      end\n\n      if locales\n        locales_downloader = Puppet::Configurer::Downloader.new(\n          \"locales\",\n          Puppet[:localedest],\n          Puppet[:localesource],\n          Puppet[:pluginsignore] + \" *.pot config.yaml\",\n          environment\n        )\n        result += locales_downloader.evaluate\n      end\n    end\n\n    Puppet::Util::Autoload.reload_changed(Puppet.lookup(:current_environment))\n\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/configurer.rb",
    "content": "# frozen_string_literal: true\n\n# The client for interacting with the puppetmaster config server.\nrequire 'timeout'\nrequire_relative '../puppet/util'\nrequire 'securerandom'\n# require 'puppet/parser/script_compiler'\nrequire_relative '../puppet/pops/evaluator/deferred_resolver'\n\nclass Puppet::Configurer\n  require_relative 'configurer/fact_handler'\n  require_relative 'configurer/plugin_handler'\n\n  include Puppet::Configurer::FactHandler\n\n  # For benchmarking\n  include Puppet::Util\n\n  attr_reader :environment\n\n  # Provide more helpful strings to the logging that the Agent does\n  def self.to_s\n    _(\"Puppet configuration client\")\n  end\n\n  def self.should_pluginsync?\n    if Puppet[:use_cached_catalog]\n      false\n    else\n      true\n    end\n  end\n\n  def execute_postrun_command\n    execute_from_setting(:postrun_command)\n  end\n\n  def execute_prerun_command\n    execute_from_setting(:prerun_command)\n  end\n\n  # Initialize and load storage\n  def init_storage\n    Puppet::Util::Storage.load\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Removing corrupt state file %{file}: %{detail}\") % { file: Puppet[:statefile], detail: detail })\n    begin\n      Puppet::FileSystem.unlink(Puppet[:statefile])\n      retry\n    rescue => detail\n      raise Puppet::Error.new(_(\"Cannot remove %{file}: %{detail}\") % { file: Puppet[:statefile], detail: detail }, detail)\n    end\n  end\n\n  def initialize(transaction_uuid = nil, job_id = nil)\n    @running = false\n    @splayed = false\n    @running_failure = false\n    @cached_catalog_status = 'not_used'\n    @environment = Puppet[:environment]\n    @transaction_uuid = transaction_uuid || SecureRandom.uuid\n    @job_id = job_id\n    @static_catalog = true\n    @checksum_type = Puppet[:supported_checksum_types]\n    @handler = Puppet::Configurer::PluginHandler.new()\n  end\n\n  # Get the remote catalog, yo.  Returns nil if no catalog can be found.\n  def retrieve_catalog(facts, query_options)\n    query_options ||= {}\n    if Puppet[:use_cached_catalog] || @running_failure\n      result = retrieve_catalog_from_cache(query_options)\n    end\n\n    if result\n      if Puppet[:use_cached_catalog]\n        @cached_catalog_status = 'explicitly_requested'\n      elsif @running_failure\n        @cached_catalog_status = 'on_failure'\n      end\n\n      Puppet.info _(\"Using cached catalog from environment '%{environment}'\") % { environment: result.environment }\n    else\n      result = retrieve_new_catalog(facts, query_options)\n\n      unless result\n        unless Puppet[:usecacheonfailure]\n          Puppet.warning _(\"Not using cache on failed catalog\")\n          return nil\n        end\n\n        result = retrieve_catalog_from_cache(query_options)\n\n        if result\n          # don't use use cached catalog if it doesn't match server specified environment\n          if result.environment != @environment\n            Puppet.err _(\"Not using cached catalog because its environment '%{catalog_env}' does not match '%{local_env}'\") % { catalog_env: result.environment, local_env: @environment }\n            return nil\n          end\n\n          @cached_catalog_status = 'on_failure'\n          Puppet.info _(\"Using cached catalog from environment '%{catalog_env}'\") % { catalog_env: result.environment }\n        end\n      end\n    end\n\n    result\n  end\n\n  # Convert a plain resource catalog into our full host catalog.\n  def convert_catalog(result, duration, facts, options = {})\n    catalog = nil\n\n    catalog_conversion_time = thinmark do\n      # Will mutate the result and replace all Deferred values with resolved values\n      if facts\n        Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(facts, result, Puppet.lookup(:current_environment), Puppet[:preprocess_deferred])\n      end\n\n      catalog = result.to_ral\n      catalog.finalize\n      catalog.retrieval_duration = duration\n\n      if Puppet[:write_catalog_summary]\n        catalog.write_class_file\n        catalog.write_resource_file\n      end\n    end\n    options[:report].add_times(:convert_catalog, catalog_conversion_time) if options[:report]\n\n    catalog\n  end\n\n  def warn_number_of_facts(size, max_number)\n    Puppet.warning _(\"The current total number of fact values: %{size} exceeds the fact values limit: %{max_size}\") % { size: size, max_size: max_number }\n  end\n\n  def warn_fact_name_length(name, max_length, fact_name_length)\n    Puppet.warning _(\"Fact %{name} with length: %{length} exceeds the fact name length limit: %{limit}\") % { name: name, length: fact_name_length, limit: max_length }\n  end\n\n  def warn_number_of_top_level_facts(size, max_number)\n    Puppet.warning _(\"The current number of top level facts: %{size} exceeds the top facts limit: %{max_size}\") % { size: size, max_size: max_number }\n  end\n\n  def warn_fact_value_length(name, value, max_length)\n    Puppet.warning _(\"Fact %{name} with value %{value} with the value length: %{length} exceeds the value length limit: %{max_length}\") % { name: name, value: value, length: value.to_s.bytesize, max_length: max_length }\n  end\n\n  def warn_fact_payload_size(payload, max_size)\n    Puppet.warning _(\"Payload with the current size of: %{payload} exceeds the payload size limit: %{max_size}\") % { payload: payload, max_size: max_size }\n  end\n\n  def check_fact_name_length(fact_path, number_of_dots)\n    max_length = Puppet[:fact_name_length_soft_limit]\n    return if max_length.zero?\n\n    name_without_dots = fact_path.join()\n    # rough byte size estimations of fact path as a postgresql btree index\n    size_as_btree_index = 8 + (number_of_dots * 2) + name_without_dots.to_s.bytesize\n    warn_fact_name_length(fact_path.join('.'), max_length, size_as_btree_index) if size_as_btree_index > max_length\n  end\n\n  def check_fact_values_length(name, values)\n    max_length = Puppet[:fact_value_length_soft_limit]\n    return if max_length.zero?\n\n    warn_fact_value_length(name, values, max_length) if values.to_s.bytesize > max_length\n  end\n\n  def check_top_level_number_limit(size)\n    max_size = Puppet[:top_level_facts_soft_limit]\n    return if max_size.zero?\n\n    warn_number_of_top_level_facts(size, max_size) if size > max_size\n  end\n\n  def check_total_number_limit(size)\n    max_size = Puppet[:number_of_facts_soft_limit]\n    return if max_size.zero?\n\n    warn_number_of_facts(size, max_size) if size > max_size\n  end\n\n  def check_payload_size(payload)\n    max_size = Puppet[:payload_soft_limit]\n    return if max_size.zero?\n\n    warn_fact_payload_size(payload, max_size) if payload > max_size\n    Puppet.debug _(\"The size of the payload is %{payload}\") % { payload: payload }\n  end\n\n  def parse_fact_name_and_value_limits(object, path = [])\n    case object\n    when Hash\n      object.each do |key, value|\n        path.push(key)\n        parse_fact_name_and_value_limits(value, path)\n        path.pop\n      end\n    when Array\n      object.each_with_index do |e, idx|\n        path.push(idx)\n        parse_fact_name_and_value_limits(e, path)\n        path.pop\n      end\n    else\n      check_fact_name_length(path, path.size)\n      check_fact_values_length(path.join('.'), object)\n      @number_of_facts += 1\n    end\n  end\n\n  def check_facts_limits(facts)\n    @number_of_facts = 0\n    check_top_level_number_limit(facts.size)\n\n    parse_fact_name_and_value_limits(facts)\n    check_total_number_limit(@number_of_facts)\n    Puppet.debug _(\"The total number of facts registered is %{number_of_facts}\") % { number_of_facts: @number_of_facts }\n  end\n\n  def get_facts(options)\n    if options[:pluginsync]\n      plugin_sync_time = thinmark do\n        remote_environment_for_plugins = Puppet::Node::Environment.remote(@environment)\n        download_plugins(remote_environment_for_plugins)\n\n        Puppet::GettextConfig.reset_text_domain('agent')\n        Puppet::ModuleTranslations.load_from_vardir(Puppet[:vardir])\n      end\n      options[:report].add_times(:plugin_sync, plugin_sync_time) if options[:report]\n    end\n\n    facts_hash = {}\n    facts = nil\n    if Puppet::Resource::Catalog.indirection.terminus_class == :rest\n      # This is a bit complicated.  We need the serialized and escaped facts,\n      # and we need to know which format they're encoded in.  Thus, we\n      # get a hash with both of these pieces of information.\n      #\n      # facts_for_uploading may set Puppet[:node_name_value] as a side effect\n      facter_time = thinmark do\n        facts = find_facts\n        check_facts_limits(facts.to_data_hash['values'])\n        facts_hash = encode_facts(facts) # encode for uploading # was: facts_for_uploading\n        check_payload_size(facts_hash[:facts].bytesize)\n      end\n      options[:report].add_times(:fact_generation, facter_time) if options[:report]\n    end\n    [facts_hash, facts]\n  end\n\n  def prepare_and_retrieve_catalog(cached_catalog, facts, options, query_options)\n    # set report host name now that we have the fact\n    options[:report].host = Puppet[:node_name_value]\n\n    query_options[:transaction_uuid] = @transaction_uuid\n    query_options[:job_id] = @job_id\n    query_options[:static_catalog] = @static_catalog\n\n    # Query params don't enforce ordered evaluation, so munge this list into a\n    # dot-separated string.\n    query_options[:checksum_type] = @checksum_type.join('.')\n\n    # apply passes in ral catalog\n    catalog = cached_catalog || options[:catalog]\n    unless catalog\n      # retrieve_catalog returns resource catalog\n      catalog = retrieve_catalog(facts, query_options)\n      Puppet.err _(\"Could not retrieve catalog; skipping run\") unless catalog\n    end\n    catalog\n  end\n\n  def prepare_and_retrieve_catalog_from_cache(options = {})\n    result = retrieve_catalog_from_cache({ :transaction_uuid => @transaction_uuid, :static_catalog => @static_catalog })\n    Puppet.info _(\"Using cached catalog from environment '%{catalog_env}'\") % { catalog_env: result.environment } if result\n    result\n  end\n\n  # Apply supplied catalog and return associated application report\n  def apply_catalog(catalog, options)\n    report = options[:report]\n    report.configuration_version = catalog.version\n\n    benchmark(:notice, _(\"Applied catalog in %{seconds} seconds\")) do\n      apply_catalog_time = thinmark do\n        catalog.apply(options)\n      end\n      options[:report].add_times(:catalog_application, apply_catalog_time)\n    end\n\n    report\n  end\n\n  # The code that actually runs the catalog.\n  # This just passes any options on to the catalog,\n  # which accepts :tags and :ignoreschedules.\n  def run(options = {})\n    # We create the report pre-populated with default settings for\n    # environment and transaction_uuid very early, this is to ensure\n    # they are sent regardless of any catalog compilation failures or\n    # exceptions.\n    options[:report] ||= Puppet::Transaction::Report.new(nil, @environment, @transaction_uuid, @job_id, options[:start_time] || Time.now)\n    report = options[:report]\n    init_storage\n\n    Puppet::Util::Log.newdestination(report)\n\n    completed = nil\n    begin\n      # Skip failover logic if the server_list setting is empty\n      do_failover = Puppet.settings[:server_list] && !Puppet.settings[:server_list].empty?\n\n      # When we are passed a catalog, that means we're in apply\n      # mode. We shouldn't try to do any failover in that case.\n      if options[:catalog].nil? && do_failover\n        server, port = find_functional_server\n        if server.nil?\n          detail = _(\"Could not select a functional puppet server from server_list: '%{server_list}'\") % { server_list: Puppet.settings.value(:server_list, Puppet[:environment].to_sym, true) }\n          if Puppet[:usecacheonfailure]\n            options[:pluginsync] = false\n            @running_failure = true\n\n            server = Puppet[:server_list].first[0]\n            port = Puppet[:server_list].first[1] || Puppet[:serverport]\n\n            Puppet.err(detail)\n          else\n            raise Puppet::Error, detail\n          end\n        else\n          # TRANSLATORS 'server_list' is the name of a setting and should not be translated\n          Puppet.debug _(\"Selected puppet server from the `server_list` setting: %{server}:%{port}\") % { server: server, port: port }\n          report.server_used = \"#{server}:#{port}\"\n        end\n        Puppet.override(server: server, serverport: port) do\n          completed = run_internal(options)\n        end\n      else\n        completed = run_internal(options)\n      end\n    ensure\n      # we may sleep for awhile, close connections now\n      Puppet.runtime[:http].close\n    end\n\n    completed ? report.exit_status : nil\n  end\n\n  def run_internal(options)\n    report = options[:report]\n    report.initial_environment = Puppet[:environment]\n\n    if options[:start_time]\n      startup_time = Time.now - options[:start_time]\n      report.add_times(:startup_time, startup_time)\n    end\n\n    # If a cached catalog is explicitly requested, attempt to retrieve it. Skip the node request,\n    # don't pluginsync and switch to the catalog's environment if we successfully retrieve it.\n    if Puppet[:use_cached_catalog]\n      Puppet::GettextConfig.reset_text_domain('agent')\n      Puppet::ModuleTranslations.load_from_vardir(Puppet[:vardir])\n\n      cached_catalog = prepare_and_retrieve_catalog_from_cache(options)\n      if cached_catalog\n        @cached_catalog_status = 'explicitly_requested'\n\n        if @environment != cached_catalog.environment && !Puppet[:strict_environment_mode]\n          Puppet.notice _(\"Local environment: '%{local_env}' doesn't match the environment of the cached catalog '%{catalog_env}', switching agent to '%{catalog_env}'.\") % { local_env: @environment, catalog_env: cached_catalog.environment }\n          @environment = cached_catalog.environment\n        end\n\n        report.environment = @environment\n      else\n        # Don't try to retrieve a catalog from the cache again after we've already\n        # failed to do so the first time.\n        Puppet[:use_cached_catalog] = false\n        Puppet[:usecacheonfailure] = false\n        options[:pluginsync] = Puppet::Configurer.should_pluginsync?\n      end\n    end\n\n    begin\n      unless Puppet[:node_name_fact].empty?\n        query_options, facts = get_facts(options)\n      end\n\n      configured_environment = Puppet[:environment] if Puppet.settings.set_by_config?(:environment)\n\n      # We only need to find out the environment to run in if we don't already have a catalog\n      unless cached_catalog || options[:catalog] || Puppet.settings.set_by_cli?(:environment) || Puppet[:strict_environment_mode]\n        Puppet.debug(_(\"Environment not passed via CLI and no catalog was given, attempting to find out the last server-specified environment\"))\n        initial_environment, loaded_last_environment = last_server_specified_environment\n\n        unless Puppet[:use_last_environment] && loaded_last_environment\n          Puppet.debug(_(\"Requesting environment from the server\"))\n          initial_environment = current_server_specified_environment(@environment, configured_environment, options)\n        end\n\n        if initial_environment\n          @environment = initial_environment\n          report.environment = initial_environment\n\n          push_current_environment_and_loaders\n        else\n          Puppet.debug(_(\"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.\"))\n        end\n      end\n\n      Puppet.info _(\"Using environment '%{env}'\") % { env: @environment }\n\n      # This is to maintain compatibility with anyone using this class\n      # aside from agent, apply, device.\n      unless Puppet.lookup(:loaders) { nil }\n        push_current_environment_and_loaders\n      end\n\n      temp_value = options[:pluginsync]\n\n      # only validate server environment if pluginsync is requested\n      options[:pluginsync] = valid_server_environment? if options[:pluginsync]\n\n      query_options, facts = get_facts(options) unless query_options\n      options[:pluginsync] = temp_value\n\n      query_options[:configured_environment] = configured_environment\n\n      catalog = prepare_and_retrieve_catalog(cached_catalog, facts, options, query_options)\n      unless catalog\n        return nil\n      end\n\n      if Puppet[:strict_environment_mode] && catalog.environment != @environment\n        Puppet.err _(\"Not using catalog because its environment '%{catalog_env}' does not match agent specified environment '%{local_env}' and strict_environment_mode is set\") % { catalog_env: catalog.environment, local_env: @environment }\n        return nil\n      end\n\n      # Here we set the local environment based on what we get from the\n      # catalog. Since a change in environment means a change in facts, and\n      # facts may be used to determine which catalog we get, we need to\n      # rerun the process if the environment is changed.\n      tries = 0\n      while catalog.environment and !catalog.environment.empty? and catalog.environment != @environment\n        if tries > 3\n          raise Puppet::Error, _(\"Catalog environment didn't stabilize after %{tries} fetches, aborting run\") % { tries: tries }\n        end\n\n        Puppet.notice _(\"Local environment: '%{local_env}' doesn't match server specified environment '%{catalog_env}', restarting agent run with environment '%{catalog_env}'\") % { local_env: @environment, catalog_env: catalog.environment }\n        @environment = catalog.environment\n        report.environment = @environment\n\n        push_current_environment_and_loaders\n\n        query_options, facts = get_facts(options)\n        query_options[:configured_environment] = configured_environment\n\n        # if we get here, ignore the cached catalog\n        catalog = prepare_and_retrieve_catalog(nil, facts, options, query_options)\n        return nil unless catalog\n\n        tries += 1\n      end\n\n      # now that environment has converged, convert resource catalog into ral catalog\n      # unless we were given a RAL catalog\n      if !cached_catalog && options[:catalog]\n        ral_catalog = options[:catalog]\n      else\n        # Ordering here matters. We have to resolve deferred resources in the\n        # resource catalog, convert the resource catalog to a RAL catalog (which\n        # triggers type/provider validation), and only if that is successful,\n        # should we cache the *original* resource catalog. However, deferred\n        # evaluation mutates the resource catalog, so we need to make a copy of\n        # it here. If PUP-9323 is ever implemented so that we resolve deferred\n        # resources in the RAL catalog as they are needed, then we could eliminate\n        # this step.\n        catalog_to_cache = Puppet.override(:rich_data => Puppet[:rich_data]) do\n          Puppet::Resource::Catalog.from_data_hash(catalog.to_data_hash)\n        end\n\n        # REMIND @duration is the time spent loading the last catalog, and doesn't\n        # account for things like we failed to download and fell back to the cache\n        ral_catalog = convert_catalog(catalog, @duration, facts, options)\n\n        # Validation succeeded, so commit the `catalog_to_cache` for non-noop runs. Don't\n        # commit `catalog` since it contains the result of deferred evaluation. Ideally\n        # we'd just copy the downloaded response body, instead of serializing the\n        # in-memory catalog, but that's hard due to the indirector.\n        indirection = Puppet::Resource::Catalog.indirection\n        if !Puppet[:noop] && indirection.cache?\n          request = indirection.request(:save, nil, catalog_to_cache, environment: Puppet::Node::Environment.remote(catalog_to_cache.environment))\n          Puppet.info(\"Caching catalog for #{request.key}\")\n          indirection.cache.save(request)\n        end\n      end\n\n      execute_prerun_command or return nil\n\n      options[:report].code_id = ral_catalog.code_id\n      options[:report].catalog_uuid = ral_catalog.catalog_uuid\n      options[:report].cached_catalog_status = @cached_catalog_status\n      apply_catalog(ral_catalog, options)\n      true\n    rescue => detail\n      Puppet.log_exception(detail, _(\"Failed to apply catalog: %{detail}\") % { detail: detail })\n      nil\n    ensure\n      execute_postrun_command or return nil # rubocop:disable Lint/EnsureReturn\n    end\n  ensure\n    if Puppet[:resubmit_facts]\n      # TODO: Should mark the report as \"failed\" if an error occurs and\n      #       resubmit_facts returns false. There is currently no API for this.\n      resubmit_facts_time = thinmark { resubmit_facts }\n\n      report.add_times(:resubmit_facts, resubmit_facts_time)\n    end\n\n    report.cached_catalog_status ||= @cached_catalog_status\n    report.add_times(:total, Time.now - report.time)\n    report.finalize_report\n    Puppet::Util::Log.close(report)\n    send_report(report)\n    Puppet.pop_context\n  end\n  private :run_internal\n\n  def valid_server_environment?\n    session = Puppet.lookup(:http_session)\n    begin\n      fs = session.route_to(:fileserver)\n      fs.get_file_metadatas(path: URI(Puppet[:pluginsource]).path, recurse: :false, environment: @environment) # rubocop:disable Lint/BooleanSymbol\n      true\n    rescue Puppet::HTTP::ResponseError => detail\n      if detail.response.code == 404\n        if Puppet[:strict_environment_mode]\n          raise Puppet::Error, _(\"Environment '%{environment}' not found on server, aborting run.\") % { environment: @environment }\n        else\n          Puppet.notice(_(\"Environment '%{environment}' not found on server, skipping initial pluginsync.\") % { environment: @environment })\n        end\n      else\n        Puppet.log_exception(detail, detail.message)\n      end\n      false\n    rescue => detail\n      Puppet.log_exception(detail, detail.message)\n      false\n    end\n  end\n\n  def find_functional_server\n    begin\n      session = Puppet.lookup(:http_session)\n      service = session.route_to(:puppet)\n      return [service.url.host, service.url.port]\n    rescue Puppet::HTTP::ResponseError => e\n      Puppet.debug(_(\"Puppet server %{host}:%{port} is unavailable: %{code} %{reason}\") %\n                   { host: e.response.url.host, port: e.response.url.port, code: e.response.code, reason: e.response.reason })\n    rescue => detail\n      # TRANSLATORS 'server_list' is the name of a setting and should not be translated\n      Puppet.debug _(\"Unable to connect to server from server_list setting: %{detail}\") % { detail: detail }\n    end\n    [nil, nil]\n  end\n  private :find_functional_server\n\n  #\n  # @api private\n  #\n  # Read the last server-specified environment from the lastrunfile. The\n  # environment is considered to be server-specified if the values of\n  # `initial_environment` and `converged_environment` are different.\n  #\n  # @return [String, Boolean] An array containing a string with the environment\n  #   read from the lastrunfile in case the server is authoritative, and a\n  #   boolean marking whether the last environment was correctly loaded.\n  def last_server_specified_environment\n    return @last_server_specified_environment, @loaded_last_environment if @last_server_specified_environment\n\n    if Puppet::FileSystem.exist?(Puppet[:lastrunfile])\n      summary = Puppet::Util::Yaml.safe_load_file(Puppet[:lastrunfile])\n      return [nil, nil] unless summary['application']['run_mode'] == 'agent'\n\n      initial_environment = summary['application']['initial_environment']\n      converged_environment = summary['application']['converged_environment']\n      @last_server_specified_environment = converged_environment if initial_environment != converged_environment\n      Puppet.debug(_(\"Successfully loaded last environment from the lastrunfile\"))\n      @loaded_last_environment = true\n    end\n\n    Puppet.debug(_(\"Found last server-specified environment: %{environment}\") % { environment: @last_server_specified_environment }) if @last_server_specified_environment\n    [@last_server_specified_environment, @loaded_last_environment]\n  rescue => detail\n    Puppet.debug(_(\"Could not find last server-specified environment: %{detail}\") % { detail: detail })\n    [nil, nil]\n  end\n  private :last_server_specified_environment\n\n  def current_server_specified_environment(current_environment, configured_environment, options)\n    return @server_specified_environment if @server_specified_environment\n\n    begin\n      node_retr_time = thinmark do\n        node = Puppet::Node.indirection.find(Puppet[:node_name_value],\n                                             :environment => Puppet::Node::Environment.remote(current_environment),\n                                             :configured_environment => configured_environment,\n                                             :ignore_cache => true,\n                                             :transaction_uuid => @transaction_uuid,\n                                             :fail_on_404 => true)\n\n        @server_specified_environment = node.environment_name.to_s\n\n        if @server_specified_environment != @environment\n          Puppet.notice _(\"Local environment: '%{local_env}' doesn't match server specified node environment '%{node_env}', switching agent to '%{node_env}'.\") % { local_env: @environment, node_env: @server_specified_environment }\n        end\n      end\n\n      options[:report].add_times(:node_retrieval, node_retr_time)\n\n      @server_specified_environment\n    rescue => detail\n      Puppet.warning(_(\"Unable to fetch my node definition, but the agent run will continue:\"))\n      Puppet.warning(detail)\n      nil\n    end\n  end\n  private :current_server_specified_environment\n\n  def send_report(report)\n    puts report.summary if Puppet[:summarize]\n    save_last_run_summary(report)\n    if Puppet[:report]\n      remote = Puppet::Node::Environment.remote(@environment)\n      begin\n        Puppet::Transaction::Report.indirection.save(report, nil, ignore_cache: true, environment: remote)\n      ensure\n        Puppet::Transaction::Report.indirection.save(report, nil, ignore_terminus: true, environment: remote)\n      end\n    end\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Could not send report: %{detail}\") % { detail: detail })\n  end\n\n  def save_last_run_summary(report)\n    mode = Puppet.settings.setting(:lastrunfile).mode\n    Puppet::Util.replace_file(Puppet[:lastrunfile], mode) do |fh|\n      fh.print YAML.dump(report.raw_summary)\n    end\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Could not save last run local report: %{detail}\") % { detail: detail })\n  end\n\n  # Submit updated facts to the Puppet Server\n  #\n  # This method will clear all current fact values, load a fresh set of\n  # fact data, and then submit it to the Puppet Server.\n  #\n  # @return [true] If fact submission succeeds.\n  # @return [false] If an exception is raised during fact generation or\n  #   submission.\n  def resubmit_facts\n    Puppet.runtime[:facter].clear\n    facts = find_facts\n\n    client = Puppet.runtime[:http]\n    session = client.create_session\n    puppet = session.route_to(:puppet)\n\n    Puppet.info(_(\"Uploading facts for %{node} to %{server}\") % {\n      node: facts.name,\n      server: puppet.url.hostname\n    })\n\n    puppet.put_facts(facts.name, facts: facts, environment: Puppet.lookup(:current_environment).name.to_s)\n\n    true\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Failed to submit facts: %{detail}\") %\n                                 { detail: detail })\n\n    false\n  end\n\n  private\n\n  def execute_from_setting(setting)\n    return true if (command = Puppet[setting]) == \"\"\n\n    begin\n      Puppet::Util::Execution.execute([command])\n      true\n    rescue => detail\n      Puppet.log_exception(detail, _(\"Could not run command from %{setting}: %{detail}\") % { setting: setting, detail: detail })\n      false\n    end\n  end\n\n  def push_current_environment_and_loaders\n    new_env = Puppet::Node::Environment.remote(@environment)\n    Puppet.push_context(\n      {\n        :current_environment => new_env,\n        :loaders => Puppet::Pops::Loaders.new(new_env, true)\n      },\n      \"Local node environment #{@environment} for configurer transaction\"\n    )\n  end\n\n  def retrieve_catalog_from_cache(query_options)\n    result = nil\n    @duration = thinmark do\n      result = Puppet::Resource::Catalog.indirection.find(\n        Puppet[:node_name_value],\n        query_options.merge(\n          :ignore_terminus => true,\n          :environment => Puppet::Node::Environment.remote(@environment)\n        )\n      )\n    end\n    result\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Could not retrieve catalog from cache: %{detail}\") % { detail: detail })\n    nil\n  end\n\n  def retrieve_new_catalog(facts, query_options)\n    result = nil\n    @duration = thinmark do\n      result = Puppet::Resource::Catalog.indirection.find(\n        Puppet[:node_name_value],\n        query_options.merge(\n          :ignore_cache => true,\n          # don't update cache until after environment converges\n          :ignore_cache_save => true,\n          :environment => Puppet::Node::Environment.remote(@environment),\n          :check_environment => true,\n          :fail_on_404 => true,\n          :facts_for_catalog => facts\n        )\n      )\n    end\n    result\n  rescue StandardError => detail\n    Puppet.log_exception(detail, _(\"Could not retrieve catalog from remote server: %{detail}\") % { detail: detail })\n    nil\n  end\n\n  def download_plugins(remote_environment_for_plugins)\n    @handler.download_plugins(remote_environment_for_plugins)\n  rescue Puppet::Error => detail\n    if !Puppet[:ignore_plugin_errors] && Puppet[:usecacheonfailure]\n      @running_failure = true\n    else\n      raise detail\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/any.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Confine::Any < Puppet::Confine\n  def self.summarize(confines)\n    confines.inject(0) { |count, confine| count + confine.summary }\n  end\n\n  def pass?(value)\n    !!value\n  end\n\n  def message(value)\n    \"0 confines (of #{value.length}) were true\"\n  end\n\n  def summary\n    result.find_all { |v| v == true }.length\n  end\n\n  def valid?\n    if @values.any? { |value| pass?(value) }\n      true\n    else\n      Puppet.debug { \"#{label}: #{message(@values)}\" }\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/boolean.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confine'\n\n# Common module for the Boolean confines. It currently\n# contains just enough code to implement PUP-9336.\nclass Puppet::Confine\n  module Boolean\n    # Returns the passing value for the Boolean confine.\n    def passing_value\n      raise NotImplementedError, \"The Boolean confine %{confine} must provide the passing value.\" % { confine: self.class.name }\n    end\n\n    # The Boolean confines 'true' and 'false' let the user specify\n    # two types of values:\n    #     * A lambda for lazy evaluation. This would be something like\n    #         confine :true => lambda { true }\n    #\n    #     * A single Boolean value, or an array of Boolean values. This would\n    #     be something like\n    #         confine :true => true OR confine :true => [true, false, false, true]\n    #\n    # This override distinguishes between the two cases.\n    def values\n      # Note that Puppet::Confine's constructor ensures that @values\n      # will always be an array, even if a lambda's passed in. This is\n      # why we have the length == 1 check.\n      unless @values.length == 1 && @values.first.respond_to?(:call)\n        return @values\n      end\n\n      # We have a lambda. Here, we want to enforce \"cache positive\"\n      # behavior, which is to cache the result _if_ it evaluates to\n      # the passing value (i.e. the class name).\n\n      return @cached_value unless @cached_value.nil?\n\n      # Double negate to coerce the value into a Boolean\n      calculated_value = !!@values.first.call\n      if calculated_value == passing_value\n        @cached_value = [calculated_value]\n      end\n\n      [calculated_value]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/exists.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confine'\n\nclass Puppet::Confine::Exists < Puppet::Confine\n  def self.summarize(confines)\n    confines.inject([]) { |total, confine| total + confine.summary }\n  end\n\n  def pass?(value)\n    value && (for_binary? ? which(value) : Puppet::FileSystem.exist?(value))\n  end\n\n  def message(value)\n    \"file #{value} does not exist\"\n  end\n\n  def summary\n    result.zip(values).each_with_object([]) { |args, array| val, f = args; array << f unless val; }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/false.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confine/boolean'\n\nclass Puppet::Confine::False < Puppet::Confine\n  include Puppet::Confine::Boolean\n\n  def passing_value\n    false\n  end\n\n  def self.summarize(confines)\n    confines.inject(0) { |count, confine| count + confine.summary }\n  end\n\n  def pass?(value)\n    !value\n  end\n\n  def message(value)\n    \"true value when expecting false\"\n  end\n\n  def summary\n    result.find_all { |v| v == false }.length\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/feature.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confine'\n\nclass Puppet::Confine::Feature < Puppet::Confine\n  def self.summarize(confines)\n    confines.collect(&:values).flatten.uniq.find_all { |value| !confines[0].pass?(value) }\n  end\n\n  # Is the named feature available?\n  def pass?(value)\n    Puppet.features.send(value.to_s + \"?\")\n  end\n\n  def message(value)\n    \"feature #{value} is missing\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/true.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confine/boolean'\n\nclass Puppet::Confine::True < Puppet::Confine\n  include Puppet::Confine::Boolean\n\n  def passing_value\n    true\n  end\n\n  def self.summarize(confines)\n    confines.inject(0) { |count, confine| count + confine.summary }\n  end\n\n  def pass?(value)\n    # Double negate, so we only get true or false.\n    !!value\n  end\n\n  def message(value)\n    \"false value when expecting true\"\n  end\n\n  def summary\n    result.find_all { |v| v == true }.length\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine/variable.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confine'\n\n# Require a specific value for a variable, either a Puppet setting\n# or a Facter value.  This class is a bit weird because the name\n# is set explicitly by the ConfineCollection class -- from this class,\n# it's not obvious how the name would ever get set.\nclass Puppet::Confine::Variable < Puppet::Confine\n  # Provide a hash summary of failing confines -- the key of the hash\n  # is the name of the confine, and the value is the missing yet required values.\n  # Only returns failed values, not all required values.\n  def self.summarize(confines)\n    result = Hash.new { |hash, key| hash[key] = [] }\n    confines.each_with_object(result) { |confine, total| total[confine.name] += confine.values unless confine.valid?; }\n  end\n\n  # This is set by ConfineCollection.\n  attr_accessor :name\n\n  # Retrieve the value from facter\n  def facter_value\n    @facter_value ||= Puppet.runtime[:facter].value(name).to_s.downcase\n  end\n\n  def initialize(values)\n    super\n    @values = @values.collect { |v| v.to_s.downcase }\n  end\n\n  def message(value)\n    \"facter value '#{test_value}' for '#{name}' not in required list '#{values.join(',')}'\"\n  end\n\n  # Compare the passed-in value to the retrieved value.\n  def pass?(value)\n    test_value.downcase.to_s == value.to_s.downcase\n  end\n\n  def reset\n    # Reset the cache.  We want to cache it during a given\n    # run, but not across runs.\n    @facter_value = nil\n  end\n\n  def valid?\n    @values.include?(test_value.to_s.downcase)\n  ensure\n    reset\n  end\n\n  private\n\n  def setting?\n    Puppet.settings.valid?(name)\n  end\n\n  def test_value\n    setting? ? Puppet.settings[name] : facter_value\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine.rb",
    "content": "# frozen_string_literal: true\n\n# The class that handles testing whether our providers\n# actually work or not.\nrequire_relative '../puppet/util'\n\nclass Puppet::Confine\n  include Puppet::Util\n\n  @tests = {}\n\n  class << self\n    attr_accessor :name\n  end\n\n  def self.inherited(klass)\n    name = klass.to_s.split(\"::\").pop.downcase.to_sym\n    raise \"Test #{name} is already defined\" if @tests.include?(name)\n\n    klass.name = name\n\n    @tests[name] = klass\n  end\n\n  def self.test(name)\n    unless @tests.include?(name)\n      begin\n        require \"puppet/confine/#{name}\"\n      rescue LoadError => detail\n        unless detail.to_s =~ /No such file|cannot load such file/i\n          Puppet.warning(\"Could not load confine test '#{name}': #{detail}\")\n        end\n        # Could not find file\n        unless Puppet[:always_retry_plugins]\n          @tests[name] = nil\n        end\n      end\n    end\n    @tests[name]\n  end\n\n  attr_reader :values\n\n  # Mark that this confine is used for testing binary existence.\n  attr_accessor :for_binary\n\n  def for_binary?\n    for_binary\n  end\n\n  # Used for logging.\n  attr_accessor :label\n\n  def initialize(values)\n    values = [values] unless values.is_a?(Array)\n    @values = values\n  end\n\n  # Provide a hook for the message when there's a failure.\n  def message(value)\n    \"\"\n  end\n\n  # Collect the results of all of them.\n  def result\n    values.collect { |value| pass?(value) }\n  end\n\n  # Test whether our confine matches.\n  def valid?\n    values.each do |value|\n      unless pass?(value)\n        Puppet.debug { label + \": \" + message(value) }\n        return false\n      end\n    end\n\n    true\n  ensure\n    reset\n  end\n\n  # Provide a hook for subclasses.\n  def reset\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confine_collection.rb",
    "content": "# frozen_string_literal: true\n\n# Manage a collection of confines, returning a boolean or\n# helpful information.\nrequire_relative '../puppet/confine'\n\nclass Puppet::ConfineCollection\n  def confine(hash)\n    if hash.include?(:for_binary)\n      for_binary = true\n      hash.delete(:for_binary)\n    else\n      for_binary = false\n    end\n    hash.each do |test, values|\n      klass = Puppet::Confine.test(test)\n      if klass\n        @confines << klass.new(values)\n        @confines[-1].for_binary = true if for_binary\n      else\n        confine = Puppet::Confine.test(:variable).new(values)\n        confine.name = test\n        @confines << confine\n      end\n      @confines[-1].label = label\n    end\n  end\n\n  attr_reader :label\n\n  def initialize(label)\n    @label = label\n    @confines = []\n  end\n\n  # Return a hash of the whole confine set, used for the Provider\n  # reference.\n  def summary\n    confines = Hash.new { |hash, key| hash[key] = [] }\n    @confines.each { |confine| confines[confine.class] << confine }\n    result = {}\n    confines.each do |klass, list|\n      value = klass.summarize(list)\n      next if (value.respond_to?(:length) and value.length == 0) or (value == 0)\n\n      result[klass.name] = value\n    end\n    result\n  end\n\n  def valid?\n    !@confines.detect { |c| !c.valid? }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/confiner.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/confine_collection'\n\n# The Confiner module contains methods for managing a Provider's confinement (suitability under given\n# conditions). The intent is to include this module in an object where confinement management is wanted.\n# It lazily adds an instance variable `@confine_collection` to the object where it is included.\n#\nmodule Puppet::Confiner\n  # Confines a provider to be suitable only under the given conditions.\n  # The hash describes a confine using mapping from symbols to values or predicate code.\n  #\n  # * _fact_name_ => value of fact (or array of facts)\n  # * `:exists` => the path to an existing file\n  # * `:true` => a predicate code block returning true\n  # * `:false` => a predicate code block returning false\n  # * `:feature` => name of system feature that must be present\n  # * `:any` => an array of expressions that will be ORed together\n  #\n  # @example\n  #   confine 'os.name' => [:redhat, :fedora]\n  #   confine :true { ... }\n  #\n  # @param hash [Hash<{Symbol => Object}>] hash of confines\n  # @return [void]\n  # @api public\n  #\n  def confine(hash)\n    confine_collection.confine(hash)\n  end\n\n  # @return [Puppet::ConfineCollection] the collection of confines\n  # @api private\n  #\n  def confine_collection\n    @confine_collection ||= Puppet::ConfineCollection.new(to_s)\n  end\n\n  # Checks whether this implementation is suitable for the current platform (or returns a summary\n  # of all confines if short == false).\n  # @return [Boolean. Hash] Returns whether the confines are all valid (if short == true), or a hash of all confines\n  #   if short == false.\n  # @api public\n  #\n  def suitable?(short = true)\n    (short ? confine_collection.valid? : confine_collection.summary)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/context/trusted_information.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/trusted_external'\n\n# @api private\nclass Puppet::Context::TrustedInformation\n  # one of 'remote', 'local', or false, where 'remote' is authenticated via cert,\n  # 'local' is trusted by virtue of running on the same machine (not a remote\n  # request), and false is an unauthenticated remote request.\n  #\n  # @return [String, Boolean]\n  attr_reader :authenticated\n\n  # The validated certificate name used for the request\n  #\n  # @return [String]\n  attr_reader :certname\n\n  # Extra information that comes from the trusted certificate's extensions.\n  #\n  # @return [Hash{Object => Object}]\n  attr_reader :extensions\n\n  # The domain name derived from the validated certificate name\n  #\n  # @return [String]\n  attr_reader :domain\n\n  # The hostname derived from the validated certificate name\n  #\n  # @return [String]\n  attr_reader :hostname\n\n  def initialize(authenticated, certname, extensions, external = {})\n    @authenticated = authenticated.freeze\n    @certname = certname.freeze\n    @extensions = extensions.freeze\n    if @certname\n      hostname, domain = @certname.split('.', 2)\n    else\n      hostname = nil\n      domain = nil\n    end\n    @hostname = hostname.freeze\n    @domain = domain.freeze\n    @external = external.is_a?(Proc) ? external : external.freeze\n  end\n\n  def self.remote(authenticated, node_name, certificate)\n    external = proc { retrieve_trusted_external(node_name) }\n\n    if authenticated\n      extensions = {}\n      if certificate.nil?\n        Puppet.info(_('TrustedInformation expected a certificate, but none was given.'))\n      else\n        extensions = certificate.custom_extensions.to_h do |ext|\n          [ext['oid'].freeze, ext['value'].freeze]\n        end\n      end\n      new('remote', node_name, extensions, external)\n    else\n      new(false, nil, {}, external)\n    end\n  end\n\n  def self.local(node)\n    # Always trust local data by picking up the available parameters.\n    client_cert = node ? node.parameters['clientcert'] : nil\n    external = proc { retrieve_trusted_external(client_cert) }\n\n    new('local', client_cert, {}, external)\n  end\n\n  # Additional external facts loaded through `trusted_external_command`.\n  #\n  # @return [Hash]\n  def external\n    if @external.is_a?(Proc)\n      @external = @external.call.freeze\n    end\n    @external\n  end\n\n  def self.retrieve_trusted_external(certname)\n    deep_freeze(Puppet::TrustedExternal.retrieve(certname) || {})\n  end\n  private_class_method :retrieve_trusted_external\n\n  # Deeply freezes the given object. The object and its content must be of the types:\n  # Array, Hash, Numeric, Boolean, Regexp, NilClass, or String. All other types raises an Error.\n  # (i.e. if they are assignable to Puppet::Pops::Types::Data type).\n  def self.deep_freeze(object)\n    case object\n    when Array\n      object.each { |v| deep_freeze(v) }\n      object.freeze\n    when Hash\n      object.each { |k, v| deep_freeze(k); deep_freeze(v) }\n      object.freeze\n    when NilClass, Numeric, TrueClass, FalseClass\n      # do nothing\n    when String\n      object.freeze\n    else\n      raise Puppet::Error, _(\"Unsupported data type: '%{klass}'\") % { klass: object.class }\n    end\n    object\n  end\n  private_class_method :deep_freeze\n\n  def to_h\n    {\n      'authenticated' => authenticated,\n      'certname' => certname,\n      'extensions' => extensions,\n      'hostname' => hostname,\n      'domain' => domain,\n      'external' => external,\n    }.freeze\n  end\nend\n"
  },
  {
    "path": "lib/puppet/context.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/thread_local'\n\n# Puppet::Context is a system for tracking services and contextual information\n# that puppet needs to be able to run. Values are \"bound\" in a context when it is created\n# and cannot be changed; however a child context can be created, using\n# {#override}, that provides a different value.\n#\n# When binding a {Proc}, the proc is called when the value is looked up, and the result\n# is memoized for subsequent lookups. This provides a lazy mechanism that can be used to\n# delay expensive production of values until they are needed.\n#\n# @api private\nclass Puppet::Context\n  require_relative 'context/trusted_information'\n\n  class UndefinedBindingError < Puppet::Error; end\n  class StackUnderflow < Puppet::Error; end\n\n  class UnknownRollbackMarkError < Puppet::Error; end\n  class DuplicateRollbackMarkError < Puppet::Error; end\n\n  # @api private\n  def initialize(initial_bindings)\n    @stack = Puppet::ThreadLocal.new(EmptyStack.new.push(initial_bindings))\n\n    # By initializing @rollbacks to nil and creating a hash lazily when #mark or\n    # #rollback are called we ensure that the hashes are never shared between\n    # threads and it's safe to mutate them\n    @rollbacks = Puppet::ThreadLocal.new(nil)\n  end\n\n  # @api private\n  def push(overrides, description = '')\n    @stack.value = @stack.value.push(overrides, description)\n  end\n\n  # Push a context and make this global across threads\n  # Do not use in a context where multiple threads may already exist\n  #\n  # @api private\n  def unsafe_push_global(overrides, description = '')\n    @stack = Puppet::ThreadLocal.new(\n      @stack.value.push(overrides, description)\n    )\n  end\n\n  # @api private\n  def pop\n    @stack.value = @stack.value.pop\n  end\n\n  # @api private\n  def lookup(name, &block)\n    @stack.value.lookup(name, &block)\n  end\n\n  # @api private\n  def override(bindings, description = '', &block)\n    saved_point = @stack.value\n    push(bindings, description)\n\n    yield\n  ensure\n    @stack.value = saved_point\n  end\n\n  # Mark a place on the context stack to later return to with {rollback}.\n  #\n  # @param name [Object] The identifier for the mark\n  #\n  # @api private\n  def mark(name)\n    @rollbacks.value ||= {}\n    if @rollbacks.value[name].nil?\n      @rollbacks.value[name] = @stack.value\n    else\n      raise DuplicateRollbackMarkError, _(\"Mark for '%{name}' already exists\") % { name: name }\n    end\n  end\n\n  # Roll back to a mark set by {mark}.\n  #\n  # Rollbacks can only reach a mark accessible via {pop}. If the mark is not on\n  # the current context stack the behavior of rollback is undefined.\n  #\n  # @param name [Object] The identifier for the mark\n  #\n  # @api private\n  def rollback(name)\n    @rollbacks.value ||= {}\n    if @rollbacks.value[name].nil?\n      raise UnknownRollbackMarkError, _(\"Unknown mark '%{name}'\") % { name: name }\n    end\n\n    @stack.value = @rollbacks.value.delete(name)\n  end\n\n  # Base case for Puppet::Context::Stack.\n  #\n  # @api private\n  class EmptyStack\n    # Lookup a binding. Since there are none in EmptyStack, this always raises\n    # an exception unless a block is passed, in which case the block is called\n    # and its return value is used.\n    #\n    # @api private\n    def lookup(name, &block)\n      if block\n        block.call\n      else\n        raise UndefinedBindingError, _(\"Unable to lookup '%{name}'\") % { name: name }\n      end\n    end\n\n    # Base case of #pop always raises an error since this is the bottom\n    #\n    # @api private\n    def pop\n      raise(StackUnderflow,\n            _('Attempted to pop, but already at root of the context stack.'))\n    end\n\n    # Push bindings onto the stack by creating a new Stack object with `self` as\n    # the parent\n    #\n    # @api private\n    def push(overrides, description = '')\n      Puppet::Context::Stack.new(self, overrides, description)\n    end\n\n    # Return the bindings table, which is always empty here\n    #\n    # @api private\n    def bindings\n      {}\n    end\n  end\n\n  # Internal implementation of the bindings stack used by Puppet::Context. An\n  # instance of Puppet::Context::Stack represents one level of bindings. It\n  # caches a merged copy of all the bindings in the stack up to this point.\n  # Each element of the stack is immutable, allowing the base to be shared\n  # between threads.\n  #\n  # @api private\n  class Stack\n    attr_reader :bindings\n\n    def initialize(parent, bindings, description = '')\n      @parent = parent\n      @bindings = parent.bindings.merge(bindings || {})\n      @description = description\n    end\n\n    # Lookup a binding in the current stack. Return the value if it is present.\n    # If the value is a stored Proc, evaluate, cache, and return the result. If\n    # no binding is found and a block is passed evaluate it and return the\n    # result. Otherwise an exception is raised.\n    #\n    # @api private\n    def lookup(name, &block)\n      if @bindings.include?(name)\n        value = @bindings[name]\n        value.is_a?(Proc) ? (@bindings[name] = value.call) : value\n      elsif block\n        block.call\n      else\n        raise UndefinedBindingError,\n              _(\"Unable to lookup '%{name}'\") % { name: name }\n      end\n    end\n\n    # Pop one level off the stack by returning the parent object.\n    #\n    # @api private\n    def pop\n      @parent\n    end\n\n    # Push bindings onto the stack by creating a new Stack object with `self` as\n    # the parent\n    #\n    # @api private\n    def push(overrides, description = '')\n      Puppet::Context::Stack.new(self, overrides, description)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/daemon.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/application'\nrequire_relative '../puppet/scheduler'\n\n# Run periodic actions in a daemonized process.\n#\n# A Daemon has 2 parts:\n#   * config reparse\n#   * an agent that responds to #run\n#\n# The config reparse will occur periodically based on Settings. The agent\n# is run periodically and a time interval based on Settings. The config\n# reparse will update this time interval when needed.\n#\n# The Daemon is also responsible for signal handling, starting, stopping,\n# running the agent on demand, and reloading the entire process. It ensures\n# that only one Daemon is running by using a lockfile.\n#\n# @api private\nclass Puppet::Daemon\n  SIGNAL_CHECK_INTERVAL = 5\n\n  attr_accessor :argv\n  attr_reader :signals, :agent\n\n  def initialize(agent, pidfile, scheduler = Puppet::Scheduler::Scheduler.new())\n    raise Puppet::DevError, _(\"Daemons must have an agent\") unless agent\n\n    @scheduler = scheduler\n    @pidfile = pidfile\n    @agent = agent\n    @signals = []\n  end\n\n  def daemonname\n    Puppet.run_mode.name\n  end\n\n  # Put the daemon into the background.\n  def daemonize\n    pid = fork\n    if pid\n      Process.detach(pid)\n      exit(0)\n    end\n\n    create_pidfile\n\n    # Get rid of console logging\n    Puppet::Util::Log.close(:console)\n\n    Process.setsid\n    Dir.chdir(\"/\")\n\n    close_streams\n  end\n\n  # Close stdin/stdout/stderr so that we can finish our transition into 'daemon' mode.\n  # @return nil\n  def self.close_streams\n    Puppet.debug(\"Closing streams for daemon mode\")\n    begin\n      $stdin.reopen \"/dev/null\"\n      $stdout.reopen \"/dev/null\", \"a\"\n      $stderr.reopen $stdout\n      Puppet::Util::Log.reopen\n      Puppet.debug(\"Finished closing streams for daemon mode\")\n    rescue => detail\n      Puppet.err \"Could not start #{Puppet.run_mode.name}: #{detail}\"\n      Puppet::Util.replace_file(\"/tmp/daemonout\", 0o644) do |f|\n        f.puts \"Could not start #{Puppet.run_mode.name}: #{detail}\"\n      end\n      exit(12)\n    end\n  end\n\n  # Convenience signature for calling Puppet::Daemon.close_streams\n  def close_streams\n    Puppet::Daemon.close_streams\n  end\n\n  def reexec\n    raise Puppet::DevError, _(\"Cannot reexec unless ARGV arguments are set\") unless argv\n\n    command = $PROGRAM_NAME + \" \" + argv.join(\" \")\n    Puppet.notice \"Restarting with '#{command}'\"\n    stop(:exit => false)\n    exec(command)\n  end\n\n  def reload\n    agent.run({ :splay => false })\n  rescue Puppet::LockError\n    Puppet.notice \"Not triggering already-running agent\"\n  end\n\n  def restart\n    Puppet::Application.restart!\n    reexec\n  end\n\n  def reopen_logs\n    Puppet::Util::Log.reopen\n  end\n\n  # Trap a couple of the main signals.  This should probably be handled\n  # in a way that anyone else can register callbacks for traps, but, eh.\n  def set_signal_traps\n    [:INT, :TERM].each do |signal|\n      Signal.trap(signal) do\n        Puppet.notice \"Caught #{signal}; exiting\"\n        stop\n      end\n    end\n\n    # extended signals not supported under windows\n    unless Puppet::Util::Platform.windows?\n      signals = { :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs }\n      signals.each do |signal, method|\n        Signal.trap(signal) do\n          Puppet.notice \"Caught #{signal}; storing #{method}\"\n          @signals << method\n        end\n      end\n    end\n  end\n\n  # Stop everything\n  def stop(args = { :exit => true })\n    Puppet::Application.stop!\n\n    remove_pidfile\n\n    Puppet::Util::Log.close_all\n\n    exit if args[:exit]\n  end\n\n  def start\n    create_pidfile\n    run_event_loop\n  end\n\n  private\n\n  # Create a pidfile for our daemon, so we can be stopped and others\n  # don't try to start.\n  def create_pidfile\n    raise \"Could not create PID file: #{@pidfile.file_path}\" unless @pidfile.lock\n  end\n\n  # Remove the pid file for our daemon.\n  def remove_pidfile\n    @pidfile.unlock\n  end\n\n  # Loop forever running events - or, at least, until we exit.\n  def run_event_loop\n    splaylimit = Puppet[:splay] ? Puppet[:splaylimit] : 0\n\n    agent_run = Puppet::Scheduler.create_job(Puppet[:runinterval], true, splaylimit) do |job|\n      if job.splay != 0\n        Puppet.info \"Running agent every #{job.run_interval} seconds with splay #{job.splay} of #{job.splay_limit} seconds\"\n      else\n        Puppet.info \"Running agent every #{job.run_interval} seconds\"\n      end\n\n      # Splay for the daemon is handled in the scheduler\n      agent.run(:splay => false)\n    end\n\n    reparse_run = Puppet::Scheduler.create_job(Puppet[:filetimeout]) do\n      Puppet.settings.reparse_config_files\n      agent_run.run_interval = Puppet[:runinterval]\n      # Puppet[:splaylimit] defaults to Puppet[:runinterval] so if runinterval\n      # changes, but splaylimit doesn't, we'll still recalculate splay\n      agent_run.splay_limit = Puppet[:splay] ? Puppet[:splaylimit] : 0\n      if Puppet[:filetimeout] == 0\n        reparse_run.disable\n      else\n        reparse_run.run_interval = Puppet[:filetimeout]\n      end\n    end\n\n    signal_loop = Puppet::Scheduler.create_job(SIGNAL_CHECK_INTERVAL) do\n      while method = @signals.shift # rubocop:disable Lint/AssignmentInCondition\n        Puppet.notice \"Processing #{method}\"\n        send(method)\n      end\n    end\n\n    reparse_run.disable if Puppet[:filetimeout] == 0\n\n    # these are added in a different order than they are defined\n    @scheduler.run_loop([reparse_run, agent_run, signal_loop])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/data_binding.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/indirector'\n\n# A class for managing data lookups\nclass Puppet::DataBinding\n  # Set up indirection, so that data can be looked for in the compiler\n  extend Puppet::Indirector\n\n  indirects(:data_binding, :terminus_setting => :data_binding_terminus,\n                           :doc => \"Where to find external data bindings.\")\n\n  class LookupError < Puppet::PreformattedError; end\n\n  class RecursiveLookupError < Puppet::DataBinding::LookupError; end\nend\n"
  },
  {
    "path": "lib/puppet/datatypes/error.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::DataTypes.create_type('Error') do\n  interface <<-PUPPET\n    type_parameters => {\n      kind => Optional[Variant[String,Regexp,Type[Enum],Type[Pattern],Type[NotUndef],Type[Undef]]],\n      issue_code => Optional[Variant[String,Regexp,Type[Enum],Type[Pattern],Type[NotUndef],Type[Undef]]]\n    },\n    attributes => {\n      msg => String[1],\n      kind => { type => Optional[String[1]], value => undef },\n      details => { type => Optional[Hash[String[1],Data]], value => undef },\n      issue_code => { type => Optional[String[1]], value => undef },\n    },\n    functions => {\n      message => Callable[[], String[1]]\n    }\n  PUPPET\n\n  require_relative '../../puppet/datatypes/impl/error'\n\n  implementation_class Puppet::DataTypes::Error\nend\n"
  },
  {
    "path": "lib/puppet/datatypes/impl/error.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::DataTypes::Error\n  attr_reader :msg, :kind, :issue_code, :details\n  alias message msg\n\n  def self.from_asserted_hash(hash)\n    new(hash['msg'], hash['kind'], hash['details'], hash['issue_code'])\n  end\n\n  def _pcore_init_hash\n    result = { 'msg' => @msg }\n    result['kind'] = @kind unless @kind.nil?\n    result['details'] = @details unless @details.nil?\n    result['issue_code'] = @issue_code unless @issue_code.nil?\n    result\n  end\n\n  def initialize(msg, kind = nil, details = nil, issue_code = nil)\n    @msg = msg\n    @kind = kind\n    @details = details\n    @issue_code = issue_code\n  end\n\n  def eql?(o)\n    self.class.equal?(o.class) &&\n      @msg == o.msg &&\n      @kind == o.kind &&\n      @issue_code == o.issue_code &&\n      @details == o.details\n  end\n  alias == eql?\n\n  def hash\n    @msg.hash ^ @kind.hash ^ @issue_code.hash\n  end\n\n  def to_s\n    Puppet::Pops::Types::StringConverter.singleton.convert(self)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/datatypes.rb",
    "content": "# frozen_string_literal: true\n\n# Data types in the Puppet Language can have implementations written in Ruby\n# and distributed in puppet modules. A data type can be declared together with\n# its implementation by creating a file in 'lib/puppet/functions/<modulename>'.\n# The name of the file must be the downcased name of the data type followed by\n# the extension '.rb'.\n#\n# A data type is created by calling {Puppet::DataTypes.create_type(<type name>)}\n# and passing it a block that defines the data type interface and implementation.\n#\n# Data types are namespaced inside the modules that contains them. The name of the\n# data type is prefixed with the name of the module. As with all type names, each\n# segment of the name must start with an uppercase letter.\n#\n# @example A simple data type\n#   Puppet::DataTypes.create_type('Auth::User') do\n#     interface <<-PUPPET\n#       attributes => {\n#         name => String,\n#         email => String\n#       }\n#     PUPPET\n#   end\n#\n# The above example does not declare an implementation which makes it equivalent\n# to adding the following contents in a file named 'user.pp' under the 'types' directory\n# of the module root.\n#\n#   type Auth::User = Object[\n#     attributes => {\n#       name => String,\n#       email => String\n#     }]\n#\n# Both declarations are valid and will be found by the module loader.\n#\n# Structure of a data type\n# ---\n#\n# A Data Type consists of an interface and an implementation. Unless a registered implementation\n# is found, the type system will automatically generate one. An  automatically generated\n# implementation is all that is needed when the interface fully  defines the behaviour (for\n# example in the common case when the data type has no other behaviour than having attributes).\n#\n# When the automatically generated implementation is not sufficient, one must be implemented and\n# registered. The implementation can either be done next to the interface definition by passing\n# a block to `implementation`, or map to an existing implementation class by passing the class\n# as an argument to `implementation_class`. An implementation class does not have to be special\n# in other respects than that it must implemented the type's interface. This makes it possible\n# to use existing Ruby data types as data types in the puppet language.\n#\n# Note that when using `implementation_class` there can only be one such implementation across\n# all environments managed by one puppet server and you must handle and install these\n# implementations as if they are part of the puppet platform. In contrast; the type\n# implementations that are done inside of the type's definition are safe to use in different\n# versions in different environments (given that they do not need additional external logic to\n# be loaded).\n#\n# When using an `implementation_class` it is sometimes desirable to load this class from the\n# 'lib' directory of the module. The method `load_file` is provided to facilitate such a load.\n# The `load_file` will use the `Puppet::Util::Autoload` to search for the given file in the 'lib'\n# directory of the current environment and the 'lib' directory in each included module.\n#\n# @example Adding implementation on top of the generated type using `implementation`\n#   Puppet::DataTypes.create_type('Auth::User') do\n#     interface <<-PUPPET\n#       attributes => {\n#         name => String,\n#         email => String,\n#         year_of_birth => Integer,\n#         age => { type => Integer, kind => derived }\n#       }\n#       PUPPET\n#\n#     implementation do\n#       def age\n#         DateTime.now.year - @year_of_birth\n#       end\n#     end\n#   end\n#\n# @example Appointing an already existing implementation class\n#\n# Assumes the following class is declared under 'lib/puppetx/auth' in the module:\n#\n#   class PuppetX::Auth::User\n#     attr_reader :name, :year_of_birth\n#     def initialize(name, year_of_birth)\n#       @name = name\n#       @year_of_birth = year_of_birth\n#     end\n#\n#     def age\n#       DateTime.now.year - @year_of_birth\n#     end\n#\n#     def send_text(sender, text)\n#       sender.send_text_from(@name, text)\n#     end\n#   end\n#\n# Then the type declaration can look like this:\n#\n#   Puppet::DataTypes.create_type('Auth::User') do\n#     interface <<-PUPPET\n#       attributes => {\n#         name => String,\n#         email => String,\n#         year_of_birth => Integer,\n#         age => { type => Integer, kind => derived }\n#       },\n#       functions => {\n#         send_text => Callable[Sender, String[1]]\n#       }\n#       PUPPET\n#\n#     # This load_file is optional and only needed in case\n#     # the implementation is not loaded by other means.\n#     load_file 'puppetx/auth/user'\n#\n#     implementation_class PuppetX::Auth::User\n#   end\n#\nmodule Puppet::DataTypes\n  def self.create_type(type_name, &block)\n    # Ruby < 2.1.0 does not have method on Binding, can only do eval\n    # and it will fail unless protected with an if defined? if the local\n    # variable does not exist in the block's binder.\n    #\n\n    loader = block.binding.eval('loader_injected_arg if defined?(loader_injected_arg)')\n    create_loaded_type(type_name, loader, &block)\n  rescue StandardError => e\n    raise ArgumentError, _(\"Data Type Load Error for type '%{type_name}': %{message}\") % { type_name: type_name, message: e.message }\n  end\n\n  def self.create_loaded_type(type_name, loader, &block)\n    builder = TypeBuilder.new(type_name.to_s)\n    api = TypeBuilderAPI.new(builder).freeze\n    api.instance_eval(&block)\n    builder.create_type(loader)\n  end\n\n  # @api private\n  class TypeBuilder\n    attr_accessor :interface, :implementation, :implementation_class\n\n    def initialize(type_name)\n      @type_name = type_name\n      @implementation = nil\n      @implementation_class = nil\n    end\n\n    def create_type(loader)\n      raise ArgumentError, _('a data type must have an interface') unless @interface.is_a?(String)\n\n      created_type = Puppet::Pops::Types::PObjectType.new(\n        @type_name,\n        Puppet::Pops::Parser::EvaluatingParser.new.parse_string(\"{ #{@interface} }\").body\n      )\n\n      if !@implementation_class.nil?\n        if @implementation_class < Puppet::Pops::Types::PuppetObject\n          @implementation_class.instance_eval do\n            include Puppet::Pops::Types::PuppetObject\n            @_pcore_type = created_type\n\n            def self._pcore_type\n              @_pcore_type\n            end\n          end\n        else\n          Puppet::Pops::Loaders.implementation_registry.register_implementation(created_type, @implementation_class)\n        end\n        created_type.implementation_class = @implementation_class\n      elsif !@implementation.nil?\n        created_type.implementation_override = @implementation\n      end\n      created_type\n    end\n\n    def has_implementation?\n      !(@implementation_class.nil? && @implementation.nil?)\n    end\n  end\n\n  # The TypeBuilderAPI class exposes only those methods that the builder API provides\n  # @api public\n  class TypeBuilderAPI\n    # @api private\n    def initialize(type_builder)\n      @type_builder = type_builder\n    end\n\n    def interface(type_string)\n      raise ArgumentError, _('a data type can only have one interface') unless @type_builder.interface.nil?\n\n      @type_builder.interface = type_string\n    end\n\n    def implementation(&block)\n      raise ArgumentError, _('a data type can only have one implementation') if @type_builder.has_implementation?\n\n      @type_builder.implementation = block\n    end\n\n    def implementation_class(ruby_class)\n      raise ArgumentError, _('a data type can only have one implementation') if @type_builder.has_implementation?\n\n      @type_builder.implementation_class = ruby_class\n    end\n\n    def load_file(file_name)\n      Puppet::Util::Autoload.load_file(file_name, Puppet.lookup(:current_environment))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/defaults.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/util/platform'\n\nmodule Puppet\n  def self.default_diffargs\n    '-u'\n  end\n\n  # If you modify this, update puppet/type/file/checksum.rb too\n  def self.default_digest_algorithm\n    'sha256'\n  end\n\n  def self.valid_digest_algorithms\n    Puppet::Util::Platform.fips_enabled? ?\n      %w[sha256 sha384 sha512 sha224] :\n      %w[sha256 sha384 sha512 sha224 md5]\n  end\n\n  def self.default_file_checksum_types\n    Puppet::Util::Platform.fips_enabled? ?\n      %w[sha256 sha384 sha512 sha224] :\n      %w[sha256 sha384 sha512 sha224 md5]\n  end\n\n  def self.valid_file_checksum_types\n    Puppet::Util::Platform.fips_enabled? ?\n      %w[sha256 sha256lite sha384 sha512 sha224 sha1 sha1lite mtime ctime] :\n      %w[sha256 sha256lite sha384 sha512 sha224 sha1 sha1lite md5 md5lite mtime ctime]\n  end\n\n  def self.default_cadir\n    return \"\" if Puppet::Util::Platform.windows?\n\n    old_ca_dir = \"#{Puppet[:ssldir]}/ca\"\n    new_ca_dir = \"/etc/puppetlabs/puppetserver/ca\"\n\n    if File.exist?(old_ca_dir)\n      if File.symlink?(old_ca_dir)\n        File.readlink(old_ca_dir)\n      else\n        old_ca_dir\n      end\n    else\n      new_ca_dir\n    end\n  end\n\n  def self.default_basemodulepath\n    path = ['$codedir/modules']\n    if (run_mode_dir = Puppet.run_mode.common_module_dir)\n      path << run_mode_dir\n    end\n    path.join(File::PATH_SEPARATOR)\n  end\n\n  def self.default_vendormoduledir\n    Puppet.run_mode.vendor_module_dir\n  end\n\n  ############################################################################################\n  # NOTE: For information about the available values for the \":type\" property of settings,\n  #   see the docs for Settings.define_settings\n  ############################################################################################\n\n  AS_DURATION = 'This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).'\n\n  # @api public\n  # @param args [Puppet::Settings] the settings object to define default settings for\n  # @return void\n  def self.initialize_default_settings!(settings)\n    settings.define_settings(:main,\n    :confdir  => {\n        :default  => nil,\n        :type     => :directory,\n        :desc     => \"The main Puppet configuration directory.  The default for this setting\n          is calculated based on the user.  If the process is running as root or\n          the user that Puppet is supposed to run as, it defaults to a system\n          directory, but if it's running as any other user, it defaults to being\n          in the user's home directory.\",\n    },\n    :codedir  => {\n        :default  => nil,\n        :type     => :directory,\n        :desc     => \"The main Puppet code directory.  The default for this setting\n          is calculated based on the user.  If the process is running as root or\n          the user that Puppet is supposed to run as, it defaults to a system\n          directory, but if it's running as any other user, it defaults to being\n          in the user's home directory.\",\n    },\n    :vardir   => {\n        :default  => nil,\n        :type     => :directory,\n        :owner    => \"service\",\n        :group    => \"service\",\n        :desc     => \"Where Puppet stores dynamic and growing data.  The default for this\n          setting is calculated specially, like `confdir`_.\",\n    },\n\n    ### NOTE: this setting is usually being set to a symbol value.  We don't officially have a\n    ###     setting type for that yet, but we might want to consider creating one.\n    :name     => {\n        :default  => nil,\n        :desc     => \"The name of the application, if we are running as one.  The\n          default is essentially $0 without the path or `.rb`.\",\n    }\n    )\n\n  settings.define_settings(:main,\n    :logdir => {\n        :default  => nil,\n        :type     => :directory,\n        :mode     => \"0750\",\n        :owner    => \"service\",\n        :group    => \"service\",\n        :desc     => \"The directory in which to store log files\",\n    },\n    :log_level => {\n      :default    => 'notice',\n      :type       => :enum,\n      :values     => %w[debug info notice warning err alert emerg crit],\n      :desc       => \"Default logging level for messages from Puppet. Allowed values are:\n\n        * debug\n        * info\n        * notice\n        * warning\n        * err\n        * alert\n        * emerg\n        * crit\n        \",\n      :hook => proc {|value| Puppet::Util::Log.level = value },\n      :call_hook => :on_initialize_and_write,\n    },\n    :disable_warnings => {\n      :default => [],\n      :type    => :array,\n      :desc    => \"A comma-separated list of warning types to suppress. If large numbers\n        of warnings are making Puppet's logs too large or difficult to use, you\n        can temporarily silence them with this setting.\n\n        If you are preparing to upgrade Puppet to a new major version, you\n        should re-enable all warnings for a while.\n\n        Valid values for this setting are:\n\n        * `deprecations` --- disables deprecation warnings.\n        * `undefined_variables` --- disables warnings about non existing variables.\n        * `undefined_resources` --- disables warnings about non existing resources.\",\n      :hook      => proc do |value|\n        values = munge(value)\n        valid   = %w[deprecations undefined_variables undefined_resources]\n        invalid = values - (values & valid)\n        unless invalid.empty?\n          raise ArgumentError, _(\"Cannot disable unrecognized warning types '%{invalid}'.\") % { invalid: invalid.join(',') } +\n              ' ' + _(\"Valid values are '%{values}'.\") % { values: valid.join(', ') }\n        end\n      end\n    },\n    :skip_logging_catalog_request_destination => {\n      :default => false,\n      :type    => :boolean,\n      :desc => \"Specifies whether to suppress the notice of which compiler\n        supplied the catalog. A value of `true` suppresses the notice.\",\n    },\n    :merge_dependency_warnings => {\n      :default => false,\n      :type    => :boolean,\n      :desc    => \"Whether to merge class-level dependency failure warnings.\n\n        When a class has a failed dependency, every resource in the class\n        generates a notice level message about the dependency failure,\n        and a warning level message about skipping the resource.\n\n        If true, all messages caused by a class dependency failure are merged\n        into one message associated with the class.\n        \",\n    },\n    :strict => {\n      :default    => :error,\n      :type       => :symbolic_enum,\n      :values     => [:off, :warning, :error],\n      :desc       => \"The strictness level of puppet. Allowed values are:\n\n        * off     - do not perform extra validation, do not report\n        * warning - perform extra validation, report as warning\n        * error   - perform extra validation, fail with error (default)\n\n        The strictness level is for both language semantics and runtime\n        evaluation validation. In addition to controlling the behavior with\n        this primary server switch some individual warnings may also be controlled\n        by the disable_warnings setting.\n\n        No new validations will be added to a micro (x.y.z) release,\n        but may be added in minor releases (x.y.0). In major releases\n        it expected that most (if not all) strictness validation become\n        standard behavior.\",\n      :hook    => proc do |value|\n        munge(value)\n        value.to_sym\n      end\n    },\n    :disable_i18n => {\n      :default => true,\n      :type    => :boolean,\n      :desc    => \"If true, turns off all translations of Puppet and module\n        log messages, which affects error, warning, and info log messages,\n        as well as any translations in the report and CLI.\",\n      :hook    => proc do |value|\n        if value\n          require_relative '../puppet/gettext/stubs'\n          Puppet::GettextConfig.disable_gettext\n        end\n      end\n    }\n  )\n\n  settings.define_settings(:main,\n    :priority => {\n      :default => nil,\n      :type    => :priority,\n      :desc    => \"The scheduling priority of the process.  Valid values are 'high',\n        'normal', 'low', or 'idle', which are mapped to platform-specific\n        values.  The priority can also be specified as an integer value and\n        will be passed as is, e.g. -5.  Puppet must be running as a privileged\n        user in order to increase scheduling priority.\",\n    },\n    :trace => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to print stack traces on some errors. Will print\n          internal Ruby stack trace interleaved with Puppet function frames.\",\n        :hook     => proc do |value|\n          # Enable or disable Facter's trace option too\n          Puppet.runtime[:facter].trace(value)\n        end\n    },\n    :puppet_trace => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to print the Puppet stack trace on some errors.\n          This is a noop if `trace` is also set.\",\n    },\n    :profile => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to enable experimental performance profiling\",\n    },\n    :versioned_environment_dirs => {\n      :default => false,\n      :type => :boolean,\n      :desc => \"Whether or not to look for versioned environment directories,\n      symlinked from `$environmentpath/<environment>`. This is an experimental\n      feature and should be used with caution.\"\n    },\n    :static_catalogs => {\n      :default    => true,\n      :type       => :boolean,\n      :desc       => \"Whether to compile a [static catalog](https://puppet.com/docs/puppet/latest/static_catalogs.html#enabling-or-disabling-static-catalogs),\n        which occurs only on Puppet Server when the `code-id-command` and\n        `code-content-command` settings are configured in its `puppetserver.conf` file.\",\n    },\n    :settings_catalog => {\n      :default    => true,\n      :type       => :boolean,\n      :desc       => \"Whether to compile and apply the settings catalog\",\n    },\n    :strict_environment_mode => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether the agent specified environment should be considered authoritative,\n        causing the run to fail if the retrieved catalog does not match it.\",\n    },\n    :autoflush => {\n      :default => true,\n      :type       => :boolean,\n      :desc       => \"Whether log files should always flush to disk.\",\n      :hook       => proc { |value| Log.autoflush = value }\n    },\n    :syslogfacility => {\n        :default  => \"daemon\",\n        :desc     => \"What syslog facility to use when logging to syslog.\n          Syslog has a fixed list of valid facilities, and you must\n          choose one of those; you cannot just make one up.\"\n    },\n    :statedir => {\n        :default  => \"$vardir/state\",\n        :type     => :directory,\n        :mode     => \"01755\",\n        :desc     => \"The directory where Puppet state is stored.  Generally,\n          this directory can be removed without causing harm (although it\n          might result in spurious service restarts).\"\n    },\n    :rundir => {\n      :default  => nil,\n      :type     => :directory,\n      :mode     => \"0755\",\n      :owner    => \"service\",\n      :group    => \"service\",\n      :desc     => \"Where Puppet PID files are kept.\"\n    },\n    :genconfig => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"When true, causes Puppet applications to print an example config file\n          to stdout and exit. The example will include descriptions of each\n          setting, and the current (or default) value of each setting,\n          incorporating any settings overridden on the CLI (with the exception\n          of `genconfig` itself). This setting only makes sense when specified\n          on the command line as `--genconfig`.\",\n    },\n    :genmanifest => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to just print a manifest to stdout and exit.  Only makes\n          sense when specified on the command line as `--genmanifest`.  Takes into account arguments specified\n          on the CLI.\",\n    },\n    :configprint => {\n        :default    => \"\",\n        :deprecated => :completely,\n        :desc       => \"Prints the value of a specific configuration setting.  If the name of a\n          setting is provided for this, then the value is printed and puppet\n          exits.  Comma-separate multiple values.  For a list of all values,\n          specify 'all'. This setting is deprecated, the 'puppet config' command replaces this functionality.\",\n    },\n    :color => {\n      :default => \"ansi\",\n      :type    => :string,\n      :desc    => \"Whether to use colors when logging to the console.  Valid values are\n        `ansi` (equivalent to `true`), `html`, and `false`, which produces no color.\"\n    },\n    :mkusers => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to create the necessary user and group that puppet agent will run as.\",\n    },\n    :manage_internal_file_permissions => {\n        :default  => ! Puppet::Util::Platform.windows?,\n        :type     => :boolean,\n        :desc     => \"Whether Puppet should manage the owner, group, and mode of files it uses internally.\n          **Note**: For Windows agents, the default is `false` for versions 4.10.13 and greater, versions 5.5.6 and greater, and versions 6.0 and greater.\",\n    },\n    :onetime => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Perform one configuration run and exit, rather than spawning a long-running\n          daemon. This is useful for interactively running puppet agent, or\n          running puppet agent from cron.\",\n        :short    => 'o',\n    },\n    :path => {\n        :default          => \"none\",\n        :desc             => \"The shell search path.  Defaults to whatever is inherited\n          from the parent process.\n\n          This setting can only be set in the `[main]` section of puppet.conf; it cannot\n          be set in `[server]`, `[agent]`, or an environment config section.\",\n        :call_hook => :on_define_and_write,\n        :hook             => proc do |value|\n          ENV['PATH'] = '' if ENV['PATH'].nil?\n          ENV['PATH'] = value unless value == 'none'\n          paths = ENV['PATH'].split(File::PATH_SEPARATOR)\n          Puppet::Util::Platform.default_paths.each do |path|\n            next if paths.include?(path)\n\n            ENV['PATH'] = ENV.fetch('PATH', nil) + File::PATH_SEPARATOR + path\n          end\n          value\n        end\n    },\n    :libdir => {\n        :type           => :directory,\n        :default        => \"$vardir/lib\",\n        :desc           => \"An extra search path for Puppet.  This is only useful\n          for those files that Puppet will load on demand, and is only\n          guaranteed to work for those cases.  In fact, the autoload\n          mechanism is responsible for making sure this directory\n          is in Ruby's search path\\n\"\n    },\n    :environment => {\n        :default  => \"production\",\n        :desc     => \"The environment in which Puppet is running. For clients,\n          such as `puppet agent`, this determines the environment itself, which\n          Puppet uses to find modules and much more. For servers, such as `puppet server`,\n          this provides the default environment for nodes that Puppet knows nothing about.\n\n          When defining an environment in the `[agent]` section, this refers to the\n          environment that the agent requests from the primary server. The environment doesn't\n          have to exist on the local filesystem because the agent fetches it from the\n          primary server. This definition is used when running `puppet agent`.\n\n          When defined in the `[user]` section, the environment refers to the path that\n          Puppet uses to search for code and modules related to its execution. This\n          requires the environment to exist locally on the filesystem where puppet is\n          being executed. Puppet subcommands, including `puppet module` and\n          `puppet apply`, use this definition.\n\n          Given that the context and effects vary depending on the\n          [config section](https://puppet.com/docs/puppet/latest/config_file_main.html#config-sections)\n          in which the `environment` setting is defined, do not set it globally.\",\n        :short    => \"E\"\n    },\n    :environmentpath => {\n      :default => \"$codedir/environments\",\n      :desc    => \"A search path for directory environments, as a list of directories\n        separated by the system path separator character. (The POSIX path separator\n        is ':', and the Windows path separator is ';'.)\n\n        This setting must have a value set to enable **directory environments.** The\n        recommended value is `$codedir/environments`. For more details, see\n        <https://puppet.com/docs/puppet/latest/environments_about.html>\",\n      :type    => :path,\n    },\n    :report_configured_environmentpath => {\n      :type     => :boolean,\n      :default  => true,\n      :desc     => <<-'EOT'\n      Specifies how environment paths are reported. When the value of\n      `versioned_environment_dirs` is `true`, Puppet applies the readlink function to\n      the `environmentpath` setting when constructing the environment's modulepath. The\n      full readlinked path is referred to as the \"resolved path,\" and the configured\n      path potentially containing symlinks is the \"configured path.\" When reporting\n      where resources come from, users may choose between the configured and resolved\n      path.\n\n      When set to `false`, the resolved paths are reported instead of the configured paths.\n      EOT\n    },\n    :use_last_environment => {\n      :type     => :boolean,\n      :default  => true,\n      :desc     => <<-'EOT'\n      Puppet saves both the initial and converged environment in the last_run_summary file.\n      If they differ, and this setting is set to true, we will use the last converged\n      environment and skip the node request.\n\n      When set to false, we will do the node request and ignore the environment data from the last_run_summary file.\n      EOT\n    },\n    :always_retry_plugins => {\n        :type     => :boolean,\n        :default  => true,\n        :desc     => <<-'EOT'\n        Affects how we cache attempts to load Puppet resource types and features.  If\n        true, then calls to `Puppet.type.<type>?` `Puppet.feature.<feature>?`\n        will always attempt to load the type or feature (which can be an\n        expensive operation) unless it has already been loaded successfully.\n        This makes it possible for a single agent run to, e.g., install a\n        package that provides the underlying capabilities for a type or feature,\n        and then later load that type or feature during the same run (even if\n        the type or feature had been tested earlier and had not been available).\n\n        If this setting is set to false, then types and features will only be\n        checked once, and if they are not available, the negative result is\n        cached and returned for all subsequent attempts to load the type or\n        feature.  This behavior is almost always appropriate for the server,\n        and can result in a significant performance improvement for types and\n        features that are checked frequently.\n        EOT\n    },\n    :diff_args => {\n        :default  => -> { default_diffargs },\n        :desc     => \"Which arguments to pass to the diff command when printing differences between\n          files. The command to use can be chosen with the `diff` setting.\",\n    },\n    :diff => {\n      :default => (Puppet::Util::Platform.windows? ? \"\" : \"diff\"),\n      :desc    => \"Which diff command to use when printing differences between files. This setting\n          has no default value on Windows, as standard `diff` is not available, but Puppet can use many\n          third-party diff tools.\",\n    },\n    :show_diff => {\n        :type     => :boolean,\n        :default  => false,\n        :desc     => \"Whether to log and report a contextual diff when files are being replaced.\n          This causes partial file contents to pass through Puppet's normal\n          logging and reporting system, so this setting should be used with\n          caution if you are sending Puppet's reports to an insecure\n          destination. This feature currently requires the `diff/lcs` Ruby\n          library.\",\n    },\n    :daemonize => {\n        :type     => :boolean,\n        :default  => !Puppet::Util::Platform.windows?,\n        :desc     => \"Whether to send the process into the background.  This defaults\n          to true on POSIX systems, and to false on Windows (where Puppet\n          currently cannot daemonize).\",\n        :short    => \"D\",\n        :hook     => proc do |value|\n                       if value and Puppet::Util::Platform.windows?\n                         raise \"Cannot daemonize on Windows\"\n                       end\n                     end\n    },\n    :maximum_uid => {\n        :default  => 4_294_967_290,\n        :type     => :integer,\n        :desc     => \"The maximum allowed UID.  Some platforms use negative UIDs\n          but then ship with tools that do not know how to handle signed ints,\n          so the UIDs show up as huge numbers that can then not be fed back into\n          the system.  This is a hackish way to fail in a slightly more useful\n          way when that happens.\",\n    },\n    :route_file => {\n      :default    => \"$confdir/routes.yaml\",\n      :desc       => \"The YAML file containing indirector route configuration.\",\n    },\n    :node_terminus => {\n      :type       => :terminus,\n      :default    => \"plain\",\n      :desc       => <<-'EOT',\n        Which node data plugin to use when compiling node catalogs.\n\n        When Puppet compiles a catalog, it combines two primary sources of info: the main manifest,\n        and a node data plugin (often called a \"node terminus,\" for historical reasons). Node data\n        plugins provide three things for a given node name:\n\n        1. A list of classes to add to that node's catalog (and, optionally, values for their\n           parameters).\n        2. Which Puppet environment the node should use.\n        3. A list of additional top-scope variables to set.\n\n        The three main node data plugins are:\n\n        * `plain` --- Returns no data, so that the main manifest controls all node configuration.\n        * `exec` --- Uses an\n          [external node classifier (ENC)](https://puppet.com/docs/puppet/latest/nodes_external.html),\n          configured by the `external_nodes` setting. This lets you pull a list of Puppet classes\n          from any external system, using a small glue script to perform the request and format the\n          result as YAML.\n        * `classifier` (formerly `console`) --- Specific to Puppet Enterprise. Uses the PE console\n          for node data.\"\n      EOT\n    },\n    :node_cache_terminus => {\n      :type       => :terminus,\n      :default    => nil,\n      :desc       => \"How to store cached nodes.\n      Valid values are (none), 'json', 'msgpack', or 'yaml'.\",\n    },\n    :data_binding_terminus => {\n      :type    => :terminus,\n      :default => \"hiera\",\n      :desc    =>\n        \"This setting has been deprecated. Use of any value other than 'hiera' should instead be configured\n         in a version 5 hiera.yaml. Until this setting is removed, it controls which data binding terminus\n         to use for global automatic data binding (across all environments). By default this value is 'hiera'.\n         A value of 'none' turns off the global binding.\",\n      :call_hook => :on_initialize_and_write,\n      :hook => proc do |value|\n        if Puppet[:strict] != :off\n          s_val = value.to_s # because sometimes the value is a symbol\n          unless s_val == 'hiera' || s_val == 'none' || value == '' || value.nil?\n            #TRANSLATORS 'data_binding_terminus' is a setting and should not be translated\n            message = _(\"Setting 'data_binding_terminus' is deprecated.\")\n            #TRANSLATORS 'hiera' should not be translated\n            message += ' ' + _(\"Convert custom terminus to hiera 5 API.\")\n            Puppet.deprecation_warning(message)\n          end\n        end\n      end\n    },\n    :hiera_config => {\n      :default => lambda do\n        config = nil\n        codedir = settings[:codedir]\n        if codedir.is_a?(String)\n          config = File.expand_path(File.join(codedir, 'hiera.yaml'))\n          config = nil unless Puppet::FileSystem.exist?(config)\n        end\n        config = File.expand_path(File.join(settings[:confdir], 'hiera.yaml')) if config.nil?\n        config\n      end,\n      :desc    => \"The hiera configuration file. Puppet only reads this file on startup, so you must restart the puppet server every time you edit it.\",\n      :type    => :file,\n    },\n    :binder_config => {\n      :default => nil,\n      :desc    => \"The binder configuration file. Puppet reads this file on each request to configure the bindings system.\n      If set to nil (the default), a $confdir/binder_config.yaml is optionally loaded. If it does not exists, a default configuration\n      is used. If the setting :binding_config is specified, it must reference a valid and existing yaml file.\",\n      :type    => :file,\n    },\n    :catalog_terminus => {\n      :type       => :terminus,\n      :default    => \"compiler\",\n      :desc       => \"Where to get node catalogs.  This is useful to change if, for instance,\n      you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store.\",\n    },\n    :catalog_cache_terminus => {\n      :type       => :terminus,\n      :default    => nil,\n      :desc       => \"How to store cached catalogs. Valid values are 'json', 'msgpack' and 'yaml'. The agent application defaults to 'json'.\"\n    },\n    :facts_terminus => {\n      :default => 'facter',\n      :desc => \"The node facts terminus.\",\n    },\n    :trusted_external_command => {\n      :default  => nil,\n      :type     => :file_or_directory,\n      :desc     => \"The external trusted facts script or directory to use.\n        This setting's value can be set to the path to an executable command that\n        can produce external trusted facts or to a directory containing those\n        executable commands. The command(s) must:\n\n        * Take the name of a node as a command-line argument.\n        * Return a JSON hash with the external trusted facts for this node.\n        * For unknown or invalid nodes, exit with a non-zero exit code.\n\n        If the setting points to an executable command, then the external trusted\n        facts will be stored in the 'external' key of the trusted facts hash. Otherwise\n        for each executable file in the directory, the external trusted facts will be\n        stored in the `<basename>` key of the `trusted['external']` hash. For example,\n        if the files foo.rb and bar.sh are in the directory, then `trusted['external']`\n        will be the hash `{ 'foo' => <foo.rb output>, 'bar' => <bar.sh output> }`.\",\n    },\n    :default_file_terminus => {\n      :type       => :terminus,\n      :default    => \"rest\",\n      :desc       => \"The default source for files if no server is given in a\n      uri, e.g. puppet:///file. The default of `rest` causes the file to be\n      retrieved using the `server` setting. When running `apply` the default\n      is `file_server`, causing requests to be filled locally.\"\n    },\n    :http_proxy_host => {\n      :default    => \"none\",\n      :desc       => \"The HTTP proxy host to use for outgoing connections. The proxy will be bypassed if\n      the server's hostname matches the NO_PROXY environment variable or `no_proxy` setting. Note: You\n      may need to use a FQDN for the server hostname when using a proxy. Environment variable\n      http_proxy or HTTP_PROXY will override this value. \",\n    },\n    :http_proxy_port => {\n      :default    => 3128,\n      :type       => :port,\n      :desc       => \"The HTTP proxy port to use for outgoing connections\",\n    },\n    :http_proxy_user => {\n      :default    => \"none\",\n      :desc       => \"The user name for an authenticated HTTP proxy. Requires the `http_proxy_host` setting.\",\n    },\n    :http_proxy_password =>{\n      :default    => \"none\",\n      :hook       => proc do |value|\n        if value =~ %r{[@!# /]}\n          raise \"Passwords set in the http_proxy_password setting must be valid as part of a URL, and any reserved characters must be URL-encoded. We received: #{value}\"\n        end\n      end,\n      :desc       => \"The password for the user of an authenticated HTTP proxy.\n        Requires the `http_proxy_user` setting.\n\n        Note that passwords must be valid when used as part of a URL. If a password\n        contains any characters with special meanings in URLs (as specified by RFC 3986\n        section 2.2), they must be URL-encoded. (For example, `#` would become `%23`.)\",\n    },\n    :no_proxy => {\n      :default    => \"localhost, 127.0.0.1\",\n      :desc       => \"List of host or domain names that should not go through `http_proxy_host`. Environment variable no_proxy or NO_PROXY will override this value. Names can be specified as an FQDN `host.example.com`, wildcard `*.example.com`, dotted domain `.example.com`, or suffix `example.com`.\",\n    },\n    :http_keepalive_timeout => {\n      :default    => \"4s\",\n      :type       => :duration,\n      :desc       => \"The maximum amount of time a persistent HTTP connection can remain idle in the connection pool, before it is closed.  This timeout should be shorter than the keepalive timeout used on the HTTP server, e.g. Apache KeepAliveTimeout directive.\n      #{AS_DURATION}\"\n    },\n    :http_debug => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether to write HTTP request and responses to stderr. This should never be used in a production environment.\"\n    },\n    :http_connect_timeout => {\n      :default => \"2m\",\n      :type    => :duration,\n      :desc    => \"The maximum amount of time to wait when establishing an HTTP connection. The default\n      value is 2 minutes.\n      #{AS_DURATION}\",\n    },\n    :http_read_timeout => {\n      :default => \"10m\",\n      :type    => :duration,\n      :desc    => \"The time to wait for data to be read from an HTTP connection. If nothing is\n      read after the elapsed interval then the connection will be closed. The default value is 10 minutes.\n      #{AS_DURATION}\",\n    },\n    :http_user_agent => {\n      :default => \"Puppet/#{Puppet.version} Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})\",\n      :desc    => \"The HTTP User-Agent string to send when making network requests.\"\n    },\n    :filetimeout => {\n      :default    => \"15s\",\n      :type       => :duration,\n      :desc       => \"The minimum time to wait between checking for updates in\n      configuration files.  This timeout determines how quickly Puppet checks whether\n      a file (such as manifests or puppet.conf) has changed on disk. The default will\n      change in a future release to be 'unlimited', requiring a reload of the Puppet\n      service to pick up changes to its internal configuration. Currently we do not\n      accept a value of 'unlimited'. To reparse files within an environment in\n      Puppet Server please use the environment_cache endpoint\",\n      :hook => proc do |val|\n        unless [0, 15, '15s'].include?(val)\n          Puppet.deprecation_warning(<<-WARNING)\nFine grained control of filetimeouts is deprecated. In future\nreleases this value will only determine if file content is cached.\n\nValid values are 0 (never cache) and 15 (15 second minimum wait time).\n          WARNING\n        end\n      end\n    },\n    :environment_timeout => {\n      :default    => \"0\",\n      :type       => :ttl,\n      :desc       => \"How long the Puppet server should cache data it loads from an\n      environment.\n\n      A value of `0` will disable caching. This setting can also be set to\n      `unlimited`, which will cache environments until the server is restarted\n      or told to refresh the cache. All other values will result in Puppet\n      server evicting environments that haven't been used within the last\n      `environment_timeout` seconds.\n\n      You should change this setting once your Puppet deployment is doing\n      non-trivial work. We chose the default value of `0` because it lets new\n      users update their code without any extra steps, but it lowers the\n      performance of your Puppet server. We recommend either:\n\n      * Setting this to `unlimited` and explicitly refreshing your Puppet server\n        as part of your code deployment process.\n\n      * Setting this to a number that will keep your most actively used\n        environments cached, but allow testing environments to fall out of the\n        cache and reduce memory usage. A value of 3 minutes (3m) is a reasonable\n        value.\n\n      Once you set `environment_timeout` to a non-zero value, you need to tell\n      Puppet server to read new code from disk using the `environment-cache` API\n      endpoint after you deploy new code. See the docs for the Puppet Server\n      [administrative API](https://puppet.com/docs/puppetserver/latest/admin-api/v1/environment-cache.html).\n      \"\n    },\n    :environment_data_provider => {\n      :desc       => \"The name of a registered environment data provider used when obtaining environment\n      specific data. The three built in and registered providers are 'none' (no data), 'function' (data\n      obtained by calling the function 'environment::data()') and 'hiera' (data obtained using a data\n      provider configured using a hiera.yaml file in root of the environment).\n      Other environment data providers may be registered in modules on the module path. For such\n      custom data providers see the respective module documentation. This setting is deprecated.\",\n      :hook => proc { |value|\n        unless value.nil? || Puppet[:strict] == :off\n          #TRANSLATORS 'environment_data_provider' is a setting and should not be translated\n          Puppet.deprecation_warning(_(\"Setting 'environment_data_provider' is deprecated.\"))\n        end\n      }\n    },\n    :prerun_command => {\n      :default    => \"\",\n      :desc       => \"A command to run before every agent run.  If this command returns a non-zero\n      return code, the entire Puppet run will fail.\",\n    },\n    :postrun_command => {\n      :default    => \"\",\n      :desc       => \"A command to run after every agent run.  If this command returns a non-zero\n      return code, the entire Puppet run will be considered to have failed, even though it might have\n      performed work during the normal run.\",\n    },\n    :freeze_main => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Freezes the 'main' class, disallowing any code to be added to it.  This\n          essentially means that you can't have any code outside of a node,\n          class, or definition other than in the site manifest.\",\n    },\n    :preview_outputdir => {\n      :default => '$vardir/preview',\n      :type     => :directory,\n      :mode     => \"0750\",\n      :owner    => \"service\",\n      :group    => \"service\",\n      :desc    => \"The directory where catalog previews per node are generated.\"\n    },\n    :location_trusted => {\n      :default => false,\n      :type     => :boolean,\n      :desc    => \"This will allow sending the name + password and the cookie header to all hosts that puppet may redirect to.\n        This may or may not introduce a security breach if puppet redirects you to a site to which you'll send your authentication info and cookies.\"\n    }\n  )\n\n  settings.define_settings(:module_tool,\n    :module_repository  => {\n      :default  => 'https://forgeapi.puppet.com',\n      :desc     => \"The module repository\",\n    },\n    :module_working_dir => {\n        :default  => (Puppet::Util::Platform.windows? ? Dir.tmpdir() : '$vardir/puppet-module'),\n        :desc     => \"The directory into which module tool data is stored\",\n    },\n    :forge_authorization => {\n        :default  => nil,\n        :desc     => \"The authorization key to connect to the Puppet Forge. Leave blank for unauthorized or license based connections\",\n    },\n    :module_groups => {\n        :default  => nil,\n        :desc     => \"Extra module groups to request from the Puppet Forge. This is an internal setting, and users should never change it.\",\n    }\n  )\n\n    settings.define_settings(\n    :main,\n    # We have to downcase the fqdn, because the current ssl stuff (as opposed to in master) doesn't have good facilities for\n    # manipulating naming.\n    :certname => {\n      :default => -> { Puppet::Settings.default_certname.downcase },\n      :desc => \"The name to use when handling certificates. When a node\n        requests a certificate from the CA Puppet Server, it uses the value of the\n        `certname` setting as its requested Subject CN.\n\n        This is the name used when managing a node's permissions in\n        Puppet Server's [auth.conf](https://puppet.com/docs/puppetserver/latest/config_file_auth.html).\n        In most cases, it is also used as the node's name when matching\n        [node definitions](https://puppet.com/docs/puppet/latest/lang_node_definitions.html)\n        and requesting data from an ENC. (This can be changed with the `node_name_value`\n        and `node_name_fact` settings, although you should only do so if you have\n        a compelling reason.)\n\n        A node's certname is available in Puppet manifests as `$trusted['certname']`. (See\n        [Facts and Built-In Variables](https://puppet.com/docs/puppet/latest/lang_facts_and_builtin_vars.html)\n        for more details.)\n\n        * For best compatibility, you should limit the value of `certname` to\n          only use lowercase letters, numbers, periods, underscores, and dashes. (That is,\n          it should match `/\\A[a-z0-9._-]+\\Z/`.)\n        * The special value `ca` is reserved, and can't be used as the certname\n          for a normal node.\n\n          **Note:** You must set the certname in the main section of the puppet.conf file. Setting it in a different section causes errors.\n\n        Defaults to the node's fully qualified domain name.\",\n      :call_hook => :on_initialize_and_write,\n      :hook => proc { |value|\n        raise(ArgumentError, _(\"Certificate names must be lower case\")) unless value == value.downcase\n      }},\n    :dns_alt_names => {\n      :default => '',\n      :desc    => <<EOT,\nA comma-separated list of alternate DNS names for Puppet Server. These are extra\nhostnames (in addition to its `certname`) that the server is allowed to use when\nserving agents. Puppet checks this setting when automatically creating a\ncertificate for Puppet agent or Puppet Server. These can be either IP or DNS, and the type\nshould be specified and followed with a colon. Untyped inputs will default to DNS.\n\nIn order to handle agent requests at a given hostname (like\n\"puppet.example.com\"), Puppet Server needs a certificate that proves it's\nallowed to use that name; if a server shows a certificate that doesn't include\nits hostname, Puppet agents will refuse to trust it. If you use a single\nhostname for Puppet traffic but load-balance it to multiple Puppet Servers, each\nof those servers needs to include the official hostname in its list of extra\nnames.\n\n**Note:** The list of alternate names is locked in when the server's\ncertificate is signed. If you need to change the list later, you can't just\nchange this setting; you also need to regenerate the certificate. For more\ninformation on that process, see the \n[cert regen docs](https://puppet.com/docs/puppet/latest/ssl_regenerate_certificates.html).\n\nTo see all the alternate names your servers are using, log into your CA server\nand run `puppetserver ca list --all`, then check the output for `(alt names: ...)`.\nMost agent nodes should NOT have alternate names; the only certs that should\nhave them are Puppet Server nodes that you want other agents to trust.\nEOT\n    },\n    :csr_attributes => {\n      :default => \"$confdir/csr_attributes.yaml\",\n      :type => :file,\n      :desc => <<EOT\nAn optional file containing custom attributes to add to certificate signing\nrequests (CSRs). You should ensure that this file does not exist on your CA\nPuppet Server; if it does, unwanted certificate extensions may leak into\ncertificates created with the `puppetserver ca generate` command.\n\nIf present, this file must be a YAML hash containing a `custom_attributes` key\nand/or an `extension_requests` key. The value of each key must be a hash, where\neach key is a valid OID and each value is an object that can be cast to a string.\n\nCustom attributes can be used by the CA when deciding whether to sign the\ncertificate, but are then discarded. Attribute OIDs can be any OID value except\nthe standard CSR attributes (i.e. attributes described in RFC 2985 section 5.4).\nThis is useful for embedding a pre-shared key for autosigning policy executables\n(see the `autosign` setting), often by using the `1.2.840.113549.1.9.7`\n(\"challenge password\") OID.\n\nExtension requests will be permanently embedded in the final certificate.\nExtension OIDs must be in the \"ppRegCertExt\" (`1.3.6.1.4.1.34380.1.1`),\n\"ppPrivCertExt\" (`1.3.6.1.4.1.34380.1.2`), or\n\"ppAuthCertExt\" (`1.3.6.1.4.1.34380.1.3`) OID arcs. The ppRegCertExt arc is\nreserved for four of the most common pieces of data to embed: `pp_uuid` (`.1`),\n`pp_instance_id` (`.2`), `pp_image_name` (`.3`), and `pp_preshared_key` (`.4`)\n--- in the YAML file, these can be referred to by their short descriptive names\ninstead of their full OID. The ppPrivCertExt arc is unregulated, and can be used\nfor site-specific extensions. The ppAuthCert arc is reserved for two pieces of\ndata to embed: `pp_authorization` (`.1`) and `pp_auth_role` (`.13`). As with\nppRegCertExt, in the YAML file, these can be referred to by their short\ndescriptive name instead of their full OID.\nEOT\n    },\n    :certdir => {\n      :default => \"$ssldir/certs\",\n      :type   => :directory,\n      :mode => \"0755\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"The certificate directory.\"\n    },\n    :ssldir => {\n      :default => \"$confdir/ssl\",\n      :type   => :directory,\n      :mode => \"0771\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where SSL certificates are kept.\"\n    },\n    :ssl_lockfile => {\n      :default => \"$ssldir/ssl.lock\",\n      :type    => :string,\n      :desc    => \"A lock file to indicate that the ssl bootstrap process is currently in progress.\",\n    },\n    :publickeydir => {\n      :default => \"$ssldir/public_keys\",\n      :type   => :directory,\n      :mode => \"0755\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"The public key directory.\"\n    },\n    :requestdir => {\n      :default => \"$ssldir/certificate_requests\",\n      :type => :directory,\n      :mode => \"0755\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where host certificate requests are stored.\"\n    },\n    :privatekeydir => {\n      :default => \"$ssldir/private_keys\",\n      :type   => :directory,\n      :mode => \"0750\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"The private key directory.\"\n    },\n    :privatedir => {\n      :default => \"$ssldir/private\",\n      :type   => :directory,\n      :mode => \"0750\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where the client stores private certificate information.\"\n    },\n    :passfile => {\n      :default => \"$privatedir/password\",\n      :type   => :file,\n      :mode => \"0640\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where puppet agent stores the password for its private key.\n        Generally unused.\"\n    },\n    :hostcsr => {\n      :default => \"$requestdir/$certname.pem\",\n      :type   => :file,\n      :mode => \"0644\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where individual hosts store their certificate request (CSR)\n         while waiting for the CA to issue their certificate.\"\n    },\n    :hostcert => {\n      :default => \"$certdir/$certname.pem\",\n      :type   => :file,\n      :mode => \"0644\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where individual hosts store and look for their certificates.\"\n    },\n    :hostprivkey => {\n      :default => \"$privatekeydir/$certname.pem\",\n      :type   => :file,\n      :mode => \"0640\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where individual hosts store and look for their private key.\"\n    },\n    :hostpubkey => {\n      :default => \"$publickeydir/$certname.pem\",\n      :type   => :file,\n      :mode => \"0644\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where individual hosts store and look for their public key.\"\n    },\n    :localcacert => {\n      :default => \"$certdir/ca.pem\",\n      :type   => :file,\n      :mode => \"0644\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where each client stores the CA certificate.\"\n    },\n    :ca_fingerprint => {\n      :default => nil,\n      :type   => :string,\n      :desc => \"The expected fingerprint of the CA certificate. If specified, the agent\n        will compare the CA certificate fingerprint that it downloads against this value\n        and reject the CA certificate if the values do not match. This only applies\n        during the first download of the CA certificate.\"\n    },\n    :ssl_trust_store => {\n      :default => nil,\n      :type => :file,\n      :desc => \"A file containing CA certificates in PEM format that puppet should trust\n        when making HTTPS requests. This **only** applies to https requests to non-puppet\n        infrastructure, such as retrieving file metadata and content from https file sources,\n        puppet module tool and the 'http' report processor. This setting is ignored when\n        making requests to puppet:// URLs such as catalog and report requests.\",\n    },\n    :hostcrl => {\n      :default => \"$ssldir/crl.pem\",\n      :type   => :file,\n      :mode => \"0644\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where the host's certificate revocation list can be found.\n        This is distinct from the certificate authority's CRL.\"\n    },\n    :certificate_revocation => {\n        :default  => 'chain',\n        :type     => :certificate_revocation,\n        :desc     => <<-'EOT'\n          Whether certificate revocation checking should be enabled, and what level of\n          checking should be performed.\n\n          When certificate revocation is enabled, Puppet expects the contents of its CRL\n          to be one or more PEM-encoded CRLs concatenated together. When using a cert\n          bundle, CRLs for all CAs in the chain of trust must be included in the crl file.\n          The chain should be ordered from least to most authoritative, with the first CRL\n          listed being for the root of the chain and the last being for the leaf CA.\n\n          When certificate_revocation is set to 'true' or 'chain', Puppet ensures\n          that each CA in the chain of trust has not been revoked by its issuing CA.\n\n          When certificate_revocation is set to 'leaf', Puppet verifies certs against\n          the issuing CA's revocation list, but it does not verify the revocation status\n          of the issuing CA or any CA above it within the chain of trust.\n\n          When certificate_revocation is set to 'false', Puppet disables all\n          certificate revocation checking and does not attempt to download the CRL.\n        EOT\n    },\n    :ciphers => {\n      :default => 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256',\n      :type => :string,\n      :desc => \"The list of ciphersuites for TLS connections initiated by puppet. The\n                default value is chosen to support TLS 1.0 and up, but can be made\n                more restrictive if needed. The ciphersuites must be specified in OpenSSL\n                format, not IANA.\"\n    },\n    :key_type => {\n      :default => 'rsa',\n      :type    => :enum,\n      :values  => %w[rsa ec],\n      :desc    => \"The type of private key. Valid values are `rsa` and `ec`. Default is `rsa`.\"\n    },\n    :named_curve => {\n      :default => 'prime256v1',\n      :type    => :string,\n      :desc    => \"The short name for the EC curve used to generate the EC private key. Valid\n                   values must be one of the curves in `OpenSSL::PKey::EC.builtin_curves`.\n                   Default is `prime256v1`.\"\n    },\n    :digest_algorithm => {\n        :default  => -> { default_digest_algorithm },\n        :type     => :enum,\n        :values   => valid_digest_algorithms,\n        :desc     => \"Which digest algorithm to use for file resources and the filebucket.\n                      Valid values are #{valid_digest_algorithms.join(', ')}. Default is\n                      #{default_digest_algorithm}.\",\n    },\n    :supported_checksum_types => {\n      :default => -> { default_file_checksum_types },\n      :type    => :array,\n      :desc    => \"Checksum types supported by this agent for use in file resources of a\n                   static catalog. Values must be comma-separated. Valid types are\n                   #{valid_file_checksum_types.join(', ')}. Default is\n                   #{default_file_checksum_types.join(', ')}.\",\n      :hook    => proc do |value|\n        values = munge(value)\n\n        invalid = values - Puppet.valid_file_checksum_types\n        unless invalid.empty?\n          raise ArgumentError, _(\"Invalid value '%{value}' for parameter %{name}. Allowed values are '%{allowed_values}'\") % {\n            value: invalid.first, name: @name, allowed_values: Puppet.valid_file_checksum_types.join(\"', '\")\n          }\n        end\n      end\n    },\n    :logdest => {\n      :type      => :string,\n      :desc      => \"Where to send log messages. Choose between 'syslog' (the POSIX syslog\n      service), 'eventlog' (the Windows Event Log), 'console', or the path to a log\n      file. Multiple destinations can be set using a comma separated list (eg: `/path/file1,console,/path/file2`)\"\n      # Sure would be nice to set the Puppet::Util::Log destination here in an :on_initialize_and_write hook,\n      # unfortunately we have a large number of tests that rely on the logging not resetting itself when the\n      # settings are initialized as they test what gets logged during settings initialization.\n    }\n  )\n\n    settings.define_settings(\n    :ca,\n    :ca_name => {\n      :default => \"Puppet CA: $certname\",\n      :desc    => \"The name to use the Certificate Authority certificate.\",\n    },\n    :cadir => {\n      :default => -> { default_cadir },\n      :type => :directory,\n      :desc => \"The root directory for the certificate authority.\",\n    },\n    :cacert => {\n      :default => \"$cadir/ca_crt.pem\",\n      :type => :file,\n      :desc => \"The CA certificate.\",\n    },\n    :cakey => {\n      :default => \"$cadir/ca_key.pem\",\n      :type => :file,\n      :desc => \"The CA private key.\",\n    },\n    :capub => {\n      :default => \"$cadir/ca_pub.pem\",\n      :type => :file,\n      :desc => \"The CA public key.\",\n    },\n    :cacrl => {\n      :default => \"$cadir/ca_crl.pem\",\n      :type => :file,\n      :desc => \"The certificate revocation list (CRL) for the CA.\",\n    },\n    :csrdir => {\n      :default => \"$cadir/requests\",\n      :type => :directory,\n      :desc => \"Where the CA stores certificate requests.\",\n    },\n    :signeddir => {\n      :default => \"$cadir/signed\",\n      :type => :directory,\n      :desc => \"Where the CA stores signed certificates.\",\n    },\n    :serial => {\n      :default => \"$cadir/serial\",\n      :type => :file,\n      :desc => \"Where the serial number for certificates is stored.\",\n    },\n    :autosign => {\n      :default => \"$confdir/autosign.conf\",\n      :type => :autosign,\n      :desc => \"Whether (and how) to autosign certificate requests. This setting\n        is only relevant on a Puppet Server acting as a certificate authority (CA).\n\n        Valid values are true (autosigns all certificate requests; not recommended),\n        false (disables autosigning certificates), or the absolute path to a file.\n\n        The file specified in this setting may be either a **configuration file**\n        or a **custom policy executable.** Puppet will automatically determine\n        what it is: If the Puppet user (see the `user` setting) can execute the\n        file, it will be treated as a policy executable; otherwise, it will be\n        treated as a config file.\n\n        If a custom policy executable is configured, the CA Puppet Server will run it\n        every time it receives a CSR. The executable will be passed the subject CN of the\n        request _as a command line argument,_ and the contents of the CSR in PEM format\n        _on stdin._ It should exit with a status of 0 if the cert should be autosigned\n        and non-zero if the cert should not be autosigned.\n\n        If a certificate request is not autosigned, it will persist for review. An admin\n        user can use the `puppetserver ca sign` command to manually sign it, or can delete\n        the request.\n\n        For info on autosign configuration files, see\n        [the guide to Puppet's config files](https://puppet.com/docs/puppet/latest/config_file_autosign.html).\",\n    },\n    :allow_duplicate_certs => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether to allow a new certificate request to overwrite an existing\n        certificate request. If true, then the old certificate must be cleaned using\n        `puppetserver ca clean`, and the new request signed using `puppetserver ca sign`.\"\n    },\n    :ca_ttl => {\n      :default    => \"5y\",\n      :type       => :duration,\n      :desc       => \"The default TTL for new certificates.\n      #{AS_DURATION}\",\n    },\n    :ca_refresh_interval => {\n      :default    => \"1d\",\n      :type       => :duration,\n      :desc       => \"How often the Puppet agent refreshes its local CA\n         certificates. By default, CA certificates are refreshed every 24 hours. If a\n         different interval is specified, the agent refreshes its CA certificates during\n         the next agent run if the elapsed time since the certificates were last\n         refreshed exceeds the specified duration.\n\n         In general, the interval should be greater than the `runinterval`\n         value. Setting the `ca_refresh_interval` value to 0 or an equal or\n         lesser value than `runinterval` causes the CA certificates to be\n         refreshed on every run.\n\n         If the agent downloads new CA certs, the agent uses those for subsequent\n         network requests. If the refresh request fails or if the CA certs are\n         unchanged on the server, then the agent run will continue using the\n         local CA certs it already has. #{AS_DURATION}\",\n    },\n    :crl_refresh_interval => {\n      :default    => \"1d\",\n      :type       => :duration,\n      :desc       => \"How often the Puppet agent refreshes its local Certificate\n         Revocation List (CRL). By default, the CRL is refreshed every 24 hours. If\n         a different interval is specified, the agent refreshes its CRL on the next\n         Puppet agent run if the elapsed time since the CRL was last refreshed\n         exceeds the specified interval.\n\n         In general, the interval should be greater than the `runinterval` value.\n         Setting the `crl_refresh_interval` value to 0 or an equal or lesser value\n         than `runinterval` causes the CRL to be refreshed on every run.\n\n         If the agent downloads a new CRL, the agent will use it for subsequent\n         network requests. If the refresh request fails or if the CRL is\n         unchanged on the server, then the agent run will continue using the\n         local CRL it already has.#{AS_DURATION}\",\n    },\n    :hostcert_renewal_interval => {\n      :default => \"30d\",\n      :type    => :duration,\n      :desc => \"How often the Puppet agent renews its client certificate. By\n         default, the client certificate is renewed 30 days before the certificate\n         expires. If a different interval is specified, the agent renews its client\n         certificate during the next agent run, assuming that the client certificate has\n         expired within the specified duration.\n\n         In general, the `hostcert_renewal_interval` value should be greater than the\n         `runinterval` value. Setting the `hostcert_renewal_interval` value to 0 disables\n         automatic renewal.\n\n         If the agent downloads a new certificate, the agent will use it\n         for subsequent network requests. If the refresh request fails, the agent run\n         continues to use its existing certificate. #{AS_DURATION}\",\n    },\n    :keylength => {\n      :default    => 4096,\n      :type       => :integer,\n      :desc       => \"The bit length of keys.\",\n    },\n    :cert_inventory => {\n      :default => \"$cadir/inventory.txt\",\n      :type => :file,\n      :desc => \"The inventory file. This is a text file to which the CA writes a\n        complete listing of all certificates.\",\n    }\n  )\n\n    # Define the config default.\n\n    settings.define_settings(:application,\n      :config_file_name => {\n          :type     => :string,\n          :default  => Puppet::Settings.default_config_file_name,\n          :desc     => \"The name of the puppet config file.\",\n      },\n      :config => {\n          :type => :file,\n          :default  => \"$confdir/${config_file_name}\",\n          :desc     => \"The configuration file for the current puppet application.\",\n      },\n      :pidfile => {\n          :type => :file,\n          :default  => \"$rundir/${run_mode}.pid\",\n          :desc     => \"The file containing the PID of a running process.\n            This file is intended to be used by service management frameworks\n            and monitoring systems to determine if a puppet process is still in\n            the process table.\",\n      },\n      :sourceaddress => {\n        :default    => nil,\n        :desc       => \"The address the agent should use to initiate requests.\",\n      }\n    )\n\n  settings.define_settings(:environment,\n    :manifest => {\n      :default    => nil,\n      :type       => :file_or_directory,\n      :desc       => \"The entry-point manifest for the primary server. This can be one file\n        or a directory of manifests to be evaluated in alphabetical order. Puppet manages\n        this path as a directory if one exists or if the path ends with a / or \\\\.\n\n        Setting a global value for `manifest` in puppet.conf is not allowed\n        (but it can be overridden from the commandline). Please use\n        directory environments instead. If you need to use something other than the\n        environment's `manifests` directory as the main manifest, you can set\n        `manifest` in environment.conf. For more info, see\n        <https://puppet.com/docs/puppet/latest/environments_about.html>\",\n    },\n    :modulepath => {\n      :default => \"\",\n      :type => :path,\n      :desc => \"The search path for modules, as a list of directories separated by the system\n        path separator character. (The POSIX path separator is ':', and the\n        Windows path separator is ';'.)\n\n        Setting a global value for `modulepath` in puppet.conf is not allowed\n        (but it can be overridden from the commandline). Please use\n        directory environments instead. If you need to use something other than the\n        default modulepath of `<ACTIVE ENVIRONMENT'S MODULES DIR>:$basemodulepath`,\n        you can set `modulepath` in environment.conf. For more info, see\n        <https://puppet.com/docs/puppet/latest/environments_about.html>\",\n    },\n    :config_version => {\n      :default    => \"\",\n      :desc       => \"How to determine the configuration version.  By default, it will be the\n      time that the configuration is parsed, but you can provide a shell script to override how the\n      version is determined.  The output of this script will be added to every log message in the\n      reports, allowing you to correlate changes on your hosts to the source version on the server.\n\n      Setting a global value for config_version in puppet.conf is not allowed\n      (but it can be overridden from the commandline). Please set a\n      per-environment value in environment.conf instead. For more info, see\n      <https://puppet.com/docs/puppet/latest/environments_about.html>\",\n    }\n  )\n\n  settings.define_settings(:server,\n    :user => {\n      :default    => \"puppet\",\n      :desc       => \"The user Puppet Server will run as. Used to ensure\n      the agent side processes (agent, apply, etc) create files and\n      directories readable by Puppet Server when necessary.\",\n    },\n    :group => {\n      :default    => \"puppet\",\n      :desc       => \"The group Puppet Server will run as. Used to ensure\n      the agent side processes (agent, apply, etc) create files and\n      directories readable by Puppet Server when necessary.\",\n    },\n    :default_manifest => {\n      :default    => \"./manifests\",\n      :type       => :string,\n      :desc       => \"The default main manifest for directory environments. Any environment that\n        doesn't set the `manifest` setting in its `environment.conf` file will use\n        this manifest.\n\n        This setting's value can be an absolute or relative path. An absolute path\n        will make all environments default to the same main manifest; a relative\n        path will allow each environment to use its own manifest, and Puppet will\n        resolve the path relative to each environment's main directory.\n\n        In either case, the path can point to a single file or to a directory of\n        manifests to be evaluated in alphabetical order.\",\n    },\n    :disable_per_environment_manifest => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether to disallow an environment-specific main manifest. When set\n        to `true`, Puppet will use the manifest specified in the `default_manifest` setting\n        for all environments. If an environment specifies a different main manifest in its\n        `environment.conf` file, catalog requests for that environment will fail with an error.\n\n        This setting requires `default_manifest` to be set to an absolute path.\",\n      :hook       => proc do |value|\n        if value && !Pathname.new(Puppet[:default_manifest]).absolute?\n          raise(Puppet::Settings::ValidationError,\n                \"The 'default_manifest' setting must be set to an absolute path when 'disable_per_environment_manifest' is true\")\n        end\n      end,\n    },\n    :code => {\n      :default    => \"\",\n      :desc       => \"Code to parse directly.  This is essentially only used\n      by `puppet`, and should only be set if you're writing your own Puppet\n      executable.\",\n    },\n    :masterport => {\n      :default    => 8140,\n      :type       => :port,\n      :desc       => \"The default port puppet subcommands use to communicate\n      with Puppet Server. (eg `puppet facts upload`, `puppet agent`). May be\n      overridden by more specific settings (see `ca_port`, `report_port`).\",\n    },\n    :serverport => {\n      :type => :alias,\n      :alias_for => :masterport\n    },\n    :bucketdir => {\n      :default => \"$vardir/bucket\",\n      :type => :directory,\n      :mode => \"0750\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"Where FileBucket files are stored.\"\n    },\n    :trusted_oid_mapping_file => {\n      :default    => \"$confdir/custom_trusted_oid_mapping.yaml\",\n      :type       => :file,\n      :desc       => \"File that provides mapping between custom SSL oids and user-friendly names\"\n    },\n    :basemodulepath => {\n      :default => -> { default_basemodulepath },\n      :type => :path,\n      :desc => \"The search path for **global** modules. Should be specified as a\n        list of directories separated by the system path separator character. (The\n        POSIX path separator is ':', and the Windows path separator is ';'.)\n\n        These are the modules that will be used by _all_ environments. Note that\n        the `modules` directory of the active environment will have priority over\n        any global directories. For more info, see\n        <https://puppet.com/docs/puppet/latest/environments_about.html>\",\n    },\n    :vendormoduledir => {\n      :default => -> { default_vendormoduledir },\n      :type => :string,\n      :desc => \"The directory containing **vendored** modules. These modules will\n      be used by _all_ environments like those in the `basemodulepath`. The only\n      difference is that modules in the `basemodulepath` are pluginsynced, while\n      vendored modules are not\",\n    },\n    :ssl_client_header => {\n      :default    => \"HTTP_X_CLIENT_DN\",\n      :desc       => \"The header containing an authenticated client's SSL DN.\n      This header must be set by the proxy to the authenticated client's SSL\n      DN (e.g., `/CN=puppet.puppetlabs.com`).  Puppet will parse out the Common\n      Name (CN) from the Distinguished Name (DN) and use the value of the CN\n      field for authorization.\n\n      Note that the name of the HTTP header gets munged by the web server\n      common gateway interface: an `HTTP_` prefix is added, dashes are converted\n      to underscores, and all letters are uppercased.  Thus, to use the\n      `X-Client-DN` header, this setting should be `HTTP_X_CLIENT_DN`.\",\n    },\n    :ssl_client_verify_header => {\n      :default    => \"HTTP_X_CLIENT_VERIFY\",\n      :desc       => \"The header containing the status message of the client\n      verification. This header must be set by the proxy to 'SUCCESS' if the\n      client successfully authenticated, and anything else otherwise.\n\n      Note that the name of the HTTP header gets munged by the web server\n      common gateway interface: an `HTTP_` prefix is added, dashes are converted\n      to underscores, and all letters are uppercased.  Thus, to use the\n      `X-Client-Verify` header, this setting should be\n      `HTTP_X_CLIENT_VERIFY`.\",\n    },\n    # To make sure this directory is created before we try to use it on the server, we need\n    # it to be in the server section (#1138).\n    :yamldir => {\n      :default => \"$vardir/yaml\",\n      :type => :directory,\n      :owner => \"service\",\n      :group => \"service\",\n      :mode => \"0750\",\n      :desc => \"The directory in which YAML data is stored, usually in a subdirectory.\"},\n    :server_datadir => {\n      :default => \"$vardir/server_data\",\n      :type => :directory,\n      :owner => \"service\",\n      :group => \"service\",\n      :mode => \"0750\",\n      :desc => \"The directory in which serialized data is stored, usually in a subdirectory.\"},\n    :reports => {\n      :default    => \"store\",\n      :desc       => \"The list of report handlers to use. When using multiple report handlers,\n        their names should be comma-separated, with whitespace allowed. (For example,\n        `reports = http, store`.)\n\n        This setting is relevant to puppet server and puppet apply. The primary Puppet\n        server will call these report handlers with the reports it receives from\n        agent nodes, and puppet apply will call them with its own report. (In\n        all cases, the node applying the catalog must have `report = true`.)\n\n        See the report reference for information on the built-in report\n        handlers; custom report handlers can also be loaded from modules.\n        (Report handlers are loaded from the lib directory, at\n        `puppet/reports/NAME.rb`.)\n\n        To turn off reports entirely, set this to `none`\",\n    },\n    :exclude_unchanged_resources => {\n      :default => true,\n      :type => :boolean,\n      :desc => \"Specifies how unchanged resources are listed in reports. When\n        set to `true`, resources that have had no changes after catalog application\n        will not have corresponding unchanged resource status updates listed in a\n        report.\"\n    },\n    :reportdir => {\n      :default => \"$vardir/reports\",\n      :type => :directory,\n      :mode => \"0750\",\n      :owner => \"service\",\n      :group => \"service\",\n      :desc => \"The directory in which to store reports. Each node gets\n        a separate subdirectory in this directory. This setting is only\n        used when the `store` report processor is enabled (see the\n        `reports` setting).\"},\n    :reporturl => {\n      :default    => \"http://localhost:3000/reports/upload\",\n      :desc       => \"The URL that reports should be forwarded to. This setting\n        is only used when the `http` report processor is enabled (see the\n        `reports` setting).\",\n    },\n    :fileserverconfig => {\n      :default    => \"$confdir/fileserver.conf\",\n      :type       => :file,\n      :desc       => \"Where the fileserver configuration is stored.\",\n    })\n\n  settings.define_settings(:device,\n    :devicedir =>  {\n        :default  => \"$vardir/devices\",\n        :type     => :directory,\n        :mode     => \"0750\",\n        :owner    => \"service\",\n        :group    => \"service\",\n        :desc     => \"The root directory of devices' $vardir.\",\n    },\n    :deviceconfig => {\n        :default  => \"$confdir/device.conf\",\n        :desc     => \"Path to the device config file for puppet device.\",\n    }\n  )\n\n  settings.define_settings(:agent,\n    :node_name_value => {\n      :default => \"$certname\",\n      :desc => \"The explicit value used for the node name for all requests the agent\n        makes to the primary server. WARNING: This setting is mutually exclusive with\n        node_name_fact.  Changing this setting also requires changes to\n        Puppet Server's default [auth.conf](https://puppet.com/docs/puppetserver/latest/config_file_auth.html).\"\n    },\n    :node_name_fact => {\n      :default => \"\",\n      :desc => \"The fact name used to determine the node name used for all requests the agent\n        makes to the primary server. WARNING: This setting is mutually exclusive with\n        node_name_value.  Changing this setting also requires changes to\n        Puppet Server's default [auth.conf](https://puppet.com/docs/puppetserver/latest/config_file_auth.html).\",\n      :hook => proc do |value|\n        if !value.empty? and Puppet[:node_name_value] != Puppet[:certname]\n          raise \"Cannot specify both the node_name_value and node_name_fact settings\"\n        end\n      end\n    },\n    :statefile => {\n      :default => \"$statedir/state.yaml\",\n      :type => :file,\n      :mode => \"0640\",\n      :desc => \"Where Puppet agent and Puppet Server store state associated\n        with the running configuration.  In the case of Puppet Server,\n        this file reflects the state discovered through interacting\n        with clients.\"\n    },\n    :statettl => {\n      :default => \"32d\",\n      :type    => :ttl,\n      :desc    => \"How long the Puppet agent should cache when a resource was last checked or synced.\n      #{AS_DURATION}\n      A value of `0` or `unlimited` will disable cache pruning.\n\n      This setting affects the usage of `schedule` resources, as the information\n      about when a resource was last checked (and therefore when it needs to be\n      checked again) is stored in the `statefile`. The `statettl` needs to be\n      large enough to ensure that a resource will not trigger multiple times\n      during a schedule due to its entry expiring from the cache.\"\n    },\n    :transactionstorefile => {\n      :default => \"$statedir/transactionstore.yaml\",\n      :type => :file,\n      :mode => \"0640\",\n      :desc => \"Transactional storage file for persisting data between\n        transactions for the purposes of inferring information (such as\n        corrective_change) on new data received.\"\n    },\n    :clientyamldir => {\n      :default => \"$vardir/client_yaml\",\n      :type => :directory,\n      :mode => \"0750\",\n      :desc => \"The directory in which client-side YAML data is stored.\"\n    },\n    :client_datadir => {\n      :default => \"$vardir/client_data\",\n      :type => :directory,\n      :mode => \"0750\",\n      :desc => \"The directory in which serialized data is stored on the client.\"\n    },\n    :write_catalog_summary => {\n      :default => true,\n      :type => :boolean,\n      :desc => \"Whether to write the `classfile` and `resourcefile` after applying\n        the catalog. It is enabled by default, except when running `puppet apply`.\",\n    },\n    :classfile => {\n      :default => \"$statedir/classes.txt\",\n      :type => :file,\n      :owner => \"root\",\n      :mode => \"0640\",\n      :desc => \"The file in which puppet agent stores a list of the classes\n        associated with the retrieved configuration.  Can be loaded in\n        the separate `puppet` executable using the `--loadclasses`\n        option.\"},\n    :resourcefile => {\n      :default => \"$statedir/resources.txt\",\n      :type => :file,\n      :owner => \"root\",\n      :mode => \"0640\",\n      :desc => \"The file in which puppet agent stores a list of the resources\n        associated with the retrieved configuration.\" },\n    :puppetdlog => {\n      :default => \"$logdir/puppetd.log\",\n      :type => :file,\n      :owner => \"root\",\n      :mode => \"0640\",\n      :desc => \"The fallback log file. This is only used when the `--logdest` option\n        is not specified AND Puppet is running on an operating system where both\n        the POSIX syslog service and the Windows Event Log are unavailable. (Currently,\n        no supported operating systems match that description.)\n\n        Despite the name, both puppet agent and puppet server will use this file\n        as the fallback logging destination.\n\n        For control over logging destinations, see the `--logdest` command line\n        option in the manual pages for puppet server, puppet agent, and puppet\n        apply. You can see man pages by running `puppet <SUBCOMMAND> --help`,\n        or read them online at https://puppet.com/docs/puppet/latest/man/.\"\n    },\n    :deviceconfdir => {\n      :default  => \"$confdir/devices\",\n      :type     => :directory,\n      :mode     => \"0750\",\n      :owner    => \"service\",\n      :group    => \"service\",\n      :desc     => \"The root directory of devices' $confdir.\",\n    },\n    :server => {\n      :default => \"puppet\",\n      :desc => \"The primary Puppet server to which the Puppet agent should connect.\",\n    },\n    :server_list => {\n      :default => [],\n      :type => :server_list,\n      :desc => \"The list of primary Puppet servers to which the Puppet agent should connect,\n        in the order that they will be tried. Each value should be a fully qualified domain name, followed by an optional ':' and port number. If a port is omitted, Puppet uses masterport for that host.\",\n    },\n    :use_srv_records => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether the server will search for SRV records in DNS for the current domain.\",\n    },\n    :srv_domain => {\n      :default    => -> { Puppet::Settings.domain_fact },\n      :desc       => \"The domain which will be queried to find the SRV records of servers to use.\",\n    },\n    :http_extra_headers => {\n      :default => [],\n      :type => :http_extra_headers,\n      :desc => \"The list of extra headers that will be sent with http requests to the primary server.\n      The header definition consists of a name and a value separated by a colon.\"\n    },\n    :ignoreschedules => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Boolean; whether puppet agent should ignore schedules.  This is useful\n      for initial puppet agent runs.\",\n    },\n    :default_schedules => {\n      :default    => true,\n      :type       => :boolean,\n      :desc       => \"Boolean; whether to generate the default schedule resources. Setting this to\n      false is useful for keeping external report processors clean of skipped schedule resources.\",\n    },\n    :noop => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether to apply catalogs in noop mode, which allows Puppet to\n        partially simulate a normal run. This setting affects puppet agent and\n        puppet apply.\n\n        When running in noop mode, Puppet will check whether each resource is in sync,\n        like it does when running normally. However, if a resource attribute is not in\n        the desired state (as declared in the catalog), Puppet will take no\n        action, and will instead report the changes it _would_ have made. These\n        simulated changes will appear in the report sent to the primary Puppet server, or\n        be shown on the console if running puppet agent or puppet apply in the\n        foreground. The simulated changes will not send refresh events to any\n        subscribing or notified resources, although Puppet will log that a refresh\n        event _would_ have been sent.\n\n        **Important note:**\n        [The `noop` metaparameter](https://puppet.com/docs/puppet/latest/metaparameter.html#noop)\n        allows you to apply individual resources in noop mode, and will override\n        the global value of the `noop` setting. This means a resource with\n        `noop => false` _will_ be changed if necessary, even when running puppet\n        agent with `noop = true` or `--noop`. (Conversely, a resource with\n        `noop => true` will only be simulated, even when noop mode is globally disabled.)\",\n    },\n    :runinterval => {\n      :default  => \"30m\",\n      :type     => :duration,\n      :desc     => \"How often puppet agent applies the catalog.\n          Note that a runinterval of 0 means \\\"run continuously\\\" rather than\n          \\\"never run.\\\" #{AS_DURATION}\",\n    },\n    :runtimeout => {\n      :default  => \"1h\",\n      :type     => :duration,\n      :desc     => \"The maximum amount of time an agent run is allowed to take.\n          A Puppet agent run that exceeds this timeout will be aborted. A value\n          of 0 disables the timeout. Defaults to 1 hour. #{AS_DURATION}\",\n    },\n    :ca_server => {\n      :default    => \"$server\",\n      :desc       => \"The server to use for certificate\n      authority requests.  It's a separate server because it cannot\n      and does not need to horizontally scale.\",\n    },\n    :ca_port => {\n      :default    => \"$serverport\",\n      :type       => :port,\n      :desc       => \"The port to use for the certificate authority.\",\n    },\n    :preferred_serialization_format => {\n      :default    => \"json\",\n      :desc       => \"The preferred means of serializing\n      ruby instances for passing over the wire.  This won't guarantee that all\n      instances will be serialized using this method, since not all classes\n      can be guaranteed to support this format, but it will be used for all\n      classes that support it.\",\n      :hook => proc { |value|\n        if value == \"pson\" && !Puppet.features.pson?\n          raise(Puppet::Settings::ValidationError, \"The 'puppet-pson' gem must be installed to use the PSON serialization format.\")\n        end\n      }\n    },\n    :allow_pson_serialization => {\n      :default    => false,\n      :type       => :boolean,\n      :desc => \"Whether to allow PSON serialization. When unable to serialize to\n        JSON or other formats, Puppet falls back to PSON. This option affects the\n        configuration management service responses of Puppet Server and the process by\n        which the agent saves its cached catalog. With a default value of `false`, this\n        option is useful in preventing the loss of data because rich data cannot be\n        serialized via PSON.\",\n    },\n    :agent_catalog_run_lockfile => {\n      :default    => \"$statedir/agent_catalog_run.lock\",\n      :type       => :string, # (#2888) Ensure this file is not added to the settings catalog.\n      :desc       => \"A lock file to indicate that a puppet agent catalog run is currently in progress.\n        The file contains the pid of the process that holds the lock on the catalog run.\",\n    },\n    :agent_disabled_lockfile => {\n        :default    => \"$statedir/agent_disabled.lock\",\n        :type       => :string,\n        :desc       => \"A lock file to indicate that puppet agent runs have been administratively\n          disabled.  File contains a JSON object with state information.\",\n    },\n    :usecacheonfailure => {\n      :default    => true,\n      :type       => :boolean,\n      :desc       => \"Whether to use the cached configuration when the remote\n        configuration will not compile.  This option is useful for testing\n        new configurations, where you want to fix the broken configuration\n        rather than reverting to a known-good one.\",\n    },\n    :include_legacy_facts => {\n      :type       => :boolean,\n      :default    => false,\n      :desc       => \"Whether to include legacy facts when requesting a catalog. This\n        option can be set to `false` if all puppet manifests, hiera.yaml, and hiera\n        configuration layers no longer access legacy facts, such as `$osfamily`, and\n        instead access structured facts, such as `$facts['os']['family']`.\"\n    },\n    :fact_name_length_soft_limit => {\n      :default    => 2560,\n      :type       => :integer,\n      :desc       => \"The soft limit for the length of a fact name.\",\n    },\n    :fact_value_length_soft_limit => {\n      :default    => 4096,\n      :type       => :integer,\n      :desc       => \"The soft limit for the length of a fact value.\",\n    },\n    :top_level_facts_soft_limit => {\n      :default    => 512,\n      :type       => :integer,\n      :desc       => \"The soft limit for the number of top level facts.\",\n    },\n    :number_of_facts_soft_limit => {\n      :default    => 10_240,\n      :type       => :integer,\n      :desc       => \"The soft limit for the total number of fact values. This counts the\n        child elements of all facts (e.g. all items of an array or a hash), not just top\n        level facts.\",\n    },\n    :payload_soft_limit => {\n      :default    => 16 * 1024 * 1024,\n      :type       => :integer,\n      :desc       => \"The soft limit for the size of the payload.\",\n    },\n    :use_cached_catalog => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether to only use the cached catalog rather than compiling a new catalog\n        on every run.  Puppet can be run with this enabled by default and then selectively\n        disabled when a recompile is desired. Because a Puppet agent using cached catalogs\n        does not contact the primary server for a new catalog, it also does not upload facts at\n        the beginning of the Puppet run.\",\n    },\n    :ignoremissingtypes => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Skip searching for classes and definitions that were missing during a\n        prior compilation. The list of missing objects is maintained per-environment and\n        persists until the environment is cleared or the primary server is restarted.\",\n    },\n    :splaylimit => {\n      :default    => \"$runinterval\",\n      :type       => :duration,\n      :desc       => \"The maximum time to delay before an agent's first run when\n        `splay` is enabled. Defaults to the agent's `$runinterval`. The\n        `splay` interval is random and recalculated each time the agent is started or\n        restarted. #{AS_DURATION}\",\n    },\n    :splay => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether to sleep for a random amount of time, ranging from\n        immediately up to its `$splaylimit`, before performing its first agent run\n        after a service restart. After this period, the agent runs periodically\n        on its `$runinterval`.\n\n        For example, assume a default 30-minute `$runinterval`, `splay` set to its\n        default of `false`, and an agent starting at :00 past the hour. The agent\n        would check in every 30 minutes at :01 and :31 past the hour.\n\n        With `splay` enabled, it waits any amount of time up to its `$splaylimit`\n        before its first run. For example, it might randomly wait 8 minutes,\n        then start its first run at :08 past the hour. With the `$runinterval`\n        at its default 30 minutes, its next run will be at :38 past the hour.\n\n        If you restart an agent's puppet service with `splay` enabled, it\n        recalculates its splay period and delays its first agent run after\n        restarting for this new period. If you simultaneously restart a group of\n        puppet agents with `splay` enabled, their checkins to your primary servers\n        can be distributed more evenly.\",\n    },\n    :clientbucketdir => {\n      :default  => \"$vardir/clientbucket\",\n      :type     => :directory,\n      :mode     => \"0750\",\n      :desc     => \"Where FileBucket files are stored locally.\"\n    },\n    :report_server => {\n      :default  => \"$server\",\n      :desc     => \"The server to send transaction reports to.\",\n    },\n    :report_port => {\n      :default  => \"$serverport\",\n      :type     => :port,\n      :desc     => \"The port to communicate with the report_server.\",\n    },\n    :report => {\n      :default  => true,\n      :type     => :boolean,\n      :desc     => \"Whether to send reports after every transaction.\",\n    },\n    :report_include_system_store => {\n      :default  => false,\n      :type     => :boolean,\n      :desc     => \"Whether the 'http' report processor should include the system\n        certificate store when submitting reports to HTTPS URLs. If false, then\n        the 'http' processor will only trust HTTPS report servers whose certificates\n        are issued by the puppet CA or one of its intermediate CAs. If true, the\n        processor will additionally trust CA certificates in the system's\n        certificate store.\"\n    },\n    :resubmit_facts => {\n      :default  => false,\n      :type     => :boolean,\n      :desc     => \"Whether to send updated facts after every transaction. By default\n        puppet only submits facts at the beginning of the transaction before applying a\n        catalog. Since puppet can modify the state of the system, the value of the facts\n        may change after puppet finishes. Therefore, any facts stored in puppetdb may not\n        be consistent until the agent next runs, typically in 30 minutes. If this feature\n        is enabled, puppet will resubmit facts after applying its catalog, ensuring facts\n        for the node stored in puppetdb are current. However, this will double the fact\n        submission load on puppetdb, so it is disabled by default.\",\n    },\n    :publicdir => {\n      :default  => nil,\n      :type     => :directory,\n      :mode     => \"0755\",\n      :desc     => \"Where Puppet stores public files.\"\n    },\n    :lastrunfile =>  {\n      :default  => \"$publicdir/last_run_summary.yaml\",\n      :type     => :file,\n      :mode     => \"0640\",\n      :desc     => \"Where puppet agent stores the last run report summary in yaml format.\"\n    },\n    :lastrunreport =>  {\n      :default  => \"$statedir/last_run_report.yaml\",\n      :type     => :file,\n      :mode     => \"0640\",\n      :desc     => \"Where Puppet Agent stores the last run report, by default, in yaml format.\n        The format of the report can be changed by setting the `cache` key of the `report` terminus\n        in the [routes.yaml](https://puppet.com/docs/puppet/latest/config_file_routes.html) file.\n        To avoid mismatches between content and file extension, this setting needs to be\n        manually updated to reflect the terminus changes.\"\n    },\n    :graph => {\n      :default  => false,\n      :type     => :boolean,\n      :desc     => \"Whether to create .dot graph files, which let you visualize the\n        dependency and containment relationships in Puppet's catalog. You\n        can load and view these files with tools like\n        [OmniGraffle](http://www.omnigroup.com/applications/omnigraffle/) (OS X)\n        or [graphviz](http://www.graphviz.org/) (multi-platform).\n\n        Graph files are created when _applying_ a catalog, so this setting\n        should be used on nodes running `puppet agent` or `puppet apply`.\n\n        The `graphdir` setting determines where Puppet will save graphs. Note\n        that we don't save graphs for historical runs; Puppet will replace the\n        previous .dot files with new ones every time it applies a catalog.\n\n        See your graphing software's documentation for details on opening .dot\n        files. If you're using GraphViz's `dot` command, you can do a quick PNG\n        render with `dot -Tpng <DOT FILE> -o <OUTPUT FILE>`.\",\n    },\n    :graphdir => {\n      :default    => \"$statedir/graphs\",\n      :type       => :directory,\n      :desc       => \"Where to save .dot-format graphs (when the `graph` setting is enabled).\",\n    },\n    :waitforcert => {\n      :default  => \"2m\",\n      :type     => :duration,\n      :desc     => \"How frequently puppet agent should ask for a signed certificate.\n\n      When starting for the first time, puppet agent will submit a certificate\n      signing request (CSR) to the server named in the `ca_server` setting\n      (usually the primary Puppet server); this may be autosigned, or may need to be\n      approved by a human, depending on the CA server's configuration.\n\n      Puppet agent cannot apply configurations until its approved certificate is\n      available. Since the certificate may or may not be available immediately,\n      puppet agent will repeatedly try to fetch it at this interval. You can\n      turn off waiting for certificates by specifying a time of 0, or a maximum\n      amount of time to wait in the `maxwaitforcert` setting, in which case\n      puppet agent will exit if it cannot get a cert.\n      #{AS_DURATION}\",\n    },\n    :maxwaitforcert => {\n      :default  => \"unlimited\",\n      :type     => :ttl,\n      :desc     => \"The maximum amount of time the Puppet agent should wait for its\n      certificate request to be signed. A value of `unlimited` will cause puppet agent\n      to ask for a signed certificate indefinitely.\n      #{AS_DURATION}\",\n    },\n    :waitforlock => {\n      :default  => \"0\",\n      :type     => :duration,\n      :desc     => \"How frequently puppet agent should try running when there is an\n      already ongoing puppet agent instance.\n\n      This argument is by default disabled (value set to 0). In this case puppet agent will\n      immediately exit if it cannot run at that moment. When a value other than 0 is set, this\n      can also be used in combination with the `maxwaitforlock` argument.\n      #{AS_DURATION}\",\n    },\n    :maxwaitforlock => {\n      :default  => \"1m\",\n      :type     => :ttl,\n      :desc     => \"The maximum amount of time the puppet agent should wait for an\n      already running puppet agent to finish before starting a new one. This is set by default to 1 minute.\n      A value of `unlimited` will cause puppet agent to wait indefinitely.\n      #{AS_DURATION}\",\n    }\n  )\n\n  # Plugin information.\n\n  settings.define_settings(\n    :main,\n    :plugindest => {\n      :type       => :directory,\n      :default    => \"$libdir\",\n      :desc       => \"Where Puppet should store plugins that it pulls down from the central\n      server.\",\n    },\n    :pluginsource => {\n      :default    => \"puppet:///plugins\",\n      :desc       => \"From where to retrieve plugins.  The standard Puppet `file` type\n      is used for retrieval, so anything that is a valid file source can\n      be used here.\",\n    },\n    :pluginfactdest => {\n      :type     => :directory,\n      :default  => \"$vardir/facts.d\",\n      :desc     => \"Where Puppet should store external facts that are being handled by pluginsync\",\n    },\n    :pluginfactsource => {\n      :default  => \"puppet:///pluginfacts\",\n      :desc     => \"Where to retrieve external facts for pluginsync\",\n    },\n    :localedest => {\n      :type       => :directory,\n      :default    => \"$vardir/locales\",\n      :desc       => \"Where Puppet should store translation files that it pulls down from the central\n      server.\",\n    },\n    :localesource => {\n      :default    => \"puppet:///locales\",\n      :desc       => \"From where to retrieve translation files.  The standard Puppet `file` type\n      is used for retrieval, so anything that is a valid file source can\n      be used here.\",\n    },\n    :pluginsync => {\n      :default    => true,\n      :type       => :boolean,\n      :desc       => \"Whether plugins should be synced with the central server. This setting is\n        deprecated.\",\n      :hook => proc { |_value|\n        #TRANSLATORS 'pluginsync' is a setting and should not be translated\n        Puppet.deprecation_warning(_(\"Setting 'pluginsync' is deprecated.\"))\n      }\n    },\n    :pluginsignore => {\n        :default  => \".svn CVS .git .hg\",\n        :desc     => \"What files to ignore when pulling down plugins.\",\n    },\n    :ignore_plugin_errors => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether the puppet run should ignore errors during pluginsync. If the setting\n        is false and there are errors during pluginsync, then the agent will abort the run and\n        submit a report containing information about the failed run.\"\n    }\n  )\n\n    # Central fact information.\n\n    settings.define_settings(\n    :main,\n    :factpath => {\n      :type     => :path,\n      :default  => \"$vardir/lib/facter#{File::PATH_SEPARATOR}$vardir/facts\",\n      :desc     => \"Where Puppet should look for facts.  Multiple directories should\n        be separated by the system path separator character. (The POSIX path\n        separator is ':', and the Windows path separator is ';'.)\",\n\n      :call_hook => :on_initialize_and_write, # Call our hook with the default value, so we always get the value added to facter.\n      :hook => proc do |value|\n        paths = value.split(File::PATH_SEPARATOR)\n        facter = Puppet.runtime[:facter]\n        facter.search(*paths)\n      end\n    }\n  )\n\n  settings.define_settings(\n    :transaction,\n    :tags => {\n      :default    => \"\",\n      :desc       => \"Tags to use to find resources.  If this is set, then\n        only resources tagged with the specified tags will be applied.\n        Values must be comma-separated.\",\n    },\n    :skip_tags => {\n      :default    => \"\",\n      :desc       => \"Tags to use to filter resources.  If this is set, then\n        only resources not tagged with the specified tags will be applied.\n        Values must be comma-separated.\",\n    },\n    :evaltrace => {\n      :default    => false,\n      :type       => :boolean,\n      :desc       => \"Whether each resource should log when it is\n        being evaluated.  This allows you to interactively see exactly\n        what is being done.\",\n    },\n    :preprocess_deferred => {\n      :default => false,\n      :type => :boolean,\n      :desc => \"Whether Puppet should call deferred functions before applying\n        the catalog. If set to `true`, all prerequisites required for the\n        deferred function must be satisfied before the Puppet run. If set to\n        `false`, deferred functions follow Puppet relationships and\n        ordering. In this way, Puppet can install the prerequisites required for a\n        deferred function and call the deferred function in the same run.\",\n    },\n    :summarize => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to print a transaction summary.\",\n    }\n  )\n\n    settings.define_settings(\n    :main,\n    :external_nodes => {\n        :default  => \"none\",\n        :desc     => \"The external node classifier (ENC) script to use for node data.\n          Puppet combines this data with the main manifest to produce node catalogs.\n\n          To enable this setting, set the `node_terminus` setting to `exec`.\n\n          This setting's value must be the path to an executable command that\n          can produce node information. The command must:\n\n          * Take the name of a node as a command-line argument.\n\n          * Return a YAML hash with up to three keys:\n            * `classes` --- A list of classes, as an array or hash.\n            * `environment` --- A string.\n            * `parameters` --- A list of top-scope variables to set, as a hash.\n\n          * For unknown nodes, exit with a non-zero exit code.\n\n          Generally, an ENC script makes requests to an external data source.\n\n          For more info, see [the ENC documentation](https://puppet.com/docs/puppet/latest/nodes_external.html).\",\n    }\n  )\n\n        settings.define_settings(\n        :ldap,\n    :ldapssl => {\n      :default  => false,\n      :type   => :boolean,\n      :desc   => \"Whether SSL should be used when searching for nodes.\n        Defaults to false because SSL usually requires certificates\n        to be set up on the client side.\",\n    },\n    :ldaptls => {\n      :default  => false,\n      :type     => :boolean,\n      :desc     => \"Whether TLS should be used when searching for nodes.\n        Defaults to false because TLS usually requires certificates\n        to be set up on the client side.\",\n    },\n    :ldapserver => {\n      :default  => \"ldap\",\n      :desc     => \"The LDAP server.\",\n    },\n    :ldapport => {\n      :default  => 389,\n      :type     => :port,\n      :desc     => \"The LDAP port.\",\n    },\n\n    :ldapstring => {\n      :default  => \"(&(objectclass=puppetClient)(cn=%s))\",\n      :desc     => \"The search string used to find an LDAP node.\",\n    },\n    :ldapclassattrs => {\n      :default  => \"puppetclass\",\n      :desc     => \"The LDAP attributes to use to define Puppet classes.  Values\n        should be comma-separated.\",\n    },\n    :ldapstackedattrs => {\n      :default  => \"puppetvar\",\n      :desc     => \"The LDAP attributes that should be stacked to arrays by adding\n        the values in all hierarchy elements of the tree.  Values\n        should be comma-separated.\",\n    },\n    :ldapattrs => {\n      :default  => \"all\",\n      :desc     => \"The LDAP attributes to include when querying LDAP for nodes.  All\n        returned attributes are set as variables in the top-level scope.\n        Multiple values should be comma-separated.  The value 'all' returns\n        all attributes.\",\n    },\n    :ldapparentattr => {\n      :default  => \"parentnode\",\n      :desc     => \"The attribute to use to define the parent node.\",\n    },\n    :ldapuser => {\n      :default  => \"\",\n      :desc     => \"The user to use to connect to LDAP.  Must be specified as a\n        full DN.\",\n    },\n    :ldappassword => {\n      :default  => \"\",\n      :desc     => \"The password to use to connect to LDAP.\",\n    },\n    :ldapbase => {\n        :default  => \"\",\n        :desc     => \"The search base for LDAP searches.  It's impossible to provide\n          a meaningful default here, although the LDAP libraries might\n          have one already set.  Generally, it should be the 'ou=Hosts'\n          branch under your main directory.\",\n    }\n      )\n\n  settings.define_settings(:server,\n    :storeconfigs => {\n      :default  => false,\n      :type     => :boolean,\n      :desc     => \"Whether to store each client's configuration, including catalogs, facts,\n        and related data. This also enables the import and export of resources in\n        the Puppet language - a mechanism for exchange resources between nodes.\n\n        By default this uses the 'puppetdb' backend.\n\n        You can adjust the backend using the storeconfigs_backend setting.\",\n      # Call our hook with the default value, so we always get the libdir set.\n      :call_hook => :on_initialize_and_write,\n      :hook => proc do |value|\n        require_relative '../puppet/node'\n        require_relative '../puppet/node/facts'\n        if value\n          Puppet::Resource::Catalog.indirection.set_global_setting(:cache_class, :store_configs)\n          settings.override_default(:catalog_cache_terminus, :store_configs)\n          Puppet::Node::Facts.indirection.set_global_setting(:cache_class, :store_configs)\n          Puppet::Resource.indirection.set_global_setting(:terminus_class, :store_configs)\n        end\n      end\n    },\n    :storeconfigs_backend => {\n      :type => :terminus,\n      :default => \"puppetdb\",\n      :desc => \"Configure the backend terminus used for StoreConfigs.\n        By default, this uses the PuppetDB store, which must be installed\n        and configured before turning on StoreConfigs.\"\n    }\n  )\n\n  settings.define_settings(:parser,\n   :max_errors => {\n     :default => 10,\n     :type => :integer,\n     :desc => <<-'EOT'\n       Sets the max number of logged/displayed parser validation errors in case\n       multiple errors have been detected. A value of 0 is the same as a value of 1; a\n       minimum of one error is always raised.  The count is per manifest.\n     EOT\n   },\n   :max_warnings => {\n     :default => 10,\n     :type => :integer,\n     :desc => <<-'EOT'\n       Sets the max number of logged/displayed parser validation warnings in\n       case multiple warnings have been detected. A value of 0 blocks logging of\n       warnings.  The count is per manifest.\n     EOT\n     },\n  :max_deprecations => {\n    :default => 10,\n    :type => :integer,\n    :desc => <<-'EOT'\n      Sets the max number of logged/displayed parser validation deprecation\n      warnings in case multiple deprecation warnings have been detected. A value of 0\n      blocks the logging of deprecation warnings.  The count is per manifest.\n    EOT\n    },\n  :strict_variables => {\n    :default => true,\n    :type => :boolean,\n    :desc => <<-'EOT'\n      Causes an evaluation error when referencing unknown variables. (This does not affect\n      referencing variables that are explicitly set to undef).\n    EOT\n    },\n  :tasks => {\n    :default => false,\n    :type => :boolean,\n    :desc => <<-'EOT'\n      Turns on experimental support for tasks and plans in the puppet language. This is for internal API use only.\n      Do not change this setting.\n    EOT\n    }\n  )\n  settings.define_settings(:puppetdoc,\n    :document_all => {\n        :default  => false,\n        :type     => :boolean,\n        :desc     => \"Whether to document all resources when using `puppet doc` to\n          generate manifest documentation.\",\n    }\n  )\n\n  settings.define_settings(\n    :main,\n    :rich_data => {\n      :default  => true,\n      :type     => :boolean,\n      :desc     => <<-'EOT'\n        Enables having extended data in the catalog by storing them as a hash with the special key\n        `__ptype`. When enabled, resource containing values of the data types `Binary`, `Regexp`,\n        `SemVer`, `SemVerRange`, `Timespan` and `Timestamp`, as well as instances of types derived\n        from `Object` retain their data type.\n      EOT\n    }\n  )\n  end\nend\n"
  },
  {
    "path": "lib/puppet/environments.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/concurrent/synchronized'\n\n# @api private\nmodule Puppet::Environments\n  class EnvironmentNotFound < Puppet::Error\n    def initialize(environment_name, original = nil)\n      environmentpath = Puppet[:environmentpath]\n      super(\"Could not find a directory environment named '#{environment_name}' anywhere in the path: #{environmentpath}. Does the directory exist?\", original)\n    end\n  end\n\n  # @api private\n  module EnvironmentCreator\n    # Create an anonymous environment.\n    #\n    # @param module_path [String] A list of module directories separated by the\n    #   PATH_SEPARATOR\n    # @param manifest [String] The path to the manifest\n    # @return A new environment with the `name` `:anonymous`\n    #\n    # @api private\n    def for(module_path, manifest)\n      Puppet::Node::Environment.create(:anonymous,\n                                       module_path.split(File::PATH_SEPARATOR),\n                                       manifest)\n    end\n  end\n\n  # Provide any common methods that loaders should have. It requires that any\n  # classes that include this module implement get\n  # @api private\n  module EnvironmentLoader\n    # @!macro loader_get_or_fail\n    def get!(name)\n      environment = get(name)\n      environment || raise(EnvironmentNotFound, name)\n    end\n\n    def clear_all\n      root = Puppet.lookup(:root_environment) { nil }\n      unless root.nil?\n        root.instance_variable_set(:@static_catalogs, nil)\n        root.instance_variable_set(:@rich_data, nil)\n      end\n    end\n\n    # The base implementation is a noop, because `get` returns a new environment\n    # each time.\n    #\n    # @see Puppet::Environments::Cached#guard\n    def guard(name); end\n    def unguard(name); end\n  end\n\n  # @!macro [new] loader_search_paths\n  #   A list of indicators of where the loader is getting its environments from.\n  #   @return [Array<String>] The URIs of the load locations\n  #\n  # @!macro [new] loader_list\n  #   @return [Array<Puppet::Node::Environment>] All of the environments known\n  #     to the loader\n  #\n  # @!macro [new] loader_get\n  #   Find a named environment\n  #\n  #   @param name [String,Symbol] The name of environment to find\n  #   @return [Puppet::Node::Environment, nil] the requested environment or nil\n  #     if it wasn't found\n  #\n  # @!macro [new] loader_get_conf\n  #   Attempt to obtain the initial configuration for the environment.  Not all\n  #   loaders can provide this.\n  #\n  #   @param name [String,Symbol] The name of the environment whose configuration\n  #     we are looking up\n  #   @return [Puppet::Setting::EnvironmentConf, nil] the configuration for the\n  #     requested environment, or nil if not found or no configuration is available\n  #\n  # @!macro [new] loader_get_or_fail\n  #   Find a named environment or raise\n  #   Puppet::Environments::EnvironmentNotFound when the named environment is\n  #   does not exist.\n  #\n  #   @param name [String,Symbol] The name of environment to find\n  #   @return [Puppet::Node::Environment] the requested environment\n\n  # A source of pre-defined environments.\n  #\n  # @api private\n  class Static\n    include EnvironmentCreator\n    include EnvironmentLoader\n\n    def initialize(*environments)\n      @environments = environments\n    end\n\n    # @!macro loader_search_paths\n    def search_paths\n      [\"data:text/plain,internal\"]\n    end\n\n    # @!macro loader_list\n    def list\n      @environments\n    end\n\n    # @!macro loader_get\n    def get(name)\n      @environments.find do |env|\n        env.name == name.intern\n      end\n    end\n\n    # Returns a basic environment configuration object tied to the environment's\n    # implementation values.  Will not interpolate.\n    #\n    # @!macro loader_get_conf\n    def get_conf(name)\n      env = get(name)\n      if env\n        Puppet::Settings::EnvironmentConf.static_for(env, Puppet[:environment_timeout], Puppet[:static_catalogs], Puppet[:rich_data])\n      else\n        nil\n      end\n    end\n  end\n\n  # A source of unlisted pre-defined environments.\n  #\n  # Used only for internal bootstrapping environments which are not relevant\n  # to an end user (such as the fall back 'configured' environment).\n  #\n  # @api private\n  class StaticPrivate < Static\n    # Unlisted\n    #\n    # @!macro loader_list\n    def list\n      []\n    end\n  end\n\n  class StaticDirectory < Static\n    # Accepts a single environment in the given directory having the given name (not required to be reflected as the name\n    # of the directory)\n    def initialize(env_name, env_dir, environment)\n      super(environment)\n      @env_dir = env_dir\n      @env_name = env_name.intern\n    end\n\n    # @!macro loader_get_conf\n    def get_conf(name)\n      return nil unless name.intern == @env_name\n\n      Puppet::Settings::EnvironmentConf.load_from(@env_dir, [])\n    end\n  end\n\n  # Reads environments from a directory on disk. Each environment is\n  # represented as a sub-directory. The environment's manifest setting is the\n  # `manifest` directory of the environment directory. The environment's\n  # modulepath setting is the global modulepath (from the `[server]` section\n  # for the server) prepended with the `modules` directory of the environment\n  # directory.\n  #\n  # @api private\n  class Directories\n    include EnvironmentLoader\n\n    def initialize(environment_dir, global_module_path)\n      @environment_dir =  Puppet::FileSystem.expand_path(environment_dir)\n      @global_module_path = global_module_path ?\n        global_module_path.map { |p| Puppet::FileSystem.expand_path(p) } :\n        nil\n    end\n\n    # Generate an array of directory loaders from a path string.\n    # @param path [String] path to environment directories\n    # @param global_module_path [Array<String>] the global modulepath setting\n    # @return [Array<Puppet::Environments::Directories>] An array\n    #   of configured directory loaders.\n    def self.from_path(path, global_module_path)\n      environments = path.split(File::PATH_SEPARATOR)\n      environments.map do |dir|\n        Puppet::Environments::Directories.new(dir, global_module_path)\n      end\n    end\n\n    def self.real_path(dir)\n      if Puppet::FileSystem.symlink?(dir) && Puppet[:versioned_environment_dirs]\n        dir = Pathname.new Puppet::FileSystem.expand_path(Puppet::FileSystem.readlink(dir))\n      end\n      dir\n    end\n\n    # @!macro loader_search_paths\n    def search_paths\n      [\"file://#{@environment_dir}\"]\n    end\n\n    # @!macro loader_list\n    def list\n      valid_environment_names.collect do |name|\n        create_environment(name)\n      end\n    end\n\n    # @!macro loader_get\n    def get(name)\n      if validated_directory(File.join(@environment_dir, name.to_s))\n        create_environment(name)\n      end\n    end\n\n    # @!macro loader_get_conf\n    def get_conf(name)\n      envdir = validated_directory(File.join(@environment_dir, name.to_s))\n      if envdir\n        Puppet::Settings::EnvironmentConf.load_from(envdir, @global_module_path)\n      else\n        nil\n      end\n    end\n\n    private\n\n    def create_environment(name)\n      # interpolated modulepaths may be cached from prior environment instances\n      Puppet.settings.clear_environment_settings(name)\n\n      env_symbol = name.intern\n      setting_values = Puppet.settings.values(env_symbol, Puppet.settings.preferred_run_mode)\n      env = Puppet::Node::Environment.create(\n        env_symbol,\n        Puppet::Node::Environment.split_path(setting_values.interpolate(:modulepath)),\n        setting_values.interpolate(:manifest),\n        setting_values.interpolate(:config_version)\n      )\n\n      configured_path = File.join(@environment_dir, name.to_s)\n      env.configured_path = configured_path\n      if Puppet.settings[:report_configured_environmentpath]\n        env.resolved_path = validated_directory(configured_path)\n      else\n        env.resolved_path = configured_path\n      end\n\n      env\n    end\n\n    def validated_directory(envdir)\n      env_name = Puppet::FileSystem.basename_string(envdir)\n      envdir = Puppet::Environments::Directories.real_path(envdir).to_s\n      if Puppet::FileSystem.directory?(envdir) && Puppet::Node::Environment.valid_name?(env_name)\n        envdir\n      else\n        nil\n      end\n    end\n\n    def valid_environment_names\n      return [] unless Puppet::FileSystem.directory?(@environment_dir)\n\n      Puppet::FileSystem.children(@environment_dir).filter_map do |child|\n        Puppet::FileSystem.basename_string(child).intern if validated_directory(child)\n      end\n    end\n  end\n\n  # Combine together multiple loaders to act as one.\n  # @api private\n  class Combined\n    include EnvironmentLoader\n\n    def initialize(*loaders)\n      @loaders = loaders\n    end\n\n    # @!macro loader_search_paths\n    def search_paths\n      @loaders.collect(&:search_paths).flatten\n    end\n\n    # @!macro loader_list\n    def list\n      @loaders.collect(&:list).flatten\n    end\n\n    # @!macro loader_get\n    def get(name)\n      @loaders.each do |loader|\n        env = loader.get(name)\n        if env\n          return env\n        end\n      end\n      nil\n    end\n\n    # @!macro loader_get_conf\n    def get_conf(name)\n      @loaders.each do |loader|\n        conf = loader.get_conf(name)\n        if conf\n          return conf\n        end\n      end\n      nil\n    end\n\n    def clear_all\n      @loaders.each(&:clear_all)\n    end\n  end\n\n  class Cached\n    include EnvironmentLoader\n    include Puppet::Concurrent::Synchronized\n\n    class DefaultCacheExpirationService\n      # Called when the environment is created.\n      #\n      # @param [Puppet::Node::Environment] env\n      def created(env)\n      end\n\n      # Is the environment with this name expired?\n      #\n      # @param [Symbol] env_name The symbolic environment name\n      # @return [Boolean]\n      def expired?(env_name)\n        false\n      end\n\n      # The environment with this name was evicted.\n      #\n      # @param [Symbol] env_name The symbolic environment name\n      def evicted(env_name)\n      end\n    end\n\n    def self.cache_expiration_service=(service)\n      @cache_expiration_service_singleton = service\n    end\n\n    def self.cache_expiration_service\n      @cache_expiration_service_singleton || DefaultCacheExpirationService.new\n    end\n\n    def initialize(loader)\n      @loader = loader\n      @cache_expiration_service = Puppet::Environments::Cached.cache_expiration_service\n      @cache = {}\n    end\n\n    # @!macro loader_list\n    def list\n      # Evict all that have expired, in the same way as `get`\n      clear_all_expired\n\n      # Evict all that was removed from disk\n      cached_envs = @cache.keys.map!(&:to_sym)\n      loader_envs = @loader.list.map!(&:name)\n      removed_envs = cached_envs - loader_envs\n\n      removed_envs.each do |env_name|\n        Puppet.debug { \"Environment no longer exists '#{env_name}'\" }\n        clear(env_name)\n      end\n\n      @loader.list.map do |env|\n        name = env.name\n        old_entry = @cache[name]\n        if old_entry\n          old_entry.value\n        else\n          add_entry(name, entry(env))\n          env\n        end\n      end\n    end\n\n    # @!macro loader_search_paths\n    def search_paths\n      @loader.search_paths\n    end\n\n    # @!macro loader_get\n    def get(name)\n      entry = get_entry(name)\n      entry ? entry.value : nil\n    end\n\n    # Get a cache entry for an envionment. It returns nil if the\n    # environment doesn't exist.\n    def get_entry(name, check_expired = true)\n      # Aggressively evict all that has expired\n      # This strategy favors smaller memory footprint over environment\n      # retrieval time.\n      clear_all_expired if check_expired\n      name = name.to_sym\n      entry = @cache[name]\n      if entry\n        Puppet.debug { \"Found in cache #{name.inspect} #{entry.label}\" }\n        # found in cache\n        entry.touch\n      elsif (env = @loader.get(name))\n        # environment loaded, cache it\n        entry = entry(env)\n        add_entry(name, entry)\n      end\n      entry\n    end\n    private :get_entry\n\n    # Adds a cache entry to the cache\n    def add_entry(name, cache_entry)\n      Puppet.debug { \"Caching environment #{name.inspect} #{cache_entry.label}\" }\n      @cache[name] = cache_entry\n      @cache_expiration_service.created(cache_entry.value)\n    end\n    private :add_entry\n\n    def clear_entry(name, entry)\n      @cache.delete(name)\n      Puppet.debug { \"Evicting cache entry for environment #{name.inspect}\" }\n      @cache_expiration_service.evicted(name.to_sym)\n      Puppet::GettextConfig.delete_text_domain(name)\n      Puppet.settings.clear_environment_settings(name)\n    end\n    private :clear_entry\n\n    # Clears the cache of the environment with the given name.\n    # (The intention is that this could be used from a MANUAL cache eviction command (TBD)\n    def clear(name)\n      name = name.to_sym\n      entry = @cache[name]\n      clear_entry(name, entry) if entry\n    end\n\n    # Clears all cached environments.\n    # (The intention is that this could be used from a MANUAL cache eviction command (TBD)\n    def clear_all\n      super\n\n      @cache.each_pair do |name, entry|\n        clear_entry(name, entry)\n      end\n\n      @cache = {}\n      Puppet::GettextConfig.delete_environment_text_domains\n    end\n\n    # Clears all environments that have expired, either by exceeding their time to live, or\n    # through an explicit eviction determined by the cache expiration service.\n    #\n    def clear_all_expired\n      t = Time.now\n\n      @cache.each_pair do |name, entry|\n        clear_if_expired(name, entry, t)\n      end\n    end\n    private :clear_all_expired\n\n    # Clear an environment if it is expired, either by exceeding its time to live, or\n    # through an explicit eviction determined by the cache expiration service.\n    #\n    def clear_if_expired(name, entry, t = Time.now)\n      return unless entry\n      return if entry.guarded?\n\n      if entry.expired?(t) || @cache_expiration_service.expired?(name.to_sym)\n        clear_entry(name, entry)\n      end\n    end\n    private :clear_if_expired\n\n    # This implementation evicts the cache, and always gets the current\n    # configuration of the environment\n    #\n    # TODO: While this is wasteful since it\n    # needs to go on a search for the conf, it is too disruptive to optimize\n    # this.\n    #\n    # @!macro loader_get_conf\n    def get_conf(name)\n      name = name.to_sym\n      clear_if_expired(name, @cache[name])\n      @loader.get_conf(name)\n    end\n\n    # Guard an environment so it can't be evicted while it's in use. The method\n    # may be called multiple times, provided it is unguarded the same number of\n    # times. If you call this method, you must call `unguard` in an ensure block.\n    def guard(name)\n      entry = get_entry(name, false)\n      entry.guard if entry\n    end\n\n    # Unguard an environment.\n    def unguard(name)\n      entry = get_entry(name, false)\n      entry.unguard if entry\n    end\n\n    # Creates a suitable cache entry given the time to live for one environment\n    #\n    def entry(env)\n      ttl = if (conf = get_conf(env.name))\n              conf.environment_timeout\n            else\n              Puppet[:environment_timeout]\n            end\n\n      case ttl\n      when 0\n        NotCachedEntry.new(env)     # Entry that is always expired (avoids syscall to get time)\n      when Float::INFINITY\n        Entry.new(env)              # Entry that never expires (avoids syscall to get time)\n      else\n        MRUEntry.new(env, ttl)      # Entry that expires in ttl from when it was last touched\n      end\n    end\n\n    # Never evicting entry\n    class Entry\n      attr_reader :value\n\n      def initialize(value)\n        @value = value\n        @guards = 0\n      end\n\n      def touch\n      end\n\n      def expired?(now)\n        false\n      end\n\n      def label\n        \"\"\n      end\n\n      # These are not protected with a lock, because all of the Cached\n      # methods are protected.\n      def guarded?\n        @guards > 0\n      end\n\n      def guard\n        @guards += 1\n      end\n\n      def unguard\n        @guards -= 1\n      end\n    end\n\n    # Always evicting entry\n    class NotCachedEntry < Entry\n      def expired?(now)\n        true\n      end\n\n      def label\n        \"(ttl = 0 sec)\"\n      end\n    end\n\n    # Policy that expires if it hasn't been touched within ttl_seconds\n    class MRUEntry < Entry\n      def initialize(value, ttl_seconds)\n        super(value)\n        @ttl = Time.now + ttl_seconds\n        @ttl_seconds = ttl_seconds\n\n        touch\n      end\n\n      def touch\n        @ttl = Time.now + @ttl_seconds\n      end\n\n      def expired?(now)\n        now > @ttl\n      end\n\n      def label\n        \"(ttl = #{@ttl_seconds} sec)\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/error.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  # The base class for all Puppet errors. It can wrap another exception\n  class Error < RuntimeError\n    attr_accessor :original\n\n    def initialize(message, original = nil)\n      super(message.scrub)\n      @original = original\n    end\n  end\n\n  module ExternalFileError\n    # This module implements logging with a filename and line number. Use this\n    # for errors that need to report a location in a non-ruby file that we\n    # parse.\n    attr_accessor :line, :file, :pos, :puppetstack\n\n    # May be called with 3 arguments for message, file, line, and exception, or\n    # 4 args including the position on the line.\n    #\n    def initialize(message, file = nil, line = nil, pos = nil, original = nil)\n      if pos.is_a? Exception\n        original = pos\n        pos = nil\n      end\n\n      super(message, original)\n\n      @file = file unless file.is_a?(String) && file.empty?\n      @line = line\n      @pos = pos\n\n      if original && original.respond_to?(:puppetstack)\n        @puppetstack = original.puppetstack\n      else\n        @puppetstack = Puppet::Pops::PuppetStack.stacktrace()\n      end\n    end\n\n    def to_s\n      msg = super\n      @file = nil if @file.is_a?(String) && @file.empty?\n      msg += Puppet::Util::Errors.error_location_with_space(@file, @line, @pos)\n      msg\n    end\n  end\n\n  class ParseError < Puppet::Error\n    include ExternalFileError\n  end\n\n  class ResourceError < Puppet::Error\n    include ExternalFileError\n  end\n\n  # Contains an issue code and can be annotated with an environment and a node\n  class ParseErrorWithIssue < Puppet::ParseError\n    attr_reader :issue_code, :basic_message, :arguments\n    attr_accessor :environment, :node\n\n    # @param message [String] The error message\n    # @param file [String] The path to the file where the error was found\n    # @param line [Integer] The line in the file\n    # @param pos [Integer] The position on the line\n    # @param original [Exception] Original exception\n    # @param issue_code [Symbol] The issue code\n    # @param arguments [Hash{Symbol=>Object}] Issue arguments\n    #\n    def initialize(message, file = nil, line = nil, pos = nil, original = nil, issue_code = nil, arguments = nil)\n      super(message, file, line, pos, original)\n      @issue_code = issue_code\n      @basic_message = message\n      @arguments = arguments\n    end\n\n    def to_s\n      msg = super\n      msg = _(\"Could not parse for environment %{environment}: %{message}\") % { environment: environment, message: msg } if environment\n      msg = _(\"%{message} on node %{node}\") % { message: msg, node: node } if node\n      msg\n    end\n\n    def to_h\n      {\n        :issue_code => issue_code,\n        :message => basic_message,\n        :full_message => to_s,\n        :file => file,\n        :line => line,\n        :pos => pos,\n        :environment => environment.to_s,\n        :node => node.to_s,\n      }\n    end\n\n    def self.from_issue_and_stack(issue, args = {})\n      filename, line = Puppet::Pops::PuppetStack.top_of_stack\n\n      new(\n        issue.format(args),\n        filename,\n        line,\n        nil,\n        nil,\n        issue.issue_code,\n        args\n      )\n    end\n  end\n\n  # An error that already contains location information in the message text\n  class PreformattedError < Puppet::ParseErrorWithIssue\n  end\n\n  # An error class for when I don't know what happened.  Automatically\n  # prints a stack trace when in debug mode.\n  class DevError < Puppet::Error\n    include ExternalFileError\n  end\n\n  class MissingCommand < Puppet::Error\n  end\n\n  # Raised when we failed to acquire a lock\n  class LockError < Puppet::Error\n  end\n\n  # An Error suitable for raising an error with details in a Puppet::Datatypes::Error\n  # that can be used as a value in the Puppet Language\n  #\n  class ErrorWithData < Puppet::Error\n    include ExternalFileError\n    attr_reader :error_data\n\n    def initialize(error_data, message, file: nil, line: nil, pos: nil, original: nil)\n      super(message, file, line, pos, original)\n      @error_data = error_data\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/etc.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/util/character_encoding'\n# Wrapper around Ruby Etc module allowing us to manage encoding in a single\n# place.\n# This represents a subset of Ruby's Etc module, only the methods required by Puppet.\n\n# On Ruby 2.1.0 and later, Etc returns strings in variable encoding depending on\n# environment. The string returned will be labeled with the environment's\n# encoding (Encoding.default_external), with one exception: If the environment\n# encoding is 7-bit ASCII, and any individual character bit representation is\n# equal to or greater than 128 - \\x80 - 0b10000000 - signifying the smallest\n# 8-bit big-endian value, the returned string will be in BINARY encoding instead\n# of environment encoding.\n#\n# Barring that exception, the returned string will be labeled as encoding\n# Encoding.default_external, regardless of validity or byte-width. For example,\n# ruby will label a string containing a four-byte characters such as \"\\u{2070E}\"\n# as EUC_KR even though EUC_KR is a two-byte width encoding.\n#\n# On Ruby 2.0.x and earlier, Etc will always return string values in BINARY,\n# ignoring encoding altogether.\n#\n# For Puppet we specifically want UTF-8 as our input from the Etc module - which\n# is our input for many resource instance 'is' values. The associated 'should'\n# value will basically always be coming from Puppet in UTF-8 - and written to\n# disk as UTF-8. Etc is defined for Windows but the majority calls to it return\n# nil and Puppet does not use it.\n#\n# That being said, we have cause to retain the original, pre-override string\n# values. `puppet resource user`\n# (Puppet::Resource::User.indirection.search('User', {})) uses self.instances to\n# query for user(s) and then iterates over the results of that query again to\n# obtain state for each user. If we've overridden the original user name and not\n# retained the original, we've lost the ability to query the system for it\n# later. Hence the Puppet::Etc::Passwd and Puppet::Etc::Group structs.\n#\n# We only use Etc for retrieving existing property values from the system. For\n# setting property values, providers leverage system tools (i.e., `useradd`)\n#\n# @api private\nmodule Puppet::Etc\n  class << self\n    # Etc::getgrent returns an Etc::Group struct object\n    # On first call opens /etc/group and returns parse of first entry. Each subsquent call\n    # returns new struct the next entry or nil if EOF. Call ::endgrent to close file.\n    def getgrent\n      override_field_values_to_utf8(::Etc.getgrent)\n    end\n\n    # closes handle to /etc/group file\n    def endgrent\n      ::Etc.endgrent\n    end\n\n    # effectively equivalent to IO#rewind of /etc/group\n    def setgrent\n      ::Etc.setgrent\n    end\n\n    # Etc::getpwent returns an Etc::Passwd struct object\n    # On first call opens /etc/passwd and returns parse of first entry. Each subsquent call\n    # returns new struct for the next entry or nil if EOF. Call ::endgrent to close file.\n    def getpwent\n      override_field_values_to_utf8(::Etc.getpwent)\n    end\n\n    # closes handle to /etc/passwd file\n    def endpwent\n      ::Etc.endpwent\n    end\n\n    # effectively equivalent to IO#rewind of /etc/passwd\n    def setpwent\n      ::Etc.setpwent\n    end\n\n    # Etc::getpwnam searches /etc/passwd file for an entry corresponding to\n    # username.\n    # returns an Etc::Passwd struct corresponding to the entry or raises\n    # ArgumentError if none\n    def getpwnam(username)\n      override_field_values_to_utf8(::Etc.getpwnam(username))\n    end\n\n    # Etc::getgrnam searches /etc/group file for an entry corresponding to groupname.\n    # returns an Etc::Group struct corresponding to the entry or raises\n    # ArgumentError if none\n    def getgrnam(groupname)\n      override_field_values_to_utf8(::Etc.getgrnam(groupname))\n    end\n\n    # Etc::getgrid searches /etc/group file for an entry corresponding to id.\n    # returns an Etc::Group struct corresponding to the entry or raises\n    # ArgumentError if none\n    def getgrgid(id)\n      override_field_values_to_utf8(::Etc.getgrgid(id))\n    end\n\n    # Etc::getpwuid searches /etc/passwd file for an entry corresponding to id.\n    # returns an Etc::Passwd struct corresponding to the entry or raises\n    # ArgumentError if none\n    def getpwuid(id)\n      override_field_values_to_utf8(::Etc.getpwuid(id))\n    end\n\n    # Etc::group returns a Ruby iterator that executes a block for\n    # each entry in the /etc/group file. The code-block is passed\n    # a Group struct. See getgrent above for more details.\n    def group\n      # The implementation here duplicates the logic in https://github.com/ruby/etc/blob/master/ext/etc/etc.c#L523-L537\n      # Note that we do not call ::Etc.group directly, because we\n      # want to use our wrappers for methods like getgrent, setgrent,\n      # endgrent, etc.\n      return getgrent unless block_given?\n\n      setgrent\n      begin\n        while cur_group = getgrent # rubocop:disable Lint/AssignmentInCondition\n          yield cur_group\n        end\n      ensure\n        endgrent\n      end\n    end\n\n    private\n\n    # @api private\n    # Defines Puppet::Etc::Passwd struct class. Contains all of the original\n    # member fields of Etc::Passwd, and additional \"canonical_\" versions of\n    # these fields as well. API compatible with Etc::Passwd. Because Struct.new\n    # defines a new Class object, we memoize to avoid superfluous extra Class\n    # instantiations.\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    def puppet_etc_passwd_class\n      @password_class ||= Struct.new(*Etc::Passwd.members, *Etc::Passwd.members.map { |member| \"canonical_#{member}\".to_sym })\n    end\n\n    # @api private\n    # Defines Puppet::Etc::Group struct class. Contains all of the original\n    # member fields of Etc::Group, and additional \"canonical_\" versions of these\n    # fields as well. API compatible with Etc::Group. Because Struct.new\n    # defines a new Class object, we memoize to avoid superfluous extra Class\n    # instantiations.\n    def puppet_etc_group_class\n      @group_class ||= Struct.new(*Etc::Group.members, *Etc::Group.members.map { |member| \"canonical_#{member}\".to_sym })\n    end\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n\n    # Utility method for overriding the String values of a struct returned by\n    # the Etc module to UTF-8. Structs returned by the ruby Etc module contain\n    # members with fields of type String, Integer, or Array of Strings, so we\n    # handle these types. Otherwise ignore fields.\n    #\n    # @api private\n    # @param [Etc::Passwd or Etc::Group struct]\n    # @return [Puppet::Etc::Passwd or Puppet::Etc::Group struct] a new struct\n    #   object with the original struct values overridden to UTF-8, if valid. For\n    #   invalid values originating in UTF-8, invalid characters are replaced with\n    #   '?'. For each member the struct also contains a corresponding\n    #   :canonical_<member name> struct member.\n    def override_field_values_to_utf8(struct)\n      return nil if struct.nil?\n\n      new_struct = struct.is_a?(Etc::Passwd) ? puppet_etc_passwd_class.new : puppet_etc_group_class.new\n      struct.each_pair do |member, value|\n        case value\n        when String\n          new_struct[\"canonical_#{member}\".to_sym] = value.dup\n          new_struct[member] = Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(value).scrub\n        when Array\n          new_struct[\"canonical_#{member}\".to_sym] = value.inject([]) { |acc, elem| acc << elem.dup }\n          new_struct[member] = value.inject([]) do |acc, elem|\n            acc << Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(elem).scrub\n          end\n        else\n          new_struct[\"canonical_#{member}\".to_sym] = value\n          new_struct[member] = value\n        end\n      end\n      new_struct\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/external/dot.rb",
    "content": "# frozen_string_literal: true\n\n# rdot.rb\n#\n#\n# This is a modified version of dot.rb from Dave Thomas's rdoc project.  I [Horst Duchene]\n# renamed it to rdot.rb to avoid collision with an installed rdoc/dot.\n#\n# It also supports undirected edges.\n\nmodule DOT\n  # These global vars are used to make nice graph source.\n\n  $tab  = '    '\n  $tab2 = $tab * 2\n\n  # if we don't like 4 spaces, we can change it any time\n\n  def change_tab(t)\n    $tab  = t\n    $tab2 = t * 2\n  end\n\n  # options for node declaration\n\n  NODE_OPTS = [\n    # attributes due to\n    # http://www.graphviz.org/Documentation/dotguide.pdf\n    # March, 26, 2005\n    'bottomlabel', # auxiliary label for nodes of shape M*\n    'color',       # default: black; node shape color\n    'comment',     # any string (format-dependent)\n    'distortion',  # default: 0.0; node distortion for shape=polygon\n    'fillcolor',   # default: lightgrey/black; node fill color\n    'fixedsize',   # default: false; label text has no affect on node size\n    'fontcolor',   # default: black; type face color\n    'fontname',    # default: Times-Roman; font family\n    'fontsize',    # default: 14; point size of label\n    'group',       # name of node's group\n    'height',      # default: .5; height in inches\n    'label',       # default: node name; any string\n    'layer',       # default: overlay range; all, id or id:id\n    'orientation', # default: 0.0; node rotation angle\n    'peripheries', # shape-dependent number of node boundaries\n    'regular',     # default: false; force polygon to be regular\n    'shape',       # default: ellipse; node shape; see Section 2.1 and Appendix E\n    'shapefile',   # external EPSF or SVG custom shape file\n    'sides',       # default: 4; number of sides for shape=polygon\n    'skew',        # default: 0.0; skewing of node for shape=polygon\n    'style',       # graphics options, e.g. bold, dotted, filled; cf. Section 2.3\n    'toplabel',    # auxiliary label for nodes of shape M*\n    'URL',         # URL associated with node (format-dependent)\n    'width',       # default: .75; width in inches\n    'z',           # default: 0.0; z coordinate for VRML output\n\n    # maintained for backward compatibility or rdot internal\n    'bgcolor',\n    'rank'\n  ]\n\n  # options for edge declaration\n\n  EDGE_OPTS = [\n    'arrowhead',      # default: normal; style of arrowhead at head end\n    'arrowsize',      # default: 1.0; scaling factor for arrowheads\n    'arrowtail',      # default: normal; style of arrowhead at tail end\n    'color',          # default: black; edge stroke color\n    'comment',        # any string (format-dependent)\n    'constraint',     # default: true use edge to affect node ranking\n    'decorate',       # if set, draws a line connecting labels with their edges\n    'dir',            # default: forward; forward, back, both, or none\n    'fontcolor',      # default: black type face color\n    'fontname',       # default: Times-Roman; font family\n    'fontsize',       # default: 14; point size of label\n    'headlabel',      # label placed near head of edge\n    'headport',       # n,ne,e,se,s,sw,w,nw\n    'headURL',        # URL attached to head label if output format is ismap\n    'label',          # edge label\n    'labelangle',     # default: -25.0; angle in degrees which head or tail label is rotated off edge\n    'labeldistance',  # default: 1.0; scaling factor for distance of head or tail label from node\n    'labelfloat',     # default: false; lessen constraints on edge label placement\n    'labelfontcolor', # default: black; type face color for head and tail labels\n    'labelfontname',  # default: Times-Roman; font family for head and tail labels\n    'labelfontsize',  # default: 14 point size for head and tail labels\n    'layer',          # default: overlay range; all, id or id:id\n    'lhead',          # name of cluster to use as head of edge\n    'ltail',          # name of cluster to use as tail of edge\n    'minlen',         # default: 1 minimum rank distance between head and tail\n    'samehead',       # tag for head node; edge heads with the same tag are merged onto the same port\n    'sametail',       # tag for tail node; edge tails with the same tag are merged onto the same port\n    'style',          # graphics options, e.g. bold, dotted, filled; cf. Section 2.3\n    'taillabel',      # label placed near tail of edge\n    'tailport',       # n,ne,e,se,s,sw,w,nw\n    'tailURL',        # URL attached to tail label if output format is ismap\n    'weight',         # default: 1; integer cost of stretching an edge\n\n    # maintained for backward compatibility or rdot internal\n    'id'\n  ]\n\n  # options for graph declaration\n\n  GRAPH_OPTS = %w[\n    bgcolor\n    center clusterrank color concentrate\n    fontcolor fontname fontsize\n    label layerseq\n    margin mclimit\n    nodesep nslimit\n    ordering orientation\n    page\n    rank rankdir ranksep ratio\n    size\n  ]\n\n  # a root class for any element in dot notation\n\n  class DOTSimpleElement\n    attr_accessor :name\n\n    def initialize(params = {})\n      @label = params['name'] || ''\n    end\n\n    def to_s\n      @name\n    end\n  end\n\n  # an element that has options ( node, edge, or graph )\n\n  class DOTElement < DOTSimpleElement\n    # attr_reader :parent\n    attr_accessor :name, :options\n\n    def initialize(params = {}, option_list = [])\n      super(params)\n      @name   = params['name']   || nil\n      @parent = params['parent'] || nil\n      @options = {}\n      option_list.each { |i|\n        @options[i] = params[i] if params[i]\n      }\n      @options['label'] ||= @name if @name != 'node'\n    end\n\n    def each_option\n      @options.each { |i| yield i }\n    end\n\n    def each_option_pair\n      @options.each_pair { |key, val| yield key, val }\n    end\n  end\n\n  # This is used when we build nodes that have shape=record\n  # ports don't have options :)\n\n  class DOTPort < DOTSimpleElement\n    attr_accessor :label\n\n    def initialize(params = {})\n      super(params)\n      @name = params['label'] || ''\n    end\n\n    def to_s\n      (@name && @name != \"\" ? \"<#{@name}>\" : \"\") + @label.to_s\n    end\n  end\n\n  # node element\n\n  class DOTNode < DOTElement\n    def initialize(params = {}, option_list = NODE_OPTS)\n      super(params, option_list)\n      @ports = params['ports'] || []\n    end\n\n    def each_port\n      @ports.each { |i| yield i }\n    end\n\n    def <<(thing)\n      @ports << thing\n    end\n\n    def push(thing)\n      @ports.push(thing)\n    end\n\n    def pop\n      @ports.pop\n    end\n\n    def to_s(t = '')\n      # This code is totally incomprehensible; it needs to be replaced!\n\n      label = if @options['shape'] != 'record' && @ports.length == 0\n                if @options['label']\n                  t + $tab + \"label = #{stringify(@options['label'])}\\n\"\n                else\n                  ''\n                end\n              else\n                t + $tab + 'label = \"' + \" \\\\\\n\" +\n                  t + $tab2 + \"#{stringify(@options['label'])}| \\\\\\n\" +\n                  @ports.collect { |i|\n                    t + $tab2 + i.to_s\n                  }.join(\"| \\\\\\n\") + \" \\\\\\n\" +\n                  t + $tab + '\"' + \"\\n\"\n              end\n\n      t + \"#{@name} [\\n\" +\n        @options.to_a.filter_map { |i|\n          i[1] && i[0] != 'label' ?\n            t + $tab + \"#{i[0]} = #{i[1]}\" : nil\n        }.join(\",\\n\") + (label != '' ? \",\\n\" : \"\\n\") +\n        label +\n        t + \"]\\n\"\n    end\n\n    private\n\n    def stringify(s)\n      %(\"#{s.gsub('\"', '\\\\\"')}\")\n    end\n  end\n\n  # A subgraph element is the same to graph, but has another header in dot\n  # notation.\n\n  class DOTSubgraph < DOTElement\n    def initialize(params = {}, option_list = GRAPH_OPTS)\n      super(params, option_list)\n      @nodes      = params['nodes'] || []\n      @dot_string = 'graph'\n    end\n\n    def each_node\n      @nodes.each { |i| yield i }\n    end\n\n    def <<(thing)\n      @nodes << thing\n    end\n\n    def push(thing)\n      @nodes.push(thing)\n    end\n\n    def pop\n      @nodes.pop\n    end\n\n    def to_s(t = '')\n      hdr = t + \"#{@dot_string} #{@name} {\\n\"\n\n      options = @options.to_a.filter_map { |name, val|\n        if val && name != 'label'\n          t + $tab + \"#{name} = #{val}\"\n        else\n          name ? t + $tab + \"#{name} = \\\"#{val}\\\"\" : nil\n        end\n      }.join(\"\\n\") + \"\\n\"\n\n      nodes = @nodes.collect { |i|\n        i.to_s(t + $tab)\n      }.join(\"\\n\") + \"\\n\"\n      hdr + options + nodes + t + \"}\\n\"\n    end\n  end\n\n  # This is a graph.\n\n  class DOTDigraph < DOTSubgraph\n    def initialize(params = {}, option_list = GRAPH_OPTS)\n      super(params, option_list)\n      @dot_string = 'digraph'\n    end\n  end\n\n  # This is an edge.\n\n  class DOTEdge < DOTElement\n    attr_accessor :from, :to\n\n    def initialize(params = {}, option_list = EDGE_OPTS)\n      super(params, option_list)\n      @from = params['from'] || nil\n      @to   = params['to'] || nil\n    end\n\n    def edge_link\n      '--'\n    end\n\n    def to_s(t = '')\n      t + \"#{@from} #{edge_link} #{to} [\\n\" +\n        @options.to_a.filter_map { |i|\n          if i[1] && i[0] != 'label'\n            t + $tab + \"#{i[0]} = #{i[1]}\"\n          else\n            i[1] ? t + $tab + \"#{i[0]} = \\\"#{i[1]}\\\"\" : nil\n          end\n        }.join(\"\\n\") + \"\\n#{t}]\\n\"\n    end\n  end\n\n  class DOTDirectedEdge < DOTEdge\n    def edge_link\n      '->'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/catalog/select.rb",
    "content": "# frozen_string_literal: true\n\n# Select and show a list of resources of a given type.\nPuppet::Face.define(:catalog, '0.0.1') do\n  action :select do\n    summary _(\"Retrieve a catalog and filter it for resources of a given type.\")\n    arguments _(\"<host> <resource_type>\")\n    returns _(<<-'EOT')\n      A list of resource references (\"Type[title]\"). When used from the API,\n      returns an array of Puppet::Resource objects excised from a catalog.\n    EOT\n    description <<-'EOT'\n      Retrieves a catalog for the specified host, then searches it for all\n      resources of the requested type.\n    EOT\n    notes <<-'NOTES'\n      By default, this action will retrieve a catalog from Puppet's compiler\n      subsystem; you must call the action with `--terminus rest` if you wish\n      to retrieve a catalog from the puppet master.\n\n      FORMATTING ISSUES: This action cannot currently render useful yaml;\n      instead, it returns an entire catalog. Use json instead.\n    NOTES\n    examples <<-'EOT'\n      Ask the puppet master for a list of managed file resources for a node:\n\n      $ puppet catalog select --terminus rest somenode.magpie.lan file\n    EOT\n    when_invoked do |host, type, _options|\n      # REVISIT: Eventually, type should have a default value that triggers\n      # the non-specific behaviour.  For now, though, this will do.\n      # --daniel 2011-05-03\n      catalog = Puppet::Resource::Catalog.indirection.find(host)\n\n      if type == '*'\n        catalog.resources\n      else\n        type = type.downcase\n        catalog.resources.reject { |res| res.type.downcase != type }\n      end\n    end\n\n    when_rendering :console do |value|\n      if value.nil? then\n        _(\"no matching resources found\")\n      else\n        value.map(&:to_s).join(\"\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/catalog.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/face'\n\nPuppet::Indirector::Face.define(:catalog, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   \"Apache 2 license; see COPYING\"\n\n  summary _(\"Compile, save, view, and convert catalogs.\")\n  description <<-'EOT'\n    This subcommand deals with catalogs, which are compiled per-node artifacts\n    generated from a set of Puppet manifests. By default, it interacts with the\n    compiling subsystem and compiles a catalog using the default manifest and\n    `certname`, but you can change the source of the catalog with the\n    `--terminus` option. You can also choose to print any catalog in 'dot'\n    format (for easy graph viewing with OmniGraffle or Graphviz) with\n    '--render-as dot'.\n  EOT\n  short_description <<-'EOT'\n    This subcommand deals with catalogs, which are compiled per-node artifacts\n    generated from a set of Puppet manifests. By default, it interacts with the\n    compiling subsystem and compiles a catalog using the default manifest and\n    `certname`; use the `--terminus` option to change the source of the catalog.\n  EOT\n\n  deactivate_action(:destroy)\n  deactivate_action(:search)\n  action(:find) do\n    summary _(\"Retrieve the catalog for the node from which the command is run.\")\n    arguments \"<certname>, <facts>\"\n    option(\"--facts_for_catalog\") do\n      summary _(\"Not implemented for the CLI; facts are collected internally.\")\n    end\n    returns <<-'EOT'\n      A serialized catalog. When used from the Ruby API, returns a\n      Puppet::Resource::Catalog object.\n    EOT\n\n    when_invoked do |*args|\n      # Default the key to Puppet[:certname] if none is supplied\n      if args.length == 1\n        key = Puppet[:certname]\n      else\n        key = args.shift\n      end\n      call_indirection_method :find, key, args.first\n    end\n  end\n\n  action(:apply) do\n    summary \"Find and apply a catalog.\"\n    description <<-'EOT'\n      Finds and applies a catalog. This action takes no arguments, but\n      the source of the catalog can be managed with the `--terminus` option.\n    EOT\n    returns <<-'EOT'\n      Nothing. When used from the Ruby API, returns a\n      Puppet::Transaction::Report object.\n    EOT\n    examples <<-'EOT'\n      Apply the locally cached catalog:\n\n      $ puppet catalog apply --terminus yaml\n\n      Retrieve a catalog from the master and apply it, in one step:\n\n      $ puppet catalog apply --terminus rest\n\n      API example:\n\n          # ...\n          Puppet::Face[:catalog, '0.0.1'].download\n          # (Termini are singletons; catalog.download has a side effect of\n          # setting the catalog terminus to yaml)\n          report  = Puppet::Face[:catalog, '0.0.1'].apply\n          # ...\n    EOT\n\n    when_invoked do |_options|\n      catalog = Puppet::Face[:catalog, \"0.0.1\"].find(Puppet[:certname]) or raise \"Could not find catalog for #{Puppet[:certname]}\"\n      catalog = catalog.to_ral\n\n      report = Puppet::Transaction::Report.new\n      report.configuration_version = catalog.version\n      report.environment = Puppet[:environment]\n\n      Puppet::Util::Log.newdestination(report)\n\n      begin\n        benchmark(:notice, \"Finished catalog run in %{seconds} seconds\") do\n          catalog.apply(:report => report)\n        end\n      rescue => detail\n        Puppet.log_exception(detail, \"Failed to apply catalog: #{detail}\")\n      end\n\n      report.finalize_report\n      report\n    end\n  end\n\n  action(:compile) do\n    summary _(\"Compile a catalog.\")\n    description <<-'EOT'\n      Compiles a catalog locally for a node, requiring access to modules, node classifier, etc.\n    EOT\n    examples <<-'EOT'\n      Compile catalog for node 'mynode':\n\n      $ puppet catalog compile mynode --codedir ...\n    EOT\n    returns <<-'EOT'\n      A serialized catalog.\n    EOT\n    when_invoked do |*args|\n      Puppet.settings.preferred_run_mode = :server\n      Puppet::Face[:catalog, :current].find(*args)\n    end\n  end\n\n  action(:download) do\n    summary \"Download this node's catalog from the puppet master server.\"\n    description <<-'EOT'\n      Retrieves a catalog from the puppet master and saves it to the local yaml\n      cache. This action always contacts the puppet master and will ignore\n      alternate termini.\n\n      The saved catalog can be used in any subsequent catalog action by specifying\n      '--terminus yaml' for that action.\n    EOT\n    returns \"Nothing.\"\n    notes <<-'EOT'\n      When used from the Ruby API, this action has a side effect of leaving\n      Puppet::Resource::Catalog.indirection.terminus_class set to yaml. The\n      terminus must be explicitly re-set for subsequent catalog actions.\n    EOT\n    examples <<-'EOT'\n      Retrieve and store a catalog:\n\n      $ puppet catalog download\n\n      API example:\n\n          Puppet::Face[:plugin, '0.0.1'].download\n          Puppet::Face[:facts, '0.0.1'].upload\n          Puppet::Face[:catalog, '0.0.1'].download\n          # ...\n    EOT\n    when_invoked do |_options|\n      Puppet::Resource::Catalog.indirection.terminus_class = :rest\n      Puppet::Resource::Catalog.indirection.cache_class = nil\n      facts = Puppet::Face[:facts, '0.0.1'].find(Puppet[:certname])\n      catalog = nil\n      retrieval_duration = thinmark do\n        catalog = Puppet::Face[:catalog, '0.0.1'].find(Puppet[:certname],\n                                                       { facts_for_catalog: facts })\n      end\n      catalog.retrieval_duration = retrieval_duration\n      catalog.write_class_file\n\n      Puppet::Resource::Catalog.indirection.terminus_class = :yaml\n      Puppet::Face[:catalog, \"0.0.1\"].save(catalog)\n      Puppet.notice \"Saved catalog for #{Puppet[:certname]} to #{Puppet::Resource::Catalog.indirection.terminus.path(Puppet[:certname])}\"\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/config.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/settings/ini_file'\n\nPuppet::Face.define(:config, '0.0.1') do\n  extend Puppet::Util::Colors\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Interact with Puppet's settings.\")\n\n  description \"This subcommand can inspect and modify settings from Puppet's\n    'puppet.conf' configuration file. For documentation about individual settings,\n    see https://puppet.com/docs/puppet/latest/configuration.html.\"\n\n  DEFAULT_SECTION_MARKER = Object.new\n  DEFAULT_SECTION = \"main\"\n  option \"--section \" + _(\"SECTION_NAME\") do\n    default_to { DEFAULT_SECTION_MARKER } # Sentinel object for default detection during commands\n    summary _(\"The section of the configuration file to interact with.\")\n    description <<-EOT\n      The section of the puppet.conf configuration file to interact with.\n\n      The three most commonly used sections are 'main', 'server', and 'agent'.\n      'Main' is the default, and is used by all Puppet applications. Other\n      sections can override 'main' values for specific applications --- the\n      'server' section affects Puppet Server, and the 'agent'\n      section affects puppet agent.\n\n      Less commonly used is the 'user' section, which affects puppet apply. Any\n      other section will be treated as the name of a legacy environment\n      (a deprecated feature), and can only include the 'manifest' and\n      'modulepath' settings.\n    EOT\n  end\n\n  action(:print) do\n    summary _(\"Examine Puppet's current settings.\")\n    arguments _(\"all | <setting> [<setting> ...]\")\n    description <<-'EOT'\n      Prints the value of a single setting or a list of settings.\n\n      This action is a replacement interface to the information available with\n      `puppet <subcommand> --configprint`.\n    EOT\n    notes <<-'EOT'\n      By default, this action reads the general configuration in the 'main'\n      section. Use the '--section' and '--environment' flags to examine other\n      configuration domains.\n    EOT\n    examples <<-'EOT'\n      Get puppet's runfile directory:\n\n      $ puppet config print rundir\n\n      Get a list of important directories from the server's config:\n\n      $ puppet config print all --section server | grep -E \"(path|dir)\"\n    EOT\n\n    when_invoked do |*args|\n      options = args.pop\n\n      @default_section = false\n      if options[:section] == DEFAULT_SECTION_MARKER\n        options[:section] = DEFAULT_SECTION\n        @default_section = true\n      end\n\n      if Puppet::Util::Log.sendlevel?(:info)\n        warn_default_section(options[:section]) if @default_section\n        report_section_and_environment(options[:section], Puppet.settings[:environment])\n      end\n\n      names = if args.empty? || args == ['all']\n                :all\n              else\n                args\n              end\n\n      Puppet.settings.stringify_settings(options[:section], names)\n    end\n\n    when_rendering :console do |to_be_rendered|\n      output = ''.dup\n      if to_be_rendered.keys.length > 1\n        to_be_rendered.keys.sort.each do |setting|\n          output << \"#{setting} = #{to_be_rendered[setting]}\\n\"\n        end\n      else\n        output << \"#{to_be_rendered.to_a[0].last}\\n\"\n      end\n\n      output\n    end\n  end\n\n  def warn_default_section(section_name)\n    messages = []\n    messages << _(\"No section specified; defaulting to '%{section_name}'.\") %\n                { section_name: section_name }\n    # TRANSLATORS '--section' is a command line option and should not be translated\n    messages << _(\"Set the config section by using the `--section` flag.\")\n    # TRANSLATORS `puppet config --section user print foo` is a command line example and should not be translated\n    messages << _(\"For example, `puppet config --section user print foo`.\")\n    messages << _(\"For more information, see https://puppet.com/docs/puppet/latest/configuration.html\")\n\n    Puppet.warning(messages.join(\"\\n\"))\n  end\n\n  def report_section_and_environment(section_name, environment_name)\n    $stderr.puts colorize(:hyellow,\n                          _(\"Resolving settings from section '%{section_name}' in environment '%{environment_name}'\") %\n                            { section_name: section_name, environment_name: environment_name })\n  end\n\n  action(:set) do\n    summary _(\"Set Puppet's settings.\")\n    arguments _(\"[setting_name] [setting_value]\")\n    description <<-'EOT'\n      Updates values in the `puppet.conf` configuration file.\n    EOT\n    notes <<-'EOT'\n      By default, this action manipulates the configuration in the\n      'main' section. Use the '--section' flag to manipulate other\n      configuration domains.\n    EOT\n    examples <<-'EOT'\n      Set puppet's runfile directory:\n\n      $ puppet config set rundir /var/run/puppetlabs\n\n      Set the vardir for only the agent:\n\n      $ puppet config set vardir /opt/puppetlabs/puppet/cache --section agent\n    EOT\n\n    when_invoked do |name, value, options|\n      @default_section = false\n      if options[:section] == DEFAULT_SECTION_MARKER\n        options[:section] = DEFAULT_SECTION\n        @default_section = true\n      end\n\n      if name == 'environment' && options[:section] == 'main'\n        Puppet.warning _(<<~EOM).chomp\n          The environment should be set in either the `[user]`, `[agent]`, or `[server]`\n          section. Variables set in the `[agent]` section are used when running\n          `puppet agent`. Variables set in the `[user]` section are used when running\n          various other puppet subcommands, like `puppet apply` and `puppet module`; these\n          require the defined environment directory to exist locally. Set the config\n          section by using the `--section` flag. For example,\n          `puppet config --section user set environment foo`. For more information, see\n          https://puppet.com/docs/puppet/latest/configuration.html#environment\n        EOM\n      end\n\n      if Puppet::Util::Log.sendlevel?(:info)\n        report_section_and_environment(options[:section], Puppet.settings[:environment])\n      end\n\n      # only validate settings we recognize\n      setting = Puppet.settings.setting(name.to_sym)\n      if setting\n        # set the value, which will call `on_*_and_write` hooks, if any\n        Puppet.settings[setting.name] = value\n\n        # read the value to trigger interpolation and munge validation logic\n        Puppet.settings[setting.name]\n      end\n\n      path = Puppet::FileSystem.pathname(Puppet.settings.which_configuration_file)\n      Puppet::FileSystem.touch(path)\n      Puppet::FileSystem.open(path, nil, 'r+:UTF-8') do |file|\n        Puppet::Settings::IniFile.update(file) do |config|\n          if options[:section] == \"master\"\n            # delete requested master section if it exists,\n            # as server section should be used\n            setting_string = config.delete(\"master\", name)\n            if setting_string\n\n              if Puppet::Util::Log.sendlevel?(:info)\n                report_section_and_environment(options[:section], Puppet.settings[:environment])\n              end\n\n              puts(_(\"Deleted setting from '%{section_name}': '%{setting_string}', and adding it to 'server' section\") %\n                       { section_name: options[:section], name: name, setting_string: setting_string.strip })\n            end\n            # add the setting to the to server section instead of master section\n            config.set(\"server\", name, value)\n          else\n            config.set(options[:section], name, value)\n          end\n        end\n      end\n      nil\n    end\n  end\n\n  action(:delete) do\n    summary _(\"Delete a Puppet setting.\")\n    arguments _(\"<setting>\")\n    # TRANSLATORS 'main' is a specific section name and should not be translated\n    description \"Deletes a setting from the specified section. (The default is the section 'main').\"\n    notes <<-'EOT'\n      By default, this action deletes the configuration setting from the 'main'\n      configuration domain. Use the '--section' flags to delete settings from other\n      configuration domains.\n    EOT\n    examples <<-'EOT'\n      Delete the setting 'setting_name' from the 'main' configuration domain:\n\n      $ puppet config delete setting_name\n\n      Delete the setting 'setting_name' from the 'server' configuration domain:\n\n      $ puppet config delete setting_name --section server\n    EOT\n\n    when_invoked do |name, options|\n      @default_section = false\n      if options[:section] == DEFAULT_SECTION_MARKER\n        options[:section] = DEFAULT_SECTION\n        @default_section = true\n      end\n\n      path = Puppet::FileSystem.pathname(Puppet.settings.which_configuration_file)\n      if Puppet::FileSystem.exist?(path)\n        Puppet::FileSystem.open(path, nil, 'r+:UTF-8') do |file|\n          Puppet::Settings::IniFile.update(file) do |config|\n            # delete from both master section and server section\n            if options[:section] == \"master\" || options[:section] == \"server\"\n              master_setting_string = config.delete(\"master\", name)\n              puts(_(\"Deleted setting from '%{section_name}': '%{setting_string}'\") %\n              { section_name: 'master', name: name, setting_string: master_setting_string.strip[/[^=]+/] }) if master_setting_string\n\n              server_setting_string = config.delete(\"server\", name)\n              puts(_(\"Deleted setting from '%{section_name}': '%{setting_string}'\") %\n              { section_name: 'server', name: name, setting_string: server_setting_string.strip[/[^=]+/] }) if server_setting_string\n\n            else\n              setting_string = config.delete(options[:section], name)\n              if setting_string\n\n                if Puppet::Util::Log.sendlevel?(:info)\n                  report_section_and_environment(options[:section], Puppet.settings[:environment])\n                end\n\n                puts(_(\"Deleted setting from '%{section_name}': '%{setting_string}'\") %\n                         { section_name: options[:section], name: name, setting_string: setting_string.strip })\n              else\n                Puppet.warning(_(\"No setting found in configuration file for section '%{section_name}' setting name '%{name}'\") %\n                                   { section_name: options[:section], name: name })\n              end\n            end\n          end\n        end\n      else\n        # TRANSLATORS the 'puppet.conf' is a specific file and should not be translated\n        Puppet.warning(_(\"The puppet.conf file does not exist %{puppet_conf}\") % { puppet_conf: path })\n      end\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/epp.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/pops'\nrequire_relative '../../puppet/parser/files'\nrequire_relative '../../puppet/file_system'\n\nPuppet::Face.define(:epp, '0.0.1') do\n  copyright \"Puppet Inc.\", 2014\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Interact directly with the EPP template parser/renderer.\")\n\n  action(:validate) do\n    summary _(\"Validate the syntax of one or more EPP templates.\")\n    arguments _(\"[<template>] [<template> ...]\")\n    returns _(\"Nothing, or encountered syntax errors.\")\n    description <<-'EOT'\n      This action validates EPP syntax without producing any output.\n\n      When validating, multiple issues per file are reported up\n      to the settings of max_error, and max_warnings. The processing\n      stops after having reported issues for the first encountered file with errors\n      unless the option --continue_on_error is given.\n\n      Files can be given using the `modulename/template.epp` style to lookup the\n      template from a module, or be given as a reference to a file. If the reference\n      to a file can be resolved against a template in a module, the module version\n      wins - in this case use an absolute path to reference the template file\n      if the module version is not wanted.\n\n      Exits with 0 if there were no validation errors.\n    EOT\n\n    option(\"--[no-]continue_on_error\") do\n      summary _(\"Whether or not to continue after errors are reported for a template.\")\n    end\n\n    examples <<-'EOT'\n      Validate the template 'template.epp' in module 'mymodule':\n\n          $ puppet epp validate mymodule/template.epp\n\n      Validate two arbitrary template files:\n\n          $ puppet epp validate mymodule/template1.epp yourmodule/something.epp\n\n        Validate a template somewhere in the file system:\n\n            $ puppet epp validate /tmp/testing/template1.epp\n\n         Validate a template against a file relative to the current directory:\n\n           $ puppet epp validate template1.epp\n           $ puppet epp validate ./template1.epp\n\n      Validate from STDIN:\n\n          $ cat template.epp | puppet epp validate\n\n      Continue on error to see errors for all templates:\n\n          $ puppet epp validate mymodule/template1.epp mymodule/template2.epp --continue_on_error\n    EOT\n    when_invoked do |*args|\n      options = args.pop\n      # pass a dummy node, as facts are not needed for validation\n      options[:node] = Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}))\n      compiler = create_compiler(options)\n\n      status = true # no validation error yet\n      files = args\n      if files.empty?\n        if !STDIN.tty?\n          tmp = validate_template_string(STDIN.read)\n          status &&= tmp\n        else\n          # This is not an error since a validate of all files in an empty\n          # directory should not be treated as a failed validation.\n          Puppet.notice _(\"No template specified. No action taken\")\n        end\n      end\n\n      missing_files = []\n      files.each do |file|\n        break if !status && !options[:continue_on_error]\n\n        template_file = effective_template(file, compiler.environment)\n        if template_file\n          tmp = validate_template(template_file)\n          status &&= tmp\n        else\n          missing_files << file\n        end\n      end\n      if !missing_files.empty?\n        raise Puppet::Error, _(\"One or more file(s) specified did not exist:\\n%{missing_files_list}\") %\n                             { missing_files_list: missing_files.map { |f| \"   #{f}\" }.join(\"\\n\") }\n      else\n        # Exit with 1 if there were errors\n        raise Puppet::Error, _(\"Errors while validating epp\") unless status\n      end\n    end\n  end\n\n  action(:dump) do\n    summary _(\"Outputs a dump of the internal template parse tree for debugging\")\n    arguments \"[--format <old|pn|json>] [--pretty] { -e <source> | [<templates> ...] } \"\n    returns _(\"A dump of the resulting AST model unless there are syntax or validation errors.\")\n    description <<-'EOT'\n      The dump action parses and validates the EPP syntax and dumps the resulting AST model\n      in a human readable (but not necessarily an easy to understand) format.\n\n      The output format can be controlled using the --format <old|pn|json> where:\n      * 'old' is the default, but now deprecated format which is not API.\n      * 'pn' is the Puppet Extended S-Expression Notation.\n      * 'json' outputs the same graph as 'pn' but with JSON syntax.\n\n      The output will be \"pretty printed\" when the option --pretty is given together with --format 'pn' or 'json'.\n      This option has no effect on the 'old' format.\n\n      The command accepts one or more templates (.epp) files, or an -e followed by the template\n      source text. The given templates can be paths to template files, or references\n      to templates in modules when given on the form <modulename>/<template-name>.epp.\n      If no arguments are given, the stdin is read (unless it is attached to a terminal)\n\n      If multiple templates are given, they are separated with a header indicating the\n      name of the template. This can be suppressed with the option --no-header.\n      The option --[no-]header has no effect when a single template is dumped.\n\n      When debugging the epp parser itself, it may be useful to suppress the validation\n      step with the `--no-validate` option to observe what the parser produced from the\n      given source.\n\n      This command ignores the --render-as setting/option.\n    EOT\n\n    option(\"--e \" + _(\"<source>\")) do\n      default_to { nil }\n      summary _(\"Dump one epp source expression given on the command line.\")\n    end\n\n    option(\"--[no-]validate\") do\n      summary _(\"Whether or not to validate the parsed result, if no-validate only syntax errors are reported.\")\n    end\n\n    option('--format ' + _('<old, pn, or json>')) do\n      summary _(\"Get result in 'old' (deprecated format), 'pn' (new format), or 'json' (new format in JSON).\")\n    end\n\n    option('--pretty') do\n      summary _('Pretty print output. Only applicable together with --format pn or json')\n    end\n\n    option(\"--[no-]header\") do\n      summary _(\"Whether or not to show a file name header between files.\")\n    end\n\n    when_invoked do |*args|\n      require_relative '../../puppet/pops'\n      options = args.pop\n      # pass a dummy node, as facts are not needed for dump\n      options[:node] = Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}))\n      options[:header] = options[:header].nil? ? true : options[:header]\n      options[:validate] = options[:validate].nil? ? true : options[:validate]\n\n      compiler = create_compiler(options)\n\n      # Print to a buffer since the face needs to return the resulting string\n      # and the face API is \"all or nothing\"\n      #\n      buffer = StringIO.new\n\n      if options[:e]\n        buffer.print dump_parse(options[:e], 'command-line-string', options, false)\n      elsif args.empty?\n        if !STDIN.tty?\n          buffer.print dump_parse(STDIN.read, 'stdin', options, false)\n        else\n          raise Puppet::Error, _(\"No input to parse given on command line or stdin\")\n        end\n      else\n        templates, missing_files = args.each_with_object([[], []]) do |file, memo|\n          template_file = effective_template(file, compiler.environment)\n          if template_file.nil?\n            memo[1] << file\n          else\n            memo[0] << template_file\n          end\n        end\n\n        show_filename = templates.count > 1\n        templates.each do |file|\n          buffer.print dump_parse(Puppet::FileSystem.read(file, :encoding => 'utf-8'), file, options, show_filename)\n        end\n\n        unless missing_files.empty?\n          raise Puppet::Error, _(\"One or more file(s) specified did not exist:\\n%{missing_files_list}\") %\n                               { missing_files_list: missing_files.collect { |f| \"   #{f}\" }.join(\"\\n\") }\n        end\n      end\n      buffer.string\n    end\n  end\n\n  action(:render) do\n    summary _(\"Renders an epp template as text\")\n    arguments \"-e <source> | [<templates> ...] \"\n    returns _(\"A rendered result of one or more given templates.\")\n    description <<-'EOT'\n      This action renders one or more EPP templates.\n\n      The command accepts one or more templates (.epp files), given the same way as templates\n      are given to the puppet `epp` function (a full path, or a relative reference\n      on the form '<modulename>/<template-name>.epp'), or as a relative path.args In case\n      the given path matches both a modulename/template and a file, the template from\n      the module is used.\n\n      An inline_epp equivalent can also be performed by giving the template after\n      an -e, or by piping the EPP source text to the command.\n\n      Values to the template can be defined using the Puppet Language on the command\n      line with `--values` or in a .pp or .yaml file referenced with `--values_file`. If\n      specifying both the result is merged with --values having higher precedence.\n\n      The --values option allows a Puppet Language sequence of expressions to be defined on the\n      command line the same way as it may be given in a .pp file referenced with `--values_file`.\n      It may set variable values (that become available in the template), and must produce\n      either `undef` or a `Hash` of values (the hash may be empty). Producing `undef` simulates\n      that the template is called without an arguments hash and thus only references\n      variables in its outer scope. When a hash is given, a template is limited to seeing\n      only the global scope. It is thus possible to simulate the different types of\n      calls to the `epp` and `inline_epp` functions, with or without a given hash. Note that if\n      variables are given, they are always available in this simulation - to test that the\n      template only references variables given as arguments, produce a hash in --values or\n      the --values_file, do not specify any variables that are not global, and\n      turn on --strict_variables setting.\n\n      If multiple templates are given, the same set of values are given to each template.\n      If both --values and --value_file are used, the --values are merged on top of those given\n      in the file.\n\n      When multiple templates are rendered, a separating header is output between the templates\n      showing the name of the template before the output. The header output can be turned off with\n      `--no-header`. This also concatenates the template results without any added newline separators.\n\n      Facts from the node where the command is being run are used by default.args Facts can be obtained\n      for other nodes if they have called in, and reported their facts by using the `--node <nodename>`\n      flag.\n\n      Overriding node facts as well as additional facts can be given in a .yaml or .json file and referencing\n      it with the --facts option. (Values can be obtained in yaml format directly from\n      `facter`, or from puppet for a given node). Note that it is not possible to simulate the\n      reserved variable name `$facts` in any other way.\n\n      Note that it is not possible to set variables using the Puppet Language that have the same\n      names as facts as this result in an error; \"attempt to redefine a variable\" since facts\n      are set first.\n\n      Exits with 0 if there were no validation errors. On errors, no rendered output is produced for\n      that template file.\n\n      When designing EPP templates, it is strongly recommended to define all template arguments\n      in the template, and to give them in a hash when calling `epp` or `inline_epp` and to use\n      as few global variables as possible, preferably only the $facts hash. This makes templates\n      more free standing and are easier to reuse, and to test.\n    EOT\n\n    examples <<-'EOT'\n      Render the template in module 'mymodule' called 'mytemplate.epp', and give it two arguments\n      `a` and `b`:\n\n          $ puppet epp render mymodule/mytemplate.epp --values '{a => 10, b => 20}'\n\n      Render a template using an absolute path:\n\n          $ puppet epp render /tmp/testing/mytemplate.epp --values '{a => 10, b => 20}'\n\n      Render a template with data from a .pp file:\n\n          $ puppet epp render /tmp/testing/mytemplate.epp --values_file mydata.pp\n\n      Render a template with data from a .pp file and override one value on the command line:\n\n          $ puppet epp render /tmp/testing/mytemplate.epp --values_file mydata.pp --values '{a=>10}'\n\n      Render from STDIN:\n\n          $ cat template.epp | puppet epp render --values '{a => 10, b => 20}'\n\n      Set variables in a .pp file and render a template that uses variable references:\n\n          # data.pp file\n          $greeted = 'a global var'\n          undef\n\n          $ puppet epp render -e 'hello <%= $greeted %>' --values_file data.pp\n\n      Render a template that outputs a fact:\n\n          $ facter --yaml > data.yaml\n          $ puppet epp render -e '<% $facts[osfamily] %>' --facts data.yaml\n    EOT\n\n    option(\"--node \" + _(\"<node_name>\")) do\n      summary _(\"The name of the node for which facts are obtained. Defaults to facts for the local node.\")\n    end\n\n    option(\"--e \" + _(\"<source>\")) do\n      default_to { nil }\n      summary _(\"Render one inline epp template given on the command line.\")\n    end\n\n    option(\"--values \" + _(\"<values_hash>\")) do\n      summary _(\"A Hash in Puppet DSL form given as arguments to the template being rendered.\")\n    end\n\n    option(\"--values_file \" + _(\"<pp_or_yaml_file>\")) do\n      summary _(\"A .pp or .yaml file that is processed to produce a hash of values for the template.\")\n    end\n\n    option(\"--facts \" + _(\"<facts_file>\")) do\n      summary _(\"A .yaml or .json file containing a hash of facts made available in $facts and $trusted\")\n    end\n\n    option(\"--[no-]header\") do\n      summary _(\"Whether or not to show a file name header between rendered results.\")\n    end\n\n    when_invoked do |*args|\n      options = args.pop\n      options[:header] = options[:header].nil? ? true : options[:header]\n\n      compiler = create_compiler(options)\n      compiler.with_context_overrides('For rendering epp') do\n        # Print to a buffer since the face needs to return the resulting string\n        # and the face API is \"all or nothing\"\n        #\n        buffer = StringIO.new\n        status = true\n        if options[:e]\n          buffer.print render_inline(options[:e], compiler, options)\n        elsif args.empty?\n          if !STDIN.tty?\n            buffer.print render_inline(STDIN.read, compiler, options)\n          else\n            raise Puppet::Error, _(\"No input to process given on command line or stdin\")\n          end\n        else\n          show_filename = args.count > 1\n          file_nbr = 0\n          args.each do |file|\n            buffer.print render_file(file, compiler, options, show_filename, file_nbr += 1)\n          rescue Puppet::ParseError => detail\n            Puppet.err(detail.message)\n            status = false\n          end\n        end\n        raise Puppet::Error, _(\"error while rendering epp\") unless status\n\n        buffer.string\n      end\n    end\n  end\n\n  def dump_parse(source, filename, options, show_filename = true)\n    output = ''.dup\n    evaluating_parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new\n    begin\n      if options[:validate]\n        parse_result = evaluating_parser.parse_string(source, filename)\n      else\n        # side step the assert_and_report step\n        parse_result = evaluating_parser.parser.parse_string(source)\n      end\n      if show_filename && options[:header]\n        output << \"--- #{filename}\\n\"\n      end\n      fmt = options[:format]\n      if fmt.nil? || fmt == 'old'\n        output << Puppet::Pops::Model::ModelTreeDumper.new.dump(parse_result) << \"\\n\"\n      else\n        require_relative '../../puppet/pops/pn'\n        pn = Puppet::Pops::Model::PNTransformer.transform(parse_result)\n        case fmt\n        when 'json'\n          options[:pretty] ? JSON.pretty_unparse(pn.to_data) : JSON.dump(pn.to_data)\n        else\n          pn.format(options[:pretty] ? Puppet::Pops::PN::Indent.new('  ') : nil, output)\n        end\n      end\n    rescue Puppet::ParseError => detail\n      if show_filename\n        Puppet.err(\"--- #{filename}\")\n      end\n      Puppet.err(detail.message)\n      \"\"\n    end\n  end\n\n  def get_values(compiler, options)\n    template_values = nil\n    values_file = options[:values_file]\n    if values_file\n      begin\n        case values_file\n        when /\\.yaml$/\n          template_values = Puppet::Util::Yaml.safe_load_file(values_file, [Symbol])\n        when /\\.pp$/\n          evaluating_parser = Puppet::Pops::Parser::EvaluatingParser.new\n          template_values = evaluating_parser.evaluate_file(compiler.topscope, values_file)\n        else\n          Puppet.err(_(\"Only .yaml or .pp can be used as a --values_file\"))\n        end\n      rescue => e\n        Puppet.err(_(\"Could not load --values_file %{error}\") % { error: e.message })\n      end\n      unless template_values.nil? || template_values.is_a?(Hash)\n        Puppet.err(_(\"--values_file option must evaluate to a Hash or undef/nil, got: '%{template_class}'\") % { template_class: template_values.class })\n      end\n    end\n\n    values = options[:values]\n    if values\n      evaluating_parser = Puppet::Pops::Parser::EvaluatingParser.new\n      result = evaluating_parser.evaluate_string(compiler.topscope, values, 'values-hash')\n      case result\n      when nil\n        template_values\n      when Hash\n        template_values.nil? ? result : template_values.merge(result)\n      else\n        Puppet.err(_(\"--values option must evaluate to a Hash or undef, got: '%{values_class}'\") % { values_class: result.class })\n      end\n    else\n      template_values\n    end\n  end\n\n  def render_inline(epp_source, compiler, options)\n    template_args = get_values(compiler, options)\n    result = Puppet::Pops::Evaluator::EppEvaluator.inline_epp(compiler.topscope, epp_source, template_args)\n    if result.instance_of?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      result.unwrap\n    else\n      result\n    end\n  end\n\n  def render_file(epp_template_name, compiler, options, show_filename, file_nbr)\n    template_args = get_values(compiler, options)\n    output = ''.dup\n    begin\n      if show_filename && options[:header]\n        output << \"\\n\" unless file_nbr == 1\n        output << \"--- #{epp_template_name}\\n\"\n      end\n      # Change to an absolute file only if reference is to a an existing file. Note that an absolute file must be used\n      # or the template must be found on the module path when calling the epp evaluator.\n      template_file = Puppet::Parser::Files.find_template(epp_template_name, compiler.environment)\n      if template_file.nil? && Puppet::FileSystem.exist?(epp_template_name)\n        epp_template_name = File.expand_path(epp_template_name)\n      end\n      result = Puppet::Pops::Evaluator::EppEvaluator.epp(compiler.topscope, epp_template_name, compiler.environment, template_args)\n      if result.instance_of?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n        output << result.unwrap\n      else\n        output << result\n      end\n    rescue Puppet::ParseError => detail\n      Puppet.err(\"--- #{epp_template_name}\") if show_filename\n      raise detail\n    end\n    output\n  end\n\n  # @api private\n  def validate_template(template)\n    parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new()\n    parser.parse_file(template)\n    true\n  rescue => detail\n    Puppet.log_exception(detail)\n    false\n  end\n\n  # @api private\n  def validate_template_string(source)\n    parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new()\n    parser.parse_string(source, '<stdin>')\n    true\n  rescue => detail\n    Puppet.log_exception(detail)\n    false\n  end\n\n  # @api private\n  def create_compiler(options)\n    if options[:node]\n      node = options[:node]\n    else\n      node = Puppet[:node_name_value]\n\n      # If we want to lookup the node we are currently on\n      # we must returning these settings to their default values\n      Puppet.settings[:facts_terminus] = 'facter'\n      Puppet.settings[:node_cache_terminus] = nil\n    end\n\n    unless node.is_a?(Puppet::Node)\n      node = Puppet::Node.indirection.find(node)\n      # Found node must be given the environment to use in some cases, use the one configured\n      # or given on the command line\n      node.environment = Puppet[:environment]\n    end\n\n    fact_file = options[:facts]\n\n    if fact_file\n      if fact_file.is_a?(Hash) # when used via the Face API\n        given_facts = fact_file\n      elsif fact_file.end_with?(\"json\")\n        given_facts = Puppet::Util::Json.load(Puppet::FileSystem.read(fact_file, :encoding => 'utf-8'))\n      else\n        given_facts = Puppet::Util::Yaml.safe_load_file(fact_file)\n      end\n\n      unless given_facts.instance_of?(Hash)\n        raise _(\"Incorrect formatted data in %{fact_file} given via the --facts flag\") % { fact_file: fact_file }\n      end\n\n      # It is difficult to add to or modify the set of facts once the node is created\n      # as changes does not show up in parameters. Rather than manually patching up\n      # a node and risking future regressions, a new node is created from scratch\n      node = Puppet::Node.new(node.name, :facts => Puppet::Node::Facts.new(\"facts\", node.facts.values.merge(given_facts)))\n      node.environment = Puppet[:environment]\n      node.merge(node.facts.values)\n    end\n\n    compiler = Puppet::Parser::Compiler.new(node)\n    # configure compiler with facts and node related data\n    # Set all global variables from facts\n    compiler.send(:set_node_parameters)\n\n    # pretend that the main class (named '') has been evaluated\n    # since it is otherwise not possible to resolve top scope variables\n    # using '::' when rendering. (There is no harm doing this for the other actions)\n    #\n    compiler.topscope.class_set('', compiler.topscope)\n    compiler\n  end\n\n  # Produces the effective template file from a module/template or file reference\n  # @api private\n  def effective_template(file, env)\n    template_file = Puppet::Parser::Files.find_template(file, env)\n    if !template_file.nil?\n      template_file\n    elsif Puppet::FileSystem.exist?(file)\n      file\n    else\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/facts.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/face'\nrequire_relative '../../puppet/node/facts'\n\nPuppet::Indirector::Face.define(:facts, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Retrieve and store facts.\")\n  description <<-'EOT'\n    This subcommand manages facts, which are collections of normalized system\n    information used by Puppet. It can read facts directly from the local system\n    (with the default `facter` terminus).\n  EOT\n\n  find = get_action(:find)\n  find.summary _(\"Retrieve a node's facts.\")\n  find.arguments _(\"[<node_certname>]\")\n  find.returns <<-'EOT'\n    A hash containing some metadata and (under the \"values\" key) the set\n    of facts for the requested node. When used from the Ruby API: A\n    Puppet::Node::Facts object.\n\n    RENDERING ISSUES: Facts cannot currently be rendered as a string; use yaml\n    or json.\n  EOT\n  find.notes <<-'EOT'\n    When using the `facter` terminus, the host argument is ignored.\n  EOT\n  find.examples <<-'EOT'\n    Get facts from the local system:\n\n    $ puppet facts find\n  EOT\n\n  deactivate_action(:destroy)\n  deactivate_action(:search)\n\n  action(:upload) do\n    summary _(\"Upload local facts to the puppet master.\")\n    description <<-'EOT'\n      Reads facts from the local system using the `facter` terminus, then\n      saves the returned facts using the rest terminus.\n    EOT\n    returns \"Nothing.\"\n    notes <<-'EOT'\n      This action requires that the Puppet Server's `auth.conf` file\n      allow `PUT` or `save` access to the `/puppet/v3/facts` API endpoint.\n\n      For details on configuring Puppet Server's `auth.conf`, see:\n\n      <https://puppet.com/docs/puppetserver/latest/config_file_auth.html>\n    EOT\n    examples <<-'EOT'\n      Upload facts:\n\n      $ puppet facts upload\n    EOT\n\n    render_as :json\n\n    when_invoked do |_options|\n      # Use `agent` sections  settings for certificates, Puppet Server URL,\n      # etc. instead of `user` section settings.\n      Puppet.settings.preferred_run_mode = :agent\n      Puppet::Node::Facts.indirection.terminus_class = :facter\n\n      facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value])\n      unless Puppet[:node_name_fact].empty?\n        Puppet[:node_name_value] = facts.values[Puppet[:node_name_fact]]\n        facts.name = Puppet[:node_name_value]\n      end\n\n      client = Puppet.runtime[:http]\n      session = client.create_session\n      puppet = session.route_to(:puppet)\n\n      Puppet.notice(_(\"Uploading facts for '%{node}' to '%{server}'\") % {\n        node: Puppet[:node_name_value],\n        server: puppet.url.hostname\n      })\n\n      puppet.put_facts(Puppet[:node_name_value], facts: facts, environment: Puppet.lookup(:current_environment).name.to_s)\n      nil\n    end\n  end\n\n  action(:show) do\n    summary _(\"Retrieve current node's facts.\")\n    arguments _(\"[<facts>]\")\n    description <<-'EOT'\n    Reads facts from the local system using `facter` terminus.\n    A query can be provided to retrieve just a specific fact or a set of facts.\n    EOT\n    returns \"The output of facter with added puppet specific facts.\"\n    notes <<-'EOT'\n\n    EOT\n    examples <<-'EOT'\n    retrieve facts:\n\n    $ puppet facts show os\n    EOT\n    default true\n\n    option(\"--config-file \" + _(\"<path>\")) do\n      default_to { nil }\n      summary _(\"The location of the config file for Facter.\")\n    end\n\n    option(\"--custom-dir \" + _(\"<path>\")) do\n      default_to { nil }\n      summary _(\"The path to a directory that contains custom facts.\")\n    end\n\n    option(\"--external-dir \" + _(\"<path>\")) do\n      default_to { nil }\n      summary _(\"The path to a directory that contains external facts.\")\n    end\n\n    option(\"--no-block\") do\n      summary _(\"Disable fact blocking mechanism.\")\n    end\n\n    option(\"--no-cache\") do\n      summary _(\"Disable fact caching mechanism.\")\n    end\n\n    option(\"--show-legacy\") do\n      summary _(\"Show legacy facts when querying all facts.\")\n    end\n\n    option(\"--value-only\") do\n      summary _(\"Show only the value when the action is called with a single query\")\n    end\n\n    option(\"--timing\") do\n      summary _(\"Show how much time it took to resolve each fact.\")\n    end\n\n    when_invoked do |*args|\n      options = args.pop\n\n      Puppet.settings.preferred_run_mode = :agent\n      Puppet::Node::Facts.indirection.terminus_class = :facter\n\n      if options[:value_only] && !args.count.eql?(1)\n        options[:value_only] = nil\n        Puppet.warning(\"Incorrect use of --value-only argument; it can only be used when querying for a single fact!\")\n      end\n\n      options[:user_query] = args\n      options[:resolve_options] = true\n      result = Puppet::Node::Facts.indirection.find(Puppet.settings[:certname], options)\n\n      if options[:value_only]\n        result.values.values.first\n      else\n        result.values\n      end\n    end\n\n    when_rendering :console do |result|\n      # VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, Symbol, String, Array, Hash].freeze\n      # from https://github.com/puppetlabs/facter/blob/4.0.49/lib/facter/custom_facts/util/normalization.rb#L8\n\n      case result\n      when Array, Hash\n        # JSON < 2.8.0 would pretty print empty arrays and hashes with newlines\n        # Maintain that behavior for our users for now\n        if result.is_a?(Array) && result.empty?\n          \"[\\n\\n]\"\n        elsif result.is_a?(Hash) && result.empty?\n          \"{\\n}\"\n        else\n          Puppet::Util::Json.dump(result, :pretty => true)\n        end\n      else # one of VALID_TYPES above\n        result\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/generate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/generate/type'\n\n# Create the Generate face\nPuppet::Face.define(:generate, '0.1.0') do\n  copyright 'Puppet Inc.', 2016\n  license   _('Apache 2 license; see COPYING')\n\n  summary _('Generates Puppet code from Ruby definitions.')\n\n  action(:types) do\n    summary _('Generates Puppet code for custom types')\n\n    description <<-'EOT'\n      Generates definitions for custom resource types using Puppet code.\n\n      Types defined in Puppet code can be used to isolate custom type definitions\n      between different environments.\n    EOT\n\n    examples <<-'EOT'\n      Generate Puppet type definitions for all custom resource types in the current environment:\n\n          $ puppet generate types\n\n      Generate Puppet type definitions for all custom resource types in the specified environment:\n\n          $ puppet generate types --environment development\n    EOT\n\n    option '--format ' + _('<format>') do\n      summary _('The generation output format to use. Supported formats: pcore.')\n      default_to { 'pcore' }\n\n      before_action do |_, _, options|\n        raise ArgumentError, _(\"'%{format}' is not a supported format for type generation.\") % { format: options[:format] } unless ['pcore'].include?(options[:format])\n      end\n    end\n\n    option '--force' do\n      summary _('Forces the generation of output files (skips up-to-date checks).')\n      default_to { false }\n    end\n\n    when_invoked do |options|\n      generator = Puppet::Generate::Type\n      inputs = generator.find_inputs(options[:format].to_sym)\n      environment = Puppet.lookup(:current_environment)\n\n      # get the common output directory (in <envroot>/.resource_types) - create it if it does not exists\n      # error if it exists and is not a directory\n      #\n      path_to_env = environment.configuration.path_to_env\n      outputdir = File.join(path_to_env, '.resource_types')\n      if Puppet::FileSystem.exist?(outputdir) && !Puppet::FileSystem.directory?(outputdir)\n        raise ArgumentError, _(\"The output directory '%{outputdir}' exists and is not a directory\") % { outputdir: outputdir }\n      end\n\n      Puppet::FileSystem.mkpath(outputdir)\n\n      generator.generate(inputs, outputdir, options[:force])\n\n      exit(1) if generator.bad_input?\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/help/action.erb",
    "content": "<%# encoding: UTF-8%>\n<% if action.deprecated? -%>\n  <%= \"Warning: 'puppet #{action.face.name} #{action.name}' is deprecated and will be removed in a future release.\" %>\n<% end %>\n<% if action.synopsis -%>\nUSAGE: <%= action.synopsis %>\n\n<% end -%>\n<%= action.short_description || action.summary || face.summary || \"undocumented subcommand\" %>\n\n<% if action.returns -%>\nRETURNS: <%= action.returns.strip %>\n\n<% end -%>\nOPTIONS:\n<%# Remove these options once we can introspect them normally. -%>\n  --render-as FORMAT             - The rendering format to use.\n  --verbose                      - Whether to log verbosely.\n  --debug                        - Whether to log debug information.\n<%  optionroom = 30\n\tsummaryroom = 80 - 5 - optionroom\n\n\tdisp_glob_opts = action.display_global_options.uniq\n \tunless disp_glob_opts.empty?\n\t\tdisp_glob_opts.sort.each do |name|\n\t\t\toption = name\n\t\t\tdesc = Puppet.settings.setting(option).desc\n\t\t\ttype = Puppet.settings.setting(option).default\n\t\t\ttype ||= Puppet.settings.setting(option).type.to_s.upcase -%>\n  <%= \t\t\"--#{option} #{type}\".ljust(optionroom) + ' - ' -%>\n<%\t\t\tif !(desc) -%>\nundocumented option\n<%     \t\telsif desc.length <= summaryroom -%>\n<%= \t\t\tdesc %>\n<%\n       \t\telse\n\t\t\t\twords = desc.split\n\t\t\t\twrapped = ['']\n\t\t\t\ti = 0\n\t\t\t\twords.each do |word|\n\t\t\t\t\tif wrapped[i].length + word.length <= summaryroom\n\t\t\t\t\t\twrapped[i] << word + ' '\n\t\t\t\t\telse\n\t\t\t\t\t\ti += 1\n\t\t\t\t\t\twrapped[i] = word + ' '\n\t\t\t\t\tend\n\t\t\t\tend -%>\n<%= \t\t\twrapped.shift.strip %>\n<%\t\t\t\twrapped.each do |line| -%>\n<%= \t\t\t\t(' ' * (optionroom + 5) ) + line.strip %>\n<%\t\t\t\tend\n\t\t \tend\n\t  \tend\n\tend\n\tunless action.options.empty?\n      action.options.sort.each do |name|\n        option = action.get_option name -%>\n<%= \"  \" + option.optparse.join(\" | \")[0,(optionroom - 1)].ljust(optionroom) + ' - ' -%>\n<%     if !(option.summary) -%>\nundocumented option\n<%     elsif option.summary.length <= summaryroom -%>\n<%= option.summary %>\n<%\n        else\n          words = option.summary.split\n          wrapped = ['']\n          i = 0\n          words.each do |word|\n            if wrapped[i].length + word.length <= summaryroom\n              wrapped[i] << word + ' '\n            else\n              i += 1\n              wrapped[i] = word + ' '\n            end\n          end\n-%>\n<%= wrapped.shift.strip %>\n<%       wrapped.each do |line| -%>\n<%= (' ' * (optionroom + 5) ) + line.strip %>\n<%       end\n        end\n      end -%>\n<% end -%>\n\n<% if face.respond_to? :indirection -%>\nTERMINI: <%= face.class.terminus_classes(face.indirection.name).join(\", \") %>\n\n<% end -%>\nSee 'puppet help <%= face.name %>' or 'man puppet-<%= face.name %>' for full help.\n"
  },
  {
    "path": "lib/puppet/face/help/face.erb",
    "content": "<%# encoding: UTF-8%>\n<% if face.deprecated? -%>\n<%= \"Warning: 'puppet #{face.name}' is deprecated and will be removed in a future release.\" %>\n<% end %>\n<% if face.synopsis -%>\nUSAGE: <%= face.synopsis %>\n\n<% end -%>\n<%= (face.short_description || face.summary || \"undocumented subcommand\").strip %>\n\nOPTIONS:\n<%# Remove these options once we can introspect them normally. -%>\n  --render-as FORMAT             - The rendering format to use.\n  --verbose                      - Whether to log verbosely.\n  --debug                        - Whether to log debug information.\n<%\toptionroom = 30\n\tsummaryroom = 80 - 5 - optionroom\n\t\n\tdisp_glob_opts = face.display_global_options.uniq\n \tunless disp_glob_opts.empty?\n\t\tdisp_glob_opts.sort.each do |name|\n\t\t\toption = name\n\t\t\tdesc = Puppet.settings.setting(option).desc\n\t\t\ttype = Puppet.settings.setting(option).default\n\t\t\ttype ||= Puppet.settings.setting(option).type.to_s.upcase -%>\n  <%= \t\t\"--#{option} #{type}\".ljust(optionroom) + ' - ' -%>\n<%\t\t\tif !(desc) -%>\nundocumented option\n<%     \t\telsif desc.length <= summaryroom -%>\n<%= \t\t\tdesc %>\n<%\t\t\telse\n\t\t\t\twords = desc.split\n\t\t\t\twrapped = ['']\n\t\t\t\ti = 0\n\t\t\t\twords.each do |word|\n\t\t\t\t\tif wrapped[i].length + word.length <= summaryroom\n\t\t\t\t\t\twrapped[i] << word + ' '\n\t\t\t\t\telse\n\t\t\t\t\t\ti += 1\n\t\t\t\t\t\twrapped[i] = word + ' '\n\t\t\t\t\tend\n\t\t\t\tend -%>\n<%= \t\t\twrapped.shift.strip %>\n<%\t\t\t\twrapped.each do |line| -%>\n<%= \t\t\t\t(' ' * (optionroom + 5) ) + line.strip %>\n<%\t\t\t\tend\n\t\t \tend\n\t  \tend\n\tend\n \tunless face.options.empty?\n      face.options.sort.each do |name|\n        option = face.get_option name -%>\n<%= \"  \" + option.optparse.join(\" | \")[0,(optionroom - 1)].ljust(optionroom) + ' - ' -%>\n<%     if !(option.summary) -%>\nundocumented option\n<%     elsif option.summary.length <= summaryroom -%>\n<%= option.summary %>\n<%\n       else\n         words = option.summary.split\n         wrapped = ['']\n         i = 0\n         words.each do |word|\n           if wrapped[i].length + word.length <= summaryroom\n             wrapped[i] << word + ' '\n           else\n             i += 1\n             wrapped[i] = word + ' '\n           end\n         end\n-%>\n<%= wrapped.shift.strip %>\n<%       wrapped.each do |line| -%>\n<%= (' ' * (optionroom + 5) ) + line.strip %>\n<%       end\n        end\n      end -%>\n<% end -%>\n\nACTIONS:\n<% padding = face.actions.map{|x| x.to_s.length}.max + 2\n   summaryroom = 80 - (padding + 4)\n   face.actions.each do |actionname|\n     action = face.get_action(actionname) -%>\n  <%= action.name.to_s.ljust(padding) + '  ' -%>\n<% if !(action.summary) -%>\nundocumented action\n<% elsif action.summary.length <= summaryroom -%>\n<%= action.summary %>\n<% else\n          words = action.summary.split\n          wrapped = ['']\n          i = 0\n          words.each do |word|\n            if wrapped[i].length + word.length <= summaryroom\n              wrapped[i] << word + ' '\n            else\n              i += 1\n              wrapped[i] = word + ' '\n            end\n          end\n-%>\n<%= wrapped.shift.strip %>\n<%       wrapped.each do |line| -%>\n<%= (' ' * (padding + 4) ) + line.strip %>\n<%       end\n        end\nend -%>\n\n<% if face.respond_to? :indirection -%>\nTERMINI: <%= face.class.terminus_classes(face.indirection.name).join(\", \") %>\n\n<% end -%>\nSee 'puppet help <%= face.name %>' or 'man puppet-<%= face.name %>' for full help.\n"
  },
  {
    "path": "lib/puppet/face/help/global.erb",
    "content": "<%# encoding: UTF-8%>\nUsage: puppet <subcommand> [options] <action> [options]\n\nAvailable subcommands:\n    <%# NOTE: this is probably not a good long-term solution for this.  We're only iterating over\n        applications to find the list of things we need to show help for... this works for now\n        because faces can't be run without an application stub.  However, when #6753 is resolved,\n        all of the application stubs for faces will go away, and this will need to be updated\n        to reflect that.  --cprice 2012-04-26 %>\n<% all_application_summaries.each do |appname, summary, indent| -%>\n  <%= indent %><%= appname.to_s.ljust(16) %>  <%= summary %>\n<% end -%>\n\nSee 'puppet help <subcommand> <action>' for help on a specific subcommand action.\nSee 'puppet help <subcommand>' for help on a specific subcommand.\nPuppet v<%= Puppet.version %>\n"
  },
  {
    "path": "lib/puppet/face/help/man.erb",
    "content": "<%# encoding: UTF-8%>\npuppet-<%= face.name %>(8) -- <%= face.summary || \"Undocumented subcommand.\" %>\n<%= '=' * (_erbout.length - 1) %>\n\n<% if face.synopsis -%>\nSYNOPSIS\n--------\n<%= face.synopsis %>\n\n<% end\n   if face.description -%>\nDESCRIPTION\n-----------\n<%= face.description.strip %>\n\n<% end -%>\nOPTIONS\n-------\nNote that any setting that's valid in the configuration\nfile is also a valid long argument, although it may or may not be\nrelevant to the present action. For example, `server` and `run_mode` are valid\nsettings, so you can specify `--server <servername>`, or\n`--run_mode <runmode>` as an argument.\n\nSee the configuration file documentation at\n<https://puppet.com/docs/puppet/latest/configuration.html> for the\nfull list of acceptable parameters. A commented list of all\nconfiguration options can also be generated by running puppet with\n`--genconfig`.\n\n* --render-as FORMAT:\n  The format in which to render output. The most common formats are `json`,\n  `s` (string), `yaml`, and `console`, but other options such as `dot` are\n  sometimes available.\n* --verbose:\n  Whether to log verbosely.\n* --debug:\n  Whether to log debug information.\n<% unless face.display_global_options.empty?\n     face.display_global_options.uniq.sort.each do |name|\n\t\toption = name\n\t\tdesc = Puppet::Util::Docs.scrub(Puppet.settings.setting(option).desc)\n\t\ttype = Puppet.settings.setting(option).default\n\t\ttype ||= Puppet.settings.setting(option).type.to_s.upcase -%>\n<%= \"* --#{option} #{type}\" %>:\n<%= (desc || 'Undocumented setting.').gsub(/^/, '  ') %>\n<%   end\n   end -%>\n<% unless face.options.empty?\n     face.options.sort.each do |name|\n       option = face.get_option name -%>\n<%= \"* \" + option.optparse.join(\" | \" ) %>:\n<%= (option.description || option.summary || \"Undocumented option.\").gsub(/^/, '  ') %>\n<%   end\n   end -%>\n\nACTIONS\n-------\n<% face.actions.each do |actionname|\n     action = face.get_action(actionname) -%>\n* `<%= action.name.to_s %>` - <%= action.summary %>:\n<%   if action.synopsis -%>\n  `SYNOPSIS`\n\n  <%= action.synopsis %>\n\n<%   end -%>\n  `DESCRIPTION`\n\n<%   if action.description -%>\n<%= action.description.gsub(/^/, '  ') %>\n<% else -%>\n  <%= action.summary || \"Undocumented action.\" %>\n<%   end -%>\n\n<%   unique_options = action.options - face.options\n\t unique_display_global_options = action.display_global_options - face.display_global_options\n     unless unique_options.empty? and unique_display_global_options.empty? -%>\n  `OPTIONS`\n<%      unique_display_global_options.uniq.sort.each do |name|\n\t\t  option = name\n\t\t  desc = Puppet::Util::Docs.scrub(Puppet.settings.setting(option).desc)\n\t\t  type = Puppet.settings.setting(option).default\n\t\t  type ||= Puppet.settings.setting(option).type.to_s.upcase -%>\n  <%= \"<--#{option} #{type}>\" %> -\n<%= (desc || \"Undocumented setting.\").gsub(/^/, '  ') %>\n<%      end -%>\n<%     unique_options.sort.each do |name|\n         option = action.get_option name\n         text = (option.description || option.summary || \"Undocumented option.\").chomp + \"\\n\" -%>\n  <%= '<' + option.optparse.join(\"> | <\") + '>' %> -\n<%= text.gsub(/^/, '  ') %>\n<%     end -%>\n\n<%   end -%>\n<%   if action.returns -%>\n  `RETURNS`\n\n<%= action.returns.gsub(/^/, '  ') %>\n\n<%   end\n     if action.notes -%>\n  `NOTES`\n\n<%= action.notes.gsub(/^/, '  ') %>\n\n<%   end\n   end\n   if face.examples or face.actions.any? {|actionname| face.get_action(actionname).examples} -%>\nEXAMPLES\n--------\n<% end\n   if face.examples -%>\n<%= face.examples %>\n\n<% end\n   face.actions.each do |actionname|\n     action = face.get_action(actionname)\n     if action.examples -%>\n`<%= action.name.to_s %>`\n\n<%= action.examples.strip %>\n\n<%   end\n   end -%>\n\n<% if face.notes or face.respond_to? :indirection -%>\nNOTES\n-----\n<% if face.notes -%>\n<%= face.notes.strip %>\n\n<% end # notes\nif face.respond_to? :indirection -%>\nThis subcommand is an indirector face, which exposes `find`, `search`, `save`,\nand `destroy` actions for an indirected subsystem of Puppet. Valid termini for\nthis face include:\n\n* `<%= face.class.terminus_classes(face.indirection.name).join(\"`\\n* `\") %>`\n\n<% end # indirection\n   end # notes or indirection\n   unless face.authors.empty? -%>\nAUTHOR\n------\n<%= face.authors.join(\"\\n\").gsub(/^/, ' * ') %>\n\n<% end -%>\nCOPYRIGHT AND LICENSE\n---------------------\n<%= face.copyright %>\n<%= face.license %>\n"
  },
  {
    "path": "lib/puppet/face/help.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/application/face_base'\nrequire_relative '../../puppet/util/constant_inflector'\nrequire 'pathname'\nrequire 'erb'\n\nPuppet::Face.define(:help, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Display Puppet help.\")\n\n  action(:help) do\n    summary _(\"Display help about Puppet subcommands and their actions.\")\n    arguments _(\"[<subcommand>] [<action>]\")\n    returns _(\"Short help text for the specified subcommand or action.\")\n    examples _(<<-'EOT')\n      Get help for an action:\n\n      $ puppet help\n    EOT\n\n    option \"--version \" + _(\"VERSION\") do\n      summary _(\"The version of the subcommand for which to show help.\")\n    end\n\n    option \"--ronn\" do\n      summary _(\"Whether to render the help text in ronn format.\")\n      default_to { false }\n    end\n\n    default\n    when_invoked do |*args|\n      options = args.pop\n\n      unless options[:ronn]\n        if default_case?(args) || help_for_help?(args)\n          return erb('global.erb').result(binding)\n        end\n      end\n\n      if args.length > 2\n        # TRANSLATORS 'puppet help' is a command line and should not be translated\n        raise ArgumentError, _(\"The 'puppet help' command takes two (optional) arguments: a subcommand and an action\")\n      end\n\n      version = :current\n      if options.has_key? :version\n        if options[:version].to_s !~ /^current$/i\n          version = options[:version]\n        elsif args.length == 0\n          raise ArgumentError, _(\"Supplying a '--version' only makes sense when a Faces subcommand is given\")\n          # TRANSLATORS '--version' is a command line option and should not be translated\n        end\n      end\n\n      facename, actionname = args\n      if legacy_applications.include? facename\n        if actionname\n          raise ArgumentError, _(\"The legacy subcommand '%{sub_command}' does not support supplying an action\") % { sub_command: facename }\n        end\n\n        # legacy apps already emit ronn output\n        return render_application_help(facename)\n      elsif options[:ronn]\n        render_face_man(facename || :help)\n      # Calling `puppet help <app> --ronn` normally calls this action with\n      # <app> as the first argument in the `args` array. However, if <app>\n      # happens to match the name of an action, like `puppet help help\n      # --ronn`, then face_base \"eats\" the argument and `args` will be\n      # empty. Rather than force users to type `puppet help help help\n      # --ronn`, default the facename to `:help`\n      else\n        render_face_help(facename, actionname, version)\n      end\n    end\n  end\n\n  def default_case?(args)\n    args.empty?\n  end\n\n  def help_for_help?(args)\n    args.length == 1 && args.first == 'help'\n  end\n\n  def render_face_man(facename)\n    # set 'face' as it's used in the erb processing.\n    face = Puppet::Face[facename.to_sym, :current]\n    # avoid unused variable warning\n    _face = face\n    erb('man.erb').result(binding)\n  end\n\n  def render_application_help(applicationname)\n    Puppet::Application[applicationname].help\n  rescue StandardError, LoadError => detail\n    message = []\n    message << _('Could not load help for the application %{application_name}.') % { application_name: applicationname }\n    message << _('Please check the error logs for more information.')\n    message << ''\n    message << _('Detail: \"%{detail}\"') % { detail: detail.message }\n    fail ArgumentError, message.join(\"\\n\"), detail.backtrace\n  end\n\n  def render_face_help(facename, actionname, version)\n    face, action = load_face_help(facename, actionname, version)\n    template_for(face, action).result(binding)\n  rescue StandardError, LoadError => detail\n    message = []\n    message << _('Could not load help for the face %{face_name}.') % { face_name: facename }\n    message << _('Please check the error logs for more information.')\n    message << ''\n    message << _('Detail: \"%{detail}\"') % { detail: detail.message }\n    fail ArgumentError, message.join(\"\\n\"), detail.backtrace\n  end\n\n  def load_face_help(facename, actionname, version)\n    face = Puppet::Face[facename.to_sym, version]\n    if actionname\n      action = face.get_action(actionname.to_sym)\n      unless action\n        fail ArgumentError, _(\"Unable to load action %{actionname} from %{face}\") % { actionname: actionname, face: face }\n      end\n    end\n\n    [face, action]\n  end\n\n  def template_for(face, action)\n    if action.nil?\n      erb('face.erb')\n    else\n      erb('action.erb')\n    end\n  end\n\n  def erb(name)\n    template = (Pathname(__FILE__).dirname + \"help\" + name)\n    erb = Puppet::Util.create_erb(template.read)\n    erb.filename = template.to_s\n    erb\n  end\n\n  # Return a list of applications that are not simply just stubs for Faces.\n  def legacy_applications\n    Puppet::Application.available_application_names.reject do |appname|\n      is_face_app?(appname) or exclude_from_docs?(appname)\n    end.sort\n  end\n\n  def generate_summary(appname)\n    if is_face_app?(appname)\n      begin\n        face = Puppet::Face[appname, :current]\n        # Add deprecation message to summary if the face is deprecated\n        summary = face.deprecated? ? face.summary + ' ' + _(\"(Deprecated)\") : face.summary\n        [appname, summary, '  ']\n      rescue StandardError, LoadError\n        error_message = _(\"!%{sub_command}! Subcommand unavailable due to error.\") % { sub_command: appname }\n        error_message += ' ' + _(\"Check error logs.\")\n        [error_message, '', '  ']\n      end\n    else\n      begin\n        summary = Puppet::Application[appname].summary\n        if summary.empty?\n          summary = horribly_extract_summary_from(appname)\n        end\n        [appname, summary, '  ']\n      rescue StandardError, LoadError\n        error_message = _(\"!%{sub_command}! Subcommand unavailable due to error.\") % { sub_command: appname }\n        error_message += ' ' + _(\"Check error logs.\")\n        [error_message, '', '  ']\n      end\n    end\n  end\n\n  # Return a list of all applications (both legacy and Face applications), along with a summary\n  #  of their functionality.\n  # @return [Array] An Array of Arrays.  The outer array contains one entry per application; each\n  #  element in the outer array is a pair whose first element is a String containing the application\n  #  name, and whose second element is a String containing the summary for that application.\n  def all_application_summaries\n    available_application_names_special_sort().inject([]) do |result, appname|\n      next result if exclude_from_docs?(appname)\n\n      if appname == COMMON || appname == SPECIALIZED || appname == BLANK\n        result << appname\n      else\n        result << generate_summary(appname)\n      end\n    end\n  end\n\n  COMMON = 'Common:'\n  SPECIALIZED = 'Specialized:'\n  BLANK = \"\\n\"\n  COMMON_APPS = %w[apply agent config help lookup module resource]\n  def available_application_names_special_sort\n    full_list = Puppet::Application.available_application_names\n    a_list = full_list & COMMON_APPS\n    a_list = a_list.sort\n    also_ran = full_list - a_list\n    also_ran = also_ran.sort\n    [[COMMON], a_list, [BLANK], [SPECIALIZED], also_ran].flatten(1)\n  end\n\n  def common_app_summaries\n    COMMON_APPS.map do |appname|\n      generate_summary(appname)\n    end\n  end\n\n  def specialized_app_summaries\n    specialized_apps = Puppet::Application.available_application_names - COMMON_APPS\n    specialized_apps.filter_map do |appname|\n      generate_summary(appname) unless exclude_from_docs?(appname)\n    end\n  end\n\n  def horribly_extract_summary_from(appname)\n    help = Puppet::Application[appname].help.split(\"\\n\")\n    # Now we find the line with our summary, extract it, and return it.  This\n    # depends on the implementation coincidence of how our pages are\n    # formatted.  If we can't match the pattern we expect we return the empty\n    # string to ensure we don't blow up in the summary. --daniel 2011-04-11\n    while line = help.shift # rubocop:disable Lint/AssignmentInCondition\n      md = /^puppet-#{appname}\\([^)]+\\) -- (.*)$/.match(line)\n      if md\n        return md[1]\n      end\n    end\n    ''\n  end\n  # This should absolutely be a private method, but for some reason it appears\n  #  that you can't use the 'private' keyword inside of a Face definition.\n  #  See #14205.\n  # private :horribly_extract_summary_from\n\n  def exclude_from_docs?(appname)\n    %w[face_base indirection_base report status].include? appname\n  end\n  # This should absolutely be a private method, but for some reason it appears\n  #  that you can't use the 'private' keyword inside of a Face definition.\n  #  See #14205.\n  # private :exclude_from_docs?\n\n  def is_face_app?(appname)\n    clazz = Puppet::Application.find(appname)\n\n    clazz.ancestors.include?(Puppet::Application::FaceBase)\n  end\n  # This should probably be a private method, but for some reason it appears\n  #  that you can't use the 'private' keyword inside of a Face definition.\n  #  See #14205.\n  # private :is_face_app?\nend\n"
  },
  {
    "path": "lib/puppet/face/module/changes.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Face.define(:module, '1.0.0') do\n  action(:changes) do\n    summary _(\"Show modified files of an installed module.\")\n    description <<-EOT\n      Shows any files in a module that have been modified since it was\n      installed. This action compares the files on disk to the md5 checksums\n      included in the module's checksums.json or, if that is missing, in\n      metadata.json.\n    EOT\n\n    returns _(\"Array of strings representing paths of modified files.\")\n\n    examples <<-EOT\n      Show modified files of an installed module:\n\n      $ puppet module changes /etc/puppetlabs/code/modules/vcsrepo/\n      warning: 1 files modified\n      lib/puppet/provider/vcsrepo.rb\n    EOT\n\n    arguments _(\"<path>\")\n\n    when_invoked do |path, options|\n      Puppet::ModuleTool.set_option_defaults options\n      root_path = Puppet::ModuleTool.find_module_root(path)\n      unless root_path\n        raise ArgumentError, _(\"Could not find a valid module at %{path}\") % { path: path.inspect }\n      end\n\n      Puppet::ModuleTool::Applications::Checksummer.run(root_path, options)\n    end\n\n    when_rendering :console do |return_value|\n      if return_value.empty?\n        Puppet.notice _(\"No modified files\")\n      else\n        Puppet.warning _(\"%{count} files modified\") % { count: return_value.size }\n      end\n      return_value.map(&:to_s).join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/module/install.rb",
    "content": "# encoding: UTF-8\n# frozen_string_literal: true\n\nrequire_relative '../../../puppet/forge'\nrequire_relative '../../../puppet/module_tool/install_directory'\nrequire 'pathname'\n\nPuppet::Face.define(:module, '1.0.0') do\n  action(:install) do\n    summary _(\"Install a module from the Puppet Forge or a release archive.\")\n    description <<-EOT\n      Installs a module from the Puppet Forge or from a release archive file.\n      Note: Module install uses MD5 checksums, which are prohibited on FIPS enabled systems.\n\n      The specified module will be installed into the directory\n      specified with the `--target-dir` option, which defaults to the first\n      directory in the modulepath.\n    EOT\n\n    returns _(\"Pathname object representing the path to the installed module.\")\n\n    examples <<-'EOT'\n      Install a module:\n\n      $ puppet module install puppetlabs-vcsrepo\n      Preparing to install into /etc/puppetlabs/code/modules ...\n      Downloading from https://forgeapi.puppet.com ...\n      Installing -- do not interrupt ...\n      /etc/puppetlabs/code/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n      Install a module to a specific environment:\n\n      $ puppet module install puppetlabs-vcsrepo --environment development\n      Preparing to install into /etc/puppetlabs/code/environments/development/modules ...\n      Downloading from https://forgeapi.puppet.com ...\n      Installing -- do not interrupt ...\n      /etc/puppetlabs/code/environments/development/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n      Install a specific module version:\n\n      $ puppet module install puppetlabs-vcsrepo -v 0.0.4\n      Preparing to install into /etc/puppetlabs/modules ...\n      Downloading from https://forgeapi.puppet.com ...\n      Installing -- do not interrupt ...\n      /etc/puppetlabs/code/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n      Install a module into a specific directory:\n\n      $ puppet module install puppetlabs-vcsrepo --target-dir=/opt/puppetlabs/puppet/modules\n      Preparing to install into /opt/puppetlabs/puppet/modules ...\n      Downloading from https://forgeapi.puppet.com ...\n      Installing -- do not interrupt ...\n      /opt/puppetlabs/puppet/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n      Install a module into a specific directory and check for dependencies in other directories:\n\n      $ puppet module install puppetlabs-vcsrepo --target-dir=/opt/puppetlabs/puppet/modules --modulepath /etc/puppetlabs/code/modules\n      Preparing to install into /opt/puppetlabs/puppet/modules ...\n      Downloading from https://forgeapi.puppet.com ...\n      Installing -- do not interrupt ...\n      /opt/puppetlabs/puppet/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n      Install a module from a release archive:\n\n      $ puppet module install puppetlabs-vcsrepo-0.0.4.tar.gz\n      Preparing to install into /etc/puppetlabs/code/modules ...\n      Downloading from https://forgeapi.puppet.com ...\n      Installing -- do not interrupt ...\n      /etc/puppetlabs/code/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n      Install a module from a release archive and ignore dependencies:\n\n      $ puppet module install puppetlabs-vcsrepo-0.0.4.tar.gz --ignore-dependencies\n      Preparing to install into /etc/puppetlabs/code/modules ...\n      Installing -- do not interrupt ...\n      /etc/puppetlabs/code/modules\n      └── puppetlabs-vcsrepo (v0.0.4)\n\n    EOT\n\n    arguments _(\"<name>\")\n\n    option \"--force\", \"-f\" do\n      summary _(\"Force overwrite of existing module, if any. (Implies --ignore-dependencies.)\")\n      description <<-EOT\n        Force overwrite of existing module, if any.\n        Implies --ignore-dependencies.\n      EOT\n    end\n\n    option \"--target-dir DIR\", \"-i DIR\" do\n      summary _(\"The directory into which modules are installed.\")\n      description <<-EOT\n        The directory into which modules are installed; defaults to the first\n        directory in the modulepath.\n\n        Specifying this option will change the installation directory, and\n        will use the existing modulepath when checking for dependencies. If\n        you wish to check a different set of directories for dependencies, you\n        must also use the `--environment` or `--modulepath` options.\n      EOT\n    end\n\n    option \"--ignore-dependencies\" do\n      summary _(\"Do not attempt to install dependencies. (Implied by --force.)\")\n      description <<-EOT\n        Do not attempt to install dependencies. Implied by --force.\n      EOT\n    end\n\n    option \"--version VER\", \"-v VER\" do\n      summary _(\"Module version to install.\")\n      description <<-EOT\n        Module version to install; can be an exact version or a requirement string,\n        eg '>= 1.0.3'. Defaults to latest version.\n      EOT\n    end\n\n    when_invoked do |name, options|\n      Puppet::ModuleTool.set_option_defaults options\n      Puppet.notice _(\"Preparing to install into %{dir} ...\") % { dir: options[:target_dir] }\n\n      install_dir = Puppet::ModuleTool::InstallDirectory.new(Pathname.new(options[:target_dir]))\n      Puppet::ModuleTool::Applications::Installer.run(name, install_dir, options)\n    end\n\n    when_rendering :console do |return_value, name, _options|\n      case return_value[:result]\n      when :noop\n        Puppet.notice _(\"Module %{name} %{version} is already installed.\") % { name: name, version: return_value[:version] }\n        exit 0\n      when :failure\n        Puppet.err(return_value[:error][:multiline])\n        exit 1\n      else\n        tree = Puppet::ModuleTool.build_tree(return_value[:graph], return_value[:install_dir])\n\n        \"#{return_value[:install_dir]}\\n\" +\n          Puppet::ModuleTool.format_tree(tree)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/module/list.rb",
    "content": "# encoding: UTF-8\n# frozen_string_literal: true\n\nPuppet::Face.define(:module, '1.0.0') do\n  action(:list) do\n    summary _(\"List installed modules\")\n    description <<-HEREDOC\n      Lists the installed puppet modules. By default, this action scans the\n      modulepath from puppet.conf's `[main]` block; use the --modulepath\n      option to change which directories are scanned.\n\n      The output of this action includes information from the module's\n      metadata, including version numbers and unmet module dependencies.\n    HEREDOC\n    returns _(\"hash of paths to module objects\")\n\n    option \"--tree\" do\n      summary _(\"Whether to show dependencies as a tree view\")\n    end\n\n    examples <<-'EOT'\n      List installed modules:\n\n      $ puppet module list\n        /etc/puppetlabs/code/modules\n        ├── bodepd-create_resources (v0.0.1)\n        ├── puppetlabs-bacula (v0.0.2)\n        ├── puppetlabs-mysql (v0.0.1)\n        ├── puppetlabs-sqlite (v0.0.1)\n        └── puppetlabs-stdlib (v2.2.1)\n        /opt/puppetlabs/puppet/modules (no modules installed)\n\n      List installed modules in a tree view:\n\n      $ puppet module list --tree\n        /etc/puppetlabs/code/modules\n        └─┬ puppetlabs-bacula (v0.0.2)\n          ├── puppetlabs-stdlib (v2.2.1)\n          ├─┬ puppetlabs-mysql (v0.0.1)\n          │ └── bodepd-create_resources (v0.0.1)\n          └── puppetlabs-sqlite (v0.0.1)\n        /opt/puppetlabs/puppet/modules (no modules installed)\n\n      List installed modules from a specified environment:\n\n      $ puppet module list --environment production\n        /etc/puppetlabs/code/modules\n        ├── bodepd-create_resources (v0.0.1)\n        ├── puppetlabs-bacula (v0.0.2)\n        ├── puppetlabs-mysql (v0.0.1)\n        ├── puppetlabs-sqlite (v0.0.1)\n        └── puppetlabs-stdlib (v2.2.1)\n        /opt/puppetlabs/puppet/modules (no modules installed)\n\n      List installed modules from a specified modulepath:\n\n      $ puppet module list --modulepath /opt/puppetlabs/puppet/modules\n        /opt/puppetlabs/puppet/modules (no modules installed)\n    EOT\n\n    when_invoked do |options|\n      Puppet::ModuleTool.set_option_defaults(options)\n      environment = options[:environment_instance]\n      modules_by_path = environment.modules_by_path\n\n      {\n        :environment => environment,\n        :modules_by_path => modules_by_path,\n        :unmet_dependencies => unmet_dependencies(environment),\n      }\n    end\n\n    when_rendering :console do |result, options|\n      environment     = result[:environment]\n      modules_by_path = result[:modules_by_path]\n\n      output = ''.dup\n\n      warn_unmet_dependencies(environment)\n\n      environment.modulepath.each do |path|\n        modules = modules_by_path[path]\n        no_mods = modules.empty? ? _(' (no modules installed)') : ''\n        output << \"#{path}#{no_mods}\\n\"\n\n        if options[:tree]\n          # The modules with fewest things depending on them will be the\n          # parent of the tree.  Can't assume to start with 0 dependencies\n          # since dependencies may be cyclical.\n          modules_by_num_requires = modules.sort_by { |m| m.required_by.size }\n          @seen = {}\n          tree = list_build_tree(modules_by_num_requires, [], nil,\n                                 :label_unmet => true, :path => path, :label_invalid => false)\n        else\n          tree = []\n          modules.sort_by { |mod| mod.forge_name or mod.name }.each do |mod|\n            tree << list_build_node(mod, path, :label_unmet => false,\n                                               :path => path, :label_invalid => true)\n          end\n        end\n\n        output << Puppet::ModuleTool.format_tree(tree)\n      end\n\n      output\n    end\n  end\n\n  def unmet_dependencies(environment)\n    error_types = [:non_semantic_version, :version_mismatch, :missing]\n\n    unmet_deps = {}\n    error_types.each do |type|\n      unmet_deps[type] = Hash.new do |hash, key|\n        hash[key] = { :errors => [], :parent => nil }\n      end\n    end\n\n    # Prepare the unmet dependencies for display on the console.\n    environment.modules.sort_by(&:name).each do |mod|\n      unmet_grouped = Hash.new { |h, k| h[k] = [] }\n      unmet_grouped = mod.unmet_dependencies.each_with_object(unmet_grouped) do |dep, acc|\n        acc[dep[:reason]] << dep\n      end\n      unmet_grouped.each do |type, deps|\n        next if deps.empty?\n\n        unmet_grouped[type].sort_by { |dep| dep[:name] }.each do |dep|\n          dep_name           = dep[:name].tr('/', '-')\n          installed_version  = dep[:mod_details][:installed_version]\n          version_constraint = dep[:version_constraint]\n          parent_name        = dep[:parent][:name].tr('/', '-')\n          parent_version     = dep[:parent][:version]\n\n          msg = _(\"'%{parent_name}' (%{parent_version}) requires '%{dependency_name}' (%{dependency_version})\") % { parent_name: parent_name, parent_version: parent_version, dependency_name: dep_name, dependency_version: version_constraint }\n          unmet_deps[type][dep[:name]][:errors] << msg\n          unmet_deps[type][dep[:name]][:parent] = {\n            :name => dep[:parent][:name],\n            :version => parent_version\n          }\n          unmet_deps[type][dep[:name]][:version] = installed_version\n        end\n      end\n    end\n    unmet_deps\n  end\n\n  def warn_unmet_dependencies(environment)\n    @unmet_deps = unmet_dependencies(environment)\n\n    # Display unmet dependencies by category.\n    error_display_order = [:non_semantic_version, :version_mismatch, :missing]\n    error_display_order.each do |type|\n      next if @unmet_deps[type].empty?\n\n      @unmet_deps[type].keys.sort.each do |dep|\n        name    = dep.tr('/', '-')\n        errors  = @unmet_deps[type][dep][:errors]\n        version = @unmet_deps[type][dep][:version]\n\n        msg = case type\n              when :version_mismatch\n                _(\"Module '%{name}' (v%{version}) fails to meet some dependencies:\\n\") % { name: name, version: version }\n              when :non_semantic_version\n                _(\"Non semantic version dependency %{name} (v%{version}):\\n\") % { name: name, version: version }\n              else\n                _(\"Missing dependency '%{name}':\\n\") % { name: name }\n              end\n\n        errors.each { |error_string| msg << \"  #{error_string}\\n\" }\n        Puppet.warning msg.chomp\n      end\n    end\n  end\n\n  # Prepare a list of module objects and their dependencies for print in a\n  # tree view.\n  #\n  # Returns an Array of Hashes\n  #\n  # Example:\n  #\n  #   [\n  #     {\n  #       :text => \"puppetlabs-bacula (v0.0.2)\",\n  #       :dependencies=> [\n  #         { :text => \"puppetlabs-stdlib (v2.2.1)\", :dependencies => [] },\n  #         {\n  #           :text => \"puppetlabs-mysql (v1.0.0)\"\n  #           :dependencies => [\n  #             {\n  #               :text => \"bodepd-create_resources (v0.0.1)\",\n  #               :dependencies => []\n  #             }\n  #           ]\n  #         },\n  #         { :text => \"puppetlabs-sqlite (v0.0.1)\", :dependencies => [] },\n  #       ]\n  #     }\n  #   ]\n  #\n  # When the above data structure is passed to Puppet::ModuleTool.build_tree\n  # you end up with something like this:\n  #\n  #   /etc/puppetlabs/code/modules\n  #   └─┬ puppetlabs-bacula (v0.0.2)\n  #     ├── puppetlabs-stdlib (v2.2.1)\n  #     ├─┬ puppetlabs-mysql (v1.0.0)\n  #     │ └── bodepd-create_resources (v0.0.1)\n  #     └── puppetlabs-sqlite (v0.0.1)\n  #\n  def list_build_tree(list, ancestors = [], parent = nil, params = {})\n    list.filter_map do |mod|\n      next if @seen[(mod.forge_name or mod.name)]\n\n      node = list_build_node(mod, parent, params)\n      @seen[(mod.forge_name or mod.name)] = true\n\n      unless ancestors.include?(mod)\n        node[:dependencies] ||= []\n        missing_deps = mod.unmet_dependencies.select do |dep|\n          dep[:reason] == :missing\n        end\n        missing_deps.map do |mis_mod|\n          str = \"#{colorize(:bg_red, _('UNMET DEPENDENCY'))} #{mis_mod[:name].tr('/', '-')} \"\n          str << \"(#{colorize(:cyan, mis_mod[:version_constraint])})\"\n          node[:dependencies] << { :text => str }\n        end\n        node[:dependencies] += list_build_tree(mod.dependencies_as_modules,\n                                               ancestors + [mod], mod, params)\n      end\n\n      node\n    end\n  end\n\n  # Prepare a module object for print in a tree view.  Each node in the tree\n  # must be a Hash in the following format:\n  #\n  #    { :text => \"puppetlabs-mysql (v1.0.0)\" }\n  #\n  # The value of a module's :text is affected by three (3) factors: the format\n  # of the tree, its dependency status, and the location in the modulepath\n  # relative to its parent.\n  #\n  # Returns a Hash\n  #\n  def list_build_node(mod, parent, params)\n    str = ''.dup\n    str << (mod.forge_name ? mod.forge_name.tr('/', '-') : mod.name)\n    str << ' (' + colorize(:cyan, mod.version ? \"v#{mod.version}\" : '???') + ')'\n\n    unless File.dirname(mod.path) == params[:path]\n      str << \" [#{File.dirname(mod.path)}]\"\n    end\n\n    if @unmet_deps[:version_mismatch].include?(mod.forge_name)\n      if params[:label_invalid]\n        str << '  ' + colorize(:red, _('invalid'))\n      elsif parent.respond_to?(:forge_name)\n        unmet_parent = @unmet_deps[:version_mismatch][mod.forge_name][:parent]\n        if unmet_parent[:name] == parent.forge_name &&\n           unmet_parent[:version] == \"v#{parent.version}\"\n          str << '  ' + colorize(:red, _('invalid'))\n        end\n      end\n    end\n\n    { :text => str }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/module/uninstall.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Face.define(:module, '1.0.0') do\n  action(:uninstall) do\n    summary _(\"Uninstall a puppet module.\")\n    description <<-EOT\n      Uninstalls a puppet module from the modulepath (or a specific\n      target directory).\n      Note: Module uninstall uses MD5 checksums, which are prohibited on FIPS enabled systems.\n    EOT\n\n    returns _(\"Hash of module objects representing uninstalled modules and related errors.\")\n\n    examples <<-'EOT'\n      Uninstall a module:\n\n      $ puppet module uninstall puppetlabs-ssh\n      Removed /etc/puppetlabs/code/modules/ssh (v1.0.0)\n\n      Uninstall a module from a specific directory:\n\n      $ puppet module uninstall puppetlabs-ssh --modulepath /opt/puppetlabs/puppet/modules\n      Removed /opt/puppetlabs/puppet/modules/ssh (v1.0.0)\n\n      Uninstall a module from a specific environment:\n\n      $ puppet module uninstall puppetlabs-ssh --environment development\n      Removed /etc/puppetlabs/code/environments/development/modules/ssh (v1.0.0)\n\n      Uninstall a specific version of a module:\n\n      $ puppet module uninstall puppetlabs-ssh --version 2.0.0\n      Removed /etc/puppetlabs/code/modules/ssh (v2.0.0)\n    EOT\n\n    arguments _(\"<name>\")\n\n    option \"--force\", \"-f\" do\n      summary _(\"Force uninstall of an installed module.\")\n      description <<-EOT\n        Force the uninstall of an installed module even if there are local\n        changes or the possibility of causing broken dependencies.\n      EOT\n    end\n\n    option \"--ignore-changes\", \"-c\" do\n      summary _(\"Ignore any local changes made. (Implied by --force.)\")\n      description <<-EOT\n        Uninstall an installed module even if there are local changes to it.  (Implied by --force.)\n      EOT\n    end\n\n    option \"--version=\" do\n      summary _(\"The version of the module to uninstall\")\n      description <<-EOT\n        The version of the module to uninstall. When using this option, a module\n        matching the specified version must be installed or else an error is raised.\n      EOT\n    end\n\n    when_invoked do |name, options|\n      name = name.tr('/', '-')\n\n      Puppet::ModuleTool.set_option_defaults options\n      message = if options[:version]\n                  module_version = colorize(:cyan, options[:version].sub(/^(?=\\d)/, 'v'))\n                  _(\"Preparing to uninstall '%{name}' (%{module_version}) ...\") % { name: name, module_version: module_version }\n                else\n                  _(\"Preparing to uninstall '%{name}' ...\") % { name: name }\n                end\n      Puppet.notice message\n      Puppet::ModuleTool::Applications::Uninstaller.run(name, options)\n    end\n\n    when_rendering :console do |return_value|\n      if return_value[:result] == :failure\n        Puppet.err(return_value[:error][:multiline])\n        exit 1\n      else\n        mod = return_value[:affected_modules].first\n        message = if mod.version\n                    module_version = colorize(:cyan, mod.version.to_s.sub(/^(?=\\d)/, 'v'))\n                    _(\"Removed '%{name}' (%{module_version}) from %{path}\") % { name: return_value[:module_name], module_version: module_version, path: mod.modulepath }\n                  else\n                    _(\"Removed '%{name}' from %{path}\") % { name: return_value[:module_name], path: mod.modulepath }\n                  end\n        message\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/module/upgrade.rb",
    "content": "# encoding: UTF-8\n# frozen_string_literal: true\n\nPuppet::Face.define(:module, '1.0.0') do\n  action(:upgrade) do\n    summary _(\"Upgrade a puppet module.\")\n    description <<-EOT\n      Upgrades a puppet module.\n      Note: Module upgrade uses MD5 checksums, which are prohibited on FIPS enabled systems.\n    EOT\n\n    returns \"Hash\"\n\n    examples <<-EOT\n      upgrade an installed module to the latest version\n\n      $ puppet module upgrade puppetlabs-apache\n      /etc/puppetlabs/puppet/modules\n      └── puppetlabs-apache (v1.0.0 -> v2.4.0)\n\n      upgrade an installed module to a specific version\n\n      $ puppet module upgrade puppetlabs-apache --version 2.1.0\n      /etc/puppetlabs/puppet/modules\n      └── puppetlabs-apache (v1.0.0 -> v2.1.0)\n\n      upgrade an installed module for a specific environment\n\n      $ puppet module upgrade puppetlabs-apache --environment test\n      /etc/puppetlabs/code/environments/test/modules\n      └── puppetlabs-apache (v1.0.0 -> v2.4.0)\n    EOT\n\n    arguments _(\"<name>\")\n\n    option \"--force\", \"-f\" do\n      summary _(\"Force upgrade of an installed module. (Implies --ignore-dependencies.)\")\n      description <<-EOT\n        Force the upgrade of an installed module even if there are local\n        changes or the possibility of causing broken dependencies.\n        Implies --ignore-dependencies.\n      EOT\n    end\n\n    option \"--ignore-dependencies\" do\n      summary _(\"Do not attempt to install dependencies. (Implied by --force.)\")\n      description <<-EOT\n        Do not attempt to install dependencies. Implied by --force.\n      EOT\n    end\n\n    option \"--ignore-changes\", \"-c\" do\n      summary _(\"Ignore and overwrite any local changes made. (Implied by --force.)\")\n      description <<-EOT\n        Upgrade an installed module even if there are local changes to it.  (Implied by --force.)\n      EOT\n    end\n\n    option \"--version=\" do\n      summary _(\"The version of the module to upgrade to.\")\n      description <<-EOT\n        The version of the module to upgrade to.\n      EOT\n    end\n\n    when_invoked do |name, options|\n      name = name.tr('/', '-')\n      Puppet.notice _(\"Preparing to upgrade '%{name}' ...\") % { name: name }\n      Puppet::ModuleTool.set_option_defaults options\n      Puppet::ModuleTool::Applications::Upgrader.new(name, options).run\n    end\n\n    when_rendering :console do |return_value|\n      case return_value[:result]\n      when :noop\n        Puppet.notice return_value[:error][:multiline]\n        exit 0\n      when :failure\n        Puppet.err(return_value[:error][:multiline])\n        exit 1\n      else\n        tree = Puppet::ModuleTool.build_tree(return_value[:graph], return_value[:base_dir])\n\n        \"#{return_value[:base_dir]}\\n\" +\n          Puppet::ModuleTool.format_tree(tree)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/module.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/module_tool'\nrequire_relative '../../puppet/util/colors'\n\nPuppet::Face.define(:module, '1.0.0') do\n  extend Puppet::Util::Colors\n\n  copyright \"Puppet Inc.\", 2012\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Creates, installs and searches for modules on the Puppet Forge.\")\n  description <<-EOT\n    This subcommand can find, install, and manage modules from the Puppet Forge,\n    a repository of user-contributed Puppet code. It can also generate empty\n    modules, and prepare locally developed modules for release on the Forge.\n  EOT\n\n  display_global_options \"environment\", \"modulepath\"\nend\n"
  },
  {
    "path": "lib/puppet/face/node/clean.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Face.define(:node, '0.0.1') do\n  action(:clean) do\n    summary _(\"Clean up signed certs, cached facts, node objects, and reports for a node stored by the puppetmaster\")\n    arguments _(\"<host1> [<host2> ...]\")\n    description <<-'EOT'\n      Cleans up the following information a puppet master knows about a node:\n\n      <Signed certificates> - ($vardir/ssl/ca/signed/node.domain.pem)\n\n      <Cached facts> - ($vardir/yaml/facts/node.domain.yaml)\n\n      <Cached node objects> - ($vardir/yaml/node/node.domain.yaml)\n\n      <Reports> - ($vardir/reports/node.domain)\n\n      NOTE: this action now cleans up certs via Puppet Server's CA API. A running server is required for certs to be cleaned.\n    EOT\n\n    when_invoked do |*args|\n      nodes = args[0..-2]\n      options = args.last\n      raise _(\"At least one node should be passed\") if nodes.empty? || nodes == options\n\n      # This seems really bad; run_mode should be set as part of a class\n      # definition, and should not be modifiable beyond that.  This is one of\n      # the only places left in the code that tries to manipulate it. Other\n      # parts of code that handle certificates behave differently if the\n      # run_mode is server. Those other behaviors are needed for cleaning the\n      # certificates correctly.\n      Puppet.settings.preferred_run_mode = \"server\"\n\n      Puppet::Node::Facts.indirection.terminus_class = :yaml\n      Puppet::Node::Facts.indirection.cache_class = :yaml\n      Puppet::Node.indirection.terminus_class = :yaml\n      Puppet::Node.indirection.cache_class = :yaml\n\n      nodes.each { |node| cleanup(node.downcase) }\n    end\n  end\n\n  def cleanup(node)\n    clean_cert(node)\n    clean_cached_facts(node)\n    clean_cached_node(node)\n    clean_reports(node)\n  end\n\n  class LoggerIO\n    def debug(message)\n      Puppet.debug(message)\n    end\n\n    def warn(message)\n      Puppet.warning(message) unless message =~ /cadir is currently configured to be inside/\n    end\n\n    def err(message)\n      Puppet.err(message) unless message =~ /^\\s*Error:\\s*/\n    end\n\n    def inform(message)\n      Puppet.notice(message)\n    end\n  end\n\n  # clean signed cert for +host+\n  def clean_cert(node)\n    if Puppet.features.puppetserver_ca?\n      Puppetserver::Ca::Action::Clean.new(LoggerIO.new).run({ 'certnames' => [node] })\n    else\n      Puppet.info _(\"Not managing %{node} certs as this host is not a CA\") % { node: node }\n    end\n  end\n\n  # clean facts for +host+\n  def clean_cached_facts(node)\n    Puppet::Node::Facts.indirection.destroy(node)\n    Puppet.info _(\"%{node}'s facts removed\") % { node: node }\n  end\n\n  # clean cached node +host+\n  def clean_cached_node(node)\n    Puppet::Node.indirection.destroy(node)\n    Puppet.info _(\"%{node}'s cached node removed\") % { node: node }\n  end\n\n  # clean node reports for +host+\n  def clean_reports(node)\n    Puppet::Transaction::Report.indirection.destroy(node)\n    Puppet.info _(\"%{node}'s reports removed\") % { node: node }\n  end\n\n  def environment\n    @environment ||= Puppet.lookup(:current_environment)\n  end\n\n  def type_is_ensurable(resource)\n    if (type = Puppet::Type.type(resource.restype)) && type.validattr?(:ensure)\n      return true\n    else\n      type = environment.known_resource_types.find_definition(resource.restype)\n      return true if type && type.arguments.keys.include?('ensure')\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/node.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/face'\nPuppet::Indirector::Face.define(:node, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"View and manage node definitions.\")\n  description <<-'EOT'\n    This subcommand interacts with node objects, which are used by Puppet to\n    build a catalog. A node object consists of the node's facts, environment,\n    node parameters (exposed in the parser as top-scope variables), and classes.\n  EOT\n\n  deactivate_action(:destroy)\n  deactivate_action(:search)\n  deactivate_action(:save)\n\n  find = get_action(:find)\n  find.summary _(\"Retrieve a node object.\")\n  find.arguments _(\"<host>\")\n  # TRANSLATORS the following are specific names and should not be translated `classes`, `environment`, `expiration`, `name`, `parameters`, Puppet::Node\n  find.returns _(<<-'EOT')\n    A hash containing the node's `classes`, `environment`, `expiration`, `name`,\n    `parameters` (its facts, combined with any ENC-set parameters), and `time`.\n    When used from the Ruby API: a Puppet::Node object.\n\n    RENDERING ISSUES: Rendering as string and json are currently broken;\n    node objects can only be rendered as yaml.\n  EOT\n  find.examples <<-'EOT'\n    Retrieve an \"empty\" (no classes, no ENC-imposed parameters, and an\n    environment of \"production\") node:\n\n    $ puppet node find somenode.puppetlabs.lan --terminus plain --render-as yaml\n\n    Retrieve a node using the Puppet Server's configured ENC:\n\n    $ puppet node find somenode.puppetlabs.lan --terminus exec --run_mode server --render-as yaml\n\n    Retrieve the same node from the Puppet Server:\n\n    $ puppet node find somenode.puppetlabs.lan --terminus rest --render-as yaml\n  EOT\nend\n"
  },
  {
    "path": "lib/puppet/face/parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/parser'\n\nPuppet::Face.define(:parser, '0.0.1') do\n  copyright \"Puppet Inc.\", 2014\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Interact directly with the parser.\")\n\n  action :validate do\n    summary _(\"Validate the syntax of one or more Puppet manifests.\")\n    arguments _(\"[<manifest>] [<manifest> ...]\")\n    returns _(\"Nothing, or the first syntax error encountered.\")\n\n    description <<-'EOT'\n      This action validates Puppet DSL syntax without compiling a catalog or\n      syncing any resources. If no manifest files are provided, it will\n      validate the default site manifest.\n\n      When validating multiple issues per file are reported up\n      to the settings of max_error, and max_warnings. The processing stops\n      after having reported issues for the first encountered file with errors.\n    EOT\n    examples <<-'EOT'\n      Validate the default site manifest at /etc/puppetlabs/puppet/manifests/site.pp:\n\n      $ puppet parser validate\n\n      Validate two arbitrary manifest files:\n\n      $ puppet parser validate init.pp vhost.pp\n\n      Validate from STDIN:\n\n      $ cat init.pp | puppet parser validate\n    EOT\n    when_invoked do |*args|\n      files = args.slice(0..-2)\n\n      parse_errors = {}\n\n      if files.empty?\n        if !STDIN.tty?\n          Puppet[:code] = STDIN.read\n          error = validate_manifest(nil)\n          parse_errors['STDIN'] = error if error\n        else\n          manifest = Puppet.lookup(:current_environment).manifest\n          files << manifest\n          Puppet.notice _(\"No manifest specified. Validating the default manifest %{manifest}\") % { manifest: manifest }\n        end\n      end\n\n      missing_files = []\n\n      files.each do |file|\n        if Puppet::FileSystem.exist?(file)\n          error = validate_manifest(file)\n          parse_errors[file] = error if error\n        else\n          missing_files << file\n        end\n      end\n\n      unless missing_files.empty?\n        raise Puppet::Error, _(\"One or more file(s) specified did not exist:\\n%{files}\") % { files: missing_files.collect { |f| \" \" * 3 + f + \"\\n\" } }\n      end\n\n      parse_errors\n    end\n\n    when_rendering :console do |errors|\n      unless errors.empty?\n        errors.each { |_, error| Puppet.log_exception(error) }\n\n        exit(1)\n      end\n\n      # Prevent face_base renderer from outputting \"null\"\n      exit(0)\n    end\n\n    when_rendering :json do |errors|\n      unless errors.empty?\n        ignore_error_keys = [:arguments, :environment, :node]\n\n        data = errors.to_h do |file, error|\n          file_errors = error.to_h.reject { |k, _| ignore_error_keys.include?(k) }\n          [file, file_errors]\n        end\n\n        puts Puppet::Util::Json.dump(Puppet::Pops::Serialization::ToDataConverter.convert(data, rich_data: false, symbol_as_string: true), :pretty => true)\n\n        exit(1)\n      end\n\n      # Prevent face_base renderer from outputting \"null\"\n      exit(0)\n    end\n  end\n\n  action(:dump) do\n    summary _(\"Outputs a dump of the internal parse tree for debugging\")\n    arguments \"[--format <old|pn|json>] [--pretty] { -e <source> | [<templates> ...] } \"\n    returns _(\"A dump of the resulting AST model unless there are syntax or validation errors.\")\n    description <<-'EOT'\n      This action parses and validates the Puppet DSL syntax without compiling a catalog\n      or syncing any resources.\n\n      The output format can be controlled using the --format <old|pn|json> where:\n      * 'old' is the default, but now deprecated format which is not API.\n      * 'pn' is the Puppet Extended S-Expression Notation.\n      * 'json' outputs the same graph as 'pn' but with JSON syntax.\n\n      The output will be \"pretty printed\" when the option --pretty is given together with --format 'pn' or 'json'.\n      This option has no effect on the 'old' format.\n\n      The command accepts one or more manifests (.pp) files, or an -e followed by the puppet\n      source text.\n      If no arguments are given, the stdin is read (unless it is attached to a terminal)\n\n      The output format of the dumped tree is intended for debugging purposes and is\n      not API, it may change from time to time.\n    EOT\n\n    option \"--e \" + _(\"<source>\") do\n      default_to { nil }\n      summary _(\"dump one source expression given on the command line.\")\n    end\n\n    option(\"--[no-]validate\") do\n      summary _(\"Whether or not to validate the parsed result, if no-validate only syntax errors are reported\")\n    end\n\n    option('--format ' + _('<old, pn, or json>')) do\n      summary _(\"Get result in 'old' (deprecated format), 'pn' (new format), or 'json' (new format in JSON).\")\n    end\n\n    option('--pretty') do\n      summary _('Pretty print output. Only applicable together with --format pn or json')\n    end\n\n    when_invoked do |*args|\n      require_relative '../../puppet/pops'\n      options = args.pop\n      if options[:e]\n        dump_parse(options[:e], 'command-line-string', options, false)\n      elsif args.empty?\n        if !STDIN.tty?\n          dump_parse(STDIN.read, 'stdin', options, false)\n        else\n          raise Puppet::Error, _(\"No input to parse given on command line or stdin\")\n        end\n      else\n        files = args\n        available_files = files.select do |file|\n          Puppet::FileSystem.exist?(file)\n        end\n        missing_files = files - available_files\n\n        dumps = available_files.collect do |file|\n          dump_parse(Puppet::FileSystem.read(file, :encoding => 'utf-8'), file, options)\n        end.join(\"\")\n\n        if missing_files.empty?\n          dumps\n        else\n          dumps + _(\"One or more file(s) specified did not exist:\\n\") + missing_files.collect { |f| \"   #{f}\" }.join(\"\\n\")\n        end\n      end\n    end\n  end\n\n  def dump_parse(source, filename, options, show_filename = true)\n    output = ''.dup\n    evaluating_parser = Puppet::Pops::Parser::EvaluatingParser.new\n    begin\n      if options[:validate]\n        parse_result = evaluating_parser.parse_string(source, filename)\n      else\n        # side step the assert_and_report step\n        parse_result = evaluating_parser.parser.parse_string(source)\n      end\n      if show_filename\n        output << \"--- #{filename}\"\n      end\n      fmt = options[:format]\n      if fmt.nil? || fmt == 'old'\n        output << Puppet::Pops::Model::ModelTreeDumper.new.dump(parse_result) << \"\\n\"\n      else\n        require_relative '../../puppet/pops/pn'\n        pn = Puppet::Pops::Model::PNTransformer.transform(parse_result)\n        case fmt\n        when 'json'\n          options[:pretty] ? JSON.pretty_unparse(pn.to_data) : JSON.dump(pn.to_data)\n        else\n          pn.format(options[:pretty] ? Puppet::Pops::PN::Indent.new('  ') : nil, output)\n        end\n      end\n    rescue Puppet::ParseError => detail\n      if show_filename\n        Puppet.err(\"--- #{filename}\")\n      end\n      Puppet.err(detail.message)\n      \"\"\n    end\n  end\n\n  # @api private\n  def validate_manifest(manifest = nil)\n    env = Puppet.lookup(:current_environment)\n    loaders = Puppet::Pops::Loaders.new(env)\n\n    Puppet.override({ :loaders => loaders }, _('For puppet parser validate')) do\n      validation_environment = manifest ? env.override_with(:manifest => manifest) : env\n      validation_environment.check_for_reparse\n      validation_environment.known_resource_types.clear\n    rescue Puppet::ParseError => parse_error\n      return parse_error\n    end\n\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/plugin.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\nrequire_relative '../../puppet/configurer/plugin_handler'\n\nPuppet::Face.define(:plugin, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Interact with the Puppet plugin system.\")\n  description <<-'EOT'\n    This subcommand provides network access to the puppet master's store of\n    plugins.\n\n    The puppet master serves Ruby code collected from the `lib` directories\n    of its modules. These plugins can be used on agent nodes to extend\n    Facter and implement custom types and providers. Plugins are normally\n    downloaded by puppet agent during the course of a run.\n  EOT\n\n  action :download do\n    summary _(\"Download plugins from the puppet master.\")\n    description <<-'EOT'\n      Downloads plugins from the configured puppet master. Any plugins\n      downloaded in this way will be used in all subsequent Puppet activity.\n      This action modifies files on disk.\n    EOT\n    returns _(<<-'EOT')\n      A list of the files downloaded, or a confirmation that no files were\n      downloaded. When used from the Ruby API, this action returns an array of\n      the files downloaded, which will be empty if none were retrieved.\n    EOT\n    examples <<-'EOT'\n      Retrieve plugins from the puppet master:\n\n      $ puppet plugin download\n\n      Retrieve plugins from the puppet master (API example):\n\n      $ Puppet::Face[:plugin, '0.0.1'].download\n    EOT\n\n    when_invoked do |_options|\n      remote_environment_for_plugins = Puppet::Node::Environment.remote(Puppet[:environment])\n\n      begin\n        handler = Puppet::Configurer::PluginHandler.new\n        handler.download_plugins(remote_environment_for_plugins)\n      ensure\n        Puppet.runtime[:http].close\n      end\n    end\n\n    when_rendering :console do |value|\n      if value.empty? then\n        _(\"No plugins downloaded.\")\n      else\n        _(\"Downloaded these plugins: %{plugins}\") % { plugins: value.join(', ') }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/face/report.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/face'\n\nPuppet::Indirector::Face.define(:report, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"Create, display, and submit reports.\")\n\n  save = get_action(:save)\n  save.summary _(\"API only: submit a report.\")\n  save.arguments _(\"<report>\")\n  save.returns _(\"Nothing.\")\n  save.examples <<-'EOT'\n    From the implementation of `puppet report submit` (API example):\n\n        begin\n          Puppet::Transaction::Report.indirection.terminus_class = :rest\n          Puppet::Face[:report, \"0.0.1\"].save(report)\n          Puppet.notice \"Uploaded report for #{report.name}\"\n        rescue => detail\n          Puppet.log_exception(detail, \"Could not send report: #{detail}\")\n        end\n  EOT\n\n  action(:submit) do\n    summary _(\"API only: submit a report with error handling.\")\n    description <<-'EOT'\n      API only: Submits a report to the puppet master. This action is\n      essentially a shortcut and wrapper for the `save` action with the `rest`\n      terminus, and provides additional details in the event of a failure.\n    EOT\n    arguments _(\"<report>\")\n    examples <<-'EOT'\n      API example:\n\n          # ...\n          report  = Puppet::Face[:catalog, '0.0.1'].apply\n          Puppet::Face[:report, '0.0.1'].submit(report)\n          return report\n    EOT\n    when_invoked do |report, _options|\n      Puppet::Transaction::Report.indirection.terminus_class = :rest\n      Puppet::Face[:report, \"0.0.1\"].save(report)\n      Puppet.notice _(\"Uploaded report for %{name}\") % { name: report.name }\n    rescue => detail\n      Puppet.log_exception(detail, _(\"Could not send report: %{detail}\") % { detail: detail })\n    end\n  end\n  deactivate_action(:find)\n  deactivate_action(:search)\n  deactivate_action(:destroy)\nend\n"
  },
  {
    "path": "lib/puppet/face/resource.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/face'\n\nPuppet::Indirector::Face.define(:resource, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   _(\"Apache 2 license; see COPYING\")\n\n  summary _(\"API only: interact directly with resources via the RAL.\")\n  description <<-'EOT'\n    API only: this face provides a Ruby API with functionality similar to the\n    puppet resource subcommand.\n  EOT\n\n  deactivate_action(:destroy)\n\n  search = get_action(:search)\n  search.summary _(\"API only: get all resources of a single type.\")\n  search.arguments _(\"<resource_type>\")\n  search.returns _(\"An array of Puppet::Resource objects.\")\n  search.examples <<-'EOT'\n    Get a list of all user resources (API example):\n\n        all_users = Puppet::Face[:resource, '0.0.1'].search(\"user\")\n  EOT\n\n  find = get_action(:find)\n  find.summary _(\"API only: get a single resource.\")\n  find.arguments _(\"<type>/<title>\")\n  find.returns _(\"A Puppet::Resource object.\")\n  find.examples <<-'EOT'\n    Print information about a user on this system (API example):\n\n        puts Puppet::Face[:resource, '0.0.1'].find(\"user/luke\").to_json\n  EOT\n\n  save = get_action(:save)\n  save.summary _(\"API only: create a new resource.\")\n  save.description <<-EOT\n    API only: creates a new resource.\n  EOT\n  save.arguments _(\"<resource_object>\")\n  save.returns _(\"The same resource object passed as an argument.\")\n  save.examples <<-'EOT'\n    Create a new file resource (API example):\n\n        my_resource = Puppet::Resource.new(\n          :file,\n          \"/tmp/demonstration\",\n          :parameters => {:ensure => :present, :content => \"some\\nthing\\n\"}\n        )\n\n        Puppet::Face[:resource, '0.0.1'].save(my_resource)\n  EOT\nend\n"
  },
  {
    "path": "lib/puppet/face.rb",
    "content": "# frozen_string_literal: true\n\n# The public name of this feature is 'face', but we have hidden all the\n# plumbing over in the 'interfaces' namespace to make clear the distinction\n# between the two.\n#\n# This file exists to ensure that the public name is usable without revealing\n# the details of the implementation; you really only need go look at anything\n# under Interfaces if you are looking to extend the implementation.\n#\n# It isn't hidden to gratuitously hide things, just to make it easier to\n# separate out the interests people will have.  --daniel 2011-04-07\nrequire_relative '../puppet/interface'\nPuppet::Face = Puppet::Interface\n"
  },
  {
    "path": "lib/puppet/facter_impl.rb",
    "content": "# frozen_string_literal: true\n\n#\n# @api private\n# Default Facter implementation that delegates to Facter API\n#\n\nmodule Puppet\n  class FacterImpl\n    def initialize\n      require 'facter'\n\n      setup_logging\n    end\n\n    def value(fact_name)\n      ::Facter.value(fact_name)\n    end\n\n    def add(name, &block)\n      ::Facter.add(name, &block)\n    end\n\n    def to_hash\n      ::Facter.to_hash\n    end\n\n    def clear\n      ::Facter.clear\n    end\n\n    def reset\n      ::Facter.reset\n    end\n\n    def resolve(options)\n      ::Facter.resolve(options)\n    end\n\n    def search_external(dirs)\n      ::Facter.search_external(dirs)\n    end\n\n    def search(*dirs)\n      ::Facter.search(*dirs)\n    end\n\n    def trace(value)\n      ::Facter.trace(value) if ::Facter.respond_to? :trace\n    end\n\n    def debugging(value)\n      ::Facter.debugging(value) if ::Facter.respond_to?(:debugging)\n    end\n\n    def load_external?\n      ::Facter.respond_to?(:load_external)\n    end\n\n    def load_external(value)\n      ::Facter.load_external(value) if load_external?\n    end\n\n    private\n\n    def setup_logging\n      return unless ::Facter.respond_to? :on_message\n\n      ::Facter.on_message do |level, message|\n        case level\n        when :trace, :debug\n          level = :debug\n        when :info\n          # Same as Puppet\n        when :warn\n          level = :warning\n        when :error\n          level = :err\n        when :fatal\n          level = :crit\n        else\n          next\n        end\n\n        Puppet::Util::Log.create(\n          {\n            :level => level,\n            :source => 'Facter',\n            :message => message\n          }\n        )\n        nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/feature/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\n# Add the simple features, all in one file.\n\n# Order is important as some features depend on others\n\n# We have a syslog implementation\nPuppet.features.add(:syslog, :libs => [\"syslog\"])\n\n# We can use POSIX user functions\nPuppet.features.add(:posix) do\n  require 'etc'\n  !Etc.getpwuid(0).nil? && Puppet.features.syslog?\nend\n\n# We can use Microsoft Windows functions\nPuppet.features.add(:microsoft_windows) { Puppet::Util::Platform.windows? }\n\nraise Puppet::Error, _(\"Cannot determine basic system flavour\") unless Puppet.features.posix? or Puppet.features.microsoft_windows?\n\n# We've got LDAP available.\nPuppet.features.add(:ldap, :libs => [\"ldap\"])\n\n# We have the Rdoc::Usage library.\nPuppet.features.add(:usage, :libs => %w[rdoc/ri/ri_paths rdoc/usage])\n\n# We have libshadow, useful for managing passwords.\nPuppet.features.add(:libshadow, :libs => [\"shadow\"])\n\n# We're running as root.\nPuppet.features.add(:root) do\n  require_relative '../../puppet/util/suidmanager'\n  Puppet::Util::SUIDManager.root?\nend\n\n# We have lcs diff\nPuppet.features.add :diff, :libs => %w[diff/lcs diff/lcs/hunk]\n\n# We have OpenSSL\nPuppet.features.add(:openssl, :libs => [\"openssl\"])\n\n# We have sqlite\nPuppet.features.add(:sqlite, :libs => [\"sqlite3\"])\n\n# We have Hiera\nPuppet.features.add(:hiera, :libs => [\"hiera\"])\n\nPuppet.features.add(:minitar, :libs => [\"archive/tar/minitar\"])\n\n# We can manage symlinks\nPuppet.features.add(:manages_symlinks) do\n  if !Puppet::Util::Platform.windows?\n    true\n  else\n    module WindowsSymlink\n      require 'ffi'\n      extend FFI::Library\n\n      def self.is_implemented # rubocop:disable Naming/PredicateName\n        ffi_lib :kernel32\n        attach_function :CreateSymbolicLinkW, [:lpwstr, :lpwstr, :dword], :boolean\n\n        true\n      rescue LoadError\n        Puppet.debug { \"CreateSymbolicLink is not available\" }\n        false\n      end\n    end\n\n    WindowsSymlink.is_implemented\n  end\nend\n\nPuppet.features.add(:puppetserver_ca, libs: ['puppetserver/ca', 'puppetserver/ca/action/clean'])\n"
  },
  {
    "path": "lib/puppet/feature/bolt.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:bolt, :libs => ['bolt'])\n"
  },
  {
    "path": "lib/puppet/feature/cfpropertylist.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:cfpropertylist, :libs => ['cfpropertylist'])\n"
  },
  {
    "path": "lib/puppet/feature/eventlog.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nif Puppet::Util::Platform.windows?\n  Puppet.features.add(:eventlog)\nend\n"
  },
  {
    "path": "lib/puppet/feature/hiera_eyaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:hiera_eyaml, :libs => ['hiera/backend/eyaml/parser/parser'])\n"
  },
  {
    "path": "lib/puppet/feature/hocon.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:hocon, :libs => ['hocon'])\n"
  },
  {
    "path": "lib/puppet/feature/libuser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\nrequire_relative '../../puppet/util/libuser'\n\nPuppet.features.add(:libuser) {\n  File.executable?(\"/usr/sbin/lgroupadd\") and\n    File.executable?(\"/usr/sbin/luseradd\") and\n    Puppet::FileSystem.exist?(Puppet::Util::Libuser.getconf)\n}\n"
  },
  {
    "path": "lib/puppet/feature/msgpack.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:msgpack, :libs => [\"msgpack\"])\n"
  },
  {
    "path": "lib/puppet/feature/pe_license.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\n# Is the pe license library installed providing the ability to read licenses.\nPuppet.features.add(:pe_license, :libs => %(pe_license))\n"
  },
  {
    "path": "lib/puppet/feature/pson.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\n# PSON is deprecated, use JSON instead\nPuppet.features.add(:pson, :libs => ['puppet/external/pson'])\n"
  },
  {
    "path": "lib/puppet/feature/selinux.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:selinux, :libs => [\"selinux\"])\n"
  },
  {
    "path": "lib/puppet/feature/ssh.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:ssh, :libs => %(net/ssh))\n"
  },
  {
    "path": "lib/puppet/feature/telnet.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\nPuppet.features.add(:telnet, :libs => %(net/telnet))\n"
  },
  {
    "path": "lib/puppet/feature/zlib.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/feature'\n\n# We want this to load if possible, but it's not automatically\n# required.\nPuppet.features.add(:zlib, :libs => %(zlib))\n"
  },
  {
    "path": "lib/puppet/ffi/posix/constants.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/posix'\n\nmodule Puppet::FFI::POSIX\n  module Constants\n    extend FFI::Library\n\n    # Maximum number of supplementary groups (groups\n    # that a user can be in plus its primary group)\n    # (64 + 1 primary group)\n    # Chosen a reasonable middle number from the list\n    # https://www.j3e.de/ngroups.html\n    MAXIMUM_NUMBER_OF_GROUPS = 65\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/posix/functions.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/posix'\n\nmodule Puppet::FFI::POSIX\n  module Functions\n    extend FFI::Library\n\n    ffi_convention :stdcall\n\n    # https://man7.org/linux/man-pages/man3/getgrouplist.3.html\n    # int getgrouplist (\n    #   const char *user,\n    #   gid_t group,\n    #   gid_t *groups,\n    #   int *ngroups\n    # );\n    begin\n      ffi_lib FFI::Library::LIBC\n      attach_function :getgrouplist, [:string, :uint, :pointer, :pointer], :int\n    rescue FFI::NotFoundError\n      # Do nothing\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/posix.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'ffi'\n\nmodule Puppet\n  module FFI\n    module POSIX\n      require_relative 'posix/functions'\n      require_relative 'posix/constants'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/windows/api_types.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\nrequire_relative '../../../puppet/util/windows/string'\n\nmodule Puppet::FFI::Windows\n  module APITypes\n    module ::FFI\n      WIN32_FALSE = 0\n\n      # standard Win32 error codes\n      ERROR_SUCCESS = 0\n    end\n\n    module ::FFI::Library\n      # Wrapper method for attach_function + private\n      def attach_function_private(*args)\n        attach_function(*args)\n        private args[0]\n      end\n    end\n\n    class ::FFI::Pointer\n      NULL_HANDLE = 0\n      WCHAR_NULL = String.new(\"\\0\\0\").force_encoding('UTF-16LE').freeze\n\n      def self.from_string_to_wide_string(str, &block)\n        str = Puppet::Util::Windows::String.wide_string(str)\n        FFI::MemoryPointer.from_wide_string(str, &block)\n\n        # ptr has already had free called, so nothing to return\n        nil\n      end\n\n      def read_win32_bool\n        # BOOL is always a 32-bit integer in Win32\n        # some Win32 APIs return 1 for true, while others are non-0\n        read_int32 != FFI::WIN32_FALSE\n      end\n\n      alias_method :read_dword, :read_uint32\n      alias_method :read_win32_ulong, :read_uint32\n      alias_method :read_qword, :read_uint64\n\n      alias_method :read_hresult, :read_int32\n\n      def read_handle\n        type_size == 4 ? read_uint32 : read_uint64\n      end\n\n      alias_method :read_wchar, :read_uint16\n      alias_method :read_word,  :read_uint16\n      alias_method :read_array_of_wchar, :read_array_of_uint16\n\n      def read_wide_string(char_length, dst_encoding = Encoding::UTF_8, strip = false, encode_options = {})\n        # char_length is number of wide chars (typically excluding NULLs), *not* bytes\n        str = get_bytes(0, char_length * 2).force_encoding('UTF-16LE')\n\n        if strip\n          i = str.index(WCHAR_NULL)\n          str = str[0, i] if i\n        end\n\n        str.encode(dst_encoding, str.encoding, **encode_options)\n      rescue EncodingError => e\n        Puppet.debug { \"Unable to convert value #{str.nil? ? 'nil' : str.dump} to encoding #{dst_encoding} due to #{e.inspect}\" }\n        raise\n      end\n\n      # @param max_char_length [Integer] Maximum number of wide chars to return (typically excluding NULLs), *not* bytes\n      # @param null_terminator [Symbol] Number of number of null wchar characters, *not* bytes, that determine the end of the string\n      #   null_terminator = :single_null, then the terminating sequence is two bytes of zero.   This is UNIT16 = 0\n      #   null_terminator = :double_null, then the terminating sequence is four bytes of zero.  This is UNIT32 = 0\n      # @param encode_options [Hash] Accepts the same option hash that may be passed to String#encode in Ruby\n      def read_arbitrary_wide_string_up_to(max_char_length = 512, null_terminator = :single_null, encode_options = {})\n        idx = case null_terminator\n              when :single_null\n                # find index of wide null between 0 and max (exclusive)\n                (0...max_char_length).find do |i|\n                  get_uint16(i * 2) == 0\n                end\n              when :double_null\n                # find index of double-wide null between 0 and max - 1 (exclusive)\n                (0...max_char_length - 1).find do |i|\n                  get_uint32(i * 2) == 0\n                end\n              else\n                raise _(\"Unable to read wide strings with %{null_terminator} terminal nulls\") % { null_terminator: null_terminator }\n              end\n\n        read_wide_string(idx || max_char_length, Encoding::UTF_8, false, encode_options)\n      end\n\n      def read_win32_local_pointer(&block)\n        ptr = read_pointer\n        begin\n          yield ptr\n        ensure\n          if !ptr.null? && FFI::WIN32::LocalFree(ptr.address) != FFI::Pointer::NULL_HANDLE\n            Puppet.debug \"LocalFree memory leak\"\n          end\n        end\n\n        # ptr has already had LocalFree called, so nothing to return\n        nil\n      end\n\n      def read_com_memory_pointer(&block)\n        ptr = read_pointer\n        begin\n          yield ptr\n        ensure\n          FFI::WIN32::CoTaskMemFree(ptr) unless ptr.null?\n        end\n\n        # ptr has already had CoTaskMemFree called, so nothing to return\n        nil\n      end\n\n      alias_method :write_dword, :write_uint32\n      alias_method :write_word, :write_uint16\n    end\n\n    class FFI::MemoryPointer\n      # Return a MemoryPointer that points to wide string. This is analogous to the\n      # FFI::MemoryPointer.from_string method.\n      def self.from_wide_string(wstr)\n        ptr = FFI::MemoryPointer.new(:uchar, wstr.bytesize + 2)\n        ptr.put_array_of_uchar(0, wstr.bytes.to_a)\n        ptr.put_uint16(wstr.bytesize, 0)\n\n        yield ptr if block_given?\n\n        ptr\n      end\n    end\n\n    # FFI Types\n    # https://github.com/ffi/ffi/wiki/Types\n\n    # Windows - Common Data Types\n    # https://msdn.microsoft.com/en-us/library/cc230309.aspx\n\n    # Windows Data Types\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx\n\n    FFI.typedef :uint16, :word\n    FFI.typedef :uint32, :dword\n    # uintptr_t is defined in an FFI conf as platform specific, either\n    # ulong_long on x64 or just ulong on x86\n    FFI.typedef :uintptr_t, :handle\n    FFI.typedef :uintptr_t, :hwnd\n\n    # buffer_inout is similar to pointer (platform specific), but optimized for buffers\n    FFI.typedef :buffer_inout, :lpwstr\n    # buffer_in is similar to pointer (platform specific), but optimized for CONST read only buffers\n    FFI.typedef :buffer_in, :lpcwstr\n    FFI.typedef :buffer_in, :lpcolestr\n\n    # string is also similar to pointer, but should be used for const char *\n    # NOTE that this is not wide, useful only for A suffixed functions\n    FFI.typedef :string, :lpcstr\n\n    # pointer in FFI is platform specific\n    # NOTE: for API calls with reserved lpvoid parameters, pass a FFI::Pointer::NULL\n    FFI.typedef :pointer, :lpcvoid\n    FFI.typedef :pointer, :lpvoid\n    FFI.typedef :pointer, :lpword\n    FFI.typedef :pointer, :lpbyte\n    FFI.typedef :pointer, :lpdword\n    FFI.typedef :pointer, :pdword\n    FFI.typedef :pointer, :phandle\n    FFI.typedef :pointer, :ulong_ptr\n    FFI.typedef :pointer, :pbool\n    FFI.typedef :pointer, :lpunknown\n\n    # any time LONG / ULONG is in a win32 API definition DO NOT USE platform specific width\n    # which is what FFI uses by default\n    # instead create new aliases for these very special cases\n    # NOTE: not a good idea to redefine FFI :ulong since other typedefs may rely on it\n    FFI.typedef :uint32, :win32_ulong\n    FFI.typedef :int32, :win32_long\n    # FFI bool can be only 1 byte at times,\n    # Win32 BOOL is a signed int, and is always 4 bytes, even on x64\n    # https://blogs.msdn.com/b/oldnewthing/archive/2011/03/28/10146459.aspx\n    FFI.typedef :int32, :win32_bool\n\n    # BOOLEAN (unlike BOOL) is a BYTE - typedef unsigned char BYTE;\n    FFI.typedef :uchar, :boolean\n\n    # Same as a LONG, a 32-bit signed integer\n    FFI.typedef :int32, :hresult\n\n    # NOTE: FFI already defines (u)short as a 16-bit (un)signed like this:\n    # FFI.typedef :uint16, :ushort\n    # FFI.typedef :int16, :short\n\n    # 8 bits per byte\n    FFI.typedef :uchar, :byte\n    FFI.typedef :uint16, :wchar\n\n    # Definitions for data types used in LSA structures and functions\n    # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/\n    # https://docs.microsoft.com/sr-latn-rs/windows/win32/secmgmt/management-data-types\n    FFI.typedef :pointer, :pwstr\n    FFI.typedef :pointer, :pulong\n    FFI.typedef :pointer, :lsa_handle\n    FFI.typedef :pointer, :plsa_handle\n    FFI.typedef :pointer, :psid\n    FFI.typedef :pointer, :pvoid\n    FFI.typedef :pointer, :plsa_unicode_string\n    FFI.typedef :pointer, :plsa_object_attributes\n    FFI.typedef :uint32,  :ntstatus\n    FFI.typedef :dword,   :access_mask\n\n    module ::FFI::WIN32\n      extend ::FFI::Library\n\n      # https://msdn.microsoft.com/en-us/library/windows/desktop/aa373931(v=vs.85).aspx\n      # typedef struct _GUID {\n      #   DWORD Data1;\n      #   WORD  Data2;\n      #   WORD  Data3;\n      #   BYTE  Data4[8];\n      # } GUID;\n      class GUID < FFI::Struct\n        layout :Data1, :dword,\n               :Data2, :word,\n               :Data3, :word,\n               :Data4, [:byte, 8]\n\n        def self.[](s)\n          raise _('Bad GUID format.') unless s =~ /^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/i\n\n          new.tap do |guid|\n            guid[:Data1] = s[0, 8].to_i(16)\n            guid[:Data2] = s[9, 4].to_i(16)\n            guid[:Data3] = s[14, 4].to_i(16)\n            guid[:Data4][0] = s[19, 2].to_i(16)\n            guid[:Data4][1] = s[21, 2].to_i(16)\n            s[24, 12].split('').each_slice(2).with_index do |a, i|\n              guid[:Data4][i + 2] = a.join('').to_i(16)\n            end\n          end\n        end\n\n        def ==(other) Windows.memcmp(other, self, size) == 0 end\n      end\n\n      # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx\n      # typedef struct _SYSTEMTIME {\n      #   WORD wYear;\n      #   WORD wMonth;\n      #   WORD wDayOfWeek;\n      #   WORD wDay;\n      #   WORD wHour;\n      #   WORD wMinute;\n      #   WORD wSecond;\n      #   WORD wMilliseconds;\n      # } SYSTEMTIME, *PSYSTEMTIME;\n      class SYSTEMTIME < FFI::Struct\n        layout :wYear, :word,\n               :wMonth, :word,\n               :wDayOfWeek, :word,\n               :wDay, :word,\n               :wHour, :word,\n               :wMinute, :word,\n               :wSecond, :word,\n               :wMilliseconds, :word\n\n        def to_local_time\n          Time.local(self[:wYear], self[:wMonth], self[:wDay],\n                     self[:wHour], self[:wMinute], self[:wSecond], self[:wMilliseconds] * 1000)\n        end\n      end\n\n      # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx\n      # Contains a 64-bit value representing the number of 100-nanosecond\n      # intervals since January 1, 1601 (UTC).\n      # typedef struct _FILETIME {\n      #   DWORD dwLowDateTime;\n      #   DWORD dwHighDateTime;\n      # } FILETIME, *PFILETIME;\n      class FILETIME < FFI::Struct\n        layout :dwLowDateTime, :dword,\n               :dwHighDateTime, :dword\n      end\n\n      ffi_convention :stdcall\n\n      # https://msdn.microsoft.com/en-us/library/windows/desktop/aa366730(v=vs.85).aspx\n      # HLOCAL WINAPI LocalFree(\n      #   _In_  HLOCAL hMem\n      # );\n      ffi_lib :kernel32\n      attach_function :LocalFree, [:handle], :handle\n\n      # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx\n      # BOOL WINAPI CloseHandle(\n      #   _In_  HANDLE hObject\n      # );\n      ffi_lib :kernel32\n      attach_function_private :CloseHandle, [:handle], :win32_bool\n\n      # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680722(v=vs.85).aspx\n      # void CoTaskMemFree(\n      #   _In_opt_  LPVOID pv\n      # );\n      ffi_lib :ole32\n      attach_function :CoTaskMemFree, [:lpvoid], :void\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/windows/constants.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::FFI::Windows\n  module Constants\n    extend FFI::Library\n\n    FILE_ATTRIBUTE_READONLY      = 0x00000001\n    FILE_ATTRIBUTE_DIRECTORY     = 0x00000010\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379607(v=vs.85).aspx\n    # The right to use the object for synchronization. This enables a thread to\n    # wait until the object is in the signaled state. Some object types do not\n    # support this access right.\n    SYNCHRONIZE                 = 0x100000\n    # The right to delete the object.\n    DELETE                      = 0x00010000\n    # The right to read the information in the object's security descriptor, not including the information in the system access control list (SACL).\n    # READ_CONTROL              = 0x00020000\n    # The right to modify the discretionary access control list (DACL) in the object's security descriptor.\n    WRITE_DAC                   = 0x00040000\n    # The right to change the owner in the object's security descriptor.\n    WRITE_OWNER                 = 0x00080000\n\n    # Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access.\n    STANDARD_RIGHTS_REQUIRED    = 0xf0000\n    # Currently defined to equal READ_CONTROL.\n    STANDARD_RIGHTS_READ        = 0x20000\n    # Currently defined to equal READ_CONTROL.\n    STANDARD_RIGHTS_WRITE       = 0x20000\n    # Currently defined to equal READ_CONTROL.\n    STANDARD_RIGHTS_EXECUTE     = 0x20000\n    # Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE access.\n    STANDARD_RIGHTS_ALL         = 0x1F0000\n    SPECIFIC_RIGHTS_ALL         = 0xFFFF\n\n    FILE_READ_DATA               = 1\n    FILE_WRITE_DATA              = 2\n    FILE_APPEND_DATA             = 4\n    FILE_READ_EA                 = 8\n    FILE_WRITE_EA                = 16\n    FILE_EXECUTE                 = 32\n    FILE_DELETE_CHILD            = 64\n    FILE_READ_ATTRIBUTES         = 128\n    FILE_WRITE_ATTRIBUTES        = 256\n\n    FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF\n\n    FILE_GENERIC_READ =\n      STANDARD_RIGHTS_READ |\n      FILE_READ_DATA |\n      FILE_READ_ATTRIBUTES |\n      FILE_READ_EA |\n      SYNCHRONIZE\n\n    FILE_GENERIC_WRITE =\n      STANDARD_RIGHTS_WRITE |\n      FILE_WRITE_DATA |\n      FILE_WRITE_ATTRIBUTES |\n      FILE_WRITE_EA |\n      FILE_APPEND_DATA |\n      SYNCHRONIZE\n\n    FILE_GENERIC_EXECUTE =\n      STANDARD_RIGHTS_EXECUTE |\n      FILE_READ_ATTRIBUTES |\n      FILE_EXECUTE |\n      SYNCHRONIZE\n\n    REPLACEFILE_WRITE_THROUGH         = 0x1\n    REPLACEFILE_IGNORE_MERGE_ERRORS   = 0x2\n    REPLACEFILE_IGNORE_ACL_ERRORS     = 0x3\n\n    INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF # define INVALID_FILE_ATTRIBUTES (DWORD (-1))\n\n    IO_REPARSE_TAG_MOUNT_POINT  = 0xA0000003\n    IO_REPARSE_TAG_HSM          = 0xC0000004\n    IO_REPARSE_TAG_HSM2         = 0x80000006\n    IO_REPARSE_TAG_SIS          = 0x80000007\n    IO_REPARSE_TAG_WIM          = 0x80000008\n    IO_REPARSE_TAG_CSV          = 0x80000009\n    IO_REPARSE_TAG_DFS          = 0x8000000A\n    IO_REPARSE_TAG_SYMLINK      = 0xA000000C\n    IO_REPARSE_TAG_DFSR         = 0x80000012\n    IO_REPARSE_TAG_DEDUP        = 0x80000013\n    IO_REPARSE_TAG_NFS          = 0x80000014\n\n    FILE_ATTRIBUTE_REPARSE_POINT = 0x400\n\n    GENERIC_READ                  = 0x80000000\n    GENERIC_WRITE                 = 0x40000000\n    GENERIC_EXECUTE               = 0x20000000\n    GENERIC_ALL                   = 0x10000000\n    METHOD_BUFFERED               = 0\n    FILE_SHARE_READ               = 1\n    FILE_SHARE_WRITE              = 2\n    OPEN_EXISTING                 = 3\n    FILE_DEVICE_FILE_SYSTEM       = 0x00000009\n    FILE_FLAG_OPEN_REPARSE_POINT  = 0x00200000\n    FILE_FLAG_BACKUP_SEMANTICS    = 0x02000000\n    SHGFI_DISPLAYNAME             = 0x000000200\n    SHGFI_PIDL                    = 0x000000008\n\n    ERROR_FILE_NOT_FOUND = 2\n    ERROR_PATH_NOT_FOUND = 3\n    ERROR_ALREADY_EXISTS = 183\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx\n    FSCTL_GET_REPARSE_POINT = 0x900a8\n\n    MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16_384\n\n    # Priority constants\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass\n    ABOVE_NORMAL_PRIORITY_CLASS = 0x0008000\n    BELOW_NORMAL_PRIORITY_CLASS = 0x0004000\n    HIGH_PRIORITY_CLASS         = 0x0000080\n    IDLE_PRIORITY_CLASS         = 0x0000040\n    NORMAL_PRIORITY_CLASS       = 0x0000020\n    REALTIME_PRIORITY_CLASS     = 0x0000010\n\n    # Process Access Rights\n    # https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights\n    PROCESS_TERMINATE         = 0x00000001\n    PROCESS_SET_INFORMATION   = 0x00000200\n    PROCESS_QUERY_INFORMATION = 0x00000400\n    PROCESS_ALL_ACCESS        = 0x001F0FFF\n    PROCESS_VM_READ           = 0x00000010\n\n    # Process creation flags\n    # https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags\n    CREATE_BREAKAWAY_FROM_JOB        = 0x01000000\n    CREATE_DEFAULT_ERROR_MODE        = 0x04000000\n    CREATE_NEW_CONSOLE               = 0x00000010\n    CREATE_NEW_PROCESS_GROUP         = 0x00000200\n    CREATE_NO_WINDOW                 = 0x08000000\n    CREATE_PROTECTED_PROCESS         = 0x00040000\n    CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000\n    CREATE_SEPARATE_WOW_VDM          = 0x00000800\n    CREATE_SHARED_WOW_VDM            = 0x00001000\n    CREATE_SUSPENDED                 = 0x00000004\n    CREATE_UNICODE_ENVIRONMENT       = 0x00000400\n    DEBUG_ONLY_THIS_PROCESS          = 0x00000002\n    DEBUG_PROCESS                    = 0x00000001\n    DETACHED_PROCESS                 = 0x00000008\n    INHERIT_PARENT_AFFINITY          = 0x00010000\n\n    # Logon options\n    LOGON_WITH_PROFILE        = 0x00000001\n\n    # STARTUPINFOA constants\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa\n    STARTF_USESTDHANDLES    = 0x00000100\n\n    # Miscellaneous\n    HANDLE_FLAG_INHERIT     = 0x00000001\n    SEM_FAILCRITICALERRORS  = 0x00000001\n    SEM_NOGPFAULTERRORBOX   = 0x00000002\n\n    # Error constants\n    INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379626(v=vs.85).aspx\n    TOKEN_INFORMATION_CLASS = enum(\n      :TokenUser, 1,\n      :TokenGroups,\n      :TokenPrivileges,\n      :TokenOwner,\n      :TokenPrimaryGroup,\n      :TokenDefaultDacl,\n      :TokenSource,\n      :TokenType,\n      :TokenImpersonationLevel,\n      :TokenStatistics,\n      :TokenRestrictedSids,\n      :TokenSessionId,\n      :TokenGroupsAndPrivileges,\n      :TokenSessionReference,\n      :TokenSandBoxInert,\n      :TokenAuditPolicy,\n      :TokenOrigin,\n      :TokenElevationType,\n      :TokenLinkedToken,\n      :TokenElevation,\n      :TokenHasRestrictions,\n      :TokenAccessInformation,\n      :TokenVirtualizationAllowed,\n      :TokenVirtualizationEnabled,\n      :TokenIntegrityLevel,\n      :TokenUIAccess,\n      :TokenMandatoryPolicy,\n      :TokenLogonSid,\n      :TokenIsAppContainer,\n      :TokenCapabilities,\n      :TokenAppContainerSid,\n      :TokenAppContainerNumber,\n      :TokenUserClaimAttributes,\n      :TokenDeviceClaimAttributes,\n      :TokenRestrictedUserClaimAttributes,\n      :TokenRestrictedDeviceClaimAttributes,\n      :TokenDeviceGroups,\n      :TokenRestrictedDeviceGroups,\n      :TokenSecurityAttributes,\n      :TokenIsRestricted,\n      :MaxTokenInfoClass\n    )\n\n    # Service error codes\n    # https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1000-1299-\n    ERROR_SERVICE_DOES_NOT_EXIST = 0x00000424\n\n    # Service control codes\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-controlserviceexw\n    SERVICE_CONTROL_STOP                  = 0x00000001\n    SERVICE_CONTROL_PAUSE                 = 0x00000002\n    SERVICE_CONTROL_CONTINUE              = 0x00000003\n    SERVICE_CONTROL_INTERROGATE           = 0x00000004\n    SERVICE_CONTROL_SHUTDOWN              = 0x00000005\n    SERVICE_CONTROL_PARAMCHANGE           = 0x00000006\n    SERVICE_CONTROL_NETBINDADD            = 0x00000007\n    SERVICE_CONTROL_NETBINDREMOVE         = 0x00000008\n    SERVICE_CONTROL_NETBINDENABLE         = 0x00000009\n    SERVICE_CONTROL_NETBINDDISABLE        = 0x0000000A\n    SERVICE_CONTROL_DEVICEEVENT           = 0x0000000B\n    SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0x0000000C\n    SERVICE_CONTROL_POWEREVENT            = 0x0000000D\n    SERVICE_CONTROL_SESSIONCHANGE         = 0x0000000E\n    SERVICE_CONTROL_PRESHUTDOWN           = 0x0000000F\n    SERVICE_CONTROL_TIMECHANGE            = 0x00000010\n    SERVICE_CONTROL_TRIGGEREVENT          = 0x00000020\n    SERVICE_CONTROL_SIGNALS               = {\n      SERVICE_CONTROL_STOP => :SERVICE_CONTROL_STOP,\n      SERVICE_CONTROL_PAUSE => :SERVICE_CONTROL_PAUSE,\n      SERVICE_CONTROL_CONTINUE => :SERVICE_CONTROL_CONTINUE,\n      SERVICE_CONTROL_INTERROGATE => :SERVICE_CONTROL_INTERROGATE,\n      SERVICE_CONTROL_SHUTDOWN => :SERVICE_CONTROL_SHUTDOWN,\n      SERVICE_CONTROL_PARAMCHANGE => :SERVICE_CONTROL_PARAMCHANGE,\n      SERVICE_CONTROL_NETBINDADD => :SERVICE_CONTROL_NETBINDADD,\n      SERVICE_CONTROL_NETBINDREMOVE => :SERVICE_CONTROL_NETBINDREMOVE,\n      SERVICE_CONTROL_NETBINDENABLE => :SERVICE_CONTROL_NETBINDENABLE,\n      SERVICE_CONTROL_NETBINDDISABLE => :SERVICE_CONTROL_NETBINDDISABLE,\n      SERVICE_CONTROL_DEVICEEVENT => :SERVICE_CONTROL_DEVICEEVENT,\n      SERVICE_CONTROL_HARDWAREPROFILECHANGE => :SERVICE_CONTROL_HARDWAREPROFILECHANGE,\n      SERVICE_CONTROL_POWEREVENT => :SERVICE_CONTROL_POWEREVENT,\n      SERVICE_CONTROL_SESSIONCHANGE => :SERVICE_CONTROL_SESSIONCHANGE,\n      SERVICE_CONTROL_PRESHUTDOWN => :SERVICE_CONTROL_PRESHUTDOWN,\n      SERVICE_CONTROL_TIMECHANGE => :SERVICE_CONTROL_TIMECHANGE,\n      SERVICE_CONTROL_TRIGGEREVENT => :SERVICE_CONTROL_TRIGGEREVENT\n    }\n\n    # Service start type codes\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-changeserviceconfigw\n    SERVICE_AUTO_START = 0x00000002\n    SERVICE_BOOT_START = 0x00000000\n    SERVICE_DEMAND_START = 0x00000003\n    SERVICE_DISABLED = 0x00000004\n    SERVICE_SYSTEM_START = 0x00000001\n    SERVICE_START_TYPES = {\n      SERVICE_AUTO_START => :SERVICE_AUTO_START,\n      SERVICE_BOOT_START => :SERVICE_BOOT_START,\n      SERVICE_DEMAND_START => :SERVICE_DEMAND_START,\n      SERVICE_DISABLED => :SERVICE_DISABLED,\n      SERVICE_SYSTEM_START => :SERVICE_SYSTEM_START,\n    }\n\n    # Service type codes\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-changeserviceconfigw\n    SERVICE_FILE_SYSTEM_DRIVER  = 0x00000002\n    SERVICE_KERNEL_DRIVER       = 0x00000001\n    SERVICE_WIN32_OWN_PROCESS   = 0x00000010\n    SERVICE_WIN32_SHARE_PROCESS = 0x00000020\n    SERVICE_USER_OWN_PROCESS    = 0x00000050\n    SERVICE_USER_SHARE_PROCESS  = 0x00000060\n    # Available only if service is also SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS\n    SERVICE_INTERACTIVE_PROCESS = 0x00000100\n    ALL_SERVICE_TYPES =\n      SERVICE_FILE_SYSTEM_DRIVER |\n      SERVICE_KERNEL_DRIVER |\n      SERVICE_WIN32_OWN_PROCESS |\n      SERVICE_WIN32_SHARE_PROCESS\n\n    # Current state codes\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_service_status_process\n    SERVICE_CONTINUE_PENDING = 0x00000005\n    SERVICE_PAUSE_PENDING    = 0x00000006\n    SERVICE_PAUSED           = 0x00000007\n    SERVICE_RUNNING          = 0x00000004\n    SERVICE_START_PENDING    = 0x00000002\n    SERVICE_STOP_PENDING     = 0x00000003\n    SERVICE_STOPPED          = 0x00000001\n    UNSAFE_PENDING_STATES    = [SERVICE_START_PENDING, SERVICE_STOP_PENDING]\n    FINAL_STATES             = {\n      SERVICE_CONTINUE_PENDING => SERVICE_RUNNING,\n      SERVICE_PAUSE_PENDING => SERVICE_PAUSED,\n      SERVICE_START_PENDING => SERVICE_RUNNING,\n      SERVICE_STOP_PENDING => SERVICE_STOPPED\n    }\n    SERVICE_STATES = {\n      SERVICE_CONTINUE_PENDING => :SERVICE_CONTINUE_PENDING,\n      SERVICE_PAUSE_PENDING => :SERVICE_PAUSE_PENDING,\n      SERVICE_PAUSED => :SERVICE_PAUSED,\n      SERVICE_RUNNING => :SERVICE_RUNNING,\n      SERVICE_START_PENDING => :SERVICE_START_PENDING,\n      SERVICE_STOP_PENDING => :SERVICE_STOP_PENDING,\n      SERVICE_STOPPED => :SERVICE_STOPPED,\n    }\n\n    # Service accepts control codes\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_service_status_process\n    SERVICE_ACCEPT_STOP                  = 0x00000001\n    SERVICE_ACCEPT_PAUSE_CONTINUE        = 0x00000002\n    SERVICE_ACCEPT_SHUTDOWN              = 0x00000004\n    SERVICE_ACCEPT_PARAMCHANGE           = 0x00000008\n    SERVICE_ACCEPT_NETBINDCHANGE         = 0x00000010\n    SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020\n    SERVICE_ACCEPT_POWEREVENT            = 0x00000040\n    SERVICE_ACCEPT_SESSIONCHANGE         = 0x00000080\n    SERVICE_ACCEPT_PRESHUTDOWN           = 0x00000100\n    SERVICE_ACCEPT_TIMECHANGE            = 0x00000200\n    SERVICE_ACCEPT_TRIGGEREVENT          = 0x00000400\n    SERVICE_ACCEPT_USER_LOGOFF           = 0x00000800\n\n    # Service manager access codes\n    # https://docs.microsoft.com/en-us/windows/desktop/Services/service-security-and-access-rights\n    SC_MANAGER_CREATE_SERVICE     = 0x00000002\n    SC_MANAGER_CONNECT            = 0x00000001\n    SC_MANAGER_ENUMERATE_SERVICE  = 0x00000004\n    SC_MANAGER_LOCK               = 0x00000008\n    SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00000020\n    SC_MANAGER_QUERY_LOCK_STATUS  = 0x00000010\n    SC_MANAGER_ALL_ACCESS         =\n      STANDARD_RIGHTS_REQUIRED |\n      SC_MANAGER_CREATE_SERVICE      |\n      SC_MANAGER_CONNECT             |\n      SC_MANAGER_ENUMERATE_SERVICE   |\n      SC_MANAGER_LOCK                |\n      SC_MANAGER_MODIFY_BOOT_CONFIG  |\n      SC_MANAGER_QUERY_LOCK_STATUS\n\n    # Service access codes\n    # https://docs.microsoft.com/en-us/windows/desktop/Services/service-security-and-access-rights\n    SERVICE_CHANGE_CONFIG        = 0x0002\n    SERVICE_ENUMERATE_DEPENDENTS = 0x0008\n    SERVICE_INTERROGATE          = 0x0080\n    SERVICE_PAUSE_CONTINUE       = 0x0040\n    SERVICE_QUERY_STATUS         = 0x0004\n    SERVICE_QUERY_CONFIG         = 0x0001\n    SERVICE_START                = 0x0010\n    SERVICE_STOP                 = 0x0020\n    SERVICE_USER_DEFINED_CONTROL = 0x0100\n    SERVICE_ALL_ACCESS           =\n      STANDARD_RIGHTS_REQUIRED |\n      SERVICE_CHANGE_CONFIG          |\n      SERVICE_ENUMERATE_DEPENDENTS   |\n      SERVICE_INTERROGATE            |\n      SERVICE_PAUSE_CONTINUE         |\n      SERVICE_QUERY_STATUS           |\n      SERVICE_QUERY_CONFIG           |\n      SERVICE_START                  |\n      SERVICE_STOP                   |\n      SERVICE_USER_DEFINED_CONTROL\n\n    # Service config codes\n    # From the windows 10 SDK:\n    # //\n    # // Value to indicate no change to an optional parameter\n    # //\n    # #define SERVICE_NO_CHANGE              0xffffffff\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-changeserviceconfig2w\n    SERVICE_CONFIG_DESCRIPTION              = 0x00000001\n    SERVICE_CONFIG_FAILURE_ACTIONS          = 0x00000002\n    SERVICE_CONFIG_DELAYED_AUTO_START_INFO  = 0x00000003\n    SERVICE_CONFIG_FAILURE_ACTIONS_FLAG     = 0x00000004\n    SERVICE_CONFIG_SERVICE_SID_INFO         = 0x00000005\n    SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 0x00000006\n    SERVICE_CONFIG_PRESHUTDOWN_INFO         = 0x00000007\n    SERVICE_CONFIG_TRIGGER_INFO             = 0x00000008\n    SERVICE_CONFIG_PREFERRED_NODE           = 0x00000009\n    SERVICE_CONFIG_LAUNCH_PROTECTED         = 0x0000000C\n    SERVICE_NO_CHANGE                       = 0xffffffff\n    SERVICE_CONFIG_TYPES = {\n      SERVICE_CONFIG_DESCRIPTION => :SERVICE_CONFIG_DESCRIPTION,\n      SERVICE_CONFIG_FAILURE_ACTIONS => :SERVICE_CONFIG_FAILURE_ACTIONS,\n      SERVICE_CONFIG_DELAYED_AUTO_START_INFO => :SERVICE_CONFIG_DELAYED_AUTO_START_INFO,\n      SERVICE_CONFIG_FAILURE_ACTIONS_FLAG => :SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,\n      SERVICE_CONFIG_SERVICE_SID_INFO => :SERVICE_CONFIG_SERVICE_SID_INFO,\n      SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO => :SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,\n      SERVICE_CONFIG_PRESHUTDOWN_INFO => :SERVICE_CONFIG_PRESHUTDOWN_INFO,\n      SERVICE_CONFIG_TRIGGER_INFO => :SERVICE_CONFIG_TRIGGER_INFO,\n      SERVICE_CONFIG_PREFERRED_NODE => :SERVICE_CONFIG_PREFERRED_NODE,\n      SERVICE_CONFIG_LAUNCH_PROTECTED => :SERVICE_CONFIG_LAUNCH_PROTECTED,\n    }\n\n    # Service enum codes\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-enumservicesstatusexa\n    SERVICE_ACTIVE = 0x00000001\n    SERVICE_INACTIVE = 0x00000002\n    SERVICE_STATE_ALL =\n      SERVICE_ACTIVE |\n      SERVICE_INACTIVE\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_enum_service_status_processw\n    SERVICENAME_MAX = 256\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/windows/functions.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::FFI::Windows\n  module Functions\n    extend FFI::Library\n    include Puppet::FFI::Windows::Constants\n\n    ffi_convention :stdcall\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-sethandleinformation\n    # BOOL SetHandleInformation(\n    #   HANDLE hObject,\n    #   DWORD  dwMask,\n    #   DWORD  dwFlags\n    # );\n    ffi_lib :kernel32\n    attach_function_private :SetHandleInformation, [:handle, :dword, :dword], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode\n    # UINT SetErrorMode(\n    #   UINT uMode\n    # );\n    ffi_lib :kernel32\n    attach_function_private :SetErrorMode, [:uint], :uint\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw\n    # BOOL CreateProcessW(\n    #   LPCWSTR               lpApplicationName,\n    #   LPWSTR                lpCommandLine,\n    #   LPSECURITY_ATTRIBUTES lpProcessAttributes,\n    #   LPSECURITY_ATTRIBUTES lpThreadAttributes,\n    #   BOOL                  bInheritHandles,\n    #   DWORD                 dwCreationFlags,\n    #   LPVOID                lpEnvironment,\n    #   LPCWSTR               lpCurrentDirectory,\n    #   LPSTARTUPINFOW        lpStartupInfo,\n    #   LPPROCESS_INFORMATION lpProcessInformation\n    # );\n    ffi_lib :kernel32\n    attach_function_private :CreateProcessW,\n                            [:lpcwstr, :lpwstr, :pointer, :pointer, :win32_bool,\n                             :dword, :lpvoid, :lpcwstr, :pointer, :pointer], :bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess\n    # HANDLE OpenProcess(\n    #   DWORD dwDesiredAccess,\n    #   BOOL  bInheritHandle,\n    #   DWORD dwProcessId\n    # );\n    ffi_lib :kernel32\n    attach_function_private :OpenProcess, [:dword, :win32_bool, :dword], :handle\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass\n    # BOOL SetPriorityClass(\n    #   HANDLE hProcess,\n    #   DWORD  dwPriorityClass\n    # );\n    ffi_lib :kernel32\n    attach_function_private :SetPriorityClass, [:handle, :dword], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw\n    # BOOL CreateProcessWithLogonW(\n    #   LPCWSTR               lpUsername,\n    #   LPCWSTR               lpDomain,\n    #   LPCWSTR               lpPassword,\n    #   DWORD                 dwLogonFlags,\n    #   LPCWSTR               lpApplicationName,\n    #   LPWSTR                lpCommandLine,\n    #   DWORD                 dwCreationFlags,\n    #   LPVOID                lpEnvironment,\n    #   LPCWSTR               lpCurrentDirectory,\n    #   LPSTARTUPINFOW        lpStartupInfo,\n    #   LPPROCESS_INFORMATION lpProcessInformation\n    # );\n    ffi_lib :advapi32\n    attach_function_private :CreateProcessWithLogonW,\n                            [:lpcwstr, :lpcwstr, :lpcwstr, :dword, :lpcwstr, :lpwstr,\n                             :dword, :lpvoid, :lpcwstr, :pointer, :pointer], :bool\n\n    # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019\n    # intptr_t _get_osfhandle(\n    #    int fd\n    # );\n    ffi_lib FFI::Library::LIBC\n    attach_function_private :get_osfhandle, :_get_osfhandle, [:int], :intptr_t\n\n    begin\n      # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-errno?view=vs-2019\n      # errno_t _get_errno(\n      #    int * pValue\n      # );\n      attach_function_private :get_errno, :_get_errno, [:pointer], :int\n    rescue FFI::NotFoundError\n      # Do nothing, Windows XP or earlier.\n    end\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx\n    # DWORD WINAPI WaitForSingleObject(\n    #   _In_  HANDLE hHandle,\n    #   _In_  DWORD dwMilliseconds\n    # );\n    ffi_lib :kernel32\n    attach_function_private :WaitForSingleObject,\n                            [:handle, :dword], :dword, :blocking => true\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects\n    #   DWORD WaitForMultipleObjects(\n    #   DWORD        nCount,\n    #   const HANDLE *lpHandles,\n    #   BOOL         bWaitAll,\n    #   DWORD        dwMilliseconds\n    # );\n    ffi_lib :kernel32\n    attach_function_private :WaitForMultipleObjects,\n                            [:dword, :phandle, :win32_bool, :dword], :dword\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw\n    # HANDLE CreateEventW(\n    #   LPSECURITY_ATTRIBUTES lpEventAttributes,\n    #   BOOL                  bManualReset,\n    #   BOOL                  bInitialState,\n    #   LPCWSTR               lpName\n    # );\n    ffi_lib :kernel32\n    attach_function_private :CreateEventW,\n                            [:pointer, :win32_bool, :win32_bool, :lpcwstr], :handle\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread\n    # HANDLE CreateThread(\n    #   LPSECURITY_ATTRIBUTES   lpThreadAttributes,\n    #   SIZE_T                  dwStackSize,\n    #   LPTHREAD_START_ROUTINE  lpStartAddress,\n    #   __drv_aliasesMem LPVOID lpParameter,\n    #   DWORD                   dwCreationFlags,\n    #   LPDWORD                 lpThreadId\n    # );\n    ffi_lib :kernel32\n    attach_function_private :CreateThread,\n                            [:pointer, :size_t, :pointer, :lpvoid, :dword, :lpdword], :handle, :blocking => true\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent\n    # BOOL SetEvent(\n    #   HANDLE hEvent\n    # );\n    ffi_lib :kernel32\n    attach_function_private :SetEvent,\n                            [:handle], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx\n    # BOOL WINAPI GetExitCodeProcess(\n    #   _In_   HANDLE hProcess,\n    #   _Out_  LPDWORD lpExitCode\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetExitCodeProcess,\n                            [:handle, :lpdword], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683179(v=vs.85).aspx\n    # HANDLE WINAPI GetCurrentProcess(void);\n    ffi_lib :kernel32\n    attach_function_private :GetCurrentProcess, [], :handle\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683187(v=vs.85).aspx\n    # LPTCH GetEnvironmentStrings(void);\n    ffi_lib :kernel32\n    attach_function_private :GetEnvironmentStringsW, [], :pointer\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683151(v=vs.85).aspx\n    # BOOL FreeEnvironmentStrings(\n    #   _In_ LPTCH lpszEnvironmentBlock\n    # );\n    ffi_lib :kernel32\n    attach_function_private :FreeEnvironmentStringsW,\n                            [:pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms686206(v=vs.85).aspx\n    # BOOL WINAPI SetEnvironmentVariableW(\n    #     _In_     LPCTSTR lpName,\n    #     _In_opt_ LPCTSTR lpValue\n    #   );\n    ffi_lib :kernel32\n    attach_function_private :SetEnvironmentVariableW,\n                            [:lpcwstr, :lpcwstr], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx\n    # HANDLE WINAPI OpenProcess(\n    #   _In_   DWORD DesiredAccess,\n    #   _In_   BOOL InheritHandle,\n    #   _In_   DWORD ProcessId\n    # );\n    ffi_lib :kernel32\n    attach_function_private :OpenProcess,\n                            [:dword, :win32_bool, :dword], :handle\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379295(v=vs.85).aspx\n    # BOOL WINAPI OpenProcessToken(\n    #   _In_   HANDLE ProcessHandle,\n    #   _In_   DWORD DesiredAccess,\n    #   _Out_  PHANDLE TokenHandle\n    # );\n    ffi_lib :advapi32\n    attach_function_private :OpenProcessToken,\n                            [:handle, :dword, :phandle], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-queryfullprocessimagenamew\n    # BOOL WINAPI QueryFullProcessImageName(\n    #   _In_   HANDLE hProcess,\n    #   _In_   DWORD dwFlags,\n    #   _Out_  LPWSTR lpExeName,\n    #   _In_   PDWORD lpdwSize,\n    # );\n    ffi_lib :kernel32\n    attach_function_private :QueryFullProcessImageNameW,\n                            [:handle, :dword, :lpwstr, :pdword], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/Windows/desktop/aa379180(v=vs.85).aspx\n    # BOOL WINAPI LookupPrivilegeValue(\n    #   _In_opt_  LPCTSTR lpSystemName,\n    #   _In_      LPCTSTR lpName,\n    #   _Out_     PLUID lpLuid\n    # );\n    ffi_lib :advapi32\n    attach_function_private :LookupPrivilegeValueW,\n                            [:lpcwstr, :lpcwstr, :pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx\n    # BOOL WINAPI GetTokenInformation(\n    #   _In_       HANDLE TokenHandle,\n    #   _In_       TOKEN_INFORMATION_CLASS TokenInformationClass,\n    #   _Out_opt_  LPVOID TokenInformation,\n    #   _In_       DWORD TokenInformationLength,\n    #   _Out_      PDWORD ReturnLength\n    # );\n    ffi_lib :advapi32\n    attach_function_private :GetTokenInformation,\n                            [:handle, TOKEN_INFORMATION_CLASS, :lpvoid, :dword, :pdword], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx\n    # BOOL WINAPI GetVersionEx(\n    #   _Inout_  LPOSVERSIONINFO lpVersionInfo\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetVersionExW,\n                            [:pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd318123(v=vs.85).aspx\n    # LANGID GetSystemDefaultUILanguage(void);\n    ffi_lib :kernel32\n    attach_function_private :GetSystemDefaultUILanguage, [], :word\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-openscmanagerw\n    # SC_HANDLE OpenSCManagerW(\n    #   LPCWSTR lpMachineName,\n    #   LPCWSTR lpDatabaseName,\n    #   DWORD   dwDesiredAccess\n    # );\n    ffi_lib :advapi32\n    attach_function_private :OpenSCManagerW,\n                            [:lpcwstr, :lpcwstr, :dword], :handle\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-openservicew\n    # SC_HANDLE OpenServiceW(\n    #   SC_HANDLE hSCManager,\n    #   LPCWSTR   lpServiceName,\n    #   DWORD     dwDesiredAccess\n    # );\n    ffi_lib :advapi32\n    attach_function_private :OpenServiceW,\n                            [:handle, :lpcwstr, :dword], :handle\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-closeservicehandle\n    # BOOL CloseServiceHandle(\n    #   SC_HANDLE hSCObject\n    # );\n    ffi_lib :advapi32\n    attach_function_private :CloseServiceHandle,\n                            [:handle], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-queryservicestatusex\n    # BOOL QueryServiceStatusEx(\n    #   SC_HANDLE      hService,\n    #   SC_STATUS_TYPE InfoLevel,\n    #   LPBYTE         lpBuffer,\n    #   DWORD          cbBufSize,\n    #   LPDWORD        pcbBytesNeeded\n    # );\n    SC_STATUS_TYPE = enum(\n      :SC_STATUS_PROCESS_INFO, 0\n    )\n    ffi_lib :advapi32\n    attach_function_private :QueryServiceStatusEx,\n                            [:handle, SC_STATUS_TYPE, :lpbyte, :dword, :lpdword], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-queryserviceconfigw\n    # BOOL QueryServiceConfigW(\n    #   SC_HANDLE               hService,\n    #   LPQUERY_SERVICE_CONFIGW lpServiceConfig,\n    #   DWORD                   cbBufSize,\n    #   LPDWORD                 pcbBytesNeeded\n    # );\n    ffi_lib :advapi32\n    attach_function_private :QueryServiceConfigW,\n                            [:handle, :lpbyte, :dword, :lpdword], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryserviceconfig2w\n    # BOOL QueryServiceConfig2W(\n    #   SC_HANDLE hService,\n    #   DWORD     dwInfoLevel,\n    #   LPBYTE    lpBuffer,\n    #   DWORD     cbBufSize,\n    #   LPDWORD   pcbBytesNeeded\n    # );\n    ffi_lib :advapi32\n    attach_function_private :QueryServiceConfig2W,\n                            [:handle, :dword, :lpbyte, :dword, :lpdword], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-startservicew\n    # BOOL StartServiceW(\n    #   SC_HANDLE hService,\n    #   DWORD     dwNumServiceArgs,\n    #   LPCWSTR   *lpServiceArgVectors\n    # );\n    ffi_lib :advapi32\n    attach_function_private :StartServiceW,\n                            [:handle, :dword, :pointer], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicectrldispatcherw\n    # BOOL StartServiceCtrlDispatcherW(\n    #   const SERVICE_TABLE_ENTRYW *lpServiceStartTable\n    # );\n    ffi_lib :advapi32\n    attach_function_private :StartServiceCtrlDispatcherW,\n                            [:pointer], :win32_bool, :blocking => true\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-setservicestatus\n    # BOOL SetServiceStatus(\n    #   SERVICE_STATUS_HANDLE hServiceStatus,\n    #   LPSERVICE_STATUS      lpServiceStatus\n    # );\n    ffi_lib :advapi32\n    attach_function_private :SetServiceStatus,\n                            [:handle, :pointer], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-controlservice\n    # BOOL ControlService(\n    #   SC_HANDLE        hService,\n    #   DWORD            dwControl,\n    #   LPSERVICE_STATUS lpServiceStatus\n    # );\n    ffi_lib :advapi32\n    attach_function_private :ControlService,\n                            [:handle, :dword, :pointer], :win32_bool\n\n    #   DWORD LphandlerFunctionEx(\n    #   DWORD dwControl,\n    #   DWORD dwEventType,\n    #   LPVOID lpEventData,\n    #   LPVOID lpContext\n    # )\n    callback :handler_ex, [:dword, :dword, :lpvoid, :lpvoid], :void\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-registerservicectrlhandlerexw\n    # SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerExW(\n    #   LPCWSTR               lpServiceName,\n    #   LPHANDLER_FUNCTION_EX lpHandlerProc,\n    #   LPVOID                lpContext\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegisterServiceCtrlHandlerExW,\n                            [:lpcwstr, :handler_ex, :lpvoid], :handle\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-changeserviceconfigw\n    # BOOL ChangeServiceConfigW(\n    #   SC_HANDLE hService,\n    #   DWORD     dwServiceType,\n    #   DWORD     dwStartType,\n    #   DWORD     dwErrorControl,\n    #   LPCWSTR   lpBinaryPathName,\n    #   LPCWSTR   lpLoadOrderGroup,\n    #   LPDWORD   lpdwTagId,\n    #   LPCWSTR   lpDependencies,\n    #   LPCWSTR   lpServiceStartName,\n    #   LPCWSTR   lpPassword,\n    #   LPCWSTR   lpDisplayName\n    # );\n    ffi_lib :advapi32\n    attach_function_private :ChangeServiceConfigW,\n                            [\n                              :handle,\n                              :dword,\n                              :dword,\n                              :dword,\n                              :lpcwstr,\n                              :lpcwstr,\n                              :lpdword,\n                              :lpcwstr,\n                              :lpcwstr,\n                              :lpcwstr,\n                              :lpcwstr\n                            ], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-changeserviceconfig2w\n    # BOOL ChangeServiceConfig2W(\n    #   SC_HANDLE hService,\n    #   DWORD     dwInfoLevel,\n    #   LPVOID    lpInfo\n    # );\n    ffi_lib :advapi32\n    attach_function_private :ChangeServiceConfig2W,\n                            [:handle, :dword, :lpvoid], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-enumservicesstatusexw\n    # BOOL EnumServicesStatusExW(\n    #   SC_HANDLE    hSCManager,\n    #   SC_ENUM_TYPE InfoLevel,\n    #   DWORD        dwServiceType,\n    #   DWORD        dwServiceState,\n    #   LPBYTE       lpServices,\n    #   DWORD        cbBufSize,\n    #   LPDWORD      pcbBytesNeeded,\n    #   LPDWORD      lpServicesReturned,\n    #   LPDWORD      lpResumeHandle,\n    #   LPCWSTR      pszGroupName\n    # );\n    SC_ENUM_TYPE = enum(\n      :SC_ENUM_PROCESS_INFO, 0\n    )\n    ffi_lib :advapi32\n    attach_function_private :EnumServicesStatusExW,\n                            [\n                              :handle,\n                              SC_ENUM_TYPE,\n                              :dword,\n                              :dword,\n                              :lpbyte,\n                              :dword,\n                              :lpdword,\n                              :lpdword,\n                              :lpdword,\n                              :lpcwstr\n                            ], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512(v=vs.85).aspx\n    # BOOL WINAPI ReplaceFile(\n    #   _In_        LPCTSTR lpReplacedFileName,\n    #   _In_        LPCTSTR lpReplacementFileName,\n    #   _In_opt_    LPCTSTR lpBackupFileName,\n    #   _In_        DWORD dwReplaceFlags - 0x1 REPLACEFILE_WRITE_THROUGH,\n    #                                      0x2 REPLACEFILE_IGNORE_MERGE_ERRORS,\n    #                                      0x4 REPLACEFILE_IGNORE_ACL_ERRORS\n    #   _Reserved_  LPVOID lpExclude,\n    #   _Reserved_  LPVOID lpReserved\n    # );\n    ffi_lib :kernel32\n    attach_function_private :ReplaceFileW,\n                            [:lpcwstr, :lpcwstr, :lpcwstr, :dword, :lpvoid, :lpvoid], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx\n    # BOOL WINAPI MoveFileEx(\n    #   _In_      LPCTSTR lpExistingFileName,\n    #   _In_opt_  LPCTSTR lpNewFileName,\n    #   _In_      DWORD dwFlags\n    # );\n    ffi_lib :kernel32\n    attach_function_private :MoveFileExW,\n                            [:lpcwstr, :lpcwstr, :dword], :win32_bool\n\n    # BOOLEAN WINAPI CreateSymbolicLink(\n    #   _In_  LPTSTR lpSymlinkFileName, - symbolic link to be created\n    #   _In_  LPTSTR lpTargetFileName, - name of target for symbolic link\n    #   _In_  DWORD dwFlags - 0x0 target is a file, 0x1 target is a directory\n    # );\n    # rescue on Windows < 6.0 so that code doesn't explode\n    begin\n      ffi_lib :kernel32\n      attach_function_private :CreateSymbolicLinkW,\n                              [:lpwstr, :lpwstr, :dword], :boolean\n    rescue LoadError\n    end\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcurrentdirectory\n    # DWORD GetCurrentDirectory(\n    #   DWORD  nBufferLength,\n    #   LPTSTR lpBuffer\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetCurrentDirectoryW,\n                            [:dword, :lpwstr], :dword\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364944(v=vs.85).aspx\n    # DWORD WINAPI GetFileAttributes(\n    #   _In_  LPCTSTR lpFileName\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetFileAttributesW,\n                            [:lpcwstr], :dword\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535(v=vs.85).aspx\n    # BOOL WINAPI SetFileAttributes(\n    #   _In_  LPCTSTR lpFileName,\n    #   _In_  DWORD dwFileAttributes\n    # );\n    ffi_lib :kernel32\n    attach_function_private :SetFileAttributesW,\n                            [:lpcwstr, :dword], :win32_bool\n\n    # HANDLE WINAPI CreateFile(\n    #   _In_      LPCTSTR lpFileName,\n    #   _In_      DWORD dwDesiredAccess,\n    #   _In_      DWORD dwShareMode,\n    #   _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,\n    #   _In_      DWORD dwCreationDisposition,\n    #   _In_      DWORD dwFlagsAndAttributes,\n    #   _In_opt_  HANDLE hTemplateFile\n    # );\n    ffi_lib :kernel32\n    attach_function_private :CreateFileW,\n                            [:lpcwstr, :dword, :dword, :pointer, :dword, :dword, :handle], :handle\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectoryw\n    # BOOL CreateDirectoryW(\n    #   LPCWSTR               lpPathName,\n    #   LPSECURITY_ATTRIBUTES lpSecurityAttributes\n    # );\n    ffi_lib :kernel32\n    attach_function_private :CreateDirectoryW,\n                            [:lpcwstr, :pointer], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectoryw\n    # BOOL RemoveDirectoryW(\n    #   LPCWSTR lpPathName\n    # );\n    ffi_lib :kernel32\n    attach_function_private :RemoveDirectoryW,\n                            [:lpcwstr], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx\n    # BOOL WINAPI DeviceIoControl(\n    #   _In_         HANDLE hDevice,\n    #   _In_         DWORD dwIoControlCode,\n    #   _In_opt_     LPVOID lpInBuffer,\n    #   _In_         DWORD nInBufferSize,\n    #   _Out_opt_    LPVOID lpOutBuffer,\n    #   _In_         DWORD nOutBufferSize,\n    #   _Out_opt_    LPDWORD lpBytesReturned,\n    #   _Inout_opt_  LPOVERLAPPED lpOverlapped\n    # );\n    ffi_lib :kernel32\n    attach_function_private :DeviceIoControl,\n                            [:handle, :dword, :lpvoid, :dword, :lpvoid, :dword, :lpdword, :pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx\n    # DWORD WINAPI GetLongPathName(\n    #   _In_  LPCTSTR lpszShortPath,\n    #   _Out_ LPTSTR  lpszLongPath,\n    #   _In_  DWORD   cchBuffer\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetLongPathNameW,\n                            [:lpcwstr, :lpwstr, :dword], :dword\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx\n    # DWORD WINAPI GetShortPathName(\n    #   _In_  LPCTSTR lpszLongPath,\n    #   _Out_ LPTSTR  lpszShortPath,\n    #   _In_  DWORD   cchBuffer\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetShortPathNameW,\n                            [:lpcwstr, :lpwstr, :dword], :dword\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew\n    # DWORD GetFullPathNameW(\n    #   LPCWSTR lpFileName,\n    #   DWORD   nBufferLength,\n    #   LPWSTR  lpBuffer,\n    #   LPWSTR  *lpFilePart\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetFullPathNameW,\n                            [:lpcwstr, :dword, :lpwstr, :pointer], :dword\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw\n    # SHFOLDERAPI SHGetFolderPathW(\n    #   HWND   hwnd,\n    #   int    csidl,\n    #   HANDLE hToken,\n    #   DWORD  dwFlags,\n    #   LPWSTR pszPath\n    # );\n    ffi_lib :shell32\n    attach_function_private :SHGetFolderPathW,\n                            [:hwnd, :int, :handle, :dword, :lpwstr], :dword\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderlocation\n    # SHSTDAPI SHGetFolderLocation(\n    #   HWND             hwnd,\n    #   int              csidl,\n    #   HANDLE           hToken,\n    #   DWORD            dwFlags,\n    #   PIDLIST_ABSOLUTE *ppidl\n    # );\n    ffi_lib :shell32\n    attach_function_private :SHGetFolderLocation,\n                            [:hwnd, :int, :handle, :dword, :pointer], :dword\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfoa\n    # DWORD_PTR SHGetFileInfoA(\n    #   LPCSTR      pszPath,\n    #   DWORD       dwFileAttributes,\n    #   SHFILEINFOA *psfi,\n    #   UINT        cbFileInfo,\n    #   UINT        uFlags\n    # );\n    ffi_lib :shell32\n    attach_function_private :SHGetFileInfo,\n                            [:dword, :dword, :pointer, :uint, :uint], :dword\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathisdirectoryemptyw\n    # BOOL PathIsDirectoryEmptyW(\n    #   LPCWSTR pszPath\n    # );\n    ffi_lib :shlwapi\n    attach_function_private :PathIsDirectoryEmptyW,\n                            [:lpcwstr], :win32_bool\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/windows/structs.rb",
    "content": "# coding: utf-8\n# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::FFI::Windows\n  module Structs\n    extend FFI::Library\n    extend Puppet::FFI::Windows::APITypes\n\n    # https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)\n    # typedef struct _SECURITY_ATTRIBUTES {\n    #   DWORD  nLength;\n    #   LPVOID lpSecurityDescriptor;\n    #   BOOL   bInheritHandle;\n    # } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;\n    class SECURITY_ATTRIBUTES < FFI::Struct\n      layout(\n        :nLength, :dword,\n        :lpSecurityDescriptor, :lpvoid,\n        :bInheritHandle, :win32_bool\n      )\n    end\n\n    private_constant :SECURITY_ATTRIBUTES\n\n    # sizeof(STARTUPINFO) == 68\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa\n    # typedef struct _STARTUPINFOA {\n    #   DWORD  cb;\n    #   LPSTR  lpReserved;\n    #   LPSTR  lpDesktop;\n    #   LPSTR  lpTitle;\n    #   DWORD  dwX;\n    #   DWORD  dwY;\n    #   DWORD  dwXSize;\n    #   DWORD  dwYSize;\n    #   DWORD  dwXCountChars;\n    #   DWORD  dwYCountChars;\n    #   DWORD  dwFillAttribute;\n    #   DWORD  dwFlags;\n    #   WORD   wShowWindow;\n    #   WORD   cbReserved2;\n    #   LPBYTE lpReserved2;\n    #   HANDLE hStdInput;\n    #   HANDLE hStdOutput;\n    #   HANDLE hStdError;\n    # } STARTUPINFOA, *LPSTARTUPINFOA;\n    class STARTUPINFO < FFI::Struct\n      layout(\n        :cb, :dword,\n        :lpReserved, :lpcstr,\n        :lpDesktop, :lpcstr,\n        :lpTitle, :lpcstr,\n        :dwX, :dword,\n        :dwY, :dword,\n        :dwXSize, :dword,\n        :dwYSize, :dword,\n        :dwXCountChars, :dword,\n        :dwYCountChars, :dword,\n        :dwFillAttribute, :dword,\n        :dwFlags, :dword,\n        :wShowWindow, :word,\n        :cbReserved2, :word,\n        :lpReserved2, :pointer,\n        :hStdInput, :handle,\n        :hStdOutput, :handle,\n        :hStdError, :handle\n      )\n    end\n\n    private_constant :STARTUPINFO\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information\n    # typedef struct _PROCESS_INFORMATION {\n    #   HANDLE hProcess;\n    #   HANDLE hThread;\n    #   DWORD  dwProcessId;\n    #   DWORD  dwThreadId;\n    # } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;\n    class PROCESS_INFORMATION < FFI::Struct\n      layout(\n        :hProcess, :handle,\n        :hThread, :handle,\n        :dwProcessId, :dword,\n        :dwThreadId, :dword\n      )\n    end\n\n    private_constant :PROCESS_INFORMATION\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379261(v=vs.85).aspx\n    # typedef struct _LUID {\n    #   DWORD LowPart;\n    #   LONG  HighPart;\n    # } LUID, *PLUID;\n    class LUID < FFI::Struct\n      layout :LowPart, :dword,\n             :HighPart, :win32_long\n    end\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379263(v=vs.85).aspx\n    # typedef struct _LUID_AND_ATTRIBUTES {\n    #   LUID  Luid;\n    #   DWORD Attributes;\n    # } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;\n    class LUID_AND_ATTRIBUTES < FFI::Struct\n      layout :Luid, LUID,\n             :Attributes, :dword\n    end\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379630(v=vs.85).aspx\n    # typedef struct _TOKEN_PRIVILEGES {\n    #   DWORD               PrivilegeCount;\n    #   LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];\n    # } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;\n    class TOKEN_PRIVILEGES < FFI::Struct\n      layout :PrivilegeCount, :dword,\n             :Privileges, [LUID_AND_ATTRIBUTES, 1] # placeholder for offset\n    end\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/bb530717(v=vs.85).aspx\n    # typedef struct _TOKEN_ELEVATION {\n    #   DWORD TokenIsElevated;\n    # } TOKEN_ELEVATION, *PTOKEN_ELEVATION;\n    class TOKEN_ELEVATION < FFI::Struct\n      layout :TokenIsElevated, :dword\n    end\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_service_status_process\n    # typedef struct _SERVICE_STATUS_PROCESS {\n    #   DWORD dwServiceType;\n    #   DWORD dwCurrentState;\n    #   DWORD dwControlsAccepted;\n    #   DWORD dwWin32ExitCode;\n    #   DWORD dwServiceSpecificExitCode;\n    #   DWORD dwCheckPoint;\n    #   DWORD dwWaitHint;\n    #   DWORD dwProcessId;\n    #   DWORD dwServiceFlags;\n    # } SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;\n    class SERVICE_STATUS_PROCESS < FFI::Struct\n      layout(\n        :dwServiceType, :dword,\n        :dwCurrentState, :dword,\n        :dwControlsAccepted, :dword,\n        :dwWin32ExitCode, :dword,\n        :dwServiceSpecificExitCode, :dword,\n        :dwCheckPoint, :dword,\n        :dwWaitHint, :dword,\n        :dwProcessId, :dword,\n        :dwServiceFlags, :dword\n      )\n    end\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_delayed_auto_start_info\n    # typedef struct _SERVICE_DELAYED_AUTO_START_INFO {\n    #   BOOL fDelayedAutostart;\n    # } SERVICE_DELAYED_AUTO_START_INFO, *LPSERVICE_DELAYED_AUTO_START_INFO;\n    class SERVICE_DELAYED_AUTO_START_INFO < FFI::Struct\n      layout(:fDelayedAutostart, :int)\n      alias aset []=\n      # Intercept the accessor so that we can handle either true/false or 1/0.\n      # Since there is only one member, there’s no need to check the key name.\n      def []=(key, value)\n        [0, false].include?(value) ? aset(key, 0) : aset(key, 1)\n      end\n    end\n\n    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_enum_service_status_processw\n    # typedef struct _ENUM_SERVICE_STATUS_PROCESSW {\n    #   LPWSTR                 lpServiceName;\n    #   LPWSTR                 lpDisplayName;\n    #   SERVICE_STATUS_PROCESS ServiceStatusProcess;\n    # } ENUM_SERVICE_STATUS_PROCESSW, *LPENUM_SERVICE_STATUS_PROCESSW;\n    class ENUM_SERVICE_STATUS_PROCESSW < FFI::Struct\n      layout(\n        :lpServiceName, :pointer,\n        :lpDisplayName, :pointer,\n        :ServiceStatusProcess, SERVICE_STATUS_PROCESS\n      )\n    end\n\n    # typedef struct _SERVICE_STATUS {\n    #   DWORD dwServiceType;\n    #   DWORD dwCurrentState;\n    #   DWORD dwControlsAccepted;\n    #   DWORD dwWin32ExitCode;\n    #   DWORD dwServiceSpecificExitCode;\n    #   DWORD dwCheckPoint;\n    #   DWORD dwWaitHint;\n    # } SERVICE_STATUS, *LPSERVICE_STATUS;\n    class SERVICE_STATUS < FFI::Struct\n      layout(\n        :dwServiceType, :dword,\n        :dwCurrentState, :dword,\n        :dwControlsAccepted, :dword,\n        :dwWin32ExitCode, :dword,\n        :dwServiceSpecificExitCode, :dword,\n        :dwCheckPoint, :dword,\n        :dwWaitHint, :dword\n      )\n    end\n\n    # typedef struct _QUERY_SERVICE_CONFIGW {\n    #   DWORD  dwServiceType;\n    #   DWORD  dwStartType;\n    #   DWORD  dwErrorControl;\n    #   LPWSTR lpBinaryPathName;\n    #   LPWSTR lpLoadOrderGroup;\n    #   DWORD  dwTagId;\n    #   LPWSTR lpDependencies;\n    #   LPWSTR lpServiceStartName;\n    #   LPWSTR lpDisplayName;\n    # } QUERY_SERVICE_CONFIGW, *LPQUERY_SERVICE_CONFIGW;\n    class QUERY_SERVICE_CONFIGW < FFI::Struct\n      layout(\n        :dwServiceType, :dword,\n        :dwStartType, :dword,\n        :dwErrorControl, :dword,\n        :lpBinaryPathName, :pointer,\n        :lpLoadOrderGroup, :pointer,\n        :dwTagId, :dword,\n        :lpDependencies, :pointer,\n        :lpServiceStartName, :pointer,\n        :lpDisplayName, :pointer\n      )\n    end\n\n    # typedef struct _SERVICE_TABLE_ENTRYW {\n    #   LPWSTR                   lpServiceName;\n    #   LPSERVICE_MAIN_FUNCTIONW lpServiceProc;\n    # } SERVICE_TABLE_ENTRYW, *LPSERVICE_TABLE_ENTRYW;\n    class SERVICE_TABLE_ENTRYW < FFI::Struct\n      layout(\n        :lpServiceName, :pointer,\n        :lpServiceProc, :pointer\n      )\n    end\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724834%28v=vs.85%29.aspx\n    # typedef struct _OSVERSIONINFO {\n    #   DWORD dwOSVersionInfoSize;\n    #   DWORD dwMajorVersion;\n    #   DWORD dwMinorVersion;\n    #   DWORD dwBuildNumber;\n    #   DWORD dwPlatformId;\n    #   TCHAR szCSDVersion[128];\n    # } OSVERSIONINFO;\n    class OSVERSIONINFO < FFI::Struct\n      layout(\n        :dwOSVersionInfoSize, :dword,\n        :dwMajorVersion, :dword,\n        :dwMinorVersion, :dword,\n        :dwBuildNumber, :dword,\n        :dwPlatformId, :dword,\n        :szCSDVersion, [:wchar, 128]\n      )\n    end\n\n    MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16_384\n\n    # SYMLINK_REPARSE_DATA_BUFFER\n    # https://msdn.microsoft.com/en-us/library/cc232006.aspx\n    # https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx\n    # struct is always MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes\n    class SYMLINK_REPARSE_DATA_BUFFER < FFI::Struct\n      layout :ReparseTag, :win32_ulong,\n             :ReparseDataLength, :ushort,\n             :Reserved, :ushort,\n             :SubstituteNameOffset, :ushort,\n             :SubstituteNameLength, :ushort,\n             :PrintNameOffset, :ushort,\n             :PrintNameLength, :ushort,\n             :Flags, :win32_ulong,\n             # max less above fields dword / uint 4 bytes, ushort 2 bytes\n             # technically a WCHAR buffer, but we care about size in bytes here\n             :PathBuffer, [:byte, MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 20]\n    end\n\n    # MOUNT_POINT_REPARSE_DATA_BUFFER\n    # https://msdn.microsoft.com/en-us/library/cc232007.aspx\n    # https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx\n    # struct is always MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes\n    class MOUNT_POINT_REPARSE_DATA_BUFFER < FFI::Struct\n      layout :ReparseTag, :win32_ulong,\n             :ReparseDataLength, :ushort,\n             :Reserved, :ushort,\n             :SubstituteNameOffset, :ushort,\n             :SubstituteNameLength, :ushort,\n             :PrintNameOffset, :ushort,\n             :PrintNameLength, :ushort,\n             # max less above fields dword / uint 4 bytes, ushort 2 bytes\n             # technically a WCHAR buffer, but we care about size in bytes here\n             :PathBuffer, [:byte, MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 16]\n    end\n\n    # SHFILEINFO\n    # https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shfileinfow\n    # typedef struct _SHFILEINFOW {\n    #   HICON hIcon;\n    #   int   iIcon;\n    #   DWORD dwAttributes;\n    #   WCHAR szDisplayName[MAX_PATH];\n    #   WCHAR szTypeName[80];\n    # } SHFILEINFOW;\n    class SHFILEINFO < FFI::Struct\n      layout(\n        :hIcon, :ulong,\n        :iIcon, :int,\n        :dwAttributes, :ulong,\n        :szDisplayName, [:char, 256],\n        :szTypeName, [:char, 80]\n      )\n    end\n\n    # REPARSE_JDATA_BUFFER\n    class REPARSE_JDATA_BUFFER < FFI::Struct\n      layout(\n        :ReparseTag, :ulong,\n        :ReparseDataLength, :ushort,\n        :Reserved, :ushort,\n        :SubstituteNameOffset, :ushort,\n        :SubstituteNameLength, :ushort,\n        :PrintNameOffset, :ushort,\n        :PrintNameLength, :ushort,\n        :PathBuffer, [:char, 1024]\n      )\n\n      # The REPARSE_DATA_BUFFER_HEADER_SIZE which is calculated as:\n      #\n      # sizeof(ReparseTag) + sizeof(ReparseDataLength) + sizeof(Reserved)\n      #\n      def header_size\n        FFI::Type::ULONG.size + FFI::Type::USHORT.size + FFI::Type::USHORT.size\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ffi/windows.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'ffi'\n\nmodule Puppet\n  module FFI\n    module Windows\n      require_relative 'windows/api_types'\n      require_relative 'windows/constants'\n      require_relative 'windows/structs'\n      require_relative 'windows/functions'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_bucket/dipper.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire_relative '../../puppet/file_bucket'\nrequire_relative '../../puppet/file_bucket/file'\nrequire_relative '../../puppet/indirector/request'\nrequire_relative '../../puppet/util/diff'\nrequire 'tempfile'\n\nclass Puppet::FileBucket::Dipper\n  include Puppet::Util::Checksums\n  # This is a transitional implementation that uses REST\n  # to access remote filebucket files.\n\n  attr_accessor :name\n\n  # Creates a bucket client\n  def initialize(hash = {})\n    # Emulate the XMLRPC client\n    server      = hash[:Server]\n    port        = hash[:Port] || Puppet[:serverport]\n\n    if hash.include?(:Path)\n      @local_path = hash[:Path]\n      @rest_path  = nil\n    else\n      @local_path = nil\n      @rest_path = \"filebucket://#{server}:#{port}/\"\n    end\n    @checksum_type = Puppet[:digest_algorithm].to_sym\n    @digest = method(@checksum_type)\n  end\n\n  def local?\n    !!@local_path\n  end\n\n  # Backs up a file to the file bucket\n  def backup(file)\n    file_handle = Puppet::FileSystem.pathname(file)\n    raise(ArgumentError, _(\"File %{file} does not exist\") % { file: file }) unless Puppet::FileSystem.exist?(file_handle)\n\n    begin\n      file_bucket_file = Puppet::FileBucket::File.new(file_handle, :bucket_path => @local_path)\n      files_original_path = absolutize_path(file)\n      dest_path = \"#{@rest_path}#{file_bucket_file.name}/#{files_original_path}\"\n      file_bucket_path = \"#{@rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}/#{files_original_path}\"\n\n      # Make a HEAD request for the file so that we don't waste time\n      # uploading it if it already exists in the bucket.\n      unless Puppet::FileBucket::File.indirection.head(file_bucket_path, :bucket_path => file_bucket_file.bucket_path)\n        Puppet::FileBucket::File.indirection.save(file_bucket_file, dest_path)\n      end\n\n      file_bucket_file.checksum_data\n    rescue => detail\n      message = _(\"Could not back up %{file}: %{detail}\") % { file: file, detail: detail }\n      Puppet.log_exception(detail, message)\n      raise Puppet::Error, message, detail.backtrace\n    end\n  end\n\n  # Diffs two filebucket files identified by their sums\n  def diff(checksum_a, checksum_b, file_a, file_b)\n    raise RuntimeError, _(\"Diff is not supported on this platform\") if Puppet[:diff] == \"\"\n\n    if checksum_a\n      source_path = \"#{@rest_path}#{@checksum_type}/#{checksum_a}\"\n      if checksum_b\n        file_diff = Puppet::FileBucket::File.indirection.find(\n          source_path,\n          :bucket_path => @local_path,\n          :diff_with => checksum_b\n        )\n      elsif file_b\n        tmp_file = ::Tempfile.new('diff')\n        begin\n          restore(tmp_file.path, checksum_a)\n          file_diff = Puppet::Util::Diff.diff(tmp_file.path, file_b)\n        ensure\n          tmp_file.close\n          tmp_file.unlink\n        end\n      else\n        raise Puppet::Error, _(\"Please provide a file or checksum to diff with\")\n      end\n    elsif file_a\n      if checksum_b\n        tmp_file = ::Tempfile.new('diff')\n        begin\n          restore(tmp_file.path, checksum_b)\n          file_diff = Puppet::Util::Diff.diff(file_a, tmp_file.path)\n        ensure\n          tmp_file.close\n          tmp_file.unlink\n        end\n      elsif file_b\n        file_diff = Puppet::Util::Diff.diff(file_a, file_b)\n      end\n    end\n    raise Puppet::Error, _(\"Failed to diff files\") unless file_diff\n\n    file_diff.to_s\n  end\n\n  # Retrieves a file by sum.\n  def getfile(sum)\n    get_bucket_file(sum).to_s\n  end\n\n  # Retrieves a FileBucket::File by sum.\n  def get_bucket_file(sum)\n    source_path = \"#{@rest_path}#{@checksum_type}/#{sum}\"\n    file_bucket_file = Puppet::FileBucket::File.indirection.find(source_path, :bucket_path => @local_path)\n\n    raise Puppet::Error, _(\"File not found\") unless file_bucket_file\n\n    file_bucket_file\n  end\n\n  # Restores the file\n  def restore(file, sum)\n    restore = true\n    file_handle = Puppet::FileSystem.pathname(file)\n    if Puppet::FileSystem.exist?(file_handle)\n      cursum = Puppet::FileBucket::File.new(file_handle).checksum_data()\n\n      # if the checksum has changed...\n      # this might be extra effort\n      if cursum == sum\n        restore = false\n      end\n    end\n\n    if restore\n      newcontents = get_bucket_file(sum)\n      if newcontents\n        newsum = newcontents.checksum_data\n        changed = nil\n        if Puppet::FileSystem.exist?(file_handle) and !Puppet::FileSystem.writable?(file_handle)\n          changed = Puppet::FileSystem.stat(file_handle).mode\n          ::File.chmod(changed | 0o200, file)\n        end\n        ::File.open(file, ::File::WRONLY | ::File::TRUNC | ::File::CREAT) { |of|\n          of.binmode\n          newcontents.stream do |source_stream|\n            FileUtils.copy_stream(source_stream, of)\n          end\n        }\n        ::File.chmod(changed, file) if changed\n      else\n        Puppet.err _(\"Could not find file with checksum %{sum}\") % { sum: sum }\n        return nil\n      end\n      newsum\n    else\n      nil\n    end\n  end\n\n  # List Filebucket content.\n  def list(fromdate, todate)\n    raise Puppet::Error, _(\"Listing remote file buckets is not allowed\") unless local?\n\n    source_path = \"#{@rest_path}#{@checksum_type}/\"\n    file_bucket_list = Puppet::FileBucket::File.indirection.find(\n      source_path,\n      :bucket_path => @local_path,\n      :list_all => true,\n      :fromdate => fromdate,\n      :todate => todate\n    )\n    raise Puppet::Error, _(\"File not found\") unless file_bucket_list\n\n    file_bucket_list.to_s\n  end\n\n  private\n\n  def absolutize_path(path)\n    Pathname.new(path).realpath\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_bucket/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_bucket'\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/util/checksums'\nrequire 'digest/md5'\nrequire 'stringio'\n\nclass Puppet::FileBucket::File\n  # This class handles the abstract notion of a file in a filebucket.\n  # There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/*\n  # There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper\n  extend Puppet::Indirector\n  indirects :file_bucket_file, :terminus_class => :selector\n\n  attr_reader :bucket_path\n\n  def self.supported_formats\n    [:binary]\n  end\n\n  def initialize(contents, options = {})\n    case contents\n    when String\n      @contents = StringContents.new(contents)\n    when Pathname\n      @contents = FileContents.new(contents)\n    else\n      raise ArgumentError, _(\"contents must be a String or Pathname, got a %{contents_class}\") % { contents_class: contents.class }\n    end\n\n    @bucket_path = options.delete(:bucket_path)\n    @checksum_type = Puppet[:digest_algorithm].to_sym\n    raise ArgumentError, _(\"Unknown option(s): %{opts}\") % { opts: options.keys.join(', ') } unless options.empty?\n  end\n\n  # @return [Num] The size of the contents\n  def size\n    @contents.size()\n  end\n\n  # @return [IO] A stream that reads the contents\n  def stream(&block)\n    @contents.stream(&block)\n  end\n\n  def checksum_type\n    @checksum_type.to_s\n  end\n\n  def checksum\n    \"{#{checksum_type}}#{checksum_data}\"\n  end\n\n  def checksum_data\n    @checksum_data ||= @contents.checksum_data(@checksum_type)\n  end\n\n  def to_s\n    to_binary\n  end\n\n  def to_binary\n    @contents.to_binary\n  end\n\n  def contents\n    to_binary\n  end\n\n  def name\n    \"#{checksum_type}/#{checksum_data}\"\n  end\n\n  def self.from_binary(contents)\n    new(contents)\n  end\n\n  class StringContents\n    def initialize(content)\n      @contents = content;\n    end\n\n    def stream(&block)\n      s = StringIO.new(@contents)\n      begin\n        block.call(s)\n      ensure\n        s.close\n      end\n    end\n\n    def size\n      @contents.size\n    end\n\n    def checksum_data(base_method)\n      Puppet.info(_(\"Computing checksum on string\"))\n      Puppet::Util::Checksums.method(base_method).call(@contents)\n    end\n\n    def to_binary\n      # This is not so horrible as for FileContent, but still possible to mutate the content that the\n      # checksum is based on... so semi horrible...\n      @contents;\n    end\n  end\n\n  class FileContents\n    def initialize(path)\n      @path = path\n    end\n\n    def stream(&block)\n      Puppet::FileSystem.open(@path, nil, 'rb', &block)\n    end\n\n    def size\n      Puppet::FileSystem.size(@path)\n    end\n\n    def checksum_data(base_method)\n      Puppet.info(_(\"Computing checksum on file %{path}\") % { path: @path })\n      Puppet::Util::Checksums.method(:\"#{base_method}_file\").call(@path)\n    end\n\n    def to_binary\n      Puppet::FileSystem.binread(@path)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_bucket.rb",
    "content": "# frozen_string_literal: true\n\n# stub\nmodule Puppet::FileBucket\n  class BucketError < RuntimeError; end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/util'\n\n# The base class for Content and Metadata; provides common\n# functionality like the behaviour around links.\nclass Puppet::FileServing::Base\n  # This is for external consumers to store the source that was used\n  # to retrieve the metadata.\n  attr_accessor :source\n\n  # Does our file exist?\n  def exist?\n    stat\n    true\n  rescue\n    false\n  end\n\n  # Return the full path to our file.  Fails if there's no path set.\n  def full_path\n    if relative_path.nil? or relative_path == \"\" or relative_path == \".\"\n      full_path = path\n    else\n      full_path = File.join(path, relative_path)\n    end\n\n    if Puppet::Util::Platform.windows?\n      # Replace multiple slashes as long as they aren't at the beginning of a filename\n      full_path.gsub(%r{(./)/+}, '\\1')\n    else\n      full_path.gsub(%r{//+}, '/')\n    end\n  end\n\n  def initialize(path, links: nil, relative_path: nil, source: nil)\n    self.path = path\n    @links = :manage\n\n    self.links = links if links\n    self.relative_path = relative_path if relative_path\n    self.source = source if source\n  end\n\n  # Determine how we deal with links.\n  attr_reader :links\n\n  def links=(value)\n    value = value.to_sym\n    value = :manage if value == :ignore\n    # TRANSLATORS ':link', ':manage', ':follow' should not be translated\n    raise(ArgumentError, _(\":links can only be set to :manage or :follow\")) unless [:manage, :follow].include?(value)\n\n    @links = value\n  end\n\n  # Set our base path.\n  attr_reader :path\n\n  def path=(path)\n    raise ArgumentError, _(\"Paths must be fully qualified\") unless Puppet::FileServing::Base.absolute?(path)\n\n    @path = path\n  end\n\n  # Set a relative path; this is used for recursion, and sets\n  # the file's path relative to the initial recursion point.\n  attr_reader :relative_path\n\n  def relative_path=(path)\n    raise ArgumentError, _(\"Relative paths must not be fully qualified\") if Puppet::FileServing::Base.absolute?(path)\n\n    @relative_path = path\n  end\n\n  # Stat our file, using the appropriate link-sensitive method.\n  def stat\n    @stat_method ||= links == :manage ? :lstat : :stat\n    Puppet::FileSystem.send(@stat_method, full_path)\n  end\n\n  def to_data_hash\n    {\n      'path' => @path,\n      'relative_path' => @relative_path,\n      'links' => @links.to_s\n    }\n  end\n\n  def self.absolute?(path)\n    Puppet::Util.absolute_path?(path, :posix) || (Puppet::Util::Platform.windows? && Puppet::Util.absolute_path?(path, :windows))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/configuration/parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/configuration'\nrequire_relative '../../../puppet/util/watched_file'\n\nclass Puppet::FileServing::Configuration::Parser\n  Mount = Puppet::FileServing::Mount\n  MODULES = 'modules'\n\n  # Parse our configuration file.\n  def parse\n    raise(_(\"File server configuration %{config_file} does not exist\") % { config_file: @file }) unless Puppet::FileSystem.exist?(@file)\n    raise(_(\"Cannot read file server configuration %{config_file}\") % { config_file: @file }) unless FileTest.readable?(@file)\n\n    @mounts = {}\n    @count = 0\n\n    File.open(@file) do |f|\n      mount = nil\n      f.each_line do |line|\n        # Have the count increment at the top, in case we throw exceptions.\n        @count += 1\n\n        case line\n        when /^\\s*#/; next # skip comments\n        when /^\\s*$/; next # skip blank lines\n        when /\\[([-\\w]+)\\]/\n          mount = newmount(::Regexp.last_match(1))\n        when /^\\s*(\\w+)\\s+(.+?)(\\s*#.*)?$/\n          var = ::Regexp.last_match(1)\n          value = ::Regexp.last_match(2)\n          value.strip!\n          raise(ArgumentError, _(\"Fileserver configuration file does not use '=' as a separator\")) if value =~ /^=/\n\n          case var\n          when \"path\"\n            path(mount, value)\n          when \"allow\", \"deny\"\n            # ignore `allow *`, otherwise report error\n            if var != 'allow' || value != '*'\n              error_location_str = Puppet::Util::Errors.error_location(@file.filename, @count)\n              Puppet.err(\"Entry '#{line.chomp}' is unsupported and will be ignored at #{error_location_str}\")\n            end\n          else\n            error_location_str = Puppet::Util::Errors.error_location(@file.filename, @count)\n            raise ArgumentError, _(\"Invalid argument '%{var}' at %{error_location}\") %\n                                 { var: var, error_location: error_location_str }\n          end\n        else\n          error_location_str = Puppet::Util::Errors.error_location(@file.filename, @count)\n          raise ArgumentError, _(\"Invalid entry at %{error_location}: '%{file_text}'\") %\n                               { file_text: line.chomp, error_location: error_location_str }\n        end\n      end\n    end\n\n    validate\n\n    @mounts\n  end\n\n  def initialize(filename)\n    @file = Puppet::Util::WatchedFile.new(filename)\n  end\n\n  def changed?\n    @file.changed?\n  end\n\n  private\n\n  # Create a new mount.\n  def newmount(name)\n    if @mounts.include?(name)\n      error_location_str = Puppet::Util::Errors.error_location(@file, @count)\n      raise ArgumentError, _(\"%{mount} is already mounted at %{name} at %{error_location}\") %\n                           { mount: @mounts[name], name: name, error_location: error_location_str }\n    end\n    case name\n    when \"modules\"\n      mount = Mount::Modules.new(name)\n    when \"plugins\"\n      mount = Mount::Plugins.new(name)\n    when \"scripts\"\n      mount = Mount::Scripts.new(name)\n    when \"tasks\"\n      mount = Mount::Tasks.new(name)\n    when \"locales\"\n      mount = Mount::Locales.new(name)\n    else\n      mount = Mount::File.new(name)\n    end\n    @mounts[name] = mount\n    mount\n  end\n\n  # Set the path for a mount.\n  def path(mount, value)\n    if mount.respond_to?(:path=)\n      begin\n        mount.path = value\n      rescue ArgumentError => detail\n        Puppet.log_exception(detail, _(\"Removing mount \\\"%{mount}\\\": %{detail}\") % { mount: mount.name, detail: detail })\n        @mounts.delete(mount.name)\n      end\n    else\n      Puppet.warning _(\"The '%{mount}' module can not have a path. Ignoring attempt to set it\") % { mount: mount.name }\n    end\n  end\n\n  # Make sure all of our mounts are valid.  We have to do this after the fact\n  # because details are added over time as the file is parsed.\n  def validate\n    @mounts.each { |_name, mount| mount.validate }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/configuration.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/file_serving/mount'\nrequire_relative '../../puppet/file_serving/mount/file'\nrequire_relative '../../puppet/file_serving/mount/modules'\nrequire_relative '../../puppet/file_serving/mount/plugins'\nrequire_relative '../../puppet/file_serving/mount/locales'\nrequire_relative '../../puppet/file_serving/mount/pluginfacts'\nrequire_relative '../../puppet/file_serving/mount/scripts'\nrequire_relative '../../puppet/file_serving/mount/tasks'\n\nclass Puppet::FileServing::Configuration\n  require_relative 'configuration/parser'\n\n  def self.configuration\n    @configuration ||= new\n  end\n\n  Mount = Puppet::FileServing::Mount\n\n  private_class_method :new\n\n  attr_reader :mounts\n\n  # private :mounts\n\n  # Find the right mount.  Does some shenanigans to support old-style module\n  # mounts.\n  def find_mount(mount_name, environment)\n    # Reparse the configuration if necessary.\n    readconfig\n    # This can be nil.\n    mounts[mount_name]\n  end\n\n  def initialize\n    @mounts = {}\n    @config_file = nil\n\n    # We don't check to see if the file is modified the first time,\n    # because we always want to parse at first.\n    readconfig(false)\n  end\n\n  # Is a given mount available?\n  def mounted?(name)\n    @mounts.include?(name)\n  end\n\n  # Split the path into the separate mount point and path.\n  def split_path(request)\n    # Reparse the configuration if necessary.\n    readconfig\n\n    mount_name, path = request.key.split(File::Separator, 2)\n\n    raise(ArgumentError, _(\"Cannot find file: Invalid mount '%{mount_name}'\") % { mount_name: mount_name }) unless mount_name =~ /^[-\\w]+$/\n    raise(ArgumentError, _(\"Cannot find file: Invalid relative path '%{path}'\") % { path: path }) if path and path.split('/').include?('..')\n\n    mount = find_mount(mount_name, request.environment)\n    return nil unless mount\n\n    if mount.name == \"modules\" and mount_name != \"modules\"\n      # yay backward-compatibility\n      path = \"#{mount_name}/#{path}\"\n    end\n\n    if path == \"\"\n      path = nil\n    elsif path\n      # Remove any double slashes that might have occurred\n      path = path.gsub(%r{/+}, \"/\")\n    end\n\n    [mount, path]\n  end\n\n  def umount(name)\n    @mounts.delete(name) if @mounts.include? name\n  end\n\n  private\n\n  def mk_default_mounts\n    @mounts[\"modules\"] ||= Mount::Modules.new(\"modules\")\n    @mounts[\"plugins\"] ||= Mount::Plugins.new(\"plugins\")\n    @mounts[\"locales\"] ||= Mount::Locales.new(\"locales\")\n    @mounts[\"pluginfacts\"] ||= Mount::PluginFacts.new(\"pluginfacts\")\n    @mounts[\"scripts\"] ||= Mount::Scripts.new(\"scripts\")\n    @mounts[\"tasks\"] ||= Mount::Tasks.new(\"tasks\")\n  end\n\n  # Read the configuration file.\n  def readconfig(check = true)\n    config = Puppet[:fileserverconfig]\n\n    return unless Puppet::FileSystem.exist?(config)\n\n    @parser ||= Puppet::FileServing::Configuration::Parser.new(config)\n\n    return if check and !@parser.changed?\n\n    # Don't assign the mounts hash until we're sure the parsing succeeded.\n    begin\n      newmounts = @parser.parse\n      @mounts = newmounts\n    rescue => detail\n      Puppet.log_exception(detail, _(\"Error parsing fileserver configuration: %{detail}; using old configuration\") % { detail: detail })\n    end\n  ensure\n    # Make sure we've got our plugins and modules.\n    mk_default_mounts\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/content.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/file_serving/base'\n\n# A class that handles retrieving file contents.\n# It only reads the file when its content is specifically\n# asked for.\nclass Puppet::FileServing::Content < Puppet::FileServing::Base\n  extend Puppet::Indirector\n  indirects :file_content, :terminus_class => :selector\n\n  attr_writer :content\n\n  def self.supported_formats\n    [:binary]\n  end\n\n  def self.from_binary(content)\n    instance = new(\"/this/is/a/fake/path\")\n    instance.content = content\n    instance\n  end\n\n  # This is no longer used, but is still called by the file server implementations when interacting\n  # with their model abstraction.\n  def collect(source_permissions = nil)\n  end\n\n  # Read the content of our file in.\n  def content\n    unless @content\n      # This stat can raise an exception, too.\n      raise(ArgumentError, _(\"Cannot read the contents of links unless following links\")) if stat.ftype == \"symlink\"\n\n      @content = Puppet::FileSystem.binread(full_path)\n    end\n    @content\n  end\n\n  def to_binary\n    File.new(full_path, \"rb\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/fileset.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'find'\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/file_serving/metadata'\n\n# Operate recursively on a path, returning a set of file paths.\nclass Puppet::FileServing::Fileset\n  attr_reader :path, :ignore, :links\n  attr_accessor :recurse, :recurselimit, :max_files, :checksum_type\n\n  # Produce a hash of files, with merged so that earlier files\n  # with the same postfix win.  E.g., /dir1/subfile beats /dir2/subfile.\n  # It's a hash because we need to know the relative path of each file,\n  # and the base directory.\n  #   This will probably only ever be used for searching for plugins.\n  def self.merge(*filesets)\n    result = {}\n\n    filesets.each do |fileset|\n      fileset.files.each do |file|\n        result[file] ||= fileset.path\n      end\n    end\n\n    result\n  end\n\n  def initialize(path, options = {})\n    if Puppet::Util::Platform.windows?\n      # REMIND: UNC path\n      path = path.chomp(File::SEPARATOR) unless path =~ %r{^[A-Za-z]:/$}\n    else\n      path = path.chomp(File::SEPARATOR) unless path == File::SEPARATOR\n    end\n    raise ArgumentError, _(\"Fileset paths must be fully qualified: %{path}\") % { path: path } unless Puppet::Util.absolute_path?(path)\n\n    @path = path\n\n    # Set our defaults.\n    self.ignore = []\n    self.links = :manage\n    @recurse = false\n    @recurselimit = :infinite\n    @max_files = 0\n\n    if options.is_a?(Puppet::Indirector::Request)\n      initialize_from_request(options)\n    else\n      initialize_from_hash(options)\n    end\n\n    raise ArgumentError, _(\"Fileset paths must exist\") unless valid?(path)\n    # TRANSLATORS \"recurse\" and \"recurselimit\" are parameter names and should not be translated\n    raise ArgumentError, _(\"Fileset recurse parameter must not be a number anymore, please use recurselimit\") if @recurse.is_a?(Integer)\n  end\n\n  # Return a list of all files in our fileset.  This is different from the\n  # normal definition of find in that we support specific levels\n  # of recursion, which means we need to know when we're going another\n  # level deep, which Find doesn't do.\n  def files\n    files = perform_recursion\n    soft_max_files = 1000\n\n    # munged_max_files is needed since puppet http handler is keeping negative numbers as strings\n    # https://github.com/puppetlabs/puppet/blob/main/lib/puppet/network/http/handler.rb#L196-L197\n    munged_max_files = max_files == '-1' ? -1 : max_files\n\n    if munged_max_files > 0 && files.size > munged_max_files\n      raise Puppet::Error, _(\"The directory '%{path}' contains %{entries} entries, which exceeds the limit of %{munged_max_files} specified by the max_files parameter for this resource. The limit may be increased, but be aware that large number of file resources can result in excessive resource consumption and degraded performance. Consider using an alternate method to manage large directory trees\") % { path: path, entries: files.size, munged_max_files: munged_max_files }\n    elsif munged_max_files == 0 && files.size > soft_max_files\n      Puppet.warning _(\"The directory '%{path}' contains %{entries} entries, which exceeds the default soft limit %{soft_max_files} and may cause excessive resource consumption and degraded performance. To remove this warning set a value for `max_files` parameter or consider using an alternate method to manage large directory trees\") % { path: path, entries: files.size, soft_max_files: soft_max_files }\n    end\n\n    # Now strip off the leading path, so each file becomes relative, and remove\n    # any slashes that might end up at the beginning of the path.\n    result = files.collect { |file| file.sub(%r{^#{Regexp.escape(@path)}/*}, '') }\n\n    # And add the path itself.\n    result.unshift(\".\")\n\n    result\n  end\n\n  def ignore=(values)\n    values = [values] unless values.is_a?(Array)\n    @ignore = values.collect(&:to_s)\n  end\n\n  def links=(links)\n    links = links.to_sym\n    # TRANSLATORS \":links\" is a parameter name and should not be translated\n    raise(ArgumentError, _(\"Invalid :links value '%{links}'\") % { links: links }) unless [:manage, :follow].include?(links)\n\n    @links = links\n    @stat_method = @links == :manage ? :lstat : :stat\n  end\n\n  private\n\n  def initialize_from_hash(options)\n    options.each do |option, value|\n      method = option.to_s + \"=\"\n      begin\n        send(method, value)\n      rescue NoMethodError => e\n        raise ArgumentError, _(\"Invalid option '%{option}'\") % { option: option }, e.backtrace\n      end\n    end\n  end\n\n  def initialize_from_request(request)\n    [:links, :ignore, :recurse, :recurselimit, :max_files, :checksum_type].each do |param|\n      if request.options.include?(param) # use 'include?' so the values can be false\n        value = request.options[param]\n      elsif request.options.include?(param.to_s)\n        value = request.options[param.to_s]\n      end\n      next if value.nil?\n\n      value = true if value == \"true\"\n      value = false if value == \"false\"\n      value = Integer(value) if value.is_a?(String) and value =~ /^\\d+$/\n      send(param.to_s + \"=\", value)\n    end\n  end\n\n  FileSetEntry = Struct.new(:depth, :path, :ignored, :stat_method) do\n    def down_level(to)\n      FileSetEntry.new(depth + 1, File.join(path, to), ignored, stat_method)\n    end\n\n    def basename\n      File.basename(path)\n    end\n\n    def children\n      return [] unless directory?\n\n      Dir.entries(path, encoding: Encoding::UTF_8)\n         .reject { |child| ignore?(child) }\n         .collect { |child| down_level(child) }\n    end\n\n    def ignore?(child)\n      return true if child == \".\" || child == \"..\"\n      return false if ignored == [nil]\n\n      ignored.any? { |pattern| File.fnmatch?(pattern, child) }\n    end\n\n    def directory?\n      Puppet::FileSystem.send(stat_method, path).directory?\n    rescue Errno::ENOENT, Errno::EACCES\n      false\n    end\n  end\n\n  # Pull the recursion logic into one place.  It's moderately hairy, and this\n  # allows us to keep the hairiness apart from what we do with the files.\n  def perform_recursion\n    current_dirs = [FileSetEntry.new(0, @path, @ignore, @stat_method)]\n\n    result = []\n\n    while entry = current_dirs.shift # rubocop:disable Lint/AssignmentInCondition\n      next unless continue_recursion_at?(entry.depth + 1)\n\n      entry.children.each do |child|\n        result << child.path\n        current_dirs << child\n      end\n    end\n\n    result\n  end\n\n  def valid?(path)\n    Puppet::FileSystem.send(@stat_method, path)\n    true\n  rescue Errno::ENOENT, Errno::EACCES\n    false\n  end\n\n  def continue_recursion_at?(depth)\n    # recurse if told to, and infinite recursion or current depth not at the limit\n    recurse && (recurselimit == :infinite || depth <= recurselimit)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/http_metadata.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving/metadata'\n\n# Simplified metadata representation, suitable for the information\n# that is available from HTTP headers.\nclass Puppet::FileServing::HttpMetadata < Puppet::FileServing::Metadata\n  def initialize(http_response, path = '/dev/null')\n    super(path)\n\n    # ignore options that do not apply to HTTP metadata\n    @owner = @group = @mode = nil\n\n    # hash available checksums for eventual collection\n    @checksums = {}\n    # use a default mtime in case there is no usable HTTP header\n    @checksums[:mtime] = \"{mtime}#{Time.now}\"\n\n    # RFC-1864, deprecated in HTTP/1.1 due to partial responses\n    checksum = http_response['content-md5']\n    if checksum\n      # convert base64 digest to hex\n      checksum = checksum.unpack1(\"m\").unpack1(\"H*\")\n      @checksums[:md5] = \"{md5}#{checksum}\"\n    end\n\n    {\n      md5: 'X-Checksum-Md5',\n      sha1: 'X-Checksum-Sha1',\n      sha256: 'X-Checksum-Sha256'\n    }.each_pair do |checksum_type, header|\n      checksum = http_response[header]\n      if checksum\n        @checksums[checksum_type] = \"{#{checksum_type}}#{checksum}\"\n      end\n    end\n\n    last_modified = http_response['last-modified']\n    if last_modified\n      mtime = DateTime.httpdate(last_modified).to_time\n      @checksums[:mtime] = \"{mtime}#{mtime.utc}\"\n    end\n\n    @ftype = 'file'\n  end\n\n  # Override of the parent class method. Does not call super!\n  # We can only return metadata that was extracted from the\n  # HTTP headers during #initialize.\n  def collect\n    # Prefer the checksum_type from the indirector request options\n    # but fall back to the alternative otherwise\n    [@checksum_type, :sha256, :sha1, :md5, :mtime].each do |type|\n      next if type == :md5 && Puppet::Util::Platform.fips_enabled?\n\n      @checksum_type = type\n      @checksum = @checksums[type]\n      break if @checksum\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/metadata.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/file_serving/base'\nrequire_relative '../../puppet/util/checksums'\nrequire 'uri'\n\n# A class that handles retrieving file metadata.\nclass Puppet::FileServing::Metadata < Puppet::FileServing::Base\n  include Puppet::Util::Checksums\n\n  extend Puppet::Indirector\n  indirects :file_metadata, :terminus_class => :selector\n\n  attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum, :ftype, :destination, :source_permissions, :content_uri\n\n  PARAM_ORDER = [:mode, :ftype, :owner, :group]\n\n  def checksum_type=(type)\n    raise(ArgumentError, _(\"Unsupported checksum type %{type}\") % { type: type }) unless Puppet::Util::Checksums.respond_to?(\"#{type}_file\")\n\n    @checksum_type = type\n  end\n\n  def source_permissions=(source_permissions)\n    raise(ArgumentError, _(\"Unsupported source_permission %{source_permissions}\") % { source_permissions: source_permissions }) unless [:use, :use_when_creating, :ignore].include?(source_permissions.intern)\n\n    @source_permissions = source_permissions.intern\n  end\n\n  def content_uri=(path)\n    begin\n      uri = URI.parse(Puppet::Util.uri_encode(path))\n    rescue URI::InvalidURIError => detail\n      raise(ArgumentError, _(\"Could not understand URI %{path}: %{detail}\") % { path: path, detail: detail })\n    end\n    raise(ArgumentError, _(\"Cannot use opaque URLs '%{path}'\") % { path: path }) unless uri.hierarchical?\n    raise(ArgumentError, _(\"Must use URLs of type puppet as content URI\")) if uri.scheme != \"puppet\"\n\n    @content_uri = path.encode(Encoding::UTF_8)\n  end\n\n  class MetaStat\n    extend Forwardable\n\n    def initialize(stat, source_permissions)\n      @stat = stat\n      @source_permissions_ignore = !source_permissions || source_permissions == :ignore\n    end\n\n    def owner\n      @source_permissions_ignore ? Process.euid : @stat.uid\n    end\n\n    def group\n      @source_permissions_ignore ? Process.egid : @stat.gid\n    end\n\n    def mode\n      @source_permissions_ignore ? 0o644 : @stat.mode\n    end\n\n    def_delegators :@stat, :ftype\n  end\n\n  class WindowsStat < MetaStat\n    if Puppet::Util::Platform.windows?\n      require_relative '../../puppet/util/windows/security'\n    end\n\n    def initialize(stat, path, source_permissions)\n      super(stat, source_permissions)\n      @path = path\n      raise(ArgumentError, _(\"Unsupported Windows source permissions option %{source_permissions}\") % { source_permissions: source_permissions }) unless @source_permissions_ignore\n    end\n\n    { :owner => 'S-1-5-32-544',\n      :group => 'S-1-0-0',\n      :mode => 0o644 }.each do |method, default_value|\n      define_method method do\n        default_value\n      end\n    end\n  end\n\n  def collect_stat(path)\n    stat = stat()\n\n    if Puppet::Util::Platform.windows?\n      WindowsStat.new(stat, path, @source_permissions)\n    else\n      MetaStat.new(stat, @source_permissions)\n    end\n  end\n\n  # Retrieve the attributes for this file, relative to a base directory.\n  # Note that Puppet::FileSystem.stat(path) raises Errno::ENOENT\n  # if the file is absent and this method does not catch that exception.\n  def collect(source_permissions = nil)\n    real_path = full_path\n\n    stat = collect_stat(real_path)\n    @owner = stat.owner\n    @group = stat.group\n    @ftype = stat.ftype\n\n    # We have to mask the mode, yay.\n    @mode = stat.mode & 0o07777\n\n    case stat.ftype\n    when \"file\"\n      @checksum = \"{#{@checksum_type}}\" + send(\"#{@checksum_type}_file\", real_path).to_s\n    when \"directory\" # Always just timestamp the directory.\n      @checksum_type = \"ctime\"\n      @checksum = \"{#{@checksum_type}}\" + send(\"#{@checksum_type}_file\", path).to_s\n    when \"link\"\n      @destination = Puppet::FileSystem.readlink(real_path)\n      @checksum = begin\n        \"{#{@checksum_type}}\" + send(\"#{@checksum_type}_file\", real_path).to_s\n      rescue\n        nil\n      end\n    when \"fifo\", \"socket\"\n      @checksum_type = \"none\"\n      @checksum = \"{#{@checksum_type}}\" + send(\"#{@checksum_type}_file\", real_path).to_s\n    else\n      raise ArgumentError, _(\"Cannot manage files of type %{file_type}\") % { file_type: stat.ftype }\n    end\n  end\n\n  def initialize(path, data = {})\n    @owner       = data.delete('owner')\n    @group       = data.delete('group')\n    @mode        = data.delete('mode')\n    checksum = data.delete('checksum')\n    if checksum\n      @checksum_type = checksum['type']\n      @checksum      = checksum['value']\n    end\n    @checksum_type ||= Puppet[:digest_algorithm]\n    @ftype       = data.delete('type')\n    @destination = data.delete('destination')\n    @source      = data.delete('source')\n    @content_uri = data.delete('content_uri')\n\n    links = data.fetch('links', nil) || data.fetch(:links, nil)\n    relative_path = data.fetch('relative_path', nil) || data.fetch(:relative_path, nil)\n    source = @source || data.fetch(:source, nil)\n    super(path, links: links, relative_path: relative_path, source: source)\n  end\n\n  def to_data_hash\n    super.update(\n      {\n        'owner' => owner,\n        'group' => group,\n        'mode' => mode,\n        'checksum' => {\n          'type' => checksum_type,\n          'value' => checksum\n        },\n        'type' => ftype,\n        'destination' => destination,\n      }.merge(content_uri ? { 'content_uri' => content_uri } : {})\n       .merge(source ? { 'source' => source } : {})\n    )\n  end\n\n  def self.from_data_hash(data)\n    new(data.delete('path'), data)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/mount'\n\nclass Puppet::FileServing::Mount::File < Puppet::FileServing::Mount\n  def self.localmap\n    @localmap ||= {\n      \"h\" => Puppet.runtime[:facter].value('networking.hostname'),\n      \"H\" => [\n        Puppet.runtime[:facter].value('networking.hostname'),\n        Puppet.runtime[:facter].value('networking.domain')\n      ].join(\".\"),\n      \"d\" => Puppet.runtime[:facter].value('networking.domain')\n    }\n  end\n\n  def complete_path(relative_path, node)\n    full_path = path(node)\n\n    raise ArgumentError, _(\"Mounts without paths are not usable\") unless full_path\n\n    # If there's no relative path name, then we're serving the mount itself.\n    return full_path unless relative_path\n\n    file = ::File.join(full_path, relative_path)\n\n    unless Puppet::FileSystem.exist?(file) or Puppet::FileSystem.symlink?(file)\n      Puppet.info(_(\"File does not exist or is not accessible: %{file}\") % { file: file })\n      return nil\n    end\n\n    file\n  end\n\n  # Return an instance of the appropriate class.\n  def find(short_file, request)\n    complete_path(short_file, request.node)\n  end\n\n  # Return the path as appropriate, expanding as necessary.\n  def path(node = nil)\n    if expandable?\n      expand(@path, node)\n    else\n      @path\n    end\n  end\n\n  # Set the path.\n  def path=(path)\n    # FIXME: For now, just don't validate paths with replacement\n    # patterns in them.\n    if path =~ /%./\n      # Mark that we're expandable.\n      @expandable = true\n    else\n      raise ArgumentError, _(\"%{path} does not exist or is not a directory\") % { path: path } unless FileTest.directory?(path)\n      raise ArgumentError, _(\"%{path} is not readable\") % { path: path } unless FileTest.readable?(path)\n\n      @expandable = false\n    end\n    @path = path\n  end\n\n  def search(path, request)\n    path = complete_path(path, request.node)\n    return nil unless path\n\n    [path]\n  end\n\n  # Verify our configuration is valid.  This should really check to\n  # make sure at least someone will be allowed, but, eh.\n  def validate\n    raise ArgumentError, _(\"Mounts without paths are not usable\") if @path.nil?\n  end\n\n  private\n\n  # Create a map for a specific node.\n  def clientmap(node)\n    {\n      \"h\" => node.sub(/\\..*$/, \"\"),\n      \"H\" => node,\n      \"d\" => node.sub(/[^.]+\\./, \"\") # domain name\n    }\n  end\n\n  # Replace % patterns as appropriate.\n  def expand(path, node = nil)\n    # This map should probably be moved into a method.\n    map = nil\n\n    if node\n      map = clientmap(node)\n    else\n      Puppet.notice _(\"No client; expanding '%{path}' with local host\") % { path: path }\n      # Else, use the local information\n      map = localmap\n    end\n\n    path.gsub(/%(.)/) do |v|\n      key = ::Regexp.last_match(1)\n      if key == \"%\"\n        \"%\"\n      else\n        map[key] || v\n      end\n    end\n  end\n\n  # Do we have any patterns in our path, yo?\n  def expandable?\n    if defined?(@expandable)\n      @expandable\n    else\n      false\n    end\n  end\n\n  # Cache this manufactured map, since if it's used it's likely\n  # to get used a lot.\n  def localmap\n    self.class.localmap\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/locales.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/mount'\n\n# Find files in the modules' locales directories.\n# This is a very strange mount because it merges\n# many directories into one.\nclass Puppet::FileServing::Mount::Locales < Puppet::FileServing::Mount\n  # Return an instance of the appropriate class.\n  def find(relative_path, request)\n    mod = request.environment.modules.find { |m| m.locale(relative_path) }\n    return nil unless mod\n\n    mod.locale(relative_path)\n  end\n\n  def search(relative_path, request)\n    # We currently only support one kind of search on locales - return\n    # them all.\n    paths = request.environment.modules.find_all(&:locales?).collect(&:locale_directory)\n    if paths.empty?\n      # If the modulepath is valid then we still need to return a valid root\n      # directory for the search, but make sure nothing inside it is\n      # returned.\n      request.options[:recurse] = false\n      request.environment.modulepath.empty? ? [Puppet[:codedir]] : request.environment.modulepath\n    else\n      paths\n    end\n  end\n\n  def valid?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/modules.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/mount'\n\n# This is the modules-specific mount: it knows how to search through\n# modules for files.  Yay.\nclass Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount\n  # Return an instance of the appropriate class.\n  def find(path, request)\n    raise _(\"No module specified\") if path.to_s.empty?\n\n    module_name, relative_path = path.split(\"/\", 2)\n    mod = request.environment.module(module_name)\n    return nil unless mod\n\n    mod.file(relative_path)\n  end\n\n  def search(path, request)\n    result = find(path, request)\n    if result\n      [result]\n    end\n  end\n\n  def valid?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/pluginfacts.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/mount'\n\n# Find files in the modules' pluginfacts directories.\n# This is a very strange mount because it merges\n# many directories into one.\nclass Puppet::FileServing::Mount::PluginFacts < Puppet::FileServing::Mount\n  # Return an instance of the appropriate class.\n  def find(relative_path, request)\n    mod = request.environment.modules.find { |m| m.pluginfact(relative_path) }\n    return nil unless mod\n\n    mod.pluginfact(relative_path)\n  end\n\n  def search(relative_path, request)\n    # We currently only support one kind of search on plugins - return\n    # them all.\n    paths = request.environment.modules.find_all(&:pluginfacts?).collect(&:plugin_fact_directory)\n    if paths.empty?\n      # If the modulepath is valid then we still need to return a valid root\n      # directory for the search, but make sure nothing inside it is\n      # returned.\n      request.options[:recurse] = false\n      request.environment.modulepath.empty? ? [Puppet[:codedir]] : request.environment.modulepath\n    else\n      paths\n    end\n  end\n\n  def valid?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/plugins.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/mount'\n\n# Find files in the modules' plugins directories.\n# This is a very strange mount because it merges\n# many directories into one.\nclass Puppet::FileServing::Mount::Plugins < Puppet::FileServing::Mount\n  # Return an instance of the appropriate class.\n  def find(relative_path, request)\n    mod = request.environment.modules.find { |m| m.plugin(relative_path) }\n    return nil unless mod\n\n    mod.plugin(relative_path)\n  end\n\n  def search(relative_path, request)\n    # We currently only support one kind of search on plugins - return\n    # them all.\n    paths = request.environment.modules.find_all(&:plugins?).collect(&:plugin_directory)\n    if paths.empty?\n      # If the modulepath is valid then we still need to return a valid root\n      # directory for the search, but make sure nothing inside it is\n      # returned.\n      request.options[:recurse] = false\n      request.environment.modulepath.empty? ? [Puppet[:codedir]] : request.environment.modulepath\n    else\n      paths\n    end\n  end\n\n  def valid?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/scripts.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'puppet/file_serving/mount'\n\nclass Puppet::FileServing::Mount::Scripts < Puppet::FileServing::Mount\n  # Return an instance of the appropriate class.\n  def find(path, request)\n    raise _(\"No module specified\") if path.to_s.empty?\n\n    module_name, relative_path = path.split(\"/\", 2)\n    mod = request.environment.module(module_name)\n    return nil unless mod\n\n    mod.script(relative_path)\n  end\n\n  def search(path, request)\n    result = find(path, request)\n    if result\n      [result]\n    end\n  end\n\n  def valid?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount/tasks.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/mount'\n\nclass Puppet::FileServing::Mount::Tasks < Puppet::FileServing::Mount\n  def find(path, request)\n    raise _(\"No task specified\") if path.to_s.empty?\n\n    module_name, task_path = path.split(\"/\", 2)\n    mod = request.environment.module(module_name)\n    return nil unless mod\n\n    mod.task_file(task_path)\n  end\n\n  def search(path, request)\n    result = find(path, request)\n    if result\n      [result]\n    end\n  end\n\n  def valid?\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/mount.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/logging'\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/file_serving/metadata'\nrequire_relative '../../puppet/file_serving/content'\n\n# Broker access to the filesystem, converting local URIs into metadata\n# or content objects.\nclass Puppet::FileServing::Mount\n  include Puppet::Util::Logging\n\n  attr_reader :name\n\n  def find(path, options)\n    raise NotImplementedError\n  end\n\n  # Create our object.  It must have a name.\n  def initialize(name)\n    unless name =~ /^[-\\w]+$/\n      raise ArgumentError, _(\"Invalid mount name format '%{name}'\") % { name: name }\n    end\n\n    @name = name\n\n    super()\n  end\n\n  def search(path, options)\n    raise NotImplementedError\n  end\n\n  def to_s\n    \"mount[#{@name}]\"\n  end\n\n  # A noop.\n  def validate\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/terminus_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving'\nrequire_relative '../../puppet/file_serving/fileset'\n\n# Define some common methods for FileServing termini.\n\nmodule Puppet::FileServing::TerminusHelper\n  # Create model instance for a file in a file server.\n  def path2instance(request, path, options = {})\n    result = model.new(path, :relative_path => options[:relative_path])\n    result.links = request.options[:links] if request.options[:links]\n\n    result.checksum_type = request.options[:checksum_type] if request.options[:checksum_type]\n    result.source_permissions = request.options[:source_permissions] if request.options[:source_permissions]\n\n    result.collect\n\n    result\n  end\n\n  # Create model instances for all files in a fileset.\n  def path2instances(request, *paths)\n    filesets = paths.collect do |path|\n      # Filesets support indirector requests as an options collection\n      Puppet::FileServing::Fileset.new(path, request)\n    end\n\n    Puppet::FileServing::Fileset.merge(*filesets).collect do |file, base_path|\n      path2instance(request, base_path, :relative_path => file)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving/terminus_selector.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving'\n\n# This module is used to pick the appropriate terminus\n# in file-serving indirections.  This is necessary because\n# the terminus varies based on the URI asked for.\nmodule Puppet::FileServing::TerminusSelector\n  def select(request)\n    # We rely on the request's parsing of the URI.\n\n    case request.protocol\n    when \"file\"\n      :file\n    when \"puppet\"\n      if request.server\n        :rest\n      else\n        Puppet[:default_file_terminus]\n      end\n    when \"http\", \"https\"\n      :http\n    when nil\n      if Puppet::Util.absolute_path?(request.key)\n        :file\n      else\n        :file_server\n      end\n    else\n      raise ArgumentError, _(\"URI protocol '%{protocol}' is not currently supported for file serving\") % { protocol: request.protocol }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_serving.rb",
    "content": "# frozen_string_literal: true\n\n# Just a stub class.\nclass Puppet::FileServing # :nodoc:\nend\n"
  },
  {
    "path": "lib/puppet/file_system/file_impl.rb",
    "content": "# frozen_string_literal: true\n\n# Abstract implementation of the Puppet::FileSystem\n#\nclass Puppet::FileSystem::FileImpl\n  def pathname(path)\n    path.is_a?(Pathname) ? path : Pathname.new(path)\n  end\n\n  def assert_path(path)\n    return path if path.is_a?(Pathname)\n\n    # Some paths are string, or in the case of WatchedFile, it pretends to be\n    # one by implementing to_str.\n    if path.respond_to?(:to_str)\n      Pathname.new(path)\n    else\n      raise ArgumentError, _(\"FileSystem implementation expected Pathname, got: '%{klass}'\") % { klass: path.class }\n    end\n  end\n\n  def path_string(path)\n    path.to_s\n  end\n\n  def expand_path(path, dir_string = nil)\n    # ensure `nil` values behave like underlying File.expand_path\n    ::File.expand_path(path.nil? ? nil : path_string(path), dir_string)\n  end\n\n  def open(path, mode, options, &block)\n    ::File.open(path, options, mode, &block)\n  end\n\n  def dir(path)\n    path.dirname\n  end\n\n  def basename(path)\n    path.basename.to_s\n  end\n\n  def size(path)\n    path.size\n  end\n\n  def exclusive_create(path, mode, &block)\n    opt = File::CREAT | File::EXCL | File::WRONLY\n    self.open(path, mode, opt, &block)\n  end\n\n  def exclusive_open(path, mode, options = 'r', timeout = 300, &block)\n    wait = 0.001 + (Kernel.rand / 1000)\n    written = false\n    until written\n      ::File.open(path, options, mode) do |rf|\n        if rf.flock(::File::LOCK_EX | ::File::LOCK_NB)\n          Puppet.debug { _(\"Locked '%{path}'\") % { path: path } }\n          yield rf\n          written = true\n          Puppet.debug { _(\"Unlocked '%{path}'\") % { path: path } }\n        else\n          Puppet.debug { \"Failed to lock '%s' retrying in %.2f milliseconds\" % [path, wait * 1000] }\n          sleep wait\n          timeout -= wait\n          wait *= 2\n          if timeout < 0\n            raise Timeout::Error, _(\"Timeout waiting for exclusive lock on %{path}\") % { path: path }\n          end\n        end\n      end\n    end\n  end\n\n  def each_line(path, &block)\n    ::File.open(path) do |f|\n      f.each_line do |line|\n        yield line\n      end\n    end\n  end\n\n  def read(path, opts = {})\n    path.read(**opts)\n  end\n\n  def read_preserve_line_endings(path)\n    default_encoding = Encoding.default_external.name\n    encoding = default_encoding.downcase.start_with?('utf-') ? \"bom|#{default_encoding}\" : default_encoding\n    read(path, encoding: encoding)\n  end\n\n  def binread(path)\n    raise NotImplementedError\n  end\n\n  def exist?(path)\n    ::File.exist?(path)\n  end\n\n  def directory?(path)\n    ::File.directory?(path)\n  end\n\n  def file?(path)\n    ::File.file?(path)\n  end\n\n  def executable?(path)\n    ::File.executable?(path)\n  end\n\n  def writable?(path)\n    path.writable?\n  end\n\n  def touch(path, mtime: nil)\n    ::FileUtils.touch(path, mtime: mtime)\n  end\n\n  def mkpath(path)\n    path.mkpath\n  end\n\n  def children(path)\n    path.children\n  end\n\n  def symlink(path, dest, options = {})\n    FileUtils.symlink(path, dest, **options)\n  end\n\n  def symlink?(path)\n    ::File.symlink?(path)\n  end\n\n  def readlink(path)\n    ::File.readlink(path)\n  end\n\n  def unlink(*paths)\n    ::File.unlink(*paths)\n  end\n\n  def stat(path)\n    ::File.stat(path)\n  end\n\n  def lstat(path)\n    ::File.lstat(path)\n  end\n\n  def compare_stream(path, stream)\n    ::File.open(path, 0, 'rb') { |this| FileUtils.compare_stream(this, stream) }\n  end\n\n  def chmod(mode, path)\n    FileUtils.chmod(mode, path)\n  end\n\n  def replace_file(path, mode = nil)\n    begin\n      stat = lstat(path)\n      gid = stat.gid\n      uid = stat.uid\n      mode ||= stat.mode & 0o7777\n    rescue Errno::ENOENT\n      mode ||= 0o640\n    end\n\n    tempfile = Puppet::FileSystem::Uniquefile.new(Puppet::FileSystem.basename_string(path), Puppet::FileSystem.dir_string(path))\n    begin\n      begin\n        yield tempfile\n        tempfile.flush\n        tempfile.fsync\n      ensure\n        tempfile.close\n      end\n\n      tempfile_path = tempfile.path\n      FileUtils.chown(uid, gid, tempfile_path) if uid && gid\n      chmod(mode, tempfile_path)\n      ::File.rename(tempfile_path, path_string(path))\n    ensure\n      tempfile.close!\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/jruby.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_system/posix'\n\nclass Puppet::FileSystem::JRuby < Puppet::FileSystem::Posix\n  def unlink(*paths)\n    File.unlink(*paths)\n  rescue Errno::ENOENT\n    # JRuby raises ENOENT if the path doesn't exist or the parent directory\n    # doesn't allow execute/traverse. If it's the former, `stat` will raise\n    # ENOENT, if it's the later, it'll raise EACCES\n    # See https://github.com/jruby/jruby/issues/5617\n    stat(*paths)\n  end\n\n  def replace_file(path, mode = nil, &block)\n    # MRI Ruby rename checks if destination is a directory and raises, while\n    # JRuby removes the directory and replaces the file.\n    if directory?(path)\n      raise Errno::EISDIR, _(\"Is a directory: %{directory}\") % { directory: path }\n    end\n\n    super\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/memory_file.rb",
    "content": "# frozen_string_literal: true\n\n# An in-memory file abstraction. Commonly used with Puppet::FileSystem::File#overlay\n# @api private\nclass Puppet::FileSystem::MemoryFile\n  attr_reader :path, :children\n\n  def self.a_missing_file(path)\n    new(path, :exist? => false, :executable? => false)\n  end\n\n  def self.a_missing_directory(path)\n    new(path,\n        :exist? => false,\n        :executable? => false,\n        :directory? => true)\n  end\n\n  def self.a_regular_file_containing(path, content)\n    new(path, :exist? => true, :executable? => false, :content => content)\n  end\n\n  def self.an_executable(path)\n    new(path, :exist? => true, :executable? => true)\n  end\n\n  def self.a_directory(path, children = [])\n    new(path,\n        :exist? => true,\n        :executable? => true,\n        :directory? => true,\n        :children => children)\n  end\n\n  def self.a_symlink(target_path, source_path)\n    new(target_path, :exist? => true, :symlink? => true, :source_path => source_path)\n  end\n\n  def initialize(path, properties)\n    @path = path\n    @properties = properties\n    @children = (properties[:children] || []).collect do |child|\n      child.duplicate_as(File.join(@path, child.path))\n    end\n  end\n\n  def directory?; @properties[:directory?]; end\n  def exist?; @properties[:exist?]; end\n  def executable?; @properties[:executable?]; end\n  def symlink?; @properties[:symlink?]; end\n  def source_path; @properties[:source_path]; end\n\n  def each_line(&block)\n    handle.each_line(&block)\n  end\n\n  def handle\n    raise Errno::ENOENT unless exist?\n\n    StringIO.new(@properties[:content] || '')\n  end\n\n  def duplicate_as(other_path)\n    self.class.new(other_path, @properties)\n  end\n\n  def absolute?\n    Pathname.new(path).absolute?\n  end\n\n  def to_path\n    path\n  end\n\n  def to_s\n    to_path\n  end\n\n  def inspect\n    \"<Puppet::FileSystem::MemoryFile:#{self}>\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/memory_impl.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::FileSystem::MemoryImpl\n  def initialize(*files)\n    @files = files + all_children_of(files)\n  end\n\n  def expand_path(path, dir_string = nil)\n    File.expand_path(path, dir_string)\n  end\n\n  def exist?(path)\n    path.exist?\n  end\n\n  def directory?(path)\n    path.directory?\n  end\n\n  def file?(path)\n    path.file?\n  end\n\n  def executable?(path)\n    path.executable?\n  end\n\n  def symlink?(path)\n    path.symlink?\n  end\n\n  def readlink(path)\n    path = path.path\n    link = find(path)\n    return Puppet::FileSystem::MemoryFile.a_missing_file(path) unless link\n\n    source = link.source_path\n    return Puppet::FileSystem::MemoryFile.a_missing_file(link) unless source\n\n    find(source) || Puppet::FileSystem::MemoryFile.a_missing_file(source)\n  end\n\n  def children(path)\n    path.children\n  end\n\n  def each_line(path, &block)\n    path.each_line(&block)\n  end\n\n  def pathname(path)\n    find(path) || Puppet::FileSystem::MemoryFile.a_missing_file(path)\n  end\n\n  def basename(path)\n    path.duplicate_as(File.basename(path_string(path)))\n  end\n\n  def path_string(object)\n    object.path\n  end\n\n  def read(path, opts = {})\n    handle = assert_path(path).handle\n    handle.read\n  end\n\n  def read_preserve_line_endings(path)\n    read(path)\n  end\n\n  def open(path, *args, &block)\n    handle = assert_path(path).handle\n    if block_given?\n      yield handle\n    else\n      handle\n    end\n  end\n\n  def assert_path(path)\n    if path.is_a?(Puppet::FileSystem::MemoryFile)\n      path\n    else\n      find(path) or raise ArgumentError, _(\"Unable to find registered object for %{path}\") % { path: path.inspect }\n    end\n  end\n\n  private\n\n  def find(path)\n    @files.find { |file| file.path == path }\n  end\n\n  def all_children_of(files)\n    children = files.collect(&:children).flatten\n    if children.empty?\n      []\n    else\n      children + all_children_of(children)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/path_pattern.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire_relative '../../puppet/error'\n\nmodule Puppet::FileSystem\n  class PathPattern\n    class InvalidPattern < Puppet::Error; end\n\n    DOTDOT = '..'\n    ABSOLUTE_UNIX = %r{^/}\n    ABSOLUTE_WINDOWS = /^[a-z]:/i\n    CURRENT_DRIVE_RELATIVE_WINDOWS = /^\\\\/\n\n    def self.relative(pattern)\n      RelativePathPattern.new(pattern)\n    end\n\n    def self.absolute(pattern)\n      AbsolutePathPattern.new(pattern)\n    end\n\n    class << self\n      protected :new\n    end\n\n    # @param prefix [AbsolutePathPattern] An absolute path pattern instance\n    # @return [AbsolutePathPattern] A new AbsolutePathPattern prepended with\n    #   the passed prefix's pattern.\n    def prefix_with(prefix)\n      new_pathname = prefix.pathname + pathname\n      self.class.absolute(new_pathname.to_s)\n    end\n\n    def glob\n      Dir.glob(@pathstr)\n    end\n\n    def to_s\n      @pathstr\n    end\n\n    protected\n\n    attr_reader :pathname\n\n    private\n\n    def validate\n      if @pathstr.split(Pathname::SEPARATOR_PAT).any? { |f| f == DOTDOT }\n        raise(InvalidPattern, _(\"PathPatterns cannot be created with directory traversals.\"))\n      elsif @pathstr.match?(CURRENT_DRIVE_RELATIVE_WINDOWS)\n        raise(InvalidPattern, _(\"A PathPattern cannot be a Windows current drive relative path.\"))\n      end\n    end\n\n    def initialize(pattern)\n      begin\n        @pathname = Pathname.new(pattern.strip)\n        @pathstr = @pathname.to_s\n      rescue ArgumentError => error\n        raise InvalidPattern.new(_(\"PathPatterns cannot be created with a zero byte.\"), error)\n      end\n      validate\n    end\n  end\n\n  class RelativePathPattern < PathPattern\n    def absolute?\n      false\n    end\n\n    def validate\n      super\n      if @pathstr.match?(ABSOLUTE_WINDOWS)\n        raise(InvalidPattern, _(\"A relative PathPattern cannot be prefixed with a drive.\"))\n      elsif @pathstr.match?(ABSOLUTE_UNIX)\n        raise(InvalidPattern, _(\"A relative PathPattern cannot be an absolute path.\"))\n      end\n    end\n  end\n\n  class AbsolutePathPattern < PathPattern\n    def absolute?\n      true\n    end\n\n    def validate\n      super\n      if !@pathstr.match?(ABSOLUTE_UNIX) && !@pathstr.match?(ABSOLUTE_WINDOWS)\n        raise(InvalidPattern, _(\"An absolute PathPattern cannot be a relative path.\"))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/posix.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::FileSystem::Posix < Puppet::FileSystem::FileImpl\n  def binread(path)\n    path.binread\n  end\n\n  # Provide an encoding agnostic version of compare_stream\n  #\n  # The FileUtils implementation in Ruby 2.0+ was modified in a manner where\n  # it cannot properly compare File and StringIO instances. To sidestep that\n  # issue this method reimplements the faster 2.0 version that will correctly\n  # compare binary File and StringIO streams.\n  def compare_stream(path, stream)\n    ::File.open(path, 'rb') do |this|\n      bsize = stream_blksize(this, stream)\n      sa = String.new.force_encoding('ASCII-8BIT')\n      sb = String.new.force_encoding('ASCII-8BIT')\n      loop do\n        this.read(bsize, sa)\n        stream.read(bsize, sb)\n        return true if sa.empty? && sb.empty?\n        break if sa != sb\n      end\n      false\n    end\n  end\n\n  private\n\n  def stream_blksize(*streams)\n    streams.each do |s|\n      next unless s.respond_to?(:stat)\n\n      size = blksize(s.stat)\n      return size if size\n    end\n    default_blksize()\n  end\n\n  def blksize(st)\n    s = st.blksize\n    return nil unless s\n    return nil if s == 0\n\n    s\n  end\n\n  def default_blksize\n    1024\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/uniquefile.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'English'\nrequire_relative '../../puppet/file_system'\nrequire 'delegate'\nrequire 'tmpdir'\n\n# A class that provides `Tempfile`-like capabilities, but does not attempt to\n# manage the deletion of the file for you.  API is identical to the\n# normal `Tempfile` class.\n#\n# @api public\nclass Puppet::FileSystem::Uniquefile < DelegateClass(File)\n  # Convenience method which ensures that the file is closed and\n  # unlinked before returning\n  #\n  # @param identifier [String] additional part of generated pathname\n  # @yieldparam file [File] the temporary file object\n  # @return result of the passed block\n  # @api private\n  def self.open_tmp(identifier)\n    f = new(identifier)\n    yield f\n  ensure\n    if f\n      f.close!\n    end\n  end\n\n  def initialize(basename, *rest)\n    create_tmpname(basename, *rest) do |tmpname, _n, opts|\n      mode = File::RDWR | File::CREAT | File::EXCL\n      perm = 0o600\n      if opts\n        mode |= opts.delete(:mode) || 0\n        opts[:perm] = perm\n        perm = nil\n      else\n        opts = perm\n      end\n      self.class.locking(tmpname) do\n        @tmpfile = File.open(tmpname, mode, opts)\n        @tmpname = tmpname\n      end\n      @mode = mode & ~(File::CREAT | File::EXCL)\n      perm or opts.freeze\n      @opts = opts\n    end\n\n    super(@tmpfile)\n  end\n\n  # Opens or reopens the file with mode \"r+\".\n  def open\n    @tmpfile.close if @tmpfile\n    @tmpfile = File.open(@tmpname, @mode, @opts)\n    __setobj__(@tmpfile)\n  end\n\n  def _close\n    @tmpfile.close if @tmpfile\n  ensure\n    @tmpfile = nil\n  end\n  protected :_close\n\n  def close(unlink_now = false)\n    if unlink_now\n      close!\n    else\n      _close\n    end\n  end\n\n  def close!\n    _close\n    unlink\n  end\n\n  def unlink\n    return unless @tmpname\n\n    begin\n      File.unlink(@tmpname)\n    rescue Errno::ENOENT\n    rescue Errno::EACCES\n      # may not be able to unlink on Windows; just ignore\n      return\n    end\n    @tmpname = nil\n  end\n  alias delete unlink\n\n  # Returns the full path name of the temporary file.\n  # This will be nil if #unlink has been called.\n  def path\n    @tmpname\n  end\n\n  private\n\n  def make_tmpname(prefix_suffix, n)\n    case prefix_suffix\n    when String\n      prefix = prefix_suffix\n      suffix = \"\"\n    when Array\n      prefix = prefix_suffix[0]\n      suffix = prefix_suffix[1]\n    else\n      raise ArgumentError, _(\"unexpected prefix_suffix: %{value}\") % { value: prefix_suffix.inspect }\n    end\n    t = Time.now.strftime(\"%Y%m%d\")\n    path = \"#{prefix}#{t}-#{$PROCESS_ID}-#{rand(0x100000000).to_s(36)}\"\n    path << \"-#{n}\" if n\n    path << suffix\n  end\n\n  def create_tmpname(basename, *rest)\n    opts = try_convert_to_hash(rest[-1])\n    if opts\n      opts = opts.dup if rest.pop.equal?(opts)\n      max_try = opts.delete(:max_try)\n      opts = [opts]\n    else\n      opts = []\n    end\n    tmpdir, = *rest\n    tmpdir ||= tmpdir()\n    n = nil\n    begin\n      path = File.join(tmpdir, make_tmpname(basename, n))\n      yield(path, n, *opts)\n    rescue Errno::EEXIST\n      n ||= 0\n      n += 1\n      retry if !max_try or n < max_try\n      raise _(\"cannot generate temporary name using `%{basename}' under `%{tmpdir}'\") % { basename: basename, tmpdir: tmpdir }\n    end\n    path\n  end\n\n  def try_convert_to_hash(h)\n    h.to_hash\n  rescue NoMethodError\n    nil\n  end\n\n  @@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'\n\n  def tmpdir\n    tmp = '.'\n    [ENV.fetch('TMPDIR', nil), ENV.fetch('TMP', nil), ENV.fetch('TEMP', nil), @@systmpdir, '/tmp'].each do |dir|\n      stat = File.stat(dir) if dir\n      begin\n        if stat && stat.directory? && stat.writable?\n          tmp = dir\n          break\n        end\n      rescue\n        nil\n      end\n    end\n    File.expand_path(tmp)\n  end\n\n  class << self\n    # yields with locking for +tmpname+ and returns the result of the\n    # block.\n    def locking(tmpname)\n      lock = tmpname + '.lock'\n      mkdir(lock)\n      yield\n    rescue Errno::ENOENT => e\n      ex = Errno::ENOENT.new(\"A directory component in #{lock} does not exist or is a dangling symbolic link\")\n      ex.set_backtrace(e.backtrace)\n      raise ex\n    ensure\n      rmdir(lock) if Puppet::FileSystem.exist?(lock)\n    end\n\n    def mkdir(*args)\n      Dir.mkdir(*args)\n    end\n\n    def rmdir(*args)\n      Dir.rmdir(*args)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system/windows.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_system/posix'\nrequire_relative '../../puppet/util/windows'\n\nclass Puppet::FileSystem::Windows < Puppet::FileSystem::Posix\n  FULL_CONTROL = Puppet::Util::Windows::File::FILE_ALL_ACCESS\n  FILE_READ = Puppet::Util::Windows::File::FILE_GENERIC_READ\n\n  def open(path, mode, options, &block)\n    # PUP-6959 mode is explicitly ignored until it can be implemented\n    # Ruby on Windows uses mode for setting file attributes like read-only and\n    # archived, not for setting permissions like POSIX\n    raise TypeError, 'mode must be specified as an Integer' if mode && !mode.is_a?(Numeric)\n\n    ::File.open(path, options, nil, &block)\n  end\n\n  def expand_path(path, dir_string = nil)\n    # ensure `nil` values behave like underlying File.expand_path\n    string_path = ::File.expand_path(path.nil? ? nil : path_string(path), dir_string)\n    # if no tildes, nothing to expand, no need to call Windows API, return original string\n    return string_path unless string_path.index('~')\n\n    begin\n      # no need to do existence check up front as GetLongPathName implies that check is performed\n      # and it should be the exception that files aren't actually present\n      string_path = Puppet::Util::Windows::File.get_long_pathname(string_path)\n    rescue Puppet::Util::Windows::Error => e\n      # preserve original File.expand_path behavior for file / path not found by returning string\n      raise if e.code != Puppet::Util::Windows::File::ERROR_FILE_NOT_FOUND &&\n               e.code != Puppet::Util::Windows::File::ERROR_PATH_NOT_FOUND\n    end\n\n    string_path\n  end\n\n  def exist?(path)\n    Puppet::Util::Windows::File.exist?(path)\n  end\n\n  def symlink(path, dest, options = {})\n    raise_if_symlinks_unsupported\n\n    dest_exists = exist?(dest) # returns false on dangling symlink\n    dest_stat = Puppet::Util::Windows::File.stat(dest) if dest_exists\n\n    # silent fail to preserve semantics of original FileUtils\n    return 0 if dest_exists && dest_stat.ftype == 'directory'\n\n    if dest_exists && dest_stat.ftype == 'file' && options[:force] != true\n      raise(Errno::EEXIST, _(\"%{dest} already exists and the :force option was not specified\") % { dest: dest })\n    end\n\n    if options[:noop] != true\n      ::File.delete(dest) if dest_exists # can only be file\n      Puppet::Util::Windows::File.symlink(path, dest)\n    end\n\n    0\n  end\n\n  def symlink?(path)\n    return false unless Puppet.features.manages_symlinks?\n\n    Puppet::Util::Windows::File.symlink?(path)\n  end\n\n  def readlink(path)\n    raise_if_symlinks_unsupported\n    Puppet::Util::Windows::File.readlink(path)\n  end\n\n  def unlink(*file_names)\n    unless Puppet.features.manages_symlinks?\n      return ::File.unlink(*file_names)\n    end\n\n    file_names.each do |file_name|\n      file_name = file_name.to_s # handle PathName\n      stat = begin\n        Puppet::Util::Windows::File.stat(file_name)\n      rescue\n        nil\n      end\n\n      # sigh, Ruby + Windows :(\n      if !stat\n        begin\n          ::File.unlink(file_name)\n        rescue\n          Dir.rmdir(file_name)\n        end\n      elsif stat.ftype == 'directory'\n        if Puppet::Util::Windows::File.symlink?(file_name)\n          Dir.rmdir(file_name)\n        else\n          raise Errno::EPERM, file_name\n        end\n      else\n        ::File.unlink(file_name)\n      end\n    end\n\n    file_names.length\n  end\n\n  def stat(path)\n    Puppet::Util::Windows::File.stat(path)\n  end\n\n  def lstat(path)\n    unless Puppet.features.manages_symlinks?\n      return Puppet::Util::Windows::File.stat(path)\n    end\n\n    Puppet::Util::Windows::File.lstat(path)\n  end\n\n  def chmod(mode, path)\n    Puppet::Util::Windows::Security.set_mode(mode, path.to_s)\n  end\n\n  def read_preserve_line_endings(path)\n    contents = path.read(:mode => 'rb', :encoding => 'bom|utf-8')\n    contents = path.read(:mode => 'rb', :encoding => \"bom|#{Encoding.default_external.name}\") unless contents.valid_encoding?\n    contents = path.read unless contents.valid_encoding?\n\n    contents\n  end\n\n  # https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--0-499-\n  FILE_NOT_FOUND = 2\n  ACCESS_DENIED = 5\n  SHARING_VIOLATION = 32\n  LOCK_VIOLATION = 33\n\n  def replace_file(path, mode = nil)\n    if directory?(path)\n      raise Errno::EISDIR, _(\"Is a directory: %{directory}\") % { directory: path }\n    end\n\n    current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n    current_sid ||= Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_sam_compatible_user_name)\n\n    dacl = case mode\n           when 0o644\n             dacl = secure_dacl(current_sid)\n             dacl.allow(Puppet::Util::Windows::SID::BuiltinUsers, FILE_READ)\n             dacl\n           when 0o660, 0o640, 0o600, 0o440\n             secure_dacl(current_sid)\n           when nil\n             get_dacl_from_file(path) || secure_dacl(current_sid)\n           else\n             raise ArgumentError, \"#{mode} is invalid: Only modes 0644, 0640, 0660, and 0440 are allowed\"\n           end\n\n    tempfile = Puppet::FileSystem::Uniquefile.new(Puppet::FileSystem.basename_string(path), Puppet::FileSystem.dir_string(path))\n    begin\n      tempdacl = Puppet::Util::Windows::AccessControlList.new\n      tempdacl.allow(current_sid, FULL_CONTROL)\n      set_dacl(tempfile.path, tempdacl)\n\n      begin\n        yield tempfile\n        tempfile.flush\n        tempfile.fsync\n      ensure\n        tempfile.close\n      end\n\n      set_dacl(tempfile.path, dacl) if dacl\n      ::File.rename(tempfile.path, path_string(path))\n    ensure\n      tempfile.close!\n    end\n  rescue Puppet::Util::Windows::Error => e\n    case e.code\n    when ACCESS_DENIED, SHARING_VIOLATION, LOCK_VIOLATION\n      raise Errno::EACCES.new(path_string(path), e)\n    else\n      raise SystemCallError, e.message\n    end\n  end\n\n  private\n\n  def set_dacl(path, dacl)\n    sd = Puppet::Util::Windows::Security.get_security_descriptor(path)\n    new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, dacl, true)\n    Puppet::Util::Windows::Security.set_security_descriptor(path, new_sd)\n  end\n\n  def secure_dacl(current_sid)\n    dacl = Puppet::Util::Windows::AccessControlList.new\n    [\n      Puppet::Util::Windows::SID::LocalSystem,\n      Puppet::Util::Windows::SID::BuiltinAdministrators,\n      current_sid\n    ].uniq.map do |sid|\n      dacl.allow(sid, FULL_CONTROL)\n    end\n    dacl\n  end\n\n  def get_dacl_from_file(path)\n    sd = Puppet::Util::Windows::Security.get_security_descriptor(path_string(path))\n    sd.dacl\n  rescue Puppet::Util::Windows::Error => e\n    raise e unless e.code == FILE_NOT_FOUND\n  end\n\n  def raise_if_symlinks_unsupported\n    unless Puppet.features.manages_symlinks?\n      msg = _(\"This version of Windows does not support symlinks.  Windows Vista / 2008 or higher is required.\")\n      raise Puppet::Util::Windows::Error, msg\n    end\n\n    unless Puppet::Util::Windows::Process.process_privilege_symlink?\n      Puppet.warning _(\"The current user does not have the necessary permission to manage symlinks.\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/file_system.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::FileSystem\n  require_relative '../puppet/util'\n  require_relative 'file_system/path_pattern'\n  require_relative 'file_system/file_impl'\n  require_relative 'file_system/memory_file'\n  require_relative 'file_system/memory_impl'\n  require_relative 'file_system/uniquefile'\n\n  # create instance of the file system implementation to use for the current platform\n  @impl = if Puppet::Util::Platform.windows?\n            require_relative 'file_system/windows'\n            Puppet::FileSystem::Windows\n          elsif Puppet::Util::Platform.jruby?\n            require_relative 'file_system/jruby'\n            Puppet::FileSystem::JRuby\n          else\n            require_relative 'file_system/posix'\n            Puppet::FileSystem::Posix\n          end.new()\n\n  # Allows overriding the filesystem for the duration of the given block.\n  # The filesystem will only contain the given file(s).\n  #\n  # @param files [Puppet::FileSystem::MemoryFile] the files to have available\n  #\n  # @api private\n  #\n  def self.overlay(*files, &block)\n    old_impl = @impl\n    @impl = Puppet::FileSystem::MemoryImpl.new(*files)\n    yield\n  ensure\n    @impl = old_impl\n  end\n\n  # Opens the given path with given mode, and options and optionally yields it to the given block.\n  #\n  # @param path [String, Pathname] the path to the file to operate on\n  # @param mode [Integer] The mode to apply to the file if it is created\n  # @param options [String] Extra file operation mode information to use\n  #   This is the standard mechanism Ruby uses in the IO class, and therefore\n  #   encoding may be specified explicitly as fmode : encoding or fmode : \"BOM|UTF-*\"\n  #   for example, a:ASCII or w+:UTF-8\n  # @yield The file handle, in the mode given by options, else read-write mode\n  # @return [Void]\n  #\n  # @api public\n  #\n  def self.open(path, mode, options, &block)\n    @impl.open(assert_path(path), mode, options, &block)\n  end\n\n  # @return [Object] The directory of this file as an opaque handle\n  #\n  # @api public\n  #\n  def self.dir(path)\n    @impl.dir(assert_path(path))\n  end\n\n  # @return [String] The directory of this file as a String\n  #\n  # @api public\n  #\n  def self.dir_string(path)\n    @impl.path_string(@impl.dir(assert_path(path)))\n  end\n\n  # @return [Boolean] Does the directory of the given path exist?\n  def self.dir_exist?(path)\n    @impl.exist?(@impl.dir(assert_path(path)))\n  end\n\n  # Creates all directories down to (inclusive) the dir of the given path\n  def self.dir_mkpath(path)\n    @impl.mkpath(@impl.dir(assert_path(path)))\n  end\n\n  # @return [Object] the name of the file as a opaque handle\n  #\n  # @api public\n  #\n  def self.basename(path)\n    @impl.basename(assert_path(path))\n  end\n\n  # @return [String] the name of the file\n  #\n  # @api public\n  #\n  def self.basename_string(path)\n    @impl.path_string(@impl.basename(assert_path(path)))\n  end\n\n  # Allows exclusive updates to a file to be made by excluding concurrent\n  # access using flock. This means that if the file is on a filesystem that\n  # does not support flock, this method will provide no protection.\n  #\n  # While polling to acquire the lock the process will wait ever increasing\n  # amounts of time in order to prevent multiple processes from wasting\n  # resources.\n  #\n  # @param path [Pathname] the path to the file to operate on\n  # @param mode [Integer] The mode to apply to the file if it is created\n  # @param options [String] Extra file operation mode information to use\n  #   (defaults to read-only mode 'r')\n  #   This is the standard mechanism Ruby uses in the IO class, and therefore\n  #   encoding may be specified explicitly as fmode : encoding or fmode : \"BOM|UTF-*\"\n  #   for example, a:ASCII or w+:UTF-8\n  # @param timeout [Integer] Number of seconds to wait for the lock (defaults to 300)\n  # @yield The file handle, in the mode given by options, else read-write mode\n  # @return [Void]\n  # @raise [Timeout::Error] If the timeout is exceeded while waiting to acquire the lock\n  #\n  # @api public\n  #\n  def self.exclusive_open(path, mode, options = 'r', timeout = 300, &block)\n    @impl.exclusive_open(assert_path(path), mode, options, timeout, &block)\n  end\n\n  # Processes each line of the file by yielding it to the given block\n  #\n  # @api public\n  #\n  def self.each_line(path, &block)\n    @impl.each_line(assert_path(path), &block)\n  end\n\n  # @return [String] The contents of the file\n  #\n  # @api public\n  #\n  def self.read(path, opts = {})\n    @impl.read(assert_path(path), opts)\n  end\n\n  # Read a file keeping the original line endings intact. This\n  # attempts to open files using binary mode using some encoding\n  # overrides and falling back to IO.read when none of the\n  # encodings are valid.\n  #\n  # @return [String] The contents of the file\n  #\n  # @api public\n  #\n  def self.read_preserve_line_endings(path)\n    @impl.read_preserve_line_endings(assert_path(path))\n  end\n\n  # @return [String] The binary contents of the file\n  #\n  # @api public\n  #\n  def self.binread(path)\n    @impl.binread(assert_path(path))\n  end\n\n  # Determines if a file exists by verifying that the file can be stat'd.\n  # Will follow symlinks and verify that the actual target path exists.\n  #\n  # @return [Boolean] true if the named file exists.\n  #\n  # @api public\n  #\n  def self.exist?(path)\n    @impl.exist?(assert_path(path))\n  end\n\n  # Determines if a file is a directory.\n  #\n  # @return [Boolean] true if the given file is a directory.\n  #\n  # @api public\n  def self.directory?(path)\n    @impl.directory?(assert_path(path))\n  end\n\n  # Determines if a file is a file.\n  #\n  # @return [Boolean] true if the given file is a file.\n  #\n  # @api public\n  def self.file?(path)\n    @impl.file?(assert_path(path))\n  end\n\n  # Determines if a file is executable.\n  #\n  # @todo Should this take into account extensions on the windows platform?\n  #\n  # @return [Boolean] true if this file can be executed\n  #\n  # @api public\n  #\n  def self.executable?(path)\n    @impl.executable?(assert_path(path))\n  end\n\n  # @return [Boolean] Whether the file is writable by the current process\n  #\n  # @api public\n  #\n  def self.writable?(path)\n    @impl.writable?(assert_path(path))\n  end\n\n  # Touches the file. On most systems this updates the mtime of the file.\n  #\n  # @param mtime [Time] The last modified time or nil to use the current time\n  #\n  # @api public\n  #\n  def self.touch(path, mtime: nil)\n    @impl.touch(assert_path(path), mtime: mtime)\n  end\n\n  # Creates directories for all parts of the given path.\n  #\n  # @api public\n  #\n  def self.mkpath(path)\n    @impl.mkpath(assert_path(path))\n  end\n\n  # @return [Array<Object>] references to all of the children of the given\n  #   directory path, excluding `.` and `..`.\n  # @api public\n  def self.children(path)\n    @impl.children(assert_path(path))\n  end\n\n  # Creates a symbolic link dest which points to the current file.\n  # If dest already exists:\n  #\n  # * and is a file, will raise Errno::EEXIST\n  # * and is a directory, will return 0 but perform no action\n  # * and is a symlink referencing a file, will raise Errno::EEXIST\n  # * and is a symlink referencing a directory, will return 0 but perform no action\n  #\n  # With the :force option set to true, when dest already exists:\n  #\n  # * and is a file, will replace the existing file with a symlink (DANGEROUS)\n  # * and is a directory, will return 0 but perform no action\n  # * and is a symlink referencing a file, will modify the existing symlink\n  # * and is a symlink referencing a directory, will return 0 but perform no action\n  #\n  # @param dest [String] The path to create the new symlink at\n  # @param [Hash] options the options to create the symlink with\n  # @option options [Boolean] :force overwrite dest\n  # @option options [Boolean] :noop do not perform the operation\n  # @option options [Boolean] :verbose verbose output\n  #\n  # @raise [Errno::EEXIST] dest already exists as a file and, :force is not set\n  #\n  # @return [Integer] 0\n  #\n  # @api public\n  #\n  def self.symlink(path, dest, options = {})\n    @impl.symlink(assert_path(path), dest, options)\n  end\n\n  # @return [Boolean] true if the file is a symbolic link.\n  #\n  # @api public\n  #\n  def self.symlink?(path)\n    @impl.symlink?(assert_path(path))\n  end\n\n  # @return [String] the name of the file referenced by the given link.\n  #\n  # @api public\n  #\n  def self.readlink(path)\n    @impl.readlink(assert_path(path))\n  end\n\n  # Deletes the given paths, returning the number of names passed as arguments.\n  # See also Dir::rmdir.\n  #\n  # @raise an exception on any error.\n  #\n  # @return [Integer] the number of paths passed as arguments\n  #\n  # @api public\n  #\n  def self.unlink(*paths)\n    @impl.unlink(*(paths.map { |p| assert_path(p) }))\n  end\n\n  # @return [File::Stat] object for the named file.\n  #\n  # @api public\n  #\n  def self.stat(path)\n    @impl.stat(assert_path(path))\n  end\n\n  # @return [Integer] the size of the file\n  #\n  # @api public\n  #\n  def self.size(path)\n    @impl.size(assert_path(path))\n  end\n\n  # @return [File::Stat] Same as stat, but does not follow the last symbolic\n  # link. Instead, reports on the link itself.\n  #\n  # @api public\n  #\n  def self.lstat(path)\n    @impl.lstat(assert_path(path))\n  end\n\n  # Compares the contents of this file against the contents of a stream.\n  #\n  # @param stream [IO] The stream to compare the contents against\n  # @return [Boolean] Whether the contents were the same\n  #\n  # @api public\n  #\n  def self.compare_stream(path, stream)\n    @impl.compare_stream(assert_path(path), stream)\n  end\n\n  # Produces an opaque pathname \"handle\" object representing the given path.\n  # Different implementations of the underlying file system may use different runtime\n  # objects. The produced \"handle\" should be used in all other operations\n  # that take a \"path\". No operation should be directly invoked on the returned opaque object\n  #\n  # @param path [String] The string representation of the path\n  # @return [Object] An opaque path handle on which no operations should be directly performed\n  #\n  # @api public\n  #\n  def self.pathname(path)\n    @impl.pathname(path)\n  end\n\n  # Produces a string representation of the opaque path handle, with expansions\n  # performed on ~.  For Windows, this means that C:\\Users\\Admini~1\\AppData will\n  # be expanded to C:\\Users\\Administrator\\AppData.  On POSIX filesystems, the\n  # value ~ will be expanded to something like /Users/Foo\n  #\n  # This method exists primarlily to resolve a Ruby deficiency where\n  # File.expand_path doesn't convert short paths to long paths, which is\n  # important when resolving the path to load.\n  #\n  # @param path [Object] a path handle produced by {#pathname}\n  # @return [String] a string representation of the path\n  #\n  def self.expand_path(path, dir_string = nil)\n    @impl.expand_path(path, dir_string)\n  end\n\n  # Asserts that the given path is of the expected type produced by #pathname\n  #\n  # @raise [ArgumentError] when path is not of the expected type\n  #\n  # @api public\n  #\n  def self.assert_path(path)\n    @impl.assert_path(path)\n  end\n\n  # Produces a string representation of the opaque path handle.\n  #\n  # @param path [Object] a path handle produced by {#pathname}\n  # @return [String] a string representation of the path\n  #\n  def self.path_string(path)\n    @impl.path_string(path)\n  end\n\n  # Create and open a file for write only if it doesn't exist.\n  #\n  # @see Puppet::FileSystem::open\n  #\n  # @raise [Errno::EEXIST] path already exists.\n  #\n  # @api public\n  #\n  def self.exclusive_create(path, mode, &block)\n    @impl.exclusive_create(assert_path(path), mode, &block)\n  end\n\n  # Changes permission bits on the named path to the bit pattern represented\n  # by mode.\n  #\n  # @param mode [Integer] The mode to apply to the file if it is created\n  # @param path [String] The path to the file, can also accept [PathName]\n  #\n  # @raise [Errno::ENOENT]: path doesn't exist\n  #\n  # @api public\n  #\n  def self.chmod(mode, path)\n    @impl.chmod(mode, assert_path(path))\n  end\n\n  # Replace the contents of a file atomically, creating the file if necessary.\n  # If a `mode` is specified, then it will always be applied to the file. If\n  # a `mode` is not specified and the file exists, its mode will be preserved.\n  # If the file doesn't exist, the mode will be set to a platform-specific\n  # default.\n  #\n  # @param path [String] The path to the file, can also accept [PathName]\n  # @param mode [Integer] Optional mode for the file.\n  #\n  # @raise [Errno::EISDIR]: path is a directory\n  #\n  # @api public\n  #\n  def self.replace_file(path, mode = nil, &block)\n    @impl.replace_file(assert_path(path), mode, &block)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/forge/cache.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'uri'\n\nrequire_relative '../../puppet/forge'\n\nclass Puppet::Forge\n  # = Cache\n  #\n  # Provides methods for reading files from local cache, filesystem or network.\n  class Cache\n    # Instantiate new cache for the +repository+ instance.\n    def initialize(repository, options = {})\n      @repository = repository\n      @options = options\n    end\n\n    # Return filename retrieved from +uri+ instance. Will download this file and\n    # cache it if needed.\n    #\n    # TODO: Add checksum support.\n    # TODO: Add error checking.\n    def retrieve(url)\n      (path + File.basename(url.to_s)).tap do |cached_file|\n        uri = url.is_a?(::URI) ? url : ::URI.parse(url)\n        unless cached_file.file?\n          if uri.scheme == 'file'\n            # CGI.unescape butchers Uris that are escaped properly\n            FileUtils.cp(Puppet::Util.uri_unescape(uri.path), cached_file)\n          else\n            # TODO: Handle HTTPS; probably should use repository.contact\n            data = read_retrieve(uri)\n            cached_file.open('wb') { |f| f.write data }\n          end\n        end\n      end\n    end\n\n    # Return contents of file at the given URI's +uri+.\n    def read_retrieve(uri)\n      uri.read\n    end\n\n    # Return Pathname for repository's cache directory, create it if needed.\n    def path\n      (self.class.base_path + @repository.cache_key).tap(&:mkpath)\n    end\n\n    # Return the base Pathname for all the caches.\n    def self.base_path\n      (Pathname(Puppet.settings[:module_working_dir]) + 'cache').cleanpath.tap do |o|\n        o.mkpath unless o.exist?\n      end\n    end\n\n    # Clean out all the caches.\n    def self.clean\n      base_path.rmtree if base_path.exist?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/forge/errors.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/json'\nrequire_relative '../../puppet/error'\nrequire_relative '../../puppet/forge'\n\n# Puppet::Forge specific exceptions\nmodule Puppet::Forge::Errors\n  # This exception is the parent for all Forge API errors\n  class ForgeError < Puppet::Error\n    # This is normally set by the child class, but if it is not this will\n    # fall back to displaying the message as a multiline.\n    #\n    # @return [String] the multiline version of the error message\n    def multiline\n      message\n    end\n  end\n\n  # This exception is raised when there is an SSL verification error when\n  # communicating with the forge.\n  class SSLVerifyError < ForgeError\n    # @option options [String] :uri The URI that failed\n    # @option options [String] :original the original exception\n    def initialize(options)\n      @uri     = options[:uri]\n      original = options[:original]\n\n      super(_(\"Unable to verify the SSL certificate at %{uri}\") % { uri: @uri }, original)\n    end\n\n    # Return a multiline version of the error message\n    #\n    # @return [String] the multiline version of the error message\n    def multiline\n      message = []\n      message << _('Could not connect via HTTPS to %{uri}') % { uri: @uri }\n      message << _('  Unable to verify the SSL certificate')\n      message << _('    The certificate may not be signed by a valid CA')\n      message << _('    The CA bundle included with OpenSSL may not be valid or up to date')\n      message.join(\"\\n\")\n    end\n  end\n\n  # This exception is raised when there is a communication error when connecting\n  # to the forge\n  class CommunicationError < ForgeError\n    # @option options [String] :uri The URI that failed\n    # @option options [String] :original the original exception\n    def initialize(options)\n      @uri     = options[:uri]\n      original = options[:original]\n      @detail  = original.message\n\n      message = _(\"Unable to connect to the server at %{uri}. Detail: %{detail}.\") % { uri: @uri, detail: @detail }\n      super(message, original)\n    end\n\n    # Return a multiline version of the error message\n    #\n    # @return [String] the multiline version of the error message\n    def multiline\n      message = []\n      message << _('Could not connect to %{uri}') % { uri: @uri }\n      message << _('  There was a network communications problem')\n      message << _(\"    The error we caught said '%{detail}'\") % { detail: @detail }\n      message << _('    Check your network connection and try again')\n      message.join(\"\\n\")\n    end\n  end\n\n  # This exception is raised when there is a bad HTTP response from the forge\n  # and optionally a message in the response.\n  class ResponseError < ForgeError\n    # @option options [String] :uri The URI that failed\n    # @option options [String] :input The user's input (e.g. module name)\n    # @option options [String] :message Error from the API response (optional)\n    # @option options [Puppet::HTTP::Response] :response The original HTTP response\n    def initialize(options)\n      @uri     = options[:uri]\n      @message = options[:message]\n      response = options[:response]\n      @response = \"#{response.code} #{response.reason.strip}\"\n\n      begin\n        body = Puppet::Util::Json.load(response.body)\n        if body['message']\n          @message ||= body['message'].strip\n        end\n      rescue Puppet::Util::Json::ParseError\n      end\n\n      message = if @message\n                  _(\"Request to Puppet Forge failed.\") + ' ' + _(\"Detail: %{detail}.\") % { detail: \"#{@message} / #{@response}\" }\n                else\n                  _(\"Request to Puppet Forge failed.\") + ' ' + _(\"Detail: %{detail}.\") % { detail: @response }\n                end\n      super(message, original)\n    end\n\n    # Return a multiline version of the error message\n    #\n    # @return [String] the multiline version of the error message\n    def multiline\n      message = []\n      message << _('Request to Puppet Forge failed.')\n      message << _('  The server being queried was %{uri}') % { uri: @uri }\n      message << _(\"  The HTTP response we received was '%{response}'\") % { response: @response }\n      message << _(\"  The message we received said '%{message}'\") % { message: @message } if @message\n      message.join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/forge/repository.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl/openssl_loader'\nrequire 'digest/sha1'\nrequire 'uri'\nrequire_relative '../../puppet/forge'\nrequire_relative '../../puppet/forge/errors'\nrequire_relative '../../puppet/network/http'\n\nclass Puppet::Forge\n  # = Repository\n  #\n  # This class is a file for accessing remote repositories with modules.\n  class Repository\n    include Puppet::Forge::Errors\n\n    attr_reader :uri, :cache\n\n    # Instantiate a new repository instance rooted at the +url+.\n    # The library will report +for_agent+ in the User-Agent to the repository.\n    def initialize(host, for_agent)\n      @host  = host\n      @agent = for_agent\n      @cache = Cache.new(self)\n      @uri   = URI.parse(host)\n\n      ssl_provider = Puppet::SSL::SSLProvider.new\n      @ssl_context = ssl_provider.create_system_context(cacerts: [])\n    end\n\n    # Return a Net::HTTPResponse read for this +path+.\n    def make_http_request(path, io = nil)\n      raise ArgumentError, \"Path must start with forward slash\" unless path.start_with?('/')\n\n      begin\n        str = @uri.to_s\n        str.chomp!('/')\n        str += Puppet::Util.uri_encode(path)\n        uri = URI(str)\n\n        headers = { \"User-Agent\" => user_agent }\n\n        if forge_authorization\n          uri.user = nil\n          uri.password = nil\n          headers[\"Authorization\"] = forge_authorization\n        end\n\n        http = Puppet.runtime[:http]\n        response = http.get(uri, headers: headers, options: { ssl_context: @ssl_context })\n        io.write(response.body) if io.respond_to?(:write)\n        response\n      rescue Puppet::SSL::CertVerifyError => e\n        raise SSLVerifyError.new(:uri => @uri.to_s, :original => e.cause)\n      rescue => e\n        raise CommunicationError.new(:uri => @uri.to_s, :original => e)\n      end\n    end\n\n    def forge_authorization\n      if Puppet[:forge_authorization]\n        Puppet[:forge_authorization]\n      elsif Puppet.features.pe_license?\n        PELicense.load_license_key.authorization_token\n      end\n    end\n\n    # Return the local file name containing the data downloaded from the\n    # repository at +release+ (e.g. \"myuser-mymodule\").\n    def retrieve(release)\n      path = @host.chomp('/') + release\n      cache.retrieve(path)\n    end\n\n    # Return the URI string for this repository.\n    def to_s\n      \"#<#{self.class} #{@host}>\"\n    end\n\n    # Return the cache key for this repository, this a hashed string based on\n    # the URI.\n    def cache_key\n      @cache_key ||= [\n        @host.to_s.gsub(/[^[:alnum:]]+/, '_').sub(/_$/, ''),\n        Digest::SHA1.hexdigest(@host.to_s)\n      ].join('-').freeze\n    end\n\n    private\n\n    def user_agent\n      @user_agent ||= [\n        @agent,\n        Puppet[:http_user_agent]\n      ].join(' ').freeze\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/forge.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/vendor'\nPuppet::Vendor.load_vendored\n\nrequire 'net/http'\nrequire 'tempfile'\nrequire 'uri'\nrequire 'pathname'\nrequire_relative '../puppet/util/json'\nrequire 'semantic_puppet'\n\nclass Puppet::Forge < SemanticPuppet::Dependency::Source\n  require_relative 'forge/cache'\n  require_relative 'forge/repository'\n  require_relative 'forge/errors'\n\n  include Puppet::Forge::Errors\n\n  USER_AGENT = \"PMT/1.1.1 (v3; Net::HTTP)\"\n\n  # From https://forgeapi.puppet.com/#!/release/getReleases\n  MODULE_RELEASE_EXCLUSIONS = %w[readme changelog license uri module tags supported file_size downloads created_at updated_at deleted_at].join(',').freeze\n\n  attr_reader :host, :repository\n\n  def initialize(host = Puppet[:module_repository])\n    @host = host\n    @repository = Puppet::Forge::Repository.new(host, USER_AGENT)\n  end\n\n  # Return a list of module metadata hashes that match the search query.\n  # This return value is used by the module_tool face install search,\n  # and displayed to on the console.\n  #\n  # Example return value:\n  #\n  # [\n  #   {\n  #     \"author\"      => \"puppetlabs\",\n  #     \"name\"        => \"bacula\",\n  #     \"tag_list\"    => [\"backup\", \"bacula\"],\n  #     \"releases\"    => [{\"version\"=>\"0.0.1\"}, {\"version\"=>\"0.0.2\"}],\n  #     \"full_name\"   => \"puppetlabs/bacula\",\n  #     \"version\"     => \"0.0.2\",\n  #     \"project_url\" => \"https://github.com/puppetlabs/puppetlabs-bacula\",\n  #     \"desc\"        => \"bacula\"\n  #   }\n  # ]\n  #\n  # @param term [String] search term\n  # @return [Array] modules found\n  # @raise [Puppet::Forge::Errors::CommunicationError] if there is a network\n  #   related error\n  # @raise [Puppet::Forge::Errors::SSLVerifyError] if there is a problem\n  #   verifying the remote SSL certificate\n  # @raise [Puppet::Forge::Errors::ResponseError] if the repository returns a\n  #   bad HTTP response\n  def search(term)\n    matches = []\n    uri = \"/v3/modules?query=#{term}\"\n    if Puppet[:module_groups]\n      uri += \"&module_groups=#{Puppet[:module_groups].tr('+', ' ')}\"\n    end\n\n    while uri\n      # make_http_request URI encodes parameters\n      response = make_http_request(uri)\n\n      if response.code == 200\n        result = Puppet::Util::Json.load(response.body)\n        uri = decode_uri(result['pagination']['next'])\n        matches.concat result['results']\n      else\n        raise ResponseError.new(:uri => response.url, :response => response)\n      end\n    end\n\n    matches.each do |mod|\n      mod['author'] = mod['owner']['username']\n      mod['tag_list'] = mod['current_release']['tags']\n      mod['full_name'] = \"#{mod['author']}/#{mod['name']}\"\n      mod['version'] = mod['current_release']['version']\n      mod['project_url'] = mod['homepage_url']\n      mod['desc'] = mod['current_release']['metadata']['summary'] || ''\n    end\n  end\n\n  # Fetches {ModuleRelease} entries for each release of the named module.\n  #\n  # @param input [String] the module name to look up\n  # @return [Array<SemanticPuppet::Dependency::ModuleRelease>] a list of releases for\n  #         the given name\n  # @see SemanticPuppet::Dependency::Source#fetch\n  def fetch(input)\n    name = input.tr('/', '-')\n    uri = \"/v3/releases?module=#{name}&sort_by=version&exclude_fields=#{MODULE_RELEASE_EXCLUSIONS}\"\n    if Puppet[:module_groups]\n      uri += \"&module_groups=#{Puppet[:module_groups].tr('+', ' ')}\"\n    end\n    releases = []\n\n    while uri\n      # make_http_request URI encodes parameters\n      response = make_http_request(uri)\n\n      if response.code == 200\n        response = Puppet::Util::Json.load(response.body)\n      else\n        raise ResponseError.new(:uri => response.url, :response => response)\n      end\n\n      releases.concat(process(response['results']))\n      uri = decode_uri(response['pagination']['next'])\n    end\n\n    releases\n  end\n\n  def make_http_request(*args)\n    @repository.make_http_request(*args)\n  end\n\n  class ModuleRelease < SemanticPuppet::Dependency::ModuleRelease\n    attr_reader :install_dir, :metadata\n\n    def initialize(source, data)\n      @data = data\n      @metadata = meta = data['metadata']\n\n      name = meta['name'].tr('/', '-')\n      version = SemanticPuppet::Version.parse(meta['version'])\n      release = \"#{name}@#{version}\"\n\n      if meta['dependencies']\n        dependencies = meta['dependencies'].collect do |dep|\n          Puppet::ModuleTool::Metadata.new.add_dependency(dep['name'], dep['version_requirement'], dep['repository'])\n          Puppet::ModuleTool.parse_module_dependency(release, dep)[0..1]\n        rescue ArgumentError => e\n          raise ArgumentError, _(\"Malformed dependency: %{name}.\") % { name: dep['name'] } +\n                               ' ' + _(\"Exception was: %{detail}\") % { detail: e }\n        end\n      else\n        dependencies = []\n      end\n\n      super(source, name, version, dependencies.to_h)\n    end\n\n    def install(dir)\n      staging_dir = prepare\n\n      module_dir = dir + name[/-(.*)/, 1]\n      module_dir.rmtree if module_dir.exist?\n\n      # Make sure unpacked module has the same ownership as the folder we are moving it into.\n      Puppet::ModuleTool::Applications::Unpacker.harmonize_ownership(dir, staging_dir)\n\n      FileUtils.mv(staging_dir, module_dir)\n      @install_dir = dir\n\n      # Return the Pathname object representing the directory where the\n      # module release archive was unpacked the to.\n      module_dir\n    ensure\n      staging_dir.rmtree if staging_dir.exist?\n    end\n\n    def prepare\n      return @unpacked_into if @unpacked_into\n\n      Puppet.warning \"#{@metadata['name']} has been deprecated by its author! View module on Puppet Forge for more info.\" if deprecated?\n\n      download(@data['file_uri'], tmpfile)\n      checksum = @data['file_sha256']\n      if checksum\n        validate_checksum(tmpfile, checksum, Digest::SHA256)\n      else\n        checksum = @data['file_md5']\n        if checksum\n          validate_checksum(tmpfile, checksum, Digest::MD5)\n        else\n          raise _(\"Forge module is missing SHA256 and MD5 checksums\")\n        end\n      end\n\n      unpack(tmpfile, tmpdir)\n\n      @unpacked_into = Pathname.new(tmpdir)\n    end\n\n    private\n\n    # Obtain a suitable temporary path for unpacking tarballs\n    #\n    # @return [Pathname] path to temporary unpacking location\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    def tmpdir\n      @dir ||= Dir.mktmpdir(name, Puppet::Forge::Cache.base_path)\n    end\n\n    def tmpfile\n      @file ||= Tempfile.new(name, Puppet::Forge::Cache.base_path).tap(&:binmode)\n    end\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n\n    def download(uri, destination)\n      response = @source.make_http_request(uri, destination)\n      destination.flush and destination.close\n      unless response.code == 200\n        raise Puppet::Forge::Errors::ResponseError.new(:uri => response.url, :response => response)\n      end\n    end\n\n    def validate_checksum(file, checksum, digest_class)\n      if Puppet.runtime[:facter].value(:fips_enabled) && digest_class == Digest::MD5\n        raise _(\"Module install using MD5 is prohibited in FIPS mode.\")\n      end\n\n      if digest_class.file(file.path).hexdigest != checksum\n        raise RuntimeError, _(\"Downloaded release for %{name} did not match expected checksum %{checksum}\") % { name: name, checksum: checksum }\n      end\n    end\n\n    def unpack(file, destination)\n      Puppet::ModuleTool::Applications::Unpacker.unpack(file.path, destination)\n    rescue Puppet::ExecutionFailure => e\n      raise RuntimeError, _(\"Could not extract contents of module archive: %{message}\") % { message: e.message }\n    end\n\n    def deprecated?\n      @data['module'] && !@data['module']['deprecated_at'].nil?\n    end\n  end\n\n  private\n\n  def process(list)\n    l = list.map do |release|\n      metadata = release['metadata']\n      begin\n        ModuleRelease.new(self, release)\n      rescue ArgumentError => e\n        Puppet.warning _(\"Cannot consider release %{name}-%{version}: %{error}\") % { name: metadata['name'], version: metadata['version'], error: e }\n        false\n      end\n    end\n\n    l.select { |r| r }\n  end\n\n  def decode_uri(uri)\n    return if uri.nil?\n\n    Puppet::Util.uri_unescape(uri.tr('+', ' '))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/abs.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the absolute value of a Numeric value, for example -34.56 becomes\n# 34.56. Takes a single `Integer` or `Float` value as an argument.\n#\n# *Deprecated behavior*\n#\n# For backwards compatibility reasons this function also works when given a\n# number in `String` format such that it first attempts to covert it to either a `Float` or\n# an `Integer` and then taking the absolute value of the result. Only strings representing\n# a number in decimal format is supported - an error is raised if\n# value is not decimal (using base 10). Leading 0 chars in the string\n# are ignored. A floating point value in string form can use some forms of\n# scientific notation but not all.\n#\n# Callers should convert strings to `Numeric` before calling\n# this function to have full control over the conversion.\n#\n# @example Converting to Numeric before calling\n# ```puppet\n# abs(Numeric($str_val))\n# ```\n#\n# It is worth noting that `Numeric` can convert to absolute value\n# directly as in the following examples:\n#\n# @example Absolute value and String to Numeric\n# ```puppet\n# Numeric($strval, true)     # Converts to absolute Integer or Float\n# Integer($strval, 10, true) # Converts to absolute Integer using base 10 (decimal)\n# Integer($strval, 16, true) # Converts to absolute Integer using base 16 (hex)\n# Float($strval, true)       # Converts to absolute Float\n# ```\n#\nPuppet::Functions.create_function(:abs) do\n  dispatch :on_numeric do\n    param 'Numeric', :val\n  end\n\n  dispatch :on_string do\n    param 'String', :val\n  end\n\n  def on_numeric(x)\n    x.abs\n  end\n\n  def on_string(x)\n    Puppet.warn_once('deprecations', 'abs_function_numeric_coerce_string',\n                     _(\"The abs() function's auto conversion of String to Numeric is deprecated - change to convert input before calling\"))\n\n    # These patterns for conversion are backwards compatible with the stdlib\n    # version of this function.\n    #\n    case x\n    when /^-?(?:\\d+)(?:\\.\\d+){1}$/\n      x.to_f.abs\n    when /^-?\\d+$/\n      x.to_i.abs\n    else\n      raise(ArgumentError, 'abs(): Requires float or integer to work with - was given non decimal string')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/alert.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `alert`.\nPuppet::Functions.create_function(:alert, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :alert do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def alert(scope, *values)\n    Puppet::Util::Log.log_func(scope, :alert, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/all.rb",
    "content": "# frozen_string_literal: true\n\n# Runs a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# repeatedly using each value in a data structure until the lambda returns a non \"truthy\" value which\n# makes the function return `false`, or if the end of the iteration is reached, `true` is returned.\n#\n# This function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It can\n# request one or two parameters.\n#\n# @example Using the `all` function\n#\n# `$data.all |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `all($data) |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# @example Using the `all` function with an Array and a one-parameter lambda\n#\n# ```puppet\n# # For the array $data, run a lambda that checks that all values are multiples of 10\n# $data = [10, 20, 30]\n# notice $data.all |$item| { $item % 10 == 0 }\n# ```\n#\n# Would notice `true`.\n#\n# When the first argument is a `Hash`, Puppet passes each key and value pair to the lambda\n# as an array in the form `[key, value]`.\n#\n# @example Using the `all` function with a `Hash` and a one-parameter lambda\n#\n# ```puppet\n# # For the hash $data, run a lambda using each item as a key-value array\n# $data = { 'a_0'=> 10, 'b_1' => 20 }\n# notice $data.all |$item| { $item[1] % 10 == 0  }\n# ```\n#\n# Would notice `true` if all values in the hash are multiples of 10.\n#\n# When the lambda accepts two arguments, the first argument gets the index in an array\n# or the key from a hash, and the second argument the value.\n#\n#\n# @example Using the `all` function with a hash and a two-parameter lambda\n#\n# ```puppet\n# # Check that all values are a multiple of 10 and keys start with 'abc'\n# $data = {abc_123 => 10, abc_42 => 20, abc_blue => 30}\n# notice $data.all |$key, $value| { $value % 10 == 0  and $key =~ /^abc/ }\n# ```\n#\n# Would notice `true`.\n#\n# For an general examples that demonstrates iteration, see the Puppet\n# [iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\n# documentation.\n#\n# @since 5.2.0\n#\nPuppet::Functions.create_function(:all) do\n  dispatch :all_Hash_2 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :all_Hash_1 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :all_Enumerable_2 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :all_Enumerable_1 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[1,1]', :block\n  end\n\n  def all_Hash_1(hash)\n    hash.each_pair.all? { |x| yield(x) }\n  end\n\n  def all_Hash_2(hash)\n    hash.each_pair.all? { |x, y| yield(x, y) }\n  end\n\n  def all_Enumerable_1(enumerable)\n    Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable).all? { |e| yield(e) }\n  end\n\n  def all_Enumerable_2(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.all? { |entry| yield(*entry) }\n    else\n      enum.each_with_index { |e, i| return false unless yield(i, e) }\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/annotate.rb",
    "content": "# frozen_string_literal: true\n\n# Handles annotations on objects. The function can be used in four different ways.\n#\n# With two arguments, an `Annotation` type and an object, the function returns the annotation\n# for the object of the given type, or `undef` if no such annotation exists.\n#\n# @example Using `annotate` with two arguments\n#\n# ```puppet\n# $annotation = Mod::NickNameAdapter.annotate(o)\n#\n# $annotation = annotate(Mod::NickNameAdapter.annotate, o)\n# ```\n#\n# With three arguments, an `Annotation` type, an object, and a block, the function returns the\n# annotation for the object of the given type, or annotates it with a new annotation initialized\n# from the hash returned by the given block when no such annotation exists. The block will not\n# be called when an annotation of the given type is already present.\n#\n# @example Using `annotate` with two arguments and a block\n#\n# ```puppet\n# $annotation = Mod::NickNameAdapter.annotate(o) || { { 'nick_name' => 'Buddy' } }\n#\n# $annotation = annotate(Mod::NickNameAdapter.annotate, o) || { { 'nick_name' => 'Buddy' } }\n# ```\n#\n# With three arguments, an `Annotation` type, an object, and an `Hash`, the function will annotate\n# the given object with a new annotation of the given type that is initialized from the given hash.\n# An existing annotation of the given type is discarded.\n#\n# @example Using `annotate` with three arguments where third argument is a Hash\n#\n# ```puppet\n# $annotation = Mod::NickNameAdapter.annotate(o, { 'nick_name' => 'Buddy' })\n#\n# $annotation = annotate(Mod::NickNameAdapter.annotate, o, { 'nick_name' => 'Buddy' })\n# ```\n#\n# With three arguments, an `Annotation` type, an object, and an the string `clear`, the function will\n# clear the annotation of the given type in the given object. The old annotation is returned if\n# it existed.\n#\n# @example Using `annotate` with three arguments where third argument is the string 'clear'\n#\n# ```puppet\n# $annotation = Mod::NickNameAdapter.annotate(o, clear)\n#\n# $annotation = annotate(Mod::NickNameAdapter.annotate, o, clear)\n# ```\n#\n# With three arguments, the type `Pcore`, an object, and a Hash of hashes keyed by `Annotation` types,\n# the function will annotate the given object with all types used as keys in the given hash. Each annotation\n# is initialized with the nested hash for the respective type. The annotated object is returned.\n#\n# @example Add multiple annotations to a new instance of `Mod::Person` using the `Pcore` type.\n#\n# ```puppet\n#   $person = Pcore.annotate(Mod::Person({'name' => 'William'}), {\n#     Mod::NickNameAdapter >= { 'nick_name' => 'Bill' },\n#     Mod::HobbiesAdapter => { 'hobbies' => ['Ham Radio', 'Philatelist'] }\n#   })\n# ```\n#\n# @since 5.0.0\n#\nPuppet::Functions.create_function(:annotate) do\n  dispatch :annotate do\n    param 'Type[Annotation]', :type\n    param 'Any', :value\n    optional_block_param 'Callable[0, 0]', :block\n  end\n\n  dispatch :annotate_new do\n    param 'Type[Annotation]', :type\n    param 'Any', :value\n    param 'Variant[Enum[clear],Hash[Pcore::MemberName,Any]]', :annotation_hash\n  end\n\n  dispatch :annotate_multi do\n    param 'Type[Pcore]', :type\n    param 'Any', :value\n    param 'Hash[Type[Annotation], Hash[Pcore::MemberName,Any]]', :annotations\n  end\n\n  # @param type [Annotation] the annotation type\n  # @param value [Object] the value to annotate\n  # @param block [Proc] optional block to produce the annotation hash\n  #\n  def annotate(type, value, &block)\n    type.implementation_class.annotate(value, &block)\n  end\n\n  # @param type [Annotation] the annotation type\n  # @param value [Object] the value to annotate\n  # @param annotation_hash [Hash{String => Object}] the annotation hash\n  #\n  def annotate_new(type, value, annotation_hash)\n    type.implementation_class.annotate_new(value, annotation_hash)\n  end\n\n  # @param type [Type] the Pcore type\n  # @param value [Object] the value to annotate\n  # @param annotations [Hash{Annotation => Hash{String => Object}}] hash of annotation hashes\n  #\n  def annotate_multi(type, value, annotations)\n    type.implementation_class.annotate(value, annotations)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/any.rb",
    "content": "# frozen_string_literal: true\n\n# Runs a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# repeatedly using each value in a data structure until the lambda returns a \"truthy\" value which\n# makes the function return `true`, or if the end of the iteration is reached, false is returned.\n#\n# This function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It can\n# request one or two parameters.\n#\n# @example Using the `any` function\n#\n# `$data.any |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `any($data) |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# @example Using the `any` function with an Array and a one-parameter lambda\n#\n# ```puppet\n# # For the array $data, run a lambda that checks if an unknown hash contains those keys\n# $data = [\"routers\", \"servers\", \"workstations\"]\n# $looked_up = lookup('somekey', Hash)\n# notice $data.any |$item| { $looked_up[$item] }\n# ```\n#\n# Would notice `true` if the looked up hash had a value that is neither `false` nor `undef` for at least\n# one of the keys. That is, it is equivalent to the expression\n# `$looked_up[routers] || $looked_up[servers] || $looked_up[workstations]`.\n#\n# When the first argument is a `Hash`, Puppet passes each key and value pair to the lambda\n# as an array in the form `[key, value]`.\n#\n# @example Using the `any` function with a `Hash` and a one-parameter lambda\n#\n# ```puppet\n# # For the hash $data, run a lambda using each item as a key-value array.\n# $data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n# $looked_up = lookup('somekey', Hash)\n# notice $data.any |$item| { $looked_up[$item[0]] }\n# ```\n#\n# Would notice `true` if the looked up hash had a value for one of the wanted key that is\n# neither `false` nor `undef`.\n#\n# When the lambda accepts two arguments, the first argument gets the index in an array\n# or the key from a hash, and the second argument the value.\n#\n#\n# @example Using the `any` function with an array and a two-parameter lambda\n#\n# ```puppet\n# # Check if there is an even numbered index that has a non String value\n# $data = [key1, 1, 2, 2]\n# notice $data.any |$index, $value| { $index % 2 == 0 and $value !~ String }\n# ```\n#\n# Would notice true as the index `2` is even and not a `String`\n#\n# For an general examples that demonstrates iteration, see the Puppet\n# [iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\n# documentation.\n#\n# @since 5.2.0\n#\nPuppet::Functions.create_function(:any) do\n  dispatch :any_Hash_2 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :any_Hash_1 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :any_Enumerable_2 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :any_Enumerable_1 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[1,1]', :block\n  end\n\n  def any_Hash_1(hash)\n    hash.each_pair.any? { |x| yield(x) }\n  end\n\n  def any_Hash_2(hash)\n    hash.each_pair.any? { |x, y| yield(x, y) }\n  end\n\n  def any_Enumerable_1(enumerable)\n    Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable).any? { |e| yield(e) }\n  end\n\n  def any_Enumerable_2(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.any? { |entry| yield(*entry) }\n    else\n      enum.each_with_index { |e, i| return true if yield(i, e) }\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/assert_type.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the given value if it is of the given\n# [data type](https://puppet.com/docs/puppet/latest/lang_data.html), or\n# otherwise either raises an error or executes an optional two-parameter\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html).\n#\n# The function takes two mandatory arguments, in this order:\n#\n# 1. The expected data type.\n# 2. A value to compare against the expected data type.\n#\n# @example Using `assert_type`\n#\n# ```puppet\n# $raw_username = 'Amy Berry'\n#\n# # Assert that $raw_username is a non-empty string and assign it to $valid_username.\n# $valid_username = assert_type(String[1], $raw_username)\n#\n# # $valid_username contains \"Amy Berry\".\n# # If $raw_username was an empty string or a different data type, the Puppet run would\n# # fail with an \"Expected type does not match actual\" error.\n# ```\n#\n# You can use an optional lambda to provide enhanced feedback. The lambda takes two\n# mandatory parameters, in this order:\n#\n# 1. The expected data type as described in the function's first argument.\n# 2. The actual data type of the value.\n#\n# @example Using `assert_type` with a warning and default value\n#\n# ```puppet\n# $raw_username = 'Amy Berry'\n#\n# # Assert that $raw_username is a non-empty string and assign it to $valid_username.\n# # If it isn't, output a warning describing the problem and use a default value.\n# $valid_username = assert_type(String[1], $raw_username) |$expected, $actual| {\n#   warning( \"The username should be \\'${expected}\\', not \\'${actual}\\'. Using 'anonymous'.\" )\n#   'anonymous'\n# }\n#\n# # $valid_username contains \"Amy Berry\".\n# # If $raw_username was an empty string, the Puppet run would set $valid_username to\n# # \"anonymous\" and output a warning: \"The username should be 'String[1, default]', not\n# # 'String[0, 0]'. Using 'anonymous'.\"\n# ```\n#\n# For more information about data types, see the\n# [documentation](https://puppet.com/docs/puppet/latest/lang_data.html).\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:assert_type) do\n  dispatch :assert_type do\n    param 'Type', :type\n    param 'Any', :value\n    optional_block_param 'Callable[Type, Type]', :block\n  end\n\n  dispatch :assert_type_s do\n    param 'String', :type_string\n    param 'Any', :value\n    optional_block_param 'Callable[Type, Type]', :block\n  end\n\n  # @param type [Type] the type the value must be an instance of\n  # @param value [Object] the value to assert\n  #\n  def assert_type(type, value)\n    unless Puppet::Pops::Types::TypeCalculator.instance?(type, value)\n      inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value)\n      if block_given?\n        # Give the inferred type to allow richer comparison in the given block (if generalized\n        # information is lost).\n        #\n        value = yield(type, inferred_type)\n      else\n        raise Puppet::Pops::Types::TypeAssertionError.new(\n          Puppet::Pops::Types::TypeMismatchDescriber.singleton.describe_mismatch('assert_type():', type, inferred_type),\n          type, inferred_type\n        )\n      end\n    end\n    value\n  end\n\n  # @param type_string [String] the type the value must be an instance of given in String form\n  # @param value [Object] the value to assert\n  #\n  def assert_type_s(type_string, value, &proc)\n    t = Puppet::Pops::Types::TypeParser.singleton.parse(type_string)\n    block_given? ? assert_type(t, value, &proc) : assert_type(t, value)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/binary_file.rb",
    "content": "# frozen_string_literal: true\n\n# Loads a binary file from a module or file system and returns its contents as a `Binary`.\n# The argument to this function should be a `<MODULE NAME>/<FILE>`\n# reference, which will load `<FILE>` from a module's `files`\n# directory. (For example, the reference `mysql/mysqltuner.pl` will load the\n# file `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n#\n# This function also accepts an absolute file path that allows reading\n# binary file content from anywhere on disk.\n#\n# An error is raised if the given file does not exists.\n#\n# To search for the existence of files, use the `find_file()` function.\n#\n# - since 4.8.0\n#\n# @since 4.8.0\n#\nPuppet::Functions.create_function(:binary_file, Puppet::Functions::InternalFunction) do\n  dispatch :binary_file do\n    scope_param\n    param 'String', :path\n  end\n\n  def binary_file(scope, unresolved_path)\n    path = Puppet::Parser::Files.find_file(unresolved_path, scope.compiler.environment)\n    unless path && Puppet::FileSystem.exist?(path)\n      # TRANSLATORS the string \"binary_file()\" should not be translated\n      raise Puppet::ParseError, _(\"binary_file(): The given file '%{unresolved_path}' does not exist\") % { unresolved_path: unresolved_path }\n    end\n\n    Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(Puppet::FileSystem.binread(path))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/break.rb",
    "content": "# frozen_string_literal: true\n\n# Breaks an innermost iteration as if it encountered an end of input.\n# This function does not return to the caller.\n#\n# The signal produced to stop the iteration bubbles up through\n# the call stack until either terminating the innermost iteration or\n# raising an error if the end of the call stack is reached.\n#\n# The break() function does not accept an argument.\n#\n# @example Using `break`\n#\n# ```puppet\n# $data = [1,2,3]\n# notice $data.map |$x| { if $x == 3 { break() } $x*10 }\n# ```\n#\n# Would notice the value `[10, 20]`\n#\n# @example Using a nested `break`\n#\n# ```puppet\n# function break_if_even($x) {\n#   if $x % 2 == 0 { break() }\n# }\n# $data = [1,2,3]\n# notice $data.map |$x| { break_if_even($x); $x*10 }\n# ```\n# Would notice the value `[10]`\n#\n# * Also see functions `next` and `return`\n#\n# @since 4.8.0\n#\nPuppet::Functions.create_function(:break) do\n  dispatch :break_impl do\n  end\n\n  def break_impl\n    # get file, line if available, else they are set to nil\n    file, line = Puppet::Pops::PuppetStack.top_of_stack\n\n    # PuppetStopIteration contains file and line and is a StopIteration exception\n    # so it can break a Ruby Kernel#loop or enumeration\n    #\n    raise Puppet::Pops::Evaluator::PuppetStopIteration.new(file, line)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/call.rb",
    "content": "# frozen_string_literal: true\n\n# Calls an arbitrary Puppet function by name.\n#\n# This function takes one mandatory argument and one or more optional arguments:\n#\n# 1. A string corresponding to a function name.\n# 2. Any number of arguments to be passed to the called function.\n# 3. An optional lambda, if the function being called supports it.\n#\n# This function can also be used to resolve a `Deferred` given as\n# the only argument to the function (does not accept arguments nor\n# a block).\n#\n# @example Using the `call` function\n#\n# ```puppet\n# $a = 'notice'\n# call($a, 'message')\n# ```\n#\n# @example Using the `call` function with a lambda\n#\n# ```puppet\n# $a = 'each'\n# $b = [1,2,3]\n# call($a, $b) |$item| {\n#  notify { $item: }\n# }\n# ```\n#\n# The `call` function can be used to call either Ruby functions or Puppet language\n# functions.\n#\n# When used with `Deferred` values, the deferred value can either describe\n# a function call, or a dig into a variable.\n#\n# @example Resolving a deferred function call\n#\n# ```puppet\n# $d = Deferred('join', [[1,2,3], ':']) # A future call to join that joins the arguments 1,2,3 with ':'\n# notice($d.call())\n# ```\n#\n# Would notice the string \"1:2:3\".\n#\n# @example Resolving a deferred variable value with optional dig into its structure\n#\n# ```puppet\n# $d = Deferred('$facts', ['processors', 'count'])\n# notice($d.call())\n# ```\n#\n# Would notice the value of `$facts['processors']['count']` at the time when the `call` is made.\n#\n# * Deferred values supported since Puppet 6.0\n#\n# @since 5.0.0\n#\nPuppet::Functions.create_function(:call, Puppet::Functions::InternalFunction) do\n  dispatch :call_impl_block do\n    scope_param\n    param 'String', :function_name\n    repeated_param 'Any', :arguments\n    optional_block_param\n  end\n\n  dispatch :call_deferred do\n    scope_param\n    param 'Deferred', :deferred\n  end\n\n  def call_impl_block(scope, function_name, *args, &block)\n    # The call function must be able to call functions loaded by any loader visible from the calling scope.\n    Puppet::Pops::Parser::EvaluatingParser.new.evaluator.external_call_function(function_name, args, scope, &block)\n  end\n\n  def call_deferred(scope, deferred)\n    Puppet::Pops::Evaluator::DeferredResolver.resolve(deferred, scope.compiler)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/camelcase.rb",
    "content": "# frozen_string_literal: true\n\n# Creates a Camel Case version of a String\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String` the conversion replaces all combinations of `*_<char>*` with an upcased version of the\n#   character following the _.  This is done using Ruby system locale which handles some, but not all\n#   special international up-casing rules (for example German double-s ß is upcased to \"Ss\").\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is capitalized and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n# * The result will not contain any underscore characters.\n#\n# Please note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\n# To ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n#\n# @example Camelcase a String\n# ```puppet\n# 'hello_friend'.camelcase()\n# camelcase('hello_friend')\n# ```\n# Would both result in `\"HelloFriend\"`\n#\n# @example Camelcase of strings in an Array\n# ```puppet\n# ['abc_def', 'bcd_xyz'].camelcase()\n# camelcase(['abc_def', 'bcd_xyz'])\n# ```\n# Would both result in `['AbcDef', 'BcdXyz']`\n#\nPuppet::Functions.create_function(:camelcase) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.split('_').map(&:capitalize).join('')\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_camelcase(x) }\n  end\n\n  def do_camelcase(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? on_string(x) : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/capitalize.rb",
    "content": "# frozen_string_literal: true\n\n# Capitalizes the first character of a String, or the first character of every String in an Iterable value (such as an Array).\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String`, a string is returned in which the first character is uppercase.\n#   This is done using Ruby system locale which handles some, but not all\n#   special international up-casing rules (for example German double-s ß is capitalized to \"Ss\").\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is capitalized and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# Please note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\n# To ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n#\n# @example Capitalizing a String\n# ```puppet\n# 'hello'.capitalize()\n# capitalize('hello')\n# ```\n# Would both result in `\"Hello\"`\n#\n# @example Capitalizing strings in an Array\n# ```puppet\n# ['abc', 'bcd'].capitalize()\n# capitalize(['abc', 'bcd'])\n# ```\n# Would both result in `['Abc', 'Bcd']`\n#\nPuppet::Functions.create_function(:capitalize) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.capitalize\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_capitalize(x) }\n  end\n\n  def do_capitalize(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? x.capitalize : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/ceiling.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the smallest `Integer` greater or equal to the argument.\n# Takes a single numeric value as an argument.\n#\n# This function is backwards compatible with the same function in stdlib\n# and accepts a `Numeric` value. A `String` that can be converted\n# to a floating point number can also be used in this version - but this\n# is deprecated.\n#\n# In general convert string input to `Numeric` before calling this function\n# to have full control over how the conversion is done.\n#\nPuppet::Functions.create_function(:ceiling) do\n  dispatch :on_numeric do\n    param 'Numeric', :val\n  end\n\n  dispatch :on_string do\n    param 'String', :val\n  end\n\n  def on_numeric(x)\n    x.ceil\n  end\n\n  def on_string(x)\n    Puppet.warn_once('deprecations', 'ceiling_function_numeric_coerce_string',\n                     _(\"The ceiling() function's auto conversion of String to Float is deprecated - change to convert input before calling\"))\n\n    begin\n      Float(x).ceil\n    rescue TypeError, ArgumentError => _e\n      # TRANSLATORS: 'ceiling' is a name and should not be translated\n      raise(ArgumentError, _('ceiling(): cannot convert given value to a floating point value.'))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/chomp.rb",
    "content": "# frozen_string_literal: true\n\n# Returns a new string with the record separator character(s) removed.\n# The record separator is the line ending characters `\\r` and `\\n`.\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String` the conversion removes `\\r\\n`, `\\n` or `\\r` from the end of a string.\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# @example Removing line endings\n# ```puppet\n# \"hello\\r\\n\".chomp()\n# chomp(\"hello\\r\\n\")\n# ```\n# Would both result in `\"hello\"`\n#\n# @example Removing line endings in an array\n# ```puppet\n# [\"hello\\r\\n\", \"hi\\r\\n\"].chomp()\n# chomp([\"hello\\r\\n\", \"hi\\r\\n\"])\n# ```\n# Would both result in `['hello', 'hi']`\n#\nPuppet::Functions.create_function(:chomp) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.chomp\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_chomp(x) }\n  end\n\n  def do_chomp(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? x.chomp : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/chop.rb",
    "content": "# frozen_string_literal: true\n\n# Returns a new string with the last character removed.\n# If the string ends with `\\r\\n`, both characters are removed. Applying chop to an empty\n# string returns an empty string. If you wish to merely remove record\n# separators then you should use the `chomp` function.\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String` the conversion removes the last character, or if it ends with \\r\\n` it removes both. If String is empty\n#   an empty string is returned.\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# @example Removing line endings\n# ```puppet\n# \"hello\\r\\n\".chop()\n# chop(\"hello\\r\\n\")\n# ```\n# Would both result in `\"hello\"`\n#\n# @example Removing last char\n# ```puppet\n# \"hello\".chop()\n# chop(\"hello\")\n# ```\n# Would both result in `\"hell\"`\n#\n# @example Removing last char in an array\n# ```puppet\n# [\"hello\\r\\n\", \"hi\\r\\n\"].chop()\n# chop([\"hello\\r\\n\", \"hi\\r\\n\"])\n# ```\n# Would both result in `['hello', 'hi']`\n#\nPuppet::Functions.create_function(:chop) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.chop\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_chop(x) }\n  end\n\n  def do_chop(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? x.chop : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/compare.rb",
    "content": "# frozen_string_literal: true\n\n# Compares two values and returns -1, 0 or 1 if first value is smaller, equal or larger than the second value.\n# The compare function accepts arguments of the data types `String`, `Numeric`, `Timespan`, `Timestamp`, and `Semver`,\n# such that:\n#\n# * two of the same data type can be compared\n# * `Timespan` and `Timestamp` can be compared with each other and with `Numeric`\n#\n# When comparing two `String` values the comparison can be made to consider case by passing a third (optional)\n# boolean `false` value - the default is `true` which ignores case as the comparison operators\n# in the Puppet Language.\n#\nPuppet::Functions.create_function(:compare) do\n  local_types do\n    type 'TimeComparable = Variant[Numeric, Timespan, Timestamp]'\n    type 'Comparable = Variant[String, Semver, TimeComparable]'\n  end\n\n  dispatch :on_numeric do\n    param 'Numeric', :a\n    param 'Numeric', :b\n  end\n\n  dispatch :on_string do\n    param 'String', :a\n    param 'String', :b\n    optional_param 'Boolean', :ignore_case\n  end\n\n  dispatch :on_version do\n    param 'Semver', :a\n    param 'Semver', :b\n  end\n\n  dispatch :on_time_num_first do\n    param 'Numeric', :a\n    param 'Variant[Timespan, Timestamp]', :b\n  end\n\n  dispatch :on_timestamp do\n    param 'Timestamp', :a\n    param 'Variant[Timestamp, Numeric]', :b\n  end\n\n  dispatch :on_timespan do\n    param 'Timespan', :a\n    param 'Variant[Timespan, Numeric]', :b\n  end\n\n  argument_mismatch :on_error do\n    param 'Comparable', :a\n    param 'Comparable', :b\n    repeated_param 'Any', :ignore_case\n  end\n\n  argument_mismatch :on_not_comparable do\n    param 'Any', :a\n    param 'Any', :b\n    repeated_param 'Any', :ignore_case\n  end\n\n  def on_numeric(a, b)\n    a <=> b\n  end\n\n  def on_string(a, b, ignore_case = true)\n    if ignore_case\n      a.casecmp(b)\n    else\n      a <=> b\n    end\n  end\n\n  def on_version(a, b)\n    a <=> b\n  end\n\n  def on_time_num_first(a, b)\n    # Time data types can compare against Numeric but not the other way around\n    # the comparison is therefore done in reverse and the answer is inverted.\n    -(b <=> a)\n  end\n\n  def on_timespan(a, b)\n    a <=> b\n  end\n\n  def on_timestamp(a, b)\n    a <=> b\n  end\n\n  def on_not_comparable(a, b, *ignore_case)\n    # TRANSLATORS 'compare' is a name\n    _(\"compare(): Non comparable type. Only values of the types Numeric, String, Semver, Timestamp and Timestamp can be compared. Got %{type_a} and %{type_b}\") % {\n      type_a: type_label(a), type_b: type_label(b)\n    }\n  end\n\n  def on_error(a, b, *ignore_case)\n    unless ignore_case.empty?\n      unless a.is_a?(String) && b.is_a?(String)\n        # TRANSLATORS 'compare' is a name\n        return _(\"compare(): The third argument (ignore case) can only be used when comparing strings\")\n      end\n      unless ignore_case.size == 1\n        # TRANSLATORS 'compare' is a name\n        return _(\"compare(): Accepts at most 3 arguments, got %{actual_number}\") % { actual_number: 2 + ignore_case.size }\n      end\n      unless ignore_case[0].is_a?(Boolean)\n        # TRANSLATORS 'compare' is a name\n        return _(\"compare(): The third argument (ignore case) must be a Boolean. Got %{type}\") % { type: type_label(ignore_case[0]) }\n      end\n    end\n\n    if a.class != b.class\n      # TRANSLATORS 'compare' is a name\n      _(\"compare(): Can only compare values of the same type (or for Timestamp/Timespan also against Numeric). Got %{type_a} and %{type_b}\") % {\n        type_a: type_label(a), type_b: type_label(b)\n      }\n    end\n  end\n\n  def type_label(x)\n    Puppet::Pops::Model::ModelLabelProvider.new.label(x)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/contain.rb",
    "content": "# frozen_string_literal: true\n\n# Makes one or more classes be contained inside the current class.\n# If any of these classes are undeclared, they will be declared as if\n# there were declared with the `include` function.\n# Accepts a class name, an array of class names, or a comma-separated\n# list of class names.\n#\n# A contained class will not be applied before the containing class is\n# begun, and will be finished before the containing class is finished.\n#\n# You must use the class's full name;\n# relative names are not allowed. In addition to names in string form,\n# you may also directly use `Class` and `Resource` `Type`-values that are produced by\n# evaluating resource and relationship expressions.\n#\n# The function returns an array of references to the classes that were contained thus\n# allowing the function call to `contain` to directly continue.\n#\n# - Since 4.0.0 support for `Class` and `Resource` `Type`-values, absolute names\n# - Since 4.7.0 a value of type `Array[Type[Class[n]]]` is returned with all the contained classes\n#\nPuppet::Functions.create_function(:contain, Puppet::Functions::InternalFunction) do\n  dispatch :contain do\n    scope_param\n    # The function supports what the type system sees as Ruby runtime objects, and\n    # they cannot be parameterized to find what is actually valid instances.\n    # The validation is instead done in the function body itself via a call to\n    # `transform_and_assert_classnames` on the calling scope.\n    required_repeated_param 'Any', :names\n  end\n\n  def contain(scope, *classes)\n    if Puppet[:tasks]\n      raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n        Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n        { :operation => 'contain' }\n      )\n    end\n\n    # Make call patterns uniform and protected against nested arrays, also make\n    # names absolute if so desired.\n    classes = scope.transform_and_assert_classnames(classes.flatten)\n\n    result = classes.map { |name| Puppet::Pops::Types::TypeFactory.host_class(name) }\n    containing_resource = scope.resource\n\n    # This is the same as calling the include function but faster and does not rely on the include\n    # function.\n    (scope.compiler.evaluate_classes(classes, scope, false) || []).each do |resource|\n      unless scope.catalog.edge?(containing_resource, resource)\n        scope.catalog.add_edge(containing_resource, resource)\n      end\n    end\n    # Result is an Array[Class, 1, n] which allows chaining other operations\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/convert_to.rb",
    "content": "# frozen_string_literal: true\n\n# The `convert_to(value, type)` is a convenience function that does the same as `new(type, value)`.\n# The difference in the argument ordering allows it to be used in chained style for\n# improved readability \"left to right\".\n#\n# When the function is given a lambda, it is called with the converted value, and the function\n# returns what the lambda returns, otherwise the converted value.\n#\n# @example 'convert_to' instead of 'new'\n#\n# ```puppet\n#   # The harder to read variant:\n#   # Using new operator - that is \"calling the type\" with operator ()\n#   Hash(Array(\"abc\").map |$i,$v| { [$i, $v] })\n#\n#   # The easier to read variant:\n#   # using 'convert_to'\n#   \"abc\".convert_to(Array).map |$i,$v| { [$i, $v] }.convert_to(Hash)\n# ```\n#\n# @since 5.4.0\n#\nPuppet::Functions.create_function(:convert_to) do\n  dispatch :convert_to do\n    param 'Any', :value\n    param 'Type', :type\n    optional_repeated_param 'Any', :args\n    optional_block_param 'Callable[1,1]', :block\n  end\n\n  def convert_to(value, type, *args, &block)\n    result = call_function('new', type, value, *args)\n    block_given? ? yield(result) : result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/crit.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `crit`.\nPuppet::Functions.create_function(:crit, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :crit do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def crit(scope, *values)\n    Puppet::Util::Log.log_func(scope, :crit, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/debug.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `debug`.\nPuppet::Functions.create_function(:debug, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :debug do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def debug(scope, *values)\n    Puppet::Util::Log.log_func(scope, :debug, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/defined.rb",
    "content": "# frozen_string_literal: true\n\n# Determines whether a given class or resource type is defined and returns a Boolean\n# value. You can also use `defined` to determine whether a specific resource is defined,\n# or whether a variable has a value (including `undef`, as opposed to the variable never\n# being declared or assigned).\n#\n# This function takes at least one string argument, which can be a class name, type name,\n# resource reference, or variable reference of the form `'$name'`. (Note that the `$` sign\n# is included in the string which must be in single quotes to prevent the `$` character\n# to be interpreted as interpolation.\n#\n# The `defined` function checks both native and defined types, including types\n# provided by modules. Types and classes are matched by their names. The function matches\n# resource declarations by using resource references.\n#\n# @example Different types of `defined` function matches\n#\n# ```puppet\n# # Matching resource types\n# defined(\"file\")\n# defined(\"customtype\")\n#\n# # Matching defines and classes\n# defined(\"foo\")\n# defined(\"foo::bar\")\n#\n# # Matching variables (note the single quotes)\n# defined('$name')\n#\n# # Matching declared resources\n# defined(File['/tmp/file'])\n# ```\n#\n# Puppet depends on the configuration's evaluation order when checking whether a resource\n# is declared.\n#\n# @example Importance of evaluation order when using `defined`\n#\n# ```puppet\n# # Assign values to $is_defined_before and $is_defined_after using identical `defined`\n# # functions.\n#\n# $is_defined_before = defined(File['/tmp/file'])\n#\n# file { \"/tmp/file\":\n#   ensure => present,\n# }\n#\n# $is_defined_after = defined(File['/tmp/file'])\n#\n# # $is_defined_before returns false, but $is_defined_after returns true.\n# ```\n#\n# This order requirement only refers to evaluation order. The order of resources in the\n# configuration graph (e.g. with `before` or `require`) does not affect the `defined`\n# function's behavior.\n#\n# > **Warning:** Avoid relying on the result of the `defined` function in modules, as you\n# > might not be able to guarantee the evaluation order well enough to produce consistent\n# > results. This can cause other code that relies on the function's result to behave\n# > inconsistently or fail.\n#\n# If you pass more than one argument to `defined`, the function returns `true` if _any_\n# of the arguments are defined. You can also match resources by type, allowing you to\n# match conditions of different levels of specificity, such as whether a specific resource\n# is of a specific data type.\n#\n# @example Matching multiple resources and resources by different types with `defined`\n#\n# ```puppet\n# file { \"/tmp/file1\":\n#   ensure => file,\n# }\n#\n# $tmp_file = file { \"/tmp/file2\":\n#   ensure => file,\n# }\n#\n# # Each of these statements return `true` ...\n# defined(File['/tmp/file1'])\n# defined(File['/tmp/file1'],File['/tmp/file2'])\n# defined(File['/tmp/file1'],File['/tmp/file2'],File['/tmp/file3'])\n# # ... but this returns `false`.\n# defined(File['/tmp/file3'])\n#\n# # Each of these statements returns `true` ...\n# defined(Type[Resource['file','/tmp/file2']])\n# defined(Resource['file','/tmp/file2'])\n# defined(File['/tmp/file2'])\n# defined('$tmp_file')\n# # ... but each of these returns `false`.\n# defined(Type[Resource['exec','/tmp/file2']])\n# defined(Resource['exec','/tmp/file2'])\n# defined(File['/tmp/file3'])\n# defined('$tmp_file2')\n# ```\n#\n# @since 2.7.0\n# @since 3.6.0 variable reference and future parser types\n# @since 3.8.1 type specific requests with future parser\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:defined, Puppet::Functions::InternalFunction) do\n  dispatch :is_defined do\n    scope_param\n    required_repeated_param 'Variant[String, Type[CatalogEntry], Type[Type[CatalogEntry]]]', :vals\n  end\n\n  def is_defined(scope, *vals) # rubocop:disable Naming/PredicateName\n    vals.any? do |val|\n      case val\n      when String\n        if val =~ /^\\$(.+)$/\n          scope.exist?(Regexp.last_match(1))\n        else\n          case val\n          when ''\n            next nil\n          when 'main'\n            # Find the main class (known as ''), it does not have to be in the catalog\n            Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_main_class(scope)\n          else\n            # Find a resource type, definition or class definition\n            Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type_or_class(scope, val)\n          end\n        end\n      when Puppet::Resource\n        # Find instance of given resource type and title that is in the catalog\n        scope.compiler.findresource(val.resource_type, val.title)\n\n      when Puppet::Pops::Types::PResourceType\n        raise ArgumentError, _('The given resource type is a reference to all kind of types') if val.type_name.nil?\n\n        type = Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type(scope, val.type_name)\n        val.title.nil? ? type : scope.compiler.findresource(type, val.title)\n\n      when Puppet::Pops::Types::PClassType\n        raise ArgumentError, _('The given class type is a reference to all classes') if val.class_name.nil?\n\n        scope.compiler.findresource(:class, val.class_name)\n\n      when Puppet::Pops::Types::PTypeType\n        case val.type\n        when Puppet::Pops::Types::PResourceType\n          # It is most reasonable to take Type[File] and Type[File[foo]] to mean the same as if not wrapped in a Type\n          # Since the difference between File and File[foo] already captures the distinction of type vs instance.\n          is_defined(scope, val.type)\n\n        when Puppet::Pops::Types::PClassType\n          # Interpreted as asking if a class (and nothing else) is defined without having to be included in the catalog\n          # (this is the same as asking for just the class' name, but with the added certainty that it cannot be a defined type.\n          #\n          raise ArgumentError, _('The given class type is a reference to all classes') if val.type.class_name.nil?\n\n          Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_hostclass(scope, val.type.class_name)\n        end\n      else\n        raise ArgumentError, _(\"Invalid argument of type '%{value_class}' to 'defined'\") % { value_class: val.class }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/dig.rb",
    "content": "# frozen_string_literal: true\n\n# Returns a value for a sequence of given keys/indexes into a structure, such as\n# an array or hash.\n#\n# This function is used to \"dig into\" a complex data structure by\n# using a sequence of keys / indexes to access a value from which\n# the next key/index is accessed recursively.\n#\n# The first encountered `undef` value or key stops the \"dig\" and `undef` is returned.\n#\n# An error is raised if an attempt is made to \"dig\" into\n# something other than an `undef` (which immediately returns `undef`), an `Array` or a `Hash`.\n#\n# @example Using `dig`\n#\n# ```puppet\n# $data = {a => { b => [{x => 10, y => 20}, {x => 100, y => 200}]}}\n# notice $data.dig('a', 'b', 1, 'x')\n# ```\n#\n# Would notice the value 100.\n#\n# This is roughly equivalent to `$data['a']['b'][1]['x']`. However, a standard\n# index will return an error and cause catalog compilation failure if any parent\n# of the final key (`'x'`) is `undef`. The `dig` function will return `undef`,\n# rather than failing catalog compilation. This allows you to check if data\n# exists in a structure without mandating that it always exists.\n#\n# @since 4.5.0\n#\nPuppet::Functions.create_function(:dig) do\n  dispatch :dig do\n    param 'Optional[Collection]', :data\n    repeated_param 'Any', :arg\n  end\n\n  def dig(data, *args)\n    walked_path = []\n    args.reduce(data) do |d, k|\n      return nil if d.nil? || k.nil?\n\n      unless d.is_a?(Array) || d.is_a?(Hash)\n        t = Puppet::Pops::Types::TypeCalculator.infer(d)\n        msg = _(\"The given data does not contain a Collection at %{walked_path}, got '%{type}'\") % { walked_path: walked_path, type: t }\n        error_data = Puppet::DataTypes::Error.new(\n          msg,\n          'SLICE_ERROR',\n          { 'walked_path' => walked_path, 'value_type' => t },\n          'EXPECTED_COLLECTION'\n        )\n        raise Puppet::ErrorWithData.new(error_data, msg)\n      end\n\n      walked_path << k\n      if d.is_a?(Array) && !k.is_a?(Integer)\n        t = Puppet::Pops::Types::TypeCalculator.infer(k)\n        msg = _(\"The given data requires an Integer index at %{walked_path}, got '%{type}'\") % { walked_path: walked_path, type: t }\n        error_data = Puppet::DataTypes::Error.new(\n          msg,\n          'SLICE_ERROR',\n          { 'walked_path' => walked_path, 'index_type' => t },\n          'EXPECTED_INTEGER_INDEX'\n        )\n        raise Puppet::ErrorWithData.new(error_data, msg)\n      end\n      d[k]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/downcase.rb",
    "content": "# frozen_string_literal: true\n\n# Converts a String, Array or Hash (recursively) into lower case.\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String`, its lower case version is returned. This is done using Ruby system locale which handles some, but not all\n#   special international up-casing rules (for example German double-s ß is upcased to \"SS\", whereas upper case double-s\n#   is downcased to ß).\n# * For `Array` and `Hash` the conversion to lower case is recursive and each key and value must be convertible by\n#   this function.\n# * When a `Hash` is converted, some keys could result in the same key - in those cases, the\n#   latest key-value wins. For example if keys \"aBC\", and \"abC\" where both present, after downcase there would only be one\n#   key \"abc\".\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# Please note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\n# To ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n#\n# @example Converting a String to lower case\n# ```puppet\n# 'HELLO'.downcase()\n# downcase('HEllO')\n# ```\n# Would both result in `\"hello\"`\n#\n# @example Converting an Array to lower case\n# ```puppet\n# ['A', 'B'].downcase()\n# downcase(['A', 'B'])\n# ```\n# Would both result in `['a', 'b']`\n#\n# @example Converting a Hash to lower case\n# ```puppet\n# {'A' => 'HEllO', 'B' => 'GOODBYE'}.downcase()\n# ```\n# Would result in `{'a' => 'hello', 'b' => 'goodbye'}`\n#\n# @example Converting a recursive structure\n# ```puppet\n# ['A', 'B', ['C', ['D']], {'X' => 'Y'}].downcase\n# ```\n# Would result in `['a', 'b', ['c', ['d']], {'x' => 'y'}]`\n#\nPuppet::Functions.create_function(:downcase) do\n  local_types do\n    type 'StringData = Variant[String, Numeric, Array[StringData], Hash[StringData, StringData]]'\n  end\n\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_array do\n    param 'Array[StringData]', :arg\n  end\n\n  dispatch :on_hash do\n    param 'Hash[StringData, StringData]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.downcase\n  end\n\n  def on_array(a)\n    a.map { |x| do_downcase(x) }\n  end\n\n  def on_hash(h)\n    result = {}\n    h.each_pair { |k, v| result[do_downcase(k)] = do_downcase(v) }\n    result\n  end\n\n  def do_downcase(x)\n    x.is_a?(String) ? x.downcase : call_function('downcase', x)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/each.rb",
    "content": "# frozen_string_literal: true\n\n# Runs a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# repeatedly using each value in a data structure, then returns the values unchanged.\n#\n# This function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It can\n# request one or two parameters.\n#\n# @example Using the `each` function\n#\n# `$data.each |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `each($data) |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# When the first argument (`$data` in the above example) is an array, Puppet passes each\n# value in turn to the lambda, then returns the original values.\n#\n# @example Using the `each` function with an array and a one-parameter lambda\n#\n# ```puppet\n# # For the array $data, run a lambda that creates a resource for each item.\n# $data = [\"routers\", \"servers\", \"workstations\"]\n# $data.each |$item| {\n#  notify { $item:\n#    message => $item\n#  }\n# }\n# # Puppet creates one resource for each of the three items in $data. Each resource is\n# # named after the item's value and uses the item's value in a parameter.\n# ```\n#\n# When the first argument is a hash, Puppet passes each key and value pair to the lambda\n# as an array in the form `[key, value]` and returns the original hash.\n#\n# @example Using the `each` function with a hash and a one-parameter lambda\n#\n# ```puppet\n# # For the hash $data, run a lambda using each item as a key-value array that creates a\n# # resource for each item.\n# $data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n# $data.each |$items| {\n#  notify { $items[0]:\n#    message => $items[1]\n#  }\n# }\n# # Puppet creates one resource for each of the three items in $data, each named after the\n# # item's key and containing a parameter using the item's value.\n# ```\n#\n# When the first argument is an array and the lambda has two parameters, Puppet passes the\n# array's indexes (enumerated from 0) in the first parameter and its values in the second\n# parameter.\n#\n# @example Using the `each` function with an array and a two-parameter lambda\n#\n# ```puppet\n# # For the array $data, run a lambda using each item's index and value that creates a\n# # resource for each item.\n# $data = [\"routers\", \"servers\", \"workstations\"]\n# $data.each |$index, $value| {\n#  notify { $value:\n#    message => $index\n#  }\n# }\n# # Puppet creates one resource for each of the three items in $data, each named after the\n# # item's value and containing a parameter using the item's index.\n# ```\n#\n# When the first argument is a hash, Puppet passes its keys to the first parameter and its\n# values to the second parameter.\n#\n# @example Using the `each` function with a hash and a two-parameter lambda\n#\n# ```puppet\n# # For the hash $data, run a lambda using each item's key and value to create a resource\n# # for each item.\n# $data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n# $data.each |$key, $value| {\n#  notify { $key:\n#    message => $value\n#  }\n# }\n# # Puppet creates one resource for each of the three items in $data, each named after the\n# # item's key and containing a parameter using the item's value.\n# ```\n#\n# For an example that demonstrates how to create multiple `file` resources using `each`,\n# see the Puppet\n# [iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\n# documentation.\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:each) do\n  dispatch :foreach_Hash_2 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :foreach_Hash_1 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :foreach_Enumerable_2 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :foreach_Enumerable_1 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[1,1]', :block\n  end\n\n  def foreach_Hash_1(hash)\n    begin\n      hash.each_pair do |pair|\n        yield(pair)\n      end\n    rescue StopIteration\n    end\n    # produces the receiver\n    hash\n  end\n\n  def foreach_Hash_2(hash)\n    begin\n      hash.each_pair do |pair|\n        yield(*pair)\n      end\n    rescue StopIteration\n    end\n    # produces the receiver\n    hash\n  end\n\n  def foreach_Enumerable_1(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    begin\n      enum.each do |value|\n        yield value\n      end\n    rescue StopIteration\n    end\n    # produces the receiver\n    enumerable\n  end\n\n  def foreach_Enumerable_2(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.each { |entry| yield(*entry) }\n    else\n      begin\n        enum.each_with_index do |value, index|\n          yield(index, value)\n        end\n      rescue StopIteration\n      end\n    end\n    # produces the receiver\n    enumerable\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/emerg.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `emerg`.\nPuppet::Functions.create_function(:emerg, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :emerg do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def emerg(scope, *values)\n    Puppet::Util::Log.log_func(scope, :emerg, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/empty.rb",
    "content": "# frozen_string_literal: true\n\n# Returns `true` if the given argument is an empty collection of values.\n#\n# This function can answer if one of the following is empty:\n# * `Array`, `Hash` - having zero entries\n# * `String`, `Binary` - having zero length\n#\n# For backwards compatibility with the stdlib function with the same name the\n# following data types are also accepted by the function instead of raising an error.\n# Using these is deprecated and will raise a warning:\n#\n# * `Numeric` - `false` is returned for all `Numeric` values.\n# * `Undef` - `true` is returned for all `Undef` values.\n#\n# @example Using `empty`\n#\n# ```puppet\n# notice([].empty)\n# notice(empty([]))\n# # would both notice 'true'\n# ```\n#\n# @since Puppet 5.5.0 - support for Binary\n#\nPuppet::Functions.create_function(:empty) do\n  dispatch :collection_empty do\n    param 'Collection', :coll\n  end\n\n  dispatch :sensitive_string_empty do\n    param 'Sensitive[String]', :str\n  end\n\n  dispatch :string_empty do\n    param 'String', :str\n  end\n\n  dispatch :numeric_empty do\n    param 'Numeric', :num\n  end\n\n  dispatch :binary_empty do\n    param 'Binary', :bin\n  end\n\n  dispatch :undef_empty do\n    param 'Undef', :x\n  end\n\n  def collection_empty(coll)\n    coll.empty?\n  end\n\n  def sensitive_string_empty(str)\n    str.unwrap.empty?\n  end\n\n  def string_empty(str)\n    str.empty?\n  end\n\n  # For compatibility reasons - return false rather than error on floats and integers\n  # (Yes, it is strange)\n  #\n  def numeric_empty(num)\n    deprecation_warning_for('Numeric')\n    false\n  end\n\n  def binary_empty(bin)\n    bin.length == 0\n  end\n\n  # For compatibility reasons - return true rather than error on undef\n  # (Yes, it is strange, but undef was passed as empty string in 3.x API)\n  #\n  def undef_empty(x)\n    true\n  end\n\n  def deprecation_warning_for(arg_type)\n    file, line = Puppet::Pops::PuppetStack.top_of_stack\n    msg = _(\"Calling function empty() with %{arg_type} value is deprecated.\") % { arg_type: arg_type }\n    Puppet.warn_once('deprecations', \"empty-from-#{file}-#{line}\", msg, file, line)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/epp.rb",
    "content": "# frozen_string_literal: true\n\n# Evaluates an Embedded Puppet (EPP) template file and returns the rendered text\n# result as a String.\n#\n# `epp('<MODULE NAME>/<TEMPLATE FILE>', <PARAMETER HASH>)`\n#\n# The first argument to this function should be a `<MODULE NAME>/<TEMPLATE FILE>`\n# reference, which loads `<TEMPLATE FILE>` from `<MODULE NAME>`'s `templates`\n# directory. In most cases, the last argument is optional; if used, it should be a\n# [hash](https://puppet.com/docs/puppet/latest/lang_data_hash.html) that contains parameters to\n# pass to the template.\n#\n# - See the [template](https://puppet.com/docs/puppet/latest/lang_template.html)\n# documentation for general template usage information.\n# - See the [EPP syntax](https://puppet.com/docs/puppet/latest/lang_template_epp.html)\n# documentation for examples of EPP.\n#\n# For example, to call the apache module's `templates/vhost/_docroot.epp`\n# template and pass the `docroot` and `virtual_docroot` parameters, call the `epp`\n# function like this:\n#\n# `epp('apache/vhost/_docroot.epp', { 'docroot' => '/var/www/html',\n# 'virtual_docroot' => '/var/www/example' })`\n#\n# This function can also accept an absolute path, which can load a template file\n# from anywhere on disk.\n#\n# Puppet produces a syntax error if you pass more parameters than are declared in\n# the template's parameter tag. When passing parameters to a template that\n# contains a parameter tag, use the same names as the tag's declared parameters.\n#\n# Parameters are required only if they are declared in the called template's\n# parameter tag without default values. Puppet produces an error if the `epp`\n# function fails to pass any required parameter.\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:epp, Puppet::Functions::InternalFunction) do\n  dispatch :epp do\n    scope_param\n    param 'String', :path\n    optional_param 'Hash[Pattern[/^\\w+$/], Any]', :parameters\n    return_type 'Variant[String, Sensitive[String]]'\n  end\n\n  def epp(scope, path, parameters = nil)\n    Puppet::Pops::Evaluator::EppEvaluator.epp(scope, path, scope.compiler.environment, parameters)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/err.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `err`.\nPuppet::Functions.create_function(:err, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :err do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def err(scope, *values)\n    Puppet::Util::Log.log_func(scope, :err, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/eyaml_lookup_key.rb",
    "content": "# frozen_string_literal: true\n\n# The `eyaml_lookup_key` is a hiera 5 `lookup_key` data provider function.\n# See [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-hiera-eyaml) for\n# how to use this function.\n#\n# @since 5.0.0\n#\nPuppet::Functions.create_function(:eyaml_lookup_key) do\n  unless Puppet.features.hiera_eyaml?\n    raise Puppet::DataBinding::LookupError, 'Lookup using eyaml lookup_key function is only supported when the hiera_eyaml library is present'\n  end\n\n  require 'hiera/backend/eyaml/encryptor'\n  require 'hiera/backend/eyaml/utils'\n  require 'hiera/backend/eyaml/options'\n  require 'hiera/backend/eyaml/parser/parser'\n\n  dispatch :eyaml_lookup_key do\n    param 'String[1]', :key\n    param 'Hash[String[1],Any]', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  def eyaml_lookup_key(key, options, context)\n    return context.cached_value(key) if context.cache_has_key(key)\n\n    # Can't do this with an argument_mismatch dispatcher since there is no way to declare a struct that at least\n    # contains some keys but may contain other arbitrary keys.\n    unless options.include?('path')\n      # TRANSLATORS 'eyaml_lookup_key':, 'path', 'paths' 'glob', 'globs', 'mapped_paths', and lookup_key should not be translated\n      raise ArgumentError,\n            _(\"'eyaml_lookup_key': one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml\"\\\n              \" when using this lookup_key function\")\n    end\n\n    # nil key is used to indicate that the cache contains the raw content of the eyaml file\n    raw_data = context.cached_value(nil)\n    if raw_data.nil?\n      raw_data = load_data_hash(options, context)\n      context.cache(nil, raw_data)\n    end\n    context.not_found unless raw_data.include?(key)\n    context.cache(key, decrypt_value(raw_data[key], context, options, key))\n  end\n\n  def load_data_hash(options, context)\n    path = options['path']\n    context.cached_file_data(path) do |content|\n      data = Puppet::Util::Yaml.safe_load(content, [Symbol], path)\n      if data.is_a?(Hash)\n        Puppet::Pops::Lookup::HieraConfig.symkeys_to_string(data)\n      else\n        msg = _(\"%{path}: file does not contain a valid yaml hash\") % { path: path }\n        raise Puppet::DataBinding::LookupError, msg if Puppet[:strict] == :error && data != false\n\n        Puppet.warning(msg)\n        {}\n      end\n    rescue Puppet::Util::Yaml::YamlLoadError => ex\n      # YamlLoadErrors include the absolute path to the file, so no need to add that\n      raise Puppet::DataBinding::LookupError, _(\"Unable to parse %{message}\") % { message: ex.message }\n    end\n  end\n\n  def decrypt_value(value, context, options, key)\n    case value\n    when String\n      decrypt(value, context, options, key)\n    when Hash\n      result = {}\n      value.each_pair { |k, v| result[context.interpolate(k)] = decrypt_value(v, context, options, key) }\n      result\n    when Array\n      value.map { |v| decrypt_value(v, context, options, key) }\n    else\n      value\n    end\n  end\n\n  def decrypt(data, context, options, key)\n    if encrypted?(data)\n      # Options must be set prior to each call to #parse since they end up as static variables in\n      # the Options class. They cannot be set once before #decrypt_value is called, since each #decrypt\n      # might cause a new lookup through interpolation. That lookup in turn, might use a different eyaml\n      # config.\n      #\n      Hiera::Backend::Eyaml::Options.set(options)\n      begin\n        tokens = Hiera::Backend::Eyaml::Parser::ParserFactory.hiera_backend_parser.parse(data)\n        data = tokens.map(&:to_plain_text).join.chomp\n      rescue StandardError => ex\n        raise Puppet::DataBinding::LookupError,\n              _(\"hiera-eyaml backend error decrypting %{data} when looking up %{key} in %{path}. Error was %{message}\") % { data: data, key: key, path: options['path'], message: ex.message }\n      end\n    end\n    context.interpolate(data)\n  end\n\n  def encrypted?(data)\n    /.*ENC\\[.*?\\]/ =~ data ? true : false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/filter.rb",
    "content": "# frozen_string_literal: true\n\n# Applies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# to every value in a data structure and returns an array or hash containing any elements\n# for which the lambda evaluates to a truthy value (not `false` or `undef`).\n#\n# This function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It can\n# request one or two parameters.\n#\n# @example Using the `filter` function\n#\n# `$filtered_data = $data.filter |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `$filtered_data = filter($data) |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# When the first argument (`$data` in the above example) is an array, Puppet passes each\n# value in turn to the lambda and returns an array containing the results.\n#\n# @example Using the `filter` function with an array and a one-parameter lambda\n#\n# ```puppet\n# # For the array $data, return an array containing the values that end with \"berry\"\n# $data = [\"orange\", \"blueberry\", \"raspberry\"]\n# $filtered_data = $data.filter |$items| { $items =~ /berry$/ }\n# # $filtered_data = [blueberry, raspberry]\n# ```\n#\n# When the first argument is a hash, Puppet passes each key and value pair to the lambda\n# as an array in the form `[key, value]` and returns a hash containing the results.\n#\n# @example Using the `filter` function with a hash and a one-parameter lambda\n#\n# ```puppet\n# # For the hash $data, return a hash containing all values of keys that end with \"berry\"\n# $data = { \"orange\" => 0, \"blueberry\" => 1, \"raspberry\" => 2 }\n# $filtered_data = $data.filter |$items| { $items[0] =~ /berry$/ }\n# # $filtered_data = {blueberry => 1, raspberry => 2}\n# ```\n#\n# When the first argument is an array and the lambda has two parameters, Puppet passes the\n# array's indexes (enumerated from 0) in the first parameter and its values in the second\n# parameter.\n#\n# @example Using the `filter` function with an array and a two-parameter lambda\n#\n# ```puppet\n# # For the array $data, return an array of all keys that both end with \"berry\" and have\n# # an even-numbered index\n# $data = [\"orange\", \"blueberry\", \"raspberry\"]\n# $filtered_data = $data.filter |$indexes, $values| { $indexes % 2 == 0 and $values =~ /berry$/ }\n# # $filtered_data = [raspberry]\n# ```\n#\n# When the first argument is a hash, Puppet passes its keys to the first parameter and its\n# values to the second parameter.\n#\n# @example Using the `filter` function with a hash and a two-parameter lambda\n#\n# ```puppet\n# # For the hash $data, return a hash of all keys that both end with \"berry\" and have\n# # values less than or equal to 1\n# $data = { \"orange\" => 0, \"blueberry\" => 1, \"raspberry\" => 2 }\n# $filtered_data = $data.filter |$keys, $values| { $keys =~ /berry$/ and $values <= 1 }\n# # $filtered_data = {blueberry => 1}\n# ```\n#\n# @since 4.0.0\n# @since 6.0.0 does not filter if truthy value is returned from block\n#\nPuppet::Functions.create_function(:filter) do\n  dispatch :filter_Hash_2 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :filter_Hash_1 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :filter_Enumerable_2 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :filter_Enumerable_1 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[1,1]', :block\n  end\n\n  def filter_Hash_1(hash)\n    result = hash.select { |x, y| yield([x, y]) }\n    # Ruby 1.8.7 returns Array\n    result = result.to_h unless result.is_a? Hash\n    result\n  end\n\n  def filter_Hash_2(hash)\n    result = hash.select { |x, y| yield(x, y) }\n    # Ruby 1.8.7 returns Array\n    result = result.to_h unless result.is_a? Hash\n    result\n  end\n\n  def filter_Enumerable_1(enumerable)\n    result = []\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    begin\n      enum.each do |value|\n        result << value if yield(value)\n      end\n    rescue StopIteration\n    end\n    result\n  end\n\n  def filter_Enumerable_2(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      result = {}\n      enum.each { |k, v| result[k] = v if yield(k, v) }\n    else\n      result = []\n      begin\n        enum.each_with_index do |value, index|\n          result << value if yield(index, value)\n        end\n      rescue StopIteration\n      end\n    end\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/find_file.rb",
    "content": "# frozen_string_literal: true\n\n# Finds an existing file from a module and returns its path.\n#\n# This function accepts an argument that is a String as a `<MODULE NAME>/<FILE>`\n# reference, which searches for `<FILE>` relative to a module's `files`\n# directory. (For example, the reference `mysql/mysqltuner.pl` will search for the\n# file `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n#\n# If this function is run via puppet agent, it checks for file existence on the\n# Puppet Primary server. If run via puppet apply, it checks on the local host.\n# In both cases, the check is performed before any resources are changed.\n#\n# This function can also accept:\n#\n# * An absolute String path, which checks for the existence of a file from anywhere on disk.\n# * Multiple String arguments, which returns the path of the **first** file\n#   found, skipping nonexistent files.\n# * An array of string paths, which returns the path of the **first** file\n#   found from the given paths in the array, skipping nonexistent files.\n#\n# The function returns `undef` if none of the given paths were found.\n#\n# @since 4.8.0\n#\nPuppet::Functions.create_function(:find_file, Puppet::Functions::InternalFunction) do\n  dispatch :find_file do\n    scope_param\n    repeated_param 'String', :paths\n  end\n\n  dispatch :find_file_array do\n    scope_param\n    repeated_param 'Array[String]', :paths_array\n  end\n\n  def find_file_array(scope, array)\n    find_file(scope, *array)\n  end\n\n  def find_file(scope, *args)\n    args.each do |file|\n      found = Puppet::Parser::Files.find_file(file, scope.compiler.environment)\n      if found && Puppet::FileSystem.exist?(found)\n        return found\n      end\n    end\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/find_template.rb",
    "content": "# frozen_string_literal: true\n\n# Finds an existing template from a module and returns its path.\n#\n# This function accepts an argument that is a String as a `<MODULE NAME>/<TEMPLATE>`\n# reference, which searches for `<TEMPLATE>` relative to a module's `templates`\n# directory on the primary server. (For example, the reference `mymod/secret.conf.epp`\n# will search for the file `<MODULES DIRECTORY>/mymod/templates/secret.conf.epp`.)\n#\n# The primary use case is for agent-side template rendering with late-bound variables\n# resolved, such as from secret stores inaccessible to the primary server, such as\n#\n# ```\n# $variables = {\n#   'password' => Deferred('vault_lookup::lookup',\n#                   ['secret/mymod', 'https://vault.example.com:8200']),\n# }\n#\n# # compile the template source into the catalog\n# file { '/etc/secrets.conf':\n#   ensure  => file,\n#   content => Deferred('inline_epp',\n#                [find_template('mymod/secret.conf.epp').file, $variables]),\n# }\n# ```\n#\n#\n#\n# This function can also accept:\n#\n# * An absolute String path, which checks for the existence of a template from anywhere on disk.\n# * Multiple String arguments, which returns the path of the **first** template\n#   found, skipping nonexistent files.\n# * An array of string paths, which returns the path of the **first** template\n#   found from the given paths in the array, skipping nonexistent files.\n#\n# The function returns `undef` if none of the given paths were found.\n#\n# @since 6.x\n#\nPuppet::Functions.create_function(:find_template, Puppet::Functions::InternalFunction) do\n  dispatch :find_template do\n    scope_param\n    repeated_param 'String', :paths\n  end\n\n  dispatch :find_template_array do\n    scope_param\n    repeated_param 'Array[String]', :paths_array\n  end\n\n  def find_template_array(scope, array)\n    find_template(scope, *array)\n  end\n\n  def find_template(scope, *args)\n    args.each do |file|\n      found = Puppet::Parser::Files.find_template(file, scope.compiler.environment)\n      if found && Puppet::FileSystem.exist?(found)\n        return found\n      end\n    end\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/flatten.rb",
    "content": "# frozen_string_literal: true\n\n# Returns a flat Array produced from its possibly deeply nested given arguments.\n#\n# One or more arguments of any data type can be given to this function.\n# The result is always a flat array representation where any nested arrays are recursively flattened.\n#\n# @example Typical use of `flatten()`\n#\n# ```puppet\n# flatten(['a', ['b', ['c']]])\n# # Would return: ['a','b','c']\n# ```\n#\n# To flatten other kinds of iterables (for example hashes, or intermediate results like from a `reverse_each`)\n# first convert the result to an array using `Array($x)`, or `$x.convert_to(Array)`. See the `new` function\n# for details and options when performing a conversion.\n#\n# @example Flattening a Hash\n#\n# ```puppet\n# $hsh = { a => 1, b => 2}\n#\n# # -- without conversion\n# $hsh.flatten()\n# # Would return [{a => 1, b => 2}]\n#\n# # -- with conversion\n# $hsh.convert_to(Array).flatten()\n# # Would return [a,1,b,2]\n#\n# flatten(Array($hsh))\n# # Would also return [a,1,b,2]\n# ```\n#\n# @example Flattening and concatenating at the same time\n#\n# ```puppet\n# $a1 = [1, [2, 3]]\n# $a2 = [[4,[5,6]]\n# $x = 7\n# flatten($a1, $a2, $x)\n# # would return [1,2,3,4,5,6,7]\n# ```\n#\n# @example Transforming to Array if not already an Array\n#\n# ```puppet\n# flatten(42)\n# # Would return [42]\n#\n# flatten([42])\n# # Would also return [42]\n# ```\n#\n# @since 5.5.0 support for flattening and concatenating at the same time\n#\nPuppet::Functions.create_function(:flatten) do\n  dispatch :flatten_args do\n    repeated_param 'Any', :args\n  end\n\n  def flatten_args(*args)\n    args.flatten()\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/floor.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the largest `Integer` less or equal to the argument.\n# Takes a single numeric value as an argument.\n#\n# This function is backwards compatible with the same function in stdlib\n# and accepts a `Numeric` value. A `String` that can be converted\n# to a floating point number can also be used in this version - but this\n# is deprecated.\n#\n# In general convert string input to `Numeric` before calling this function\n# to have full control over how the conversion is done.\n#\nPuppet::Functions.create_function(:floor) do\n  dispatch :on_numeric do\n    param 'Numeric', :val\n  end\n\n  dispatch :on_string do\n    param 'String', :val\n  end\n\n  def on_numeric(x)\n    x.floor\n  end\n\n  def on_string(x)\n    Puppet.warn_once('deprecations', 'floor_function_numeric_coerce_string',\n                     _(\"The floor() function's auto conversion of String to Float is deprecated - change to convert input before calling\"))\n\n    begin\n      Float(x).floor\n    rescue TypeError, ArgumentError => _e\n      # TRANSLATORS: 'floor' is a name and should not be translated\n      raise(ArgumentError, _('floor(): cannot convert given value to a floating point value.'))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/get.rb",
    "content": "# frozen_string_literal: true\n\n# Digs into a value with dot notation to get a value from within a structure.\n#\n# **To dig into a given value**, call the function with (at least) two arguments:\n#\n# * The **first** argument must be an Array, or Hash. Value can also be `undef`\n#   (which also makes the result `undef` unless a _default value_ is given).\n# * The **second** argument must be a _dot notation navigation string_.\n# * The **optional third** argument can be any type of value and it is used\n#   as the _default value_ if the function would otherwise return `undef`.\n# * An **optional lambda** for error handling taking one `Error` argument.\n#\n# **Dot notation navigation string** -\n# The dot string consists of period `.` separated segments where each\n# segment is either the index into an array or the value of a hash key.\n# If a wanted key contains a period it must be quoted to avoid it being\n# taken as a segment separator. Quoting can be done with either\n# single quotes `'` or double quotes `\"`. If a segment is\n# a decimal number it is converted to an Integer index. This conversion\n# can be prevented by quoting the value.\n#\n# @example Navigating into a value\n# ```puppet\n# #get($facts, 'os.family')\n# $facts.get('os.family')\n# ```\n# Would both result in the value of `$facts['os']['family']`\n#\n# @example Getting the value from an expression\n# ```puppet\n# get([1,2,[{'name' =>'waldo'}]], '2.0.name')\n# ```\n# Would result in `'waldo'`\n#\n# @example Using a default value\n# ```puppet\n# get([1,2,[{'name' =>'waldo'}]], '2.1.name', 'not waldo')\n#\n# ```\n# Would result in `'not waldo'`\n#\n# @example Quoting a key with period\n# ```puppet\n# $x = [1, 2, { 'readme.md' => \"This is a readme.\"}]\n# $x.get('2.\"readme.md\"')\n# ```\n#\n# @example Quoting a numeric string\n# ```puppet\n# $x = [1, 2, { '10' => \"ten\"}]\n# $x.get('2.\"0\"')\n# ```\n#\n# **Error Handling** - There are two types of common errors that can\n# be handled by giving the function a code block to execute.\n# (A third kind or error; when the navigation string has syntax errors\n# (for example an empty segment or unbalanced quotes) will always raise\n# an error).\n#\n# The given block will be given an instance of the `Error` data type,\n# and it has methods to extract `msg`, `issue_code`, `kind`, and\n# `details`.\n#\n# The `msg` will be a preformatted message describing the error.\n# This is the error message that would have surfaced if there was\n# no block to handle the error.\n#\n# The `kind` is the string `'SLICE_ERROR'` for both kinds of errors,\n# and the `issue_code` is either the string `'EXPECTED_INTEGER_INDEX'`\n# for an attempt to index into an array with a String,\n# or `'EXPECTED_COLLECTION'` for an attempt to index into something that\n# is not a Collection.\n#\n# The `details` is a Hash that for both issue codes contain the\n# entry `'walked_path'` which is an Array with each key in the\n# progression of the dig up to the place where the error occurred.\n#\n# For an `EXPECTED_INTEGER_INDEX`-issue the detail `'index_type'` is\n# set to the data type of the index value and for an\n# `'EXPECTED_COLLECTION'`-issue the detail `'value_type'` is set\n# to the type of the value.\n#\n# The logic in the error handling block can inspect the details,\n# and either call `fail()` with a custom error message or produce\n# the wanted value.\n#\n# If the block produces `undef` it will not be replaced with a\n# given default value.\n#\n# @example Ensure `undef` result on error\n# ```puppet\n# $x = 'blue'\n# $x.get('0.color', 'green') |$error| { undef } # result is undef\n#\n# $y = ['blue']\n# $y.get('color', 'green') |$error| { undef } # result is undef\n# ```\n#\n# @example Accessing information in the Error\n# ```puppet\n# $x = [1, 2, ['blue']]\n# $x.get('2.color') |$error| {\n#   notice(\"Walked path is ${error.details['walked_path']}\")\n# }\n# ```\n# Would notice `Walked path is [2, color]`\n#\n# Also see:\n# * `getvar()` that takes the first segment to be the name of a variable\n#   and then delegates to this function.\n# * `dig()` function which is similar but uses an\n#   array of navigation values instead of a dot notation string.\n#\n# @since 6.0.0\n#\nPuppet::Functions.create_function(:get, Puppet::Functions::InternalFunction) do\n  dispatch :get_from_value do\n    param 'Any', :value\n    param 'String', :dotted_string\n    optional_param 'Any', :default_value\n    optional_block_param 'Callable[1,1]', :block\n  end\n\n  # Gets a result from given value and a navigation string\n  #\n  def get_from_value(value, navigation, default_value = nil, &block)\n    return default_value if value.nil?\n    return value if navigation.empty?\n\n    # Note: split_key always processes the initial segment as a string even if it could be an integer.\n    # This since it is designed for lookup keys. For a numeric first segment\n    # like '0.1' the wanted result is `[0,1]`, not `[\"0\", 1]`. The workaround here is to\n    # prefix the navigation with `\"x.\"` thus giving split_key a first segment that is a string.\n    # The fake segment is then dropped.\n    segments = split_key(\"x.\" + navigation) { |_err| _(\"Syntax error in dotted-navigation string\") }\n    segments.shift\n\n    begin\n      result = call_function('dig', value, *segments)\n      result.nil? ? default_value : result\n    rescue Puppet::ErrorWithData => e\n      if block_given?\n        yield(e.error_data)\n      else\n        raise e\n      end\n    end\n  end\n  # reuse the split_key parser used also by lookup\n  include Puppet::Pops::Lookup::SubLookup\nend\n"
  },
  {
    "path": "lib/puppet/functions/getvar.rb",
    "content": "# frozen_string_literal: true\n\n# Digs into a variable with dot notation to get a value from a structure.\n#\n# **To get the value from a variable** (that may or may not exist), call the function with\n# one or two arguments:\n#\n# * The **first** argument must be a string, and must start with a variable name without leading `$`,\n#   for example `get('facts')`. The variable name can be followed\n#   by a _dot notation navigation string_ to dig out a value in the array or hash value\n#   of the variable.\n# * The **optional second** argument can be any type of value and it is used as the\n#   _default value_ if the function would otherwise return `undef`.\n# * An **optional lambda** for error handling taking one `Error` argument.\n#\n# **Dot notation navigation string** -\n# The dot string consists of period `.` separated segments where each\n# segment is either the index into an array or the value of a hash key.\n# If a wanted key contains a period it must be quoted to avoid it being\n# taken as a segment separator. Quoting can be done with either\n# single quotes `'` or double quotes `\"`. If a segment is\n# a decimal number it is converted to an Integer index. This conversion\n# can be prevented by quoting the value.\n#\n# @example Getting the value of a variable\n# ```puppet\n# getvar('facts') # results in the value of $facts\n# ```\n#\n# @example Navigating into a variable\n# ```puppet\n# getvar('facts.os.family') # results in the value of $facts['os']['family']\n# ```\n#\n# @example Using a default value\n# ```puppet\n# $x = [1,2,[{'name' =>'waldo'}]]\n# getvar('x.2.1.name', 'not waldo')\n# # results in 'not waldo'\n# ```\n#\n# For further examples and how to perform error handling, see the `get()` function\n# which this function delegates to after having resolved the variable value.\n#\n# @since 6.0.0 - the ability to dig into the variable's value with dot notation.\n#\nPuppet::Functions.create_function(:getvar, Puppet::Functions::InternalFunction) do\n  dispatch :get_from_navigation do\n    scope_param\n    param 'Pattern[/\\A(?:::)?(?:[a-z]\\w*::)*[a-z_]\\w*(?:\\.|\\Z)/]', :get_string\n    optional_param 'Any', :default_value\n    optional_block_param 'Callable[1,1]', :block\n  end\n\n  argument_mismatch :invalid_variable_error do\n    param 'String', :get_string\n    optional_param 'Any', :default_value\n    optional_block_param 'Callable', :block\n  end\n\n  def invalid_variable_error(navigation, default_value = nil, &block)\n    _(\"The given string does not start with a valid variable name\")\n  end\n\n  # Gets a result from a navigation string starting with $var\n  #\n  def get_from_navigation(scope, navigation, default_value = nil, &block)\n    # asserted to start with a valid variable name - dig out the variable\n    matches = navigation.match(/^((::)?(\\w+::)*\\w+)(.*)\\z/)\n    navigation = matches[4]\n    if navigation[0] == '.'\n      navigation = navigation[1..]\n    else\n      unless navigation.empty?\n        raise ArgumentError, _(\"First character after var name in get string must be a '.' - got %{char}\") % { char: navigation[0] }\n      end\n    end\n    get_from_var_name(scope, matches[1], navigation, default_value, &block)\n  end\n\n  # Gets a result from a $var name and a navigation string\n  #\n  def get_from_var_name(scope, var_string, navigation, default_value = nil, &block)\n    catch(:undefined_variable) do\n      return call_function_with_scope(scope, 'get', scope.lookupvar(var_string), navigation, default_value, &block)\n    end\n    default_value\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/group_by.rb",
    "content": "# frozen_string_literal: true\n\n# Groups the collection by result of the block. Returns a hash where the keys are the evaluated result from the block\n# and the values are arrays of elements in the collection that correspond to the key.\nPuppet::Functions.create_function(:group_by) do\n  # @param collection A collection of things to group.\n  # @example Group array of strings by length, results in e.g. `{ 1 => [a, b], 2 => [ab] }`\n  #   ```puppet\n  #   [a, b, ab].group_by |$s| { $s.length }\n  #   ```\n  # @example Group array of strings by length and index, results in e.g. `{1 => ['a'], 2 => ['b', 'ab']}`\n  #   ```puppet\n  #   [a, b, ab].group_by |$i, $s| { $i%2 + $s.length }\n  #   ```\n  # @example Group hash iterating by key-value pair, results in e.g. `{ 2 => [['a', [1, 2]]], 1 => [['b', [1]]] }`\n  #   ```puppet\n  #   { a => [1, 2], b => [1] }.group_by |$kv| { $kv[1].length }\n  #   ```\n  # @example Group hash iterating by key and value, results in e.g. `{ 2 => [['a', [1, 2]]], 1 => [['b', [1]]] }`\n  #   ```puppet\n  #    { a => [1, 2], b => [1] }.group_by |$k, $v| { $v.length }\n  #   ```\n  dispatch :group_by_1 do\n    required_param 'Collection', :collection\n    block_param 'Callable[1,1]', :block\n    return_type 'Hash'\n  end\n\n  dispatch :group_by_2a do\n    required_param 'Array', :array\n    block_param 'Callable[2,2]', :block\n    return_type 'Hash'\n  end\n\n  dispatch :group_by_2 do\n    required_param 'Collection', :collection\n    block_param 'Callable[2,2]', :block\n    return_type 'Hash'\n  end\n\n  def group_by_1(collection)\n    collection.group_by do |item|\n      yield(item)\n    end.freeze\n  end\n\n  def group_by_2a(array)\n    grouped = array.size.times.zip(array).group_by do |k, v|\n      yield(k, v)\n    end\n\n    grouped.transform_values do |v|\n      v.map { |item| item[1] }\n    end.freeze\n  end\n\n  def group_by_2(collection)\n    collection.group_by do |k, v|\n      yield(k, v)\n    end.freeze\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/hiera.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera/puppet_function'\n\n# Performs a standard priority lookup of the hierarchy and returns the most specific value\n# for a given key. The returned value can be any type of data.\n#\n# This function is deprecated in favor of the `lookup` function. While this function\n# continues to work, it does **not** support:\n# * `lookup_options` stored in the data\n# * lookup across global, environment, and module layers\n#\n# The function takes up to three arguments, in this order:\n#\n# 1. A string key that Hiera searches for in the hierarchy. **Required**.\n# 2. An optional default value to return if Hiera doesn't find anything matching the key.\n#     * If this argument isn't provided and this function results in a lookup failure, Puppet\n#     fails with a compilation error.\n# 3. The optional name of an arbitrary\n# [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n# top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n#     * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n#     searching the rest of the hierarchy.\n#\n# The `hiera` function does **not** find all matches throughout a hierarchy, instead\n# returning the first specific value starting at the top of the hierarchy. To search\n# throughout a hierarchy, use the `hiera_array` or `hiera_hash` functions.\n#\n# @example Using `hiera`\n#\n# ```yaml\n# # Assuming hiera.yaml\n# # :hierarchy:\n# #   - web01.example.com\n# #   - common\n#\n# # Assuming web01.example.com.yaml:\n# # users:\n# #   - \"Amy Barry\"\n# #   - \"Carrie Douglas\"\n#\n# # Assuming common.yaml:\n# users:\n#   admins:\n#     - \"Edith Franklin\"\n#     - \"Ginny Hamilton\"\n#   regular:\n#     - \"Iris Jackson\"\n#     - \"Kelly Lambert\"\n# ```\n#\n# ```puppet\n# # Assuming we are not web01.example.com:\n#\n# $users = hiera('users', undef)\n#\n# # $users contains {admins  => [\"Edith Franklin\", \"Ginny Hamilton\"],\n# #                  regular => [\"Iris Jackson\", \"Kelly Lambert\"]}\n# ```\n#\n# You can optionally generate the default value with a\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n# takes one parameter.\n#\n# @example Using `hiera` with a lambda\n#\n# ```puppet\n# # Assuming the same Hiera data as the previous example:\n#\n# $users = hiera('users') | $key | { \"Key \\'${key}\\' not found\" }\n#\n# # $users contains {admins  => [\"Edith Franklin\", \"Ginny Hamilton\"],\n# #                  regular => [\"Iris Jackson\", \"Kelly Lambert\"]}\n# # If hiera couldn't match its key, it would return the lambda result,\n# # \"Key 'users' not found\".\n# ```\n#\n# The returned value's data type depends on the types of the results. In the example\n# above, Hiera matches the 'users' key and returns it as a hash.\n#\n# See\n# [the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\n# Also see\n# [the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\n# for more information about the Hiera 3 functions.\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:hiera, Hiera::PuppetFunction) do\n  init_dispatch\nend\n"
  },
  {
    "path": "lib/puppet/functions/hiera_array.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera/puppet_function'\n\n# Finds all matches of a key throughout the hierarchy and returns them as a single flattened\n# array of unique values. If any of the matched values are arrays, they're flattened and\n# included in the results. This is called an\n# [array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge).\n#\n# This function is deprecated in favor of the `lookup` function. While this function\n# continues to work, it does **not** support:\n# * `lookup_options` stored in the data\n# * lookup across global, environment, and module layers\n#\n# The `hiera_array` function takes up to three arguments, in this order:\n#\n# 1. A string key that Hiera searches for in the hierarchy. **Required**.\n# 2. An optional default value to return if Hiera doesn't find anything matching the key.\n#     * If this argument isn't provided and this function results in a lookup failure, Puppet\n#     fails with a compilation error.\n# 3. The optional name of an arbitrary\n# [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n# top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n#     * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n#     searching the rest of the hierarchy.\n#\n# @example Using `hiera_array`\n#\n# ```yaml\n# # Assuming hiera.yaml\n# # :hierarchy:\n# #   - web01.example.com\n# #   - common\n#\n# # Assuming common.yaml:\n# # users:\n# #   - 'cdouglas = regular'\n# #   - 'efranklin = regular'\n#\n# # Assuming web01.example.com.yaml:\n# # users: 'abarry = admin'\n# ```\n#\n# ```puppet\n# $allusers = hiera_array('users', undef)\n#\n# # $allusers contains [\"cdouglas = regular\", \"efranklin = regular\", \"abarry = admin\"].\n# ```\n#\n# You can optionally generate the default value with a\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n# takes one parameter.\n#\n# @example Using `hiera_array` with a lambda\n#\n# ```puppet\n# # Assuming the same Hiera data as the previous example:\n#\n# $allusers = hiera_array('users') | $key | { \"Key \\'${key}\\' not found\" }\n#\n# # $allusers contains [\"cdouglas = regular\", \"efranklin = regular\", \"abarry = admin\"].\n# # If hiera_array couldn't match its key, it would return the lambda result,\n# # \"Key 'users' not found\".\n# ```\n#\n# `hiera_array` expects that all values returned will be strings or arrays. If any matched\n# value is a hash, Puppet raises a type mismatch error.\n#\n# See\n# [the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\n# Also see\n# [the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\n# for more information about the Hiera 3 functions.\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:hiera_array, Hiera::PuppetFunction) do\n  init_dispatch\n\n  def merge_type\n    :unique\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/hiera_hash.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera/puppet_function'\n\n# Finds all matches of a key throughout the hierarchy and returns them in a merged hash.\n#\n# This function is deprecated in favor of the `lookup` function. While this function\n# continues to work, it does **not** support:\n# * `lookup_options` stored in the data\n# * lookup across global, environment, and module layers\n#\n# If any of the matched hashes share keys, the final hash uses the value from the\n# highest priority match. This is called a\n# [hash merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#hash-merge).\n#\n# The merge strategy is determined by Hiera's\n# [`:merge_behavior`](https://puppet.com/docs/hiera/latest/configuring.html#mergebehavior)\n# setting.\n#\n# The `hiera_hash` function takes up to three arguments, in this order:\n#\n# 1. A string key that Hiera searches for in the hierarchy. **Required**.\n# 2. An optional default value to return if Hiera doesn't find anything matching the key.\n#     * If this argument isn't provided and this function results in a lookup failure, Puppet\n#     fails with a compilation error.\n# 3. The optional name of an arbitrary\n# [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n# top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n#     * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n#     searching the rest of the hierarchy.\n#\n# @example Using `hiera_hash`\n#\n# ```yaml\n# # Assuming hiera.yaml\n# # :hierarchy:\n# #   - web01.example.com\n# #   - common\n#\n# # Assuming common.yaml:\n# # users:\n# #   regular:\n# #     'cdouglas': 'Carrie Douglas'\n#\n# # Assuming web01.example.com.yaml:\n# # users:\n# #   administrators:\n# #     'aberry': 'Amy Berry'\n# ```\n#\n# ```puppet\n# # Assuming we are not web01.example.com:\n#\n# $allusers = hiera_hash('users', undef)\n#\n# # $allusers contains {regular => {\"cdouglas\" => \"Carrie Douglas\"},\n# #                     administrators => {\"aberry\" => \"Amy Berry\"}}\n# ```\n#\n# You can optionally generate the default value with a\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n# takes one parameter.\n#\n# @example Using `hiera_hash` with a lambda\n#\n# ```puppet\n# # Assuming the same Hiera data as the previous example:\n#\n# $allusers = hiera_hash('users') | $key | { \"Key \\'${key}\\' not found\" }\n#\n# # $allusers contains {regular => {\"cdouglas\" => \"Carrie Douglas\"},\n# #                     administrators => {\"aberry\" => \"Amy Berry\"}}\n# # If hiera_hash couldn't match its key, it would return the lambda result,\n# # \"Key 'users' not found\".\n# ```\n#\n# `hiera_hash` expects that all values returned will be hashes. If any of the values\n# found in the data sources are strings or arrays, Puppet raises a type mismatch error.\n#\n# See\n# [the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\n# Also see\n# [the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\n# for more information about the Hiera 3 functions.\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:hiera_hash, Hiera::PuppetFunction) do\n  init_dispatch\n\n  def merge_type\n    :hash\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/hiera_include.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera/puppet_function'\n\n# Assigns classes to a node using an\n# [array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge)\n# that retrieves the value for a user-specified key from Hiera's data.\n#\n# This function is deprecated in favor of the `lookup` function in combination with `include`.\n# While this function continues to work, it does **not** support:\n# * `lookup_options` stored in the data\n# * lookup across global, environment, and module layers\n#\n# @example Using `lookup` and `include` instead of of the deprecated `hiera_include`\n#\n# ```puppet\n# # In site.pp, outside of any node definitions and below any top-scope variables:\n# lookup('classes', Array[String], 'unique').include\n# ```\n#\n# The `hiera_include` function requires:\n#\n# - A string key name to use for classes.\n# - A call to this function (i.e. `hiera_include('classes')`) in your environment's\n# `sites.pp` manifest, outside of any node definitions and below any top-scope variables\n# that Hiera uses in lookups.\n# - `classes` keys in the appropriate Hiera data sources, with an array for each\n# `classes` key and each value of the array containing the name of a class.\n#\n# The function takes up to three arguments, in this order:\n#\n# 1. A string key that Hiera searches for in the hierarchy. **Required**.\n# 2. An optional default value to return if Hiera doesn't find anything matching the key.\n#     * If this argument isn't provided and this function results in a lookup failure, Puppet\n#     fails with a compilation error.\n# 3. The optional name of an arbitrary\n# [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n# top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n#     * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n#     searching the rest of the hierarchy.\n#\n# The function uses an\n# [array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge)\n# to retrieve the `classes` array, so every node gets every class from the hierarchy.\n#\n# @example Using `hiera_include`\n#\n# ```yaml\n# # Assuming hiera.yaml\n# # :hierarchy:\n# #   - web01.example.com\n# #   - common\n#\n# # Assuming web01.example.com.yaml:\n# # classes:\n# #   - apache::mod::php\n#\n# # Assuming common.yaml:\n# # classes:\n# #   - apache\n# ```\n#\n# ```puppet\n# # In site.pp, outside of any node definitions and below any top-scope variables:\n# hiera_include('classes', undef)\n#\n# # Puppet assigns the apache and apache::mod::php classes to the web01.example.com node.\n# ```\n#\n# You can optionally generate the default value with a\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n# takes one parameter.\n#\n# @example Using `hiera_include` with a lambda\n#\n# ```puppet\n# # Assuming the same Hiera data as the previous example:\n#\n# # In site.pp, outside of any node definitions and below any top-scope variables:\n# hiera_include('classes') | $key | {\"Key \\'${key}\\' not found\" }\n#\n# # Puppet assigns the apache and apache::mod::php classes to the web01.example.com node.\n# # If hiera_include couldn't match its key, it would return the lambda result,\n# # \"Key 'classes' not found\".\n# ```\n#\n# See\n# [the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\n# Also see\n# [the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\n# for more information about the Hiera 3 functions.\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:hiera_include, Hiera::PuppetFunction) do\n  init_dispatch\n\n  def merge_type\n    :unique\n  end\n\n  def post_lookup(scope, key, value)\n    raise Puppet::ParseError, _(\"Could not find data item %{key}\") % { key: key } if value.nil?\n\n    call_function_with_scope(scope, 'include', value) unless value.empty?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/hocon_data.rb",
    "content": "# frozen_string_literal: true\n\n# The `hocon_data` is a hiera 5 `data_hash` data provider function.\n# See [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-built-in-backends) for\n# how to use this function.\n#\n# Note that this function is not supported without a hocon library being present.\n#\n# @since 4.9.0\n#\nPuppet::Functions.create_function(:hocon_data) do\n  unless Puppet.features.hocon?\n    raise Puppet::DataBinding::LookupError, _('Lookup using Hocon data_hash function is not supported without hocon library')\n  end\n\n  require 'hocon'\n  require 'hocon/config_error'\n\n  dispatch :hocon_data do\n    param 'Struct[{path=>String[1]}]', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  argument_mismatch :missing_path do\n    param 'Hash', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  def hocon_data(options, context)\n    path = options['path']\n    context.cached_file_data(path) do |content|\n      Hocon.parse(content)\n    rescue Hocon::ConfigError => ex\n      raise Puppet::DataBinding::LookupError, _(\"Unable to parse (%{path}): %{message}\") % { path: path, message: ex.message }\n    end\n  end\n\n  def missing_path(options, context)\n    \"one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/import.rb",
    "content": "# frozen_string_literal: true\n\n# The import function raises an error when called to inform the user that import is no longer supported.\n#\nPuppet::Functions.create_function(:import) do\n  def import(*args)\n    raise Puppet::Pops::SemanticError, Puppet::Pops::Issues::DISCONTINUED_IMPORT\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/include.rb",
    "content": "# frozen_string_literal: true\n\n# Declares one or more classes, causing the resources in them to be\n# evaluated and added to the catalog. Accepts a class name, an array of class\n# names, or a comma-separated list of class names.\n#\n# The `include` function can be used multiple times on the same class and will\n# only declare a given class once. If a class declared with `include` has any\n# parameters, Puppet will automatically look up values for them in Hiera, using\n# `<class name>::<parameter name>` as the lookup key.\n#\n# Contrast this behavior with resource-like class declarations\n# (`class {'name': parameter => 'value',}`), which must be used in only one place\n# per class and can directly set parameters. You should avoid using both `include`\n# and resource-like declarations with the same class.\n#\n# The `include` function does not cause classes to be contained in the class\n# where they are declared. For that, see the `contain` function. It also\n# does not create a dependency relationship between the declared class and the\n# surrounding class; for that, see the `require` function.\n#\n# You must use the class's full name;\n# relative names are not allowed. In addition to names in string form,\n# you may also directly use `Class` and `Resource` `Type`-values that are produced by\n# the resource and relationship expressions.\n#\n# - Since < 3.0.0\n# - Since 4.0.0 support for class and resource type values, absolute names\n# - Since 4.7.0 returns an `Array[Type[Class]]` of all included classes\n#\nPuppet::Functions.create_function(:include, Puppet::Functions::InternalFunction) do\n  dispatch :include do\n    scope_param\n    # The function supports what the type system sees as Ruby runtime objects, and\n    # they cannot be parameterized to find what is actually valid instances.\n    # The validation is instead done in the function body itself via a call to\n    # `transform_and_assert_classnames` on the calling scope.\n    required_repeated_param 'Any', :names\n  end\n\n  def include(scope, *classes)\n    if Puppet[:tasks]\n      raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n        Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n        { :operation => 'include' }\n      )\n    end\n\n    classes = scope.transform_and_assert_classnames(classes.flatten)\n    result = classes.map { |name| Puppet::Pops::Types::TypeFactory.host_class(name) }\n    scope.compiler.evaluate_classes(classes, scope, false)\n\n    # Result is an Array[Class, 1, n] which allows chaining other operations\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/index.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the index (or key in a hash) to a first-found value in an `Iterable` value.\n#\n# When called with a  [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# the lambda is called repeatedly using each value in a data structure until the lambda returns a \"truthy\" value which\n# makes the function return the index or key, or if the end of the iteration is reached, undef is returned.\n#\n# This function can be called in two different ways; with a value to be searched for, or with\n# a lambda that determines if an entry in the iterable matches.\n#\n# When called with a lambda the function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, string, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It can request one (value) or two (index/key, value) parameters.\n#\n# @example Using the `index` function\n#\n# `$data.index |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `index($data) |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# @example Using the `index` function with an Array and a one-parameter lambda\n#\n# ```puppet\n# $data = [\"routers\", \"servers\", \"workstations\"]\n# notice $data.index |$value| { $value == 'servers' } # notices 1\n# notice $data.index |$value| { $value == 'hosts'  }  # notices undef\n# ```\n#\n# @example Using the `index` function with a Hash and a one-parameter lambda\n#\n# ```puppet\n# $data = {types => [\"routers\", \"servers\", \"workstations\"], colors => ['red', 'blue', 'green']}\n# notice $data.index |$value| { 'servers' in $value } # notices 'types'\n# notice $data.index |$value| { 'red' in $value }     # notices 'colors'\n# ```\n# Note that the lambda gets the value and not an array with `[key, value]` as in other\n# iterative functions.\n#\n# Using a lambda that accepts two values works the same way. The lambda gets the index/key\n# as the first parameter and the value as the second parameter.\n#\n# @example Using the `index` function with an Array and a two-parameter lambda\n#\n# ```puppet\n# # Find the first even numbered index that has a non String value\n# $data = [key1, 1, 3, 5]\n# notice $data.index |$idx, $value| { $idx % 2 == 0 and $value !~ String } # notices 2\n# ```\n#\n# When called on a `String`, the lambda is given each character as a value. What is typically wanted is to\n# find a sequence of characters which is achieved by calling the function with a value to search for instead\n# of giving a lambda.\n#\n#\n# @example Using the `index` function with a String, search for first occurrence of a sequence of characters\n#\n# ```puppet\n# # Find first occurrence of 'ah'\n# $data = \"blablahbleh\"\n# notice $data.index('ah') # notices 5\n# ```\n#\n# @example Using the `index` function with a String, search for first occurrence of a regular expression\n#\n# ```puppet\n# # Find first occurrence of 'la' or 'le'\n# $data = \"blablahbleh\"\n# notice $data.index(/l(a|e)/ # notices 1\n# ```\n#\n# When searching in a `String` with a given value that is neither `String` nor `Regexp` the answer is always `undef`.\n# When searching in any other iterable, the value is matched against each value in the iteration using strict\n# Ruby `==` semantics. If Puppet Language semantics are wanted (where string compare is case insensitive) use a\n# lambda and the `==` operator in Puppet.\n#\n# @example Using the `index` function to search for a given value in an Array\n#\n# ```puppet\n# $data = ['routers', 'servers', 'WORKstations']\n# notice $data.index('servers')      # notices 1\n# notice $data.index('workstations') # notices undef (not matching case)\n# ```\n#\n# For an general examples that demonstrates iteration, see the Puppet\n# [iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\n# documentation.\n#\n# @since 6.3.0\n#\nPuppet::Functions.create_function(:index) do\n  dispatch :index_Hash_2 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :index_Hash_1 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :index_Enumerable_2 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :index_Enumerable_1 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :string_index do\n    param 'String', :str\n    param 'Variant[String,Regexp]', :match\n  end\n\n  dispatch :index_value do\n    param 'Iterable', :enumerable\n    param 'Any', :match\n  end\n\n  def index_Hash_1(hash)\n    hash.each_pair { |x, y| return x if yield(y) }\n    nil\n  end\n\n  def index_Hash_2(hash)\n    hash.each_pair.any? { |x, y| return x if yield(x, y) }\n    nil\n  end\n\n  def index_Enumerable_1(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.each { |entry| return entry[0] if yield(entry[1]) }\n    else\n      enum.each_with_index { |e, i| return i if yield(e) }\n    end\n    nil\n  end\n\n  def index_Enumerable_2(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.each { |entry| return entry[0] if yield(*entry) }\n    else\n      enum.each_with_index { |e, i| return i if yield(i, e) }\n    end\n    nil\n  end\n\n  def string_index(str, match)\n    str.index(match)\n  end\n\n  def index_value(enumerable, match)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.each { |entry| return entry[0] if entry[1] == match }\n    else\n      enum.each_with_index { |e, i| return i if e == match }\n    end\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/info.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `info`.\nPuppet::Functions.create_function(:info, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :info do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def info(scope, *values)\n    Puppet::Util::Log.log_func(scope, :info, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/inline_epp.rb",
    "content": "# frozen_string_literal: true\n\n# Evaluates an Embedded Puppet (EPP) template string and returns the rendered\n# text result as a String.\n#\n# `inline_epp('<EPP TEMPLATE STRING>', <PARAMETER HASH>)`\n#\n# The first argument to this function should be a string containing an EPP\n# template. In most cases, the last argument is optional; if used, it should be a\n# [hash](https://puppet.com/docs/puppet/latest/lang_data_hash.html) that contains parameters to\n# pass to the template.\n#\n# - See the [template](https://puppet.com/docs/puppet/latest/lang_template.html)\n# documentation for general template usage information.\n# - See the [EPP syntax](https://puppet.com/docs/puppet/latest/lang_template_epp.html)\n# documentation for examples of EPP.\n#\n# For example, to evaluate an inline EPP template and pass it the `docroot` and\n# `virtual_docroot` parameters, call the `inline_epp` function like this:\n#\n# `inline_epp('docroot: <%= $docroot %> Virtual docroot: <%= $virtual_docroot %>',\n# { 'docroot' => '/var/www/html', 'virtual_docroot' => '/var/www/example' })`\n#\n# Puppet produces a syntax error if you pass more parameters than are declared in\n# the template's parameter tag. When passing parameters to a template that\n# contains a parameter tag, use the same names as the tag's declared parameters.\n#\n# Parameters are required only if they are declared in the called template's\n# parameter tag without default values. Puppet produces an error if the\n# `inline_epp` function fails to pass any required parameter.\n#\n# An inline EPP template should be written as a single-quoted string or\n# [heredoc](https://puppet.com/docs/puppet/latest/lang_data_string.html#heredocs).\n# A double-quoted string is subject to expression interpolation before the string\n# is parsed as an EPP template.\n#\n# For example, to evaluate an inline EPP template using a heredoc, call the\n# `inline_epp` function like this:\n#\n# ```puppet\n# # Outputs 'Hello given argument planet!'\n# inline_epp(@(END), { x => 'given argument' })\n# <%- | $x, $y = planet | -%>\n# Hello <%= $x %> <%= $y %>!\n# END\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:inline_epp, Puppet::Functions::InternalFunction) do\n  dispatch :inline_epp do\n    scope_param()\n    param 'String', :template\n    optional_param 'Hash[Pattern[/^\\w+$/], Any]', :parameters\n    return_type 'Variant[String, Sensitive[String]]'\n  end\n\n  def inline_epp(scope, template, parameters = nil)\n    Puppet::Pops::Evaluator::EppEvaluator.inline_epp(scope, template, parameters)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/join.rb",
    "content": "# frozen_string_literal: true\n\n# Joins the values of an Array into a string with elements separated by a delimiter.\n#\n# Supports up to two arguments\n# * **values** - first argument is required and must be an an `Array`\n# * **delimiter** - second arguments is the delimiter between elements, must be a `String` if given, and defaults to an empty string.\n#\n# @example Typical use of `join`\n#\n# ```puppet\n# join(['a','b','c'], \",\")\n# # Would result in: \"a,b,c\"\n# ```\n#\n# Note that array is flattened before elements are joined, but flattening does not extend to arrays nested in hashes or other objects.\n#\n# @example Arrays nested in hashes are not joined\n#\n# ```puppet\n# $a = [1,2, undef, 'hello', [x,y,z], {a => 2, b => [3, 4]}]\n# notice join($a, ', ')\n#\n# # would result in noticing:\n# # 1, 2, , hello, x, y, z, {\"a\"=>2, \"b\"=>[3, 4]}\n# ```\n#\n# For joining iterators and other containers of elements a conversion must first be made to\n# an `Array`. The reason for this is that there are many options how such a conversion should\n# be made.\n#\n# @example Joining the result of a reverse_each converted to an array\n#\n# ```puppet\n# [1,2,3].reverse_each.convert_to(Array).join(', ')\n# # would result in: \"3, 2, 1\"\n# ```\n# @example Joining a hash\n#\n# ```puppet\n# {a => 1, b => 2}.convert_to(Array).join(', ')\n# # would result in \"a, 1, b, 2\"\n# ```\n#\n# For more detailed control over the formatting (including indentations and line breaks, delimiters around arrays\n# and hash entries, between key/values in hash entries, and individual formatting of values in the array)\n# see the `new` function for `String` and its formatting options for `Array` and `Hash`.\n#\nPuppet::Functions.create_function(:join) do\n  dispatch :join do\n    param 'Array', :arg\n    optional_param 'String', :delimiter\n  end\n\n  def join(arg, delimiter = '', puppet_formatting = false)\n    arg.join(delimiter)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/json_data.rb",
    "content": "# frozen_string_literal: true\n\n# The `json_data` is a hiera 5 `data_hash` data provider function.\n# See [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-built-in-backends) for\n# how to use this function.\n#\n# @since 4.8.0\n#\nPuppet::Functions.create_function(:json_data) do\n  dispatch :json_data do\n    param 'Struct[{path=>String[1]}]', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  argument_mismatch :missing_path do\n    param 'Hash', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  def json_data(options, context)\n    path = options['path']\n    context.cached_file_data(path) do |content|\n      Puppet::Util::Json.load(content)\n    rescue Puppet::Util::Json::ParseError => ex\n      # Filename not included in message, so we add it here.\n      raise Puppet::DataBinding::LookupError, \"Unable to parse (%{path}): %{message}\" % { path: path, message: ex.message }\n    end\n  end\n\n  def missing_path(options, context)\n    \"one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/keys.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the keys of a hash as an Array\n#\n# @example Using `keys`\n#\n# ```puppet\n# $hsh = {\"apples\" => 3, \"oranges\" => 4 }\n# $hsh.keys()\n# keys($hsh)\n# # both results in the array [\"apples\", \"oranges\"]\n# ```\n#\n# * Note that a hash in the puppet language accepts any data value (including `undef`) unless\n#   it is constrained with a `Hash` data type that narrows the allowed data types.\n# * For an empty hash, an empty array is returned.\n# * The order of the keys is the same as the order in the hash (typically the order in which they were added).\n#\nPuppet::Functions.create_function(:keys) do\n  dispatch :keys do\n    param 'Hash', :hsh\n  end\n\n  def keys(hsh)\n    hsh.keys\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/length.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the length of an Array, Hash, String, or Binary value.\n#\n# The returned value is a positive integer indicating the number\n# of elements in the container; counting (possibly multibyte) characters for a `String`,\n# bytes in a `Binary`, number of elements in an `Array`, and number of\n# key-value associations in a Hash.\n#\n# @example Using `length`\n#\n# ```puppet\n# \"roses\".length()        # 5\n# length(\"violets\")       # 7\n# [10, 20].length         # 2\n# {a => 1, b => 3}.length # 2\n# ```\n#\n# @since 5.5.0 - also supporting Binary\n#\nPuppet::Functions.create_function(:length) do\n  dispatch :collection_length do\n    param 'Collection', :arg\n  end\n\n  dispatch :string_length do\n    param 'String', :arg\n  end\n\n  dispatch :binary_length do\n    param 'Binary', :arg\n  end\n\n  def collection_length(col)\n    col.size\n  end\n\n  def string_length(s)\n    s.length\n  end\n\n  def binary_length(bin)\n    bin.length\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/lest.rb",
    "content": "# frozen_string_literal: true\n\n# Calls a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# without arguments if the value given to `lest` is `undef`.\n# Returns the result of calling the lambda if the argument is `undef`, otherwise the\n# given argument.\n#\n# The `lest` function is useful in a chain of `then` calls, or in general\n# as a guard against `undef` values. The function can be used to call `fail`, or to\n# return a default value.\n#\n# These two expressions are equivalent:\n#\n# ```puppet\n# if $x == undef { do_things() }\n# lest($x) || { do_things() }\n# ```\n#\n# @example Using the `lest` function\n#\n# ```puppet\n# $data = {a => [ b, c ] }\n# notice $data.dig(a, b, c)\n#  .then |$x| { $x * 2 }\n#  .lest || { fail(\"no value for $data[a][b][c]\" }\n# ```\n#\n# Would fail the operation because `$data[a][b][c]` results in `undef`\n# (there is no `b` key in `a`).\n#\n# In contrast - this example:\n#\n# ```puppet\n# $data = {a => { b => { c => 10 } } }\n# notice $data.dig(a, b, c)\n#  .then |$x| { $x * 2 }\n#  .lest || { fail(\"no value for $data[a][b][c]\" }\n# ```\n#\n# Would notice the value `20`\n#\n# @since 4.5.0\n#\nPuppet::Functions.create_function(:lest) do\n  dispatch :lest do\n    param 'Any', :arg\n    block_param 'Callable[0,0]', :block\n  end\n\n  def lest(arg)\n    if arg.nil?\n      yield()\n    else\n      arg\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/lookup.rb",
    "content": "# frozen_string_literal: true\n\n# Uses the Puppet lookup system to retrieve a value for a given key. By default,\n# this returns the first value found (and fails compilation if no values are\n# available), but you can configure it to merge multiple values into one, fail\n# gracefully, and more.\n#\n# When looking up a key, Puppet will search up to three tiers of data, in the\n# following order:\n#\n# 1. Hiera.\n# 2. The current environment's data provider.\n# 3. The indicated module's data provider, if the key is of the form\n#    `<MODULE NAME>::<SOMETHING>`.\n#\n# #### Arguments\n#\n# You must provide the name of a key to look up, and can optionally provide other\n# arguments. You can combine these arguments in the following ways:\n#\n# * `lookup( <NAME>, [<VALUE TYPE>], [<MERGE BEHAVIOR>], [<DEFAULT VALUE>] )`\n# * `lookup( [<NAME>], <OPTIONS HASH> )`\n# * `lookup( as above ) |$key| { # lambda returns a default value }`\n#\n# Arguments in `[square brackets]` are optional.\n#\n# The arguments accepted by `lookup` are as follows:\n#\n# 1. `<NAME>` (string or array) --- The name of the key to look up.\n#     * This can also be an array of keys. If Puppet doesn't find anything for the\n#     first key, it will try again with the subsequent ones, only resorting to a\n#     default value if none of them succeed.\n# 2. `<VALUE TYPE>` (data type) --- A\n# [data type](https://puppet.com/docs/puppet/latest/lang_data_type.html)\n# that must match the retrieved value; if not, the lookup (and catalog\n# compilation) will fail. Defaults to `Data` (accepts any normal value).\n# 3. `<MERGE BEHAVIOR>` (string or hash; see **\"Merge Behaviors\"** below) ---\n# Whether (and how) to combine multiple values. If present, this overrides any\n# merge behavior specified in the data sources. Defaults to no value; Puppet will\n# use merge behavior from the data sources if present, and will otherwise do a\n# first-found lookup.\n# 4. `<DEFAULT VALUE>` (any normal value) --- If present, `lookup` returns this\n# when it can't find a normal value. Default values are never merged with found\n# values. Like a normal value, the default must match the value type. Defaults to\n# no value; if Puppet can't find a normal value, the lookup (and compilation) will\n# fail.\n# 5. `<OPTIONS HASH>` (hash) --- Alternate way to set the arguments above, plus\n# some less-common extra options. If you pass an options hash, you can't combine\n# it with any regular arguments (except `<NAME>`). An options hash can have the\n# following keys:\n#     * `'name'` --- Same as `<NAME>` (argument 1). You can pass this as an\n#     argument or in the hash, but not both.\n#     * `'value_type'` --- Same as `<VALUE TYPE>` (argument 2).\n#     * `'merge'` --- Same as `<MERGE BEHAVIOR>` (argument 3).\n#     * `'default_value'` --- Same as `<DEFAULT VALUE>` (argument 4).\n#     * `'default_values_hash'` (hash) --- A hash of lookup keys and default\n#     values. If Puppet can't find a normal value, it will check this hash for the\n#     requested key before giving up. You can combine this with `default_value` or\n#     a lambda, which will be used if the key isn't present in this hash. Defaults\n#     to an empty hash.\n#     * `'override'` (hash) --- A hash of lookup keys and override values. Puppet\n#     will check for the requested key in the overrides hash _first;_ if found, it\n#     returns that value as the _final_ value, ignoring merge behavior. Defaults\n#     to an empty hash.\n#\n# Finally, `lookup` can take a lambda, which must accept a single parameter.\n# This is yet another way to set a default value for the lookup; if no results are\n# found, Puppet will pass the requested key to the lambda and use its result as\n# the default value.\n#\n# #### Merge Behaviors\n#\n# Puppet lookup uses a hierarchy of data sources, and a given key might have\n# values in multiple sources. By default, Puppet returns the first value it finds,\n# but it can also continue searching and merge all the values together.\n#\n# > **Note:** Data sources can use the special `lookup_options` metadata key to\n# request a specific merge behavior for a key. The `lookup` function will use that\n# requested behavior unless you explicitly specify one.\n#\n# The valid merge behaviors are:\n#\n# * `'first'` --- Returns the first value found, with no merging. Puppet lookup's\n# default behavior.\n# * `'unique'` (called \"array merge\" in classic Hiera) --- Combines any number of\n# arrays and scalar values to return a merged, flattened array with all duplicate\n# values removed. The lookup will fail if any hash values are found.\n# * `'hash'` --- Combines the keys and values of any number of hashes to return a\n# merged hash. If the same key exists in multiple source hashes, Puppet will use\n# the value from the highest-priority data source; it won't recursively merge the\n# values.\n# * `'deep'` --- Combines the keys and values of any number of hashes to return a\n# merged hash. If the same key exists in multiple source hashes, Puppet will\n# recursively merge hash or array values (with duplicate values removed from\n# arrays). For conflicting scalar values, the highest-priority value will win.\n# * `{'strategy' => 'first'}`, `{'strategy' => 'unique'}`,\n# or `{'strategy' => 'hash'}` --- Same as the string versions of these merge behaviors.\n# * `{'strategy' => 'deep', <DEEP OPTION> => <VALUE>, ...}` --- Same as `'deep'`,\n# but can adjust the merge with additional options. The available options are:\n#     * `'knockout_prefix'` (string) --- A string prefix to indicate a\n#     value should be _removed_ from the final result. If a value is exactly equal\n#     to the prefix, it will knockout the entire element. Defaults to `undef`, which\n#     disables this feature.\n#     * `'sort_merged_arrays'` (boolean) --- Whether to sort all arrays that are\n#     merged together. Defaults to `false`.\n#     * `'merge_hash_arrays'` (boolean) --- Whether to merge hashes within arrays.\n#     Defaults to `false`.\n#\n# @example Look up a key and return the first value found\n#\n#     lookup('ntp::service_name')\n#\n# @example Do a unique merge lookup of class names, then add all of those classes to the catalog (like `hiera_include`)\n#\n#     lookup('classes', Array[String], 'unique').include\n#\n# @example Do a deep hash merge lookup of user data, but let higher priority sources remove values by prefixing them with `--`\n#\n#     lookup( { 'name'  => 'users',\n#               'merge' => {\n#                 'strategy'        => 'deep',\n#                 'knockout_prefix' => '--',\n#               },\n#     })\n#\n# @since 4.0.0\nPuppet::Functions.create_function(:lookup, Puppet::Functions::InternalFunction) do\n  local_types do\n    type 'NameType         = Variant[String, Array[String]]'\n    type 'ValueType        = Type'\n    type 'DefaultValueType = Any'\n    type 'MergeType        = Variant[String[1], Hash[String, Scalar]]'\n    type 'BlockType        = Callable[NameType]'\n    type \"OptionsWithName  = Struct[{\\\n      name                => NameType,\\\n      value_type          => Optional[ValueType],\\\n      default_value       => Optional[DefaultValueType],\\\n      override            => Optional[Hash[String,Any]],\\\n      default_values_hash => Optional[Hash[String,Any]],\\\n      merge               => Optional[MergeType]\\\n    }]\"\n    type \"OptionsWithoutName = Struct[{\\\n      value_type          => Optional[ValueType],\\\n      default_value       => Optional[DefaultValueType],\\\n      override            => Optional[Hash[String,Any]],\\\n      default_values_hash => Optional[Hash[String,Any]],\\\n      merge               => Optional[MergeType]\\\n    }]\"\n  end\n\n  dispatch :lookup_1 do\n    scope_param\n    param           'NameType',       :name\n    optional_param  'ValueType',      :value_type\n    optional_param  'MergeType',      :merge\n  end\n\n  dispatch :lookup_2 do\n    scope_param\n    param 'NameType',                 :name\n    param 'Optional[ValueType]',      :value_type\n    param 'Optional[MergeType]',      :merge\n    param 'DefaultValueType',         :default_value\n  end\n\n  dispatch :lookup_3 do\n    scope_param\n    param                'NameType',   :name\n    optional_param       'ValueType',  :value_type\n    optional_param       'MergeType',  :merge\n    required_block_param 'BlockType',  :block\n  end\n\n  # Lookup without name. Name then becomes a required entry in the options hash\n  dispatch :lookup_4 do\n    scope_param\n    param                'OptionsWithName', :options_hash\n    optional_block_param 'BlockType',       :block\n  end\n\n  # Lookup using name and options hash.\n  dispatch :lookup_5 do\n    scope_param\n    param                'Variant[String,Array[String]]', :name\n    param                'OptionsWithoutName',            :options_hash\n    optional_block_param 'BlockType',                     :block\n  end\n\n  def lookup_1(scope, name, value_type = nil, merge = nil)\n    do_lookup(scope, name, value_type, nil, false, {}, {}, merge)\n  end\n\n  def lookup_2(scope, name, value_type, merge, default_value)\n    do_lookup(scope, name, value_type, default_value, true, {}, {}, merge)\n  end\n\n  def lookup_3(scope, name, value_type = nil, merge = nil, &block)\n    do_lookup(scope, name, value_type, nil, false, {}, {}, merge, &block)\n  end\n\n  def lookup_4(scope, options_hash, &block)\n    do_lookup(scope, options_hash['name'], *hash_args(options_hash), &block)\n  end\n\n  def lookup_5(scope, name, options_hash, &block)\n    do_lookup(scope, name, *hash_args(options_hash), &block)\n  end\n\n  def do_lookup(scope, name, value_type, default_value, has_default, override, default_values_hash, merge, &block)\n    Puppet::Pops::Lookup.lookup(name, value_type, default_value, has_default, merge,\n                                Puppet::Pops::Lookup::Invocation.new(scope, override, default_values_hash), &block)\n  end\n\n  def hash_args(options_hash)\n    [\n      options_hash['value_type'],\n      options_hash['default_value'],\n      options_hash.include?('default_value'),\n      options_hash['override'] || {},\n      options_hash['default_values_hash'] || {},\n      options_hash['merge']\n    ]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/lstrip.rb",
    "content": "# frozen_string_literal: true\n\n# Strips leading spaces from a String\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String` the conversion removes all leading ASCII white space characters such as space, tab, newline, and return.\n#   It does not remove other space-like characters like hard space (Unicode U+00A0). (Tip, `/^[[:space:]]/` regular expression\n#   matches all space-like characters).\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# @example Removing leading space from a String\n# ```puppet\n# \"\\n\\thello \".lstrip()\n# lstrip(\"\\n\\thello \")\n# ```\n# Would both result in `\"hello\"`\n#\n# @example Removing leading space from strings in an Array\n# ```puppet\n# [\"\\n\\thello \", \"\\n\\thi \"].lstrip()\n# lstrip([\"\\n\\thello \", \"\\n\\thi \"])\n# ```\n# Would both result in `['hello', 'hi']`\n#\nPuppet::Functions.create_function(:lstrip) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.lstrip\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_lstrip(x) }\n  end\n\n  def do_lstrip(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? x.lstrip : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/map.rb",
    "content": "# frozen_string_literal: true\n\n# Applies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# to every value in a data structure and returns an array containing the results.\n#\n# This function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It can\n# request one or two parameters.\n#\n# @example Using the `map` function\n#\n# `$transformed_data = $data.map |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `$transformed_data = map($data) |$parameter| { <PUPPET CODE BLOCK> }`\n#\n# When the first argument (`$data` in the above example) is an array, Puppet passes each\n# value in turn to the lambda.\n#\n# @example Using the `map` function with an array and a one-parameter lambda\n#\n# ```puppet\n# # For the array $data, return an array containing each value multiplied by 10\n# $data = [1,2,3]\n# $transformed_data = $data.map |$items| { $items * 10 }\n# # $transformed_data contains [10,20,30]\n# ```\n#\n# When the first argument is a hash, Puppet passes each key and value pair to the lambda\n# as an array in the form `[key, value]`.\n#\n# @example Using the `map` function with a hash and a one-parameter lambda\n#\n# ```puppet\n# # For the hash $data, return an array containing the keys\n# $data = {'a'=>1,'b'=>2,'c'=>3}\n# $transformed_data = $data.map |$items| { $items[0] }\n# # $transformed_data contains ['a','b','c']\n# ```\n#\n# When the first argument is an array and the lambda has two parameters, Puppet passes the\n# array's indexes (enumerated from 0) in the first parameter and its values in the second\n# parameter.\n#\n# @example Using the `map` function with an array and a two-parameter lambda\n#\n# ```puppet\n# # For the array $data, return an array containing the indexes\n# $data = [1,2,3]\n# $transformed_data = $data.map |$index,$value| { $index }\n# # $transformed_data contains [0,1,2]\n# ```\n#\n# When the first argument is a hash, Puppet passes its keys to the first parameter and its\n# values to the second parameter.\n#\n# @example Using the `map` function with a hash and a two-parameter lambda\n#\n# ```puppet\n# # For the hash $data, return an array containing each value\n# $data = {'a'=>1,'b'=>2,'c'=>3}\n# $transformed_data = $data.map |$key,$value| { $value }\n# # $transformed_data contains [1,2,3]\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:map) do\n  dispatch :map_Hash_2 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :map_Hash_1 do\n    param 'Hash[Any, Any]', :hash\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :map_Enumerable_2 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :map_Enumerable_1 do\n    param 'Iterable', :enumerable\n    block_param 'Callable[1,1]', :block\n  end\n\n  def map_Hash_1(hash)\n    result = []\n    begin\n      hash.each { |x, y| result << yield([x, y]) }\n    rescue StopIteration\n    end\n    result\n  end\n\n  def map_Hash_2(hash)\n    result = []\n    begin\n      hash.each { |x, y| result << yield(x, y) }\n    rescue StopIteration\n    end\n    result\n  end\n\n  def map_Enumerable_1(enumerable)\n    result = []\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    begin\n      enum.each do |val|\n        result << yield(val)\n      end\n    rescue StopIteration\n    end\n    result\n  end\n\n  def map_Enumerable_2(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    if enum.hash_style?\n      enum.map { |entry| yield(*entry) }\n    else\n      result = []\n      begin\n        enum.each_with_index do |val, index|\n          result << yield(index, val)\n        end\n      rescue StopIteration\n      end\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/match.rb",
    "content": "# frozen_string_literal: true\n\n# Matches a regular expression against a string and returns an array containing the match\n# and any matched capturing groups.\n#\n# The first argument is a string or array of strings. The second argument is either a\n# regular expression, regular expression represented as a string, or Regex or Pattern\n# data type that the function matches against the first argument.\n#\n# The returned array contains the entire match at index 0, and each captured group at\n# subsequent index values. If the value or expression being matched is an array, the\n# function returns an array with mapped match results.\n#\n# If the function doesn't find a match, it returns 'undef'.\n#\n# @example Matching a regular expression in a string\n#\n# ```puppet\n# $matches = \"abc123\".match(/[a-z]+[1-9]+/)\n# # $matches contains [abc123]\n# ```\n#\n# @example Matching a regular expressions with grouping captures in a string\n#\n# ```puppet\n# $matches = \"abc123\".match(/([a-z]+)([1-9]+)/)\n# # $matches contains [abc123, abc, 123]\n# ```\n#\n# @example Matching a regular expression with grouping captures in an array of strings\n#\n# ```puppet\n# $matches = [\"abc123\",\"def456\"].match(/([a-z]+)([1-9]+)/)\n# # $matches contains [[abc123, abc, 123], [def456, def, 456]]\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:match) do\n  dispatch :match do\n    param 'String', :string\n    param 'Variant[Any, Type]', :pattern\n  end\n\n  dispatch :enumerable_match do\n    param 'Array[String]', :string\n    param 'Variant[Any, Type]', :pattern\n  end\n\n  def initialize(closure_scope, loader)\n    super\n\n    # Make this visitor shared among all instantiations of this function since it is faster.\n    # This can be used because it is not possible to replace\n    # a puppet runtime (where this function is) without a reboot. If you model a function in a module after\n    # this class, use a regular instance variable instead to enable reloading of the module without reboot\n    #\n    @@match_visitor ||= Puppet::Pops::Visitor.new(self, \"match\", 1, 1)\n  end\n\n  # Matches given string against given pattern and returns an Array with matches.\n  # @param string [String] the string to match\n  # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern\n  # @return [Array<String>] matches where first match is the entire match, and index 1-n are captures from left to right\n  #\n  def match(string, pattern)\n    @@match_visitor.visit_this_1(self, pattern, string)\n  end\n\n  # Matches given Array[String] against given pattern and returns an Array with mapped match results.\n  #\n  # @param array [Array<String>] the array of strings to match\n  # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern\n  # @return [Array<Array<String, nil>>] Array with matches (see {#match}), non matching entries produce a nil entry\n  #\n  def enumerable_match(array, pattern)\n    array.map { |s| match(s, pattern) }\n  end\n\n  protected\n\n  def match_Object(obj, s)\n    msg = _(\"match() expects pattern of T, where T is String, Regexp, Regexp[r], Pattern[p], or Array[T]. Got %{klass}\") % { klass: obj.class }\n    raise ArgumentError, msg\n  end\n\n  def match_String(pattern_string, s)\n    do_match(s, Regexp.new(pattern_string))\n  end\n\n  def match_Regexp(regexp, s)\n    do_match(s, regexp)\n  end\n\n  def match_PTypeAliasType(alias_t, s)\n    match(s, alias_t.resolved_type)\n  end\n\n  def match_PVariantType(var_t, s)\n    # Find first matching type (or error out if one of the variants is not acceptable)\n    result = nil\n    var_t.types.find { |t| result = match(s, t) }\n    result\n  end\n\n  def match_PRegexpType(regexp_t, s)\n    raise ArgumentError, _(\"Given Regexp Type has no regular expression\") unless regexp_t.pattern\n\n    do_match(s, regexp_t.regexp)\n  end\n\n  def match_PPatternType(pattern_t, s)\n    # Since we want the actual match result (not just a boolean), an iteration over\n    # Pattern's regular expressions is needed. (They are of PRegexpType)\n    result = nil\n    pattern_t.patterns.find { |pattern| result = match(s, pattern) }\n    result\n  end\n\n  # Returns the first matching entry\n  def match_Array(array, s)\n    result = nil\n    array.flatten.find { |entry| result = match(s, entry) }\n    result\n  end\n\n  private\n\n  def do_match(s, regexp)\n    result = regexp.match(s)\n    result.to_a if result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/max.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the highest value among a variable number of arguments.\n# Takes at least one argument.\n#\n# This function is (with one exception) compatible with the stdlib function\n# with the same name and performs deprecated type conversion before\n# comparison as follows:\n#\n# * If a value converted to String is an optionally '-' prefixed,\n#   string of digits, one optional decimal point, followed by optional\n#   decimal digits - then the comparison is performed on the values\n#   converted to floating point.\n# * If a value is not considered convertible to float, it is converted\n#   to a `String` and the comparison is a lexical compare where min is\n#   the lexicographical later value.\n# * A lexicographical compare is performed in a system locale - international\n#   characters may therefore not appear in what a user thinks is the correct order.\n# * The conversion rules apply to values in pairs - the rule must hold for both\n#   values - a value may therefore be compared using different rules depending\n#   on the \"other value\".\n# * The returned result found to be the \"highest\" is the original unconverted value.\n#\n# The above rules have been deprecated in Puppet 6.0.0 as they produce strange results when\n# given values of mixed data types. In general, either convert values to be\n# all `String` or all `Numeric` values before calling the function, or call the\n# function with a lambda that performs type conversion and comparison. This because one\n# simply cannot compare `Boolean` with `Regexp` and with any arbitrary `Array`, `Hash` or\n# `Object` and getting a meaningful result.\n#\n# The one change in the function's behavior is when the function is given a single\n# array argument. The stdlib implementation would return that array as the result where\n# it now instead returns the max value from that array.\n#\n# @example 'max of values - stdlib compatible'\n#\n# ```puppet\n# notice(max(1)) # would notice 1\n# notice(max(1,2)) # would notice 2\n# notice(max(\"1\", 2)) # would notice 2\n# notice(max(\"0777\", 512)) # would notice \"0777\", since \"0777\" is not converted from octal form\n# notice(max(0777, 512)) # would notice 512, since 0777 is decimal 511\n# notice(max('aa', 'ab')) # would notice 'ab'\n# notice(max(['a'], ['b'])) # would notice ['b'], since \"['b']\" is after \"['a']\"\n# ```\n#\n# @example find 'max' value in an array - stdlib compatible\n#\n# ```puppet\n# $x = [1,2,3,4]\n# notice(max(*$x)) # would notice 4\n# ```\n#\n# @example find 'max' value in an array directly - since Puppet 6.0.0\n#\n# ```puppet\n# $x = [1,2,3,4]\n# notice(max($x)) # would notice 4\n# notice($x.max) # would notice 4\n# ```\n# This example shows that a single array argument is used as the set of values\n# as opposed to being a single returned value.\n#\n# When calling with a lambda, it must accept two variables and it must return\n# one of -1, 0, or 1 depending on if first argument is before/lower than, equal to,\n# or higher/after the second argument.\n#\n# @example 'max of values using a lambda - since Puppet 6.0.0'\n#\n# ```puppet\n# notice(max(\"2\", \"10\", \"100\") |$a, $b| { compare($a, $b) })\n# ```\n#\n# Would notice \"2\" as higher since it is lexicographically higher/after the other values. Without the\n# lambda the stdlib compatible (deprecated) behavior would have been to return \"100\" since number conversion\n# kicks in.\n#\nPuppet::Functions.create_function(:max) do\n  dispatch :on_numeric do\n    repeated_param 'Numeric', :values\n  end\n\n  dispatch :on_string do\n    repeated_param 'String', :values\n  end\n\n  dispatch :on_semver do\n    repeated_param 'Semver', :values\n  end\n\n  dispatch :on_timespan do\n    repeated_param 'Timespan', :values\n  end\n\n  dispatch :on_timestamp do\n    repeated_param 'Timestamp', :values\n  end\n\n  dispatch :on_single_numeric_array do\n    param 'Array[Numeric]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_string_array do\n    param 'Array[String]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_semver_array do\n    param 'Array[Semver]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_timespan_array do\n    param 'Array[Timespan]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_timestamp_array do\n    param 'Array[Timestamp]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_any_array do\n    param 'Array', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_any_with_block do\n    repeated_param 'Any', :values\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_any do\n    repeated_param 'Any', :values\n  end\n\n  # All are Numeric - ok now, will be ok later\n  def on_numeric(*args)\n    assert_arg_count(args)\n    args.max\n  end\n\n  # All are String, may convert to numeric (which is deprecated)\n  def on_string(*args)\n    assert_arg_count(args)\n\n    args.max do |a, b|\n      if a.to_s =~ /\\A^-?\\d+([._eE]\\d+)?\\z/ && b.to_s =~ /\\A-?\\d+([._eE]\\d+)?\\z/\n        Puppet.warn_once('deprecations', 'max_function_numeric_coerce_string',\n                         _(\"The max() function's auto conversion of String to Numeric is deprecated - change to convert input before calling, or use lambda\"))\n        a.to_f <=> b.to_f\n      else\n        # case sensitive as in the stdlib function\n        a <=> b\n      end\n    end\n  end\n\n  def on_semver(*args)\n    assert_arg_count(args)\n    args.max\n  end\n\n  def on_timespan(*args)\n    assert_arg_count(args)\n    args.max\n  end\n\n  def on_timestamp(*args)\n    assert_arg_count(args)\n    args.max\n  end\n\n  def on_any_with_block(*args, &block)\n    args.max { |x, y| block.call(x, y) }\n  end\n\n  def on_single_numeric_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_numeric(*array)\n    end\n  end\n\n  def on_single_string_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_string(*array)\n    end\n  end\n\n  def on_single_semver_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_semver(*array)\n    end\n  end\n\n  def on_single_timespan_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_timespan(*array)\n    end\n  end\n\n  def on_single_timestamp_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_timestamp(*array)\n    end\n  end\n\n  def on_single_any_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_any(*array)\n    end\n  end\n\n  # Mix of data types - while only some compares are actually bad it will deprecate\n  # the entire call\n  #\n  def on_any(*args)\n    assert_arg_count(args)\n    args.max do |a, b|\n      as = a.to_s\n      bs = b.to_s\n      if as =~ /\\A^-?\\d+([._eE]\\d+)?\\z/ && bs =~ /\\A-?\\d+([._eE]\\d+)?\\z/\n        Puppet.warn_once('deprecations', 'max_function_numeric_coerce_string',\n                         _(\"The max() function's auto conversion of String to Numeric is deprecated - change to convert input before calling, or use lambda\"))\n        a.to_f <=> b.to_f\n      else\n        Puppet.warn_once('deprecations', 'max_function_string_coerce_any',\n                         _(\"The max() function's auto conversion of Any to String is deprecated - change to convert input before calling, or use lambda\"))\n        as <=> bs\n      end\n    end\n  end\n\n  def assert_arg_count(args)\n    raise(ArgumentError, 'max(): Wrong number of arguments need at least one') if args.empty?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/min.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the lowest value among a variable number of arguments.\n# Takes at least one argument.\n#\n# This function is (with one exception) compatible with the stdlib function\n# with the same name and performs deprecated type conversion before\n# comparison as follows:\n#\n# * If a value converted to String is an optionally '-' prefixed,\n#   string of digits, one optional decimal point, followed by optional\n#   decimal digits - then the comparison is performed on the values\n#   converted to floating point.\n# * If a value is not considered convertible to float, it is converted\n#   to a `String` and the comparison is a lexical compare where min is\n#   the lexicographical earlier value.\n# * A lexicographical compare is performed in a system locale - international\n#   characters may therefore not appear in what a user thinks is the correct order.\n# * The conversion rules apply to values in pairs - the rule must hold for both\n#   values - a value may therefore be compared using different rules depending\n#   on the \"other value\".\n# * The returned result found to be the \"lowest\" is the original unconverted value.\n#\n# The above rules have been deprecated in Puppet 6.0.0 as they produce strange results when\n# given values of mixed data types. In general, either convert values to be\n# all `String` or all `Numeric` values before calling the function, or call the\n# function with a lambda that performs type conversion and comparison. This because one\n# simply cannot compare `Boolean` with `Regexp` and with any arbitrary `Array`, `Hash` or\n# `Object` and getting a meaningful result.\n#\n# The one change in the function's behavior is when the function is given a single\n# array argument. The stdlib implementation would return that array as the result where\n# it now instead returns the max value from that array.\n#\n# @example 'min of values - stdlib compatible'\n#\n# ```puppet\n# notice(min(1)) # would notice 1\n# notice(min(1,2)) # would notice 1\n# notice(min(\"1\", 2)) # would notice 1\n# notice(min(\"0777\", 512)) # would notice 512, since \"0777\" is not converted from octal form\n# notice(min(0777, 512)) # would notice 511, since 0777 is decimal 511\n# notice(min('aa', 'ab')) # would notice 'aa'\n# notice(min(['a'], ['b'])) # would notice ['a'], since \"['a']\" is before \"['b']\"\n# ```\n#\n# @example find 'min' value in an array\n#\n# ```puppet\n# $x = [1,2,3,4]\n# notice(min(*$x)) # would notice 1\n# ```\n#\n# @example find 'min' value in an array directly - since Puppet 6.0.0\n#\n# ```puppet\n# $x = [1,2,3,4]\n# notice(min($x)) # would notice 1\n# notice($x.min) # would notice 1\n# ```\n# This example shows that a single array argument is used as the set of values\n# as opposed to being a single returned value.\n#\n# When calling with a lambda, it must accept two variables and it must return\n# one of -1, 0, or 1 depending on if first argument is before/lower than, equal to,\n# or higher/after the second argument.\n#\n# @example 'min of values using a lambda'\n#\n# ```puppet\n# notice(min(\"2\", \"10\", \"100\") |$a, $b| { compare($a, $b) })\n# ```\n#\n# Would notice \"10\" as lower since it is lexicographically lower/before the other values. Without the\n# lambda the stdlib compatible (deprecated) behavior would have been to return \"2\" since number conversion kicks in.\n#\nPuppet::Functions.create_function(:min) do\n  dispatch :on_numeric do\n    repeated_param 'Numeric', :values\n  end\n\n  dispatch :on_string do\n    repeated_param 'String', :values\n  end\n\n  dispatch :on_semver do\n    repeated_param 'Semver', :values\n  end\n\n  dispatch :on_timespan do\n    repeated_param 'Timespan', :values\n  end\n\n  dispatch :on_timestamp do\n    repeated_param 'Timestamp', :values\n  end\n\n  dispatch :on_single_numeric_array do\n    param 'Array[Numeric]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_semver_array do\n    param 'Array[Semver]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_timespan_array do\n    param 'Array[Timespan]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_timestamp_array do\n    param 'Array[Timestamp]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_string_array do\n    param 'Array[String]', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_single_any_array do\n    param 'Array', :values\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_any_with_block do\n    repeated_param 'Any', :values\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :on_any do\n    repeated_param 'Any', :values\n  end\n\n  # All are Numeric - ok now, will be ok later\n  def on_numeric(*args)\n    assert_arg_count(args)\n    args.min\n  end\n\n  # All are String, may convert to numeric (which is deprecated)\n  def on_string(*args)\n    assert_arg_count(args)\n\n    args.min do |a, b|\n      if a.to_s =~ /\\A^-?\\d+([._eE]\\d+)?\\z/ && b.to_s =~ /\\A-?\\d+([._eE]\\d+)?\\z/\n        Puppet.warn_once('deprecations', 'min_function_numeric_coerce_string',\n                         _(\"The min() function's auto conversion of String to Numeric is deprecated - change to convert input before calling, or use lambda\"))\n        a.to_f <=> b.to_f\n      else\n        # case sensitive as in the stdlib function\n        a <=> b\n      end\n    end\n  end\n\n  def on_semver(*args)\n    assert_arg_count(args)\n    args.min\n  end\n\n  def on_timespan(*args)\n    assert_arg_count(args)\n    args.min\n  end\n\n  def on_timestamp(*args)\n    assert_arg_count(args)\n    args.min\n  end\n\n  def on_any_with_block(*args, &block)\n    args.min { |x, y| block.call(x, y) }\n  end\n\n  def on_single_numeric_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_numeric(*array)\n    end\n  end\n\n  def on_single_string_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_string(*array)\n    end\n  end\n\n  def on_single_semver_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_semver(*array)\n    end\n  end\n\n  def on_single_timespan_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_timespan(*array)\n    end\n  end\n\n  def on_single_timestamp_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_timestamp(*array)\n    end\n  end\n\n  def on_single_any_array(array, &block)\n    if block_given?\n      on_any_with_block(*array, &block)\n    else\n      on_any(*array)\n    end\n  end\n\n  # Mix of data types - while only some compares are actually bad it will deprecate\n  # the entire call\n  #\n  def on_any(*args)\n    assert_arg_count(args)\n    args.min do |a, b|\n      as = a.to_s\n      bs = b.to_s\n      if as =~ /\\A^-?\\d+([._eE]\\d+)?\\z/ && bs =~ /\\A-?\\d+([._eE]\\d+)?\\z/\n        Puppet.warn_once('deprecations', 'min_function_numeric_coerce_string',\n                         _(\"The min() function's auto conversion of String to Numeric is deprecated - change to convert input before calling, or use lambda\"))\n        a.to_f <=> b.to_f\n      else\n        Puppet.warn_once('deprecations', 'min_function_string_coerce_any',\n                         _(\"The min() function's auto conversion of Any to String is deprecated - change to convert input before calling, or use lambda\"))\n        as <=> bs\n      end\n    end\n  end\n\n  def assert_arg_count(args)\n    raise(ArgumentError, 'min(): Wrong number of arguments need at least one') if args.empty?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/module_directory.rb",
    "content": "# frozen_string_literal: true\n\n# Finds an existing module and returns the path to its root directory.\n#\n# The argument to this function should be a module name String\n# For example, the reference `mysql` will search for the\n# directory `<MODULES DIRECTORY>/mysql` and return the first\n# found on the modulepath.\n#\n# This function can also accept:\n#\n# * Multiple String arguments, which will return the path of the **first** module\n#  found, skipping non existing modules.\n# * An array of module names, which will return the path of the **first** module\n#  found from the given names in the array, skipping non existing modules.\n#\n# The function returns `undef` if none of the given modules were found\n#\n# @since 5.4.0\n#\nPuppet::Functions.create_function(:module_directory, Puppet::Functions::InternalFunction) do\n  dispatch :module_directory do\n    scope_param\n    repeated_param 'String', :names\n  end\n\n  dispatch :module_directory_array do\n    scope_param\n    repeated_param 'Array[String]', :names\n  end\n\n  def module_directory_array(scope, names)\n    module_directory(scope, *names)\n  end\n\n  def module_directory(scope, *names)\n    names.each do |module_name|\n      found = scope.compiler.environment.module(module_name)\n      return found.path if found\n    end\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/new.rb",
    "content": "# frozen_string_literal: true\n\n# Creates a new instance/object of a given data type.\n#\n# This function makes it possible to create new instances of\n# concrete data types. If a block is given it is called with the\n# just created instance as an argument.\n#\n# Calling this function is equivalent to directly\n# calling the data type:\n#\n# @example `new` and calling type directly are equivalent\n#\n# ```puppet\n# $a = Integer.new(\"42\")\n# $b = Integer(\"42\")\n# ```\n#\n# These would both convert the string `\"42\"` to the decimal value `42`.\n#\n# @example arguments by position or by name\n#\n# ```puppet\n# $a = Integer.new(\"42\", 8)\n# $b = Integer({from => \"42\", radix => 8})\n# ```\n#\n# This would convert the octal (radix 8) number `\"42\"` in string form\n# to the decimal value `34`.\n#\n# The new function supports two ways of giving the arguments:\n#\n# * by name (using a hash with property to value mapping)\n# * by position (as regular arguments)\n#\n# Note that it is not possible to create new instances of\n# some abstract data types (for example `Variant`). The data type `Optional[T]` is an\n# exception as it will create an instance of `T` or `undef` if the\n# value to convert is `undef`.\n#\n# The arguments that can be given is determined by the data type.\n#\n# > An assertion is always made that the produced value complies with the given type constraints.\n#\n# @example data type constraints are checked\n#\n# ```puppet\n# Integer[0].new(\"-100\")\n# ```\n#\n# Would fail with an assertion error (since value is less than 0).\n#\n# The following sections show the arguments and conversion rules\n# per data type built into the Puppet Type System.\n#\n# ### Conversion to `Optional[T]` and `NotUndef[T]`\n#\n# Conversion to these data types is the same as a conversion to the type argument `T`.\n# In the case of `Optional[T]` it is accepted that the argument to convert may be `undef`.\n# It is however not acceptable to give other arguments (than `undef`) that cannot be\n# converted to `T`.\n#\n# ### Conversion to Integer\n#\n# A new `Integer` can be created from `Integer`, `Float`, `Boolean`, and `String` values.\n# For conversion from `String` it is possible to specify the radix (base).\n#\n# ```puppet\n# type Radix = Variant[Default, Integer[2,2], Integer[8,8], Integer[10,10], Integer[16,16]]\n#\n# function Integer.new(\n#   String $value,\n#   Radix $radix = 10,\n#   Boolean $abs = false\n# )\n#\n# function Integer.new(\n#   Variant[Numeric, Boolean] $value,\n#   Boolean $abs = false\n# )\n# ```\n#\n# * When converting from `String` the default radix is 10.\n# * If radix is not specified an attempt is made to detect the radix from the start of the string:\n#   * `0b` or `0B` is taken as radix 2.\n#   * `0x` or `0X` is taken as radix 16.\n#   * `0` as radix 8.\n#   * All others are decimal.\n# * Conversion from `String` accepts an optional sign in the string.\n# * For hexadecimal (radix 16) conversion an optional leading `\"0x\"`, or `\"0X\"` is accepted.\n# * For octal (radix 8) an optional leading `\"0\"` is accepted.\n# * For binary (radix 2) an optional leading `\"0b\"` or `\"0B\"` is accepted.\n# * When `radix` is set to `default`, the conversion is based on the leading.\n#   characters in the string. A leading `\"0\"` for radix 8, a leading `\"0x\"`, or `\"0X\"` for\n#   radix 16, and leading `\"0b\"` or `\"0B\"` for binary.\n# * Conversion from `Boolean` results in `0` for `false` and `1` for `true`.\n# * Conversion from `Integer`, `Float`, and `Boolean` ignores the radix.\n# * `Float` value fractions are truncated (no rounding).\n# * When `abs` is set to `true`, the result will be an absolute integer.\n#\n# @example Converting to Integer in multiple ways\n#\n# ```puppet\n# $a_number = Integer(\"0xFF\", 16)    # results in 255\n# $a_number = Integer(\"010\")         # results in 8\n# $a_number = Integer(\"010\", 10)     # results in 10\n# $a_number = Integer(true)          # results in 1\n# $a_number = Integer(-38, 10, true) # results in 38\n# ```\n#\n# ### Conversion to Float\n#\n# A new `Float` can be created from `Integer`, `Float`, `Boolean`, and `String` values.\n# For conversion from `String` both float and integer formats are supported.\n#\n# ```puppet\n# function Float.new(\n#   Variant[Numeric, Boolean, String] $value,\n#   Boolean $abs = true\n# )\n# ```\n#\n# * For an integer, the floating point fraction of `.0` is added to the value.\n# * A `Boolean` `true` is converted to `1.0`, and a `false` to `0.0`.\n# * In `String` format, integer prefixes for hex and binary are understood (but not octal since\n#   floating point in string format may start with a `'0'`).\n# * When `abs` is set to `true`, the result will be an absolute floating point value.\n#\n# ### Conversion to Numeric\n#\n# A new `Integer` or `Float` can be created from `Integer`, `Float`, `Boolean` and\n# `String` values.\n#\n# ```puppet\n# function Numeric.new(\n#   Variant[Numeric, Boolean, String] $value,\n#   Boolean $abs = true\n# )\n# ```\n#\n# * If the value has a decimal period, or if given in scientific notation\n#   (e/E), the result is a `Float`, otherwise the value is an `Integer`. The\n#   conversion from `String` always uses a radix based on the prefix of the string.\n# * Conversion from `Boolean` results in `0` for `false` and `1` for `true`.\n# * When `abs` is set to `true`, the result will be an absolute `Float`or `Integer` value.\n#\n# @example Converting to Numeric in different ways\n#\n# ```puppet\n# $a_number = Numeric(true)        # results in 1\n# $a_number = Numeric(\"0xFF\")      # results in 255\n# $a_number = Numeric(\"010\")       # results in 8\n# $a_number = Numeric(\"3.14\")      # results in 3.14 (a float)\n# $a_number = Numeric(-42.3, true) # results in 42.3\n# $a_number = Numeric(-42, true)   # results in 42\n# ```\n#\n# ### Conversion to Timespan\n#\n# A new `Timespan` can be created from `Integer`, `Float`, `String`, and `Hash` values. Several variants of the constructor are provided.\n#\n# **Timespan from seconds**\n#\n# When a Float is used, the decimal part represents fractions of a second.\n#\n# ```puppet\n# function Timespan.new(\n#   Variant[Float, Integer] $value\n# )\n# ```\n#\n# **Timespan from days, hours, minutes, seconds, and fractions of a second**\n#\n# The arguments can be passed separately in which case the first four, days, hours, minutes, and seconds are mandatory and the rest are optional.\n# All values may overflow and/or be negative. The internal 128-bit nano-second integer is calculated as:\n#\n# ```\n# (((((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000 + milliseconds) * 1000 + microseconds) * 1000 + nanoseconds\n# ```\n#\n# ```puppet\n# function Timespan.new(\n#   Integer $days, Integer $hours, Integer $minutes, Integer $seconds,\n#   Integer $milliseconds = 0, Integer $microseconds = 0, Integer $nanoseconds = 0\n# )\n# ```\n#\n# or, all arguments can be passed as a `Hash`, in which case all entries are optional:\n#\n# ```puppet\n# function Timespan.new(\n#   Struct[{\n#     Optional[negative] => Boolean,\n#     Optional[days] => Integer,\n#     Optional[hours] => Integer,\n#     Optional[minutes] => Integer,\n#     Optional[seconds] => Integer,\n#     Optional[milliseconds] => Integer,\n#     Optional[microseconds] => Integer,\n#     Optional[nanoseconds] => Integer\n#   }] $hash\n# )\n# ```\n#\n# **Timespan from String and format directive patterns**\n#\n# The first argument is parsed using the format optionally passed as a string or array of strings. When an array is used, an attempt\n# will be made to parse the string using the first entry and then with each entry in succession until parsing succeeds. If the second\n# argument is omitted, an array of default formats will be used.\n#\n# An exception is raised when no format was able to parse the given string.\n#\n# ```puppet\n# function Timespan.new(\n#   String $string, Variant[String[2],Array[String[2], 1]] $format = <default format>)\n# )\n# ```\n#\n# the arguments may also be passed as a `Hash`:\n#\n# ```puppet\n# function Timespan.new(\n#   Struct[{\n#     string => String[1],\n#     Optional[format] => Variant[String[2],Array[String[2], 1]]\n#   }] $hash\n# )\n# ```\n#\n# The directive consists of a percent (`%`) character, zero or more flags, optional minimum field width and\n# a conversion specifier as follows:\n# ```\n# %[Flags][Width]Conversion\n# ```\n#\n# ##### Flags:\n#\n# | Flag  | Meaning\n# | ----  | ---------------\n# | -     | Don't pad numerical output\n# | _     | Use spaces for padding\n# | 0     | Use zeros for padding\n#\n# ##### Format directives:\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | D | Number of Days |\n# | H | Hour of the day, 24-hour clock |\n# | M | Minute of the hour (00..59) |\n# | S | Second of the minute (00..59) |\n# | L | Millisecond of the second (000..999) |\n# | N | Fractional seconds digits |\n#\n# The format directive that represents the highest magnitude in the format will be allowed to\n# overflow. I.e. if no \"%D\" is used but a \"%H\" is present, then the hours may be more than 23.\n#\n# The default array contains the following patterns:\n#\n# ```\n# ['%D-%H:%M:%S', '%D-%H:%M', '%H:%M:%S', '%H:%M']\n# ```\n#\n# Examples - Converting to Timespan\n#\n# ```puppet\n# $duration = Timespan(13.5)       # 13 seconds and 500 milliseconds\n# $duration = Timespan({days=>4})  # 4 days\n# $duration = Timespan(4, 0, 0, 2) # 4 days and 2 seconds\n# $duration = Timespan('13:20')    # 13 hours and 20 minutes (using default pattern)\n# $duration = Timespan('10:03.5', '%M:%S.%L') # 10 minutes, 3 seconds, and 5 milli-seconds\n# $duration = Timespan('10:03.5', '%M:%S.%N') # 10 minutes, 3 seconds, and 5 nano-seconds\n# ```\n#\n# ### Conversion to Timestamp\n#\n# A new `Timestamp` can be created from `Integer`, `Float`, `String`, and `Hash` values. Several variants of the constructor are provided.\n#\n# **Timestamp from seconds since epoch (1970-01-01 00:00:00 UTC)**\n#\n# When a Float is used, the decimal part represents fractions of a second.\n#\n# ```puppet\n# function Timestamp.new(\n#   Variant[Float, Integer] $value\n# )\n# ```\n#\n# **Timestamp from String and patterns consisting of format directives**\n#\n# The first argument is parsed using the format optionally passed as a string or array of strings. When an array is used, an attempt\n# will be made to parse the string using the first entry and then with each entry in succession until parsing succeeds. If the second\n# argument is omitted, an array of default formats will be used.\n#\n# A third optional timezone argument can be provided. The first argument will then be parsed as if it represents a local time in that\n# timezone. The timezone can be any timezone that is recognized when using the `'%z'` or `'%Z'` formats, or the word `'current'`, in which\n# case the current timezone of the evaluating process will be used. The timezone argument is case insensitive.\n#\n# The default timezone, when no argument is provided, or when using the keyword `default`, is 'UTC'.\n#\n# It is illegal to provide a timezone argument other than `default` in combination with a format that contains '%z' or '%Z' since that\n# would introduce an ambiguity as to which timezone to use. The one extracted from the string, or the one provided as an argument.\n#\n# An exception is raised when no format was able to parse the given string.\n#\n# ```puppet\n# function Timestamp.new(\n#   String $string,\n#   Variant[String[2],Array[String[2], 1]] $format = <default format>,\n#   String $timezone = default)\n# )\n# ```\n#\n# the arguments may also be passed as a `Hash`:\n#\n# ```puppet\n# function Timestamp.new(\n#   Struct[{\n#     string => String[1],\n#     Optional[format] => Variant[String[2],Array[String[2], 1]],\n#     Optional[timezone] => String[1]\n#   }] $hash\n# )\n# ```\n#\n# The directive consists of a percent (%) character, zero or more flags, optional minimum field width and\n# a conversion specifier as follows:\n# ```\n# %[Flags][Width]Conversion\n# ```\n#\n# ##### Flags:\n#\n# | Flag  | Meaning\n# | ----  | ---------------\n# | -     | Don't pad numerical output\n# | _     | Use spaces for padding\n# | 0     | Use zeros for padding\n# | #     | Change names to upper-case or change case of am/pm\n# | ^     | Use uppercase\n# | :     | Use colons for `%z`\n#\n# ##### Format directives (names and padding can be altered using flags):\n#\n# **Date (Year, Month, Day):**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | Y | Year with century, zero-padded to at least 4 digits |\n# | C | year / 100 (rounded down such as `20` in `2009`) |\n# | y | year % 100 (`00..99`) |\n# | m | Month of the year, zero-padded (`01..12`) |\n# | B | The full month name (`\"January\"`) |\n# | b | The abbreviated month name (`\"Jan\"`) |\n# | h | Equivalent to `%b` |\n# | d | Day of the month, zero-padded (`01..31`) |\n# | e | Day of the month, blank-padded (`1..31`) |\n# | j | Day of the year (`001..366`) |\n#\n# **Time (Hour, Minute, Second, Subsecond):**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | H | Hour of the day, 24-hour clock, zero-padded (`00..23`) |\n# | k | Hour of the day, 24-hour clock, blank-padded (`0..23`) |\n# | I | Hour of the day, 12-hour clock, zero-padded (`01..12`) |\n# | l | Hour of the day, 12-hour clock, blank-padded (`1..12`) |\n# | P | Meridian indicator, lowercase (`\"am\"` or `\"pm\"`) |\n# | p | Meridian indicator, uppercase (`\"AM\"` or `\"PM\"`) |\n# | M | Minute of the hour (`00..59`) |\n# | S | Second of the minute (`00..60`) |\n# | L | Millisecond of the second (`000..999`). Digits under millisecond are truncated to not produce 1000 |\n# | N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified width are truncated to avoid carry up |\n#\n# **Time (Hour, Minute, Second, Subsecond):**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | z   | Time zone as hour and minute offset from UTC (e.g. `+0900`) |\n# | :z  | hour and minute offset from UTC with a colon (e.g. `+09:00`) |\n# | ::z | hour, minute and second offset from UTC (e.g. `+09:00:00`) |\n# | Z   | Abbreviated time zone name or similar information.  (OS dependent) |\n#\n# **Weekday:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | A | The full weekday name (`\"Sunday\"`) |\n# | a | The abbreviated name (`\"Sun\"`) |\n# | u | Day of the week (Monday is `1`, `1..7`) |\n# | w | Day of the week (Sunday is `0`, `0..6`) |\n#\n# **ISO 8601 week-based year and week number:**\n#\n# The first week of YYYY starts with a Monday and includes YYYY-01-04.\n# The days in the year before the first week are in the last week of\n# the previous year.\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | G | The week-based year |\n# | g | The last 2 digits of the week-based year (`00..99`) |\n# | V | Week number of the week-based year (`01..53`) |\n#\n# **Week number:**\n#\n# The first week of YYYY that starts with a Sunday or Monday (according to %U\n# or %W). The days in the year before the first week are in week 0.\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | U | Week number of the year. The week starts with Sunday. (`00..53`) |\n# | W | Week number of the year. The week starts with Monday. (`00..53`) |\n#\n# **Seconds since the Epoch:**\n#\n# | Format | Meaning |\n# | s | Number of seconds since 1970-01-01 00:00:00 UTC. |\n#\n# **Literal string:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | n | Newline character (`\\n`) |\n# | t | Tab character (`\\t`) |\n# | % | Literal `%` character |\n#\n# **Combination:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | c | date and time (`%a %b %e %T %Y`) |\n# | D | Date (`%m/%d/%y`) |\n# | F | The ISO 8601 date format (`%Y-%m-%d`) |\n# | v | VMS date (`%e-%^b-%4Y`) |\n# | x | Same as `%D` |\n# | X | Same as `%T` |\n# | r | 12-hour time (`%I:%M:%S %p`) |\n# | R | 24-hour time (`%H:%M`) |\n# | T | 24-hour time (`%H:%M:%S`) |\n#\n# The default array contains the following patterns:\n#\n# When a timezone argument (other than `default`) is explicitly provided:\n#\n# ```\n# ['%FT%T.L', '%FT%T', '%F']\n# ```\n#\n# otherwise:\n#\n# ```\n# ['%FT%T.%L %Z', '%FT%T %Z', '%F %Z', '%FT%T.L', '%FT%T', '%F']\n# ```\n#\n# Examples - Converting to Timestamp\n#\n# ```puppet\n# $ts = Timestamp(1473150899)                              # 2016-09-06 08:34:59 UTC\n# $ts = Timestamp({string=>'2015', format=>'%Y'})          # 2015-01-01 00:00:00.000 UTC\n# $ts = Timestamp('Wed Aug 24 12:13:14 2016', '%c')        # 2016-08-24 12:13:14 UTC\n# $ts = Timestamp('Wed Aug 24 12:13:14 2016 PDT', '%c %Z') # 2016-08-24 19:13:14.000 UTC\n# $ts = Timestamp('2016-08-24 12:13:14', '%F %T', 'PST')   # 2016-08-24 20:13:14.000 UTC\n# $ts = Timestamp('2016-08-24T12:13:14', default, 'PST')   # 2016-08-24 20:13:14.000 UTC\n#\n# ```\n#\n# ### Conversion to Type\n#\n# A new `Type` can be created from its `String` representation.\n#\n# @example Creating a type from a string\n#\n# ```puppet\n# $t = Type.new('Integer[10]')\n# ```\n#\n# ### Conversion to String\n#\n# Conversion to `String` is the most comprehensive conversion as there are many\n# use cases where a string representation is wanted. The defaults for the many options\n# have been chosen with care to be the most basic \"value in textual form\" representation.\n# The more advanced forms of formatting are intended to enable writing special purposes formatting\n# functions in the Puppet language.\n#\n# A new string can be created from all other data types. The process is performed in\n# several steps - first the data type of the given value is inferred, then the resulting data type\n# is used to find the most significant format specified for that data type. And finally,\n# the found format is used to convert the given value.\n#\n# The mapping from data type to format is referred to as the *format map*. This map\n# allows different formatting depending on type.\n#\n# @example Positive Integers in Hexadecimal prefixed with `'0x'`, negative in Decimal\n#\n# ```puppet\n# $format_map = {\n#   Integer[default, 0] => \"%d\",\n#   Integer[1, default] => \"%#x\"\n# }\n# String(\"-1\", $format_map)  # produces '-1'\n# String(\"10\", $format_map)  # produces '0xa'\n# ```\n#\n# A format is specified on the form:\n#\n# ```\n# %[Flags][Width][.Precision]Format\n# ```\n#\n# `Width` is the number of characters into which the value should be fitted. This allocated space is\n# padded if value is shorter. By default it is space padded, and the flag `0` will cause padding with `0`\n# for numerical formats.\n#\n# `Precision` is the number of fractional digits to show for floating point, and the maximum characters\n# included in a string format.\n#\n# Note that all data type supports the formats `s` and `p` with the meaning \"default string representation\" and\n# \"default programmatic string representation\" (which for example means that a String is quoted in 'p' format).\n#\n# **Signatures of String conversion**\n#\n# ```puppet\n# type Format = Pattern[/^%([\\s\\+\\-#0\\[\\{<\\(\\|]*)([1-9][0-9]*)?(?:\\.([0-9]+))?([a-zA-Z])/]\n# type ContainerFormat = Struct[{\n#   format         => Optional[String],\n#   separator      => Optional[String],\n#   separator2     => Optional[String],\n#   string_formats => Hash[Type, Format]\n#   }]\n# type TypeMap = Hash[Type, Variant[Format, ContainerFormat]]\n# type Formats = Variant[Default, String[1], TypeMap]\n#\n# function String.new(\n#   Any $value,\n#   Formats $string_formats\n# )\n# ```\n#\n# Where:\n#\n# * `separator` is the string used to separate entries in an array, or hash (extra space should not be included at\n#   the end), defaults to `\",\"`\n# * `separator2` is the separator between key and value in a hash entry (space padding should be included as\n#   wanted), defaults to `\" => \"`.\n# * `string_formats` is a data type to format map for values contained in arrays and hashes - defaults to `{Any => \"%p\"}`. Note that\n#   these nested formats are not applicable to data types that are containers; they are always formatted as per the top level\n#   format specification.\n#\n# @example Simple Conversion to String (using defaults)\n#\n# ```puppet\n# $str = String(10)      # produces '10'\n# $str = String([10])    # produces '[\"10\"]'\n# ```\n#\n# @example Simple Conversion to String specifying the format for the given value directly\n#\n# ```puppet\n# $str = String(10, \"%#x\")    # produces '0xa'\n# $str = String([10], \"%(a\")  # produces '(\"10\")'\n# ```\n#\n# @example Specifying type for values contained in an array\n#\n# ```puppet\n# $formats = {\n#   Array => {\n#     format => '%(a',\n#     string_formats => { Integer => '%#x' }\n#   }\n# }\n# $str = String([1,2,3], $formats) # produces '(0x1, 0x2, 0x3)'\n# ```\n#\n# The given formats are merged with the default formats, and matching of values to convert against format is based on\n# the specificity of the mapped type; for example, different formats can be used for short and long arrays.\n#\n# **Integer to String**\n#\n# | Format  | Integer Formats\n# | ------  | ---------------\n# | d       | Decimal, negative values produces leading `-`.\n# | x X     | Hexadecimal in lower or upper case. Uses `..f/..F` for negative values unless `+` is also used. A `#` adds prefix `0x/0X`.\n# | o       | Octal. Uses `..0` for negative values unless `+` is also used. A `#` adds prefix `0`.\n# | b B     | Binary with prefix `b` or `B`. Uses `..1/..1` for negative values unless `+` is also used.\n# | c       | Numeric value representing a Unicode value, result is a one unicode character string, quoted if alternative flag `#` is used\n# | s       | Same as `d`, or `d` in quotes if alternative flag `#` is used.\n# | p       | Same as `d`.\n# | eEfgGaA | Converts integer to float and formats using the floating point rules.\n#\n# Defaults to `d`.\n#\n# **Float to String**\n#\n# | Format  | Float formats\n# | ------  | -------------\n# | f       | Floating point in non exponential notation.\n# | e E     | Exponential notation with `e` or `E`.\n# | g G     | Conditional exponential with `e` or `E` if exponent `< -4` or `>=` the precision.\n# | a A     | Hexadecimal exponential form, using `x`/`X` as prefix and `p`/`P` before exponent.\n# | s       | Converted to string using format `p`, then applying string formatting rule, alternate form `#`` quotes result.\n# | p       | Same as `f` format with minimum significant number of fractional digits, prec has no effect.\n# | dxXobBc | Converts float to integer and formats using the integer rules.\n#\n# Defaults to `p`.\n#\n# **String to String**\n#\n# | Format | String\n# | ------ | ------\n# | s      | Unquoted string, verbatim output of control chars.\n# | p      | Programmatic representation - strings are quoted, interior quotes and control chars are escaped. Selects single or double quotes based on content, or uses double quotes if alternative flag `#` is used.\n# | C      | Each `::` name segment capitalized, quoted if alternative flag `#` is used.\n# | c      | Capitalized string, quoted if alternative flag `#` is used.\n# | d      | Downcased string, quoted if alternative flag `#` is used.\n# | u      | Upcased string, quoted if alternative flag `#` is used.\n# | t      | Trims leading and trailing whitespace from the string, quoted if alternative flag `#` is used.\n#\n# Defaults to `s` at top level and `p` inside array or hash.\n#\n# **Boolean to String**\n#\n# | Format    | Boolean Formats\n# | ----      | -------------------\n# | t T       | String `'true'/'false'` or `'True'/'False'`, first char if alternate form is used (i.e. `'t'/'f'` or `'T'/'F'`).\n# | y Y       | String `'yes'/'no'`, `'Yes'/'No'`, `'y'/'n'` or `'Y'/'N'` if alternative flag `#` is used.\n# | dxXobB    | Numeric value `0/1` in accordance with the given format which must be valid integer format.\n# | eEfgGaA   | Numeric value `0.0/1.0` in accordance with the given float format and flags.\n# | s         | String `'true'` / `'false'`.\n# | p         | String `'true'` / `'false'`.\n#\n# **Regexp to String**\n#\n# | Format    | Regexp Formats\n# | ----      | --------------\n# | s         | No delimiters, quoted if alternative flag `#` is used.\n# | p         | Delimiters `/ /`.\n#\n# **Undef to String**\n#\n# | Format    | Undef formats\n# | ------    | -------------\n# | s         | Empty string, or quoted empty string if alternative flag `#` is used.\n# | p         | String `'undef'`, or quoted `'\"undef\"'` if alternative flag `#` is used.\n# | n         | String `'nil'`, or `'null'` if alternative flag `#` is used.\n# | dxXobB    | String `'NaN'`.\n# | eEfgGaA   | String `'NaN'`.\n# | v         | String `'n/a'`.\n# | V         | String `'N/A'`.\n# | u         | String `'undef'`, or `'undefined'` if alternative `#` flag is used.\n#\n# **Default value to String**\n#\n# | Format    | Default formats\n# | ------    | ---------------\n# | d D       | String `'default'` or `'Default'`, alternative form `#` causes value to be quoted.\n# | s         | Same as `d`.\n# | p         | Same as `d`.\n#\n# **Binary value to String**\n#\n# | Format    | Default formats\n# | ------    | ---------------\n# | s         | binary as unquoted UTF-8 characters (errors if byte sequence is invalid UTF-8). Alternate form escapes non ascii bytes.\n# | p         | `'Binary(\"<base64strict>\")'`\n# | b         | `'<base64>'` - base64 string with newlines inserted\n# | B         | `'<base64strict>'` - base64 strict string (without newlines inserted)\n# | u         | `'<base64urlsafe>'` - base64 urlsafe string\n# | t         | `'Binary'` - outputs the name of the type only\n# | T         | `'BINARY'` - output the name of the type in all caps only\n#\n# * The alternate form flag `#` will quote the binary or base64 text output.\n# * The format `%#s` allows invalid UTF-8 characters and outputs all non ascii bytes\n#   as hex escaped characters on the form `\\\\xHH` where `H` is a hex digit.\n# * The width and precision values are applied to the text part only in `%p` format.\n#\n# **Array & Tuple to String**\n#\n# | Format    | Array/Tuple Formats\n# | ------    | -------------\n# | a         | Formats with `[ ]` delimiters and `,`, alternate form `#` indents nested arrays/hashes.\n# | s         | Same as `a`.\n# | p         | Same as `a`.\n#\n# See \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\n# more information about options.\n#\n# The alternate form flag `#` will cause indentation of nested array or hash containers. If width is also set\n# it is taken as the maximum allowed length of a sequence of elements (not including delimiters). If this max length\n# is exceeded, each element will be indented.\n#\n# **Hash & Struct to String**\n#\n# | Format    | Hash/Struct Formats\n# | ------    | -------------\n# | h         | Formats with `{ }` delimiters, `,` element separator and ` => ` inner element separator unless overridden by flags.\n# | s         | Same as h.\n# | p         | Same as h.\n# | a         | Converts the hash to an array of `[k,v]` tuples and formats it using array rule(s).\n#\n# See \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\n# more information about options.\n#\n# The alternate form flag `#` will format each hash key/value entry indented on a separate line.\n#\n# **Type to String**\n#\n# | Format    | Array/Tuple Formats\n# | ------    | -------------\n# | s         | The same as `p`, quoted if alternative flag `#` is used.\n# | p         | Outputs the type in string form as specified by the Puppet Language.\n#\n# **Flags**\n#\n# | Flag     | Effect\n# | ------   | ------\n# | (space)  | A space instead of `+` for numeric output (`-` is shown), for containers skips delimiters.\n# | #        | Alternate format; prefix `0x/0x`, `0` (octal) and `0b/0B` for binary, Floats force decimal '.'. For g/G keep trailing `0`.\n# | +        | Show sign `+/-` depending on value's sign, changes `x`, `X`, `o`, `b`, `B` format to not use 2's complement form.\n# | -        | Left justify the value in the given width.\n# | 0        | Pad with `0` instead of space for widths larger than value.\n# | <[({\\|   | Defines an enclosing pair `<> [] () {} or \\| \\|` when used with a container type.\n#\n# ### Conversion to Boolean\n#\n# Accepts a single value as argument:\n#\n# * Float `0.0` is `false`, all other float values are `true`\n# * Integer `0` is `false`, all other integer values are `true`\n# * Strings\n#   * `true` if 'true', 'yes', 'y' (case independent compare)\n#   * `false` if 'false', 'no', 'n' (case independent compare)\n# * Boolean is already boolean and is simply returned\n#\n# ### Conversion to Array and Tuple\n#\n# When given a single value as argument:\n#\n# * A non empty `Hash` is converted to an array matching `Array[Tuple[Any,Any], 1]`.\n# * An empty `Hash` becomes an empty array.\n# * An `Array` is simply returned.\n# * An `Iterable[T]` is turned into an array of `T` instances.\n# * A `Binary` is converted to an `Array[Integer[0,255]]` of byte values\n#\n# When given a second Boolean argument:\n#\n# * if `true`, a value that is not already an array is returned as a one element array.\n# * if `false`, (the default), converts the first argument as shown above.\n#\n# @example Ensuring value is an array\n#\n# ```puppet\n# $arr = Array($value, true)\n# ```\n#\n# Conversion to a `Tuple` works exactly as conversion to an `Array`, only that the constructed array is\n# asserted against the given tuple type.\n#\n# ### Conversion to Hash and Struct\n#\n# Accepts a single value as argument:\n#\n# * An empty `Array` becomes an empty `Hash`\n# * An `Array` matching `Array[Tuple[Any,Any], 1]` is converted to a hash where each tuple describes a key/value entry\n# * An `Array` with an even number of entries is interpreted as `[key1, val1, key2, val2, ...]`\n# * An `Iterable` is turned into an `Array` and then converted to hash as per the array rules\n# * A `Hash` is simply returned\n#\n# Alternatively, a tree can be constructed by giving two values; an array of tuples on the form `[path, value]`\n# (where the `path` is the path from the root of a tree, and `value` the value at that position in the tree), and\n# either the option `'tree'` (do not convert arrays to hashes except the top level), or\n# `'hash_tree'` (convert all arrays to hashes).\n#\n# The tree/hash_tree forms of Hash creation are suited for transforming the result of an iteration\n# using `tree_each` and subsequent filtering or mapping.\n#\n# @example Mapping a hash tree\n#\n# Mapping an arbitrary structure in a way that keeps the structure, but where some values are replaced\n# can be done by using the `tree_each` function, mapping, and then constructing a new Hash from the result:\n#\n# ```puppet\n# # A hash tree with 'water' at different locations\n# $h = { a => { b => { x => 'water'}}, b => { y => 'water'} }\n# # a helper function that turns water into wine\n# function make_wine($x) { if $x == 'water' { 'wine' } else { $x } }\n# # create a flattened tree with water turned into wine\n# $flat_tree = $h.tree_each.map |$entry| { [$entry[0], make_wine($entry[1])] }\n# # create a new Hash and log it\n# notice Hash($flat_tree, 'hash_tree')\n# ```\n#\n# Would notice the hash `{a => {b => {x => wine}}, b => {y => wine}}`\n#\n# Conversion to a `Struct` works exactly as conversion to a `Hash`, only that the constructed hash is\n# asserted against the given struct type.\n#\n# ### Conversion to a Regexp\n#\n# A `String` can be converted into a `Regexp`\n#\n# **Example**: Converting a String into a Regexp\n# ```puppet\n# $s = '[a-z]+\\.com'\n# $r = Regexp($s)\n# if('foo.com' =~ $r) {\n#   ...\n# }\n# ```\n#\n# ### Creating a SemVer\n#\n# A SemVer object represents a single [Semantic Version](http://semver.org/).\n# It can be created from a String, individual values for its parts, or a hash specifying the value per part.\n# See the specification at [semver.org](http://semver.org/) for the meaning of the SemVer's parts.\n#\n# The signatures are:\n#\n# ```puppet\n# type PositiveInteger = Integer[0,default]\n# type SemVerQualifier = Pattern[/\\A(?<part>[0-9A-Za-z-]+)(?:\\.\\g<part>)*\\Z/]\n# type SemVerString = String[1]\n# type SemVerHash =Struct[{\n#   major                => PositiveInteger,\n#   minor                => PositiveInteger,\n#   patch                => PositiveInteger,\n#   Optional[prerelease] => SemVerQualifier,\n#   Optional[build]      => SemVerQualifier\n# }]\n#\n# function SemVer.new(SemVerString $str)\n#\n# function SemVer.new(\n#         PositiveInteger           $major\n#         PositiveInteger           $minor\n#         PositiveInteger           $patch\n#         Optional[SemVerQualifier] $prerelease = undef\n#         Optional[SemVerQualifier] $build = undef\n#         )\n#\n# function SemVer.new(SemVerHash $hash_args)\n# ```\n#\n# @example `SemVer` and `SemVerRange` usage\n#\n# ```puppet\n# # As a type, SemVer can describe disjunct ranges which versions can be\n# # matched against - here the type is constructed with two\n# # SemVerRange objects.\n# #\n# $t = SemVer[\n#   SemVerRange('>=1.0.0 <2.0.0'),\n#   SemVerRange('>=3.0.0 <4.0.0')\n# ]\n# notice(SemVer('1.2.3') =~ $t) # true\n# notice(SemVer('2.3.4') =~ $t) # false\n# notice(SemVer('3.4.5') =~ $t) # true\n# ```\n#\n# ### Creating a `SemVerRange`\n#\n# A `SemVerRange` object represents a range of `SemVer`. It can be created from\n# a `String`, or from two `SemVer` instances, where either end can be given as\n# a literal `default` to indicate infinity. The string format of a `SemVerRange` is specified by\n# the [Semantic Version Range Grammar](https://github.com/npm/node-semver#ranges).\n#\n# > Use of the comparator sets described in the grammar (joining with `||`) is not supported.\n#\n# The signatures are:\n#\n# ```puppet\n# type SemVerRangeString = String[1]\n# type SemVerRangeHash = Struct[{\n#   min                   => Variant[Default, SemVer],\n#   Optional[max]         => Variant[Default, SemVer],\n#   Optional[exclude_max] => Boolean\n# }]\n#\n# function SemVerRange.new(\n#   SemVerRangeString $semver_range_string\n# )\n#\n# function SemVerRange.new(\n#   Variant[Default,SemVer] $min\n#   Variant[Default,SemVer] $max\n#   Optional[Boolean]       $exclude_max = undef\n# )\n#\n# function SemVerRange.new(\n#   SemVerRangeHash $semver_range_hash\n# )\n# ```\n#\n# For examples of `SemVerRange` use see \"Creating a SemVer\"\n#\n# ### Creating a Binary\n#\n# A `Binary` object represents a sequence of bytes and it can be created from a String in Base64 format,\n# an Array containing byte values. A Binary can also be created from a Hash containing the value to convert to\n# a `Binary`.\n#\n# The signatures are:\n#\n# ```puppet\n# type ByteInteger = Integer[0,255]\n# type Base64Format = Enum[\"%b\", \"%u\", \"%B\", \"%s\"]\n# type StringHash = Struct[{value => String, \"format\" => Optional[Base64Format]}]\n# type ArrayHash = Struct[{value => Array[ByteInteger]}]\n# type BinaryArgsHash = Variant[StringHash, ArrayHash]\n#\n# function Binary.new(\n#   String $base64_str,\n#   Optional[Base64Format] $format\n# )\n#\n#\n# function Binary.new(\n#   Array[ByteInteger] $byte_array\n# }\n#\n# # Same as for String, or for Array, but where arguments are given in a Hash.\n# function Binary.new(BinaryArgsHash $hash_args)\n# ```\n#\n# The formats have the following meaning:\n#\n# | format | explanation |\n# | ----   | ----        |\n# | B | The data is in base64 strict encoding\n# | u | The data is in URL safe base64 encoding\n# | b | The data is in base64 encoding, padding as required by base64 strict, is added by default\n# | s | The data is a puppet string. The string must be valid UTF-8, or convertible to UTF-8 or an error is raised.\n# | r | (Ruby Raw) the byte sequence in the given string is used verbatim irrespective of possible encoding errors\n#\n# * The default format is `%B`.\n# * Note that the format `%r` should be used sparingly, or not at all. It exists for backwards compatibility reasons when someone receiving\n#   a string from some function and that string should be treated as Binary. Such code should be changed to return a Binary instead of a String.\n#\n# @example Creating a Binary\n#\n# ```puppet\n# # create the binary content \"abc\"\n# $a = Binary('YWJj')\n#\n# # create the binary content from content in a module's file\n# $b = binary_file('mymodule/mypicture.jpg')\n# ```\n#\n# * Since 4.5.0\n# * Binary type since 4.8.0\n#\n# ### Creating an instance of a `Type` using the `Init` type\n#\n# The type `Init[T]` describes a value that can be used when instantiating a type. When used as the first argument in a call to `new`, it\n# will dispatch the call to its contained type and optionally augment the parameter list with additional arguments.\n#\n# @example Creating an instance of Integer using Init[Integer]\n#\n# ```puppet\n# # The following declaration\n# $x = Init[Integer].new('128')\n# # is exactly the same as\n# $x = Integer.new('128')\n# ```\n#\n# or, with base 16 and using implicit new\n#\n# ```puppet\n# # The following declaration\n# $x = Init[Integer,16]('80')\n# # is exactly the same as\n# $x = Integer('80', 16)\n# ```\n#\n# @example Creating an instance of String using a predefined format\n#\n# ```puppet\n# $fmt = Init[String,'%#x']\n# notice($fmt(256)) # will notice '0x100'\n# ```\n#\n# @since 4.5.0\n#\nPuppet::Functions.create_function(:new, Puppet::Functions::InternalFunction) do\n  dispatch :new_instance do\n    scope_param\n    param          'Type', :type\n    repeated_param 'Any',  :args\n    optional_block_param\n  end\n\n  def new_instance(scope, t, *args)\n    return args[0] if args.size == 1 && !t.is_a?(Puppet::Pops::Types::PInitType) && t.instance?(args[0])\n\n    result = assert_type(t, new_function_for_type(t).call(scope, *args))\n    block_given? ? yield(result) : result\n  end\n\n  def new_function_for_type(t)\n    @new_function_cache ||= {}\n\n    unless @new_function_cache.key?(t)\n      @new_function_cache[t] = t.new_function.new(nil, loader)\n    end\n\n    @new_function_cache[t]\n  end\n\n  def assert_type(type, value)\n    Puppet::Pops::Types::TypeAsserter.assert_instance_of(['Converted value from %s.new()', type], type, value)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/next.rb",
    "content": "# frozen_string_literal: true\n\n# Makes iteration continue with the next value, optionally with a given value for this iteration.\n# If a value is not given it defaults to `undef`\n#\n# @example Using the `next()` function\n#\n# ```puppet\n# $data = ['a','b','c']\n# $data.each |Integer $index, String $value| {\n#   if $index == 1 {\n#     next()\n#   }\n#   notice (\"${index} = ${value}\")\n# }\n# ```\n#\n# Would notice:\n# ```\n# Notice: Scope(Class[main]): 0 = a\n# Notice: Scope(Class[main]): 2 = c\n# ```\n#\n# @since 4.7.0\nPuppet::Functions.create_function(:next) do\n  dispatch :next_impl do\n    optional_param 'Any', :value\n  end\n\n  def next_impl(value = nil)\n    file, line = Puppet::Pops::PuppetStack.top_of_stack\n    exc = Puppet::Pops::Evaluator::Next.new(value, file, line)\n    raise exc\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/notice.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `notice`.\nPuppet::Functions.create_function(:notice, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :notice do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def notice(scope, *values)\n    Puppet::Util::Log.log_func(scope, :notice, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/partition.rb",
    "content": "# frozen_string_literal: true\n\n# Returns two arrays, the first containing the elements of enum for which the block evaluates to true,\n# the second containing the rest.\nPuppet::Functions.create_function(:partition) do\n  # @param collection A collection of things to partition.\n  # @example Partition array of empty strings, results in e.g. `[[''], [b, c]]`\n  #   ```puppet\n  #   ['', b, c].partition |$s| { $s.empty }\n  #   ```\n  # @example Partition array of strings using index, results in e.g. `[['', 'ab'], ['b']]`\n  #   ```puppet\n  #   ['', b, ab].partition |$i, $s| { $i == 2 or $s.empty }\n  #   ```\n  # @example Partition hash of strings by key-value pair, results in e.g. `[[['b', []]], [['a', [1, 2]]]]`\n  #   ```puppet\n  #   { a => [1, 2], b => [] }.partition |$kv| { $kv[1].empty }\n  #   ```\n  # @example Partition hash of strings by key and value, results in e.g. `[[['b', []]], [['a', [1, 2]]]]`\n  #   ```puppet\n  #   { a => [1, 2], b => [] }.partition |$k, $v| { $v.empty }\n  #   ```\n  dispatch :partition_1 do\n    required_param 'Collection', :collection\n    block_param 'Callable[1,1]', :block\n    return_type 'Tuple[Array, Array]'\n  end\n\n  dispatch :partition_2a do\n    required_param 'Array', :array\n    block_param 'Callable[2,2]', :block\n    return_type 'Tuple[Array, Array]'\n  end\n\n  dispatch :partition_2 do\n    required_param 'Collection', :collection\n    block_param 'Callable[2,2]', :block\n    return_type 'Tuple[Array, Array]'\n  end\n\n  def partition_1(collection)\n    collection.partition do |item|\n      yield(item)\n    end.freeze\n  end\n\n  def partition_2a(array)\n    partitioned = array.size.times.zip(array).partition do |k, v|\n      yield(k, v)\n    end\n\n    partitioned.map do |part|\n      part.map { |item| item[1] }\n    end.freeze\n  end\n\n  def partition_2(collection)\n    collection.partition do |k, v|\n      yield(k, v)\n    end.freeze\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/reduce.rb",
    "content": "# frozen_string_literal: true\n\n# Applies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# to every value in a data structure from the first argument, carrying over the returned\n# value of each iteration, and returns the result of the lambda's final iteration. This\n# lets you create a new value or data structure by combining values from the first\n# argument's data structure.\n#\n# This function takes two mandatory arguments, in this order:\n#\n# 1. An array, hash, or other iterable object that the function will iterate over.\n# 2. A lambda, which the function calls for each element in the first argument. It takes\n# two mandatory parameters:\n#     1. A memo value that is overwritten after each iteration with the iteration's result.\n#     2. A second value that is overwritten after each iteration with the next value in the\n#     function's first argument.\n#\n# @example Using the `reduce` function\n#\n# `$data.reduce |$memo, $value| { ... }`\n#\n# or\n#\n# `reduce($data) |$memo, $value| { ... }`\n#\n# You can also pass an optional \"start memo\" value as an argument, such as `start` below:\n#\n# `$data.reduce(start) |$memo, $value| { ... }`\n#\n# or\n#\n# `reduce($data, start) |$memo, $value| { ... }`\n#\n# When the first argument (`$data` in the above example) is an array, Puppet passes each\n# of the data structure's values in turn to the lambda's parameters. When the first\n# argument is a hash, Puppet converts each of the hash's values to an array in the form\n# `[key, value]`.\n#\n# If you pass a start memo value, Puppet executes the lambda with the provided memo value\n# and the data structure's first value. Otherwise, Puppet passes the structure's first two\n# values to the lambda.\n#\n# Puppet calls the lambda for each of the data structure's remaining values. For each\n# call, it passes the result of the previous call as the first parameter (`$memo` in the\n# above examples) and the next value from the data structure as the second parameter\n# (`$value`).\n#\n# @example Using the `reduce` function\n#\n# ```puppet\n# # Reduce the array $data, returning the sum of all values in the array.\n# $data = [1, 2, 3]\n# $sum = $data.reduce |$memo, $value| { $memo + $value }\n# # $sum contains 6\n#\n# # Reduce the array $data, returning the sum of a start memo value and all values in the\n# # array.\n# $data = [1, 2, 3]\n# $sum = $data.reduce(4) |$memo, $value| { $memo + $value }\n# # $sum contains 10\n#\n# # Reduce the hash $data, returning the sum of all values and concatenated string of all\n# # keys.\n# $data = {a => 1, b => 2, c => 3}\n# $combine = $data.reduce |$memo, $value| {\n#   $string = \"${memo[0]}${value[0]}\"\n#   $number = $memo[1] + $value[1]\n#   [$string, $number]\n# }\n# # $combine contains [abc, 6]\n# ```\n#\n# @example Using the `reduce` function with a start memo and two-parameter lambda\n#\n# ```puppet\n# # Reduce the array $data, returning the sum of all values in the array and starting\n# # with $memo set to an arbitrary value instead of $data's first value.\n# $data = [1, 2, 3]\n# $sum = $data.reduce(4) |$memo, $value| { $memo + $value }\n# # At the start of the lambda's first iteration, $memo contains 4 and $value contains 1.\n# # After all iterations, $sum contains 10.\n#\n# # Reduce the hash $data, returning the sum of all values and concatenated string of\n# # all keys, and starting with $memo set to an arbitrary array instead of $data's first\n# # key-value pair.\n# $data = {a => 1, b => 2, c => 3}\n# $combine = $data.reduce( [d, 4] ) |$memo, $value| {\n#   $string = \"${memo[0]}${value[0]}\"\n#   $number = $memo[1] + $value[1]\n#   [$string, $number]\n# }\n# # At the start of the lambda's first iteration, $memo contains [d, 4] and $value\n# # contains [a, 1].\n# # $combine contains [dabc, 10]\n# ```\n#\n# @example Using the `reduce` function to reduce a hash of hashes\n#\n# ```puppet\n# # Reduce a hash of hashes $data, merging defaults into the inner hashes.\n# $data = {\n#   'connection1' => {\n#     'username' => 'user1',\n#     'password' => 'pass1',\n#   },\n#   'connection_name2' => {\n#     'username' => 'user2',\n#     'password' => 'pass2',\n#   },\n# }\n#\n# $defaults = {\n#   'maxActive' => '20',\n#   'maxWait'   => '10000',\n#   'username'  => 'defaultuser',\n#   'password'  => 'defaultpass',\n# }\n#\n# $merged = $data.reduce( {} ) |$memo, $x| {\n#   $memo + { $x[0] => $defaults + $data[$x[0]] }\n# }\n# # At the start of the lambda's first iteration, $memo is set to {}, and $x is set to\n# # the first [key, value] tuple. The key in $data is, therefore, given by $x[0]. In\n# # subsequent rounds, $memo retains the value returned by the expression, i.e.\n# # $memo + { $x[0] => $defaults + $data[$x[0]] }.\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:reduce) do\n  dispatch :reduce_without_memo do\n    param 'Iterable', :enumerable\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :reduce_with_memo do\n    param 'Iterable', :enumerable\n    param 'Any', :memo\n    block_param 'Callable[2,2]', :block\n  end\n\n  def reduce_without_memo(enumerable)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    enum.reduce do |memo, x|\n      yield(memo, x)\n    rescue StopIteration\n      return memo\n    end\n  end\n\n  def reduce_with_memo(enumerable, given_memo)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    enum.reduce(given_memo) do |memo, x|\n      yield(memo, x)\n    rescue StopIteration\n      return memo\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/regsubst.rb",
    "content": "# frozen_string_literal: true\n\n# Performs regexp replacement on a string or array of strings.\nPuppet::Functions.create_function(:regsubst) do\n  # @param target [String]\n  #      The string or array of strings to operate on.  If an array, the replacement will be\n  #      performed on each of the elements in the array, and the return value will be an array.\n  # @param pattern [String, Regexp, Type[Regexp]]\n  #      The regular expression matching the target string.  If you want it anchored at the start\n  #      and or end of the string, you must do that with ^ and $ yourself.\n  # @param replacement [String, Hash[String, String]]\n  #      Replacement string. Can contain backreferences to what was matched using \\\\0 (whole match),\n  #      \\\\1 (first set of parentheses), and so on.\n  #      If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.\n  # @param flags [Optional[Pattern[/^[GEIM]*$/]], Pattern[/^G?$/]]\n  #      Optional. String of single letter flags for how the regexp is interpreted (E, I, and M cannot be used\n  #      if pattern is a precompiled regexp):\n  #        - *E*         Extended regexps\n  #        - *I*         Ignore case in regexps\n  #        - *M*         Multiline regexps\n  #        - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.\n  # @param encoding [Enum['N','E','S','U']]\n  #      Deprecated and ignored parameter, included only for compatibility.\n  # @return [Array[String], String] The result of the substitution. Result type is the same as for the target parameter.\n  # @deprecated\n  #   This method has the optional encoding parameter, which is ignored.\n  # @example Get the third octet from the node's IP address:\n  #   ```puppet\n  #   $i3 = regsubst($ipaddress,'^(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)$','\\\\3')\n  #   ```\n  dispatch :regsubst_string do\n    param          'Variant[Array[Variant[String,Sensitive[String]]],Sensitive[Array[Variant[String,Sensitive[String]]]],Variant[String,Sensitive[String]]]', :target\n    param          'String',                              :pattern\n    param          'Variant[String,Hash[String,String]]', :replacement\n    optional_param 'Optional[Pattern[/^[GEIM]*$/]]',      :flags\n    optional_param \"Enum['N','E','S','U']\",               :encoding\n  end\n\n  # @param target [String, Array[String]]\n  #      The string or array of strings to operate on.  If an array, the replacement will be\n  #      performed on each of the elements in the array, and the return value will be an array.\n  # @param pattern [Regexp, Type[Regexp]]\n  #      The regular expression matching the target string.  If you want it anchored at the start\n  #      and or end of the string, you must do that with ^ and $ yourself.\n  # @param replacement [String, Hash[String, String]]\n  #      Replacement string. Can contain backreferences to what was matched using \\\\0 (whole match),\n  #      \\\\1 (first set of parentheses), and so on.\n  #      If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.\n  # @param flags [Optional[Pattern[/^[GEIM]*$/]], Pattern[/^G?$/]]\n  #      Optional. String of single letter flags for how the regexp is interpreted (E, I, and M cannot be used\n  #      if pattern is a precompiled regexp):\n  #        - *E*         Extended regexps\n  #        - *I*         Ignore case in regexps\n  #        - *M*         Multiline regexps\n  #        - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.\n  # @return [Array[String], String] The result of the substitution. Result type is the same as for the target parameter.\n  # @example Put angle brackets around each octet in the node's IP address:\n  #   ```puppet\n  #   $x = regsubst($ipaddress, /([0-9]+)/, '<\\\\1>', 'G')\n  #   ```\n  dispatch :regsubst_regexp do\n    param          'Variant[Array[Variant[String,Sensitive[String]]],Sensitive[Array[Variant[String,Sensitive[String]]]],Variant[String,Sensitive[String]]]', :target\n    param          'Variant[Regexp,Type[Regexp]]',        :pattern\n    param          'Variant[String,Hash[String,String]]', :replacement\n    optional_param 'Pattern[/^G?$/]',                     :flags\n  end\n\n  def regsubst_string(target, pattern, replacement, flags = nil, encoding = nil)\n    if encoding\n      Puppet.warn_once(\n        'deprecations', 'regsubst_function_encoding',\n        _(\"The regsubst() function's encoding argument has been ignored since Ruby 1.9 and will be removed in a future release\")\n      )\n    end\n\n    re_flags = 0\n    operation = :sub\n    unless flags.nil?\n      flags.split(//).each do |f|\n        case f\n        when 'G' then operation = :gsub\n        when 'E' then re_flags |= Regexp::EXTENDED\n        when 'I' then re_flags |= Regexp::IGNORECASE\n        when 'M' then re_flags |= Regexp::MULTILINE\n        end\n      end\n    end\n    inner_regsubst(target, Regexp.compile(pattern, re_flags), replacement, operation)\n  end\n\n  def regsubst_regexp(target, pattern, replacement, flags = nil)\n    pattern = pattern.pattern || '' if pattern.is_a?(Puppet::Pops::Types::PRegexpType)\n    inner_regsubst(target, pattern, replacement, flags == 'G' ? :gsub : :sub)\n  end\n\n  def inner_regsubst(target, re, replacement, op)\n    if target.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive) && target.unwrap.is_a?(Array)\n      # this is a Sensitive Array\n      target = target.unwrap\n      target.map do |item|\n        inner_regsubst(item, re, replacement, op)\n      end\n    elsif target.is_a?(Array)\n      # this is an Array\n      target.map do |item|\n        inner_regsubst(item, re, replacement, op)\n      end\n    elsif target.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      # this is a Sensitive\n      target = target.unwrap\n      target = target.respond_to?(op) ? target.send(op, re, replacement) : target.map { |e| e.send(op, re, replacement) }\n      Puppet::Pops::Types::PSensitiveType::Sensitive.new(target)\n    else\n      # this should be a String\n      target.respond_to?(op) ? target.send(op, re, replacement) : target.map { |e| e.send(op, re, replacement) }\n    end\n  end\n  private :inner_regsubst\nend\n"
  },
  {
    "path": "lib/puppet/functions/require.rb",
    "content": "# frozen_string_literal: true\n\n# Requires the specified classes.\n# Evaluate one or more classes, adding the required class as a dependency.\n#\n# The relationship metaparameters work well for specifying relationships\n# between individual resources, but they can be clumsy for specifying\n# relationships between classes.  This function is a superset of the\n# `include` function, adding a class relationship so that the requiring\n# class depends on the required class.\n#\n# Warning: using `require` in place of `include` can lead to unwanted dependency cycles.\n#\n# For instance, the following manifest, with `require` instead of `include`, would produce a nasty\n# dependence cycle, because `notify` imposes a `before` between `File[/foo]` and `Service[foo]`:\n#\n# ```puppet\n# class myservice {\n#   service { foo: ensure => running }\n# }\n#\n# class otherstuff {\n#    include myservice\n#    file { '/foo': notify => Service[foo] }\n# }\n# ```\n#\n# Note that this function only works with clients 0.25 and later, and it will\n# fail if used with earlier clients.\n#\n# You must use the class's full name;\n# relative names are not allowed. In addition to names in string form,\n# you may also directly use Class and Resource Type values that are produced when evaluating\n# resource and relationship expressions.\n#\n# - Since 4.0.0 Class and Resource types, absolute names\n# - Since 4.7.0 Returns an `Array[Type[Class]]` with references to the required classes\n#\nPuppet::Functions.create_function(:require, Puppet::Functions::InternalFunction) do\n  dispatch :require_impl do\n    scope_param\n    # The function supports what the type system sees as Ruby runtime objects, and\n    # they cannot be parameterized to find what is actually valid instances.\n    # The validation is instead done in the function body itself via a call to\n    # `transform_and_assert_classnames` on the calling scope.\n    required_repeated_param 'Any', :names\n  end\n\n  def require_impl(scope, *classes)\n    if Puppet[:tasks]\n      raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n        Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n        { :operation => 'require' }\n      )\n    end\n\n    # Make call patterns uniform and protected against nested arrays, also make\n    # names absolute if so desired.\n    classes = scope.transform_and_assert_classnames(classes.flatten)\n\n    result = classes.map { |name| Puppet::Pops::Types::TypeFactory.host_class(name) }\n\n    # This is the same as calling the include function (but faster) since it again\n    # would otherwise need to perform the optional absolute name transformation\n    # (for no reason since they are already made absolute here).\n    #\n    scope.compiler.evaluate_classes(classes, scope, false)\n    krt = scope.environment.known_resource_types\n\n    classes.each do |klass|\n      # lookup the class in the scopes\n      klass = (classobj = krt.find_hostclass(klass)) ? classobj.name : nil\n      raise Puppet::ParseError, _(\"Could not find class %{klass}\") % { klass: klass } unless klass\n\n      ref = Puppet::Resource.new(:class, klass)\n      resource = scope.resource\n      resource.set_parameter(:require, [resource[:require]].flatten.compact << ref)\n    end\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/return.rb",
    "content": "# frozen_string_literal: true\n\n# Makes iteration continue with the next value, optionally with a given value for this iteration.\n# If a value is not given it defaults to `undef`\n#\n# @since 4.7.0\n#\nPuppet::Functions.create_function(:return, Puppet::Functions::InternalFunction) do\n  dispatch :return_impl do\n    optional_param 'Any', :value\n  end\n\n  def return_impl(value = nil)\n    file, line = Puppet::Pops::PuppetStack.top_of_stack\n    raise Puppet::Pops::Evaluator::Return.new(value, file, line)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/reverse_each.rb",
    "content": "# frozen_string_literal: true\n\n# Reverses the order of the elements of something that is iterable and optionally runs a\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) for each\n# element.\n#\n# This function takes one to two arguments:\n#\n# 1. An `Iterable` that the function will iterate over.\n# 2. An optional lambda, which the function calls for each element in the first argument. It must\n#    request one parameter.\n#\n# @example Using the `reverse_each` function\n#\n# ```puppet\n# $data.reverse_each |$parameter| { <PUPPET CODE BLOCK> }\n# ```\n#\n# or\n#\n# ```puppet\n# $reverse_data = $data.reverse_each\n# ```\n#\n# or\n#\n# ```puppet\n# reverse_each($data) |$parameter| { <PUPPET CODE BLOCK> }\n# ```\n#\n# or\n#\n# ```puppet\n# $reverse_data = reverse_each($data)\n# ```\n#\n# When no second argument is present, Puppet returns an `Iterable` that represents the reverse\n# order of its first argument. This allows methods on `Iterable` to be chained.\n#\n# When a lambda is given as the second argument, Puppet iterates the first argument in reverse\n# order and passes each value in turn to the lambda, then returns `undef`.\n#\n# @example Using the `reverse_each` function with an array and a one-parameter lambda\n#\n# ```puppet\n# # Puppet will log a notice for each of the three items\n# # in $data in reverse order.\n# $data = [1,2,3]\n# $data.reverse_each |$item| { notice($item) }\n# ```\n#\n# When no second argument is present, Puppet returns a new `Iterable` which allows it to\n# be directly chained into another function that takes an `Iterable` as an argument.\n#\n# @example Using the `reverse_each` function chained with a `map` function.\n#\n# ```puppet\n# # For the array $data, return an array containing each\n# # value multiplied by 10 in reverse order\n# $data = [1,2,3]\n# $transformed_data = $data.reverse_each.map |$item| { $item * 10 }\n# # $transformed_data is set to [30,20,10]\n# ```\n#\n# @example Using `reverse_each` function chained with a `map` in alternative syntax\n#\n# ```puppet\n# # For the array $data, return an array containing each\n# # value multiplied by 10 in reverse order\n# $data = [1,2,3]\n# $transformed_data = map(reverse_each($data)) |$item| { $item * 10 }\n# # $transformed_data is set to [30,20,10]\n# ```\n#\n# @since 4.4.0\n#\nPuppet::Functions.create_function(:reverse_each) do\n  dispatch :reverse_each do\n    param 'Iterable', :iterable\n  end\n\n  dispatch :reverse_each_block do\n    param 'Iterable', :iterable\n    block_param 'Callable[1,1]', :block\n  end\n\n  def reverse_each(iterable)\n    # produces an Iterable\n    Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable, true).reverse_each\n  end\n\n  def reverse_each_block(iterable, &block)\n    Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).reverse_each(&block)\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/round.rb",
    "content": "# frozen_string_literal: true\n\n# Returns an `Integer` value rounded to the nearest value.\n# Takes a single `Numeric` value as an argument.\n#\n# @example 'rounding a value'\n#\n# ```puppet\n# notice(round(2.9)) # would notice 3\n# notice(round(2.1)) # would notice 2\n# notice(round(-2.9)) # would notice -3\n# ```\n#\nPuppet::Functions.create_function(:round) do\n  dispatch :on_numeric do\n    param 'Numeric', :val\n  end\n\n  def on_numeric(x)\n    if x > 0\n      Integer(x + 0.5)\n    else\n      Integer(x - 0.5)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/rstrip.rb",
    "content": "# frozen_string_literal: true\n\n# Strips trailing spaces from a String\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String` the conversion removes all trailing ASCII white space characters such as space, tab, newline, and return.\n#   It does not remove other space-like characters like hard space (Unicode U+00A0). (Tip, `/^[[:space:]]/` regular expression\n#   matches all space-like characters).\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# @example Removing trailing space from a String\n# ```puppet\n# \" hello\\n\\t\".rstrip()\n# rstrip(\" hello\\n\\t\")\n# ```\n# Would both result in `\"hello\"`\n#\n# @example Removing trailing space from strings in an Array\n# ```puppet\n# [\" hello\\n\\t\", \" hi\\n\\t\"].rstrip()\n# rstrip([\" hello\\n\\t\", \" hi\\n\\t\"])\n# ```\n# Would both result in `['hello', 'hi']`\n#\nPuppet::Functions.create_function(:rstrip) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.rstrip\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_rstrip(x) }\n  end\n\n  def do_rstrip(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? x.rstrip : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/scanf.rb",
    "content": "# frozen_string_literal: true\n\n# Scans a string and returns an array of one or more converted values based on the given format string.\n# See the documentation of Ruby's String#scanf method for details about the supported formats (which\n# are similar but not identical to the formats used in Puppet's `sprintf` function.)\n#\n# This function takes two mandatory arguments: the first is the string to convert, and the second is\n# the format string. The result of the scan is an array, with each successfully scanned and transformed value.\n# The scanning stops if a scan is unsuccessful, and the scanned result up to that point is returned. If there\n# was no successful scan, the result is an empty array.\n#\n#    \"42\".scanf(\"%i\")\n#\n# You can also optionally pass a lambda to scanf, to do additional validation or processing.\n#\n#\n#     \"42\".scanf(\"%i\") |$x| {\n#       unless $x[0] =~ Integer {\n#         fail \"Expected a well formed integer value, got '$x[0]'\"\n#       }\n#       $x[0]\n#     }\n#\n#\n#\n#\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:scanf) do\n  require 'scanf'\n\n  dispatch :scanf do\n    param 'String', :data\n    param 'String', :format\n    optional_block_param\n  end\n\n  def scanf(data, format)\n    result = data.scanf(format)\n    if block_given?\n      result = yield(result)\n    end\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/size.rb",
    "content": "# frozen_string_literal: true\n\n# The same as length() - returns the size of an Array, Hash, String, or Binary value.\n#\n# @since 6.0.0 - also supporting Binary\n#\nPuppet::Functions.create_function(:size) do\n  dispatch :generic_size do\n    param 'Variant[Collection, String, Binary]', :arg\n  end\n\n  def generic_size(arg)\n    call_function('length', arg)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/slice.rb",
    "content": "# frozen_string_literal: true\n\n# Slices an array or hash into pieces of a given size.\n#\n# This function takes two mandatory arguments: the first should be an array or hash, and the second specifies\n# the number of elements to include in each slice.\n#\n# When the first argument is a hash, each key value pair is counted as one. For example, a slice size of 2 will produce\n# an array of two arrays with key, and value.\n#\n# @example Slicing a Hash\n#\n# ```puppet\n# $a.slice(2) |$entry|          { notice \"first ${$entry[0]}, second ${$entry[1]}\" }\n# $a.slice(2) |$first, $second| { notice \"first ${first}, second ${second}\" }\n# ```\n# The function produces a concatenated result of the slices.\n#\n# @example Slicing an Array\n#\n# ```puppet\n# slice([1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]]\n# slice(Integer[1,6], 2)  # produces [[1,2], [3,4], [5,6]]\n# slice(4,2)              # produces [[0,1], [2,3]]\n# slice('hello',2)        # produces [[h, e], [l, l], [o]]\n# ```\n#\n# @example Passing a lambda to a slice (optional)\n#\n# ```puppet\n#  $a.slice($n) |$x| { ... }\n#  slice($a) |$x| { ... }\n# ```\n#\n# The lambda should have either one parameter (receiving an array with the slice), or the same number\n# of parameters as specified by the slice size (each parameter receiving its part of the slice).\n# If there are fewer remaining elements than the slice size for the last slice, it will contain the remaining\n# elements. If the lambda has multiple parameters, excess parameters are set to undef for an array, or\n# to empty arrays for a hash.\n#\n# @example Getting individual values of a slice\n#\n# ```puppet\n#     $a.slice(2) |$first, $second| { ... }\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:slice) do\n  dispatch :slice_Hash do\n    param 'Hash[Any, Any]', :hash\n    param 'Integer[1, default]', :slice_size\n    optional_block_param\n  end\n\n  dispatch :slice_Enumerable do\n    param 'Iterable', :enumerable\n    param 'Integer[1, default]', :slice_size\n    optional_block_param\n  end\n\n  def slice_Hash(hash, slice_size, &pblock)\n    result = slice_Common(hash, slice_size, [], block_given? ? pblock : nil)\n    block_given? ? hash : result\n  end\n\n  def slice_Enumerable(enumerable, slice_size, &pblock)\n    enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)\n    result = slice_Common(enum, slice_size, nil, block_given? ? pblock : nil)\n    block_given? ? enumerable : result\n  end\n\n  def slice_Common(o, slice_size, filler, pblock)\n    serving_size = asserted_slice_serving_size(pblock, slice_size)\n\n    enumerator = o.each_slice(slice_size)\n    result = []\n    if serving_size == 1\n      begin\n        if pblock\n          loop do\n            pblock.call(enumerator.next)\n          end\n        else\n          loop do\n            result << enumerator.next\n          end\n        end\n      rescue StopIteration\n      end\n    else\n      begin\n        loop do\n          a = enumerator.next\n          if a.size < serving_size\n            a = a.dup.fill(filler, a.length...serving_size)\n          end\n          pblock.call(*a)\n        end\n      rescue StopIteration\n      end\n    end\n    if pblock\n      o\n    else\n      result\n    end\n  end\n\n  def asserted_slice_serving_size(pblock, slice_size)\n    if pblock\n      arity = pblock.arity\n      serving_size = arity < 0 ? slice_size : arity\n    else\n      serving_size = 1\n    end\n    if serving_size == 0\n      raise ArgumentError, _(\"slice(): block must define at least one parameter. Block has 0.\")\n    end\n\n    unless serving_size == 1 || serving_size == slice_size\n      raise ArgumentError, _(\"slice(): block must define one parameter, or the same number of parameters as the given size of the slice (%{slice_size}). Block has %{serving_size}; %{parameter_names}\") %\n                           { slice_size: slice_size, serving_size: serving_size, parameter_names: pblock.parameter_names.join(', ') }\n    end\n    serving_size\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/sort.rb",
    "content": "# frozen_string_literal: true\n\n# Sorts an Array numerically or lexicographically or the characters of a String lexicographically.\n# Please note: This function is based on Ruby String comparison and as such may not be entirely UTF8 compatible.\n# To ensure compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n#\n# This function is compatible with the function `sort()` in `stdlib`.\n# * Comparison of characters in a string always uses a system locale and may not be what is expected for a particular locale\n# * Sorting is based on Ruby's `<=>` operator unless a lambda is given that performs the comparison.\n#   * comparison of strings is case dependent (use lambda with `compare($a,$b)` to ignore case)\n#   * comparison of mixed data types raises an error (if there is the need to sort mixed data types use a lambda)\n#\n# Also see the `compare()` function for information about comparable data types in general.\n#\n# @example Sorting a String\n#\n# ```puppet\n# notice(sort(\"xadb\")) # notices 'abdx'\n# ```\n#\n# @example Sorting an Array\n#\n# ```puppet\n# notice(sort([3,6,2])) # notices [2, 3, 6]\n# ```\n#\n# @example Sorting with a lambda\n#\n# ```puppet\n# notice(sort([3,6,2]) |$a,$b| { compare($a, $b) }) # notices [2, 3, 6]\n# notice(sort([3,6,2]) |$a,$b| { compare($b, $a) }) # notices [6, 3, 2]\n# ```\n#\n# @example Case independent sorting with a lambda\n#\n# ```puppet\n# notice(sort(['A','b','C']))                                    # notices ['A', 'C', 'b']\n# notice(sort(['A','b','C']) |$a,$b| { compare($a, $b) })        # notices ['A', 'b', 'C']\n# notice(sort(['A','b','C']) |$a,$b| { compare($a, $b, true) })  # notices ['A', 'b', 'C']\n# notice(sort(['A','b','C']) |$a,$b| { compare($a, $b, false) }) # notices ['A','C', 'b']\n# ```\n#\n# @example Sorting Array with Numeric and String so that numbers are before strings\n#\n# ```puppet\n# notice(sort(['b', 3, 'a', 2]) |$a, $b| {\n#   case [$a, $b] {\n#     [String, Numeric] : { 1 }\n#     [Numeric, String] : { -1 }\n#     default:            { compare($a, $b) }\n#   }\n# })\n# ```\n# Would notice `[2,3,'a','b']`\n#\n# @since 6.0.0 - supporting a lambda to do compare\n#\nPuppet::Functions.create_function(:sort) do\n  dispatch :sort_string do\n    param 'String', :string_value\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :sort_array do\n    param 'Array', :array_value\n    optional_block_param 'Callable[2,2]', :block\n  end\n\n  def sort_string(s, &block)\n    sort_array(s.split(''), &block).join('')\n  end\n\n  def sort_array(a, &block)\n    a.sort(&block)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/split.rb",
    "content": "# frozen_string_literal: true\n\n# Splits a string into an array using a given pattern.\n# The pattern can be a string, regexp or regexp type.\n#\n# @example Splitting a String value\n#\n# ```puppet\n# $string     = 'v1.v2:v3.v4'\n# $array_var1 = split($string, /:/)\n# $array_var2 = split($string, '[.]')\n# $array_var3 = split($string, Regexp['[.:]'])\n#\n# #`$array_var1` now holds the result `['v1.v2', 'v3.v4']`,\n# # while `$array_var2` holds `['v1', 'v2:v3', 'v4']`, and\n# # `$array_var3` holds `['v1', 'v2', 'v3', 'v4']`.\n# ```\n#\n# Note that in the second example, we split on a literal string that contains\n# a regexp meta-character (`.`), which must be escaped.  A simple\n# way to do that for a single character is to enclose it in square\n# brackets; a backslash will also escape a single character.\n#\nPuppet::Functions.create_function(:split) do\n  dispatch :split_String do\n    param 'String', :str\n    param 'String', :pattern\n  end\n\n  dispatch :split_Regexp do\n    param 'String', :str\n    param 'Regexp', :pattern\n  end\n\n  dispatch :split_RegexpType do\n    param 'String', :str\n    param 'Type[Regexp]', :pattern\n  end\n\n  dispatch :split_String_sensitive do\n    param 'Sensitive[String]', :sensitive\n    param 'String', :pattern\n  end\n\n  dispatch :split_Regexp_sensitive do\n    param 'Sensitive[String]', :sensitive\n    param 'Regexp', :pattern\n  end\n\n  dispatch :split_RegexpType_sensitive do\n    param 'Sensitive[String]', :sensitive\n    param 'Type[Regexp]', :pattern\n  end\n\n  def split_String(str, pattern)\n    str.split(Regexp.compile(pattern))\n  end\n\n  def split_Regexp(str, pattern)\n    str.split(pattern)\n  end\n\n  def split_RegexpType(str, pattern)\n    str.split(pattern.regexp)\n  end\n\n  def split_String_sensitive(sensitive, pattern)\n    Puppet::Pops::Types::PSensitiveType::Sensitive.new(split_String(sensitive.unwrap, pattern))\n  end\n\n  def split_Regexp_sensitive(sensitive, pattern)\n    Puppet::Pops::Types::PSensitiveType::Sensitive.new(split_Regexp(sensitive.unwrap, pattern))\n  end\n\n  def split_RegexpType_sensitive(sensitive, pattern)\n    Puppet::Pops::Types::PSensitiveType::Sensitive.new(split_RegexpType(sensitive.unwrap, pattern))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/step.rb",
    "content": "# frozen_string_literal: true\n\n# Provides stepping with given interval over elements in an iterable and optionally runs a\n# [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) for each\n# element.\n#\n# This function takes two to three arguments:\n#\n# 1. An 'Iterable' that the function will iterate over.\n# 2. An `Integer` step factor. This must be a positive integer.\n# 3. An optional lambda, which the function calls for each element in the interval. It must\n#    request one parameter.\n#\n# @example Using the `step` function\n#\n# ```puppet\n# $data.step(<n>) |$parameter| { <PUPPET CODE BLOCK> }\n# ```\n#\n# or\n#\n# ```puppet\n# $stepped_data = $data.step(<n>)\n# ```\n#\n# or\n#\n# ```puppet\n# step($data, <n>) |$parameter| { <PUPPET CODE BLOCK> }\n# ```\n#\n# or\n#\n# ```puppet\n# $stepped_data = step($data, <n>)\n# ```\n#\n# When no block is given, Puppet returns an `Iterable` that yields the first element and every nth successor\n# element, from its first argument. This allows functions on iterables to be chained.\n# When a block is given, Puppet iterates and calls the block with the first element and then with\n# every nth successor element. It then returns `undef`.\n#\n# @example Using the `step` function with an array, a step factor, and a one-parameter block\n#\n# ```puppet\n# # For the array $data, call a block with the first element and then with each 3rd successor element\n# $data = [1,2,3,4,5,6,7,8]\n# $data.step(3) |$item| {\n#  notice($item)\n# }\n# # Puppet notices the values '1', '4', '7'.\n# ```\n\n# When no block is given, Puppet returns a new `Iterable` which allows it to be directly chained into\n# another function that takes an `Iterable` as an argument.\n#\n# @example Using the `step` function chained with a `map` function.\n#\n# ```puppet\n# # For the array $data, return an array, set to the first element and each 5th successor element, in reverse\n# # order multiplied by 10\n# $data = Integer[0,20]\n# $transformed_data = $data.step(5).map |$item| { $item * 10 }\n# $transformed_data contains [0,50,100,150,200]\n# ```\n#\n# @example The same example using `step` function chained with a `map` in alternative syntax\n#\n# ```puppet\n# # For the array $data, return an array, set to the first and each 5th\n# # successor, in reverse order, multiplied by 10\n# $data = Integer[0,20]\n# $transformed_data = map(step($data, 5)) |$item| { $item * 10 }\n# $transformed_data contains [0,50,100,150,200]\n# ```\n#\n# @since 4.4.0\n#\nPuppet::Functions.create_function(:step) do\n  dispatch :step do\n    param 'Iterable', :iterable\n    param 'Integer[1]', :step\n  end\n\n  dispatch :step_block do\n    param 'Iterable', :iterable\n    param 'Integer[1]', :step\n    block_param 'Callable[1,1]', :block\n  end\n\n  def step(iterable, step)\n    # produces an Iterable\n    Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable, true).step(step)\n  end\n\n  def step_block(iterable, step, &block)\n    Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).step(step, &block)\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/strftime.rb",
    "content": "# frozen_string_literal: true\n\n# Formats timestamp or timespan according to the directives in the given format string. The directives begins with a percent (%) character.\n# Any text not listed as a directive will be passed through to the output string.\n#\n# A third optional timezone argument can be provided. The first argument will then be formatted to represent a local time in that\n# timezone. The timezone can be any timezone that is recognized when using the '%z' or '%Z' formats, or the word 'current', in which\n# case the current timezone of the evaluating process will be used. The timezone argument is case insensitive.\n#\n# The default timezone, when no argument is provided, or when using the keyword `default`, is 'UTC'.\n#\n# The directive consists of a percent (%) character, zero or more flags, optional minimum field width and\n# a conversion specifier as follows:\n#\n# ```\n# %[Flags][Width]Conversion\n# ```\n#\n# ### Flags that controls padding\n#\n# | Flag  | Meaning\n# | ----  | ---------------\n# | -     | Don't pad numerical output\n# | _     | Use spaces for padding\n# | 0     | Use zeros for padding\n#\n# ### `Timestamp` specific flags\n#\n# | Flag  | Meaning\n# | ----  | ---------------\n# | #     | Change case\n# | ^     | Use uppercase\n# | :     | Use colons for %z\n#\n# ### Format directives applicable to `Timestamp` (names and padding can be altered using flags):\n#\n# **Date (Year, Month, Day):**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | Y | Year with century, zero-padded to at least 4 digits |\n# | C | year / 100 (rounded down such as 20 in 2009) |\n# | y | year % 100 (00..99) |\n# | m | Month of the year, zero-padded (01..12) |\n# | B | The full month name (\"January\") |\n# | b | The abbreviated month name (\"Jan\") |\n# | h | Equivalent to %b |\n# | d | Day of the month, zero-padded (01..31) |\n# | e | Day of the month, blank-padded ( 1..31) |\n# | j | Day of the year (001..366) |\n#\n# **Time (Hour, Minute, Second, Subsecond):**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | H | Hour of the day, 24-hour clock, zero-padded (00..23) |\n# | k | Hour of the day, 24-hour clock, blank-padded ( 0..23) |\n# | I | Hour of the day, 12-hour clock, zero-padded (01..12) |\n# | l | Hour of the day, 12-hour clock, blank-padded ( 1..12) |\n# | P | Meridian indicator, lowercase (\"am\" or \"pm\") |\n# | p | Meridian indicator, uppercase (\"AM\" or \"PM\") |\n# | M | Minute of the hour (00..59) |\n# | S | Second of the minute (00..60) |\n# | L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000 |\n# | N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified width are truncated to avoid carry up |\n#\n# **Time (Hour, Minute, Second, Subsecond):**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | z   | Time zone as hour and minute offset from UTC (e.g. +0900) |\n# | :z  | hour and minute offset from UTC with a colon (e.g. +09:00) |\n# | ::z | hour, minute and second offset from UTC (e.g. +09:00:00) |\n# | Z   | Abbreviated time zone name or similar information.  (OS dependent) |\n#\n# **Weekday:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | A | The full weekday name (\"Sunday\") |\n# | a | The abbreviated name (\"Sun\") |\n# | u | Day of the week (Monday is 1, 1..7) |\n# | w | Day of the week (Sunday is 0, 0..6) |\n#\n# **ISO 8601 week-based year and week number:**\n#\n# The first week of YYYY starts with a Monday and includes YYYY-01-04.\n# The days in the year before the first week are in the last week of\n# the previous year.\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | G | The week-based year |\n# | g | The last 2 digits of the week-based year (00..99) |\n# | V | Week number of the week-based year (01..53) |\n#\n# **Week number:**\n#\n# The first week of YYYY that starts with a Sunday or Monday (according to %U\n# or %W). The days in the year before the first week are in week 0.\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | U | Week number of the year. The week starts with Sunday. (00..53) |\n# | W | Week number of the year. The week starts with Monday. (00..53) |\n#\n# **Seconds since the Epoch:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | s | Number of seconds since 1970-01-01 00:00:00 UTC. |\n#\n# **Literal string:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | n | Newline character (\\n) |\n# | t | Tab character (\\t) |\n# | % | Literal \"%\" character |\n#\n# **Combination:**\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | c | date and time (%a %b %e %T %Y) |\n# | D | Date (%m/%d/%y) |\n# | F | The ISO 8601 date format (%Y-%m-%d) |\n# | v | VMS date (%e-%^b-%4Y) |\n# | x | Same as %D |\n# | X | Same as %T |\n# | r | 12-hour time (%I:%M:%S %p) |\n# | R | 24-hour time (%H:%M) |\n# | T | 24-hour time (%H:%M:%S) |\n#\n# @example Using `strftime` with a `Timestamp`:\n#\n# ```puppet\n# $timestamp = Timestamp('2016-08-24T12:13:14')\n#\n# # Notice the timestamp using a format that notices the ISO 8601 date format\n# notice($timestamp.strftime('%F')) # outputs '2016-08-24'\n#\n# # Notice the timestamp using a format that notices weekday, month, day, time (as UTC), and year\n# notice($timestamp.strftime('%c')) # outputs 'Wed Aug 24 12:13:14 2016'\n#\n# # Notice the timestamp using a specific timezone\n# notice($timestamp.strftime('%F %T %z', 'PST')) # outputs '2016-08-24 04:13:14 -0800'\n#\n# # Notice the timestamp using timezone that is current for the evaluating process\n# notice($timestamp.strftime('%F %T', 'current')) # outputs the timestamp using the timezone for the current process\n# ```\n#\n# ### Format directives applicable to `Timespan`:\n#\n# | Format | Meaning |\n# | ------ | ------- |\n# | D | Number of Days |\n# | H | Hour of the day, 24-hour clock |\n# | M | Minute of the hour (00..59) |\n# | S | Second of the minute (00..59) |\n# | L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000. |\n# | N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified length are truncated to avoid carry up |\n#\n# The format directive that represents the highest magnitude in the format will be allowed to overflow.\n# I.e. if no \"%D\" is used but a \"%H\" is present, then the hours will be more than 23 in case the\n# timespan reflects more than a day.\n#\n# @example Using `strftime` with a Timespan and a format\n#\n# ```puppet\n# $duration = Timespan({ hours => 3, minutes => 20, seconds => 30 })\n#\n# # Notice the duration using a format that outputs <hours>:<minutes>:<seconds>\n# notice($duration.strftime('%H:%M:%S')) # outputs '03:20:30'\n#\n# # Notice the duration using a format that outputs <minutes>:<seconds>\n# notice($duration.strftime('%M:%S')) # outputs '200:30'\n# ```\n#\n# - Since 4.8.0\n#\nPuppet::Functions.create_function(:strftime) do\n  dispatch :format_timespan do\n    param 'Timespan', :time_object\n    param 'String', :format\n  end\n\n  dispatch :format_timestamp do\n    param 'Timestamp', :time_object\n    param 'String', :format\n    optional_param 'String', :timezone\n  end\n\n  dispatch :legacy_strftime do\n    param 'String', :format\n    optional_param 'String', :timezone\n  end\n\n  def format_timespan(time_object, format)\n    time_object.format(format)\n  end\n\n  def format_timestamp(time_object, format, timezone = nil)\n    time_object.format(format, timezone)\n  end\n\n  def legacy_strftime(format, timezone = nil)\n    file, line = Puppet::Pops::PuppetStack.top_of_stack\n    Puppet.warn_once('deprecations', 'legacy#strftime',\n                     _('The argument signature (String format, [String timezone]) is deprecated for #strftime. See #strftime documentation and Timespan type for more info'),\n                     file, line)\n    Puppet::Pops::Time::Timestamp.format_time(format, Time.now.utc, timezone)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/strip.rb",
    "content": "# frozen_string_literal: true\n\n# Strips leading and trailing spaces from a String\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String` the conversion removes all leading and trailing ASCII white space characters such as space, tab, newline, and return.\n#   It does not remove other space-like characters like hard space (Unicode U+00A0). (Tip, `/^[[:space:]]/` regular expression\n#   matches all space-like characters).\n# * For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# @example Removing leading and trailing space from a String\n# ```puppet\n# \" hello\\n\\t\".strip()\n# strip(\" hello\\n\\t\")\n# ```\n# Would both result in `\"hello\"`\n#\n# @example Removing trailing space from strings in an Array\n# ```puppet\n# [\" hello\\n\\t\", \" hi\\n\\t\"].strip()\n# strip([\" hello\\n\\t\", \" hi\\n\\t\"])\n# ```\n# Would both result in `['hello', 'hi']`\n#\nPuppet::Functions.create_function(:strip) do\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_iterable do\n    param 'Iterable[Variant[String, Numeric]]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.strip\n  end\n\n  def on_iterable(a)\n    a.map { |x| do_strip(x) }\n  end\n\n  def do_strip(x)\n    # x can only be a String or Numeric because type constraints have been automatically applied\n    x.is_a?(String) ? x.strip : x\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/then.rb",
    "content": "# frozen_string_literal: true\n\n# Calls a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# with the given argument unless the argument is `undef`.\n# Returns `undef` if the argument is `undef`, and otherwise the result of giving the\n# argument to the lambda.\n#\n# This is useful to process a sequence of operations where an intermediate\n# result may be `undef` (which makes the entire sequence `undef`).\n# The `then` function is especially useful with the function `dig` which\n# performs in a similar way \"digging out\" a value in a complex structure.\n#\n# @example Using `dig` and `then`\n#\n# ```puppet\n# $data = {a => { b => [{x => 10, y => 20}, {x => 100, y => 200}]}}\n# notice $data.dig(a, b, 1, x).then |$x| { $x * 2 }\n# ```\n#\n# Would notice the value 200\n#\n# Contrast this with:\n#\n# ```puppet\n# $data = {a => { b => [{x => 10, y => 20}, {not_x => 100, why => 200}]}}\n# notice $data.dig(a, b, 1, x).then |$x| { $x * 2 }\n# ```\n#\n# Which would notice `undef` since the last lookup of 'x' results in `undef` which\n# is returned (without calling the lambda given to the `then` function).\n#\n# As a result there is no need for conditional logic or a temporary (non local)\n# variable as the result is now either the wanted value (`x`) multiplied\n# by 2 or `undef`.\n#\n# Calls to `then` can be chained. In the next example, a structure is using an offset based on\n# using 1 as the index to the first element (instead of 0 which is used in the language).\n# We are not sure if user input actually contains an index at all, or if it is\n# outside the range of available names.args.\n#\n# @example Chaining calls to the `then` function\n#\n# ```puppet\n# # Names to choose from\n# $names = ['Ringo', 'Paul', 'George', 'John']\n#\n# # Structure where 'beatle 2' is wanted (but where the number refers\n# # to 'Paul' because input comes from a source using 1 for the first\n# # element).\n#\n# $data = ['singer', { beatle => 2 }]\n# $picked = assert_type(String,\n#   # the data we are interested in is the second in the array,\n#   # a hash, where we want the value of the key 'beatle'\n#   $data.dig(1, 'beatle')\n#     # and we want the index in $names before the given index\n#     .then |$x| { $names[$x-1] }\n#     # so we can construct a string with that beatle's name\n#     .then |$x| { \"Picked Beatle '${x}'\" }\n# )\n# notice $picked\n# ```\n#\n# Would notice \"Picked Beatle 'Paul'\", and would raise an error if the result\n# was not a String.\n#\n# * Since 4.5.0\n#\nPuppet::Functions.create_function(:then) do\n  dispatch :then do\n    param 'Any', :arg\n    block_param 'Callable[1,1]', :block\n  end\n\n  def then(arg)\n    return nil if arg.nil?\n\n    yield(arg)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/tree_each.rb",
    "content": "# frozen_string_literal: true\n\n# Runs a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# recursively and repeatedly using values from a data structure, then returns the unchanged data structure, or if\n# a lambda is not given, returns an `Iterator` for the tree.\n#\n# This function takes one mandatory argument, one optional, and an optional block in this order:\n#\n# 1. An `Array`, `Hash`, `Iterator`, or `Object` that the function will iterate over.\n# 2. An optional hash with the options:\n#    * `include_containers` => `Optional[Boolean]` # default `true` - if containers should be given to the lambda\n#    * `include_values` => `Optional[Boolean]` # default `true` - if non containers should be given to the lambda\n#    * `include_root` => `Optional[Boolean]` # default `true` - if the root container should be given to the lambda\n#    * `container_type` => `Optional[Type[Variant[Array, Hash, Object]]]` # a type that determines what a container is - can only\n#       be set to a type that matches the default `Variant[Array, Hash, Object]`.\n#    * `order` => `Enum[depth_first, breadth_first]` # default ´depth_first`, the order in which elements are visited\n#    * `include_refs` => `Optional[Boolean]` # default `false`, if attributes in objects marked as bing of `reference` kind\n#       should be included.\n# 3. An optional lambda, which the function calls for each element in the first argument. It must\n#    accept one or two arguments; either `$path`, and `$value`, or just `$value`.\n#\n# @example Using the `tree_each` function\n#\n# `$data.tree_each |$path, $value| { <PUPPET CODE BLOCK> }`\n# `$data.tree_each |$value| { <PUPPET CODE BLOCK> }`\n#\n# or\n#\n# `tree_each($data) |$path, $value| { <PUPPET CODE BLOCK> }`\n# `tree_each($data) |$value| { <PUPPET CODE BLOCK> }`\n#\n# The parameter `$path` is always given as an `Array` containing the path that when applied to\n# the tree as `$data.dig(*$path) yields the `$value`.\n# The `$value` is the value at that path.\n#\n# For `Array` values, the path will contain `Integer` entries with the array index,\n# and for `Hash` values, the path will contain the hash key, which may be `Any` value.\n# For `Object` containers, the entry is the name of the attribute (a `String`).\n#\n# The tree is walked in either depth-first order, or in breadth-first order under the control of the\n# `order` option, yielding each `Array`, `Hash`, `Object`, and each entry/attribute.\n# The default is `depth_first` which means that children are processed before siblings.\n# An order of `breadth_first` means that siblings are processed before children.\n#\n# @example depth- or breadth-first order\n#\n# ```puppet\n# [1, [2, 3], 4]\n# ```\n#\n# If containers are skipped, results in:\n#\n# * `depth_first` order `1`, `2`, `3`, `4`\n# * `breadth_first` order `1`, `4`,`2`, `3`\n#\n# If containers and root are included, results in:\n#\n# * `depth_first` order `[1, [2, 3], 4]`, `1`, `[2, 3]`, `2`, `3`, `4`\n# * `breadth_first` order `[1, [2, 3], 4]`, `1`, `[2, 3]`, `4`, `2`, `3`\n#\n# Typical use of the `tree_each` function include:\n# * a more efficient way to iterate over a tree than first using `flatten` on an array\n#   as that requires a new (potentially very large) array to be created\n# * when a tree needs to be transformed and 'pretty printed' in a template\n# * avoiding having to write a special recursive function when tree contains hashes (flatten does\n#   not work on hashes)\n#\n# @example A flattened iteration over a tree excluding Collections\n#\n# ```puppet\n# $data = [1, 2, [3, [4, 5]]]\n# $data.tree_each({include_containers => false}) |$v| { notice \"$v\" }\n# ```\n#\n# This would call the lambda 5 times with with the following values in sequence: `1`, `2`, `3`, `4`, `5`\n#\n# @example A flattened iteration over a tree (including containers by default)\n#\n# ```puppet\n# $data = [1, 2, [3, [4, 5]]]\n# $data.tree_each |$v| { notice \"$v\" }\n# ```\n#\n# This would call the lambda 7 times with the following values in sequence:\n# `1`, `2`, `[3, [4, 5]]`, `3`, `[4, 5]`, `4`, `5`\n#\n# @example A flattened iteration over a tree (including only non root containers)\n#\n# ```puppet\n# $data = [1, 2, [3, [4, 5]]]\n# $data.tree_each({include_values => false, include_root => false}) |$v| { notice \"$v\" }\n# ```\n#\n# This would call the lambda 2 times with the following values in sequence:\n# `[3, [4, 5]]`, `[4, 5]`\n#\n# Any Puppet Type system data type can be used to filter what is\n# considered to be a container, but it must be a narrower type than one of\n# the default `Array`, `Hash`, `Object` types - for example it is not possible to make a\n# `String` be a container type.\n#\n# @example Only `Array` as container type\n#\n# ```puppet\n# $data = [1, {a => 'hello', b => [100, 200]}, [3, [4, 5]]]\n# $data.tree_each({container_type => Array, include_containers => false} |$v| { notice \"$v\" }\n# ```\n#\n# Would call the lambda 5 times with `1`, `{a => 'hello', b => [100, 200]}`, `3`, `4`, `5`\n#\n# **Chaining** When calling `tree_each` without a lambda the function produces an `Iterator`\n# that can be chained into another iteration. Thus it is easy to use one of:\n#\n# * `reverse_each` - get \"leaves before root\"\n# * `filter` - prune the tree\n# * `map` - transform each element\n#\n# Note than when chaining, the value passed on is a `Tuple` with `[path, value]`.\n#\n# @example Pruning a tree\n#\n# ```puppet\n# # A tree of some complexity (here very simple for readability)\n# $tree = [\n#  { name => 'user1', status => 'inactive', id => '10'},\n#  { name => 'user2', status => 'active', id => '20'}\n# ]\n# notice $tree.tree_each.filter |$v| {\n#  $value = $v[1]\n#  $value =~ Hash and $value[status] == active\n# }\n# ```\n#\n# Would notice `[[[1], {name => user2, status => active, id => 20}]]`, which can then be processed\n# further as each filtered result appears as a `Tuple` with `[path, value]`.\n#\n#\n# For general examples that demonstrates iteration see the Puppet\n# [iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\n# documentation.\n#\n# @since 5.0.0\n#\nPuppet::Functions.create_function(:tree_each) do\n  local_types do\n    type \"OptionsType  = Struct[{\\\n      container_type => Optional[Type],\\\n      include_root   => Optional[Boolean],\n      include_containers => Optional[Boolean],\\\n      include_values => Optional[Boolean],\\\n      order => Optional[Enum[depth_first, breadth_first]],\\\n      include_refs   => Optional[Boolean]\\\n    }]\"\n  end\n\n  dispatch :tree_Enumerable2 do\n    param 'Variant[Iterator, Array, Hash, Object]', :tree\n    optional_param 'OptionsType', :options\n    block_param 'Callable[2,2]', :block\n  end\n\n  dispatch :tree_Enumerable1 do\n    param 'Variant[Iterator, Array, Hash, Object]', :tree\n    optional_param 'OptionsType', :options\n    block_param 'Callable[1,1]', :block\n  end\n\n  dispatch :tree_Iterable do\n    param 'Variant[Iterator, Array, Hash, Object]', :tree\n    optional_param 'OptionsType', :options\n  end\n\n  def tree_Enumerable1(enum, options = {}, &block)\n    iterator(enum, options).each { |_, v| yield(v) }\n    enum\n  end\n\n  def tree_Enumerable2(enum, options = {}, &block)\n    iterator(enum, options).each { |path, v| yield(path, v) }\n    enum\n  end\n\n  def tree_Iterable(enum, options = {}, &block)\n    Puppet::Pops::Types::Iterable.on(iterator(enum, options))\n  end\n\n  def iterator(enum, options)\n    if depth_first?(options)\n      Puppet::Pops::Types::Iterable::DepthFirstTreeIterator.new(enum, options)\n    else\n      Puppet::Pops::Types::Iterable::BreadthFirstTreeIterator.new(enum, options)\n    end\n  end\n\n  def depth_first?(options)\n    (order = options['order']).nil? ? true : order == 'depth_first'\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/type.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the data type of a given value with a given degree of generality.\n#\n# ```puppet\n# type InferenceFidelity = Enum[generalized, reduced, detailed]\n#\n# function type(Any $value, InferenceFidelity $fidelity = 'detailed') # returns Type\n# ```\n#\n# @example Using `type`\n#\n# ``` puppet\n# notice type(42) =~ Type[Integer]\n# ```\n#\n# Would notice `true`.\n#\n# By default, the best possible inference is made where all details are retained.\n# This is good when the type is used for further type calculations but is overwhelmingly\n# rich in information if it is used in a error message.\n#\n# The optional argument `$fidelity` may be given as (from lowest to highest fidelity):\n#\n# * `generalized` - reduces to common type and drops size constraints\n# * `reduced` - reduces to common type in collections\n# * `detailed` - (default) all details about inferred types is retained\n#\n# @example Using `type()` with different inference fidelity:\n#\n# ``` puppet\n# notice type([3.14, 42], 'generalized')\n# notice type([3.14, 42], 'reduced'')\n# notice type([3.14, 42], 'detailed')\n# notice type([3.14, 42])\n# ```\n#\n# Would notice the four values:\n#\n# 1. `Array[Numeric]`\n# 2. `Array[Numeric, 2, 2]`\n# 3. `Tuple[Float[3.14], Integer[42,42]]]`\n# 4. `Tuple[Float[3.14], Integer[42,42]]]`\n#\n# @since 4.4.0\n#\nPuppet::Functions.create_function(:type) do\n  dispatch :type_detailed do\n    param 'Any', :value\n    optional_param 'Enum[detailed]', :inference_method\n  end\n\n  dispatch :type_parameterized do\n    param 'Any', :value\n    param 'Enum[reduced]', :inference_method\n  end\n\n  dispatch :type_generalized do\n    param 'Any', :value\n    param 'Enum[generalized]', :inference_method\n  end\n\n  def type_detailed(value, _ = nil)\n    Puppet::Pops::Types::TypeCalculator.infer_set(value)\n  end\n\n  def type_parameterized(value, _)\n    Puppet::Pops::Types::TypeCalculator.infer(value)\n  end\n\n  def type_generalized(value, _)\n    Puppet::Pops::Types::TypeCalculator.infer(value).generalize\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/unique.rb",
    "content": "# frozen_string_literal: true\n\n# Produces a unique set of values from an `Iterable` argument.\n#\n# * If the argument is a `String`, the unique set of characters are returned as a new `String`.\n# * If the argument is a `Hash`, the resulting hash associates a set of keys with a set of unique values.\n# * For all other types of `Iterable` (`Array`, `Iterator`) the result is an `Array` with\n#   a unique set of entries.\n# * Comparison of all `String` values are case sensitive.\n# * An optional code block can be given - if present it is given each candidate value and its return is used instead of the given value. This\n#   enables transformation of the value before comparison. The result of the lambda is only used for comparison.\n# * The optional code block when used with a hash is given each value (not the keys).\n#\n# @example Using unique with a String\n#\n# ```puppet\n# # will produce 'abc'\n# \"abcaabb\".unique\n# ```\n#\n# @example Using unique with an Array\n#\n# ```puppet\n# # will produce ['a', 'b', 'c']\n# ['a', 'b', 'c', 'a', 'a', 'b'].unique\n# ```\n#\n# @example Using unique with a Hash\n#\n# ```puppet\n# # will produce { ['a', 'b'] => [10], ['c'] => [20]}\n# {'a' => 10, 'b' => 10, 'c' => 20}.unique\n#\n# # will produce { 'a' => 10, 'c' => 20 } (use first key with first value)\n# Hash.new({'a' => 10, 'b' => 10, 'c' => 20}.unique.map |$k, $v| { [ $k[0] , $v[0]] })\n#\n# # will produce { 'b' => 10, 'c' => 20 } (use last key with first value)\n# Hash.new({'a' => 10, 'b' => 10, 'c' => 20}.unique.map |$k, $v| { [ $k[-1] , $v[0]] })\n# ```\n#\n# @example Using unique with an Iterable\n#\n# ```\n# # will produce [3, 2, 1]\n# [1,2,2,3,3].reverse_each.unique\n# ```\n#\n# @example Using unique with a lambda\n#\n# ```puppet\n# # will produce [['sam', 'smith'], ['sue', 'smith']]\n# [['sam', 'smith'], ['sam', 'brown'], ['sue', 'smith']].unique |$x| { $x[0] }\n#\n# # will produce [['sam', 'smith'], ['sam', 'brown']]\n# [['sam', 'smith'], ['sam', 'brown'], ['sue', 'smith']].unique |$x| { $x[1] }\n#\n# # will produce ['aBc', 'bbb'] (using a lambda to make comparison using downcased (%d) strings)\n# ['aBc', 'AbC', 'bbb'].unique |$x| { String($x,'%d') }\n#\n# # will produce {[a] => [10], [b, c, d, e] => [11, 12, 100]}\n# {a => 10, b => 11, c => 12, d => 100, e => 11}.unique |$v| { if $v > 10 { big } else { $v } }\n# ```\n#\n# Note that for `Hash` the result is slightly different than for the other data types. For those the result contains the\n# *first-found* unique value, but for `Hash` it contains associations from a set of keys to the set of values clustered by the\n# equality lambda (or the default value equality if no lambda was given). This makes the `unique` function more versatile for hashes\n# in general, while requiring that the simple computation of \"hash's unique set of values\" is performed as `$hsh.map |$k, $v| { $v }.unique`.\n# (Generally, it's meaningless to compute the unique set of hash keys because they are unique by definition. However, the\n# situation can change if the hash keys are processed with a different lambda for equality. For this unique computation,\n# first map the hash to an array of its keys.)\n# If the more advanced clustering is wanted for one of the other data types, simply transform it into a `Hash` as shown in the\n# following example.\n#\n# @example turning a string or array into a hash with index keys\n#\n# ```puppet\n# # Array ['a', 'b', 'c'] to Hash with index results in\n# # {0 => 'a', 1 => 'b', 2 => 'c'}\n# Hash(['a', 'b', 'c'].map |$i, $v| { [$i, $v]})\n#\n# # String \"abc\" to Hash with index results in\n# # {0 => 'a', 1 => 'b', 2 => 'c'}\n# Hash(Array(\"abc\").map |$i,$v| { [$i, $v]})\n# \"abc\".to(Array).map |$i,$v| { [$i, $v]}.to(Hash)\n# ```\n#\n# @since Puppet 5.0.0\n#\nPuppet::Functions.create_function(:unique) do\n  dispatch :unique_string do\n    param 'String', :string\n    optional_block_param 'Callable[String]', :block\n  end\n\n  dispatch :unique_hash do\n    param 'Hash', :hash\n    optional_block_param 'Callable[Any]', :block\n  end\n\n  dispatch :unique_array do\n    param 'Array', :array\n    optional_block_param 'Callable[Any]', :block\n  end\n\n  dispatch :unique_iterable do\n    param 'Iterable', :iterable\n    optional_block_param 'Callable[Any]', :block\n  end\n\n  def unique_string(string, &block)\n    string.split('').uniq(&block).join('')\n  end\n\n  def unique_hash(hash, &block)\n    block = ->(v) { v } unless block_given?\n    result = Hash.new { |h, k| h[k] = { :keys => [], :values => [] } }\n    hash.each_pair do |k, v|\n      rc = result[block.call(v)]\n      rc[:keys] << k\n      rc[:values] << v\n    end\n    # reduce the set of possibly duplicated value entries\n    inverted = {}\n    result.each_pair { |_k, v| inverted[v[:keys]] = v[:values].uniq }\n    inverted\n  end\n\n  def unique_array(array, &block)\n    array.uniq(&block)\n  end\n\n  def unique_iterable(iterable, &block)\n    Puppet::Pops::Types::Iterable.on(iterable).uniq(&block)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/unwrap.rb",
    "content": "# frozen_string_literal: true\n\n# Unwraps a Sensitive value and returns the wrapped object.\n# Returns the Value itself, if it is not Sensitive.\n#\n# @example Usage of unwrap\n#\n# ```puppet\n# $plaintext = 'hunter2'\n# $pw = Sensitive.new($plaintext)\n# notice(\"Wrapped object is $pw\") #=> Prints \"Wrapped object is Sensitive [value redacted]\"\n# $unwrapped = $pw.unwrap\n# notice(\"Unwrapped object is $unwrapped\") #=> Prints \"Unwrapped object is hunter2\"\n# ```\n#\n# You can optionally pass a block to unwrap in order to limit the scope where the\n# unwrapped value is visible.\n#\n# @example Unwrapping with a block of code\n#\n# ```puppet\n# $pw = Sensitive.new('hunter2')\n# notice(\"Wrapped object is $pw\") #=> Prints \"Wrapped object is Sensitive [value redacted]\"\n# $pw.unwrap |$unwrapped| {\n#   $conf = inline_template(\"password: ${unwrapped}\\n\")\n#   Sensitive.new($conf)\n# } #=> Returns a new Sensitive object containing an interpolated config file\n# # $unwrapped is now out of scope\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:unwrap) do\n  dispatch :from_sensitive do\n    param 'Sensitive', :arg\n    optional_block_param\n  end\n\n  dispatch :from_any do\n    param 'Any', :arg\n    optional_block_param\n  end\n\n  def from_sensitive(arg)\n    unwrapped = arg.unwrap\n    if block_given?\n      yield(unwrapped)\n    else\n      unwrapped\n    end\n  end\n\n  def from_any(arg)\n    unwrapped = arg\n    if block_given?\n      yield(unwrapped)\n    else\n      unwrapped\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/upcase.rb",
    "content": "# frozen_string_literal: true\n\n# Converts a String, Array or Hash (recursively) into upper case.\n#\n# This function is compatible with the stdlib function with the same name.\n#\n# The function does the following:\n# * For a `String`, its upper case version is returned. This is done using Ruby system locale which handles some, but not all\n#   special international up-casing rules (for example German double-s ß is upcased to \"SS\", whereas upper case double-s\n#   is downcased to ß).\n# * For `Array` and `Hash` the conversion to upper case is recursive and each key and value must be convertible by\n#   this function.\n# * When a `Hash` is converted, some keys could result in the same key - in those cases, the\n#   latest key-value wins. For example if keys \"aBC\", and \"abC\" where both present, after upcase there would only be one\n#   key \"ABC\".\n# * If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n# * An error is raised for all other data types.\n#\n# Please note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\n# To ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n#\n# @example Converting a String to upper case\n# ```puppet\n# 'hello'.upcase()\n# upcase('hello')\n# ```\n# Would both result in `\"HELLO\"`\n#\n# @example Converting an Array to upper case\n# ```puppet\n# ['a', 'b'].upcase()\n# upcase(['a', 'b'])\n# ```\n# Would both result in `['A', 'B']`\n#\n# @example Converting a Hash to upper case\n# ```puppet\n# {'a' => 'hello', 'b' => 'goodbye'}.upcase()\n# ```\n# Would result in `{'A' => 'HELLO', 'B' => 'GOODBYE'}`\n#\n# @example Converting a recursive structure\n# ```puppet\n# ['a', 'b', ['c', ['d']], {'x' => 'y'}].upcase\n# ```\n# Would result in `['A', 'B', ['C', ['D']], {'X' => 'Y'}]`\n#\nPuppet::Functions.create_function(:upcase) do\n  local_types do\n    type 'StringData = Variant[String, Numeric, Array[StringData], Hash[StringData, StringData]]'\n  end\n\n  dispatch :on_numeric do\n    param 'Numeric', :arg\n  end\n\n  dispatch :on_string do\n    param 'String', :arg\n  end\n\n  dispatch :on_array do\n    param 'Array[StringData]', :arg\n  end\n\n  dispatch :on_hash do\n    param 'Hash[StringData, StringData]', :arg\n  end\n\n  # unit function - since the old implementation skipped Numeric values\n  def on_numeric(n)\n    n\n  end\n\n  def on_string(s)\n    s.upcase\n  end\n\n  def on_array(a)\n    a.map { |x| do_upcase(x) }\n  end\n\n  def on_hash(h)\n    result = {}\n    h.each_pair { |k, v| result[do_upcase(k)] = do_upcase(v) }\n    result\n  end\n\n  def do_upcase(x)\n    x.is_a?(String) ? x.upcase : call_function('upcase', x)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/values.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the values of a hash as an Array\n#\n# @example Using `values`\n#\n# ```puppet\n# $hsh = {\"apples\" => 3, \"oranges\" => 4 }\n# $hsh.values()\n# values($hsh)\n# # both results in the array [3, 4]\n# ```\n#\n# * Note that a hash in the puppet language accepts any data value (including `undef`) unless\n#   it is constrained with a `Hash` data type that narrows the allowed data types.\n# * For an empty hash, an empty array is returned.\n# * The order of the values is the same as the order in the hash (typically the order in which they were added).\n#\nPuppet::Functions.create_function(:values) do\n  dispatch :values do\n    param 'Hash', :hsh\n  end\n\n  def values(hsh)\n    hsh.values\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/versioncmp.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/package'\n\n# Compares two version numbers.\n#\n# Prototype:\n#\n#     $result = versioncmp(a, b)\n#\n# Where a and b are arbitrary version strings.\n#\n# Optional parameter ignore_trailing_zeroes is used to ignore unnecessary\n# trailing version numbers like .0 or .0.00\n#\n# This function returns:\n#\n# * `1` if version a is greater than version b\n# * `0` if the versions are equal\n# * `-1` if version a is less than version b\n#\n# @example Using versioncmp\n#\n#     if versioncmp('2.6-1', '2.4.5') > 0 {\n#         notice('2.6-1 is > than 2.4.5')\n#     }\n#\n# This function uses the same version comparison algorithm used by Puppet's\n# `package` type.\n#\nPuppet::Functions.create_function(:versioncmp) do\n  dispatch :versioncmp do\n    param 'String', :a\n    param 'String', :b\n    optional_param 'Boolean', :ignore_trailing_zeroes\n  end\n\n  def versioncmp(a, b, ignore_trailing_zeroes = false)\n    Puppet::Util::Package.versioncmp(a, b, ignore_trailing_zeroes)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/warning.rb",
    "content": "# frozen_string_literal: true\n\n# Logs a message on the server at level `warning`.\nPuppet::Functions.create_function(:warning, Puppet::Functions::InternalFunction) do\n  # @param values The values to log.\n  # @return [Undef]\n  dispatch :warning do\n    scope_param\n    repeated_param 'Any', :values\n    return_type 'Undef'\n  end\n\n  def warning(scope, *values)\n    Puppet::Util::Log.log_func(scope, :warning, values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/with.rb",
    "content": "# frozen_string_literal: true\n\n# Calls a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n# with the given arguments and returns the result.\n#\n# Since a lambda's scope is\n# [local](https://puppet.com/docs/puppet/latest/lang_lambdas.html#lambda-scope)\n# to the lambda, you can use the `with` function to create private blocks of code within a\n# class using variables whose values cannot be accessed outside of the lambda.\n#\n# @example Using `with`\n#\n# ```puppet\n# # Concatenate three strings into a single string formatted as a list.\n# $fruit = with(\"apples\", \"oranges\", \"bananas\") |$x, $y, $z| {\n#   \"${x}, ${y}, and ${z}\"\n# }\n# $check_var = $x\n# # $fruit contains \"apples, oranges, and bananas\"\n# # $check_var is undefined, as the value of $x is local to the lambda.\n# ```\n#\n# @since 4.0.0\n#\nPuppet::Functions.create_function(:with) do\n  dispatch :with do\n    repeated_param 'Any', :arg\n    block_param\n  end\n\n  def with(*args)\n    yield(*args)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions/yaml_data.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\n\n# The `yaml_data` is a hiera 5 `data_hash` data provider function.\n# See [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-built-in-backends) for\n# how to use this function.\n#\n# @since 4.8.0\n#\nPuppet::Functions.create_function(:yaml_data) do\n  # @since 4.8.0\n  dispatch :yaml_data do\n    param 'Struct[{path=>String[1]}]', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  argument_mismatch :missing_path do\n    param 'Hash', :options\n    param 'Puppet::LookupContext', :context\n  end\n\n  def yaml_data(options, context)\n    path = options['path']\n    context.cached_file_data(path) do |content|\n      data = Puppet::Util::Yaml.safe_load(content, [Symbol], path)\n      if data.is_a?(Hash)\n        Puppet::Pops::Lookup::HieraConfig.symkeys_to_string(data)\n      else\n        msg = _(\"%{path}: file does not contain a valid yaml hash\" % { path: path })\n        raise Puppet::DataBinding::LookupError, msg if Puppet[:strict] == :error && data != false\n\n        Puppet.warning(msg)\n        {}\n      end\n    rescue Puppet::Util::Yaml::YamlLoadError => ex\n      # YamlLoadErrors include the absolute path to the file, so no need to add that\n      raise Puppet::DataBinding::LookupError, _(\"Unable to parse %{message}\") % { message: ex.message }\n    end\n  end\n\n  def missing_path(options, context)\n    \"one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/functions.rb",
    "content": "# frozen_string_literal: true\n\n# Functions in the puppet language can be written in Ruby and distributed in\n# puppet modules. The function is written by creating a file in the module's\n# `lib/puppet/functions/<modulename>` directory, where `<modulename>` is\n# replaced with the module's name. The file should have the name of the function.\n# For example, to create a function named `min` in a module named `math` create\n# a file named `lib/puppet/functions/math/min.rb` in the module.\n#\n# A function is implemented by calling {Puppet::Functions.create_function}, and\n# passing it a block that defines the implementation of the function.\n#\n# Functions are namespaced inside the module that contains them. The name of\n# the function is prefixed with the name of the module. For example,\n# `math::min`.\n#\n# @example A simple function\n#   Puppet::Functions.create_function('math::min') do\n#     def min(a, b)\n#       a <= b ? a : b\n#     end\n#   end\n#\n# Anatomy of a function\n# ---\n#\n# Functions are composed of four parts: the name, the implementation methods,\n# the signatures, and the dispatches.\n#\n# The name is the string given to the {Puppet::Functions.create_function}\n# method. It specifies the name to use when calling the function in the puppet\n# language, or from other functions.\n#\n# The implementation methods are ruby methods (there can be one or more) that\n# provide that actual implementation of the function's behavior. In the\n# simplest case the name of the function (excluding any namespace) and the name\n# of the method are the same. When that is done no other parts (signatures and\n# dispatches) need to be used.\n#\n# Signatures are a way of specifying the types of the function's parameters.\n# The types of any arguments will be checked against the types declared in the\n# signature and an error will be produced if they don't match. The types are\n# defined by using the same syntax for types as in the puppet language.\n#\n# Dispatches are how signatures and implementation methods are tied together.\n# When the function is called, puppet searches the signatures for one that\n# matches the supplied arguments. Each signature is part of a dispatch, which\n# specifies the method that should be called for that signature. When a\n# matching signature is found, the corresponding method is called.\n#\n# Special dispatches designed to create error messages for an argument mismatch\n# can be added using the keyword `argument_mismatch` instead of `dispatch`. The\n# method appointed by an `argument_mismatch` will be called with arguments\n# just like a normal `dispatch` would, but the method must produce a string.\n# The string is then used as the message in the `ArgumentError` that is raised\n# when the method returns. A block parameter can be given, but it is not\n# propagated in the method call.\n#\n# Documentation for the function should be placed as comments to the\n# implementation method(s).\n#\n# @todo Documentation for individual instances of these new functions is not\n#   yet tied into the puppet doc system.\n#\n# @example Dispatching to different methods by type\n#   Puppet::Functions.create_function('math::min') do\n#     dispatch :numeric_min do\n#       param 'Numeric', :a\n#       param 'Numeric', :b\n#     end\n#\n#     dispatch :string_min do\n#       param 'String', :a\n#       param 'String', :b\n#     end\n#\n#     def numeric_min(a, b)\n#       a <= b ? a : b\n#     end\n#\n#     def string_min(a, b)\n#       a.downcase <= b.downcase ? a : b\n#     end\n#   end\n#\n# @example Using an argument mismatch handler\n#   Puppet::Functions.create_function('math::min') do\n#     dispatch :numeric_min do\n#       param 'Numeric', :a\n#       param 'Numeric', :b\n#     end\n#\n#     argument_mismatch :on_error do\n#       param 'Any', :a\n#       param 'Any', :b\n#     end\n#\n#     def numeric_min(a, b)\n#       a <= b ? a : b\n#     end\n#\n#     def on_error(a, b)\n#       'both arguments must be of type Numeric'\n#     end\n#   end\n#\n# Specifying Signatures\n# ---\n#\n# If nothing is specified, the number of arguments given to the function must\n# be the same as the number of parameters, and all of the parameters are of\n# type 'Any'.\n#\n# The following methods can be used to define a parameter\n#\n#  - _param_ - the argument must be given in the call.\n#  - _optional_param_ - the argument may be missing in the call. May not be followed by a required parameter\n#  - _repeated_param_ - the type specifies a repeating type that occurs 0 to \"infinite\" number of times. It may only appear last or just before a block parameter.\n#  - _block_param_ - a block must be given in the call. May only appear last.\n#  - _optional_block_param_ - a block may be given in the call. May only appear last.\n#\n# The method name _required_param_ is an alias for _param_ and _required_block_param_ is an alias for _block_param_\n#\n# A parameter definition takes 2 arguments:\n#  - _type_ A string that must conform to a type in the puppet language\n#  - _name_ A symbol denoting the parameter name\n#\n# Both arguments are optional when defining a block parameter. The _type_ defaults to \"Callable\"\n# and the _name_ to :block.\n#\n# Note that the dispatch definition is used to match arguments given in a call to the function with the defined\n# parameters. It then dispatches the call to the implementation method simply passing the given arguments on to\n# that method without any further processing and it is the responsibility of that method's implementor to ensure\n# that it can handle those arguments.\n#\n# @example Variable number of arguments\n#   Puppet::Functions.create_function('foo') do\n#     dispatch :foo do\n#       param 'Numeric', :first\n#       repeated_param 'Numeric', :values\n#     end\n#\n#     def foo(first, *values)\n#       # do something\n#     end\n#   end\n#\n# There is no requirement for direct mapping between parameter definitions and the parameters in the\n# receiving implementation method so the following example is also legal. Here the dispatch will ensure\n# that `*values` in the receiver will be an array with at least one entry of type String and that any\n# remaining entries are of type Numeric:\n#\n# @example Inexact mapping or parameters\n#   Puppet::Functions.create_function('foo') do\n#     dispatch :foo do\n#       param 'String', :first\n#       repeated_param 'Numeric', :values\n#     end\n#\n#     def foo(*values)\n#       # do something\n#     end\n#   end\n#\n# Access to Scope\n# ---\n# In general, functions should not need access to scope; they should be\n# written to act on their given input only. If they absolutely must look up\n# variable values, they should do so via the closure scope (the scope where\n# they are defined) - this is done by calling `closure_scope()`.\n#\n# Calling other Functions\n# ---\n# Calling other functions by name is directly supported via\n# {Puppet::Pops::Functions::Function#call_function}. This allows a function to\n# call other functions visible from its loader.\n#\n# @api public\nmodule Puppet::Functions\n  # @param func_name [String, Symbol] a simple or qualified function name\n  # @param block [Proc] the block that defines the methods and dispatch of the\n  #   Function to create\n  # @return [Class<Function>] the newly created Function class\n  #\n  # @api public\n  def self.create_function(func_name, function_base = Function, &block)\n    # Ruby < 2.1.0 does not have method on Binding, can only do eval\n    # and it will fail unless protected with an if defined? if the local\n    # variable does not exist in the block's binder.\n    #\n\n    loader = block.binding.eval('loader_injected_arg if defined?(loader_injected_arg)')\n    create_loaded_function(func_name, loader, function_base, &block)\n  rescue StandardError => e\n    raise ArgumentError, _(\"Function Load Error for function '%{function_name}': %{message}\") % { function_name: func_name, message: e.message }\n  end\n\n  # Creates a function in, or in a local loader under the given loader.\n  # This method should only be used when manually creating functions\n  # for the sake of testing. Functions that are autoloaded should\n  # always use the `create_function` method and the autoloader will supply\n  # the correct loader.\n  #\n  # @param func_name [String, Symbol] a simple or qualified function name\n  # @param loader [Puppet::Pops::Loaders::Loader] the loader loading the function\n  # @param block [Proc] the block that defines the methods and dispatch of the\n  #   Function to create\n  # @return [Class<Function>] the newly created Function class\n  #\n  # @api public\n  def self.create_loaded_function(func_name, loader, function_base = Function, &block)\n    if function_base.ancestors.none? { |s| s == Puppet::Pops::Functions::Function }\n      raise ArgumentError, _(\"Functions must be based on Puppet::Pops::Functions::Function. Got %{function_base}\") % { function_base: function_base }\n    end\n\n    func_name = func_name.to_s\n    # Creates an anonymous class to represent the function\n    # The idea being that it is garbage collected when there are no more\n    # references to it.\n    #\n    # (Do not give the class the block here, as instance variables should be set first)\n    the_class = Class.new(function_base)\n\n    unless loader.nil?\n      the_class.instance_variable_set(:'@loader', loader.private_loader)\n    end\n\n    # Make the anonymous class appear to have the class-name <func_name>\n    # Even if this class is not bound to such a symbol in a global ruby scope and\n    # must be resolved via the loader.\n    # This also overrides any attempt to define a name method in the given block\n    # (Since it redefines it)\n    #\n    # TODO, enforce name in lower case (to further make it stand out since Ruby\n    # class names are upper case)\n    #\n    the_class.instance_eval do\n      @func_name = func_name\n\n      def name\n        @func_name\n      end\n\n      def loader\n        @loader\n      end\n    end\n\n    # The given block can now be evaluated and have access to name and loader\n    #\n    the_class.class_eval(&block)\n\n    # Automatically create an object dispatcher based on introspection if the\n    # loaded user code did not define any dispatchers. Fail if function name\n    # does not match a given method name in user code.\n    #\n    if the_class.dispatcher.empty?\n      simple_name = func_name.split(/::/)[-1]\n      type, names = default_dispatcher(the_class, simple_name)\n      last_captures_rest = (type.size_range[1] == Float::INFINITY)\n      the_class.dispatcher.add(Puppet::Pops::Functions::Dispatch.new(type, simple_name, names, last_captures_rest))\n    end\n\n    # The function class is returned as the result of the create function method\n    the_class\n  end\n\n  # Creates a default dispatcher configured from a method with the same name as the function\n  #\n  # @api private\n  def self.default_dispatcher(the_class, func_name)\n    unless the_class.method_defined?(func_name)\n      raise ArgumentError, _(\"Function Creation Error, cannot create a default dispatcher for function '%{func_name}', no method with this name found\") % { func_name: func_name }\n    end\n\n    any_signature(*min_max_param(the_class.instance_method(func_name)))\n  end\n\n  # @api private\n  def self.min_max_param(method)\n    result = { :req => 0, :opt => 0, :rest => 0 }\n    # count per parameter kind, and get array of names\n    names = method.parameters.map { |p| result[p[0]] += 1; p[1].to_s }\n    from = result[:req]\n    to = result[:rest] > 0 ? :default : from + result[:opt]\n    [from, to, names]\n  end\n\n  # Construct a signature consisting of Object type, with min, and max, and given names.\n  # (there is only one type entry).\n  #\n  # @api private\n  def self.any_signature(from, to, names)\n    # Construct the type for the signature\n    # Tuple[Object, from, to]\n    param_types = Puppet::Pops::Types::PTupleType.new([Puppet::Pops::Types::PAnyType::DEFAULT], Puppet::Pops::Types::PIntegerType.new(from, to))\n    [Puppet::Pops::Types::PCallableType.new(param_types), names]\n  end\n\n  # Function\n  # ===\n  # This class is the base class for all Puppet 4x Function API functions. A\n  # specialized class is created for each puppet function.\n  #\n  # @api public\n  class Function < Puppet::Pops::Functions::Function\n    # @api private\n    def self.builder\n      DispatcherBuilder.new(dispatcher, Puppet::Pops::Types::PCallableType::DEFAULT, loader)\n    end\n\n    # Dispatch any calls that match the signature to the provided method name.\n    #\n    # @param meth_name [Symbol] The name of the implementation method to call\n    #   when the signature defined in the block matches the arguments to a call\n    #   to the function.\n    # @return [Void]\n    #\n    # @api public\n    def self.dispatch(meth_name, &block)\n      builder().instance_eval do\n        dispatch(meth_name, false, &block)\n      end\n    end\n\n    # Like `dispatch` but used for a specific type of argument mismatch. Will not be include in the list of valid\n    # parameter overloads for the function.\n    #\n    # @param meth_name [Symbol] The name of the implementation method to call\n    #   when the signature defined in the block matches the arguments to a call\n    #   to the function.\n    # @return [Void]\n    #\n    # @api public\n    def self.argument_mismatch(meth_name, &block)\n      builder().instance_eval do\n        dispatch(meth_name, true, &block)\n      end\n    end\n\n    # Allows types local to the function to be defined to ease the use of complex types\n    # in a 4.x function. Within the given block, calls to `type` can be made with a string\n    # 'AliasType = ExistingType` can be made to define aliases. The defined aliases are\n    # available for further aliases, and in all dispatchers.\n    #\n    # @since 4.5.0\n    # @api public\n    #\n    def self.local_types(&block)\n      if loader.nil?\n        raise ArgumentError, _(\"No loader present. Call create_loaded_function(:myname, loader,...), instead of 'create_function' if running tests\")\n      end\n\n      aliases = LocalTypeAliasesBuilder.new(loader, name)\n      aliases.instance_eval(&block)\n      # Add the loaded types to the builder\n      aliases.local_types.each do |type_alias_expr|\n        # Bind the type alias to the local_loader using the alias\n        t = Puppet::Pops::Loader::TypeDefinitionInstantiator.create_from_model(type_alias_expr, aliases.loader)\n\n        # Also define a method for convenient access to the defined type alias.\n        # Since initial capital letter in Ruby means a Constant these names use a prefix of\n        # `type`. As an example, the type 'MyType' is accessed by calling `type_MyType`.\n        define_method(\"type_#{t.name}\") { t }\n      end\n      # Store the loader in the class\n      @loader = aliases.loader\n    end\n\n    # Creates a new function instance in the given closure scope (visibility to variables), and a loader\n    # (visibility to other definitions). The created function will either use the `given_loader` or\n    # (if it has local type aliases) a loader that was constructed from the loader used when loading\n    # the function's class.\n    #\n    # TODO: It would be of value to get rid of the second parameter here, but that would break API.\n    #\n    def self.new(closure_scope, given_loader)\n      super(closure_scope, @loader || given_loader)\n    end\n  end\n\n  # Base class for all functions implemented in the puppet language\n  class PuppetFunction < Function\n    def self.init_dispatch(a_closure)\n      # A closure is compatible with a dispatcher - they are both callable signatures\n      dispatcher.add(a_closure)\n    end\n  end\n\n  # Public api methods of the DispatcherBuilder are available within dispatch()\n  # blocks declared in a Puppet::Function.create_function() call.\n  #\n  # @api public\n  class DispatcherBuilder\n    attr_reader :loader\n\n    # @api private\n    def initialize(dispatcher, all_callables, loader)\n      @all_callables = all_callables\n      @dispatcher = dispatcher\n      @loader = loader\n    end\n\n    # Defines a required positional parameter with _type_ and _name_.\n    #\n    # @param type [String] The type specification for the parameter.\n    # @param name [Symbol] The name of the parameter. This is primarily used\n    #   for error message output and does not have to match an implementation\n    #   method parameter.\n    # @return [Void]\n    #\n    # @api public\n    def param(type, name)\n      internal_param(type, name)\n      raise ArgumentError, _('A required parameter cannot be added after an optional parameter') if @min != @max\n\n      @min += 1\n      @max += 1\n    end\n    alias required_param param\n\n    # Defines an optional positional parameter with _type_ and _name_.\n    # May not be followed by a required parameter.\n    #\n    # @param type [String] The type specification for the parameter.\n    # @param name [Symbol] The name of the parameter. This is primarily used\n    #   for error message output and does not have to match an implementation\n    #   method parameter.\n    # @return [Void]\n    #\n    # @api public\n    def optional_param(type, name)\n      internal_param(type, name)\n      @max += 1\n    end\n\n    # Defines a repeated positional parameter with _type_ and _name_ that may occur 0 to \"infinite\" number of times.\n    # It may only appear last or just before a block parameter.\n    #\n    # @param type [String] The type specification for the parameter.\n    # @param name [Symbol] The name of the parameter. This is primarily used\n    #   for error message output and does not have to match an implementation\n    #   method parameter.\n    # @return [Void]\n    #\n    # @api public\n    def repeated_param(type, name)\n      internal_param(type, name, true)\n      @max = :default\n    end\n    alias optional_repeated_param repeated_param\n\n    # Defines a repeated positional parameter with _type_ and _name_ that may occur 1 to \"infinite\" number of times.\n    # It may only appear last or just before a block parameter.\n    #\n    # @param type [String] The type specification for the parameter.\n    # @param name [Symbol] The name of the parameter. This is primarily used\n    #   for error message output and does not have to match an implementation\n    #   method parameter.\n    # @return [Void]\n    #\n    # @api public\n    def required_repeated_param(type, name)\n      internal_param(type, name, true)\n      raise ArgumentError, _('A required repeated parameter cannot be added after an optional parameter') if @min != @max\n\n      @min += 1\n      @max = :default\n    end\n\n    # Defines one required block parameter that may appear last. If type and name is missing the\n    # default type is \"Callable\", and the name is \"block\". If only one\n    # parameter is given, then that is the name and the type is \"Callable\".\n    #\n    # @api public\n    def block_param(*type_and_name)\n      case type_and_name.size\n      when 0\n        type = @all_callables\n        name = :block\n      when 1\n        type = @all_callables\n        name = type_and_name[0]\n      when 2\n        type, name = type_and_name\n        type = Puppet::Pops::Types::TypeParser.singleton.parse(type, loader) unless type.is_a?(Puppet::Pops::Types::PAnyType)\n      else\n        raise ArgumentError, _(\"block_param accepts max 2 arguments (type, name), got %{size}.\") % { size: type_and_name.size }\n      end\n\n      unless Puppet::Pops::Types::TypeCalculator.is_kind_of_callable?(type, false)\n        raise ArgumentError, _(\"Expected PCallableType or PVariantType thereof, got %{type_class}\") % { type_class: type.class }\n      end\n\n      unless name.is_a?(Symbol)\n        raise ArgumentError, _(\"Expected block_param name to be a Symbol, got %{name_class}\") % { name_class: name.class }\n      end\n\n      if @block_type.nil?\n        @block_type = type\n        @block_name = name\n      else\n        raise ArgumentError, _('Attempt to redefine block')\n      end\n    end\n    alias required_block_param block_param\n\n    # Defines one optional block parameter that may appear last. If type or name is missing the\n    # defaults are \"any callable\", and the name is \"block\". The implementor of the dispatch target\n    # must use block = nil when it is optional (or an error is raised when the call is made).\n    #\n    # @api public\n    def optional_block_param(*type_and_name)\n      # same as required, only wrap the result in an optional type\n      required_block_param(*type_and_name)\n      @block_type = Puppet::Pops::Types::TypeFactory.optional(@block_type)\n    end\n\n    # Defines the return type. Defaults to 'Any'\n    # @param [String] type a reference to a Puppet Data Type\n    #\n    # @api public\n    def return_type(type)\n      unless type.is_a?(String) || type.is_a?(Puppet::Pops::Types::PAnyType)\n        raise ArgumentError, _(\"Argument to 'return_type' must be a String reference to a Puppet Data Type. Got %{type_class}\") % { type_class: type.class }\n      end\n\n      @return_type = type\n    end\n\n    private\n\n    # @api private\n    def internal_param(type, name, repeat = false)\n      raise ArgumentError, _('Parameters cannot be added after a block parameter') unless @block_type.nil?\n      raise ArgumentError, _('Parameters cannot be added after a repeated parameter') if @max == :default\n\n      if name.is_a?(String)\n        raise ArgumentError, _(\"Parameter name argument must be a Symbol. Got %{name_class}\") % { name_class: name.class }\n      end\n\n      if type.is_a?(String) || type.is_a?(Puppet::Pops::Types::PAnyType)\n        @types << type\n        @names << name\n        # mark what should be picked for this position when dispatching\n        if repeat\n          @weaving << -@names.size()\n        else\n          @weaving << @names.size() - 1\n        end\n      else\n        raise ArgumentError, _(\"Parameter 'type' must be a String reference to a Puppet Data Type. Got %{type_class}\") % { type_class: type.class }\n      end\n    end\n\n    # @api private\n    def dispatch(meth_name, argument_mismatch_handler, &block)\n      # an array of either an index into names/types, or an array with\n      # injection information [type, name, injection_name] used when the call\n      # is being made to weave injections into the given arguments.\n      #\n      @types = []\n      @names = []\n      @weaving = []\n      @injections = []\n      @min = 0\n      @max = 0\n      @block_type = nil\n      @block_name = nil\n      @return_type = nil\n      @argument_mismatch_hander = argument_mismatch_handler\n      instance_eval(&block)\n      callable_t = create_callable(@types, @block_type, @return_type, @min, @max)\n      @dispatcher.add(Puppet::Pops::Functions::Dispatch.new(callable_t, meth_name, @names, @max == :default, @block_name, @injections, @weaving, @argument_mismatch_hander))\n    end\n\n    # Handles creation of a callable type from strings specifications of puppet\n    # types and allows the min/max occurs of the given types to be given as one\n    # or two integer values at the end.  The given block_type should be\n    # Optional[Callable], Callable, or nil.\n    #\n    # @api private\n    def create_callable(types, block_type, return_type, from, to)\n      mapped_types = types.map do |t|\n        t.is_a?(Puppet::Pops::Types::PAnyType) ? t : internal_type_parse(t, loader)\n      end\n      param_types = Puppet::Pops::Types::PTupleType.new(mapped_types, from > 0 && from == to ? nil : Puppet::Pops::Types::PIntegerType.new(from, to))\n      return_type = internal_type_parse(return_type, loader) unless return_type.nil? || return_type.is_a?(Puppet::Pops::Types::PAnyType)\n      Puppet::Pops::Types::PCallableType.new(param_types, block_type, return_type)\n    end\n\n    def internal_type_parse(type_string, loader)\n      Puppet::Pops::Types::TypeParser.singleton.parse(type_string, loader)\n    rescue StandardError => e\n      raise ArgumentError, _(\"Parsing of type string '\\\"%{type_string}\\\"' failed with message: <%{message}>.\\n\") % {\n        type_string: type_string,\n        message: e.message\n      }\n    end\n    private :internal_type_parse\n  end\n\n  # The LocalTypeAliasBuilder is used by the 'local_types' method to collect the individual\n  # type aliases given by the function's author.\n  #\n  class LocalTypeAliasesBuilder\n    attr_reader :local_types, :parser, :loader\n\n    def initialize(loader, name)\n      @loader = Puppet::Pops::Loader::PredefinedLoader.new(loader, :\"local_function_#{name}\", loader.environment)\n      @local_types = []\n      # get the shared parser used by puppet's compiler\n      @parser = Puppet::Pops::Parser::EvaluatingParser.singleton()\n    end\n\n    # Defines a local type alias, the given string should be a Puppet Language type alias expression\n    # in string form without the leading 'type' keyword.\n    # Calls to local_type must be made before the first parameter definition or an error will\n    # be raised.\n    #\n    # @param assignment_string [String] a string on the form 'AliasType = ExistingType'\n    # @api public\n    #\n    def type(assignment_string)\n      # Get location to use in case of error - this produces ruby filename and where call to 'type' occurred\n      # but strips off the rest of the internal \"where\" as it is not meaningful to user.\n      #\n      rb_location = caller(1, 1).first\n      begin\n        result = parser.parse_string(\"type #{assignment_string}\", nil)\n      rescue StandardError => e\n        rb_location = rb_location.gsub(/:in.*$/, '')\n        # Create a meaningful location for parse errors - show both what went wrong with the parsing\n        # and in which ruby file it was found.\n        raise ArgumentError, _(\"Parsing of 'type \\\"%{assignment_string}\\\"' failed with message: <%{message}>.\\n\" \\\n                               \"Called from <%{ruby_file_location}>\") % {\n                                 assignment_string: assignment_string,\n                                 message: e.message,\n                                 ruby_file_location: rb_location\n                               }\n      end\n      unless result.body.is_a?(Puppet::Pops::Model::TypeAlias)\n        rb_location = rb_location.gsub(/:in.*$/, '')\n        raise ArgumentError, _(\"Expected a type alias assignment on the form 'AliasType = T', got '%{assignment_string}'.\\n\" \\\n                               \"Called from <%{ruby_file_location}>\") % {\n                                 assignment_string: assignment_string,\n                                 ruby_file_location: rb_location\n                               }\n      end\n      @local_types << result.body\n    end\n  end\n\n  # @note WARNING: This style of creating functions is not public. It is a system\n  #   under development that will be used for creating \"system\" functions.\n  #\n  # This is a private, internal, system for creating functions. It supports\n  # everything that the public function definition system supports as well as a\n  # few extra features such as injection of well known parameters.\n  #\n  # @api private\n  class InternalFunction < Function\n    # @api private\n    def self.builder\n      InternalDispatchBuilder.new(dispatcher, Puppet::Pops::Types::PCallableType::DEFAULT, loader)\n    end\n\n    # Allows the implementation of a function to call other functions by name and pass the caller\n    # scope. The callable functions are those visible to the same loader that loaded this function\n    # (the calling function).\n    #\n    # @param scope [Puppet::Parser::Scope] The caller scope\n    # @param function_name [String] The name of the function\n    # @param *args [Object] splat of arguments\n    # @return [Object] The result returned by the called function\n    #\n    # @api public\n    def call_function_with_scope(scope, function_name, *args, &block)\n      internal_call_function(scope, function_name, args, &block)\n    end\n  end\n\n  class Function3x < InternalFunction\n    # Table of optimized parameter names - 0 to 5 parameters\n    PARAM_NAMES = [\n      [],\n      ['p0'].freeze,\n      %w[p0 p1].freeze,\n      %w[p0 p1 p2].freeze,\n      %w[p0 p1 p2 p3].freeze,\n      %w[p0 p1 p2 p3 p4].freeze\n    ]\n\n    # Creates an anonymous Function3x class that wraps a 3x function\n    #\n    # @api private\n    def self.create_function(func_name, func_info, loader)\n      func_name = func_name.to_s\n\n      # Creates an anonymous class to represent the function\n      # The idea being that it is garbage collected when there are no more\n      # references to it.\n      #\n      # (Do not give the class the block here, as instance variables should be set first)\n      the_class = Class.new(Function3x)\n\n      unless loader.nil?\n        the_class.instance_variable_set(:'@loader', loader.private_loader)\n      end\n\n      the_class.instance_variable_set(:'@func_name', func_name)\n      the_class.instance_variable_set(:'@method3x', :\"function_#{func_name}\")\n\n      # Make the anonymous class appear to have the class-name <func_name>\n      # Even if this class is not bound to such a symbol in a global ruby scope and\n      # must be resolved via the loader.\n      # This also overrides any attempt to define a name method in the given block\n      # (Since it redefines it)\n      #\n      the_class.instance_eval do\n        def name\n          @func_name\n        end\n\n        def loader\n          @loader\n        end\n\n        def method3x\n          @method3x\n        end\n      end\n\n      # Add the method that is called - it simply delegates to\n      # the 3.x function by calling it via the calling scope using the @method3x symbol\n      # :\"function_#{name}\".\n      #\n      # When function is not an rvalue function, make sure it produces nil\n      #\n      the_class.class_eval do\n        # Bypasses making the  call via the dispatcher to make sure errors\n        # are reported exactly the same way as in 3x. The dispatcher is still needed as it is\n        # used to support other features than calling.\n        #\n        def call(scope, *args, &block)\n          result = catch(:return) do\n            mapped_args = Puppet::Pops::Evaluator::Runtime3FunctionArgumentConverter.map_args(args, scope, '')\n            # this is the scope.function_xxx(...) call\n            return scope.send(self.class.method3x, mapped_args)\n          end\n          result.value\n        rescue Puppet::Pops::Evaluator::Next => jumper\n          begin\n            throw :next, jumper.value\n          rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION\n            raise Puppet::ParseError.new(\"next() from context where this is illegal\", jumper.file, jumper.line)\n          end\n        rescue Puppet::Pops::Evaluator::Return => jumper\n          begin\n            throw :return, jumper\n          rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION\n            raise Puppet::ParseError.new(\"return() from context where this is illegal\", jumper.file, jumper.line)\n          end\n        end\n      end\n\n      # Create a dispatcher based on func_info\n      type, names = Puppet::Functions.any_signature(*from_to_names(func_info))\n      last_captures_rest = (type.size_range[1] == Float::INFINITY)\n\n      # The method '3x_function' here is a dummy as the dispatcher is not used for calling, only for information.\n      the_class.dispatcher.add(Puppet::Pops::Functions::Dispatch.new(type, '3x_function', names, last_captures_rest))\n      # The function class is returned as the result of the create function method\n      the_class\n    end\n\n    # Compute min and max number of arguments and a list of constructed\n    # parameter names p0 - pn (since there are no parameter names in 3x functions).\n    #\n    # @api private\n    def self.from_to_names(func_info)\n      arity = func_info[:arity]\n      if arity.nil?\n        arity = -1\n      end\n      if arity < 0\n        from = -arity - 1 # arity -1 is 0 min param, -2 is min 1 param\n        to = :default     # infinite range\n        count = -arity    # the number of named parameters\n      else\n        count = from = to = arity\n      end\n      # Names of parameters, up to 5 are optimized and use frozen version\n      # Note that (0..count-1) produces expected empty array for count == 0, 0-n for count >= 1\n      names = count <= 5 ? PARAM_NAMES[count] : (0..count - 1).map { |n| \"p#{n}\" }\n      [from, to, names]\n    end\n  end\n\n  # Injection and Weaving of parameters\n  # ---\n  # It is possible to inject and weave a set of well known parameters into a call.\n  # These extra parameters are not part of the parameters passed from the Puppet\n  # logic, and  they can not be overridden by parameters given as arguments in the\n  # call. They are invisible to the Puppet Language.\n  #\n  # @example using injected parameters\n  #   Puppet::Functions.create_function('test') do\n  #     dispatch :test do\n  #       param 'Scalar', 'a'\n  #       param 'Scalar', 'b'\n  #       scope_param\n  #     end\n  #     def test(a, b, scope)\n  #       a > b ? scope['a'] : scope['b']\n  #     end\n  #   end\n  #\n  # The function in the example above is called like this:\n  #\n  #     test(10, 20)\n  #\n  # @api private\n  class InternalDispatchBuilder < DispatcherBuilder\n    # Inject parameter for `Puppet::Parser::Scope`\n    def scope_param\n      inject(:scope)\n    end\n\n    # Inject parameter for `Puppet::Pal::ScriptCompiler`\n    def script_compiler_param\n      inject(:pal_script_compiler)\n    end\n\n    # Inject a parameter getting a cached hash for this function\n    def cache_param\n      inject(:cache)\n    end\n\n    # Inject parameter for `Puppet::Pal::CatalogCompiler`\n    def compiler_param\n      inject(:pal_catalog_compiler)\n    end\n\n    # Inject parameter for either `Puppet::Pal::CatalogCompiler` or `Puppet::Pal::ScriptCompiler`\n    def pal_compiler_param\n      inject(:pal_compiler)\n    end\n\n    private\n\n    def inject(injection_name)\n      @injections << injection_name\n      # mark what should be picked for this position when dispatching\n      @weaving << [@injections.size() - 1]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/generate/models/type/property.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  module Generate\n    module Models\n      module Type\n        # A model for resource type properties and parameters.\n        class Property\n          # Gets the name of the property as a Puppet string literal.\n          attr_reader :name\n\n          # Gets the Puppet type of the property.\n          attr_reader :type\n\n          # Gets the doc string of the property.\n          attr_reader :doc\n\n          # Initializes a property model.\n          # @param property [Puppet::Property] The Puppet property to model.\n          # @return [void]\n          def initialize(property)\n            @name = Puppet::Pops::Types::StringConverter.convert(property.name.to_s, '%p')\n            @type = self.class.get_puppet_type(property)\n            @doc = property.doc.strip\n            @is_namevar = property.isnamevar?\n          end\n\n          # Determines if this property is a namevar.\n          # @return [Boolean] Returns true if the property is a namevar or false if not.\n          def is_namevar?\n            @is_namevar\n          end\n\n          # Gets the Puppet type for a property.\n          # @param property [Puppet::Property] The Puppet property to get the Puppet type for.\n          # @return [String] Returns the string representing the Puppet type.\n          def self.get_puppet_type(property)\n            # HACK: the value collection does not expose the underlying value information at all\n            #       thus this horribleness to get the underlying values hash\n            regexes = []\n            strings = []\n            values = property.value_collection.instance_variable_get('@values') || {}\n            values.each do |_, value|\n              if value.regex?\n                regexes << Puppet::Pops::Types::StringConverter.convert(value.name, '%p')\n                next\n              end\n\n              strings << Puppet::Pops::Types::StringConverter.convert(value.name.to_s, '%p')\n              value.aliases.each do |a|\n                strings << Puppet::Pops::Types::StringConverter.convert(a.to_s, '%p')\n              end\n            end\n\n            # If no string or regexes, default to Any type\n            return 'Any' if strings.empty? && regexes.empty?\n\n            # Calculate a variant of supported values\n            # Note that boolean strings are mapped to Variant[Boolean, Enum['true', 'false']]\n            # because of tech debt...\n            enum    = strings.empty? ? nil : \"Enum[#{strings.join(', ')}]\"\n            pattern = regexes.empty? ? nil : \"Pattern[#{regexes.join(', ')}]\"\n            boolean = strings.include?('\\'true\\'') || strings.include?('\\'false\\'') ? 'Boolean' : nil\n            variant = [boolean, enum, pattern].compact\n            return variant[0] if variant.size == 1\n\n            \"Variant[#{variant.join(', ')}]\"\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/generate/models/type/type.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/generate/models/type/property'\n\nmodule Puppet\n  module Generate\n    module Models\n      module Type\n        # A model for Puppet resource types.\n        class Type\n          # Gets the name of the type as a Puppet string literal.\n          attr_reader :name\n\n          # Gets the doc string of the type.\n          attr_reader :doc\n\n          # Gets the properties of the type.\n          attr_reader :properties\n\n          # Gets the parameters of the type.\n          attr_reader :parameters\n\n          # Gets the title patterns of the type\n          attr_reader :title_patterns\n\n          # Gets the isomorphic member attribute of the type\n          attr_reader :isomorphic\n\n          # Gets the capability member attribute of the type\n          attr_reader :capability\n\n          # Initializes a type model.\n          # @param type [Puppet::Type] The Puppet type to model.\n          # @return [void]\n          def initialize(type)\n            @name = Puppet::Pops::Types::StringConverter.convert(type.name.to_s, '%p')\n            @doc = type.doc.strip\n            @properties = type.properties.map { |p| Property.new(p) }\n            @parameters = type.parameters.map do |name|\n              Property.new(type.paramclass(name))\n            end\n            sc = Puppet::Pops::Types::StringConverter.singleton\n            @title_patterns = type.title_patterns.to_h do |mapping|\n              [\n                sc.convert(mapping[0], '%p'),\n                sc.convert(mapping[1].map do |names|\n                  next if names.empty?\n                  raise Puppet::Error, _('title patterns that use procs are not supported.') unless names.size == 1\n\n                  names[0].to_s\n                end, '%p')\n              ]\n            end\n            @isomorphic = type.isomorphic?\n            # continue to emit capability as false when rendering the ERB\n            # template, so that pcore modules generated prior to puppet7 can be\n            # read by puppet7 and vice-versa.\n            @capability = false\n          end\n\n          def render(template)\n            template.result(binding)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/generate/templates/type/pcore.erb",
    "content": "# This file was automatically generated on <%= Time.now %>.\n# Use the 'puppet generate types' command to regenerate this file.\n\n<%- unless doc.empty? -%>\n  <%- doc.each_line do |line| -%>\n# <%= line -%>\n  <%- end %>\n<%- end -%>\nPuppet::Resource::ResourceType3.new(\n  <%= name %>,\n  [\n<%- properties.each_with_index do |property, index| -%>\n  <%- unless property.doc.empty? -%>\n    <%- property.doc.each_line do |line| -%>\n    # <%= line -%>\n    <%- end %>\n  <%- end -%>\n    Puppet::Resource::Param(<%= property.type %>, <%= property.name %><% if property.is_namevar? %>, true<% end %>)<% if index + 1 < properties.size %>,\n\n    <%- end -%>\n<%- end %>\n  ],\n  [\n<%- parameters.each_with_index do |parameter, index| -%>\n  <%- unless parameter.doc.empty? -%>\n    <%- parameter.doc.each_line do |line| -%>\n    # <%= line -%>\n    <%- end %>\n  <%- end -%>\n    Puppet::Resource::Param(<%= parameter.type %>, <%= parameter.name %><% if parameter.is_namevar? %>, true<% end %>)<% if index + 1 < parameters.size %>,\n\n    <%- end -%>\n<%- end %>\n  ],\n  {\n<%- title_patterns.each_with_index do |mapping, index| -%>\n    <%= mapping[0] %> => <%= mapping[1] %><%= \",\" if index + 1 < title_patterns.size %>\n<%- end -%>\n  },\n  <%= isomorphic -%>,\n  <%= capability -%>\n)\n"
  },
  {
    "path": "lib/puppet/generate/type.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'erb'\nrequire 'fileutils'\nrequire_relative '../../puppet/util/autoload'\nrequire_relative '../../puppet/generate/models/type/type'\n\nmodule Puppet\n  module Generate\n    # Responsible for generating type definitions in Puppet\n    class Type\n      # Represents an input to the type generator\n      class Input\n        # Gets the path to the input.\n        attr_reader :path\n\n        # Gets the format to use for generating the output file.\n        attr_reader :format\n\n        # Initializes an input.\n        # @param base [String] The base path where the input is located.\n        # @param path [String] The path to the input file.\n        # @param format [Symbol] The format to use for generation.\n        # @return [void]\n        def initialize(base, path, format)\n          @base = base\n          @path = path\n          self.format = format\n        end\n\n        # Gets the expected resource type name for the input.\n        # @return [Symbol] Returns the expected resource type name for the input.\n        def type_name\n          File.basename(@path, '.rb').to_sym\n        end\n\n        # Sets the format to use for this input.\n        # @param format [Symbol] The format to use for generation.\n        # @return [Symbol] Returns the new format.\n        def format=(format)\n          format = format.to_sym\n          raise _(\"unsupported format '%{format}'.\") % { format: format } unless self.class.supported_format?(format)\n\n          @format = format\n        end\n\n        # Determines if the output file is up-to-date with respect to the input file.\n        # @param [String, nil] The path to output to, or nil if determined by input\n        # @return [Boolean] Returns true if the output is up-to-date or false if not.\n        def up_to_date?(outputdir)\n          f = effective_output_path(outputdir)\n          Puppet::FileSystem.exist?(f) && (Puppet::FileSystem.stat(@path) <=> Puppet::FileSystem.stat(f)) <= 0\n        end\n\n        # Gets the filename of the output file.\n        # @return [String] Returns the name to the output file.\n        def output_name\n          @output_name ||=\n            case @format\n            when :pcore\n              \"#{File.basename(@path, '.rb')}.pp\"\n            else\n              raise _(\"unsupported format '%{format}'.\") % { format: @format }\n            end\n        end\n\n        # Gets the path to the output file.\n        # @return [String] Returns the path to the output file.\n        def output_path\n          @output_path ||=\n            case @format\n            when :pcore\n              File.join(@base, 'pcore', 'types', output_name)\n            else\n              raise _(\"unsupported format '%{format}'.\") % { format: @format }\n            end\n        end\n\n        # Sets the path to the output file.\n        # @param path [String] The new path to the output file.\n        # @return [String] Returns the new path to the output file.\n        def output_path=(path)\n          @output_path = path\n        end\n\n        # Returns the outputpath to use given an outputdir that may be nil\n        # If outputdir is not nil, the returned path is relative to that outpudir\n        # otherwise determined by this input.\n        # @param [String, nil] The outputdirectory to use, or nil if to be determined by this Input\n        def effective_output_path(outputdir)\n          outputdir ? File.join(outputdir, output_name) : output_path\n        end\n\n        # Gets the path to the template to use for this input.\n        # @return [String] Returns the path to the template.\n        def template_path\n          File.join(File.dirname(__FILE__), 'templates', 'type', \"#{@format}.erb\")\n        end\n\n        # Gets the string representation of the input.\n        # @return [String] Returns the string representation of the input.\n        def to_s\n          @path\n        end\n\n        # Determines if the given format is supported\n        # @param format [Symbol] The format to use for generation.\n        # @return [Boolean] Returns true if the format is supported or false if not.\n        def self.supported_format?(format)\n          [:pcore].include?(format)\n        end\n      end\n\n      # Finds the inputs for the generator.\n      # @param format [Symbol] The format to use.\n      # @param environment [Puppet::Node::Environment] The environment to search for inputs. Defaults to the current environment.\n      # @return [Array<Input>] Returns the array of inputs.\n      def self.find_inputs(format = :pcore, environment = Puppet.lookup(:current_environment))\n        Puppet.debug \"Searching environment '#{environment.name}' for custom types.\"\n        inputs = []\n        environment.modules.each do |mod|\n          directory = File.join(Puppet::Util::Autoload.cleanpath(mod.plugin_directory), 'puppet', 'type')\n          unless Puppet::FileSystem.exist?(directory)\n            Puppet.debug \"Skipping '#{mod.name}' module because it contains no custom types.\"\n            next\n          end\n\n          Puppet.debug \"Searching '#{mod.name}' module for custom types.\"\n          Dir.glob(\"#{directory}/*.rb\") do |file|\n            next unless Puppet::FileSystem.file?(file)\n\n            Puppet.debug \"Found custom type source file '#{file}'.\"\n            inputs << Input.new(mod.path, file, format)\n          end\n        end\n\n        # Sort the inputs by path\n        inputs.sort_by!(&:path)\n      end\n\n      def self.bad_input?\n        @bad_input\n      end\n\n      # Generates files for the given inputs.\n      # If a file is up to date (newer than input) it is kept.\n      # If a file is out of date it is regenerated.\n      # If there is a file for a non existing output in a given output directory it is removed.\n      # If using input specific output removal must be made by hand if input is removed.\n      #\n      # @param inputs [Array<Input>] The inputs to generate files for.\n      # @param outputdir [String, nil] the outputdir where all output should be generated, or nil if next to input\n      # @param force [Boolean] True to force the generation of the output files (skip up-to-date checks) or false if not.\n      # @return [void]\n      def self.generate(inputs, outputdir = nil, force = false)\n        # remove files for non existing inputs\n        unless outputdir.nil?\n          filenames_to_keep = inputs.map(&:output_name)\n          existing_files = Puppet::FileSystem.children(outputdir).map { |f| Puppet::FileSystem.basename(f) }\n          files_to_remove = existing_files - filenames_to_keep\n          files_to_remove.each do |f|\n            Puppet::FileSystem.unlink(File.join(outputdir, f))\n          end\n          Puppet.notice(_(\"Removed output '%{files_to_remove}' for non existing inputs\") % { files_to_remove: files_to_remove }) unless files_to_remove.empty?\n        end\n\n        if inputs.empty?\n          Puppet.notice _('No custom types were found.')\n          return nil\n        end\n\n        templates = {}\n        templates.default_proc = lambda { |_hash, key|\n          raise _(\"template was not found at '%{key}'.\") % { key: key } unless Puppet::FileSystem.file?(key)\n\n          template = Puppet::Util.create_erb(File.read(key))\n          template.filename = key\n          template\n        }\n\n        up_to_date = true\n        @bad_input = false\n\n        Puppet.notice _('Generating Puppet resource types.')\n        inputs.each do |input|\n          if !force && input.up_to_date?(outputdir)\n            Puppet.debug \"Skipping '#{input}' because it is up-to-date.\"\n            next\n          end\n\n          up_to_date = false\n\n          type_name = input.type_name\n          Puppet.debug \"Loading custom type '#{type_name}' in '#{input}'.\"\n          begin\n            require input.path\n          rescue SystemExit\n            raise\n          rescue Exception => e\n            # Log the exception and move on to the next input\n            @bad_input = true\n            Puppet.log_exception(e, _(\"Failed to load custom type '%{type_name}' from '%{input}': %{message}\") % { type_name: type_name, input: input, message: e.message })\n            next\n          end\n\n          # HACK: there's no way to get a type without loading it (sigh); for now, just get the types hash directly\n          types ||= Puppet::Type.instance_variable_get('@types')\n\n          # Assume the type follows the naming convention\n          type = types[type_name]\n          unless type\n            Puppet.err _(\"Custom type '%{type_name}' was not defined in '%{input}'.\") % { type_name: type_name, input: input }\n            next\n          end\n\n          # Create the model\n          begin\n            model = Models::Type::Type.new(type)\n          rescue Exception => e\n            @bad_input = true\n            # Move on to the next input\n            Puppet.log_exception(e, \"#{input}: #{e.message}\")\n            next\n          end\n\n          # Render the template\n          begin\n            result = model.render(templates[input.template_path])\n          rescue Exception => e\n            @bad_input = true\n            Puppet.log_exception(e)\n            raise\n          end\n\n          # Write the output file\n          begin\n            effective_output_path = input.effective_output_path(outputdir)\n            Puppet.notice _(\"Generating '%{effective_output_path}' using '%{format}' format.\") % { effective_output_path: effective_output_path, format: input.format }\n            FileUtils.mkdir_p(File.dirname(effective_output_path))\n            Puppet::FileSystem.open(effective_output_path, nil, 'w:UTF-8') do |file|\n              file.write(result)\n            end\n          rescue Exception => e\n            @bad_input = true\n            Puppet.log_exception(e, _(\"Failed to generate '%{effective_output_path}': %{message}\") % { effective_output_path: effective_output_path, message: e.message })\n            # Move on to the next input\n            next\n          end\n        end\n\n        Puppet.notice _('No files were generated because all inputs were up-to-date.') if up_to_date\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/gettext/config.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/platform'\nrequire_relative '../../puppet/file_system'\n\nmodule Puppet::GettextConfig\n  LOCAL_PATH = File.absolute_path('../../../locales', File.dirname(__FILE__))\n  POSIX_PATH = File.absolute_path('../../../../../share/locale', File.dirname(__FILE__))\n  WINDOWS_PATH = File.absolute_path('../../../../../../puppet/share/locale', File.dirname(__FILE__))\n\n  # This is the only domain name that won't be a symbol, making it unique from environments.\n  DEFAULT_TEXT_DOMAIN = 'default-text-domain'\n\n  # Load gettext helpers and track whether they're available.\n  # Used instead of features because we initialize gettext before features is available.\n  begin\n    require 'fast_gettext'\n    require 'locale'\n\n    # Make translation methods (e.g. `_()` and `n_()`) available everywhere.\n    class ::Object\n      include FastGettext::Translation\n    end\n\n    @gettext_loaded = true\n  rescue LoadError\n    # Stub out gettext's `_` and `n_()` methods, which attempt to load translations,\n    # with versions that do nothing\n    require_relative '../../puppet/gettext/stubs'\n    @gettext_loaded = false\n  end\n\n  # @api private\n  # Whether we were able to require fast_gettext and locale\n  # @return [Boolean] true if translation gems were successfully loaded\n  def self.gettext_loaded?\n    @gettext_loaded\n  end\n\n  # @api private\n  # Returns the currently selected locale from FastGettext,\n  # or 'en' of gettext has not been loaded\n  # @return [String] the active locale\n  def self.current_locale\n    if gettext_loaded?\n      FastGettext.default_locale\n    else\n      'en'\n    end\n  end\n\n  # @api private\n  # Returns a list of the names of the loaded text domains\n  # @return [[String]] the names of the loaded text domains\n  def self.loaded_text_domains\n    return [] if @gettext_disabled || !gettext_loaded?\n\n    FastGettext.translation_repositories.keys\n  end\n\n  # @api private\n  # Clears the translation repository for the given text domain,\n  # creating it if it doesn't exist, then adds default translations\n  # and switches to using this domain.\n  # @param [String, Symbol] domain_name the name of the domain to create\n  def self.reset_text_domain(domain_name)\n    return if @gettext_disabled || !gettext_loaded?\n\n    domain_name = domain_name.to_sym\n\n    Puppet.debug { \"Reset text domain to #{domain_name.inspect}\" }\n    FastGettext.add_text_domain(domain_name,\n                                type: :chain,\n                                chain: [],\n                                report_warning: false)\n    copy_default_translations(domain_name)\n    FastGettext.text_domain = domain_name\n  end\n\n  # @api private\n  # Resets the thread's configured text_domain to the default text domain.\n  # In Puppet Server, thread A may process a compile request that configures\n  # a domain, while thread B may invalidate that environment and delete the\n  # domain. That leaves thread A with an invalid text_domain selected.\n  # To avoid that, clear_text_domain after any processing that needs the\n  # non-default text domain.\n  def self.clear_text_domain\n    return if @gettext_disabled || !gettext_loaded?\n\n    FastGettext.text_domain = nil\n  end\n\n  # @api private\n  # Creates a default text domain containing the translations for\n  # Puppet as the start of chain. When semantic_puppet gets initialized,\n  # its translations are added to this chain. This is used as a cache\n  # so that all non-module translations only need to be loaded once as\n  # we create and reset environment-specific text domains.\n  #\n  # @return true if Puppet translations were successfully loaded, false\n  # otherwise\n  def self.create_default_text_domain\n    return if @gettext_disabled || !gettext_loaded?\n\n    FastGettext.add_text_domain(DEFAULT_TEXT_DOMAIN,\n                                type: :chain,\n                                chain: [],\n                                report_warning: false)\n    FastGettext.default_text_domain = DEFAULT_TEXT_DOMAIN\n\n    load_translations('puppet', puppet_locale_path, translation_mode(puppet_locale_path), DEFAULT_TEXT_DOMAIN)\n  end\n\n  # @api private\n  # Switches the active text domain, if the requested domain exists.\n  # @param [String, Symbol] domain_name the name of the domain to switch to\n  def self.use_text_domain(domain_name)\n    return if @gettext_disabled || !gettext_loaded?\n\n    domain_name = domain_name.to_sym\n\n    if FastGettext.translation_repositories.include?(domain_name)\n      Puppet.debug { \"Use text domain #{domain_name.inspect}\" }\n      FastGettext.text_domain = domain_name\n    else\n      Puppet.debug { \"Requested unknown text domain #{domain_name.inspect}\" }\n    end\n  end\n\n  # @api private\n  # Delete all text domains.\n  def self.delete_all_text_domains\n    FastGettext.translation_repositories.clear\n    FastGettext.default_text_domain = nil\n    FastGettext.text_domain = nil\n  end\n\n  # @api private\n  # Deletes the text domain with the given name\n  # @param [String, Symbol] domain_name the name of the domain to delete\n  def self.delete_text_domain(domain_name)\n    return if @gettext_disabled || !gettext_loaded?\n\n    domain_name = domain_name.to_sym\n\n    deleted = FastGettext.translation_repositories.delete(domain_name)\n    if FastGettext.text_domain == domain_name\n      Puppet.debug { \"Deleted current text domain #{domain_name.inspect}: #{!deleted.nil?}\" }\n      FastGettext.text_domain = nil\n    else\n      Puppet.debug { \"Deleted text domain #{domain_name.inspect}: #{!deleted.nil?}\" }\n    end\n  end\n\n  # @api private\n  # Deletes all text domains except the default one\n  def self.delete_environment_text_domains\n    return if @gettext_disabled || !gettext_loaded?\n\n    FastGettext.translation_repositories.keys.each do |key|\n      # do not clear default translations\n      next if key == DEFAULT_TEXT_DOMAIN\n\n      FastGettext.translation_repositories.delete(key)\n    end\n    FastGettext.text_domain = nil\n  end\n\n  # @api private\n  # Adds translations from the default text domain to the specified\n  # text domain. Creates the default text domain if one does not exist\n  # (this will load Puppet's translations).\n  #\n  # Since we are currently (Nov 2017) vendoring semantic_puppet, in normal\n  # flows these translations will be copied along with Puppet's.\n  #\n  # @param [Symbol] domain_name the name of the domain to add translations to\n  def self.copy_default_translations(domain_name)\n    return if @gettext_disabled || !gettext_loaded?\n\n    if FastGettext.default_text_domain.nil?\n      create_default_text_domain\n    end\n\n    puppet_translations = FastGettext.translation_repositories[FastGettext.default_text_domain].chain\n    FastGettext.translation_repositories[domain_name].chain.push(*puppet_translations)\n  end\n\n  # @api private\n  # Search for puppet gettext config files\n  # @return [String] path to the config, or nil if not found\n  def self.puppet_locale_path\n    if Puppet::FileSystem.exist?(LOCAL_PATH)\n      LOCAL_PATH\n    elsif Puppet::Util::Platform.windows? && Puppet::FileSystem.exist?(WINDOWS_PATH)\n      WINDOWS_PATH\n    elsif !Puppet::Util::Platform.windows? && Puppet::FileSystem.exist?(POSIX_PATH)\n      POSIX_PATH\n    else\n      nil\n    end\n  end\n\n  # @api private\n  # Determine which translation file format to use\n  # @param [String] conf_path the path to the gettext config file\n  # @return [Symbol] :mo if in a package structure, :po otherwise\n  def self.translation_mode(conf_path)\n    if WINDOWS_PATH == conf_path || POSIX_PATH == conf_path\n      :mo\n    else\n      :po\n    end\n  end\n\n  # @api private\n  # Prevent future gettext initializations\n  def self.disable_gettext\n    @gettext_disabled = true\n  end\n\n  # @api private\n  # Attempt to load translations for the given project.\n  # @param [String] project_name the project whose translations we want to load\n  # @param [String] locale_dir the path to the directory containing translations\n  # @param [Symbol] file_format translation file format to use, either :po or :mo\n  # @return true if initialization succeeded, false otherwise\n  def self.load_translations(project_name, locale_dir, file_format, text_domain = FastGettext.text_domain)\n    if project_name.nil? || project_name.empty?\n      raise Puppet::Error, \"A project name must be specified in order to initialize translations.\"\n    end\n\n    return false if @gettext_disabled || !@gettext_loaded\n\n    return false unless locale_dir && Puppet::FileSystem.exist?(locale_dir)\n\n    unless file_format == :po || file_format == :mo\n      raise Puppet::Error, \"Unsupported translation file format #{file_format}; please use :po or :mo\"\n    end\n\n    add_repository_to_domain(project_name, locale_dir, file_format, text_domain)\n    true\n  end\n\n  # @api private\n  # Add the translations for this project to the domain's repository chain\n  # chain for the currently selected text domain, if needed.\n  # @param [String] project_name the name of the project for which to load translations\n  # @param [String] locale_dir the path to the directory containing translations\n  # @param [Symbol] file_format the format of the translations files, :po or :mo\n  def self.add_repository_to_domain(project_name, locale_dir, file_format, text_domain = FastGettext.text_domain)\n    return if @gettext_disabled || !gettext_loaded?\n\n    current_chain = FastGettext.translation_repositories[text_domain].chain\n\n    repository = FastGettext::TranslationRepository.build(project_name,\n                                                          path: locale_dir,\n                                                          type: file_format,\n                                                          report_warning: false)\n    current_chain << repository\n  end\n\n  # @api private\n  # Sets FastGettext's locale to the current system locale\n  def self.setup_locale\n    return if @gettext_disabled || !gettext_loaded?\n\n    set_locale(Locale.current.language)\n  end\n\n  # @api private\n  # Sets the language in which to display strings.\n  # @param [String] locale the language portion of a locale string (e.g. \"ja\")\n  def self.set_locale(locale)\n    return if @gettext_disabled || !gettext_loaded?\n\n    # make sure we're not using the `available_locales` machinery\n    FastGettext.default_available_locales = nil\n\n    FastGettext.default_locale = locale\n  end\nend\n"
  },
  {
    "path": "lib/puppet/gettext/module_translations.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/gettext/config'\n\nmodule Puppet::ModuleTranslations\n  # @api private\n  # Loads translation files for each of the specified modules,\n  # if present. Requires the modules to have `forge_name` specified.\n  # @param [[Module]] modules a list of modules for which to\n  #        load translations\n  def self.load_from_modulepath(modules)\n    modules.each do |mod|\n      next unless mod.forge_name && mod.has_translations?(Puppet::GettextConfig.current_locale)\n\n      module_name = mod.forge_name.tr('/', '-')\n      if Puppet::GettextConfig.load_translations(module_name, mod.locale_directory, :po)\n        Puppet.debug { \"Loaded translations for #{module_name}.\" }\n      elsif Puppet::GettextConfig.gettext_loaded?\n        Puppet.debug { \"Could not find translation files for #{module_name} at #{mod.locale_directory}. Skipping translation initialization.\" }\n      else\n        Puppet.warn_once(\"gettext_unavailable\", \"gettext_unavailable\", \"No gettext library found, skipping translation initialization.\")\n      end\n    end\n  end\n\n  # @api private\n  # Loads translation files that have been pluginsync'd for modules\n  # from the $vardir.\n  # @param [String] vardir the path to Puppet's vardir\n  def self.load_from_vardir(vardir)\n    locale = Puppet::GettextConfig.current_locale\n    Dir.glob(\"#{vardir}/locales/#{locale}/*.po\") do |f|\n      module_name = File.basename(f, \".po\")\n      if Puppet::GettextConfig.load_translations(module_name, File.join(vardir, \"locales\"), :po)\n        Puppet.debug { \"Loaded translations for #{module_name}.\" }\n      elsif Puppet::GettextConfig.gettext_loaded?\n        Puppet.debug { \"Could not load translations for #{module_name}.\" }\n      else\n        Puppet.warn_once(\"gettext_unavailable\", \"gettext_unavailable\", \"No gettext library found, skipping translation initialization.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/gettext/stubs.rb",
    "content": "# frozen_string_literal: true\n\n# These stub the translation methods normally brought in\n# by FastGettext. Used when Gettext could not be properly\n# initialized.\ndef _(msg)\n  msg\nend\n\ndef n_(*args, &block)\n  plural = args[2] == 1 ? args[0] : args[1]\n  block ? block.call : plural\nend\n"
  },
  {
    "path": "lib/puppet/graph/key.rb",
    "content": "# frozen_string_literal: true\n\n# Sequential, nestable keys for tracking order of insertion in \"the graph\"\n# @api private\nclass Puppet::Graph::Key\n  include Comparable\n\n  attr_reader :value\n  protected :value\n\n  def initialize(value = [0])\n    @value = value\n  end\n\n  def next\n    next_values = @value.clone\n    next_values[-1] += 1\n    Puppet::Graph::Key.new(next_values)\n  end\n\n  def down\n    Puppet::Graph::Key.new(@value + [0])\n  end\n\n  def <=>(other)\n    @value <=> other.value\n  end\nend\n"
  },
  {
    "path": "lib/puppet/graph/prioritizer.rb",
    "content": "# frozen_string_literal: true\n\n# Base, template method, class for Prioritizers. This provides the basic\n# tracking facilities used.\n#\n# @api private\nclass Puppet::Graph::Prioritizer\n  def initialize\n    @priority = {}\n  end\n\n  def forget(key)\n    @priority.delete(key)\n  end\n\n  def record_priority_for(key, priority)\n    @priority[key] = priority\n  end\n\n  def generate_priority_for(key)\n    raise NotImplementedError\n  end\n\n  def generate_priority_contained_in(container, key)\n    raise NotImplementedError\n  end\n\n  def priority_of(key)\n    @priority[key]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/graph/rb_tree_map.rb",
    "content": "# frozen_string_literal: true\n\n# Algorithms and Containers project is Copyright (c) 2009 Kanwei Li\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the 'Software'), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n#\n# A RbTreeMap is a map that is stored in sorted order based on the order of its keys. This ordering is\n# determined by applying the function <=> to compare the keys. No duplicate values for keys are allowed,\n# so duplicate values are overwritten.\n#\n# A major advantage of RBTreeMap over a Hash is the fact that keys are stored in order and can thus be\n# iterated over in order. This is useful for many datasets.\n#\n# The implementation is adapted from Robert Sedgewick's Left Leaning Red-Black Tree implementation,\n# which can be found at https://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java\n#\n# Most methods have O(log n) complexity.\n\nclass Puppet::Graph::RbTreeMap\n  include Enumerable\n\n  attr_reader :size\n\n  alias_method :length, :size\n\n  # Create and initialize a new empty TreeMap.\n  def initialize\n    @root = nil\n    @size = 0\n  end\n\n  # Insert an item with an associated key into the TreeMap, and returns the item inserted\n  #\n  # Complexity: O(log n)\n  #\n  # map = Containers::TreeMap.new\n  # map.push(\"MA\", \"Massachusetts\") #=> \"Massachusetts\"\n  # map.get(\"MA\") #=> \"Massachusetts\"\n  def push(key, value)\n    @root = insert(@root, key, value)\n    @root.color = :black\n    value\n  end\n  alias_method :[]=, :push\n\n  # Return true if key is found in the TreeMap, false otherwise\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.has_key?(\"GA\") #=> true\n  #   map.has_key?(\"DE\") #=> false\n  def has_key?(key)\n    !get_recursive(@root, key).nil?\n  end\n\n  # Return the item associated with the key, or nil if none found.\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.get(\"GA\") #=> \"Georgia\"\n  def get(key)\n    node = get_recursive(@root, key)\n    node ? node.value : nil\n    node.value if node\n  end\n  alias_method :[], :get\n\n  # Return the smallest key in the map.\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.min_key #=> \"GA\"\n  def min_key\n    @root.nil? ? nil : min_recursive(@root).key\n  end\n\n  # Return the largest key in the map.\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.max_key #=> \"MA\"\n  def max_key\n    @root.nil? ? nil : max_recursive(@root).key\n  end\n\n  # Deletes the item and key if it's found, and returns the item. Returns nil\n  # if key is not present.\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.delete(\"MA\") #=> \"Massachusetts\"\n  def delete(key)\n    result = nil\n    if @root\n      return unless has_key? key\n\n      @root, result = delete_recursive(@root, key)\n      @root.color = :black if @root\n      @size -= 1\n    end\n    result\n  end\n\n  # Returns true if the tree is empty, false otherwise\n  def empty?\n    @root.nil?\n  end\n\n  # Deletes the item with the smallest key and returns the item. Returns nil\n  # if key is not present.\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.delete_min #=> \"Massachusetts\"\n  #   map.size #=> 1\n  def delete_min\n    result = nil\n    if @root\n      @root, result = delete_min_recursive(@root)\n      @root.color = :black if @root\n      @size -= 1\n    end\n    result\n  end\n\n  # Deletes the item with the largest key and returns the item. Returns nil\n  # if key is not present.\n  #\n  # Complexity: O(log n)\n  #\n  #   map = Containers::TreeMap.new\n  #   map.push(\"MA\", \"Massachusetts\")\n  #   map.push(\"GA\", \"Georgia\")\n  #   map.delete_max #=> \"Georgia\"\n  #   map.size #=> 1\n  def delete_max\n    result = nil\n    if @root\n      @root, result = delete_max_recursive(@root)\n      @root.color = :black if @root\n      @size -= 1\n    end\n    result\n  end\n\n  # Yields [key, value] pairs in order by key.\n  def each(&blk)\n    recursive_yield(@root, &blk)\n  end\n\n  def first\n    return nil unless @root\n\n    node = min_recursive(@root)\n    [node.key, node.value]\n  end\n\n  def last\n    return nil unless @root\n\n    node = max_recursive(@root)\n    [node.key, node.value]\n  end\n\n  def to_hash\n    @root ? @root.to_hash : {}\n  end\n\n  class Node # :nodoc: all\n    attr_accessor :color, :key, :value, :left, :right\n\n    def initialize(key, value)\n      @key = key\n      @value = value\n      @color = :red\n      @left = nil\n      @right = nil\n    end\n\n    def to_hash\n      h = {\n        :node => {\n          :key => @key,\n          :value => @value,\n          :color => @color,\n        }\n      }\n      h[:left] = left.to_hash if @left\n      h[:right] = right.to_hash if @right\n      h\n    end\n\n    def red?\n      @color == :red\n    end\n\n    def colorflip\n      @color       = @color == :red       ? :black : :red\n      @left.color  = @left.color == :red  ? :black : :red\n      @right.color = @right.color == :red ? :black : :red\n    end\n\n    def rotate_left\n      r = @right\n      r_key = r.key\n      r_value = r.value\n      b = r.left\n      r.left = @left\n      @left = r\n      @right = r.right\n      r.right = b\n      r.color = :red\n      r.key = @key\n      r.value = @value\n      @key = r_key\n      @value = r_value\n      self\n    end\n\n    def rotate_right\n      l = @left\n      l_key = l.key\n      l_value = l.value\n      b = l.right\n      l.right = @right\n      @right = l\n      @left = l.left\n      l.left = b\n      l.color = :red\n      l.key = @key\n      l.value = @value\n      @key = l_key\n      @value = l_value\n      self\n    end\n\n    def move_red_left\n      colorflip\n      if @right.left && @right.left.red?\n        @right.rotate_right\n        rotate_left\n        colorflip\n      end\n      self\n    end\n\n    def move_red_right\n      colorflip\n      if @left.left && @left.left.red?\n        rotate_right\n        colorflip\n      end\n      self\n    end\n\n    def fixup\n      rotate_left if @right && @right.red?\n      rotate_right if (@left && @left.red?) && (@left.left && @left.left.red?)\n      colorflip if (@left && @left.red?) && (@right && @right.red?)\n\n      self\n    end\n  end\n\n  private\n\n  def recursive_yield(node, &blk)\n    return unless node\n\n    recursive_yield(node.left, &blk)\n    yield node.key, node.value\n    recursive_yield(node.right, &blk)\n  end\n\n  def delete_recursive(node, key)\n    if (key <=> node.key) == -1\n      node.move_red_left if !isred(node.left) && !isred(node.left.left)\n      node.left, result = delete_recursive(node.left, key)\n    else\n      node.rotate_right if isred(node.left)\n      if ((key <=> node.key) == 0) && node.right.nil?\n        return nil, node.value\n      end\n\n      if !isred(node.right) && !isred(node.right.left)\n        node.move_red_right\n      end\n      if (key <=> node.key) == 0\n        result = node.value\n        min_child = min_recursive(node.right)\n        node.value = min_child.value\n        node.key = min_child.key\n        node.right = delete_min_recursive(node.right).first\n      else\n        node.right, result = delete_recursive(node.right, key)\n      end\n    end\n    [node.fixup, result]\n  end\n\n  def delete_min_recursive(node)\n    if node.left.nil?\n      return nil, node.value\n    end\n\n    if !isred(node.left) && !isred(node.left.left)\n      node.move_red_left\n    end\n    node.left, result = delete_min_recursive(node.left)\n\n    [node.fixup, result]\n  end\n\n  def delete_max_recursive(node)\n    if isred(node.left)\n      node = node.rotate_right\n    end\n    return nil, node.value if node.right.nil?\n\n    if !isred(node.right) && !isred(node.right.left)\n      node.move_red_right\n    end\n    node.right, result = delete_max_recursive(node.right)\n\n    [node.fixup, result]\n  end\n\n  def get_recursive(node, key)\n    return nil if node.nil?\n\n    case key <=> node.key\n    when 0 then node\n    when -1 then get_recursive(node.left, key)\n    when  1 then get_recursive(node.right, key)\n    end\n  end\n\n  def min_recursive(node)\n    return node if node.left.nil?\n\n    min_recursive(node.left)\n  end\n\n  def max_recursive(node)\n    return node if node.right.nil?\n\n    max_recursive(node.right)\n  end\n\n  def insert(node, key, value)\n    unless node\n      @size += 1\n      return Node.new(key, value)\n    end\n\n    case key <=> node.key\n    when 0 then node.value = value\n    when -1 then node.left = insert(node.left, key, value)\n    when  1 then node.right = insert(node.right, key, value)\n    end\n\n    node.rotate_left if node.right && node.right.red?\n    node.rotate_right if node.left && node.left.red? && node.left.left && node.left.left.red?\n    node.colorflip if node.left && node.left.red? && node.right && node.right.red?\n    node\n  end\n\n  def isred(node)\n    return false if node.nil?\n\n    node.color == :red\n  end\nend\n"
  },
  {
    "path": "lib/puppet/graph/relationship_graph.rb",
    "content": "# frozen_string_literal: true\n\n# The relationship graph is the final form of a puppet catalog in\n# which all dependency edges are explicitly in the graph. This form of the\n# catalog is used to traverse the graph in the order in which resources are\n# managed.\n#\n# @api private\nclass Puppet::Graph::RelationshipGraph < Puppet::Graph::SimpleGraph\n  attr_reader :blockers\n\n  def initialize(prioritizer)\n    super()\n\n    @prioritizer = prioritizer\n\n    @ready = Puppet::Graph::RbTreeMap.new\n    @generated = {}\n    @done = {}\n    @blockers = {}\n    @providerless_types = []\n  end\n\n  def populate_from(catalog)\n    add_all_resources_as_vertices(catalog)\n    build_manual_dependencies\n    build_autorelation_dependencies(catalog)\n\n    write_graph(:relationships) if catalog.host_config?\n\n    replace_containers_with_anchors(catalog)\n\n    write_graph(:expanded_relationships) if catalog.host_config?\n  end\n\n  def add_vertex(vertex, priority = nil)\n    super(vertex)\n\n    if priority\n      @prioritizer.record_priority_for(vertex, priority)\n    else\n      @prioritizer.generate_priority_for(vertex)\n    end\n  end\n\n  def add_relationship(f, t, label = nil)\n    super(f, t, label)\n    @ready.delete(@prioritizer.priority_of(t))\n  end\n\n  def remove_vertex!(vertex)\n    super\n    @prioritizer.forget(vertex)\n  end\n\n  def resource_priority(resource)\n    @prioritizer.priority_of(resource)\n  end\n\n  # Enqueue the initial set of resources, those with no dependencies.\n  def enqueue_roots\n    vertices.each do |v|\n      @blockers[v] = direct_dependencies_of(v).length\n      enqueue(v) if @blockers[v] == 0\n    end\n  end\n\n  # Decrement the blocker count for the resource by 1. If the number of\n  # blockers is unknown, count them and THEN decrement by 1.\n  def unblock(resource)\n    @blockers[resource] ||= direct_dependencies_of(resource).select { |r2| !@done[r2] }.length\n    if @blockers[resource] > 0\n      @blockers[resource] -= 1\n    else\n      resource.warning _(\"appears to have a negative number of dependencies\")\n    end\n    @blockers[resource] <= 0\n  end\n\n  def clear_blockers\n    @blockers.clear\n  end\n\n  def enqueue(*resources)\n    resources.each do |resource|\n      @ready[@prioritizer.priority_of(resource)] = resource\n    end\n  end\n\n  def finish(resource)\n    direct_dependents_of(resource).each do |v|\n      enqueue(v) if unblock(v)\n    end\n    @done[resource] = true\n  end\n\n  def next_resource\n    @ready.delete_min\n  end\n\n  def traverse(options = {}, &block)\n    continue_while = options[:while] || -> { true }\n    pre_process = options[:pre_process] || ->(resource) {}\n    overly_deferred_resource_handler = options[:overly_deferred_resource_handler] || ->(resource) {}\n    canceled_resource_handler = options[:canceled_resource_handler] || ->(resource) {}\n    teardown = options[:teardown] || -> {}\n    graph_cycle_handler = options[:graph_cycle_handler] || -> { [] }\n\n    cycles = report_cycles_in_graph\n    if cycles\n      graph_cycle_handler.call(cycles)\n    end\n\n    enqueue_roots\n\n    deferred_resources = []\n\n    while continue_while.call() && (resource = next_resource)\n      if resource.suitable?\n        made_progress = true\n\n        pre_process.call(resource)\n\n        yield resource\n\n        finish(resource)\n      else\n        deferred_resources << resource\n      end\n\n      next unless @ready.empty? and deferred_resources.any?\n\n      if made_progress\n        enqueue(*deferred_resources)\n      else\n        deferred_resources.each do |res|\n          overly_deferred_resource_handler.call(res)\n          finish(res)\n        end\n      end\n\n      made_progress = false\n      deferred_resources = []\n    end\n\n    unless continue_while.call()\n      while (resource = next_resource)\n        canceled_resource_handler.call(resource)\n        finish(resource)\n      end\n    end\n\n    teardown.call()\n  end\n\n  private\n\n  def add_all_resources_as_vertices(catalog)\n    catalog.resources.each do |vertex|\n      add_vertex(vertex)\n    end\n  end\n\n  def build_manual_dependencies\n    vertices.each do |vertex|\n      vertex.builddepends.each do |edge|\n        add_edge(edge)\n      end\n    end\n  end\n\n  def build_autorelation_dependencies(catalog)\n    vertices.each do |vertex|\n      [:require, :subscribe].each do |rel_type|\n        vertex.send(\"auto#{rel_type}\".to_sym, catalog).each do |edge|\n          # don't let automatic relationships conflict with manual ones.\n          next if edge?(edge.source, edge.target)\n\n          if edge?(edge.target, edge.source)\n            vertex.debug \"Skipping automatic relationship with #{edge.source}\"\n          else\n            vertex.debug \"Adding auto#{rel_type} relationship with #{edge.source}\"\n            if rel_type == :require\n              edge.event = :NONE\n            else\n              edge.callback = :refresh\n              edge.event = :ALL_EVENTS\n            end\n            add_edge(edge)\n          end\n        end\n      end\n\n      [:before, :notify].each do |rel_type|\n        vertex.send(\"auto#{rel_type}\".to_sym, catalog).each do |edge|\n          # don't let automatic relationships conflict with manual ones.\n          next if edge?(edge.target, edge.source)\n\n          if edge?(edge.source, edge.target)\n            vertex.debug \"Skipping automatic relationship with #{edge.target}\"\n          else\n            vertex.debug \"Adding auto#{rel_type} relationship with #{edge.target}\"\n            if rel_type == :before\n              edge.event = :NONE\n            else\n              edge.callback = :refresh\n              edge.event = :ALL_EVENTS\n            end\n            add_edge(edge)\n          end\n        end\n      end\n    end\n  end\n\n  # Impose our container information on another graph by using it\n  # to replace any container vertices X with a pair of vertices\n  # { admissible_X and completed_X } such that\n  #\n  #    0) completed_X depends on admissible_X\n  #    1) contents of X each depend on admissible_X\n  #    2) completed_X depends on each on the contents of X\n  #    3) everything which depended on X depends on completed_X\n  #    4) admissible_X depends on everything X depended on\n  #    5) the containers and their edges must be removed\n  #\n  # Note that this requires attention to the possible case of containers\n  # which contain or depend on other containers, but has the advantage\n  # that the number of new edges created scales linearly with the number\n  # of contained vertices regardless of how containers are related;\n  # alternatives such as replacing container-edges with content-edges\n  # scale as the product of the number of external dependencies, which is\n  # to say geometrically in the case of nested / chained containers.\n  #\n  Default_label = { :callback => :refresh, :event => :ALL_EVENTS }\n  def replace_containers_with_anchors(catalog)\n    stage_class      = Puppet::Type.type(:stage)\n    whit_class       = Puppet::Type.type(:whit)\n    component_class  = Puppet::Type.type(:component)\n    containers = catalog.resources.find_all { |v| (v.is_a?(component_class) or v.is_a?(stage_class)) and vertex?(v) }\n    #\n    # These two hashes comprise the aforementioned attention to the possible\n    #   case of containers that contain / depend on other containers; they map\n    #   containers to their sentinels but pass other vertices through.  Thus we\n    #   can \"do the right thing\" for references to other vertices that may or\n    #   may not be containers.\n    #\n    admissible = Hash.new { |_h, k| k }\n    completed  = Hash.new { |_h, k| k }\n    containers.each { |x|\n      admissible[x] = whit_class.new(:name => \"admissible_#{x.ref}\", :catalog => catalog)\n      completed[x]  = whit_class.new(:name => \"completed_#{x.ref}\",  :catalog => catalog)\n\n      # This copies the original container's tags over to the two anchor whits.\n      # Without this, tags are not propagated to the container's resources.\n      admissible[x].set_tags(x)\n      completed[x].set_tags(x)\n\n      priority = @prioritizer.priority_of(x)\n      add_vertex(admissible[x], priority)\n      add_vertex(completed[x], priority)\n    }\n    #\n    # Implement the six requirements listed above\n    #\n    containers.each { |x|\n      contents = catalog.adjacent(x, :direction => :out)\n      add_edge(admissible[x], completed[x]) if contents.empty? # (0)\n      contents.each { |v|\n        add_edge(admissible[x], admissible[v], Default_label) # (1)\n        add_edge(completed[v], completed[x], Default_label) # (2)\n      }\n      # (3) & (5)\n      adjacent(x, :direction => :in, :type => :edges).each { |e|\n        add_edge(completed[e.source], admissible[x], e.label)\n        remove_edge! e\n      }\n      # (4) & (5)\n      adjacent(x, :direction => :out, :type => :edges).each { |e|\n        add_edge(completed[x], admissible[e.target], e.label)\n        remove_edge! e\n      }\n    }\n    containers.each { |x| remove_vertex! x } # (5)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/graph/sequential_prioritizer.rb",
    "content": "# frozen_string_literal: true\n\n# This implements a priority in which keys are given values that will keep them\n# in the same priority in which they priorities are requested. Nested\n# structures (those in which a key is contained within another key) are\n# preserved in such a way that child keys are after the parent and before the\n# key after the parent.\n#\n# @api private\nclass Puppet::Graph::SequentialPrioritizer < Puppet::Graph::Prioritizer\n  def initialize\n    super\n    @container = {}\n    @count = Puppet::Graph::Key.new\n  end\n\n  def generate_priority_for(key)\n    if priority_of(key).nil?\n      @count = @count.next\n      record_priority_for(key, @count)\n    else\n      priority_of(key)\n    end\n  end\n\n  def generate_priority_contained_in(container, key)\n    @container[container] ||= priority_of(container).down\n    priority = @container[container].next\n    record_priority_for(key, priority)\n    @container[container] = priority\n    priority\n  end\nend\n"
  },
  {
    "path": "lib/puppet/graph/simple_graph.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/external/dot'\nrequire_relative '../../puppet/relationship'\nrequire 'set'\n\n# A hopefully-faster graph class to replace the use of GRATR.\nclass Puppet::Graph::SimpleGraph\n  include Puppet::Util::PsychSupport\n\n  #\n  # All public methods of this class must maintain (assume ^ ensure) the following invariants, where \"=~=\" means\n  # equiv. up to order:\n  #\n  #   @in_to.keys =~= @out_to.keys =~= all vertices\n  #   @in_to.values.collect { |x| x.values }.flatten =~= @out_from.values.collect { |x| x.values }.flatten =~= all edges\n  #   @in_to[v1][v2] =~= @out_from[v2][v1] =~= all edges from v1 to v2\n  #   @in_to   [v].keys =~= vertices with edges leading to   v\n  #   @out_from[v].keys =~= vertices with edges leading from v\n  #   no operation may shed reference loops (for gc)\n  #   recursive operation must scale with the depth of the spanning trees, or better (e.g. no recursion over the set\n  #       of all vertices, etc.)\n  #\n  # This class is intended to be used with DAGs.  However, if the\n  # graph has a cycle, it will not cause non-termination of any of the\n  # algorithms.\n  #\n  def initialize\n    @in_to = {}\n    @out_from = {}\n    @upstream_from = {}\n    @downstream_from = {}\n  end\n\n  # Clear our graph.\n  def clear\n    @in_to.clear\n    @out_from.clear\n    @upstream_from.clear\n    @downstream_from.clear\n  end\n\n  # Which resources the given resource depends on.\n  def dependencies(resource)\n    vertex?(resource) ? upstream_from_vertex(resource).keys : []\n  end\n\n  # Which resources depend upon the given resource.\n  def dependents(resource)\n    vertex?(resource) ? downstream_from_vertex(resource).keys : []\n  end\n\n  # Whether our graph is directed.  Always true.  Used to produce dot files.\n  def directed?\n    true\n  end\n\n  # Determine all of the leaf nodes below a given vertex.\n  def leaves(vertex, direction = :out)\n    tree_from_vertex(vertex, direction).keys.find_all { |c| adjacent(c, :direction => direction).empty? }\n  end\n\n  # Collect all of the edges that the passed events match.  Returns\n  # an array of edges.\n  def matching_edges(event, base = nil)\n    source = base || event.resource\n\n    unless vertex?(source)\n      Puppet.warning _(\"Got an event from invalid vertex %{source}\") % { source: source.ref }\n      return []\n    end\n    # Get all of the edges that this vertex should forward events\n    # to, which is the same thing as saying all edges directly below\n    # This vertex in the graph.\n    @out_from[source].values.flatten.find_all { |edge| edge.match?(event.name) }\n  end\n\n  # Return a reversed version of this graph.\n  def reversal\n    result = self.class.new\n    vertices.each { |vertex| result.add_vertex(vertex) }\n    edges.each do |edge|\n      result.add_edge edge.class.new(edge.target, edge.source, edge.label)\n    end\n    result\n  end\n\n  # Return the size of the graph.\n  def size\n    vertices.size\n  end\n\n  def to_a\n    vertices\n  end\n\n  # This is a simple implementation of Tarjan's algorithm to find strongly\n  # connected components in the graph; this is a fairly ugly implementation,\n  # because I can't just decorate the vertices themselves.\n  #\n  # This method has an unhealthy relationship with the find_cycles_in_graph\n  # method below, which contains the knowledge of how the state object is\n  # maintained.\n  def tarjan(root, s)\n    # initialize the recursion stack we use to work around the nasty lack of a\n    # decent Ruby stack.\n    recur = [{ :node => root }]\n\n    until recur.empty?\n      frame = recur.last\n      vertex = frame[:node]\n\n      case frame[:step]\n      when nil then\n        s[:index][vertex]   = s[:number]\n        s[:lowlink][vertex] = s[:number]\n        s[:number]          = s[:number] + 1\n\n        s[:stack].push(vertex)\n        s[:seen][vertex] = true\n\n        frame[:children] = adjacent(vertex)\n        frame[:step]     = :children\n\n      when :children then\n        if frame[:children].length > 0 then\n          child = frame[:children].shift\n          if !s[:index][child] then\n            # Never seen, need to recurse.\n            frame[:step] = :after_recursion\n            frame[:child] = child\n            recur.push({ :node => child })\n          elsif s[:seen][child] then\n            s[:lowlink][vertex] = [s[:lowlink][vertex], s[:index][child]].min\n          end\n        else\n          if s[:lowlink][vertex] == s[:index][vertex] then\n            this_scc = []\n            loop do\n              top = s[:stack].pop\n              s[:seen][top] = false\n              this_scc << top\n              break if top == vertex\n            end\n            s[:scc] << this_scc\n          end\n          recur.pop # done with this node, finally.\n        end\n\n      when :after_recursion then\n        s[:lowlink][vertex] = [s[:lowlink][vertex], s[:lowlink][frame[:child]]].min\n        frame[:step] = :children\n\n      else\n        fail \"#{frame[:step]} is an unknown step\"\n      end\n    end\n  end\n\n  # Find all cycles in the graph by detecting all the strongly connected\n  # components, then eliminating everything with a size of one as\n  # uninteresting - which it is, because it can't be a cycle. :)\n  #\n  # This has an unhealthy relationship with the 'tarjan' method above, which\n  # it uses to implement the detection of strongly connected components.\n  def find_cycles_in_graph\n    state = {\n      :number => 0, :index => {}, :lowlink => {}, :scc => [],\n      :stack => [], :seen => {}\n    }\n\n    # we usually have a disconnected graph, must walk all possible roots\n    vertices.each do |vertex|\n      unless state[:index][vertex] then\n        tarjan vertex, state\n      end\n    end\n\n    # To provide consistent results to the user, given that a hash is never\n    # assured to return the same order, and given our graph processing is\n    # based on hash tables, we need to sort the cycles internally, as well as\n    # the set of cycles.\n    #\n    # Given we are in a failure state here, any extra cost is more or less\n    # irrelevant compared to the cost of a fix - which is on a human\n    # time-scale.\n    state[:scc].select do |component|\n      multi_vertex_component?(component) || single_vertex_referring_to_self?(component)\n    end.map(&:sort).sort\n  end\n\n  # Perform a BFS on the sub graph representing the cycle, with a view to\n  # generating a sufficient set of paths to report the cycle meaningfully, and\n  # ideally usefully, for the end user.\n  #\n  # BFS is preferred because it will generally report the shortest paths\n  # through the graph first, which are more likely to be interesting to the\n  # user.  I think; it would be interesting to verify that. --daniel 2011-01-23\n  def paths_in_cycle(cycle, max_paths = 1)\n    # TRANSLATORS \"negative or zero\" refers to the count of paths\n    raise ArgumentError, _(\"negative or zero max_paths\") if max_paths < 1\n\n    # Calculate our filtered outbound vertex lists...\n    adj = {}\n    cycle.each do |vertex|\n      adj[vertex] = adjacent(vertex).select { |s| cycle.member? s }\n    end\n\n    found = []\n\n    # frame struct is vertex, [path]\n    stack = [[cycle.first, []]]\n    while frame = stack.shift # rubocop:disable Lint/AssignmentInCondition\n      if frame[1].member?(frame[0]) then\n        found << frame[1] + [frame[0]]\n        break if found.length >= max_paths\n      else\n        adj[frame[0]].each do |to|\n          stack.push [to, frame[1] + [frame[0]]]\n        end\n      end\n    end\n\n    found.sort\n  end\n\n  # @return [Array] array of dependency cycles (arrays)\n  def report_cycles_in_graph\n    cycles = find_cycles_in_graph\n    number_of_cycles = cycles.length\n    return if number_of_cycles == 0\n\n    message = n_(\"Found %{num} dependency cycle:\\n\", \"Found %{num} dependency cycles:\\n\", number_of_cycles) % { num: number_of_cycles }\n\n    cycles.each do |cycle|\n      paths = paths_in_cycle(cycle)\n      message += paths.map { |path| '(' + path.join(' => ') + ')' }.join('\\n') + '\\n'\n    end\n\n    if Puppet[:graph] then\n      filename = write_cycles_to_graph(cycles)\n      message += _(\"Cycle graph written to %{filename}.\") % { filename: filename }\n    else\n      # TRANSLATORS '--graph' refers to a command line option and OmniGraffle and GraphViz are program names and should not be translated\n      message += _(\"Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz\")\n    end\n    Puppet.err(message)\n    cycles\n  end\n\n  def write_cycles_to_graph(cycles)\n    # This does not use the DOT graph library, just writes the content\n    # directly.  Given the complexity of this, there didn't seem much point\n    # using a heavy library to generate exactly the same content. --daniel 2011-01-27\n    graph = [\"digraph Resource_Cycles {\"]\n    graph << '  label = \"Resource Cycles\"'\n\n    cycles.each do |cycle|\n      paths_in_cycle(cycle, 10).each do |path|\n        graph << path.map { |v| '\"' + v.to_s.gsub(/\"/, '\\\\\"') + '\"' }.join(\" -> \")\n      end\n    end\n\n    graph << '}'\n\n    filename = File.join(Puppet[:graphdir], \"cycles.dot\")\n    # DOT files are assumed to be UTF-8 by default - http://www.graphviz.org/doc/info/lang.html\n    File.open(filename, \"w:UTF-8\") { |f| f.puts graph }\n    filename\n  end\n\n  # Add a new vertex to the graph.\n  def add_vertex(vertex)\n    @in_to[vertex]    ||= {}\n    @out_from[vertex] ||= {}\n  end\n\n  # Remove a vertex from the graph.\n  def remove_vertex!(v)\n    return unless vertex?(v)\n\n    @upstream_from.clear\n    @downstream_from.clear\n    (@in_to[v].values + @out_from[v].values).flatten.each { |e| remove_edge!(e) }\n    @in_to.delete(v)\n    @out_from.delete(v)\n  end\n\n  # Test whether a given vertex is in the graph.\n  def vertex?(v)\n    @in_to.include?(v)\n  end\n\n  # Return a list of all vertices.\n  def vertices\n    @in_to.keys\n  end\n\n  # Add a new edge.  The graph user has to create the edge instance,\n  # since they have to specify what kind of edge it is.\n  def add_edge(e, *a)\n    return add_relationship(e, *a) unless a.empty?\n\n    e = Puppet::Relationship.from_data_hash(e) if e.is_a?(Hash)\n    @upstream_from.clear\n    @downstream_from.clear\n    add_vertex(e.source)\n    add_vertex(e.target)\n    # Avoid multiple lookups here. This code is performance critical\n    arr = (@in_to[e.target][e.source] ||= [])\n    arr << e unless arr.include?(e)\n    arr = (@out_from[e.source][e.target] ||= [])\n    arr << e unless arr.include?(e)\n  end\n\n  def add_relationship(source, target, label = nil)\n    add_edge Puppet::Relationship.new(source, target, label)\n  end\n\n  # Find all matching edges.\n  def edges_between(source, target)\n    (@out_from[source] || {})[target] || []\n  end\n\n  # Is there an edge between the two vertices?\n  def edge?(source, target)\n    vertex?(source) and vertex?(target) and @out_from[source][target]\n  end\n\n  def edges\n    @in_to.values.collect(&:values).flatten\n  end\n\n  def each_edge\n    @in_to.each { |_t, ns| ns.each { |_s, es| es.each { |e| yield e } } }\n  end\n\n  # Remove an edge from our graph.\n  def remove_edge!(e)\n    if edge?(e.source, e.target)\n      @upstream_from.clear\n      @downstream_from.clear\n      @in_to[e.target].delete e.source if (@in_to[e.target][e.source] -= [e]).empty?\n      @out_from[e.source].delete e.target if (@out_from[e.source][e.target] -= [e]).empty?\n    end\n  end\n\n  # Find adjacent edges.\n  def adjacent(v, options = {})\n    ns = (options[:direction] == :in) ? @in_to[v] : @out_from[v]\n    return [] unless ns\n\n    (options[:type] == :edges) ? ns.values.flatten : ns.keys\n  end\n\n  # Just walk the tree and pass each edge.\n  def walk(source, direction)\n    # Use an iterative, breadth-first traversal of the graph. One could do\n    # this recursively, but Ruby's slow function calls and even slower\n    # recursion make the shorter, recursive algorithm cost-prohibitive.\n    stack = [source]\n    seen = Set.new\n    until stack.empty?\n      node = stack.shift\n      next if seen.member? node\n\n      connected = adjacent(node, :direction => direction)\n      connected.each do |target|\n        yield node, target\n      end\n      stack.concat(connected)\n      seen << node\n    end\n  end\n\n  # A different way of walking a tree, and a much faster way than the\n  # one that comes with GRATR.\n  def tree_from_vertex(start, direction = :out)\n    predecessor = {}\n    walk(start, direction) do |parent, child|\n      predecessor[child] = parent\n    end\n    predecessor\n  end\n\n  def downstream_from_vertex(v)\n    return @downstream_from[v] if @downstream_from[v]\n\n    result = @downstream_from[v] = {}\n    @out_from[v].keys.each do |node|\n      result[node] = 1\n      result.update(downstream_from_vertex(node))\n    end\n    result\n  end\n\n  def direct_dependents_of(v)\n    (@out_from[v] || {}).keys\n  end\n\n  def upstream_from_vertex(v)\n    return @upstream_from[v] if @upstream_from[v]\n\n    result = @upstream_from[v] = {}\n    @in_to[v].keys.each do |node|\n      result[node] = 1\n      result.update(upstream_from_vertex(node))\n    end\n    result\n  end\n\n  def direct_dependencies_of(v)\n    (@in_to[v] || {}).keys\n  end\n\n  # Return an array of the edge-sets between a series of n+1 vertices (f=v0,v1,v2...t=vn)\n  #   connecting the two given vertices.  The ith edge set is an array containing all the\n  #   edges between v(i) and v(i+1); these are (by definition) never empty.\n  #\n  #     * if f == t, the list is empty\n  #     * if they are adjacent the result is an array consisting of\n  #       a single array (the edges from f to t)\n  #     * and so on by induction on a vertex m between them\n  #     * if there is no path from f to t, the result is nil\n  #\n  # This implementation is not particularly efficient; it's used in testing where clarity\n  #   is more important than last-mile efficiency.\n  #\n  def path_between(f, t)\n    if f == t\n      []\n    elsif direct_dependents_of(f).include?(t)\n      [edges_between(f, t)]\n    elsif dependents(f).include?(t)\n      m = (dependents(f) & direct_dependencies_of(t)).first\n      path_between(f, m) + path_between(m, t)\n    else\n      nil\n    end\n  end\n\n  # LAK:FIXME This is just a paste of the GRATR code with slight modifications.\n\n  # Return a DOT::DOTDigraph for directed graphs or a DOT::DOTSubgraph for an\n  # undirected Graph.  _params_ can contain any graph property specified in\n  # rdot.rb. If an edge or vertex label is a kind of Hash then the keys\n  # which match +dot+ properties will be used as well.\n  def to_dot_graph(params = {})\n    params['name'] ||= self.class.name.tr(':', '_')\n    fontsize   = params['fontsize'] || '8'\n    graph      = (directed? ? DOT::DOTDigraph : DOT::DOTSubgraph).new(params)\n    edge_klass = directed? ? DOT::DOTDirectedEdge : DOT::DOTEdge\n    vertices.each do |v|\n      name = v.ref\n      params = { 'name' => stringify(name),\n                 'fontsize' => fontsize,\n                 'label' => name }\n      v_label = v.ref\n      params.merge!(v_label) if v_label and v_label.is_a? Hash\n      graph << DOT::DOTNode.new(params)\n    end\n    edges.each do |e|\n      params = { 'from' => stringify(e.source.ref),\n                 'to' => stringify(e.target.ref),\n                 'fontsize' => fontsize }\n      e_label = e.ref\n      params.merge!(e_label) if e_label and e_label.is_a? Hash\n      graph << edge_klass.new(params)\n    end\n    graph\n  end\n\n  def stringify(s)\n    %(\"#{s.gsub('\"', '\\\\\"')}\")\n  end\n\n  # Output the dot format as a string\n  def to_dot(params = {}) to_dot_graph(params).to_s; end\n\n  # Produce the graph files if requested.\n  def write_graph(name)\n    return unless Puppet[:graph]\n\n    file = File.join(Puppet[:graphdir], \"#{name}.dot\")\n    # DOT files are assumed to be UTF-8 by default - http://www.graphviz.org/doc/info/lang.html\n    File.open(file, \"w:UTF-8\") { |f|\n      f.puts to_dot(\"name\" => name.to_s.capitalize)\n    }\n  end\n\n  # This flag may be set to true to use the new YAML serialization\n  # format (where @vertices is a simple list of vertices rather than a\n  # list of VertexWrapper objects).  Deserialization supports both\n  # formats regardless of the setting of this flag.\n  class << self\n    attr_accessor :use_new_yaml_format\n  end\n  self.use_new_yaml_format = false\n\n  def initialize_from_hash(hash)\n    initialize\n    vertices = hash['vertices']\n    edges = hash['edges']\n    if vertices.is_a?(Hash)\n      # Support old (2.6) format\n      vertices = vertices.keys\n    end\n    vertices.each { |v| add_vertex(v) } unless vertices.nil?\n    edges.each { |e| add_edge(e) } unless edges.nil?\n  end\n\n  def to_data_hash\n    hash = { 'edges' => edges.map(&:to_data_hash) }\n    hash['vertices'] = if self.class.use_new_yaml_format\n                         vertices\n                       else\n                         # Represented in YAML using the old (version 2.6) format.\n                         result = {}\n                         vertices.each do |vertex|\n                           adjacencies = {}\n                           [:in, :out].each do |direction|\n                             direction_hash = {}\n                             adjacencies[direction.to_s] = direction_hash\n                             adjacent(vertex, :direction => direction, :type => :edges).each do |edge|\n                               other_vertex = direction == :in ? edge.source : edge.target\n                               (direction_hash[other_vertex.to_s] ||= []) << edge\n                             end\n                             direction_hash.each_pair { |key, edges| direction_hash[key] = edges.uniq.map(&:to_data_hash) }\n                           end\n                           vname = vertex.to_s\n                           result[vname] = { 'adjacencies' => adjacencies, 'vertex' => vname }\n                         end\n                         result\n                       end\n    hash\n  end\n\n  def multi_vertex_component?(component)\n    component.length > 1\n  end\n  private :multi_vertex_component?\n\n  def single_vertex_referring_to_self?(component)\n    if component.length == 1\n      vertex = component[0]\n      adjacent(vertex).include?(vertex)\n    else\n      false\n    end\n  end\n  private :single_vertex_referring_to_self?\nend\n"
  },
  {
    "path": "lib/puppet/graph.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Graph\n  require_relative 'graph/prioritizer'\n  require_relative 'graph/sequential_prioritizer'\n\n  require_relative 'graph/simple_graph'\n  require_relative 'graph/rb_tree_map'\n  require_relative 'graph/key'\n  require_relative 'graph/relationship_graph'\nend\n"
  },
  {
    "path": "lib/puppet/http/client.rb",
    "content": "# frozen_string_literal: true\n\n# The HTTP client provides methods for making `GET`, `POST`, etc requests to\n# HTTP(S) servers. It also provides methods for resolving Puppetserver REST\n# service endpoints using SRV records and settings (such as `server_list`,\n# `server`, `ca_server`, etc). Once a service endpoint has been resolved, there\n# are methods for making REST requests (such as getting a node, sending facts,\n# etc).\n#\n# The client uses persistent HTTP connections by default unless the `Connection:\n# close` header is specified and supports streaming response bodies.\n#\n# By default the client only trusts the Puppet CA for HTTPS connections. However,\n# if the `include_system_store` request option is set to true, then Puppet will\n# trust certificates in the puppet-agent CA bundle.\n#\n# @example To access the HTTP client:\n#   client = Puppet.runtime[:http]\n#\n# @example To make an HTTP GET request:\n#   response = client.get(URI(\"http://www.example.com\"))\n#\n# @example To make an HTTPS GET request, trusting the puppet CA and certs in Puppet's CA bundle:\n#   response = client.get(URI(\"https://www.example.com\"), options: { include_system_store: true })\n#\n# @example To use a URL containing special characters, such as spaces:\n#  response = client.get(URI(Puppet::Util.uri_encode(\"https://www.example.com/path to file\")))\n#\n# @example To pass query parameters:\n#   response = client.get(URI(\"https://www.example.com\"), query: {'q' => 'puppet'})\n#\n# @example To pass custom headers:\n#   response = client.get(URI(\"https://www.example.com\"), headers: {'Accept-Content' => 'application/json'})\n#\n# @example To check if the response is successful (2xx):\n#   response = client.get(URI(\"http://www.example.com\"))\n#   puts response.success?\n#\n# @example To get the response code and reason:\n#   response = client.get(URI(\"http://www.example.com\"))\n#   unless response.success?\n#     puts \"HTTP #{response.code} #{response.reason}\"\n#    end\n#\n# @example To read response headers:\n#   response = client.get(URI(\"http://www.example.com\"))\n#   puts response['Content-Type']\n#\n# @example To stream the response body:\n#   client.get(URI(\"http://www.example.com\")) do |response|\n#     if response.success?\n#       response.read_body do |data|\n#         puts data\n#       end\n#     end\n#   end\n#\n# @example To handle exceptions:\n#   begin\n#     client.get(URI(\"https://www.example.com\"))\n#   rescue Puppet::HTTP::ResponseError => e\n#     puts \"HTTP #{e.response.code} #{e.response.reason}\"\n#   rescue Puppet::HTTP::ConnectionError => e\n#     puts \"Connection error #{e.message}\"\n#   rescue Puppet::SSL::SSLError => e\n#     puts \"SSL error #{e.message}\"\n#   rescue Puppet::HTTP::HTTPError => e\n#     puts \"General HTTP error #{e.message}\"\n#   end\n#\n# @example To route to the `:puppet` service:\n#   session = client.create_session\n#   service = session.route_to(:puppet)\n#\n# @example To make a node request:\n#   node = service.get_node(Puppet[:certname], environment: 'production')\n#\n# @example To submit facts:\n#   facts = Puppet::Indirection::Facts.indirection.find(Puppet[:certname])\n#   service.put_facts(Puppet[:certname], environment: 'production', facts: facts)\n#\n# @example To submit a report to the `:report` service:\n#   report = Puppet::Transaction::Report.new\n#   service = session.route_to(:report)\n#   service.put_report(Puppet[:certname], report, environment: 'production')\n#\n# @api public\nclass Puppet::HTTP::Client\n  attr_reader :pool\n\n  # Create a new http client instance. Use `Puppet.runtime[:http]` to get\n  # the current client instead of creating an instance of this class.\n  #\n  # @param [Puppet::HTTP::Pool] pool pool of persistent Net::HTTP\n  #   connections\n  # @param [Puppet::SSL::SSLContext] ssl_context ssl context to be used for\n  #   connections\n  # @param [Puppet::SSL::SSLContext] system_ssl_context the system ssl context\n  #   used if :include_system_store is set to true\n  # @param [Integer] redirect_limit default number of HTTP redirections to allow\n  #   in a given request. Can also be specified per-request.\n  # @param [Integer] retry_limit number of HTTP retries allowed in a given\n  #   request\n  #\n  def initialize(pool: Puppet::HTTP::Pool.new(Puppet[:http_keepalive_timeout]), ssl_context: nil, system_ssl_context: nil, redirect_limit: 10, retry_limit: 100)\n    @pool = pool\n    @default_headers = {\n      'X-Puppet-Version' => Puppet.version,\n      'User-Agent' => Puppet[:http_user_agent],\n    }.freeze\n    @default_ssl_context = ssl_context\n    @default_system_ssl_context = system_ssl_context\n    @default_redirect_limit = redirect_limit\n    @retry_after_handler = Puppet::HTTP::RetryAfterHandler.new(retry_limit, Puppet[:runinterval])\n  end\n\n  # Create a new HTTP session. A session is the object through which services\n  # may be connected to and accessed.\n  #\n  # @return [Puppet::HTTP::Session] the newly created HTTP session\n  #\n  # @api public\n  def create_session\n    Puppet::HTTP::Session.new(self, build_resolvers)\n  end\n\n  # Open a connection to the given URI. It is typically not necessary to call\n  # this method as the client will create connections as needed when a request\n  # is made.\n  #\n  # @param [URI] uri the connection destination\n  # @param [Hash] options\n  # @option options [Puppet::SSL::SSLContext] :ssl_context (nil) ssl context to\n  #   be used for connections\n  # @option options [Boolean] :include_system_store (false) if we should include\n  #   the system store for connection\n  def connect(uri, options: {}, &block)\n    start = Time.now\n    verifier = nil\n    connected = false\n\n    site = Puppet::HTTP::Site.from_uri(uri)\n    if site.use_ssl?\n      ssl_context = options.fetch(:ssl_context, nil)\n      include_system_store = options.fetch(:include_system_store, false)\n      ctx = resolve_ssl_context(ssl_context, include_system_store)\n      verifier = Puppet::SSL::Verifier.new(site.host, ctx)\n    end\n\n    @pool.with_connection(site, verifier) do |http|\n      connected = true\n      if block_given?\n        yield http\n      end\n    end\n  rescue Net::OpenTimeout => e\n    raise_error(_(\"Request to %{uri} timed out connect operation after %{elapsed} seconds\") % { uri: uri, elapsed: elapsed(start) }, e, connected)\n  rescue Net::ReadTimeout => e\n    raise_error(_(\"Request to %{uri} timed out read operation after %{elapsed} seconds\") % { uri: uri, elapsed: elapsed(start) }, e, connected)\n  rescue EOFError => e\n    raise_error(_(\"Request to %{uri} interrupted after %{elapsed} seconds\") % { uri: uri, elapsed: elapsed(start) }, e, connected)\n  rescue Puppet::SSL::SSLError\n    raise\n  rescue Puppet::HTTP::HTTPError\n    raise\n  rescue => e\n    raise_error(_(\"Request to %{uri} failed after %{elapsed} seconds: %{message}\") %\n                { uri: uri, elapsed: elapsed(start), message: e.message }, e, connected)\n  end\n\n  # These options apply to all HTTP request methods\n  #\n  # @!macro [new] request_options\n  #   @param [Hash] options HTTP request options. Options not recognized by the\n  #     HTTP implementation will be ignored.\n  #   @option options [Puppet::SSL::SSLContext] :ssl_context (nil) ssl context to\n  #     be used for connections\n  #   @option options [Boolean] :include_system_store (false) if we should include\n  #     the system store for connection\n  #   @option options [Integer] :redirect_limit (10) The maximum number of HTTP\n  #     redirections to allow for this request.\n  #   @option options [Hash] :basic_auth A map of `:username` => `String` and\n  #     `:password` => `String`\n  #   @option options [String] :metric_id The metric id used to track metrics\n  #     on requests.\n\n  # Submits a GET HTTP request to the given url\n  #\n  # @param [URI] url the location to submit the http request\n  # @param [Hash] headers merged with the default headers defined by the client\n  # @param [Hash] params encoded and set as the url query\n  # @!macro request_options\n  #\n  # @yield [Puppet::HTTP::Response] if a block is given yields the response\n  #\n  # @return [Puppet::HTTP::Response] the response\n  #\n  # @api public\n  def get(url, headers: {}, params: {}, options: {}, &block)\n    url = encode_query(url, params)\n\n    request = Net::HTTP::Get.new(url, @default_headers.merge(headers))\n\n    execute_streaming(request, options: options, &block)\n  end\n\n  # Submits a HEAD HTTP request to the given url\n  #\n  # @param [URI] url the location to submit the http request\n  # @param [Hash] headers merged with the default headers defined by the client\n  # @param [Hash] params encoded and set as the url query\n  # @!macro request_options\n  #\n  # @return [Puppet::HTTP::Response] the response\n  #\n  # @api public\n  def head(url, headers: {}, params: {}, options: {})\n    url = encode_query(url, params)\n\n    request = Net::HTTP::Head.new(url, @default_headers.merge(headers))\n\n    execute_streaming(request, options: options)\n  end\n\n  # Submits a PUT HTTP request to the given url\n  #\n  # @param [URI] url the location to submit the http request\n  # @param [String] body the body of the PUT request\n  # @param [Hash] headers merged with the default headers defined by the client. The\n  #   `Content-Type` header is required and should correspond to the type of data passed\n  #   as the `body` argument.\n  # @param [Hash] params encoded and set as the url query\n  # @!macro request_options\n  #\n  # @return [Puppet::HTTP::Response] the response\n  #\n  # @api public\n  def put(url, body, headers: {}, params: {}, options: {})\n    raise ArgumentError, \"'put' requires a string 'body' argument\" unless body.is_a?(String)\n\n    url = encode_query(url, params)\n\n    request = Net::HTTP::Put.new(url, @default_headers.merge(headers))\n    request.body = body\n    request.content_length = body.bytesize\n\n    raise ArgumentError, \"'put' requires a 'content-type' header\" unless request['Content-Type']\n\n    execute_streaming(request, options: options)\n  end\n\n  # Submits a POST HTTP request to the given url\n  #\n  # @param [URI] url the location to submit the http request\n  # @param [String] body the body of the POST request\n  # @param [Hash] headers merged with the default headers defined by the client. The\n  #   `Content-Type` header is required and should correspond to the type of data passed\n  #   as the `body` argument.\n  # @param [Hash] params encoded and set as the url query\n  # @!macro request_options\n  #\n  # @yield [Puppet::HTTP::Response] if a block is given yields the response\n  #\n  # @return [Puppet::HTTP::Response] the response\n  #\n  # @api public\n  def post(url, body, headers: {}, params: {}, options: {}, &block)\n    raise ArgumentError, \"'post' requires a string 'body' argument\" unless body.is_a?(String)\n\n    url = encode_query(url, params)\n\n    request = Net::HTTP::Post.new(url, @default_headers.merge(headers))\n    request.body = body\n    request.content_length = body.bytesize\n\n    raise ArgumentError, \"'post' requires a 'content-type' header\" unless request['Content-Type']\n\n    execute_streaming(request, options: options, &block)\n  end\n\n  # Submits a DELETE HTTP request to the given url.\n  #\n  # @param [URI] url the location to submit the http request\n  # @param [Hash] headers merged with the default headers defined by the client\n  # @param [Hash] params encoded and set as the url query\n  # @!macro request_options\n  #\n  # @return [Puppet::HTTP::Response] the response\n  #\n  # @api public\n  def delete(url, headers: {}, params: {}, options: {})\n    url = encode_query(url, params)\n\n    request = Net::HTTP::Delete.new(url, @default_headers.merge(headers))\n\n    execute_streaming(request, options: options)\n  end\n\n  # Close persistent connections in the pool.\n  #\n  # @return [void]\n  #\n  # @api public\n  def close\n    @pool.close\n    @default_ssl_context = nil\n    @default_system_ssl_context = nil\n  end\n\n  def default_ssl_context\n    cert = Puppet::X509::CertProvider.new\n    password = cert.load_private_key_password\n\n    ssl = Puppet::SSL::SSLProvider.new\n    ctx = ssl.load_context(certname: Puppet[:certname], password: password)\n    ssl.print(ctx)\n    ctx\n  rescue => e\n    # TRANSLATORS: `message` is an already translated string of why SSL failed to initialize\n    Puppet.log_exception(e, _(\"Failed to initialize SSL: %{message}\") % { message: e.message })\n    # TRANSLATORS: `puppet agent -t` is a command and should not be translated\n    Puppet.err(_(\"Run `puppet agent -t`\"))\n    raise e\n  end\n\n  protected\n\n  def encode_query(url, params)\n    return url if params.empty?\n\n    url = url.dup\n    url.query = encode_params(params)\n    url\n  end\n\n  private\n\n  # Connect or borrow a connection from the pool to the host and port associated\n  # with the request's URL. Then execute the HTTP request, retrying and\n  # following redirects as needed, and return the HTTP response. The response\n  # body will always be fully drained/consumed when this method returns.\n  #\n  # If a block is provided, then the response will be yielded to the caller,\n  # allowing the response body to be streamed.\n  #\n  # If the request/response did not result in an exception and the caller did\n  # not ask for the connection to be closed (via Connection: close), then the\n  # connection will be returned to the pool.\n  #\n  # @yieldparam [Puppet::HTTP::Response] response The final response, after\n  # following redirects and retrying\n  # @return [Puppet::HTTP::Response]\n  def execute_streaming(request, options: {}, &block)\n    redirector = Puppet::HTTP::Redirector.new(options.fetch(:redirect_limit, @default_redirect_limit))\n\n    basic_auth = options.fetch(:basic_auth, nil)\n    unless basic_auth\n      if request.uri.user && request.uri.password\n        basic_auth = { user: request.uri.user, password: request.uri.password }\n      end\n    end\n\n    redirects = 0\n    retries = 0\n    response = nil\n    done = false\n\n    until done\n      connect(request.uri, options: options) do |http|\n        apply_auth(request, basic_auth) if redirects.zero?\n\n        # don't call return within the `request` block\n        close_and_sleep = nil\n        http.request(request) do |nethttp|\n          response = Puppet::HTTP::ResponseNetHTTP.new(request.uri, nethttp)\n          begin\n            Puppet.debug(\"HTTP #{request.method.upcase} #{request.uri} returned #{response.code} #{response.reason}\")\n\n            if redirector.redirect?(request, response)\n              request = redirector.redirect_to(request, response, redirects)\n              redirects += 1\n              next\n            elsif @retry_after_handler.retry_after?(request, response)\n              interval = @retry_after_handler.retry_after_interval(request, response, retries)\n              retries += 1\n              if interval\n                close_and_sleep = proc do\n                  if http.started?\n                    Puppet.debug(\"Closing connection for #{Puppet::HTTP::Site.from_uri(request.uri)}\")\n                    http.finish\n                  end\n                  Puppet.warning(_(\"Sleeping for %{interval} seconds before retrying the request\") % { interval: interval })\n                  ::Kernel.sleep(interval)\n                end\n                next\n              end\n            end\n\n            if block_given?\n              yield response\n            else\n              response.body\n            end\n          ensure\n            # we need to make sure the response body is fully consumed before\n            # the connection is put back in the pool, otherwise the response\n            # for one request could leak into a future response.\n            response.drain\n          end\n\n          done = true\n        end\n      ensure\n        # If a server responded with a retry, make sure the connection is closed and then\n        # sleep the specified time.\n        close_and_sleep.call if close_and_sleep\n      end\n    end\n\n    response\n  end\n\n  def expand_into_parameters(data)\n    data.inject([]) do |params, key_value|\n      key, value = key_value\n\n      expanded_value = case value\n                       when Array\n                         value.collect { |val| [key, val] }\n                       else\n                         [key_value]\n                       end\n\n      params.concat(expand_primitive_types_into_parameters(expanded_value))\n    end\n  end\n\n  def expand_primitive_types_into_parameters(data)\n    data.inject([]) do |params, key_value|\n      key, value = key_value\n      case value\n      when nil\n        params\n      when true, false, String, Symbol, Integer, Float\n        params << [key, value]\n      else\n        raise Puppet::HTTP::SerializationError, _(\"HTTP REST queries cannot handle values of type '%{klass}'\") % { klass: value.class }\n      end\n    end\n  end\n\n  def encode_params(params)\n    params = expand_into_parameters(params)\n    params.map do |key, value|\n      \"#{key}=#{Puppet::Util.uri_query_encode(value.to_s)}\"\n    end.join('&')\n  end\n\n  def elapsed(start)\n    (Time.now - start).to_f.round(3)\n  end\n\n  def raise_error(message, cause, connected)\n    if connected\n      raise Puppet::HTTP::HTTPError.new(message, cause)\n    else\n      raise Puppet::HTTP::ConnectionError.new(message, cause)\n    end\n  end\n\n  def resolve_ssl_context(ssl_context, include_system_store)\n    if ssl_context\n      raise Puppet::HTTP::HTTPError, \"The ssl_context and include_system_store parameters are mutually exclusive\" if include_system_store\n\n      ssl_context\n    elsif include_system_store\n      system_ssl_context\n    else\n      @default_ssl_context || Puppet.lookup(:ssl_context)\n    end\n  end\n\n  def system_ssl_context\n    return @default_system_ssl_context if @default_system_ssl_context\n\n    cert_provider = Puppet::X509::CertProvider.new\n    cacerts = cert_provider.load_cacerts || []\n\n    ssl = Puppet::SSL::SSLProvider.new\n    @default_system_ssl_context = ssl.create_system_context(cacerts: cacerts, include_client_cert: true)\n    ssl.print(@default_system_ssl_context)\n    @default_system_ssl_context\n  end\n\n  def apply_auth(request, basic_auth)\n    if basic_auth\n      request.basic_auth(basic_auth[:user], basic_auth[:password])\n    end\n  end\n\n  def build_resolvers\n    resolvers = []\n\n    if Puppet[:use_srv_records]\n      resolvers << Puppet::HTTP::Resolver::SRV.new(self, domain: Puppet[:srv_domain])\n    end\n\n    server_list_setting = Puppet.settings.setting(:server_list)\n    if server_list_setting.value && !server_list_setting.value.empty?\n      # use server list to resolve all services\n      services = Puppet::HTTP::Service::SERVICE_NAMES.dup\n\n      # except if it's been explicitly set\n      if Puppet.settings.set_by_config?(:ca_server)\n        services.delete(:ca)\n      end\n\n      if Puppet.settings.set_by_config?(:report_server)\n        services.delete(:report)\n      end\n\n      resolvers << Puppet::HTTP::Resolver::ServerList.new(self, server_list_setting: server_list_setting, default_port: Puppet[:serverport], services: services)\n    end\n\n    resolvers << Puppet::HTTP::Resolver::Settings.new(self)\n\n    resolvers.freeze\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/dns.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'resolv'\n\nmodule Puppet::HTTP\n  class DNS\n    class CacheEntry\n      attr_reader :records, :ttl, :resolution_time\n\n      def initialize(records)\n        @records = records\n        @resolution_time = Time.now\n        @ttl = choose_lowest_ttl(records)\n      end\n\n      def choose_lowest_ttl(records)\n        ttl = records.first.ttl\n        records.each do |rec|\n          if rec.ttl < ttl\n            ttl = rec.ttl\n          end\n        end\n        ttl\n      end\n    end\n\n    def initialize(resolver = Resolv::DNS.new)\n      @resolver = resolver\n\n      # Stores DNS records per service, along with their TTL\n      # and the time at which they were resolved, for cache\n      # eviction.\n      @record_cache = {}\n    end\n\n    # Iterate through the list of records for this service\n    # and yield each server and port pair. Records are only fetched\n    # via DNS query the first time and cached for the duration of their\n    # service's TTL thereafter.\n    # @param [String] domain the domain to search for\n    # @param [Symbol] service_name the key of the service we are querying\n    # @yields [String, Integer] server and port of selected record\n    def each_srv_record(domain, service_name = :puppet, &block)\n      if domain.nil? or domain.empty?\n        Puppet.debug \"Domain not known; skipping SRV lookup\"\n        return\n      end\n\n      Puppet.debug \"Searching for SRV records for domain: #{domain}\"\n\n      case service_name\n      when :puppet then service = '_x-puppet'\n      when :file   then service = '_x-puppet-fileserver'\n      else              service = \"_x-puppet-#{service_name}\"\n      end\n      record_name = \"#{service}._tcp.#{domain}\"\n\n      if @record_cache.has_key?(service_name) && !expired?(service_name)\n        records = @record_cache[service_name].records\n        Puppet.debug \"Using cached record for #{record_name}\"\n      else\n        records = @resolver.getresources(record_name, Resolv::DNS::Resource::IN::SRV)\n        if records.size > 0\n          @record_cache[service_name] = CacheEntry.new(records)\n        end\n        Puppet.debug \"Found #{records.size} SRV records for: #{record_name}\"\n      end\n\n      if records.size == 0 && service_name != :puppet\n        # Try the generic :puppet service if no SRV records were found\n        # for the specific service.\n        each_srv_record(domain, :puppet, &block)\n      else\n        each_priority(records) do |recs|\n          while next_rr = recs.delete(find_weighted_server(recs)) # rubocop:disable Lint/AssignmentInCondition\n            Puppet.debug \"Yielding next server of #{next_rr.target}:#{next_rr.port}\"\n            yield next_rr.target.to_s, next_rr.port\n          end\n        end\n      end\n    end\n\n    # Given a list of records of the same priority, chooses a random one\n    # from among them, favoring those with higher weights.\n    # @param [[Resolv::DNS::Resource::IN::SRV]] records a list of records\n    #        of the same priority\n    # @return [Resolv::DNS::Resource::IN:SRV] the chosen record\n    def find_weighted_server(records)\n      return nil if records.nil? || records.empty?\n      return records.first if records.size == 1\n\n      # Calculate the sum of all weights in the list of resource records,\n      # This is used to then select hosts until the weight exceeds what\n      # random number we selected.  For example, if we have weights of 1 8 and 3:\n      #\n      # |-|--------|---|\n      #        ^\n      # We generate a random number 5, and iterate through the records, adding\n      # the current record's weight to the accumulator until the weight of the\n      # current record plus previous records is greater than the random number.\n      total_weight = records.inject(0) { |sum, record|\n        sum + weight(record)\n      }\n      current_weight = 0\n      chosen_weight  = 1 + Kernel.rand(total_weight)\n\n      records.each do |record|\n        current_weight += weight(record)\n        return record if current_weight >= chosen_weight\n      end\n    end\n\n    def weight(record)\n      record.weight == 0 ? 1 : record.weight * 10\n    end\n\n    # Returns TTL for the cached records for this service.\n    # @param [String] service_name the service whose TTL we want\n    # @return [Integer] the TTL for this service, in seconds\n    def ttl(service_name)\n      @record_cache[service_name].ttl\n    end\n\n    # Checks if the cached entry for the given service has expired.\n    # @param [String] service_name the name of the service to check\n    # @return [Boolean] true if the entry has expired, false otherwise.\n    #                  Always returns true if the record had no TTL.\n    def expired?(service_name)\n      entry = @record_cache[service_name]\n      if entry\n        Time.now > (entry.resolution_time + entry.ttl)\n      else\n        true\n      end\n    end\n\n    private\n\n    # Groups the records by their priority and yields the groups\n    # in order of highest to lowest priority (lowest to highest numbers),\n    # one at a time.\n    # { 1 => [records], 2 => [records], etc. }\n    #\n    # @param [[Resolv::DNS::Resource::IN::SRV]] records the list of\n    #        records for a given service\n    # @yields [[Resolv::DNS::Resource::IN::SRV]] a group of records of\n    #         the same priority\n    def each_priority(records)\n      pri_hash = records.each_with_object({}) do |element, groups|\n        groups[element.priority] ||= []\n        groups[element.priority] << element\n      end\n\n      pri_hash.keys.sort.each do |key|\n        yield pri_hash[key]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/errors.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::HTTP\n  # A base class for puppet http errors\n  # @api public\n  class HTTPError < Puppet::Error; end\n\n  # A connection error such as if the server refuses the connection.\n  # @api public\n  class ConnectionError < HTTPError; end\n\n  # A failure to route to the server such as if the `server_list` is exhausted.\n  # @api public\n  class RouteError < HTTPError; end\n\n  # An HTTP protocol error, such as the server's response missing a required header.\n  # @api public\n  class ProtocolError < HTTPError; end\n\n  # An error serializing or deserializing an object via REST.\n  # @api public\n  class SerializationError < HTTPError; end\n\n  # An error due to an unsuccessful HTTP response, such as HTTP 500.\n  # @api public\n  class ResponseError < HTTPError\n    attr_reader :response\n\n    def initialize(response)\n      super(response.reason)\n      @response = response\n    end\n  end\n\n  # An error if asked to follow too many redirects (such as HTTP 301).\n  # @api public\n  class TooManyRedirects < HTTPError\n    def initialize(addr)\n      super(_(\"Too many HTTP redirections for %{addr}\") % { addr: addr })\n    end\n  end\n\n  # An error if asked to retry (such as HTTP 503) too many times.\n  # @api public\n  class TooManyRetryAfters < HTTPError\n    def initialize(addr)\n      super(_(\"Too many HTTP retries for %{addr}\") % { addr: addr })\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/external_client.rb",
    "content": "# frozen_string_literal: true\n\n# Adapts an external http_client_class to the HTTP client API. The former\n# is typically registered by puppetserver and only implements a subset of\n# the Puppet::Network::HTTP::Connection methods. As a result, only the\n# `get` and `post` methods are supported. Calling `delete`, etc will\n# raise a NotImplementedError.\n#\n# @api private\nclass Puppet::HTTP::ExternalClient < Puppet::HTTP::Client\n  # Create an external http client.\n  #\n  # @param [Class] http_client_class The class to create to handle the request\n  def initialize(http_client_class)\n    @http_client_class = http_client_class\n  end\n\n  # (see Puppet::HTTP::Client#get)\n  # @api private\n  def get(url, headers: {}, params: {}, options: {}, &block)\n    url = encode_query(url, params)\n\n    options[:use_ssl] = url.scheme == 'https'\n\n    client = @http_client_class.new(url.host, url.port, options)\n    response = Puppet::HTTP::ResponseNetHTTP.new(url, client.get(url.request_uri, headers, options))\n\n    if block_given?\n      yield response\n    else\n      response\n    end\n  rescue Puppet::HTTP::HTTPError\n    raise\n  rescue => e\n    raise Puppet::HTTP::HTTPError.new(e.message, e)\n  end\n\n  # (see Puppet::HTTP::Client#post)\n  # @api private\n  def post(url, body, headers: {}, params: {}, options: {}, &block)\n    raise ArgumentError, \"'post' requires a string 'body' argument\" unless body.is_a?(String)\n\n    url = encode_query(url, params)\n\n    options[:use_ssl] = url.scheme == 'https'\n\n    client = @http_client_class.new(url.host, url.port, options)\n    response = Puppet::HTTP::ResponseNetHTTP.new(url, client.post(url.request_uri, body, headers, options))\n\n    if block_given?\n      yield response\n    else\n      response\n    end\n  rescue Puppet::HTTP::HTTPError, ArgumentError\n    raise\n  rescue => e\n    raise Puppet::HTTP::HTTPError.new(e.message, e)\n  end\n\n  # (see Puppet::HTTP::Client#close)\n  # @api private\n  def close\n    # This is a noop as puppetserver doesn't provide a way to close its http client.\n  end\n\n  # The following are intentionally not documented\n\n  def create_session\n    raise NotImplementedError\n  end\n\n  def connect(uri, options: {}, &block)\n    raise NotImplementedError\n  end\n\n  def head(url, headers: {}, params: {}, options: {})\n    raise NotImplementedError\n  end\n\n  def put(url, headers: {}, params: {}, options: {})\n    raise NotImplementedError\n  end\n\n  def delete(url, headers: {}, params: {}, options: {})\n    raise NotImplementedError\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/factory.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl/openssl_loader'\nrequire 'net/http'\nrequire_relative '../../puppet/http'\n\n# Factory for `Net::HTTP` objects.\n#\n# Encapsulates the logic for creating a `Net::HTTP` object based on the\n# specified {Site} and puppet settings.\n#\n# @api private\nclass Puppet::HTTP::Factory\n  @@openssl_initialized = false\n\n  KEEP_ALIVE_TIMEOUT = 2**31 - 1\n\n  def initialize\n    # PUP-1411, make sure that openssl is initialized before we try to connect\n    unless @@openssl_initialized\n      OpenSSL::SSL::SSLContext.new\n      @@openssl_initialized = true\n    end\n  end\n\n  def create_connection(site)\n    Puppet.debug(\"Creating new connection for #{site}\")\n\n    http = Puppet::HTTP::Proxy.proxy(URI(site.addr))\n    http.use_ssl = site.use_ssl?\n    if site.use_ssl?\n      http.min_version = OpenSSL::SSL::TLS1_VERSION if http.respond_to?(:min_version)\n      http.ciphers = Puppet[:ciphers]\n    end\n    http.read_timeout = Puppet[:http_read_timeout]\n    http.open_timeout = Puppet[:http_connect_timeout]\n    http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT if http.respond_to?(:keep_alive_timeout=)\n\n    # 0 means make one request and never retry\n    http.max_retries = 0\n\n    if Puppet[:sourceaddress]\n      Puppet.debug(\"Using source IP #{Puppet[:sourceaddress]}\")\n      http.local_host = Puppet[:sourceaddress]\n    end\n\n    if Puppet[:http_debug]\n      http.set_debug_output($stderr)\n    end\n\n    http\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/pool.rb",
    "content": "# frozen_string_literal: true\n\n# A pool for persistent `Net::HTTP` connections. Connections are\n# stored in the pool indexed by their {Site}.\n# Connections are borrowed from the pool, yielded to the caller, and\n# released back into the pool. If a connection is expired, it will be\n# closed either when a connection to that site is requested, or when\n# the pool is closed. The pool can store multiple connections to the\n# same site, and will be reused in MRU order.\n#\n# @api private\nclass Puppet::HTTP::Pool\n  attr_reader :factory, :keepalive_timeout\n\n  def initialize(keepalive_timeout)\n    @pool = {}\n    @factory = Puppet::HTTP::Factory.new\n    @keepalive_timeout = keepalive_timeout\n  end\n\n  def with_connection(site, verifier, &block)\n    reuse = true\n\n    http = borrow(site, verifier)\n    begin\n      if http.use_ssl? && http.verify_mode != OpenSSL::SSL::VERIFY_PEER\n        reuse = false\n      end\n\n      yield http\n    rescue => detail\n      reuse = false\n      raise detail\n    ensure\n      if reuse && http.started?\n        release(site, verifier, http)\n      else\n        close_connection(site, http)\n      end\n    end\n  end\n\n  def close\n    @pool.each_pair do |site, entries|\n      entries.each do |entry|\n        close_connection(site, entry.connection)\n      end\n    end\n    @pool.clear\n  end\n\n  # @api private\n  def pool\n    @pool\n  end\n\n  # Start a persistent connection\n  #\n  # @api private\n  def start(site, verifier, http)\n    Puppet.debug(\"Starting connection for #{site}\")\n    if site.use_ssl?\n      verifier.setup_connection(http)\n      begin\n        http.start\n        print_ssl_info(http) if Puppet::Util::Log.sendlevel?(:debug)\n      rescue OpenSSL::SSL::SSLError => error\n        verifier.handle_connection_error(http, error)\n      end\n    else\n      http.start\n    end\n  end\n\n  # Safely close a persistent connection.\n  # Don't try to close a connection that's already closed.\n  #\n  # @api private\n  def close_connection(site, http)\n    return false unless http.started?\n\n    Puppet.debug(\"Closing connection for #{site}\")\n    http.finish\n    true\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Failed to close connection for %{site}: %{detail}\") % { site: site, detail: detail })\n    nil\n  end\n\n  # Borrow and take ownership of a persistent connection. If a new\n  # connection is created, it will be started prior to being returned.\n  #\n  # @api private\n  def borrow(site, verifier)\n    @pool[site] = active_entries(site)\n    index = @pool[site].index do |entry|\n      (verifier.nil? && entry.verifier.nil?) ||\n        (!verifier.nil? && verifier.reusable?(entry.verifier))\n    end\n    entry = index ? @pool[site].delete_at(index) : nil\n    if entry\n      @pool.delete(site) if @pool[site].empty?\n\n      Puppet.debug(\"Using cached connection for #{site}\")\n      entry.connection\n    else\n      http = @factory.create_connection(site)\n\n      start(site, verifier, http)\n      setsockopts(http.instance_variable_get(:@socket))\n      http\n    end\n  end\n\n  # Set useful socket option(s) which lack from default settings in Net:HTTP\n  #\n  # @api private\n  def setsockopts(netio)\n    return unless netio\n\n    socket = netio.io\n    socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)\n  end\n\n  # Release a connection back into the pool.\n  #\n  # @api private\n  def release(site, verifier, http)\n    expiration = Time.now + @keepalive_timeout\n    entry = Puppet::HTTP::PoolEntry.new(http, verifier, expiration)\n    Puppet.debug(\"Caching connection for #{site}\")\n\n    entries = @pool[site]\n    if entries\n      entries.unshift(entry)\n    else\n      @pool[site] = [entry]\n    end\n  end\n\n  # Returns an Array of entries whose connections are not expired.\n  #\n  # @api private\n  def active_entries(site)\n    now = Time.now\n\n    entries = @pool[site] || []\n    entries.select do |entry|\n      if entry.expired?(now)\n        close_connection(site, entry.connection)\n        false\n      else\n        true\n      end\n    end\n  end\n\n  private\n\n  def print_ssl_info(http)\n    buffered_io = http.instance_variable_get(:@socket)\n    return unless buffered_io\n\n    socket = buffered_io.io\n    return unless socket\n\n    cipher = if Puppet::Util::Platform.jruby?\n               socket.cipher\n             else\n               socket.cipher.first\n             end\n    Puppet.debug(\"Using #{socket.ssl_version} with cipher #{cipher}\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/pool_entry.rb",
    "content": "# frozen_string_literal: true\n\n# An entry in the peristent HTTP pool that references the connection and\n# an expiration time for the connection.\n#\n# @api private\nclass Puppet::HTTP::PoolEntry\n  attr_reader :connection, :verifier\n\n  def initialize(connection, verifier, expiration_time)\n    @connection = connection\n    @verifier = verifier\n    @expiration_time = expiration_time\n  end\n\n  def expired?(now)\n    @expiration_time <= now\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/proxy.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'uri'\nrequire_relative '../../puppet/ssl/openssl_loader'\n\nmodule Puppet::HTTP::Proxy\n  def self.proxy(uri)\n    if http_proxy_host && !no_proxy?(uri)\n      Net::HTTP.new(uri.host, uri.port, http_proxy_host, http_proxy_port, http_proxy_user, http_proxy_password)\n    else\n      http = Net::HTTP.new(uri.host, uri.port, nil, nil, nil, nil)\n      # Net::HTTP defaults the proxy port even though we said not to\n      # use one. Set it to nil so caller is not surprised\n      http.proxy_port = nil\n      http\n    end\n  end\n\n  def self.http_proxy_env\n    # Returns a URI object if proxy is set, or nil\n    proxy_env = ENV.fetch(\"http_proxy\", nil) || ENV.fetch(\"HTTP_PROXY\", nil)\n    begin\n      return URI.parse(proxy_env) if proxy_env\n    rescue URI::InvalidURIError\n      return nil\n    end\n    nil\n  end\n\n  # The documentation around the format of the no_proxy variable seems\n  # inconsistent.  Some suggests the use of the * as a way of matching any\n  # hosts under a domain, e.g.:\n  #   *.example.com\n  # Other documentation suggests that just a leading '.' indicates a domain\n  # level exclusion, e.g.:\n  #   .example.com\n  # We'll accommodate both here.\n  def self.no_proxy?(dest)\n    no_proxy = self.no_proxy\n    unless no_proxy\n      return false\n    end\n\n    unless dest.is_a? URI\n      begin\n        dest = URI.parse(dest)\n      rescue URI::InvalidURIError\n        return false\n      end\n    end\n\n    no_proxy.split(/\\s*,\\s*/).each do |d|\n      host, port = d.split(':')\n      host = Regexp.escape(host).gsub('\\*', '.*')\n\n      # If this no_proxy entry specifies a port, we want to match it against\n      # the destination port.  Otherwise just match hosts.\n      if port\n        no_proxy_regex  = /#{host}:#{port}$/\n        dest_string     = \"#{dest.host}:#{dest.port}\"\n      else\n        no_proxy_regex  = /#{host}$/\n        dest_string     = dest.host.to_s\n      end\n\n      if no_proxy_regex.match(dest_string)\n        return true\n      end\n    end\n\n    false\n  end\n\n  def self.http_proxy_host\n    env = http_proxy_env\n\n    if env and env.host\n      return env.host\n    end\n\n    if Puppet.settings[:http_proxy_host] == 'none'\n      return nil\n    end\n\n    Puppet.settings[:http_proxy_host]\n  end\n\n  def self.http_proxy_port\n    env = http_proxy_env\n\n    if env and env.port\n      return env.port\n    end\n\n    Puppet.settings[:http_proxy_port]\n  end\n\n  def self.http_proxy_user\n    env = http_proxy_env\n\n    if env and env.user\n      return env.user\n    end\n\n    if Puppet.settings[:http_proxy_user] == 'none'\n      return nil\n    end\n\n    Puppet.settings[:http_proxy_user]\n  end\n\n  def self.http_proxy_password\n    env = http_proxy_env\n\n    if env and env.password\n      return env.password\n    end\n\n    if Puppet.settings[:http_proxy_user] == 'none' or Puppet.settings[:http_proxy_password] == 'none'\n      return nil\n    end\n\n    Puppet.settings[:http_proxy_password]\n  end\n\n  def self.no_proxy\n    no_proxy_env = ENV.fetch(\"no_proxy\", nil) || ENV.fetch(\"NO_PROXY\", nil)\n\n    if no_proxy_env\n      return no_proxy_env\n    end\n\n    if Puppet.settings[:no_proxy] == 'none'\n      return nil\n    end\n\n    Puppet.settings[:no_proxy]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/redirector.rb",
    "content": "# frozen_string_literal: true\n\n# Handle HTTP redirects\n#\n# @api private\nclass Puppet::HTTP::Redirector\n  # Create a new redirect handler\n  #\n  # @param [Integer] redirect_limit maximum number of redirects allowed\n  #\n  # @api private\n  def initialize(redirect_limit)\n    @redirect_limit = redirect_limit\n  end\n\n  # Determine of the HTTP response code indicates a redirect\n  #\n  # @param [Net::HTTP] request request that received the response\n  # @param [Puppet::HTTP::Response] response\n  #\n  # @return [Boolean] true if the response code is 301, 302, or 307.\n  #\n  # @api private\n  def redirect?(request, response)\n    # Net::HTTPRedirection is not used because historically puppet\n    # has only handled these, and we're not a browser\n    case response.code\n    when 301, 302, 307\n      true\n    else\n      false\n    end\n  end\n\n  # Implement the HTTP request redirection\n  #\n  # @param [Net::HTTP] request request that has been redirected\n  # @param [Puppet::HTTP::Response] response\n  # @param [Integer] redirects the current number of redirects\n  #\n  # @return [Net::HTTP] A new request based on the original request, but with\n  #   the redirected location\n  #\n  # @api private\n  def redirect_to(request, response, redirects)\n    raise Puppet::HTTP::TooManyRedirects, request.uri if redirects >= @redirect_limit\n\n    location = parse_location(response)\n    url = request.uri.merge(location)\n\n    new_request = request.class.new(url)\n    new_request.body = request.body\n    request.each do |header, value|\n      unless Puppet[:location_trusted]\n        # skip adding potentially sensitive header to other hosts\n        next if header.casecmp('Authorization').zero? && request.uri.host.casecmp(location.host) != 0\n        next if header.casecmp('Cookie').zero? && request.uri.host.casecmp(location.host) != 0\n      end\n      # Allow Net::HTTP to set its own Accept-Encoding header to avoid errors with HTTP compression.\n      # See https://github.com/puppetlabs/puppet/issues/9143\n      next if header.casecmp('Accept-Encoding').zero?\n\n      new_request[header] = value\n    end\n\n    # mimic private Net::HTTP#addr_port\n    new_request['Host'] = if (location.scheme == 'https' && location.port == 443) ||\n                             (location.scheme == 'http' && location.port == 80)\n                            location.host\n                          else\n                            \"#{location.host}:#{location.port}\"\n                          end\n\n    new_request\n  end\n\n  private\n\n  def parse_location(response)\n    location = response['location']\n    raise Puppet::HTTP::ProtocolError, _(\"Location response header is missing\") unless location\n\n    URI.parse(location)\n  rescue URI::InvalidURIError => e\n    raise Puppet::HTTP::ProtocolError.new(_(\"Location URI is invalid: %{detail}\") % { detail: e.message }, e)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/resolver/server_list.rb",
    "content": "# frozen_string_literal: true\n\n# Use the server_list setting to resolve a service. This resolver is only used\n# if server_list is set either on the command line or in the configuration file.\n#\n# @api public\nclass Puppet::HTTP::Resolver::ServerList < Puppet::HTTP::Resolver\n  # Create a server list resolver.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Array<String>] server_list_setting array of servers set via the\n  #   configuration or the command line\n  # @param [Integer] default_port if a port is not set for a server in\n  #   server_list, use this port\n  # @param [Array<Symbol>] services array of services that server_list can be\n  #   used to resolve. If a service is not included in this array, this resolver\n  #   will return nil.\n  #\n  def initialize(client, server_list_setting:, default_port:, services:)\n    @client = client\n    @server_list_setting = server_list_setting\n    @default_port = default_port\n    @services = services\n  end\n\n  # Walk the server_list to find a server and port that will connect successfully.\n  #\n  # @param [Puppet::HTTP::Session] session\n  # @param [Symbol] name the name of the service being resolved\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  # @param [Proc] canceled_handler optional callback allowing a resolver\n  #   to cancel resolution.\n  #\n  # @return [nil] return nil if the service to be resolved does not support\n  #   server_list\n  # @return [Puppet::HTTP::Service] a validated service to use for future HTTP\n  #   requests\n  #\n  # @raise [Puppet::Error] raise if none of the servers defined in server_list\n  #   are available\n  #\n  # @api public\n  def resolve(session, name, ssl_context: nil, canceled_handler: nil)\n    # If we're configured to use an explicit service host, e.g. report_server\n    # then don't use server_list to resolve the `:report` service.\n    return nil unless @services.include?(name)\n\n    # If we resolved the URL already, use its host & port for the service\n    if @resolved_url\n      return Puppet::HTTP::Service.create_service(@client, session, name, @resolved_url.host, @resolved_url.port)\n    end\n\n    # Return the first simple service status endpoint we can connect to\n    @server_list_setting.value.each_with_index do |server, index|\n      host = server[0]\n      port = server[1] || @default_port\n\n      service = Puppet::HTTP::Service.create_service(@client, session, :puppetserver, host, port)\n      begin\n        service.get_simple_status(ssl_context: ssl_context)\n        @resolved_url = service.url\n        return Puppet::HTTP::Service.create_service(@client, session, name, @resolved_url.host, @resolved_url.port)\n      rescue Puppet::HTTP::ResponseError => detail\n        if index < @server_list_setting.value.length - 1\n          Puppet.warning(_(\"Puppet server %{host}:%{port} is unavailable: %{code} %{reason}\") %\n                              { host: service.url.host, port: service.url.port, code: detail.response.code, reason: detail.response.reason } +\n                              ' ' + _(\"Trying with next server from server_list.\"))\n        else\n          Puppet.log_exception(detail, _(\"Puppet server %{host}:%{port} is unavailable: %{code} %{reason}\") %\n                               { host: service.url.host, port: service.url.port, code: detail.response.code, reason: detail.response.reason })\n        end\n      rescue Puppet::HTTP::HTTPError => detail\n        if index < @server_list_setting.value.length - 1\n          Puppet.warning(_(\"Unable to connect to server from server_list setting: %{detail}\") % { detail: detail } +\n                             ' ' + _(\"Trying with next server from server_list.\"))\n        else\n          Puppet.log_exception(detail, _(\"Unable to connect to server from server_list setting: %{detail}\") % { detail: detail })\n        end\n      end\n    end\n\n    # don't fallback to other resolvers\n    canceled_handler.call(true) if canceled_handler\n\n    # not found\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/resolver/settings.rb",
    "content": "# frozen_string_literal: true\n\n# Resolve a service using settings. This is the default resolver if none of the\n# other resolvers find a functional connection.\n#\n# @api public\nclass Puppet::HTTP::Resolver::Settings < Puppet::HTTP::Resolver\n  # Resolve a service using the default server and port settings for this service.\n  #\n  # @param [Puppet::HTTP::Session] session\n  # @param [Symbol] name the name of the service to be resolved\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  # @param [Proc] canceled_handler optional callback allowing a resolver\n  #   to cancel resolution.\n  #\n  # @return [Puppet::HTTP::Service] if the service successfully connects,\n  #   return it. Otherwise, return nil.\n  #\n  # @api public\n  def resolve(session, name, ssl_context: nil, canceled_handler: nil)\n    service = Puppet::HTTP::Service.create_service(@client, session, name)\n    check_connection?(session, service, ssl_context: ssl_context) ? service : nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/resolver/srv.rb",
    "content": "# frozen_string_literal: true\n\n# Resolve a service using DNS SRV records.\n#\n# @api public\nclass Puppet::HTTP::Resolver::SRV < Puppet::HTTP::Resolver\n  # Create an DNS SRV resolver.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [String] domain srv domain\n  # @param [Resolv::DNS] dns\n  #\n  def initialize(client, domain:, dns: Resolv::DNS.new)\n    @client = client\n    @srv_domain = domain\n    @delegate = Puppet::HTTP::DNS.new(dns)\n  end\n\n  # Walk the available srv records and return the first that successfully connects\n  #\n  # @param [Puppet::HTTP::Session] session\n  # @param [Symbol] name the service being resolved\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  # @param [Proc] canceled_handler optional callback allowing a resolver\n  #   to cancel resolution.\n  #\n  # @return [Puppet::HTTP::Service] if an available service is found, return\n  #   it. Return nil otherwise.\n  #\n  # @api public\n  def resolve(session, name, ssl_context: nil, canceled_handler: nil)\n    # Here we pass our HTTP service name as the DNS SRV service name\n    # This is fine for :ca, but note that :puppet and :file are handled\n    # specially in `each_srv_record`.\n    @delegate.each_srv_record(@srv_domain, name) do |server, port|\n      service = Puppet::HTTP::Service.create_service(@client, session, name, server, port)\n      return service if check_connection?(session, service, ssl_context: ssl_context)\n    end\n\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/resolver.rb",
    "content": "# frozen_string_literal: true\n\n# Resolver base class. Each resolver represents a different strategy for\n# resolving a service name into a list of candidate servers and ports.\n#\n# @abstract Subclass and override {#resolve} to create a new resolver.\n# @api public\nclass Puppet::HTTP::Resolver\n  #\n  # Create a new resolver.\n  #\n  # @param [Puppet::HTTP::Client] client\n  def initialize(client)\n    @client = client\n  end\n\n  # Return a working server/port for the resolver. This is the base\n  # implementation and is meant to be a placeholder.\n  #\n  # @param [Puppet::HTTP::Session] session\n  # @param [Symbol] name the service to resolve\n  # @param [Puppet::SSL::SSLContext] ssl_context (nil) optional ssl context to\n  #   use when creating a connection\n  # @param [Proc] canceled_handler (nil) optional callback allowing a resolver\n  #   to cancel resolution.\n  #\n  # @raise [NotImplementedError] this base class is not implemented\n  #\n  # @api public\n  def resolve(session, name, ssl_context: nil, canceled_handler: nil)\n    raise NotImplementedError\n  end\n\n  # Check a given connection to establish if it can be relied on for future use.\n  #\n  # @param [Puppet::HTTP::Session] session\n  # @param [Puppet::HTTP::Service] service\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  #\n  # @return [Boolean] Returns true if a connection is successful, false otherwise\n  #\n  # @api public\n  def check_connection?(session, service, ssl_context: nil)\n    service.connect(ssl_context: ssl_context)\n    true\n  rescue Puppet::HTTP::ConnectionError => e\n    Puppet.log_exception(e, \"Connection to #{service.url} failed, trying next route: #{e.message}\")\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/response.rb",
    "content": "# frozen_string_literal: true\n\n# Represents the response returned from the server from an HTTP request.\n#\n# @api abstract\n# @api public\nclass Puppet::HTTP::Response\n  # @return [URI] the response url\n  attr_reader :url\n\n  # Create a response associated with the URL.\n  #\n  # @param [URI] url\n  # @param [Integer] HTTP status\n  # @param [String] HTTP reason\n  def initialize(url, code, reason)\n    @url = url\n    @code = code\n    @reason = reason\n  end\n\n  # Return the response code.\n  #\n  # @return [Integer] Response code for the request\n  #\n  # @api public\n  def code\n    @code\n  end\n\n  # Return the response message.\n  #\n  # @return [String] Response message for the request\n  #\n  # @api public\n  def reason\n    @reason\n  end\n\n  # Returns the entire response body. Can be used instead of\n  #   `Puppet::HTTP::Response.read_body`, but both methods cannot be used for the\n  #   same response.\n  #\n  # @return [String] Response body for the request\n  #\n  # @api public\n  def body\n    raise NotImplementedError\n  end\n\n  # Streams the response body to the caller in chunks. Can be used instead of\n  #   `Puppet::HTTP::Response.body`, but both methods cannot be used for the same\n  #   response.\n  #\n  # @yield [String] Streams the response body in chunks\n  #\n  # @raise [ArgumentError] raise if a block is not given\n  #\n  # @api public\n  def read_body(&block)\n    raise NotImplementedError\n  end\n\n  # Check if the request received a response of success (HTTP 2xx).\n  #\n  # @return [Boolean] Returns true if the response indicates success\n  #\n  # @api public\n  def success?\n    200 <= @code && @code < 300\n  end\n\n  # Get a header case-insensitively.\n  #\n  # @param [String] name The header name\n  # @return [String] The header value\n  #\n  # @api public\n  def [](name)\n    raise NotImplementedError\n  end\n\n  # Yield each header name and value. Returns an enumerator if no block is given.\n  #\n  # @yieldparam [String] header name\n  # @yieldparam [String] header value\n  #\n  # @api public\n  def each_header(&block)\n    raise NotImplementedError\n  end\n\n  # Ensure the response body is fully read so that the server is not blocked\n  # waiting for us to read data from the socket. Also if the caller streamed\n  # the response, but didn't read the data, we need a way to drain the socket\n  # before adding the connection back to the connection pool, otherwise the\n  # unread response data would \"leak\" into the next HTTP request/response.\n  #\n  # @api public\n  def drain\n    body\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/response_converter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::HTTP::ResponseConverter\n  module_function\n\n  # Borrowed from puppetserver, see https://github.com/puppetlabs/puppetserver/commit/a1ebeaaa5af590003ccd23c89f808ba4f0c89609\n  def to_ruby_response(response)\n    str_code = response.code.to_s\n\n    # Copied from Net::HTTPResponse because it is private there.\n    clazz = Net::HTTPResponse::CODE_TO_OBJ[str_code] or\n      Net::HTTPResponse::CODE_CLASS_TO_OBJ[str_code[0, 1]] or\n      Net::HTTPUnknownResponse\n    result = clazz.new(nil, str_code, nil)\n    result.body = response.body\n    # This is nasty, nasty.  But apparently there is no way to create\n    # an instance of Net::HttpResponse from outside of the library and have\n    # the body be readable, unless you do stupid things like this.\n    result.instance_variable_set(:@read, true)\n    response.each_header do |k, v|\n      result[k] = v\n    end\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/response_net_http.rb",
    "content": "# frozen_string_literal: true\n\n# Adapts Net::HTTPResponse to Puppet::HTTP::Response\n#\n# @api public\nclass Puppet::HTTP::ResponseNetHTTP < Puppet::HTTP::Response\n  # Create a response associated with the URL.\n  #\n  # @param [URI] url\n  # @param [Net::HTTPResponse] nethttp The response\n  def initialize(url, nethttp)\n    super(url, nethttp.code.to_i, nethttp.message)\n\n    @nethttp = nethttp\n  end\n\n  # (see Puppet::HTTP::Response#body)\n  def body\n    @nethttp.body\n  end\n\n  # (see Puppet::HTTP::Response#read_body)\n  def read_body(&block)\n    raise ArgumentError, \"A block is required\" unless block_given?\n\n    @nethttp.read_body(&block)\n  end\n\n  # (see Puppet::HTTP::Response#success?)\n  def success?\n    @nethttp.is_a?(Net::HTTPSuccess)\n  end\n\n  # (see Puppet::HTTP::Response#[])\n  def [](name)\n    @nethttp[name]\n  end\n\n  # (see Puppet::HTTP::Response#each_header)\n  def each_header(&block)\n    @nethttp.each_header(&block)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/retry_after_handler.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'date'\nrequire 'time'\n\n# Parse information relating to responses containing a Retry-After headers\n#\n# @api private\nclass Puppet::HTTP::RetryAfterHandler\n  # Create a handler to allow the system to sleep between HTTP requests\n  #\n  # @param [Integer] retry_limit number of retries allowed\n  # @param [Integer] max_sleep maximum sleep time allowed\n  def initialize(retry_limit, max_sleep)\n    @retry_limit = retry_limit\n    @max_sleep = max_sleep\n  end\n\n  # Does the response from the server tell us to wait until we attempt the next\n  # retry?\n  #\n  # @param [Net::HTTP] request\n  # @param [Puppet::HTTP::Response] response\n  #\n  # @return [Boolean] Return true if the response code is 429 or 503, return\n  #   false otherwise\n  #\n  # @api private\n  def retry_after?(request, response)\n    case response.code\n    when 429, 503\n      true\n    else\n      false\n    end\n  end\n\n  # The amount of time to wait before attempting a retry\n  #\n  # @param [Net::HTTP] request\n  # @param [Puppet::HTTP::Response] response\n  # @param [Integer] retries number of retries attempted so far\n  #\n  # @return [Integer] the amount of time to wait\n  #\n  # @raise [Puppet::HTTP::TooManyRetryAfters] raise if we have hit our retry\n  #   limit\n  #\n  # @api private\n  def retry_after_interval(request, response, retries)\n    raise Puppet::HTTP::TooManyRetryAfters, request.uri if retries >= @retry_limit\n\n    retry_after = response['Retry-After']\n    return nil unless retry_after\n\n    seconds = parse_retry_after(retry_after)\n\n    # if retry-after is far in the future, we could end up sleeping repeatedly\n    # for 30 minutes, effectively waiting indefinitely, seems like we should wait\n    # in total for 30 minutes, in which case this upper limit needs to be enforced\n    # by the client.\n    [seconds, @max_sleep].min\n  end\n\n  private\n\n  def parse_retry_after(retry_after)\n    Integer(retry_after)\n  rescue TypeError, ArgumentError\n    begin\n      tm = DateTime.rfc2822(retry_after)\n      seconds = (tm.to_time - DateTime.now.to_time).to_i\n      [seconds, 0].max\n    rescue ArgumentError\n      raise Puppet::HTTP::ProtocolError, _(\"Failed to parse Retry-After header '%{retry_after}' as an integer or RFC 2822 date\") % { retry_after: retry_after }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/service/ca.rb",
    "content": "# frozen_string_literal: true\n\n# The CA service is used to handle certificate related REST requests.\n#\n# @api public\nclass Puppet::HTTP::Service::Ca < Puppet::HTTP::Service\n  # @return [Hash] default headers for the ca service\n  HEADERS = { 'Accept' => 'text/plain' }.freeze\n\n  # @return [String] default API for the ca service\n  API = '/puppet-ca/v1'\n\n  # Use `Puppet::HTTP::Session.route_to(:ca)` to create or get an instance of this class.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Puppet::HTTP::Session] session\n  # @param [String] server (`Puppet[:ca_server]`) If an explicit server is given,\n  #   create a service using that server. If server is nil, the default value\n  #   is used to create the service.\n  # @param [Integer] port (`Puppet[:ca_port]`) If an explicit port is given, create\n  #   a service using that port. If port is nil, the default value is used to\n  #   create the service.\n  #\n  def initialize(client, session, server, port)\n    url = build_url(API, server || Puppet[:ca_server], port || Puppet[:ca_port])\n    super(client, session, url)\n  end\n\n  # Submit a GET request to retrieve the named certificate from the server.\n  #\n  # @param [String] name name of the certificate to request\n  # @param [Time] if_modified_since If not nil, only download the cert if it has\n  #   been modified since the specified time.\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  #\n  # @return [Array<Puppet::HTTP::Response, String>] An array containing the\n  #   request response and the stringified body of the request response\n  #\n  # @api public\n  def get_certificate(name, if_modified_since: nil, ssl_context: nil)\n    headers = add_puppet_headers(HEADERS)\n    headers['If-Modified-Since'] = if_modified_since.httpdate if if_modified_since\n\n    response = @client.get(\n      with_base_url(\"/certificate/#{name}\"),\n      headers: headers,\n      options: { ssl_context: ssl_context }\n    )\n\n    process_response(response)\n\n    [response, response.body.to_s]\n  end\n\n  # Submit a GET request to retrieve the certificate revocation list from the\n  #   server.\n  #\n  # @param [Time] if_modified_since If not nil, only download the CRL if it has\n  #   been modified since the specified time.\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  #\n  # @return [Array<Puppet::HTTP::Response, String>] An array containing the\n  #   request response and the stringified body of the request response\n  #\n  # @api public\n  def get_certificate_revocation_list(if_modified_since: nil, ssl_context: nil)\n    headers = add_puppet_headers(HEADERS)\n    headers['If-Modified-Since'] = if_modified_since.httpdate if if_modified_since\n\n    response = @client.get(\n      with_base_url(\"/certificate_revocation_list/ca\"),\n      headers: headers,\n      options: { ssl_context: ssl_context }\n    )\n\n    process_response(response)\n\n    [response, response.body.to_s]\n  end\n\n  # Submit a PUT request to send a certificate request to the server.\n  #\n  # @param [String] name The name of the certificate request being sent\n  # @param [OpenSSL::X509::Request] csr Certificate request to send to the\n  #   server\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  #\n  # @return [Puppet::HTTP::Response] The request response\n  #\n  # @api public\n  def put_certificate_request(name, csr, ssl_context: nil)\n    headers = add_puppet_headers(HEADERS)\n    headers['Content-Type'] = 'text/plain'\n\n    response = @client.put(\n      with_base_url(\"/certificate_request/#{name}\"),\n      csr.to_pem,\n      headers: headers,\n      options: {\n        ssl_context: ssl_context\n      }\n    )\n\n    process_response(response)\n\n    response\n  end\n\n  # Submit a POST request to send a certificate renewal request to the server\n  #\n  # @param [Puppet::SSL::SSLContext] ssl_context\n  #\n  # @return [Array<Puppet::HTTP::Response, String>] The request response\n  #\n  # @api public\n  def post_certificate_renewal(ssl_context)\n    headers = add_puppet_headers(HEADERS)\n    headers['Content-Type'] = 'text/plain'\n\n    response = @client.post(\n      with_base_url('/certificate_renewal'),\n      '', # Puppet::HTTP::Client.post requires a body, the API endpoint does not\n      headers: headers,\n      options: { ssl_context: ssl_context }\n    )\n\n    raise ArgumentError, _('SSL context must contain a client certificate.') unless ssl_context.client_cert\n\n    process_response(response)\n\n    [response, response.body.to_s]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/service/compiler.rb",
    "content": "# frozen_string_literal: true\n\n# The Compiler service is used to submit and retrieve data from the\n# puppetserver.\n#\n# @api public\nclass Puppet::HTTP::Service::Compiler < Puppet::HTTP::Service\n  # @return [String] Default API for the Compiler service\n  API = '/puppet/v3'\n\n  # Use `Puppet::HTTP::Session.route_to(:puppet)` to create or get an instance of this class.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Puppet::HTTP::Session] session\n  # @param [String] server (`Puppet[:server]`) If an explicit server is given,\n  #   create a service using that server. If server is nil, the default value\n  #   is used to create the service.\n  # @param [Integer] port (`Puppet[:masterport]`) If an explicit port is given, create\n  #   a service using that port. If port is nil, the default value is used to\n  #   create the service.\n  #\n  def initialize(client, session, server, port)\n    url = build_url(API, server || Puppet[:server], port || Puppet[:serverport])\n    super(client, session, url)\n  end\n\n  # Submit a GET request to retrieve a node from the server.\n  #\n  # @param [String] name The name of the node being requested\n  # @param [String] environment The name of the environment we are operating in\n  # @param [String] configured_environment Optional, the name of the configured\n  #   environment. If unset, `environment` is used.\n  # @param [String] transaction_uuid An agent generated transaction uuid, used\n  #   for connecting catalogs and reports.\n  #\n  # @return [Array<Puppet::HTTP::Response, Puppet::Node>] An array containing\n  #   the request response and the deserialized requested node\n  #\n  # @api public\n  def get_node(name, environment:, configured_environment: nil, transaction_uuid: nil)\n    headers = add_puppet_headers('Accept' => get_mime_types(Puppet::Node).join(', '))\n\n    response = @client.get(\n      with_base_url(\"/node/#{name}\"),\n      headers: headers,\n      params: {\n        environment: environment,\n        configured_environment: configured_environment || environment,\n        transaction_uuid: transaction_uuid,\n      }\n    )\n\n    process_response(response)\n\n    [response, deserialize(response, Puppet::Node)]\n  end\n\n  # Submit a POST request to submit a catalog to the server.\n  #\n  # @param [String] name The name of the catalog to be submitted\n  # @param [Puppet::Node::Facts] facts Facts for this catalog\n  # @param [String] environment The name of the environment we are operating in\n  # @param [String] configured_environment Optional, the name of the configured\n  #   environment. If unset, `environment` is used.\n  # @param [Boolean] check_environment If true, request that the server check if\n  #   our `environment` matches the server-specified environment. If they do not\n  #   match, then the server may return an empty catalog in the server-specified\n  #   environment.\n  # @param [String] transaction_uuid An agent generated transaction uuid, used\n  #   for connecting catalogs and reports.\n  # @param [String] job_uuid A unique job identifier defined when the orchestrator\n  #   starts a puppet run via pxp-agent. This is used to correlate catalogs and\n  #   reports with the orchestrator job.\n  # @param [Boolean] static_catalog Indicates if the file metadata(s) are inlined\n  #   in the catalog. This informs the agent if it needs to make a second request\n  #   to retrieve metadata in addition to the initial catalog request.\n  # @param [Array<String>] checksum_type An array of accepted checksum types.\n  #\n  # @return [Array<Puppet::HTTP::Response, Puppet::Resource::Catalog>] An array\n  #   containing the request response and the deserialized catalog returned by\n  #   the server\n  #\n  # @api public\n  def post_catalog(name, facts:, environment:, configured_environment: nil, check_environment: false, transaction_uuid: nil, job_uuid: nil, static_catalog: true, checksum_type: Puppet[:supported_checksum_types])\n    if Puppet[:preferred_serialization_format] == \"pson\"\n      formatter = Puppet::Network::FormatHandler.format_for(:pson)\n      # must use 'pson' instead of 'text/pson'\n      facts_format = 'pson'\n    else\n      formatter = Puppet::Network::FormatHandler.format_for(:json)\n      facts_format = formatter.mime\n    end\n\n    facts_as_string = serialize(formatter, facts)\n\n    # query parameters are sent in the POST request body\n    body = {\n      facts_format: facts_format,\n      facts: Puppet::Util.uri_query_encode(facts_as_string),\n      environment: environment,\n      configured_environment: configured_environment || environment,\n      check_environment: !!check_environment,\n      transaction_uuid: transaction_uuid,\n      job_uuid: job_uuid,\n      static_catalog: static_catalog,\n      checksum_type: checksum_type.join('.')\n    }.map do |key, value|\n      \"#{key}=#{Puppet::Util.uri_query_encode(value.to_s)}\"\n    end.join(\"&\")\n\n    headers = add_puppet_headers(\n      'Accept' => get_mime_types(Puppet::Resource::Catalog).join(', '),\n      'Content-Type' => 'application/x-www-form-urlencoded'\n    )\n\n    response = @client.post(\n      with_base_url(\"/catalog/#{name}\"),\n      body,\n      headers: headers,\n      # for legacy reasons we always send environment as a query parameter too\n      params: { environment: environment }\n    )\n\n    if (compiler = response['X-Puppet-Compiler-Name'])\n      Puppet.notice(\"Catalog compiled by #{compiler}\")\n    end\n\n    process_response(response)\n\n    [response, deserialize(response, Puppet::Resource::Catalog)]\n  end\n\n  #\n  # @api private\n  #\n  # Submit a POST request to request a catalog to the server using v4 endpoint\n  #\n  # @param [String] certname The name of the node for which to compile the catalog.\n  # @param [Hash] persistent A hash containing two required keys, facts and catalog,\n  #   which when set to true will cause the facts and reports to be stored in\n  #   PuppetDB, or discarded if set to false.\n  # @param [String] environment The name of the environment for which to compile the catalog.\n  # @param [Hash] facts A hash with a required values key, containing a hash of all the\n  #    facts for the node. If not provided, Puppet will attempt to fetch facts for the node\n  #    from PuppetDB.\n  # @param [Hash] trusted_facts A hash with a required values key containing a hash of\n  #    the trusted facts for a node\n  # @param [String] transaction_uuid The id for tracking the catalog compilation and\n  #    report submission.\n  # @param [String] job_id The id of the orchestrator job that triggered this run.\n  # @param [Hash] options A hash of options beyond direct input to catalogs. Options:\n  #    - prefer_requested_environment Whether to always override a node's classified\n  #      environment with the one supplied in the request. If this is true and no environment\n  #      is supplied, fall back to the classified environment, or finally, 'production'.\n  #    - capture_logs Whether to return the errors and warnings that occurred during\n  #      compilation alongside the catalog in the response body.\n  #    - log_level The logging level to use during the compile when capture_logs is true.\n  #      Options are 'err', 'warning', 'info', and 'debug'.\n  #\n  # @return [Array<Puppet::HTTP::Response, Puppet::Resource::Catalog, Array<String>>] An array\n  #   containing the request response, the deserialized catalog returned by\n  #   the server and array containing logs (log array will be empty if capture_logs is false)\n  #\n  def post_catalog4(certname, persistence:, environment:, facts: nil, trusted_facts: nil, transaction_uuid: nil, job_id: nil, options: nil)\n    unless persistence.is_a?(Hash) && (missing = [:facts, :catalog] - persistence.keys.map(&:to_sym)).empty?\n      raise ArgumentError, \"The 'persistence' hash is missing the keys: #{missing.join(', ')}\"\n    end\n    raise ArgumentError, \"Facts must be a Hash not a #{facts.class}\" unless facts.nil? || facts.is_a?(Hash)\n\n    body = {\n      certname: certname,\n      persistence: persistence,\n      environment: environment,\n      transaction_uuid: transaction_uuid,\n      job_id: job_id,\n      options: options\n    }\n    body[:facts] = { values: facts } unless facts.nil?\n    body[:trusted_facts] = { values: trusted_facts } unless trusted_facts.nil?\n    headers = add_puppet_headers(\n      'Accept' => get_mime_types(Puppet::Resource::Catalog).join(', '),\n      'Content-Type' => 'application/json'\n    )\n\n    url = URI::HTTPS.build(host: @url.host, port: @url.port, path: Puppet::Util.uri_encode(\"/puppet/v4/catalog\"))\n    response = @client.post(\n      url,\n      body.to_json,\n      headers: headers\n    )\n    process_response(response)\n    begin\n      response_body = JSON.parse(response.body)\n      catalog = Puppet::Resource::Catalog.from_data_hash(response_body['catalog'])\n    rescue => err\n      raise Puppet::HTTP::SerializationError.new(\"Failed to deserialize catalog from puppetserver response: #{err.message}\", err)\n    end\n\n    logs = response_body['logs'] || []\n    [response, catalog, logs]\n  end\n\n  #\n  # @api private\n  #\n  # Submit a GET request to retrieve the facts for the named node\n  #\n  # @param [String] name Name of the node to retrieve facts for\n  # @param [String] environment Name of the environment we are operating in\n  #\n  # @return [Array<Puppet::HTTP::Response, Puppet::Node::Facts>] An array\n  #   containing the request response and the deserialized facts for the\n  #   specified node\n  #\n  # @api public\n  def get_facts(name, environment:)\n    headers = add_puppet_headers('Accept' => get_mime_types(Puppet::Node::Facts).join(', '))\n\n    response = @client.get(\n      with_base_url(\"/facts/#{name}\"),\n      headers: headers,\n      params: { environment: environment }\n    )\n\n    process_response(response)\n\n    [response, deserialize(response, Puppet::Node::Facts)]\n  end\n\n  # Submits a PUT request to submit facts for the node to the server.\n  #\n  # @param [String] name Name of the node we are submitting facts for\n  # @param [String] environment Name of the environment we are operating in\n  # @param [Puppet::Node::Facts] facts Facts for the named node\n  #\n  # @return [Puppet::HTTP::Response] The request response\n  #\n  # @api public\n  def put_facts(name, environment:, facts:)\n    formatter = Puppet::Network::FormatHandler.format_for(Puppet[:preferred_serialization_format])\n\n    headers = add_puppet_headers(\n      'Accept' => get_mime_types(Puppet::Node::Facts).join(', '),\n      'Content-Type' => formatter.mime\n    )\n\n    response = @client.put(\n      with_base_url(\"/facts/#{name}\"),\n      serialize(formatter, facts),\n      headers: headers,\n      params: { environment: environment }\n    )\n\n    process_response(response)\n\n    response\n  end\n\n  # Submit a GET request to retrieve a file stored with filebucket.\n  #\n  # @param [String] path The request path, formatted by `Puppet::FileBucket::Dipper`\n  # @param [String] environment Name of the environment we are operating in.\n  #   This should not impact filebucket at all, but is included to be consistent\n  #   with legacy code.\n  # @param [String] bucket_path\n  # @param [String] diff_with a checksum to diff against if we are comparing\n  #   files that are both stored in the bucket\n  # @param [String] list_all\n  # @param [String] fromdate\n  # @param [String] todate\n  #\n  # @return [Array<Puppet::HTTP::Response, Puppet::FileBucket::File>] An array\n  #   containing the request response and the deserialized file returned from\n  #   the server.\n  #\n  # @api public\n  def get_filebucket_file(path, environment:, bucket_path: nil, diff_with: nil, list_all: nil, fromdate: nil, todate: nil)\n    headers = add_puppet_headers('Accept' => 'application/octet-stream')\n\n    response = @client.get(\n      with_base_url(\"/file_bucket_file/#{path}\"),\n      headers: headers,\n      params: {\n        environment: environment,\n        bucket_path: bucket_path,\n        diff_with: diff_with,\n        list_all: list_all,\n        fromdate: fromdate,\n        todate: todate\n      }\n    )\n\n    process_response(response)\n\n    [response, deserialize(response, Puppet::FileBucket::File)]\n  end\n\n  # Submit a PUT request to store a file with filebucket.\n  #\n  # @param [String] path The request path, formatted by `Puppet::FileBucket::Dipper`\n  # @param [String] body The contents of the file to be backed\n  # @param [String] environment Name of the environment we are operating in.\n  #   This should not impact filebucket at all, but is included to be consistent\n  #   with legacy code.\n  #\n  # @return [Puppet::HTTP::Response] The response request\n  #\n  # @api public\n  def put_filebucket_file(path, body:, environment:)\n    headers = add_puppet_headers({\n                                   'Accept' => 'application/octet-stream',\n                                   'Content-Type' => 'application/octet-stream'\n                                 })\n\n    response = @client.put(\n      with_base_url(\"/file_bucket_file/#{path}\"),\n      body,\n      headers: headers,\n      params: {\n        environment: environment\n      }\n    )\n\n    process_response(response)\n\n    response\n  end\n\n  # Submit a HEAD request to check the status of a file stored with filebucket.\n  #\n  # @param [String] path The request path, formatted by `Puppet::FileBucket::Dipper`\n  # @param [String] environment Name of the environment we are operating in.\n  #   This should not impact filebucket at all, but is included to be consistent\n  #   with legacy code.\n  # @param [String] bucket_path\n  #\n  # @return [Puppet::HTTP::Response] The request response\n  #\n  # @api public\n  def head_filebucket_file(path, environment:, bucket_path: nil)\n    headers = add_puppet_headers('Accept' => 'application/octet-stream')\n\n    response = @client.head(\n      with_base_url(\"/file_bucket_file/#{path}\"),\n      headers: headers,\n      params: {\n        environment: environment,\n        bucket_path: bucket_path\n      }\n    )\n\n    process_response(response)\n\n    response\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/service/file_server.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/metadata'\n\n# The FileServer service is used to retrieve file metadata and content.\n#\n# @api public\n#\nclass Puppet::HTTP::Service::FileServer < Puppet::HTTP::Service\n  # @return [String] Default API for the FileServer service\n  API = '/puppet/v3'\n\n  # @return [RegEx] RegEx used to determine if a path contains a leading slash\n  PATH_REGEX = %r{^/}\n\n  # Use `Puppet::HTTP::Session.route_to(:fileserver)` to create or get an instance of this class.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Puppet::HTTP::Session] session\n  # @param [String] server (`Puppet[:server]`) If an explicit server is given,\n  #   create a service using that server. If server is nil, the default value\n  #   is used to create the service.\n  # @param [Integer] port (`Puppet[:masterport]`) If an explicit port is given, create\n  #   a service using that port. If port is nil, the default value is used to\n  #   create the service.\n  #\n  def initialize(client, session, server, port)\n    url = build_url(API, server || Puppet[:server], port || Puppet[:serverport])\n    super(client, session, url)\n  end\n\n  # Submit a GET request to the server to retrieve the metadata for a specified file.\n  #\n  # @param [String] path path to the file to retrieve data from\n  # @param [String] environment the name of the environment we are operating in\n  # @param [Symbol] links Can be one of either `:follow` or `:manage`, defines\n  #   how links are handled.\n  # @param [String] checksum_type The digest algorithm used to verify the file.\n  #   Defaults to `sha256`.\n  # @param [Symbol] source_permissions Can be one of `:use`, `:use_when_creating`,\n  #   or `:ignore`. This parameter tells the server if it should include the\n  #   file permissions in the response. If set to `:ignore`, the server will\n  #   return default permissions.\n  #\n  # @return [Array<Puppet::HTTP::Response, Puppet::FileServing::Metadata>] An\n  #   array with the request response and the deserialized metadata for the\n  #   file returned from the server\n  #\n  # @api public\n  #\n  def get_file_metadata(path:, environment:, links: :manage, checksum_type: Puppet[:digest_algorithm], source_permissions: :ignore)\n    validate_path(path)\n\n    headers = add_puppet_headers('Accept' => get_mime_types(Puppet::FileServing::Metadata).join(', '))\n\n    response = @client.get(\n      with_base_url(\"/file_metadata#{path}\"),\n      headers: headers,\n      params: {\n        links: links,\n        checksum_type: checksum_type,\n        source_permissions: source_permissions,\n        environment: environment\n      }\n    )\n\n    process_response(response)\n\n    [response, deserialize(response, Puppet::FileServing::Metadata)]\n  end\n\n  # Submit a GET request to the server to retrieve the metadata for multiple files\n  #\n  # @param [String] path path to the file(s) to retrieve data from\n  # @param [String] environment the name of the environment we are operating in\n  # @param [Symbol] recurse  Can be `:true`, `:false`, or `:remote`. Defines if\n  #   we recursively return the contents of the directory. Used in conjunction\n  #   with `:recurselimit`. See the reference documentation for the file type\n  #   for more details.\n  # @param [Integer] recurselimit When `recurse` is set, `recurselimit` defines\n  #   how far Puppet should descend into subdirectories. `0` is effectively the\n  #   same as `recurse => false`, `1` will return files and directories directly\n  #   inside the defined directory, `2` will return the direct content of the\n  #   directory as well as the contents of the _first_ level of subdirectories.\n  #   The pattern continues for each incremental value. See the reference\n  #   documentation for the file type for more details.\n  # @param [Array<String>] ignore An optional array of files to ignore, ie `['CVS', '.git', '.hg']`\n  # @param [Symbol] links Can be one of either `:follow` or `:manage`, defines\n  #   how links are handled.\n  # @param [String] checksum_type The digest algorithm used to verify the file.\n  #   Currently if fips is enabled, this defaults to `sha256`. Otherwise, it's `md5`.\n  # @param [Symbol] source_permissions Can be one of `:use`, `:use_when_creating`,\n  #   or `:ignore`. This parameter tells the server if it should include the\n  #   file permissions in the report. If set to `:ignore`, the server will return\n  #   default permissions.\n  #\n  # @return [Array<Puppet::HTTP::Response, Array<Puppet::FileServing::Metadata>>]\n  #   An array with the request response and an array of the deserialized\n  #   metadata for each file returned from the server\n  #\n  # @api public\n  #\n  def get_file_metadatas(environment:, path: nil, recurse: :false, recurselimit: nil, max_files: nil, ignore: nil, links: :manage, checksum_type: Puppet[:digest_algorithm], source_permissions: :ignore) # rubocop:disable Lint/BooleanSymbol\n    validate_path(path)\n\n    headers = add_puppet_headers('Accept' => get_mime_types(Puppet::FileServing::Metadata).join(', '))\n\n    response = @client.get(\n      with_base_url(\"/file_metadatas#{path}\"),\n      headers: headers,\n      params: {\n        recurse: recurse,\n        recurselimit: recurselimit,\n        max_files: max_files,\n        ignore: ignore,\n        links: links,\n        checksum_type: checksum_type,\n        source_permissions: source_permissions,\n        environment: environment,\n      }\n    )\n\n    process_response(response)\n\n    [response, deserialize_multiple(response, Puppet::FileServing::Metadata)]\n  end\n\n  # Submit a GET request to the server to retrieve content of a file.\n  #\n  # @param [String] path path to the file to retrieve data from\n  # @param [String] environment the name of the environment we are operating in\n  #\n  # @yield [Sting] Yields the body of the response returned from the server\n  #\n  # @return [Puppet::HTTP::Response] The request response\n  #\n  # @api public\n  #\n  def get_file_content(path:, environment:, &block)\n    validate_path(path)\n\n    headers = add_puppet_headers('Accept' => 'application/octet-stream')\n    response = @client.get(\n      with_base_url(\"/file_content#{path}\"),\n      headers: headers,\n      params: {\n        environment: environment\n      }\n    ) do |res|\n      if res.success?\n        res.read_body(&block)\n      end\n    end\n\n    process_response(response)\n\n    response\n  end\n\n  # Submit a GET request to retrieve file content using the `static_file_content` API\n  # uniquely identified by (`code_id`, `environment`, `path`).\n  #\n  # @param [String] path path to the file to retrieve data from\n  # @param [String] environment the name of the environment we are operating in\n  # @param [String] code_id Defines the version of the resource to return\n  #\n  # @yield [String] Yields the body of the response returned\n  #\n  # @return [Puppet::HTTP::Response] The request response\n  #\n  # @api public\n  #\n  def get_static_file_content(path:, environment:, code_id:, &block)\n    validate_path(path)\n\n    headers = add_puppet_headers('Accept' => 'application/octet-stream')\n    response = @client.get(\n      with_base_url(\"/static_file_content#{path}\"),\n      headers: headers,\n      params: {\n        environment: environment,\n        code_id: code_id,\n      }\n    ) do |res|\n      if res.success?\n        res.read_body(&block)\n      end\n    end\n\n    process_response(response)\n\n    response\n  end\n\n  private\n\n  def validate_path(path)\n    raise ArgumentError, \"Path must start with a slash\" unless path =~ PATH_REGEX\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/service/puppetserver.rb",
    "content": "# frozen_string_literal: true\n\n# The puppetserver service.\n#\n# @api public\n#\nclass Puppet::HTTP::Service::Puppetserver < Puppet::HTTP::Service\n  # Use `Puppet::HTTP::Session.route_to(:puppetserver)` to create or get an instance of this class.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Puppet::HTTP::Session] session\n  # @param [String] server (`Puppet[:server]`) If an explicit server is given,\n  #   create a service using that server. If server is nil, the default value\n  #   is used to create the service.\n  # @param [Integer] port (`Puppet[:masterport]`) If an explicit port is given, create\n  #   a service using that port. If port is nil, the default value is used to\n  #   create the service.\n  #\n  def initialize(client, session, server, port)\n    url = build_url('', server || Puppet[:server], port || Puppet[:serverport])\n    super(client, session, url)\n  end\n\n  # Request the puppetserver's simple status.\n  #\n  # @param [Puppet::SSL::SSLContext] ssl_context to use when establishing\n  # the connection.\n  # @return Puppet::HTTP::Response The HTTP response\n  #\n  # @api public\n  #\n  def get_simple_status(ssl_context: nil)\n    request_path = \"/status/v1/simple/server\"\n\n    begin\n      response = @client.get(\n        with_base_url(request_path),\n        headers: add_puppet_headers({}),\n        options: { ssl_context: ssl_context }\n      )\n\n      process_response(response)\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 404 && e.response.url.path == \"/status/v1/simple/server\"\n        request_path = \"/status/v1/simple/master\"\n        retry\n      else\n        raise e\n      end\n    end\n\n    [response, response.body.to_s]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/service/report.rb",
    "content": "# frozen_string_literal: true\n\n# The Report service is used to submit run reports to the report server.\n#\n# @api public\n#\nclass Puppet::HTTP::Service::Report < Puppet::HTTP::Service\n  # @return [String] Default API for the report service\n  API = '/puppet/v3'\n\n  # Use `Puppet::HTTP::Session.route_to(:report)` to create or get an instance of this class.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Puppet::HTTP::Session] session\n  # @param [String] server (Puppet[:ca_server]) If an explicit server is given,\n  #   create a service using that server. If server is nil, the default value\n  #   is used to create the service.\n  # @param [Integer] port (Puppet[:ca_port]) If an explicit port is given, create\n  #   a service using that port. If port is nil, the default value is used to\n  #   create the service.\n  #\n  # @api private\n  #\n  def initialize(client, session, server, port)\n    url = build_url(API, server || Puppet[:report_server], port || Puppet[:report_port])\n    super(client, session, url)\n  end\n\n  # Submit a report to the report server.\n  #\n  # @param [String] name the name of the report being submitted\n  # @param [Puppet::Transaction::Report] report run report to be submitted\n  # @param [String] environment name of the agent environment\n  #\n  # @return [Puppet::HTTP::Response] response returned by the server\n  #\n  # @api public\n  #\n  def put_report(name, report, environment:)\n    formatter = Puppet::Network::FormatHandler.format_for(Puppet[:preferred_serialization_format])\n    headers = add_puppet_headers(\n      'Accept' => get_mime_types(Puppet::Transaction::Report).join(', '),\n      'Content-Type' => formatter.mime\n    )\n\n    response = @client.put(\n      with_base_url(\"/report/#{name}\"),\n      serialize(formatter, report),\n      headers: headers,\n      params: { environment: environment }\n    )\n\n    # override parent's process_response handling\n    @session.process_response(response)\n\n    if response.success?\n      response\n    else\n      raise Puppet::HTTP::ResponseError, response\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/service.rb",
    "content": "# frozen_string_literal: true\n\n# Represents an abstract Puppet web service.\n#\n# @abstract Subclass and implement methods for the service's REST APIs.\n# @api public\nclass Puppet::HTTP::Service\n  # @return [URI] the url associated with this service\n  attr_reader :url\n\n  # @return [Array<Symbol>] available services\n  SERVICE_NAMES = [:ca, :fileserver, :puppet, :puppetserver, :report].freeze\n\n  # @return [Array<Symbol>] format types that are unsupported\n  EXCLUDED_FORMATS = [:yaml, :b64_zlib_yaml, :dot].freeze\n\n  # Create a new web service, which contains the URL used to connect to the\n  # service. The four services implemented are `:ca`, `:fileserver`, `:puppet`,\n  # and `:report`.\n  #\n  # The `:ca` and `:report` services handle certs and reports, respectively. The\n  # `:fileserver` service handles puppet file metadata and content requests. And\n  # the default service, `:puppet`, handles nodes, facts, and catalogs.\n  #\n  # @param [Puppet::HTTP::Client] client the owner of the session\n  # @param [Puppet::HTTP::Session] session the owner of the service\n  # @param [Symbol] name the type of service to create\n  # @param [<Type>] server optional, the server to connect to\n  # @param [<Type>] port optional, the port to connect to\n  #\n  # @return [Puppet::HTTP::Service] an instance of the service type requested\n  #\n  # @api private\n  def self.create_service(client, session, name, server = nil, port = nil)\n    case name\n    when :ca\n      Puppet::HTTP::Service::Ca.new(client, session, server, port)\n    when :fileserver\n      Puppet::HTTP::Service::FileServer.new(client, session, server, port)\n    when :puppet\n      ::Puppet::HTTP::Service::Compiler.new(client, session, server, port)\n    when :puppetserver\n      ::Puppet::HTTP::Service::Puppetserver.new(client, session, server, port)\n    when :report\n      Puppet::HTTP::Service::Report.new(client, session, server, port)\n    else\n      raise ArgumentError, \"Unknown service #{name}\"\n    end\n  end\n\n  # Check if the service named is included in the list of available services.\n  #\n  # @param [Symbol] name\n  #\n  # @return [Boolean]\n  #\n  # @api private\n  def self.valid_name?(name)\n    SERVICE_NAMES.include?(name)\n  end\n\n  # Create a new service. Services should be created by calling `Puppet::HTTP::Session#route_to`.\n  #\n  # @param [Puppet::HTTP::Client] client\n  # @param [Puppet::HTTP::Session] session\n  # @param [URI] url The url to connect to\n  #\n  # @api private\n  def initialize(client, session, url)\n    @client = client\n    @session = session\n    @url = url\n  end\n\n  # Return the url with the given path encoded and appended\n  #\n  # @param [String] path the string to append to the base url\n  #\n  # @return [URI] the URI object containing the encoded path\n  #\n  # @api public\n  def with_base_url(path)\n    u = @url.dup\n    u.path += Puppet::Util.uri_encode(path)\n    u\n  end\n\n  # Open a connection using the given ssl context.\n  #\n  # @param [Puppet::SSL::SSLContext] ssl_context An optional ssl context to connect with\n  # @return [void]\n  #\n  # @api public\n  def connect(ssl_context: nil)\n    @client.connect(@url, options: { ssl_context: ssl_context })\n  end\n\n  protected\n\n  def add_puppet_headers(headers)\n    modified_headers = headers.dup\n\n    # Add 'X-Puppet-Profiling' to enable performance profiling if turned on\n    modified_headers['X-Puppet-Profiling'] = 'true' if Puppet[:profile]\n\n    # Add additional user-defined headers if they are defined\n    Puppet[:http_extra_headers].each do |name, value|\n      if modified_headers.keys.find { |key| key.casecmp(name) == 0 }\n        Puppet.warning(_('Ignoring extra header \"%{name}\" as it was previously set.') % { name: name })\n      elsif value.nil? || value.empty?\n        Puppet.warning(_('Ignoring extra header \"%{name}\" as it has no value.') % { name: name })\n      else\n        modified_headers[name] = value\n      end\n    end\n    modified_headers\n  end\n\n  def build_url(api, server, port)\n    URI::HTTPS.build(host: server,\n                     port: port,\n                     path: api).freeze\n  end\n\n  def get_mime_types(model)\n    network_formats = model.supported_formats - EXCLUDED_FORMATS\n    network_formats.map { |f| model.get_format(f).mime }\n  end\n\n  def formatter_for_response(response)\n    header = response['Content-Type']\n    raise Puppet::HTTP::ProtocolError, _(\"No content type in http response; cannot parse\") unless header\n\n    header.gsub!(/\\s*;.*$/, '') # strip any charset\n\n    formatter = Puppet::Network::FormatHandler.mime(header)\n    raise Puppet::HTTP::ProtocolError, \"Content-Type is unsupported\" if EXCLUDED_FORMATS.include?(formatter.name)\n\n    formatter\n  end\n\n  def serialize(formatter, object)\n    formatter.render(object)\n  rescue => err\n    raise Puppet::HTTP::SerializationError.new(\"Failed to serialize #{object.class} to #{formatter.name}: #{err.message}\", err)\n  end\n\n  def serialize_multiple(formatter, object)\n    formatter.render_multiple(object)\n  rescue => err\n    raise Puppet::HTTP::SerializationError.new(\"Failed to serialize multiple #{object.class} to #{formatter.name}: #{err.message}\", err)\n  end\n\n  def deserialize(response, model)\n    formatter = formatter_for_response(response)\n    begin\n      formatter.intern(model, response.body.to_s)\n    rescue => err\n      raise Puppet::HTTP::SerializationError.new(\"Failed to deserialize #{model} from #{formatter.name}: #{err.message}\", err)\n    end\n  end\n\n  def deserialize_multiple(response, model)\n    formatter = formatter_for_response(response)\n    begin\n      formatter.intern_multiple(model, response.body.to_s)\n    rescue => err\n      raise Puppet::HTTP::SerializationError.new(\"Failed to deserialize multiple #{model} from #{formatter.name}: #{err.message}\", err)\n    end\n  end\n\n  def process_response(response)\n    @session.process_response(response)\n\n    raise Puppet::HTTP::ResponseError, response unless response.success?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/session.rb",
    "content": "# frozen_string_literal: true\n\n# The session is the mechanism by which services may be connected to and accessed.\n#\n# @api public\nclass Puppet::HTTP::Session\n  # capabilities for a site\n  CAP_LOCALES = 'locales'\n  CAP_JSON = 'json'\n\n  # puppet version where locales mount was added\n  SUPPORTED_LOCALES_MOUNT_AGENT_VERSION = Gem::Version.new(\"5.3.4\")\n\n  # puppet version where JSON was enabled by default\n  SUPPORTED_JSON_DEFAULT = Gem::Version.new(\"5.0.0\")\n\n  # Create a new HTTP session. The session is the mechanism by which services\n  # may be connected to and accessed. Sessions should be created using\n  # `Puppet::HTTP::Client#create_session`.\n  #\n  # @param [Puppet::HTTP::Client] client the container for this session\n  # @param [Array<Puppet::HTTP::Resolver>] resolvers array of resolver strategies\n  #   to implement.\n  #\n  # @api private\n  def initialize(client, resolvers)\n    @client = client\n    @resolvers = resolvers\n    @resolved_services = {}\n    @server_versions = {}\n  end\n\n  # If an explicit server and port are specified on the command line or\n  # configuration file, this method always returns a Service with that host and\n  # port. Otherwise, we walk the list of resolvers in priority order:\n  #     - DNS SRV\n  #     - Server List\n  #     - Puppet server/port settings\n  # If a given resolver fails to connect, it tries the next available resolver\n  # until a successful connection is found and returned. The successful service\n  # is cached and returned if `route_to` is called again.\n  #\n  # @param [Symbol] name the service to resolve\n  # @param [URI] url optional explicit url to use, if it is already known\n  # @param [Puppet::SSL::SSLContext] ssl_context ssl context to be\n  #   used for connections\n  #\n  # @return [Puppet::HTTP::Service] the resolved service\n  #\n  # @api public\n  def route_to(name, url: nil, ssl_context: nil)\n    raise ArgumentError, \"Unknown service #{name}\" unless Puppet::HTTP::Service.valid_name?(name)\n\n    # short circuit if explicit URL host & port given\n    if url && !url.host.nil? && !url.host.empty?\n      service = Puppet::HTTP::Service.create_service(@client, self, name, url.host, url.port)\n      service.connect(ssl_context: ssl_context)\n      return service\n    end\n\n    cached = @resolved_services[name]\n    return cached if cached\n\n    canceled = false\n    canceled_handler = ->(cancel) { canceled = cancel }\n\n    @resolvers.each do |resolver|\n      Puppet.debug(\"Resolving service '#{name}' using #{resolver.class}\")\n      service = resolver.resolve(self, name, ssl_context: ssl_context, canceled_handler: canceled_handler)\n      if service\n        @resolved_services[name] = service\n        Puppet.debug(\"Resolved service '#{name}' to #{service.url}\")\n        return service\n      elsif canceled\n        break\n      end\n    end\n\n    raise Puppet::HTTP::RouteError, \"No more routes to #{name}\"\n  end\n\n  # Collect per-site server versions. This will allow us to modify future\n  # requests based on the version of puppetserver we are talking to.\n  #\n  # @param [Puppet::HTTP::Response] response the request response containing headers\n  #\n  # @api private\n  def process_response(response)\n    version = response[Puppet::HTTP::HEADER_PUPPET_VERSION]\n    if version\n      site = Puppet::HTTP::Site.from_uri(response.url)\n      @server_versions[site] = version\n    end\n  end\n\n  # Determine if a session supports a capability. Depending on the server version\n  # we are talking to, we know certain features are available or not. These\n  # specifications are defined here so we can modify our requests appropriately.\n  #\n  # @param [Symbol] name name of the service to check\n  # @param [String] capability the capability, ie `locales` or `json`\n  #\n  # @return [Boolean]\n  #\n  # @api public\n  def supports?(name, capability)\n    raise ArgumentError, \"Unknown service #{name}\" unless Puppet::HTTP::Service.valid_name?(name)\n\n    service = @resolved_services[name]\n    return false unless service\n\n    site = Puppet::HTTP::Site.from_uri(service.url)\n    server_version = @server_versions[site]\n\n    case capability\n    when CAP_LOCALES\n      !server_version.nil? && Gem::Version.new(server_version) >= SUPPORTED_LOCALES_MOUNT_AGENT_VERSION\n    when CAP_JSON\n      server_version.nil? || Gem::Version.new(server_version) >= SUPPORTED_JSON_DEFAULT\n    else\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http/site.rb",
    "content": "# frozen_string_literal: true\n\n# Represents a site to which HTTP connections are made. It is a value\n# object, and is suitable for use in a hash. If two sites are equal,\n# then a persistent connection made to the first site, can be re-used\n# for the second.\n#\n# @api private\nclass Puppet::HTTP::Site\n  attr_reader :scheme, :host, :port\n\n  def self.from_uri(uri)\n    new(uri.scheme, uri.host, uri.port)\n  end\n\n  def initialize(scheme, host, port)\n    @scheme = scheme\n    @host = host\n    @port = port.to_i\n  end\n\n  def addr\n    \"#{@scheme}://#{@host}:#{@port}\"\n  end\n  alias to_s addr\n\n  def ==(rhs)\n    (@scheme == rhs.scheme) && (@host == rhs.host) && (@port == rhs.port)\n  end\n\n  alias eql? ==\n\n  def hash\n    [@scheme, @host, @port].hash\n  end\n\n  def use_ssl?\n    @scheme == 'https'\n  end\n\n  def move_to(uri)\n    self.class.new(uri.scheme, uri.host, uri.port)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/http.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  # Contains an HTTP client for making network requests to puppet and other\n  # HTTP servers.\n  #\n  # @see Puppet::HTTP::Client\n  # @see Puppet::HTTP::HTTPError\n  # @see Puppet::HTTP::Response\n  # @api public\n  module HTTP\n    ACCEPT_ENCODING = \"gzip;q=1.0,deflate;q=0.6,identity;q=0.3\"\n    HEADER_PUPPET_VERSION = \"X-Puppet-Version\"\n\n    require_relative 'http/errors'\n    require_relative 'http/site'\n    require_relative 'http/pool_entry'\n    require_relative 'http/proxy'\n    require_relative 'http/factory'\n    require_relative 'http/pool'\n    require_relative 'http/dns'\n    require_relative 'http/response'\n    require_relative 'http/response_converter'\n    require_relative 'http/response_net_http'\n    require_relative 'http/service'\n    require_relative 'http/service/ca'\n    require_relative 'http/service/compiler'\n    require_relative 'http/service/file_server'\n    require_relative 'http/service/puppetserver'\n    require_relative 'http/service/report'\n    require_relative 'http/session'\n    require_relative 'http/resolver'\n    require_relative 'http/resolver/server_list'\n    require_relative 'http/resolver/settings'\n    require_relative 'http/resolver/srv'\n    require_relative 'http/client'\n    require_relative 'http/redirector'\n    require_relative 'http/retry_after_handler'\n    require_relative 'http/external_client'\n  end\n\n  # Legacy HTTP API\n  module Network\n    module HTTP\n      require_relative '../puppet/network/http_pool'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/catalog/compiler.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/environments'\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/node/server_facts'\nrequire_relative '../../../puppet/resource/catalog'\nrequire_relative '../../../puppet/indirector/code'\nrequire_relative '../../../puppet/util/profiler'\nrequire_relative '../../../puppet/util/checksums'\nrequire 'yaml'\nrequire 'uri'\n\nclass Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code\n  desc \"Compiles catalogs on demand using Puppet's compiler.\"\n\n  include Puppet::Util\n  include Puppet::Util::Checksums\n\n  attr_accessor :code\n\n  # @param request [Puppet::Indirector::Request] an indirection request\n  #   (possibly) containing facts\n  # @return [Puppet::Node::Facts] facts object corresponding to facts in request\n  def extract_facts_from_request(request)\n    text_facts = request.options[:facts]\n    return unless text_facts\n\n    format = request.options[:facts_format]\n    unless format\n      raise ArgumentError, _(\"Facts but no fact format provided for %{request}\") % { request: request.key }\n    end\n\n    Puppet::Util::Profiler.profile(_(\"Found facts\"), [:compiler, :find_facts]) do\n      facts = text_facts.is_a?(Puppet::Node::Facts) ? text_facts :\n                                                      convert_wire_facts(text_facts, format)\n\n      unless facts.name == request.key\n        raise Puppet::Error, _(\"Catalog for %{request} was requested with fact definition for the wrong node (%{fact_name}).\") % { request: request.key.inspect, fact_name: facts.name.inspect }\n      end\n\n      return facts\n    end\n  end\n\n  def save_facts_from_request(facts, request)\n    Puppet::Node::Facts.indirection.save(facts, nil,\n                                         :environment => request.environment,\n                                         :transaction_uuid => request.options[:transaction_uuid])\n  end\n\n  # Compile a node's catalog.\n  def find(request)\n    facts = extract_facts_from_request(request)\n\n    save_facts_from_request(facts, request) unless facts.nil?\n\n    node = node_from_request(facts, request)\n    node.trusted_data = Puppet.lookup(:trusted_information) { Puppet::Context::TrustedInformation.local(node) }.to_h\n\n    if node.environment\n      # If the requested environment name doesn't match the server specified environment\n      # name, as determined by the node terminus, and the request wants us to check for an\n      # environment mismatch, then return an empty catalog with the server-specified\n      # enviroment.\n      if request.remote? && request.options[:check_environment]\n        # The \"environment\" may be same while environment objects differ. This\n        # is most likely because the environment cache was flushed between the request\n        # processing and node lookup. Environment overrides `==` but requires the\n        # name and modulepath to be the same. When using versioned environment dirs the\n        # same \"environment\" can have different modulepaths so simply compare names here.\n        if node.environment.name != request.environment.name\n          Puppet.warning _(\"Requested environment '%{request_env}' did not match server specified environment '%{server_env}'\") % { request_env: request.environment.name, server_env: node.environment.name }\n          return Puppet::Resource::Catalog.new(node.name, node.environment)\n        end\n      end\n\n      node.environment.with_text_domain do\n        envs = Puppet.lookup(:environments)\n        envs.guard(node.environment.name)\n        begin\n          compile(node, request.options)\n        ensure\n          envs.unguard(node.environment.name)\n        end\n      end\n    else\n      compile(node, request.options)\n    end\n  end\n\n  # filter-out a catalog to remove exported resources\n  def filter(catalog)\n    return catalog.filter(&:virtual?) if catalog.respond_to?(:filter)\n\n    catalog\n  end\n\n  def initialize\n    Puppet::Util::Profiler.profile(_(\"Setup server facts for compiling\"), [:compiler, :init_server_facts]) do\n      set_server_facts\n    end\n  end\n\n  # Is our compiler part of a network, or are we just local?\n  def networked?\n    Puppet.run_mode.server?\n  end\n\n  def require_environment?\n    false\n  end\n\n  private\n\n  # @param facts [String] facts in a wire format for decoding\n  # @param format [String] a content-type string\n  # @return [Puppet::Node::Facts] facts object deserialized from supplied string\n  # @api private\n  def convert_wire_facts(facts, format)\n    case format\n    when 'pson'\n      # We unescape here because the corresponding code in Puppet::Configurer::FactHandler encodes with Puppet::Util.uri_query_encode\n      # PSON is deprecated, but continue to accept from older agents\n      Puppet::Node::Facts.convert_from('pson', CGI.unescape(facts))\n    when 'application/json'\n      Puppet::Node::Facts.convert_from('json', CGI.unescape(facts))\n    else\n      raise ArgumentError, _(\"Unsupported facts format\")\n    end\n  end\n\n  # Add any extra data necessary to the node.\n  def add_node_data(node)\n    # Merge in our server-side facts, so they can be used during compilation.\n    node.add_server_facts(@server_facts)\n  end\n\n  # Determine which checksum to use; if agent_checksum_type is not nil,\n  # use the first entry in it that is also in known_checksum_types.\n  # If no match is found, return nil.\n  def common_checksum_type(agent_checksum_type)\n    if agent_checksum_type\n      agent_checksum_types = agent_checksum_type.split('.').map(&:to_sym)\n      checksum_type = agent_checksum_types.drop_while do |type|\n        !known_checksum_types.include? type\n      end.first\n    end\n    checksum_type\n  end\n\n  def get_content_uri(metadata, source, environment_path)\n    # The static file content server doesn't know how to expand mountpoints, so\n    # we need to do that ourselves from the actual system path of the source file.\n    # This does that, while preserving any user-specified server or port.\n    source_path = Pathname.new(metadata.full_path)\n    path = source_path.relative_path_from(environment_path).to_s\n    source_as_uri = URI.parse(Puppet::Util.uri_encode(source))\n    server = source_as_uri.host\n    port = \":#{source_as_uri.port}\" if source_as_uri.port\n    \"puppet://#{server}#{port}/#{path}\"\n  end\n\n  # Helper method to decide if a file resource's metadata can be inlined.\n  # Also used to profile/log reasons for not inlining.\n  def inlineable?(resource, sources)\n    if resource[:ensure] == 'absent'\n      # TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n      Puppet::Util::Profiler.profile(_(\"Not inlining absent resource\"), [:compiler, :static_compile_inlining, :skipped_file_metadata, :absent]) { false }\n    elsif sources.empty?\n      # TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n      Puppet::Util::Profiler.profile(_(\"Not inlining resource without sources\"), [:compiler, :static_compile_inlining, :skipped_file_metadata, :no_sources]) { false }\n    elsif !(sources.all? { |source| source =~ /^puppet:/ })\n      # TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n      Puppet::Util::Profiler.profile(_(\"Not inlining unsupported source scheme\"), [:compiler, :static_compile_inlining, :skipped_file_metadata, :unsupported_scheme]) { false }\n    else\n      true\n    end\n  end\n\n  # Return true if metadata is inlineable, meaning the request's source is\n  # for the 'modules' mount and the resolved path is of the form:\n  #   $codedir/environments/$environment/*/*/files/**\n  def inlineable_metadata?(metadata, source, environment_path)\n    source_as_uri = URI.parse(Puppet::Util.uri_encode(source))\n\n    location = Puppet::Module::FILETYPES['files']\n\n    !!(source_as_uri.path =~ %r{^/modules/} &&\n       metadata.full_path =~ %r{#{environment_path}/[^/]+/[^/]+/#{location}/.+})\n  end\n\n  # Helper method to log file resources that could not be inlined because they\n  # fall outside of an environment.\n  def log_file_outside_environment\n    # TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n    Puppet::Util::Profiler.profile(_(\"Not inlining file outside environment\"), [:compiler, :static_compile_inlining, :skipped_file_metadata, :file_outside_environment]) { true }\n  end\n\n  # Helper method to log file resources that were successfully inlined.\n  def log_metadata_inlining\n    # TRANSLATORS Inlining refers to adding additional metadata\n    Puppet::Util::Profiler.profile(_(\"Inlining file metadata\"), [:compiler, :static_compile_inlining, :inlined_file_metadata]) { true }\n  end\n\n  # Inline file metadata for static catalogs\n  # Initially restricted to files sourced from codedir via puppet:/// uri.\n  def inline_metadata(catalog, checksum_type)\n    environment_path = Pathname.new File.join(Puppet[:environmentpath], catalog.environment)\n    environment_path = Puppet::Environments::Directories.real_path(environment_path)\n    list_of_resources = catalog.resources.find_all { |res| res.type == \"File\" }\n\n    # TODO: get property/parameter defaults if entries are nil in the resource\n    # For now they're hard-coded to match the File type.\n\n    list_of_resources.each do |resource|\n      sources = [resource[:source]].flatten.compact\n      next unless inlineable?(resource, sources)\n\n      # both need to handle multiple sources\n      if resource[:recurse] == true || resource[:recurse] == 'true' || resource[:recurse] == 'remote'\n        # Construct a hash mapping sources to arrays (list of files found recursively) of metadata\n        options = {\n          :environment => catalog.environment_instance,\n          :links => resource[:links] ? resource[:links].to_sym : :manage,\n          :checksum_type => resource[:checksum] ? resource[:checksum].to_sym : checksum_type.to_sym,\n          :source_permissions => resource[:source_permissions] ? resource[:source_permissions].to_sym : :ignore,\n          :recurse => true,\n          :recurselimit => resource[:recurselimit],\n          :max_files => resource[:max_files],\n          :ignore => resource[:ignore],\n        }\n\n        sources_in_environment = true\n\n        source_to_metadatas = {}\n        sources.each do |source|\n          source = Puppet::Type.type(:file).attrclass(:source).normalize(source)\n\n          list_of_data = Puppet::FileServing::Metadata.indirection.search(source, options)\n          next unless list_of_data\n\n          basedir_meta = list_of_data.find { |meta| meta.relative_path == '.' }\n          devfail \"FileServing::Metadata search should always return the root search path\" if basedir_meta.nil?\n\n          unless inlineable_metadata?(basedir_meta, source, environment_path)\n            # If any source is not in the environment path, skip inlining this resource.\n            log_file_outside_environment\n            sources_in_environment = false\n            break\n          end\n\n          base_content_uri = get_content_uri(basedir_meta, source, environment_path)\n          list_of_data.each do |metadata|\n            if metadata.relative_path == '.'\n              metadata.content_uri = base_content_uri\n            else\n              metadata.content_uri = \"#{base_content_uri}/#{metadata.relative_path}\"\n            end\n          end\n\n          source_to_metadatas[source] = list_of_data\n          # Optimize for returning less data if sourceselect is first\n          if resource[:sourceselect] == 'first' || resource[:sourceselect].nil?\n            break\n          end\n        end\n\n        if sources_in_environment && !source_to_metadatas.empty?\n          log_metadata_inlining\n          catalog.recursive_metadata[resource.title] = source_to_metadatas\n        end\n      else\n        options = {\n          :environment => catalog.environment_instance,\n          :links => resource[:links] ? resource[:links].to_sym : :manage,\n          :checksum_type => resource[:checksum] ? resource[:checksum].to_sym : checksum_type.to_sym,\n          :source_permissions => resource[:source_permissions] ? resource[:source_permissions].to_sym : :ignore\n        }\n\n        metadata = nil\n        sources.each do |source|\n          source = Puppet::Type.type(:file).attrclass(:source).normalize(source)\n\n          data = Puppet::FileServing::Metadata.indirection.find(source, options)\n          next unless data\n\n          metadata = data\n          metadata.source = source\n          break\n        end\n\n        raise _(\"Could not get metadata for %{resource}\") % { resource: resource[:source] } unless metadata\n\n        if inlineable_metadata?(metadata, metadata.source,  environment_path)\n          metadata.content_uri = get_content_uri(metadata, metadata.source, environment_path)\n          log_metadata_inlining\n\n          # If the file is in the environment directory, we can safely inline\n          catalog.metadata[resource.title] = metadata\n        else\n          # Log a profiler event that we skipped this file because it is not in an environment.\n          log_file_outside_environment\n        end\n      end\n    end\n  end\n\n  # Compile the actual catalog.\n  def compile(node, options)\n    if node.environment && node.environment.static_catalogs? && options[:static_catalog] && options[:code_id]\n      # Check for errors before compiling the catalog\n      checksum_type = common_checksum_type(options[:checksum_type])\n      raise Puppet::Error, _(\"Unable to find a common checksum type between agent '%{agent_type}' and master '%{master_type}'.\") % { agent_type: options[:checksum_type], master_type: known_checksum_types } unless checksum_type\n    end\n\n    escaped_node_name = node.name.gsub(/%/, '%%')\n    if checksum_type\n      if node.environment\n        escaped_node_environment = node.environment.to_s.gsub(/%/, '%%')\n        benchmark_str = _(\"Compiled static catalog for %{node} in environment %{environment} in %%{seconds} seconds\") % { node: escaped_node_name, environment: escaped_node_environment }\n        profile_str   = _(\"Compiled static catalog for %{node} in environment %{environment}\") % { node: node.name, environment: node.environment }\n      else\n        benchmark_str = _(\"Compiled static catalog for %{node} in %%{seconds} seconds\") % { node: escaped_node_name }\n        profile_str   = _(\"Compiled static catalog for %{node}\") % { node: node.name }\n      end\n    elsif node.environment\n      escaped_node_environment = node.environment.to_s.gsub(/%/, '%%')\n      benchmark_str = _(\"Compiled catalog for %{node} in environment %{environment} in %%{seconds} seconds\") % { node: escaped_node_name, environment: escaped_node_environment }\n      profile_str   = _(\"Compiled catalog for %{node} in environment %{environment}\") % { node: node.name, environment: node.environment }\n    else\n      benchmark_str = _(\"Compiled catalog for %{node} in %%{seconds} seconds\") % { node: escaped_node_name }\n      profile_str   = _(\"Compiled catalog for %{node}\") % { node: node.name }\n    end\n    config = nil\n\n    benchmark(:notice, benchmark_str) do\n      compile_type = checksum_type ? :static_compile : :compile\n      Puppet::Util::Profiler.profile(profile_str, [:compiler, compile_type, node.environment, node.name]) do\n        begin\n          config = Puppet::Parser::Compiler.compile(node, options[:code_id])\n        rescue Puppet::Error => detail\n          Puppet.err(detail.to_s) if networked?\n          raise\n        ensure\n          Puppet::Type.clear_misses unless Puppet[:always_retry_plugins]\n        end\n\n        if checksum_type && config.is_a?(model)\n          escaped_node_name = node.name.gsub(/%/, '%%')\n          if node.environment\n            escaped_node_environment = node.environment.to_s.gsub(/%/, '%%')\n            # TRANSLATORS Inlined refers to adding additional metadata\n            benchmark_str = _(\"Inlined resource metadata into static catalog for %{node} in environment %{environment} in %%{seconds} seconds\") % { node: escaped_node_name, environment: escaped_node_environment }\n            # TRANSLATORS Inlined refers to adding additional metadata\n            profile_str   = _(\"Inlined resource metadata into static catalog for %{node} in environment %{environment}\") % { node: node.name, environment: node.environment }\n          else\n            # TRANSLATORS Inlined refers to adding additional metadata\n            benchmark_str = _(\"Inlined resource metadata into static catalog for %{node} in %%{seconds} seconds\") % { node: escaped_node_name }\n            # TRANSLATORS Inlined refers to adding additional metadata\n            profile_str   = _(\"Inlined resource metadata into static catalog for %{node}\") % { node: node.name }\n          end\n          benchmark(:notice, benchmark_str) do\n            Puppet::Util::Profiler.profile(profile_str, [:compiler, :static_compile_postprocessing, node.environment, node.name]) do\n              inline_metadata(config, checksum_type)\n            end\n          end\n        end\n      end\n    end\n\n    config\n  end\n\n  # Use indirection to find the node associated with a given request\n  def find_node(name, environment, transaction_uuid, configured_environment, facts)\n    Puppet::Util::Profiler.profile(_(\"Found node information\"), [:compiler, :find_node]) do\n      node = nil\n      begin\n        node = Puppet::Node.indirection.find(name, :environment => environment,\n                                                   :transaction_uuid => transaction_uuid,\n                                                   :configured_environment => configured_environment,\n                                                   :facts => facts)\n      rescue => detail\n        message = _(\"Failed when searching for node %{name}: %{detail}\") % { name: name, detail: detail }\n        Puppet.log_exception(detail, message)\n        raise Puppet::Error, message, detail.backtrace\n      end\n\n      # Add any external data to the node.\n      if node\n        add_node_data(node)\n      end\n      node\n    end\n  end\n\n  # Extract the node from the request, or use the request\n  # to find the node.\n  def node_from_request(facts, request)\n    node = request.options[:use_node]\n    if node\n      if request.remote?\n        raise Puppet::Error, _(\"Invalid option use_node for a remote request\")\n      else\n        return node\n      end\n    end\n\n    # We rely on our authorization system to determine whether the connected\n    # node is allowed to compile the catalog's node referenced by key.\n    # By default the REST authorization system makes sure only the connected node\n    # can compile his catalog.\n    # This allows for instance monitoring systems or puppet-load to check several\n    # node's catalog with only one certificate and a modification to auth.conf\n    # If no key is provided we can only compile the currently connected node.\n    name = request.key || request.node\n    node = find_node(name, request.environment, request.options[:transaction_uuid], request.options[:configured_environment], facts)\n    if node\n      return node\n    end\n\n    raise ArgumentError, _(\"Could not find node '%{name}'; cannot compile\") % { name: name }\n  end\n\n  # Initialize our server fact hash; we add these to each client, and they\n  # won't change while we're running, so it's safe to cache the values.\n  #\n  # See also set_server_facts in Puppet::Server::Compiler in puppetserver.\n  def set_server_facts\n    @server_facts = Puppet::Node::ServerFacts.load\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/catalog/json.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/resource/catalog'\nrequire_relative '../../../puppet/indirector/json'\n\nclass Puppet::Resource::Catalog::Json < Puppet::Indirector::JSON\n  desc \"Store catalogs as flat files, serialized using JSON.\"\n\n  def from_json(text)\n    utf8 = text.force_encoding(Encoding::UTF_8)\n\n    if utf8.valid_encoding?\n      model.convert_from(json_format, utf8)\n    else\n      Puppet.info(_(\"Unable to deserialize catalog from json, retrying with pson\"))\n      model.convert_from('pson', text.force_encoding(Encoding::BINARY))\n    end\n  end\n\n  def to_json(object)\n    object.render(json_format)\n  rescue Puppet::Network::FormatHandler::FormatError => err\n    if Puppet[:allow_pson_serialization]\n      Puppet.info(_(\"Unable to serialize catalog to json, retrying with pson. PSON is deprecated and will be removed in a future release\"))\n      Puppet.log_exception(err, err.message, level: :debug)\n      object.render('pson').force_encoding(Encoding::BINARY)\n    else\n      Puppet.info(_(\"Unable to serialize catalog to json, no other acceptable format\"))\n      Puppet.log_exception(err, err.message, level: :err)\n    end\n  end\n\n  private\n\n  def json_format\n    if Puppet[:rich_data]\n      'rich_data_json'\n    else\n      'json'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/catalog/msgpack.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/resource/catalog'\nrequire_relative '../../../puppet/indirector/msgpack'\n\nclass Puppet::Resource::Catalog::Msgpack < Puppet::Indirector::Msgpack\n  desc \"Store catalogs as flat files, serialized using MessagePack.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/catalog/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/resource/catalog'\nrequire_relative '../../../puppet/indirector/rest'\n\nclass Puppet::Resource::Catalog::Rest < Puppet::Indirector::REST\n  desc \"Find resource catalogs over HTTP via REST.\"\n\n  def find(request)\n    checksum_type = if request.options[:checksum_type]\n                      request.options[:checksum_type].split('.')\n                    else\n                      Puppet[:supported_checksum_types]\n                    end\n\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:puppet)\n\n    unless Puppet.settings[:skip_logging_catalog_request_destination]\n      ip_address = begin\n        \" (#{Resolv.getaddress(api.url.host)})\"\n      rescue Resolv::ResolvError\n        nil\n      end\n      Puppet.notice(\"Requesting catalog from #{api.url.host}:#{api.url.port}#{ip_address}\")\n    end\n\n    _, catalog = api.post_catalog(\n      request.key,\n      facts: request.options[:facts_for_catalog],\n      environment: request.environment.to_s,\n      configured_environment: request.options[:configured_environment],\n      check_environment: request.options[:check_environment],\n      transaction_uuid: request.options[:transaction_uuid],\n      job_uuid: request.options[:job_id],\n      static_catalog: request.options[:static_catalog],\n      checksum_type: checksum_type\n    )\n    catalog\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(e.response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(e.response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(e.response)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/catalog/store_configs.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/store_configs'\nrequire_relative '../../../puppet/resource/catalog'\n\nclass Puppet::Resource::Catalog::StoreConfigs < Puppet::Indirector::StoreConfigs\n  desc 'Part of the \"storeconfigs\" feature. Should not be directly set by end users.'\nend\n"
  },
  {
    "path": "lib/puppet/indirector/catalog/yaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/resource/catalog'\nrequire_relative '../../../puppet/indirector/yaml'\n\nclass Puppet::Resource::Catalog::Yaml < Puppet::Indirector::Yaml\n  desc \"Store catalogs as flat files, serialized using YAML.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/code.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\n\n# Do nothing, requiring that the back-end terminus do all\n# of the work.\nclass Puppet::Indirector::Code < Puppet::Indirector::Terminus\nend\n"
  },
  {
    "path": "lib/puppet/indirector/data_binding/hiera.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/hiera'\nrequire 'hiera/scope'\n\nclass Puppet::DataBinding::Hiera < Puppet::Indirector::Hiera\n  desc \"Retrieve data using Hiera.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/data_binding/none.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/none'\n\nclass Puppet::DataBinding::None < Puppet::Indirector::None\n  desc \"A Dummy terminus that always throws :no_such_key for data lookups.\"\n  def find(request)\n    throw :no_such_key\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/direct_file_server.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving/terminus_helper'\nrequire_relative '../../puppet/indirector/terminus'\n\nclass Puppet::Indirector::DirectFileServer < Puppet::Indirector::Terminus\n  include Puppet::FileServing::TerminusHelper\n\n  def find(request)\n    return nil unless Puppet::FileSystem.exist?(request.key)\n\n    path2instance(request, request.key)\n  end\n\n  def search(request)\n    return nil unless Puppet::FileSystem.exist?(request.key)\n\n    path2instances(request, request.key)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/envelope.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector'\n\n# Provide any attributes or functionality needed for indirected\n# instances.\nmodule Puppet::Indirector::Envelope\n  attr_accessor :expiration\n\n  def expired?\n    expiration and expiration < Time.now\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/errors.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/error'\n\nmodule Puppet::Indirector\n  class ValidationError < Puppet::Error; end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/exec.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\nrequire_relative '../../puppet/util'\n\nclass Puppet::Indirector::Exec < Puppet::Indirector::Terminus\n  # Look for external node definitions.\n  def find(request)\n    name = request.key\n    external_command = command\n\n    # Make sure it's an array\n    raise Puppet::DevError, _(\"Exec commands must be an array\") unless external_command.is_a?(Array)\n\n    # Make sure it's fully qualified.\n    raise ArgumentError, _(\"You must set the exec parameter to a fully qualified command\") unless Puppet::Util.absolute_path?(external_command[0])\n\n    # Add our name to it.\n    external_command << name\n    begin\n      output = execute(external_command, :failonfail => true, :combine => false)\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, _(\"Failed to find %{name} via exec: %{detail}\") % { name: name, detail: detail }, detail.backtrace\n    end\n\n    if output =~ /\\A\\s*\\Z/ # all whitespace\n      Puppet.debug { \"Empty response for #{name} from #{self.name} terminus\" }\n      nil\n    else\n      output\n    end\n  end\n\n  private\n\n  # Proxy the execution, so it's easier to test.\n  def execute(command, arguments)\n    Puppet::Util::Execution.execute(command, arguments)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/face.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/face'\n\nclass Puppet::Indirector::Face < Puppet::Face\n  option \"--terminus _\" + _(\"TERMINUS\") do\n    summary _(\"The indirector terminus to use.\")\n    description <<-EOT\n      Indirector faces expose indirected subsystems of Puppet. These\n      subsystems are each able to retrieve and alter a specific type of data\n      (with the familiar actions of `find`, `search`, `save`, and `destroy`)\n      from an arbitrary number of pluggable backends. In Puppet parlance,\n      these backends are called terminuses.\n\n      Almost all indirected subsystems have a `rest` terminus that interacts\n      with the puppet master's data. Most of them have additional terminuses\n      for various local data models, which are in turn used by the indirected\n      subsystem on the puppet master whenever it receives a remote request.\n\n      The terminus for an action is often determined by context, but\n      occasionally needs to be set explicitly. See the \"Notes\" section of this\n      face's manpage for more details.\n    EOT\n\n    before_action do |_action, _args, options|\n      set_terminus(options[:terminus])\n    end\n\n    after_action do |_action, _args, _options|\n      indirection.reset_terminus_class\n    end\n  end\n\n  def self.indirections\n    Puppet::Indirector::Indirection.instances.collect(&:to_s).sort\n  end\n\n  def self.terminus_classes(indirection)\n    Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect(&:to_s).sort\n  end\n\n  def call_indirection_method(method, key, options)\n    begin\n      if method == :save\n        # key is really the instance to save\n        result = indirection.__send__(method, key, nil, options)\n      else\n        result = indirection.__send__(method, key, options)\n      end\n    rescue => detail\n      message = _(\"Could not call '%{method}' on '%{indirection}': %{detail}\") % { method: method, indirection: indirection_name, detail: detail }\n      Puppet.log_exception(detail, message)\n      raise RuntimeError, message, detail.backtrace\n    end\n\n    result\n  end\n\n  action :destroy do\n    summary _(\"Delete an object.\")\n    arguments _(\"<key>\")\n    when_invoked { |key, _options| call_indirection_method :destroy, key, {} }\n  end\n\n  action :find do\n    summary _(\"Retrieve an object by name.\")\n    arguments _(\"[<key>]\")\n    when_invoked do |*args|\n      # Default the key to Puppet[:certname] if none is supplied\n      if args.length == 1\n        key = Puppet[:certname]\n      else\n        key = args.first\n      end\n      call_indirection_method :find, key, {}\n    end\n  end\n\n  action :save do\n    summary _(\"API only: create or overwrite an object.\")\n    arguments _(\"<key>\")\n    description <<-EOT\n      API only: create or overwrite an object. As the Faces framework does not\n      currently accept data from STDIN, save actions cannot currently be invoked\n      from the command line.\n    EOT\n    when_invoked { |key, _options| call_indirection_method :save, key, {} }\n  end\n\n  action :search do\n    summary _(\"Search for an object or retrieve multiple objects.\")\n    arguments _(\"<query>\")\n    when_invoked { |key, _options| call_indirection_method :search, key, {} }\n  end\n\n  # Print the configuration for the current terminus class\n  action :info do\n    summary _(\"Print the default terminus class for this face.\")\n    description <<-EOT\n      Prints the default terminus class for this subcommand. Note that different\n      run modes may have different default termini; when in doubt, specify the\n      run mode with the '--run_mode' option.\n    EOT\n\n    when_invoked do |_options|\n      if indirection.terminus_class\n        _(\"Run mode '%{mode}': %{terminus}\") % { mode: Puppet.run_mode.name, terminus: indirection.terminus_class }\n      else\n        _(\"No default terminus class for run mode '%{mode}'\") % { mode: Puppet.run_mode.name }\n      end\n    end\n  end\n\n  attr_accessor :from\n\n  def indirection_name\n    @indirection_name || name.to_sym\n  end\n\n  # Here's your opportunity to override the indirection name.  By default it\n  # will be the same name as the face.\n  def set_indirection_name(name)\n    @indirection_name = name\n  end\n\n  # Return an indirection associated with a face, if one exists;\n  # One usually does.\n  def indirection\n    unless @indirection\n      @indirection = Puppet::Indirector::Indirection.instance(indirection_name)\n      @indirection or raise _(\"Could not find terminus for %{indirection}\") % { indirection: indirection_name }\n    end\n    @indirection\n  end\n\n  def set_terminus(from)\n    indirection.terminus_class = from\n  rescue => detail\n    msg = _(\"Could not set '%{indirection}' terminus to '%{from}' (%{detail}); valid terminus types are %{types}\") % { indirection: indirection.name, from: from, detail: detail, types: self.class.terminus_classes(indirection.name).join(\", \") }\n    raise detail, msg, detail.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/fact_search.rb",
    "content": "# frozen_string_literal: true\n\n# module containing common methods used by json and yaml facts indirection terminus\nmodule Puppet::Indirector::FactSearch\n  def node_matches?(facts, options)\n    options.each do |key, value|\n      type, name, operator = key.to_s.split(\".\")\n      operator ||= 'eq'\n\n      return false unless node_matches_option?(type, name, operator, value, facts)\n    end\n    true\n  end\n\n  def node_matches_option?(type, name, operator, value, facts)\n    case type\n    when \"meta\"\n      case name\n      when \"timestamp\"\n        compare_timestamp(operator, facts.timestamp, Time.parse(value))\n      end\n    when \"facts\"\n      compare_facts(operator, facts.values[name], value)\n    end\n  end\n\n  def compare_facts(operator, value1, value2)\n    return false unless value1\n\n    case operator\n    when \"eq\"\n      value1.to_s == value2.to_s\n    when \"le\"\n      value1.to_f <= value2.to_f\n    when \"ge\"\n      value1.to_f >= value2.to_f\n    when \"lt\"\n      value1.to_f < value2.to_f\n    when \"gt\"\n      value1.to_f > value2.to_f\n    when \"ne\"\n      value1.to_s != value2.to_s\n    end\n  end\n\n  def compare_timestamp(operator, value1, value2)\n    case operator\n    when \"eq\"\n      value1 == value2\n    when \"le\"\n      value1 <= value2\n    when \"ge\"\n      value1 >= value2\n    when \"lt\"\n      value1 < value2\n    when \"gt\"\n      value1 > value2\n    when \"ne\"\n      value1 != value2\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/facter.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/code'\n\nclass Puppet::Node::Facts::Facter < Puppet::Indirector::Code\n  desc \"Retrieve facts from Facter.  This provides a somewhat abstract interface\n    between Puppet and Facter.  It's only `somewhat` abstract because it always\n    returns the local host's facts, regardless of what you attempt to find.\"\n\n  def allow_remote_requests?\n    false\n  end\n\n  def destroy(facts)\n    raise Puppet::DevError, _('You cannot destroy facts in the code store; it is only used for getting facts from Facter')\n  end\n\n  def save(facts)\n    raise Puppet::DevError, _('You cannot save facts to the code store; it is only used for getting facts from Facter')\n  end\n\n  # Lookup a host's facts up in Facter.\n  def find(request)\n    Puppet.runtime[:facter].reset\n\n    # Note: we need to setup puppet's external search paths before adding the puppetversion\n    # fact. This is because in Facter 2.x, the first `Puppet.runtime[:facter].add` causes Facter to create\n    # its directory loaders which cannot be changed, meaning other external facts won't\n    # be resolved. (PUP-4607)\n    self.class.setup_external_search_paths(request)\n    self.class.setup_search_paths(request)\n\n    # Initialize core Puppet facts, such as puppetversion\n    Puppet.initialize_facts\n\n    result = if request.options[:resolve_options]\n               raise(Puppet::Error, _(\"puppet facts show requires version 4.0.40 or greater of Facter.\")) unless Facter.respond_to?(:resolve)\n\n               find_with_options(request)\n             elsif Puppet[:include_legacy_facts]\n               # to_hash returns both structured and legacy facts\n               Puppet::Node::Facts.new(request.key, Puppet.runtime[:facter].to_hash)\n             else\n               # resolve does not return legacy facts unless requested\n               facts = Puppet.runtime[:facter].resolve('')\n               # some versions of Facter 4 return a Facter::FactCollection instead of\n               # a Hash, breaking API compatibility, so force a hash using `to_h`\n               Puppet::Node::Facts.new(request.key, facts.to_h)\n             end\n\n    result.add_local_facts unless request.options[:resolve_options]\n    result.sanitize\n    result\n  end\n\n  def self.setup_search_paths(request)\n    # Add any per-module fact directories to facter's search path\n    dirs = request.environment.modulepath.collect do |dir|\n      %w[lib plugins].map do |subdirectory|\n        Dir.glob(\"#{dir}/*/#{subdirectory}/facter\")\n      end\n    end.flatten + Puppet[:factpath].split(File::PATH_SEPARATOR)\n\n    dirs = dirs.select do |dir|\n      next false unless FileTest.directory?(dir)\n\n      # Even through we no longer directly load facts in the terminus,\n      # print out each .rb in the facts directory as module\n      # developers may find that information useful for debugging purposes\n      if Puppet::Util::Log.sendlevel?(:info)\n        Puppet.info _(\"Loading facts\")\n        Dir.glob(\"#{dir}/*.rb\").each do |file|\n          Puppet.debug { \"Loading facts from #{file}\" }\n        end\n      end\n\n      true\n    end\n    dirs << request.options[:custom_dir] if request.options[:custom_dir]\n    Puppet.runtime[:facter].search(*dirs)\n  end\n\n  def self.setup_external_search_paths(request)\n    # Add any per-module external fact directories to facter's external search path\n    dirs = []\n    request.environment.modules.each do |m|\n      next unless m.has_external_facts?\n\n      dir = m.plugin_fact_directory\n      Puppet.debug { \"Loading external facts from #{dir}\" }\n      dirs << dir\n    end\n\n    # Add system external fact directory if it exists\n    if FileTest.directory?(Puppet[:pluginfactdest])\n      dir = Puppet[:pluginfactdest]\n      Puppet.debug { \"Loading external facts from #{dir}\" }\n      dirs << dir\n    end\n\n    dirs << request.options[:external_dir] if request.options[:external_dir]\n    Puppet.runtime[:facter].search_external dirs\n  end\n\n  private\n\n  def find_with_options(request)\n    options = request.options\n    options_for_facter = ''.dup\n    options_for_facter += options[:user_query].join(' ')\n    options_for_facter += \" --config #{options[:config_file]}\" if options[:config_file]\n    options_for_facter += \" --show-legacy\" if options[:show_legacy]\n    options_for_facter += \" --no-block\" if options[:no_block] == false\n    options_for_facter += \" --no-cache\" if options[:no_cache] == false\n    options_for_facter += \" --timing\" if options[:timing]\n\n    Puppet::Node::Facts.new(request.key, Puppet.runtime[:facter].resolve(options_for_facter))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/json.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/json'\nrequire_relative '../../../puppet/indirector/fact_search'\n\nclass Puppet::Node::Facts::Json < Puppet::Indirector::JSON\n  desc \"Store client facts as flat files, serialized using JSON, or\n    return deserialized facts from disk.\"\n\n  include Puppet::Indirector::FactSearch\n\n  def search(request)\n    node_names = []\n    Dir.glob(json_dir_path).each do |file|\n      facts = load_json_from_file(file, '')\n      if facts && node_matches?(facts, request.options)\n        node_names << facts.name\n      end\n    end\n    node_names\n  end\n\n  private\n\n  def json_dir_path\n    path(\"*\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/memory.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/memory'\n\nclass Puppet::Node::Facts::Memory < Puppet::Indirector::Memory\n  desc \"Keep track of facts in memory but nowhere else.  This is used for\n    one-time compiles, such as what the stand-alone `puppet` does.\n    To use this terminus, you must load it with the data you want it\n    to contain.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/network_device.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/code'\n\nclass Puppet::Node::Facts::NetworkDevice < Puppet::Indirector::Code\n  desc \"Retrieve facts from a network device.\"\n\n  def allow_remote_requests?\n    false\n  end\n\n  # Look a device's facts up through the current device.\n  def find(request)\n    result = Puppet::Node::Facts.new(request.key, Puppet::Util::NetworkDevice.current.facts)\n\n    result.add_local_facts\n    result.sanitize\n    result\n  end\n\n  def destroy(facts)\n    raise Puppet::DevError, _(\"You cannot destroy facts in the code store; it is only used for getting facts from a remote device\")\n  end\n\n  def save(facts)\n    raise Puppet::DevError, _(\"You cannot save facts to the code store; it is only used for getting facts from a remote device\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/rest'\n\nclass Puppet::Node::Facts::Rest < Puppet::Indirector::REST\n  desc \"Find and save facts about nodes over HTTP via REST.\"\n\n  def find(request)\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:puppet)\n    _, facts = api.get_facts(\n      request.key,\n      environment: request.environment.to_s\n    )\n    facts\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(e.response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(e.response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(e.response)\n    end\n  end\n\n  def save(request)\n    raise ArgumentError, _(\"PUT does not accept options\") unless request.options.empty?\n\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:puppet)\n    api.put_facts(\n      request.key,\n      facts: request.instance,\n      environment: request.environment.to_s\n    )\n\n    # preserve existing behavior\n    nil\n  rescue Puppet::HTTP::ResponseError => e\n    # always raise even if fail_on_404 is false\n    raise convert_to_http_error(e.response)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/store_configs.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/store_configs'\n\nclass Puppet::Node::Facts::StoreConfigs < Puppet::Indirector::StoreConfigs\n  desc 'Part of the \"storeconfigs\" feature. Should not be directly set by end users.'\n\n  def allow_remote_requests?\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/facts/yaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node/facts'\nrequire_relative '../../../puppet/indirector/yaml'\nrequire_relative '../../../puppet/indirector/fact_search'\n\nclass Puppet::Node::Facts::Yaml < Puppet::Indirector::Yaml\n  desc \"Store client facts as flat files, serialized using YAML, or\n    return deserialized facts from disk.\"\n\n  include Puppet::Indirector::FactSearch\n\n  def search(request)\n    node_names = []\n    Dir.glob(yaml_dir_path).each do |file|\n      facts = load_file(file)\n      if facts && node_matches?(facts, request.options)\n        node_names << facts.name\n      end\n    end\n    node_names\n  end\n\n  private\n\n  # Return the path to a given node's file.\n  def yaml_dir_path\n    base = Puppet.run_mode.server? ? Puppet[:yamldir] : Puppet[:clientyamldir]\n    File.join(base, 'facts', '*.yaml')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_bucket_file/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/code'\nrequire_relative '../../../puppet/file_bucket/file'\nrequire_relative '../../../puppet/util/checksums'\nrequire_relative '../../../puppet/util/diff'\nrequire 'fileutils'\n\nmodule Puppet::FileBucketFile\n  class File < Puppet::Indirector::Code\n    include Puppet::Util::Checksums\n    include Puppet::Util::Diff\n\n    desc \"Store files in a directory set based on their checksums.\"\n\n    def find(request)\n      request.options[:bucket_path] ||= Puppet[:bucketdir]\n      # If filebucket mode is 'list'\n      if request.options[:list_all]\n        return nil unless ::File.exist?(request.options[:bucket_path])\n\n        return list(request)\n      end\n      checksum, files_original_path = request_to_checksum_and_path(request)\n      contents_file = path_for(request.options[:bucket_path], checksum, 'contents')\n      paths_file = path_for(request.options[:bucket_path], checksum, 'paths')\n\n      if Puppet::FileSystem.exist?(contents_file) && matches(paths_file, files_original_path)\n        if request.options[:diff_with]\n          other_contents_file = path_for(request.options[:bucket_path], request.options[:diff_with], 'contents')\n          raise _(\"could not find diff_with %{diff}\") % { diff: request.options[:diff_with] } unless Puppet::FileSystem.exist?(other_contents_file)\n          raise _(\"Unable to diff on this platform\") unless Puppet[:diff] != \"\"\n\n          diff(Puppet::FileSystem.path_string(contents_file), Puppet::FileSystem.path_string(other_contents_file))\n        else\n          # TRANSLATORS \"FileBucket\" should not be translated\n          Puppet.info _(\"FileBucket read %{checksum}\") % { checksum: checksum }\n          model.new(Puppet::FileSystem.binread(contents_file))\n        end\n      else\n        nil\n      end\n    end\n\n    def list(request)\n      if request.remote?\n        raise Puppet::Error, _(\"Listing remote file buckets is not allowed\")\n      end\n\n      fromdate = request.options[:fromdate] || \"0:0:0 1-1-1970\"\n      todate = request.options[:todate] || Time.now.strftime(\"%F %T\")\n      begin\n        to = Time.parse(todate)\n      rescue ArgumentError\n        raise Puppet::Error, _(\"Error while parsing 'todate'\")\n      end\n      begin\n        from = Time.parse(fromdate)\n      rescue ArgumentError\n        raise Puppet::Error, _(\"Error while parsing 'fromdate'\")\n      end\n      # Setting hash's default value to [], needed by the following loop\n      bucket = Hash.new { [] }\n      msg = ''.dup\n      # Get all files with mtime between 'from' and 'to'\n      Pathname.new(request.options[:bucket_path]).find { |item|\n        next unless item.file? and item.basename.to_s == \"paths\"\n\n        filenames = item.read.strip.split(\"\\n\")\n        filestat = Time.parse(item.stat.mtime.to_s)\n        next unless from <= filestat and filestat <= to\n\n        filenames.each do |filename|\n          bucket[filename] += [[item.stat.mtime, item.parent.basename]]\n        end\n      }\n      # Sort the results\n      bucket.each { |_filename, contents|\n        contents.sort_by! do |item|\n          # NOTE: Ruby 2.4 may reshuffle item order even if the keys in sequence are sorted already\n          item[0]\n        end\n      }\n      # Build the output message. Sorted by names then by dates\n      bucket.sort.each { |filename, contents|\n        contents.each { |mtime, chksum|\n          date = mtime.strftime(\"%F %T\")\n          msg += \"#{chksum} #{date} #{filename}\\n\"\n        }\n      }\n      model.new(msg)\n    end\n\n    def head(request)\n      checksum, files_original_path = request_to_checksum_and_path(request)\n      contents_file = path_for(request.options[:bucket_path], checksum, 'contents')\n      paths_file = path_for(request.options[:bucket_path], checksum, 'paths')\n\n      Puppet::FileSystem.exist?(contents_file) && matches(paths_file, files_original_path)\n    end\n\n    def save(request)\n      instance = request.instance\n      _, files_original_path = request_to_checksum_and_path(request)\n      contents_file = path_for(instance.bucket_path, instance.checksum_data, 'contents')\n      paths_file = path_for(instance.bucket_path, instance.checksum_data, 'paths')\n\n      save_to_disk(instance, files_original_path, contents_file, paths_file)\n\n      # don't echo the request content back to the agent\n      model.new('')\n    end\n\n    def validate_key(request)\n      # There are no ACLs on filebucket files so validating key is not important\n    end\n\n    private\n\n    # @param paths_file [Object] Opaque file path\n    # @param files_original_path [String]\n    #\n    def matches(paths_file, files_original_path)\n      # Puppet will have already written the paths_file in the systems encoding\n      # given its possible that request.options[:bucket_path] or Puppet[:bucketdir]\n      # contained characters in an encoding that are not represented the\n      # same way when the bytes are decoded as UTF-8, continue using system encoding\n      Puppet::FileSystem.open(paths_file, 0o640, 'a+:external') do |f|\n        path_match(f, files_original_path)\n      end\n    end\n\n    def path_match(file_handle, files_original_path)\n      return true unless files_original_path # if no path was provided, it's a match\n\n      file_handle.rewind\n      file_handle.each_line do |line|\n        return true if line.chomp == files_original_path\n      end\n      false\n    end\n\n    # @param bucket_file [Puppet::FileBucket::File] IO object representing\n    #   content to back up\n    # @param files_original_path [String] Path to original source file on disk\n    # @param contents_file [Pathname] Opaque file path to intended backup\n    #   location\n    # @param paths_file [Pathname] Opaque file path to file containing source\n    #   file paths on disk\n    # @return [void]\n    # @raise [Puppet::FileBucket::BucketError] on possible sum collision between\n    #   existing and new backup\n    # @api private\n    def save_to_disk(bucket_file, files_original_path, contents_file, paths_file)\n      Puppet::Util.withumask(0o007) do\n        unless Puppet::FileSystem.dir_exist?(paths_file)\n          Puppet::FileSystem.dir_mkpath(paths_file)\n        end\n\n        # Puppet will have already written the paths_file in the systems encoding\n        # given its possible that request.options[:bucket_path] or Puppet[:bucketdir]\n        # contained characters in an encoding that are not represented the\n        # same way when the bytes are decoded as UTF-8, continue using system encoding\n        Puppet::FileSystem.exclusive_open(paths_file, 0o640, 'a+:external') do |f|\n          if Puppet::FileSystem.exist?(contents_file)\n            if verify_identical_file(contents_file, bucket_file)\n              # TRANSLATORS \"FileBucket\" should not be translated\n              Puppet.info _(\"FileBucket got a duplicate file %{file_checksum}\") % { file_checksum: bucket_file.checksum }\n              # Don't touch the contents file on Windows, since we can't update the\n              # mtime of read-only files there.\n              unless Puppet::Util::Platform.windows?\n                Puppet::FileSystem.touch(contents_file)\n              end\n            elsif contents_file_matches_checksum?(contents_file, bucket_file.checksum_data, bucket_file.checksum_type)\n              # If the contents or sizes don't match, but the checksum does,\n              # then we've found a conflict (potential hash collision).\n              # Unlikely, but quite bad. Don't remove the file in case it's\n              # needed, but ask the user to validate.\n              # Note: Don't print the full path to the bucket file in the\n              # exception to avoid disclosing file system layout on server.\n              # TRANSLATORS \"FileBucket\" should not be translated\n              Puppet.err(_(\"Unable to verify existing FileBucket backup at '%{path}'.\") % { path: contents_file.to_path })\n              raise Puppet::FileBucket::BucketError, _(\"Existing backup and new file have different content but same checksum, %{value}. Verify existing backup and remove if incorrect.\") %\n                                                     { value: bucket_file.checksum }\n            else\n              # PUP-1334 If the contents_file exists but does not match its\n              # checksum, our backup has been corrupted. Warn about overwriting\n              # it, and proceed with new backup.\n              Puppet.warning(_(\"Existing backup does not match its expected sum, %{sum}. Overwriting corrupted backup.\") % { sum: bucket_file.checksum })\n              copy_bucket_file_to_contents_file(contents_file, bucket_file)\n            end\n          else\n            copy_bucket_file_to_contents_file(contents_file, bucket_file)\n          end\n\n          unless path_match(f, files_original_path)\n            f.seek(0, IO::SEEK_END)\n            f.puts(files_original_path)\n          end\n        end\n      end\n    end\n\n    def request_to_checksum_and_path(request)\n      checksum_type, checksum, path = request.key.split(%r{/}, 3)\n      if path == '' # Treat \"md5/<checksum>/\" like \"md5/<checksum>\"\n        path = nil\n      end\n      raise ArgumentError, _(\"Unsupported checksum type %{checksum_type}\") % { checksum_type: checksum_type.inspect } if checksum_type != Puppet[:digest_algorithm]\n\n      expected = method(checksum_type + \"_hex_length\").call\n      raise _(\"Invalid checksum %{checksum}\") % { checksum: checksum.inspect } if checksum !~ /^[0-9a-f]{#{expected}}$/\n\n      [checksum, path]\n    end\n\n    # @return [Object] Opaque path as constructed by the Puppet::FileSystem\n    #\n    def path_for(bucket_path, digest, subfile = nil)\n      bucket_path ||= Puppet[:bucketdir]\n\n      dir     = ::File.join(digest[0..7].split(\"\"))\n      basedir = ::File.join(bucket_path, dir, digest)\n\n      Puppet::FileSystem.pathname(subfile ? ::File.join(basedir, subfile) : basedir)\n    end\n\n    # @param contents_file [Pathname] Opaque file path to intended backup\n    #   location\n    # @param bucket_file [Puppet::FileBucket::File] IO object representing\n    #   content to back up\n    # @return [Boolean] whether the data in contents_file is of the same size\n    #   and content as that in the bucket_file\n    # @api private\n    def verify_identical_file(contents_file, bucket_file)\n      (bucket_file.to_binary.bytesize == Puppet::FileSystem.size(contents_file)) &&\n        (bucket_file.stream() { |s| Puppet::FileSystem.compare_stream(contents_file, s) })\n    end\n\n    # @param contents_file [Pathname] Opaque file path to intended backup\n    #   location\n    # @param expected_checksum_data [String] expected value of checksum of type\n    #   checksum_type\n    # @param checksum_type [String] type of check sum of checksum_data, ie \"md5\"\n    # @return [Boolean] whether the checksum of the contents_file matches the\n    #   supplied checksum\n    # @api private\n    def contents_file_matches_checksum?(contents_file, expected_checksum_data, checksum_type)\n      contents_file_checksum_data = Puppet::Util::Checksums.method(:\"#{checksum_type}_file\").call(contents_file.to_path)\n      contents_file_checksum_data == expected_checksum_data\n    end\n\n    # @param contents_file [Pathname] Opaque file path to intended backup\n    #   location\n    # @param bucket_file [Puppet::FileBucket::File] IO object representing\n    #   content to back up\n    # @return [void]\n    # @api private\n    def copy_bucket_file_to_contents_file(contents_file, bucket_file)\n      Puppet::FileSystem.replace_file(contents_file, 0o440) do |of|\n        # PUP-1044 writes all of the contents\n        bucket_file.stream() do |src|\n          FileUtils.copy_stream(src, of)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_bucket_file/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/rest'\nrequire_relative '../../../puppet/file_bucket/file'\n\nmodule Puppet::FileBucketFile\n  class Rest < Puppet::Indirector::REST\n    desc \"This is a REST based mechanism to send/retrieve file to/from the filebucket\"\n\n    def head(request)\n      session = Puppet.lookup(:http_session)\n      api = session.route_to(:puppet)\n      api.head_filebucket_file(\n        request.key,\n        environment: request.environment.to_s,\n        bucket_path: request.options[:bucket_path]\n      )\n    rescue Puppet::HTTP::ResponseError => e\n      return nil if e.response.code == 404\n\n      raise convert_to_http_error(e.response)\n    end\n\n    def find(request)\n      session = Puppet.lookup(:http_session)\n      api = session.route_to(:puppet)\n      _, filebucket_file = api.get_filebucket_file(\n        request.key,\n        environment: request.environment.to_s,\n        bucket_path: request.options[:bucket_path],\n        diff_with: request.options[:diff_with],\n        list_all: request.options[:list_all],\n        fromdate: request.options[:fromdate],\n        todate: request.options[:todate]\n      )\n      filebucket_file\n    rescue Puppet::HTTP::ResponseError => e\n      raise convert_to_http_error(e.response)\n    end\n\n    def save(request)\n      session = Puppet.lookup(:http_session)\n      api = session.route_to(:puppet)\n      api.put_filebucket_file(\n        request.key,\n        body: request.instance.render,\n        environment: request.environment.to_s\n      )\n    rescue Puppet::HTTP::ResponseError => e\n      raise convert_to_http_error(e.response)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_bucket_file/selector.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/code'\n\nmodule Puppet::FileBucketFile\n  class Selector < Puppet::Indirector::Code\n    desc \"Select the terminus based on the request\"\n\n    def select(request)\n      if request.protocol == 'https'\n        :rest\n      else\n        :file\n      end\n    end\n\n    def get_terminus(request)\n      indirection.terminus(select(request))\n    end\n\n    def head(request)\n      get_terminus(request).head(request)\n    end\n\n    def find(request)\n      get_terminus(request).find(request)\n    end\n\n    def save(request)\n      get_terminus(request).save(request)\n    end\n\n    def search(request)\n      get_terminus(request).search(request)\n    end\n\n    def destroy(request)\n      get_terminus(request).destroy(request)\n    end\n\n    def authorized?(request)\n      terminus = get_terminus(request)\n      if terminus.respond_to?(:authorized?)\n        terminus.authorized?(request)\n      else\n        true\n      end\n    end\n\n    def validate_key(request)\n      get_terminus(request).validate(request)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_content/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/content'\nrequire_relative '../../../puppet/indirector/file_content'\nrequire_relative '../../../puppet/indirector/direct_file_server'\n\nclass Puppet::Indirector::FileContent::File < Puppet::Indirector::DirectFileServer\n  desc \"Retrieve file contents from disk.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_content/file_server.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/content'\nrequire_relative '../../../puppet/indirector/file_content'\nrequire_relative '../../../puppet/indirector/file_server'\n\nclass Puppet::Indirector::FileContent::FileServer < Puppet::Indirector::FileServer\n  desc \"Retrieve file contents using Puppet's fileserver.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_content/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/content'\nrequire_relative '../../../puppet/indirector/file_content'\nrequire_relative '../../../puppet/indirector/rest'\n\nclass Puppet::Indirector::FileContent::Rest < Puppet::Indirector::REST\n  desc \"Retrieve file contents via a REST HTTP interface.\"\n\n  def find(request)\n    content = StringIO.new\n    content.binmode\n\n    url = URI.parse(Puppet::Util.uri_encode(request.uri))\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:fileserver, url: url)\n\n    api.get_file_content(\n      path: Puppet::Util.uri_unescape(url.path),\n      environment: request.environment.to_s\n    ) do |data|\n      content << data\n    end\n\n    Puppet::FileServing::Content.from_binary(content.string)\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(e.response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(e.response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(e.response)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_content/selector.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/content'\nrequire_relative '../../../puppet/indirector/file_content'\nrequire_relative '../../../puppet/indirector/code'\nrequire_relative '../../../puppet/file_serving/terminus_selector'\n\nclass Puppet::Indirector::FileContent::Selector < Puppet::Indirector::Code\n  desc \"Select the terminus based on the request\"\n  include Puppet::FileServing::TerminusSelector\n\n  def get_terminus(request)\n    indirection.terminus(select(request))\n  end\n\n  def find(request)\n    get_terminus(request).find(request)\n  end\n\n  def search(request)\n    get_terminus(request).search(request)\n  end\n\n  def authorized?(request)\n    terminus = get_terminus(request)\n    if terminus.respond_to?(:authorized?)\n      terminus.authorized?(request)\n    else\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_content.rb",
    "content": "# frozen_string_literal: true\n\n# A stub class, so our constants work.\nclass Puppet::Indirector::FileContent # :nodoc:\nend\n\nrequire_relative '../../puppet/file_serving/content'\n"
  },
  {
    "path": "lib/puppet/indirector/file_metadata/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/metadata'\nrequire_relative '../../../puppet/indirector/file_metadata'\nrequire_relative '../../../puppet/indirector/direct_file_server'\n\nclass Puppet::Indirector::FileMetadata::File < Puppet::Indirector::DirectFileServer\n  desc \"Retrieve file metadata directly from the local filesystem.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_metadata/file_server.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/metadata'\nrequire_relative '../../../puppet/indirector/file_metadata'\nrequire_relative '../../../puppet/indirector/file_server'\n\nclass Puppet::Indirector::FileMetadata::FileServer < Puppet::Indirector::FileServer\n  desc \"Retrieve file metadata using Puppet's fileserver.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_metadata/http.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/http_metadata'\nrequire_relative '../../../puppet/indirector/generic_http'\nrequire_relative '../../../puppet/indirector/file_metadata'\nrequire 'net/http'\n\nclass Puppet::Indirector::FileMetadata::Http < Puppet::Indirector::GenericHttp\n  desc \"Retrieve file metadata from a remote HTTP server.\"\n\n  include Puppet::FileServing::TerminusHelper\n\n  def find(request)\n    checksum_type = request.options[:checksum_type]\n    # See URL encoding comment in Puppet::Type::File::ParamSource#chunk_file_from_source\n    uri = URI(request.uri)\n    client = Puppet.runtime[:http]\n    head = client.head(uri, options: { include_system_store: true })\n\n    return create_httpmetadata(head, checksum_type) if head.success?\n\n    case head.code\n    when 403, 405\n      # AMZ presigned URL and puppetserver may return 403\n      # instead of 405. Fallback to partial get\n      get = partial_get(client, uri)\n      return create_httpmetadata(get, checksum_type) if get.success?\n    end\n\n    nil\n  end\n\n  def search(request)\n    raise Puppet::Error, _(\"cannot lookup multiple files\")\n  end\n\n  private\n\n  def partial_get(client, uri)\n    client.get(uri, headers: { 'Range' => 'bytes=0-0' }, options: { include_system_store: true })\n  end\n\n  def create_httpmetadata(http_request, checksum_type)\n    metadata = Puppet::FileServing::HttpMetadata.new(http_request)\n    metadata.checksum_type = checksum_type if checksum_type\n    metadata.collect\n    metadata\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_metadata/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/metadata'\nrequire_relative '../../../puppet/indirector/file_metadata'\nrequire_relative '../../../puppet/indirector/rest'\n\nclass Puppet::Indirector::FileMetadata::Rest < Puppet::Indirector::REST\n  desc \"Retrieve file metadata via a REST HTTP interface.\"\n\n  def find(request)\n    url = URI.parse(Puppet::Util.uri_encode(request.uri))\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:fileserver, url: url)\n\n    _, file_metadata = api.get_file_metadata(\n      path: Puppet::Util.uri_unescape(url.path),\n      environment: request.environment.to_s,\n      links: request.options[:links],\n      checksum_type: request.options[:checksum_type],\n      source_permissions: request.options[:source_permissions]\n    )\n    file_metadata\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(e.response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(e.response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(e.response)\n    end\n  end\n\n  def search(request)\n    url = URI.parse(Puppet::Util.uri_encode(request.uri))\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:fileserver, url: url)\n\n    _, file_metadatas = api.get_file_metadatas(\n      path: Puppet::Util.uri_unescape(url.path),\n      environment: request.environment.to_s,\n      recurse: request.options[:recurse],\n      recurselimit: request.options[:recurselimit],\n      max_files: request.options[:max_files],\n      ignore: request.options[:ignore],\n      links: request.options[:links],\n      checksum_type: request.options[:checksum_type],\n      source_permissions: request.options[:source_permissions]\n    )\n    file_metadatas\n  rescue Puppet::HTTP::ResponseError => e\n    # since it's search, return empty array instead of nil\n    return [] if e.response.code == 404\n\n    raise convert_to_http_error(e.response)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_metadata/selector.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/metadata'\nrequire_relative '../../../puppet/indirector/file_metadata'\nrequire_relative '../../../puppet/indirector/code'\nrequire_relative '../../../puppet/file_serving/terminus_selector'\n\nclass Puppet::Indirector::FileMetadata::Selector < Puppet::Indirector::Code\n  desc \"Select the terminus based on the request\"\n  include Puppet::FileServing::TerminusSelector\n\n  def get_terminus(request)\n    indirection.terminus(select(request))\n  end\n\n  def find(request)\n    get_terminus(request).find(request)\n  end\n\n  def search(request)\n    get_terminus(request).search(request)\n  end\n\n  def authorized?(request)\n    terminus = get_terminus(request)\n    if terminus.respond_to?(:authorized?)\n      terminus.authorized?(request)\n    else\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/file_metadata.rb",
    "content": "# frozen_string_literal: true\n\n# A stub class, so our constants work.\nclass Puppet::Indirector::FileMetadata # :nodoc:\nend\n\nrequire_relative '../../puppet/file_serving/metadata'\n"
  },
  {
    "path": "lib/puppet/indirector/file_server.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving/configuration'\nrequire_relative '../../puppet/file_serving/fileset'\nrequire_relative '../../puppet/file_serving/terminus_helper'\nrequire_relative '../../puppet/indirector/terminus'\n\n# Look files up using the file server.\nclass Puppet::Indirector::FileServer < Puppet::Indirector::Terminus\n  include Puppet::FileServing::TerminusHelper\n\n  # Is the client authorized to perform this action?\n  def authorized?(request)\n    return false unless [:find, :search].include?(request.method)\n\n    mount, _ = configuration.split_path(request)\n\n    # If we're not serving this mount, then access is denied.\n    return false unless mount\n\n    true\n  end\n\n  # Find our key using the fileserver.\n  def find(request)\n    mount, relative_path = configuration.split_path(request)\n\n    return nil unless mount\n\n    # The mount checks to see if the file exists, and returns nil\n    # if not.\n    path = mount.find(relative_path, request)\n    return nil unless path\n\n    path2instance(request, path)\n  end\n\n  # Search for files.  This returns an array rather than a single\n  # file.\n  def search(request)\n    mount, relative_path = configuration.split_path(request)\n\n    paths = mount.search(relative_path, request) if mount\n    unless paths\n      Puppet.info _(\"Could not find filesystem info for file '%{request}' in environment %{env}\") % { request: request.key, env: request.environment }\n      return nil\n    end\n    path2instances(request, *paths)\n  end\n\n  private\n\n  # Our fileserver configuration, if needed.\n  def configuration\n    Puppet::FileServing::Configuration.configuration\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/generic_http.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/file_serving/terminus_helper'\n\nclass Puppet::Indirector::GenericHttp < Puppet::Indirector::Terminus\n  desc \"Retrieve data from a remote HTTP server.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/hiera.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\nrequire 'hiera/scope'\n\n# This class can't be collapsed into Puppet::Indirector::DataBindings::Hiera\n# because some community plugins rely on this class directly, see PUP-1843.\n# This class is deprecated and will be deleted in a future release.\n# Use `Puppet::DataBinding.indirection.terminus(:hiera)` instead.\nclass Puppet::Indirector::Hiera < Puppet::Indirector::Terminus\n  def initialize(*args)\n    unless Puppet.features.hiera?\n      # TRANSLATORS \"Hiera\" is the name of a code library and should not be translated\n      raise _(\"Hiera terminus not supported without hiera library\")\n    end\n\n    super\n  end\n\n  if defined?(::Psych::SyntaxError)\n    DataBindingExceptions = [::StandardError, ::Psych::SyntaxError]\n  else\n    DataBindingExceptions = [::StandardError]\n  end\n\n  def find(request)\n    not_found = Object.new\n    options = request.options\n    Puppet.debug { \"Performing a hiera indirector lookup of #{request.key} with options #{options.inspect}\" }\n    value = hiera.lookup(request.key, not_found, Hiera::Scope.new(options[:variables]), nil, convert_merge(options[:merge]))\n    throw :no_such_key if value.equal?(not_found)\n    value\n  rescue *DataBindingExceptions => detail\n    error = Puppet::DataBinding::LookupError.new(\"DataBinding 'hiera': #{detail.message}\")\n    error.set_backtrace(detail.backtrace)\n    raise error\n  end\n\n  private\n\n  # Converts a lookup 'merge' parameter argument into a Hiera 'resolution_type' argument.\n  #\n  # @param merge [String,Hash,nil] The lookup 'merge' argument\n  # @return [Symbol,Hash,nil] The Hiera 'resolution_type'\n  def convert_merge(merge)\n    case merge\n    when nil, 'first'\n      # Nil is OK. Defaults to Hiera :priority\n      nil\n    when Puppet::Pops::MergeStrategy\n      convert_merge(merge.configuration)\n    when 'unique'\n      # Equivalent to Hiera :array\n      :array\n    when 'hash'\n      # Equivalent to Hiera :hash with default :native merge behavior. A Hash must be passed here\n      # to override possible Hiera deep merge config settings.\n      { :behavior => :native }\n    when 'deep'\n      # Equivalent to Hiera :hash with :deeper merge behavior.\n      { :behavior => :deeper }\n    when Hash\n      strategy = merge['strategy']\n      if strategy == 'deep'\n        result = { :behavior => :deeper }\n        # Remaining entries must have symbolic keys\n        merge.each_pair { |k, v| result[k.to_sym] = v unless k == 'strategy' }\n        result\n      else\n        convert_merge(strategy)\n      end\n    else\n      # TRANSLATORS \"merge\" is a parameter name and should not be translated\n      raise Puppet::DataBinding::LookupError, _(\"Unrecognized value for request 'merge' parameter: '%{merge}'\") % { merge: merge }\n    end\n  end\n\n  public\n\n  def self.hiera_config\n    hiera_config = Puppet.settings[:hiera_config]\n    config = {}\n\n    if Puppet::FileSystem.exist?(hiera_config)\n      config = Hiera::Config.load(hiera_config)\n    else\n      Puppet.warning _(\"Config file %{hiera_config} not found, using Hiera defaults\") % { hiera_config: hiera_config }\n    end\n\n    config[:logger] = 'puppet'\n    config\n  end\n\n  def self.hiera\n    @hiera ||= Hiera.new(:config => hiera_config)\n  end\n\n  def hiera\n    self.class.hiera\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/indirection.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/docs'\nrequire_relative '../../puppet/util/profiler'\nrequire_relative '../../puppet/indirector/envelope'\nrequire_relative '../../puppet/indirector/request'\nrequire_relative '../../puppet/thread_local'\n\n# The class that connects functional classes with their different collection\n# back-ends.  Each indirection has a set of associated terminus classes,\n# each of which is a subclass of Puppet::Indirector::Terminus.\nclass Puppet::Indirector::Indirection\n  include Puppet::Util::Docs\n\n  attr_accessor :name, :model\n  attr_reader :termini\n\n  @@indirections = []\n\n  # Find an indirection by name.  This is provided so that Terminus classes\n  # can specifically hook up with the indirections they are associated with.\n  def self.instance(name)\n    @@indirections.find { |i| i.name == name }\n  end\n\n  # Return a list of all known indirections.  Used to generate the\n  # reference.\n  def self.instances\n    @@indirections.collect(&:name)\n  end\n\n  # Find an indirected model by name.  This is provided so that Terminus classes\n  # can specifically hook up with the indirections they are associated with.\n  def self.model(name)\n    match = @@indirections.find { |i| i.name == name }\n    return nil unless match\n\n    match.model\n  end\n\n  # Create and return our cache terminus.\n  def cache\n    raise Puppet::DevError, _(\"Tried to cache when no cache class was set\") unless cache_class\n\n    terminus(cache_class)\n  end\n\n  # Should we use a cache?\n  def cache?\n    cache_class ? true : false\n  end\n\n  def cache_class\n    @cache_class.value\n  end\n\n  # Define a terminus class to be used for caching.\n  def cache_class=(class_name)\n    validate_terminus_class(class_name) if class_name\n    @cache_class.value = class_name\n  end\n\n  # This is only used for testing.\n  def delete\n    @@indirections.delete(self) if @@indirections.include?(self)\n  end\n\n  # Set the time-to-live for instances created through this indirection.\n  def ttl=(value)\n    # TRANSLATORS \"TTL\" stands for \"time to live\" and refers to a duration of time\n    raise ArgumentError, _(\"Indirection TTL must be an integer\") unless value.is_a?(Integer)\n\n    @ttl = value\n  end\n\n  # Default to the runinterval for the ttl.\n  def ttl\n    @ttl ||= Puppet[:runinterval]\n  end\n\n  # Calculate the expiration date for a returned instance.\n  def expiration\n    Time.now + ttl\n  end\n\n  # Generate the full doc string.\n  def doc\n    text = ''.dup\n\n    text << scrub(@doc) << \"\\n\\n\" if @doc\n\n    text << \"* **Indirected Class**: `#{@indirected_class}`\\n\";\n    if terminus_setting\n      text << \"* **Terminus Setting**: #{terminus_setting}\\n\"\n    end\n\n    text\n  end\n\n  def initialize(model, name, doc: nil, indirected_class: nil, cache_class: nil, terminus_class: nil, terminus_setting: nil, extend: nil)\n    @model = model\n    @name = name\n    @termini = {}\n\n    @doc = doc\n\n    raise(ArgumentError, _(\"Indirection %{name} is already defined\") % { name: @name }) if @@indirections.find { |i| i.name == @name }\n\n    @@indirections << self\n\n    @indirected_class = indirected_class\n    self.extend(extend) if extend\n\n    # Setting these depend on the indirection already being installed so they have to be at the end\n    set_global_setting(:cache_class, cache_class)\n    set_global_setting(:terminus_class, terminus_class)\n    set_global_setting(:terminus_setting, terminus_setting)\n  end\n\n  # Use this to set indirector settings globally across threads.\n  def set_global_setting(setting, value)\n    case setting\n    when :cache_class\n      validate_terminus_class(value) unless value.nil?\n      @cache_class = Puppet::ThreadLocal.new(value)\n    when :terminus_class\n      validate_terminus_class(value) unless value.nil?\n      @terminus_class = Puppet::ThreadLocal.new(value)\n    when :terminus_setting\n      @terminus_setting = Puppet::ThreadLocal.new(value)\n    else\n      raise(ArgumentError, _(\"The setting %{setting} is not a valid indirection setting.\") % { setting: setting })\n    end\n  end\n\n  # Set up our request object.\n  def request(*args)\n    Puppet::Indirector::Request.new(name, *args)\n  end\n\n  # Return the singleton terminus for this indirection.\n  def terminus(terminus_name = nil)\n    # Get the name of the terminus.\n    raise Puppet::DevError, _(\"No terminus specified for %{name}; cannot redirect\") % { name: name } unless terminus_name ||= terminus_class\n\n    termini[terminus_name] ||= make_terminus(terminus_name)\n  end\n\n  # These can be used to select the terminus class.\n  def terminus_setting\n    @terminus_setting.value\n  end\n\n  def terminus_setting=(setting)\n    @terminus_setting.value = setting\n  end\n\n  # Determine the terminus class.\n  def terminus_class\n    unless @terminus_class.value\n      setting = terminus_setting\n      if setting\n        self.terminus_class = Puppet.settings[setting]\n      else\n        raise Puppet::DevError, _(\"No terminus class nor terminus setting was provided for indirection %{name}\") % { name: name }\n      end\n    end\n    @terminus_class.value\n  end\n\n  def reset_terminus_class\n    @terminus_class.value = nil\n  end\n\n  # Specify the terminus class to use.\n  def terminus_class=(klass)\n    validate_terminus_class(klass)\n    @terminus_class.value = klass\n  end\n\n  # This is used by terminus_class= and cache=.\n  def validate_terminus_class(terminus_class)\n    unless terminus_class and terminus_class.to_s != \"\"\n      raise ArgumentError, _(\"Invalid terminus name %{terminus_class}\") % { terminus_class: terminus_class.inspect }\n    end\n\n    unless Puppet::Indirector::Terminus.terminus_class(name, terminus_class)\n      raise ArgumentError, _(\"Could not find terminus %{terminus_class} for indirection %{name}\") %\n                           { terminus_class: terminus_class, name: name }\n    end\n  end\n\n  # Expire a cached object, if one is cached.  Note that we don't actually\n  # remove it, we expire it and write it back out to disk.  This way people\n  # can still use the expired object if they want.\n  def expire(key, options = {})\n    request = request(:expire, key, nil, options)\n\n    return nil unless cache? && !request.ignore_cache_save?\n\n    instance = cache.find(request(:find, key, nil, options))\n    return nil unless instance\n\n    Puppet.info _(\"Expiring the %{cache} cache of %{instance}\") % { cache: name, instance: instance.name }\n\n    # Set an expiration date in the past\n    instance.expiration = Time.now - 60\n\n    cache.save(request(:save, nil, instance, options))\n  end\n\n  def allow_remote_requests?\n    terminus.allow_remote_requests?\n  end\n\n  # Search for an instance in the appropriate terminus, caching the\n  # results if caching is configured..\n  def find(key, options = {})\n    request = request(:find, key, nil, options)\n    terminus = prepare(request)\n\n    result = find_in_cache(request)\n    if !result.nil?\n      result\n    elsif request.ignore_terminus?\n      nil\n    else\n      # Otherwise, return the result from the terminus, caching if\n      # appropriate.\n      result = terminus.find(request)\n      unless result.nil?\n        result.expiration ||= expiration if result.respond_to?(:expiration)\n        if cache? && !request.ignore_cache_save?\n          Puppet.info _(\"Caching %{indirection} for %{request}\") % { indirection: name, request: request.key }\n          begin\n            cache.save request(:save, key, result, options)\n          rescue => detail\n            Puppet.log_exception(detail)\n            raise detail\n          end\n        end\n\n        filtered = result\n        if terminus.respond_to?(:filter)\n          Puppet::Util::Profiler.profile(_(\"Filtered result for %{indirection} %{request}\") % { indirection: name, request: request.key }, [:indirector, :filter, name, request.key]) do\n            filtered = terminus.filter(result)\n          rescue Puppet::Error => detail\n            Puppet.log_exception(detail)\n            raise detail\n          end\n        end\n        filtered\n      end\n    end\n  end\n\n  # Search for an instance in the appropriate terminus, and return a\n  # boolean indicating whether the instance was found.\n  def head(key, options = {})\n    request = request(:head, key, nil, options)\n    terminus = prepare(request)\n\n    # Look in the cache first, then in the terminus.  Force the result\n    # to be a boolean.\n    !!(find_in_cache(request) || terminus.head(request))\n  end\n\n  def find_in_cache(request)\n    # See if our instance is in the cache and up to date.\n    cached = cache.find(request) if cache? && !request.ignore_cache?\n    return nil unless cached\n\n    if cached.expired?\n      Puppet.info _(\"Not using expired %{indirection} for %{request} from cache; expired at %{expiration}\") % { indirection: name, request: request.key, expiration: cached.expiration }\n      return nil\n    end\n\n    Puppet.debug { \"Using cached #{name} for #{request.key}\" }\n    cached\n  rescue => detail\n    Puppet.log_exception(detail, _(\"Cached %{indirection} for %{request} failed: %{detail}\") % { indirection: name, request: request.key, detail: detail })\n    nil\n  end\n\n  # Remove something via the terminus.\n  def destroy(key, options = {})\n    request = request(:destroy, key, nil, options)\n    terminus = prepare(request)\n\n    result = terminus.destroy(request)\n\n    if cache? and cache.find(request(:find, key, nil, options))\n      # Reuse the existing request, since it's equivalent.\n      cache.destroy(request)\n    end\n\n    result\n  end\n\n  # Search for more than one instance.  Should always return an array.\n  def search(key, options = {})\n    request = request(:search, key, nil, options)\n    terminus = prepare(request)\n\n    result = terminus.search(request)\n    if result\n      raise Puppet::DevError, _(\"Search results from terminus %{terminus_name} are not an array\") % { terminus_name: terminus.name } unless result.is_a?(Array)\n\n      result.each do |instance|\n        next unless instance.respond_to? :expiration\n\n        instance.expiration ||= expiration\n      end\n      result\n    end\n  end\n\n  # Save the instance in the appropriate terminus.  This method is\n  # normally an instance method on the indirected class.\n  def save(instance, key = nil, options = {})\n    request = request(:save, key, instance, options)\n    terminus = prepare(request)\n\n    result = terminus.save(request) unless request.ignore_terminus?\n\n    # If caching is enabled, save our document there\n    cache.save(request) if cache? && !request.ignore_cache_save?\n\n    result\n  end\n\n  private\n\n  # Check authorization if there's a hook available; fail if there is one\n  # and it returns false.\n  def check_authorization(request, terminus)\n    # At this point, we're assuming authorization makes no sense without\n    # client information.\n    return unless request.node\n\n    # This is only to authorize via a terminus-specific authorization hook.\n    return unless terminus.respond_to?(:authorized?)\n\n    unless terminus.authorized?(request)\n      msg = if request.options.empty?\n              _(\"Not authorized to call %{method} on %{description}\") %\n                { method: request.method, description: request.description }\n            else\n              _(\"Not authorized to call %{method} on %{description} with %{option}\") %\n                { method: request.method, description: request.description, option: request.options.inspect }\n            end\n      raise ArgumentError, msg\n    end\n  end\n\n  # Pick the appropriate terminus, check the request's authorization, and return it.\n  # @param [Puppet::Indirector::Request] request instance\n  # @return [Puppet::Indirector::Terminus] terminus instance (usually a subclass\n  #   of Puppet::Indirector::Terminus) for this request\n  def prepare(request)\n    # Pick our terminus.\n    terminus_name = terminus_class\n\n    dest_terminus = terminus(terminus_name)\n    check_authorization(request, dest_terminus)\n    dest_terminus.validate(request)\n\n    dest_terminus\n  end\n\n  # Create a new terminus instance.\n  def make_terminus(terminus_class)\n    # Load our terminus class.\n    klass = Puppet::Indirector::Terminus.terminus_class(name, terminus_class)\n    unless klass\n      raise ArgumentError, _(\"Could not find terminus %{terminus_class} for indirection %{indirection}\") % { terminus_class: terminus_class, indirection: name }\n    end\n\n    klass.new\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/json.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\nrequire_relative '../../puppet/util'\n\n# The base class for JSON indirection terminus implementations.\n#\n# This should generally be preferred to the YAML base for any future\n# implementations, since it is faster and can load untrusted data safely.\nclass Puppet::Indirector::JSON < Puppet::Indirector::Terminus\n  def find(request)\n    load_json_from_file(path(request.key), request.key)\n  end\n\n  def save(request)\n    filename = path(request.key)\n    FileUtils.mkdir_p(File.dirname(filename))\n\n    Puppet::FileSystem.replace_file(filename, 0o660) { |f| f.print to_json(request.instance).force_encoding(Encoding::BINARY) }\n  rescue TypeError => detail\n    Puppet.log_exception(detail, _(\"Could not save %{json} %{request}: %{detail}\") % { json: name, request: request.key, detail: detail })\n  end\n\n  def destroy(request)\n    Puppet::FileSystem.unlink(path(request.key))\n  rescue => detail\n    unless detail.is_a? Errno::ENOENT\n      raise Puppet::Error, _(\"Could not destroy %{json} %{request}: %{detail}\") % { json: name, request: request.key, detail: detail }, detail.backtrace\n    end\n\n    1 # emulate success...\n  end\n\n  def search(request)\n    Dir.glob(path(request.key)).collect do |file|\n      load_json_from_file(file, request.key)\n    end\n  end\n\n  # Return the path to a given node's file.\n  def path(name, ext = '.json')\n    if name =~ Puppet::Indirector::BadNameRegexp then\n      Puppet.crit(_(\"directory traversal detected in %{json}: %{name}\") % { json: self.class, name: name.inspect })\n      raise ArgumentError, _(\"invalid key\")\n    end\n\n    base = data_dir\n    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)\n  end\n\n  private\n\n  def data_dir\n    Puppet.run_mode.server? ? Puppet[:server_datadir] : Puppet[:client_datadir]\n  end\n\n  def load_json_from_file(file, key)\n    json = nil\n\n    begin\n      json = Puppet::FileSystem.read(file, :encoding => Encoding::BINARY)\n    rescue Errno::ENOENT\n      return nil\n    rescue => detail\n      raise Puppet::Error, _(\"Could not read JSON data for %{name} %{key}: %{detail}\") % { name: indirection.name, key: key, detail: detail }, detail.backtrace\n    end\n\n    begin\n      from_json(json)\n    rescue => detail\n      raise Puppet::Error, _(\"Could not parse JSON data for %{name} %{key}: %{detail}\") % { name: indirection.name, key: key, detail: detail }, detail.backtrace\n    end\n  end\n\n  def from_json(text)\n    model.convert_from('json', text.force_encoding(Encoding::UTF_8))\n  end\n\n  def to_json(object)\n    object.render('json')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/memory.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\n\n# Manage a memory-cached list of instances.\nclass Puppet::Indirector::Memory < Puppet::Indirector::Terminus\n  def initialize\n    clear\n  end\n\n  def clear\n    @instances = {}\n  end\n\n  def destroy(request)\n    raise ArgumentError, _(\"Could not find %{request} to destroy\") % { request: request.key } unless @instances.include?(request.key)\n\n    @instances.delete(request.key)\n  end\n\n  def find(request)\n    @instances[request.key]\n  end\n\n  def search(request)\n    found_keys = @instances.keys.find_all { |key| key.include?(request.key) }\n    found_keys.collect { |key| @instances[key] }\n  end\n\n  def head(request)\n    !find(request).nil?\n  end\n\n  def save(request)\n    @instances[request.key] = request.instance\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/msgpack.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\nrequire_relative '../../puppet/util'\n\n# The base class for MessagePack indirection terminus implementations.\n#\n# This should generally be preferred to the PSON base for any future\n# implementations, since it is ~ 30 times faster\nclass Puppet::Indirector::Msgpack < Puppet::Indirector::Terminus\n  def initialize(*args)\n    unless Puppet.features.msgpack?\n      raise _(\"MessagePack terminus not supported without msgpack library\")\n    end\n\n    super\n  end\n\n  def find(request)\n    load_msgpack_from_file(path(request.key), request.key)\n  end\n\n  def save(request)\n    filename = path(request.key)\n    FileUtils.mkdir_p(File.dirname(filename))\n\n    Puppet::FileSystem.replace_file(filename, 0o660) { |f| f.print to_msgpack(request.instance) }\n  rescue TypeError => detail\n    Puppet.log_exception(detail, _(\"Could not save %{name} %{request}: %{detail}\") % { name: name, request: request.key, detail: detail })\n  end\n\n  def destroy(request)\n    Puppet::FileSystem.unlink(path(request.key))\n  rescue => detail\n    unless detail.is_a? Errno::ENOENT\n      raise Puppet::Error, _(\"Could not destroy %{name} %{request}: %{detail}\") % { name: name, request: request.key, detail: detail }, detail.backtrace\n    end\n\n    1 # emulate success...\n  end\n\n  def search(request)\n    Dir.glob(path(request.key)).collect do |file|\n      load_msgpack_from_file(file, request.key)\n    end\n  end\n\n  # Return the path to a given node's file.\n  def path(name, ext = '.msgpack')\n    if name =~ Puppet::Indirector::BadNameRegexp then\n      Puppet.crit(_(\"directory traversal detected in %{indirection}: %{name}\") % { indirection: self.class, name: name.inspect })\n      raise ArgumentError, _(\"invalid key\")\n    end\n\n    base = Puppet.run_mode.server? ? Puppet[:server_datadir] : Puppet[:client_datadir]\n    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)\n  end\n\n  private\n\n  def load_msgpack_from_file(file, key)\n    msgpack = nil\n\n    begin\n      msgpack = Puppet::FileSystem.read(file, :encoding => 'utf-8')\n    rescue Errno::ENOENT\n      return nil\n    rescue => detail\n      # TRANSLATORS \"MessagePack\" is a program name and should not be translated\n      raise Puppet::Error, _(\"Could not read MessagePack data for %{indirection} %{key}: %{detail}\") % { indirection: indirection.name, key: key, detail: detail }, detail.backtrace\n    end\n\n    begin\n      from_msgpack(msgpack)\n    rescue => detail\n      raise Puppet::Error, _(\"Could not parse MessagePack data for %{indirection} %{key}: %{detail}\") % { indirection: indirection.name, key: key, detail: detail }, detail.backtrace\n    end\n  end\n\n  def from_msgpack(text)\n    model.convert_from('msgpack', text)\n  end\n\n  def to_msgpack(object)\n    object.render('msgpack')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/exec.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/exec'\n\nclass Puppet::Node::Exec < Puppet::Indirector::Exec\n  desc \"Call an external program to get node information.  See\n  the [External Nodes](https://puppet.com/docs/puppet/latest/lang_write_functions_in_puppet.html) page for more information.\"\n  include Puppet::Util\n\n  def command\n    command = Puppet[:external_nodes]\n    raise ArgumentError, _(\"You must set the 'external_nodes' parameter to use the external node terminus\") unless command != _(\"none\")\n\n    command.split\n  end\n\n  # Look for external node definitions.\n  def find(request)\n    output = super or return nil\n\n    # Translate the output to ruby.\n    result = translate(request.key, output)\n\n    facts = request.options[:facts].is_a?(Puppet::Node::Facts) ? request.options[:facts] : nil\n\n    # Set the requested environment if it wasn't overridden\n    # If we don't do this it gets set to the local default\n    result[:environment] ||= request.environment\n\n    create_node(request.key, result, facts)\n  end\n\n  private\n\n  # Proxy the execution, so it's easier to test.\n  def execute(command, arguments)\n    Puppet::Util::Execution.execute(command, arguments)\n  end\n\n  # Turn our outputted objects into a Puppet::Node instance.\n  def create_node(name, result, facts = nil)\n    node = Puppet::Node.new(name)\n    [:parameters, :classes, :environment].each do |param|\n      value = result[param]\n      if value\n        node.send(param.to_s + \"=\", value)\n      end\n    end\n\n    node.fact_merge(facts)\n    node\n  end\n\n  # Translate the yaml string into Ruby objects.\n  def translate(name, output)\n    Puppet::Util::Yaml.safe_load(output, [Symbol]).each_with_object({}) do |data, hash|\n      case data[0]\n      when String\n        hash[data[0].intern] = data[1]\n      when Symbol\n        hash[data[0]] = data[1]\n      else\n        raise Puppet::Error, _(\"key is a %{klass}, not a string or symbol\") % { klass: data[0].class }\n      end\n    end\n  rescue => detail\n    raise Puppet::Error, _(\"Could not load external node results for %{name}: %{detail}\") % { name: name, detail: detail }, detail.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/json.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/json'\n\nclass Puppet::Node::Json < Puppet::Indirector::JSON\n  desc \"Store node information as flat files, serialized using JSON,\n    or deserialize stored JSON nodes.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/memory.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/memory'\n\nclass Puppet::Node::Memory < Puppet::Indirector::Memory\n  desc \"Keep track of nodes in memory but nowhere else.  This is used for\n    one-time compiles, such as what the stand-alone `puppet` does.\n    To use this terminus, you must load it with the data you want it\n    to contain; it is only useful for developers and should generally not\n    be chosen by a normal user.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/msgpack.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/msgpack'\n\nclass Puppet::Node::Msgpack < Puppet::Indirector::Msgpack\n  desc \"Store node information as flat files, serialized using MessagePack,\n    or deserialize stored MessagePack nodes.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/plain.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/plain'\n\nclass Puppet::Node::Plain < Puppet::Indirector::Plain\n  desc \"Always return an empty node object. Assumes you keep track of nodes\n    in flat file manifests.  You should use it when you don't have some other,\n    functional source you want to use, as the compiler will not work without a\n    valid node terminus.\n\n    Note that class is responsible for merging the node's facts into the\n    node instance before it is returned.\"\n\n  # Just return an empty node.\n  def find(request)\n    node = super\n    node.environment = request.environment\n    facts = request.options[:facts].is_a?(Puppet::Node::Facts) ? request.options[:facts] : nil\n    node.fact_merge(facts)\n    node\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/rest'\n\nclass Puppet::Node::Rest < Puppet::Indirector::REST\n  desc \"Get a node via REST. Puppet agent uses this to allow the puppet master\n    to override its environment.\"\n\n  def find(request)\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:puppet)\n    _, node = api.get_node(\n      request.key,\n      environment: request.environment.to_s,\n      configured_environment: request.options[:configured_environment],\n      transaction_uuid: request.options[:transaction_uuid]\n    )\n    node\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(e.response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(e.response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(e.response)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/store_configs.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/store_configs'\nrequire_relative '../../../puppet/node'\n\nclass Puppet::Node::StoreConfigs < Puppet::Indirector::StoreConfigs\n  desc 'Part of the \"storeconfigs\" feature. Should not be directly set by end users.'\nend\n"
  },
  {
    "path": "lib/puppet/indirector/node/yaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/node'\nrequire_relative '../../../puppet/indirector/yaml'\n\nclass Puppet::Node::Yaml < Puppet::Indirector::Yaml\n  desc \"Store node information as flat files, serialized using YAML,\n    or deserialize stored YAML nodes.\"\nend\n"
  },
  {
    "path": "lib/puppet/indirector/none.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\n\n# A none terminus type, meant to always return nil\nclass Puppet::Indirector::None < Puppet::Indirector::Terminus\n  def find(request)\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/plain.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\n\n# An empty terminus type, meant to just return empty objects.\nclass Puppet::Indirector::Plain < Puppet::Indirector::Terminus\n  # Just return nothing.\n  def find(request)\n    indirection.model.new(request.key)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/report/json.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/transaction/report'\nrequire_relative '../../../puppet/indirector/json'\n\nclass Puppet::Transaction::Report::Json < Puppet::Indirector::JSON\n  include Puppet::Util::SymbolicFileMode\n\n  desc \"Store last report as a flat file, serialized using JSON.\"\n\n  # Force report to be saved there\n  def path(name, ext = '.json')\n    Puppet[:lastrunreport]\n  end\n\n  def save(request)\n    filename = path(request.key)\n    mode = Puppet.settings.setting(:lastrunreport).mode\n\n    unless valid_symbolic_mode?(mode)\n      raise Puppet::DevError, _(\"replace_file mode: %{mode} is invalid\") % { mode: mode }\n    end\n\n    mode = symbolic_mode_to_int(normalize_symbolic_mode(mode))\n\n    FileUtils.mkdir_p(File.dirname(filename))\n\n    begin\n      Puppet::FileSystem.replace_file(filename, mode) do |fh|\n        fh.print JSON.dump(request.instance)\n      end\n    rescue TypeError => detail\n      Puppet.err _(\"Could not save %{indirection} %{request}: %{detail}\") % { indirection: name, request: request.key, detail: detail }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/report/msgpack.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/transaction/report'\nrequire_relative '../../../puppet/indirector/msgpack'\n\nclass Puppet::Transaction::Report::Msgpack < Puppet::Indirector::Msgpack\n  desc \"Store last report as a flat file, serialized using MessagePack.\"\n\n  # Force report to be saved there\n  def path(name, ext = '.msgpack')\n    Puppet[:lastrunreport]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/report/processor.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/transaction/report'\nrequire_relative '../../../puppet/indirector/code'\nrequire_relative '../../../puppet/reports'\n\nclass Puppet::Transaction::Report::Processor < Puppet::Indirector::Code\n  desc \"Puppet's report processor.  Processes the report with each of\n    the report types listed in the 'reports' setting.\"\n\n  def initialize\n    Puppet.settings.use(:main, :reporting, :metrics)\n  end\n\n  def save(request)\n    process(request.instance)\n  end\n\n  def destroy(request)\n    processors do |mod|\n      mod.destroy(request.key) if mod.respond_to?(:destroy)\n    end\n  end\n\n  private\n\n  # Process the report with each of the configured report types.\n  # LAK:NOTE This isn't necessarily the best design, but it's backward\n  # compatible and that's good enough for now.\n  def process(report)\n    Puppet.debug { \"Received report to process from #{report.host}\" }\n    processors do |mod|\n      Puppet.debug { \"Processing report from #{report.host} with processor #{mod}\" }\n      # We have to use a dup because we're including a module in the\n      # report.\n      newrep = report.dup\n      begin\n        newrep.extend(mod)\n        newrep.process\n      rescue => detail\n        Puppet.log_exception(detail, _(\"Report %{report} failed: %{detail}\") % { report: name, detail: detail })\n      end\n    end\n  end\n\n  # Handle the parsing of the reports attribute.\n  def reports\n    Puppet[:reports].gsub(/(^\\s+)|(\\s+$)/, '').split(/\\s*,\\s*/)\n  end\n\n  def processors(&blk)\n    return [] if Puppet[:reports] == \"none\"\n\n    reports.each do |name|\n      mod = Puppet::Reports.report(name)\n      if mod\n        yield(mod)\n      else\n        Puppet.warning _(\"No report named '%{name}'\") % { name: name }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/report/rest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/rest'\nrequire 'semantic_puppet'\n\nclass Puppet::Transaction::Report::Rest < Puppet::Indirector::REST\n  desc \"Get server report over HTTP via REST.\"\n\n  def save(request)\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:report)\n    response = api.put_report(\n      request.key,\n      request.instance,\n      environment: request.environment.to_s\n    )\n    content_type, body = parse_response(response)\n    deserialize_save(content_type, body)\n  rescue Puppet::HTTP::ResponseError => e\n    return nil if e.response.code == 404\n\n    raise convert_to_http_error(e.response)\n  end\n\n  private\n\n  def deserialize_save(content_type, body)\n    format = Puppet::Network::FormatHandler.format_for(content_type)\n    format.intern(Array, body)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/report/yaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/transaction/report'\nrequire_relative '../../../puppet/indirector/yaml'\n\nclass Puppet::Transaction::Report::Yaml < Puppet::Indirector::Yaml\n  include Puppet::Util::SymbolicFileMode\n\n  desc \"Store last report as a flat file, serialized using YAML.\"\n\n  # Force report to be saved there\n  def path(name, ext = '.yaml')\n    Puppet[:lastrunreport]\n  end\n\n  def save(request)\n    filename = path(request.key)\n    mode = Puppet.settings.setting(:lastrunreport).mode\n\n    unless valid_symbolic_mode?(mode)\n      raise Puppet::DevError, _(\"replace_file mode: %{mode} is invalid\") % { mode: mode }\n    end\n\n    mode = symbolic_mode_to_int(normalize_symbolic_mode(mode))\n\n    FileUtils.mkdir_p(File.dirname(filename))\n\n    begin\n      Puppet::FileSystem.replace_file(filename, mode) do |fh|\n        fh.print YAML.dump(request.instance)\n      end\n    rescue TypeError => detail\n      Puppet.err _(\"Could not save %{indirection} %{request}: %{detail}\") % { indirection: name, request: request.key, detail: detail }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/request.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'cgi'\nrequire 'uri'\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/util/psych_support'\nrequire_relative '../../puppet/util/warnings'\n\n# This class encapsulates all of the information you need to make an\n# Indirection call, and as a result also handles REST calls.  It's somewhat\n# analogous to an HTTP Request object, except tuned for our Indirector.\nclass Puppet::Indirector::Request\n  include Puppet::Util::PsychSupport\n  include Puppet::Util::Warnings\n\n  attr_accessor :key, :method, :options, :instance, :node, :ip, :authenticated, :ignore_cache, :ignore_cache_save, :ignore_terminus\n\n  attr_accessor :server, :port, :uri, :protocol\n\n  attr_reader :indirection_name\n\n  # trusted_information is specifically left out because we can't serialize it\n  # and keep it \"trusted\"\n  OPTION_ATTRIBUTES = [:ip, :node, :authenticated, :ignore_terminus, :ignore_cache, :ignore_cache_save, :instance, :environment]\n\n  # Is this an authenticated request?\n  def authenticated?\n    # Double negative, so we just get true or false\n    !!authenticated\n  end\n\n  def environment\n    # If environment has not been set directly, we should use the application's\n    # current environment\n    @environment ||= Puppet.lookup(:current_environment)\n  end\n\n  def environment=(env)\n    @environment =\n      if env.is_a?(Puppet::Node::Environment)\n        env\n      else\n        Puppet.lookup(:environments).get!(env)\n      end\n  end\n\n  # LAK:NOTE This is a messy interface to the cache, and it's only\n  # used by the Configurer class.  I decided it was better to implement\n  # it now and refactor later, when we have a better design, than\n  # to spend another month coming up with a design now that might\n  # not be any better.\n  def ignore_cache?\n    ignore_cache\n  end\n\n  def ignore_cache_save?\n    ignore_cache_save\n  end\n\n  def ignore_terminus?\n    ignore_terminus\n  end\n\n  def initialize(indirection_name, method, key, instance, options = {})\n    @instance = instance\n    options ||= {}\n\n    self.indirection_name = indirection_name\n    self.method = method\n\n    options = options.each_with_object({}) { |ary, hash| hash[ary[0].to_sym] = ary[1]; }\n\n    set_attributes(options)\n\n    @options = options\n\n    if key\n      # If the request key is a URI, then we need to treat it specially,\n      # because it rewrites the key.  We could otherwise strip server/port/etc\n      # info out in the REST class, but it seemed bad design for the REST\n      # class to rewrite the key.\n\n      if key.to_s =~ %r{^\\w+:/} and !Puppet::Util.absolute_path?(key.to_s) # it's a URI\n        set_uri_key(key)\n      else\n        @key = key\n      end\n    end\n\n    @key = @instance.name if !@key and @instance\n  end\n\n  # Look up the indirection based on the name provided.\n  def indirection\n    Puppet::Indirector::Indirection.instance(indirection_name)\n  end\n\n  def indirection_name=(name)\n    @indirection_name = name.to_sym\n  end\n\n  def model\n    ind = indirection\n    raise ArgumentError, _(\"Could not find indirection '%{indirection}'\") % { indirection: indirection_name } unless ind\n\n    ind.model\n  end\n\n  # Are we trying to interact with multiple resources, or just one?\n  def plural?\n    method == :search\n  end\n\n  def initialize_from_hash(hash)\n    @indirection_name = hash['indirection_name'].to_sym\n    @method = hash['method'].to_sym\n    @key = hash['key']\n    @instance = hash['instance']\n    @options = hash['options']\n  end\n\n  def to_data_hash\n    { 'indirection_name' => @indirection_name.to_s,\n      'method' => @method.to_s,\n      'key' => @key,\n      'instance' => @instance,\n      'options' => @options }\n  end\n\n  def to_hash\n    result = options.dup\n\n    OPTION_ATTRIBUTES.each do |attribute|\n      value = send(attribute)\n      if value\n        result[attribute] = value\n      end\n    end\n    result\n  end\n\n  def description\n    uri || \"/#{indirection_name}/#{key}\"\n  end\n\n  def remote?\n    node or ip\n  end\n\n  private\n\n  def set_attributes(options)\n    OPTION_ATTRIBUTES.each do |attribute|\n      if options.include?(attribute.to_sym)\n        send(attribute.to_s + \"=\", options[attribute])\n        options.delete(attribute)\n      end\n    end\n  end\n\n  # Parse the key as a URI, setting attributes appropriately.\n  def set_uri_key(key)\n    @uri = key\n    begin\n      # calling uri_encode for UTF-8 characters will % escape them and keep them UTF-8\n      uri = URI.parse(Puppet::Util.uri_encode(key))\n    rescue => detail\n      raise ArgumentError, _(\"Could not understand URL %{key}: %{detail}\") % { key: key, detail: detail }, detail.backtrace\n    end\n\n    # Just short-circuit these to full paths\n    if uri.scheme == \"file\"\n      @key = Puppet::Util.uri_to_path(uri)\n      return\n    end\n\n    @server = uri.host if uri.host && !uri.host.empty?\n\n    # If the URI class can look up the scheme, it will provide a port,\n    # otherwise it will default to '0'.\n    if uri.port.to_i == 0 and uri.scheme == \"puppet\"\n      @port = Puppet.settings[:serverport].to_i\n    else\n      @port = uri.port.to_i\n    end\n\n    # filebucket:// is only used internally to pass request details\n    # from Dipper objects to the indirector. The wire always uses HTTPS.\n    if uri.scheme == 'filebucket'\n      @protocol = 'https'\n    else\n      @protocol = uri.scheme\n    end\n\n    @key = Puppet::Util.uri_unescape(uri.path.sub(%r{^/}, ''))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/resource/ral.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/resource/validator'\n\nclass Puppet::Resource::Ral < Puppet::Indirector::Code\n  include Puppet::Resource::Validator\n\n  desc \"Manipulate resources with the resource abstraction layer. Only used internally.\"\n\n  def allow_remote_requests?\n    false\n  end\n\n  def find(request)\n    # find by name\n    res   = type(request).instances.find { |o| o.name == resource_name(request) }\n    res ||= type(request).new(:name => resource_name(request), :audit => type(request).properties.collect(&:name))\n\n    res.to_resource\n  end\n\n  def search(request)\n    conditions = request.options.dup\n    conditions[:name] = resource_name(request) if resource_name(request)\n\n    type(request).instances.map(&:to_resource).find_all do |res|\n      conditions.all? do |property, value|\n        # even though `res` is an instance of Puppet::Resource, calling\n        # `res[:name]` on it returns nil, and for some reason it is necessary\n        # to invoke the Puppet::Resource#copy_as_resource copy constructor...\n        res.copy_as_resource[property].to_s == value.to_s\n      end\n    end.sort_by(&:title)\n  end\n\n  def save(request)\n    # In RAL-land, to \"save\" means to actually try to change machine state\n    res = request.instance\n    ral_res = res.to_ral\n\n    catalog = Puppet::Resource::Catalog.new(nil, request.environment)\n    catalog.add_resource ral_res\n    transaction = catalog.apply\n\n    [ral_res.to_resource, transaction.report]\n  end\n\n  private\n\n  # {type,resource}_name: the resource name may contain slashes:\n  # File[\"/etc/hosts\"]. To handle, assume the type name does\n  # _not_ have any slashes in it, and split only on the first.\n\n  def type_name(request)\n    request.key.split('/', 2)[0]\n  end\n\n  def resource_name(request)\n    name = request.key.split('/', 2)[1]\n    name unless name == \"\"\n  end\n\n  def type(request)\n    Puppet::Type.type(type_name(request)) or raise Puppet::Error, _(\"Could not find type %{request_type}\") % { request_type: type_name(request) }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/resource/store_configs.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/indirector/store_configs'\nrequire_relative '../../../puppet/indirector/resource/validator'\n\nclass Puppet::Resource::StoreConfigs < Puppet::Indirector::StoreConfigs\n  include Puppet::Resource::Validator\n\n  desc 'Part of the \"storeconfigs\" feature. Should not be directly set by end users.'\n\n  def allow_remote_requests?\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/resource/validator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Resource::Validator\n  def validate_key(request)\n    type, title = request.key.split('/', 2)\n    unless type.casecmp(request.instance.type).zero? and title == request.instance.title\n      raise Puppet::Indirector::ValidationError, _(\"Resource instance does not match request key\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/rest.rb",
    "content": "# frozen_string_literal: true\n\n# Access objects via REST\nclass Puppet::Indirector::REST < Puppet::Indirector::Terminus\n  def find(request)\n    raise NotImplementedError\n  end\n\n  def head(request)\n    raise NotImplementedError\n  end\n\n  def search(request)\n    raise NotImplementedError\n  end\n\n  def destroy(request)\n    raise NotImplementedError\n  end\n\n  def save(request)\n    raise NotImplementedError\n  end\n\n  def validate_key(request)\n    # Validation happens on the remote end\n  end\n\n  private\n\n  def convert_to_http_error(response)\n    if response.body.to_s.empty? && response.reason\n      returned_message = response.reason\n    elsif response['content-type'].is_a?(String)\n      content_type, body = parse_response(response)\n      if content_type =~ /[pj]son/\n        returned_message = Puppet::Util::Json.load(body)[\"message\"]\n      else\n        returned_message = response.body\n      end\n    else\n      returned_message = response.body\n    end\n\n    message = _(\"Error %{code} on SERVER: %{returned_message}\") % { code: response.code, returned_message: returned_message }\n    Net::HTTPError.new(message, Puppet::HTTP::ResponseConverter.to_ruby_response(response))\n  end\n\n  # Returns the content_type, stripping any appended charset, and the\n  # body, decompressed if necessary\n  def parse_response(response)\n    if response['content-type']\n      [response['content-type'].gsub(/\\s*;.*$/, ''), response.body]\n    else\n      raise _(\"No content type in http response; cannot parse\")\n    end\n  end\n\n  def elide(string, length)\n    if Puppet::Util::Log.level == :debug || string.length <= length\n      string\n    else\n      string[0, length - 3] + \"...\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/store_configs.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Indirector::StoreConfigs < Puppet::Indirector::Terminus\n  def initialize\n    super\n    # This will raise if the indirection can't be found, so we can assume it\n    # is always set to a valid instance from here on in.\n    @target = indirection.terminus Puppet[:storeconfigs_backend]\n  end\n\n  attr_reader :target\n\n  def head(request)\n    target.head request\n  end\n\n  def find(request)\n    target.find request\n  end\n\n  def search(request)\n    target.search request\n  end\n\n  def save(request)\n    target.save request\n  end\n\n  def destroy(request)\n    target.save request\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/terminus.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/indirector/errors'\nrequire_relative '../../puppet/indirector/indirection'\nrequire_relative '../../puppet/util/instance_loader'\n\n# A simple class that can function as the base class for indirected types.\nclass Puppet::Indirector::Terminus\n  require_relative '../../puppet/util/docs'\n  extend Puppet::Util::Docs\n\n  class << self\n    include Puppet::Util::InstanceLoader\n\n    attr_accessor :name, :terminus_type\n    attr_reader :abstract_terminus, :indirection\n\n    # Are we an abstract terminus type, rather than an instance with an\n    # associated indirection?\n    def abstract_terminus?\n      abstract_terminus\n    end\n\n    # Convert a constant to a short name.\n    def const2name(const)\n      const.sub(/^[A-Z]/, &:downcase).gsub(/[A-Z]/) { |i| \"_#{i.downcase}\" }.intern\n    end\n\n    # Look up the indirection if we were only provided a name.\n    def indirection=(name)\n      if name.is_a?(Puppet::Indirector::Indirection)\n        @indirection = name\n      else\n        ind = Puppet::Indirector::Indirection.instance(name)\n        if ind\n          @indirection = ind\n        else\n          raise ArgumentError, _(\"Could not find indirection instance %{name} for %{terminus}\") % { name: name, terminus: self.name }\n        end\n      end\n    end\n\n    def indirection_name\n      @indirection.name\n    end\n\n    # Register our subclass with the appropriate indirection.\n    # This follows the convention that our terminus is named after the\n    # indirection.\n    def inherited(subclass)\n      longname = subclass.to_s\n      if longname =~ /#<Class/\n        raise Puppet::DevError, _(\"Terminus subclasses must have associated constants\")\n      end\n\n      names = longname.split(\"::\")\n\n      # Convert everything to a lower-case symbol, converting camelcase to underscore word separation.\n      name = names.pop.sub(/^[A-Z]/, &:downcase).gsub(/[A-Z]/) { |i| \"_#{i.downcase}\" }.intern\n\n      subclass.name = name\n\n      # Short-circuit the abstract types, which are those that directly subclass\n      # the Terminus class.\n      if self == Puppet::Indirector::Terminus\n        subclass.mark_as_abstract_terminus\n        return\n      end\n\n      # Set the terminus type to be the name of the abstract terminus type.\n      # Yay, class/instance confusion.\n      subclass.terminus_type = self.name\n\n      # This subclass is specifically associated with an indirection.\n      raise(\"Invalid name #{longname}\") unless names.length > 0\n\n      processed_name = names.pop.sub(/^[A-Z]/, &:downcase).gsub(/[A-Z]/) { |i| \"_#{i.downcase}\" }\n\n      if processed_name.empty?\n        raise Puppet::DevError, _(\"Could not discern indirection model from class constant\")\n      end\n\n      # This will throw an exception if the indirection instance cannot be found.\n      # Do this last, because it also registers the terminus type with the indirection,\n      # which needs the above information.\n      subclass.indirection = processed_name.intern\n\n      # And add this instance to the instance hash.\n      Puppet::Indirector::Terminus.register_terminus_class(subclass)\n    end\n\n    # Mark that this instance is abstract.\n    def mark_as_abstract_terminus\n      @abstract_terminus = true\n    end\n\n    def model\n      indirection.model\n    end\n\n    # Convert a short name to a constant.\n    def name2const(name)\n      name.to_s.capitalize.sub(/_(.)/) { |_i| ::Regexp.last_match(1).upcase }\n    end\n\n    # Register a class, probably autoloaded.\n    def register_terminus_class(klass)\n      setup_instance_loading klass.indirection_name\n      instance_hash(klass.indirection_name)[klass.name] = klass\n    end\n\n    # Return a terminus by name, using the autoloader.\n    def terminus_class(indirection_name, terminus_type)\n      setup_instance_loading indirection_name\n      loaded_instance(indirection_name, terminus_type)\n    end\n\n    # Return all terminus classes for a given indirection.\n    def terminus_classes(indirection_name)\n      setup_instance_loading indirection_name\n      instance_loader(indirection_name).files_to_load(Puppet.lookup(:current_environment)).map do |file|\n        File.basename(file).chomp(\".rb\").intern\n      end\n    end\n\n    private\n\n    def setup_instance_loading(type)\n      instance_load type, \"puppet/indirector/#{type}\" unless instance_loading?(type)\n    end\n  end\n\n  def indirection\n    self.class.indirection\n  end\n\n  def initialize\n    raise Puppet::DevError, _(\"Cannot create instances of abstract terminus types\") if self.class.abstract_terminus?\n  end\n\n  def model\n    self.class.model\n  end\n\n  def name\n    self.class.name\n  end\n\n  def require_environment?\n    true\n  end\n\n  def allow_remote_requests?\n    true\n  end\n\n  def terminus_type\n    self.class.terminus_type\n  end\n\n  def validate(request)\n    if request.instance\n      validate_model(request)\n      validate_key(request)\n    end\n  end\n\n  def validate_key(request)\n    unless request.key == request.instance.name\n      raise Puppet::Indirector::ValidationError, _(\"Instance name %{name} does not match requested key %{key}\") % { name: request.instance.name.inspect, key: request.key.inspect }\n    end\n  end\n\n  def validate_model(request)\n    unless model === request.instance\n      raise Puppet::Indirector::ValidationError, _(\"Invalid instance type %{klass}, expected %{model_type}\") % { klass: request.instance.class.inspect, model_type: model.inspect }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector/yaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/terminus'\nrequire_relative '../../puppet/util/yaml'\n\n# The base class for YAML indirection termini.\nclass Puppet::Indirector::Yaml < Puppet::Indirector::Terminus\n  # Read a given name's file in and convert it from YAML.\n  def find(request)\n    file = path(request.key)\n    return nil unless Puppet::FileSystem.exist?(file)\n\n    begin\n      load_file(file)\n    rescue Puppet::Util::Yaml::YamlLoadError => detail\n      raise Puppet::Error, _(\"Could not parse YAML data for %{indirection} %{request}: %{detail}\") % { indirection: indirection.name, request: request.key, detail: detail }, detail.backtrace\n    end\n  end\n\n  # Convert our object to YAML and store it to the disk.\n  def save(request)\n    raise ArgumentError, _(\"You can only save objects that respond to :name\") unless request.instance.respond_to?(:name)\n\n    file = path(request.key)\n\n    basedir = File.dirname(file)\n\n    # This is quite likely a bad idea, since we're not managing ownership or modes.\n    Dir.mkdir(basedir) unless Puppet::FileSystem.exist?(basedir)\n\n    begin\n      Puppet::Util::Yaml.dump(request.instance, file)\n    rescue TypeError => detail\n      Puppet.err _(\"Could not save %{indirection} %{request}: %{detail}\") % { indirection: name, request: request.key, detail: detail }\n    end\n  end\n\n  # Return the path to a given node's file.\n  def path(name, ext = '.yaml')\n    if name =~ Puppet::Indirector::BadNameRegexp then\n      Puppet.crit(_(\"directory traversal detected in %{indirection}: %{name}\") % { indirection: self.class, name: name.inspect })\n      raise ArgumentError, _(\"invalid key\")\n    end\n\n    base = Puppet.run_mode.server? ? Puppet[:yamldir] : Puppet[:clientyamldir]\n    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)\n  end\n\n  def destroy(request)\n    file_path = path(request.key)\n    Puppet::FileSystem.unlink(file_path) if Puppet::FileSystem.exist?(file_path)\n  end\n\n  def search(request)\n    Dir.glob(path(request.key, '')).collect do |file|\n      load_file(file)\n    end\n  end\n\n  protected\n\n  def load_file(file)\n    Puppet::Util::Yaml.safe_load_file(file, [model, Symbol])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/indirector.rb",
    "content": "# frozen_string_literal: true\n\n# Manage indirections to termini.  They are organized in terms of indirections -\n# - e.g., configuration, node, file, certificate -- and each indirection has one\n# or more terminus types defined.  The indirection is configured via the\n# +indirects+ method, which will be called by the class extending itself\n# with this module.\nmodule Puppet::Indirector\n  # LAK:FIXME We need to figure out how to handle documentation for the\n  # different indirection types.\n\n  require_relative 'indirector/indirection'\n  require_relative 'indirector/terminus'\n  require_relative 'indirector/code'\n  require_relative 'indirector/envelope'\n  require_relative '../puppet/network/format_support'\n\n  def self.configure_routes(application_routes)\n    application_routes.each do |indirection_name, termini|\n      indirection_name = indirection_name.to_sym\n      terminus_name = termini[\"terminus\"]\n      cache_name    = termini[\"cache\"]\n\n      Puppet::Indirector::Terminus.terminus_class(indirection_name, terminus_name || cache_name)\n\n      indirection = Puppet::Indirector::Indirection.instance(indirection_name)\n      raise _(\"Indirection %{indirection_name} does not exist\") % { indirection_name: indirection_name } unless indirection\n\n      indirection.set_global_setting(:terminus_class, terminus_name) if terminus_name\n      indirection.set_global_setting(:cache_class, cache_name) if cache_name\n    end\n  end\n\n  # Declare that the including class indirects its methods to\n  # this terminus.  The terminus name must be the name of a Puppet\n  # default, not the value -- if it's the value, then it gets\n  # evaluated at parse time, which is before the user has had a chance\n  # to override it.\n  def indirects(indirection, options = {})\n    raise(ArgumentError, _(\"Already handling indirection for %{current}; cannot also handle %{next}\") % { current: @indirection.name, next: indirection }) if @indirection\n\n    # populate this class with the various new methods\n    extend ClassMethods\n    include Puppet::Indirector::Envelope\n    include Puppet::Network::FormatSupport\n\n    # record the indirected class name for documentation purposes\n    options[:indirected_class] = name\n\n    # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node)\n    # & hook the instantiated Terminus into this class (Node: @indirection = terminus)\n    @indirection = Puppet::Indirector::Indirection.new(self, indirection, **options)\n  end\n\n  module ClassMethods\n    attr_reader :indirection\n  end\n\n  # Helper definition for indirections that handle filenames.\n  BadNameRegexp = Regexp.union(/^\\.\\./,\n                               %r{[\\\\/]},\n                               \"\\0\",\n                               /(?i)^[a-z]:/)\nend\n"
  },
  {
    "path": "lib/puppet/info_service/class_information_service.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/pops'\nrequire_relative '../../puppet/pops/evaluator/json_strict_literal_evaluator'\n\nclass Puppet::InfoService::ClassInformationService\n  def initialize\n    @file_to_result = {}\n    @parser = Puppet::Pops::Parser::EvaluatingParser.new()\n  end\n\n  def classes_per_environment(env_file_hash)\n    # In this version of puppet there is only one way to parse manifests, as feature switches per environment\n    # are added or removed, this logic needs to change to compute the result per environment with the correct\n    # feature flags in effect.\n\n    unless env_file_hash.is_a?(Hash)\n      raise ArgumentError, _('Given argument must be a Hash')\n    end\n\n    result = {}\n\n    # for each environment\n    #   for each file\n    #     if file already processed, use last result or error\n    #\n    env_file_hash.each do |env, files|\n      env_result = result[env] = {}\n      files.each do |f|\n        env_result[f] = result_of(f)\n      end\n    end\n    result\n  end\n\n  private\n\n  def type_parser\n    Puppet::Pops::Types::TypeParser.singleton\n  end\n\n  def literal_evaluator\n    @@literal_evaluator ||= Puppet::Pops::Evaluator::JsonStrictLiteralEvaluator.new\n  end\n\n  def result_of(f)\n    entry = @file_to_result[f]\n    if entry.nil?\n      @file_to_result[f] = entry = parse_file(f)\n    end\n    entry\n  end\n\n  def parse_file(f)\n    return { :error => _(\"The file %{f} does not exist\") % { f: f } } unless Puppet::FileSystem.exist?(f)\n\n    begin\n      parse_result = @parser.parse_file(f)\n      { :classes =>\n        parse_result.definitions.select { |d| d.is_a?(Puppet::Pops::Model::HostClassDefinition) }.map do |d|\n          { :name => d.name,\n            :params => d.parameters.map { |p| extract_param(p) } }\n        end }\n    rescue StandardError => e\n      { :error => e.message }\n    end\n  end\n\n  def extract_param(p)\n    extract_default(extract_type({ :name => p.name }, p), p)\n  end\n\n  def extract_type(structure, p)\n    return structure if p.type_expr.nil?\n\n    structure[:type] = typeexpr_to_string(p.name, p.type_expr)\n    structure\n  end\n\n  def extract_default(structure, p)\n    value_expr = p.value\n    return structure if value_expr.nil?\n\n    default_value = value_as_literal(value_expr)\n    structure[:default_literal] = default_value unless default_value.nil?\n    structure[:default_source] = extract_value_source(value_expr)\n    structure\n  end\n\n  def typeexpr_to_string(name, type_expr)\n    type_parser.interpret_any(type_expr, nil).to_s\n  rescue Puppet::ParseError => e\n    raise Puppet::Error, \"The parameter '$#{name}' is invalid: #{e.message}\", e.backtrace\n  end\n\n  def value_as_literal(value_expr)\n    catch(:not_literal) do\n      return literal_evaluator.literal(value_expr)\n    end\n    nil\n  end\n\n  # Extracts the source for the expression\n  def extract_value_source(value_expr)\n    value_expr.locator.extract_tree_text(value_expr)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/info_service/plan_information_service.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::InfoService::PlanInformationService\n  require_relative '../../puppet/module'\n\n  def self.plans_per_environment(environment_name)\n    # get the actual environment object, raise error if the named env doesn't exist\n    env = Puppet.lookup(:environments).get!(environment_name)\n    env.modules.map do |mod|\n      mod.plans.map do |plan|\n        { :module => { :name => plan.module.name }, :name => plan.name }\n      end\n    end.flatten\n  end\n\n  def self.plan_data(environment_name, module_name, plan_name)\n    # raise EnvironmentNotFound if applicable\n    Puppet.lookup(:environments).get!(environment_name)\n\n    pup_module = Puppet::Module.find(module_name, environment_name)\n    if pup_module.nil?\n      raise Puppet::Module::MissingModule, _(\"Module %{module_name} not found in environment %{environment_name}.\") %\n                                           { module_name: module_name, environment_name: environment_name }\n    end\n\n    plan = pup_module.plans.find { |t| t.name == plan_name }\n    if plan.nil?\n      raise Puppet::Module::Plan::PlanNotFound.new(plan_name, module_name)\n    end\n\n    begin\n      plan.validate\n      { :metadata => plan.metadata, :files => plan.files }\n    rescue Puppet::Module::Plan::Error => err\n      { :metadata => nil, :files => [], :error => err.to_h }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/info_service/task_information_service.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::InfoService::TaskInformationService\n  require_relative '../../puppet/module'\n\n  def self.tasks_per_environment(environment_name)\n    # get the actual environment object, raise error if the named env doesn't exist\n    env = Puppet.lookup(:environments).get!(environment_name)\n\n    env.modules.map do |mod|\n      mod.tasks.map do |task|\n        # If any task is malformed continue to list other tasks in module\n\n        task.validate\n        { :module => { :name => task.module.name }, :name => task.name, :metadata => task.metadata }\n      rescue Puppet::Module::Task::Error => err\n        Puppet.log_exception(err)\n        nil\n      end\n    end.flatten.compact\n  end\n\n  def self.task_data(environment_name, module_name, task_name)\n    # raise EnvironmentNotFound if applicable\n    Puppet.lookup(:environments).get!(environment_name)\n\n    pup_module = Puppet::Module.find(module_name, environment_name)\n    if pup_module.nil?\n      raise Puppet::Module::MissingModule, _(\"Module %{module_name} not found in environment %{environment_name}.\") %\n                                           { module_name: module_name, environment_name: environment_name }\n    end\n\n    task = pup_module.tasks.find { |t| t.name == task_name }\n    if task.nil?\n      raise Puppet::Module::Task::TaskNotFound.new(task_name, module_name)\n    end\n\n    begin\n      task.validate\n      { :metadata => task.metadata, :files => task.files }\n    rescue Puppet::Module::Task::Error => err\n      { :metadata => nil, :files => [], :error => err.to_h }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/info_service.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::InfoService\n  require_relative 'info_service/class_information_service'\n  require_relative 'info_service/task_information_service'\n  require_relative 'info_service/plan_information_service'\n\n  def self.classes_per_environment(env_file_hash)\n    Puppet::InfoService::ClassInformationService.new.classes_per_environment(env_file_hash)\n  end\n\n  def self.tasks_per_environment(environment_name)\n    Puppet::InfoService::TaskInformationService.tasks_per_environment(environment_name)\n  end\n\n  def self.task_data(environment_name, module_name, task_name)\n    Puppet::InfoService::TaskInformationService.task_data(environment_name, module_name, task_name)\n  end\n\n  def self.plans_per_environment(environment_name)\n    Puppet::InfoService::PlanInformationService.plans_per_environment(environment_name)\n  end\n\n  def self.plan_data(environment_name, module_name, plan_name)\n    Puppet::InfoService::PlanInformationService.plan_data(environment_name, module_name, plan_name)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/action.rb",
    "content": "# coding: utf-8\n# frozen_string_literal: true\n\nrequire 'prettyprint'\n\n# This represents an action that is attached to a face. Actions should\n# be constructed by calling {Puppet::Interface::ActionManager#action},\n# which is available on {Puppet::Interface}, and then calling methods of\n# {Puppet::Interface::ActionBuilder} in the supplied block.\n# @api private\nclass Puppet::Interface::Action\n  extend  Puppet::Interface::DocGen\n  include Puppet::Interface::FullDocs\n\n  # @api private\n  def initialize(face, name)\n    raise \"#{name.inspect} is an invalid action name\" unless name.to_s =~ /^[a-z]\\w*$/\n\n    @face    = face\n    @name    = name.to_sym\n\n    # The few bits of documentation we actually demand.  The default license\n    # is a favour to our end users; if you happen to get that in a core face\n    # report it as a bug, please. --daniel 2011-04-26\n    @authors = []\n    @license = 'All Rights Reserved'\n\n    # @options collects the added options in the order they're declared.\n    # @options_hash collects the options keyed by alias for quick lookups.\n    @options = []\n    @display_global_options = []\n    @options_hash   = {}\n    @when_rendering = {}\n  end\n\n  # This is not nice, but it is the easiest way to make us behave like the\n  # Ruby Method object rather than UnboundMethod.  Duplication is vaguely\n  # annoying, but at least we are a shallow clone. --daniel 2011-04-12\n\n  # @return [void]\n  # @api private\n  def __dup_and_rebind_to(to)\n    bound_version = dup\n    bound_version.instance_variable_set(:@face, to)\n    bound_version\n  end\n\n  def to_s() \"#{@face}##{@name}\" end\n\n  # The name of this action\n  # @return [Symbol]\n  attr_reader   :name\n\n  # The face this action is attached to\n  # @return [Puppet::Interface]\n  attr_reader   :face\n\n  # Whether this is the default action for the face\n  # @return [Boolean]\n  # @api private\n  attr_accessor :default\n\n  def default?\n    !!@default\n  end\n\n  ########################################################################\n  # Documentation...\n  attr_doc :returns\n  attr_doc :arguments\n  def synopsis\n    build_synopsis(@face.name, default? ? nil : name, arguments)\n  end\n\n  ########################################################################\n  # Support for rendering formats and all.\n\n  # @api private\n  def when_rendering(type)\n    unless type.is_a? Symbol\n      raise ArgumentError, _(\"The rendering format must be a symbol, not %{class_name}\") % { class_name: type.class.name }\n    end\n    # Do we have a rendering hook for this name?\n    return @when_rendering[type].bind(@face) if @when_rendering.has_key? type\n\n    # How about by another name?\n    alt = type.to_s.sub(/^to_/, '').to_sym\n    return @when_rendering[alt].bind(@face) if @when_rendering.has_key? alt\n\n    # Guess not, nothing to run.\n    nil\n  end\n\n  # @api private\n  def set_rendering_method_for(type, proc)\n    unless proc.is_a? Proc\n      msg = if proc.nil?\n              # TRANSLATORS 'set_rendering_method_for' and 'Proc' should not be translated\n              _(\"The second argument to set_rendering_method_for must be a Proc\")\n            else\n              # TRANSLATORS 'set_rendering_method_for' and 'Proc' should not be translated\n              _(\"The second argument to set_rendering_method_for must be a Proc, not %{class_name}\") %\n                { class_name: proc.class.name }\n            end\n      raise ArgumentError, msg\n    end\n\n    if proc.arity != 1 and proc.arity != (@positional_arg_count + 1)\n      msg = if proc.arity < 0 then\n              # TRANSLATORS 'when_rendering', 'when_invoked' are method names and should not be translated\n              _(\"The when_rendering method for the %{face} face %{name} action takes either just one argument,\"\\\n                \" the result of when_invoked, or the result plus the %{arg_count} arguments passed to the\"\\\n                \" when_invoked block, not a variable number\") %\n                { face: @face.name, name: name, arg_count: @positional_arg_count }\n            else\n              # TRANSLATORS 'when_rendering', 'when_invoked' are method names and should not be translated\n              _(\"The when_rendering method for the %{face} face %{name} action takes either just one argument,\"\\\n                \" the result of when_invoked, or the result plus the %{arg_count} arguments passed to the\"\\\n                \" when_invoked block, not %{string}\") %\n                { face: @face.name, name: name, arg_count: @positional_arg_count, string: proc.arity.to_s }\n            end\n      raise ArgumentError, msg\n    end\n    unless type.is_a? Symbol\n      raise ArgumentError, _(\"The rendering format must be a symbol, not %{class_name}\") % { class_name: type.class.name }\n    end\n    if @when_rendering.has_key? type then\n      raise ArgumentError, _(\"You can't define a rendering method for %{type} twice\") % { type: type }\n    end\n\n    # Now, the ugly bit.  We add the method to our interface object, and\n    # retrieve it, to rotate through the dance of getting a suitable method\n    # object out of the whole process. --daniel 2011-04-18\n    @when_rendering[type] =\n      @face.__send__(:__add_method, __render_method_name_for(type), proc)\n  end\n\n  # @return [void]\n  # @api private\n  def __render_method_name_for(type)\n    :\"#{name}_when_rendering_#{type}\"\n  end\n  private :__render_method_name_for\n\n  # @api private\n  # @return [Symbol]\n  attr_reader :render_as\n\n  def render_as=(value)\n    @render_as = value.to_sym\n  end\n\n  # @api private\n  # @return [void]\n  def deprecate\n    @deprecated = true\n  end\n\n  # @api private\n  # @return [Boolean]\n  def deprecated?\n    @deprecated\n  end\n\n  ########################################################################\n  # Initially, this was defined to allow the @action.invoke pattern, which is\n  # a very natural way to invoke behaviour given our introspection\n  # capabilities.   Heck, our initial plan was to have the faces delegate to\n  # the action object for invocation and all.\n  #\n  # It turns out that we have a binding problem to solve: @face was bound to\n  # the parent class, not the subclass instance, and we don't pass the\n  # appropriate context or change the binding enough to make this work.\n  #\n  # We could hack around it, by either mandating that you pass the context in\n  # to invoke, or try to get the binding right, but that has probably got\n  # subtleties that we don't instantly think of – especially around threads.\n  #\n  # So, we are pulling this method for now, and will return it to life when we\n  # have the time to resolve the problem.  For now, you should replace...\n  #\n  #     @action = @face.get_action(name)\n  #     @action.invoke(arg1, arg2, arg3)\n  #\n  # ...with...\n  #\n  #     @action = @face.get_action(name)\n  #     @face.send(@action.name, arg1, arg2, arg3)\n  #\n  # I understand that is somewhat cumbersome, but it functions as desired.\n  # --daniel 2011-03-31\n  #\n  # PS: This code is left present, but commented, to support this chunk of\n  # documentation, for the benefit of the reader.\n  #\n  # def invoke(*args, &block)\n  #   @face.send(name, *args, &block)\n  # end\n\n  # We need to build an instance method as a wrapper, using normal code, to be\n  # able to expose argument defaulting between the caller and definer in the\n  # Ruby API.  An extra method is, sadly, required for Ruby 1.8 to work since\n  # it doesn't expose bind on a block.\n  #\n  # Hopefully we can improve this when we finally shuffle off the last of Ruby\n  # 1.8 support, but that looks to be a few \"enterprise\" release eras away, so\n  # we are pretty stuck with this for now.\n  #\n  # Patches to make this work more nicely with Ruby 1.9 using runtime version\n  # checking and all are welcome, provided that they don't change anything\n  # outside this little ol' bit of code and all.\n  #\n  # Incidentally, we though about vendoring evil-ruby and actually adjusting\n  # the internal C structure implementation details under the hood to make\n  # this stuff work, because it would have been cleaner.  Which gives you an\n  # idea how motivated we were to make this cleaner.  Sorry.\n  # --daniel 2011-03-31\n\n  # The arity of the action\n  # @return [Integer]\n  attr_reader :positional_arg_count\n\n  # The block that is executed when the action is invoked\n  # @return [block]\n  attr_reader :when_invoked\n\n  def when_invoked=(block)\n    internal_name = \"#{@name} implementation, required on Ruby 1.8\".to_sym\n\n    arity = @positional_arg_count = block.arity\n    if arity == 0 then\n      # This will never fire on 1.8.7, which treats no arguments as \"*args\",\n      # but will on 1.9.2, which treats it as \"no arguments\".  Which bites,\n      # because this just begs for us to wind up in the horrible situation\n      # where a 1.8 vs 1.9 error bites our end users. --daniel 2011-04-19\n      # TRANSLATORS 'when_invoked' should not be translated\n      raise ArgumentError, _(\"when_invoked requires at least one argument (options) for action %{name}\") % { name: @name }\n    elsif arity > 0 then\n      range = Range.new(1, arity - 1)\n      decl = range.map { |x| \"arg#{x}\" } << \"options = {}\"\n      optn = \"\"\n      args = \"[\" + (range.map { |x| \"arg#{x}\" } << \"options\").join(\", \") + \"]\"\n    else\n      range = Range.new(1, arity.abs - 1)\n      decl = range.map { |x| \"arg#{x}\" } << \"*rest\"\n      optn = \"rest << {} unless rest.last.is_a?(Hash)\"\n      if arity == -1 then\n        args = \"rest\"\n      else\n        args = \"[\" + range.map { |x| \"arg#{x}\" }.join(\", \") + \"] + rest\"\n      end\n    end\n\n    file    = __FILE__ + \"+eval[wrapper]\"\n    line    = __LINE__ + 2 # <== points to the same line as 'def' in the wrapper.\n    wrapper = <<~WRAPPER\n      def #{@name}(#{decl.join(', ')})\n        #{optn}\n        args    = #{args}\n        action  = get_action(#{name.inspect})\n        args   << action.validate_and_clean(args.pop)\n        __invoke_decorations(:before, action, args, args.last)\n        rval = self.__send__(#{internal_name.inspect}, *args)\n        __invoke_decorations(:after, action, args, args.last)\n        return rval\n      end\n    WRAPPER\n\n    # It should be possible to rewrite this code to use `define_method`\n    # instead of `class/instance_eval` since Ruby 1.8 is long dead.\n    if @face.is_a?(Class)\n      @face.class_eval do eval wrapper, nil, file, line end # rubocop:disable Security/Eval\n      @face.send(:define_method, internal_name, &block)\n      @when_invoked = @face.instance_method(name)\n    else\n      @face.instance_eval do eval wrapper, nil, file, line end # rubocop:disable Security/Eval\n      @face.meta_def(internal_name, &block)\n      @when_invoked = @face.method(name).unbind\n    end\n  end\n\n  def add_option(option)\n    option.aliases.each do |name|\n      conflict = get_option(name)\n      if conflict\n        raise ArgumentError, _(\"Option %{option} conflicts with existing option %{conflict}\") %\n                             { option: option, conflict: conflict }\n      else\n        conflict = @face.get_option(name)\n        if conflict\n          raise ArgumentError, _(\"Option %{option} conflicts with existing option %{conflict} on %{face}\") %\n                               { option: option, conflict: conflict, face: @face }\n        end\n      end\n    end\n\n    @options << option.name\n\n    option.aliases.each do |name|\n      @options_hash[name] = option\n    end\n\n    option\n  end\n\n  def option?(name)\n    @options_hash.include? name.to_sym\n  end\n\n  def options\n    @face.options + @options\n  end\n\n  def add_display_global_options(*args)\n    @display_global_options ||= []\n    [args].flatten.each do |refopt|\n      unless Puppet.settings.include? refopt\n        # TRANSLATORS 'Puppet.settings' should not be translated\n        raise ArgumentError, _(\"Global option %{option} does not exist in Puppet.settings\") % { option: refopt }\n      end\n\n      @display_global_options << refopt\n    end\n    @display_global_options.uniq!\n    @display_global_options\n  end\n\n  def display_global_options(*args)\n    args ? add_display_global_options(args) : @display_global_options + @face.display_global_options\n  end\n  alias :display_global_option :display_global_options\n\n  def get_option(name, with_inherited_options = true)\n    option = @options_hash[name.to_sym]\n    if option.nil? and with_inherited_options\n      option = @face.get_option(name)\n    end\n    option\n  end\n\n  def validate_and_clean(original)\n    # The final set of arguments; effectively a hand-rolled shallow copy of\n    # the original, which protects the caller from the surprises they might\n    # get if they passed us a hash and we mutated it...\n    result = {}\n\n    # Check for multiple aliases for the same option, and canonicalize the\n    # name of the argument while we are about it.\n    overlap = Hash.new do |h, k| h[k] = [] end\n    unknown = []\n    original.keys.each do |name|\n      option = get_option(name)\n      if option\n        canonical = option.name\n        if result.has_key? canonical\n          overlap[canonical] << name\n        else\n          result[canonical] = original[name]\n        end\n      elsif Puppet.settings.include? name\n        result[name] = original[name]\n      else\n        unknown << name\n      end\n    end\n\n    unless overlap.empty?\n      overlap_list = overlap.map { |k, v| \"(#{k}, #{v.sort.join(', ')})\" }.join(\", \")\n      raise ArgumentError, _(\"Multiple aliases for the same option passed: %{overlap_list}\") %\n                           { overlap_list: overlap_list }\n    end\n\n    unless unknown.empty?\n      unknown_list = unknown.sort.join(\", \")\n      raise ArgumentError, _(\"Unknown options passed: %{unknown_list}\") % { unknown_list: unknown_list }\n    end\n\n    # Inject default arguments and check for missing mandating options.\n    missing = []\n    options.map { |x| get_option(x) }.each do |option|\n      name = option.name\n      next if result.has_key? name\n\n      if option.has_default?\n        result[name] = option.default\n      elsif option.required?\n        missing << name\n      end\n    end\n\n    unless missing.empty?\n      missing_list = missing.sort.join(', ')\n      raise ArgumentError, _(\"The following options are required: %{missing_list}\") % { missing_list: missing_list }\n    end\n\n    # All done.\n    result\n  end\n\n  ########################################################################\n  # Support code for action decoration; see puppet/interface.rb for the gory\n  # details of why this is hidden away behind private. --daniel 2011-04-15\n  private\n\n  # @return [void]\n  # @api private\n  def __add_method(name, proc)\n    @face.__send__ :__add_method, name, proc\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/action_builder.rb",
    "content": "# frozen_string_literal: true\n\n# This class is used to build {Puppet::Interface::Action actions}.\n# When an action is defined with\n# {Puppet::Interface::ActionManager#action} the block is evaluated\n# within the context of a new instance of this class.\n# @api public\nclass Puppet::Interface::ActionBuilder\n  extend Forwardable\n\n  # The action under construction\n  # @return [Puppet::Interface::Action]\n  # @api private\n  attr_reader :action\n\n  # Builds a new action.\n  # @return [Puppet::Interface::Action]\n  # @api private\n  def self.build(face, name, &block)\n    raise \"Action #{name.inspect} must specify a block\" unless block\n\n    new(face, name, &block).action\n  end\n\n  # Deprecates the action\n  # @return [void]\n  # @api private\n  # @dsl Faces\n  def deprecate\n    @action.deprecate\n  end\n\n  # Ideally the method we're defining here would be added to the action, and a\n  # method on the face would defer to it, but we can't get scope correct, so\n  # we stick with this. --daniel 2011-03-24\n\n  # Sets what the action does when it is invoked. This takes a block\n  # which will be called when the action is invoked. The action will\n  # accept arguments based on the arity of the block. It should always\n  # take at least one argument for options. Options will be the last\n  # argument.\n  #\n  # @overload when_invoked({|options| ... })\n  #   An action with no arguments\n  # @overload when_invoked({|arg1, arg2, options| ... })\n  #   An action with two arguments\n  # @return [void]\n  # @api public\n  # @dsl Faces\n  def when_invoked(&block)\n    @action.when_invoked = block\n  end\n\n  # Sets a block to be run at the rendering stage, for a specific\n  # rendering type (eg JSON, YAML, console), after the block for\n  # when_invoked gets run. This manipulates the value returned by the\n  # action. It makes it possible to work around limitations in the\n  # underlying object returned, and should be avoided in favor of\n  # returning a more capable object.\n  # @api private\n  # @todo this needs more\n  # @dsl Faces\n  def when_rendering(type = nil, &block)\n    if type.nil? then # the default error message sucks --daniel 2011-04-18\n      # TRANSLATORS 'when_rendering' is a method name and should not be translated\n      raise ArgumentError, _('You must give a rendering format to when_rendering')\n    end\n    if block.nil? then\n      # TRANSLATORS 'when_rendering' is a method name and should not be translated\n      raise ArgumentError, _('You must give a block to when_rendering')\n    end\n\n    @action.set_rendering_method_for(type, block)\n  end\n\n  # Declare that this action can take a specific option, and provide the\n  # code to do so.  One or more strings are given, in the style of\n  # OptionParser (see example). These strings are parsed to derive a\n  # name for the option. Any `-` characters within the option name (ie\n  # excluding the initial `-` or `--` for an option) will be translated\n  # to `_`.The first long option will be used as the name, and the rest\n  # are retained as aliases. The original form of the option is used\n  # when invoking the face, the translated form is used internally.\n  #\n  # When the action is invoked the value of the option is available in\n  # a hash passed to the {Puppet::Interface::ActionBuilder#when_invoked\n  # when_invoked} block, using the option name in symbol form as the\n  # hash key.\n  #\n  # The block to this method is used to set attributes for the option\n  # (see {Puppet::Interface::OptionBuilder}).\n  #\n  # @param declaration [String] Option declarations, as described above\n  #   and in the example.\n  #\n  # @example Say hi\n  #   action :say_hi do\n  #     option \"-u USER\", \"--user-name USER\" do\n  #       summary \"Who to say hi to\"\n  #     end\n  #\n  #     when_invoked do |options|\n  #       \"Hi, #{options[:user_name]}\"\n  #     end\n  #   end\n  # @api public\n  # @dsl Faces\n  def option(*declaration, &block)\n    option = Puppet::Interface::OptionBuilder.build(@action, *declaration, &block)\n    @action.add_option(option)\n  end\n\n  # Set this as the default action for the face.\n  # @api public\n  # @dsl Faces\n  # @return [void]\n  def default(value = true)\n    @action.default = !!value\n  end\n\n  # @api private\n  def display_global_options(*args)\n    @action.add_display_global_options args\n  end\n  alias :display_global_option :display_global_options\n\n  # Sets the default rendering format\n  # @api private\n  def render_as(value = nil)\n    if value.nil?\n      # TRANSLATORS 'render_as' is a method name and should not be translated\n      raise ArgumentError, _(\"You must give a rendering format to render_as\")\n    end\n\n    formats = Puppet::Network::FormatHandler.formats\n    unless formats.include? value\n      raise ArgumentError, _(\"%{value} is not a valid rendering format: %{formats_list}\") %\n                           { value: value.inspect, formats_list: formats.sort.join(\", \") }\n    end\n\n    @action.render_as = value\n  end\n\n  # Metaprogram the simple DSL from the target class.\n  Puppet::Interface::Action.instance_methods.grep(/=$/).each do |setter|\n    next if setter =~ /^=/\n\n    property = setter.to_s.chomp('=')\n\n    unless method_defined? property\n      # ActionBuilder#<property> delegates to Action#<setter>\n      def_delegator :@action, setter, property\n    end\n  end\n\n  private\n\n  def initialize(face, name, &block)\n    @face   = face\n    @action = Puppet::Interface::Action.new(face, name)\n    instance_eval(&block)\n    unless @action.when_invoked\n      # TRANSLATORS 'when_invoked' is a method name and should not be translated and 'block' is a Ruby code block\n      raise ArgumentError, _(\"actions need to know what to do when_invoked; please add the block\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/action_manager.rb",
    "content": "# frozen_string_literal: true\n\n# This class is not actually public API, but the method\n# {Puppet::Interface::ActionManager#action action} is public when used\n# as part of the Faces DSL (i.e. from within a\n# {Puppet::Interface.define define} block).\n# @api public\nmodule Puppet::Interface::ActionManager\n  # Declare that this app can take a specific action, and provide\n  # the code to do so.\n\n  # Defines a new action. This takes a block to build the action using\n  # the methods on {Puppet::Interface::ActionBuilder}.\n  # @param name [Symbol] The name that will be used to invoke the\n  #   action\n  # @overload action(name, { block })\n  # @return [void]\n  # @api public\n  # @dsl Faces\n  def action(name, &block)\n    @actions ||= {}\n    Puppet.debug _(\"Redefining action %{name} for %{self}\") % { name: name, self: self } if action?(name)\n\n    action = Puppet::Interface::ActionBuilder.build(self, name, &block)\n\n    # REVISIT: (#18042) doesn't this mean we can't redefine the default action? -- josh\n    current = get_default_action if action.default\n    if current\n      raise \"Actions #{current.name} and #{name} cannot both be default\"\n    end\n\n    @actions[action.name] = action\n  end\n\n  # Returns the list of available actions for this face.\n  # @return [Array<Symbol>] The names of the actions for this face\n  # @api private\n  def actions\n    @actions ||= {}\n    result = @actions.keys\n\n    if is_a?(Class) and superclass.respond_to?(:actions)\n      result += superclass.actions\n    elsif self.class.respond_to?(:actions)\n      result += self.class.actions\n    end\n    # We need to uniq the result, because we duplicate actions when they are\n    # fetched to ensure that they have the correct bindings; they shadow the\n    # parent, and uniq implements that. --daniel 2011-06-01\n    (result - @deactivated_actions.to_a).uniq.sort\n  end\n\n  # Retrieves a named action\n  # @param name [Symbol] The name of the action\n  # @return [Puppet::Interface::Action] The action object\n  # @api private\n  def get_action(name)\n    @actions ||= {}\n    result = @actions[name.to_sym]\n    if result.nil?\n      if is_a?(Class) and superclass.respond_to?(:get_action)\n        found = superclass.get_action(name)\n      elsif self.class.respond_to?(:get_action)\n        found = self.class.get_action(name)\n      end\n\n      if found then\n        # This is not the nicest way to make action equivalent to the Ruby\n        # Method object, rather than UnboundMethod, but it will do for now,\n        # and we only have to make this change in *one* place. --daniel 2011-04-12\n        result = @actions[name.to_sym] = found.__dup_and_rebind_to(self)\n      end\n    end\n    result\n  end\n\n  # Retrieves the default action for the face\n  # @return [Puppet::Interface::Action]\n  # @api private\n  def get_default_action\n    default = actions.map { |x| get_action(x) }.select(&:default)\n    if default.length > 1\n      raise \"The actions #{default.map(&:name).join(', ')} cannot all be default\"\n    end\n\n    default.first\n  end\n\n  # Deactivate a named action\n  # @return [Puppet::Interface::Action]\n  # @api public\n  def deactivate_action(name)\n    @deactivated_actions ||= Set.new\n    @deactivated_actions.add name.to_sym\n  end\n\n  # @api private\n  def action?(name)\n    actions.include?(name.to_sym)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/documentation.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Interface\n  # @api private\n  module DocGen\n    require_relative '../../puppet/util/docs'\n\n    # @api private\n    def self.strip_whitespace(text)\n      # I don't want no...\n      Puppet::Util::Docs.scrub(text)\n    end\n\n    # The documentation attributes all have some common behaviours; previously\n    # we open-coded them across the set of six things, but that seemed\n    # wasteful - especially given that they were literally the same, and had\n    # the same bug hidden in them.\n    #\n    # This feels a bit like overkill, but at least the common code is common\n    # now. --daniel 2011-04-29\n\n    # @api private\n    def attr_doc(name, &validate)\n      # Now, which form of the setter do we want, validated or not?\n      get_arg = \"value.to_s\"\n      if validate\n        define_method(:\"_validate_#{name}\", validate)\n        get_arg = \"_validate_#{name}(#{get_arg})\"\n      end\n\n      # We use module_eval, which I don't like much, because we can't have an\n      # argument to a block with a default value in Ruby 1.8, and I don't like\n      # the side-effects (eg: no argument count validation) of using blocks\n      # without as methods.  When we are 1.9 only (hah!) you can totally\n      # replace this with some up-and-up define_method. --daniel 2011-04-29\n      module_eval(<<-EOT, __FILE__, __LINE__ + 1)\n        def #{name}(value = nil)                 # def attribute(value=nil)\n          self.#{name} = value unless value.nil? #   self.attribute = value unless value.nil?\n          @#{name}                               #   @value\n        end                                      # end\n\n        def #{name}=(value)                                                 # def attribute=(value)\n          @#{name} = Puppet::Interface::DocGen.strip_whitespace(#{get_arg}) #   @value = Puppet::Interface::DocGen.strip_whitespace(#{get_arg})\n        end                                                                 # end\n      EOT\n    end\n  end\n\n  # This module can be mixed in to provide a minimal set of\n  # documentation attributes.\n  # @api public\n  module TinyDocs\n    extend Puppet::Interface::DocGen\n\n    # @!method summary(summary)\n    # Sets a summary of this object.\n    # @api public\n    # @dsl Faces\n    attr_doc :summary do |value|\n      value =~ /\\n/ and\n        # TRANSLATORS 'Face' refers to a programming API in Puppet, 'summary' and 'description' are specifc attribute names and should not be translated\n        raise ArgumentError, _(\"Face summary should be a single line; put the long text in 'description' instead.\")\n      value\n    end\n\n    # @!method description(description)\n    # Sets the long description of this object.\n    # @param description [String] The description of this object.\n    # @api public\n    # @dsl Faces\n    attr_doc :description\n\n    # @api private\n    def build_synopsis(face, action = nil, arguments = nil)\n      PrettyPrint.format do |s|\n        s.text(\"puppet #{face}\")\n        s.text(\" #{action}\") unless action.nil?\n        s.text(\" \")\n\n        options.each do |option|\n          option = get_option(option)\n          wrap = option.required? ? %w[< >] : %w{[ ]}\n\n          s.group(0, *wrap) do\n            option.optparse.each do |item|\n              unless s.current_group.first?\n                s.breakable\n                s.text '|'\n                s.breakable\n              end\n              s.text item\n            end\n          end\n\n          s.breakable\n        end\n\n        display_global_options.sort.each do |option|\n          wrap = %w{[ ]}\n          s.group(0, *wrap) do\n            type = Puppet.settings.setting(option).default\n            type ||= Puppet.settings.setting(option).type.to_s.upcase\n            s.text \"--#{option} #{type}\"\n            s.breakable\n          end\n          s.breakable\n        end\n\n        if arguments then\n          s.text arguments.to_s\n        end\n      end\n    end\n  end\n\n  # This module can be mixed in to provide a full set of documentation\n  # attributes. It is intended to be used for {Puppet::Interface}.\n  # @api public\n  module FullDocs\n    extend Puppet::Interface::DocGen\n    include TinyDocs\n\n    # @!method examples\n    # @overload examples(text)\n    #   Sets examples.\n    #   @param text [String] Example text\n    #   @api public\n    #   @return [void]\n    #   @dsl Faces\n    # @overload examples\n    #   Returns documentation of examples\n    #   @return [String] The examples\n    #   @api private\n    attr_doc :examples\n\n    # @!method notes(text)\n    # @overload notes(text)\n    #   Sets optional notes.\n    #   @param text [String] The notes\n    #   @api public\n    #   @return [void]\n    #   @dsl Faces\n    # @overload notes\n    #   Returns any optional notes\n    #   @return [String] The notes\n    #   @api private\n    attr_doc :notes\n\n    # @!method license(text)\n    # @overload license(text)\n    #   Sets the license text\n    #   @param text [String] the license text\n    #   @api public\n    #   @return [void]\n    #   @dsl Faces\n    # @overload license\n    #   Returns the license\n    #   @return [String] The license\n    #   @api private\n    attr_doc :license\n\n    attr_doc :short_description\n    # @overload short_description(value)\n    #   Sets a short description for this object.\n    #   @param value [String, nil] A short description (about a paragraph)\n    #     of this component. If `value` is `nil` the short_description\n    #     will be set to the shorter of the first paragraph or the first\n    #     five lines of {description}.\n    #   @return [void]\n    #   @api public\n    #   @dsl Faces\n    # @overload short_description\n    #   Get the short description for this object\n    #   @return [String, nil] The short description of this object. If none is\n    #     set it will be derived from {description}. Returns `nil` if\n    #     {description} is `nil`.\n    #   @api private\n    def short_description(value = nil)\n      self.short_description = value unless value.nil?\n      if @short_description.nil? then\n        return nil if @description.nil?\n\n        lines = @description.split(\"\\n\")\n        first_paragraph_break = lines.index('') || 5\n        grab = [5, first_paragraph_break].min\n        @short_description = lines[0, grab].join(\"\\n\")\n        @short_description += ' [...]' if grab < lines.length and first_paragraph_break >= 5\n      end\n      @short_description\n    end\n\n    # @overload author(value)\n    #   Adds an author to the documentation for this object. To set\n    #   multiple authors, call this once for each author.\n    #   @param value [String] the name of the author\n    #   @api public\n    #   @dsl Faces\n    # @overload author\n    #   Returns a list of authors\n    #   @return [String, nil] The names of all authors separated by\n    #     newlines, or `nil` if no authors have been set.\n    #   @api private\n    def author(value = nil)\n      unless value.nil? then\n        unless value.is_a? String\n          # TRANSLATORS 'author' is an attribute name and should not be translated\n          raise ArgumentError, _('author must be a string; use multiple statements for multiple authors')\n        end\n\n        if value =~ /\\n/ then\n          # TRANSLATORS 'author' is an attribute name and should not be translated\n          raise ArgumentError, _('author should be a single line; use multiple statements for multiple authors')\n        end\n\n        @authors.push(Puppet::Interface::DocGen.strip_whitespace(value))\n      end\n      @authors.empty? ? nil : @authors.join(\"\\n\")\n    end\n\n    # Returns a list of authors. See {author}.\n    # @return [String] The list of authors, separated by newlines.\n    # @api private\n    def authors\n      @authors\n    end\n\n    # @api private\n    def author=(value)\n      # I think it's a bug that this ends up being the exposed\n      # version of `author` on ActionBuilder\n      if Array(value).any? { |x| x =~ /\\n/ } then\n        # TRANSLATORS 'author' is an attribute name and should not be translated\n        raise ArgumentError, _('author should be a single line; use multiple statements')\n      end\n\n      @authors = Array(value).map { |x| Puppet::Interface::DocGen.strip_whitespace(x) }\n    end\n    alias :authors= :author=\n\n    # Sets the copyright owner and year. This returns the copyright\n    # string, so it can be called with no arguments retrieve that string\n    # without side effects.\n    # @param owner [String, Array<String>] The copyright owner or an\n    #   array of owners\n    # @param years [Integer, Range<Integer>, Array<Integer,Range<Integer>>]\n    #   The copyright year or years. Years can be specified with integers,\n    #   a range of integers, or an array of integers and ranges of\n    #   integers.\n    # @return [String] A string describing the copyright on this object.\n    # @api public\n    # @dsl Faces\n    def copyright(owner = nil, years = nil)\n      if years.nil? and !owner.nil? then\n        # TRANSLATORS 'copyright' is an attribute name and should not be translated\n        raise ArgumentError, _('copyright takes the owners names, then the years covered')\n      end\n\n      self.copyright_owner = owner unless owner.nil?\n      self.copyright_years = years unless years.nil?\n\n      if copyright_years or copyright_owner then\n        \"Copyright #{copyright_years} by #{copyright_owner}\"\n      else\n        \"Unknown copyright owner and years.\"\n      end\n    end\n\n    # Sets the copyright owner\n    # @param value [String, Array<String>] The copyright owner or\n    #   owners.\n    # @return [String] Comma-separated list of copyright owners\n    # @api private\n    attr_reader :copyright_owner\n\n    def copyright_owner=(value)\n      case value\n      when String then @copyright_owner = value\n      when Array  then @copyright_owner = value.join(\", \")\n      else\n        # TRANSLATORS 'copyright' is an attribute name and should not be translated\n        raise ArgumentError, _(\"copyright owner must be a string or an array of strings\")\n      end\n    end\n\n    # Sets the copyright year\n    # @param value [Integer, Range<Integer>, Array<Integer, Range>] The\n    #   copyright year or years.\n    # @return [String]\n    # @api private\n    attr_reader :copyright_years\n\n    def copyright_years=(value)\n      years = munge_copyright_year value\n      years = (years.is_a?(Array) ? years : [years])\n              .sort_by do |x| x.is_a?(Range) ? x.first : x end\n\n      @copyright_years = years.map do |year|\n        if year.is_a? Range then\n          \"#{year.first}-#{year.last}\"\n        else\n          year\n        end\n      end.join(\", \")\n    end\n\n    # @api private\n    def munge_copyright_year(input)\n      case input\n      when Range then input\n      when Integer then\n        if input < 1970 then\n          fault = \"before 1970\"\n        elsif input > (future = Time.now.year + 2) then\n          fault = \"after #{future}\"\n        end\n        if fault then\n          # TRANSLATORS 'copyright' is an attribute name and should not be translated\n          raise ArgumentError, _(\"copyright with a year %{value} is very strange; did you accidentally add or subtract two years?\") %\n                               { value: fault }\n        end\n\n        input\n\n      when String then\n        input.strip.split(/,/).map do |part|\n          part = part.strip\n          if part =~ /^\\d+$/\n            part.to_i\n          else\n            found = part.split(/-/)\n            if found\n              unless found.length == 2 and found.all? { |x| x.strip =~ /^\\d+$/ }\n                # TRANSLATORS 'copyright' is an attribute name and should not be translated\n                raise ArgumentError, _(\"%{value} is not a good copyright year or range\") % { value: part.inspect }\n              end\n\n              Range.new(found[0].to_i, found[1].to_i)\n            else\n              # TRANSLATORS 'copyright' is an attribute name and should not be translated\n              raise ArgumentError, _(\"%{value} is not a good copyright year or range\") % { value: part.inspect }\n            end\n          end\n        end\n\n      when Array then\n        result = []\n        input.each do |item|\n          item = munge_copyright_year item\n          if item.is_a? Array\n            result.concat item\n          else\n            result << item\n          end\n        end\n        result\n\n      else\n        # TRANSLATORS 'copyright' is an attribute name and should not be translated\n        raise ArgumentError, _(\"%{value} is not a good copyright year, set, or range\") % { value: input.inspect }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/face_collection.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Interface::FaceCollection\n  @faces = Hash.new { |hash, key| hash[key] = {} }\n\n  @loader = Puppet::Util::Autoload.new(:application, 'puppet/face')\n\n  def self.faces\n    unless @loaded\n      @loaded = true\n      names = @loader.files_to_load(Puppet.lookup(:current_environment)).map do |fn|\n        ::File.basename(fn, '.rb')\n      end.uniq\n      names.each { |name| self[name, :current] }\n    end\n    @faces.keys.select { |name| @faces[name].length > 0 }\n  end\n\n  def self.[](name, version)\n    name = underscorize(name)\n    get_face(name, version) or load_face(name, version)\n  end\n\n  def self.get_action_for_face(name, action_name, version)\n    name = underscorize(name)\n\n    # If the version they request specifically doesn't exist, don't search\n    # elsewhere.  Usually this will start from :current and all...\n    face = self[name, version]\n    return nil unless face\n\n    action = face.get_action(action_name)\n    unless action\n      # ...we need to search for it bound to an o{lder,ther} version.  Since\n      # we load all actions when the face is first references, this will be in\n      # memory in the known set of versions of the face.\n      (@faces[name].keys - [:current]).sort.reverse_each do |vers|\n        action = @faces[name][vers].get_action(action_name)\n        break if action\n      end\n    end\n\n    action\n  end\n\n  # get face from memory, without loading.\n  def self.get_face(name, pattern)\n    return nil unless @faces.has_key? name\n    return @faces[name][:current] if pattern == :current\n\n    versions = @faces[name].keys - [:current]\n    range = pattern.is_a?(SemanticPuppet::Version) ? SemanticPuppet::VersionRange.new(pattern, pattern) : SemanticPuppet::VersionRange.parse(pattern)\n    found = find_matching(range, versions)\n    @faces[name][found]\n  end\n\n  def self.find_matching(range, versions)\n    versions.select { |v| range === v }.max\n  end\n\n  # try to load the face, and return it.\n  def self.load_face(name, version)\n    # We always load the current version file; the common case is that we have\n    # the expected version and any compatibility versions in the same file,\n    # the default.  Which means that this is almost always the case.\n    #\n    # We use require to avoid executing the code multiple times, like any\n    # other Ruby library that we might want to use.  --daniel 2011-04-06\n    if safely_require name then\n      # If we wanted :current, we need to index to find that; direct version\n      # requests just work as they go. --daniel 2011-04-06\n      if version == :current then\n        # We need to find current out of this.  This is the largest version\n        # number that doesn't have a dedicated on-disk file present; those\n        # represent \"experimental\" versions of faces, which we don't fully\n        # support yet.\n        #\n        # We walk the versions from highest to lowest and take the first version\n        # that is not defined in an explicitly versioned file on disk as the\n        # current version.\n        #\n        # This constrains us to only ship experimental versions with *one*\n        # version in the file, not multiple, but given you can't reliably load\n        # them except by side-effect when you ignore that rule this seems safe\n        # enough...\n        #\n        # Given those constraints, and that we are not going to ship a versioned\n        # interface that is not :current in this release, we are going to leave\n        # these thoughts in place, and just punt on the actual versioning.\n        #\n        # When we upgrade the core to support multiple versions we can solve the\n        # problems then; as lazy as possible.\n        #\n        # We do support multiple versions in the same file, though, so we sort\n        # versions here and return the last item in that set.\n        #\n        # --daniel 2011-04-06\n        latest_ver = @faces[name].keys.max\n        @faces[name][:current] = @faces[name][latest_ver]\n      end\n    end\n\n    unless version == :current or get_face(name, version)\n      # Try an obsolete version of the face, if needed, to see if that helps?\n      safely_require name, version\n    end\n\n    get_face(name, version)\n  end\n\n  def self.safely_require(name, version = nil)\n    path = @loader.expand(version ? ::File.join(version.to_s, name.to_s) : name)\n    require path\n    true\n  rescue LoadError => e\n    raise unless e.message =~ /-- #{path}$/\n\n    # ...guess we didn't find the file; return a much better problem.\n    nil\n  rescue SyntaxError => e\n    raise unless e.message =~ /#{path}\\.rb:\\d+: /\n\n    Puppet.err _(\"Failed to load face %{name}:\\n%{detail}\") % { name: name, detail: e }\n    # ...but we just carry on after complaining.\n    nil\n  end\n\n  def self.register(face)\n    @faces[underscorize(face.name)][face.version] = face\n  end\n\n  def self.underscorize(name)\n    unless name.to_s =~ /^[-_a-z][-_a-z0-9]*$/i then\n      # TRANSLATORS 'face' refers to a programming API in Puppet\n      raise ArgumentError, _(\"%{name} (%{class_name}) is not a valid face name\") %\n                           { name: name.inspect, class_name: name.class }\n    end\n\n    name.to_s.downcase.split(/[-_]/).join('_').to_sym\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/option.rb",
    "content": "# frozen_string_literal: true\n\n# This represents an option on an action or face (to be globally applied\n# to its actions). Options should be constructed by calling\n# {Puppet::Interface::OptionManager#option}, which is available on\n# {Puppet::Interface}, and then calling methods of\n# {Puppet::Interface::OptionBuilder} in the supplied block.\n# @api public\nclass Puppet::Interface::Option\n  include Puppet::Interface::TinyDocs\n\n  # @api private\n  def initialize(parent, *declaration, &block)\n    @parent   = parent\n    @optparse = []\n    @default  = nil\n\n    # Collect and sort the arguments in the declaration.\n    dups = {}\n    declaration.each do |item|\n      if item.is_a? String and item.to_s =~ /^-/ then\n        unless item =~ /^-[a-z]\\b/ or item =~ /^--[^-]/ then\n          raise ArgumentError, _(\"%{option}: long options need two dashes (--)\") % { option: item.inspect }\n        end\n\n        @optparse << item\n\n        # Duplicate checking...\n        # for our duplicate checking purpose, we don't make a check with the\n        # translated '-' -> '_'. Right now, we do that on purpose because of\n        # a duplicated option made publicly available on certificate and ca\n        # faces for dns alt names. Puppet defines 'dns_alt_names', those\n        # faces include 'dns-alt-names'.  We can't get rid of 'dns-alt-names'\n        # yet, so we need to do our duplicate checking on the untranslated\n        # version of the option.\n        # jeffweiss 17 april 2012\n        name = optparse_to_optionname(item)\n        if Puppet.settings.include? name then\n          raise ArgumentError, _(\"%{option}: already defined in puppet\") % { option: item.inspect }\n        end\n\n        dup = dups[name]\n        if dup\n          raise ArgumentError, _(\"%{option}: duplicates existing alias %{duplicate} in %{parent}\") %\n                               { option: item.inspect, duplicate: dup.inspect, parent: @parent }\n        else\n          dups[name] = item\n        end\n      else\n        raise ArgumentError, _(\"%{option} is not valid for an option argument\") % { option: item.inspect }\n      end\n    end\n\n    if @optparse.empty? then\n      raise ArgumentError, _(\"No option declarations found while building\")\n    end\n\n    # Now, infer the name from the options; we prefer the first long option as\n    # the name, rather than just the first option.\n    @name = optparse_to_name(@optparse.find do |a| a =~ /^--/ end || @optparse.first)\n    @aliases = @optparse.map { |o| optparse_to_name(o) }\n\n    # Do we take an argument?  If so, are we consistent about it, because\n    # incoherence here makes our life super-difficult, and we can more easily\n    # relax this rule later if we find a valid use case for it. --daniel 2011-03-30\n    @argument = @optparse.any? { |o| o =~ /[ =]/ }\n    if @argument and !@optparse.all? { |o| o =~ /[ =]/ } then\n      raise ArgumentError, _(\"Option %{name} is inconsistent about taking an argument\") % { name: @name }\n    end\n\n    # Is our argument optional?  The rules about consistency apply here, also,\n    # just like they do to taking arguments at all. --daniel 2011-03-30\n    @optional_argument = @optparse.any? { |o| o =~ /[ =]\\[/ }\n    if @optional_argument\n      raise ArgumentError, _(\"Options with optional arguments are not supported\")\n    end\n    if @optional_argument and !@optparse.all? { |o| o =~ /[ =]\\[/ } then\n      raise ArgumentError, _(\"Option %{name} is inconsistent about the argument being optional\") % { name: @name }\n    end\n  end\n\n  # to_s and optparse_to_name are roughly mirrored, because they are used to\n  # transform options to name symbols, and vice-versa.  This isn't a full\n  # bidirectional transformation though. --daniel 2011-04-07\n\n  def to_s\n    @name.to_s.tr('_', '-')\n  end\n\n  # @api private\n  def optparse_to_optionname(declaration)\n    found = declaration.match(/^-+(?:\\[no-\\])?([^ =]+)/)\n    unless found\n      raise ArgumentError, _(\"Can't find a name in the declaration %{declaration}\") % { declaration: declaration.inspect }\n    end\n\n    found.captures.first\n  end\n\n  # @api private\n  def optparse_to_name(declaration)\n    name = optparse_to_optionname(declaration).tr('-', '_')\n    unless name.to_s =~ /^[a-z]\\w*$/\n      raise _(\"%{name} is an invalid option name\") % { name: name.inspect }\n    end\n\n    name.to_sym\n  end\n\n  def takes_argument?\n    !!@argument\n  end\n\n  def optional_argument?\n    !!@optional_argument\n  end\n\n  def required?\n    !!@required\n  end\n\n  def has_default?\n    !!@default\n  end\n\n  def default=(proc)\n    if required\n      raise ArgumentError, _(\"%{name} can't be optional and have a default value\") % { name: self }\n    end\n\n    unless proc.is_a? Proc\n      # TRANSLATORS 'proc' is a Ruby block of code\n      raise ArgumentError, _(\"default value for %{name} is a %{class_name}, not a proc\") %\n                           { name: self, class_name: proc.class.name.inspect }\n    end\n    @default = proc\n  end\n\n  def default\n    @default and @default.call\n  end\n\n  attr_reader :parent, :name, :aliases, :optparse, :required\n\n  def required=(value)\n    if has_default?\n      raise ArgumentError, _(\"%{name} can't be optional and have a default value\") % { name: self }\n    end\n\n    @required = value\n  end\n\n  attr_reader :before_action\n\n  def before_action=(proc)\n    unless proc.is_a? Proc\n      # TRANSLATORS 'proc' is a Ruby block of code\n      raise ArgumentError, _(\"before action hook for %{name} is a %{class_name}, not a proc\") %\n                           { name: self, class_name: proc.class.name.inspect }\n    end\n    @before_action =\n      @parent.__send__(:__add_method, __decoration_name(:before), proc)\n  end\n\n  attr_reader :after_action\n\n  def after_action=(proc)\n    unless proc.is_a? Proc\n      # TRANSLATORS 'proc' is a Ruby block of code\n      raise ArgumentError, _(\"after action hook for %{name} is a %{class_name}, not a proc\") %\n                           { name: self, class_name: proc.class.name.inspect }\n    end\n    @after_action =\n      @parent.__send__(:__add_method, __decoration_name(:after), proc)\n  end\n\n  def __decoration_name(type)\n    if @parent.is_a? Puppet::Interface::Action then\n      :\"option #{name} from #{parent.name} #{type} decoration\"\n    else\n      :\"option #{name} #{type} decoration\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/option_builder.rb",
    "content": "# frozen_string_literal: true\n\n# @api public\nclass Puppet::Interface::OptionBuilder\n  # The option under construction\n  # @return [Puppet::Interface::Option]\n  # @api private\n  attr_reader :option\n\n  # Build an option\n  # @return [Puppet::Interface::Option]\n  # @api private\n  def self.build(face, *declaration, &block)\n    new(face, *declaration, &block).option\n  end\n\n  def initialize(face, *declaration, &block)\n    @face   = face\n    @option = Puppet::Interface::Option.new(face, *declaration)\n    instance_eval(&block) if block_given?\n  end\n\n  # Metaprogram the simple DSL from the option class.\n  Puppet::Interface::Option.instance_methods.grep(/=$/).each do |setter|\n    next if setter =~ /^=/\n\n    dsl = setter.to_s.chomp('=')\n\n    unless private_method_defined? dsl\n      define_method(dsl) do |value| @option.send(setter, value) end\n    end\n  end\n\n  # Override some methods that deal in blocks, not objects.\n\n  # Sets a block to be executed when an action is invoked before the\n  # main action code. This is most commonly used to validate an option.\n  # @yieldparam action [Puppet::Interface::Action] The action being\n  #   invoked\n  # @yieldparam args [Array] The arguments given to the action\n  # @yieldparam options [Hash<Symbol=>Object>] Any options set\n  # @api public\n  # @dsl Faces\n  def before_action(&block)\n    unless block\n      # TRANSLATORS 'before_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} before_action requires a block\") % { option: @option }\n    end\n    if @option.before_action\n      # TRANSLATORS 'before_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} already has a before_action set\") % { option: @option }\n    end\n    unless block.arity == 3 then\n      # TRANSLATORS 'before_action' is a method name and should not be translated\n      raise ArgumentError, _(\"before_action takes three arguments, action, args, and options\")\n    end\n\n    @option.before_action = block\n  end\n\n  # Sets a block to be executed after an action is invoked.\n  # !(see before_action)\n  # @api public\n  # @dsl Faces\n  def after_action(&block)\n    unless block\n      # TRANSLATORS 'after_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} after_action requires a block\") % { option: @option }\n    end\n    if @option.after_action\n      # TRANSLATORS 'after_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} already has an after_action set\") % { option: @option }\n    end\n    unless block.arity == 3 then\n      # TRANSLATORS 'after_action' is a method name and should not be translated\n      raise ArgumentError, _(\"after_action takes three arguments, action, args, and options\")\n    end\n\n    @option.after_action = block\n  end\n\n  # Sets whether the option is required. If no argument is given it\n  # defaults to setting it as a required option.\n  # @api public\n  # @dsl Faces\n  def required(value = true)\n    @option.required = value\n  end\n\n  # Sets a block that will be used to compute the default value for this\n  # option. It will be evaluated when the action is invoked. The block\n  # should take no arguments.\n  # @api public\n  # @dsl Faces\n  def default_to(&block)\n    unless block\n      # TRANSLATORS 'default_to' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} default_to requires a block\") % { option: @option }\n    end\n    if @option.has_default?\n      raise ArgumentError, _(\"%{option} already has a default value\") % { option: @option }\n    end\n    unless block.arity == 0\n      # TRANSLATORS 'default_to' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} default_to block should not take any arguments\") % { option: @option }\n    end\n\n    @option.default = block\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface/option_manager.rb",
    "content": "# frozen_string_literal: true\n\n# This class is not actually public API, but the method\n# {Puppet::Interface::OptionManager#option option} is public when used\n# as part of the Faces DSL (i.e. from within a\n# {Puppet::Interface.define define} block).\n# @api public\nmodule Puppet::Interface::OptionManager\n  # @api private\n  def display_global_options(*args)\n    @display_global_options ||= []\n    [args].flatten.each do |refopt|\n      unless Puppet.settings.include?(refopt)\n        # TRANSLATORS 'Puppet.settings' references to the Puppet settings options and should not be translated\n        raise ArgumentError, _(\"Global option %{option} does not exist in Puppet.settings\") % { option: refopt }\n      end\n\n      @display_global_options << refopt if refopt\n    end\n    @display_global_options.uniq!\n    @display_global_options\n  end\n  alias :display_global_option :display_global_options\n\n  def all_display_global_options\n    walk_inheritance_tree(@display_global_options, :all_display_global_options)\n  end\n\n  # @api private\n  def walk_inheritance_tree(start, sym)\n    result = start || []\n    if is_a?(Class) and superclass.respond_to?(sym)\n      result = superclass.send(sym) + result\n    elsif self.class.respond_to?(sym)\n      result = self.class.send(sym) + result\n    end\n    result\n  end\n\n  # Declare that this app can take a specific option, and provide the\n  # code to do so. See {Puppet::Interface::ActionBuilder#option} for\n  # details.\n  #\n  # @api public\n  # @dsl Faces\n  def option(*declaration, &block)\n    add_option Puppet::Interface::OptionBuilder.build(self, *declaration, &block)\n  end\n\n  # @api private\n  def add_option(option)\n    # @options collects the added options in the order they're declared.\n    # @options_hash collects the options keyed by alias for quick lookups.\n    @options      ||= []\n    @options_hash ||= {}\n\n    option.aliases.each do |name|\n      conflict = get_option(name)\n      if conflict\n        raise ArgumentError, _(\"Option %{option} conflicts with existing option %{conflict}\") %\n                             { option: option, conflict: conflict }\n      end\n\n      actions.each do |action|\n        action = get_action(action)\n        conflict = action.get_option(name)\n        if conflict\n          raise ArgumentError, _(\"Option %{option} conflicts with existing option %{conflict} on %{action}\") %\n                               { option: option, conflict: conflict, action: action }\n        end\n      end\n    end\n\n    @options << option.name\n\n    option.aliases.each do |name|\n      @options_hash[name] = option\n    end\n\n    option\n  end\n\n  # @api private\n  def options\n    walk_inheritance_tree(@options, :options)\n  end\n\n  # @api private\n  def get_option(name, with_inherited_options = true)\n    @options_hash ||= {}\n\n    result = @options_hash[name.to_sym]\n    if result.nil? and with_inherited_options then\n      if is_a?(Class) and superclass.respond_to?(:get_option)\n        result = superclass.get_option(name)\n      elsif self.class.respond_to?(:get_option)\n        result = self.class.get_option(name)\n      end\n    end\n\n    result\n  end\n\n  # @api private\n  def option?(name)\n    options.include? name.to_sym\n  end\nend\n"
  },
  {
    "path": "lib/puppet/interface.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/util/autoload'\nrequire 'prettyprint'\n\n# @api public\nclass Puppet::Interface\n  require_relative 'interface/documentation'\n  require_relative 'interface/face_collection'\n\n  require_relative 'interface/action'\n  require_relative 'interface/action_builder'\n  require_relative 'interface/action_manager'\n\n  require_relative 'interface/option'\n  require_relative 'interface/option_builder'\n  require_relative 'interface/option_manager'\n\n  include FullDocs\n\n  include Puppet::Interface::ActionManager\n  extend Puppet::Interface::ActionManager\n\n  include Puppet::Interface::OptionManager\n  extend Puppet::Interface::OptionManager\n\n  include Puppet::Util\n\n  class << self\n    # This is just so we can search for actions.  We only use its\n    # list of directories to search.\n\n    # Lists all loaded faces\n    # @return [Array<Symbol>] The names of the loaded faces\n    def faces\n      Puppet::Interface::FaceCollection.faces\n    end\n\n    # Register a face\n    # @param instance [Puppet::Interface] The face\n    # @return [void]\n    # @api private\n    def register(instance)\n      Puppet::Interface::FaceCollection.register(instance)\n    end\n\n    # Defines a new Face.\n    # @todo Talk about using Faces DSL inside the block\n    #\n    # @param name [Symbol] the name of the face\n    # @param version [String] the version of the face (this should\n    #   conform to {http://semver.org/ Semantic Versioning})\n    # @overload define(name, version, { ... })\n    # @return [Puppet::Interface] The created face\n    # @api public\n    # @dsl Faces\n    def define(name, version, &block)\n      face = Puppet::Interface::FaceCollection[name, version]\n      if face.nil? then\n        face = new(name, version)\n        Puppet::Interface::FaceCollection.register(face)\n        # REVISIT: Shouldn't this be delayed until *after* we evaluate the\n        # current block, not done before? --daniel 2011-04-07\n        face.load_actions\n      end\n\n      face.instance_eval(&block) if block_given?\n\n      face\n    end\n\n    # Retrieves a face by name and version. Use `:current` for the\n    # version to get the most recent available version.\n    #\n    # @param name [Symbol] the name of the face\n    # @param version [String, :current] the version of the face\n    #\n    # @return [Puppet::Interface] the face\n    #\n    # @api public\n    def face?(name, version)\n      Puppet::Interface::FaceCollection[name, version]\n    end\n\n    # Retrieves a face by name and version\n    #\n    # @param name [Symbol] the name of the face\n    # @param version [String] the version of the face\n    #\n    # @return [Puppet::Interface] the face\n    #\n    # @api public\n    def [](name, version)\n      face = Puppet::Interface::FaceCollection[name, version]\n      unless face\n        # REVISIT (#18042) no sense in rechecking if version == :current -- josh\n        if Puppet::Interface::FaceCollection[name, :current]\n          raise Puppet::Error, \"Could not find version #{version} of #{name}\"\n        else\n          raise Puppet::Error, \"Could not find Puppet Face #{name}\"\n        end\n      end\n\n      face\n    end\n\n    # Retrieves an action for a face\n    # @param name [Symbol] The face\n    # @param action [Symbol] The action name\n    # @param version [String, :current] The version of the face\n    # @return [Puppet::Interface::Action] The action\n    def find_action(name, action, version = :current)\n      Puppet::Interface::FaceCollection.get_action_for_face(name, action, version)\n    end\n  end\n\n  ########################################################################\n  # Documentation.  We currently have to rewrite both getters because we share\n  # the same instance between build-time and the runtime instance.  When that\n  # splits out this should merge into a module that both the action and face\n  # include. --daniel 2011-04-17\n\n  # Returns the synopsis for the face. This shows basic usage and global\n  # options.\n  # @return [String] usage synopsis\n  # @api private\n  def synopsis\n    build_synopsis name, '<action>'\n  end\n\n  ########################################################################\n\n  # The name of the face\n  # @return [Symbol]\n  # @api private\n  attr_reader :name\n\n  # The version of the face\n  # @return [SemanticPuppet::Version]\n  attr_reader :version\n\n  # The autoloader instance for the face\n  # @return [Puppet::Util::Autoload]\n  # @api private\n  attr_reader :loader\n  private :loader\n\n  # @api private\n  def initialize(name, version, &block)\n    unless SemanticPuppet::Version.valid?(version)\n      raise ArgumentError, _(\"Cannot create face %{name} with invalid version number '%{version}'!\") % { name: name.inspect, version: version }\n    end\n\n    @name    = Puppet::Interface::FaceCollection.underscorize(name)\n    @version = SemanticPuppet::Version.parse(version)\n\n    # The few bits of documentation we actually demand.  The default license\n    # is a favour to our end users; if you happen to get that in a core face\n    # report it as a bug, please. --daniel 2011-04-26\n    @authors  = []\n    @license  = 'All Rights Reserved'\n\n    @loader = Puppet::Util::Autoload.new(@name, \"puppet/face/#{@name}\")\n    instance_eval(&block) if block_given?\n  end\n\n  # Loads all actions defined in other files.\n  #\n  # @return [void]\n  # @api private\n  def load_actions\n    loader.loadall(Puppet.lookup(:current_environment))\n  end\n\n  # Returns a string representation with the face's name and version\n  # @return [String]\n  def to_s\n    \"Puppet::Face[#{name.inspect}, #{version.inspect}]\"\n  end\n  alias_method :inspect, :to_s\n\n  # @return [void]\n  def deprecate\n    @deprecated = true\n  end\n\n  # @return [Boolean]\n  def deprecated?\n    @deprecated\n  end\n  ########################################################################\n  # Action decoration, whee!  You are not expected to care about this code,\n  # which exists to support face building and construction.  I marked these\n  # private because the implementation is crude and ugly, and I don't yet know\n  # enough to work out how to make it clean.\n  #\n  # Once we have established that these methods will likely change radically,\n  # to be unrecognizable in the final outcome.  At which point we will throw\n  # all this away, replace it with something nice, and work out if we should\n  # be making this visible to the outside world... --daniel 2011-04-14\n\n  private\n\n  # @return [void]\n  # @api private\n  def __invoke_decorations(type, action, passed_args = [], passed_options = {})\n    [:before, :after].member?(type) or fail \"unknown decoration type #{type}\"\n\n    # Collect the decoration methods matching our pass.\n    methods = action.options.select do |name|\n      passed_options.has_key? name\n    end.map do |name|\n      action.get_option(name).__decoration_name(type)\n    end\n\n    methods.reverse! if type == :after\n\n    # Exceptions here should propagate up; this implements a hook we can use\n    # reasonably for option validation.\n    methods.each do |hook|\n      respond_to? hook and __send__(hook, action, passed_args, passed_options)\n    end\n  end\n\n  # @return [void]\n  # @api private\n  def __add_method(name, proc)\n    meta_def(name, &proc)\n    method(name).unbind\n  end\n\n  # @return [void]\n  # @api private\n  def self.__add_method(name, proc)\n    define_method(name, proc)\n    instance_method(name)\n  end\n  private_class_method :__add_method\nend\n"
  },
  {
    "path": "lib/puppet/loaders.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/concurrent/synchronized'\n\nmodule Puppet\n  module Pops\n    require_relative '../puppet/pops/loaders'\n\n    module Loader\n      require_relative '../puppet/pops/loader/typed_name'\n      require_relative '../puppet/pops/loader/loader'\n      require_relative '../puppet/pops/loader/base_loader'\n      require_relative '../puppet/pops/loader/gem_support'\n      require_relative '../puppet/pops/loader/module_loaders'\n      require_relative '../puppet/pops/loader/dependency_loader'\n      require_relative '../puppet/pops/loader/static_loader'\n      require_relative '../puppet/pops/loader/runtime3_type_loader'\n      require_relative '../puppet/pops/loader/ruby_function_instantiator'\n      require_relative '../puppet/pops/loader/ruby_legacy_function_instantiator'\n      require_relative '../puppet/pops/loader/ruby_data_type_instantiator'\n      require_relative '../puppet/pops/loader/puppet_function_instantiator'\n      require_relative '../puppet/pops/loader/type_definition_instantiator'\n      require_relative '../puppet/pops/loader/puppet_resource_type_impl_instantiator'\n      require_relative '../puppet/pops/loader/loader_paths'\n      require_relative '../puppet/pops/loader/simple_environment_loader'\n      require_relative '../puppet/pops/loader/predefined_loader'\n      require_relative '../puppet/pops/loader/generic_plan_instantiator'\n      require_relative '../puppet/pops/loader/puppet_plan_instantiator'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/metatype/manager.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/util/classgen'\nrequire_relative '../../puppet/node/environment'\n\n# This module defines methods dealing with Type management.\n# This module gets included into the Puppet::Type class, it's just split out here for clarity.\n# @api public\n#\nmodule Puppet::MetaType\nmodule Manager\n  include Puppet::Util::ClassGen\n\n  # An implementation specific method that removes all type instances during testing.\n  # @note Only use this method for testing purposes.\n  # @api private\n  #\n  def allclear\n    @types.each { |_name, type|\n      type.clear\n    }\n  end\n\n  # Clears any types that were used but absent when types were last loaded.\n  # @note Used after each catalog compile when always_retry_plugins is false\n  # @api private\n  #\n  def clear_misses\n    unless @types.nil?\n      @types.compact\n    end\n  end\n\n  # Iterates over all already loaded Type subclasses.\n  # @yield [t] a block receiving each type\n  # @yieldparam t [Puppet::Type] each defined type\n  # @yieldreturn [Object] the last returned object is also returned from this method\n  # @return [Object] the last returned value from the block.\n  def eachtype\n    @types.each do |_name, type|\n      # Only consider types that have names\n      # if ! type.parameters.empty? or ! type.validproperties.empty?\n      yield type\n      # end\n    end\n  end\n\n  # Loads all types.\n  # @note Should only be used for purposes such as generating documentation as this is potentially a very\n  #  expensive operation.\n  # @return [void]\n  #\n  def loadall\n    typeloader.loadall(Puppet.lookup(:current_environment))\n  end\n\n  # Defines a new type or redefines an existing type with the given name.\n  # A convenience method on the form `new<name>` where name is the name of the type is also created.\n  # (If this generated method happens to clash with an existing method, a warning is issued and the original\n  # method is kept).\n  #\n  # @param name [String] the name of the type to create or redefine.\n  # @param options [Hash] options passed on to {Puppet::Util::ClassGen#genclass} as the option `:attributes`.\n  # @option options [Puppet::Type]\n  #   Puppet::Type. This option is not passed on as an attribute to genclass.\n  # @yield [ ] a block evaluated in the context of the created class, thus allowing further detailing of\n  #   that class.\n  # @return [Class<inherits Puppet::Type>] the created subclass\n  # @see Puppet::Util::ClassGen.genclass\n  #\n  # @dsl type\n  # @api public\n  def newtype(name, options = {}, &block)\n    @manager_lock.synchronize do\n      # Handle backward compatibility\n      unless options.is_a?(Hash)\n        # TRANSLATORS 'Puppet::Type.newtype' should not be translated\n        Puppet.warning(_(\"Puppet::Type.newtype(%{name}) now expects a hash as the second argument, not %{argument}\") %\n                       { name: name, argument: options.inspect })\n      end\n\n      # First make sure we don't have a method sitting around\n      name = name.intern\n      newmethod = \"new#{name}\"\n\n      # Used for method manipulation.\n      selfobj = singleton_class\n\n      if @types.include?(name)\n        if respond_to?(newmethod)\n          # Remove the old newmethod\n          selfobj.send(:remove_method, newmethod)\n        end\n      end\n\n      # Then create the class.\n\n      klass = genclass(\n        name,\n        :parent => Puppet::Type,\n        :overwrite => true,\n        :hash => @types,\n        :attributes => options,\n        &block\n      )\n\n      # Now define a \"new<type>\" method for convenience.\n      if respond_to? newmethod\n        # Refuse to overwrite existing methods like 'newparam' or 'newtype'.\n        # TRANSLATORS 'new%{method}' will become a method name, do not translate this string\n        Puppet.warning(_(\"'new%{method}' method already exists; skipping\") % { method: name.to_s })\n      else\n        selfobj.send(:define_method, newmethod) do |*args|\n          klass.new(*args)\n        end\n      end\n\n      # If they've got all the necessary methods defined and they haven't\n      # already added the property, then do so now.\n      klass.ensurable if klass.ensurable? and !klass.validproperty?(:ensure)\n\n      # Now set up autoload any providers that might exist for this type.\n\n      klass.providerloader = Puppet::Util::Autoload.new(klass, \"puppet/provider/#{klass.name}\")\n\n      # We have to load everything so that we can figure out the default provider.\n      klass.providerloader.loadall(Puppet.lookup(:current_environment))\n      klass.providify unless klass.providers.empty?\n\n      loc = block_given? ? block.source_location : nil\n      uri = loc.nil? ? nil : URI(\"#{Puppet::Util.path_to_uri(loc[0])}?line=#{loc[1]}\")\n      Puppet::Pops::Loaders.register_runtime3_type(name, uri)\n\n      klass\n    end\n  end\n\n  # Removes an existing type.\n  # @note Only use this for testing.\n  # @api private\n  def rmtype(name)\n    # Then create the class.\n\n    rmclass(name, :hash => @types)\n\n    singleton_class.send(:remove_method, \"new#{name}\") if respond_to?(\"new#{name}\")\n  end\n\n  # Returns a Type instance by name.\n  # This will load the type if not already defined.\n  # @param [String, Symbol] name of the wanted Type\n  # @return [Puppet::Type, nil] the type or nil if the type was not defined and could not be loaded\n  #\n  def type(name)\n    @manager_lock.synchronize do\n      # Avoid loading if name obviously is not a type name\n      if name.to_s.include?(':')\n        return nil\n      end\n\n      # We are overwhelmingly symbols here, which usually match, so it is worth\n      # having this special-case to return quickly.  Like, 25K symbols vs. 300\n      # strings in this method. --daniel 2012-07-17\n      return @types[name] if @types.include? name\n\n      # Try mangling the name, if it is a string.\n      if name.is_a? String\n        name = name.downcase.intern\n        return @types[name] if @types.include? name\n      end\n      # Try loading the type.\n      if typeloader.load(name, Puppet.lookup(:current_environment))\n        # TRANSLATORS 'puppet/type/%{name}' should not be translated\n        Puppet.warning(_(\"Loaded puppet/type/%{name} but no class was created\") % { name: name }) unless @types.include? name\n      elsif !Puppet[:always_retry_plugins]\n        # PUP-5482 - Only look for a type once if plugin retry is disabled\n        @types[name] = nil\n      end\n\n      # ...and I guess that is that, eh.\n      return @types[name]\n    end\n  end\n\n  # Creates a loader for Puppet types.\n  # Defaults to an instance of {Puppet::Util::Autoload} if no other auto loader has been set.\n  # @return [Puppet::Util::Autoload] the loader to use.\n  # @api private\n  def typeloader\n    unless defined?(@typeloader)\n      @typeloader = Puppet::Util::Autoload.new(self, \"puppet/type\")\n    end\n\n    @typeloader\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/module/plan.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/logging'\n\nclass Puppet::Module\n  class Plan\n    class Error < Puppet::Error\n      attr_accessor :kind, :details\n\n      def initialize(message, kind, details = nil)\n        super(message)\n        @details = details || {}\n        @kind = kind\n      end\n\n      def to_h\n        {\n          msg: message,\n          kind: kind,\n          details: details\n        }\n      end\n    end\n\n    class InvalidName < Error\n      def initialize(name, msg)\n        super(msg, 'puppet.plans/invalid-name')\n      end\n    end\n\n    class InvalidFile < Error\n      def initialize(msg)\n        super(msg, 'puppet.plans/invalid-file')\n      end\n    end\n\n    class InvalidPlan < Error\n    end\n\n    class InvalidMetadata < Error\n    end\n\n    class PlanNotFound < Error\n      def initialize(plan_name, module_name)\n        msg = _(\"Plan %{plan_name} not found in module %{module_name}.\") %\n              { plan_name: plan_name, module_name: module_name }\n        super(msg, 'puppet.plans/plan-not-found', { 'name' => plan_name })\n      end\n    end\n\n    ALLOWED_EXTENSIONS = %w[.pp .yaml]\n    RESERVED_WORDS = %w[and application attr case class consumes default else\n                        elsif environment false function if import in inherits node or private\n                        produces site true type undef unless]\n    RESERVED_DATA_TYPES = %w[any array boolean catalogentry class collection\n                             callable data default enum float hash integer numeric optional pattern\n                             resource runtime scalar string struct tuple type undef variant]\n\n    def self.is_plan_name?(name)\n      return true if name =~ /^[a-z][a-z0-9_]*$/\n\n      false\n    end\n\n    # Determine whether a plan file has a legal name and extension\n    def self.is_plans_filename?(path)\n      name = File.basename(path, '.*')\n      ext = File.extname(path)\n      return [false, _(\"Plan names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores\")] unless is_plan_name?(name)\n      unless ALLOWED_EXTENSIONS.include? ext\n        return [false, _(\"Plan name cannot have extension %{ext}, must be .pp or .yaml\") % { ext: ext }]\n      end\n      if RESERVED_WORDS.include?(name)\n        return [false, _(\"Plan name cannot be a reserved word, but was '%{name}'\") % { name: name }]\n      end\n      if RESERVED_DATA_TYPES.include?(name)\n        return [false, _(\"Plan name cannot be a Puppet data type, but was '%{name}'\") % { name: name }]\n      end\n\n      [true]\n    end\n\n    # Executables list should contain the full path of all possible implementation files\n    def self.find_implementations(name, plan_files)\n      basename = name.split('::')[1] || 'init'\n\n      # If implementations isn't defined, then we use executables matching the\n      # plan name, and only one may exist.\n      implementations = plan_files.select { |impl| File.basename(impl, '.*') == basename }\n\n      # Select .pp before .yaml, since .pp comes before .yaml alphabetically.\n      chosen = implementations.min\n\n      [{ \"name\" => File.basename(chosen), \"path\" => chosen }]\n    end\n    private_class_method :find_implementations\n\n    def self.find_files(name, plan_files)\n      find_implementations(name, plan_files)\n    end\n\n    def self.plans_in_module(pup_module)\n      # Search e.g. 'modules/<pup_module>/plans' for all plans\n      plan_files = Dir.glob(File.join(pup_module.plans_directory, '*'))\n                      .keep_if { |f| valid, _ = is_plans_filename?(f); valid }\n\n      plans = plan_files.group_by { |f| plan_name_from_path(f) }\n\n      plans.map do |plan, plan_filenames|\n        new_with_files(pup_module, plan, plan_filenames)\n      end\n    end\n\n    attr_reader :name, :module, :metadata_file\n\n    # file paths must be relative to the modules plan directory\n    def initialize(pup_module, plan_name, plan_files)\n      valid, reason = Puppet::Module::Plan.is_plans_filename?(plan_files.first)\n      unless valid\n        raise InvalidName.new(plan_name, reason)\n      end\n\n      name = plan_name == \"init\" ? pup_module.name : \"#{pup_module.name}::#{plan_name}\"\n\n      @module = pup_module\n      @name = name\n      @metadata_file = metadata_file\n      @plan_files = plan_files || []\n    end\n\n    def metadata\n      # Nothing to go here unless plans eventually support metadata.\n      @metadata ||= {}\n    end\n\n    def files\n      @files ||= self.class.find_files(@name, @plan_files)\n    end\n\n    def validate\n      files\n      true\n    end\n\n    def ==(other)\n      name == other.name &&\n        self.module == other.module\n    end\n\n    def environment_name\n      @module.environment.respond_to?(:name) ? @module.environment.name : 'production'\n    end\n    private :environment_name\n\n    def self.new_with_files(pup_module, name, plan_files)\n      Puppet::Module::Plan.new(pup_module, name, plan_files)\n    end\n    private_class_method :new_with_files\n\n    # Abstracted here so we can add support for subdirectories later\n    def self.plan_name_from_path(path)\n      File.basename(path, '.*')\n    end\n    private_class_method :plan_name_from_path\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module/task.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/logging'\n\nclass Puppet::Module\n  class Task\n    class Error < Puppet::Error\n      attr_accessor :kind, :details\n\n      def initialize(message, kind, details = nil)\n        super(message)\n        @details = details || {}\n        @kind = kind\n      end\n\n      def to_h\n        {\n          msg: message,\n          kind: kind,\n          details: details\n        }\n      end\n    end\n\n    class InvalidName < Error\n      def initialize(name)\n        msg = _(\"Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores\")\n        super(msg, 'puppet.tasks/invalid-name')\n      end\n    end\n\n    class InvalidFile < Error\n      def initialize(msg)\n        super(msg, 'puppet.tasks/invalid-file')\n      end\n    end\n\n    class InvalidTask < Error\n    end\n\n    class InvalidMetadata < Error\n    end\n\n    class TaskNotFound < Error\n      def initialize(task_name, module_name)\n        msg = _(\"Task %{task_name} not found in module %{module_name}.\") %\n              { task_name: task_name, module_name: module_name }\n        super(msg, 'puppet.tasks/task-not-found', { 'name' => task_name })\n      end\n    end\n\n    FORBIDDEN_EXTENSIONS = %w[.conf .md]\n    MOUNTS = %w[files lib scripts tasks]\n\n    def self.is_task_name?(name)\n      return true if name =~ /^[a-z][a-z0-9_]*$/\n\n      false\n    end\n\n    def self.is_tasks_file?(path)\n      File.file?(path) && is_tasks_filename?(path)\n    end\n\n    # Determine whether a file has a legal name for either a task's executable or metadata file.\n    def self.is_tasks_filename?(path)\n      name_less_extension = File.basename(path, '.*')\n      return false unless is_task_name?(name_less_extension)\n\n      FORBIDDEN_EXTENSIONS.each do |ext|\n        return false if path.end_with?(ext)\n      end\n      true\n    end\n\n    def self.get_file_details(path, mod)\n      # This gets the path from the starting point onward\n      # For files this should be the file subpath from the metadata\n      # For directories it should be the directory subpath plus whatever we globbed\n      # Partition matches on the first instance it finds of the parameter\n      name = \"#{mod.name}#{path.partition(mod.path).last}\"\n\n      { \"name\" => name, \"path\" => path }\n    end\n    private_class_method :get_file_details\n\n    # Find task's required lib files and retrieve paths for both 'files' and 'implementation:files' metadata keys\n    def self.find_extra_files(metadata, envname = nil)\n      return [] if metadata.nil?\n\n      files = metadata.fetch('files', [])\n      unless files.is_a?(Array)\n        msg = _(\"The 'files' task metadata expects an array, got %{files}.\") % { files: files }\n        raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n      end\n      impl_files = metadata.fetch('implementations', []).flat_map do |impl|\n        file_array = impl.fetch('files', [])\n        unless file_array.is_a?(Array)\n          msg = _(\"The 'files' task metadata expects an array, got %{files}.\") % { files: file_array }\n          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n        end\n        file_array\n      end\n\n      combined_files = files + impl_files\n      combined_files.uniq.flat_map do |file|\n        module_name, mount, endpath = file.split(\"/\", 3)\n        # If there's a mount directory with no trailing slash this will be nil\n        # We want it to be empty to construct a path\n        endpath ||= ''\n\n        pup_module = Puppet::Module.find(module_name, envname)\n        if pup_module.nil?\n          msg = _(\"Could not find module %{module_name} containing task file %{filename}\" %\n                  { module_name: module_name, filename: endpath })\n          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n        end\n\n        unless MOUNTS.include? mount\n          msg = _(\"Files must be saved in module directories that Puppet makes available via mount points: %{mounts}\" %\n                  { mounts: MOUNTS.join(', ') })\n          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n        end\n\n        path = File.join(pup_module.path, mount, endpath)\n        unless File.absolute_path(path) == File.path(path).chomp('/')\n          msg = _(\"File pathnames cannot include relative paths\")\n          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n        end\n\n        unless File.exist?(path)\n          msg = _(\"Could not find %{path} on disk\" % { path: path })\n          raise InvalidFile, msg\n        end\n\n        last_char = file[-1] == '/'\n        if File.directory?(path)\n          unless last_char\n            msg = _(\"Directories specified in task metadata must include a trailing slash: %{dir}\" % { dir: file })\n            raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n          end\n          dir_files = Dir.glob(\"#{path}**/*\").select { |f| File.file?(f) }\n          dir_files.map { |f| get_file_details(f, pup_module) }\n        else\n          if last_char\n            msg = _(\"Files specified in task metadata cannot include a trailing slash: %{file}\" % { file: file })\n            raise InvalidMetadata.new(msg, 'puppet.task/invalid-metadata')\n          end\n          get_file_details(path, pup_module)\n        end\n      end\n    end\n    private_class_method :find_extra_files\n\n    # Executables list should contain the full path of all possible implementation files\n    def self.find_implementations(name, directory, metadata, executables)\n      basename = name.split('::')[1] || 'init'\n      # If 'implementations' is defined, it needs to mention at least one\n      # implementation, and everything it mentions must exist.\n      metadata ||= {}\n      if metadata.key?('implementations')\n        unless metadata['implementations'].is_a?(Array)\n          msg = _(\"Task metadata for task %{name} does not specify implementations as an array\" % { name: name })\n          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n        end\n\n        implementations = metadata['implementations'].map do |impl|\n          unless impl['requirements'].is_a?(Array) || impl['requirements'].nil?\n            msg = _(\"Task metadata for task %{name} does not specify requirements as an array\" % { name: name })\n            raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n          end\n          path = executables.find { |real_impl| File.basename(real_impl) == impl['name'] }\n          unless path\n            msg = _(\"Task metadata for task %{name} specifies missing implementation %{implementation}\" % { name: name, implementation: impl['name'] })\n            raise InvalidTask.new(msg, 'puppet.tasks/missing-implementation', { missing: [impl['name']] })\n          end\n          { \"name\" => impl['name'], \"path\" => path }\n        end\n        return implementations\n      end\n\n      # If implementations isn't defined, then we use executables matching the\n      # task name, and only one may exist.\n      implementations = executables.select { |impl| File.basename(impl, '.*') == basename }\n      if implementations.empty?\n        msg = _('No source besides task metadata was found in directory %{directory} for task %{name}') %\n              { name: name, directory: directory }\n        raise InvalidTask.new(msg, 'puppet.tasks/no-implementation')\n      elsif implementations.length > 1\n        msg = _(\"Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them\") %\n              { name: name, directory: implementations[0] }\n        raise InvalidTask.new(msg, 'puppet.tasks/multiple-implementations')\n      end\n\n      [{ \"name\" => File.basename(implementations.first), \"path\" => implementations.first }]\n    end\n    private_class_method :find_implementations\n\n    def self.find_files(name, directory, metadata, executables, envname = nil)\n      # PXP agent relies on 'impls' (which is the task file) being first if there is no metadata\n      find_implementations(name, directory, metadata, executables) + find_extra_files(metadata, envname)\n    end\n\n    def self.is_tasks_metadata_filename?(name)\n      is_tasks_filename?(name) && name.end_with?('.json')\n    end\n\n    def self.is_tasks_executable_filename?(name)\n      is_tasks_filename?(name) && !name.end_with?('.json')\n    end\n\n    def self.tasks_in_module(pup_module)\n      task_files = Dir.glob(File.join(pup_module.tasks_directory, '*'))\n                      .keep_if { |f| is_tasks_file?(f) }\n\n      module_executables = task_files.reject(&method(:is_tasks_metadata_filename?)).map.to_a\n\n      tasks = task_files.group_by { |f| task_name_from_path(f) }\n\n      tasks.map do |task, executables|\n        new_with_files(pup_module, task, executables, module_executables)\n      end\n    end\n\n    attr_reader :name, :module, :metadata_file\n\n    # file paths must be relative to the modules task directory\n    def initialize(pup_module, task_name, module_executables, metadata_file = nil)\n      unless Puppet::Module::Task.is_task_name?(task_name)\n        raise InvalidName, _(\"Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores\")\n      end\n\n      name = task_name == \"init\" ? pup_module.name : \"#{pup_module.name}::#{task_name}\"\n\n      @module = pup_module\n      @name = name\n      @metadata_file = metadata_file\n      @module_executables = module_executables || []\n    end\n\n    def self.read_metadata(file)\n      if file\n        content = Puppet::FileSystem.read(file, :encoding => 'utf-8')\n        content.empty? ? {} : Puppet::Util::Json.load(content)\n      end\n    rescue SystemCallError, IOError => err\n      msg = _(\"Error reading metadata: %{message}\" % { message: err.message })\n      raise InvalidMetadata.new(msg, 'puppet.tasks/unreadable-metadata')\n    rescue Puppet::Util::Json::ParseError => err\n      raise InvalidMetadata.new(err.message, 'puppet.tasks/unparseable-metadata')\n    end\n\n    def metadata\n      @metadata ||= self.class.read_metadata(@metadata_file)\n    end\n\n    def files\n      @files ||= self.class.find_files(@name, @module.tasks_directory, metadata, @module_executables, environment_name)\n    end\n\n    def validate\n      files\n      true\n    end\n\n    def ==(other)\n      name == other.name &&\n        self.module == other.module\n    end\n\n    def environment_name\n      @module.environment.respond_to?(:name) ? @module.environment.name : 'production'\n    end\n    private :environment_name\n\n    def self.new_with_files(pup_module, name, task_files, module_executables)\n      metadata_file = task_files.find { |f| is_tasks_metadata_filename?(f) }\n      Puppet::Module::Task.new(pup_module, name, module_executables, metadata_file)\n    end\n    private_class_method :new_with_files\n\n    # Abstracted here so we can add support for subdirectories later\n    def self.task_name_from_path(path)\n      File.basename(path, '.*')\n    end\n    private_class_method :task_name_from_path\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/util/logging'\nrequire_relative 'module/task'\nrequire_relative 'module/plan'\nrequire_relative '../puppet/util/json'\nrequire 'semantic_puppet/gem_version'\n\n# Support for modules\nclass Puppet::Module\n  class Error < Puppet::Error; end\n  class MissingModule < Error; end\n  class IncompatibleModule < Error; end\n  class UnsupportedPlatform < Error; end\n  class IncompatiblePlatform < Error; end\n  class MissingMetadata < Error; end\n  class FaultyMetadata < Error; end\n  class InvalidName < Error; end\n  class InvalidFilePattern < Error; end\n\n  include Puppet::Util::Logging\n\n  FILETYPES = {\n    \"manifests\" => \"manifests\",\n    \"files\" => \"files\",\n    \"templates\" => \"templates\",\n    \"plugins\" => \"lib\",\n    \"pluginfacts\" => \"facts.d\",\n    \"locales\" => \"locales\",\n    \"scripts\" => \"scripts\",\n  }\n\n  # Find and return the +module+ that +path+ belongs to. If +path+ is\n  # absolute, or if there is no module whose name is the first component\n  # of +path+, return +nil+\n  def self.find(modname, environment = nil)\n    return nil unless modname\n\n    # Unless a specific environment is given, use the current environment\n    env = environment ? Puppet.lookup(:environments).get!(environment) : Puppet.lookup(:current_environment)\n    env.module(modname)\n  end\n\n  def self.is_module_directory?(name, path)\n    # it must be a directory\n    fullpath = File.join(path, name)\n    return false unless Puppet::FileSystem.directory?(fullpath)\n\n    is_module_directory_name?(name)\n  end\n\n  def self.is_module_directory_name?(name)\n    # it must match an installed module name according to forge validator\n    return true if name =~ /^[a-z][a-z0-9_]*$/\n\n    false\n  end\n\n  def self.is_module_namespaced_name?(name)\n    # it must match the full module name according to forge validator\n    return true if name =~ /^[a-zA-Z0-9]+-[a-z][a-z0-9_]*$/\n\n    false\n  end\n\n  # @api private\n  def self.parse_range(range)\n    SemanticPuppet::VersionRange.parse(range)\n  end\n\n  attr_reader :name, :environment, :path, :metadata\n  attr_writer :environment\n\n  attr_accessor :dependencies, :forge_name\n  attr_accessor :source, :author, :version, :license, :summary, :description, :project_page\n\n  def initialize(name, path, environment)\n    @name = name\n    @path = path\n    @environment = environment\n\n    assert_validity\n    load_metadata\n\n    @absolute_path_to_manifests = Puppet::FileSystem::PathPattern.absolute(manifests)\n  end\n\n  # @deprecated The puppetversion module metadata field is no longer used.\n  def puppetversion\n    nil\n  end\n\n  # @deprecated The puppetversion module metadata field is no longer used.\n  def puppetversion=(something)\n  end\n\n  # @deprecated The puppetversion module metadata field is no longer used.\n  def validate_puppet_version\n    nil\n  end\n\n  def has_metadata?\n    load_metadata\n    @metadata.is_a?(Hash) && !@metadata.empty?\n  rescue Puppet::Module::MissingMetadata\n    false\n  end\n\n  FILETYPES.each do |type, location|\n    # A boolean method to let external callers determine if\n    # we have files of a given type.\n    define_method(type + '?') do\n      type_subpath = subpath(location)\n      unless Puppet::FileSystem.exist?(type_subpath)\n        Puppet.debug { \"No #{type} found in subpath '#{type_subpath}' (file / directory does not exist)\" }\n        return false\n      end\n\n      true\n    end\n\n    # A method for returning a given file of a given type.\n    # e.g., file = mod.manifest(\"my/manifest.pp\")\n    #\n    # If the file name is nil, then the base directory for the\n    # file type is passed; this is used for fileserving.\n    define_method(type.sub(/s$/, '')) do |file|\n      # If 'file' is nil then they're asking for the base path.\n      # This is used for things like fileserving.\n      if file\n        full_path = File.join(subpath(location), file)\n      else\n        full_path = subpath(location)\n      end\n\n      return nil unless Puppet::FileSystem.exist?(full_path)\n\n      full_path\n    end\n\n    # Return the base directory for the given type\n    define_method(type) do\n      subpath(location)\n    end\n  end\n\n  def tasks_directory\n    subpath(\"tasks\")\n  end\n\n  def tasks\n    return @tasks if instance_variable_defined?(:@tasks)\n\n    if Puppet::FileSystem.exist?(tasks_directory)\n      @tasks = Puppet::Module::Task.tasks_in_module(self)\n    else\n      @tasks = []\n    end\n  end\n\n  # This is a re-implementation of the Filetypes singular type method (e.g.\n  # `manifest('my/manifest.pp')`. We don't implement the full filetype \"API\" for\n  # tasks since tasks don't map 1:1 onto files.\n  def task_file(name)\n    # If 'file' is nil then they're asking for the base path.\n    # This is used for things like fileserving.\n    if name\n      full_path = File.join(tasks_directory, name)\n    else\n      full_path = tasks_directory\n    end\n\n    if Puppet::FileSystem.exist?(full_path)\n      full_path\n    else\n      nil\n    end\n  end\n\n  def plans_directory\n    subpath(\"plans\")\n  end\n\n  def plans\n    return @plans if instance_variable_defined?(:@plans)\n\n    if Puppet::FileSystem.exist?(plans_directory)\n      @plans = Puppet::Module::Plan.plans_in_module(self)\n    else\n      @plans = []\n    end\n  end\n\n  # This is a re-implementation of the Filetypes singular type method (e.g.\n  # `manifest('my/manifest.pp')`. We don't implement the full filetype \"API\" for\n  # plans.\n  def plan_file(name)\n    # If 'file' is nil then they're asking for the base path.\n    # This is used for things like fileserving.\n    if name\n      full_path = File.join(plans_directory, name)\n    else\n      full_path = plans_directory\n    end\n\n    if Puppet::FileSystem.exist?(full_path)\n      full_path\n    else\n      nil\n    end\n  end\n\n  def license_file\n    return @license_file if defined?(@license_file)\n\n    return @license_file = nil unless path\n\n    @license_file = File.join(path, \"License\")\n  end\n\n  def read_metadata\n    md_file = metadata_file\n    return {} if md_file.nil?\n\n    content = File.read(md_file, :encoding => 'utf-8')\n    content.empty? ? {} : Puppet::Util::Json.load(content)\n  rescue Errno::ENOENT\n    {}\n  rescue Puppet::Util::Json::ParseError => e\n    # TRANSLATORS 'metadata.json' is a specific file name and should not be translated.\n    msg = _(\"%{name} has an invalid and unparsable metadata.json file. The parse error: %{error}\") % { name: name, error: e.message }\n    case Puppet[:strict]\n    when :off\n      Puppet.debug(msg)\n    when :warning\n      Puppet.warning(msg)\n    when :error\n      raise FaultyMetadata, msg\n    end\n    {}\n  end\n\n  def load_metadata\n    return if instance_variable_defined?(:@metadata)\n\n    @metadata = data = read_metadata\n    return if data.empty?\n\n    @forge_name = data['name'].tr('-', '/') if data['name']\n\n    [:source, :author, :version, :license, :dependencies].each do |attr|\n      value = data[attr.to_s]\n      raise MissingMetadata, \"No #{attr} module metadata provided for #{name}\" if value.nil?\n\n      if attr == :dependencies\n        unless value.is_a?(Array)\n          raise MissingMetadata, \"The value for the key dependencies in the file metadata.json of the module #{name} must be an array, not: '#{value}'\"\n        end\n\n        value.each do |dep|\n          name = dep['name']\n          dep['name'] = name.tr('-', '/') unless name.nil?\n          dep['version_requirement'] ||= '>= 0.0.0'\n        end\n      end\n\n      send(attr.to_s + \"=\", value)\n    end\n  end\n\n  # Return the list of manifests matching the given glob pattern,\n  # defaulting to 'init.pp' for empty modules.\n  def match_manifests(rest)\n    if rest\n      wanted_manifests = wanted_manifests_from(rest)\n      searched_manifests = wanted_manifests.glob.reject { |f| FileTest.directory?(f) }\n    else\n      searched_manifests = []\n    end\n\n    # (#4220) Always ensure init.pp in case class is defined there.\n    init_manifest = manifest(\"init.pp\")\n    if !init_manifest.nil? && !searched_manifests.include?(init_manifest)\n      searched_manifests.unshift(init_manifest)\n    end\n    searched_manifests\n  end\n\n  def all_manifests\n    return [] unless Puppet::FileSystem.exist?(manifests)\n\n    Dir.glob(File.join(manifests, '**', '*.pp'))\n  end\n\n  def metadata_file\n    return @metadata_file if defined?(@metadata_file)\n\n    return @metadata_file = nil unless path\n\n    @metadata_file = File.join(path, \"metadata.json\")\n  end\n\n  def hiera_conf_file\n    unless defined?(@hiera_conf_file)\n      @hiera_conf_file = path.nil? ? nil : File.join(path, Puppet::Pops::Lookup::HieraConfig::CONFIG_FILE_NAME)\n    end\n    @hiera_conf_file\n  end\n\n  def has_hiera_conf?\n    hiera_conf_file.nil? ? false : Puppet::FileSystem.exist?(hiera_conf_file)\n  end\n\n  def modulepath\n    File.dirname(path) if path\n  end\n\n  # Find all plugin directories.  This is used by the Plugins fileserving mount.\n  def plugin_directory\n    subpath(\"lib\")\n  end\n\n  def plugin_fact_directory\n    subpath(\"facts.d\")\n  end\n\n  # @return [String]\n  def locale_directory\n    subpath(\"locales\")\n  end\n\n  # Returns true if the module has translation files for the\n  # given locale.\n  # @param [String] locale the two-letter language code to check\n  #        for translations\n  # @return true if the module has a directory for the locale, false\n  #         false otherwise\n  def has_translations?(locale)\n    Puppet::FileSystem.exist?(File.join(locale_directory, locale))\n  end\n\n  def has_external_facts?\n    File.directory?(plugin_fact_directory)\n  end\n\n  def supports(name, version = nil)\n    @supports ||= []\n    @supports << [name, version]\n  end\n\n  def to_s\n    result = \"Module #{name}\"\n    result += \"(#{path})\" if path\n    result\n  end\n\n  def dependencies_as_modules\n    dependent_modules = []\n    dependencies and dependencies.each do |dep|\n      _, dep_name = dep[\"name\"].split('/')\n      found_module = environment.module(dep_name)\n      dependent_modules << found_module if found_module\n    end\n\n    dependent_modules\n  end\n\n  def required_by\n    environment.module_requirements[forge_name] || {}\n  end\n\n  # Identify and mark unmet dependencies.  A dependency will be marked unmet\n  # for the following reasons:\n  #\n  #   * not installed and is thus considered missing\n  #   * installed and does not meet the version requirements for this module\n  #   * installed and doesn't use semantic versioning\n  #\n  # Returns a list of hashes representing the details of an unmet dependency.\n  #\n  # Example:\n  #\n  #   [\n  #     {\n  #       :reason => :missing,\n  #       :name   => 'puppetlabs-mysql',\n  #       :version_constraint => 'v0.0.1',\n  #       :mod_details => {\n  #         :installed_version => '0.0.1'\n  #       }\n  #       :parent => {\n  #         :name    => 'puppetlabs-bacula',\n  #         :version => 'v1.0.0'\n  #       }\n  #     }\n  #   ]\n  #\n  def unmet_dependencies\n    unmet_dependencies = []\n    return unmet_dependencies unless dependencies\n\n    dependencies.each do |dependency|\n      name = dependency['name']\n      version_string = dependency['version_requirement'] || '>= 0.0.0'\n\n      dep_mod = begin\n        environment.module_by_forge_name(name)\n      rescue\n        nil\n      end\n\n      error_details = {\n        :name => name,\n        :version_constraint => version_string.gsub(/^(?=\\d)/, \"v\"),\n        :parent => {\n          :name => forge_name,\n          :version => version.gsub(/^(?=\\d)/, \"v\")\n        },\n        :mod_details => {\n          :installed_version => dep_mod.nil? ? nil : dep_mod.version\n        }\n      }\n\n      unless dep_mod\n        error_details[:reason] = :missing\n        unmet_dependencies << error_details\n        next\n      end\n\n      next unless version_string\n\n      begin\n        required_version_semver_range = self.class.parse_range(version_string)\n        actual_version_semver = SemanticPuppet::Version.parse(dep_mod.version)\n      rescue ArgumentError\n        error_details[:reason] = :non_semantic_version\n        unmet_dependencies << error_details\n        next\n      end\n\n      next if required_version_semver_range.include? actual_version_semver\n\n      error_details[:reason] = :version_mismatch\n      unmet_dependencies << error_details\n      next\n    end\n\n    unmet_dependencies\n  end\n\n  def ==(other)\n    name == other.name &&\n      version == other.version &&\n      path == other.path &&\n      environment == other.environment\n  end\n\n  private\n\n  def wanted_manifests_from(pattern)\n    begin\n      extended = File.extname(pattern).empty? ? \"#{pattern}.pp\" : pattern\n      relative_pattern = Puppet::FileSystem::PathPattern.relative(extended)\n    rescue Puppet::FileSystem::PathPattern::InvalidPattern => error\n      raise Puppet::Module::InvalidFilePattern.new(\n        \"The pattern \\\"#{pattern}\\\" to find manifests in the module \\\"#{name}\\\" \" \\\n        \"is invalid and potentially unsafe.\", error\n      )\n    end\n\n    relative_pattern.prefix_with(@absolute_path_to_manifests)\n  end\n\n  def subpath(type)\n    File.join(path, type)\n  end\n\n  def assert_validity\n    if !Puppet::Module.is_module_directory_name?(@name) && !Puppet::Module.is_module_namespaced_name?(@name)\n      raise InvalidName, _(<<-ERROR_STRING).chomp % { name: @name }\n        Invalid module name '%{name}'; module names must match either:\n        An installed module name (ex. modulename) matching the expression /^[a-z][a-z0-9_]*$/ -or-\n        A namespaced module name (ex. author-modulename) matching the expression /^[a-zA-Z0-9]+[-][a-z][a-z0-9_]*$/\n      ERROR_STRING\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications/application.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'net/http'\nrequire_relative '../../../puppet/util/json'\nrequire_relative '../../../puppet/util/colors'\n\nmodule Puppet::ModuleTool\n  module Applications\n    class Application\n      include Puppet::Util::Colors\n\n      def self.run(*args)\n        new(*args).run\n      end\n\n      attr_accessor :options\n\n      def initialize(options = {})\n        @options = options\n      end\n\n      def run\n        raise NotImplementedError, \"Should be implemented in child classes.\"\n      end\n\n      def discuss(response, success, failure)\n        case response\n        when Net::HTTPOK, Net::HTTPCreated\n          Puppet.notice success\n        else\n          errors = begin\n            Puppet::Util::Json.load(response.body)['error']\n          rescue\n            \"HTTP #{response.code}, #{response.body}\"\n          end\n          Puppet.warning \"#{failure} (#{errors})\"\n        end\n      end\n\n      def metadata(require_metadata = false)\n        return @metadata if @metadata\n\n        @metadata = Puppet::ModuleTool::Metadata.new\n\n        unless @path\n          raise ArgumentError, _(\"Could not determine module path\")\n        end\n\n        if require_metadata && !Puppet::ModuleTool.is_module_root?(@path)\n          raise ArgumentError, _(\"Unable to find metadata.json in module root at %{path} See https://puppet.com/docs/puppet/latest/modules_publishing.html for required file format.\") % { path: @path }\n        end\n\n        metadata_path = File.join(@path, 'metadata.json')\n\n        if File.file?(metadata_path)\n          File.open(metadata_path) do |f|\n            @metadata.update(Puppet::Util::Json.load(f))\n          rescue Puppet::Util::Json::ParseError => ex\n            raise ArgumentError, _(\"Could not parse JSON %{metadata_path}\") % { metadata_path: metadata_path }, ex.backtrace\n          end\n        end\n\n        if File.file?(File.join(@path, 'Modulefile'))\n          Puppet.warning _(\"A Modulefile was found in the root directory of the module. This file will be ignored and can safely be removed.\")\n        end\n\n        @metadata\n      end\n\n      def load_metadata!\n        @metadata = nil\n        metadata(true)\n      end\n\n      def parse_filename(filename)\n        match = /^((.*?)-(.*?))-(\\d+\\.\\d+\\.\\d+.*?)$/.match(File.basename(filename, '.tar.gz'))\n        if match\n          module_name, author, shortname, version = match.captures\n        else\n          raise ArgumentError, _(\"Could not parse filename to obtain the username, module name and version.  (%{release_name})\") % { release_name: @release_name }\n        end\n\n        unless SemanticPuppet::Version.valid?(version)\n          raise ArgumentError, _(\"Invalid version format: %{version} (Semantic Versions are acceptable: http://semver.org)\") % { version: version }\n        end\n\n        {\n          :module_name => module_name,\n          :author => author,\n          :dir_name => shortname,\n          :version => version\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications/checksummer.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/json'\nrequire_relative '../../../puppet/module_tool/checksums'\n\nmodule Puppet::ModuleTool\n  module Applications\n    class Checksummer < Application\n      def initialize(path, options = {})\n        @path = Pathname.new(path)\n        super(options)\n      end\n\n      def run\n        changes = []\n        sums = Puppet::ModuleTool::Checksums.new(@path)\n        checksums.each do |child_path, canonical_checksum|\n          # Avoid checksumming the checksums.json file\n          next if File.basename(child_path) == \"checksums.json\"\n\n          path = @path + child_path\n          unless path.exist? && canonical_checksum == sums.checksum(path)\n            changes << child_path\n          end\n        end\n\n        # Return an Array of strings representing file paths of files that have\n        # been modified since this module was installed. All paths are relative\n        # to the installed module directory. This return value is used by the\n        # module_tool face changes action, and displayed on the console.\n        #\n        # Example return value:\n        #\n        #   [ \"REVISION\", \"manifests/init.pp\"]\n        #\n        changes\n      end\n\n      private\n\n      def checksums\n        if checksums_file.exist?\n          Puppet::Util::Json.load(checksums_file.read)\n        elsif metadata_file.exist?\n          # Check metadata.json too; legacy modules store their checksums there.\n          Puppet::Util::Json.load(metadata_file.read)['checksums'] or\n            raise ArgumentError, _(\"No file containing checksums found.\")\n        else\n          raise ArgumentError, _(\"No file containing checksums found.\")\n        end\n      end\n\n      def metadata_file\n        @path + 'metadata.json'\n      end\n\n      def checksums_file\n        @path + 'checksums.json'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications/installer.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'open-uri'\nrequire 'pathname'\nrequire 'fileutils'\nrequire 'tmpdir'\n\nrequire_relative '../../../puppet/forge'\nrequire_relative '../../../puppet/module_tool'\nrequire_relative '../../../puppet/module_tool/shared_behaviors'\nrequire_relative '../../../puppet/module_tool/install_directory'\nrequire_relative '../../../puppet/module_tool/local_tarball'\nrequire_relative '../../../puppet/module_tool/installed_modules'\nrequire_relative '../../../puppet/network/uri'\n\nmodule Puppet::ModuleTool\n  module Applications\n    class Installer < Application\n      include Puppet::ModuleTool::Errors\n      include Puppet::Forge::Errors\n      include Puppet::Network::Uri\n\n      def initialize(name, install_dir, options = {})\n        super(options)\n\n        @action              = :install\n        @environment         = options[:environment_instance]\n        @ignore_dependencies = forced? || options[:ignore_dependencies]\n        @name                = name\n        @install_dir         = install_dir\n\n        Puppet::Forge::Cache.clean\n\n        @local_tarball = Puppet::FileSystem.exist?(name)\n\n        if @local_tarball\n          release = local_tarball_source.release\n          @name = release.name\n          options[:version] = release.version.to_s\n          SemanticPuppet::Dependency.add_source(local_tarball_source)\n\n          # If we're operating on a local tarball and ignoring dependencies, we\n          # don't need to search any additional sources.  This will cut down on\n          # unnecessary network traffic.\n          unless @ignore_dependencies\n            SemanticPuppet::Dependency.add_source(installed_modules_source)\n            SemanticPuppet::Dependency.add_source(module_repository)\n          end\n\n        else\n          SemanticPuppet::Dependency.add_source(installed_modules_source) unless forced?\n          SemanticPuppet::Dependency.add_source(module_repository)\n        end\n      end\n\n      def run\n        name = @name.tr('/', '-')\n        version = options[:version] || '>= 0.0.0'\n\n        results = { :action => :install, :module_name => name, :module_version => version }\n\n        begin\n          if !@local_tarball && name !~ /-/\n            raise InvalidModuleNameError.new(module_name: @name, suggestion: \"puppetlabs-#{@name}\", action: :install)\n          end\n\n          installed_module = installed_modules[name]\n          if installed_module\n            unless forced?\n              if Puppet::Module.parse_range(version).include? installed_module.version\n                results[:result] = :noop\n                results[:version] = installed_module.version\n                return results\n              else\n                changes = begin\n                  Checksummer.run(installed_modules[name].mod.path)\n                rescue\n                  []\n                end\n                raise AlreadyInstalledError,\n                      :module_name => name,\n                      :installed_version => installed_modules[name].version,\n                      :requested_version => options[:version] || :latest,\n                      :local_changes => changes\n              end\n            end\n          end\n\n          @install_dir.prepare(name, options[:version] || 'latest')\n          results[:install_dir] = @install_dir.target\n\n          unless @local_tarball && @ignore_dependencies\n            Puppet.notice _(\"Downloading from %{host} ...\") % {\n              host: mask_credentials(module_repository.host)\n            }\n          end\n\n          if @ignore_dependencies\n            graph = build_single_module_graph(name, version)\n          else\n            graph = build_dependency_graph(name, version)\n          end\n\n          unless forced?\n            add_module_name_constraints_to_graph(graph)\n          end\n\n          installed_modules.each do |mod, release|\n            mod = mod.tr('/', '-')\n            next if mod == name\n\n            version = release.version\n\n            next if forced?\n\n            # Since upgrading already installed modules can be troublesome,\n            # we'll place constraints on the graph for each installed module,\n            # locking it to upgrades within the same major version.\n            installed_range = \">=#{version} #{version.major}.x\"\n            graph.add_constraint('installed', mod, installed_range) do |node|\n              Puppet::Module.parse_range(installed_range).include? node.version\n            end\n\n            release.mod.dependencies.each do |dep|\n              dep_name = dep['name'].tr('/', '-')\n\n              range = dep['version_requirement']\n              graph.add_constraint(\"#{mod} constraint\", dep_name, range) do |node|\n                Puppet::Module.parse_range(range).include? node.version\n              end\n            end\n          end\n\n          # Ensure that there is at least one candidate release available\n          # for the target package.\n          if graph.dependencies[name].empty?\n            raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host, :requested_version => options[:version] || :latest)\n          end\n\n          begin\n            Puppet.info _(\"Resolving dependencies ...\")\n            releases = SemanticPuppet::Dependency.resolve(graph)\n          rescue SemanticPuppet::Dependency::UnsatisfiableGraph => e\n            unsatisfied = nil\n\n            if e.respond_to?(:unsatisfied) && e.unsatisfied\n              constraints = {}\n              # If the module we're installing satisfies all its\n              # dependencies, but would break an already installed\n              # module that depends on it, show what would break.\n              if name == e.unsatisfied\n                graph.constraints[name].each do |mod, range, _|\n                  next unless mod.split.include?('constraint')\n\n                  # If the user requested a specific version or range,\n                  # only show the modules with non-intersecting ranges\n                  if options[:version]\n                    requested_range = SemanticPuppet::VersionRange.parse(options[:version])\n                    constraint_range = SemanticPuppet::VersionRange.parse(range)\n\n                    if requested_range.intersection(constraint_range) == SemanticPuppet::VersionRange::EMPTY_RANGE\n                      constraints[mod.split.first] = range\n                    end\n                  else\n                    constraints[mod.split.first] = range\n                  end\n                end\n\n              # If the module fails to satisfy one of its\n              # dependencies, show the unsatisfiable module\n              else\n                dep_constraints = graph.dependencies[name].max.constraints\n\n                if dep_constraints.key?(e.unsatisfied)\n                  unsatisfied_range = dep_constraints[e.unsatisfied].first[1]\n                  constraints[e.unsatisfied] = unsatisfied_range\n                end\n              end\n\n              installed_module = @environment.module_by_forge_name(e.unsatisfied.tr('-', '/'))\n              current_version = installed_module.version if installed_module\n\n              unsatisfied = {\n                :name => e.unsatisfied,\n                :constraints => constraints,\n                :current_version => current_version\n              } if constraints.any?\n            end\n\n            raise NoVersionsSatisfyError, results.merge(\n              :requested_name => name,\n              :requested_version => options[:version] || graph.dependencies[name].max.version.to_s,\n              :unsatisfied => unsatisfied\n            )\n          end\n\n          unless forced?\n            # Check for module name conflicts.\n            releases.each do |rel|\n              installed_module = installed_modules_source.by_name[rel.name.split('-').last]\n              next unless installed_module\n              next if installed_module.has_metadata? && installed_module.forge_name.tr('/', '-') == rel.name\n\n              if rel.name != name\n                dependency = {\n                  :name => rel.name,\n                  :version => rel.version\n                }\n              end\n\n              raise InstallConflictError,\n                    :requested_module => name,\n                    :requested_version => options[:version] || 'latest',\n                    :dependency => dependency,\n                    :directory => installed_module.path,\n                    :metadata => installed_module.metadata\n            end\n          end\n\n          Puppet.info _(\"Preparing to install ...\")\n          releases.each(&:prepare)\n\n          Puppet.notice _('Installing -- do not interrupt ...')\n          releases.each do |release|\n            installed = installed_modules[release.name]\n            if forced? || installed.nil?\n              release.install(Pathname.new(results[:install_dir]))\n            else\n              release.install(Pathname.new(installed.mod.modulepath))\n            end\n          end\n\n          results[:result] = :success\n          results[:installed_modules] = releases\n          results[:graph] = [build_install_graph(releases.first, releases)]\n        rescue ModuleToolError, ForgeError => err\n          results[:error] = {\n            :oneline => err.message,\n            :multiline => err.multiline,\n          }\n        ensure\n          results[:result] ||= :failure\n        end\n\n        results\n      end\n\n      private\n\n      def module_repository\n        @repo ||= Puppet::Forge.new(Puppet[:module_repository])\n      end\n\n      def local_tarball_source\n        @tarball_source ||= begin\n          Puppet::ModuleTool::LocalTarball.new(@name)\n        rescue Puppet::Module::Error => e\n          raise InvalidModuleError.new(@name, :action => @action, :error => e)\n        end\n      end\n\n      def installed_modules_source\n        @installed ||= Puppet::ModuleTool::InstalledModules.new(@environment)\n      end\n\n      def installed_modules\n        installed_modules_source.modules\n      end\n\n      def build_single_module_graph(name, version)\n        range = Puppet::Module.parse_range(version)\n        graph = SemanticPuppet::Dependency::Graph.new(name => range)\n        releases = SemanticPuppet::Dependency.fetch_releases(name)\n        releases.each { |release| release.dependencies.clear }\n        graph << releases\n      end\n\n      def build_dependency_graph(name, version)\n        SemanticPuppet::Dependency.query(name => version)\n      end\n\n      def build_install_graph(release, installed, graphed = [])\n        graphed << release\n        dependencies = release.dependencies.values.map do |deps|\n          dep = (deps & installed).first\n          unless dep.nil? || graphed.include?(dep)\n            build_install_graph(dep, installed, graphed)\n          end\n        end\n\n        previous = installed_modules[release.name]\n        previous = previous.version if previous\n        {\n          :release => release,\n          :name => release.name,\n          :path => release.install_dir.to_s,\n          :dependencies => dependencies.compact,\n          :version => release.version,\n          :previous_version => previous,\n          :action => (previous.nil? || previous == release.version || forced? ? :install : :upgrade),\n        }\n      end\n\n      include Puppet::ModuleTool::Shared\n\n      # Return a Pathname object representing the path to the module\n      # release package in the `Puppet.settings[:module_working_dir]`.\n      def get_release_packages\n        get_local_constraints\n\n        if !forced? && @installed.include?(@module_name)\n          raise AlreadyInstalledError,\n                :module_name => @module_name,\n                :installed_version => @installed[@module_name].first.version,\n                :requested_version => @version || (@conditions[@module_name].empty? ? :latest : :best),\n                :local_changes => Puppet::ModuleTool::Applications::Checksummer.run(@installed[@module_name].first.path)\n        end\n\n        if @ignore_dependencies && @source == :filesystem\n          @urls   = {}\n          @remote = { \"#{@module_name}@#{@version}\" => {} }\n          @versions = {\n            @module_name => [\n              { :vstring => @version, :semver => SemanticPuppet::Version.parse(@version) }\n            ]\n          }\n        else\n          get_remote_constraints(@forge)\n        end\n\n        @graph = resolve_constraints({ @module_name => @version })\n        @graph.first[:tarball] = @filename if @source == :filesystem\n        resolve_install_conflicts(@graph) unless forced?\n\n        # This clean call means we never \"cache\" the module we're installing, but this\n        # is desired since module authors can easily rerelease modules different content but the same\n        # version number, meaning someone with the old content cached will be very confused as to why\n        # they can't get new content.\n        # Long term we should just get rid of this caching behavior and cleanup downloaded modules after they install\n        # but for now this is a quick fix to disable caching\n        Puppet::Forge::Cache.clean\n        download_tarballs(@graph, @graph.last[:path], @forge)\n      end\n\n      #\n      # Resolve installation conflicts by checking if the requested module\n      # or one of its dependencies conflicts with an installed module.\n      #\n      # Conflicts occur under the following conditions:\n      #\n      # When installing 'puppetlabs-foo' and an existing directory in the\n      # target install path contains a 'foo' directory and we cannot determine\n      # the \"full name\" of the installed module.\n      #\n      # When installing 'puppetlabs-foo' and 'pete-foo' is already installed.\n      # This is considered a conflict because 'puppetlabs-foo' and 'pete-foo'\n      # install into the same directory 'foo'.\n      #\n      def resolve_install_conflicts(graph, is_dependency = false)\n        Puppet.debug(\"Resolving conflicts for #{graph.map { |n| n[:module] }.join(',')}\")\n\n        graph.each do |release|\n          @environment.modules_by_path[options[:target_dir]].each do |mod|\n            if mod.has_metadata?\n              metadata = {\n                :name => mod.forge_name.tr('/', '-'),\n                :version => mod.version\n              }\n              next if release[:module] == metadata[:name]\n            else\n              metadata = nil\n            end\n\n            next unless release[:module] =~ /-#{mod.name}$/\n\n            dependency_info = {\n              :name => release[:module],\n              :version => release[:version][:vstring]\n            }\n            dependency = is_dependency ? dependency_info : nil\n            all_versions = @versions[@module_name.to_s].sort_by { |h| h[:semver] }\n            versions = all_versions.select { |x| x[:semver].special == '' }\n            versions = all_versions if versions.empty?\n            latest_version = versions.last[:vstring]\n\n            raise InstallConflictError,\n                  :requested_module => @module_name,\n                  :requested_version => @version || \"latest: v#{latest_version}\",\n                  :dependency => dependency,\n                  :directory => mod.path,\n                  :metadata => metadata\n          end\n\n          deps = release[:dependencies]\n          if deps && !deps.empty?\n            resolve_install_conflicts(deps, true)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications/uninstaller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool\n  module Applications\n    class Uninstaller < Application\n      include Puppet::ModuleTool::Errors\n\n      def initialize(name, options)\n        @name        = name\n        @options     = options\n        @errors      = Hash.new { |h, k| h[k] = {} }\n        @unfiltered  = []\n        @installed   = []\n        @suggestions = []\n        @environment = options[:environment_instance]\n        @ignore_changes = options[:force] || options[:ignore_changes]\n      end\n\n      def run\n        results = {\n          :module_name => @name,\n          :requested_version => @version,\n        }\n\n        begin\n          find_installed_module\n          validate_module\n\n          FileUtils.rm_rf(@installed.first.path, :secure => true)\n\n          results[:affected_modules] = @installed\n          results[:result] = :success\n        rescue ModuleToolError => err\n          results[:error] = {\n            :oneline => err.message,\n            :multiline => err.multiline,\n          }\n        rescue => e\n          results[:error] = {\n            :oneline => e.message,\n            :multiline => e.respond_to?(:multiline) ? e.multiline : [e.to_s, e.backtrace].join(\"\\n\")\n          }\n        ensure\n          results[:result] ||= :failure\n        end\n\n        results\n      end\n\n      private\n\n      def find_installed_module\n        @environment.modules_by_path.values.flatten.each do |mod|\n          mod_name = (mod.forge_name || mod.name).tr('/', '-')\n          if mod_name == @name\n            @unfiltered << {\n              :name => mod_name,\n              :version => mod.version,\n              :path => mod.modulepath,\n            }\n            if @options[:version] && mod.version\n              next unless Puppet::Module.parse_range(@options[:version]).include?(SemanticPuppet::Version.parse(mod.version))\n            end\n            @installed << mod\n          elsif mod_name =~ /#{@name}/\n            @suggestions << mod_name\n          end\n        end\n\n        if @installed.length > 1\n          raise MultipleInstalledError,\n                :action => :uninstall,\n                :module_name => @name,\n                :installed_modules => @installed.sort_by { |mod| @environment.modulepath.index(mod.modulepath) }\n        elsif @installed.empty?\n          if @unfiltered.empty?\n            raise NotInstalledError,\n                  :action => :uninstall,\n                  :suggestions => @suggestions,\n                  :module_name => @name\n          else\n            raise NoVersionMatchesError,\n                  :installed_modules => @unfiltered.sort_by { |mod| @environment.modulepath.index(mod[:path]) },\n                  :version_range => @options[:version],\n                  :module_name => @name\n          end\n        end\n      end\n\n      def validate_module\n        mod = @installed.first\n\n        unless @ignore_changes\n          raise _(\"Either the `--ignore_changes` or `--force` argument must be specified to uninstall modules when running in FIPS mode.\") if Puppet.runtime[:facter].value(:fips_enabled)\n\n          changes = begin\n            Puppet::ModuleTool::Applications::Checksummer.run(mod.path)\n          rescue ArgumentError\n            []\n          end\n\n          if mod.has_metadata? && !changes.empty?\n            raise LocalChangesError,\n                  :action => :uninstall,\n                  :module_name => (mod.forge_name || mod.name).tr('/', '-'),\n                  :requested_version => @options[:version],\n                  :installed_version => mod.version\n          end\n        end\n\n        if !@options[:force] && !mod.required_by.empty?\n          raise ModuleIsRequiredError,\n                :module_name => (mod.forge_name || mod.name).tr('/', '-'),\n                :required_by => mod.required_by,\n                :requested_version => @options[:version],\n                :installed_version => mod.version\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications/unpacker.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire 'tmpdir'\nrequire_relative '../../../puppet/util/json'\nrequire_relative '../../../puppet/file_system'\n\nmodule Puppet::ModuleTool\n  module Applications\n    class Unpacker < Application\n      def self.unpack(filename, target)\n        app = new(filename, :target_dir => target)\n        app.unpack\n        app.sanity_check\n        app.move_into(target)\n      end\n\n      def self.harmonize_ownership(source, target)\n        unless Puppet::Util::Platform.windows?\n          source = Pathname.new(source) unless source.respond_to?(:stat)\n          target = Pathname.new(target) unless target.respond_to?(:stat)\n\n          FileUtils.chown_R(source.stat.uid, source.stat.gid, target)\n        end\n      end\n\n      def initialize(filename, options = {})\n        @filename = Pathname.new(filename)\n        super(options)\n        @module_path = Pathname(options[:target_dir])\n      end\n\n      def run\n        unpack\n        sanity_check\n        module_dir = @module_path + module_name\n        move_into(module_dir)\n\n        # Return the Pathname object representing the directory where the\n        # module release archive was unpacked the to.\n        module_dir\n      end\n\n      # @api private\n      # Error on symlinks and other junk\n      def sanity_check\n        symlinks = Dir.glob(\"#{tmpdir}/**/*\", File::FNM_DOTMATCH).map { |f| Pathname.new(f) }.select { |p| Puppet::FileSystem.symlink? p }\n        tmpdirpath = Pathname.new tmpdir\n\n        symlinks.each do |s|\n          Puppet.warning _(\"Symlinks in modules are unsupported. Please investigate symlink %{from}->%{to}.\") % { from: s.relative_path_from(tmpdirpath), to: Puppet::FileSystem.readlink(s) }\n        end\n      end\n\n      # @api private\n      def unpack\n        Puppet::ModuleTool::Tar.instance.unpack(@filename.to_s, tmpdir, [@module_path.stat.uid, @module_path.stat.gid].join(':'))\n      rescue Puppet::ExecutionFailure => e\n        raise RuntimeError, _(\"Could not extract contents of module archive: %{message}\") % { message: e.message }\n      end\n\n      # @api private\n      def root_dir\n        return @root_dir if @root_dir\n\n        # Grab the first directory containing a metadata.json file\n        metadata_file = Dir[\"#{tmpdir}/**/metadata.json\"].min_by(&:length)\n\n        if metadata_file\n          @root_dir = Pathname.new(metadata_file).dirname\n        else\n          raise _(\"No valid metadata.json found!\")\n        end\n      end\n\n      # @api private\n      def module_name\n        metadata = Puppet::Util::Json.load((root_dir + 'metadata.json').read)\n        metadata['name'][/-(.*)/, 1]\n      end\n\n      # @api private\n      def move_into(dir)\n        dir = Pathname.new(dir)\n        dir.rmtree if dir.exist?\n        FileUtils.mv(root_dir, dir)\n      ensure\n        FileUtils.rmtree(tmpdir)\n      end\n\n      # Obtain a suitable temporary path for unpacking tarballs\n      #\n      # @api private\n      # @return [String] path to temporary unpacking location\n      # rubocop:disable Naming/MemoizedInstanceVariableName\n      def tmpdir\n        @dir ||= Dir.mktmpdir('tmp', Puppet::Forge::Cache.base_path)\n      end\n      # rubocop:enable Naming/MemoizedInstanceVariableName\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications/upgrader.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\n\nrequire_relative '../../../puppet/forge'\nrequire_relative '../../../puppet/module_tool'\nrequire_relative '../../../puppet/module_tool/shared_behaviors'\nrequire_relative '../../../puppet/module_tool/install_directory'\nrequire_relative '../../../puppet/module_tool/installed_modules'\n\nmodule Puppet::ModuleTool\n  module Applications\n    class Upgrader < Application\n      include Puppet::ModuleTool::Errors\n\n      def initialize(name, options)\n        super(options)\n\n        @action              = :upgrade\n        @environment         = options[:environment_instance]\n        @name                = name\n        @ignore_changes      = forced? || options[:ignore_changes]\n        @ignore_dependencies = forced? || options[:ignore_dependencies]\n\n        SemanticPuppet::Dependency.add_source(installed_modules_source)\n        SemanticPuppet::Dependency.add_source(module_repository)\n      end\n\n      def run\n        # Disallow anything that invokes md5 to avoid un-friendly termination due to FIPS\n        raise _(\"Module upgrade is prohibited in FIPS mode.\") if Puppet.runtime[:facter].value(:fips_enabled)\n\n        name = @name.tr('/', '-')\n        version = options[:version] || '>= 0.0.0'\n\n        results = {\n          :action => :upgrade,\n          :requested_version => options[:version] || :latest,\n        }\n\n        begin\n          all_modules = @environment.modules_by_path.values.flatten\n          matching_modules = all_modules.select do |x|\n            x.forge_name && x.forge_name.tr('/', '-') == name\n          end\n\n          if matching_modules.empty?\n            raise NotInstalledError, results.merge(:module_name => name)\n          elsif matching_modules.length > 1\n            raise MultipleInstalledError, results.merge(:module_name => name, :installed_modules => matching_modules)\n          end\n\n          installed_release = installed_modules[name]\n\n          # `priority` is an attribute of a `SemanticPuppet::Dependency::Source`,\n          # which is delegated through `ModuleRelease` instances for the sake of\n          # comparison (sorting). By default, the `InstalledModules` source has\n          # a priority of 10 (making it the most preferable source, so that\n          # already installed versions of modules are selected in preference to\n          # modules from e.g. the Forge). Since we are specifically looking to\n          # upgrade this module, we don't want the installed version of this\n          # module to be chosen in preference to those with higher versions.\n          #\n          # This implementation is suboptimal, and since we can expect this sort\n          # of behavior to be reasonably common in Semantic, we should probably\n          # see about implementing a `ModuleRelease#override_priority` method\n          # (or something similar).\n          def installed_release.priority\n            0\n          end\n\n          mod = installed_release.mod\n          results[:installed_version] = SemanticPuppet::Version.parse(mod.version)\n          dir = Pathname.new(mod.modulepath)\n\n          vstring = mod.version ? \"v#{mod.version}\" : '???'\n          Puppet.notice _(\"Found '%{name}' (%{version}) in %{dir} ...\") % { name: name, version: colorize(:cyan, vstring), dir: dir }\n          unless @ignore_changes\n            changes = begin\n              Checksummer.run(mod.path)\n            rescue\n              []\n            end\n            if mod.has_metadata? && !changes.empty?\n              raise LocalChangesError,\n                    :action => :upgrade,\n                    :module_name => name,\n                    :requested_version => results[:requested_version],\n                    :installed_version => mod.version\n            end\n          end\n\n          Puppet::Forge::Cache.clean\n\n          # Ensure that there is at least one candidate release available\n          # for the target package.\n          available_versions = module_repository.fetch(name)\n          if available_versions.empty?\n            raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host)\n          elsif results[:requested_version] != :latest\n            requested = Puppet::Module.parse_range(results[:requested_version])\n            unless available_versions.any? { |m| requested.include? m.version }\n              raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host)\n            end\n          end\n\n          Puppet.notice _(\"Downloading from %{host} ...\") % { host: module_repository.host }\n          if @ignore_dependencies\n            graph = build_single_module_graph(name, version)\n          else\n            graph = build_dependency_graph(name, version)\n          end\n\n          unless forced?\n            add_module_name_constraints_to_graph(graph)\n          end\n\n          installed_modules.each do |installed_module, release|\n            installed_module = installed_module.tr('/', '-')\n            next if installed_module == name\n\n            version = release.version\n\n            next if forced?\n\n            # Since upgrading already installed modules can be troublesome,\n            # we'll place constraints on the graph for each installed\n            # module, locking it to upgrades within the same major version.\n            installed_range = \">=#{version} #{version.major}.x\"\n            graph.add_constraint('installed', installed_module, installed_range) do |node|\n              Puppet::Module.parse_range(installed_range).include? node.version\n            end\n\n            release.mod.dependencies.each do |dep|\n              dep_name = dep['name'].tr('/', '-')\n\n              range = dep['version_requirement']\n              graph.add_constraint(\"#{installed_module} constraint\", dep_name, range) do |node|\n                Puppet::Module.parse_range(range).include? node.version\n              end\n            end\n          end\n\n          begin\n            Puppet.info _(\"Resolving dependencies ...\")\n            releases = SemanticPuppet::Dependency.resolve(graph)\n          rescue SemanticPuppet::Dependency::UnsatisfiableGraph\n            raise NoVersionsSatisfyError, results.merge(:requested_name => name)\n          end\n\n          releases.each do |rel|\n            mod = installed_modules_source.by_name[rel.name.split('-').last]\n            next unless mod\n            next if mod.has_metadata? && mod.forge_name.tr('/', '-') == rel.name\n\n            if rel.name != name\n              dependency = {\n                :name => rel.name,\n                :version => rel.version\n              }\n            end\n\n            raise InstallConflictError,\n                  :requested_module => name,\n                  :requested_version => options[:version] || 'latest',\n                  :dependency => dependency,\n                  :directory => mod.path,\n                  :metadata => mod.metadata\n          end\n\n          child = releases.find { |x| x.name == name }\n\n          unless forced?\n            if child.version == results[:installed_version]\n              versions = graph.dependencies[name].map(&:version)\n              newer_versions = versions.select { |v| v > results[:installed_version] }\n\n              raise VersionAlreadyInstalledError,\n                    :module_name => name,\n                    :requested_version => results[:requested_version],\n                    :installed_version => results[:installed_version],\n                    :newer_versions => newer_versions,\n                    :possible_culprits => installed_modules_source.fetched.reject { |x| x == name }\n            elsif child.version < results[:installed_version]\n              raise DowngradingUnsupportedError,\n                    :module_name => name,\n                    :requested_version => results[:requested_version],\n                    :installed_version => results[:installed_version]\n            end\n          end\n\n          Puppet.info _(\"Preparing to upgrade ...\")\n          releases.each(&:prepare)\n\n          Puppet.notice _('Upgrading -- do not interrupt ...')\n          releases.each do |release|\n            installed = installed_modules[release.name]\n            if installed\n              release.install(Pathname.new(installed.mod.modulepath))\n            else\n              release.install(dir)\n            end\n          end\n\n          results[:result] = :success\n          results[:base_dir] = releases.first.install_dir\n          results[:affected_modules] = releases\n          results[:graph] = [build_install_graph(releases.first, releases)]\n        rescue VersionAlreadyInstalledError => e\n          results[:result] = (e.newer_versions.empty? ? :noop : :failure)\n          results[:error] = { :oneline => e.message, :multiline => e.multiline }\n        rescue => e\n          results[:error] = {\n            :oneline => e.message,\n            :multiline => e.respond_to?(:multiline) ? e.multiline : [e.to_s, e.backtrace].join(\"\\n\")\n          }\n        ensure\n          results[:result] ||= :failure\n        end\n\n        results\n      end\n\n      private\n\n      # rubocop:disable Naming/MemoizedInstanceVariableName\n      def module_repository\n        @repo ||= Puppet::Forge.new(Puppet[:module_repository])\n      end\n\n      def installed_modules_source\n        @installed ||= Puppet::ModuleTool::InstalledModules.new(@environment)\n      end\n      # rubocop:enable Naming/MemoizedInstanceVariableName\n\n      def installed_modules\n        installed_modules_source.modules\n      end\n\n      def build_single_module_graph(name, version)\n        range = Puppet::Module.parse_range(version)\n        graph = SemanticPuppet::Dependency::Graph.new(name => range)\n        releases = SemanticPuppet::Dependency.fetch_releases(name)\n        releases.each { |release| release.dependencies.clear }\n        graph << releases\n      end\n\n      def build_dependency_graph(name, version)\n        SemanticPuppet::Dependency.query(name => version)\n      end\n\n      def build_install_graph(release, installed, graphed = [])\n        previous = installed_modules[release.name]\n        previous = previous.version if previous\n\n        action = :upgrade\n        unless previous && previous != release.version\n          action = :install\n        end\n\n        graphed << release\n\n        dependencies = release.dependencies.values.filter_map do |deps|\n          dep = (deps & installed).first\n          if dep == installed_modules[dep.name]\n            next\n          end\n\n          if dep && !graphed.include?(dep)\n            build_install_graph(dep, installed, graphed)\n          end\n        end\n\n        {\n          :release => release,\n          :name => release.name,\n          :path => release.install_dir,\n          :dependencies => dependencies.compact,\n          :version => release.version,\n          :previous_version => previous,\n          :action => action,\n        }\n      end\n\n      include Puppet::ModuleTool::Shared\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/applications.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\n\nmodule Puppet::ModuleTool\n  module Applications\n    require_relative 'applications/application'\n    require_relative 'applications/checksummer'\n    require_relative 'applications/installer'\n    require_relative 'applications/unpacker'\n    require_relative 'applications/uninstaller'\n    require_relative 'applications/upgrader'\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/checksums.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest/md5'\nrequire_relative '../../puppet/network/format_support'\n\nmodule Puppet::ModuleTool\n  # = Checksums\n  #\n  # This class provides methods for generating checksums for data and adding\n  # them to +Metadata+.\n  class Checksums\n    include Puppet::Network::FormatSupport\n    include Enumerable\n\n    # Instantiate object with string +path+ to create checksums from.\n    def initialize(path)\n      @path = Pathname.new(path)\n    end\n\n    # Return checksum for the +Pathname+.\n    def checksum(pathname)\n      Digest::MD5.hexdigest(Puppet::FileSystem.binread(pathname))\n    end\n\n    # Return checksums for object's +Pathname+, generate if it's needed.\n    # Result is a hash of path strings to checksum strings.\n    def data\n      unless @data\n        @data = {}\n        @path.find do |descendant|\n          if Puppet::ModuleTool.artifact?(descendant)\n            Find.prune\n          elsif descendant.file?\n            path = descendant.relative_path_from(@path)\n            @data[path.to_s] = checksum(descendant)\n          end\n        end\n      end\n      @data\n    end\n\n    alias :to_data_hash :data\n    alias :to_hash :data\n\n    # TODO: Why?\n    def each(&block)\n      data.each(&block)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/contents_description.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\n\nmodule Puppet::ModuleTool\n  # = ContentsDescription\n  #\n  # This class populates +Metadata+'s Puppet type information.\n  class ContentsDescription\n    # Instantiate object for string +module_path+.\n    def initialize(module_path)\n      @module_path = module_path\n    end\n\n    # Update +Metadata+'s Puppet type information.\n    def annotate(metadata)\n      metadata.types.replace data.clone\n    end\n\n    # Return types for this module. Result is an array of hashes, each of which\n    # describes a Puppet type. The type description hash structure is:\n    # * :name => Name of this Puppet type.\n    # * :doc => Documentation for this type.\n    # * :properties => Array of hashes representing the type's properties, each\n    #   containing :name and :doc.\n    # * :parameters => Array of hashes representing the type's parameters, each\n    #   containing :name and :doc.\n    # * :providers => Array of hashes representing the types providers, each\n    #   containing :name and :doc.\n    # TODO Write a TypeDescription to encapsulate these structures and logic?\n    def data\n      unless @data\n        @data = []\n        type_names = []\n        (Dir[File.join(@module_path, \"lib/puppet/type/*.rb\")]).each do |module_filename|\n          require module_filename\n          type_name = File.basename(module_filename, \".rb\")\n          type_names << type_name\n\n          (Dir[File.join(@module_path, \"lib/puppet/provider/#{type_name}/*.rb\")]).each do |provider_filename|\n            require provider_filename\n          end\n        end\n\n        type_names.each do |name|\n          type = Puppet::Type.type(name.to_sym)\n          if type\n            type_hash = { :name => name, :doc => type.doc }\n            type_hash[:properties] = attr_doc(type, :property)\n            type_hash[:parameters] = attr_doc(type, :param)\n            if type.providers.size > 0\n              type_hash[:providers] = provider_doc(type)\n            end\n            @data << type_hash\n          else\n            Puppet.warning _(\"Could not find/load type: %{name}\") % { name: name }\n          end\n        end\n      end\n      @data\n    end\n\n    # Return an array of hashes representing this +type+'s attrs of +kind+\n    # (e.g. :param or :property), each containing :name and :doc.\n    def attr_doc(type, kind)\n      attrs = []\n\n      type.allattrs.each do |name|\n        if type.attrtype(name) == kind && name != :provider\n          attrs.push(:name => name, :doc => type.attrclass(name).doc)\n        end\n      end\n\n      attrs\n    end\n\n    # Return an array of hashes representing this +type+'s providers, each\n    # containing :name and :doc.\n    def provider_doc(type)\n      providers = []\n\n      type.providers.sort.each do |prov|\n        providers.push(:name => prov, :doc => type.provider(prov).doc)\n      end\n\n      providers\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/dependency.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\nrequire_relative '../../puppet/network/format_support'\n\nmodule Puppet::ModuleTool\n  class Dependency\n    include Puppet::Network::FormatSupport\n\n    attr_reader :full_module_name, :username, :name, :version_requirement, :repository\n\n    # Instantiates a new module dependency with a +full_module_name+ (e.g.\n    # \"myuser-mymodule\"), and optional +version_requirement+ (e.g. \"0.0.1\") and\n    # optional repository (a URL string).\n    def initialize(full_module_name, version_requirement = nil, repository = nil)\n      @full_module_name = full_module_name\n      # TODO: add error checking, the next line raises ArgumentError when +full_module_name+ is invalid\n      @username, @name = Puppet::ModuleTool.username_and_modname_from(full_module_name)\n      @version_requirement = version_requirement\n      @repository = repository ? Puppet::Forge::Repository.new(repository, nil) : nil\n    end\n\n    # We override Object's ==, eql, and hash so we can more easily find identical\n    # dependencies.\n    def ==(o)\n      hash == o.hash\n    end\n\n    alias :eql? :==\n\n    def hash\n      [@full_module_name, @version_requirement, @repository].hash\n    end\n\n    def to_data_hash\n      result = { :name => @full_module_name }\n      result[:version_requirement] = @version_requirement if @version_requirement && !@version_requirement.nil?\n      result[:repository] = @repository.to_s if @repository && !@repository.nil?\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/errors/base.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool::Errors\n  class ModuleToolError < Puppet::Error\n    def v(version)\n      (version || '???').to_s.sub(/^(?=\\d)/, 'v')\n    end\n\n    def vstring\n      if @action == :upgrade\n        \"#{v(@installed_version)} -> #{v(@requested_version)}\"\n      else\n        v(@installed_version || @requested_version).to_s\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/errors/installer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool::Errors\n  class InstallError < ModuleToolError; end\n\n  class AlreadyInstalledError < InstallError\n    def initialize(options)\n      @module_name       = options[:module_name]\n      @installed_version = v(options[:installed_version])\n      @requested_version = v(options[:requested_version])\n      @local_changes     = options[:local_changes]\n      super _(\"'%{module_name}' (%{version}) requested; '%{module_name}' (%{installed_version}) already installed\") % { module_name: @module_name, version: @requested_version, installed_version: @installed_version }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not install module '%{module_name}' (%{version})\") % { module_name: @module_name, version: @requested_version }\n      message << _(\"  Module '%{module_name}' (%{installed_version}) is already installed\") % { module_name: @module_name, installed_version: @installed_version }\n      message << _(\"    Installed module has had changes made locally\") unless @local_changes.empty?\n      # TRANSLATORS `puppet module upgrade` is a command line and should not be translated\n      message << _(\"    Use `puppet module upgrade` to install a different version\")\n      # TRANSLATORS `puppet module install --force` is a command line and should not be translated\n      message << _(\"    Use `puppet module install --force` to re-install only this module\")\n      message.join(\"\\n\")\n    end\n  end\n\n  class MissingPackageError < InstallError\n    def initialize(options)\n      @requested_package = options[:requested_package]\n      @source = options[:source]\n\n      super _(\"Could not install '%{requested_package}'; no releases are available from %{source}\") % { requested_package: @requested_package, source: @source }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not install '%{requested_package}'\") % { requested_package: @requested_package }\n      message << _(\"  No releases are available from %{source}\") % { source: @source }\n      message << _(\"    Does '%{requested_package}' have at least one published release?\") % { requested_package: @requested_package }\n      message.join(\"\\n\")\n    end\n  end\n\n  class InstallPathExistsNotDirectoryError < InstallError\n    def initialize(original, options)\n      @requested_module  = options[:requested_module]\n      @requested_version = options[:requested_version]\n      @directory         = options[:directory]\n      super(_(\"'%{module_name}' (%{version}) requested; Path %{dir} is not a directory.\") % { module_name: @requested_module, version: @requested_version, dir: @directory }, original)\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not install module '%{module_name}' (%{version})\") % { module_name: @requested_module, version: @requested_version }\n      message << _(\"  Path '%{directory}' exists but is not a directory.\") % { directory: @directory }\n      # TRANSLATORS \"mkdir -p '%{directory}'\" is a command line example and should not be translated\n      message << _(\"  A potential solution is to rename the path and then \\\"mkdir -p '%{directory}'\\\"\") % { directory: @directory }\n      message.join(\"\\n\")\n    end\n  end\n\n  class PermissionDeniedCreateInstallDirectoryError < InstallError\n    def initialize(original, options)\n      @requested_module  = options[:requested_module]\n      @requested_version = options[:requested_version]\n      @directory         = options[:directory]\n      super(_(\"'%{module_name}' (%{version}) requested; Permission is denied to create %{dir}.\") % { module_name: @requested_module, version: @requested_version, dir: @directory }, original)\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not install module '%{module_name}' (%{version})\") % { module_name: @requested_module, version: @requested_version }\n      message << _(\"  Permission is denied when trying to create directory '%{directory}'.\") % { directory: @directory }\n      message << _('  A potential solution is to check the ownership and permissions of parent directories.')\n      message.join(\"\\n\")\n    end\n  end\n\n  class InvalidPathInPackageError < InstallError\n    def initialize(options)\n      @entry_path = options[:entry_path]\n      @directory  = options[:directory]\n      super _(\"Attempt to install file with an invalid path into %{path} under %{dir}\") % { path: @entry_path.inspect, dir: @directory.inspect }\n    end\n\n    def multiline\n      message = []\n      message << _('Could not install package with an invalid path.')\n      message << _('  Package attempted to install file into %{path} under %{directory}.') % { path: @entry_path.inspect, directory: @directory.inspect }\n      message.join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/errors/shared.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool::Errors\n  class NoVersionsSatisfyError < ModuleToolError\n    def initialize(options)\n      @requested_name    = options[:requested_name]\n      @requested_version = options[:requested_version]\n      @installed_version = options[:installed_version]\n      @conditions        = options[:conditions]\n      @action            = options[:action]\n      @unsatisfied       = options[:unsatisfied]\n\n      super _(\"Could not %{action} '%{module_name}' (%{version}); no version satisfies all dependencies\") % { action: @action, module_name: @requested_name, version: vstring }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}' (%{version})\") % { action: @action, module_name: @requested_name, version: vstring }\n\n      if @unsatisfied\n        message << _(\"  The requested version cannot satisfy one or more of the following installed modules:\")\n        if @unsatisfied[:current_version]\n          message << _(\"    %{name}, installed: %{current_version}, expected: %{constraints}\") % { name: @unsatisfied[:name], current_version: @unsatisfied[:current_version], constraints: @unsatisfied[:constraints][@unsatisfied[:name]] }\n        else\n          @unsatisfied[:constraints].each do |mod, range|\n            message << _(\"    %{mod}, expects '%{name}': %{range}\") % { mod: mod, name: @requested_name, range: range }\n          end\n        end\n        message << _(\"\")\n      else\n        message << _(\"  The requested version cannot satisfy all dependencies\")\n      end\n\n      # TRANSLATORS `puppet module %{action} --ignore-dependencies` is a command line and should not be translated\n      message << _(\"  Use `puppet module %{action} '%{module_name}' --ignore-dependencies` to %{action} only this module\") % { action: @action, module_name: @requested_name }\n      message.join(\"\\n\")\n    end\n  end\n\n  class NoCandidateReleasesError < ModuleToolError\n    def initialize(options)\n      @module_name       = options[:module_name]\n      @requested_version = options[:requested_version]\n      @installed_version = options[:installed_version]\n      @source            = options[:source]\n      @action            = options[:action]\n\n      if @requested_version == :latest\n        super _(\"Could not %{action} '%{module_name}'; no releases are available from %{source}\") % { action: @action, module_name: @module_name, source: @source }\n      else\n        super _(\"Could not %{action} '%{module_name}'; no releases matching '%{version}' are available from %{source}\") % { action: @action, module_name: @module_name, version: @requested_version, source: @source }\n      end\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} '%{module_name}' (%{version})\") % { action: @action, module_name: @module_name, version: vstring }\n      if @requested_version == :latest\n        message << _(\"  No releases are available from %{source}\") % { source: @source }\n        message << _(\"    Does '%{module_name}' have at least one published release?\") % { module_name: @module_name }\n      else\n        message << _(\"  No releases matching '%{requested_version}' are available from %{source}\") % { requested_version: @requested_version, source: @source }\n      end\n      message.join(\"\\n\")\n    end\n  end\n\n  class InstallConflictError < ModuleToolError\n    def initialize(options)\n      @requested_module  = options[:requested_module]\n      @requested_version = v(options[:requested_version])\n      @dependency        = options[:dependency]\n      @directory         = options[:directory]\n      @metadata          = options[:metadata]\n      super _(\"'%{module_name}' (%{version}) requested; installation conflict\") % { module_name: @requested_module, version: @requested_version }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not install module '%{module_name}' (%{version})\") % { module_name: @requested_module, version: @requested_version }\n\n      if @dependency\n        message << _(\"  Dependency '%{name}' (%{version}) would overwrite %{directory}\") % { name: @dependency[:name], version: v(@dependency[:version]), directory: @directory }\n      else\n        message << _(\"  Installation would overwrite %{directory}\") % { directory: @directory }\n      end\n\n      if @metadata\n        message << _(\"    Currently, '%{current_name}' (%{current_version}) is installed to that directory\") % { current_name: @metadata[\"name\"], current_version: v(@metadata[\"version\"]) }\n      end\n\n      if @dependency\n        # TRANSLATORS `puppet module install --ignore-dependencies` is a command line and should not be translated\n        message << _(\"    Use `puppet module install --ignore-dependencies` to install only this module\")\n      else\n        # TRANSLATORS `puppet module install --force` is a command line and should not be translated\n        message << _(\"    Use `puppet module install --force` to install this module anyway\")\n      end\n      message.join(\"\\n\")\n    end\n  end\n\n  class InvalidDependencyCycleError < ModuleToolError\n    def initialize(options)\n      @module_name       = options[:module_name]\n      @requested_module  = options[:requested_module]\n      @requested_version = options[:requested_version]\n      @conditions        = options[:conditions]\n      @source            = options[:source][1..]\n\n      super _(\"'%{module_name}' (%{version}) requested; Invalid dependency cycle\") % { module_name: @requested_module, version: v(@requested_version) }\n    end\n\n    def multiline\n      dependency_list = []\n      dependency_list << _(\"You specified '%{name}' (%{version})\") % { name: @source.first[:name], version: v(@requested_version) }\n      dependency_list += @source[1..].map do |m|\n        # TRANSLATORS This message repeats as separate lines as a list under the heading \"You specified '%{name}' (%{version})\\n\"\n        _(\"This depends on '%{name}' (%{version})\") % { name: m[:name], version: v(m[:version]) }\n      end\n      message = []\n      message << _(\"Could not install module '%{module_name}' (%{version})\") % { module_name: @requested_module, version: v(@requested_version) }\n      message << _(\"  No version of '%{module_name}' will satisfy dependencies\") % { module_name: @module_name }\n      message << dependency_list.map { |s| \"    #{s}\".join(\",\\n\") }\n      # TRANSLATORS `puppet module install --force` is a command line and should not be translated\n      message << _(\"    Use `puppet module install --force` to install this module anyway\")\n      message.join(\"\\n\")\n    end\n  end\n\n  class InvalidModuleNameError < ModuleToolError\n    def initialize(options)\n      @module_name = options[:module_name]\n      @suggestion = options[:suggestion]\n      @action = options[:action]\n      super _(\"Could not %{action} '%{module_name}', did you mean '%{suggestion}'?\") % { action: @action, module_name: @module_name, suggestion: @suggestion }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}'\") % { action: @action, module_name: @module_name }\n      message << _(\"  The name '%{module_name}' is invalid\") % { module_name: @module_name }\n      message << _(\"    Did you mean `puppet module %{action} %{suggestion}`?\") % { action: @action, suggestion: @suggestion }\n      message.join(\"\\n\")\n    end\n  end\n\n  class NotInstalledError < ModuleToolError\n    def initialize(options)\n      @module_name = options[:module_name]\n      @suggestions = options[:suggestions] || []\n      @action      = options[:action]\n      super _(\"Could not %{action} '%{module_name}'; module is not installed\") % { action: @action, module_name: @module_name }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}'\") % { action: @action, module_name: @module_name }\n      message << _(\"  Module '%{module_name}' is not installed\") % { module_name: @module_name }\n      message += @suggestions.map do |suggestion|\n        # TRANSLATORS `puppet module %{action} %{suggestion}` is a command line and should not be translated\n        _(\"    You may have meant `puppet module %{action} %{suggestion}`\") % { action: @action, suggestion: suggestion }\n      end\n      # TRANSLATORS `puppet module install` is a command line and should not be translated\n      message << _(\"    Use `puppet module install` to install this module\") if @action == :upgrade\n      message.join(\"\\n\")\n    end\n  end\n\n  class MultipleInstalledError < ModuleToolError\n    def initialize(options)\n      @module_name = options[:module_name]\n      @modules     = options[:installed_modules]\n      @action      = options[:action]\n      # TRANSLATORS \"module path\" refers to a set of directories where modules may be installed\n      super _(\"Could not %{action} '%{module_name}'; module appears in multiple places in the module path\") % { action: @action, module_name: @module_name }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}'\") % { action: @action, module_name: @module_name }\n      message << _(\"  Module '%{module_name}' appears multiple places in the module path\") % { module_name: @module_name }\n      message += @modules.map do |mod|\n        # TRANSLATORS This is repeats as separate lines as a list under \"Module '%{module_name}' appears multiple places in the module path\"\n        _(\"    '%{module_name}' (%{version}) was found in %{path}\") % { module_name: @module_name, version: v(mod.version), path: mod.modulepath }\n      end\n      # TRANSLATORS `--modulepath` is command line option and should not be translated\n      message << _(\"    Use the `--modulepath` option to limit the search to specific directories\")\n      message.join(\"\\n\")\n    end\n  end\n\n  class LocalChangesError < ModuleToolError\n    def initialize(options)\n      @module_name       = options[:module_name]\n      @requested_version = options[:requested_version]\n      @installed_version = options[:installed_version]\n      @action            = options[:action]\n      super _(\"Could not %{action} '%{module_name}'; module has had changes made locally\") % { action: @action, module_name: @module_name }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}' (%{version})\") % { action: @action, module_name: @module_name, version: vstring }\n      message << _(\"  Installed module has had changes made locally\")\n      # TRANSLATORS `puppet module %{action} --ignore-changes` is a command line and should not be translated\n      message << _(\"    Use `puppet module %{action} --ignore-changes` to %{action} this module anyway\") % { action: @action }\n      message.join(\"\\n\")\n    end\n  end\n\n  class InvalidModuleError < ModuleToolError\n    def initialize(name, options)\n      @name   = name\n      @action = options[:action]\n      @error  = options[:error]\n      super _(\"Could not %{action} '%{module_name}'; %{error}\") % { action: @action, module_name: @name, error: @error.message }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}'\") % { action: @action, module_name: @name }\n      message << _(\"  Failure trying to parse metadata\")\n      message << _(\"    Original message was: %{message}\") % { message: @error.message }\n      message.join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/errors/uninstaller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool::Errors\n  class UninstallError < ModuleToolError; end\n\n  class NoVersionMatchesError < UninstallError\n    def initialize(options)\n      @module_name = options[:module_name]\n      @modules     = options[:installed_modules]\n      @version     = options[:version_range]\n      super _(\"Could not uninstall '%{module_name}'; no installed version matches\") % { module_name: @module_name }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not uninstall module '%{module_name}' (%{version})\") % { module_name: @module_name, version: v(@version) }\n      message << _(\"  No installed version of '%{module_name}' matches (%{version})\") % { module_name: @module_name, version: v(@version) }\n      message += @modules.map do |mod|\n        _(\"    '%{module_name}' (%{version}) is installed in %{path}\") % { module_name: mod[:name], version: v(mod[:version]), path: mod[:path] }\n      end\n      message.join(\"\\n\")\n    end\n  end\n\n  class ModuleIsRequiredError < UninstallError\n    def initialize(options)\n      @module_name       = options[:module_name]\n      @required_by       = options[:required_by]\n      @requested_version = options[:requested_version]\n      @installed_version = options[:installed_version]\n\n      super _(\"Could not uninstall '%{module_name}'; installed modules still depend upon it\") % { module_name: @module_name }\n    end\n\n    def multiline\n      message = []\n      if @requested_version\n        message << _(\"Could not uninstall module '%{module_name}' (v%{requested_version})\") % { module_name: @module_name, requested_version: @requested_version }\n      else\n        message << _(\"Could not uninstall module '%{module_name}'\") % { module_name: @module_name }\n      end\n      message << _(\"  Other installed modules have dependencies on '%{module_name}' (%{version})\") % { module_name: @module_name, version: v(@installed_version) }\n      message += @required_by.map do |mod|\n        _(\"    '%{module_name}' (%{version}) requires '%{module_dep}' (%{dep_version})\") % { module_name: mod['name'], version: v(mod['version']), module_dep: @module_name, dep_version: v(mod['version_requirement']) }\n      end\n      # TRANSLATORS `puppet module uninstall --force` is a command line option that should not be translated\n      message << _(\"    Use `puppet module uninstall --force` to uninstall this module anyway\")\n      message.join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/errors/upgrader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool::Errors\n  class UpgradeError < ModuleToolError\n    def initialize(msg)\n      @action = :upgrade\n      super\n    end\n  end\n\n  class VersionAlreadyInstalledError < UpgradeError\n    attr_reader :newer_versions\n\n    def initialize(options)\n      @module_name       = options[:module_name]\n      @requested_version = options[:requested_version]\n      @installed_version = options[:installed_version]\n      @dependency_name   = options[:dependency_name]\n      @newer_versions    = options[:newer_versions]\n      @possible_culprits = options[:possible_culprits]\n      super _(\"Could not upgrade '%{module_name}'; more recent versions not found\") % { module_name: @module_name }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not upgrade module '%{module_name}' (%{version})\") % { module_name: @module_name, version: vstring }\n      if @newer_versions.empty?\n        message << _(\"  The installed version is already the latest version matching %{version}\") % { version: vstring }\n      else\n        message << _(\"  There are %{count} newer versions\") % { count: @newer_versions.length }\n        message << _(\"    No combination of dependency upgrades would satisfy all dependencies\")\n        unless @possible_culprits.empty?\n          message << _(\"    Dependencies will not be automatically upgraded across major versions\")\n          message << _(\"    Upgrading one or more of these modules may permit the upgrade to succeed:\")\n          @possible_culprits.each do |name|\n            message << \"    - #{name}\"\n          end\n        end\n      end\n      # TRANSLATORS `puppet module upgrade --force` is a command line option that should not be translated\n      message << _(\"    Use `puppet module upgrade --force` to upgrade only this module\")\n      message.join(\"\\n\")\n    end\n  end\n\n  class DowngradingUnsupportedError < UpgradeError\n    def initialize(options)\n      @module_name = options[:module_name]\n      @requested_version = options[:requested_version]\n      @installed_version = options[:installed_version]\n      @conditions        = options[:conditions]\n      @action            = options[:action]\n\n      super _(\"Could not %{action} '%{module_name}' (%{version}); downgrades are not allowed\") % { action: @action, module_name: @module_name, version: vstring }\n    end\n\n    def multiline\n      message = []\n      message << _(\"Could not %{action} module '%{module_name}' (%{version})\") % { action: @action, module_name: @module_name, version: vstring }\n      message << _(\"  Downgrading is not allowed.\")\n      message.join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/errors.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\n\nmodule Puppet::ModuleTool\n  module Errors\n    require_relative 'errors/base'\n    require_relative 'errors/installer'\n    require_relative 'errors/uninstaller'\n    require_relative 'errors/upgrader'\n    require_relative 'errors/shared'\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/install_directory.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\nrequire_relative '../../puppet/module_tool/errors'\n\nmodule Puppet\n  module ModuleTool\n    # Control the install location for modules.\n    class InstallDirectory\n      include Puppet::ModuleTool::Errors\n\n      attr_reader :target\n\n      def initialize(target)\n        @target = target\n      end\n\n      # prepare the module install location. This will create the location if\n      # needed.\n      def prepare(module_name, version)\n        return if @target.directory?\n\n        begin\n          @target.mkpath\n          Puppet.notice _(\"Created target directory %{dir}\") % { dir: @target }\n        rescue SystemCallError => orig_error\n          raise converted_to_friendly_error(module_name, version, orig_error)\n        end\n      end\n\n      private\n\n      ERROR_MAPPINGS = {\n        Errno::EACCES => PermissionDeniedCreateInstallDirectoryError,\n        Errno::EEXIST => InstallPathExistsNotDirectoryError,\n      }\n\n      def converted_to_friendly_error(module_name, version, orig_error)\n        return orig_error unless ERROR_MAPPINGS.include?(orig_error.class)\n\n        ERROR_MAPPINGS[orig_error.class].new(orig_error,\n                                             :requested_module => module_name,\n                                             :requested_version => version,\n                                             :directory => @target.to_s)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/installed_modules.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\n\nrequire_relative '../../puppet/forge'\nrequire_relative '../../puppet/module_tool'\n\nmodule Puppet::ModuleTool\n  class InstalledModules < SemanticPuppet::Dependency::Source\n    attr_reader :modules, :by_name\n\n    def priority\n      10\n    end\n\n    def initialize(env)\n      @env = env\n      modules = env.modules_by_path\n\n      @fetched = []\n      @modules = {}\n      @by_name = {}\n      env.modulepath.each do |path|\n        modules[path].each do |mod|\n          @by_name[mod.name] = mod\n          next unless mod.has_metadata?\n\n          release = ModuleRelease.new(self, mod)\n          @modules[release.name] ||= release\n        end\n      end\n\n      @modules.freeze\n    end\n\n    # Fetches {ModuleRelease} entries for each release of the named module.\n    #\n    # @param name [String] the module name to look up\n    # @return [Array<SemanticPuppet::Dependency::ModuleRelease>] a list of releases for\n    #         the given name\n    # @see SemanticPuppet::Dependency::Source#fetch\n    def fetch(name)\n      name = name.tr('/', '-')\n\n      if @modules.key? name\n        @fetched << name\n        [@modules[name]]\n      else\n        []\n      end\n    end\n\n    def fetched\n      @fetched\n    end\n\n    class ModuleRelease < SemanticPuppet::Dependency::ModuleRelease\n      attr_reader :mod, :metadata\n\n      def initialize(source, mod)\n        @mod = mod\n        @metadata = mod.metadata\n        name = mod.forge_name.tr('/', '-')\n        begin\n          version = SemanticPuppet::Version.parse(mod.version)\n        rescue SemanticPuppet::Version::ValidationFailure\n          Puppet.warning _(\"%{module_name} (%{path}) has an invalid version number (%{version}). The version has been set to 0.0.0. If you are the maintainer for this module, please update the metadata.json with a valid Semantic Version (http://semver.org).\") % { module_name: mod.name, path: mod.path, version: mod.version }\n          version = SemanticPuppet::Version.parse(\"0.0.0\")\n        end\n        release = \"#{name}@#{version}\"\n\n        super(source, name, version, {})\n\n        if mod.dependencies\n          mod.dependencies.each do |dependency|\n            results = Puppet::ModuleTool.parse_module_dependency(release, dependency)\n            dep_name, parsed_range, range = results\n\n            add_constraint('initialize', dep_name, range.to_s) do |node|\n              parsed_range === node.version\n            end\n          end\n        end\n      end\n\n      def install_dir\n        Pathname.new(@mod.path).dirname\n      end\n\n      def install(dir)\n        # If we're already installed, there's no need for us to faff about.\n      end\n\n      def prepare\n        # We're already installed; what preparation remains?\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/local_tarball.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire 'tmpdir'\n\nrequire_relative '../../puppet/forge'\nrequire_relative '../../puppet/module_tool'\n\nmodule Puppet::ModuleTool\n  class LocalTarball < SemanticPuppet::Dependency::Source\n    attr_accessor :release\n\n    def initialize(filename)\n      unpack(filename, tmpdir)\n      Puppet.debug \"Unpacked local tarball to #{tmpdir}\"\n\n      mod = Puppet::Module.new('tarball', tmpdir, nil)\n      @release = ModuleRelease.new(self, mod)\n    end\n\n    def fetch(name)\n      if @release.name == name\n        [@release]\n      else\n        []\n      end\n    end\n\n    def prepare(release)\n      release.mod.path\n    end\n\n    def install(release, dir)\n      staging_dir = release.prepare\n\n      module_dir = dir + release.name[/-(.*)/, 1]\n      module_dir.rmtree if module_dir.exist?\n\n      # Make sure unpacked module has the same ownership as the folder we are moving it into.\n      Puppet::ModuleTool::Applications::Unpacker.harmonize_ownership(dir, staging_dir)\n\n      FileUtils.mv(staging_dir, module_dir)\n    end\n\n    class ModuleRelease < SemanticPuppet::Dependency::ModuleRelease\n      attr_reader :mod, :install_dir, :metadata\n\n      def initialize(source, mod)\n        @mod = mod\n        @metadata = mod.metadata\n        name = mod.forge_name.tr('/', '-')\n        version = SemanticPuppet::Version.parse(mod.version)\n        release = \"#{name}@#{version}\"\n\n        if mod.dependencies\n          dependencies = mod.dependencies.map do |dep|\n            Puppet::ModuleTool.parse_module_dependency(release, dep)[0..1]\n          end\n          dependencies = dependencies.to_h\n        end\n\n        super(source, name, version, dependencies || {})\n      end\n\n      def install(dir)\n        @source.install(self, dir)\n        @install_dir = dir\n      end\n\n      def prepare\n        @source.prepare(self)\n      end\n    end\n\n    private\n\n    # Obtain a suitable temporary path for unpacking tarballs\n    #\n    # @return [String] path to temporary unpacking location\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    def tmpdir\n      @dir ||= Dir.mktmpdir('local-tarball', Puppet::Forge::Cache.base_path)\n    end\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n\n    def unpack(file, destination)\n      Puppet::ModuleTool::Applications::Unpacker.unpack(file, destination)\n    rescue Puppet::ExecutionFailure => e\n      raise RuntimeError, _(\"Could not extract contents of module archive: %{message}\") % { message: e.message }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/metadata.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\nrequire_relative '../../puppet/network/format_support'\nrequire 'uri'\nrequire_relative '../../puppet/util/json'\nrequire 'set'\n\nmodule Puppet::ModuleTool\n  # This class provides a data structure representing a module's metadata.\n  # @api private\n  class Metadata\n    include Puppet::Network::FormatSupport\n\n    attr_accessor :module_name\n\n    DEFAULTS = {\n      'name' => nil,\n      'version' => nil,\n      'author' => nil,\n      'summary' => nil,\n      'license' => 'Apache-2.0',\n      'source' => '',\n      'project_page' => nil,\n      'issues_url' => nil,\n      'dependencies' => Set.new.freeze,\n      'data_provider' => nil,\n    }\n\n    def initialize\n      @data = DEFAULTS.dup\n      @data['dependencies'] = @data['dependencies'].dup\n    end\n\n    # Returns a filesystem-friendly version of this module name.\n    def dashed_name\n      @data['name'].tr('/', '-') if @data['name']\n    end\n\n    # Returns a string that uniquely represents this version of this module.\n    def release_name\n      return nil unless @data['name'] && @data['version']\n\n      [dashed_name, @data['version']].join('-')\n    end\n\n    alias :name :module_name\n    alias :full_module_name :dashed_name\n\n    # Merges the current set of metadata with another metadata hash.  This\n    # method also handles the validation of module names and versions, in an\n    # effort to be proactive about module publishing constraints.\n    def update(data)\n      process_name(data) if data['name']\n      process_version(data) if data['version']\n      process_source(data) if data['source']\n      process_data_provider(data) if data['data_provider']\n      merge_dependencies(data) if data['dependencies']\n\n      @data.merge!(data)\n      self\n    end\n\n    # Validates the name and version_requirement for a dependency, then creates\n    # the Dependency and adds it.\n    # Returns the Dependency that was added.\n    def add_dependency(name, version_requirement = nil, repository = nil)\n      validate_name(name)\n      validate_version_range(version_requirement) if version_requirement\n\n      dup = @data['dependencies'].find { |d| d.full_module_name == name && d.version_requirement != version_requirement }\n      raise ArgumentError, _(\"Dependency conflict for %{module_name}: Dependency %{name} was given conflicting version requirements %{version_requirement} and %{dup_version}. Verify that there are no duplicates in the metadata.json.\") % { module_name: full_module_name, name: name, version_requirement: version_requirement, dup_version: dup.version_requirement } if dup\n\n      dep = Dependency.new(name, version_requirement, repository)\n      @data['dependencies'].add(dep)\n\n      dep\n    end\n\n    # Provides an accessor for the now defunct 'description' property.  This\n    # addresses a regression in Puppet 3.6.x where previously valid templates\n    # referring to the 'description' property were broken.\n    # @deprecated\n    def description\n      @data['description']\n    end\n\n    def dependencies\n      @data['dependencies'].to_a\n    end\n\n    # Returns a hash of the module's metadata.  Used by Puppet's automated\n    # serialization routines.\n    #\n    # @see Puppet::Network::FormatSupport#to_data_hash\n    def to_hash\n      @data\n    end\n    alias :to_data_hash :to_hash\n\n    def to_json\n      data = @data.dup.merge('dependencies' => dependencies)\n\n      contents = data.keys.map do |k|\n        value = begin\n          Puppet::Util::Json.dump(data[k], :pretty => true)\n        rescue\n          data[k].to_json\n        end\n        %Q(\"#{k}\": #{value})\n      end\n\n      \"{\\n\" + contents.join(\",\\n\").gsub(/^/, '  ') + \"\\n}\\n\"\n    end\n\n    # Expose any metadata keys as callable reader methods.\n    def method_missing(name, *args)\n      return @data[name.to_s] if @data.key? name.to_s\n\n      super\n    end\n\n    private\n\n    # Do basic validation and parsing of the name parameter.\n    def process_name(data)\n      validate_name(data['name'])\n      author, @module_name = data['name'].split(%r{[-/]}, 2)\n\n      data['author'] ||= author if @data['author'] == DEFAULTS['author']\n    end\n\n    # Do basic validation on the version parameter.\n    def process_version(data)\n      validate_version(data['version'])\n    end\n\n    def process_data_provider(data)\n      validate_data_provider(data['data_provider'])\n    end\n\n    # Do basic parsing of the source parameter.  If the source is hosted on\n    # GitHub, we can predict sensible defaults for both project_page and\n    # issues_url.\n    def process_source(data)\n      if data['source'] =~ %r{://}\n        source_uri = URI.parse(data['source'])\n      else\n        source_uri = URI.parse(\"http://#{data['source']}\")\n      end\n\n      if source_uri.host =~ /^(www\\.)?github\\.com$/\n        source_uri.scheme = 'https'\n        source_uri.path.sub!(/\\.git$/, '')\n        data['project_page'] ||= @data['project_page'] || source_uri.to_s\n        data['issues_url'] ||= @data['issues_url'] || source_uri.to_s.sub(%r{/*$}, '') + '/issues'\n      end\n    rescue URI::Error\n      nil\n    end\n\n    # Validates and parses the dependencies.\n    def merge_dependencies(data)\n      data['dependencies'].each do |dep|\n        add_dependency(dep['name'], dep['version_requirement'], dep['repository'])\n      end\n\n      # Clear dependencies so @data dependencies are not overwritten\n      data.delete 'dependencies'\n    end\n\n    # Validates that the given module name is both namespaced and well-formed.\n    def validate_name(name)\n      return if name =~ %r{\\A[a-z0-9]+[-/][a-z][a-z0-9_]*\\Z}i\n\n      namespace, modname = name.split(%r{[-/]}, 2)\n      modname = :namespace_missing if namespace == ''\n\n      err = case modname\n            when nil, '', :namespace_missing\n              _(\"the field must be a namespaced module name\")\n            when /[^a-z0-9_]/i\n              _(\"the module name contains non-alphanumeric (or underscore) characters\")\n            when /^[^a-z]/i\n              _(\"the module name must begin with a letter\")\n            else\n              _(\"the namespace contains non-alphanumeric characters\")\n            end\n\n      raise ArgumentError, _(\"Invalid 'name' field in metadata.json: %{err}\") % { err: err }\n    end\n\n    # Validates that the version string can be parsed as per SemVer.\n    def validate_version(version)\n      return if SemanticPuppet::Version.valid?(version)\n\n      err = _(\"version string cannot be parsed as a valid Semantic Version\")\n      raise ArgumentError, _(\"Invalid 'version' field in metadata.json: %{err}\") % { err: err }\n    end\n\n    # Validates that the given _value_ is a symbolic name that starts with a letter\n    # and then contains only letters, digits, or underscore. Will raise an ArgumentError\n    # if that's not the case.\n    #\n    # @param value [Object] The value to be tested\n    def validate_data_provider(value)\n      if value.is_a?(String)\n        unless value =~ /^[a-zA-Z][a-zA-Z0-9_]*$/\n          if value =~ /^[a-zA-Z]/\n            raise ArgumentError, _(\"field 'data_provider' contains non-alphanumeric characters\")\n          else\n            raise ArgumentError, _(\"field 'data_provider' must begin with a letter\")\n          end\n        end\n      else\n        raise ArgumentError, _(\"field 'data_provider' must be a string\")\n      end\n    end\n\n    # Validates that the version range can be parsed by Semantic.\n    def validate_version_range(version_range)\n      SemanticPuppet::VersionRange.parse(version_range)\n    rescue ArgumentError => e\n      raise ArgumentError, _(\"Invalid 'version_range' field in metadata.json: %{err}\") % { err: e }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/shared_behaviors.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::ModuleTool::Shared\n  include Puppet::ModuleTool::Errors\n\n  def get_local_constraints\n    @local      = Hash.new { |h, k| h[k] = {} }\n    @conditions = Hash.new { |h, k| h[k] = [] }\n    @installed  = Hash.new { |h, k| h[k] = [] }\n\n    @environment.modules_by_path.values.flatten.each do |mod|\n      mod_name = (mod.forge_name || mod.name).tr('/', '-')\n      @installed[mod_name] << mod\n      d = @local[\"#{mod_name}@#{mod.version}\"]\n      (mod.dependencies || []).each do |hash|\n        name = hash['name']\n        conditions = hash['version_requirement']\n        name = name.tr('/', '-')\n        d[name] = conditions\n        @conditions[name] << {\n          :module => mod_name,\n          :version => mod.version,\n          :dependency => conditions\n        }\n      end\n    end\n  end\n\n  def get_remote_constraints(forge)\n    @remote   = Hash.new { |h, k| h[k] = {} }\n    @urls     = {}\n    @versions = Hash.new { |h, k| h[k] = [] }\n\n    Puppet.notice _(\"Downloading from %{uri} ...\") % { uri: forge.uri }\n    author, modname = Puppet::ModuleTool.username_and_modname_from(@module_name)\n    info = forge.remote_dependency_info(author, modname, @options[:version])\n    info.each do |pair|\n      mod_name, releases = pair\n      mod_name = mod_name.tr('/', '-')\n      releases.each do |rel|\n        semver = begin\n          SemanticPuppet::Version.parse(rel['version'])\n        rescue\n          SemanticPuppet::Version::MIN\n        end\n        @versions[mod_name] << { :vstring => rel['version'], :semver => semver }\n        @versions[mod_name].sort_by! { |a| a[:semver] }\n        @urls[\"#{mod_name}@#{rel['version']}\"] = rel['file']\n        d = @remote[\"#{mod_name}@#{rel['version']}\"]\n        (rel['dependencies'] || []).each do |name, conditions|\n          d[name.tr('/', '-')] = conditions\n        end\n      end\n    end\n  end\n\n  def implicit_version(mod)\n    return :latest if @conditions[mod].empty?\n    if @conditions[mod].all? { |c| c[:queued] || c[:module] == :you }\n      return :latest\n    end\n\n    :best\n  end\n\n  def annotated_version(mod, versions)\n    if versions.empty?\n      implicit_version(mod)\n    else\n      \"#{implicit_version(mod)}: #{versions.last}\"\n    end\n  end\n\n  def resolve_constraints(dependencies, source = [{ :name => :you }], seen = {}, action = @action)\n    dependencies = dependencies.filter_map do |mod, range|\n      source.last[:dependency] = range\n\n      @conditions[mod] << {\n        :module => source.last[:name],\n        :version => source.last[:version],\n        :dependency => range,\n        :queued => true\n      }\n\n      if forced?\n        range = begin\n          Puppet::Module.parse_range(@version)\n        rescue\n          Puppet::Module.parse_range('>= 0.0.0')\n        end\n      else\n        range = (@conditions[mod]).map do |r|\n          Puppet::Module.parse_range(r[:dependency])\n        rescue\n          Puppet::Module.parse_range('>= 0.0.0')\n        end.inject(&:&)\n      end\n\n      if @action == :install && seen.include?(mod)\n        next if range === seen[mod][:semver]\n\n        req_module   = @module_name\n        req_versions = @versions[@module_name.to_s].map { |v| v[:semver] }\n        raise InvalidDependencyCycleError,\n              :module_name => mod,\n              :source => (source + [{ :name => mod, :version => source.last[:dependency] }]),\n              :requested_module => req_module,\n              :requested_version => @version || annotated_version(req_module, req_versions),\n              :conditions => @conditions\n      end\n\n      if !(forced? || @installed[mod].empty? || source.last[:name] == :you)\n        next if range === SemanticPuppet::Version.parse(@installed[mod].first.version)\n\n        action = :upgrade\n      elsif @installed[mod].empty?\n        action = :install\n      end\n\n      if action == :upgrade\n        @conditions.each { |_, conds| conds.delete_if { |c| c[:module] == mod } }\n      end\n\n      versions = @versions[mod.to_s].select { |h| range === h[:semver] }\n      valid_versions = versions.select { |x| x[:semver].special == '' }\n      valid_versions = versions if valid_versions.empty?\n\n      version = valid_versions.last\n      unless version\n        req_module   = @module_name\n        req_versions = @versions[@module_name.to_s].map { |v| v[:semver] }\n        raise NoVersionsSatisfyError,\n              :requested_name => req_module,\n              :requested_version => @version || annotated_version(req_module, req_versions),\n              :installed_version => @installed[@module_name].empty? ? nil : @installed[@module_name].first.version,\n              :dependency_name => mod,\n              :conditions => @conditions[mod],\n              :action => @action\n      end\n\n      seen[mod] = version\n\n      {\n        :module => mod,\n        :version => version,\n        :action => action,\n        :previous_version => @installed[mod].empty? ? nil : @installed[mod].first.version,\n        :file => @urls[\"#{mod}@#{version[:vstring]}\"],\n        :path => if action == :install\n                   @options[:target_dir]\n                 else\n                   @installed[mod].empty? ? @options[:target_dir] : @installed[mod].first.modulepath\n                 end,\n        :dependencies => []\n      }\n    end\n    dependencies.each do |mod|\n      deps = @remote[\"#{mod[:module]}@#{mod[:version][:vstring]}\"].sort_by(&:first)\n      mod[:dependencies] = resolve_constraints(deps, source + [{ :name => mod[:module], :version => mod[:version][:vstring] }], seen, :install)\n    end unless @ignore_dependencies\n    dependencies\n  end\n\n  def download_tarballs(graph, default_path, forge)\n    graph.map do |release|\n      begin\n        if release[:tarball]\n          cache_path = Pathname(release[:tarball])\n        else\n          cache_path = forge.retrieve(release[:file])\n        end\n      rescue OpenURI::HTTPError => e\n        raise RuntimeError, _(\"Could not download module: %{message}\") % { message: e.message }, e.backtrace\n      end\n\n      [\n        { (release[:path] ||= default_path) => cache_path },\n        *download_tarballs(release[:dependencies], default_path, forge)\n      ]\n    end.flatten\n  end\n\n  def forced?\n    options[:force]\n  end\n\n  def add_module_name_constraints_to_graph(graph)\n    # Puppet modules are installed by \"module name\", but resolved by\n    # \"full name\" (including namespace).  So that we don't run into\n    # problems at install time, we should reject any solution that\n    # depends on multiple nodes with the same \"module name\".\n    graph.add_graph_constraint('PMT') do |nodes|\n      names = nodes.map { |x| x.dependency_names + [x.name] }.flatten\n      names = names.map { |x| x.tr('/', '-') }.uniq\n      names = names.map { |x| x[/-(.*)/, 1] }\n      names.length == names.uniq.length\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/tar/gnu.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'shellwords'\n\nclass Puppet::ModuleTool::Tar::Gnu\n  def unpack(sourcefile, destdir, owner)\n    safe_sourcefile = Shellwords.shellescape(File.expand_path(sourcefile))\n    destdir = File.expand_path(destdir)\n    safe_destdir = Shellwords.shellescape(destdir)\n\n    Puppet::Util::Execution.execute(\"gzip -dc #{safe_sourcefile} | tar --extract --no-same-owner --directory #{safe_destdir} --file -\")\n    Puppet::Util::Execution.execute(['find', destdir, '-type', 'd', '-exec', 'chmod', '755', '{}', '+'])\n    Puppet::Util::Execution.execute(['find', destdir, '-type', 'f', '-exec', 'chmod', 'u+rw,g+r,a-st', '{}', '+'])\n    Puppet::Util::Execution.execute(['chown', '-R', owner, destdir])\n  end\n\n  def pack(sourcedir, destfile)\n    safe_sourcedir = Shellwords.shellescape(sourcedir)\n    safe_destfile = Shellwords.shellescape(File.basename(destfile))\n\n    Puppet::Util::Execution.execute(\"tar cf - #{safe_sourcedir} | gzip -c > #{safe_destfile}\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/tar/mini.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::ModuleTool::Tar::Mini\n  def unpack(sourcefile, destdir, _)\n    Zlib::GzipReader.open(sourcefile) do |reader|\n      # puppet doesn't have a hard dependency on minitar, so we\n      # can't be certain which version is installed. If it's 0.9\n      # or above then we can prevent minitar from fsync'ing each\n      # extracted file and directory, otherwise fallback to the\n      # old behavior\n      args = [reader, destdir, find_valid_files(reader)]\n      spec = Gem::Specification.find_by_name('minitar')\n      if spec && spec.version >= Gem::Version.new('0.9')\n        args << { :fsync => false }\n      end\n      Archive::Tar::Minitar.unpack(*args) do |action, name, stats|\n        case action\n        when :dir\n          validate_entry(destdir, name)\n          set_dir_mode!(stats)\n          Puppet.debug(\"Extracting: #{destdir}/#{name}\")\n        when :file_start\n          # Octal string of the old file mode.\n          validate_entry(destdir, name)\n          set_file_mode!(stats)\n          Puppet.debug(\"Extracting: #{destdir}/#{name}\")\n        end\n        set_default_user_and_group!(stats)\n        stats\n      end\n    end\n  end\n\n  def pack(sourcedir, destfile)\n    Zlib::GzipWriter.open(destfile) do |writer|\n      Archive::Tar::Minitar.pack(sourcedir, writer) do |step, name, stats|\n        # TODO smcclellan 2017-10-31 Set permissions here when this yield block\n        # executes before the header is written. As it stands, the `stats`\n        # argument isn't mutable in a way that will effect the desired mode for\n        # the file.\n      end\n    end\n  end\n\n  private\n\n  EXECUTABLE = 0o755\n  NOT_EXECUTABLE = 0o644\n  USER_EXECUTE = 0o100\n\n  def set_dir_mode!(stats)\n    if stats.key?(:mode)\n      # This is only the case for `pack`, so this code will not run.\n      stats[:mode] = EXECUTABLE\n    elsif stats.key?(:entry)\n      old_mode = stats[:entry].instance_variable_get(:@mode)\n      if old_mode.is_a?(Integer)\n        stats[:entry].instance_variable_set(:@mode, EXECUTABLE)\n      end\n    end\n  end\n\n  # Sets a file mode to 0755 if the file is executable by the user.\n  # Sets a file mode to 0644 if the file mode is set (non-Windows).\n  def sanitized_mode(old_mode)\n    old_mode & USER_EXECUTE != 0 ? EXECUTABLE : NOT_EXECUTABLE\n  end\n\n  def set_file_mode!(stats)\n    if stats.key?(:mode)\n      # This is only the case for `pack`, so this code will not run.\n      stats[:mode] = sanitized_mode(stats[:mode])\n    elsif stats.key?(:entry)\n      old_mode = stats[:entry].instance_variable_get(:@mode)\n      # If the user can execute the file, set 0755, otherwise 0644.\n      if old_mode.is_a?(Integer)\n        new_mode = sanitized_mode(old_mode)\n        stats[:entry].instance_variable_set(:@mode, new_mode)\n      end\n    end\n  end\n\n  # Sets UID and GID to 0 for standardization.\n  def set_default_user_and_group!(stats)\n    stats[:uid] = 0\n    stats[:gid] = 0\n  end\n\n  # Find all the valid files in tarfile.\n  #\n  # This check was mainly added to ignore 'x' and 'g' flags from the PAX\n  # standard but will also ignore any other non-standard tar flags.\n  # tar format info: https://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Ftaf.htm\n  # pax format info: https://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Fpxarchfm.htm\n  def find_valid_files(tarfile)\n    Archive::Tar::Minitar.open(tarfile).collect do |entry|\n      flag = entry.typeflag\n      if flag.nil? || flag =~ /[[:digit:]]/ && (0..7).cover?(flag.to_i)\n        entry.full_name\n      else\n        Puppet.debug \"Invalid tar flag '#{flag}' will not be extracted: #{entry.name}\"\n        next\n      end\n    end\n  end\n\n  def validate_entry(destdir, path)\n    if Pathname.new(path).absolute?\n      raise Puppet::ModuleTool::Errors::InvalidPathInPackageError, :entry_path => path, :directory => destdir\n    end\n\n    path = Pathname.new(File.join(destdir, path)).cleanpath.to_path\n\n    if path !~ /\\A#{Regexp.escape destdir}/\n      raise Puppet::ModuleTool::Errors::InvalidPathInPackageError, :entry_path => path, :directory => destdir\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool/tar.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/module_tool'\nrequire_relative '../../puppet/util'\n\nmodule Puppet::ModuleTool::Tar\n  require_relative 'tar/gnu'\n  require_relative 'tar/mini'\n\n  def self.instance\n    if Puppet.features.minitar? && Puppet.features.zlib?\n      Mini.new\n    elsif Puppet::Util.which('tar') && !Puppet::Util::Platform.windows?\n      Gnu.new\n    else\n      # TRANSLATORS \"tar\" is a program name and should not be translated\n      raise RuntimeError, _('No suitable tar implementation found')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/module_tool.rb",
    "content": "# encoding: UTF-8\n# frozen_string_literal: true\n\n# Load standard libraries\nrequire 'pathname'\nrequire 'fileutils'\nrequire_relative '../puppet/util/colors'\n\nmodule Puppet\n  module ModuleTool\n    require_relative 'module_tool/tar'\n    extend Puppet::Util::Colors\n\n    # Directory and names that should not be checksummed.\n    ARTIFACTS = ['pkg', /^\\./, /^~/, /^#/, 'coverage', 'checksums.json', 'REVISION']\n    FULL_MODULE_NAME_PATTERN = %r{\\A([^-/|.]+)[-|/](.+)\\z}\n    REPOSITORY_URL = Puppet.settings[:module_repository]\n\n    # Is this a directory that shouldn't be checksummed?\n    #\n    # TODO: Should this be part of Checksums?\n    # TODO: Rename this method to reflect its purpose?\n    # TODO: Shouldn't this be used when building packages too?\n    def self.artifact?(path)\n      case File.basename(path)\n      when *ARTIFACTS\n        true\n      else\n        false\n      end\n    end\n\n    # Return the +username+ and +modname+ for a given +full_module_name+, or raise an\n    # ArgumentError if the argument isn't parseable.\n    def self.username_and_modname_from(full_module_name)\n      matcher = full_module_name.match(FULL_MODULE_NAME_PATTERN)\n      if matcher\n        matcher.captures\n      else\n        raise ArgumentError, _(\"Not a valid full name: %{full_module_name}\") % { full_module_name: full_module_name }\n      end\n    end\n\n    # Find the module root when given a path by checking each directory up from\n    # its current location until it finds one that satisfies is_module_root?\n    #\n    # @param path [Pathname, String] path to start from\n    # @return [Pathname, nil] the root path of the module directory or nil if\n    #   we cannot find one\n    def self.find_module_root(path)\n      path = Pathname.new(path) if path.instance_of?(String)\n\n      path.expand_path.ascend do |p|\n        return p if is_module_root?(p)\n      end\n\n      nil\n    end\n\n    # Analyse path to see if it is a module root directory by detecting a\n    # file named 'metadata.json'\n    #\n    # @param path [Pathname, String] path to analyse\n    # @return [Boolean] true if the path is a module root, false otherwise\n    def self.is_module_root?(path)\n      path = Pathname.new(path) if path.instance_of?(String)\n\n      FileTest.file?(path + 'metadata.json')\n    end\n\n    # Builds a formatted tree from a list of node hashes containing +:text+\n    # and +:dependencies+ keys.\n    def self.format_tree(nodes, level = 0)\n      str = ''.dup\n      nodes.each_with_index do |node, i|\n        last_node = nodes.length - 1 == i\n        deps = node[:dependencies] || []\n\n        str << (indent = \"  \" * level)\n        str << (last_node ? \"└\" : \"├\")\n        str << \"─\"\n        str << (deps.empty? ? \"─\" : \"┬\")\n        str << \" #{node[:text]}\\n\"\n\n        branch = format_tree(deps, level + 1)\n        branch.gsub!(/^#{indent} /, indent + '│') unless last_node\n        str << branch\n      end\n\n      str\n    end\n\n    def self.build_tree(mods, dir)\n      mods.each do |mod|\n        version_string = mod[:version].to_s.sub(/^(?!v)/, 'v')\n\n        if mod[:action] == :upgrade\n          previous_version = mod[:previous_version].to_s.sub(/^(?!v)/, 'v')\n          version_string = \"#{previous_version} -> #{version_string}\"\n        end\n\n        mod[:text] = \"#{mod[:name]} (#{colorize(:cyan, version_string)})\"\n        mod[:text] += \" [#{mod[:path]}]\" unless mod[:path].to_s == dir.to_s\n\n        deps = mod[:dependencies] || []\n        deps.sort_by! { |a| a[:name] }\n        build_tree(deps, dir)\n      end\n    end\n\n    # @param options [Hash<Symbol,String>] This hash will contain any\n    #   command-line arguments that are not Settings, as those will have already\n    #   been extracted by the underlying application code.\n    #\n    # @note Unfortunately the whole point of this method is the side effect of\n    # modifying the options parameter.  This same hash is referenced both\n    # when_invoked and when_rendering.  For this reason, we are not returning\n    # a duplicate.\n    # @todo Validate the above note...\n    #\n    # An :environment_instance and a :target_dir are added/updated in the\n    # options parameter.\n    #\n    # @api private\n    def self.set_option_defaults(options)\n      current_environment = environment_from_options(options)\n\n      modulepath = [options[:target_dir]] + current_environment.full_modulepath\n\n      face_environment = current_environment.override_with(:modulepath => modulepath.compact)\n\n      options[:environment_instance] = face_environment\n\n      # Note: environment will have expanded the path\n      options[:target_dir] = face_environment.full_modulepath.first\n    end\n\n    # Given a hash of options, we should discover or create a\n    # {Puppet::Node::Environment} instance that reflects the provided options.\n    #\n    # Generally speaking, the `:modulepath` parameter should supersede all\n    # others, the `:environment` parameter should follow after that, and we\n    # should default to Puppet's current environment.\n    #\n    # @param options [{Symbol => Object}] the options to derive environment from\n    # @return [Puppet::Node::Environment] the environment described by the options\n    def self.environment_from_options(options)\n      if options[:modulepath]\n        path = options[:modulepath].split(File::PATH_SEPARATOR)\n        Puppet::Node::Environment.create(:anonymous, path, '')\n      elsif options[:environment].is_a?(Puppet::Node::Environment)\n        options[:environment]\n      elsif options[:environment]\n        # This use of looking up an environment is correct since it honours\n        # a request to get a particular environment via environment name.\n        Puppet.lookup(:environments).get!(options[:environment])\n      else\n        Puppet.lookup(:current_environment)\n      end\n    end\n\n    # Handles parsing of module dependency expressions into proper\n    # {SemanticPuppet::VersionRange}s, including reasonable error handling.\n    #\n    # @param where [String] a description of the thing we're parsing the\n    #        dependency expression for\n    # @param dep [Hash] the dependency description to parse\n    # @return [Array(String, SemanticPuppet::VersionRange, String)] a tuple of the\n    #         dependent module's name, the version range dependency, and the\n    #         unparsed range expression.\n    def self.parse_module_dependency(where, dep)\n      dep_name = dep['name'].tr('/', '-')\n      range = dep['version_requirement'] || '>= 0.0.0'\n\n      begin\n        parsed_range = Module.parse_range(range)\n      rescue ArgumentError => e\n        Puppet.debug \"Error in #{where} parsing dependency #{dep_name} (#{e.message}); using empty range.\"\n        parsed_range = SemanticPuppet::VersionRange::EMPTY_RANGE\n      end\n\n      [dep_name, parsed_range, range]\n    end\n  end\nend\n\n# Load remaining libraries\nrequire_relative 'module_tool/errors'\nrequire_relative 'module_tool/applications'\nrequire_relative 'module_tool/checksums'\nrequire_relative 'module_tool/contents_description'\nrequire_relative 'module_tool/dependency'\nrequire_relative 'module_tool/metadata'\nrequire_relative '../puppet/forge/cache'\nrequire_relative '../puppet/forge'\n"
  },
  {
    "path": "lib/puppet/network/authconfig.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  class Network::AuthConfig\n    def self.authprovider_class=(_)\n      # legacy auth is not supported, ignore\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/authorization.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Network\n  module Authorization\n    class << self\n      # This method is deprecated and will be removed in a future release.\n      def authconfigloader_class=(klass)\n        @authconfigloader_class = klass\n      end\n\n      # Verify something external to puppet is authorizing REST requests, so\n      # we don't fail insecurely due to misconfiguration.\n      def check_external_authorization(method, path)\n        if @authconfigloader_class.nil?\n          message = \"Forbidden request: #{path} (method #{method})\"\n          raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError.new(message, Puppet::Network::HTTP::Issues::FAILED_AUTHORIZATION)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/client_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Network # :nodoc:\n  # A struct-like class for passing around a client request.  It's mostly\n  # just used for validation and authorization.\n  class ClientRequest\n    attr_accessor :name, :ip, :authenticated, :handler, :method\n\n    def authenticated?\n      authenticated\n    end\n\n    # A common way of talking about the full call.  Individual servers\n    # are responsible for setting the values correctly, but this common\n    # format makes it possible to check rights.\n    def call\n      raise ArgumentError, _(\"Request is not set up; cannot build call\") unless handler and method\n\n      [handler, method].join(\".\")\n    end\n\n    def initialize(name, ip, authenticated)\n      @name = name\n      @ip = ip\n      @authenticated = authenticated\n    end\n\n    def to_s\n      \"#{name}(#{ip})\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/format.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/confiner'\n\n# A simple class for modeling encoding formats for moving\n# instances around the network.\nclass Puppet::Network::Format\n  include Puppet::Confiner\n\n  attr_reader :name, :mime\n  attr_accessor :intern_method, :render_method, :intern_multiple_method, :render_multiple_method, :weight, :required_methods, :extension, :charset\n\n  def init_attribute(name, default)\n    value = @options.delete(name)\n    value = default if value.nil?\n\n    send(name.to_s + \"=\", value)\n  end\n\n  def initialize(name, options = {}, &block)\n    @name = name.to_s.downcase.intern\n\n    @options = options\n\n    # This must be done early the values can be used to set required_methods\n    define_method_names\n\n    method_list = {\n      :intern_method => \"from_#{name}\",\n      :intern_multiple_method => \"from_multiple_#{name}\",\n      :render_multiple_method => \"to_multiple_#{name}\",\n      :render_method => \"to_#{name}\"\n    }\n\n    init_attribute(:mime, \"text/#{name}\")\n    init_attribute(:weight, 5)\n    init_attribute(:required_methods, method_list.keys)\n    init_attribute(:extension, name.to_s)\n    init_attribute(:charset, nil)\n\n    method_list.each do |method, value|\n      init_attribute(method, value)\n    end\n\n    raise ArgumentError, _(\"Unsupported option(s) %{options_list}\") % { options_list: @options.keys } unless @options.empty?\n\n    @options = nil\n\n    instance_eval(&block) if block_given?\n  end\n\n  def intern(klass, text)\n    return klass.send(intern_method, text) if klass.respond_to?(intern_method)\n\n    raise NotImplementedError, \"#{klass} does not respond to #{intern_method}; can not intern instances from #{mime}\"\n  end\n\n  def intern_multiple(klass, text)\n    return klass.send(intern_multiple_method, text) if klass.respond_to?(intern_multiple_method)\n\n    raise NotImplementedError, \"#{klass} does not respond to #{intern_multiple_method}; can not intern multiple instances from #{mime}\"\n  end\n\n  def mime=(mime)\n    @mime = mime.to_s.downcase\n  end\n\n  def render(instance)\n    return instance.send(render_method) if instance.respond_to?(render_method)\n\n    raise NotImplementedError, \"#{instance.class} does not respond to #{render_method}; can not render instances to #{mime}\"\n  end\n\n  def render_multiple(instances)\n    # This method implicitly assumes that all instances are of the same type.\n    return instances[0].class.send(render_multiple_method, instances) if instances[0].class.respond_to?(render_multiple_method)\n\n    raise NotImplementedError, _(\"%{klass} does not respond to %{method}; can not render multiple instances to %{mime}\") %\n                               { klass: instances[0].class, method: render_multiple_method, mime: mime }\n  end\n\n  def required_methods_present?(klass)\n    [:intern_method, :intern_multiple_method, :render_multiple_method].each do |name|\n      return false unless required_method_present?(name, klass, :class)\n    end\n\n    return false unless required_method_present?(:render_method, klass, :instance)\n\n    true\n  end\n\n  def supported?(klass)\n    suitable? and required_methods_present?(klass)\n  end\n\n  def to_s\n    \"Puppet::Network::Format[#{name}]\"\n  end\n\n  private\n\n  def define_method_names\n    @intern_method = \"from_#{name}\"\n    @render_method = \"to_#{name}\"\n    @intern_multiple_method = \"from_multiple_#{name}\"\n    @render_multiple_method = \"to_multiple_#{name}\"\n  end\n\n  def required_method_present?(name, klass, type)\n    return true unless required_methods.include?(name)\n\n    method = send(name)\n\n    (type == :class ? klass.respond_to?(method) : klass.method_defined?(method))\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/format_handler.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\nrequire_relative '../../puppet/network'\nrequire_relative '../../puppet/network/format'\n\nmodule Puppet::Network::FormatHandler\n  class FormatError < Puppet::Error; end\n\n  ALL_MEDIA_TYPES = '*/*'\n\n  @formats = {}\n\n  def self.create(*args, &block)\n    instance = Puppet::Network::Format.new(*args, &block)\n\n    @formats[instance.name] = instance\n    instance\n  end\n\n  def self.create_serialized_formats(name, options = {}, &block)\n    [\"application/x-#{name}\", \"application/#{name}\", \"text/x-#{name}\", \"text/#{name}\"].each { |mime_type|\n      create name, { :mime => mime_type }.update(options), &block\n    }\n  end\n\n  def self.format(name)\n    @formats[name.to_s.downcase.intern]\n  end\n\n  def self.format_for(name)\n    name = format_to_canonical_name(name)\n    format(name)\n  end\n\n  def self.format_by_extension(ext)\n    @formats.each do |_name, format|\n      return format if format.extension == ext\n    end\n    nil\n  end\n\n  # Provide a list of all formats.\n  def self.formats\n    @formats.keys\n  end\n\n  # Return a format capable of handling the provided mime type.\n  def self.mime(mimetype)\n    mimetype = mimetype.to_s.downcase\n    @formats.values.find { |format| format.mime == mimetype }\n  end\n\n  # Return a format name given:\n  #  * a format name\n  #  * a mime-type\n  #  * a format instance\n  def self.format_to_canonical_name(format)\n    case format\n    when Puppet::Network::Format\n      out = format\n    when %r{\\w+/\\w+}\n      out = mime(format)\n    else\n      out = format(format)\n    end\n\n    if out.nil?\n      raise ArgumentError, _(\"No format matches the given format name or mime-type (%{format})\") % { format: format }\n    end\n\n    out.name\n  end\n\n  # Determine which of the accepted formats should be used given what is supported.\n  #\n  # @param accepted [Array<String, Symbol>] the accepted formats in a form a\n  #   that generally conforms to an HTTP Accept header. Any quality specifiers\n  #   are ignored and instead the formats are simply in strict preference order\n  #   (most preferred is first)\n  # @param supported [Array<Symbol>] the names of the supported formats (the\n  #   most preferred format is first)\n  # @return [Array<Puppet::Network::Format>] the most suitable formats that\n  #   are both accepted and supported\n  # @api private\n  def self.most_suitable_formats_for(accepted, supported)\n    accepted.collect do |format|\n      format.to_s.sub(/;q=.*$/, '')\n    end.filter_map do |format|\n      if format == ALL_MEDIA_TYPES\n        supported.first\n      else\n        format_to_canonical_name_or_nil(format)\n      end\n    end.find_all do |format|\n      supported.include?(format)\n    end.collect do |format|\n      format_for(format)\n    end\n  end\n\n  # @api private\n  def self.format_to_canonical_name_or_nil(format)\n    format_to_canonical_name(format)\n  rescue ArgumentError\n    nil\n  end\nend\n\nrequire_relative '../../puppet/network/formats'\n"
  },
  {
    "path": "lib/puppet/network/format_support.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/network/format_handler'\n\n# Provides network serialization support when included\n# @api public\nmodule Puppet::Network::FormatSupport\n  def self.included(klass)\n    klass.extend(ClassMethods)\n  end\n\n  module ClassMethods\n    def convert_from(format, data)\n      get_format(format).intern(self, data)\n    rescue => err\n      # TRANSLATORS \"intern\" is a function name and should not be translated\n      raise Puppet::Network::FormatHandler::FormatError, _(\"Could not intern from %{format}: %{err}\") % { format: format, err: err }, err.backtrace\n    end\n\n    def convert_from_multiple(format, data)\n      get_format(format).intern_multiple(self, data)\n    rescue => err\n      # TRANSLATORS \"intern_multiple\" is a function name and should not be translated\n      raise Puppet::Network::FormatHandler::FormatError, _(\"Could not intern_multiple from %{format}: %{err}\") % { format: format, err: err }, err.backtrace\n    end\n\n    def render_multiple(format, instances)\n      get_format(format).render_multiple(instances)\n    rescue => err\n      # TRANSLATORS \"render_multiple\" is a function name and should not be translated\n      raise Puppet::Network::FormatHandler::FormatError, _(\"Could not render_multiple to %{format}: %{err}\") % { format: format, err: err }, err.backtrace\n    end\n\n    def default_format\n      supported_formats[0]\n    end\n\n    def support_format?(name)\n      Puppet::Network::FormatHandler.format(name).supported?(self)\n    end\n\n    def supported_formats\n      result = format_handler.formats.collect do |f|\n        format_handler.format(f)\n      end.find_all do |f|\n        f.supported?(self)\n      end.sort do |a, b|\n        # It's an inverse sort -- higher weight formats go first.\n        b.weight <=> a.weight\n      end\n\n      result = put_preferred_format_first(result).map(&:name)\n\n      Puppet.debug { \"#{friendly_name} supports formats: #{result.join(' ')}\" }\n\n      result\n    end\n\n    # @api private\n    def get_format(format_name)\n      format_handler.format_for(format_name)\n    end\n\n    private\n\n    def format_handler\n      Puppet::Network::FormatHandler\n    end\n\n    def friendly_name\n      if respond_to? :indirection\n        indirection.name\n      else\n        self\n      end\n    end\n\n    def put_preferred_format_first(list)\n      preferred_format = Puppet.settings[:preferred_serialization_format].to_s\n\n      preferred = list.select { |format|\n        format.mime.end_with?(preferred_format)\n      }\n\n      if preferred.empty?\n        Puppet.debug { \"Value of 'preferred_serialization_format' (#{preferred_format}) is invalid for #{friendly_name}, using default (#{list.first.name})\" }\n      else\n        list = preferred + list.reject { |format|\n          format.mime.end_with?(preferred_format)\n        }\n      end\n\n      list\n    end\n  end\n\n  def to_msgpack(*args)\n    to_data_hash.to_msgpack(*args)\n  end\n\n  # @deprecated, use to_json\n  def to_pson(*args)\n    to_data_hash.to_pson(*args)\n  end\n\n  def to_json(*args)\n    Puppet::Util::Json.dump(to_data_hash, *args)\n  end\n\n  def render(format = nil)\n    format ||= self.class.default_format\n\n    self.class.get_format(format).render(self)\n  rescue => err\n    # TRANSLATORS \"render\" is a function name and should not be translated\n    raise Puppet::Network::FormatHandler::FormatError, _(\"Could not render to %{format}: %{err}\") % { format: format, err: err }, err.backtrace\n  end\n\n  def mime(format = nil)\n    format ||= self.class.default_format\n\n    self.class.get_format(format).mime\n  rescue => err\n    # TRANSLATORS \"mime\" is a function name and should not be translated\n    raise Puppet::Network::FormatHandler::FormatError, _(\"Could not mime to %{format}: %{err}\") % { format: format, err: err }, err.backtrace\n  end\n\n  def support_format?(name)\n    self.class.support_format?(name)\n  end\n\n  # @comment Document to_data_hash here as it is called as a hook from to_msgpack if it exists\n  # @!method to_data_hash(*args)\n  # @api public\n  # @abstract\n  # This method may be implemented to return a hash object that is used for serializing.\n  # The object returned by this method should contain all the info needed to instantiate it again.\n  # If the method exists it will be called from to_msgpack and other serialization methods.\n  # @return [Hash]\nend\n"
  },
  {
    "path": "lib/puppet/network/formats.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/network/format_handler'\nrequire_relative '../../puppet/util/json'\n\nPuppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20, :mime => \"application/x-msgpack\", :required_methods => [:render_method, :intern_method], :intern_method => :from_data_hash) do\n  confine :feature => :msgpack\n\n  def intern(klass, text)\n    data = MessagePack.unpack(text)\n    return data if data.is_a?(klass)\n\n    klass.from_data_hash(data)\n  end\n\n  def intern_multiple(klass, text)\n    MessagePack.unpack(text).collect do |data|\n      klass.from_data_hash(data)\n    end\n  end\n\n  def render_multiple(instances)\n    instances.to_msgpack\n  end\nend\n\nPuppet::Network::FormatHandler.create_serialized_formats(:yaml) do\n  def allowed_yaml_classes\n    @allowed_yaml_classes ||= [\n      Puppet::Node::Facts,\n      Puppet::Node,\n      Puppet::Transaction::Report,\n      Puppet::Resource,\n      Puppet::Resource::Catalog\n    ]\n  end\n\n  def intern(klass, text)\n    data = Puppet::Util::Yaml.safe_load(text, allowed_yaml_classes)\n    data_to_instance(klass, data)\n  rescue Puppet::Util::Yaml::YamlLoadError => e\n    raise Puppet::Network::FormatHandler::FormatError, _(\"Serialized YAML did not contain a valid instance of %{klass}: %{message}\") % { klass: klass, message: e.message }\n  end\n\n  def intern_multiple(klass, text)\n    data = Puppet::Util::Yaml.safe_load(text, allowed_yaml_classes)\n    unless data.respond_to?(:collect)\n      raise Puppet::Network::FormatHandler::FormatError, _(\"Serialized YAML did not contain a collection of instances when calling intern_multiple\")\n    end\n\n    data.collect do |datum|\n      data_to_instance(klass, datum)\n    end\n  rescue Puppet::Util::Yaml::YamlLoadError => e\n    raise Puppet::Network::FormatHandler::FormatError, _(\"Serialized YAML did not contain a valid instance of %{klass}: %{message}\") % { klass: klass, message: e.message }\n  end\n\n  def data_to_instance(klass, data)\n    return data if data.is_a?(klass)\n\n    unless data.is_a? Hash\n      raise Puppet::Network::FormatHandler::FormatError, _(\"Serialized YAML did not contain a valid instance of %{klass}\") % { klass: klass }\n    end\n\n    klass.from_data_hash(data)\n  end\n\n  def render(instance)\n    instance.to_yaml\n  end\n\n  # Yaml monkey-patches Array, so this works.\n  def render_multiple(instances)\n    instances.to_yaml\n  end\n\n  def supported?(klass)\n    true\n  end\nend\n\nPuppet::Network::FormatHandler.create(:s, :mime => \"text/plain\", :charset => Encoding::UTF_8, :extension => \"txt\")\n\n# By default, to_binary is called to render and from_binary called to intern. Note unlike\n# text-based formats (json, yaml, etc), we don't use to_data_hash for binary.\nPuppet::Network::FormatHandler.create(:binary, :mime => \"application/octet-stream\", :weight => 1,\n                                               :required_methods => [:render_method, :intern_method]) do\nend\n\n# PSON is deprecated\nPuppet::Network::FormatHandler.create_serialized_formats(:pson, :weight => 10, :required_methods => [:render_method, :intern_method], :intern_method => :from_data_hash) do\n  confine :feature => :pson\n\n  def intern(klass, text)\n    data_to_instance(klass, PSON.parse(text))\n  end\n\n  def intern_multiple(klass, text)\n    PSON.parse(text).collect do |data|\n      data_to_instance(klass, data)\n    end\n  end\n\n  # PSON monkey-patches Array, so this works.\n  def render_multiple(instances)\n    instances.to_pson\n  end\n\n  # If they pass class information, we want to ignore it.\n  # This is required for compatibility with Puppet 3.x\n  def data_to_instance(klass, data)\n    d = data['data'] if data.is_a?(Hash)\n    if d\n      data = d\n    end\n    return data if data.is_a?(klass)\n\n    klass.from_data_hash(data)\n  end\nend\n\nPuppet::Network::FormatHandler.create_serialized_formats(:json, :mime => 'application/json', :charset => Encoding::UTF_8, :weight => 15, :required_methods => [:render_method, :intern_method], :intern_method => :from_data_hash) do\n  def intern(klass, text)\n    data_to_instance(klass, Puppet::Util::Json.load(text))\n  end\n\n  def intern_multiple(klass, text)\n    Puppet::Util::Json.load(text).collect do |data|\n      data_to_instance(klass, data)\n    end\n  end\n\n  def render_multiple(instances)\n    Puppet::Util::Json.dump(instances)\n  end\n\n  # Unlike PSON, we do not need to unwrap the data envelope, because legacy 3.x agents\n  # have never supported JSON\n  def data_to_instance(klass, data)\n    return data if data.is_a?(klass)\n\n    klass.from_data_hash(data)\n  end\nend\n\n# This is really only ever going to be used for Catalogs.\nPuppet::Network::FormatHandler.create_serialized_formats(:dot, :required_methods => [:render_method])\n\nPuppet::Network::FormatHandler.create(:console,\n                                      :mime => 'text/x-console-text',\n                                      :weight => 0) do\n  def json\n    @json ||= Puppet::Network::FormatHandler.format(:json)\n  end\n\n  def render(datum)\n    return datum if datum.is_a?(String) || datum.is_a?(Numeric)\n\n    # Simple hash to table\n    if datum.is_a?(Hash) && datum.keys.all? { |x| x.is_a?(String) || x.is_a?(Numeric) }\n      output = ''.dup\n      column_a = datum.empty? ? 2 : datum.map { |k, _v| k.to_s.length }.max + 2\n      datum.sort_by { |k, _v| k.to_s }.each do |key, value|\n        output << key.to_s.ljust(column_a)\n        output << json.render(value)\n                      .chomp.gsub(/\\n */) { |x| x + (' ' * column_a) }\n        output << \"\\n\"\n      end\n      return output\n    end\n\n    # Print one item per line for arrays\n    if datum.is_a? Array\n      output = ''.dup\n      datum.each do |item|\n        output << item.to_s\n        output << \"\\n\"\n      end\n      return output\n    end\n\n    # ...or pretty-print the inspect outcome.\n    Puppet::Util::Json.dump(datum, :pretty => true, :quirks_mode => true)\n  end\n\n  def render_multiple(data)\n    data.collect(&:render).join(\"\\n\")\n  end\nend\n\nPuppet::Network::FormatHandler.create(:flat,\n                                      :mime => 'text/x-flat-text',\n                                      :weight => 0) do\n  def flatten_hash(hash)\n    hash.each_with_object({}) do |(k, v), h|\n      case v\n      when Hash\n        flatten_hash(v).map do |h_k, h_v|\n          h[\"#{k}.#{h_k}\"] = h_v\n        end\n      when Array\n        v.each_with_index do |el, i|\n          if el.is_a? Hash\n            flatten_hash(el).map do |el_k, el_v|\n              h[\"#{k}.#{i}.#{el_k}\"] = el_v\n            end\n          else\n            h[\"#{k}.#{i}\"] = el\n          end\n        end\n      else\n        h[k] = v\n      end\n    end\n  end\n\n  def flatten_array(array)\n    a = {}\n    array.each_with_index do |el, i|\n      if el.is_a? Hash\n        flatten_hash(el).map do |el_k, el_v|\n          a[\"#{i}.#{el_k}\"] = el_v\n        end\n      else\n        a[i.to_s] = el\n      end\n    end\n    a\n  end\n\n  def construct_output(data)\n    output = ''.dup\n    data.each do |key, value|\n      output << \"#{key}=#{value}\"\n      output << \"\\n\"\n    end\n    output\n  end\n\n  def render(datum)\n    return datum if datum.is_a?(String) || datum.is_a?(Numeric)\n\n    # Simple hash\n    case datum\n    when Hash\n      data = flatten_hash(datum)\n      return construct_output(data)\n    when Array\n      data = flatten_array(datum)\n      return construct_output(data)\n    end\n    Puppet::Util::Json.dump(datum, :pretty => true, :quirks_mode => true)\n  end\n\n  def render_multiple(data)\n    data.collect(&:render).join(\"\\n\")\n  end\nend\n\nPuppet::Network::FormatHandler.create(:rich_data_json, mime: 'application/vnd.puppet.rich+json', charset: Encoding::UTF_8, weight: 30) do\n  def intern(klass, text)\n    Puppet.override({ :rich_data => true }) do\n      data_to_instance(klass, Puppet::Util::Json.load(text))\n    end\n  end\n\n  def intern_multiple(klass, text)\n    Puppet.override({ :rich_data => true }) do\n      Puppet::Util::Json.load(text).collect do |data|\n        data_to_instance(klass, data)\n      end\n    end\n  end\n\n  def render(instance)\n    Puppet.override({ :rich_data => true }) do\n      instance.to_json\n    end\n  end\n\n  def render_multiple(instances)\n    Puppet.override({ :rich_data => true }) do\n      Puppet::Util::Json.dump(instances)\n    end\n  end\n\n  def data_to_instance(klass, data)\n    Puppet.override({ :rich_data => true }) do\n      return data if data.is_a?(klass)\n\n      klass.from_data_hash(data)\n    end\n  end\n\n  def supported?(klass)\n    klass == Puppet::Resource::Catalog &&\n      Puppet.lookup(:current_environment).rich_data?\n  end\nend\n\nPuppet::Network::FormatHandler.create_serialized_formats(:rich_data_msgpack, mime: \"application/vnd.puppet.rich+msgpack\", weight: 35) do\n  confine :feature => :msgpack\n\n  def intern(klass, text)\n    Puppet.override(rich_data: true) do\n      data = MessagePack.unpack(text)\n      return data if data.is_a?(klass)\n\n      klass.from_data_hash(data)\n    end\n  end\n\n  def intern_multiple(klass, text)\n    Puppet.override(rich_data: true) do\n      MessagePack.unpack(text).collect do |data|\n        klass.from_data_hash(data)\n      end\n    end\n  end\n\n  def render_multiple(instances)\n    Puppet.override(rich_data: true) do\n      instances.to_msgpack\n    end\n  end\n\n  def render(instance)\n    Puppet.override(rich_data: true) do\n      instance.to_msgpack\n    end\n  end\n\n  def supported?(klass)\n    suitable? &&\n      klass == Puppet::Resource::Catalog &&\n      Puppet.lookup(:current_environment).rich_data?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/api/indirected_routes.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/network/http/api/indirection_type'\n\nclass Puppet::Network::HTTP::API::IndirectedRoutes\n  # How we map http methods and the indirection name in the URI\n  # to an indirection method.\n  METHOD_MAP = {\n    \"GET\" => {\n      :plural => :search,\n      :singular => :find\n    },\n    \"POST\" => {\n      :singular => :find,\n    },\n    \"PUT\" => {\n      :singular => :save\n    },\n    \"DELETE\" => {\n      :singular => :destroy\n    },\n    \"HEAD\" => {\n      :singular => :head\n    }\n  }\n\n  IndirectionType = Puppet::Network::HTTP::API::IndirectionType\n\n  def self.routes\n    Puppet::Network::HTTP::Route.path(/.*/).any(new)\n  end\n\n  # Handle an HTTP request. The request has already been authenticated prior\n  # to calling this method.\n  def call(request, response)\n    indirection, method, key, params = uri2indirection(request.method, request.path, request.params)\n    certificate = request.client_cert\n\n    unless indirection.allow_remote_requests?\n      # TODO: should we tell the user we found an indirection but it doesn't\n      # allow remote requests, or just pretend there's no handler at all? what\n      # are the security implications for the former?\n      raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_(\"No handler for %{indirection}\") % { indirection: indirection.name }, :NO_INDIRECTION_REMOTE_REQUESTS)\n    end\n\n    overrides = {\n      trusted_information: Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate),\n    }\n    if params[:environment]\n      overrides[:current_environment] = params[:environment]\n    end\n\n    Puppet.override(overrides) do\n      send(\"do_#{method}\", indirection, key, params, request, response)\n    end\n  end\n\n  def uri2indirection(http_method, uri, params)\n    # the first field is always nil because of the leading slash,\n    indirection_type, version, indirection_name, key = uri.split(\"/\", 5)[1..]\n    url_prefix = \"/#{indirection_type}/#{version}\"\n    environment = params.delete(:environment)\n\n    if indirection_name !~ /^\\w+$/\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"The indirection name must be purely alphanumeric, not '%{indirection_name}'\") % { indirection_name: indirection_name }\n    end\n\n    # this also depluralizes the indirection_name if it is a search\n    method = indirection_method(http_method, indirection_name)\n\n    # check whether this indirection matches the prefix and version in the\n    # request\n    if url_prefix != IndirectionType.url_prefix_for(indirection_name)\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"Indirection '%{indirection_name}' does not match url prefix '%{url_prefix}'\") % { indirection_name: indirection_name, url_prefix: url_prefix }\n    end\n\n    indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym)\n    unless indirection\n      raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(\n        _(\"Could not find indirection '%{indirection_name}'\") % { indirection_name: indirection_name },\n        Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND\n      )\n    end\n\n    unless environment\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"An environment parameter must be specified\")\n    end\n\n    unless Puppet::Node::Environment.valid_name?(environment)\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"The environment must be purely alphanumeric, not '%{environment}'\") % { environment: environment }\n    end\n\n    configured_environment = Puppet.lookup(:environments).get(environment)\n    unless configured_environment.nil?\n      configured_environment = configured_environment.override_from_commandline(Puppet.settings)\n      params[:environment] = configured_environment\n    end\n\n    if configured_environment.nil? && indirection.terminus.require_environment?\n      raise Puppet::Network::HTTP::Error::HTTPNotFoundError, _(\"Could not find environment '%{environment}'\") % { environment: environment }\n    end\n\n    params.delete(:bucket_path)\n\n    if key == \"\" or key.nil?\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"No request key specified in %{uri}\") % { uri: uri }\n    end\n\n    [indirection, method, key, params]\n  end\n\n  private\n\n  # Execute our find.\n  def do_find(indirection, key, params, request, response)\n    result = indirection.find(key, params)\n    unless result\n      raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_(\"Could not find %{value0} %{key}\") % { value0: indirection.name, key: key }, Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND)\n    end\n\n    rendered_result = result\n\n    rendered_format = first_response_formatter_for(indirection.model, request, key) do |format|\n      if result.respond_to?(:render)\n        Puppet::Util::Profiler.profile(_(\"Rendered result in %{format}\") % { format: format }, [:http, :v3_render, format]) do\n          rendered_result = result.render(format)\n        end\n      end\n    end\n\n    Puppet::Util::Profiler.profile(_(\"Sent response\"), [:http, :v3_response]) do\n      response.respond_with(200, rendered_format, rendered_result)\n    end\n  end\n\n  # Execute our head.\n  def do_head(indirection, key, params, request, response)\n    unless indirection.head(key, params)\n      raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_(\"Could not find %{indirection} %{key}\") % { indirection: indirection.name, key: key }, Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND)\n    end\n\n    # No need to set a response because no response is expected from a\n    # HEAD request.  All we need to do is not die.\n  end\n\n  # Execute our search.\n  def do_search(indirection, key, params, request, response)\n    result = indirection.search(key, params)\n\n    if result.nil?\n      raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_(\"Could not find instances in %{indirection} with '%{key}'\") % { indirection: indirection.name, key: key }, Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND)\n    end\n\n    rendered_result = nil\n\n    rendered_format = first_response_formatter_for(indirection.model, request, key) do |format|\n      rendered_result = indirection.model.render_multiple(format, result)\n    end\n\n    response.respond_with(200, rendered_format, rendered_result)\n  end\n\n  # Execute our destroy.\n  def do_destroy(indirection, key, params, request, response)\n    formatter = accepted_response_formatter_or_json_for(indirection.model, request)\n\n    result = indirection.destroy(key, params)\n\n    response.respond_with(200, formatter, formatter.render(result))\n  end\n\n  # Execute our save.\n  def do_save(indirection, key, params, request, response)\n    formatter = accepted_response_formatter_or_json_for(indirection.model, request)\n    sent_object = read_body_into_model(indirection.model, request)\n\n    result = indirection.save(sent_object, key)\n\n    response.respond_with(200, formatter, formatter.render(result))\n  end\n\n  # Return the first response formatter that didn't cause the yielded\n  # block to raise a FormatError.\n  def first_response_formatter_for(model, request, key, &block)\n    formats = accepted_response_formatters_for(model, request)\n    formatter = formats.find do |format|\n      yield format\n      true\n    rescue Puppet::Network::FormatHandler::FormatError => err\n      msg = _(\"Failed to serialize %{model} for '%{key}': %{detail}\") %\n            { model: model, key: key, detail: err }\n      if Puppet[:allow_pson_serialization]\n        Puppet.warning(msg)\n      else\n        raise Puppet::Network::FormatHandler::FormatError, msg\n      end\n      false\n    end\n\n    return formatter if formatter\n\n    raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError.new(\n      _(\"No supported formats are acceptable (Accept: %{accepted_formats})\") % { accepted_formats: formats.map(&:mime).join(', ') },\n      Puppet::Network::HTTP::Issues::UNSUPPORTED_FORMAT\n    )\n  end\n\n  # Return an array of response formatters that the client accepts and\n  # the server supports.\n  def accepted_response_formatters_for(model_class, request)\n    request.response_formatters_for(model_class.supported_formats)\n  end\n\n  # Return the first response formatter that the client accepts and\n  # the server supports, or default to 'application/json'.\n  def accepted_response_formatter_or_json_for(model_class, request)\n    request.response_formatters_for(model_class.supported_formats, \"application/json\").first\n  end\n\n  def read_body_into_model(model_class, request)\n    data = request.body.to_s\n    formatter = request.formatter\n\n    if formatter.supported?(model_class)\n      begin\n        return model_class.convert_from(formatter.name.to_s, data)\n      rescue => e\n        raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"The request body is invalid: %{message}\") % { message: e.message }\n      end\n    end\n\n    # TRANSLATORS \"mime-type\" is a keyword and should not be translated\n    raise Puppet::Network::HTTP::Error::HTTPUnsupportedMediaTypeError.new(\n      _(\"Client sent a mime-type (%{header}) that doesn't correspond to a format we support\") % { header: request.headers['content-type'] },\n      Puppet::Network::HTTP::Issues::UNSUPPORTED_MEDIA_TYPE\n    )\n  end\n\n  def indirection_method(http_method, indirection)\n    raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError, _(\"No support for http method %{http_method}\") % { http_method: http_method } unless METHOD_MAP[http_method]\n\n    method = METHOD_MAP[http_method][plurality(indirection)]\n    unless method\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _(\"No support for plurality %{indirection} for %{http_method} operations\") % { indirection: plurality(indirection), http_method: http_method }\n    end\n\n    method\n  end\n\n  def self.pluralize(indirection)\n    (indirection == \"status\" ? \"statuses\" : indirection + \"s\")\n  end\n  private_class_method :pluralize\n\n  def plurality(indirection)\n    # NOTE These specific hooks for paths are ridiculous, but it's a *many*-line\n    # fix to not need this, and our goal is to move away from the complication\n    # that leads to the fix being too long.\n    return :singular if indirection == \"facts\"\n    return :singular if indirection == \"status\"\n    return :singular if indirection == \"certificate_status\"\n\n    result = (indirection =~ /s$|_search$/) ? :plural : :singular\n\n    indirection.sub!(/s$|_search$/, '')\n    indirection.sub!(/statuse$/, 'status')\n\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/api/indirection_type.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Network::HTTP::API::IndirectionType\n  INDIRECTION_TYPE_MAP = {\n    \"certificate\" => :ca,\n    \"certificate_request\" => :ca,\n    \"certificate_revocation_list\" => :ca,\n    \"certificate_status\" => :ca\n  }\n\n  def self.master_url_prefix\n    \"#{Puppet::Network::HTTP::MASTER_URL_PREFIX}/v3\"\n  end\n\n  def self.ca_url_prefix\n    \"#{Puppet::Network::HTTP::CA_URL_PREFIX}/v1\"\n  end\n\n  def self.type_for(indirection)\n    INDIRECTION_TYPE_MAP[indirection] || :master\n  end\n\n  def self.url_prefix_for(indirection_name)\n    case type_for(indirection_name)\n    when :ca\n      ca_url_prefix\n    when :master\n      master_url_prefix\n    else\n      raise ArgumentError, _(\"Not a valid indirection type\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/api/master/v3/environments.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../../puppet/network/http/api/master'\nrequire_relative '../../../../../../puppet/network/http/api/server/v3/environments'\n"
  },
  {
    "path": "lib/puppet/network/http/api/master/v3.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/network/http/api/master'\nrequire_relative '../../../../../puppet/network/http/api/server/v3'\n"
  },
  {
    "path": "lib/puppet/network/http/api/master.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/network/http/api/server'\n\nPuppet::Network::HTTP::API::Master = Puppet::Network::HTTP::API::Server\n"
  },
  {
    "path": "lib/puppet/network/http/api/server/v3/environments.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../../puppet/util/json'\n\nmodule Puppet\n  module Network\n    module HTTP\n      class API\n        module Server\n          class V3\n            class Environments\n              def initialize(env_loader)\n                @env_loader = env_loader\n              end\n\n              def call(request, response)\n                response\n                  .respond_with(\n                    200,\n                    \"application/json\",\n                    Puppet::Util::Json\n                      .dump({\n                              \"search_paths\" => @env_loader.search_paths,\n                              \"environments\" => @env_loader.list.to_h do |env|\n                                [env.name, {\n                                  \"settings\" => {\n                                    \"modulepath\" => env.full_modulepath,\n                                    \"manifest\" => env.manifest,\n                                    \"environment_timeout\" => timeout(env),\n                                    \"config_version\" => env.config_version || '',\n                                  }\n                                }]\n                              end\n                            })\n                  )\n              end\n\n              private\n\n              def timeout(env)\n                ttl = @env_loader.get_conf(env.name).environment_timeout\n                if ttl == Float::INFINITY\n                  \"unlimited\"\n                else\n                  ttl\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/api/server/v3.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'v3/environments'\nrequire_relative '../../../../../puppet/network/http/api/indirected_routes'\n\nmodule Puppet\n  module Network\n    module HTTP\n      class API\n        module Server\n          class V3\n            def self.wrap(&block)\n              lambda do |request, response|\n                Puppet::Network::Authorization\n                  .check_external_authorization(request.method,\n                                                request.path)\n\n                block.call.call(request, response)\n              end\n            end\n\n            INDIRECTED = Puppet::Network::HTTP::Route\n                         .path(/.*/)\n                         .any(wrap { Puppet::Network::HTTP::API::IndirectedRoutes.new })\n\n            ENVIRONMENTS = Puppet::Network::HTTP::Route\n                           .path(%r{^/environments$})\n                           .get(wrap { Environments.new(Puppet.lookup(:environments)) })\n\n            def self.routes\n              Puppet::Network::HTTP::Route.path(/v3/)\n                                          .any\n                                          .chain(ENVIRONMENTS, INDIRECTED)\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/api/server.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  module Network\n    module HTTP\n      class API\n        module Server\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/api.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Network::HTTP::API\n  require_relative '../../../puppet/version'\n\n  def self.not_found\n    Puppet::Network::HTTP::Route\n      .path(/.*/)\n      .any(lambda do |req, _res|\n        raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(\"No route for #{req.method} #{req.path}\", Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND)\n      end)\n  end\n\n  def self.not_found_upgrade\n    Puppet::Network::HTTP::Route\n      .path(/.*/)\n      .any(lambda do |_req, _res|\n        raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(\"Error: Invalid URL - Puppet expects requests that conform to the \" \\\n                                                                  \"/puppet and /puppet-ca APIs.\\n\\n\" \\\n                                                                  \"Note that Puppet 3 agents aren't compatible with this version; if you're \" \\\n                                                                  \"running Puppet 3, you must either upgrade your agents to match the server \" \\\n                                                                  \"or point them to a server running Puppet 3.\\n\\n\" \\\n                                                                  \"Server Info:\\n\" \\\n                                                                  \"  Puppet version: #{Puppet.version}\\n\" \\\n                                                                  \"  Supported /puppet API versions: #{Puppet::Network::HTTP::SERVER_URL_VERSIONS}\\n\",\n                                                                  Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND)\n      end)\n  end\n\n  def self.server_routes\n    server_prefix = Regexp.new(\"^#{Puppet::Network::HTTP::SERVER_URL_PREFIX}/\")\n    Puppet::Network::HTTP::Route.path(server_prefix)\n                                .any\n                                .chain(Puppet::Network::HTTP::API::Server::V3.routes,\n                                       Puppet::Network::HTTP::API.not_found)\n  end\n\n  def self.master_routes\n    server_routes\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/connection.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/http'\n\n# This will be raised if too many redirects happen for a given HTTP request\nclass Puppet::Network::HTTP::RedirectionLimitExceededException < Puppet::Error; end\n\n# This class provides simple methods for issuing various types of HTTP\n# requests.  It's interface is intended to mirror Ruby's Net::HTTP\n# object, but it provides a few important bits of additional\n# functionality.  Notably:\n#\n# * Any HTTPS requests made using this class will use Puppet's SSL\n#   certificate configuration for their authentication, and\n# * Provides some useful error handling for any SSL errors that occur\n#   during a request.\n#\n# @deprecated Use {Puppet.runtime[:http]}\n# @api public\nclass Puppet::Network::HTTP::Connection\n  include Puppet::HTTP::ResponseConverter\n\n  OPTION_DEFAULTS = {\n    :use_ssl => true,\n    :verifier => nil,\n    :redirect_limit => 10,\n  }\n\n  # Creates a new HTTP client connection to `host`:`port`.\n  # @param host [String] the host to which this client will connect to\n  # @param port [Integer] the port to which this client will connect to\n  # @param options [Hash] options influencing the properties of the created\n  #   connection,\n  # @option options [Boolean] :use_ssl true to connect with SSL, false\n  #   otherwise, defaults to true\n  # @option options [Puppet::SSL::Verifier] :verifier An object that will configure\n  #   any verification to do on the connection\n  # @option options [Integer] :redirect_limit the number of allowed\n  #   redirections, defaults to 10 passing any other option in the options\n  #   hash results in a Puppet::Error exception\n  #\n  # @note the HTTP connection itself happens lazily only when {#request}, or\n  #   one of the {#get}, {#post}, {#delete}, {#head} or {#put} is called\n  # @note The correct way to obtain a connection is to use one of the factory\n  #   methods on {Puppet::Network::HttpPool}\n  # @api private\n  def initialize(host, port, options = {})\n    unknown_options = options.keys - OPTION_DEFAULTS.keys\n    raise Puppet::Error, _(\"Unrecognized option(s): %{opts}\") % { opts: unknown_options.map(&:inspect).sort.join(', ') } unless unknown_options.empty?\n\n    options = OPTION_DEFAULTS.merge(options)\n    @use_ssl = options[:use_ssl]\n    if @use_ssl\n      unless options[:verifier].is_a?(Puppet::SSL::Verifier)\n        raise ArgumentError, _(\"Expected an instance of Puppet::SSL::Verifier but was passed a %{klass}\") % { klass: options[:verifier].class }\n      end\n\n      @verifier = options[:verifier]\n    end\n    @redirect_limit = options[:redirect_limit]\n    @site = Puppet::HTTP::Site.new(@use_ssl ? 'https' : 'http', host, port)\n    @client = Puppet.runtime[:http]\n  end\n\n  # The address to connect to.\n  def address\n    @site.host\n  end\n\n  # The port to connect to.\n  def port\n    @site.port\n  end\n\n  # Whether to use ssl\n  def use_ssl?\n    @site.use_ssl?\n  end\n\n  # @api private\n  def verifier\n    @verifier\n  end\n\n  # @!macro [new] common_options\n  #   @param options [Hash] options influencing the request made. Any\n  #   options not recognized by this class will be ignored - no error will\n  #   be thrown.\n  #   @option options [Hash{Symbol => String}] :basic_auth The basic auth\n  #     :username and :password to use for the request, :metric_id Ignored\n  #     by this class - used by Puppet Server only. The metric id by which\n  #     to track metrics on requests.\n\n  # @param path [String]\n  # @param headers [Hash{String => String}]\n  # @!macro common_options\n  # @api public\n  def get(path, headers = {}, options = {})\n    headers ||= {}\n    options[:ssl_context] ||= resolve_ssl_context\n    options[:redirect_limit] ||= @redirect_limit\n\n    with_error_handling do\n      to_ruby_response(@client.get(to_url(path), headers: headers, options: options))\n    end\n  end\n\n  # @param path [String]\n  # @param data [String]\n  # @param headers [Hash{String => String}]\n  # @!macro common_options\n  # @api public\n  def post(path, data, headers = nil, options = {})\n    headers ||= {}\n    headers['Content-Type'] ||= \"application/x-www-form-urlencoded\"\n    data ||= ''\n    options[:ssl_context] ||= resolve_ssl_context\n    options[:redirect_limit] ||= @redirect_limit\n\n    with_error_handling do\n      to_ruby_response(@client.post(to_url(path), data, headers: headers, options: options))\n    end\n  end\n\n  # @param path [String]\n  # @param headers [Hash{String => String}]\n  # @!macro common_options\n  # @api public\n  def head(path, headers = {}, options = {})\n    headers ||= {}\n    options[:ssl_context] ||= resolve_ssl_context\n    options[:redirect_limit] ||= @redirect_limit\n\n    with_error_handling do\n      to_ruby_response(@client.head(to_url(path), headers: headers, options: options))\n    end\n  end\n\n  # @param path [String]\n  # @param headers [Hash{String => String}]\n  # @!macro common_options\n  # @api public\n  def delete(path, headers = { 'Depth' => 'Infinity' }, options = {})\n    headers ||= {}\n    options[:ssl_context] ||= resolve_ssl_context\n    options[:redirect_limit] ||= @redirect_limit\n\n    with_error_handling do\n      to_ruby_response(@client.delete(to_url(path), headers: headers, options: options))\n    end\n  end\n\n  # @param path [String]\n  # @param data [String]\n  # @param headers [Hash{String => String}]\n  # @!macro common_options\n  # @api public\n  def put(path, data, headers = nil, options = {})\n    headers ||= {}\n    headers['Content-Type'] ||= \"application/x-www-form-urlencoded\"\n    data ||= ''\n    options[:ssl_context] ||= resolve_ssl_context\n    options[:redirect_limit] ||= @redirect_limit\n\n    with_error_handling do\n      to_ruby_response(@client.put(to_url(path), data, headers: headers, options: options))\n    end\n  end\n\n  def request_get(*args, &block)\n    path, headers = *args\n    headers ||= {}\n    options = {\n      ssl_context: resolve_ssl_context,\n      redirect_limit: @redirect_limit\n    }\n\n    ruby_response = nil\n    @client.get(to_url(path), headers: headers, options: options) do |response|\n      ruby_response = to_ruby_response(response)\n      yield ruby_response if block_given?\n    end\n    ruby_response\n  end\n\n  def request_head(*args, &block)\n    path, headers = *args\n    headers ||= {}\n    options = {\n      ssl_context: resolve_ssl_context,\n      redirect_limit: @redirect_limit\n    }\n\n    response = @client.head(to_url(path), headers: headers, options: options)\n    ruby_response = to_ruby_response(response)\n    yield ruby_response if block_given?\n    ruby_response\n  end\n\n  def request_post(*args, &block)\n    path, data, headers = *args\n    headers ||= {}\n    headers['Content-Type'] ||= \"application/x-www-form-urlencoded\"\n    options = {\n      ssl_context: resolve_ssl_context,\n      redirect_limit: @redirect_limit\n    }\n\n    ruby_response = nil\n    @client.post(to_url(path), data, headers: headers, options: options) do |response|\n      ruby_response = to_ruby_response(response)\n      yield ruby_response if block_given?\n    end\n    ruby_response\n  end\n\n  private\n\n  # Resolve the ssl_context based on the verifier associated with this\n  # connection or load the available set of certs and key on disk.\n  # Don't try to bootstrap the agent, as we only want that to be triggered\n  # when running `puppet ssl` or `puppet agent`.\n  def resolve_ssl_context\n    # don't need an ssl context for http connections\n    return nil unless @site.use_ssl?\n\n    # if our verifier has an ssl_context, use that\n    ctx = @verifier.ssl_context\n    return ctx if ctx\n\n    # load available certs\n    cert = Puppet::X509::CertProvider.new\n    ssl = Puppet::SSL::SSLProvider.new\n    begin\n      password = cert.load_private_key_password\n      ssl.load_context(certname: Puppet[:certname], password: password)\n    rescue Puppet::SSL::SSLError => e\n      Puppet.log_exception(e)\n\n      # if we don't have cacerts, then create a root context that doesn't\n      # trust anything. The old code used to fallback to VERIFY_NONE,\n      # which we don't want to emulate.\n      ssl.create_root_context(cacerts: [])\n    end\n  end\n\n  def to_url(path)\n    if path =~ %r{^https?://}\n      # The old Connection class accepts a URL as the request path, and sends\n      # it in \"absolute-form\" in the request line, e.g. GET https://puppet:8140/.\n      # See https://httpwg.org/specs/rfc7230.html#absolute-form. It just so happens\n      # to work because HTTP 1.1 servers are required to accept absolute-form even\n      # though clients are only supposed to send them to proxies, so the proxy knows\n      # what upstream server to CONNECT to. This method creates a URL using the\n      # scheme/host/port that the connection was created with, and appends the path\n      # and query portions of the absolute-form. The resulting request will use \"origin-form\"\n      # as it should have done all along.\n      abs_form = URI(path)\n      url = URI(\"#{@site.addr}/#{normalize_path(abs_form.path)}\")\n      url.query = abs_form.query if abs_form.query\n      url\n    else\n      URI(\"#{@site.addr}/#{normalize_path(path)}\")\n    end\n  end\n\n  def normalize_path(path)\n    if path[0] == '/'\n      path[1..]\n    else\n      path\n    end\n  end\n\n  def with_error_handling(&block)\n    yield\n  rescue Puppet::HTTP::TooManyRedirects => e\n    raise Puppet::Network::HTTP::RedirectionLimitExceededException.new(_(\"Too many HTTP redirections for %{host}:%{port}\") % { host: @host, port: @port }, e)\n  rescue Puppet::HTTP::HTTPError => e\n    Puppet.log_exception(e, e.message)\n    case e.cause\n    when Net::OpenTimeout, Net::ReadTimeout, Net::HTTPError, EOFError\n      raise e.cause\n    else\n      raise e\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/error.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/json'\n\nmodule Puppet::Network::HTTP::Error\n  Issues = Puppet::Network::HTTP::Issues\n\n  class HTTPError < Exception # rubocop:disable Lint/InheritException\n    attr_reader :status, :issue_kind\n\n    def initialize(message, status, issue_kind)\n      super(message)\n      @status = status\n      @issue_kind = issue_kind\n    end\n\n    def to_json\n      Puppet::Util::Json.dump({ :message => message, :issue_kind => @issue_kind })\n    end\n  end\n\n  class HTTPNotAcceptableError < HTTPError\n    CODE = 406\n    def initialize(message, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Not Acceptable: %{message}\") % { message: message }, CODE, issue_kind)\n    end\n  end\n\n  class HTTPNotFoundError < HTTPError\n    CODE = 404\n    def initialize(message, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Not Found: %{message}\") % { message: message }, CODE, issue_kind)\n    end\n  end\n\n  class HTTPNotAuthorizedError < HTTPError\n    CODE = 403\n    def initialize(message, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Not Authorized: %{message}\") % { message: message }, CODE, issue_kind)\n    end\n  end\n\n  class HTTPBadRequestError < HTTPError\n    CODE = 400\n    def initialize(message, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Bad Request: %{message}\") % { message: message }, CODE, issue_kind)\n    end\n  end\n\n  class HTTPMethodNotAllowedError < HTTPError\n    CODE = 405\n    def initialize(message, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Method Not Allowed: %{message}\") % { message: message }, CODE, issue_kind)\n    end\n  end\n\n  class HTTPUnsupportedMediaTypeError < HTTPError\n    CODE = 415\n    def initialize(message, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Unsupported Media Type: %{message}\") % { message: message }, CODE, issue_kind)\n    end\n  end\n\n  class HTTPServerError < HTTPError\n    CODE = 500\n\n    def initialize(original_error, issue_kind = Issues::RUNTIME_ERROR)\n      super(_(\"Server Error: %{message}\") % { message: original_error.message }, CODE, issue_kind)\n    end\n\n    def to_json\n      Puppet::Util::Json.dump({ :message => message, :issue_kind => @issue_kind })\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/handler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Network::HTTP\nend\n\nrequire_relative '../../../puppet/network/http'\nrequire_relative '../../../puppet/util/profiler'\nrequire_relative '../../../puppet/util/profiler/aggregate'\nrequire 'resolv'\n\nmodule Puppet::Network::HTTP::Handler\n  include Puppet::Network::HTTP::Issues\n\n  # These shouldn't be allowed to be set by clients\n  # in the query string, for security reasons.\n  DISALLOWED_KEYS = %w[node ip]\n\n  def register(routes)\n    # There's got to be a simpler way to do this, right?\n    dupes = {}\n    routes.each { |r| dupes[r.path_matcher] = (dupes[r.path_matcher] || 0) + 1 }\n    dupes = dupes.filter_map { |pm, count| pm if count > 1 }\n    if dupes.count > 0\n      raise ArgumentError, _(\"Given multiple routes with identical path regexes: %{regexes}\") % { regexes: dupes.map(&:inspect).join(', ') }\n    end\n\n    @routes = routes\n    Puppet.debug(\"Routes Registered:\")\n    @routes.each do |route|\n      Puppet.debug(route.inspect)\n    end\n  end\n\n  # Retrieve all headers from the http request, as a hash with the header names\n  # (lower-cased) as the keys\n  def headers(request)\n    raise NotImplementedError\n  end\n\n  # The mime type is always passed to the `set_content_type` method, so\n  # it is no longer necessary to retrieve the Format's mime type.\n  #\n  # @deprecated\n  def format_to_mime(format)\n    format.is_a?(Puppet::Network::Format) ? format.mime : format\n  end\n\n  # Create a generic puppet request from the implementation-specific request\n  # created by the web server\n  def make_generic_request(request)\n    request_path = path(request)\n\n    Puppet::Network::HTTP::Request.new(\n      headers(request),\n      params(request),\n      http_method(request),\n      request_path, # path\n      request_path, # routing_path\n      client_cert(request),\n      body(request)\n    )\n  end\n\n  def with_request_profiling(request)\n    profiler = configure_profiler(request.headers, request.params)\n\n    Puppet::Util::Profiler.profile(\n      _(\"Processed request %{request_method} %{request_path}\") % { request_method: request.method, request_path: request.path },\n      [:http, request.method, request.path]\n    ) do\n      yield\n    end\n  ensure\n    remove_profiler(profiler) if profiler\n  end\n\n  # handle an HTTP request\n  def process(external_request, response)\n    # The response_wrapper stores the response and modifies it as a side effect.\n    # The caller will use the original response\n    response_wrapper = Puppet::Network::HTTP::Response.new(self, response)\n    request = make_generic_request(external_request)\n\n    set_puppet_version_header(response)\n\n    respond_to_errors(response_wrapper) do\n      with_request_profiling(request) do\n        find_route_or_raise(request).process(request, response_wrapper)\n      end\n    end\n  end\n\n  def respond_to_errors(response)\n    yield\n  rescue Puppet::Network::HTTP::Error::HTTPError => e\n    Puppet.info(e.message)\n    respond_with_http_error(response, e)\n  rescue StandardError => e\n    http_e = Puppet::Network::HTTP::Error::HTTPServerError.new(e)\n    Puppet.err([http_e.message, *e.backtrace].join(\"\\n\"))\n    respond_with_http_error(response, http_e)\n  end\n\n  def respond_with_http_error(response, exception)\n    response.respond_with(exception.status, \"application/json\", exception.to_json)\n  end\n\n  def find_route_or_raise(request)\n    route = @routes.find { |r| r.matches?(request) }\n    route || raise(Puppet::Network::HTTP::Error::HTTPNotFoundError.new(\n                     _(\"No route for %{request} %{path}\") % { request: request.method, path: request.path },\n                     HANDLER_NOT_FOUND\n                   ))\n  end\n\n  def set_puppet_version_header(response)\n    response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version\n  end\n\n  # Set the response up, with the body and status.\n  def set_response(response, body, status = 200)\n    raise NotImplementedError\n  end\n\n  # Set the specified format as the content type of the response.\n  def set_content_type(response, format)\n    raise NotImplementedError\n  end\n\n  # resolve node name from peer's ip address\n  # this is used when the request is unauthenticated\n  def resolve_node(result)\n    begin\n      return Resolv.getname(result[:ip])\n    rescue => detail\n      Puppet.err _(\"Could not resolve %{ip}: %{detail}\") % { ip: result[:ip], detail: detail }\n    end\n    result[:ip]\n  end\n\n  private\n\n  # methods to be overridden by the including web server class\n\n  def http_method(request)\n    raise NotImplementedError\n  end\n\n  def path(request)\n    raise NotImplementedError\n  end\n\n  def request_key(request)\n    raise NotImplementedError\n  end\n\n  def body(request)\n    raise NotImplementedError\n  end\n\n  def params(request)\n    raise NotImplementedError\n  end\n\n  def client_cert(request)\n    raise NotImplementedError\n  end\n\n  def decode_params(params)\n    params.select { |key, _| allowed_parameter?(key) }.each_with_object({}) do |ary, result|\n      param, value = ary\n      result[param.to_sym] = parse_parameter_value(param, value)\n    end\n  end\n\n  def allowed_parameter?(name)\n    !(name.nil? || name.empty? || DISALLOWED_KEYS.include?(name))\n  end\n\n  def parse_parameter_value(param, value)\n    if value.is_a?(Array)\n      value.collect { |v| parse_primitive_parameter_value(v) }\n    else\n      parse_primitive_parameter_value(value)\n    end\n  end\n\n  def parse_primitive_parameter_value(value)\n    case value\n    when \"true\"\n      true\n    when \"false\"\n      false\n    when /^\\d+$/\n      Integer(value)\n    when /^\\d+\\.\\d+$/\n      value.to_f\n    else\n      value\n    end\n  end\n\n  def configure_profiler(request_headers, request_params)\n    if request_headers.has_key?(Puppet::Network::HTTP::HEADER_ENABLE_PROFILING.downcase) or Puppet[:profile]\n      Puppet::Util::Profiler.add_profiler(Puppet::Util::Profiler::Aggregate.new(Puppet.method(:info), request_params.object_id))\n    end\n  end\n\n  def remove_profiler(profiler)\n    profiler.shutdown\n    Puppet::Util::Profiler.remove_profiler(profiler)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/issues.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Network::HTTP::Issues\n  NO_INDIRECTION_REMOTE_REQUESTS = :NO_INDIRECTION_REMOTE_REQUESTS\n  HANDLER_NOT_FOUND = :HANDLER_NOT_FOUND\n  RESOURCE_NOT_FOUND = :RESOURCE_NOT_FOUND\n  ENVIRONMENT_NOT_FOUND = :ENVIRONMENT_NOT_FOUND\n  RUNTIME_ERROR = :RUNTIME_ERROR\n  MISSING_HEADER_FIELD = :MISSING_HEADER_FIELD\n  UNSUPPORTED_FORMAT = :UNSUPPORTED_FORMAT\n  UNSUPPORTED_METHOD = :UNSUPPORTED_METHOD\n  FAILED_AUTHORIZATION = :FAILED_AUTHORIZATION\n  UNSUPPORTED_MEDIA_TYPE = :UNSUPPORTED_MEDIA_TYPE\nend\n"
  },
  {
    "path": "lib/puppet/network/http/memory_response.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Network::HTTP::MemoryResponse\n  attr_reader :code, :type, :body\n\n  def initialize\n    @body = ''.dup\n  end\n\n  def respond_with(code, type, body)\n    @code = code\n    @type = type\n    @body += body\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/request.rb",
    "content": "# frozen_string_literal: true\n\n# This class is effectively public API, because a Request object is passed as a\n# parameter to the current Handler subclass. Puppetserver implements its own\n# Handler\n# https://github.com/puppetlabs/puppetserver/blob/8.3.0/src/ruby/puppetserver-lib/puppet/server/network/http/handler.rb#L9\n# and the Request object is passed to its Handler#body method\n# https://github.com/puppetlabs/puppetserver/blob/8.3.0/src/ruby/puppetserver-lib/puppet/server/network/http/handler.rb#L36\nPuppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, :routing_path, :client_cert, :body) do # rubocop:disable Lint/StructNewOverride\n  def self.from_hash(hash)\n    symbol_members = members.collect(&:intern)\n    unknown = hash.keys - symbol_members\n    if unknown.empty?\n      new(hash[:headers] || {},\n          hash[:params] || {},\n          hash[:method] || \"GET\",\n          hash[:path],\n          hash[:routing_path] || hash[:path],\n          hash[:client_cert],\n          hash[:body])\n    else\n      raise ArgumentError, _(\"Unknown arguments: %{args}\") % { args: unknown.collect(&:inspect).join(', ') }\n    end\n  end\n\n  def route_into(prefix)\n    self.class.new(headers, params, method, path, routing_path.sub(prefix, ''), client_cert, body)\n  end\n\n  def formatter\n    header = headers['content-type']\n    if header\n      header.gsub!(/\\s*;.*$/, '') # strip any charset\n      format = Puppet::Network::FormatHandler.mime(header)\n\n      return format if valid_network_format?(format)\n\n      # TRANSLATORS \"mime-type\" is a keyword and should not be translated\n      raise Puppet::Network::HTTP::Error::HTTPUnsupportedMediaTypeError.new(\n        _(\"Client sent a mime-type (%{header}) that doesn't correspond to a format we support\") % { header: headers['content-type'] },\n        Puppet::Network::HTTP::Issues::UNSUPPORTED_MEDIA_TYPE\n      )\n    end\n\n    raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new(\n      _(\"No Content-Type header was received, it isn't possible to unserialize the request\"),\n      Puppet::Network::HTTP::Issues::MISSING_HEADER_FIELD\n    )\n  end\n\n  def response_formatters_for(supported_formats, default_accepted_formats = nil)\n    accepted_formats = headers['accept'] || default_accepted_formats\n\n    if accepted_formats.nil?\n      raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new(_(\"Missing required Accept header\"), Puppet::Network::HTTP::Issues::MISSING_HEADER_FIELD)\n    end\n\n    formats = Puppet::Network::FormatHandler.most_suitable_formats_for(\n      accepted_formats.split(/\\s*,\\s*/),\n      supported_formats\n    )\n\n    formats.find_all do |format|\n      # we are only passed supported_formats that are suitable\n      # and whose klass implements the required_methods\n      valid_network_format?(format)\n    end\n\n    return formats unless formats.empty?\n\n    raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError.new(\n      _(\"No supported formats are acceptable (Accept: %{accepted_formats})\") % { accepted_formats: accepted_formats },\n      Puppet::Network::HTTP::Issues::UNSUPPORTED_FORMAT\n    )\n  end\n\n  private\n\n  def valid_network_format?(format)\n    # YAML in network requests is not supported. See http://links.puppet.com/deprecate_yaml_on_network\n    !format.nil? && format.name != :yaml && format.name != :b64_zlib_yaml\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/response.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Network::HTTP::Response\n  def initialize(handler, response)\n    @handler = handler\n    @response = response\n  end\n\n  def respond_with(code, type, body)\n    format = Puppet::Network::FormatHandler.format_for(type)\n    mime = format.mime\n    charset = format.charset\n\n    if charset\n      if body.is_a?(String) && body.encoding != charset\n        body.encode!(charset)\n      end\n\n      mime += \"; charset=#{charset.name.downcase}\"\n    end\n\n    @handler.set_content_type(@response, mime)\n    @handler.set_response(@response, body, code)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http/route.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Network::HTTP::Route\n  MethodNotAllowedHandler = lambda do |req, _res|\n    raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError.new(\"method #{req.method} not allowed for route #{req.path}\", Puppet::Network::HTTP::Issues::UNSUPPORTED_METHOD)\n  end\n\n  NO_HANDLERS = [MethodNotAllowedHandler]\n\n  attr_reader :path_matcher\n\n  def self.path(path_matcher)\n    new(path_matcher)\n  end\n\n  def initialize(path_matcher)\n    @path_matcher = path_matcher\n    @method_handlers = {\n      :GET => NO_HANDLERS,\n      :HEAD => NO_HANDLERS,\n      :OPTIONS => NO_HANDLERS,\n      :POST => NO_HANDLERS,\n      :PUT => NO_HANDLERS,\n      :DELETE => NO_HANDLERS\n    }\n    @chained = []\n  end\n\n  def get(*handlers)\n    @method_handlers[:GET] = handlers\n    self\n  end\n\n  def head(*handlers)\n    @method_handlers[:HEAD] = handlers\n    self\n  end\n\n  def options(*handlers)\n    @method_handlers[:OPTIONS] = handlers\n    self\n  end\n\n  def post(*handlers)\n    @method_handlers[:POST] = handlers\n    self\n  end\n\n  def put(*handlers)\n    @method_handlers[:PUT] = handlers\n    self\n  end\n\n  def delete(*handlers)\n    @method_handlers[:DELETE] = handlers\n    self\n  end\n\n  def any(*handlers)\n    @method_handlers.each do |method, _registered_handlers|\n      @method_handlers[method] = handlers\n    end\n    self\n  end\n\n  def chain(*routes)\n    @chained = routes\n    self\n  end\n\n  def matches?(request)\n    Puppet.debug { \"Evaluating match for #{inspect}\" }\n    if match(request.routing_path)\n      return true\n    else\n      Puppet.debug { \"Did not match path (#{request.routing_path.inspect})\" }\n    end\n\n    false\n  end\n\n  def process(request, response)\n    handlers = @method_handlers[request.method.upcase.intern] || NO_HANDLERS\n    handlers.each do |handler|\n      handler.call(request, response)\n    end\n\n    subrequest = request.route_into(match(request.routing_path).to_s)\n    chained_route = @chained.find { |route| route.matches?(subrequest) }\n    if chained_route\n      chained_route.process(subrequest, response)\n    end\n  end\n\n  def inspect\n    \"Route #{@path_matcher.inspect}\"\n  end\n\n  private\n\n  def match(path)\n    @path_matcher.match(path)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/http.rb",
    "content": "# frozen_string_literal: true\n\n# This module is used to handle puppet REST requests in puppetserver.\nmodule Puppet::Network::HTTP\n  HEADER_ENABLE_PROFILING = \"X-Puppet-Profiling\"\n  HEADER_PUPPET_VERSION = \"X-Puppet-Version\"\n\n  SERVER_URL_PREFIX = \"/puppet\"\n  SERVER_URL_VERSIONS = \"v3\"\n\n  MASTER_URL_PREFIX = SERVER_URL_PREFIX\n  MASTER_URL_VERSIONS = SERVER_URL_VERSIONS\n\n  CA_URL_PREFIX = \"/puppet-ca\"\n  CA_URL_VERSIONS = \"v1\"\n\n  require_relative '../../puppet/network/authconfig'\n  require_relative '../../puppet/network/authorization'\n\n  require_relative 'http/issues'\n  require_relative 'http/error'\n  require_relative 'http/route'\n  require_relative 'http/api'\n  require_relative 'http/api/master'\n  require_relative 'http/api/master/v3'\n  require_relative 'http/handler'\n  require_relative 'http/response'\n  require_relative 'http/request'\n  require_relative 'http/memory_response'\nend\n"
  },
  {
    "path": "lib/puppet/network/http_pool.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/network/http/connection'\n\nmodule Puppet::Network; end\n\n# This module is deprecated.\n#\n# @api public\n# @deprecated Use {Puppet::HTTP::Client} instead.\n#\nmodule Puppet::Network::HttpPool\n  @http_client_class = Puppet::Network::HTTP::Connection\n\n  def self.http_client_class\n    @http_client_class\n  end\n\n  def self.http_client_class=(klass)\n    @http_client_class = klass\n  end\n\n  # Retrieve a connection for the given host and port.\n  #\n  # @param host [String] The hostname to connect to\n  # @param port [Integer] The port on the host to connect to\n  # @param use_ssl [Boolean] Whether to use an SSL connection\n  # @param verify_peer [Boolean] Whether to verify the peer credentials, if possible. Verification will not take place if the CA certificate is missing.\n  # @return [Puppet::Network::HTTP::Connection]\n  #\n  # @deprecated Use {Puppet.runtime[:http]} instead.\n  # @api public\n  #\n  def self.http_instance(host, port, use_ssl = true, verify_peer = true)\n    Puppet.warn_once('deprecations', self, \"The method 'Puppet::Network::HttpPool.http_instance' is deprecated. Use Puppet.runtime[:http] instead\")\n\n    if verify_peer\n      verifier = Puppet::SSL::Verifier.new(host, nil)\n    else\n      ssl = Puppet::SSL::SSLProvider.new\n      verifier = Puppet::SSL::Verifier.new(host, ssl.create_insecure_context)\n    end\n    http_client_class.new(host, port, use_ssl: use_ssl, verifier: verifier)\n  end\n\n  # Retrieve a connection for the given host and port.\n  #\n  # @param host [String] The host to connect to\n  # @param port [Integer] The port to connect to\n  # @param use_ssl [Boolean] Whether to use SSL, defaults to `true`.\n  # @param ssl_context [Puppet::SSL:SSLContext, nil] The ssl context to use\n  #   when making HTTPS connections. Required when `use_ssl` is `true`.\n  # @return [Puppet::Network::HTTP::Connection]\n  #\n  # @deprecated Use {Puppet.runtime[:http]} instead.\n  # @api public\n  #\n  def self.connection(host, port, use_ssl: true, ssl_context: nil)\n    Puppet.warn_once('deprecations', self, \"The method 'Puppet::Network::HttpPool.connection' is deprecated. Use Puppet.runtime[:http] instead\")\n\n    if use_ssl\n      unless ssl_context\n        # TRANSLATORS 'ssl_context' is an argument and should not be translated\n        raise ArgumentError, _(\"An ssl_context is required when connecting to 'https://%{host}:%{port}'\") % { host: host, port: port }\n      end\n\n      verifier = Puppet::SSL::Verifier.new(host, ssl_context)\n      http_client_class.new(host, port, use_ssl: true, verifier: verifier)\n    else\n      if ssl_context\n        # TRANSLATORS 'ssl_context' is an argument and should not be translated\n        Puppet.warning(_(\"An ssl_context is unnecessary when connecting to 'http://%{host}:%{port}' and will be ignored\") % { host: host, port: port })\n      end\n\n      http_client_class.new(host, port, use_ssl: false)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network/uri.rb",
    "content": "# frozen_string_literal: true\n\n# This module holds funtions for network URI's\nmodule Puppet::Network::Uri\n  # Mask credentials in given URI or address as string. Resulting string will\n  # contain '***' in place of password. It will only be replaced if actual\n  # password is given.\n  #\n  # @param uri [URI|String] an uri or address to be masked\n  # @return [String] a masked url\n  def mask_credentials(uri)\n    if uri.is_a? URI\n      uri = uri.dup\n    else\n      uri = URI.parse(uri)\n    end\n    uri.password = '***' unless uri.password.nil?\n    uri.to_s\n  end\nend\n"
  },
  {
    "path": "lib/puppet/network.rb",
    "content": "# frozen_string_literal: true\n\n# Just a stub, so we can correctly scope other classes.\nmodule Puppet::Network # :nodoc:\nend\n"
  },
  {
    "path": "lib/puppet/node/environment.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util'\nrequire 'monitor'\nrequire_relative '../../puppet/parser/parser_factory'\nrequire_relative '../../puppet/concurrent/lock'\n\n# Just define it, so this class has fewer load dependencies.\nclass Puppet::Node\nend\n\n# Puppet::Node::Environment acts as a container for all configuration\n# that is expected to vary between environments.\n#\n# ## The root environment\n#\n# In addition to normal environments that are defined by the user,there is a\n# special 'root' environment. It is defined as an instance variable on the\n# Puppet::Node::Environment metaclass. The environment name is `*root*` and can\n# be accessed by looking up the `:root_environment` using {Puppet.lookup}.\n#\n# The primary purpose of the root environment is to contain parser functions\n# that are not bound to a specific environment. The main case for this is for\n# logging functions. Logging functions are attached to the 'root' environment\n# when {Puppet::Parser::Functions.reset} is called.\nclass Puppet::Node::Environment\n  NO_MANIFEST = :no_manifest\n\n  # The create() factory method should be used instead.\n  private_class_method :new\n\n  # Create a new environment with the given name\n  #\n  # @param name [Symbol] the name of the environment\n  # @param modulepath [Array<String>] the list of paths from which to load modules\n  # @param manifest [String] the path to the manifest for the environment or\n  #   the constant Puppet::Node::Environment::NO_MANIFEST if there is none.\n  # @param config_version [String] path to a script whose output will be added\n  #   to report logs (optional)\n  # @return [Puppet::Node::Environment]\n  #\n  # @api public\n  def self.create(name, modulepath, manifest = NO_MANIFEST, config_version = nil)\n    new(name, modulepath, manifest, config_version)\n  end\n\n  # A remote subclass to make it easier to trace instances when debugging.\n  # @api private\n  class Remote < Puppet::Node::Environment; end\n\n  # A \"reference\" to a remote environment. The created environment instance\n  # isn't expected to exist on the local system, but is instead a reference to\n  # environment information on a remote system. For instance when a catalog is\n  # being applied, this will be used on the agent.\n  #\n  # @note This does not provide access to the information of the remote\n  # environment's modules, manifest, or anything else. It is simply a value\n  # object to pass around and use as an environment.\n  #\n  # @param name [Symbol] The name of the remote environment\n  #\n  def self.remote(name)\n    Remote.create(name, [], NO_MANIFEST)\n  end\n\n  # Instantiate a new environment\n  #\n  # @note {Puppet::Node::Environment.new} is private for historical reasons, as\n  #   previously it had been overridden to return memoized objects and was\n  #   replaced with {Puppet::Node::Environment.create}, so this will not be\n  #   invoked with the normal Ruby initialization semantics.\n  #\n  # @param name [Symbol] The environment name\n  def initialize(name, modulepath, manifest, config_version)\n    @lock = Puppet::Concurrent::Lock.new\n    @name = name.intern\n    @modulepath = self.class.expand_dirs(self.class.extralibs() + modulepath)\n    @manifest = manifest == NO_MANIFEST ? manifest : Puppet::FileSystem.expand_path(manifest)\n\n    @config_version = config_version\n  end\n\n  # Creates a new Puppet::Node::Environment instance, overriding any of the passed\n  # parameters.\n  #\n  # @param env_params [Hash<{Symbol => String,Array<String>}>] new environment\n  #   parameters (:modulepath, :manifest, :config_version)\n  # @return [Puppet::Node::Environment]\n  def override_with(env_params)\n    self.class.create(name,\n                      env_params[:modulepath] || modulepath,\n                      env_params[:manifest] || manifest,\n                      env_params[:config_version] || config_version)\n  end\n\n  # Creates a new Puppet::Node::Environment instance, overriding :manifest,\n  # :modulepath, or :config_version from the passed settings if they were\n  # originally set from the commandline, or returns self if there is nothing to\n  # override.\n  #\n  # @param settings [Puppet::Settings] an initialized puppet settings instance\n  # @return [Puppet::Node::Environment] new overridden environment or self if\n  #   there are no commandline changes from settings.\n  def override_from_commandline(settings)\n    overrides = {}\n\n    if settings.set_by_cli?(:modulepath)\n      overrides[:modulepath] = self.class.split_path(settings.value(:modulepath))\n    end\n\n    if settings.set_by_cli?(:config_version)\n      overrides[:config_version] = settings.value(:config_version)\n    end\n\n    if settings.set_by_cli?(:manifest)\n      overrides[:manifest] = settings.value(:manifest)\n    end\n\n    overrides.empty? ?\n      self :\n      override_with(overrides)\n  end\n\n  # @param [String] name Environment name to check for valid syntax.\n  # @return [Boolean] true if name is valid\n  # @api public\n  def self.valid_name?(name)\n    !!name.match(/\\A\\w+\\Z/)\n  end\n\n  # @!attribute [r] name\n  #   @api public\n  #   @return [Symbol] the human readable environment name that serves as the\n  #     environment identifier\n  attr_reader :name\n\n  # @api public\n  # @return [Array<String>] All directories present on disk in the modulepath\n  def modulepath\n    @modulepath.find_all do |p|\n      Puppet::FileSystem.directory?(p)\n    end\n  end\n\n  # @api public\n  # @return [Array<String>] All directories in the modulepath (even if they are not present on disk)\n  def full_modulepath\n    @modulepath\n  end\n\n  # @!attribute [r] manifest\n  #   @api public\n  #   @return [String] path to the manifest file or directory.\n  attr_reader :manifest\n\n  # @!attribute [r] config_version\n  #   @api public\n  #   @return [String] path to a script whose output will be added to report logs\n  #     (optional)\n  attr_reader :config_version\n\n  # Cached loaders - management of value handled by Puppet::Pops::Loaders\n  # @api private\n  attr_accessor :loaders\n\n  # Lock for compilation that needs exclusive access to the environment\n  # @api private\n  attr_reader :lock\n\n  # For use with versioned dirs\n  # our environment path may contain symlinks, while we want to resolve the\n  # path while reading the manifests we may want to report the resources as\n  # coming from the configured path.\n  attr_accessor :configured_path\n\n  # See :configured_path above\n  attr_accessor :resolved_path\n\n  # Ensure the path given is of the format we want in the catalog/report.\n  #\n  # Intended for use with versioned symlinked environments. If this\n  # environment is configured with \"/etc/puppetlabs/code/environments/production\"\n  # but the resolved path is\n  #\n  # \"/opt/puppetlabs/server/puppetserver/filesync/client/puppet-code/production_abcdef1234\"\n  #\n  # this changes the filepath\n  #\n  # \"/opt/puppetlabs/server/puppetserver/filesync/client/puppet-code/production_abcdef1234/modules/foo/manifests/init.pp\"\n  #\n  # to\n  #\n  # \"/etc/puppetlabs/code/environments/production/modules/foo/manifests/init.pp\"\n  def externalize_path(filepath)\n    paths_set        = configured_path && resolved_path\n    munging_possible = paths_set && configured_path != resolved_path\n    munging_desired  = munging_possible &&\n                       Puppet[:report_configured_environmentpath] &&\n                       filepath.to_s.start_with?(resolved_path)\n\n    if munging_desired\n      File.join(configured_path, filepath.delete_prefix(resolved_path))\n    else\n      filepath\n    end\n  end\n\n  # Checks to make sure that this environment did not have a manifest set in\n  # its original environment.conf if Puppet is configured with\n  # +disable_per_environment_manifest+ set true.  If it did, the environment's\n  # modules may not function as intended by the original authors, and we may\n  # seek to halt a puppet compilation for a node in this environment.\n  #\n  # The only exception to this would be if the environment.conf manifest is an exact,\n  # uninterpolated match for the current +default_manifest+ setting.\n  #\n  # @return [Boolean] true if using directory environments, and\n  #   Puppet[:disable_per_environment_manifest] is true, and this environment's\n  #   original environment.conf had a manifest setting that is not the\n  #   Puppet[:default_manifest].\n  # @api private\n  def conflicting_manifest_settings?\n    return false unless Puppet[:disable_per_environment_manifest]\n\n    original_manifest = configuration.raw_setting(:manifest)\n    !original_manifest.nil? && !original_manifest.empty? && original_manifest != Puppet[:default_manifest]\n  end\n\n  # @api private\n  def static_catalogs?\n    if @static_catalogs.nil?\n      environment_conf = Puppet.lookup(:environments).get_conf(name)\n      @static_catalogs = (environment_conf.nil? ? Puppet[:static_catalogs] : environment_conf.static_catalogs)\n    end\n    @static_catalogs\n  end\n\n  # Return the environment configuration\n  # @return [Puppet::Settings::EnvironmentConf] The configuration\n  #\n  # @api private\n  def configuration\n    Puppet.lookup(:environments).get_conf(name)\n  end\n\n  # Checks the environment and settings for any conflicts\n  # @return [Array<String>] an array of validation errors\n  # @api public\n  def validation_errors\n    errors = []\n    if conflicting_manifest_settings?\n      errors << _(\"The 'disable_per_environment_manifest' setting is true, and the '%{env_name}' environment has an environment.conf manifest that conflicts with the 'default_manifest' setting.\") % { env_name: name }\n    end\n    errors\n  end\n\n  def rich_data_from_env_conf\n    unless @checked_conf_for_rich_data\n      environment_conf = Puppet.lookup(:environments).get_conf(name)\n      @rich_data_from_conf = environment_conf&.rich_data\n      @checked_conf_for_rich_data = true\n    end\n    @rich_data_from_conf\n  end\n\n  # Checks if this environment permits use of rich data types in the catalog\n  # Checks the environment conf for an override on first query, then going forward\n  # either uses that, or if unset, uses the current value of the `rich_data` setting.\n  # @return [Boolean] `true` if rich data is permitted.\n  # @api private\n  def rich_data?\n    @rich_data = rich_data_from_env_conf.nil? ? Puppet[:rich_data] : rich_data_from_env_conf\n  end\n\n  # Return an environment-specific Puppet setting.\n  #\n  # @api public\n  #\n  # @param param [String, Symbol] The environment setting to look up\n  # @return [Object] The resolved setting value\n  def [](param)\n    Puppet.settings.value(param, name)\n  end\n\n  # @api public\n  # @return [Puppet::Resource::TypeCollection] The current global TypeCollection\n  def known_resource_types\n    @lock.synchronize do\n      if @known_resource_types.nil?\n        @known_resource_types = Puppet::Resource::TypeCollection.new(self)\n        @known_resource_types.import_ast(perform_initial_import(), '')\n      end\n      @known_resource_types\n    end\n  end\n\n  # Yields each modules' plugin directory if the plugin directory (modulename/lib)\n  # is present on the filesystem.\n  #\n  # @yield [String] Yields the plugin directory from each module to the block.\n  # @api public\n  def each_plugin_directory(&block)\n    modules.map(&:plugin_directory).each do |lib|\n      lib = Puppet::Util::Autoload.cleanpath(lib)\n      yield lib if File.directory?(lib)\n    end\n  end\n\n  # Locate a module instance by the module name alone.\n  #\n  # @api public\n  #\n  # @param name [String] The module name\n  # @return [Puppet::Module, nil] The module if found, else nil\n  def module(name)\n    modules_by_name[name]\n  end\n\n  # Locate a module instance by the full forge name (EG authorname/module)\n  #\n  # @api public\n  #\n  # @param forge_name [String] The module name\n  # @return [Puppet::Module, nil] The module if found, else nil\n  def module_by_forge_name(forge_name)\n    _, modname = forge_name.split('/')\n    found_mod = self.module(modname)\n    found_mod and found_mod.forge_name == forge_name ?\n      found_mod :\n      nil\n  end\n\n  # Return all modules for this environment in the order they appear in the\n  # modulepath.\n  # @note If multiple modules with the same name are present they will\n  #   both be added, but methods like {#module} and {#module_by_forge_name}\n  #   will return the first matching entry in this list.\n  # @note This value is cached so that the filesystem doesn't have to be\n  #   re-enumerated every time this method is invoked, since that\n  #   enumeration could be a costly operation and this method is called\n  #   frequently. The cache expiry is determined by `Puppet[:filetimeout]`.\n  # @api public\n  # @return [Array<Puppet::Module>] All modules for this environment\n  def modules\n    if @modules.nil?\n      module_references = []\n      project = Puppet.lookup(:bolt_project) { nil }\n      seen_modules = if project && project.load_as_module?\n                       module_references << project.to_h\n                       { project.name => true }\n                     else\n                       {}\n                     end\n      modulepath.each do |path|\n        Puppet::FileSystem.children(path).map do |p|\n          Puppet::FileSystem.basename_string(p)\n        end.each do |name|\n          next unless Puppet::Module.is_module_directory?(name, path)\n\n          warn_about_mistaken_path(path, name)\n          unless seen_modules[name]\n            module_references << { :name => name, :path => File.join(path, name) }\n            seen_modules[name] = true\n          end\n        end\n      end\n\n      @modules = module_references.filter_map do |reference|\n        Puppet::Module.new(reference[:name], reference[:path], self)\n      rescue Puppet::Module::Error => e\n        Puppet.log_exception(e)\n        nil\n      end\n    end\n    @modules\n  end\n\n  # @api private\n  def modules_by_name\n    @modules_by_name ||= modules.to_h { |mod| [mod.name, mod] }\n  end\n  private :modules_by_name\n\n  # Generate a warning if the given directory in a module path entry is named `lib`.\n  #\n  # @api private\n  #\n  # @param path [String] The module directory containing the given directory\n  # @param name [String] The directory name\n  def warn_about_mistaken_path(path, name)\n    if name == \"lib\"\n      Puppet.debug {\n        \"Warning: Found directory named 'lib' in module path ('#{path}/lib'); unless you \\\n        are expecting to load a module named 'lib', your module path may be set incorrectly.\"\n      }\n    end\n  end\n\n  # Modules broken out by directory in the modulepath\n  #\n  # @api public\n  #\n  # @return [Hash<String, Array<Puppet::Module>>] A hash whose keys are file\n  #   paths, and whose values is an array of Puppet Modules for that path\n  def modules_by_path\n    modules_by_path = {}\n    modulepath.each do |path|\n      if Puppet::FileSystem.exist?(path)\n        module_names = Puppet::FileSystem.children(path).map do |p|\n          Puppet::FileSystem.basename_string(p)\n        end.select do |name|\n          Puppet::Module.is_module_directory?(name, path)\n        end\n        modules_by_path[path] = module_names.sort.map do |name|\n          Puppet::Module.new(name, File.join(path, name), self)\n        end\n      else\n        modules_by_path[path] = []\n      end\n    end\n    modules_by_path\n  end\n\n  # All module requirements for all modules in the environment modulepath\n  #\n  # @api public\n  #\n  # @comment This has nothing to do with an environment. It seems like it was\n  #   stuffed into the first convenient class that vaguely involved modules.\n  #\n  # @example\n  #   environment.module_requirements\n  #   # => {\n  #   #   'username/amodule' => [\n  #   #     {\n  #   #       'name'    => 'username/moduledep',\n  #   #       'version' => '1.2.3',\n  #   #       'version_requirement' => '>= 1.0.0',\n  #   #     },\n  #   #     {\n  #   #       'name'    => 'username/anotherdep',\n  #   #       'version' => '4.5.6',\n  #   #       'version_requirement' => '>= 3.0.0',\n  #   #     }\n  #   #   ]\n  #   # }\n  #   #\n  #\n  # @return [Hash<String, Array<Hash<String, String>>>] See the method example\n  #   for an explanation of the return value.\n  def module_requirements\n    deps = {}\n\n    modules.each do |mod|\n      next unless mod.forge_name\n\n      deps[mod.forge_name] ||= []\n\n      mod.dependencies and mod.dependencies.each do |mod_dep|\n        dep_name = mod_dep['name'].tr('-', '/')\n        (deps[dep_name] ||= []) << {\n          'name' => mod.forge_name,\n          'version' => mod.version,\n          'version_requirement' => mod_dep['version_requirement']\n        }\n      end\n    end\n\n    deps.each do |mod, mod_deps|\n      deps[mod] = mod_deps.sort_by { |d| d['name'] }\n    end\n\n    deps\n  end\n\n  # Loads module translations for the current environment once for\n  # the lifetime of the environment. Execute a block in the context\n  # of that translation domain.\n  def with_text_domain\n    return yield if Puppet[:disable_i18n]\n\n    if @text_domain.nil?\n      @text_domain = @name\n      Puppet::GettextConfig.reset_text_domain(@text_domain)\n      Puppet::ModuleTranslations.load_from_modulepath(modules)\n    else\n      Puppet::GettextConfig.use_text_domain(@text_domain)\n    end\n\n    yield\n  ensure\n    # Is a noop if disable_i18n is true\n    Puppet::GettextConfig.clear_text_domain\n  end\n\n  # Checks if a reparse is required (cache of files is stale).\n  #\n  def check_for_reparse\n    @lock.synchronize do\n      if Puppet[:code] != @parsed_code || @known_resource_types.parse_failed?\n        @parsed_code = nil\n        @known_resource_types = nil\n      end\n    end\n  end\n\n  # @return [String] The YAML interpretation of the object\n  # Return the name of the environment as a string interpretation of the object\n  def to_yaml\n    to_s.to_yaml\n  end\n\n  # @return [String] The stringified value of the `name` instance variable\n  # @api public\n  def to_s\n    name.to_s\n  end\n\n  # @api public\n  def inspect\n    %Q(<#{self.class}:#{object_id} @name=\"#{name}\" @manifest=\"#{manifest}\" @modulepath=\"#{full_modulepath.join(':')}\" >)\n  end\n\n  # @return [Symbol] The `name` value, cast to a string, then cast to a symbol.\n  #\n  # @api public\n  #\n  # @note the `name` instance variable is a Symbol, but this casts the value\n  #   to a String and then converts it back into a Symbol which will needlessly\n  #   create an object that needs to be garbage collected\n  def to_sym\n    to_s.to_sym\n  end\n\n  def self.split_path(path_string)\n    path_string.split(File::PATH_SEPARATOR)\n  end\n\n  def ==(other)\n    true if other.is_a?(Puppet::Node::Environment) &&\n            name == other.name &&\n            full_modulepath == other.full_modulepath &&\n            manifest == other.manifest\n  end\n\n  alias eql? ==\n\n  def hash\n    [self.class, name, full_modulepath, manifest].hash\n  end\n\n  # not private so it can be called in tests\n  def self.extralibs\n    if ENV['PUPPETLIB']\n      split_path(ENV.fetch('PUPPETLIB', nil))\n    else\n      []\n    end\n  end\n\n  # not private so it can be called in initialize\n  def self.expand_dirs(dirs)\n    dirs.collect do |dir|\n      Puppet::FileSystem.expand_path(dir)\n    end\n  end\n\n  private\n\n  # Reparse the manifests for the given environment\n  #\n  # There are two sources that can be used for the initial parse:\n  #\n  #   1. The value of `Puppet[:code]`: Puppet can take a string from\n  #     its settings and parse that as a manifest. This is used by various\n  #     Puppet applications to read in a manifest and pass it to the\n  #     environment as a side effect. This is attempted first.\n  #   2. The contents of this environment's +manifest+ attribute: Puppet will\n  #     try to load the environment manifest.\n  #\n  # @return [Puppet::Parser::AST::Hostclass] The AST hostclass object\n  #   representing the 'main' hostclass\n  def perform_initial_import\n    parser = Puppet::Parser::ParserFactory.parser\n    @parsed_code = Puppet[:code]\n    if @parsed_code != \"\"\n      parser.string = @parsed_code\n      parser.parse\n    else\n      file = manifest\n      # if the manifest file is a reference to a directory, parse and combine\n      # all .pp files in that directory\n      if file == NO_MANIFEST\n        empty_parse_result\n      elsif File.directory?(file)\n        # JRuby does not properly perform Dir.glob operations with wildcards, (see PUP-11788 and https://github.com/jruby/jruby/issues/7836).\n        # We sort the results because Dir.glob order is inconsistent in Ruby < 3 (see PUP-10115).\n        parse_results = Puppet::FileSystem::PathPattern.absolute(File.join(file, '**/*')).glob.select { |globbed_file| globbed_file.end_with?('.pp') }.sort.map do |file_to_parse|\n          parser.file = file_to_parse\n          parser.parse\n        end\n        # Use a parser type specific merger to concatenate the results\n        Puppet::Parser::AST::Hostclass.new('', :code => Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results))\n      else\n        parser.file = file\n        parser.parse\n      end\n    end\n  rescue Puppet::ParseErrorWithIssue => detail\n    @known_resource_types.parse_failed = true\n    detail.environment = name\n    raise\n  rescue => detail\n    @known_resource_types.parse_failed = true\n\n    msg = _(\"Could not parse for environment %{env}: %{detail}\") % { env: self, detail: detail }\n    error = Puppet::Error.new(msg)\n    error.set_backtrace(detail.backtrace)\n    raise error\n  end\n\n  # Return an empty top-level hostclass to indicate that no file was loaded\n  #\n  # @return [Puppet::Parser::AST::Hostclass]\n  def empty_parse_result\n    Puppet::Parser::AST::Hostclass.new('')\n  end\n\n  # A None subclass to make it easier to trace the NONE environment when debugging.\n  # @api private\n  class None < Puppet::Node::Environment; end\n\n  # A special \"null\" environment\n  #\n  # This environment should be used when there is no specific environment in\n  # effect.\n  NONE = None.create(:none, [])\nend\n"
  },
  {
    "path": "lib/puppet/node/facts.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'time'\n\nrequire_relative '../../puppet/node'\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/util/psych_support'\n\n# Manage a given node's facts.  This either accepts facts and stores them, or\n# returns facts for a given node.\nclass Puppet::Node::Facts\n  include Puppet::Util::PsychSupport\n\n  # Set up indirection, so that nodes can be looked for in\n  # the node sources.\n  extend Puppet::Indirector\n\n  # We want to expire any cached nodes if the facts are saved.\n  module NodeExpirer\n    def save(instance, key = nil, options = {})\n      Puppet::Node.indirection.expire(instance.name, options)\n      super\n    end\n  end\n\n  indirects :facts, :terminus_setting => :facts_terminus, :extend => NodeExpirer\n\n  attr_accessor :name, :values, :timestamp\n\n  def add_local_facts\n    values[\"clientcert\"] = Puppet.settings[:certname]\n    values[\"clientversion\"] = Puppet.version.to_s\n    values[\"clientnoop\"] = Puppet.settings[:noop]\n  end\n\n  def initialize(name, values = {})\n    @name = name\n    @values = values\n\n    add_timestamp\n  end\n\n  def initialize_from_hash(data)\n    @name = data['name']\n    @values = data['values']\n    # Timestamp will be here in YAML, e.g. when reading old reports\n    timestamp = @values.delete('_timestamp')\n    # Timestamp will be here in JSON\n    timestamp ||= data['timestamp']\n\n    if timestamp.is_a? String\n      @timestamp = Time.parse(timestamp)\n    else\n      @timestamp = timestamp\n    end\n\n    self.expiration = data['expiration']\n    if expiration.is_a? String\n      self.expiration = Time.parse(expiration)\n    end\n  end\n\n  # Add extra values, such as facts given to lookup on the command line. The\n  # extra values will override existing values.\n  # @param extra_values [Hash{String=>Object}] the values to add\n  # @api private\n  def add_extra_values(extra_values)\n    @values.merge!(extra_values)\n    nil\n  end\n\n  # Sanitize fact values by converting everything not a string, Boolean\n  # numeric, array or hash into strings.\n  def sanitize\n    values.each do |fact, value|\n      values[fact] = sanitize_fact value\n    end\n  end\n\n  def ==(other)\n    return false unless name == other.name\n\n    values == other.values\n  end\n\n  def self.from_data_hash(data)\n    new_facts = allocate\n    new_facts.initialize_from_hash(data)\n    new_facts\n  end\n\n  def to_data_hash\n    result = {\n      'name' => name,\n      'values' => values\n    }\n\n    if @timestamp\n      if @timestamp.is_a? Time\n        result['timestamp'] = @timestamp.iso8601(9)\n      else\n        result['timestamp'] = @timestamp\n      end\n    end\n\n    if expiration\n      if expiration.is_a? Time\n        result['expiration'] = expiration.iso8601(9)\n      else\n        result['expiration'] = expiration\n      end\n    end\n\n    result\n  end\n\n  def add_timestamp\n    @timestamp = Time.now\n  end\n\n  def to_yaml\n    facts_to_display = Psych.parse_stream(YAML.dump(self))\n    quote_special_strings(facts_to_display)\n  end\n\n  private\n\n  def quote_special_strings(fact_hash)\n    fact_hash.grep(Psych::Nodes::Scalar).each do |node|\n      next unless node.value =~ /:/\n\n      node.plain  = false\n      node.quoted = true\n      node.style  = Psych::Nodes::Scalar::DOUBLE_QUOTED\n    end\n\n    fact_hash.yaml\n  end\n\n  def sanitize_fact(fact)\n    case fact\n    when Hash\n      ret = {}\n      fact.each_pair { |k, v| ret[sanitize_fact k] = sanitize_fact v }\n      ret\n    when Array\n      fact.collect { |i| sanitize_fact i }\n    when Numeric, TrueClass, FalseClass, String\n      fact\n    else\n      result = fact.to_s\n      # The result may be ascii-8bit encoded without being a binary (low level object.inspect returns ascii-8bit string)\n      if result.encoding == Encoding::ASCII_8BIT\n        begin\n          result = result.encode(Encoding::UTF_8)\n        rescue\n          # return the ascii-8bit - it will be taken as a binary\n          result\n        end\n      end\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/node/server_facts.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Node::ServerFacts\n  def self.load\n    server_facts = {}\n\n    # Add our server Puppet Enterprise version, if available.\n    pe_version_file = '/opt/puppetlabs/server/pe_version'\n    if File.readable?(pe_version_file) and !File.zero?(pe_version_file)\n      server_facts['pe_serverversion'] = File.read(pe_version_file).chomp\n    end\n\n    # Add our server version to the fact list\n    server_facts[\"serverversion\"] = Puppet.version.to_s\n\n    # And then add the server name and IP\n    { \"servername\" => \"networking.fqdn\",\n      \"serverip\" => \"networking.ip\",\n      \"serverip6\" => \"networking.ip6\" }.each do |var, fact|\n      value = Puppet.runtime[:facter].value(fact)\n      unless value.nil?\n        server_facts[var] = value\n      end\n    end\n\n    if server_facts[\"servername\"].nil?\n      host = Puppet.runtime[:facter].value('networking.hostname')\n      if host.nil?\n        Puppet.warning _(\"Could not retrieve fact servername\")\n      elsif domain = Puppet.runtime[:facter].value('networking.domain') # rubocop:disable Lint/AssignmentInCondition\n        server_facts[\"servername\"] = [host, domain].join(\".\")\n      else\n        server_facts[\"servername\"] = host\n      end\n    end\n\n    if server_facts[\"serverip\"].nil? && server_facts[\"serverip6\"].nil?\n      Puppet.warning _(\"Could not retrieve either serverip or serverip6 fact\")\n    end\n\n    server_facts\n  end\nend\n"
  },
  {
    "path": "lib/puppet/node.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/indirector'\n\n# A class for managing nodes, including their facts and environment.\nclass Puppet::Node\n  require_relative 'node/facts'\n  require_relative 'node/environment'\n\n  # Set up indirection, so that nodes can be looked for in\n  # the node sources.\n  extend Puppet::Indirector\n\n  # Asymmetric serialization/deserialization required in this class via to/from datahash\n  include Puppet::Util::PsychSupport\n\n  # Use the node source as the indirection terminus.\n  indirects :node, :terminus_setting => :node_terminus, :doc => \"Where to find node information.\n    A node is composed of its name, its facts, and its environment.\"\n\n  attr_accessor :name, :classes, :source, :ipaddress, :parameters, :environment_name\n  attr_reader :time, :facts, :trusted_data\n\n  attr_reader :server_facts\n\n  ENVIRONMENT = 'environment'\n\n  def initialize_from_hash(data)\n    @name       = data['name']       || (raise ArgumentError, _(\"No name provided in serialized data\"))\n    @classes    = data['classes']    || []\n    @parameters = data['parameters'] || {}\n    env_name = data['environment'] || @parameters[ENVIRONMENT]\n    unless env_name.nil?\n      @parameters[ENVIRONMENT] = env_name\n      @environment_name = env_name.intern\n    end\n  end\n\n  def self.from_data_hash(data)\n    node = new(name)\n    node.initialize_from_hash(data)\n    node\n  end\n\n  def to_data_hash\n    result = {\n      'name' => name,\n      'environment' => environment.name.to_s,\n    }\n    result['classes'] = classes unless classes.empty?\n    serialized_params = serializable_parameters\n    result['parameters'] = serialized_params unless serialized_params.empty?\n    result\n  end\n\n  def serializable_parameters\n    new_params = parameters.dup\n    new_params.delete(ENVIRONMENT)\n    new_params\n  end\n\n  def environment\n    unless @environment\n      env = parameters[ENVIRONMENT]\n      if env\n        self.environment = env\n      elsif environment_name\n        self.environment = environment_name\n      else\n        # This should not be :current_environment, this is the default\n        # for a node when it has not specified its environment\n        # it will be used to establish what the current environment is.\n        #\n        self.environment = Puppet.lookup(:environments).get!(Puppet[:environment])\n      end\n\n    end\n    @environment\n  end\n\n  def environment=(env)\n    if env.is_a?(String) or env.is_a?(Symbol)\n      @environment = Puppet.lookup(:environments).get!(env)\n    else\n      @environment = env\n    end\n\n    # Keep environment_name attribute and parameter in sync if they have been set\n    unless @environment.nil?\n      # always set the environment parameter. It becomes top scope $environment for a manifest during catalog compilation.\n      @parameters[ENVIRONMENT] = @environment.name.to_s\n      self.environment_name = @environment.name\n    end\n  end\n\n  def has_environment_instance?\n    !@environment.nil?\n  end\n\n  def initialize(name, options = {})\n    raise ArgumentError, _(\"Node names cannot be nil\") unless name\n\n    @name = name\n\n    classes = options[:classes]\n    if classes\n      if classes.is_a?(String)\n        @classes = [classes]\n      else\n        @classes = classes\n      end\n    else\n      @classes = []\n    end\n\n    @parameters = options[:parameters] || {}\n\n    @facts = options[:facts]\n\n    @server_facts = {}\n\n    env = options[:environment]\n    if env\n      self.environment = env\n    end\n\n    @time = Time.now\n  end\n\n  # Merge the node facts with parameters from the node source.\n  # @api public\n  # @param facts [optional, Puppet::Node::Facts] facts to merge into node parameters.\n  #   Will query Facts indirection if not supplied.\n  # @raise [Puppet::Error] Raise on failure to retrieve facts if not supplied\n  # @return [nil]\n  def fact_merge(facts = nil)\n    begin\n      @facts = facts.nil? ? Puppet::Node::Facts.indirection.find(name, :environment => environment) : facts\n    rescue => detail\n      error = Puppet::Error.new(_(\"Could not retrieve facts for %{name}: %{detail}\") % { name: name, detail: detail }, detail)\n      error.set_backtrace(detail.backtrace)\n      raise error\n    end\n\n    unless @facts.nil?\n      @facts.sanitize\n      # facts should never modify the environment parameter\n      orig_param_env = @parameters[ENVIRONMENT]\n      merge(@facts.values)\n      @parameters[ENVIRONMENT] = orig_param_env\n    end\n  end\n\n  # Merge any random parameters into our parameter list.\n  def merge(params)\n    params.each do |name, value|\n      if @parameters.include?(name)\n        Puppet::Util::Warnings.warnonce(_(\"The node parameter '%{param_name}' for node '%{node_name}' was already set to '%{value}'. It could not be set to '%{desired_value}'\") % { param_name: name, node_name: @name, value: @parameters[name], desired_value: value })\n      else\n        @parameters[name] = value\n      end\n    end\n  end\n\n  # Add extra facts, such as facts given to lookup on the command line The\n  # extra facts will override existing ones.\n  # @param extra_facts [Hash{String=>Object}] the facts to tadd\n  # @api private\n  def add_extra_facts(extra_facts)\n    @facts.add_extra_values(extra_facts)\n    @parameters.merge!(extra_facts)\n    nil\n  end\n\n  def add_server_facts(facts)\n    # Append the current environment to the list of server facts\n    @server_facts = facts.merge({ \"environment\" => environment.name.to_s })\n\n    # Merge the server facts into the parameters for the node\n    merge(facts)\n  end\n\n  # Calculate the list of names we might use for looking\n  # up our node.  This is only used for AST nodes.\n  def names\n    @names ||= [name]\n  end\n\n  def split_name(name)\n    list = name.split(\".\")\n    tmp = []\n    list.each_with_index do |_short, i|\n      tmp << list[0..i].join(\".\")\n    end\n    tmp.reverse\n  end\n\n  # Ensures the data is frozen\n  #\n  def trusted_data=(data)\n    Puppet.warning(_(\"Trusted node data modified for node %{name}\") % { name: name }) unless @trusted_data.nil?\n    @trusted_data = data.freeze\n  end\n\n  # Resurrects and sanitizes trusted information in the node by modifying it and setting\n  # the trusted_data in the node from parameters.\n  # This modifies the node\n  #\n  def sanitize\n    # Resurrect \"trusted information\" that comes from node/fact terminus.\n    # The current way this is done in puppet db (currently the only one)\n    # is to store the node parameter 'trusted' as a hash of the trusted information.\n    #\n    # Thus here there are two main cases:\n    # 1. This terminus was used in a real agent call (only meaningful if someone curls the request as it would\n    #  fail since the result is a hash of two catalogs).\n    # 2  It is a command line call with a given node that use a terminus that:\n    # 2.1 does not include a 'trusted' fact - use local from node trusted information\n    # 2.2 has a 'trusted' fact - this in turn could be\n    # 2.2.1 puppet db having stored trusted node data as a fact (not a great design)\n    # 2.2.2 some other terminus having stored a fact called \"trusted\" (most likely that would have failed earlier, but could\n    #       be spoofed).\n    #\n    # For the reasons above, the resurrection of trusted node data with authenticated => true is only performed\n    # if user is running as root, else it is resurrected as unauthenticated.\n    #\n    trusted_param = @parameters['trusted']\n    if trusted_param\n      # Blows up if it is a parameter as it will be set as $trusted by the compiler as if it was a variable\n      @parameters.delete('trusted')\n      unless trusted_param.is_a?(Hash) && %w[authenticated certname extensions].all? { |key| trusted_param.has_key?(key) }\n        # trusted is some kind of garbage, do not resurrect\n        trusted_param = nil\n      end\n    else\n      # trusted may be Boolean false if set as a fact by someone\n      trusted_param = nil\n    end\n\n    # The options for node.trusted_data in priority order are:\n    # 1) node came with trusted_data so use that\n    # 2) else if there is :trusted_information in the puppet context\n    # 3) else if the node provided a 'trusted' parameter (parsed out above)\n    # 4) last, fallback to local node trusted information\n    #\n    # Note that trusted_data should be a hash, but (2) and (4) are not\n    # hashes, so we to_h at the end\n    unless trusted_data\n      trusted = Puppet.lookup(:trusted_information) do\n        trusted_param || Puppet::Context::TrustedInformation.local(self)\n      end\n\n      self.trusted_data = trusted.to_h\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pal/catalog_compiler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Pal\n  # A CatalogCompiler is a compiler that builds a catalog of resources and dependencies as a side effect of\n  # evaluating puppet language code.\n  # When the compilation of the given input manifest(s)/code string/file is finished the catalog is complete\n  # for encoding and use. It is also possible to evaluate more strings within the same compilation context to\n  # add or remove things from the catalog.\n  #\n  # @api public\n  class CatalogCompiler < Compiler\n    # @api private\n    def catalog\n      internal_compiler.catalog\n    end\n    private :catalog\n\n    # Returns true if this is a compiler that compiles a catalog.\n    # This implementation returns `true`\n    # @return [Boolean] true\n    # @api public\n    def has_catalog?\n      true\n    end\n\n    # Calls a block of code and yields a configured `JsonCatalogEncoder` to the block.\n    # @example Get resulting catalog as pretty printed Json\n    #   Puppet::Pal.in_environment(...) do |pal|\n    #     pal.with_catalog_compiler(...) do |compiler|\n    #       compiler.with_json_encoding { |encoder| encoder.encode }\n    #     end\n    #   end\n    #\n    # @api public\n    #\n    def with_json_encoding(pretty: true, exclude_virtual: true)\n      yield JsonCatalogEncoder.new(catalog, pretty: pretty, exclude_virtual: exclude_virtual)\n    end\n\n    # Returns a hash representation of the compiled catalog.\n    #\n    # @api public\n    def catalog_data_hash\n      catalog.to_data_hash\n    end\n\n    # Evaluates an AST obtained from `parse_string` or `parse_file` in topscope.\n    # If the ast is a `Puppet::Pops::Model::Program` (what is returned from the `parse` methods, any definitions\n    # in the program (that is, any function, plan, etc. that is defined will be made available for use).\n    #\n    # @param ast [Puppet::Pops::Model::PopsObject] typically the returned `Program` from the parse methods, but can be any `Expression`\n    # @returns [Object] whatever the ast evaluates to\n    #\n    def evaluate(ast)\n      if ast.is_a?(Puppet::Pops::Model::Program)\n        bridged = Puppet::Parser::AST::PopsBridge::Program.new(ast)\n        # define all catalog types\n        internal_compiler.environment.known_resource_types.import_ast(bridged, \"\")\n        bridged.evaluate(internal_compiler.topscope)\n      else\n        internal_evaluator.evaluate(topscope, ast)\n      end\n    end\n\n    # Compiles the result of additional evaluation taking place in a PAL catalog compilation.\n    # This will evaluate all lazy constructs until all have been evaluated, and will the validate\n    # the result.\n    #\n    # This should be called if evaluating string or files of puppet logic after the initial\n    # compilation taking place by giving PAL a manifest or code-string.\n    # This method should be called when a series of evaluation should have reached a\n    # valid state (there should be no dangling relationships (to resources that does not\n    # exist).\n    #\n    # As an alternative the methods `evaluate_additions` can be called without any\n    # requirements on consistency and then calling `validate` at the end.\n    #\n    # Can be called multiple times.\n    #\n    # @return [Void]\n    def compile_additions\n      internal_compiler.compile_additions\n    end\n\n    # Validates the state of the catalog (without performing evaluation of any elements\n    # requiring lazy evaluation. Can be called multiple times.\n    #\n    def validate\n      internal_compiler.validate\n    end\n\n    # Evaluates all lazy constructs that were produced as a side effect of evaluating puppet logic.\n    # Can be called multiple times.\n    #\n    def evaluate_additions\n      internal_compiler.evaluate_additions\n    end\n\n    # Attempts to evaluate AST for node defnintions https://puppet.com/docs/puppet/latest/lang_node_definitions.html\n    # if there are any.\n    def evaluate_ast_node\n      internal_compiler.evaluate_ast_node\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/compiler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Pal\n  # A configured compiler as obtained in the callback from `Puppet::Pal.with_script_compiler`.\n  # (Later, there may also be a catalog compiler available.)\n  #\n  class Compiler\n    attr_reader :internal_compiler\n    protected :internal_compiler\n\n    attr_reader :internal_evaluator\n    protected :internal_evaluator\n\n    def initialize(internal_compiler)\n      @internal_compiler = internal_compiler\n      @internal_evaluator = Puppet::Pops::Parser::EvaluatingParser.new\n    end\n\n    # Calls a function given by name with arguments specified in an `Array`, and optionally accepts a code block.\n    # @param function_name [String] the name of the function to call\n    # @param args [Object] the arguments to the function\n    # @param block [Proc] an optional callable block that is given to the called function\n    # @return [Object] what the called function returns\n    #\n    def call_function(function_name, *args, &block)\n      # TRANSLATORS: do not translate variable name strings in these assertions\n      Pal.assert_non_empty_string(function_name, 'function_name', false)\n      Pal.assert_type(Pal::T_ANY_ARRAY, args, 'args', false)\n      internal_evaluator.evaluator.external_call_function(function_name, args, topscope, &block)\n    end\n\n    # Returns a Puppet::Pal::FunctionSignature object or nil if function is not found\n    # The returned FunctionSignature has information about all overloaded signatures of the function\n    #\n    # @example using function_signature\n    #   # returns true if 'myfunc' is callable with three integer arguments 1, 2, 3\n    #   compiler.function_signature('myfunc').callable_with?([1,2,3])\n    #\n    # @param function_name [String] the name of the function to get a signature for\n    # @return [Puppet::Pal::FunctionSignature] a function signature, or nil if function not found\n    #\n    def function_signature(function_name)\n      loader = internal_compiler.loaders.private_environment_loader\n      func = loader.load(:function, function_name)\n      if func\n        return FunctionSignature.new(func.class)\n      end\n\n      # Could not find function\n      nil\n    end\n\n    # Returns an array of TypedName objects for all functions, optionally filtered by a regular expression.\n    # The returned array has more information than just the leaf name - the typical thing is to just get\n    # the name as showing the following example.\n    #\n    # Errors that occur during function discovery will either be logged as warnings or collected by the optional\n    # `error_collector` array. When provided, it will receive {Puppet::DataTypes::Error} instances describing\n    # each error in detail and no warnings will be logged.\n    #\n    # @example getting the names of all functions\n    #   compiler.list_functions.map {|tn| tn.name }\n    #\n    # @param filter_regex [Regexp] an optional regexp that filters based on name (matching names are included in the result)\n    # @param error_collector [Array<Puppet::DataTypes::Error>] an optional array that will receive errors during load\n    # @return [Array<Puppet::Pops::Loader::TypedName>] an array of typed names\n    #\n    def list_functions(filter_regex = nil, error_collector = nil)\n      list_loadable_kind(:function, filter_regex, error_collector)\n    end\n\n    # Evaluates a string of puppet language code in top scope.\n    # A \"source_file\" reference to a source can be given - if not an actual file name, by convention the name should\n    # be bracketed with < > to indicate it is something symbolic; for example `<commandline>` if the string was given on the\n    # command line.\n    #\n    # If the given `puppet_code` is `nil` or an empty string, `nil` is returned, otherwise the result of evaluating the\n    # puppet language string. The given string must form a complete and valid expression/statement as an error is raised\n    # otherwise. That is, it is not possible to divide a compound expression by line and evaluate each line individually.\n    #\n    # @param puppet_code [String, nil] the puppet language code to evaluate, must be a complete expression/statement\n    # @param source_file [String, nil] an optional reference to a source (a file or symbolic name/location)\n    # @return [Object] what the `puppet_code` evaluates to\n    #\n    def evaluate_string(puppet_code, source_file = nil)\n      return nil if puppet_code.nil? || puppet_code == ''\n      unless puppet_code.is_a?(String)\n        raise ArgumentError, _(\"The argument 'puppet_code' must be a String, got %{type}\") % { type: puppet_code.class }\n      end\n\n      evaluate(parse_string(puppet_code, source_file))\n    end\n\n    # Evaluates a puppet language file in top scope.\n    # The file must exist and contain valid puppet language code or an error is raised.\n    #\n    # @param file [Path, String] an absolute path to a file with puppet language code, must exist\n    # @return [Object] what the last evaluated expression in the file evaluated to\n    #\n    def evaluate_file(file)\n      evaluate(parse_file(file))\n    end\n\n    # Evaluates an AST obtained from `parse_string` or `parse_file` in topscope.\n    # If the ast is a `Puppet::Pops::Model::Program` (what is returned from the `parse` methods, any definitions\n    # in the program (that is, any function, plan, etc. that is defined will be made available for use).\n    #\n    # @param ast [Puppet::Pops::Model::PopsObject] typically the returned `Program` from the parse methods, but can be any `Expression`\n    # @returns [Object] whatever the ast evaluates to\n    #\n    def evaluate(ast)\n      if ast.is_a?(Puppet::Pops::Model::Program)\n        loaders = Puppet.lookup(:loaders)\n        loaders.instantiate_definitions(ast, loaders.public_environment_loader)\n      end\n      internal_evaluator.evaluate(topscope, ast)\n    end\n\n    # Produces a literal value if the AST obtained from `parse_string` or `parse_file` does not require any actual evaluation.\n    # This method is useful if obtaining an AST that represents literal values; string, integer, float, boolean, regexp, array, hash;\n    # for example from having read this from the command line or as values in some file.\n    #\n    # @param ast [Puppet::Pops::Model::PopsObject] typically the returned `Program` from the parse methods, but can be any `Expression`\n    # @returns [Object] whatever the literal value the ast evaluates to\n    #\n    def evaluate_literal(ast)\n      catch :not_literal do\n        return Puppet::Pops::Evaluator::LiteralEvaluator.new().literal(ast)\n      end\n      # TRANSLATORS, the 'ast' is the name of a parameter, do not translate\n      raise ArgumentError, _(\"The given 'ast' does not represent a literal value\")\n    end\n\n    # Parses and validates a puppet language string and returns an instance of Puppet::Pops::Model::Program on success.\n    # If the content is not valid an error is raised.\n    #\n    # @param code_string [String] a puppet language string to parse and validate\n    # @param source_file [String] an optional reference to a file or other location in angled brackets\n    # @return [Puppet::Pops::Model::Program] returns a `Program` instance on success\n    #\n    def parse_string(code_string, source_file = nil)\n      unless code_string.is_a?(String)\n        raise ArgumentError, _(\"The argument 'code_string' must be a String, got %{type}\") % { type: code_string.class }\n      end\n\n      internal_evaluator.parse_string(code_string, source_file)\n    end\n\n    # Parses and validates a puppet language file and returns an instance of Puppet::Pops::Model::Program on success.\n    # If the content is not valid an error is raised.\n    #\n    # @param file [String] a file with puppet language content to parse and validate\n    # @return [Puppet::Pops::Model::Program] returns a `Program` instance on success\n    #\n    def parse_file(file)\n      unless file.is_a?(String)\n        raise ArgumentError, _(\"The argument 'file' must be a String, got %{type}\") % { type: file.class }\n      end\n\n      internal_evaluator.parse_file(file)\n    end\n\n    # Parses a puppet data type given in String format and returns that type, or raises an error.\n    # A type is needed in calls to `new` to create an instance of the data type, or to perform type checking\n    # of values - typically using `type.instance?(obj)` to check if `obj` is an instance of the type.\n    #\n    # @example Verify if obj is an instance of a data type\n    #   # evaluates to true\n    #   pal.type('Enum[red, blue]').instance?(\"blue\")\n    #\n    # @example Create an instance of a data type\n    #   # using an already create type\n    #   t = pal.type('Car')\n    #   pal.create(t, 'color' => 'black', 'make' => 't-ford')\n    #\n    #   # letting 'new_object' parse the type from a string\n    #   pal.create('Car', 'color' => 'black', 'make' => 't-ford')\n    #\n    # @param type_string [String] a puppet language data type\n    # @return [Puppet::Pops::Types::PAnyType] the data type\n    #\n    def type(type_string)\n      Puppet::Pops::Types::TypeParser.singleton.parse(type_string)\n    end\n\n    # Creates a new instance of a given data type.\n    # @param data_type [String, Puppet::Pops::Types::PAnyType] the data type as a data type or in String form.\n    # @param arguments [Object] one or more arguments to the called `new` function\n    # @return [Object] an instance of the given data type,\n    #   or raises an error if it was not possible to parse data type or create an instance.\n    #\n    def create(data_type, *arguments)\n      t = data_type.is_a?(String) ? type(data_type) : data_type\n      unless t.is_a?(Puppet::Pops::Types::PAnyType)\n        raise ArgumentError, _(\"Given data_type value is not a data type, got '%{type}'\") % { type: t.class }\n      end\n\n      call_function('new', t, *arguments)\n    end\n\n    # Returns true if this is a compiler that compiles a catalog.\n    # This implementation returns `false`\n    # @return Boolan false\n    def has_catalog?\n      false\n    end\n\n    protected\n\n    def list_loadable_kind(kind, filter_regex = nil, error_collector = nil)\n      loader = internal_compiler.loaders.private_environment_loader\n      if filter_regex.nil?\n        loader.discover(kind, error_collector)\n      else\n        loader.discover(kind, error_collector) { |f| f.name =~ filter_regex }\n      end\n    end\n\n    private\n\n    def topscope\n      internal_compiler.topscope\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/function_signature.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Pal\n  # A FunctionSignature is returned from `function_signature`. Its purpose is to answer questions about the function's parameters\n  # and if it can be called with a set of parameters.\n  #\n  # It is also possible to get an array of puppet Callable data type where each callable describes one possible way\n  # the function can be called.\n  #\n  # @api public\n  #\n  class FunctionSignature\n    # @api private\n    def initialize(function_class)\n      @func = function_class\n    end\n\n    # Returns true if the function can be called with the given arguments and false otherwise.\n    # If the function is not callable, and a code block is given, it is given a formatted error message that describes\n    # the type mismatch. That error message can be quite complex if the function has multiple dispatch depending on\n    # given types.\n    #\n    # @param args [Array] The arguments as given to the function call\n    # @param callable [Proc, nil] An optional ruby Proc or puppet lambda given to the function\n    # @yield [String] a formatted error message describing a type mismatch if the function is not callable with given args + block\n    # @return [Boolean] true if the function can be called with given args + block, and false otherwise\n    # @api public\n    #\n    def callable_with?(args, callable = nil)\n      signatures = @func.dispatcher.to_type\n      callables = signatures.is_a?(Puppet::Pops::Types::PVariantType) ? signatures.types : [signatures]\n\n      return true if callables.any? { |t| t.callable_with?(args) }\n      return false unless block_given?\n\n      args_type = Puppet::Pops::Types::TypeCalculator.singleton.infer_set(callable.nil? ? args : args + [callable])\n      error_message = Puppet::Pops::Types::TypeMismatchDescriber.describe_signatures(@func.name, @func.signatures, args_type)\n      yield error_message\n      false\n    end\n\n    # Returns an array of Callable puppet data type\n    # @return [Array<Puppet::Pops::Types::PCallableType] one callable per way the function can be called\n    #\n    # @api public\n    #\n    def callables\n      signatures = @func.dispatcher.to_type\n      signatures.is_a?(Puppet::Pops::Types::PVariantType) ? signatures.types : [signatures]\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/json_catalog_encoder.rb",
    "content": "# frozen_string_literal: true\n\n# The JsonCatalogEncoder is a wrapper around a catalog produced by the Pal::CatalogCompiler.with_json_encoding\n# method.\n# It allows encoding the entire catalog or an individual resource as Rich Data Json.\n#\n# @api public\n#\nmodule Puppet\nmodule Pal\nclass JsonCatalogEncoder\n  # Is the resulting Json pretty printed or not.\n  attr_reader :pretty\n\n  # Should unrealized virtual resources be included in the result or not.\n  attr_reader :exclude_virtual\n\n  # The internal catalog being build - what this class wraps with a public API.\n  attr_reader :catalog\n  private :catalog\n\n  # Do not instantiate this class directly! Use the `Pal::CatalogCompiler#with_json_encoding` method\n  # instead.\n  #\n  # @param catalog [Puppet::Resource::Catalog] the internal catalog that this class wraps\n  # @param pretty [Boolean] (true), if the resulting JSON should be pretty printed or not\n  # @param exclude_virtual [Boolean] (true), if the resulting catalog should contain unrealzed virtual resources or not\n  #\n  # @api private\n  #\n  def initialize(catalog, pretty: true, exclude_virtual: true)\n    @catalog = catalog\n    @pretty = pretty\n    @exclude_virtual = exclude_virtual\n  end\n\n  # Encodes the entire catalog as a rich-data Json catalog.\n  # @return String The catalog in Json format using rich data format\n  # @api public\n  #\n  def encode\n    possibly_filtered_catalog.to_json(:pretty => pretty)\n  end\n\n  # Returns one particular resource as a Json string, or returns nil if resource was not found.\n  # @param type [String] the name of the puppet type (case independent)\n  # @param title [String] the title of the wanted resource\n  # @return [String] the resulting Json text\n  # @api public\n  #\n  def encode_resource(type, title)\n    # Ensure that both type and title are given since the underlying API will do mysterious things\n    # if 'title' is nil. (Other assertions are made by the catalog when looking up the resource).\n    #\n    # TRANSLATORS 'type' and 'title' are internal parameter names - do not translate\n    raise ArgumentError, _(\"Both type and title must be given\") if type.nil? or title.nil?\n\n    r = possibly_filtered_catalog.resource(type, title)\n    return nil if r.nil?\n\n    r.to_data_hash.to_json(:pretty => pretty)\n  end\n\n  # Applies a filter for virtual resources and returns filtered catalog\n  # or the catalog itself if filtering was not needed.\n  # The result is cached.\n  # @api private\n  # rubocop:disable Naming/MemoizedInstanceVariableName\n  def possibly_filtered_catalog\n    @filtered ||= (exclude_virtual ? catalog.filter(&:virtual?) : catalog)\n  end\n  private :possibly_filtered_catalog\n  # rubocop:enable Naming/MemoizedInstanceVariableName\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/pal_api.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  require_relative '../../puppet/parser/script_compiler'\n  require_relative '../../puppet/parser/catalog_compiler'\n\n  module Pal\n    require_relative '../../puppet/pal/json_catalog_encoder'\n    require_relative '../../puppet/pal/function_signature'\n    require_relative '../../puppet/pal/task_signature'\n    require_relative '../../puppet/pal/plan_signature'\n    require_relative '../../puppet/pal/compiler'\n    require_relative '../../puppet/pal/script_compiler'\n    require_relative '../../puppet/pal/catalog_compiler'\n    require_relative '../../puppet/pal/pal_impl'\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pal/pal_impl.rb",
    "content": "# frozen_string_literal: true\n\n# Puppet as a Library \"PAL\"\n\n# Yes, this requires all of puppet for now because 'settings' and many other things...\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/parser/script_compiler'\nrequire_relative '../../puppet/parser/catalog_compiler'\n\nmodule Puppet\n# This is the main entry point for \"Puppet As a Library\" PAL.\n# This file should be required instead of \"puppet\"\n# Initially, this will require ALL of puppet - over time this will change as the monolithical \"puppet\" is broken up\n# into smaller components.\n#\n# @example Running a snippet of Puppet Language code\n#   require 'puppet_pal'\n#   result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: ['/tmp/testmodules']) do |pal|\n#     pal.evaluate_script_string('1+2+3')\n#   end\n#   # The result is the value 6\n#\n# @example Calling a function\n#   require 'puppet_pal'\n#   result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: ['/tmp/testmodules']) do |pal|\n#     pal.call_function('mymodule::myfunction', 10, 20)\n#   end\n#   # The result is what 'mymodule::myfunction' returns\n#\n# @api public\nmodule Pal\n  # Defines a context in which multiple operations in an env with a script compiler can be performed in a given block.\n  # The calls that takes place to PAL inside of the given block are all with the same instance of the compiler.\n  # The parameter `configured_by_env` makes it possible to either use the configuration in the environment, or specify\n  # `manifest_file` or `code_string` manually. If neither is given, an empty `code_string` is used.\n  #\n  # @example define a script compiler without any initial logic\n  #   pal.with_script_compiler do | compiler |\n  #     # do things with compiler\n  #   end\n  #\n  # @example define a script compiler with a code_string containing initial logic\n  #   pal.with_script_compiler(code_string: '$myglobal_var = 42')  do | compiler |\n  #     # do things with compiler\n  #   end\n  #\n  # @param configured_by_env [Boolean] when true the environment's settings are used, otherwise the given `manifest_file` or `code_string`\n  # @param manifest_file [String] a Puppet Language file to load and evaluate before calling the given block, mutually exclusive with `code_string`\n  # @param code_string [String] a Puppet Language source string to load and evaluate before calling the given block, mutually exclusive with `manifest_file`\n  # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)\n  #   If given at the environment level, the facts given here are merged with higher priority.\n  # @param variables [Hash] optional map of fully qualified variable name to value. If given at the environment level, the variables\n  #   given here are merged with higher priority.\n  # @param set_local_facts [Boolean] when true, the $facts, $server_facts, and $trusted variables are set for the scope.\n  # @param block [Proc] the block performing operations on compiler\n  # @return [Object] what the block returns\n  # @yieldparam [Puppet::Pal::ScriptCompiler] compiler, a ScriptCompiler to perform operations on.\n  #\n  def self.with_script_compiler(\n    configured_by_env: false,\n    manifest_file:     nil,\n    code_string:       nil,\n    facts:             {},\n    variables:         {},\n    set_local_facts:   true,\n    &block\n  )\n    # TRANSLATORS: do not translate variable name strings in these assertions\n    assert_mutually_exclusive(manifest_file, code_string, 'manifest_file', 'code_string')\n    assert_non_empty_string(manifest_file, 'manifest_file', true)\n    assert_non_empty_string(code_string, 'code_string', true)\n    assert_type(T_BOOLEAN, configured_by_env, \"configured_by_env\", false)\n\n    if configured_by_env\n      unless manifest_file.nil? && code_string.nil?\n        # TRANSLATORS: do not translate the variable names in this error message\n        raise ArgumentError, _(\"manifest_file or code_string cannot be given when configured_by_env is true\")\n      end\n\n      # Use the manifest setting\n      manifest_file = Puppet[:manifest]\n    elsif manifest_file.nil? && code_string.nil?\n      # An \"undef\" code_string is the only way to override Puppet[:manifest] & Puppet[:code] settings since an\n      # empty string is taken as Puppet[:code] not being set.\n      #\n      code_string = 'undef'\n    end\n\n    previous_tasks_value = Puppet[:tasks]\n    previous_code_value = Puppet[:code]\n    Puppet[:tasks] = true\n    # After the assertions, if code_string is non nil - it has the highest precedence\n    Puppet[:code] = code_string unless code_string.nil?\n\n    # If manifest_file is nil, the #main method will use the env configured manifest\n    # to do things in the block while a Script Compiler is in effect\n    main(\n      manifest: manifest_file,\n      facts: facts,\n      variables: variables,\n      internal_compiler_class: :script,\n      set_local_facts: set_local_facts,\n      &block\n    )\n  ensure\n    Puppet[:tasks] = previous_tasks_value\n    Puppet[:code] = previous_code_value\n  end\n\n  # Evaluates a Puppet Language script string.\n  # @param code_string [String] a snippet of Puppet Language source code\n  # @return [Object] what the Puppet Language code_string evaluates to\n  # @deprecated Use {#with_script_compiler} and then evaluate_string on the given compiler - to be removed in 1.0 version\n  #\n  def self.evaluate_script_string(code_string)\n    # prevent the default loading of Puppet[:manifest] which is the environment's manifest-dir by default settings\n    # by setting code_string to 'undef'\n    with_script_compiler do |compiler|\n      compiler.evaluate_string(code_string)\n    end\n  end\n\n  # Evaluates a Puppet Language script (.pp) file.\n  # @param manifest_file [String] a file with Puppet Language source code\n  # @return [Object] what the Puppet Language manifest_file contents evaluates to\n  # @deprecated Use {#with_script_compiler} and then evaluate_file on the given compiler - to be removed in 1.0 version\n  #\n  def self.evaluate_script_manifest(manifest_file)\n    with_script_compiler do |compiler|\n      compiler.evaluate_file(manifest_file)\n    end\n  end\n\n  # Defines a context in which multiple operations in an env with a catalog producing compiler can be performed\n  # in a given block.\n  # The calls that takes place to PAL inside of the given block are all with the same instance of the compiler.\n  # The parameter `configured_by_env` makes it possible to either use the configuration in the environment, or specify\n  # `manifest_file` or `code_string` manually. If neither is given, an empty `code_string` is used.\n  #\n  # @example define a catalog compiler without any initial logic\n  #   pal.with_catalog_compiler do | compiler |\n  #     # do things with compiler\n  #   end\n  #\n  # @example define a catalog compiler with a code_string containing initial logic\n  #   pal.with_catalog_compiler(code_string: '$myglobal_var = 42')  do | compiler |\n  #     # do things with compiler\n  #   end\n  #\n  # @param configured_by_env [Boolean] when true the environment's settings are used, otherwise the\n  #   given `manifest_file` or `code_string`\n  # @param manifest_file [String] a Puppet Language file to load and evaluate before calling the given block, mutually exclusive\n  #   with `code_string`\n  # @param code_string [String] a Puppet Language source string to load and evaluate before calling the given block, mutually\n  #   exclusive with `manifest_file`\n  # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)\n  #   If given at the environment level, the facts given here are merged with higher priority.\n  # @param variables [Hash] optional map of fully qualified variable name to value. If given at the environment level, the variables\n  #   given here are merged with higher priority.\n  # @param block [Proc] the block performing operations on compiler\n  # @return [Object] what the block returns\n  # @yieldparam [Puppet::Pal::CatalogCompiler] compiler, a CatalogCompiler to perform operations on.\n  #\n  def self.with_catalog_compiler(\n    configured_by_env: false,\n    manifest_file:     nil,\n    code_string:       nil,\n    facts:             {},\n    variables:         {},\n    target_variables:  {},\n    &block\n  )\n    # TRANSLATORS: do not translate variable name strings in these assertions\n    assert_mutually_exclusive(manifest_file, code_string, 'manifest_file', 'code_string')\n    assert_non_empty_string(manifest_file, 'manifest_file', true)\n    assert_non_empty_string(code_string, 'code_string', true)\n    assert_type(T_BOOLEAN, configured_by_env, \"configured_by_env\", false)\n\n    if configured_by_env\n      unless manifest_file.nil? && code_string.nil?\n        # TRANSLATORS: do not translate the variable names in this error message\n        raise ArgumentError, _(\"manifest_file or code_string cannot be given when configured_by_env is true\")\n      end\n\n      # Use the manifest setting\n      manifest_file = Puppet[:manifest]\n    elsif manifest_file.nil? && code_string.nil?\n      # An \"undef\" code_string is the only way to override Puppet[:manifest] & Puppet[:code] settings since an\n      # empty string is taken as Puppet[:code] not being set.\n      #\n      code_string = 'undef'\n    end\n\n    # We need to make sure to set these back when we're done\n    previous_tasks_value = Puppet[:tasks]\n    previous_code_value = Puppet[:code]\n\n    Puppet[:tasks] = false\n    # After the assertions, if code_string is non nil - it has the highest precedence\n    Puppet[:code] = code_string unless code_string.nil?\n\n    # If manifest_file is nil, the #main method will use the env configured manifest\n    # to do things in the block while a Script Compiler is in effect\n    main(\n      manifest: manifest_file,\n      facts: facts,\n      variables: variables,\n      target_variables: target_variables,\n      internal_compiler_class: :catalog,\n      set_local_facts: false,\n      &block\n    )\n  ensure\n    # Clean up after ourselves\n    Puppet[:tasks] = previous_tasks_value\n    Puppet[:code] = previous_code_value\n  end\n\n  # Defines the context in which to perform puppet operations (evaluation, etc)\n  # The code to evaluate in this context is given in a block.\n  #\n  # @param env_name [String] a name to use for the temporary environment - this only shows up in errors\n  # @param modulepath [Array<String>] an array of directory paths containing Puppet modules, may be empty, defaults to empty array\n  # @param settings_hash [Hash] a hash of settings - currently not used for anything, defaults to empty hash\n  # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)\n  # @param variables [Hash] optional map of fully qualified variable name to value\n  # @return [Object] returns what the given block returns\n  # @yieldparam [Puppet::Pal] context, a context that responds to Puppet::Pal methods\n  #\n  def self.in_tmp_environment(env_name,\n                              modulepath:    [],\n                              settings_hash: {},\n                              facts:         nil,\n                              variables:     {},\n                              &block)\n    assert_non_empty_string(env_name, _(\"temporary environment name\"))\n    # TRANSLATORS: do not translate variable name string in these assertions\n    assert_optionally_empty_array(modulepath, 'modulepath')\n\n    unless block_given?\n      raise ArgumentError, _(\"A block must be given to 'in_tmp_environment'\") # TRANSLATORS 'in_tmp_environment' is a name, do not translate\n    end\n\n    env = Puppet::Node::Environment.create(env_name, modulepath)\n\n    in_environment_context(\n      Puppet::Environments::Static.new(env), # The tmp env is the only known env\n      env, facts, variables, &block\n    )\n  end\n\n  # Defines the context in which to perform puppet operations (evaluation, etc)\n  # The code to evaluate in this context is given in a block.\n  #\n  # The name of an environment (env_name) is always given. The location of that environment on disk\n  # is then either constructed by:\n  # * searching a given envpath where name is a child of a directory on that path, or\n  # * it is the directory given in env_dir (which must exist).\n  #\n  # The env_dir and envpath options are mutually exclusive.\n  #\n  # @param env_name [String] the name of an existing environment\n  # @param modulepath [Array<String>] an array of directory paths containing Puppet modules, overrides the modulepath of an existing env.\n  #   Defaults to `{env_dir}/modules` if `env_dir` is given,\n  # @param pre_modulepath [Array<String>] like modulepath, but is prepended to the modulepath\n  # @param post_modulepath [Array<String>] like modulepath, but is appended to the modulepath\n  # @param settings_hash [Hash] a hash of settings - currently not used for anything, defaults to empty hash\n  # @param env_dir [String] a reference to a directory being the named environment (mutually exclusive with `envpath`)\n  # @param envpath [String] a path of directories in which there are environments to search for `env_name` (mutually exclusive with `env_dir`).\n  #   Should be a single directory, or several directories separated with platform specific `File::PATH_SEPARATOR` character.\n  # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)\n  # @param variables [Hash] optional map of fully qualified variable name to value\n  # @return [Object] returns what the given block returns\n  # @yieldparam [Puppet::Pal] context, a context that responds to Puppet::Pal methods\n  #\n  # @api public\n  def self.in_environment(env_name,\n                          modulepath: nil,\n                          pre_modulepath: [],\n                          post_modulepath: [],\n                          settings_hash: {},\n                          env_dir:       nil,\n                          envpath:       nil,\n                          facts:         nil,\n                          variables:     {},\n                          &block)\n    # TRANSLATORS terms in the assertions below are names of terms in code\n    assert_non_empty_string(env_name, 'env_name')\n    assert_optionally_empty_array(modulepath, 'modulepath', true)\n    assert_optionally_empty_array(pre_modulepath, 'pre_modulepath', false)\n    assert_optionally_empty_array(post_modulepath, 'post_modulepath', false)\n    assert_mutually_exclusive(env_dir, envpath, 'env_dir', 'envpath')\n\n    unless block_given?\n      raise ArgumentError, _(\"A block must be given to 'in_environment'\") # TRANSLATORS 'in_environment' is a name, do not translate\n    end\n\n    if env_dir\n      unless Puppet::FileSystem.exist?(env_dir)\n        raise ArgumentError, _(\"The environment directory '%{env_dir}' does not exist\") % { env_dir: env_dir }\n      end\n\n      # a nil modulepath for env_dir means it should use its ./modules directory\n      mid_modulepath = modulepath.nil? ? [Puppet::FileSystem.expand_path(File.join(env_dir, 'modules'))] : modulepath\n\n      env = Puppet::Node::Environment.create(env_name, pre_modulepath + mid_modulepath + post_modulepath)\n      environments = Puppet::Environments::StaticDirectory.new(env_name, env_dir, env) # The env being used is the only one...\n    else\n      assert_non_empty_string(envpath, 'envpath')\n\n      # The environment is resolved against the envpath. This is setup without a basemodulepath\n      # The modulepath defaults to the 'modulepath' in the found env when \"Directories\" is used\n      #\n      if envpath.is_a?(String) && envpath.include?(File::PATH_SEPARATOR)\n        # potentially more than one directory to search\n        env_loaders = Puppet::Environments::Directories.from_path(envpath, [])\n        environments = Puppet::Environments::Combined.new(*env_loaders)\n      else\n        environments = Puppet::Environments::Directories.new(envpath, [])\n      end\n      env = environments.get(env_name)\n      if env.nil?\n        raise ArgumentError, _(\"No directory found for the environment '%{env_name}' on the path '%{envpath}'\") % { env_name: env_name, envpath: envpath }\n      end\n\n      # A given modulepath should override the default\n      mid_modulepath = modulepath.nil? ? env.modulepath : modulepath\n      env_path = env.configuration.path_to_env\n      env = env.override_with(:modulepath => pre_modulepath + mid_modulepath + post_modulepath)\n      # must configure this in case logic looks up the env by name again (otherwise the looked up env does\n      # not have the same effective modulepath).\n      environments = Puppet::Environments::StaticDirectory.new(env_name, env_path, env) # The env being used is the only one...\n    end\n    in_environment_context(environments, env, facts, variables, &block)\n  end\n\n  # Prepares the puppet context with pal information - and delegates to the block\n  # No set up is performed at this step - it is delayed until it is known what the\n  # operation is going to be (for example - using a ScriptCompiler).\n  #\n  def self.in_environment_context(environments, env, facts, variables, &block)\n    # Create a default node to use (may be overridden later)\n    node = Puppet::Node.new(Puppet[:node_name_value], :environment => env)\n\n    Puppet.override(\n      environments: environments,     # The env being used is the only one...\n      pal_env: env,                   # provide as convenience\n      pal_current_node: node,         # to allow it to be picked up instead of created\n      pal_variables: variables,       # common set of variables across several inner contexts\n      pal_facts: facts                # common set of facts across several inner contexts (or nil)\n    ) do\n      # DELAY: prepare_node_facts(node, facts)\n      return block.call(self)\n    end\n  end\n  private_class_method :in_environment_context\n\n  # Prepares the node for use by giving it node_facts (if given)\n  # If a hash of facts values is given, then the operation of creating a node with facts is much\n  # speeded up (as getting a fresh set of facts is avoided in a later step).\n  #\n  def self.prepare_node_facts(node, facts)\n    # Prepare the node with facts if it does not already have them\n    if node.facts.nil?\n      node_facts = facts.nil? ? nil : Puppet::Node::Facts.new(Puppet[:node_name_value], facts)\n      node.fact_merge(node_facts)\n      # Add server facts so $server_facts[environment] exists when doing a puppet script\n      # SCRIPT TODO: May be needed when running scripts under orchestrator. Leave it for now.\n      #\n      node.add_server_facts({})\n    end\n  end\n  private_class_method :prepare_node_facts\n\n  def self.add_variables(scope, variables)\n    return if variables.nil?\n    unless variables.is_a?(Hash)\n      raise ArgumentError, _(\"Given variables must be a hash, got %{type}\") % { type: variables.class }\n    end\n\n    rich_data_t = Puppet::Pops::Types::TypeFactory.rich_data\n    variables.each_pair do |k, v|\n      unless k =~ Puppet::Pops::Patterns::VAR_NAME\n        raise ArgumentError, _(\"Given variable '%{varname}' has illegal name\") % { varname: k }\n      end\n\n      unless rich_data_t.instance?(v)\n        raise ArgumentError, _(\"Given value for '%{varname}' has illegal type - got: %{type}\") % { varname: k, type: v.class }\n      end\n\n      scope.setvar(k, v)\n    end\n  end\n  private_class_method :add_variables\n\n  # The main routine for script compiler\n  # Picks up information from the puppet context and configures a script compiler which is given to\n  # the provided block\n  #\n  def self.main(\n    manifest:                nil,\n    facts:                   {},\n    variables:               {},\n    target_variables:        {},\n    internal_compiler_class: nil,\n    set_local_facts:         true\n  )\n    # Configure the load path\n    env = Puppet.lookup(:pal_env)\n    env.each_plugin_directory do |dir|\n      $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)\n    end\n\n    # Puppet requires Facter, which initializes its lookup paths. Reset Facter to\n    # pickup the new $LOAD_PATH.\n    Puppet.runtime[:facter].reset\n\n    node = Puppet.lookup(:pal_current_node)\n    pal_facts = Puppet.lookup(:pal_facts)\n    pal_variables = Puppet.lookup(:pal_variables)\n\n    overrides = {}\n\n    unless facts.nil? || facts.empty?\n      pal_facts = pal_facts.merge(facts)\n      overrides[:pal_facts] = pal_facts\n    end\n\n    prepare_node_facts(node, pal_facts)\n\n    configured_environment = node.environment || Puppet.lookup(:current_environment)\n\n    apply_environment = manifest ?\n      configured_environment.override_with(:manifest => manifest) :\n      configured_environment\n\n    # Modify the node descriptor to use the special apply_environment.\n    # It is based on the actual environment from the node, or the locally\n    # configured environment if the node does not specify one.\n    # If a manifest file is passed on the command line, it overrides\n    # the :manifest setting of the apply_environment.\n    node.environment = apply_environment\n\n    # TRANSLATORS, the string \"For puppet PAL\" is not user facing\n    Puppet.override({ :current_environment => apply_environment }, \"For puppet PAL\") do\n      node.sanitize()\n      compiler = create_internal_compiler(internal_compiler_class, node)\n\n      case internal_compiler_class\n      when :script\n        pal_compiler = ScriptCompiler.new(compiler)\n        overrides[:pal_script_compiler] = overrides[:pal_compiler] = pal_compiler\n      when :catalog\n        pal_compiler = CatalogCompiler.new(compiler)\n        overrides[:pal_catalog_compiler] = overrides[:pal_compiler] = pal_compiler\n      end\n\n      # When scripting the trusted data are always local; default is to set them anyway\n      # When compiling for a catalog, the catalog compiler does this\n      if set_local_facts\n        compiler.topscope.set_trusted(node.trusted_data)\n\n        # Server facts are always about the local node's version etc.\n        compiler.topscope.set_server_facts(node.server_facts)\n\n        # Set $facts for the node running the script\n        facts_hash = node.facts.nil? ? {} : node.facts.values\n        compiler.topscope.set_facts(facts_hash)\n\n        # create the $settings:: variables\n        compiler.topscope.merge_settings(node.environment.name, false)\n      end\n\n      # Make compiler available to Puppet#lookup and injection in functions\n      # TODO: The compiler instances should be available under non PAL use as well!\n      # TRANSLATORS: Do not translate, symbolic name\n      Puppet.override(overrides, \"PAL::with_#{internal_compiler_class}_compiler\") do\n        compiler.compile do |_compiler_yield|\n          # In case the variables passed to the compiler are PCore types defined in modules, they\n          # need to be deserialized and added from within the this scope, so that loaders are\n          # available during deserizlization.\n          pal_variables = Puppet::Pops::Serialization::FromDataConverter.convert(pal_variables)\n          variables     = Puppet::Pops::Serialization::FromDataConverter.convert(variables)\n\n          # Merge together target variables and plan variables. This will also shadow any\n          # collisions with facts and emit a warning.\n          topscope_vars = pal_variables.merge(merge_vars(target_variables, variables, node.facts.values))\n\n          add_variables(compiler.topscope, topscope_vars)\n          # wrap the internal compiler to prevent it from leaking in the PAL API\n          if block_given?\n            yield(pal_compiler)\n          end\n        end\n      end\n    rescue Puppet::Error\n      # already logged and handled by the compiler, including Puppet::ParseErrorWithIssue\n      raise\n    rescue => detail\n      Puppet.log_exception(detail)\n      raise\n    end\n  end\n  private_class_method :main\n\n  # Warn and remove variables that will be shadowed by facts of the same\n  # name, which are set in scope earlier.\n  def self.merge_vars(target_vars, vars, facts)\n    # First, shadow plan and target variables by facts of the same name\n    vars        = shadow_vars(facts || {}, vars, 'fact', 'plan variable')\n    target_vars = shadow_vars(facts || {}, target_vars, 'fact', 'target variable')\n    # Then, shadow target variables by plan variables of the same name\n    target_vars = shadow_vars(vars, target_vars, 'plan variable', 'target variable')\n\n    target_vars.merge(vars)\n  end\n  private_class_method :merge_vars\n\n  def self.shadow_vars(vars, other_vars, vars_type, other_vars_type)\n    collisions, valid = other_vars.partition do |k, _|\n      vars.include?(k)\n    end\n\n    if collisions.any?\n      names = collisions.map { |k, _| \"$#{k}\" }.join(', ')\n      plural = collisions.length == 1 ? '' : 's'\n\n      Puppet.warning(\n        \"#{other_vars_type.capitalize}#{plural} #{names} will be overridden by \"\\\n        \"#{vars_type}#{plural} of the same name in the apply block\"\n      )\n    end\n\n    valid.to_h\n  end\n  private_class_method :shadow_vars\n\n  def self.create_internal_compiler(compiler_class_reference, node)\n    case compiler_class_reference\n    when :script\n      Puppet::Parser::ScriptCompiler.new(node.environment, node.name)\n    when :catalog\n      Puppet::Parser::CatalogCompiler.new(node)\n    else\n      raise ArgumentError, \"Internal Error: Invalid compiler type requested.\"\n    end\n  end\n\n  T_STRING = Puppet::Pops::Types::PStringType::NON_EMPTY\n  T_STRING_ARRAY = Puppet::Pops::Types::TypeFactory.array_of(T_STRING)\n  T_ANY_ARRAY = Puppet::Pops::Types::TypeFactory.array_of_any\n  T_BOOLEAN = Puppet::Pops::Types::PBooleanType::DEFAULT\n\n  T_GENERIC_TASK_HASH = Puppet::Pops::Types::TypeFactory.hash_kv(\n    Puppet::Pops::Types::TypeFactory.pattern(/\\A[a-z][a-z0-9_]*\\z/), Puppet::Pops::Types::TypeFactory.data\n  )\n\n  def self.assert_type(type, value, what, allow_nil = false)\n    Puppet::Pops::Types::TypeAsserter.assert_instance_of(nil, type, value, allow_nil) { _('Puppet Pal: %{what}') % { what: what } }\n  end\n\n  def self.assert_non_empty_string(s, what, allow_nil = false)\n    assert_type(T_STRING, s, what, allow_nil)\n  end\n\n  def self.assert_optionally_empty_array(a, what, allow_nil = false)\n    assert_type(T_STRING_ARRAY, a, what, allow_nil)\n  end\n  private_class_method :assert_optionally_empty_array\n\n  def self.assert_mutually_exclusive(a, b, a_term, b_term)\n    if a && b\n      raise ArgumentError, _(\"Cannot use '%{a_term}' and '%{b_term}' at the same time\") % { a_term: a_term, b_term: b_term }\n    end\n  end\n  private_class_method :assert_mutually_exclusive\n\n  def self.assert_block_given(block)\n    if block.nil?\n      raise ArgumentError, _(\"A block must be given\")\n    end\n  end\n  private_class_method :assert_block_given\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/plan_signature.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Pal\n  # A PlanSignature is returned from `plan_signature`. Its purpose is to answer questions about the plans's parameters\n  # and if it can be called with a hash of named parameters.\n  #\n  # @api public\n  #\n  class PlanSignature\n    def initialize(plan_function)\n      @plan_func = plan_function\n    end\n\n    # Returns true or false depending on if the given PlanSignature is callable with a set of named arguments or not\n    # In addition to returning the boolean outcome, if a block is given, it is called with a string of formatted\n    # error messages that describes the difference between what was given and what is expected. The error message may\n    # have multiple lines of text, and each line is indented one space.\n    #\n    # @example Checking if signature is acceptable\n    #\n    #   signature = pal.plan_signature('myplan')\n    #   signature.callable_with?({x => 10}) { |errors| raise ArgumentError(\"Ooops: given arguments does not match\\n#{errors}\") }\n    #\n    # @api public\n    #\n    def callable_with?(args_hash)\n      dispatcher = @plan_func.class.dispatcher.dispatchers[0]\n\n      param_scope = {}\n      # Assign all non-nil values, even those that represent non-existent parameters.\n      args_hash.each { |k, v| param_scope[k] = v unless v.nil? }\n      dispatcher.parameters.each do |p|\n        name = p.name\n        arg = args_hash[name]\n        if arg.nil?\n          # Arg either wasn't given, or it was undef\n          if p.value.nil?\n            # No default. Assign nil if the args_hash included it\n            param_scope[name] = nil if args_hash.include?(name)\n          else\n            # parameter does not have a default value, it will be assigned its default when being called\n            # we assume that the default value is of the correct type and therefore simply skip\n            # checking this\n            # param_scope[name] = param_scope.evaluate(name, p.value, closure_scope, @evaluator)\n          end\n        end\n      end\n\n      errors = Puppet::Pops::Types::TypeMismatchDescriber.singleton.describe_struct_signature(dispatcher.params_struct, param_scope).flatten\n      return true if errors.empty?\n\n      if block_given?\n        yield errors.map(&:format).join(\"\\n\")\n      end\n      false\n    end\n\n    # Returns a PStructType describing the parameters as a puppet Struct data type\n    # Note that a `to_s` on the returned structure will result in a human readable Struct datatype as a\n    # description of what a plan expects.\n    #\n    # @return [Puppet::Pops::Types::PStructType] a struct data type describing the parameters and their types\n    #\n    # @api public\n    #\n    def params_type\n      dispatcher = @plan_func.class.dispatcher.dispatchers[0]\n      dispatcher.params_struct\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/script_compiler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Pal\n  class ScriptCompiler < Compiler\n    # Returns the signature of the given plan name\n    # @param plan_name [String] the name of the plan to get the signature of\n    # @return [Puppet::Pal::PlanSignature, nil] returns a PlanSignature, or nil if plan is not found\n    #\n    def plan_signature(plan_name)\n      loader = internal_compiler.loaders.private_environment_loader\n      func = loader.load(:plan, plan_name)\n      if func\n        return PlanSignature.new(func)\n      end\n\n      # Could not find plan\n      nil\n    end\n\n    # Returns an array of TypedName objects for all plans, optionally filtered by a regular expression.\n    # The returned array has more information than just the leaf name - the typical thing is to just get\n    # the name as showing the following example.\n    #\n    # Errors that occur during plan discovery will either be logged as warnings or collected by the optional\n    # `error_collector` array. When provided, it will receive {Puppet::DataTypes::Error} instances describing\n    # each error in detail and no warnings will be logged.\n    #\n    # @example getting the names of all plans\n    #   compiler.list_plans.map {|tn| tn.name }\n    #\n    # @param filter_regex [Regexp] an optional regexp that filters based on name (matching names are included in the result)\n    # @param error_collector [Array<Puppet::DataTypes::Error>] an optional array that will receive errors during load\n    # @return [Array<Puppet::Pops::Loader::TypedName>] an array of typed names\n    #\n    def list_plans(filter_regex = nil, error_collector = nil)\n      list_loadable_kind(:plan, filter_regex, error_collector)\n    end\n\n    # Returns the signature callable of the given task (the arguments it accepts, and the data type it returns)\n    # @param task_name [String] the name of the task to get the signature of\n    # @return [Puppet::Pal::TaskSignature, nil] returns a TaskSignature, or nil if task is not found\n    #\n    def task_signature(task_name)\n      loader = internal_compiler.loaders.private_environment_loader\n      task = loader.load(:task, task_name)\n      if task\n        return TaskSignature.new(task)\n      end\n\n      # Could not find task\n      nil\n    end\n\n    # Returns an array of TypedName objects for all tasks, optionally filtered by a regular expression.\n    # The returned array has more information than just the leaf name - the typical thing is to just get\n    # the name as showing the following example.\n    #\n    # @example getting the names of all tasks\n    #   compiler.list_tasks.map {|tn| tn.name }\n    #\n    # Errors that occur during task discovery will either be logged as warnings or collected by the optional\n    # `error_collector` array. When provided, it will receive {Puppet::DataTypes::Error} instances describing\n    # each error in detail and no warnings will be logged.\n    #\n    # @param filter_regex [Regexp] an optional regexp that filters based on name (matching names are included in the result)\n    # @param error_collector [Array<Puppet::DataTypes::Error>] an optional array that will receive errors during load\n    # @return [Array<Puppet::Pops::Loader::TypedName>] an array of typed names\n    #\n    def list_tasks(filter_regex = nil, error_collector = nil)\n      list_loadable_kind(:task, filter_regex, error_collector)\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pal/task_signature.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\nmodule Pal\n  # A TaskSignature is returned from `task_signature`. Its purpose is to answer questions about the task's parameters\n  # and if it can be run/called with a hash of named parameters.\n  #\n  class TaskSignature\n    def initialize(task)\n      @task = task\n    end\n\n    # Returns whether or not the given arguments are acceptable when running the task.\n    # In addition to returning the boolean outcome, if a block is given, it is called with a string of formatted\n    # error messages that describes the difference between what was given and what is expected. The error message may\n    # have multiple lines of text, and each line is indented one space.\n    #\n    # @param args_hash [Hash] a hash mapping parameter names to argument values\n    # @yieldparam [String] a formatted error message if a type mismatch occurs that explains the mismatch\n    # @return [Boolean] if the given arguments are acceptable when running the task\n    #\n    def runnable_with?(args_hash)\n      params = @task.parameters\n      params_type = if params.nil?\n                      T_GENERIC_TASK_HASH\n                    else\n                      Puppet::Pops::Types::TypeFactory.struct(params)\n                    end\n      return true if params_type.instance?(args_hash)\n\n      if block_given?\n        tm = Puppet::Pops::Types::TypeMismatchDescriber.singleton\n        error = if params.nil?\n                  tm.describe_mismatch('', params_type,\n                                       Puppet::Pops::Types::TypeCalculator\n                                         .infer_set(args_hash))\n                else\n                  tm.describe_struct_signature(params_type, args_hash)\n                    .flatten\n                    .map(&:format)\n                    .join(\"\\n\")\n                end\n        yield \"Task #{@task.name}:\\n#{error}\"\n      end\n      false\n    end\n\n    # Returns the Task instance as a hash\n    #\n    # @return [Hash{String=>Object}] the hash representation of the task\n    def task_hash\n      @task._pcore_init_hash\n    end\n\n    # Returns the Task instance which can be further explored. It contains all meta-data defined for\n    # the task such as the description, parameters, output, etc.\n    #\n    # @return [Puppet::Pops::Types::PuppetObject] An instance of a dynamically created Task class\n    def task\n      @task\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/parameter/boolean.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/coercion'\n\n# This specialized {Puppet::Parameter} handles Boolean options, accepting lots\n# of strings and symbols for both truth and falsehood.\n#\nclass Puppet::Parameter::Boolean < Puppet::Parameter\n  def unsafe_munge(value)\n    Puppet::Coercion.boolean(value)\n  end\n\n  def self.initvars\n    super\n    @value_collection.newvalues(*Puppet::Coercion.boolean_values)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parameter/package_options.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parameter'\n\n# This specialized {Puppet::Parameter} handles munging of package options.\n# Package options are passed as an array of key value pairs. Special munging is\n# required as the keys and values needs to be quoted in a safe way.\n#\nclass Puppet::Parameter::PackageOptions < Puppet::Parameter\n  def unsafe_munge(values)\n    values = [values] unless values.is_a? Array\n\n    values.collect do |val|\n      case val\n      when Hash\n        safe_hash = {}\n        val.each_pair do |k, v|\n          safe_hash[quote(k)] = quote(v)\n        end\n        safe_hash\n      when String\n        quote(val)\n      else\n        fail(_(\"Expected either a string or hash of options\"))\n      end\n    end\n  end\n\n  # @api private\n  def quote(value)\n    value.include?(' ') ? %Q(\"#{value.gsub(/\"/, '\\\"')}\") : value\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parameter/path.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parameter'\n\n# This specialized {Puppet::Parameter} handles validation and munging of paths.\n# By default, a single path is accepted, and by calling {accept_arrays} it is possible to\n# allow an array of paths.\n#\nclass Puppet::Parameter::Path < Puppet::Parameter\n  # Specifies whether multiple paths are accepted or not.\n  # @dsl type\n  #\n  def self.accept_arrays(bool = true)\n    @accept_arrays = !!bool\n  end\n\n  def self.arrays?\n    @accept_arrays\n  end\n\n  # Performs validation of the given paths.\n  # If the concrete parameter defines a validation method, it may call this method to perform\n  # path validation.\n  # @raise [Puppet::Error] if this property is configured for single paths and an array is given\n  # @raise [Puppet::Error] if a path is not an absolute path\n  # @return [Array<String>] the given paths\n  #\n  def validate_path(paths)\n    if paths.is_a?(Array) and !self.class.arrays? then\n      fail _(\"%{name} only accepts a single path, not an array of paths\") % { name: name }\n    end\n\n    fail(_(\"%{name} must be a fully qualified path\") % { name: name }) unless Array(paths).all? { |path| absolute_path?(path) }\n\n    paths\n  end\n\n  # This is the default implementation of the `validate` method.\n  # It will be overridden if the validate option is used when defining the parameter.\n  # @return [void]\n  #\n  def unsafe_validate(paths)\n    validate_path(paths)\n  end\n\n  # This is the default implementation  of `munge`.\n  # If the concrete parameter defines a `munge` method, this default implementation will be overridden.\n  # This default implementation does not perform any munging, it just checks the one/many paths\n  # constraints. A derived implementation can perform this check as:\n  # `paths.is_a?(Array) and ! self.class.arrays?` and raise a {Puppet::Error}.\n  # @param paths [String, Array<String>] one of multiple paths\n  # @return [String, Array<String>] the given paths\n  # @raise [Puppet::Error] if the given paths does not comply with the on/many paths rule.\n  def unsafe_munge(paths)\n    if paths.is_a?(Array) and !self.class.arrays? then\n      fail _(\"%{name} only accepts a single path, not an array of paths\") % { name: name }\n    end\n\n    paths\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parameter/value.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parameter/value_collection'\n\n# Describes an acceptable value for a parameter or property.\n# An acceptable value is either specified as a literal value or a regular expression.\n# @note this class should be used via the api methods in {Puppet::Parameter} and {Puppet::Property}\n# @api private\n#\nclass Puppet::Parameter::Value\n  attr_reader :name, :options, :event\n  attr_accessor :block, :method, :required_features, :invalidate_refreshes\n\n  # Adds an alias for this value.\n  # Makes the given _name_ be an alias for this acceptable value.\n  # @param name [Symbol] the additional alias this value should be known as\n  # @api private\n  #\n  def alias(name)\n    @aliases << convert(name)\n  end\n\n  # @return [Array<Symbol>] Returns all aliases (or an empty array).\n  # @api private\n  #\n  def aliases\n    @aliases.dup\n  end\n\n  # Stores the event that our value generates, if it does so.\n  # @api private\n  #\n  def event=(value)\n    @event = convert(value)\n  end\n\n  # Initializes the instance with a literal accepted value, or a regular expression.\n  # If anything else is passed, it is turned into a String, and then made into a Symbol.\n  # @param name [Symbol, Regexp, Object] the value to accept, Symbol, a regular expression, or object to convert.\n  # @api private\n  #\n  def initialize(name)\n    if name.is_a?(Regexp)\n      @name = name\n    else\n      # Convert to a string and then a symbol, so things like true/false\n      # still show up as symbols.\n      @name = convert(name)\n    end\n\n    @aliases = []\n  end\n\n  # Checks if the given value matches the acceptance rules (literal value, regular expression, or one\n  # of the aliases.\n  # @api private\n  #\n  def match?(value)\n    if regex?\n      true if name =~ value.to_s\n    else\n      (name == convert(value) ? true : @aliases.include?(convert(value)))\n    end\n  end\n\n  # @return [Boolean] whether the accepted value is a regular expression or not.\n  # @api private\n  #\n  def regex?\n    @name.is_a?(Regexp)\n  end\n\n  private\n\n  # A standard way of converting all of our values, so we're always\n  # comparing apples to apples.\n  # @api private\n  #\n  def convert(value)\n    case value\n    when Symbol, '' # can't intern an empty string\n      value\n    when String\n      value.intern\n    when true\n      :true\n    when false\n      :false\n    else\n      value.to_s.intern\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parameter/value_collection.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parameter/value'\n\n# A collection of values and regular expressions, used for specifying allowed values\n# in a given parameter.\n# @note This class is considered part of the internal implementation of {Puppet::Parameter}, and\n#   {Puppet::Property} and the functionality provided by this class should be used via their interfaces.\n# @comment This class probably have several problems when trying to use it with a combination of\n#   regular expressions and aliases as it finds an acceptable value holder vi \"name\" which may be\n#   a regular expression...\n#\n# @api private\n#\nclass Puppet::Parameter::ValueCollection\n  # Aliases the given existing _other_ value with the additional given _name_.\n  # @return [void]\n  # @api private\n  #\n  def aliasvalue(name, other)\n    other = other.to_sym\n    value = match?(other)\n    raise Puppet::DevError, _(\"Cannot alias nonexistent value %{value}\") % { value: other } unless value\n\n    value.alias(name)\n  end\n\n  # Returns a doc string (enumerating the acceptable values) for all of the values in this parameter/property.\n  # @return [String] a documentation string.\n  # @api private\n  #\n  def doc\n    unless defined?(@doc)\n      @doc = ''.dup\n      unless values.empty?\n        @doc << \"Valid values are \"\n        @doc << @strings.collect do |value|\n          aliases = value.aliases\n          if aliases && !aliases.empty?\n            \"`#{value.name}` (also called `#{aliases.join(', ')}`)\"\n          else\n            \"`#{value.name}`\"\n          end\n        end.join(\", \") << \". \"\n      end\n\n      unless regexes.empty?\n        @doc << \"Values can match `#{regexes.join('`, `')}`.\"\n      end\n    end\n\n    @doc\n  end\n\n  # @return [Boolean] Returns whether the set of allowed values is empty or not.\n  # @api private\n  #\n  def empty?\n    @values.empty?\n  end\n\n  # @api private\n  #\n  def initialize\n    # We often look values up by name, so a hash makes more sense.\n    @values = {}\n\n    # However, we want to retain the ability to match values in order,\n    # but we always prefer directly equality (i.e., strings) over regex matches.\n    @regexes = []\n    @strings = []\n  end\n\n  # Checks if the given value is acceptable (matches one of the literal values or patterns) and returns\n  # the \"matcher\" that matched.\n  # Literal string matchers are tested first, if both a literal and a regexp match would match, the literal\n  # match wins.\n  #\n  # @param test_value [Object] the value to test if it complies with the configured rules\n  # @return [Puppet::Parameter::Value, nil] The instance of Puppet::Parameter::Value that matched the given value, or nil if there was no match.\n  # @api private\n  #\n  def match?(test_value)\n    # First look for normal values\n    value = @strings.find { |v| v.match?(test_value) }\n    return value if value\n\n    # Then look for a regex match\n    @regexes.find { |v| v.match?(test_value) }\n  end\n\n  # Munges the value if it is valid, else produces the same value.\n  # @param value [Object] the value to munge\n  # @return [Object] the munged value, or the given value\n  # @todo This method does not seem to do any munging. It just returns the value if it matches the\n  #   regexp, or the (most likely Symbolic) allowed value if it matches (which is more of a replacement\n  #   of one instance with an equal one. Is the intent that this method should be specialized?\n  # @api private\n  #\n  def munge(value)\n    return value if empty?\n\n    instance = match?(value)\n    if instance\n      if instance.regex?\n        value\n      else\n        instance.name\n      end\n    else\n      value\n    end\n  end\n\n  # Defines a new valid value for a {Puppet::Property}.\n  # A valid value is specified as a literal (typically a Symbol), but can also be\n  # specified with a regexp.\n  #\n  # @param name [Symbol, Regexp] a valid literal value, or a regexp that matches a value\n  # @param options [Hash] a hash with options\n  # @option options [Symbol] :event The event that should be emitted when this value is set.\n  # @todo Option :event original comment says \"event should be returned...\", is \"returned\" the correct word\n  #   to use?\n  # @option options [Symbol] :invalidate_refreshes True if a change on this property should invalidate and\n  #   remove any scheduled refreshes (from notify or subscribe) targeted at the same resource. For example, if\n  #   a change in this property takes into account any changes that a scheduled refresh would have performed,\n  #   then the scheduled refresh would be deleted.\n  # @option options [Object] _any_ Any other option is treated as a call to a setter having the given\n  #   option name (e.g. `:required_features` calls `required_features=` with the option's value as an\n  #   argument).\n  # @api private\n  #\n  def newvalue(name, options = {}, &block)\n    call_opt = options[:call]\n    unless call_opt.nil?\n      devfail \"Cannot use obsolete :call value '#{call_opt}' for property '#{self.class.name}'\" unless call_opt == :none || call_opt == :instead\n      # TRANSLATORS ':call' is a property and should not be translated\n      message = _(\"Property option :call is deprecated and no longer used.\")\n      message += ' ' + _(\"Please remove it.\")\n      Puppet.deprecation_warning(message)\n      options = options.reject { |k, _v| k == :call }\n    end\n\n    value = Puppet::Parameter::Value.new(name)\n    @values[value.name] = value\n    if value.regex?\n      @regexes << value\n    else\n      @strings << value\n    end\n\n    options.each { |opt, arg| value.send(opt.to_s + \"=\", arg) }\n    if block_given?\n      devfail \"Cannot use :call value ':none' in combination with a block for property '#{self.class.name}'\" if call_opt == :none\n      value.block = block\n      value.method ||= \"set_#{value.name}\" unless value.regex?\n    elsif call_opt == :instead\n      devfail \"Cannot use :call value ':instead' without a block for property '#{self.class.name}'\"\n    end\n    value\n  end\n\n  # Defines one or more valid values (literal or regexp) for a parameter or property.\n  # @return [void]\n  # @dsl type\n  # @api private\n  #\n  def newvalues(*names)\n    names.each { |name| newvalue(name) }\n  end\n\n  # @return [Array<String>] An array of the regular expressions in string form, configured as matching valid values.\n  # @api private\n  #\n  def regexes\n    @regexes.collect { |r| r.name.inspect }\n  end\n\n  # Validates the given value against the set of valid literal values and regular expressions.\n  # @raise [ArgumentError] if the value is not accepted\n  # @return [void]\n  # @api private\n  #\n  def validate(value)\n    return if empty?\n\n    unless @values.detect { |_name, v| v.match?(value) }\n      str = _(\"Invalid value %{value}.\") % { value: value.inspect }\n      str += \" \" + _(\"Valid values are %{value_list}.\") % { value_list: values.join(\", \") } unless values.empty?\n      str += \" \" + _(\"Valid values match %{pattern}.\") % { pattern: regexes.join(\", \") } unless regexes.empty?\n      raise ArgumentError, str\n    end\n  end\n\n  # Returns a valid value matcher (a literal or regular expression)\n  # @todo This looks odd, asking for an instance that matches a symbol, or an instance that has\n  #   a regexp. What is the intention here? Marking as api private...\n  #\n  # @return [Puppet::Parameter::Value] a valid value matcher\n  # @api private\n  #\n  def value(name)\n    @values[name]\n  end\n\n  # @return [Array<Symbol>] Returns a list of valid literal values.\n  # @see regexes\n  # @api private\n  #\n  def values\n    @strings.collect(&:name)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parameter.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/util/logging'\nrequire_relative '../puppet/util/docs'\n\n# The Parameter class is the implementation of a resource's attributes of _parameter_ kind.\n# The Parameter class is also the base class for {Puppet::Property}, and is used to describe meta-parameters\n# (parameters that apply to all resource types).\n# A Parameter (in contrast to a Property) has a single value where a property has both a current and a wanted value.\n# The Parameter class methods are used to configure and create an instance of Parameter that represents\n# one particular attribute data type; its valid value(s), and conversion to/from internal form.\n#\n# The intention is that a new parameter is created by using the DSL method {Puppet::Type.newparam}, or\n# {Puppet::Type.newmetaparam} if the parameter should be applicable to all resource types.\n#\n# A Parameter that does not specify and valid values (via {newvalues}) accepts any value.\n#\n# @see Puppet::Type\n# @see Puppet::Property\n# @api public\n#\nclass Puppet::Parameter\n  include Puppet::Util\n  include Puppet::Util::Errors\n  include Puppet::Util::Logging\n\n  require_relative 'parameter/value_collection'\n\n  class << self\n    include Puppet::Util\n    include Puppet::Util::Docs\n\n    # @return [Symbol] The parameter name as given when it was created.\n    attr_reader :name\n\n    # @return [Object] The default value of the parameter as determined by the {defaultto} method, or nil if no\n    #  default has been set.\n    attr_reader :default\n\n    # @comment This somewhat odd documentation construct is because the getter and setter are not\n    #  orthogonal; the setter uses varargs and this confuses yard. To overcome the problem both the\n    #  getter and the setter are documented here. If this issues is fixed, a todo will be displayed\n    #  for the setter method, and the setter documentation can be moved there.\n    #  Since the attribute is actually RW it should  perhaps instead just be implemented as a setter\n    #  and a getter method (and no attr_xxx declaration).\n    #\n    # @!attribute [rw] required_features\n    # @return [Array<Symbol>] The names of the _provider features_ required for this parameter to work.\n    #   the returned names are always all lower case symbols.\n    # @overload required_features\n    #   Returns the required _provider features_ as an array of lower case symbols\n    # @overload required_features=(*args)\n    #   @param *args [Symbol] one or more names of required provider features\n    #   Sets the required_provider_features_ from one or more values, or array. The given arguments\n    #   are flattened, and internalized.\n    # @api public\n    # @dsl type\n    #\n    attr_reader :required_features\n\n    # @return [Puppet::Parameter::ValueCollection] The set of valid values (or an empty set that accepts any value).\n    # @api private\n    #\n    attr_reader :value_collection\n\n    # @return [Boolean] Flag indicating whether this parameter is a meta-parameter or not.\n    attr_accessor :metaparam\n\n    # Defines how the `default` value of a parameter is computed.\n    # The computation of the parameter's default value is defined by providing a value or a block.\n    # A default of `nil` can not be used.\n    # @overload defaultto(value)\n    #   Defines the default value with a literal value\n    #   @param value [Object] the literal value to use as the default value\n    # @overload defaultto({ ... })\n    #   Defines that the default value is produced by the given block. The given block\n    #   should produce the default value.\n    # @raise [Puppet::DevError] if value is nil, and no block is given.\n    # @return [void]\n    # @see Parameter.default\n    # @dsl type\n    # @api public\n    #\n    def defaultto(value = nil, &block)\n      if block\n        define_method(:default, &block)\n      else\n        if value.nil?\n          raise Puppet::DevError,\n                \"Either a default value or block must be provided\"\n        end\n        define_method(:default) do value end\n      end\n    end\n\n    # rubocop:disable Naming/PredicateName\n    def sensitive(value = nil, &block)\n      if block\n        define_method(:is_sensitive, &block)\n      else\n        define_method(:is_sensitive) do value end\n      end\n    end\n    # rubocop:enable Naming/PredicateName\n\n    # Produces a documentation string.\n    # If an enumeration of _valid values_ has been defined, it is appended to the documentation\n    # for this parameter specified with the {desc} method.\n    # @return [String] Returns a documentation string.\n    # @api public\n    #\n    def doc\n      @doc ||= \"\"\n\n      unless defined?(@addeddocvals)\n        @doc = Puppet::Util::Docs.scrub(@doc)\n        vals = value_collection.doc\n        if vals\n          @doc << \"\\n\\n#{vals}\"\n        end\n\n        features = required_features\n        if features\n          @doc << \"\\n\\nRequires features #{features.flatten.collect(&:to_s).join(' ')}.\"\n        end\n        @addeddocvals = true\n      end\n\n      @doc\n    end\n\n    # Removes the `default` method if defined.\n    # Has no effect if the default method is not defined.\n    # This method is intended to be used in a DSL scenario where a parameter inherits from a parameter\n    # with a default value that is not wanted in the derived parameter (otherwise, simply do not define\n    # a default value method).\n    #\n    # @return [void]\n    # @see desc\n    # @api public\n    # @dsl type\n    #\n    def nodefault\n      undef_method :default if public_method_defined? :default\n    end\n\n    # Sets the documentation for this parameter.\n    # @param str [String] The documentation string to set\n    # @return [String] the given `str` parameter\n    # @see doc\n    # @dsl type\n    # @api public\n    #\n    def desc(str)\n      @doc = str\n    end\n\n    # Initializes the instance variables.\n    # Clears the internal value collection (set of allowed values).\n    # @return [void]\n    # @api private\n    #\n    def initvars\n      @value_collection = ValueCollection.new\n    end\n\n    # @overload munge { ... }\n    # Defines an optional method used to convert the parameter value from DSL/string form to an internal form.\n    # If a munge method is not defined, the DSL/string value is used as is.\n    # @note This adds a method with the name `unsafe_munge` in the created parameter class. Later this method is\n    #   called in a context where exceptions will be rescued and handled.\n    # @dsl type\n    # @api public\n    #\n    def munge(&block)\n      # I need to wrap the unsafe version in begin/rescue parameterments,\n      # but if I directly call the block then it gets bound to the\n      # class's context, not the instance's, thus the two methods,\n      # instead of just one.\n      define_method(:unsafe_munge, &block)\n    end\n\n    # @overload unmunge { ... }\n    # Defines an optional method used to convert the parameter value from internal form to DSL/string form.\n    # If an `unmunge` method is not defined, the internal form is used.\n    # @see munge\n    # @note This adds a method with the name `unsafe_unmunge` in the created parameter class.\n    # @dsl type\n    # @api public\n    #\n    def unmunge(&block)\n      define_method(:unsafe_unmunge, &block)\n    end\n\n    # Sets a marker indicating that this parameter is the _namevar_ (unique identifier) of the type\n    # where the parameter is contained.\n    # This also makes the parameter a required value. The marker can not be unset once it has been set.\n    # @return [void]\n    # @dsl type\n    # @api public\n    #\n    def isnamevar\n      @isnamevar = true\n      @required = true\n    end\n\n    # @return [Boolean] Returns whether this parameter is the _namevar_ or not.\n    # @api public\n    #\n    def isnamevar?\n      @isnamevar\n    end\n\n    # Sets a marker indicating that this parameter is required.\n    # Once set, it is not possible to make a parameter optional.\n    # @return [void]\n    # @dsl type\n    # @api public\n    #\n    def isrequired\n      @required = true\n    end\n\n    # @comment This method is not picked up by yard as it has a different signature than\n    #   expected for an attribute (varargs). Instead, this method is documented as an overload\n    #   of the attribute required_features. (Not ideal, but better than nothing).\n    # @todo If this text appears in documentation - see comment in source and makes corrections - it means\n    #   that an issue in yardoc has been fixed.\n    #\n    def required_features=(*args)\n      @required_features = args.flatten.collect { |a| a.to_s.downcase.intern }\n    end\n\n    # Returns whether this parameter is required or not.\n    # A parameter is required if a call has been made to the DSL method {isrequired}.\n    # @return [Boolean] Returns whether this parameter is required or not.\n    # @api public\n    #\n    def required?\n      @required\n    end\n\n    # @overload validate { ... }\n    # Defines an optional method that is used to validate the parameter's DSL/string value.\n    # Validation should raise appropriate exceptions, the return value of the given block is ignored.\n    # The easiest way to raise an appropriate exception is to call the method {Puppet::Util::Errors.fail} with\n    # the message as an argument.\n    # To validate the munged value instead, just munge the value (`munge(value)`).\n    #\n    # @return [void]\n    # @dsl type\n    # @api public\n    #\n    def validate(&block)\n      define_method(:unsafe_validate, &block)\n    end\n\n    # Defines valid values for the parameter (enumeration or regular expressions).\n    # The set of valid values for the parameter can be limited to a (mix of) literal values and\n    # regular expression patterns.\n    # @note Each call to this method adds to the set of valid values\n    # @param names [Symbol, Regexp] The set of valid literal values and/or patterns for the parameter.\n    # @return [void]\n    # @dsl type\n    # @api public\n    #\n    def newvalues(*names)\n      @value_collection.newvalues(*names)\n    end\n\n    # Makes the given `name` an alias for the given `other` name.\n    # Or said differently, the valid value `other` can now also be referred to via the given `name`.\n    # Aliasing may affect how the parameter's value is serialized/stored (it may store the `other` value\n    # instead of the alias).\n    # @api public\n    # @dsl type\n    #\n    def aliasvalue(name, other)\n      @value_collection.aliasvalue(name, other)\n    end\n  end\n\n  # Creates instance (proxy) methods that delegates to a class method with the same name.\n  # @api private\n  #\n  def self.proxymethods(*values)\n    values.each { |val|\n      define_method(val) do\n        self.class.send(val)\n      end\n    }\n  end\n\n  # @!method required?\n  #   (see required?)\n  # @!method isnamevar?\n  #   (see isnamevar?)\n  #\n  proxymethods(\"required?\", \"isnamevar?\")\n\n  # @return [Puppet::Resource] A reference to the resource this parameter is an attribute of (the _associated resource_).\n  attr_accessor :resource\n\n  # @comment LAK 2007-05-09: Keep the @parent around for backward compatibility.\n  # @return [Puppet::Parameter] A reference to the parameter's parent kept for backwards compatibility.\n  # @api private\n  #\n  attr_accessor :parent\n\n  # @!attribute [rw] sensitive\n  #   @return [true, false] If this parameter has been tagged as sensitive.\n  attr_accessor :sensitive\n\n  # Returns a string representation of the resource's containment path in\n  # the catalog.\n  # @return [String]\n  def path\n    @path ||= '/' + pathbuilder.join('/')\n  end\n\n  # @return [Integer] Returns the result of calling the same method on the associated resource.\n  def line\n    resource.line\n  end\n\n  # @return [Integer] Returns the result of calling the same method on the associated resource.\n  def file\n    resource.file\n  end\n\n  # @return [Integer] Returns the result of calling the same method on the associated resource.\n  def version\n    resource.version\n  end\n\n  # Initializes the parameter with a required resource reference and optional attribute settings.\n  # The option `:resource` must be specified or an exception is raised. Any additional options passed\n  # are used to initialize the attributes of this parameter by treating each key in the `options` hash as\n  # the name of the attribute to set, and the value as the value to set.\n  # @param options [Hash{Symbol => Object]] Options, where `resource` is required\n  # @option options [Puppet::Resource] :resource The resource this parameter holds a value for. Required.\n  # @raise [Puppet::DevError] If resource is not specified in the options hash.\n  # @api public\n  # @note A parameter should be created via the DSL method {Puppet::Type::newparam}\n  #\n  def initialize(resource: nil, value: nil, should: nil)\n    if resource\n      self.resource = resource\n    else\n      raise Puppet::DevError, _(\"No resource set for %{name}\") % { name: self.class.name }\n    end\n\n    self.value = value if value\n    self.should = should if should\n  end\n\n  # Writes the given `msg` to the log with the loglevel indicated by the associated resource's\n  # `loglevel` parameter.\n  # @todo is loglevel a metaparameter? it is looked up with `resource[:loglevel]`\n  # @return [void]\n  # @api public\n  def log(msg)\n    send_log(resource[:loglevel], msg)\n  end\n\n  # @return [Boolean] Returns whether this parameter is a meta-parameter or not.\n  def metaparam?\n    self.class.metaparam\n  end\n\n  # @!attribute [r] name\n  # @return [Symbol] The parameter's name as given when it was created.\n  # @note Since a Parameter defines the name at the class level, each Parameter class must be\n  #  unique within a type's inheritance chain.\n  # @comment each parameter class must define the name method, and parameter\n  #   instances do not change that name this implicitly means that a given\n  #   object can only have one parameter instance of a given parameter\n  #   class\n  def name\n    self.class.name\n  end\n\n  # @return [Boolean] Returns true if this parameter, the associated resource, or overall puppet mode is `noop`.\n  # @todo How is noop mode set for a parameter? Is this of value in DSL to inhibit a parameter?\n  #\n  def noop\n    @noop ||= false\n    @noop || resource.noop || Puppet[:noop] || false\n  end\n\n  # Returns an array of strings representing the containment hierarchy\n  # (types/classes) that make up the path to the resource from the root\n  # of the catalog.  This is mostly used for logging purposes.\n  #\n  # @api private\n  def pathbuilder\n    if @resource\n      [@resource.pathbuilder, name]\n    else\n      [name]\n    end\n  end\n\n  # This is the default implementation of `munge` that simply produces the value (if it is valid).\n  # The DSL method {munge} should be used to define an overriding method if munging is required.\n  #\n  # @api private\n  #\n  def unsafe_munge(value)\n    self.class.value_collection.munge(value)\n  end\n\n  # Unmunges the value by transforming it from internal form to DSL form.\n  # This is the default implementation of `unmunge` that simply returns the value without processing.\n  # The DSL method {unmunge} should be used to define an overriding method if required.\n  # @return [Object] the unmunged value\n  #\n  def unmunge(value)\n    return value if value.is_a?(Puppet::Pops::Evaluator::DeferredValue)\n\n    unsafe_unmunge(value)\n  end\n\n  # This is the default implementation of `unmunge` that simply produces the value (if it is valid).\n  # The DSL method {unmunge} should be used to define an overriding method if unmunging is required.\n  #\n  # @api private\n  #\n  def unsafe_unmunge(value)\n    value\n  end\n\n  # Munges the value from DSL form to internal form.\n  # This implementation of `munge` provides exception handling around the specified munging of this parameter.\n  # @note This method should not be overridden. Use the DSL method {munge} to define a munging method\n  #   if required.\n  # @param value [Object] the DSL value to munge\n  # @return [Object] the munged (internal) value\n  #\n  def munge(value)\n    return value if value.is_a?(Puppet::Pops::Evaluator::DeferredValue)\n\n    begin\n      ret = unsafe_munge(value)\n    rescue Puppet::Error => detail\n      Puppet.debug { \"Reraising #{detail}\" }\n      raise\n    rescue => detail\n      raise Puppet::DevError, _(\"Munging failed for value %{value} in class %{class_name}: %{detail}\") % { value: value.inspect, class_name: name, detail: detail }, detail.backtrace\n    end\n    ret\n  end\n\n  # This is the default implementation of `validate` that may be overridden by the DSL method {validate}.\n  # If no valid values have been defined, the given value is accepted, else it is validated against\n  # the literal values (enumerator) and/or patterns defined by calling {newvalues}.\n  #\n  # @param value [Object] the value to check for validity\n  # @raise [ArgumentError] if the value is not valid\n  # @return [void]\n  # @api private\n  #\n  def unsafe_validate(value)\n    self.class.value_collection.validate(value)\n  end\n\n  # Performs validation of the given value against the rules defined by this parameter.\n  # @return [void]\n  # @todo Better description of when the various exceptions are raised.ArgumentError is rescued and\n  #   changed into Puppet::Error.\n  # @raise [ArgumentError, TypeError, Puppet::DevError, Puppet::Error] under various conditions\n  # A protected validation method that only ever raises useful exceptions.\n  # @api public\n  #\n  def validate(value)\n    return if value.is_a?(Puppet::Pops::Evaluator::DeferredValue)\n\n    begin\n      unsafe_validate(value)\n    rescue ArgumentError => detail\n      self.fail Puppet::Error, detail.to_s, detail\n    rescue Puppet::Error, TypeError\n      raise\n    rescue => detail\n      raise Puppet::DevError, _(\"Validate method failed for class %{class_name}: %{detail}\") % { class_name: name, detail: detail }, detail.backtrace\n    end\n  end\n\n  # Sets the associated resource to nil.\n  # @todo Why - what is the intent/purpose of this?\n  # @return [nil]\n  #\n  def remove\n    @resource = nil\n  end\n\n  # @return [Object] Gets the value of this parameter after performing any specified unmunging.\n  def value\n    unmunge(@value) unless @value.nil?\n  end\n\n  # Sets the given value as the value of this parameter.\n  # @todo This original comment _\"All of the checking should possibly be\n  #   late-binding (e.g., users might not exist when the value is assigned\n  #   but might when it is asked for).\"_ does not seem to be correct, the implementation\n  #   calls both validate and munge on the given value, so no late binding.\n  #\n  # The given value is validated and then munged (if munging has been specified). The result is store\n  # as the value of this parameter.\n  # @return [Object] The given `value` after munging.\n  # @raise (see #validate)\n  #\n  def value=(value)\n    validate(value)\n\n    @value = munge(value)\n  end\n\n  # @return [Puppet::Provider] Returns the provider of the associated resource.\n  # @todo The original comment says = _\"Retrieve the resource's provider.\n  #   Some types don't have providers, in which case we return the resource object itself.\"_\n  #   This does not seem to be true, the default implementation that sets this value may be\n  #   {Puppet::Type.provider=} which always gets either the name of a provider or an instance of one.\n  #\n  def provider\n    @resource.provider\n  end\n\n  # @return [Array<Symbol>] Returns an array of the associated resource's symbolic tags (including the parameter itself).\n  # Returns an array of the associated resource's symbolic tags (including the parameter itself).\n  # At a minimum, the array contains the name of the parameter. If the associated resource\n  # has tags, these tags are also included in the array.\n  # @todo The original comment says = _\"The properties need to return tags so that logs correctly\n  #   collect them.\"_ what if anything of that is of interest to document. Should tags and their relationship\n  #   to logs be described. This is a more general concept.\n  #\n  def tags\n    unless defined?(@tags)\n      @tags = []\n      # This might not be true in testing\n      @tags = @resource.tags if @resource.respond_to? :tags\n      @tags << name.to_s\n    end\n    @tags\n  end\n\n  # @return [String] The name of the parameter in string form.\n  def to_s\n    name.to_s\n  end\n\n  # Formats the given string and conditionally redacts the provided interpolation variables, depending on if\n  # this property is sensitive.\n  #\n  # @note Because the default implementation of Puppet::Property#is_to_s returns the current value as-is, it\n  #   doesn't necessarily return a string. For the sake of sanity we just cast everything to a string for\n  #   interpolation so we don't introduce issues with unexpected property values.\n  #\n  # @see String#format\n  # @param fmt [String] The format string to interpolate.\n  # @param args [Array<String>] One or more strings to conditionally redact and interpolate into the format string.\n  #\n  # @return [String]\n  def format(fmt, *args)\n    fmt % args.map { |arg| @sensitive ? \"[redacted]\" : arg.to_s }\n  end\n\n  # Produces a String with the value formatted for display to a human.\n  #\n  # The output is created using the StringConverter with format '%#p' to produce\n  # human readable code that is understood by puppet.\n  #\n  # @return [String] The formatted value in string form.\n  #\n  def self.format_value_for_display(value)\n    Puppet::Pops::Types::StringConverter.convert(value, Puppet::Pops::Types::StringConverter::DEFAULT_PARAMETER_FORMAT)\n  end\n\n  # @comment Document post_compile_hook here as it does not exist anywhere (called from type if implemented)\n  # @!method post_compile()\n  # @since 3.4.0\n  # @api public\n  #   @abstract A subclass may implement this - it is not implemented in the Parameter class\n  #   This method may be implemented by a parameter in order to perform actions during compilation\n  #   after all resources have been added to the catalog.\n  #   @see Puppet::Type#finish\n  #   @see Puppet::Parser::Compiler#finish\nend\n\nrequire_relative 'parameter/path'\n"
  },
  {
    "path": "lib/puppet/parser/abstract_compiler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Parser::AbstractCompiler\n  # Returns the catalog for a compilation. Must return a Puppet::Resource::Catalog or fail with an\n  # error if the specific compiler does not support catalog operations.\n  #\n  def catalog\n    raise Puppet::DevError(\"Class '#{self.class}' should have implemented 'catalog'\")\n  end\n\n  # Returns the environment for the compilation\n  #\n  def environment\n    raise Puppet::DevError(\"Class '#{self.class}' should have implemented 'environment'\")\n  end\n\n  # Produces a new scope\n  # This method is here if there are functions/logic that will call this for some other purpose than to create\n  # a named scope for a class. It may not have to be here. (TODO)\n  #\n  def newscope(scope, options)\n    raise Puppet::DevError(\"Class '#{self.class}' should have implemented 'newscope'\")\n  end\n\n  # Returns a hash of all externally referenceable qualified variables\n  #\n  def qualified_variables\n    raise Puppet::DevError(\"Class '#{self.class}' should have implemented 'qualified_variables'\")\n  end\n\n  # Returns the top scope instance\n  def topscope\n    raise Puppet::DevError(\"Class '#{self.class}' should have implemented 'topscope'\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/block_expression.rb",
    "content": "# frozen_string_literal: true\n\n# Evaluates contained expressions, produce result of the last\n#\nclass Puppet::Parser::AST::BlockExpression < Puppet::Parser::AST::Branch\n  def evaluate(scope)\n    @children.reduce(nil) { |_, child| child.safeevaluate(scope) }\n  end\n\n  def sequence_with(other)\n    Puppet::Parser::AST::BlockExpression.new(:children => children + other.children)\n  end\n\n  def to_s\n    \"[\" + @children.collect(&:to_s).join(', ') + \"]\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/branch.rb",
    "content": "# frozen_string_literal: true\n\n# The parent class of all AST objects that contain other AST objects.\n# Everything but the really simple objects descend from this.  It is\n# important to note that Branch objects contain other AST objects only --\n# if you want to contain values, use a descendant of the AST::Leaf class.\n#\n# @api private\nclass Puppet::Parser::AST::Branch < Puppet::Parser::AST\n  include Enumerable\n  attr_accessor :pin, :children\n\n  def each\n    @children.each { |child| yield child }\n  end\n\n  def initialize(children: [], **args)\n    @children = children\n    super(**args)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/hostclass.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/parser/ast/top_level_construct'\n\nclass Puppet::Parser::AST::Hostclass < Puppet::Parser::AST::TopLevelConstruct\n  attr_accessor :name, :context\n\n  def initialize(name, context = {})\n    @context = context\n    @name = name\n  end\n\n  def instantiate(modname)\n    new_class = Puppet::Resource::Type.new(:hostclass, @name, @context.merge(:module_name => modname))\n    all_types = [new_class]\n    if code\n      code.each do |nested_ast_node|\n        if nested_ast_node.respond_to? :instantiate\n          all_types += nested_ast_node.instantiate(modname)\n        end\n      end\n    end\n    all_types\n  end\n\n  def code\n    @context[:code]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/leaf.rb",
    "content": "# frozen_string_literal: true\n\n# The base class for all of the leaves of the parse trees.  These\n# basically just have types and values.  Both of these parameters\n# are simple values, not AST objects.\n#\nclass Puppet::Parser::AST::Leaf < Puppet::Parser::AST\n  attr_accessor :value, :type\n\n  # Return our value.\n  def evaluate(scope)\n    @value\n  end\n\n  def match(value)\n    @value == value\n  end\n\n  def to_s\n    @value.to_s unless @value.nil?\n  end\n\n  def initialize(value: nil, file: nil, line: nil, pos: nil)\n    @value = value\n    super(file: file, line: line, pos: pos)\n  end\nend\n\n# Host names, either fully qualified or just the short name, or even a regex\n#\nclass Puppet::Parser::AST::HostName < Puppet::Parser::AST::Leaf\n  def initialize(value: nil, file: nil, line: nil, pos: nil)\n    super(value: value, file: file, line: line, pos: pos)\n\n    # Note that this is an AST::Regex, not a Regexp\n    unless @value.is_a?(Regex)\n      @value = @value.to_s.downcase\n      if @value =~ /[^-\\w.]/\n        raise Puppet::DevError, _(\"'%{value}' is not a valid hostname\") % { value: @value }\n      end\n    end\n  end\n\n  # implementing eql? and hash so that when an HostName is stored\n  # in a hash it has the same hashing properties as the underlying value\n  def eql?(value)\n    @value.eql?(value.is_a?(HostName) ? value.value : value)\n  end\n\n  def hash\n    @value.hash\n  end\nend\n\nclass Puppet::Parser::AST::Regex < Puppet::Parser::AST::Leaf\n  def initialize(value: nil, file: nil, line: nil, pos: nil)\n    super(value: value, file: file, line: line, pos: pos)\n\n    # transform value from hash options unless it is already a regular expression\n    @value = Regexp.new(@value) unless @value.is_a?(Regexp)\n  end\n\n  # we're returning self here to wrap the regexp and to be used in places\n  # where a string would have been used, without modifying any client code.\n  # For instance, in many places we have the following code snippet:\n  #  val = @val.safeevaluate(@scope)\n  #  if val.match(otherval)\n  #      ...\n  #  end\n  # this way, we don't have to modify this test specifically for handling\n  # regexes.\n  #\n  def evaluate(scope)\n    self\n  end\n\n  def match(value)\n    @value.match(value)\n  end\n\n  def to_s\n    Puppet::Pops::Types::PRegexpType.regexp_to_s_with_delimiters(@value)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/node.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Parser::AST::Node < Puppet::Parser::AST::TopLevelConstruct\n  attr_accessor :names, :context\n\n  def initialize(names, context = {})\n    raise ArgumentError, _(\"names should be an array\") unless names.is_a? Array\n    if context[:parent]\n      raise Puppet::DevError, _(\"Node inheritance is removed in Puppet 4.0.0. See http://links.puppet.com/puppet-node-inheritance-deprecation\")\n    end\n\n    @names = names\n    @context = context\n  end\n\n  def instantiate(modname)\n    @names.map { |name| Puppet::Resource::Type.new(:node, name, @context.merge(:module_name => modname)) }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/pops_bridge.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/parser/ast/top_level_construct'\nrequire_relative '../../../puppet/pops'\n\n# The AST::Bridge contains classes that bridges between the new Pops based model\n# and the 3.x AST. This is required to be able to reuse the Puppet::Resource::Type which is\n# fundamental for the rest of the logic.\n#\nclass Puppet::Parser::AST::PopsBridge\n  # Bridges to one Pops Model Expression\n  # The @value is the expression\n  # This is used to represent the body of a class, definition, or node, and for each parameter's default value\n  # expression.\n  #\n  class Expression < Puppet::Parser::AST::Leaf\n    def to_s\n      Puppet::Pops::Model::ModelTreeDumper.new.dump(@value)\n    end\n\n    def source_text\n      source_adapter = Puppet::Pops::Utils.find_closest_positioned(@value)\n      source_adapter ? source_adapter.extract_text() : nil\n    end\n\n    def evaluate(scope)\n      evaluator = Puppet::Pops::Parser::EvaluatingParser.singleton\n      object = evaluator.evaluate(scope, @value)\n      evaluator.convert_to_3x(object, scope)\n    end\n\n    # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this\n    # by yielding self. By adding this there is no need to wrap a pops expression inside an AST::BlockExpression\n    #\n    def each\n      yield self\n    end\n\n    def sequence_with(other)\n      if value.nil?\n        # This happens when testing and not having a complete setup\n        other\n      else\n        # When does this happen ? Ever ?\n        raise \"sequence_with called on Puppet::Parser::AST::PopsBridge::Expression - please report use case\"\n        # What should be done if the above happens (We don't want this to happen).\n        # Puppet::Parser::AST::BlockExpression.new(:children => [self] + other.children)\n      end\n    end\n\n    # The 3x requires code plugged in to an AST to have this in certain positions in the tree. The purpose\n    # is to either print the content, or to look for things that needs to be defined. This implementation\n    # cheats by always returning an empty array. (This allows simple files to not require a \"Program\" at the top.\n    #\n    def children\n      []\n    end\n  end\n\n  class ExpressionSupportingReturn < Expression\n    def evaluate(scope)\n      catch(:return) do\n        return catch(:next) do\n          return super(scope)\n        end\n      end\n    end\n  end\n\n  # Bridges the top level \"Program\" produced by the pops parser.\n  # Its main purpose is to give one point where all definitions are instantiated (actually defined since the\n  # Puppet 3x terminology is somewhat misleading - the definitions are instantiated, but instances of the created types\n  # are not created, that happens when classes are included / required, nodes are matched and when resources are instantiated\n  # by a resource expression (which is also used to instantiate a host class).\n  #\n  class Program < Puppet::Parser::AST::TopLevelConstruct\n    attr_reader :program_model, :context\n\n    def initialize(program_model, context = {})\n      @program_model = program_model\n      @context = context\n      @ast_transformer ||= Puppet::Pops::Model::AstTransformer.new(@context[:file])\n    end\n\n    # This is the 3x API, the 3x AST searches through all code to find the instructions that can be instantiated.\n    # This Pops-model based instantiation relies on the parser to build this list while parsing (which is more\n    # efficient as it avoids one full scan of all logic via recursive enumeration/yield)\n    #\n    def instantiate(modname)\n      @program_model.definitions.map do |d|\n        case d\n        when Puppet::Pops::Model::HostClassDefinition\n          instantiate_HostClassDefinition(d, modname)\n        when Puppet::Pops::Model::ResourceTypeDefinition\n          instantiate_ResourceTypeDefinition(d, modname)\n        when Puppet::Pops::Model::NodeDefinition\n          instantiate_NodeDefinition(d, modname)\n        else\n          loaders = Puppet::Pops::Loaders.loaders\n          loaders.instantiate_definition(d, loaders.find_loader(modname))\n\n          # The 3x logic calling this will not know what to do with the result, it is compacted away at the end\n          nil\n        end\n      end.flatten().compact() # flatten since node definition may have returned an array\n      # Compact since 4x definitions are not understood by compiler\n    end\n\n    def evaluate(scope)\n      Puppet::Pops::Parser::EvaluatingParser.singleton.evaluate(scope, program_model)\n    end\n\n    # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this\n    # by yielding self. This means that the HostClass container will call this bridge instance with `instantiate`.\n    #\n    def each\n      yield self\n    end\n\n    # Returns true if this Program only contains definitions\n    def is_definitions_only?\n      is_definition?(program_model)\n    end\n\n    private\n\n    def is_definition?(o)\n      case o\n      when Puppet::Pops::Model::Program\n        is_definition?(o.body)\n      when Puppet::Pops::Model::BlockExpression\n        o.statements.all { |s| is_definition?(s) }\n      when Puppet::Pops::Model::Definition\n        true\n      else\n        false\n      end\n    end\n\n    def instantiate_Parameter(o)\n      # 3x needs parameters as an array of `[name]` or `[name, value_expr]`\n      if o.value\n        [o.name, Expression.new(:value => o.value)]\n      else\n        [o.name]\n      end\n    end\n\n    def create_type_map(definition)\n      result = {}\n      # No need to do anything if there are no parameters\n      return result unless definition.parameters.size > 0\n\n      # No need to do anything if there are no typed parameters\n      typed_parameters = definition.parameters.select(&:type_expr)\n      return result if typed_parameters.empty?\n\n      # If there are typed parameters, they need to be evaluated to produce the corresponding type\n      # instances. This evaluation requires a scope. A scope is not available when doing deserialization\n      # (there is also no initialized evaluator). When running apply and test however, the environment is\n      # reused and we may reenter without a scope (which is fine). A debug message is then output in case\n      # there is the need to track down the odd corner case. See {#obtain_scope}.\n      #\n      scope = obtain_scope\n      if scope\n        evaluator = Puppet::Pops::Parser::EvaluatingParser.singleton\n        typed_parameters.each do |p|\n          result[p.name] = evaluator.evaluate(scope, p.type_expr)\n        end\n      end\n      result\n    end\n\n    # Obtains the scope or issues a warning if :global_scope is not bound\n    def obtain_scope\n      Puppet.lookup(:global_scope) do\n        # This occurs when testing and when applying a catalog (there is no scope available then), and\n        # when running tests that run a partial setup.\n        # This is bad if the logic is trying to compile, but a warning can not be issues since it is a normal\n        # use case that there is no scope when requesting the type in order to just get the parameters.\n        Puppet.debug { _(\"Instantiating Resource with type checked parameters - scope is missing, skipping type checking.\") }\n        nil\n      end\n    end\n\n    # Produces a hash with data for Definition and HostClass\n    def args_from_definition(o, modname, expr_class = Expression)\n      args = {\n        :arguments => o.parameters.collect { |p| instantiate_Parameter(p) },\n        :argument_types => create_type_map(o),\n        :module_name => modname\n      }\n      unless is_nop?(o.body)\n        args[:code] = expr_class.new(:value => o.body)\n      end\n      @ast_transformer.merge_location(args, o)\n    end\n\n    def instantiate_HostClassDefinition(o, modname)\n      args = args_from_definition(o, modname, ExpressionSupportingReturn)\n      args[:parent] = absolute_reference(o.parent_class)\n      Puppet::Resource::Type.new(:hostclass, o.name, @context.merge(args))\n    end\n\n    def instantiate_ResourceTypeDefinition(o, modname)\n      instance = Puppet::Resource::Type.new(:definition, o.name, @context.merge(args_from_definition(o, modname, ExpressionSupportingReturn)))\n      Puppet::Pops::Loaders.register_runtime3_type(instance.name, o.locator.to_uri(o))\n      instance\n    end\n\n    def instantiate_NodeDefinition(o, modname)\n      args = { :module_name => modname }\n\n      unless is_nop?(o.body)\n        args[:code] = Expression.new(:value => o.body)\n      end\n\n      unless is_nop?(o.parent)\n        args[:parent] = @ast_transformer.hostname(o.parent)\n      end\n      args = @ast_transformer.merge_location(args, o)\n\n      host_matches = @ast_transformer.hostname(o.host_matches)\n      host_matches.collect do |name|\n        Puppet::Resource::Type.new(:node, name, @context.merge(args))\n      end\n    end\n\n    def code\n      Expression.new(:value => @value)\n    end\n\n    def is_nop?(o)\n      @ast_transformer.is_nop?(o)\n    end\n\n    def absolute_reference(ref)\n      if ref.nil? || ref.empty? || ref.start_with?('::')\n        ref\n      else\n        \"::#{ref}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/resource.rb",
    "content": "# frozen_string_literal: true\n\n# Instruction for Resource instantiation.\n# Instantiates resources of both native and user defined types.\n#\nclass Puppet::Parser::AST::Resource < Puppet::Parser::AST::Branch\n  attr_accessor :type, :instances, :exported, :virtual\n\n  def initialize(argshash)\n    Puppet.warn_once('deprecations', 'AST::Resource', _('Use of Puppet::Parser::AST::Resource is deprecated and not fully functional'))\n    super(argshash)\n  end\n\n  # Evaluates resources by adding them to the compiler for lazy evaluation\n  # and returning the produced resource references.\n  #\n  def evaluate(scope)\n    # We want virtual to be true if exported is true.  We can't\n    # just set :virtual => self.virtual in the initialization,\n    # because sometimes the :virtual attribute is set *after*\n    # :exported, in which case it clobbers :exported if :exported\n    # is true.  Argh, this was a very tough one to track down.\n    virt = virtual || exported\n\n    # First level of implicit iteration: build a resource for each\n    # instance.  This handles things like:\n    # file { '/foo': owner => blah; '/bar': owner => blah }\n    @instances.map do |instance|\n      # Evaluate all of the specified params.\n      paramobjects = instance.parameters.map { |param| param.safeevaluate(scope) }\n\n      resource_titles = instance.title.safeevaluate(scope)\n\n      # it's easier to always use an array, even for only one name\n      resource_titles = [resource_titles] unless resource_titles.is_a?(Array)\n\n      fully_qualified_type, resource_titles = scope.resolve_type_and_titles(type, resource_titles)\n\n      # Second level of implicit iteration; build a resource for each\n      # title.  This handles things like:\n      # file { ['/foo', '/bar']: owner => blah }\n      resource_titles.flatten.map do |resource_title|\n        exceptwrap :type => Puppet::ParseError do\n          resource = Puppet::Parser::Resource.new(\n            fully_qualified_type, resource_title,\n            :parameters => paramobjects,\n            :file => file,\n            :line => line,\n            :exported => exported,\n            :virtual => virt,\n            :source => scope.source,\n            :scope => scope,\n            :strict => true\n          )\n\n          if resource.resource_type.is_a? Puppet::Resource::Type\n            resource.resource_type.instantiate_resource(scope, resource)\n          end\n          scope.compiler.add_resource(scope, resource)\n          scope.compiler.evaluate_classes([resource_title], scope, false) if fully_qualified_type == 'class'\n          resource\n        end\n      end\n    end.flatten.compact\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/resource_instance.rb",
    "content": "# frozen_string_literal: true\n\n# A simple container for a parameter for an object.  Consists of a\n# title and a set of parameters.\n#\nclass Puppet::Parser::AST::ResourceInstance < Puppet::Parser::AST::Branch\n  attr_accessor :title, :parameters\n\n  def initialize(argshash)\n    Puppet.warn_once('deprecations', 'AST::ResourceInstance', _('Use of Puppet::Parser::AST::ResourceInstance is deprecated'))\n    super(argshash)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/resourceparam.rb",
    "content": "# frozen_string_literal: true\n\n# The AST object for the parameters inside resource expressions\n#\nclass Puppet::Parser::AST::ResourceParam < Puppet::Parser::AST::Branch\n  attr_accessor :value, :param, :add\n\n  def initialize(argshash)\n    Puppet.warn_once('deprecations', 'AST::ResourceParam', _('Use of Puppet::Parser::AST::ResourceParam is deprecated and not fully functional'))\n    super(argshash)\n  end\n\n  def each\n    [@param, @value].each { |child| yield child }\n  end\n\n  # Return the parameter and the value.\n  def evaluate(scope)\n    value = @value.safeevaluate(scope)\n    Puppet::Parser::Resource::Param.new(\n      :name => @param,\n      :value => value.nil? ? :undef : value,\n      :source => scope.source,\n      :line => line,\n      :file => file,\n      :add => add\n    )\n  end\n\n  def to_s\n    \"#{@param} => #{@value}\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast/top_level_construct.rb",
    "content": "# frozen_string_literal: true\n\n# The base class for AST nodes representing top level things:\n# hostclasses, definitions, and nodes.\nclass Puppet::Parser::AST::TopLevelConstruct < Puppet::Parser::AST\nend\n"
  },
  {
    "path": "lib/puppet/parser/ast.rb",
    "content": "# frozen_string_literal: true\n\n# The base class for the 3x \"parse tree\", now only used by the top level\n# constructs and the compiler.\n# Handles things like file name, line #, and also does the initialization\n# for all of the parameters of all of the child objects.\n#\nclass Puppet::Parser::AST\n  AST = Puppet::Parser::AST\n\n  include Puppet::Util::Errors\n\n  attr_accessor :parent, :scope, :file, :line, :pos\n\n  def inspect\n    \"( #{self.class} #{self} #{@children.inspect} )\"\n  end\n\n  # Evaluate the current object.  Just a stub method, since the subclass\n  # should override this method.\n  def evaluate(scope)\n  end\n\n  # The version of the evaluate method that should be called, because it\n  # correctly handles errors.  It is critical to use this method because\n  # it can enable you to catch the error where it happens, rather than\n  # much higher up the stack.\n  def safeevaluate(scope)\n    # We duplicate code here, rather than using exceptwrap, because this\n    # is called so many times during parsing.\n\n    evaluate(scope)\n  rescue Puppet::Pops::Evaluator::PuppetStopIteration => detail\n    raise detail\n  #      # Only deals with StopIteration from the break() function as a general\n  #      # StopIteration is a general runtime problem\n  #      raise Puppet::ParseError.new(detail.message, detail.file, detail.line, detail)\n  rescue Puppet::Error => detail\n    raise adderrorcontext(detail)\n  rescue => detail\n    error = Puppet::ParseError.new(detail.to_s, nil, nil, detail)\n    # We can't use self.fail here because it always expects strings,\n    # not exceptions.\n    raise adderrorcontext(error, detail)\n  end\n\n  def initialize(file: nil, line: nil, pos: nil)\n    @file = file\n    @line = line\n    @pos = pos\n  end\nend\n\n# And include all of the AST subclasses.\nrequire_relative 'ast/branch'\nrequire_relative 'ast/leaf'\nrequire_relative 'ast/block_expression'\nrequire_relative 'ast/hostclass'\nrequire_relative 'ast/node'\nrequire_relative 'ast/resource'\nrequire_relative 'ast/resource_instance'\nrequire_relative 'ast/resourceparam'\n"
  },
  {
    "path": "lib/puppet/parser/catalog_compiler.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/loaders'\nrequire_relative '../../puppet/pops'\n\n# A Catalog \"compiler\" that is like the regular compiler but with an API\n# that is harmonized with the ScriptCompiler\n#\n# The Script compiler is \"one shot\" - it does not support rechecking if underlying source has changed or\n# deal with possible errors in a cached environment.\n#\nclass Puppet::Parser::CatalogCompiler < Puppet::Parser::Compiler\n  # Evaluates the configured setup for a script + code in an environment with modules\n  #\n  def compile\n    Puppet[:strict_variables] = true\n    Puppet[:strict] = :error\n\n    Puppet.override(rich_data: true) do\n      super\n    end\n  rescue Puppet::ParseErrorWithIssue => detail\n    detail.node = node.name\n    Puppet.log_exception(detail)\n    raise\n  rescue => detail\n    message = \"#{detail} on node #{node.name}\"\n    Puppet.log_exception(detail, message)\n    raise Puppet::Error, message, detail.backtrace\n  end\n\n  # Evaluates all added constructs, and validates the resulting catalog.\n  # This can be called whenever a series of evaluation of puppet code strings\n  # have reached a stable state (essentially that there are no relationships to\n  # non-existing resources).\n  #\n  # Raises an error if validation fails.\n  #\n  def compile_additions\n    evaluate_additions\n    validate\n  end\n\n  # Evaluates added constructs that are lazily evaluated until all of them have been evaluated.\n  #\n  def evaluate_additions\n    evaluate_generators\n    finish\n  end\n\n  # Validates the current state of the catalog.\n  # Does not cause evaluation of lazy constructs.\n  def validate\n    validate_catalog(CatalogValidator::FINAL)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/compiler/catalog_validator/relationship_validator.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Parser::Compiler\n  # Validator that asserts relationship metaparameters refer to valid resources\n  class CatalogValidator::RelationshipValidator < CatalogValidator\n    def validate\n      catalog.resources.each do |resource|\n        next unless resource.is_a?(Puppet::Parser::Resource)\n        next if resource.virtual?\n\n        resource.eachparam do |param|\n          pclass = Puppet::Type.metaparamclass(param.name)\n          validate_relationship(param) if !pclass.nil? && pclass < Puppet::Type::RelationshipMetaparam\n        end\n      end\n      nil\n    end\n\n    private\n\n    def validate_relationship(param)\n      # the referenced resource must exist\n      refs = param.value.is_a?(Array) ? param.value.flatten : [param.value]\n      refs.each do |r|\n        next if r.nil? || r == :undef\n\n        res = r.to_s\n        begin\n          found = catalog.resource(res)\n        rescue ArgumentError => e\n          # Raise again but with file and line information\n          raise CatalogValidationError.new(e.message, param.file, param.line)\n        end\n        unless found\n          msg = _(\"Could not find resource '%{res}' in parameter '%{param}'\") % { res: res, param: param.name.to_s }\n          raise CatalogValidationError.new(msg, param.file, param.line)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/compiler/catalog_validator.rb",
    "content": "# frozen_string_literal: true\n\n# Abstract class for a catalog validator that can be registered with the compiler to run at\n# a certain stage.\nclass Puppet::Parser::Compiler\n  class CatalogValidator\n    PRE_FINISH = :pre_finish\n    FINAL = :final\n\n    # Returns true if the validator should run at the given stage. The default\n    # implementation will only run at stage `FINAL`\n    #\n    # @param stage [Symbol] One of the stage constants defined in this class\n    # @return [Boolean] true if the validator should run at the given stage\n    #\n    def self.validation_stage?(stage)\n      FINAL.equal?(stage)\n    end\n\n    attr_reader :catalog\n\n    # @param catalog [Puppet::Resource::Catalog] The catalog to validate\n    def initialize(catalog)\n      @catalog = catalog\n    end\n\n    # Validate some aspect of the catalog and raise a `CatalogValidationError` on failure\n    def validate\n    end\n  end\n\n  class CatalogValidationError < Puppet::Error\n    include Puppet::ExternalFileError\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/compiler.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nrequire_relative '../../puppet/node'\nrequire_relative '../../puppet/resource/catalog'\nrequire_relative '../../puppet/util/errors'\n\nrequire_relative '../../puppet/loaders'\nrequire_relative '../../puppet/pops'\n\n# Maintain a graph of scopes, along with a bunch of data\n# about the individual catalog we're compiling.\nclass Puppet::Parser::Compiler\n  include Puppet::Parser::AbstractCompiler\n\n  extend Forwardable\n\n  include Puppet::Util\n  include Puppet::Util::Errors\n  include Puppet::Pops::Evaluator::Runtime3Support\n\n  def self.compile(node, code_id = nil)\n    node.environment.check_for_reparse\n\n    errors = node.environment.validation_errors\n    unless errors.empty?\n      errors.each { |e| Puppet.err(e) } if errors.size > 1\n      errmsg = [\n        _(\"Compilation has been halted because: %{error}\") % { error: errors.first },\n        _(\"For more information, see https://puppet.com/docs/puppet/latest/environments_about.html\")\n      ]\n      raise(Puppet::Error, errmsg.join(' '))\n    end\n\n    new(node, :code_id => code_id).compile(&:to_resource)\n  rescue Puppet::ParseErrorWithIssue => detail\n    detail.node = node.name\n    Puppet.log_exception(detail)\n    raise\n  rescue => detail\n    message = _(\"%{message} on node %{node}\") % { message: detail, node: node.name }\n    Puppet.log_exception(detail, message)\n    raise Puppet::Error, message, detail.backtrace\n  end\n\n  attr_reader :node, :facts, :collections, :catalog, :resources, :relationships, :topscope\n  attr_reader :qualified_variables\n\n  # Access to the configured loaders for 4x\n  # @return [Puppet::Pops::Loader::Loaders] the configured loaders\n  # @api private\n  attr_reader :loaders\n\n  # The id of code input to the compiler.\n  # @api private\n  attr_accessor :code_id\n\n  # Add a collection to the global list.\n  def_delegator :@collections,   :<<, :add_collection\n  def_delegator :@relationships, :<<, :add_relationship\n\n  # Store a resource override.\n  def add_override(override)\n    # If possible, merge the override in immediately.\n    resource = @catalog.resource(override.ref)\n    if resource\n      resource.merge(override)\n    else\n      # Otherwise, store the override for later; these\n      # get evaluated in Resource#finish.\n      @resource_overrides[override.ref] << override\n    end\n  end\n\n  def add_resource(scope, resource)\n    @resources << resource\n\n    # Note that this will fail if the resource is not unique.\n    @catalog.add_resource(resource)\n\n    if !resource.class? and resource[:stage]\n      # TRANSLATORS \"stage\" is a keyword in Puppet and should not be translated\n      raise ArgumentError, _(\"Only classes can set 'stage'; normal resources like %{resource} cannot change run stage\") % { resource: resource }\n    end\n\n    # Stages should not be inside of classes.  They are always a\n    # top-level container, regardless of where they appear in the\n    # manifest.\n    return if resource.stage?\n\n    # This adds a resource to the class it lexically appears in in the\n    # manifest.\n    unless resource.class?\n      @catalog.add_edge(scope.resource, resource)\n    end\n  end\n\n  # Store the fact that we've evaluated a class\n  def add_class(name)\n    @catalog.add_class(name) unless name == \"\"\n  end\n\n  # Add a catalog validator that will run at some stage to this compiler\n  # @param catalog_validators [Class<CatalogValidator>] The catalog validator class to add\n  def add_catalog_validator(catalog_validators)\n    @catalog_validators << catalog_validators\n    nil\n  end\n\n  def add_catalog_validators\n    add_catalog_validator(CatalogValidator::RelationshipValidator)\n  end\n\n  # Return a list of all of the defined classes.\n  def_delegator :@catalog, :classes, :classlist\n\n  def with_context_overrides(description = '', &block)\n    Puppet.override(@context_overrides, description, &block)\n  end\n\n  # Compiler our catalog.  This mostly revolves around finding and evaluating classes.\n  # This is the main entry into our catalog.\n  def compile\n    Puppet.override(@context_overrides, _(\"For compiling %{node}\") % { node: node.name }) do\n      @catalog.environment_instance = environment\n\n      # Set the client's parameters into the top scope.\n      Puppet::Util::Profiler.profile(_(\"Compile: Set node parameters\"), [:compiler, :set_node_params]) { set_node_parameters }\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Created settings scope\"), [:compiler, :create_settings_scope]) { create_settings_scope }\n\n      # TRANSLATORS \"main\" is a function name and should not be translated\n      Puppet::Util::Profiler.profile(_(\"Compile: Evaluated main\"), [:compiler, :evaluate_main]) { evaluate_main }\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Evaluated AST node\"), [:compiler, :evaluate_ast_node]) { evaluate_ast_node }\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Evaluated node classes\"), [:compiler, :evaluate_node_classes]) { evaluate_node_classes }\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Evaluated generators\"), [:compiler, :evaluate_generators]) { evaluate_generators }\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Validate Catalog pre-finish\"), [:compiler, :validate_pre_finish]) do\n        validate_catalog(CatalogValidator::PRE_FINISH)\n      end\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Finished catalog\"), [:compiler, :finish_catalog]) { finish }\n\n      fail_on_unevaluated\n\n      Puppet::Util::Profiler.profile(_(\"Compile: Validate Catalog final\"), [:compiler, :validate_final]) do\n        validate_catalog(CatalogValidator::FINAL)\n      end\n\n      if block_given?\n        yield @catalog\n      else\n        @catalog\n      end\n    end\n  end\n\n  def validate_catalog(validation_stage)\n    @catalog_validators.select { |vclass| vclass.validation_stage?(validation_stage) }.each { |vclass| vclass.new(@catalog).validate }\n  end\n\n  # Constructs the overrides for the context\n  def context_overrides\n    {\n      :current_environment => environment,\n      :global_scope => @topscope, # 4x placeholder for new global scope\n      :loaders => @loaders, # 4x loaders\n    }\n  end\n\n  def_delegator :@collections, :delete, :delete_collection\n\n  # Return the node's environment.\n  def environment\n    node.environment\n  end\n\n  # Evaluate all of the classes specified by the node.\n  # Classes with parameters are evaluated as if they were declared.\n  # Classes without parameters or with an empty set of parameters are evaluated\n  # as if they were included. This means classes with an empty set of\n  # parameters won't conflict even if the class has already been included.\n  def evaluate_node_classes\n    if @node.classes.is_a? Hash\n      classes_with_params, classes_without_params = @node.classes.partition { |_name, params| params and !params.empty? }\n\n      # The results from Hash#partition are arrays of pairs rather than hashes,\n      # so we have to convert to the forms evaluate_classes expects (Hash, and\n      # Array of class names)\n      classes_with_params = classes_with_params.to_h\n      classes_without_params.map!(&:first)\n    else\n      classes_with_params = {}\n      classes_without_params = @node.classes\n    end\n\n    evaluate_classes(classes_with_params, @node_scope || topscope)\n    evaluate_classes(classes_without_params, @node_scope || topscope)\n  end\n\n  # If ast nodes are enabled, then see if we can find and evaluate one.\n  #\n  # @api private\n  def evaluate_ast_node\n    krt = environment.known_resource_types\n    return unless krt.nodes? # ast_nodes?\n\n    # Now see if we can find the node.\n    astnode = nil\n    @node.names.each do |name|\n      astnode = krt.node(name.to_s.downcase)\n      break if astnode\n    end\n\n    unless astnode ||= krt.node(\"default\")\n      raise Puppet::ParseError, _(\"Could not find node statement with name 'default' or '%{names}'\") % { names: node.names.join(\", \") }\n    end\n\n    # Create a resource to model this node, and then add it to the list\n    # of resources.\n    resource = astnode.ensure_in_catalog(topscope)\n\n    resource.evaluate\n\n    @node_scope = topscope.class_scope(astnode)\n  end\n\n  # Evaluates each specified class in turn. If there are any classes that\n  # can't be found, an error is raised. This method really just creates resource objects\n  # that point back to the classes, and then the resources are themselves\n  # evaluated later in the process.\n  #\n  def evaluate_classes(classes, scope, lazy_evaluate = true)\n    raise Puppet::DevError, _(\"No source for scope passed to evaluate_classes\") unless scope.source\n\n    class_parameters = nil\n    # if we are a param class, save the classes hash\n    # and transform classes to be the keys\n    if classes.instance_of?(Hash)\n      class_parameters = classes\n      classes = classes.keys\n    end\n\n    hostclasses = classes.collect do |name|\n      environment.known_resource_types.find_hostclass(name) or raise Puppet::Error, _(\"Could not find class %{name} for %{node}\") % { name: name, node: node.name }\n    end\n\n    if class_parameters\n      resources = ensure_classes_with_parameters(scope, hostclasses, class_parameters)\n      unless lazy_evaluate\n        resources.each(&:evaluate)\n      end\n\n      resources\n    else\n      already_included, newly_included = ensure_classes_without_parameters(scope, hostclasses)\n      unless lazy_evaluate\n        newly_included.each(&:evaluate)\n      end\n\n      already_included + newly_included\n    end\n  end\n\n  def evaluate_relationships\n    @relationships.each { |rel| rel.evaluate(catalog) }\n  end\n\n  # Return a resource by either its ref or its type and title.\n  def_delegator :@catalog, :resource, :findresource\n\n  def initialize(node, code_id: nil)\n    @node = sanitize_node(node)\n    @code_id = code_id\n    initvars\n    add_catalog_validators\n    # Resolutions of fully qualified variable names\n    @qualified_variables = {}\n  end\n\n  # Create a new scope, with either a specified parent scope or\n  # using the top scope.\n  def newscope(parent, options = {})\n    parent ||= topscope\n    scope = Puppet::Parser::Scope.new(self, **options)\n    scope.parent = parent\n    scope\n  end\n\n  # Return any overrides for the given resource.\n  def resource_overrides(resource)\n    @resource_overrides[resource.ref]\n  end\n\n  private\n\n  def ensure_classes_with_parameters(scope, hostclasses, parameters)\n    hostclasses.collect do |klass|\n      klass.ensure_in_catalog(scope, parameters[klass.name] || {})\n    end\n  end\n\n  def ensure_classes_without_parameters(scope, hostclasses)\n    already_included = []\n    newly_included = []\n    hostclasses.each do |klass|\n      class_scope = scope.class_scope(klass)\n      if class_scope\n        already_included << class_scope.resource\n      else\n        newly_included << klass.ensure_in_catalog(scope)\n      end\n    end\n\n    [already_included, newly_included]\n  end\n\n  # Evaluate our collections and return true if anything returned an object.\n  # The 'true' is used to continue a loop, so it's important.\n  def evaluate_collections\n    return false if @collections.empty?\n\n    exceptwrap do\n      # We have to iterate over a dup of the array because\n      # collections can delete themselves from the list, which\n      # changes its length and causes some collections to get missed.\n      Puppet::Util::Profiler.profile(_(\"Evaluated collections\"), [:compiler, :evaluate_collections]) do\n        found_something = false\n        @collections.dup.each do |collection|\n          found_something = true if collection.evaluate\n        end\n        found_something\n      end\n    end\n  end\n\n  # Make sure all of our resources have been evaluated into native resources.\n  # We return true if any resources have, so that we know to continue the\n  # evaluate_generators loop.\n  def evaluate_definitions\n    exceptwrap do\n      Puppet::Util::Profiler.profile(_(\"Evaluated definitions\"), [:compiler, :evaluate_definitions]) do\n        urs = unevaluated_resources.each do |resource|\n          resource.evaluate\n        rescue Puppet::Pops::Evaluator::PuppetStopIteration => detail\n          # needs to be handled specifically as the error has the file/line/position where this\n          # occurred rather than the resource\n          fail(Puppet::Pops::Issues::RUNTIME_ERROR, detail, { :detail => detail.message }, detail)\n        rescue Puppet::Error => e\n          # PuppetError has the ability to wrap an exception, if so, use the wrapped exception's\n          # call stack instead\n          fail(Puppet::Pops::Issues::RUNTIME_ERROR, resource, { :detail => e.message }, e.original || e)\n        end\n        !urs.empty?\n      end\n    end\n  end\n\n  # Iterate over collections and resources until we're sure that the whole\n  # compile is evaluated.  This is necessary because both collections\n  # and defined resources can generate new resources, which themselves could\n  # be defined resources.\n  def evaluate_generators\n    count = 0\n    loop do\n      done = true\n\n      Puppet::Util::Profiler.profile(_(\"Iterated (%{count}) on generators\") % { count: count + 1 }, [:compiler, :iterate_on_generators]) do\n        # Call collections first, then definitions.\n        done = false if evaluate_collections\n        done = false if evaluate_definitions\n      end\n\n      break if done\n\n      count += 1\n\n      if count > 1000\n        raise Puppet::ParseError, _(\"Somehow looped more than 1000 times while evaluating host catalog\")\n      end\n    end\n  end\n  protected :evaluate_generators\n\n  # Find and evaluate our main object, if possible.\n  def evaluate_main\n    krt = environment.known_resource_types\n    @main = krt.find_hostclass('') || krt.add(Puppet::Resource::Type.new(:hostclass, ''))\n    @topscope.source = @main\n    @main_resource = Puppet::Parser::Resource.new('class', :main, :scope => @topscope, :source => @main)\n    @topscope.resource = @main_resource\n\n    add_resource(@topscope, @main_resource)\n\n    @main_resource.evaluate\n  end\n\n  # Make sure the entire catalog is evaluated.\n  def fail_on_unevaluated\n    fail_on_unevaluated_overrides\n    fail_on_unevaluated_resource_collections\n  end\n\n  # If there are any resource overrides remaining, then we could\n  # not find the resource they were supposed to override, so we\n  # want to throw an exception.\n  def fail_on_unevaluated_overrides\n    remaining = @resource_overrides.values.flatten.collect(&:ref)\n\n    unless remaining.empty?\n      raise Puppet::ParseError, _(\"Could not find resource(s) %{resources} for overriding\") % { resources: remaining.join(', ') }\n    end\n  end\n\n  # Make sure there are no remaining collections that are waiting for\n  # resources that have not yet been instantiated. If this occurs it\n  # is an error (missing resource - it could not be realized).\n  #\n  def fail_on_unevaluated_resource_collections\n    remaining = @collections.collect(&:unresolved_resources).flatten.compact\n    unless remaining.empty?\n      raise Puppet::ParseError, _(\"Failed to realize virtual resources %{resources}\") % { resources: remaining.join(', ') }\n    end\n  end\n\n  # Make sure all of our resources and such have done any last work\n  # necessary.\n  def finish\n    evaluate_relationships\n\n    resources.each do |resource|\n      # Add in any resource overrides.\n      overrides = resource_overrides(resource)\n      if overrides\n        overrides.each do |over|\n          resource.merge(over)\n        end\n\n        # Remove the overrides, so that the configuration knows there\n        # are none left.\n        overrides.clear\n      end\n\n      resource.finish if resource.respond_to?(:finish)\n    end\n\n    add_resource_metaparams\n  end\n  protected :finish\n\n  def add_resource_metaparams\n    main = catalog.resource(:class, :main)\n    unless main\n      # TRANSLATORS \"main\" is a function name and should not be translated\n      raise _(\"Couldn't find main\")\n    end\n\n    names = Puppet::Type.metaparams.select do |name|\n      !Puppet::Parser::Resource.relationship_parameter?(name)\n    end\n    data = {}\n    catalog.walk(main, :out) do |source, target|\n      source_data = data[source] || metaparams_as_data(source, names)\n      if source_data\n        # only store anything in the data hash if we've actually got\n        # data\n        data[source] ||= source_data\n        source_data.each do |param, value|\n          target[param] = value if target[param].nil?\n        end\n        data[target] = source_data.merge(metaparams_as_data(target, names))\n      end\n\n      target.merge_tags_from(source)\n    end\n  end\n\n  def metaparams_as_data(resource, params)\n    data = nil\n    params.each do |param|\n      next if resource[param].nil?\n\n      # Because we could be creating a hash for every resource,\n      # and we actually probably don't often have any data here at all,\n      # we're optimizing a bit by only creating a hash if there's\n      # any data to put in it.\n      data ||= {}\n      data[param] = resource[param]\n    end\n    data\n  end\n\n  # Set up all of our internal variables.\n  def initvars\n    # The list of overrides.  This is used to cache overrides on objects\n    # that don't exist yet.  We store an array of each override.\n    @resource_overrides = Hash.new do |overs, ref|\n      overs[ref] = []\n    end\n\n    # The list of collections that have been created.  This is a global list,\n    # but they each refer back to the scope that created them.\n    @collections = []\n\n    # The list of relationships to evaluate.\n    @relationships = []\n\n    # For maintaining the relationship between scopes and their resources.\n    @catalog = Puppet::Resource::Catalog.new(@node.name, @node.environment, @code_id)\n\n    # MOVED HERE - SCOPE IS NEEDED (MOVE-SCOPE)\n    # Create the initial scope, it is needed early\n    @topscope = Puppet::Parser::Scope.new(self)\n\n    # Initialize loaders and Pcore\n    @loaders = Puppet::Pops::Loaders.new(environment)\n\n    # Need to compute overrides here, and remember them, because we are about to\n    # enter the magic zone of known_resource_types and initial import.\n    # Expensive entries in the context are bound lazily.\n    @context_overrides = context_overrides()\n\n    # This construct ensures that initial import (triggered by instantiating\n    # the structure 'known_resource_types') has a configured context\n    # It cannot survive the initvars method, and is later reinstated\n    # as part of compiling...\n    #\n    Puppet.override(@context_overrides, _(\"For initializing compiler\")) do\n      # THE MAGIC STARTS HERE ! This triggers parsing, loading etc.\n      @catalog.version = environment.known_resource_types.version\n      @loaders.pre_load\n    end\n\n    @catalog.add_resource(Puppet::Parser::Resource.new(\"stage\", :main, :scope => @topscope))\n\n    # local resource array to maintain resource ordering\n    @resources = []\n\n    # Make sure any external node classes are in our class list\n    if @node.classes.instance_of?(Hash)\n      @catalog.add_class(*@node.classes.keys)\n    else\n      @catalog.add_class(*@node.classes)\n    end\n\n    @catalog_validators = []\n  end\n\n  def sanitize_node(node)\n    node.sanitize\n    node\n  end\n\n  # Set the node's parameters into the top-scope as variables.\n  def set_node_parameters\n    node.parameters.each do |param, value|\n      # We don't want to set @topscope['environment'] from the parameters,\n      # instead we want to get that from the node's environment itself in\n      # case a custom node terminus has done any mucking about with\n      # node.parameters.\n      next if param.to_s == 'environment'\n\n      # Ensure node does not leak Symbol instances in general\n      @topscope[param.to_s] = value.is_a?(Symbol) ? value.to_s : value\n    end\n    @topscope['environment'] = node.environment.name.to_s\n\n    # These might be nil.\n    catalog.client_version = node.parameters[\"clientversion\"]\n    catalog.server_version = node.parameters[\"serverversion\"]\n    @topscope.set_trusted(node.trusted_data)\n\n    @topscope.set_server_facts(node.server_facts)\n\n    facts_hash = node.facts.nil? ? {} : node.facts.values\n    @topscope.set_facts(facts_hash)\n  end\n\n  SETTINGS = 'settings'\n\n  def create_settings_scope\n    settings_type = create_settings_type\n    settings_resource = Puppet::Parser::Resource.new('class', SETTINGS, :scope => @topscope)\n\n    @catalog.add_resource(settings_resource)\n    settings_type.evaluate_code(settings_resource)\n    settings_resource.instance_variable_set(:@evaluated, true) # Prevents settings from being reevaluated\n\n    scope = @topscope.class_scope(settings_type)\n    scope.merge_settings(environment.name)\n  end\n\n  def create_settings_type\n    environment.lock.synchronize do\n      resource_types = environment.known_resource_types\n      settings_type = resource_types.hostclass(SETTINGS)\n      if settings_type.nil?\n        settings_type = Puppet::Resource::Type.new(:hostclass, SETTINGS)\n        resource_types.add(settings_type)\n      end\n\n      settings_type\n    end\n  end\n\n  # Return an array of all of the unevaluated resources.  These will be definitions,\n  # which need to get evaluated into native resources.\n  def unevaluated_resources\n    # The order of these is significant for speed due to short-circuiting\n    resources.reject { |resource| resource.evaluated? or resource.virtual? or resource.builtin_type? }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/e4_parser_adapter.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/pops'\n\nmodule Puppet\nmodule Parser\n# Adapts an egrammar/eparser to respond to the public API of the classic parser\n# and makes use of the new evaluator.\n#\nclass E4ParserAdapter\n  def initialize\n    @file = ''\n    @string = ''\n    @use = :unspecified\n  end\n\n  def file=(file)\n    @file = file\n    @use = :file\n  end\n\n  def parse(string = nil)\n    self.string = string if string\n    parser = Pops::Parser::EvaluatingParser.singleton\n    model =\n      if @use == :string\n        # Parse with a source_file to set in created AST objects (it was either given, or it may be unknown\n        # if caller did not set a file and the present a string.\n        #\n        parser.parse_string(@string, @file || \"unknown-source-location\")\n      else\n        parser.parse_file(@file)\n      end\n\n    # the parse_result may be\n    # * empty / nil (no input)\n    # * a Model::Program\n    # * a Model::Expression\n    #\n    args = {}\n    Pops::Model::AstTransformer.new(@file).merge_location(args, model)\n\n    ast_code =\n      if model.is_a? Pops::Model::Program\n        AST::PopsBridge::Program.new(model, args)\n      else\n        args[:value] = model\n        AST::PopsBridge::Expression.new(args)\n      end\n\n    # Create the \"main\" class for the content - this content will get merged with all other \"main\" content\n    AST::Hostclass.new('', :code => ast_code)\n  end\n\n  def string=(string)\n    @string = string\n    @use = :string\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/parser/files.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Parser::Files\n  module_function\n\n  # Return a list of manifests as absolute filenames matching the given\n  # pattern.\n  #\n  # @param pattern [String] A reference for a file in a module. It is the\n  #   format \"<modulename>/<file glob>\"\n  # @param environment [Puppet::Node::Environment] the environment of modules\n  #\n  # @return [Array(String, Array<String>)] the module name and the list of files found\n  # @api private\n  def find_manifests_in_modules(pattern, environment)\n    module_name, file_pattern = split_file_path(pattern)\n\n    mod = environment.module(module_name)\n    if mod\n      [mod.name, mod.match_manifests(file_pattern)]\n    else\n      [nil, []]\n    end\n  end\n\n  # Find the path to the given file selector. Files can be selected in\n  # one of two ways:\n  #   * absolute path: the path is simply returned\n  #   * modulename/filename selector: a file is found in the file directory\n  #     of the named module.\n  #\n  # The check for file existence is performed on the node compiling the\n  # manifest. A node running \"puppet apply\" compiles its own manifest, but\n  # a node running \"puppet agent\" depends on the configured puppetserver\n  # for compiling. In either case, a nil is returned if no file is found.\n  #\n  # @param template [String] the file selector\n  # @param environment [Puppet::Node::Environment] the environment in which to search\n  # @return [String, nil] the absolute path to the file or nil if there is no file found\n  #\n  # @api private\n  def find_file(file, environment)\n    find_in_module(file, environment) do |mod, module_file|\n      mod.file(module_file)\n    end\n  end\n\n  # Find the path to the given template selector. Templates can be selected in\n  # a couple of ways:\n  #   * absolute path: the path is simply returned\n  #   * modulename/filename selector: a file is found in the template directory\n  #     of the named module.\n  #\n  # In the last two cases a nil is returned if there isn't a file found. In the\n  # first case (absolute path), there is no existence check done and so the\n  # path will be returned even if there isn't a file available.\n  #\n  # @param template [String] the template selector\n  # @param environment [Puppet::Node::Environment] the environment in which to search\n  # @return [String, nil] the absolute path to the template file or nil if there is no file found\n  #\n  # @api private\n  def find_template(template, environment)\n    find_in_module(template, environment) do |mod, template_file|\n      mod.template(template_file)\n    end\n  end\n\n  # @api private\n  def find_in_module(reference, environment)\n    if Puppet::Util.absolute_path?(reference)\n      reference\n    else\n      path, file = split_file_path(reference)\n      mod = environment.module(path)\n\n      if file && mod\n        yield(mod, file)\n      else\n        nil\n      end\n    end\n  end\n\n  # Split the path into the module and the rest of the path, or return\n  # nil if the path is empty or absolute (starts with a /).\n  # @api private\n  def split_file_path(path)\n    if path == \"\" || Puppet::Util.absolute_path?(path)\n      nil\n    else\n      path.split(File::SEPARATOR, 2)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/assert_type.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :assert_type,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    Returns the given value if it is of the given\n    [data type](https://puppet.com/docs/puppet/latest/lang_data.html), or\n    otherwise either raises an error or executes an optional two-parameter\n    [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html).\n\n    The function takes two mandatory arguments, in this order:\n\n    1. The expected data type.\n    2. A value to compare against the expected data type.\n\n    **Example**: Using `assert_type`\n\n    ~~~ puppet\n    $raw_username = 'Amy Berry'\n\n    # Assert that $raw_username is a non-empty string and assign it to $valid_username.\n    $valid_username = assert_type(String[1], $raw_username)\n\n    # $valid_username contains \"Amy Berry\".\n    # If $raw_username was an empty string or a different data type, the Puppet run would\n    # fail with an \"Expected type does not match actual\" error.\n    ~~~\n\n    You can use an optional lambda to provide enhanced feedback. The lambda takes two\n    mandatory parameters, in this order:\n\n    1. The expected data type as described in the function's first argument.\n    2. The actual data type of the value.\n\n    **Example**: Using `assert_type` with a warning and default value\n\n    ~~~ puppet\n    $raw_username = 'Amy Berry'\n\n    # Assert that $raw_username is a non-empty string and assign it to $valid_username.\n    # If it isn't, output a warning describing the problem and use a default value.\n    $valid_username = assert_type(String[1], $raw_username) |$expected, $actual| {\n      warning( \"The username should be \\'${expected}\\', not \\'${actual}\\'. Using 'anonymous'.\" )\n      'anonymous'\n    }\n\n    # $valid_username contains \"Amy Berry\".\n    # If $raw_username was an empty string, the Puppet run would set $valid_username to\n    # \"anonymous\" and output a warning: \"The username should be 'String[1, default]', not\n    # 'String[0, 0]'. Using 'anonymous'.\"\n    ~~~\n\n    For more information about data types, see the\n    [documentation](https://puppet.com/docs/puppet/latest/lang_data.html).\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('assert_type')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/binary_file.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :binary_file,\n  :type => :rvalue,\n  :arity => 1,\n  :doc => <<~DOC\n    Loads a binary file from a module or file system and returns its contents as a Binary.\n\n    The argument to this function should be a `<MODULE NAME>/<FILE>`\n    reference, which will load `<FILE>` from a module's `files`\n    directory. (For example, the reference `mysql/mysqltuner.pl` will load the\n    file `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n\n    This function also accepts an absolute file path that allows reading\n    binary file content from anywhere on disk.\n\n    An error is raised if the given file does not exists.\n\n    To search for the existence of files, use the `find_file()` function.\n\n    - since 4.8.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('binary_file')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/break.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :break,\n  :arity => 0,\n  :doc => <<~DOC\n    Breaks the innermost iteration as if it encountered an end of input.\n    This function does not return to the caller.\n\n    The signal produced to stop the iteration bubbles up through\n    the call stack until either terminating the innermost iteration or\n    raising an error if the end of the call stack is reached.\n\n    The break() function does not accept an argument.\n\n    **Example:** Using `break`\n\n    ```puppet\n    $data = [1,2,3]\n    notice $data.map |$x| { if $x == 3 { break() } $x*10 }\n    ```\n\n    Would notice the value `[10, 20]`\n\n    **Example:** Using a nested `break`\n\n    ```puppet\n    function break_if_even($x) {\n      if $x % 2 == 0 { break() }\n    }\n    $data = [1,2,3]\n    notice $data.map |$x| { break_if_even($x); $x*10 }\n    ```\n    Would notice the value `[10]`\n\n    * Also see functions `next` and `return`\n    * Since 4.8.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('break')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/contain.rb",
    "content": "# frozen_string_literal: true\n\n# Called within a class definition, establishes a containment\n# relationship with another class\n\nPuppet::Parser::Functions.newfunction(\n  :contain,\n  :arity => -2,\n  :doc => \"Contain one or more classes inside the current class. If any of\nthese classes are undeclared, they will be declared as if called with the\n`include` function. Accepts a class name, an array of class names, or a\ncomma-separated list of class names.\n\nA contained class will not be applied before the containing class is\nbegun, and will be finished before the containing class is finished.\n\nYou must use the class's full name;\nrelative names are not allowed. In addition to names in string form,\nyou may also directly use Class and Resource Type values that are produced by\nevaluating resource and relationship expressions.\n\nThe function returns an array of references to the classes that were contained thus\nallowing the function call to `contain` to directly continue.\n\n- Since 4.0.0 support for Class and Resource Type values, absolute names\n- Since 4.7.0 an Array[Type[Class[n]]] is returned with all the contained classes\n\"\n) do |classes|\n  # Call the 4.x version of this function in case 3.x ruby code uses this function\n  Puppet.warn_once('deprecations', '3xfunction#contain', _(\"Calling function_contain via the Scope class is deprecated. Use Scope#call_function instead\"))\n  call_function('contain', classes)\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/create_resources.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(:create_resources, :arity => -3, :doc => <<-'ENDHEREDOC') do |args|\n    Converts a hash into a set of resources and adds them to the catalog.\n\n    **Note**: Use this function selectively. It's generally better to write resources in\n     [Puppet](https://puppet.com/docs/puppet/latest/lang_resources.html), as\n     resources created with `create_resource` are difficult to read and troubleshoot.\n\n    This function takes two mandatory arguments: a resource type, and a hash describing\n    a set of resources. The hash should be in the form `{title => {parameters} }`:\n\n        # A hash of user resources:\n        $myusers = {\n          'nick' => { uid    => '1330',\n                      gid    => allstaff,\n                      groups => ['developers', 'operations', 'release'], },\n          'dan'  => { uid    => '1308',\n                      gid    => allstaff,\n                      groups => ['developers', 'prosvc', 'release'], },\n        }\n\n        create_resources(user, $myusers)\n\n    A third, optional parameter may be given, also as a hash:\n\n        $defaults = {\n          'ensure'   => present,\n          'provider' => 'ldap',\n        }\n\n        create_resources(user, $myusers, $defaults)\n\n    The values given on the third argument are added to the parameters of each resource\n    present in the set given on the second argument. If a parameter is present on both\n    the second and third arguments, the one on the second argument takes precedence.\n\n    This function can be used to create defined resources and classes, as well\n    as native resources.\n\n    Virtual and Exported resources may be created by prefixing the type name\n    with @ or @@ respectively. For example, the $myusers hash may be exported\n    in the following manner:\n\n        create_resources(\"@@user\", $myusers)\n\n    The $myusers may be declared as virtual resources using:\n\n        create_resources(\"@user\", $myusers)\n\n    Note that `create_resources` filters out parameter values that are `undef` so that normal\n    data binding and Puppet default value expressions are considered (in that order) for the\n    final value of a parameter (just as when setting a parameter to `undef` in a Puppet language\n    resource declaration).\nENDHEREDOC\n  if Puppet[:tasks]\n    raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n      Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n      { :operation => 'create_resources' }\n    )\n  end\n\n  raise ArgumentError, (_(\"create_resources(): wrong number of arguments (%{count}; must be 2 or 3)\") % { count: args.length }) if args.length > 3\n  raise ArgumentError, _('create_resources(): second argument must be a hash') unless args[1].is_a?(Hash)\n\n  if args.length == 3\n    raise ArgumentError, _('create_resources(): third argument, if provided, must be a hash') unless args[2].is_a?(Hash)\n  end\n\n  type, instances, defaults = args\n  defaults ||= {}\n  type_name = type.sub(/^@{1,2}/, '').downcase\n\n  # Get file/line information from the Puppet stack (where call comes from in Puppet source)\n  # If relayed via other Puppet functions in ruby that do not nest their calls, the source position\n  # will be in the original Puppet source.\n  #\n  file, line = Puppet::Pops::PuppetStack.top_of_stack\n\n  if type.start_with? '@@'\n    exported = true\n    virtual = true\n  elsif type.start_with? '@'\n    virtual = true\n  end\n\n  if type_name == 'class' && (exported || virtual)\n    # cannot find current evaluator, so use another\n    evaluator = Puppet::Pops::Parser::EvaluatingParser.new.evaluator\n    # optionally fails depending on configured severity of issue\n    evaluator.runtime_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n  end\n\n  instances.map do |title, params|\n    # Add support for iteration if title is an array\n    resource_titles = title.is_a?(Array) ? title : [title]\n    Puppet::Pops::Evaluator::Runtime3ResourceSupport.create_resources(\n      file, line,\n      self,\n      virtual, exported,\n      type_name,\n      resource_titles,\n      defaults.merge(params).filter_map do |name, value|\n        value = nil if value == :undef\n        Puppet::Parser::Resource::Param.new(\n          :name => name,\n          :value => value, # wide open to various data types, must be correct\n          :source => source, # TODO: support :line => line, :file => file,\n          :add => false\n        )\n      end\n    )\n  end.flatten.compact\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/defined.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :defined,\n  :type => :rvalue,\n  :arity => -2,\n  :doc => <<~DOC\n    Determines whether a given class or resource type is defined and returns a Boolean\n    value. You can also use `defined` to determine whether a specific resource is defined,\n    or whether a variable has a value (including `undef`, as opposed to the variable never\n    being declared or assigned).\n\n    This function takes at least one string argument, which can be a class name, type name,\n    resource reference, or variable reference of the form `'$name'`.\n\n    The `defined` function checks both native and defined types, including types\n    provided by modules. Types and classes are matched by their names. The function matches\n    resource declarations by using resource references.\n\n    **Examples**: Different types of `defined` function matches\n\n    ~~~ puppet\n    # Matching resource types\n    defined(\"file\")\n    defined(\"customtype\")\n\n    # Matching defines and classes\n    defined(\"foo\")\n    defined(\"foo::bar\")\n\n    # Matching variables\n    defined('$name')\n\n    # Matching declared resources\n    defined(File['/tmp/file'])\n    ~~~\n\n    Puppet depends on the configuration's evaluation order when checking whether a resource\n    is declared.\n\n    **Example**: Importance of evaluation order when using `defined`\n\n    ~~~ puppet\n    # Assign values to $is_defined_before and $is_defined_after using identical `defined`\n    # functions.\n\n    $is_defined_before = defined(File['/tmp/file'])\n\n    file { \"/tmp/file\":\n      ensure => present,\n    }\n\n    $is_defined_after = defined(File['/tmp/file'])\n\n    # $is_defined_before returns false, but $is_defined_after returns true.\n    ~~~\n\n    This order requirement only refers to evaluation order. The order of resources in the\n    configuration graph (e.g. with `before` or `require`) does not affect the `defined`\n    function's behavior.\n\n    > **Warning:** Avoid relying on the result of the `defined` function in modules, as you\n    > might not be able to guarantee the evaluation order well enough to produce consistent\n    > results. This can cause other code that relies on the function's result to behave\n    > inconsistently or fail.\n\n    If you pass more than one argument to `defined`, the function returns `true` if _any_\n    of the arguments are defined. You can also match resources by type, allowing you to\n    match conditions of different levels of specificity, such as whether a specific resource\n    is of a specific data type.\n\n    **Example**: Matching multiple resources and resources by different types with `defined`\n\n    ~~~ puppet\n    file { \"/tmp/file1\":\n      ensure => file,\n    }\n\n    $tmp_file = file { \"/tmp/file2\":\n      ensure => file,\n    }\n\n    # Each of these statements return `true` ...\n    defined(File['/tmp/file1'])\n    defined(File['/tmp/file1'],File['/tmp/file2'])\n    defined(File['/tmp/file1'],File['/tmp/file2'],File['/tmp/file3'])\n    # ... but this returns `false`.\n    defined(File['/tmp/file3'])\n\n    # Each of these statements returns `true` ...\n    defined(Type[Resource['file','/tmp/file2']])\n    defined(Resource['file','/tmp/file2'])\n    defined(File['/tmp/file2'])\n    defined('$tmp_file')\n    # ... but each of these returns `false`.\n    defined(Type[Resource['exec','/tmp/file2']])\n    defined(Resource['exec','/tmp/file2'])\n    defined(File['/tmp/file3'])\n    defined('$tmp_file2')\n    ~~~\n\n    - Since 2.7.0\n    - Since 3.6.0 variable reference and future parser types\n    - Since 3.8.1 type specific requests with future parser\n    - Since 4.0.0 includes all future parser features\n  DOC\n) do |_vals|\n  Puppet::Parser::Functions::Error.is4x('defined')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/dig.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :dig,\n  :type => :rvalue,\n  :arity => -1,\n  :doc => <<~DOC\n    Returns a value for a sequence of given keys/indexes into a structure, such as\n    an array or hash.\n    This function is used to \"dig into\" a complex data structure by\n    using a sequence of keys / indexes to access a value from which\n    the next key/index is accessed recursively.\n\n    The first encountered `undef` value or key stops the \"dig\" and `undef` is returned.\n\n    An error is raised if an attempt is made to \"dig\" into\n    something other than an `undef` (which immediately returns `undef`), an `Array` or a `Hash`.\n\n\n\n    **Example:** Using `dig`\n\n    ```puppet\n    $data = {a => { b => [{x => 10, y => 20}, {x => 100, y => 200}]}}\n    notice $data.dig('a', 'b', 1, 'x')\n    ```\n\n    Would notice the value 100.\n\n    This is roughly equivalent to `$data['a']['b'][1]['x']`. However, a standard\n    index will return an error and cause catalog compilation failure if any parent\n    of the final key (`'x'`) is `undef`. The `dig` function will return undef,\n    rather than failing catalog compilation. This allows you to check if data\n    exists in a structure without mandating that it always exists.\n\n    * Since 4.5.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('dig')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/digest.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/checksums'\nPuppet::Parser::Functions.newfunction(:digest, :type => :rvalue, :arity => 1, :doc => \"Returns a hash value from a provided string using the digest_algorithm setting from the Puppet config file.\") do |args|\n  algo = Puppet[:digest_algorithm]\n  Puppet::Util::Checksums.method(algo.intern).call args[0]\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/each.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :each,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    Runs a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    repeatedly using each value in a data structure, then returns the values unchanged.\n\n    This function takes two mandatory arguments, in this order:\n\n    1. An array or hash the function will iterate over.\n    2. A lambda, which the function calls for each element in the first argument. It can\n    request one or two parameters.\n\n    **Example**: Using the `each` function\n\n    `$data.each |$parameter| { <PUPPET CODE BLOCK> }`\n\n    or\n\n    `each($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\n    When the first argument (`$data` in the above example) is an array, Puppet passes each\n    value in turn to the lambda, then returns the original values.\n\n    **Example**: Using the `each` function with an array and a one-parameter lambda\n\n    ~~~ puppet\n    # For the array $data, run a lambda that creates a resource for each item.\n    $data = [\"routers\", \"servers\", \"workstations\"]\n    $data.each |$item| {\n     notify { $item:\n       message => $item\n     }\n    }\n    # Puppet creates one resource for each of the three items in $data. Each resource is\n    # named after the item's value and uses the item's value in a parameter.\n    ~~~\n\n    When the first argument is a hash, Puppet passes each key and value pair to the lambda\n    as an array in the form `[key, value]` and returns the original hash.\n\n    **Example**: Using the `each` function with a hash and a one-parameter lambda\n\n    ~~~ puppet\n    # For the hash $data, run a lambda using each item as a key-value array that creates a\n    # resource for each item.\n    $data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n    $data.each |$items| {\n     notify { $items[0]:\n       message => $items[1]\n     }\n    }\n    # Puppet creates one resource for each of the three items in $data, each named after the\n    # item's key and containing a parameter using the item's value.\n    ~~~\n\n    When the first argument is an array and the lambda has two parameters, Puppet passes the\n    array's indexes (enumerated from 0) in the first parameter and its values in the second\n    parameter.\n\n    **Example**: Using the `each` function with an array and a two-parameter lambda\n\n    ~~~ puppet\n    # For the array $data, run a lambda using each item's index and value that creates a\n    # resource for each item.\n    $data = [\"routers\", \"servers\", \"workstations\"]\n    $data.each |$index, $value| {\n     notify { $value:\n       message => $index\n     }\n    }\n    # Puppet creates one resource for each of the three items in $data, each named after the\n    # item's value and containing a parameter using the item's index.\n    ~~~\n\n    When the first argument is a hash, Puppet passes its keys to the first parameter and its\n    values to the second parameter.\n\n    **Example**: Using the `each` function with a hash and a two-parameter lambda\n\n    ~~~ puppet\n    # For the hash $data, run a lambda using each item's key and value to create a resource\n    # for each item.\n    $data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n    $data.each |$key, $value| {\n     notify { $key:\n       message => $value\n     }\n    }\n    # Puppet creates one resource for each of the three items in $data, each named after the\n    # item's key and containing a parameter using the item's value.\n    ~~~\n\n    For an example that demonstrates how to create multiple `file` resources using `each`,\n    see the Puppet\n    [iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\n    documentation.\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('each')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/epp.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(:epp, :type => :rvalue, :arity => -2, :doc =>\n\"Evaluates an Embedded Puppet (EPP) template file and returns the rendered text\nresult as a String.\n\n`epp('<MODULE NAME>/<TEMPLATE FILE>', <PARAMETER HASH>)`\n\nThe first argument to this function should be a `<MODULE NAME>/<TEMPLATE FILE>`\nreference, which loads `<TEMPLATE FILE>` from `<MODULE NAME>`'s `templates`\ndirectory. In most cases, the last argument is optional; if used, it should be a\n[hash](https://puppet.com/docs/puppet/latest/lang_data_hash.html) that contains parameters to\npass to the template.\n\n- See the [template](https://puppet.com/docs/puppet/latest/lang_template.html) documentation\nfor general template usage information.\n- See the [EPP syntax](https://puppet.com/docs/puppet/latest/lang_template_epp.html)\ndocumentation for examples of EPP.\n\nFor example, to call the apache module's `templates/vhost/_docroot.epp`\ntemplate and pass the `docroot` and `virtual_docroot` parameters, call the `epp`\nfunction like this:\n\n`epp('apache/vhost/_docroot.epp', { 'docroot' => '/var/www/html',\n'virtual_docroot' => '/var/www/example' })`\n\nThis function can also accept an absolute path, which can load a template file\nfrom anywhere on disk.\n\nPuppet produces a syntax error if you pass more parameters than are declared in\nthe template's parameter tag. When passing parameters to a template that\ncontains a parameter tag, use the same names as the tag's declared parameters.\n\nParameters are required only if they are declared in the called template's\nparameter tag without default values. Puppet produces an error if the `epp`\nfunction fails to pass any required parameter.\n\n- Since 4.0.0\") do |_args|\n  Puppet::Parser::Functions::Error.is4x('epp')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/fail.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :fail,\n  :arity => -1,\n  :doc => <<~DOC\n    Fail with a parse error. Any parameters will be stringified,\n    concatenated, and passed to the exception-handler.\n  DOC\n) do |vals|\n  vals = vals.collect(&:to_s).join(\" \") if vals.is_a? Array\n  raise Puppet::ParseError, vals.to_s\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_system'\n\nPuppet::Parser::Functions.newfunction(\n  :file, :arity => -2, :type => :rvalue,\n         :doc => \"Loads a file from a module and returns its contents as a string.\n\n  The argument to this function should be a `<MODULE NAME>/<FILE>`\n  reference, which will load `<FILE>` from a module's `files`\n  directory. (For example, the reference `mysql/mysqltuner.pl` will load the\n  file `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n\n  This function can also accept:\n\n  * An absolute path, which can load a file from anywhere on disk.\n  * Multiple arguments, which will return the contents of the **first** file\n  found, skipping any files that don't exist.\n  \"\n) do |vals|\n  path = nil\n  vals.each do |file|\n    found = Puppet::Parser::Files.find_file(file, compiler.environment)\n    if found && Puppet::FileSystem.exist?(found)\n      path = found\n      break\n    end\n  end\n\n  if path\n    Puppet::FileSystem.read_preserve_line_endings(path)\n  else\n    raise Puppet::ParseError, _(\"Could not find any files from %{values}\") % { values: vals.join(\", \") }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/filter.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :filter,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    Applies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    to every value in a data structure and returns an array or hash containing any elements\n    for which the lambda evaluates to a truthy value (not `false` or `undef`).\n\n    This function takes two mandatory arguments, in this order:\n\n    1. An array or hash the function will iterate over.\n    2. A lambda, which the function calls for each element in the first argument. It can\n    request one or two parameters.\n\n    **Example**: Using the `filter` function\n\n    `$filtered_data = $data.filter |$parameter| { <PUPPET CODE BLOCK> }`\n\n    or\n\n    `$filtered_data = filter($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\n    When the first argument (`$data` in the above example) is an array, Puppet passes each\n    value in turn to the lambda and returns an array containing the results.\n\n    **Example**: Using the `filter` function with an array and a one-parameter lambda\n\n    ~~~ puppet\n    # For the array $data, return an array containing the values that end with \"berry\"\n    $data = [\"orange\", \"blueberry\", \"raspberry\"]\n    $filtered_data = $data.filter |$items| { $items =~ /berry$/ }\n    # $filtered_data = [blueberry, raspberry]\n    ~~~\n\n    When the first argument is a hash, Puppet passes each key and value pair to the lambda\n    as an array in the form `[key, value]` and returns a hash containing the results.\n\n    **Example**: Using the `filter` function with a hash and a one-parameter lambda\n\n    ~~~ puppet\n    # For the hash $data, return a hash containing all values of keys that end with \"berry\"\n    $data = { \"orange\" => 0, \"blueberry\" => 1, \"raspberry\" => 2 }\n    $filtered_data = $data.filter |$items| { $items[0] =~ /berry$/ }\n    # $filtered_data = {blueberry => 1, raspberry => 2}\n    ~~~\n\n    When the first argument is an array and the lambda has two parameters, Puppet passes the\n    array's indexes (enumerated from 0) in the first parameter and its values in the second\n    parameter.\n\n    **Example**: Using the `filter` function with an array and a two-parameter lambda\n\n    ~~~ puppet\n    # For the array $data, return an array of all keys that both end with \"berry\" and have\n    # an even-numbered index\n    $data = [\"orange\", \"blueberry\", \"raspberry\"]\n    $filtered_data = $data.filter |$indexes, $values| { $indexes % 2 == 0 and $values =~ /berry$/ }\n    # $filtered_data = [raspberry]\n    ~~~\n\n    When the first argument is a hash, Puppet passes its keys to the first parameter and its\n    values to the second parameter.\n\n    **Example**: Using the `filter` function with a hash and a two-parameter lambda\n\n    ~~~ puppet\n    # For the hash $data, return a hash of all keys that both end with \"berry\" and have\n    # values less than or equal to 1\n    $data = { \"orange\" => 0, \"blueberry\" => 1, \"raspberry\" => 2 }\n    $filtered_data = $data.filter |$keys, $values| { $keys =~ /berry$/ and $values <= 1 }\n    # $filtered_data = {blueberry => 1}\n    ~~~\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('filter')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/find_file.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :find_file,\n  :type => :rvalue,\n  :arity => -2,\n  :doc => <<~DOC\n    Finds an existing file from a module and returns its path.\n\n    The argument to this function should be a String as a `<MODULE NAME>/<FILE>`\n    reference, which will search for `<FILE>` relative to a module's `files`\n    directory. (For example, the reference `mysql/mysqltuner.pl` will search for the\n    file `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n\n    This function can also accept:\n\n    * An absolute String path, which will check for the existence of a file from anywhere on disk.\n    * Multiple String arguments, which will return the path of the **first** file\n      found, skipping non existing files.\n    * An array of string paths, which will return the path of the **first** file\n      found from the given paths in the array, skipping non existing files.\n\n    The function returns `undef` if none of the given paths were found\n\n    - since 4.8.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('find_file')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/fqdn_rand.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest/md5'\nrequire 'digest/sha2'\n\nPuppet::Parser::Functions.newfunction(:fqdn_rand, :arity => -2, :type => :rvalue, :doc =>\n  \"Usage: `fqdn_rand(MAX, [SEED], [DOWNCASE])`. MAX is required and must be a positive\n  integer; SEED is optional and may be any number or string; DOWNCASE is optional\n  and should be a boolean true or false.\n\n  Generates a random Integer number greater than or equal to 0 and less than MAX,\n  combining the `$fqdn` fact and the value of SEED for repeatable randomness.\n  (That is, each node will get a different random number from this function, but\n  a given node's result will be the same every time unless its hostname changes.) If\n  DOWNCASE is true, then the `fqdn` fact will be downcased when computing the value\n  so that the result is not sensitive to the case of the `fqdn` fact.\n\n  This function is usually used for spacing out runs of resource-intensive cron\n  tasks that run on many nodes, which could cause a thundering herd or degrade\n  other services if they all fire at once. Adding a SEED can be useful when you\n  have more than one such task and need several unrelated random numbers per\n  node. (For example, `fqdn_rand(30)`, `fqdn_rand(30, 'expensive job 1')`, and\n  `fqdn_rand(30, 'expensive job 2')` will produce totally different numbers.)\") do |args|\n  max = args.shift.to_i\n  initial_seed = args.shift\n  downcase = !!args.shift\n\n  fqdn = self['facts'].dig('networking', 'fqdn')\n  fqdn = fqdn.downcase if downcase\n\n  # Puppet 5.4's fqdn_rand function produces a different value than earlier versions\n  # for the same set of inputs.\n  # This causes problems because the values are often written into service configuration files.\n  # When they change, services get notified and restart.\n\n  # Restoring previous fqdn_rand behavior of calculating its seed value using MD5\n  # when running on a non-FIPS enabled platform and only using SHA256 on FIPS enabled\n  # platforms.\n  if Puppet::Util::Platform.fips_enabled?\n    seed = Digest::SHA256.hexdigest([fqdn, max, initial_seed].join(':')).hex\n  else\n    seed = Digest::MD5.hexdigest([fqdn, max, initial_seed].join(':')).hex\n  end\n\n  Puppet::Util.deterministic_rand_int(seed, max)\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/generate.rb",
    "content": "# frozen_string_literal: true\n\n# Runs an external command and returns the results\nPuppet::Parser::Functions.newfunction(:generate, :arity => -2, :type => :rvalue,\n                                                 :doc => \"Calls an external command on the Puppet master and returns\n    the results of the command. Any arguments are passed to the external command as\n    arguments. If the generator does not exit with return code of 0,\n    the generator is considered to have failed and a parse error is\n    thrown. Generators can only have file separators, alphanumerics, dashes,\n    and periods in them. This function will attempt to protect you from\n    malicious generator calls (e.g., those with '..' in them), but it can\n    never be entirely safe. No subshell is used to execute\n    generators, so all shell metacharacters are passed directly to\n    the generator, and all metacharacters are returned by the function.\n    Consider cleaning white space from any string generated.\") do |args|\n  # TRANSLATORS \"fully qualified\" refers to a fully qualified file system path\n  raise Puppet::ParseError, _(\"Generators must be fully qualified\") unless Puppet::Util.absolute_path?(args[0])\n\n  if Puppet::Util::Platform.windows?\n    valid = args[0] =~ %r{^[a-z]:(?:[/\\\\][-.~\\w]+)+$}i\n  else\n    valid = args[0] =~ %r{^[-/\\w.+]+$}\n  end\n\n  unless valid\n    raise Puppet::ParseError, _(\"Generators can only contain alphanumerics, file separators, and dashes\")\n  end\n\n  if args[0] =~ /\\.\\./\n    raise Puppet::ParseError, _(\"Can not use generators with '..' in them.\")\n  end\n\n  begin\n    dir = File.dirname(args[0])\n    Puppet::Util::Execution.execute(args, failonfail: true, combine: true, cwd: dir).to_str\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::ParseError, _(\"Failed to execute generator %{generator}: %{detail}\") % { generator: args[0], detail: detail }, detail.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/hiera.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera_puppet'\n\nmodule Puppet::Parser::Functions\n  newfunction(\n    :hiera,\n    :type => :rvalue,\n    :arity => -2,\n    :doc => <<~DOC\n      Performs a standard priority lookup of the hierarchy and returns the most specific value\n      for a given key. The returned value can be any type of data.\n\n      The function takes up to three arguments, in this order:\n\n      1. A string key that Hiera searches for in the hierarchy. **Required**.\n      2. An optional default value to return if Hiera doesn't find anything matching the key.\n          * If this argument isn't provided and this function results in a lookup failure, Puppet\n          fails with a compilation error.\n      3. The optional name of an arbitrary\n      [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n      top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n          * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n          searching the rest of the hierarchy.\n\n      The `hiera` function does **not** find all matches throughout a hierarchy, instead\n      returining the first specific value starting at the top of the hierarchy. To search\n      throughout a hierarchy, use the `hiera_array` or `hiera_hash` functions.\n\n      **Example**: Using `hiera`\n\n      ~~~ yaml\n      # Assuming hiera.yaml\n      # :hierarchy:\n      #   - web01.example.com\n      #   - common\n\n      # Assuming web01.example.com.yaml:\n      # users:\n      #   - \"Amy Barry\"\n      #   - \"Carrie Douglas\"\n\n      # Assuming common.yaml:\n      users:\n        admins:\n          - \"Edith Franklin\"\n          - \"Ginny Hamilton\"\n        regular:\n          - \"Iris Jackson\"\n          - \"Kelly Lambert\"\n      ~~~\n\n      ~~~ puppet\n      # Assuming we are not web01.example.com:\n\n      $users = hiera('users', undef)\n\n      # $users contains {admins  => [\"Edith Franklin\", \"Ginny Hamilton\"],\n      #                  regular => [\"Iris Jackson\", \"Kelly Lambert\"]}\n      ~~~\n\n      You can optionally generate the default value with a\n      [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n      takes one parameter.\n\n      **Example**: Using `hiera` with a lambda\n\n      ~~~ puppet\n      # Assuming the same Hiera data as the previous example:\n\n      $users = hiera('users') | $key | { \"Key \\'${key}\\' not found\" }\n\n      # $users contains {admins  => [\"Edith Franklin\", \"Ginny Hamilton\"],\n      #                  regular => [\"Iris Jackson\", \"Kelly Lambert\"]}\n      # If hiera couldn't match its key, it would return the lambda result,\n      # \"Key 'users' not found\".\n      ~~~\n\n      The returned value's data type depends on the types of the results. In the example\n      above, Hiera matches the 'users' key and returns it as a hash.\n\n      The `hiera` function is deprecated in favor of using `lookup` and will be removed in 6.0.0.\n      See https://puppet.com/docs/puppet/#{Puppet.minor_version}/deprecated_language.html.\n      Replace the calls as follows:\n\n      | from  | to |\n      | ----  | ---|\n      | hiera($key) | lookup($key) |\n      | hiera($key, $default) | lookup($key, { 'default_value' => $default }) |\n      | hiera($key, $default, $level) | override level not supported |\n\n      Note that calls using the 'override level' option are not directly supported by 'lookup' and the produced\n      result must be post processed to get exactly the same result, for example using simple hash/array `+` or\n      with calls to stdlib's `deep_merge` function depending on kind of hiera call and setting of merge in hiera.yaml.\n\n      See\n      [the documentation](https://puppet.com/docs/hiera/latest/puppet.html#hiera-lookup-functions)\n      for more information about Hiera lookup functions.\n\n      - Since 4.0.0\n    DOC\n  ) do |*_args|\n    Error.is4x('hiera')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/hiera_array.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera_puppet'\n\nmodule Puppet::Parser::Functions\n  newfunction(\n    :hiera_array,\n    :type => :rvalue,\n    :arity => -2,\n    :doc => <<~DOC\n      Finds all matches of a key throughout the hierarchy and returns them as a single flattened\n      array of unique values. If any of the matched values are arrays, they're flattened and\n      included in the results. This is called an\n      [array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge).\n\n      The `hiera_array` function takes up to three arguments, in this order:\n\n      1. A string key that Hiera searches for in the hierarchy. **Required**.\n      2. An optional default value to return if Hiera doesn't find anything matching the key.\n          * If this argument isn't provided and this function results in a lookup failure, Puppet\n          fails with a compilation error.\n      3. The optional name of an arbitrary\n      [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n      top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n          * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n          searching the rest of the hierarchy.\n\n      **Example**: Using `hiera_array`\n\n      ~~~ yaml\n      # Assuming hiera.yaml\n      # :hierarchy:\n      #   - web01.example.com\n      #   - common\n\n      # Assuming common.yaml:\n      # users:\n      #   - 'cdouglas = regular'\n      #   - 'efranklin = regular'\n\n      # Assuming web01.example.com.yaml:\n      # users: 'abarry = admin'\n      ~~~\n\n      ~~~ puppet\n      $allusers = hiera_array('users', undef)\n\n      # $allusers contains [\"cdouglas = regular\", \"efranklin = regular\", \"abarry = admin\"].\n      ~~~\n\n      You can optionally generate the default value with a\n      [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n      takes one parameter.\n\n      **Example**: Using `hiera_array` with a lambda\n\n      ~~~ puppet\n      # Assuming the same Hiera data as the previous example:\n\n      $allusers = hiera_array('users') | $key | { \"Key \\'${key}\\' not found\" }\n\n      # $allusers contains [\"cdouglas = regular\", \"efranklin = regular\", \"abarry = admin\"].\n      # If hiera_array couldn't match its key, it would return the lambda result,\n      # \"Key 'users' not found\".\n      ~~~\n\n      `hiera_array` expects that all values returned will be strings or arrays. If any matched\n      value is a hash, Puppet raises a type mismatch error.\n\n      `hiera_array` is deprecated in favor of using `lookup` and will be removed in 6.0.0.\n      See https://puppet.com/docs/puppet/#{Puppet.minor_version}/deprecated_language.html.\n      Replace the calls as follows:\n\n      | from  | to |\n      | ----  | ---|\n      | hiera_array($key) | lookup($key, { 'merge' => 'unique' }) |\n      | hiera_array($key, $default) | lookup($key, { 'default_value' => $default, 'merge' => 'unique' }) |\n      | hiera_array($key, $default, $level) | override level not supported |\n\n      Note that calls using the 'override level' option are not directly supported by 'lookup' and the produced\n      result must be post processed to get exactly the same result, for example using simple hash/array `+` or\n      with calls to stdlib's `deep_merge` function depending on kind of hiera call and setting of merge in hiera.yaml.\n\n      See\n      [the documentation](https://puppet.com/docs/hiera/latest/puppet.html#hiera-lookup-functions)\n      for more information about Hiera lookup functions.\n\n      - Since 4.0.0\n    DOC\n  ) do |*_args|\n    Error.is4x('hiera_array')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/hiera_hash.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera_puppet'\n\nmodule Puppet::Parser::Functions\n  newfunction(\n    :hiera_hash,\n    :type => :rvalue,\n    :arity => -2,\n    :doc => <<~DOC\n      Finds all matches of a key throughout the hierarchy and returns them in a merged hash.\n      If any of the matched hashes share keys, the final hash uses the value from the\n      highest priority match. This is called a\n      [hash merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#hash-merge).\n\n      The merge strategy is determined by Hiera's\n      [`:merge_behavior`](https://puppet.com/docs/hiera/latest/configuring.html#mergebehavior)\n      setting.\n\n      The `hiera_hash` function takes up to three arguments, in this order:\n\n      1. A string key that Hiera searches for in the hierarchy. **Required**.\n      2. An optional default value to return if Hiera doesn't find anything matching the key.\n          * If this argument isn't provided and this function results in a lookup failure, Puppet\n          fails with a compilation error.\n      3. The optional name of an arbitrary\n      [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n      top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n          * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n          searching the rest of the hierarchy.\n\n      **Example**: Using `hiera_hash`\n\n      ~~~ yaml\n      # Assuming hiera.yaml\n      # :hierarchy:\n      #   - web01.example.com\n      #   - common\n\n      # Assuming common.yaml:\n      # users:\n      #   regular:\n      #     'cdouglas': 'Carrie Douglas'\n\n      # Assuming web01.example.com.yaml:\n      # users:\n      #   administrators:\n      #     'aberry': 'Amy Berry'\n      ~~~\n\n      ~~~ puppet\n      # Assuming we are not web01.example.com:\n\n      $allusers = hiera_hash('users', undef)\n\n      # $allusers contains {regular => {\"cdouglas\" => \"Carrie Douglas\"},\n      #                     administrators => {\"aberry\" => \"Amy Berry\"}}\n      ~~~\n\n      You can optionally generate the default value with a\n      [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n      takes one parameter.\n\n      **Example**: Using `hiera_hash` with a lambda\n\n      ~~~ puppet\n      # Assuming the same Hiera data as the previous example:\n\n      $allusers = hiera_hash('users') | $key | { \"Key \\'${key}\\' not found\" }\n\n      # $allusers contains {regular => {\"cdouglas\" => \"Carrie Douglas\"},\n      #                     administrators => {\"aberry\" => \"Amy Berry\"}}\n      # If hiera_hash couldn't match its key, it would return the lambda result,\n      # \"Key 'users' not found\".\n      ~~~\n\n      `hiera_hash` expects that all values returned will be hashes. If any of the values\n      found in the data sources are strings or arrays, Puppet raises a type mismatch error.\n\n      `hiera_hash` is deprecated in favor of using `lookup` and will be removed in 6.0.0.\n      See  https://puppet.com/docs/puppet/#{Puppet.minor_version}/deprecated_language.html.\n      Replace the calls as follows:\n\n      | from  | to |\n      | ----  | ---|\n      | hiera_hash($key) | lookup($key, { 'merge' => 'hash' }) |\n      | hiera_hash($key, $default) | lookup($key, { 'default_value' => $default, 'merge' => 'hash' }) |\n      | hiera_hash($key, $default, $level) | override level not supported |\n\n      Note that calls using the 'override level' option are not directly supported by 'lookup' and the produced\n      result must be post processed to get exactly the same result, for example using simple hash/array `+` or\n      with calls to stdlib's `deep_merge` function depending on kind of hiera call and setting of merge in hiera.yaml.\n\n      See\n      [the documentation](https://puppet.com/docs/hiera/latest/puppet.html#hiera-lookup-functions)\n      for more information about Hiera lookup functions.\n\n      - Since 4.0.0\n    DOC\n  ) do |*_args|\n    Error.is4x('hiera_hash')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/hiera_include.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera_puppet'\n\nmodule Puppet::Parser::Functions\n  newfunction(\n    :hiera_include,\n    :arity => -2,\n    :doc => <<~DOC\n      Assigns classes to a node using an\n      [array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge)\n      that retrieves the value for a user-specified key from Hiera's data.\n\n      The `hiera_include` function requires:\n\n      - A string key name to use for classes.\n      - A call to this function (i.e. `hiera_include('classes')`) in your environment's\n      `sites.pp` manifest, outside of any node definitions and below any top-scope variables\n      that Hiera uses in lookups.\n      - `classes` keys in the appropriate Hiera data sources, with an array for each\n      `classes` key and each value of the array containing the name of a class.\n\n      The function takes up to three arguments, in this order:\n\n      1. A string key that Hiera searches for in the hierarchy. **Required**.\n      2. An optional default value to return if Hiera doesn't find anything matching the key.\n          * If this argument isn't provided and this function results in a lookup failure, Puppet\n          fails with a compilation error.\n      3. The optional name of an arbitrary\n      [hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\n      top of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n          * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n          searching the rest of the hierarchy.\n\n      The function uses an\n      [array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge)\n      to retrieve the `classes` array, so every node gets every class from the hierarchy.\n\n      **Example**: Using `hiera_include`\n\n      ~~~ yaml\n      # Assuming hiera.yaml\n      # :hierarchy:\n      #   - web01.example.com\n      #   - common\n\n      # Assuming web01.example.com.yaml:\n      # classes:\n      #   - apache::mod::php\n\n      # Assuming common.yaml:\n      # classes:\n      #   - apache\n      ~~~\n\n      ~~~ puppet\n      # In site.pp, outside of any node definitions and below any top-scope variables:\n      hiera_include('classes', undef)\n\n      # Puppet assigns the apache and apache::mod::php classes to the web01.example.com node.\n      ~~~\n\n      You can optionally generate the default value with a\n      [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\n      takes one parameter.\n\n      **Example**: Using `hiera_include` with a lambda\n\n      ~~~ puppet\n      # Assuming the same Hiera data as the previous example:\n\n      # In site.pp, outside of any node definitions and below any top-scope variables:\n      hiera_include('classes') | $key | {\"Key \\'${key}\\' not found\" }\n\n      # Puppet assigns the apache and apache::mod::php classes to the web01.example.com node.\n      # If hiera_include couldn't match its key, it would return the lambda result,\n      # \"Key 'classes' not found\".\n      ~~~\n\n      `hiera_include` is deprecated in favor of using a combination of `include` and `lookup` and will be\n      removed in Puppet 6.0.0. Replace the calls as follows:\n\n      | from  | to |\n      | ----  | ---|\n      | hiera_include($key) | include(lookup($key, { 'merge' => 'unique' })) |\n      | hiera_include($key, $default) | include(lookup($key, { 'default_value' => $default, 'merge' => 'unique' })) |\n      | hiera_include($key, $default, $level) | override level not supported |\n\n      See\n      [the Upgrading to Hiera 5 migration guide](https://puppet.com/docs/puppet/5.5/hiera_migrate.html)\n      for more information.\n\n      Note that calls using the 'override level' option are not directly supported by 'lookup' and the produced\n      result must be post processed to get exactly the same result, for example using simple hash/array `+` or\n      with calls to stdlib's `deep_merge` function depending on kind of hiera call and setting of merge in hiera.yaml.\n\n      - Since 4.0.0\n    DOC\n  ) do |*_args|\n    Error.is4x('hiera_include')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/include.rb",
    "content": "# frozen_string_literal: true\n\n# Include the specified classes\nPuppet::Parser::Functions.newfunction(:include, :arity => -2, :doc =>\n\"Declares one or more classes, causing the resources in them to be\nevaluated and added to the catalog. Accepts a class name, an array of class\nnames, or a comma-separated list of class names.\n\nThe `include` function can be used multiple times on the same class and will\nonly declare a given class once. If a class declared with `include` has any\nparameters, Puppet will automatically look up values for them in Hiera, using\n`<class name>::<parameter name>` as the lookup key.\n\nContrast this behavior with resource-like class declarations\n(`class {'name': parameter => 'value',}`), which must be used in only one place\nper class and can directly set parameters. You should avoid using both `include`\nand resource-like declarations with the same class.\n\nThe `include` function does not cause classes to be contained in the class\nwhere they are declared. For that, see the `contain` function. It also\ndoes not create a dependency relationship between the declared class and the\nsurrounding class; for that, see the `require` function.\n\nYou must use the class's full name;\nrelative names are not allowed. In addition to names in string form,\nyou may also directly use Class and Resource Type values that are produced by\nthe future parser's resource and relationship expressions.\n\n- Since < 3.0.0\n- Since 4.0.0 support for class and resource type values, absolute names\n- Since 4.7.0 returns an Array[Type[Class]] of all included classes\n\") do |classes|\n  call_function('include', classes)\n  # TRANSLATORS \"function_include\", \"Scope\", and \"Scope#call_function\" refer to Puppet internals and should not be translated\n  Puppet.warn_once('deprecations', '3xfunction#include', _(\"Calling function_include via the Scope class is deprecated. Use Scope#call_function instead\"))\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/inline_epp.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(:inline_epp, :type => :rvalue, :arity => -2, :doc =>\n\"Evaluates an Embedded Puppet (EPP) template string and returns the rendered\ntext result as a String.\n\n`inline_epp('<EPP TEMPLATE STRING>', <PARAMETER HASH>)`\n\nThe first argument to this function should be a string containing an EPP\ntemplate. In most cases, the last argument is optional; if used, it should be a\n[hash](https://puppet.com/docs/puppet/latest/lang_data_hash.html) that contains parameters to\npass to the template.\n\n- See the [template](https://puppet.com/docs/puppet/latest/lang_template.html) documentation\nfor general template usage information.\n- See the [EPP syntax](https://puppet.com/docs/puppet/latest/lang_template_epp.html)\ndocumentation for examples of EPP.\n\nFor example, to evaluate an inline EPP template and pass it the `docroot` and\n`virtual_docroot` parameters, call the `inline_epp` function like this:\n\n`inline_epp('docroot: <%= $docroot %> Virtual docroot: <%= $virtual_docroot %>',\n{ 'docroot' => '/var/www/html', 'virtual_docroot' => '/var/www/example' })`\n\nPuppet produces a syntax error if you pass more parameters than are declared in\nthe template's parameter tag. When passing parameters to a template that\ncontains a parameter tag, use the same names as the tag's declared parameters.\n\nParameters are required only if they are declared in the called template's\nparameter tag without default values. Puppet produces an error if the\n`inline_epp` function fails to pass any required parameter.\n\nAn inline EPP template should be written as a single-quoted string or\n[heredoc](https://puppet.com/docs/puppet/latest/lang_data_string.html#heredocs).\nA double-quoted string is subject to expression interpolation before the string\nis parsed as an EPP template.\n\nFor example, to evaluate an inline EPP template using a heredoc, call the\n`inline_epp` function like this:\n\n~~~ puppet\n# Outputs 'Hello given argument planet!'\ninline_epp(@(END), { x => 'given argument' })\n<%- | $x, $y = planet | -%>\nHello <%= $x %> <%= $y %>!\nEND\n~~~\n\n- Since 3.5\n- Requires [future parser](https://puppet.com/docs/puppet/3.8/experiments_future.html) in Puppet 3.5 to 3.8\") do |_arguments|\n  Puppet::Parser::Functions::Error.is4x('inline_epp')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/inline_template.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(:inline_template, :type => :rvalue, :arity => -2, :doc =>\n  \"Evaluate a template string and return its value.  See\n  [the templating docs](https://puppet.com/docs/puppet/latest/lang_template.html) for\n  more information. Note that if multiple template strings are specified, their\n  output is all concatenated and returned as the output of the function.\") do |vals|\n  if Puppet[:tasks]\n    raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n      Puppet::Pops::Issues::FEATURE_NOT_SUPPORTED_WHEN_SCRIPTING,\n      { :feature => 'ERB inline_template' }\n    )\n  end\n\n  require 'erb'\n\n  vals.collect do |string|\n    # Use a wrapper, so the template can't get access to the full\n    # Scope object.\n\n    wrapper = Puppet::Parser::TemplateWrapper.new(self)\n    begin\n      wrapper.result(string)\n    rescue => detail\n      raise Puppet::ParseError, _(\"Failed to parse inline template: %{detail}\") % { detail: detail }, detail.backtrace\n    end\n  end.join(\"\")\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/lest.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :lest,\n  :type => :rvalue,\n  :arity => -2,\n  :doc => <<~DOC\n    Call a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    (which should accept no arguments) if the argument given to the function is `undef`.\n    Returns the result of calling the lambda if the argument is `undef`, otherwise the\n    given argument.\n\n    The `lest` function is useful in a chain of `then` calls, or in general\n    as a guard against `undef` values. The function can be used to call `fail`, or to\n    return a default value.\n\n    These two expressions are equivalent:\n\n    ```puppet\n    if $x == undef { do_things() }\n    lest($x) || { do_things() }\n    ```\n\n    **Example:** Using the `lest` function\n\n    ```puppet\n    $data = {a => [ b, c ] }\n    notice $data.dig(a, b, c)\n     .then |$x| { $x * 2 }\n     .lest || { fail(\"no value for $data[a][b][c]\" }\n    ```\n\n    Would fail the operation because $data[a][b][c] results in `undef`\n    (there is no `b` key in `a`).\n\n    In contrast - this example:\n\n    ```puppet\n    $data = {a => { b => { c => 10 } } }\n    notice $data.dig(a, b, c)\n     .then |$x| { $x * 2 }\n     .lest || { fail(\"no value for $data[a][b][c]\" }\n    ```\n\n    Would notice the value `20`\n\n    * Since 4.5.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('lest')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/lookup.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Parser::Functions\n  newfunction(:lookup, :type => :rvalue, :arity => -2, :doc => <<~'ENDHEREDOC') do |_args|\n    Uses the Puppet lookup system to retrieve a value for a given key. By default,\n    this returns the first value found (and fails compilation if no values are\n    available), but you can configure it to merge multiple values into one, fail\n    gracefully, and more.\n\n    When looking up a key, Puppet will search up to three tiers of data, in the\n    following order:\n\n    1. Hiera.\n    2. The current environment's data provider.\n    3. The indicated module's data provider, if the key is of the form\n       `<MODULE NAME>::<SOMETHING>`.\n\n    #### Arguments\n\n    You must provide the name of a key to look up, and can optionally provide other\n    arguments. You can combine these arguments in the following ways:\n\n    * `lookup( <NAME>, [<VALUE TYPE>], [<MERGE BEHAVIOR>], [<DEFAULT VALUE>] )`\n    * `lookup( [<NAME>], <OPTIONS HASH> )`\n    * `lookup( as above ) |$key| { # lambda returns a default value }`\n\n    Arguments in `[square brackets]` are optional.\n\n    The arguments accepted by `lookup` are as follows:\n\n    1. `<NAME>` (string or array) --- The name of the key to look up.\n        * This can also be an array of keys. If Puppet doesn't find anything for the\n        first key, it will try again with the subsequent ones, only resorting to a\n        default value if none of them succeed.\n    2. `<VALUE TYPE>` (data type) --- A\n    [data type](https://puppet.com/docs/puppet/latest/lang_data_type.html)\n    that must match the retrieved value; if not, the lookup (and catalog\n    compilation) will fail. Defaults to `Data` (accepts any normal value).\n    3. `<MERGE BEHAVIOR>` (string or hash; see **\"Merge Behaviors\"** below) ---\n    Whether (and how) to combine multiple values. If present, this overrides any\n    merge behavior specified in the data sources. Defaults to no value; Puppet will\n    use merge behavior from the data sources if present, and will otherwise do a\n    first-found lookup.\n    4. `<DEFAULT VALUE>` (any normal value) --- If present, `lookup` returns this\n    when it can't find a normal value. Default values are never merged with found\n    values. Like a normal value, the default must match the value type. Defaults to\n    no value; if Puppet can't find a normal value, the lookup (and compilation) will\n    fail.\n    5. `<OPTIONS HASH>` (hash) --- Alternate way to set the arguments above, plus\n    some less-common extra options. If you pass an options hash, you can't combine\n    it with any regular arguments (except `<NAME>`). An options hash can have the\n    following keys:\n        * `'name'` --- Same as `<NAME>` (argument 1). You can pass this as an\n        argument or in the hash, but not both.\n        * `'value_type'` --- Same as `<VALUE TYPE>` (argument 2).\n        * `'merge'` --- Same as `<MERGE BEHAVIOR>` (argument 3).\n        * `'default_value'` --- Same as `<DEFAULT VALUE>` (argument 4).\n        * `'default_values_hash'` (hash) --- A hash of lookup keys and default\n        values. If Puppet can't find a normal value, it will check this hash for the\n        requested key before giving up. You can combine this with `default_value` or\n        a lambda, which will be used if the key isn't present in this hash. Defaults\n        to an empty hash.\n        * `'override'` (hash) --- A hash of lookup keys and override values. Puppet\n        will check for the requested key in the overrides hash _first;_ if found, it\n        returns that value as the _final_ value, ignoring merge behavior. Defaults\n        to an empty hash.\n\n    Finally, `lookup` can take a lambda, which must accept a single parameter.\n    This is yet another way to set a default value for the lookup; if no results are\n    found, Puppet will pass the requested key to the lambda and use its result as\n    the default value.\n\n    #### Merge Behaviors\n\n    Puppet lookup uses a hierarchy of data sources, and a given key might have\n    values in multiple sources. By default, Puppet returns the first value it finds,\n    but it can also continue searching and merge all the values together.\n\n    > **Note:** Data sources can use the special `lookup_options` metadata key to\n    request a specific merge behavior for a key. The `lookup` function will use that\n    requested behavior unless you explicitly specify one.\n\n    The valid merge behaviors are:\n\n    * `'first'` --- Returns the first value found, with no merging. Puppet lookup's\n    default behavior.\n    * `'unique'` (called \"array merge\" in classic Hiera) --- Combines any number of\n    arrays and scalar values to return a merged, flattened array with all duplicate\n    values removed. The lookup will fail if any hash values are found.\n    * `'hash'` --- Combines the keys and values of any number of hashes to return a\n    merged hash. If the same key exists in multiple source hashes, Puppet will use\n    the value from the highest-priority data source; it won't recursively merge the\n    values.\n    * `'deep'` --- Combines the keys and values of any number of hashes to return a\n    merged hash. If the same key exists in multiple source hashes, Puppet will\n    recursively merge hash or array values (with duplicate values removed from\n    arrays). For conflicting scalar values, the highest-priority value will win.\n    * `{'strategy' => 'first|unique|hash'}` --- Same as the string versions of these\n    merge behaviors.\n    * `{'strategy' => 'deep', <DEEP OPTION> => <VALUE>, ...}` --- Same as `'deep'`,\n    but can adjust the merge with additional options. The available options are:\n        * `'knockout_prefix'` (string or undef) --- A string prefix to indicate a\n        value should be _removed_ from the final result. Defaults to `undef`, which\n        disables this feature.\n        * `'sort_merged_arrays'` (boolean) --- Whether to sort all arrays that are\n        merged together. Defaults to `false`.\n        * `'merge_hash_arrays'` (boolean) --- Whether to merge hashes within arrays.\n        Defaults to `false`.\n\n    #### Examples\n\n    Look up a key and return the first value found:\n\n        lookup('ntp::service_name')\n\n    Do a unique merge lookup of class names, then add all of those classes to the\n    catalog (like `hiera_include`):\n\n        lookup('classes', Array[String], 'unique').include\n\n    Do a deep hash merge lookup of user data, but let higher priority sources\n    remove values by prefixing them with `--`:\n\n        lookup( { 'name'  => 'users',\n                  'merge' => {\n                    'strategy'        => 'deep',\n                    'knockout_prefix' => '--',\n                  },\n        })\n\n  ENDHEREDOC\n    Error.is4x('lookup')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/map.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :map,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    Applies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    to every value in a data structure and returns an array containing the results.\n\n    This function takes two mandatory arguments, in this order:\n\n    1. An array or hash the function will iterate over.\n    2. A lambda, which the function calls for each element in the first argument. It can\n    request one or two parameters.\n\n    **Example**: Using the `map` function\n\n    `$transformed_data = $data.map |$parameter| { <PUPPET CODE BLOCK> }`\n\n    or\n\n    `$transformed_data = map($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\n    When the first argument (`$data` in the above example) is an array, Puppet passes each\n    value in turn to the lambda.\n\n    **Example**: Using the `map` function with an array and a one-parameter lambda\n\n    ~~~ puppet\n    # For the array $data, return an array containing each value multiplied by 10\n    $data = [1,2,3]\n    $transformed_data = $data.map |$items| { $items * 10 }\n    # $transformed_data contains [10,20,30]\n    ~~~\n\n    When the first argument is a hash, Puppet passes each key and value pair to the lambda\n    as an array in the form `[key, value]`.\n\n    **Example**: Using the `map` function with a hash and a one-parameter lambda\n\n    ~~~ puppet\n    # For the hash $data, return an array containing the keys\n    $data = {'a'=>1,'b'=>2,'c'=>3}\n    $transformed_data = $data.map |$items| { $items[0] }\n    # $transformed_data contains ['a','b','c']\n    ~~~\n\n    When the first argument is an array and the lambda has two parameters, Puppet passes the\n    array's indexes (enumerated from 0) in the first parameter and its values in the second\n    parameter.\n\n    **Example**: Using the `map` function with an array and a two-parameter lambda\n\n    ~~~ puppet\n    # For the array $data, return an array containing the indexes\n    $data = [1,2,3]\n    $transformed_data = $data.map |$index,$value| { $index }\n    # $transformed_data contains [0,1,2]\n    ~~~\n\n    When the first argument is a hash, Puppet passes its keys to the first parameter and its\n    values to the second parameter.\n\n    **Example**: Using the `map` function with a hash and a two-parameter lambda\n\n    ~~~ puppet\n    # For the hash $data, return an array containing each value\n    $data = {'a'=>1,'b'=>2,'c'=>3}\n    $transformed_data = $data.map |$key,$value| { $value }\n    # $transformed_data contains [1,2,3]\n    ~~~\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('map')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/match.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :match,\n  :arity => 2,\n  :doc => <<~DOC\n    Matches a regular expression against a string and returns an array containing the match\n    and any matched capturing groups.\n\n    The first argument is a string or array of strings. The second argument is either a\n    regular expression, regular expression represented as a string, or Regex or Pattern\n    data type that the function matches against the first argument.\n\n    The returned array contains the entire match at index 0, and each captured group at\n    subsequent index values. If the value or expression being matched is an array, the\n    function returns an array with mapped match results.\n\n    If the function doesn't find a match, it returns 'undef'.\n\n    **Example**: Matching a regular expression in a string\n\n    ~~~ ruby\n    $matches = \"abc123\".match(/[a-z]+[1-9]+/)\n    # $matches contains [abc123]\n    ~~~\n\n    **Example**: Matching a regular expressions with grouping captures in a string\n\n    ~~~ ruby\n    $matches = \"abc123\".match(/([a-z]+)([1-9]+)/)\n    # $matches contains [abc123, abc, 123]\n    ~~~\n\n    **Example**: Matching a regular expression with grouping captures in an array of strings\n\n    ~~~ ruby\n    $matches = [\"abc123\",\"def456\"].match(/([a-z]+)([1-9]+)/)\n    # $matches contains [[abc123, abc, 123], [def456, def, 456]]\n    ~~~\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('match')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/md5.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest/md5'\n\nPuppet::Parser::Functions.newfunction(:md5, :type => :rvalue, :arity => 1, :doc => \"Returns a MD5 hash value from a provided string.\") do |args|\n  Digest::MD5.hexdigest(args[0])\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/new.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :new,\n  :type => :rvalue,\n  :arity => -1,\n  :doc => <<~DOC\n    Creates a new instance/object of a given data type.\n\n    This function makes it possible to create new instances of\n    concrete data types. If a block is given it is called with the\n    just created instance as an argument.\n\n    Calling this function is equivalent to directly\n    calling the data type:\n\n    **Example:** `new` and calling type directly are equivalent\n\n    ```puppet\n    $a = Integer.new(\"42\")\n    $b = Integer(\"42\")\n    ```\n\n    These would both convert the string `\"42\"` to the decimal value `42`.\n\n    **Example:** arguments by position or by name\n\n    ```puppet\n    $a = Integer.new(\"42\", 8)\n    $b = Integer({from => \"42\", radix => 8})\n    ```\n\n    This would convert the octal (radix 8) number `\"42\"` in string form\n    to the decimal value `34`.\n\n    The new function supports two ways of giving the arguments:\n\n    * by name (using a hash with property to value mapping)\n    * by position (as regular arguments)\n\n    Note that it is not possible to create new instances of\n    some abstract data types (for example `Variant`). The data type `Optional[T]` is an\n    exception as it will create an instance of `T` or `undef` if the\n    value to convert is `undef`.\n\n    The arguments that can be given is determined by the data type.\n\n    > An assertion is always made that the produced value complies with the given type constraints.\n\n    **Example:** data type constraints are checked\n\n    ```puppet\n    Integer[0].new(\"-100\")\n    ```\n\n    Would fail with an assertion error (since value is less than 0).\n\n    The following sections show the arguments and conversion rules\n    per data type built into the Puppet Type System.\n\n    ### Conversion to Optional[T] and NotUndef[T]\n\n    Conversion to these data types is the same as a conversion to the type argument `T`.\n    In the case of `Optional[T]` it is accepted that the argument to convert may be `undef`.\n    It is however not acceptable to give other arguments (than `undef`) that cannot be\n    converted to `T`.\n\n    ### Conversion to Integer\n\n    A new `Integer` can be created from `Integer`, `Float`, `Boolean`, and `String` values.\n    For conversion from `String` it is possible to specify the radix (base).\n\n    ```puppet\n    type Radix = Variant[Default, Integer[2,2], Integer[8,8], Integer[10,10], Integer[16,16]]\n\n    function Integer.new(\n      String $value,\n      Radix $radix = 10,\n      Boolean $abs = false\n    )\n\n    function Integer.new(\n      Variant[Numeric, Boolean] $value,\n      Boolean $abs = false\n    )\n    ```\n\n    * When converting from `String` the default radix is 10.\n    * If radix is not specified an attempt is made to detect the radix from the start of the string:\n      * `0b` or `0B` is taken as radix 2.\n      * `0x` or `0X` is taken as radix 16.\n      * `0` as radix 8.\n      * All others are decimal.\n    * Conversion from `String` accepts an optional sign in the string.\n    * For hexadecimal (radix 16) conversion an optional leading \"0x\", or \"0X\" is accepted.\n    * For octal (radix 8) an optional leading \"0\" is accepted.\n    * For binary (radix 2) an optional leading \"0b\" or \"0B\" is accepted.\n    * When `radix` is set to `default`, the conversion is based on the leading.\n      characters in the string. A leading \"0\" for radix 8, a leading \"0x\", or \"0X\" for\n      radix 16, and leading \"0b\" or \"0B\" for binary.\n    * Conversion from `Boolean` results in 0 for `false` and 1 for `true`.\n    * Conversion from `Integer`, `Float`, and `Boolean` ignores the radix.\n    * `Float` value fractions are truncated (no rounding).\n    * When `abs` is set to `true`, the result will be an absolute integer.\n\n    Examples - Converting to Integer:\n\n    ```puppet\n    $a_number = Integer(\"0xFF\", 16)    # results in 255\n    $a_number = Integer(\"010\")         # results in 8\n    $a_number = Integer(\"010\", 10)     # results in 10\n    $a_number = Integer(true)          # results in 1\n    $a_number = Integer(-38, 10, true) # results in 38\n    ```\n\n    ### Conversion to Float\n\n    A new `Float` can be created from `Integer`, `Float`, `Boolean`, and `String` values.\n    For conversion from `String` both float and integer formats are supported.\n\n    ```puppet\n    function Float.new(\n      Variant[Numeric, Boolean, String] $value,\n      Boolean $abs = true\n    )\n    ```\n\n    * For an integer, the floating point fraction of `.0` is added to the value.\n    * A `Boolean` `true` is converted to 1.0, and a `false` to 0.0\n    * In `String` format, integer prefixes for hex and binary are understood (but not octal since\n      floating point in string format may start with a '0').\n    * When `abs` is set to `true`, the result will be an absolute floating point value.\n\n    ### Conversion to Numeric\n\n    A new `Integer` or `Float` can be created from `Integer`, `Float`, `Boolean` and\n    `String` values.\n\n    ```puppet\n    function Numeric.new(\n      Variant[Numeric, Boolean, String] $value,\n      Boolean $abs = true\n    )\n    ```\n\n    * If the value has a decimal period, or if given in scientific notation\n      (e/E), the result is a `Float`, otherwise the value is an `Integer`. The\n      conversion from `String` always uses a radix based on the prefix of the string.\n    * Conversion from `Boolean` results in 0 for `false` and 1 for `true`.\n    * When `abs` is set to `true`, the result will be an absolute `Float`or `Integer` value.\n\n    Examples - Converting to Numeric\n\n    ```puppet\n    $a_number = Numeric(true)        # results in 1\n    $a_number = Numeric(\"0xFF\")      # results in 255\n    $a_number = Numeric(\"010\")       # results in 8\n    $a_number = Numeric(\"3.14\")      # results in 3.14 (a float)\n    $a_number = Numeric(-42.3, true) # results in 42.3\n    $a_number = Numeric(-42, true)   # results in 42\n    ```\n\n    ### Conversion to Timespan\n\n    A new `Timespan` can be created from `Integer`, `Float`, `String`, and `Hash` values. Several variants of the constructor are provided.\n\n    #### Timespan from seconds\n\n    When a Float is used, the decimal part represents fractions of a second.\n\n    ```puppet\n    function Timespan.new(\n      Variant[Float, Integer] $value\n    )\n    ```\n\n    #### Timespan from days, hours, minutes, seconds, and fractions of a second\n\n    The arguments can be passed separately in which case the first four, days, hours, minutes, and seconds are mandatory and the rest are optional.\n    All values may overflow and/or be negative. The internal 128-bit nano-second integer is calculated as:\n\n    ```\n    (((((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000 + milliseconds) * 1000 + microseconds) * 1000 + nanoseconds\n    ```\n\n    ```puppet\n    function Timespan.new(\n      Integer $days, Integer $hours, Integer $minutes, Integer $seconds,\n      Integer $milliseconds = 0, Integer $microseconds = 0, Integer $nanoseconds = 0\n    )\n    ```\n\n    or, all arguments can be passed as a `Hash`, in which case all entries are optional:\n\n    ```puppet\n    function Timespan.new(\n      Struct[{\n        Optional[negative] => Boolean,\n        Optional[days] => Integer,\n        Optional[hours] => Integer,\n        Optional[minutes] => Integer,\n        Optional[seconds] => Integer,\n        Optional[milliseconds] => Integer,\n        Optional[microseconds] => Integer,\n        Optional[nanoseconds] => Integer\n      }] $hash\n    )\n    ```\n\n    #### Timespan from String and format directive patterns\n\n    The first argument is parsed using the format optionally passed as a string or array of strings. When an array is used, an attempt\n    will be made to parse the string using the first entry and then with each entry in succession until parsing succeeds. If the second\n    argument is omitted, an array of default formats will be used.\n\n    An exception is raised when no format was able to parse the given string.\n\n    ```puppet\n    function Timespan.new(\n      String $string, Variant[String[2],Array[String[2], 1]] $format = <default format>)\n    )\n    ```\n\n    the arguments may also be passed as a `Hash`:\n\n    ```puppet\n    function Timespan.new(\n      Struct[{\n        string => String[1],\n        Optional[format] => Variant[String[2],Array[String[2], 1]]\n      }] $hash\n    )\n    ```\n\n    The directive consists of a percent (%) character, zero or more flags, optional minimum field width and\n    a conversion specifier as follows:\n    ```\n    %[Flags][Width]Conversion\n    ```\n\n    ##### Flags:\n\n    | Flag  | Meaning\n    | ----  | ---------------\n    | -     | Don't pad numerical output\n    | _     | Use spaces for padding\n    | 0     | Use zeros for padding\n\n    ##### Format directives:\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | D | Number of Days |\n    | H | Hour of the day, 24-hour clock |\n    | M | Minute of the hour (00..59) |\n    | S | Second of the minute (00..59) |\n    | L | Millisecond of the second (000..999) |\n    | N | Fractional seconds digits |\n\n    The format directive that represents the highest magnitude in the format will be allowed to\n    overflow. I.e. if no \"%D\" is used but a \"%H\" is present, then the hours may be more than 23.\n\n    The default array contains the following patterns:\n\n    ```\n    ['%D-%H:%M:%S', '%D-%H:%M', '%H:%M:%S', '%H:%M']\n    ```\n\n    Examples - Converting to Timespan\n\n    ```puppet\n    $duration = Timespan(13.5)       # 13 seconds and 500 milliseconds\n    $duration = Timespan({days=>4})  # 4 days\n    $duration = Timespan(4, 0, 0, 2) # 4 days and 2 seconds\n    $duration = Timespan('13:20')    # 13 hours and 20 minutes (using default pattern)\n    $duration = Timespan('10:03.5', '%M:%S.%L') # 10 minutes, 3 seconds, and 5 milli-seconds\n    $duration = Timespan('10:03.5', '%M:%S.%N') # 10 minutes, 3 seconds, and 5 nano-seconds\n    ```\n\n    ### Conversion to Timestamp\n\n    A new `Timestamp` can be created from `Integer`, `Float`, `String`, and `Hash` values. Several variants of the constructor are provided.\n\n    #### Timestamp from seconds since epoch (1970-01-01 00:00:00 UTC)\n\n    When a Float is used, the decimal part represents fractions of a second.\n\n    ```puppet\n    function Timestamp.new(\n      Variant[Float, Integer] $value\n    )\n    ```\n\n    #### Timestamp from String and patterns consisting of format directives\n\n    The first argument is parsed using the format optionally passed as a string or array of strings. When an array is used, an attempt\n    will be made to parse the string using the first entry and then with each entry in succession until parsing succeeds. If the second\n    argument is omitted, an array of default formats will be used.\n\n    A third optional timezone argument can be provided. The first argument will then be parsed as if it represents a local time in that\n    timezone. The timezone can be any timezone that is recognized when using the '%z' or '%Z' formats, or the word 'current', in which\n    case the current timezone of the evaluating process will be used. The timezone argument is case insensitive.\n\n    The default timezone, when no argument is provided, or when using the keyword `default`, is 'UTC'.\n\n    It is illegal to provide a timezone argument other than `default` in combination with a format that contains '%z' or '%Z' since that\n    would introduce an ambiguity as to which timezone to use. The one extracted from the string, or the one provided as an argument.\n\n    An exception is raised when no format was able to parse the given string.\n\n    ```puppet\n    function Timestamp.new(\n      String $string,\n      Variant[String[2],Array[String[2], 1]] $format = <default format>,\n      String $timezone = default)\n    )\n    ```\n\n    the arguments may also be passed as a `Hash`:\n\n    ```puppet\n    function Timestamp.new(\n      Struct[{\n        string => String[1],\n        Optional[format] => Variant[String[2],Array[String[2], 1]],\n        Optional[timezone] => String[1]\n      }] $hash\n    )\n    ```\n\n    The directive consists of a percent (%) character, zero or more flags, optional minimum field width and\n    a conversion specifier as follows:\n    ```\n    %[Flags][Width]Conversion\n    ```\n\n    ##### Flags:\n\n    | Flag  | Meaning\n    | ----  | ---------------\n    | -     | Don't pad numerical output\n    | _     | Use spaces for padding\n    | 0     | Use zeros for padding\n    | #     | Change names to upper-case or change case of am/pm\n    | ^     | Use uppercase\n    | :     | Use colons for %z\n\n    ##### Format directives (names and padding can be altered using flags):\n\n    **Date (Year, Month, Day):**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | Y | Year with century, zero-padded to at least 4 digits |\n    | C | year / 100 (rounded down such as 20 in 2009) |\n    | y | year % 100 (00..99) |\n    | m | Month of the year, zero-padded (01..12) |\n    | B | The full month name (\"January\") |\n    | b | The abbreviated month name (\"Jan\") |\n    | h | Equivalent to %b |\n    | d | Day of the month, zero-padded (01..31) |\n    | e | Day of the month, blank-padded ( 1..31) |\n    | j | Day of the year (001..366) |\n\n    **Time (Hour, Minute, Second, Subsecond):**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | H | Hour of the day, 24-hour clock, zero-padded (00..23) |\n    | k | Hour of the day, 24-hour clock, blank-padded ( 0..23) |\n    | I | Hour of the day, 12-hour clock, zero-padded (01..12) |\n    | l | Hour of the day, 12-hour clock, blank-padded ( 1..12) |\n    | P | Meridian indicator, lowercase (\"am\" or \"pm\") |\n    | p | Meridian indicator, uppercase (\"AM\" or \"PM\") |\n    | M | Minute of the hour (00..59) |\n    | S | Second of the minute (00..60) |\n    | L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000 |\n    | N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified width are truncated to avoid carry up |\n\n    **Time (Hour, Minute, Second, Subsecond):**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | z   | Time zone as hour and minute offset from UTC (e.g. +0900) |\n    | :z  | hour and minute offset from UTC with a colon (e.g. +09:00) |\n    | ::z | hour, minute and second offset from UTC (e.g. +09:00:00) |\n    | Z   | Abbreviated time zone name or similar information.  (OS dependent) |\n\n    **Weekday:**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | A | The full weekday name (\"Sunday\") |\n    | a | The abbreviated name (\"Sun\") |\n    | u | Day of the week (Monday is 1, 1..7) |\n    | w | Day of the week (Sunday is 0, 0..6) |\n\n    **ISO 8601 week-based year and week number:**\n\n    The first week of YYYY starts with a Monday and includes YYYY-01-04.\n    The days in the year before the first week are in the last week of\n    the previous year.\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | G | The week-based year |\n    | g | The last 2 digits of the week-based year (00..99) |\n    | V | Week number of the week-based year (01..53) |\n\n    **Week number:**\n\n    The first week of YYYY that starts with a Sunday or Monday (according to %U\n    or %W). The days in the year before the first week are in week 0.\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | U | Week number of the year. The week starts with Sunday. (00..53) |\n    | W | Week number of the year. The week starts with Monday. (00..53) |\n\n    **Seconds since the Epoch:**\n\n    | Format | Meaning |\n    | s | Number of seconds since 1970-01-01 00:00:00 UTC. |\n\n    **Literal string:**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | n | Newline character (\\n) |\n    | t | Tab character (\\t) |\n    | % | Literal \"%\" character |\n\n    **Combination:**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | c | date and time (%a %b %e %T %Y) |\n    | D | Date (%m/%d/%y) |\n    | F | The ISO 8601 date format (%Y-%m-%d) |\n    | v | VMS date (%e-%^b-%4Y) |\n    | x | Same as %D |\n    | X | Same as %T |\n    | r | 12-hour time (%I:%M:%S %p) |\n    | R | 24-hour time (%H:%M) |\n    | T | 24-hour time (%H:%M:%S) |\n\n    The default array contains the following patterns:\n\n    When a timezone argument (other than `default`) is explicitly provided:\n\n    ```\n    ['%FT%T.L', '%FT%T', '%F']\n    ```\n\n    otherwise:\n\n    ```\n    ['%FT%T.%L %Z', '%FT%T %Z', '%F %Z', '%FT%T.L', '%FT%T', '%F']\n    ```\n\n    Examples - Converting to Timestamp\n\n    ```puppet\n    $ts = Timestamp(1473150899)                              # 2016-09-06 08:34:59 UTC\n    $ts = Timestamp({string=>'2015', format=>'%Y'})          # 2015-01-01 00:00:00.000 UTC\n    $ts = Timestamp('Wed Aug 24 12:13:14 2016', '%c')        # 2016-08-24 12:13:14 UTC\n    $ts = Timestamp('Wed Aug 24 12:13:14 2016 PDT', '%c %Z') # 2016-08-24 19:13:14.000 UTC\n    $ts = Timestamp('2016-08-24 12:13:14', '%F %T', 'PST')   # 2016-08-24 20:13:14.000 UTC\n    $ts = Timestamp('2016-08-24T12:13:14', default, 'PST')   # 2016-08-24 20:13:14.000 UTC\n\n    ```\n\n    ### Conversion to Type\n\n    A new `Type` can be create from its `String` representation.\n\n    **Example:** Creating a type from a string\n\n    ```puppet\n    $t = Type.new('Integer[10]')\n    ```\n\n    ### Conversion to String\n\n    Conversion to `String` is the most comprehensive conversion as there are many\n    use cases where a string representation is wanted. The defaults for the many options\n    have been chosen with care to be the most basic \"value in textual form\" representation.\n    The more advanced forms of formatting are intended to enable writing special purposes formatting\n    functions in the Puppet language.\n\n    A new string can be created from all other data types. The process is performed in\n    several steps - first the data type of the given value is inferred, then the resulting data type\n    is used to find the most significant format specified for that data type. And finally,\n    the found format is used to convert the given value.\n\n    The mapping from data type to format is referred to as the *format map*. This map\n    allows different formatting depending on type.\n\n    **Example:** Positive Integers in Hexadecimal prefixed with '0x', negative in Decimal\n\n    ```puppet\n    $format_map = {\n      Integer[default, 0] => \"%d\",\n      Integer[1, default] => \"%#x\"\n    }\n    String(\"-1\", $format_map)  # produces '-1'\n    String(\"10\", $format_map)  # produces '0xa'\n    ```\n\n    A format is specified on the form:\n\n    ```\n    %[Flags][Width][.Precision]Format\n    ```\n\n    `Width` is the number of characters into which the value should be fitted. This allocated space is\n    padded if value is shorter. By default it is space padded, and the flag `0` will cause padding with `0`\n    for numerical formats.\n\n    `Precision` is the number of fractional digits to show for floating point, and the maximum characters\n    included in a string format.\n\n    Note that all data type supports the formats `s` and `p` with the meaning \"default string representation\" and\n    \"default programmatic string representation\" (which for example means that a String is quoted in 'p' format).\n\n    #### Signatures of String conversion\n\n    ```puppet\n    type Format = Pattern[/^%([\\s\\+\\-#0\\[\\{<\\(\\|]*)([1-9][0-9]*)?(?:\\.([0-9]+))?([a-zA-Z])/]\n    type ContainerFormat = Struct[{\n      format         => Optional[String],\n      separator      => Optional[String],\n      separator2     => Optional[String],\n      string_formats => Hash[Type, Format]\n      }]\n    type TypeMap = Hash[Type, Variant[Format, ContainerFormat]]\n    type Formats = Variant[Default, String[1], TypeMap]\n\n    function String.new(\n      Any $value,\n      Formats $string_formats\n    )\n    ```\n\n    Where:\n\n    * `separator` is the string used to separate entries in an array, or hash (extra space should not be included at\n      the end), defaults to `\",\"`\n    * `separator2` is the separator between key and value in a hash entry (space padding should be included as\n      wanted), defaults to `\" => \"`.\n    * `string_formats` is a data type to format map for values contained in arrays and hashes - defaults to `{Any => \"%p\"}`. Note that\n      these nested formats are not applicable to data types that are containers; they are always formatted as per the top level\n      format specification.\n\n    **Example:** Simple Conversion to String (using defaults)\n\n    ```puppet\n    $str = String(10)      # produces '10'\n    $str = String([10])    # produces '[\"10\"]'\n    ```\n\n    **Example:** Simple Conversion to String specifying the format for the given value directly\n\n    ```puppet\n    $str = String(10, \"%#x\")    # produces '0x10'\n    $str = String([10], \"%(a\")  # produces '(\"10\")'\n    ```\n\n    **Example:** Specifying type for values contained in an array\n\n    ```puppet\n    $formats = {\n      Array => {\n        format => '%(a',\n        string_formats => { Integer => '%#x' }\n      }\n    }\n    $str = String([1,2,3], $formats) # produces '(0x1, 0x2, 0x3)'\n    ```\n\n    The given formats are merged with the default formats, and matching of values to convert against format is based on\n    the specificity of the mapped type; for example, different formats can be used for short and long arrays.\n\n    #### Integer to String\n\n    | Format  | Integer Formats\n    | ------  | ---------------\n    | d       | Decimal, negative values produces leading '-'.\n    | x X     | Hexadecimal in lower or upper case. Uses ..f/..F for negative values unless + is also used. A `#` adds prefix 0x/0X.\n    | o       | Octal. Uses ..0 for negative values unless `+` is also used. A `#` adds prefix 0.\n    | b B     | Binary with prefix 'b' or 'B'. Uses ..1/..1 for negative values unless `+` is also used.\n    | c       | Numeric value representing a Unicode value, result is a one unicode character string, quoted if alternative flag # is used\n    | s       | Same as d, or d in quotes if alternative flag # is used.\n    | p       | Same as d.\n    | eEfgGaA | Converts integer to float and formats using the floating point rules.\n\n    Defaults to `d`.\n\n    #### Float to String\n\n    | Format  | Float formats\n    | ------  | -------------\n    | f       | Floating point in non exponential notation.\n    | e E     | Exponential notation with 'e' or 'E'.\n    | g G     | Conditional exponential with 'e' or 'E' if exponent < -4 or >= the precision.\n    | a A     | Hexadecimal exponential form, using 'x'/'X' as prefix and 'p'/'P' before exponent.\n    | s       | Converted to string using format p, then applying string formatting rule, alternate form # quotes result.\n    | p       | Same as f format with minimum significant number of fractional digits, prec has no effect.\n    | dxXobBc | Converts float to integer and formats using the integer rules.\n\n    Defaults to `p`.\n\n    #### String to String\n\n    | Format | String\n    | ------ | ------\n    | s      | Unquoted string, verbatim output of control chars.\n    | p      | Programmatic representation - strings are quoted, interior quotes and control chars are escaped.\n    | C      | Each `::` name segment capitalized, quoted if alternative flag `#` is used.\n    | c      | Capitalized string, quoted if alternative flag `#` is used.\n    | d      | Downcased string, quoted if alternative flag `#` is used.\n    | u      | Upcased string, quoted if alternative flag `#` is used.\n    | t      | Trims leading and trailing whitespace from the string, quoted if alternative flag `#` is used.\n\n    Defaults to `s` at top level and `p` inside array or hash.\n\n    #### Boolean to String\n\n    | Format    | Boolean Formats\n    | ----      | -------------------\n    | t T       | String 'true'/'false' or 'True'/'False', first char if alternate form is used (i.e. 't'/'f' or 'T'/'F').\n    | y Y       | String 'yes'/'no', 'Yes'/'No', 'y'/'n' or 'Y'/'N' if alternative flag `#` is used.\n    | dxXobB    | Numeric value 0/1 in accordance with the given format which must be valid integer format.\n    | eEfgGaA   | Numeric value 0.0/1.0 in accordance with the given float format and flags.\n    | s         | String 'true' / 'false'.\n    | p         | String 'true' / 'false'.\n\n    #### Regexp to String\n\n    | Format    | Regexp Formats\n    | ----      | --------------\n    | s         | No delimiters, quoted if alternative flag `#` is used.\n    | p         | Delimiters `/ /`.\n\n    #### Undef to String\n\n    | Format    | Undef formats\n    | ------    | -------------\n    | s         | Empty string, or quoted empty string if alternative flag `#` is used.\n    | p         | String 'undef', or quoted '\"undef\"' if alternative flag `#` is used.\n    | n         | String 'nil', or 'null' if alternative flag `#` is used.\n    | dxXobB    | String 'NaN'.\n    | eEfgGaA   | String 'NaN'.\n    | v         | String 'n/a'.\n    | V         | String 'N/A'.\n    | u         | String 'undef', or 'undefined' if alternative `#` flag is used.\n\n    #### Default value to String\n\n    | Format    | Default formats\n    | ------    | ---------------\n    | d D       | String 'default' or 'Default', alternative form `#` causes value to be quoted.\n    | s         | Same as d.\n    | p         | Same as d.\n\n    #### Binary value to String\n\n    | Format    | Default formats\n    | ------    | ---------------\n    | s         | binary as unquoted UTF-8 characters (errors if byte sequence is invalid UTF-8). Alternate form escapes non ascii bytes.\n    | p         | 'Binary(\"<base64strict>\")'\n    | b         | '<base64>' - base64 string with newlines inserted\n    | B         | '<base64strict>' - base64 strict string (without newlines inserted)\n    | u         | '<base64urlsafe>' - base64 urlsafe string\n    | t         | 'Binary' - outputs the name of the type only\n    | T         | 'BINARY' - output the name of the type in all caps only\n\n    * The alternate form flag `#` will quote the binary or base64 text output.\n    * The format `%#s` allows invalid UTF-8 characters and outputs all non ascii bytes\n      as hex escaped characters on the form `\\\\xHH` where `H` is a hex digit.\n    * The width and precision values are applied to the text part only in `%p` format.\n\n    #### Array & Tuple to String\n\n    | Format    | Array/Tuple Formats\n    | ------    | -------------\n    | a         | Formats with `[ ]` delimiters and `,`, alternate form `#` indents nested arrays/hashes.\n    | s         | Same as a.\n    | p         | Same as a.\n\n    See \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\n    more information about options.\n\n    The alternate form flag `#` will cause indentation of nested array or hash containers. If width is also set\n    it is taken as the maximum allowed length of a sequence of elements (not including delimiters). If this max length\n    is exceeded, each element will be indented.\n\n    #### Hash & Struct to String\n\n    | Format    | Hash/Struct Formats\n    | ------    | -------------\n    | h         | Formats with `{ }` delimiters, `,` element separator and ` => ` inner element separator unless overridden by flags.\n    | s         | Same as h.\n    | p         | Same as h.\n    | a         | Converts the hash to an array of [k,v] tuples and formats it using array rule(s).\n\n    See \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\n    more information about options.\n\n    The alternate form flag `#` will format each hash key/value entry indented on a separate line.\n\n    #### Type to String\n\n    | Format    | Array/Tuple Formats\n    | ------    | -------------\n    | s         | The same as `p`, quoted if alternative flag `#` is used.\n    | p         | Outputs the type in string form as specified by the Puppet Language.\n\n    #### Flags\n\n    | Flag     | Effect\n    | ------   | ------\n    | (space)  | A space instead of `+` for numeric output (`-` is shown), for containers skips delimiters.\n    | #        | Alternate format; prefix 0x/0x, 0 (octal) and 0b/0B for binary, Floats force decimal '.'. For g/G keep trailing 0.\n    | +        | Show sign +/- depending on value's sign, changes x, X, o, b, B format to not use 2's complement form.\n    | -        | Left justify the value in the given width.\n    | 0        | Pad with 0 instead of space for widths larger than value.\n    | <[({\\|   | Defines an enclosing pair <> [] () {} or \\| \\| when used with a container type.\n\n    ### Conversion to Boolean\n\n    Accepts a single value as argument:\n\n    * Float 0.0 is `false`, all other float values are `true`\n    * Integer 0 is `false`, all other integer values are `true`\n    * Strings\n      * `true` if 'true', 'yes', 'y' (case independent compare)\n      * `false` if 'false', 'no', 'n' (case independent compare)\n    * Boolean is already boolean and is simply returned\n\n    ### Conversion to Array and Tuple\n\n    When given a single value as argument:\n\n    * A non empty `Hash` is converted to an array matching `Array[Tuple[Any,Any], 1]`.\n    * An empty `Hash` becomes an empty array.\n    * An `Array` is simply returned.\n    * An `Iterable[T]` is turned into an array of `T` instances.\n    * A `Binary` is converted to an `Array[Integer[0,255]]` of byte values\n\n\n    When given a second Boolean argument:\n\n    * if `true`, a value that is not already an array is returned as a one element array.\n    * if `false`, (the default), converts the first argument as shown above.\n\n    **Example:** Ensuring value is an array\n\n    ```puppet\n    $arr = Array($value, true)\n    ```\n\n    Conversion to a `Tuple` works exactly as conversion to an `Array`, only that the constructed array is\n    asserted against the given tuple type.\n\n    ### Conversion to Hash and Struct\n\n    Accepts a single value as argument:\n\n    * An empty `Array` becomes an empty `Hash`\n    * An `Array` matching `Array[Tuple[Any,Any], 1]` is converted to a hash where each tuple describes a key/value entry\n    * An `Array` with an even number of entries is interpreted as `[key1, val1, key2, val2, ...]`\n    * An `Iterable` is turned into an `Array` and then converted to hash as per the array rules\n    * A `Hash` is simply returned\n\n    Alternatively, a tree can be constructed by giving two values; an array of tuples on the form `[path, value]`\n    (where the `path` is the path from the root of a tree, and `value` the value at that position in the tree), and\n    either the option `'tree'` (do not convert arrays to hashes except the top level), or\n    `'hash_tree'` (convert all arrays to hashes).\n\n    The tree/hash_tree forms of Hash creation are suited for transforming the result of an iteration\n    using `tree_each` and subsequent filtering or mapping.\n\n    **Example:** Mapping a hash tree\n\n    Mapping an arbitrary structure in a way that keeps the structure, but where some values are replaced\n    can be done by using the `tree_each` function, mapping, and then constructing a new Hash from the result:\n\n    ```puppet\n    # A hash tree with 'water' at different locations\n    $h = { a => { b => { x => 'water'}}, b => { y => 'water'} }\n    # a helper function that turns water into wine\n    function make_wine($x) { if $x == 'water' { 'wine' } else { $x } }\n    # create a flattened tree with water turned into wine\n    $flat_tree = $h.tree_each.map |$entry| { [$entry[0], make_wine($entry[1])] }\n    # create a new Hash and log it\n    notice Hash($flat_tree, 'hash_tree')\n    ```\n\n    Would notice the hash `{a => {b => {x => wine}}, b => {y => wine}}`\n\n    Conversion to a `Struct` works exactly as conversion to a `Hash`, only that the constructed hash is\n    asserted against the given struct type.\n\n    ### Conversion to a Regexp\n\n    A `String` can be converted into a `Regexp`\n\n    **Example**: Converting a String into a Regexp\n    ```puppet\n    $s = '[a-z]+\\.com'\n    $r = Regexp($s)\n    if('foo.com' =~ $r) {\n      ...\n    }\n    ```\n\n    ### Creating a SemVer\n\n    A SemVer object represents a single [Semantic Version](http://semver.org/).\n    It can be created from a String, individual values for its parts, or a hash specifying the value per part.\n    See the specification at [semver.org](http://semver.org/) for the meaning of the SemVer's parts.\n\n    The signatures are:\n\n    ```puppet\n    type PositiveInteger = Integer[0,default]\n    type SemVerQualifier = Pattern[/\\A(?<part>[0-9A-Za-z-]+)(?:\\.\\g<part>)*\\Z/]\n    type SemVerString = String[1]\n    type SemVerHash =Struct[{\n      major                => PositiveInteger,\n      minor                => PositiveInteger,\n      patch                => PositiveInteger,\n      Optional[prerelease] => SemVerQualifier,\n      Optional[build]      => SemVerQualifier\n    }]\n\n    function SemVer.new(SemVerString $str)\n\n    function SemVer.new(\n            PositiveInteger           $major\n            PositiveInteger           $minor\n            PositiveInteger           $patch\n            Optional[SemVerQualifier] $prerelease = undef\n            Optional[SemVerQualifier] $build = undef\n            )\n\n    function SemVer.new(SemVerHash $hash_args)\n    ```\n\n    **Examples:** SemVer and SemVerRange usage\n\n    ```puppet\n    # As a type, SemVer can describe disjunct ranges which versions can be\n    # matched against - here the type is constructed with two\n    # SemVerRange objects.\n    #\n    $t = SemVer[\n      SemVerRange('>=1.0.0 <2.0.0'),\n      SemVerRange('>=3.0.0 <4.0.0')\n    ]\n    notice(SemVer('1.2.3') =~ $t) # true\n    notice(SemVer('2.3.4') =~ $t) # false\n    notice(SemVer('3.4.5') =~ $t) # true\n    ```\n\n    ### Creating a SemVerRange\n\n    A `SemVerRange` object represents a range of `SemVer`. It can be created from\n    a `String`, or from two `SemVer` instances, where either end can be given as\n    a literal `default` to indicate infinity. The string format of a `SemVerRange` is specified by\n    the [Semantic Version Range Grammar](https://github.com/npm/node-semver#ranges).\n\n    > Use of the comparator sets described in the grammar (joining with `||`) is not supported.\n\n    The signatures are:\n\n    ```puppet\n    type SemVerRangeString = String[1]\n    type SemVerRangeHash = Struct[{\n      min                   => Variant[Default, SemVer],\n      Optional[max]         => Variant[Default, SemVer],\n      Optional[exclude_max] => Boolean\n    }]\n\n    function SemVerRange.new(\n      SemVerRangeString $semver_range_string\n    )\n\n    function SemVerRange.new(\n      Variant[Default,SemVer] $min\n      Variant[Default,SemVer] $max\n      Optional[Boolean]       $exclude_max = undef\n    )\n\n    function SemVerRange.new(\n      SemVerRangeHash $semver_range_hash\n    )\n    ```\n\n    For examples of `SemVerRange` use, see \"Creating a SemVer\".\n\n    ### Creating a Binary\n\n    A `Binary` object represents a sequence of bytes and it can be created from a String in Base64 format,\n    an Array containing byte values. A Binary can also be created from a Hash containing the value to convert to\n    a `Binary`.\n\n    The signatures are:\n\n    ```puppet\n    type ByteInteger = Integer[0,255]\n    type Base64Format = Enum[\"%b\", \"%u\", \"%B\", \"%s\"]\n    type StringHash = Struct[{value => String, \"format\" => Optional[Base64Format]}]\n    type ArrayHash = Struct[{value => Array[ByteInteger]}]\n    type BinaryArgsHash = Variant[StringHash, ArrayHash]\n\n    function Binary.new(\n      String $base64_str,\n      Optional[Base64Format] $format\n    )\n\n    function Binary.new(\n      Array[ByteInteger] $byte_array\n    }\n\n    # Same as for String, or for Array, but where arguments are given in a Hash.\n    function Binary.new(BinaryArgsHash $hash_args)\n    ```\n\n    The formats have the following meaning:\n\n    | format | explanation |\n    | ----   | ----        |\n    | B | The data is in base64 strict encoding\n    | u | The data is in URL safe base64 encoding\n    | b | The data is in base64 encoding, padding as required by base64 strict, is added by default\n    | s | The data is a puppet string. The string must be valid UTF-8, or convertible to UTF-8 or an error is raised.\n    | r | (Ruby Raw) the byte sequence in the given string is used verbatim irrespective of possible encoding errors\n\n    * The default format is `%B`.\n    * Note that the format `%r` should be used sparingly, or not at all. It exists for backwards compatibility reasons when someone receiving\n      a string from some function and that string should be treated as Binary. Such code should be changed to return a Binary instead of a String.\n\n    **Examples:** Creating a Binary\n\n    ```puppet\n    # create the binary content \"abc\"\n    $a = Binary('YWJj')\n\n    # create the binary content from content in a module's file\n    $b = binary_file('mymodule/mypicture.jpg')\n    ```\n\n    * Since 4.5.0\n    * Binary type since 4.8.0\n\n    Creating an instance of a `Type` using the `Init` type.\n    -------\n\n    The type `Init[T]` describes a value that can be used when instantiating a type. When used as the first argument in a call to `new`, it\n    will dispatch the call to its contained type and optionally augment the parameter list with additional arguments.\n\n    **Example:** Creating an instance of Integer using Init[Integer]\n\n    ```puppet\n    # The following declaration\n    $x = Init[Integer].new('128')\n    # is exactly the same as\n    $x = Integer.new('128')\n    ```\n\n    or, with base 16 and using implicit new\n\n    ```puppet\n    # The following declaration\n    $x = Init[Integer,16]('80')\n    # is exactly the same as\n    $x = Integer('80', 16)\n    ```\n\n    **Example:** Creating an instance of String using a predefined format\n\n    ```puppet\n    $fmt = Init[String,'%#x']\n    notice($fmt(256)) # will notice '0x100'\n    ```\n\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('new')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/next.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :next,\n  :arity => -2,\n  :doc => <<~DOC\n    Immediately returns the given optional value from a block (lambda), function, class body or user defined type body.\n    If a value is not given, an `undef` value is returned. This function does not return to the immediate caller.\n\n    The signal produced to return a value bubbles up through\n    the call stack until reaching a code block (lambda), function, class definition or\n    definition of a user defined type at which point the value given to the function will\n    be produced as the result of that body of code. An error is raised\n    if the signal to return a value reaches the end of the call stack.\n\n    **Example:** Using `next` in `each`\n\n    ```puppet\n    $data = [1,2,3]\n    $data.each |$x| { if $x == 2 { next() } notice $x }\n    ```\n\n    Would notice the values `1` and `3`\n\n    **Example:** Using `next` to produce a value\n\n    If logic consists of deeply nested conditionals it may be complicated to get out of the innermost conditional.\n    A call to `next` can then simplify the logic. This example however, only shows the principle.\n    ```puppet\n    $data = [1,2,3]\n    notice $data.map |$x| { if $x == 2 { next($x*100) }; $x*10 }\n    ```\n    Would notice the value `[10, 200, 30]`\n\n    * Also see functions `return` and `break`\n    * Since 4.8.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('next')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/realize.rb",
    "content": "# frozen_string_literal: true\n\n# This is just syntactic sugar for a collection, although it will generally\n# be a good bit faster.\n\nPuppet::Parser::Functions.newfunction(:realize, :arity => -2, :doc => \"Make a virtual object real.  This is useful\n    when you want to know the name of the virtual object and don't want to\n    bother with a full collection.  It is slightly faster than a collection,\n    and, of course, is a bit shorter.  You must pass the object using a\n    reference; e.g.: `realize User[luke]`.\") do |vals|\n  if Puppet[:tasks]\n    raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n      Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n      { :operation => 'realize' }\n    )\n  end\n\n  vals = [vals] unless vals.is_a?(Array)\n\n  coll = Puppet::Pops::Evaluator::Collectors::FixedSetCollector.new(self, vals.flatten)\n  compiler.add_collection(coll)\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/reduce.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :reduce,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    Applies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    to every value in a data structure from the first argument, carrying over the returned\n    value of each iteration, and returns the result of the lambda's final iteration. This\n    lets you create a new value or data structure by combining values from the first\n    argument's data structure.\n\n    This function takes two mandatory arguments, in this order:\n\n    1. An array or hash the function will iterate over.\n    2. A lambda, which the function calls for each element in the first argument. It takes\n    two mandatory parameters:\n        1. A memo value that is overwritten after each iteration with the iteration's result.\n        2. A second value that is overwritten after each iteration with the next value in the\n        function's first argument.\n\n    **Example**: Using the `reduce` function\n\n    `$data.reduce |$memo, $value| { ... }`\n\n    or\n\n    `reduce($data) |$memo, $value| { ... }`\n\n    You can also pass an optional \"start memo\" value as an argument, such as `start` below:\n\n    `$data.reduce(start) |$memo, $value| { ... }`\n\n    or\n\n    `reduce($data, start) |$memo, $value| { ... }`\n\n    When the first argument (`$data` in the above example) is an array, Puppet passes each\n    of the data structure's values in turn to the lambda's parameters. When the first\n    argument is a hash, Puppet converts each of the hash's values to an array in the form\n    `[key, value]`.\n\n    If you pass a start memo value, Puppet executes the lambda with the provided memo value\n    and the data structure's first value. Otherwise, Puppet passes the structure's first two\n    values to the lambda.\n\n    Puppet calls the lambda for each of the data structure's remaining values. For each\n    call, it passes the result of the previous call as the first parameter ($memo in the\n    above examples) and the next value from the data structure as the second parameter\n    ($value).\n\n    If the structure has one value, Puppet returns the value and does not call the lambda.\n\n    **Example**: Using the `reduce` function\n\n    ~~~ puppet\n    # Reduce the array $data, returning the sum of all values in the array.\n    $data = [1, 2, 3]\n    $sum = $data.reduce |$memo, $value| { $memo + $value }\n    # $sum contains 6\n\n    # Reduce the array $data, returning the sum of a start memo value and all values in the\n    # array.\n    $data = [1, 2, 3]\n    $sum = $data.reduce(4) |$memo, $value| { $memo + $value }\n    # $sum contains 10\n\n    # Reduce the hash $data, returning the sum of all values and concatenated string of all\n    # keys.\n    $data = {a => 1, b => 2, c => 3}\n    $combine = $data.reduce |$memo, $value| {\n      $string = \"${memo[0]}${value[0]}\"\n      $number = $memo[1] + $value[1]\n      [$string, $number]\n    }\n    # $combine contains [abc, 6]\n    ~~~\n\n    **Example**: Using the `reduce` function with a start memo and two-parameter lambda\n\n    ~~~ puppet\n    # Reduce the array $data, returning the sum of all values in the array and starting\n    # with $memo set to an arbitrary value instead of $data's first value.\n    $data = [1, 2, 3]\n    $sum = $data.reduce(4) |$memo, $value| { $memo + $value }\n    # At the start of the lambda's first iteration, $memo contains 4 and $value contains 1.\n    # After all iterations, $sum contains 10.\n\n    # Reduce the hash $data, returning the sum of all values and concatenated string of\n    # all keys, and starting with $memo set to an arbitrary array instead of $data's first\n    # key-value pair.\n    $data = {a => 1, b => 2, c => 3}\n    $combine = $data.reduce( [d, 4] ) |$memo, $value| {\n      $string = \"${memo[0]}${value[0]}\"\n      $number = $memo[1] + $value[1]\n      [$string, $number]\n    }\n    # At the start of the lambda's first iteration, $memo contains [d, 4] and $value\n    # contains [a, 1].\n    # $combine contains [dabc, 10]\n    ~~~\n\n    **Example**: Using the `reduce` function to reduce a hash of hashes\n\n    ~~~ puppet\n    # Reduce a hash of hashes $data, merging defaults into the inner hashes.\n    $data = {\n      'connection1' => {\n        'username' => 'user1',\n        'password' => 'pass1',\n      },\n      'connection_name2' => {\n        'username' => 'user2',\n        'password' => 'pass2',\n      },\n    }\n\n    $defaults = {\n      'maxActive' => '20',\n      'maxWait'   => '10000',\n      'username'  => 'defaultuser',\n      'password'  => 'defaultpass',\n    }\n\n    $merged = $data.reduce( {} ) |$memo, $x| {\n      $memo + { $x[0] => $defaults + $data[$x[0]] }\n    }\n    # At the start of the lambda's first iteration, $memo is set to {}, and $x is set to\n    # the first [key, value] tuple. The key in $data is, therefore, given by $x[0]. In\n    # subsequent rounds, $memo retains the value returned by the expression, i.e.\n    # $memo + { $x[0] => $defaults + $data[$x[0]] }.\n    ~~~\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('reduce')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/regsubst.rb",
    "content": "# frozen_string_literal: true\n\n# Copyright (C) 2009 Thomas Bellman\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n# IN NO EVENT SHALL THOMAS BELLMAN BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n# OTHER DEALINGS IN THE SOFTWARE.\n#\n# Except as contained in this notice, the name of Thomas Bellman shall\n# not be used in advertising or otherwise to promote the sale, use or\n# other dealings in this Software without prior written authorization\n# from Thomas Bellman.\n\nmodule Puppet::Parser::Functions\n  newfunction(\n    :regsubst, :type => :rvalue,\n               :arity => -4,\n\n               :doc => \"\nPerform regexp replacement on a string or array of strings.\n\n* *Parameters* (in order):\n    * _target_  The string or array of strings to operate on.  If an array, the replacement will be performed on each of the elements in the array, and the return value will be an array.\n    * _regexp_  The regular expression matching the target string.  If you want it anchored at the start and or end of the string, you must do that with ^ and $ yourself.\n    * _replacement_  Replacement string. Can contain backreferences to what was matched using \\\\0 (whole match), \\\\1 (first set of parentheses), and so on.\n    * _flags_  Optional. String of single letter flags for how the regexp is interpreted:\n        - *E*         Extended regexps\n        - *I*         Ignore case in regexps\n        - *M*         Multiline regexps\n        - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.\n    * _encoding_  Optional.  How to handle multibyte characters.  A single-character string with the following values:\n        - *N*         None\n        - *E*         EUC\n        - *S*         SJIS\n        - *U*         UTF-8\n\n* *Examples*\n\nGet the third octet from the node's IP address:\n\n    $i3 = regsubst($ipaddress,'^(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)$','\\\\3')\n\nPut angle brackets around each octet in the node's IP address:\n\n    $x = regsubst($ipaddress, '([0-9]+)', '<\\\\1>', 'G')\n\"\n  ) do |_args|\n    Error.is4x('regsubst')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/require.rb",
    "content": "# frozen_string_literal: true\n\n# Requires the specified classes\n\nPuppet::Parser::Functions.newfunction(\n  :require,\n  :arity => -2,\n  :doc => \"Evaluate one or more classes,  adding the required class as a dependency.\n\nThe relationship metaparameters work well for specifying relationships\nbetween individual resources, but they can be clumsy for specifying\nrelationships between classes.  This function is a superset of the\n'include' function, adding a class relationship so that the requiring\nclass depends on the required class.\n\nWarning: using require in place of include can lead to unwanted dependency cycles.\n\nFor instance the following manifest, with 'require' instead of 'include' would produce a nasty dependence cycle, because notify imposes a before between File[/foo] and Service[foo]:\n\n    class myservice {\n      service { foo: ensure => running }\n    }\n\n    class otherstuff {\n      include myservice\n      file { '/foo': notify => Service[foo] }\n    }\n\nNote that this function only works with clients 0.25 and later, and it will\nfail if used with earlier clients.\n\nYou must use the class's full name;\nrelative names are not allowed. In addition to names in string form,\nyou may also directly use Class and Resource Type values that are produced when evaluating\nresource and relationship expressions.\n\n- Since 4.0.0 Class and Resource types, absolute names\n- Since 4.7.0 Returns an Array[Type[Class]] with references to the required classes\n\"\n) do |classes|\n  call_function('require', classes)\n  Puppet.warn_once('deprecations', '3xfunction#require', _(\"Calling function_require via the Scope class is deprecated. Use Scope#call_function instead\"))\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/return.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :return,\n  :arity => -2,\n  :doc => <<~DOC\n    Immediately returns the given optional value from a function, class body or user defined type body.\n    If a value is not given, an `undef` value is returned. This function does not return to the immediate caller.\n    If this function is called from within a lambda, the return action is from the scope of the\n    function containing the lambda (top scope), not the function accepting the lambda (local scope).\n\n    The signal produced to return a value bubbles up through\n    the call stack until reaching a function, class definition or\n    definition of a user defined type at which point the value given to the function will\n    be produced as the result of that body of code. An error is raised\n    if the signal to return a value reaches the end of the call stack.\n\n    **Example:** Using `return`\n\n    ```puppet\n    function example($x) {\n      # handle trivial cases first for better readability of\n      # what follows\n      if $x == undef or $x == [] or $x == '' {\n        return false\n      }\n      # complex logic to determine if value is true\n      true\n    }\n    notice example([]) # would notice false\n    notice example(42) # would notice true\n    ```\n\n    **Example:** Using `return` in a class\n\n    ```puppet\n    class example($x) {\n      # handle trivial cases first for better readability of\n      # what follows\n      if $x == undef or $x == [] or $x == '' {\n        # Do some default configuration of this class\n        notice 'foo'\n        return()\n      }\n      # complex logic configuring the class if something more interesting\n      # was given in $x\n      notice 'bar'\n    }\n    ```\n\n    When used like this:\n\n    ```puppet\n    class { example: x => [] }\n    ```\n\n    The code would notice `'foo'`, but not `'bar'`.\n\n    When used like this:\n\n    ```puppet\n    class { example: x => [some_value] }\n    ```\n\n    The code would notice `'bar'` but not `'foo'`\n\n    Note that the returned value is ignored if used in a class or user defined type.\n\n    **Example:** Using `return` in a lambda\n\n    ```puppet\n    # Concatenate three strings into a single string formatted as a list.\n    function getFruit() {\n      with(\"apples\", \"oranges\", \"bananas\") |$x, $y, $z| {\n        return(\"${x}, ${y}, and ${z}\")\n      }\n      notice \"not reached\"\n    }\n    $fruit = getFruit()\n    notice $fruit\n\n    # The output contains \"apples, oranges, and bananas\".\n    # \"not reached\" is not output because the function returns its value within the\n    # calling function's scope, which stops processing the calling function before\n    # the `notice \"not reached\"` statement.\n    # Using `return()` outside of a calling function results in an error.\n    ```\n\n    * Also see functions `return` and `break`\n    * Since 4.8.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('return')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/reverse_each.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :reverse_each,\n  :type => :rvalue,\n  :arity => -1,\n  :doc => <<~DOC\n    Reverses the order of the elements of something that is iterable and optionally runs a\n    [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) for each\n    element.\n\n    This function takes one to two arguments:\n\n    1. An `Iterable` that the function will iterate over.\n    2. An optional lambda, which the function calls for each element in the first argument. It must\n       request one parameter.\n\n    **Example:** Using the `reverse_each` function\n\n    ```puppet\n    $data.reverse_each |$parameter| { <PUPPET CODE BLOCK> }\n    ```\n\n    or\n\n    ```puppet\n    $reverse_data = $data.reverse_each\n    ```\n\n    or\n\n    ```puppet\n    reverse_each($data) |$parameter| { <PUPPET CODE BLOCK> }\n    ```\n\n    or\n\n    ```puppet\n    $reverse_data = reverse_each($data)\n    ```\n\n    When no second argument is present, Puppet returns an `Iterable` that represents the reverse\n    order of its first argument. This allows methods on `Iterable` to be chained.\n\n    When a lambda is given as the second argument, Puppet iterates the first argument in reverse\n    order and passes each value in turn to the lambda, then returns `undef`.\n\n    **Example:** Using the `reverse_each` function with an array and a one-parameter lambda\n\n    ``` puppet\n    # Puppet will log a notice for each of the three items\n    # in $data in reverse order.\n    $data = [1,2,3]\n    $data.reverse_each |$item| { notice($item) }\n    ```\n\n    When no second argument is present, Puppet returns a new `Iterable` which allows it to\n    be directly chained into another function that takes an `Iterable` as an argument.\n\n    **Example:** Using the `reverse_each` function chained with a `map` function.\n\n    ```puppet\n    # For the array $data, return an array containing each\n    # value multiplied by 10 in reverse order\n    $data = [1,2,3]\n    $transformed_data = $data.reverse_each.map |$item| { $item * 10 }\n    # $transformed_data is set to [30,20,10]\n    ```\n\n    **Example:** Using `reverse_each` function chained with a `map` in alternative syntax\n\n    ```puppet\n    # For the array $data, return an array containing each\n    # value multiplied by 10 in reverse order\n    $data = [1,2,3]\n    $transformed_data = map(reverse_each($data)) |$item| { $item * 10 }\n    # $transformed_data is set to [30,20,10]\n    ```\n\n    * Since 4.4.0\n\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('reverse_each')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/scanf.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'scanf'\n\nPuppet::Parser::Functions.newfunction(\n  :scanf,\n  :type => :rvalue,\n  :arity => 2,\n  :doc => <<~DOC\n    Scans a string and returns an array of one or more converted values based on the given format string.\n    See the documentation of Ruby's String#scanf method for details about the supported formats (which\n    are similar but not identical to the formats used in Puppet's `sprintf` function.)\n\n    This function takes two mandatory arguments: the first is the string to convert, and the second is\n    the format string. The result of the scan is an array, with each successfully scanned and transformed value.\n    The scanning stops if a scan is unsuccessful, and the scanned result up to that point is returned. If there\n    was no successful scan, the result is an empty array.\n\n    ```puppet\n    \"42\".scanf(\"%i\")\n    ```\n\n    You can also optionally pass a lambda to scanf, to do additional validation or processing.\n\n    ```puppet\n    \"42\".scanf(\"%i\") |$x| {\n      unless $x[0] =~ Integer {\n        fail \"Expected a well formed integer value, got '$x[0]'\"\n      }\n      $x[0]\n    }\n    ```\n\n    - Since 4.0.0\n  DOC\n) do |args|\n  data = args[0]\n  format = args[1]\n  data.scanf(format)\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/sha1.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest/sha1'\n\nPuppet::Parser::Functions.newfunction(:sha1, :type => :rvalue, :arity => 1, :doc => \"Returns a SHA1 hash value from a provided string.\") do |args|\n  Digest::SHA1.hexdigest(args[0])\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/sha256.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest/sha2'\n\nPuppet::Parser::Functions.newfunction(:sha256, :type => :rvalue, :arity => 1, :doc => \"Returns a SHA256 hash value from a provided string.\") do |args|\n  Digest::SHA256.hexdigest(args[0])\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/shellquote.rb",
    "content": "# frozen_string_literal: true\n\n# Copyright (C) 2009 Thomas Bellman\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n# IN NO EVENT SHALL THOMAS BELLMAN BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n# OTHER DEALINGS IN THE SOFTWARE.\n#\n# Except as contained in this notice, the name of Thomas Bellman shall\n# not be used in advertising or otherwise to promote the sale, use or\n# other dealings in this Software without prior written authorization\n# from Thomas Bellman.\n\nPuppet::Parser::Functions.newfunction(:shellquote, :type => :rvalue, :arity => -1, :doc => \"\\\n    Quote and concatenate arguments for use in Bourne shell.\n\n    Each argument is quoted separately, and then all are concatenated\n    with spaces.  If an argument is an array, the elements of that\n    array is interpolated within the rest of the arguments; this makes\n    it possible to have an array of arguments and pass that array to\n    shellquote instead of having to specify each argument\n    individually in the call.\n    \") \\\ndo |args|\n  safe = 'a-zA-Z0-9@%_+=:,./-'    # Safe unquoted\n  dangerous = '!\"`$\\\\'            # Unsafe inside double quotes\n\n  result = []\n  args.flatten.each do |word|\n    if word.length != 0 and word.count(safe) == word.length\n      result << word\n    elsif word.count(dangerous) == 0\n      result << ('\"' + word + '\"')\n    elsif word.count(\"'\") == 0\n      result << (\"'\" + word + \"'\")\n    else\n      r = '\"'\n      word.each_byte do |c|\n        r += \"\\\\\" if dangerous.include?(c.chr)\n        r += c.chr\n      end\n      r += '\"'\n      result << r\n    end\n  end\n\n  return result.join(\" \")\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/slice.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :slice,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    This function takes two mandatory arguments: the first should be an array or hash, and the second specifies\n    the number of elements to include in each slice.\n\n    When the first argument is a hash, each key value pair is counted as one. For example, a slice size of 2 will produce\n    an array of two arrays with key, and value.\n\n        $a.slice(2) |$entry|          { notice \"first ${$entry[0]}, second ${$entry[1]}\" }\n        $a.slice(2) |$first, $second| { notice \"first ${first}, second ${second}\" }\n\n    The function produces a concatenated result of the slices.\n\n        slice([1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]]\n        slice(Integer[1,6], 2)  # produces [[1,2], [3,4], [5,6]]\n        slice(4,2)              # produces [[0,1], [2,3]]\n        slice('hello',2)        # produces [[h, e], [l, l], [o]]\n\n    You can also optionally pass a lambda to slice.\n\n        $a.slice($n) |$x| { ... }\n        slice($a) |$x| { ... }\n\n    The lambda should have either one parameter (receiving an array with the slice), or the same number\n    of parameters as specified by the slice size (each parameter receiving its part of the slice).\n    If there are fewer remaining elements than the slice size for the last slice, it will contain the remaining\n    elements. If the lambda has multiple parameters, excess parameters are set to undef for an array, or\n    to empty arrays for a hash.\n\n        $a.slice(2) |$first, $second| { ... }\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('slice')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/split.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Parser::Functions\n  newfunction(\n    :split, :type => :rvalue,\n            :arity => 2,\n\n            :doc => \"\\\nSplit a string variable into an array using the specified split regexp.\n\n*Example:*\n\n    $string     = 'v1.v2:v3.v4'\n    $array_var1 = split($string, ':')\n    $array_var2 = split($string, '[.]')\n    $array_var3 = split($string, Regexp['[.:]'])\n\n`$array_var1` now holds the result `['v1.v2', 'v3.v4']`,\nwhile `$array_var2` holds `['v1', 'v2:v3', 'v4']`, and\n`$array_var3` holds `['v1', 'v2', 'v3', 'v4']`.\n\nNote that in the second example, we split on a literal string that contains\na regexp meta-character (.), which must be escaped.  A simple\nway to do that for a single character is to enclose it in square\nbrackets; a backslash will also escape a single character.\"\n  ) do |_args|\n    Error.is4x('split')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/sprintf.rb",
    "content": "# frozen_string_literal: true\n\n# Copyright (C) 2009 Thomas Bellman\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n# IN NO EVENT SHALL THOMAS BELLMAN BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n# OTHER DEALINGS IN THE SOFTWARE.\n#\n# Except as contained in this notice, the name of Thomas Bellman shall\n# not be used in advertising or otherwise to promote the sale, use or\n# other dealings in this Software without prior written authorization\n# from Thomas Bellman.\n\nPuppet::Parser::Functions.newfunction(\n  :sprintf, :type => :rvalue,\n            :arity => -2,\n            :doc => \"Perform printf-style formatting of text.\n\n  The first parameter is format string describing how the rest of the parameters should be formatted.\n  See the documentation for the [`Kernel::sprintf` function](https://ruby-doc.org/core/Kernel.html)\n  in Ruby for details.\n\n  To use [named format](https://idiosyncratic-ruby.com/49-what-the-format.html) arguments, provide a\n  hash containing the target string values as the argument to be formatted. For example:\n\n  ```puppet\n  notice sprintf(\\\"%<x>s : %<y>d\\\", { 'x' => 'value is', 'y' => 42 })\n  ```\n\n  This statement produces a notice of `value is : 42`.\"\n) do |args|\n  fmt = args[0]\n  args = args[1..]\n  begin\n    return sprintf(fmt, *args)\n  rescue KeyError => e\n    if args.size == 1 && args[0].is_a?(Hash)\n      # map the single hash argument such that all top level string keys are symbols\n      # as that allows named arguments to be used in the format string.\n      #\n      result = {}\n      args[0].each_pair { |k, v| result[k.is_a?(String) ? k.to_sym : k] = v }\n      return sprintf(fmt, result)\n    end\n    raise e\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/step.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :step,\n  :type => :rvalue,\n  :arity => -1,\n  :doc => <<~DOC\n    Provides stepping with given interval over elements in an iterable and optionally runs a\n    [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) for each\n    element.\n\n    This function takes two to three arguments:\n\n    1. An 'Iterable' that the function will iterate over.\n    2. An `Integer` step factor. This must be a positive integer.\n    3. An optional lambda, which the function calls for each element in the interval. It must\n       request one parameter.\n\n    **Example:** Using the `step` function\n\n    ```puppet\n    $data.step(<n>) |$parameter| { <PUPPET CODE BLOCK> }\n    ```\n\n    or\n\n    ```puppet\n    $stepped_data = $data.step(<n>)\n    ```\n\n    or\n    ```puppet\n    step($data, <n>) |$parameter| { <PUPPET CODE BLOCK> }\n    ```\n\n    or\n\n    ```puppet\n    $stepped_data = step($data, <n>)\n    ```\n\n    When no block is given, Puppet returns an `Iterable` that yields the first element and every nth successor\n    element, from its first argument. This allows functions on iterables to be chained.\n    When a block is given, Puppet iterates and calls the block with the first element and then with\n    every nth successor element. It then returns `undef`.\n\n    **Example:** Using the `step` function with an array, a step factor, and a one-parameter block\n\n    ```puppet\n    # For the array $data, call a block with the first element and then with each 3rd successor element\n    $data = [1,2,3,4,5,6,7,8]\n    $data.step(3) |$item| {\n     notice($item)\n    }\n    # Puppet notices the values '1', '4', '7'.\n    ```\n\n    When no block is given, Puppet returns a new `Iterable` which allows it to be directly chained into\n    another function that takes an `Iterable` as an argument.\n\n    **Example:** Using the `step` function chained with a `map` function.\n\n    ```puppet\n    # For the array $data, return an array, set to the first element and each 5th successor element, in reverse\n    # order multiplied by 10\n    $data = Integer[0,20]\n    $transformed_data = $data.step(5).map |$item| { $item * 10 }\n    $transformed_data contains [0,50,100,150,200]\n    ```\n\n    **Example:** The same example using `step` function chained with a `map` in alternative syntax\n\n    ```puppet\n    # For the array $data, return an array, set to the first and each 5th\n    # successor, in reverse order, multiplied by 10\n    $data = Integer[0,20]\n    $transformed_data = map(step($data, 5)) |$item| { $item * 10 }\n    $transformed_data contains [0,50,100,150,200]\n    ```\n\n    * Since 4.4.0\n\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('step')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/strftime.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :strftime,\n  :type => :rvalue,\n  :arity => -3,\n  :doc => <<~DOC\n    Formats timestamp or timespan according to the directives in the given format string. The directives begins with a percent (%) character.\n    Any text not listed as a directive will be passed through to the output string.\n\n    A third optional timezone argument can be provided. The first argument will then be formatted to represent a local time in that\n    timezone. The timezone can be any timezone that is recognized when using the '%z' or '%Z' formats, or the word 'current', in which\n    case the current timezone of the evaluating process will be used. The timezone argument is case insensitive.\n\n    The default timezone, when no argument is provided, or when using the keyword `default`, is 'UTC'.\n\n    The directive consists of a percent (%) character, zero or more flags, optional minimum field width and\n    a conversion specifier as follows:\n    ```\n    %[Flags][Width]Conversion\n    ```\n\n    ### Flags that controls padding\n\n    | Flag  | Meaning\n    | ----  | ---------------\n    | -     | Don't pad numerical output\n    | _     | Use spaces for padding\n    | 0     | Use zeros for padding\n\n    ### `Timestamp` specific flags\n\n    | Flag  | Meaning\n    | ----  | ---------------\n    | #     | Change case\n    | ^     | Use uppercase\n    | :     | Use colons for %z\n\n    ### Format directives applicable to `Timestamp` (names and padding can be altered using flags):\n\n    **Date (Year, Month, Day):**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | Y | Year with century, zero-padded to at least 4 digits |\n    | C | year / 100 (rounded down such as 20 in 2009) |\n    | y | year % 100 (00..99) |\n    | m | Month of the year, zero-padded (01..12) |\n    | B | The full month name (\"January\") |\n    | b | The abbreviated month name (\"Jan\") |\n    | h | Equivalent to %b |\n    | d | Day of the month, zero-padded (01..31) |\n    | e | Day of the month, blank-padded ( 1..31) |\n    | j | Day of the year (001..366) |\n\n    **Time (Hour, Minute, Second, Subsecond):**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | H | Hour of the day, 24-hour clock, zero-padded (00..23) |\n    | k | Hour of the day, 24-hour clock, blank-padded ( 0..23) |\n    | I | Hour of the day, 12-hour clock, zero-padded (01..12) |\n    | l | Hour of the day, 12-hour clock, blank-padded ( 1..12) |\n    | P | Meridian indicator, lowercase (\"am\" or \"pm\") |\n    | p | Meridian indicator, uppercase (\"AM\" or \"PM\") |\n    | M | Minute of the hour (00..59) |\n    | S | Second of the minute (00..60) |\n    | L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000 |\n    | N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified width are truncated to avoid carry up |\n\n    **Time (Hour, Minute, Second, Subsecond):**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | z   | Time zone as hour and minute offset from UTC (e.g. +0900) |\n    | :z  | hour and minute offset from UTC with a colon (e.g. +09:00) |\n    | ::z | hour, minute and second offset from UTC (e.g. +09:00:00) |\n    | Z   | Abbreviated time zone name or similar information.  (OS dependent) |\n\n    **Weekday:**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | A | The full weekday name (\"Sunday\") |\n    | a | The abbreviated name (\"Sun\") |\n    | u | Day of the week (Monday is 1, 1..7) |\n    | w | Day of the week (Sunday is 0, 0..6) |\n\n    **ISO 8601 week-based year and week number:**\n\n    The first week of YYYY starts with a Monday and includes YYYY-01-04.\n    The days in the year before the first week are in the last week of\n    the previous year.\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | G | The week-based year |\n    | g | The last 2 digits of the week-based year (00..99) |\n    | V | Week number of the week-based year (01..53) |\n\n    **Week number:**\n\n    The first week of YYYY that starts with a Sunday or Monday (according to %U\n    or %W). The days in the year before the first week are in week 0.\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | U | Week number of the year. The week starts with Sunday. (00..53) |\n    | W | Week number of the year. The week starts with Monday. (00..53) |\n\n    **Seconds since the Epoch:**\n\n    | Format | Meaning |\n    | s | Number of seconds since 1970-01-01 00:00:00 UTC. |\n\n    **Literal string:**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | n | Newline character (\\n) |\n    | t | Tab character (\\t) |\n    | % | Literal \"%\" character |\n\n    **Combination:**\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | c | date and time (%a %b %e %T %Y) |\n    | D | Date (%m/%d/%y) |\n    | F | The ISO 8601 date format (%Y-%m-%d) |\n    | v | VMS date (%e-%^b-%4Y) |\n    | x | Same as %D |\n    | X | Same as %T |\n    | r | 12-hour time (%I:%M:%S %p) |\n    | R | 24-hour time (%H:%M) |\n    | T | 24-hour time (%H:%M:%S) |\n\n    **Example**: Using `strftime` with a `Timestamp`:\n\n    ~~~ puppet\n    $timestamp = Timestamp('2016-08-24T12:13:14')\n\n    # Notice the timestamp using a format that notices the ISO 8601 date format\n    notice($timestamp.strftime('%F')) # outputs '2016-08-24'\n\n    # Notice the timestamp using a format that notices weekday, month, day, time (as UTC), and year\n    notice($timestamp.strftime('%c')) # outputs 'Wed Aug 24 12:13:14 2016'\n\n    # Notice the timestamp using a specific timezone\n    notice($timestamp.strftime('%F %T %z', 'PST')) # outputs '2016-08-24 04:13:14 -0800'\n\n    # Notice the timestamp using timezone that is current for the evaluating process\n    notice($timestamp.strftime('%F %T', 'current')) # outputs the timestamp using the timezone for the current process\n    ~~~\n\n    ### Format directives applicable to `Timespan`:\n\n    | Format | Meaning |\n    | ------ | ------- |\n    | D | Number of Days |\n    | H | Hour of the day, 24-hour clock |\n    | M | Minute of the hour (00..59) |\n    | S | Second of the minute (00..59) |\n    | L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000. |\n    | N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified length are truncated to avoid carry up |\n\n    The format directive that represents the highest magnitude in the format will be allowed to overflow.\n    I.e. if no \"%D\" is used but a \"%H\" is present, then the hours will be more than 23 in case the\n    timespan reflects more than a day.\n\n    **Example**: Using `strftime` with a Timespan and a format\n\n    ~~~ puppet\n    $duration = Timespan({ hours => 3, minutes => 20, seconds => 30 })\n\n    # Notice the duration using a format that outputs <hours>:<minutes>:<seconds>\n    notice($duration.strftime('%H:%M:%S')) # outputs '03:20:30'\n\n    # Notice the duration using a format that outputs <minutes>:<seconds>\n    notice($duration.strftime('%M:%S')) # outputs '200:30'\n    ~~~\n\n    - Since 4.8.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('strftime')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/tag.rb",
    "content": "# frozen_string_literal: true\n\n# Tag the current scope with each passed name\nPuppet::Parser::Functions.newfunction(:tag, :arity => -2, :doc => \"Add the specified tags to the containing class\n  or definition.  All contained objects will then acquire that tag, also.\n  \") do |vals|\n  if Puppet[:tasks]\n    raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n      Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n      { :operation => 'tag' }\n    )\n  end\n\n  resource.tag(*vals)\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/tagged.rb",
    "content": "# frozen_string_literal: true\n\n# Test whether a given tag is set.  This functions as a big OR -- if any of the specified tags are unset, we return false.\nPuppet::Parser::Functions.newfunction(:tagged, :type => :rvalue, :arity => -2, :doc => \"A boolean function that\n  tells you whether the current container is tagged with the specified tags.\n  The tags are ANDed, so that all of the specified tags must be included for\n  the function to return true.\") do |vals|\n  if Puppet[:tasks]\n    raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n      Puppet::Pops::Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING,\n      { :operation => 'tagged' }\n    )\n  end\n\n  retval = true\n  vals.each do |val|\n    unless compiler.catalog.tagged?(val) or resource.tagged?(val)\n      retval = false\n      break\n    end\n  end\n\n  return retval\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/template.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(:template, :type => :rvalue, :arity => -2, :doc =>\n  \"Loads an ERB template from a module, evaluates it, and returns the resulting\n  value as a string.\n\n  The argument to this function should be a `<MODULE NAME>/<TEMPLATE FILE>`\n  reference, which will load `<TEMPLATE FILE>` from a module's `templates`\n  directory. (For example, the reference `apache/vhost.conf.erb` will load the\n  file `<MODULES DIRECTORY>/apache/templates/vhost.conf.erb`.)\n\n  This function can also accept:\n\n  * An absolute path, which can load a template file from anywhere on disk.\n  * Multiple arguments, which will evaluate all of the specified templates and\n  return their outputs concatenated into a single string.\") do |vals|\n  if Puppet[:tasks]\n    raise Puppet::ParseErrorWithIssue.from_issue_and_stack(\n      Puppet::Pops::Issues::FEATURE_NOT_SUPPORTED_WHEN_SCRIPTING,\n      { :feature => 'ERB template' }\n    )\n  end\n  vals.collect do |file|\n    # Use a wrapper, so the template can't get access to the full\n    # Scope object.\n    debug \"Retrieving template #{file}\"\n\n    wrapper = Puppet::Parser::TemplateWrapper.new(self)\n    wrapper.file = file\n    begin\n      wrapper.result\n    rescue => detail\n      info = detail.backtrace.first.split(':')\n      message = []\n      message << _(\"Failed to parse template %{file}:\") % { file: file }\n      message << _(\"  Filepath: %{file_path}\") % { file_path: info[0] }\n      message << _(\"  Line: %{line}\") % { line: info[1] }\n      message << _(\"  Detail: %{detail}\") % { detail: detail }\n      raise Puppet::ParseError, message.join(\"\\n\") + \"\\n\"\n    end\n  end.join(\"\")\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/then.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :then,\n  :type => :rvalue,\n  :arity => -2,\n  :doc => <<~DOC\n    Call a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    with the given argument unless the argument is undef. Return `undef` if argument is\n    `undef`, and otherwise the result of giving the argument to the lambda.\n\n    This is useful to process a sequence of operations where an intermediate\n    result may be `undef` (which makes the entire sequence `undef`).\n    The `then` function is especially useful with the function `dig` which\n    performs in a similar way \"digging out\" a value in a complex structure.\n\n    **Example:** Using `dig` and `then`\n\n    ```puppet\n    $data = {a => { b => [{x => 10, y => 20}, {x => 100, y => 200}]}}\n    notice $data.dig(a, b, 1, x).then |$x| { $x * 2 }\n    ```\n\n    Would notice the value 200\n\n    Contrast this with:\n\n    ```puppet\n    $data = {a => { b => [{x => 10, y => 20}, {ex => 100, why => 200}]}}\n    notice $data.dig(a, b, 1, x).then |$x| { $x * 2 }\n    ```\n\n    Which would notice `undef` since the last lookup of 'x' results in `undef` which\n    is returned (without calling the lambda given to the `then` function).\n\n    As a result there is no need for conditional logic or a temporary (non local)\n    variable as the result is now either the wanted value (`x`) multiplied\n    by 2 or `undef`.\n\n    Calls to `then` can be chained. In the next example, a structure is using an offset based on\n    using 1 as the index to the first element (instead of 0 which is used in the language).\n    We are not sure if user input actually contains an index at all, or if it is\n    outside the range of available names.args.\n\n    **Example:** Chaining calls to the `then` function\n\n    ```puppet\n    # Names to choose from\n    $names = ['Ringo', 'Paul', 'George', 'John']\n\n    # Structure where 'beatle 2' is wanted (but where the number refers\n    # to 'Paul' because input comes from a source using 1 for the first\n    # element).\n\n    $data = ['singer', { beatle => 2 }]\n    $picked = assert_type(String,\n      # the data we are interested in is the second in the array,\n      # a hash, where we want the value of the key 'beatle'\n      $data.dig(1, 'beatle')\n        # and we want the index in $names before the given index\n        .then |$x| { $names[$x-1] }\n        # so we can construct a string with that beatle's name\n        .then |$x| { \"Picked Beatle '${x}'\" }\n    )\n    ```\n\n    Would notice \"Picked Beatle 'Paul'\", and would raise an error if the result\n    was not a String.\n\n    * Since 4.5.0\n\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('then')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/type.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :type,\n  :type => :rvalue,\n  :arity => -1,\n  :doc => <<~DOC\n    Returns the data type of a given value with a given degree of generality.\n\n    ```puppet\n    type InferenceFidelity = Enum[generalized, reduced, detailed]\n\n    function type(Any $value, InferenceFidelity $fidelity = 'detailed') # returns Type\n    ```\n\n     **Example:** Using `type`\n\n     ``` puppet\n     notice type(42) =~ Type[Integer]\n     ```\n\n     Would notice `true`.\n\n     By default, the best possible inference is made where all details are retained.\n     This is good when the type is used for further type calculations but is overwhelmingly\n     rich in information if it is used in a error message.\n\n     The optional argument `$fidelity` may be given as (from lowest to highest fidelity):\n\n     * `generalized` - reduces to common type and drops size constraints\n     * `reduced` - reduces to common type in collections\n     * `detailed` - (default) all details about inferred types is retained\n\n     **Example:** Using `type()` with different inference fidelity:\n\n     ``` puppet\n     notice type([3.14, 42], 'generalized')\n     notice type([3.14, 42], 'reduced'')\n     notice type([3.14, 42], 'detailed')\n     notice type([3.14, 42])\n     ```\n\n     Would notice the four values:\n\n     1. 'Array[Numeric]'\n     2. 'Array[Numeric, 2, 2]'\n     3. 'Tuple[Float[3.14], Integer[42,42]]]'\n     4. 'Tuple[Float[3.14], Integer[42,42]]]'\n\n     * Since 4.4.0\n\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('type')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/versioncmp.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/package'\n\nPuppet::Parser::Functions.newfunction(:versioncmp, :type => :rvalue, :arity => 2, :doc =>\n\"Compares two version numbers.\n\nPrototype:\n\n    \\$result = versioncmp(a, b)\n\nWhere a and b are arbitrary version strings.\n\nThis function returns:\n\n* `1` if version a is greater than version b\n* `0` if the versions are equal\n* `-1` if version a is less than version b\n\nExample:\n\n    if versioncmp('2.6-1', '2.4.5') > 0 {\n        notice('2.6-1 is > than 2.4.5')\n    }\n\nThis function uses the same version comparison algorithm used by Puppet's\n`package` type.\n\n\") do |args|\n  return Puppet::Util::Package.versioncmp(args[0], args[1])\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions/with.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Parser::Functions.newfunction(\n  :with,\n  :type => :rvalue,\n  :arity => -1,\n  :doc => <<~DOC\n    Call a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\n    with the given arguments and return the result. Since a lambda's scope is\n    local to the lambda, you can use the `with` function to create private blocks\n    of code within a class using variables whose values cannot be accessed outside\n    of the lambda.\n\n    **Example**: Using `with`\n\n    ~~~ puppet\n    # Concatenate three strings into a single string formatted as a list.\n    $fruit = with(\"apples\", \"oranges\", \"bananas\") |$x, $y, $z| {\n      \"${x}, ${y}, and ${z}\"\n    }\n    $check_var = $x\n    # $fruit contains \"apples, oranges, and bananas\"\n    # $check_var is undefined, as the value of $x is local to the lambda.\n    ~~~\n\n    - Since 4.0.0\n  DOC\n) do |_args|\n  Puppet::Parser::Functions::Error.is4x('with')\nend\n"
  },
  {
    "path": "lib/puppet/parser/functions.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/autoload'\nrequire_relative '../../puppet/parser/scope'\nrequire_relative '../../puppet/pops/adaptable'\nrequire_relative '../../puppet/concurrent/lock'\n\n# A module for managing parser functions.  Each specified function\n# is added to a central module that then gets included into the Scope\n# class.\n#\n# @api public\nmodule Puppet::Parser::Functions\n  Environment = Puppet::Node::Environment\n\n  class << self\n    include Puppet::Util\n  end\n\n  # Reset the list of loaded functions.\n  #\n  # @api private\n  def self.reset\n    # Runs a newfunction to create a function for each of the log levels\n    root_env = Puppet.lookup(:root_environment)\n    AnonymousModuleAdapter.clear(root_env)\n    Puppet::Util::Log.levels.each do |level|\n      newfunction(level,\n                  :environment => root_env,\n                  :doc => \"Log a message on the server at level #{level}.\") do |vals|\n        send(level, vals.join(\" \"))\n      end\n    end\n  end\n\n  class AutoloaderDelegate\n    attr_reader :delegatee\n\n    def initialize\n      @delegatee = Puppet::Util::Autoload.new(self, \"puppet/parser/functions\")\n    end\n\n    def loadall(env = Puppet.lookup(:current_environment))\n      if Puppet[:strict] != :off\n        Puppet.warn_once('deprecations', 'Puppet::Parser::Functions#loadall', _(\"The method 'Puppet::Parser::Functions.autoloader#loadall' is deprecated in favor of using 'Scope#call_function'.\"))\n      end\n\n      @delegatee.loadall(env)\n    end\n\n    def load(name, env = Puppet.lookup(:current_environment))\n      if Puppet[:strict] != :off\n        Puppet.warn_once('deprecations', \"Puppet::Parser::Functions#load('#{name}')\", _(\"The method 'Puppet::Parser::Functions.autoloader#load(\\\"%{name}\\\")' is deprecated in favor of using 'Scope#call_function'.\") % { name: name })\n      end\n\n      @delegatee.load(name, env)\n    end\n\n    def loaded?(name)\n      if Puppet[:strict] != :off\n        Puppet.warn_once('deprecations', \"Puppet::Parser::Functions.loaded?('#{name}')\", _(\"The method 'Puppet::Parser::Functions.autoloader#loaded?(\\\"%{name}\\\")' is deprecated in favor of using 'Scope#call_function'.\") % { name: name })\n      end\n\n      @delegatee.loaded?(name)\n    end\n  end\n\n  # Accessor for singleton autoloader\n  #\n  # @api private\n  def self.autoloader\n    @autoloader ||= AutoloaderDelegate.new\n  end\n\n  # An adapter that ties the anonymous module that acts as the container for all 3x functions to the environment\n  # where the functions are created. This adapter ensures that the life-cycle of those functions doesn't exceed\n  # the life-cycle of the environment.\n  #\n  # @api private\n  class AnonymousModuleAdapter < Puppet::Pops::Adaptable::Adapter\n    attr_accessor :module\n\n    def self.create_adapter(env)\n      adapter = super(env)\n      adapter.module = Module.new do\n        @metadata = {}\n\n        def self.all_function_info\n          @metadata\n        end\n\n        def self.get_function_info(name)\n          @metadata[name]\n        end\n\n        def self.add_function_info(name, info)\n          @metadata[name] = info\n        end\n      end\n      adapter\n    end\n  end\n\n  @environment_module_lock = Puppet::Concurrent::Lock.new\n\n  # Get the module that functions are mixed into corresponding to an\n  # environment\n  #\n  # @api private\n  def self.environment_module(env)\n    @environment_module_lock.synchronize do\n      AnonymousModuleAdapter.adapt(env).module\n    end\n  end\n\n  # Create a new Puppet DSL function.\n  #\n  # **The {newfunction} method provides a public API.**\n  #\n  # This method is used both internally inside of Puppet to define parser\n  # functions.  For example, template() is defined in\n  # {file:lib/puppet/parser/functions/template.rb template.rb} using the\n  # {newfunction} method.  Third party Puppet modules such as\n  # [stdlib](https://forge.puppetlabs.com/puppetlabs/stdlib) use this method to\n  # extend the behavior and functionality of Puppet.\n  #\n  # See also [Docs: Custom\n  # Functions](https://puppet.com/docs/puppet/5.5/lang_write_functions_in_puppet.html)\n  #\n  # @example Define a new Puppet DSL Function\n  #     >> Puppet::Parser::Functions.newfunction(:double, :arity => 1,\n  #          :doc => \"Doubles an object, typically a number or string.\",\n  #          :type => :rvalue) {|i| i[0]*2 }\n  #     => {:arity=>1, :type=>:rvalue,\n  #         :name=>\"function_double\",\n  #         :doc=>\"Doubles an object, typically a number or string.\"}\n  #\n  # @example Invoke the double function from irb as is done in RSpec examples:\n  #     >> require 'puppet_spec/scope'\n  #     >> scope = PuppetSpec::Scope.create_test_scope_for_node('example')\n  #     => Scope()\n  #     >> scope.function_double([2])\n  #     => 4\n  #     >> scope.function_double([4])\n  #     => 8\n  #     >> scope.function_double([])\n  #     ArgumentError: double(): Wrong number of arguments given (0 for 1)\n  #     >> scope.function_double([4,8])\n  #     ArgumentError: double(): Wrong number of arguments given (2 for 1)\n  #     >> scope.function_double([\"hello\"])\n  #     => \"hellohello\"\n  #\n  # @param [Symbol] name the name of the function represented as a ruby Symbol.\n  #   The {newfunction} method will define a Ruby method based on this name on\n  #   the parser scope instance.\n  #\n  # @param [Proc] block the block provided to the {newfunction} method will be\n  #   executed when the Puppet DSL function is evaluated during catalog\n  #   compilation.  The arguments to the function will be passed as an array to\n  #   the first argument of the block.  The return value of the block will be\n  #   the return value of the Puppet DSL function for `:rvalue` functions.\n  #\n  # @option options [:rvalue, :statement] :type (:statement) the type of function.\n  #   Either `:rvalue` for functions that return a value, or `:statement` for\n  #   functions that do not return a value.\n  #\n  # @option options [String] :doc ('') the documentation for the function.\n  #   This string will be extracted by documentation generation tools.\n  #\n  # @option options [Integer] :arity (-1) the\n  #   [arity](https://en.wikipedia.org/wiki/Arity) of the function.  When\n  #   specified as a positive integer the function is expected to receive\n  #   _exactly_ the specified number of arguments.  When specified as a\n  #   negative number, the function is expected to receive _at least_ the\n  #   absolute value of the specified number of arguments incremented by one.\n  #   For example, a function with an arity of `-4` is expected to receive at\n  #   minimum 3 arguments.  A function with the default arity of `-1` accepts\n  #   zero or more arguments.  A function with an arity of 2 must be provided\n  #   with exactly two arguments, no more and no less.  Added in Puppet 3.1.0.\n  #\n  # @option options [Puppet::Node::Environment] :environment (nil) can\n  #   explicitly pass the environment we wanted the function added to.  Only used\n  #   to set logging functions in root environment\n  #\n  # @return [Hash] describing the function.\n  #\n  # @api public\n  def self.newfunction(name, options = {}, &block)\n    name = name.intern\n    environment = options[:environment] || Puppet.lookup(:current_environment)\n\n    Puppet.warning _(\"Overwriting previous definition for function %{name}\") % { name: name } if get_function(name, environment)\n\n    arity = options[:arity] || -1\n    ftype = options[:type] || :statement\n\n    unless ftype == :statement or ftype == :rvalue\n      raise Puppet::DevError, _(\"Invalid statement type %{type}\") % { type: ftype.inspect }\n    end\n\n    # the block must be installed as a method because it may use \"return\",\n    # which is not allowed from procs.\n    real_fname = \"real_function_#{name}\"\n    environment_module(environment).send(:define_method, real_fname, &block)\n\n    fname = \"function_#{name}\"\n    env_module = environment_module(environment)\n\n    env_module.send(:define_method, fname) do |*args|\n      Puppet::Util::Profiler.profile(_(\"Called %{name}\") % { name: name }, [:functions, name]) do\n        if args[0].is_a? Array\n          if arity >= 0 and args[0].size != arity\n            raise ArgumentError, _(\"%{name}(): Wrong number of arguments given (%{arg_count} for %{arity})\") % { name: name, arg_count: args[0].size, arity: arity }\n          elsif arity < 0 and args[0].size < (arity + 1).abs\n            raise ArgumentError, _(\"%{name}(): Wrong number of arguments given (%{arg_count} for minimum %{min_arg_count})\") % { name: name, arg_count: args[0].size, min_arg_count: (arity + 1).abs }\n          end\n\n          r = Puppet::Pops::Evaluator::Runtime3FunctionArgumentConverter.convert_return(send(real_fname, args[0]))\n          # avoid leaking aribtrary value if not being an rvalue function\n          options[:type] == :rvalue ? r : nil\n        else\n          raise ArgumentError, _(\"custom functions must be called with a single array that contains the arguments. For example, function_example([1]) instead of function_example(1)\")\n        end\n      end\n    end\n\n    func = { :arity => arity, :type => ftype, :name => fname }\n    func[:doc] = options[:doc] if options[:doc]\n\n    env_module.add_function_info(name, func)\n\n    func\n  end\n\n  # Determine if a function is defined\n  #\n  # @param [Symbol] name the function\n  # @param [Puppet::Node::Environment] environment the environment to find the function in\n  #\n  # @return [Symbol, false] The name of the function if it's defined,\n  #   otherwise false.\n  #\n  # @api public\n  def self.function(name, environment = Puppet.lookup(:current_environment))\n    name = name.intern\n\n    func = get_function(name, environment)\n    unless func\n      autoloader.delegatee.load(name, environment)\n      func = get_function(name, environment)\n    end\n\n    if func\n      func[:name]\n    else\n      false\n    end\n  end\n\n  def self.functiondocs(environment = Puppet.lookup(:current_environment))\n    autoloader.delegatee.loadall(environment)\n\n    ret = ''.dup\n\n    merged_functions(environment).sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |name, hash|\n      ret << \"#{name}\\n#{'-' * name.to_s.length}\\n\"\n      if hash[:doc]\n        ret << Puppet::Util::Docs.scrub(hash[:doc])\n      else\n        ret << \"Undocumented.\\n\"\n      end\n\n      ret << \"\\n\\n- *Type*: #{hash[:type]}\\n\\n\"\n    end\n\n    ret\n  end\n\n  # Determine whether a given function returns a value.\n  #\n  # @param [Symbol] name the function\n  # @param [Puppet::Node::Environment] environment The environment to find the function in\n  # @return [Boolean] whether it is an rvalue function\n  #\n  # @api public\n  def self.rvalue?(name, environment = Puppet.lookup(:current_environment))\n    func = get_function(name, environment)\n    func ? func[:type] == :rvalue : false\n  end\n\n  # Return the number of arguments a function expects.\n  #\n  # @param [Symbol] name the function\n  # @param [Puppet::Node::Environment] environment The environment to find the function in\n  # @return [Integer] The arity of the function. See {newfunction} for\n  #   the meaning of negative values.\n  #\n  # @api public\n  def self.arity(name, environment = Puppet.lookup(:current_environment))\n    func = get_function(name, environment)\n    func ? func[:arity] : -1\n  end\n\n  class << self\n    private\n\n    def merged_functions(environment)\n      root = environment_module(Puppet.lookup(:root_environment))\n      env = environment_module(environment)\n\n      root.all_function_info.merge(env.all_function_info)\n    end\n\n    def get_function(name, environment)\n      environment_module(environment).get_function_info(name.intern) || environment_module(Puppet.lookup(:root_environment)).get_function_info(name.intern)\n    end\n  end\n\n  class Error\n    def self.is4x(name)\n      raise Puppet::ParseError, _(\"%{name}() can only be called using the 4.x function API. See Scope#call_function\") % { name: name }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/parser_factory.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet; end\n\nmodule Puppet::Parser\n  # The ParserFactory makes selection of parser possible.\n  # Currently, it is possible to switch between two different parsers:\n  # * classic_parser, the parser in 3.1\n  # * eparser, the Expression Based Parser\n  #\n  class ParserFactory\n    # Produces a parser instance for the given environment\n    def self.parser\n      evaluating_parser\n    end\n\n    # Creates an instance of an E4ParserAdapter that adapts an\n    # EvaluatingParser to the 3x way of parsing.\n    #\n    def self.evaluating_parser\n      unless defined?(Puppet::Parser::E4ParserAdapter)\n        require_relative '../../puppet/parser/e4_parser_adapter'\n        require_relative '../../puppet/pops/parser/code_merger'\n      end\n      E4ParserAdapter.new\n    end\n\n    def self.code_merger\n      Puppet::Pops::Parser::CodeMerger.new\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/relationship.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Parser::Relationship\n  attr_accessor :source, :target, :type\n\n  PARAM_MAP = { :relationship => :before, :subscription => :notify }\n\n  def arrayify(resources, left)\n    case resources\n    when Puppet::Pops::Evaluator::Collectors::AbstractCollector\n      # on the LHS, go as far left as possible, else whatever the collected result is\n      left ? leftmost_alternative(resources) : resources.collected.values\n    when Array\n      resources\n    else\n      [resources]\n    end\n  end\n\n  def evaluate(catalog)\n    arrayify(source, true).each do |s|\n      arrayify(target, false).each do |t|\n        mk_relationship(s, t, catalog)\n      end\n    end\n  end\n\n  def initialize(source, target, type)\n    @source = source\n    @target = target\n    @type = type\n  end\n\n  def param_name\n    PARAM_MAP[type] || raise(ArgumentError, _(\"Invalid relationship type %{relationship_type}\") % { relationship_type: type })\n  end\n\n  def mk_relationship(source, target, catalog)\n    source_ref = canonical_ref(source)\n    target_ref = canonical_ref(target)\n    rel_param = param_name\n\n    source_resource = catalog.resource(*source_ref)\n    unless source_resource\n      raise ArgumentError, _(\"Could not find resource '%{source}' for relationship on '%{target}'\") % { source: source.to_s, target: target.to_s }\n    end\n    unless catalog.resource(*target_ref)\n      raise ArgumentError, _(\"Could not find resource '%{target}' for relationship from '%{source}'\") % { target: target.to_s, source: source.to_s }\n    end\n\n    Puppet.debug { \"Adding relationship from #{source} to #{target} with '#{param_name}'\" }\n    if source_resource[rel_param].class != Array\n      source_resource[rel_param] = [source_resource[rel_param]].compact\n    end\n    source_resource[rel_param] << (target_ref[1].nil? ? target_ref[0] : \"#{target_ref[0]}[#{target_ref[1]}]\")\n  end\n\n  private\n\n  # Finds the leftmost alternative for a collector (if it is empty, try its empty alternative recursively until there is\n  # either nothing left, or a non empty set is found.\n  #\n  def leftmost_alternative(x)\n    if x.is_a?(Puppet::Pops::Evaluator::Collectors::AbstractCollector)\n      collected = x.collected\n      return collected.values unless collected.empty?\n\n      adapter = Puppet::Pops::Adapters::EmptyAlternativeAdapter.get(x)\n      adapter.nil? ? [] : leftmost_alternative(adapter.empty_alternative)\n    elsif x.is_a?(Array) && x.size == 1 && x[0].is_a?(Puppet::Pops::Evaluator::Collectors::AbstractCollector)\n      leftmost_alternative(x[0])\n    else\n      x\n    end\n  end\n\n  # Turns a PResourceType or PClassType into an array [type, title] and all other references to [ref, nil]\n  # This is needed since it is not possible to find resources in the catalog based on the type system types :-(\n  # (note, the catalog is also used on the agent side)\n  def canonical_ref(ref)\n    case ref\n    when Puppet::Pops::Types::PResourceType\n      [ref.type_name, ref.title]\n    when Puppet::Pops::Types::PClassType\n      ['class', ref.class_name]\n    else\n      [ref.to_s, nil]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/resource/param.rb",
    "content": "# frozen_string_literal: true\n\n# The parameters we stick in Resources.\nclass Puppet::Parser::Resource::Param\n  include Puppet::Util\n  include Puppet::Util::Errors\n\n  attr_accessor :name, :value, :source, :add, :file, :line\n\n  def initialize(name: nil, value: nil, source: nil, line: nil, file: nil, add: nil)\n    @value = value\n    @source = source\n    @line = line\n    @file = file\n    @add = add\n\n    unless name\n      # This must happen after file and line are set to have them reported in the error\n      self.fail(Puppet::ResourceError, \"'name' is a required option for #{self.class}\")\n    end\n    @name = name.intern\n  end\n\n  def line_to_i\n    line ? Integer(line) : nil\n  end\n\n  def to_s\n    \"#{name} => #{value}\"\n  end\n\n  def self.from_param(param, value)\n    new_param = param.dup\n    new_param.value = value\n    new_param\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/resource.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/resource'\n\n# The primary difference between this class and its\n# parent is that this class has rules on who can set\n# parameters\nclass Puppet::Parser::Resource < Puppet::Resource\n  require_relative 'resource/param'\n  require_relative '../../puppet/util/tagging'\n\n  include Puppet::Util\n  include Puppet::Util::Errors\n  include Puppet::Util::Logging\n\n  attr_accessor :source, :scope, :collector_id\n  attr_accessor :virtual, :override, :translated, :catalog, :evaluated\n  attr_accessor :file, :line, :kind\n\n  attr_reader :exported, :parameters\n\n  # Determine whether the provided parameter name is a relationship parameter.\n  def self.relationship_parameter?(name)\n    @relationship_names ||= Puppet::Type.relationship_params.collect(&:name)\n    @relationship_names.include?(name)\n  end\n\n  # Set up some boolean test methods\n  def translated?; !!@translated; end\n  def override?;   !!@override;   end\n  def evaluated?;  !!@evaluated;  end\n\n  def [](param)\n    param = param.intern\n    if param == :title\n      return title\n    end\n\n    if @parameters.has_key?(param)\n      @parameters[param].value\n    else\n      nil\n    end\n  end\n\n  def eachparam\n    @parameters.each do |_name, param|\n      yield param\n    end\n  end\n\n  def environment\n    scope.environment\n  end\n\n  # Process the  stage metaparameter for a class.   A containment edge\n  # is drawn from  the class to the stage.   The stage for containment\n  # defaults to main, if none is specified.\n  def add_edge_to_stage\n    return unless class?\n\n    stage = catalog.resource(:stage, self[:stage] || (scope && scope.resource && scope.resource[:stage]) || :main)\n    unless stage\n      raise ArgumentError, _(\"Could not find stage %{stage} specified by %{resource}\") % { stage: self[:stage] || :main, resource: self }\n    end\n\n    self[:stage] ||= stage.title unless stage.title == :main\n    catalog.add_edge(stage, self)\n  end\n\n  # Retrieve the associated definition and evaluate it.\n  def evaluate\n    return if evaluated?\n\n    Puppet::Util::Profiler.profile(_(\"Evaluated resource %{res}\") % { res: self }, [:compiler, :evaluate_resource, self]) do\n      @evaluated = true\n      if builtin_type?\n        devfail \"Cannot evaluate a builtin type (#{type})\"\n      elsif resource_type.nil?\n        self.fail \"Cannot find definition #{type}\"\n      else\n        finish_evaluation() # do not finish completely (as that destroys Sensitive data)\n        resource_type.evaluate_code(self)\n      end\n    end\n  end\n\n  # Mark this resource as both exported and virtual,\n  # or remove the exported mark.\n  def exported=(value)\n    if value\n      @virtual = true\n    else\n    end\n    @exported = value\n  end\n\n  # Finish the evaluation by assigning defaults and scope tags\n  # @api private\n  #\n  def finish_evaluation\n    return if @evaluation_finished\n\n    add_scope_tags\n    @evaluation_finished = true\n  end\n\n  # Do any finishing work on this object, called before\n  # storage/translation. The method does nothing the second time\n  # it is called on the same resource.\n  #\n  # @param do_validate [Boolean] true if validation should be performed\n  #\n  # @api private\n  def finish(do_validate = true)\n    return if finished?\n\n    @finished = true\n    finish_evaluation\n    replace_sensitive_data\n    validate if do_validate\n  end\n\n  # Has this resource already been finished?\n  def finished?\n    @finished\n  end\n\n  def initialize(type, title, attributes, with_defaults = true)\n    raise ArgumentError, _('Resources require a hash as last argument') unless attributes.is_a? Hash\n    raise ArgumentError, _('Resources require a scope') unless attributes[:scope]\n\n    super(type, title, attributes)\n\n    @source ||= scope.source\n\n    if with_defaults\n      scope.lookupdefaults(self.type).each_pair do |name, param|\n        next if @parameters.include?(name)\n\n        debug \"Adding default for #{name}\"\n\n        param = param.dup\n        @parameters[name] = param\n        tag(*param.value) if param.name == :tag\n      end\n    end\n  end\n\n  # Is this resource modeling an isomorphic resource type?\n  def isomorphic?\n    if builtin_type?\n      resource_type.isomorphic?\n    else\n      true\n    end\n  end\n\n  # Merge an override resource in.  This will throw exceptions if\n  # any overrides aren't allowed.\n  def merge(resource)\n    # Test the resource scope, to make sure the resource is even allowed\n    # to override.\n    unless source.equal?(resource.source) || resource.source.child_of?(source)\n      raise Puppet::ParseError.new(_(\"Only subclasses can override parameters\"), resource.file, resource.line)\n    end\n\n    if evaluated?\n      error_location_str = Puppet::Util::Errors.error_location(file, line)\n      msg = if error_location_str.empty?\n              _('Attempt to override an already evaluated resource with new values')\n            else\n              _('Attempt to override an already evaluated resource, defined at %{error_location}, with new values') % { error_location: error_location_str }\n            end\n      strict = Puppet[:strict]\n      unless strict == :off\n        if strict == :error\n          raise Puppet::ParseError.new(msg, resource.file, resource.line)\n        else\n          msg += Puppet::Util::Errors.error_location_with_space(resource.file, resource.line)\n          Puppet.warning(msg)\n        end\n      end\n    end\n\n    # Some of these might fail, but they'll fail in the way we want.\n    resource.parameters.each do |_name, param|\n      override_parameter(param)\n    end\n  end\n\n  def name\n    self[:name] || title\n  end\n\n  # A temporary occasion, until I get paths in the scopes figured out.\n  alias path to_s\n\n  # Define a parameter in our resource.\n  # if we ever receive a parameter named 'tag', set\n  # the resource tags with its value.\n  def set_parameter(param, value = nil)\n    unless param.is_a?(Puppet::Parser::Resource::Param)\n      param = param.name if param.is_a?(Puppet::Pops::Resource::Param)\n      param = Puppet::Parser::Resource::Param.new(\n        :name => param, :value => value, :source => source\n      )\n    end\n\n    tag(*param.value) if param.name == :tag\n\n    # And store it in our parameter hash.\n    @parameters[param.name] = param\n  end\n  alias []= set_parameter\n\n  def to_hash\n    parse_title.merge(@parameters.each_with_object({}) do |(_, param), result|\n      value = param.value\n      value = (:undef == value) ? nil : value\n\n      next if value.nil?\n\n      case param.name\n      when :before, :subscribe, :notify, :require\n        if value.is_a?(Array)\n          value = value.flatten.reject { |v| v.nil? || :undef == v }\n        end\n      else\n      end\n      result[param.name] = value\n    end)\n  end\n\n  # Convert this resource to a RAL resource.\n  def to_ral\n    copy_as_resource.to_ral\n  end\n\n  # Answers if this resource is tagged with at least one of the tags given in downcased string form.\n  #\n  # The method is a faster variant of the tagged? method that does no conversion of its\n  # arguments.\n  #\n  # The match takes into account the tags that a resource will inherit from its container\n  # but have not been set yet.\n  # It does *not* take tags set via resource defaults as these will *never* be set on\n  # the resource itself since all resources always have tags that are automatically\n  # assigned.\n  #\n  # @param tag_array [Array[String]] list tags to look for\n  # @return [Boolean] true if this instance is tagged with at least one of the provided tags\n  #\n  def raw_tagged?(tag_array)\n    super || ((scope_resource = scope.resource) && !scope_resource.equal?(self) && scope_resource.raw_tagged?(tag_array))\n  end\n\n  def offset\n    nil\n  end\n\n  def pos\n    nil\n  end\n\n  private\n\n  def add_scope_tags\n    scope_resource = scope.resource\n    unless scope_resource.nil? || scope_resource.equal?(self)\n      merge_tags_from(scope_resource)\n    end\n  end\n\n  def replace_sensitive_data\n    parameters.keys.each do |name|\n      param = parameters[name]\n      if param.value.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n        @sensitive_parameters << name\n        parameters[name] = Puppet::Parser::Resource::Param.from_param(param, param.value.unwrap)\n      end\n    end\n  end\n\n  # Accept a parameter from an override.\n  def override_parameter(param)\n    # This can happen if the override is defining a new parameter, rather\n    # than replacing an existing one.\n    current = @parameters[param.name]\n    (set_parameter(param) and return) unless current\n\n    # Parameter is already set - if overriding with a default - simply ignore the setting of the default value\n    return if scope.is_default?(type, param.name, param.value)\n\n    # The parameter is already set.  Fail if they're not allowed to override it.\n    unless param.source.child_of?(current.source) || param.source.equal?(current.source) && scope.is_default?(type, param.name, current.value)\n      error_location_str = Puppet::Util::Errors.error_location(current.file, current.line)\n      msg = if current.source.to_s == ''\n              if error_location_str.empty?\n                _(\"Parameter '%{name}' is already set on %{resource}; cannot redefine\") %\n                  { name: param.name, resource: ref }\n              else\n                _(\"Parameter '%{name}' is already set on %{resource} at %{error_location}; cannot redefine\") %\n                  { name: param.name, resource: ref, error_location: error_location_str }\n              end\n            elsif error_location_str.empty?\n              _(\"Parameter '%{name}' is already set on %{resource} by %{source}; cannot redefine\") %\n                { name: param.name, resource: ref, source: current.source.to_s }\n            else\n              _(\"Parameter '%{name}' is already set on %{resource} by %{source} at %{error_location}; cannot redefine\") %\n                { name: param.name, resource: ref, source: current.source.to_s, error_location: error_location_str }\n            end\n      raise Puppet::ParseError.new(msg, param.file, param.line)\n    end\n\n    # If we've gotten this far, we're allowed to override.\n\n    # Merge with previous value, if the parameter was generated with the +>\n    # syntax.  It's important that we use a copy of the new param instance\n    # here, not the old one, and not the original new one, so that the source\n    # is registered correctly for later overrides but the values aren't\n    # implicitly shared when multiple resources are overridden at once (see\n    # ticket #3556).\n    if param.add\n      param = param.dup\n      param.value = [current.value, param.value].flatten\n    end\n\n    set_parameter(param)\n  end\n\n  # Make sure the resource's parameters are all valid for the type.\n  def validate\n    if builtin_type?\n      begin\n        @parameters.each { |name, _value| validate_parameter(name) }\n      rescue => detail\n        self.fail Puppet::ParseError, detail.to_s + \" on #{self}\", detail\n      end\n    else\n      resource_type.validate_resource(self)\n    end\n  end\n\n  def extract_parameters(params)\n    params.each do |param|\n      # Don't set the same parameter twice\n      self.fail Puppet::ParseError, _(\"Duplicate parameter '%{param}' for on %{resource}\") % { param: param.name, resource: self } if @parameters[param.name]\n\n      set_parameter(param)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/scope.rb",
    "content": "# frozen_string_literal: true\n\n# The scope class, which handles storing and retrieving variables and types and\n# such.\nrequire 'forwardable'\n\nrequire_relative '../../puppet/parser'\nrequire_relative '../../puppet/parser/templatewrapper'\nrequire_relative '../../puppet/parser/resource'\n\n# This class is part of the internal parser/evaluator/compiler functionality of Puppet.\n# It is passed between the various classes that participate in evaluation.\n# None of its methods are API except those that are clearly marked as such.\n#\n# @api public\nclass Puppet::Parser::Scope\n  extend Forwardable\n\n  # Variables that always exist with nil value even if not set\n  BUILT_IN_VARS = %w[module_name caller_module_name].freeze\n  EMPTY_HASH = {}.freeze\n\n  Puppet::Util.logmethods(self)\n\n  include Puppet::Util::Errors\n  attr_accessor :source, :resource\n  attr_reader :compiler\n  attr_accessor :parent\n\n  # Hash of hashes of default values per type name\n  attr_reader :defaults\n\n  # Alias for `compiler.environment`\n  def environment\n    @compiler.environment\n  end\n\n  # Alias for `compiler.catalog`\n  def catalog\n    @compiler.catalog\n  end\n\n  # Abstract base class for LocalScope and MatchScope\n  #\n  class Ephemeral\n    attr_reader :parent\n\n    def initialize(parent = nil)\n      @parent = parent\n    end\n\n    def is_local_scope?\n      false\n    end\n\n    def [](name)\n      if @parent\n        @parent[name]\n      end\n    end\n\n    def include?(name)\n      (@parent and @parent.include?(name))\n    end\n\n    def bound?(name)\n      false\n    end\n\n    def add_entries_to(target = {}, include_undef = false)\n      @parent.add_entries_to(target, include_undef) unless @parent.nil?\n      # do not include match data ($0-$n)\n      target\n    end\n  end\n\n  class LocalScope < Ephemeral\n    def initialize(parent = nil)\n      super parent\n      @symbols = {}\n    end\n\n    def [](name)\n      val = @symbols[name]\n      val.nil? && !@symbols.include?(name) ? super : val\n    end\n\n    def is_local_scope?\n      true\n    end\n\n    def []=(name, value)\n      @symbols[name] = value\n    end\n\n    def include?(name)\n      bound?(name) || super\n    end\n\n    def delete(name)\n      @symbols.delete(name)\n    end\n\n    def bound?(name)\n      @symbols.include?(name)\n    end\n\n    def add_entries_to(target = {}, include_undef = false)\n      super\n      @symbols.each do |k, v|\n        if (v == :undef || v.nil?) && !include_undef\n          target.delete(k)\n        else\n          target[k] = v\n        end\n      end\n      target\n    end\n  end\n\n  class MatchScope < Ephemeral\n    attr_accessor :match_data\n\n    def initialize(parent = nil, match_data = nil)\n      super parent\n      @match_data = match_data\n    end\n\n    def is_local_scope?\n      false\n    end\n\n    def [](name)\n      if bound?(name)\n        @match_data[name.to_i]\n      else\n        super\n      end\n    end\n\n    def include?(name)\n      bound?(name) or super\n    end\n\n    def bound?(name)\n      # A \"match variables\" scope reports all numeric variables to be bound if the scope has\n      # match_data. Without match data the scope is transparent.\n      #\n      @match_data && name =~ /^\\d+$/\n    end\n\n    def []=(name, value)\n      # TODO: Bad choice of exception\n      raise Puppet::ParseError, _(\"Numerical variables cannot be changed. Attempt to set $%{name}\") % { name: name }\n    end\n\n    def delete(name)\n      # TODO: Bad choice of exception\n      raise Puppet::ParseError, _(\"Numerical variables cannot be deleted: Attempt to delete: $%{name}\") % { name: name }\n    end\n\n    def add_entries_to(target = {}, include_undef = false)\n      # do not include match data ($0-$n)\n      super\n    end\n  end\n\n  # @api private\n  class ParameterScope < Ephemeral\n    class Access\n      attr_accessor :value\n\n      def assigned?\n        instance_variable_defined?(:@value)\n      end\n    end\n\n    # A parameter default must be evaluated using a special scope. The scope that is given to this method must\n    # have a `ParameterScope` as its last ephemeral scope. This method will then push a `MatchScope` while the\n    # given `expression` is evaluated. The method will catch any throw of `:unevaluated_parameter` and produce\n    # an error saying that the evaluated parameter X tries to access the unevaluated parameter Y.\n    #\n    # @param name [String] the name of the currently evaluated parameter\n    # @param expression [Puppet::Parser::AST] the expression to evaluate\n    # @param scope [Puppet::Parser::Scope] a scope where a `ParameterScope` has been pushed\n    # @return [Object] the result of the evaluation\n    #\n    # @api private\n    def evaluate3x(name, expression, scope)\n      scope.with_guarded_scope do\n        bad = catch(:unevaluated_parameter) do\n          scope.new_match_scope(nil)\n          return as_read_only { expression.safeevaluate(scope) }\n        end\n        parameter_reference_failure(name, bad)\n      end\n    end\n\n    def evaluate(name, expression, scope, evaluator)\n      scope.with_guarded_scope do\n        bad = catch(:unevaluated_parameter) do\n          scope.new_match_scope(nil)\n          return as_read_only { evaluator.evaluate(expression, scope) }\n        end\n        parameter_reference_failure(name, bad)\n      end\n    end\n\n    def parameter_reference_failure(from, to)\n      # Parameters are evaluated in the order they have in the @params hash.\n      keys = @params.keys\n      raise Puppet::Error, _(\"%{callee}: expects a value for parameter $%{to}\") % { callee: @callee_name, to: to } if keys.index(to) < keys.index(from)\n\n      raise Puppet::Error, _(\"%{callee}: default expression for $%{from} tries to illegally access not yet evaluated $%{to}\") % { callee: @callee_name, from: from, to: to }\n    end\n    private :parameter_reference_failure\n\n    def initialize(parent, callee_name, param_names)\n      super(parent)\n      @callee_name = callee_name\n      @params = {}\n      param_names.each { |name| @params[name] = Access.new }\n    end\n\n    def [](name)\n      access = @params[name]\n      return super if access.nil?\n\n      throw(:unevaluated_parameter, name) unless access.assigned?\n      access.value\n    end\n\n    def []=(name, value)\n      raise Puppet::Error, _(\"Attempt to assign variable %{name} when evaluating parameters\") % { name: name } if @read_only\n\n      @params[name] ||= Access.new\n      @params[name].value = value\n    end\n\n    def bound?(name)\n      @params.include?(name)\n    end\n\n    def include?(name)\n      @params.include?(name) || super\n    end\n\n    def is_local_scope?\n      true\n    end\n\n    def as_read_only\n      read_only = @read_only\n      @read_only = true\n      begin\n        yield\n      ensure\n        @read_only = read_only\n      end\n    end\n\n    def to_hash\n      @params.select { |_, access| access.assigned? }.transform_values(&:value)\n    end\n  end\n\n  # Returns true if the variable of the given name has a non nil value.\n  # TODO: This has vague semantics - does the variable exist or not?\n  #       use ['name'] to get nil or value, and if nil check with exist?('name')\n  #       this include? is only useful because of checking against the boolean value false.\n  #\n  def include?(name)\n    catch(:undefined_variable) {\n      return !self[name].nil?\n    }\n    false\n  end\n\n  # Returns true if the variable of the given name is set to any value (including nil)\n  #\n  # @return [Boolean] if variable exists or not\n  #\n  def exist?(name)\n    # Note !! ensure the answer is boolean\n    !!if name =~ /^(.*)::(.+)$/\n        class_name = ::Regexp.last_match(1)\n        variable_name = ::Regexp.last_match(2)\n        return true if class_name == '' && BUILT_IN_VARS.include?(variable_name)\n\n        # lookup class, but do not care if it is not evaluated since that will result\n        # in it not existing anyway. (Tests may run with just scopes and no evaluated classes which\n        # will result in class_scope for \"\" not returning topscope).\n        klass = find_hostclass(class_name)\n        other_scope = klass.nil? ? nil : class_scope(klass)\n        if other_scope.nil?\n          class_name == '' ? compiler.topscope.exist?(variable_name) : false\n        else\n          other_scope.exist?(variable_name)\n        end\n    else # rubocop:disable Layout/ElseAlignment\n      next_scope = inherited_scope || enclosing_scope\n      effective_symtable(true).include?(name) || next_scope && next_scope.exist?(name) || BUILT_IN_VARS.include?(name)\n    end # rubocop:disable Layout/EndAlignment\n  end\n\n  # Returns true if the given name is bound in the current (most nested) scope for assignments.\n  #\n  def bound?(name)\n    # Do not look in ephemeral (match scope), the semantics is to answer if an assignable variable is bound\n    effective_symtable(false).bound?(name)\n  end\n\n  # Is the value true?  This allows us to control the definition of truth\n  # in one place.\n  def self.true?(value)\n    case value\n    when ''\n      false\n    when :undef\n      false\n    else\n      !!value\n    end\n  end\n\n  # Coerce value to a number, or return `nil` if it isn't one.\n  def self.number?(value)\n    case value\n    when Numeric\n      value\n    when /^-?\\d+(:?\\.\\d+|(:?\\.\\d+)?e\\d+)$/\n      value.to_f\n    when /^0x[0-9a-f]+$/i\n      value.to_i(16)\n    when /^0[0-7]+$/\n      value.to_i(8)\n    when /^-?\\d+$/\n      value.to_i\n    else\n      nil\n    end\n  end\n\n  def find_hostclass(name)\n    environment.known_resource_types.find_hostclass(name)\n  end\n\n  def find_definition(name)\n    environment.known_resource_types.find_definition(name)\n  end\n\n  def find_global_scope\n    # walk upwards until first found node_scope or top_scope\n    if is_nodescope? || is_topscope?\n      self\n    else\n      next_scope = inherited_scope || enclosing_scope\n      if next_scope.nil?\n        # this happens when testing, and there is only a single test scope and no link to any\n        # other scopes\n        self\n      else\n        next_scope.find_global_scope()\n      end\n    end\n  end\n\n  def findresource(type, title = nil)\n    @compiler.catalog.resource(type, title)\n  end\n\n  # Initialize our new scope.  Defaults to having no parent.\n  def initialize(compiler, source: nil, resource: nil)\n    if compiler.is_a? Puppet::Parser::AbstractCompiler\n      @compiler = compiler\n    else\n      raise Puppet::DevError, _(\"you must pass a compiler instance to a new scope object\")\n    end\n\n    @source = source\n    @resource = resource\n\n    extend_with_functions_module\n\n    # The symbol table for this scope.  This is where we store variables.\n    #    @symtable = Ephemeral.new(nil, true)\n    @symtable = LocalScope.new(nil)\n\n    @ephemeral = [MatchScope.new(@symtable, nil)]\n\n    # All of the defaults set for types.  It's a hash of hashes,\n    # with the first key being the type, then the second key being\n    # the parameter.\n    @defaults = Hash.new { |dhash, type|\n      dhash[type] = {}\n    }\n\n    # The table for storing class singletons.  This will only actually\n    # be used by top scopes and node scopes.\n    @class_scopes = {}\n  end\n\n  # Store the fact that we've evaluated a class, and store a reference to\n  # the scope in which it was evaluated, so that we can look it up later.\n  def class_set(name, scope)\n    if parent\n      parent.class_set(name, scope)\n    else\n      @class_scopes[name] = scope\n    end\n  end\n\n  # Return the scope associated with a class.  This is just here so\n  # that subclasses can set their parent scopes to be the scope of\n  # their parent class, and it's also used when looking up qualified\n  # variables.\n  def class_scope(klass)\n    # They might pass in either the class or class name\n    k = klass.respond_to?(:name) ? klass.name : klass\n    @class_scopes[k] || (parent && parent.class_scope(k))\n  end\n\n  # Collect all of the defaults set at any higher scopes.\n  # This is a different type of lookup because it's\n  # additive -- it collects all of the defaults, with defaults\n  # in closer scopes overriding those in later scopes.\n  #\n  # The lookupdefaults searches in the order:\n  #\n  #   * inherited\n  #   * contained (recursive)\n  #   * self\n  #\n  def lookupdefaults(type)\n    values = {}\n\n    # first collect the values from the parents\n    if parent\n      parent.lookupdefaults(type).each { |var, value|\n        values[var] = value\n      }\n    end\n\n    # then override them with any current values\n    # this should probably be done differently\n    if @defaults.include?(type)\n      @defaults[type].each { |var, value|\n        values[var] = value\n      }\n    end\n\n    values\n  end\n\n  # Check if the given value is a known default for the given type\n  #\n  def is_default?(type, key, value)\n    defaults_for_type = @defaults[type]\n    unless defaults_for_type.nil?\n      default_param = defaults_for_type[key]\n      return true if !default_param.nil? && value == default_param.value\n    end\n    !parent.nil? && parent.is_default?(type, key, value)\n  end\n\n  # Look up a defined type.\n  def lookuptype(name)\n    # This happens a lot, avoid making a call to make a call\n    krt = environment.known_resource_types\n    krt.find_definition(name) || krt.find_hostclass(name)\n  end\n\n  def undef_as(x, v)\n    if v.nil? or v == :undef\n      x\n    else\n      v\n    end\n  end\n\n  # Lookup a variable within this scope using the Puppet language's\n  # scoping rules. Variables can be qualified using just as in a\n  # manifest.\n  #\n  # @param [String] name the variable name to lookup\n  # @param [Hash] hash of options, only internal code should give this\n  # @param [Boolean] if resolution is of the leaf of a qualified name - only internal code should give this\n  # @return Object the value of the variable, or if not found; nil if `strict_variables` is false, and thrown :undefined_variable otherwise\n  #\n  # @api public\n  def lookupvar(name, options = EMPTY_HASH)\n    unless name.is_a? String\n      raise Puppet::ParseError, _(\"Scope variable name %{name} is a %{klass}, not a string\") % { name: name.inspect, klass: name.class }\n    end\n\n    # If name has '::' in it, it is resolved as a qualified variable\n    unless (idx = name.index('::')).nil?\n      # Always drop leading '::' if present as that is how the values are keyed.\n      return lookup_qualified_variable(idx == 0 ? name[2..] : name, options)\n    end\n\n    # At this point, search is for a non qualified (simple) name\n    table = @ephemeral.last\n    val = table[name]\n    return val unless val.nil? && !table.include?(name)\n\n    next_scope = inherited_scope || enclosing_scope\n    if next_scope\n      next_scope.lookupvar(name, options)\n    else\n      variable_not_found(name)\n    end\n  end\n\n  UNDEFINED_VARIABLES_KIND = 'undefined_variables'\n\n  # The exception raised when a throw is uncaught is different in different versions\n  # of ruby. In >=2.2.0 it is UncaughtThrowError (which did not exist prior to this)\n  #\n  UNCAUGHT_THROW_EXCEPTION = defined?(UncaughtThrowError) ? UncaughtThrowError : ArgumentError\n\n  def variable_not_found(name, reason = nil)\n    # Built in variables and numeric variables always exist\n    if BUILT_IN_VARS.include?(name) || name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME\n      return nil\n    end\n\n    begin\n      throw(:undefined_variable, reason)\n    rescue UNCAUGHT_THROW_EXCEPTION\n      case Puppet[:strict]\n      when :off\n        # do nothing\n      when :warning\n        Puppet.warn_once(UNDEFINED_VARIABLES_KIND, _(\"Variable: %{name}\") % { name: name },\n                         _(\"Undefined variable '%{name}'; %{reason}\") % { name: name, reason: reason })\n      when :error\n        if Puppet.lookup(:avoid_hiera_interpolation_errors) { false }\n          Puppet.warn_once(UNDEFINED_VARIABLES_KIND, _(\"Variable: %{name}\") % { name: name },\n                           _(\"Interpolation failed with '%{name}', but compilation continuing; %{reason}\") % { name: name, reason: reason })\n        else\n          raise ArgumentError, _(\"Undefined variable '%{name}'; %{reason}\") % { name: name, reason: reason }\n        end\n      end\n    end\n    nil\n  end\n\n  # Retrieves the variable value assigned to the name given as an argument. The name must be a String,\n  # and namespace can be qualified with '::'. The value is looked up in this scope, its parent scopes,\n  # or in a specific visible named scope.\n  #\n  # @param varname [String] the name of the variable (may be a qualified name using `(ns'::')*varname`\n  # @param options [Hash] Additional options, not part of api.\n  # @return [Object] the value assigned to the given varname\n  # @see #[]=\n  # @api public\n  #\n  def [](varname, options = EMPTY_HASH)\n    lookupvar(varname, options)\n  end\n\n  # The class scope of the inherited thing of this scope's resource.\n  #\n  # @return [Puppet::Parser::Scope] The scope or nil if there is not an inherited scope\n  def inherited_scope\n    if resource && resource.type == TYPENAME_CLASS && !resource.resource_type.parent.nil?\n      qualified_scope(resource.resource_type.parent)\n    else\n      nil\n    end\n  end\n\n  # The enclosing scope (topscope or nodescope) of this scope.\n  # The enclosing scopes are produced when a class or define is included at\n  # some point. The parent scope of the included class or define becomes the\n  # scope in which it was included. The chain of parent scopes is followed\n  # until a node scope or the topscope is found\n  #\n  # @return [Puppet::Parser::Scope] The scope or nil if there is no enclosing scope\n  def enclosing_scope\n    if has_enclosing_scope?\n      if parent.is_topscope? || parent.is_nodescope?\n        parent\n      else\n        parent.enclosing_scope\n      end\n    end\n  end\n\n  def is_classscope?\n    resource && resource.type == TYPENAME_CLASS\n  end\n\n  def is_nodescope?\n    resource && resource.type == TYPENAME_NODE\n  end\n\n  def is_topscope?\n    equal?(@compiler.topscope)\n  end\n\n  # @api private\n  def lookup_qualified_variable(fqn, options)\n    table = @compiler.qualified_variables\n    val = table[fqn]\n    return val if !val.nil? || table.include?(fqn)\n\n    # not found - search inherited scope for class\n    leaf_index = fqn.rindex('::')\n    unless leaf_index.nil?\n      leaf_name = fqn[(leaf_index + 2)..]\n      class_name = fqn[0, leaf_index]\n      begin\n        qs = qualified_scope(class_name)\n        unless qs.nil?\n          return qs.get_local_variable(leaf_name) if qs.has_local_variable?(leaf_name)\n\n          iscope = qs.inherited_scope\n          return lookup_qualified_variable(\"#{iscope.source.name}::#{leaf_name}\", options) unless iscope.nil?\n        end\n      rescue RuntimeError => e\n        # because a failure to find the class, or inherited should be reported against given name\n        return handle_not_found(class_name, leaf_name, options, e.message)\n      end\n    end\n    # report with leading '::' by using empty class_name\n    handle_not_found('', fqn, options)\n  end\n\n  # @api private\n  def has_local_variable?(name)\n    @ephemeral.last.include?(name)\n  end\n\n  # @api private\n  def get_local_variable(name)\n    @ephemeral.last[name]\n  end\n\n  def handle_not_found(class_name, variable_name, position, reason = nil)\n    unless Puppet[:strict_variables]\n      # Do not issue warning if strict variables are on, as an error will be raised by variable_not_found\n      location = if position[:lineproc]\n                   Puppet::Util::Errors.error_location_with_space(nil, position[:lineproc].call)\n                 else\n                   Puppet::Util::Errors.error_location_with_space(position[:file], position[:line])\n                 end\n      variable_not_found(\"#{class_name}::#{variable_name}\", \"#{reason}#{location}\")\n      return nil\n    end\n    variable_not_found(\"#{class_name}::#{variable_name}\", reason)\n  end\n\n  def has_enclosing_scope?\n    !parent.nil?\n  end\n  private :has_enclosing_scope?\n\n  def qualified_scope(classname)\n    klass = find_hostclass(classname)\n    raise _(\"class %{classname} could not be found\") % { classname: classname } unless klass\n\n    kscope = class_scope(klass)\n    raise _(\"class %{classname} has not been evaluated\") % { classname: classname } unless kscope\n\n    kscope\n  end\n  private :qualified_scope\n\n  # Returns a Hash containing all variables and their values, optionally (and\n  # by default) including the values defined in parent. Local values\n  # shadow parent values. Ephemeral scopes for match results ($0 - $n) are not included.\n  # Optionally include the variables that are explicitly set to `undef`.\n  #\n  def to_hash(recursive = true, include_undef = false)\n    if recursive and has_enclosing_scope?\n      target = enclosing_scope.to_hash(recursive)\n      unless (inherited = inherited_scope).nil?\n        target.merge!(inherited.to_hash(recursive))\n      end\n    else\n      target = Hash.new\n    end\n\n    # add all local scopes\n    @ephemeral.last.add_entries_to(target, include_undef)\n    target\n  end\n\n  # Create a new scope and set these options.\n  def newscope(options = {})\n    compiler.newscope(self, options)\n  end\n\n  def parent_module_name\n    return nil unless @parent && @parent.source\n\n    @parent.source.module_name\n  end\n\n  # Set defaults for a type.  The typename should already be downcased,\n  # so that the syntax is isolated.  We don't do any kind of type-checking\n  # here; instead we let the resource do it when the defaults are used.\n  def define_settings(type, params)\n    table = @defaults[type]\n\n    # if we got a single param, it'll be in its own array\n    params = [params] unless params.is_a?(Array)\n\n    params.each { |param|\n      if table.include?(param.name)\n        raise Puppet::ParseError.new(_(\"Default already defined for %{type} { %{param} }; cannot redefine\") % { type: type, param: param.name }, param.file, param.line)\n      end\n\n      table[param.name] = param\n    }\n  end\n\n  # Merge all settings for the given _env_name_ into this scope\n  # @param env_name [Symbol] the name of the environment\n  # @param set_in_this_scope [Boolean] if the settings variables should also be set in this instance of scope\n  def merge_settings(env_name, set_in_this_scope = true)\n    settings = Puppet.settings\n    table = effective_symtable(false)\n    global_table = compiler.qualified_variables\n    all_local = {}\n    settings.each_key do |name|\n      next if :name == name\n\n      key = name.to_s\n      value = transform_setting(settings.value_sym(name, env_name))\n      if set_in_this_scope\n        table[key] = value\n      end\n      all_local[key] = value\n      # also write the fqn into global table for direct lookup\n      global_table[\"settings::#{key}\"] = value\n    end\n    # set the 'all_local' - a hash of all settings\n    global_table[\"settings::all_local\"] = all_local\n    nil\n  end\n\n  def transform_setting(val)\n    case val\n    when String, Numeric, true, false, nil\n      val\n    when Array\n      val.map { |entry| transform_setting(entry) }\n    when Hash\n      result = {}\n      val.each { |k, v| result[transform_setting(k)] = transform_setting(v) }\n      result\n    else\n      # not ideal, but required as there are settings values that are special\n      :undef == val ? nil : val.to_s\n    end\n  end\n  private :transform_setting\n\n  VARNAME_TRUSTED = 'trusted'\n  VARNAME_FACTS = 'facts'\n  VARNAME_SERVER_FACTS = 'server_facts'\n  RESERVED_VARIABLE_NAMES = [VARNAME_TRUSTED, VARNAME_FACTS].freeze\n  TYPENAME_CLASS = 'Class'\n  TYPENAME_NODE = 'Node'\n\n  # Set a variable in the current scope.  This will override settings\n  # in scopes above, but will not allow variables in the current scope\n  # to be reassigned.\n  #   It's preferred that you use self[]= instead of this; only use this\n  # when you need to set options.\n  def setvar(name, value, options = EMPTY_HASH)\n    if name =~ /^[0-9]+$/\n      raise Puppet::ParseError, _(\"Cannot assign to a numeric match result variable '$%{name}'\") % { name: name } # unless options[:ephemeral]\n    end\n    unless name.is_a? String\n      raise Puppet::ParseError, _(\"Scope variable name %{name} is a %{class_type}, not a string\") % { name: name.inspect, class_type: name.class }\n    end\n\n    # Check for reserved variable names\n    if (name == VARNAME_TRUSTED || name == VARNAME_FACTS) && !options[:privileged]\n      raise Puppet::ParseError, _(\"Attempt to assign to a reserved variable name: '%{name}'\") % { name: name }\n    end\n\n    # Check for server_facts reserved variable name\n    if name == VARNAME_SERVER_FACTS && !options[:privileged]\n      raise Puppet::ParseError, _(\"Attempt to assign to a reserved variable name: '%{name}'\") % { name: name }\n    end\n\n    table = effective_symtable(options[:ephemeral])\n    if table.bound?(name)\n      error = Puppet::ParseError.new(_(\"Cannot reassign variable '$%{name}'\") % { name: name })\n      error.file = options[:file] if options[:file]\n      error.line = options[:line] if options[:line]\n      raise error\n    end\n\n    table[name] = value\n\n    # Assign the qualified name in the environment\n    # Note that Settings scope has a source set to Boolean true.\n    #\n    # Only meaningful to set a fqn globally if table to assign to is the top of the scope's ephemeral stack\n    if @symtable.equal?(table)\n      if is_topscope?\n        # the scope name is '::'\n        compiler.qualified_variables[name] = value\n      elsif source.is_a?(Puppet::Resource::Type) && source.type == :hostclass\n        # the name is the name of the class\n        sourcename = source.name\n        compiler.qualified_variables[\"#{sourcename}::#{name}\"] = value\n      end\n    end\n    value\n  end\n\n  def set_trusted(hash)\n    setvar('trusted', deep_freeze(hash), :privileged => true)\n  end\n\n  def set_facts(hash)\n    setvar('facts', deep_freeze(hash), :privileged => true)\n  end\n\n  def set_server_facts(hash)\n    setvar('server_facts', deep_freeze(hash), :privileged => true)\n  end\n\n  # Deeply freezes the given object. The object and its content must be of the types:\n  # Array, Hash, Numeric, Boolean, Regexp, NilClass, or String. All other types raises an Error.\n  # (i.e. if they are assignable to Puppet::Pops::Types::Data type).\n  #\n  def deep_freeze(object)\n    case object\n    when Array\n      object.each { |v| deep_freeze(v) }\n      object.freeze\n    when Hash\n      object.each { |k, v| deep_freeze(k); deep_freeze(v) }\n      object.freeze\n    when NilClass, Numeric, TrueClass, FalseClass\n      # do nothing\n    when String\n      object.freeze\n    else\n      raise Puppet::Error, _(\"Unsupported data type: '%{klass}'\") % { klass: object.class }\n    end\n    object\n  end\n  private :deep_freeze\n\n  # Return the effective \"table\" for setting variables.\n  # This method returns the first ephemeral \"table\" that acts as a local scope, or this\n  # scope's symtable. If the parameter `use_ephemeral` is true, the \"top most\" ephemeral \"table\"\n  # will be returned (irrespective of it being a match scope or a local scope).\n  #\n  # @param use_ephemeral [Boolean] whether the top most ephemeral (of any kind) should be used or not\n  def effective_symtable(use_ephemeral)\n    s = @ephemeral[-1]\n    return s || @symtable if use_ephemeral\n\n    while s && !s.is_local_scope?()\n      s = s.parent\n    end\n    s || @symtable\n  end\n\n  # Sets the variable value of the name given as an argument to the given value. The value is\n  # set in the current scope and may shadow a variable with the same name in a visible outer scope.\n  # It is illegal to re-assign a variable in the same scope. It is illegal to set a variable in some other\n  # scope/namespace than the scope passed to a method.\n  #\n  # @param varname [String] The variable name to which the value is assigned. Must not contain `::`\n  # @param value [String] The value to assign to the given variable name.\n  # @param options [Hash] Additional options, not part of api and no longer used.\n  #\n  # @api public\n  #\n  def []=(varname, value, _ = nil)\n    setvar(varname, value)\n  end\n\n  # Used mainly for logging\n  def to_s\n    # As this is used for logging, this should really not be done in this class at all...\n    return \"Scope(#{@resource})\" unless @resource.nil?\n\n    # For logging of function-scope - it is now showing the file and line.\n    detail = Puppet::Pops::PuppetStack.top_of_stack\n    return \"Scope()\" if detail.empty?\n\n    # shorten the path if possible\n    path = detail[0]\n    env_path = nil\n    env_path = environment.configuration.path_to_env unless environment.nil? || environment.configuration.nil?\n    # check module paths first since they may be in the environment (i.e. they are longer)\n    module_path = environment.full_modulepath.detect { |m_path| path.start_with?(m_path) }\n    if module_path\n      path = \"<module>\" + path[module_path.length..]\n    elsif env_path && path && path.start_with?(env_path)\n      path = \"<env>\" + path[env_path.length..]\n    end\n    # Make the output appear as \"Scope(path, line)\"\n    \"Scope(#{[path, detail[1]].join(', ')})\"\n  end\n\n  alias_method :inspect, :to_s\n\n  # Pop ephemeral scopes up to level and return them\n  #\n  # @param level [Integer] a positive integer\n  # @return [Array] the removed ephemeral scopes\n  # @api private\n  def pop_ephemerals(level)\n    @ephemeral.pop(@ephemeral.size - level)\n  end\n\n  # Push ephemeral scopes onto the ephemeral scope stack\n  # @param ephemeral_scopes [Array]\n  # @api private\n  def push_ephemerals(ephemeral_scopes)\n    ephemeral_scopes.each { |ephemeral_scope| @ephemeral.push(ephemeral_scope) } unless ephemeral_scopes.nil?\n  end\n\n  def ephemeral_level\n    @ephemeral.size\n  end\n\n  # TODO: Who calls this?\n  def new_ephemeral(local_scope = false)\n    if local_scope\n      @ephemeral.push(LocalScope.new(@ephemeral.last))\n    else\n      @ephemeral.push(MatchScope.new(@ephemeral.last, nil))\n    end\n  end\n\n  # Execute given block in global scope with no ephemerals present\n  #\n  # @yieldparam [Scope] global_scope the global and ephemeral less scope\n  # @return [Object] the return of the block\n  #\n  # @api private\n  def with_global_scope(&block)\n    find_global_scope.without_ephemeral_scopes(&block)\n  end\n\n  # Execute given block with a ephemeral scope containing the given variables\n  # @api private\n  def with_local_scope(scope_variables)\n    local = LocalScope.new(@ephemeral.last)\n    scope_variables.each_pair { |k, v| local[k] = v }\n    @ephemeral.push(local)\n    begin\n      yield(self)\n    ensure\n      @ephemeral.pop\n    end\n  end\n\n  # Execute given block with all ephemeral popped from the ephemeral stack\n  #\n  # @api private\n  def without_ephemeral_scopes\n    save_ephemeral = @ephemeral\n    begin\n      @ephemeral = [@symtable]\n      yield(self)\n    ensure\n      @ephemeral = save_ephemeral\n    end\n  end\n\n  # Nests a parameter scope\n  # @param [String] callee_name the name of the function, template, or resource that defines the parameters\n  # @param [Array<String>] param_names list of parameter names\n  # @yieldparam [ParameterScope] param_scope the nested scope\n  # @api private\n  def with_parameter_scope(callee_name, param_names)\n    param_scope = ParameterScope.new(@ephemeral.last, callee_name, param_names)\n    with_guarded_scope do\n      @ephemeral.push(param_scope)\n      yield(param_scope)\n    end\n  end\n\n  # Execute given block and ensure that ephemeral level is restored\n  #\n  # @return [Object] the return of the block\n  #\n  # @api private\n  def with_guarded_scope\n    elevel = ephemeral_level\n    begin\n      yield\n    ensure\n      pop_ephemerals(elevel)\n    end\n  end\n\n  # Sets match data in the most nested scope (which always is a MatchScope), it clobbers match data already set there\n  #\n  def set_match_data(match_data)\n    @ephemeral.last.match_data = match_data\n  end\n\n  # Nests a match data scope\n  def new_match_scope(match_data)\n    @ephemeral.push(MatchScope.new(@ephemeral.last, match_data))\n  end\n\n  def ephemeral_from(match, file = nil, line = nil)\n    case match\n    when Hash\n      # Create local scope ephemeral and set all values from hash\n      new_ephemeral(true)\n      match.each { |k, v| setvar(k, v, :file => file, :line => line, :ephemeral => true) }\n      # Must always have an inner match data scope (that starts out as transparent)\n      # In 3x slightly wasteful, since a new nested scope is created for a match\n      # (TODO: Fix that problem)\n      new_ephemeral(false)\n    else\n      raise(ArgumentError, _(\"Invalid regex match data. Got a %{klass}\") % { klass: match.class }) unless match.is_a?(MatchData)\n\n      # Create a match ephemeral and set values from match data\n      new_match_scope(match)\n    end\n  end\n\n  # @api private\n  def find_resource_type(type)\n    raise Puppet::DevError, _(\"Scope#find_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\")\n  end\n\n  # @api private\n  def find_builtin_resource_type(type)\n    raise Puppet::DevError, _(\"Scope#find_builtin_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\")\n  end\n\n  # @api private\n  def find_defined_resource_type(type)\n    raise Puppet::DevError, _(\"Scope#find_defined_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\")\n  end\n\n  def method_missing(method, *args, &block)\n    method.to_s =~ /^function_(.*)$/\n    name = ::Regexp.last_match(1)\n    super unless name\n    super unless Puppet::Parser::Functions.function(name)\n    # In odd circumstances, this might not end up defined by the previous\n    # method, so we might as well be certain.\n    if respond_to? method\n      send(method, *args)\n    else\n      raise Puppet::DevError, _(\"Function %{name} not defined despite being loaded!\") % { name: name }\n    end\n  end\n\n  # To be removed when enough time has passed after puppet 5.0.0\n  # @api private\n  def resolve_type_and_titles(type, titles)\n    raise Puppet::DevError, _(\"Scope#resolve_type_and_title() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\")\n  end\n\n  # Transforms references to classes to the form suitable for\n  # lookup in the compiler.\n  #\n  # Makes names passed in the names array absolute if they are relative.\n  #\n  # Transforms Class[] and Resource[] type references to class name\n  # or raises an error if a Class[] is unspecific, if a Resource is not\n  # a 'class' resource, or if unspecific (no title).\n  #\n  #\n  # @param names [Array<String>] names to (optionally) make absolute\n  # @return [Array<String>] names after transformation\n  #\n  def transform_and_assert_classnames(names)\n    names.map do |name|\n      case name\n      when NilClass\n        raise ArgumentError, _(\"Cannot use undef as a class name\")\n      when String\n        raise ArgumentError, _(\"Cannot use empty string as a class name\") if name.empty?\n\n        name.sub(/^([^:]{1,2})/, '::\\1')\n\n      when Puppet::Resource\n        assert_class_and_title(name.type, name.title)\n        name.title.sub(/^([^:]{1,2})/, '::\\1')\n\n      when Puppet::Pops::Types::PClassType\n        # TRANSLATORS \"Class\" and \"Type\" are Puppet keywords and should not be translated\n        raise ArgumentError, _(\"Cannot use an unspecific Class[] Type\") unless name.class_name\n\n        name.class_name.sub(/^([^:]{1,2})/, '::\\1')\n\n      when Puppet::Pops::Types::PResourceType\n        assert_class_and_title(name.type_name, name.title)\n        name.title.sub(/^([^:]{1,2})/, '::\\1')\n      end.downcase\n    end\n  end\n\n  # Calls a 3.x or 4.x function by name with arguments given in an array using the 4.x calling convention\n  # and returns the result.\n  # Note that it is the caller's responsibility to rescue the given ArgumentError and provide location information\n  # to aid the user find the problem. The problem is otherwise reported against the source location that\n  # invoked the function that ultimately called this method.\n  #\n  # @return [Object] the result of the called function\n  # @raise ArgumentError if the function does not exist\n  def call_function(func_name, args, &block)\n    Puppet::Pops::Parser::EvaluatingParser.new.evaluator.external_call_function(func_name, args, self, &block)\n  end\n\n  private\n\n  def assert_class_and_title(type_name, title)\n    if type_name.nil? || type_name == ''\n      # TRANSLATORS \"Resource\" is a class name and should not be translated\n      raise ArgumentError, _(\"Cannot use an unspecific Resource[] where a Resource['class', name] is expected\")\n    end\n    unless type_name =~ /^[Cc]lass$/\n      # TRANSLATORS \"Resource\" is a class name and should not be translated\n      raise ArgumentError, _(\"Cannot use a Resource[%{type_name}] where a Resource['class', name] is expected\") % { type_name: type_name }\n    end\n    if title.nil?\n      # TRANSLATORS \"Resource\" is a class name and should not be translated\n      raise ArgumentError, _(\"Cannot use an unspecific Resource['class'] where a Resource['class', name] is expected\")\n    end\n  end\n\n  def extend_with_functions_module\n    root = Puppet.lookup(:root_environment)\n    extend Puppet::Parser::Functions.environment_module(root)\n    extend Puppet::Parser::Functions.environment_module(environment) if environment != root\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/script_compiler.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/loaders'\nrequire_relative '../../puppet/pops'\n\n# A Script \"compiler\" that does not support catalog operations\n#\n# The Script compiler is \"one shot\" - it does not support rechecking if underlying source has changed or\n# deal with possible errors in a cached environment.\n#\nclass Puppet::Parser::ScriptCompiler\n  # Allows the ScriptCompiler to use the 3.x Scope class without being an actual \"Compiler\"\n  #\n  include Puppet::Parser::AbstractCompiler\n\n  # @api private\n  attr_reader :topscope\n\n  # @api private\n  attr_reader :qualified_variables\n\n  # Access to the configured loaders for 4x\n  # @return [Puppet::Pops::Loader::Loaders] the configured loaders\n  # @api private\n  attr_reader :loaders\n\n  # @api private\n  attr_reader :environment\n\n  # @api private\n  attr_reader :node_name\n\n  def with_context_overrides(description = '', &block)\n    Puppet.override(@context_overrides, description, &block)\n  end\n\n  # Evaluates the configured setup for a script + code in an environment with modules\n  #\n  def compile\n    Puppet[:strict_variables] = true\n    Puppet[:strict] = :error\n\n    # TRANSLATORS, \"For running script\" is not user facing\n    Puppet.override(@context_overrides, \"For running script\") do\n      # TRANSLATORS \"main\" is a function name and should not be translated\n      result = Puppet::Util::Profiler.profile(_(\"Script: Evaluated main\"), [:script, :evaluate_main]) { evaluate_main }\n      if block_given?\n        yield self\n      else\n        result\n      end\n    end\n  rescue Puppet::ParseErrorWithIssue => detail\n    detail.node = node_name\n    Puppet.log_exception(detail)\n    raise\n  rescue => detail\n    message = \"#{detail} on node #{node_name}\"\n    Puppet.log_exception(detail, message)\n    raise Puppet::Error, message, detail.backtrace\n  end\n\n  # Constructs the overrides for the context\n  def context_overrides\n    {\n      :current_environment => environment,\n      :global_scope => @topscope, # 4x placeholder for new global scope\n      :loaders => @loaders, # 4x loaders\n      :rich_data => true,\n    }\n  end\n\n  # Create a script compiler for the given environment where errors are logged as coming\n  # from the given node_name\n  #\n  def initialize(environment, node_name, for_agent = false)\n    @environment = environment\n    @node_name = node_name\n\n    # Create the initial scope, it is needed early\n    @topscope = Puppet::Parser::Scope.new(self)\n\n    # Initialize loaders and Pcore\n    if for_agent\n      @loaders = Puppet::Pops::Loaders.new(environment, true)\n    else\n      @loaders = Puppet::Pops::Loaders.new(environment)\n    end\n\n    # Need to compute overrides here, and remember them, because we are about to\n    # Expensive entries in the context are bound lazily.\n    @context_overrides = context_overrides()\n\n    # Resolutions of fully qualified variable names\n    @qualified_variables = {}\n  end\n\n  # Having multiple named scopes hanging from top scope is not supported when scripting\n  # in the regular compiler this is used to create one named scope per class.\n  # When scripting, the \"main class\" is just a container of the top level code to evaluate\n  # and it is not evaluated as a class added to a catalog. Since classes are not supported\n  # there is no need to support the concept of \"named scopes\" as all variables are local\n  # or in the top scope itself (notably, the $settings:: namespace is initialized\n  # as just a set of variables in that namespace - there is no named scope for 'settings'\n  # when scripting.\n  #\n  # Keeping this method here to get specific error as being unsure if there are functions/logic\n  # that will call this. The AbstractCompiler defines this method, but maybe it does not\n  # have to (TODO).\n  #\n  def newscope(parent, options = {})\n    raise _('having multiple named scopes is not supported when scripting')\n  end\n\n  private\n\n  # Find and evaluate the top level code.\n  def evaluate_main\n    @loaders.pre_load\n    program = @loaders.load_main_manifest\n    program.nil? ? nil : Puppet::Pops::Parser::EvaluatingParser.singleton.evaluator.evaluate(program, @topscope)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/templatewrapper.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parser/files'\nrequire 'erb'\nrequire_relative '../../puppet/file_system'\n\n# A simple wrapper for templates, so they don't have full access to\n# the scope objects.\n#\n# @api private\nclass Puppet::Parser::TemplateWrapper\n  include Puppet::Util\n  Puppet::Util.logmethods(self)\n\n  def initialize(scope)\n    @__scope__ = scope\n  end\n\n  # @return [String] The full path name of the template that is being executed\n  # @api public\n  def file\n    @__file__\n  end\n\n  # @return [Puppet::Parser::Scope] The scope in which the template is evaluated\n  # @api public\n  def scope\n    @__scope__\n  end\n\n  # Find which line in the template (if any) we were called from.\n  # @return [String] the line number\n  # @api private\n  def script_line\n    identifier = Regexp.escape(@__file__ || \"(erb)\")\n    (caller.find { |l| l =~ /#{identifier}:/ } || \"\")[/:(\\d+):/, 1]\n  end\n  private :script_line\n\n  # Should return true if a variable is defined, false if it is not\n  # @api public\n  def has_variable?(name)\n    scope.include?(name.to_s)\n  end\n\n  # @return [Array<String>] The list of defined classes\n  # @api public\n  def classes\n    scope.catalog.classes\n  end\n\n  # @return [Array<String>] The tags defined in the current scope\n  # @api public\n  def tags\n    raise NotImplementedError, \"Call 'all_tags' instead.\"\n  end\n\n  # @return [Array<String>] All the defined tags\n  # @api public\n  def all_tags\n    scope.catalog.tags\n  end\n\n  # @api private\n  def file=(filename)\n    @__file__ = Puppet::Parser::Files.find_template(filename, scope.compiler.environment)\n    unless @__file__\n      raise Puppet::ParseError, _(\"Could not find template '%{filename}'\") % { filename: filename }\n    end\n  end\n\n  # @api private\n  def result(string = nil)\n    if string\n      template_source = \"inline template\"\n    else\n      string = Puppet::FileSystem.read_preserve_line_endings(@__file__)\n      template_source = @__file__\n    end\n\n    # Expose all the variables in our scope as instance variables of the\n    # current object, making it possible to access them without conflict\n    # to the regular methods.\n    escaped_template_source = template_source.gsub(/%/, '%%')\n    benchmark(:debug, _(\"Bound template variables for %{template_source} in %%{seconds} seconds\") % { template_source: escaped_template_source }) do\n      scope.to_hash.each do |name, value|\n        realname = name.gsub(/[^\\w]/, \"_\")\n        instance_variable_set(\"@#{realname}\", value)\n      end\n    end\n\n    result = nil\n    benchmark(:debug, _(\"Interpolated template %{template_source} in %%{seconds} seconds\") % { template_source: escaped_template_source }) do\n      template = Puppet::Util.create_erb(string)\n      template.filename = @__file__\n      result = template.result(binding)\n    end\n\n    result\n  end\n\n  def to_s\n    \"template[#{@__file__ || 'inline'}]\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser/type_loader.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'find'\nrequire 'forwardable'\nrequire_relative '../../puppet/parser/parser_factory'\n\nclass Puppet::Parser::TypeLoader\n  extend Forwardable\n\n  class TypeLoaderError < StandardError; end\n\n  # Import manifest files that match a given file glob pattern.\n  #\n  # @param pattern [String] the file glob to apply when determining which files\n  #   to load\n  # @param dir [String] base directory to use when the file is not\n  #   found in a module\n  # @api private\n  def import(pattern, dir)\n    modname, files = Puppet::Parser::Files.find_manifests_in_modules(pattern, environment)\n    if files.empty?\n      abspat = File.expand_path(pattern, dir)\n      file_pattern = abspat + (File.extname(abspat).empty? ? '.pp' : '')\n\n      files = Dir.glob(file_pattern).uniq.reject { |f| FileTest.directory?(f) }\n      modname = nil\n\n      if files.empty?\n        raise_no_files_found(pattern)\n      end\n    end\n\n    load_files(modname, files)\n  end\n\n  # Load all of the manifest files in all known modules.\n  # @api private\n  def import_all\n    # And then load all files from each module, but (relying on system\n    # behavior) only load files from the first module of a given name.  E.g.,\n    # given first/foo and second/foo, only files from first/foo will be loaded.\n    environment.modules.each do |mod|\n      load_files(mod.name, mod.all_manifests)\n    end\n  end\n\n  def_delegator :environment, :known_resource_types\n\n  def initialize(env)\n    self.environment = env\n  end\n\n  def environment\n    @environment\n  end\n\n  def environment=(env)\n    if env.is_a?(String) or env.is_a?(Symbol)\n      @environment = Puppet.lookup(:environments).get!(env)\n    else\n      @environment = env\n    end\n  end\n\n  # Try to load the object with the given fully qualified name.\n  def try_load_fqname(type, fqname)\n    return nil if fqname == \"\" # special-case main.\n\n    files_to_try_for(fqname).each do |filename|\n      imported_types = import_from_modules(filename)\n      result = imported_types.find { |t| t.type == type and t.name == fqname }\n      if result\n        Puppet.debug { \"Automatically imported #{fqname} from #{filename} into #{environment}\" }\n        return result\n      end\n    rescue TypeLoaderError\n      # I'm not convinced we should just drop these errors, but this\n      # preserves existing behaviours.\n    end\n    # Nothing found.\n    nil\n  end\n\n  def parse_file(file)\n    Puppet.debug { \"importing '#{file}' in environment #{environment}\" }\n    parser = Puppet::Parser::ParserFactory.parser\n    parser.file = file\n    parser.parse\n  end\n\n  private\n\n  def import_from_modules(pattern)\n    modname, files = Puppet::Parser::Files.find_manifests_in_modules(pattern, environment)\n    if files.empty?\n      raise_no_files_found(pattern)\n    end\n\n    load_files(modname, files)\n  end\n\n  def raise_no_files_found(pattern)\n    raise TypeLoaderError, _(\"No file(s) found for import of '%{pattern}'\") % { pattern: pattern }\n  end\n\n  def load_files(modname, files)\n    @loaded ||= {}\n    loaded_asts = []\n    files.reject { |file| @loaded[file] }.each do |file|\n      # The squelch_parse_errors use case is for parsing for the purpose of searching\n      # for information and it should not abort.\n      # There is currently one user in indirector/resourcetype/parser\n      #\n      if Puppet.lookup(:squelch_parse_errors) { false }\n        begin\n          loaded_asts << parse_file(file)\n        rescue => e\n          # Resume from errors so that all parseable files may\n          # still be parsed. Mark this file as loaded so that\n          # it would not be parsed next time (handle it as if\n          # it was successfully parsed).\n          Puppet.debug { \"Unable to parse '#{file}': #{e.message}\" }\n        end\n      else\n        loaded_asts << parse_file(file)\n      end\n\n      @loaded[file] = true\n    end\n\n    loaded_asts.collect do |ast|\n      known_resource_types.import_ast(ast, modname)\n    end.flatten\n  end\n\n  # Return a list of all file basenames that should be tried in order\n  # to load the object with the given fully qualified name.\n  def files_to_try_for(qualified_name)\n    qualified_name.split('::').inject([]) do |paths, name|\n      add_path_for_name(paths, name)\n    end\n  end\n\n  def add_path_for_name(paths, name)\n    if paths.empty?\n      [name]\n    else\n      paths.unshift(File.join(paths.first, name))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/parser.rb",
    "content": "# frozen_string_literal: true\n\n# is only needed to create the name space\nmodule Puppet::Parser; end\n\nrequire_relative 'parser/ast'\nrequire_relative 'parser/abstract_compiler'\nrequire_relative 'parser/compiler'\nrequire_relative 'parser/compiler/catalog_validator'\nrequire_relative '../puppet/resource/type_collection'\n\nrequire_relative 'parser/functions'\nrequire_relative 'parser/files'\nrequire_relative 'parser/relationship'\n\nrequire_relative '../puppet/resource/type'\nrequire 'monitor'\n\nrequire_relative 'parser/compiler/catalog_validator/relationship_validator'\n\n# PUP-3274 This should probably go someplace else\nclass Puppet::LexError < RuntimeError; end\n"
  },
  {
    "path": "lib/puppet/plugins/configuration.rb",
    "content": "# frozen_string_literal: true\n\n# Configures the Puppet Plugins, by registering extension points\n# and default implementations.\n#\n# See the respective configured services for more information.\n#\n# @api private\n#\nmodule Puppet::Plugins\n  module Configuration\n    require_relative '../../puppet/plugins/syntax_checkers'\n    require_relative '../../puppet/syntax_checkers/base64'\n    require_relative '../../puppet/syntax_checkers/json'\n    require_relative '../../puppet/syntax_checkers/pp'\n    require_relative '../../puppet/syntax_checkers/epp'\n\n    def self.load_plugins\n      # Register extensions\n      # -------------------\n      {\n        SyntaxCheckers::SYNTAX_CHECKERS_KEY => {\n          'json' => Puppet::SyntaxCheckers::Json.new,\n          'base64' => Puppet::SyntaxCheckers::Base64.new,\n          'pp' => Puppet::SyntaxCheckers::PP.new,\n          'epp' => Puppet::SyntaxCheckers::EPP.new\n        }\n      }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/plugins/syntax_checkers.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Plugins\nmodule SyntaxCheckers\n  # The lookup **key** for the multibind containing syntax checkers used to syntax check embedded string in non\n  # puppet DSL syntax.\n  # @api public\n  SYNTAX_CHECKERS_KEY = :'puppet::syntaxcheckers'\n\n  # SyntaxChecker is a Puppet Extension Point for the purpose of extending Puppet with syntax checkers.\n  # The intended use is to create a class derived from this class and then register it with the Puppet context\n  #\n  # Creating the Extension Class\n  # ----------------------------\n  # As an example, a class for checking custom xml (aware of some custom schemes) may be authored in\n  # say a puppet module called 'exampleorg/xmldata'. The name of the class should start with `Puppetx::<user>::<module>`,\n  # e.g. 'Puppetx::Exampleorg::XmlData::XmlChecker\" and\n  # be located in `lib/puppetx/exampleorg/xml_data/xml_checker.rb`. The Puppet Binder will auto-load this file when it\n  # has a binding to the class `Puppetx::Exampleorg::XmlData::XmlChecker'\n  # The Ruby Module `Puppetx` is created by Puppet, the remaining modules should be created by the loaded logic - e.g.:\n  #\n  # @example Defining an XmlChecker\n  #   module Puppetx::Exampleorg\n  #     module XmlData\n  #       class XmlChecker < Puppetx::Puppetlabs::SyntaxCheckers::SyntaxChecker\n  #         def check(text, syntax_identifier, acceptor, location_hash)\n  #            # do the checking\n  #         end\n  #       end\n  #     end\n  #   end\n  #\n  # Implementing the check method\n  # -----------------------------\n  # The implementation of the {#check} method should naturally perform syntax checking of the given text/string and\n  # produce found issues on the given `acceptor`. These can be warnings or errors. The method should return `false` if\n  # any warnings or errors were produced (it is up to the caller to check for error/warning conditions and report them\n  # to the user).\n  #\n  # Issues are reported by calling the given `acceptor`, which takes a severity (e.g. `:error`,\n  # or `:warning), an {Puppet::Pops::Issues::Issue} instance, and a {Puppet::Pops::Adapters::SourcePosAdapter}\n  # (which describes details about linenumber, position, and length of the problem area). Note that the\n  # `location_info` given to the check method holds information about the location of the string in its *container*\n  # (e.g. the source position of a Heredoc); this information can be used if more detailed information is not\n  # available, or combined if there are more details (relative to the start of the checked string).\n  #\n  # @example Reporting an issue\n  #    # create an issue with a symbolic name (that can serve as a reference to more details about the problem),\n  #    # make the name unique\n  #    issue = Puppet::Pops::Issues::issue(:EXAMPLEORG_XMLDATA_ILLEGAL_XML) { \"syntax error found in xml text\" }\n  #    source_pos = Puppet::Pops::Adapters::SourcePosAdapter.new()\n  #    source_pos.line = info[:line] # use this if there is no detail from the used parser\n  #    source_pos.pos = info[:pos]   # use this pos if there is no detail from used parser\n  #\n  #    # report it\n  #    acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, info[:file], source_pos, {}))\n  #\n  # There is usually a cap on the number of errors/warnings that are presented to the user, this is handled by the\n  # reporting logic, but care should be taken to not generate too many as the issues are kept in memory until\n  # the checker returns. The acceptor may set a limit and simply ignore issues past a certain (high) number of reported\n  # issues (this number is typically higher than the cap on issues reported to the user).\n  #\n  # The `syntax_identifier`\n  # -----------------------\n  # The extension makes use of a syntax identifier written in mime-style. This identifier can be something simple\n  # as 'xml', or 'json', but can also consist of several segments joined with '+' where the most specific syntax variant\n  # is placed first. When searching for a syntax checker; say for JSON having some special traits, say 'userdata', the\n  # author of the text may indicate this as the text having the syntax \"userdata+json\" - when a checker is looked up it is\n  # first checked if there is a checker for \"userdata+json\", if none is found, a lookup is made for \"json\" (since the text\n  # must at least be valid json). The given identifier is passed to the checker (to allow the same checker to check for\n  # several dialects/specializations).\n  #\n  # Use in Puppet DSL\n  # -----------------\n  # The Puppet DSL Heredoc support makes use of the syntax checker extension. A user of a\n  # heredoc can specify the syntax in the heredoc tag, e.g.`@(END:userdata+json)`.\n  #\n  #\n  # @abstract\n  #\n  class SyntaxChecker\n    # Checks the text for syntax issues and reports them to the given acceptor.\n    # This implementation is abstract, it raises {NotImplementedError} since a subclass should have implemented the\n    # method.\n    #\n    # @param text [String] The text to check\n    # @param syntax_identifier [String] The syntax identifier in mime style (e.g. 'json', 'json-patch+json', 'xml', 'myapp+xml'\n    # @option location_info [String] :file The filename where the string originates\n    # @option location_info [Integer] :line The line number identifying the location where the string is being used/checked\n    # @option location_info [Integer] :position The position on the line identifying the location where the string is being used/checked\n    # @return [Boolean] Whether the checked string had issues (warnings and/or errors) or not.\n    # @api public\n    #\n    def check(text, syntax_identifier, acceptor, location_info)\n      raise NotImplementedError, \"The class #{self.class.name} should have implemented the method check()\"\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/plugins.rb",
    "content": "# frozen_string_literal: true\n\n# The Puppet Plugins module defines extension points where plugins can be configured\n# to add or modify puppet's behavior. See the respective classes in this module for more\n# details.\n#\n# @api public\n# @since Puppet 4.0.0\n#\nmodule Puppet::Plugins\nend\n"
  },
  {
    "path": "lib/puppet/pops/adaptable.rb",
    "content": "# frozen_string_literal: true\n\n# Adaptable is a mix-in module that adds adaptability to a class.\n# This means that an adapter can\n# associate itself with an instance of the class and store additional data/have behavior.\n#\n# This mechanism should be used when there is a desire to keep implementation concerns separate.\n# In Ruby it is always possible to open and modify a class or instance to teach it new tricks, but it\n# is however not possible to do this for two different versions of some service at the same time.\n# The Adaptable pattern is also good when only a few of the objects of some class needs to have extra\n# information (again possible in Ruby by adding instance variables dynamically). In fact, the implementation\n# of Adaptable does just that; it adds an instance variable named after the adapter class and keeps an\n# instance of this class in this slot.\n#\n# @note the implementation details; the fact that an instance variable is used to keep the adapter\n#   instance data should not\n#   be exploited as the implementation of _being adaptable_ may change in the future.\n# @api private\n#\nmodule Puppet::Pops\nmodule Adaptable\n  # Base class for an Adapter.\n  #\n  # A typical adapter just defines some accessors.\n  #\n  # A more advanced adapter may need to setup the adapter based on the object it is adapting.\n  # @example Making Duck adaptable\n  #   class Duck\n  #     include Puppet::Pops::Adaptable\n  #   end\n  # @example Giving a Duck a nick name\n  #   class NickNameAdapter < Puppet::Pops::Adaptable::Adapter\n  #     attr_accessor :nick_name\n  #   end\n  #   d = Duck.new\n  #   NickNameAdapter.adapt(d).nick_name = \"Daffy\"\n  #   NickNameAdapter.get(d).nick_name # => \"Daffy\"\n  #\n  # @example Giving a Duck a more elaborate nick name\n  #   class NickNameAdapter < Puppet::Pops::Adaptable::Adapter\n  #     attr_accessor :nick_name, :object\n  #     def initialize o\n  #       @object = o\n  #       @nick_name = \"Yo\"\n  #     end\n  #     def nick_name\n  #       \"#{@nick_name}, the #{o.class.name}\"\n  #     end\n  #     def NickNameAdapter.create_adapter(o)\n  #       x = new o\n  #       x\n  #     end\n  #   end\n  #   d = Duck.new\n  #   n = NickNameAdapter.adapt(d)\n  #   n.nick_name # => \"Yo, the Duck\"\n  #   n.nick_name = \"Daffy\"\n  #   n.nick_name # => \"Daffy, the Duck\"\n  # @example Using a block to set values\n  #   NickNameAdapter.adapt(o) { |a| a.nick_name = \"Buddy!\" }\n  #   NickNameAdapter.adapt(o) { |a, o| a.nick_name = \"You're the best #{o.class.name} I met.\"}\n  #\n  class Adapter\n    # Returns an existing adapter for the given object, or nil, if the object is not\n    # adapted.\n    #\n    # @param o [Adaptable] object to get adapter from\n    # @return [Adapter<self>] an adapter of the same class as the receiver of #get\n    # @return [nil] if the given object o has not been adapted by the receiving adapter\n    # @raise [ArgumentError] if the object is not adaptable\n    #\n    def self.get(o)\n      attr_name = self_attr_name\n      o.instance_variable_get(attr_name)\n    end\n\n    # Returns an existing adapter for the given object, or creates a new adapter if the\n    # object has not been adapted, or the adapter has been cleared.\n    #\n    # @example Using a block to set values\n    #   NickNameAdapter.adapt(o) { |a| a.nick_name = \"Buddy!\" }\n    #   NickNameAdapter.adapt(o) { |a, o| a.nick_name = \"Your the best #{o.class.name} I met.\"}\n    # @overload adapt(o)\n    # @overload adapt(o, {|adapter| block})\n    # @overload adapt(o, {|adapter, o| block})\n    # @param o [Adaptable] object to add adapter to\n    # @yieldparam adapter [Adapter<self>] the created adapter\n    # @yieldparam o [Adaptable] optional, the given adaptable\n    # @param block [Proc] optional, evaluated in the context of the adapter (existing or new)\n    # @return [Adapter<self>] an adapter of the same class as the receiver of the call\n    # @raise [ArgumentError] if the given object o is not adaptable\n    #\n    def self.adapt(o, &block)\n      attr_name = self_attr_name\n      value = o.instance_variable_get(attr_name)\n      adapter = value || associate_adapter(create_adapter(o), o)\n      if block_given?\n        if block.arity == 1\n          block.call(adapter)\n        else\n          block.call(adapter, o)\n        end\n      end\n      adapter\n    end\n\n    # Creates a new adapter, associates it with the given object and returns the adapter.\n    #\n    # @example Using a block to set values\n    #   NickNameAdapter.adapt_new(o) { |a| a.nick_name = \"Buddy!\" }\n    #   NickNameAdapter.adapt_new(o) { |a, o| a.nick_name = \"Your the best #{o.class.name} I met.\"}\n    # This is used when a fresh adapter is wanted instead of possible returning an\n    # existing adapter as in the case of {Adapter.adapt}.\n    # @overload adapt_new(o)\n    # @overload adapt_new(o, {|adapter| block})\n    # @overload adapt_new(o, {|adapter, o| block})\n    # @yieldparam adapter [Adapter<self>] the created adapter\n    # @yieldparam o [Adaptable] optional, the given adaptable\n    # @param o [Adaptable] object to add adapter to\n    # @param block [Proc] optional, evaluated in the context of the new adapter\n    # @return [Adapter<self>] an adapter of the same class as the receiver of the call\n    # @raise [ArgumentError] if the given object o is not adaptable\n    #\n    def self.adapt_new(o, &block)\n      adapter = associate_adapter(create_adapter(o), o)\n      if block_given?\n        if block.arity == 1\n          block.call(adapter)\n        else\n          block.call(adapter, o)\n        end\n      end\n      adapter\n    end\n\n    # Clears the adapter set in the given object o. Returns any set adapter or nil.\n    # @param o [Adaptable] the object where the adapter should be cleared\n    # @return [Adapter] if an adapter was set\n    # @return [nil] if the adapter has not been set\n    #\n    def self.clear(o)\n      attr_name = self_attr_name\n      if o.instance_variable_defined?(attr_name)\n        o.send(:remove_instance_variable, attr_name)\n      else\n        nil\n      end\n    end\n\n    # This base version creates an instance of the class (i.e. an instance of the concrete subclass\n    # of Adapter). A Specialization may want to create an adapter instance specialized for the given target\n    # object.\n    # @param o [Adaptable] The object to adapt. This implementation ignores this variable, but a\n    #   specialization may want to initialize itself differently depending on the object it is adapting.\n    # @return [Adapter<self>] instance of the subclass of Adapter receiving the call\n    #\n    def self.create_adapter(o)\n      new\n    end\n\n    # Associates the given adapter with the given target object\n    # @param adapter [Adapter] the adapter to associate with the given object _o_\n    # @param o [Adaptable] the object to adapt\n    # @return [adapter] the given adapter\n    #\n    def self.associate_adapter(adapter, o)\n      o.instance_variable_set(self_attr_name, adapter)\n      adapter\n    end\n\n    # Returns a suitable instance variable name given a class name.\n    # The returned string is the fully qualified name of a class with '::' replaced by '_' since\n    # '::' is not allowed in an instance variable name.\n    # @param name [String] the fully qualified name of a class\n    # @return [String] the name with all '::' replaced by '_'\n    # @api private\n    #\n    def self.instance_var_name(name)\n      name.split(DOUBLE_COLON).join(USCORE)\n    end\n\n    # Returns the name of the class, or the name of the type if the class represents an Object type\n    # @return [String] the name of the class or type\n    def self.type_name\n      name\n    end\n\n    # Returns a suitable instance variable name for the _name_ of this instance. The name is created by calling\n    # Adapter#instance_var_name and then cached.\n    # @return [String] the instance variable name for _name_\n    # @api private\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    def self.self_attr_name\n      @attr_name_sym ||= :\"@#{instance_var_name(type_name)}\"\n    end\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/adapters.rb",
    "content": "# frozen_string_literal: true\n\n# The Adapters module contains adapters for Documentation, Origin, SourcePosition, and Loader.\n#\nmodule Puppet::Pops\nmodule Adapters\n  class ObjectIdCacheAdapter < Puppet::Pops::Adaptable::Adapter\n    attr_accessor :cache\n\n    def initialize\n      @cache = {}\n    end\n\n    # Retrieves a mutable hash with all stored values\n    def retrieve(o)\n      @cache[o.__id__] ||= {}\n    end\n  end\n\n  # A documentation adapter adapts an object with a documentation string.\n  # (The intended use is for a source text parser to extract documentation and store this\n  # in DocumentationAdapter instances).\n  #\n  class DocumentationAdapter < Adaptable::Adapter\n    # @return [String] The documentation associated with an object\n    attr_accessor :documentation\n  end\n\n  # An  empty alternative adapter is used when there is the need to\n  # attach a value to be used if the original is empty. This is used\n  # when a lazy evaluation takes place, and the decision how to handle an\n  # empty case must be delayed.\n  #\n  class EmptyAlternativeAdapter < Adaptable::Adapter\n    # @return [Object] The alternative value associated with an object\n    attr_accessor :empty_alternative\n  end\n\n  # This class is for backward compatibility only. It's not really an adapter but it is\n  # needed for the puppetlabs-strings gem\n  # @deprecated\n  class SourcePosAdapter\n    def self.adapt(object)\n      new(object)\n    end\n\n    def initialize(object)\n      @object = object\n    end\n\n    def file\n      @object.file\n    end\n\n    def line\n      @object.line\n    end\n\n    def pos\n      @object.pos\n    end\n\n    def extract_text\n      @object.locator.extract_text(@object.offset, @object.length)\n    end\n  end\n\n  # A LoaderAdapter adapts an object with a {Loader}. This is used to make further loading from the\n  # perspective of the adapted object take place in the perspective of this Loader.\n  #\n  # It is typically enough to adapt the root of a model as a search is made towards the root of the model\n  # until a loader is found, but there is no harm in duplicating this information provided a contained\n  # object is adapted with the correct loader.\n  #\n  # @see Utils#find_adapter\n  # @api private\n  class LoaderAdapter < Adaptable::Adapter\n    attr_accessor :loader_name\n\n    # Finds the loader to use when loading originates from the source position of the given argument.\n    #\n    # @param instance [Model::PopsObject] The model object\n    # @param file [String] the file from where the model was parsed\n    # @param default_loader [Loader] the loader to return if no loader is found for the model\n    # @return [Loader] the found loader or default_loader if it could not be found\n    #\n    def self.loader_for_model_object(model, file = nil, default_loader = nil)\n      loaders = Puppet.lookup(:loaders) { nil }\n      if loaders.nil?\n        default_loader || Loaders.static_loader\n      else\n        loader_name = loader_name_by_source(loaders.environment, model, file)\n        if loader_name.nil?\n          default_loader || loaders[Loader::ENVIRONMENT_PRIVATE]\n        else\n          loaders[loader_name]\n        end\n      end\n    end\n\n    class PathsAndNameCacheAdapter < Puppet::Pops::Adaptable::Adapter\n      attr_accessor :cache, :paths\n\n      def self.create_adapter(env)\n        adapter = super(env)\n        adapter.paths = env.modulepath.map { |p| Pathname.new(p) }\n        adapter.cache = {}\n        adapter\n      end\n    end\n\n    # Attempts to find the module that `instance` originates from by looking at it's {SourcePosAdapter} and\n    # compare the `locator.file` found there with the module paths given in the environment found in the\n    # given `scope`. If the file is found to be relative to a path, then the first segment of the relative\n    # path is interpreted as the name of a module. The object that the {SourcePosAdapter} is adapted to\n    # will then be adapted to the private loader for that module and that adapter is returned.\n    #\n    # The method returns `nil` when no module could be found.\n    #\n    # @param environment [Puppet::Node::Environment] the current environment\n    # @param instance [Model::PopsObject] the AST for the code\n    # @param file [String] the path to the file for the code or `nil`\n    # @return [String] the name of the loader associated with the source\n    # @api private\n    def self.loader_name_by_source(environment, instance, file)\n      file = instance.file if file.nil?\n      return nil if file.nil? || EMPTY_STRING == file\n\n      pn_adapter = PathsAndNameCacheAdapter.adapt(environment)\n      dir = File.dirname(file)\n      pn_adapter.cache.fetch(dir) do |key|\n        mod = find_module_for_dir(environment, pn_adapter.paths, dir)\n        loader_name = mod.nil? ? nil : \"#{mod.name} private\"\n        pn_adapter.cache[key] = loader_name\n      end\n    end\n\n    # @api private\n    def self.find_module_for_dir(environment, paths, dir)\n      return nil if dir.nil?\n\n      file_path = Pathname.new(dir)\n      paths.each do |path|\n        begin\n          relative_path = file_path.relative_path_from(path).to_s.split(File::SEPARATOR)\n        rescue ArgumentError\n          # file_path was not relative to the module_path. That's OK.\n          next\n        end\n        if relative_path.length > 1\n          mod = environment.module(relative_path[0])\n          return mod unless mod.nil?\n        end\n      end\n      nil\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/access_operator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n# AccessOperator handles operator []\n# This operator is part of evaluation.\n#\nclass AccessOperator\n  # Provides access to the Puppet 3.x runtime (scope, etc.)\n  # This separation has been made to make it easier to later migrate the evaluator to an improved runtime.\n  #\n  include Runtime3Support\n\n  attr_reader :semantic\n\n  # Initialize with AccessExpression to enable reporting issues\n  # @param access_expression [Model::AccessExpression] the semantic object being evaluated\n  # @return [void]\n  #\n  def initialize(access_expression)\n    @@access_visitor ||= Visitor.new(self, \"access\", 2, nil)\n    @semantic = access_expression\n  end\n\n  def access(o, scope, *keys)\n    @@access_visitor.visit_this_2(self, o, scope, keys)\n  end\n\n  protected\n\n  def access_Object(o, scope, keys)\n    type = Puppet::Pops::Types::TypeCalculator.infer_callable_methods_t(o)\n    if type.is_a?(Puppet::Pops::Types::TypeWithMembers)\n      access_func = type['[]']\n      return access_func.invoke(o, scope, keys) unless access_func.nil?\n    end\n    fail(Issues::OPERATOR_NOT_APPLICABLE, @semantic.left_expr, :operator => '[]', :left_value => o)\n  end\n\n  def access_Binary(o, scope, keys)\n    Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(access_String(o.binary_buffer, scope, keys))\n  end\n\n  def access_String(o, scope, keys)\n    keys.flatten!\n    result = case keys.size\n             when 0\n               fail(Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, { :actual => keys.size })\n             when 1\n               # Note that Ruby 1.8.7 requires a length of 1 to produce a String\n               k1 = Utils.to_n(keys[0])\n               bad_string_access_key_type(o, 0, k1.nil? ? keys[0] : k1) unless k1.is_a?(Integer)\n               k2 = 1\n               k1 = k1 < 0 ? o.length + k1 : k1 # abs pos\n               # if k1 is outside, a length of 1 always produces an empty string\n               if k1 < 0\n                 EMPTY_STRING\n               else\n                 o[k1, k2]\n               end\n             when 2\n               k1 = Utils.to_n(keys[0])\n               k2 = Utils.to_n(keys[1])\n               [k1, k2].each_with_index { |k, i| bad_string_access_key_type(o, i, k.nil? ? keys[i] : k) unless k.is_a?(Integer) }\n\n               k1 = k1 < 0 ? o.length + k1 : k1           # abs pos (negative is count from end)\n               k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2  # abs length (negative k2 is length from pos to end count)\n               # if k1 is outside, adjust to first position, and adjust length\n               if k1 < 0\n                 k2 += k1\n                 k1 = 0\n               end\n               o[k1, k2]\n             else\n               fail(Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, { :actual => keys.size })\n             end\n    # Specified as: an index outside of range, or empty result == empty string\n    (result.nil? || result.empty?) ? EMPTY_STRING : result\n  end\n\n  # Parameterizes a PRegexp Type with a pattern string or r ruby egexp\n  #\n  def access_PRegexpType(o, scope, keys)\n    keys.flatten!\n    unless keys.size == 1\n      blamed = keys.size == 0 ? @semantic : @semantic.keys[1]\n      fail(Issues::BAD_TYPE_SLICE_ARITY, blamed, :base_type => o, :min => 1, :actual => keys.size)\n    end\n    assert_keys(keys, o, 1, 1, String, Regexp)\n    Types::TypeFactory.regexp(*keys)\n  end\n\n  # Evaluates <ary>[] with 1 or 2 arguments. One argument is an index lookup, two arguments is a slice from/to.\n  #\n  def access_Array(o, scope, keys)\n    keys.flatten!\n    case keys.size\n    when 0\n      fail(Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, { :actual => keys.size })\n    when 1\n      key = coerce_numeric(keys[0], @semantic.keys[0], scope)\n      unless key.is_a?(Integer)\n        bad_access_key_type(o, 0, key, Integer)\n      end\n      o[key]\n    when 2\n      # A slice [from, to] with support for -1 to mean start, or end respectively.\n      k1 = coerce_numeric(keys[0], @semantic.keys[0], scope)\n      k2 = coerce_numeric(keys[1], @semantic.keys[1], scope)\n\n      [k1, k2].each_with_index { |k, i| bad_access_key_type(o, i, k, Integer) unless k.is_a?(Integer) }\n\n      # Help confused Ruby do the right thing (it truncates to the right, but negative index + length can never overlap\n      # the available range.\n      k1 = k1 < 0 ? o.length + k1 : k1           # abs pos (negative is count from end)\n      k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2  # abs length (negative k2 is length from pos to end count)\n      # if k1 is outside, adjust to first position, and adjust length\n      if k1 < 0\n        k2 += k1\n        k1 = 0\n      end\n      # Help ruby always return empty array when asking for a sub array\n      result = o[k1, k2]\n      result.nil? ? [] : result\n    else\n      fail(Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, { :actual => keys.size })\n    end\n  end\n\n  # Evaluates <hsh>[] with support for one or more arguments. If more than one argument is used, the result\n  # is an array with each lookup.\n  # @note\n  #   Does not flatten its keys to enable looking up with a structure\n  #\n  def access_Hash(o, scope, keys)\n    # Look up key in hash, if key is nil, try alternate form (:undef) before giving up.\n    # This is done because the hash may have been produced by 3x logic and may thus contain :undef.\n    result = keys.collect do |k|\n      o.fetch(k) { |key| key.nil? ? o[:undef] : nil }\n    end\n    case result.size\n    when 0\n      fail(Issues::BAD_HASH_SLICE_ARITY, @semantic.left_expr, { :actual => keys.size })\n    when 1\n      result.pop\n    else\n      # remove nil elements and return\n      result.compact!\n      result\n    end\n  end\n\n  def access_PBooleanType(o, scope, keys)\n    keys.flatten!\n    assert_keys(keys, o, 1, 1, TrueClass, FalseClass)\n    Types::TypeFactory.boolean(keys[0])\n  end\n\n  def access_PEnumType(o, scope, keys)\n    keys.flatten!\n    last = keys.last\n    case_insensitive = false\n    if last == true || last == false\n      keys = keys[0...-1]\n      case_insensitive = last\n    end\n    assert_keys(keys, o, 1, Float::INFINITY, String)\n    Types::PEnumType.new(keys, case_insensitive)\n  end\n\n  def access_PVariantType(o, scope, keys)\n    keys.flatten!\n    assert_keys(keys, o, 1, Float::INFINITY, Types::PAnyType)\n    Types::TypeFactory.variant(*keys)\n  end\n\n  def access_PSemVerType(o, scope, keys)\n    keys.flatten!\n    assert_keys(keys, o, 1, Float::INFINITY, String, SemanticPuppet::VersionRange)\n    Types::TypeFactory.sem_ver(*keys)\n  end\n\n  def access_PTimestampType(o, scope, keys)\n    keys.flatten!\n    fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min => 0, :max => 2, :actual => keys.size) if keys.size > 2\n\n    Types::TypeFactory.timestamp(*keys)\n  end\n\n  def access_PTimespanType(o, scope, keys)\n    keys.flatten!\n    fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min => 0, :max => 2, :actual => keys.size) if keys.size > 2\n\n    Types::TypeFactory.timespan(*keys)\n  end\n\n  def access_PTupleType(o, scope, keys)\n    keys.flatten!\n    if Types::TypeFactory.is_range_parameter?(keys[-2]) && Types::TypeFactory.is_range_parameter?(keys[-1])\n      size_type = Types::TypeFactory.range(keys[-2], keys[-1])\n      keys = keys[0, keys.size - 2]\n    elsif Types::TypeFactory.is_range_parameter?(keys[-1])\n      size_type = Types::TypeFactory.range(keys[-1], :default)\n      keys = keys[0, keys.size - 1]\n    end\n    assert_keys(keys, o, 1, Float::INFINITY, Types::PAnyType)\n    Types::TypeFactory.tuple(keys, size_type)\n  end\n\n  def access_PCallableType(o, scope, keys)\n    if keys.size > 0 && keys[0].is_a?(Array)\n      unless keys.size == 2\n        fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min => 2, :max => 2, :actual => keys.size)\n      end\n\n      unless keys[1].is_a?(Types::PAnyType)\n        bad_type_specialization_key_type(o, 1, k, Types::PAnyType)\n      end\n    end\n    Types::TypeFactory.callable(*keys)\n  end\n\n  def access_PStructType(o, scope, keys)\n    assert_keys(keys, o, 1, 1, Hash)\n    Types::TypeFactory.struct(keys[0])\n  end\n\n  def access_PStringType(o, scope, keys)\n    keys.flatten!\n    case keys.size\n    when 1\n      size_t = collection_size_t(0, keys[0])\n    when 2\n      size_t = collection_size_t(0, keys[0], keys[1])\n    else\n      fail(Issues::BAD_STRING_SLICE_ARITY, @semantic, { :actual => keys.size })\n    end\n    Types::TypeFactory.string(size_t)\n  end\n\n  # Asserts type of each key and calls fail with BAD_TYPE_SPECIFICATION\n  # @param keys [Array<Object>] the evaluated keys\n  # @param o [Object] evaluated LHS reported as :base_type\n  # @param min [Integer] the minimum number of keys (typically 1)\n  # @param max [Numeric] the maximum number of keys (use same as min, specific number, or Float::INFINITY)\n  # @param allowed_classes [Class] a variable number of classes that each key must be an instance of (any)\n  # @api private\n  #\n  def assert_keys(keys, o, min, max, *allowed_classes)\n    size = keys.size\n    unless size.between?(min, max || Float::INFINITY)\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min => 1, :max => max, :actual => keys.size)\n    end\n\n    keys.each_with_index do |k, i|\n      unless allowed_classes.any? { |clazz| k.is_a?(clazz) }\n        bad_type_specialization_key_type(o, i, k, *allowed_classes)\n      end\n    end\n  end\n\n  def bad_access_key_type(lhs, key_index, actual, *expected_classes)\n    fail(Issues::BAD_SLICE_KEY_TYPE, @semantic.keys[key_index], {\n           :left_value => lhs,\n           :actual => bad_key_type_name(actual),\n           :expected_classes => expected_classes\n         })\n  end\n\n  def bad_string_access_key_type(lhs, key_index, actual)\n    fail(Issues::BAD_STRING_SLICE_KEY_TYPE, @semantic.keys[key_index], {\n           :left_value => lhs,\n           :actual_type => bad_key_type_name(actual),\n         })\n  end\n\n  def bad_key_type_name(actual)\n    case actual\n    when nil\n      'Undef'\n    when :default\n      'Default'\n    else\n      Types::TypeCalculator.generalize(Types::TypeCalculator.infer(actual)).to_s\n    end\n  end\n\n  def bad_type_specialization_key_type(type, key_index, actual, *expected_classes)\n    label_provider = Model::ModelLabelProvider.new()\n    expected = expected_classes.map { |c| label_provider.label(c) }.join(' or ')\n    fail(Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[key_index], {\n           :type => type,\n           :message => _(\"Cannot use %{key} where %{expected} is expected\") % { key: bad_key_type_name(actual), expected: expected }\n         })\n  end\n\n  def access_PPatternType(o, scope, keys)\n    keys.flatten!\n    assert_keys(keys, o, 1, Float::INFINITY, String, Regexp, Types::PPatternType, Types::PRegexpType)\n    Types::TypeFactory.pattern(*keys)\n  end\n\n  def access_PURIType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      param = keys[0]\n      unless Types::PURIType::TYPE_URI_PARAM_TYPE.instance?(param)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'URI-Type', :actual => param.class })\n      end\n\n      Types::PURIType.new(param)\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'URI-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_POptionalType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      type = keys[0]\n      unless type.is_a?(Types::PAnyType)\n        if type.is_a?(String)\n          type = Types::TypeFactory.string(type)\n        else\n          fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Optional-Type', :actual => type.class })\n        end\n      end\n      Types::POptionalType.new(type)\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'Optional-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PSensitiveType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      type = keys[0]\n      unless type.is_a?(Types::PAnyType)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Sensitive-Type', :actual => type.class })\n      end\n\n      Types::PSensitiveType.new(type)\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'Sensitive-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PObjectType(o, scope, keys)\n    keys.flatten!\n    if o.resolved? && !o.name.nil?\n      Types::PObjectTypeExtension.create(o, keys)\n    elsif keys.size == 1\n      Types::TypeFactory.object(keys[0])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'Object-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PTypeSetType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      Types::TypeFactory.type_set(keys[0])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'TypeSet-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PNotUndefType(o, scope, keys)\n    keys.flatten!\n    case keys.size\n    when 0\n      Types::TypeFactory.not_undef\n    when 1\n      type = keys[0]\n      case type\n      when String\n        type = Types::TypeFactory.string(type)\n      when Types::PAnyType\n        type = nil if type.instance_of?(Types::PAnyType)\n      else\n        fail(Issues::BAD_NOT_UNDEF_SLICE_TYPE, @semantic.keys[0], { :base_type => 'NotUndef-Type', :actual => type.class })\n      end\n      Types::TypeFactory.not_undef(type)\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'NotUndef-Type', :min => 0, :max => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PTypeType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      unless keys[0].is_a?(Types::PAnyType)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Type-Type', :actual => keys[0].class })\n      end\n\n      Types::PTypeType.new(keys[0])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'Type-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PInitType(o, scope, keys)\n    unless keys[0].is_a?(Types::PAnyType)\n      fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Init-Type', :actual => keys[0].class })\n    end\n\n    Types::TypeFactory.init(*keys)\n  end\n\n  def access_PIterableType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      unless keys[0].is_a?(Types::PAnyType)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Iterable-Type', :actual => keys[0].class })\n      end\n\n      Types::PIterableType.new(keys[0])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'Iterable-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PIteratorType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 1\n      unless keys[0].is_a?(Types::PAnyType)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Iterator-Type', :actual => keys[0].class })\n      end\n\n      Types::PIteratorType.new(keys[0])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, { :base_type => 'Iterator-Type', :min => 1, :actual => keys.size })\n    end\n  end\n\n  def access_PRuntimeType(o, scope, keys)\n    keys.flatten!\n    assert_keys(keys, o, 2, 2, String, String)\n    # create runtime type based on runtime and name of class, (not inference of key's type)\n    Types::TypeFactory.runtime(*keys)\n  end\n\n  def access_PIntegerType(o, scope, keys)\n    keys.flatten!\n    unless keys.size.between?(1, 2)\n      fail(Issues::BAD_INTEGER_SLICE_ARITY, @semantic, { :actual => keys.size })\n    end\n\n    keys.each_with_index do |x, index|\n      fail(Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index],\n           { :actual => x.class }) unless x.is_a?(Integer) || x == :default\n    end\n    Types::PIntegerType.new(*keys)\n  end\n\n  def access_PFloatType(o, scope, keys)\n    keys.flatten!\n    unless keys.size.between?(1, 2)\n      fail(Issues::BAD_FLOAT_SLICE_ARITY, @semantic, { :actual => keys.size })\n    end\n\n    keys.each_with_index do |x, index|\n      fail(Issues::BAD_FLOAT_SLICE_TYPE, @semantic.keys[index],\n           { :actual => x.class }) unless x.is_a?(Float) || x.is_a?(Integer) || x == :default\n    end\n    from, to = keys\n    from = from == :default || from.nil? ? nil : Float(from)\n    to = to == :default || to.nil? ? nil : Float(to)\n    Types::PFloatType.new(from, to)\n  end\n\n  # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type.\n  # With 3 or 4 arguments, these are used to create a size constraint.\n  # It is not possible to create a collection of Hash types directly.\n  #\n  def access_PHashType(o, scope, keys)\n    keys.flatten!\n    if keys.size == 2 && keys[0].is_a?(Integer) && keys[1].is_a?(Integer)\n      return Types::PHashType.new(nil, nil, Types::PIntegerType.new(*keys))\n    end\n\n    keys[0, 2].each_with_index do |k, index|\n      unless k.is_a?(Types::PAnyType)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], { :base_type => 'Hash-Type', :actual => k.class })\n      end\n    end\n    case keys.size\n    when 2\n      size_t = nil\n    when 3\n      size_t = keys[2]\n      size_t = Types::PIntegerType.new(size_t) unless size_t.is_a?(Types::PIntegerType)\n    when 4\n      size_t = collection_size_t(2, keys[2], keys[3])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {\n             :base_type => 'Hash-Type', :min => 2, :max => 4, :actual => keys.size\n           })\n    end\n    Types::PHashType.new(keys[0], keys[1], size_t)\n  end\n\n  # CollectionType is parameterized with a range\n  def access_PCollectionType(o, scope, keys)\n    keys.flatten!\n    case keys.size\n    when 1\n      size_t = collection_size_t(0, keys[0])\n    when 2\n      size_t = collection_size_t(0, keys[0], keys[1])\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic,\n           { :base_type => 'Collection-Type', :min => 1, :max => 2, :actual => keys.size })\n    end\n    Types::PCollectionType.new(size_t)\n  end\n\n  # An Array can create a new Array type. It is not possible to create a collection of Array types.\n  #\n  def access_PArrayType(o, scope, keys)\n    keys.flatten!\n    case keys.size\n    when 1\n      unless keys[0].is_a?(Types::PAnyType)\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Array-Type', :actual => keys[0].class })\n      end\n\n      type = keys[0]\n      size_t = nil\n    when 2\n      if keys[0].is_a?(Types::PAnyType)\n        size_t = collection_size_t(1, keys[1])\n        type = keys[0]\n      else\n        size_t = collection_size_t(0, keys[0], keys[1])\n        type = nil\n      end\n    when 3\n      if keys[0].is_a?(Types::PAnyType)\n        size_t = collection_size_t(1, keys[1], keys[2])\n        type = keys[0]\n      else\n        fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], { :base_type => 'Array-Type', :actual => keys[0].class })\n      end\n    else\n      fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic,\n           { :base_type => 'Array-Type', :min => 1, :max => 3, :actual => keys.size })\n    end\n    Types::PArrayType.new(type, size_t)\n  end\n\n  # Produces an PIntegerType (range) given one or two keys.\n  def collection_size_t(start_index, *keys)\n    if keys.size == 1 && keys[0].is_a?(Types::PIntegerType)\n      keys[0]\n    else\n      keys.each_with_index do |x, index|\n        fail(Issues::BAD_COLLECTION_SLICE_TYPE, @semantic.keys[start_index + index],\n             { :actual => x.class }) unless x.is_a?(Integer) || x == :default\n      end\n      Types::PIntegerType.new(*keys)\n    end\n  end\n\n  # A Puppet::Resource represents either just a type (no title), or is a fully qualified type/title.\n  #\n  def access_Resource(o, scope, keys)\n    # To access a Puppet::Resource as if it was a PResourceType, simply infer it, and take the type of\n    # the parameterized meta type (i.e. Type[Resource[the_resource_type, the_resource_title]])\n    t = Types::TypeCalculator.infer(o).type\n    # must map \"undefined title\" from resource to nil\n    t.title = nil if t.title == EMPTY_STRING\n    access(t, scope, *keys)\n  end\n\n  # If a type reference is encountered here, it's an error\n  def access_PTypeReferenceType(o, scope, keys)\n    fail(Issues::UNKNOWN_RESOURCE_TYPE, @semantic, { :type_name => o.type_string })\n  end\n\n  # A Resource can create a new more specific Resource type, and/or an array of resource types\n  # If the given type has title set, it can not be specified further.\n  # @example\n  #   Resource[File]               # => File\n  #   Resource[File, 'foo']        # => File[foo]\n  #   Resource[File. 'foo', 'bar'] # => [File[foo], File[bar]]\n  #   File['foo', 'bar']           # => [File[foo], File[bar]]\n  #   File['foo']['bar']           # => Value of the 'bar' parameter in the File['foo'] resource\n  #   Resource[File]['foo', 'bar'] # => [File[Foo], File[bar]]\n  #   Resource[File, 'foo', 'bar'] # => [File[foo], File[bar]]\n  #   Resource[File, 'foo']['bar'] # => Value of the 'bar' parameter in the File['foo'] resource\n  #\n  def access_PResourceType(o, scope, keys)\n    blamed = keys.size == 0 ? @semantic : @semantic.keys[0]\n\n    if keys.size == 0\n      fail(Issues::BAD_TYPE_SLICE_ARITY, blamed,\n           :base_type => o.to_s, :min => 1, :max => -1, :actual => 0)\n    end\n\n    # Must know which concrete resource type to operate on in all cases.\n    # It is not allowed to specify the type in an array arg - e.g. Resource[[File, 'foo']]\n    # type_name is LHS type_name if set, else the first given arg\n    type_name = o.type_name || Types::TypeFormatter.singleton.capitalize_segments(keys.shift)\n    type_name = case type_name\n                when Types::PResourceType\n                  type_name.type_name\n                when String\n                  type_name\n                else\n                  # blame given left expression if it defined the type, else the first given key expression\n                  blame = o.type_name.nil? ? @semantic.keys[0] : @semantic.left_expr\n                  fail(Issues::ILLEGAL_RESOURCE_SPECIALIZATION, blame, { :actual => bad_key_type_name(type_name) })\n                end\n\n    # type name must conform\n    if type_name !~ Patterns::CLASSREF_EXT\n      fail(Issues::ILLEGAL_CLASSREF, blamed, { :name => type_name })\n    end\n\n    # The result is an array if multiple titles are given, or if titles are specified with an array\n    # (possibly multiple arrays, and nested arrays).\n    result_type_array = keys.size > 1 || keys[0].is_a?(Array)\n    keys_orig_size = keys.size\n\n    keys.flatten!\n    keys.compact!\n\n    # If given keys  that were just a mix of empty/nil with empty array as a result.\n    # As opposed to calling the function the wrong way (without any arguments), (configurable issue),\n    # Return an empty array\n    #\n    if keys.empty? && keys_orig_size > 0\n      optionally_fail(Issues::EMPTY_RESOURCE_SPECIALIZATION, blamed)\n      return result_type_array ? [] : nil\n    end\n\n    unless o.title.nil?\n      # lookup resource and return one or more parameter values\n      resource = find_resource(scope, o.type_name, o.title)\n      unless resource\n        fail(Issues::UNKNOWN_RESOURCE, @semantic, { :type_name => o.type_name, :title => o.title })\n      end\n\n      result = keys.map do |k|\n        unless is_parameter_of_resource?(scope, resource, k)\n          fail(Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic,\n               { :type_name => o.type_name, :title => o.title, :param_name => k })\n        end\n        get_resource_parameter_value(scope, resource, k)\n      end\n      return result_type_array ? result : result.pop\n    end\n\n    keys = [:no_title] if keys.size < 1 # if there was only a type_name and it was consumed\n    result = keys.each_with_index.map do |t, i|\n      unless t.is_a?(String) || t == :no_title\n        index = keys_orig_size != keys.size ? i + 1 : i\n        fail(Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[index], {\n               :type => o,\n               :message => \"Cannot use #{bad_key_type_name(t)} where a resource title String is expected\"\n             })\n      end\n\n      Types::PResourceType.new(type_name, t == :no_title ? nil : t)\n    end\n    # returns single type if request was for a single entity, else an array of types (possibly empty)\n    result_type_array ? result : result.pop\n  end\n\n  NS = '::'\n\n  def access_PClassType(o, scope, keys)\n    blamed = keys.size == 0 ? @semantic : @semantic.keys[0]\n    keys_orig_size = keys.size\n\n    if keys_orig_size == 0\n      fail(Issues::BAD_TYPE_SLICE_ARITY, blamed,\n           :base_type => o.to_s, :min => 1, :max => -1, :actual => 0)\n    end\n\n    # The result is an array if multiple classnames are given, or if classnames are specified with an array\n    # (possibly multiple arrays, and nested arrays).\n    result_type_array = keys.size > 1 || keys[0].is_a?(Array)\n\n    keys.flatten!\n    keys.compact!\n\n    # If given keys  that were just a mix of empty/nil with empty array as a result.\n    # As opposed to calling the function the wrong way (without any arguments), (configurable issue),\n    # Return an empty array\n    #\n    if keys.empty? && keys_orig_size > 0\n      optionally_fail(Issues::EMPTY_RESOURCE_SPECIALIZATION, blamed)\n      return result_type_array ? [] : nil\n    end\n\n    if o.class_name.nil?\n      result = keys.each_with_index.map do |c, i|\n        fail(Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys[i], { :name => c }) unless c.is_a?(String)\n\n        name = c.downcase\n        # Remove leading '::' since all references are global, and 3x runtime does the wrong thing\n        name = name[2..] if name[0, 2] == NS\n\n        fail(Issues::ILLEGAL_NAME, @semantic.keys[i], { :name => c }) unless name =~ Patterns::NAME\n\n        Types::PClassType.new(name)\n      end\n    else\n      # lookup class resource and return one or more parameter values\n      resource = find_resource(scope, 'class', o.class_name)\n      if resource\n        result = keys.map do |k|\n          if is_parameter_of_resource?(scope, resource, k)\n            get_resource_parameter_value(scope, resource, k)\n          else\n            fail(Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic,\n                 { :type_name => 'Class', :title => o.class_name, :param_name => k })\n          end\n        end\n      else\n        fail(Issues::UNKNOWN_RESOURCE, @semantic, { :type_name => 'Class', :title => o.class_name })\n      end\n    end\n\n    # returns single type as type, else an array of types\n    result_type_array ? result : result.pop\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/callable_signature.rb",
    "content": "# frozen_string_literal: true\n\n# CallableSignature\n# ===\n# A CallableSignature describes how something callable expects to be called.\n# Different implementation of this class are used for different types of callables.\n#\n# @api public\n#\nclass Puppet::Pops::Evaluator::CallableSignature\n  # Returns the names of the parameters as an array of strings. This does not include the name\n  # of an optional block parameter.\n  #\n  # All implementations are not required to supply names for parameters. They may be used if present,\n  # to provide user feedback in errors etc. but they are not authoritative about the number of\n  # required arguments, optional arguments, etc.\n  #\n  # A derived class must implement this method.\n  #\n  # @return [Array<String>] - an array of names (that may be empty if names are unavailable)\n  #\n  # @api public\n  #\n  def parameter_names\n    raise NotImplementedError\n  end\n\n  # Returns a PCallableType with the type information, required and optional count, and type information about\n  # an optional block.\n  #\n  # A derived class must implement this method.\n  #\n  # @return [Puppet::Pops::Types::PCallableType]\n  # @api public\n  #\n  def type\n    raise NotImplementedError\n  end\n\n  # Returns the expected type for an optional block. The type may be nil, which means that the callable does\n  # not accept a block. If a type is returned it is one of Callable, Optional[Callable], Variant[Callable,...],\n  # or Optional[Variant[Callable, ...]]. The Variant type is used when multiple signatures are acceptable.\n  # The Optional type is used when the block is optional.\n  #\n  # @return [Puppet::Pops::Types::PAnyType, nil] the expected type of a block given as the last parameter in a call.\n  #\n  # @api public\n  #\n  def block_type\n    type.block_type\n  end\n\n  # Returns the name of the block parameter if the callable accepts a block.\n  # @return [String] the name of the block parameter\n  # A derived class must implement this method.\n  # @api public\n  #\n  def block_name\n    raise NotImplementedError\n  end\n\n  # Returns a range indicating the optionality of a block. One of [0,0] (does not accept block), [0,1] (optional\n  # block), and [1,1] (block required)\n  #\n  # @return [Array(Integer, Integer)] the range of the block parameter\n  #\n  def block_range\n    type.block_range\n  end\n\n  # Returns the range of required/optional argument values as an array of [min, max], where an infinite\n  # end is given as Float::INFINITY. To test against infinity, use the infinity? method.\n  #\n  # @return [Array[Integer, Numeric]] - an Array with [min, max]\n  #\n  # @api public\n  #\n  def args_range\n    type.size_range\n  end\n\n  # Returns true if the last parameter captures the rest of the arguments, with a possible cap, as indicated\n  # by the `args_range` method.\n  # A derived class must implement this method.\n  #\n  # @return [Boolean] true if last parameter captures the rest of the given arguments (up to a possible cap)\n  # @api public\n  #\n  def last_captures_rest?\n    raise NotImplementedError\n  end\n\n  # Returns true if the given x is infinity\n  # @return [Boolean] true, if given value represents infinity\n  #\n  # @api public\n  #\n  def infinity?(x)\n    x == Float::INFINITY\n  end\n\n  # @return [Boolean] true if this signature represents an argument mismatch, false otherwise\n  #\n  # @api private\n  def argument_mismatch_handler?\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/closure.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n  class Jumper < Exception # rubocop:disable Lint/InheritException\n    attr_reader :value\n    attr_reader :file\n    attr_reader :line\n\n    def initialize(value, file, line)\n      @value = value\n      @file = file\n      @line = line\n    end\n  end\n\n  class Next < Jumper; end\n\n  class Return < Jumper; end\n\n  class PuppetStopIteration < StopIteration\n    attr_reader :file\n    attr_reader :line\n    attr_reader :pos\n\n    def initialize(file, line, pos = nil)\n      @file = file\n      @line = line\n      @pos = pos\n    end\n\n    def message\n      \"break() from context where this is illegal\"\n    end\n  end\n\n  # A Closure represents logic bound to a particular scope.\n  # As long as the runtime (basically the scope implementation) has the behavior of Puppet 3x it is not\n  # safe to return and later use this closure.\n  #\n  # The 3x scope is essentially a named scope with an additional internal local/ephemeral nested scope state.\n  # In 3x there is no way to directly refer to the nested scopes, instead, the named scope must be in a particular\n  # state. Specifically, closures that require a local/ephemeral scope to exist at a later point will fail.\n  # It is safe to call a closure (even with 3x scope) from the very same place it was defined, but not\n  # returning it and expecting the closure to reference the scope's state at the point it was created.\n  #\n  # Note that this class is a CallableSignature, and the methods defined there should be used\n  # as the API for obtaining information in a callable-implementation agnostic way.\n  #\n  class Closure < CallableSignature\n    attr_reader :evaluator\n    attr_reader :model\n    attr_reader :enclosing_scope\n\n    def initialize(evaluator, model)\n      @evaluator = evaluator\n      @model = model\n    end\n\n    # Evaluates a closure in its enclosing scope after having matched given arguments with parameters (from left to right)\n    # @api public\n    def call(*args)\n      call_with_scope(enclosing_scope, args)\n    end\n\n    # This method makes a Closure compatible with a Dispatch. This is used when the closure is wrapped in a Function\n    # and the function is called. (Saves an extra Dispatch that just delegates to a Closure and avoids having two\n    # checks of the argument type/arity validity).\n    # @api private\n    def invoke(instance, calling_scope, args, &block)\n      enclosing_scope.with_global_scope do |global_scope|\n        call_with_scope(global_scope, args, &block)\n      end\n    end\n\n    def call_by_name_with_scope(scope, args_hash, enforce_parameters)\n      call_by_name_internal(scope, args_hash, enforce_parameters)\n    end\n\n    def call_by_name(args_hash, enforce_parameters)\n      call_by_name_internal(enclosing_scope, args_hash, enforce_parameters)\n    end\n\n    # Call closure with argument assignment by name\n    def call_by_name_internal(closure_scope, args_hash, enforce_parameters)\n      if enforce_parameters\n        # Push a temporary parameter scope used while resolving the parameter defaults\n        closure_scope.with_parameter_scope(closure_name, parameter_names) do |param_scope|\n          # Assign all non-nil values, even those that represent non-existent parameters.\n          args_hash.each { |k, v| param_scope[k] = v unless v.nil? }\n          parameters.each do |p|\n            name = p.name\n            arg = args_hash[name]\n            if arg.nil?\n              # Arg either wasn't given, or it was undef\n              if p.value.nil?\n                # No default. Assign nil if the args_hash included it\n                param_scope[name] = nil if args_hash.include?(name)\n              else\n                param_scope[name] = param_scope.evaluate(name, p.value, closure_scope, @evaluator)\n              end\n            end\n          end\n          args_hash = param_scope.to_hash\n        end\n        Types::TypeMismatchDescriber.validate_parameters(closure_name, params_struct, args_hash)\n        result = catch(:next) do\n          @evaluator.evaluate_block_with_bindings(closure_scope, args_hash, @model.body)\n        end\n        Types::TypeAsserter.assert_instance_of(nil, return_type, result) do\n          \"value returned from #{closure_name}\"\n        end\n      else\n        @evaluator.evaluate_block_with_bindings(closure_scope, args_hash, @model.body)\n      end\n    end\n    private :call_by_name_internal\n\n    def parameters\n      @model.parameters\n    end\n\n    # Returns the number of parameters (required and optional)\n    # @return [Integer] the total number of accepted parameters\n    def parameter_count\n      # yes, this is duplication of code, but it saves a method call\n      @model.parameters.size\n    end\n\n    # @api public\n    def parameter_names\n      @model.parameters.collect(&:name)\n    end\n\n    def return_type\n      @return_type ||= create_return_type\n    end\n\n    # @api public\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    def type\n      @callable ||= create_callable_type\n    end\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n\n    # @api public\n    def params_struct\n      @params_struct ||= create_params_struct\n    end\n\n    # @api public\n    def last_captures_rest?\n      last = @model.parameters[-1]\n      last && last.captures_rest\n    end\n\n    # @api public\n    def block_name\n      # TODO: Lambda's does not support blocks yet. This is a placeholder\n      'unsupported_block'\n    end\n\n    CLOSURE_NAME = 'lambda'\n\n    # @api public\n    def closure_name\n      CLOSURE_NAME\n    end\n\n    class Dynamic < Closure\n      def initialize(evaluator, model, scope)\n        @enclosing_scope = scope\n        super(evaluator, model)\n      end\n\n      def enclosing_scope\n        @enclosing_scope\n      end\n\n      def call(*args)\n        # A return from an unnamed closure is treated as a return from the context evaluating\n        # calling this closure - that is, as if it was the return call itself.\n        #\n        jumper = catch(:return) do\n          return call_with_scope(enclosing_scope, args)\n        end\n        raise jumper\n      end\n    end\n\n    class Named < Closure\n      def initialize(name, evaluator, model)\n        @name = name\n        super(evaluator, model)\n      end\n\n      def closure_name\n        @name\n      end\n\n      # The assigned enclosing scope, or global scope if enclosing scope was initialized to nil\n      #\n      def enclosing_scope\n        # Named closures are typically used for puppet functions and they cannot be defined\n        # in an enclosing scope as they are cashed and reused. They need to bind to the\n        # global scope at time of use rather at time of definition.\n        # Unnamed closures are always a runtime construct, they are never bound by a loader\n        # and are thus garbage collected at end of a compilation.\n        #\n        Puppet.lookup(:global_scope) { {} }\n      end\n    end\n\n    private\n\n    def call_with_scope(scope, args)\n      variable_bindings = combine_values_with_parameters(scope, args)\n\n      final_args = parameters.reduce([]) do |tmp_args, param|\n        if param.captures_rest\n          tmp_args.concat(variable_bindings[param.name])\n        else\n          tmp_args << variable_bindings[param.name]\n        end\n      end\n\n      if type.callable_with?(final_args, block_type)\n        result = catch(:next) do\n          @evaluator.evaluate_block_with_bindings(scope, variable_bindings, @model.body)\n        end\n        Types::TypeAsserter.assert_instance_of(nil, return_type, result) do\n          \"value returned from #{closure_name}\"\n        end\n      else\n        tc = Types::TypeCalculator.singleton\n        args_type = tc.infer_set(final_args)\n        raise ArgumentError, Types::TypeMismatchDescriber.describe_signatures(closure_name, [self], args_type)\n      end\n    end\n\n    def combine_values_with_parameters(scope, args)\n      scope.with_parameter_scope(closure_name, parameter_names) do |param_scope|\n        parameters.each_with_index do |parameter, index|\n          param_captures     = parameter.captures_rest\n          default_expression = parameter.value\n\n          if index >= args.size\n            if default_expression\n              # not given, has default\n              value = param_scope.evaluate(parameter.name, default_expression, scope, @evaluator)\n\n              if param_captures && !value.is_a?(Array)\n                # correct non array default value\n                value = [value]\n              end\n            elsif param_captures\n              # not given, does not have default\n              value = []\n            # default for captures rest is an empty array\n            else\n              @evaluator.fail(Issues::MISSING_REQUIRED_PARAMETER, parameter, { :param_name => parameter.name })\n            end\n          else\n            given_argument = args[index]\n            if param_captures\n              # get excess arguments\n              value = args[(parameter_count - 1)..]\n              # If the input was a single nil, or undef, and there is a default, use the default\n              # This supports :undef in case it was used in a 3x data structure and it is passed as an arg\n              #\n              if value.size == 1 && (given_argument.nil? || given_argument == :undef) && default_expression\n                value = param_scope.evaluate(parameter.name, default_expression, scope, @evaluator)\n                # and ensure it is an array\n                value = [value] unless value.is_a?(Array)\n              end\n            else\n              value = given_argument\n            end\n          end\n          param_scope[parameter.name] = value\n        end\n        param_scope.to_hash\n      end\n    end\n\n    def create_callable_type\n      types = []\n      from = 0\n      to = 0\n      in_optional_parameters = false\n      closure_scope = enclosing_scope\n\n      parameters.each do |param|\n        type, param_range = create_param_type(param, closure_scope)\n\n        types << type\n\n        if param_range[0] == 0\n          in_optional_parameters = true\n        elsif param_range[0] != 0 && in_optional_parameters\n          @evaluator.fail(Issues::REQUIRED_PARAMETER_AFTER_OPTIONAL, param, { :param_name => param.name })\n        end\n\n        from += param_range[0]\n        to += param_range[1]\n      end\n      param_types = Types::PTupleType.new(types, Types::PIntegerType.new(from, to))\n      # The block_type for a Closure is always nil for now, see comment in block_name above\n      Types::PCallableType.new(param_types, nil, return_type)\n    end\n\n    def create_params_struct\n      type_factory = Types::TypeFactory\n      members = {}\n      closure_scope = enclosing_scope\n\n      parameters.each do |param|\n        arg_type, _ = create_param_type(param, closure_scope)\n        key_type = type_factory.string(param.name.to_s)\n        key_type = type_factory.optional(key_type) unless param.value.nil?\n        members[key_type] = arg_type\n      end\n      type_factory.struct(members)\n    end\n\n    def create_return_type\n      if @model.return_type\n        @evaluator.evaluate(@model.return_type, @enclosing_scope)\n      else\n        Types::PAnyType::DEFAULT\n      end\n    end\n\n    def create_param_type(param, closure_scope)\n      type = if param.type_expr\n               @evaluator.evaluate(param.type_expr, closure_scope)\n             else\n               Types::PAnyType::DEFAULT\n             end\n\n      if param.captures_rest && type.is_a?(Types::PArrayType)\n        # An array on a slurp parameter is how a size range is defined for a\n        # slurp (Array[Integer, 1, 3] *$param). However, the callable that is\n        # created can't have the array in that position or else type checking\n        # will require the parameters to be arrays, which isn't what is\n        # intended. The array type contains the intended information and needs\n        # to be unpacked.\n        param_range = type.size_range\n        type = type.element_type\n      elsif param.captures_rest && !type.is_a?(Types::PArrayType)\n        param_range = ANY_NUMBER_RANGE\n      elsif param.value\n        param_range = OPTIONAL_SINGLE_RANGE\n      else\n        param_range = REQUIRED_SINGLE_RANGE\n      end\n      [type, param_range]\n    end\n\n    # Produces information about parameters compatible with a 4x Function (which can have multiple signatures)\n    def signatures\n      [self]\n    end\n\n    ANY_NUMBER_RANGE = [0, Float::INFINITY]\n    OPTIONAL_SINGLE_RANGE = [0, 1]\n    REQUIRED_SINGLE_RANGE = [1, 1]\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/collector_transformer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\nclass CollectorTransformer\n  def initialize\n    @@query_visitor    ||= Visitor.new(nil, \"query\", 1, 1)\n    @@match_visitor    ||= Visitor.new(nil, \"match\", 1, 1)\n    @@evaluator        ||= EvaluatorImpl.new\n    @@compare_operator ||= CompareOperator.new()\n  end\n\n  def transform(o, scope)\n    # TRANSLATORS 'CollectExpression' is a class name and should not be translated\n    raise ArgumentError, _(\"Expected CollectExpression\") unless o.is_a? Model::CollectExpression\n\n    raise \"LHS is not a type\" unless o.type_expr.is_a? Model::QualifiedReference\n\n    type = o.type_expr.value().downcase()\n\n    if type == 'class'\n      fail \"Classes cannot be collected\"\n    end\n\n    resource_type = Runtime3ResourceSupport.find_resource_type(scope, type)\n\n    fail \"Resource type #{type} doesn't exist\" unless resource_type\n\n    unless o.operations.empty?\n      overrides = {\n        :parameters => o.operations.map { |x| @@evaluator.evaluate(x, scope) }.flatten,\n        :file => o.file,\n        :line => o.line,\n        :source => scope.source,\n        :scope => scope\n      }\n    end\n\n    code = query_unless_nop(o.query, scope)\n\n    case o.query\n    when Model::VirtualQuery\n      newcoll = Collectors::CatalogCollector.new(scope, resource_type, code, overrides)\n    when Model::ExportedQuery\n      match = match_unless_nop(o.query, scope)\n      newcoll = Collectors::ExportedCollector.new(scope, resource_type, match, code, overrides)\n    end\n\n    scope.compiler.add_collection(newcoll)\n\n    newcoll\n  end\n\n  protected\n\n  def query(o, scope)\n    @@query_visitor.visit_this_1(self, o, scope)\n  end\n\n  def match(o, scope)\n    @@match_visitor.visit_this_1(self, o, scope)\n  end\n\n  def query_unless_nop(query, scope)\n    unless query.expr.nil? || query.expr.is_a?(Model::Nop)\n      query(query.expr, scope)\n    end\n  end\n\n  def match_unless_nop(query, scope)\n    unless query.expr.nil? || query.expr.is_a?(Model::Nop)\n      match(query.expr, scope)\n    end\n  end\n\n  def query_AndExpression(o, scope)\n    left_code = query(o.left_expr, scope)\n    right_code = query(o.right_expr, scope)\n    proc do |resource|\n      left_code.call(resource) && right_code.call(resource)\n    end\n  end\n\n  def query_OrExpression(o, scope)\n    left_code = query(o.left_expr, scope)\n    right_code = query(o.right_expr, scope)\n    proc do |resource|\n      left_code.call(resource) || right_code.call(resource)\n    end\n  end\n\n  def query_ComparisonExpression(o, scope)\n    left_code = query(o.left_expr, scope)\n    right_code = query(o.right_expr, scope)\n\n    case o.operator\n    when '=='\n      if left_code == \"tag\"\n        # Ensure that to_s and downcase is done once, i.e. outside the proc block and\n        # then use raw_tagged? instead of tagged?\n        if right_code.is_a?(Array)\n          tags = right_code\n        else\n          tags = [right_code]\n        end\n        tags = tags.collect do |t|\n          raise ArgumentError, _('Cannot transform a number to a tag') if t.is_a?(Numeric)\n\n          t.to_s.downcase\n        end\n        proc do |resource|\n          resource.raw_tagged?(tags)\n        end\n      else\n        proc do |resource|\n          if (tmp = resource[left_code]).is_a?(Array)\n            @@compare_operator.include?(tmp, right_code, scope)\n          else\n            @@compare_operator.equals(tmp, right_code)\n          end\n        end\n      end\n    when '!='\n      proc do |resource|\n        !@@compare_operator.equals(resource[left_code], right_code)\n      end\n    end\n  end\n\n  def query_AccessExpression(o, scope)\n    pops_object = @@evaluator.evaluate(o, scope)\n\n    # Convert to Puppet 3 style objects since that is how they are represented\n    # in the catalog.\n    @@evaluator.convert(pops_object, scope, nil)\n  end\n\n  def query_VariableExpression(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def query_LiteralBoolean(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def query_LiteralString(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def query_ConcatenatedString(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def query_LiteralNumber(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def query_LiteralUndef(o, scope)\n    nil\n  end\n\n  def query_QualifiedName(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def query_ParenthesizedExpression(o, scope)\n    query(o.expr, scope)\n  end\n\n  def query_Object(o, scope)\n    raise ArgumentError, _(\"Cannot transform object of class %{klass}\") % { klass: o.class }\n  end\n\n  def match_AccessExpression(o, scope)\n    pops_object = @@evaluator.evaluate(o, scope)\n\n    # Convert to Puppet 3 style objects since that is how they are represented\n    # in the catalog.\n    @@evaluator.convert(pops_object, scope, nil)\n  end\n\n  def match_AndExpression(o, scope)\n    left_match = match(o.left_expr, scope)\n    right_match = match(o.right_expr, scope)\n    [left_match, 'and', right_match]\n  end\n\n  def match_OrExpression(o, scope)\n    left_match = match(o.left_expr, scope)\n    right_match = match(o.right_expr, scope)\n    [left_match, 'or', right_match]\n  end\n\n  def match_ComparisonExpression(o, scope)\n    left_match = match(o.left_expr, scope)\n    right_match = match(o.right_expr, scope)\n    [left_match, o.operator.to_s, right_match]\n  end\n\n  def match_VariableExpression(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def match_LiteralBoolean(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def match_LiteralString(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def match_LiteralUndef(o, scope)\n    nil\n  end\n\n  def match_ConcatenatedString(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def match_LiteralNumber(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def match_QualifiedName(o, scope)\n    @@evaluator.evaluate(o, scope)\n  end\n\n  def match_ParenthesizedExpression(o, scope)\n    match(o.expr, scope)\n  end\n\n  def match_Object(o, scope)\n    raise ArgumentError, _(\"Cannot transform object of class %{klass}\") % { klass: o.class }\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/collectors/abstract_collector.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Pops::Evaluator::Collectors::AbstractCollector\n  attr_reader :scope\n\n  # The collector's hash of overrides {:parameters => params}\n  attr_reader :overrides\n\n  # The set of collected resources\n  attr_reader :collected\n\n  # An empty array which will be returned by the unresolved_resources\n  # method unless we have a FixSetCollector\n  EMPTY_RESOURCES = [].freeze\n\n  # Initialized the instance variables needed by the base\n  # collector class to perform evaluation\n  #\n  # @param [Puppet::Parser::Scope] scope\n  #\n  # @param [Hash] overrides a hash of optional overrides\n  # @options opts [Array] :parameters\n  # @options opts [String] :file\n  # @options opts [Array] :line\n  # @options opts [Puppet::Resource::Type] :source\n  # @options opts [Puppet::Parser::Scope] :scope\n  def initialize(scope, overrides = nil)\n    @collected = {}\n    @scope = scope\n\n    unless overrides.nil? || overrides[:parameters]\n      raise ArgumentError, _(\"Exported resource try to override without parameters\")\n    end\n\n    @overrides = overrides\n  end\n\n  # Collects resources and marks collected objects as non-virtual. Also\n  # handles overrides.\n  #\n  # @return [Array] the resources we have collected\n  def evaluate\n    objects = collect.each do |obj|\n      obj.virtual = false\n    end\n\n    return false if objects.empty?\n\n    if @overrides and !objects.empty?\n      overrides[:source].override = true\n\n      objects.each do |res|\n        next if @collected.include?(res.ref)\n\n        t = res.type\n        t = Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type(scope, t)\n        newres = Puppet::Parser::Resource.new(t, res.title, @overrides)\n        scope.compiler.add_override(newres)\n      end\n    end\n\n    objects.reject! { |o| @collected.include?(o.ref) }\n\n    return false if objects.empty?\n\n    objects.each_with_object(@collected) { |o, c| c[o.ref] = o; }\n\n    objects\n  end\n\n  # This should only return an empty array unless we have\n  # an FixedSetCollector, in which case it will return the\n  # resources that have not yet been realized\n  #\n  # @return [Array] the resources that have not been resolved\n  def unresolved_resources\n    EMPTY_RESOURCES\n  end\n\n  # Collect the specified resources. The way this is done depends on which type\n  # of collector we are dealing with. This method is implemented differently in\n  # each of the three child classes\n  #\n  # @return [Array] the collected resources\n  def collect\n    raise NotImplementedError, \"This method must be implemented by the child class\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/collectors/catalog_collector.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Pops::Evaluator::Collectors::CatalogCollector < Puppet::Pops::Evaluator::Collectors::AbstractCollector\n  # Creates a CatalogCollector using the AbstractCollector's\n  # constructor to set the scope and overrides\n  #\n  # param [Puppet::CompilableResourceType] type the resource type to be collected\n  # param [Proc] query the query which defines which resources to match\n  def initialize(scope, type, query, overrides = nil)\n    super(scope, overrides)\n\n    @query = query\n\n    @type = Puppet::Resource.new(type, 'whatever').type\n  end\n\n  # Collects virtual resources based off a collection in a manifest\n  def collect\n    t = @type\n    q = @query\n\n    scope.compiler.resources.find_all do |resource|\n      resource.type == t && (q ? q.call(resource) : true)\n    end\n  end\n\n  def to_s\n    \"Catalog-Collector[#{@type}]\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/collectors/exported_collector.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Pops::Evaluator::Collectors::ExportedCollector < Puppet::Pops::Evaluator::Collectors::AbstractCollector\n  # Creates an ExportedCollector using the AbstractCollector's\n  # constructor to set the scope and overrides\n  #\n  # param [Puppet::CompilableResourceType] type the resource type to be collected\n  # param [Array] equery an array representation of the query (exported query)\n  # param [Proc] cquery a proc representation of the query (catalog query)\n  def initialize(scope, type, equery, cquery, overrides = nil)\n    super(scope, overrides)\n\n    @equery = equery\n    @cquery = cquery\n\n    @type = Puppet::Resource.new(type, 'whatever').type\n  end\n\n  # Ensures that storeconfigs is present before calling AbstractCollector's\n  # evaluate method\n  def evaluate\n    if Puppet[:storeconfigs] != true\n      return false\n    end\n\n    super\n  end\n\n  # Collect exported resources as defined by an exported\n  # collection. Used with PuppetDB\n  def collect\n    resources = []\n\n    time = Puppet::Util.thinmark do\n      t = @type\n      q = @cquery\n\n      resources = scope.compiler.resources.find_all do |resource|\n        resource.type == t && resource.exported? && (q.nil? || q.call(resource))\n      end\n\n      found = Puppet::Resource.indirection\n                              .search(@type, :host => @scope.compiler.node.name, :filter => @equery, :scope => @scope)\n\n      found_resources = found.map { |x| x.is_a?(Puppet::Parser::Resource) ? x : x.to_resource(@scope) }\n\n      found_resources.each do |item|\n        existing = @scope.findresource(item.resource_type, item.title)\n        if existing\n          unless existing.collector_id == item.collector_id\n            raise Puppet::ParseError,\n                  _(\"A duplicate resource was found while collecting exported resources, with the type and title %{title}\") % { title: item.ref }\n          end\n        else\n          item.exported = false\n          @scope.compiler.add_resource(@scope, item)\n          resources << item\n        end\n      end\n    end\n\n    scope.debug(\"Collected %s %s resource%s in %.2f seconds\" %\n                [resources.length, @type, resources.length == 1 ? \"\" : \"s\", time])\n\n    resources\n  end\n\n  def to_s\n    \"Exported-Collector[#{@type}]\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/collectors/fixed_set_collector.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Pops::Evaluator::Collectors::FixedSetCollector < Puppet::Pops::Evaluator::Collectors::AbstractCollector\n  # Creates a FixedSetCollector using the AbstractCollector constructor\n  # to set the scope. It is not possible for a collection to have\n  # overrides in this case, since we have a fixed set of resources that\n  # can be different types.\n  #\n  # @param [Array] resources the fixed set of resources we want to realize\n  def initialize(scope, resources)\n    super(scope)\n    @resources = resources.is_a?(Array) ? resources.dup : [resources]\n  end\n\n  # Collects a fixed set of resources and realizes them. Used\n  # by the realize function\n  def collect\n    resolved = []\n    result = @resources.each_with_object([]) do |ref, memo|\n      res = @scope.findresource(ref.to_s)\n      next unless res\n\n      res.virtual = false\n      memo << res\n      resolved << ref\n    end\n\n    @resources -= resolved\n\n    @scope.compiler.delete_collection(self) if @resources.empty?\n\n    result\n  end\n\n  def unresolved_resources\n    @resources\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/compare_operator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n# Compares the puppet DSL way\n#\n# ==Equality\n# All string vs. numeric equalities check for numeric equality first, then string equality\n# Arrays are equal to arrays if they have the same length, and each element #equals\n# Hashes  are equal to hashes if they have the same size and keys and values #equals.\n# All other objects are equal if they are ruby #== equal\n#\nclass CompareOperator\n  include Utils\n\n  # Provides access to the Puppet 3.x runtime (scope, etc.)\n  # This separation has been made to make it easier to later migrate the evaluator to an improved runtime.\n  #\n  include Runtime3Support\n\n  def initialize\n    @@equals_visitor  ||= Visitor.new(self, \"equals\", 1, 1)\n    @@compare_visitor ||= Visitor.new(self, \"cmp\", 1, 1)\n    @@match_visitor ||= Visitor.new(self, \"match\", 2, 2)\n    @@include_visitor ||= Visitor.new(self, \"include\", 2, 2)\n  end\n\n  def equals(a, b)\n    @@equals_visitor.visit_this_1(self, a, b)\n  end\n\n  # Performs a comparison of a and b, and return > 0 if a is bigger, 0 if equal, and < 0 if b is bigger.\n  # Comparison of String vs. Numeric always compares using numeric.\n  def compare(a, b)\n    @@compare_visitor.visit_this_1(self, a, b)\n  end\n\n  # Performs a match of a and b, and returns true if b matches a\n  def match(a, b, scope = nil)\n    @@match_visitor.visit_this_2(self, b, a, scope)\n  end\n\n  # Answers is b included in a\n  def include?(a, b, scope)\n    @@include_visitor.visit_this_2(self, a, b, scope)\n  end\n\n  protected\n\n  def cmp_String(a, b)\n    return a.casecmp(b) if b.is_a?(String)\n\n    raise ArgumentError, _(\"A String is not comparable to a non String\")\n  end\n\n  # Equality is case independent.\n  def equals_String(a, b)\n    return false unless b.is_a?(String)\n\n    a.casecmp(b) == 0\n  end\n\n  def cmp_Numeric(a, b)\n    case b\n    when Numeric\n      a <=> b\n    when Time::Timespan, Time::Timestamp\n      -(b <=> a) # compare other way and invert result\n    else\n      raise ArgumentError, _(\"A Numeric is not comparable to non Numeric\")\n    end\n  end\n\n  def equals_Numeric(a, b)\n    if b.is_a?(Numeric)\n      a == b\n    else\n      false\n    end\n  end\n\n  def equals_Array(a, b)\n    return false unless b.is_a?(Array) && a.size == b.size\n\n    a.each_index { |i| return false unless equals(a.slice(i), b.slice(i)) }\n    true\n  end\n\n  def equals_Hash(a, b)\n    return false unless b.is_a?(Hash) && a.size == b.size\n\n    a.each { |ak, av| return false unless equals(b[ak], av) }\n    true\n  end\n\n  def cmp_Symbol(a, b)\n    if b.is_a?(Symbol)\n      a <=> b\n    else\n      raise ArgumentError, _(\"Symbol not comparable to non Symbol\")\n    end\n  end\n\n  def cmp_Timespan(a, b)\n    raise ArgumentError, _('Timespans are only comparable to Timespans, Integers, and Floats') unless b.is_a?(Time::Timespan) || b.is_a?(Integer) || b.is_a?(Float)\n\n    a <=> b\n  end\n\n  def cmp_Timestamp(a, b)\n    raise ArgumentError, _('Timestamps are only comparable to Timestamps, Integers, and Floats') unless b.is_a?(Time::Timestamp) || b.is_a?(Integer) || b.is_a?(Float)\n\n    a <=> b\n  end\n\n  def cmp_Version(a, b)\n    raise ArgumentError, _('Versions not comparable to non Versions') unless b.is_a?(SemanticPuppet::Version)\n\n    a <=> b\n  end\n\n  def cmp_Object(a, b)\n    raise ArgumentError, _('Only Strings, Numbers, Timespans, Timestamps, and Versions are comparable')\n  end\n\n  def equals_Object(a, b)\n    a == b\n  end\n\n  def equals_NilClass(a, b)\n    # :undef supported in case it is passed from a 3x data structure\n    b.nil? || b == :undef\n  end\n\n  def equals_Symbol(a, b)\n    # :undef supported in case it is passed from a 3x data structure\n    a == b || a == :undef && b.nil?\n  end\n\n  def include_Object(a, b, scope)\n    false\n  end\n\n  def include_String(a, b, scope)\n    case b\n    when String\n      # substring search downcased\n      a.downcase.include?(b.downcase)\n    when Regexp\n      matched = a.match(b)           # nil, or MatchData\n      set_match_data(matched, scope) # creates ephemeral\n      !!matched                      # match (convert to boolean)\n    when Numeric\n      # convert string to number, true if ==\n      equals(a, b)\n    else\n      false\n    end\n  end\n\n  def include_Binary(a, b, scope)\n    case b\n    when Puppet::Pops::Types::PBinaryType::Binary\n      a.binary_buffer.include?(b.binary_buffer)\n    when String\n      a.binary_buffer.include?(b)\n    when Numeric\n      a.binary_buffer.bytes.include?(b)\n    else\n      false\n    end\n  end\n\n  def include_Array(a, b, scope)\n    case b\n    when Regexp\n      matched = nil\n      a.each do |element|\n        next unless element.is_a? String\n\n        matched = element.match(b) # nil, or MatchData\n        break if matched\n      end\n      # Always set match data, a \"not found\" should not keep old match data visible\n      set_match_data(matched, scope) # creates ephemeral\n      !!matched\n    when String, SemanticPuppet::Version\n      a.any? { |element| match(b, element, scope) }\n    when Types::PAnyType\n      a.each { |element| return true if b.instance?(element) }\n      false\n    else\n      a.each { |element| return true if equals(element, b) }\n      false\n    end\n  end\n\n  def include_Hash(a, b, scope)\n    include?(a.keys, b, scope)\n  end\n\n  def include_VersionRange(a, b, scope)\n    Types::PSemVerRangeType.include?(a, b)\n  end\n\n  # Matches in general by using == operator\n  def match_Object(pattern, a, scope)\n    equals(a, pattern)\n  end\n\n  # Matches only against strings\n  def match_Regexp(regexp, left, scope)\n    return false unless left.is_a? String\n\n    matched = regexp.match(left)\n    set_match_data(matched, scope) unless scope.nil? # creates or clears ephemeral\n    !!matched # convert to boolean\n  end\n\n  # Matches against semvers and strings\n  def match_Version(version, left, scope)\n    case left\n    when SemanticPuppet::Version\n      version == left\n    when String\n      begin\n        version == SemanticPuppet::Version.parse(left)\n      rescue ArgumentError\n        false\n      end\n    else\n      false\n    end\n  end\n\n  # Matches against semvers and strings\n  def match_VersionRange(range, left, scope)\n    Types::PSemVerRangeType.include?(range, left)\n  end\n\n  def match_PAnyType(any_type, left, scope)\n    # right is a type and left is not - check if left is an instance of the given type\n    # (The reverse is not terribly meaningful - computing which of the case options that first produces\n    # an instance of a given type).\n    #\n    any_type.instance?(left)\n  end\n\n  def match_Array(array, left, scope)\n    return false unless left.is_a?(Array)\n    return false unless left.length == array.length\n\n    array.each_with_index.all? { |pattern, index| match(left[index], pattern, scope) }\n  end\n\n  def match_Hash(hash, left, scope)\n    return false unless left.is_a?(Hash)\n\n    hash.all? { |x, y| match(left[x], y, scope) }\n  end\n\n  def match_Symbol(symbol, left, scope)\n    return true if symbol == :default\n\n    equals(left, default)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/deferred_resolver.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/parser/script_compiler'\n\nmodule Puppet::Pops\nmodule Evaluator\nclass DeferredValue\n  def initialize(proc)\n    @proc = proc\n  end\n\n  def resolve\n    val = @proc.call\n    # Deferred sensitive values will be marked as such in resolve_futures()\n    if val.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      val.unwrap\n    else\n      val\n    end\n  end\nend\n\n# Utility class to help resolve instances of Puppet::Pops::Types::PDeferredType::Deferred\n#\nclass DeferredResolver\n  DOLLAR = '$'\n  DIG    = 'dig'\n\n  # Resolves and replaces all Deferred values in a catalog's resource attributes\n  # found as direct values or nested inside Array, Hash or Sensitive values.\n  # Deferred values inside of custom Object instances are not resolved as this\n  # is expected to be done by such objects.\n  #\n  # @param facts [Puppet::Node::Facts] the facts object for the node\n  # @param catalog [Puppet::Resource::Catalog] the catalog where all deferred values should be replaced\n  # @param environment [Puppet::Node::Environment] the environment whose anonymous module methods\n  #  are to be mixed into the scope\n  # @return [nil] does not return anything - the catalog is modified as a side effect\n  #\n  def self.resolve_and_replace(facts, catalog, environment = catalog.environment_instance, preprocess_deferred = true)\n    compiler = Puppet::Parser::ScriptCompiler.new(environment, catalog.name, preprocess_deferred)\n    resolver = new(compiler, preprocess_deferred)\n    resolver.set_facts_variable(facts)\n    # TODO:\n    #    # When scripting the trusted data are always local, but set them anyway\n    #    @scope.set_trusted(node.trusted_data)\n    #\n    #    # Server facts are always about the local node's version etc.\n    #    @scope.set_server_facts(node.server_facts)\n\n    resolver.resolve_futures(catalog)\n    nil\n  end\n\n  # Resolves a value such that a direct Deferred, or any nested Deferred values\n  # are resolved and used instead of the deferred value.\n  # A direct Deferred value, or nested deferred values inside of Array, Hash or\n  # Sensitive values are resolved and replaced inside of freshly created\n  # containers.\n  #\n  # The resolution takes place in the topscope of the given compiler.\n  # Variable values are supposed to already have been set.\n  #\n  # @param value [Object] the (possibly nested) value to resolve\n  # @param compiler [Puppet::Parser::ScriptCompiler, Puppet::Parser::Compiler] the compiler in effect\n  # @return [Object] the resolved value (a new Array, Hash, or Sensitive if needed), with all deferred values resolved\n  #\n  def self.resolve(value, compiler)\n    resolver = new(compiler)\n    resolver.resolve(value)\n  end\n\n  def initialize(compiler, preprocess_deferred = true)\n    @compiler = compiler\n    # Always resolve in top scope\n    @scope = @compiler.topscope\n    @deferred_class = Puppet::Pops::Types::TypeFactory.deferred.implementation_class\n    @preprocess_deferred = preprocess_deferred\n  end\n\n  # @param facts [Puppet::Node::Facts] the facts to set in $facts in the compiler's topscope\n  #\n  def set_facts_variable(facts)\n    @scope.set_facts(facts.nil? ? {} : facts.values)\n  end\n\n  def resolve_futures(catalog)\n    catalog.resources.each do |r|\n      overrides = {}\n      r.parameters.each_pair do |k, v|\n        resolved = resolve(v)\n        case resolved\n        when Puppet::Pops::Types::PSensitiveType::Sensitive\n          # If the resolved value is instance of Sensitive - assign the unwrapped value\n          # and mark it as sensitive if not already marked\n          #\n          resolved = resolved.unwrap\n          mark_sensitive_parameters(r, k)\n\n        when Puppet::Pops::Evaluator::DeferredValue\n          # If the resolved value is a DeferredValue and it has an argument of type\n          # PSensitiveType, mark it as sensitive. Since DeferredValues can nest,\n          # we must walk all arguments, e.g. the DeferredValue may call the `epp`\n          # function, where one of its arguments is a DeferredValue to call the\n          # `vault:lookup` function.\n          #\n          # The DeferredValue.resolve method will unwrap the sensitive during\n          # catalog application\n          #\n          if contains_sensitive_args?(v)\n            mark_sensitive_parameters(r, k)\n          end\n        end\n        overrides[k] = resolved\n      end\n      r.parameters.merge!(overrides) unless overrides.empty?\n    end\n  end\n\n  # Return true if x contains an argument that is an instance of PSensitiveType:\n  #\n  #   Deferred('new', [Sensitive, 'password'])\n  #\n  # Or an instance of PSensitiveType::Sensitive:\n  #\n  #   Deferred('join', [['a', Sensitive('b')], ':'])\n  #\n  # Since deferred values can nest, descend into Arrays and Hash keys and values,\n  # short-circuiting when the first occurrence is found.\n  #\n  def contains_sensitive_args?(x)\n    case x\n    when @deferred_class\n      contains_sensitive_args?(x.arguments)\n    when Array\n      x.any? { |v| contains_sensitive_args?(v) }\n    when Hash\n      x.any? { |k, v| contains_sensitive_args?(k) || contains_sensitive_args?(v) }\n    when Puppet::Pops::Types::PSensitiveType, Puppet::Pops::Types::PSensitiveType::Sensitive\n      true\n    else\n      false\n    end\n  end\n  private :contains_sensitive_args?\n\n  def mark_sensitive_parameters(r, k)\n    unless r.sensitive_parameters.include?(k.to_sym)\n      r.sensitive_parameters = (r.sensitive_parameters + [k.to_sym]).freeze\n    end\n  end\n  private :mark_sensitive_parameters\n\n  def resolve(x)\n    if x.instance_of?(@deferred_class)\n      resolve_future(x)\n    elsif x.is_a?(Array)\n      x.map { |v| resolve(v) }\n    elsif x.is_a?(Hash)\n      result = {}\n      x.each_pair { |k, v| result[k] = resolve(v) }\n      result\n    elsif x.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      # rewrap in a new Sensitive after resolving any nested deferred values\n      Puppet::Pops::Types::PSensitiveType::Sensitive.new(resolve(x.unwrap))\n    elsif x.is_a?(Puppet::Pops::Types::PBinaryType::Binary)\n      # use the ASCII-8BIT string that it wraps\n      x.binary_buffer\n    else\n      x\n    end\n  end\n\n  def resolve_lazy_args(x)\n    case x\n    when DeferredValue\n      x.resolve\n    when Array\n      x.map { |v| resolve_lazy_args(v) }\n    when Hash\n      result = {}\n      x.each_pair { |k, v| result[k] = resolve_lazy_args(v) }\n      result\n    when Puppet::Pops::Types::PSensitiveType::Sensitive\n      # rewrap in a new Sensitive after resolving any nested deferred values\n      Puppet::Pops::Types::PSensitiveType::Sensitive.new(resolve_lazy_args(x.unwrap))\n    else\n      x\n    end\n  end\n  private :resolve_lazy_args\n\n  def resolve_future(f)\n    # If any of the arguments to a future is a future it needs to be resolved first\n    func_name = f.name\n    mapped_arguments = map_arguments(f.arguments)\n    # if name starts with $ then this is a call to dig\n    if func_name[0] == DOLLAR\n      var_name = func_name[1..]\n      func_name = DIG\n      mapped_arguments.insert(0, @scope[var_name])\n    end\n\n    if @preprocess_deferred\n      # call the function (name in deferred, or 'dig' for a variable)\n      @scope.call_function(func_name, mapped_arguments)\n    else\n      # call the function later\n      DeferredValue.new(\n        proc {\n          # deferred functions can have nested deferred arguments\n          resolved_arguments = mapped_arguments.map { |arg| resolve_lazy_args(arg) }\n          @scope.call_function(func_name, resolved_arguments)\n        }\n      )\n    end\n  end\n\n  def map_arguments(args)\n    return [] if args.nil?\n\n    args.map { |v| resolve(v) }\n  end\n  private :map_arguments\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/epp_evaluator.rb",
    "content": "# frozen_string_literal: true\n\n# Handler of Epp call/evaluation from the epp and inline_epp functions\n#\nclass Puppet::Pops::Evaluator::EppEvaluator\n  def self.inline_epp(scope, epp_source, template_args = nil)\n    unless epp_source.is_a?(String)\n      # TRANSLATORS 'inline_epp()' is a method name and 'epp' refers to 'Embedded Puppet (EPP) template' and should not be translated\n      raise ArgumentError, _(\"inline_epp(): the first argument must be a String with the epp source text, got a %{class_name}\") %\n                           { class_name: epp_source.class }\n    end\n\n    # Parse and validate the source\n    parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new\n    begin\n      result = parser.parse_string(epp_source, 'inlined-epp-text')\n    rescue Puppet::ParseError => e\n      # TRANSLATORS 'inline_epp()' is a method name and 'EPP' refers to 'Embedded Puppet (EPP) template' and should not be translated\n      raise ArgumentError, _(\"inline_epp(): Invalid EPP: %{detail}\") % { detail: e.message }\n    end\n\n    # Evaluate (and check template_args)\n    evaluate(parser, 'inline_epp', scope, false, result, template_args)\n  end\n\n  def self.epp(scope, file, env_name, template_args = nil)\n    unless file.is_a?(String)\n      # TRANSLATORS 'epp()' is a method name and should not be translated\n      raise ArgumentError, _(\"epp(): the first argument must be a String with the filename, got a %{class_name}\") % { class_name: file.class }\n    end\n\n    unless Puppet::FileSystem.exist?(file)\n      unless file =~ /\\.epp$/\n        file += \".epp\"\n      end\n    end\n\n    scope.debug \"Retrieving epp template #{file}\"\n    template_file = Puppet::Parser::Files.find_template(file, env_name)\n    if template_file.nil? || !Puppet::FileSystem.exist?(template_file)\n      raise Puppet::ParseError, _(\"Could not find template '%{file}'\") % { file: file }\n    end\n\n    # Parse and validate the source\n    parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new\n    begin\n      result = parser.parse_file(template_file)\n    rescue Puppet::ParseError => e\n      # TRANSLATORS 'epp()' is a method name and 'EPP' refers to 'Embedded Puppet (EPP) template' and should not be translated\n      raise ArgumentError, _(\"epp(): Invalid EPP: %{detail}\") % { detail: e.message }\n    end\n\n    # Evaluate (and check template_args)\n    evaluate(parser, 'epp', scope, true, result, template_args)\n  end\n\n  def self.evaluate(parser, func_name, scope, use_global_scope_only, parse_result, template_args)\n    template_args, template_args_set = handle_template_args(func_name, template_args)\n\n    body = parse_result.body\n    unless body.is_a?(Puppet::Pops::Model::LambdaExpression)\n      # TRANSLATORS 'LambdaExpression' is a class name and should not be translated\n      raise ArgumentError, _(\"%{function_name}(): the parser did not produce a LambdaExpression, got '%{class_name}'\") %\n                           { function_name: func_name, class_name: body.class }\n    end\n    unless body.body.is_a?(Puppet::Pops::Model::EppExpression)\n      # TRANSLATORS 'EppExpression' is a class name and should not be translated\n      raise ArgumentError, _(\"%{function_name}(): the parser did not produce an EppExpression, got '%{class_name}'\") %\n                           { function_name: func_name, class_name: body.body.class }\n    end\n    unless parse_result.definitions.empty?\n      # TRANSLATORS 'EPP' refers to 'Embedded Puppet (EPP) template'\n      raise ArgumentError, _(\"%{function_name}(): The EPP template contains illegal expressions (definitions)\") %\n                           { function_name: func_name }\n    end\n\n    parameters_specified = body.body.parameters_specified\n    if parameters_specified || template_args_set\n      enforce_parameters = parameters_specified\n    else\n      enforce_parameters = true\n    end\n\n    # filter out all qualified names and set them in qualified_variables\n    # only pass unqualified (filtered) variable names to the template\n    filtered_args = {}\n    template_args.each_pair do |k, v|\n      if k =~ /::/\n        k = k[2..] if k.start_with?('::')\n        scope[k] = v\n      else\n        filtered_args[k] = v\n      end\n    end\n    template_args = filtered_args\n\n    # inline_epp() logic sees all local variables, epp() all global\n    if use_global_scope_only\n      scope.with_global_scope do |global_scope|\n        parser.closure(body, global_scope).call_by_name(template_args, enforce_parameters)\n      end\n    else\n      parser.closure(body, scope).call_by_name(template_args, enforce_parameters)\n    end\n  end\n  private_class_method :evaluate\n\n  def self.handle_template_args(func_name, template_args)\n    if template_args.nil?\n      [{}, false]\n    else\n      unless template_args.is_a?(Hash)\n        # TRANSLATORS 'template_args' is a variable name and should not be translated\n        raise ArgumentError, _(\"%{function_name}(): the template_args must be a Hash, got a %{class_name}\") %\n                             { function_name: func_name, class_name: template_args.class }\n      end\n      [template_args, true]\n    end\n  end\n  private_class_method :handle_template_args\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/evaluator_impl.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/parser/scope'\nrequire_relative '../../../puppet/pops/evaluator/compare_operator'\nrequire_relative '../../../puppet/pops/evaluator/relationship_operator'\nrequire_relative '../../../puppet/pops/evaluator/access_operator'\nrequire_relative '../../../puppet/pops/evaluator/closure'\nrequire_relative '../../../puppet/pops/evaluator/external_syntax_support'\nrequire_relative '../../../puppet/pops/types/iterable'\n\nmodule Puppet::Pops\nmodule Evaluator\n# This implementation of {Evaluator} performs evaluation using the puppet 3.x runtime system\n# in a manner largely compatible with Puppet 3.x, but adds new features and introduces constraints.\n#\n# The evaluation uses _polymorphic dispatch_ which works by dispatching to the first found method named after\n# the class or one of its super-classes. The EvaluatorImpl itself mainly deals with evaluation (it currently\n# also handles assignment), and it uses a delegation pattern to more specialized handlers of some operators\n# that in turn use polymorphic dispatch; this to not clutter EvaluatorImpl with too much responsibility).\n#\n# Since a pattern is used, only the main entry points are fully documented. The parameters _o_ and _scope_ are\n# the same in all the polymorphic methods, (the type of the parameter _o_ is reflected in the method's name;\n# either the actual class, or one of its super classes). The _scope_ parameter is always the scope in which\n# the evaluation takes place. If nothing else is mentioned, the return is always the result of evaluation.\n#\n# See {Visitable} and {Visitor} for more information about\n# polymorphic calling.\n#\nclass EvaluatorImpl\n  include Utils\n\n  # Provides access to the Puppet 3.x runtime (scope, etc.)\n  # This separation has been made to make it easier to later migrate the evaluator to an improved runtime.\n  #\n  include Runtime3Support\n  include ExternalSyntaxSupport\n\n  COMMA_SEPARATOR = ', '\n\n  def initialize\n    @@initialized ||= static_initialize\n\n    # Use null migration checker unless given in context\n    @migration_checker = Puppet.lookup(:migration_checker) { Migration::MigrationChecker.singleton }\n  end\n\n  # @api private\n  def static_initialize\n    @@eval_visitor     ||= Visitor.new(self, \"eval\", 1, 1)\n    @@lvalue_visitor   ||= Visitor.new(self, \"lvalue\", 1, 1)\n    @@assign_visitor   ||= Visitor.new(self, \"assign\", 3, 3)\n    @@string_visitor   ||= Visitor.new(self, \"string\", 1, 1)\n\n    @@type_calculator  ||= Types::TypeCalculator.singleton\n\n    @@compare_operator      ||= CompareOperator.new\n    @@relationship_operator ||= RelationshipOperator.new\n    true\n  end\n  private :static_initialize\n\n  # @api private\n  def type_calculator\n    @@type_calculator\n  end\n\n  # Evaluates the given _target_ object in the given scope.\n  #\n  # @overload evaluate(target, scope)\n  # @param target [Object] evaluation target - see methods on the pattern assign_TYPE for actual supported types.\n  # @param scope [Object] the runtime specific scope class where evaluation should take place\n  # @return [Object] the result of the evaluation\n  #\n  # @api public\n  #\n  def evaluate(target, scope)\n    @@eval_visitor.visit_this_1(self, target, scope)\n  rescue SemanticError => e\n    # A raised issue may not know the semantic target, use errors call stack, but fill in the\n    # rest from a supplied semantic object, or the target instruction if there is not semantic\n    # object.\n    #\n    fail(e.issue, e.semantic || target, e.options, e)\n  rescue Puppet::PreformattedError => e\n    # Already formatted with location information, and with the wanted call stack.\n    # Note this is currently a specialized ParseError, so rescue-order is important\n    #\n    raise e\n  rescue Puppet::ParseError => e\n    # ParseError may be raised in ruby code without knowing the location\n    # in puppet code.\n    # Accept a ParseError that has file or line information available\n    # as an error that should be used verbatim. (Tests typically run without\n    # setting a file name).\n    # ParseError can supply an original - it is impossible to determine which\n    # call stack that should be propagated, using the ParseError's backtrace.\n    #\n    if e.file || e.line\n      raise e\n    else\n      # Since it had no location information, treat it as user intended a general purpose\n      # error. Pass on its call stack.\n      fail(Issues::RUNTIME_ERROR, target, { :detail => e.message }, e)\n    end\n  rescue Puppet::Error => e\n    # PuppetError has the ability to wrap an exception, if so, use the wrapped exception's\n    # call stack instead\n    fail(Issues::RUNTIME_ERROR, target, { :detail => e.message }, e.original || e)\n  rescue StopIteration => e\n    # Ensure these are not rescued as StandardError\n    raise e\n  rescue StandardError => e\n    # All other errors, use its message and call stack\n    fail(Issues::RUNTIME_ERROR, target, { :detail => e.message }, e)\n  end\n\n  # Assigns the given _value_ to the given _target_. The additional argument _o_ is the instruction that\n  # produced the target/value tuple and it is used to set the origin of the result.\n  #\n  # @param target [Object] assignment target - see methods on the pattern assign_TYPE for actual supported types.\n  # @param value [Object] the value to assign to `target`\n  # @param o [Model::PopsObject] originating instruction\n  # @param scope [Object] the runtime specific scope where evaluation should take place\n  #\n  # @api private\n  #\n  def assign(target, value, o, scope)\n    @@assign_visitor.visit_this_3(self, target, value, o, scope)\n  end\n\n  # Computes a value that can be used as the LHS in an assignment.\n  # @param o [Object] the expression to evaluate as a left (assignable) entity\n  # @param scope [Object] the runtime specific scope where evaluation should take place\n  #\n  # @api private\n  #\n  def lvalue(o, scope)\n    @@lvalue_visitor.visit_this_1(self, o, scope)\n  end\n\n  # Produces a String representation of the given object _o_ as used in interpolation.\n  # @param o [Object] the expression of which a string representation is wanted\n  # @param scope [Object] the runtime specific scope where evaluation should take place\n  #\n  # @api public\n  #\n  def string(o, scope)\n    @@string_visitor.visit_this_1(self, o, scope)\n  end\n\n  # Evaluate a BlockExpression in a new scope with variables bound to the\n  # given values.\n  #\n  # @param scope [Puppet::Parser::Scope] the parent scope\n  # @param variable_bindings [Hash{String => Object}] the variable names and values to bind (names are keys, bound values are values)\n  # @param block [Model::BlockExpression] the sequence of expressions to evaluate in the new scope\n  #\n  # @api private\n  #\n  def evaluate_block_with_bindings(scope, variable_bindings, block_expr)\n    scope.with_guarded_scope do\n      # change to create local scope_from - cannot give it file and line -\n      # that is the place of the call, not \"here\"\n      create_local_scope_from(variable_bindings, scope)\n      evaluate(block_expr, scope)\n    end\n  end\n\n  # Implementation of case option matching.\n  #\n  # This is the type of matching performed in a case option, using == for every type\n  # of value except regular expression where a match is performed.\n  #\n  def match?(left, right)\n    @@compare_operator.match(left, right, nil)\n  end\n\n  protected\n\n  def lvalue_VariableExpression(o, scope)\n    # evaluate the name\n    evaluate(o.expr, scope)\n  end\n\n  # Catches all illegal lvalues\n  #\n  def lvalue_Object(o, scope)\n    fail(Issues::ILLEGAL_ASSIGNMENT, o)\n  end\n\n  # An array is assignable if all entries are lvalues\n  def lvalue_LiteralList(o, scope)\n    o.values.map { |x| lvalue(x, scope) }\n  end\n\n  # Assign value to named variable.\n  # The '$' sign is never part of the name.\n  # @example In Puppet DSL\n  #   $name = value\n  # @param name [String] name of variable without $\n  # @param value [Object] value to assign to the variable\n  # @param o [Model::PopsObject] originating instruction\n  # @param scope [Object] the runtime specific scope where evaluation should take place\n  # @return [value<Object>]\n  #\n  def assign_String(name, value, o, scope)\n    if name =~ /::/\n      fail(Issues::CROSS_SCOPE_ASSIGNMENT, o.left_expr, { :name => name })\n    end\n\n    set_variable(name, value, o, scope)\n    value\n  end\n\n  def assign_Numeric(n, value, o, scope)\n    fail(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o.left_expr, { :varname => n.to_s })\n  end\n\n  # Catches all illegal assignment (e.g. 1 = 2, {'a'=>1} = 2, etc)\n  #\n  def assign_Object(name, value, o, scope)\n    fail(Issues::ILLEGAL_ASSIGNMENT, o)\n  end\n\n  def assign_Array(lvalues, values, o, scope)\n    case values\n    when Hash\n      lvalues.map do |lval|\n        assign(lval,\n               values.fetch(lval) { |k| fail(Issues::MISSING_MULTI_ASSIGNMENT_KEY, o, :key => k) },\n               o, scope)\n      end\n    when Puppet::Pops::Types::PClassType\n      if Puppet[:tasks]\n        fail(Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING, o, { :operation => _('multi var assignment from class') })\n      end\n\n      # assign variables from class variables\n      # lookup class resource and return one or more parameter values\n      # TODO: behavior when class_name is nil\n      resource = find_resource(scope, 'class', values.class_name)\n      if resource\n        base_name = \"#{values.class_name.downcase}::\"\n        idx = -1\n        result = lvalues.map do |lval|\n          idx += 1\n          varname = \"#{base_name}#{lval}\"\n          if variable_exists?(varname, scope)\n            result = get_variable_value(varname, o, scope)\n            assign(lval, result, o, scope)\n          else\n            fail(Puppet::Pops::Issues::MISSING_MULTI_ASSIGNMENT_VARIABLE, o.left_expr.values[idx], { :name => varname })\n          end\n        end\n      else\n        fail(Issues::UNKNOWN_RESOURCE, o.right_expr, { :type_name => 'Class', :title => values.class_name })\n      end\n\n    else\n      values = [values] unless values.is_a?(Array)\n      if values.size != lvalues.size\n        fail(Issues::ILLEGAL_MULTI_ASSIGNMENT_SIZE, o, :expected => lvalues.size, :actual => values.size)\n      end\n\n      lvalues.zip(values).map { |lval, val| assign(lval, val, o, scope) }\n    end\n  end\n\n  def eval_Factory(o, scope)\n    evaluate(o.model, scope)\n  end\n\n  # Evaluates any object not evaluated to something else to itself.\n  def eval_Object o, scope\n    o\n  end\n\n  # Allows nil to be used as a Nop, Evaluates to nil\n  def eval_NilClass(o, scope)\n    nil\n  end\n\n  # Evaluates Nop to nil.\n  def eval_Nop(o, scope)\n    nil\n  end\n\n  # Captures all LiteralValues not handled elsewhere.\n  #\n  def eval_LiteralValue(o, scope)\n    o.value\n  end\n\n  # Reserved Words fail to evaluate\n  #\n  def eval_ReservedWord(o, scope)\n    if !o.future\n      fail(Issues::RESERVED_WORD, o, { :word => o.word })\n    else\n      o.word\n    end\n  end\n\n  def eval_LiteralDefault(o, scope)\n    :default\n  end\n\n  def eval_LiteralUndef(o, scope)\n    nil\n  end\n\n  # A QualifiedReference (i.e. a  capitalized qualified name such as Foo, or Foo::Bar) evaluates to a PTypeType\n  #\n  def eval_QualifiedReference(o, scope)\n    type = Types::TypeParser.singleton.interpret(o)\n    fail(Issues::UNKNOWN_RESOURCE_TYPE, o, { :type_name => type.type_string }) if type.is_a?(Types::PTypeReferenceType)\n\n    type\n  end\n\n  def eval_NotExpression(o, scope)\n    !is_true?(evaluate(o.expr, scope), o.expr)\n  end\n\n  def eval_UnaryMinusExpression(o, scope)\n    - coerce_numeric(evaluate(o.expr, scope), o, scope)\n  end\n\n  def eval_UnfoldExpression(o, scope)\n    candidate = evaluate(o.expr, scope)\n    case candidate\n    when nil\n      []\n    when Array\n      candidate\n    when Hash\n      candidate.to_a\n    when Puppet::Pops::Types::Iterable\n      candidate.to_a\n    else\n      # turns anything else into an array (so result can be unfolded)\n      [candidate]\n    end\n  end\n\n  # Abstract evaluation, returns array [left, right] with the evaluated result of left_expr and\n  # right_expr\n  # @return <Array<Object, Object>> array with result of evaluating left and right expressions\n  #\n  def eval_BinaryExpression o, scope\n    [evaluate(o.left_expr, scope), evaluate(o.right_expr, scope)]\n  end\n\n  # Evaluates assignment with operators =, +=, -= and\n  #\n  # @example Puppet DSL\n  #   $a = 1\n  #   $a += 1\n  #   $a -= 1\n  #\n  def eval_AssignmentExpression(o, scope)\n    name = lvalue(o.left_expr, scope)\n    value = evaluate(o.right_expr, scope)\n\n    if o.operator == '='\n      assign(name, value, o, scope)\n    else\n      fail(Issues::UNSUPPORTED_OPERATOR, o, { :operator => o.operator })\n    end\n    value\n  end\n\n  ARITHMETIC_OPERATORS = ['+', '-', '*', '/', '%', '<<', '>>'].freeze\n  COLLECTION_OPERATORS = ['+', '-', '<<'].freeze\n\n  # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >>\n  #\n  def eval_ArithmeticExpression(o, scope)\n    left = evaluate(o.left_expr, scope)\n    right = evaluate(o.right_expr, scope)\n\n    begin\n      result = calculate(left, right, o, scope)\n    rescue ArgumentError => e\n      fail(Issues::RUNTIME_ERROR, o, { :detail => e.message }, e)\n    end\n    result\n  end\n\n  # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >>\n  #\n  def calculate(left, right, bin_expr, scope)\n    operator = bin_expr.operator\n    unless ARITHMETIC_OPERATORS.include?(operator)\n      fail(Issues::UNSUPPORTED_OPERATOR, bin_expr, { :operator => operator })\n    end\n\n    left_o = bin_expr.left_expr\n    if (left.is_a?(URI) || left.is_a?(Types::PBinaryType::Binary)) && operator == '+'\n      concatenate(left, right)\n    elsif (left.is_a?(Array) || left.is_a?(Hash)) && COLLECTION_OPERATORS.include?(operator)\n      # Handle operation on collections\n      case operator\n      when '+'\n        concatenate(left, right)\n      when '-'\n        delete(left, right)\n      when '<<'\n        unless left.is_a?(Array)\n          fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, { :operator => operator, :left_value => left })\n        end\n\n        left + [right]\n      end\n    else\n      # Handle operation on numeric\n      left = coerce_numeric(left, left_o, scope)\n      right = coerce_numeric(right, bin_expr.right_expr, scope)\n      begin\n        if operator == '%' && (left.is_a?(Float) || right.is_a?(Float))\n          # Deny users the fun of seeing severe rounding errors and confusing results\n          fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, { :operator => operator, :left_value => left }) if left.is_a?(Float)\n\n          fail(Issues::OPERATOR_NOT_APPLICABLE_WHEN, left_o, { :operator => operator, :left_value => left, :right_value => right })\n        end\n        if right.is_a?(Time::TimeData) && !left.is_a?(Time::TimeData)\n          if operator == '+' || operator == '*' && right.is_a?(Time::Timespan)\n            # Switch places. Let the TimeData do the arithmetic\n            x = left\n            left = right\n            right = x\n          elsif operator == '-' && right.is_a?(Time::Timespan)\n            left = Time::Timespan.new((left * Time::NSECS_PER_SEC).to_i)\n          else\n            fail(Issues::OPERATOR_NOT_APPLICABLE_WHEN, left_o, { :operator => operator, :left_value => left, :right_value => right })\n          end\n        end\n        result = left.send(operator, right)\n      rescue NoMethodError\n        fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, { :operator => operator, :left_value => left })\n      rescue ZeroDivisionError\n        fail(Issues::DIV_BY_ZERO, bin_expr.right_expr)\n      end\n      case result\n      when Float\n        if result == Float::INFINITY || result == -Float::INFINITY\n          fail(Issues::RESULT_IS_INFINITY, left_o, { :operator => operator })\n        end\n      when Integer\n        if result < MIN_INTEGER || result > MAX_INTEGER\n          fail(Issues::NUMERIC_OVERFLOW, bin_expr, { :value => result })\n        end\n      end\n      result\n    end\n  end\n\n  def eval_EppExpression(o, scope)\n    contains_sensitive = false\n\n    scope[\"@epp\"] = []\n    evaluate(o.body, scope)\n    result = scope[\"@epp\"].map do |r|\n      if r.instance_of?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n        contains_sensitive = true\n        string(r.unwrap, scope)\n      else\n        r\n      end\n    end.join\n\n    if contains_sensitive\n      Puppet::Pops::Types::PSensitiveType::Sensitive.new(result)\n    else\n      result\n    end\n  end\n\n  def eval_RenderStringExpression(o, scope)\n    scope[\"@epp\"] << o.value.dup\n    nil\n  end\n\n  def eval_RenderExpression(o, scope)\n    result = evaluate(o.expr, scope)\n    if result.instance_of?(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      scope[\"@epp\"] << result\n    else\n      scope[\"@epp\"] << string(result, scope)\n    end\n    nil\n  end\n\n  # Evaluates Puppet DSL ->, ~>, <-, and <~\n  def eval_RelationshipExpression(o, scope)\n    # First level evaluation, reduction to basic data types or puppet types, the relationship operator then translates this\n    # to the final set of references (turning strings into references, which can not naturally be done by the main evaluator since\n    # all strings should not be turned into references.\n    #\n    real = eval_BinaryExpression(o, scope)\n    @@relationship_operator.evaluate(real, o, scope)\n  end\n\n  # Evaluates x[key, key, ...]\n  #\n  def eval_AccessExpression(o, scope)\n    left = evaluate(o.left_expr, scope)\n    keys = o.keys || []\n    if left.is_a?(Types::PClassType)\n      # Evaluate qualified references without errors no undefined types\n      keys = keys.map { |key| key.is_a?(Model::QualifiedReference) ? Types::TypeParser.singleton.interpret(key) : evaluate(key, scope) }\n    else\n      keys = keys.map { |key| evaluate(key, scope) }\n      # Resource[File] becomes File\n      return keys[0] if Types::PResourceType::DEFAULT == left && keys.size == 1 && keys[0].is_a?(Types::PResourceType)\n    end\n    AccessOperator.new(o).access(left, scope, *keys)\n  end\n\n  # Evaluates <, <=, >, >=, and ==\n  #\n  def eval_ComparisonExpression o, scope\n    left = evaluate(o.left_expr, scope)\n    right = evaluate(o.right_expr, scope)\n\n    begin\n      # Left is a type\n      if left.is_a?(Types::PAnyType)\n        case o.operator\n        when '=='\n          @@type_calculator.equals(left, right)\n\n        when '!='\n          !@@type_calculator.equals(left, right)\n\n        when '<'\n          # left can be assigned to right, but they are not equal\n          @@type_calculator.assignable?(right, left) && !@@type_calculator.equals(left, right)\n        when '<='\n          # left can be assigned to right\n          @@type_calculator.assignable?(right, left)\n        when '>'\n          # right can be assigned to left, but they are not equal\n          @@type_calculator.assignable?(left, right) && !@@type_calculator.equals(left, right)\n        when '>='\n          # right can be assigned to left\n          @@type_calculator.assignable?(left, right)\n        else\n          fail(Issues::UNSUPPORTED_OPERATOR, o, { :operator => o.operator })\n        end\n      else\n        case o.operator\n        when '=='\n          @@compare_operator.equals(left, right)\n        when '!='\n          !@@compare_operator.equals(left, right)\n        when '<'\n          @@compare_operator.compare(left, right) < 0\n        when '<='\n          @@compare_operator.compare(left, right) <= 0\n        when '>'\n          @@compare_operator.compare(left, right) > 0\n        when '>='\n          @@compare_operator.compare(left, right) >= 0\n        else\n          fail(Issues::UNSUPPORTED_OPERATOR, o, { :operator => o.operator })\n        end\n      end\n    rescue ArgumentError => e\n      fail(Issues::COMPARISON_NOT_POSSIBLE, o, {\n             :operator => o.operator,\n             :left_value => left,\n             :right_value => right,\n             :detail => e.message\n           }, e)\n    end\n  end\n\n  # Evaluates matching expressions with type, string or regexp rhs expression.\n  # If RHS is a type, the =~ matches compatible (instance? of) type.\n  #\n  # @example\n  #   x =~ /abc.*/\n  # @example\n  #   x =~ \"abc.*/\"\n  # @example\n  #   y = \"abc\"\n  #   x =~ \"${y}.*\"\n  # @example\n  #   [1,2,3] =~ Array[Integer[1,10]]\n  #\n  # Note that a string is not instance? of Regexp, only Regular expressions are.\n  # The Pattern type should instead be used as it is specified as subtype of String.\n  #\n  # @return [Boolean] if a match was made or not. Also sets $0..$n to matchdata in current scope.\n  #\n  def eval_MatchExpression o, scope\n    left = evaluate(o.left_expr, scope)\n    pattern = evaluate(o.right_expr, scope)\n\n    # matches RHS types as instance of for all types except a parameterized Regexp[R]\n    if pattern.is_a?(Types::PAnyType)\n      # evaluate as instance? of type check\n      matched = pattern.instance?(left)\n      # convert match result to Boolean true, or false\n      return o.operator == '=~' ? !!matched : !matched\n    end\n\n    if pattern.is_a?(SemanticPuppet::VersionRange)\n      # evaluate if range includes version\n      matched = Types::PSemVerRangeType.include?(pattern, left)\n      return o.operator == '=~' ? matched : !matched\n    end\n\n    begin\n      pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)\n    rescue StandardError => e\n      fail(Issues::MATCH_NOT_REGEXP, o.right_expr, { :detail => e.message }, e)\n    end\n    unless left.is_a?(String)\n      fail(Issues::MATCH_NOT_STRING, o.left_expr, { :left_value => left })\n    end\n\n    matched = pattern.match(left) # nil, or MatchData\n    set_match_data(matched, scope) # creates ephemeral\n\n    # convert match result to Boolean true, or false\n    o.operator == '=~' ? !!matched : !matched\n  end\n\n  # Evaluates Puppet DSL `in` expression\n  #\n  def eval_InExpression o, scope\n    left = evaluate(o.left_expr, scope)\n    right = evaluate(o.right_expr, scope)\n    @@compare_operator.include?(right, left, scope)\n  end\n\n  # @example\n  #   $a and $b\n  # b is only evaluated if a is true\n  #\n  def eval_AndExpression o, scope\n    is_true?(evaluate(o.left_expr, scope), o.left_expr) ? is_true?(evaluate(o.right_expr, scope), o.right_expr) : false\n  end\n\n  # @example\n  #   a or b\n  # b is only evaluated if a is false\n  #\n  def eval_OrExpression o, scope\n    is_true?(evaluate(o.left_expr, scope), o.left_expr) ? true : is_true?(evaluate(o.right_expr, scope), o.right_expr)\n  end\n\n  # Evaluates each entry of the literal list and creates a new Array\n  # Supports unfolding of entries\n  # @return [Array] with the evaluated content\n  #\n  def eval_LiteralList o, scope\n    unfold([], o.values, scope)\n  end\n\n  # Evaluates each entry of the literal hash and creates a new Hash.\n  # @return [Hash] with the evaluated content\n  #\n  def eval_LiteralHash o, scope\n    # optimized\n    o.entries.each_with_object({}) { |entry, h| h[evaluate(entry.key, scope)] = evaluate(entry.value, scope); }\n  end\n\n  # Evaluates all statements and produces the last evaluated value\n  #\n  def eval_BlockExpression o, scope\n    o.statements.reduce(nil) { |_memo, s| evaluate(s, scope) }\n  end\n\n  # Performs optimized search over case option values, lazily evaluating each\n  # until there is a match. If no match is found, the case expression's default expression\n  # is evaluated (it may be nil or Nop if there is no default, thus producing nil).\n  # If an option matches, the result of evaluating that option is returned.\n  # @return [Object, nil] what a matched option returns, or nil if nothing matched.\n  #\n  def eval_CaseExpression(o, scope)\n    # memo scope level before evaluating test - don't want a match in the case test to leak $n match vars\n    # to expressions after the case expression.\n    #\n    scope.with_guarded_scope do\n      test = evaluate(o.test, scope)\n\n      result = nil\n      the_default = nil\n      if o.options.find do |co|\n        # the first case option that matches\n        next unless co.values.find do |c|\n          c = unwind_parentheses(c)\n          case c\n          when Model::LiteralDefault\n            the_default = co.then_expr\n            next false\n          when Model::UnfoldExpression\n            # not ideal for error reporting, since it is not known which unfolded result\n            # that caused an error - the entire unfold expression is blamed (i.e. the var c, passed to is_match?)\n            evaluate(c, scope).any? { |v| is_match?(test, v, c, co, scope) }\n          else\n            is_match?(test, evaluate(c, scope), c, co, scope)\n          end\n        end\n\n        result = evaluate(co.then_expr, scope)\n        true # the option was picked\n      end\n        result # an option was picked, and produced a result\n      else\n        evaluate(the_default, scope) # evaluate the default (should be a nop/nil) if there is no default).\n      end\n    end\n  end\n\n  # Evaluates a CollectExpression by creating a collector transformer. The transformer\n  # will evaluate the collection, create the appropriate collector, and hand it off\n  # to the compiler to collect the resources specified by the query.\n  #\n  def eval_CollectExpression o, scope\n    if o.query.is_a?(Model::ExportedQuery)\n      optionally_fail(Issues::RT_NO_STORECONFIGS, o);\n    end\n    CollectorTransformer.new().transform(o, scope)\n  end\n\n  def eval_ParenthesizedExpression(o, scope)\n    evaluate(o.expr, scope)\n  end\n\n  # This evaluates classes, nodes and resource type definitions to nil, since 3x:\n  # instantiates them, and evaluates their parameters and body. This is achieved by\n  # providing bridge AST classes in Puppet::Parser::AST::PopsBridge that bridges a\n  # Pops Program and a Pops Expression.\n  #\n  # Since all Definitions are handled \"out of band\", they are treated as a no-op when\n  # evaluated.\n  #\n  def eval_Definition(o, scope)\n    nil\n  end\n\n  def eval_Program(o, scope)\n    file = o.locator.file\n    line = 0\n    # Add stack frame for \"top scope\" logic. See Puppet::Pops::PuppetStack\n    Puppet::Pops::PuppetStack.stack(file, line, self, 'evaluate', [o.body, scope])\n    # evaluate(o.body, scope)\n  rescue Puppet::Pops::Evaluator::PuppetStopIteration => ex\n    # breaking out of a file level program is not allowed\n    # TRANSLATOR break() is a method that should not be translated\n    raise Puppet::ParseError.new(_(\"break() from context where this is illegal\"), ex.file, ex.line)\n  end\n\n  # Produces Array[PAnyType], an array of resource references\n  #\n  def eval_ResourceExpression(o, scope)\n    exported = o.exported\n    virtual = o.virtual\n\n    # Get the type name\n    type_name =\n      if (tmp_name = o.type_name).is_a?(Model::QualifiedName)\n        tmp_name.value # already validated as a name\n      else\n        type_name_acceptable =\n          case o.type_name\n          when Model::QualifiedReference\n            true\n          when Model::AccessExpression\n            o.type_name.left_expr.is_a?(Model::QualifiedReference)\n          end\n\n        evaluated_name = evaluate(tmp_name, scope)\n        unless type_name_acceptable\n          actual = type_calculator.generalize(type_calculator.infer(evaluated_name)).to_s\n          fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, { :actual => actual })\n        end\n\n        # must be a CatalogEntry subtype\n        case evaluated_name\n        when Types::PClassType\n          unless evaluated_name.class_name.nil?\n            fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, { :actual => evaluated_name.to_s })\n          end\n\n          'class'\n\n        when Types::PResourceType\n          unless evaluated_name.title().nil?\n            fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, { :actual => evaluated_name.to_s })\n          end\n\n          evaluated_name.type_name # assume validated\n\n        when Types::PTypeReferenceType\n          fail(Issues::UNKNOWN_RESOURCE_TYPE, o.type_string, { :type_name => evaluated_name.to_s })\n\n        else\n          actual = type_calculator.generalize(type_calculator.infer(evaluated_name)).to_s\n          fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, { :actual => actual })\n        end\n      end\n\n    # This is a runtime check - the model is valid, but will have runtime issues when evaluated\n    # and storeconfigs is not set.\n    if o.exported\n      optionally_fail(Issues::RT_NO_STORECONFIGS_EXPORT, o);\n    end\n\n    titles_to_body = {}\n    body_to_titles = {}\n    body_to_params = {}\n\n    # titles are evaluated before attribute operations\n    o.bodies.map do |body|\n      titles = evaluate(body.title, scope)\n\n      # Title may not be nil\n      # Titles may be given as an array, it is ok if it is empty, but not if it contains nil entries\n      # Titles may not be an empty String\n      # Titles must be unique in the same resource expression\n      # There may be a :default entry, its entries apply with lower precedence\n      #\n      if titles.nil?\n        fail(Issues::MISSING_TITLE, body.title)\n      end\n\n      titles = [titles].flatten\n\n      # Check types of evaluated titles and duplicate entries\n      titles.each_with_index do |title, index|\n        if title.nil?\n          fail(Issues::MISSING_TITLE_AT, body.title, { :index => index })\n\n        elsif !title.is_a?(String) && title != :default\n          actual = type_calculator.generalize(type_calculator.infer(title)).to_s\n          fail(Issues::ILLEGAL_TITLE_TYPE_AT, body.title, { :index => index, :actual => actual })\n\n        elsif title == EMPTY_STRING\n          fail(Issues::EMPTY_STRING_TITLE_AT, body.title, { :index => index })\n\n        elsif titles_to_body[title]\n          fail(Issues::DUPLICATE_TITLE, o, { :title => title })\n        end\n\n        titles_to_body[title] = body\n      end\n\n      # Do not create a real instance from the :default case\n      titles.delete(:default)\n\n      body_to_titles[body] = titles\n\n      # Store evaluated parameters in a hash associated with the body, but do not yet create resource\n      # since the entry containing :defaults may appear later\n      body_to_params[body] = body.operations.each_with_object({}) do |op, param_memo|\n        params = evaluate(op, scope)\n        params = [params] unless params.is_a?(Array)\n        params.each do |p|\n          if param_memo.include? p.name\n            fail(Issues::DUPLICATE_ATTRIBUTE, o, { :attribute => p.name })\n          end\n\n          param_memo[p.name] = p\n        end\n      end\n    end\n\n    # Titles and Operations have now been evaluated and resources can be created\n    # Each production is a PResource, and an array of all is produced as the result of\n    # evaluating the ResourceExpression.\n    #\n    defaults_hash = body_to_params[titles_to_body[:default]] || {}\n    o.bodies.map do |body|\n      titles = body_to_titles[body]\n      params = defaults_hash.merge(body_to_params[body] || {})\n      create_resources(o, scope, virtual, exported, type_name, titles, params.values)\n    end.flatten.compact\n  end\n\n  def eval_ResourceOverrideExpression(o, scope)\n    evaluated_resources = evaluate(o.resources, scope)\n    evaluated_parameters = o.operations.map { |op| evaluate(op, scope) }\n    create_resource_overrides(o, scope, [evaluated_resources].flatten, evaluated_parameters)\n    evaluated_resources\n  end\n\n  def eval_ApplyExpression(o, scope)\n    # All expressions are wrapped in an ApplyBlockExpression so we can identify the contents of\n    # that block. However we don't want to serialize the block expression, so unwrap here.\n    body = if o.body.statements.count == 1\n             o.body.statements[0]\n           else\n             Model::BlockExpression.from_asserted_hash(o.body._pcore_init_hash)\n           end\n\n    Puppet.lookup(:apply_executor).apply(unfold([], o.arguments, scope), body, scope)\n  end\n\n  # Produces 3x parameter\n  def eval_AttributeOperation(o, scope)\n    create_resource_parameter(o, scope, o.attribute_name, evaluate(o.value_expr, scope), o.operator)\n  end\n\n  def eval_AttributesOperation(o, scope)\n    hashed_params = evaluate(o.expr, scope)\n    unless hashed_params.is_a?(Hash)\n      actual = type_calculator.generalize(type_calculator.infer(hashed_params)).to_s\n      fail(Issues::TYPE_MISMATCH, o.expr, { :expected => 'Hash', :actual => actual })\n    end\n    hashed_params.map { |k, v| create_resource_parameter(o, scope, k, v, '=>') }\n  end\n\n  # Sets default parameter values for a type, produces the type\n  #\n  def eval_ResourceDefaultsExpression(o, scope)\n    type = evaluate(o.type_ref, scope)\n    type_name =\n      if type.is_a?(Types::PResourceType) && !type.type_name.nil? && type.title.nil?\n        type.type_name # assume it is a valid name\n      else\n        actual = type_calculator.generalize(type_calculator.infer(type))\n        fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_ref, { :actual => actual })\n      end\n    evaluated_parameters = o.operations.map { |op| evaluate(op, scope) }\n    create_resource_defaults(o, scope, type_name, evaluated_parameters)\n    # Produce the type\n    type\n  end\n\n  # Evaluates function call by name.\n  #\n  def eval_CallNamedFunctionExpression(o, scope)\n    # If LHS is a type (i.e. Integer, or Integer[...]\n    # the call is taken as an instantiation of the given type\n    #\n    functor = o.functor_expr\n    if functor.is_a?(Model::QualifiedReference) ||\n       functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference)\n      # instantiation\n      type = evaluate(functor, scope)\n      return call_function_with_block('new', unfold([type], o.arguments || [], scope), o, scope)\n    end\n\n    # The functor expression is not evaluated, it is not possible to select the function to call\n    # via an expression like $a()\n    case functor\n    when Model::QualifiedName\n      # ok\n    when Model::RenderStringExpression\n      # helpful to point out this easy to make Epp error\n      fail(Issues::ILLEGAL_EPP_PARAMETERS, o)\n    else\n      fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, { :feature => 'function name', :container => o })\n    end\n    name = o.functor_expr.value\n    call_function_with_block(name, unfold([], o.arguments, scope), o, scope)\n  end\n\n  # Evaluation of CallMethodExpression handles a NamedAccessExpression functor (receiver.function_name)\n  #\n  def eval_CallMethodExpression(o, scope)\n    unless o.functor_expr.is_a? Model::NamedAccessExpression\n      fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, { :feature => 'function accessor', :container => o })\n    end\n\n    receiver = unfold([], [o.functor_expr.left_expr], scope)\n    name = o.functor_expr.right_expr\n    unless name.is_a? Model::QualifiedName\n      fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, { :feature => 'function name', :container => o })\n    end\n\n    name = name.value # the string function name\n\n    obj = receiver[0]\n    receiver_type = Types::TypeCalculator.infer_callable_methods_t(obj)\n    if receiver_type.is_a?(Types::TypeWithMembers)\n      member = receiver_type[name]\n      unless member.nil?\n        args = unfold([], o.arguments || [], scope)\n        return o.lambda.nil? ? member.invoke(obj, scope, args) : member.invoke(obj, scope, args, &proc_from_lambda(o.lambda, scope))\n      end\n    end\n\n    call_function_with_block(name, unfold(receiver, o.arguments || [], scope), o, scope)\n  end\n\n  def call_function_with_block(name, evaluated_arguments, o, scope)\n    if o.lambda.nil?\n      call_function(name, evaluated_arguments, o, scope)\n    else\n      call_function(name, evaluated_arguments, o, scope, &proc_from_lambda(o.lambda, scope))\n    end\n  end\n  private :call_function_with_block\n\n  def proc_from_lambda(lambda, scope)\n    closure = Closure::Dynamic.new(self, lambda, scope)\n    PuppetProc.new(closure) { |*args| closure.call(*args) }\n  end\n  private :proc_from_lambda\n\n  # @example\n  #   $x ? { 10 => true, 20 => false, default => 0 }\n  #\n  def eval_SelectorExpression o, scope\n    # memo scope level before evaluating test - don't want a match in the case test to leak $n match vars\n    # to expressions after the selector expression.\n    #\n    scope.with_guarded_scope do\n      test = evaluate(o.left_expr, scope)\n\n      the_default = nil\n      selected = o.selectors.find do |s|\n        me = unwind_parentheses(s.matching_expr)\n        case me\n        when Model::LiteralDefault\n          the_default = s.value_expr\n          false\n        when Model::UnfoldExpression\n          # not ideal for error reporting, since it is not known which unfolded result\n          # that caused an error - the entire unfold expression is blamed (i.e. the var c, passed to is_match?)\n          evaluate(me, scope).any? { |v| is_match?(test, v, me, s, scope) }\n        else\n          is_match?(test, evaluate(me, scope), me, s, scope)\n        end\n      end\n      if selected\n        evaluate(selected.value_expr, scope)\n      elsif the_default\n        evaluate(the_default, scope)\n      else\n        fail(Issues::UNMATCHED_SELECTOR, o.left_expr, :param_value => test)\n      end\n    end\n  end\n\n  # Evaluates Puppet DSL Heredoc\n  def eval_HeredocExpression o, scope\n    expr = o.text_expr\n    result = evaluate(o.text_expr, scope)\n    unless expr.is_a?(Model::LiteralString)\n      # When expr is a LiteralString, validation has already validated this\n      assert_external_syntax(scope, result, o.syntax, o.text_expr)\n    end\n    result\n  end\n\n  # Evaluates Puppet DSL `if`\n  def eval_IfExpression o, scope\n    scope.with_guarded_scope do\n      if is_true?(evaluate(o.test, scope), o.test)\n        evaluate(o.then_expr, scope)\n      else\n        evaluate(o.else_expr, scope)\n      end\n    end\n  end\n\n  # Evaluates Puppet DSL `unless`\n  def eval_UnlessExpression o, scope\n    scope.with_guarded_scope do\n      if is_true?(evaluate(o.test, scope), o.test)\n        evaluate(o.else_expr, scope)\n      else\n        evaluate(o.then_expr, scope)\n      end\n    end\n  end\n\n  # Evaluates a variable (getting its value)\n  # The evaluator is lenient; any expression producing a String is used as a name\n  # of a variable.\n  #\n  def eval_VariableExpression o, scope\n    # Evaluator is not too fussy about what constitutes a name as long as the result\n    # is a String and a valid variable name\n    #\n    name = evaluate(o.expr, scope)\n\n    # Should be caught by validation, but make this explicit here as well, or mysterious evaluation issues\n    # may occur for some evaluation use cases.\n    case name\n    when String\n      # do nothing\n    when Numeric\n      # do nothing\n    else\n      fail(Issues::ILLEGAL_VARIABLE_EXPRESSION, o.expr)\n    end\n    get_variable_value(name, o, scope)\n  end\n\n  # Evaluates double quoted strings that may contain interpolation\n  #\n  def eval_ConcatenatedString o, scope\n    o.segments.collect { |expr| string(evaluate(expr, scope), scope) }.join\n  end\n\n  # If the wrapped expression is a QualifiedName, it is taken as the name of a variable in scope.\n  # Note that this is different from the 3.x implementation, where an initial qualified name\n  # is accepted. (e.g. `\"---${var + 1}---\"` is legal. This implementation requires such concrete\n  # syntax to be expressed in a model as `(TextExpression (+ (Variable var) 1)` - i.e. moving the decision to\n  # the parser.\n  #\n  # Semantics; the result of an expression is turned into a string, nil is silently transformed to empty\n  # string.\n  # @return [String] the interpolated result\n  #\n  def eval_TextExpression o, scope\n    if o.expr.is_a?(Model::QualifiedName)\n      string(get_variable_value(o.expr.value, o, scope), scope)\n    else\n      string(evaluate(o.expr, scope), scope)\n    end\n  end\n\n  def string_Object(o, scope)\n    o.to_s\n  end\n\n  def string_Symbol(o, scope)\n    if :undef == o # optimized comparison 1.44 vs 1.95\n      EMPTY_STRING\n    else\n      o.to_s\n    end\n  end\n\n  def string_Array(o, scope)\n    \"[#{o.map { |e| string(e, scope) }.join(COMMA_SEPARATOR)}]\"\n  end\n\n  def string_Hash(o, scope)\n    \"{#{o.map { |k, v| \"#{string(k, scope)} => #{string(v, scope)}\" }.join(COMMA_SEPARATOR)}}\"\n  end\n\n  def string_Regexp(o, scope)\n    Types::PRegexpType.regexp_to_s_with_delimiters(o)\n  end\n\n  def string_PAnyType(o, scope)\n    o.to_s\n  end\n\n  # Produces concatenation / merge of x and y.\n  #\n  # When x is an Array, y of type produces:\n  #\n  # * Array => concatenation `[1,2], [3,4] => [1,2,3,4]`\n  # * Hash => concatenation of hash as array `[key, value, key, value, ...]`\n  # * any other => concatenation of single value\n  #\n  # When x is a Hash, y of type produces:\n  #\n  # * Array => merge of array interpreted as `[key, value, key, value,...]`\n  # * Hash => a merge, where entries in `y` overrides\n  # * any other => error\n  #\n  # When x is a URI, y of type produces:\n  #\n  # * String => merge of URI interpreted x + URI(y) using URI merge semantics\n  # * URI => merge of URI interpreted x + y using URI merge semantics\n  # * any other => error\n  #\n  # When x is nil, an empty array is used instead.\n  #\n  # @note to concatenate an Array, nest the array - i.e. `[1,2], [[2,3]]`\n  #\n  # @overload concatenate(obj_x, obj_y)\n  #   @param obj_x [Object] object to wrap in an array and concatenate to; see other overloaded methods for return type\n  #   @param ary_y [Object] array to concatenate at end of `ary_x`\n  #   @return [Object] wraps obj_x in array before using other overloaded option based on type of obj_y\n  # @overload concatenate(ary_x, ary_y)\n  #   @param ary_x [Array] array to concatenate to\n  #   @param ary_y [Array] array to concatenate at end of `ary_x`\n  #   @return [Array] new array with `ary_x` + `ary_y`\n  # @overload concatenate(ary_x, hsh_y)\n  #   @param ary_x [Array] array to concatenate to\n  #   @param hsh_y [Hash] converted to array form, and concatenated to array\n  #   @return [Array] new array with `ary_x` + `hsh_y` converted to array\n  # @overload concatenate (ary_x, obj_y)\n  #   @param ary_x [Array] array to concatenate to\n  #   @param obj_y [Object] non array or hash object to add to array\n  #   @return [Array] new array with `ary_x` + `obj_y` added as last entry\n  # @overload concatenate(hsh_x, ary_y)\n  #   @param hsh_x [Hash] the hash to merge with\n  #   @param ary_y [Array] array interpreted as even numbered sequence of key, value merged with `hsh_x`\n  #   @return [Hash] new hash with `hsh_x` merged with `ary_y` interpreted as hash in array form\n  # @overload concatenate(hsh_x, hsh_y)\n  #   @param hsh_x [Hash] the hash to merge to\n  #   @param hsh_y [Hash] hash merged with `hsh_x`\n  #   @return [Hash] new hash with `hsh_x` merged with `hsh_y`\n  # @overload concatenate(uri_x, uri_y)\n  #   @param uri_x [URI] the uri to merge to\n  #   @param uri_y [URI] uri to merged with `uri_x`\n  #   @return [URI] new uri with `uri_x` merged with `uri_y`\n  # @overload concatenate(uri_x, string_y)\n  #   @param uri_x [URI] the uri to merge to\n  #   @param string_y [String] string to merge with `uri_x`\n  #   @return [URI] new uri with `uri_x` merged with `string_y`\n  # @raise [ArgumentError] when `xxx_x` is neither an Array nor a Hash\n  # @raise [ArgumentError] when `xxx_x` is a Hash, and `xxx_y` is neither Array nor Hash.\n  #\n  def concatenate(x, y)\n    case x\n    when Array\n      y = case y\n          when Array then y\n          when Hash  then y.to_a\n          else            [y]\n          end\n      x + y # new array with concatenation\n    when Hash\n      y = case y\n          when Hash then y\n          when Array\n            # Hash[[a, 1, b, 2]] => {}\n            # Hash[a,1,b,2] => {a => 1, b => 2}\n            # Hash[[a,1], [b,2]] => {[a,1] => [b,2]}\n            # Hash[[[a,1], [b,2]]] => {a => 1, b => 2}\n            # Use type calculator to determine if array is Array[Array[?]], and if so use second form\n            # of call\n            t = @@type_calculator.infer(y)\n            if t.element_type.is_a? Types::PArrayType\n              y.to_h\n            else\n              Hash[*y]\n            end\n          else\n            raise ArgumentError, _('Can only append Array or Hash to a Hash')\n          end\n      x.merge y # new hash with overwrite\n    when URI\n      raise ArgumentError, _('An URI can only be merged with an URI or String') unless y.is_a?(String) || y.is_a?(URI)\n\n      x + y\n    when Types::PBinaryType::Binary\n      raise ArgumentError, _('Can only append Binary to a Binary') unless y.is_a?(Types::PBinaryType::Binary)\n\n      Types::PBinaryType::Binary.from_binary_string(x.binary_buffer + y.binary_buffer)\n    else\n      concatenate([x], y)\n    end\n  end\n\n  # Produces the result x \\ y (set difference)\n  # When `x` is an Array, `y` is transformed to an array and then all matching elements removed from x.\n  # When `x` is a Hash, all contained keys are removed from x as listed in `y` if it is an Array, or all its keys if it is a Hash.\n  # The difference is returned. The given `x` and `y` are not modified by this operation.\n  # @raise [ArgumentError] when `x` is neither an Array nor a Hash\n  #\n  def delete(x, y)\n    result = x.dup\n    case x\n    when Array\n      y = case y\n          when Array then y\n          when Hash  then y.to_a\n          else            [y]\n          end\n      y.each { |e| result.delete(e) }\n    when Hash\n      y = case y\n          when Array then y\n          when Hash  then y.keys\n          else            [y]\n          end\n      y.each { |e| result.delete(e) }\n    else\n      raise ArgumentError, _(\"Can only delete from an Array or Hash.\")\n    end\n    result\n  end\n\n  # Implementation of case option matching.\n  #\n  # This is the type of matching performed in a case option, using == for every type\n  # of value except regular expression where a match is performed.\n  #\n  def is_match?(left, right, o, option_expr, scope)\n    @@compare_operator.match(left, right, scope)\n  end\n\n  # Maps the expression in the given array to their product except for UnfoldExpressions which are first unfolded.\n  # The result is added to the given result Array.\n  # @param result [Array] Where to add the result (may contain information to add to)\n  # @param array [Array[Model::Expression] the expressions to map\n  # @param scope [Puppet::Parser::Scope] the scope to evaluate in\n  # @return [Array] the given result array with content added from the operation\n  #\n  def unfold(result, array, scope)\n    array.each do |x|\n      x = unwind_parentheses(x)\n      if x.is_a?(Model::UnfoldExpression)\n        result.concat(evaluate(x, scope))\n      else\n        result << evaluate(x, scope)\n      end\n    end\n    result\n  end\n  private :unfold\n\n  def unwind_parentheses(o)\n    return o unless o.is_a?(Model::ParenthesizedExpression)\n\n    unwind_parentheses(o.expr)\n  end\n  private :unwind_parentheses\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/external_syntax_support.rb",
    "content": "# frozen_string_literal: true\n\n# This module is an integral part of the evaluator. It deals with the concern of validating\n# external syntax in text produced by heredoc and templates.\n#\nrequire_relative '../../../puppet/plugins/syntax_checkers'\nmodule Puppet::Pops::Evaluator::ExternalSyntaxSupport\n  def assert_external_syntax(_, result, syntax, reference_expr)\n    # ignore 'unspecified syntax'\n    return if syntax.nil? || syntax == ''\n\n    checker = checker_for_syntax(nil, syntax)\n    # ignore syntax with no matching checker\n    return unless checker\n\n    # Call checker and give it the location information from the expression\n    # (as opposed to where the heredoc tag is (somewhere on the line above)).\n    acceptor = Puppet::Pops::Validation::Acceptor.new()\n    checker.check(result, syntax, acceptor, reference_expr)\n\n    if acceptor.error_count > 0\n      checker_message = \"Invalid produced text having syntax: '#{syntax}'.\"\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, :message => checker_message)\n      raise ArgumentError, _(\"Internal Error: Configuration of runtime error handling wrong: should have raised exception\")\n    end\n  end\n\n  # Finds the most significant checker for the given syntax (most significant is to the right).\n  # Returns nil if there is no registered checker.\n  #\n  def checker_for_syntax(_, syntax)\n    checkers_hash = Puppet.lookup(:plugins)[Puppet::Plugins::SyntaxCheckers::SYNTAX_CHECKERS_KEY]\n    checkers_hash[lookup_keys_for_syntax(syntax).find { |x| checkers_hash[x] }]\n  end\n\n  # Returns an array of possible syntax names\n  def lookup_keys_for_syntax(syntax)\n    segments = syntax.split(/\\+/)\n    result = []\n    loop do\n      result << segments.join(\"+\")\n      segments.shift\n      break if segments.empty?\n    end\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/json_strict_literal_evaluator.rb",
    "content": "# frozen_string_literal: true\n\n# Literal values for\n#\n#   * String\n#   * Numbers\n#   * Booleans\n#   * Undef (produces nil)\n#   * Array\n#   * Hash where keys must be Strings\n#   * QualifiedName\n#\n# Not considered literal:\n#\n#   * QualifiedReference  # i.e. File, FooBar\n#   * Default is not accepted as being literal\n#   * Regular Expression is not accepted as being literal\n#   * Hash with non String keys\n#   * String with interpolation\n#\nclass Puppet::Pops::Evaluator::JsonStrictLiteralEvaluator\n  # include Puppet::Pops::Utils\n\n  COMMA_SEPARATOR = ', '\n\n  def initialize\n    @@literal_visitor ||= Puppet::Pops::Visitor.new(self, \"literal\", 0, 0)\n  end\n\n  def literal(ast)\n    @@literal_visitor.visit_this_0(self, ast)\n  end\n\n  def literal_Object(o)\n    throw :not_literal\n  end\n\n  def literal_Factory(o)\n    literal(o.model)\n  end\n\n  def literal_Program(o)\n    literal(o.body)\n  end\n\n  def literal_LiteralString(o)\n    o.value\n  end\n\n  def literal_QualifiedName(o)\n    o.value\n  end\n\n  def literal_LiteralNumber(o)\n    o.value\n  end\n\n  def literal_LiteralBoolean(o)\n    o.value\n  end\n\n  def literal_LiteralUndef(o)\n    nil\n  end\n\n  def literal_ConcatenatedString(o)\n    # use double quoted string value if there is no interpolation\n    throw :not_literal unless o.segments.size == 1 && o.segments[0].is_a?(Puppet::Pops::Model::LiteralString)\n    o.segments[0].value\n  end\n\n  def literal_LiteralList(o)\n    o.values.map { |v| literal(v) }\n  end\n\n  def literal_LiteralHash(o)\n    o.entries.each_with_object({}) do |entry, result|\n      key = literal(entry.key)\n      throw :not_literal unless key.is_a?(String)\n      result[key] = literal(entry.value)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/literal_evaluator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n# Literal values for\n# String (not containing interpolation)\n# Numbers\n# Booleans\n# Undef (produces nil)\n# Array\n# Hash\n# QualifiedName\n# Default (produced :default)\n# Regular Expression (produces ruby regular expression)\n# QualifiedReference\n# AccessExpresion\n#\nclass LiteralEvaluator\n  COMMA_SEPARATOR = ', '\n\n  def initialize\n    @@literal_visitor ||= Visitor.new(self, \"literal\", 0, 0)\n  end\n\n  def literal(ast)\n    @@literal_visitor.visit_this_0(self, ast)\n  end\n\n  def literal_Object(o)\n    throw :not_literal\n  end\n\n  def literal_Factory(o)\n    literal(o.model)\n  end\n\n  def literal_Program(o)\n    literal(o.body)\n  end\n\n  def literal_LiteralString(o)\n    o.value\n  end\n\n  def literal_QualifiedName(o)\n    o.value\n  end\n\n  def literal_LiteralNumber(o)\n    o.value\n  end\n\n  def literal_LiteralBoolean(o)\n    o.value\n  end\n\n  def literal_LiteralUndef(o)\n    nil\n  end\n\n  def literal_LiteralDefault(o)\n    :default\n  end\n\n  def literal_LiteralRegularExpression(o)\n    o.value\n  end\n\n  def literal_QualifiedReference(o)\n    o.value\n  end\n\n  def literal_AccessExpression(o)\n    # to prevent parameters with [[]] like Optional[[String]]\n    throw :not_literal if o.keys.size == 1 && o.keys[0].is_a?(Model::LiteralList)\n    o.keys.map { |v| literal(v) }\n  end\n\n  def literal_UnaryMinusExpression(o)\n    -literal(o.expr)\n  end\n\n  def literal_ConcatenatedString(o)\n    # use double quoted string value if there is no interpolation\n    throw :not_literal unless o.segments.size == 1 && o.segments[0].is_a?(Model::LiteralString)\n    o.segments[0].value\n  end\n\n  def literal_LiteralList(o)\n    o.values.map { |v| literal(v) }\n  end\n\n  def literal_LiteralHash(o)\n    o.entries.each_with_object({}) do |entry, result|\n      result[literal(entry.key)] = literal(entry.value)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/puppet_proc.rb",
    "content": "# frozen_string_literal: true\n\n# Complies with Proc API by mapping a Puppet::Pops::Evaluator::Closure to a ruby Proc.\n# Creating and passing an instance of this class instead of just a plain block makes\n# it possible to inherit the parameter info and arity from the closure. Advanced users\n# may also access the closure itself. The Puppet::Pops::Functions::Dispatcher uses this\n# when it needs to get the Callable type of the closure.\n#\n# The class is part of the Puppet Function API for Ruby and thus public API but a user\n# should never create an instance of this class.\n#\n# @api public\nclass Puppet::Pops::Evaluator::PuppetProc < Proc\n  # Creates a new instance from a closure and a block that will dispatch\n  # all parameters to the closure. The block must be similar to:\n  #\n  #   { |*args| closure.call(*args) }\n  #\n  # @param closure [Puppet::Pops::Evaluator::Closure] The closure to map\n  # @param &block [Block] The varargs block that invokes the closure.call method\n  #\n  # @api private\n  def self.new(closure, &block)\n    proc = super(&block)\n    proc.instance_variable_set(:@closure, closure)\n    proc\n  end\n\n  # @return  [Puppet::Pops::Evaluator::Closure] the mapped closure\n  # @api public\n  attr_reader :closure\n\n  # @overrides Block.lambda?\n  # @return [Boolean] always false since this proc doesn't do the Ruby lambda magic\n  # @api public\n  def lambda?\n    false\n  end\n\n  # Maps the closure parameters to standard Block parameter info where each\n  # parameter is represented as a two element Array where the first\n  # element is :req, :opt, or :rest and the second element is the name\n  # of the parameter.\n  #\n  # @return [Array<Array<Symbol>>] array of parameter info pairs\n  # @overrides Block.parameters\n  # @api public\n  def parameters\n    @closure.parameters.map do |param|\n      sym = param.name.to_sym\n      if param.captures_rest\n        [:rest, sym]\n      elsif param.value\n        [:opt, sym]\n      else\n        [:req, sym]\n      end\n    end\n  end\n\n  # @return [Integer] the arity of the block\n  # @overrides Block.arity\n  # @api public\n  def arity\n    parameters.reduce(0) do |memo, param|\n      count = memo + 1\n      break -count unless param[0] == :req\n\n      count\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/relationship_operator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n# The RelationshipOperator implements the semantics of the -> <- ~> <~ operators creating relationships or notification\n# relationships between the left and right hand side's references to resources.\n#\n# This is separate class since a second level of evaluation is required that transforms string in left or right hand\n# to type references. The task of \"making a relationship\" is delegated to the \"runtime support\" class that is included.\n# This is done to separate the concerns of the new evaluator from the 3x runtime; messy logic goes into the runtime support\n# module. Later when more is cleaned up this can be simplified further.\n#\nclass RelationshipOperator\n  # Provides access to the Puppet 3.x runtime (scope, etc.)\n  # This separation has been made to make it easier to later migrate the evaluator to an improved runtime.\n  #\n  include Runtime3Support\n\n  class IllegalRelationshipOperandError < RuntimeError\n    attr_reader :operand\n\n    def initialize operand\n      @operand = operand\n    end\n  end\n\n  class NotCatalogTypeError < RuntimeError\n    attr_reader :type\n\n    def initialize type\n      @type = type\n    end\n  end\n\n  def initialize\n    @type_transformer_visitor = Visitor.new(self, \"transform\", 1, 1)\n    @type_calculator = Types::TypeCalculator.new()\n\n    tf = Types::TypeFactory\n    @catalog_type = tf.variant(tf.catalog_entry, tf.type_type(tf.catalog_entry))\n  end\n\n  def transform(o, scope)\n    @type_transformer_visitor.visit_this_1(self, o, scope)\n  end\n\n  # Catch all non transformable objects\n  # @api private\n  def transform_Object(o, scope)\n    raise IllegalRelationshipOperandError, o\n  end\n\n  # A Resource is by definition a Catalog type, but of 3.x type\n  # @api private\n  def transform_Resource(o, scope)\n    Types::TypeFactory.resource(o.type, o.title)\n  end\n\n  # A string must be a type reference in string format\n  # @api private\n  def transform_String(o, scope)\n    assert_catalog_type(Types::TypeParser.singleton.parse(o), scope)\n  end\n\n  # A qualified name is short hand for a class with this name\n  # @api private\n  def transform_QualifiedName(o, scope)\n    Types::TypeFactory.host_class(o.value)\n  end\n\n  # Types are what they are, just check the type\n  # @api private\n  def transform_PAnyType(o, scope)\n    assert_catalog_type(o, scope)\n  end\n\n  # This transforms a 3x Collector (the result of evaluating a 3x AST::Collection).\n  # It is passed through verbatim since it is evaluated late by the compiler. At the point\n  # where the relationship is evaluated, it is simply recorded with the compiler for later evaluation.\n  # If one of the sides of the relationship is a Collector it is evaluated before the actual\n  # relationship is formed. (All of this happens at a later point in time.\n  #\n  def transform_Collector(o, scope)\n    o\n  end\n\n  def transform_AbstractCollector(o, scope)\n    o\n  end\n\n  # Array content needs to be transformed\n  def transform_Array(o, scope)\n    o.map { |x| transform(x, scope) }\n  end\n\n  # Asserts (and returns) the type if it is a PCatalogEntryType\n  # (A PCatalogEntryType is the base class of PClassType, and PResourceType).\n  #\n  def assert_catalog_type(o, scope)\n    unless @type_calculator.assignable?(@catalog_type, o)\n      raise NotCatalogTypeError, o\n    end\n\n    # TODO must check if this is an abstract PResourceType (i.e. without a type_name) - which should fail ?\n    # e.g. File -> File (and other similar constructs) - maybe the catalog protects against this since references\n    # may be to future objects...\n    o\n  end\n\n  RELATIONSHIP_OPERATORS = ['->', '~>', '<-', '<~'].freeze\n  REVERSE_OPERATORS      = ['<-', '<~'].freeze\n  RELATION_TYPE = {\n    '->' => :relationship,\n    '<-' => :relationship,\n    '~>' => :subscription,\n    '<~' => :subscription\n  }.freeze\n\n  # Evaluate a relationship.\n  # TODO: The error reporting is not fine grained since evaluation has already taken place\n  # There is no references to the original source expressions at this point, only the overall\n  # relationship expression. (e.g.. the expression may be ['string', func_call(), etc.] -> func_call())\n  # To implement this, the general evaluator needs to be able to track each evaluation result and associate\n  # it with a corresponding expression. This structure should then be passed to the relationship operator.\n  #\n  def evaluate(left_right_evaluated, relationship_expression, scope)\n    # assert operator (should have been validated, but this logic makes assumptions which would\n    # screw things up royally). Better safe than sorry.\n    unless RELATIONSHIP_OPERATORS.include?(relationship_expression.operator)\n      fail(Issues::UNSUPPORTED_OPERATOR, relationship_expression, { :operator => relationship_expression.operator })\n    end\n\n    begin\n      # Turn each side into an array of types (this also asserts their type)\n      # (note wrap in array first if value is not already an array)\n      #\n      # TODO: Later when objects are Puppet Runtime Objects and know their type, it will be more efficient to check/infer\n      # the type first since a chained operation then does not have to visit each element again. This is not meaningful now\n      # since inference needs to visit each object each time, and this is what the transformation does anyway).\n      #\n      # real is [left, right], and both the left and right may be a single value or an array. In each case all content\n      # should be flattened, and then transformed to a type. left or right may also be a value that is transformed\n      # into an array, and thus the resulting left and right must be flattened individually\n      # Once flattened, the operands should be sets (to remove duplicate entries)\n      #\n      real = left_right_evaluated.collect { |x| [x].flatten.collect { |y| transform(y, scope) } }\n      real[0].flatten!\n      real[1].flatten!\n      real[0].uniq!\n      real[1].uniq!\n\n      # reverse order if operator is Right to Left\n      source, target = reverse_operator?(relationship_expression) ? real.reverse : real\n\n      # Add the relationships to the catalog\n      source.each { |s| target.each { |t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator], scope) } }\n\n      # The result is the transformed source RHS unless it is empty, in which case the transformed LHS is returned.\n      # This closes the gap created by an empty set of references in a chain of relationship\n      # such that X -> [ ] -> Y results in  X -> Y.\n      # result = real[1].empty? ? real[0] : real[1]\n      if real[1].empty?\n        # right side empty, simply use the left (whatever it may be)\n        result = real[0]\n      else\n        right = real[1]\n        if right.size == 1 && right[0].is_a?(Puppet::Pops::Evaluator::Collectors::AbstractCollector)\n          # the collector when evaluated later may result in an empty set, if so, the\n          # lazy relationship forming logic needs to have access to the left value.\n          adapter = Puppet::Pops::Adapters::EmptyAlternativeAdapter.adapt(right[0])\n          adapter.empty_alternative = real[0]\n        end\n        result = right\n      end\n      result\n    rescue NotCatalogTypeError => e\n      fail(Issues::NOT_CATALOG_TYPE, relationship_expression, { :type => @type_calculator.string(e.type) })\n    rescue IllegalRelationshipOperandError => e\n      fail(Issues::ILLEGAL_RELATIONSHIP_OPERAND_TYPE, relationship_expression, { :operand => e.operand })\n    end\n  end\n\n  def reverse_operator?(o)\n    REVERSE_OPERATORS.include?(o.operator)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/runtime3_converter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Evaluator\n# Converts nested 4x supported values to 3x values. This is required because\n# resources and other objects do not know about the new type system, and does not support\n# regular expressions. Unfortunately this has to be done for array and hash as well.\n# A complication is that catalog types needs to be resolved against the scope.\n#\n# Users should not create instances of this class. Instead the class methods {Runtime3Converter.convert},\n# {Runtime3Converter.map_args}, or {Runtime3Converter.instance} should be used\nclass Runtime3Converter\n  MAX_INTEGER = Puppet::Pops::MAX_INTEGER\n  MIN_INTEGER = Puppet::Pops::MIN_INTEGER\n\n  # Converts 4x supported values to a 3x values. Same as calling Runtime3Converter.instance.map_args(...)\n  #\n  # @param args [Array] Array of values to convert\n  # @param scope [Puppet::Parser::Scope] The scope to use when converting\n  # @param undef_value [Object] The value that nil is converted to\n  # @return [Array] The converted values\n  #\n  def self.map_args(args, scope, undef_value)\n    @instance.map_args(args, scope, undef_value)\n  end\n\n  # Converts 4x supported values to a 3x values. Same as calling Runtime3Converter.instance.convert(...)\n  #\n  # @param o [Object]The value to convert\n  # @param scope [Puppet::Parser::Scope] The scope to use when converting\n  # @param undef_value [Object] The value that nil is converted to\n  # @return [Object] The converted value\n  #\n  def self.convert(o, scope, undef_value)\n    @instance.convert(o, scope, undef_value)\n  end\n\n  # Returns the singleton instance of this class.\n  # @return [Runtime3Converter] The singleton instance\n  def self.instance\n    @instance\n  end\n\n  # Converts 4x supported values to a 3x values.\n  #\n  # @param args [Array] Array of values to convert\n  # @param scope [Puppet::Parser::Scope] The scope to use when converting\n  # @param undef_value [Object] The value that nil is converted to\n  # @return [Array] The converted values\n  #\n  def map_args(args, scope, undef_value)\n    args.map { |a| convert(a, scope, undef_value) }\n  end\n\n  # Converts a 4x supported value to a 3x value.\n  #\n  # @param o [Object]The value to convert\n  # @param scope [Puppet::Parser::Scope] The scope to use when converting\n  # @param undef_value [Object] The value that nil is converted to\n  # @return [Object] The converted value\n  #\n  def convert(o, scope, undef_value)\n    @convert_visitor.visit_this_2(self, o, scope, undef_value)\n  end\n\n  def convert_NilClass(o, scope, undef_value)\n    @inner ? nil : undef_value\n  end\n\n  def convert_Integer(o, scope, undef_value)\n    return o unless o < MIN_INTEGER || o > MAX_INTEGER\n\n    range_end = o > MAX_INTEGER ? 'max' : 'min'\n    raise Puppet::Error, \"Use of a Ruby Integer outside of Puppet Integer #{range_end} range, got '#{'0x%x' % o}'\"\n  end\n\n  def convert_BigDecimal(o, scope, undef_value)\n    # transform to same value float value if possible without any rounding error\n    f = o.to_f\n    return f unless f != o\n\n    raise Puppet::Error, \"Use of a Ruby BigDecimal value outside Puppet Float range, got '#{o}'\"\n  end\n\n  def convert_String(o, scope, undef_value)\n    # Although wasteful, a dup is needed because user code may mutate these strings when applying\n    # Resources. This does not happen when in server mode since it only uses Resources that are\n    # in puppet core and those are all safe.\n    o.frozen? && !Puppet.run_mode.server? ? o.dup : o\n  end\n\n  def convert_Object(o, scope, undef_value)\n    o\n  end\n\n  def convert_Array(o, scope, undef_value)\n    ic = @inner_converter\n    o.map { |x| ic.convert(x, scope, undef_value) }\n  end\n\n  def convert_Hash(o, scope, undef_value)\n    result = {}\n    ic = @inner_converter\n    o.each { |k, v| result[ic.convert(k, scope, undef_value)] = ic.convert(v, scope, undef_value) }\n    result\n  end\n\n  def convert_Iterator(o, scope, undef_value)\n    raise Puppet::Error, _('Use of an Iterator is not supported here')\n  end\n\n  def convert_Symbol(o, scope, undef_value)\n    return o unless o == :undef\n\n    !@inner ? undef_value : nil\n  end\n\n  def convert_PAnyType(o, scope, undef_value)\n    o\n  end\n\n  def convert_PCatalogEntryType(o, scope, undef_value)\n    # Since 4x does not support dynamic scoping, all names are absolute and can be\n    # used as is (with some check/transformation/mangling between absolute/relative form\n    # due to Puppet::Resource's idiosyncratic behavior where some references must be\n    # absolute and others cannot be.\n    # Thus there is no need to call scope.resolve_type_and_titles to do dynamic lookup.\n    t, title = catalog_type_to_split_type_title(o)\n    t = Runtime3ResourceSupport.find_resource_type(scope, t) unless t == 'class' || t == 'node'\n    Puppet::Resource.new(t, title)\n  end\n\n  # Produces an array with [type, title] from a PCatalogEntryType\n  # This method is used to produce the arguments for creation of reference resource instances\n  # (used when 3x is operating on a resource).\n  # Ensures that resources are *not* absolute.\n  #\n  def catalog_type_to_split_type_title(catalog_type)\n    split_type = catalog_type.is_a?(Puppet::Pops::Types::PTypeType) ? catalog_type.type : catalog_type\n    case split_type\n    when Puppet::Pops::Types::PClassType\n      class_name = split_type.class_name\n      ['class', class_name.nil? ? nil : class_name.sub(/^::/, '')]\n    when Puppet::Pops::Types::PResourceType\n      type_name = split_type.type_name\n      title = split_type.title\n      if type_name =~ /^(::)?[Cc]lass$/\n        ['class', title.nil? ? nil : title.sub(/^::/, '')]\n      else\n        # Ensure that title is '' if nil\n        # Resources with absolute name always results in error because tagging does not support leading ::\n        [type_name.nil? ? nil : type_name.sub(/^::/, '').downcase, title.nil? ? '' : title]\n      end\n    else\n      # TRANSLATORS 'PClassType' and 'PResourceType' are Puppet types and should not be translated\n      raise ArgumentError, _(\"Cannot split the type %{class_name}, it represents neither a PClassType, nor a PResourceType.\") %\n                           { class_name: catalog_type.class }\n    end\n  end\n\n  protected\n\n  def initialize(inner = false)\n    @inner = inner\n    @inner_converter = inner ? self : self.class.new(true)\n    @convert_visitor = Puppet::Pops::Visitor.new(self, 'convert', 2, 2)\n  end\n\n  @instance = new\nend\n\n# A Ruby function written for the 3.x API cannot be expected to handle extended data types. This\n# converter ensures that they are converted to String format\n# @api private\nclass Runtime3FunctionArgumentConverter < Runtime3Converter\n  def convert_Regexp(o, scope, undef_value)\n    # Puppet 3x cannot handle parameter values that are regular expressions. Turn into regexp string in\n    # source form\n    o.inspect\n  end\n\n  def convert_Version(o, scope, undef_value)\n    # Puppet 3x cannot handle SemVers. Use the string form\n    o.to_s\n  end\n\n  def convert_VersionRange(o, scope, undef_value)\n    # Puppet 3x cannot handle SemVerRanges. Use the string form\n    o.to_s\n  end\n\n  def convert_Binary(o, scope, undef_value)\n    # Puppet 3x cannot handle Binary. Use the string form\n    o.to_s\n  end\n\n  def convert_Timespan(o, scope, undef_value)\n    # Puppet 3x cannot handle Timespans. Use the string form\n    o.to_s\n  end\n\n  def convert_Timestamp(o, scope, undef_value)\n    # Puppet 3x cannot handle Timestamps. Use the string form\n    o.to_s\n  end\n\n  # Converts result back to 4.x by replacing :undef with nil in Array and Hash objects\n  #\n  def self.convert_return(val3x)\n    case val3x\n    when :undef\n      nil\n    when Array\n      val3x.map { |v| convert_return(v) }\n    when Hash\n      hsh = {}\n      val3x.each_pair { |k, v| hsh[convert_return(k)] = convert_return(v) }\n      hsh\n    else\n      val3x\n    end\n  end\n\n  @instance = new\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/runtime3_resource_support.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n# @api private\nmodule Runtime3ResourceSupport\n  CLASS_STRING = 'class'\n\n  def self.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)\n    env = scope.environment\n    #    loader = Adapters::LoaderAdapter.loader_for_model_object(o, scope)\n\n    if type_name.is_a?(String) && type_name.casecmp(CLASS_STRING) == 0\n      # Resolve a 'class' and its titles\n      resource_titles = resource_titles.collect do |a_title|\n        hostclass = env.known_resource_types.find_hostclass(a_title)\n        hostclass ? hostclass.name : a_title\n      end\n      # resolved type is just the string CLASS\n      resolved_type = CLASS_STRING\n    else\n      # resolve a resource type - pcore based, ruby impl or user defined\n      resolved_type = find_resource_type(scope, type_name)\n    end\n\n    # TODO: Unknown resource causes creation of Resource to fail with ArgumentError, should give\n    # a proper Issue. Now the result is \"Error while evaluating a Resource Statement\" with the message\n    # from the raised exception. (It may be good enough).\n    unless resolved_type\n      # TODO: do this the right way\n      raise ArgumentError, _(\"Unknown resource type: '%{type}'\") % { type: type_name }\n    end\n\n    # Build a resource for each title - use the resolved *type* as opposed to a reference\n    # as this makes the created resource retain the type instance.\n    #\n    resource_titles.map do |resource_title|\n      resource = Puppet::Parser::Resource.new(\n        resolved_type, resource_title,\n        :parameters => evaluated_parameters,\n        :file => file,\n        :line => line,\n        :kind => Puppet::Resource.to_kind(resolved_type),\n        :exported => exported,\n        :virtual => virtual,\n        # WTF is this? Which source is this? The file? The name of the context ?\n        :source => scope.source,\n        :scope => scope,\n        :strict => true\n      )\n\n      # If this resource type supports inheritance (e.g. 'class') the parent chain must be walked\n      # This impl delegates to the resource type to figure out what is needed.\n      #\n      if resource.resource_type.is_a? Puppet::Resource::Type\n        resource.resource_type.instantiate_resource(scope, resource)\n      end\n\n      scope.compiler.add_resource(scope, resource)\n\n      # Classes are evaluated immediately\n      scope.compiler.evaluate_classes([resource_title], scope, false) if resolved_type == CLASS_STRING\n\n      # Turn the resource into a PTypeType (a reference to a resource type)\n      # weed out nil's\n      resource_to_ptype(resource)\n    end\n  end\n\n  def self.find_resource_type(scope, type_name)\n    find_builtin_resource_type(scope, type_name) || find_defined_resource_type(scope, type_name)\n  end\n\n  def self.find_resource_type_or_class(scope, name)\n    find_builtin_resource_type(scope, name) || find_defined_resource_type(scope, name) || find_hostclass(scope, name)\n  end\n\n  def self.resource_to_ptype(resource)\n    return nil if resource.nil?\n\n    # inference returns the meta type since the 3x Resource is an alternate way to describe a type\n    Puppet::Pops::Types::TypeCalculator.singleton().infer(resource).type\n  end\n\n  def self.find_main_class(scope)\n    # Find the main class (known as ''), it does not have to be in the catalog\n    scope.environment.known_resource_types.find_hostclass('')\n  end\n\n  def self.find_hostclass(scope, class_name)\n    scope.environment.known_resource_types.find_hostclass(class_name)\n  end\n\n  def self.find_builtin_resource_type(scope, type_name)\n    if type_name.include?(':')\n      # Skip the search for built in types as they are always in global namespace\n      # (At least for now).\n      return nil\n    end\n\n    loader = scope.compiler.loaders.private_environment_loader\n    loaded = loader.load(:resource_type_pp, type_name)\n    if loaded\n      return loaded\n    end\n\n    # horrible - should be loaded by a \"last loader\" in 4.x loaders instead.\n    Puppet::Type.type(type_name)\n  end\n  private_class_method :find_builtin_resource_type\n\n  def self.find_defined_resource_type(scope, type_name)\n    krt = scope.environment.known_resource_types\n    krt.find_definition(type_name)\n  end\n  private_class_method :find_defined_resource_type\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/evaluator/runtime3_support.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Evaluator\n# A module with bindings between the new evaluator and the 3x runtime.\n# The intention is to separate all calls into scope, compiler, resource, etc. in this module\n# to make it easier to later refactor the evaluator for better implementations of the 3x classes.\n#\n# @api private\nmodule Runtime3Support\n  NAME_SPACE_SEPARATOR = '::'\n\n  # Fails the evaluation of _semantic_ with a given issue.\n  #\n  # @param issue [Issue] the issue to report\n  # @param semantic [ModelPopsObject] the object for which evaluation failed in some way. Used to determine origin.\n  # @param options [Hash] hash of optional named data elements for the given issue\n  # @return [!] this method does not return\n  # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError?)\n  #\n  def fail(issue, semantic, options = {}, except = nil)\n    optionally_fail(issue, semantic, options, except)\n    # an error should have been raised since fail always fails\n    raise ArgumentError, _(\"Internal Error: Configuration of runtime error handling wrong: should have raised exception\")\n  end\n\n  # Optionally (based on severity) Fails the evaluation of _semantic_ with a given issue\n  # If the given issue is configured to be of severity < :error it is only reported, and the function returns.\n  #\n  # @param issue [Issue] the issue to report\n  # @param semantic [ModelPopsObject] the object for which evaluation failed in some way. Used to determine origin.\n  # @param options [Hash] hash of optional named data elements for the given issue\n  # @return [!] this method does not return\n  # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError?)\n  #\n  def optionally_fail(issue, semantic, options = {}, except = nil)\n    if except.nil? && diagnostic_producer.severity_producer[issue] == :error\n      # Want a stacktrace, and it must be passed as an exception\n      begin\n        raise EvaluationError\n      rescue EvaluationError => e\n        except = e\n      end\n    end\n    diagnostic_producer.accept(issue, semantic, options, except)\n  end\n\n  # Optionally (based on severity) Fails the evaluation with a given issue\n  # If the given issue is configured to be of severity < :error it is only reported, and the function returns.\n  # The location the issue is reported against is found is based on the top file/line in the puppet call stack\n  #\n  # @param issue [Issue] the issue to report\n  # @param options [Hash] hash of optional named data elements for the given issue\n  # @return [!] this method may not return, nil if it does\n  # @raise [Puppet::ParseError] an evaluation error initialized from the arguments\n  #\n  def runtime_issue(issue, options = {})\n    # Get position from puppet runtime stack\n    file, line = Puppet::Pops::PuppetStack.top_of_stack\n\n    # Use a SemanticError as the sourcepos\n    semantic = Puppet::Pops::SemanticError.new(issue, nil, options.merge({ :file => file, :line => line }))\n    optionally_fail(issue, semantic)\n    nil\n  end\n\n  # Binds the given variable name to the given value in the given scope.\n  # The reference object `o` is intended to be used for origin information - the 3x scope implementation\n  # only makes use of location when there is an error. This is now handled by other mechanisms; first a check\n  # is made if a variable exists and an error is raised if attempting to change an immutable value. Errors\n  # in name, numeric variable assignment etc. have also been validated prior to this call. In the event the\n  # scope.setvar still raises an error, the general exception handling for evaluation of the assignment\n  # expression knows about its location. Because of this, there is no need to extract the location for each\n  # setting (extraction is somewhat expensive since 3x requires line instead of offset).\n  #\n  def set_variable(name, value, o, scope)\n    # Scope also checks this but requires that location information are passed as options.\n    # Those are expensive to calculate and a test is instead made here to enable failing with better information.\n    # The error is not specific enough to allow catching it - need to check the actual message text.\n    # TODO: Improve the messy implementation in Scope.\n    #\n    if name == \"server_facts\"\n      fail(Issues::ILLEGAL_RESERVED_ASSIGNMENT, o, { :name => name })\n    end\n\n    if scope.bound?(name)\n      if Puppet::Parser::Scope::RESERVED_VARIABLE_NAMES.include?(name)\n        fail(Issues::ILLEGAL_RESERVED_ASSIGNMENT, o, { :name => name })\n      else\n        fail(Issues::ILLEGAL_REASSIGNMENT, o, { :name => name })\n      end\n    end\n    scope.setvar(name, value)\n  end\n\n  # Returns the value of the variable (nil is returned if variable has no value, or if variable does not exist)\n  #\n  def get_variable_value(name, o, scope)\n    # Puppet 3x stores all variables as strings (then converts them back to numeric with a regexp... to see if it is a match variable)\n    # Not ideal, scope should support numeric lookup directly instead.\n    # TODO: consider fixing scope\n    catch(:undefined_variable) {\n      x = scope.lookupvar(name.to_s)\n      # Must convert :undef back to nil - this can happen when an undefined variable is used in a\n      # parameter's default value expression - there nil must be :undef to work with the rest of 3x.\n      # Now that the value comes back to 4x it is changed to nil.\n      return :undef.equal?(x) ? nil : x\n    }\n    # It is always ok to reference numeric variables even if they are not assigned. They are always undef\n    # if not set by a match expression.\n    #\n    unless name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME\n      optionally_fail(Puppet::Pops::Issues::UNKNOWN_VARIABLE, o, { :name => name })\n    end\n    nil # in case unknown variable is configured as a warning or ignore\n  end\n\n  # Returns true if the variable of the given name is set in the given most nested scope. True is returned even if\n  # variable is bound to nil.\n  #\n  def variable_bound?(name, scope)\n    scope.bound?(name.to_s)\n  end\n\n  # Returns true if the variable is bound to a value or nil, in the scope or it's parent scopes.\n  #\n  def variable_exists?(name, scope)\n    scope.exist?(name.to_s)\n  end\n\n  def set_match_data(match_data, scope)\n    # See set_variable for rationale for not passing file and line to ephemeral_from.\n    # NOTE: The 3x scope adds one ephemeral(match) to its internal stack per match that succeeds ! It never\n    # clears anything. Thus a context that performs many matches will get very deep (there simply is no way to\n    # clear the match variables without rolling back the ephemeral stack.)\n    # This implementation does not attempt to fix this, it behaves the same bad way.\n    unless match_data.nil?\n      scope.ephemeral_from(match_data)\n    end\n  end\n\n  # Creates a local scope with vairalbes set from a hash of variable name to value\n  #\n  def create_local_scope_from(hash, scope)\n    # two dummy values are needed since the scope tries to give an error message (can not happen in this\n    # case - it is just wrong, the error should be reported by the caller who knows in more detail where it\n    # is in the source.\n    #\n    raise ArgumentError, _(\"Internal error - attempt to create a local scope without a hash\") unless hash.is_a?(Hash)\n\n    scope.ephemeral_from(hash)\n  end\n\n  # Creates a nested match scope\n  def create_match_scope_from(scope)\n    # Create a transparent match scope (for future matches)\n    scope.new_match_scope(nil)\n  end\n\n  def get_scope_nesting_level(scope)\n    scope.ephemeral_level\n  end\n\n  def set_scope_nesting_level(scope, level)\n    # 3x uses this method to reset the level,\n    scope.pop_ephemerals(level)\n  end\n\n  # Adds a relationship between the given `source` and `target` of the given `relationship_type`\n  # @param source [Puppet:Pops::Types::PCatalogEntryType] the source end of the relationship (from)\n  # @param target [Puppet:Pops::Types::PCatalogEntryType] the target end of the relationship (to)\n  # @param relationship_type [:relationship, :subscription] the type of the relationship\n  #\n  def add_relationship(source, target, relationship_type, scope)\n    # The 3x way is to record a Puppet::Parser::Relationship that is evaluated at the end of the compilation.\n    # This means it is not possible to detect any duplicates at this point (and signal where an attempt is made to\n    # add a duplicate. There is also no location information to signal the original place in the logic. The user will have\n    # to go fish.\n    # The 3.x implementation is based on Strings :-o, so the source and target must be transformed. The resolution is\n    # done by Catalog#resource(type, title). To do that, it creates a Puppet::Resource since it is responsible for\n    # translating the name/type/title and create index-keys used by the catalog. The Puppet::Resource has bizarre parsing of\n    # the type and title (scan for [] that is interpreted as type/title (but it gets it wrong).\n    # Moreover if the type is \"\" or \"component\", the type is Class, and if the type is :main, it is :main, all other cases\n    # undergo capitalization of name-segments (foo::bar becomes Foo::Bar). (This was earlier done in the reverse by the parser).\n    # Further, the title undergoes the same munging !!!\n    #\n    # That bug infested nest of messy logic needs serious Exorcism!\n    #\n    # Unfortunately it is not easy to simply call more intelligent methods at a lower level as the compiler evaluates the recorded\n    # Relationship object at a much later point, and it is responsible for invoking all the messy logic.\n    #\n    # TODO: Revisit the below logic when there is a sane implementation of the catalog, compiler and resource. For now\n    # concentrate on transforming the type references to what is expected by the wacky logic.\n    #\n    # HOWEVER, the Compiler only records the Relationships, and the only method it calls is @relationships.each{|x| x.evaluate(catalog) }\n    # Which means a smarter Relationship class could do this right. Instead of obtaining the resource from the catalog using\n    # the borked resource(type, title) which creates a resource for the purpose of looking it up, it needs to instead\n    # scan the catalog's resources\n    #\n    # GAAAH, it is even worse!\n    # It starts in the parser, which parses \"File['foo']\" into an AST::ResourceReference with type = File, and title = foo\n    # This AST is evaluated by looking up the type/title in the scope - causing it to be loaded if it exists, and if not, the given\n    # type name/title is used. It does not search for resource instances, only classes and types. It returns symbolic information\n    # [type, [title, title]]. From this, instances of Puppet::Resource are created and returned. These only have type/title information\n    # filled out. One or an array of resources are returned.\n    # This set of evaluated (empty reference) Resource instances are then passed to the relationship operator. It creates a\n    # Puppet::Parser::Relationship giving it a source and a target that are (empty reference) Resource instances. These are then remembered\n    # until the relationship is evaluated by the compiler (at the end). When evaluation takes place, the (empty reference) Resource instances\n    # are converted to String (!?! WTF) on the simple format \"#{type}[#{title}]\", and the catalog is told to find a resource, by giving\n    # it this string. If it cannot find the resource it fails, else the before/notify parameter is appended with the target.\n    # The search for the resource begin with (you guessed it) again creating an (empty reference) resource from type and title (WTF?!?!).\n    # The catalog now uses the reference resource to compute a key [r.type, r.title.to_s] and also gets a uniqueness key from the\n    # resource (This is only a reference type created from title and type). If it cannot find it with the first key, it uses the\n    # uniqueness key to lookup.\n    #\n    # This is probably done to allow a resource type to munge/translate the title in some way (but it is quite unclear from the long\n    # and convoluted path of evaluation.\n    # In order to do this in a way that is similar to 3.x two resources are created to be used as keys.\n    #\n    # And if that is not enough, a source/target may be a Collector (a baked query that will be evaluated by the\n    # compiler - it is simply passed through here for processing by the compiler at the right time).\n    #\n    if source.is_a?(Collectors::AbstractCollector)\n      # use verbatim - behavior defined by 3x\n      source_resource = source\n    else\n      # transform into the wonderful String representation in 3x\n      type, title = Runtime3Converter.instance.catalog_type_to_split_type_title(source)\n      type = Runtime3ResourceSupport.find_resource_type(scope, type) unless type == 'class' || type == 'node'\n      source_resource = Puppet::Resource.new(type, title)\n    end\n    if target.is_a?(Collectors::AbstractCollector)\n      # use verbatim - behavior defined by 3x\n      target_resource = target\n    else\n      # transform into the wonderful String representation in 3x\n      type, title = Runtime3Converter.instance.catalog_type_to_split_type_title(target)\n      type = Runtime3ResourceSupport.find_resource_type(scope, type) unless type == 'class' || type == 'node'\n      target_resource = Puppet::Resource.new(type, title)\n    end\n    # Add the relationship to the compiler for later evaluation.\n    scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, relationship_type))\n  end\n\n  # Coerce value `v` to numeric or fails.\n  # The given value `v` is coerced to Numeric, and if that fails the operation\n  # calls {#fail}.\n  # @param v [Object] the value to convert\n  # @param o [Object] originating instruction\n  # @param scope [Object] the (runtime specific) scope where evaluation of o takes place\n  # @return [Numeric] value `v` converted to Numeric.\n  #\n  def coerce_numeric(v, o, scope)\n    if v.is_a?(Numeric)\n      return v\n    end\n\n    n = Utils.to_n(v)\n    unless n\n      fail(Issues::NOT_NUMERIC, o, { :value => v })\n    end\n\n    # this point is reached if there was a conversion\n    optionally_fail(Issues::NUMERIC_COERCION, o, { :before => v, :after => n })\n    n\n  end\n\n  # Provides the ability to call a 3.x or 4.x function from the perspective of a 3.x function or ERB template.\n  # The arguments to the function must be an Array containing values compliant with the 4.x calling convention.\n  # If the targeted function is a 3.x function, the values will be transformed.\n  # @param name [String] the name of the function (without the 'function_' prefix used by scope)\n  # @param args [Array] arguments, may be empty\n  # @param scope [Object] the (runtime specific) scope where evaluation takes place\n  # @raise [ArgumentError] 'unknown function' if the function does not exist\n  #\n  def external_call_function(name, args, scope, &block)\n    # Call via 4x API if the function exists there\n    loaders = scope.compiler.loaders\n    # Since this is a call from non puppet code, it is not possible to relate it to a module loader\n    # It is known where the call originates by using the scope associated module - but this is the calling scope\n    # and it does not defined the visibility of functions from a ruby function's perspective. Instead,\n    # this is done from the perspective of the environment.\n    loader = loaders.private_environment_loader\n    func = loader.load(:function, name) if loader\n    if func\n      Puppet::Util::Profiler.profile(name, [:functions, name]) do\n        return func.call(scope, *args, &block)\n      end\n    end\n\n    # Call via 3x API if function exists there\n    raise ArgumentError, _(\"Unknown function '%{name}'\") % { name: name } unless Puppet::Parser::Functions.function(name)\n\n    # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x.\n    # NOTE: Passing an empty string last converts nil/:undef to empty string\n    mapped_args = Runtime3FunctionArgumentConverter.map_args(args, scope, '')\n    result = scope.send(\"function_#{name}\", mapped_args, &block)\n    # Prevent non r-value functions from leaking their result (they are not written to care about this)\n    Puppet::Parser::Functions.rvalue?(name) ? result : nil\n  end\n\n  def call_function(name, args, o, scope, &block)\n    file, line = extract_file_line(o)\n    loader = Adapters::LoaderAdapter.loader_for_model_object(o, file)\n    func = loader.load(:function, name) if loader\n    if func\n      Puppet::Util::Profiler.profile(name, [:functions, name]) do\n        # Add stack frame when calling.\n        return Puppet::Pops::PuppetStack.stack(file || '', line, func, :call, [scope, *args], &block)\n      end\n    end\n    # Call via 3x API if function exists there without having been autoloaded\n    fail(Issues::UNKNOWN_FUNCTION, o, { :name => name }) unless Puppet::Parser::Functions.function(name)\n\n    # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x.\n    # NOTE: Passing an empty string last converts nil/:undef to empty string\n    mapped_args = Runtime3FunctionArgumentConverter.map_args(args, scope, '')\n    # The 3x function performs return value mapping and returns nil if it is not of rvalue type\n    Puppet::Pops::PuppetStack.stack(file, line, scope, \"function_#{name}\", [mapped_args], &block)\n  end\n\n  # The o is used for source reference\n  def create_resource_parameter(o, scope, name, value, operator)\n    file, line = extract_file_line(o)\n    Puppet::Parser::Resource::Param.new(\n      :name => name,\n      :value => convert(value, scope, nil), # converted to 3x since 4x supports additional objects / types\n      :source => scope.source, :line => line, :file => file,\n      :add => operator == '+>'\n    )\n  end\n\n  def convert(value, scope, undef_value)\n    Runtime3Converter.instance.convert(value, scope, undef_value)\n  end\n\n  def create_resources(o, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)\n    # Not 100% accurate as this is the resource expression location and each title is processed separately\n    # The titles are however the result of evaluation and they have no location at this point (an array\n    # of positions for the source expressions are required for this to work).\n    # TODO: Revisit and possible improve the accuracy.\n    #\n    file, line = extract_file_line(o)\n    Runtime3ResourceSupport.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)\n  end\n\n  # Defines default parameters for a type with the given name.\n  #\n  def create_resource_defaults(o, scope, type_name, evaluated_parameters)\n    # Note that name must be capitalized in this 3x call\n    # The 3x impl creates a Resource instance with a bogus title and then asks the created resource\n    # for the type of the name.\n    # Note, locations are available per parameter.\n    #\n    scope.define_settings(capitalize_qualified_name(type_name), evaluated_parameters.flatten)\n  end\n\n  # Capitalizes each segment of a qualified name\n  #\n  def capitalize_qualified_name(name)\n    name.split(/::/).map(&:capitalize).join(NAME_SPACE_SEPARATOR)\n  end\n\n  # Creates resource overrides for all resource type objects in evaluated_resources. The same set of\n  # evaluated parameters are applied to all.\n  #\n  def create_resource_overrides(o, scope, evaluated_resources, evaluated_parameters)\n    # Not 100% accurate as this is the resource expression location and each title is processed separately\n    # The titles are however the result of evaluation and they have no location at this point (an array\n    # of positions for the source expressions are required for this to work.\n    # TODO: Revisit and possible improve the accuracy.\n    #\n    file, line = extract_file_line(o)\n    # A *=> results in an array of arrays\n    evaluated_parameters = evaluated_parameters.flatten\n    evaluated_resources.each do |r|\n      unless r.is_a?(Types::PResourceType) && r.type_name != 'class'\n        fail(Issues::ILLEGAL_OVERRIDDEN_TYPE, o, { :actual => r })\n      end\n\n      t = Runtime3ResourceSupport.find_resource_type(scope, r.type_name)\n      resource = Puppet::Parser::Resource.new(\n        t, r.title, {\n          :parameters => evaluated_parameters,\n          :file => file,\n          :line => line,\n          # WTF is this? Which source is this? The file? The name of the context ?\n          :source => scope.source,\n          :scope => scope\n        }, false # defaults should not override\n      )\n\n      scope.compiler.add_override(resource)\n    end\n  end\n\n  # Finds a resource given a type and a title.\n  #\n  def find_resource(scope, type_name, title)\n    scope.compiler.findresource(type_name, title)\n  end\n\n  # Returns the value of a resource's parameter by first looking up the parameter in the resource\n  # and then in the defaults for the resource. Since the resource exists (it must in order to look up its\n  # parameters, any overrides have already been applied). Defaults are not applied to a resource until it\n  # has been finished (which typically has not taken place when this is evaluated; hence the dual lookup).\n  #\n  def get_resource_parameter_value(scope, resource, parameter_name)\n    # This gets the parameter value, or nil (for both valid parameters and parameters that do not exist).\n    val = resource[parameter_name]\n\n    # Sometimes the resource is a Puppet::Parser::Resource and sometimes it is\n    # a Puppet::Resource. The Puppet::Resource case occurs when puppet language\n    # is evaluated against an already completed catalog (where all instances of\n    # Puppet::Parser::Resource are converted to Puppet::Resource instances).\n    # Evaluating against an already completed catalog is really only found in\n    # the language specification tests, where the puppet language is used to\n    # test itself.\n    if resource.is_a?(Puppet::Parser::Resource)\n      # The defaults must be looked up in the scope where the resource was created (not in the given\n      # scope where the lookup takes place.\n      resource_scope = resource.scope\n      defaults = resource_scope.lookupdefaults(resource.type) if val.nil? && resource_scope\n      if defaults\n        # NOTE: 3x resource keeps defaults as hash using symbol for name as key to Parameter which (again) holds\n        # name and value.\n        # NOTE: meta parameters that are unset ends up here, and there are no defaults for those encoded\n        # in the defaults, they may receive hardcoded defaults later (e.g. 'tag').\n        param = defaults[parameter_name.to_sym]\n        # Some parameters (meta parameters like 'tag') does not return a param from which the value can be obtained\n        # at all times. Instead, they return a nil param until a value has been set.\n        val = param.nil? ? nil : param.value\n      end\n    end\n    val\n  end\n\n  # Returns true, if the given name is the name of a resource parameter.\n  #\n  def is_parameter_of_resource?(scope, resource, name)\n    return false unless name.is_a?(String)\n\n    resource.valid_parameter?(name)\n  end\n\n  # This is the same type of \"truth\" as used in the current Puppet DSL.\n  #\n  def is_true?(value, o)\n    # Is the value true?  This allows us to control the definition of truth\n    # in one place.\n    case value\n    # Support :undef since it may come from a 3x structure\n    when :undef\n      false\n    when String\n      true\n    else\n      !!value\n    end\n  end\n\n  def extract_file_line(o)\n    o.is_a?(Model::Positioned) ? [o.file, o.line] : [nil, -1]\n  end\n\n  # Creates a diagnostic producer\n  def diagnostic_producer\n    Validation::DiagnosticProducer.new(\n      ExceptionRaisingAcceptor.new(), # Raises exception on all issues\n      SeverityProducer.new(), # All issues are errors\n      Model::ModelLabelProvider.new()\n    )\n  end\n\n  # Configure the severity of failures\n  class SeverityProducer < Validation::SeverityProducer\n    def initialize\n      super\n      p = self\n      # Issues triggering warning only if --debug is on\n      if Puppet[:debug]\n        p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :warning\n      else\n        p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :ignore\n      end\n\n      # if strict variables are on, an error is raised\n      # if strict variables are off, the Puppet[strict] defines what is done\n      #\n      if Puppet[:strict_variables]\n        p[Issues::UNKNOWN_VARIABLE] = :error\n      elsif Puppet[:strict] == :off\n        p[Issues::UNKNOWN_VARIABLE] = :ignore\n      else\n        p[Issues::UNKNOWN_VARIABLE] = Puppet[:strict]\n      end\n\n      # Store config issues, ignore or warning\n      p[Issues::RT_NO_STORECONFIGS_EXPORT]    = Puppet[:storeconfigs] ? :ignore : :warning\n      p[Issues::RT_NO_STORECONFIGS]           = Puppet[:storeconfigs] ? :ignore : :warning\n      p[Issues::CLASS_NOT_VIRTUALIZABLE]      = :error\n      p[Issues::NUMERIC_COERCION]             = Puppet[:strict] == :off ? :ignore : Puppet[:strict]\n      p[Issues::SERIALIZATION_DEFAULT_CONVERTED_TO_STRING] = Puppet[:strict] == :off ? :warning : Puppet[:strict]\n      p[Issues::SERIALIZATION_UNKNOWN_CONVERTED_TO_STRING] = Puppet[:strict] == :off ? :warning : Puppet[:strict]\n      p[Issues::SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING] = Puppet[:strict] == :off ? :warning : Puppet[:strict]\n    end\n  end\n\n  # An acceptor of diagnostics that immediately raises an exception.\n  class ExceptionRaisingAcceptor < Validation::Acceptor\n    def accept(diagnostic)\n      super\n      IssueReporter.assert_and_report(self, {\n                                        :message => \"Evaluation Error:\",\n                                        :emit_warnings => true, # log warnings\n                                        :exception_class => Puppet::PreformattedError\n                                      })\n      if errors?\n        raise ArgumentError, _(\"Internal Error: Configuration of runtime error handling wrong: should have raised exception\")\n      end\n    end\n  end\n\n  class EvaluationError < StandardError\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/functions/dispatch.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Functions\n# Defines a connection between a implementation method and the signature that\n# the method will handle.\n#\n# This interface should not be used directly. Instead dispatches should be\n# constructed using the DSL defined in {Puppet::Functions}.\n#\n# @api private\nclass Dispatch < Evaluator::CallableSignature\n  # @api public\n  attr_reader :type\n  # TODO: refactor to parameter_names since that makes it API\n  attr_reader :param_names\n  attr_reader :injections\n\n  # Describes how arguments are woven if there are injections, a regular argument is a given arg index, an array\n  # an injection description.\n  #\n  attr_reader :weaving\n  # @api public\n  attr_reader :block_name\n\n  # @param type [Puppet::Pops::Types::PArrayType, Puppet::Pops::Types::PTupleType] - type describing signature\n  # @param method_name [String] the name of the method that will be called when type matches given arguments\n  # @param param_names [Array<String>] names matching the number of parameters specified by type (or empty array)\n  # @param block_name [String,nil] name of block parameter, no nil\n  # @param injections [Array<Array>] injection data for weaved parameters\n  # @param weaving [Array<Integer,Array>] weaving knits\n  # @param last_captures [Boolean] true if last parameter is captures rest\n  # @param argument_mismatch_handler [Boolean] true if this is a dispatch for an argument mismatch\n  # @api private\n  def initialize(type, method_name, param_names, last_captures = false, block_name = nil, injections = EMPTY_ARRAY, weaving = EMPTY_ARRAY, argument_mismatch_handler = false)\n    @type = type\n    @method_name = method_name\n    @param_names = param_names\n    @last_captures = last_captures\n    @block_name = block_name\n    @injections = injections\n    @weaving = weaving\n    @argument_mismatch_handler = argument_mismatch_handler\n  end\n\n  # @api private\n  def parameter_names\n    @param_names\n  end\n\n  # @api private\n  def last_captures_rest?\n    @last_captures\n  end\n\n  def argument_mismatch_handler?\n    @argument_mismatch_handler\n  end\n\n  # @api private\n  def invoke(instance, calling_scope, args, &block)\n    result = instance.send(@method_name, *weave(calling_scope, args), &block)\n    return_type = @type.return_type\n    Types::TypeAsserter.assert_instance_of(nil, return_type, result) { \"value returned from function '#{@method_name}'\" } unless return_type.nil?\n    result\n  end\n\n  # @api private\n  def weave(scope, args)\n    # no need to weave if there are no injections\n    if @injections.empty?\n      args\n    else\n      new_args = []\n      @weaving.each do |knit|\n        if knit.is_a?(Array)\n          injection_name = @injections[knit[0]]\n          new_args <<\n            case injection_name\n            when :scope\n              scope\n            when :pal_script_compiler\n              Puppet.lookup(:pal_script_compiler)\n            when :cache\n              Puppet::Pops::Adapters::ObjectIdCacheAdapter.adapt(scope.compiler)\n            when :pal_catalog_compiler\n              Puppet.lookup(:pal_catalog_compiler)\n            when :pal_compiler\n              Puppet.lookup(:pal_compiler)\n            else\n              raise ArgumentError, _(\"Unknown injection %{injection_name}\") % { injection_name: injection_name }\n            end\n        elsif knit < 0\n          # Careful so no new nil arguments are added since they would override default\n          # parameter values in the received\n          idx = -knit - 1\n          new_args += args[idx..] if idx < args.size\n        elsif knit < args.size\n          new_args << args[knit]\n        end\n      end\n      new_args\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/functions/dispatcher.rb",
    "content": "# frozen_string_literal: true\n\n# Evaluate the dispatches defined as {Puppet::Pops::Functions::Dispatch}\n# instances to call the appropriate method on the\n# {Puppet::Pops::Functions::Function} instance.\n#\n# @api private\nclass Puppet::Pops::Functions::Dispatcher\n  attr_reader :dispatchers\n\n  # @api private\n  def initialize\n    @dispatchers = []\n  end\n\n  # Answers if dispatching has been defined\n  # @return [Boolean] true if dispatching has been defined\n  #\n  # @api private\n  def empty?\n    @dispatchers.empty?\n  end\n\n  def find_matching_dispatcher(args, &block)\n    @dispatchers.find { |d| d.type.callable_with?(args, block) }\n  end\n\n  # Dispatches the call to the first found signature (entry with matching type).\n  #\n  # @param instance [Puppet::Functions::Function] - the function to call\n  # @param calling_scope [T.B.D::Scope] - the scope of the caller\n  # @param args [Array<Object>] - the given arguments in the form of an Array\n  # @return [Object] - what the called function produced\n  #\n  # @api private\n  def dispatch(instance, calling_scope, args, &block)\n    dispatcher = find_matching_dispatcher(args, &block)\n    unless dispatcher\n      args_type = Puppet::Pops::Types::TypeCalculator.singleton.infer_set(block_given? ? args + [block] : args)\n      raise ArgumentError, Puppet::Pops::Types::TypeMismatchDescriber.describe_signatures(instance.class.name, signatures, args_type)\n    end\n    if dispatcher.argument_mismatch_handler?\n      msg = dispatcher.invoke(instance, calling_scope, args)\n      raise ArgumentError, \"'#{instance.class.name}' #{msg}\"\n    end\n\n    catch(:next) do\n      dispatcher.invoke(instance, calling_scope, args, &block)\n    end\n  end\n\n  # Adds a dispatch directly to the set of dispatchers.\n  # @api private\n  def add(a_dispatch)\n    @dispatchers << a_dispatch\n  end\n\n  # Produces a CallableType for a single signature, and a Variant[<callables>] otherwise\n  #\n  # @api private\n  def to_type\n    # make a copy to make sure it can be contained by someone else (even if it is not contained here, it\n    # should be treated as immutable).\n    #\n    callables = dispatchers.map(&:type)\n\n    # multiple signatures, produce a Variant type of Callable1-n (must copy them)\n    # single signature, produce single Callable\n    callables.size > 1 ? Puppet::Pops::Types::TypeFactory.variant(*callables) : callables.pop\n  end\n\n  # @api private\n  def signatures\n    @dispatchers.reject(&:argument_mismatch_handler?)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/functions/function.rb",
    "content": "# frozen_string_literal: true\n\n# @note WARNING: This new function API is still under development and may change at\n#   any time\n#\n# A function in the puppet evaluator.\n#\n# Functions are normally defined by another system, which produces subclasses\n# of this class as well as constructing delegations to call the appropriate methods.\n#\n# This class should rarely be used directly. Instead functions should be\n# constructed using {Puppet::Functions.create_function}.\n#\n# @api public\nclass Puppet::Pops::Functions::Function\n  # The loader that loaded this function.\n  # Should be used if function wants to load other things.\n  #\n  attr_reader :loader\n\n  def initialize(closure_scope, loader)\n    @closure_scope = closure_scope\n    @loader = loader\n  end\n\n  # Invokes the function via the dispatching logic that performs type check and weaving.\n  # A specialized function may override this method to do its own dispatching and checking of\n  # the raw arguments. A specialized implementation can rearrange arguments, add or remove\n  # arguments and then delegate to the dispatching logic by calling:\n  #\n  # @example Delegating to the dispatcher\n  #     def call(scope, *args)\n  #       manipulated_args = args + ['easter_egg']\n  #       self.class.dispatcher.dispatch(self, scope, manipulated_args)\n  #     end\n  #\n  # System functions that must have access to the calling scope can use this technique. Functions\n  # in general should not need the calling scope. (The closure scope; what is visible where the function\n  # is defined) is available via the method `closure_scope`).\n  #\n  # @api public\n  def call(scope, *args, &block)\n    result = catch(:return) do\n      return self.class.dispatcher.dispatch(self, scope, args, &block)\n    end\n    result.value\n  rescue Puppet::Pops::Evaluator::Next => jumper\n    begin\n      throw :next, jumper.value\n    rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION\n      raise Puppet::ParseError.new(\"next() from context where this is illegal\", jumper.file, jumper.line)\n    end\n  rescue Puppet::Pops::Evaluator::Return => jumper\n    begin\n      throw :return, jumper\n    rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION\n      raise Puppet::ParseError.new(\"return() from context where this is illegal\", jumper.file, jumper.line)\n    end\n  end\n\n  # Allows the implementation of a function to call other functions by name. The callable functions\n  # are those visible to the same loader that loaded this function (the calling function). The\n  # referenced function is called with the calling functions closure scope as the caller's scope.\n  #\n  # @param function_name [String] The name of the function\n  # @param *args [Object] splat of arguments\n  # @return [Object] The result returned by the called function\n  #\n  # @api public\n  def call_function(function_name, *args, &block)\n    internal_call_function(closure_scope, function_name, args, &block)\n  end\n\n  # The scope where the function was defined\n  def closure_scope\n    # If closure scope is explicitly set to not nil, if there is a global scope, otherwise an empty hash\n    @closure_scope || Puppet.lookup(:global_scope) { {} }\n  end\n\n  # The dispatcher for the function\n  #\n  # @api private\n  def self.dispatcher\n    @dispatcher ||= Puppet::Pops::Functions::Dispatcher.new\n  end\n\n  # Produces information about parameters in a way that is compatible with Closure\n  #\n  # @api private\n  def self.signatures\n    @dispatcher.signatures\n  end\n\n  protected\n\n  # Allows the implementation of a function to call other functions by name and pass the caller\n  # scope. The callable functions are those visible to the same loader that loaded this function\n  # (the calling function).\n  #\n  # @param scope [Puppet::Parser::Scope] The caller scope\n  # @param function_name [String] The name of the function\n  # @param args [Array] array of arguments\n  # @return [Object] The result returned by the called function\n  #\n  # @api public\n  def internal_call_function(scope, function_name, args, &block)\n    the_loader = loader\n    unless the_loader\n      raise ArgumentError, _(\"Function %{class_name}(): cannot call function '%{function_name}' - no loader specified\") %\n                           { class_name: self.class.name, function_name: function_name }\n    end\n\n    func = the_loader.load(:function, function_name)\n    if func\n      Puppet::Util::Profiler.profile(function_name, [:functions, function_name]) do\n        return func.call(scope, *args, &block)\n      end\n    end\n\n    # Check if a 3x function is present. Raise a generic error if it's not to allow upper layers to fill in the details\n    # about where in a puppet manifest this error originates. (Such information is not available here).\n    loader_scope = closure_scope\n    func_3x = Puppet::Parser::Functions.function(function_name, loader_scope.environment) if loader_scope.is_a?(Puppet::Parser::Scope)\n    unless func_3x\n      raise ArgumentError, _(\"Function %{class_name}(): Unknown function: '%{function_name}'\") %\n                           { class_name: self.class.name, function_name: function_name }\n    end\n\n    # Call via 3x API\n    # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x.\n    # NOTE: Passing an empty string last converts nil/:undef to empty string\n    result = scope.send(func_3x, Puppet::Pops::Evaluator::Runtime3FunctionArgumentConverter.map_args(args, loader_scope, ''), &block)\n\n    # Prevent non r-value functions from leaking their result (they are not written to care about this)\n    Puppet::Parser::Functions.rvalue?(function_name) ? result : nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/issue_reporter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nclass IssueReporter\n  # @param acceptor [Validation::Acceptor] the acceptor containing reported issues\n  # @option options [String] :message (nil) A message text to use as prefix in\n  #   a single Error message\n  # @option options [Boolean] :emit_warnings (false) whether warnings should be emitted\n  # @option options [Boolean] :emit_errors (true) whether errors should be\n  #   emitted or only the given message\n  # @option options [Exception] :exception_class (Puppet::ParseError) The exception to raise\n  #\n  def self.assert_and_report(acceptor, options)\n    return unless acceptor\n\n    max_errors       = options[:max_errors]   || Puppet[:max_errors]\n    max_warnings     = options[:max_warnings] || Puppet[:max_warnings]\n    max_deprecations = options[:max_deprecations] || (Puppet[:disable_warnings].include?('deprecations') ? 0 : Puppet[:max_deprecations])\n\n    emit_warnings    = options[:emit_warnings] || false\n    emit_errors      = options[:emit_errors].nil? ? true : !!options[:emit_errors]\n    emit_message     = options[:message]\n    emit_exception   = options[:exception_class] || Puppet::ParseErrorWithIssue\n\n    # If there are warnings output them\n    warnings = acceptor.warnings\n    if emit_warnings && warnings.size > 0\n      formatter = Validation::DiagnosticFormatterPuppetStyle.new\n      emitted_w = 0\n      emitted_dw = 0\n      acceptor.warnings.each do |w|\n        if w.severity == :deprecation\n          # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not\n          # deprecation of constructs in manifests! (It is not designed for that purpose even if\n          # used throughout the code base).\n          #\n          log_message(:warning, formatter, w) if emitted_dw < max_deprecations\n          emitted_dw += 1\n        else\n          log_message(:warning, formatter, w) if emitted_w < max_warnings\n          emitted_w += 1\n        end\n        break if emitted_w >= max_warnings && emitted_dw >= max_deprecations # but only then\n      end\n    end\n\n    # If there were errors, report the first found. Use a puppet style formatter.\n    errors = acceptor.errors\n    if errors.size > 0\n      unless emit_errors\n        raise emit_exception, emit_message\n      end\n\n      formatter = Validation::DiagnosticFormatterPuppetStyle.new\n      if errors.size == 1 || max_errors <= 1\n        # raise immediately\n        exception = create_exception(emit_exception, emit_message, formatter, errors[0])\n        # if an exception was given as cause, use it's backtrace instead of the one indicating \"here\"\n        if errors[0].exception\n          exception.set_backtrace(errors[0].exception.backtrace)\n        end\n        raise exception\n      end\n      emitted = 0\n      if emit_message\n        Puppet.err(emit_message)\n      end\n      errors.each do |e|\n        log_message(:err, formatter, e)\n        emitted += 1\n        break if emitted >= max_errors\n      end\n      giving_up_message = if emit_warnings && warnings.size > 0\n                            _(\"Language validation logged %{error_count} errors, and %{warning_count} warnings. Giving up\") %\n                              { error_count: errors.size, warning_count: warnings.size }\n                          else\n                            _(\"Language validation logged %{error_count} errors. Giving up\") %\n                              { error_count: errors.size }\n                          end\n      exception = emit_exception.new(giving_up_message)\n      exception.file = errors[0].file\n      raise exception\n    end\n  end\n\n  def self.format_with_prefix(prefix, message)\n    return message unless prefix\n\n    [prefix, message].join(' ')\n  end\n\n  def self.warning(semantic, issue, args)\n    Puppet::Util::Log.create({\n                               :level => :warning,\n                               :message => issue.format(args),\n                               :arguments => args,\n                               :issue_code => issue.issue_code,\n                               :file => semantic.file,\n                               :line => semantic.line,\n                               :pos => semantic.pos,\n                             })\n  end\n\n  def self.error(exception_class, semantic, issue, args)\n    raise exception_class.new(issue.format(args), semantic.file, semantic.line, semantic.pos, nil, issue.issue_code, args)\n  end\n\n  def self.create_exception(exception_class, emit_message, formatter, diagnostic)\n    file = diagnostic.file\n    file = (file.is_a?(String) && file.empty?) ? nil : file\n    line = pos = nil\n    if diagnostic.source_pos\n      line = diagnostic.source_pos.line\n      pos = diagnostic.source_pos.pos\n    end\n    exception_class.new(format_with_prefix(emit_message, formatter.format_message(diagnostic)), file, line, pos, nil, diagnostic.issue.issue_code, diagnostic.arguments)\n  end\n  private_class_method :create_exception\n\n  def self.log_message(severity, formatter, diagnostic)\n    file = diagnostic.file\n    file = (file.is_a?(String) && file.empty?) ? nil : file\n    line = pos = nil\n    if diagnostic.source_pos\n      line = diagnostic.source_pos.line\n      pos = diagnostic.source_pos.pos\n    end\n    Puppet::Util::Log.create({\n                               :level => severity,\n                               :message => formatter.format_message(diagnostic),\n                               :arguments => diagnostic.arguments,\n                               :issue_code => diagnostic.issue.issue_code,\n                               :file => file,\n                               :line => line,\n                               :pos => pos,\n                             })\n  end\n  private_class_method :log_message\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/issues.rb",
    "content": "# frozen_string_literal: true\n\n# Defines classes to deal with issues, and message formatting and defines constants with Issues.\n# @api public\n#\nmodule Puppet::Pops\nmodule Issues\n  # Describes an issue, and can produce a message for an occurrence of the issue.\n  #\n  class Issue\n    # The issue code\n    # @return [Symbol]\n    attr_reader :issue_code\n\n    # A block producing the message\n    # @return [Proc]\n    attr_reader :message_block\n\n    # Names that must be bound in an occurrence of the issue to be able to produce a message.\n    # These are the names in addition to requirements stipulated by the Issue formatter contract; i.e. :label`,\n    # and `:semantic`.\n    #\n    attr_reader :arg_names\n\n    # If this issue can have its severity lowered to :warning, :deprecation, or :ignored\n    attr_writer :demotable\n\n    # Configures the Issue with required arguments (bound by occurrence), and a block producing a message.\n    def initialize issue_code, *args, &block\n      @issue_code = issue_code\n      @message_block = block\n      @arg_names = args\n      @demotable = true\n    end\n\n    # Returns true if it is allowed to demote this issue\n    def demotable?\n      @demotable\n    end\n\n    # Formats a message for an occurrence of the issue with argument bindings passed in a hash.\n    # The hash must contain a LabelProvider bound to the key `label` and the semantic model element\n    # bound to the key `semantic`. All required arguments as specified by `arg_names` must be bound\n    # in the given `hash`.\n    # @api public\n    #\n    def format(hash = {})\n      # Create a Message Data where all hash keys become methods for convenient interpolation\n      # in issue text.\n      msgdata = MessageData.new(*arg_names)\n      begin\n        # Evaluate the message block in the msg data's binding\n        msgdata.format(hash, &message_block)\n      rescue StandardError => e\n        raise RuntimeError, _(\"Error while reporting issue: %{code}. %{message}\") % { code: issue_code, message: e.message }, caller\n      end\n    end\n  end\n\n  # Provides a binding of arguments passed to Issue.format to method names available\n  # in the issue's message producing block.\n  # @api private\n  #\n  class MessageData\n    def initialize *argnames\n      singleton = class << self; self end\n      argnames.each do |name|\n        singleton.send(:define_method, name) do\n          @data[name]\n        end\n      end\n    end\n\n    def format(hash, &block)\n      @data = hash\n      instance_eval(&block)\n    end\n\n    # Obtains the label provider given as a key `:label` in the hash passed to #format. The label provider is\n    # return if no arguments are given. If given an argument, returns the result of calling #label on the label\n    # provider.\n    #\n    # @param args [Object] one object to obtain a label for or zero arguments to obtain the label provider\n    # @return [LabelProvider,String] the label provider or label depending on if an argument is given or not\n    # @raise [Puppet::Error] if no label provider is found\n    def label(*args)\n      args.empty? ? label_provider : label_provider.label(args[0])\n    end\n\n    # Returns the label provider given as key `:label` in the hash passed to #format.\n    # @return [LabelProvider] the label provider\n    # @raise [Puppet::Error] if no label provider is found\n    def label_provider\n      label_provider = @data[:label]\n      # TRANSLATORS \":label\" is a keyword and should not be translated\n      raise Puppet::Error, _('Label provider key :label must be set to produce the text of the message!') unless label_provider\n\n      label_provider\n    end\n\n    # Returns the label provider given as a key in the hash passed to #format.\n    #\n    def semantic\n      # TRANSLATORS \":semantic\" is a keyword and should not be translated\n      raise Puppet::Error, _('Label provider key :semantic must be set to produce the text of the message!') unless @data[:semantic]\n\n      @data[:semantic]\n    end\n  end\n\n  # Defines an issue with the given `issue_code`, additional required parameters, and a block producing a message.\n  # The block is evaluated in the context of a MessageData which provides convenient access to all required arguments\n  # via accessor methods. In addition to accessors for specified arguments, these are also available:\n  # * `label` - a `LabelProvider` that provides human understandable names for model elements and production of article (a/an/the).\n  # * `semantic` - the model element for which the issue is reported\n  #\n  # @param issue_code [Symbol] the issue code for the issue used as an identifier, should be the same as the constant\n  #   the issue is bound to.\n  # @param args [Symbol] required arguments that must be passed when formatting the message, may be empty\n  # @param block [Proc] a block producing the message string, evaluated in a MessageData scope. The produced string\n  #   should not end with a period as additional information may be appended.\n  #\n  # @see MessageData\n  # @api public\n  #\n  def self.issue(issue_code, *args, &block)\n    Issue.new(issue_code, *args, &block)\n  end\n\n  # Creates a non demotable issue.\n  # @see Issue.issue\n  #\n  def self.hard_issue(issue_code, *args, &block)\n    result = Issue.new(issue_code, *args, &block)\n    result.demotable = false\n    result\n  end\n\n  # @comment Here follows definitions of issues. The intent is to provide a list from which yardoc can be generated\n  #   containing more detailed information / explanation of the issue.\n  #   These issues are set as constants, but it is unfortunately not possible for the created object to easily know which\n  #   name it is bound to. Instead the constant has to be repeated. (Alternatively, it could be done by instead calling\n  #   #const_set on the module, but the extra work required to get yardoc output vs. the extra effort to repeat the name\n  #   twice makes it not worth it (if doable at all, since there is no tag to artificially construct a constant, and\n  #   the parse tag does not produce any result for a constant assignment).\n\n  # This is allowed (3.1) and has not yet been deprecated.\n  # @todo configuration\n  #\n  NAME_WITH_HYPHEN = issue :NAME_WITH_HYPHEN, :name do\n    _(\"%{issue} may not have a name containing a hyphen. The name '%{name}' is not legal\") % { issue: label.a_an_uc(semantic), name: name }\n  end\n\n  # When a variable name contains a hyphen and these are illegal.\n  # It is possible to control if a hyphen is legal in a name or not using the setting TODO\n  # @todo describe the setting\n  # @api public\n  # @todo configuration if this is error or warning\n  #\n  VAR_WITH_HYPHEN = issue :VAR_WITH_HYPHEN, :name do\n    _(\"A variable name may not contain a hyphen. The name '%{name}' is not legal\") % { name: name }\n  end\n\n  # A class, definition, or node may only appear at top level or inside other classes\n  # @todo Is this really true for nodes? Can they be inside classes? Isn't that too late?\n  # @api public\n  #\n  NOT_TOP_LEVEL = hard_issue :NOT_TOP_LEVEL do\n    _(\"Classes, definitions, and nodes may only appear at toplevel or inside other classes\")\n  end\n\n  NOT_ABSOLUTE_TOP_LEVEL = hard_issue :NOT_ABSOLUTE_TOP_LEVEL do\n    _(\"%{value} may only appear at toplevel\") % { value: label.a_an_uc(semantic) }\n  end\n\n  CROSS_SCOPE_ASSIGNMENT = hard_issue :CROSS_SCOPE_ASSIGNMENT, :name do\n    _(\"Illegal attempt to assign to '%{name}'. Cannot assign to variables in other namespaces\") % { name: name }\n  end\n\n  # Assignment can only be made to certain types of left hand expressions such as variables.\n  ILLEGAL_ASSIGNMENT = hard_issue :ILLEGAL_ASSIGNMENT do\n    _(\"Illegal attempt to assign to '%{value}'. Not an assignable reference\") % { value: label.a_an(semantic) }\n  end\n\n  # Variables are immutable, cannot reassign in the same assignment scope\n  ILLEGAL_REASSIGNMENT = hard_issue :ILLEGAL_REASSIGNMENT, :name do\n    if Validation::Checker4_0::RESERVED_PARAMETERS[name]\n      _(\"Cannot reassign built in (or already assigned) variable '$%{var}'\") % { var: name }\n    else\n      _(\"Cannot reassign variable '$%{var}'\") % { var: name }\n    end\n  end\n\n  # Variables facts and trusted\n  ILLEGAL_RESERVED_ASSIGNMENT = hard_issue :ILLEGAL_RESERVED_ASSIGNMENT, :name do\n    _(\"Attempt to assign to a reserved variable name: '$%{var}'\") % { var: name }\n  end\n\n  # Assignment cannot be made to numeric match result variables\n  ILLEGAL_NUMERIC_ASSIGNMENT = issue :ILLEGAL_NUMERIC_ASSIGNMENT, :varname do\n    _(\"Illegal attempt to assign to the numeric match result variable '$%{var}'. Numeric variables are not assignable\") % { var: varname }\n  end\n\n  # Assignment can only be made to certain types of left hand expressions such as variables.\n  ILLEGAL_ASSIGNMENT_CONTEXT = hard_issue :ILLEGAL_ASSIGNMENT_CONTEXT do\n    _(\"Assignment not allowed here\")\n  end\n\n  # parameters cannot have numeric names, clashes with match result variables\n  ILLEGAL_NUMERIC_PARAMETER = issue :ILLEGAL_NUMERIC_PARAMETER, :name do\n    _(\"The numeric parameter name '$%{name}' cannot be used (clashes with numeric match result variables)\") % { name: name }\n  end\n\n  ILLEGAL_NONLITERAL_PARAMETER_TYPE = issue :ILLEGAL_NONLITERAL_PARAMETER_TYPE, :name, :type_class do\n    _(\"The parameter '$%{name}' must be a literal type, not %{type_class}\") % { name: name, type_class: label.a_an(type_class) }\n  end\n\n  # In certain versions of Puppet it may be allowed to assign to a not already assigned key\n  # in an array or a hash. This is an optional validation that may be turned on to prevent accidental\n  # mutation.\n  #\n  ILLEGAL_INDEXED_ASSIGNMENT = issue :ILLEGAL_INDEXED_ASSIGNMENT do\n    _(\"Illegal attempt to assign via [index/key]. Not an assignable reference\")\n  end\n\n  # When indexed assignment ($x[]=) is allowed, the leftmost expression must be\n  # a variable expression.\n  #\n  ILLEGAL_ASSIGNMENT_VIA_INDEX = hard_issue :ILLEGAL_ASSIGNMENT_VIA_INDEX do\n    _(\"Illegal attempt to assign to %{value} via [index/key]. Not an assignable reference\") % { value: label.a_an(semantic) }\n  end\n\n  ILLEGAL_MULTI_ASSIGNMENT_SIZE = hard_issue :ILLEGAL_MULTI_ASSIGNMENT_SIZE, :expected, :actual do\n    _(\"Mismatched number of assignable entries and values, expected %{expected}, got %{actual}\") % { expected: expected, actual: actual }\n  end\n\n  MISSING_MULTI_ASSIGNMENT_KEY = hard_issue :MISSING_MULTI_ASSIGNMENT_KEY, :key do\n    _(\"No value for required key '%{key}' in assignment to variables from hash\") % { key: key }\n  end\n\n  MISSING_MULTI_ASSIGNMENT_VARIABLE = hard_issue :MISSING_MULTI_ASSIGNMENT_VARIABLE, :name do\n    _(\"No value for required variable '$%{name}' in assignment to variables from class reference\") % { name: name }\n  end\n\n  APPENDS_DELETES_NO_LONGER_SUPPORTED = hard_issue :APPENDS_DELETES_NO_LONGER_SUPPORTED, :operator do\n    _(\"The operator '%{operator}' is no longer supported. See http://links.puppet.com/remove-plus-equals\") % { operator: operator }\n  end\n\n  # For unsupported operators (e.g. += and -= in puppet 4).\n  #\n  UNSUPPORTED_OPERATOR = hard_issue :UNSUPPORTED_OPERATOR, :operator do\n    _(\"The operator '%{operator}' is not supported.\") % { operator: operator }\n  end\n\n  # For operators that are not supported in specific contexts (e.g. '* =>' in\n  # resource defaults)\n  #\n  UNSUPPORTED_OPERATOR_IN_CONTEXT = hard_issue :UNSUPPORTED_OPERATOR_IN_CONTEXT, :operator do\n    _(\"The operator '%{operator}' in %{value} is not supported.\") % { operator: operator, value: label.a_an(semantic) }\n  end\n\n  # For non applicable operators (e.g. << on Hash).\n  #\n  OPERATOR_NOT_APPLICABLE = hard_issue :OPERATOR_NOT_APPLICABLE, :operator, :left_value do\n    _(\"Operator '%{operator}' is not applicable to %{left}.\") % { operator: operator, left: label.a_an(left_value) }\n  end\n\n  OPERATOR_NOT_APPLICABLE_WHEN = hard_issue :OPERATOR_NOT_APPLICABLE_WHEN, :operator, :left_value, :right_value do\n    _(\"Operator '%{operator}' is not applicable to %{left} when right side is %{right}.\") % { operator: operator, left: label.a_an(left_value), right: label.a_an(right_value) }\n  end\n\n  COMPARISON_NOT_POSSIBLE = hard_issue :COMPARISON_NOT_POSSIBLE, :operator, :left_value, :right_value, :detail do\n    _(\"Comparison of: %{left} %{operator} %{right}, is not possible. Caused by '%{detail}'.\") % { left: label(left_value), operator: operator, right: label(right_value), detail: detail }\n  end\n\n  MATCH_NOT_REGEXP = hard_issue :MATCH_NOT_REGEXP, :detail do\n    _(\"Can not convert right match operand to a regular expression. Caused by '%{detail}'.\") % { detail: detail }\n  end\n\n  MATCH_NOT_STRING = hard_issue :MATCH_NOT_STRING, :left_value do\n    _(\"Left match operand must result in a String value. Got %{left}.\") % { left: label.a_an(left_value) }\n  end\n\n  # Some expressions/statements may not produce a value (known as right-value, or rvalue).\n  # This may vary between puppet versions.\n  #\n  NOT_RVALUE = issue :NOT_RVALUE do\n    _(\"Invalid use of expression. %{value} does not produce a value\") % { value: label.a_an_uc(semantic) }\n  end\n\n  # Appending to attributes is only allowed in certain types of resource expressions.\n  #\n  ILLEGAL_ATTRIBUTE_APPEND = hard_issue :ILLEGAL_ATTRIBUTE_APPEND, :name, :parent do\n    _(\"Illegal +> operation on attribute %{attr}. This operator can not be used in %{expression}\") % { attr: name, expression: label.a_an(parent) }\n  end\n\n  ILLEGAL_NAME = hard_issue :ILLEGAL_NAME, :name do\n    _(\"Illegal name. The given name '%{name}' does not conform to the naming rule /^((::)?[a-z_]\\\\w*)(::[a-z]\\\\w*)*$/\") % { name: name }\n  end\n\n  ILLEGAL_SINGLE_TYPE_MAPPING = hard_issue :ILLEGAL_TYPE_MAPPING, :expression do\n    _(\"Illegal type mapping. Expected a Type on the left side, got %{expression}\") % { expression: label.a_an_uc(semantic) }\n  end\n\n  ILLEGAL_REGEXP_TYPE_MAPPING = hard_issue :ILLEGAL_TYPE_MAPPING, :expression do\n    _(\"Illegal type mapping. Expected a Tuple[Regexp,String] on the left side, got %{expression}\") % { expression: label.a_an_uc(semantic) }\n  end\n\n  ILLEGAL_PARAM_NAME = hard_issue :ILLEGAL_PARAM_NAME, :name do\n    _(\"Illegal parameter name. The given name '%{name}' does not conform to the naming rule /^[a-z_]\\\\w*$/\") % { name: name }\n  end\n\n  ILLEGAL_VAR_NAME = hard_issue :ILLEGAL_VAR_NAME, :name do\n    _(\"Illegal variable name, The given name '%{name}' does not conform to the naming rule /^((::)?[a-z]\\\\w*)*((::)?[a-z_]\\\\w*)$/\") % { name: name }\n  end\n\n  ILLEGAL_NUMERIC_VAR_NAME = hard_issue :ILLEGAL_NUMERIC_VAR_NAME, :name do\n    _(\"Illegal numeric variable name, The given name '%{name}' must be a decimal value if it starts with a digit 0-9\") % { name: name }\n  end\n\n  # In case a model is constructed programmatically, it must create valid type references.\n  #\n  ILLEGAL_CLASSREF = hard_issue :ILLEGAL_CLASSREF, :name do\n    _(\"Illegal type reference. The given name '%{name}' does not conform to the naming rule\") % { name: name }\n  end\n\n  # This is a runtime issue - storeconfigs must be on in order to collect exported. This issue should be\n  # set to :ignore when just checking syntax.\n  # @todo should be a :warning by default\n  #\n  RT_NO_STORECONFIGS = issue :RT_NO_STORECONFIGS do\n    _(\"You cannot collect exported resources without storeconfigs being set; the collection will be ignored\")\n  end\n\n  # This is a runtime issue - storeconfigs must be on in order to export a resource. This issue should be\n  # set to :ignore when just checking syntax.\n  # @todo should be a :warning by default\n  #\n  RT_NO_STORECONFIGS_EXPORT = issue :RT_NO_STORECONFIGS_EXPORT do\n    _(\"You cannot collect exported resources without storeconfigs being set; the export is ignored\")\n  end\n\n  # A hostname may only contain letters, digits, '_', '-', and '.'.\n  #\n  ILLEGAL_HOSTNAME_CHARS = hard_issue :ILLEGAL_HOSTNAME_CHARS, :hostname do\n    _(\"The hostname '%{hostname}' contains illegal characters (only letters, digits, '_', '-', and '.' are allowed)\") % { hostname: hostname }\n  end\n\n  # A hostname may only contain letters, digits, '_', '-', and '.'.\n  #\n  ILLEGAL_HOSTNAME_INTERPOLATION = hard_issue :ILLEGAL_HOSTNAME_INTERPOLATION do\n    _(\"An interpolated expression is not allowed in a hostname of a node\")\n  end\n\n  # Issues when an expression is used where it is not legal.\n  # E.g. an arithmetic expression where a hostname is expected.\n  #\n  ILLEGAL_EXPRESSION = hard_issue :ILLEGAL_EXPRESSION, :feature, :container do\n    _(\"Illegal expression. %{expression} is unacceptable as %{feature} in %{container}\") % { expression: label.a_an_uc(semantic), feature: feature, container: label.a_an(container) }\n  end\n\n  # Issues when a variable is not a NAME\n  #\n  ILLEGAL_VARIABLE_EXPRESSION = hard_issue :ILLEGAL_VARIABLE_EXPRESSION do\n    _(\"Illegal variable expression. %{expression} did not produce a variable name (String or Numeric).\") % { expression: label.a_an_uc(semantic) }\n  end\n\n  # Issues when an expression is used illegally in a query.\n  # query only supports == and !=, and not <, > etc.\n  #\n  ILLEGAL_QUERY_EXPRESSION = hard_issue :ILLEGAL_QUERY_EXPRESSION do\n    _(\"Illegal query expression. %{expression} cannot be used in a query\") % { expression: label.a_an_uc(semantic) }\n  end\n\n  # If an attempt is made to make a resource default virtual or exported.\n  #\n  NOT_VIRTUALIZEABLE = hard_issue :NOT_VIRTUALIZEABLE do\n    _(\"Resource Defaults are not virtualizable\")\n  end\n\n  CLASS_NOT_VIRTUALIZABLE = issue :CLASS_NOT_VIRTUALIZABLE do\n    _(\"Classes are not virtualizable\")\n  end\n\n  # When an attempt is made to use multiple keys (to produce a range in Ruby - e.g. $arr[2,-1]).\n  # This is not supported in 3x, but it allowed in 4x.\n  #\n  UNSUPPORTED_RANGE = issue :UNSUPPORTED_RANGE, :count do\n    _(\"Attempt to use unsupported range in %{expression}, %{count} values given for max 1\") % { expression: label.a_an(semantic), count: count }\n  end\n\n  # Issues when expressions that are not implemented or activated in the current version are used.\n  #\n  UNSUPPORTED_EXPRESSION = issue :UNSUPPORTED_EXPRESSION do\n    _(\"Expressions of type %{expression} are not supported in this version of Puppet\") % { expression: label.a_an(semantic) }\n  end\n\n  ILLEGAL_RELATIONSHIP_OPERAND_TYPE = issue :ILLEGAL_RELATIONSHIP_OPERAND_TYPE, :operand do\n    _(\"Illegal relationship operand, can not form a relationship with %{expression}. A Catalog type is required.\") % { expression: label.a_an(operand) }\n  end\n\n  NOT_CATALOG_TYPE = issue :NOT_CATALOG_TYPE, :type do\n    _(\"Illegal relationship operand, can not form a relationship with something of type %{expression_type}. A Catalog type is required.\") % { expression_type: type }\n  end\n\n  BAD_STRING_SLICE_ARITY = issue :BAD_STRING_SLICE_ARITY, :actual do\n    _(\"String supports [] with one or two arguments. Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_STRING_SLICE_TYPE = issue :BAD_STRING_SLICE_TYPE, :actual do\n    _(\"String-Type [] requires all arguments to be integers (or default). Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_ARRAY_SLICE_ARITY = issue :BAD_ARRAY_SLICE_ARITY, :actual do\n    _(\"Array supports [] with one or two arguments. Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_HASH_SLICE_ARITY = issue :BAD_HASH_SLICE_ARITY, :actual do\n    _(\"Hash supports [] with one or more arguments. Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_INTEGER_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do\n    _(\"Integer-Type supports [] with one or two arguments (from, to). Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_INTEGER_SLICE_TYPE = issue :BAD_INTEGER_SLICE_TYPE, :actual do\n    _(\"Integer-Type [] requires all arguments to be integers (or default). Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_COLLECTION_SLICE_TYPE = issue :BAD_COLLECTION_SLICE_TYPE, :actual do\n    _(\"A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got %{actual}\") % { actual: label.a_an(actual) }\n  end\n\n  BAD_FLOAT_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do\n    _(\"Float-Type supports [] with one or two arguments (from, to). Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_FLOAT_SLICE_TYPE = issue :BAD_INTEGER_SLICE_TYPE, :actual do\n    _(\"Float-Type [] requires all arguments to be floats, or integers (or default). Got %{actual}\") % { actual: actual }\n  end\n\n  BAD_SLICE_KEY_TYPE = issue :BAD_SLICE_KEY_TYPE, :left_value, :expected_classes, :actual do\n    if expected_classes.size > 1\n      _(\"%{expression}[] cannot use %{actual} where one of the following is expected: %{expected}\") % { expression: label.a_an_uc(left_value), actual: actual, expected: expected_classes.join(', ') }\n    else\n      _(\"%{expression}[] cannot use %{actual} where %{expected} is expected\") % { expression: label.a_an_uc(left_value), actual: actual, expected: expected_classes[0] }\n    end\n  end\n\n  BAD_STRING_SLICE_KEY_TYPE = issue :BAD_STRING_SLICE_KEY_TYPE, :left_value, :actual_type do\n    _(\"A substring operation does not accept %{label_article} %{actual_type} as a character index. Expected an Integer\") % { label_article: label.article(actual_type), actual_type: actual_type }\n  end\n\n  BAD_NOT_UNDEF_SLICE_TYPE = issue :BAD_NOT_UNDEF_SLICE_TYPE, :base_type, :actual do\n    _(\"%{expression}[] argument must be a Type or a String. Got %{actual}\") % { expression: base_type, actual: actual }\n  end\n\n  BAD_TYPE_SLICE_TYPE = issue :BAD_TYPE_SLICE_TYPE, :base_type, :actual do\n    _(\"%{base_type}[] arguments must be types. Got %{actual}\") % { base_type: base_type, actual: actual }\n  end\n\n  BAD_TYPE_SLICE_ARITY = issue :BAD_TYPE_SLICE_ARITY, :base_type, :min, :max, :actual do\n    base_type_label = base_type.is_a?(String) ? base_type : label.a_an_uc(base_type)\n    if max == -1 || max == Float::INFINITY\n      _(\"%{base_type_label}[] accepts %{min} or more arguments. Got %{actual}\") % { base_type_label: base_type_label, min: min, actual: actual }\n    elsif max && max != min\n      _(\"%{base_type_label}[] accepts %{min} to %{max} arguments. Got %{actual}\") % { base_type_label: base_type_label, min: min, max: max, actual: actual }\n    else\n      _(\"%{base_type_label}[] accepts %{min} %{label}. Got %{actual}\") % { base_type_label: base_type_label, min: min, label: label.plural_s(min, _('argument')), actual: actual }\n    end\n  end\n\n  BAD_TYPE_SPECIALIZATION = hard_issue :BAD_TYPE_SPECIALIZATION, :type, :message do\n    _(\"Error creating type specialization of %{base_type}, %{message}\") % { base_type: label.a_an(type), message: message }\n  end\n\n  ILLEGAL_TYPE_SPECIALIZATION = issue :ILLEGAL_TYPE_SPECIALIZATION, :kind do\n    _(\"Cannot specialize an already specialized %{kind} type\") % { kind: kind }\n  end\n\n  ILLEGAL_RESOURCE_SPECIALIZATION = issue :ILLEGAL_RESOURCE_SPECIALIZATION, :actual do\n    _(\"First argument to Resource[] must be a resource type or a String. Got %{actual}.\") % { actual: actual }\n  end\n\n  EMPTY_RESOURCE_SPECIALIZATION = issue :EMPTY_RESOURCE_SPECIALIZATION do\n    _(\"Arguments to Resource[] are all empty/undefined\")\n  end\n\n  ILLEGAL_HOSTCLASS_NAME = hard_issue :ILLEGAL_HOSTCLASS_NAME, :name do\n    _(\"Illegal Class name in class reference. %{expression} cannot be used where a String is expected\") % { expression: label.a_an_uc(name) }\n  end\n\n  ILLEGAL_DEFINITION_NAME = hard_issue :ILLEGAL_DEFINITION_NAME, :name do\n    _(\"Unacceptable name. The name '%{name}' is unacceptable as the name of %{value}\") % { name: name, value: label.a_an(semantic) }\n  end\n\n  ILLEGAL_DEFINITION_LOCATION = issue :ILLEGAL_DEFINITION_LOCATION, :name, :file do\n    _(\"Unacceptable location. The name '%{name}' is unacceptable in file '%{file}'\") % { name: name, file: file }\n  end\n\n  ILLEGAL_TOP_CONSTRUCT_LOCATION = issue :ILLEGAL_TOP_CONSTRUCT_LOCATION do\n    if semantic.is_a?(Puppet::Pops::Model::NamedDefinition)\n      _(\"The %{value} '%{name}' is unacceptable as a top level construct in this location\") % { name: semantic.name, value: label(semantic) }\n    else\n      _(\"This %{value} is unacceptable as a top level construct in this location\") % { value: label(semantic) }\n    end\n  end\n\n  CAPTURES_REST_NOT_LAST = hard_issue :CAPTURES_REST_NOT_LAST, :param_name do\n    _(\"Parameter $%{param} is not last, and has 'captures rest'\") % { param: param_name }\n  end\n\n  CAPTURES_REST_NOT_SUPPORTED = hard_issue :CAPTURES_REST_NOT_SUPPORTED, :container, :param_name do\n    _(\"Parameter $%{param} has 'captures rest' - not supported in %{container}\") % { param: param_name, container: label.a_an(container) }\n  end\n\n  REQUIRED_PARAMETER_AFTER_OPTIONAL = hard_issue :REQUIRED_PARAMETER_AFTER_OPTIONAL, :param_name do\n    _(\"Parameter $%{param} is required but appears after optional parameters\") % { param: param_name }\n  end\n\n  MISSING_REQUIRED_PARAMETER = hard_issue :MISSING_REQUIRED_PARAMETER, :param_name do\n    _(\"Parameter $%{param} is required but no value was given\") % { param: param_name }\n  end\n\n  NOT_NUMERIC = issue :NOT_NUMERIC, :value do\n    _(\"The value '%{value}' cannot be converted to Numeric.\") % { value: value }\n  end\n\n  NUMERIC_COERCION = issue :NUMERIC_COERCION, :before, :after do\n    _(\"The string '%{before}' was automatically coerced to the numerical value %{after}\") % { before: before, after: after }\n  end\n\n  UNKNOWN_FUNCTION = issue :UNKNOWN_FUNCTION, :name do\n    _(\"Unknown function: '%{name}'.\") % { name: name }\n  end\n\n  UNKNOWN_VARIABLE = issue :UNKNOWN_VARIABLE, :name do\n    _(\"Unknown variable: '%{name}'.\") % { name: name }\n  end\n\n  RUNTIME_ERROR = issue :RUNTIME_ERROR, :detail do\n    _(\"Error while evaluating %{expression}, %{detail}\") % { expression: label.a_an(semantic), detail: detail }\n  end\n\n  UNKNOWN_RESOURCE_TYPE = issue :UNKNOWN_RESOURCE_TYPE, :type_name do\n    _(\"Resource type not found: %{res_type}\") % { res_type: type_name }\n  end\n\n  ILLEGAL_RESOURCE_TYPE = hard_issue :ILLEGAL_RESOURCE_TYPE, :actual do\n    _(\"Illegal Resource Type expression, expected result to be a type name, or untitled Resource, got %{actual}\") % { actual: actual }\n  end\n\n  DUPLICATE_TITLE = issue :DUPLICATE_TITLE, :title do\n    _(\"The title '%{title}' has already been used in this resource expression\") % { title: title }\n  end\n\n  DUPLICATE_ATTRIBUTE = issue :DUPLICATE_ATTRIBUE, :attribute  do\n    _(\"The attribute '%{attribute}' has already been set\") % { attribute: attribute }\n  end\n\n  MISSING_TITLE = hard_issue :MISSING_TITLE do\n    _(\"Missing title. The title expression resulted in undef\")\n  end\n\n  MISSING_TITLE_AT = hard_issue :MISSING_TITLE_AT, :index do\n    _(\"Missing title at index %{index}. The title expression resulted in an undef title\") % { index: index }\n  end\n\n  ILLEGAL_TITLE_TYPE_AT = hard_issue :ILLEGAL_TITLE_TYPE_AT, :index, :actual do\n    _(\"Illegal title type at index %{index}. Expected String, got %{actual}\") % { index: index, actual: actual }\n  end\n\n  EMPTY_STRING_TITLE_AT = hard_issue :EMPTY_STRING_TITLE_AT, :index do\n    _(\"Empty string title at %{index}. Title strings must have a length greater than zero.\") % { index: index }\n  end\n\n  UNKNOWN_RESOURCE = issue :UNKNOWN_RESOURCE, :type_name, :title do\n    _(\"Resource not found: %{type_name}['%{title}']\") % { type_name: type_name, title: title }\n  end\n\n  UNKNOWN_RESOURCE_PARAMETER = issue :UNKNOWN_RESOURCE_PARAMETER, :type_name, :title, :param_name do\n    _(\"The resource %{type_name}['%{title}'] does not have a parameter called '%{param}'\") % { type_name: type_name.capitalize, title: title, param: param_name }\n  end\n\n  DIV_BY_ZERO = hard_issue :DIV_BY_ZERO do\n    _(\"Division by 0\")\n  end\n\n  RESULT_IS_INFINITY = hard_issue :RESULT_IS_INFINITY, :operator do\n    _(\"The result of the %{operator} expression is Infinity\") % { operator: operator }\n  end\n\n  # TODO_HEREDOC\n  EMPTY_HEREDOC_SYNTAX_SEGMENT = issue :EMPTY_HEREDOC_SYNTAX_SEGMENT, :syntax do\n    _(\"Heredoc syntax specification has empty segment between '+' : '%{syntax}'\") % { syntax: syntax }\n  end\n\n  ILLEGAL_EPP_PARAMETERS = issue :ILLEGAL_EPP_PARAMETERS do\n    _(\"Ambiguous EPP parameter expression. Probably missing '<%-' before parameters to remove leading whitespace\")\n  end\n\n  DISCONTINUED_IMPORT = hard_issue :DISCONTINUED_IMPORT do\n    # TRANSLATORS \"import\" is a function name and should not be translated\n    _(\"Use of 'import' has been discontinued in favor of a manifest directory. See http://links.puppet.com/puppet-import-deprecation\")\n  end\n\n  IDEM_EXPRESSION_NOT_LAST = issue :IDEM_EXPRESSION_NOT_LAST do\n    _(\"This %{expression} has no effect. A value was produced and then forgotten (one or more preceding expressions may have the wrong form)\") % { expression: label.label(semantic) }\n  end\n\n  RESOURCE_WITHOUT_TITLE = issue :RESOURCE_WITHOUT_TITLE, :name do\n    _(\"This expression is invalid. Did you try declaring a '%{name}' resource without a title?\") % { name: name }\n  end\n\n  IDEM_NOT_ALLOWED_LAST = hard_issue :IDEM_NOT_ALLOWED_LAST, :container do\n    _(\"This %{expression} has no effect. %{container} can not end with a value-producing expression without other effect\") % { expression: label.label(semantic), container: label.a_an_uc(container) }\n  end\n\n  RESERVED_WORD = hard_issue :RESERVED_WORD, :word do\n    _(\"Use of reserved word: %{word}, must be quoted if intended to be a String value\") % { word: word }\n  end\n\n  FUTURE_RESERVED_WORD = issue :FUTURE_RESERVED_WORD, :word do\n    _(\"Use of future reserved word: '%{word}'\") % { word: word }\n  end\n\n  RESERVED_TYPE_NAME = hard_issue :RESERVED_TYPE_NAME, :name do\n    _(\"The name: '%{name}' is already defined by Puppet and can not be used as the name of %{expression}.\") % { name: name, expression: label.a_an(semantic) }\n  end\n\n  UNMATCHED_SELECTOR = hard_issue :UNMATCHED_SELECTOR, :param_value do\n    _(\"No matching entry for selector parameter with value '%{param}'\") % { param: param_value }\n  end\n\n  ILLEGAL_NODE_INHERITANCE = issue :ILLEGAL_NODE_INHERITANCE do\n    _(\"Node inheritance is not supported in Puppet >= 4.0.0. See http://links.puppet.com/puppet-node-inheritance-deprecation\")\n  end\n\n  ILLEGAL_OVERRIDDEN_TYPE = issue :ILLEGAL_OVERRIDDEN_TYPE, :actual do\n    _(\"Resource Override can only operate on resources, got: %{actual}\") % { actual: label.label(actual) }\n  end\n\n  DUPLICATE_PARAMETER = hard_issue :DUPLICATE_PARAMETER, :param_name do\n    _(\"The parameter '%{param}' is declared more than once in the parameter list\") % { param: param_name }\n  end\n\n  DUPLICATE_KEY = issue :DUPLICATE_KEY, :key do\n    _(\"The key '%{key}' is declared more than once\") % { key: key }\n  end\n\n  DUPLICATE_DEFAULT = hard_issue :DUPLICATE_DEFAULT, :container do\n    _(\"This %{container} already has a 'default' entry - this is a duplicate\") % { container: label.label(container) }\n  end\n\n  RESERVED_PARAMETER = hard_issue :RESERVED_PARAMETER, :container, :param_name do\n    _(\"The parameter $%{param} redefines a built in parameter in %{container}\") % { param: param_name, container: label.the(container) }\n  end\n\n  TYPE_MISMATCH = hard_issue :TYPE_MISMATCH, :expected, :actual do\n    _(\"Expected value of type %{expected}, got %{actual}\") % { expected: expected, actual: actual }\n  end\n\n  MULTIPLE_ATTRIBUTES_UNFOLD = hard_issue :MULTIPLE_ATTRIBUTES_UNFOLD do\n    _(\"Unfolding of attributes from Hash can only be used once per resource body\")\n  end\n\n  ILLEGAL_CATALOG_RELATED_EXPRESSION = hard_issue :ILLEGAL_CATALOG_RELATED_EXPRESSION do\n    _(\"This %{expression} appears in a context where catalog related expressions are not allowed\") % { expression: label.label(semantic) }\n  end\n\n  SYNTAX_ERROR = hard_issue :SYNTAX_ERROR, :where do\n    _(\"Syntax error at %{location}\") % { location: where }\n  end\n\n  ILLEGAL_CLASS_REFERENCE = hard_issue :ILLEGAL_CLASS_REFERENCE do\n    _('Illegal class reference')\n  end\n\n  ILLEGAL_FULLY_QUALIFIED_CLASS_REFERENCE = hard_issue :ILLEGAL_FULLY_QUALIFIED_CLASS_REFERENCE do\n    _('Illegal fully qualified class reference')\n  end\n\n  ILLEGAL_FULLY_QUALIFIED_NAME = hard_issue :ILLEGAL_FULLY_QUALIFIED_NAME do\n    _('Illegal fully qualified name')\n  end\n\n  ILLEGAL_NAME_OR_BARE_WORD = hard_issue :ILLEGAL_NAME_OR_BARE_WORD do\n    _('Illegal name or bare word')\n  end\n\n  ILLEGAL_NUMBER = hard_issue :ILLEGAL_NUMBER, :value do\n    _(\"Illegal number '%{value}'\") % { value: value }\n  end\n\n  ILLEGAL_UNICODE_ESCAPE = issue :ILLEGAL_UNICODE_ESCAPE do\n    _(\"Unicode escape '\\\\u' was not followed by 4 hex digits or 1-6 hex digits in {} or was > 10ffff\")\n  end\n\n  INVALID_HEX_NUMBER = hard_issue :INVALID_HEX_NUMBER, :value do\n    _(\"Not a valid hex number %{value}\") % { value: value }\n  end\n\n  INVALID_OCTAL_NUMBER = hard_issue :INVALID_OCTAL_NUMBER, :value do\n    _(\"Not a valid octal number %{value}\") % { value: value }\n  end\n\n  INVALID_DECIMAL_NUMBER = hard_issue :INVALID_DECIMAL_NUMBER, :value do\n    _(\"Not a valid decimal number %{value}\") % { value: value }\n  end\n\n  NO_INPUT_TO_LEXER = hard_issue :NO_INPUT_TO_LEXER do\n    _(\"Internal Error: No string or file given to lexer to process.\")\n  end\n\n  UNRECOGNIZED_ESCAPE = issue :UNRECOGNIZED_ESCAPE, :ch do\n    _(\"Unrecognized escape sequence '\\\\%{ch}'\") % { ch: ch }\n  end\n\n  UNCLOSED_QUOTE = hard_issue :UNCLOSED_QUOTE, :after, :followed_by do\n    _(\"Unclosed quote after %{after} followed by '%{followed_by}'\") % { after: after, followed_by: followed_by }\n  end\n\n  UNCLOSED_MLCOMMENT = hard_issue :UNCLOSED_MLCOMMENT do\n    _('Unclosed multiline comment')\n  end\n\n  EPP_INTERNAL_ERROR = hard_issue :EPP_INTERNAL_ERROR, :error do\n    _(\"Internal error: %{error}\") % { error: error }\n  end\n\n  EPP_UNBALANCED_TAG = hard_issue :EPP_UNBALANCED_TAG do\n    _('Unbalanced epp tag, reached <eof> without closing tag.')\n  end\n\n  EPP_UNBALANCED_COMMENT = hard_issue :EPP_UNBALANCED_COMMENT do\n    _('Reaching end after opening <%# without seeing %>')\n  end\n\n  EPP_UNBALANCED_EXPRESSION = hard_issue :EPP_UNBALANCED_EXPRESSION do\n    _('Unbalanced embedded expression - opening <% and reaching end of input')\n  end\n\n  HEREDOC_UNCLOSED_PARENTHESIS = hard_issue :HEREDOC_UNCLOSED_PARENTHESIS, :followed_by do\n    _(\"Unclosed parenthesis after '@(' followed by '%{followed_by}'\") % { followed_by: followed_by }\n  end\n\n  HEREDOC_WITHOUT_END_TAGGED_LINE = hard_issue :HEREDOC_WITHOUT_END_TAGGED_LINE do\n    _('Heredoc without end-tagged line')\n  end\n\n  HEREDOC_INVALID_ESCAPE = hard_issue :HEREDOC_INVALID_ESCAPE, :actual do\n    _(\"Invalid heredoc escape char. Only t, r, n, s,  u, L, $ allowed. Got '%{actual}'\") % { actual: actual }\n  end\n\n  HEREDOC_INVALID_SYNTAX = hard_issue :HEREDOC_INVALID_SYNTAX do\n    _('Invalid syntax in heredoc expected @(endtag[:syntax][/escapes])')\n  end\n\n  HEREDOC_WITHOUT_TEXT = hard_issue :HEREDOC_WITHOUT_TEXT do\n    _('Heredoc without any following lines of text')\n  end\n\n  HEREDOC_EMPTY_ENDTAG = hard_issue :HEREDOC_EMPTY_ENDTAG do\n    _('Heredoc with an empty endtag')\n  end\n\n  HEREDOC_MULTIPLE_AT_ESCAPES = hard_issue :HEREDOC_MULTIPLE_AT_ESCAPES, :escapes do\n    _(\"An escape char for @() may only appear once. Got '%{escapes}'\") % { escapes: escapes.join(', ') }\n  end\n\n  HEREDOC_DIRTY_MARGIN = hard_issue :HEREDOC_DIRTY_MARGIN, :heredoc_line do\n    _(\"Heredoc with text in the margin is not allowed (line %{heredoc_line} in this heredoc)\") % { heredoc_line: heredoc_line }\n  end\n\n  ILLEGAL_BOM = hard_issue :ILLEGAL_BOM, :format_name, :bytes do\n    _(\"Illegal %{format} Byte Order mark at beginning of input: %{bom} - remove these from the puppet source\") % { format: format_name, bom: bytes }\n  end\n\n  NO_SUCH_FILE_OR_DIRECTORY = hard_issue :NO_SUCH_FILE_OR_DIRECTORY, :file do\n    _('No such file or directory: %{file}') % { file: file }\n  end\n\n  NOT_A_FILE = hard_issue :NOT_A_FILE, :file do\n    _('%{file} is not a file') % { file: file }\n  end\n\n  NUMERIC_OVERFLOW = hard_issue :NUMERIC_OVERFLOW, :value do\n    if value > 0\n      _(\"%{expression} resulted in a value outside of Puppet Integer max range, got '%{value}'\") % { expression: label.a_an_uc(semantic), value: (\"%#+x\" % value) }\n    else\n      _(\"%{expression} resulted in a value outside of Puppet Integer min range, got '%{value}'\") % { expression: label.a_an_uc(semantic), value: (\"%#+x\" % value) }\n    end\n  end\n\n  HIERA_UNSUPPORTED_VERSION = hard_issue :HIERA_UNSUPPORTED_VERSION, :version do\n    _(\"This runtime does not support hiera.yaml version %{version}\") % { version: version }\n  end\n\n  HIERA_VERSION_3_NOT_GLOBAL = hard_issue :HIERA_VERSION_3_NOT_GLOBAL, :where do\n    _(\"hiera.yaml version 3 cannot be used in %{location}\") % { location: label.a_an(where) }\n  end\n\n  HIERA_UNSUPPORTED_VERSION_IN_GLOBAL = hard_issue :HIERA_UNSUPPORTED_VERSION_IN_GLOBAL do\n    _('hiera.yaml version 4 cannot be used in the global layer')\n  end\n\n  HIERA_UNDEFINED_VARIABLE = hard_issue :HIERA_UNDEFINED_VARIABLE, :name do\n    _(\"Undefined variable '%{name}'\") % { name: name }\n  end\n\n  HIERA_BACKEND_MULTIPLY_DEFINED = hard_issue :HIERA_BACKEND_MULTIPLY_DEFINED, :name, :first_line do\n    msg = _(\"Backend '%{name}' is defined more than once.\") % { name: name }\n    fl = first_line\n    if fl\n      msg += ' ' + _(\"First defined at %{error_location}\") % { error_location: Puppet::Util::Errors.error_location(nil, fl) }\n    end\n    msg\n  end\n\n  HIERA_NO_PROVIDER_FOR_BACKEND = hard_issue :HIERA_NO_PROVIDER_FOR_BACKEND, :name do\n    _(\"No data provider is registered for backend '%{name}'\") % { name: name }\n  end\n\n  HIERA_HIERARCHY_NAME_MULTIPLY_DEFINED = hard_issue :HIERA_HIERARCHY_NAME_MULTIPLY_DEFINED, :name, :first_line do\n    msg = _(\"Hierarchy name '%{name}' defined more than once.\") % { name: name }\n    fl = first_line\n    if fl\n      msg += ' ' + _(\"First defined at %{error_location}\") % { error_location: Puppet::Util::Errors.error_location(nil, fl) }\n    end\n    msg\n  end\n\n  HIERA_V3_BACKEND_NOT_GLOBAL = hard_issue :HIERA_V3_BACKEND_NOT_GLOBAL do\n    _(\"'hiera3_backend' is only allowed in the global layer\")\n  end\n\n  HIERA_DEFAULT_HIERARCHY_NOT_IN_MODULE = hard_issue :HIERA_DEFAULT_HIERARCHY_NOT_IN_MODULE do\n    _(\"'default_hierarchy' is only allowed in the module layer\")\n  end\n\n  HIERA_V3_BACKEND_REPLACED_BY_DATA_HASH = hard_issue :HIERA_V3_BACKEND_REPLACED_BY_DATA_HASH, :function_name do\n    _(\"Use \\\"data_hash: %{function_name}_data\\\" instead of \\\"hiera3_backend: %{function_name}\\\"\") % { function_name: function_name }\n  end\n\n  HIERA_MISSING_DATA_PROVIDER_FUNCTION = hard_issue :HIERA_MISSING_DATA_PROVIDER_FUNCTION, :name do\n    _(\"One of %{keys} must be defined in hierarchy '%{name}'\") % { keys: label.combine_strings(Lookup::HieraConfig::FUNCTION_KEYS), name: name }\n  end\n\n  HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS = hard_issue :HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS, :name do\n    _(\"Only one of %{keys} can be defined in hierarchy '%{name}'\") % { keys: label.combine_strings(Lookup::HieraConfig::FUNCTION_KEYS), name: name }\n  end\n\n  HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS_IN_DEFAULT = hard_issue :HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS_IN_DEFAULT do\n    _(\"Only one of %{keys} can be defined in defaults\") % { keys: label.combine_strings(Lookup::HieraConfig::FUNCTION_KEYS) }\n  end\n\n  HIERA_MULTIPLE_LOCATION_SPECS = hard_issue :HIERA_MULTIPLE_LOCATION_SPECS, :name do\n    _(\"Only one of %{keys} can be defined in hierarchy '%{name}'\") % { keys: label.combine_strings(Lookup::HieraConfig::LOCATION_KEYS), name: name }\n  end\n\n  HIERA_OPTION_RESERVED_BY_PUPPET = hard_issue :HIERA_OPTION_RESERVED_BY_PUPPET, :key, :name do\n    _(\"Option key '%{key}' used in hierarchy '%{name}' is reserved by Puppet\") % { key: key, name: name }\n  end\n\n  HIERA_DEFAULT_OPTION_RESERVED_BY_PUPPET = hard_issue :HIERA_DEFAULT_OPTION_RESERVED_BY_PUPPET, :key do\n    _(\"Option key '%{key}' used in defaults is reserved by Puppet\") % { key: key }\n  end\n\n  HIERA_DATA_PROVIDER_FUNCTION_NOT_FOUND = hard_issue :HIERA_DATA_PROVIDER_FUNCTION_NOT_FOUND, :function_type, :function_name do\n    _(\"Unable to find '%{function_type}' function named '%{function_name}'\") % { function_type: function_type, function_name: function_name }\n  end\n\n  HIERA_INTERPOLATION_ALIAS_NOT_ENTIRE_STRING = hard_issue :HIERA_INTERPOLATION_ALIAS_NOT_ENTIRE_STRING do\n    _(\"'alias' interpolation is only permitted if the expression is equal to the entire string\")\n  end\n\n  HIERA_INTERPOLATION_UNKNOWN_INTERPOLATION_METHOD = hard_issue :HIERA_INTERPOLATION_UNKNOWN_INTERPOLATION_METHOD, :name do\n    _(\"Unknown interpolation method '%{name}'\") % { name: name }\n  end\n\n  HIERA_INTERPOLATION_METHOD_SYNTAX_NOT_ALLOWED = hard_issue :HIERA_INTERPOLATION_METHOD_SYNTAX_NOT_ALLOWED do\n    _('Interpolation using method syntax is not allowed in this context')\n  end\n\n  SERIALIZATION_ENDLESS_RECURSION = hard_issue :SERIALIZATION_ENDLESS_RECURSION, :type_name do\n    _('Endless recursion detected when attempting to serialize value of class %{type_name}') % { :type_name => type_name }\n  end\n\n  SERIALIZATION_DEFAULT_CONVERTED_TO_STRING = issue :SERIALIZATION_DEFAULT_CONVERTED_TO_STRING, :path, :klass, :value do\n    _(\"%{path} contains the special value default. It will be converted to the String 'default'\") % { path: path }\n  end\n\n  SERIALIZATION_UNKNOWN_CONVERTED_TO_STRING = issue :SERIALIZATION_UNKNOWN_CONVERTED_TO_STRING, :path, :klass, :value do\n    _(\"%{path} contains %{klass} value. It will be converted to the String '%{value}'\") % { path: path, klass: label.a_an(klass), value: value }\n  end\n\n  SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING = issue :SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING, :path, :klass, :value do\n    _(\"%{path} contains a hash with %{klass} key. It will be converted to the String '%{value}'\") % { path: path, klass: label.a_an(klass), value: value }\n  end\n\n  FEATURE_NOT_SUPPORTED_WHEN_SCRIPTING = issue :NOT_SUPPORTED_WHEN_SCRIPTING, :feature do\n    _(\"The feature '%{feature}' is only available when compiling a catalog\") % { feature: feature }\n  end\n\n  CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING = issue :CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING, :operation do\n    _(\"The catalog operation '%{operation}' is only available when compiling a catalog\") % { operation: operation }\n  end\n\n  EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING = issue :EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING, :klass do\n    _(\"%{expr} is only available when compiling a catalog\") % { expr: label.a_an_uc(klass) }\n  end\n\n  TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING = issue :TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, :operation do\n    _(\"The task operation '%{operation}' is not available when compiling a catalog\") % { operation: operation }\n  end\n\n  EXPRESSION_NOT_SUPPORTED_WHEN_COMPILING = issue :EXPRESSION_NOT_SUPPORTED_WHEN_COMPILING, :klass do\n    _(\"%{expr} is not available when compiling a catalog\") % { expr: label.a_an_uc(klass) }\n  end\n\n  TASK_MISSING_BOLT = issue :TASK_MISSING_BOLT, :action do\n    _(\"The 'bolt' library is required to %{action}\") % { action: action }\n  end\n\n  UNKNOWN_TASK = issue :UNKNOWN_TASK, :type_name do\n    _('Task not found: %{type_name}') % { type_name: type_name }\n  end\n\n  LOADER_FAILURE = issue :LOADER_FAILURE, :type do\n    _('Failed to load: %{type_name}') % { type: type }\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/label_provider.rb",
    "content": "# frozen_string_literal: true\n\n# Provides a label for an object.\n# This simple implementation calls #to_s on the given object, and handles articles 'a/an/the'.\n#\nmodule Puppet::Pops::LabelProvider\n  VOWELS = %w[a e i o u y]\n  SKIPPED_CHARACTERS = %w[\" ']\n  A = \"a\"\n  AN = \"an\"\n\n  # Provides a label for the given object by calling `to_s` on the object.\n  # The intent is for this method to be overridden in concrete label providers.\n  def label o\n    o.to_s\n  end\n\n  # Produces a label for the given text with indefinite article (a/an)\n  def a_an o\n    text = label(o)\n    \"#{article(text)} #{text}\"\n  end\n\n  # Produces a label for the given text with indefinite article (A/An)\n  def a_an_uc o\n    text = label(o)\n    \"#{article(text).capitalize} #{text}\"\n  end\n\n  # Produces a label for the given text with *definite article* (the).\n  def the o\n    \"the #{label(o)}\"\n  end\n\n  # Produces a label for the given text with *definite article* (The).\n  def the_uc o\n    \"The #{label(o)}\"\n  end\n\n  # Appends 's' to (optional) text if count != 1 else an empty string\n  def plural_s(count, text = '')\n    count == 1 ? text : \"#{text}s\"\n  end\n\n  # Combines several strings using commas and a final conjunction\n  def combine_strings(strings, conjunction = 'or')\n    case strings.size\n    when 0\n      ''\n    when 1\n      strings[0]\n    when 2\n      \"#{strings[0]} #{conjunction} #{strings[1]}\"\n    else\n      \"#{strings[0...-1].join(', ')}, #{conjunction} #{strings.last}\"\n    end\n  end\n\n  # Produces an *indefinite article* (a/an) for the given text ('a' if\n  # it starts with a vowel) This is obviously flawed in the general\n  # sense as may labels have punctuation at the start and this method\n  # does not translate punctuation to English words. Also, if a vowel is\n  # pronounced as a consonant, the article should not be \"an\".\n  #\n  def article s\n    article_for_letter(first_letter_of(s))\n  end\n\n  private\n\n  def first_letter_of(string)\n    char = string[0, 1]\n    if SKIPPED_CHARACTERS.include? char\n      char = string[1, 1]\n    end\n\n    if char == \"\"\n      raise Puppet::DevError, _(\"<%{string}> does not appear to contain a word\") % { string: string }\n    end\n\n    char\n  end\n\n  def article_for_letter(letter)\n    downcased = letter.downcase\n    if VOWELS.include? downcased\n      AN\n    else\n      A\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/base_loader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# BaseLoader\n# ===\n# An abstract implementation of Loader\n#\n# A derived class should implement `find(typed_name)` and set entries, and possible handle \"miss caching\".\n#\n# @api private\n#\nclass BaseLoader < Loader\n  # The parent loader\n  attr_reader :parent\n\n  def initialize(parent_loader, loader_name, environment)\n    super(loader_name, environment)\n    @parent = parent_loader # the higher priority loader to consult\n    @named_values = {}      # hash name => NamedEntry\n    @last_result = nil      # the value of the last name (optimization)\n  end\n\n  def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block)\n    result = []\n    @named_values.each_pair do |key, entry|\n      result << key unless entry.nil? || entry.value.nil? || key.type != type || (block_given? && !yield(key))\n    end\n    result.concat(parent.discover(type, error_collector, name_authority, &block))\n    result.uniq!\n    result\n  end\n\n  # @api public\n  #\n  def load_typed(typed_name)\n    # The check for \"last queried name\" is an optimization when a module searches. First it checks up its parent\n    # chain, then itself, and then delegates to modules it depends on.\n    # These modules are typically parented by the same\n    # loader as the one initiating the search. It is inefficient to again try to search the same loader for\n    # the same name.\n    synchronize do\n      if @last_result.nil? || typed_name != @last_result.typed_name\n        @last_result = internal_load(typed_name)\n      else\n        @last_result\n      end\n    end\n  end\n\n  # @api public\n  #\n  def loaded_entry(typed_name, check_dependencies = false)\n    synchronize do\n      if @named_values.has_key?(typed_name)\n        @named_values[typed_name]\n      elsif parent\n        parent.loaded_entry(typed_name, check_dependencies)\n      else\n        nil\n      end\n    end\n  end\n\n  # This method is final (subclasses should not override it)\n  #\n  # @api private\n  #\n  def get_entry(typed_name)\n    @named_values[typed_name]\n  end\n\n  # @api private\n  #\n  def set_entry(typed_name, value, origin = nil)\n    synchronize do\n      # It is never ok to redefine in the very same loader unless redefining a 'not found'\n      entry = @named_values[typed_name]\n      if entry\n        fail_redefine(entry) unless entry.value.nil?\n      end\n\n      # Check if new entry shadows existing entry and fail\n      # (unless special loader allows shadowing)\n      if typed_name.type == :type && !allow_shadowing?\n        entry = loaded_entry(typed_name)\n        if entry\n          fail_redefine(entry) unless entry.value.nil? # || entry.value == value\n        end\n      end\n\n      @last_result = Loader::NamedEntry.new(typed_name, value, origin)\n      @named_values[typed_name] = @last_result\n    end\n  end\n\n  # @api private\n  #\n  def add_entry(type, name, value, origin)\n    set_entry(TypedName.new(type, name), value, origin)\n  end\n\n  # @api private\n  #\n  def remove_entry(typed_name)\n    synchronize do\n      unless @named_values.delete(typed_name).nil?\n        @last_result = nil unless @last_result.nil? || typed_name != @last_result.typed_name\n      end\n    end\n  end\n\n  # Promotes an already created entry (typically from another loader) to this loader\n  #\n  # @api private\n  #\n  def promote_entry(named_entry)\n    synchronize do\n      typed_name = named_entry.typed_name\n      entry = @named_values[typed_name]\n      if entry then fail_redefine(entry); end\n      @named_values[typed_name] = named_entry\n    end\n  end\n\n  protected\n\n  def allow_shadowing?\n    false\n  end\n\n  private\n\n  def fail_redefine(entry)\n    origin_info = entry.origin ? _(\"Originally set %{original}.\") % { original: origin_label(entry.origin) } : _(\"Set at unknown location\")\n    raise ArgumentError, _(\"Attempt to redefine entity '%{name}'. %{origin_info}\") % { name: entry.typed_name, origin_info: origin_info }\n  end\n\n  # TODO: Should not really be here?? - TODO: A Label provider ? semantics for the URI?\n  #\n  def origin_label(origin)\n    if origin && origin.is_a?(URI)\n      format_uri(origin)\n    elsif origin.respond_to?(:uri)\n      format_uri(origin.uri)\n    else\n      origin\n    end\n  end\n\n  def format_uri(uri)\n    (uri.scheme == 'puppet' ? 'by ' : 'at ') + uri.to_s.sub(/^puppet:/, '')\n  end\n\n  # loads in priority order:\n  # 1. already loaded here\n  # 2. load from parent\n  # 3. find it here\n  # 4. give up\n  #\n  def internal_load(typed_name)\n    # avoid calling get_entry by looking it up\n    te = @named_values[typed_name]\n    return te unless te.nil? || te.value.nil?\n\n    te = parent.load_typed(typed_name)\n    return te unless te.nil? || te.value.nil?\n\n    # Under some circumstances, the call to the parent loader will have resulted in files being\n    # parsed that in turn contained references to the requested entity and hence, caused a\n    # recursive call into this loader. This means that the entry might be present now, so a new\n    # check must be made.\n    te = @named_values[typed_name]\n    te.nil? || te.value.nil? ? find(typed_name) : te\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/dependency_loader.rb",
    "content": "# frozen_string_literal: true\n\n# =DependencyLoader\n# This loader provides visibility into a set of other loaders. It is used as a child of a ModuleLoader (or other\n# loader) to make its direct dependencies visible for loading from contexts that have access to this dependency loader.\n# Access is typically given to logic that resides inside of the module, but not to those that just depend on the module.\n#\n# It is instantiated with a name, and with a set of dependency_loaders.\n#\n# @api private\n#\nclass Puppet::Pops::Loader::DependencyLoader < Puppet::Pops::Loader::BaseLoader\n  # Creates a DependencyLoader for one parent loader\n  #\n  # @param parent_loader [Puppet::Pops::Loader] typically a module loader for the root\n  # @param name [String] the name of the dependency-loader (used for debugging and tracing only)\n  # @param dependency_loaders [Array<Puppet::Pops::Loader>] array of loaders for modules this module depends on\n  #\n  def initialize(parent_loader, name, dependency_loaders, environment)\n    super(parent_loader, name, environment)\n    @dependency_loaders = dependency_loaders\n  end\n\n  def discover(type, error_collector = nil, name_authority = Puppet::Pops::Pcore::RUNTIME_NAME_AUTHORITY, &block)\n    result = []\n    @dependency_loaders.each { |loader| result.concat(loader.discover(type, error_collector, name_authority, &block)) }\n    result.concat(super)\n    result\n  end\n\n  # Finds name in a loader this loader depends on / can see\n  #\n  def find(typed_name)\n    if typed_name.qualified?\n      l = index()[typed_name.name_parts[0]]\n      if l\n        l.load_typed(typed_name)\n      else\n        # no module entered as dependency with name matching first segment of wanted name\n        nil\n      end\n    else\n      # a non name-spaced name, have to search since it can be anywhere.\n      # (Note: superclass caches the result in this loader as it would have to repeat this search for every\n      # lookup otherwise).\n      loaded = @dependency_loaders.reduce(nil) do |previous, loader|\n        break previous unless previous.nil?\n\n        loader.load_typed(typed_name)\n      end\n      if loaded\n        promote_entry(loaded)\n      end\n      loaded\n    end\n  end\n\n  # @api public\n  #\n  def loaded_entry(typed_name, check_dependencies = false)\n    super || (check_dependencies ? loaded_entry_in_dependency(typed_name, check_dependencies) : nil)\n  end\n\n  def to_s\n    \"(DependencyLoader '#{@loader_name}' [\" + @dependency_loaders.map(&:to_s).join(' ,') + \"])\"\n  end\n\n  private\n\n  def loaded_entry_in_dependency(typed_name, check_dependencies)\n    if typed_name.qualified?\n      l = index[typed_name.name_parts[0]]\n      if l\n        l.loaded_entry(typed_name)\n      else\n        # no module entered as dependency with name matching first segment of wanted name\n        nil\n      end\n    else\n      # a non name-spaced name, have to search since it can be anywhere.\n      # (Note: superclass caches the result in this loader as it would have to repeat this search for every\n      # lookup otherwise).\n      @dependency_loaders.reduce(nil) do |previous, loader|\n        break previous unless previous.nil?\n\n        loader.loaded_entry(typed_name, check_dependencies)\n      end\n    end\n  end\n\n  # An index of module_name to module loader used to speed up lookup of qualified names\n  def index\n    @index ||= @dependency_loaders.each_with_object({}) { |loader, index| index[loader.module_name] = loader; }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/gem_support.rb",
    "content": "# frozen_string_literal: true\n\n# GemSupport offers methods to find a gem's location by name or gem://gemname URI.\n#\n# TODO: The Puppet 3x, uses Puppet::Util::RubyGems to do this, and obtain paths, and avoids using ::Gems\n# when ::Bundler is in effect. A quick check what happens on Ruby 1.8.7 and Ruby 1.9.3 with current\n# version of bundler seems to work just fine without jumping through any hoops. Hopefully the Puppet::Utils::RubyGems is\n# just dealing with arcane things prior to RubyGems 1.8 that are not needed any more. To verify there is\n# the need to set up a scenario where additional bundles than what Bundler allows for a given configuration are available\n# and then trying to access those.\n#\nmodule Puppet::Pops::Loader::GemSupport\n  # Produces the root directory of a gem given as an URI (gem://gemname/optional/path), or just the\n  # gemname as a string.\n  #\n  def gem_dir(uri_or_string)\n    case uri_or_string\n    when URI\n      gem_dir_from_uri(uri_or_string)\n    when String\n      gem_dir_from_name(uri_or_string)\n    end\n  end\n\n  # Produces the root directory of a gem given as an uri, where hostname is the gemname, and an optional\n  # path is appended to the root of the gem (i.e. if the reference is given to a sub-location within a gem.\n  # TODO: FIND by name raises exception Gem::LoadError with list of all gems on the path\n  #\n  def gem_dir_from_uri(uri)\n    spec = Gem::Specification.find_by_name(uri.hostname)\n    unless spec\n      raise ArgumentError, _(\"Gem not found %{uri}\") % { uri: uri }\n    end\n\n    # if path given append that, else append given subdir\n    if uri.path.empty?\n      spec.gem_dir\n    else\n      File.join(spec.full_gem_path, uri.path)\n    end\n  end\n\n  # Produces the root directory of a gem given as a string with the gem's name.\n  # TODO: FIND by name raises exception Gem::LoadError with list of all gems on the path\n  #\n  def gem_dir_from_name(gem_name)\n    spec = Gem::Specification.find_by_name(gem_name)\n    unless spec\n      raise ArgumentError, _(\"Gem not found '%{gem_name}'\") % { gem_name: gem_name }\n    end\n\n    spec.full_gem_path\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/generic_plan_instantiator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# The GenericPlanInstantiator dispatches to either PuppetPlanInstantiator or a\n# yaml_plan_instantiator injected through the Puppet context, depending on\n# the type of the plan.\n#\nclass GenericPlanInstantiator\n  def self.create(loader, typed_name, source_refs)\n    if source_refs.length > 1\n      raise ArgumentError, _(\"Found multiple files for plan '%{plan_name}' but only one is allowed\") % { plan_name: typed_name.name }\n    end\n\n    source_ref = source_refs[0]\n    code_string = Puppet::FileSystem.read(source_ref, :encoding => 'utf-8')\n\n    instantiator = if source_ref.end_with?('.pp')\n                     Puppet::Pops::Loader::PuppetPlanInstantiator\n                   else\n                     Puppet.lookup(:yaml_plan_instantiator) do\n                       raise Puppet::DevError, _(\"No instantiator is available to load plan from %{source_ref}\") % { source_ref: source_ref }\n                     end\n                   end\n\n    instantiator.create(loader, typed_name, source_ref, code_string)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/loader.rb",
    "content": "# frozen_string_literal: true\n\n# Loader\n# ===\n# A Loader is responsible for loading \"entities\" (\"instantiable and executable objects in the puppet language\" which\n# are type, hostclass, definition, function, and bindings.\n#\n# The main method for users of a Loader is the `load` or `load_typed methods`, which returns a previously loaded entity\n# of a given type/name, and searches and loads the entity if not already loaded.\n#\n# private entities\n# ---\n# TODO: handle loading of entities that are private. Suggest that all calls pass an origin_loader (the loader\n# where request originated (or symbol :public). A module loader has one (or possibly a list) of what is\n# considered to represent private loader - i.e. the dependency loader for a module. If an entity is private\n# it should be stored with this status, and an error should be raised if the origin_loader is not on the list\n# of accepted \"private\" loaders.\n# The private loaders can not be given at creation time (they are parented by the loader in question). Another\n# alternative is to check if the origin_loader is a child loader, but this requires bidirectional links\n# between loaders or a search if loader with private entity is a parent of the origin_loader).\n#\n# @api public\n#\nmodule Puppet::Pops\nmodule Loader\nENVIRONMENT = 'environment'\nENVIRONMENT_PRIVATE = 'environment private'\n\nclass Loader\n  attr_reader :environment, :loader_name\n\n  # Describes the kinds of things that loaders can load\n  LOADABLE_KINDS = [:func_4x, :func_4xpp, :func_3x, :datatype, :type_pp, :resource_type_pp, :plan, :task].freeze\n\n  # @param [String] name the name of the loader. Must be unique among all loaders maintained by a {Loader} instance\n  def initialize(loader_name, environment)\n    @loader_name = loader_name.freeze\n    @environment = environment\n  end\n\n  # Search all places where this loader would find values of a given type and return a list the\n  # found values for which the given block returns true. All found entries will be returned if no\n  # block is given.\n  #\n  # Errors that occur function discovery will either be logged as warnings or collected by the optional\n  # `error_collector` array. When provided, it will receive {Puppet::DataTypes::Error} instances describing\n  # each error in detail and no warnings will be logged.\n  #\n  # @param type [Symbol] the type of values to search for\n  # @param error_collector [Array<Puppet::DataTypes::Error>] an optional array that will receive errors during load\n  # @param name_authority [String] the name authority, defaults to the pcore runtime\n  # @yield [typed_name] optional block to filter the results\n  # @yieldparam [TypedName] typed_name the typed name of a found entry\n  # @yieldreturn [Boolean] `true` to keep the entry, `false` to discard it.\n  # @return [Array<TypedName>] the list of names of discovered values\n  def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block)\n    EMPTY_ARRAY\n  end\n\n  # Produces the value associated with the given name if already loaded, or available for loading\n  # by this loader, one of its parents, or other loaders visible to this loader.\n  # This is the method an external party should use to \"get\" the named element.\n  #\n  # An implementor of this method should first check if the given name is already loaded by self, or a parent\n  # loader, and if so return that result. If not, it should call `find` to perform the loading.\n  #\n  # @param type [:Symbol] the type to load\n  # @param name [String, Symbol]  the name of the entity to load\n  # @return [Object, nil] the value or nil if not found\n  #\n  # @api public\n  #\n  def load(type, name)\n    synchronize do\n      result = load_typed(TypedName.new(type, name.to_s))\n      if result\n        result.value\n      end\n    end\n  end\n\n  # Loads the given typed name, and returns a NamedEntry if found, else returns nil.\n  # This the same a `load`, but returns a NamedEntry with origin/value information.\n  #\n  # @param typed_name [TypedName] - the type, name combination to lookup\n  # @return [NamedEntry, nil] the entry containing the loaded value, or nil if not found\n  #\n  # @api public\n  #\n  def load_typed(typed_name)\n    raise NotImplementedError, \"Class #{self.class.name} must implement method #load_typed\"\n  end\n\n  # Returns an already loaded entry if one exists, or nil. This does not trigger loading\n  # of the given type/name.\n  #\n  # @param typed_name [TypedName] - the type, name combination to lookup\n  # @param check_dependencies [Boolean] - if dependencies should be checked in addition to here and parent\n  # @return [NamedEntry, nil] the entry containing the loaded value, or nil if not found\n  # @api public\n  #\n  def loaded_entry(typed_name, check_dependencies = false)\n    raise NotImplementedError, \"Class #{self.class.name} must implement method #loaded_entry\"\n  end\n\n  # Produces the value associated with the given name if defined **in this loader**, or nil if not defined.\n  # This lookup does not trigger any loading, or search of the given name.\n  # An implementor of this method may not search or look up in any other loader, and it may not\n  # define the name.\n  #\n  # @param typed_name [TypedName] - the type, name combination to lookup\n  #\n  # @api private\n  #\n  def [](typed_name)\n    found = get_entry(typed_name)\n    if found\n      found.value\n    else\n      nil\n    end\n  end\n\n  # Searches for the given name in this loader's context (parents should already have searched their context(s) without\n  # producing a result when this method is called).\n  # An implementation of find typically caches the result.\n  #\n  # @param typed_name [TypedName] the type, name combination to lookup\n  # @return [NamedEntry, nil] the entry for the loaded entry, or nil if not found\n  #\n  # @api private\n  #\n  def find(typed_name)\n    raise NotImplementedError, \"Class #{self.class.name} must implement method #find\"\n  end\n\n  # Returns the parent of the loader, or nil, if this is the top most loader. This implementation returns nil.\n  def parent\n    nil\n  end\n\n  # Produces the private loader for loaders that have a one (the visibility given to loaded entities).\n  # For loaders that does not provide a private loader, self is returned.\n  #\n  # @api private\n  def private_loader\n    self\n  end\n\n  # Lock around a block\n  # This exists so some subclasses that are set up statically and don't actually\n  # load can override it\n  def synchronize(&block)\n    @environment.lock.synchronize(&block)\n  end\n\n  # Binds a value to a name. The name should not start with '::', but may contain multiple segments.\n  #\n  # @param type [:Symbol] the type of the entity being set\n  # @param name [String, Symbol] the name of the entity being set\n  # @param origin [URI, #uri, String] the origin of the set entity, a URI, or provider of URI, or URI in string form\n  # @return [NamedEntry, nil] the created entry\n  #\n  # @api private\n  #\n  def set_entry(type, name, value, origin = nil)\n    raise NotImplementedError\n  end\n\n  # Produces a NamedEntry if a value is bound to the given name, or nil if nothing is bound.\n  #\n  # @param typed_name [TypedName] the type, name combination to lookup\n  # @return [NamedEntry, nil] the value bound in an entry\n  #\n  # @api private\n  #\n  def get_entry(typed_name)\n    raise NotImplementedError\n  end\n\n  # A loader is by default a loader for all kinds of loadables. An implementation may override\n  # if it cannot load all kinds.\n  #\n  # @api private\n  def loadables\n    LOADABLE_KINDS\n  end\n\n  # A loader may want to implement its own version with more detailed information.\n  def to_s\n    loader_name\n  end\n\n  # Loaders may contain references to the environment they load items within.\n  # Consequently, calling Kernel#inspect may return strings that are large\n  # enough to cause OutOfMemoryErrors on some platforms.\n  #\n  # We do not call alias_method here as that would copy the content of to_s\n  # at this point to inspect (ie children would print out `loader_name`\n  # rather than their version of to_s if they chose to implement it).\n  def inspect\n    to_s\n  end\n\n  # An entry for one entity loaded by the loader.\n  #\n  class NamedEntry\n    attr_reader :typed_name\n    attr_reader :value\n    attr_reader :origin\n\n    def initialize(typed_name, value, origin)\n      @typed_name = typed_name\n      @value = value\n      @origin = origin\n      freeze()\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/loader_paths.rb",
    "content": "# frozen_string_literal: true\n\n# LoaderPaths\n# ===\n# The central loader knowledge about paths, what they represent and how to instantiate from them.\n# Contains helpers (*smart paths*) to deal with lazy resolution of paths.\n#\n# TODO: Currently only supports loading of functions (2 kinds)\n#\nmodule Puppet::Pops\nmodule Loader\nmodule LoaderPaths\n  # Returns an array of SmartPath, each instantiated with a reference to the given loader (for root path resolution\n  # and existence checks). The smart paths in the array appear in precedence order. The returned array may be\n  # mutated.\n  #\n  def self.relative_paths_for_type(type, loader)\n    result = []\n    case type\n    when :function\n      # Only include support for the loadable items the loader states it can contain\n      if loader.loadables.include?(:func_4x)\n        result << FunctionPath4x.new(loader)\n      end\n      if loader.loadables.include?(:func_4xpp)\n        result << FunctionPathPP.new(loader)\n      end\n      if loader.loadables.include?(:func_3x)\n        result << FunctionPath3x.new(loader)\n      end\n    when :plan\n      result << PlanPath.new(loader)\n    when :task\n      result << TaskPath.new(loader) if Puppet[:tasks] && loader.loadables.include?(:task)\n    when :type\n      result << DataTypePath.new(loader) if loader.loadables.include?(:datatype)\n      result << TypePathPP.new(loader) if loader.loadables.include?(:type_pp)\n    when :resource_type_pp\n      result << ResourceTypeImplPP.new(loader) if loader.loadables.include?(:resource_type_pp)\n    else\n      # unknown types, simply produce an empty result; no paths to check, nothing to find... move along...\n      []\n    end\n    result\n  end\n\n  #  # DO NOT REMOVE YET. needed later? when there is the need to decamel a classname\n  #  def de_camel(fq_name)\n  #    fq_name.to_s.gsub(/::/, '/').\n  #    gsub(/([A-Z]+)([A-Z][a-z])/,'\\1_\\2').\n  #    gsub(/([a-z\\d])([A-Z])/,'\\1_\\2').\n  #    tr(\"-\", \"_\").\n  #    downcase\n  #  end\n\n  class SmartPath\n    # Creates SmartPath for the given loader (loader knows how to check for existence etc.)\n    def initialize(loader)\n      @loader = loader\n    end\n\n    # Generic path, in the sense of \"if there are any entities of this kind to load, where are they?\"\n    def generic_path\n      return @generic_path unless @generic_path.nil?\n\n      the_root_path = root_path # @loader.path\n      @generic_path = (the_root_path.nil? ? relative_path : File.join(the_root_path, relative_path))\n    end\n\n    def fuzzy_matching?\n      false\n    end\n\n    def root_path\n      @loader.path\n    end\n\n    def lib_root?\n      @loader.lib_root?\n    end\n\n    # Effective path is the generic path + the name part(s) + extension.\n    #\n    def effective_path(typed_name, start_index_in_name)\n      \"#{File.join(generic_path, typed_name.name_parts)}#{extension}\"\n    end\n\n    def typed_name(type, name_authority, relative_path, module_name)\n      # Module name is assumed to be included in the path and therefore not added here\n      n = ''.dup\n      unless extension.empty?\n        # Remove extension\n        relative_path = relative_path[0..-(extension.length + 1)]\n      end\n      relative_path.split('/').each do |segment|\n        n << '::' if n.size > 0\n        n << segment\n      end\n      TypedName.new(type, n, name_authority)\n    end\n\n    def valid_path?(path)\n      path.end_with?(extension) && path.start_with?(generic_path)\n    end\n\n    def valid_name?(typed_name)\n      true\n    end\n\n    def relative_path\n      raise NotImplementedError\n    end\n\n    def instantiator\n      raise NotImplementedError\n    end\n  end\n\n  class RubySmartPath < SmartPath\n    EXTENSION = '.rb'\n\n    def extension\n      EXTENSION\n    end\n\n    # Duplication of extension information, but avoids one call\n    def effective_path(typed_name, start_index_in_name)\n      \"#{File.join(generic_path, typed_name.name_parts)}.rb\"\n    end\n  end\n\n  # A PuppetSmartPath is rooted at the loader's directory one level up from what the loader specifies as it\n  # path (which is a reference to its 'lib' directory.\n  #\n  class PuppetSmartPath < SmartPath\n    EXTENSION = '.pp'\n\n    def extension\n      EXTENSION\n    end\n\n    # Duplication of extension information, but avoids one call\n    def effective_path(typed_name, start_index_in_name)\n      # Puppet name to path always skips the name-space as that is part of the generic path\n      # i.e. <module>/mymodule/functions/foo.pp is the function mymodule::foo\n      parts = typed_name.name_parts\n      if start_index_in_name > 0\n        return nil if start_index_in_name >= parts.size\n\n        parts = parts[start_index_in_name..]\n      end\n      \"#{File.join(generic_path, parts)}#{extension}\"\n    end\n\n    def typed_name(type, name_authority, relative_path, module_name)\n      n = ''.dup\n      n << module_name unless module_name.nil?\n      unless extension.empty?\n        # Remove extension\n        relative_path = relative_path[0..-(extension.length + 1)]\n      end\n      relative_path.split('/').each do |segment|\n        n << '::' if n.size > 0\n        n << segment\n      end\n      TypedName.new(type, n, name_authority)\n    end\n  end\n\n  class FunctionPath4x < RubySmartPath\n    SYSTEM_FUNCTION_PATH_4X = File.join('puppet', 'functions').freeze\n    FUNCTION_PATH_4X = File.join('lib', SYSTEM_FUNCTION_PATH_4X).freeze\n\n    def relative_path\n      lib_root? ? SYSTEM_FUNCTION_PATH_4X : FUNCTION_PATH_4X\n    end\n\n    def instantiator\n      RubyFunctionInstantiator\n    end\n  end\n\n  class FunctionPath3x < RubySmartPath\n    SYSTEM_FUNCTION_PATH_3X = File.join('puppet', 'parser', 'functions').freeze\n    FUNCTION_PATH_3X = File.join('lib', SYSTEM_FUNCTION_PATH_3X).freeze\n\n    def relative_path\n      lib_root? ? SYSTEM_FUNCTION_PATH_3X : FUNCTION_PATH_3X\n    end\n\n    def instantiator\n      RubyLegacyFunctionInstantiator\n    end\n  end\n\n  class FunctionPathPP < PuppetSmartPath\n    FUNCTION_PATH_PP = 'functions'\n\n    def relative_path\n      FUNCTION_PATH_PP\n    end\n\n    def instantiator\n      PuppetFunctionInstantiator\n    end\n  end\n\n  class DataTypePath < RubySmartPath\n    SYSTEM_TYPE_PATH = File.join('puppet', 'datatypes').freeze\n    TYPE_PATH = File.join('lib', SYSTEM_TYPE_PATH).freeze\n\n    def relative_path\n      lib_root? ? SYSTEM_TYPE_PATH : TYPE_PATH\n    end\n\n    def instantiator\n      RubyDataTypeInstantiator\n    end\n  end\n\n  class TypePathPP < PuppetSmartPath\n    TYPE_PATH_PP = 'types'\n\n    def relative_path\n      TYPE_PATH_PP\n    end\n\n    def instantiator\n      TypeDefinitionInstantiator\n    end\n  end\n\n  # TaskPath is like PuppetSmartPath but it does not use an extension and may\n  # match more than one path with one name\n  class TaskPath < PuppetSmartPath\n    TASKS_PATH = 'tasks'\n    FORBIDDEN_EXTENSIONS = %w[.conf .md].freeze\n\n    def extension\n      EMPTY_STRING\n    end\n\n    def fuzzy_matching?\n      true\n    end\n\n    def relative_path\n      TASKS_PATH\n    end\n\n    def typed_name(type, name_authority, relative_path, module_name)\n      n = ''.dup\n      n << module_name unless module_name.nil?\n\n      # Remove the file extension, defined as everything after the *last* dot.\n      relative_path = relative_path.sub(%r{\\.[^/.]*\\z}, '')\n\n      if relative_path == 'init' && !(module_name.nil? || module_name.empty?)\n        TypedName.new(type, module_name, name_authority)\n      else\n        relative_path.split('/').each do |segment|\n          n << '::' if n.size > 0\n          n << segment\n        end\n        TypedName.new(type, n, name_authority)\n      end\n    end\n\n    def instantiator\n      require_relative 'task_instantiator'\n      TaskInstantiator\n    end\n\n    def valid_name?(typed_name)\n      # TODO: Remove when PE has proper namespace handling\n      typed_name.name_parts.size <= 2\n    end\n\n    def valid_path?(path)\n      path.start_with?(generic_path) && is_task_name?(File.basename(path, '.*')) && !FORBIDDEN_EXTENSIONS.any? { |ext| path.end_with?(ext) }\n    end\n\n    def is_task_name?(name)\n      !!(name =~ /^[a-z][a-z0-9_]*$/)\n    end\n  end\n\n  class ResourceTypeImplPP < PuppetSmartPath\n    RESOURCE_TYPES_PATH_PP = '.resource_types'\n\n    def relative_path\n      RESOURCE_TYPES_PATH_PP\n    end\n\n    def root_path\n      @loader.path\n    end\n\n    def instantiator\n      PuppetResourceTypeImplInstantiator\n    end\n\n    # The effect paths for resource type impl is the full name\n    # since resource types are not name spaced.\n    # This overrides the default PuppetSmartPath.\n    #\n    def effective_path(typed_name, start_index_in_name)\n      # Resource type to name does not skip the name-space\n      # i.e. <module>/mymodule/resource_types/foo.pp is the resource type foo\n      \"#{File.join(generic_path, typed_name.name_parts)}.pp\"\n    end\n  end\n\n  class PlanPath < PuppetSmartPath\n    PLAN_PATH = File.join('plans')\n    PP_EXT = '.pp'\n    YAML_EXT = '.yaml'\n\n    def initialize(loader)\n      super\n\n      if Puppet.lookup(:yaml_plan_instantiator) { nil }\n        @extensions = [PP_EXT, YAML_EXT]\n      else\n        @extensions = [PP_EXT]\n      end\n      @init_filenames = @extensions.map { |ext| \"init#{ext}\" }\n    end\n\n    def extension\n      EMPTY_STRING\n    end\n\n    def relative_path\n      PLAN_PATH\n    end\n\n    def instantiator\n      Puppet::Pops::Loader::GenericPlanInstantiator\n    end\n\n    def fuzzy_matching?\n      true\n    end\n\n    def valid_path?(path)\n      @extensions.any? { |ext| path.end_with?(ext) } && path.start_with?(generic_path)\n    end\n\n    def typed_name(type, name_authority, relative_path, module_name)\n      if @init_filenames.include?(relative_path) && !(module_name.nil? || module_name.empty?)\n        TypedName.new(type, module_name, name_authority)\n      else\n        n = ''.dup\n        n << module_name unless module_name.nil?\n        ext = @extensions.find { |extension| relative_path.end_with?(extension) }\n        relative_path = relative_path[0..-(ext.length + 1)]\n\n        relative_path.split('/').each do |segment|\n          n << '::' if n.size > 0\n          n << segment\n        end\n        TypedName.new(type, n, name_authority)\n      end\n    end\n\n    def effective_path(typed_name, start_index_in_name)\n      # Puppet name to path always skips the name-space as that is part of the generic path\n      # i.e. <module>/mymodule/functions/foo.pp is the function mymodule::foo\n      parts = typed_name.name_parts\n      if start_index_in_name > 0\n        return nil if start_index_in_name >= parts.size\n\n        parts = parts[start_index_in_name..]\n      end\n      basename = File.join(generic_path, parts)\n      @extensions.map { |ext| \"#{basename}#{ext}\" }\n    end\n  end\n\n  # SmartPaths\n  # ===\n  # Holds effective SmartPath instances per type\n  #\n  class SmartPaths\n    def initialize(path_based_loader)\n      @loader = path_based_loader\n      @smart_paths = {}\n    end\n\n    # Ensures that the paths for the type have been probed and pruned to what is existing relative to\n    # the given root.\n    #\n    # @param type [Symbol] the entity type to load\n    # @return [Array<SmartPath>] array of effective paths for type (may be empty)\n    #\n    def effective_paths(type)\n      smart_paths = @smart_paths\n      loader = @loader\n      effective_paths = smart_paths[type]\n      unless effective_paths\n        # type not yet processed, does the various directories for the type exist ?\n        # Get the relative dirs for the type\n        paths_for_type = LoaderPaths.relative_paths_for_type(type, loader)\n        # Check which directories exist in the loader's content/index\n        effective_paths = smart_paths[type] = paths_for_type.select { |sp| loader.meaningful_to_search?(sp) }\n      end\n      effective_paths\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/module_loaders.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# =ModuleLoaders\n# A ModuleLoader loads items from a single module.\n# The ModuleLoaders (ruby) module contains various such loaders. There is currently one concrete\n# implementation, ModuleLoaders::FileBased that loads content from the file system.\n# Other implementations can be created - if they are based on name to path mapping where the path\n# is relative to a root path, they can derive the base behavior from the ModuleLoaders::AbstractPathBasedModuleLoader class.\n#\n# Examples of such extensions could be a zip/jar/compressed file base loader.\n#\n# Notably, a ModuleLoader does not configure itself - it is given the information it needs (the root, its name etc.)\n# Logic higher up in the loader hierarchy of things makes decisions based on the \"shape of modules\", and \"available\n# modules\" to determine which module loader to use for each individual module. (There could be differences in\n# internal layout etc.)\n#\n# A module loader is also not aware of the mapping of name to relative paths.\n#\n# @api private\n#\nmodule ModuleLoaders\n  # Wildcard module name for module loaders, makes loading possible from any namespace.\n  NAMESPACE_WILDCARD = '*'\n\n  # This is exactly the same as the #system_loader_from method, but the argument for path is changed to\n  # location where pluginsync stores functions. It also accepts definitions in any namespace since pluginsync\n  # places all of them in the same directory.\n  #\n  def self.cached_loader_from(parent_loader, loaders)\n    LibRootedFileBased.new(parent_loader,\n                           loaders,\n                           NAMESPACE_WILDCARD,\n                           Puppet[:libdir],\n                           'cached_puppet_lib',\n                           [:func_4x, :func_3x, :datatype])\n  end\n\n  def self.system_loader_from(parent_loader, loaders)\n    # Puppet system may be installed in a fixed location via RPM, installed as a Gem, via source etc.\n    # The only way to find this across the different ways puppet can be installed is\n    # to search up the path from this source file's __FILE__ location until it finds the base of\n    # puppet.\n    #\n    puppet_lib = File.realpath(File.join(File.dirname(__FILE__), '../../..'))\n    LibRootedFileBased.new(parent_loader,\n                           loaders,\n                           nil,\n                           puppet_lib, # may or may not have a 'lib' above 'puppet'\n                           'puppet_system',\n                           [:func_4x, :func_3x, :datatype]) # only load ruby functions and types from \"puppet\"\n  end\n\n  def self.environment_loader_from(parent_loader, loaders, env_path)\n    if env_path.nil? || env_path.empty?\n      EmptyLoader.new(parent_loader, ENVIRONMENT, loaders.environment)\n    else\n      FileBased.new(parent_loader,\n                    loaders,\n                    ENVIRONMENT,\n                    env_path,\n                    ENVIRONMENT)\n    end\n  end\n\n  def self.module_loader_from(parent_loader, loaders, module_name, module_path)\n    ModuleLoaders::FileBased.new(parent_loader,\n                                 loaders,\n                                 module_name,\n                                 module_path,\n                                 module_name)\n  end\n\n  def self.pcore_resource_type_loader_from(parent_loader, loaders, environment_path)\n    ModuleLoaders::FileBased.new(parent_loader,\n                                 loaders,\n                                 nil,\n                                 environment_path,\n                                 'pcore_resource_types')\n  end\n\n  class EmptyLoader < BaseLoader\n    def find(typed_name)\n      nil\n    end\n\n    def private_loader\n      @private_loader ||= self\n    end\n\n    def private_loader=(loader)\n      @private_loader = loader\n    end\n  end\n\n  class AbstractPathBasedModuleLoader < BaseLoader\n    # The name of the module, or nil, if this is a global \"component\", or \"any module\" if set to the `NAMESPACE_WILDCARD` (*)\n    attr_reader :module_name\n\n    # The path to the location of the module/component - semantics determined by subclass\n    attr_reader :path\n\n    # A map of type to smart-paths that help with minimizing the number of paths to scan\n    attr_reader :smart_paths\n\n    # A Module Loader has a private loader, it is lazily obtained on request to provide the visibility\n    # for entities contained in the module. Since a ModuleLoader also represents an environment and it is\n    # created a different way, this loader can be set explicitly by the loaders bootstrap logic.\n    #\n    # @api private\n    attr_writer :private_loader\n\n    # Initialize a kind of ModuleLoader for one module\n    # @param parent_loader [Loader] loader with higher priority\n    # @param loaders [Loaders] the container for this loader\n    # @param module_name [String] the name of the module (non qualified name), may be nil for a global \"component\"\n    # @param path [String] the path to the root of the module (semantics defined by subclass)\n    # @param loader_name [String] a name that is used for human identification (useful when module_name is nil)\n    #\n    def initialize(parent_loader, loaders, module_name, path, loader_name, loadables)\n      super(parent_loader, loader_name, loaders.environment)\n\n      raise ArgumentError, 'path based loader cannot be instantiated without a path' if path.nil? || path.empty?\n\n      @module_name = module_name\n      @path = path\n      @smart_paths = LoaderPaths::SmartPaths.new(self)\n      @loaders = loaders\n      @loadables = loadables\n      unless (loadables - LOADABLE_KINDS).empty?\n        # TRANSLATORS 'loadables' is a variable containing loadable modules and should not be translated\n        raise ArgumentError, _('given loadables are not of supported loadable kind')\n      end\n\n      loaders.add_loader_by_name(self)\n    end\n\n    def loadables\n      @loadables\n    end\n\n    def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block)\n      global = global?\n      if name_authority == Pcore::RUNTIME_NAME_AUTHORITY\n        smart_paths.effective_paths(type).each do |sp|\n          relative_paths(sp).each do |rp|\n            tp = sp.typed_name(type, name_authority, rp, global ? nil : @module_name)\n            next unless sp.valid_name?(tp)\n\n            begin\n              load_typed(tp) unless block_given? && !block.yield(tp)\n            rescue StandardError => e\n              if error_collector.nil?\n                Puppet.warn_once(:unloadable_entity, tp.to_s, e.message)\n              else\n                err = Puppet::DataTypes::Error.new(\n                  Issues::LOADER_FAILURE.format(:type => type),\n                  'PUPPET_LOADER_FAILURE',\n                  { 'original_error' => e.message },\n                  Issues::LOADER_FAILURE.issue_code\n                )\n                error_collector << err unless error_collector.include?(err)\n              end\n            end\n          end\n        end\n      end\n      super\n    end\n\n    # Finds typed/named entity in this module\n    # @param typed_name [TypedName] the type/name to find\n    # @return [Loader::NamedEntry, nil found/created entry, or nil if not found\n    #\n    def find(typed_name)\n      # This loader is tailored to only find entries in the current runtime\n      return nil unless typed_name.name_authority == Pcore::RUNTIME_NAME_AUTHORITY\n\n      # Assume it is a global name, and that all parts of the name should be used when looking up\n      name_parts = typed_name.name_parts\n\n      # Certain types and names can be disqualified up front\n      if name_parts.size > 1\n        # The name is in a name space.\n\n        # Then entity cannot possible be in this module unless the name starts with the module name.\n        # Note:\n        # * If \"module\" represents a \"global component\", the module_name is nil and cannot match which is\n        #   ok since such a \"module\" cannot have namespaced content).\n        # * If this loader is allowed to have namespaced content, the module_name can be set to NAMESPACE_WILDCARD `*`\n        #\n        return nil unless name_parts[0] == module_name || module_name == NAMESPACE_WILDCARD\n      else\n        # The name is in the global name space.\n\n        case typed_name.type\n        when :function, :resource_type, :resource_type_pp\n          # Can be defined in module using a global name. No action required\n\n        when :plan\n          unless global?\n            # Global name must be the name of the module\n            return nil unless name_parts[0] == module_name\n\n            # Look for the special 'init' plan.\n            origin, smart_path = find_existing_path(init_plan_name)\n            return smart_path.nil? ? nil : instantiate(smart_path, typed_name, origin)\n          end\n\n        when :task\n          unless global?\n            # Global name must be the name of the module\n            return nil unless name_parts[0] == module_name\n\n            # Look for the special 'init' Task\n            origin, smart_path = find_existing_path(init_task_name)\n            return smart_path.nil? ? nil : instantiate(smart_path, typed_name, origin)\n          end\n\n        when :type\n          unless global?\n            # Global name must be the name of the module\n            unless name_parts[0] == module_name || module_name == NAMESPACE_WILDCARD\n              # Check for ruby defined data type in global namespace before giving up\n              origin, smart_path = find_existing_path(typed_name)\n              return smart_path.is_a?(LoaderPaths::DataTypePath) ? instantiate(smart_path, typed_name, origin) : nil\n            end\n\n            # Look for the special 'init_typeset' TypeSet\n            origin, smart_path = find_existing_path(init_typeset_name)\n            return nil if smart_path.nil?\n\n            value = smart_path.instantiator.create(self, typed_name, origin, get_contents(origin))\n            if value.is_a?(Types::PTypeSetType)\n              # cache the entry and return it\n              return set_entry(typed_name, value, origin)\n            end\n\n            # TRANSLATORS 'TypeSet' should not be translated\n            raise ArgumentError, _(\"The code loaded from %{origin} does not define the TypeSet '%{module_name}'\") %\n                                 { origin: origin, module_name: name_parts[0].capitalize }\n          end\n        else\n          # anything else cannot possibly be in this module\n          # TODO: should not be allowed anyway... may have to revisit this decision\n          return nil\n        end\n      end\n\n      # Get the paths that actually exist in this module (they are lazily processed once and cached).\n      # The result is an array (that may be empty).\n      # Find the file to instantiate, and instantiate the entity if file is found\n      origin, smart_path = find_existing_path(typed_name)\n      return instantiate(smart_path, typed_name, origin) unless smart_path.nil?\n\n      return nil unless typed_name.type == :type && typed_name.qualified?\n\n      # Search for TypeSet using parent name\n      ts_name = typed_name.parent\n      while ts_name\n        # Do not traverse parents here. This search must be confined to this loader\n        tse = get_entry(ts_name)\n        tse = find(ts_name) if tse.nil? || tse.value.nil?\n        if tse && (ts = tse.value).is_a?(Types::PTypeSetType)\n          # The TypeSet might be unresolved at this point. If so, it must be resolved using\n          # this loader. That in turn, adds all contained types to this loader.\n          ts.resolve(self)\n          te = get_entry(typed_name)\n          return te unless te.nil?\n        end\n        ts_name = ts_name.parent\n      end\n      nil\n    end\n\n    def instantiate(smart_path, typed_name, origin)\n      if origin.is_a?(Array)\n        value = smart_path.instantiator.create(self, typed_name, origin)\n      else\n        value = smart_path.instantiator.create(self, typed_name, origin, get_contents(origin))\n      end\n      # cache the entry and return it\n      set_entry(typed_name, value, origin)\n    end\n\n    # Abstract method that subclasses override that checks if it is meaningful to search using a generic smart path.\n    # This optimization is performed to not be tricked into searching an empty directory over and over again.\n    # The implementation may perform a deep search for file content other than directories and cache this in\n    # and index. It is guaranteed that a call to meaningful_to_search? takes place before checking any other\n    # path with relative_path_exists?.\n    #\n    # This optimization exists because many modules have been created from a template and they have\n    # empty directories for functions, types, etc. (It is also the place to create a cached index of the content).\n    #\n    # @param smart_path [String] a path relative to the module's root\n    # @return [Boolean] true if there is content in the directory appointed by the relative path\n    #\n    def meaningful_to_search?(smart_path)\n      raise NotImplementedError\n    end\n\n    # Abstract method that subclasses override to answer if the given relative path exists, and if so returns that path\n    #\n    # @param resolved_path [String] a path resolved by a smart path against the loader's root (if it has one)\n    # @return [String, nil] the found path or nil if no such path was found\n    #\n    def existing_path(resolved_path)\n      raise NotImplementedError\n    end\n\n    # Abstract method that subclasses override to return an array of paths that may be associated with the resolved path.\n    #\n    # @param resolved_path [String] a path, without extension, resolved by a smart path against the loader's root (if it has one)\n    # @return [Array<String>]\n    #\n    def candidate_paths(resolved_path)\n      raise NotImplementedError\n    end\n\n    # Abstract method that subclasses override to produce the content of the effective path.\n    # It should either succeed and return a String or fail with an exception.\n    #\n    # @param effective_path [String] a path as resolved by a smart path\n    # @return [String] the content of the file\n    #\n    def get_contents(effective_path)\n      raise NotImplementedError\n    end\n\n    # Abstract method that subclasses override to produce a source reference String used to identify the\n    # system resource (resource in the URI sense).\n    #\n    # @param relative_path [String] a path relative to the module's root\n    # @return [String] a reference to the source file (in file system, zip file, or elsewhere).\n    #\n    def get_source_ref(relative_path)\n      raise NotImplementedError\n    end\n\n    # Answers the question if this loader represents a global component (true for resource type loader and environment loader)\n    #\n    # @return [Boolean] `true` if this loader represents a global component\n    #\n    def global?\n      module_name.nil? || module_name == NAMESPACE_WILDCARD || module_name == ENVIRONMENT\n    end\n\n    # Answers `true` if the loader used by this instance is rooted beneath 'lib'. This is\n    # typically true for the system_loader. It will have a path relative to the parent\n    # of 'puppet' instead of the parent of 'lib/puppet' since the 'lib' directory of puppet\n    # is renamed during install. This is significant for loaders that load ruby code.\n    #\n    # @return [Boolean] a boolean answering if the loader is rooted beneath 'lib'.\n    def lib_root?\n      false\n    end\n\n    # Produces the private loader for the module. If this module is not already resolved, this will trigger resolution\n    #\n    def private_loader\n      # The system loader has a nil module_name and it does not have a private_loader as there are no functions\n      # that can only by called by puppet runtime - if so, it acts as the private loader directly.\n      @private_loader ||= (global? ? self : @loaders.private_loader_for_module(module_name))\n    end\n\n    # Return all paths that matches the given smart path. The returned paths are\n    # relative to the `#generic_path` of the given smart path.\n    #\n    # @param smart_path [SmartPath] the path to find relative paths for\n    # @return [Array<String>] found paths\n    def relative_paths(smart_path)\n      raise NotImplementedError\n    end\n\n    private\n\n    # @return [TypedName] the fake typed name that maps to the init_typeset path for this module\n    def init_typeset_name\n      @init_typeset_name ||= TypedName.new(:type, \"#{module_name}::init_typeset\")\n    end\n\n    # @return [TypedName] the fake typed name that maps to the path of an init[arbitrary extension]\n    #   file that represents a task named after the module\n    def init_task_name\n      @init_task_name ||= TypedName.new(:task, \"#{module_name}::init\")\n    end\n\n    # @return [TypedName] the fake typed name that maps to the path of an init.pp file that represents\n    #   a plan named after the module\n    def init_plan_name\n      @init_plan_name ||= TypedName.new(:plan, \"#{module_name}::init\")\n    end\n\n    # Find an existing path or paths for the given `typed_name`. Return `nil` if no path is found\n    # @param typed_name [TypedName] the `typed_name` to find a path for\n    # @return [Array,nil] `nil`or a two element array where the first element is an effective path or array of paths\n    #   (depending on the `SmartPath`) and the second element is the `SmartPath` that produced the effective path or\n    #   paths. A path is a String\n    def find_existing_path(typed_name)\n      is_global = global?\n      smart_paths.effective_paths(typed_name.type).each do |sp|\n        next unless sp.valid_name?(typed_name)\n\n        origin = sp.effective_path(typed_name, is_global ? 0 : 1)\n        unless origin.nil?\n          if sp.fuzzy_matching?\n            # If there are multiple *specific* paths for the file, find\n            # whichever ones exist. Otherwise, find all paths that *might* be\n            # related to origin\n            if origin.is_a?(Array)\n              origins = origin.filter_map { |ori| existing_path(ori) }\n            else\n              origins = candidate_paths(origin)\n            end\n            return [origins, sp] unless origins.empty?\n          else\n            existing = existing_path(origin)\n            return [origin, sp] unless existing.nil?\n          end\n        end\n      end\n      nil\n    end\n  end\n\n  # @api private\n  #\n  class FileBased < AbstractPathBasedModuleLoader\n    attr_reader :smart_paths\n    attr_reader :path_index\n\n    # Create a kind of ModuleLoader for one module (Puppet Module, or module like)\n    #\n    # @param parent_loader [Loader] typically the loader for the environment or root\n    # @param module_name [String] the name of the module (non qualified name), may be nil for \"modules\" only containing globals\n    # @param path [String] the path to the root of the module (semantics defined by subclass)\n    # @param loader_name [String] a name that identifies the loader\n    #\n    def initialize(parent_loader, loaders, module_name, path, loader_name, loadables = LOADABLE_KINDS)\n      super\n      @path_index = Set.new\n    end\n\n    def existing_path(effective_path)\n      # Optimized, checks index instead of visiting file system\n      @path_index.include?(effective_path) ? effective_path : nil\n    end\n\n    def candidate_paths(effective_path)\n      basename = File.basename(effective_path, '.*')\n      dirname = File.dirname(effective_path)\n\n      files = @path_index.select do |path|\n        File.dirname(path) == dirname\n      end\n\n      # At least one file has to match what we're loading, or it certainly doesn't exist\n      if files.any? { |file| File.basename(file, '.*') == basename }\n        files\n      else\n        []\n      end\n    end\n\n    def meaningful_to_search?(smart_path)\n      !add_to_index(smart_path).empty?\n    end\n\n    def to_s\n      \"(ModuleLoader::FileBased '#{loader_name}' '#{module_name}')\"\n    end\n\n    def add_to_index(smart_path)\n      found = Dir.glob(File.join(smart_path.generic_path, '**', \"*#{smart_path.extension}\"))\n\n      # The reason for not always rejecting directories here is performance (avoid extra stat calls). The\n      # false positives (directories with a matching extension) is an error in any case and will be caught\n      # later.\n      found = found.reject { |file_name| File.directory?(file_name) } if smart_path.extension.empty?\n\n      @path_index.merge(found)\n      found\n    end\n\n    def get_contents(effective_path)\n      Puppet::FileSystem.read(effective_path, :encoding => 'utf-8')\n    end\n\n    # Return all paths that matches the given smart path. The returned paths are\n    # relative to the `#generic_path` of the given smart path.\n    #\n    # This method relies on the cache and does not perform any file system access\n    #\n    # @param smart_path [SmartPath] the path to find relative paths for\n    # @return [Array<String>] found paths\n    def relative_paths(smart_path)\n      root = smart_path.generic_path\n      found = []\n      @path_index.each do |path|\n        found << Pathname(path).relative_path_from(Pathname(root)).to_s if smart_path.valid_path?(path)\n      end\n      found\n    end\n  end\n\n  # Specialization used by the system_loader which is limited to see what's beneath 'lib' and hence\n  # cannot be rooted in its parent. The 'lib' directory is renamed during install so any attempt\n  # to traverse into it from above would fail.\n  #\n  # @api private\n  #\n  class LibRootedFileBased < FileBased\n    def lib_root?\n      true\n    end\n  end\n\n  # Loads from a gem specified as a URI, gem://gemname/optional/path/in/gem, or just a String gemname.\n  # The source reference (shown in errors etc.) is the expanded path of the gem as this is believed to be more\n  # helpful - given the location it should be quite obvious which gem it is, without the location, the user would\n  # need to go on a hunt for where the file actually is located.\n  #\n  # TODO: How does this get instantiated? Does the gemname refelect the name of the module (the namespace)\n  #   or is that specified a different way? Can a gem be the container of multiple modules?\n  #\n  # @api private\n  #\n  class GemBased < FileBased\n    include GemSupport\n\n    attr_reader :gem_ref\n\n    # Create a kind of ModuleLoader for one module\n    # The parameters are:\n    # * parent_loader - typically the loader for the root\n    # * module_name - the name of the module (non qualified name)\n    # * gem_ref - [URI, String] gem reference to the root of the module (URI, gem://gemname/optional/path/in/gem), or\n    #     just the gem's name as a String.\n    #\n    def initialize(parent_loader, loaders, module_name, gem_ref, loader_name, loadables = LOADABLE_KINDS)\n      @gem_ref = gem_ref\n      super parent_loader, loaders, module_name, gem_dir(gem_ref), loader_name, loadables\n    end\n\n    def to_s\n      \"(ModuleLoader::GemBased '#{loader_name}' '#{@gem_ref}' [#{module_name}])\"\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/predefined_loader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Loader\n# A PredefinedLoader is a loader that is manually populated with loaded elements\n# before being used. It never loads anything on its own.\n#\nclass PredefinedLoader < BaseLoader\n  def find(typed_name)\n    nil\n  end\n\n  def to_s\n    \"(PredefinedLoader '#{loader_name}')\"\n  end\n\n  # Allows shadowing since this loader is used internally for things like function local types\n  # And they should win as there is otherwise a risk that the local types clash with built in types\n  # that were added after the function was written, or by resource types loaded by the 3x auto loader.\n  #\n  def allow_shadowing?\n    true\n  end\n\n  def synchronize(&block)\n    yield\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/puppet_function_instantiator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# The PuppetFunctionInstantiator instantiates a Puppet::Functions::PuppetFunction given a Puppet Programming language\n# source that when called evaluates the Puppet logic it contains.\n#\nclass PuppetFunctionInstantiator\n  # Produces an instance of the Function class with the given typed_name, or fails with an error if the\n  # given puppet source does not produce this instance when evaluated.\n  #\n  # @param loader [Loader] The loader the function is associated with\n  # @param typed_name [TypedName] the type / name of the function to load\n  # @param source_ref [URI, String] a reference to the source / origin of the puppet code to evaluate\n  # @param pp_code_string [String] puppet code in a string\n  #\n  # @return [Functions::Function] - an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create(loader, typed_name, source_ref, pp_code_string)\n    parser = Parser::EvaluatingParser.new()\n\n    # parse and validate\n    result = parser.parse_string(pp_code_string, source_ref)\n    # Only one function is allowed (and no other definitions)\n    case result.definitions.size\n    when 0\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not define the function '%{func_name}' - it is empty.\") % { source_ref: source_ref, func_name: typed_name.name }\n    when 1\n      # ok\n    else\n      raise ArgumentError, _(\"The code loaded from %{source_ref} must contain only the function '%{type_name}' - it has additional definitions.\") % { source_ref: source_ref, type_name: typed_name.name }\n    end\n    the_function_definition = result.definitions[0]\n\n    unless the_function_definition.is_a?(Model::FunctionDefinition)\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not define the function '%{type_name}' - no function found.\") % { source_ref: source_ref, type_name: typed_name.name }\n    end\n\n    unless the_function_definition.name == typed_name.name\n      expected = typed_name.name\n      actual = the_function_definition.name\n      raise ArgumentError, _(\"The code loaded from %{source_ref} produced function with the wrong name, expected %{expected}, actual %{actual}\") % { source_ref: source_ref, expected: expected, actual: actual }\n    end\n    unless result.body == the_function_definition\n      raise ArgumentError, _(\"The code loaded from %{source} contains additional logic - can only contain the function %{name}\") % { source: source_ref, name: typed_name.name }\n    end\n\n    # Adapt the function definition with loader - this is used from logic contained in it body to find the\n    # loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where\n    # the loader is known - hence this mechanism\n    private_loader = loader.private_loader\n    Adapters::LoaderAdapter.adapt(the_function_definition).loader_name = private_loader.loader_name\n\n    # Cannot bind loaded functions to global scope, that must be done without binding that scope as\n    # loaders survive a compilation.\n    closure_scope = nil # Puppet.lookup(:global_scope) { {} }\n\n    created = create_function_class(the_function_definition)\n    # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things\n    # when calling functions etc.\n    # It should be bound to global scope\n\n    created.new(closure_scope, private_loader)\n  end\n\n  # Creates Function class and instantiates it based on a FunctionDefinition model\n  # @return [Array<TypedName, Functions.Function>] - array of\n  #   typed name, and an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create_from_model(function_definition, loader)\n    created = create_function_class(function_definition)\n    typed_name = TypedName.new(:function, function_definition.name)\n    [typed_name, created.new(nil, loader)]\n  end\n\n  def self.create_function_class(function_definition)\n    # Create a 4x function wrapper around a named closure\n    Puppet::Functions.create_function(function_definition.name, Puppet::Functions::PuppetFunction) do\n      # TODO: should not create a new evaluator per function\n      init_dispatch(Evaluator::Closure::Named.new(\n                      function_definition.name,\n                      Evaluator::EvaluatorImpl.new(), function_definition\n                    ))\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/puppet_plan_instantiator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# The PuppetPlanInstantiator instantiates a Puppet::Functions::PuppetFunction given a Puppet Programming language\n# source that when called evaluates the Puppet logic it contains.\n#\nclass PuppetPlanInstantiator\n  # Produces an instance of the Function class with the given typed_name, or fails with an error if the\n  # given puppet source does not produce this instance when evaluated.\n  #\n  # @param loader [Loader] The loader the function is associated with\n  # @param typed_name [TypedName] the type / name of the function to load\n  # @param source_ref [URI, String] a reference to the source / origin of the puppet code to evaluate\n  # @param pp_code_string [String] puppet code in a string\n  #\n  # @return [Functions::Function] - an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create(loader, typed_name, source_ref, pp_code_string)\n    parser = Parser::EvaluatingParser.new()\n\n    # parse and validate\n    result = parser.parse_string(pp_code_string, source_ref)\n\n    # The parser attaches all definitions, including those nested in apply\n    # blocks, to the Program object. Node definitions in apply blocks are\n    # perfectly legal and don't count as the file containing multiple\n    # definitions for this purpose. By this point, we've already validated that\n    # there are no node definitions *outside* apply blocks, so we simply ignore\n    # them here.\n    definitions = result.definitions.reject { |definition| definition.is_a?(Puppet::Pops::Model::NodeDefinition) }\n\n    # Only one plan is allowed (and no other definitions)\n    case definitions.size\n    when 0\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not define the plan '%{plan_name}' - it is empty.\") % { source_ref: source_ref, plan_name: typed_name.name }\n    when 1\n      # ok\n    else\n      raise ArgumentError, _(\"The code loaded from %{source_ref} must contain only the plan '%{plan_name}' - it has additional definitions.\") % { source_ref: source_ref, plan_name: typed_name.name }\n    end\n    the_plan_definition = definitions[0]\n\n    unless the_plan_definition.is_a?(Model::PlanDefinition)\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found.\") % { source_ref: source_ref, plan_name: typed_name.name }\n    end\n\n    unless the_plan_definition.name == typed_name.name\n      expected = typed_name.name\n      actual = the_plan_definition.name\n      raise ArgumentError, _(\"The code loaded from %{source_ref} produced plan with the wrong name, expected %{expected}, actual %{actual}\") % { source_ref: source_ref, expected: expected, actual: actual }\n    end\n    unless result.body == the_plan_definition\n      raise ArgumentError, _(\"The code loaded from %{source} contains additional logic - can only contain the plan %{plan_name}\") % { source: source_ref, plan_name: typed_name.name }\n    end\n\n    # Adapt the function definition with loader - this is used from logic contained in it body to find the\n    # loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where\n    # the loader is known - hence this mechanism\n    private_loader = loader.private_loader\n    Adapters::LoaderAdapter.adapt(the_plan_definition).loader_name = private_loader.loader_name\n\n    # Cannot bind loaded functions to global scope, that must be done without binding that scope as\n    # loaders survive a compilation.\n    closure_scope = nil\n\n    created = create_function_class(the_plan_definition)\n    # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things\n    # when calling functions etc.\n    # It should be bound to global scope\n\n    created.new(closure_scope, private_loader)\n  end\n\n  # Creates Function class and instantiates it based on a FunctionDefinition model\n  # @return [Array<TypedName, Functions.Function>] - array of\n  #   typed name, and an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create_from_model(plan_definition, loader)\n    created = create_function_class(plan_definition)\n    typed_name = TypedName.new(:plan, plan_definition.name)\n    [typed_name, created.new(nil, loader)]\n  end\n\n  def self.create_function_class(plan_definition)\n    # Create a 4x function wrapper around a named closure\n    Puppet::Functions.create_function(plan_definition.name, Puppet::Functions::PuppetFunction) do\n      # TODO: should not create a new evaluator per function\n      init_dispatch(Evaluator::Closure::Named.new(\n                      plan_definition.name,\n                      Evaluator::EvaluatorImpl.new(), plan_definition\n                    ))\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/puppet_resource_type_impl_instantiator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# The PuppetResourceTypeImplInstantiator instantiates a Puppet::Pops::ResourceTypeImpl object.\n# given a Puppet Programming language source that when called evaluates the Puppet logic it contains.\n#\nclass PuppetResourceTypeImplInstantiator\n  # Produces an instance of Puppet::Pops::ResourceTypeImpl, or fails with an error if the\n  # given puppet source does not produce such an instance when evaluated.\n  #\n  # @param loader [Loader] The loader the function is associated with\n  # @param typed_name [TypedName] the type / name of the resource type impl to load\n  # @param source_ref [URI, String] a reference to the source / origin of the puppet code to evaluate\n  # @param pp_code_string [String] puppet code in a string\n  #\n  # @return [Puppet::Pops::ResourceTypeImpl] - an instantiated ResourceTypeImpl\n  #\n  def self.create(loader, typed_name, source_ref, pp_code_string)\n    parser = Parser::EvaluatingParser.new()\n\n    # parse and validate\n    model = parser.parse_string(pp_code_string, source_ref)\n    statements = if model.is_a?(Model::Program)\n                   if model.body.is_a?(Model::BlockExpression)\n                     model.body.statements\n                   else\n                     [model.body]\n                   end\n                 else\n                   EMPTY_ARRAY\n                 end\n    statements = statements.reject { |s| s.is_a?(Model::Nop) }\n    if statements.empty?\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not create the resource type '%{type_name}' - it is empty\") % { source_ref: source_ref, type_name: typed_name.name }\n    end\n\n    rname = Resource::ResourceTypeImpl._pcore_type.name\n    unless statements.find do |s|\n      if s.is_a?(Model::CallMethodExpression)\n        functor_expr = s.functor_expr\n        functor_expr.is_a?(Model::NamedAccessExpression) &&\n        functor_expr.left_expr.is_a?(Model::QualifiedReference) &&\n        functor_expr.left_expr.cased_value == rname &&\n        functor_expr.right_expr.is_a?(Model::QualifiedName) &&\n        functor_expr.right_expr.value == 'new'\n      else\n        false\n      end\n    end\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not create the resource type '%{type_name}' - no call to %{rname}.new found.\") % { source_ref: source_ref, type_name: typed_name.name, rname: rname }\n    end\n\n    unless statements.size == 1\n      raise ArgumentError, _(\"The code loaded from %{source_ref} must contain only the creation of resource type '%{type_name}' - it has additional logic.\") % { source_ref: source_ref, type_name: typed_name.name }\n    end\n\n    closure_scope = Puppet.lookup(:global_scope) { {} }\n    resource_type_impl = parser.evaluate(closure_scope, model)\n\n    unless resource_type_impl.is_a?(Puppet::Pops::Resource::ResourceTypeImpl)\n      got = resource_type.class\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not define the resource type '%{type_name}' - got '%{got}'.\") % { source_ref: source_ref, type_name: typed_name.name, got: got }\n    end\n\n    unless resource_type_impl.name == typed_name.name\n      expected = typed_name.name\n      actual = resource_type_impl.name\n      raise ArgumentError, _(\"The code loaded from %{source_ref} produced resource type with the wrong name, expected '%{expected}', actual '%{actual}'\") % { source_ref: source_ref, expected: expected, actual: actual }\n    end\n\n    # Adapt the resource type definition with loader - this is used from logic contained in it body to find the\n    # loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where\n    # the loader is known - hence this mechanism\n    Adapters::LoaderAdapter.adapt(resource_type_impl).loader_name = loader.loader_name\n    resource_type_impl\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/ruby_data_type_instantiator.rb",
    "content": "# frozen_string_literal: true\n\n# The RubyTypeInstantiator instantiates a data type from the ruby source\n# that calls Puppet::DataTypes.create_type.\n#\nclass Puppet::Pops::Loader::RubyDataTypeInstantiator\n  # Produces an instance of class derived from PAnyType class with the given typed_name, or fails with an error if the\n  # given ruby source does not produce this instance when evaluated.\n  #\n  # @param loader [Puppet::Pops::Loader::Loader] The loader the type is associated with\n  # @param typed_name [Puppet::Pops::Loader::TypedName] the type / name of the type to load\n  # @param source_ref [URI, String] a reference to the source / origin of the ruby code to evaluate\n  # @param ruby_code_string [String] ruby code in a string\n  #\n  # @return [Puppet::Pops::Types::PAnyType] - an instantiated data type associated with the given loader\n  #\n  def self.create(loader, typed_name, source_ref, ruby_code_string)\n    unless ruby_code_string.is_a?(String) && ruby_code_string =~ /Puppet::DataTypes\\.create_type/\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not seem to be a Puppet 5x API data type - no create_type call.\") % { source_ref: source_ref }\n    end\n\n    # make the private loader available in a binding to allow it to be passed on\n    loader_for_type = loader.private_loader\n    here = get_binding(loader_for_type)\n    created = eval(ruby_code_string, here, source_ref, 1) # rubocop:disable Security/Eval\n    unless created.is_a?(Puppet::Pops::Types::PAnyType)\n      raise ArgumentError, _(\"The code loaded from %{source_ref} did not produce a data type when evaluated. Got '%{klass}'\") % { source_ref: source_ref, klass: created.class }\n    end\n    unless created.name.casecmp(typed_name.name) == 0\n      raise ArgumentError, _(\"The code loaded from %{source_ref} produced mis-matched name, expected '%{type_name}', got %{created_name}\") % { source_ref: source_ref, type_name: typed_name.name, created_name: created.name }\n    end\n\n    created\n  end\n\n  # Produces a binding where the given loader is bound as a local variable (loader_injected_arg). This variable can be used in loaded\n  # ruby code - e.g. to call Puppet::Function.create_loaded_function(:name, loader,...)\n  #\n  def self.get_binding(loader_injected_arg)\n    binding\n  end\n  private_class_method :get_binding\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/ruby_function_instantiator.rb",
    "content": "# frozen_string_literal: true\n\n# The RubyFunctionInstantiator instantiates a Puppet::Functions::Function given the ruby source\n# that calls Puppet::Functions.create_function.\n#\nclass Puppet::Pops::Loader::RubyFunctionInstantiator\n  # Produces an instance of the Function class with the given typed_name, or fails with an error if the\n  # given ruby source does not produce this instance when evaluated.\n  #\n  # @param loader [Puppet::Pops::Loader::Loader] The loader the function is associated with\n  # @param typed_name [Puppet::Pops::Loader::TypedName] the type / name of the function to load\n  # @param source_ref [URI, String] a reference to the source / origin of the ruby code to evaluate\n  # @param ruby_code_string [String] ruby code in a string\n  #\n  # @return [Puppet::Pops::Functions.Function] - an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create(loader, typed_name, source_ref, ruby_code_string)\n    unless ruby_code_string.is_a?(String) && ruby_code_string =~ /Puppet::Functions\\.create_function/\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not seem to be a Puppet 4x API function - no create_function call.\") % { source_ref: source_ref }\n    end\n\n    # make the private loader available in a binding to allow it to be passed on\n    loader_for_function = loader.private_loader\n    here = get_binding(loader_for_function)\n    created = eval(ruby_code_string, here, source_ref, 1) # rubocop:disable Security/Eval\n    unless created.is_a?(Class)\n      raise ArgumentError, _(\"The code loaded from %{source_ref} did not produce a Function class when evaluated. Got '%{klass}'\") % { source_ref: source_ref, klass: created.class }\n    end\n    unless created.name.to_s == typed_name.name()\n      raise ArgumentError, _(\"The code loaded from %{source_ref} produced mis-matched name, expected '%{type_name}', got %{created_name}\") % { source_ref: source_ref, type_name: typed_name.name, created_name: created.name }\n    end\n\n    # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things\n    # when calling functions etc.\n    # It should be bound to global scope\n\n    # Sets closure scope to nil, to let it be picked up at runtime from Puppet.lookup(:global_scope)\n    # If function definition used the loader from the binding to create a new loader, that loader wins\n    created.new(nil, loader_for_function)\n  end\n\n  # Produces a binding where the given loader is bound as a local variable (loader_injected_arg). This variable can be used in loaded\n  # ruby code - e.g. to call Puppet::Function.create_loaded_function(:name, loader,...)\n  #\n  def self.get_binding(loader_injected_arg)\n    binding\n  end\n  private_class_method :get_binding\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb",
    "content": "# frozen_string_literal: true\n\n# The RubyLegacyFunctionInstantiator instantiates a Puppet::Functions::Function given the ruby source\n# that calls Puppet::Functions.create_function.\n#\nrequire 'ripper'\nclass Puppet::Pops::Loader::RubyLegacyFunctionInstantiator\n  UNKNOWN = '<unknown>'\n\n  # Produces an instance of the Function class with the given typed_name, or fails with an error if the\n  # given ruby source does not produce this instance when evaluated.\n  #\n  # @param loader [Puppet::Pops::Loader::Loader] The loader the function is associated with\n  # @param typed_name [Puppet::Pops::Loader::TypedName] the type / name of the function to load\n  # @param source_ref [URI, String] a reference to the source / origin of the ruby code to evaluate\n  # @param ruby_code_string [String] ruby code in a string\n  #\n  # @return [Puppet::Pops::Functions.Function] - an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create(loader, typed_name, source_ref, ruby_code_string)\n    # Assert content of 3x function by parsing\n    assertion_result = []\n    if assert_code(ruby_code_string, source_ref, assertion_result)\n      unless ruby_code_string.is_a?(String) && assertion_result.include?(:found_newfunction)\n        raise ArgumentError, _(\"The code loaded from %{source_ref} does not seem to be a Puppet 3x API function - no 'newfunction' call.\") % { source_ref: source_ref }\n      end\n    end\n\n    # make the private loader available in a binding to allow it to be passed on\n    loader_for_function = loader.private_loader\n    here = get_binding(loader_for_function)\n\n    # Avoid reloading the function if already loaded via one of the APIs that trigger 3x function loading\n    # Check if function is already loaded the 3x way (and obviously not the 4x way since we would not be here in the\n    # first place.\n    environment = Puppet.lookup(:current_environment)\n    func_info = Puppet::Parser::Functions.environment_module(environment).get_function_info(typed_name.name.to_sym)\n    if func_info.nil?\n      # This will do the 3x loading and define the \"function_<name>\" and \"real_function_<name>\" methods\n      # in the anonymous module used to hold function definitions.\n      #\n      func_info = eval(ruby_code_string, here, source_ref, 1) # rubocop:disable Security/Eval\n\n      # Validate what was loaded\n      unless func_info.is_a?(Hash)\n        # TRANSLATORS - the word 'newfunction' should not be translated as it is a method name.\n        raise ArgumentError, _(\"Illegal legacy function definition! The code loaded from %{source_ref} did not return the result of calling 'newfunction'. Got '%{klass}'\") % { source_ref: source_ref, klass: func_info.class }\n      end\n\n      unless func_info[:name] == \"function_#{typed_name.name()}\"\n        raise ArgumentError, _(\"The code loaded from %{source_ref} produced mis-matched name, expected 'function_%{type_name}', got '%{created_name}'\") % {\n          source_ref: source_ref, type_name: typed_name.name, created_name: func_info[:name]\n        }\n      end\n    end\n\n    created = Puppet::Functions::Function3x.create_function(typed_name.name(), func_info, loader_for_function)\n\n    # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things\n    # when calling functions etc.\n    # It should be bound to global scope\n\n    # Sets closure scope to nil, to let it be picked up at runtime from Puppet.lookup(:global_scope)\n    # If function definition used the loader from the binding to create a new loader, that loader wins\n    created.new(nil, loader_for_function)\n  end\n\n  # Produces a binding where the given loader is bound as a local variable (loader_injected_arg). This variable can be used in loaded\n  # ruby code - e.g. to call Puppet::Function.create_loaded_function(:name, loader,...)\n  #\n  def self.get_binding(loader_injected_arg)\n    binding\n  end\n  private_class_method :get_binding\n\n  def self.assert_code(code_string, source_ref, result)\n    ripped = Ripper.sexp(code_string)\n    return false if ripped.nil? # Let the next real parse crash and tell where and what is wrong\n\n    ripped.each { |x| walk(x, source_ref, result) }\n    true\n  end\n  private_class_method :assert_code\n\n  def self.walk(x, source_ref, result)\n    return unless x.is_a?(Array)\n\n    first = x[0]\n    case first\n    when :fcall, :call\n      # Ripper returns a :fcall for a function call in a module (want to know there is a call to newfunction()).\n      # And it returns :call for a qualified named call\n      identity_part = find_identity(x)\n      result << :found_newfunction if identity_part.is_a?(Array) && identity_part[1] == 'newfunction'\n    when :def, :defs\n      # There should not be any calls to def in a 3x function\n      mname, mline = extract_name_line(find_identity(x))\n      raise SecurityError, _(\"Illegal method definition of method '%{method_name}' in source %{source_ref} on line %{line} in legacy function. See %{url} for more information\") % {\n        method_name: mname,\n        source_ref: source_ref,\n        line: mline,\n        url: \"https://puppet.com/docs/puppet/latest/functions_refactor_legacy.html\"\n      }\n    end\n    x.each { |v| walk(v, source_ref, result) }\n  end\n  private_class_method :walk\n\n  def self.find_identity(rast)\n    rast.find { |x| x.is_a?(Array) && x[0] == :@ident }\n  end\n  private_class_method :find_identity\n\n  # Extracts the method name and line number from the Ripper Rast for an id entry.\n  # The expected input (a result from Ripper :@ident entry) is an array with:\n  # [0] == :def (or :defs for self.def)\n  # [1] == method name\n  # [2] == [ <filename>, <linenumber> ]\n  #\n  # Returns an Array; a tuple with method name and line number or \"<unknown>\" if either is missing, or format is not the expected\n  #\n  def self.extract_name_line(x)\n    (if x.is_a?(Array)\n       [x[1], x[2].is_a?(Array) ? x[2][1] : nil]\n     else\n       [nil, nil]\n     end).map { |v| v.nil? ? UNKNOWN : v }\n  end\n  private_class_method :extract_name_line\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/runtime3_type_loader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# Runtime3TypeLoader\n# ===\n# Loads a resource type using the 3.x type loader\n#\n# @api private\nclass Runtime3TypeLoader < BaseLoader\n  attr_reader :resource_3x_loader\n\n  def initialize(parent_loader, loaders, environment, resource_3x_loader)\n    super(parent_loader, environment.name, environment)\n    @environment = environment\n    @resource_3x_loader = resource_3x_loader\n  end\n\n  def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block)\n    # TODO: Use generated index of all known types (requires separate utility).\n    parent.discover(type, error_collector, name_authority, &block)\n  end\n\n  def to_s\n    \"(Runtime3TypeLoader '#{loader_name()}')\"\n  end\n\n  # Finds typed/named entity in this module\n  # @param typed_name [TypedName] the type/name to find\n  # @return [Loader::NamedEntry, nil found/created entry, or nil if not found\n  #\n  def find(typed_name)\n    return nil unless typed_name.name_authority == Pcore::RUNTIME_NAME_AUTHORITY\n\n    case typed_name.type\n    when :type\n      value = nil\n      name = typed_name.name\n      if @resource_3x_loader.nil?\n        value = Puppet::Type.type(name) unless typed_name.qualified?\n        if value.nil?\n          # Look for a user defined type\n          value = @environment.known_resource_types.find_definition(name)\n        end\n      else\n        impl_te = find_impl(TypedName.new(:resource_type_pp, name, typed_name.name_authority))\n        value = impl_te.value unless impl_te.nil?\n      end\n\n      if value.nil?\n        # Cache the fact that it wasn't found\n        set_entry(typed_name, nil)\n        return nil\n      end\n\n      # Loaded types doesn't have the same life cycle as this loader, so we must start by\n      # checking if the type was created. If it was, an entry will already be stored in\n      # this loader. If not, then it was created before this loader was instantiated and\n      # we must therefore add it.\n      te = get_entry(typed_name)\n      te = set_entry(typed_name, Types::TypeFactory.resource(value.name.to_s)) if te.nil? || te.value.nil?\n      te\n    when :resource_type_pp\n      @resource_3x_loader.nil? ? nil : find_impl(typed_name)\n    else\n      nil\n    end\n  end\n\n  # Find the implementation for the resource type by first consulting the internal loader for pp defined 'Puppet::Resource::ResourceType3'\n  # instances, then check for a Puppet::Type and lastly check for a defined type.\n  #\n  def find_impl(typed_name)\n    name = typed_name.name\n    te = StaticLoader::BUILTIN_TYPE_NAMES_LC.include?(name) ? nil : @resource_3x_loader.load_typed(typed_name)\n    if te.nil? || te.value.nil?\n      # Look for Puppet::Type\n      value = Puppet::Type.type(name) unless typed_name.qualified?\n      if value.nil?\n        # Look for a user defined type\n        value = @environment.known_resource_types.find_definition(name)\n        if value.nil?\n          # Cache the fact that it wasn't found\n          @resource_3x_loader.set_entry(typed_name, nil)\n          return nil\n        end\n      end\n      te = @resource_3x_loader.get_entry(typed_name)\n      te = @resource_3x_loader.set_entry(typed_name, value) if te.nil? || te.value.nil?\n    end\n    te\n  end\n  private :find_impl\n\n  # Allows shadowing since this loader is populated with all loaded resource types at time\n  # of loading. This loading will, for built in types override the aliases configured in the static\n  # loader.\n  #\n  def allow_shadowing?\n    true\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/simple_environment_loader.rb",
    "content": "# frozen_string_literal: true\n\n# SimpleEnvironmentLoader\n# ===\n# This loader does not load anything and it is populated by the bootstrapping logic that loads\n# the site.pp or equivalent for an environment. It does not restrict the names of what it may contain,\n# and what is loaded here overrides any child loaders (modules).\n#\nclass Puppet::Pops::Loader::SimpleEnvironmentLoader < Puppet::Pops::Loader::BaseLoader\n  attr_accessor :private_loader\n\n  # Never finds anything, everything \"loaded\" is set externally\n  def find(typed_name)\n    nil\n  end\n\n  def to_s\n    \"(SimpleEnvironmentLoader '#{loader_name}')\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/static_loader.rb",
    "content": "# frozen_string_literal: true\n\n# Static Loader contains constants, basic data types and other types required for the system\n# to boot.\n#\nmodule Puppet::Pops\nmodule Loader\nclass StaticLoader < Loader\n  BUILTIN_TYPE_NAMES = %w[\n    Component\n    Exec\n    File\n    Filebucket\n    Group\n    Node\n    Notify\n    Package\n    Resources\n    Schedule\n    Service\n    Stage\n    Tidy\n    User\n    Whit\n  ].freeze\n\n  BUILTIN_TYPE_NAMES_LC = Set.new(BUILTIN_TYPE_NAMES.map(&:downcase)).freeze\n\n  BUILTIN_ALIASES = {\n    'Data' => 'Variant[ScalarData,Undef,Hash[String,Data],Array[Data]]',\n    'RichDataKey' => 'Variant[String,Numeric]',\n    'RichData' => 'Variant[Scalar,SemVerRange,Binary,Sensitive,Type,TypeSet,URI,Object,Undef,Default,Hash[RichDataKey,RichData],Array[RichData]]',\n\n    # Backward compatible aliases.\n    'Puppet::LookupKey' => 'RichDataKey',\n    'Puppet::LookupValue' => 'RichData'\n  }.freeze\n\n  attr_reader :loaded\n\n  def initialize\n    @loaded = {}\n    @runtime_3_initialized = false\n    create_built_in_types\n  end\n\n  def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY)\n    # Static loader only contains runtime types\n    return EMPTY_ARRAY unless type == :type && name_authority == Pcore::RUNTIME_NAME_AUTHORITY\n\n    typed_names = @loaded.keys\n    block_given? ? typed_names.select { |tn| yield(tn) } : typed_names\n  end\n\n  def load_typed(typed_name)\n    load_constant(typed_name)\n  end\n\n  def get_entry(typed_name)\n    load_constant(typed_name)\n  end\n\n  def set_entry(typed_name, value, origin = nil)\n    @loaded[typed_name] = Loader::NamedEntry.new(typed_name, value, origin)\n  end\n\n  def find(name)\n    # There is nothing to search for, everything this loader knows about is already available\n    nil\n  end\n\n  def parent\n    nil # at top of the hierarchy\n  end\n\n  def to_s\n    \"(StaticLoader)\"\n  end\n\n  def loaded_entry(typed_name, check_dependencies = false)\n    @loaded[typed_name]\n  end\n\n  def runtime_3_init\n    unless @runtime_3_initialized\n      @runtime_3_initialized = true\n      create_resource_type_references\n    end\n    nil\n  end\n\n  def register_aliases\n    aliases = BUILTIN_ALIASES.map { |name, string| add_type(name, Types::PTypeAliasType.new(name, Types::TypeFactory.type_reference(string), nil)) }\n    aliases.each { |type| type.resolve(self) }\n  end\n\n  private\n\n  def load_constant(typed_name)\n    @loaded[typed_name]\n  end\n\n  def create_built_in_types\n    origin_uri = URI(\"puppet:Puppet-Type-System/Static-Loader\")\n    type_map = Puppet::Pops::Types::TypeParser.type_map\n    type_map.each do |name, type|\n      set_entry(TypedName.new(:type, name), type, origin_uri)\n    end\n  end\n\n  def create_resource_type_references\n    # These needs to be done quickly and we do not want to scan the file system for these\n    # We are also not interested in their definition only that they exist.\n    # These types are in all environments.\n    #\n    BUILTIN_TYPE_NAMES.each { |name| create_resource_type_reference(name) }\n  end\n\n  def add_type(name, type)\n    set_entry(TypedName.new(:type, name), type)\n    type\n  end\n\n  def create_resource_type_reference(name)\n    add_type(name, Types::TypeFactory.resource(name))\n  end\n\n  def synchronize(&block)\n    yield\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/task_instantiator.rb",
    "content": "# frozen_string_literal: true\n\n# The TypeDefinitionInstantiator instantiates a type alias or a type definition\n#\nrequire_relative '../../../puppet/module/task'\nmodule Puppet::Pops\nmodule Loader\nclass TaskInstantiator\n  def self.create(loader, typed_name, source_refs)\n    name = typed_name.name\n    basename = typed_name.name_parts[1] || 'init'\n    dirname = File.dirname(source_refs[0])\n    metadata_files, executables = source_refs.partition { |source_ref| source_ref.end_with?('.json') }\n    metadata_file = metadata_files.find { |source_ref| File.basename(source_ref, '.json') == basename }\n\n    metadata = Puppet::Module::Task.read_metadata(metadata_file) || {}\n\n    files = Puppet::Module::Task.find_files(name, dirname, metadata, executables)\n\n    task = { 'name' => name, 'metadata' => metadata, 'files' => files }\n\n    begin\n      unless metadata['parameters'].is_a?(Hash) || metadata['parameters'].nil?\n        msg = _('Failed to load metadata for task %{name}: \\'parameters\\' must be a hash') % { name: name }\n        raise Puppet::ParseError.new(msg, metadata_file)\n      end\n      task['parameters'] = convert_types(metadata['parameters'])\n\n      Types::TypeFactory.task.from_hash(task)\n    rescue Types::TypeAssertionError => ex\n      # Not strictly a parser error but from the users perspective, the file content didn't parse properly. The\n      # ParserError also conveys file info (even though line is unknown)\n      msg = _('Failed to load metadata for task %{name}: %{reason}') % { name: name, reason: ex.message }\n      raise Puppet::ParseError.new(msg, metadata_file)\n    end\n  end\n\n  def self.convert_types(args)\n    args.transform_values do |v|\n      v['type'].nil? ? Types::TypeFactory.data : Types::TypeParser.singleton.parse(v['type'])\n    end if args\n  end\n  private_class_method :convert_types\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/type_definition_instantiator.rb",
    "content": "# frozen_string_literal: true\n\n# The TypeDefinitionInstantiator instantiates a type alias or a type definition\n#\nmodule Puppet::Pops\nmodule Loader\nclass TypeDefinitionInstantiator\n  def self.create(loader, typed_name, source_ref, pp_code_string)\n    # parse and validate\n    parser = Parser::EvaluatingParser.new()\n    model = parser.parse_string(pp_code_string, source_ref)\n    # Only one type is allowed (and no other definitions)\n\n    name = typed_name.name\n    case model.definitions.size\n    when 0\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not define the type '%{name}' - it is empty.\") % { source_ref: source_ref, name: name }\n    when 1\n      # ok\n    else\n      raise ArgumentError,\n            _(\"The code loaded from %{source_ref} must contain only the type '%{name}' - it has additional definitions.\") % { source_ref: source_ref, name: name }\n    end\n    type_definition = model.definitions[0]\n\n    unless type_definition.is_a?(Model::TypeAlias) || type_definition.is_a?(Model::TypeDefinition)\n      raise ArgumentError,\n            _(\"The code loaded from %{source_ref} does not define the type '%{name}' - no type alias or type definition found.\") % { source_ref: source_ref, name: name }\n    end\n\n    actual_name = type_definition.name\n    unless name == actual_name.downcase\n      raise ArgumentError,\n            _(\"The code loaded from %{source_ref} produced type with the wrong name, expected '%{name}', actual '%{actual_name}'\") % { source_ref: source_ref, name: name, actual_name: actual_name }\n    end\n\n    unless model.body == type_definition\n      raise ArgumentError,\n            _(\"The code loaded from %{source_ref} contains additional logic - can only contain the type '%{name}'\") % { source_ref: source_ref, name: name }\n    end\n\n    # Adapt the type definition with loader - this is used from logic contained in its body to find the\n    # loader to use when resolving contained aliases API. Such logic have a hard time finding the closure (where\n    # the loader is known - hence this mechanism\n    private_loader = loader.private_loader\n    Adapters::LoaderAdapter.adapt(type_definition).loader_name = private_loader.loader_name\n    create_runtime_type(type_definition)\n  end\n\n  def self.create_from_model(type_definition, loader)\n    typed_name = TypedName.new(:type, type_definition.name)\n    type = create_runtime_type(type_definition)\n    loader.set_entry(\n      typed_name,\n      type,\n      type_definition.locator.to_uri(type_definition)\n    )\n    type\n  end\n\n  # @api private\n  def self.create_runtime_type(type_definition)\n    # Using the RUNTIME_NAME_AUTHORITY as the name_authority is motivated by the fact that the type\n    # alias name (managed by the runtime) becomes the name of the created type\n    #\n    create_type(type_definition.name, type_definition.type_expr, Pcore::RUNTIME_NAME_AUTHORITY)\n  end\n\n  # @api private\n  def self.create_type(name, type_expr, name_authority)\n    create_named_type(name, named_definition(type_expr), type_expr, name_authority)\n  end\n\n  # @api private\n  def self.create_named_type(name, type_name, type_expr, name_authority)\n    case type_name\n    when 'Object'\n      # No need for an alias. The Object type itself will receive the name instead\n      unless type_expr.is_a?(Model::LiteralHash)\n        type_expr = type_expr.keys.empty? ? nil : type_expr.keys[0] unless type_expr.is_a?(Hash)\n      end\n      Types::PObjectType.new(name, type_expr)\n    when 'TypeSet'\n      # No need for an alias. The Object type itself will receive the name instead\n      type_expr = type_expr.keys.empty? ? nil : type_expr.keys[0] unless type_expr.is_a?(Hash)\n      Types::PTypeSetType.new(name, type_expr, name_authority)\n    else\n      Types::PTypeAliasType.new(name, type_expr)\n    end\n  end\n\n  # @api private\n  def self.named_definition(te)\n    return 'Object' if te.is_a?(Model::LiteralHash)\n\n    te.is_a?(Model::AccessExpression) && (left = te.left_expr).is_a?(Model::QualifiedReference) ? left.cased_value : nil\n  end\n\n  def several_paths?\n    false\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/typed_name.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Loader\n# A namespace/name/type combination that can be used as a compound hash key\n#\n# @api public\nclass TypedName\n  attr_reader :hash\n  attr_reader :type\n  attr_reader :name_authority\n  attr_reader :name\n  attr_reader :name_parts\n  attr_reader :compound_name\n\n  def initialize(type, name, name_authority = Pcore::RUNTIME_NAME_AUTHORITY)\n    name = name.downcase\n    @type = type\n    @name_authority = name_authority\n    # relativize the name (get rid of leading ::), and make the split string available\n    parts = name.to_s.split(DOUBLE_COLON)\n    if parts[0].empty?\n      parts.shift\n      @name = name[2..]\n    else\n      @name = name\n    end\n    @name_parts = parts.freeze\n\n    # Use a frozen compound key for the hash and comparison. Most varying part first\n    @compound_name = \"#{@name}/#{@type}/#{@name_authority}\"\n    @hash = @compound_name.hash\n    freeze\n  end\n\n  def ==(o)\n    o.class == self.class && o.compound_name == @compound_name\n  end\n\n  alias eql? ==\n\n  # @return the parent of this instance, or nil if this instance is not qualified\n  def parent\n    @name_parts.size > 1 ? self.class.new(@type, @name_parts[0...-1].join(DOUBLE_COLON), @name_authority) : nil\n  end\n\n  def qualified?\n    @name_parts.size > 1\n  end\n\n  def to_s\n    \"#{@name_authority}/#{@type}/#{@name}\"\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/loader/uri_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Loader::UriHelper\n  # Raises an exception if specified gem can not be located\n  #\n  def path_for_uri(uri, subdir = 'lib')\n    case uri.scheme\n    when \"gem\"\n      begin\n        spec = Gem::Specification.find_by_name(uri.hostname)\n        # if path given append that, else append given subdir\n        File.join(spec.gem_dir, uri.path.empty?() ? subdir : uri.path)\n      rescue StandardError => e\n        raise \"TODO TYPE: Failed to located gem #{uri}. #{e.message}\"\n      end\n    when \"file\"\n      File.join(uri.path, subdir)\n    when nil\n      File.join(uri.path, subdir)\n    else\n      raise \"Not a valid scheme for a loader: #{uri.scheme}. Use a 'file:' (or just a path), or 'gem://gemname[/path]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/loaders.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\n# This is the container for all Loader instances. Each Loader instance has a `loader_name` by which it can be uniquely\n# identified within this container.\n# A Loader can be private or public. In general, code will have access to the private loader associated with the\n# location of the code. It will be parented by a loader that in turn have access to other public loaders that\n# can load only such entries that have been publicly available. The split between public and private is not\n# yet enforced in Puppet.\n#\n# The name of a private loader should always end with ' private'\n#\nclass Loaders\n  class LoaderError < Puppet::Error; end\n\n  # Commented out the :static_loader and :puppet_system_loader\n  # because of rubocop offenses with duplicated definitions of\n  # these generated methods, but keeping them here for visibility\n  # on how the loaders are stacked.\n  # attr_reader :static_loader\n  # attr_reader :puppet_system_loader\n  attr_reader :puppet_cache_loader\n  attr_reader :public_environment_loader\n  attr_reader :private_environment_loader\n  attr_reader :environment\n\n  def self.new(environment, for_agent = false, load_from_pcore = true)\n    environment.lock.synchronize do\n      obj = environment.loaders\n      if obj.nil?\n        obj = allocate\n        obj.send(:initialize, environment, for_agent, load_from_pcore)\n      end\n      obj\n    end\n  end\n\n  def initialize(environment, for_agent, load_from_pcore = true)\n    # Protect against environment havoc\n    raise ArgumentError, _(\"Attempt to redefine already initialized loaders for environment\") unless environment.loaders.nil?\n\n    environment.loaders = self\n    @environment = environment\n    @loaders_by_name = {}\n\n    add_loader_by_name(self.class.static_loader)\n\n    # Create the set of loaders\n    # 1. Puppet, loads from the \"running\" puppet - i.e. bundled functions, types, extension points and extensions\n    #    These cannot be cached since a  loaded instance will be bound to its closure scope which holds on to\n    #    a compiler and all loaded types. Subsequent request would find remains of the environment that loaded\n    #    the content. PUP-4461.\n    #\n    @puppet_system_loader = create_puppet_system_loader()\n\n    # 2. Cache loader(optional) - i.e. what puppet stores on disk via pluginsync; gate behind the for_agent flag.\n    # 3. Environment loader - i.e. what is bound across the environment, may change for each setup\n    #    TODO: loaders need to work when also running in an agent doing catalog application. There is no\n    #    concept of environment the same way as when running as a master (except when doing apply).\n    #    The creation mechanisms should probably differ between the two.\n    @private_environment_loader =\n      if for_agent\n        @puppet_cache_loader = create_puppet_cache_loader\n        create_environment_loader(environment, @puppet_cache_loader, load_from_pcore)\n      else\n        create_environment_loader(environment, @puppet_system_loader, load_from_pcore)\n      end\n\n    Pcore.init_env(@private_environment_loader)\n\n    # 4. module loaders are set up from the create_environment_loader, they register themselves\n  end\n\n  # Called after loader has been added to Puppet Context as :loaders so that dynamic types can\n  # be pre-loaded with a fully configured loader system\n  def pre_load\n    @puppet_system_loader.load(:type, 'error')\n  end\n\n  # Clears the cached static and puppet_system loaders (to enable testing)\n  #\n  def self.clear\n    @@static_loader = nil\n    Puppet::Pops::Types::TypeFactory.clear\n    Model.class_variable_set(:@@pcore_ast_initialized, false)\n    Model.register_pcore_types\n  end\n\n  # Calls {#loaders} to obtain the {{Loaders}} instance and then uses it to find the appropriate loader\n  # for the given `module_name`, or for the environment in case `module_name` is `nil` or empty.\n  #\n  # @param module_name [String,nil] the name of the module\n  # @return [Loader::Loader] the found loader\n  # @raise [Puppet::ParseError] if no loader can be found\n  # @api private\n  def self.find_loader(module_name)\n    loaders.find_loader(module_name)\n  end\n\n  def self.static_implementation_registry\n    if !class_variable_defined?(:@@static_implementation_registry) || @@static_implementation_registry.nil?\n      ir = Types::ImplementationRegistry.new\n      Types::TypeParser.type_map.values.each { |t| ir.register_implementation(t.simple_name, t.class.name) }\n      @@static_implementation_registry = ir\n    end\n    @@static_implementation_registry\n  end\n\n  def self.static_loader\n    # The static loader can only be changed after a reboot\n    if !class_variable_defined?(:@@static_loader) || @@static_loader.nil?\n      @@static_loader = Loader::StaticLoader.new()\n      @@static_loader.register_aliases\n      Pcore.init(@@static_loader, static_implementation_registry)\n    end\n    @@static_loader\n  end\n\n  def self.implementation_registry\n    loaders = Puppet.lookup(:loaders) { nil }\n    loaders.nil? ? nil : loaders.implementation_registry\n  end\n\n  def register_implementations(obj_classes, name_authority)\n    self.class.register_implementations_with_loader(obj_classes, name_authority, @private_environment_loader)\n  end\n\n  # Register implementations using the global static loader\n  def self.register_static_implementations(obj_classes)\n    register_implementations_with_loader(obj_classes, Pcore::RUNTIME_NAME_AUTHORITY, static_loader)\n  end\n\n  def self.register_implementations_with_loader(obj_classes, name_authority, loader)\n    types = obj_classes.map do |obj_class|\n      type = obj_class._pcore_type\n      typed_name = Loader::TypedName.new(:type, type.name, name_authority)\n      entry = loader.loaded_entry(typed_name)\n      loader.set_entry(typed_name, type) if entry.nil? || entry.value.nil?\n      type\n    end\n    # Resolve lazy so that all types can cross reference each other\n    types.each { |type| type.resolve(loader) }\n  end\n\n  # Register the given type with the Runtime3TypeLoader. The registration will not happen unless\n  # the type system has been initialized.\n  #\n  # @param name [String,Symbol] the name of the entity being set\n  # @param origin [URI] the origin or the source where the type is defined\n  # @api private\n  def self.register_runtime3_type(name, origin)\n    loaders = Puppet.lookup(:loaders) { nil }\n    return nil if loaders.nil?\n\n    rt3_loader = loaders.runtime3_type_loader\n    return nil if rt3_loader.nil?\n\n    name = name.to_s\n    caps_name = Types::TypeFormatter.singleton.capitalize_segments(name)\n    typed_name = Loader::TypedName.new(:type, name)\n    rt3_loader.set_entry(typed_name, Types::PResourceType.new(caps_name), origin)\n    nil\n  end\n\n  # Finds a loader to use when deserializing a catalog and then subsequenlty use user\n  # defined types found in that catalog.\n  #\n  def self.catalog_loader\n    loaders = Puppet.lookup(:loaders) { nil }\n    if loaders.nil?\n      loaders = Loaders.new(Puppet.lookup(:current_environment), true)\n      Puppet.push_context(:loaders => loaders)\n    end\n    loaders.find_loader(nil)\n  end\n\n  # Finds the `Loaders` instance by looking up the :loaders in the global Puppet context\n  #\n  # @return [Loaders] the loaders instance\n  # @raise [Puppet::ParseError] if loader has been bound to the global context\n  # @api private\n  def self.loaders\n    loaders = Puppet.lookup(:loaders) { nil }\n    raise Puppet::ParseError, _(\"Internal Error: Puppet Context ':loaders' missing\") if loaders.nil?\n\n    loaders\n  end\n\n  # Lookup a loader by its unique name.\n  #\n  # @param [String] loader_name the name of the loader to lookup\n  # @return [Loader] the found loader\n  # @raise [Puppet::ParserError] if no loader is found\n  def [](loader_name)\n    loader = @loaders_by_name[loader_name]\n    if loader.nil?\n      # Unable to find the module private loader. Try resolving the module\n      loader = private_loader_for_module(loader_name[0..-9]) if loader_name.end_with?(' private')\n      raise Puppet::ParseError, _(\"Unable to find loader named '%{loader_name}'\") % { loader_name: loader_name } if loader.nil?\n    end\n    loader\n  end\n\n  # Finds the appropriate loader for the given `module_name`, or for the environment in case `module_name`\n  # is `nil` or empty.\n  #\n  # @param module_name [String,nil] the name of the module\n  # @return [Loader::Loader] the found loader\n  # @raise [Puppet::ParseError] if no loader can be found\n  # @api private\n  def find_loader(module_name)\n    if module_name.nil? || EMPTY_STRING == module_name\n      # Use the public environment loader\n      public_environment_loader\n    else\n      # TODO : Later check if definition is private, and then add it to private_loader_for_module\n      #\n      loader = public_loader_for_module(module_name)\n      if loader.nil?\n        raise Puppet::ParseError, _(\"Internal Error: did not find public loader for module: '%{module_name}'\") % { module_name: module_name }\n      end\n\n      loader\n    end\n  end\n\n  def implementation_registry\n    # Environment specific implementation registry\n    @implementation_registry ||= Types::ImplementationRegistry.new(self.class.static_implementation_registry)\n  end\n\n  def static_loader\n    self.class.static_loader\n  end\n\n  def puppet_system_loader\n    @puppet_system_loader\n  end\n\n  def runtime3_type_loader\n    @runtime3_type_loader\n  end\n\n  def public_loader_for_module(module_name)\n    md = @module_resolver[module_name] || (return nil)\n    # Note, this loader is not resolved until there is interest in the visibility of entities from the\n    # perspective of something contained in the module. (Many request may pass through a module loader\n    # without it loading anything.\n    # See {#private_loader_for_module}, and not in {#configure_loaders_for_modules}\n    md.public_loader\n  end\n\n  def private_loader_for_module(module_name)\n    md = @module_resolver[module_name] || (return nil)\n    # Since there is interest in the visibility from the perspective of entities contained in the\n    # module, it must be resolved (to provide this visibility).\n    # See {#configure_loaders_for_modules}\n    unless md.resolved?\n      @module_resolver.resolve(md)\n    end\n    md.private_loader\n  end\n\n  def add_loader_by_name(loader)\n    name = loader.loader_name\n    if @loaders_by_name.include?(name)\n      raise Puppet::ParseError, _(\"Internal Error: Attempt to redefine loader named '%{name}'\") % { name: name }\n    end\n\n    @loaders_by_name[name] = loader\n  end\n\n  # Load the main manifest for the given environment\n  #\n  # There are two sources that can be used for the initial parse:\n  #\n  #   1. The value of `Puppet[:code]`: Puppet can take a string from\n  #     its settings and parse that as a manifest. This is used by various\n  #     Puppet applications to read in a manifest and pass it to the\n  #     environment as a side effect. This is attempted first.\n  #   2. The contents of the environment's +manifest+ attribute: Puppet will\n  #     try to load the environment manifest. The manifest must be a file.\n  #\n  # @return [Model::Program] The manifest parsed into a model object\n  def load_main_manifest\n    parser = Parser::EvaluatingParser.singleton\n    parsed_code = Puppet[:code]\n    program = if parsed_code != \"\"\n                parser.parse_string(parsed_code, 'unknown-source-location')\n              else\n                file = @environment.manifest\n\n                # if the manifest file is a reference to a directory, parse and\n                # combine all .pp files in that directory\n                if file == Puppet::Node::Environment::NO_MANIFEST\n                  nil\n                elsif File.directory?(file)\n                  raise Puppet::Error, \"manifest of environment '#{@environment.name}' appoints directory '#{file}'. It must be a file\"\n                elsif File.exist?(file)\n                  parser.parse_file(file)\n                else\n                  raise Puppet::Error, \"manifest of environment '#{@environment.name}' appoints '#{file}'. It does not exist\"\n                end\n              end\n    instantiate_definitions(program, public_environment_loader) unless program.nil?\n    program\n  rescue Puppet::ParseErrorWithIssue => detail\n    detail.environment = @environment.name\n    raise\n  rescue => detail\n    msg = _('Could not parse for environment %{env}: %{detail}') % { env: @environment, detail: detail }\n    error = Puppet::Error.new(msg)\n    error.set_backtrace(detail.backtrace)\n    raise error\n  end\n\n  # Add 4.x definitions found in the given program to the given loader.\n  def instantiate_definitions(program, loader)\n    program.definitions.each { |d| instantiate_definition(d, loader) }\n    nil\n  end\n\n  # Add given 4.x definition to the given loader.\n  def instantiate_definition(definition, loader)\n    case definition\n    when Model::PlanDefinition\n      instantiate_PlanDefinition(definition, loader)\n    when Model::FunctionDefinition\n      instantiate_FunctionDefinition(definition, loader)\n    when Model::TypeAlias\n      instantiate_TypeAlias(definition, loader)\n    when Model::TypeMapping\n      instantiate_TypeMapping(definition, loader)\n    else\n      raise Puppet::ParseError, \"Internal Error: Unknown type of definition - got '#{definition.class}'\"\n    end\n  end\n\n  private\n\n  def instantiate_PlanDefinition(plan_definition, loader)\n    typed_name, f = Loader::PuppetPlanInstantiator.create_from_model(plan_definition, loader)\n    loader.set_entry(typed_name, f, plan_definition.locator.to_uri(plan_definition))\n    nil\n  end\n\n  def instantiate_FunctionDefinition(function_definition, loader)\n    # Instantiate Function, and store it in the loader\n    typed_name, f = Loader::PuppetFunctionInstantiator.create_from_model(function_definition, loader)\n    loader.set_entry(typed_name, f, function_definition.locator.to_uri(function_definition))\n    nil\n  end\n\n  def instantiate_TypeAlias(type_alias, loader)\n    # Bind the type alias to the loader using the alias\n    Puppet::Pops::Loader::TypeDefinitionInstantiator.create_from_model(type_alias, loader)\n    nil\n  end\n\n  def instantiate_TypeMapping(type_mapping, loader)\n    tf = Types::TypeParser.singleton\n    lhs = tf.interpret(type_mapping.type_expr, loader)\n    rhs = tf.interpret_any(type_mapping.mapping_expr, loader)\n    implementation_registry.register_type_mapping(lhs, rhs)\n    nil\n  end\n\n  def create_puppet_system_loader\n    Loader::ModuleLoaders.system_loader_from(static_loader, self)\n  end\n\n  def create_puppet_cache_loader\n    Loader::ModuleLoaders.cached_loader_from(puppet_system_loader, self)\n  end\n\n  def create_environment_loader(environment, parent_loader, load_from_pcore = true)\n    # This defines where to start parsing/evaluating - the \"initial import\" (to use 3x terminology)\n    # Is either a reference to a single .pp file, or a directory of manifests. If the environment becomes\n    # a module and can hold functions, types etc. then these are available across all other modules without\n    # them declaring this dependency - it is however valuable to be able to treat it the same way\n    # bindings and other such system related configuration.\n\n    # This is further complicated by the many options available:\n    # - The environment may not have a directory, the code comes from one appointed 'manifest' (site.pp)\n    # - The environment may have a directory and also point to a 'manifest'\n    # - The code to run may be set in settings (code)\n\n    # Further complication is that there is nothing specifying what the visibility is into\n    # available modules. (3x is everyone sees everything).\n    # Puppet binder currently reads confdir/bindings - that is bad, it should be using the new environment support.\n\n    # env_conf is setup from the environment_dir value passed into Puppet::Environments::Directories.new\n    env_conf = Puppet.lookup(:environments).get_conf(environment.name)\n    env_path = env_conf.nil? || !env_conf.is_a?(Puppet::Settings::EnvironmentConf) ? nil : env_conf.path_to_env\n\n    if Puppet[:tasks]\n      loader = Loader::ModuleLoaders.environment_loader_from(parent_loader, self, env_path)\n    else\n      # Create the 3.x resource type loader\n      static_loader.runtime_3_init\n      # Create pcore resource type loader, if applicable\n      pcore_resource_type_loader = if load_from_pcore && env_path\n                                     Loader::ModuleLoaders.pcore_resource_type_loader_from(parent_loader, self, env_path)\n                                   else\n                                     nil\n                                   end\n      @runtime3_type_loader = add_loader_by_name(Loader::Runtime3TypeLoader.new(parent_loader, self, environment, pcore_resource_type_loader))\n\n      if env_path.nil?\n        # Not a real directory environment, cannot work as a module TODO: Drop when legacy env are dropped?\n        loader = add_loader_by_name(Loader::SimpleEnvironmentLoader.new(@runtime3_type_loader, Loader::ENVIRONMENT, environment))\n      else\n        # View the environment as a module to allow loading from it - this module is always called 'environment'\n        loader = Loader::ModuleLoaders.environment_loader_from(@runtime3_type_loader, self, env_path)\n      end\n    end\n\n    # An environment has a module path even if it has a null loader\n    configure_loaders_for_modules(loader, environment)\n    # modules should see this loader\n    @public_environment_loader = loader\n\n    # Code in the environment gets to see all modules (since there is no metadata for the environment)\n    # but since this is not given to the module loaders, they can not load global code (since they can not\n    # have prior knowledge about this\n    loader = add_loader_by_name(Loader::DependencyLoader.new(loader, Loader::ENVIRONMENT_PRIVATE, @module_resolver.all_module_loaders(), environment))\n\n    # The module loader gets the private loader via a lazy operation to look up the module's private loader.\n    # This does not work for an environment since it is not resolved the same way.\n    # TODO: The EnvironmentLoader could be a specialized loader instead of using a ModuleLoader to do the work.\n    #       This is subject to future design - an Environment may move more in the direction of a Module.\n    @public_environment_loader.private_loader = loader\n    loader\n  end\n\n  def configure_loaders_for_modules(parent_loader, environment)\n    @module_resolver = mr = ModuleResolver.new(self)\n    environment.modules.each do |puppet_module|\n      # Create data about this module\n      md = LoaderModuleData.new(puppet_module)\n      mr[puppet_module.name] = md\n      md.public_loader = Loader::ModuleLoaders.module_loader_from(parent_loader, self, md.name, md.path)\n    end\n    # NOTE: Do not resolve all modules here - this is wasteful if only a subset of modules / functions are used\n    #       The resolution is triggered by asking for a module's private loader, since this means there is interest\n    #       in the visibility from that perspective.\n    #       If later, it is wanted that all resolutions should be made up-front (to capture errors eagerly, this\n    #       can be introduced (better for production), but may be irritating in development mode.\n  end\n\n  # =LoaderModuleData\n  # Information about a Module and its loaders.\n  # TODO: should have reference to real model element containing all module data; this is faking it\n  # TODO: Should use Puppet::Module to get the metadata (as a hash) - a somewhat blunt instrument, but that is\n  #       what is available with a reasonable API.\n  #\n  class LoaderModuleData\n    attr_accessor :public_loader\n    attr_accessor :private_loader\n    attr_accessor :resolutions\n\n    # The Puppet::Module this LoaderModuleData represents in the loader configuration\n    attr_reader :puppet_module\n\n    # @param puppet_module [Puppet::Module] the module instance for the module being represented\n    #\n    def initialize(puppet_module)\n      @puppet_module = puppet_module\n      @resolutions = []\n      @public_loader = nil\n      @private_loader = nil\n    end\n\n    def name\n      @puppet_module.name\n    end\n\n    def version\n      @puppet_module.version\n    end\n\n    def path\n      @puppet_module.path\n    end\n\n    def resolved?\n      !@private_loader.nil?\n    end\n\n    def restrict_to_dependencies?\n      @puppet_module.has_metadata?\n    end\n\n    def unmet_dependencies?\n      @puppet_module.unmet_dependencies.any?\n    end\n\n    def dependency_names\n      @puppet_module.dependencies_as_modules.collect(&:name)\n    end\n  end\n\n  # Resolves module loaders - resolution of model dependencies is done by Puppet::Module\n  #\n  class ModuleResolver\n    def initialize(loaders)\n      @loaders = loaders\n      @index = {}\n      @all_module_loaders = nil\n    end\n\n    def [](name)\n      @index[name]\n    end\n\n    def []=(name, module_data)\n      @index[name] = module_data\n    end\n\n    def all_module_loaders\n      @all_module_loaders ||= @index.values.map(&:public_loader)\n    end\n\n    def resolve(module_data)\n      if module_data.resolved?\n        nil\n      else\n        module_data.private_loader =\n          if module_data.restrict_to_dependencies?\n            create_loader_with_dependencies_first(module_data)\n          else\n            create_loader_with_all_modules_visible(module_data)\n          end\n      end\n    end\n\n    private\n\n    def create_loader_with_all_modules_visible(from_module_data)\n      @loaders.add_loader_by_name(Loader::DependencyLoader.new(from_module_data.public_loader, \"#{from_module_data.name} private\", all_module_loaders(), @loaders.environment))\n    end\n\n    def create_loader_with_dependencies_first(from_module_data)\n      dependency_loaders = from_module_data.dependency_names.collect { |name| @index[name].public_loader }\n      visible_loaders = dependency_loaders + (all_module_loaders() - dependency_loaders)\n      @loaders.add_loader_by_name(Loader::DependencyLoader.new(from_module_data.public_loader, \"#{from_module_data.name} private\", visible_loaders, @loaders.environment))\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/configured_data_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'hiera_config'\nrequire_relative 'data_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass ConfiguredDataProvider\n  include DataProvider\n\n  # @param config [HieraConfig,nil] the configuration\n  def initialize(config = nil)\n    @config = config.nil? ? nil : assert_config_version(config)\n  end\n\n  def config(lookup_invocation)\n    @config ||= assert_config_version(HieraConfig.create(lookup_invocation, configuration_path(lookup_invocation), self))\n  end\n\n  # Needed to assign generated version 4 config\n  # @deprecated\n  def config=(config)\n    @config = config\n  end\n\n  # @return [Pathname] the path to the configuration\n  def config_path\n    @config.nil? ? nil : @config.config_path\n  end\n\n  # @return [String] the name of this provider\n  def name\n    n = \"#{place} \"\n    n << '\"' << module_name << '\" ' unless module_name.nil?\n    n << 'Data Provider'\n    n << \" (#{@config.name})\" unless @config.nil?\n    n\n  end\n\n  # Performs a lookup by searching all configured locations for the given _key_. A merge will be performed if\n  # the value is found in more than one location.\n  #\n  # @param key [String] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies \"first found\")\n  # @return [Object] the found object\n  # @throw :no_such_key when the object is not found\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    lookup_invocation.with(:data_provider, self) do\n      merge_strategy = MergeStrategy.strategy(merge)\n      dps = data_providers(lookup_invocation)\n      if dps.empty?\n        lookup_invocation.report_not_found(key)\n        throw :no_such_key\n      end\n      merge_strategy.lookup(dps, lookup_invocation) do |data_provider|\n        data_provider.unchecked_key_lookup(key, lookup_invocation, merge_strategy)\n      end\n    end\n  end\n\n  protected\n\n  # Assert that the given config version is accepted by this data provider.\n  #\n  # @param config [HieraConfig] the configuration to check\n  # @return [HieraConfig] the argument\n  # @raise [Puppet::DataBinding::LookupError] if the configuration version is unacceptable\n  def assert_config_version(config)\n    config\n  end\n\n  # Return the root of the configured entity\n  #\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @return [Pathname] Path to root of the module\n  # @raise [Puppet::DataBinding::LookupError] if the given module is can not be found\n  #\n  def provider_root(lookup_invocation)\n    raise NotImplementedError, \"#{self.class.name} must implement method '#provider_root'\"\n  end\n\n  def configuration_path(lookup_invocation)\n    provider_root(lookup_invocation) + HieraConfig::CONFIG_FILE_NAME\n  end\n\n  private\n\n  def data_providers(lookup_invocation)\n    config(lookup_invocation).configured_data_providers(lookup_invocation, self)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/context.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'interpolation'\n\nmodule Puppet::Pops\nmodule Lookup\n# The EnvironmentContext is adapted to the current environment\n#\nclass EnvironmentContext < Adaptable::Adapter\n  class FileData\n    attr_reader :data\n\n    def initialize(path, inode, mtime, size, data)\n      @path = path\n      @inode = inode\n      @mtime = mtime\n      @size = size\n      @data = data\n    end\n\n    def valid?(stat)\n      stat.ino == @inode && stat.mtime == @mtime && stat.size == @size\n    end\n  end\n\n  attr_reader :environment_name\n\n  def self.create_adapter(environment)\n    new(environment)\n  end\n\n  def initialize(environment)\n    @environment_name = environment.name\n    @file_data_cache = {}\n  end\n\n  # Loads the contents of the file given by _path_. The content is then yielded to the provided block in\n  # case a block is given, and the returned value from that block is cached and returned by this method.\n  # If no block is given, the content is stored instead.\n  #\n  # The cache is retained as long as the inode, mtime, and size of the file remains unchanged.\n  #\n  # @param path [String] path to the file to be read\n  # @yieldparam content [String] the content that was read from the file\n  # @yieldreturn [Object] some result based on the content\n  # @return [Object] the content, or if a block was given, the return value of the block\n  #\n  def cached_file_data(path)\n    file_data = @file_data_cache[path]\n    stat = Puppet::FileSystem.stat(path)\n    unless file_data && file_data.valid?(stat)\n      Puppet.debug { \"File at '#{path}' was changed, reloading\" } if file_data\n      content = Puppet::FileSystem.read(path, :encoding => 'utf-8')\n      file_data = FileData.new(path, stat.ino, stat.mtime, stat.size, block_given? ? yield(content) : content)\n      @file_data_cache[path] = file_data\n    end\n    file_data.data\n  end\nend\n\n# A FunctionContext is created for each unique hierarchy entry and adapted to the Compiler (and hence shares\n# the compiler's life-cycle).\n# @api private\nclass FunctionContext\n  include Interpolation\n\n  attr_reader :module_name, :function\n  attr_accessor :data_hash\n\n  def initialize(environment_context, module_name, function)\n    @data_hash = nil\n    @cache = {}\n    @environment_context = environment_context\n    @module_name = module_name\n    @function = function\n  end\n\n  def cache(key, value)\n    @cache[key] = value\n  end\n\n  def cache_all(hash)\n    @cache.merge!(hash)\n    nil\n  end\n\n  def cache_has_key(key)\n    @cache.include?(key)\n  end\n\n  def cached_value(key)\n    @cache[key]\n  end\n\n  def cached_entries(&block)\n    if block_given?\n      @cache.each_pair do |pair|\n        if block.arity == 2\n          yield(*pair)\n        else\n          yield(pair)\n        end\n      end\n      nil\n    else\n      Types::Iterable.on(@cache)\n    end\n  end\n\n  def cached_file_data(path, &block)\n    @environment_context.cached_file_data(path, &block)\n  end\n\n  def environment_name\n    @environment_context.environment_name\n  end\nend\n\n# The Context is created once for each call to a function. It provides a combination of the {Invocation} object needed\n# to provide explanation support and the {FunctionContext} object needed to provide the private cache.\n# The {Context} is part of the public API. It will be passed to a _data_hash_, _data_dig_, or _lookup_key_ function and its\n# attributes and methods can be used in a Puppet function as well as in a Ruby function.\n# The {Context} is maps to the Pcore type 'Puppet::LookupContext'\n#\n# @api public\nclass Context\n  include Types::PuppetObject\n  extend Forwardable\n\n  def self._pcore_type\n    @type\n  end\n\n  def self.register_ptype(loader, ir)\n    tf = Types::TypeFactory\n    key_type = tf.optional(tf.scalar)\n    @type =\n      Pcore.create_object_type(\n        loader,\n        ir,\n        self,\n        'Puppet::LookupContext',\n        'Any',\n        {\n          'environment_name' => {\n            Types::KEY_TYPE => Types::PStringType::NON_EMPTY,\n            Types::KEY_KIND => Types::PObjectType::ATTRIBUTE_KIND_DERIVED\n          },\n          'module_name' => {\n            Types::KEY_TYPE => tf.variant(Types::PStringType::NON_EMPTY, Types::PUndefType::DEFAULT)\n          }\n        },\n        {\n          'not_found' => tf.callable([0, 0], tf.undef),\n          'explain' => tf.callable([0, 0, tf.callable(0, 0)], tf.undef),\n          'interpolate' => tf.callable(1, 1),\n          'cache' => tf.callable([key_type, tf.any], tf.any),\n          'cache_all' => tf.callable([tf.hash_kv(key_type, tf.any)], tf.undef),\n          'cache_has_key' => tf.callable([key_type], tf.boolean),\n          'cached_value' => tf.callable([key_type], tf.any),\n          'cached_entries' => tf.variant(\n            tf.callable([0, 0, tf.callable(1, 1)], tf.undef),\n            tf.callable([0, 0, tf.callable(2, 2)], tf.undef),\n            tf.callable([0, 0], tf.iterable(tf.tuple([key_type, tf.any])))\n          ),\n          'cached_file_data' => tf.callable(tf.string, tf.optional(tf.callable([1, 1])))\n        }\n      ).resolve(loader)\n  end\n\n  # Mainly for test purposes. Makes it possible to create a {Context} in Puppet code provided that a current {Invocation} exists.\n  def self.from_asserted_args(module_name)\n    new(FunctionContext.new(EnvironmentContext.adapt(Puppet.lookup(:environments).get(Puppet[:environment])), module_name, nil), Invocation.current)\n  end\n\n  # Public methods delegated to the {FunctionContext}\n  def_delegators :@function_context, :cache, :cache_all, :cache_has_key, :cached_value, :cached_entries, :environment_name, :module_name, :cached_file_data\n\n  def initialize(function_context, lookup_invocation)\n    @lookup_invocation = lookup_invocation\n    @function_context = function_context\n  end\n\n  # Will call the given block to obtain a textual explanation if explanation support is active.\n  #\n  def explain(&block)\n    @lookup_invocation.report_text(&block)\n    nil\n  end\n\n  # Resolve interpolation expressions in the given value\n  # @param [Object] value\n  # @return [Object] the value with all interpolation expressions resolved\n  def interpolate(value)\n    @function_context.interpolate(value, @lookup_invocation, true)\n  end\n\n  def not_found\n    throw :no_such_key\n  end\n\n  # @api private\n  def invocation\n    @lookup_invocation\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/data_adapter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Lookup\n# A class that adapts a Hash\n# @api private\nclass DataAdapter < Adaptable::Adapter\n  def self.create_adapter(o)\n    new\n  end\n\n  def initialize\n    @data = {}\n  end\n\n  def [](name)\n    @data[name]\n  end\n\n  def include?(name)\n    @data.include? name\n  end\n\n  def []=(name, value)\n    @data[name] = value\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/data_dig_function_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'function_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass DataDigFunctionProvider < FunctionProvider\n  TAG = 'data_dig'\n\n  # Performs a lookup with the assumption that a recursive check has been made.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies \"first found\")\n  # @return [Object] the found object\n  # @throw :no_such_key when the object is not found\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    lookup_invocation.with(:data_provider, self) do\n      MergeStrategy.strategy(merge).lookup(locations, lookup_invocation) do |location|\n        invoke_with_location(lookup_invocation, location, key, merge)\n      end\n    end\n  end\n\n  def invoke_with_location(lookup_invocation, location, key, merge)\n    if location.nil?\n      key.undig(lookup_invocation.report_found(key, validated_data_dig(key, lookup_invocation, nil, merge)))\n    else\n      lookup_invocation.with(:location, location) do\n        key.undig(lookup_invocation.report_found(key, validated_data_dig(key, lookup_invocation, location, merge)))\n      end\n    end\n  end\n\n  def label\n    'Data Dig'\n  end\n\n  def validated_data_dig(key, lookup_invocation, location, merge)\n    validate_data_value(data_dig(key, lookup_invocation, location, merge)) do\n      msg = \"Value for key '#{key}', returned from #{full_name}\"\n      location.nil? ? msg : \"#{msg}, when using location '#{location}',\"\n    end\n  end\n\n  private\n\n  def data_dig(key, lookup_invocation, location, merge)\n    unless location.nil? || location.exist?\n      lookup_invocation.report_location_not_found\n      throw :no_such_key\n    end\n    ctx = function_context(lookup_invocation, location)\n    ctx.data_hash ||= {}\n    catch(:no_such_key) do\n      hash = ctx.data_hash\n      hash[key] = ctx.function.call(lookup_invocation.scope, key.to_a, options(location), Context.new(ctx, lookup_invocation)) unless hash.include?(key)\n      return hash[key]\n    end\n    lookup_invocation.report_not_found(key)\n    throw :no_such_key\n  end\nend\n\n# @api private\nclass V3BackendFunctionProvider < DataDigFunctionProvider\n  TAG = 'hiera3_backend'\n\n  def data_dig(key, lookup_invocation, location, merge)\n    @backend ||= instantiate_backend(lookup_invocation)\n\n    # A merge_behavior retrieved from hiera.yaml must not be converted here. Instead, passing the symbol :hash\n    # tells the V3 backend to pick it up from the config.\n    resolution_type = lookup_invocation.hiera_v3_merge_behavior? ? :hash : convert_merge(merge)\n    @backend.lookup(key.to_s, lookup_invocation.scope, lookup_invocation.hiera_v3_location_overrides, resolution_type, { :recurse_guard => nil })\n  end\n\n  def full_name\n    \"hiera version 3 backend '#{options[HieraConfig::KEY_BACKEND]}'\"\n  end\n\n  def value_is_validated?\n    false\n  end\n\n  private\n\n  def instantiate_backend(lookup_invocation)\n    backend_name = options[HieraConfig::KEY_BACKEND]\n    begin\n      require 'hiera/backend'\n      require \"hiera/backend/#{backend_name.downcase}_backend\"\n      backend = Hiera::Backend.const_get(\"#{backend_name.capitalize}_backend\").new\n      backend.method(:lookup).arity == 4 ? Hiera::Backend::Backend1xWrapper.new(backend) : backend\n    rescue LoadError => e\n      lookup_invocation.report_text { \"Unable to load backend '#{backend_name}': #{e.message}\" }\n      throw :no_such_key\n    rescue NameError => e\n      lookup_invocation.report_text { \"Unable to instantiate backend '#{backend_name}': #{e.message}\" }\n      throw :no_such_key\n    end\n  end\n\n  # Converts a lookup 'merge' parameter argument into a Hiera 'resolution_type' argument.\n  #\n  # @param merge [String,Hash,nil] The lookup 'merge' argument\n  # @return [Symbol,Hash,nil] The Hiera 'resolution_type'\n  def convert_merge(merge)\n    case merge\n    when nil, 'first', 'default'\n      # Nil is OK. Defaults to Hiera :priority\n      nil\n    when Puppet::Pops::MergeStrategy\n      convert_merge(merge.configuration)\n    when 'unique'\n      # Equivalent to Hiera :array\n      :array\n    when 'hash'\n      # Equivalent to Hiera :hash with default :native merge behavior. A Hash must be passed here\n      # to override possible Hiera deep merge config settings.\n      { :behavior => :native }\n    when 'deep', 'unconstrained_deep'\n      # Equivalent to Hiera :hash with :deeper merge behavior.\n      { :behavior => :deeper }\n    when 'reverse_deep'\n      # Equivalent to Hiera :hash with :deep merge behavior.\n      { :behavior => :deep }\n    when Hash\n      strategy = merge['strategy']\n      case strategy\n      when 'deep', 'unconstrained_deep', 'reverse_deep'\n        result = { :behavior => strategy == 'reverse_deep' ? :deep : :deeper }\n        # Remaining entries must have symbolic keys\n        merge.each_pair { |k, v| result[k.to_sym] = v unless k == 'strategy' }\n        result\n      else\n        convert_merge(strategy)\n      end\n    else\n      raise Puppet::DataBinding::LookupError, \"Unrecognized value for request 'merge' parameter: '#{merge}'\"\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/data_hash_function_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'function_provider'\nrequire_relative 'interpolation'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass DataHashFunctionProvider < FunctionProvider\n  include SubLookup\n  include Interpolation\n\n  TAG = 'data_hash'\n\n  def self.trusted_return_type\n    @trusted_return_type ||= Types::PHashType.new(DataProvider.key_type, DataProvider.value_type)\n  end\n\n  # Performs a lookup with the assumption that a recursive check has been made.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies \"first found\")\n  # @return [Object] the found object\n  # @throw :no_such_key when the object is not found\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    root_key = key.root_key\n    lookup_invocation.with(:data_provider, self) do\n      MergeStrategy.strategy(merge).lookup(locations, lookup_invocation) do |location|\n        invoke_with_location(lookup_invocation, location, root_key)\n      end\n    end\n  end\n\n  private\n\n  def invoke_with_location(lookup_invocation, location, root_key)\n    if location.nil?\n      lookup_key(lookup_invocation, nil, root_key)\n    else\n      lookup_invocation.with(:location, location) do\n        if location.exist?\n          lookup_key(lookup_invocation, location, root_key)\n        else\n          lookup_invocation.report_location_not_found\n          throw :no_such_key\n        end\n      end\n    end\n  end\n\n  def lookup_key(lookup_invocation, location, root_key)\n    lookup_invocation.report_found(root_key, data_value(lookup_invocation, location, root_key))\n  end\n\n  def data_value(lookup_invocation, location, root_key)\n    hash = data_hash(lookup_invocation, location)\n    value = hash[root_key]\n    if value.nil? && !hash.include?(root_key)\n      lookup_invocation.report_not_found(root_key)\n      throw :no_such_key\n    end\n    value = validate_data_value(value) do\n      msg = \"Value for key '#{root_key}', in hash returned from #{full_name}\"\n      location.nil? ? msg : \"#{msg}, when using location '#{location}',\"\n    end\n    interpolate(value, lookup_invocation, true)\n  end\n\n  def data_hash(lookup_invocation, location)\n    ctx = function_context(lookup_invocation, location)\n    ctx.data_hash ||= parent_data_provider.validate_data_hash(call_data_hash_function(ctx, lookup_invocation, location)) do\n      msg = \"Value returned from #{full_name}\"\n      location.nil? ? msg : \"#{msg}, when using location '#{location}',\"\n    end\n  end\n\n  def call_data_hash_function(ctx, lookup_invocation, location)\n    ctx.function.call(lookup_invocation.scope, options(location), Context.new(ctx, lookup_invocation))\n  end\nend\n\n# @api private\nclass V3DataHashFunctionProvider < DataHashFunctionProvider\n  TAG = 'v3_data_hash'\n\n  def initialize(name, parent_data_provider, function_name, options, locations)\n    @datadir = options.delete(HieraConfig::KEY_DATADIR)\n    super\n  end\n\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    extra_paths = lookup_invocation.hiera_v3_location_overrides\n    if extra_paths.nil? || extra_paths.empty?\n      super\n    else\n      # Extra paths provided. Must be resolved and placed in front of known paths\n      paths = parent_data_provider.config(lookup_invocation).resolve_paths(@datadir, extra_paths, lookup_invocation, false, \".#{@name}\")\n      all_locations = paths + locations\n      root_key = key.root_key\n      lookup_invocation.with(:data_provider, self) do\n        MergeStrategy.strategy(merge).lookup(all_locations, lookup_invocation) do |location|\n          invoke_with_location(lookup_invocation, location, root_key)\n        end\n      end\n    end\n  end\nend\n\n# TODO: API 5.0, remove this class\n# @api private\nclass V4DataHashFunctionProvider < DataHashFunctionProvider\n  TAG = 'v4_data_hash'\n\n  def name\n    \"Deprecated API function \\\"#{function_name}\\\"\"\n  end\n\n  def full_name\n    \"deprecated API function '#{function_name}'\"\n  end\n\n  def call_data_hash_function(ctx, lookup_invocation, location)\n    ctx.function.call(lookup_invocation.scope)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/data_provider.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nmodule DataProvider\n  def self.key_type\n    @key_type\n  end\n\n  def self.value_type\n    @value_type\n  end\n\n  def self.register_types(loader)\n    tp = Types::TypeParser.singleton\n    @key_type = tp.parse('RichDataKey', loader)\n    @value_type = tp.parse('RichData', loader)\n  end\n\n  # Performs a lookup with an endless recursion check.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String=>Object},nil] Merge strategy or hash with strategy and options\n  #\n  def key_lookup(key, lookup_invocation, merge)\n    lookup_invocation.check(key.to_s) { unchecked_key_lookup(key, lookup_invocation, merge) }\n  end\n\n  # Performs a lookup using a module default hierarchy with an endless recursion check. All providers except\n  # the `ModuleDataProvider` will throw `:no_such_key` if this method is called.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String=>Object},nil] Merge strategy or hash with strategy and options\n  #\n  def key_lookup_in_default(key, lookup_invocation, merge)\n    throw :no_such_key\n  end\n\n  def lookup(key, lookup_invocation, merge)\n    lookup_invocation.check(key.to_s) { unchecked_key_lookup(key, lookup_invocation, merge) }\n  end\n\n  # Performs a lookup with the assumption that a recursive check has been made.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies \"first found\")\n  # @return [Object] the found object\n  # @throw :no_such_key when the object is not found\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    raise NotImplementedError, \"Subclass of #{DataProvider.name} must implement 'unchecked_lookup' method\"\n  end\n\n  # @return [String,nil] the name of the module that this provider belongs to nor `nil` if it doesn't belong to a module\n  def module_name\n    nil\n  end\n\n  # @return [String] the name of the this data provider\n  def name\n    raise NotImplementedError, \"Subclass of #{DataProvider.name} must implement 'name' method\"\n  end\n\n  # @returns `true` if the value provided by this instance can always be trusted, `false` otherwise\n  def value_is_validated?\n    false\n  end\n\n  # Asserts that _data_hash_ is a hash. Will yield to obtain origin of value in case an error is produced\n  #\n  # @param data_hash [Hash{String=>Object}] The data hash\n  # @return [Hash{String=>Object}] The data hash\n  def validate_data_hash(data_hash, &block)\n    Types::TypeAsserter.assert_instance_of(nil, Types::PHashType::DEFAULT, data_hash, &block)\n  end\n\n  # Asserts that _data_value_ is of valid type. Will yield to obtain origin of value in case an error is produced\n  #\n  # @param data_provider [DataProvider] The data provider that produced the hash\n  # @return [Object] The data value\n  def validate_data_value(value, &block)\n    # The DataProvider.value_type is self recursive so further recursive check of collections is needed here\n    unless value_is_validated? || DataProvider.value_type.instance?(value)\n      actual_type = Types::TypeCalculator.singleton.infer(value)\n      raise Types::TypeAssertionError.new(\"#{yield} has wrong type, expects Puppet::LookupValue, got #{actual_type}\", DataProvider.value_type, actual_type)\n    end\n    value\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/environment_data_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'configured_data_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass EnvironmentDataProvider < ConfiguredDataProvider\n  def place\n    'Environment'\n  end\n\n  protected\n\n  def assert_config_version(config)\n    if config.version > 3\n      config\n    else\n      if Puppet[:strict] == :error\n        config.fail(Issues::HIERA_VERSION_3_NOT_GLOBAL, :where => 'environment')\n      else\n        Puppet.warn_once(:hiera_v3_at_env_root, config.config_path, _('hiera.yaml version 3 found at the environment root was ignored'), config.config_path)\n      end\n      nil\n    end\n  end\n\n  # Return the root of the environment\n  #\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @return [Pathname] Path to root of the environment\n  def provider_root(lookup_invocation)\n    Pathname.new(lookup_invocation.scope.environment.configuration.path_to_env)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/explainer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Lookup\n  # The ExplainNode contains information of a specific node in a tree traversed during\n  # lookup. The tree can be traversed using the `parent` and `branches` attributes of\n  # each node.\n  #\n  # Each leaf node contains information about what happened when the leaf of the branch\n  # was traversed.\n  class ExplainNode\n    def branches\n      @branches ||= []\n    end\n\n    def to_hash\n      hash = {}\n      hash[:branches] = @branches.map(&:to_hash) unless @branches.nil? || @branches.empty?\n      hash\n    end\n\n    def explain\n      io = ''.dup\n      dump_on(io, '', '')\n      io\n    end\n\n    def inspect\n      to_s\n    end\n\n    def to_s\n      s = self.class.name\n      s = \"#{s} with #{@branches.size} branches\" unless @branches.nil?\n      s\n    end\n\n    def text(text)\n      @texts ||= []\n      @texts << text\n    end\n\n    def dump_on(io, indent, first_indent)\n      dump_texts(io, indent)\n    end\n\n    def dump_texts(io, indent)\n      @texts.each { |text| io << indent << text << \"\\n\" } if instance_variable_defined?(:@texts)\n    end\n  end\n\n  class ExplainTreeNode < ExplainNode\n    attr_reader :parent, :event, :value\n    attr_accessor :key\n\n    def initialize(parent)\n      @parent = parent\n      @event = nil\n    end\n\n    def found_in_overrides(key, value)\n      @key = key.to_s\n      @value = value\n      @event = :found_in_overrides\n    end\n\n    def found_in_defaults(key, value)\n      @key = key.to_s\n      @value = value\n      @event = :found_in_defaults\n    end\n\n    def found(key, value)\n      @key = key.to_s\n      @value = value\n      @event = :found\n    end\n\n    def result(value)\n      @value = value\n      @event = :result\n    end\n\n    def not_found(key)\n      @key = key.to_s\n      @event = :not_found\n    end\n\n    def location_not_found\n      @event = :location_not_found\n    end\n\n    def increase_indent(indent)\n      indent + '  '\n    end\n\n    def to_hash\n      hash = super\n      hash[:key] = @key unless @key.nil?\n      hash[:value] = @value if [:found, :found_in_defaults, :found_in_overrides, :result].include?(@event)\n      hash[:event] = @event unless @event.nil?\n      hash[:texts] = @texts unless @texts.nil?\n      hash[:type] = type\n      hash\n    end\n\n    def type\n      :root\n    end\n\n    def dump_outcome(io, indent)\n      case @event\n      when :not_found\n        io << indent << 'No such key: \"' << @key << \"\\\"\\n\"\n      when :found, :found_in_overrides, :found_in_defaults\n        io << indent << 'Found key: \"' << @key << '\" value: '\n        dump_value(io, indent, @value)\n        io << ' in overrides' if @event == :found_in_overrides\n        io << ' in defaults' if @event == :found_in_defaults\n        io << \"\\n\"\n      end\n      dump_texts(io, indent)\n    end\n\n    def dump_value(io, indent, value)\n      case value\n      when Hash\n        io << '{'\n        unless value.empty?\n          inner_indent = increase_indent(indent)\n          value.reduce(\"\\n\") do |sep, (k, v)|\n            io << sep << inner_indent\n            dump_value(io, inner_indent, k)\n            io << ' => '\n            dump_value(io, inner_indent, v)\n            \",\\n\"\n          end\n          io << \"\\n\" << indent\n        end\n        io << '}'\n      when Array\n        io << '['\n        unless value.empty?\n          inner_indent = increase_indent(indent)\n          value.reduce(\"\\n\") do |sep, v|\n            io << sep << inner_indent\n            dump_value(io, inner_indent, v)\n            \",\\n\"\n          end\n          io << \"\\n\" << indent\n        end\n        io << ']'\n      else\n        io << value.inspect\n      end\n    end\n\n    def to_s\n      \"#{self.class.name}: #{@key}, #{@event}\"\n    end\n  end\n\n  class ExplainTop < ExplainTreeNode\n    def initialize(parent, type, key)\n      super(parent)\n      @type = type\n      self.key = key.to_s\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << first_indent << 'Searching for \"' << key << \"\\\"\\n\"\n      indent = increase_indent(indent)\n      branches.each { |b| b.dump_on(io, indent, indent) }\n    end\n  end\n\n  class ExplainInvalidKey < ExplainTreeNode\n    def initialize(parent, key)\n      super(parent)\n      @key = key.to_s\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << first_indent << \"Invalid key \\\"\" << @key << \"\\\"\\n\"\n    end\n\n    def type\n      :invalid_key\n    end\n  end\n\n  class ExplainMergeSource < ExplainNode\n    attr_reader :merge_source\n\n    def initialize(merge_source)\n      @merge_source = merge_source\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << first_indent << 'Using merge options from \"' << merge_source << \"\\\" hash\\n\"\n    end\n\n    def to_hash\n      { :type => type, :merge_source => merge_source }\n    end\n\n    def type\n      :merge_source\n    end\n  end\n\n  class ExplainModule < ExplainTreeNode\n    def initialize(parent, module_name)\n      super(parent)\n      @module_name = module_name\n    end\n\n    def dump_on(io, indent, first_indent)\n      case @event\n      when :module_not_found\n        io << indent << 'Module \"' << @module_name << \"\\\" not found\\n\"\n      when :module_provider_not_found\n        io << indent << 'Module data provider for module \"' << @module_name << \"\\\" not found\\n\"\n      end\n    end\n\n    def module_not_found\n      @event = :module_not_found\n    end\n\n    def module_provider_not_found\n      @event = :module_provider_not_found\n    end\n\n    def type\n      :module\n    end\n  end\n\n  class ExplainInterpolate < ExplainTreeNode\n    def initialize(parent, expression)\n      super(parent)\n      @expression = expression\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << first_indent << 'Interpolation on \"' << @expression << \"\\\"\\n\"\n      indent = increase_indent(indent)\n      branches.each { |b| b.dump_on(io, indent, indent) }\n    end\n\n    def to_hash\n      hash = super\n      hash[:expression] = @expression\n      hash\n    end\n\n    def type\n      :interpolate\n    end\n  end\n\n  class ExplainMerge < ExplainTreeNode\n    def initialize(parent, merge)\n      super(parent)\n      @merge = merge\n    end\n\n    def dump_on(io, indent, first_indent)\n      return if branches.size == 0\n\n      # It's pointless to report a merge where there's only one branch\n      return branches[0].dump_on(io, indent, first_indent) if branches.size == 1\n\n      io << first_indent << 'Merge strategy ' << @merge.class.key.to_s << \"\\n\"\n      indent = increase_indent(indent)\n      options = options_wo_strategy\n      unless options.nil?\n        io << indent << 'Options: '\n        dump_value(io, indent, options)\n        io << \"\\n\"\n      end\n      branches.each { |b| b.dump_on(io, indent, indent) }\n      if @event == :result\n        io << indent << 'Merged result: '\n        dump_value(io, indent, @value)\n        io << \"\\n\"\n      end\n    end\n\n    def to_hash\n      return branches[0].to_hash if branches.size == 1\n\n      hash = super\n      hash[:merge] = @merge.class.key\n      options = options_wo_strategy\n      hash[:options] = options unless options.nil?\n      hash\n    end\n\n    def type\n      :merge\n    end\n\n    def options_wo_strategy\n      options = @merge.options\n      if !options.nil? && options.include?('strategy')\n        options = options.dup\n        options.delete('strategy')\n      end\n      options.empty? ? nil : options\n    end\n  end\n\n  class ExplainGlobal < ExplainTreeNode\n    def initialize(parent, binding_terminus)\n      super(parent)\n      @binding_terminus = binding_terminus\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << first_indent << 'Data Binding \"' << @binding_terminus.to_s << \"\\\"\\n\"\n      indent = increase_indent(indent)\n      branches.each { |b| b.dump_on(io, indent, indent) }\n      dump_outcome(io, indent)\n    end\n\n    def to_hash\n      hash = super\n      hash[:name] = @binding_terminus\n      hash\n    end\n\n    def type\n      :global\n    end\n  end\n\n  class ExplainDataProvider < ExplainTreeNode\n    def initialize(parent, provider)\n      super(parent)\n      @provider = provider\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << first_indent << @provider.name << \"\\n\"\n      indent = increase_indent(indent)\n      if @provider.respond_to?(:config_path)\n        path = @provider.config_path\n        io << indent << 'Using configuration \"' << path.to_s << \"\\\"\\n\" unless path.nil?\n      end\n      branches.each { |b| b.dump_on(io, indent, indent) }\n      dump_outcome(io, indent)\n    end\n\n    def to_hash\n      hash = super\n      hash[:name] = @provider.name\n      if @provider.respond_to?(:config_path)\n        path = @provider.config_path\n        hash[:configuration_path] = path.to_s unless path.nil?\n      end\n      hash[:module] = @provider.module_name if @provider.is_a?(ModuleDataProvider)\n      hash\n    end\n\n    def type\n      :data_provider\n    end\n  end\n\n  class ExplainLocation < ExplainTreeNode\n    def initialize(parent, location)\n      super(parent)\n      @location = location\n    end\n\n    def dump_on(io, indent, first_indent)\n      location = @location.location\n      type_name = type == :path ? 'Path' : 'URI'\n      io << indent << type_name << ' \"' << location.to_s << \"\\\"\\n\"\n      indent = increase_indent(indent)\n      io << indent << 'Original ' << type_name.downcase << ': \"' << @location.original_location << \"\\\"\\n\"\n      branches.each { |b| b.dump_on(io, indent, indent) }\n      io << indent << type_name << \" not found\\n\" if @event == :location_not_found\n      dump_outcome(io, indent)\n    end\n\n    def to_hash\n      hash = super\n      location = @location.location\n      if type == :path\n        hash[:original_path] = @location.original_location\n        hash[:path] = location.to_s\n      else\n        hash[:original_uri] = @location.original_location\n        hash[:uri] = location.to_s\n      end\n      hash\n    end\n\n    def type\n      @location.location.is_a?(Pathname) ? :path : :uri\n    end\n  end\n\n  class ExplainSubLookup < ExplainTreeNode\n    def initialize(parent, sub_key)\n      super(parent)\n      @sub_key = sub_key\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << indent << 'Sub key: \"' << @sub_key.join('.') << \"\\\"\\n\"\n      indent = increase_indent(indent)\n      branches.each { |b| b.dump_on(io, indent, indent) }\n      dump_outcome(io, indent)\n    end\n\n    def type\n      :sub_key\n    end\n  end\n\n  class ExplainKeySegment < ExplainTreeNode\n    def initialize(parent, segment)\n      super(parent)\n      @segment = segment\n    end\n\n    def dump_on(io, indent, first_indent)\n      dump_outcome(io, indent)\n    end\n\n    def type\n      :segment\n    end\n  end\n\n  class ExplainScope < ExplainTreeNode\n    def initialize(parent, name)\n      super(parent)\n      @name = name\n    end\n\n    def dump_on(io, indent, first_indent)\n      io << indent << @name << \"\\n\"\n      indent = increase_indent(indent)\n      branches.each { |b| b.dump_on(io, indent, indent) }\n      dump_outcome(io, indent)\n    end\n\n    def to_hash\n      hash = super\n      hash[:name] = @name\n      hash\n    end\n\n    def type\n      :scope\n    end\n  end\n\n  class Explainer < ExplainNode\n    def initialize(explain_options = false, only_explain_options = false)\n      @current = self\n      @explain_options = explain_options\n      @only_explain_options = only_explain_options\n    end\n\n    def push(qualifier_type, qualifier)\n      node = case qualifier_type\n             when :global\n               ExplainGlobal.new(@current, qualifier)\n             when :location\n               ExplainLocation.new(@current, qualifier)\n             when :interpolate\n               ExplainInterpolate.new(@current, qualifier)\n             when :data_provider\n               ExplainDataProvider.new(@current, qualifier)\n             when :merge\n               ExplainMerge.new(@current, qualifier)\n             when :module\n               ExplainModule.new(@current, qualifier)\n             when :scope\n               ExplainScope.new(@current, qualifier)\n             when :sub_lookup\n               ExplainSubLookup.new(@current, qualifier)\n             when :segment\n               ExplainKeySegment.new(@current, qualifier)\n             when :meta, :data\n               ExplainTop.new(@current, qualifier_type, qualifier)\n             when :invalid_key\n               ExplainInvalidKey.new(@current, qualifier)\n             else\n               # TRANSLATORS 'Explain' is referring to the 'Explainer' class and should not be translated\n               raise ArgumentError, _(\"Unknown Explain type %{qualifier_type}\") % { qualifier_type: qualifier_type }\n             end\n      @current.branches << node\n      @current = node\n    end\n\n    def only_explain_options?\n      @only_explain_options\n    end\n\n    def explain_options?\n      @explain_options\n    end\n\n    def pop\n      @current = @current.parent unless @current.parent.nil?\n    end\n\n    def accept_found_in_overrides(key, value)\n      @current.found_in_overrides(key, value)\n    end\n\n    def accept_found_in_defaults(key, value)\n      @current.found_in_defaults(key, value)\n    end\n\n    def accept_found(key, value)\n      @current.found(key, value)\n    end\n\n    def accept_merge_source(merge_source)\n      @current.branches << ExplainMergeSource.new(merge_source)\n    end\n\n    def accept_not_found(key)\n      @current.not_found(key)\n    end\n\n    def accept_location_not_found\n      @current.location_not_found\n    end\n\n    def accept_module_not_found(module_name)\n      push(:module, module_name)\n      @current.module_not_found\n      pop\n    end\n\n    def accept_module_provider_not_found(module_name)\n      push(:module, module_name)\n      @current.module_provider_not_found\n      pop\n    end\n\n    def accept_result(result)\n      @current.result(result)\n    end\n\n    def accept_text(text)\n      @current.text(text)\n    end\n\n    def dump_on(io, indent, first_indent)\n      branches.each { |b| b.dump_on(io, indent, first_indent) }\n      dump_texts(io, indent)\n    end\n\n    def to_hash\n      branches.size == 1 ? branches[0].to_hash : super\n    end\n  end\n\n  class DebugExplainer < Explainer\n    attr_reader :wrapped_explainer\n\n    def initialize(wrapped_explainer)\n      @wrapped_explainer = wrapped_explainer\n      if wrapped_explainer.nil?\n        @current = self\n        @explain_options = false\n        @only_explain_options = false\n      else\n        @current = wrapped_explainer\n        @explain_options = wrapped_explainer.explain_options?\n        @only_explain_options = wrapped_explainer.only_explain_options?\n      end\n    end\n\n    def dump_on(io, indent, first_indent)\n      @current.equal?(self) ? super : @current.dump_on(io, indent, first_indent)\n    end\n\n    def emit_debug_info(preamble)\n      io = String.new\n      io << preamble << \"\\n\"\n      dump_on(io, '  ', '  ')\n      Puppet.debug(io.chomp!)\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/function_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'data_adapter'\nrequire_relative 'context'\nrequire_relative 'data_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass FunctionProvider\n  include DataProvider\n\n  attr_reader :parent_data_provider, :function_name, :locations\n\n  # Returns the type that all the return type of all functions must be assignable to.\n  # For `lookup_key` and `data_dig`, that will be the `Puppet::LookupValue` type. For\n  # `data_hash` it will be a Hash[Puppet::LookupKey,Puppet::LookupValue]`\n  #\n  # @return [Type] the trusted return type\n  def self.trusted_return_type\n    DataProvider.value_type\n  end\n\n  def initialize(name, parent_data_provider, function_name, options, locations)\n    @name = name\n    @parent_data_provider = parent_data_provider\n    @function_name = function_name\n    @options = options\n    @locations = locations || [nil]\n    @contexts = {}\n  end\n\n  # @return [FunctionContext] the function context associated with this provider\n  def function_context(lookup_invocation, location)\n    @contexts[location] ||= create_function_context(lookup_invocation)\n  end\n\n  def create_function_context(lookup_invocation)\n    FunctionContext.new(EnvironmentContext.adapt(lookup_invocation.scope.compiler.environment), module_name, function(lookup_invocation))\n  end\n\n  def module_name\n    @parent_data_provider.module_name\n  end\n\n  def name\n    \"Hierarchy entry \\\"#{@name}\\\"\"\n  end\n\n  def full_name\n    \"#{self.class::TAG} function '#{@function_name}'\"\n  end\n\n  def to_s\n    name\n  end\n\n  # Obtains the options to send to the function, optionally merged with a 'path' or 'uri' option\n  #\n  # @param [Pathname,URI] location The location to add to the options\n  # @return [Hash{String => Object}] The options hash\n  def options(location = nil)\n    location = location.location unless location.nil?\n    case location\n    when Pathname\n      @options.merge(HieraConfig::KEY_PATH => location.to_s)\n    when URI\n      @options.merge(HieraConfig::KEY_URI => location.to_s)\n    else\n      @options\n    end\n  end\n\n  def value_is_validated?\n    @value_is_validated\n  end\n\n  private\n\n  def function(lookup_invocation)\n    @function ||= load_function(lookup_invocation)\n  end\n\n  def load_function(lookup_invocation)\n    loaders = lookup_invocation.scope.compiler.loaders\n    typed_name = Loader::TypedName.new(:function, @function_name)\n    loader = if typed_name.qualified?\n               qualifier = typed_name.name_parts[0]\n               qualifier == 'environment' ? loaders.private_environment_loader : loaders.private_loader_for_module(qualifier)\n             else\n               loaders.private_environment_loader\n             end\n    te = loader.load_typed(typed_name)\n    if te.nil? || te.value.nil?\n      @parent_data_provider.config(lookup_invocation).fail(Issues::HIERA_DATA_PROVIDER_FUNCTION_NOT_FOUND,\n                                                           :function_type => self.class::TAG, :function_name => @function_name)\n    end\n    func = te.value\n    @value_is_validated = func.class.dispatcher.dispatchers.all? do |dispatcher|\n      rt = dispatcher.type.return_type\n      if rt.nil?\n        false\n      else\n        Types::TypeAsserter.assert_assignable(nil, self.class.trusted_return_type, rt) { \"Return type of '#{self.class::TAG}' function named '#{function_name}'\" }\n        true\n      end\n    end\n    func\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/global_data_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera/scope'\nrequire_relative 'configured_data_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass GlobalDataProvider < ConfiguredDataProvider\n  def place\n    'Global'\n  end\n\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    config = config(lookup_invocation)\n    if config.version == 3\n      # Hiera version 3 needs access to special scope variables\n      scope = lookup_invocation.scope\n      unless scope.is_a?(Hiera::Scope)\n        return lookup_invocation.with_scope(Hiera::Scope.new(scope)) do |hiera_invocation|\n          # Confine to global scope unless an environment data provider has been defined (same as for hiera_xxx functions)\n          adapter = lookup_invocation.lookup_adapter\n          hiera_invocation.set_global_only unless adapter.global_only? || adapter.has_environment_data_provider?(lookup_invocation)\n          hiera_invocation.lookup(key, lookup_invocation.module_name) { unchecked_key_lookup(key, hiera_invocation, merge) }\n        end\n      end\n\n      merge = MergeStrategy.strategy(merge)\n      unless config.merge_strategy.is_a?(DefaultMergeStrategy)\n        if lookup_invocation.hiera_xxx_call? && merge.is_a?(HashMergeStrategy)\n          # Merge strategy defined in the hiera config only applies when the call stems from a hiera_hash call.\n          merge = config.merge_strategy\n          lookup_invocation.set_hiera_v3_merge_behavior\n        end\n      end\n\n      value = super(key, lookup_invocation, merge)\n      if lookup_invocation.hiera_xxx_call?\n        if merge.is_a?(HashMergeStrategy) || merge.is_a?(DeepMergeStrategy)\n          # hiera_hash calls should error when found values are not hashes\n          Types::TypeAsserter.assert_instance_of('value', Types::PHashType::DEFAULT, value)\n        end\n        if !key.segments.nil? && (merge.is_a?(HashMergeStrategy) || merge.is_a?(UniqueMergeStrategy))\n          strategy = merge.is_a?(HashMergeStrategy) ? 'hash' : 'array'\n\n          # Fail with old familiar message from Hiera 3\n          raise Puppet::DataBinding::LookupError, \"Resolution type :#{strategy} is illegal when accessing values using dotted keys. Offending key was '#{key}'\"\n        end\n      end\n      value\n    else\n      super\n    end\n  end\n\n  protected\n\n  def assert_config_version(config)\n    config.fail(Issues::HIERA_UNSUPPORTED_VERSION_IN_GLOBAL) if config.version == 4\n    config\n  end\n\n  # Return the root of the environment\n  #\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @return [Pathname] Path to the parent of the hiera configuration file\n  def provider_root(lookup_invocation)\n    configuration_path(lookup_invocation).parent\n  end\n\n  def configuration_path(lookup_invocation)\n    lookup_invocation.global_hiera_config_path\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/hiera_config.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'data_dig_function_provider'\nrequire_relative 'data_hash_function_provider'\nrequire_relative 'lookup_key_function_provider'\nrequire_relative 'location_resolver'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass ScopeLookupCollectingInvocation < Invocation\n  def initialize(scope)\n    super(scope)\n    @scope_interpolations = []\n  end\n\n  def remember_scope_lookup(key, root_key, segments, value)\n    @scope_interpolations << [key, root_key, segments, value] unless !value.nil? && key.start_with?('::')\n  end\n\n  def scope_interpolations\n    # Save extra checks by keeping the array unique with respect to the key (first entry)\n    @scope_interpolations.uniq! { |si| si[0] }\n    @scope_interpolations\n  end\n\n  # Yield invocation that remembers all but the given name\n  def with_local_memory_eluding(name)\n    save_si = @scope_interpolations\n    @scope_interpolations = []\n    result = yield\n    save_si.concat(@scope_interpolations.reject { |entry| entry[1] == name })\n    @scope_interpolations = save_si\n    result\n  end\nend\n\n# @api private\nclass HieraConfig\n  include LocationResolver\n  include LabelProvider\n\n  CONFIG_FILE_NAME = 'hiera.yaml'\n\n  KEY_NAME = 'name'\n  KEY_VERSION = 'version'\n  KEY_DATADIR = 'datadir'\n  KEY_DEFAULT_HIERARCHY = 'default_hierarchy'\n  KEY_HIERARCHY = 'hierarchy'\n  KEY_PLAN_HIERARCHY = 'plan_hierarchy'\n  KEY_LOGGER = 'logger'\n  KEY_OPTIONS = 'options'\n  KEY_PATH = 'path'\n  KEY_PATHS = 'paths'\n  KEY_MAPPED_PATHS = 'mapped_paths'\n  KEY_GLOB = 'glob'\n  KEY_GLOBS = 'globs'\n  KEY_URI = 'uri'\n  KEY_URIS = 'uris'\n  KEY_DEFAULTS = 'defaults'\n  KEY_DATA_HASH = DataHashFunctionProvider::TAG\n  KEY_LOOKUP_KEY = LookupKeyFunctionProvider::TAG\n  KEY_DATA_DIG = DataDigFunctionProvider::TAG\n  KEY_V3_DATA_HASH = V3DataHashFunctionProvider::TAG\n  KEY_V3_LOOKUP_KEY = V3LookupKeyFunctionProvider::TAG\n  KEY_V3_BACKEND = V3BackendFunctionProvider::TAG\n  KEY_V4_DATA_HASH = V4DataHashFunctionProvider::TAG\n  KEY_BACKEND = 'backend'\n  KEY_EXTENSION = 'extension'\n\n  FUNCTION_KEYS = [KEY_DATA_HASH, KEY_LOOKUP_KEY, KEY_DATA_DIG, KEY_V3_BACKEND]\n  ALL_FUNCTION_KEYS = FUNCTION_KEYS + [KEY_V4_DATA_HASH]\n  LOCATION_KEYS = [KEY_PATH, KEY_PATHS, KEY_GLOB, KEY_GLOBS, KEY_URI, KEY_URIS, KEY_MAPPED_PATHS]\n  FUNCTION_PROVIDERS = {\n    KEY_DATA_HASH => DataHashFunctionProvider,\n    KEY_DATA_DIG => DataDigFunctionProvider,\n    KEY_LOOKUP_KEY => LookupKeyFunctionProvider,\n    KEY_V3_DATA_HASH => V3DataHashFunctionProvider,\n    KEY_V3_BACKEND => V3BackendFunctionProvider,\n    KEY_V3_LOOKUP_KEY => V3LookupKeyFunctionProvider,\n    KEY_V4_DATA_HASH => V4DataHashFunctionProvider\n  }\n\n  def self.v4_function_config(config_root, function_name, owner)\n    unless Puppet[:strict] == :off\n      Puppet.warn_once('deprecations', 'legacy_provider_function',\n                       _(\"Using of legacy data provider function '%{function_name}'. Please convert to a 'data_hash' function\") % { function_name: function_name })\n    end\n    HieraConfigV5.new(config_root, nil,\n                      {\n                        KEY_VERSION => 5,\n                        KEY_HIERARCHY => [\n                          {\n                            KEY_NAME => \"Legacy function '#{function_name}'\",\n                            KEY_V4_DATA_HASH => function_name\n                          }\n                        ]\n                      }.freeze,\n                      owner)\n  end\n\n  def self.config_exist?(config_root)\n    config_path = config_root + CONFIG_FILE_NAME\n    config_path.exist?\n  end\n\n  def self.symkeys_to_string(struct)\n    case struct\n    when Hash\n      map = {}\n      struct.each_pair { |k, v| map[k.is_a?(Symbol) ? k.to_s : k] = symkeys_to_string(v) }\n      map\n    when Array\n      struct.map { |v| symkeys_to_string(v) }\n    else\n      struct\n    end\n  end\n\n  # Creates a new HieraConfig from the given _config_root_. This is where the 'hiera.yaml' is expected to be found\n  # and is also the base location used when resolving relative paths.\n  #\n  # @param lookup_invocation [Invocation] Invocation data containing scope, overrides, and defaults\n  # @param config_path [Pathname] Absolute path to the configuration file\n  # @param owner [ConfiguredDataProvider] The data provider that will own the created configuration\n  # @return [LookupConfiguration] the configuration\n  def self.create(lookup_invocation, config_path, owner)\n    if config_path.is_a?(Hash)\n      config_path = nil\n      loaded_config = config_path\n    else\n      config_root = config_path.parent\n      if config_path.exist?\n        env_context = EnvironmentContext.adapt(lookup_invocation.scope.compiler.environment)\n        loaded_config = env_context.cached_file_data(config_path) do |content|\n          parsed = Puppet::Util::Yaml.safe_load(content, [Symbol], config_path)\n\n          # For backward compatibility, we must treat an empty file, or a yaml that doesn't\n          # produce a Hash as Hiera version 3 default.\n          if parsed.is_a?(Hash)\n            parsed\n          else\n            Puppet.warning(_(\"%{config_path}: File exists but does not contain a valid YAML hash. Falling back to Hiera version 3 default config\") % { config_path: config_path })\n            HieraConfigV3::DEFAULT_CONFIG_HASH\n          end\n        end\n      else\n        config_path = nil\n        loaded_config = HieraConfigV5::DEFAULT_CONFIG_HASH\n      end\n    end\n\n    version = loaded_config[KEY_VERSION] || loaded_config[:version]\n    version = version.nil? ? 3 : version.to_i\n    case version\n    when 5\n      HieraConfigV5.new(config_root, config_path, loaded_config, owner)\n    when 4\n      HieraConfigV4.new(config_root, config_path, loaded_config, owner)\n    when 3\n      HieraConfigV3.new(config_root, config_path, loaded_config, owner)\n    else\n      issue = Issues::HIERA_UNSUPPORTED_VERSION\n      raise Puppet::DataBinding::LookupError.new(\n        issue.format(:version => version), config_path, nil, nil, nil, issue.issue_code\n      )\n    end\n  end\n\n  attr_reader :config_path\n\n  # Creates a new HieraConfig from the given _config_root_. This is where the 'lookup.yaml' is expected to be found\n  # and is also the base location used when resolving relative paths.\n  #\n  # @param config_path [Pathname] Absolute path to the configuration\n  # @param loaded_config [Hash] the loaded configuration\n  def initialize(config_root, config_path, loaded_config, owner)\n    @config_root = config_root\n    @config_path = config_path\n    @loaded_config = loaded_config\n    @config = validate_config(self.class.symkeys_to_string(@loaded_config), owner)\n    @data_providers = nil\n  end\n\n  def fail(issue, args = EMPTY_HASH, line = nil)\n    raise Puppet::DataBinding::LookupError.new(\n      issue.format(args.merge(:label => self)), @config_path, line, nil, nil, issue.issue_code\n    )\n  end\n\n  def has_default_hierarchy?\n    false\n  end\n\n  # Returns the data providers for this config\n  #\n  # @param lookup_invocation [Invocation] Invocation data containing scope, overrides, and defaults\n  # @param parent_data_provider [DataProvider] The data provider that loaded this configuration\n  # @return [Array<DataProvider>] the data providers\n  def configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy = false)\n    unless @data_providers && scope_interpolations_stable?(lookup_invocation)\n      if @data_providers\n        lookup_invocation.report_text { _('Hiera configuration recreated due to change of scope variables used in interpolation expressions') }\n      end\n      slc_invocation = ScopeLookupCollectingInvocation.new(lookup_invocation.scope)\n      begin\n        @data_providers = create_configured_data_providers(slc_invocation, parent_data_provider, false)\n        if has_default_hierarchy?\n          @default_data_providers = create_configured_data_providers(slc_invocation, parent_data_provider, true)\n        end\n      rescue StandardError => e\n        # Raise a LookupError with a RUNTIME_ERROR issue to prevent this being translated to an evaluation error triggered in the pp file\n        # where the lookup started\n        if e.message =~ /^Undefined variable '([^']+)'/\n          var = ::Regexp.last_match(1)\n          fail(Issues::HIERA_UNDEFINED_VARIABLE, { :name => var }, find_line_matching(/%\\{['\"]?#{var}['\"]?}/))\n        end\n        raise e\n      end\n      @scope_interpolations = slc_invocation.scope_interpolations\n    end\n    use_default_hierarchy ? @default_data_providers : @data_providers\n  end\n\n  # Find first line in configuration that matches regexp after given line. Comments are stripped\n  def find_line_matching(regexp, start_line = 1)\n    line_number = 0\n    File.foreach(@config_path) do |line|\n      line_number += 1\n      next if line_number < start_line\n\n      quote = nil\n      stripped = ''.dup\n      line.each_codepoint do |cp|\n        case cp\n        when 0x22, 0x27 # double or single quote\n          if quote == cp\n            quote = nil\n          elsif quote.nil?\n            quote = cp\n          end\n        when 0x23 # unquoted hash mark\n          break\n        end\n        stripped << cp\n      end\n      return line_number if stripped =~ regexp\n    end\n    nil\n  end\n\n  def scope_interpolations_stable?(lookup_invocation)\n    if @scope_interpolations.empty?\n      true\n    else\n      scope = lookup_invocation.scope\n      lookup_invocation.without_explain do\n        @scope_interpolations.all? do |key, root_key, segments, old_value|\n          value = Puppet.override(avoid_hiera_interpolation_errors: true) { scope[root_key] }\n          unless value.nil? || segments.empty?\n            found = nil;\n            catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }\n            value = found;\n          end\n          old_value.eql?(value)\n        end\n      end\n    end\n  end\n\n  # @api private\n  def create_configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy)\n    self.class.not_implemented(self, 'create_configured_data_providers')\n  end\n\n  def validate_config(config, owner)\n    self.class.not_implemented(self, 'validate_config')\n  end\n\n  def version\n    self.class.not_implemented(self, 'version')\n  end\n\n  def name\n    \"hiera configuration version #{version}\"\n  end\n\n  def create_hiera3_backend_provider(name, backend, parent_data_provider, datadir, paths, hiera3_config)\n    # Custom backend. Hiera 3 must be installed, its logger configured, and it must be made aware of the loaded config\n    raise Puppet::DataBinding::LookupError, 'Hiera 3 is not installed' unless Puppet.features.hiera?\n\n    if Hiera::Config.instance_variable_defined?(:@config) && (current_config = Hiera::Config.instance_variable_get(:@config)).is_a?(Hash)\n      current_config.each_pair do |key, val|\n        case key\n        when :hierarchy, :backends\n          hiera3_config[key] = ([val] + [hiera3_config[key]]).flatten.uniq\n        else\n          hiera3_config[key] = val\n        end\n      end\n    elsif hiera3_config.include?(KEY_LOGGER)\n      Hiera.logger = hiera3_config[KEY_LOGGER].to_s\n    else\n      Hiera.logger = 'puppet'\n    end\n\n    unless Hiera::Interpolate.const_defined?(:PATCHED_BY_HIERA_5)\n      # Replace the class methods 'hiera_interpolate' and 'alias_interpolate' with a method that wires back and performs global\n      # lookups using the lookup framework. This is necessary since the classic Hiera is made aware only of custom backends.\n      class << Hiera::Interpolate\n        hiera_interpolate = proc do |_data, key, scope, _extra_data, context|\n          override = context[:order_override]\n          invocation = Puppet::Pops::Lookup::Invocation.current\n          unless override.nil? && invocation.global_only?\n            invocation = Puppet::Pops::Lookup::Invocation.new(scope)\n            invocation.set_global_only\n            invocation.set_hiera_v3_location_overrides(override) unless override.nil?\n          end\n          Puppet::Pops::Lookup::LookupAdapter.adapt(scope.compiler).lookup(key, invocation, nil)\n        end\n\n        send(:remove_method, :hiera_interpolate)\n        send(:remove_method, :alias_interpolate)\n        send(:define_method, :hiera_interpolate, hiera_interpolate)\n        send(:define_method, :alias_interpolate, hiera_interpolate)\n      end\n      Hiera::Interpolate.send(:const_set, :PATCHED_BY_HIERA_5, true)\n    end\n\n    Hiera::Config.instance_variable_set(:@config, hiera3_config)\n\n    # Use a special lookup_key that delegates to the backend\n    paths = nil if !paths.nil? && paths.empty?\n    create_data_provider(name, parent_data_provider, KEY_V3_BACKEND, 'hiera_v3_data', { KEY_DATADIR => datadir, KEY_BACKEND => backend }, paths)\n  end\n\n  private\n\n  def create_data_provider(name, parent_data_provider, function_kind, function_name, options, locations)\n    FUNCTION_PROVIDERS[function_kind].new(name, parent_data_provider, function_name, options, locations)\n  end\n\n  def self.not_implemented(impl, method_name)\n    raise NotImplementedError, \"The class #{impl.class.name} should have implemented the method #{method_name}()\"\n  end\n  private_class_method :not_implemented\nend\n\n# @api private\nclass HieraConfigV3 < HieraConfig\n  KEY_BACKENDS = 'backends'\n  KEY_MERGE_BEHAVIOR = 'merge_behavior'\n  KEY_DEEP_MERGE_OPTIONS = 'deep_merge_options'\n\n  def self.config_type\n    return @@CONFIG_TYPE if class_variable_defined?(:@@CONFIG_TYPE)\n\n    tf = Types::TypeFactory\n    nes_t = Types::PStringType::NON_EMPTY\n\n    # This is a hash, not a type. Contained backends are added prior to validation\n    @@CONFIG_TYPE = {\n      tf.optional(KEY_VERSION) => tf.range(3, 3),\n      tf.optional(KEY_BACKENDS) => tf.variant(nes_t, tf.array_of(nes_t)),\n      tf.optional(KEY_LOGGER) => nes_t,\n      tf.optional(KEY_MERGE_BEHAVIOR) => tf.enum('deep', 'deeper', 'native'),\n      tf.optional(KEY_DEEP_MERGE_OPTIONS) => tf.hash_kv(nes_t, tf.variant(tf.string, tf.boolean)),\n      tf.optional(KEY_HIERARCHY) => tf.variant(nes_t, tf.array_of(nes_t))\n    }\n  end\n\n  def create_configured_data_providers(lookup_invocation, parent_data_provider, _)\n    scope = lookup_invocation.scope\n    unless scope.is_a?(Hiera::Scope)\n      lookup_invocation = Invocation.new(\n        Hiera::Scope.new(scope),\n        lookup_invocation.override_values,\n        lookup_invocation.default_values,\n        lookup_invocation.explainer\n      )\n    end\n\n    default_datadir = File.join(Puppet.settings[:codedir], 'environments', '%{::environment}', 'hieradata')\n    data_providers = {}\n\n    [@config[KEY_BACKENDS]].flatten.each do |backend|\n      if data_providers.include?(backend)\n        first_line = find_line_matching(/[^\\w]#{backend}(?:[^\\w]|$)/)\n        line = find_line_matching(/[^\\w]#{backend}(?:[^\\w]|$)/, first_line + 1) if first_line\n        unless line\n          line = first_line\n          first_line = nil\n        end\n        fail(Issues::HIERA_BACKEND_MULTIPLY_DEFINED, { :name => backend, :first_line => first_line }, line)\n      end\n      original_paths = [@config[KEY_HIERARCHY]].flatten\n      backend_config = @config[backend]\n      if backend_config.nil?\n        backend_config = EMPTY_HASH\n      else\n        backend_config = interpolate(backend_config, lookup_invocation, false)\n      end\n      datadir = Pathname(backend_config[KEY_DATADIR] || interpolate(default_datadir, lookup_invocation, false))\n      ext = backend_config[KEY_EXTENSION]\n      if ext.nil?\n        ext = backend == 'hocon' ? '.conf' : \".#{backend}\"\n      else\n        ext = \".#{ext}\"\n      end\n      paths = resolve_paths(datadir, original_paths, lookup_invocation, @config_path.nil?, ext)\n      data_providers[backend] =\n        if %w[json yaml].include? backend\n          create_data_provider(backend, parent_data_provider, KEY_V3_DATA_HASH,\n                               \"#{backend}_data\", { KEY_DATADIR => datadir },\n                               paths)\n        elsif backend == 'hocon' && Puppet.features.hocon?\n          create_data_provider(backend, parent_data_provider, KEY_V3_DATA_HASH,\n                               'hocon_data', { KEY_DATADIR => datadir }, paths)\n        elsif backend == 'eyaml' && Puppet.features.hiera_eyaml?\n          create_data_provider(backend, parent_data_provider,\n                               KEY_V3_LOOKUP_KEY, 'eyaml_lookup_key',\n                               backend_config.merge(KEY_DATADIR => datadir),\n                               paths)\n        else\n          create_hiera3_backend_provider(backend, backend,\n                                         parent_data_provider, datadir, paths,\n                                         @loaded_config)\n        end\n    end\n    data_providers.values\n  end\n\n  DEFAULT_CONFIG_HASH = {\n    KEY_BACKENDS => %w[yaml],\n    KEY_HIERARCHY => %w[nodes/%{::trusted.certname} common],\n    KEY_MERGE_BEHAVIOR => 'native'\n  }\n\n  def validate_config(config, owner)\n    unless Puppet[:strict] == :off\n      Puppet.warn_once('deprecations', 'hiera.yaml',\n                       _(\"%{config_path}: Use of 'hiera.yaml' version 3 is deprecated. It should be converted to version 5\") % { config_path: @config_path }, config_path.to_s)\n    end\n    config[KEY_VERSION] ||= 3\n    config[KEY_BACKENDS] ||= DEFAULT_CONFIG_HASH[KEY_BACKENDS]\n    config[KEY_HIERARCHY] ||= DEFAULT_CONFIG_HASH[KEY_HIERARCHY]\n    config[KEY_MERGE_BEHAVIOR] ||= DEFAULT_CONFIG_HASH[KEY_MERGE_BEHAVIOR]\n    config[KEY_DEEP_MERGE_OPTIONS] ||= {}\n\n    backends = [config[KEY_BACKENDS]].flatten\n\n    # Create the final struct used for validation (backends are included as keys to arbitrary configs in the form of a hash)\n    tf = Types::TypeFactory\n    backend_elements = {}\n    backends.each { |backend| backend_elements[tf.optional(backend)] = tf.hash_kv(Types::PStringType::NON_EMPTY, tf.any) }\n    v3_struct = tf.struct(self.class.config_type.merge(backend_elements))\n\n    Types::TypeAsserter.assert_instance_of([\"The Lookup Configuration at '%s'\", @config_path], v3_struct, config)\n  end\n\n  def merge_strategy\n    @merge_strategy ||= create_merge_strategy\n  end\n\n  def version\n    3\n  end\n\n  private\n\n  def create_merge_strategy\n    key = @config[KEY_MERGE_BEHAVIOR]\n    case key\n    when nil, 'native'\n      MergeStrategy.strategy(nil)\n    when 'array'\n      MergeStrategy.strategy(:unique)\n    when 'deep', 'deeper'\n      merge = { 'strategy' => key == 'deep' ? 'reverse_deep' : 'unconstrained_deep' }\n      dm_options = @config[KEY_DEEP_MERGE_OPTIONS]\n      merge.merge!(dm_options) if dm_options\n      MergeStrategy.strategy(merge)\n    end\n  end\nend\n\n# @api private\nclass HieraConfigV4 < HieraConfig\n  def self.config_type\n    return @@CONFIG_TYPE if class_variable_defined?(:@@CONFIG_TYPE)\n\n    tf = Types::TypeFactory\n    nes_t = Types::PStringType::NON_EMPTY\n\n    @@CONFIG_TYPE =\n      tf.struct({\n                  KEY_VERSION => tf.range(4, 4),\n                  tf.optional(KEY_DATADIR) => nes_t,\n                  tf.optional(KEY_HIERARCHY) => tf.array_of(tf.struct(\n                                                              KEY_BACKEND => nes_t,\n                                                              KEY_NAME => nes_t,\n                                                              tf.optional(KEY_DATADIR) => nes_t,\n                                                              tf.optional(KEY_PATH) => nes_t,\n                                                              tf.optional(KEY_PATHS) => tf.array_of(nes_t)\n                                                            ))\n                })\n  end\n\n  def create_configured_data_providers(lookup_invocation, parent_data_provider, _)\n    default_datadir = @config[KEY_DATADIR]\n    data_providers = {}\n\n    @config[KEY_HIERARCHY].each do |he|\n      name = he[KEY_NAME]\n      if data_providers.include?(name)\n        first_line = find_line_matching(/\\s+name:\\s+['\"]?#{name}(?:[^\\w]|$)/)\n        line = find_line_matching(/\\s+name:\\s+['\"]?#{name}(?:[^\\w]|$)/, first_line + 1) if first_line\n        unless line\n          line = first_line\n          first_line = nil\n        end\n        fail(Issues::HIERA_HIERARCHY_NAME_MULTIPLY_DEFINED, { :name => name, :first_line => first_line }, line)\n      end\n      original_paths = he[KEY_PATHS] || [he[KEY_PATH] || name]\n      datadir = @config_root + (he[KEY_DATADIR] || default_datadir)\n      provider_name = he[KEY_BACKEND]\n      data_providers[name] =\n        if %w[json yaml].include?(provider_name)\n          create_data_provider(name, parent_data_provider, KEY_DATA_HASH,\n                               \"#{provider_name}_data\", {},\n                               resolve_paths(datadir,\n                                             original_paths,\n                                             lookup_invocation,\n                                             @config_path.nil?,\n                                             \".#{provider_name}\"))\n        elsif provider_name == 'hocon' && Puppet.features.hocon?\n          create_data_provider(name, parent_data_provider, KEY_DATA_HASH,\n                               'hocon_data', {},\n                               resolve_paths(datadir,\n                                             original_paths,\n                                             lookup_invocation,\n                                             @config_path.nil?,\n                                             '.conf'))\n        else\n          fail(Issues::HIERA_NO_PROVIDER_FOR_BACKEND,\n               { :name => provider_name },\n               find_line_matching(/[^\\w]#{provider_name}(?:[^\\w]|$)/))\n        end\n    end\n    data_providers.values\n  end\n\n  def validate_config(config, owner)\n    unless Puppet[:strict] == :off\n      Puppet.warn_once('deprecations', 'hiera.yaml',\n                       _(\"%{config_path}: Use of 'hiera.yaml' version 4 is deprecated. It should be converted to version 5\") % { config_path: @config_path }, config_path.to_s)\n    end\n    config[KEY_DATADIR] ||= 'data'\n    config[KEY_HIERARCHY] ||= [{ KEY_NAME => 'common', KEY_BACKEND => 'yaml' }]\n    Types::TypeAsserter.assert_instance_of([\"The Lookup Configuration at '%s'\", @config_path], self.class.config_type, config)\n  end\n\n  def version\n    4\n  end\nend\n\n# @api private\nclass HieraConfigV5 < HieraConfig\n  def self.config_type\n    return @@CONFIG_TYPE if class_variable_defined?(:@@CONFIG_TYPE_V5)\n\n    tf = Types::TypeFactory\n    nes_t = Types::PStringType::NON_EMPTY\n\n    # Validated using Ruby URI implementation\n    uri_t = Types::PStringType::NON_EMPTY\n\n    # The option name must start with a letter and end with a letter or digit. May contain underscore and dash.\n    option_name_t = tf.pattern(/\\A[A-Za-z](:?[0-9A-Za-z_-]*[0-9A-Za-z])?\\z/)\n\n    hierarchy_t =\n      tf.array_of(tf.struct(\n                    {\n                      KEY_NAME => nes_t,\n                      tf.optional(KEY_OPTIONS) => tf.hash_kv(option_name_t, tf.data),\n                      tf.optional(KEY_DATA_HASH) => nes_t,\n                      tf.optional(KEY_LOOKUP_KEY) => nes_t,\n                      tf.optional(KEY_V3_BACKEND) => nes_t,\n                      tf.optional(KEY_V4_DATA_HASH) => nes_t,\n                      tf.optional(KEY_DATA_DIG) => nes_t,\n                      tf.optional(KEY_PATH) => nes_t,\n                      tf.optional(KEY_PATHS) => tf.array_of(nes_t, tf.range(1, :default)),\n                      tf.optional(KEY_GLOB) => nes_t,\n                      tf.optional(KEY_GLOBS) => tf.array_of(nes_t, tf.range(1, :default)),\n                      tf.optional(KEY_URI) => uri_t,\n                      tf.optional(KEY_URIS) => tf.array_of(uri_t, tf.range(1, :default)),\n                      tf.optional(KEY_MAPPED_PATHS) => tf.array_of(nes_t, tf.range(3, 3)),\n                      tf.optional(KEY_DATADIR) => nes_t\n                    }\n                  ))\n\n    @@CONFIG_TYPE =\n      tf.struct({\n                  KEY_VERSION => tf.range(5, 5),\n                  tf.optional(KEY_DEFAULTS) => tf.struct(\n                    {\n                      tf.optional(KEY_DATA_HASH) => nes_t,\n                      tf.optional(KEY_LOOKUP_KEY) => nes_t,\n                      tf.optional(KEY_DATA_DIG) => nes_t,\n                      tf.optional(KEY_DATADIR) => nes_t,\n                      tf.optional(KEY_OPTIONS) => tf.hash_kv(option_name_t, tf.data),\n                    }\n                  ),\n                  tf.optional(KEY_HIERARCHY) => hierarchy_t,\n                  tf.optional(KEY_PLAN_HIERARCHY) => hierarchy_t,\n                  tf.optional(KEY_DEFAULT_HIERARCHY) => hierarchy_t\n                })\n  end\n\n  def create_configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy)\n    defaults = @config[KEY_DEFAULTS] || EMPTY_HASH\n    datadir = defaults[KEY_DATADIR] || 'data'\n\n    # Hashes enumerate their values in the order that the corresponding keys were inserted so it's safe to use\n    # a hash for the data_providers.\n    data_providers = {}\n\n    if @config.include?(KEY_DEFAULT_HIERARCHY)\n      unless parent_data_provider.is_a?(ModuleDataProvider)\n        fail(Issues::HIERA_DEFAULT_HIERARCHY_NOT_IN_MODULE, EMPTY_HASH, find_line_matching(/\\s+default_hierarchy:/))\n      end\n    elsif use_default_hierarchy\n      return data_providers\n    end\n\n    compiler = Puppet.lookup(:pal_compiler) { nil }\n    config_key = if compiler.is_a?(Puppet::Pal::ScriptCompiler) && !@config[KEY_PLAN_HIERARCHY].nil?\n                   KEY_PLAN_HIERARCHY\n                 elsif use_default_hierarchy\n                   KEY_DEFAULT_HIERARCHY\n                 else\n                   KEY_HIERARCHY\n                 end\n    @config[config_key].each do |he|\n      name = he[KEY_NAME]\n      if data_providers.include?(name)\n        first_line = find_line_matching(/\\s+name:\\s+['\"]?#{name}(?:[^\\w]|$)/)\n        line = find_line_matching(/\\s+name:\\s+['\"]?#{name}(?:[^\\w]|$)/, first_line + 1) if first_line\n        unless line\n          line = first_line\n          first_line = nil\n        end\n        fail(Issues::HIERA_HIERARCHY_NAME_MULTIPLY_DEFINED, { :name => name, :first_line => first_line }, line)\n      end\n      function_kind = ALL_FUNCTION_KEYS.find { |key| he.include?(key) }\n      if function_kind.nil?\n        function_kind = FUNCTION_KEYS.find { |key| defaults.include?(key) }\n        function_name = defaults[function_kind]\n      else\n        function_name = he[function_kind]\n      end\n\n      entry_datadir = @config_root + (he[KEY_DATADIR] || datadir)\n      entry_datadir = Pathname(interpolate(entry_datadir.to_s, lookup_invocation, false))\n      location_key = LOCATION_KEYS.find { |key| he.include?(key) }\n      locations = []\n      Puppet.override(avoid_hiera_interpolation_errors: true) do\n        locations = case location_key\n                    when KEY_PATHS\n                      resolve_paths(entry_datadir, he[location_key], lookup_invocation, @config_path.nil?)\n                    when KEY_PATH\n                      resolve_paths(entry_datadir, [he[location_key]], lookup_invocation, @config_path.nil?)\n                    when KEY_GLOBS\n                      expand_globs(entry_datadir, he[location_key], lookup_invocation)\n                    when KEY_GLOB\n                      expand_globs(entry_datadir, [he[location_key]], lookup_invocation)\n                    when KEY_URIS\n                      expand_uris(he[location_key], lookup_invocation)\n                    when KEY_URI\n                      expand_uris([he[location_key]], lookup_invocation)\n                    when KEY_MAPPED_PATHS\n                      expand_mapped_paths(entry_datadir, he[location_key], lookup_invocation)\n                    else\n                      nil\n                    end\n      end\n      next if @config_path.nil? && !locations.nil? && locations.empty? # Default config and no existing paths found\n\n      options = he[KEY_OPTIONS] || defaults[KEY_OPTIONS]\n      options = options.nil? ? EMPTY_HASH : interpolate(options, lookup_invocation, false)\n      if function_kind == KEY_V3_BACKEND\n        v3options = { :datadir => entry_datadir.to_s }\n        options.each_pair { |k, v| v3options[k.to_sym] = v }\n        data_providers[name] =\n          create_hiera3_backend_provider(name,\n                                         function_name,\n                                         parent_data_provider,\n                                         entry_datadir,\n                                         locations,\n                                         {\n                                           :hierarchy =>\n                                             if locations.nil?\n                                               []\n                                             else\n                                               locations.map do |loc|\n                                                 path = loc.original_location\n                                                 path.end_with?(\".#{function_name}\") ? path[0..-(function_name.length + 2)] : path\n                                               end\n                                             end,\n                                           function_name.to_sym => v3options,\n                                           :backends => [function_name],\n                                           :logger => 'puppet'\n                                         })\n      else\n        data_providers[name] = create_data_provider(name, parent_data_provider, function_kind, function_name, options, locations)\n      end\n    end\n    data_providers.values\n  end\n\n  def has_default_hierarchy?\n    @config.include?(KEY_DEFAULT_HIERARCHY)\n  end\n\n  RESERVED_OPTION_KEYS = %w[path uri].freeze\n\n  DEFAULT_CONFIG_HASH = {\n    KEY_VERSION => 5,\n    KEY_DEFAULTS => {\n      KEY_DATADIR => 'data',\n      KEY_DATA_HASH => 'yaml_data'\n    },\n    KEY_HIERARCHY => [\n      {\n        KEY_NAME => 'Common',\n        KEY_PATH => 'common.yaml',\n      }\n    ]\n  }.freeze\n\n  def validate_config(config, owner)\n    config[KEY_DEFAULTS] ||= DEFAULT_CONFIG_HASH[KEY_DEFAULTS]\n    config[KEY_HIERARCHY] ||= DEFAULT_CONFIG_HASH[KEY_HIERARCHY]\n\n    Types::TypeAsserter.assert_instance_of([\"The Lookup Configuration at '%s'\", @config_path], self.class.config_type, config)\n    defaults = config[KEY_DEFAULTS]\n    validate_defaults(defaults) unless defaults.nil?\n    config[KEY_HIERARCHY].each { |he| validate_hierarchy(he, defaults, owner) }\n    if config.include?(KEY_PLAN_HIERARCHY)\n      config[KEY_PLAN_HIERARCHY].each { |he| validate_hierarchy(he, defaults, owner) }\n    end\n\n    if config.include?(KEY_DEFAULT_HIERARCHY)\n      unless owner.is_a?(ModuleDataProvider)\n        fail(Issues::HIERA_DEFAULT_HIERARCHY_NOT_IN_MODULE, EMPTY_HASH, find_line_matching(/(?:^|\\s+)#{KEY_DEFAULT_HIERARCHY}:/))\n      end\n\n      config[KEY_DEFAULT_HIERARCHY].each { |he| validate_hierarchy(he, defaults, owner) }\n    end\n    config\n  end\n\n  def validate_hierarchy(he, defaults, owner)\n    name = he[KEY_NAME]\n    case ALL_FUNCTION_KEYS.count { |key| he.include?(key) }\n    when 0\n      if defaults.nil? || FUNCTION_KEYS.count { |key| defaults.include?(key) } == 0\n        fail(Issues::HIERA_MISSING_DATA_PROVIDER_FUNCTION, :name => name)\n      end\n    when 1\n      # OK\n    else\n      fail(Issues::HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS, :name => name)\n    end\n\n    v3_backend = he[KEY_V3_BACKEND]\n    unless v3_backend.nil?\n      unless owner.is_a?(GlobalDataProvider)\n        fail(Issues::HIERA_V3_BACKEND_NOT_GLOBAL, EMPTY_HASH, find_line_matching(/\\s+#{KEY_V3_BACKEND}:/))\n      end\n\n      if v3_backend == 'json' || v3_backend == 'yaml' || v3_backend == 'hocon' && Puppet.features.hocon?\n        # Disallow use of backends that have corresponding \"data_hash\" functions in version 5\n        fail(Issues::HIERA_V3_BACKEND_REPLACED_BY_DATA_HASH, { :function_name => v3_backend },\n             find_line_matching(/\\s+#{KEY_V3_BACKEND}:\\s*['\"]?#{v3_backend}(?:[^\\w]|$)/))\n      end\n    end\n\n    if LOCATION_KEYS.count { |key| he.include?(key) } > 1\n      fail(Issues::HIERA_MULTIPLE_LOCATION_SPECS, :name => name)\n    end\n\n    options = he[KEY_OPTIONS]\n    unless options.nil?\n      RESERVED_OPTION_KEYS.each do |key|\n        fail(Issues::HIERA_OPTION_RESERVED_BY_PUPPET, :key => key, :name => name) if options.include?(key)\n      end\n    end\n  end\n\n  def validate_defaults(defaults)\n    case FUNCTION_KEYS.count { |key| defaults.include?(key) }\n    when 0, 1\n      # OK\n    else\n      fail(Issues::HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS_IN_DEFAULT)\n    end\n\n    options = defaults[KEY_OPTIONS]\n    unless options.nil?\n      RESERVED_OPTION_KEYS.each do |key|\n        fail(Issues::HIERA_DEFAULT_OPTION_RESERVED_BY_PUPPET, :key => key) if options.include?(key)\n      end\n    end\n  end\n\n  def version\n    5\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/interpolation.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hiera/scope'\nrequire_relative 'sub_lookup'\nmodule Puppet::Pops\nmodule Lookup\n# Adds support for interpolation expressions. The expressions may contain keys that uses dot-notation\n# to further navigate into hashes and arrays\n#\n# @api public\nmodule Interpolation\n  include SubLookup\n\n  # @param value [Object] The value to interpolate\n  # @param context [Context] The current lookup context\n  # @param allow_methods [Boolean] `true` if interpolation expression that contains lookup methods are allowed\n  # @return [Object] the result of resolving all interpolations in the given value\n  # @api public\n  def interpolate(value, context, allow_methods)\n    case value\n    when String\n      value.index('%{').nil? ? value : interpolate_string(value, context, allow_methods)\n    when Array\n      value.map { |element| interpolate(element, context, allow_methods) }\n    when Hash\n      result = {}\n      value.each_pair { |k, v| result[interpolate(k, context, allow_methods)] = interpolate(v, context, allow_methods) }\n      result\n    else\n      value\n    end\n  end\n\n  private\n\n  EMPTY_INTERPOLATIONS = {\n    '' => true,\n    '::' => true,\n    '\"\"' => true,\n    \"''\" => true,\n    '\"::\"' => true,\n    \"'::'\" => true\n  }.freeze\n\n  # Matches a key that is quoted using a matching pair of either single or double quotes.\n  QUOTED_KEY = /^(?:\"([^\"]+)\"|'([^']+)')$/\n\n  def interpolate_string(subject, context, allow_methods)\n    lookup_invocation = context.is_a?(Invocation) ? context : context.invocation\n    lookup_invocation.with(:interpolate, subject) do\n      subject.gsub(/%\\{([^}]*)\\}/) do |match|\n        expr = ::Regexp.last_match(1)\n        # Leading and trailing spaces inside an interpolation expression are insignificant\n        expr.strip!\n        value = nil\n        unless EMPTY_INTERPOLATIONS[expr]\n          method_key, key = get_method_and_data(expr, allow_methods)\n          is_alias = method_key == :alias\n\n          # Alias is only permitted if the entire string is equal to the interpolate expression\n          fail(Issues::HIERA_INTERPOLATION_ALIAS_NOT_ENTIRE_STRING) if is_alias && subject != match\n\n          value = interpolate_method(method_key).call(key, lookup_invocation, subject)\n\n          # break gsub and return value immediately if this was an alias substitution. The value might be something other than a String\n          return value if is_alias\n\n          value = lookup_invocation.check(method_key == :scope ? \"scope:#{key}\" : key) { interpolate(value, lookup_invocation, allow_methods) }\n        end\n        value.nil? ? '' : value\n      end\n    end\n  end\n\n  def interpolate_method(method_key)\n    @@interpolate_methods ||= begin\n      global_lookup = lambda do |key, lookup_invocation, _|\n        scope = lookup_invocation.scope\n        if scope.is_a?(Hiera::Scope) && !lookup_invocation.global_only?\n          # \"unwrap\" the Hiera::Scope\n          scope = scope.real\n        end\n        lookup_invocation.with_scope(scope) do |sub_invocation|\n          sub_invocation.lookup(key) { Lookup.lookup(key, nil, '', true, nil, sub_invocation) }\n        end\n      end\n      scope_lookup = lambda do |key, lookup_invocation, subject|\n        segments = split_key(key) { |problem| Puppet::DataBinding::LookupError.new(\"#{problem} in string: #{subject}\") }\n        root_key = segments.shift\n        value = lookup_invocation.with(:scope, 'Global Scope') do\n          ovr = lookup_invocation.override_values\n          if ovr.include?(root_key)\n            lookup_invocation.report_found_in_overrides(root_key, ovr[root_key])\n          else\n            scope = lookup_invocation.scope\n            val = nil\n            if (default_key_exists = lookup_invocation.default_values.include?(root_key))\n              catch(:undefined_variable) { val = scope[root_key] }\n            else\n              val = scope[root_key]\n            end\n            if val.nil? && !nil_in_scope?(scope, root_key)\n              if default_key_exists\n                lookup_invocation.report_found_in_defaults(root_key,\n                                                           lookup_invocation.default_values[root_key])\n              else\n                nil\n              end\n            else\n              lookup_invocation.report_found(root_key, val)\n            end\n          end\n        end\n        unless value.nil? || segments.empty?\n          found = nil;\n          catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }\n          value = found;\n        end\n        lookup_invocation.remember_scope_lookup(key, root_key, segments, value)\n        value\n      end\n\n      {\n        :lookup => global_lookup,\n        :hiera => global_lookup, # this is just an alias for 'lookup'\n        :alias => global_lookup, # same as 'lookup' but expression must be entire string and result is not subject to string substitution\n        :scope => scope_lookup,\n        :literal => ->(key, _, _) { key }\n      }.freeze\n    end\n    interpolate_method = @@interpolate_methods[method_key]\n    fail(Issues::HIERA_INTERPOLATION_UNKNOWN_INTERPOLATION_METHOD, :name => method_key) unless interpolate_method\n\n    interpolate_method\n  end\n\n  # Because the semantics of Puppet::Parser::Scope#include? differs from Hash#include?\n  def nil_in_scope?(scope, key)\n    if scope.is_a?(Hash)\n      scope.include?(key)\n    else\n      scope.exist?(key)\n    end\n  end\n\n  def get_method_and_data(data, allow_methods)\n    match = data.match(/^(\\w+)\\((?:\"([^\"]+)\"|'([^']+)')\\)$/)\n    if match\n      fail(Issues::HIERA_INTERPOLATION_METHOD_SYNTAX_NOT_ALLOWED) unless allow_methods\n\n      key = match[1].to_sym\n      data = match[2] || match[3] # double or single qouted\n    else\n      key = :scope\n    end\n    [key, data]\n  end\n\n  def fail(issue, args = EMPTY_HASH)\n    raise Puppet::DataBinding::LookupError.new(\n      issue.format(args), nil, nil, nil, nil, issue.issue_code\n    )\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/invocation.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/thread_local'\n\nrequire_relative 'explainer'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass Invocation\n  attr_reader :scope, :override_values, :default_values, :explainer, :module_name, :top_key, :adapter_class\n\n  def self.current\n    (@current ||= Puppet::ThreadLocal.new(nil)).value\n  end\n\n  def self.current=(new_value)\n    @current.value = new_value\n  end\n\n  # Creates a new instance with same settings as this instance but with a new given scope\n  # and yields with that scope.\n  #\n  # @param scope [Puppet::Parser::Scope] The new scope\n  # @return [Invocation] the new instance\n  def with_scope(scope)\n    yield(Invocation.new(scope, override_values, default_values, explainer))\n  end\n\n  # Creates a context object for a lookup invocation. The object contains the current scope, overrides, and default\n  # values and may optionally contain an {ExplanationAcceptor} instance that will receive book-keeping information\n  # about the progress of the lookup.\n  #\n  # If the _explain_ argument is a boolean, then _false_ means that no explanation is needed and _true_ means that\n  # the default explanation acceptor should be used. The _explain_ argument may also be an instance of the\n  # `ExplanationAcceptor` class.\n  #\n  # @param scope [Puppet::Parser::Scope] The scope to use for the lookup\n  # @param override_values [Hash<String,Object>|nil] A map to use as override. Values found here are returned immediately (no merge)\n  # @param default_values [Hash<String,Object>] A map to use as the last resort (but before default)\n  # @param explainer [boolean,Explanainer] An boolean true to use the default explanation acceptor or an explainer instance that will receive information about the lookup\n  def initialize(scope, override_values = EMPTY_HASH, default_values = EMPTY_HASH, explainer = nil, adapter_class = nil)\n    @scope = scope\n    @override_values = override_values\n    @default_values = default_values\n\n    parent_invocation = self.class.current\n\n    if parent_invocation && (adapter_class.nil? || adapter_class == parent_invocation.adapter_class)\n      # Inherit from parent invocation (track recursion)\n      @name_stack = parent_invocation.name_stack\n      @adapter_class = parent_invocation.adapter_class\n\n      # Inherit Hiera 3 legacy properties\n      set_hiera_xxx_call if parent_invocation.hiera_xxx_call?\n      set_hiera_v3_merge_behavior if parent_invocation.hiera_v3_merge_behavior?\n      set_global_only if parent_invocation.global_only?\n      povr = parent_invocation.hiera_v3_location_overrides\n      set_hiera_v3_location_overrides(povr) unless povr.empty?\n\n      # Inherit explainer unless a new explainer is given or disabled using false\n      explainer = explainer == false ? nil : parent_invocation.explainer\n    else\n      @name_stack = []\n      @adapter_class = adapter_class.nil? ? LookupAdapter : adapter_class\n      unless explainer.is_a?(Explainer)\n        explainer = explainer == true ? Explainer.new : nil\n      end\n      explainer = DebugExplainer.new(explainer) if Puppet[:debug] && !explainer.is_a?(DebugExplainer)\n    end\n    @explainer = explainer\n  end\n\n  def lookup(key, module_name = nil)\n    key = LookupKey.new(key) unless key.is_a?(LookupKey)\n    @top_key = key\n    @module_name = module_name.nil? ? key.module_name : module_name\n    save_current = self.class.current\n    if save_current.equal?(self)\n      yield\n    else\n      begin\n        self.class.current = self\n        yield\n      ensure\n        self.class.current = save_current\n      end\n    end\n  end\n\n  def check(name)\n    if @name_stack.include?(name)\n      raise Puppet::DataBinding::RecursiveLookupError, _(\"Recursive lookup detected in [%{name_stack}]\") % { name_stack: @name_stack.join(', ') }\n    end\n    return unless block_given?\n\n    @name_stack.push(name)\n    begin\n      yield\n    rescue Puppet::DataBinding::LookupError\n      raise\n    rescue Puppet::Error => detail\n      raise Puppet::DataBinding::LookupError.new(detail.message, nil, nil, nil, detail)\n    ensure\n      @name_stack.pop\n    end\n  end\n\n  def emit_debug_info(preamble)\n    @explainer.emit_debug_info(preamble) if @explainer.is_a?(DebugExplainer)\n  end\n\n  # rubocop:disable Naming/MemoizedInstanceVariableName\n  def lookup_adapter\n    @adapter ||= @adapter_class.adapt(scope.compiler)\n  end\n  # rubocop:enable Naming/MemoizedInstanceVariableName\n\n  # This method is overridden by the special Invocation used while resolving interpolations in a\n  # Hiera configuration file (hiera.yaml) where it's used for collecting and remembering the current\n  # values that the configuration was based on\n  #\n  # @api private\n  def remember_scope_lookup(*lookup_result)\n    # Does nothing by default\n  end\n\n  # The qualifier_type can be one of:\n  # :global - qualifier is the data binding terminus name\n  # :data_provider - qualifier a DataProvider instance\n  # :path - qualifier is a ResolvedPath instance\n  # :merge - qualifier is a MergeStrategy instance\n  # :interpolation - qualifier is the unresolved interpolation expression\n  # :meta - qualifier is the module name\n  # :data - qualifier is the key\n  #\n  # @param qualifier [Object] A branch, a provider, or a path\n  def with(qualifier_type, qualifier)\n    if explainer.nil?\n      yield\n    else\n      @explainer.push(qualifier_type, qualifier)\n      begin\n        yield\n      ensure\n        @explainer.pop\n      end\n    end\n  end\n\n  def without_explain\n    if explainer.nil?\n      yield\n    else\n      save_explainer = @explainer\n      begin\n        @explainer = nil\n        yield\n      ensure\n        @explainer = save_explainer\n      end\n    end\n  end\n\n  def only_explain_options?\n    @explainer.nil? ? false : @explainer.only_explain_options?\n  end\n\n  def explain_options?\n    @explainer.nil? ? false : @explainer.explain_options?\n  end\n\n  def report_found_in_overrides(key, value)\n    @explainer.accept_found_in_overrides(key, value) unless @explainer.nil?\n    value\n  end\n\n  def report_found_in_defaults(key, value)\n    @explainer.accept_found_in_defaults(key, value) unless @explainer.nil?\n    value\n  end\n\n  def report_found(key, value)\n    @explainer.accept_found(key, value) unless @explainer.nil?\n    value\n  end\n\n  def report_merge_source(merge_source)\n    @explainer.accept_merge_source(merge_source) unless @explainer.nil?\n  end\n\n  # Report the result of a merge or fully resolved interpolated string\n  # @param value [Object] The result to report\n  # @return [Object] the given value\n  def report_result(value)\n    @explainer.accept_result(value) unless @explainer.nil?\n    value\n  end\n\n  def report_not_found(key)\n    @explainer.accept_not_found(key) unless @explainer.nil?\n  end\n\n  def report_location_not_found\n    @explainer.accept_location_not_found unless @explainer.nil?\n  end\n\n  def report_module_not_found(module_name)\n    @explainer.accept_module_not_found(module_name) unless @explainer.nil?\n  end\n\n  def report_module_provider_not_found(module_name)\n    @explainer.accept_module_provider_not_found(module_name) unless @explainer.nil?\n  end\n\n  def report_text(&block)\n    unless @explainer.nil?\n      @explainer.accept_text(block.call)\n    end\n  end\n\n  def global_only?\n    lookup_adapter.global_only? || (instance_variable_defined?(:@global_only) ? @global_only : false)\n  end\n\n  # Instructs the lookup framework to only perform lookups in the global layer\n  # @return [Invocation] self\n  def set_global_only\n    @global_only = true\n    self\n  end\n\n  # @return [Pathname] the full path of the hiera.yaml config file\n  def global_hiera_config_path\n    lookup_adapter.global_hiera_config_path\n  end\n\n  # @return [Boolean] `true` if the invocation stems from the hiera_xxx function family\n  def hiera_xxx_call?\n    instance_variable_defined?(:@hiera_xxx_call)\n  end\n\n  def set_hiera_xxx_call\n    @hiera_xxx_call = true\n  end\n\n  # @return [Boolean] `true` if the invocation stems from the hiera_xxx function family\n  def hiera_v3_merge_behavior?\n    instance_variable_defined?(:@hiera_v3_merge_behavior)\n  end\n\n  def set_hiera_v3_merge_behavior\n    @hiera_v3_merge_behavior = true\n  end\n\n  # Overrides passed from hiera_xxx functions down to V3DataHashFunctionProvider\n  def set_hiera_v3_location_overrides(overrides)\n    @hiera_v3_location_overrides = [overrides].flatten unless overrides.nil?\n  end\n\n  def hiera_v3_location_overrides\n    instance_variable_defined?(:@hiera_v3_location_overrides) ? @hiera_v3_location_overrides : EMPTY_ARRAY\n  end\n\n  protected\n\n  def name_stack\n    @name_stack.clone\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/key_recorder.rb",
    "content": "# frozen_string_literal: true\n\n# This class defines the private API of the Lookup Key Recorder support.\n# @api private\n#\nclass Puppet::Pops::Lookup::KeyRecorder\n  def initialize\n  end\n\n  # rubocop:disable Naming/MemoizedInstanceVariableName\n  def self.singleton\n    @null_recorder ||= new\n  end\n  # rubocop:enable Naming/MemoizedInstanceVariableName\n\n  # Records a key\n  # (This implementation does nothing)\n  #\n  def record(key)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/location_resolver.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire_relative 'interpolation'\n\nmodule Puppet::Pops\nmodule Lookup\n  # Class that keeps track of the original location (as it appears in the declaration, before interpolation),\n  # and the fully resolved location, and whether or not the resolved location exists.\n  #\n  # @api private\n  class ResolvedLocation\n    attr_reader :original_location, :location\n\n    # @param original_location [String] location as found in declaration. May contain interpolation expressions\n    # @param location [Pathname,URI] the expanded location\n    # @param exist [Boolean] `true` if the location is assumed to exist\n    # @api public\n    def initialize(original_location, location, exist)\n      @original_location = original_location\n      @location = location\n      @exist = exist\n    end\n\n    # @return [Boolean] `true` if the location is assumed to exist\n    # @api public\n    def exist?\n      @exist\n    end\n\n    # @return the resolved location as a string\n    def to_s\n      @location.to_s\n    end\n  end\n\n  # Helper methods to resolve interpolated locations\n  #\n  # @api private\n  module LocationResolver\n    include Interpolation\n\n    def expand_globs(datadir, declared_globs, lookup_invocation)\n      declared_globs.map do |declared_glob|\n        glob = datadir + interpolate(declared_glob, lookup_invocation, false)\n        Pathname.glob(glob).reject(&:directory?).map { |path| ResolvedLocation.new(glob.to_s, path, true) }\n      end.flatten\n    end\n\n    # @param datadir [Pathname] The base when creating absolute paths\n    # @param declared_paths [Array<String>] paths as found in declaration. May contain interpolation expressions\n    # @param lookup_invocation [Puppet::Pops::Lookup::Invocation] The current lookup invocation\n    # @param is_default_config [Boolean] `true` if this is the default config and non-existent paths should be excluded\n    # @param extension [String] Required extension such as '.yaml' or '.json'. Use only if paths without extension can be expected\n    # @return [Array<ResolvedLocation>] Array of resolved paths\n    def resolve_paths(datadir, declared_paths, lookup_invocation, is_default_config, extension = nil)\n      result = []\n      declared_paths.each do |declared_path|\n        path = interpolate(declared_path, lookup_invocation, false)\n        path += extension unless extension.nil? || path.end_with?(extension)\n        path = datadir + path\n        path_exists = path.exist?\n        result << ResolvedLocation.new(declared_path, path, path_exists) unless is_default_config && !path_exists\n      end\n      result\n    end\n\n    # @param declared_uris [Array<String>] paths as found in declaration. May contain interpolation expressions\n    # @param lookup_invocation [Puppet::Pops::Lookup::Invocation] The current lookup invocation\n    # @return [Array<ResolvedLocation>] Array of resolved paths\n    def expand_uris(declared_uris, lookup_invocation)\n      declared_uris.map do |declared_uri|\n        uri = URI(interpolate(declared_uri, lookup_invocation, false))\n        ResolvedLocation.new(declared_uri, uri, true)\n      end\n    end\n\n    def expand_mapped_paths(datadir, mapped_path_triplet, lookup_invocation)\n      # The scope interpolation method is used directly to avoid unnecessary parsing of the string that otherwise\n      # would need to be generated\n      mapped_vars = interpolate_method(:scope).call(mapped_path_triplet[0], lookup_invocation, 'mapped_path[0]')\n\n      # No paths here unless the scope lookup returned something\n      return EMPTY_ARRAY if mapped_vars.nil? || mapped_vars.empty?\n\n      mapped_vars = [mapped_vars] if mapped_vars.is_a?(String)\n      var_key = mapped_path_triplet[1]\n      template = mapped_path_triplet[2]\n      scope = lookup_invocation.scope\n      lookup_invocation.with_local_memory_eluding(var_key) do\n        mapped_vars.map do |var|\n          # Need to use parent lookup invocation to avoid adding 'var' to the set of variables to track for changes. The\n          # variable that 'var' stems from is added above.\n          path = scope.with_local_scope(var_key => var) { datadir + interpolate(template, lookup_invocation, false) }\n          ResolvedLocation.new(template, path, path.exist?)\n        end\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/lookup_adapter.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'data_adapter'\nrequire_relative 'lookup_key'\n\nmodule Puppet::Pops\nmodule Lookup\n# A LookupAdapter is a specialized DataAdapter that uses its hash to store data providers. It also remembers the compiler\n# that it is attached to and maintains a cache of _lookup options_ retrieved from the data providers associated with the\n# compiler's environment.\n#\n# @api private\nclass LookupAdapter < DataAdapter\n  LOOKUP_OPTIONS_PREFIX = LOOKUP_OPTIONS + '.'\n  LOOKUP_OPTIONS_PREFIX.freeze\n  LOOKUP_OPTIONS_PATTERN_START = '^'\n\n  HASH = 'hash'\n  MERGE = 'merge'\n  CONVERT_TO = 'convert_to'\n  NEW = 'new'\n\n  def self.create_adapter(compiler)\n    new(compiler)\n  end\n\n  def initialize(compiler)\n    super()\n    @compiler = compiler\n    @lookup_options = {}\n    # Get a KeyRecorder from context, and set a \"null recorder\" if not defined\n    @key_recorder = Puppet.lookup(:lookup_key_recorder) { KeyRecorder.singleton }\n  end\n\n  # Performs a lookup using global, environment, and module data providers. Merge the result using the given\n  # _merge_ strategy. If the merge strategy is nil, then an attempt is made to find merge options in the\n  # `lookup_options` hash for an entry associated with the key. If no options are found, the no merge is performed\n  # and the first found entry is returned.\n  #\n  # @param key [String] The key to lookup\n  # @param lookup_invocation [Invocation] the lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies \"first found\")\n  # @return [Object] the found object\n  # @throw :no_such_key when the object is not found\n  #\n  def lookup(key, lookup_invocation, merge)\n    # The 'lookup_options' key is reserved and not found as normal data\n    if key == LOOKUP_OPTIONS || key.start_with?(LOOKUP_OPTIONS_PREFIX)\n      lookup_invocation.with(:invalid_key, LOOKUP_OPTIONS) do\n        throw :no_such_key\n      end\n    end\n\n    # Record that the key was looked up. This will record all keys for which a lookup is performed\n    # except 'lookup_options' (since that is illegal from a user perspective,\n    # and from an impact perspective is always looked up).\n    @key_recorder.record(key)\n\n    key = LookupKey.new(key)\n    lookup_invocation.lookup(key, key.module_name) do\n      if lookup_invocation.only_explain_options?\n        catch(:no_such_key) { do_lookup(LookupKey::LOOKUP_OPTIONS, lookup_invocation, HASH) }\n        nil\n      else\n        lookup_options = lookup_lookup_options(key, lookup_invocation) || {}\n\n        if merge.nil?\n          # Used cached lookup_options\n          # merge = lookup_merge_options(key, lookup_invocation)\n          merge = lookup_options[MERGE]\n          lookup_invocation.report_merge_source(LOOKUP_OPTIONS) unless merge.nil?\n        end\n        convert_result(key.to_s, lookup_options, lookup_invocation, lambda do\n          lookup_invocation.with(:data, key.to_s) do\n            catch(:no_such_key) { return do_lookup(key, lookup_invocation, merge) }\n            throw :no_such_key if lookup_invocation.global_only?\n            key.dig(lookup_invocation, lookup_default_in_module(key, lookup_invocation))\n          end\n        end)\n      end\n    end\n  end\n\n  # Performs a possible conversion of the result of calling `the_lookup` lambda\n  # The conversion takes place if there is a 'convert_to' key in the lookup_options\n  # If there is no conversion, the result of calling `the_lookup` is returned\n  # otherwise the successfully converted value.\n  # Errors are raised if the convert_to is faulty (bad type string, or if a call to\n  # new(T, <args>) fails.\n  #\n  # @param key [String] The key to lookup\n  # @param lookup_options [Hash] a hash of options\n  # @param lookup_invocation [Invocation] the lookup invocation\n  # @param the_lookup [Lambda] zero arg lambda that performs the lookup of a value\n  # @return [Object] the looked up value, or converted value if there was conversion\n  # @throw :no_such_key when the object is not found (if thrown by `the_lookup`)\n  #\n  def convert_result(key, lookup_options, lookup_invocation, the_lookup)\n    result = the_lookup.call\n    convert_to = lookup_options[CONVERT_TO]\n    return result if convert_to.nil?\n\n    convert_to = convert_to.is_a?(Array) ? convert_to : [convert_to]\n    if convert_to[0].is_a?(String)\n      begin\n        convert_to[0] = Puppet::Pops::Types::TypeParser.singleton.parse(convert_to[0])\n      rescue StandardError => e\n        raise Puppet::DataBinding::LookupError,\n              _(\"Invalid data type in lookup_options for key '%{key}' could not parse '%{source}', error: '%{msg}\") %\n              { key: key, source: convert_to[0], msg: e.message }\n      end\n    end\n    begin\n      result = lookup_invocation.scope.call_function(NEW, [convert_to[0], result, *convert_to[1..]])\n      # TRANSLATORS 'lookup_options', 'convert_to' and args_string variable should not be translated,\n      args_string = Puppet::Pops::Types::StringConverter.singleton.convert(convert_to)\n      lookup_invocation.report_text { _(\"Applying convert_to lookup_option with arguments %{args}\") % { args: args_string } }\n    rescue StandardError => e\n      raise Puppet::DataBinding::LookupError,\n            _(\"The convert_to lookup_option for key '%{key}' raised error: %{msg}\") %\n            { key: key, msg: e.message }\n    end\n    result\n  end\n\n  def lookup_global(key, lookup_invocation, merge_strategy)\n    # hiera_xxx will always use global_provider regardless of data_binding_terminus setting\n    terminus = lookup_invocation.hiera_xxx_call? ? :hiera : Puppet[:data_binding_terminus]\n    case terminus\n    when :hiera, 'hiera'\n      provider = global_provider(lookup_invocation)\n      throw :no_such_key if provider.nil?\n      provider.key_lookup(key, lookup_invocation, merge_strategy)\n    when :none, 'none', '', nil\n      # If global lookup is disabled, immediately report as not found\n      lookup_invocation.report_not_found(key)\n      throw :no_such_key\n    else\n      lookup_invocation.with(:global, terminus) do\n        catch(:no_such_key) do\n          return lookup_invocation.report_found(key, Puppet::DataBinding.indirection.find(key.root_key,\n                                                                                          { :environment => environment, :variables => lookup_invocation.scope, :merge => merge_strategy }))\n        end\n        lookup_invocation.report_not_found(key)\n        throw :no_such_key\n      end\n    end\n  rescue Puppet::DataBinding::LookupError => detail\n    raise detail unless detail.issue_code.nil?\n\n    error = Puppet::Error.new(_(\"Lookup of key '%{key}' failed: %{detail}\") % { key: lookup_invocation.top_key, detail: detail.message })\n    error.set_backtrace(detail.backtrace)\n    raise error\n  end\n\n  def lookup_in_environment(key, lookup_invocation, merge_strategy)\n    provider = env_provider(lookup_invocation)\n    throw :no_such_key if provider.nil?\n    provider.key_lookup(key, lookup_invocation, merge_strategy)\n  end\n\n  def lookup_in_module(key, lookup_invocation, merge_strategy)\n    module_name = lookup_invocation.module_name\n\n    # Do not attempt to do a lookup in a module unless the name is qualified.\n    throw :no_such_key if module_name.nil?\n\n    provider = module_provider(lookup_invocation, module_name)\n    if provider.nil?\n      if environment.module(module_name).nil?\n        lookup_invocation.report_module_not_found(module_name)\n      else\n        lookup_invocation.report_module_provider_not_found(module_name)\n      end\n      throw :no_such_key\n    end\n    provider.key_lookup(key, lookup_invocation, merge_strategy)\n  end\n\n  def lookup_default_in_module(key, lookup_invocation)\n    module_name = lookup_invocation.module_name\n\n    # Do not attempt to do a lookup in a module unless the name is qualified.\n    throw :no_such_key if module_name.nil?\n\n    provider = module_provider(lookup_invocation, module_name)\n    throw :no_such_key if provider.nil? || !provider.config(lookup_invocation).has_default_hierarchy?\n\n    lookup_invocation.with(:scope, \"Searching default_hierarchy of module \\\"#{module_name}\\\"\") do\n      merge_strategy = nil\n      if merge_strategy.nil?\n        @module_default_lookup_options ||= {}\n        options = @module_default_lookup_options.fetch(module_name) do |k|\n          meta_invocation = Invocation.new(lookup_invocation.scope)\n          meta_invocation.lookup(LookupKey::LOOKUP_OPTIONS, k) do\n            opts = nil\n            lookup_invocation.with(:scope, \"Searching for \\\"#{LookupKey::LOOKUP_OPTIONS}\\\"\") do\n              catch(:no_such_key) do\n                opts = compile_patterns(\n                  validate_lookup_options(\n                    provider.key_lookup_in_default(LookupKey::LOOKUP_OPTIONS, meta_invocation, MergeStrategy.strategy(HASH)), k\n                  )\n                )\n              end\n            end\n            @module_default_lookup_options[k] = opts\n          end\n        end\n        lookup_options = extract_lookup_options_for_key(key, options)\n        merge_strategy = lookup_options[MERGE] unless lookup_options.nil?\n      end\n\n      lookup_invocation.with(:scope, \"Searching for \\\"#{key}\\\"\") do\n        provider.key_lookup_in_default(key, lookup_invocation, merge_strategy)\n      end\n    end\n  end\n\n  # Retrieve the merge options that match the given `name`.\n  #\n  # @param key [LookupKey] The key for which we want merge options\n  # @param lookup_invocation [Invocation] the lookup invocation\n  # @return [String,Hash,nil] The found merge options or nil\n  #\n  def lookup_merge_options(key, lookup_invocation)\n    lookup_options = lookup_lookup_options(key, lookup_invocation)\n    lookup_options.nil? ? nil : lookup_options[MERGE]\n  end\n\n  # Retrieve the lookup options that match the given `name`.\n  #\n  # @param key [LookupKey] The key for which we want lookup options\n  # @param lookup_invocation [Puppet::Pops::Lookup::Invocation] the lookup invocation\n  # @return [String,Hash,nil] The found lookup options or nil\n  #\n  def lookup_lookup_options(key, lookup_invocation)\n    module_name = key.module_name\n\n    # Retrieve the options for the module. We use nil as a key in case we have no module\n    if !@lookup_options.include?(module_name)\n      options = retrieve_lookup_options(module_name, lookup_invocation, MergeStrategy.strategy(HASH))\n      @lookup_options[module_name] = options\n    else\n      options = @lookup_options[module_name]\n    end\n    extract_lookup_options_for_key(key, options)\n  end\n\n  def extract_lookup_options_for_key(key, options)\n    return nil if options.nil?\n\n    rk = key.root_key\n    key_opts = options[0]\n    unless key_opts.nil?\n      key_opt = key_opts[rk]\n      return key_opt unless key_opt.nil?\n    end\n\n    patterns = options[1]\n    patterns.each_pair { |pattern, value| return value if pattern =~ rk } unless patterns.nil?\n    nil\n  end\n\n  # @param lookup_invocation [Puppet::Pops::Lookup::Invocation] the lookup invocation\n  # @return [Boolean] `true` if an environment data provider version 5 is configured\n  def has_environment_data_provider?(lookup_invocation)\n    ep = env_provider(lookup_invocation)\n    ep.nil? ? false : ep.config(lookup_invocation).version >= 5\n  end\n\n  # @return [Pathname] the full path of the hiera.yaml config file\n  def global_hiera_config_path\n    @global_hiera_config_path ||= Pathname.new(Puppet.settings[:hiera_config])\n  end\n\n  # @param path [String] the absolute path name of the global hiera.yaml file.\n  # @return [LookupAdapter] self\n  def set_global_hiera_config_path(path)\n    @global_hiera_config_path = Pathname.new(path)\n    self\n  end\n\n  def global_only?\n    instance_variable_defined?(:@global_only) ? @global_only : false\n  end\n\n  # Instructs the lookup framework to only perform lookups in the global layer\n  # @return [LookupAdapter] self\n  def set_global_only\n    @global_only = true\n    self\n  end\n\n  private\n\n  PROVIDER_STACK = [:lookup_global, :lookup_in_environment, :lookup_in_module].freeze\n\n  def validate_lookup_options(options, module_name)\n    return nil if options.nil?\n    raise Puppet::DataBinding::LookupError, _(\"value of %{opts} must be a hash\") % { opts: LOOKUP_OPTIONS } unless options.is_a?(Hash)\n    return options if module_name.nil?\n\n    pfx = \"#{module_name}::\"\n    options.each_pair do |key, _value|\n      if key.start_with?(LOOKUP_OPTIONS_PATTERN_START)\n        unless key[1..pfx.length] == pfx\n          raise Puppet::DataBinding::LookupError, _(\"all %{opts} patterns must match a key starting with module name '%{module_name}'\") % { opts: LOOKUP_OPTIONS, module_name: module_name }\n        end\n      else\n        unless key.start_with?(pfx)\n          raise Puppet::DataBinding::LookupError, _(\"all %{opts} keys must start with module name '%{module_name}'\") % { opts: LOOKUP_OPTIONS, module_name: module_name }\n        end\n      end\n    end\n  end\n\n  def compile_patterns(options)\n    return nil if options.nil?\n\n    key_options = {}\n    pattern_options = {}\n    options.each_pair do |key, value|\n      if key.start_with?(LOOKUP_OPTIONS_PATTERN_START)\n        pattern_options[Regexp.compile(key)] = value\n      else\n        key_options[key] = value\n      end\n    end\n    [key_options.empty? ? nil : key_options, pattern_options.empty? ? nil : pattern_options]\n  end\n\n  def do_lookup(key, lookup_invocation, merge)\n    if lookup_invocation.global_only?\n      key.dig(lookup_invocation, lookup_global(key, lookup_invocation, merge))\n    else\n      merge_strategy = Puppet::Pops::MergeStrategy.strategy(merge)\n      key.dig(lookup_invocation,\n              merge_strategy.lookup(PROVIDER_STACK, lookup_invocation) { |m| send(m, key, lookup_invocation, merge_strategy) })\n    end\n  end\n\n  GLOBAL_ENV_MERGE = 'Global and Environment'\n\n  # Retrieve lookup options that applies when using a specific module (i.e. a merge of the pre-cached\n  # `env_lookup_options` and the module specific data)\n  def retrieve_lookup_options(module_name, lookup_invocation, merge_strategy)\n    meta_invocation = Invocation.new(lookup_invocation.scope)\n    meta_invocation.lookup(LookupKey::LOOKUP_OPTIONS, lookup_invocation.module_name) do\n      meta_invocation.with(:meta, LOOKUP_OPTIONS) do\n        if meta_invocation.global_only?\n          compile_patterns(global_lookup_options(meta_invocation, merge_strategy))\n        else\n          opts = env_lookup_options(meta_invocation, merge_strategy)\n          unless module_name.nil?\n            # Store environment options at key nil. This removes the need for an additional lookup for keys that are not prefixed.\n            @lookup_options[nil] = compile_patterns(opts) unless @lookup_options.include?(nil)\n            catch(:no_such_key) do\n              module_opts = validate_lookup_options(lookup_in_module(LookupKey::LOOKUP_OPTIONS, meta_invocation, merge_strategy), module_name)\n              opts = if opts.nil?\n                       module_opts\n                     elsif module_opts\n                       merge_strategy.lookup([GLOBAL_ENV_MERGE, \"Module #{lookup_invocation.module_name}\"], meta_invocation) do |n|\n                         meta_invocation.with(:scope, n) { meta_invocation.report_found(LOOKUP_OPTIONS, n == GLOBAL_ENV_MERGE ? opts : module_opts) }\n                       end\n                     end\n            end\n          end\n          compile_patterns(opts)\n        end\n      end\n    end\n  end\n\n  # Retrieve and cache the global lookup options\n  def global_lookup_options(lookup_invocation, merge_strategy)\n    unless instance_variable_defined?(:@global_lookup_options)\n      @global_lookup_options = nil\n      catch(:no_such_key) { @global_lookup_options = validate_lookup_options(lookup_global(LookupKey::LOOKUP_OPTIONS, lookup_invocation, merge_strategy), nil) }\n    end\n    @global_lookup_options\n  end\n\n  # Retrieve and cache lookup options specific to the environment of the compiler that this adapter is attached to (i.e. a merge\n  # of global and environment lookup options).\n  def env_lookup_options(lookup_invocation, merge_strategy)\n    unless instance_variable_defined?(:@env_lookup_options)\n      global_options = global_lookup_options(lookup_invocation, merge_strategy)\n      @env_only_lookup_options = nil\n      catch(:no_such_key) { @env_only_lookup_options = validate_lookup_options(lookup_in_environment(LookupKey::LOOKUP_OPTIONS, lookup_invocation, merge_strategy), nil) }\n      if global_options.nil?\n        @env_lookup_options = @env_only_lookup_options\n      elsif @env_only_lookup_options.nil?\n        @env_lookup_options = global_options\n      else\n        @env_lookup_options = merge_strategy.merge(global_options, @env_only_lookup_options)\n      end\n    end\n    @env_lookup_options\n  end\n\n  def global_provider(lookup_invocation)\n    @global_provider = GlobalDataProvider.new unless instance_variable_defined?(:@global_provider)\n    @global_provider\n  end\n\n  def env_provider(lookup_invocation)\n    @env_provider = initialize_env_provider(lookup_invocation) unless instance_variable_defined?(:@env_provider)\n    @env_provider\n  end\n\n  def module_provider(lookup_invocation, module_name)\n    # Test if the key is present for the given module_name. It might be there even if the\n    # value is nil (which indicates that no module provider is configured for the given name)\n    unless include?(module_name)\n      self[module_name] = initialize_module_provider(lookup_invocation, module_name)\n    end\n    self[module_name]\n  end\n\n  def initialize_module_provider(lookup_invocation, module_name)\n    mod = environment.module(module_name)\n    return nil if mod.nil?\n\n    metadata = mod.metadata\n    provider_name = metadata.nil? ? nil : metadata['data_provider']\n\n    mp = nil\n    if mod.has_hiera_conf?\n      mp = ModuleDataProvider.new(module_name)\n      # A version 5 hiera.yaml trumps a data provider setting in the module\n      mp_config = mp.config(lookup_invocation)\n      if mp_config.nil?\n        mp = nil\n      elsif mp_config.version >= 5\n        unless provider_name.nil? || Puppet[:strict] == :off\n          Puppet.warn_once('deprecations', \"metadata.json#data_provider-#{module_name}\",\n                           _(\"Defining \\\"data_provider\\\": \\\"%{name}\\\" in metadata.json is deprecated. It is ignored since a '%{config}' with version >= 5 is present\") % { name: provider_name, config: HieraConfig::CONFIG_FILE_NAME }, mod.metadata_file)\n        end\n        provider_name = nil\n      end\n    end\n\n    if provider_name.nil?\n      mp\n    else\n      unless Puppet[:strict] == :off\n        msg = _(\"Defining \\\"data_provider\\\": \\\"%{name}\\\" in metadata.json is deprecated.\") % { name: provider_name }\n        msg += \" \" + _(\"A '%{hiera_config}' file should be used instead\") % { hiera_config: HieraConfig::CONFIG_FILE_NAME } if mp.nil?\n        Puppet.warn_once('deprecations', \"metadata.json#data_provider-#{module_name}\", msg, mod.metadata_file)\n      end\n\n      case provider_name\n      when 'none'\n        nil\n      when 'hiera'\n        mp || ModuleDataProvider.new(module_name)\n      when 'function'\n        mp = ModuleDataProvider.new(module_name)\n        mp.config = HieraConfig.v4_function_config(Pathname(mod.path), \"#{module_name}::data\", mp)\n        mp\n      else\n        raise Puppet::Error.new(_(\"Environment '%{env}', cannot find module_data_provider '%{provider}'\")) % { env: environment.name, provider: provider_name }\n      end\n    end\n  end\n\n  def initialize_env_provider(lookup_invocation)\n    env_conf = environment.configuration\n    return nil if env_conf.nil? || env_conf.path_to_env.nil?\n\n    # Get the name of the data provider from the environment's configuration\n    provider_name = env_conf.environment_data_provider\n    env_path = Pathname(env_conf.path_to_env)\n    config_path = env_path + HieraConfig::CONFIG_FILE_NAME\n\n    ep = nil\n    if config_path.exist?\n      ep = EnvironmentDataProvider.new\n      # A version 5 hiera.yaml trumps any data provider setting in the environment.conf\n      ep_config = ep.config(lookup_invocation)\n      if ep_config.nil?\n        ep = nil\n      elsif ep_config.version >= 5\n        unless provider_name.nil? || Puppet[:strict] == :off\n          Puppet.warn_once('deprecations', 'environment.conf#data_provider',\n                           _(\"Defining environment_data_provider='%{provider_name}' in environment.conf is deprecated\") % { provider_name: provider_name }, env_path + 'environment.conf')\n\n          unless provider_name == 'hiera'\n            Puppet.warn_once('deprecations', 'environment.conf#data_provider_overridden',\n                             _(\"The environment_data_provider='%{provider_name}' setting is ignored since '%{config_path}' version >= 5\") % { provider_name: provider_name, config_path: config_path }, env_path + 'environment.conf')\n          end\n        end\n        provider_name = nil\n      end\n    end\n\n    if provider_name.nil?\n      ep\n    else\n      unless Puppet[:strict] == :off\n        msg = _(\"Defining environment_data_provider='%{provider_name}' in environment.conf is deprecated.\") % { provider_name: provider_name }\n        msg += \" \" + _(\"A '%{hiera_config}' file should be used instead\") % { hiera_config: HieraConfig::CONFIG_FILE_NAME } if ep.nil?\n        Puppet.warn_once('deprecations', 'environment.conf#data_provider', msg, env_path + 'environment.conf')\n      end\n\n      case provider_name\n      when 'none'\n        nil\n      when 'hiera'\n        # Use hiera.yaml or default settings if it is missing\n        ep || EnvironmentDataProvider.new\n      when 'function'\n        ep = EnvironmentDataProvider.new\n        ep.config = HieraConfigV5.v4_function_config(env_path, 'environment::data', ep)\n        ep\n      else\n        raise Puppet::Error, _(\"Environment '%{env}', cannot find environment_data_provider '%{provider}'\") % { env: environment.name, provider: provider_name }\n      end\n    end\n  end\n\n  # @return [Puppet::Node::Environment] the environment of the compiler that this adapter is associated with\n  def environment\n    @compiler.environment\n  end\nend\nend\nend\n\nrequire_relative 'invocation'\nrequire_relative 'global_data_provider'\nrequire_relative 'environment_data_provider'\nrequire_relative 'module_data_provider'\n"
  },
  {
    "path": "lib/puppet/pops/lookup/lookup_key.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'sub_lookup'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass LookupKey\n  include SubLookup\n\n  attr_reader :module_name, :root_key, :segments\n\n  def initialize(key)\n    segments = split_key(key) { |problem| Puppet::DataBinding::LookupError.new(_(\"%{problem} in key: '%{key}'\") % { problem: problem, key: key }) }\n    root_key = segments.shift.freeze\n    qual_index = root_key.index(DOUBLE_COLON)\n\n    @key = key\n    @module_name = qual_index.nil? ? nil : root_key[0..qual_index - 1].freeze\n    @root_key = root_key\n    @segments = segments.empty? ? nil : segments.freeze\n  end\n\n  def dig(lookup_invocation, value)\n    @segments.nil? ? value : sub_lookup(@key, lookup_invocation, @segments, value)\n  end\n\n  # Prunes a found root value with respect to subkeys in this key. The given _value_ is returned untouched\n  # if this key has no subkeys. Otherwise an attempt is made to create a Hash or Array that contains only the\n  # path to the appointed value and that value.\n  #\n  # If subkeys exists and no value is found, then this method will return `nil`, an empty `Array` or an empty `Hash`\n  # to enable further merges to be applied. The returned type depends on the given _value_.\n  #\n  # @param value [Object] the value to prune\n  # @return the possibly pruned value\n  def prune(value)\n    if @segments.nil?\n      value\n    else\n      pruned = @segments.reduce(value) do |memo, segment|\n        memo.is_a?(Hash) || memo.is_a?(Array) && segment.is_a?(Integer) ? memo[segment] : nil\n      end\n      if pruned.nil?\n        case value\n        when Hash\n          EMPTY_HASH\n        when Array\n          EMPTY_ARRAY\n        else\n          nil\n        end\n      else\n        undig(pruned)\n      end\n    end\n  end\n\n  # Create a structure that can be dug into using the subkeys of this key in order to find the\n  # given _value_. If this key has no subkeys, the _value_ is returned.\n  #\n  # @param value [Object] the value to wrap in a structure in case this value has subkeys\n  # @return [Object] the possibly wrapped value\n  def undig(value)\n    @segments.nil? ? value : segments.reverse.reduce(value) do |memo, segment|\n      if segment.is_a?(Integer)\n        x = []\n        x[segment] = memo\n      else\n        x = { segment => memo }\n      end\n      x\n    end\n  end\n\n  def to_a\n    unless instance_variable_defined?(:@all_segments)\n      a = [@root_key]\n      a += @segments unless @segments.nil?\n      @all_segments = a.freeze\n    end\n    @all_segments\n  end\n\n  def eql?(v)\n    v.is_a?(LookupKey) && @key == v.to_s\n  end\n  alias == eql?\n\n  def hash\n    @key.hash\n  end\n\n  def to_s\n    @key\n  end\n\n  LOOKUP_OPTIONS = LookupKey.new('lookup_options')\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/lookup_key_function_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'function_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass LookupKeyFunctionProvider < FunctionProvider\n  TAG = 'lookup_key'\n\n  # Performs a lookup with the assumption that a recursive check has been made.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies \"first found\")\n  # @return [Object] the found object\n  # @throw :no_such_key when the object is not found\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    root_key = key.root_key\n    lookup_invocation.with(:data_provider, self) do\n      MergeStrategy.strategy(merge).lookup(locations, lookup_invocation) do |location|\n        invoke_with_location(lookup_invocation, location, root_key, merge)\n      end\n    end\n  end\n\n  def invoke_with_location(lookup_invocation, location, root_key, merge)\n    if location.nil?\n      value = lookup_key(root_key, lookup_invocation, nil, merge)\n      lookup_invocation.report_found(root_key, value)\n    else\n      lookup_invocation.with(:location, location) do\n        value = lookup_key(root_key, lookup_invocation, location, merge)\n        lookup_invocation.report_found(root_key, value)\n      end\n    end\n  end\n\n  def label\n    'Lookup Key'\n  end\n\n  private\n\n  def lookup_key(key, lookup_invocation, location, merge)\n    unless location.nil? || location.exist?\n      lookup_invocation.report_location_not_found\n      throw :no_such_key\n    end\n    ctx = function_context(lookup_invocation, location)\n    ctx.data_hash ||= {}\n    catch(:no_such_key) do\n      hash = ctx.data_hash\n      unless hash.include?(key)\n        hash[key] = validate_data_value(ctx.function.call(lookup_invocation.scope, key, options(location), Context.new(ctx, lookup_invocation))) do\n          msg = \"Value for key '#{key}', returned from #{full_name}\"\n          location.nil? ? msg : \"#{msg}, when using location '#{location}',\"\n        end\n      end\n      return hash[key]\n    end\n    lookup_invocation.report_not_found(key)\n    throw :no_such_key\n  end\nend\n\n# @api private\nclass V3LookupKeyFunctionProvider < LookupKeyFunctionProvider\n  TAG = 'v3_lookup_key'\n\n  def initialize(name, parent_data_provider, function_name, options, locations)\n    @datadir = options.delete(HieraConfig::KEY_DATADIR)\n    super\n  end\n\n  def unchecked_key_lookup(key, lookup_invocation, merge)\n    extra_paths = lookup_invocation.hiera_v3_location_overrides\n    if extra_paths.nil? || extra_paths.empty?\n      super\n    else\n      # Extra paths provided. Must be resolved and placed in front of known paths\n      paths = parent_data_provider.config(lookup_invocation).resolve_paths(@datadir, extra_paths, lookup_invocation, false, \".#{@name}\")\n      all_locations = paths + locations\n      root_key = key.root_key\n      lookup_invocation.with(:data_provider, self) do\n        MergeStrategy.strategy(merge).lookup(all_locations, lookup_invocation) do |location|\n          invoke_with_location(lookup_invocation, location, root_key, merge)\n        end\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/module_data_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'configured_data_provider'\n\nmodule Puppet::Pops\nmodule Lookup\n# @api private\nclass ModuleDataProvider < ConfiguredDataProvider\n  attr_reader :module_name\n\n  def initialize(module_name, config = nil)\n    super(config)\n    @module_name = module_name\n  end\n\n  def place\n    'Module'\n  end\n\n  # Performs a lookup using a module default hierarchy with an endless recursion check.\n  #\n  # @param key [LookupKey] The key to lookup\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @param merge [MergeStrategy,String,Hash{String=>Object},nil] Merge strategy or hash with strategy and options\n  #\n  def key_lookup_in_default(key, lookup_invocation, merge)\n    dps = config(lookup_invocation).configured_data_providers(lookup_invocation, self, true)\n    if dps.empty?\n      lookup_invocation.report_not_found(key)\n      throw :no_such_key\n    end\n    merge_strategy = MergeStrategy.strategy(merge)\n    lookup_invocation.check(key.to_s) do\n      lookup_invocation.with(:data_provider, self) do\n        merge_strategy.lookup(dps, lookup_invocation) do |data_provider|\n          data_provider.unchecked_key_lookup(key, lookup_invocation, merge_strategy)\n        end\n      end\n    end\n  end\n\n  # Asserts that all keys in the given _data_hash_ are prefixed with the configured _module_name_. Removes entries\n  # that does not follow the convention and logs a warning.\n  #\n  # @param data_hash [Hash] The data hash\n  # @return [Hash] The possibly pruned hash\n  def validate_data_hash(data_hash)\n    super\n    module_prefix = \"#{module_name}::\"\n    data_hash_to_return = {}\n    data_hash.keys.each do |k|\n      if k == LOOKUP_OPTIONS || k.start_with?(module_prefix)\n        data_hash_to_return[k] = data_hash[k]\n      else\n        msg = \"#{yield} must use keys qualified with the name of the module\"\n        Puppet.warning(\"Module '#{module_name}': #{msg}; got #{k}\")\n      end\n    end\n    data_hash_to_return\n  end\n\n  protected\n\n  def assert_config_version(config)\n    if config.version > 3\n      config\n    else\n      if Puppet[:strict] == :error\n        config.fail(Issues::HIERA_VERSION_3_NOT_GLOBAL, :where => 'module')\n      else\n        Puppet.warn_once(:hiera_v3_at_module_root, config.config_path, _('hiera.yaml version 3 found at module root was ignored'), config.config_path)\n      end\n      nil\n    end\n  end\n\n  # Return the root of the module with the name equal to the configured module name\n  #\n  # @param lookup_invocation [Invocation] The current lookup invocation\n  # @return [Pathname] Path to root of the module\n  # @raise [Puppet::DataBinding::LookupError] if the module can not be found\n  #\n  def provider_root(lookup_invocation)\n    env = lookup_invocation.scope.environment\n    mod = env.module(module_name)\n    raise Puppet::DataBinding::LookupError, _(\"Environment '%{env}', cannot find module '%{module_name}'\") % { env: env.name, module_name: module_name } unless mod\n\n    Pathname.new(mod.path)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup/sub_lookup.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Lookup\nmodule SubLookup\n  SPECIAL = /['\".]/\n\n  # Split key into segments. A segment may be a quoted string (both single and double quotes can\n  # be used) and the segment separator is the '.' character. Whitespace will be trimmed off on\n  # both sides of each segment. Whitespace within quotes are not trimmed.\n  #\n  # If the key cannot be parsed, this method will yield a string describing the problem to a one\n  # parameter block. The block must return an exception instance.\n  #\n  # @param key [String] the string to split\n  # @return [Array<String>] the array of segments\n  # @yieldparam problem [String] the problem, i.e. 'Syntax error'\n  # @yieldreturn [Exception] the exception to raise\n  #\n  # @api public\n  def split_key(key)\n    return [key] if key.match(SPECIAL).nil?\n\n    segments = key.split(/(\\s*\"[^\"]+\"\\s*|\\s*'[^']+'\\s*|[^'\".]+)/)\n    if segments.empty?\n      # Only happens if the original key was an empty string\n      raise yield('Syntax error')\n    elsif segments.shift == ''\n      count = segments.size\n      raise yield('Syntax error') unless count > 0\n\n      segments.keep_if { |seg| seg != '.' }\n      raise yield('Syntax error') unless segments.size * 2 == count + 1\n\n      segments.map! do |segment|\n        segment.strip!\n        if segment.start_with?('\"', \"'\")\n          segment[1..-2]\n        elsif segment =~ /^(:?[+-]?[0-9]+)$/\n          segment.to_i\n        else\n          segment\n        end\n      end\n    else\n      raise yield('Syntax error')\n    end\n  end\n\n  # Perform a sub-lookup using the given _segments_ to access the given _value_. Each segment must be a string. A string\n  # consisting entirely of digits will be treated as an indexed lookup which means that the value that it is applied to\n  # must be an array. Other types of segments will expect that the given value is something other than a String that\n  # implements the '#[]' method.\n  #\n  # @param key [String] the original key (only used for error messages)\n  # @param context [Context] The current lookup context\n  # @param segments [Array<String>] the segments to use for lookup\n  # @param value [Object] the value to access using the segments\n  # @return [Object] the value obtained when accessing the value\n  #\n  # @api public\n  def sub_lookup(key, context, segments, value)\n    lookup_invocation = context.is_a?(Invocation) ? context : context.invocation\n    lookup_invocation.with(:sub_lookup, segments) do\n      segments.each do |segment|\n        lookup_invocation.with(:segment, segment) do\n          if value.nil?\n            lookup_invocation.report_not_found(segment)\n            throw :no_such_key\n          end\n          if segment.is_a?(Integer) && value.instance_of?(Array)\n            unless segment >= 0 && segment < value.size\n              lookup_invocation.report_not_found(segment)\n              throw :no_such_key\n            end\n          else\n            unless value.respond_to?(:'[]') && !(value.is_a?(Array) || value.instance_of?(String))\n              raise Puppet::DataBinding::LookupError,\n                    _(\"Data Provider type mismatch: Got %{klass} when a hash-like object was expected to access value using '%{segment}' from key '%{key}'\") %\n                    { klass: value.class.name, segment: segment, key: key }\n            end\n            unless value.include?(segment)\n              lookup_invocation.report_not_found(segment)\n              throw :no_such_key\n            end\n          end\n          value = value[segment]\n          lookup_invocation.report_found(segment, value)\n        end\n      end\n      value\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/lookup.rb",
    "content": "# frozen_string_literal: true\n\n# This class is the backing implementation of the Puppet function 'lookup'.\n# See puppet/functions/lookup.rb for documentation.\n#\nmodule Puppet::Pops\nmodule Lookup\n  LOOKUP_OPTIONS = 'lookup_options'\n  GLOBAL = '__global__'\n\n  # Performs a lookup in the configured scopes and optionally merges the default.\n  #\n  # This is a backing function and all parameters are assumed to have been type checked.\n  # See puppet/functions/lookup.rb for full documentation and all parameter combinations.\n  #\n  # @param name [String|Array<String>] The name or names to lookup\n  # @param type [Types::PAnyType|nil] The expected type of the found value\n  # @param default_value [Object] The value to use as default when no value is found\n  # @param has_default [Boolean] Set to _true_ if _default_value_ is included (_nil_ is a valid _default_value_)\n  # @param merge [MergeStrategy,String,Hash<String,Object>,nil] Merge strategy or hash with strategy and options\n  # @param lookup_invocation [Invocation] Invocation data containing scope, overrides, and defaults\n  # @return [Object] The found value\n  #\n  def self.lookup(name, value_type, default_value, has_default, merge, lookup_invocation)\n    names = name.is_a?(Array) ? name : [name]\n\n    # find first name that yields a non-nil result and wrap it in a two element array\n    # with name and value.\n    not_found = MergeStrategy::NOT_FOUND\n    override_values = lookup_invocation.override_values\n    result_with_name = [nil, not_found]\n    names.each do |key|\n      value = override_values.include?(key) ? assert_type([\"Value found for key '%s' in override hash\", key], value_type, override_values[key]) : not_found\n      catch(:no_such_key) { value = search_and_merge(key, lookup_invocation, merge, false) } if value.equal?(not_found)\n      next if value.equal?(not_found)\n\n      result_with_name = [key, assert_type('Found value', value_type, value)]\n      break\n    end\n\n    # Use the 'default_values' hash as a last resort if nothing is found\n    if result_with_name[1].equal?(not_found)\n      default_values = lookup_invocation.default_values\n      unless default_values.empty?\n        names.each do |key|\n          value = default_values.include?(key) ? assert_type([\"Value found for key '%s' in default values hash\", key], value_type, default_values[key]) : not_found\n          next if value.equal?(not_found)\n\n          result_with_name = [key, value]\n          break\n        end\n      end\n    end\n\n    answer = result_with_name[1]\n    if answer.equal?(not_found)\n      if block_given?\n        answer = assert_type('Value returned from default block', value_type, yield(name))\n      elsif has_default\n        answer = assert_type('Default value', value_type, default_value)\n      else\n        lookup_invocation.emit_debug_info(debug_preamble(names)) if Puppet[:debug]\n        fail_lookup(names)\n      end\n    end\n    lookup_invocation.emit_debug_info(debug_preamble(names)) if Puppet[:debug]\n    answer\n  end\n\n  # @api private\n  def self.debug_preamble(names)\n    if names.size == 1\n      names = \"'#{names[0]}'\"\n    else\n      names = names.map { |n| \"'#{n}'\" }.join(', ')\n    end\n    \"Lookup of #{names}\"\n  end\n\n  # @api private\n  def self.search_and_merge(name, lookup_invocation, merge, apl = true)\n    answer = lookup_invocation.lookup_adapter.lookup(name, lookup_invocation, merge)\n    lookup_invocation.emit_debug_info(\"Automatic Parameter Lookup of '#{name}'\") if apl && Puppet[:debug]\n    answer\n  end\n\n  def self.assert_type(subject, type, value)\n    type ? Types::TypeAsserter.assert_instance_of(subject, type, value) : value\n  end\n  private_class_method :assert_type\n\n  def self.fail_lookup(names)\n    raise Puppet::DataBinding::LookupError,\n          n_(\"Function lookup() did not find a value for the name '%{name}'\",\n             \"Function lookup() did not find a value for any of the names [%{name_list}]\", names.size) % { name: names[0], name_list: names.map { |n| \"'#{n}'\" }.join(', ') }\n  end\n  private_class_method :fail_lookup\nend\nend\n\nrequire_relative 'lookup/lookup_adapter'\nrequire_relative 'lookup/key_recorder'\n"
  },
  {
    "path": "lib/puppet/pops/merge_strategy.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'deep_merge/core'\n\nmodule Puppet::Pops\n  # Merges to objects into one based on an implemented strategy.\n  #\n  class MergeStrategy\n    NOT_FOUND = Object.new.freeze\n\n    def self.strategies\n      @@strategies ||= {}\n    end\n    private_class_method :strategies\n\n    # Finds the merge strategy for the given _merge_, creates an instance of it and returns that instance.\n    #\n    # @param merge [MergeStrategy,String,Hash<String,Object>,nil] The merge strategy. Can be a string or symbol denoting the key\n    #   identifier or a hash with options where the key 'strategy' denotes the key\n    # @return [MergeStrategy] The matching merge strategy\n    #\n    def self.strategy(merge)\n      return DefaultMergeStrategy::INSTANCE unless merge\n      return merge if merge.is_a?(MergeStrategy)\n\n      if merge.is_a?(Hash)\n        merge_strategy = merge['strategy']\n        if merge_strategy.nil?\n          # TRANSLATORS 'merge' is a variable name and 'strategy' is a key and should not be translated\n          raise ArgumentError, _(\"The hash given as 'merge' must contain the name of a strategy in string form for the key 'strategy'\")\n        end\n\n        merge_options  = merge.size == 1 ? EMPTY_HASH : merge\n      else\n        merge_strategy = merge\n        merge_options = EMPTY_HASH\n      end\n      merge_strategy = merge_strategy.to_sym if merge_strategy.is_a?(String)\n      strategy_class = strategies[merge_strategy]\n      raise ArgumentError, _(\"Unknown merge strategy: '%{strategy}'\") % { strategy: merge_strategy } if strategy_class.nil?\n\n      merge_options == EMPTY_HASH ? strategy_class::INSTANCE : strategy_class.new(merge_options)\n    end\n\n    # Returns the list of merge strategy keys known to this class\n    #\n    # @return [Array<Symbol>] List of strategy keys\n    #\n    def self.strategy_keys\n      strategies.keys - [:default, :unconstrained_deep, :reverse_deep]\n    end\n\n    # Adds a new merge strategy to the map of strategies known to this class\n    #\n    # @param strategy_class [Class<MergeStrategy>] The class of the added strategy\n    #\n    def self.add_strategy(strategy_class)\n      unless MergeStrategy > strategy_class\n        # TRANSLATORS 'MergeStrategies.add_strategy' is a method, 'stratgey_class' is a variable and 'MergeStrategy' is a class name and should not be translated\n        raise ArgumentError, _(\"MergeStrategies.add_strategy 'strategy_class' must be a 'MergeStrategy' class. Got %{strategy_class}\") %\n                             { strategy_class: strategy_class }\n      end\n      strategies[strategy_class.key] = strategy_class\n      nil\n    end\n\n    # Finds a merge strategy that corresponds to the given _merge_ argument and delegates the task of merging the elements of _e1_ and _e2_ to it.\n    #\n    # @param e1 [Object] The first element\n    # @param e2 [Object] The second element\n    # @return [Object] The result of the merge\n    #\n    def self.merge(e1, e2, merge)\n      strategy(merge).merge(e1, e2)\n    end\n\n    def self.key\n      raise NotImplementedError, \"Subclass must implement 'key'\"\n    end\n\n    # Create a new instance of this strategy configured with the given _options_\n    # @param merge_options [Hash<String,Object>] Merge options\n    def initialize(options)\n      assert_type('The merge options', self.class.options_t, options) unless options.empty?\n      @options = options\n    end\n\n    # Merges the elements of _e1_ and _e2_ according to the rules of this strategy and options given when this\n    # instance was created\n    #\n    # @param e1 [Object] The first element\n    # @param e2 [Object] The second element\n    # @return [Object] The result of the merge\n    #\n    def merge(e1, e2)\n      checked_merge(\n        assert_type('The first element of the merge', value_t, e1),\n        assert_type('The second element of the merge', value_t, e2)\n      )\n    end\n\n    # TODO: API 5.0 Remove this method\n    # @deprecated\n    def merge_lookup(lookup_variants)\n      lookup(lookup_variants, Lookup::Invocation.current)\n    end\n\n    # Merges the result of yielding the given _lookup_variants_ to a given block.\n    #\n    # @param lookup_variants [Array] The variants to pass as second argument to the given block\n    # @return [Object] the merged value.\n    # @yield [} ]\n    # @yieldparam variant [Object] each variant given in the _lookup_variants_ array.\n    # @yieldreturn [Object] the value to merge with other values\n    # @throws :no_such_key if the lookup was unsuccessful\n    #\n    # Merges the result of yielding the given _lookup_variants_ to a given block.\n    #\n    # @param lookup_variants [Array] The variants to pass as second argument to the given block\n    # @return [Object] the merged value.\n    # @yield [} ]\n    # @yieldparam variant [Object] each variant given in the _lookup_variants_ array.\n    # @yieldreturn [Object] the value to merge with other values\n    # @throws :no_such_key if the lookup was unsuccessful\n    #\n    def lookup(lookup_variants, lookup_invocation)\n      case lookup_variants.size\n      when 0\n        throw :no_such_key\n      when 1\n        merge_single(yield(lookup_variants[0]))\n      else\n        lookup_invocation.with(:merge, self) do\n          result = lookup_variants.reduce(NOT_FOUND) do |memo, lookup_variant|\n            not_found = true\n            value = catch(:no_such_key) do\n              v = yield(lookup_variant)\n              not_found = false\n              v\n            end\n            if not_found\n              memo\n            else\n              memo.equal?(NOT_FOUND) ? convert_value(value) : merge(memo, value)\n            end\n          end\n          throw :no_such_key if result == NOT_FOUND\n          lookup_invocation.report_result(result)\n        end\n      end\n    end\n\n    # Converts a single value to the type expected when merging two elements\n    # @param value [Object] the value to convert\n    # @return [Object] the converted value\n    def convert_value(value)\n      value\n    end\n\n    # Applies the merge strategy on a single element. Only applicable for `unique`\n    # @param value [Object] the value to merge with nothing\n    # @return [Object] the merged value\n    def merge_single(value)\n      value\n    end\n\n    def options\n      @options\n    end\n\n    def configuration\n      if @options.nil? || @options.empty?\n        self.class.key.to_s\n      else\n        @options.include?('strategy') ? @options : { 'strategy' => self.class.key.to_s }.merge(@options)\n      end\n    end\n\n    protected\n\n    class << self\n      # Returns the type used to validate the options hash\n      #\n      # @return [Types::PStructType] the puppet type\n      #\n      def options_t\n        @options_t ||= Types::TypeParser.singleton.parse(\"Struct[{strategy=>Optional[Pattern[/#{key}/]]}]\")\n      end\n    end\n\n    # Returns the type used to validate the options hash\n    #\n    # @return [Types::PAnyType] the puppet type\n    #\n    def value_t\n      raise NotImplementedError, \"Subclass must implement 'value_t'\"\n    end\n\n    def checked_merge(e1, e2)\n      raise NotImplementedError, \"Subclass must implement 'checked_merge(e1,e2)'\"\n    end\n\n    def assert_type(param, type, value)\n      Types::TypeAsserter.assert_instance_of(param, type, value)\n    end\n  end\n\n  # Simple strategy that returns the first value found. It never merges any values.\n  #\n  class FirstFoundStrategy < MergeStrategy\n    INSTANCE = new(EMPTY_HASH)\n\n    def self.key\n      :first\n    end\n\n    # Returns the first value found\n    #\n    # @param lookup_variants [Array] The variants to pass as second argument to the given block\n    # @return [Object] the merged value\n    # @throws :no_such_key unless the lookup was successful\n    #\n    def lookup(lookup_variants, _)\n      # First found does not continue when a root key was found and a subkey wasn't since that would\n      # simulate a hash merge\n      lookup_variants.each { |lookup_variant| catch(:no_such_key) { return yield(lookup_variant) } }\n      throw :no_such_key\n    end\n\n    protected\n\n    def value_t\n      @value_t ||= Types::PAnyType::DEFAULT\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\n\n  # Same as {FirstFoundStrategy} but used when no strategy has been explicitly given\n  class DefaultMergeStrategy < FirstFoundStrategy\n    INSTANCE = new(EMPTY_HASH)\n\n    def self.key\n      :default\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\n\n  # Produces a new hash by merging hash e1 with hash e2 in such a way that the values of duplicate keys\n  # will be those of e1\n  #\n  class HashMergeStrategy < MergeStrategy\n    INSTANCE = new(EMPTY_HASH)\n\n    def self.key\n      :hash\n    end\n\n    # @param e1 [Hash<String,Object>] The hash that will act as the source of the merge\n    # @param e2 [Hash<String,Object>] The hash that will act as the receiver for the merge\n    # @return [Hash<String,Object]] The merged hash\n    # @see Hash#merge\n    def checked_merge(e1, e2)\n      e2.merge(e1)\n    end\n\n    protected\n\n    def value_t\n      @value_t ||= Types::TypeParser.singleton.parse('Hash[String,Data]')\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\n\n  # Merges two values that must be either scalar or arrays into a unique set of values.\n  #\n  # Scalar values will be converted into a one element arrays and array values will be flattened\n  # prior to forming the unique set. The order of the elements is preserved with e1 being the\n  # first contributor of elements and e2 the second.\n  #\n  class UniqueMergeStrategy < MergeStrategy\n    INSTANCE = new(EMPTY_HASH)\n\n    def self.key\n      :unique\n    end\n\n    # @param e1 [Array<Object>] The first array\n    # @param e2 [Array<Object>] The second array\n    # @return [Array<Object>] The unique set of elements\n    #\n    def checked_merge(e1, e2)\n      convert_value(e1) | convert_value(e2)\n    end\n\n    def convert_value(e)\n      e.is_a?(Array) ? e.flatten : [e]\n    end\n\n    # If _value_ is an array, then return the result of calling `uniq` on that array. Otherwise,\n    # the argument is returned.\n    # @param value [Object] the value to merge with nothing\n    # @return [Object] the merged value\n    def merge_single(value)\n      value.is_a?(Array) ? value.uniq : value\n    end\n\n    protected\n\n    def value_t\n      @value_t ||= Types::TypeParser.singleton.parse('Variant[Scalar,Array[Data]]')\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\n\n  # Documentation copied from https://github.com/danielsdeleo/deep_merge/blob/master/lib/deep_merge/core.rb\n  # altered with respect to _preserve_unmergeables_ since this implementation always disables that option.\n  #\n  # The destination is dup'ed before the deep_merge is called to allow frozen objects as values.\n  #\n  # deep_merge method permits merging of arbitrary child elements. The two top level\n  # elements must be hashes. These hashes can contain unlimited (to stack limit) levels\n  # of child elements. These child elements to not have to be of the same types.\n  # Where child elements are of the same type, deep_merge will attempt to merge them together.\n  # Where child elements are not of the same type, deep_merge will skip or optionally overwrite\n  # the destination element with the contents of the source element at that level.\n  # So if you have two hashes like this:\n  #   source = {:x => [1,2,3], :y => 2}\n  #   dest =   {:x => [4,5,'6'], :y => [7,8,9]}\n  #   dest.deep_merge!(source)\n  #   Results: {:x => [1,2,3,4,5,'6'], :y => 2}\n  #\n  # \"deep_merge\" will unconditionally overwrite any unmergeables and merge everything else.\n  #\n  # Options:\n  #   Options are specified in the last parameter passed, which should be in hash format:\n  #   hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})\n  #   - 'knockout_prefix' Set to string value to signify prefix which deletes elements from existing element. Defaults is _undef_\n  #   - 'sort_merged_arrays' Set to _true_ to sort all arrays that are merged together. Default is _false_\n  #   - 'merge_hash_arrays' Set to _true_ to merge hashes within arrays. Default is _false_\n  #\n  # Selected Options Details:\n  # :knockout_prefix => The purpose of this is to provide a way to remove elements\n  #   from existing Hash by specifying them in a special way in incoming hash\n  #    source = {:x => ['--1', '2']}\n  #    dest   = {:x => ['1', '3']}\n  #    dest.ko_deep_merge!(source)\n  #    Results: {:x => ['2','3']}\n  #   Additionally, if the knockout_prefix is passed alone as a string, it will cause\n  #   the entire element to be removed:\n  #    source = {:x => '--'}\n  #    dest   = {:x => [1,2,3]}\n  #    dest.ko_deep_merge!(source)\n  #    Results: {:x => \"\"}\n  #\n  # :merge_hash_arrays => merge hashes within arrays\n  #   source = {:x => [{:y => 1}]}\n  #   dest   = {:x => [{:z => 2}]}\n  #   dest.deep_merge!(source, {:merge_hash_arrays => true})\n  #   Results: {:x => [{:y => 1, :z => 2}]}\n  #\n  class DeepMergeStrategy < MergeStrategy\n    INSTANCE = new(EMPTY_HASH)\n\n    def self.key\n      :deep\n    end\n\n    def checked_merge(e1, e2)\n      dm_options = { :preserve_unmergeables => false }\n      options.each_pair { |k, v| dm_options[k.to_sym] = v unless k == 'strategy' }\n      # e2 (the destination) is deep cloned to avoid that the passed in object mutates\n      DeepMerge.deep_merge!(e1, deep_clone(e2), dm_options)\n    end\n\n    def deep_clone(value)\n      case value\n      when Hash\n        result = value.clone\n        value.each { |k, v| result[k] = deep_clone(v) }\n        result\n      when Array\n        value.map { |v| deep_clone(v) }\n      else\n        value\n      end\n    end\n\n    protected\n\n    class << self\n      # Returns a type that allows all deep_merge options except 'preserve_unmergeables' since we force\n      # the setting of that option to false\n      #\n      # @return [Types::PAnyType] the puppet type used when validating the options hash\n      def options_t\n        @options_t ||= Types::TypeParser.singleton.parse('Struct[{'\\\n                                                         \"strategy=>Optional[Pattern[#{key}]],\"\\\n                                                         'knockout_prefix=>Optional[String],'\\\n                                                         'merge_debug=>Optional[Boolean],'\\\n                                                         'merge_hash_arrays=>Optional[Boolean],'\\\n                                                         'sort_merged_arrays=>Optional[Boolean],'\\\n                                                         '}]')\n      end\n    end\n\n    def value_t\n      @value_t ||= Types::PAnyType::DEFAULT\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\n\n  # Same as {DeepMergeStrategy} but without constraint on valid merge options\n  # (needed for backward compatibility with Hiera v3)\n  class UnconstrainedDeepMergeStrategy < DeepMergeStrategy\n    def self.key\n      :unconstrained_deep\n    end\n\n    # @return [Types::PAnyType] the puppet type used when validating the options hash\n    def self.options_t\n      @options_t ||= Types::TypeParser.singleton.parse('Hash[String[1],Any]')\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\n\n  # Same as {UnconstrainedDeepMergeStrategy} but with reverse priority of merged elements.\n  # (needed for backward compatibility with Hiera v3)\n  class ReverseDeepMergeStrategy < UnconstrainedDeepMergeStrategy\n    INSTANCE = new(EMPTY_HASH)\n\n    def self.key\n      :reverse_deep\n    end\n\n    def checked_merge(e1, e2)\n      super(e2, e1)\n    end\n\n    MergeStrategy.add_strategy(self)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/migration/migration_checker.rb",
    "content": "# frozen_string_literal: true\n\n# This class defines the private API of the MigrationChecker support.\n# @api private\n#\nclass Puppet::Pops::Migration::MigrationChecker\n  def initialize\n  end\n\n  # rubocop:disable Naming/MemoizedInstanceVariableName\n  def self.singleton\n    @null_checker ||= new\n  end\n  # rubocop:enable Naming/MemoizedInstanceVariableName\n\n  # Produces a hash of available migrations; a map from a symbolic name in string form to a brief description.\n  # This version has no such supported migrations.\n  def available_migrations\n    {}\n  end\n\n  # For 3.8/4.0\n  def report_ambiguous_integer(o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_ambiguous_float(o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_empty_string_true(value, o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_uc_bareword_type(value, o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_equality_type_mismatch(left, right, o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_option_type_mismatch(test_value, option_value, option_expr, matching_expr)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_in_expression(o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\n\n  # For 3.8/4.0\n  def report_array_last_in_block(o)\n    raise Puppet::DevError, _(\"Unsupported migration method called\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/ast.pp",
    "content": "type Puppet::AST = TypeSet[{\n  pcore_version => '1.0.0',\n  types => {\n    Locator => Object[{\n      attributes => {\n        'string' => String,\n        'file' => String,\n        'line_index' => {\n          type => Optional[Array[Integer]],\n          value => undef\n        }\n      }\n    }],\n    PopsObject => Object[{\n    }],\n    Positioned => Object[{\n      parent => PopsObject,\n      attributes => {\n        'locator' => {\n          type => Locator,\n          kind => reference\n        },\n        'offset' => Integer,\n        'length' => Integer,\n        'file' => {\n          type => String,\n          kind => derived,\n          annotations => {\n             RubyMethod => { 'body' => '@locator.file' }\n          }\n        },\n        'line' => {\n          type => Integer,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => '@locator.line_for_offset(@offset)' }\n          }\n        },\n        'pos' => {\n          type => Integer,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => '@locator.pos_on_line(@offset)' }\n          }\n        }\n      },\n      equality => []\n    }],\n    Expression => Object[{\n      parent => Positioned\n    }],\n    Nop => Object[{\n      parent => Expression\n    }],\n    BinaryExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'left_expr' => Expression,\n        'right_expr' => Expression\n      }\n    }],\n    UnaryExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'expr' => Expression\n      }\n    }],\n    ParenthesizedExpression => Object[{\n      parent => UnaryExpression\n    }],\n    NotExpression => Object[{\n      parent => UnaryExpression\n    }],\n    UnaryMinusExpression => Object[{\n      parent => UnaryExpression\n    }],\n    UnfoldExpression => Object[{\n      parent => UnaryExpression\n    }],\n    AssignmentExpression => Object[{\n      parent => BinaryExpression,\n      attributes => {\n        'operator' => Enum['+=', '-=', '=']\n      }\n    }],\n    ArithmeticExpression => Object[{\n      parent => BinaryExpression,\n      attributes => {\n        'operator' => Enum['%', '*', '+', '-', '/', '<<', '>>']\n      }\n    }],\n    RelationshipExpression => Object[{\n      parent => BinaryExpression,\n      attributes => {\n        'operator' => Enum['->', '<-', '<~', '~>']\n      }\n    }],\n    AccessExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'left_expr' => Expression,\n        'keys' => {\n          type => Array[Expression],\n          value => []\n        }\n      }\n    }],\n    ComparisonExpression => Object[{\n      parent => BinaryExpression,\n      attributes => {\n        'operator' => Enum['!=', '<', '<=', '==', '>', '>=']\n      }\n    }],\n    MatchExpression => Object[{\n      parent => BinaryExpression,\n      attributes => {\n        'operator' => Enum['!~', '=~']\n      }\n    }],\n    InExpression => Object[{\n      parent => BinaryExpression\n    }],\n    BooleanExpression => Object[{\n      parent => BinaryExpression\n    }],\n    AndExpression => Object[{\n      parent => BooleanExpression\n    }],\n    OrExpression => Object[{\n      parent => BooleanExpression\n    }],\n    LiteralList => Object[{\n      parent => Expression,\n      attributes => {\n        'values' => {\n          type => Array[Expression],\n          value => []\n        }\n      }\n    }],\n    KeyedEntry => Object[{\n      parent => Positioned,\n      attributes => {\n        'key' => Expression,\n        'value' => Expression\n      }\n    }],\n    LiteralHash => Object[{\n      parent => Expression,\n      attributes => {\n        'entries' => {\n          type => Array[KeyedEntry],\n          value => []\n        }\n      }\n    }],\n    BlockExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'statements' => {\n          type => Array[Expression],\n          value => []\n        }\n      }\n    }],\n    ApplyBlockExpression => Object[{\n      parent => BlockExpression,\n    }],\n    CaseOption => Object[{\n      parent => Expression,\n      attributes => {\n        'values' => Array[Expression, 1, default],\n        'then_expr' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    CaseExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'test' => Expression,\n        'options' => {\n          type => Array[CaseOption],\n          value => []\n        }\n      }\n    }],\n    QueryExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'expr' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    ExportedQuery => Object[{\n      parent => QueryExpression\n    }],\n    VirtualQuery => Object[{\n      parent => QueryExpression\n    }],\n    AbstractAttributeOperation => Object[{\n      parent => Positioned\n    }],\n    AttributeOperation => Object[{\n      parent => AbstractAttributeOperation,\n      attributes => {\n        'attribute_name' => String,\n        'operator' => Enum['+>', '=>'],\n        'value_expr' => Expression\n      }\n    }],\n    AttributesOperation => Object[{\n      parent => AbstractAttributeOperation,\n      attributes => {\n        'expr' => Expression\n      }\n    }],\n    CollectExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'type_expr' => Expression,\n        'query' => QueryExpression,\n        'operations' => {\n          type => Array[AbstractAttributeOperation],\n          value => []\n        }\n      }\n    }],\n    Parameter => Object[{\n      parent => Positioned,\n      attributes => {\n        'name' => String,\n        'value' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'type_expr' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'captures_rest' => {\n          type => Optional[Boolean],\n          value => undef\n        }\n      }\n    }],\n    Definition => Object[{\n      parent => Expression\n    }],\n    NamedDefinition => Object[{\n      parent => Definition,\n      attributes => {\n        'name' => String,\n        'parameters' => {\n          type => Array[Parameter],\n          value => []\n        },\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    FunctionDefinition => Object[{\n      parent => NamedDefinition,\n      attributes => {\n        'return_type' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    ResourceTypeDefinition => Object[{\n      parent => NamedDefinition\n    }],\n    QRefDefinition => Object[{\n      parent => Definition,\n      attributes => {\n        'name' => String\n      }\n    }],\n    TypeAlias => Object[{\n      parent => QRefDefinition,\n      attributes => {\n        'type_expr' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    TypeMapping => Object[{\n      parent => Definition,\n      attributes => {\n        'type_expr' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'mapping_expr' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    TypeDefinition => Object[{\n      parent => QRefDefinition,\n      attributes => {\n        'parent' => {\n          type => Optional[String],\n          value => undef\n        },\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    NodeDefinition => Object[{\n      parent => Definition,\n      attributes => {\n        'parent' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'host_matches' => Array[Expression, 1, default],\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    HeredocExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'syntax' => {\n          type => Optional[String],\n          value => undef\n        },\n        'text_expr' => Expression\n      }\n    }],\n    HostClassDefinition => Object[{\n      parent => NamedDefinition,\n      attributes => {\n        'parent_class' => {\n          type => Optional[String],\n          value => undef\n        }\n      }\n    }],\n    PlanDefinition => Object[{\n      parent => FunctionDefinition,\n    }],\n    LambdaExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'parameters' => {\n          type => Array[Parameter],\n          value => []\n        },\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'return_type' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    ApplyExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'arguments' => {\n          type => Array[Expression],\n          value => []\n        },\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    IfExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'test' => Expression,\n        'then_expr' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'else_expr' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    UnlessExpression => Object[{\n      parent => IfExpression\n    }],\n    CallExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'rval_required' => {\n          type => Boolean,\n          value => false\n        },\n        'functor_expr' => Expression,\n        'arguments' => {\n          type => Array[Expression],\n          value => []\n        },\n        'lambda' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    CallFunctionExpression => Object[{\n      parent => CallExpression\n    }],\n    CallNamedFunctionExpression => Object[{\n      parent => CallExpression\n    }],\n    CallMethodExpression => Object[{\n      parent => CallExpression\n    }],\n    Literal => Object[{\n      parent => Expression\n    }],\n    LiteralValue => Object[{\n      parent => Literal\n    }],\n    LiteralRegularExpression => Object[{\n      parent => LiteralValue,\n      attributes => {\n        'value' => Any,\n        'pattern' => String\n      }\n    }],\n    LiteralString => Object[{\n      parent => LiteralValue,\n      attributes => {\n        'value' => String\n      }\n    }],\n    LiteralNumber => Object[{\n      parent => LiteralValue\n    }],\n    LiteralInteger => Object[{\n      parent => LiteralNumber,\n      attributes => {\n        'radix' => {\n          type => Integer,\n          value => 10\n        },\n        'value' => Integer\n      }\n    }],\n    LiteralFloat => Object[{\n      parent => LiteralNumber,\n      attributes => {\n        'value' => Float\n      }\n    }],\n    LiteralUndef => Object[{\n      parent => Literal\n    }],\n    LiteralDefault => Object[{\n      parent => Literal\n    }],\n    LiteralBoolean => Object[{\n      parent => LiteralValue,\n      attributes => {\n        'value' => Boolean\n      }\n    }],\n    TextExpression => Object[{\n      parent => UnaryExpression\n    }],\n    ConcatenatedString => Object[{\n      parent => Expression,\n      attributes => {\n        'segments' => {\n          type => Array[Expression],\n          value => []\n        }\n      }\n    }],\n    QualifiedName => Object[{\n      parent => LiteralValue,\n      attributes => {\n        'value' => String\n      }\n    }],\n    ReservedWord => Object[{\n      parent => LiteralValue,\n      attributes => {\n        'word' => String,\n        'future' => {\n          type => Optional[Boolean],\n          value => undef\n        }\n      }\n    }],\n    QualifiedReference => Object[{\n      parent => LiteralValue,\n      attributes => {\n        'cased_value' => String,\n        'value' => {\n          type => String,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => '@cased_value.downcase' }\n          }\n        }\n      }\n    }],\n    VariableExpression => Object[{\n      parent => UnaryExpression\n    }],\n    EppExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'parameters_specified' => {\n          type => Optional[Boolean],\n          value => undef\n        },\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        }\n      }\n    }],\n    RenderStringExpression => Object[{\n      parent => LiteralString\n    }],\n    RenderExpression => Object[{\n      parent => UnaryExpression\n    }],\n    ResourceBody => Object[{\n      parent => Positioned,\n      attributes => {\n        'title' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'operations' => {\n          type => Array[AbstractAttributeOperation],\n          value => []\n        }\n      }\n    }],\n    AbstractResource => Object[{\n      parent => Expression,\n      attributes => {\n        'form' => {\n          type => Enum['exported', 'regular', 'virtual'],\n          value => 'regular'\n        },\n        'virtual' => {\n          type => Boolean,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => \"@form == 'virtual' || @form == 'exported'\" }\n          }\n        },\n        'exported' => {\n          type => Boolean,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => \"@form == 'exported'\" }\n          }\n        }\n      }\n    }],\n    ResourceExpression => Object[{\n      parent => AbstractResource,\n      attributes => {\n        'type_name' => Expression,\n        'bodies' => {\n          type => Array[ResourceBody],\n          value => []\n        }\n      }\n    }],\n    ResourceDefaultsExpression => Object[{\n      parent => AbstractResource,\n      attributes => {\n        'type_ref' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'operations' => {\n          type => Array[AbstractAttributeOperation],\n          value => []\n        }\n      }\n    }],\n    ResourceOverrideExpression => Object[{\n      parent => AbstractResource,\n      attributes => {\n        'resources' => Expression,\n        'operations' => {\n          type => Array[AbstractAttributeOperation],\n          value => []\n        }\n      }\n    }],\n    SelectorEntry => Object[{\n      parent => Positioned,\n      attributes => {\n        'matching_expr' => Expression,\n        'value_expr' => Expression\n      }\n    }],\n    SelectorExpression => Object[{\n      parent => Expression,\n      attributes => {\n        'left_expr' => Expression,\n        'selectors' => {\n          type => Array[SelectorEntry],\n          value => []\n        }\n      }\n    }],\n    NamedAccessExpression => Object[{\n      parent => BinaryExpression\n    }],\n    Program => Object[{\n      parent => PopsObject,\n      attributes => {\n        'body' => {\n          type => Optional[Expression],\n          value => undef\n        },\n        'definitions' => {\n          type => Array[Definition],\n          kind => reference,\n          value => []\n        },\n        'source_text' => {\n          type => String,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => '@locator.string' }\n          }\n        },\n        'source_ref' => {\n          type => String,\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => '@locator.file' }\n          }\n        },\n        'line_offsets' => {\n          type => Array[Integer],\n          kind => derived,\n          annotations => {\n            RubyMethod => { 'body' => '@locator.line_index' }\n          }\n        },\n        'locator' => Locator\n      }\n    }]\n  }\n}]\n"
  },
  {
    "path": "lib/puppet/pops/model/ast.rb",
    "content": "# frozen_string_literal: true\n\n# # Generated by Puppet::Pops::Types::RubyGenerator from TypeSet Puppet::AST on -4712-01-01\n\nmodule Puppet\nmodule Pops\nmodule Model\nclass PopsObject\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::PopsObject', {\n                                            })\n  end\n\n  include Types::PuppetObject\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::PopsObject initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new\n  end\n\n  def self.create\n    new\n  end\n  attr_reader :hash\n\n  def initialize\n    @hash = 2270595461303489901\n  end\n\n  def _pcore_init_hash\n    {}\n  end\n\n  def _pcore_contents\n  end\n\n  def _pcore_all_contents(path)\n  end\n\n  def to_s\n    Types::TypeFormatter.string(self)\n  end\n\n  def eql?(o)\n    o.instance_of?(self.class)\n  end\n  alias == eql?\nend\n\nclass Positioned < PopsObject\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::Positioned',\n             {\n               'parent' => PopsObject._pcore_type,\n               'attributes' => {\n                 'locator' => {\n                   'type' => Parser::Locator::Locator19._pcore_type,\n                   'kind' => 'reference'\n                 },\n                 'offset' => Types::PIntegerType::DEFAULT,\n                 'length' => Types::PIntegerType::DEFAULT,\n                 'file' => {\n                   'type' => Types::PStringType::DEFAULT,\n                   'kind' => 'derived'\n                 },\n                 'line' => {\n                   'type' => Types::PIntegerType::DEFAULT,\n                   'kind' => 'derived'\n                 },\n                 'pos' => {\n                   'type' => Types::PIntegerType::DEFAULT,\n                   'kind' => 'derived'\n                 }\n               },\n               'equality' => []\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::Positioned initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'])\n  end\n\n  def self.create(locator, offset, length)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    new(locator, offset, length)\n  end\n\n  attr_reader :locator\n  attr_reader :offset\n  attr_reader :length\n\n  def file\n    @locator.file\n  end\n\n  def line\n    @locator.line_for_offset(@offset)\n  end\n\n  def pos\n    @locator.pos_on_line(@offset)\n  end\n\n  def initialize(locator, offset, length)\n    super()\n    @locator = locator\n    @offset = offset\n    @length = length\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['locator'] = @locator\n    result['offset'] = @offset\n    result['length'] = @length\n    result\n  end\nend\n\nclass Expression < Positioned\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::Expression', {\n                                              'parent' => Positioned._pcore_type\n                                            })\n  end\nend\n\nclass Nop < Expression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::Nop', {\n                                              'parent' => Expression._pcore_type\n                                            })\n  end\nend\n\nclass BinaryExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType.new('Puppet::AST::BinaryExpression', {\n                               'parent' => Expression._pcore_type,\n                               'attributes' => {\n                                 'left_expr' => Expression._pcore_type,\n                                 'right_expr' => Expression._pcore_type\n                               }\n                             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::BinaryExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash['right_expr'])\n  end\n\n  def self.create(locator, offset, length, left_expr, right_expr)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[right_expr]', attrs['right_expr'].type, right_expr)\n    new(locator, offset, length, left_expr, right_expr)\n  end\n\n  attr_reader :left_expr\n  attr_reader :right_expr\n\n  def initialize(locator, offset, length, left_expr, right_expr)\n    super(locator, offset, length)\n    @hash = @hash ^ left_expr.hash ^ right_expr.hash\n    @left_expr = left_expr\n    @right_expr = right_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['left_expr'] = @left_expr\n    result['right_expr'] = @right_expr\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @left_expr.eql?(o.left_expr) &&\n    @right_expr.eql?(o.right_expr)\n  end\n  alias == eql?\nend\n\nclass UnaryExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType.new('Puppet::AST::UnaryExpression', {\n                               'parent' => Expression._pcore_type,\n                               'attributes' => {\n                                 'expr' => Expression._pcore_type\n                               }\n                             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::UnaryExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['expr'])\n  end\n\n  def self.create(locator, offset, length, expr)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::UnaryExpression[expr]', attrs['expr'].type, expr)\n    new(locator, offset, length, expr)\n  end\n\n  attr_reader :expr\n\n  def initialize(locator, offset, length, expr)\n    super(locator, offset, length)\n    @hash = @hash ^ expr.hash\n    @expr = expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['expr'] = @expr\n    result\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @expr.eql?(o.expr)\n  end\n  alias == eql?\nend\n\nclass ParenthesizedExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::ParenthesizedExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass NotExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::NotExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass UnaryMinusExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::UnaryMinusExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass UnfoldExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::UnfoldExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass AssignmentExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType.new('Puppet::AST::AssignmentExpression', {\n                               'parent' => BinaryExpression._pcore_type,\n                               'attributes' => {\n                                 'operator' => Types::PEnumType.new(['+=', '-=', '='])\n                               }\n                             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::AssignmentExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash['right_expr'],\n      init_hash['operator'])\n  end\n\n  def self.create(locator, offset, length, left_expr, right_expr, operator)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[right_expr]', attrs['right_expr'].type, right_expr)\n    ta.assert_instance_of('Puppet::AST::AssignmentExpression[operator]', attrs['operator'].type, operator)\n    new(locator, offset, length, left_expr, right_expr, operator)\n  end\n\n  attr_reader :operator\n\n  def initialize(locator, offset, length, left_expr, right_expr, operator)\n    super(locator, offset, length, left_expr, right_expr)\n    @hash = @hash ^ operator.hash\n    @operator = operator\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['operator'] = @operator\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @operator.eql?(o.operator)\n  end\n  alias == eql?\nend\n\nclass ArithmeticExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ArithmeticExpression', {\n               'parent' => BinaryExpression._pcore_type,\n               'attributes' => {\n                 'operator' => Types::PEnumType.new(['%', '*', '+', '-', '/', '<<', '>>'])\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ArithmeticExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash['right_expr'],\n      init_hash['operator'])\n  end\n\n  def self.create(locator, offset, length, left_expr, right_expr, operator)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[right_expr]', attrs['right_expr'].type, right_expr)\n    ta.assert_instance_of('Puppet::AST::ArithmeticExpression[operator]', attrs['operator'].type, operator)\n    new(locator, offset, length, left_expr, right_expr, operator)\n  end\n\n  attr_reader :operator\n\n  def initialize(locator, offset, length, left_expr, right_expr, operator)\n    super(locator, offset, length, left_expr, right_expr)\n    @hash = @hash ^ operator.hash\n    @operator = operator\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['operator'] = @operator\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @operator.eql?(o.operator)\n  end\n  alias == eql?\nend\n\nclass RelationshipExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::RelationshipExpression', {\n                                              'parent' => BinaryExpression._pcore_type,\n                                              'attributes' => {\n                                                'operator' => Types::PEnumType.new(['->', '<-', '<~', '~>'])\n                                              }\n                                            })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::RelationshipExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash['right_expr'],\n      init_hash['operator'])\n  end\n\n  def self.create(locator, offset, length, left_expr, right_expr, operator)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[right_expr]', attrs['right_expr'].type, right_expr)\n    ta.assert_instance_of('Puppet::AST::RelationshipExpression[operator]', attrs['operator'].type, operator)\n    new(locator, offset, length, left_expr, right_expr, operator)\n  end\n\n  attr_reader :operator\n\n  def initialize(locator, offset, length, left_expr, right_expr, operator)\n    super(locator, offset, length, left_expr, right_expr)\n    @hash = @hash ^ operator.hash\n    @operator = operator\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['operator'] = @operator\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @operator.eql?(o.operator)\n  end\n  alias == eql?\nend\n\nclass AccessExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::AccessExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'left_expr' => Expression._pcore_type,\n                 'keys' => {\n                   'type' => Types::PArrayType.new(Expression._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::AccessExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash.fetch('keys') { _pcore_type['keys'].value })\n  end\n\n  def self.create(locator, offset, length, left_expr, keys = _pcore_type['keys'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AccessExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::AccessExpression[keys]', attrs['keys'].type, keys)\n    new(locator, offset, length, left_expr, keys)\n  end\n\n  attr_reader :left_expr\n  attr_reader :keys\n\n  def initialize(locator, offset, length, left_expr, keys = _pcore_type['keys'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ left_expr.hash ^ keys.hash\n    @left_expr = left_expr\n    @keys = keys\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['left_expr'] = @left_expr\n    result['keys'] = @keys unless _pcore_type['keys'].default_value?(@keys)\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    @keys.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    @keys.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @left_expr.eql?(o.left_expr) &&\n    @keys.eql?(o.keys)\n  end\n  alias == eql?\nend\n\nclass ComparisonExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ComparisonExpression', {\n               'parent' => BinaryExpression._pcore_type,\n               'attributes' => {\n                 'operator' => Types::PEnumType.new(['!=', '<', '<=', '==', '>', '>='])\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ComparisonExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash['right_expr'],\n      init_hash['operator'])\n  end\n\n  def self.create(locator, offset, length, left_expr, right_expr, operator)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[right_expr]', attrs['right_expr'].type, right_expr)\n    ta.assert_instance_of('Puppet::AST::ComparisonExpression[operator]', attrs['operator'].type, operator)\n    new(locator, offset, length, left_expr, right_expr, operator)\n  end\n\n  attr_reader :operator\n\n  def initialize(locator, offset, length, left_expr, right_expr, operator)\n    super(locator, offset, length, left_expr, right_expr)\n    @hash = @hash ^ operator.hash\n    @operator = operator\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['operator'] = @operator\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @operator.eql?(o.operator)\n  end\n  alias == eql?\nend\n\nclass MatchExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::MatchExpression', {\n               'parent' => BinaryExpression._pcore_type,\n               'attributes' => {\n                 'operator' => Types::PEnumType.new(['!~', '=~'])\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::MatchExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash['right_expr'],\n      init_hash['operator'])\n  end\n\n  def self.create(locator, offset, length, left_expr, right_expr, operator)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::BinaryExpression[right_expr]', attrs['right_expr'].type, right_expr)\n    ta.assert_instance_of('Puppet::AST::MatchExpression[operator]', attrs['operator'].type, operator)\n    new(locator, offset, length, left_expr, right_expr, operator)\n  end\n\n  attr_reader :operator\n\n  def initialize(locator, offset, length, left_expr, right_expr, operator)\n    super(locator, offset, length, left_expr, right_expr)\n    @hash = @hash ^ operator.hash\n    @operator = operator\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['operator'] = @operator\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @operator.eql?(o.operator)\n  end\n  alias == eql?\nend\n\nclass InExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::InExpression', {\n                                              'parent' => BinaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass BooleanExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::BooleanExpression', {\n                                              'parent' => BinaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass AndExpression < BooleanExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::AndExpression', {\n                                              'parent' => BooleanExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass OrExpression < BooleanExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::OrExpression', {\n                                              'parent' => BooleanExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass LiteralList < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralList', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'values' => {\n                   'type' => Types::PArrayType.new(Expression._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralList initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('values') { _pcore_type['values'].value })\n  end\n\n  def self.create(locator, offset, length, values = _pcore_type['values'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralList[values]', attrs['values'].type, values)\n    new(locator, offset, length, values)\n  end\n\n  attr_reader :values\n\n  def initialize(locator, offset, length, values = _pcore_type['values'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ values.hash\n    @values = values\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['values'] = @values unless _pcore_type['values'].default_value?(@values)\n    result\n  end\n\n  def _pcore_contents\n    @values.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @values.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @values.eql?(o.values)\n  end\n  alias == eql?\nend\n\nclass KeyedEntry < Positioned\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::KeyedEntry', {\n               'parent' => Positioned._pcore_type,\n               'attributes' => {\n                 'key' => Expression._pcore_type,\n                 'value' => Expression._pcore_type\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::KeyedEntry initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['key'],\n      init_hash['value'])\n  end\n\n  def self.create(locator, offset, length, key, value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::KeyedEntry[key]', attrs['key'].type, key)\n    ta.assert_instance_of('Puppet::AST::KeyedEntry[value]', attrs['value'].type, value)\n    new(locator, offset, length, key, value)\n  end\n\n  attr_reader :key\n  attr_reader :value\n\n  def initialize(locator, offset, length, key, value)\n    super(locator, offset, length)\n    @hash = @hash ^ key.hash ^ value.hash\n    @key = key\n    @value = value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['key'] = @key\n    result['value'] = @value\n    result\n  end\n\n  def _pcore_contents\n    yield(@key) unless @key.nil?\n    yield(@value) unless @value.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @key.nil?\n      block.call(@key, path)\n      @key._pcore_all_contents(path, &block)\n    end\n    unless @value.nil?\n      block.call(@value, path)\n      @value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @key.eql?(o.key) &&\n    @value.eql?(o.value)\n  end\n  alias == eql?\nend\n\nclass LiteralHash < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralHash', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'entries' => {\n                   'type' => Types::PArrayType.new(KeyedEntry._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralHash initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('entries') { _pcore_type['entries'].value })\n  end\n\n  def self.create(locator, offset, length, entries = _pcore_type['entries'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralHash[entries]', attrs['entries'].type, entries)\n    new(locator, offset, length, entries)\n  end\n\n  attr_reader :entries\n\n  def initialize(locator, offset, length, entries = _pcore_type['entries'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ entries.hash\n    @entries = entries\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['entries'] = @entries unless _pcore_type['entries'].default_value?(@entries)\n    result\n  end\n\n  def _pcore_contents\n    @entries.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @entries.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @entries.eql?(o.entries)\n  end\n  alias == eql?\nend\n\nclass BlockExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::BlockExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'statements' => {\n                   'type' => Types::PArrayType.new(Expression._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::BlockExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('statements') { _pcore_type['statements'].value })\n  end\n\n  def self.create(locator, offset, length, statements = _pcore_type['statements'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::BlockExpression[statements]', attrs['statements'].type, statements)\n    new(locator, offset, length, statements)\n  end\n\n  attr_reader :statements\n\n  def initialize(locator, offset, length, statements = _pcore_type['statements'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ statements.hash\n    @statements = statements\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['statements'] = @statements unless _pcore_type['statements'].default_value?(@statements)\n    result\n  end\n\n  def _pcore_contents\n    @statements.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @statements.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @statements.eql?(o.statements)\n  end\n  alias == eql?\nend\n\nclass ApplyBlockExpression < BlockExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::ApplyBlockExpression', {\n                                              'parent' => BlockExpression._pcore_type,\n                                            })\n  end\nend\n\nclass CaseOption < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::CaseOption', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'values' => Types::PArrayType.new(Expression._pcore_type, Types::PCollectionType::NOT_EMPTY_SIZE),\n                 'then_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::CaseOption initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['values'],\n      init_hash['then_expr'])\n  end\n\n  def self.create(locator, offset, length, values, then_expr = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::CaseOption[values]', attrs['values'].type, values)\n    ta.assert_instance_of('Puppet::AST::CaseOption[then_expr]', attrs['then_expr'].type, then_expr)\n    new(locator, offset, length, values, then_expr)\n  end\n\n  attr_reader :values\n  attr_reader :then_expr\n\n  def initialize(locator, offset, length, values, then_expr = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ values.hash ^ then_expr.hash\n    @values = values\n    @then_expr = then_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['values'] = @values\n    result['then_expr'] = @then_expr unless @then_expr == nil\n    result\n  end\n\n  def _pcore_contents\n    @values.each { |value| yield(value) }\n    yield(@then_expr) unless @then_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @values.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @then_expr.nil?\n      block.call(@then_expr, path)\n      @then_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @values.eql?(o.values) &&\n    @then_expr.eql?(o.then_expr)\n  end\n  alias == eql?\nend\n\nclass CaseExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::CaseExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'test' => Expression._pcore_type,\n                 'options' => {\n                   'type' => Types::PArrayType.new(CaseOption._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::CaseExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['test'],\n      init_hash.fetch('options') { _pcore_type['options'].value })\n  end\n\n  def self.create(locator, offset, length, test, options = _pcore_type['options'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::CaseExpression[test]', attrs['test'].type, test)\n    ta.assert_instance_of('Puppet::AST::CaseExpression[options]', attrs['options'].type, options)\n    new(locator, offset, length, test, options)\n  end\n\n  attr_reader :test\n  attr_reader :options\n\n  def initialize(locator, offset, length, test, options = _pcore_type['options'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ test.hash ^ options.hash\n    @test = test\n    @options = options\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['test'] = @test\n    result['options'] = @options unless _pcore_type['options'].default_value?(@options)\n    result\n  end\n\n  def _pcore_contents\n    yield(@test) unless @test.nil?\n    @options.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @test.nil?\n      block.call(@test, path)\n      @test._pcore_all_contents(path, &block)\n    end\n    @options.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @test.eql?(o.test) &&\n    @options.eql?(o.options)\n  end\n  alias == eql?\nend\n\nclass QueryExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::QueryExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::QueryExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['expr'])\n  end\n\n  def self.create(locator, offset, length, expr = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::QueryExpression[expr]', attrs['expr'].type, expr)\n    new(locator, offset, length, expr)\n  end\n\n  attr_reader :expr\n\n  def initialize(locator, offset, length, expr = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ expr.hash\n    @expr = expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['expr'] = @expr unless @expr == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @expr.eql?(o.expr)\n  end\n  alias == eql?\nend\n\nclass ExportedQuery < QueryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::ExportedQuery', {\n                                              'parent' => QueryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass VirtualQuery < QueryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::VirtualQuery', {\n                                              'parent' => QueryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass AbstractAttributeOperation < Positioned\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::AbstractAttributeOperation', {\n                                              'parent' => Positioned._pcore_type\n                                            })\n  end\nend\n\nclass AttributeOperation < AbstractAttributeOperation\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::AttributeOperation', {\n               'parent' => AbstractAttributeOperation._pcore_type,\n               'attributes' => {\n                 'attribute_name' => Types::PStringType::DEFAULT,\n                 'operator' => Types::PEnumType.new(['+>', '=>']),\n                 'value_expr' => Expression._pcore_type\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::AttributeOperation initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['attribute_name'],\n      init_hash['operator'],\n      init_hash['value_expr'])\n  end\n\n  def self.create(locator, offset, length, attribute_name, operator, value_expr)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AttributeOperation[attribute_name]', attrs['attribute_name'].type, attribute_name)\n    ta.assert_instance_of('Puppet::AST::AttributeOperation[operator]', attrs['operator'].type, operator)\n    ta.assert_instance_of('Puppet::AST::AttributeOperation[value_expr]', attrs['value_expr'].type, value_expr)\n    new(locator, offset, length, attribute_name, operator, value_expr)\n  end\n\n  attr_reader :attribute_name\n  attr_reader :operator\n  attr_reader :value_expr\n\n  def initialize(locator, offset, length, attribute_name, operator, value_expr)\n    super(locator, offset, length)\n    @hash = @hash ^ attribute_name.hash ^ operator.hash ^ value_expr.hash\n    @attribute_name = attribute_name\n    @operator = operator\n    @value_expr = value_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['attribute_name'] = @attribute_name\n    result['operator'] = @operator\n    result['value_expr'] = @value_expr\n    result\n  end\n\n  def _pcore_contents\n    yield(@value_expr) unless @value_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @value_expr.nil?\n      block.call(@value_expr, path)\n      @value_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @attribute_name.eql?(o.attribute_name) &&\n    @operator.eql?(o.operator) &&\n    @value_expr.eql?(o.value_expr)\n  end\n  alias == eql?\nend\n\nclass AttributesOperation < AbstractAttributeOperation\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::AttributesOperation', {\n               'parent' => AbstractAttributeOperation._pcore_type,\n               'attributes' => {\n                 'expr' => Expression._pcore_type\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::AttributesOperation initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['expr'])\n  end\n\n  def self.create(locator, offset, length, expr)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AttributesOperation[expr]', attrs['expr'].type, expr)\n    new(locator, offset, length, expr)\n  end\n\n  attr_reader :expr\n\n  def initialize(locator, offset, length, expr)\n    super(locator, offset, length)\n    @hash = @hash ^ expr.hash\n    @expr = expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['expr'] = @expr\n    result\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @expr.eql?(o.expr)\n  end\n  alias == eql?\nend\n\nclass CollectExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::CollectExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'type_expr' => Expression._pcore_type,\n                 'query' => QueryExpression._pcore_type,\n                 'operations' => {\n                   'type' => Types::PArrayType.new(AbstractAttributeOperation._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::CollectExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['type_expr'],\n      init_hash['query'],\n      init_hash.fetch('operations') { _pcore_type['operations'].value })\n  end\n\n  def self.create(locator, offset, length, type_expr, query, operations = _pcore_type['operations'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::CollectExpression[type_expr]', attrs['type_expr'].type, type_expr)\n    ta.assert_instance_of('Puppet::AST::CollectExpression[query]', attrs['query'].type, query)\n    ta.assert_instance_of('Puppet::AST::CollectExpression[operations]', attrs['operations'].type, operations)\n    new(locator, offset, length, type_expr, query, operations)\n  end\n\n  attr_reader :type_expr\n  attr_reader :query\n  attr_reader :operations\n\n  def initialize(locator, offset, length, type_expr, query, operations = _pcore_type['operations'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ type_expr.hash ^ query.hash ^ operations.hash\n    @type_expr = type_expr\n    @query = query\n    @operations = operations\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['type_expr'] = @type_expr\n    result['query'] = @query\n    result['operations'] = @operations unless _pcore_type['operations'].default_value?(@operations)\n    result\n  end\n\n  def _pcore_contents\n    yield(@type_expr) unless @type_expr.nil?\n    yield(@query) unless @query.nil?\n    @operations.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @type_expr.nil?\n      block.call(@type_expr, path)\n      @type_expr._pcore_all_contents(path, &block)\n    end\n    unless @query.nil?\n      block.call(@query, path)\n      @query._pcore_all_contents(path, &block)\n    end\n    @operations.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @type_expr.eql?(o.type_expr) &&\n    @query.eql?(o.query) &&\n    @operations.eql?(o.operations)\n  end\n  alias == eql?\nend\n\nclass Parameter < Positioned\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::Parameter', {\n               'parent' => Positioned._pcore_type,\n               'attributes' => {\n                 'name' => Types::PStringType::DEFAULT,\n                 'value' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'type_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'captures_rest' => {\n                   'type' => Types::POptionalType.new(Types::PBooleanType::DEFAULT),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::Parameter initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'],\n      init_hash['value'],\n      init_hash['type_expr'],\n      init_hash['captures_rest'])\n  end\n\n  def self.create(locator, offset, length, name, value = nil, type_expr = nil, captures_rest = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::Parameter[name]', attrs['name'].type, name)\n    ta.assert_instance_of('Puppet::AST::Parameter[value]', attrs['value'].type, value)\n    ta.assert_instance_of('Puppet::AST::Parameter[type_expr]', attrs['type_expr'].type, type_expr)\n    ta.assert_instance_of('Puppet::AST::Parameter[captures_rest]', attrs['captures_rest'].type, captures_rest)\n    new(locator, offset, length, name, value, type_expr, captures_rest)\n  end\n\n  attr_reader :name\n  attr_reader :value\n  attr_reader :type_expr\n  attr_reader :captures_rest\n\n  def initialize(locator, offset, length, name, value = nil, type_expr = nil, captures_rest = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ name.hash ^ value.hash ^ type_expr.hash ^ captures_rest.hash\n    @name = name\n    @value = value\n    @type_expr = type_expr\n    @captures_rest = captures_rest\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['name'] = @name\n    result['value'] = @value unless @value == nil\n    result['type_expr'] = @type_expr unless @type_expr == nil\n    result['captures_rest'] = @captures_rest unless @captures_rest == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@value) unless @value.nil?\n    yield(@type_expr) unless @type_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @value.nil?\n      block.call(@value, path)\n      @value._pcore_all_contents(path, &block)\n    end\n    unless @type_expr.nil?\n      block.call(@type_expr, path)\n      @type_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @name.eql?(o.name) &&\n    @value.eql?(o.value) &&\n    @type_expr.eql?(o.type_expr) &&\n    @captures_rest.eql?(o.captures_rest)\n  end\n  alias == eql?\nend\n\nclass Definition < Expression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::Definition', {\n                                              'parent' => Expression._pcore_type\n                                            })\n  end\nend\n\nclass NamedDefinition < Definition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::NamedDefinition', {\n               'parent' => Definition._pcore_type,\n               'attributes' => {\n                 'name' => Types::PStringType::DEFAULT,\n                 'parameters' => {\n                   'type' => Types::PArrayType.new(Parameter._pcore_type),\n                   'value' => []\n                 },\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::NamedDefinition initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'],\n      init_hash.fetch('parameters') { _pcore_type['parameters'].value },\n      init_hash['body'])\n  end\n\n  def self.create(locator, offset, length, name, parameters = _pcore_type['parameters'].value, body = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[name]', attrs['name'].type, name)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[parameters]', attrs['parameters'].type, parameters)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[body]', attrs['body'].type, body)\n    new(locator, offset, length, name, parameters, body)\n  end\n\n  attr_reader :name\n  attr_reader :parameters\n  attr_reader :body\n\n  def initialize(locator, offset, length, name, parameters = _pcore_type['parameters'].value, body = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ name.hash ^ parameters.hash ^ body.hash\n    @name = name\n    @parameters = parameters\n    @body = body\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['name'] = @name\n    result['parameters'] = @parameters unless _pcore_type['parameters'].default_value?(@parameters)\n    result['body'] = @body unless @body == nil\n    result\n  end\n\n  def _pcore_contents\n    @parameters.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @parameters.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @name.eql?(o.name) &&\n    @parameters.eql?(o.parameters) &&\n    @body.eql?(o.body)\n  end\n  alias == eql?\nend\n\nclass FunctionDefinition < NamedDefinition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::FunctionDefinition', {\n               'parent' => NamedDefinition._pcore_type,\n               'attributes' => {\n                 'return_type' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::FunctionDefinition initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'],\n      init_hash.fetch('parameters') { _pcore_type['parameters'].value },\n      init_hash['body'],\n      init_hash['return_type'])\n  end\n\n  def self.create(locator, offset, length, name, parameters = _pcore_type['parameters'].value, body = nil, return_type = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[name]', attrs['name'].type, name)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[parameters]', attrs['parameters'].type, parameters)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[body]', attrs['body'].type, body)\n    ta.assert_instance_of('Puppet::AST::FunctionDefinition[return_type]', attrs['return_type'].type, return_type)\n    new(locator, offset, length, name, parameters, body, return_type)\n  end\n\n  attr_reader :return_type\n\n  def initialize(locator, offset, length, name, parameters = _pcore_type['parameters'].value, body = nil, return_type = nil)\n    super(locator, offset, length, name, parameters, body)\n    @hash = @hash ^ return_type.hash\n    @return_type = return_type\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['return_type'] = @return_type unless @return_type == nil\n    result\n  end\n\n  def _pcore_contents\n    @parameters.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n    yield(@return_type) unless @return_type.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @parameters.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    unless @return_type.nil?\n      block.call(@return_type, path)\n      @return_type._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @return_type.eql?(o.return_type)\n  end\n  alias == eql?\nend\n\nclass ResourceTypeDefinition < NamedDefinition\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::ResourceTypeDefinition', {\n                                              'parent' => NamedDefinition._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    @parameters.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @parameters.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass QRefDefinition < Definition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::QRefDefinition', {\n               'parent' => Definition._pcore_type,\n               'attributes' => {\n                 'name' => Types::PStringType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::QRefDefinition initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'])\n  end\n\n  def self.create(locator, offset, length, name)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::QRefDefinition[name]', attrs['name'].type, name)\n    new(locator, offset, length, name)\n  end\n\n  attr_reader :name\n\n  def initialize(locator, offset, length, name)\n    super(locator, offset, length)\n    @hash = @hash ^ name.hash\n    @name = name\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['name'] = @name\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @name.eql?(o.name)\n  end\n  alias == eql?\nend\n\nclass TypeAlias < QRefDefinition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::TypeAlias', {\n               'parent' => QRefDefinition._pcore_type,\n               'attributes' => {\n                 'type_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::TypeAlias initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'],\n      init_hash['type_expr'])\n  end\n\n  def self.create(locator, offset, length, name, type_expr = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::QRefDefinition[name]', attrs['name'].type, name)\n    ta.assert_instance_of('Puppet::AST::TypeAlias[type_expr]', attrs['type_expr'].type, type_expr)\n    new(locator, offset, length, name, type_expr)\n  end\n\n  attr_reader :type_expr\n\n  def initialize(locator, offset, length, name, type_expr = nil)\n    super(locator, offset, length, name)\n    @hash = @hash ^ type_expr.hash\n    @type_expr = type_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['type_expr'] = @type_expr unless @type_expr == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@type_expr) unless @type_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @type_expr.nil?\n      block.call(@type_expr, path)\n      @type_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @type_expr.eql?(o.type_expr)\n  end\n  alias == eql?\nend\n\nclass TypeMapping < Definition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::TypeMapping', {\n               'parent' => Definition._pcore_type,\n               'attributes' => {\n                 'type_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'mapping_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::TypeMapping initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['type_expr'],\n      init_hash['mapping_expr'])\n  end\n\n  def self.create(locator, offset, length, type_expr = nil, mapping_expr = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::TypeMapping[type_expr]', attrs['type_expr'].type, type_expr)\n    ta.assert_instance_of('Puppet::AST::TypeMapping[mapping_expr]', attrs['mapping_expr'].type, mapping_expr)\n    new(locator, offset, length, type_expr, mapping_expr)\n  end\n\n  attr_reader :type_expr\n  attr_reader :mapping_expr\n\n  def initialize(locator, offset, length, type_expr = nil, mapping_expr = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ type_expr.hash ^ mapping_expr.hash\n    @type_expr = type_expr\n    @mapping_expr = mapping_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['type_expr'] = @type_expr unless @type_expr == nil\n    result['mapping_expr'] = @mapping_expr unless @mapping_expr == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@type_expr) unless @type_expr.nil?\n    yield(@mapping_expr) unless @mapping_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @type_expr.nil?\n      block.call(@type_expr, path)\n      @type_expr._pcore_all_contents(path, &block)\n    end\n    unless @mapping_expr.nil?\n      block.call(@mapping_expr, path)\n      @mapping_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @type_expr.eql?(o.type_expr) &&\n    @mapping_expr.eql?(o.mapping_expr)\n  end\n  alias == eql?\nend\n\nclass TypeDefinition < QRefDefinition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::TypeDefinition', {\n               'parent' => QRefDefinition._pcore_type,\n               'attributes' => {\n                 'parent' => {\n                   'type' => Types::POptionalType.new(Types::PStringType::DEFAULT),\n                   'value' => nil\n                 },\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::TypeDefinition initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'],\n      init_hash['parent'],\n      init_hash['body'])\n  end\n\n  def self.create(locator, offset, length, name, parent = nil, body = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::QRefDefinition[name]', attrs['name'].type, name)\n    ta.assert_instance_of('Puppet::AST::TypeDefinition[parent]', attrs['parent'].type, parent)\n    ta.assert_instance_of('Puppet::AST::TypeDefinition[body]', attrs['body'].type, body)\n    new(locator, offset, length, name, parent, body)\n  end\n\n  attr_reader :parent\n  attr_reader :body\n\n  def initialize(locator, offset, length, name, parent = nil, body = nil)\n    super(locator, offset, length, name)\n    @hash = @hash ^ parent.hash ^ body.hash\n    @parent = parent\n    @body = body\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['parent'] = @parent unless @parent == nil\n    result['body'] = @body unless @body == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @parent.eql?(o.parent) &&\n    @body.eql?(o.body)\n  end\n  alias == eql?\nend\n\nclass NodeDefinition < Definition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::NodeDefinition', {\n               'parent' => Definition._pcore_type,\n               'attributes' => {\n                 'parent' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'host_matches' => Types::PArrayType.new(Expression._pcore_type, Types::PCollectionType::NOT_EMPTY_SIZE),\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::NodeDefinition initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['host_matches'],\n      init_hash['parent'],\n      init_hash['body'])\n  end\n\n  def self.create(locator, offset, length, host_matches, parent = nil, body = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::NodeDefinition[parent]', attrs['parent'].type, parent)\n    ta.assert_instance_of('Puppet::AST::NodeDefinition[host_matches]', attrs['host_matches'].type, host_matches)\n    ta.assert_instance_of('Puppet::AST::NodeDefinition[body]', attrs['body'].type, body)\n    new(locator, offset, length, host_matches, parent, body)\n  end\n\n  attr_reader :parent\n  attr_reader :host_matches\n  attr_reader :body\n\n  def initialize(locator, offset, length, host_matches, parent = nil, body = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ parent.hash ^ host_matches.hash ^ body.hash\n    @parent = parent\n    @host_matches = host_matches\n    @body = body\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['parent'] = @parent unless @parent == nil\n    result['host_matches'] = @host_matches\n    result['body'] = @body unless @body == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@parent) unless @parent.nil?\n    @host_matches.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @parent.nil?\n      block.call(@parent, path)\n      @parent._pcore_all_contents(path, &block)\n    end\n    @host_matches.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @parent.eql?(o.parent) &&\n    @host_matches.eql?(o.host_matches) &&\n    @body.eql?(o.body)\n  end\n  alias == eql?\nend\n\nclass HeredocExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::HeredocExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'syntax' => {\n                   'type' => Types::POptionalType.new(Types::PStringType::DEFAULT),\n                   'value' => nil\n                 },\n                 'text_expr' => Expression._pcore_type\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::HeredocExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['text_expr'],\n      init_hash['syntax'])\n  end\n\n  def self.create(locator, offset, length, text_expr, syntax = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::HeredocExpression[syntax]', attrs['syntax'].type, syntax)\n    ta.assert_instance_of('Puppet::AST::HeredocExpression[text_expr]', attrs['text_expr'].type, text_expr)\n    new(locator, offset, length, text_expr, syntax)\n  end\n\n  attr_reader :syntax\n  attr_reader :text_expr\n\n  def initialize(locator, offset, length, text_expr, syntax = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ syntax.hash ^ text_expr.hash\n    @syntax = syntax\n    @text_expr = text_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['syntax'] = @syntax unless @syntax == nil\n    result['text_expr'] = @text_expr\n    result\n  end\n\n  def _pcore_contents\n    yield(@text_expr) unless @text_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @text_expr.nil?\n      block.call(@text_expr, path)\n      @text_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @syntax.eql?(o.syntax) &&\n    @text_expr.eql?(o.text_expr)\n  end\n  alias == eql?\nend\n\nclass HostClassDefinition < NamedDefinition\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::HostClassDefinition', {\n               'parent' => NamedDefinition._pcore_type,\n               'attributes' => {\n                 'parent_class' => {\n                   'type' => Types::POptionalType.new(Types::PStringType::DEFAULT),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::HostClassDefinition initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['name'],\n      init_hash.fetch('parameters') { _pcore_type['parameters'].value },\n      init_hash['body'],\n      init_hash['parent_class'])\n  end\n\n  def self.create(locator, offset, length, name, parameters = _pcore_type['parameters'].value, body = nil, parent_class = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[name]', attrs['name'].type, name)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[parameters]', attrs['parameters'].type, parameters)\n    ta.assert_instance_of('Puppet::AST::NamedDefinition[body]', attrs['body'].type, body)\n    ta.assert_instance_of('Puppet::AST::HostClassDefinition[parent_class]', attrs['parent_class'].type, parent_class)\n    new(locator, offset, length, name, parameters, body, parent_class)\n  end\n\n  attr_reader :parent_class\n\n  def initialize(locator, offset, length, name, parameters = _pcore_type['parameters'].value, body = nil, parent_class = nil)\n    super(locator, offset, length, name, parameters, body)\n    @hash = @hash ^ parent_class.hash\n    @parent_class = parent_class\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['parent_class'] = @parent_class unless @parent_class == nil\n    result\n  end\n\n  def _pcore_contents\n    @parameters.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @parameters.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @parent_class.eql?(o.parent_class)\n  end\n  alias == eql?\nend\n\nclass PlanDefinition < FunctionDefinition\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::PlanDefinition', {\n                                              'parent' => FunctionDefinition._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    @parameters.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n    yield(@return_type) unless @return_type.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @parameters.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    unless @return_type.nil?\n      block.call(@return_type, path)\n      @return_type._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass LambdaExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LambdaExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'parameters' => {\n                   'type' => Types::PArrayType.new(Parameter._pcore_type),\n                   'value' => []\n                 },\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'return_type' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LambdaExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('parameters') { _pcore_type['parameters'].value },\n      init_hash['body'],\n      init_hash['return_type'])\n  end\n\n  def self.create(locator, offset, length, parameters = _pcore_type['parameters'].value, body = nil, return_type = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LambdaExpression[parameters]', attrs['parameters'].type, parameters)\n    ta.assert_instance_of('Puppet::AST::LambdaExpression[body]', attrs['body'].type, body)\n    ta.assert_instance_of('Puppet::AST::LambdaExpression[return_type]', attrs['return_type'].type, return_type)\n    new(locator, offset, length, parameters, body, return_type)\n  end\n\n  attr_reader :parameters\n  attr_reader :body\n  attr_reader :return_type\n\n  def initialize(locator, offset, length, parameters = _pcore_type['parameters'].value, body = nil, return_type = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ parameters.hash ^ body.hash ^ return_type.hash\n    @parameters = parameters\n    @body = body\n    @return_type = return_type\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['parameters'] = @parameters unless _pcore_type['parameters'].default_value?(@parameters)\n    result['body'] = @body unless @body == nil\n    result['return_type'] = @return_type unless @return_type == nil\n    result\n  end\n\n  def _pcore_contents\n    @parameters.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n    yield(@return_type) unless @return_type.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @parameters.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    unless @return_type.nil?\n      block.call(@return_type, path)\n      @return_type._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @parameters.eql?(o.parameters) &&\n    @body.eql?(o.body) &&\n    @return_type.eql?(o.return_type)\n  end\n  alias == eql?\nend\n\nclass ApplyExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ApplyExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'arguments' => {\n                   'type' => Types::PArrayType.new(Expression._pcore_type),\n                   'value' => []\n                 },\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ApplyExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('arguments') { _pcore_type['arguments'].value },\n      init_hash['body'])\n  end\n\n  def self.create(locator, offset, length, arguments = _pcore_type['arguments'].value, body = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::ApplyExpression[arguments]', attrs['arguments'].type, arguments)\n    ta.assert_instance_of('Puppet::AST::ApplyExpression[body]', attrs['body'].type, body)\n    new(locator, offset, length, arguments, body)\n  end\n\n  attr_reader :arguments\n  attr_reader :body\n\n  def initialize(locator, offset, length, arguments = _pcore_type['arguments'].value, body = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ arguments.hash ^ body.hash\n    @arguments = arguments\n    @body = body\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['arguments'] = @arguments unless _pcore_type['arguments'].default_value?(@arguments)\n    result['body'] = @body unless @body == nil\n    result\n  end\n\n  def _pcore_contents\n    @arguments.each { |value| yield(value) }\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @arguments.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @arguments.eql?(o.arguments) &&\n    @body.eql?(o.body)\n  end\n  alias == eql?\nend\n\nclass IfExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::IfExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'test' => Expression._pcore_type,\n                 'then_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'else_expr' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::IfExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['test'],\n      init_hash['then_expr'],\n      init_hash['else_expr'])\n  end\n\n  def self.create(locator, offset, length, test, then_expr = nil, else_expr = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::IfExpression[test]', attrs['test'].type, test)\n    ta.assert_instance_of('Puppet::AST::IfExpression[then_expr]', attrs['then_expr'].type, then_expr)\n    ta.assert_instance_of('Puppet::AST::IfExpression[else_expr]', attrs['else_expr'].type, else_expr)\n    new(locator, offset, length, test, then_expr, else_expr)\n  end\n\n  attr_reader :test\n  attr_reader :then_expr\n  attr_reader :else_expr\n\n  def initialize(locator, offset, length, test, then_expr = nil, else_expr = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ test.hash ^ then_expr.hash ^ else_expr.hash\n    @test = test\n    @then_expr = then_expr\n    @else_expr = else_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['test'] = @test\n    result['then_expr'] = @then_expr unless @then_expr == nil\n    result['else_expr'] = @else_expr unless @else_expr == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@test) unless @test.nil?\n    yield(@then_expr) unless @then_expr.nil?\n    yield(@else_expr) unless @else_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @test.nil?\n      block.call(@test, path)\n      @test._pcore_all_contents(path, &block)\n    end\n    unless @then_expr.nil?\n      block.call(@then_expr, path)\n      @then_expr._pcore_all_contents(path, &block)\n    end\n    unless @else_expr.nil?\n      block.call(@else_expr, path)\n      @else_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @test.eql?(o.test) &&\n    @then_expr.eql?(o.then_expr) &&\n    @else_expr.eql?(o.else_expr)\n  end\n  alias == eql?\nend\n\nclass UnlessExpression < IfExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::UnlessExpression', {\n                                              'parent' => IfExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@test) unless @test.nil?\n    yield(@then_expr) unless @then_expr.nil?\n    yield(@else_expr) unless @else_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @test.nil?\n      block.call(@test, path)\n      @test._pcore_all_contents(path, &block)\n    end\n    unless @then_expr.nil?\n      block.call(@then_expr, path)\n      @then_expr._pcore_all_contents(path, &block)\n    end\n    unless @else_expr.nil?\n      block.call(@else_expr, path)\n      @else_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass CallExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::CallExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'rval_required' => {\n                   'type' => Types::PBooleanType::DEFAULT,\n                   'value' => false\n                 },\n                 'functor_expr' => Expression._pcore_type,\n                 'arguments' => {\n                   'type' => Types::PArrayType.new(Expression._pcore_type),\n                   'value' => []\n                 },\n                 'lambda' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::CallExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['functor_expr'],\n      init_hash.fetch('rval_required') { false },\n      init_hash.fetch('arguments') { _pcore_type['arguments'].value },\n      init_hash['lambda'])\n  end\n\n  def self.create(locator, offset, length, functor_expr, rval_required = false, arguments = _pcore_type['arguments'].value, lambda = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::CallExpression[rval_required]', attrs['rval_required'].type, rval_required)\n    ta.assert_instance_of('Puppet::AST::CallExpression[functor_expr]', attrs['functor_expr'].type, functor_expr)\n    ta.assert_instance_of('Puppet::AST::CallExpression[arguments]', attrs['arguments'].type, arguments)\n    ta.assert_instance_of('Puppet::AST::CallExpression[lambda]', attrs['lambda'].type, lambda)\n    new(locator, offset, length, functor_expr, rval_required, arguments, lambda)\n  end\n\n  attr_reader :rval_required\n  attr_reader :functor_expr\n  attr_reader :arguments\n  attr_reader :lambda\n\n  def initialize(locator, offset, length, functor_expr, rval_required = false, arguments = _pcore_type['arguments'].value, lambda = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ rval_required.hash ^ functor_expr.hash ^ arguments.hash ^ lambda.hash\n    @rval_required = rval_required\n    @functor_expr = functor_expr\n    @arguments = arguments\n    @lambda = lambda\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['rval_required'] = @rval_required unless @rval_required == false\n    result['functor_expr'] = @functor_expr\n    result['arguments'] = @arguments unless _pcore_type['arguments'].default_value?(@arguments)\n    result['lambda'] = @lambda unless @lambda == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@functor_expr) unless @functor_expr.nil?\n    @arguments.each { |value| yield(value) }\n    yield(@lambda) unless @lambda.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @functor_expr.nil?\n      block.call(@functor_expr, path)\n      @functor_expr._pcore_all_contents(path, &block)\n    end\n    @arguments.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @lambda.nil?\n      block.call(@lambda, path)\n      @lambda._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @rval_required.eql?(o.rval_required) &&\n    @functor_expr.eql?(o.functor_expr) &&\n    @arguments.eql?(o.arguments) &&\n    @lambda.eql?(o.lambda)\n  end\n  alias == eql?\nend\n\nclass CallFunctionExpression < CallExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::CallFunctionExpression', {\n                                              'parent' => CallExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@functor_expr) unless @functor_expr.nil?\n    @arguments.each { |value| yield(value) }\n    yield(@lambda) unless @lambda.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @functor_expr.nil?\n      block.call(@functor_expr, path)\n      @functor_expr._pcore_all_contents(path, &block)\n    end\n    @arguments.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @lambda.nil?\n      block.call(@lambda, path)\n      @lambda._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass CallNamedFunctionExpression < CallExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::CallNamedFunctionExpression', {\n                                              'parent' => CallExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@functor_expr) unless @functor_expr.nil?\n    @arguments.each { |value| yield(value) }\n    yield(@lambda) unless @lambda.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @functor_expr.nil?\n      block.call(@functor_expr, path)\n      @functor_expr._pcore_all_contents(path, &block)\n    end\n    @arguments.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @lambda.nil?\n      block.call(@lambda, path)\n      @lambda._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass CallMethodExpression < CallExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::CallMethodExpression', {\n                                              'parent' => CallExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@functor_expr) unless @functor_expr.nil?\n    @arguments.each { |value| yield(value) }\n    yield(@lambda) unless @lambda.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @functor_expr.nil?\n      block.call(@functor_expr, path)\n      @functor_expr._pcore_all_contents(path, &block)\n    end\n    @arguments.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    unless @lambda.nil?\n      block.call(@lambda, path)\n      @lambda._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass Literal < Expression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::Literal', {\n                                              'parent' => Expression._pcore_type\n                                            })\n  end\nend\n\nclass LiteralValue < Literal\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::LiteralValue', {\n                                              'parent' => Literal._pcore_type\n                                            })\n  end\nend\n\nclass LiteralRegularExpression < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralRegularExpression', {\n               'parent' => LiteralValue._pcore_type,\n               'attributes' => {\n                 'value' => Types::PAnyType::DEFAULT,\n                 'pattern' => Types::PStringType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralRegularExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['value'],\n      init_hash['pattern'])\n  end\n\n  def self.create(locator, offset, length, value, pattern)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralRegularExpression[value]', attrs['value'].type, value)\n    ta.assert_instance_of('Puppet::AST::LiteralRegularExpression[pattern]', attrs['pattern'].type, pattern)\n    new(locator, offset, length, value, pattern)\n  end\n\n  attr_reader :value\n  attr_reader :pattern\n\n  def initialize(locator, offset, length, value, pattern)\n    super(locator, offset, length)\n    @hash = @hash ^ value.hash ^ pattern.hash\n    @value = value\n    @pattern = pattern\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['value'] = @value\n    result['pattern'] = @pattern\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @value.eql?(o.value) &&\n    @pattern.eql?(o.pattern)\n  end\n  alias == eql?\nend\n\nclass LiteralString < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralString', {\n               'parent' => LiteralValue._pcore_type,\n               'attributes' => {\n                 'value' => Types::PStringType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralString initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['value'])\n  end\n\n  def self.create(locator, offset, length, value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralString[value]', attrs['value'].type, value)\n    new(locator, offset, length, value)\n  end\n\n  attr_reader :value\n\n  def initialize(locator, offset, length, value)\n    super(locator, offset, length)\n    @hash = @hash ^ value.hash\n    @value = value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['value'] = @value\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @value.eql?(o.value)\n  end\n  alias == eql?\nend\n\nclass LiteralNumber < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::LiteralNumber', {\n                                              'parent' => LiteralValue._pcore_type\n                                            })\n  end\nend\n\nclass LiteralInteger < LiteralNumber\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralInteger', {\n               'parent' => LiteralNumber._pcore_type,\n               'attributes' => {\n                 'radix' => {\n                   'type' => Types::PIntegerType::DEFAULT,\n                   'value' => 10\n                 },\n                 'value' => Types::PIntegerType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralInteger initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['value'],\n      init_hash.fetch('radix') { 10 })\n  end\n\n  def self.create(locator, offset, length, value, radix = 10)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralInteger[radix]', attrs['radix'].type, radix)\n    ta.assert_instance_of('Puppet::AST::LiteralInteger[value]', attrs['value'].type, value)\n    new(locator, offset, length, value, radix)\n  end\n\n  attr_reader :radix\n  attr_reader :value\n\n  def initialize(locator, offset, length, value, radix = 10)\n    super(locator, offset, length)\n    @hash = @hash ^ radix.hash ^ value.hash\n    @radix = radix\n    @value = value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['radix'] = @radix unless @radix == 10\n    result['value'] = @value\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @radix.eql?(o.radix) &&\n    @value.eql?(o.value)\n  end\n  alias == eql?\nend\n\nclass LiteralFloat < LiteralNumber\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralFloat', {\n               'parent' => LiteralNumber._pcore_type,\n               'attributes' => {\n                 'value' => Types::PFloatType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralFloat initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['value'])\n  end\n\n  def self.create(locator, offset, length, value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralFloat[value]', attrs['value'].type, value)\n    new(locator, offset, length, value)\n  end\n\n  attr_reader :value\n\n  def initialize(locator, offset, length, value)\n    super(locator, offset, length)\n    @hash = @hash ^ value.hash\n    @value = value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['value'] = @value\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @value.eql?(o.value)\n  end\n  alias == eql?\nend\n\nclass LiteralUndef < Literal\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::LiteralUndef', {\n                                              'parent' => Literal._pcore_type\n                                            })\n  end\nend\n\nclass LiteralDefault < Literal\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::LiteralDefault', {\n                                              'parent' => Literal._pcore_type\n                                            })\n  end\nend\n\nclass LiteralBoolean < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::LiteralBoolean', {\n               'parent' => LiteralValue._pcore_type,\n               'attributes' => {\n                 'value' => Types::PBooleanType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::LiteralBoolean initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['value'])\n  end\n\n  def self.create(locator, offset, length, value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::LiteralBoolean[value]', attrs['value'].type, value)\n    new(locator, offset, length, value)\n  end\n\n  attr_reader :value\n\n  def initialize(locator, offset, length, value)\n    super(locator, offset, length)\n    @hash = @hash ^ value.hash\n    @value = value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['value'] = @value\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @value.eql?(o.value)\n  end\n  alias == eql?\nend\n\nclass TextExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::TextExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass ConcatenatedString < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ConcatenatedString', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'segments' => {\n                   'type' => Types::PArrayType.new(Expression._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ConcatenatedString initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('segments') { _pcore_type['segments'].value })\n  end\n\n  def self.create(locator, offset, length, segments = _pcore_type['segments'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::ConcatenatedString[segments]', attrs['segments'].type, segments)\n    new(locator, offset, length, segments)\n  end\n\n  attr_reader :segments\n\n  def initialize(locator, offset, length, segments = _pcore_type['segments'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ segments.hash\n    @segments = segments\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['segments'] = @segments unless _pcore_type['segments'].default_value?(@segments)\n    result\n  end\n\n  def _pcore_contents\n    @segments.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    @segments.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @segments.eql?(o.segments)\n  end\n  alias == eql?\nend\n\nclass QualifiedName < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::QualifiedName', {\n               'parent' => LiteralValue._pcore_type,\n               'attributes' => {\n                 'value' => Types::PStringType::DEFAULT\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::QualifiedName initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['value'])\n  end\n\n  def self.create(locator, offset, length, value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::QualifiedName[value]', attrs['value'].type, value)\n    new(locator, offset, length, value)\n  end\n\n  attr_reader :value\n\n  def initialize(locator, offset, length, value)\n    super(locator, offset, length)\n    @hash = @hash ^ value.hash\n    @value = value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['value'] = @value\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @value.eql?(o.value)\n  end\n  alias == eql?\nend\n\nclass ReservedWord < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ReservedWord', {\n               'parent' => LiteralValue._pcore_type,\n               'attributes' => {\n                 'word' => Types::PStringType::DEFAULT,\n                 'future' => {\n                   'type' => Types::POptionalType.new(Types::PBooleanType::DEFAULT),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ReservedWord initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['word'],\n      init_hash['future'])\n  end\n\n  def self.create(locator, offset, length, word, future = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::ReservedWord[word]', attrs['word'].type, word)\n    ta.assert_instance_of('Puppet::AST::ReservedWord[future]', attrs['future'].type, future)\n    new(locator, offset, length, word, future)\n  end\n\n  attr_reader :word\n  attr_reader :future\n\n  def initialize(locator, offset, length, word, future = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ word.hash ^ future.hash\n    @word = word\n    @future = future\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['word'] = @word\n    result['future'] = @future unless @future == nil\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @word.eql?(o.word) &&\n    @future.eql?(o.future)\n  end\n  alias == eql?\nend\n\nclass QualifiedReference < LiteralValue\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::QualifiedReference', {\n               'parent' => LiteralValue._pcore_type,\n               'attributes' => {\n                 'cased_value' => Types::PStringType::DEFAULT,\n                 'value' => {\n                   'type' => Types::PStringType::DEFAULT,\n                   'kind' => 'derived'\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::QualifiedReference initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['cased_value'])\n  end\n\n  def self.create(locator, offset, length, cased_value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::QualifiedReference[cased_value]', attrs['cased_value'].type, cased_value)\n    new(locator, offset, length, cased_value)\n  end\n\n  attr_reader :cased_value\n\n  def value\n    @cased_value.downcase\n  end\n\n  def initialize(locator, offset, length, cased_value)\n    super(locator, offset, length)\n    @hash = @hash ^ cased_value.hash\n    @cased_value = cased_value\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['cased_value'] = @cased_value\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @cased_value.eql?(o.cased_value)\n  end\n  alias == eql?\nend\n\nclass VariableExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::VariableExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass EppExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::EppExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'parameters_specified' => {\n                   'type' => Types::POptionalType.new(Types::PBooleanType::DEFAULT),\n                   'value' => nil\n                 },\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::EppExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['parameters_specified'],\n      init_hash['body'])\n  end\n\n  def self.create(locator, offset, length, parameters_specified = nil, body = nil)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::EppExpression[parameters_specified]', attrs['parameters_specified'].type, parameters_specified)\n    ta.assert_instance_of('Puppet::AST::EppExpression[body]', attrs['body'].type, body)\n    new(locator, offset, length, parameters_specified, body)\n  end\n\n  attr_reader :parameters_specified\n  attr_reader :body\n\n  def initialize(locator, offset, length, parameters_specified = nil, body = nil)\n    super(locator, offset, length)\n    @hash = @hash ^ parameters_specified.hash ^ body.hash\n    @parameters_specified = parameters_specified\n    @body = body\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['parameters_specified'] = @parameters_specified unless @parameters_specified == nil\n    result['body'] = @body unless @body == nil\n    result\n  end\n\n  def _pcore_contents\n    yield(@body) unless @body.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @parameters_specified.eql?(o.parameters_specified) &&\n    @body.eql?(o.body)\n  end\n  alias == eql?\nend\n\nclass RenderStringExpression < LiteralString\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::RenderStringExpression', {\n                                              'parent' => LiteralString._pcore_type\n                                            })\n  end\nend\n\nclass RenderExpression < UnaryExpression\n  def self._pcore_type\n    @_pcore_type ||= Types::PObjectType.new('Puppet::AST::RenderExpression', {\n                                              'parent' => UnaryExpression._pcore_type\n                                            })\n  end\n\n  def _pcore_contents\n    yield(@expr) unless @expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @expr.nil?\n      block.call(@expr, path)\n      @expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass ResourceBody < Positioned\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ResourceBody', {\n               'parent' => Positioned._pcore_type,\n               'attributes' => {\n                 'title' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'operations' => {\n                   'type' => Types::PArrayType.new(AbstractAttributeOperation._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ResourceBody initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['title'],\n      init_hash.fetch('operations') { _pcore_type['operations'].value })\n  end\n\n  def self.create(locator, offset, length, title = nil, operations = _pcore_type['operations'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::ResourceBody[title]', attrs['title'].type, title)\n    ta.assert_instance_of('Puppet::AST::ResourceBody[operations]', attrs['operations'].type, operations)\n    new(locator, offset, length, title, operations)\n  end\n\n  attr_reader :title\n  attr_reader :operations\n\n  def initialize(locator, offset, length, title = nil, operations = _pcore_type['operations'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ title.hash ^ operations.hash\n    @title = title\n    @operations = operations\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['title'] = @title unless @title == nil\n    result['operations'] = @operations unless _pcore_type['operations'].default_value?(@operations)\n    result\n  end\n\n  def _pcore_contents\n    yield(@title) unless @title.nil?\n    @operations.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @title.nil?\n      block.call(@title, path)\n      @title._pcore_all_contents(path, &block)\n    end\n    @operations.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @title.eql?(o.title) &&\n    @operations.eql?(o.operations)\n  end\n  alias == eql?\nend\n\nclass AbstractResource < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::AbstractResource', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'form' => {\n                   'type' => Types::PEnumType.new(['exported', 'regular', 'virtual']),\n                   'value' => 'regular'\n                 },\n                 'virtual' => {\n                   'type' => Types::PBooleanType::DEFAULT,\n                   'kind' => 'derived'\n                 },\n                 'exported' => {\n                   'type' => Types::PBooleanType::DEFAULT,\n                   'kind' => 'derived'\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::AbstractResource initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('form') { \"regular\" })\n  end\n\n  def self.create(locator, offset, length, form = \"regular\")\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AbstractResource[form]', attrs['form'].type, form)\n    new(locator, offset, length, form)\n  end\n\n  attr_reader :form\n\n  def virtual\n    @form == 'virtual' || @form == 'exported'\n  end\n\n  def exported\n    @form == 'exported'\n  end\n\n  def initialize(locator, offset, length, form = \"regular\")\n    super(locator, offset, length)\n    @hash = @hash ^ form.hash\n    @form = form\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['form'] = @form unless @form == \"regular\"\n    result\n  end\n\n  def eql?(o)\n    super &&\n    @form.eql?(o.form)\n  end\n  alias == eql?\nend\n\nclass ResourceExpression < AbstractResource\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ResourceExpression', {\n               'parent' => AbstractResource._pcore_type,\n               'attributes' => {\n                 'type_name' => Expression._pcore_type,\n                 'bodies' => {\n                   'type' => Types::PArrayType.new(ResourceBody._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ResourceExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['type_name'],\n      init_hash.fetch('form') { \"regular\" },\n      init_hash.fetch('bodies') { _pcore_type['bodies'].value })\n  end\n\n  def self.create(locator, offset, length, type_name, form = \"regular\", bodies = _pcore_type['bodies'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AbstractResource[form]', attrs['form'].type, form)\n    ta.assert_instance_of('Puppet::AST::ResourceExpression[type_name]', attrs['type_name'].type, type_name)\n    ta.assert_instance_of('Puppet::AST::ResourceExpression[bodies]', attrs['bodies'].type, bodies)\n    new(locator, offset, length, type_name, form, bodies)\n  end\n\n  attr_reader :type_name\n  attr_reader :bodies\n\n  def initialize(locator, offset, length, type_name, form = \"regular\", bodies = _pcore_type['bodies'].value)\n    super(locator, offset, length, form)\n    @hash = @hash ^ type_name.hash ^ bodies.hash\n    @type_name = type_name\n    @bodies = bodies\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['type_name'] = @type_name\n    result['bodies'] = @bodies unless _pcore_type['bodies'].default_value?(@bodies)\n    result\n  end\n\n  def _pcore_contents\n    yield(@type_name) unless @type_name.nil?\n    @bodies.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @type_name.nil?\n      block.call(@type_name, path)\n      @type_name._pcore_all_contents(path, &block)\n    end\n    @bodies.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @type_name.eql?(o.type_name) &&\n    @bodies.eql?(o.bodies)\n  end\n  alias == eql?\nend\n\nclass ResourceDefaultsExpression < AbstractResource\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ResourceDefaultsExpression', {\n               'parent' => AbstractResource._pcore_type,\n               'attributes' => {\n                 'type_ref' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'operations' => {\n                   'type' => Types::PArrayType.new(AbstractAttributeOperation._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ResourceDefaultsExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash.fetch('form') { \"regular\" },\n      init_hash['type_ref'],\n      init_hash.fetch('operations') { _pcore_type['operations'].value })\n  end\n\n  def self.create(locator, offset, length, form = \"regular\", type_ref = nil, operations = _pcore_type['operations'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AbstractResource[form]', attrs['form'].type, form)\n    ta.assert_instance_of('Puppet::AST::ResourceDefaultsExpression[type_ref]', attrs['type_ref'].type, type_ref)\n    ta.assert_instance_of('Puppet::AST::ResourceDefaultsExpression[operations]', attrs['operations'].type, operations)\n    new(locator, offset, length, form, type_ref, operations)\n  end\n\n  attr_reader :type_ref\n  attr_reader :operations\n\n  def initialize(locator, offset, length, form = \"regular\", type_ref = nil, operations = _pcore_type['operations'].value)\n    super(locator, offset, length, form)\n    @hash = @hash ^ type_ref.hash ^ operations.hash\n    @type_ref = type_ref\n    @operations = operations\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['type_ref'] = @type_ref unless @type_ref == nil\n    result['operations'] = @operations unless _pcore_type['operations'].default_value?(@operations)\n    result\n  end\n\n  def _pcore_contents\n    yield(@type_ref) unless @type_ref.nil?\n    @operations.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @type_ref.nil?\n      block.call(@type_ref, path)\n      @type_ref._pcore_all_contents(path, &block)\n    end\n    @operations.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @type_ref.eql?(o.type_ref) &&\n    @operations.eql?(o.operations)\n  end\n  alias == eql?\nend\n\nclass ResourceOverrideExpression < AbstractResource\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::ResourceOverrideExpression', {\n               'parent' => AbstractResource._pcore_type,\n               'attributes' => {\n                 'resources' => Expression._pcore_type,\n                 'operations' => {\n                   'type' => Types::PArrayType.new(AbstractAttributeOperation._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::ResourceOverrideExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['resources'],\n      init_hash.fetch('form') { \"regular\" },\n      init_hash.fetch('operations') { _pcore_type['operations'].value })\n  end\n\n  def self.create(locator, offset, length, resources, form = \"regular\", operations = _pcore_type['operations'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::AbstractResource[form]', attrs['form'].type, form)\n    ta.assert_instance_of('Puppet::AST::ResourceOverrideExpression[resources]', attrs['resources'].type, resources)\n    ta.assert_instance_of('Puppet::AST::ResourceOverrideExpression[operations]', attrs['operations'].type, operations)\n    new(locator, offset, length, resources, form, operations)\n  end\n\n  attr_reader :resources\n  attr_reader :operations\n\n  def initialize(locator, offset, length, resources, form = \"regular\", operations = _pcore_type['operations'].value)\n    super(locator, offset, length, form)\n    @hash = @hash ^ resources.hash ^ operations.hash\n    @resources = resources\n    @operations = operations\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['resources'] = @resources\n    result['operations'] = @operations unless _pcore_type['operations'].default_value?(@operations)\n    result\n  end\n\n  def _pcore_contents\n    yield(@resources) unless @resources.nil?\n    @operations.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @resources.nil?\n      block.call(@resources, path)\n      @resources._pcore_all_contents(path, &block)\n    end\n    @operations.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @resources.eql?(o.resources) &&\n    @operations.eql?(o.operations)\n  end\n  alias == eql?\nend\n\nclass SelectorEntry < Positioned\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::SelectorEntry', {\n               'parent' => Positioned._pcore_type,\n               'attributes' => {\n                 'matching_expr' => Expression._pcore_type,\n                 'value_expr' => Expression._pcore_type\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::SelectorEntry initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['matching_expr'],\n      init_hash['value_expr'])\n  end\n\n  def self.create(locator, offset, length, matching_expr, value_expr)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::SelectorEntry[matching_expr]', attrs['matching_expr'].type, matching_expr)\n    ta.assert_instance_of('Puppet::AST::SelectorEntry[value_expr]', attrs['value_expr'].type, value_expr)\n    new(locator, offset, length, matching_expr, value_expr)\n  end\n\n  attr_reader :matching_expr\n  attr_reader :value_expr\n\n  def initialize(locator, offset, length, matching_expr, value_expr)\n    super(locator, offset, length)\n    @hash = @hash ^ matching_expr.hash ^ value_expr.hash\n    @matching_expr = matching_expr\n    @value_expr = value_expr\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['matching_expr'] = @matching_expr\n    result['value_expr'] = @value_expr\n    result\n  end\n\n  def _pcore_contents\n    yield(@matching_expr) unless @matching_expr.nil?\n    yield(@value_expr) unless @value_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @matching_expr.nil?\n      block.call(@matching_expr, path)\n      @matching_expr._pcore_all_contents(path, &block)\n    end\n    unless @value_expr.nil?\n      block.call(@value_expr, path)\n      @value_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @matching_expr.eql?(o.matching_expr) &&\n    @value_expr.eql?(o.value_expr)\n  end\n  alias == eql?\nend\n\nclass SelectorExpression < Expression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::SelectorExpression', {\n               'parent' => Expression._pcore_type,\n               'attributes' => {\n                 'left_expr' => Expression._pcore_type,\n                 'selectors' => {\n                   'type' => Types::PArrayType.new(SelectorEntry._pcore_type),\n                   'value' => []\n                 }\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::SelectorExpression initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['offset'],\n      init_hash['length'],\n      init_hash['left_expr'],\n      init_hash.fetch('selectors') { _pcore_type['selectors'].value })\n  end\n\n  def self.create(locator, offset, length, left_expr, selectors = _pcore_type['selectors'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Positioned[locator]', attrs['locator'].type, locator)\n    ta.assert_instance_of('Puppet::AST::Positioned[offset]', attrs['offset'].type, offset)\n    ta.assert_instance_of('Puppet::AST::Positioned[length]', attrs['length'].type, length)\n    ta.assert_instance_of('Puppet::AST::SelectorExpression[left_expr]', attrs['left_expr'].type, left_expr)\n    ta.assert_instance_of('Puppet::AST::SelectorExpression[selectors]', attrs['selectors'].type, selectors)\n    new(locator, offset, length, left_expr, selectors)\n  end\n\n  attr_reader :left_expr\n  attr_reader :selectors\n\n  def initialize(locator, offset, length, left_expr, selectors = _pcore_type['selectors'].value)\n    super(locator, offset, length)\n    @hash = @hash ^ left_expr.hash ^ selectors.hash\n    @left_expr = left_expr\n    @selectors = selectors\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['left_expr'] = @left_expr\n    result['selectors'] = @selectors unless _pcore_type['selectors'].default_value?(@selectors)\n    result\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    @selectors.each { |value| yield(value) }\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    @selectors.each do |value|\n      block.call(value, path)\n      value._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @left_expr.eql?(o.left_expr) &&\n    @selectors.eql?(o.selectors)\n  end\n  alias == eql?\nend\n\nclass NamedAccessExpression < BinaryExpression\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::NamedAccessExpression', {\n               'parent' => BinaryExpression._pcore_type\n             })\n  end\n\n  def _pcore_contents\n    yield(@left_expr) unless @left_expr.nil?\n    yield(@right_expr) unless @right_expr.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @left_expr.nil?\n      block.call(@left_expr, path)\n      @left_expr._pcore_all_contents(path, &block)\n    end\n    unless @right_expr.nil?\n      block.call(@right_expr, path)\n      @right_expr._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\nend\n\nclass Program < PopsObject\n  def self._pcore_type\n    @_pcore_type ||=\n      Types::PObjectType\n        .new('Puppet::AST::Program', {\n               'parent' => PopsObject._pcore_type,\n               'attributes' => {\n                 'body' => {\n                   'type' => Types::POptionalType.new(Expression._pcore_type),\n                   'value' => nil\n                 },\n                 'definitions' => {\n                   'type' => Types::PArrayType.new(Definition._pcore_type),\n                   'kind' => 'reference',\n                   'value' => []\n                 },\n                 'source_text' => {\n                   'type' => Types::PStringType::DEFAULT,\n                   'kind' => 'derived'\n                 },\n                 'source_ref' => {\n                   'type' => Types::PStringType::DEFAULT,\n                   'kind' => 'derived'\n                 },\n                 'line_offsets' => {\n                   'type' => Types::PArrayType.new(Types::PIntegerType::DEFAULT),\n                   'kind' => 'derived'\n                 },\n                 'locator' => Parser::Locator::Locator19._pcore_type\n               }\n             })\n  end\n\n  def self.from_hash(init_hash)\n    from_asserted_hash(Types::TypeAsserter.assert_instance_of('Puppet::AST::Program initializer', _pcore_type.init_hash_type, init_hash))\n  end\n\n  def self.from_asserted_hash(init_hash)\n    new(\n      init_hash['locator'],\n      init_hash['body'],\n      init_hash.fetch('definitions') { _pcore_type['definitions'].value })\n  end\n\n  def self.create(locator, body = nil, definitions = _pcore_type['definitions'].value)\n    ta = Types::TypeAsserter\n    attrs = _pcore_type.attributes(true)\n    ta.assert_instance_of('Puppet::AST::Program[body]', attrs['body'].type, body)\n    ta.assert_instance_of('Puppet::AST::Program[definitions]', attrs['definitions'].type, definitions)\n    ta.assert_instance_of('Puppet::AST::Program[locator]', attrs['locator'].type, locator)\n    new(locator, body, definitions)\n  end\n\n  attr_reader :body\n  attr_reader :definitions\n  attr_reader :locator\n\n  def current\n    self\n  end\n\n  def source_text\n    @locator.string\n  end\n\n  def source_ref\n    @locator.file\n  end\n\n  def line_offsets\n    @locator.line_index\n  end\n\n  def initialize(locator, body = nil, definitions = _pcore_type['definitions'].value)\n    super()\n    @hash = @hash ^ body.hash ^ definitions.hash ^ locator.hash\n    @body = body\n    @definitions = definitions\n    @locator = locator\n  end\n\n  def _pcore_init_hash\n    result = super\n    result['body'] = @body unless @body == nil\n    result['definitions'] = @definitions unless _pcore_type['definitions'].default_value?(@definitions)\n    result['locator'] = @locator\n    result\n  end\n\n  def _pcore_contents\n    yield(@body) unless @body.nil?\n    yield(@locator) unless @locator.nil?\n  end\n\n  def _pcore_all_contents(path, &block)\n    path << self\n    unless @body.nil?\n      block.call(@body, path)\n      @body._pcore_all_contents(path, &block)\n    end\n    unless @locator.nil?\n      block.call(@locator, path)\n      @locator._pcore_all_contents(path, &block)\n    end\n    path.pop\n  end\n\n  def eql?(o)\n    super &&\n    @body.eql?(o.body) &&\n    @definitions.eql?(o.definitions) &&\n    @locator.eql?(o.locator)\n  end\n  alias == eql?\nend\nend\n\nmodule Model\n@@pcore_ast_initialized = false\ndef self.register_pcore_types\n  return if @@pcore_ast_initialized\n\n  @@pcore_ast_initialized = true\n  all_types = [\n    Parser::Locator::Locator19,\n    Model::PopsObject,\n    Model::Positioned,\n    Model::Expression,\n    Model::Nop,\n    Model::BinaryExpression,\n    Model::UnaryExpression,\n    Model::ParenthesizedExpression,\n    Model::NotExpression,\n    Model::UnaryMinusExpression,\n    Model::UnfoldExpression,\n    Model::AssignmentExpression,\n    Model::ArithmeticExpression,\n    Model::RelationshipExpression,\n    Model::AccessExpression,\n    Model::ComparisonExpression,\n    Model::MatchExpression,\n    Model::InExpression,\n    Model::BooleanExpression,\n    Model::AndExpression,\n    Model::OrExpression,\n    Model::LiteralList,\n    Model::KeyedEntry,\n    Model::LiteralHash,\n    Model::BlockExpression,\n    Model::ApplyBlockExpression,\n    Model::CaseOption,\n    Model::CaseExpression,\n    Model::QueryExpression,\n    Model::ExportedQuery,\n    Model::VirtualQuery,\n    Model::AbstractAttributeOperation,\n    Model::AttributeOperation,\n    Model::AttributesOperation,\n    Model::CollectExpression,\n    Model::Parameter,\n    Model::Definition,\n    Model::NamedDefinition,\n    Model::FunctionDefinition,\n    Model::ResourceTypeDefinition,\n    Model::QRefDefinition,\n    Model::TypeAlias,\n    Model::TypeMapping,\n    Model::TypeDefinition,\n    Model::NodeDefinition,\n    Model::HeredocExpression,\n    Model::HostClassDefinition,\n    Model::PlanDefinition,\n    Model::LambdaExpression,\n    Model::ApplyExpression,\n    Model::IfExpression,\n    Model::UnlessExpression,\n    Model::CallExpression,\n    Model::CallFunctionExpression,\n    Model::CallNamedFunctionExpression,\n    Model::CallMethodExpression,\n    Model::Literal,\n    Model::LiteralValue,\n    Model::LiteralRegularExpression,\n    Model::LiteralString,\n    Model::LiteralNumber,\n    Model::LiteralInteger,\n    Model::LiteralFloat,\n    Model::LiteralUndef,\n    Model::LiteralDefault,\n    Model::LiteralBoolean,\n    Model::TextExpression,\n    Model::ConcatenatedString,\n    Model::QualifiedName,\n    Model::ReservedWord,\n    Model::QualifiedReference,\n    Model::VariableExpression,\n    Model::EppExpression,\n    Model::RenderStringExpression,\n    Model::RenderExpression,\n    Model::ResourceBody,\n    Model::AbstractResource,\n    Model::ResourceExpression,\n    Model::ResourceDefaultsExpression,\n    Model::ResourceOverrideExpression,\n    Model::SelectorEntry,\n    Model::SelectorExpression,\n    Model::NamedAccessExpression,\n    Model::Program]\n\n  # Create and register a TypeSet that corresponds to all types in the AST model\n  types_map = {}\n  all_types.each do |type|\n    types_map[type._pcore_type.simple_name] = type._pcore_type\n  end\n  type_set = Types::PTypeSetType.new({\n                                       'name' => 'Puppet::AST',\n                                       'pcore_version' => '1.0.0',\n                                       'types' => types_map\n                                     })\n  loc = Puppet::Util.path_to_uri(\"#{__FILE__}\")\n  Loaders.static_loader.set_entry(Loader::TypedName.new(:type, 'puppet::ast', Pcore::RUNTIME_NAME_AUTHORITY), type_set, URI(\"#{loc}?line=1\"))\n  Loaders.register_static_implementations(all_types)\nend\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/ast_transformer.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/parser/ast'\n\n# The receiver of `import(file)` calls; once per imported file, or nil if imports are ignored\n#\n# Transforms a Pops::Model to classic Puppet AST.\n# TODO: Documentation is currently skipped completely (it is only used for Rdoc)\n#\nclass Puppet::Pops::Model::AstTransformer\n  AST = Puppet::Parser::AST\n  Model = Puppet::Pops::Model\n\n  attr_reader :importer\n\n  def initialize(source_file = \"unknown-file\", importer = nil)\n    @@transform_visitor ||= Puppet::Pops::Visitor.new(nil, \"transform\", 0, 0)\n    @@query_transform_visitor ||= Puppet::Pops::Visitor.new(nil, \"query\", 0, 0)\n    @@hostname_transform_visitor ||= Puppet::Pops::Visitor.new(nil, \"hostname\", 0, 0)\n    @importer = importer\n    @source_file = source_file\n  end\n\n  # Initialize klass from o (location) and hash (options to created instance).\n  # The object o is used to compute a source location. It may be nil. Source position is merged into\n  # the given options (non surgically). If o is non-nil, the first found source position going up\n  # the containment hierarchy is set. I.e. callers should pass nil if a source position is not wanted\n  # or known to be unobtainable for the object.\n  #\n  # @param o [Object, nil] object from which source position / location is obtained, may be nil\n  # @param klass [Class<Puppet::Parser::AST>] the ast class to create an instance of\n  # @param hash [Hash] hash with options for the class to create\n  #\n  def ast(o, klass, hash = {})\n    # create and pass hash with file and line information\n    # PUP-3274 - still needed since hostname transformation requires AST::HostName, and AST::Regexp\n    klass.new(**merge_location(hash, o))\n  end\n\n  # THIS IS AN EXPENSIVE OPERATION\n  # The 3x AST requires line, pos etc. to be recorded directly in the AST nodes and this information\n  # must be computed.\n  # (Newer implementation only computes the information that is actually needed; typically when raising an\n  # exception).\n  #\n  def merge_location(hash, o)\n    if o\n      pos = {}\n      locator = o.locator\n      offset = o.is_a?(Model::Program) ? 0 : o.offset\n      pos[:line] = locator.line_for_offset(offset)\n      pos[:pos]  = locator.pos_on_line(offset)\n      pos[:file] = locator.file\n      if nil_or_empty?(pos[:file]) && !nil_or_empty?(@source_file)\n        pos[:file] = @source_file\n      end\n      hash = hash.merge(pos)\n    end\n    hash\n  end\n\n  # Transforms pops expressions into AST 3.1 statements/expressions\n  def transform(o)\n    @@transform_visitor.visit_this_0(self, o)\n  rescue StandardError => e\n    loc_data = {}\n    merge_location(loc_data, o)\n    raise Puppet::ParseError.new(_(\"Error while transforming to Puppet 3 AST: %{message}\") % { message: e.message },\n                                 loc_data[:file], loc_data[:line], loc_data[:pos], e)\n  end\n\n  # Transforms pops expressions into AST 3.1 query expressions\n  def query(o)\n    @@query_transform_visitor.visit_this_0(self, o)\n  end\n\n  # Transforms pops expressions into AST 3.1 hostnames\n  def hostname(o)\n    @@hostname_transform_visitor.visit_this_0(self, o)\n  end\n\n  # Ensures transformation fails if a 3.1 non supported object is encountered in a query expression\n  #\n  def query_Object(o)\n    raise _(\"Not a valid expression in a collection query: %{class_name}\") % { class_name: o.class.name }\n  end\n\n  # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName\n  def hostname_Array(o)\n    o.collect { |x| ast x, AST::HostName, :value => hostname(x) }\n  end\n\n  def hostname_LiteralValue(o)\n    o.value\n  end\n\n  def hostname_QualifiedName(o)\n    o.value\n  end\n\n  def hostname_LiteralNumber(o)\n    transform(o) # Number to string with correct radix\n  end\n\n  def hostname_LiteralDefault(o)\n    'default'\n  end\n\n  def hostname_LiteralRegularExpression(o)\n    ast o, AST::Regex, :value => o.value\n  end\n\n  def hostname_Object(o)\n    raise _(\"Illegal expression - unacceptable as a node name\")\n  end\n\n  def transform_Object(o)\n    raise _(\"Unacceptable transform - found an Object without a rule: %{klass}\") % { klass: o.class }\n  end\n\n  # Nil, nop\n  # Bee bopp a luh-lah, a bop bop boom.\n  #\n  def is_nop?(o)\n    o.nil? || o.is_a?(Model::Nop)\n  end\n\n  def nil_or_empty?(x)\n    x.nil? || x == ''\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/factory.rb",
    "content": "# frozen_string_literal: true\n\n# Factory is a helper class that makes construction of a Pops Model\n# much more convenient. It can be viewed as a small internal DSL for model\n# constructions.\n# For usage see tests using the factory.\n#\n# @todo All those uppercase methods ... they look bad in one way, but stand out nicely in the grammar...\n#   decide if they should change into lower case names (some of the are lower case)...\n#\nmodule Puppet::Pops\nmodule Model\nclass Factory\n  # Shared build_visitor, since there are many instances of Factory being used\n\n  KEY_LENGTH = 'length'\n  KEY_OFFSET = 'offset'\n  KEY_LOCATOR = 'locator'\n  KEY_OPERATOR = 'operator'\n\n  KEY_VALUE = 'value'\n  KEY_KEYS = 'keys'\n  KEY_NAME = 'name'\n  KEY_BODY = 'body'\n  KEY_EXPR = 'expr'\n  KEY_LEFT_EXPR = 'left_expr'\n  KEY_RIGHT_EXPR = 'right_expr'\n  KEY_PARAMETERS = 'parameters'\n\n  BUILD_VISITOR = Visitor.new(self, 'build')\n  INFER_VISITOR = Visitor.new(self, 'infer')\n  INTERPOLATION_VISITOR = Visitor.new(self, 'interpolate')\n  MAPOFFSET_VISITOR = Visitor.new(self, 'map_offset')\n\n  def self.infer(o)\n    if o.instance_of?(Factory)\n      o\n    else\n      new(o)\n    end\n  end\n\n  attr_reader :model_class, :unfolded\n\n  def [](key)\n    @init_hash[key]\n  end\n\n  def []=(key, value)\n    @init_hash[key] = value\n  end\n\n  def all_factories(&block)\n    block.call(self)\n    @init_hash.each_value { |value| value.all_factories(&block) if value.instance_of?(Factory) }\n  end\n\n  def model\n    if @current.nil?\n      # Assign a default Locator if it's missing. Should only happen when the factory is used by other\n      # means than from a parser (e.g. unit tests)\n      unless @init_hash.include?(KEY_LOCATOR)\n        @init_hash[KEY_LOCATOR] = Parser::Locator.locator('<no source>', 'no file')\n        unless @model_class <= Program\n          @init_hash[KEY_OFFSET] = 0\n          @init_hash[KEY_LENGTH] = 0\n        end\n      end\n      @current = create_model\n    end\n    @current\n  end\n\n  # Backward API compatibility\n  alias current model\n\n  def create_model\n    @init_hash.each_pair { |key, elem| @init_hash[key] = factory_to_model(elem) }\n    model_class.from_asserted_hash(@init_hash)\n  end\n\n  # Initialize a factory with a single object, or a class with arguments applied to build of\n  # created instance\n  #\n  def initialize(o, *args)\n    @init_hash = {}\n    if o.instance_of?(Class)\n      @model_class = o\n      BUILD_VISITOR.visit_this_class(self, o, args)\n    else\n      INFER_VISITOR.visit_this(self, o, EMPTY_ARRAY)\n    end\n  end\n\n  def map_offset(model, locator)\n    MAPOFFSET_VISITOR.visit_this_1(self, model, locator)\n  end\n\n  def map_offset_Object(o, locator)\n    o\n  end\n\n  def map_offset_Factory(o, locator)\n    map_offset(o.model, locator)\n  end\n\n  def map_offset_Positioned(o, locator)\n    # Transpose the local offset, length to global \"coordinates\"\n    global_offset, global_length = locator.to_global(o.offset, o.length)\n\n    # mutate\n    o.instance_variable_set(:'@offset', global_offset)\n    o.instance_variable_set(:'@length', global_length)\n    # Change locator since the positions were transposed to the global coordinates\n    o.instance_variable_set(:'@locator', locator.locator) if locator.is_a? Puppet::Pops::Parser::Locator::SubLocator\n  end\n\n  # Polymorphic interpolate\n  def interpolate\n    INTERPOLATION_VISITOR.visit_this_class(self, @model_class, EMPTY_ARRAY)\n  end\n\n  # Building of Model classes\n\n  def build_ArithmeticExpression(o, op, a, b)\n    @init_hash[KEY_OPERATOR] = op\n    build_BinaryExpression(o, a, b)\n  end\n\n  def build_AssignmentExpression(o, op, a, b)\n    @init_hash[KEY_OPERATOR] = op\n    build_BinaryExpression(o, a, b)\n  end\n\n  def build_AttributeOperation(o, name, op, value)\n    @init_hash[KEY_OPERATOR] = op\n    @init_hash['attribute_name'] = name.to_s # BOOLEAN is allowed in the grammar\n    @init_hash['value_expr'] = value\n  end\n\n  def build_AttributesOperation(o, value)\n    @init_hash[KEY_EXPR] = value\n  end\n\n  def build_AccessExpression(o, left, keys)\n    @init_hash[KEY_LEFT_EXPR] = left\n    @init_hash[KEY_KEYS] = keys\n  end\n\n  def build_BinaryExpression(o, left, right)\n    @init_hash[KEY_LEFT_EXPR] = left\n    @init_hash[KEY_RIGHT_EXPR] = right\n  end\n\n  def build_BlockExpression(o, args)\n    @init_hash['statements'] = args\n  end\n\n  def build_EppExpression(o, parameters_specified, body)\n    @init_hash['parameters_specified'] = parameters_specified\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n  end\n\n  # @param rval_required [Boolean] if the call must produce a value\n  def build_CallExpression(o, functor, rval_required, args)\n    @init_hash['functor_expr'] = functor\n    @init_hash['rval_required'] = rval_required\n    @init_hash['arguments'] = args\n  end\n\n  def build_CallMethodExpression(o, functor, rval_required, lambda, args)\n    build_CallExpression(o, functor, rval_required, args)\n    @init_hash['lambda'] = lambda\n  end\n\n  def build_CaseExpression(o, test, args)\n    @init_hash['test'] = test\n    @init_hash['options'] = args\n  end\n\n  def build_CaseOption(o, value_list, then_expr)\n    value_list = [value_list] unless value_list.is_a?(Array)\n    @init_hash['values'] = value_list\n    b = f_build_body(then_expr)\n    @init_hash['then_expr'] = b unless b.nil?\n  end\n\n  def build_CollectExpression(o, type_expr, query_expr, attribute_operations)\n    @init_hash['type_expr'] = type_expr\n    @init_hash['query'] = query_expr\n    @init_hash['operations'] = attribute_operations\n  end\n\n  def build_ComparisonExpression(o, op, a, b)\n    @init_hash[KEY_OPERATOR] = op\n    build_BinaryExpression(o, a, b)\n  end\n\n  def build_ConcatenatedString(o, args)\n    # Strip empty segments\n    @init_hash['segments'] = args.reject { |arg| arg.model_class == LiteralString && arg['value'].empty? }\n  end\n\n  def build_HeredocExpression(o, name, expr)\n    @init_hash['syntax'] = name\n    @init_hash['text_expr'] = expr\n  end\n\n  # @param name [String] a valid classname\n  # @param parameters [Array<Parameter>] may be empty\n  # @param parent_class_name [String, nil] a valid classname referencing a parent class, optional.\n  # @param body [Array<Expression>, Expression, nil] expression that constitute the body\n  # @return [HostClassDefinition] configured from the parameters\n  #\n  def build_HostClassDefinition(o, name, parameters, parent_class_name, body)\n    build_NamedDefinition(o, name, parameters, body)\n    @init_hash['parent_class'] = parent_class_name unless parent_class_name.nil?\n  end\n\n  def build_ResourceOverrideExpression(o, resources, attribute_operations)\n    @init_hash['resources'] = resources\n    @init_hash['operations'] = attribute_operations\n  end\n\n  def build_ReservedWord(o, name, future)\n    @init_hash['word'] = name\n    @init_hash['future'] = future\n  end\n\n  def build_KeyedEntry(o, k, v)\n    @init_hash['key'] = k\n    @init_hash[KEY_VALUE] = v\n  end\n\n  def build_LiteralHash(o, keyed_entries, unfolded)\n    @init_hash['entries'] = keyed_entries\n    @unfolded = unfolded\n  end\n\n  def build_LiteralList(o, values)\n    @init_hash['values'] = values\n  end\n\n  def build_LiteralFloat(o, val)\n    @init_hash[KEY_VALUE] = val\n  end\n\n  def build_LiteralInteger(o, val, radix)\n    @init_hash[KEY_VALUE] = val\n    @init_hash['radix'] = radix\n  end\n\n  def build_LiteralString(o, value)\n    @init_hash[KEY_VALUE] = val\n  end\n\n  def build_IfExpression(o, t, ift, els)\n    @init_hash['test'] = t\n    @init_hash['then_expr'] = ift\n    @init_hash['else_expr'] = els\n  end\n\n  def build_ApplyExpression(o, args, body)\n    @init_hash['arguments'] = args\n    @init_hash['body'] = body\n  end\n\n  def build_MatchExpression(o, op, a, b)\n    @init_hash[KEY_OPERATOR] = op\n    build_BinaryExpression(o, a, b)\n  end\n\n  # Building model equivalences of Ruby objects\n  # Allows passing regular ruby objects to the factory to produce instructions\n  # that when evaluated produce the same thing.\n\n  def infer_String(o)\n    @model_class = LiteralString\n    @init_hash[KEY_VALUE] = o\n  end\n\n  def infer_NilClass(o)\n    @model_class = Nop\n  end\n\n  def infer_TrueClass(o)\n    @model_class = LiteralBoolean\n    @init_hash[KEY_VALUE] = o\n  end\n\n  def infer_FalseClass(o)\n    @model_class = LiteralBoolean\n    @init_hash[KEY_VALUE] = o\n  end\n\n  def infer_Integer(o)\n    @model_class = LiteralInteger\n    @init_hash[KEY_VALUE] = o\n  end\n\n  def infer_Float(o)\n    @model_class = LiteralFloat\n    @init_hash[KEY_VALUE] = o\n  end\n\n  def infer_Regexp(o)\n    @model_class = LiteralRegularExpression\n    @init_hash['pattern'] = o.inspect\n    @init_hash[KEY_VALUE] = o\n  end\n\n  # Creates a String literal, unless the symbol is one of the special :undef, or :default\n  # which instead creates a LiterlUndef, or a LiteralDefault.\n  # Supports :undef because nil creates a no-op instruction.\n  def infer_Symbol(o)\n    case o\n    when :undef\n      @model_class = LiteralUndef\n    when :default\n      @model_class = LiteralDefault\n    else\n      infer_String(o.to_s)\n    end\n  end\n\n  # Creates a LiteralList instruction from an Array, where the entries are built.\n  def infer_Array(o)\n    @model_class = LiteralList\n    @init_hash['values'] = o.map { |e| Factory.infer(e) }\n  end\n\n  # Create a LiteralHash instruction from a hash, where keys and values are built\n  # The hash entries are added in sorted order based on key.to_s\n  #\n  def infer_Hash(o)\n    @model_class = LiteralHash\n    @init_hash['entries'] = o.sort_by { |k, _| k.to_s }.map { |k, v| Factory.new(KeyedEntry, Factory.infer(k), Factory.infer(v)) }\n    @unfolded = false\n  end\n\n  def f_build_body(body)\n    case body\n    when NilClass\n      nil\n    when Array\n      Factory.new(BlockExpression, body)\n    when Factory\n      body\n    else\n      Factory.infer(body)\n    end\n  end\n\n  def build_LambdaExpression(o, parameters, body, return_type)\n    @init_hash[KEY_PARAMETERS] = parameters\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n    @init_hash['return_type'] = return_type unless return_type.nil?\n  end\n\n  def build_NamedDefinition(o, name, parameters, body)\n    @init_hash[KEY_PARAMETERS] = parameters\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n    @init_hash[KEY_NAME] = name\n  end\n\n  def build_FunctionDefinition(o, name, parameters, body, return_type)\n    @init_hash[KEY_PARAMETERS] = parameters\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n    @init_hash[KEY_NAME] = name\n    @init_hash['return_type'] = return_type unless return_type.nil?\n  end\n\n  def build_PlanDefinition(o, name, parameters, body, return_type = nil)\n    @init_hash[KEY_PARAMETERS] = parameters\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n    @init_hash[KEY_NAME] = name\n    @init_hash['return_type'] = return_type unless return_type.nil?\n  end\n\n  def build_NodeDefinition(o, hosts, parent, body)\n    @init_hash['host_matches'] = hosts\n    @init_hash['parent'] = parent unless parent.nil? # no nop here\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n  end\n\n  def build_Parameter(o, name, expr)\n    @init_hash[KEY_NAME] = name\n    @init_hash[KEY_VALUE] = expr\n  end\n\n  def build_QualifiedReference(o, name)\n    @init_hash['cased_value'] = name.to_s\n  end\n\n  def build_RelationshipExpression(o, op, a, b)\n    @init_hash[KEY_OPERATOR] = op\n    build_BinaryExpression(o, a, b)\n  end\n\n  def build_ResourceExpression(o, type_name, bodies)\n    @init_hash['type_name'] = type_name\n    @init_hash['bodies'] = bodies\n  end\n\n  def build_RenderStringExpression(o, string)\n    @init_hash[KEY_VALUE] = string;\n  end\n\n  def build_ResourceBody(o, title_expression, attribute_operations)\n    @init_hash['title'] = title_expression\n    @init_hash['operations'] = attribute_operations\n  end\n\n  def build_ResourceDefaultsExpression(o, type_ref, attribute_operations)\n    @init_hash['type_ref'] = type_ref\n    @init_hash['operations'] = attribute_operations\n  end\n\n  def build_SelectorExpression(o, left, *selectors)\n    @init_hash[KEY_LEFT_EXPR] = left\n    @init_hash['selectors'] = selectors\n  end\n\n  def build_SelectorEntry(o, matching, value)\n    @init_hash['matching_expr'] = matching\n    @init_hash['value_expr'] = value\n  end\n\n  def build_QueryExpression(o, expr)\n    @init_hash[KEY_EXPR] = expr unless Factory.nop?(expr)\n  end\n\n  def build_TypeAlias(o, name, type_expr)\n    if type_expr.model_class <= KeyedEntry\n      # KeyedEntry is used for the form:\n      #\n      #   type Foo = Bar { ... }\n      #\n      # The entry contains Bar => { ... } and must be transformed into:\n      #\n      #   Object[{parent => Bar, ... }]\n      #\n      parent = type_expr['key']\n      hash = type_expr['value']\n      pn = parent['cased_value']\n      unless pn == 'Object' || pn == 'TypeSet'\n        hash['entries'] << Factory.KEY_ENTRY(Factory.QNAME('parent'), parent)\n        parent = Factory.QREF('Object')\n      end\n      type_expr = parent.access([hash])\n    elsif type_expr.model_class <= LiteralHash\n      # LiteralHash is used for the form:\n      #\n      #   type Foo = { ... }\n      #\n      # The hash must be transformed into:\n      #\n      #   Object[{ ... }]\n      #\n      type_expr = Factory.QREF('Object').access([type_expr])\n    end\n    @init_hash['type_expr'] = type_expr\n    @init_hash[KEY_NAME] = name\n  end\n\n  def build_TypeMapping(o, lhs, rhs)\n    @init_hash['type_expr'] = lhs\n    @init_hash['mapping_expr'] = rhs\n  end\n\n  def build_TypeDefinition(o, name, parent, body)\n    b = f_build_body(body)\n    @init_hash[KEY_BODY] = b unless b.nil?\n    @init_hash['parent'] = parent\n    @init_hash[KEY_NAME] = name\n  end\n\n  def build_UnaryExpression(o, expr)\n    @init_hash[KEY_EXPR] = expr unless Factory.nop?(expr)\n  end\n\n  def build_Program(o, body, definitions, locator)\n    @init_hash[KEY_BODY] = body\n    # non containment\n    @init_hash['definitions'] = definitions\n    @init_hash[KEY_LOCATOR] = locator\n  end\n\n  def build_QualifiedName(o, name)\n    @init_hash[KEY_VALUE] = name\n  end\n\n  def build_TokenValue(o)\n    raise \"Factory can not deal with a Lexer Token. Got token: #{o}. Probably caused by wrong index in grammar val[n].\"\n  end\n\n  # Factory helpers\n  def f_build_unary(klazz, expr)\n    Factory.new(klazz, expr)\n  end\n\n  def f_build_binary_op(klazz, op, left, right)\n    Factory.new(klazz, op, left, right)\n  end\n\n  def f_build_binary(klazz, left, right)\n    Factory.new(klazz, left, right)\n  end\n\n  def f_arithmetic(op, r)\n    f_build_binary_op(ArithmeticExpression, op, self, r)\n  end\n\n  def f_comparison(op, r)\n    f_build_binary_op(ComparisonExpression, op, self, r)\n  end\n\n  def f_match(op, r)\n    f_build_binary_op(MatchExpression, op, self, r)\n  end\n\n  # Operator helpers\n  def in(r)     f_build_binary(InExpression, self, r);          end\n\n  def or(r)     f_build_binary(OrExpression, self, r);          end\n\n  def and(r)    f_build_binary(AndExpression, self, r);         end\n\n  def not();    f_build_unary(NotExpression, self);             end\n\n  def minus();  f_build_unary(UnaryMinusExpression, self);      end\n\n  def unfold(); f_build_unary(UnfoldExpression, self);          end\n\n  def text();   f_build_unary(TextExpression, self);            end\n\n  def var();    f_build_unary(VariableExpression, self);        end\n\n  def access(r); f_build_binary(AccessExpression, self, r);     end\n\n  def dot r;    f_build_binary(NamedAccessExpression, self, r); end\n\n  def + r;      f_arithmetic('+', r);                           end\n\n  def - r;      f_arithmetic('-', r);                           end\n\n  def / r;      f_arithmetic('/', r);                           end\n\n  def * r;      f_arithmetic('*', r);                           end\n\n  def % r;      f_arithmetic('%', r);                           end\n\n  def << r;     f_arithmetic('<<', r);                          end\n\n  def >> r;     f_arithmetic('>>', r);                          end\n\n  def < r;      f_comparison('<', r);                           end\n\n  def <= r;     f_comparison('<=', r);                          end\n\n  def > r;      f_comparison('>', r);                           end\n\n  def >= r;     f_comparison('>=', r);                          end\n\n  def eq r;     f_comparison('==', r);                          end\n\n  def ne r;     f_comparison('!=', r);                          end\n\n  def =~ r;     f_match('=~', r);                               end\n\n  def mne r;    f_match('!~', r);                               end\n\n  def paren;    f_build_unary(ParenthesizedExpression, self);   end\n\n  def relop(op, r)\n    f_build_binary_op(RelationshipExpression, op, self, r)\n  end\n\n  def select(*args)\n    Factory.new(SelectorExpression, self, *args)\n  end\n\n  # Same as access, but with varargs and arguments that must be inferred. For testing purposes\n  def access_at(*r)\n    f_build_binary(AccessExpression, self, r.map { |arg| Factory.infer(arg) })\n  end\n\n  # For CaseExpression, setting the default for an already build CaseExpression\n  def default(r)\n    @init_hash['options'] << Factory.WHEN(Factory.infer(:default), r)\n    self\n  end\n\n  def lambda=(lambda)\n    @init_hash['lambda'] = lambda\n  end\n\n  # Assignment =\n  def set(r)\n    f_build_binary_op(AssignmentExpression, '=', self, r)\n  end\n\n  # Assignment +=\n  def plus_set(r)\n    f_build_binary_op(AssignmentExpression, '+=', self, r)\n  end\n\n  # Assignment -=\n  def minus_set(r)\n    f_build_binary_op(AssignmentExpression, '-=', self, r)\n  end\n\n  def attributes(*args)\n    @init_hash['attributes'] = args\n    self\n  end\n\n  def offset\n    @init_hash[KEY_OFFSET]\n  end\n\n  def length\n    @init_hash[KEY_LENGTH]\n  end\n\n  # Records the position (start -> end) and computes the resulting length.\n  #\n  def record_position(locator, start_locatable, end_locatable)\n    # record information directly in the Positioned object\n    start_offset = start_locatable.offset\n    @init_hash[KEY_LOCATOR] = locator\n    @init_hash[KEY_OFFSET] = start_offset\n    @init_hash[KEY_LENGTH] = end_locatable.nil? ? start_locatable.length : end_locatable.offset + end_locatable.length - start_offset\n    self\n  end\n\n  # Sets the form of the resource expression (:regular (the default), :virtual, or :exported).\n  # Produces true if the expression was a resource expression, false otherwise.\n  #\n  def self.set_resource_form(expr, form)\n    # Note: Validation handles illegal combinations\n    return false unless expr.instance_of?(self) && expr.model_class <= AbstractResource\n\n    expr['form'] = form\n    true\n  end\n\n  # Returns symbolic information about an expected shape of a resource expression given the LHS of a resource expr.\n  #\n  # * `name { }` => `:resource`,  create a resource of the given type\n  # * `Name { }` => ':defaults`, set defaults for the referenced type\n  # * `Name[] { }` => `:override`, overrides instances referenced by LHS\n  # * _any other_ => ':error', all other are considered illegal\n  #\n  def self.resource_shape(expr)\n    if expr == 'class'\n      :class\n    elsif expr.instance_of?(self)\n      mc = expr.model_class\n      if mc <= QualifiedName\n        :resource\n      elsif mc <= QualifiedReference\n        :defaults\n      elsif mc <= AccessExpression\n        # if Resource[e], then it is not resource specific\n        lhs = expr[KEY_LEFT_EXPR]\n        if lhs.model_class <= QualifiedReference && lhs[KEY_VALUE] == 'resource' && expr[KEY_KEYS].size == 1\n          :defaults\n        else\n          :override\n        end\n      else\n        :error\n      end\n    else\n      :error\n    end\n  end\n\n  # Factory starting points\n\n  def self.literal(o);                   infer(o);                                       end\n\n  def self.minus(o);                     infer(o).minus;                                 end\n\n  def self.unfold(o);                    infer(o).unfold;                                end\n\n  def self.var(o);                       infer(o).var;                                   end\n\n  def self.block(*args);                 new(BlockExpression, args.map { |arg| infer(arg) }); end\n\n  def self.string(*args);                new(ConcatenatedString, args.map { |arg| infer(arg) }); end\n\n  def self.text(o);                      infer(o).text; end\n\n  def self.IF(test_e, then_e, else_e);     new(IfExpression, test_e, then_e, else_e);      end\n\n  def self.UNLESS(test_e, then_e, else_e); new(UnlessExpression, test_e, then_e, else_e);  end\n\n  def self.CASE(test_e, *options); new(CaseExpression, test_e, options); end\n\n  def self.WHEN(values_list, block);     new(CaseOption, values_list, block);            end\n\n  def self.MAP(match, value);            new(SelectorEntry, match, value);               end\n\n  def self.KEY_ENTRY(key, val);          new(KeyedEntry, key, val);                      end\n\n  def self.HASH(entries);                new(LiteralHash, entries, false);               end\n\n  def self.HASH_UNFOLDED(entries);       new(LiteralHash, entries, true);                end\n\n  def self.HEREDOC(name, expr);          new(HeredocExpression, name, expr);             end\n\n  def self.STRING(*args);                new(ConcatenatedString, args);                  end\n\n  def self.LIST(entries);                new(LiteralList, entries);                      end\n\n  def self.PARAM(name, expr = nil); new(Parameter, name, expr); end\n\n  def self.NODE(hosts, parent, body); new(NodeDefinition, hosts, parent, body); end\n\n  # Parameters\n\n  # Mark parameter as capturing the rest of arguments\n  def captures_rest\n    @init_hash['captures_rest'] = true\n  end\n\n  # Set Expression that should evaluate to the parameter's type\n  def type_expr(o)\n    @init_hash['type_expr'] = o\n  end\n\n  # Creates a QualifiedName representation of o, unless o already represents a QualifiedName in which\n  # case it is returned.\n  #\n  def self.fqn(o)\n    o.instance_of?(Factory) && o.model_class <= QualifiedName ? self : new(QualifiedName, o)\n  end\n\n  # Creates a QualifiedName representation of o, unless o already represents a QualifiedName in which\n  # case it is returned.\n  #\n  def self.fqr(o)\n    o.instance_of?(Factory) && o.model_class <= QualifiedReference ? self : new(QualifiedReference, o)\n  end\n\n  def self.SUBLOCATE(token, expr_factory)\n    # expr is a Factory wrapped LiteralString, or ConcatenatedString\n    # The token is SUBLOCATED token which has a SubLocator as the token's locator\n    # Use the SubLocator to recalculate the offsets and lengths.\n    model = expr_factory.model\n    locator = token.locator\n    expr_factory.map_offset(model, locator)\n    model._pcore_all_contents([]) { |element| expr_factory.map_offset(element, locator) }\n\n    # Returned the factory wrapping the now offset/length transformed expression(s)\n    expr_factory\n  end\n\n  def self.TEXT(expr)\n    new(TextExpression, infer(expr).interpolate)\n  end\n\n  # TODO_EPP\n  def self.RENDER_STRING(o)\n    new(RenderStringExpression, o)\n  end\n\n  def self.RENDER_EXPR(expr)\n    new(RenderExpression, expr)\n  end\n\n  def self.EPP(parameters, body)\n    if parameters.nil?\n      params = []\n      parameters_specified = false\n    else\n      params = parameters\n      parameters_specified = true\n    end\n    LAMBDA(params, new(EppExpression, parameters_specified, body), nil)\n  end\n\n  def self.RESERVED(name, future = false)\n    new(ReservedWord, name, future)\n  end\n\n  # TODO: This is the same a fqn factory method, don't know if callers to fqn and QNAME can live with the\n  # same result or not yet - refactor into one method when decided.\n  #\n  def self.QNAME(name)\n    new(QualifiedName, name)\n  end\n\n  def self.NUMBER(name_or_numeric)\n    n_radix = Utils.to_n_with_radix(name_or_numeric)\n    if n_radix\n      val, radix = n_radix\n      if val.is_a?(Float)\n        new(LiteralFloat, val)\n      else\n        new(LiteralInteger, val, radix)\n      end\n    else\n      # Bad number should already have been caught by lexer - this should never happen\n      # TRANSLATORS 'NUMBER' refers to a method name and the 'name_or_numeric' was the passed in value and should not be translated\n      raise ArgumentError, _(\"Internal Error, NUMBER token does not contain a valid number, %{name_or_numeric}\") %\n                           { name_or_numeric: name_or_numeric }\n    end\n  end\n\n  # Convert input string to either a qualified name, a LiteralInteger with radix, or a LiteralFloat\n  #\n  def self.QNAME_OR_NUMBER(name)\n    n_radix = Utils.to_n_with_radix(name)\n    if n_radix\n      val, radix = n_radix\n      if val.is_a?(Float)\n        new(LiteralFloat, val)\n      else\n        new(LiteralInteger, val, radix)\n      end\n    else\n      new(QualifiedName, name)\n    end\n  end\n\n  def self.QREF(name)\n    new(QualifiedReference, name)\n  end\n\n  def self.VIRTUAL_QUERY(query_expr)\n    new(VirtualQuery, query_expr)\n  end\n\n  def self.EXPORTED_QUERY(query_expr)\n    new(ExportedQuery, query_expr)\n  end\n\n  def self.ARGUMENTS(args, arg)\n    if !args.empty? && arg.model_class <= LiteralHash && arg.unfolded\n      last = args[args.size() - 1]\n      if last.model_class <= LiteralHash && last.unfolded\n        last['entries'].concat(arg['entries'])\n        return args\n      end\n    end\n    args.push(arg)\n  end\n\n  def self.ATTRIBUTE_OP(name, op, expr)\n    new(AttributeOperation, name, op, expr)\n  end\n\n  def self.ATTRIBUTES_OP(expr)\n    new(AttributesOperation, expr)\n  end\n\n  # Same as CALL_NAMED but with inference and varargs (for testing purposes)\n  def self.call_named(name, rval_required, *argument_list)\n    new(CallNamedFunctionExpression, fqn(name), rval_required, argument_list.map { |arg| infer(arg) })\n  end\n\n  def self.CALL_NAMED(name, rval_required, argument_list)\n    new(CallNamedFunctionExpression, name, rval_required, argument_list)\n  end\n\n  def self.CALL_METHOD(functor, argument_list)\n    new(CallMethodExpression, functor, true, nil, argument_list)\n  end\n\n  def self.COLLECT(type_expr, query_expr, attribute_operations)\n    new(CollectExpression, type_expr, query_expr, attribute_operations)\n  end\n\n  def self.NAMED_ACCESS(type_name, bodies)\n    new(NamedAccessExpression, type_name, bodies)\n  end\n\n  def self.RESOURCE(type_name, bodies)\n    new(ResourceExpression, type_name, bodies)\n  end\n\n  def self.RESOURCE_DEFAULTS(type_name, attribute_operations)\n    new(ResourceDefaultsExpression, type_name, attribute_operations)\n  end\n\n  def self.RESOURCE_OVERRIDE(resource_ref, attribute_operations)\n    new(ResourceOverrideExpression, resource_ref, attribute_operations)\n  end\n\n  def self.RESOURCE_BODY(resource_title, attribute_operations)\n    new(ResourceBody, resource_title, attribute_operations)\n  end\n\n  def self.PROGRAM(body, definitions, locator)\n    new(Program, body, definitions, locator)\n  end\n\n  # Builds a BlockExpression if args size > 1, else the single expression/value in args\n  def self.block_or_expression(args, left_brace = nil, right_brace = nil)\n    if args.size > 1\n      block_expr = new(BlockExpression, args)\n\n      # If given a left and right brace position, use those\n      # otherwise use the first and last element of the block\n      if !left_brace.nil? && !right_brace.nil?\n        block_expr.record_position(args.first[KEY_LOCATOR], left_brace, right_brace)\n      else\n        block_expr.record_position(args.first[KEY_LOCATOR], args.first, args.last)\n      end\n\n      block_expr\n    else\n      args[0]\n    end\n  end\n\n  def self.HOSTCLASS(name, parameters, parent, body)\n    new(HostClassDefinition, name, parameters, parent, body)\n  end\n\n  def self.DEFINITION(name, parameters, body)\n    new(ResourceTypeDefinition, name, parameters, body)\n  end\n\n  def self.PLAN(name, parameters, body)\n    new(PlanDefinition, name, parameters, body, nil)\n  end\n\n  def self.APPLY(arguments, body)\n    new(ApplyExpression, arguments, body)\n  end\n\n  def self.APPLY_BLOCK(statements)\n    new(ApplyBlockExpression, statements)\n  end\n\n  def self.FUNCTION(name, parameters, body, return_type)\n    new(FunctionDefinition, name, parameters, body, return_type)\n  end\n\n  def self.LAMBDA(parameters, body, return_type)\n    new(LambdaExpression, parameters, body, return_type)\n  end\n\n  def self.TYPE_ASSIGNMENT(lhs, rhs)\n    if lhs.model_class <= AccessExpression\n      new(TypeMapping, lhs, rhs)\n    else\n      new(TypeAlias, lhs['cased_value'], rhs)\n    end\n  end\n\n  def self.TYPE_DEFINITION(name, parent, body)\n    new(TypeDefinition, name, parent, body)\n  end\n\n  def self.nop? o\n    o.nil? || o.instance_of?(Factory) && o.model_class <= Nop\n  end\n\n  STATEMENT_CALLS = {\n    'require' => true,\n    'realize' => true,\n    'include' => true,\n    'contain' => true,\n    'tag' => true,\n\n    'debug' => true,\n    'info' => true,\n    'notice' => true,\n    'warning' => true,\n    'err' => true,\n\n    'fail' => true,\n    'import' => true, # discontinued, but transform it to make it call error reporting function\n    'break' => true,\n    'next' => true,\n    'return' => true\n  }.freeze\n\n  # Returns true if the given name is a \"statement keyword\" (require, include, contain,\n  # error, notice, info, debug\n  #\n  def self.name_is_statement?(name)\n    STATEMENT_CALLS.include?(name)\n  end\n\n  class ArgsToNonCallError < RuntimeError\n    attr_reader :args, :name_expr\n\n    def initialize(args, name_expr)\n      @args = args\n      @name_expr = name_expr\n    end\n  end\n\n  # Transforms an array of expressions containing literal name expressions to calls if followed by an\n  # expression, or expression list.\n  #\n  def self.transform_calls(expressions)\n    expressions.each_with_object([]) do |expr, memo|\n      name = memo[-1]\n      if name.instance_of?(Factory) && name.model_class <= QualifiedName && name_is_statement?(name[KEY_VALUE])\n        if expr.is_a?(Array)\n          expr = expr.reject { |e| e.is_a?(Parser::LexerSupport::TokenValue) }\n        else\n          expr = [expr]\n        end\n        the_call = self.CALL_NAMED(name, false, expr)\n        # last positioned is last arg if there are several\n        the_call.record_position(name[KEY_LOCATOR], name, expr[-1])\n        memo[-1] = the_call\n        if expr.is_a?(CallNamedFunctionExpression)\n          # Patch statement function call to expression style\n          # This is needed because it is first parsed as a \"statement\" and the requirement changes as it becomes\n          # an argument to the name to call transform above.\n          expr.rval_required = true\n        end\n      elsif expr.is_a?(Array)\n        raise ArgsToNonCallError.new(expr, name)\n      else\n        memo << expr\n        if expr.model_class <= CallNamedFunctionExpression\n          # Patch rvalue expression function call to statement style.\n          # This is not really required but done to be AST model compliant\n          expr['rval_required'] = false\n        end\n      end\n    end\n  end\n\n  # Transforms a left expression followed by an untitled resource (in the form of attribute_operations)\n  # @param left [Factory, Expression] the lhs followed what may be a hash\n  def self.transform_resource_wo_title(left, attribute_ops, lbrace_token, rbrace_token)\n    # Returning nil means accepting the given as a potential resource expression\n    return nil unless attribute_ops.is_a? Array\n    return nil unless left.model_class <= QualifiedName\n\n    keyed_entries = attribute_ops.map do |ao|\n      return nil if ao[KEY_OPERATOR] == '+>'\n\n      KEY_ENTRY(infer(ao['attribute_name']), ao['value_expr'])\n    end\n    a_hash = HASH(keyed_entries)\n    a_hash.record_position(left[KEY_LOCATOR], lbrace_token, rbrace_token)\n    block_or_expression(transform_calls([left, a_hash]))\n  end\n\n  def interpolate_Factory(c)\n    self\n  end\n\n  def interpolate_LiteralInteger(c)\n    # convert number to a variable\n    var\n  end\n\n  def interpolate_Object(c)\n    self\n  end\n\n  def interpolate_QualifiedName(c)\n    var\n  end\n\n  # rewrite left expression to variable if it is name, number, and recurse if it is an access expression\n  # this is for interpolation support in new lexer (${NAME}, ${NAME[}}, ${NUMBER}, ${NUMBER[]} - all\n  # other expressions requires variables to be preceded with $\n  #\n  def interpolate_AccessExpression(c)\n    lhs = @init_hash[KEY_LEFT_EXPR]\n    if is_interop_rewriteable?(lhs)\n      @init_hash[KEY_LEFT_EXPR] = lhs.interpolate\n    end\n    self\n  end\n\n  def interpolate_NamedAccessExpression(c)\n    lhs = @init_hash[KEY_LEFT_EXPR]\n    if is_interop_rewriteable?(lhs)\n      @init_hash[KEY_LEFT_EXPR] = lhs.interpolate\n    end\n    self\n  end\n\n  # Rewrite method calls on the form ${x.each ...} to ${$x.each}\n  def interpolate_CallMethodExpression(c)\n    functor_expr = @init_hash['functor_expr']\n    if is_interop_rewriteable?(functor_expr)\n      @init_hash['functor_expr'] = functor_expr.interpolate\n    end\n    self\n  end\n\n  def is_interop_rewriteable?(o)\n    mc = o.model_class\n    if mc <= AccessExpression || mc <= QualifiedName || mc <= NamedAccessExpression || mc <= CallMethodExpression\n      true\n    elsif mc <= LiteralInteger\n      # Only decimal integers can represent variables, else it is a number\n      o['radix'] == 10\n    else\n      false\n    end\n  end\n\n  def self.concat(*args)\n    result = ''.dup\n    args.each do |e|\n      if e.instance_of?(Factory) && e.model_class <= LiteralString\n        result << e[KEY_VALUE]\n      elsif e.is_a?(String)\n        result << e\n      else\n        raise ArgumentError, _(\"can only concatenate strings, got %{class_name}\") % { class_name: e.class }\n      end\n    end\n    infer(result)\n  end\n\n  def to_s\n    \"Factory for #{@model_class}\"\n  end\n\n  def factory_to_model(value)\n    if value.instance_of?(Factory)\n      value.contained_current(self)\n    elsif value.instance_of?(Array)\n      value.each_with_index { |el, idx| value[idx] = el.contained_current(self) if el.instance_of?(Factory) }\n    else\n      value\n    end\n  end\n\n  def contained_current(container)\n    if @current.nil?\n      unless @init_hash.include?(KEY_LOCATOR)\n        @init_hash[KEY_LOCATOR] = container[KEY_LOCATOR]\n        @init_hash[KEY_OFFSET] = container[KEY_OFFSET] || 0\n        @init_hash[KEY_LENGTH] = 0\n      end\n      @current = create_model\n    end\n    @current\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/model_label_provider.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Model\n# A provider of labels for model object, producing a human name for the model object.\n# As an example, if object is an ArithmeticExpression with operator +, `#a_an(o)` produces \"a '+' Expression\",\n# #the(o) produces \"the + Expression\", and #label produces \"+ Expression\".\n#\nclass ModelLabelProvider\n  include LabelProvider\n\n  def initialize\n    @@label_visitor ||= Visitor.new(self, \"label\", 0, 0)\n  end\n\n  # Produces a label for the given objects type/operator without article.\n  # If a Class is given, its name is used as label\n  #\n  def label o\n    @@label_visitor.visit_this_0(self, o)\n  end\n\n  # rubocop:disable Layout/SpaceBeforeSemicolon\n  def label_Factory o                     ; label(o.model)                      end\n  def label_Array o                       ; \"Array\"                             end\n  def label_LiteralInteger o              ; \"Literal Integer\"                   end\n  def label_LiteralFloat o                ; \"Literal Float\"                     end\n  def label_ArithmeticExpression o        ; \"'#{o.operator}' expression\"        end\n  def label_AccessExpression o            ; \"'[]' expression\"                   end\n  def label_MatchExpression o             ; \"'#{o.operator}' expression\"        end\n  def label_CollectExpression o           ; label(o.query)                      end\n  def label_EppExpression o               ; \"Epp Template\"                      end\n  def label_ExportedQuery o               ; \"Exported Query\"                    end\n  def label_VirtualQuery o                ; \"Virtual Query\"                     end\n  def label_QueryExpression o             ; \"Collect Query\"                     end\n  def label_ComparisonExpression o        ; \"'#{o.operator}' expression\"        end\n  def label_AndExpression o               ; \"'and' expression\"                  end\n  def label_OrExpression o                ; \"'or' expression\"                   end\n  def label_InExpression o                ; \"'in' expression\"                   end\n  def label_AssignmentExpression o        ; \"'#{o.operator}' expression\"        end\n  def label_AttributeOperation o          ; \"'#{o.operator}' expression\"        end\n  def label_LiteralList o                 ; \"Array Expression\"                  end\n  def label_LiteralHash o                 ; \"Hash Expression\"                   end\n  def label_KeyedEntry o                  ; \"Hash Entry\"                        end\n  def label_LiteralBoolean o              ; \"Boolean\"                           end\n  def label_TrueClass o                   ; \"Boolean\"                           end\n  def label_FalseClass o                  ; \"Boolean\"                           end\n  def label_LiteralString o               ; \"String\"                            end\n  def label_LambdaExpression o            ; \"Lambda\"                            end\n  def label_LiteralDefault o              ; \"'default' expression\"              end\n  def label_LiteralUndef o                ; \"'undef' expression\"                end\n  def label_LiteralRegularExpression o    ; \"Regular Expression\"                end\n  def label_Nop o                         ; \"Nop Expression\"                    end\n  def label_NamedAccessExpression o       ; \"'.' expression\"                    end\n  def label_NilClass o                    ; \"Undef Value\"                       end\n  def label_NotExpression o               ; \"'not' expression\"                  end\n  def label_VariableExpression o          ; \"Variable\"                          end\n  def label_TextExpression o              ; \"Expression in Interpolated String\" end\n  def label_UnaryMinusExpression o        ; \"Unary Minus\"                       end\n  def label_UnfoldExpression o            ; \"Unfold\"                            end\n  def label_BlockExpression o             ; \"Block Expression\"                  end\n  def label_ApplyBlockExpression o        ; \"Apply Block Expression\"            end\n  def label_ConcatenatedString o          ; \"Double Quoted String\"              end\n  def label_HeredocExpression o           ; \"'@(#{o.syntax})' expression\"       end\n  def label_HostClassDefinition o         ; \"Host Class Definition\"             end\n  def label_FunctionDefinition o          ; \"Function Definition\"               end\n  def label_PlanDefinition o              ; \"Plan Definition\"                   end\n  def label_NodeDefinition o              ; \"Node Definition\"                   end\n  def label_ResourceTypeDefinition o      ; \"'define' expression\"               end\n  def label_ResourceOverrideExpression o  ; \"Resource Override\"                 end\n  def label_Parameter o                   ; \"Parameter Definition\"              end\n  def label_ParenthesizedExpression o     ; \"Parenthesized Expression\"          end\n  def label_IfExpression o                ; \"'if' statement\"                    end\n  def label_UnlessExpression o            ; \"'unless' Statement\"                end\n  def label_CallNamedFunctionExpression o ; \"Function Call\"                     end\n  def label_CallMethodExpression o        ; \"Method call\"                       end\n  def label_ApplyExpression o             ; \"'apply' expression\"                end\n  def label_CaseExpression o              ; \"'case' statement\"                  end\n  def label_CaseOption o                  ; \"Case Option\"                       end\n  def label_RenderStringExpression o      ; \"Epp Text\"                          end\n  def label_RenderExpression o            ; \"Epp Interpolated Expression\"       end\n  def label_RelationshipExpression o      ; \"'#{o.operator}' expression\"        end\n  def label_ResourceBody o                ; \"Resource Instance Definition\"      end\n  def label_ResourceDefaultsExpression o  ; \"Resource Defaults Expression\"      end\n  def label_ResourceExpression o          ; \"Resource Statement\"                end\n  def label_SelectorExpression o          ; \"Selector Expression\"               end\n  def label_SelectorEntry o               ; \"Selector Option\"                   end\n  def label_Integer o                     ; \"Integer\"                           end\n  def label_Float o                       ; \"Float\"                             end\n  def label_String o                      ; \"String\"                            end\n  def label_Regexp o                      ; \"Regexp\"                            end\n  def label_Object o                      ; \"Object\"                            end\n  def label_Hash o                        ; \"Hash\"                              end\n  def label_QualifiedName o               ; \"Name\"                              end\n  def label_QualifiedReference o          ; \"Type-Name\"                         end\n  def label_PAnyType o                    ; \"#{o}-Type\" end\n  def label_ReservedWord o                ; \"Reserved Word '#{o.word}'\"         end\n  def label_CatalogCollector o            ; \"Catalog-Collector\"                 end\n  def label_ExportedCollector o           ; \"Exported-Collector\"                end\n  def label_TypeAlias o                   ; \"Type Alias\"                        end\n  def label_TypeMapping o                 ; \"Type Mapping\"                      end\n  def label_TypeDefinition o              ; \"Type Definition\"                   end\n  def label_Binary o                      ; \"Binary\"                            end\n  def label_Sensitive o                   ; \"Sensitive\"                         end\n  def label_Timestamp o                   ; \"Timestamp\"                         end\n  def label_Timespan o                    ; \"Timespan\"                          end\n  def label_Version o                     ; \"Semver\"                            end\n  def label_VersionRange o                ; \"SemverRange\"                       end\n  # rubocop:enable Layout/SpaceBeforeSemicolon\n\n  def label_PResourceType o\n    if o.title\n      \"#{o} Resource-Reference\"\n    else\n      \"#{o}-Type\"\n    end\n  end\n\n  def label_Resource o\n    'Resource Statement'\n  end\n\n  def label_Class o\n    if o <= Types::PAnyType\n      simple_name = o.name.split('::').last\n      simple_name[1..-5] + \"-Type\"\n    else\n      n = o.name\n      if n.nil?\n        n = o.respond_to?(:_pcore_type) ? o._pcore_type.name : 'Anonymous Class'\n      end\n      n\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/model_tree_dumper.rb",
    "content": "# frozen_string_literal: true\n\n# Dumps a Pops::Model in reverse polish notation; i.e. LISP style\n# The intention is to use this for debugging output\n# TODO: BAD NAME - A DUMP is a Ruby Serialization\n#\nclass Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper\n  def dump_Array o\n    o.collect { |e| do_dump(e) }\n  end\n\n  def dump_LiteralFloat o\n    o.value.to_s\n  end\n\n  def dump_LiteralInteger o\n    case o.radix\n    when 10\n      o.value.to_s\n    when 8\n      \"0%o\" % o.value\n    when 16\n      \"0x%X\" % o.value\n    else\n      \"bad radix:\" + o.value.to_s\n    end\n  end\n\n  def dump_LiteralValue o\n    o.value.to_s\n  end\n\n  def dump_QualifiedReference o\n    o.cased_value.to_s\n  end\n\n  def dump_Factory o\n    o['locator'] ||= Puppet::Pops::Parser::Locator.locator(\"<not from source>\", nil)\n    do_dump(o.model)\n  end\n\n  def dump_ArithmeticExpression o\n    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  # x[y] prints as (slice x y)\n  def dump_AccessExpression o\n    if o.keys.size <= 1\n      [\"slice\", do_dump(o.left_expr), do_dump(o.keys[0])]\n    else\n      [\"slice\", do_dump(o.left_expr), do_dump(o.keys)]\n    end\n  end\n\n  def dump_MatchesExpression o\n    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_CollectExpression o\n    result = [\"collect\", do_dump(o.type_expr), :indent, :break, do_dump(o.query), :indent]\n    o.operations do |ao|\n      result << :break << do_dump(ao)\n    end\n    result += [:dedent, :dedent]\n    result\n  end\n\n  def dump_EppExpression o\n    result = [\"epp\"]\n    #    result << [\"parameters\"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_ExportedQuery o\n    result = [\"<<| |>>\"]\n    result += dump_QueryExpression(o) unless is_nop?(o.expr)\n    result\n  end\n\n  def dump_VirtualQuery o\n    result = [\"<| |>\"]\n    result += dump_QueryExpression(o) unless is_nop?(o.expr)\n    result\n  end\n\n  def dump_QueryExpression o\n    [do_dump(o.expr)]\n  end\n\n  def dump_ComparisonExpression o\n    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_AndExpression o\n    [\"&&\", do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_OrExpression o\n    [\"||\", do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_InExpression o\n    [\"in\", do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_AssignmentExpression o\n    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  # Produces (name => expr) or (name +> expr)\n  def dump_AttributeOperation o\n    [o.attribute_name, o.operator, do_dump(o.value_expr)]\n  end\n\n  def dump_AttributesOperation o\n    ['* =>', do_dump(o.expr)]\n  end\n\n  def dump_LiteralList o\n    [\"[]\"] + o.values.collect { |x| do_dump(x) }\n  end\n\n  def dump_LiteralHash o\n    [\"{}\"] + o.entries.collect { |x| do_dump(x) }\n  end\n\n  def dump_KeyedEntry o\n    [do_dump(o.key), do_dump(o.value)]\n  end\n\n  def dump_MatchExpression o\n    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_LiteralString o\n    \"'#{o.value}'\"\n  end\n\n  def dump_LambdaExpression o\n    result = [\"lambda\"]\n    result << [\"parameters\"] + o.parameters.collect { |p| do_dump(p) } if o.parameters.size() > 0\n    result << ['return_type', do_dump(o.return_type)] unless o.return_type.nil?\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_LiteralDefault o\n    \":default\"\n  end\n\n  def dump_LiteralUndef o\n    \":undef\"\n  end\n\n  def dump_LiteralRegularExpression o\n    Puppet::Pops::Types::StringConverter.convert(o.value, '%p')\n  end\n\n  def dump_Nop o\n    \":nop\"\n  end\n\n  def dump_NamedAccessExpression o\n    [\".\", do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_NilClass o\n    \"()\"\n  end\n\n  def dump_NotExpression o\n    ['!', dump(o.expr)]\n  end\n\n  def dump_VariableExpression o\n    \"$#{dump(o.expr)}\"\n  end\n\n  # Interpolation (to string) shown as (str expr)\n  def dump_TextExpression o\n    [\"str\", do_dump(o.expr)]\n  end\n\n  def dump_UnaryMinusExpression o\n    ['-', do_dump(o.expr)]\n  end\n\n  def dump_UnfoldExpression o\n    ['unfold', do_dump(o.expr)]\n  end\n\n  def dump_BlockExpression o\n    result = [\"block\", :indent]\n    o.statements.each { |x| result << :break; result << do_dump(x) }\n    result << :dedent << :break\n    result\n  end\n\n  # Interpolated strings are shown as (cat seg0 seg1 ... segN)\n  def dump_ConcatenatedString o\n    [\"cat\"] + o.segments.collect { |x| do_dump(x) }\n  end\n\n  def dump_HeredocExpression(o)\n    [\"@(#{o.syntax})\", :indent, :break, do_dump(o.text_expr), :dedent, :break]\n  end\n\n  def dump_HostClassDefinition o\n    result = [\"class\", o.name]\n    result << [\"inherits\", o.parent_class] if o.parent_class\n    result << [\"parameters\"] + o.parameters.collect { |p| do_dump(p) } if o.parameters.size() > 0\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_PlanDefinition o\n    result = [\"plan\", o.name]\n    result << [\"parameters\"] + o.parameters.collect { |p| do_dump(p) } if o.parameters.size() > 0\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_NodeDefinition o\n    result = [\"node\"]\n    result << [\"matches\"] + o.host_matches.collect { |m| do_dump(m) }\n    result << [\"parent\", do_dump(o.parent)] if o.parent\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_NamedDefinition o\n    # the nil must be replaced with a string\n    result = [nil, o.name]\n    result << [\"parameters\"] + o.parameters.collect { |p| do_dump(p) } if o.parameters.size() > 0\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_FunctionDefinition o\n    result = ['function', o.name]\n    result << ['parameters'] + o.parameters.collect { |p| do_dump(p) } if o.parameters.size() > 0\n    result << ['return_type', do_dump(o.return_type)] unless o.return_type.nil?\n    if o.body\n      result << do_dump(o.body)\n    else\n      result << []\n    end\n    result\n  end\n\n  def dump_ResourceTypeDefinition o\n    result = dump_NamedDefinition(o)\n    result[0] = 'define'\n    result\n  end\n\n  def dump_ResourceOverrideExpression o\n    form = o.form == 'regular' ? '' : o.form + '-'\n    result = [form + 'override', do_dump(o.resources), :indent]\n    o.operations.each do |p|\n      result << :break << do_dump(p)\n    end\n    result << :dedent\n    result\n  end\n\n  def dump_ReservedWord o\n    ['reserved', o.word]\n  end\n\n  # Produces parameters as name, or (= name value)\n  def dump_Parameter o\n    name_prefix = o.captures_rest ? '*' : ''\n    name_part = \"#{name_prefix}#{o.name}\"\n    if o.value && o.type_expr\n      [\"=t\", do_dump(o.type_expr), name_part, do_dump(o.value)]\n    elsif o.value\n      [\"=\", name_part, do_dump(o.value)]\n    elsif o.type_expr\n      [\"t\", do_dump(o.type_expr), name_part]\n    else\n      name_part\n    end\n  end\n\n  def dump_ParenthesizedExpression o\n    do_dump(o.expr)\n  end\n\n  # Hides that Program exists in the output (only its body is shown), the definitions are just\n  # references to contained classes, resource types, and nodes\n  def dump_Program(o)\n    dump(o.body)\n  end\n\n  def dump_IfExpression o\n    result = [\"if\", do_dump(o.test), :indent, :break,\n              [\"then\", :indent, do_dump(o.then_expr), :dedent]]\n    result +=\n      [:break,\n       [\"else\", :indent, do_dump(o.else_expr), :dedent],\n       :dedent] unless is_nop? o.else_expr\n    result\n  end\n\n  def dump_UnlessExpression o\n    result = [\"unless\", do_dump(o.test), :indent, :break,\n              [\"then\", :indent, do_dump(o.then_expr), :dedent]]\n    result +=\n      [:break,\n       [\"else\", :indent, do_dump(o.else_expr), :dedent],\n       :dedent] unless is_nop? o.else_expr\n    result\n  end\n\n  # Produces (invoke name args...) when not required to produce an rvalue, and\n  # (call name args ... ) otherwise.\n  #\n  def dump_CallNamedFunctionExpression o\n    result = [o.rval_required ? \"call\" : \"invoke\", do_dump(o.functor_expr)]\n    o.arguments.collect { |a| result << do_dump(a) }\n    result << do_dump(o.lambda) if o.lambda\n    result\n  end\n\n  #    def dump_CallNamedFunctionExpression o\n  #      result = [o.rval_required ? \"call\" : \"invoke\", do_dump(o.functor_expr)]\n  #      o.arguments.collect {|a| result << do_dump(a) }\n  #      result\n  #    end\n\n  def dump_CallMethodExpression o\n    result = [o.rval_required ? \"call-method\" : \"invoke-method\", do_dump(o.functor_expr)]\n    o.arguments.collect { |a| result << do_dump(a) }\n    result << do_dump(o.lambda) if o.lambda\n    result\n  end\n\n  def dump_CaseExpression o\n    result = [\"case\", do_dump(o.test), :indent]\n    o.options.each do |s|\n      result << :break << do_dump(s)\n    end\n    result << :dedent\n  end\n\n  def dump_CaseOption o\n    result = [\"when\"]\n    result << o.values.collect { |x| do_dump(x) }\n    result << [\"then\", do_dump(o.then_expr)]\n    result\n  end\n\n  def dump_RelationshipExpression o\n    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]\n  end\n\n  def dump_RenderStringExpression o\n    [\"render-s\", \" '#{o.value}'\"]\n  end\n\n  def dump_RenderExpression o\n    [\"render\", do_dump(o.expr)]\n  end\n\n  def dump_ResourceBody o\n    result = [do_dump(o.title), :indent]\n    o.operations.each do |p|\n      result << :break << do_dump(p)\n    end\n    result << :dedent\n    result\n  end\n\n  def dump_ResourceDefaultsExpression o\n    form = o.form == 'regular' ? '' : o.form + '-'\n    result = [form + 'resource-defaults', do_dump(o.type_ref), :indent]\n    o.operations.each do |p|\n      result << :break << do_dump(p)\n    end\n    result << :dedent\n    result\n  end\n\n  def dump_ResourceExpression o\n    form = o.form == 'regular' ? '' : o.form + '-'\n    result = [form + 'resource', do_dump(o.type_name), :indent]\n    o.bodies.each do |b|\n      result << :break << do_dump(b)\n    end\n    result << :dedent\n    result\n  end\n\n  def dump_SelectorExpression o\n    [\"?\", do_dump(o.left_expr)] + o.selectors.collect { |x| do_dump(x) }\n  end\n\n  def dump_SelectorEntry o\n    [do_dump(o.matching_expr), \"=>\", do_dump(o.value_expr)]\n  end\n\n  def dump_TypeAlias(o)\n    ['type-alias', o.name, do_dump(o.type_expr)]\n  end\n\n  def dump_TypeMapping(o)\n    ['type-mapping', do_dump(o.type_expr), do_dump(o.mapping_expr)]\n  end\n\n  def dump_TypeDefinition(o)\n    ['type-definition', o.name, o.parent, do_dump(o.body)]\n  end\n\n  def dump_Object o\n    [o.class.to_s, o.to_s]\n  end\n\n  def is_nop? o\n    o.nil? || o.is_a?(Puppet::Pops::Model::Nop)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/pn_transformer.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Model\nclass PNTransformer\n  extend Puppet::Concurrent::ThreadLocalSingleton\n\n  def self.transform(ast)\n    singleton.transform(ast)\n  end\n\n  def initialize\n    @visitor = Visitor.new(nil, 'transform', 0, 0)\n  end\n\n  def transform(ast)\n    @visitor.visit_this_0(self, ast)\n  end\n\n  def transform_AccessExpression(e)\n    PN::List.new([transform(e.left_expr)] + pn_array(e.keys)).as_call('access')\n  end\n\n  def transform_AndExpression(e)\n    binary_op(e, 'and')\n  end\n\n  def transform_ArithmeticExpression(e)\n    binary_op(e, e.operator)\n  end\n\n  def transform_Array(a)\n    PN::List.new(pn_array(a))\n  end\n\n  def transform_AssignmentExpression(e)\n    binary_op(e, e.operator)\n  end\n\n  def transform_AttributeOperation(e)\n    PN::Call.new(e.operator, PN::Literal.new(e.attribute_name), transform(e.value_expr))\n  end\n\n  def transform_AttributesOperation(e)\n    PN::Call.new('splat-hash', transform(e.expr))\n  end\n\n  def transform_BlockExpression(e)\n    transform(e.statements).as_call('block')\n  end\n\n  def transform_CallFunctionExpression(e)\n    call_to_pn(e, 'call-lambda', 'invoke-lambda')\n  end\n\n  def transform_CallMethodExpression(e)\n    call_to_pn(e, 'call-method', 'invoke-method')\n  end\n\n  def transform_CallNamedFunctionExpression(e)\n    call_to_pn(e, 'call', 'invoke')\n  end\n\n  def transform_CaseExpression(e)\n    PN::Call.new('case', transform(e.test), transform(e.options))\n  end\n\n  def transform_CaseOption(e)\n    PN::Map.new([transform(e.values).with_name('when'), block_as_entry('then', e.then_expr)])\n  end\n\n  def transform_CollectExpression(e)\n    entries = [transform(e.type_expr).with_name('type'), transform(e.query).with_name('query')]\n    entries << transform(e.operations).with_name('ops') unless e.operations.empty?\n    PN::Map.new(entries).as_call('collect')\n  end\n\n  def transform_ComparisonExpression(e)\n    binary_op(e, e.operator)\n  end\n\n  def transform_ConcatenatedString(e)\n    transform(e.segments).as_call('concat')\n  end\n\n  def transform_EppExpression(e)\n    e.body.nil? ? PN::Call.new('epp') : transform(e.body).as_call('epp')\n  end\n\n  def transform_ExportedQuery(e)\n    is_nop?(e.expr) ? PN::Call.new('exported-query') : PN::Call.new('exported-query', transform(e.expr))\n  end\n\n  def transform_Factory(e)\n    transform(e.model)\n  end\n\n  def transform_FunctionDefinition(e)\n    definition_to_pn(e, 'function', nil, e.return_type)\n  end\n\n  def transform_HeredocExpression(e)\n    entries = []\n    entries << PN::Literal.new(e.syntax).with_name('syntax') unless e.syntax == ''\n    entries << transform(e.text_expr).with_name('text')\n    PN::Map.new(entries).as_call('heredoc')\n  end\n\n  def transform_HostClassDefinition(e)\n    definition_to_pn(e, 'class', e.parent_class)\n  end\n\n  def transform_IfExpression(e)\n    if_to_pn(e, 'if')\n  end\n\n  def transform_InExpression(e)\n    binary_op(e, 'in')\n  end\n\n  def transform_KeyedEntry(e)\n    PN::Call.new('=>', transform(e.key), transform(e.value))\n  end\n\n  def transform_LambdaExpression(e)\n    entries = []\n    entries << parameters_entry(e.parameters) unless e.parameters.empty?\n    entries << transform(e.return_type).with_name('returns') unless e.return_type.nil?\n    entries << block_as_entry('body', e.body) unless e.body.nil?\n    PN::Map.new(entries).as_call('lambda')\n  end\n\n  def transform_LiteralBoolean(e)\n    PN::Literal.new(e.value)\n  end\n\n  def transform_LiteralDefault(_)\n    PN::Call.new('default')\n  end\n\n  def transform_LiteralFloat(e)\n    PN::Literal.new(e.value)\n  end\n\n  def transform_LiteralHash(e)\n    transform(e.entries).as_call('hash')\n  end\n\n  def transform_LiteralInteger(e)\n    vl = PN::Literal.new(e.value)\n    e.radix == 10 ? vl : PN::Map.new([PN::Literal.new(e.radix).with_name('radix'), vl.with_name('value')]).as_call('int')\n  end\n\n  def transform_LiteralList(e)\n    transform(e.values).as_call('array')\n  end\n\n  def transform_LiteralRegularExpression(e)\n    PN::Literal.new(Types::PRegexpType.regexp_to_s(e.value)).as_call('regexp')\n  end\n\n  def transform_LiteralString(e)\n    PN::Literal.new(e.value)\n  end\n\n  def transform_LiteralUndef(_)\n    PN::Literal.new(nil)\n  end\n\n  def transform_MatchExpression(e)\n    binary_op(e, e.operator)\n  end\n\n  def transform_NamedAccessExpression(e)\n    binary_op(e, '.')\n  end\n\n  def transform_NodeDefinition(e)\n    entries = [transform(e.host_matches).with_name('matches')]\n    entries << transform(e.parent).with_name('parent') unless e.parent.nil?\n    entries << block_as_entry('body', e.body) unless e.body.nil?\n    PN::Map.new(entries).as_call('node')\n  end\n\n  def transform_Nop(_)\n    PN::Call.new('nop')\n  end\n\n  # Some elements may have a nil element instead of a Nop Expression\n  def transform_NilClass(e)\n    PN::Call.new('nop')\n  end\n\n  def transform_NotExpression(e)\n    PN::Call.new('!', transform(e.expr))\n  end\n\n  def transform_OrExpression(e)\n    binary_op(e, 'or')\n  end\n\n  def transform_Parameter(e)\n    entries = [PN::Literal.new(e.name).with_name('name')]\n    entries << transform(e.type_expr).with_name('type') unless e.type_expr.nil?\n    entries << PN::Literal.new(true).with_name('splat') if e.captures_rest\n    entries << transform(e.value).with_name('value') unless e.value.nil?\n    PN::Map.new(entries).with_name('param')\n  end\n\n  def transform_ParenthesizedExpression(e)\n    PN::Call.new('paren', transform(e.expr))\n  end\n\n  def transform_PlanDefinition(e)\n    definition_to_pn(e, 'plan', nil, e.return_type)\n  end\n\n  def transform_Program(e)\n    transform(e.body)\n  end\n\n  def transform_QualifiedName(e)\n    PN::Call.new('qn', PN::Literal.new(e.value))\n  end\n\n  def transform_QualifiedReference(e)\n    PN::Call.new('qr', PN::Literal.new(e.cased_value))\n  end\n\n  def transform_RelationshipExpression(e)\n    binary_op(e, e.operator)\n  end\n\n  def transform_RenderExpression(e)\n    PN::Call.new('render', transform(e.expr))\n  end\n\n  def transform_RenderStringExpression(e)\n    PN::Literal.new(e.value).as_call('render-s')\n  end\n\n  def transform_ReservedWord(e)\n    PN::Literal.new(e.word).as_call('reserved')\n  end\n\n  def transform_ResourceBody(e)\n    PN::Map.new([\n                  transform(e.title).with_name('title'),\n                  transform(e.operations).with_name('ops')\n                ]).as_call('resource_body')\n  end\n\n  def transform_ResourceDefaultsExpression(e)\n    entries = [transform(e.type_ref).with_name('type'), transform(e.operations).with_name('ops')]\n    entries << PN::Literal.new(e.form).with_name('form') unless e.form == 'regular'\n    PN::Map.new(entries).as_call('resource-defaults')\n  end\n\n  def transform_ResourceExpression(e)\n    entries = [\n      transform(e.type_name).with_name('type'),\n      PN::List.new(pn_array(e.bodies).map { |body| body[0] }).with_name('bodies')\n    ]\n    entries << PN::Literal.new(e.form).with_name('form') unless e.form == 'regular'\n    PN::Map.new(entries).as_call('resource')\n  end\n\n  def transform_ResourceOverrideExpression(e)\n    entries = [transform(e.resources).with_name('resources'), transform(e.operations).with_name('ops')]\n    entries << PN::Literal.new(e.form).with_name('form') unless e.form == 'regular'\n    PN::Map.new(entries).as_call('resource-override')\n  end\n\n  def transform_ResourceTypeDefinition(e)\n    definition_to_pn(e, 'define')\n  end\n\n  def transform_SelectorEntry(e)\n    PN::Call.new('=>', transform(e.matching_expr), transform(e.value_expr))\n  end\n\n  def transform_SelectorExpression(e)\n    PN::Call.new('?', transform(e.left_expr), transform(e.selectors))\n  end\n\n  def transform_TextExpression(e)\n    PN::Call.new('str', transform(e.expr))\n  end\n\n  def transform_TypeAlias(e)\n    PN::Call.new('type-alias', PN::Literal.new(e.name), transform(e.type_expr))\n  end\n\n  def transform_TypeDefinition(e)\n    PN::Call.new('type-definition', PN::Literal.new(e.name), PN::Literal.new(e.parent), transform(e.body))\n  end\n\n  def transform_TypeMapping(e)\n    PN::Call.new('type-mapping', transform(e.type_expr), transform(e.mapping_expr))\n  end\n\n  def transform_UnaryMinusExpression(e)\n    if e.expr.is_a?(LiteralValue)\n      v = e.expr.value\n      if v.is_a?(Numeric)\n        return PN::Literal.new(-v)\n      end\n    end\n    PN::Call.new('-', transform(e.expr))\n  end\n\n  def transform_UnfoldExpression(e)\n    PN::Call.new('unfold', transform(e.expr))\n  end\n\n  def transform_UnlessExpression(e)\n    if_to_pn(e, 'unless')\n  end\n\n  def transform_VariableExpression(e)\n    ne = e.expr\n    PN::Call.new('var', ne.is_a?(Model::QualifiedName) ? PN::Literal.new(ne.value) : transform(ne))\n  end\n\n  def transform_VirtualQuery(e)\n    is_nop?(e.expr) ? PN::Call.new('virtual-query') : PN::Call.new('virtual-query', transform(e.expr))\n  end\n\n  def is_nop?(e)\n    e.nil? || e.is_a?(Nop)\n  end\n\n  def binary_op(e, op)\n    PN::Call.new(op, transform(e.left_expr), transform(e.right_expr))\n  end\n\n  def definition_to_pn(e, type_name, parent = nil, return_type = nil)\n    entries = [PN::Literal.new(e.name).with_name('name')]\n    entries << PN::Literal.new(parent).with_name('parent') unless parent.nil?\n    entries << parameters_entry(e.parameters) unless e.parameters.empty?\n    entries << block_as_entry('body', e.body) unless e.body.nil?\n    entries << transform(return_type).with_name('returns') unless return_type.nil?\n    PN::Map.new(entries).as_call(type_name)\n  end\n\n  def parameters_entry(parameters)\n    PN::Map.new(parameters.map do |p|\n      entries = []\n      entries << transform(p.type_expr).with_name('type') unless p.type_expr.nil?\n      entries << PN::Literal(true).with_name('splat') if p.captures_rest\n      entries << transform(p.value).with_name('value') unless p.value.nil?\n      PN::Map.new(entries).with_name(p.name)\n    end).with_name('params')\n  end\n\n  def block_as_entry(name, expr)\n    if expr.is_a?(BlockExpression)\n      transform(expr.statements).with_name(name)\n    else\n      transform([expr]).with_name(name)\n    end\n  end\n\n  def pn_array(a)\n    a.map { |e| transform(e) }\n  end\n\n  def call_to_pn(e, r, nr)\n    entries = [transform(e.functor_expr).with_name('functor'), transform(e.arguments).with_name('args')]\n    entries << transform(e.lambda).with_name('block') unless e.lambda.nil?\n    PN::Map.new(entries).as_call(e.rval_required ? r : nr)\n  end\n\n  def if_to_pn(e, name)\n    entries = [transform(e.test).with_name('test')]\n    entries << block_as_entry('then', e.then_expr) unless is_nop?(e.then_expr)\n    entries << block_as_entry('else', e.else_expr) unless is_nop?(e.else_expr)\n    PN::Map.new(entries).as_call(name)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/model/tree_dumper.rb",
    "content": "# frozen_string_literal: true\n\n# Base class for formatted textual dump of a \"model\"\n#\nclass Puppet::Pops::Model::TreeDumper\n  attr_accessor :indent_count\n\n  def initialize initial_indentation = 0\n    @@dump_visitor ||= Puppet::Pops::Visitor.new(nil, \"dump\", 0, 0)\n    @indent_count = initial_indentation\n  end\n\n  def dump(o)\n    format(do_dump(o))\n  end\n\n  def do_dump(o)\n    @@dump_visitor.visit_this_0(self, o)\n  end\n\n  def indent\n    \"  \" * indent_count\n  end\n\n  def format(x)\n    result = ''.dup\n    parts = format_r(x)\n    parts.each_index do |i|\n      if i > 0\n        # separate with space unless previous ends with whitespace or (\n        result << ' ' if parts[i] != \")\" && parts[i - 1] !~ /.*(?:\\s+|\\()$/ && parts[i] !~ /^\\s+/\n      end\n      result << parts[i].to_s\n    end\n    result\n  end\n\n  def format_r(x)\n    result = []\n    case x\n    when :break\n      result << \"\\n\" + indent\n    when :indent\n      @indent_count += 1\n    when :dedent\n      @indent_count -= 1\n    when Array\n      result << '('\n      result += x.collect { |a| format_r(a) }.flatten\n      result << ')'\n    when Symbol\n      result << x.to_s # Allows Symbols in arrays e.g. [\"text\", =>, \"text\"]\n    else\n      result << x\n    end\n    result\n  end\n\n  def is_nop? o\n    o.nil? || o.is_a?(Puppet::Pops::Model::Nop)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/code_merger.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Pops::Parser::CodeMerger\n  # Concatenates the logic in the array of parse results into one parse result.\n  # @return Puppet::Parser::AST::BlockExpression\n  #\n  def concatenate(parse_results)\n    # this is a bit brute force as the result is already 3x ast with wrapped 4x content\n    # this could be combined in a more elegant way, but it is only used to process a handful of files\n    # at the beginning of a puppet run. TODO: Revisit for Puppet 4x when there is no 3x ast at the top.\n    # PUP-5299, some sites have thousands of entries, and run out of stack when evaluating - the logic\n    # below maps the logic as flatly as possible.\n    #\n    children = parse_results.select { |x| !x.nil? && x.code }.flat_map do |parsed_class|\n      case parsed_class.code\n      when Puppet::Parser::AST::BlockExpression\n        # the BlockExpression wraps a single 4x instruction that is most likely wrapped in a Factory\n        parsed_class.code.children.map { |c| c.is_a?(Puppet::Pops::Model::Factory) ? c.model : c }\n      when Puppet::Pops::Model::Factory\n        # If it is a 4x instruction wrapped in a Factory\n        parsed_class.code.model\n      else\n        # It is the instruction directly\n        parsed_class.code\n      end\n    end\n    Puppet::Parser::AST::BlockExpression.new(:children => children)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/egrammar.ra",
    "content": "# vim: syntax=ruby\n\n# Parser using the Pops model, expression based\n\nclass Puppet::Pops::Parser::Parser\n\ntoken STRING DQPRE DQMID DQPOST\ntoken WORD\ntoken LBRACK  RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE\ntoken FALSE EQUALS APPENDS DELETES LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT\ntoken QMARK WSLPAREN LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN\ntoken IF ELSE\ntoken DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN\ntoken NAME SEMIC CASE DEFAULT AT ATAT LCOLLECT RCOLLECT CLASSREF\ntoken NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS\ntoken MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB\ntoken IN UNLESS PIPE\ntoken LAMBDA SELBRACE\ntoken NUMBER\ntoken HEREDOC SUBLOCATE\ntoken RENDER_STRING RENDER_EXPR EPP_START EPP_END EPP_END_TRIM\ntoken FUNCTION\ntoken TYPE\ntoken PRIVATE ATTR\ntoken PLAN APPLY\ntoken LOW\n\nprechigh\n  left  HIGH\n  left  SEMIC\n  left  PIPE\n  left  LPAREN WSLPAREN\n  left  RPAREN\n  left  DOT\n  nonassoc EPP_START\n  left  LBRACK LISTSTART\n  left  RBRACK\n  left  LCOLLECT LLCOLLECT\n  right NOT\n  nonassoc SPLAT\n  nonassoc UMINUS\n  left  IN\n  left  MATCH NOMATCH\n  left  TIMES DIV MODULO\n  left  MINUS PLUS\n  left  LSHIFT RSHIFT\n  left  NOTEQUAL ISEQUAL\n  left  GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL\n  left  QMARK\n  left  AND\n  left  OR\n  left  LBRACE\n  left  SELBRACE\n  left  RBRACE\n  right AT ATAT\n  right APPENDS DELETES EQUALS\n  left  IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB\n  left  FARROW\n  left  COMMA\n  nonassoc RENDER_EXPR\n  nonassoc RENDER_STRING\n  left  LOW\npreclow\n\nrule\n# Produces [Model::Program] with a body containing what was parsed\nprogram\n  : statements     { result = create_program(Factory.block_or_expression(val[0])) }\n  | epp_expression { result = create_program(val[0]) }\n  |                { result = create_empty_program }\n\n# Produces a semantic model (non validated, but semantically adjusted).\nstatements\n  : syntactic_statements { result = transform_calls(val[0]) }\n\n# Collects sequence of elements into a list that the statements rule can transform\n# (Needed because language supports function calls without parentheses around arguments).\n# Produces Array<Model::Expression>\n#\nsyntactic_statements\n  : syntactic_statement                            { result = [val[0]]}\n  | syntactic_statements SEMIC syntactic_statement { result = val[0].push val[2] }\n  | syntactic_statements syntactic_statement       { result = val[0].push val[1] }\n\n# Produce a single expression or Array of expression\n# This exists to handle multiple arguments to non parenthesized function call. If e is expression,\n# the a program can consists of e [e,e,e] where the first may be a name of a function to call.\n#\nsyntactic_statement\n  : assignment                             =LOW { result = val[0] }\n  | syntactic_statement COMMA assignment   =LOW { result = aryfy(val[0]).push(val[1]).push(val[2]) }\n\n# Assignment (is right recursive since assignment is right associative)\nassignment\n  : relationship =LOW\n  | relationship EQUALS  assignment         { result = val[0].set(val[2])      ; loc result, val[1] }\n  | relationship APPENDS assignment         { result = val[0].plus_set(val[2]) ; loc result, val[1] }\n  | relationship DELETES assignment         { result = val[0].minus_set(val[2]); loc result, val[1] }\n\n# Argument is like assignment but permits KEY_ENTRY which it converts it to a HASH_UNFOLDED\nargument\n  : assignment\n  | hashpair { result = Factory.HASH_UNFOLDED([val[0]]); loc result, val[0] }\n\n# Repeated adjacent HASH_UNFOLDED entries merged into one HASH_UNFOLDED\narguments\n  : argument                            { result = [val[0]] }\n  | arguments COMMA argument            { result = Factory.ARGUMENTS(val[0], val[2]) }\n\nrelationship\n  : resource =LOW\n  | relationship IN_EDGE      resource      { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }\n  | relationship IN_EDGE_SUB  resource      { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }\n  | relationship OUT_EDGE     resource      { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }\n  | relationship OUT_EDGE_SUB resource      { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }\n\n#-- RESOURCE\n#\nresource\n  : expression = LOW\n\n    #---VIRTUAL\n  | AT resource {\n      result = val[1]\n      unless Factory.set_resource_form(result, 'virtual')\n        # This is equivalent to a syntax error - additional semantic restrictions apply\n        error val[0], \"Virtual (@) can only be applied to a Resource Expression\"\n      end\n      # relocate the result\n      loc result, val[0], val[1]\n    }\n\n    #---EXPORTED\n  | ATAT resource {\n      result = val[1]\n      unless Factory.set_resource_form(result, 'exported')\n        # This is equivalent to a syntax error - additional semantic restrictions apply\n        error val[0], \"Exported (@@) can only be applied to a Resource Expression\"\n      end\n      # relocate the result\n      loc result, val[0], val[1]\n    }\n\n    #---RESOURCE TITLED 3x and 4x\n  | resource LBRACE expression COLON attribute_operations additional_resource_bodies RBRACE {\n      bodies = [Factory.RESOURCE_BODY(val[2], val[4])] + val[5]\n      result = Factory.RESOURCE(val[0], bodies)\n      loc result, val[0], val[6]\n    }\n\n    #---CLASS RESOURCE\n  | CLASS LBRACE resource_bodies endsemi RBRACE {\n        result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])\n        loc result, val[0], val[4]\n    }\n\n    # --RESOURCE 3X Expression\n    #   Handles both 3x overrides and defaults (i.e. single resource_body without title colon)\n    #   Slated for possible deprecation since it requires transformation and mix static/evaluation check\n    #\n  | resource LBRACE attribute_operations endcomma RBRACE {\n       result = case Factory.resource_shape(val[0])\n       when :resource, :class\n        # This catches deprecated syntax.\n        # If the attribute operations does not include +>, then the found expression\n        # is actually a LEFT followed by LITERAL_HASH\n        #\n        unless tmp = transform_resource_wo_title(val[0], val[2], val[1], val[4])\n          error val[1], \"Syntax error resource body without title or hash with +>\"\n        end\n        tmp\n      when :defaults\n        Factory.RESOURCE_DEFAULTS(val[0], val[2])\n      when :override\n        # This was only done for override in original - TODO should it be here at all\n        Factory.RESOURCE_OVERRIDE(val[0], val[2])\n      else\n        error val[0], \"Expression is not valid as a resource, resource-default, or resource-override\"\n      end\n     loc result, val[0], val[4]\n    }\n\n  resource_body\n    : expression COLON attribute_operations endcomma { result = Factory.RESOURCE_BODY(val[0], val[2]) }\n\n  resource_bodies\n    : resource_body                         =HIGH { result = [val[0]] }\n    | resource_bodies SEMIC resource_body   =HIGH { result = val[0].push val[2] }\n\n  # This is a rule for the intermediate state where RACC has seen enough tokens to understand that\n  # what is expressed is a Resource Expression, it now has to get to the finishing line\n  #\n  additional_resource_bodies\n    : endcomma                         { result = [] }\n    | endcomma SEMIC                   { result = [] }\n    | endcomma SEMIC resource_bodies endsemi { result = val[2] }\n\n#-- EXPRESSION\n#\nexpression\n  : primary_expression\n  | call_function_expression\n  | bracketed_expression\n  | expression IN           expression                 { result = val[0].in val[2]        ; loc result, val[1] }\n  | expression MATCH        expression                 { result = val[0] =~  val[2]       ; loc result, val[1] }\n  | expression NOMATCH      expression                 { result = val[0].mne val[2]       ; loc result, val[1] }\n  | expression PLUS         expression                 { result = val[0] +   val[2]       ; loc result, val[1] }\n  | expression MINUS        expression                 { result = val[0] -   val[2]       ; loc result, val[1] }\n  | expression DIV          expression                 { result = val[0] /   val[2]       ; loc result, val[1] }\n  | expression TIMES        expression                 { result = val[0] *   val[2]       ; loc result, val[1] }\n  | expression MODULO       expression                 { result = val[0] %   val[2]       ; loc result, val[1] }\n  | expression LSHIFT       expression                 { result = val[0] <<  val[2]       ; loc result, val[1] }\n  | expression RSHIFT       expression                 { result = val[0] >>  val[2]       ; loc result, val[1] }\n  |            MINUS        expression         =UMINUS { result = val[1].minus            ; loc result, val[0] }\n  |            TIMES        expression         =SPLAT  { result = val[1].unfold           ; loc result, val[0] }\n  | expression NOTEQUAL     expression                 { result = val[0].ne  val[2]       ; loc result, val[1] }\n  | expression ISEQUAL      expression                 { result = val[0].eq  val[2]       ; loc result, val[1] }\n  | expression GREATERTHAN  expression                 { result = val[0] >   val[2]       ; loc result, val[1] }\n  | expression GREATEREQUAL expression                 { result = val[0] >=  val[2]       ; loc result, val[1] }\n  | expression LESSTHAN     expression                 { result = val[0] <   val[2]       ; loc result, val[1] }\n  | expression LESSEQUAL    expression                 { result = val[0] <=  val[2]       ; loc result, val[1] }\n  |            NOT          expression                 { result = val[1].not              ; loc result, val[0] }\n  | expression AND          expression                 { result = val[0].and val[2]       ; loc result, val[1] }\n  | expression OR           expression                 { result = val[0].or  val[2]       ; loc result, val[1] }\n  | expression QMARK selector_entries                  { result = val[0].select(*val[2])  ; loc result, val[0] }\n  |            LPAREN       assignment RPAREN          { result = val[1].paren            ; loc result, val[0] }\n  |            WSLPAREN     assignment RPAREN          { result = val[1].paren          ; loc result, val[0] }\n\nbracketed_expression\n  : expression LBRACK access_args endcomma RBRACK =LBRACK { result = val[0].access(val[2]); loc result, val[0], val[4] }\n\naccess_args\n  : access_arg                   { result = [val[0]] }\n  | access_args COMMA access_arg { result = Factory.ARGUMENTS(val[0], val[2]) }\n\naccess_arg\n  : expression\n  | hashpair { result = Factory.HASH_UNFOLDED([val[0]]); loc result, val[0] }\n\n#---EXPRESSIONS\n#   (i.e. \"argument list\")\n#\n# This expression list can not contain function calls without parentheses around arguments\n# Produces Array<Model::Expression>\n#\nexpressions\n  : expression                   { result = [val[0]] }\n  | expressions COMMA expression { result = val[0].push(val[2]) }\n\nprimary_expression\n  : variable\n  | call_method_with_lambda_expression\n  | collection_expression\n  | case_expression\n  | if_expression\n  | unless_expression\n  | definition_expression\n  | hostclass_expression\n  | plan_expression\n  | apply_expression\n  | node_definition_expression\n  | epp_render_expression\n  | function_definition\n  | type_alias\n  | type_definition\n  | reserved_word\n  | array\n  | hash\n  | regex\n  | quotedtext\n  | type\n  | NUMBER       { result = Factory.NUMBER(val[0][:value])          ; loc result, val[0] }\n  | BOOLEAN      { result = Factory.literal(val[0][:value])         ; loc result, val[0] }\n  | DEFAULT      { result = Factory.literal(:default)               ; loc result, val[0] }\n  | UNDEF        { result = Factory.literal(:undef)                 ; loc result, val[0] }\n  | NAME         { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] }\n\n\n#---CALL FUNCTION\n#\n# Produces Model::CallNamedFunction\n\ncall_function_expression\n  : call_function_start arguments endcomma RPAREN {\n      result = Factory.CALL_NAMED(val[0], true, val[1])\n      loc result, val[0], val[3]\n    }\n  | call_function_start RPAREN {\n      result = Factory.CALL_NAMED(val[0], true, [])\n      loc result, val[0], val[1]\n    }\n  | call_function_start arguments endcomma RPAREN lambda {\n      result = Factory.CALL_NAMED(val[0], true, val[1])\n      loc result, val[0], val[4]\n      result.lambda = val[4]\n    }\n  | call_function_start RPAREN  lambda {\n      result = Factory.CALL_NAMED(val[0], true, [])\n      loc result, val[0], val[2]\n      result.lambda = val[2]\n    }\n\ncall_function_start\n  : expression LPAREN { result = val[0] }\n  | TYPE LPAREN { result = Factory.QNAME(val[0][:value]); loc result, val[0] }\n\n#---CALL METHOD\n#\ncall_method_with_lambda_expression\n  : call_method_expression         =LOW { result = val[0] }\n  | call_method_expression lambda       { result = val[0]; val[0].lambda = val[1] }\n\n  call_method_expression\n    : named_access LPAREN arguments RPAREN { result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] }\n    | named_access LPAREN RPAREN             { result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] }\n    | named_access =LOW                      { result = Factory.CALL_METHOD(val[0], []); loc result, val[0] }\n\n  named_access\n    : expression DOT NAME {\n        result = val[0].dot(Factory.fqn(val[2][:value]))\n        loc result, val[1], val[2]\n      }\n    | expression DOT TYPE {\n        result = val[0].dot(Factory.fqn(val[2][:value]))\n        loc result, val[1], val[2]\n      }\n\n#---LAMBDA\n#\nlambda\n  : lambda_parameter_list opt_return_type lambda_rest {\n      result = Factory.LAMBDA(val[0][:value], val[2][:value], val[1])\n      loc result, val[0][:start], val[2][:end]\n    }\n\nlambda_rest\n  : LBRACE statements RBRACE  { result = {:end => val[2], :value =>val[1] } }\n  | LBRACE RBRACE             { result = {:end => val[1], :value => nil } }\n\n\nlambda_parameter_list\n  : PIPE PIPE                     { result = {:start => val[0], :value => [] } }\n  | PIPE parameters endcomma PIPE { result = {:start => val[0], :value => val[1] } }\n\n#---CONDITIONALS\n\n#--IF\n#\nif_expression\n  : IF if_part {\n      result = val[1]\n      loc(result, val[0], val[1])\n    }\n\n  # Produces Model::IfExpression\n  if_part\n    : expression LBRACE statements RBRACE else {\n        result = Factory.IF(val[0], Factory.block_or_expression(val[2], val[1], val[3]), val[4])\n        loc(result, val[0], (val[4] ? val[4] : val[3]))\n      }\n    | expression LBRACE RBRACE else {\n        result = Factory.IF(val[0], nil, val[3])\n        loc(result, val[0], (val[3] ? val[3] : val[2]))\n      }\n\n  # Produces [Model::Expression, nil] - nil if there is no else or elsif part\n  else\n    : # nothing\n    | ELSIF if_part {\n        result = val[1]\n        loc(result, val[0], val[1])\n      }\n    | ELSE LBRACE statements RBRACE {\n        result = Factory.block_or_expression(val[2], val[1], val[3])\n      }\n    | ELSE LBRACE RBRACE {\n        result = nil # don't think a nop is needed here either\n      }\n\n#--UNLESS\n#\nunless_expression\n  : UNLESS expression LBRACE statements RBRACE unless_else {\n      result = Factory.UNLESS(val[1], Factory.block_or_expression(val[3], val[2], val[4]), val[5])\n      loc result, val[0], val[4]\n    }\n  | UNLESS expression LBRACE RBRACE unless_else {\n      result = Factory.UNLESS(val[1], nil, val[4])\n      loc result, val[0], val[4]\n    }\n\n  # Different from else part of if, since \"elsif\" is not supported, but 'else' is\n  #\n  # Produces [Model::Expression, nil] - nil if there is no else or elsif part\n  unless_else\n    : # nothing\n    | ELSE LBRACE statements RBRACE {\n        result = Factory.block_or_expression(val[2], val[1], val[3])\n      }\n    | ELSE LBRACE RBRACE {\n        result = nil # don't think a nop is needed here either\n      }\n\n#--- CASE EXPRESSION\n#\ncase_expression\n  : CASE expression LBRACE case_options RBRACE {\n      result = Factory.CASE(val[1], *val[3])\n      loc result, val[0], val[4]\n    }\n\n  # Produces Array<Model::CaseOption>\n  case_options\n    : case_option               { result = [val[0]] }\n    | case_options case_option  { result = val[0].push val[1] }\n\n  # Produced Model::CaseOption (aka When)\n  case_option\n    : expressions COLON LBRACE options_statements RBRACE {\n        result = Factory.WHEN(val[0], val[3]); loc result, val[1], val[4]\n      }\n\n  options_statements\n    : nil\n    | statements\n\n  # This special construct is required or racc will produce the wrong result when the selector entry\n  # LHS is generalized to any expression (LBRACE looks like a hash). Thus it is not possible to write\n  # a selector with a single entry where the entry LHS is a hash.\n  # The SELBRACE token is a LBRACE that follows a QMARK, and this is produced by the lexer with a lookback\n  # Produces Array<Model::SelectorEntry>\n  #\n  selector_entries\n    : selector_entry\n    | SELBRACE selector_entry_list endcomma RBRACE {\n        result = val[1]\n      }\n\n  # Produces Array<Model::SelectorEntry>\n  selector_entry_list\n    : selector_entry                           { result = [val[0]] }\n    | selector_entry_list COMMA selector_entry { result = val[0].push val[2] }\n\n  # Produces a Model::SelectorEntry\n  # This FARROW wins over FARROW in Hash\n  selector_entry\n    : expression FARROW expression { result = Factory.MAP(val[0], val[2]) ; loc result, val[1] }\n\n#---COLLECTION\n#\n# A Collection is a predicate applied to a set of objects with an implied context (used variables are\n# attributes of the object.\n# i.e. this is equivalent to source.select(QUERY).apply(ATTRIBUTE_OPERATIONS)\n#\ncollection_expression\n  : expression collect_query LBRACE attribute_operations endcomma RBRACE {\n      result = Factory.COLLECT(val[0], val[1], val[3])\n      loc result, val[0], val[5]\n    }\n  | expression collect_query =LOW {\n      result = Factory.COLLECT(val[0], val[1], [])\n      loc result, val[0], val[1]\n    }\n\n  collect_query\n    : LCOLLECT  optional_query RCOLLECT  { result = Factory.VIRTUAL_QUERY(val[1])   ; loc result, val[0], val[2] }\n    | LLCOLLECT optional_query RRCOLLECT { result = Factory.EXPORTED_QUERY(val[1])  ; loc result, val[0], val[2] }\n\n  optional_query\n    : nil\n    | expression\n\n#---ATTRIBUTE OPERATIONS (Not an expression)\n#\nattribute_operations\n  :                                                { result = [] }\n  | attribute_operation                            { result = [val[0]] }\n  | attribute_operations COMMA attribute_operation { result = val[0].push(val[2]) }\n\n  # Produces String\n  # QUESTION: Why is BOOLEAN valid as an attribute name?\n  #\n  attribute_name\n    : NAME\n    | keyword\n\n  # In this version, illegal combinations are validated instead of producing syntax errors\n  # (Can give nicer error message \"+> is not applicable to...\")\n  # Produces Model::AttributeOperation\n  #\n  attribute_operation\n    : attribute_name FARROW expression {\n        result = Factory.ATTRIBUTE_OP(val[0][:value], '=>', val[2])\n        loc result, val[0], val[2]\n      }\n    | attribute_name PARROW expression {\n        result = Factory.ATTRIBUTE_OP(val[0][:value], '+>', val[2])\n        loc result, val[0], val[2]\n      }\n  | TIMES FARROW expression {\n      result = Factory.ATTRIBUTES_OP(val[2]) ; loc result, val[0], val[2]\n   }\n\n#---DEFINE\n#\n# Produces Model::Definition\n#\ndefinition_expression\n  : DEFINE classname parameter_list LBRACE opt_statements RBRACE {\n      definition = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n      # New lexer does not keep track of this, this is done in validation\n      if @lexer.respond_to?(:'indefine=')\n        @lexer.indefine = false\n      end\n    }\n\n#--PLAN\nplan_expression\n  : PLAN stacked_classname parameter_list LBRACE opt_statements RBRACE {\n      # Remove this plan's name from the namestack as all nested plans have been parsed\n      namepop\n      definition = Factory.PLAN(classname(val[1][:value]), val[2], val[4])\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n    }\n\napply_expression\n  : APPLY LPAREN arguments endcomma RPAREN LBRACE statements RBRACE {\n      result = Factory.APPLY(val[2], Factory.APPLY_BLOCK(val[6]))\n      loc result, val[0], val[7]\n    }\n  | APPLY LPAREN arguments endcomma RPAREN LBRACE RBRACE {\n      result = Factory.APPLY(val[2], Factory.APPLY_BLOCK([]))\n      loc result, val[0], val[6]\n    }\n\n#---HOSTCLASS\n#\n# Produces Model::HostClassDefinition\n#\nhostclass_expression\n  : CLASS stacked_classname parameter_list classparent LBRACE opt_statements RBRACE {\n      # Remove this class' name from the namestack as all nested classes have been parsed\n      namepop\n      definition = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])\n      loc(definition, val[0], val[6])\n      result = add_definition(definition)\n    }\n\n  # Record the classname so nested classes gets a fully qualified name at parse-time\n  # This is a separate rule since racc does not support intermediate actions.\n  #\n  stacked_classname\n    : classname { namestack(val[0][:value]) ; result = val[0] }\n\n  opt_statements\n    : statements\n    | nil\n\n  # Produces String, name or nil result\n  classparent\n    : nil\n    | INHERITS classnameordefault { result = val[1] }\n\n  # Produces String (this construct allows a class to be named \"default\" and to be referenced as\n  # the parent class.\n  # TODO: Investigate the validity\n  # Produces a String (classname), or a token (DEFAULT).\n  #\n  classnameordefault\n    : classname\n    | DEFAULT\n\n#---NODE\n#\n# Produces Model::NodeDefinition\n#\nnode_definition_expression\n  : NODE hostnames endcomma nodeparent LBRACE statements RBRACE {\n      definition = Factory.NODE(val[1], val[3], val[5])\n      loc(definition, val[0], val[6])\n      result = add_definition(definition)\n    }\n  | NODE hostnames endcomma nodeparent LBRACE RBRACE {\n      definition = Factory.NODE(val[1], val[3], nil)\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n    }\n\n  # Hostnames is not a list of names, it is a list of name matchers (including a Regexp).\n  # (The old implementation had a special \"Hostname\" object with some minimal validation)\n  #\n  # Produces Array<Model::LiteralExpression>\n  #\n  hostnames\n    : hostname                 { result = [result] }\n    | hostnames COMMA hostname { result = val[0].push(val[2]) }\n\n  # Produces a LiteralExpression (string, :default, or regexp)\n  # String with interpolation is validated for better error message\n  hostname\n    : dotted_name\n    | quotedtext\n    | DEFAULT     { result = Factory.literal(:default); loc result, val[0] }\n    | regex\n\n  dotted_name\n    : name_or_number   { result = Factory.literal(val[0][:value]); loc result, val[0] }\n    | dotted_name DOT name_or_number { result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] }\n\n  name_or_number\n    : NAME\n    | NUMBER\n\n  # Produces Expression, since hostname is an Expression\n  nodeparent\n    : nil\n    | INHERITS hostname { result = val[1] }\n\n#---FUNCTION DEFINITION\n#\nfunction_definition\n  : FUNCTION classname parameter_list opt_return_type LBRACE opt_statements RBRACE {\n      definition = Factory.FUNCTION(val[1][:value], val[2], val[5], val[3])\n      loc(definition, val[0], val[6])\n      result = add_definition(definition)\n    }\n\nopt_return_type\n  : RSHIFT type { result = val[1] }\n  | RSHIFT type LBRACK access_args RBRACK { result = val[1].access(val[3]) ; loc result, val[1], val[4] }\n  | nil\n\n#---NAMES AND PARAMETERS COMMON TO SEVERAL RULES\n#   Produces String\n#   TODO: The error that \"class\" is not a valid classname is bad - classname rule is also used for other things\nclassname\n  : NAME\n  | WORD\n  | CLASSREF\n  | CLASS { error val[0], \"'class' keyword not allowed at this location\" }\n  | STRING { error val[0], \"A quoted string is not valid as a name here\" }\n\n# Produces Array<Model::Parameter>\nparameter_list\n  : nil                                   { result = [] }\n  | LPAREN  RPAREN                        { result = [] }\n  | WSLPAREN  RPAREN                      { result = [] }\n  | LPAREN parameters endcomma RPAREN     { result = val[1] }\n  | WSLPAREN parameters endcomma RPAREN   { result = val[1] }\n\n# Produces Array<Model::Parameter>\nparameters\n  : parameter                  { result = [val[0]] }\n  | parameters COMMA parameter { result = val[0].push(val[2]) }\n\n# Produces Model::Parameter\nparameter\n  : untyped_parameter\n  | typed_parameter\n\nuntyped_parameter\n  : regular_parameter\n  | splat_parameter\n\nregular_parameter\n  : VARIABLE EQUALS expression { result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] }\n  | VARIABLE                   { result = Factory.PARAM(val[0][:value]); loc result, val[0] }\n\nsplat_parameter\n  : TIMES regular_parameter { result = val[1]; val[1].captures_rest }\n\ntyped_parameter\n  : parameter_type untyped_parameter { val[1].type_expr(val[0]) ; result = val[1] }\n\nparameter_type\n  : type { result = val[0] }\n  | type LBRACK access_args RBRACK { result = val[0].access(val[2]) ; loc result, val[0], val[3] }\n\n#--TYPE ALIAS\ntype_alias\n  : type_alias_lhs EQUALS type hash {\n      definition = Factory.TYPE_ASSIGNMENT(val[0], Factory.KEY_ENTRY(val[2], val[3]))\n      loc(definition, val[0], val[3])\n      result = add_definition(definition)\n    }\n  | type_alias_lhs EQUALS type LBRACK access_args endcomma RBRACK {\n      definition = Factory.TYPE_ASSIGNMENT(val[0], val[2].access(val[4]))\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n    }\n  | type_alias_lhs EQUALS type {\n      definition = Factory.TYPE_ASSIGNMENT(val[0], val[2])\n      loc(definition, val[0], val[2])\n      result = add_definition(definition)\n    }\n  | type_alias_lhs EQUALS hash {\n      definition = Factory.TYPE_ASSIGNMENT(val[0], val[2])\n      loc(definition, val[0], val[2])\n      result = add_definition(definition)\n    }\n  | type_alias_lhs EQUALS array {\n      definition = Factory.TYPE_ASSIGNMENT(val[0], val[2])\n      loc(definition, val[0], val[4])\n      result = add_definition(definition)\n    }\n\ntype_alias_lhs\n  : TYPE parameter_type { result = val[1] }\n\n#--TYPE definition\n# TODO: Uses the optional_statements rule temporarily since the actual body awaits final spec on methods and attributes.\ntype_definition\n  : TYPE CLASSREF LBRACE optional_statements RBRACE {\n        definition = Factory.TYPE_DEFINITION(val[1][:value], nil, val[3])\n        loc(definition, val[0], val[4])\n        result = add_definition(definition)\n      }\n  | TYPE CLASSREF INHERITS CLASSREF LBRACE optional_statements RBRACE {\n        definition = Factory.TYPE_DEFINITION(val[1][:value], val[3][:value], val[5])\n        loc(definition, val[0], val[6])\n        result = add_definition(definition)\n      }\n\n#--VARIABLE\n#\nvariable\n  : VARIABLE {\n     fqn = Factory.fqn(val[0][:value])\n     loc(fqn, val[0])\n     fqn['offset'] += 1\n     fqn['length'] -= 1\n     result = fqn.var\n     loc(result, val[0])\n  }\n\n#---RESERVED WORDS\n#\nreserved_word\n  : PRIVATE   { result = Factory.RESERVED(val[0][:value]) ; loc result, val[0] }\n  | ATTR      { result = Factory.RESERVED(val[0][:value]) ; loc result, val[0] }\n\n#---LITERALS (dynamic and static)\n#\n\narray\n  : LISTSTART collection_entries endcomma  RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] }\n  | LISTSTART                              RBRACK { result = Factory.literal([]) ; loc result, val[0], val[1] }\n  | LBRACK    collection_entries endcomma  RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] }\n  | LBRACK                                 RBRACK { result = Factory.literal([]) ; loc result, val[0], val[1] }\n\nhash\n  : LBRACE hashpairs RBRACE       { result = Factory.HASH(val[1]); loc result, val[0], val[2] }\n  | LBRACE hashpairs COMMA RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[3] }\n  | LBRACE RBRACE                 { result = Factory.literal({}) ; loc result, val[0], val[1] }\n\n  hashpairs\n    : hashpair                 { result = [val[0]] }\n    | hashpairs COMMA hashpair { result = val[0].push val[2] }\n\n  hashpair\n    : hash_entry FARROW hash_entry { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] }\n\ncollection_entry:\n  : argument\n  | collection_entry_keyword { result = Factory.literal(val[0][:value]) ; loc result, val[0] }\n\n# Like collection_entry but will not allow nested hashpair\nhash_entry:\n  : assignment\n  | collection_entry_keyword { result = Factory.literal(val[0][:value]) ; loc result, val[0] }\n\ncollection_entries\n  : collection_entry                           { result = [val[0]] }\n  | collection_entries COMMA collection_entry  { result = Factory.ARGUMENTS(val[0], val[2]) }\n\n# Keywords valid as a collection value\ncollection_entry_keyword\n  : TYPE\n  | FUNCTION\n\nquotedtext\n  : string\n  | dq_string\n  | heredoc\n\nstring\n  : STRING  { result = Factory.literal(val[0][:value]) ; loc result, val[0] }\n  | WORD    { result = Factory.literal(val[0][:value]) ; loc result, val[0] }\n\ndq_string       : dqpre dqrval           { result = Factory.STRING(val[0], *val[1]) ; loc result, val[0], val[1][-1] }\ndqpre           : DQPRE                  { result = Factory.literal(val[0][:value]); loc result, val[0] }\ndqpost          : DQPOST                 { result = Factory.literal(val[0][:value]); loc result, val[0] }\ndqmid           : DQMID                  { result = Factory.literal(val[0][:value]); loc result, val[0] }\ndqrval          : text_expression dqtail { result = [val[0]] + val[1] }\ntext_expression : assignment             { result = Factory.TEXT(val[0]) }\n\ndqtail\n  : dqpost        { result = [val[0]] }\n  | dqmid dqrval  { result = [val[0]] + val[1] }\n\nheredoc\n  : HEREDOC sublocated_text  { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] }\n\nsublocated_text\n  : SUBLOCATE string    { result = Factory.SUBLOCATE(val[0], val[1]); }\n  | SUBLOCATE dq_string { result = Factory.SUBLOCATE(val[0], val[1]); }\n\nepp_expression\n  : EPP_START epp_parameters_list optional_statements { result = Factory.EPP(val[1], val[2]); loc result, val[0] }\n\noptional_statements\n  :\n  | statements\n\nepp_parameters_list\n  :                                  =LOW{ result = nil }\n  | PIPE PIPE                        { result = [] }\n  | PIPE parameters endcomma PIPE    { result = val[1] }\n\nepp_render_expression\n  : RENDER_STRING                  { result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] }\n  | RENDER_EXPR expression epp_end { result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[2] }\n  | RENDER_EXPR LBRACE statements RBRACE epp_end { result = Factory.RENDER_EXPR(Factory.block_or_expression(val[2], val[1], val[3])); loc result, val[0], val[4] }\n\nepp_end\n  : EPP_END\n  | EPP_END_TRIM\n\ntype     : CLASSREF { result = Factory.QREF(val[0][:value])  ; loc result, val[0] }\n\nregex\n  : REGEX { result = Factory.literal(val[0][:value]); loc result, val[0] }\n\n#---MARKERS, SPECIAL TOKENS, SYNTACTIC SUGAR, etc.\n\nendcomma\n  : #\n  | COMMA { result = nil }\n\nendsemi\n  : #\n  | SEMIC\n\nkeyword\n  : AND\n  | CASE\n  | CLASS\n  | DEFAULT\n  | DEFINE\n  | ELSE\n  | ELSIF\n  | IF\n  | IN\n  | INHERITS\n  | NODE\n  | OR\n  | UNDEF\n  | UNLESS\n  | TYPE\n  | ATTR\n  | FUNCTION\n  | PRIVATE\n  | PLAN\n  | APPLY\n\nnil\n  : { result = nil}\n\nend\n\n---- header ----\nrequire_relative '../../../puppet'\nrequire_relative '../../../puppet/pops'\n\nmodule Puppet\n  class ParseError < Puppet::Error; end\n  class ImportError < Racc::ParseError; end\n  class AlreadyImportedError < ImportError; end\nend\n\n---- inner ----\n\n# Make emacs happy\n# Local Variables:\n# mode: ruby\n# End:\n"
  },
  {
    "path": "lib/puppet/pops/parser/eparser.rb",
    "content": "#\n# DO NOT MODIFY!!!!\n# This file is automatically generated by Racc 1.5.2\n# from Racc grammar file \"\".\n#\n\nrequire 'racc/parser.rb'\n\nrequire_relative '../../../puppet'\nrequire_relative '../../../puppet/pops'\n\nmodule Puppet\n  class ParseError < Puppet::Error; end\n  class ImportError < Racc::ParseError; end\n  class AlreadyImportedError < ImportError; end\nend\n\nmodule Puppet\n  module Pops\n    module Parser\n      class Parser < Racc::Parser\n\nmodule_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 885)\n\n# Make emacs happy\n# Local Variables:\n# mode: ruby\n# End:\n...end egrammar.ra/module_eval...\n##### State transition tables begin ###\n\nclist = [\n'69,72,283,-267,70,64,79,65,83,84,85,64,110,65,-276,376,304,163,396,305',\n'65,-145,326,284,20,19,112,-281,115,-279,377,51,111,54,82,60,12,250,57',\n'43,46,271,53,44,10,11,-267,90,77,18,164,147,45,114,77,16,17,-276,86',\n'88,87,89,125,78,-145,327,122,141,148,52,-281,272,-279,42,73,91,75,76',\n'74,251,155,58,48,61,62,55,56,69,72,63,144,70,64,283,65,63,444,125,124',\n'110,166,122,121,283,118,445,77,440,182,439,123,20,19,112,284,115,77',\n'125,51,111,54,122,60,12,284,57,43,46,80,53,44,10,11,124,184,77,18,121',\n'276,45,114,428,16,17,125,123,427,125,122,187,78,122,82,124,125,252,52',\n'121,122,141,42,73,91,75,76,123,263,262,58,48,61,62,55,56,69,72,63,90',\n'70,64,124,65,144,124,121,125,489,121,141,122,124,427,123,90,121,123',\n'125,264,20,19,122,440,123,439,265,51,-225,54,266,60,12,144,57,43,46',\n'147,53,44,10,11,269,124,77,18,270,121,45,263,262,16,17,125,124,123,125',\n'122,121,78,122,263,262,125,274,52,123,122,295,42,73,296,75,76,263,262',\n'302,58,48,61,62,55,56,69,72,63,-226,70,64,124,65,302,124,121,69,72,121',\n'82,70,124,90,123,90,121,123,90,423,20,19,307,306,123,318,319,51,90,54',\n'324,60,12,110,57,43,46,155,53,44,10,11,332,350,77,18,351,112,45,115',\n'353,16,17,111,69,72,357,363,70,78,367,369,77,372,373,52,283,386,387',\n'42,73,266,75,76,114,391,274,58,48,61,62,55,56,69,72,63,397,70,64,110',\n'65,399,372,-225,163,404,406,160,413,414,324,325,417,112,420,115,372',\n'20,19,111,372,147,429,430,51,433,54,78,60,127,110,57,43,46,434,53,44',\n'164,73,437,114,77,18,441,112,45,115,443,16,17,111,69,72,452,453,70,78',\n'455,457,324,461,463,52,324,466,467,42,73,469,75,76,114,473,443,58,48',\n'61,62,55,56,69,72,63,475,70,64,110,65,477,478,479,163,480,332,160,485',\n'283,486,487,488,112,499,115,500,20,19,111,501,503,77,504,51,505,54,78',\n'60,127,284,57,43,46,506,53,44,164,73,353,114,77,18,,316,45,,,16,17,',\n'69,72,,,70,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63',\n',70,64,,65,,,,163,,,160,,,,,,,,,,20,19,,,358,,,51,360,54,78,60,127,283',\n'57,43,46,283,53,44,164,73,,,77,18,77,,45,,77,16,17,,284,,,,284,78,,',\n',,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,',\n',,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,',\n',45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46',\n',53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,',\n',,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,136,,',\n',,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,69,72,',\n',70,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64',\n',65,,,,163,,,160,,,,,,,,,,20,19,,,,,,51,,54,78,60,127,,57,43,46,,53',\n'44,164,73,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58',\n'48,61,62,55,56,69,72,63,110,70,64,,65,,,,,,,,,,112,,115,,,,111,20,19',\n',,,,,51,,54,,60,127,,57,43,46,,53,44,114,,,,77,18,,,45,,,16,17,,,92',\n'93,,,78,,,91,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,110',\n'70,64,,65,,,,,,,,,,112,,115,,,,111,20,19,,,,,,51,,54,,60,127,,57,43',\n'46,,53,44,114,,,,77,18,,,45,,,16,17,,,92,93,,,78,,,91,,,52,,,,42,73',\n',75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,168,65,,,,,,,,,,,,,,,,,20',\n'19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,',\n',,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64',\n'173,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10',\n'11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137',\n'61,62,55,56,69,72,63,,70,64,,65,175,,,,,,,,,,,,,,,,20,19,,,,,,51,,54',\n',60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52',\n',,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,',\n',,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45',\n',,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72',\n'63,110,70,64,,186,,,,,,,,,,112,,115,,,,111,20,19,,,,,,51,,54,,60,127',\n',57,43,46,,53,44,114,,,,77,18,,,45,,,16,17,,,92,93,,,78,,,91,,,52,,',\n',42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,',\n',,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16',\n'17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70',\n'64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10',\n'11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61',\n'62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12',\n',57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73',\n',75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19',\n',,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,',\n',78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65',\n',,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77',\n'18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56',\n'69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43',\n'46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76',\n',,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,',\n'51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,',\n',,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,',\n',,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18',\n',,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46',\n',53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,',\n',,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,204',\n'219,210,220,60,213,222,214,43,202,,206,200,,,,,77,18,223,218,201,,,16',\n'199,,,,,,,78,,,,,221,205,,,,42,73,,75,76,,,,215,203,216,217,211,212',\n'69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43',\n'46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,',\n',58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51',\n',54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52',\n',,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,',\n',,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16',\n'17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70',\n'64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,',\n',,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62',\n'55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127',\n',57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75',\n'76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,',\n',,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,',\n',,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,',\n',,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45',\n',,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72',\n'63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53',\n'44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48',\n'61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60',\n'127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73',\n',75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19',\n',,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78',\n',,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,',\n',,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,',\n'45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46',\n',53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58',\n'48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54',\n',60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,',\n'42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,',\n',,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17',\n',,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64',\n',65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77',\n'18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56',\n'69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43',\n'46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,',\n',58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51',\n',54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52',\n',,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,',\n',,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16',\n'17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70',\n'64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,',\n',,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,245,42,73,,75,76,,,,58,48,61',\n'62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12',\n',57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73',\n',75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20',\n'19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,',\n',78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65',\n',,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18',\n',,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46',\n',53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58',\n'48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,287,,,,,51',\n',54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,',\n',52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,,65,,,,,',\n',,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,',\n',45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56',\n'69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43',\n'46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76',\n',,,58,48,61,62,55,56,69,72,63,,70,64,,65,175,,,,,,,,,,,,,,,,20,19,,',\n',,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78',\n',,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,,65,',\n',325,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77',\n'18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56',\n'69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43',\n'46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,',\n',58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51',\n',54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52',\n',,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,',\n',,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16',\n'17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70',\n'64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,',\n',,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62',\n'55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,',\n'57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73',\n',75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20',\n'19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,',\n',,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64',\n',65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11',\n',,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61',\n'62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12',\n',57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73',\n',75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,379,,,,,,,,,,,,,,,,20',\n'19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,',\n',,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64',\n',65,381,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10',\n'11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61',\n'62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127',\n',57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75',\n'76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,',\n',,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78',\n',,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,,65,400',\n',,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77',\n'18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55',\n'56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57',\n'43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75',\n'76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,',\n',,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,',\n',,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,',\n',,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45',\n',,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72',\n'63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53',\n'44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48',\n'61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60',\n'127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73',\n',75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19',\n',,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,',\n',78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56,69,72,63,,70,64,',\n'65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77',\n'18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56',\n'69,72,63,,70,64,,65,432,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57',\n'43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75',\n'76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,',\n',,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,',\n',,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,',\n',,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18',\n',,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56',\n'69,72,63,,70,64,,65,446,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57',\n'43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76',\n',,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,',\n'51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,',\n',,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,',\n',,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18',\n',,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46',\n',53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,',\n',,138,137,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,',\n',51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,',\n',,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,',\n',,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,',\n'45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46',\n',53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,',\n',,138,137,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,',\n',51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78',\n',,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,',\n',,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18',\n',,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,138,137,61,62,55,56',\n'69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43',\n'46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,',\n',58,48,61,62,55,56,69,72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51',\n',54,,60,127,,57,43,46,,53,44,,,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52',\n',,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,482,,,,,,,',\n',,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45',\n',,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72',\n'63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53',\n'44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58',\n'48,61,62,55,56,69,72,63,,70,64,,65,491,,,,,,,,,,,,,,,,20,19,,,,,,51',\n',54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,',\n',52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,493,,,',\n',,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18',\n',,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69',\n'72,63,,70,64,,65,,,,,,,,,,,,,,,,,20,19,,,,,,51,,54,,60,12,,57,43,46',\n',53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,',\n',,58,48,61,62,55,56,69,72,63,,70,64,,65,498,,,,,,,,,,,,,,,,20,19,,,',\n',,51,,54,,60,12,,57,43,46,,53,44,10,11,,,77,18,,,45,,,16,17,,,,,,,78',\n',,,,,52,,,,42,73,,75,76,,,,58,48,61,62,55,56,69,72,63,,70,64,,65,,,',\n',,,,,,,,,,,,,20,19,,,,,,51,,54,,60,127,,57,43,46,,53,44,,,,,77,18,,',\n'45,,,16,17,,,,,,,78,,,,,,52,,,,42,73,,75,76,110,,,58,48,61,62,55,56',\n',,63,106,101,112,,115,,109,,111,,102,104,103,105,,,,,,,,,,,,,,,,114',\n',,,108,107,,,94,95,97,96,99,100,,92,93,110,,288,,,91,,,,,,,106,101,112',\n',115,,109,,111,,102,104,103,105,,,98,,,,,,,,,,,,,114,,,,108,107,,,94',\n'95,97,96,99,100,,92,93,110,,289,,,91,,,,,,,106,101,112,,115,,109,,111',\n',102,104,103,105,,,98,,,,,,,,,,,,,114,,,,108,107,,,94,95,97,96,99,100',\n',92,93,110,,290,,,91,,,,,,,106,101,112,,115,,109,,111,,102,104,103,105',\n',,98,,,,,,,,,,,,,114,,,,108,107,,110,94,95,97,96,99,100,,92,93,,,106',\n'101,112,91,115,,109,,111,,102,104,103,105,,,,,,,,,,,,,98,,,114,,,,108',\n'107,,,94,95,97,96,99,100,,92,93,,,,,,91,,110,,,,,,,,,318,319,,106,101',\n'112,322,115,110,109,,111,98,102,104,103,105,,,,,,112,,115,,,,111,,,',\n'114,,,,108,107,,,94,95,97,96,99,100,,92,93,114,,,110,,91,,,,,97,96,',\n',,92,93,112,,115,110,,91,111,,,,,98,,,,,,112,,115,,,,111,,,,114,98,',\n',,,,,,,97,96,,,,92,93,114,,,110,,91,,,94,95,97,96,,,,92,93,112,,115',\n'110,,91,111,,,,,98,,,,,,112,,115,,,,111,,,,114,98,,,,,,,94,95,97,96',\n',,,92,93,114,,,110,,91,,,94,95,97,96,99,100,,92,93,112,,115,110,,91',\n'111,,,,,98,,,,,101,112,,115,,,,111,,102,,114,98,,,,,,,94,95,97,96,99',\n'100,,92,93,114,,,,,91,,110,94,95,97,96,99,100,,92,93,,,,101,112,91,115',\n'110,,,111,98,102,,,,,,,,101,112,,115,,,,111,98,102,,114,,,,,,,,94,95',\n'97,96,99,100,,92,93,114,,,,,91,,110,94,95,97,96,99,100,,92,93,,,,101',\n'112,91,115,,,,111,98,102,,,,,,,,,,,,,,,,98,,,114,,,,,110,,,94,95,97',\n'96,99,100,,92,93,106,101,112,,115,91,109,,111,,102,104,103,105,,,,,',\n',,,,,,,,,98,114,,,,,110,,,94,95,97,96,99,100,,92,93,106,101,112,,115',\n'91,109,,111,,102,104,103,105,,,,,,,,,,,,,,,98,114,,,,,107,,,94,95,97',\n'96,99,100,110,92,93,,,328,,,91,,,,106,101,112,,115,,109,,111,,102,104',\n'103,105,,,,,,98,,,,,,,,,,114,,,,108,107,,,94,95,97,96,99,100,,92,93',\n'110,-65,,,,91,-65,,,,,,106,101,112,,115,,109,,111,,102,104,103,105,',\n',98,,,,,,,,,,,,,114,,,,108,107,,110,94,95,97,96,99,100,,92,93,,,106',\n'101,112,91,115,,109,,111,,102,104,103,105,,,,,,,,,,,,,98,,,114,,,,108',\n'107,,,94,95,97,96,99,100,110,92,93,,,,,,91,,,,106,101,112,354,115,,109',\n',111,,102,104,103,105,,,,,,98,,,,,,,,,,114,,,,108,107,,110,94,95,97',\n'96,99,100,,92,93,,,106,101,112,91,115,,109,,111,,102,104,103,105,,,',\n',,,,,,,,,98,,,114,,,,108,107,,110,94,95,97,96,99,100,,92,93,,,106,101',\n'112,91,115,,109,,111,,102,104,103,105,,,,,,,,,,,,,98,,,114,,,,108,107',\n',110,94,95,97,96,99,100,,92,93,,,106,101,112,91,115,,109,,111,,102,104',\n'103,105,,,,,,,,,,,,,98,,,114,,,,108,107,,110,94,95,97,96,99,100,,92',\n'93,,,106,101,112,91,115,,109,,111,,102,104,103,105,,,,,,,,,,,,,98,,',\n'114,,,,108,107,,110,94,95,97,96,99,100,,92,93,,,106,101,112,91,115,',\n'109,,111,,102,104,103,105,,,,,,,,,,,,,98,,,114,,,,108,107,,110,94,95',\n'97,96,99,100,,92,93,,,106,101,112,91,115,,109,,111,,102,104,103,105',\n',,,,,,,,,,,,98,,,114,,,,108,107,,110,94,95,97,96,99,100,,92,93,,,106',\n'101,112,91,115,,109,,111,,102,104,103,105,,,,,,,,,,,,,98,,,114,,,,108',\n'107,,,94,95,97,96,99,100,,92,93,,340,219,339,220,91,337,222,341,,334',\n',336,338,,,,,,,223,218,342,,,,335,,98,,,,,,,,,,221,343,,,,,,,,,,,,346',\n'344,347,345,348,349,340,219,339,220,,337,222,341,,334,,336,338,,,,,',\n',223,218,342,,,,335,,,,,,,,,,,,221,343,,,,,,,,,,,,346,344,347,345,348',\n'349,340,219,339,220,,337,222,341,,334,,336,338,,,,,,,223,218,342,,,',\n'335,,,,,,,,,,,,221,343,,,,,,,,,,,,346,344,347,345,348,349,340,219,339',\n'220,,337,222,341,,334,,336,338,,,,,,,223,218,342,,,,335,,,,,,,,,,,,221',\n'343,,,,,,,,,,,,346,344,347,345,348,349' ]\n        racc_action_table = arr = ::Array.new(9957, nil)\n        idx = 0\n        clist.each do |str|\n          str.split(',', -1).each do |i|\n            arr[idx] = i.to_i unless i.empty?\n            idx += 1\n          end\n        end\n\nclist = [\n'0,0,285,200,0,0,1,0,7,7,7,166,225,166,201,286,174,296,298,174,298,202',\n'209,285,0,0,225,216,225,217,286,0,225,0,5,0,0,112,0,0,0,144,0,0,0,0',\n'200,8,0,0,296,49,0,225,166,0,0,201,7,7,7,7,54,0,202,209,54,48,50,0,216',\n'144,217,0,0,225,0,0,0,112,56,0,0,0,0,0,0,4,4,0,48,4,4,372,4,166,383',\n'12,54,226,59,12,54,147,12,383,372,379,73,379,54,4,4,226,372,226,147',\n'55,4,226,4,55,4,4,147,4,4,4,4,4,4,4,4,12,74,4,4,12,147,4,226,365,4,4',\n'58,12,365,127,58,79,4,127,81,55,138,113,4,55,138,137,4,4,226,4,4,55',\n'119,119,4,4,4,4,4,4,10,10,4,116,10,10,58,10,137,127,58,210,470,127,203',\n'210,138,470,58,117,138,127,211,130,10,10,211,437,138,437,131,10,132',\n'10,135,10,10,203,10,10,10,136,10,10,10,10,139,210,10,10,142,210,10,153',\n'153,10,10,213,211,210,215,213,211,10,215,154,154,357,146,10,211,357',\n'156,10,10,158,10,10,165,165,167,10,10,10,10,10,10,11,11,10,170,11,11',\n'213,11,172,215,213,182,182,215,188,182,357,193,213,194,357,215,195,357',\n'11,11,179,179,357,404,404,11,196,11,198,11,11,126,11,11,11,212,11,11',\n'11,11,247,254,11,11,256,126,11,126,257,11,11,126,57,57,260,267,57,11',\n'272,273,274,277,283,11,284,291,292,11,11,293,11,11,126,294,297,11,11',\n'11,11,11,11,16,16,11,301,16,16,128,16,303,315,320,57,321,323,57,329',\n'331,333,335,352,128,355,128,359,16,16,128,361,363,366,367,16,370,16',\n'57,16,16,129,16,16,16,371,16,16,57,57,378,128,16,16,380,129,16,129,381',\n'16,16,129,214,214,388,389,214,16,394,403,405,412,416,16,419,424,425',\n'16,16,431,16,16,129,440,441,16,16,16,16,16,16,17,17,16,443,17,17,224',\n'17,445,448,451,214,452,456,214,459,184,460,465,468,224,481,224,483,17',\n'17,224,484,490,184,492,17,494,17,214,17,17,184,17,17,17,497,17,17,214',\n'214,502,224,17,17,,184,17,,,17,17,,295,295,,,295,17,,,,,,17,,,,17,17',\n',17,17,,,,17,17,17,17,17,17,18,18,17,,18,18,,18,,,,295,,,295,,,,,,,',\n',,18,18,,,262,,,18,263,18,295,18,18,262,18,18,18,263,18,18,295,295,',\n',18,18,262,,18,,263,18,18,,262,,,,263,18,,,,,,18,,,,18,18,,18,18,,,',\n'18,18,18,18,18,18,19,19,18,,19,19,,19,,,,,,,,,,,,,,,,,19,19,,,,,,19',\n',19,,19,19,,19,19,19,,19,19,19,19,,,19,19,,,19,,,19,19,,,,,,,19,,,,',\n',19,,,,19,19,,19,19,,,,19,19,19,19,19,19,20,20,19,,20,20,,20,,,,,,,',\n',,,,,,,,,20,20,,,,,,20,,20,,20,20,,20,20,20,,20,20,20,20,,,20,20,,,20',\n',,20,20,,,,,,,20,,,,,,20,,,,20,20,,20,20,,,,20,20,20,20,20,20,47,47',\n'20,,47,47,,47,,,,,,,,,,,,,,,,,47,47,47,,,,,47,,47,,47,47,,47,47,47,',\n'47,47,47,47,,,47,47,,,47,,,47,47,,391,391,,,391,47,,,,,,47,,,,47,47',\n',47,47,,,,47,47,47,47,47,47,51,51,47,,51,51,,51,,,,391,,,391,,,,,,,',\n',,51,51,,,,,,51,,51,391,51,51,,51,51,51,,51,51,391,391,,,51,51,,,51',\n',,51,51,,,,,,,51,,,,,,51,,,,51,51,,51,51,,,,51,51,51,51,51,51,52,52',\n'51,229,52,52,,52,,,,,,,,,,229,,229,,,,229,52,52,,,,,,52,,52,,52,52,',\n'52,52,52,,52,52,229,,,,52,52,,,52,,,52,52,,,229,229,,,52,,,229,,,52',\n',,,52,52,,52,52,,,,52,52,52,52,52,52,53,53,52,230,53,53,,53,,,,,,,,',\n',230,,230,,,,230,53,53,,,,,,53,,53,,53,53,,53,53,53,,53,53,230,,,,53',\n'53,,,53,,,53,53,,,230,230,,,53,,,230,,,53,,,,53,53,,53,53,,,,53,53,53',\n'53,53,53,63,63,53,,63,63,63,63,,,,,,,,,,,,,,,,,63,63,,,,,,63,,63,,63',\n'63,,63,63,63,,63,63,63,63,,,63,63,,,63,,,63,63,,,,,,,63,,,,,,63,,,,63',\n'63,,63,63,,,,63,63,63,63,63,63,64,64,63,,64,64,64,64,,,,,,,,,,,,,,,',\n',64,64,,,,,,64,,64,,64,64,,64,64,64,,64,64,64,64,,,64,64,,,64,,,64,64',\n',,,,,,64,,,,,,64,,,,64,64,,64,64,,,,64,64,64,64,64,64,65,65,64,,65,65',\n',65,65,,,,,,,,,,,,,,,,65,65,,,,,,65,,65,,65,65,,65,65,65,,65,65,65,65',\n',,65,65,,,65,,,65,65,,,,,,,65,,,,,,65,,,,65,65,,65,65,,,,65,65,65,65',\n'65,65,71,71,65,,71,71,,71,,,,,,,,,,,,,,,,,71,71,,,,,,71,,71,,71,71,',\n'71,71,71,,71,71,71,71,,,71,71,,,71,,,71,71,,,,,,,71,,,,,,71,,,,71,71',\n',71,71,,,,71,71,71,71,71,71,76,76,71,231,76,76,,76,,,,,,,,,,231,,231',\n',,,231,76,76,,,,,,76,,76,,76,76,,76,76,76,,76,76,231,,,,76,76,,,76,',\n',76,76,,,231,231,,,76,,,231,,,76,,,,76,76,,76,76,,,,76,76,76,76,76,76',\n'80,80,76,,80,80,,80,,,,,,,,,,,,,,,,,80,80,,,,,,80,,80,,80,80,,80,80',\n'80,,80,80,80,80,,,80,80,,,80,,,80,80,,,,,,,80,,,,,,80,,,,80,80,,80,80',\n',,,80,80,80,80,80,80,82,82,80,,82,82,,82,,,,,,,,,,,,,,,,,82,82,,,,,',\n'82,,82,,82,82,,82,82,82,,82,82,82,82,,,82,82,,,82,,,82,82,,,,,,,82,',\n',,,,82,,,,82,82,,82,82,,,,82,82,82,82,82,82,83,83,82,,83,83,,83,,,,',\n',,,,,,,,,,,,83,83,,,,,,83,,83,,83,83,,83,83,83,,83,83,83,83,,,83,83',\n',,83,,,83,83,,,,,,,83,,,,,,83,,,,83,83,,83,83,,,,83,83,83,83,83,83,84',\n'84,83,,84,84,,84,,,,,,,,,,,,,,,,,84,84,,,,,,84,,84,,84,84,,84,84,84',\n',84,84,84,84,,,84,84,,,84,,,84,84,,,,,,,84,,,,,,84,,,,84,84,,84,84,',\n',,84,84,84,84,84,84,85,85,84,,85,85,,85,,,,,,,,,,,,,,,,,85,85,,,,,,85',\n',85,,85,85,,85,85,85,,85,85,85,85,,,85,85,,,85,,,85,85,,,,,,,85,,,,',\n',85,,,,85,85,,85,85,,,,85,85,85,85,85,85,86,86,85,,86,86,,86,,,,,,,',\n',,,,,,,,,86,86,,,,,,86,,86,,86,86,,86,86,86,,86,86,86,86,,,86,86,,,86',\n',,86,86,,,,,,,86,,,,,,86,,,,86,86,,86,86,,,,86,86,86,86,86,86,87,87',\n'86,,87,87,,87,,,,,,,,,,,,,,,,,87,87,,,,,,87,,87,,87,87,,87,87,87,,87',\n'87,87,87,,,87,87,,,87,,,87,87,,,,,,,87,,,,,,87,,,,87,87,,87,87,,,,87',\n'87,87,87,87,87,88,88,87,,88,88,,88,,,,,,,,,,,,,,,,,88,88,,,,,,88,,88',\n',88,88,,88,88,88,,88,88,88,88,,,88,88,,,88,,,88,88,,,,,,,88,,,,,,88',\n',,,88,88,,88,88,,,,88,88,88,88,88,88,89,89,88,,89,89,,89,,,,,,,,,,,',\n',,,,,89,89,,,,,,89,,89,,89,89,,89,89,89,,89,89,89,89,,,89,89,,,89,,',\n'89,89,,,,,,,89,,,,,,89,,,,89,89,,89,89,,,,89,89,89,89,89,89,90,90,89',\n',90,90,,90,,,,,,,,,,,,,,,,,90,90,,,,,,90,90,90,90,90,90,90,90,90,90',\n',90,90,,,,,90,90,90,90,90,,,90,90,,,,,,,90,,,,,90,90,,,,90,90,,90,90',\n',,,90,90,90,90,90,90,91,91,90,,91,91,,91,,,,,,,,,,,,,,,,,91,91,,,,,',\n'91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,,,91,,,91,91,,,,,,,91,,,,,',\n'91,,,,91,91,,91,91,,,,91,91,91,91,91,91,92,92,91,,92,92,,92,,,,,,,,',\n',,,,,,,,92,92,,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,,,92,,,92',\n'92,,,,,,,92,,,,,,92,,,,92,92,,92,92,,,,92,92,92,92,92,92,93,93,92,,93',\n'93,,93,,,,,,,,,,,,,,,,,93,93,,,,,,93,,93,,93,93,,93,93,93,,93,93,,,',\n',93,93,,,93,,,93,93,,,,,,,93,,,,,,93,,,,93,93,,93,93,,,,93,93,93,93',\n'93,93,94,94,93,,94,94,,94,,,,,,,,,,,,,,,,,94,94,,,,,,94,,94,,94,94,',\n'94,94,94,,94,94,,,,,94,94,,,94,,,94,94,,,,,,,94,,,,,,94,,,,94,94,,94',\n'94,,,,94,94,94,94,94,94,95,95,94,,95,95,,95,,,,,,,,,,,,,,,,,95,95,,',\n',,,95,,95,,95,95,,95,95,95,,95,95,,,,,95,95,,,95,,,95,95,,,,,,,95,,',\n',,,95,,,,95,95,,95,95,,,,95,95,95,95,95,95,96,96,95,,96,96,,96,,,,,',\n',,,,,,,,,,,96,96,,,,,,96,,96,,96,96,,96,96,96,,96,96,,,,,96,96,,,96',\n',,96,96,,,,,,,96,,,,,,96,,,,96,96,,96,96,,,,96,96,96,96,96,96,97,97',\n'96,,97,97,,97,,,,,,,,,,,,,,,,,97,97,,,,,,97,,97,,97,97,,97,97,97,,97',\n'97,,,,,97,97,,,97,,,97,97,,,,,,,97,,,,,,97,,,,97,97,,97,97,,,,97,97',\n'97,97,97,97,98,98,97,,98,98,,98,,,,,,,,,,,,,,,,,98,98,,,,,,98,,98,,98',\n'98,,98,98,98,,98,98,,,,,98,98,,,98,,,98,98,,,,,,,98,,,,,,98,,,,98,98',\n',98,98,,,,98,98,98,98,98,98,99,99,98,,99,99,,99,,,,,,,,,,,,,,,,,99,99',\n',,,,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99,,,99,,,99,99,,,,,,,99',\n',,,,,99,,,,99,99,,99,99,,,,99,99,99,99,99,99,100,100,99,,100,100,,100',\n',,,,,,,,,,,,,,,,100,100,,,,,,100,,100,,100,100,,100,100,100,,100,100',\n',,,,100,100,,,100,,,100,100,,,,,,,100,,,,,,100,,,,100,100,,100,100,',\n',,100,100,100,100,100,100,101,101,100,,101,101,,101,,,,,,,,,,,,,,,,',\n'101,101,,,,,,101,,101,,101,101,,101,101,101,,101,101,,,,,101,101,,,101',\n',,101,101,,,,,,,101,,,,,,101,,,,101,101,,101,101,,,,101,101,101,101',\n'101,101,102,102,101,,102,102,,102,,,,,,,,,,,,,,,,,102,102,,,,,,102,',\n'102,,102,102,,102,102,102,,102,102,,,,,102,102,,,102,,,102,102,,,,,',\n',102,,,,,,102,,,,102,102,,102,102,,,,102,102,102,102,102,102,103,103',\n'102,,103,103,,103,,,,,,,,,,,,,,,,,103,103,,,,,,103,,103,,103,103,,103',\n'103,103,,103,103,,,,,103,103,,,103,,,103,103,,,,,,,103,,,,,,103,,,,103',\n'103,,103,103,,,,103,103,103,103,103,103,104,104,103,,104,104,,104,,',\n',,,,,,,,,,,,,,104,104,,,,,,104,,104,,104,104,,104,104,104,,104,104,',\n',,,104,104,,,104,,,104,104,,,,,,,104,,,,,,104,,,,104,104,,104,104,,',\n',104,104,104,104,104,104,105,105,104,,105,105,,105,,,,,,,,,,,,,,,,,105',\n'105,,,,,,105,,105,,105,105,,105,105,105,,105,105,,,,,105,105,,,105,',\n',105,105,,,,,,,105,,,,,,105,,,,105,105,,105,105,,,,105,105,105,105,105',\n'105,106,106,105,,106,106,,106,,,,,,,,,,,,,,,,,106,106,,,,,,106,,106',\n',106,106,,106,106,106,,106,106,,,,,106,106,,,106,,,106,106,,,,,,,106',\n',,,,,106,,,,106,106,,106,106,,,,106,106,106,106,106,106,107,107,106',\n',107,107,,107,,,,,,,,,,,,,,,,,107,107,,,,,,107,,107,,107,107,,107,107',\n'107,,107,107,,,,,107,107,,,107,,,107,107,,,,,,,107,,,,,,107,,,,107,107',\n',107,107,,,,107,107,107,107,107,107,108,108,107,,108,108,,108,,,,,,',\n',,,,,,,,,,108,108,,,,,,108,,108,,108,108,,108,108,108,,108,108,,,,,108',\n'108,,,108,,,108,108,,,,,,,108,,,,,,108,,,,108,108,,108,108,,,,108,108',\n'108,108,108,108,109,109,108,,109,109,,109,,,,,,,,,,,,,,,,,109,109,,',\n',,,109,,109,,109,109,,109,109,109,,109,109,,,,,109,109,,,109,,,109,109',\n',,,,,,109,,,,,,109,,,109,109,109,,109,109,,,,109,109,109,109,109,109',\n'110,110,109,,110,110,,110,,,,,,,,,,,,,,,,,110,110,,,,,,110,,110,,110',\n'110,,110,110,110,,110,110,110,110,,,110,110,,,110,,,110,110,,,,,,,110',\n',,,,,110,,,,110,110,,110,110,,,,110,110,110,110,110,110,114,114,110',\n',114,114,,114,,,,,,,,,,,,,,,,,114,114,,,,,,114,,114,,114,114,,114,114',\n'114,,114,114,,,,,114,114,,,114,,,114,114,,,,,,,114,,,,,,114,,,,114,114',\n',114,114,,,,114,114,114,114,114,114,115,115,114,,115,115,,115,,,,,,',\n',,,,,,,,,,115,115,,,,,,115,,115,,115,115,,115,115,115,,115,115,,,,,115',\n'115,,,115,,,115,115,,,,,,,115,,,,,,115,,,,115,115,,115,115,,,,115,115',\n'115,115,115,115,118,118,115,,118,118,,118,,,,,,,,,,,,,,,,,118,118,,',\n',,,118,,118,,118,118,,118,118,118,,118,118,,,,,118,118,,,118,,,118,118',\n',,,,,,118,,,,,,118,,,,118,118,,118,118,,,,118,118,118,118,118,118,148',\n'148,118,,148,148,,148,,,,,,,,,,,,,,,,,148,148,148,,,,,148,,148,,148',\n'148,,148,148,148,,148,148,148,148,,,148,148,,,148,,,148,148,,,,,,,148',\n',,,,,148,,,,148,148,,148,148,,,,148,148,148,148,148,148,155,155,148',\n',155,155,,155,,,,,,,,,,,,,,,,,155,155,,,,,,155,,155,,155,155,,155,155',\n'155,,155,155,155,155,,,155,155,,,155,,,155,155,,,,,,,155,,,,,,155,,',\n',155,155,,155,155,,,,155,155,155,155,155,155,183,183,155,,183,183,,183',\n',,,,,,,,,,,,,,,,183,183,,,,,,183,,183,,183,183,,183,183,183,,183,183',\n'183,183,,,183,183,,,183,,,183,183,,,,,,,183,,,,,,183,,,,183,183,,183',\n'183,,,,183,183,183,183,183,183,186,186,183,,186,186,,186,186,,,,,,,',\n',,,,,,,,186,186,,,,,,186,,186,,186,186,,186,186,186,,186,186,186,186',\n',,186,186,,,186,,,186,186,,,,,,,186,,,,,,186,,,,186,186,,186,186,,,',\n'186,186,186,186,186,186,199,199,186,,199,199,,199,,,199,,,,,,,,,,,,',\n',199,199,,,,,,199,,199,,199,199,,199,199,199,,199,199,,,,,199,199,,',\n'199,,,199,199,,,,,,,199,,,,,,199,,,,199,199,,199,199,,,,199,199,199',\n'199,199,199,204,204,199,,204,204,,204,,,,,,,,,,,,,,,,,204,204,,,,,,204',\n',204,,204,204,,204,204,204,,204,204,,,,,204,204,,,204,,,204,204,,,,',\n',,204,,,,,,204,,,,204,204,,204,204,,,,204,204,204,204,204,204,205,205',\n'204,,205,205,,205,,,,,,,,,,,,,,,,,205,205,,,,,,205,,205,,205,205,,205',\n'205,205,,205,205,,,,,205,205,,,205,,,205,205,,,,,,,205,,,,,,205,,,,205',\n'205,,205,205,,,,205,205,205,205,205,205,206,206,205,,206,206,,206,,',\n',,,,,,,,,,,,,,206,206,,,,,,206,,206,,206,206,,206,206,206,,206,206,',\n',,,206,206,,,206,,,206,206,,,,,,,206,,,,,,206,,,,206,206,,206,206,,',\n',206,206,206,206,206,206,245,245,206,,245,245,,245,,,,,,,,,,,,,,,,,245',\n'245,,,,,,245,,245,,245,245,,245,245,245,,245,245,,,,,245,245,,,245,',\n',245,245,,,,,,,245,,,,,,245,,,,245,245,,245,245,,,,245,245,245,245,245',\n'245,266,266,245,,266,266,,266,,,,,,,,,,,,,,,,,266,266,,,,,,266,,266',\n',266,266,,266,266,266,,266,266,266,266,,,266,266,,,266,,,266,266,,,',\n',,,266,,,,,,266,,,,266,266,,266,266,,,,266,266,266,266,266,266,269,269',\n'266,,269,269,,269,,,,,,,,,,,,,,,,,269,269,,,,,,269,,269,,269,269,,269',\n'269,269,,269,269,269,269,,,269,269,,,269,,,269,269,,,,,,,269,,,,,,269',\n',,,269,269,,269,269,,,,269,269,269,269,269,269,270,270,269,,270,270',\n',270,,,,,,,,,,,,,,,,,270,270,,,,,,270,,270,,270,270,,270,270,270,,270',\n'270,270,270,,,270,270,,,270,,,270,270,,,,,,,270,,,,,,270,,,,270,270',\n',270,270,,,,270,270,270,270,270,270,271,271,270,,271,271,,271,,,,,,',\n',,,,,,,,,,271,271,,,,,,271,,271,,271,271,,271,271,271,,271,271,271,271',\n',,271,271,,,271,,,271,271,,,,,,,271,,,,,,271,,,,271,271,,271,271,,,',\n'271,271,271,271,271,271,288,288,271,,288,288,,288,288,,,,,,,,,,,,,,',\n',288,288,,,,,,288,,288,,288,288,,288,288,288,,288,288,288,288,,,288',\n'288,,,288,,,288,288,,,,,,,288,,,,,,288,,,,288,288,,288,288,,,,288,288',\n'288,288,288,288,289,289,288,,289,289,,289,289,,,,,,,,,,,,,,,,289,289',\n',,,,,289,,289,,289,289,,289,289,289,,289,289,289,289,,,289,289,,,289',\n',,289,289,,,,,,,289,,,,,,289,,,,289,289,,289,289,,,,289,289,289,289',\n'289,289,290,290,289,,290,290,,290,,,,,,,,,,,,,,,,,290,290,,,,,,290,',\n'290,,290,290,,290,290,290,,290,290,,,,,290,290,,,290,,,290,290,,,,,',\n',290,,,,,,290,,,,290,290,,290,290,,,,290,290,290,290,290,290,302,302',\n'290,,302,302,,302,,,,,,,,,,,,,,,,,302,302,,,,,,302,,302,,302,302,,302',\n'302,302,,302,302,302,302,,,302,302,,,302,,,302,302,,,,,,,302,,,,,,302',\n',,,302,302,,302,302,,,,302,302,302,302,302,302,305,305,302,,305,305',\n',305,305,,,,,,,,,,,,,,,,305,305,,,,,,305,,305,,305,305,,305,305,305',\n',305,305,305,305,,,305,305,,,305,,,305,305,,,,,,,305,,,,,,305,,,,305',\n'305,,305,305,,,,305,305,305,305,305,305,310,310,305,,310,310,,310,,',\n',,,,,,,,,,,,,,310,310,,,,,,310,,310,,310,310,,310,310,310,,310,310,310',\n'310,,,310,310,,,310,,,310,310,,,,,,,310,,,,,,310,,,,310,310,,310,310',\n',,,310,310,310,310,310,310,325,325,310,,325,325,,325,,,,,,,,,,,,,,,',\n',325,325,,,,,,325,,325,,325,325,,325,325,325,,325,325,,,,,325,325,,',\n'325,,,325,325,,,,,,,325,,,,,,325,,,,325,325,,325,325,,,,325,325,325',\n'325,325,325,326,326,325,,326,326,,326,,,,,,,,,,,,,,,,,326,326,,,,,,326',\n',326,,326,326,,326,326,326,,326,326,,,,,326,326,,,326,,,326,326,,,,',\n',,326,,,,,,326,,,,326,326,,326,326,,,,326,326,326,326,326,326,327,327',\n'326,,327,327,,327,,,,,,,,,,,,,,,,,327,327,,,,,,327,,327,,327,327,,327',\n'327,327,,327,327,,,,,327,327,,,327,,,327,327,,,,,,,327,,,,,,327,,,,327',\n'327,,327,327,,,,327,327,327,327,327,327,328,328,327,,328,328,,328,,',\n',,,,,,,,,,,,,,328,328,,,,,,328,,328,,328,328,,328,328,328,,328,328,',\n',,,328,328,,,328,,,328,328,,,,,,,328,,,,,,328,,,,328,328,,328,328,,',\n',328,328,328,328,328,328,332,332,328,,332,332,,332,,,,,,,,,,,,,,,,,332',\n'332,,,,,,332,,332,,332,332,,332,332,332,,332,332,332,332,,,332,332,',\n',332,,,332,332,,,,,,,332,,,,,,332,,,,332,332,,332,332,,,,332,332,332',\n'332,332,332,353,353,332,,353,353,,353,,,,,,,,,,,,,,,,,353,353,,,,,,353',\n',353,,353,353,,353,353,353,,353,353,,,,,353,353,,,353,,,353,353,,,,',\n',,353,,,,,,353,,,,353,353,,353,353,,,,353,353,353,353,353,353,369,369',\n'353,,369,369,,369,369,,,,,,,,,,,,,,,,369,369,,,,,,369,,369,,369,369',\n',369,369,369,,369,369,369,369,,,369,369,,,369,,,369,369,,,,,,,369,,',\n',,,369,,,,369,369,,369,369,,,,369,369,369,369,369,369,373,373,369,,373',\n'373,,373,,,,,,,,,,,,,,,,,373,373,,,,,,373,,373,,373,373,,373,373,373',\n',373,373,,,,,373,373,,,373,,,373,373,,,,,,,373,,,,,,373,,,,373,373,',\n'373,373,,,,373,373,373,373,373,373,376,376,373,,376,376,,376,,,,,,,',\n',,,,,,,,,376,376,,,,,,376,,376,,376,376,,376,376,376,,376,376,376,376',\n',,376,376,,,376,,,376,376,,,,,,,376,,,,,,376,,,,376,376,,376,376,,,',\n'376,376,376,376,376,376,384,384,376,,384,384,,384,384,,,,,,,,,,,,,,',\n',384,384,,,,,,384,,384,,384,384,,384,384,384,,384,384,,,,,384,384,,',\n'384,,,384,384,,,,,,,384,,,,,,384,,,,384,384,,384,384,,,,384,384,384',\n'384,384,384,386,386,384,,386,386,,386,,,,,,,,,,,,,,,,,386,386,,,,,,386',\n',386,,386,386,,386,386,386,,386,386,386,386,,,386,386,,,386,,,386,386',\n',,,,,,386,,,,,,386,,,,386,386,,386,386,,,,386,386,386,386,386,386,387',\n'387,386,,387,387,,387,,,,,,,,,,,,,,,,,387,387,,,,,,387,,387,,387,387',\n',387,387,387,,387,387,387,387,,,387,387,,,387,,,387,387,,,,,,,387,,',\n',,,387,,,,387,387,,387,387,,,,387,387,387,387,387,387,396,396,387,,396',\n'396,,396,,,,,,,,,,,,,,,,,396,396,,,,,,396,,396,,396,396,,396,396,396',\n',396,396,396,396,,,396,396,,,396,,,396,396,,,,,,,396,,,,,,396,,,,396',\n'396,,396,396,,,,396,396,396,396,396,396,413,413,396,,413,413,,413,,',\n',,,,,,,,,,,,,,413,413,,,,,,413,,413,,413,413,,413,413,413,,413,413,',\n',,,413,413,,,413,,,413,413,,,,,,,413,,,,,,413,,,,413,413,,413,413,,',\n',413,413,413,413,413,413,420,420,413,,420,420,,420,,,,,,,,,,,,,,,,,420',\n'420,,,,,,420,,420,,420,420,,420,420,420,,420,420,420,420,,,420,420,',\n',420,,,420,420,,,,,,,420,,,,,,420,,,,420,420,,420,420,,,,420,420,420',\n'420,420,420,427,427,420,,427,427,,427,,,,,,,,,,,,,,,,,427,427,,,,,,427',\n',427,,427,427,,427,427,427,,427,427,427,427,,,427,427,,,427,,,427,427',\n',,,,,,427,,,,,,427,,,,427,427,,427,427,,,,427,427,427,427,427,427,430',\n'430,427,,430,430,,430,,,,,,,,,,,,,,,,,430,430,,,,,,430,,430,,430,430',\n',430,430,430,,430,430,430,430,,,430,430,,,430,,,430,430,,,,,,,430,,',\n',,,430,,,,430,430,,430,430,,,,430,430,430,430,430,430,433,433,430,,433',\n'433,,433,,,,,,,,,,,,,,,,,433,433,,,,,,433,,433,,433,433,,433,433,433',\n',433,433,433,433,,,433,433,,,433,,,433,433,,,,,,,433,,,,,,433,,,,433',\n'433,,433,433,,,,433,433,433,433,433,433,439,439,433,,439,439,,439,,',\n',,,,,,,,,,,,,,439,439,,,,,,439,,439,,439,439,,439,439,439,,439,439,',\n',,,439,439,,,439,,,439,439,,,,,,,439,,,,,,439,,,,439,439,,439,439,,',\n',439,439,439,439,439,439,444,444,439,,444,444,,444,,,,,,,,,,,,,,,,,444',\n'444,,,,,,444,,444,,444,444,,444,444,444,,444,444,,,,,444,444,,,444,',\n',444,444,,,,,,,444,,,,,,444,,,,444,444,,444,444,,,,444,444,444,444,444',\n'444,453,453,444,,453,453,,453,453,,,,,,,,,,,,,,,,453,453,,,,,,453,,453',\n',453,453,,453,453,453,,453,453,453,453,,,453,453,,,453,,,453,453,,,',\n',,,453,,,,,,453,,,,453,453,,453,453,,,,453,453,453,453,453,453,455,455',\n'453,,455,455,,455,,,,,,,,,,,,,,,,,455,455,,,,,,455,,455,,455,455,,455',\n'455,455,,455,455,455,455,,,455,455,,,455,,,455,455,,,,,,,455,,,,,,455',\n',,,455,455,,455,455,,,,455,455,455,455,455,455,473,473,455,,473,473',\n',473,473,,,,,,,,,,,,,,,,473,473,,,,,,473,,473,,473,473,,473,473,473',\n',473,473,473,473,,,473,473,,,473,,,473,473,,,,,,,473,,,,,,473,,,,473',\n'473,,473,473,,,,473,473,473,473,473,473,475,475,473,,475,475,,475,475',\n',,,,,,,,,,,,,,,475,475,,,,,,475,,475,,475,475,,475,475,475,,475,475',\n'475,475,,,475,475,,,475,,,475,475,,,,,,,475,,,,,,475,,,,475,475,,475',\n'475,,,,475,475,475,475,475,475,477,477,475,,477,477,,477,,,,,,,,,,,',\n',,,,,477,477,,,,,,477,,477,,477,477,,477,477,477,,477,477,477,477,,',\n'477,477,,,477,,,477,477,,,,,,,477,,,,,,477,,,,477,477,,477,477,,,,477',\n'477,477,477,477,477,480,480,477,,480,480,,480,480,,,,,,,,,,,,,,,,480',\n'480,,,,,,480,,480,,480,480,,480,480,480,,480,480,480,480,,,480,480,',\n',480,,,480,480,,,,,,,480,,,,,,480,,,,480,480,,480,480,,,,480,480,480',\n'480,480,480,486,486,480,,486,486,,486,,,,,,,,,,,,,,,,,486,486,,,,,,486',\n',486,,486,486,,486,486,486,,486,486,,,,,486,486,,,486,,,486,486,,,,',\n',,486,,,,,,486,,,,486,486,,486,486,9,,,486,486,486,486,486,486,,,486',\n'9,9,9,,9,,9,,9,,9,9,9,9,,,,,,,,,,,,,,,,9,,,,9,9,,,9,9,9,9,9,9,,9,9,149',\n',149,,,9,,,,,,,149,149,149,,149,,149,,149,,149,149,149,149,,,9,,,,,',\n',,,,,,,149,,,,149,149,,,149,149,149,149,149,149,,149,149,151,,151,,',\n'149,,,,,,,151,151,151,,151,,151,,151,,151,151,151,151,,,149,,,,,,,,',\n',,,,151,,,,151,151,,,151,151,151,151,151,151,,151,151,152,,152,,,151',\n',,,,,,152,152,152,,152,,152,,152,,152,152,152,152,,,151,,,,,,,,,,,,',\n'152,,,,152,152,,185,152,152,152,152,152,152,,152,152,,,185,185,185,152',\n'185,,185,,185,,185,185,185,185,,,,,,,,,,,,,152,,,185,,,,185,185,,,185',\n'185,185,185,185,185,,185,185,,,,,,185,,197,,,,,,,,,185,185,,197,197',\n'197,197,197,227,197,,197,185,197,197,197,197,,,,,,227,,227,,,,227,,',\n',197,,,,197,197,,,197,197,197,197,197,197,,197,197,227,,,228,,197,,',\n',,227,227,,,,227,227,228,,228,232,,227,228,,,,,197,,,,,,232,,232,,,',\n'232,,,,228,227,,,,,,,,,228,228,,,,228,228,232,,,233,,228,,,232,232,232',\n'232,,,,232,232,233,,233,234,,232,233,,,,,228,,,,,,234,,234,,,,234,,',\n',233,232,,,,,,,233,233,233,233,,,,233,233,234,,,235,,233,,,234,234,234',\n'234,234,234,,234,234,235,,235,236,,234,235,,,,,233,,,,,236,236,,236',\n',,,236,,236,,235,234,,,,,,,235,235,235,235,235,235,,235,235,236,,,,',\n'235,,237,236,236,236,236,236,236,,236,236,,,,237,237,236,237,238,,,237',\n'235,237,,,,,,,,238,238,,238,,,,238,236,238,,237,,,,,,,,237,237,237,237',\n'237,237,,237,237,238,,,,,237,,239,238,238,238,238,238,238,,238,238,',\n',,239,239,238,239,,,,239,237,239,,,,,,,,,,,,,,,,238,,,239,,,,,240,,',\n'239,239,239,239,239,239,,239,239,240,240,240,,240,239,240,,240,,240',\n'240,240,240,,,,,,,,,,,,,,,239,240,,,,,241,,,240,240,240,240,240,240',\n',240,240,241,241,241,,241,240,241,,241,,241,241,241,241,,,,,,,,,,,,',\n',,240,241,,,,,241,,,241,241,241,241,241,241,242,241,241,,,242,,,241',\n',,,242,242,242,,242,,242,,242,,242,242,242,242,,,,,,241,,,,,,,,,,242',\n',,,242,242,,,242,242,242,242,242,242,,242,242,246,246,,,,242,246,,,',\n',,246,246,246,,246,,246,,246,,246,246,246,246,,,242,,,,,,,,,,,,,246',\n',,,246,246,,253,246,246,246,246,246,246,,246,246,,,253,253,253,246,253',\n',253,,253,,253,253,253,253,,,,,,,,,,,,,246,,,253,,,,253,253,,,253,253',\n'253,253,253,253,258,253,253,,,,,,253,,,,258,258,258,258,258,,258,,258',\n',258,258,258,258,,,,,,253,,,,,,,,,,258,,,,258,258,,382,258,258,258,258',\n'258,258,,258,258,,,382,382,382,258,382,,382,,382,,382,382,382,382,,',\n',,,,,,,,,,258,,,382,,,,382,382,,408,382,382,382,382,382,382,,382,382',\n',,408,408,408,382,408,,408,,408,,408,408,408,408,,,,,,,,,,,,,382,,,408',\n',,,408,408,,409,408,408,408,408,408,408,,408,408,,,409,409,409,408,409',\n',409,,409,,409,409,409,409,,,,,,,,,,,,,408,,,409,,,,409,409,,410,409',\n'409,409,409,409,409,,409,409,,,410,410,410,409,410,,410,,410,,410,410',\n'410,410,,,,,,,,,,,,,409,,,410,,,,410,410,,411,410,410,410,410,410,410',\n',410,410,,,411,411,411,410,411,,411,,411,,411,411,411,411,,,,,,,,,,',\n',,410,,,411,,,,411,411,,436,411,411,411,411,411,411,,411,411,,,436,436',\n'436,411,436,,436,,436,,436,436,436,436,,,,,,,,,,,,,411,,,436,,,,436',\n'436,,476,436,436,436,436,436,436,,436,436,,,476,476,476,436,476,,476',\n',476,,476,476,476,476,,,,,,,,,,,,,436,,,476,,,,476,476,,,476,476,476',\n'476,476,476,,476,476,,252,252,252,252,476,252,252,252,,252,,252,252',\n',,,,,,252,252,252,,,,252,,476,,,,,,,,,,252,252,,,,,,,,,,,,252,252,252',\n'252,252,252,322,322,322,322,,322,322,322,,322,,322,322,,,,,,,322,322',\n'322,,,,322,,,,,,,,,,,,322,322,,,,,,,,,,,,322,322,322,322,322,322,324',\n'324,324,324,,324,324,324,,324,,324,324,,,,,,,324,324,324,,,,324,,,,',\n',,,,,,,324,324,,,,,,,,,,,,324,324,324,324,324,324,354,354,354,354,,354',\n'354,354,,354,,354,354,,,,,,,354,354,354,,,,354,,,,,,,,,,,,354,354,,',\n',,,,,,,,,354,354,354,354,354,354' ]\n        racc_action_check = arr = ::Array.new(9957, nil)\n        idx = 0\n        clist.each do |str|\n          str.split(',', -1).each do |i|\n            arr[idx] = i.to_i unless i.empty?\n            idx += 1\n          end\n        end\n\nracc_action_pointer = [\n    -2,     6,   nil,   nil,    85,    21,   nil,    -8,    38,  8423,\n   172,   259,    95,   nil,   nil,   nil,   346,   433,   520,   607,\n   694,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   781,    40,   -21,\n    41,   868,   955,  1042,    60,   115,    53,   317,   142,    84,\n   nil,   nil,   nil,  1129,  1216,  1303,   nil,   nil,   nil,   nil,\n   nil,  1390,   nil,    31,    62,   nil,  1477,   nil,   nil,   149,\n  1564,   139,  1651,  1738,  1825,  1912,  1999,  2086,  2173,  2260,\n  2347,  2434,  2521,  2608,  2695,  2782,  2869,  2956,  3043,  3130,\n  3217,  3304,  3391,  3478,  3565,  3652,  3739,  3826,  3913,  4000,\n  4087,   nil,    -5,   146,  4174,  4261,   168,   184,  4348,   140,\n   nil,   nil,   nil,   nil,   nil,   nil,   291,   145,   347,   378,\n   169,   176,   194,   nil,   nil,   195,   143,   132,   152,   208,\n   nil,   nil,   217,   nil,    32,   nil,   181,    66,  4435,  8481,\n   nil,  8539,  8597,   201,   213,  4522,   233,   nil,   228,   nil,\n   nil,   nil,   nil,   nil,   nil,   226,     4,   241,   nil,   nil,\n   252,   nil,   256,   nil,     6,   nil,   nil,   nil,   nil,   283,\n   nil,   nil,   270,  4609,   414,  8645,  4696,   nil,   262,   nil,\n   nil,   nil,   nil,   269,   271,   274,   284,  8710,   282,  4783,\n    -9,     2,     9,   161,  4870,  4957,  5044,   nil,   nil,    10,\n   183,   194,   275,   229,   404,   232,    15,    17,   nil,   nil,\n   nil,   nil,   nil,   nil,   434,     5,    92,  8727,  8771,   953,\n  1040,  1475,  8788,  8832,  8849,  8893,  8910,  8958,  8975,  9023,\n  9069,  9115,  9170,   nil,   nil,  5131,  9228,   294,   nil,   nil,\n   nil,   nil,  9700,  9276,   259,   nil,   287,   272,  9331,   nil,\n   282,   nil,   522,   526,   nil,   nil,  5218,   294,   nil,  5305,\n  5392,  5479,   275,   317,   277,   nil,   nil,   315,   nil,   nil,\n   nil,   nil,   nil,   313,   294,   -35,     2,   nil,  5566,  5653,\n  5740,   323,   324,   323,   301,   491,   -25,   280,    11,   nil,\n   nil,   343,  5827,   348,   nil,  5914,   nil,   nil,   nil,   nil,\n  6001,   nil,   nil,   nil,   nil,   344,   nil,   nil,   nil,   nil,\n   346,   350,  9756,   351,  9812,  6088,  6175,  6262,  6349,   350,\n   nil,   356,  6436,   352,   nil,   354,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   357,  6523,  9868,   360,   nil,   239,   nil,   358,\n   nil,   362,   nil,   304,   nil,   133,   367,   369,   nil,  6610,\n   373,   317,    56,  6697,   nil,   nil,  6784,   nil,   384,    73,\n   388,   368,  9379,    83,  6871,   nil,  6958,  7045,   380,   400,\n   nil,   839,   nil,   nil,   403,   nil,  7132,   nil,   nil,   nil,\n   nil,   nil,   nil,   341,   209,   401,   nil,   nil,  9427,  9475,\n  9523,  9571,   405,  7219,   nil,   nil,   406,   nil,   nil,   405,\n  7306,   nil,   nil,   nil,   391,   392,   nil,  7393,   nil,   nil,\n  7480,   413,   nil,  7567,   nil,   nil,  9619,   167,   nil,  7654,\n   418,   394,   nil,   429,  7741,   434,   nil,   nil,   434,   nil,\n   nil,   435,   438,  7828,   nil,  7915,   435,   nil,   nil,   440,\n   409,   nil,   nil,   nil,   nil,   443,   nil,   nil,   444,   nil,\n   178,   nil,   nil,  8002,   nil,  8089,  9667,  8176,   nil,   nil,\n  8263,   446,   nil,   448,   454,   nil,  8350,   nil,   nil,   nil,\n   453,   nil,   455,   nil,   457,   nil,   nil,   466,   nil,   nil,\n   nil,   nil,   438,   nil,   nil,   nil,   nil,   nil ]\n\nracc_action_default = [\n    -3,  -285,    -1,    -2,    -4,    -5,    -8,   -10,   -18,   -23,\n  -285,  -285,  -285,   -35,   -36,   -37,  -285,  -285,  -285,  -285,\n  -285,   -69,   -70,   -71,   -72,   -73,   -74,   -75,   -76,   -77,\n   -78,   -79,   -80,   -81,   -82,   -83,   -84,   -85,   -86,   -87,\n   -88,   -89,   -90,   -91,   -92,   -93,   -94,  -285,  -285,  -101,\n  -105,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,\n  -210,  -211,  -212,  -285,  -285,  -285,  -231,  -232,  -233,  -234,\n  -235,  -285,  -237,  -285,  -250,  -253,  -285,  -258,  -259,  -285,\n  -285,    -7,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,\n  -142,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,\n  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,  -285,\n  -285,   -99,  -285,  -137,  -284,  -284,   -24,   -25,  -285,  -284,\n  -155,  -180,  -181,  -182,  -183,  -184,   -48,  -285,   -49,   -56,\n  -285,  -285,   -14,   -15,   -16,  -260,   -96,  -229,  -230,  -285,\n  -226,  -100,  -200,  -207,  -258,  -102,  -284,  -285,  -285,  -285,\n  -113,  -285,  -285,  -284,  -284,  -285,  -260,  -164,  -166,  -167,\n  -168,  -169,  -170,  -172,  -173,  -284,  -285,  -260,  -214,  -223,\n  -224,  -227,  -260,  -216,  -285,  -219,  -220,  -225,  -236,  -285,\n  -241,  -244,  -285,  -248,  -285,  -285,  -285,   508,    -6,    -9,\n   -11,   -12,   -13,   -19,   -20,   -21,   -22,  -285,  -260,  -285,\n   -92,   -93,   -94,  -278,  -271,  -277,  -265,  -143,  -146,  -285,\n  -268,  -282,  -283,  -266,  -274,  -280,  -211,  -212,  -264,  -269,\n  -270,  -272,  -273,  -275,   -38,   -39,   -40,   -41,   -42,   -43,\n   -44,   -45,   -46,   -47,   -50,   -51,   -52,   -53,   -54,   -55,\n   -57,   -58,  -285,   -59,  -131,  -285,   -23,  -260,   -63,   -66,\n  -106,  -107,  -142,  -141,  -285,  -140,  -285,  -262,  -285,   -30,\n  -284,  -185,  -285,  -285,   -60,   -61,  -261,  -285,   -98,  -285,\n  -285,  -248,  -285,  -285,  -285,  -179,  -111,  -260,  -190,  -192,\n  -193,  -194,  -195,  -197,  -285,  -285,  -285,  -104,  -285,  -285,\n  -285,  -285,  -285,  -260,  -284,  -261,  -285,  -284,  -204,  -205,\n  -206,  -285,  -261,  -285,  -217,  -285,  -238,  -239,  -240,  -242,\n  -285,  -245,  -246,  -247,  -249,  -260,  -251,  -254,  -256,  -257,\n    -8,  -285,  -142,  -285,  -261,  -285,  -285,  -285,  -285,  -260,\n  -133,  -285,  -261,  -260,  -145,  -285,  -265,  -266,  -267,  -268,\n  -271,  -274,  -276,  -277,  -278,  -279,  -280,  -281,  -282,  -283,\n  -138,  -139,  -285,  -263,  -142,  -285,  -158,  -285,  -186,  -260,\n  -187,  -260,   -17,   -95,  -222,  -285,  -285,  -285,  -108,  -285,\n  -177,  -285,  -261,  -285,  -198,  -199,  -285,  -103,  -285,  -116,\n  -285,  -122,   -67,  -285,  -285,  -126,  -284,  -284,  -285,  -285,\n  -174,  -285,  -165,  -171,  -285,  -202,  -285,  -213,  -228,  -215,\n  -218,  -221,  -243,  -285,  -285,  -260,   -28,  -144,  -149,  -147,\n  -148,  -135,  -285,  -261,   -62,   -64,  -285,   -27,   -31,  -260,\n  -284,  -159,  -160,  -161,  -285,  -285,   -97,  -285,  -201,  -208,\n  -248,  -285,  -110,  -285,  -112,  -191,  -196,  -116,  -115,  -285,\n  -285,  -122,  -121,  -285,  -285,  -285,  -125,  -127,  -285,  -156,\n  -157,  -285,  -285,  -285,  -175,  -284,  -260,  -252,  -255,  -285,\n   -32,  -132,  -134,  -136,   -29,  -285,  -188,  -189,  -285,  -109,\n  -285,  -114,  -117,  -285,  -120,  -285,   -68,  -284,  -150,  -151,\n  -285,  -285,  -163,  -285,  -285,   -26,   -33,  -154,  -209,  -178,\n  -285,  -119,  -285,  -124,  -285,  -129,  -130,  -285,  -153,  -162,\n  -176,  -203,  -262,  -118,  -123,  -128,  -152,   -34 ]\n\nracc_goto_table = [\n     2,   161,   176,   257,   126,   128,   129,   244,   159,   130,\n   131,   247,   142,   169,   169,   178,   198,   352,   255,   255,\n   299,   313,   317,   145,   153,   170,   170,   143,   165,   392,\n   273,   277,   154,   135,   415,     1,    81,   132,   385,   149,\n   151,   152,   116,   117,   254,   256,     3,   249,   459,   438,\n   275,   167,   172,   132,   132,   177,   418,   442,   243,   300,\n   368,   180,   472,   384,   185,   494,   329,   407,   315,   355,\n   448,   451,   189,   190,   191,   192,   421,   389,   197,   224,\n   225,   226,   227,   228,   229,   230,   231,   232,   233,   234,\n   235,   236,   237,   238,   239,   240,   241,   242,   246,   393,\n   177,   142,   253,   253,   465,   267,   258,   471,   165,   366,\n   268,   142,   188,   435,   375,   260,   143,   474,   193,   194,\n   195,   196,   374,   176,   364,   454,   294,   398,   311,   415,\n   298,   312,   447,   309,   286,   310,   308,   301,   132,   483,\n   181,   293,   303,   330,   183,   132,   359,   361,   142,   291,\n   292,   nil,   395,   nil,   nil,   nil,   nil,   nil,   161,   nil,\n   nil,   297,   nil,   nil,   356,   159,   nil,   142,   323,   nil,\n   nil,   365,   nil,   nil,   nil,   nil,   320,   nil,   333,   nil,\n   153,   394,   143,   314,   nil,   165,   321,   128,   154,   nil,\n   nil,   nil,   149,   151,   152,   nil,   nil,   nil,   390,   nil,\n   nil,   275,   nil,   nil,   nil,   nil,   nil,   249,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   362,   331,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   142,   142,   nil,   nil,\n   nil,   nil,   nil,   242,   nil,   nil,   nil,   nil,   370,   161,\n   nil,   458,   401,   nil,   nil,   nil,   159,   371,   405,   nil,\n   nil,   nil,   169,   nil,   402,   nil,   132,   nil,   246,   177,\n   177,   nil,   507,   388,   170,   nil,   nil,   nil,   468,   249,\n   nil,   314,   nil,   nil,   nil,   nil,   nil,   nil,   382,   nil,\n   419,   nil,   nil,   nil,   nil,   403,   nil,   nil,   378,   380,\n   450,   450,   132,   nil,   nil,   177,   nil,   456,   nil,   412,\n   180,   nil,   nil,   416,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   462,   nil,   408,   409,   410,   411,   nil,   nil,   nil,\n   246,   nil,   177,   nil,   450,   nil,   362,   422,   nil,   424,\n   nil,   425,   nil,   249,   470,   161,   142,   426,   nil,   nil,\n   nil,   258,   159,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   450,\n   nil,   436,   nil,   nil,   249,   nil,   132,   nil,   nil,   431,\n   249,   502,   382,   nil,   nil,   460,   nil,   nil,   nil,   nil,\n   nil,   495,   nil,   nil,   246,   nil,   177,   nil,   nil,   464,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   242,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   246,   nil,   177,   nil,   nil,\n   nil,   246,   nil,   177,   nil,   nil,   484,   149,   nil,   nil,\n   314,   nil,   476,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   481,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   490,   258,   492,   nil,   496,   nil,   nil,\n   497 ]\n\nracc_goto_check = [\n     2,    44,     9,    15,    12,    12,    12,    62,    45,     6,\n     6,    23,    46,     8,     8,    97,    13,    16,    61,    61,\n    43,    87,   104,    48,    69,    92,    92,    85,    69,    77,\n    52,    54,    72,    10,    24,     1,     5,     6,    59,    12,\n    12,    12,    11,    11,    65,    65,     3,     9,    14,    56,\n    61,    88,    88,     6,     6,     6,    18,    57,    22,    42,\n    53,     6,    55,    58,    12,    60,    63,    66,    54,    73,\n    71,    71,     6,     6,     6,     6,    74,    76,    12,    12,\n    12,    12,    12,    12,    12,    12,    12,    12,    12,    12,\n    12,    12,    12,    12,    12,    12,    12,    12,    12,    79,\n     6,    46,    12,    12,    71,    17,    12,    56,    69,    87,\n    48,    46,     5,    80,    81,    70,    85,    57,    11,    11,\n    11,    11,    83,     9,    90,    77,    17,    91,    93,    24,\n    46,    94,    59,    98,    10,    99,   101,    17,     6,    71,\n   102,    10,    17,    62,   103,     6,    54,    54,    46,    70,\n    70,   nil,    43,   nil,   nil,   nil,   nil,   nil,    44,   nil,\n   nil,    70,   nil,   nil,    61,    45,   nil,    46,    17,   nil,\n   nil,    23,   nil,   nil,   nil,   nil,     6,   nil,    13,   nil,\n    69,    52,    85,     2,   nil,    69,     2,    12,    72,   nil,\n   nil,   nil,    12,    12,    12,   nil,   nil,   nil,    61,   nil,\n   nil,    61,   nil,   nil,   nil,   nil,   nil,     9,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,     8,    17,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,    46,    46,   nil,   nil,\n   nil,   nil,   nil,    12,   nil,   nil,   nil,   nil,    46,    44,\n   nil,   104,     9,   nil,   nil,   nil,    45,    17,    13,   nil,\n   nil,   nil,     8,   nil,    97,   nil,     6,   nil,    12,     6,\n     6,   nil,    16,    17,    92,   nil,   nil,   nil,    87,     9,\n   nil,     2,   nil,   nil,   nil,   nil,   nil,   nil,    12,   nil,\n    13,   nil,   nil,   nil,   nil,    17,   nil,   nil,     2,     2,\n    61,    61,     6,   nil,   nil,     6,   nil,    23,   nil,    17,\n     6,   nil,   nil,    17,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,    62,   nil,    12,    12,    12,    12,   nil,   nil,   nil,\n    12,   nil,     6,   nil,    61,   nil,     8,    69,   nil,    17,\n   nil,    17,   nil,     9,    23,    44,    46,    48,   nil,   nil,\n   nil,    12,    45,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    61,\n   nil,    12,   nil,   nil,     9,   nil,     6,   nil,   nil,     2,\n     9,    15,    12,   nil,   nil,    17,   nil,   nil,   nil,   nil,\n   nil,    61,   nil,   nil,    12,   nil,     6,   nil,   nil,    17,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,    12,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,    12,   nil,     6,   nil,   nil,\n   nil,    12,   nil,     6,   nil,   nil,    17,    12,   nil,   nil,\n     2,   nil,    12,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,     2,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,     2,    12,     2,   nil,     2,   nil,   nil,\n     2 ]\n\nracc_goto_pointer = [\n   nil,    35,     0,    46,   nil,    32,   -10,   nil,   -50,   -63,\n   -14,    32,   -12,   -74,  -357,  -115,  -240,   -30,  -297,   nil,\n   nil,   nil,   -51,   -99,  -298,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,\n   nil,   nil,  -107,  -146,   -56,   -49,   -36,   nil,   -26,   nil,\n   nil,   nil,  -116,  -213,  -116,  -377,  -330,  -324,  -227,  -252,\n  -412,   -96,  -102,  -179,   nil,   -70,  -257,   nil,   nil,   -30,\n    -4,  -316,   -23,  -191,  -281,   nil,  -217,  -266,   nil,  -197,\n  -259,  -171,   nil,  -162,   nil,   -21,   nil,  -162,   -12,   nil,\n  -145,  -175,   -38,   -54,   -51,   nil,   nil,   -56,   -46,   -44,\n   nil,   -43,    67,    70,  -163 ]\n\nracc_goto_default = [\n   nil,   nil,   449,   nil,     4,     5,     6,     7,   134,   133,\n   nil,     8,     9,   nil,   nil,   nil,   nil,   nil,   259,    13,\n    14,    15,   nil,   nil,   248,   383,    21,    22,    23,    24,\n    25,    26,    27,    28,    29,    30,    31,    32,    33,    34,\n    35,    36,    37,    38,    39,    40,    41,    47,   nil,    49,\n    50,   146,   nil,   nil,   nil,   150,   nil,   nil,   nil,   nil,\n   nil,   261,   nil,   nil,   113,   nil,   207,   209,   208,   120,\n   nil,   nil,   119,   nil,   nil,   156,   nil,   157,   158,   162,\n   278,   279,   280,   281,   282,   285,    59,   nil,   nil,   174,\n   139,   171,   140,    66,    67,    68,    71,   nil,   nil,   nil,\n   179,   nil,   nil,   nil,   nil ]\n\nracc_reduce_table = [\n  0, 0, :racc_error,\n  1, 95, :_reduce_1,\n  1, 95, :_reduce_2,\n  0, 95, :_reduce_3,\n  1, 96, :_reduce_4,\n  1, 98, :_reduce_5,\n  3, 98, :_reduce_6,\n  2, 98, :_reduce_7,\n  1, 99, :_reduce_8,\n  3, 99, :_reduce_9,\n  1, 100, :_reduce_none,\n  3, 100, :_reduce_11,\n  3, 100, :_reduce_12,\n  3, 100, :_reduce_13,\n  1, 102, :_reduce_none,\n  1, 102, :_reduce_15,\n  1, 104, :_reduce_16,\n  3, 104, :_reduce_17,\n  1, 101, :_reduce_none,\n  3, 101, :_reduce_19,\n  3, 101, :_reduce_20,\n  3, 101, :_reduce_21,\n  3, 101, :_reduce_22,\n  1, 105, :_reduce_none,\n  2, 105, :_reduce_24,\n  2, 105, :_reduce_25,\n  7, 105, :_reduce_26,\n  5, 105, :_reduce_27,\n  5, 105, :_reduce_28,\n  4, 112, :_reduce_29,\n  1, 109, :_reduce_30,\n  3, 109, :_reduce_31,\n  1, 108, :_reduce_32,\n  2, 108, :_reduce_33,\n  4, 108, :_reduce_34,\n  1, 106, :_reduce_none,\n  1, 106, :_reduce_none,\n  1, 106, :_reduce_none,\n  3, 106, :_reduce_38,\n  3, 106, :_reduce_39,\n  3, 106, :_reduce_40,\n  3, 106, :_reduce_41,\n  3, 106, :_reduce_42,\n  3, 106, :_reduce_43,\n  3, 106, :_reduce_44,\n  3, 106, :_reduce_45,\n  3, 106, :_reduce_46,\n  3, 106, :_reduce_47,\n  2, 106, :_reduce_48,\n  2, 106, :_reduce_49,\n  3, 106, :_reduce_50,\n  3, 106, :_reduce_51,\n  3, 106, :_reduce_52,\n  3, 106, :_reduce_53,\n  3, 106, :_reduce_54,\n  3, 106, :_reduce_55,\n  2, 106, :_reduce_56,\n  3, 106, :_reduce_57,\n  3, 106, :_reduce_58,\n  3, 106, :_reduce_59,\n  3, 106, :_reduce_60,\n  3, 106, :_reduce_61,\n  5, 115, :_reduce_62,\n  1, 117, :_reduce_63,\n  3, 117, :_reduce_64,\n  1, 118, :_reduce_none,\n  1, 118, :_reduce_66,\n  1, 119, :_reduce_67,\n  3, 119, :_reduce_68,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_none,\n  1, 113, :_reduce_90,\n  1, 113, :_reduce_91,\n  1, 113, :_reduce_92,\n  1, 113, :_reduce_93,\n  1, 113, :_reduce_94,\n  4, 114, :_reduce_95,\n  2, 114, :_reduce_96,\n  5, 114, :_reduce_97,\n  3, 114, :_reduce_98,\n  2, 141, :_reduce_99,\n  2, 141, :_reduce_100,\n  1, 121, :_reduce_101,\n  2, 121, :_reduce_102,\n  4, 143, :_reduce_103,\n  3, 143, :_reduce_104,\n  1, 143, :_reduce_105,\n  3, 144, :_reduce_106,\n  3, 144, :_reduce_107,\n  3, 142, :_reduce_108,\n  3, 147, :_reduce_109,\n  2, 147, :_reduce_110,\n  2, 145, :_reduce_111,\n  4, 145, :_reduce_112,\n  2, 124, :_reduce_113,\n  5, 149, :_reduce_114,\n  4, 149, :_reduce_115,\n  0, 150, :_reduce_none,\n  2, 150, :_reduce_117,\n  4, 150, :_reduce_118,\n  3, 150, :_reduce_119,\n  6, 125, :_reduce_120,\n  5, 125, :_reduce_121,\n  0, 151, :_reduce_none,\n  4, 151, :_reduce_123,\n  3, 151, :_reduce_124,\n  5, 123, :_reduce_125,\n  1, 152, :_reduce_126,\n  2, 152, :_reduce_127,\n  5, 153, :_reduce_128,\n  1, 154, :_reduce_none,\n  1, 154, :_reduce_none,\n  1, 116, :_reduce_none,\n  4, 116, :_reduce_132,\n  1, 157, :_reduce_133,\n  3, 157, :_reduce_134,\n  3, 156, :_reduce_135,\n  6, 122, :_reduce_136,\n  2, 122, :_reduce_137,\n  3, 158, :_reduce_138,\n  3, 158, :_reduce_139,\n  1, 159, :_reduce_none,\n  1, 159, :_reduce_none,\n  0, 107, :_reduce_142,\n  1, 107, :_reduce_143,\n  3, 107, :_reduce_144,\n  1, 161, :_reduce_none,\n  1, 161, :_reduce_none,\n  3, 160, :_reduce_147,\n  3, 160, :_reduce_148,\n  3, 160, :_reduce_149,\n  6, 126, :_reduce_150,\n  6, 128, :_reduce_151,\n  8, 129, :_reduce_152,\n  7, 129, :_reduce_153,\n  7, 127, :_reduce_154,\n  1, 166, :_reduce_155,\n  1, 165, :_reduce_none,\n  1, 165, :_reduce_none,\n  1, 167, :_reduce_none,\n  2, 167, :_reduce_159,\n  1, 168, :_reduce_none,\n  1, 168, :_reduce_none,\n  7, 130, :_reduce_162,\n  6, 130, :_reduce_163,\n  1, 169, :_reduce_164,\n  3, 169, :_reduce_165,\n  1, 171, :_reduce_none,\n  1, 171, :_reduce_none,\n  1, 171, :_reduce_168,\n  1, 171, :_reduce_none,\n  1, 172, :_reduce_170,\n  3, 172, :_reduce_171,\n  1, 173, :_reduce_none,\n  1, 173, :_reduce_none,\n  1, 170, :_reduce_none,\n  2, 170, :_reduce_175,\n  7, 132, :_reduce_176,\n  2, 146, :_reduce_177,\n  5, 146, :_reduce_178,\n  1, 146, :_reduce_none,\n  1, 163, :_reduce_none,\n  1, 163, :_reduce_none,\n  1, 163, :_reduce_none,\n  1, 163, :_reduce_183,\n  1, 163, :_reduce_184,\n  1, 164, :_reduce_185,\n  2, 164, :_reduce_186,\n  2, 164, :_reduce_187,\n  4, 164, :_reduce_188,\n  4, 164, :_reduce_189,\n  1, 148, :_reduce_190,\n  3, 148, :_reduce_191,\n  1, 174, :_reduce_none,\n  1, 174, :_reduce_none,\n  1, 175, :_reduce_none,\n  1, 175, :_reduce_none,\n  3, 177, :_reduce_196,\n  1, 177, :_reduce_197,\n  2, 178, :_reduce_198,\n  2, 176, :_reduce_199,\n  1, 179, :_reduce_200,\n  4, 179, :_reduce_201,\n  4, 133, :_reduce_202,\n  7, 133, :_reduce_203,\n  3, 133, :_reduce_204,\n  3, 133, :_reduce_205,\n  3, 133, :_reduce_206,\n  2, 180, :_reduce_207,\n  5, 134, :_reduce_208,\n  7, 134, :_reduce_209,\n  1, 120, :_reduce_210,\n  1, 135, :_reduce_211,\n  1, 135, :_reduce_212,\n  4, 136, :_reduce_213,\n  2, 136, :_reduce_214,\n  4, 136, :_reduce_215,\n  2, 136, :_reduce_216,\n  3, 137, :_reduce_217,\n  4, 137, :_reduce_218,\n  2, 137, :_reduce_219,\n  1, 183, :_reduce_220,\n  3, 183, :_reduce_221,\n  3, 103, :_reduce_222,\n  1, 185, :_reduce_none,\n  1, 185, :_reduce_224,\n  1, 184, :_reduce_none,\n  1, 184, :_reduce_226,\n  1, 182, :_reduce_227,\n  3, 182, :_reduce_228,\n  1, 186, :_reduce_none,\n  1, 186, :_reduce_none,\n  1, 139, :_reduce_none,\n  1, 139, :_reduce_none,\n  1, 139, :_reduce_none,\n  1, 187, :_reduce_234,\n  1, 187, :_reduce_235,\n  2, 188, :_reduce_236,\n  1, 190, :_reduce_237,\n  1, 192, :_reduce_238,\n  1, 193, :_reduce_239,\n  2, 191, :_reduce_240,\n  1, 194, :_reduce_241,\n  1, 195, :_reduce_242,\n  2, 195, :_reduce_243,\n  2, 189, :_reduce_244,\n  2, 196, :_reduce_245,\n  2, 196, :_reduce_246,\n  3, 97, :_reduce_247,\n  0, 181, :_reduce_none,\n  1, 181, :_reduce_none,\n  0, 197, :_reduce_250,\n  2, 197, :_reduce_251,\n  4, 197, :_reduce_252,\n  1, 131, :_reduce_253,\n  3, 131, :_reduce_254,\n  5, 131, :_reduce_255,\n  1, 198, :_reduce_none,\n  1, 198, :_reduce_none,\n  1, 140, :_reduce_258,\n  1, 138, :_reduce_259,\n  0, 111, :_reduce_none,\n  1, 111, :_reduce_261,\n  0, 110, :_reduce_none,\n  1, 110, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  1, 162, :_reduce_none,\n  0, 155, :_reduce_284 ]\n\nracc_reduce_n = 285\n\nracc_shift_n = 508\n\nracc_token_table = {\n  false => 0,\n  :error => 1,\n  :STRING => 2,\n  :DQPRE => 3,\n  :DQMID => 4,\n  :DQPOST => 5,\n  :WORD => 6,\n  :LBRACK => 7,\n  :RBRACK => 8,\n  :LBRACE => 9,\n  :RBRACE => 10,\n  :SYMBOL => 11,\n  :FARROW => 12,\n  :COMMA => 13,\n  :TRUE => 14,\n  :FALSE => 15,\n  :EQUALS => 16,\n  :APPENDS => 17,\n  :DELETES => 18,\n  :LESSEQUAL => 19,\n  :NOTEQUAL => 20,\n  :DOT => 21,\n  :COLON => 22,\n  :LLCOLLECT => 23,\n  :RRCOLLECT => 24,\n  :QMARK => 25,\n  :WSLPAREN => 26,\n  :LPAREN => 27,\n  :RPAREN => 28,\n  :ISEQUAL => 29,\n  :GREATEREQUAL => 30,\n  :GREATERTHAN => 31,\n  :LESSTHAN => 32,\n  :IF => 33,\n  :ELSE => 34,\n  :DEFINE => 35,\n  :ELSIF => 36,\n  :VARIABLE => 37,\n  :CLASS => 38,\n  :INHERITS => 39,\n  :NODE => 40,\n  :BOOLEAN => 41,\n  :NAME => 42,\n  :SEMIC => 43,\n  :CASE => 44,\n  :DEFAULT => 45,\n  :AT => 46,\n  :ATAT => 47,\n  :LCOLLECT => 48,\n  :RCOLLECT => 49,\n  :CLASSREF => 50,\n  :NOT => 51,\n  :OR => 52,\n  :AND => 53,\n  :UNDEF => 54,\n  :PARROW => 55,\n  :PLUS => 56,\n  :MINUS => 57,\n  :TIMES => 58,\n  :DIV => 59,\n  :LSHIFT => 60,\n  :RSHIFT => 61,\n  :UMINUS => 62,\n  :MATCH => 63,\n  :NOMATCH => 64,\n  :REGEX => 65,\n  :IN_EDGE => 66,\n  :OUT_EDGE => 67,\n  :IN_EDGE_SUB => 68,\n  :OUT_EDGE_SUB => 69,\n  :IN => 70,\n  :UNLESS => 71,\n  :PIPE => 72,\n  :LAMBDA => 73,\n  :SELBRACE => 74,\n  :NUMBER => 75,\n  :HEREDOC => 76,\n  :SUBLOCATE => 77,\n  :RENDER_STRING => 78,\n  :RENDER_EXPR => 79,\n  :EPP_START => 80,\n  :EPP_END => 81,\n  :EPP_END_TRIM => 82,\n  :FUNCTION => 83,\n  :TYPE => 84,\n  :PRIVATE => 85,\n  :ATTR => 86,\n  :PLAN => 87,\n  :APPLY => 88,\n  :LOW => 89,\n  :HIGH => 90,\n  :LISTSTART => 91,\n  :SPLAT => 92,\n  :MODULO => 93 }\n\nracc_nt_base = 94\n\nracc_use_result_var = true\n\nRacc_arg = [\n  racc_action_table,\n  racc_action_check,\n  racc_action_default,\n  racc_action_pointer,\n  racc_goto_table,\n  racc_goto_check,\n  racc_goto_default,\n  racc_goto_pointer,\n  racc_nt_base,\n  racc_reduce_table,\n  racc_token_table,\n  racc_shift_n,\n  racc_reduce_n,\n  racc_use_result_var ]\n\nRacc_token_to_s_table = [\n  \"$end\",\n  \"error\",\n  \"STRING\",\n  \"DQPRE\",\n  \"DQMID\",\n  \"DQPOST\",\n  \"WORD\",\n  \"LBRACK\",\n  \"RBRACK\",\n  \"LBRACE\",\n  \"RBRACE\",\n  \"SYMBOL\",\n  \"FARROW\",\n  \"COMMA\",\n  \"TRUE\",\n  \"FALSE\",\n  \"EQUALS\",\n  \"APPENDS\",\n  \"DELETES\",\n  \"LESSEQUAL\",\n  \"NOTEQUAL\",\n  \"DOT\",\n  \"COLON\",\n  \"LLCOLLECT\",\n  \"RRCOLLECT\",\n  \"QMARK\",\n  \"WSLPAREN\",\n  \"LPAREN\",\n  \"RPAREN\",\n  \"ISEQUAL\",\n  \"GREATEREQUAL\",\n  \"GREATERTHAN\",\n  \"LESSTHAN\",\n  \"IF\",\n  \"ELSE\",\n  \"DEFINE\",\n  \"ELSIF\",\n  \"VARIABLE\",\n  \"CLASS\",\n  \"INHERITS\",\n  \"NODE\",\n  \"BOOLEAN\",\n  \"NAME\",\n  \"SEMIC\",\n  \"CASE\",\n  \"DEFAULT\",\n  \"AT\",\n  \"ATAT\",\n  \"LCOLLECT\",\n  \"RCOLLECT\",\n  \"CLASSREF\",\n  \"NOT\",\n  \"OR\",\n  \"AND\",\n  \"UNDEF\",\n  \"PARROW\",\n  \"PLUS\",\n  \"MINUS\",\n  \"TIMES\",\n  \"DIV\",\n  \"LSHIFT\",\n  \"RSHIFT\",\n  \"UMINUS\",\n  \"MATCH\",\n  \"NOMATCH\",\n  \"REGEX\",\n  \"IN_EDGE\",\n  \"OUT_EDGE\",\n  \"IN_EDGE_SUB\",\n  \"OUT_EDGE_SUB\",\n  \"IN\",\n  \"UNLESS\",\n  \"PIPE\",\n  \"LAMBDA\",\n  \"SELBRACE\",\n  \"NUMBER\",\n  \"HEREDOC\",\n  \"SUBLOCATE\",\n  \"RENDER_STRING\",\n  \"RENDER_EXPR\",\n  \"EPP_START\",\n  \"EPP_END\",\n  \"EPP_END_TRIM\",\n  \"FUNCTION\",\n  \"TYPE\",\n  \"PRIVATE\",\n  \"ATTR\",\n  \"PLAN\",\n  \"APPLY\",\n  \"LOW\",\n  \"HIGH\",\n  \"LISTSTART\",\n  \"SPLAT\",\n  \"MODULO\",\n  \"$start\",\n  \"program\",\n  \"statements\",\n  \"epp_expression\",\n  \"syntactic_statements\",\n  \"syntactic_statement\",\n  \"assignment\",\n  \"relationship\",\n  \"argument\",\n  \"hashpair\",\n  \"arguments\",\n  \"resource\",\n  \"expression\",\n  \"attribute_operations\",\n  \"additional_resource_bodies\",\n  \"resource_bodies\",\n  \"endsemi\",\n  \"endcomma\",\n  \"resource_body\",\n  \"primary_expression\",\n  \"call_function_expression\",\n  \"bracketed_expression\",\n  \"selector_entries\",\n  \"access_args\",\n  \"access_arg\",\n  \"expressions\",\n  \"variable\",\n  \"call_method_with_lambda_expression\",\n  \"collection_expression\",\n  \"case_expression\",\n  \"if_expression\",\n  \"unless_expression\",\n  \"definition_expression\",\n  \"hostclass_expression\",\n  \"plan_expression\",\n  \"apply_expression\",\n  \"node_definition_expression\",\n  \"epp_render_expression\",\n  \"function_definition\",\n  \"type_alias\",\n  \"type_definition\",\n  \"reserved_word\",\n  \"array\",\n  \"hash\",\n  \"regex\",\n  \"quotedtext\",\n  \"type\",\n  \"call_function_start\",\n  \"lambda\",\n  \"call_method_expression\",\n  \"named_access\",\n  \"lambda_parameter_list\",\n  \"opt_return_type\",\n  \"lambda_rest\",\n  \"parameters\",\n  \"if_part\",\n  \"else\",\n  \"unless_else\",\n  \"case_options\",\n  \"case_option\",\n  \"options_statements\",\n  \"nil\",\n  \"selector_entry\",\n  \"selector_entry_list\",\n  \"collect_query\",\n  \"optional_query\",\n  \"attribute_operation\",\n  \"attribute_name\",\n  \"keyword\",\n  \"classname\",\n  \"parameter_list\",\n  \"opt_statements\",\n  \"stacked_classname\",\n  \"classparent\",\n  \"classnameordefault\",\n  \"hostnames\",\n  \"nodeparent\",\n  \"hostname\",\n  \"dotted_name\",\n  \"name_or_number\",\n  \"parameter\",\n  \"untyped_parameter\",\n  \"typed_parameter\",\n  \"regular_parameter\",\n  \"splat_parameter\",\n  \"parameter_type\",\n  \"type_alias_lhs\",\n  \"optional_statements\",\n  \"collection_entries\",\n  \"hashpairs\",\n  \"hash_entry\",\n  \"collection_entry\",\n  \"collection_entry_keyword\",\n  \"string\",\n  \"dq_string\",\n  \"heredoc\",\n  \"dqpre\",\n  \"dqrval\",\n  \"dqpost\",\n  \"dqmid\",\n  \"text_expression\",\n  \"dqtail\",\n  \"sublocated_text\",\n  \"epp_parameters_list\",\n  \"epp_end\" ]\n\nRacc_debug_parser = false\n\n##### State transition tables end #####\n\n# reduce 0 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 67)\n  def _reduce_1(val, _values, result)\n     result = create_program(Factory.block_or_expression(val[0]))\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 68)\n  def _reduce_2(val, _values, result)\n     result = create_program(val[0])\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 69)\n  def _reduce_3(val, _values, result)\n     result = create_empty_program\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 73)\n  def _reduce_4(val, _values, result)\n     result = transform_calls(val[0])\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 80)\n  def _reduce_5(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 81)\n  def _reduce_6(val, _values, result)\n     result = val[0].push val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 82)\n  def _reduce_7(val, _values, result)\n     result = val[0].push val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 89)\n  def _reduce_8(val, _values, result)\n     result = val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 90)\n  def _reduce_9(val, _values, result)\n     result = aryfy(val[0]).push(val[1]).push(val[2])\n    result\n  end\n.,.,\n\n# reduce 10 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 95)\n  def _reduce_11(val, _values, result)\n     result = val[0].set(val[2])      ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 96)\n  def _reduce_12(val, _values, result)\n     result = val[0].plus_set(val[2]) ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 97)\n  def _reduce_13(val, _values, result)\n     result = val[0].minus_set(val[2]); loc result, val[1]\n    result\n  end\n.,.,\n\n# reduce 14 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 102)\n  def _reduce_15(val, _values, result)\n     result = Factory.HASH_UNFOLDED([val[0]]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 106)\n  def _reduce_16(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 107)\n  def _reduce_17(val, _values, result)\n     result = Factory.ARGUMENTS(val[0], val[2])\n    result\n  end\n.,.,\n\n# reduce 18 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 111)\n  def _reduce_19(val, _values, result)\n     result = val[0].relop(val[1][:value], val[2]); loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 112)\n  def _reduce_20(val, _values, result)\n     result = val[0].relop(val[1][:value], val[2]); loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 113)\n  def _reduce_21(val, _values, result)\n     result = val[0].relop(val[1][:value], val[2]); loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 114)\n  def _reduce_22(val, _values, result)\n     result = val[0].relop(val[1][:value], val[2]); loc result, val[1]\n    result\n  end\n.,.,\n\n# reduce 23 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 123)\n  def _reduce_24(val, _values, result)\n          result = val[1]\n      unless Factory.set_resource_form(result, 'virtual')\n        # This is equivalent to a syntax error - additional semantic restrictions apply\n        error val[0], \"Virtual (@) can only be applied to a Resource Expression\"\n      end\n      # relocate the result\n      loc result, val[0], val[1]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 134)\n  def _reduce_25(val, _values, result)\n          result = val[1]\n      unless Factory.set_resource_form(result, 'exported')\n        # This is equivalent to a syntax error - additional semantic restrictions apply\n        error val[0], \"Exported (@@) can only be applied to a Resource Expression\"\n      end\n      # relocate the result\n      loc result, val[0], val[1]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 145)\n  def _reduce_26(val, _values, result)\n          bodies = [Factory.RESOURCE_BODY(val[2], val[4])] + val[5]\n      result = Factory.RESOURCE(val[0], bodies)\n      loc result, val[0], val[6]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 152)\n  def _reduce_27(val, _values, result)\n            result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])\n        loc result, val[0], val[4]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 161)\n  def _reduce_28(val, _values, result)\n           result = case Factory.resource_shape(val[0])\n       when :resource, :class\n        # This catches deprecated syntax.\n        # If the attribute operations does not include +>, then the found expression\n        # is actually a LEFT followed by LITERAL_HASH\n        #\n        unless tmp = transform_resource_wo_title(val[0], val[2], val[1], val[4])\n          error val[1], \"Syntax error resource body without title or hash with +>\"\n        end\n        tmp\n      when :defaults\n        Factory.RESOURCE_DEFAULTS(val[0], val[2])\n      when :override\n        # This was only done for override in original - TODO should it be here at all\n        Factory.RESOURCE_OVERRIDE(val[0], val[2])\n      else\n        error val[0], \"Expression is not valid as a resource, resource-default, or resource-override\"\n      end\n     loc result, val[0], val[4]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 183)\n  def _reduce_29(val, _values, result)\n     result = Factory.RESOURCE_BODY(val[0], val[2])\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 186)\n  def _reduce_30(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 187)\n  def _reduce_31(val, _values, result)\n     result = val[0].push val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 193)\n  def _reduce_32(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 194)\n  def _reduce_33(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 195)\n  def _reduce_34(val, _values, result)\n     result = val[2]\n    result\n  end\n.,.,\n\n# reduce 35 omitted\n\n# reduce 36 omitted\n\n# reduce 37 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 203)\n  def _reduce_38(val, _values, result)\n     result = val[0].in val[2]        ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 204)\n  def _reduce_39(val, _values, result)\n     result = val[0] =~  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 205)\n  def _reduce_40(val, _values, result)\n     result = val[0].mne val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 206)\n  def _reduce_41(val, _values, result)\n     result = val[0] +   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 207)\n  def _reduce_42(val, _values, result)\n     result = val[0] -   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 208)\n  def _reduce_43(val, _values, result)\n     result = val[0] /   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 209)\n  def _reduce_44(val, _values, result)\n     result = val[0] *   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 210)\n  def _reduce_45(val, _values, result)\n     result = val[0] %   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 211)\n  def _reduce_46(val, _values, result)\n     result = val[0] <<  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 212)\n  def _reduce_47(val, _values, result)\n     result = val[0] >>  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 213)\n  def _reduce_48(val, _values, result)\n     result = val[1].minus            ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 214)\n  def _reduce_49(val, _values, result)\n     result = val[1].unfold           ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 215)\n  def _reduce_50(val, _values, result)\n     result = val[0].ne  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 216)\n  def _reduce_51(val, _values, result)\n     result = val[0].eq  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 217)\n  def _reduce_52(val, _values, result)\n     result = val[0] >   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 218)\n  def _reduce_53(val, _values, result)\n     result = val[0] >=  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 219)\n  def _reduce_54(val, _values, result)\n     result = val[0] <   val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 220)\n  def _reduce_55(val, _values, result)\n     result = val[0] <=  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 221)\n  def _reduce_56(val, _values, result)\n     result = val[1].not              ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 222)\n  def _reduce_57(val, _values, result)\n     result = val[0].and val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 223)\n  def _reduce_58(val, _values, result)\n     result = val[0].or  val[2]       ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 224)\n  def _reduce_59(val, _values, result)\n     result = val[0].select(*val[2])  ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 225)\n  def _reduce_60(val, _values, result)\n     result = val[1].paren            ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 226)\n  def _reduce_61(val, _values, result)\n     result = val[1].paren          ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 229)\n  def _reduce_62(val, _values, result)\n     result = val[0].access(val[2]); loc result, val[0], val[4]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 232)\n  def _reduce_63(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 233)\n  def _reduce_64(val, _values, result)\n     result = Factory.ARGUMENTS(val[0], val[2])\n    result\n  end\n.,.,\n\n# reduce 65 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 237)\n  def _reduce_66(val, _values, result)\n     result = Factory.HASH_UNFOLDED([val[0]]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 246)\n  def _reduce_67(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 247)\n  def _reduce_68(val, _values, result)\n     result = val[0].push(val[2])\n    result\n  end\n.,.,\n\n# reduce 69 omitted\n\n# reduce 70 omitted\n\n# reduce 71 omitted\n\n# reduce 72 omitted\n\n# reduce 73 omitted\n\n# reduce 74 omitted\n\n# reduce 75 omitted\n\n# reduce 76 omitted\n\n# reduce 77 omitted\n\n# reduce 78 omitted\n\n# reduce 79 omitted\n\n# reduce 80 omitted\n\n# reduce 81 omitted\n\n# reduce 82 omitted\n\n# reduce 83 omitted\n\n# reduce 84 omitted\n\n# reduce 85 omitted\n\n# reduce 86 omitted\n\n# reduce 87 omitted\n\n# reduce 88 omitted\n\n# reduce 89 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 271)\n  def _reduce_90(val, _values, result)\n     result = Factory.NUMBER(val[0][:value])          ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 272)\n  def _reduce_91(val, _values, result)\n     result = Factory.literal(val[0][:value])         ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 273)\n  def _reduce_92(val, _values, result)\n     result = Factory.literal(:default)               ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 274)\n  def _reduce_93(val, _values, result)\n     result = Factory.literal(:undef)                 ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 275)\n  def _reduce_94(val, _values, result)\n     result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 284)\n  def _reduce_95(val, _values, result)\n          result = Factory.CALL_NAMED(val[0], true, val[1])\n      loc result, val[0], val[3]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 288)\n  def _reduce_96(val, _values, result)\n          result = Factory.CALL_NAMED(val[0], true, [])\n      loc result, val[0], val[1]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 292)\n  def _reduce_97(val, _values, result)\n          result = Factory.CALL_NAMED(val[0], true, val[1])\n      loc result, val[0], val[4]\n      result.lambda = val[4]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 297)\n  def _reduce_98(val, _values, result)\n          result = Factory.CALL_NAMED(val[0], true, [])\n      loc result, val[0], val[2]\n      result.lambda = val[2]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 303)\n  def _reduce_99(val, _values, result)\n     result = val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 304)\n  def _reduce_100(val, _values, result)\n     result = Factory.QNAME(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 309)\n  def _reduce_101(val, _values, result)\n     result = val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 310)\n  def _reduce_102(val, _values, result)\n     result = val[0]; val[0].lambda = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 313)\n  def _reduce_103(val, _values, result)\n     result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 314)\n  def _reduce_104(val, _values, result)\n     result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 315)\n  def _reduce_105(val, _values, result)\n     result = Factory.CALL_METHOD(val[0], []); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 319)\n  def _reduce_106(val, _values, result)\n            result = val[0].dot(Factory.fqn(val[2][:value]))\n        loc result, val[1], val[2]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 323)\n  def _reduce_107(val, _values, result)\n            result = val[0].dot(Factory.fqn(val[2][:value]))\n        loc result, val[1], val[2]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 331)\n  def _reduce_108(val, _values, result)\n          result = Factory.LAMBDA(val[0][:value], val[2][:value], val[1])\n      loc result, val[0][:start], val[2][:end]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 336)\n  def _reduce_109(val, _values, result)\n     result = {:end => val[2], :value =>val[1] }\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 337)\n  def _reduce_110(val, _values, result)\n     result = {:end => val[1], :value => nil }\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 341)\n  def _reduce_111(val, _values, result)\n     result = {:start => val[0], :value => [] }\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 342)\n  def _reduce_112(val, _values, result)\n     result = {:start => val[0], :value => val[1] }\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 350)\n  def _reduce_113(val, _values, result)\n          result = val[1]\n      loc(result, val[0], val[1])\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 357)\n  def _reduce_114(val, _values, result)\n            result = Factory.IF(val[0], Factory.block_or_expression(val[2], val[1], val[3]), val[4])\n        loc(result, val[0], (val[4] ? val[4] : val[3]))\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 361)\n  def _reduce_115(val, _values, result)\n            result = Factory.IF(val[0], nil, val[3])\n        loc(result, val[0], (val[3] ? val[3] : val[2]))\n\n    result\n  end\n.,.,\n\n# reduce 116 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 369)\n  def _reduce_117(val, _values, result)\n            result = val[1]\n        loc(result, val[0], val[1])\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 373)\n  def _reduce_118(val, _values, result)\n            result = Factory.block_or_expression(val[2], val[1], val[3])\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 376)\n  def _reduce_119(val, _values, result)\n            result = nil # don't think a nop is needed here either\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 383)\n  def _reduce_120(val, _values, result)\n          result = Factory.UNLESS(val[1], Factory.block_or_expression(val[3], val[2], val[4]), val[5])\n      loc result, val[0], val[4]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 387)\n  def _reduce_121(val, _values, result)\n          result = Factory.UNLESS(val[1], nil, val[4])\n      loc result, val[0], val[4]\n\n    result\n  end\n.,.,\n\n# reduce 122 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 397)\n  def _reduce_123(val, _values, result)\n            result = Factory.block_or_expression(val[2], val[1], val[3])\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 400)\n  def _reduce_124(val, _values, result)\n            result = nil # don't think a nop is needed here either\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 407)\n  def _reduce_125(val, _values, result)\n          result = Factory.CASE(val[1], *val[3])\n      loc result, val[0], val[4]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 413)\n  def _reduce_126(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 414)\n  def _reduce_127(val, _values, result)\n     result = val[0].push val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 419)\n  def _reduce_128(val, _values, result)\n            result = Factory.WHEN(val[0], val[3]); loc result, val[1], val[4]\n\n    result\n  end\n.,.,\n\n# reduce 129 omitted\n\n# reduce 130 omitted\n\n# reduce 131 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 435)\n  def _reduce_132(val, _values, result)\n            result = val[1]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 440)\n  def _reduce_133(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 441)\n  def _reduce_134(val, _values, result)\n     result = val[0].push val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 446)\n  def _reduce_135(val, _values, result)\n     result = Factory.MAP(val[0], val[2]) ; loc result, val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 456)\n  def _reduce_136(val, _values, result)\n          result = Factory.COLLECT(val[0], val[1], val[3])\n      loc result, val[0], val[5]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 460)\n  def _reduce_137(val, _values, result)\n          result = Factory.COLLECT(val[0], val[1], [])\n      loc result, val[0], val[1]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 465)\n  def _reduce_138(val, _values, result)\n     result = Factory.VIRTUAL_QUERY(val[1])   ; loc result, val[0], val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 466)\n  def _reduce_139(val, _values, result)\n     result = Factory.EXPORTED_QUERY(val[1])  ; loc result, val[0], val[2]\n    result\n  end\n.,.,\n\n# reduce 140 omitted\n\n# reduce 141 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 475)\n  def _reduce_142(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 476)\n  def _reduce_143(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 477)\n  def _reduce_144(val, _values, result)\n     result = val[0].push(val[2])\n    result\n  end\n.,.,\n\n# reduce 145 omitted\n\n# reduce 146 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 492)\n  def _reduce_147(val, _values, result)\n            result = Factory.ATTRIBUTE_OP(val[0][:value], '=>', val[2])\n        loc result, val[0], val[2]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 496)\n  def _reduce_148(val, _values, result)\n            result = Factory.ATTRIBUTE_OP(val[0][:value], '+>', val[2])\n        loc result, val[0], val[2]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 500)\n  def _reduce_149(val, _values, result)\n          result = Factory.ATTRIBUTES_OP(val[2]) ; loc result, val[0], val[2]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 509)\n  def _reduce_150(val, _values, result)\n          definition = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n      # New lexer does not keep track of this, this is done in validation\n      if @lexer.respond_to?(:'indefine=')\n        @lexer.indefine = false\n      end\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 521)\n  def _reduce_151(val, _values, result)\n          # Remove this plan's name from the namestack as all nested plans have been parsed\n      namepop\n      definition = Factory.PLAN(classname(val[1][:value]), val[2], val[4])\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 530)\n  def _reduce_152(val, _values, result)\n          result = Factory.APPLY(val[2], Factory.APPLY_BLOCK(val[6]))\n      loc result, val[0], val[7]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 534)\n  def _reduce_153(val, _values, result)\n          result = Factory.APPLY(val[2], Factory.APPLY_BLOCK([]))\n      loc result, val[0], val[6]\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 544)\n  def _reduce_154(val, _values, result)\n          # Remove this class' name from the namestack as all nested classes have been parsed\n      namepop\n      definition = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])\n      loc(definition, val[0], val[6])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 555)\n  def _reduce_155(val, _values, result)\n     namestack(val[0][:value]) ; result = val[0]\n    result\n  end\n.,.,\n\n# reduce 156 omitted\n\n# reduce 157 omitted\n\n# reduce 158 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 564)\n  def _reduce_159(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\n# reduce 160 omitted\n\n# reduce 161 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 581)\n  def _reduce_162(val, _values, result)\n          definition = Factory.NODE(val[1], val[3], val[5])\n      loc(definition, val[0], val[6])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 586)\n  def _reduce_163(val, _values, result)\n          definition = Factory.NODE(val[1], val[3], nil)\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 597)\n  def _reduce_164(val, _values, result)\n     result = [result]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 598)\n  def _reduce_165(val, _values, result)\n     result = val[0].push(val[2])\n    result\n  end\n.,.,\n\n# reduce 166 omitted\n\n# reduce 167 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 605)\n  def _reduce_168(val, _values, result)\n     result = Factory.literal(:default); loc result, val[0]\n    result\n  end\n.,.,\n\n# reduce 169 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 609)\n  def _reduce_170(val, _values, result)\n     result = Factory.literal(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 610)\n  def _reduce_171(val, _values, result)\n     result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2]\n    result\n  end\n.,.,\n\n# reduce 172 omitted\n\n# reduce 173 omitted\n\n# reduce 174 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 619)\n  def _reduce_175(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 625)\n  def _reduce_176(val, _values, result)\n          definition = Factory.FUNCTION(val[1][:value], val[2], val[5], val[3])\n      loc(definition, val[0], val[6])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 631)\n  def _reduce_177(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 632)\n  def _reduce_178(val, _values, result)\n     result = val[1].access(val[3]) ; loc result, val[1], val[4]\n    result\n  end\n.,.,\n\n# reduce 179 omitted\n\n# reduce 180 omitted\n\n# reduce 181 omitted\n\n# reduce 182 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 642)\n  def _reduce_183(val, _values, result)\n     error val[0], \"'class' keyword not allowed at this location\"\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 643)\n  def _reduce_184(val, _values, result)\n     error val[0], \"A quoted string is not valid as a name here\"\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 647)\n  def _reduce_185(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 648)\n  def _reduce_186(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 649)\n  def _reduce_187(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 650)\n  def _reduce_188(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 651)\n  def _reduce_189(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 655)\n  def _reduce_190(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 656)\n  def _reduce_191(val, _values, result)\n     result = val[0].push(val[2])\n    result\n  end\n.,.,\n\n# reduce 192 omitted\n\n# reduce 193 omitted\n\n# reduce 194 omitted\n\n# reduce 195 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 668)\n  def _reduce_196(val, _values, result)\n     result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 669)\n  def _reduce_197(val, _values, result)\n     result = Factory.PARAM(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 672)\n  def _reduce_198(val, _values, result)\n     result = val[1]; val[1].captures_rest\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 675)\n  def _reduce_199(val, _values, result)\n     val[1].type_expr(val[0]) ; result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 678)\n  def _reduce_200(val, _values, result)\n     result = val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 679)\n  def _reduce_201(val, _values, result)\n     result = val[0].access(val[2]) ; loc result, val[0], val[3]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 684)\n  def _reduce_202(val, _values, result)\n          definition = Factory.TYPE_ASSIGNMENT(val[0], Factory.KEY_ENTRY(val[2], val[3]))\n      loc(definition, val[0], val[3])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 689)\n  def _reduce_203(val, _values, result)\n          definition = Factory.TYPE_ASSIGNMENT(val[0], val[2].access(val[4]))\n      loc(definition, val[0], val[5])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 694)\n  def _reduce_204(val, _values, result)\n          definition = Factory.TYPE_ASSIGNMENT(val[0], val[2])\n      loc(definition, val[0], val[2])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 699)\n  def _reduce_205(val, _values, result)\n          definition = Factory.TYPE_ASSIGNMENT(val[0], val[2])\n      loc(definition, val[0], val[2])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 704)\n  def _reduce_206(val, _values, result)\n          definition = Factory.TYPE_ASSIGNMENT(val[0], val[2])\n      loc(definition, val[0], val[4])\n      result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 710)\n  def _reduce_207(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 716)\n  def _reduce_208(val, _values, result)\n            definition = Factory.TYPE_DEFINITION(val[1][:value], nil, val[3])\n        loc(definition, val[0], val[4])\n        result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 721)\n  def _reduce_209(val, _values, result)\n            definition = Factory.TYPE_DEFINITION(val[1][:value], val[3][:value], val[5])\n        loc(definition, val[0], val[6])\n        result = add_definition(definition)\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 730)\n  def _reduce_210(val, _values, result)\n         fqn = Factory.fqn(val[0][:value])\n     loc(fqn, val[0])\n     fqn['offset'] += 1\n     fqn['length'] -= 1\n     result = fqn.var\n     loc(result, val[0])\n\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 741)\n  def _reduce_211(val, _values, result)\n     result = Factory.RESERVED(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 742)\n  def _reduce_212(val, _values, result)\n     result = Factory.RESERVED(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 748)\n  def _reduce_213(val, _values, result)\n     result = Factory.LIST(val[1]); loc result, val[0], val[3]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 749)\n  def _reduce_214(val, _values, result)\n     result = Factory.literal([]) ; loc result, val[0], val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 750)\n  def _reduce_215(val, _values, result)\n     result = Factory.LIST(val[1]); loc result, val[0], val[3]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 751)\n  def _reduce_216(val, _values, result)\n     result = Factory.literal([]) ; loc result, val[0], val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 754)\n  def _reduce_217(val, _values, result)\n     result = Factory.HASH(val[1]); loc result, val[0], val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 755)\n  def _reduce_218(val, _values, result)\n     result = Factory.HASH(val[1]); loc result, val[0], val[3]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 756)\n  def _reduce_219(val, _values, result)\n     result = Factory.literal({}) ; loc result, val[0], val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 759)\n  def _reduce_220(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 760)\n  def _reduce_221(val, _values, result)\n     result = val[0].push val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 763)\n  def _reduce_222(val, _values, result)\n     result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1]\n    result\n  end\n.,.,\n\n# reduce 223 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 767)\n  def _reduce_224(val, _values, result)\n     result = Factory.literal(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\n# reduce 225 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 772)\n  def _reduce_226(val, _values, result)\n     result = Factory.literal(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 775)\n  def _reduce_227(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 776)\n  def _reduce_228(val, _values, result)\n     result = Factory.ARGUMENTS(val[0], val[2])\n    result\n  end\n.,.,\n\n# reduce 229 omitted\n\n# reduce 230 omitted\n\n# reduce 231 omitted\n\n# reduce 232 omitted\n\n# reduce 233 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 789)\n  def _reduce_234(val, _values, result)\n     result = Factory.literal(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 790)\n  def _reduce_235(val, _values, result)\n     result = Factory.literal(val[0][:value]) ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 792)\n  def _reduce_236(val, _values, result)\n     result = Factory.STRING(val[0], *val[1]) ; loc result, val[0], val[1][-1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 793)\n  def _reduce_237(val, _values, result)\n     result = Factory.literal(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 794)\n  def _reduce_238(val, _values, result)\n     result = Factory.literal(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 795)\n  def _reduce_239(val, _values, result)\n     result = Factory.literal(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 796)\n  def _reduce_240(val, _values, result)\n     result = [val[0]] + val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 797)\n  def _reduce_241(val, _values, result)\n     result = Factory.TEXT(val[0])\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 800)\n  def _reduce_242(val, _values, result)\n     result = [val[0]]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 801)\n  def _reduce_243(val, _values, result)\n     result = [val[0]] + val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 804)\n  def _reduce_244(val, _values, result)\n     result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 807)\n  def _reduce_245(val, _values, result)\n     result = Factory.SUBLOCATE(val[0], val[1]);\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 808)\n  def _reduce_246(val, _values, result)\n     result = Factory.SUBLOCATE(val[0], val[1]);\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 811)\n  def _reduce_247(val, _values, result)\n     result = Factory.EPP(val[1], val[2]); loc result, val[0]\n    result\n  end\n.,.,\n\n# reduce 248 omitted\n\n# reduce 249 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 818)\n  def _reduce_250(val, _values, result)\n     result = nil\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 819)\n  def _reduce_251(val, _values, result)\n     result = []\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 820)\n  def _reduce_252(val, _values, result)\n     result = val[1]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 823)\n  def _reduce_253(val, _values, result)\n     result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 824)\n  def _reduce_254(val, _values, result)\n     result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[2]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 825)\n  def _reduce_255(val, _values, result)\n     result = Factory.RENDER_EXPR(Factory.block_or_expression(val[2], val[1], val[3])); loc result, val[0], val[4]\n    result\n  end\n.,.,\n\n# reduce 256 omitted\n\n# reduce 257 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 831)\n  def _reduce_258(val, _values, result)\n     result = Factory.QREF(val[0][:value])  ; loc result, val[0]\n    result\n  end\n.,.,\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 834)\n  def _reduce_259(val, _values, result)\n     result = Factory.literal(val[0][:value]); loc result, val[0]\n    result\n  end\n.,.,\n\n# reduce 260 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 840)\n  def _reduce_261(val, _values, result)\n     result = nil\n    result\n  end\n.,.,\n\n# reduce 262 omitted\n\n# reduce 263 omitted\n\n# reduce 264 omitted\n\n# reduce 265 omitted\n\n# reduce 266 omitted\n\n# reduce 267 omitted\n\n# reduce 268 omitted\n\n# reduce 269 omitted\n\n# reduce 270 omitted\n\n# reduce 271 omitted\n\n# reduce 272 omitted\n\n# reduce 273 omitted\n\n# reduce 274 omitted\n\n# reduce 275 omitted\n\n# reduce 276 omitted\n\n# reduce 277 omitted\n\n# reduce 278 omitted\n\n# reduce 279 omitted\n\n# reduce 280 omitted\n\n# reduce 281 omitted\n\n# reduce 282 omitted\n\n# reduce 283 omitted\n\nmodule_eval(<<'.,.,', 'egrammar.ra', 869)\n  def _reduce_284(val, _values, result)\n     result = nil\n    result\n  end\n.,.,\n\ndef _reduce_none(val, _values, result)\n  val[0]\nend\n\n      end   # class Parser\n    end   # module Parser\n  end   # module Pops\nend   # module Puppet\n"
  },
  {
    "path": "lib/puppet/pops/parser/epp_parser.rb",
    "content": "# frozen_string_literal: true\n\n# The EppParser is a specialized Puppet Parser that starts parsing in Epp Text mode\nclass Puppet::Pops::Parser::EppParser < Puppet::Pops::Parser::Parser\n  # Initializes the epp parser support by creating a new instance of {Puppet::Pops::Parser::Lexer}\n  # configured to start in Epp Lexing mode.\n  # @return [void]\n  #\n  def initvars\n    self.lexer = Puppet::Pops::Parser::Lexer2.new()\n  end\n\n  # Parses a file expected to contain epp text/DSL logic.\n  def parse_file(file)\n    unless FileTest.exist?(file)\n      unless file =~ /\\.epp$/\n        file += \".epp\"\n      end\n    end\n    @lexer.file = file\n    _parse()\n  end\n\n  # Performs the parsing and returns the resulting model.\n  # The lexer holds state, and this is setup with {#parse_string}, or {#parse_file}.\n  #\n  # TODO: deal with options containing origin (i.e. parsing a string from externally known location).\n  # TODO: should return the model, not a Hostclass\n  #\n  # @api private\n  #\n  def _parse\n    begin\n      @yydebug = false\n      main = yyparse(@lexer, :scan_epp)\n      # #Commented out now because this hides problems in the racc grammar while developing\n      # # TODO include this when test coverage is good enough.\n      #      rescue Puppet::ParseError => except\n      #        except.line ||= @lexer.line\n      #        except.file ||= @lexer.file\n      #        except.pos  ||= @lexer.pos\n      #        raise except\n      #      rescue => except\n      #        raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, @lexer.pos, except)\n    end\n    main\n  ensure\n    @lexer.clear\n    @namestack = []\n    @definitions = []\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/epp_support.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Parser\n# This module is an integral part of the Lexer.\n# It handles scanning of EPP (Embedded Puppet), a form of string/expression interpolation similar to ERB.\n#\nrequire 'strscan'\nmodule EppSupport\n  TOKEN_RENDER_STRING = [:RENDER_STRING, nil, 0]\n  TOKEN_RENDER_EXPR   = [:RENDER_EXPR, nil, 0]\n\n  # Scans all of the content and returns it in an array\n  # Note that the terminating [false, false] token is included in the result.\n  #\n  def fullscan_epp\n    result = []\n    scan_epp { |token, value| result.push([token, value]) }\n    result\n  end\n\n  # A block must be passed to scan. It will be called with two arguments, a symbol for the token,\n  # and an instance of LexerSupport::TokenValue\n  # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / temporary data\n  # and to only convert the lexer's internal tokens on demand. It is slightly more costly to create an\n  # instance of a class defined in Ruby than an Array or Hash, but the gain is much bigger since transformation\n  # logic is avoided for many of its members (most are never used (e.g. line/pos information which is only of\n  # value in general for error messages, and for some expressions (which the lexer does not know about).\n  #\n  def scan_epp\n    # PERFORMANCE note: it is faster to access local variables than instance variables.\n    # This makes a small but notable difference since instance member access is avoided for\n    # every token in the lexed content.\n    #\n    scn   = @scanner\n    ctx   = @lexing_context\n    queue = @token_queue\n\n    lex_error(Issues::EPP_INTERNAL_ERROR, :error => 'No string or file given to lexer to process.') unless scn\n\n    ctx[:epp_mode] = :text\n    enqueue_completed([:EPP_START, nil, 0], 0)\n\n    interpolate_epp\n\n    # This is the lexer's main loop\n    until queue.empty? && scn.eos?\n      token = queue.shift || lex_token\n      if token\n        yield [ctx[:after] = token[0], token[1]]\n      end\n    end\n    if ctx[:epp_open_position]\n      lex_error(Issues::EPP_UNBALANCED_TAG, {}, ctx[:epp_position])\n    end\n\n    # Signals end of input\n    yield [false, false]\n  end\n\n  def interpolate_epp(skip_leading = false)\n    scn = @scanner\n    ctx = @lexing_context\n    eppscanner = EppScanner.new(scn)\n    before = scn.pos\n\n    s = eppscanner.scan(skip_leading)\n\n    case eppscanner.mode\n    when :text\n      # Should be at end of scan, or something is terribly wrong\n      unless @scanner.eos?\n        lex_error(Issues::EPP_INTERNAL_ERROR, :error => 'template scanner returns text mode and is not and end of input')\n      end\n      if s\n        # s may be nil if scanned text ends with an epp tag (i.e. no trailing text).\n        enqueue_completed([:RENDER_STRING, s, scn.pos - before], before)\n      end\n      ctx[:epp_open_position] = nil\n      # do nothing else, scanner is at the end\n\n    when :error\n      lex_error(eppscanner.issue)\n\n    when :epp\n      # It is meaningless to render empty string segments, and it is harmful to do this at\n      # the start of the scan as it prevents specification of parameters with <%- ($x, $y) -%>\n      #\n      if s && s.length > 0\n        enqueue_completed([:RENDER_STRING, s, scn.pos - before], before)\n      end\n      # switch epp_mode to general (embedded) pp logic (non rendered result)\n      ctx[:epp_mode] = :epp\n      ctx[:epp_open_position] = scn.pos\n\n    when :expr\n      # It is meaningless to render an empty string segment\n      if s && s.length > 0\n        enqueue_completed([:RENDER_STRING, s, scn.pos - before], before)\n      end\n      enqueue_completed(TOKEN_RENDER_EXPR, before)\n      # switch mode to \"epp expr interpolation\"\n      ctx[:epp_mode] = :expr\n      ctx[:epp_open_position] = scn.pos\n    else\n      lex_error(Issues::EPP_INTERNAL_ERROR, :error => \"Unknown mode #{eppscanner.mode} returned by template scanner\")\n    end\n    nil\n  end\n\n  # A scanner specialized in processing text with embedded EPP (Embedded Puppet) tags.\n  # The scanner is initialized with a StringScanner which it mutates as scanning takes place.\n  # The intent is to use one instance of EppScanner per wanted scan, and this instance represents\n  # the state after the scan.\n  #\n  # @example Sample usage\n  #   a = \"some text <% pp code %> some more text\"\n  #   scan = StringScanner.new(a)\n  #   eppscan = EppScanner.new(scan)\n  #   str = eppscan.scan\n  #   eppscan.mode # => :epp\n  #   eppscan.lines # => 0\n  #   eppscan\n  #\n  # The scanner supports\n  # * scanning text until <%, <%-, <%=\n  # * while scanning text:\n  #   * tokens <%% and %%> are translated to <% and %>, respectively, and is returned as text.\n  #   * tokens <%# and %> (or ending with -%>) and the enclosed text is a comment and is not included in the returned text\n  #   * text following a comment that ends with -%> gets trailing whitespace (up to and including a line break) trimmed\n  #     and this whitespace is not included in the returned text.\n  # * The continuation {#mode} is set to one of:\n  #   * `:epp` - for a <% token\n  #   * `:expr` - for a <%= token\n  #   * `:text` - when there was no continuation mode (e.g. when input ends with text)\n  #   * ':error` - if the tokens are unbalanced (reaching the end without a closing matching token). An error message\n  #     is then also available via the method {#message}.\n  #\n  # Note that the intent is to use this specialized scanner to scan the text parts, when continuation mode is `:epp` or `:expr`\n  # the pp lexer should advance scanning (using the string scanner) until it reaches and consumes a `-%>` or '%>´ token. If it\n  # finds a `-%> token it should pass this on as a `skip_leading` parameter when it performs the next {#scan}.\n  #\n  class EppScanner\n    # The original scanner used by the lexer/container using EppScanner\n    attr_reader :scanner\n\n    # The resulting mode after the scan.\n    # The mode is one of `:text` (the initial mode), `:epp` embedded code (no output), `:expr` (embedded\n    # expression), or `:error`\n    #\n    attr_reader :mode\n\n    # An error issue if `mode == :error`, `nil` otherwise.\n    attr_reader :issue\n\n    # If the first scan should skip leading whitespace (typically detected by the pp lexer when the\n    # pp mode end-token is found (i.e. `-%>`) and then passed on to the scanner.\n    #\n    attr_reader :skip_leading\n\n    # Creates an EppScanner based on a StringScanner that represents the state where EppScanner should start scanning.\n    # The given scanner will be mutated (i.e. position moved) to reflect the EppScanner's end state after a scan.\n    #\n    def initialize(scanner)\n      @scanner = scanner\n    end\n\n    # Here for backwards compatibility.\n    # @deprecated Use issue instead\n    # @return [String] the issue message\n    def message\n      @issue.nil? ? nil : @issue.format\n    end\n\n    # Scans from the current position in the configured scanner, advances this scanner's position until the end\n    # of the input, or to the first position after a mode switching token (`<%`, `<%-` or `<%=`). Number of processed\n    # lines and continuation mode can be obtained via {#lines}, and {#mode}.\n    #\n    # @return [String, nil] the scanned and processed text, or nil if at the end of the input.\n    #\n    def scan(skip_leading = false)\n      @mode = :text\n      @skip_leading = skip_leading\n\n      return nil if scanner.eos?\n\n      s = ''.dup\n      until scanner.eos?\n        part = @scanner.scan_until(/(<%)|\\z/)\n        if @skip_leading\n          part.sub!(/^[ \\t]*\\r?(?:\\n|\\z)?/, '')\n          @skip_leading = false\n        end\n        # The spec for %%> is to transform it into a literal %>. This is done here, as %%> otherwise would go\n        # undetected in text mode. (i.e. it is not really necessary to escape %> with %%> in text mode unless\n        # adding checks stating that a literal %> is illegal in text (unbalanced).\n        #\n        part.gsub!(/%%>/, '%>')\n        s += part\n        case @scanner.peek(1)\n        when \"\"\n          # at the end\n          # if s ends with <% then this is an error (unbalanced <% %>)\n          if s.end_with? \"<%\"\n            @mode = :error\n            @issue = Issues::EPP_UNBALANCED_EXPRESSION\n          end\n          return s\n\n        when \"-\"\n          # trim trailing whitespace on same line from accumulated s\n          # return text and signal switch to pp mode\n          @scanner.getch # drop the -\n          s.sub!(/[ \\t]*<%\\z/, '')\n          @mode = :epp\n          return s\n\n        when \"%\"\n          # verbatim text\n          # keep the scanned <%, and continue scanning after skipping one %\n          # (i.e. do nothing here)\n          @scanner.getch # drop the % to get a literal <% in the output\n\n        when \"=\"\n          # expression\n          # return text and signal switch to expression mode\n          # drop the scanned <%, and skip past -%>, or %>, but also skip %%>\n          @scanner.getch # drop the =\n          s.slice!(-2..-1)\n          @mode = :expr\n          return s\n\n        when \"#\"\n          # template comment\n\n          # drop the scanned <%, and skip past -%>, or %>, but also skip %%>\n          s.slice!(-2..-1)\n\n          # unless there is an immediate termination i.e. <%#%> scan for the next %> that is not\n          # preceded by a % (i.e. skip %%>)\n          part = scanner.scan_until(/[^%]%>/)\n          unless part\n            @issue = Issues::EPP_UNBALANCED_COMMENT\n            @mode = :error\n            return s\n          end\n          # Trim leading whitespace on the same line when start was <%#-\n          if part[1] == '-'\n            s.sub!(/[ \\t]*\\z/, '')\n          end\n\n          @skip_leading = true if part.end_with?(\"-%>\")\n          # Continue scanning for more text\n\n        else\n          # Switch to pp after having removed the <%\n          s.slice!(-2..-1)\n          @mode = :epp\n          return s\n        end\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/evaluating_parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Parser\n# Does not support \"import\" and parsing ruby files\n#\nclass EvaluatingParser\n  extend Puppet::Concurrent::ThreadLocalSingleton\n\n  attr_reader :parser\n\n  def initialize\n    @parser = Parser.new()\n  end\n\n  def parse_string(s, file_source = nil)\n    clear()\n    # Handling of syntax error can be much improved (in general), now it bails out of the parser\n    # and does not have as rich information (when parsing a string), need to update it with the file source\n    # (ideally, a syntax error should be entered as an issue, and not just thrown - but that is a general problem\n    # and an improvement that can be made in the eparser (rather than here).\n    # Also a possible improvement (if the YAML parser returns positions) is to provide correct output of position.\n    #\n    begin\n      assert_and_report(parser.parse_string(s, file_source), file_source).model\n    rescue Puppet::ParseErrorWithIssue => e\n      raise e\n    rescue Puppet::ParseError => e\n      # TODO: This is not quite right, why does not the exception have the correct file?\n      e.file = file_source unless e.file.is_a?(String) && !e.file.empty?\n      raise e\n    end\n  end\n\n  def parse_file(file)\n    clear()\n    assert_and_report(parser.parse_file(file), file).model\n  end\n\n  def evaluate_string(scope, s, file_source = nil)\n    evaluate(scope, parse_string(s, file_source))\n  end\n\n  def evaluate_file(scope, file)\n    evaluate(scope, parse_file(file))\n  end\n\n  def clear\n    @acceptor = nil\n  end\n\n  # Create a closure that can be called in the given scope\n  def closure(model, scope)\n    Evaluator::Closure::Dynamic.new(evaluator, model, scope)\n  end\n\n  def evaluate(scope, model)\n    return nil unless model\n\n    evaluator.evaluate(model, scope)\n  end\n\n  # Evaluates the given expression in a local scope with the given variable bindings\n  # set in this local scope, returns what the expression returns.\n  #\n  def evaluate_expression_with_bindings(scope, variable_bindings, expression)\n    evaluator.evaluate_block_with_bindings(scope, variable_bindings, expression)\n  end\n\n  def evaluator\n    # Do not use the cached evaluator if this is a migration run\n    if Puppet.lookup(:migration_checker) { nil }\n      return Evaluator::EvaluatorImpl.new()\n    end\n\n    @@evaluator ||= Evaluator::EvaluatorImpl.new()\n    @@evaluator\n  end\n\n  def convert_to_3x(object, scope)\n    evaluator.convert(object, scope, nil)\n  end\n\n  def validate(parse_result)\n    resulting_acceptor = acceptor()\n    validator(resulting_acceptor).validate(parse_result)\n    resulting_acceptor\n  end\n\n  def acceptor\n    Validation::Acceptor.new\n  end\n\n  def validator(acceptor)\n    Validation::ValidatorFactory_4_0.new().validator(acceptor)\n  end\n\n  def assert_and_report(parse_result, file_source)\n    return nil unless parse_result\n\n    if parse_result['source_ref'].nil? || parse_result['source_ref'] == ''\n      parse_result['source_ref'] = file_source\n    end\n    validation_result = validate(parse_result.model)\n\n    IssueReporter.assert_and_report(validation_result,\n                                    :emit_warnings => true)\n    parse_result\n  end\n\n  def quote(x)\n    self.class.quote(x)\n  end\n\n  # Translates an already parsed string that contains control characters, quotes\n  # and backslashes into a quoted string where all such constructs have been escaped.\n  # Parsing the return value of this method using the puppet parser should yield\n  # exactly the same string as the argument passed to this method\n  #\n  # The method makes an exception for the two character sequences \\$ and \\s. They\n  # will not be escaped since they have a special meaning in puppet syntax.\n  #\n  # TODO: Handle \\uXXXX characters ??\n  #\n  # @param x [String] The string to quote and \"unparse\"\n  # @return [String] The quoted string\n  #\n  def self.quote(x)\n    escaped = '\"'.dup\n    p = nil\n    x.each_char do |c|\n      case p\n      when nil\n        # do nothing\n      when \"\\t\"\n        escaped << '\\\\t'\n      when \"\\n\"\n        escaped << '\\\\n'\n      when \"\\f\"\n        escaped << '\\\\f'\n      # TODO: \\cx is a range of characters - skip for now\n      #      when \"\\c\"\n      #        escaped << '\\\\c'\n      when '\"'\n        escaped << '\\\\\"'\n      when '\\\\'\n        escaped << ((c == '$' || c == 's') ? p : '\\\\\\\\') # don't escape \\ when followed by s or $\n      else\n        escaped << p\n      end\n      p = c\n    end\n    escaped << p unless p.nil?\n    escaped << '\"'\n  end\n\n  class EvaluatingEppParser < EvaluatingParser\n    def initialize\n      @parser = EppParser.new()\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/heredoc_support.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Parser\nmodule HeredocSupport\n  include LexerSupport\n\n  # Pattern for heredoc `@(endtag[:syntax][/escapes])\n  # Produces groups for endtag (group 1), syntax (group 2), and escapes (group 3)\n  #\n  PATTERN_HEREDOC = %r{@\\(([^:/\\r\\n)]+)(?::[[:blank:]]*([a-z][a-zA-Z0-9_+]+)[[:blank:]]*)?(?:/((?:\\w|[$])*)[[:blank:]]*)?\\)}\n\n  def heredoc\n    scn = @scanner\n    ctx = @lexing_context\n    locator = @locator\n    before = scn.pos\n\n    # scanner is at position before @(\n    # find end of the heredoc spec\n    str = scn.scan_until(/\\)/) || lex_error(Issues::HEREDOC_UNCLOSED_PARENTHESIS, :followed_by => followed_by)\n    pos_after_heredoc = scn.pos\n    # Note: allows '+' as separator in syntax, but this needs validation as empty segments are not allowed\n    md = str.match(PATTERN_HEREDOC)\n    lex_error(Issues::HEREDOC_INVALID_SYNTAX) unless md\n    endtag = md[1]\n    syntax = md[2] || ''\n    escapes = md[3]\n\n    endtag.strip!\n\n    # Is this a dq string style heredoc? (endtag enclosed in \"\")\n    if endtag =~ /^\"(.*)\"$/\n      dqstring_style = true\n      endtag = ::Regexp.last_match(1).strip\n    end\n\n    lex_error(Issues::HEREDOC_EMPTY_ENDTAG) unless endtag.length >= 1\n\n    resulting_escapes = []\n    if escapes\n      escapes = \"trnsuL$\" if escapes.length < 1\n\n      escapes = escapes.split('')\n      unless escapes.length == escapes.uniq.length\n        lex_error(Issues::HEREDOC_MULTIPLE_AT_ESCAPES, :escapes => escapes)\n      end\n      resulting_escapes = [\"\\\\\"]\n      escapes.each do |e|\n        case e\n        when \"t\", \"r\", \"n\", \"s\", \"u\", \"$\"\n          resulting_escapes << e\n        when \"L\"\n          resulting_escapes += [\"\\n\", \"\\r\\n\"]\n        else\n          lex_error(Issues::HEREDOC_INVALID_ESCAPE, :actual => e)\n        end\n      end\n    end\n\n    # Produce a heredoc token to make the syntax available to the grammar\n    enqueue_completed([:HEREDOC, syntax, pos_after_heredoc - before], before)\n\n    # If this is the second or subsequent heredoc on the line, the lexing context's :newline_jump contains\n    # the position after the \\n where the next heredoc text should scan. If not set, this is the first\n    # and it should start scanning after the first found \\n (or if not found == error).\n\n    if ctx[:newline_jump]\n      scn.pos = ctx[:newline_jump]\n    else\n      scn.scan_until(/\\n/) || lex_error(Issues::HEREDOC_WITHOUT_TEXT)\n    end\n    # offset 0 for the heredoc, and its line number\n    heredoc_offset = scn.pos\n    heredoc_line = locator.line_for_offset(heredoc_offset) - 1\n\n    # Compute message to emit if there is no end (to make it refer to the opening heredoc position).\n    eof_error = create_lex_error(Issues::HEREDOC_WITHOUT_END_TAGGED_LINE)\n\n    # Text from this position (+ lexing contexts offset for any preceding heredoc) is heredoc until a line\n    # that terminates the heredoc is found.\n\n    # (Endline in EBNF form): WS* ('|' WS*)? ('-' WS*)? endtag WS* \\r? (\\n|$)\n    endline_pattern = /([[:blank:]]*)(?:([|])[[:blank:]]*)?(?:(-)[[:blank:]]*)?#{Regexp.escape(endtag)}[[:blank:]]*\\r?(?:\\n|\\z)/\n    lines = []\n    until scn.eos?\n      one_line = scn.scan_until(/(?:\\n|\\z)/)\n      raise eof_error unless one_line\n\n      md = one_line.match(endline_pattern)\n      if md\n        leading      = md[1]\n        has_margin   = md[2] == '|'\n        remove_break = md[3] == '-'\n        # Record position where next heredoc (from same line as current @()) should start scanning for content\n        ctx[:newline_jump] = scn.pos\n\n        # Process captured lines - remove leading, and trailing newline\n        # get processed string and index of removed margin/leading size per line\n        str, margin_per_line = heredoc_text(lines, leading, has_margin, remove_break)\n\n        # Use a new lexer instance configured with a sub-locator to enable correct positioning\n        sublexer = self.class.new()\n        locator = Locator::SubLocator.new(locator, str, heredoc_line, heredoc_offset, has_margin, margin_per_line)\n\n        # Emit a token that provides the grammar with location information about the lines on which the heredoc\n        # content is based.\n        enqueue([:SUBLOCATE,\n                 LexerSupport::TokenValue.new([:SUBLOCATE,\n                                               lines, lines.reduce(0) { |size, s| size + s.length }],\n                                              heredoc_offset,\n                                              locator)])\n\n        sublexer.lex_unquoted_string(str, locator, resulting_escapes, dqstring_style)\n        sublexer.interpolate_uq_to(self)\n        # Continue scan after @(...)\n        scn.pos = pos_after_heredoc\n        return\n      else\n        lines << one_line\n      end\n    end\n    raise eof_error\n  end\n\n  # Produces the heredoc text string given the individual (unprocessed) lines as an array and array with margin sizes per line\n  # @param lines [Array<String>] unprocessed lines of text in the heredoc w/o terminating line\n  # @param leading [String] the leading text up (up to pipe or other terminating char)\n  # @param has_margin [Boolean] if the left margin should be adjusted as indicated by `leading`\n  # @param remove_break [Boolean] if the line break (\\r?\\n) at the end of the last line should be removed or not\n  # @return [Array] - a tuple with resulting string, and an array with margin size per line\n  #\n  def heredoc_text(lines, leading, has_margin, remove_break)\n    if has_margin && leading.length > 0\n      leading_pattern = /^#{Regexp.escape(leading)}/\n      # TODO: This implementation is not according to the specification, but is kept to be bug compatible.\n      # The specification says that leading space up to the margin marker should be removed, but this implementation\n      # simply leaves lines that have text in the margin untouched.\n      #\n      processed_lines = lines.collect { |s| s.gsub(leading_pattern, '') }\n      margin_per_line = Array.new(processed_lines.length) { |x| lines[x].length - processed_lines[x].length }\n      lines = processed_lines\n    else\n      # Array with a 0 per line\n      margin_per_line = Array.new(lines.length, 0)\n    end\n    result = lines.join('')\n    result.gsub!(/\\r?\\n\\z/m, '') if remove_break\n    [result, margin_per_line]\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/interpolation_support.rb",
    "content": "# frozen_string_literal: true\n\n# This module is an integral part of the Lexer.\n# It defines interpolation support\n# PERFORMANCE NOTE: There are 4 very similar methods in this module that are designed to be as\n# performant as possible. While it is possible to parameterize them into one common method, the overhead\n# of passing parameters and evaluating conditional logic has a negative impact on performance.\n#\nmodule Puppet::Pops::Parser::InterpolationSupport\n  PATTERN_VARIABLE = /(::)?(\\w+::)*\\w+/\n\n  # This is the starting point for a double quoted string with possible interpolation\n  # The structure mimics that of the grammar.\n  # The logic is explicit (where the former implementation used parameters/structures) given to a\n  # generic handler.\n  # (This is both easier to understand and faster).\n  #\n  def interpolate_dq\n    scn = @scanner\n    ctx = @lexing_context\n    before = scn.pos\n    # skip the leading \" by doing a scan since the slurp_dqstring uses last matched when there is an error\n    scn.scan(/\"/)\n    value, terminator = slurp_dqstring()\n    text = value\n    after = scn.pos\n    loop do\n      case terminator\n      when '\"'\n        # simple case, there was no interpolation, return directly\n        return emit_completed([:STRING, text, scn.pos - before], before)\n      when '${'\n        count = ctx[:brace_count]\n        ctx[:brace_count] += 1\n        # The ${ terminator is counted towards the string part\n        enqueue_completed([:DQPRE, text, scn.pos - before], before)\n        # Lex expression tokens until a closing (balanced) brace count is reached\n        enqueue_until count\n        break\n      when '$'\n        varname = scn.scan(PATTERN_VARIABLE)\n        if varname\n          # The $ is counted towards the variable\n          enqueue_completed([:DQPRE, text, after - before - 1], before)\n          enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)\n          break\n        else\n          # false $ variable start\n          text += terminator\n          value, terminator = slurp_dqstring()\n          text += value\n          after = scn.pos\n        end\n      end\n    end\n    interpolate_tail_dq\n    # return the first enqueued token and shift the queue\n    @token_queue.shift\n  end\n\n  def interpolate_tail_dq\n    scn = @scanner\n    ctx = @lexing_context\n    before = scn.pos\n    value, terminator = slurp_dqstring\n    text = value\n    after = scn.pos\n    loop do\n      case terminator\n      when '\"'\n        # simple case, there was no further interpolation, return directly\n        enqueue_completed([:DQPOST, text, scn.pos - before], before)\n        return\n      when '${'\n        count = ctx[:brace_count]\n        ctx[:brace_count] += 1\n        # The ${ terminator is counted towards the string part\n        enqueue_completed([:DQMID, text, scn.pos - before], before)\n        # Lex expression tokens until a closing (balanced) brace count is reached\n        enqueue_until count\n        break\n      when '$'\n        varname = scn.scan(PATTERN_VARIABLE)\n        if varname\n          # The $ is counted towards the variable\n          enqueue_completed([:DQMID, text, after - before - 1], before)\n          enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)\n          break\n        else\n          # false $ variable start\n          text += terminator\n          value, terminator = slurp_dqstring\n          text += value\n          after = scn.pos\n        end\n      end\n    end\n    interpolate_tail_dq\n  end\n\n  # This is the starting point for a un-quoted string with possible interpolation\n  # The logic is explicit (where the former implementation used parameters/strucures) given to a\n  # generic handler.\n  # (This is both easier to understand and faster).\n  #\n  def interpolate_uq\n    scn = @scanner\n    ctx = @lexing_context\n    before = scn.pos\n    value, terminator = slurp_uqstring()\n    text = value\n    after = scn.pos\n    loop do\n      case terminator\n      when ''\n        # simple case, there was no interpolation, return directly\n        enqueue_completed([:STRING, text, scn.pos - before], before)\n        return\n      when '${'\n        count = ctx[:brace_count]\n        ctx[:brace_count] += 1\n        # The ${ terminator is counted towards the string part\n        enqueue_completed([:DQPRE, text, scn.pos - before], before)\n        # Lex expression tokens until a closing (balanced) brace count is reached\n        enqueue_until count\n        break\n      when '$'\n        varname = scn.scan(PATTERN_VARIABLE)\n        if varname\n          # The $ is counted towards the variable\n          enqueue_completed([:DQPRE, text, after - before - 1], before)\n          enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)\n          break\n        else\n          # false $ variable start\n          text += terminator\n          value, terminator = slurp_uqstring()\n          text += value\n          after = scn.pos\n        end\n      end\n    end\n    interpolate_tail_uq\n    nil\n  end\n\n  def interpolate_tail_uq\n    scn = @scanner\n    ctx = @lexing_context\n    before = scn.pos\n    value, terminator = slurp_uqstring\n    text = value\n    after = scn.pos\n    loop do\n      case terminator\n      when ''\n        # simple case, there was no further interpolation, return directly\n        enqueue_completed([:DQPOST, text, scn.pos - before], before)\n        return\n      when '${'\n        count = ctx[:brace_count]\n        ctx[:brace_count] += 1\n        # The ${ terminator is counted towards the string part\n        enqueue_completed([:DQMID, text, scn.pos - before], before)\n        # Lex expression tokens until a closing (balanced) brace count is reached\n        enqueue_until count\n        break\n      when '$'\n        varname = scn.scan(PATTERN_VARIABLE)\n        if varname\n          # The $ is counted towards the variable\n          enqueue_completed([:DQMID, text, after - before - 1], before)\n          enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)\n          break\n        else\n          # false $ variable start\n          text += terminator\n          value, terminator = slurp_uqstring\n          text += value\n          after = scn.pos\n        end\n      end\n    end\n    interpolate_tail_uq\n  end\n\n  # Enqueues lexed tokens until either end of input, or the given brace_count is reached\n  #\n  def enqueue_until brace_count\n    scn = @scanner\n    ctx = @lexing_context\n    queue = @token_queue\n    queue_base = @token_queue[0]\n\n    scn.skip(self.class::PATTERN_WS)\n    queue_size = queue.size\n    until scn.eos?\n      token = lex_token\n      if token\n        if token.equal?(queue_base)\n          # A nested #interpolate_dq call shifted the queue_base token from the @token_queue. It must\n          # be put back since it is intended for the top level #interpolate_dq call only.\n          queue.insert(0, token)\n          next # all relevant tokens are already on the queue\n        end\n        token_name = token[0]\n        ctx[:after] = token_name\n        if token_name == :RBRACE && ctx[:brace_count] == brace_count\n          qlength = queue.size - queue_size\n          if qlength == 1\n            # Single token is subject to replacement\n            queue[-1] = transform_to_variable(queue[-1])\n          elsif qlength > 1 && [:DOT, :LBRACK].include?(queue[queue_size + 1][0])\n            # A first word, number of name token followed by '[' or '.' is subject to replacement\n            # But not for other operators such as ?, +, - etc. where user must use a $ before the name\n            # to get a variable\n            queue[queue_size] = transform_to_variable(queue[queue_size])\n          end\n          return\n        end\n        queue << token\n      else\n        scn.skip(self.class::PATTERN_WS)\n      end\n    end\n  end\n\n  def transform_to_variable(token)\n    token_name = token[0]\n    if [:NUMBER, :NAME, :WORD].include?(token_name) || self.class::KEYWORD_NAMES[token_name] || @taskm_keywords[token_name]\n      t = token[1]\n      ta = t.token_array\n      [:VARIABLE, self.class::TokenValue.new([:VARIABLE, ta[1], ta[2]], t.offset, t.locator)]\n    else\n      token\n    end\n  end\n\n  # Interpolates unquoted string and transfers the result to the given lexer\n  # (This is used when a second lexer instance is used to lex a substring)\n  #\n  def interpolate_uq_to(lexer)\n    interpolate_uq\n    queue = @token_queue\n    until queue.empty?\n      lexer.enqueue(queue.shift)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/lexer2.rb",
    "content": "# frozen_string_literal: true\n\n# The Lexer is responsible for turning source text into tokens.\n# This version is a performance enhanced lexer (in comparison to the 3.x and earlier \"future parser\" lexer.\n#\n# Old returns tokens [:KEY, value, { locator = }\n# Could return [[token], locator]\n# or Token.new([token], locator) with the same API x[0] = token_symbol, x[1] = self, x[:key] = (:value, :file, :line, :pos) etc\n\nrequire 'strscan'\nrequire_relative '../../../puppet/pops/parser/lexer_support'\nrequire_relative '../../../puppet/pops/parser/heredoc_support'\nrequire_relative '../../../puppet/pops/parser/interpolation_support'\nrequire_relative '../../../puppet/pops/parser/epp_support'\nrequire_relative '../../../puppet/pops/parser/slurp_support'\n\nmodule Puppet::Pops\nmodule Parser\nclass Lexer2\n  include LexerSupport\n  include HeredocSupport\n  include InterpolationSupport\n  include SlurpSupport\n  include EppSupport\n\n  # ALl tokens have three slots, the token name (a Symbol), the token text (String), and a token text length.\n  # All operator and punctuation tokens reuse singleton arrays Tokens that require unique values create\n  # a unique array per token.\n  #\n  # PEFORMANCE NOTES:\n  # This construct reduces the amount of object that needs to be created for operators and punctuation.\n  # The length is pre-calculated for all singleton tokens. The length is used both to signal the length of\n  # the token, and to advance the scanner position (without having to advance it with a scan(regexp)).\n  #\n  TOKEN_LBRACK       = [:LBRACK,       '[',   1].freeze\n  TOKEN_LISTSTART    = [:LISTSTART,    '[',   1].freeze\n  TOKEN_RBRACK       = [:RBRACK,       ']',   1].freeze\n  TOKEN_LBRACE       = [:LBRACE,       '{',   1].freeze\n  TOKEN_RBRACE       = [:RBRACE,       '}',   1].freeze\n  TOKEN_SELBRACE     = [:SELBRACE,     '{',   1].freeze\n  TOKEN_LPAREN       = [:LPAREN,       '(',   1].freeze\n  TOKEN_WSLPAREN     = [:WSLPAREN,     '(',   1].freeze\n  TOKEN_RPAREN       = [:RPAREN,       ')',   1].freeze\n\n  TOKEN_EQUALS       = [:EQUALS,       '=',   1].freeze\n  TOKEN_APPENDS      = [:APPENDS,      '+=',  2].freeze\n  TOKEN_DELETES      = [:DELETES,      '-=',  2].freeze\n\n  TOKEN_ISEQUAL      = [:ISEQUAL,      '==',  2].freeze\n  TOKEN_NOTEQUAL     = [:NOTEQUAL,     '!=',  2].freeze\n  TOKEN_MATCH        = [:MATCH,        '=~',  2].freeze\n  TOKEN_NOMATCH      = [:NOMATCH,      '!~',  2].freeze\n  TOKEN_GREATEREQUAL = [:GREATEREQUAL, '>=',  2].freeze\n  TOKEN_GREATERTHAN  = [:GREATERTHAN,  '>',   1].freeze\n  TOKEN_LESSEQUAL    = [:LESSEQUAL,    '<=',  2].freeze\n  TOKEN_LESSTHAN     = [:LESSTHAN,     '<',   1].freeze\n\n  TOKEN_FARROW       = [:FARROW,       '=>',  2].freeze\n  TOKEN_PARROW       = [:PARROW,       '+>',  2].freeze\n\n  TOKEN_LSHIFT       = [:LSHIFT,       '<<',  2].freeze\n  TOKEN_LLCOLLECT    = [:LLCOLLECT,    '<<|', 3].freeze\n  TOKEN_LCOLLECT     = [:LCOLLECT,     '<|',  2].freeze\n\n  TOKEN_RSHIFT       = [:RSHIFT,       '>>',  2].freeze\n  TOKEN_RRCOLLECT    = [:RRCOLLECT,    '|>>', 3].freeze\n  TOKEN_RCOLLECT     = [:RCOLLECT,     '|>',  2].freeze\n\n  TOKEN_PLUS         = [:PLUS,         '+',   1].freeze\n  TOKEN_MINUS        = [:MINUS,        '-',   1].freeze\n  TOKEN_DIV          = [:DIV,          '/',   1].freeze\n  TOKEN_TIMES        = [:TIMES,        '*',   1].freeze\n  TOKEN_MODULO       = [:MODULO,       '%',   1].freeze\n\n  # rubocop:disable Layout/SpaceBeforeComma\n  TOKEN_NOT          = [:NOT,          '!',   1].freeze\n  TOKEN_DOT          = [:DOT,          '.',   1].freeze\n  TOKEN_PIPE         = [:PIPE,         '|',   1].freeze\n  TOKEN_AT           = [:AT ,          '@',   1].freeze\n  TOKEN_ATAT         = [:ATAT ,        '@@',  2].freeze\n  TOKEN_COLON        = [:COLON,        ':',   1].freeze\n  TOKEN_COMMA        = [:COMMA,        ',',   1].freeze\n  TOKEN_SEMIC        = [:SEMIC,        ';',   1].freeze\n  TOKEN_QMARK        = [:QMARK,        '?',   1].freeze\n  TOKEN_TILDE        = [:TILDE,        '~',   1].freeze # lexed but not an operator in Puppet\n  # rubocop:enable Layout/SpaceBeforeComma\n\n  TOKEN_REGEXP       = [:REGEXP,       nil,   0].freeze\n\n  TOKEN_IN_EDGE      = [:IN_EDGE,      '->',  2].freeze\n  TOKEN_IN_EDGE_SUB  = [:IN_EDGE_SUB,  '~>',  2].freeze\n  TOKEN_OUT_EDGE     = [:OUT_EDGE,     '<-',  2].freeze\n  TOKEN_OUT_EDGE_SUB = [:OUT_EDGE_SUB, '<~',  2].freeze\n\n  # Tokens that are always unique to what has been lexed\n  TOKEN_STRING         = [:STRING,      nil,  0].freeze\n  TOKEN_WORD           = [:WORD,        nil,  0].freeze\n  TOKEN_DQPRE          = [:DQPRE,       nil,  0].freeze\n  TOKEN_DQMID          = [:DQPRE,       nil,  0].freeze\n  TOKEN_DQPOS          = [:DQPRE,       nil,  0].freeze\n  TOKEN_NUMBER         = [:NUMBER,      nil,  0].freeze\n  TOKEN_VARIABLE       = [:VARIABLE,    nil,  1].freeze\n  TOKEN_VARIABLE_EMPTY = [:VARIABLE,    '',   1].freeze\n\n  # HEREDOC has syntax as an argument.\n  TOKEN_HEREDOC        = [:HEREDOC,     nil,  0].freeze\n\n  # EPP_START is currently a marker token, may later get syntax\n  # rubocop:disable Layout/ExtraSpacing\n  TOKEN_EPPSTART    = [:EPP_START,      nil,  0].freeze\n  TOKEN_EPPEND      = [:EPP_END,       '%>',  2].freeze\n  TOKEN_EPPEND_TRIM = [:EPP_END_TRIM, '-%>',  3].freeze\n  # rubocop:enable Layout/ExtraSpacing\n\n  # This is used for unrecognized tokens, will always be a single character. This particular instance\n  # is not used, but is kept here for documentation purposes.\n  TOKEN_OTHER = [:OTHER, nil, 0]\n\n  # Keywords are all singleton tokens with pre calculated lengths.\n  # Booleans are pre-calculated (rather than evaluating the strings \"false\" \"true\" repeatedly.\n  #\n  KEYWORDS = {\n    'case' => [:CASE, 'case', 4],\n    'class' => [:CLASS, 'class', 5],\n    'default' => [:DEFAULT, 'default', 7],\n    'define' => [:DEFINE, 'define', 6],\n    'if' => [:IF, 'if', 2],\n    'elsif' => [:ELSIF, 'elsif', 5],\n    'else' => [:ELSE, 'else', 4],\n    'inherits' => [:INHERITS, 'inherits', 8],\n    'node' => [:NODE, 'node', 4],\n    'and' => [:AND, 'and', 3],\n    'or' => [:OR, 'or', 2],\n    'undef' => [:UNDEF,    'undef',    5],\n    'false' => [:BOOLEAN,  false,      5],\n    'true' => [:BOOLEAN, true, 4],\n    'in' => [:IN, 'in', 2],\n    'unless' => [:UNLESS, 'unless', 6],\n    'function' => [:FUNCTION, 'function', 8],\n    'type' => [:TYPE,     'type',     4],\n    'attr' => [:ATTR,     'attr',     4],\n    'private' => [:PRIVATE, 'private', 7],\n  }\n\n  KEYWORDS.each { |_k, v| v[1].freeze; v.freeze }\n  KEYWORDS.freeze\n\n  # Reverse lookup of keyword name to string\n  KEYWORD_NAMES = {}\n  KEYWORDS.each { |k, v| KEYWORD_NAMES[v[0]] = k }\n  KEYWORD_NAMES.freeze\n\n  PATTERN_WS        = /[[:blank:]\\r]+/\n  PATTERN_NON_WS    = /\\w+\\b?/\n\n  # The single line comment includes the line ending.\n  PATTERN_COMMENT   = /#.*\\r?/\n  PATTERN_MLCOMMENT = %r{/\\*(.*?)\\*/}m\n\n  PATTERN_REGEX     = %r{/[^/]*/}\n  PATTERN_REGEX_END = %r{/}\n  PATTERN_REGEX_A   = %r{\\A/} # for replacement to \"\"\n  PATTERN_REGEX_Z   = %r{/\\Z} # for replacement to \"\"\n  PATTERN_REGEX_ESC = %r{\\\\/} # for replacement to \"/\"\n\n  # The 3x patterns:\n  # PATTERN_CLASSREF       = %r{((::){0,1}[A-Z][-\\w]*)+}\n  # PATTERN_NAME           = %r{((::)?[a-z0-9][-\\w]*)(::[a-z0-9][-\\w]*)*}\n\n  # The NAME and CLASSREF in 4x are strict. Each segment must start with\n  # a letter a-z and may not contain dashes (\\w includes letters, digits and _).\n  #\n  PATTERN_CLASSREF       = /((::){0,1}[A-Z]\\w*)+/\n  PATTERN_NAME           = /^((::)?[a-z]\\w*)(::[a-z]\\w*)*$/\n\n  PATTERN_BARE_WORD      = /((?:::){0,1}(?:[a-z_](?:[\\w-]*\\w)?))+/\n\n  PATTERN_DOLLAR_VAR     = /\\$(::)?(\\w+::)*\\w+/\n  PATTERN_NUMBER         = /\\b(?:0[xX][0-9A-Fa-f]+|0?\\d+(?:\\.\\d+)?(?:[eE]-?\\d+)?)\\b/\n\n  # PERFORMANCE NOTE:\n  # Comparison against a frozen string is faster (than unfrozen).\n  #\n  STRING_BSLASH_SLASH = '\\/'\n\n  attr_reader :locator\n\n  def initialize\n    @selector = {\n      '.' => -> { emit(TOKEN_DOT, @scanner.pos) },\n      ',' => -> { emit(TOKEN_COMMA, @scanner.pos) },\n      '[' => lambda do\n        before = @scanner.pos\n        # Must check the preceding character to see if it is whitespace.\n        # The fastest thing to do is to simply byteslice to get the string ending at the offset before\n        # and then check what the last character is. (This is the same as  what an locator.char_offset needs\n        # to compute, but with less overhead of trying to find out the global offset from a local offset in the\n        # case when this is sublocated in a heredoc).\n        if before == 0 || @scanner.string.byteslice(0, before)[-1] =~ /[[:blank:]\\r\\n]+/\n          emit(TOKEN_LISTSTART, before)\n        else\n          emit(TOKEN_LBRACK, before)\n        end\n      end,\n      ']' => -> { emit(TOKEN_RBRACK, @scanner.pos) },\n      '(' => lambda do\n        before = @scanner.pos\n        # If first on a line, or only whitespace between start of line and '('\n        # then the token is special to avoid being taken as start of a call.\n        line_start = @lexing_context[:line_lexical_start]\n        if before == line_start || @scanner.string.byteslice(line_start, before - line_start) =~ /\\A[[:blank:]\\r]+\\Z/\n          emit(TOKEN_WSLPAREN, before)\n        else\n          emit(TOKEN_LPAREN, before)\n        end\n      end,\n      ')' => -> { emit(TOKEN_RPAREN, @scanner.pos) },\n      ';' => -> { emit(TOKEN_SEMIC, @scanner.pos) },\n      '?' => -> { emit(TOKEN_QMARK, @scanner.pos) },\n      '*' => -> { emit(TOKEN_TIMES, @scanner.pos) },\n      '%' => lambda do\n        scn = @scanner\n        before = scn.pos\n        la = scn.peek(2)\n        if la[1] == '>' && @lexing_context[:epp_mode]\n          scn.pos += 2\n          if @lexing_context[:epp_mode] == :expr\n            enqueue_completed(TOKEN_EPPEND, before)\n          end\n          @lexing_context[:epp_mode] = :text\n          interpolate_epp\n        else\n          emit(TOKEN_MODULO, before)\n        end\n      end,\n      '{' => lambda do\n        # The lexer needs to help the parser since the technology used cannot deal with\n        # lookahead of same token with different precedence. This is solved by making left brace\n        # after ? into a separate token.\n        #\n        @lexing_context[:brace_count] += 1\n        emit(if @lexing_context[:after] == :QMARK\n               TOKEN_SELBRACE\n             else\n               TOKEN_LBRACE\n             end, @scanner.pos)\n      end,\n      '}' => lambda do\n        @lexing_context[:brace_count] -= 1\n        emit(TOKEN_RBRACE, @scanner.pos)\n      end,\n\n      # TOKENS @, @@, @(\n      '@' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        case la[1]\n        when '@'\n          emit(TOKEN_ATAT, scn.pos) # TODO; Check if this is good for the grammar\n        when '('\n          heredoc\n        else\n          emit(TOKEN_AT, scn.pos)\n        end\n      end,\n\n      # TOKENS |, |>, |>>\n      '|' => lambda do\n        scn = @scanner\n        la = scn.peek(3)\n        emit(if la[1] == '>'\n               la[2] == '>' ? TOKEN_RRCOLLECT : TOKEN_RCOLLECT\n             else\n               TOKEN_PIPE\n             end, scn.pos)\n      end,\n\n      # TOKENS =, =>, ==, =~\n      '=' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        emit(case la[1]\n             when '='\n               TOKEN_ISEQUAL\n             when '>'\n               TOKEN_FARROW\n             when '~'\n               TOKEN_MATCH\n             else\n               TOKEN_EQUALS\n             end, scn.pos)\n      end,\n\n      # TOKENS '+', '+=', and '+>'\n      '+' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        emit(case la[1]\n             when '='\n               TOKEN_APPENDS\n             when '>'\n               TOKEN_PARROW\n             else\n               TOKEN_PLUS\n             end, scn.pos)\n      end,\n\n      # TOKENS '-', '->', and epp '-%>' (end of interpolation with trim)\n      '-' => lambda do\n        scn = @scanner\n        la = scn.peek(3)\n        before = scn.pos\n        if @lexing_context[:epp_mode] && la[1] == '%' && la[2] == '>'\n          scn.pos += 3\n          if @lexing_context[:epp_mode] == :expr\n            enqueue_completed(TOKEN_EPPEND_TRIM, before)\n          end\n          interpolate_epp(:with_trim)\n        else\n          emit(case la[1]\n               when '>'\n                 TOKEN_IN_EDGE\n               when '='\n                 TOKEN_DELETES\n               else\n                 TOKEN_MINUS\n               end, before)\n        end\n      end,\n\n      # TOKENS !, !=, !~\n      '!' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        emit(case la[1]\n             when '='\n               TOKEN_NOTEQUAL\n             when '~'\n               TOKEN_NOMATCH\n             else\n               TOKEN_NOT\n             end, scn.pos)\n      end,\n\n      # TOKENS ~>, ~\n      '~' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        emit(la[1] == '>' ? TOKEN_IN_EDGE_SUB : TOKEN_TILDE, scn.pos)\n      end,\n\n      '#' => -> { @scanner.skip(PATTERN_COMMENT); nil },\n\n      # TOKENS '/', '/*' and '/ regexp /'\n      '/' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        if la[1] == '*'\n          lex_error(Issues::UNCLOSED_MLCOMMENT) if scn.skip(PATTERN_MLCOMMENT).nil?\n          nil\n        else\n          before = scn.pos\n          # regexp position is a regexp, else a div\n          value = scn.scan(PATTERN_REGEX) if regexp_acceptable?\n          if value\n            # Ensure an escaped / was not matched\n            while escaped_end(value)\n              more = scn.scan_until(PATTERN_REGEX_END)\n              return emit(TOKEN_DIV, before) unless more\n\n              value << more\n            end\n            regex = value.sub(PATTERN_REGEX_A, '').sub(PATTERN_REGEX_Z, '').gsub(PATTERN_REGEX_ESC, '/')\n            emit_completed([:REGEX, Regexp.new(regex), scn.pos - before], before)\n          else\n            emit(TOKEN_DIV, before)\n          end\n        end\n      end,\n\n      # TOKENS <, <=, <|, <<|, <<, <-, <~\n      '<' => lambda do\n        scn = @scanner\n        la = scn.peek(3)\n        emit(case la[1]\n             when '<'\n               if la[2] == '|'\n                 TOKEN_LLCOLLECT\n               else\n                 TOKEN_LSHIFT\n               end\n             when '='\n               TOKEN_LESSEQUAL\n             when '|'\n               TOKEN_LCOLLECT\n             when '-'\n               TOKEN_OUT_EDGE\n             when '~'\n               TOKEN_OUT_EDGE_SUB\n             else\n               TOKEN_LESSTHAN\n             end, scn.pos)\n      end,\n\n      # TOKENS >, >=, >>\n      '>' => lambda do\n        scn = @scanner\n        la = scn.peek(2)\n        emit(case la[1]\n             when '>'\n               TOKEN_RSHIFT\n             when '='\n               TOKEN_GREATEREQUAL\n             else\n               TOKEN_GREATERTHAN\n             end, scn.pos)\n      end,\n\n      # TOKENS :, ::CLASSREF, ::NAME\n      ':' => lambda do\n        scn = @scanner\n        la = scn.peek(3)\n        before = scn.pos\n        if la[1] == ':'\n          # PERFORMANCE NOTE: This could potentially be speeded up by using a case/when listing all\n          # upper case letters. Alternatively, the 'A', and 'Z' comparisons may be faster if they are\n          # frozen.\n          #\n          la2 = la[2]\n          if la2 >= 'A' && la2 <= 'Z'\n            # CLASSREF or error\n            value = scn.scan(PATTERN_CLASSREF)\n            if value && scn.peek(2) != '::'\n              after = scn.pos\n              emit_completed([:CLASSREF, value.freeze, after - before], before)\n            else\n              # move to faulty position ('::<uc-letter>' was ok)\n              scn.pos = scn.pos + 3\n              lex_error(Issues::ILLEGAL_FULLY_QUALIFIED_CLASS_REFERENCE)\n            end\n          else\n            value = scn.scan(PATTERN_BARE_WORD)\n            if value\n              if value =~ PATTERN_NAME\n                emit_completed([:NAME, value.freeze, scn.pos - before], before)\n              else\n                emit_completed([:WORD, value.freeze, scn.pos - before], before)\n              end\n            else\n              # move to faulty position ('::' was ok)\n              scn.pos = scn.pos + 2\n              lex_error(Issues::ILLEGAL_FULLY_QUALIFIED_NAME)\n            end\n          end\n        else\n          emit(TOKEN_COLON, before)\n        end\n      end,\n\n      '$' => lambda do\n        scn = @scanner\n        before = scn.pos\n        value = scn.scan(PATTERN_DOLLAR_VAR)\n        if value\n          emit_completed([:VARIABLE, value[1..].freeze, scn.pos - before], before)\n        else\n          # consume the $ and let higher layer complain about the error instead of getting a syntax error\n          emit(TOKEN_VARIABLE_EMPTY, before)\n        end\n      end,\n\n      '\"' => lambda do\n        # Recursive string interpolation, 'interpolate' either returns a STRING token, or\n        # a DQPRE with the rest of the string's tokens placed in the @token_queue\n        interpolate_dq\n      end,\n\n      \"'\" => lambda do\n        scn = @scanner\n        before = scn.pos\n        emit_completed([:STRING, slurp_sqstring.freeze, scn.pos - before], before)\n      end,\n\n      \"\\n\" => lambda do\n        # If heredoc_cont is in effect there are heredoc text lines to skip over\n        # otherwise just skip the newline.\n        #\n        ctx = @lexing_context\n        if ctx[:newline_jump]\n          @scanner.pos = ctx[:newline_jump]\n          ctx[:newline_jump] = nil\n        else\n          @scanner.pos += 1\n        end\n        ctx[:line_lexical_start] = @scanner.pos\n        nil\n      end,\n      '' => -> { nil } # when the peek(1) returns empty\n    }\n\n    [' ', \"\\t\", \"\\r\"].each { |c| @selector[c] = -> { @scanner.skip(PATTERN_WS); nil } }\n\n    ('0'..'9').each do |c|\n      @selector[c] = lambda do\n        scn = @scanner\n        before = scn.pos\n        value = scn.scan(PATTERN_NUMBER)\n        if value\n          length = scn.pos - before\n          assert_numeric(value, before)\n          emit_completed([:NUMBER, value.freeze, length], before)\n        else\n          invalid_number = scn.scan_until(PATTERN_NON_WS)\n          if before > 1\n            after = scn.pos\n            scn.pos = before - 1\n            if scn.peek(1) == '.'\n              # preceded by a dot. Is this a bad decimal number then?\n              scn.pos = before - 2\n              while scn.peek(1) =~ /^\\d$/\n                invalid_number = nil\n                before = scn.pos\n                break if before == 0\n\n                scn.pos = scn.pos - 1\n              end\n            end\n            scn.pos = before\n            invalid_number ||= scn.peek(after - before)\n          end\n          assert_numeric(invalid_number, before)\n          scn.pos = before + 1\n          lex_error(Issues::ILLEGAL_NUMBER, { :value => invalid_number })\n        end\n      end\n    end\n    ('a'..'z').to_a.push('_').each do |c|\n      @selector[c] = lambda do\n        scn = @scanner\n        before = scn.pos\n        value = scn.scan(PATTERN_BARE_WORD)\n        if value && value =~ PATTERN_NAME\n          emit_completed(KEYWORDS[value] || @taskm_keywords[value] || [:NAME, value.freeze, scn.pos - before], before)\n        elsif value\n          emit_completed([:WORD, value.freeze, scn.pos - before], before)\n        else\n          # move to faulty position ([a-z_] was ok)\n          scn.pos = scn.pos + 1\n          fully_qualified = scn.match?(/::/)\n          if fully_qualified\n            lex_error(Issues::ILLEGAL_FULLY_QUALIFIED_NAME)\n          else\n            lex_error(Issues::ILLEGAL_NAME_OR_BARE_WORD)\n          end\n        end\n      end\n    end\n\n    ('A'..'Z').each do |c|\n      @selector[c] = lambda do\n        scn = @scanner\n        before = scn.pos\n        value = @scanner.scan(PATTERN_CLASSREF)\n        if value && @scanner.peek(2) != '::'\n          emit_completed([:CLASSREF, value.freeze, scn.pos - before], before)\n        else\n          # move to faulty position ([A-Z] was ok)\n          scn.pos = scn.pos + 1\n          lex_error(Issues::ILLEGAL_CLASS_REFERENCE)\n        end\n      end\n    end\n\n    @selector.default = lambda do\n      # In case of unicode spaces of various kinds that are captured by a regexp, but not by the\n      # simpler case expression above (not worth handling those special cases with better performance).\n      scn = @scanner\n      if scn.skip(PATTERN_WS)\n        nil\n      else\n        # \"unrecognized char\"\n        emit([:OTHER, scn.peek(0), 1], scn.pos)\n      end\n    end\n    @selector.each { |k, _v| k.freeze }\n    @selector.freeze\n  end\n\n  # Determine if last char of value is escaped by a backslash\n  def escaped_end(value)\n    escaped = false\n    if value.end_with?(STRING_BSLASH_SLASH)\n      value[1...-1].each_codepoint do |cp|\n        if cp == 0x5c # backslash\n          escaped = !escaped\n        else\n          escaped = false\n        end\n      end\n    end\n    escaped\n  end\n\n  # Clears the lexer state (it is not required to call this as it will be garbage collected\n  # and the next lex call (lex_string, lex_file) will reset the internal state.\n  #\n  def clear\n    # not really needed, but if someone wants to ensure garbage is collected as early as possible\n    @scanner = nil\n    @locator = nil\n    @lexing_context = nil\n  end\n\n  # Convenience method, and for compatibility with older lexer. Use the lex_string instead which allows\n  # passing the path to use without first having to call file= (which reads the file if it exists).\n  # (Bad form to use overloading of assignment operator for something that is not really an assignment. Also,\n  # overloading of = does not allow passing more than one argument).\n  #\n  def string=(string)\n    lex_string(string, nil)\n  end\n\n  def lex_string(string, path = nil)\n    initvars\n    assert_not_bom(string)\n    @scanner = StringScanner.new(string)\n    @locator = Locator.locator(string, path)\n  end\n\n  # Lexes an unquoted string.\n  # @param string [String] the string to lex\n  # @param locator [Locator] the locator to use (a default is used if nil is given)\n  # @param escapes [Array<String>] array of character strings representing the escape sequences to transform\n  # @param interpolate [Boolean] whether interpolation of expressions should be made or not.\n  #\n  def lex_unquoted_string(string, locator, escapes, interpolate)\n    initvars\n    assert_not_bom(string)\n    @scanner = StringScanner.new(string)\n    @locator = locator || Locator.locator(string, '')\n    @lexing_context[:escapes] = escapes || UQ_ESCAPES\n    @lexing_context[:uq_slurp_pattern] = if interpolate\n                                           escapes.include?('$') ? SLURP_UQ_PATTERN : SLURP_UQNE_PATTERN\n                                         else\n                                           SLURP_ALL_PATTERN\n                                         end\n  end\n\n  # Convenience method, and for compatibility with older lexer. Use the lex_file instead.\n  # (Bad form to use overloading of assignment operator for something that is not really an assignment).\n  #\n  def file=(file)\n    lex_file(file)\n  end\n\n  # TODO: This method should not be used, callers should get the locator since it is most likely required to\n  # compute line, position etc given offsets.\n  #\n  def file\n    @locator ? @locator.file : nil\n  end\n\n  # Initializes lexing of the content of the given file. An empty string is used if the file does not exist.\n  #\n  def lex_file(file)\n    initvars\n    contents = Puppet::FileSystem.exist?(file) ? Puppet::FileSystem.read(file, :mode => 'rb', :encoding => 'utf-8') : ''\n    assert_not_bom(contents)\n    @scanner = StringScanner.new(contents.freeze)\n    @locator = Locator.locator(contents, file)\n  end\n\n  def initvars\n    @token_queue = []\n    # NOTE: additional keys are used; :escapes, :uq_slurp_pattern, :newline_jump, :epp_*\n    @lexing_context = {\n      :brace_count => 0,\n      :after => nil,\n      :line_lexical_start => 0\n    }\n    # Use of --tasks introduces the new keyword 'plan'\n    @taskm_keywords = Puppet[:tasks] ? { 'plan' => [:PLAN, 'plan', 4], 'apply' => [:APPLY, 'apply', 5] }.freeze : EMPTY_HASH\n  end\n\n  # Scans all of the content and returns it in an array\n  # Note that the terminating [false, false] token is included in the result.\n  #\n  def fullscan\n    result = []\n    scan { |token| result.push(token) }\n    result\n  end\n\n  # A block must be passed to scan. It will be called with two arguments, a symbol for the token,\n  # and an instance of LexerSupport::TokenValue\n  # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / temporary data\n  # and to only convert the lexer's internal tokens on demand. It is slightly more costly to create an\n  # instance of a class defined in Ruby than an Array or Hash, but the gain is much bigger since transformation\n  # logic is avoided for many of its members (most are never used (e.g. line/pos information which is only of\n  # value in general for error messages, and for some expressions (which the lexer does not know about).\n  #\n  def scan\n    # PERFORMANCE note: it is faster to access local variables than instance variables.\n    # This makes a small but notable difference since instance member access is avoided for\n    # every token in the lexed content.\n    #\n    scn = @scanner\n    lex_error_without_pos(Issues::NO_INPUT_TO_LEXER) unless scn\n\n    ctx   = @lexing_context\n    queue = @token_queue\n    selector = @selector\n\n    scn.skip(PATTERN_WS)\n\n    # This is the lexer's main loop\n    until queue.empty? && scn.eos?\n      token = queue.shift || selector[scn.peek(1)].call\n      if token\n        ctx[:after] = token[0]\n        yield token\n      end\n    end\n\n    # Signals end of input\n    yield [false, false]\n  end\n\n  # This lexes one token at the current position of the scanner.\n  # PERFORMANCE NOTE: Any change to this logic should be performance measured.\n  #\n  def lex_token\n    @selector[@scanner.peek(1)].call\n  end\n\n  # Emits (produces) a token [:tokensymbol, TokenValue] and moves the scanner's position past the token\n  #\n  def emit(token, byte_offset)\n    @scanner.pos = byte_offset + token[2]\n    [token[0], TokenValue.new(token, byte_offset, @locator)]\n  end\n\n  # Emits the completed token on the form [:tokensymbol, TokenValue. This method does not alter\n  # the scanner's position.\n  #\n  def emit_completed(token, byte_offset)\n    [token[0], TokenValue.new(token, byte_offset, @locator)]\n  end\n\n  # Enqueues a completed token at the given offset\n  def enqueue_completed(token, byte_offset)\n    @token_queue << emit_completed(token, byte_offset)\n  end\n\n  # Allows subprocessors for heredoc etc to enqueue tokens that are tokenized by a different lexer instance\n  #\n  def enqueue(emitted_token)\n    @token_queue << emitted_token\n  end\n\n  # Answers after which tokens it is acceptable to lex a regular expression.\n  # PERFORMANCE NOTE:\n  # It may be beneficial to turn this into a hash with default value of true for missing entries.\n  # A case expression with literal values will however create a hash internally. Since a reference is\n  # always needed to the hash, this access is almost as costly as a method call.\n  #\n  def regexp_acceptable?\n    case @lexing_context[:after]\n\n    # Ends of (potential) R-value generating expressions\n    when :RPAREN, :RBRACK, :RRCOLLECT, :RCOLLECT\n      false\n\n    # End of (potential) R-value - but must be allowed because of case expressions\n    # Called out here to not be mistaken for a bug.\n    when :RBRACE\n      true\n\n    # Operands (that can be followed by DIV (even if illegal in grammar)\n    when :NAME, :CLASSREF, :NUMBER, :STRING, :BOOLEAN, :DQPRE, :DQMID, :DQPOST, :HEREDOC, :REGEX, :VARIABLE, :WORD\n      false\n\n    else\n      true\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/lexer_support.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Parser\nrequire_relative '../../../puppet/util/multi_match'\n\n# This is an integral part of the Lexer. It is broken out into a separate module\n# for maintainability of the code, and making the various parts of the lexer focused.\n#\nmodule LexerSupport\n  # Returns \"<eof>\" if at end of input, else the following 5 characters with \\n \\r \\t escaped\n  def followed_by\n    return \"<eof>\" if @scanner.eos?\n\n    result = @scanner.rest[0, 5] + \"...\"\n    result.gsub!(\"\\t\", '\\t')\n    result.gsub!(\"\\n\", '\\n')\n    result.gsub!(\"\\r\", '\\r')\n    result\n  end\n\n  # Returns a quoted string using \" or ' depending on the given a strings's content\n  def format_quote(q)\n    if q == \"'\"\n      '\"\\'\"'\n    else\n      \"'#{q}'\"\n    end\n  end\n\n  # Raises a Puppet::LexError with the given message\n  def lex_error_without_pos(issue, args = {})\n    raise Puppet::ParseErrorWithIssue.new(issue.format(args), nil, nil, nil, nil, issue.issue_code, args)\n  end\n\n  # Raises a Puppet::ParserErrorWithIssue with the given issue and arguments\n  def lex_error(issue, args = {}, pos = nil)\n    raise create_lex_error(issue, args, pos)\n  end\n\n  def filename\n    file = @locator.file\n    file.is_a?(String) && !file.empty? ? file : nil\n  end\n\n  def line(pos)\n    @locator.line_for_offset(pos || @scanner.pos)\n  end\n\n  def position(pos)\n    @locator.pos_on_line(pos || @scanner.pos)\n  end\n\n  def lex_warning(issue, args = {}, pos = nil)\n    Puppet::Util::Log.create({\n                               :level => :warning,\n                               :message => issue.format(args),\n                               :issue_code => issue.issue_code,\n                               :file => filename,\n                               :line => line(pos),\n                               :pos => position(pos),\n                             })\n  end\n\n  # @param issue [Issues::Issue] the issue\n  # @param args [Hash<Symbol,String>] Issue arguments\n  # @param pos [Integer]\n  # @return [Puppet::ParseErrorWithIssue] the created error\n  def create_lex_error(issue, args = {}, pos = nil)\n    Puppet::ParseErrorWithIssue.new(\n      issue.format(args),\n      filename,\n      line(pos),\n      position(pos),\n      nil,\n      issue.issue_code,\n      args\n    )\n  end\n\n  # Asserts that the given string value is a float, or an integer in decimal, octal or hex form.\n  # An error is raised if the given value does not comply.\n  #\n  def assert_numeric(value, pos)\n    case value\n    when /^0[xX]/\n      lex_error(Issues::INVALID_HEX_NUMBER, { :value => value }, pos)     unless value =~ /^0[xX][0-9A-Fa-f]+$/\n\n    when /^0[^.]/\n      lex_error(Issues::INVALID_OCTAL_NUMBER, { :value => value }, pos)   unless value =~ /^0[0-7]+$/\n\n    when /^\\d+[eE.]/\n      lex_error(Issues::INVALID_DECIMAL_NUMBER, { :value => value }, pos) unless value =~ /^\\d+(?:\\.\\d+)?(?:[eE]-?\\d+)?$/\n\n    else\n      lex_error(Issues::ILLEGAL_NUMBER, { :value => value }, pos) unless value =~ /^\\d+$/\n    end\n  end\n\n  # A TokenValue keeps track of the token symbol, the lexed text for the token, its length\n  # and its position in its source container. There is a cost associated with computing the\n  # line and position on line information.\n  #\n  class TokenValue < Locatable\n    attr_reader :token_array\n    attr_reader :offset\n    attr_reader :locator\n\n    def initialize(token_array, offset, locator)\n      @token_array = token_array\n      @offset = offset\n      @locator = locator\n    end\n\n    def length\n      @token_array[2]\n    end\n\n    def [](key)\n      case key\n      when :value\n        @token_array[1]\n      when :file\n        @locator.file\n      when :line\n        @locator.line_for_offset(@offset)\n      when :pos\n        @locator.pos_on_line(@offset)\n      when :length\n        @token_array[2]\n      when :locator\n        @locator\n      when :offset\n        @offset\n      else\n        nil\n      end\n    end\n\n    def to_s\n      # This format is very compact and is intended for debugging output from racc parser in\n      # debug mode. If this is made more elaborate the output from a debug run becomes very hard to read.\n      #\n      \"'#{self[:value]} #{@token_array[0]}'\"\n    end\n    # TODO: Make this comparable for testing\n    # vs symbolic, vs array with symbol and non hash, array with symbol and hash)\n    #\n  end\n\n  MM = Puppet::Util::MultiMatch\n  MM_ANY = MM::NOT_NIL\n\n  BOM_UTF_8      = MM.new(0xEF, 0xBB, 0xBF,        MM_ANY)\n  BOM_UTF_16_1   = MM.new(0xFE, 0xFF,              MM_ANY, MM_ANY)\n  BOM_UTF_16_2   = MM.new(0xFF, 0xFE,              MM_ANY, MM_ANY)\n  BOM_UTF_32_1   = MM.new(0x00, 0x00, 0xFE, 0xFF)\n  BOM_UTF_32_2   = MM.new(0xFF, 0xFE, 0x00, 0x00)\n\n  BOM_UTF_1      = MM.new(0xF7, 0x64, 0x4C, MM_ANY)\n  BOM_UTF_EBCDIC = MM.new(0xDD, 0x73, 0x66, 0x73)\n  BOM_SCSU       = MM.new(0x0E, 0xFE, 0xFF,        MM_ANY)\n  BOM_BOCU       = MM.new(0xFB, 0xEE, 0x28,        MM_ANY)\n  BOM_GB_18030   = MM.new(0x84, 0x31, 0x95, 0x33)\n\n  LONGEST_BOM    = 4\n\n  def assert_not_bom(content)\n    name, size =\n      case bom = get_bom(content)\n\n      when BOM_UTF_32_1, BOM_UTF_32_2\n        ['UTF-32', 4]\n\n      when BOM_GB_18030\n        ['GB-18030', 4]\n\n      when BOM_UTF_EBCDIC\n        ['UTF-EBCDIC', 4]\n\n      when BOM_SCSU\n        ['SCSU', 3]\n\n      when BOM_UTF_8\n        ['UTF-8', 3]\n\n      when BOM_UTF_1\n        ['UTF-1', 3]\n\n      when BOM_BOCU\n        ['BOCU', 3]\n\n      when BOM_UTF_16_1, BOM_UTF_16_2\n        ['UTF-16', 2]\n\n      else\n        return\n      end\n\n    lex_error_without_pos(\n      Puppet::Pops::Issues::ILLEGAL_BOM,\n      { :format_name => name,\n        :bytes => \"[#{bom.values[0, size].map { |b| '%X' % b }.join(' ')}]\" }\n    )\n  end\n\n  def get_bom(content)\n    # get 5 bytes as efficiently as possible (none of the string methods works since a bom consists of\n    # illegal characters on most platforms, and there is no get_bytes(n). Explicit calls are faster than\n    # looping with a lambda. The get_byte returns nil if there are too few characters, and they\n    # are changed to spaces\n    MM.new(\n      (content.getbyte(0) || ' '),\n      (content.getbyte(1) || ' '),\n      (content.getbyte(2) || ' '),\n      (content.getbyte(3) || ' ')\n    )\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/locatable.rb",
    "content": "# frozen_string_literal: true\n\n# Interface for something that is \"locatable\" (holds offset and length).\nclass Puppet::Pops::Parser::Locatable\n  # The offset in the locator's content\n  def offset\n  end\n\n  # The length in the locator from the given offset\n  def length\n  end\n\n  # This class is useful for testing\n  class Fixed < Puppet::Pops::Parser::Locatable\n    attr_reader :offset\n    attr_reader :length\n\n    def initialize(offset, length)\n      @offset = offset\n      @length = length\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/locator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Parser\n# Helper class that keeps track of where line breaks are located and can answer questions about positions.\n#\nclass Locator\n  # Creates, or recreates a Locator. A Locator is created if index is not given (a scan is then\n  # performed of the given source string.\n  #\n  def self.locator(string, file, index = nil, char_offsets = false)\n    if char_offsets\n      LocatorForChars.new(string, file, index)\n    else\n      Locator19.new(string, file, index)\n    end\n  end\n\n  # Returns the file name associated with the string content\n  def file\n  end\n\n  # Returns the string content\n  def string\n  end\n\n  def to_s\n    \"Locator for file #{file}\"\n  end\n\n  # Returns the position on line (first position on a line is 1)\n  def pos_on_line(offset)\n  end\n\n  # Returns the line number (first line is 1) for the given offset\n  def line_for_offset(offset)\n  end\n\n  # Returns the offset on line (first offset on a line is 0).\n  #\n  def offset_on_line(offset)\n  end\n\n  # Returns the character offset for a given reported offset\n  def char_offset(byte_offset)\n  end\n\n  # Returns the length measured in number of characters from the given start and end byte offset\n  def char_length(offset, end_offset)\n  end\n\n  # Extracts the text from offset with given length (measured in what the locator uses for offset)\n  # @returns String - the extracted text\n  def extract_text(offset, length)\n  end\n\n  def extract_tree_text(ast)\n    first = ast.offset\n    last = first + ast.length\n    ast._pcore_all_contents([]) do |m|\n      next unless m.is_a?(Model::Positioned)\n\n      m_offset = m.offset\n      m_last = m_offset + m.length\n      first = m_offset if m_offset < first\n      last = m_last if m_last > last\n    end\n    extract_text(first, last - first)\n  end\n\n  # Returns the line index - an array of line offsets for the start position of each line, starting at 0 for\n  # the first line.\n  #\n  def line_index\n  end\n\n  # Common byte based impl that works for all rubies (stringscanner is byte based\n  def self.compute_line_index(string)\n    scanner = StringScanner.new(string)\n    result = [0] # first line starts at 0\n    while scanner.scan_until(/\\n/)\n      result << scanner.pos\n    end\n    result.freeze\n  end\n\n  # Produces an URI with path?line=n&pos=n. If origin is unknown the URI is string:?line=n&pos=n\n  def to_uri(ast)\n    f = file\n    if f.nil? || f.empty?\n      f = 'string:'\n    else\n      f = Puppet::Util.path_to_uri(f).to_s\n    end\n    offset = ast.offset\n    URI(\"#{f}?line=#{line_for_offset(offset)}&pos=#{pos_on_line(offset)}\")\n  end\n\n  class AbstractLocator < Locator\n    attr_accessor :line_index\n    attr_reader   :string\n    attr_reader   :file\n\n    # Create a locator based on a content string, and a boolean indicating if ruby version support multi-byte strings\n    # or not.\n    #\n    def initialize(string, file, line_index = nil)\n      @string = string.freeze\n      @file = file.freeze\n      @prev_offset = nil\n      @prev_line = nil\n      @line_index = line_index.nil? ? Locator.compute_line_index(@string) : line_index\n    end\n\n    # Returns the position on line (first position on a line is 1)\n    def pos_on_line(offset)\n      offset_on_line(offset) + 1\n    end\n\n    def to_location_hash(reported_offset, end_offset)\n      pos        = pos_on_line(reported_offset)\n      offset     = char_offset(reported_offset)\n      length     = char_length(reported_offset, end_offset)\n      start_line = line_for_offset(reported_offset)\n      { :line => start_line, :pos => pos, :offset => offset, :length => length }\n    end\n\n    # Returns the index of the smallest item for which the item > the given value\n    # This is a min binary search. Although written in Ruby it is only slightly slower than\n    # the corresponding method in C in Ruby 2.0.0 - the main benefit to use this method over\n    # the Ruby C version is that it returns the index (not the value) which means there is not need\n    # to have an additional structure to get the index (or record the index in the structure). This\n    # saves both memory and CPU. It also does not require passing a block that is called since this\n    # method is specialized to search the line index.\n    #\n    def ary_bsearch_i(ary, value)\n      low = 0\n      high = ary.length\n      mid = nil\n      smaller = false\n      satisfied = false\n      v = nil\n\n      while low < high\n        mid = low + ((high - low) / 2)\n        v = (ary[mid] > value)\n        if v == true\n          satisfied = true\n          smaller = true\n        elsif !v\n          smaller = false\n        else\n          raise TypeError, \"wrong argument, must be boolean or nil, got '#{v.class}'\"\n        end\n\n        if smaller\n          high = mid\n        else\n          low = mid + 1;\n        end\n      end\n\n      return nil if low == ary.length\n      return nil unless satisfied\n\n      low\n    end\n\n    def hash\n      [string, file, line_index].hash\n    end\n\n    # Equal method needed by serializer to perform tabulation\n    def eql?(o)\n      self.class == o.class && string == o.string && file == o.file && line_index == o.line_index\n    end\n\n    # Returns the line number (first line is 1) for the given offset\n    def line_for_offset(offset)\n      if @prev_offset == offset\n        # use cache\n        return @prev_line\n      end\n\n      line_nbr = ary_bsearch_i(line_index, offset)\n      if line_nbr\n        # cache\n        @prev_offset = offset\n        @prev_line = line_nbr\n        return line_nbr\n      end\n      # If not found it is after last\n      # clear cache\n      @prev_offset = @prev_line = nil\n      line_index.size\n    end\n  end\n\n  # A Sublocator locates a concrete locator (subspace) in a virtual space.\n  # The `leading_line_count` is the (virtual) number of lines preceding the first line in the concrete locator.\n  # The `leading_offset` is the (virtual) byte offset of the first byte in the concrete locator.\n  # The `leading_line_offset` is the (virtual) offset / margin in characters for each line.\n  #\n  # This illustrates characters in the sublocator (`.`) inside the subspace (`X`):\n  #\n  #      1:XXXXXXXX\n  #      2:XXXX.... .. ... ..\n  #      3:XXXX. . .... ..\n  #      4:XXXX............\n  #\n  # This sublocator would be configured with leading_line_count = 1,\n  # leading_offset=8, and leading_line_offset=4\n  #\n  # Note that leading_offset must be the same for all lines and measured in characters.\n  #\n  # A SubLocator is only used during parsing as the parser will translate the local offsets/lengths to\n  # the parent locator when a sublocated expression is reduced. Do not call the methods\n  # `char_offset` or `char_length` as those methods will raise an error.\n  #\n  class SubLocator < AbstractLocator\n    attr_reader :locator\n    attr_reader :leading_line_count\n    attr_reader :leading_offset\n    attr_reader :has_margin\n    attr_reader :margin_per_line\n\n    def initialize(locator, str, leading_line_count, leading_offset, has_margin, margin_per_line)\n      super(str, locator.file)\n      @locator = locator\n      @leading_line_count = leading_line_count\n      @leading_offset = leading_offset\n      @has_margin = has_margin\n      @margin_per_line = margin_per_line\n\n      # Since lines can have different margin - accumulated margin per line must be computed\n      # and since this accumulated margin adjustment is needed more than once; both for start offset,\n      # and for end offset (to compute global length) it is computed up front here.\n      # The accumulated_offset holds the sum of all removed margins before a position on line n (line index is 1-n,\n      # and (unused) position 0 is always 0).\n      # The last entry is duplicated since there will be  the line \"after last line\" that would otherwise require\n      # conditional logic.\n      #\n      @accumulated_margin = margin_per_line.each_with_object([0]) { |val, memo| memo << memo[-1] + val; }\n      @accumulated_margin << @accumulated_margin[-1]\n    end\n\n    def file\n      @locator.file\n    end\n\n    # Returns array with transposed (local) offset and (local) length. The transposed values\n    # take the margin into account such that it is added to the content to the right\n    #\n    # Using X to denote margin and where end of line is explicitly shown as \\n:\n    # ```\n    # XXXXabc\\n\n    # XXXXdef\\n\n    # ```\n    # A local offset of 0 is translated to the start of the first heredoc line, and a length of 1 is adjusted to\n    # 5 - i.e to cover \"XXXXa\". A local offset of 1, with length 1 would cover \"b\".\n    # A local offset of 4 and length 1 would cover \"XXXXd\"\n    #\n    # It is possible that lines have different margin and that is taken into account.\n    #\n    def to_global(offset, length)\n      # simple case, no margin\n      return [offset + @leading_offset, length] unless @has_margin\n\n      # compute local start and end line\n      start_line = line_for_offset(offset)\n      end_line = line_for_offset(offset + length)\n\n      # complex case when there is a margin\n      transposed_offset = offset == 0 ? @leading_offset : offset + @leading_offset + @accumulated_margin[start_line]\n      transposed_length = length +\n                          @accumulated_margin[end_line] - @accumulated_margin[start_line] +    # the margins between start and end (0 is line 1)\n                          (offset_on_line(offset) == 0 ? margin_per_line[start_line - 1] : 0)  # include start's margin in position 0\n      [transposed_offset, transposed_length]\n    end\n\n    # Do not call this method\n    def char_offset(offset)\n      raise \"Should not be called\"\n    end\n\n    # Do not call this method\n    def char_length(offset, end_offset)\n      raise \"Should not be called\"\n    end\n  end\n\n  class LocatorForChars < AbstractLocator\n    def offset_on_line(offset)\n      line_offset = line_index[line_for_offset(offset) - 1]\n      offset - line_offset\n    end\n\n    def char_offset(char_offset)\n      char_offset\n    end\n\n    def char_length(offset, end_offset)\n      end_offset - offset\n    end\n\n    # Extracts the text from char offset with given byte length\n    # @returns String - the extracted text\n    def extract_text(offset, length)\n      string.slice(offset, length)\n    end\n  end\n\n  # This implementation is for Ruby19 and Ruby20. It uses byteslice to get strings from byte based offsets.\n  # For Ruby20 this is faster than using the Stringscanner.charpos method (byteslice outperforms it, when\n  # strings are frozen).\n  #\n  class Locator19 < AbstractLocator\n    include Types::PuppetObject\n\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    def self._pcore_type\n      @type ||= Types::PObjectType.new('Puppet::AST::Locator', {\n                                         'attributes' => {\n                                           'string' => Types::PStringType::DEFAULT,\n                                           'file' => Types::PStringType::DEFAULT,\n                                           'line_index' => {\n                                             Types::KEY_TYPE => Types::POptionalType.new(Types::PArrayType.new(Types::PIntegerType::DEFAULT)),\n                                             Types::KEY_VALUE => nil\n                                           }\n                                         }\n                                       })\n    end\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n\n    # Returns the offset on line (first offset on a line is 0).\n    # Ruby 19 is multibyte but has no character position methods, must use byteslice\n    def offset_on_line(offset)\n      line_offset = line_index[line_for_offset(offset) - 1]\n      @string.byteslice(line_offset, offset - line_offset).length\n    end\n\n    # Returns the character offset for a given byte offset\n    # Ruby 19 is multibyte but has no character position methods, must use byteslice\n    def char_offset(byte_offset)\n      string.byteslice(0, byte_offset).length\n    end\n\n    # Returns the length measured in number of characters from the given start and end byte offset\n    def char_length(offset, end_offset)\n      string.byteslice(offset, end_offset - offset).length\n    end\n\n    # Extracts the text from byte offset with given byte length\n    # @returns String - the extracted text\n    def extract_text(offset, length)\n      string.byteslice(offset, length)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/parser_support.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/parser/functions'\nrequire_relative '../../../puppet/parser/files'\nrequire_relative '../../../puppet/resource/type_collection'\nrequire_relative '../../../puppet/resource/type'\nrequire 'monitor'\n\nmodule Puppet::Pops\nmodule Parser\n# Supporting logic for the parser.\n# This supporting logic has slightly different responsibilities compared to the original Puppet::Parser::Parser.\n# It is only concerned with parsing.\n#\nclass Parser\n  # Note that the name of the contained class and the file name (currently parser_support.rb)\n  # needs to be different as the class is generated by Racc, and this file (parser_support.rb) is included as a mix in\n  #\n\n  # Simplify access to the Model factory\n  # Note that the parser/parser support does not have direct knowledge about the Model.\n  # All model construction/manipulation is made by the Factory.\n  #\n  Factory = Model::Factory\n\n  attr_accessor :lexer\n  attr_reader :definitions\n\n  # Returns the token text of the given lexer token, or nil, if token is nil\n  def token_text t\n    return t if t.nil?\n\n    if t.is_a?(Factory) && t.model_class <= Model::QualifiedName\n      t['value']\n    elsif t.is_a?(Model::QualifiedName)\n      t.value\n    else\n      # else it is a lexer token\n      t[:value]\n    end\n  end\n\n  # Produces the fully qualified name, with the full (current) namespace for a given name.\n  #\n  # This is needed because class bodies are lazily evaluated and an inner class' container(s) may not\n  # have been evaluated before some external reference is made to the inner class; its must therefore know its complete name\n  # before evaluation-time.\n  #\n  def classname(name)\n    [namespace, name].join('::').sub(/^::/, '')\n  end\n\n  # Raises a Parse error with location information. Information about file is always obtained from the\n  # lexer. Line and position is produced if the given semantic is a Positioned object and have been given an offset.\n  #\n  def error(semantic, message)\n    except = Puppet::ParseError.new(message)\n    if semantic.is_a?(LexerSupport::TokenValue)\n      except.file = semantic[:file];\n      except.line = semantic[:line];\n      except.pos = semantic[:pos];\n    else\n      locator = @lexer.locator\n      except.file = locator.file\n      if semantic.is_a?(Factory)\n        offset = semantic['offset']\n        unless offset.nil?\n          except.line = locator.line_for_offset(offset)\n          except.pos = locator.pos_on_line(offset)\n        end\n      end\n    end\n    raise except\n  end\n\n  # Parses a file expected to contain pp DSL logic.\n  def parse_file(file)\n    unless Puppet::FileSystem.exist?(file)\n      unless file =~ /\\.pp$/\n        file += \".pp\"\n      end\n    end\n    @lexer.file = file\n    _parse\n  end\n\n  def initialize\n    @lexer = Lexer2.new\n    @namestack = []\n    @definitions = []\n  end\n\n  # This is a callback from the generated parser (when an error occurs while parsing)\n  #\n  def on_error(token, value, stack)\n    if token == 0 # denotes end of file\n      value_at = 'end of input'\n    else\n      value_at = \"'#{value[:value]}'\"\n    end\n    error = Issues::SYNTAX_ERROR.format(:where => value_at)\n    error = \"#{error}, token: #{token}\" if @yydebug\n\n    # Note, old parser had processing of \"expected token here\" - do not try to reinstate:\n    # The 'expected' is only of value at end of input, otherwise any parse error involving a\n    # start of a pair will be reported as expecting the close of the pair - e.g. \"$x.each |$x {|\", would\n    # report that \"seeing the '{', the '}' is expected. That would be wrong.\n    # Real \"expected\" tokens are very difficult to compute (would require parsing of racc output data). Output of the stack\n    # could help, but can require extensive backtracking and produce many options.\n    #\n    # The lexer should handle the \"expected instead of end of file for strings, and interpolation\", other expectancies\n    # must be handled by the grammar. The lexer may have enqueued tokens far ahead - the lexer's opinion about this\n    # is not trustworthy.\n    #\n    file = nil\n    line = nil\n    pos  = nil\n    if token != 0\n      file = value[:file]\n      locator = value.locator\n      if locator.is_a?(Puppet::Pops::Parser::Locator::SubLocator)\n        # The error occurs when doing sub-parsing and the token must be transformed\n        # Transpose the local offset, length to global \"coordinates\"\n        global_offset, _ = locator.to_global(value.offset, value.length)\n        line = locator.locator.line_for_offset(global_offset)\n        pos = locator.locator.pos_on_line(global_offset)\n      else\n        line = value[:line]\n        pos  = value[:pos]\n      end\n    else\n      # At end of input, use what the lexer thinks is the source file\n      file = lexer.file\n    end\n    file = nil unless file.is_a?(String) && !file.empty?\n    raise Puppet::ParseErrorWithIssue.new(error, file, line, pos, nil, Issues::SYNTAX_ERROR.issue_code)\n  end\n\n  # Parses a String of pp DSL code.\n  #\n  def parse_string(code, path = nil)\n    @lexer.lex_string(code, path)\n    _parse()\n  end\n\n  # Mark the factory wrapped model object with location information\n  # @return [Factory] the given factory\n  # @api private\n  #\n  def loc(factory, start_locatable, end_locatable = nil)\n    factory.record_position(@lexer.locator, start_locatable, end_locatable)\n  end\n\n  # Mark the factory wrapped heredoc model object with location information\n  # @return [Factory] the given factory\n  # @api private\n  #\n  def heredoc_loc(factory, start_locatable, end_locatable = nil)\n    factory.record_heredoc_position(start_locatable, end_locatable)\n  end\n\n  def aryfy(o)\n    o = [o] unless o.is_a?(Array)\n    o\n  end\n\n  def namespace\n    @namestack.join('::')\n  end\n\n  def namestack(name)\n    @namestack << name\n  end\n\n  def namepop\n    @namestack.pop\n  end\n\n  def add_definition(definition)\n    @definitions << definition.model\n    definition\n  end\n\n  # Transforms an array of expressions containing literal name expressions to calls if followed by an\n  # expression, or expression list\n  #\n  def transform_calls(expressions)\n    # Factory transform raises an error if a non qualified name is followed by an argument list\n    # since there is no way that that can be transformed back to sanity. This occurs in situations like this:\n    #\n    #  $a = 10, notice hello\n    #\n    # where the \"10, notice\" forms an argument list. The parser builds an Array with the expressions and includes\n    # the comma tokens to enable the error to be reported against the first comma.\n    #\n\n    Factory.transform_calls(expressions)\n  rescue Factory::ArgsToNonCallError => e\n    # e.args[1] is the first comma token in the list\n    # e.name_expr is the function name expression\n    if e.name_expr.is_a?(Factory) && e.name_expr.model_class <= Model::QualifiedName\n      error(e.args[1], _(\"attempt to pass argument list to the function '%{name}' which cannot be called without parentheses\") % { name: e.name_expr['value'] })\n    else\n      error(e.args[1], _(\"illegal comma separated argument list\"))\n    end\n  end\n\n  # Transforms a LEFT followed by the result of attribute_operations, this may be a call or an invalid sequence\n  def transform_resource_wo_title(left, resource, lbrace_token, rbrace_token)\n    Factory.transform_resource_wo_title(left, resource, lbrace_token, rbrace_token)\n  end\n\n  # Creates a program with the given body.\n  #\n  def create_program(body)\n    locator = @lexer.locator\n    Factory.PROGRAM(body, definitions, locator)\n  end\n\n  # Creates an empty program with a single No-op at the input's EOF offset with 0 length.\n  #\n  def create_empty_program\n    locator = @lexer.locator\n    no_op = Factory.literal(nil)\n    # Create a synthetic NOOP token at EOF offset with 0 size. The lexer does not produce an EOF token that is\n    # visible to the grammar rules. Creating this token is mainly to reuse the positioning logic as it\n    # expects a token decorated with location information.\n    _, token = @lexer.emit_completed([:NOOP, '', 0], locator.string.bytesize)\n    loc(no_op, token)\n    # Program with a Noop\n    Factory.PROGRAM(no_op, [], locator)\n  end\n\n  # Performs the parsing and returns the resulting model.\n  # The lexer holds state, and this is setup with {#parse_string}, or {#parse_file}.\n  #\n  # @api private\n  #\n  def _parse\n    begin\n      @yydebug = false\n      main = yyparse(@lexer, :scan)\n    end\n    main\n  ensure\n    @lexer.clear\n    @namestack = []\n    @definitions = []\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/pn_parser.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Parser\nclass PNParser\n  LIT_TRUE = 'true'\n  LIT_FALSE = 'false'\n  LIT_NIL = 'nil'\n\n  TOKEN_END = 0\n  TOKEN_BOOL = 1\n  TOKEN_NIL = 2\n  TOKEN_INT = 3\n  TOKEN_FLOAT = 4\n  TOKEN_IDENTIFIER = 5\n  TOKEN_WS = 0x20\n  TOKEN_STRING = 0x22\n  TOKEN_KEY = 0x3a\n  TOKEN_LP = 0x28\n  TOKEN_RP = 0x29\n  TOKEN_LB = 0x5b\n  TOKEN_RB = 0x5d\n  TOKEN_LC = 0x7b\n  TOKEN_RC = 0x7d\n\n  TYPE_END = 0\n  TYPE_WS = 1\n  TYPE_DELIM = 2\n  TYPE_KEY_START = 3\n  TYPE_STRING_START = 4\n  TYPE_IDENTIFIER = 5\n  TYPE_MINUS = 6\n  TYPE_DIGIT = 7\n  TYPE_ALPHA = 8\n\n  def initialize\n    @char_types = self.class.char_types\n  end\n\n  def parse(text, locator = nil, offset = nil)\n    @locator = locator\n    @offset = offset\n    @text = text\n    @codepoints = text.codepoints.to_a.freeze\n    @pos = 0\n    @token = TOKEN_END\n    @token_value = nil\n    next_token\n    parse_next\n  end\n\n  def self.char_types\n    unless instance_variable_defined?(:@char_types)\n      @char_types = Array.new(0x80, TYPE_IDENTIFIER)\n      @char_types[0] = TYPE_END\n      [0x09, 0x0d, 0x0a, 0x20].each { |n| @char_types[n] = TYPE_WS }\n      [TOKEN_LP, TOKEN_RP, TOKEN_LB, TOKEN_RB, TOKEN_LC, TOKEN_RC].each { |n| @char_types[n] = TYPE_DELIM }\n      @char_types[0x2d] = TYPE_MINUS\n      (0x30..0x39).each { |n| @char_types[n] = TYPE_DIGIT }\n      (0x41..0x5a).each { |n| @char_types[n] = TYPE_ALPHA }\n      (0x61..0x7a).each { |n| @char_types[n] = TYPE_ALPHA }\n      @char_types[TOKEN_KEY] = TYPE_KEY_START\n      @char_types[TOKEN_STRING] = TYPE_STRING_START\n      @char_types.freeze\n    end\n    @char_types\n  end\n\n  private\n\n  def parse_next\n    case @token\n    when TOKEN_LB\n      parse_array\n    when TOKEN_LC\n      parse_map\n    when TOKEN_LP\n      parse_call\n    when TOKEN_BOOL, TOKEN_INT, TOKEN_FLOAT, TOKEN_STRING, TOKEN_NIL\n      parse_literal\n    when TOKEN_END\n      parse_error(_('unexpected end of input'))\n    else\n      parse_error(_('unexpected %{value}' % { value: @token_value }))\n    end\n  end\n\n  def parse_error(message)\n    file = ''\n    line = 1\n    pos = 1\n    if @locator\n      file = @locator.file\n      line = @locator.line_for_offset(@offset)\n      pos  = @locator.pos_on_line(@offset)\n    end\n    @codepoints[0, @pos].each do |c|\n      if c == 0x09\n        line += 1\n        pos = 1\n      end\n    end\n    raise Puppet::ParseError.new(message, file, line, pos)\n  end\n\n  def parse_literal\n    pn = PN::Literal.new(@token_value)\n    next_token\n    pn\n  end\n\n  def parse_array\n    next_token\n    PN::List.new(parse_elements(TOKEN_RB))\n  end\n\n  def parse_map\n    next_token\n    entries = []\n    while @token != TOKEN_RC && @token != TOKEN_END\n      parse_error(_('map key expected')) unless @token == TOKEN_KEY\n      key = @token_value\n      next_token\n      entries << parse_next.with_name(key)\n    end\n    next_token\n    PN::Map.new(entries)\n  end\n\n  def parse_call\n    next_token\n    parse_error(_(\"expected identifier to follow '('\")) unless @token == TOKEN_IDENTIFIER\n    name = @token_value\n    next_token\n    PN::Call.new(name, *parse_elements(TOKEN_RP))\n  end\n\n  def parse_elements(end_token)\n    elements = []\n    while @token != end_token && @token != TOKEN_END\n      elements << parse_next\n    end\n    parse_error(_(\"missing '%{token}' to end list\") % { token: end_token.chr(Encoding::UTF_8) }) unless @token == end_token\n    next_token\n    elements\n  end\n\n  # All methods below belong to the PN lexer\n\n  def next_token\n    skip_white\n    s = @pos\n    c = next_cp\n\n    case @char_types[c]\n    when TYPE_END\n      @token_value = nil\n      @token = TOKEN_END\n\n    when TYPE_MINUS\n      if @char_types[peek_cp] == TYPE_DIGIT\n        next_token # consume float or integer\n        @token_value = -@token_value\n      else\n        consume_identifier(s)\n      end\n\n    when TYPE_DIGIT\n      skip_decimal_digits\n      c = peek_cp\n      if c == 0x2e # '.'\n        @pos += 1\n        consume_float(s, c)\n      else\n        @token_value = @text[s..@pos].to_i\n        @token = TOKEN_INT\n      end\n\n    when TYPE_DELIM\n      @token_value = @text[s]\n      @token = c\n\n    when TYPE_KEY_START\n      if @char_types[peek_cp] == TYPE_ALPHA\n        next_token\n        @token = TOKEN_KEY\n      else\n        parse_error(_(\"expected identifier after ':'\"))\n      end\n\n    when TYPE_STRING_START\n      consume_string\n    else\n      consume_identifier(s)\n    end\n  end\n\n  def consume_identifier(s)\n    while @char_types[peek_cp] >= TYPE_IDENTIFIER\n      @pos += 1\n    end\n    id = @text[s...@pos]\n    case id\n    when LIT_TRUE\n      @token = TOKEN_BOOL\n      @token_value = true\n    when LIT_FALSE\n      @token = TOKEN_BOOL\n      @token_value = false\n    when LIT_NIL\n      @token = TOKEN_NIL\n      @token_value = nil\n    else\n      @token = TOKEN_IDENTIFIER\n      @token_value = id\n    end\n  end\n\n  def consume_string\n    s = @pos\n    b = ''.dup\n    loop do\n      c = next_cp\n      case c\n      when TOKEN_END\n        @pos = s - 1\n        parse_error(_('unterminated quote'))\n      when TOKEN_STRING\n        @token_value = b\n        @token = TOKEN_STRING\n        break\n      when 0x5c # '\\'\n        c = next_cp\n        case c\n        when 0x74 # 't'\n          b << \"\\t\"\n        when 0x72 # 'r'\n          b << \"\\r\"\n        when 0x6e # 'n'\n          b << \"\\n\"\n        when TOKEN_STRING\n          b << '\"'\n        when 0x5c # '\\'\n          b << \"\\\\\"\n        when 0x6f # 'o'\n          c = 0\n          3.times do\n            n = next_cp\n            if 0x30 <= n && n <= 0x37c\n              c *= 8\n              c += n - 0x30\n            else\n              parse_error(_('malformed octal quote'))\n            end\n          end\n          b << c\n        else\n          b << \"\\\\\"\n          b << c\n        end\n      else\n        b << c\n      end\n    end\n  end\n\n  def consume_float(s, d)\n    parse_error(_('digit expected')) if skip_decimal_digits == 0\n    c = peek_cp\n    if d == 0x2e # '.'\n      if c == 0x45 || c == 0x65 # 'E' or 'e'\n        @pos += 1\n        parse_error(_('digit expected')) if skip_decimal_digits == 0\n        c = peek_cp\n      end\n    end\n    parse_error(_('digit expected')) if @char_types[c] == TYPE_ALPHA\n    @token_value = @text[s...@pos].to_f\n    @token = TOKEN_FLOAT\n  end\n\n  def skip_decimal_digits\n    count = 0\n    c = peek_cp\n    if c == 0x2d || c == 0x2b # '-' or '+'\n      @pos += 1\n      c = peek_cp\n    end\n\n    while @char_types[c] == TYPE_DIGIT\n      @pos += 1\n      c = peek_cp\n      count += 1\n    end\n    count\n  end\n\n  def skip_white\n    while @char_types[peek_cp] == TYPE_WS\n      @pos += 1\n    end\n  end\n\n  def next_cp\n    c = 0\n    if @pos < @codepoints.size\n      c = @codepoints[@pos]\n      @pos += 1\n    end\n    c\n  end\n\n  def peek_cp\n    @pos < @codepoints.size ? @codepoints[@pos] : 0\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/parser/slurp_support.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Parser\n# This module is an integral part of the Lexer.\n# It defines the string slurping behavior - finding the string and non string parts in interpolated\n# strings, translating escape sequences in strings to their single character equivalence.\n#\n# PERFORMANCE NOTE: The various kinds of slurping could be made even more generic, but requires\n# additional parameter passing and evaluation of conditional logic.\n# TODO: More detailed performance analysis of excessive character escaping and interpolation.\n#\nmodule SlurpSupport\n  include LexerSupport\n\n  SLURP_SQ_PATTERN  = /(?:[^\\\\]|^)(?:\\\\{2})*'/\n  SLURP_DQ_PATTERN  = /(?:[^\\\\]|^)(?:\\\\{2})*(\"|[$]\\{?)/\n  SLURP_UQ_PATTERN  = /(?:[^\\\\]|^)(?:\\\\{2})*([$]\\{?|\\z)/\n  # unquoted, no escapes\n  SLURP_UQNE_PATTERN = /(\\$\\{?|\\z)/m\n  SLURP_ALL_PATTERN = /.*(\\z)/m\n  SQ_ESCAPES = %w[\\\\ ']\n  DQ_ESCAPES = %w[\\\\  $ ' \" r n t s u] + [\"\\r\\n\", \"\\n\"]\n  UQ_ESCAPES = %w[\\\\  $ r n t s u] + [\"\\r\\n\", \"\\n\"]\n\n  def slurp_sqstring\n    # skip the leading '\n    @scanner.pos += 1\n    str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes)\n    lex_error(Issues::UNCLOSED_QUOTE, :after => \"\\\"'\\\"\", :followed_by => followed_by) unless str\n    str[0..-2] # strip closing \"'\" from result\n  end\n\n  def slurp_dqstring\n    scn = @scanner\n    last = scn.matched\n    str = slurp(scn, SLURP_DQ_PATTERN, DQ_ESCAPES, false)\n    unless str\n      lex_error(Issues::UNCLOSED_QUOTE, :after => format_quote(last), :followed_by => followed_by)\n    end\n\n    # Terminator may be a single char '\"', '$', or two characters '${' group match 1 (scn[1]) from the last slurp holds this\n    terminator = scn[1]\n    [str[0..(-1 - terminator.length)], terminator]\n  end\n\n  # Copy from old lexer - can do much better\n  def slurp_uqstring\n    scn = @scanner\n    str = slurp(scn, @lexing_context[:uq_slurp_pattern], @lexing_context[:escapes], :ignore_invalid_escapes)\n\n    # Terminator may be a single char '$', two characters '${', or empty string '' at the end of intput.\n    # Group match 1 holds this.\n    # The exceptional case is found by looking at the subgroup 1 of the most recent match made by the scanner (i.e. @scanner[1]).\n    # This is the last match made by the slurp method (having called scan_until on the scanner).\n    # If there is a terminating character is must be stripped and returned separately.\n    #\n    terminator = scn[1]\n    [str[0..(-1 - terminator.length)], terminator]\n  end\n\n  # Slurps a string from the given scanner until the given pattern and then replaces any escaped\n  # characters given by escapes into their control-character equivalent or in case of line breaks, replaces the\n  # pattern \\r?\\n with an empty string.\n  # The returned string contains the terminating character. Returns nil if the scanner can not scan until the given\n  # pattern.\n  #\n  def slurp(scanner, pattern, escapes, ignore_invalid_escapes)\n    str = scanner.scan_until(pattern) || return\n\n    return str unless str.include?('\\\\')\n\n    return str.gsub!(/\\\\(\\\\|')/m, '\\1') || str if escapes.equal?(SQ_ESCAPES)\n\n    # Process unicode escapes first as they require getting 4 hex digits\n    # If later a \\u is found it is warned not to be a unicode escape\n    if escapes.include?('u')\n      # gsub must be repeated to cater for adjacent escapes\n      while str.gsub!(/((?:[^\\\\]|^)(?:\\\\{2})*)\\\\u(?:([\\da-fA-F]{4})|\\{([\\da-fA-F]{1,6})\\})/m) { ::Regexp.last_match(1) + [(::Regexp.last_match(2) || ::Regexp.last_match(3)).hex].pack(\"U\") }\n        # empty block. Everything happens in the gsub block\n      end\n    end\n\n    begin\n      str.gsub!(/\\\\([^\\r\\n]|(?:\\r?\\n))/m) {\n        ch = ::Regexp.last_match(1)\n        if escapes.include? ch\n          case ch\n          when 'r'; \"\\r\"\n          when 'n'; \"\\n\"\n          when 't'; \"\\t\"\n          when 's'; ' '\n          when 'u'\n            lex_warning(Issues::ILLEGAL_UNICODE_ESCAPE)\n            \"\\\\u\"\n          when \"\\n\"; ''\n          when \"\\r\\n\"; ''\n          else ch\n          end\n        else\n          lex_warning(Issues::UNRECOGNIZED_ESCAPE, :ch => ch) unless ignore_invalid_escapes\n          \"\\\\#{ch}\"\n        end\n      }\n    rescue ArgumentError => e\n      # A invalid byte sequence may be the result of faulty input as well, but that could not possibly\n      # have reached this far... Unfortunately there is no more specific error and a match on message is\n      # required to differentiate from other internal problems.\n      if e.message =~ /invalid byte sequence/\n        lex_error(Issues::ILLEGAL_UNICODE_ESCAPE)\n      else\n        raise e\n      end\n    end\n    str\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/patterns.rb",
    "content": "# frozen_string_literal: true\n\n# The Patterns module contains common regular expression patters for the Puppet DSL language\nmodule Puppet::Pops::Patterns\n  # NUMERIC matches hex, octal, decimal, and floating point and captures several parts\n  # 0 = entire matched number, leading and trailing whitespace and sign included\n  # 1 = sign, +, - or nothing\n  # 2 = entire numeric part\n  # 3 = hexadecimal number\n  # 4 = non hex integer portion, possibly with leading 0 (octal)\n  # 5 = floating point part, starts with \".\", decimals and optional exponent\n  #\n  # Thus, a hex number has group 3 value, an octal value has group 4 (if it starts with 0), and no group 3\n  # and a floating point value has group 4 and group 5.\n  #\n  NUMERIC = /\\A[[:blank:]]*([-+]?)[[:blank:]]*((0[xX][0-9A-Fa-f]+)|(0?\\d+)((?:\\.\\d+)?(?:[eE]-?\\d+)?))[[:blank:]]*\\z/\n\n  # Special expression that tests if there is whitespace between sign and number. The expression is used\n  # to strip such whitespace when normal Float or Integer conversion fails.\n  WS_BETWEEN_SIGN_AND_NUMBER = /\\A([+-])[[:blank:]]+(.*)\\z/\n\n  # ILLEGAL_P3_1_HOSTNAME matches if a hostname contains illegal characters.\n  # This check does not prevent pathological names like 'a....b', '.....', \"---\". etc.\n  ILLEGAL_HOSTNAME_CHARS = /[^-\\w.]/\n\n  # NAME matches a name the same way as the lexer.\n  NAME = /\\A((::)?[a-z]\\w*)(::[a-z]\\w*)*\\z/\n\n  # CLASSREF_EXT matches a class reference the same way as the lexer - i.e. the external source form\n  # where each part must start with a capital letter A-Z.\n  #\n  CLASSREF_EXT = /\\A((::){0,1}[A-Z]\\w*)+\\z/\n\n  # Same as CLASSREF_EXT but cannot start with '::'\n  #\n  CLASSREF_EXT_DECL = /\\A[A-Z]\\w*(?:::[A-Z]\\w*)*\\z/\n\n  # CLASSREF matches a class reference the way it is represented internally in the\n  # model (i.e. in lower case).\n  #\n  CLASSREF = /\\A((::){0,1}[a-z]\\w*)+\\z/\n\n  # Same as CLASSREF but cannot start with '::'\n  #\n  CLASSREF_DECL = /\\A[a-z]\\w*(?:::[a-z]\\w*)*\\z/\n\n  # DOLLAR_VAR matches a variable name including the initial $ character\n  DOLLAR_VAR = /\\$(::)?(\\w+::)*\\w+/\n\n  # VAR_NAME matches the name part of a variable (The $ character is not included)\n  # Note, that only the final segment may start with an underscore.\n  # Note, regexp sensitive to backtracking\n  VAR_NAME = /\\A(?:::)?(?:[a-z]\\w*::)*[a-z_]\\w*\\z/\n\n  # PARAM_NAME matches the name part of a parameter (The $ character is not included)\n  PARAM_NAME = /\\A[a-z_]\\w*\\z/\n\n  # A Numeric var name must be the decimal number 0, or a decimal number not starting with 0\n  NUMERIC_VAR_NAME = /\\A(?:0|(?:[1-9][0-9]*))\\z/\nend\n"
  },
  {
    "path": "lib/puppet/pops/pcore.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'uri'\n\nmodule Puppet::Pops\nmodule Pcore\n  include Types::PuppetObject\n\n  TYPE_URI_RX = Types::TypeFactory.regexp(URI::DEFAULT_PARSER.make_regexp)\n  TYPE_URI = Types::TypeFactory.pattern(TYPE_URI_RX)\n  TYPE_URI_ALIAS = Types::PTypeAliasType.new('Pcore::URI', nil, TYPE_URI)\n  TYPE_SIMPLE_TYPE_NAME = Types::TypeFactory.pattern(/\\A[A-Z]\\w*\\z/)\n  TYPE_QUALIFIED_REFERENCE = Types::TypeFactory.pattern(/\\A[A-Z]\\w*(?:::[A-Z]\\w*)*\\z/)\n  TYPE_MEMBER_NAME = Types::PPatternType.new([Types::PRegexpType.new(Patterns::PARAM_NAME)])\n\n  KEY_PCORE_URI = 'pcore_uri'\n  KEY_PCORE_VERSION = 'pcore_version'\n\n  PCORE_URI = 'http://puppet.com/2016.1/pcore'\n  PCORE_VERSION = SemanticPuppet::Version.new(1, 0, 0)\n  PARSABLE_PCORE_VERSIONS = SemanticPuppet::VersionRange.parse('1.x')\n\n  RUNTIME_NAME_AUTHORITY = 'http://puppet.com/2016.1/runtime'\n\n  def self._pcore_type\n    @type\n  end\n\n  def self.annotate(instance, annotations_hash)\n    annotations_hash.each_pair do |type, init_hash|\n      type.implementation_class.annotate(instance) { init_hash }\n    end\n    instance\n  end\n\n  def self.init_env(loader)\n    if Puppet[:tasks]\n      add_object_type('Task', <<-PUPPET, loader)\n        {\n          attributes => {\n            # Fully qualified name of the task\n            name => { type => Pattern[/\\\\A[a-z][a-z0-9_]*(?:::[a-z][a-z0-9_]*)*\\\\z/] },\n\n            # List of file references referenced by metadata and their paths on disk.\n            # If there are no implementations listed in metadata, the first file is always\n            # the task executable.\n            files => { type => Array[Struct[name => String, path => String]] },\n\n            # Task metadata\n            metadata => { type => Hash[String, Any] },\n\n            # Map parameter names to their parsed data type\n            parameters => { type => Optional[Hash[Pattern[/\\\\A[a-z][a-z0-9_]*\\\\z/], Type]], value => undef },\n          }\n        }\n      PUPPET\n    end\n  end\n\n  def self.init(loader, ir)\n    add_alias('Pcore::URI_RX', TYPE_URI_RX, loader)\n    add_type(TYPE_URI_ALIAS, loader)\n    add_alias('Pcore::SimpleTypeName', TYPE_SIMPLE_TYPE_NAME, loader)\n    add_alias('Pcore::MemberName', TYPE_MEMBER_NAME, loader)\n    add_alias('Pcore::TypeName', TYPE_QUALIFIED_REFERENCE, loader)\n    add_alias('Pcore::QRef', TYPE_QUALIFIED_REFERENCE, loader)\n    Types::TypedModelObject.register_ptypes(loader, ir)\n\n    @type = create_object_type(loader, ir, Pcore, 'Pcore', nil)\n\n    ir.register_implementation_namespace('Pcore', 'Puppet::Pops::Pcore')\n    ir.register_implementation_namespace('Puppet::AST', 'Puppet::Pops::Model')\n    ir.register_implementation('Puppet::AST::Locator', 'Puppet::Pops::Parser::Locator::Locator19')\n    Resource.register_ptypes(loader, ir)\n    Lookup::Context.register_ptype(loader, ir);\n    Lookup::DataProvider.register_types(loader)\n\n    add_object_type('Deferred', <<-PUPPET, loader)\n      {\n        attributes => {\n          # Fully qualified name of the function\n          name  => { type => Pattern[/\\\\A[$]?[a-z][a-z0-9_]*(?:::[a-z][a-z0-9_]*)*\\\\z/] },\n          arguments => { type => Optional[Array[Any]], value => undef},\n        }\n      }\n    PUPPET\n  end\n\n  # Create and register a new `Object` type in the Puppet Type System and map it to an implementation class\n  #\n  # @param loader [Loader::Loader] The loader where the new type will be registered\n  # @param ir [ImplementationRegistry] The implementation registry that maps this class to the new type\n  # @param impl_class [Class] The class that is the implementation of the type\n  # @param type_name [String] The fully qualified name of the new type\n  # @param parent_name [String,nil] The fully qualified name of the parent type\n  # @param attributes_hash [Hash{String => Object}] A hash of attribute definitions for the new type\n  # @param functions_hash [Hash{String => Object}] A hash of function definitions for the new type\n  # @param equality [Array<String>] An array with names of attributes that participate in equality comparison\n  # @return [PObjectType] the created type. Not yet resolved\n  #\n  # @api private\n  def self.create_object_type(loader, ir, impl_class, type_name, parent_name, attributes_hash = EMPTY_HASH, functions_hash = EMPTY_HASH, equality = nil)\n    init_hash = {}\n    init_hash[Types::KEY_PARENT] = Types::PTypeReferenceType.new(parent_name) unless parent_name.nil?\n    init_hash[Types::KEY_ATTRIBUTES] = attributes_hash unless attributes_hash.empty?\n    init_hash[Types::KEY_FUNCTIONS] = functions_hash unless functions_hash.empty?\n    init_hash[Types::KEY_EQUALITY] = equality unless equality.nil?\n    ir.register_implementation(type_name, impl_class)\n    add_type(Types::PObjectType.new(type_name, init_hash), loader)\n  end\n\n  def self.add_object_type(name, body, loader)\n    add_type(Types::PObjectType.new(name, Parser::EvaluatingParser.new.parse_string(body).body), loader)\n  end\n\n  def self.add_alias(name, type, loader, name_authority = RUNTIME_NAME_AUTHORITY)\n    add_type(Types::PTypeAliasType.new(name, nil, type), loader, name_authority)\n  end\n\n  def self.add_type(type, loader, name_authority = RUNTIME_NAME_AUTHORITY)\n    loader.set_entry(Loader::TypedName.new(:type, type.name, name_authority), type)\n    type\n  end\n\n  def self.register_implementations(impls, name_authority = RUNTIME_NAME_AUTHORITY)\n    Loaders.loaders.register_implementations(impls, name_authority)\n  end\n\n  def self.register_aliases(aliases, name_authority = RUNTIME_NAME_AUTHORITY, loader = Loaders.loaders.private_environment_loader)\n    aliases.each do |name, type_string|\n      add_type(Types::PTypeAliasType.new(name, Types::TypeFactory.type_reference(type_string), nil), loader, name_authority)\n    end\n    aliases.each_key.map { |name| loader.load(:type, name).resolve(loader) }\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/pn.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule PN\n  KEY_PATTERN = /^[A-Za-z_-][0-9A-Za-z_-]*$/\n\n  def pnError(message)\n    raise ArgumentError, message\n  end\n\n  def as_call(name)\n    Call.new(name, self)\n  end\n\n  def as_parameters\n    [self]\n  end\n\n  def ==(o)\n    eql?(o)\n  end\n\n  def to_s\n    s = ''.dup\n    format(nil, s)\n    s\n  end\n\n  def with_name(name)\n    Entry.new(name, self)\n  end\n\n  def double_quote(str, bld)\n    bld << '\"'\n    str.each_codepoint do |codepoint|\n      case codepoint\n      when 0x09\n        bld << '\\\\t'\n      when 0x0a\n        bld << '\\\\n'\n      when 0x0d\n        bld << '\\\\r'\n      when 0x22\n        bld << '\\\\\"'\n      when 0x5c\n        bld << '\\\\\\\\'\n      else\n        if codepoint < 0x20\n          bld << sprintf('\\\\o%3.3o', codepoint)\n        elsif codepoint <= 0x7f\n          bld << codepoint\n        else\n          bld << [codepoint].pack('U')\n        end\n      end\n    end\n    bld << '\"'\n  end\n\n  def format_elements(elements, indent, b)\n    elements.each_with_index do |e, i|\n      if indent\n        b << \"\\n\" << indent.current\n      elsif i > 0\n        b << ' '\n      end\n      e.format(indent, b)\n    end\n  end\n\n  class Indent\n    attr_reader :current\n\n    def initialize(indent = '  ', current = '')\n      @indent = indent\n      @current = current\n    end\n\n    def increase\n      Indent.new(@indent, @current + @indent)\n    end\n  end\n\n  class Call\n    include PN\n    attr_reader :name, :elements\n\n    def initialize(name, *elements)\n      @name = name\n      @elements = elements\n    end\n\n    def [](idx)\n      @elements[idx]\n    end\n\n    def as_call(name)\n      Call.new(name, *@elements)\n    end\n\n    def as_parameters\n      List.new(@elements)\n    end\n\n    def eql?(o)\n      o.is_a?(Call) && @name == o.name && @elements == o.elements\n    end\n\n    def format(indent, b)\n      b << '(' << @name\n      if @elements.size > 0\n        b << ' ' unless indent\n        format_elements(@elements, indent ? indent.increase : nil, b)\n      end\n      b << ')'\n    end\n\n    def to_data\n      { '^' => [@name] + @elements.map(&:to_data) }\n    end\n  end\n\n  class Entry\n    attr_reader :key, :value\n\n    def initialize(key, value)\n      @key = key\n      @value = value\n    end\n\n    def eql?(o)\n      o.is_a?(Entry) && @key == o.key && @value == o.value\n    end\n\n    alias == eql?\n  end\n\n  class List\n    include PN\n    attr_reader :elements\n\n    def initialize(elements)\n      @elements = elements\n    end\n\n    def [](idx)\n      @elements[idx]\n    end\n\n    def as_call(name)\n      Call.new(name, *@elements)\n    end\n\n    def as_parameters\n      @elements\n    end\n\n    def eql?(o)\n      o.is_a?(List) && @elements == o.elements\n    end\n\n    def format(indent, b)\n      b << '['\n      format_elements(@elements, indent ? indent.increase : nil, b) unless @elements.empty?\n      b << ']'\n    end\n\n    def to_data\n      @elements.map(&:to_data)\n    end\n  end\n\n  class Literal\n    include PN\n    attr_reader :value\n\n    def initialize(value)\n      @value = value\n    end\n\n    def format(indent, b)\n      if @value.nil?\n        b << 'nil'\n      elsif value.is_a?(String)\n        double_quote(value, b)\n      else\n        b << value.to_s\n      end\n    end\n\n    def eql?(o)\n      o.is_a?(Literal) && @value == o.value\n    end\n\n    def to_data\n      @value\n    end\n  end\n\n  class Map\n    include PN\n    attr_reader :entries\n\n    def initialize(entries)\n      entries.each { |e| pnError(\"key #{e.key} does not conform to pattern /#{KEY_PATTERN.source}/)\") unless e.key =~ KEY_PATTERN }\n      @entries = entries\n    end\n\n    def eql?(o)\n      o.is_a?(Map) && @entries == o.entries\n    end\n\n    def format(indent, b)\n      local_indent = indent ? indent.increase : nil\n      b << '{'\n      @entries.each_with_index do |e, i|\n        if indent\n          b << \"\\n\" << local_indent.current\n        elsif i > 0\n          b << ' '\n        end\n        b << ':' << e.key\n        b << ' '\n        e.value.format(local_indent, b)\n      end\n      b << '}'\n    end\n\n    def to_data\n      r = []\n      @entries.each { |e| r << e.key << e.value.to_data }\n      { '#' => r }\n    end\n  end\nend\nend\n\nrequire_relative 'model/pn_transformer'\nrequire_relative 'parser/pn_parser'\n"
  },
  {
    "path": "lib/puppet/pops/puppet_stack.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/thread_local'\n\nmodule Puppet\n  module Pops\n    # Utility class for keeping track of the \"Puppet stack\", ie the file\n    # and line numbers of Puppet Code that created the current context.\n    #\n    # To use this make a call with:\n    #\n    # ```rb\n    # Puppet::Pops::PuppetStack.stack(file, line, receiver, message, args)\n    # ```\n    #\n    # To get the stack call:\n    #\n    # ```rb\n    # Puppet::Pops::PuppetStack.stacktrace\n    # ```\n    #\n    # or\n    #\n    # ```rb\n    # Puppet::Pops::PuppetStack.top_of_stack\n    # ```\n    #\n    # To support testing, a given file that is an empty string, or nil\n    # as well as a nil line number are supported. Such stack frames\n    # will be represented with the text `unknown` and `0´ respectively.\n    module PuppetStack\n      @stack = Puppet::ThreadLocal.new { Array.new }\n\n      def self.stack(file, line, obj, message, args, &block)\n        file = 'unknown' if file.nil? || file == ''\n        line = 0 if line.nil?\n\n        result = nil\n        @stack.value.unshift([file, line])\n        begin\n          if block_given?\n            result = obj.send(message, *args, &block)\n          else\n            result = obj.send(message, *args)\n          end\n        ensure\n          @stack.value.shift()\n        end\n        result\n      end\n\n      def self.stacktrace\n        @stack.value.dup\n      end\n\n      # Returns an Array with the top of the puppet stack, or an empty\n      # Array if there was no such entry.\n      def self.top_of_stack\n        @stack.value.first || []\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/resource/param.rb",
    "content": "# frozen_string_literal: true\n\n# An implementation of the interface Puppet::Resource\n# that adapts the 3.x compiler and catalog expectations on\n# a resource instance. This instance is backed by a\n# pcore representation of the resource type an instance of this\n# ruby class.\n#\n# This class must inherit from Puppet::Resource because of the\n# class expectations in existing logic.\n#\n# This implementation does not support\n# * setting 'strict' - strictness (must refer to an existing type) is always true\n# * does not support the indirector\n#\n#\nmodule Puppet::Pops\nmodule Resource\nclass Param\n  # This make this class instantiable from Puppet\n  include Puppet::Pops::Types::PuppetObject\n\n  def self.register_ptype(loader, ir)\n    @ptype = Pcore.create_object_type(loader, ir, self, 'Puppet::Resource::Param', nil,\n                                      {\n                                        Types::KEY_TYPE => Types::PTypeType::DEFAULT,\n                                        Types::KEY_NAME => Types::PStringType::NON_EMPTY,\n                                        'name_var' => {\n                                          Types::KEY_TYPE => Types::PBooleanType::DEFAULT,\n                                          Types::KEY_VALUE => false\n                                        }\n                                      },\n                                      EMPTY_HASH,\n                                      [Types::KEY_NAME])\n  end\n\n  attr_reader :name\n  attr_reader :type\n  attr_reader :name_var\n\n  def initialize(type, name, name_var = false)\n    @type = type\n    @name = name\n    @name_var = name_var\n  end\n\n  def to_s\n    name\n  end\n\n  def self._pcore_type\n    @ptype\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/resource/resource_type_impl.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'param'\n\nmodule Puppet::Pops\nmodule Resource\ndef self.register_ptypes(loader, ir)\n  types = [Param, ResourceTypeImpl].map do |c|\n    c.register_ptype(loader, ir)\n  end\n  types.each { |t| t.resolve(loader) }\nend\n\nclass ResourceTypeImpl\n  # Make instances of this class directly creatable from the Puppet Language\n  # as object.\n  #\n  include Puppet::Pops::Types::PuppetObject\n\n  # Instances of ResourceTypeImpl can be used as the type of a Puppet::Parser::Resource/Puppet::Resource when compiling\n  #\n  include Puppet::CompilableResourceType\n\n  # Returns the Puppet Type for this instance.\n  def self._pcore_type\n    @ptype\n  end\n\n  def self.register_ptype(loader, ir)\n    param_ref = Types::PTypeReferenceType.new('Puppet::Resource::Param')\n    @ptype = Pcore.create_object_type(loader, ir, self, 'Puppet::Resource::ResourceType3', nil,\n                                      {\n                                        Types::KEY_NAME => Types::PStringType::NON_EMPTY,\n                                        'properties' => {\n                                          Types::KEY_TYPE => Types::PArrayType.new(param_ref),\n                                          Types::KEY_VALUE => EMPTY_ARRAY\n                                        },\n                                        'parameters' => {\n                                          Types::KEY_TYPE => Types::PArrayType.new(param_ref),\n                                          Types::KEY_VALUE => EMPTY_ARRAY\n                                        },\n                                        'title_patterns_hash' => {\n                                          Types::KEY_TYPE => Types::POptionalType.new(\n                                            Types::PHashType.new(Types::PRegexpType::DEFAULT, Types::PArrayType.new(Types::PStringType::NON_EMPTY))\n                                          ),\n                                          Types::KEY_VALUE => nil\n                                        },\n                                        'isomorphic' => {\n                                          Types::KEY_TYPE => Types::PBooleanType::DEFAULT,\n                                          Types::KEY_VALUE => true\n                                        },\n                                        'capability' => {\n                                          Types::KEY_TYPE => Types::PBooleanType::DEFAULT,\n                                          Types::KEY_VALUE => false\n                                        },\n                                      },\n                                      EMPTY_HASH,\n                                      [Types::KEY_NAME])\n  end\n\n  def eql?(other)\n    other.is_a?(Puppet::CompilableResourceType) && @name == other.name\n  end\n  alias == eql?\n\n  # Compares this type against the given _other_ (type) and returns -1, 0, or +1 depending on the order.\n  # @param other [Object] the object to compare against (produces nil, if not kind of Type}\n  # @return [-1, 0, +1, nil] produces -1 if this type is before the given _other_ type, 0 if equals, and 1 if after.\n  #   Returns nil, if the given _other_ is not a kind of Type.\n  # @see Comparable\n  #\n  def <=>(other)\n    # Order is only maintained against other types, not arbitrary objects.\n    # The natural order is based on the reference name used when comparing\n    return nil unless other.is_a?(Puppet::CompilableResourceType)\n\n    # against other type instances.\n    ref <=> other.ref\n  end\n\n  METAPARAMS = [\n    :noop,\n    :schedule,\n    :audit,\n    :loglevel,\n    :alias,\n    :tag,\n    :require,\n    :subscribe,\n    :before,\n    :notify,\n    :stage\n  ].freeze\n\n  # Speed up lookup\n  METAPARAMSET = Set.new(METAPARAMS).freeze\n\n  attr_reader :name\n  attr_reader :properties\n  attr_reader :parameters\n  attr_reader :title_patterns_hash\n  attr_reader :title_patterns\n\n  def initialize(name, properties = EMPTY_ARRAY, parameters = EMPTY_ARRAY, title_patterns_hash = nil, isomorphic = true, capability = false)\n    @name = name\n    @properties = properties\n    @parameters = parameters\n    @title_patterns_hash = title_patterns_hash\n    @isomorphic = isomorphic\n    # ignore capability parameter\n\n    # Compute attributes hash\n    # Compute key_names (possibly compound key if there are multiple name vars).\n    @attributes = {}\n    @key_attributes = []\n\n    # Name to kind of attribute\n    @attr_types = {}\n\n    # Add all meta params\n    METAPARAMS.each { |p| @attr_types[p] = :meta }\n\n    @property_set = Set.new(properties.map do |p|\n      symname = p.name.to_sym\n      @attributes[symname] = p\n      @key_attributes << symname if p.name_var\n      @attr_types[symname] = :property\n      symname\n    end).freeze\n\n    @param_set = Set.new(parameters.map do |p|\n      symname = p.name.to_sym\n      @attributes[symname] = p\n      @key_attributes << symname if p.name_var\n      @attr_types[symname] = :param\n      symname\n    end).freeze\n\n    # API for title patterns is [ [regexp, [ [ [sym, <lambda>], [sym, <lambda>] ] ] ] ]\n    # Where lambdas are optional. This resource type impl does not support lambdas\n    # Note that the pcore file has a simpler hashmap that is post processed here\n    # since the structure must have Symbol instances for names which the .pp representation\n    # does not deliver.\n    #\n    @title_patterns =\n      case @key_attributes.length\n      when 0\n        # TechDebt: The case of specifying title patterns when having no name vars is unspecified behavior in puppet\n        # Here it is silently ignored.\n        nil\n      when 1\n        if @title_patterns_hash.nil?\n          [[/(.*)/m, [[@key_attributes.first]]]]\n        else\n          # TechDebt: The case of having one namevar and an empty title patterns is unspecified behavior in puppet.\n          # Here, it may lead to an empty map which may or may not trigger the wanted/expected behavior.\n          #\n          @title_patterns_hash.map { |k, v| [k, v.map { |n| [n.to_sym] }] }\n        end\n      else\n        if @title_patterns_hash.nil? || @title_patterns_hash.empty?\n          # TechDebt: While title patterns must be specified when more than one is used, they do not have\n          # to match/set them all since some namevars can be omitted (to support the use case in\n          # the 'package' type where 'provider' attribute is handled as part of the key without being\n          # set from the title.\n          #\n          raise Puppet::DevError, _(\"you must specify title patterns when there are two or more key attributes\")\n        end\n\n        @title_patterns_hash.nil? ? [] : @title_patterns_hash.map { |k, v| [k, v.map { |n| [n.to_sym] }] }\n      end\n  end\n\n  # Override CompilableResource inclusion\n  def is_3x_ruby_plugin?\n    false\n  end\n\n  # Answers if the parameter name is a parameter/attribute of this type\n  # This is part of the Puppet::Type API\n  # Check if used when compiling (it is triggered in an apply)\n  #\n  def valid_parameter?(name)\n    @attributes.include?(name) || METAPARAMSET.include?(name)\n  end\n\n  # The type implementation of finish does a lot of things\n  # * iterates over all parameters and calls post_compile on them if the parameter impl responds to post_compile\n  # * validates the relationship parameters\n  #\n  # This implementation does nothing - it is assumed that the catalog is already validated\n  # via the relationship validator (done late in the game).\n  def finish\n    # Do nothing.\n  end\n\n  # This is called on a resource type\n  # it performs tagging if it is a Class or Node.\n  # It also ensure the parent type is in the catalog, but that is weird since\n  # resource types cannot really inherit\n  def instantiate_resource(scope, resource)\n    # Do nothing because nothing is needed when compiling.\n\n    # This is what the Puppet::Type implementation does\n    # None of this should be needed\n\n    #    # Make sure our parent class has been evaluated, if we have one.\n    #    if parent && !scope.catalog.resource(resource.type, parent)\n    #      parent_type(scope).ensure_in_catalog(scope)\n    #    end\n\n    # This will never happen\n\n    #    if ['Class', 'Node'].include? resource.type\n    #      scope.catalog.tag(*resource.tags)\n    #    end\n  end\n\n  # Being isomorphic in puppet means that the resource is managing a state\n  # (as opposed to a resource like Exec that is a function, possibly with side effect.\n  # In a Ruby implementation of a resource type, @isomorphic = false is used to turn\n  # off isomorphism, it is true by default.\n  # This is part of the Puppet::Type API.\n  #\n  def isomorphic?\n    @isomorphic\n  end\n\n  # Produces the names of the attributes that make out the unique id of a resource\n  #\n  def key_attributes\n    @key_attributes\n  end\n\n  # Gives a type a chance to issue deprecations for parameters.\n  # @param title [String] the title of the resource of this type\n  # @param attributes [Array<Param>] the set parameters in the resource instance\n  def deprecate_params(title, attributes)\n    # TODO: Current API somewhat unclear, if done at type level, or per\n    #       Param.\n  end\n\n  ##################################################\n  # NEVER CALLED COMPILE SIDE FOR A COMPILATION\n  ##################################################\n\n  # Answers :property, :param or :meta depending on the type of the attribute\n  # According to original version, this is called millions of times\n  # and a cache is required.\n  # @param name [Symbol]\n  def attrtype(name)\n    raise NotImplementedError, \"attrtype() - returns the kind (:meta, :param, or :property) of the parameter\"\n    # @attr_types[name]\n  end\n\n  # Returns the implementation of a param/property/attribute - i.e. a Param class\n  def attrclass(name)\n    raise NotImplementedError, \"attrclass() - returns the (param) class of the parameter\"\n  end\n\n  # PROBABLY NOT USED WHEN COMPILING\n  # Returns the names of all attributes in a defined order:\n  # * all key attributes (the composite id)\n  # * :provider if it is specified\n  # * all properties\n  # * all parameters\n  # * meta parameters\n  #\n  def allattrs\n    raise NotImplementedError, \"allattrs() - return all attribute names in order - probably not used master side\"\n    # key_attributes | (parameters & [:provider]) | properties.collect { |property| property.name } | parameters | metaparams\n  end\n\n  # Sets \"applies to host\"\n  def apply_to\n    raise NotImplementedError, \"apply_to() - probably used when selecting a provider (device/host support)\"\n  end\n\n  def apply_to_host\n    raise NotImplementedError, \"apply_to_host() - probably used when selecting a provider (device/host support)\"\n  end\n\n  def apply_to_device\n    raise NotImplementedError, \"apply_to_device() - probably used when selecting a provider (device/host support)\"\n  end\n\n  def apply_to_all\n    raise NotImplementedError, \"apply_to_all() - probably used when selecting a provider (device/host support)\"\n  end\n\n  def can_apply_to_target(target)\n    raise NotImplementedError, \"can_apply_to_target() - probably used when selecting a provider (device/host support)\"\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/resource/resource_type_set.pcore",
    "content": "type Puppet::Resource::Param = Object[{\n  attributes => {\n    type => Type,\n    name => String[1],\n    name_var => { type => Boolean, value => false }\n  },\n  equality => [name],\n}]\n\n\ntype Puppet::Resource::ResourceType3 = Object[{\n  attributes => {\n    name           => String[1],\n    properties     => { type => Array[Puppet::Resource::Param], value = []},\n    parameters     => { type => Array[Puppet::Resource::Param], value = []},\n    title_patterns => { type => Optional[Hash[Regexp, Array[String[1]]], value => nil },\n    isomorphic     => { type => Boolean, value => true },\n    capability     => { type => Boolean, value => false },\n  },\n  equality => [name],\n}]\n\n"
  },
  {
    "path": "lib/puppet/pops/semantic_error.rb",
    "content": "# frozen_string_literal: true\n\n# Error that is used to raise an Issue. See {Puppet::Pops::Issues}.\n#\nclass Puppet::Pops::SemanticError < RuntimeError\n  attr_accessor :issue\n  attr_accessor :semantic\n  attr_accessor :options\n\n  # @param issue [Puppet::Pops::Issues::Issue] the issue describing the severity and message\n  # @param semantic [Puppet::Pops::Model::Locatable, nil] the expression causing the failure, or nil if unknown\n  # @param options [Hash] an options hash with Symbol to value mapping - these are the arguments to the issue\n  #\n  def initialize(issue, semantic = nil, options = {})\n    @issue = issue\n    @semantic = semantic\n    @options = options\n  end\n\n  def file\n    @options[:file]\n  end\n\n  def line\n    @options[:line]\n  end\n\n  def pos\n    @options[:pos]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/abstract_reader.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'extension'\nrequire_relative 'time_factory'\n\nmodule Puppet::Pops\nmodule Serialization\n# Abstract class for protocol specific readers such as MsgPack or JSON\n# The abstract reader is capable of reading the primitive scalars:\n# - Boolean\n# - Integer\n# - Float\n# - String\n# and, by using extensions, also\n# - Array start\n# - Map start\n# - Object start\n# - Regexp\n# - Version\n# - VersionRange\n# - Timespan\n# - Timestamp\n# - Default\n#\n# @api public\nclass AbstractReader\n  # @param [MessagePack::Unpacker,JSON::Unpacker] unpacker The low lever unpacker that delivers the primitive objects\n  # @param [MessagePack::Unpacker,JSON::Unpacker] extension_unpacker Optional unpacker for extensions. Defaults to the unpacker\n  # @api public\n  def initialize(unpacker, extension_unpacker = nil)\n    @read = []\n    @unpacker = unpacker\n    @extension_unpacker = extension_unpacker.nil? ? unpacker : extension_unpacker\n    register_types\n  end\n\n  # Read an object from the underlying unpacker\n  # @return [Object] the object that was read\n  # @api public\n  def read\n    obj = @unpacker.read\n    case obj\n    when Extension::InnerTabulation\n      @read[obj.index]\n    when Numeric, Symbol, Extension::NotTabulated, true, false, nil\n      # not tabulated\n      obj\n    else\n      @read << obj\n      obj\n    end\n  end\n\n  # @return [Integer] The total count of unique primitive values that has been read\n  # @api private\n  def primitive_count\n    @read.size\n  end\n\n  # @api private\n  def read_payload(data)\n    raise SerializationError, \"Internal error: Class #{self.class} does not implement method 'read_payload'\"\n  end\n\n  # @api private\n  def read_tpl_qname(ep)\n    Array.new(ep.read) { read_tpl(ep) }.join('::')\n  end\n\n  # @api private\n  def read_tpl(ep)\n    obj = ep.read\n    case obj\n    when Integer\n      @read[obj]\n    else\n      @read << obj\n      obj\n    end\n  end\n\n  # @api private\n  def extension_unpacker\n    @extension_unpacker\n  end\n\n  # @api private\n  def register_type(extension_number, &block)\n    @unpacker.register_type(extension_number, &block)\n  end\n\n  # @api private\n  def register_types\n    register_type(Extension::INNER_TABULATION) do |data|\n      read_payload(data) { |ep| Extension::InnerTabulation.new(ep.read) }\n    end\n\n    register_type(Extension::TABULATION) do |data|\n      read_payload(data) { |ep| Extension::Tabulation.new(ep.read) }\n    end\n\n    register_type(Extension::ARRAY_START) do |data|\n      read_payload(data) { |ep| Extension::ArrayStart.new(ep.read) }\n    end\n\n    register_type(Extension::MAP_START) do |data|\n      read_payload(data) { |ep| Extension::MapStart.new(ep.read) }\n    end\n\n    register_type(Extension::PCORE_OBJECT_START) do |data|\n      read_payload(data) { |ep| type_name = read_tpl_qname(ep); Extension::PcoreObjectStart.new(type_name, ep.read) }\n    end\n\n    register_type(Extension::OBJECT_START) do |data|\n      read_payload(data) { |ep| Extension::ObjectStart.new(ep.read) }\n    end\n\n    register_type(Extension::DEFAULT) do |data|\n      read_payload(data) { |_ep| Extension::Default::INSTANCE }\n    end\n\n    register_type(Extension::COMMENT) do |data|\n      read_payload(data) { |ep| Extension::Comment.new(ep.read) }\n    end\n\n    register_type(Extension::SENSITIVE_START) do |data|\n      read_payload(data) { |_ep| Extension::SensitiveStart::INSTANCE }\n    end\n\n    register_type(Extension::REGEXP) do |data|\n      read_payload(data) { |ep| Regexp.new(ep.read) }\n    end\n\n    register_type(Extension::TYPE_REFERENCE) do |data|\n      read_payload(data) { |ep| Types::PTypeReferenceType.new(ep.read) }\n    end\n\n    register_type(Extension::SYMBOL) do |data|\n      read_payload(data) { |ep| ep.read.to_sym }\n    end\n\n    register_type(Extension::TIME) do |data|\n      read_payload(data) do |ep|\n        sec = ep.read\n        nsec = ep.read\n        Time::Timestamp.new(sec * 1_000_000_000 + nsec)\n      end\n    end\n\n    register_type(Extension::TIMESPAN) do |data|\n      read_payload(data) do |ep|\n        sec = ep.read\n        nsec = ep.read\n        Time::Timespan.new(sec * 1_000_000_000 + nsec)\n      end\n    end\n\n    register_type(Extension::VERSION) do |data|\n      read_payload(data) { |ep| SemanticPuppet::Version.parse(ep.read) }\n    end\n\n    register_type(Extension::VERSION_RANGE) do |data|\n      read_payload(data) { |ep| SemanticPuppet::VersionRange.parse(ep.read) }\n    end\n\n    register_type(Extension::BASE64) do |data|\n      read_payload(data) { |ep| Types::PBinaryType::Binary.from_base64_strict(ep.read) }\n    end\n\n    register_type(Extension::BINARY) do |data|\n      # The Ruby MessagePack implementation have special treatment for \"ASCII-8BIT\" strings. They\n      # are written as binary data.\n      read_payload(data) { |ep| Types::PBinaryType::Binary.new(ep.read) }\n    end\n\n    register_type(Extension::URI) do |data|\n      read_payload(data) { |ep| URI(ep.read) }\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/abstract_writer.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'extension'\n\nmodule Puppet::Pops\nmodule Serialization\nMAX_INTEGER = 0x7fffffffffffffff\nMIN_INTEGER = -0x8000000000000000\n\n# Abstract class for protocol specific writers such as MsgPack or JSON\n# The abstract write is capable of writing the primitive scalars:\n# - Boolean\n# - Integer\n# - Float\n# - String\n# and, by using extensions, also\n# - Array start\n# - Map start\n# - Object start\n# - Regexp\n# - Version\n# - VersionRange\n# - Timespan\n# - Timestamp\n# - Default\n#\n# @api public\nclass AbstractWriter\n  # @param [MessagePack::Packer,JSON::Packer] packer the underlying packer stream\n  # @param [Hash] options\n  # @option options [Boolean] :tabulate `true` if tabulation is enabled (which is the default).\n  # @param [DebugPacker,nil] extension_packer Optional specific extension packer. Only used for debug output\n  # @api public\n  def initialize(packer, options, extension_packer = nil)\n    @tabulate = options[:tabulate]\n    @tabulate = true if @tabulate.nil?\n    @written = {}\n    @packer = packer\n    @extension_packer = extension_packer.nil? ? packer : extension_packer\n    register_types\n  end\n\n  # Tell the underlying packer to flush.\n  # @api public\n  def finish\n    @packer.flush\n  end\n\n  def supports_binary?\n    false\n  end\n\n  # Write a value on the underlying stream\n  # @api public\n  def write(value)\n    written = false\n    case value\n    when Integer\n      # not tabulated, but integers larger than 64-bit cannot be allowed.\n      raise SerializationError, _('Integer out of bounds') if value > MAX_INTEGER || value < MIN_INTEGER\n    when Numeric, Symbol, Extension::NotTabulated, true, false, nil\n      # not tabulated\n    else\n      if @tabulate\n        index = @written[value]\n        if index.nil?\n          @packer.write(value)\n          written = true\n          @written[value] = @written.size\n        else\n          value = Extension::InnerTabulation.new(index)\n        end\n      end\n    end\n    @packer.write(value) unless written\n  end\n\n  # Called from extension callbacks only\n  #\n  # @api private\n  def build_payload\n    raise SerializationError, \"Internal error: Class #{self.class} does not implement method 'build_payload'\"\n  end\n\n  # @api private\n  def extension_packer\n    @extension_packer\n  end\n\n  # Called from extension callbacks only\n  #\n  # @api private\n  def write_tpl_qname(ep, qname)\n    names = qname.split('::')\n    ep.write(names.size)\n    names.each { |n| write_tpl(ep, n) }\n  end\n\n  # Called from extension callbacks only\n  #\n  # @api private\n  def write_tpl(ep, value)\n    # TRANSLATORS 'Integers' is a Ruby class for numbers and should not be translated\n    raise ArgumentError, _('Internal error. Integers cannot be tabulated in extension payload') if value.is_a?(Integer)\n\n    if @tabulate\n      index = @written[value]\n      if index.nil?\n        @written[value] = @written.size\n      else\n        value = index\n      end\n    end\n    ep.write(value)\n  end\n\n  # @api private\n  def register_type(extension_number, payload_class, &block)\n    @packer.register_type(extension_number, payload_class, &block)\n  end\n\n  # @api private\n  def register_types\n    # 0x00 - 0x0F are reserved for low-level serialization / tabulation extensions\n\n    register_type(Extension::INNER_TABULATION, Extension::InnerTabulation) do |o|\n      build_payload { |ep| ep.write(o.index) }\n    end\n\n    register_type(Extension::TABULATION, Extension::Tabulation) do |o|\n      build_payload { |ep| ep.write(o.index) }\n    end\n\n    # 0x10 - 0x1F are reserved for structural extensions\n\n    register_type(Extension::ARRAY_START, Extension::ArrayStart) do |o|\n      build_payload { |ep| ep.write(o.size) }\n    end\n\n    register_type(Extension::MAP_START, Extension::MapStart) do |o|\n      build_payload { |ep| ep.write(o.size) }\n    end\n\n    register_type(Extension::PCORE_OBJECT_START, Extension::PcoreObjectStart) do |o|\n      build_payload { |ep| write_tpl_qname(ep, o.type_name); ep.write(o.attribute_count) }\n    end\n\n    register_type(Extension::OBJECT_START, Extension::ObjectStart) do |o|\n      build_payload { |ep| ep.write(o.attribute_count) }\n    end\n\n    # 0x20 - 0x2f reserved for special extension objects\n\n    register_type(Extension::DEFAULT, Extension::Default) do |_o|\n      build_payload { |ep| }\n    end\n\n    register_type(Extension::COMMENT, Extension::Comment) do |o|\n      build_payload { |ep| ep.write(o.comment) }\n    end\n\n    register_type(Extension::SENSITIVE_START, Extension::SensitiveStart) do |_o|\n      build_payload { |ep| }\n    end\n\n    # 0x30 - 0x7f reserved for mapping of specific runtime classes\n\n    register_type(Extension::REGEXP, Regexp) do |o|\n      build_payload { |ep| ep.write(o.source) }\n    end\n\n    register_type(Extension::TYPE_REFERENCE, Types::PTypeReferenceType) do |o|\n      build_payload { |ep| ep.write(o.type_string) }\n    end\n\n    register_type(Extension::SYMBOL, Symbol) do |o|\n      build_payload { |ep| ep.write(o.to_s) }\n    end\n\n    register_type(Extension::TIME, Time::Timestamp) do |o|\n      build_payload { |ep| nsecs = o.nsecs; ep.write(nsecs / 1_000_000_000); ep.write(nsecs % 1_000_000_000) }\n    end\n\n    register_type(Extension::TIMESPAN, Time::Timespan) do |o|\n      build_payload { |ep| nsecs = o.nsecs; ep.write(nsecs / 1_000_000_000); ep.write(nsecs % 1_000_000_000) }\n    end\n\n    register_type(Extension::VERSION, SemanticPuppet::Version) do |o|\n      build_payload { |ep| ep.write(o.to_s) }\n    end\n\n    register_type(Extension::VERSION_RANGE, SemanticPuppet::VersionRange) do |o|\n      build_payload { |ep| ep.write(o.to_s) }\n    end\n\n    if supports_binary?\n      register_type(Extension::BINARY, Types::PBinaryType::Binary) do |o|\n        # The Ruby MessagePack implementation has special treatment for \"ASCII-8BIT\" strings. They\n        # are written as binary data.\n        build_payload { |ep| ep.write(o.binary_buffer) }\n      end\n    else\n      register_type(Extension::BASE64, Types::PBinaryType::Binary) do |o|\n        build_payload { |ep| ep.write(o.to_s) }\n      end\n    end\n\n    URI.scheme_list.values.each do |uri_class|\n      register_type(Extension::URI, uri_class) do |o|\n        build_payload { |ep| ep.write(o.to_s) }\n      end\n    end\n  end\n\n  def to_s\n    self.class.name.to_s\n  end\n\n  def inspect\n    to_s\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/deserializer.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'extension'\n\nmodule Puppet::Pops\nmodule Serialization\n  # The deserializer is capable of reading, arrays, maps, and complex objects using an underlying protocol reader. It takes care of\n  # resolving tabulations and assembling complex objects. The type of the complex objects are resolved using a loader.\n  # @api public\n  class Deserializer\n    # Provides access to the reader.\n    # @api private\n    attr_reader :reader, :loader\n\n    # @param [AbstractReader] reader the reader used when reading primitive objects from a stream\n    # @param [Loader::Loader] loader the loader used when resolving names of types\n    # @api public\n    def initialize(reader, loader)\n      @read = []\n      @reader = reader\n      @loader = loader\n    end\n\n    # Read the next value from the reader.\n    #\n    # @return [Object] the value that was read\n    # @api public\n    def read\n      val = @reader.read\n      case val\n      when Extension::Tabulation\n        @read[val.index]\n      when Extension::Default\n        :default\n      when Extension::ArrayStart\n        result = remember([])\n        val.size.times { result << read }\n        result\n      when Extension::MapStart\n        result = remember({})\n        val.size.times { key = read; result[key] = read }\n        result\n      when Extension::SensitiveStart\n        Types::PSensitiveType::Sensitive.new(read)\n      when Extension::PcoreObjectStart\n        type_name = val.type_name\n        type = Types::TypeParser.singleton.parse(type_name, @loader)\n        raise SerializationError, _(\"No implementation mapping found for Puppet Type %{type_name}\") % { type_name: type_name } if type.is_a?(Types::PTypeReferenceType)\n\n        result = type.read(val.attribute_count, self)\n        if result.is_a?(Types::PObjectType)\n          existing_type = loader.load(:type, result.name)\n          if result.eql?(existing_type)\n            result = existing_type\n          else\n            # Add result to the loader unless it is equal to the existing_type. The add\n            # will only succeed when the existing_type is nil.\n            loader.add_entry(:type, result.name, result, nil)\n          end\n        end\n        result\n      when Extension::ObjectStart\n        type = read\n        type.read(val.attribute_count - 1, self)\n      when Numeric, String, true, false, nil\n        val\n      else\n        remember(val)\n      end\n    end\n\n    # Remember that a value has been read. This means that the value is given an index\n    # and that subsequent reads of a tabulation with that index should return the value.\n    # @param [Object] value The value to remember\n    # @return [Object] the argument\n    # @api private\n    def remember(value)\n      @read << value\n      value\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/extension.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\nmodule Extension\n  # 0x00 - 0x0F are reserved for low-level serialization / tabulation extensions\n\n  # Tabulation internal to the low level protocol reader/writer\n  INNER_TABULATION = 0x00\n\n  # Tabulation managed by the serializer / deserializer\n  TABULATION = 0x01\n\n  # 0x10 - 0x1F are reserved for structural extensions\n  ARRAY_START = 0x10\n  MAP_START = 0x11\n  PCORE_OBJECT_START = 0x12\n  OBJECT_START = 0x13\n  SENSITIVE_START = 0x14\n\n  # 0x20 - 0x2f reserved for special extension objects\n  DEFAULT = 0x20\n  COMMENT = 0x21\n\n  # 0x30 - 0x7f reserved for mapping of specific runtime classes\n  REGEXP = 0x30\n  TYPE_REFERENCE = 0x31\n  SYMBOL = 0x32\n  TIME   = 0x33\n  TIMESPAN = 0x34\n  VERSION = 0x35\n  VERSION_RANGE = 0x36\n  BINARY = 0x37\n  BASE64 = 0x38\n  URI    = 0x39\n\n  # Marker module indicating whether or not an instance is tabulated or not\n  module NotTabulated; end\n\n  # Marker module for objects that starts a sequence, i.e. ArrayStart, MapStart, and PcoreObjectStart\n  module SequenceStart; end\n\n  # The class that triggers the use of the DEFAULT extension. It doesn't have any payload\n  class Default\n    include NotTabulated\n    INSTANCE = Default.new\n  end\n\n  # The class that triggers the use of the TABULATION extension. The payload is the tabulation index\n  class Tabulation\n    include NotTabulated\n    attr_reader :index\n\n    def initialize(index)\n      @index = index\n    end\n  end\n\n  # Tabulation internal to the protocol reader/writer\n  class InnerTabulation < Tabulation\n  end\n\n  # The class that triggers the use of the MAP_START extension. The payload is the map size (number of entries)\n  class MapStart\n    include NotTabulated\n    include SequenceStart\n    attr_reader :size\n\n    def initialize(size)\n      @size = size\n    end\n\n    # Sequence size is twice the map size since each entry is written as key and value\n    def sequence_size\n      @size * 2\n    end\n  end\n\n  # The class that triggers the use of the ARRAY_START extension. The payload is the array size\n  class ArrayStart\n    include NotTabulated\n    include SequenceStart\n    attr_reader :size\n\n    def initialize(size)\n      @size = size\n    end\n\n    def sequence_size\n      @size\n    end\n  end\n\n  # The class that triggers the use of the SENSITIVE_START extension. It has no payload\n  class SensitiveStart\n    include NotTabulated\n    INSTANCE = SensitiveStart.new\n  end\n\n  # The class that triggers the use of the PCORE_OBJECT_START extension. The payload is the name of the object type\n  # and the number of attributes in the instance.\n  class PcoreObjectStart\n    include SequenceStart\n    attr_reader :type_name, :attribute_count\n\n    def initialize(type_name, attribute_count)\n      @type_name = type_name\n      @attribute_count = attribute_count\n    end\n\n    def hash\n      @type_name.hash * 29 + attribute_count.hash\n    end\n\n    def eql?(o)\n      o.is_a?(PcoreObjectStart) && o.type_name == @type_name && o.attribute_count == @attribute_count\n    end\n    alias == eql?\n\n    def sequence_size\n      @attribute_count\n    end\n  end\n\n  class ObjectStart\n    include SequenceStart\n    attr_reader :attribute_count\n\n    def initialize(attribute_count)\n      @attribute_count = attribute_count\n    end\n\n    def hash\n      attribute_count.hash\n    end\n\n    def eql?(o)\n      o.is_a?(ObjectStart) && o.attribute_count == @attribute_count\n    end\n    alias == eql?\n\n    def sequence_size\n      @attribute_count\n    end\n  end\n\n  # The class that triggers the use of the COMMENT extension. The payload is comment text\n  class Comment\n    attr_reader :comment\n\n    def initialize(comment)\n      @comment = comment\n    end\n\n    def hash\n      @comment.hash\n    end\n\n    def eql?(o)\n      o.is_a?(Comment) && o.comment == @comment\n    end\n    alias == eql?\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/from_data_converter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  class Builder\n    def initialize(values)\n      @values = values\n      @resolved = true\n    end\n\n    def [](key)\n      @values[key]\n    end\n\n    def []=(key, value)\n      @values[key] = value\n      @resolved = false if value.is_a?(Builder)\n    end\n\n    def resolve\n      unless @resolved\n        @resolved = true\n        case @values\n        when Array\n          @values.each_with_index { |v, idx| @values[idx] = v.resolve if v.is_a?(Builder) }\n        when Hash\n          @values.each_pair { |k, v| @values[k] = v.resolve if v.is_a?(Builder) }\n        end\n      end\n      @values\n    end\n  end\n\n  class ObjectHashBuilder < Builder\n    def initialize(instance)\n      super({})\n      @instance = instance\n    end\n\n    def resolve\n      @instance._pcore_init_from_hash(super)\n      @instance\n    end\n  end\n\n  class ObjectArrayBuilder < Builder\n    def initialize(instance)\n      super({})\n      @instance = instance\n    end\n\n    def resolve\n      @instance.send(:initialize, *super.values)\n      @instance\n    end\n  end\n\n  # Class that can process the `Data` produced by the {ToDataConverter} class and reassemble\n  # the objects that were converted.\n  #\n  # @api public\n  class FromDataConverter\n    # Converts the given `Data` _value_ according to the given _options_ and returns the resulting `RichData`.\n    #\n    # @param value [Data] the value to convert\n    # @param options {Symbol => <Boolean,String>} options hash\n    # @option options [Loaders::Loader] :loader the loader to use. Can be `nil` in which case the default is\n    #    determined by the {Types::TypeParser}.\n    # @option options [Boolean] :allow_unresolved `true` to allow that rich_data hashes are kept \"as is\" if the\n    #    designated '__ptype' cannot be resolved. Defaults to `false`.\n    # @return [RichData] the processed result.\n    #\n    # @api public\n    def self.convert(value, options = EMPTY_HASH)\n      new(options).convert(value)\n    end\n\n    # Creates a new instance of the processor\n    #\n    # @param options {Symbol => Object} options hash\n    # @option options [Loaders::Loader] :loader the loader to use. Can be `nil` in which case the default is\n    #    determined by the {Types::TypeParser}.\n    # @option options [Boolean] :allow_unresolved `true` to allow that rich_data hashes are kept \"as is\" if the\n    #    designated '__ptype' cannot be resolved. Defaults to `false`.\n    #\n    # @api public\n    def initialize(options = EMPTY_HASH)\n      @allow_unresolved = options[:allow_unresolved]\n      @allow_unresolved = false if @allow_unresolved.nil?\n      @loader = options[:loader]\n\n      @pcore_type_procs = {\n        PCORE_TYPE_HASH => proc do |hash, _|\n          value = hash[PCORE_VALUE_KEY]\n          build({}) do\n            top = value.size\n            idx = 0\n            while idx < top\n              key = without_value { convert(value[idx]) }\n              idx += 1\n              with(key) { convert(value[idx]) }\n              idx += 1\n            end\n          end\n        end,\n\n        PCORE_TYPE_SENSITIVE => proc do |hash, _|\n          build(Types::PSensitiveType::Sensitive.new(convert(hash[PCORE_VALUE_KEY])))\n        end,\n\n        PCORE_TYPE_DEFAULT => proc do |_, _|\n          build(:default)\n        end,\n\n        PCORE_TYPE_SYMBOL => proc do |hash, _|\n          build(:\"#{hash[PCORE_VALUE_KEY]}\")\n        end,\n\n        PCORE_LOCAL_REF_SYMBOL => proc do |hash, _|\n          build(JsonPath::Resolver.singleton.resolve(@root, hash[PCORE_VALUE_KEY]))\n        end\n      }\n      @pcore_type_procs.default = proc do |hash, type_value|\n        value = hash.include?(PCORE_VALUE_KEY) ? hash[PCORE_VALUE_KEY] : hash.reject { |key, _| PCORE_TYPE_KEY == key }\n        if type_value.is_a?(Hash)\n          type = without_value { convert(type_value) }\n          if type.is_a?(Hash)\n            raise SerializationError, _('Unable to deserialize type from %{type}') % { type: type } unless @allow_unresolved\n\n            hash\n          else\n            pcore_type_hash_to_value(type, value)\n          end\n        else\n          type = Types::TypeParser.singleton.parse(type_value, @loader)\n          if type.is_a?(Types::PTypeReferenceType)\n            unless @allow_unresolved\n              raise SerializationError, _('No implementation mapping found for Puppet Type %{type_name}') % { type_name: type_value }\n            end\n\n            hash\n          else\n            # not a string\n            pcore_type_hash_to_value(type, value)\n          end\n        end\n      end\n    end\n\n    # Converts the given `Data` _value_ and returns the resulting `RichData`\n    #\n    # @param value [Data] the value to convert\n    # @return [RichData] the processed result\n    #\n    # @api public\n    def convert(value)\n      case value\n      when Hash\n        pcore_type = value[PCORE_TYPE_KEY]\n        if pcore_type && (pcore_type.is_a?(String) || pcore_type.is_a?(Hash))\n          @pcore_type_procs[pcore_type].call(value, pcore_type)\n        else\n          build({}) { value.each_pair { |key, elem| with(key) { convert(elem) } } }\n        end\n      when Array\n        build([]) { value.each_with_index { |elem, idx| with(idx) { convert(elem) } } }\n      else\n        build(value)\n      end\n    end\n\n    private\n\n    def with(key)\n      parent_key = @key\n      @key = key\n      yield\n      @key = parent_key\n    end\n\n    def with_value(value)\n      @root = value unless instance_variable_defined?(:@root)\n      parent = @current\n      @current = value\n      yield\n      @current = parent\n      value\n    end\n\n    def without_value\n      parent = @current\n      @current = nil\n      value = yield\n      @current = parent\n      value\n    end\n\n    def build(value, &block)\n      vx = Builder.new(value)\n      @current[@key] = vx unless @current.nil?\n      with_value(vx, &block) if block_given?\n      vx.resolve\n    end\n\n    def build_object(builder, &block)\n      @current[@key] = builder unless @current.nil?\n      with_value(builder, &block) if block_given?\n      builder.resolve\n    end\n\n    def pcore_type_hash_to_value(pcore_type, value)\n      case value\n      when Hash\n        # Complex object\n        if value.empty?\n          build(pcore_type.create)\n        elsif pcore_type.implementation_class.respond_to?(:_pcore_init_from_hash)\n          build_object(ObjectHashBuilder.new(pcore_type.allocate)) { value.each_pair { |key, elem| with(key) { convert(elem) } } }\n        else\n          build_object(ObjectArrayBuilder.new(pcore_type.allocate)) { value.each_pair { |key, elem| with(key) { convert(elem) } } }\n        end\n      when String\n        build(pcore_type.create(value))\n      else\n        raise SerializationError, _('Cannot create a %{type_name} from a %{arg_class}') %\n                                  { :type_name => pcore_type.name, :arg_class => value.class.name }\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/instance_reader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  # An InstanceReader is responsible for reading an instance of a complex object using a deserializer. The read involves creating the\n  # instance, register it with the deserializer (so that self references can be resolved) and then read the instance data (which normally\n  # amounts to all attribute values).\n  # Instance readers are registered with of {Types::PObjectType}s to aid the type when reading instances.\n  #\n  # @api private\n  module InstanceReader\n    # @param [Class] impl_class the class of the instance to be created and initialized\n    # @param [Integer] value_count the expected number of objects that forms the initialization data\n    # @param [Deserializer] deserializer the deserializer to read from, and to register the instance with\n    # @return [Object] the instance that has been read\n    def read(impl_class, value_count, deserializer)\n      Serialization.not_implemented(self, 'read')\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/instance_writer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  # An instance writer is responsible for writing complex objects using a {Serializer}\n  # @api private\n  module InstanceWriter\n    # @param [Types::PObjectType] type the type of instance to write\n    # @param [Object] value the instance\n    # @param [Serializer] serializer the serializer that will receive the written instance\n    def write(type, value, serializer)\n      Serialization.not_implemented(self, 'write')\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/json.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/json'\n\nmodule Puppet::Pops\nmodule Serialization\nrequire_relative 'time_factory'\nrequire_relative 'abstract_reader'\nrequire_relative 'abstract_writer'\n\nmodule JSON\n  # A Writer that writes output in JSON format\n  # @api private\n  class Writer < AbstractWriter\n    def initialize(io, options = {})\n      super(Packer.new(io, options), options)\n    end\n\n    # Clear the underlying io stream but does not clear tabulation cache\n    # Specifically designed to enable tabulation to span more than one\n    # separately deserialized object.\n    def clear_io\n      @packer.clear_io\n    end\n\n    def extension_packer\n      @packer\n    end\n\n    def packer\n      @packer\n    end\n\n    def build_payload\n      yield(@packer)\n    end\n\n    def to_a\n      @packer.to_a\n    end\n\n    def to_json\n      @packer.to_json\n    end\n  end\n\n  # A Reader that reads JSON formatted input\n  # @api private\n  class Reader < AbstractReader\n    def initialize(io)\n      super(Unpacker.new(io))\n    end\n\n    def re_initialize(io)\n      @unpacker.re_initialize(io)\n    end\n\n    def read_payload(data)\n      yield(@unpacker)\n    end\n  end\n\n  # The JSON Packer. Modeled after the MessagePack::Packer\n  # @api private\n  class Packer\n    def initialize(io, options = {})\n      @io = io\n      @io << '['\n      @type_registry = {}\n      @nested = []\n      @verbose = options[:verbose]\n      @verbose = false if @verbose.nil?\n      @indent = options[:indent] || 0\n    end\n\n    def register_type(type, klass, &block)\n      @type_registry[klass] = [type, klass, block]\n    end\n\n    def clear_io\n      # Truncate everything except leading '['\n      if @io.is_a?(String)\n        @io.slice!(1..-1)\n      else\n        @io.truncate(1)\n      end\n    end\n\n    def empty?\n      @io.is_a?(String) ? io.length == 1 : @io.pos == 1\n    end\n\n    def flush\n      # Drop last comma unless just start marker present\n      if @io.is_a?(String)\n        @io.chop! unless @io.length == 1\n        @io << ']'\n      else\n        pos = @io.pos\n        @io.pos = pos - 1 unless pos == 1\n        @io << ']'\n        @io.flush\n      end\n    end\n\n    def write(obj)\n      case obj\n      when Array\n        write_array_header(obj.size)\n        obj.each { |x| write(x) }\n      when Hash\n        write_map_header(obj.size)\n        obj.each_pair { |k, v| write(k); write(v) }\n      when nil\n        write_nil\n      else\n        write_scalar(obj)\n      end\n    end\n    alias pack write\n\n    def write_array_header(n)\n      if n < 1\n        @io << '[]'\n      else\n        @io << '['\n        @nested << [false, n]\n      end\n    end\n\n    def write_map_header(n)\n      if n < 1\n        @io << '{}'\n      else\n        @io << '{'\n        @nested << [true, n * 2]\n      end\n    end\n\n    def write_nil\n      @io << 'null'\n      write_delim\n    end\n\n    def to_s\n      to_json\n    end\n\n    def to_a\n      ::Puppet::Util::Json.load(io_string)\n    end\n\n    def to_json\n      if @indent > 0\n        ::Puppet::Util::Json.dump(to_a, { :pretty => true, :indent => ' ' * @indent })\n      else\n        io_string\n      end\n    end\n\n    # Write a payload object. Not subject to extensions\n    def write_pl(obj)\n      @io << Puppet::Util::Json.dump(obj) << ','\n    end\n\n    def io_string\n      if @io.is_a?(String)\n        @io\n      else\n        @io.pos = 0\n        @io.read\n      end\n    end\n    private :io_string\n\n    def write_delim\n      nesting = @nested.last\n      cnt = nesting.nil? ? nil : nesting[1]\n      case cnt\n      when 1\n        @io << (nesting[0] ? '}' : ']')\n        @nested.pop\n        write_delim\n      when Integer\n        if cnt.even? || !nesting[0]\n          @io << ','\n        else\n          @io << ':'\n        end\n        nesting[1] = cnt - 1\n      else\n        @io << ','\n      end\n    end\n    private :write_delim\n\n    def write_scalar(obj)\n      ext = @type_registry[obj.class]\n      if ext.nil?\n        case obj\n        when Numeric, String, true, false, nil\n          @io << Puppet::Util::Json.dump(obj)\n          write_delim\n        else\n          raise SerializationError, _(\"Unable to serialize a %{obj}\") % { obj: obj.class.name }\n        end\n      else\n        write_extension(ext, obj)\n      end\n    end\n    private :write_scalar\n\n    def write_extension(ext, obj)\n      @io << '[' << extension_indicator(ext).to_json << ','\n      @nested << nil\n      write_extension_payload(ext, obj)\n      @nested.pop\n      if obj.is_a?(Extension::SequenceStart) && obj.sequence_size > 0\n        @nested << [false, obj.sequence_size]\n      else\n        if @io.is_a?(String)\n          @io.chop!\n        else\n          @io.pos -= 1\n        end\n        @io << ']'\n        write_delim\n      end\n    end\n    private :write_extension\n\n    def write_extension_payload(ext, obj)\n      ext[2].call(obj)\n    end\n    private :write_extension_payload\n\n    def extension_indicator(ext)\n      @verbose ? ext[1].name.sub(/^Puppet::Pops::Serialization::\\w+::(.+)$/, '\\1') : ext[0]\n    end\n    private :extension_indicator\n  end\n\n  class Unpacker\n    def initialize(io)\n      re_initialize(io)\n      @type_registry = {}\n      @nested = []\n    end\n\n    def re_initialize(io)\n      parsed = parse_io(io)\n      raise SerializationError, _(\"JSON stream is not an array. It is a %{klass}\") % { klass: io.class.name } unless parsed.is_a?(Array)\n\n      @etor_stack = [parsed.each]\n    end\n\n    def read\n      obj = nil\n      loop do\n        raise SerializationError, _('Unexpected end of input') if @etor_stack.empty?\n\n        etor = @etor_stack.last\n        begin\n          obj = etor.next\n          break\n        rescue StopIteration\n          @etor_stack.pop\n        end\n      end\n      if obj.is_a?(Array)\n        ext_etor = obj.each\n        @etor_stack << ext_etor\n        ext_no = ext_etor.next\n        ext_block = @type_registry[ext_no]\n        raise SerializationError, _(\"Invalid input. %{ext_no} is not a valid extension number\") % { ext_no: ext_no } if ext_block.nil?\n\n        obj = ext_block.call(nil)\n      end\n      obj\n    end\n\n    def register_type(extension_number, &block)\n      @type_registry[extension_number] = block\n    end\n\n    private\n\n    def parse_io(io)\n      case io\n      when IO, StringIO\n        ::Puppet::Util::Json.load(io.read)\n      when String\n        ::Puppet::Util::Json.load(io)\n      else\n        io\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/json_path.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Serialization\nmodule JsonPath\n  # Creates a _json_path_ reference from the given `path` argument\n  #\n  # @path path [Array<Integer,String>] An array of integers and strings\n  # @return [String] the created json_path\n  #\n  # @api private\n  def self.to_json_path(path)\n    p = '$'.dup\n    path.each do |seg|\n      if seg.nil?\n        p << '[null]'\n      elsif Types::PScalarDataType::DEFAULT.instance?(seg)\n        p << '[' << Types::StringConverter.singleton.convert(seg, '%p') << ']'\n      else\n        # Unable to construct json path from complex segments\n        return nil\n      end\n    end\n    p\n  end\n\n  # Resolver for JSON path that uses the Puppet parser to create the AST. The path must start\n  # with '$' which denotes the value that is passed into the parser. This parser can easily\n  # be extended with more elaborate resolution mechanisms involving document sets.\n  #\n  # The parser is limited to constructs generated by the {JsonPath#to_json_path}\n  # method.\n  #\n  # @api private\n  class Resolver\n    extend Puppet::Concurrent::ThreadLocalSingleton\n\n    def initialize\n      @parser = Parser::Parser.new\n      @visitor = Visitor.new(nil, 'resolve', 2, 2)\n    end\n\n    # Resolve the given _path_ in the given _context_.\n    # @param context [Object] the context used for resolution\n    # @param path [String] the json path\n    # @return [Object] the resolved value\n    #\n    def resolve(context, path)\n      factory = @parser.parse_string(path)\n      v = resolve_any(factory.model.body, context, path)\n      v.is_a?(Builder) ? v.resolve : v\n    end\n\n    def resolve_any(ast, context, path)\n      @visitor.visit_this_2(self, ast, context, path)\n    end\n\n    def resolve_AccessExpression(ast, context, path)\n      bad_json_path(path) unless ast.keys.size == 1\n      receiver = resolve_any(ast.left_expr, context, path)\n      key = resolve_any(ast.keys[0], context, path)\n      if receiver.is_a?(Types::PuppetObject)\n        PCORE_TYPE_KEY == key ? receiver._pcore_type : receiver.send(key)\n      else\n        receiver[key]\n      end\n    end\n\n    def resolve_NamedAccessExpression(ast, context, path)\n      receiver = resolve_any(ast.left_expr, context, path)\n      key = resolve_any(ast.right_expr, context, path)\n      if receiver.is_a?(Types::PuppetObject)\n        PCORE_TYPE_KEY == key ? receiver._pcore_type : receiver.send(key)\n      else\n        receiver[key]\n      end\n    end\n\n    def resolve_QualifiedName(ast, _, _)\n      v = ast.value\n      'null' == v ? nil : v\n    end\n\n    def resolve_QualifiedReference(ast, _, _)\n      v = ast.cased_value\n      'null'.casecmp(v) == 0 ? nil : v\n    end\n\n    def resolve_ReservedWord(ast, _, _)\n      ast.word\n    end\n\n    def resolve_LiteralUndef(_, _, _)\n      'undef'\n    end\n\n    def resolve_LiteralDefault(_, _, _)\n      'default'\n    end\n\n    def resolve_VariableExpression(ast, context, path)\n      # A single '$' means root, i.e. the context.\n      bad_json_path(path) unless EMPTY_STRING == resolve_any(ast.expr, context, path)\n      context\n    end\n\n    def resolve_CallMethodExpression(ast, context, path)\n      bad_json_path(path) unless ast.arguments.empty?\n      resolve_any(ast.functor_expr, context, path)\n    end\n\n    def resolve_LiteralValue(ast, _, _)\n      ast.value\n    end\n\n    def resolve_Object(ast, _, path)\n      bad_json_path(path)\n    end\n\n    def bad_json_path(path)\n      raise SerializationError, _('Unable to parse jsonpath \"%{path}\"') % { :path => path }\n    end\n    private :bad_json_path\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/object.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'instance_reader'\nrequire_relative 'instance_writer'\n\nmodule Puppet::Pops\nmodule Serialization\n# Instance reader for objects that implement {Types::PuppetObject}\n# @api private\nclass ObjectReader\n  include InstanceReader\n\n  def read(type, impl_class, value_count, deserializer)\n    (names, types, required_count) = type.parameter_info(impl_class)\n    max = names.size\n    unless value_count >= required_count && value_count <= max\n      raise Serialization::SerializationError, _(\"Feature count mismatch for %{value0}. Expected %{required_count} - %{max}, actual %{value_count}\") % { value0: impl_class.name, required_count: required_count, max: max, value_count: value_count }\n    end\n\n    # Deserializer must know about this instance before we read its attributes\n    val = deserializer.remember(impl_class.allocate)\n    args = Array.new(value_count) { deserializer.read }\n    types.each_with_index do |ptype, index|\n      if index < args.size\n        arg = args[index]\n        Types::TypeAsserter.assert_instance_of(nil, ptype, arg) { \"#{type.name}[#{names[index]}]\" } unless arg == :default\n      else\n        attr = type[names[index]]\n        raise Serialization::SerializationError, _(\"Missing default value for %{type_name}[%{name}]\") % { type_name: type.name, name: names[index] } unless attr && attr.value?\n\n        args << attr.value\n      end\n    end\n    val.send(:initialize, *args)\n    val\n  end\n\n  INSTANCE = ObjectReader.new\nend\n\n# Instance writer for objects that implement {Types::PuppetObject}\n# @api private\nclass ObjectWriter\n  include InstanceWriter\n\n  def write(type, value, serializer)\n    impl_class = value.class\n    (names, _, required_count) = type.parameter_info(impl_class)\n    args = names.map { |name| value.send(name) }\n\n    # Pop optional arguments that are default\n    while args.size > required_count\n      break unless type[names[args.size - 1]].default_value?(args.last)\n\n      args.pop\n    end\n\n    if type.name.start_with?('Pcore::') || serializer.type_by_reference?\n      serializer.push_written(value)\n      serializer.start_pcore_object(type.name, args.size)\n    else\n      serializer.start_object(args.size + 1)\n      serializer.write(type)\n      serializer.push_written(value)\n    end\n\n    args.each { |arg| serializer.write(arg) }\n  end\n\n  INSTANCE = ObjectWriter.new\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/serializer.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'extension'\n\nmodule Puppet::Pops\nmodule Serialization\n  # The serializer is capable of writing, arrays, maps, and complex objects using an underlying protocol writer. It takes care of\n  # tabulating and disassembling complex objects.\n  # @api public\n  class Serializer\n    # Provides access to the writer.\n    # @api private\n    attr_reader :writer\n\n    # @param writer [AbstractWriter] the writer that is used for writing primitive values\n    # @param options [{String, Object}] serialization options\n    # @option options [Boolean] :type_by_reference `true` if Object types are serialized by name only.\n    # @api public\n    def initialize(writer, options = EMPTY_HASH)\n      # Hash#compare_by_identity is intentionally not used since we only need to map\n      # object ids to their size and we don't want to store entire objects in memory\n      @written = {}\n      @writer = writer\n      @options = options\n    end\n\n    # Tell the underlying writer to finish\n    # @api public\n    def finish\n      @writer.finish\n    end\n\n    # Write an object\n    # @param [Object] value the object to write\n    # @api public\n    def write(value)\n      case value\n      when Integer, Float, String, true, false, nil\n        @writer.write(value)\n      when :default\n        @writer.write(Extension::Default::INSTANCE)\n      else\n        index = @written[value.object_id] # rubocop:disable Lint/HashCompareByIdentity\n        if index.nil?\n          write_tabulated_first_time(value)\n        else\n          @writer.write(Extension::Tabulation.new(index))\n        end\n      end\n    end\n\n    # Write the start of an array.\n    # @param [Integer] size the size of the array\n    # @api private\n    def start_array(size)\n      @writer.write(Extension::ArrayStart.new(size))\n    end\n\n    # Write the start of a map (hash).\n    # @param [Integer] size the number of entries in the map\n    # @api private\n    def start_map(size)\n      @writer.write(Extension::MapStart.new(size))\n    end\n\n    # Write the start of a complex pcore object\n    # @param [String] type_ref the name of the type\n    # @param [Integer] attr_count the number of attributes in the object\n    # @api private\n    def start_pcore_object(type_ref, attr_count)\n      @writer.write(Extension::PcoreObjectStart.new(type_ref, attr_count))\n    end\n\n    # Write the start of a complex object\n    # @param [Integer] attr_count the number of attributes in the object\n    # @api private\n    def start_object(attr_count)\n      @writer.write(Extension::ObjectStart.new(attr_count))\n    end\n\n    def push_written(value)\n      @written[value.object_id] = @written.size # rubocop:disable Lint/HashCompareByIdentity\n    end\n\n    # Write the start of a sensitive object\n    # @api private\n    def start_sensitive\n      @writer.write(Extension::SensitiveStart::INSTANCE)\n    end\n\n    def type_by_reference?\n      @options[:type_by_reference] == true\n    end\n\n    def to_s\n      \"#{self.class.name} with #{@writer}\"\n    end\n\n    def inspect\n      to_s\n    end\n\n    # First time write of a tabulated object. This means that the object is written and then remembered. Subsequent writes\n    # of the same object will yield a write of a tabulation index instead.\n    # @param [Object] value the value to write\n    # @api private\n    def write_tabulated_first_time(value)\n      if value.instance_of?(Symbol) ||\n         value.instance_of?(Regexp) ||\n         value.instance_of?(SemanticPuppet::Version) ||\n         value.instance_of?(SemanticPuppet::VersionRange) ||\n         value.instance_of?(Time::Timestamp) ||\n         value.instance_of?(Time::Timespan) ||\n         value.instance_of?(Types::PBinaryType::Binary) ||\n         value.is_a?(URI)\n        push_written(value)\n        @writer.write(value)\n      elsif value.instance_of?(Array)\n        push_written(value)\n        start_array(value.size)\n        value.each { |elem| write(elem) }\n      elsif value.instance_of?(Hash)\n        push_written(value)\n        start_map(value.size)\n        value.each_pair { |key, val| write(key); write(val) }\n      elsif value.instance_of?(Types::PSensitiveType::Sensitive)\n        start_sensitive\n        write(value.unwrap)\n      elsif value.instance_of?(Types::PTypeReferenceType)\n        push_written(value)\n        @writer.write(value)\n      elsif value.is_a?(Types::PuppetObject)\n        value._pcore_type.write(value, self)\n      else\n        impl_class = value.class\n        type = Loaders.implementation_registry.type_for_module(impl_class)\n        raise SerializationError, _(\"No Puppet Type found for %{klass}\") % { klass: impl_class.name } unless type.is_a?(Types::PObjectType)\n\n        type.write(value, self)\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/time_factory.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  # Implements all the constructors found in the Time class and ensures that\n  # the created Time object can be serialized and deserialized using its\n  # seconds and nanoseconds without loss of precision.\n  #\n  # @deprecated No longer in use. Functionality replaced by Timestamp\n  # @api private\n  class TimeFactory\n    NANO_DENOMINATOR = 10**9\n\n    def self.at(*args)\n      sec_nsec_safe(Time.at(*args))\n    end\n\n    def self.gm(*args)\n      sec_nsec_safe(Time.gm(*args))\n    end\n\n    def self.local(*args)\n      sec_nsec_safe(Time.local(*args))\n    end\n\n    def self.mktime(*args)\n      sec_nsec_safe(Time.mktime(*args))\n    end\n\n    def self.new(*args)\n      sec_nsec_safe(Time.new(*args))\n    end\n\n    def self.now\n      sec_nsec_safe(Time.now)\n    end\n\n    def self.utc(*args)\n      sec_nsec_safe(Time.utc(*args))\n    end\n\n    # Creates a Time object from a Rational defined as:\n    #\n    # (_sec_ * #NANO_DENOMINATOR + _nsec_) / #NANO_DENOMINATOR\n    #\n    # This ensures that a Time object can be reliably serialized and using its\n    # its #tv_sec and #tv_nsec values and then recreated again (using this method)\n    # without loss of precision.\n    #\n    # @param sec [Integer] seconds since Epoch\n    # @param nsec [Integer] nano seconds\n    # @return [Time] the created object\n    #\n    def self.from_sec_nsec(sec, nsec)\n      Time.at(Rational(sec * NANO_DENOMINATOR + nsec, NANO_DENOMINATOR))\n    end\n\n    # Returns a new Time object that is adjusted to ensure that precision is not\n    # lost when it is serialized and deserialized using its seconds and nanoseconds\n    # @param t [Time] the object to adjust\n    # @return [Time] the adjusted object\n    #\n    def self.sec_nsec_safe(t)\n      from_sec_nsec(t.tv_sec, t.tv_nsec)\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/to_data_converter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  # Class that can process an arbitrary object into a value that is assignable to `Data`.\n  #\n  # @api public\n  class ToDataConverter\n    include Evaluator::Runtime3Support\n\n    # Convert the given _value_ according to the given _options_ and return the result of the conversion\n    #\n    # @param value [Object] the value to convert\n    # @param options {Symbol => <Boolean,String>} options hash\n    # @option options [Boolean] :rich_data `true` if rich data is enabled\n    # @option options [Boolean] :local_reference use local references instead of duplicating complex entries\n    # @option options [Boolean] :type_by_reference `true` if Object types are converted to references rather than embedded.\n    # @option options [Boolean] :symbol_as_string `true` if Symbols should be converted to strings (with type loss)\n    # @option options [String] :message_prefix String to prepend to in warnings and errors\n    # @return [Data] the processed result. An object assignable to `Data`.\n    #\n    # @api public\n    def self.convert(value, options = EMPTY_HASH)\n      new(options).convert(value)\n    end\n\n    # Create a new instance of the processor\n    #\n    # @param options {Symbol => Object} options hash\n    # @option options [Boolean] :rich_data `true` if rich data is enabled\n    # @option options [Boolean] :local_references use local references instead of duplicating complex entries\n    # @option options [Boolean] :type_by_reference `true` if Object types are converted to references rather than embedded.\n    # @option options [Boolean] :symbol_as_string `true` if Symbols should be converted to strings (with type loss)\n    # @option options [String] :message_prefix String to prepend to path in warnings and errors\n    # @option semantic [Object] :semantic object to pass to the issue reporter\n    def initialize(options = EMPTY_HASH)\n      @type_by_reference = options[:type_by_reference]\n      @type_by_reference = true if @type_by_reference.nil?\n\n      @local_reference = options[:local_reference]\n      @local_reference = true if @local_reference.nil?\n\n      @symbol_as_string = options[:symbol_as_string]\n      @symbol_as_string = false if @symbol_as_string.nil?\n\n      @rich_data = options[:rich_data]\n      @rich_data = false if @rich_data.nil?\n\n      @message_prefix = options[:message_prefix]\n      @semantic = options[:semantic]\n    end\n\n    # Convert the given _value_\n    #\n    # @param value [Object] the value to convert\n    # @return [Data] the processed result. An object assignable to `Data`.\n    #\n    # @api public\n    def convert(value)\n      @path = []\n      @values = {}\n      to_data(value)\n    end\n\n    private\n\n    def path_to_s\n      s = String.new(@message_prefix || '')\n      s << JsonPath.to_json_path(@path)[1..]\n      s\n    end\n\n    def to_data(value)\n      if value.nil? || Types::PScalarDataType::DEFAULT.instance?(value)\n        if @rich_data && value.is_a?(String) && value.encoding == Encoding::ASCII_8BIT\n          # Transform the binary string to rich Binary\n          {\n            PCORE_TYPE_KEY => PCORE_TYPE_BINARY,\n            PCORE_VALUE_KEY => Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(value).to_s\n          }\n        else\n          value\n        end\n      elsif :default == value\n        if @rich_data\n          { PCORE_TYPE_KEY => PCORE_TYPE_DEFAULT }\n        else\n          serialization_issue(Issues::SERIALIZATION_DEFAULT_CONVERTED_TO_STRING, :path => path_to_s)\n          'default'\n        end\n      elsif value.is_a?(Symbol)\n        if @symbol_as_string\n          value.to_s\n        elsif @rich_data\n          { PCORE_TYPE_KEY => PCORE_TYPE_SYMBOL, PCORE_VALUE_KEY => value.to_s }\n        else\n          unknown_to_string_with_warning(value)\n        end\n      elsif value.instance_of?(Array)\n        process(value) do\n          result = []\n          value.each_with_index do |elem, index|\n            with(index) { result << to_data(elem) }\n          end\n          result\n        end\n      elsif value.instance_of?(Hash)\n        process(value) do\n          if value.keys.all? { |key| key.is_a?(String) && key != PCORE_TYPE_KEY }\n            result = {}\n            value.each_pair { |key, elem| with(key) { result[key] = to_data(elem) } }\n            result\n          else\n            non_string_keyed_hash_to_data(value)\n          end\n        end\n      elsif value.instance_of?(Types::PSensitiveType::Sensitive)\n        process(value) do\n          { PCORE_TYPE_KEY => PCORE_TYPE_SENSITIVE, PCORE_VALUE_KEY => to_data(value.unwrap) }\n        end\n      else\n        unknown_to_data(value)\n      end\n    end\n\n    # If `:local_references` is enabled, then the `object_id` will be associated with the current _path_ of\n    # the context the first time this method is called. The method then returns the result of  yielding to\n    # the given block. Subsequent calls with a value that has the same `object_id` will instead return a\n    # reference based on the given path.\n    #\n    # If `:local_references` is disabled, then this method performs a check for endless recursion before\n    # it yields to the given block. The result of yielding is returned.\n    #\n    # @param value [Object] the value\n    # @yield The block that will produce the data for the value\n    # @return [Data] the result of yielding to the given block, or a hash denoting a reference\n    #\n    # @api private\n    def process(value, &block)\n      if @local_reference\n        id = value.object_id\n        ref = @values[id]\n        if ref.nil?\n          @values[id] = @path.dup\n          yield\n        elsif ref.instance_of?(Hash)\n          ref\n        else\n          json_ref = JsonPath.to_json_path(ref)\n          if json_ref.nil?\n            # Complex key and hence no way to reference the prior value. The value must therefore be\n            # duplicated which in turn introduces a risk for endless recursion in case of self\n            # referencing structures\n            with_recursive_guard(value, &block)\n          else\n            @values[id] = { PCORE_TYPE_KEY => PCORE_LOCAL_REF_SYMBOL, PCORE_VALUE_KEY => json_ref }\n          end\n        end\n      else\n        with_recursive_guard(value, &block)\n      end\n    end\n\n    # Pushes `key` to the end of the path and yields to the given block. The\n    # `key` is popped when the yield returns.\n    # @param key [Object] the key to push on the current path\n    # @yield The block that will produce the returned value\n    # @return [Object] the result of yielding to the given block\n    #\n    # @api private\n    def with(key)\n      @path.push(key)\n      value = yield\n      @path.pop\n      value\n    end\n\n    # @param value [Object] the value to use when checking endless recursion\n    # @yield The block that will produce the data\n    # @return [Data] the result of yielding to the given block\n    def with_recursive_guard(value)\n      id = value.object_id\n      if @recursive_lock\n        if @recursive_lock.include?(id)\n          serialization_issue(Issues::SERIALIZATION_ENDLESS_RECURSION, :type_name => value.class.name)\n        end\n        @recursive_lock[id] = true\n      else\n        @recursive_lock = { id => true }\n      end\n      v = yield\n      @recursive_lock.delete(id)\n      v\n    end\n\n    def unknown_to_data(value)\n      @rich_data ? value_to_data_hash(value) : unknown_to_string_with_warning(value)\n    end\n\n    def unknown_key_to_string_with_warning(value)\n      str = unknown_to_string(value)\n      serialization_issue(Issues::SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING, :path => path_to_s, :klass => value.class, :value => str)\n      str\n    end\n\n    def unknown_to_string_with_warning(value)\n      str = unknown_to_string(value)\n      serialization_issue(Issues::SERIALIZATION_UNKNOWN_CONVERTED_TO_STRING, :path => path_to_s, :klass => value.class, :value => str)\n      str\n    end\n\n    def unknown_to_string(value)\n      value.is_a?(Regexp) ? Puppet::Pops::Types::PRegexpType.regexp_to_s_with_delimiters(value) : value.to_s\n    end\n\n    def non_string_keyed_hash_to_data(hash)\n      if @rich_data\n        to_key_extended_hash(hash)\n      else\n        result = {}\n        hash.each_pair do |key, value|\n          if key.is_a?(Symbol) && @symbol_as_string\n            key = key.to_s\n          elsif !key.is_a?(String)\n            key = unknown_key_to_string_with_warning(key)\n          end\n          with(key) { result[key] = to_data(value) }\n        end\n        result\n      end\n    end\n\n    # A Key extended hash is a hash whose keys are not entirely strings. Such a hash\n    # cannot be safely represented as JSON or YAML\n    #\n    # @param hash {Object => Object} the hash to process\n    # @return [String => Data] the representation of the extended hash\n    def to_key_extended_hash(hash)\n      key_value_pairs = []\n      hash.each_pair do |key, value|\n        key = to_data(key)\n        key_value_pairs << key\n        key_value_pairs << with(key) { to_data(value) }\n      end\n      { PCORE_TYPE_KEY => PCORE_TYPE_HASH, PCORE_VALUE_KEY => key_value_pairs }\n    end\n\n    def value_to_data_hash(value)\n      pcore_type = value.is_a?(Types::PuppetObject) ? value._pcore_type : Types::TypeCalculator.singleton.infer(value)\n      if pcore_type.is_a?(Puppet::Pops::Types::PRuntimeType)\n        unknown_to_string_with_warning(value)\n      else\n        pcore_tv = pcore_type_to_data(pcore_type)\n        if pcore_type.roundtrip_with_string?\n          {\n            PCORE_TYPE_KEY => pcore_tv,\n\n            # Scalar values are stored using their default string representation\n            PCORE_VALUE_KEY => Types::StringConverter.singleton.convert(value)\n          }\n        elsif pcore_type.implementation_class.respond_to?(:_pcore_init_from_hash)\n          process(value) do\n            {\n              PCORE_TYPE_KEY => pcore_tv,\n            }.merge(to_data(value._pcore_init_hash))\n          end\n        else\n          process(value) do\n            (names, _, required_count) = pcore_type.parameter_info(value.class)\n            args = names.map { |name| value.send(name) }\n\n            # Pop optional arguments that are default\n            while args.size > required_count\n              break unless pcore_type[names[args.size - 1]].default_value?(args.last)\n\n              args.pop\n            end\n            result = {\n              PCORE_TYPE_KEY => pcore_tv\n            }\n            args.each_with_index do |val, idx|\n              key = names[idx]\n              with(key) { result[key] = to_data(val) }\n            end\n            result\n          end\n        end\n      end\n    end\n\n    def pcore_type_to_data(pcore_type)\n      type_name = pcore_type.name\n      if @type_by_reference || type_name.start_with?('Pcore::')\n        type_name\n      else\n        with(PCORE_TYPE_KEY) { to_data(pcore_type) }\n      end\n    end\n    private :pcore_type_to_data\n\n    def serialization_issue(issue, options = EMPTY_HASH)\n      semantic = @semantic\n      if semantic.nil?\n        tos = Puppet::Pops::PuppetStack.top_of_stack\n        if tos.empty?\n          semantic = Puppet::Pops::SemanticError.new(issue, nil, EMPTY_HASH)\n        else\n          file, line = stacktrace\n          semantic = Puppet::Pops::SemanticError.new(issue, nil, { :file => file, :line => line })\n        end\n      end\n      optionally_fail(issue, semantic, options)\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization/to_stringified_converter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  # Class that can process an arbitrary object into a value that is assignable to `Data`\n  # and where contents is converted from rich data to one of:\n  # * Numeric (Integer, Float)\n  # * Boolean\n  # * Undef (nil)\n  # * String\n  # * Array\n  # * Hash\n  #\n  # The conversion is lossy - the result cannot be deserialized to produce the original data types.\n  # All rich values are transformed to strings..\n  # Hashes with rich keys are transformed to use string representation of such keys.\n  #\n  # @api public\n  class ToStringifiedConverter\n    include Evaluator::Runtime3Support\n\n    # Converts the given _value_ according to the given _options_ and return the result of the conversion\n    #\n    # @param value [Object] the value to convert\n    # @param options {Symbol => <Boolean,String>} options hash\n    # @option options [String] :message_prefix String to prepend to in warnings and errors\n    # @option options [String] :semantic object (AST) to pass to the issue reporter\n    # @return [Data] the processed result. An object assignable to `Data` with rich data stringified.\n    #\n    # @api public\n    def self.convert(value, options = EMPTY_HASH)\n      new(options).convert(value)\n    end\n\n    # Creates a new instance of the processor\n    #\n    # @param options {Symbol => Object} options hash\n    # @option options [String] :message_prefix String to prepend to path in warnings and errors\n    # @option semantic [Object] :semantic object to pass to the issue reporter\n    def initialize(options = EMPTY_HASH)\n      @message_prefix = options[:message_prefix]\n      @semantic = options[:semantic]\n    end\n\n    # Converts the given _value_\n    #\n    # @param value [Object] the value to convert\n    # @return [Data] the processed result. An object assignable to `Data` with rich data stringified.\n    #\n    # @api public\n    def convert(value)\n      @path = []\n      @values = {}\n      to_data(value)\n    end\n\n    private\n\n    def path_to_s\n      s = @message_prefix || ''\n      s << JsonPath.to_json_path(@path)[1..]\n      s\n    end\n\n    def to_data(value)\n      if value.instance_of?(String)\n        to_string_or_binary(value)\n      elsif value.nil? || Types::PScalarDataType::DEFAULT.instance?(value)\n        value\n      elsif :default == value\n        'default'\n      elsif value.is_a?(Symbol)\n        value.to_s\n      elsif value.instance_of?(Array)\n        process(value) do\n          result = []\n          value.each_with_index do |elem, index|\n            with(index) { result << to_data(elem) }\n          end\n          result\n        end\n      elsif value.instance_of?(Hash)\n        process(value) do\n          if value.keys.all? { |key| key.is_a?(String) && key != PCORE_TYPE_KEY && key != PCORE_VALUE_KEY }\n            result = {}\n            value.each_pair { |key, elem| with(key) { result[key] = to_data(elem) } }\n            result\n          else\n            non_string_keyed_hash_to_data(value)\n          end\n        end\n      else\n        unknown_to_string(value)\n      end\n    end\n\n    # Turns an ASCII-8BIT encoded string into a Binary, returns US_ASCII encoded and transforms all other strings to UTF-8\n    # with replacements for non Unicode characters.\n    # If String cannot be represented as UTF-8\n    def to_string_or_binary(value)\n      encoding = value.encoding\n      if encoding == Encoding::ASCII_8BIT\n        Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(value).to_s\n      else\n        # Transform to UTF-8 (do not assume UTF-8 is correct) with source invalid byte\n        # sequences and UTF-8 undefined characters replaced by the default unicode uFFFD character\n        # (black diamond with question mark).\n        value.encode(Encoding::UTF_8, encoding, :invalid => :replace, :undef => :replace)\n      end\n    end\n\n    # Performs a check for endless recursion before\n    # it yields to the given block. The result of yielding is returned.\n    #\n    # @param value [Object] the value\n    # @yield The block that will produce the data for the value\n    # @return [Data] the result of yielding to the given block, or a hash denoting a reference\n    #\n    # @api private\n    def process(value, &block)\n      with_recursive_guard(value, &block)\n    end\n\n    # Pushes `key` to the end of the path and yields to the given block. The\n    # `key` is popped when the yield returns.\n    # @param key [Object] the key to push on the current path\n    # @yield The block that will produce the returned value\n    # @return [Object] the result of yielding to the given block\n    #\n    # @api private\n    def with(key)\n      @path.push(key)\n      value = yield\n      @path.pop\n      value\n    end\n\n    # @param value [Object] the value to use when checking endless recursion\n    # @yield The block that will produce the data\n    # @return [Data] the result of yielding to the given block\n    def with_recursive_guard(value)\n      id = value.object_id\n      if @recursive_lock\n        if @recursive_lock.include?(id)\n          serialization_issue(Issues::SERIALIZATION_ENDLESS_RECURSION, :type_name => value.class.name)\n        end\n        @recursive_lock[id] = true\n      else\n        @recursive_lock = { id => true }\n      end\n      v = yield\n      @recursive_lock.delete(id)\n      v\n    end\n\n    # A hash key that is non conforming\n    def unknown_key_to_string(value)\n      unknown_to_string(value)\n    end\n\n    def unknown_to_string(value)\n      if value.is_a?(Regexp)\n        return Puppet::Pops::Types::PRegexpType.regexp_to_s_with_delimiters(value)\n\n      elsif value.instance_of?(Types::PSensitiveType::Sensitive)\n        # to_s does not differentiate between instances - if they were used as keys in a hash\n        # the stringified result would override all Sensitive keys with the last such key's value\n        # this adds object_id.\n        #\n        return \"#<#{value}:#{value.object_id}>\"\n\n      elsif value.is_a?(Puppet::Pops::Types::PObjectType)\n        # regular to_s on an ObjectType gives the entire definition\n        return value.name\n\n      end\n\n      # Do a to_s on anything else\n      result = value.to_s\n\n      # The result may be ascii-8bit encoded without being a binary (low level object.inspect returns ascii-8bit string)\n      # This can be the case if runtime objects have very simple implementation (no to_s or inspect method).\n      # They are most likely not of Binary nature. Therefore the encoding is forced and only if it errors\n      # will the result be taken as binary and encoded as base64 string.\n      if result.encoding == Encoding::ASCII_8BIT\n        begin\n          result.force_encoding(Encoding::UTF_8)\n        rescue\n          # The result cannot be represented in UTF-8, make it a binary Base64 encoded string\n          Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(result).to_s\n        end\n      end\n      result\n    end\n\n    def non_string_keyed_hash_to_data(hash)\n      result = {}\n      hash.each_pair do |key, value|\n        if key.is_a?(Symbol)\n          key = key.to_s\n        elsif !key.is_a?(String)\n          key = unknown_key_to_string(key)\n        end\n        if key == \"__ptype\" || key == \"__pvalue\"\n          key = \"reserved key: #{key}\"\n        end\n        with(key) { result[key] = to_data(value) }\n      end\n      result\n    end\n\n    def serialization_issue(issue, options = EMPTY_HASH)\n      semantic = @semantic\n      if semantic.nil?\n        tos = Puppet::Pops::PuppetStack.top_of_stack\n        if tos.empty?\n          semantic = Puppet::Pops::SemanticError.new(issue, nil, EMPTY_HASH)\n        else\n          file, line = stacktrace\n          semantic = Puppet::Pops::SemanticError.new(issue, nil, { :file => file, :line => line })\n        end\n      end\n      optionally_fail(issue, semantic, options)\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/serialization.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Serialization\n  def self.not_implemented(impl, method_name)\n    raise NotImplementedError, \"The class #{impl.class.name} should have implemented the method #{method_name}()\"\n  end\n\n  class SerializationError < Puppet::Error\n  end\n\n  PCORE_TYPE_KEY = '__ptype'\n\n  # Key used when the value can be represented as, and recreated from, a single string that can\n  # be passed to a `from_string` method or an array of values that can be passed to the default\n  # initializer method.\n  PCORE_VALUE_KEY = '__pvalue'\n\n  # Type key used for hashes that contain keys that are not of type String\n  PCORE_TYPE_HASH = 'Hash'\n\n  # Type key used for symbols\n  PCORE_TYPE_SENSITIVE = 'Sensitive'\n  PCORE_TYPE_BINARY = 'Binary'\n\n  # Type key used for symbols\n  PCORE_TYPE_SYMBOL = 'Symbol'\n\n  # Type key used for Default\n  PCORE_TYPE_DEFAULT = 'Default'\n\n  # Type key used for document local references\n  PCORE_LOCAL_REF_SYMBOL = 'LocalRef'\nend\nend\n\nrequire_relative 'serialization/json_path'\nrequire_relative 'serialization/from_data_converter'\nrequire_relative 'serialization/to_data_converter'\nrequire_relative 'serialization/to_stringified_converter'\nrequire_relative 'serialization/serializer'\nrequire_relative 'serialization/deserializer'\nrequire_relative 'serialization/json'\nrequire_relative 'serialization/time_factory'\nrequire_relative 'serialization/object'\n"
  },
  {
    "path": "lib/puppet/pops/time/timespan.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Time\n  NSECS_PER_USEC = 1000\n  NSECS_PER_MSEC = NSECS_PER_USEC * 1000\n  NSECS_PER_SEC  = NSECS_PER_MSEC * 1000\n  NSECS_PER_MIN  = NSECS_PER_SEC  * 60\n  NSECS_PER_HOUR = NSECS_PER_MIN  * 60\n  NSECS_PER_DAY  = NSECS_PER_HOUR * 24\n\n  KEY_STRING = 'string'\n  KEY_FORMAT = 'format'\n  KEY_NEGATIVE = 'negative'\n  KEY_DAYS = 'days'\n  KEY_HOURS = 'hours'\n  KEY_MINUTES = 'minutes'\n  KEY_SECONDS = 'seconds'\n  KEY_MILLISECONDS = 'milliseconds'\n  KEY_MICROSECONDS = 'microseconds'\n  KEY_NANOSECONDS = 'nanoseconds'\n\n  # TimeData is a Numeric that stores its value internally as nano-seconds but will be considered to be seconds and fractions of\n  # seconds when used in arithmetic or comparison with other Numeric types.\n  #\n  class TimeData < Numeric\n    include LabelProvider\n\n    attr_reader :nsecs\n\n    def initialize(nanoseconds)\n      @nsecs = nanoseconds\n    end\n\n    def <=>(o)\n      case o\n      when self.class\n        @nsecs <=> o.nsecs\n      when Integer\n        to_int <=> o\n      when Float\n        to_f <=> o\n      else\n        nil\n      end\n    end\n\n    def label(o)\n      Utils.name_to_segments(o.class.name).last\n    end\n\n    # @return [Float] the number of seconds\n    def to_f\n      @nsecs.fdiv(NSECS_PER_SEC)\n    end\n\n    # @return [Integer] the number of seconds with fraction part truncated\n    def to_int\n      @nsecs / NSECS_PER_SEC\n    end\n\n    def to_i\n      to_int\n    end\n\n    # @return [Complex] short for `#to_f.to_c`\n    def to_c\n      to_f.to_c\n    end\n\n    # @return [Rational] initial numerator is nano-seconds and denominator is nano-seconds per second\n    def to_r\n      Rational(@nsecs, NSECS_PER_SEC)\n    end\n\n    undef_method :phase, :polar, :rect, :rectangular\n  end\n\n  class Timespan < TimeData\n    def self.from_fields(negative, days, hours, minutes, seconds, milliseconds = 0, microseconds = 0, nanoseconds = 0)\n      ns = (((((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000 + milliseconds) * 1000 + microseconds) * 1000 + nanoseconds\n      new(negative ? -ns : ns)\n    end\n\n    def self.from_hash(hash)\n      hash.include?('string') ? from_string_hash(hash) : from_fields_hash(hash)\n    end\n\n    def self.from_string_hash(hash)\n      parse(hash[KEY_STRING], hash[KEY_FORMAT] || Format::DEFAULTS)\n    end\n\n    def self.from_fields_hash(hash)\n      from_fields(\n        hash[KEY_NEGATIVE] || false,\n        hash[KEY_DAYS] || 0,\n        hash[KEY_HOURS] || 0,\n        hash[KEY_MINUTES] || 0,\n        hash[KEY_SECONDS] || 0,\n        hash[KEY_MILLISECONDS] || 0,\n        hash[KEY_MICROSECONDS] || 0,\n        hash[KEY_NANOSECONDS] || 0\n      )\n    end\n\n    def self.parse(str, format = Format::DEFAULTS)\n      if format.is_a?(::Array)\n        format.each do |fmt|\n          fmt = FormatParser.singleton.parse_format(fmt) unless fmt.is_a?(Format)\n          begin\n            return fmt.parse(str)\n          rescue ArgumentError\n          end\n        end\n        raise ArgumentError, _(\"Unable to parse '%{str}' using any of the formats %{formats}\") % { str: str, formats: format.join(', ') }\n      end\n      format = FormatParser.singleton.parse_format(format) unless format.is_a?(Format)\n      format.parse(str)\n    end\n\n    # @return [true] if the stored value is negative\n    def negative?\n      @nsecs < 0\n    end\n\n    def +(o)\n      case o\n      when Timestamp\n        Timestamp.new(@nsecs + o.nsecs)\n      when Timespan\n        Timespan.new(@nsecs + o.nsecs)\n      when Integer, Float\n        # Add seconds\n        Timespan.new(@nsecs + (o * NSECS_PER_SEC).to_i)\n      else\n        raise ArgumentError, _(\"%{klass} cannot be added to a Timespan\") % { klass: a_an_uc(o) } unless o.is_a?(Timespan)\n      end\n    end\n\n    def -(o)\n      case o\n      when Timespan\n        Timespan.new(@nsecs - o.nsecs)\n      when Integer, Float\n        # Subtract seconds\n        Timespan.new(@nsecs - (o * NSECS_PER_SEC).to_i)\n      else\n        raise ArgumentError, _(\"%{klass} cannot be subtracted from a Timespan\") % { klass: a_an_uc(o) }\n      end\n    end\n\n    def -@\n      Timespan.new(-@nsecs)\n    end\n\n    def *(o)\n      case o\n      when Integer, Float\n        Timespan.new((@nsecs * o).to_i)\n      else\n        raise ArgumentError, _(\"A Timestamp cannot be multiplied by %{klass}\") % { klass: a_an(o) }\n      end\n    end\n\n    def divmod(o)\n      case o\n      when Integer\n        to_i.divmod(o)\n      when Float\n        to_f.divmod(o)\n      else\n        raise ArgumentError, _(\"Can not do modulus on a Timespan using a %{klass}\") % { klass: a_an(o) }\n      end\n    end\n\n    def modulo(o)\n      divmod(o)[1]\n    end\n\n    def %(o)\n      modulo(o)\n    end\n\n    def div(o)\n      case o\n      when Timespan\n        # Timespan/Timespan yields a Float\n        @nsecs.fdiv(o.nsecs)\n      when Integer, Float\n        Timespan.new(@nsecs.div(o))\n      else\n        raise ArgumentError, _(\"A Timespan cannot be divided by %{klass}\") % { klass: a_an(o) }\n      end\n    end\n\n    def /(o)\n      div(o)\n    end\n\n    # @return [Integer] a positive integer denoting the number of days\n    def days\n      total_days\n    end\n\n    # @return [Integer] a positive integer, 0 - 23 denoting hours of day\n    def hours\n      total_hours % 24\n    end\n\n    # @return [Integer] a positive integer, 0 - 59 denoting minutes of hour\n    def minutes\n      total_minutes % 60\n    end\n\n    # @return [Integer] a positive integer, 0 - 59 denoting seconds of minute\n    def seconds\n      total_seconds % 60\n    end\n\n    # @return [Integer] a positive integer, 0 - 999 denoting milliseconds of second\n    def milliseconds\n      total_milliseconds % 1000\n    end\n\n    # @return [Integer] a positive integer, 0 - 999.999.999 denoting nanoseconds of second\n    def nanoseconds\n      total_nanoseconds % NSECS_PER_SEC\n    end\n\n    # Formats this timestamp into a string according to the given `format`\n    #\n    # @param [String,Format] format The format to use when producing the string\n    # @return [String] the string representing the formatted timestamp\n    # @raise [ArgumentError] if the format is a string with illegal format characters\n    # @api public\n    def format(format)\n      format = FormatParser.singleton.parse_format(format) unless format.is_a?(Format)\n      format.format(self)\n    end\n\n    # Formats this timestamp into a string according to {Format::DEFAULTS[0]}\n    #\n    # @return [String] the string representing the formatted timestamp\n    # @api public\n    def to_s\n      format(Format::DEFAULTS[0])\n    end\n\n    def to_hash(compact = false)\n      result = {}\n      n = nanoseconds\n      if compact\n        s = total_seconds\n        result[KEY_SECONDS] = negative? ? -s : s\n        result[KEY_NANOSECONDS] = negative? ? -n : n unless n == 0\n      else\n        add_unless_zero(result, KEY_DAYS, days)\n        add_unless_zero(result, KEY_HOURS, hours)\n        add_unless_zero(result, KEY_MINUTES, minutes)\n        add_unless_zero(result, KEY_SECONDS, seconds)\n        unless n == 0\n          add_unless_zero(result, KEY_NANOSECONDS, n % 1000)\n          n /= 1000\n          add_unless_zero(result, KEY_MICROSECONDS, n % 1000)\n          add_unless_zero(result, KEY_MILLISECONDS, n / 1000)\n        end\n        result[KEY_NEGATIVE] = true if negative?\n      end\n      result\n    end\n\n    def add_unless_zero(result, key, value)\n      result[key] = value unless value == 0\n    end\n    private :add_unless_zero\n\n    # @api private\n    def total_days\n      total_nanoseconds / NSECS_PER_DAY\n    end\n\n    # @api private\n    def total_hours\n      total_nanoseconds / NSECS_PER_HOUR\n    end\n\n    # @api private\n    def total_minutes\n      total_nanoseconds / NSECS_PER_MIN\n    end\n\n    # @api private\n    def total_seconds\n      total_nanoseconds / NSECS_PER_SEC\n    end\n\n    # @api private\n    def total_milliseconds\n      total_nanoseconds / NSECS_PER_MSEC\n    end\n\n    # @api private\n    def total_microseconds\n      total_nanoseconds / NSECS_PER_USEC\n    end\n\n    # @api private\n    def total_nanoseconds\n      @nsecs.abs\n    end\n\n    # Represents a compiled Timestamp format. The format is used both when parsing a timestamp\n    # in string format and when producing a string from a timestamp instance.\n    #\n    class Format\n      # A segment is either a string that will be represented literally in the formatted timestamp\n      # or a value that corresponds to one of the possible format characters.\n      class Segment\n        def append_to(bld, ts)\n          raise NotImplementedError, \"'#{self.class.name}' should implement #append_to\"\n        end\n\n        def append_regexp(bld, ts)\n          raise NotImplementedError, \"'#{self.class.name}' should implement #append_regexp\"\n        end\n\n        def multiplier\n          raise NotImplementedError, \"'#{self.class.name}' should implement #multiplier\"\n        end\n      end\n\n      class LiteralSegment < Segment\n        def append_regexp(bld)\n          bld << \"(#{Regexp.escape(@literal)})\"\n        end\n\n        def initialize(literal)\n          @literal = literal\n        end\n\n        def append_to(bld, ts)\n          bld << @literal\n        end\n\n        def concat(codepoint)\n          @literal.concat(codepoint)\n        end\n\n        def nanoseconds\n          0\n        end\n      end\n\n      class ValueSegment < Segment\n        def initialize(padchar, width, default_width)\n          @use_total = false\n          @padchar = padchar\n          @width = width\n          @default_width = default_width\n          @format = create_format\n        end\n\n        def create_format\n          case @padchar\n          when nil\n            '%d'\n          when ' '\n            \"%#{@width || @default_width}d\"\n          else\n            \"%#{@padchar}#{@width || @default_width}d\"\n          end\n        end\n\n        def append_regexp(bld)\n          if @width.nil?\n            case @padchar\n            when nil\n              bld << (use_total? ? '([0-9]+)' : \"([0-9]{1,#{@default_width}})\")\n            when '0'\n              bld << (use_total? ? '([0-9]+)' : \"([0-9]{1,#{@default_width}})\")\n            else\n              bld << (use_total? ? '\\s*([0-9]+)' : \"([0-9\\\\s]{1,#{@default_width}})\")\n            end\n          else\n            case @padchar\n            when nil\n              bld << \"([0-9]{1,#{@width}})\"\n            when '0'\n              bld << \"([0-9]{#{@width}})\"\n            else\n              bld << \"([0-9\\\\s]{#{@width}})\"\n            end\n          end\n        end\n\n        def nanoseconds(group)\n          group.to_i * multiplier\n        end\n\n        def multiplier\n          0\n        end\n\n        def set_use_total\n          @use_total = true\n        end\n\n        def use_total?\n          @use_total\n        end\n\n        def append_value(bld, n)\n          bld << sprintf(@format, n)\n        end\n      end\n\n      class DaySegment < ValueSegment\n        def initialize(padchar, width)\n          super(padchar, width, 1)\n        end\n\n        def multiplier\n          NSECS_PER_DAY\n        end\n\n        def append_to(bld, ts)\n          append_value(bld, ts.days)\n        end\n      end\n\n      class HourSegment < ValueSegment\n        def initialize(padchar, width)\n          super(padchar, width, 2)\n        end\n\n        def multiplier\n          NSECS_PER_HOUR\n        end\n\n        def append_to(bld, ts)\n          append_value(bld, use_total? ? ts.total_hours : ts.hours)\n        end\n      end\n\n      class MinuteSegment < ValueSegment\n        def initialize(padchar, width)\n          super(padchar, width, 2)\n        end\n\n        def multiplier\n          NSECS_PER_MIN\n        end\n\n        def append_to(bld, ts)\n          append_value(bld, use_total? ? ts.total_minutes : ts.minutes)\n        end\n      end\n\n      class SecondSegment < ValueSegment\n        def initialize(padchar, width)\n          super(padchar, width, 2)\n        end\n\n        def multiplier\n          NSECS_PER_SEC\n        end\n\n        def append_to(bld, ts)\n          append_value(bld, use_total? ? ts.total_seconds : ts.seconds)\n        end\n      end\n\n      # Class that assumes that leading zeroes are significant and that trailing zeroes are not and left justifies when formatting.\n      # Applicable after a decimal point, and hence to the %L and %N formats.\n      class FragmentSegment < ValueSegment\n        def nanoseconds(group)\n          # Using %L or %N to parse a string only makes sense when they are considered to be fractions. Using them\n          # as a total quantity would introduce ambiguities.\n          raise ArgumentError, _('Format specifiers %L and %N denotes fractions and must be used together with a specifier of higher magnitude') if use_total?\n\n          n = group.to_i\n          p = 9 - group.length\n          p <= 0 ? n : n * 10**p\n        end\n\n        def create_format\n          if @padchar.nil?\n            '%d'\n          else\n            \"%-#{@width || @default_width}d\"\n          end\n        end\n\n        def append_value(bld, n)\n          # Strip trailing zeroes when default format is used\n          n = n.to_s.sub(/\\A([0-9]+?)0*\\z/, '\\1').to_i unless use_total? || @padchar == '0'\n          super(bld, n)\n        end\n      end\n\n      class MilliSecondSegment < FragmentSegment\n        def initialize(padchar, width)\n          super(padchar, width, 3)\n        end\n\n        def multiplier\n          NSECS_PER_MSEC\n        end\n\n        def append_to(bld, ts)\n          append_value(bld, use_total? ? ts.total_milliseconds : ts.milliseconds)\n        end\n      end\n\n      class NanoSecondSegment < FragmentSegment\n        def initialize(padchar, width)\n          super(padchar, width, 9)\n        end\n\n        def multiplier\n          width = @width || @default_width\n          if width < 9\n            10**(9 - width)\n          else\n            1\n          end\n        end\n\n        def append_to(bld, ts)\n          ns = ts.total_nanoseconds\n          width = @width || @default_width\n          if width < 9\n            # Truncate digits to the right, i.e. let %6N reflect microseconds\n            ns /= 10**(9 - width)\n            ns %= 10**width unless use_total?\n          else\n            ns %= NSECS_PER_SEC unless use_total?\n          end\n          append_value(bld, ns)\n        end\n      end\n\n      def initialize(format, segments)\n        @format = format.freeze\n        @segments = segments.freeze\n      end\n\n      def format(timespan)\n        bld = timespan.negative? ? '-'.dup : ''.dup\n        @segments.each { |segment| segment.append_to(bld, timespan) }\n        bld\n      end\n\n      def parse(timespan)\n        md = regexp.match(timespan)\n        raise ArgumentError, _(\"Unable to parse '%{timespan}' using format '%{format}'\") % { timespan: timespan, format: @format } if md.nil?\n\n        nanoseconds = 0\n        md.captures.each_with_index do |group, index|\n          segment = @segments[index]\n          next if segment.is_a?(LiteralSegment)\n\n          group.lstrip!\n          raise ArgumentError, _(\"Unable to parse '%{timespan}' using format '%{format}'\") % { timespan: timespan, format: @format } unless group =~ /\\A[0-9]+\\z/\n\n          nanoseconds += segment.nanoseconds(group)\n        end\n        Timespan.new(timespan.start_with?('-') ? -nanoseconds : nanoseconds)\n      end\n\n      def to_s\n        @format\n      end\n\n      private\n\n      def regexp\n        @regexp ||= build_regexp\n      end\n\n      def build_regexp\n        bld = '\\A-?'.dup\n        @segments.each { |segment| segment.append_regexp(bld) }\n        bld << '\\z'\n        Regexp.new(bld)\n      end\n    end\n\n    # Parses a string into a Timestamp::Format instance\n    class FormatParser\n      extend Puppet::Concurrent::ThreadLocalSingleton\n\n      def initialize\n        @formats = Hash.new { |hash, str| hash[str] = internal_parse(str) }\n      end\n\n      def parse_format(format)\n        @formats[format]\n      end\n\n      private\n\n      NSEC_MAX = 0\n      MSEC_MAX = 1\n      SEC_MAX = 2\n      MIN_MAX = 3\n      HOUR_MAX = 4\n      DAY_MAX = 5\n\n      SEGMENT_CLASS_BY_ORDINAL = [\n        Format::NanoSecondSegment, Format::MilliSecondSegment, Format::SecondSegment, Format::MinuteSegment, Format::HourSegment, Format::DaySegment\n      ]\n\n      def bad_format_specifier(format, start, position)\n        _(\"Bad format specifier '%{expression}' in '%{format}', at position %{position}\") % { expression: format[start, position - start], format: format, position: position }\n      end\n\n      def append_literal(bld, codepoint)\n        if bld.empty? || !bld.last.is_a?(Format::LiteralSegment)\n          bld << Format::LiteralSegment.new(''.dup.concat(codepoint))\n        else\n          bld.last.concat(codepoint)\n        end\n      end\n\n      # States used by the #internal_parser function\n      STATE_LITERAL = 0 # expects literal or '%'\n      STATE_PAD = 1 # expects pad, width, or format character\n      STATE_WIDTH = 2 # expects width, or format character\n\n      def internal_parse(str)\n        bld = []\n        raise ArgumentError, _('Format must be a String') unless str.is_a?(String)\n\n        highest = -1\n        state = STATE_LITERAL\n        padchar = '0'\n        width = nil\n        position = -1\n        fstart = 0\n\n        str.each_codepoint do |codepoint|\n          position += 1\n          if state == STATE_LITERAL\n            if codepoint == 0x25 # '%'\n              state = STATE_PAD\n              fstart = position\n              padchar = '0'\n              width = nil\n            else\n              append_literal(bld, codepoint)\n            end\n            next\n          end\n\n          case codepoint\n          when 0x25 # '%'\n            append_literal(bld, codepoint)\n            state = STATE_LITERAL\n          when 0x2D # '-'\n            raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_PAD\n\n            padchar = nil\n            state = STATE_WIDTH\n          when 0x5F # '_'\n            raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_PAD\n\n            padchar = ' '\n            state = STATE_WIDTH\n          when 0x44 # 'D'\n            highest = DAY_MAX\n            bld << Format::DaySegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x48 # 'H'\n            highest = HOUR_MAX unless highest > HOUR_MAX\n            bld << Format::HourSegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x4D # 'M'\n            highest = MIN_MAX unless highest > MIN_MAX\n            bld << Format::MinuteSegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x53 # 'S'\n            highest = SEC_MAX unless highest > SEC_MAX\n            bld << Format::SecondSegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x4C # 'L'\n            highest = MSEC_MAX unless highest > MSEC_MAX\n            bld << Format::MilliSecondSegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x4E # 'N'\n            highest = NSEC_MAX unless highest > NSEC_MAX\n            bld << Format::NanoSecondSegment.new(padchar, width)\n            state = STATE_LITERAL\n          else # only digits allowed at this point\n            raise ArgumentError, bad_format_specifier(str, fstart, position) unless codepoint >= 0x30 && codepoint <= 0x39\n\n            if state == STATE_PAD && codepoint == 0x30\n              padchar = '0'\n            else\n              n = codepoint - 0x30\n              if width.nil?\n                width = n\n              else\n                width = width * 10 + n\n              end\n            end\n            state = STATE_WIDTH\n          end\n        end\n\n        raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_LITERAL\n\n        unless highest == -1\n          hc = SEGMENT_CLASS_BY_ORDINAL[highest]\n          bld.find { |s| s.instance_of?(hc) }.set_use_total\n        end\n        Format.new(str, bld)\n      end\n    end\n\n    class Format\n      DEFAULTS = ['%D-%H:%M:%S.%-N', '%H:%M:%S.%-N', '%M:%S.%-N', '%S.%-N', '%D-%H:%M:%S', '%H:%M:%S', '%D-%H:%M', '%S'].map { |str| FormatParser.singleton.parse_format(str) }\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/time/timestamp.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Time\nclass Timestamp < TimeData\n  DEFAULT_FORMATS_WO_TZ = ['%FT%T.%N', '%FT%T', '%F %T.%N', '%F %T', '%F']\n  DEFAULT_FORMATS = ['%FT%T.%N %Z', '%FT%T %Z', '%F %T.%N %Z', '%F %T %Z', '%F %Z'] + DEFAULT_FORMATS_WO_TZ\n\n  CURRENT_TIMEZONE = 'current'\n  KEY_TIMEZONE = 'timezone'\n\n  # Converts a timezone that strptime can parse using '%z' into '-HH:MM' or '+HH:MM'\n  # @param [String] tz the timezone to convert\n  # @return [String] the converted timezone\n  #\n  # @api private\n  def self.convert_timezone(tz)\n    if tz =~ /\\A[+-]\\d\\d:\\d\\d\\z/\n      tz\n    else\n      offset = utc_offset(tz) / 60\n      if offset < 0\n        offset = offset.abs\n        sprintf('-%2.2d:%2.2d', offset / 60, offset % 60)\n      else\n        sprintf('+%2.2d:%2.2d', offset / 60, offset % 60)\n      end\n    end\n  end\n\n  # Returns the zone offset from utc for the given `timezone`\n  # @param [String] timezone the timezone to get the offset for\n  # @return [Integer] the timezone offset, in seconds\n  #\n  # @api private\n  def self.utc_offset(timezone)\n    if CURRENT_TIMEZONE.casecmp(timezone) == 0\n      ::Time.now.utc_offset\n    else\n      hash = DateTime._strptime(timezone, '%z')\n      offset = hash.nil? ? nil : hash[:offset]\n      raise ArgumentError, _(\"Illegal timezone '%{timezone}'\") % { timezone: timezone } if offset.nil?\n\n      offset\n    end\n  end\n\n  # Formats a ruby Time object using the given timezone\n  def self.format_time(format, time, timezone)\n    unless timezone.nil? || timezone.empty?\n      time = time.localtime(convert_timezone(timezone))\n    end\n    time.strftime(format)\n  end\n\n  def self.now\n    from_time(::Time.now)\n  end\n\n  def self.from_time(t)\n    new(t.tv_sec * NSECS_PER_SEC + t.tv_nsec)\n  end\n\n  def self.from_hash(args_hash)\n    parse(args_hash[KEY_STRING], args_hash[KEY_FORMAT], args_hash[KEY_TIMEZONE])\n  end\n\n  def self.parse(str, format = :default, timezone = nil)\n    has_timezone = !(timezone.nil? || timezone.empty? || timezone == :default)\n    if format.nil? || format == :default\n      format = has_timezone ? DEFAULT_FORMATS_WO_TZ : DEFAULT_FORMATS\n    end\n\n    parsed = nil\n    if format.is_a?(Array)\n      format.each do |fmt|\n        parsed = DateTime._strptime(str, fmt)\n        next if parsed.nil?\n\n        if parsed.include?(:leftover) || (has_timezone && parsed.include?(:zone))\n          parsed = nil\n          next\n        end\n        break\n      end\n      if parsed.nil?\n        raise ArgumentError, _(\n          \"Unable to parse '%{str}' using any of the formats %{formats}\"\n        ) % { str: str, formats: format.join(', ') }\n      end\n    else\n      parsed = DateTime._strptime(str, format)\n      if parsed.nil? || parsed.include?(:leftover)\n        raise ArgumentError, _(\"Unable to parse '%{str}' using format '%{format}'\") % { str: str, format: format }\n      end\n\n      if has_timezone && parsed.include?(:zone)\n        raise ArgumentError, _(\n          'Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument'\n        )\n      end\n    end\n    unless has_timezone\n      timezone = parsed[:zone]\n      has_timezone = !timezone.nil?\n    end\n    fraction = parsed[:sec_fraction]\n\n    # Convert msec rational found in _strptime hash to usec\n    fraction *= 1_000_000 unless fraction.nil?\n\n    # Create the Time instance and adjust for timezone\n    parsed_time = ::Time.utc(parsed[:year], parsed[:mon], parsed[:mday], parsed[:hour], parsed[:min], parsed[:sec], fraction)\n    parsed_time -= utc_offset(timezone) if has_timezone\n\n    # Convert to Timestamp\n    from_time(parsed_time)\n  end\n\n  undef_method :-@, :+@, :div, :fdiv, :abs, :abs2, :magnitude # does not make sense on a Timestamp\n  if method_defined?(:negative?)\n    undef_method :negative?, :positive?\n  end\n  if method_defined?(:%)\n    undef_method :%, :modulo, :divmod\n  end\n\n  def +(o)\n    case o\n    when Timespan\n      Timestamp.new(@nsecs + o.nsecs)\n    when Integer, Float\n      Timestamp.new(@nsecs + (o * NSECS_PER_SEC).to_i)\n    else\n      raise ArgumentError, _(\"%{klass} cannot be added to a Timestamp\") % { klass: a_an_uc(o) }\n    end\n  end\n\n  def -(o)\n    case o\n    when Timestamp\n      # Diff between two timestamps is a timespan\n      Timespan.new(@nsecs - o.nsecs)\n    when Timespan\n      Timestamp.new(@nsecs - o.nsecs)\n    when Integer, Float\n      # Subtract seconds\n      Timestamp.new(@nsecs - (o * NSECS_PER_SEC).to_i)\n    else\n      raise ArgumentError, _(\"%{klass} cannot be subtracted from a Timestamp\") % { klass: a_an_uc(o) }\n    end\n  end\n\n  def format(format, timezone = nil)\n    self.class.format_time(format, to_time, timezone)\n  end\n\n  def to_s\n    format(DEFAULT_FORMATS[0])\n  end\n\n  def to_time\n    ::Time.at(to_r).utc\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/annotatable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\nKEY_ANNOTATIONS = 'annotations'\n\n# Behaviour common to all Pcore annotatable classes\n#\n# @api public\nmodule Annotatable\n  TYPE_ANNOTATIONS = PHashType.new(PTypeType.new(PTypeReferenceType.new('Annotation')), PHashType::DEFAULT)\n\n  # @return [{PTypeType => PStructType}] the map of annotations\n  # @api public\n  def annotations\n    @annotations.nil? ? EMPTY_HASH : @annotations\n  end\n\n  # @api private\n  def init_annotatable(init_hash)\n    @annotations = init_hash[KEY_ANNOTATIONS].freeze\n  end\n\n  # @api private\n  def annotatable_accept(visitor, guard)\n    @annotations.each_key { |key| key.accept(visitor, guard) } unless @annotations.nil?\n  end\n\n  # @api private\n  def _pcore_init_hash\n    result = {}\n    result[KEY_ANNOTATIONS] = @annotations unless @annotations.nil?\n    result\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/annotation.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n  # Pcore variant of the Adaptable::Adapter. Uses a Pcore Object type instead of a Class\n  class Annotation < Adaptable::Adapter\n    include Types::PuppetObject\n\n    CLEAR = 'clear'\n\n    # Register the Annotation type. This is the type that all custom Annotations will inherit from.\n    def self.register_ptype(loader, ir)\n      @type = Pcore.create_object_type(loader, ir, self, 'Annotation', nil, EMPTY_HASH)\n    end\n\n    def self._pcore_type\n      @type\n    end\n\n    # Finds an existing annotation for the given object and returns it.\n    # If no annotation was found, and a block is given, a new annotation is created from the\n    # initializer hash that must be returned from the block.\n    # If no annotation was found and no block is given, this method returns `nil`\n    #\n    # @param o [Object] object to annotate\n    # @param block [Proc] optional, evaluated when a new annotation must be created. Should return the init hash\n    # @return [Annotation<self>] an annotation of the same class as the receiver of the call\n    #\n    def self.annotate(o)\n      adapter = get(o)\n      if adapter.nil?\n        if o.is_a?(Annotatable)\n          init_hash = o.annotations[_pcore_type]\n          init_hash = yield if init_hash.nil? && block_given?\n        elsif block_given?\n          init_hash = yield\n        end\n        adapter = associate_adapter(_pcore_type.from_hash(init_hash), o) unless init_hash.nil?\n      end\n      adapter\n    end\n\n    # Forces the creation or removal of an annotation of this type.\n    # If `init_hash` is a hash, a new annotation is created and returned\n    # If `init_hash` is `nil`, then the annotation is cleared and the previous annotation is returned.\n    #\n    # @param o [Object] object to annotate\n    # @param init_hash [Hash{String,Object},nil] the initializer for the annotation or `nil` to clear the annotation\n    # @return [Annotation<self>] an annotation of the same class as the receiver of the call\n    #\n    def self.annotate_new(o, init_hash)\n      if o.is_a?(Annotatable) && o.annotations.include?(_pcore_type)\n        # Prevent clear or redefine of annotations declared on type\n        action = init_hash == CLEAR ? 'clear' : 'redefine'\n        raise ArgumentError, \"attempt to #{action} #{type_name} annotation declared on #{o.label}\"\n      end\n\n      if init_hash == CLEAR\n        clear(o)\n      else\n        associate_adapter(_pcore_type.from_hash(init_hash), o)\n      end\n    end\n\n    # Uses name of type instead of name of the class (the class is likely dynamically generated and as such,\n    # has no name)\n    # @return [String] the name of the type\n    def self.type_name\n      _pcore_type.name\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/class_loader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# The ClassLoader provides a Class instance given a class name or a meta-type.\n# If the class is not already loaded, it is loaded using the Puppet Autoloader.\n# This means it can load a class from a gem, or from puppet modules.\n#\nclass ClassLoader\n  @autoloader = Puppet::Util::Autoload.new(\"ClassLoader\", \"\")\n\n  # Returns a Class given a fully qualified class name.\n  # Lookup of class is never relative to the calling namespace.\n  # @param name [String, Array<String>, Array<Symbol>, PAnyType] A fully qualified\n  #   class name String (e.g. '::Foo::Bar', 'Foo::Bar'), a PAnyType, or a fully qualified name in Array form where each part\n  #   is either a String or a Symbol, e.g. `%w{Puppetx Puppetlabs SomeExtension}`.\n  # @return [Class, nil] the looked up class or nil if no such class is loaded\n  # @raise ArgumentError If the given argument has the wrong type\n  # @api public\n  #\n  def self.provide(name)\n    case name\n    when String\n      provide_from_string(name)\n\n    when Array\n      provide_from_name_path(name.join('::'), name)\n\n    when PAnyType, PTypeType\n      provide_from_type(name)\n\n    else\n      raise ArgumentError, \"Cannot provide a class from a '#{name.class.name}'\"\n    end\n  end\n\n  def self.provide_from_type(type)\n    case type\n    when PRuntimeType\n      raise ArgumentError, \"Only Runtime type 'ruby' is supported, got #{type.runtime}\" unless type.runtime == :ruby\n\n      provide_from_string(type.runtime_type_name)\n\n    when PBooleanType\n      # There is no other thing to load except this Enum meta type\n      RGen::MetamodelBuilder::MMBase::Boolean\n\n    when PTypeType\n      # TODO: PTypeType should have a type argument (a PAnyType) so the Class' class could be returned\n      #       (but this only matters in special circumstances when meta programming has been used).\n      Class\n\n    when POptionalType\n      # cannot make a distinction between optional and its type\n      provide_from_type(type.optional_type)\n\n    # Although not expected to be the first choice for getting a concrete class for these\n    # types, these are of value if the calling logic just has a reference to type.\n    # rubocop:disable Layout/SpaceBeforeSemicolon\n    when PArrayType    ; Array\n    when PTupleType    ; Array\n    when PHashType     ; Hash\n    when PStructType   ; Hash\n    when PRegexpType   ; Regexp\n    when PIntegerType  ; Integer\n    when PStringType   ; String\n    when PPatternType  ; String\n    when PEnumType     ; String\n    when PFloatType    ; Float\n    when PUndefType    ; NilClass\n    when PCallableType ; Proc\n    # rubocop:enable Layout/SpaceBeforeSemicolon\n    else\n      nil\n    end\n  end\n  private_class_method :provide_from_type\n\n  def self.provide_from_string(name)\n    name_path = name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n    # always from the root, so remove an empty first segment\n    name_path.shift if name_path[0].empty?\n    provide_from_name_path(name, name_path)\n  end\n\n  def self.provide_from_name_path(name, name_path)\n    # If class is already loaded, try this first\n    result = find_class(name_path)\n\n    unless result.is_a?(Module)\n      # Attempt to load it using the auto loader\n      loaded_path = nil\n      if paths_for_name(name_path).find { |path| loaded_path = path; @autoloader.load(path, Puppet.lookup(:current_environment)) }\n        result = find_class(name_path)\n        unless result.is_a?(Module)\n          raise RuntimeError, \"Loading of #{name} using relative path: '#{loaded_path}' did not create expected class\"\n        end\n      end\n    end\n    return nil unless result.is_a?(Module)\n\n    result\n  end\n  private_class_method :provide_from_string\n\n  def self.find_class(name_path)\n    name_path.reduce(Object) do |ns, name|\n      ns.const_get(name, false) # don't search ancestors\n    rescue NameError\n      return nil\n    end\n  end\n  private_class_method :find_class\n\n  def self.paths_for_name(fq_named_parts)\n    # search two entries, one where all parts are decamelized, and one with names just downcased\n    # TODO:this is not perfect - it will not produce the correct mix if a mix of styles are used\n    # The alternative is to test many additional paths.\n    #\n    [fq_named_parts.map { |part| de_camel(part) }.join('/'), fq_named_parts.join('/').downcase]\n  end\n  private_class_method :paths_for_name\n\n  def self.de_camel(fq_name)\n    fq_name.to_s.gsub(/::/, '/')\n           .gsub(/([A-Z]+)([A-Z][a-z])/, '\\1_\\2')\n           .gsub(/([a-z\\d])([A-Z])/, '\\1_\\2')\n           .tr(\"-\", \"_\")\n           .downcase\n  end\n  private_class_method :de_camel\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/implementation_registry.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n  # The {ImplementationRegistry} maps names types in the Puppet Type System to names of corresponding implementation\n  # modules/classes. Each mapping is unique and bidirectional so that for any given type name there is only one\n  # implementation and vice versa.\n  #\n  # @api private\n  class ImplementationRegistry\n    TYPE_REGEXP_SUBST = TypeFactory.tuple([PRegexpType::DEFAULT, PStringType::NON_EMPTY])\n\n    # Create a new instance. This method is normally only called once\n    #\n    # @param parent [ImplementationRegistry, nil] the parent of this registry\n    def initialize(parent = nil)\n      @parent = parent\n      @type_names_per_implementation = {}\n      @implementations_per_type_name = {}\n      @type_name_substitutions = []\n      @impl_name_substitutions = []\n    end\n\n    # Register a bidirectional type mapping.\n    #\n    # @overload register_type_mapping(runtime_type, puppet_type)\n    #   @param runtime_type [PRuntimeType] type that represents the runtime module or class to map to a puppet type\n    #   @param puppet_type [PAnyType] type that will be mapped to the runtime module or class\n    # @overload register_type_mapping(runtime_type, pattern_replacement)\n    #   @param runtime_type [PRuntimeType] type containing the pattern and replacement to map the runtime type to a puppet type\n    #   @param puppet_type [Array(Regexp,String)] the pattern and replacement to map a puppet type to a runtime type\n    def register_type_mapping(runtime_type, puppet_type_or_pattern, _ = nil)\n      TypeAsserter.assert_assignable('First argument of type mapping', PRuntimeType::RUBY, runtime_type)\n      expr = runtime_type.name_or_pattern\n      if expr.is_a?(Array)\n        TypeAsserter.assert_instance_of('Second argument of type mapping', TYPE_REGEXP_SUBST, puppet_type_or_pattern)\n        register_implementation_regexp(puppet_type_or_pattern, expr)\n      else\n        TypeAsserter.assert_instance_of('Second argument of type mapping', PTypeType::DEFAULT, puppet_type_or_pattern)\n        register_implementation(puppet_type_or_pattern, expr)\n      end\n    end\n\n    # Register a bidirectional namespace mapping\n    #\n    # @param type_namespace [String] the namespace for the puppet types\n    # @param impl_namespace [String] the namespace for the implementations\n    def register_implementation_namespace(type_namespace, impl_namespace, _ = nil)\n      ns = TypeFormatter::NAME_SEGMENT_SEPARATOR\n      register_implementation_regexp(\n        [/\\A#{type_namespace}#{ns}(\\w+)\\z/, \"#{impl_namespace}#{ns}\\\\1\"],\n        [/\\A#{impl_namespace}#{ns}(\\w+)\\z/, \"#{type_namespace}#{ns}\\\\1\"]\n      )\n    end\n\n    # Register a bidirectional regexp mapping\n    #\n    # @param type_name_subst [Array(Regexp,String)] regexp and replacement mapping type names to runtime names\n    # @param impl_name_subst [Array(Regexp,String)] regexp and replacement mapping runtime names to type names\n    def register_implementation_regexp(type_name_subst, impl_name_subst, _ = nil)\n      @type_name_substitutions << type_name_subst\n      @impl_name_substitutions << impl_name_subst\n      nil\n    end\n\n    # Register a bidirectional mapping between a type and an implementation\n    #\n    # @param type [PAnyType,String] the type or type name\n    # @param impl_module[Module,String] the module or module name\n    def register_implementation(type, impl_module, _ = nil)\n      type = type.name if type.is_a?(PAnyType)\n      impl_module = impl_module.name if impl_module.is_a?(Module)\n      @type_names_per_implementation[impl_module] = type\n      @implementations_per_type_name[type] = impl_module\n      nil\n    end\n\n    # Find the name for the module that corresponds to the given type or type name\n    #\n    # @param type [PAnyType,String] the name of the type\n    # @return [String,nil] the name of the implementation module, or `nil` if no mapping was found\n    def module_name_for_type(type)\n      type = type.name if type.is_a?(PAnyType)\n      name = @parent.module_name_for_type(type) unless @parent.nil?\n      name.nil? ? find_mapping(type, @implementations_per_type_name, @type_name_substitutions) : name\n    end\n\n    # Find the module that corresponds to the given type or type name\n    #\n    # @param type [PAnyType,String] the name of the type\n    # @return [Module,nil] the name of the implementation module, or `nil` if no mapping was found\n    def module_for_type(type)\n      name = module_name_for_type(type)\n      # TODO Shouldn't ClassLoader be module specific?\n      name.nil? ? nil : ClassLoader.provide(name)\n    end\n\n    # Find the type name and loader that corresponds to the given runtime module or module name\n    #\n    # @param impl_module [Module,String] the implementation class or class name\n    # @return [String,nil] the name of the type, or `nil` if no mapping was found\n    def type_name_for_module(impl_module)\n      impl_module = impl_module.name if impl_module.is_a?(Module)\n      name = @parent.type_name_for_module(impl_module) unless @parent.nil?\n      name.nil? ? find_mapping(impl_module, @type_names_per_implementation, @impl_name_substitutions) : name\n    end\n\n    # Find the name for, and then load, the type  that corresponds to the given runtime module or module name\n    # The method will return `nil` if no mapping is found, a TypeReference if a mapping was found but the\n    # loader didn't find the type, or the loaded type.\n    #\n    # @param impl_module [Module,String] the implementation class or class name\n    # @return [PAnyType,nil] the type, or `nil` if no mapping was found\n    def type_for_module(impl_module)\n      name = type_name_for_module(impl_module)\n      if name.nil?\n        nil\n      else\n        TypeParser.singleton.parse(name)\n      end\n    end\n\n    private\n\n    def find_mapping(name, names, substitutions)\n      found = names[name]\n      if found.nil?\n        substitutions.each do |subst|\n          substituted = name.sub(*subst)\n          return substituted unless substituted == name\n        end\n      end\n      found\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/iterable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Types\n  # Implemented by classes that can produce an iterator to iterate over their contents\n  module IteratorProducer\n    def iterator\n      raise ArgumentError, 'iterator() is not implemented'\n    end\n  end\n\n  # The runtime Iterable type for an Iterable\n  module Iterable\n    # Produces an `Iterable` for one of the following types with the following characterstics:\n    #\n    # `String`       - yields each character in the string\n    # `Array`        - yields each element in the array\n    # `Hash`         - yields each key/value pair as a two element array\n    # `Integer`      - when positive, yields each value from zero to the given number\n    # `PIntegerType` - yields each element from min to max (inclusive) provided min < max and neither is unbounded.\n    # `PEnumtype`    - yields each possible value of the enum.\n    # `Range`        - yields an iterator for all elements in the range provided that the range start and end\n    #                  are both integers or both strings and start is less than end using natural ordering.\n    # `Dir`          - yields each name in the directory\n    #\n    # An `ArgumentError` is raised for all other objects.\n    #\n    # @param my_caller [Object] The calling object to reference in errors\n    # @param obj [Object] The object to produce an `Iterable` for\n    # @param infer_elements [Boolean] Whether or not to recursively infer all elements of obj. Optional\n    #\n    # @return [Iterable,nil] The produced `Iterable`\n    # @raise [ArgumentError] In case an `Iterable` cannot be produced\n    # @api public\n    def self.asserted_iterable(my_caller, obj, infer_elements = false)\n      iter = on(obj, nil, infer_elements)\n      raise ArgumentError, \"#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable.\" if iter.nil?\n\n      iter\n    end\n\n    # Produces an `Iterable` for one of the following types with the following characteristics:\n    #\n    # `String`       - yields each character in the string\n    # `Array`        - yields each element in the array\n    # `Hash`         - yields each key/value pair as a two element array\n    # `Integer`      - when positive, yields each value from zero to the given number\n    # `PIntegerType` - yields each element from min to max (inclusive) provided min < max and neither is unbounded.\n    # `PEnumtype`    - yields each possible value of the enum.\n    # `Range`        - yields an iterator for all elements in the range provided that the range start and end\n    #                  are both integers or both strings and start is less than end using natural ordering.\n    # `Dir`          - yields each name in the directory\n    #\n    # The value `nil` is returned for all other objects.\n    #\n    # @param o [Object] The object to produce an `Iterable` for\n    # @param element_type [PAnyType] the element type for the iterator. Optional\n    # @param infer_elements [Boolean] if element_type is nil, whether or not to recursively\n    #   infer types for the entire collection. Optional\n    #\n    # @return [Iterable,nil] The produced `Iterable` or `nil` if it couldn't be produced\n    #\n    # @api public\n    def self.on(o, element_type = nil, infer_elements = true)\n      case o\n      when IteratorProducer\n        o.iterator\n      when Iterable\n        o\n      when String\n        Iterator.new(PStringType.new(PIntegerType.new(1, 1)), o.each_char)\n      when Array\n        if o.empty?\n          Iterator.new(PUnitType::DEFAULT, o.each)\n        else\n          if element_type.nil? && infer_elements\n            tc = TypeCalculator.singleton\n            element_type = PVariantType.maybe_create(o.map { |e| tc.infer_set(e) })\n          end\n          Iterator.new(element_type, o.each)\n        end\n      when Hash\n        # Each element is a two element [key, value] tuple.\n        if o.empty?\n          HashIterator.new(PHashType::DEFAULT_KEY_PAIR_TUPLE, o.each)\n        else\n          if element_type.nil? && infer_elements\n            tc = TypeCalculator.singleton\n            element_type =\n              PTupleType\n              .new([\n                     PVariantType.maybe_create(o.keys.map { |e| tc.infer_set(e) }),\n                     PVariantType.maybe_create(o.values.map { |e| tc.infer_set(e) })\n                   ],\n                   PHashType::KEY_PAIR_TUPLE_SIZE)\n          end\n          HashIterator.new(element_type, o.each_pair)\n        end\n      when Integer\n        if o == 0\n          Iterator.new(PUnitType::DEFAULT, o.times)\n        elsif o > 0\n          IntegerRangeIterator.new(PIntegerType.new(0, o - 1))\n        else\n          nil\n        end\n      when PIntegerType\n        # a finite range will always produce at least one element since it's inclusive\n        o.finite_range? ? IntegerRangeIterator.new(o) : nil\n      when PEnumType\n        Iterator.new(o, o.values.each)\n      when PTypeAliasType\n        on(o.resolved_type)\n      when Range\n        min = o.min\n        max = o.max\n        if min.is_a?(Integer) && max.is_a?(Integer) && max >= min\n          IntegerRangeIterator.new(PIntegerType.new(min, max))\n        elsif min.is_a?(String) && max.is_a?(String) && max >= min\n          # A generalized element type where only the size is inferred is used here since inferring the full\n          # range might waste a lot of memory.\n          if min.length < max.length\n            shortest = min\n            longest = max\n          else\n            shortest = max\n            longest = min\n          end\n          Iterator.new(PStringType.new(PIntegerType.new(shortest.length, longest.length)), o.each)\n        else\n          # Unsupported range. It's either descending or nonsensical for other reasons (float, mixed types, etc.)\n          nil\n        end\n      else\n        # Not supported. We cannot determine the element type\n        nil\n      end\n    end\n\n    # Answers the question if there is an end to the iteration. Puppet does not currently provide any unbounded\n    # iterables.\n    #\n    # @return [Boolean] `true` if the iteration is unbounded\n    def self.unbounded?(object)\n      case object\n      when Iterable\n        object.unbounded?\n      when String, Integer, Array, Hash, Enumerator, PIntegerType, PEnumType, Dir\n        false\n      else\n        TypeAsserter.assert_instance_of('', PIterableType::DEFAULT, object, false)\n        !object.respond_to?(:size)\n      end\n    end\n\n    def each(&block)\n      step(1, &block)\n    end\n\n    def element_type\n      PAnyType::DEFAULT\n    end\n\n    def reverse_each(&block)\n      # Default implementation cannot propagate reverse_each to a new enumerator so chained\n      # calls must put reverse_each last.\n      raise ArgumentError, 'reverse_each() is not implemented'\n    end\n\n    def step(step, &block)\n      # Default implementation cannot propagate step to a new enumerator so chained\n      # calls must put stepping last.\n      raise ArgumentError, 'step() is not implemented'\n    end\n\n    def to_a\n      raise Puppet::Error, 'Attempt to create an Array from an unbounded Iterable' if unbounded?\n\n      super\n    end\n\n    def hash_style?\n      false\n    end\n\n    def unbounded?\n      true\n    end\n  end\n\n  # @api private\n  class Iterator\n    # Note! We do not include Enumerable module here since that would make this class respond\n    # in a bad way to all enumerable methods. We want to delegate all those calls directly to\n    # the contained @enumeration\n    include Iterable\n\n    def initialize(element_type, enumeration)\n      @element_type = element_type\n      @enumeration = enumeration\n    end\n\n    def element_type\n      @element_type\n    end\n\n    def size\n      @enumeration.size\n    end\n\n    def respond_to_missing?(name, include_private)\n      @enumeration.respond_to?(name, include_private)\n    end\n\n    def method_missing(name, *arguments, &block)\n      @enumeration.send(name, *arguments, &block)\n    end\n\n    def next\n      @enumeration.next\n    end\n\n    def map(*args, &block)\n      @enumeration.map(*args, &block)\n    end\n\n    def reduce(*args, &block)\n      @enumeration.reduce(*args, &block)\n    end\n\n    def all?(&block)\n      @enumeration.all?(&block)\n    end\n\n    def any?(&block)\n      @enumeration.any?(&block)\n    end\n\n    def step(step, &block)\n      raise ArgumentError if step <= 0\n\n      r = self\n      r = r.step_iterator(step) if step > 1\n\n      if block_given?\n        begin\n          if block.arity == 1\n            loop { yield(r.next) }\n          else\n            loop { yield(*r.next) }\n          end\n        rescue StopIteration\n        end\n        self\n      else\n        r\n      end\n    end\n\n    def reverse_each(&block)\n      r = Iterator.new(@element_type, @enumeration.reverse_each)\n      block_given? ? r.each(&block) : r\n    end\n\n    def step_iterator(step)\n      StepIterator.new(@element_type, self, step)\n    end\n\n    def to_s\n      et = element_type\n      et.nil? ? 'Iterator-Value' : \"Iterator[#{et.generalize}]-Value\"\n    end\n\n    def unbounded?\n      Iterable.unbounded?(@enumeration)\n    end\n  end\n\n  # Special iterator used when iterating over hashes. Returns `true` for `#hash_style?` so that\n  # it is possible to differentiate between two element arrays and key => value associations\n  class HashIterator < Iterator\n    def hash_style?\n      true\n    end\n  end\n\n  # @api private\n  class StepIterator < Iterator\n    include Enumerable\n\n    def initialize(element_type, enumeration, step_size)\n      super(element_type, enumeration)\n      raise ArgumentError if step_size <= 0\n\n      @step_size = step_size\n    end\n\n    def next\n      result = @enumeration.next\n      skip = @step_size - 1\n      if skip > 0\n        begin\n          skip.times { @enumeration.next }\n        rescue StopIteration\n        end\n      end\n      result\n    end\n\n    def reverse_each(&block)\n      r = Iterator.new(@element_type, to_a.reverse_each)\n      block_given? ? r.each(&block) : r\n    end\n\n    def size\n      super / @step_size\n    end\n  end\n\n  # @api private\n  class IntegerRangeIterator < Iterator\n    include Enumerable\n\n    def initialize(range, step = 1)\n      raise ArgumentError if step == 0\n\n      @range = range\n      @step_size = step\n      @current = (step < 0 ? range.to : range.from) - step\n    end\n\n    def element_type\n      @range\n    end\n\n    def next\n      value = @current + @step_size\n      if @step_size < 0\n        raise StopIteration if value < @range.from\n      elsif value > @range.to\n        raise StopIteration\n      end\n      @current = value\n    end\n\n    def reverse_each(&block)\n      r = IntegerRangeIterator.new(@range, -@step_size)\n      block_given? ? r.each(&block) : r\n    end\n\n    def size\n      (@range.to - @range.from) / @step_size.abs\n    end\n\n    def step_iterator(step)\n      # The step iterator must use a range that has its logical end truncated at an even step boundary. This will\n      # fulfil two objectives:\n      # 1. The element_type method should not report excessive integers as possible numbers\n      # 2. A reversed iterator must start at the correct number\n      #\n      range = @range\n      step = @step_size * step\n      mod = (range.to - range.from) % step\n      if mod < 0\n        range = PIntegerType.new(range.from - mod, range.to)\n      elsif mod > 0\n        range = PIntegerType.new(range.from, range.to - mod)\n      end\n      IntegerRangeIterator.new(range, step)\n    end\n\n    def unbounded?\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_binary_type.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'base64'\nmodule Puppet::Pops\nmodule Types\n# A Puppet Language Type that represents binary data content (a sequence of 8-bit bytes).\n# Instances of this data type can be created from `String` and `Array[Integer[0,255]]`\n# values. Also see the `binary_file` function for reading binary content from a file.\n#\n# A `Binary` can be converted to `String` and `Array` form - see function `new` for\n# the respective target data type for more information.\n#\n# Instances of this data type serialize as base 64 encoded strings when the serialization\n# format is textual, and as binary content when a serialization format supports this.\n#\n# @api public\nclass PBinaryType < PAnyType\n  # Represents a binary buffer\n  # @api public\n  class Binary\n    attr_reader :binary_buffer\n\n    # Constructs an instance of Binary from a base64 urlsafe encoded string (RFC 2045).\n    # @param [String] A string with RFC 2045 compliant encoded binary\n    #\n    def self.from_base64(str)\n      new(Base64.decode64(str))\n    end\n\n    # Constructs an instance of Binary from a base64 encoded string (RFC4648 with \"URL and Filename\n    # Safe Alphabet\" (That is with '-' instead of '+', and '_' instead of '/').\n    #\n    def self.from_base64_urlsafe(str)\n      new(Base64.urlsafe_decode64(str))\n    end\n\n    # Constructs an instance of Binary from a base64 strict encoded string (RFC 4648)\n    # Where correct padding must be used and line breaks causes an error to be raised.\n    #\n    # @param [String] A string with RFC 4648 compliant encoded binary\n    #\n    def self.from_base64_strict(str)\n      new(Base64.strict_decode64(str))\n    end\n\n    # Creates a new Binary from a String containing binary data. If the string's encoding\n    # is not already ASCII-8BIT, a copy of the string is force encoded as ASCII-8BIT (that is Ruby's \"binary\" format).\n    # This means that this binary will have the exact same content, but the string will considered\n    # to hold a sequence of bytes in the range 0 to 255.\n    #\n    # The given string will be frozen as a side effect if it is in ASCII-8BIT encoding. If this is not\n    # wanted, a copy should be given to this method.\n    #\n    # @param [String] A string with binary data\n    # @api public\n    #\n    def self.from_binary_string(bin)\n      new(bin)\n    end\n\n    # Creates a new Binary from a String containing text/binary in its given encoding. If the string's encoding\n    # is not already UTF-8, the string is first transcoded to UTF-8.\n    # This means that this binary will have the UTF-8 byte representation of the original string.\n    # For this to be valid, the encoding used in the given string must be valid.\n    # The validity of the given string is therefore asserted.\n    #\n    # The given string will be frozen as a side effect if it is in ASCII-8BIT encoding. If this is not\n    # wanted, a copy should be given to this method.\n    #\n    # @param [String] A string with valid content in its given encoding\n    # @return [Puppet::Pops::Types::PBinaryType::Binary] with the UTF-8 representation of the UTF-8 transcoded string\n    # @api public\n    #\n    def self.from_string(encoded_string)\n      enc = encoded_string.encoding.name\n      unless encoded_string.valid_encoding?\n        raise ArgumentError, _(\"The given string in encoding '%{enc}' is invalid. Cannot create a Binary UTF-8 representation\") % { enc: enc }\n      end\n\n      # Convert to UTF-8 (if not already UTF-8), and then to binary\n      encoded_string = (enc == \"UTF-8\") ? encoded_string.dup : encoded_string.encode('UTF-8')\n      encoded_string.force_encoding(\"ASCII-8BIT\")\n      new(encoded_string)\n    end\n\n    # Creates a new Binary from a String containing raw binary data of unknown encoding. If the string's encoding\n    # is not already ASCII-8BIT, a copy of the string is forced to ASCII-8BIT (that is Ruby's \"binary\" format).\n    # This means that this binary will have the exact same content, but the string will considered\n    # to hold a sequence of bytes in the range 0 to 255.\n    #\n    # @param [String] A string with binary data\n    # @api private\n    #\n    def initialize(bin)\n      @binary_buffer = (bin.encoding.name == \"ASCII-8BIT\" ? bin : bin.b).freeze\n    end\n\n    # Presents the binary content as a string base64 encoded string (without line breaks).\n    #\n    def to_s\n      Base64.strict_encode64(@binary_buffer)\n    end\n\n    # Returns the binary content as a \"relaxed\" base64 (standard) encoding where\n    # the string is broken up with new lines.\n    def relaxed_to_s\n      Base64.encode64(@binary_buffer)\n    end\n\n    # Returns the binary content as a url safe base64 string (where + and / are replaced by - and _)\n    #\n    def urlsafe_to_s\n      Base64.urlsafe_encode64(@binary_buffer)\n    end\n\n    def hash\n      @binary_buffer.hash\n    end\n\n    def eql?(o)\n      self.class == o.class && @binary_buffer == o.binary_buffer\n    end\n\n    def ==(o)\n      eql?(o)\n    end\n\n    def length\n      @binary_buffer.length\n    end\n  end\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  # Only instances of Binary are instances of the PBinaryType\n  #\n  def instance?(o, guard = nil)\n    o.is_a?(Binary)\n  end\n\n  def eql?(o)\n    self.class == o.class\n  end\n\n  # Binary uses the strict base64 format as its string representation\n  # @return [TrueClass] true\n  def roundtrip_with_string?\n    true\n  end\n\n  # @api private\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_Binary, type.loader) do\n      local_types do\n        type 'ByteInteger = Integer[0,255]'\n        type 'Base64Format = Enum[\"%b\", \"%u\", \"%B\", \"%s\", \"%r\"]'\n        type 'StringHash = Struct[{value => String, \"format\" => Optional[Base64Format]}]'\n        type 'ArrayHash = Struct[{value => Array[ByteInteger]}]'\n        type 'BinaryArgsHash = Variant[StringHash, ArrayHash]'\n      end\n\n      # Creates a binary from a base64 encoded string in one of the formats %b, %u, %B, %s, or %r\n      dispatch :from_string do\n        param 'String', :str\n        optional_param 'Base64Format', :format\n      end\n\n      dispatch :from_array do\n        param 'Array[ByteInteger]', :byte_array\n      end\n\n      # Same as from_string, or from_array, but value and (for string) optional format are given in the form\n      # of a hash.\n      #\n      dispatch :from_hash do\n        param 'BinaryArgsHash', :hash_args\n      end\n\n      def from_string(str, format = nil)\n        format ||= '%B'\n        case format\n        when \"%b\"\n          # padding must be added for older rubies to avoid truncation\n          padding = '=' * (str.length % 3)\n          Binary.new(Base64.decode64(str + padding))\n\n        when \"%u\"\n          Binary.new(Base64.urlsafe_decode64(str))\n\n        when \"%B\"\n          Binary.new(Base64.strict_decode64(str))\n\n        when \"%s\"\n          Binary.from_string(str)\n\n        when \"%r\"\n          Binary.from_binary_string(str)\n\n        else\n          raise ArgumentError, \"Unsupported Base64 format '#{format}'\"\n        end\n      end\n\n      def from_array(array)\n        # The array is already known to have bytes in the range 0-255, or it is in error\n        # Without this pack C would produce weird results\n        Binary.from_binary_string(array.pack(\"C*\"))\n      end\n\n      def from_hash(hash)\n        case hash['value']\n        when Array\n          from_array(hash['value'])\n        when String\n          from_string(hash['value'], hash['format'])\n        end\n      end\n    end\n  end\n\n  DEFAULT = PBinaryType.new\n\n  protected\n\n  def _assignable?(o, guard)\n    o.instance_of?(self.class)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_init_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# @api public\nclass PInitType < PTypeWithContainedType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 },\n                 'init_args' => {\n                   KEY_TYPE => PArrayType::DEFAULT,\n                   KEY_VALUE => EMPTY_ARRAY\n                 })\n  end\n\n  attr_reader :init_args\n\n  def initialize(type, init_args)\n    super(type)\n    @init_args = init_args.nil? ? EMPTY_ARRAY : init_args\n\n    if type.nil?\n      raise ArgumentError, _('Init cannot be parameterized with an undefined type and additional arguments') unless @init_args.empty?\n\n      @initialized = true\n    else\n      @initialized = false\n    end\n  end\n\n  def instance?(o, guard = nil)\n    really_instance?(o, guard) == 1\n  end\n\n  # @api private\n  def really_instance?(o, guard = nil)\n    if @type.nil?\n      TypeFactory.rich_data.really_instance?(o)\n    else\n      assert_initialized\n      guarded_recursion(guard, 0) do |g|\n        v = @type.really_instance?(o, g)\n        if v < 1\n          if @single_type\n            s = @single_type.really_instance?(o, g)\n            v = s if s > v\n          end\n        end\n        if v < 1\n          if @other_type\n            s = @other_type.really_instance?(o, g)\n            s = @other_type.really_instance?([o], g) if s < 0 && @has_optional_single\n            v = s if s > v\n          end\n        end\n        v\n      end\n    end\n  end\n\n  def eql?(o)\n    super && @init_args == o.init_args\n  end\n\n  def hash\n    super ^ @init_args.hash\n  end\n\n  def new_function\n    return super if type.nil?\n\n    assert_initialized\n\n    target_type = type\n    single_type = @single_type\n    if @init_args.empty?\n      @new_function ||= Puppet::Functions.create_function(:new_Init, Puppet::Functions::InternalFunction) do\n        @target_type = target_type\n        @single_type = single_type\n\n        dispatch :from_array do\n          scope_param\n          param 'Array', :value\n        end\n\n        dispatch :create do\n          scope_param\n          param 'Any', :value\n        end\n\n        def self.create(scope, value, func)\n          func.call(scope, @target_type, value)\n        end\n\n        def self.from_array(scope, value, func)\n          # If there is a single argument that matches the array, then that gets priority over\n          # expanding the array into all arguments\n          if @single_type.instance?(value) || (@other_type && !@other_type.instance?(value) && @has_optional_single && @other_type.instance?([value]))\n            func.call(scope, @target_type, value)\n          else\n            func.call(scope, @target_type, *value)\n          end\n        end\n\n        def from_array(scope, value)\n          self.class.from_array(scope, value, loader.load(:function, 'new'))\n        end\n\n        def create(scope, value)\n          self.class.create(scope, value, loader.load(:function, 'new'))\n        end\n      end\n    else\n      init_args = @init_args\n      @new_function ||= Puppet::Functions.create_function(:new_Init, Puppet::Functions::InternalFunction) do\n        @target_type = target_type\n        @init_args = init_args\n\n        dispatch :create do\n          scope_param\n          param 'Any', :value\n        end\n\n        def self.create(scope, value, func)\n          func.call(scope, @target_type, value, *@init_args)\n        end\n\n        def create(scope, value)\n          self.class.create(scope, value, loader.load(:function, 'new'))\n        end\n      end\n    end\n  end\n\n  DEFAULT = PInitType.new(nil, EMPTY_ARRAY)\n\n  EXACTLY_ONE = [1, 1].freeze\n\n  def assert_initialized\n    return self if @initialized\n\n    @initialized = true\n    @self_recursion = true\n\n    begin\n      # Filter out types that will provide a new_function but are unsuitable to be contained in Init\n      #\n      # Calling Init#new would cause endless recursion\n      # The Optional is the same as Variant[T,Undef].\n      # The NotUndef is not meaningful to create instances of\n      if @type.instance_of?(PInitType) || @type.instance_of?(POptionalType) || @type.instance_of?(PNotUndefType)\n        raise ArgumentError\n      end\n\n      new_func = @type.new_function\n    rescue ArgumentError\n      raise ArgumentError, _(\"Creation of new instance of type '%{type_name}' is not supported\") % { type_name: @type.to_s }\n    end\n    param_tuples = new_func.dispatcher.signatures.map { |closure| closure.type.param_types }\n\n    # An instance of the contained type is always a match to this type.\n    single_types = [@type]\n\n    if @init_args.empty?\n      # A value that is assignable to the type of a single parameter is also a match\n      single_tuples, other_tuples = param_tuples.partition { |tuple| EXACTLY_ONE == tuple.size_range }\n      single_types.concat(single_tuples.map { |tuple| tuple.types[0] })\n    else\n      tc = TypeCalculator.singleton\n      init_arg_types = @init_args.map { |arg| tc.infer_set(arg) }\n      arg_count = 1 + init_arg_types.size\n\n      # disqualify all parameter tuples that doesn't allow one value (type unknown at ths stage) + init args.\n      param_tuples = param_tuples.select do |tuple|\n        min, max = tuple.size_range\n        if arg_count >= min && arg_count <= max\n          # Aside from the first parameter, does the other parameters match?\n          tuple.assignable?(PTupleType.new(tuple.types[0..0].concat(init_arg_types)))\n        else\n          false\n        end\n      end\n      if param_tuples.empty?\n        raise ArgumentError, _(\"The type '%{type}' does not represent a valid set of parameters for %{subject}.new()\") %\n                             { type: to_s, subject: @type.generalize.name }\n      end\n      single_types.concat(param_tuples.map { |tuple| tuple.types[0] })\n      other_tuples = EMPTY_ARRAY\n    end\n    @single_type = PVariantType.maybe_create(single_types)\n    unless other_tuples.empty?\n      @other_type = PVariantType.maybe_create(other_tuples)\n      @has_optional_single = other_tuples.any? { |tuple| tuple.size_range.min == 1 }\n    end\n\n    guard = RecursionGuard.new\n    accept(NoopTypeAcceptor::INSTANCE, guard)\n    @self_recursion = guard.recursive_this?(self)\n  end\n\n  def accept(visitor, guard)\n    guarded_recursion(guard, nil) do |g|\n      super(visitor, g)\n      @single_type.accept(visitor, guard) if @single_type\n      @other_type.accept(visitor, guard) if @other_type\n    end\n  end\n\n  protected\n\n  def _assignable?(o, guard)\n    guarded_recursion(guard, false) do |g|\n      assert_initialized\n      if o.is_a?(PInitType)\n        @type.nil? || @type.assignable?(o.type, g)\n      elsif @type.nil?\n        TypeFactory.rich_data.assignable?(o, g)\n      else\n        @type.assignable?(o, g) ||\n          @single_type && @single_type.assignable?(o, g) ||\n          @other_type && (@other_type.assignable?(o, g) || @has_optional_single && @other_type.assignable?(PTupleType.new([o])))\n      end\n    end\n  end\n\n  private\n\n  def guarded_recursion(guard, dflt)\n    if @self_recursion\n      guard ||= RecursionGuard.new\n      guard.with_this(self) { |state| (state & RecursionGuard::SELF_RECURSION_IN_THIS) == 0 ? yield(guard) : dflt }\n    else\n      yield(guard)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_meta_type.rb",
    "content": "# frozen_string_literal: true\n\n# @abstract base class for PObjectType and other types that implements lazy evaluation of content\n# @api private\nmodule Puppet::Pops\nmodule Types\nKEY_NAME = 'name'\nKEY_TYPE = 'type'\nKEY_VALUE = 'value'\n\nclass PMetaType < PAnyType\n  include Annotatable\n\n  attr_reader :loader\n\n  def self.register_ptype(loader, ir)\n    # Abstract type. It doesn't register anything\n  end\n\n  def accept(visitor, guard)\n    annotatable_accept(visitor, guard)\n    super\n  end\n\n  def instance?(o, guard = nil)\n    raise NotImplementedError, \"Subclass of PMetaType should implement 'instance?'\"\n  end\n\n  # Called from the TypeParser once it has found a type using the Loader. The TypeParser will\n  # interpret the contained expression and the resolved type is remembered. This method also\n  # checks and remembers if the resolve type contains self recursion.\n  #\n  # @param type_parser [TypeParser] type parser that will interpret the type expression\n  # @param loader [Loader::Loader] loader to use when loading type aliases\n  # @return [PTypeAliasType] the receiver of the call, i.e. `self`\n  # @api private\n  def resolve(loader)\n    unless @init_hash_expression.nil?\n      @loader = loader\n      @self_recursion = true # assumed while it being found out below\n\n      init_hash_expression = @init_hash_expression\n      @init_hash_expression = nil\n      if init_hash_expression.is_a?(Model::LiteralHash)\n        init_hash = resolve_literal_hash(loader, init_hash_expression)\n      else\n        init_hash = resolve_hash(loader, init_hash_expression)\n      end\n      _pcore_init_from_hash(init_hash)\n\n      # Find out if this type is recursive. A recursive type has performance implications\n      # on several methods and this knowledge is used to avoid that for non-recursive\n      # types.\n      guard = RecursionGuard.new\n      accept(NoopTypeAcceptor::INSTANCE, guard)\n      @self_recursion = guard.recursive_this?(self)\n    end\n    self\n  end\n\n  def resolve_literal_hash(loader, init_hash_expression)\n    TypeParser.singleton.interpret_LiteralHash(init_hash_expression, loader)\n  end\n\n  def resolve_hash(loader, init_hash)\n    resolve_type_refs(loader, init_hash)\n  end\n\n  def resolve_type_refs(loader, o)\n    case o\n    when Hash\n      o.to_h { |k, v| [resolve_type_refs(loader, k), resolve_type_refs(loader, v)] }\n    when Array\n      o.map { |e| resolve_type_refs(loader, e) }\n    when PAnyType\n      o.resolve(loader)\n    else\n      o\n    end\n  end\n\n  def resolved?\n    @init_hash_expression.nil?\n  end\n\n  # Returns the expanded string the form of the alias, e.g. <alias name> = <resolved type>\n  #\n  # @return [String] the expanded form of this alias\n  # @api public\n  def to_s\n    TypeFormatter.singleton.alias_expanded_string(self)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_object_type.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'ruby_generator'\nrequire_relative 'type_with_members'\n\nmodule Puppet::Pops\nmodule Types\nKEY_ATTRIBUTES = 'attributes'\nKEY_CHECKS = 'checks'\nKEY_CONSTANTS = 'constants'\nKEY_EQUALITY = 'equality'\nKEY_EQUALITY_INCLUDE_TYPE = 'equality_include_type'\nKEY_FINAL = 'final'\nKEY_FUNCTIONS = 'functions'\nKEY_KIND = 'kind'\nKEY_OVERRIDE = 'override'\nKEY_PARENT = 'parent'\nKEY_TYPE_PARAMETERS = 'type_parameters'\n\n# @api public\nclass PObjectType < PMetaType\n  include TypeWithMembers\n\n  ATTRIBUTE_KIND_CONSTANT = 'constant'\n  ATTRIBUTE_KIND_DERIVED = 'derived'\n  ATTRIBUTE_KIND_GIVEN_OR_DERIVED = 'given_or_derived'\n  ATTRIBUTE_KIND_REFERENCE = 'reference'\n  TYPE_ATTRIBUTE_KIND = TypeFactory.enum(ATTRIBUTE_KIND_CONSTANT, ATTRIBUTE_KIND_DERIVED, ATTRIBUTE_KIND_GIVEN_OR_DERIVED, ATTRIBUTE_KIND_REFERENCE)\n\n  TYPE_OBJECT_NAME = Pcore::TYPE_QUALIFIED_REFERENCE\n\n  TYPE_ATTRIBUTE =\n    TypeFactory\n    .struct({\n              KEY_TYPE => PTypeType::DEFAULT,\n              TypeFactory.optional(KEY_FINAL) => PBooleanType::DEFAULT,\n              TypeFactory.optional(KEY_OVERRIDE) => PBooleanType::DEFAULT,\n              TypeFactory.optional(KEY_KIND) => TYPE_ATTRIBUTE_KIND,\n              KEY_VALUE => PAnyType::DEFAULT,\n              TypeFactory.optional(KEY_ANNOTATIONS) => TYPE_ANNOTATIONS\n            })\n\n  TYPE_PARAMETER =\n    TypeFactory\n    .struct({\n              KEY_TYPE => PTypeType::DEFAULT,\n              TypeFactory.optional(KEY_ANNOTATIONS) => TYPE_ANNOTATIONS\n            })\n\n  TYPE_CONSTANTS = TypeFactory.hash_kv(Pcore::TYPE_MEMBER_NAME, PAnyType::DEFAULT)\n  TYPE_ATTRIBUTES = TypeFactory.hash_kv(Pcore::TYPE_MEMBER_NAME, TypeFactory.not_undef)\n  TYPE_PARAMETERS = TypeFactory.hash_kv(Pcore::TYPE_MEMBER_NAME, TypeFactory.not_undef)\n  TYPE_ATTRIBUTE_CALLABLE = TypeFactory.callable(0, 0)\n\n  TYPE_FUNCTION_TYPE = PTypeType.new(PCallableType::DEFAULT)\n\n  TYPE_FUNCTION =\n    TypeFactory\n    .struct({\n              KEY_TYPE => TYPE_FUNCTION_TYPE,\n              TypeFactory.optional(KEY_FINAL) => PBooleanType::DEFAULT,\n              TypeFactory.optional(KEY_OVERRIDE) => PBooleanType::DEFAULT,\n              TypeFactory.optional(KEY_ANNOTATIONS) => TYPE_ANNOTATIONS\n            })\n\n  TYPE_FUNCTIONS = TypeFactory.hash_kv(PVariantType.new([Pcore::TYPE_MEMBER_NAME, PStringType.new('[]')]), TypeFactory.not_undef)\n\n  TYPE_EQUALITY = TypeFactory.variant(Pcore::TYPE_MEMBER_NAME, TypeFactory.array_of(Pcore::TYPE_MEMBER_NAME))\n\n  TYPE_CHECKS = PAnyType::DEFAULT # TBD\n\n  TYPE_OBJECT_I12N =\n    TypeFactory\n    .struct({\n              TypeFactory.optional(KEY_NAME) => TYPE_OBJECT_NAME,\n              TypeFactory.optional(KEY_PARENT) => PTypeType::DEFAULT,\n              TypeFactory.optional(KEY_TYPE_PARAMETERS) => TYPE_PARAMETERS,\n              TypeFactory.optional(KEY_ATTRIBUTES) => TYPE_ATTRIBUTES,\n              TypeFactory.optional(KEY_CONSTANTS) => TYPE_CONSTANTS,\n              TypeFactory.optional(KEY_FUNCTIONS) => TYPE_FUNCTIONS,\n              TypeFactory.optional(KEY_EQUALITY) => TYPE_EQUALITY,\n              TypeFactory.optional(KEY_EQUALITY_INCLUDE_TYPE) => PBooleanType::DEFAULT,\n              TypeFactory.optional(KEY_CHECKS) => TYPE_CHECKS,\n              TypeFactory.optional(KEY_ANNOTATIONS) => TYPE_ANNOTATIONS\n            })\n\n  def self.register_ptype(loader, ir)\n    type = create_ptype(loader, ir, 'AnyType', '_pcore_init_hash' => TYPE_OBJECT_I12N)\n\n    # Now, when the Object type exists, add annotations with keys derived from Annotation and freeze the types.\n    annotations = TypeFactory.optional(PHashType.new(PTypeType.new(Annotation._pcore_type), TypeFactory.hash_kv(Pcore::TYPE_MEMBER_NAME, PAnyType::DEFAULT)))\n    TYPE_ATTRIBUTE.hashed_elements[KEY_ANNOTATIONS].replace_value_type(annotations)\n    TYPE_FUNCTION.hashed_elements[KEY_ANNOTATIONS].replace_value_type(annotations)\n    TYPE_OBJECT_I12N.hashed_elements[KEY_ANNOTATIONS].replace_value_type(annotations)\n    PTypeSetType::TYPE_TYPESET_I12N.hashed_elements[KEY_ANNOTATIONS].replace_value_type(annotations)\n    PTypeSetType::TYPE_TYPE_REFERENCE_I12N.hashed_elements[KEY_ANNOTATIONS].replace_value_type(annotations)\n    type\n  end\n\n  # @abstract Encapsulates behavior common to {PAttribute} and {PFunction}\n  # @api public\n  class PAnnotatedMember\n    include Annotatable\n    include InvocableMember\n\n    # @return [PObjectType] the object type containing this member\n    # @api public\n    attr_reader :container\n\n    # @return [String] the name of this member\n    # @api public\n    attr_reader :name\n\n    # @return [PAnyType] the type of this member\n    # @api public\n    attr_reader :type\n\n    # @param name [String] The name of the member\n    # @param container [PObjectType] The containing object type\n    # @param init_hash [Hash{String=>Object}] Hash containing feature options\n    # @option init_hash [PAnyType] 'type' The member type (required)\n    # @option init_hash [Boolean] 'override' `true` if this feature must override an inherited feature. Default is `false`.\n    # @option init_hash [Boolean] 'final' `true` if this feature cannot be overridden. Default is `false`.\n    # @option init_hash [Hash{PTypeType => Hash}] 'annotations' Annotations hash. Default is `nil`.\n    # @api public\n    def initialize(name, container, init_hash)\n      @name = name\n      @container = container\n      @type = init_hash[KEY_TYPE]\n      @override = init_hash[KEY_OVERRIDE]\n      @override = false if @override.nil?\n      @final = init_hash[KEY_FINAL]\n      @final = false if @final.nil?\n      init_annotatable(init_hash)\n    end\n\n    # Delegates to the contained type\n    # @param visitor [TypeAcceptor] the visitor\n    # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n    # @api public\n    def accept(visitor, guard)\n      annotatable_accept(visitor, guard)\n      @type.accept(visitor, guard)\n    end\n\n    # Checks if the this _member_ overrides an inherited member, and if so, that this member is declared with override = true and that\n    # the inherited member accepts to be overridden by this member.\n    #\n    # @param parent_members [Hash{String=>PAnnotatedMember}] the hash of inherited members\n    # @return [PAnnotatedMember] this instance\n    # @raises [Puppet::ParseError] if the assertion fails\n    # @api private\n    def assert_override(parent_members)\n      parent_member = parent_members[@name]\n      if parent_member.nil?\n        if @override\n          raise Puppet::ParseError, _(\"expected %{label} to override an inherited %{feature_type}, but no such %{feature_type} was found\") %\n                                    { label: label, feature_type: feature_type }\n        end\n        self\n      else\n        parent_member.assert_can_be_overridden(self)\n      end\n    end\n\n    # Checks if the given _member_ can override this member.\n    #\n    # @param member [PAnnotatedMember] the overriding member\n    # @return [PAnnotatedMember] its argument\n    # @raises [Puppet::ParseError] if the assertion fails\n    # @api private\n    def assert_can_be_overridden(member)\n      unless instance_of?(member.class)\n        raise Puppet::ParseError, _(\"%{member} attempts to override %{label}\") % { member: member.label, label: label }\n      end\n      if @final && !(constant? && member.constant?)\n        raise Puppet::ParseError, _(\"%{member} attempts to override final %{label}\") % { member: member.label, label: label }\n      end\n      unless member.override?\n        # TRANSLATOR 'override => true' is a puppet syntax and should not be translated\n        raise Puppet::ParseError, _(\"%{member} attempts to override %{label} without having override => true\") % { member: member.label, label: label }\n      end\n      unless @type.assignable?(member.type)\n        raise Puppet::ParseError, _(\"%{member} attempts to override %{label} with a type that does not match\") % { member: member.label, label: label }\n      end\n\n      member\n    end\n\n    def constant?\n      false\n    end\n\n    # @return [Boolean] `true` if this feature cannot be overridden\n    # @api public\n    def final?\n      @final\n    end\n\n    # @return [Boolean] `true` if this feature must override an inherited feature\n    # @api public\n    def override?\n      @override\n    end\n\n    # @api public\n    def hash\n      @name.hash ^ @type.hash\n    end\n\n    # @api public\n    def eql?(o)\n      self.class == o.class && @name == o.name && @type == o.type && @override == o.override? && @final == o.final?\n    end\n\n    # @api public\n    def ==(o)\n      eql?(o)\n    end\n\n    # Returns the member as a hash suitable as an argument for constructor. Name is excluded\n    # @return [Hash{String=>Object}] the initialization hash\n    # @api private\n    def _pcore_init_hash\n      hash = { KEY_TYPE => @type }\n      hash[KEY_FINAL] = true if @final\n      hash[KEY_OVERRIDE] = true if @override\n      hash[KEY_ANNOTATIONS] = @annotations unless @annotations.nil?\n      hash\n    end\n\n    # @api private\n    def feature_type\n      self.class.feature_type\n    end\n\n    # @api private\n    def label\n      self.class.label(@container, @name)\n    end\n\n    # Performs type checking of arguments and invokes the method that corresponds to this\n    # method. The result of the invocation is returned\n    #\n    # @param receiver [Object] The receiver of the call\n    # @param scope [Puppet::Parser::Scope] The caller scope\n    # @param args [Array] Array of arguments.\n    # @return [Object] The result returned by the member function or attribute\n    #\n    # @api private\n    def invoke(receiver, scope, args, &block)\n      @dispatch ||= create_dispatch(receiver)\n\n      args_type = TypeCalculator.infer_set(block_given? ? args + [block] : args)\n      found = @dispatch.find { |d| d.type.callable?(args_type) }\n      raise ArgumentError, TypeMismatchDescriber.describe_signatures(label, @dispatch, args_type) if found.nil?\n\n      found.invoke(receiver, scope, args, &block)\n    end\n\n    # @api private\n    def create_dispatch(instance)\n      # TODO: Assumes Ruby implementation for now\n      if callable_type.is_a?(PVariantType)\n        callable_type.types.map do |ct|\n          Functions::Dispatch.new(ct, RubyGenerator.protect_reserved_name(name), [], false, ct.block_type.nil? ? nil : 'block')\n        end\n      else\n        [Functions::Dispatch.new(callable_type, RubyGenerator.protect_reserved_name(name), [], false, callable_type.block_type.nil? ? nil : 'block')]\n      end\n    end\n\n    # @api private\n    def self.feature_type\n      raise NotImplementedError, \"'#{self.class.name}' should implement #feature_type\"\n    end\n\n    def self.label(container, name)\n      \"#{feature_type} #{container.label}[#{name}]\"\n    end\n  end\n\n  # Describes a named Attribute in an Object type\n  # @api public\n  class PAttribute < PAnnotatedMember\n    # @return [String,nil] The attribute kind as defined by #TYPE_ATTRIBUTE_KIND, or `nil`\n    attr_reader :kind\n\n    # @param name [String] The name of the attribute\n    # @param container [PObjectType] The containing object type\n    # @param init_hash [Hash{String=>Object}] Hash containing attribute options\n    # @option init_hash [PAnyType] 'type' The attribute type (required)\n    # @option init_hash [Object] 'value' The default value, must be an instanceof the given `type` (optional)\n    # @option init_hash [String] 'kind' The attribute kind, matching #TYPE_ATTRIBUTE_KIND\n    # @api public\n    def initialize(name, container, init_hash)\n      super(name, container, TypeAsserter.assert_instance_of(nil, TYPE_ATTRIBUTE, init_hash) { \"initializer for #{self.class.label(container, name)}\" })\n      if name == Serialization::PCORE_TYPE_KEY || name == Serialization::PCORE_VALUE_KEY\n        raise Puppet::ParseError, _(\"The attribute '%{name}' is reserved and cannot be used\") % { name: name }\n      end\n\n      @kind = init_hash[KEY_KIND]\n      if @kind == ATTRIBUTE_KIND_CONSTANT # final is implied\n        if init_hash.include?(KEY_FINAL) && !@final\n          # TRANSLATOR 'final => false' is puppet syntax and should not be translated\n          raise Puppet::ParseError, _(\"%{label} of kind 'constant' cannot be combined with final => false\") % { label: label }\n        end\n\n        @final = true\n      end\n\n      if init_hash.include?(KEY_VALUE)\n        if @kind == ATTRIBUTE_KIND_DERIVED || @kind == ATTRIBUTE_KIND_GIVEN_OR_DERIVED\n          raise Puppet::ParseError, _(\"%{label} of kind '%{kind}' cannot be combined with an attribute value\") % { label: label, kind: @kind }\n        end\n\n        v = init_hash[KEY_VALUE]\n        @value = v == :default ? v : TypeAsserter.assert_instance_of(nil, type, v) { \"#{label} #{KEY_VALUE}\" }\n      else\n        raise Puppet::ParseError, _(\"%{label} of kind 'constant' requires a value\") % { label: label } if @kind == ATTRIBUTE_KIND_CONSTANT\n\n        @value = :undef # Not to be confused with nil or :default\n      end\n    end\n\n    def callable_type\n      TYPE_ATTRIBUTE_CALLABLE\n    end\n\n    # @api public\n    def eql?(o)\n      super && @kind == o.kind && @value == (o.value? ? o.value : :undef)\n    end\n\n    # Returns the member as a hash suitable as an argument for constructor. Name is excluded\n    # @return [Hash{String=>Object}] the hash\n    # @api private\n    def _pcore_init_hash\n      hash = super\n      unless @kind.nil?\n        hash[KEY_KIND] = @kind\n        hash.delete(KEY_FINAL) if @kind == ATTRIBUTE_KIND_CONSTANT # final is implied\n      end\n      hash[KEY_VALUE] = @value unless @value == :undef\n      hash\n    end\n\n    def constant?\n      @kind == ATTRIBUTE_KIND_CONSTANT\n    end\n\n    # @return [Booelan] true if the given value equals the default value for this attribute\n    def default_value?(value)\n      @value == value\n    end\n\n    # @return [Boolean] `true` if a value has been defined for this attribute.\n    def value?\n      @value != :undef\n    end\n\n    # Returns the value of this attribute, or raises an error if no value has been defined. Raising an error\n    # is necessary since a defined value may be `nil`.\n    #\n    # @return [Object] the value that has been defined for this attribute.\n    # @raise [Puppet::Error] if no value has been defined\n    # @api public\n    def value\n      # An error must be raised here since `nil` is a valid value and it would be bad to leak the :undef symbol\n      raise Puppet::Error, \"#{label} has no value\" if @value == :undef\n\n      @value\n    end\n\n    # @api private\n    def self.feature_type\n      'attribute'\n    end\n  end\n\n  class PTypeParameter < PAttribute\n    # @return [Hash{String=>Object}] the hash\n    # @api private\n    def _pcore_init_hash\n      hash = super\n      hash[KEY_TYPE] = hash[KEY_TYPE].type\n      hash.delete(KEY_VALUE) if hash.include?(KEY_VALUE) && hash[KEY_VALUE].nil?\n      hash\n    end\n\n    # @api private\n    def self.feature_type\n      'type_parameter'\n    end\n  end\n\n  # Describes a named Function in an Object type\n  # @api public\n  class PFunction < PAnnotatedMember\n    # @param name [String] The name of the attribute\n    # @param container [PObjectType] The containing object type\n    # @param init_hash [Hash{String=>Object}] Hash containing function options\n    # @api public\n    def initialize(name, container, init_hash)\n      super(name, container, TypeAsserter.assert_instance_of([\"initializer for function '%s'\", name], TYPE_FUNCTION, init_hash))\n    end\n\n    def callable_type\n      type\n    end\n\n    # @api private\n    def self.feature_type\n      'function'\n    end\n  end\n\n  attr_reader :name\n  attr_reader :parent\n  attr_reader :equality\n  attr_reader :checks\n  attr_reader :annotations\n\n  # Initialize an Object Type instance. The initialization will use either a name and an initialization\n  # hash expression, or a fully resolved initialization hash.\n  #\n  # @overload initialize(name, init_hash_expression)\n  #   Used when the Object type is loaded using a type alias expression. When that happens, it is important that\n  #   the actual resolution of the expression is deferred until all definitions have been made known to the current\n  #   loader. The object will then be resolved when it is loaded by the {TypeParser}. \"resolved\" here, means that\n  #   the hash expression is fully resolved, and then passed to the {#_pcore_init_from_hash} method.\n  #   @param name [String] The name of the object\n  #   @param init_hash_expression [Model::LiteralHash] The hash describing the Object features\n  #\n  # @overload initialize(init_hash)\n  #   Used when the object is created by the {TypeFactory}. The init_hash must be fully resolved.\n  #   @param _pcore_init_hash [Hash{String=>Object}] The hash describing the Object features\n  #   @param loader [Loaders::Loader,nil] the loader that loaded the type\n  #\n  # @api private\n  def initialize(_pcore_init_hash, init_hash_expression = nil) # rubocop:disable Lint/UnderscorePrefixedVariableName\n    if _pcore_init_hash.is_a?(Hash)\n      _pcore_init_from_hash(_pcore_init_hash)\n      @loader = init_hash_expression unless init_hash_expression.nil?\n    else\n      @type_parameters = EMPTY_HASH\n      @attributes = EMPTY_HASH\n      @functions = EMPTY_HASH\n      @name = TypeAsserter.assert_instance_of('object name', TYPE_OBJECT_NAME, _pcore_init_hash)\n      @init_hash_expression = init_hash_expression\n    end\n  end\n\n  def instance?(o, guard = nil)\n    if o.is_a?(PuppetObject)\n      assignable?(o._pcore_type, guard)\n    else\n      name = o.class.name\n      return false if name.nil? # anonymous class that doesn't implement PuppetObject is not an instance\n\n      ir = Loaders.implementation_registry\n      type = ir.nil? ? nil : ir.type_for_module(name)\n      !type.nil? && assignable?(type, guard)\n    end\n  end\n\n  # @api private\n  def new_function\n    @new_function ||= create_new_function\n  end\n\n  # Assign a new instance reader to this type\n  # @param [Serialization::InstanceReader] reader the reader to assign\n  # @api private\n  def reader=(reader)\n    @reader = reader\n  end\n\n  # Assign a new instance write to this type\n  # @param [Serialization::InstanceWriter] the writer to assign\n  # @api private\n  def writer=(writer)\n    @writer = writer\n  end\n\n  # Read an instance of this type from a deserializer\n  # @param [Integer] value_count the number attributes needed to create the instance\n  # @param [Serialization::Deserializer] deserializer the deserializer to read from\n  # @return [Object] the created instance\n  # @api private\n  def read(value_count, deserializer)\n    reader.read(self, implementation_class, value_count, deserializer)\n  end\n\n  # Write an instance of this type using a serializer\n  # @param [Object] value the instance to write\n  # @param [Serialization::Serializer] the serializer to write to\n  # @api private\n  def write(value, serializer)\n    writer.write(self, value, serializer)\n  end\n\n  # @api private\n  def create_new_function\n    impl_class = implementation_class\n    return impl_class.create_new_function(self) if impl_class.respond_to?(:create_new_function)\n\n    (param_names, param_types, required_param_count) = parameter_info(impl_class)\n\n    # Create the callable with a size that reflects the required and optional parameters\n    create_type = TypeFactory.callable(*param_types, required_param_count, param_names.size)\n    from_hash_type = TypeFactory.callable(init_hash_type, 1, 1)\n\n    # Create and return a #new_XXX function where the dispatchers are added programmatically.\n    Puppet::Functions.create_loaded_function(:\"new_#{name}\", loader) do\n      # The class that creates new instances must be available to the constructor methods\n      # and is therefore declared as a variable and accessor on the class that represents\n      # this added function.\n      @impl_class = impl_class\n\n      def self.impl_class\n        @impl_class\n      end\n\n      # It's recommended that an implementor of an Object type provides the method #from_asserted_hash.\n      # This method should accept a hash and assume that type assertion has been made already (it is made\n      # by the dispatch added here).\n      if impl_class.respond_to?(:from_asserted_hash)\n        dispatcher.add(Functions::Dispatch.new(from_hash_type, :from_hash, ['hash']))\n        def from_hash(hash)\n          self.class.impl_class.from_asserted_hash(hash)\n        end\n      end\n\n      # Add the dispatch that uses the standard #from_asserted_args or #new method on the class. It's assumed that the\n      # method performs no assertions.\n      dispatcher.add(Functions::Dispatch.new(create_type, :create, param_names))\n      if impl_class.respond_to?(:from_asserted_args)\n        def create(*args)\n          self.class.impl_class.from_asserted_args(*args)\n        end\n      else\n        def create(*args)\n          self.class.impl_class.new(*args)\n        end\n      end\n    end\n  end\n\n  # @api private\n  def implementation_class(create = true)\n    if @implementation_class.nil? && create\n      ir = Loaders.implementation_registry\n      class_name = ir.nil? ? nil : ir.module_name_for_type(self)\n      if class_name.nil?\n        # Use generator to create a default implementation\n        @implementation_class = RubyGenerator.new.create_class(self)\n        @implementation_class.class_eval(&@implementation_override) if instance_variable_defined?(:@implementation_override)\n      else\n        # Can the mapping be loaded?\n        @implementation_class = ClassLoader.provide(class_name)\n\n        raise Puppet::Error, \"Unable to load class #{class_name}\" if @implementation_class.nil?\n        unless @implementation_class < PuppetObject || @implementation_class.respond_to?(:ecore)\n          raise Puppet::Error, \"Unable to create an instance of #{name}. #{class_name} does not include module #{PuppetObject.name}\"\n        end\n      end\n    end\n    @implementation_class\n  end\n\n  # @api private\n  def implementation_class=(cls)\n    raise ArgumentError, \"attempt to redefine implementation class for #{label}\" unless @implementation_class.nil?\n\n    @implementation_class = cls\n  end\n\n  # The block passed to this method will be passed in a call to `#class_eval` on the dynamically generated\n  # class for this data type. It's indended use is to complement or redefine the generated methods and\n  # attribute readers.\n  #\n  # The method is normally called with the block passed to `#implementation` when a data type is defined using\n  # {Puppet::DataTypes::create_type}.\n  #\n  # @api private\n  def implementation_override=(block)\n    if !@implementation_class.nil? || instance_variable_defined?(:@implementation_override)\n      raise ArgumentError, \"attempt to redefine implementation override for #{label}\"\n    end\n\n    @implementation_override = block\n  end\n\n  def extract_init_hash(o)\n    return o._pcore_init_hash if o.respond_to?(:_pcore_init_hash)\n\n    result = {}\n    pic = parameter_info(o.class)\n    attrs = attributes(true)\n    pic[0].each do |name|\n      v = o.send(name)\n      result[name] = v unless attrs[name].default_value?(v)\n    end\n    result\n  end\n\n  # @api private\n  # @return [(Array<String>, Array<PAnyType>, Integer)] array of parameter names, array of parameter types, and a count reflecting the required number of parameters\n  def parameter_info(impl_class)\n    # Create a types and a names array where optional entries ends up last\n    @parameter_info ||= {}\n    pic = @parameter_info[impl_class]\n    return pic if pic\n\n    opt_types = []\n    opt_names = []\n    non_opt_types = []\n    non_opt_names = []\n    init_hash_type.elements.each do |se|\n      if se.key_type.is_a?(POptionalType)\n        opt_names << se.name\n        opt_types << se.value_type\n      else\n        non_opt_names << se.name\n        non_opt_types << se.value_type\n      end\n    end\n    param_names = non_opt_names + opt_names\n    param_types = non_opt_types + opt_types\n    param_count = param_names.size\n\n    init = impl_class.respond_to?(:from_asserted_args) ? impl_class.method(:from_asserted_args) : impl_class.instance_method(:initialize)\n    init_non_opt_count = 0\n    init_param_names = init.parameters.map do |p|\n      init_non_opt_count += 1 if :req == p[0]\n      n = p[1].to_s\n      r = RubyGenerator.unprotect_reserved_name(n)\n      unless r.equal?(n)\n        # assert that the protected name wasn't a real name (names can start with underscore)\n        n = r unless param_names.index(r).nil?\n      end\n      n\n    end\n\n    if init_param_names != param_names\n      if init_param_names.size < param_count || init_non_opt_count > param_count\n        raise Serialization::SerializationError, \"Initializer for class #{impl_class.name} does not match the attributes of #{name}\"\n      end\n\n      init_param_names = init_param_names[0, param_count] if init_param_names.size > param_count\n      unless init_param_names == param_names\n        # Reorder needed to match initialize method arguments\n        new_param_types = []\n        init_param_names.each do |ip|\n          index = param_names.index(ip)\n          if index.nil?\n            raise Serialization::SerializationError,\n                  \"Initializer for class #{impl_class.name} parameter '#{ip}' does not match any of the attributes of type #{name}\"\n          end\n          new_param_types << param_types[index]\n        end\n        param_names = init_param_names\n        param_types = new_param_types\n      end\n    end\n\n    pic = [param_names.freeze, param_types.freeze, non_opt_types.size].freeze\n    @parameter_info[impl_class] = pic\n    pic\n  end\n\n  # @api private\n  def attr_reader_name(se)\n    if se.value_type.is_a?(PBooleanType) || se.value_type.is_a?(POptionalType) && se.value_type.type.is_a?(PBooleanType)\n      \"#{se.name}?\"\n    else\n      se.name\n    end\n  end\n\n  def self.from_hash(hash)\n    new(hash, nil)\n  end\n\n  # @api private\n  def _pcore_init_from_hash(init_hash)\n    TypeAsserter.assert_instance_of('object initializer', TYPE_OBJECT_I12N, init_hash)\n    @type_parameters = EMPTY_HASH\n    @attributes = EMPTY_HASH\n    @functions = EMPTY_HASH\n\n    # Name given to the loader have higher precedence than a name declared in the type\n    @name ||= init_hash[KEY_NAME]\n    @name.freeze unless @name.nil?\n\n    @parent = init_hash[KEY_PARENT]\n\n    parent_members = EMPTY_HASH\n    parent_type_params = EMPTY_HASH\n    parent_object_type = nil\n    unless @parent.nil?\n      check_self_recursion(self)\n      rp = resolved_parent\n      raise Puppet::ParseError, _(\"reference to unresolved type '%{name}'\") % { :name => rp.type_string } if rp.is_a?(PTypeReferenceType)\n\n      if rp.is_a?(PObjectType)\n        parent_object_type = rp\n        parent_members = rp.members(true)\n        parent_type_params = rp.type_parameters(true)\n      end\n    end\n\n    type_parameters = init_hash[KEY_TYPE_PARAMETERS]\n    unless type_parameters.nil? || type_parameters.empty?\n      @type_parameters = {}\n      type_parameters.each do |key, param_spec|\n        param_value = :undef\n        if param_spec.is_a?(Hash)\n          param_type = param_spec[KEY_TYPE]\n          param_value = param_spec[KEY_VALUE] if param_spec.include?(KEY_VALUE)\n        else\n          param_type = TypeAsserter.assert_instance_of(nil, PTypeType::DEFAULT, param_spec) { \"type_parameter #{label}[#{key}]\" }\n        end\n        param_type = POptionalType.new(param_type) unless param_type.is_a?(POptionalType)\n        type_param = PTypeParameter.new(key, self, KEY_TYPE => param_type, KEY_VALUE => param_value).assert_override(parent_type_params)\n        @type_parameters[key] = type_param\n      end\n    end\n\n    constants = init_hash[KEY_CONSTANTS]\n    attr_specs = init_hash[KEY_ATTRIBUTES]\n    if attr_specs.nil?\n      attr_specs = {}\n    else\n      # attr_specs might be frozen\n      attr_specs = attr_specs.to_h\n    end\n    unless constants.nil? || constants.empty?\n      constants.each do |key, value|\n        if attr_specs.include?(key)\n          raise Puppet::ParseError, _(\"attribute %{label}[%{key}] is defined as both a constant and an attribute\") % { label: label, key: key }\n        end\n\n        attr_spec = {\n          # Type must be generic here, or overrides would become impossible\n          KEY_TYPE => TypeCalculator.infer(value).generalize,\n          KEY_VALUE => value,\n          KEY_KIND => ATTRIBUTE_KIND_CONSTANT\n        }\n        # Indicate override if parent member exists. Type check etc. will take place later on.\n        attr_spec[KEY_OVERRIDE] = parent_members.include?(key)\n        attr_specs[key] = attr_spec\n      end\n    end\n\n    unless attr_specs.empty?\n      @attributes = attr_specs.to_h do |key, attr_spec|\n        unless attr_spec.is_a?(Hash)\n          attr_type = TypeAsserter.assert_instance_of(nil, PTypeType::DEFAULT, attr_spec) { \"attribute #{label}[#{key}]\" }\n          attr_spec = { KEY_TYPE => attr_type }\n          attr_spec[KEY_VALUE] = nil if attr_type.is_a?(POptionalType)\n        end\n        attr = PAttribute.new(key, self, attr_spec)\n        [attr.name, attr.assert_override(parent_members)]\n      end.freeze\n    end\n\n    func_specs = init_hash[KEY_FUNCTIONS]\n    unless func_specs.nil? || func_specs.empty?\n      @functions = func_specs.to_h do |key, func_spec|\n        func_spec = { KEY_TYPE => TypeAsserter.assert_instance_of(nil, TYPE_FUNCTION_TYPE, func_spec) { \"function #{label}[#{key}]\" } } unless func_spec.is_a?(Hash)\n        func = PFunction.new(key, self, func_spec)\n        name = func.name\n        raise Puppet::ParseError, _(\"%{label} conflicts with attribute with the same name\") % { label: func.label } if @attributes.include?(name)\n\n        [name, func.assert_override(parent_members)]\n      end.freeze\n    end\n\n    @equality_include_type = init_hash[KEY_EQUALITY_INCLUDE_TYPE]\n    @equality_include_type = true if @equality_include_type.nil?\n\n    equality = init_hash[KEY_EQUALITY]\n    equality = [equality] if equality.is_a?(String)\n    if equality.is_a?(Array)\n      unless equality.empty?\n        # TRANSLATORS equality_include_type = false should not be translated\n        raise Puppet::ParseError, _('equality_include_type = false cannot be combined with non empty equality specification') unless @equality_include_type\n\n        parent_eq_attrs = nil\n        equality.each do |attr_name|\n          attr = parent_members[attr_name]\n          if attr.nil?\n            attr = @attributes[attr_name] || @functions[attr_name]\n          elsif attr.is_a?(PAttribute)\n            # Assert that attribute is not already include by parent equality\n            parent_eq_attrs ||= parent_object_type.equality_attributes\n            if parent_eq_attrs.include?(attr_name)\n              including_parent = find_equality_definer_of(attr)\n              raise Puppet::ParseError, _(\"%{label} equality is referencing %{attribute} which is included in equality of %{including_parent}\") %\n                                        { label: label, attribute: attr.label, including_parent: including_parent.label }\n            end\n          end\n\n          unless attr.is_a?(PAttribute)\n            if attr.nil?\n              raise Puppet::ParseError, _(\"%{label} equality is referencing non existent attribute '%{attribute}'\") % { label: label, attribute: attr_name }\n            end\n\n            raise Puppet::ParseError, _(\"%{label} equality is referencing %{attribute}. Only attribute references are allowed\") %\n                                      { label: label, attribute: attr.label }\n          end\n          if attr.kind == ATTRIBUTE_KIND_CONSTANT\n            raise Puppet::ParseError, _(\"%{label} equality is referencing constant %{attribute}.\") % { label: label, attribute: attr.label } + ' ' +\n                                      _(\"Reference to constant is not allowed in equality\")\n          end\n        end\n      end\n      equality.freeze\n    end\n    @equality = equality\n\n    @checks = init_hash[KEY_CHECKS]\n    init_annotatable(init_hash)\n  end\n\n  def [](name)\n    member = @attributes[name] || @functions[name]\n    if member.nil?\n      rp = resolved_parent\n      member = rp[name] if rp.is_a?(PObjectType)\n    end\n    member\n  end\n\n  def accept(visitor, guard)\n    guarded_recursion(guard, nil) do |g|\n      super(visitor, g)\n      @parent.accept(visitor, g) unless parent.nil?\n      @type_parameters.values.each { |p| p.accept(visitor, g) }\n      @attributes.values.each { |a| a.accept(visitor, g) }\n      @functions.values.each { |f| f.accept(visitor, g) }\n    end\n  end\n\n  def callable_args?(callable, guard)\n    @parent.nil? ? false : @parent.callable_args?(callable, guard)\n  end\n\n  # Returns the type that a initialization hash used for creating instances of this type must conform to.\n  #\n  # @return [PStructType] the initialization hash type\n  # @api public\n  def init_hash_type\n    @init_hash_type ||= create_init_hash_type\n  end\n\n  def allocate\n    implementation_class.allocate\n  end\n\n  def create(*args)\n    implementation_class.create(*args)\n  end\n\n  def from_hash(hash)\n    implementation_class.from_hash(hash)\n  end\n\n  # Creates the type that a initialization hash used for creating instances of this type must conform to.\n  #\n  # @return [PStructType] the initialization hash type\n  # @api private\n  def create_init_hash_type\n    struct_elems = {}\n    attributes(true).values.each do |attr|\n      unless attr.kind == ATTRIBUTE_KIND_CONSTANT || attr.kind == ATTRIBUTE_KIND_DERIVED\n        if attr.value?\n          struct_elems[TypeFactory.optional(attr.name)] = attr.type\n        else\n          struct_elems[attr.name] = attr.type\n        end\n      end\n    end\n    TypeFactory.struct(struct_elems)\n  end\n\n  # The init_hash is primarily intended for serialization and string representation purposes. It creates a hash\n  # suitable for passing to {PObjectType#new(init_hash)}\n  #\n  # @return [Hash{String=>Object}] the features hash\n  # @api public\n  def _pcore_init_hash(include_name = true)\n    result = super()\n    result[KEY_NAME] = @name if include_name && !@name.nil?\n    result[KEY_PARENT] = @parent unless @parent.nil?\n    result[KEY_TYPE_PARAMETERS] = compressed_members_hash(@type_parameters) unless @type_parameters.empty?\n    unless @attributes.empty?\n      # Divide attributes into constants and others\n      tc = TypeCalculator.singleton\n      constants, others = @attributes.partition do |_, a|\n        a.kind == ATTRIBUTE_KIND_CONSTANT && a.type == tc.infer(a.value).generalize\n      end.map(&:to_h)\n\n      result[KEY_ATTRIBUTES] = compressed_members_hash(others) unless others.empty?\n      unless constants.empty?\n        # { kind => 'constant', type => <type of value>, value => <value> } becomes just <value>\n        constants.each_pair { |key, a| constants[key] = a.value }\n        result[KEY_CONSTANTS] = constants\n      end\n    end\n    result[KEY_FUNCTIONS] = compressed_members_hash(@functions) unless @functions.empty?\n    result[KEY_EQUALITY] = @equality unless @equality.nil?\n    result[KEY_CHECKS] = @checks unless @checks.nil?\n    result\n  end\n\n  def eql?(o)\n    self.class == o.class &&\n      @name == o.name &&\n      @parent == o.parent &&\n      @type_parameters == o.type_parameters &&\n      @attributes == o.attributes &&\n      @functions == o.functions &&\n      @equality == o.equality &&\n      @checks == o.checks\n  end\n\n  def hash\n    @name.nil? ? [@parent, @type_parameters, @attributes, @functions].hash : @name.hash\n  end\n\n  def kind_of_callable?(optional = true, guard = nil)\n    @parent.nil? ? false : @parent.kind_of_callable?(optional, guard)\n  end\n\n  def iterable?(guard = nil)\n    @parent.nil? ? false : @parent.iterable?(guard)\n  end\n\n  def iterable_type(guard = nil)\n    @parent.nil? ? false : @parent.iterable_type(guard)\n  end\n\n  def parameterized?\n    if @type_parameters.empty?\n      @parent.is_a?(PObjectType) ? @parent.parameterized? : false\n    else\n      true\n    end\n  end\n\n  # Returns the members (attributes and functions) of this `Object` type. If _include_parent_ is `true`, then all\n  # inherited members will be included in the returned `Hash`.\n  #\n  # @param include_parent [Boolean] `true` if inherited members should be included\n  # @return [Hash{String=>PAnnotatedMember}] a hash with the members\n  # @api public\n  def members(include_parent = false)\n    get_members(include_parent, :both)\n  end\n\n  # Returns the attributes of this `Object` type. If _include_parent_ is `true`, then all\n  # inherited attributes will be included in the returned `Hash`.\n  #\n  # @param include_parent [Boolean] `true` if inherited attributes should be included\n  # @return [Hash{String=>PAttribute}] a hash with the attributes\n  # @api public\n  def attributes(include_parent = false)\n    get_members(include_parent, :attributes)\n  end\n\n  # Returns the attributes that participate in equality comparison. Inherited equality attributes\n  # are included.\n  # @return [Hash{String=>PAttribute}] a hash of attributes\n  # @api public\n  def equality_attributes\n    all = {}\n    collect_equality_attributes(all)\n    all\n  end\n\n  # @return [Boolean] `true` if this type is included when comparing instances\n  # @api public\n  def equality_include_type?\n    @equality_include_type\n  end\n\n  # Returns the functions of this `Object` type. If _include_parent_ is `true`, then all\n  # inherited functions will be included in the returned `Hash`.\n  #\n  # @param include_parent [Boolean] `true` if inherited functions should be included\n  # @return [Hash{String=>PFunction}] a hash with the functions\n  # @api public\n  def functions(include_parent = false)\n    get_members(include_parent, :functions)\n  end\n\n  DEFAULT = PObjectType.new(EMPTY_HASH)\n  # Assert that this type does not inherit from itself\n  # @api private\n  def check_self_recursion(originator)\n    unless @parent.nil?\n      raise Puppet::Error, \"The Object type '#{originator.label}' inherits from itself\" if @parent.equal?(originator)\n\n      @parent.check_self_recursion(originator)\n    end\n  end\n\n  # @api private\n  def label\n    @name || 'Object'\n  end\n\n  # @api private\n  def resolved_parent\n    parent = @parent\n    while parent.is_a?(PTypeAliasType)\n      parent = parent.resolved_type\n    end\n    parent\n  end\n\n  def simple_name\n    label.split(DOUBLE_COLON).last\n  end\n\n  # Returns the type_parameters of this `Object` type. If _include_parent_ is `true`, then all\n  # inherited type_parameters will be included in the returned `Hash`.\n  #\n  # @param include_parent [Boolean] `true` if inherited type_parameters should be included\n  # @return [Hash{String=>PTypeParameter}] a hash with the type_parameters\n  # @api public\n  def type_parameters(include_parent = false)\n    all = {}\n    collect_type_parameters(all, include_parent)\n    all\n  end\n\n  protected\n\n  # An Object type is only assignable from another Object type. The other type\n  # or one of its parents must be equal to this type.\n  def _assignable?(o, guard)\n    case o\n    when PObjectType\n      if DEFAULT == self || self == o\n        true\n      else\n        op = o.parent\n        op.nil? ? false : assignable?(op, guard)\n      end\n    when PObjectTypeExtension\n      assignable?(o.base_type, guard)\n    else\n      false\n    end\n  end\n\n  def get_members(include_parent, member_type)\n    all = {}\n    collect_members(all, include_parent, member_type)\n    all\n  end\n\n  def collect_members(collector, include_parent, member_type)\n    if include_parent\n      parent = resolved_parent\n      parent.collect_members(collector, include_parent, member_type) if parent.is_a?(PObjectType)\n    end\n    collector.merge!(@attributes) unless member_type == :functions\n    collector.merge!(@functions) unless member_type == :attributes\n    nil\n  end\n\n  def collect_equality_attributes(collector)\n    parent = resolved_parent\n    parent.collect_equality_attributes(collector) if parent.is_a?(PObjectType)\n    if @equality.nil?\n      # All attributes except constants participate\n      collector.merge!(@attributes.reject { |_, attr| attr.kind == ATTRIBUTE_KIND_CONSTANT })\n    else\n      collector.merge!(@equality.to_h { |attr_name| [attr_name, @attributes[attr_name]] })\n    end\n    nil\n  end\n\n  def collect_type_parameters(collector, include_parent)\n    if include_parent\n      parent = resolved_parent\n      parent.collect_type_parameters(collector, include_parent) if parent.is_a?(PObjectType)\n    end\n    collector.merge!(@type_parameters)\n    nil\n  end\n\n  private\n\n  def compressed_members_hash(features)\n    features.values.to_h do |feature|\n      fh = feature._pcore_init_hash\n      if fh.size == 1\n        type = fh[KEY_TYPE]\n        fh = type unless type.nil?\n      end\n      [feature.name, fh]\n    end\n  end\n\n  # @return [PObjectType] the topmost parent who's #equality_attributes include the given _attr_\n  def find_equality_definer_of(attr)\n    type = self\n    until type.nil?\n      p = type.resolved_parent\n      return type unless p.is_a?(PObjectType)\n      return type unless p.equality_attributes.include?(attr.name)\n\n      type = p\n    end\n    nil\n  end\n\n  def guarded_recursion(guard, dflt)\n    if @self_recursion\n      guard ||= RecursionGuard.new\n      guard.with_this(self) { |state| (state & RecursionGuard::SELF_RECURSION_IN_THIS) == 0 ? yield(guard) : dflt }\n    else\n      yield(guard)\n    end\n  end\n\n  def reader\n    @reader ||= Serialization::ObjectReader::INSTANCE\n  end\n\n  def writer\n    @writer ||= Serialization::ObjectWriter::INSTANCE\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_object_type_extension.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# Base class for Parameterized Object implementations. The wrapper impersonates the base\n# object and extends it with methods to filter assignable types and instances based on parameter\n# values.\n#\n# @api public\nclass PObjectTypeExtension < PAnyType\n  include TypeWithMembers\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'base_type' => {\n                   KEY_TYPE => PTypeType::DEFAULT\n                 },\n                 'init_parameters' => {\n                   KEY_TYPE => PArrayType::DEFAULT\n                 })\n  end\n\n  attr_reader :base_type, :parameters\n\n  # @api private\n  def self.create(base_type, init_parameters)\n    impl_class = Loaders.implementation_registry.module_for_type(\"#{base_type.name}TypeExtension\") || self\n    impl_class.new(base_type, init_parameters)\n  end\n\n  # Creates an array of type parameters from the attributes of the given instance that matches the\n  # type parameters by name. Type parameters for which there is no matching attribute\n  # will have `nil` in their corresponding position on the array. The array is then passed\n  # as the `init_parameters` argument in a call to `create`\n  #\n  # @return [PObjectTypeExtension] the created extension\n  # @api private\n  def self.create_from_instance(base_type, instance)\n    type_parameters = base_type.type_parameters(true)\n    attrs = base_type.attributes(true)\n    params = type_parameters.keys.map do |pn|\n      attr = attrs[pn]\n      attr.nil? ? nil : instance.send(pn)\n    end\n    create(base_type, params)\n  end\n\n  def [](name)\n    @base_type[name]\n  end\n\n  # @api private\n  def initialize(base_type, init_parameters)\n    pts = base_type.type_parameters(true)\n    raise Puppet::ParseError, _('The %{label}-Type cannot be parameterized using []') % { label: base_type.label } if pts.empty?\n\n    @base_type = base_type\n\n    named_args = init_parameters.size == 1 && init_parameters[0].is_a?(Hash)\n    if named_args\n      # Catch case when first parameter is an assignable Hash\n      named_args = pts.size >= 1 && !pts.values[0].type.instance?(init_parameters[0])\n    end\n\n    by_name = {}\n    if named_args\n      hash = init_parameters[0]\n      hash.each_pair do |pn, pv|\n        tp = pts[pn]\n        if tp.nil?\n          raise Puppet::ParseError, _(\"'%{pn}' is not a known type parameter for %{label}-Type\") % { pn: pn, label: base_type.label }\n        end\n\n        by_name[pn] = check_param(tp, pv) unless pv == :default\n      end\n    else\n      pts.values.each_with_index do |tp, idx|\n        if idx < init_parameters.size\n          pv = init_parameters[idx]\n          by_name[tp.name] = check_param(tp, pv) unless pv == :default\n        end\n      end\n    end\n    if by_name.empty?\n      raise Puppet::ParseError, _('The %{label}-Type cannot be parameterized using an empty parameter list') % { label: base_type.label }\n    end\n\n    @parameters = by_name\n  end\n\n  def check_param(type_param, v)\n    TypeAsserter.assert_instance_of(nil, type_param.type, v) { type_param.label }\n  end\n\n  # Return the parameter values as positional arguments with unset values as :default. The\n  # array is stripped from trailing :default values\n  # @return [Array] the parameter values\n  # @api private\n  def init_parameters\n    pts = @base_type.type_parameters(true)\n    if pts.size > 2\n      @parameters\n    else\n      result = pts.values.map do |tp|\n        pn = tp.name\n        @parameters.include?(pn) ? @parameters[pn] : :default\n      end\n      # Remove trailing defaults\n      result.pop while result.last == :default\n      result\n    end\n  end\n\n  # @api private\n  def eql?(o)\n    super(o) && @base_type.eql?(o.base_type) && @parameters.eql?(o.parameters)\n  end\n\n  # @api private\n  def generalize\n    @base_type\n  end\n\n  # @api private\n  def hash\n    @base_type.hash ^ @parameters.hash\n  end\n\n  # @api private\n  def loader\n    @base_type.loader\n  end\n\n  # @api private\n  def check_self_recursion(originator)\n    @base_type.check_self_recursion(originator)\n  end\n\n  # @api private\n  def create(*args)\n    @base_type.create(*args)\n  end\n\n  # @api private\n  def instance?(o, guard = nil)\n    @base_type.instance?(o, guard) && test_instance?(o, guard)\n  end\n\n  # @api private\n  def new_function\n    @base_type.new_function\n  end\n\n  # @api private\n  def simple_name\n    @base_type.simple_name\n  end\n\n  # @api private\n  def implementation_class(create = true)\n    @base_type.implementation_class(create)\n  end\n\n  # @api private\n  def parameter_info(impl_class)\n    @base_type.parameter_info(impl_class)\n  end\n\n  protected\n\n  # Checks that the given `param_values` hash contains all keys present in the `parameters` of\n  # this instance and that each keyed value is a match for the given parameter. The match is done\n  # using case expression semantics.\n  #\n  # This method is only called when a given type is found to be assignable to the base type of\n  # this extension.\n  #\n  # @param param_values[Hash] the parameter values of the assignable type\n  # @param guard[RecursionGuard] guard against endless recursion\n  # @return [Boolean] true or false to indicate assignability\n  # @api public\n  def test_assignable?(param_values, guard)\n    # Default implementation performs case expression style matching of all parameter values\n    # provided that the value exist (this should always be the case, since all defaults have\n    # been assigned at this point)\n    eval = Parser::EvaluatingParser.singleton.evaluator\n    @parameters.keys.all? do |pn|\n      if param_values.include?(pn)\n        a = param_values[pn]\n        b = @parameters[pn]\n        eval.match?(a, b) || a.is_a?(PAnyType) && b.is_a?(PAnyType) && b.assignable?(a)\n      else\n        false\n      end\n    end\n  end\n\n  # Checks that the given instance `o` has one attribute for each key present in the `parameters` of\n  # this instance and that each attribute value is a match for the given parameter. The match is done\n  # using case expression semantics.\n  #\n  # This method is only called when the given value is found to be an instance of the base type of\n  # this extension.\n  #\n  # @param o [Object] the instance to test\n  # @param guard[RecursionGuard] guard against endless recursion\n  # @return [Boolean] true or false to indicate if the value is an instance or not\n  # @api public\n  def test_instance?(o, guard)\n    eval = Parser::EvaluatingParser.singleton.evaluator\n    @parameters.keys.all? do |pn|\n      m = o.public_method(pn)\n      m.arity == 0 ? eval.match?(m.call, @parameters[pn]) : false\n    rescue NameError\n      false\n    end\n  end\n\n  # @api private\n  def _assignable?(o, guard = nil)\n    if o.is_a?(PObjectTypeExtension)\n      @base_type.assignable?(o.base_type, guard) && test_assignable?(o.parameters, guard)\n    else\n      @base_type.assignable?(o, guard) && test_assignable?(EMPTY_HASH, guard)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_runtime_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# @api public\nclass PRuntimeType < PAnyType\n  TYPE_NAME_OR_PATTERN = PVariantType.new([PStringType::NON_EMPTY, PTupleType.new([PRegexpType::DEFAULT, PStringType::NON_EMPTY])])\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'runtime' => {\n                   KEY_TYPE => POptionalType.new(PStringType::NON_EMPTY),\n                   KEY_VALUE => nil\n                 },\n                 'name_or_pattern' => {\n                   KEY_TYPE => POptionalType.new(TYPE_NAME_OR_PATTERN),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  attr_reader :runtime, :name_or_pattern\n\n  # Creates a new instance of a Runtime type\n  #\n  # @param runtime [String] the name of the runtime, e.g. 'ruby'\n  # @param name_or_pattern [String,Array(Regexp,String)] name of runtime or two patterns, mapping Puppet name => runtime name\n  # @api public\n  def initialize(runtime, name_or_pattern)\n    unless runtime.nil? || runtime.is_a?(Symbol)\n      runtime = TypeAsserter.assert_instance_of(\"Runtime 'runtime'\", PStringType::NON_EMPTY, runtime).to_sym\n    end\n    @runtime = runtime\n    @name_or_pattern = TypeAsserter.assert_instance_of(\"Runtime 'name_or_pattern'\", TYPE_NAME_OR_PATTERN, name_or_pattern, true)\n  end\n\n  def hash\n    @runtime.hash ^ @name_or_pattern.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @runtime == o.runtime && @name_or_pattern == o.name_or_pattern\n  end\n\n  def instance?(o, guard = nil)\n    assignable?(TypeCalculator.infer(o), guard)\n  end\n\n  def iterable?(guard = nil)\n    if @runtime == :ruby && !runtime_type_name.nil?\n      begin\n        c = ClassLoader.provide(self)\n        return c < Iterable unless c.nil?\n      rescue ArgumentError\n      end\n    end\n    false\n  end\n\n  def iterable_type(guard = nil)\n    iterable?(guard) ? PIterableType.new(self) : nil\n  end\n\n  # @api private\n  def runtime_type_name\n    @name_or_pattern.is_a?(String) ? @name_or_pattern : nil\n  end\n\n  # @api private\n  def class_or_module\n    raise \"Only ruby classes or modules can be produced by this runtime, got '#{runtime}\" unless runtime == :ruby\n    raise 'A pattern based Runtime type cannot produce a class or module' if @name_or_pattern.is_a?(Array)\n\n    com = ClassLoader.provide(self)\n    raise \"The name #{@name_or_pattern} does not represent a ruby class or module\" if com.nil?\n\n    com\n  end\n\n  # @api private\n  def from_puppet_name(puppet_name)\n    if @name_or_pattern.is_a?(Array)\n      substituted = puppet_name.sub(*@name_or_pattern)\n      substituted == puppet_name ? nil : PRuntimeType.new(@runtime, substituted)\n    else\n      nil\n    end\n  end\n\n  DEFAULT = PRuntimeType.new(nil, nil)\n  RUBY = PRuntimeType.new(:ruby, nil)\n\n  protected\n\n  # Assignable if o's has the same runtime and the runtime name resolves to\n  # a class that is the same or subclass of t1's resolved runtime type name\n  # @api private\n  def _assignable?(o, guard)\n    return false unless o.is_a?(PRuntimeType)\n    return false unless @runtime.nil? || @runtime == o.runtime\n    return true if @name_or_pattern.nil? # t1 is wider\n\n    onp = o.name_or_pattern\n    return true if @name_or_pattern == onp\n    return false unless @name_or_pattern.is_a?(String) && onp.is_a?(String)\n\n    # NOTE: This only supports Ruby, must change when/if the set of runtimes is expanded\n    begin\n      c1 = ClassLoader.provide(self)\n      c2 = ClassLoader.provide(o)\n      c1.is_a?(Module) && c2.is_a?(Module) && !!(c2 <= c1)\n    rescue ArgumentError\n      false\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_sem_ver_range_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# An unparameterized type that represents all VersionRange instances\n#\n# @api public\nclass PSemVerRangeType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  # Check if a version is included in a version range. The version can be a string or\n  # a `SemanticPuppet::SemVer`\n  #\n  # @param range [SemanticPuppet::VersionRange] the range to match against\n  # @param version [SemanticPuppet::Version,String] the version to match\n  # @return [Boolean] `true` if the range includes the given version\n  #\n  # @api public\n  def self.include?(range, version)\n    case version\n    when SemanticPuppet::Version\n      range.include?(version)\n    when String\n      begin\n        range.include?(SemanticPuppet::Version.parse(version))\n      rescue SemanticPuppet::Version::ValidationFailure\n        false\n      end\n    else\n      false\n    end\n  end\n\n  # Creates a {SemanticPuppet::VersionRange} from the given _version_range_ argument. If the argument is `nil` or\n  # a {SemanticPuppet::VersionRange}, it is returned. If it is a {String}, it will be parsed into a\n  # {SemanticPuppet::VersionRange}. Any other class will raise an {ArgumentError}.\n  #\n  # @param version_range [SemanticPuppet::VersionRange,String,nil] the version range to convert\n  # @return [SemanticPuppet::VersionRange] the converted version range\n  # @raise [ArgumentError] when the argument cannot be converted into a version range\n  #\n  def self.convert(version_range)\n    case version_range\n    when nil, SemanticPuppet::VersionRange\n      version_range\n    when String\n      SemanticPuppet::VersionRange.parse(version_range)\n    else\n      raise ArgumentError, \"Unable to convert a #{version_range.class.name} to a SemVerRange\"\n    end\n  end\n\n  # Checks if range _a_ is a sub-range of (i.e. completely covered by) range _b_\n  # @param a [SemanticPuppet::VersionRange] the first range\n  # @param b [SemanticPuppet::VersionRange] the second range\n  #\n  # @return [Boolean] `true` if _a_ is completely covered by _b_\n  def self.covered_by?(a, b)\n    b.begin <= a.begin && (b.end > a.end || b.end == a.end && (!b.exclude_end? || a.exclude_end?))\n  end\n\n  # Merge two ranges so that the result matches all versions matched by both. A merge\n  # is only possible when the ranges are either adjacent or have an overlap.\n  #\n  # @param a [SemanticPuppet::VersionRange] the first range\n  # @param b [SemanticPuppet::VersionRange] the second range\n  # @return [SemanticPuppet::VersionRange,nil] the result of the merge\n  #\n  # @api public\n  def self.merge(a, b)\n    if a.include?(b.begin) || b.include?(a.begin)\n      max = [a.end, b.end].max\n      exclude_end = false\n      if a.exclude_end?\n        exclude_end = max == a.end && (max > b.end || b.exclude_end?)\n      elsif b.exclude_end?\n        exclude_end = max == b.end && (max > a.end || a.exclude_end?)\n      end\n      SemanticPuppet::VersionRange.new([a.begin, b.begin].min, max, exclude_end)\n    elsif a.exclude_end? && a.end == b.begin\n      # Adjacent, a before b\n      SemanticPuppet::VersionRange.new(a.begin, b.end, b.exclude_end?)\n    elsif b.exclude_end? && b.end == a.begin\n      # Adjacent, b before a\n      SemanticPuppet::VersionRange.new(b.begin, a.end, a.exclude_end?)\n    else\n      # No overlap\n      nil\n    end\n  end\n\n  def roundtrip_with_string?\n    true\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(SemanticPuppet::VersionRange)\n  end\n\n  def eql?(o)\n    self.class == o.class\n  end\n\n  def hash?\n    super ^ @version_range.hash\n  end\n\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_VersionRange, type.loader) do\n      local_types do\n        type 'SemVerRangeString = String[1]'\n        type 'SemVerRangeHash = Struct[{min=>Variant[Default,SemVer],Optional[max]=>Variant[Default,SemVer],Optional[exclude_max]=>Boolean}]'\n      end\n\n      # Constructs a VersionRange from a String with a format specified by\n      #\n      # https://github.com/npm/node-semver#range-grammar\n      #\n      # The logical or || operator is not implemented since it effectively builds\n      # an array of ranges that may be disparate. The {{SemanticPuppet::VersionRange}} inherits\n      # from the standard ruby range. It must be possible to describe that range in terms\n      # of min, max, and exclude max.\n      #\n      # The Puppet Version type is parameterized and accepts multiple ranges so creating such\n      # constraints is still possible. It will just require several parameters rather than one\n      # parameter containing the '||' operator.\n      #\n      dispatch :from_string do\n        param 'SemVerRangeString', :str\n      end\n\n      # Constructs a VersionRange from a min, and a max version. The Boolean argument denotes\n      # whether or not the max version is excluded or included in the range. It is included by\n      # default.\n      #\n      dispatch :from_versions do\n        param 'Variant[Default,SemVer]', :min\n        param 'Variant[Default,SemVer]', :max\n        optional_param 'Boolean', :exclude_max\n      end\n\n      # Same as #from_versions but each argument is instead given in a Hash\n      #\n      dispatch :from_hash do\n        param 'SemVerRangeHash', :hash_args\n      end\n\n      def from_string(str)\n        SemanticPuppet::VersionRange.parse(str)\n      end\n\n      def from_versions(min, max = :default, exclude_max = false)\n        min = SemanticPuppet::Version::MIN if min == :default\n        max = SemanticPuppet::Version::MAX if max == :default\n        SemanticPuppet::VersionRange.new(min, max, exclude_max)\n      end\n\n      def from_hash(hash)\n        from_versions(hash['min'], hash.fetch('max', :default), hash.fetch('exclude_max', false))\n      end\n    end\n  end\n\n  DEFAULT = PSemVerRangeType.new\n\n  protected\n\n  def _assignable?(o, guard)\n    self == o\n  end\n\n  def self.range_pattern\n    part = '(?<part>[0-9A-Za-z-]+)'\n    parts = \"(?<parts>#{part}(?:\\\\.\\\\g<part>)*)\"\n\n    qualifier = \"(?:-#{parts})?(?:\\\\+\\\\g<parts>)?\"\n\n    xr = '(?<xr>[xX*]|0|[1-9][0-9]*)'\n    partial = \"(?<partial>#{xr}(?:\\\\.\\\\g<xr>(?:\\\\.\\\\g<xr>#{qualifier})?)?)\"\n\n    hyphen = \"(?:#{partial}\\\\s+-\\\\s+\\\\g<partial>)\"\n    simple = \"(?<simple>(?:<|>|>=|<=|~|\\\\^)?\\\\g<partial>)\"\n\n    \"#{hyphen}|#{simple}(?:\\\\s+\\\\g<simple>)*\"\n  end\n  private_class_method :range_pattern\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_sem_ver_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# A Puppet Language Type that exposes the {{SemanticPuppet::Version}} and {{SemanticPuppet::VersionRange}}.\n# The version type is parameterized with version ranges.\n#\n# @api public\nclass PSemVerType < PScalarType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarType',\n                 'ranges' => {\n                   KEY_TYPE => PArrayType.new(PVariantType.new([PSemVerRangeType::DEFAULT, PStringType::NON_EMPTY])),\n                   KEY_VALUE => []\n                 })\n  end\n\n  attr_reader :ranges\n\n  def initialize(ranges)\n    ranges = ranges.map { |range| range.is_a?(SemanticPuppet::VersionRange) ? range : SemanticPuppet::VersionRange.parse(range) }\n    ranges = merge_ranges(ranges) if ranges.size > 1\n    @ranges = ranges\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(SemanticPuppet::Version) && (@ranges.empty? || @ranges.any? { |range| range.include?(o) })\n  end\n\n  def eql?(o)\n    self.class == o.class && @ranges == o.ranges\n  end\n\n  def hash?\n    super ^ @ranges.hash\n  end\n\n  # Creates a SemVer version from the given _version_ argument. If the argument is `nil` or\n  # a {SemanticPuppet::Version}, it is returned. If it is a {String}, it will be parsed into a\n  # {SemanticPuppet::Version}. Any other class will raise an {ArgumentError}.\n  #\n  # @param version [SemanticPuppet::Version,String,nil] the version to convert\n  # @return [SemanticPuppet::Version] the converted version\n  # @raise [ArgumentError] when the argument cannot be converted into a version\n  #\n  def self.convert(version)\n    case version\n    when nil, SemanticPuppet::Version\n      version\n    when String\n      SemanticPuppet::Version.parse(version)\n    else\n      raise ArgumentError, \"Unable to convert a #{version.class.name} to a SemVer\"\n    end\n  end\n\n  # @api private\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_Version, type.loader) do\n      local_types do\n        type 'PositiveInteger = Integer[0,default]'\n        type 'SemVerQualifier = Pattern[/\\A(?<part>[0-9A-Za-z-]+)(?:\\.\\g<part>)*\\Z/]'\n        type \"SemVerPattern = Pattern[/\\\\A#{SemanticPuppet::Version::REGEX_FULL}\\\\Z/]\"\n        type 'SemVerHash = Struct[{major=>PositiveInteger,minor=>PositiveInteger,patch=>PositiveInteger,Optional[prerelease]=>SemVerQualifier,Optional[build]=>SemVerQualifier}]'\n      end\n\n      # Creates a SemVer from a string as specified by http://semver.org/\n      #\n      dispatch :from_string do\n        param 'SemVerPattern', :str\n      end\n\n      # Creates a SemVer from integers, prerelease, and build arguments\n      #\n      dispatch :from_args do\n        param 'PositiveInteger', :major\n        param 'PositiveInteger', :minor\n        param 'PositiveInteger', :patch\n        optional_param 'SemVerQualifier', :prerelease\n        optional_param 'SemVerQualifier', :build\n      end\n\n      # Same as #from_args but each argument is instead given in a Hash\n      #\n      dispatch :from_hash do\n        param 'SemVerHash', :hash_args\n      end\n\n      argument_mismatch :on_error do\n        param 'String', :str\n      end\n\n      def from_string(str)\n        SemanticPuppet::Version.parse(str)\n      end\n\n      def from_args(major, minor, patch, prerelease = nil, build = nil)\n        SemanticPuppet::Version.new(major, minor, patch, to_array(prerelease), to_array(build))\n      end\n\n      def from_hash(hash)\n        SemanticPuppet::Version.new(hash['major'], hash['minor'], hash['patch'], to_array(hash['prerelease']), to_array(hash['build']))\n      end\n\n      def on_error(str)\n        _(\"The string '%{str}' cannot be converted to a SemVer\") % { str: str }\n      end\n\n      private\n\n      def to_array(component)\n        component ? [component] : nil\n      end\n    end\n  end\n\n  DEFAULT = PSemVerType.new(EMPTY_ARRAY)\n\n  protected\n\n  def _assignable?(o, guard)\n    return false unless o.instance_of?(self.class)\n    return true if @ranges.empty?\n    return false if o.ranges.empty?\n\n    # All ranges in o must be covered by at least one range in self\n    o.ranges.all? do |o_range|\n      @ranges.any? do |range|\n        PSemVerRangeType.covered_by?(o_range, range)\n      end\n    end\n  end\n\n  # @api private\n  def merge_ranges(ranges)\n    result = []\n    until ranges.empty?\n      unmerged = []\n      x = ranges.pop\n      result << ranges.inject(x) do |memo, y|\n        merged = PSemVerRangeType.merge(memo, y)\n        if merged.nil?\n          unmerged << y\n        else\n          memo = merged\n        end\n        memo\n      end\n      ranges = unmerged\n    end\n    result.reverse!\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_sensitive_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# A Puppet Language type that wraps sensitive information. The sensitive type is parameterized by\n# the wrapped value type.\n#\n#\n# @api public\nclass PSensitiveType < PTypeWithContainedType\n  class Sensitive\n    def initialize(value)\n      @value = value\n    end\n\n    def unwrap\n      @value\n    end\n\n    def to_s\n      \"Sensitive [value redacted]\"\n    end\n\n    def inspect\n      \"#<#{self}>\"\n    end\n\n    def hash\n      @value.hash\n    end\n\n    def ==(other)\n      other.is_a?(Sensitive) &&\n        other.hash == hash\n    end\n    alias eql? ==\n  end\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  def initialize(type = nil)\n    @type = type.nil? ? PAnyType.new : type.generalize\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(Sensitive) && @type.instance?(o.unwrap, guard)\n  end\n\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_Sensitive, type.loader) do\n      dispatch :from_sensitive do\n        param 'Sensitive', :value\n      end\n\n      dispatch :from_any do\n        param 'Any', :value\n      end\n\n      def from_any(value)\n        Sensitive.new(value)\n      end\n\n      # Since the Sensitive value is immutable we can reuse the existing instance instead of making a copy.\n      def from_sensitive(value)\n        value\n      end\n    end\n  end\n\n  private\n\n  def _assignable?(o, guard)\n    instance_of?(o.class) && @type.assignable?(o.type, guard)\n  end\n\n  DEFAULT = PSensitiveType.new\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_timespan_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n  class PAbstractTimeDataType < PScalarType\n    # @param from [AbstractTime] lower bound for this type. Nil or :default means unbounded\n    # @param to [AbstractTime] upper bound for this type. Nil or :default means unbounded\n    def initialize(from, to = nil)\n      @from = convert_arg(from, true)\n      @to = convert_arg(to, false)\n      raise ArgumentError, \"'from' must be less or equal to 'to'. Got (#{@from}, #{@to}\" unless @from <= @to\n    end\n\n    # Checks if this numeric range intersects with another\n    #\n    # @param o [PNumericType] the range to compare with\n    # @return [Boolean] `true` if this range intersects with the other range\n    # @api public\n    def intersect?(o)\n      instance_of?(o.class) && !(@to < o.numeric_from || o.numeric_to < @from)\n    end\n\n    # Returns the lower bound of the numeric range or `nil` if no lower bound is set.\n    # @return [Float,Integer]\n    def from\n      @from == -Float::INFINITY ? nil : @from\n    end\n\n    # Returns the upper bound of the numeric range or `nil` if no upper bound is set.\n    # @return [Float,Integer]\n    def to\n      @to == Float::INFINITY ? nil : @to\n    end\n\n    # Same as #from but will return `-Float::Infinity` instead of `nil` if no lower bound is set.\n    # @return [Float,Integer]\n    def numeric_from\n      @from\n    end\n\n    # Same as #to but will return `Float::Infinity` instead of `nil` if no lower bound is set.\n    # @return [Float,Integer]\n    def numeric_to\n      @to\n    end\n\n    def hash\n      @from.hash ^ @to.hash\n    end\n\n    def eql?(o)\n      self.class == o.class && @from == o.numeric_from && @to == o.numeric_to\n    end\n\n    def unbounded?\n      @from == -Float::INFINITY && @to == Float::INFINITY\n    end\n\n    def convert_arg(arg, min)\n      case arg\n      when impl_class\n        arg\n      when Hash\n        impl_class.from_hash(arg)\n      when nil, :default\n        min ? -Float::INFINITY : Float::INFINITY\n      when String\n        impl_class.parse(arg)\n      when Integer\n        impl_class.new(arg * Time::NSECS_PER_SEC)\n      when Float\n        impl_class.new(arg * Time::NSECS_PER_SEC)\n      else\n        raise ArgumentError, \"Unable to create a #{impl_class.name} from a #{arg.class.name}\" unless arg.nil? || arg == :default\n\n        nil\n      end\n    end\n\n    # Concatenates this range with another range provided that the ranges intersect or\n    # are adjacent. When that's not the case, this method will return `nil`\n    #\n    # @param o [PAbstractTimeDataType] the range to concatenate with this range\n    # @return [PAbstractTimeDataType,nil] the concatenated range or `nil` when the ranges were apart\n    # @api public\n    def merge(o)\n      if intersect?(o) || adjacent?(o)\n        new_min = numeric_from <= o.numeric_from ? numeric_from : o.numeric_from\n        new_max = numeric_to >= o.numeric_to ? numeric_to : o.numeric_to\n        self.class.new(new_min, new_max)\n      else\n        nil\n      end\n    end\n\n    def _assignable?(o, guard)\n      instance_of?(o.class) && numeric_from <= o.numeric_from && numeric_to >= o.numeric_to\n    end\n  end\n\n  class PTimespanType < PAbstractTimeDataType\n    def self.register_ptype(loader, ir)\n      create_ptype(loader, ir, 'ScalarType',\n                   'from' => { KEY_TYPE => POptionalType.new(PTimespanType::DEFAULT), KEY_VALUE => nil },\n                   'to' => { KEY_TYPE => POptionalType.new(PTimespanType::DEFAULT), KEY_VALUE => nil })\n    end\n\n    def self.new_function(type)\n      @new_function ||= Puppet::Functions.create_loaded_function(:new_timespan, type.loader) do\n        local_types do\n          type 'Formats = Variant[String[2],Array[String[2], 1]]'\n        end\n\n        dispatch :from_seconds do\n          param           'Variant[Integer,Float]', :seconds\n        end\n\n        dispatch :from_string do\n          param           'String[1]', :string\n          optional_param  'Formats', :format\n        end\n\n        dispatch :from_fields do\n          param          'Integer', :days\n          param          'Integer', :hours\n          param          'Integer', :minutes\n          param          'Integer', :seconds\n          optional_param 'Integer', :milliseconds\n          optional_param 'Integer', :microseconds\n          optional_param 'Integer', :nanoseconds\n        end\n\n        dispatch :from_string_hash do\n          param <<-TYPE, :hash_arg\n            Struct[{\n              string => String[1],\n              Optional[format] => Formats\n            }]\n          TYPE\n        end\n\n        dispatch :from_fields_hash do\n          param <<-TYPE, :hash_arg\n            Struct[{\n              Optional[negative] => Boolean,\n              Optional[days] => Integer,\n              Optional[hours] => Integer,\n              Optional[minutes] => Integer,\n              Optional[seconds] => Integer,\n              Optional[milliseconds] => Integer,\n              Optional[microseconds] => Integer,\n              Optional[nanoseconds] => Integer\n            }]\n          TYPE\n        end\n\n        def from_seconds(seconds)\n          Time::Timespan.new((seconds * Time::NSECS_PER_SEC).to_i)\n        end\n\n        def from_string(string, format = Time::Timespan::Format::DEFAULTS)\n          Time::Timespan.parse(string, format)\n        end\n\n        def from_fields(days, hours, minutes, seconds, milliseconds = 0, microseconds = 0, nanoseconds = 0)\n          Time::Timespan.from_fields(false, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds)\n        end\n\n        def from_string_hash(args_hash)\n          Time::Timespan.from_string_hash(args_hash)\n        end\n\n        def from_fields_hash(args_hash)\n          Time::Timespan.from_fields_hash(args_hash)\n        end\n      end\n    end\n\n    def generalize\n      DEFAULT\n    end\n\n    def impl_class\n      Time::Timespan\n    end\n\n    def instance?(o, guard = nil)\n      o.is_a?(Time::Timespan) && o >= @from && o <= @to\n    end\n\n    DEFAULT = PTimespanType.new(nil, nil)\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_timestamp_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n  class PTimestampType < PAbstractTimeDataType\n    def self.register_ptype(loader, ir)\n      create_ptype(loader, ir, 'ScalarType',\n                   'from' => { KEY_TYPE => POptionalType.new(PTimestampType::DEFAULT), KEY_VALUE => nil },\n                   'to' => { KEY_TYPE => POptionalType.new(PTimestampType::DEFAULT), KEY_VALUE => nil })\n    end\n\n    def self.new_function(type)\n      @new_function ||= Puppet::Functions.create_loaded_function(:new_timestamp, type.loader) do\n        local_types do\n          type 'Formats = Variant[String[2],Array[String[2], 1]]'\n        end\n\n        dispatch :now do\n        end\n\n        dispatch :from_seconds do\n          param 'Variant[Integer,Float]', :seconds\n        end\n\n        dispatch :from_string do\n          param           'String[1]', :string\n          optional_param  'Formats',   :format\n          optional_param  'String[1]', :timezone\n        end\n\n        dispatch :from_string_hash do\n          param <<-TYPE, :hash_arg\n            Struct[{\n              string => String[1],\n              Optional[format] => Formats,\n              Optional[timezone] => String[1]\n            }]\n          TYPE\n        end\n\n        def now\n          Time::Timestamp.now\n        end\n\n        def from_string(string, format = :default, timezone = nil)\n          Time::Timestamp.parse(string, format, timezone)\n        end\n\n        def from_string_hash(args_hash)\n          Time::Timestamp.from_hash(args_hash)\n        end\n\n        def from_seconds(seconds)\n          Time::Timestamp.new((seconds * Time::NSECS_PER_SEC).to_i)\n        end\n      end\n    end\n\n    def generalize\n      DEFAULT\n    end\n\n    def impl_class\n      Time::Timestamp\n    end\n\n    def instance?(o, guard = nil)\n      o.is_a?(Time::Timestamp) && o >= @from && o <= @to\n    end\n\n    DEFAULT = PTimestampType.new(nil, nil)\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_type_set_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\nKEY_NAME_AUTHORITY = 'name_authority'\nKEY_TYPES = 'types'\nKEY_ALIAS = 'alias'\nKEY_VERSION = 'version'\nKEY_VERSION_RANGE = 'version_range'\nKEY_REFERENCES = 'references'\n\nclass PTypeSetType < PMetaType\n  # A Loader that makes the types known to the TypeSet visible\n  #\n  # @api private\n  class TypeSetLoader < Loader::BaseLoader\n    def initialize(type_set, parent)\n      super(parent, \"(TypeSetFirstLoader '#{type_set.name}')\", parent.environment)\n      @type_set = type_set\n    end\n\n    def name_authority\n      @type_set.name_authority\n    end\n\n    def model_loader\n      @type_set.loader\n    end\n\n    def find(typed_name)\n      if typed_name.type == :type && typed_name.name_authority == @type_set.name_authority\n        type = @type_set[typed_name.name]\n        return set_entry(typed_name, type) unless type.nil?\n      end\n      nil\n    end\n  end\n\n  TYPE_STRING_OR_VERSION = TypeFactory.variant(PStringType::NON_EMPTY, TypeFactory.sem_ver)\n  TYPE_STRING_OR_RANGE = TypeFactory.variant(PStringType::NON_EMPTY, TypeFactory.sem_ver_range)\n\n  TYPE_TYPE_REFERENCE_I12N =\n    TypeFactory\n    .struct({\n              KEY_NAME => Pcore::TYPE_QUALIFIED_REFERENCE,\n              KEY_VERSION_RANGE => TYPE_STRING_OR_RANGE,\n              TypeFactory.optional(KEY_NAME_AUTHORITY) => Pcore::TYPE_URI,\n              TypeFactory.optional(KEY_ANNOTATIONS) => TYPE_ANNOTATIONS\n            })\n\n  TYPE_TYPESET_I12N =\n    TypeFactory\n    .struct({\n              TypeFactory.optional(Pcore::KEY_PCORE_URI) => Pcore::TYPE_URI,\n              Pcore::KEY_PCORE_VERSION => TYPE_STRING_OR_VERSION,\n              TypeFactory.optional(KEY_NAME_AUTHORITY) => Pcore::TYPE_URI,\n              TypeFactory.optional(KEY_NAME) => Pcore::TYPE_QUALIFIED_REFERENCE,\n              TypeFactory.optional(KEY_VERSION) => TYPE_STRING_OR_VERSION,\n              TypeFactory.optional(KEY_TYPES) => TypeFactory.hash_kv(Pcore::TYPE_SIMPLE_TYPE_NAME, PVariantType.new([PTypeType::DEFAULT, PObjectType::TYPE_OBJECT_I12N]), PCollectionType::NOT_EMPTY_SIZE),\n              TypeFactory.optional(KEY_REFERENCES) => TypeFactory.hash_kv(Pcore::TYPE_SIMPLE_TYPE_NAME, TYPE_TYPE_REFERENCE_I12N, PCollectionType::NOT_EMPTY_SIZE),\n              TypeFactory.optional(KEY_ANNOTATIONS) => TYPE_ANNOTATIONS,\n            })\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType', '_pcore_init_hash' => TYPE_TYPESET_I12N.resolve(loader))\n  end\n\n  attr_reader :pcore_uri\n  attr_reader :pcore_version\n  attr_reader :name_authority\n  attr_reader :name\n  attr_reader :version\n  attr_reader :types\n  attr_reader :references\n  attr_reader :annotations\n\n  # Initialize a TypeSet Type instance. The initialization will use either a name and an initialization\n  # hash expression, or a fully resolved initialization hash.\n  #\n  # @overload initialize(name, init_hash_expression)\n  #   Used when the TypeSet type is loaded using a type alias expression. When that happens, it is important that\n  #   the actual resolution of the expression is deferred until all definitions have been made known to the current\n  #   loader. The package will then be resolved when it is loaded by the {TypeParser}. \"resolved\" here, means that\n  #   the hash expression is fully resolved, and then passed to the {#_pcore_init_from_hash} method.\n  #   @param name [String] The name of the type set\n  #   @param init_hash_expression [Model::LiteralHash] The hash describing the TypeSet features\n  #   @param name_authority [String] The default name authority for the type set\n  #\n  # @overload initialize(init_hash)\n  #   Used when the package is created by the {TypeFactory}. The init_hash must be fully resolved.\n  #   @param init_hash [Hash{String=>Object}] The hash describing the TypeSet features\n  #\n  # @api private\n  def initialize(name_or_init_hash, init_hash_expression = nil, name_authority = nil)\n    @types = EMPTY_HASH\n    @references = EMPTY_HASH\n\n    if name_or_init_hash.is_a?(Hash)\n      _pcore_init_from_hash(name_or_init_hash)\n    else\n      # Creation using \"type XXX = TypeSet[{}]\". This means that the name is given\n      @name = TypeAsserter.assert_instance_of('TypeSet name', Pcore::TYPE_QUALIFIED_REFERENCE, name_or_init_hash)\n      @name_authority = TypeAsserter.assert_instance_of('TypeSet name_authority', Pcore::TYPE_URI, name_authority, true)\n      @init_hash_expression = init_hash_expression\n    end\n  end\n\n  # @api private\n  def _pcore_init_from_hash(init_hash)\n    TypeAsserter.assert_instance_of('TypeSet initializer', TYPE_TYPESET_I12N, init_hash)\n\n    # Name given to the loader have higher precedence than a name declared in the type\n    @name ||= init_hash[KEY_NAME].freeze\n    @name_authority ||= init_hash[KEY_NAME_AUTHORITY].freeze\n\n    @pcore_version = PSemVerType.convert(init_hash[Pcore::KEY_PCORE_VERSION]).freeze\n    unless Pcore::PARSABLE_PCORE_VERSIONS.include?(@pcore_version)\n      raise ArgumentError,\n            \"The pcore version for TypeSet '#{@name}' is not understood by this runtime. Expected range #{Pcore::PARSABLE_PCORE_VERSIONS}, got #{@pcore_version}\"\n    end\n\n    @pcore_uri = init_hash[Pcore::KEY_PCORE_URI].freeze\n    @version = PSemVerType.convert(init_hash[KEY_VERSION])\n    @types = init_hash[KEY_TYPES] || EMPTY_HASH\n    @types.freeze\n\n    # Map downcase names to their camel-cased equivalent\n    @dc_to_cc_map = {}\n    @types.keys.each { |key| @dc_to_cc_map[key.downcase] = key }\n\n    refs = init_hash[KEY_REFERENCES]\n    if refs.nil?\n      @references = EMPTY_HASH\n    else\n      ref_map = {}\n      root_map = Hash.new { |h, k| h[k] = {} }\n      refs.each do |ref_alias, ref|\n        ref = TypeSetReference.new(self, ref)\n\n        # Protect against importing the exact same name_authority/name combination twice if the version ranges intersect\n        ref_name = ref.name\n        ref_na = ref.name_authority || @name_authority\n        na_roots = root_map[ref_na]\n\n        ranges = na_roots[ref_name]\n        if ranges.nil?\n          na_roots[ref_name] = [ref.version_range]\n        else\n          unless ranges.all? { |range| (range & ref.version_range).nil? }\n            raise ArgumentError, \"TypeSet '#{@name}' references TypeSet '#{ref_na}/#{ref_name}' more than once using overlapping version ranges\"\n          end\n\n          ranges << ref.version_range\n        end\n\n        if ref_map.has_key?(ref_alias)\n          raise ArgumentError, \"TypeSet '#{@name}' references a TypeSet using alias '#{ref_alias}' more than once\"\n        end\n        if @types.has_key?(ref_alias)\n          raise ArgumentError, \"TypeSet '#{@name}' references a TypeSet using alias '#{ref_alias}'. The alias collides with the name of a declared type\"\n        end\n\n        ref_map[ref_alias] = ref\n\n        @dc_to_cc_map[ref_alias.downcase] = ref_alias\n        ref_map[ref_alias] = ref\n      end\n      @references = ref_map.freeze\n    end\n    @dc_to_cc_map.freeze\n    init_annotatable(init_hash)\n  end\n\n  # Produce a hash suitable for the initializer\n  # @return [Hash{String => Object}] the initialization hash\n  #\n  # @api private\n  def _pcore_init_hash\n    result = super()\n    result[Pcore::KEY_PCORE_URI] = @pcore_uri unless @pcore_uri.nil?\n    result[Pcore::KEY_PCORE_VERSION] = @pcore_version.to_s\n    result[KEY_NAME_AUTHORITY] = @name_authority unless @name_authority.nil?\n    result[KEY_NAME] = @name\n    result[KEY_VERSION] = @version.to_s unless @version.nil?\n    result[KEY_TYPES] = @types unless @types.empty?\n    result[KEY_REFERENCES] = @references.transform_values(&:_pcore_init_hash) unless @references.empty?\n    result\n  end\n\n  # Resolve a type in this type set using a qualified name. The resolved type may either be a type defined in this type set\n  # or a type defined in a type set that is referenced by this type set (nesting may occur to any level).\n  # The name resolution is case insensitive.\n  #\n  # @param qname [String,Loader::TypedName] the qualified name of the type to resolve\n  # @return [PAnyType,nil] the resolved type, or `nil` in case no type could be found\n  #\n  # @api public\n  def [](qname)\n    if qname.is_a?(Loader::TypedName)\n      return nil unless qname.type == :type && qname.name_authority == @name_authority\n\n      qname = qname.name\n    end\n\n    type = @types[qname] || @types[@dc_to_cc_map[qname.downcase]]\n    if type.nil? && !@references.empty?\n      segments = qname.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n      first = segments[0]\n      type_set_ref = @references[first] || @references[@dc_to_cc_map[first.downcase]]\n      if type_set_ref.nil?\n        nil\n      else\n        type_set = type_set_ref.type_set\n        case segments.size\n        when 1\n          type_set\n        when 2\n          type_set[segments[1]]\n        else\n          segments.shift\n          type_set[segments.join(TypeFormatter::NAME_SEGMENT_SEPARATOR)]\n        end\n      end\n    else\n      type\n    end\n  end\n\n  def defines_type?(t)\n    !@types.key(t).nil?\n  end\n\n  # Returns the name by which the given type is referenced from within this type set\n  # @param t [PAnyType]\n  # @return [String] the name by which the type is referenced within this type set\n  #\n  # @api private\n  def name_for(t, default_name)\n    key = @types.key(t)\n    if key.nil?\n      unless @references.empty?\n        @references.each_pair do |ref_key, ref|\n          ref_name = ref.type_set.name_for(t, nil)\n          return \"#{ref_key}::#{ref_name}\" unless ref_name.nil?\n        end\n      end\n      default_name\n    else\n      key\n    end\n  end\n\n  def accept(visitor, guard)\n    super\n    @types.each_value { |type| type.accept(visitor, guard) }\n    @references.each_value { |ref| ref.accept(visitor, guard) }\n  end\n\n  # @api private\n  def label\n    \"TypeSet '#{@name}'\"\n  end\n\n  # @api private\n  def resolve(loader)\n    super\n    @references.each_value { |ref| ref.resolve(loader) }\n    tsa_loader = TypeSetLoader.new(self, loader)\n    @types.values.each { |type| type.resolve(tsa_loader) }\n    self\n  end\n\n  # @api private\n  def resolve_literal_hash(loader, init_hash_expression)\n    result = {}\n    type_parser = TypeParser.singleton\n    init_hash_expression.entries.each do |entry|\n      key = type_parser.interpret_any(entry.key, loader)\n      if (key == KEY_TYPES || key == KEY_REFERENCES) && entry.value.is_a?(Model::LiteralHash)\n        # Skip type parser interpretation and convert qualified references directly to String keys.\n        hash = {}\n        entry.value.entries.each do |he|\n          kex = he.key\n          name = kex.is_a?(Model::QualifiedReference) ? kex.cased_value : type_parser.interpret_any(kex, loader)\n          hash[name] = key == KEY_TYPES ? he.value : type_parser.interpret_any(he.value, loader)\n        end\n        result[key] = hash\n      else\n        result[key] = type_parser.interpret_any(entry.value, loader)\n      end\n    end\n\n    name_auth = resolve_name_authority(result, loader)\n\n    types = result[KEY_TYPES]\n    if types.is_a?(Hash)\n      types.each do |type_name, value|\n        full_name = \"#{@name}::#{type_name}\"\n        typed_name = Loader::TypedName.new(:type, full_name, name_auth)\n        if value.is_a?(Model::ResourceDefaultsExpression)\n          # This is actually a <Parent> { <key-value entries> } notation. Convert to a literal hash that contains the parent\n          n = value.type_ref\n          name = n.cased_value\n          entries = []\n          unless name == 'Object' or name == 'TypeSet'\n            if value.operations.any? { |op| op.attribute_name == KEY_PARENT }\n              case Puppet[:strict]\n              when :warning\n                IssueReporter.warning(value, Issues::DUPLICATE_KEY, :key => KEY_PARENT)\n              when :error\n                IssueReporter.error(Puppet::ParseErrorWithIssue, value, Issues::DUPLICATE_KEY, :key => KEY_PARENT)\n              end\n            end\n            entries << Model::KeyedEntry.new(n.locator, n.offset, n.length, KEY_PARENT, n)\n          end\n          value.operations.each { |op| entries << Model::KeyedEntry.new(op.locator, op.offset, op.length, op.attribute_name, op.value_expr) }\n          value = Model::LiteralHash.new(value.locator, value.offset, value.length, entries)\n        end\n        type = Loader::TypeDefinitionInstantiator.create_type(full_name, value, name_auth)\n        loader.set_entry(typed_name, type, value.locator.to_uri(value))\n        types[type_name] = type\n      end\n    end\n    result\n  end\n\n  # @api private\n  def resolve_hash(loader, init_hash)\n    result = init_hash.to_h do |key, value|\n      key = resolve_type_refs(loader, key)\n      value = resolve_type_refs(loader, value) unless key == KEY_TYPES && value.is_a?(Hash)\n      [key, value]\n    end\n    name_auth = resolve_name_authority(result, loader)\n    types = result[KEY_TYPES]\n    if types.is_a?(Hash)\n      types.each do |type_name, value|\n        full_name = \"#{@name}::#{type_name}\"\n        typed_name = Loader::TypedName.new(:type, full_name, name_auth)\n        meta_name = value.is_a?(Hash) ? 'Object' : 'TypeAlias'\n        type = Loader::TypeDefinitionInstantiator.create_named_type(full_name, meta_name, value, name_auth)\n        loader.set_entry(typed_name, type)\n        types[type_name] = type\n      end\n    end\n    result\n  end\n\n  def hash\n    @name_authority.hash ^ @name.hash ^ @version.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @name_authority == o.name_authority && @name == o.name && @version == o.version\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(PTypeSetType)\n  end\n\n  DEFAULT =\n\n    new({\n          KEY_NAME => 'DefaultTypeSet',\n          KEY_NAME_AUTHORITY => Pcore::RUNTIME_NAME_AUTHORITY,\n          Pcore::KEY_PCORE_URI => Pcore::PCORE_URI,\n          Pcore::KEY_PCORE_VERSION => Pcore::PCORE_VERSION,\n          KEY_VERSION => SemanticPuppet::Version.new(0, 0, 0)\n        })\n\n  protected\n\n  # @api_private\n  def _assignable?(o, guard)\n    instance_of?(o.class) && (self == DEFAULT || eql?(o))\n  end\n\n  private\n\n  def resolve_name_authority(init_hash, loader)\n    name_auth = @name_authority\n    if name_auth.nil?\n      name_auth = init_hash[KEY_NAME_AUTHORITY]\n      name_auth = loader.name_authority if name_auth.nil? && loader.is_a?(TypeSetLoader)\n      if name_auth.nil?\n        name = @name || init_hash[KEY_NAME]\n        raise ArgumentError, \"No 'name_authority' is declared in TypeSet '#{name}' and it cannot be inferred\"\n      end\n    end\n    name_auth\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/p_uri_type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\nclass PURIType < PAnyType\n  # Tell evaluator that an members of instances of this type can be invoked using dot notation\n  include TypeWithMembers\n\n  SCHEME = 'scheme'\n  USERINFO = 'userinfo'\n  HOST = 'host'\n  PORT = 'port'\n  PATH = 'path'\n  QUERY = 'query'\n  FRAGMENT = 'fragment'\n  OPAQUE = 'opaque'\n\n  URI_MEMBERS = {\n    SCHEME => AttrReader.new(SCHEME),\n    USERINFO => AttrReader.new(USERINFO),\n    HOST => AttrReader.new(HOST),\n    PORT => AttrReader.new(PORT),\n    PATH => AttrReader.new(PATH),\n    QUERY => AttrReader.new(QUERY),\n    FRAGMENT => AttrReader.new(FRAGMENT),\n    OPAQUE => AttrReader.new(OPAQUE),\n  }\n\n  TYPE_URI_INIT_HASH = TypeFactory.struct(\n    TypeFactory.optional(SCHEME) => PStringType::NON_EMPTY,\n    TypeFactory.optional(USERINFO) => PStringType::NON_EMPTY,\n    TypeFactory.optional(HOST) => PStringType::NON_EMPTY,\n    TypeFactory.optional(PORT) => PIntegerType.new(0),\n    TypeFactory.optional(PATH) => PStringType::NON_EMPTY,\n    TypeFactory.optional(QUERY) => PStringType::NON_EMPTY,\n    TypeFactory.optional(FRAGMENT) => PStringType::NON_EMPTY,\n    TypeFactory.optional(OPAQUE) => PStringType::NON_EMPTY\n  )\n\n  TYPE_STRING_PARAM =\n    TypeFactory\n    .optional(PVariantType\n        .new([\n               PStringType::NON_EMPTY,\n               PRegexpType::DEFAULT,\n               TypeFactory.type_type(PPatternType::DEFAULT),\n               TypeFactory.type_type(PEnumType::DEFAULT),\n               TypeFactory.type_type(PNotUndefType::DEFAULT),\n               TypeFactory.type_type(PUndefType::DEFAULT)\n             ]))\n\n  TYPE_INTEGER_PARAM =\n    TypeFactory\n    .optional(PVariantType\n        .new([\n               PIntegerType.new(0),\n               TypeFactory.type_type(PNotUndefType::DEFAULT),\n               TypeFactory.type_type(PUndefType::DEFAULT)\n             ]))\n\n  TYPE_URI_PARAM_HASH_TYPE = TypeFactory.struct(\n    TypeFactory.optional(SCHEME) => TYPE_STRING_PARAM,\n    TypeFactory.optional(USERINFO) => TYPE_STRING_PARAM,\n    TypeFactory.optional(HOST) => TYPE_STRING_PARAM,\n    TypeFactory.optional(PORT) => TYPE_INTEGER_PARAM,\n    TypeFactory.optional(PATH) => TYPE_STRING_PARAM,\n    TypeFactory.optional(QUERY) => TYPE_STRING_PARAM,\n    TypeFactory.optional(FRAGMENT) => TYPE_STRING_PARAM,\n    TypeFactory.optional(OPAQUE) => TYPE_STRING_PARAM\n  )\n\n  TYPE_URI_PARAM_TYPE = PVariantType.new([PStringType::NON_EMPTY, TYPE_URI_PARAM_HASH_TYPE])\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 {\n                   'parameters' => { KEY_TYPE => TypeFactory.optional(TYPE_URI_PARAM_TYPE), KEY_VALUE => nil }\n                 })\n  end\n\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_error, type.loader) do\n      dispatch :create do\n        param 'String[1]', :uri\n      end\n\n      dispatch :from_hash do\n        param TYPE_URI_INIT_HASH, :hash\n      end\n\n      def create(uri)\n        URI.parse(uri)\n      end\n\n      def from_hash(init_hash)\n        sym_hash = {}\n        init_hash.each_pair { |k, v| sym_hash[k.to_sym] = v }\n        scheme = sym_hash[:scheme]\n        scheme_class = scheme.nil? ? URI::Generic : (URI.scheme_list[scheme.upcase] || URI::Generic)\n        scheme_class.build(sym_hash)\n      end\n    end\n  end\n\n  attr_reader :parameters\n\n  def initialize(parameters = nil)\n    case parameters\n    when String\n      parameters = TypeAsserter.assert_instance_of('URI-Type parameter', Pcore::TYPE_URI, parameters, true)\n      @parameters = uri_to_hash(URI.parse(parameters))\n    when URI\n      @parameters = uri_to_hash(parameters)\n    when Hash\n      params = TypeAsserter.assert_instance_of('URI-Type parameter', TYPE_URI_PARAM_TYPE, parameters, true)\n      @parameters = params.empty? ? nil : params\n    end\n  end\n\n  def eql?(o)\n    self.class == o.class && @parameters == o.parameters\n  end\n\n  def ==(o)\n    eql?(o)\n  end\n\n  def [](key)\n    URI_MEMBERS[key]\n  end\n\n  def generalize\n    DEFAULT\n  end\n\n  def hash\n    self.class.hash ^ @parameters.hash\n  end\n\n  def instance?(o, guard = nil)\n    return false unless o.is_a?(URI)\n    return true if @parameters.nil?\n\n    eval = Parser::EvaluatingParser.singleton.evaluator\n    @parameters.keys.all? { |pn| eval.match?(o.send(pn), @parameters[pn]) }\n  end\n\n  def roundtrip_with_string?\n    true\n  end\n\n  def _pcore_init_hash\n    @parameters == nil? ? EMPTY_HASH : { 'parameters' => @parameters }\n  end\n\n  protected\n\n  def _assignable?(o, guard = nil)\n    return false unless o.instance_of?(self.class)\n    return true if @parameters.nil?\n\n    o_params = o.parameters || EMPTY_HASH\n\n    eval = Parser::EvaluatingParser.singleton.evaluator\n    @parameters.keys.all? do |pn|\n      if o_params.include?(pn)\n        a = o_params[pn]\n        b = @parameters[pn]\n        eval.match?(a, b) || a.is_a?(PAnyType) && b.is_a?(PAnyType) && b.assignable?(a)\n      else\n        false\n      end\n    end\n  end\n\n  private\n\n  def uri_to_hash(uri)\n    result = {}\n    scheme = uri.scheme\n    unless scheme.nil?\n      scheme = scheme.downcase\n      result[SCHEME] = scheme\n    end\n    result[USERINFO] = uri.userinfo unless uri.userinfo.nil?\n    result[HOST] = uri.host.downcase unless uri.host.nil? || uri.host.empty?\n    result[PORT] = uri.port.to_s unless uri.port.nil? || uri.port == 80 && 'http' == scheme || uri.port == 443 && 'https' == scheme\n    result[PATH] = uri.path unless uri.path.nil? || uri.path.empty?\n    result[QUERY] = uri.query unless uri.query.nil?\n    result[FRAGMENT] = uri.fragment unless uri.fragment.nil?\n    result[OPAQUE] = uri.opaque unless uri.opaque.nil?\n    result.empty? ? nil : result\n  end\n\n  DEFAULT = PURIType.new(nil)\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/puppet_object.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# Marker module for implementations that are mapped to Object types\n# @api public\nmodule PuppetObject\n  # Returns the Puppet Type for this instance. The implementing class must\n  # add the {#_pcore_type} as a class method.\n  #\n  # @return [PObjectType] the type\n  def _pcore_type\n    t = self.class._pcore_type\n    if t.parameterized?\n      unless instance_variable_defined?(:@_cached_ptype)\n        # Create a parameterized type based on the values of this instance that\n        # contains a parameter value for each type parameter that matches an\n        # attribute by name and type of value\n        @_cached_ptype = PObjectTypeExtension.create_from_instance(t, self)\n      end\n      t = @_cached_ptype\n    end\n    t\n  end\n\n  def _pcore_all_contents(path, &block)\n  end\n\n  def _pcore_contents\n  end\n\n  def _pcore_init_hash\n    {}\n  end\n\n  def to_s\n    TypeFormatter.string(self)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/recursion_guard.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# Keeps track of self recursion of conceptual 'this' and 'that' instances using two separate maps and\n# a state. The class is used when tracking self recursion in two objects ('this' and 'that') simultaneously.\n# A typical example of when this is needed is when testing if 'that' Puppet Type is assignable to 'this'\n# Puppet Type since both types may contain self references.\n#\n# All comparisons are made using the `object_id` of the instance rather than the instance itself.\n#\n# Hash#compare_by_identity is intentionally not used since we only need to keep\n# track of object ids we've seen before, based on object_id, so we don't store\n# entire objects in memory.\n#\n# @api private\nclass RecursionGuard\n  attr_reader :state\n\n  NO_SELF_RECURSION = 0\n  SELF_RECURSION_IN_THIS = 1\n  SELF_RECURSION_IN_THAT = 2\n  SELF_RECURSION_IN_BOTH = 3\n\n  def initialize\n    @state = NO_SELF_RECURSION\n  end\n\n  # Checks if recursion was detected for the given argument in the 'this' context\n  # @param instance [Object] the instance to check\n  # @return [Integer] the resulting state\n  def recursive_this?(instance)\n    instance_variable_defined?(:@recursive_this_map) && @recursive_this_map.has_key?(instance.object_id) # rubocop:disable Lint/HashCompareByIdentity\n  end\n\n  # Checks if recursion was detected for the given argument in the 'that' context\n  # @param instance [Object] the instance to check\n  # @return [Integer] the resulting state\n  def recursive_that?(instance)\n    instance_variable_defined?(:@recursive_that_map) && @recursive_that_map.has_key?(instance.object_id) # rubocop:disable Lint/HashCompareByIdentity\n  end\n\n  # Add the given argument as 'this' invoke the given block with the resulting state\n  # @param instance [Object] the instance to add\n  # @return [Object] the result of yielding\n  def with_this(instance)\n    if (@state & SELF_RECURSION_IN_THIS) == 0\n      tc = this_count\n      @state |= SELF_RECURSION_IN_THIS if this_put(instance)\n      if tc < this_count\n        # recursive state detected\n        result = yield(@state)\n\n        # pop state\n        @state &= ~SELF_RECURSION_IN_THIS\n        @this_map.delete(instance.object_id)\n        return result\n      end\n    end\n    yield(@state)\n  end\n\n  # Add the given argument as 'that' invoke the given block with the resulting state\n  # @param instance [Object] the instance to add\n  # @return [Object] the result of yielding\n  def with_that(instance)\n    if (@state & SELF_RECURSION_IN_THAT) == 0\n      tc = that_count\n      @state |= SELF_RECURSION_IN_THAT if that_put(instance)\n      if tc < that_count\n        # recursive state detected\n        result = yield(@state)\n\n        # pop state\n        @state &= ~SELF_RECURSION_IN_THAT\n        @that_map.delete(instance.object_id)\n        return result\n      end\n    end\n    yield(@state)\n  end\n\n  # Add the given argument as 'this' and return the resulting state\n  # @param instance [Object] the instance to add\n  # @return [Integer] the resulting state\n  def add_this(instance)\n    if (@state & SELF_RECURSION_IN_THIS) == 0\n      @state |= SELF_RECURSION_IN_THIS if this_put(instance)\n    end\n    @state\n  end\n\n  # Add the given argument as 'that' and return the resulting state\n  # @param instance [Object] the instance to add\n  # @return [Integer] the resulting state\n  def add_that(instance)\n    if (@state & SELF_RECURSION_IN_THAT) == 0\n      @state |= SELF_RECURSION_IN_THAT if that_put(instance)\n    end\n    @state\n  end\n\n  # @return the number of objects added to the `this` map\n  def this_count\n    instance_variable_defined?(:@this_map) ? @this_map.size : 0\n  end\n\n  # @return the number of objects added to the `that` map\n  def that_count\n    instance_variable_defined?(:@that_map) ? @that_map.size : 0\n  end\n\n  private\n\n  def this_put(o)\n    id = o.object_id\n    @this_map ||= {}\n    if @this_map.has_key?(id)\n      @recursive_this_map ||= {}\n      @recursive_this_map[id] = true\n      true\n    else\n      @this_map[id] = true\n      false\n    end\n  end\n\n  def that_put(o)\n    id = o.object_id\n    @that_map ||= {}\n    if @that_map.has_key?(id)\n      @recursive_that_map ||= {}\n      @recursive_that_map[id] = true\n      true\n    else\n      @that_map[id] = true\n      false\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/ruby_generator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# @api private\nclass RubyGenerator < TypeFormatter\n  RUBY_RESERVED_WORDS = {\n    'alias' => '_alias',\n    'begin' => '_begin',\n    'break' => '_break',\n    'def' => '_def',\n    'do' => '_do',\n    'end' => '_end',\n    'ensure' => '_ensure',\n    'for' => '_for',\n    'module' => '_module',\n    'next' => '_next',\n    'nil' => '_nil',\n    'not' => '_not',\n    'redo' => '_redo',\n    'rescue' => '_rescue',\n    'retry' => '_retry',\n    'return' => '_return',\n    'self' => '_self',\n    'super' => '_super',\n    'then' => '_then',\n    'until' => '_until',\n    'when' => '_when',\n    'while' => '_while',\n    'yield' => '_yield',\n  }\n\n  RUBY_RESERVED_WORDS_REVERSED = RUBY_RESERVED_WORDS.to_h { |k, v| [v, k] }\n\n  def self.protect_reserved_name(name)\n    RUBY_RESERVED_WORDS[name] || name\n  end\n\n  def self.unprotect_reserved_name(name)\n    RUBY_RESERVED_WORDS_REVERSED[name] || name\n  end\n\n  def remove_common_namespace(namespace_segments, name)\n    segments = name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n    namespace_segments.size.times do |idx|\n      break if segments.empty? || namespace_segments[idx] != segments[0]\n\n      segments.shift\n    end\n    segments\n  end\n\n  def namespace_relative(namespace_segments, name)\n    remove_common_namespace(namespace_segments, name).join(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n  end\n\n  def create_class(obj)\n    @dynamic_classes ||= Hash.new do |hash, key|\n      cls = key.implementation_class(false)\n      if cls.nil?\n        rp = key.resolved_parent\n        parent_class = rp.is_a?(PObjectType) ? rp.implementation_class : Object\n        class_def = ''.dup\n        class_body(key, EMPTY_ARRAY, class_def)\n        cls = Class.new(parent_class)\n        cls.class_eval(class_def)\n        cls.define_singleton_method(:_pcore_type) { key }\n        key.implementation_class = cls\n      end\n      hash[key] = cls\n    end\n    raise ArgumentError, \"Expected a Puppet Type, got '#{obj.class.name}'\" unless obj.is_a?(PAnyType)\n\n    @dynamic_classes[obj]\n  end\n\n  def module_definition_from_typeset(typeset, *impl_subst)\n    module_definition(\n      typeset.types.values,\n      \"# Generated by #{self.class.name} from TypeSet #{typeset.name} on #{Date.new}\\n\",\n      *impl_subst\n    )\n  end\n\n  def module_definition(types, comment, *impl_subst)\n    object_types, aliased_types = types.partition { |type| type.is_a?(PObjectType) }\n    if impl_subst.empty?\n      impl_names = implementation_names(object_types)\n    else\n      impl_names = object_types.map { |type| type.name.gsub(*impl_subst) }\n    end\n\n    # extract common implementation module prefix\n    names_by_prefix = Hash.new { |hash, key| hash[key] = [] }\n    index = 0\n    min_prefix_length = impl_names.reduce(Float::INFINITY) do |len, impl_name|\n      segments = impl_name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n      leaf_name = segments.pop\n      names_by_prefix[segments.freeze] << [index, leaf_name, impl_name]\n      index += 1\n      len > segments.size ? segments.size : len\n    end\n    min_prefix_length = 0 if min_prefix_length == Float::INFINITY\n\n    common_prefix = []\n    segments_array = names_by_prefix.keys\n    min_prefix_length.times do |idx|\n      segment = segments_array[0][idx]\n      break unless segments_array.all? { |sn| sn[idx] == segment }\n\n      common_prefix << segment\n    end\n\n    # Create class definition of all contained types\n    bld = ''.dup\n    start_module(common_prefix, comment, bld)\n    class_names = []\n    names_by_prefix.each_pair do |seg_array, index_and_name_array|\n      added_to_common_prefix = seg_array[common_prefix.length..]\n      added_to_common_prefix.each { |name| bld << 'module ' << name << \"\\n\" }\n      index_and_name_array.each do |idx, name, full_name|\n        scoped_class_definition(object_types[idx], name, bld, full_name, *impl_subst)\n        class_names << (added_to_common_prefix + [name]).join(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n        bld << \"\\n\"\n      end\n      added_to_common_prefix.size.times { bld << \"end\\n\" }\n    end\n\n    aliases = aliased_types.to_h { |type| [type.name, type.resolved_type] }\n    end_module(common_prefix, aliases, class_names, bld)\n    bld\n  end\n\n  def start_module(common_prefix, comment, bld)\n    bld << '# ' << comment << \"\\n\"\n    common_prefix.each { |cp| bld << 'module ' << cp << \"\\n\" }\n  end\n\n  def end_module(common_prefix, aliases, class_names, bld)\n    # Emit registration of contained type aliases\n    unless aliases.empty?\n      bld << \"Puppet::Pops::Pcore.register_aliases({\\n\"\n      aliases.each { |name, type| bld << \"  '\" << name << \"' => \" << TypeFormatter.string(type.to_s) << \"\\n\" }\n      bld.chomp!(\",\\n\")\n      bld << \"})\\n\\n\"\n    end\n\n    # Emit registration of contained types\n    unless class_names.empty?\n      bld << \"Puppet::Pops::Pcore.register_implementations([\\n\"\n      class_names.each { |class_name| bld << '  ' << class_name << \",\\n\" }\n      bld.chomp!(\",\\n\")\n      bld << \"])\\n\\n\"\n    end\n    bld.chomp!(\"\\n\")\n\n    common_prefix.size.times { bld << \"end\\n\" }\n  end\n\n  def implementation_names(object_types)\n    object_types.map do |type|\n      ir = Loaders.implementation_registry\n      impl_name = ir.module_name_for_type(type)\n      raise Puppet::Error, \"Unable to create an instance of #{type.name}. No mapping exists to runtime object\" if impl_name.nil?\n\n      impl_name\n    end\n  end\n\n  def class_definition(obj, namespace_segments, bld, class_name, *impl_subst)\n    module_segments = remove_common_namespace(namespace_segments, class_name)\n    leaf_name = module_segments.pop\n    module_segments.each { |segment| bld << 'module ' << segment << \"\\n\" }\n    scoped_class_definition(obj, leaf_name, bld, class_name, *impl_subst)\n    module_segments.size.times { bld << \"end\\n\" }\n    module_segments << leaf_name\n    module_segments.join(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n  end\n\n  def scoped_class_definition(obj, leaf_name, bld, class_name, *impl_subst)\n    bld << 'class ' << leaf_name\n    segments = class_name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n\n    unless obj.parent.nil?\n      if impl_subst.empty?\n        ir = Loaders.implementation_registry\n        parent_name = ir.module_name_for_type(obj.parent)\n        raise Puppet::Error, \"Unable to create an instance of #{obj.parent.name}. No mapping exists to runtime object\" if parent_name.nil?\n      else\n        parent_name = obj.parent.name.gsub(*impl_subst)\n      end\n      bld << ' < ' << namespace_relative(segments, parent_name)\n    end\n\n    bld << \"\\n\"\n    bld << \"  def self._pcore_type\\n\"\n    bld << '    @_pcore_type ||= ' << namespace_relative(segments, obj.class.name) << \".new('\" << obj.name << \"', \"\n    bld << TypeFormatter.singleton.ruby('ref').indented(2).string(obj._pcore_init_hash(false)) << \")\\n\"\n    bld << \"  end\\n\"\n\n    class_body(obj, segments, bld)\n\n    bld << \"end\\n\"\n  end\n\n  def class_body(obj, segments, bld)\n    unless obj.parent.is_a?(PObjectType)\n      bld << \"\\n  include \" << namespace_relative(segments, Puppet::Pops::Types::PuppetObject.name) << \"\\n\\n\" # marker interface\n      bld << \"  def self.ref(type_string)\\n\"\n      bld << '    ' << namespace_relative(segments, Puppet::Pops::Types::PTypeReferenceType.name) << \".new(type_string)\\n\"\n      bld << \"  end\\n\"\n    end\n\n    # Output constants\n    constants, others = obj.attributes(true).values.partition { |a| a.kind == PObjectType::ATTRIBUTE_KIND_CONSTANT }\n    constants = constants.select { |ca| ca.container.equal?(obj) }\n    unless constants.empty?\n      constants.each { |ca| bld << \"\\n  def self.\" << rname(ca.name) << \"\\n    _pcore_type['\" << ca.name << \"'].value\\n  end\\n\" }\n      constants.each { |ca| bld << \"\\n  def \" << rname(ca.name) << \"\\n    self.class.\" << ca.name << \"\\n  end\\n\" }\n    end\n\n    init_params = others.reject { |a| a.kind == PObjectType::ATTRIBUTE_KIND_DERIVED }\n    opt, non_opt = init_params.partition(&:value?)\n    derived_attrs, obj_attrs = others.select { |a| a.container.equal?(obj) }.partition { |ip| ip.kind == PObjectType::ATTRIBUTE_KIND_DERIVED }\n\n    include_type = obj.equality_include_type? && !(obj.parent.is_a?(PObjectType) && obj.parent.equality_include_type?)\n    if obj.equality.nil?\n      eq_names = obj_attrs.reject { |a| a.kind == PObjectType::ATTRIBUTE_KIND_CONSTANT }.map(&:name)\n    else\n      eq_names = obj.equality\n    end\n\n    # Output type safe hash constructor\n    bld << \"\\n  def self.from_hash(init_hash)\\n\"\n    bld << '    from_asserted_hash(' << namespace_relative(segments, TypeAsserter.name) << '.assert_instance_of('\n    bld << \"'\" << obj.label << \" initializer', _pcore_type.init_hash_type, init_hash))\\n  end\\n\\n  def self.from_asserted_hash(init_hash)\\n    new\"\n    unless non_opt.empty? && opt.empty?\n      bld << \"(\\n\"\n      non_opt.each { |ip| bld << \"      init_hash['\" << ip.name << \"'],\\n\" }\n      opt.each do |ip|\n        if ip.value.nil?\n          bld << \"      init_hash['\" << ip.name << \"'],\\n\"\n        else\n          bld << \"      init_hash.fetch('\" << ip.name << \"') { \"\n          default_string(bld, ip)\n          bld << \" },\\n\"\n        end\n      end\n      bld.chomp!(\",\\n\")\n      bld << ')'\n    end\n    bld << \"\\n  end\\n\"\n\n    # Output type safe constructor\n    bld << \"\\n  def self.create\"\n    if init_params.empty?\n      bld << \"\\n    new\"\n    else\n      bld << '('\n      non_opt.each { |ip| bld << rname(ip.name) << ', ' }\n      opt.each do |ip|\n        bld << rname(ip.name) << ' = '\n        default_string(bld, ip)\n        bld << ', '\n      end\n      bld.chomp!(', ')\n      bld << \")\\n\"\n      bld << '    ta = ' << namespace_relative(segments, TypeAsserter.name) << \"\\n\"\n      bld << \"    attrs = _pcore_type.attributes(true)\\n\"\n      init_params.each do |a|\n        bld << \"    ta.assert_instance_of('\" << a.container.name << '[' << a.name << ']'\n        bld << \"', attrs['\" << a.name << \"'].type, \" << rname(a.name) << \")\\n\"\n      end\n      bld << '    new('\n      non_opt.each { |a| bld << rname(a.name) << ', ' }\n      opt.each { |a| bld << rname(a.name) << ', ' }\n      bld.chomp!(', ')\n      bld << ')'\n    end\n    bld << \"\\n  end\\n\"\n\n    unless obj.parent.is_a?(PObjectType) && obj_attrs.empty?\n      # Output attr_readers\n      unless obj_attrs.empty?\n        bld << \"\\n\"\n        obj_attrs.each { |a| bld << '  attr_reader :' << rname(a.name) << \"\\n\" }\n      end\n\n      bld << \"  attr_reader :hash\\n\" if obj.parent.nil?\n\n      derived_attrs.each do |a|\n        bld << \"\\n  def \" << rname(a.name) << \"\\n\"\n        code_annotation = RubyMethod.annotate(a)\n        ruby_body = code_annotation.nil? ? nil : code_annotation.body\n        if ruby_body.nil?\n          bld << \"    raise Puppet::Error, \\\"no method is implemented for derived #{a.label}\\\"\\n\"\n        else\n          bld << '    ' << ruby_body << \"\\n\"\n        end\n        bld << \"  end\\n\"\n      end\n\n      if init_params.empty?\n        bld << \"\\n  def initialize\\n    @hash = \" << obj.hash.to_s << \"\\n  end\" if obj.parent.nil?\n      else\n        # Output initializer\n        bld << \"\\n  def initialize\"\n        bld << '('\n        non_opt.each { |ip| bld << rname(ip.name) << ', ' }\n        opt.each do |ip|\n          bld << rname(ip.name) << ' = '\n          default_string(bld, ip)\n          bld << ', '\n        end\n        bld.chomp!(', ')\n        bld << ')'\n\n        hash_participants = init_params.select { |ip| eq_names.include?(ip.name) }\n        if obj.parent.nil?\n          bld << \"\\n    @hash = \"\n          bld << obj.hash.to_s << \"\\n\" if hash_participants.empty?\n        else\n          bld << \"\\n    super(\"\n          super_args = (non_opt + opt).select { |ip| !ip.container.equal?(obj) }\n          unless super_args.empty?\n            super_args.each { |ip| bld << rname(ip.name) << ', ' }\n            bld.chomp!(', ')\n          end\n          bld << \")\\n\"\n          bld << '    @hash = @hash ^ ' unless hash_participants.empty?\n        end\n        unless hash_participants.empty?\n          hash_participants.each { |a| bld << rname(a.name) << '.hash ^ ' if a.container.equal?(obj) }\n          bld.chomp!(' ^ ')\n          bld << \"\\n\"\n        end\n        init_params.each { |a| bld << '    @' << rname(a.name) << ' = ' << rname(a.name) << \"\\n\" if a.container.equal?(obj) }\n        bld << \"  end\\n\"\n      end\n    end\n\n    unless obj_attrs.empty? && obj.parent.nil?\n      bld << \"\\n  def _pcore_init_hash\\n\"\n      bld << '    result = '\n      bld << (obj.parent.nil? ? '{}' : 'super')\n      bld << \"\\n\"\n      obj_attrs.each do |a|\n        bld << \"    result['\" << a.name << \"'] = @\" << rname(a.name)\n        if a.value?\n          bld << ' unless '\n          equals_default_string(bld, a)\n        end\n        bld << \"\\n\"\n      end\n      bld << \"    result\\n  end\\n\"\n    end\n\n    content_participants = init_params.select { |a| content_participant?(a) }\n    if content_participants.empty?\n      unless obj.parent.is_a?(PObjectType)\n        bld << \"\\n  def _pcore_contents\\n  end\\n\"\n        bld << \"\\n  def _pcore_all_contents(path)\\n  end\\n\"\n      end\n    else\n      bld << \"\\n  def _pcore_contents\\n\"\n      content_participants.each do |cp|\n        if array_type?(cp.type)\n          bld << '    @' << rname(cp.name) << \".each { |value| yield(value) }\\n\"\n        else\n          bld << '    yield(@' << rname(cp.name) << ') unless @' << rname(cp.name) << \".nil?\\n\"\n        end\n      end\n      bld << \"  end\\n\\n  def _pcore_all_contents(path, &block)\\n    path << self\\n\"\n      content_participants.each do |cp|\n        if array_type?(cp.type)\n          bld << '    @' << rname(cp.name) << \".each do |value|\\n\"\n          bld << \"      block.call(value, path)\\n\"\n          bld << \"      value._pcore_all_contents(path, &block)\\n\"\n        else\n          bld << '    unless @' << rname(cp.name) << \".nil?\\n\"\n          bld << '      block.call(@' << rname(cp.name) << \", path)\\n\"\n          bld << '      @' << rname(cp.name) << \"._pcore_all_contents(path, &block)\\n\"\n        end\n        bld << \"    end\\n\"\n      end\n      bld << \"    path.pop\\n  end\\n\"\n    end\n\n    # Output function placeholders\n    obj.functions(false).each_value do |func|\n      code_annotation = RubyMethod.annotate(func)\n      if code_annotation\n        body = code_annotation.body\n        params = code_annotation.parameters\n        bld << \"\\n  def \" << rname(func.name)\n        unless params.nil? || params.empty?\n          bld << '(' << params << ')'\n        end\n        bld << \"\\n    \" << body << \"\\n\"\n      else\n        bld << \"\\n  def \" << rname(func.name) << \"(*args)\\n\"\n        bld << \"    # Placeholder for #{func.type}\\n\"\n        bld << \"    raise Puppet::Error, \\\"no method is implemented for #{func.label}\\\"\\n\"\n      end\n      bld << \"  end\\n\"\n    end\n\n    unless eq_names.empty? && !include_type\n      bld << \"\\n  def eql?(o)\\n\"\n      bld << \"    super &&\\n\" unless obj.parent.nil?\n      bld << \"    o.instance_of?(self.class) &&\\n\" if include_type\n      eq_names.each { |eqn| bld << '    @' << rname(eqn) << '.eql?(o.' << rname(eqn) << \") &&\\n\" }\n      bld.chomp!(\" &&\\n\")\n      bld << \"\\n  end\\n  alias == eql?\\n\"\n    end\n  end\n\n  def content_participant?(a)\n    a.kind != PObjectType::ATTRIBUTE_KIND_REFERENCE && obj_type?(a.type)\n  end\n\n  def obj_type?(t)\n    case t\n    when PObjectType\n      true\n    when POptionalType\n      obj_type?(t.optional_type)\n    when PNotUndefType\n      obj_type?(t.type)\n    when PArrayType\n      obj_type?(t.element_type)\n    when PVariantType\n      t.types.all? { |v| obj_type?(v) }\n    else\n      false\n    end\n  end\n\n  def array_type?(t)\n    case t\n    when PArrayType\n      true\n    when POptionalType\n      array_type?(t.optional_type)\n    when PNotUndefType\n      array_type?(t.type)\n    when PVariantType\n      t.types.all? { |v| array_type?(v) }\n    else\n      false\n    end\n  end\n\n  def default_string(bld, a)\n    case a.value\n    when nil, true, false, Numeric, String\n      bld << a.value.inspect\n    else\n      bld << \"_pcore_type['\" << a.name << \"'].value\"\n    end\n  end\n\n  def equals_default_string(bld, a)\n    case a.value\n    when nil, true, false, Numeric, String\n      bld << '@' << a.name << ' == ' << a.value.inspect\n    else\n      bld << \"_pcore_type['\" << a.name << \"'].default_value?(@\" << a.name << ')'\n    end\n  end\n\n  def rname(name)\n    RUBY_RESERVED_WORDS[name] || name\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/ruby_method.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n  class RubyMethod < Annotation\n    # Register the Annotation type. This is the type that all custom Annotations will inherit from.\n    def self.register_ptype(loader, ir)\n      @type = Pcore.create_object_type(loader, ir, self, 'RubyMethod', 'Annotation',\n                                       'body' => PStringType::DEFAULT,\n                                       'parameters' => {\n                                         KEY_TYPE => POptionalType.new(PStringType::NON_EMPTY),\n                                         KEY_VALUE => nil\n                                       })\n    end\n\n    def self.from_hash(init_hash)\n      from_asserted_hash(Types::TypeAsserter.assert_instance_of('RubyMethod initializer', _pcore_type.init_hash_type, init_hash))\n    end\n\n    def self.from_asserted_hash(init_hash)\n      new(init_hash['body'], init_hash['parameters'])\n    end\n\n    attr_reader :body, :parameters\n\n    def initialize(body, parameters = nil)\n      @body = body\n      @parameters = parameters\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/string_converter.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Types\n# Converts Puppet runtime objects to String under the control of a Format.\n# Use from Puppet Language is via the function `new`.\n#\n# @api private\n#\nclass StringConverter\n  # @api private\n  class FormatError < ArgumentError\n    def initialize(type_string, actual, expected)\n      super \"Illegal format '#{actual}' specified for value of #{type_string} type - expected one of the characters '#{expected}'\"\n    end\n  end\n\n  class Indentation\n    attr_reader :level\n    attr_reader :first\n    attr_reader :is_indenting\n    alias :first? :first\n    alias :is_indenting? :is_indenting\n\n    def initialize(level, first, is_indenting)\n      @level = level\n      @first = first\n      @is_indenting = is_indenting\n    end\n\n    def subsequent\n      first? ? self.class.new(level, false, @is_indenting) : self\n    end\n\n    def indenting(indenting_flag)\n      self.class.new(level, first?, indenting_flag)\n    end\n\n    def increase(indenting_flag = false)\n      self.class.new(level + 1, true, indenting_flag)\n    end\n\n    def breaks?\n      is_indenting? && level > 0 && !first?\n    end\n\n    def padding\n      ' ' * 2 * level\n    end\n  end\n\n  # Format represents one format specification that is textually represented by %<flags><width>.<precision><format>\n  # Format parses and makes the individual parts available when an instance is created.\n  #\n  # @api private\n  #\n  class Format\n    # Boolean, alternate form (varies in meaning)\n    attr_reader :alt\n    alias :alt? :alt\n\n    # Nil or Integer with width of field > 0\n    attr_reader :width\n    # Nil or Integer precisions\n    attr_reader :prec\n    # One char symbol denoting the format\n    attr_reader :format\n    # Symbol, :space, :plus, :ignore\n    attr_reader :plus\n    # Boolean, left adjust in given width or not\n    attr_reader :left\n    # Boolean left_pad with zero instead of space\n    attr_reader :zero_pad\n    # Delimiters for containers, a \"left\" char representing the pair <[{(\n    attr_reader :delimiters\n\n    # Map of type to format for elements contained in an object this format applies to\n    attr_accessor :container_string_formats\n\n    # Separator string inserted between elements in a container\n    attr_accessor :separator\n\n    # Separator string inserted between sub elements in a container\n    attr_accessor :separator2\n\n    attr_reader :orig_fmt\n\n    FMT_PATTERN_STR = '^%([\\s\\[+#0{<(|-]*)([1-9][0-9]*)?(?:\\.([0-9]+))?([a-zA-Z])$'\n    FMT_PATTERN = Regexp.compile(FMT_PATTERN_STR)\n    DELIMITERS  = ['[', '{', '(', '<', '|']\n    DELIMITER_MAP = {\n      '[' => ['[', ']'],\n      '{' => ['{', '}'],\n      '(' => ['(', ')'],\n      '<' => ['<', '>'],\n      '|' => ['|', '|'],\n      :space => ['', '']\n    }.freeze\n\n    def initialize(fmt)\n      @orig_fmt = fmt\n      match = FMT_PATTERN.match(fmt)\n      unless match\n        raise ArgumentError, \"The format '#{fmt}' is not a valid format on the form '%<flags><width>.<prec><format>'\"\n      end\n\n      @format = match[4]\n      unless @format.is_a?(String) && @format.length == 1\n        raise ArgumentError, \"The format must be a one letter format specifier, got '#{@format}'\"\n      end\n\n      @format = @format.to_sym\n      flags  = match[1].split('') || []\n      unless flags.uniq.size == flags.size\n        raise ArgumentError, \"The same flag can only be used once, got '#{fmt}'\"\n      end\n\n      @left  = flags.include?('-')\n      @alt   = flags.include?('#')\n      @plus  = if flags.include?(' ')\n                 :space\n               else\n                 flags.include?('+') ? :plus : :ignore\n               end\n      @zero_pad = flags.include?('0')\n\n      @delimiters = nil\n      DELIMITERS.each do |d|\n        next unless flags.include?(d)\n        unless @delimiters.nil?\n          raise ArgumentError, \"Only one of the delimiters [ { ( < | can be given in the format flags, got '#{fmt}'\"\n        end\n\n        @delimiters = d\n      end\n\n      @width = match[2] ? match[2].to_i : nil\n      @prec  = match[3] ? match[3].to_i : nil\n    end\n\n    # Merges one format into this and returns a new `Format`. The `other` format overrides this.\n    # @param [Format] other\n    # @returns [Format] a merged format\n    #\n    def merge(other)\n      result = Format.new(other.orig_fmt)\n      result.separator = other.separator || separator\n      result.separator2 = other.separator2 || separator2\n      result.container_string_formats = Format.merge_string_formats(container_string_formats, other.container_string_formats)\n      result\n    end\n\n    # Merges two formats where the `higher` format overrides the `lower`. Produces a new `Format`\n    # @param [Format] lower\n    # @param [Format] higher\n    # @returns [Format] the merged result\n    #\n    def self.merge(lower, higher)\n      unless lower && higher\n        return lower || higher\n      end\n\n      lower.merge(higher)\n    end\n\n    # Merges a type => format association and returns a new merged and sorted association.\n    # @param [Format] lower\n    # @param [Format] higher\n    # @returns [Hash] the merged type => format result\n    #\n    def self.merge_string_formats(lower, higher)\n      unless lower && higher\n        return lower || higher\n      end\n\n      # drop all formats in lower than is more generic in higher. Lower must never\n      # override higher\n      lower = lower.reject { |lk, _| higher.keys.any? { |hk| hk != lk && hk.assignable?(lk) } }\n\n      merged = (lower.keys + higher.keys).uniq.map do |k|\n        [k, merge(lower[k], higher[k])]\n      end\n      sort_formats(merged)\n    end\n\n    # Sorts format based on generality of types - most specific types before general\n    #\n    def self.sort_formats(format_map)\n      format_map = format_map.sort do |(a, _), (b, _)|\n        ab = b.assignable?(a)\n        ba = a.assignable?(b)\n        if a == b\n          0\n        elsif ab && !ba\n          -1\n        elsif !ab && ba\n          1\n        else\n          # arbitrary order if disjunct (based on name of type)\n          rank_a = type_rank(a)\n          rank_b = type_rank(b)\n          if rank_a == 0 || rank_b == 0\n            a.to_s <=> b.to_s\n          else\n            rank_a <=> rank_b\n          end\n        end\n      end\n      format_map.to_h\n    end\n\n    # Ranks type on specificity where it matters\n    # lower number means more specific\n    def self.type_rank(t)\n      case t\n      when PStructType\n        1\n      when PHashType\n        2\n      when PTupleType\n        3\n      when PArrayType\n        4\n      when PPatternType\n        10\n      when PEnumType\n        11\n      when PStringType\n        12\n      else\n        0\n      end\n    end\n\n    # Returns an array with a delimiter pair derived from the format.\n    # If format does not contain a delimiter specification the given default is returned\n    #\n    # @param [Array<String>] the default delimiters\n    # @returns [Array<String>] a tuple with left, right delimiters\n    #\n    def delimiter_pair(default = StringConverter::DEFAULT_ARRAY_DELIMITERS)\n      DELIMITER_MAP[@delimiters || @plus] || default\n    end\n\n    def to_s\n      \"%#{@flags}#{@width}.#{@prec}#{@format}\"\n    end\n  end\n\n  extend Puppet::Concurrent::ThreadLocalSingleton\n\n  # @api public\n  def self.convert(value, string_formats = :default)\n    singleton.convert(value, string_formats)\n  end\n\n  # @api private\n  #\n  def initialize\n    @string_visitor = Visitor.new(self, \"string\", 3, 3)\n  end\n\n  DEFAULT_INDENTATION = Indentation.new(0, true, false).freeze\n\n  # format used by default for values in a container\n  # (basically strings are quoted since they may contain a ','))\n  #\n  DEFAULT_CONTAINER_FORMATS = {\n    PAnyType::DEFAULT => Format.new('%p').freeze, # quoted string (Ruby inspect)\n  }.freeze\n\n  DEFAULT_ARRAY_FORMAT                          = Format.new('%a')\n  DEFAULT_ARRAY_FORMAT.separator                = ', '\n  DEFAULT_ARRAY_FORMAT.separator2               = ', '\n  DEFAULT_ARRAY_FORMAT.container_string_formats = DEFAULT_CONTAINER_FORMATS\n  DEFAULT_ARRAY_FORMAT.freeze\n\n  DEFAULT_HASH_FORMAT                           = Format.new('%h')\n  DEFAULT_HASH_FORMAT.separator                 = ', '\n  DEFAULT_HASH_FORMAT.separator2                = ' => '\n  DEFAULT_HASH_FORMAT.container_string_formats  = DEFAULT_CONTAINER_FORMATS\n  DEFAULT_HASH_FORMAT.freeze\n\n  DEFAULT_HASH_DELIMITERS                       = ['{', '}'].freeze\n  DEFAULT_ARRAY_DELIMITERS                      = ['[', ']'].freeze\n\n  DEFAULT_STRING_FORMATS = {\n    PObjectType::DEFAULT => Format.new('%p').freeze, # call with initialization hash\n    PFloatType::DEFAULT => Format.new('%f').freeze, # float\n    PNumericType::DEFAULT => Format.new('%d').freeze, # decimal number\n    PArrayType::DEFAULT => DEFAULT_ARRAY_FORMAT.freeze,\n    PHashType::DEFAULT => DEFAULT_HASH_FORMAT.freeze,\n    PBinaryType::DEFAULT => Format.new('%B').freeze, # strict base64 string unquoted\n    PAnyType::DEFAULT => Format.new('%s').freeze, # unquoted string\n  }.freeze\n\n  DEFAULT_PARAMETER_FORMAT = {\n    PCollectionType::DEFAULT => '%#p',\n    PObjectType::DEFAULT => '%#p',\n    PBinaryType::DEFAULT => '%p',\n    PStringType::DEFAULT => '%p',\n    PRuntimeType::DEFAULT => '%p'\n  }.freeze\n\n  # Converts the given value to a String, under the direction of formatting rules per type.\n  #\n  # When converting to string it is possible to use a set of built in conversion rules.\n  #\n  # A format is specified on the form:\n  #\n  # ´´´\n  # %[Flags][Width][.Precision]Format\n  # ´´´\n  #\n  # `Width` is the number of characters into which the value should be fitted. This allocated space is\n  # padded if value is shorter. By default it is space padded, and the flag 0 will cause padding with 0\n  # for numerical formats.\n  #\n  # `Precision` is the number of fractional digits to show for floating point, and the maximum characters\n  # included in a string format.\n  #\n  # Note that all data type supports the formats `s` and `p` with the meaning \"default to-string\" and\n  # \"default-programmatic to-string\".\n  #\n  # ### Integer\n  #\n  # | Format  | Integer Formats\n  # | ------  | ---------------\n  # | d       | Decimal, negative values produces leading '-'\n  # | x X     | Hexadecimal in lower or upper case. Uses ..f/..F for negative values unless # is also used\n  # | o       | Octal. Uses ..0 for negative values unless # is also used\n  # | b B     | Binary with prefix 'b' or 'B'. Uses ..1/..1 for negative values unless # is also used\n  # | c       | numeric value representing a Unicode value, result is a one unicode character string, quoted if alternative flag # is used\n  # | s       | same as d, or d in quotes if alternative flag # is used\n  # | p       | same as d\n  # | eEfgGaA | converts integer to float and formats using the floating point rules\n  #\n  # Defaults to `d`\n  #\n  # ### Float\n  #\n  # | Format  | Float formats\n  # | ------  | -------------\n  # | f       | floating point in non exponential notation\n  # | e E     | exponential notation with 'e' or 'E'\n  # | g G     | conditional exponential with 'e' or 'E' if exponent < -4 or >= the precision\n  # | a A     | hexadecimal exponential form, using 'x'/'X' as prefix and 'p'/'P' before exponent\n  # | s       | converted to string using format p, then applying string formatting rule, alternate form # quotes result\n  # | p       | f format with minimum significant number of fractional digits, prec has no effect\n  # | dxXobBc | converts float to integer and formats using the integer rules\n  #\n  # Defaults to `p`\n  #\n  # ### String\n  #\n  # | Format | String\n  # | ------ | ------\n  # | s      | unquoted string, verbatim output of control chars\n  # | p      | programmatic representation - strings are quoted, interior quotes and control chars are escaped\n  # | C      | each :: name segment capitalized, quoted if alternative flag # is used\n  # | c      | capitalized string, quoted if alternative flag # is used\n  # | d      | downcased string, quoted if alternative flag # is used\n  # | u      | upcased string, quoted if alternative flag # is used\n  # | t      | trims leading and trailing whitespace from the string, quoted if alternative flag # is used\n  #\n  # Defaults to `s` at top level and `p` inside array or hash.\n  #\n  # ### Boolean\n  #\n  # | Format    | Boolean Formats\n  # | ----      | -------------------\n  # | t T       | 'true'/'false' or 'True'/'False' , first char if alternate form is used (i.e. 't'/'f' or 'T'/'F').\n  # | y Y       | 'yes'/'no', 'Yes'/'No', 'y'/'n' or 'Y'/'N' if alternative flag # is used\n  # | dxXobB    | numeric value 0/1 in accordance with the given format which must be valid integer format\n  # | eEfgGaA   | numeric value 0.0/1.0 in accordance with the given float format and flags\n  # | s         | 'true' / 'false'\n  # | p         | 'true' / 'false'\n  #\n  # ### Regexp\n  #\n  # | Format    | Regexp Formats (%/)\n  # | ----      | ------------------\n  # | s         | / / delimiters, alternate flag replaces / delimiters with quotes\n  # | p         | / / delimiters\n  #\n  # ### Undef\n  #\n  # | Format    | Undef formats\n  # | ------    | -------------\n  # | s         | empty string, or quoted empty string if alternative flag # is used\n  # | p         | 'undef', or quoted '\"undef\"' if alternative flag # is used\n  # | n         | 'nil', or 'null' if alternative flag # is used\n  # | dxXobB    | 'NaN'\n  # | eEfgGaA   | 'NaN'\n  # | v         | 'n/a'\n  # | V         | 'N/A'\n  # | u         | 'undef', or 'undefined' if alternative # flag is used\n  #\n  # ### Default (value)\n  #\n  # | Format    | Default formats\n  # | ------    | ---------------\n  # | d D       | 'default' or 'Default', alternative form # causes value to be quoted\n  # | s         | same as d\n  # | p         | same as d\n  #\n  # ### Binary (value)\n  #\n  # | Format    | Default formats\n  # | ------    | ---------------\n  # | s         | binary as unquoted characters\n  # | p         | 'Binary(\"<base64strict>\")'\n  # | b         | '<base64>' - base64 string with newlines inserted\n  # | B         | '<base64strict>' - base64 strict string (without newlines inserted)\n  # | u         | '<base64urlsafe>' - base64 urlsafe string\n  # | t         | 'Binary' - outputs the name of the type only\n  # | T         | 'BINARY' - output the name of the type in all caps only\n  #\n  # The alternate form flag `#` will quote the binary or base64 text output\n  # The width and precision values are applied to the text part only in `%p` format.\n  #\n  # ### Array & Tuple\n  #\n  # | Format    | Array/Tuple Formats\n  # | ------    | -------------\n  # | a         | formats with `[ ]` delimiters and `,`, alternate form `#` indents nested arrays/hashes\n  # | s         | same as a\n  # | p         | same as a\n  #\n  # See \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\n  # more information about options.\n  #\n  # The alternate form flag `#` will cause indentation of nested array or hash containers. If width is also set\n  # it is taken as the maximum allowed length of a sequence of elements (not including delimiters). If this max length\n  # is exceeded, each element will be indented.\n  #\n  # ### Hash & Struct\n  #\n  # | Format    | Hash/Struct Formats\n  # | ------    | -------------\n  # | h         | formats with `{ }` delimiters, `,` element separator and ` => ` inner element separator unless overridden by flags\n  # | s         | same as h\n  # | p         | same as h\n  # | a         | converts the hash to an array of [k,v] tuples and formats it using array rule(s)\n  #\n  # See \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\n  # more information about options.\n  #\n  # The alternate form flag `#` will format each hash key/value entry indented on a separate line.\n  #\n  # ### Type\n  #\n  # | Format    | Array/Tuple Formats\n  # | ------    | -------------\n  # | s        | The same as p, quoted if alternative flag # is used\n  # | p        | Outputs the type in string form as specified by the Puppet Language\n  #\n  # ### Flags\n  #\n  # | Flag     | Effect\n  # | ------   | ------\n  # | (space)  | space instead of + for numeric output (- is shown), for containers skips delimiters\n  # | #        | alternate format; prefix 0x/0x, 0 (octal) and 0b/0B for binary, Floats force decimal '.'. For g/G keep trailing 0.\n  # | +        | show sign +/- depending on value's sign, changes x,X, o,b, B format to not use 2's complement form\n  # | -        | left justify the value in the given width\n  # | 0        | pad with 0 instead of space for widths larger than value\n  # | <[({\\|   | defines an enclosing pair <> [] () {} or \\| \\| when used with a container type\n  #\n  #\n  # ### Additional parameters for containers; Array and Hash\n  #\n  # For containers (Array and Hash), the format is specified by a hash where the following keys can be set:\n  # * `'format'` - the format specifier for the container itself\n  # * `'separator'` - the separator string to use between elements, should not contain padding space at the end\n  # * `'separator2'` - the separator string to use between association of hash entries key/value\n  # * `'string_formats'´ - a map of type to format for elements contained in the container\n  #\n  # Note that the top level format applies to Array and Hash objects contained/nested in an Array or a Hash.\n  #\n  # Given format mappings are merged with (default) formats and a format specified for a narrower type\n  # wins over a broader.\n  #\n  # @param mode [String, Symbol] :strict or :extended (or :default which is the same as :strict)\n  # @param string_formats [String, Hash] format tring, or a hash mapping type to a format string, and for Array and Hash types map to hash of details\n  #\n  def convert(value, string_formats = :default)\n    options = DEFAULT_STRING_FORMATS\n\n    value_type = TypeCalculator.infer_set(value)\n    if string_formats.is_a?(String)\n      # For Array and Hash, the format is given as a Hash where 'format' key is the format for the collection itself\n      if Puppet::Pops::Types::PArrayType::DEFAULT.assignable?(value_type)\n        # add the format given for the exact type\n        string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => string_formats } }\n      elsif Puppet::Pops::Types::PHashType::DEFAULT.assignable?(value_type)\n        # add the format given for the exact type\n        string_formats = { Puppet::Pops::Types::PHashType::DEFAULT => { 'format' => string_formats } }\n      else\n        # add the format given for the exact type\n        string_formats = { value_type => string_formats }\n      end\n    end\n\n    case string_formats\n    when :default\n    # do nothing, use default formats\n\n    when Hash\n      # Convert and validate user input\n      string_formats = validate_input(string_formats)\n      # Merge user given with defaults such that user options wins, merge is deep and format specific\n      options = Format.merge_string_formats(DEFAULT_STRING_FORMATS, string_formats)\n    else\n      raise ArgumentError, \"string conversion expects a Default value or a Hash of type to format mappings, got a '#{string_formats.class}'\"\n    end\n\n    _convert(value_type, value, options, DEFAULT_INDENTATION)\n  end\n\n  #  # A method only used for manual debugging as the default output of the formatting rules is\n  #  # very hard to read otherwise.\n  #  #\n  #  # @api private\n  #  def dump_string_formats(f, indent = 1)\n  #     return f.to_s unless f.is_a?(Hash)\n  #     \"{#{f.map {|k,v| \"#{k.to_s} => #{dump_string_formats(v,indent+1)}\"}.join(\",\\n#{'  '*indent}  \")}}\"\n  #  end\n\n  def _convert(val_type, value, format_map, indentation)\n    @string_visitor.visit_this_3(self, val_type, value, format_map, indentation)\n  end\n  private :_convert\n\n  def validate_input(fmt)\n    return nil if fmt.nil?\n    unless fmt.is_a?(Hash)\n      raise ArgumentError, \"expected a hash with type to format mappings, got instance of '#{fmt.class}'\"\n    end\n\n    fmt.each_with_object({}) do |entry, result|\n      key, value = entry\n      unless key.is_a?(Types::PAnyType)\n        raise ArgumentError, \"top level keys in the format hash must be data types, got instance of '#{key.class}'\"\n      end\n\n      if value.is_a?(Hash)\n        result[key] = validate_container_input(value)\n      else\n        result[key] = Format.new(value)\n      end\n    end\n  end\n  private :validate_input\n\n  FMT_KEYS = %w[separator separator2 format string_formats].freeze\n\n  def validate_container_input(fmt)\n    if (fmt.keys - FMT_KEYS).size > 0\n      raise ArgumentError, \"only #{FMT_KEYS.map { |k| \"'#{k}'\" }.join(', ')} are allowed in a container format, got #{fmt}\"\n    end\n\n    result                          = Format.new(fmt['format'])\n    result.separator                = fmt['separator']\n    result.separator2               = fmt['separator2']\n    result.container_string_formats = validate_input(fmt['string_formats'])\n    result\n  end\n  private :validate_container_input\n\n  def string_PObjectType(val_type, val, format_map, indentation)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :p\n      fmt = TypeFormatter.singleton\n      indentation = indentation.indenting(f.alt? || indentation.is_indenting?)\n      fmt = fmt.indented(indentation.level, 2) if indentation.is_indenting?\n      fmt.string(val)\n    when :s\n      val.to_s\n    when :q\n      val.inspect\n    else\n      raise FormatError.new('Object', f.format, 'spq')\n    end\n  end\n\n  def string_PObjectTypeExtension(val_type, val, format_map, indentation)\n    string_PObjectType(val_type.base_type, val, format_map, indentation)\n  end\n\n  def string_PRuntimeType(val_type, val, format_map, indent)\n    # Before giving up on this, and use a string representation of the unknown\n    # object, a check is made to see if the object can present itself as\n    # a hash or an array. If it can, then that representation is used instead.\n    case val\n    when Hash\n      hash = val.to_hash\n      # Ensure that the returned value isn't derived from Hash\n      return string_PHashType(val_type, hash, format_map, indent) if hash.instance_of?(Hash)\n    when Array\n      array = val.to_a\n      # Ensure that the returned value isn't derived from Array\n      return string_PArrayType(val_type, array, format_map, indent) if array.instance_of?(Array)\n    end\n\n    f = get_format(val_type, format_map)\n    case f.format\n    when :s\n      val.to_s\n    when :p\n      puppet_quote(val.to_s)\n    when :q\n      val.inspect\n    else\n      raise FormatError.new('Runtime', f.format, 'spq')\n    end\n  end\n\n  # Basically string_PAnyType converts the value to a String and then formats it according\n  # to the resulting type\n  #\n  # @api private\n  def string_PAnyType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    Kernel.format(f.orig_fmt, val)\n  end\n\n  def string_PDefaultType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    apply_string_flags(f, case f.format\n                          when :d, :s, :p\n                            f.alt? ? '\"default\"' : 'default'\n                          when :D\n                            f.alt? ? '\"Default\"' : 'Default'\n                          else\n                            raise FormatError.new('Default', f.format, 'dDsp')\n                          end)\n  end\n\n  # @api private\n  def string_PUndefType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    apply_string_flags(f, case f.format\n                          when :n\n                            f.alt? ? 'null' : 'nil'\n                          when :u\n                            f.alt? ? 'undefined' : 'undef'\n                          when :d, :x, :X, :o, :b, :B, :e, :E, :f, :g, :G, :a, :A\n                            'NaN'\n                          when :v\n                            'n/a'\n                          when :V\n                            'N/A'\n                          when :s\n                            f.alt? ? '\"\"' : ''\n                          when :p\n                            f.alt? ? '\"undef\"' : 'undef'\n                          else\n                            raise FormatError.new('Undef', f.format,\n                                                  'nudxXobBeEfgGaAvVsp')\n                          end)\n  end\n\n  # @api private\n  def string_PBooleanType(val_type, val, format_map, indentation)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :t\n      # 'true'/'false' or 't'/'f' if in alt mode\n      str_bool = val.to_s\n      apply_string_flags(f, f.alt? ? str_bool[0] : str_bool)\n\n    when :T\n      # 'True'/'False' or 'T'/'F' if in alt mode\n      str_bool = val.to_s.capitalize\n      apply_string_flags(f, f.alt? ? str_bool[0] : str_bool)\n\n    when :y\n      # 'yes'/'no' or 'y'/'n' if in alt mode\n      str_bool = val ? 'yes' : 'no'\n      apply_string_flags(f, f.alt? ? str_bool[0] : str_bool)\n\n    when :Y\n      # 'Yes'/'No' or 'Y'/'N' if in alt mode\n      str_bool = val ? 'Yes' : 'No'\n      apply_string_flags(f, f.alt? ? str_bool[0] : str_bool)\n\n    when :d, :x, :X, :o, :b, :B\n      # Boolean in numeric form, formated by integer rule\n      numeric_bool = val ? 1 : 0\n      string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => f }\n      _convert(TypeCalculator.infer_set(numeric_bool), numeric_bool, string_formats, indentation)\n\n    when :e, :E, :f, :g, :G, :a, :A\n      # Boolean in numeric form, formated by float rule\n      numeric_bool = val ? 1.0 : 0.0\n      string_formats = { Puppet::Pops::Types::PFloatType::DEFAULT => f }\n      _convert(TypeCalculator.infer_set(numeric_bool), numeric_bool, string_formats, indentation)\n\n    when :s\n      apply_string_flags(f, val.to_s)\n\n    when :p\n      apply_string_flags(f, val.inspect)\n\n    else\n      raise FormatError.new('Boolean', f.format, 'tTyYdxXobBeEfgGaAsp')\n    end\n  end\n\n  # Performs post-processing of literals to apply width and precision flags\n  def apply_string_flags(f, literal_str)\n    if f.left || f.width || f.prec\n      fmt = '%'.dup\n      fmt << '-' if f.left\n      fmt << f.width.to_s if f.width\n      fmt << '.' << f.prec.to_s if f.prec\n      fmt << 's'\n      Kernel.format(fmt, literal_str)\n    else\n      literal_str\n    end\n  end\n  private :apply_string_flags\n\n  # @api private\n  def string_PIntegerType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :d, :x, :X, :o, :b, :B, :p\n      Kernel.format(f.orig_fmt, val)\n\n    when :e, :E, :f, :g, :G, :a, :A\n      Kernel.format(f.orig_fmt, val.to_f)\n\n    when :c\n      char = [val].pack(\"U\")\n      char = f.alt? ? \"\\\"#{char}\\\"\" : char\n      Kernel.format(f.orig_fmt.tr('c', 's'), char)\n\n    when :s\n      fmt = f.alt? ? 'p' : 's'\n      int_str = Kernel.format('%d', val)\n      Kernel.format(f.orig_fmt.gsub('s', fmt), int_str)\n\n    else\n      raise FormatError.new('Integer', f.format, 'dxXobBeEfgGaAspc')\n    end\n  end\n\n  # @api private\n  def string_PFloatType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :d, :x, :X, :o, :b, :B\n      Kernel.format(f.orig_fmt, val.to_i)\n\n    when :e, :E, :f, :g, :G, :a, :A, :p\n      Kernel.format(f.orig_fmt, val)\n\n    when :s\n      float_str = f.alt? ? \"\\\"#{Kernel.format('%p', val)}\\\"\" : Kernel.format('%p', val)\n      Kernel.format(f.orig_fmt, float_str)\n\n    else\n      raise FormatError.new('Float', f.format, 'dxXobBeEfgGaAsp')\n    end\n  end\n\n  # @api private\n  def string_PBinaryType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    substitute = f.alt? ? 'p' : 's'\n    case f.format\n    when :s\n      val_to_convert = val.binary_buffer\n      if !f.alt?\n        # Assume it is valid UTF-8\n        val_to_convert = val_to_convert.dup.force_encoding('UTF-8')\n        # If it isn't\n        unless val_to_convert.valid_encoding?\n          # try to convert and fail with details about what is wrong\n          val_to_convert = val.binary_buffer.encode('UTF-8')\n        end\n      else\n        val_to_convert = val.binary_buffer\n      end\n      Kernel.format(f.orig_fmt.gsub('s', substitute), val_to_convert)\n\n    when :p\n      # width & precision applied to string, not the name of the type\n      \"Binary(\\\"#{Kernel.format(f.orig_fmt.tr('p', 's'), val.to_s)}\\\")\"\n\n    when :b\n      Kernel.format(f.orig_fmt.gsub('b', substitute), val.relaxed_to_s)\n\n    when :B\n      Kernel.format(f.orig_fmt.gsub('B', substitute), val.to_s)\n\n    when :u\n      Kernel.format(f.orig_fmt.gsub('u', substitute), val.urlsafe_to_s)\n\n    when :t\n      # Output as the type without any data\n      Kernel.format(f.orig_fmt.gsub('t', substitute), 'Binary')\n\n    when :T\n      # Output as the type without any data in all caps\n      Kernel.format(f.orig_fmt.gsub('T', substitute), 'BINARY')\n\n    else\n      raise FormatError.new('Binary', f.format, 'bButTsp')\n    end\n  end\n\n  # @api private\n  def string_PStringType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :s\n      Kernel.format(f.orig_fmt, val)\n\n    when :p\n      apply_string_flags(f, puppet_quote(val, f.alt?))\n\n    when :c\n      c_val = val.capitalize\n      f.alt? ? apply_string_flags(f, puppet_quote(c_val)) : Kernel.format(f.orig_fmt.tr('c', 's'), c_val)\n\n    when :C\n      c_val = val.split('::').map(&:capitalize).join('::')\n      f.alt? ? apply_string_flags(f, puppet_quote(c_val)) : Kernel.format(f.orig_fmt.tr('C', 's'), c_val)\n\n    when :u\n      c_val = val.upcase\n      f.alt? ? apply_string_flags(f, puppet_quote(c_val)) : Kernel.format(f.orig_fmt.tr('u', 's'), c_val)\n\n    when :d\n      c_val = val.downcase\n      f.alt? ? apply_string_flags(f, puppet_quote(c_val)) : Kernel.format(f.orig_fmt.tr('d', 's'), c_val)\n\n    when :t # trim\n      c_val = val.strip\n      f.alt? ? apply_string_flags(f, puppet_quote(c_val)) : Kernel.format(f.orig_fmt.tr('t', 's'), c_val)\n\n    else\n      raise FormatError.new('String', f.format, 'cCudspt')\n    end\n  end\n\n  # Performs a '%p' formatting of the given _str_ such that the output conforms to Puppet syntax. An ascii string\n  # without control characters, dollar, single-qoute, or backslash, will be quoted using single quotes. All other\n  # strings will be quoted using double quotes.\n  #\n  # @param [String] str the string that should be formatted\n  # @param [Boolean] enforce_double_quotes if true the result will be double quoted (even if single quotes would be possible)\n  # @return [String] the formatted string\n  #\n  # @api public\n  def puppet_quote(str, enforce_double_quotes = false)\n    if enforce_double_quotes\n      return puppet_double_quote(str)\n    end\n\n    # Assume that the string can be single quoted\n    bld = \"'\".dup\n    bld.force_encoding(str.encoding)\n    escaped = false\n    str.each_codepoint do |codepoint|\n      # Control characters and non-ascii characters cannot be present in a single quoted string\n      return puppet_double_quote(str) if codepoint < 0x20\n\n      if escaped\n        bld << 0x5c << codepoint\n        escaped = false\n      elsif codepoint == 0x27\n        bld << 0x5c << codepoint\n      elsif codepoint == 0x5c\n        escaped = true\n      elsif codepoint <= 0x7f\n        bld << codepoint\n      else\n        bld << [codepoint].pack('U')\n      end\n    end\n\n    # If string ended with a backslash, then that backslash must be escaped\n    bld << 0x5c if escaped\n\n    bld << \"'\"\n    bld\n  end\n\n  def puppet_double_quote(str)\n    bld = '\"'.dup\n    str.each_codepoint do |codepoint|\n      case codepoint\n      when 0x09\n        bld << '\\\\t'\n      when 0x0a\n        bld << '\\\\n'\n      when 0x0d\n        bld << '\\\\r'\n      when 0x22\n        bld << '\\\\\"'\n      when 0x24\n        bld << '\\\\$'\n      when 0x5c\n        bld << '\\\\\\\\'\n      else\n        if codepoint < 0x20\n          bld << sprintf('\\\\u{%X}', codepoint)\n        elsif codepoint <= 0x7f\n          bld << codepoint\n        else\n          bld << [codepoint].pack('U')\n        end\n      end\n    end\n    bld << '\"'\n    bld\n  end\n\n  # @api private\n  def string_PRegexpType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :p\n      str_regexp = PRegexpType.regexp_to_s_with_delimiters(val)\n      f.orig_fmt == '%p' ? str_regexp : Kernel.format(f.orig_fmt.tr('p', 's'), str_regexp)\n    when :s\n      str_regexp = PRegexpType.regexp_to_s(val)\n      str_regexp = puppet_quote(str_regexp) if f.alt?\n      f.orig_fmt == '%s' ? str_regexp : Kernel.format(f.orig_fmt, str_regexp)\n    else\n      raise FormatError.new('Regexp', f.format, 'sp')\n    end\n  end\n\n  def string_PArrayType(val_type, val, format_map, indentation)\n    format         = get_format(val_type, format_map)\n    sep            = format.separator || DEFAULT_ARRAY_FORMAT.separator\n    string_formats = format.container_string_formats || DEFAULT_CONTAINER_FORMATS\n    delims         = format.delimiter_pair(DEFAULT_ARRAY_DELIMITERS)\n\n    # Make indentation active, if array is in alternative format, or if nested in indenting\n    indentation = indentation.indenting(format.alt? || indentation.is_indenting?)\n\n    case format.format\n    when :a, :s, :p\n      buf = ''.dup\n      if indentation.breaks?\n        buf << \"\\n\"\n        buf << indentation.padding\n      end\n      buf << delims[0]\n\n      # Make a first pass to format each element\n      children_indentation = indentation.increase(format.alt?) # tell children they are expected to indent\n      mapped = val.map do |v|\n        if children_indentation.first?\n          children_indentation = children_indentation.subsequent\n        end\n        val_t = TypeCalculator.infer_set(v)\n        _convert(val_t, v, is_container?(val_t) ? format_map : string_formats, children_indentation)\n      end\n\n      # compute widest run in the array, skip nested arrays and hashes\n      # then if size > width, set flag if a break on each element should be performed\n      if format.alt? && format.width\n        widest = val.each_with_index.each_with_object([0]) do |v_i, memo|\n          # array or hash breaks\n          if is_a_or_h?(v_i[0])\n            memo << 0\n          else\n            memo[-1] += mapped[v_i[1]].length\n          end\n        end\n        widest = widest.max\n        sz_break = widest > (format.width || Float::INFINITY)\n      else\n        sz_break = false\n      end\n\n      # output each element with breaks and padding\n      children_indentation = indentation.increase(format.alt?)\n      val.each_with_index do |v, i|\n        str_val = mapped[i]\n        if children_indentation.first?\n          children_indentation = children_indentation.subsequent\n          # if breaking, indent first element by one\n          if sz_break && !is_a_or_h?(v)\n            buf << ' '\n          end\n        else\n          buf << sep\n          # if break on each (and breaking will not occur because next is an array or hash)\n          # or, if indenting, and previous was an array or hash, then break and continue on next line\n          # indented.\n          if (sz_break && !is_a_or_h?(v)) || (format.alt? && i > 0 && is_a_or_h?(val[i - 1]) && !is_a_or_h?(v))\n            buf.rstrip! unless buf[-1] == \"\\n\"\n            buf << \"\\n\"\n            buf << children_indentation.padding\n          end\n        end\n        # remove trailing space added by separator if followed by break\n        buf.rstrip! if buf[-1] == ' ' && str_val[0] == \"\\n\"\n        buf << str_val\n      end\n      buf << delims[1]\n      buf\n    else\n      raise FormatError.new('Array', format.format, 'asp')\n    end\n  end\n\n  def is_a_or_h?(x)\n    x.is_a?(Array) || x.is_a?(Hash)\n  end\n\n  def is_container?(t)\n    case t\n    when PArrayType, PHashType, PStructType, PTupleType, PObjectType\n      true\n    else\n      false\n    end\n  end\n\n  # @api private\n  def string_PTupleType(val_type, val, format_map, indentation)\n    string_PArrayType(val_type, val, format_map, indentation)\n  end\n\n  # @api private\n  def string_PIteratorType(val_type, val, format_map, indentation)\n    v = val.to_a\n    _convert(TypeCalculator.infer_set(v), v, format_map, indentation)\n  end\n\n  # @api private\n  def string_PHashType(val_type, val, format_map, indentation)\n    format         = get_format(val_type, format_map)\n    sep            = format.separator  || DEFAULT_HASH_FORMAT.separator\n    assoc          = format.separator2 || DEFAULT_HASH_FORMAT.separator2\n    string_formats = format.container_string_formats || DEFAULT_CONTAINER_FORMATS\n    delims         = format.delimiter_pair(DEFAULT_HASH_DELIMITERS)\n\n    if format.alt?\n      sep = sep.rstrip unless sep[-1] == \"\\n\"\n      sep = \"#{sep}\\n\"\n    end\n\n    cond_break     = ''\n    padding        = ''\n\n    case format.format\n    when :a\n      # Convert to array and use array rules\n      array_hash = val.to_a\n      _convert(TypeCalculator.infer_set(array_hash), array_hash, format_map, indentation)\n\n    when :h, :s, :p\n      indentation = indentation.indenting(format.alt? || indentation.is_indenting?)\n      buf = ''.dup\n      if indentation.breaks?\n        buf << \"\\n\"\n        buf << indentation.padding\n      end\n\n      children_indentation = indentation.increase\n      if format.alt?\n        cond_break = \"\\n\"\n        padding = children_indentation.padding\n      end\n      buf << delims[0]\n      buf << cond_break # break after opening delimiter if pretty printing\n      buf << val.map do |k, v|\n        key_type = TypeCalculator.infer_set(k)\n        val_type = TypeCalculator.infer_set(v)\n        key = _convert(key_type, k, is_container?(key_type) ? format_map : string_formats, children_indentation)\n        val = _convert(val_type, v, is_container?(val_type) ? format_map : string_formats, children_indentation)\n        \"#{padding}#{key}#{assoc}#{val}\"\n      end.join(sep)\n      if format.alt?\n        buf << cond_break\n        buf << indentation.padding\n      end\n      buf << delims[1]\n      buf\n    else\n      raise FormatError.new('Hash', format.format, 'hasp')\n    end\n  end\n\n  # @api private\n  def string_PStructType(val_type, val, format_map, indentation)\n    string_PHashType(val_type, val, format_map, indentation)\n  end\n\n  # @api private\n  def string_PTypeType(val_type, val, format_map, _)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :s\n      str_val = f.alt? ? \"\\\"#{val}\\\"\" : val.to_s\n      Kernel.format(f.orig_fmt, str_val)\n    when :p\n      Kernel.format(f.orig_fmt.tr('p', 's'), val.to_s)\n    else\n      raise FormatError.new('Type', f.format, 'sp')\n    end\n  end\n\n  # @api private\n  def string_PURIType(val_type, val, format_map, indentation)\n    f = get_format(val_type, format_map)\n    case f.format\n    when :p\n      fmt = TypeFormatter.singleton\n      indentation = indentation.indenting(f.alt? || indentation.is_indenting?)\n      fmt = fmt.indented(indentation.level, 2) if indentation.is_indenting?\n      fmt.string(val)\n    when :s\n      str_val = val.to_s\n      Kernel.format(f.orig_fmt, f.alt? ? puppet_quote(str_val) : str_val)\n    else\n      raise FormatError.new('URI', f.format, 'sp')\n    end\n  end\n\n  # Maps the inferred type of o to a formatting rule\n  def get_format(val_t, format_options)\n    fmt = format_options.find { |k, _| k.assignable?(val_t) }\n    return fmt[1] unless fmt.nil?\n\n    Format.new(\"%s\")\n  end\n  private :get_format\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/tree_iterators.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Types\nmodule Iterable\nclass TreeIterator\n  include Iterable\n\n  DEFAULT_CONTAINERS = TypeFactory.variant(\n    PArrayType::DEFAULT,\n    PHashType::DEFAULT,\n    PObjectType::DEFAULT\n  )\n\n  # Creates a TreeIterator that by default treats all Array, Hash and Object instances as\n  # containers - the 'containers' option can be set to a type that denotes which types of values\n  # should be treated as containers - a `Variant[Array, Hash]` would for instance not treat\n  # Object values as containers, whereas just `Object` would only treat objects as containers.\n  #\n  # Unrecognized options are silently ignored\n  #\n  # @param [Hash] options the options\n  # @option options [PTypeType] :container_type ('Variant[Hash, Array, Object]') The type(s) that should be treated as containers. The\n  #   given type(s) must be assignable to the default container_type.\n  # @option options [Boolean] :include_root ('true') If the root container itself should be included in the iteration (requires\n  #   `include_containers` to also be `true` to take effect).\n  # @option options [Boolean] :include_containers ('true') If containers should be included in the iteration\n  # @option options [Boolean] :include_values ('true') If non containers (values) should be included in the iteration\n  # @option options [Boolean] :include_refs ('false') If (non containment) referenced values in Objects should be included\n  #\n  def initialize(enum, options = EMPTY_HASH)\n    @root = enum\n    @element_t = nil\n    @value_stack = [enum]\n    @indexer_stack = []\n    @current_path = []\n    @recursed = false\n    @containers_t = options['container_type'] || DEFAULT_CONTAINERS\n    unless DEFAULT_CONTAINERS.assignable?(@containers_t)\n      raise ArgumentError, _(\"Only Array, Hash, and Object types can be used as container types. Got %{type}\") % { type: @containers_t }\n    end\n\n    @with_root       = extract_option(options, 'include_root', true)\n    @with_containers = extract_option(options, 'include_containers', true)\n    @with_values     = extract_option(options, 'include_values', true)\n    @with_root       = @with_containers && extract_option(options, 'include_root', true)\n    unless @with_containers || @with_values\n      raise ArgumentError, _(\"Options 'include_containers' and 'include_values' cannot both be false\")\n    end\n\n    @include_refs = !!options['include_refs']\n  end\n\n  # Yields each `path, value` if the block arity is 2, and only `value` if arity is 1\n  #\n  def each(&block)\n    loop do\n      if block.arity == 1\n        yield(self.next)\n      else\n        yield(*self.next)\n      end\n    end\n  end\n\n  def size\n    raise \"Not yet implemented - computes size lazily\"\n  end\n\n  def unbounded?\n    false\n  end\n\n  def to_a\n    result = []\n    loop do\n      result << self.next\n    end\n    result\n  end\n\n  def to_array\n    to_a\n  end\n\n  def reverse_each(&block)\n    r = Iterator.new(PAnyType::DEFAULT, to_array.reverse_each)\n    block_given? ? r.each(&block) : r\n  end\n\n  def step(step, &block)\n    r = StepIterator.new(PAnyType::DEFAULT, self, step)\n    block_given? ? r.each(&block) : r\n  end\n\n  def indexer_on(val)\n    return nil unless @containers_t.instance?(val)\n\n    if val.is_a?(Array)\n      val.size.times\n    elsif val.is_a?(Hash)\n      val.each_key\n    elsif @include_refs\n      val._pcore_type.attributes.each_key\n    else\n      val._pcore_type.attributes.reject { |_k, v| v.kind == PObjectType::ATTRIBUTE_KIND_REFERENCE }.each_key\n    end\n  end\n  private :indexer_on\n\n  def has_next?(iterator)\n    iterator.peek\n    true\n  rescue StopIteration\n    false\n  end\n  private :has_next?\n\n  def extract_option(options, key, default)\n    v = options[key]\n    v.nil? ? default : !!v\n  end\n  private :extract_option\nend\n\nclass DepthFirstTreeIterator < TreeIterator\n  # Creates a DepthFirstTreeIterator that by default treats all Array, Hash and Object instances as\n  # containers - the 'containers' option can be set to a type that denotes which types of values\n  # should be treated as containers - a `Variant[Array, Hash]` would for instance not treat\n  # Object values as containers, whereas just `Object` would only treat objects as containers.\n  #\n  # @param [Hash] options the options\n  # @option options [PTypeType] :containers ('Variant[Hash, Array, Object]') The type(s) that should be treated as containers\n  # @option options [Boolean] :with_root ('true') If the root container itself should be included in the iteration\n  #\n  def initialize(enum, options = EMPTY_HASH)\n    super\n  end\n\n  def next\n    loop do\n      break if @value_stack.empty?\n\n      # first call\n      if @indexer_stack.empty?\n        @indexer_stack << indexer_on(@root)\n        @recursed = true\n        return [[], @root] if @with_root\n      end\n\n      begin\n        if @recursed\n          @current_path << nil\n          @recursed = false\n        end\n        idx = @indexer_stack[-1].next\n        @current_path[-1] = idx\n        v = @value_stack[-1]\n        value = v.is_a?(PuppetObject) ? v.send(idx) : v[idx]\n        indexer = indexer_on(value)\n        if indexer\n          # recurse\n          @recursed = true\n          @value_stack << value\n          @indexer_stack << indexer\n          redo unless @with_containers\n        else\n          redo unless @with_values\n        end\n        return [@current_path.dup, value]\n      rescue StopIteration\n        # end of current value's range of content\n        # pop all until out of next values\n        at_the_very_end = false\n        loop do\n          pop_level\n          at_the_very_end = @indexer_stack.empty?\n          break if at_the_very_end || has_next?(@indexer_stack[-1])\n        end\n      end\n    end\n    raise StopIteration\n  end\n\n  def pop_level\n    @value_stack.pop\n    @indexer_stack.pop\n    @current_path.pop\n  end\n  private :pop_level\nend\n\nclass BreadthFirstTreeIterator < TreeIterator\n  def initialize(enum, options = EMPTY_HASH)\n    @path_stack = []\n    super\n  end\n\n  def next\n    loop do\n      break if @value_stack.empty?\n\n      # first call\n      if @indexer_stack.empty?\n        @indexer_stack << indexer_on(@root)\n        @recursed = true\n        return [[], @root] if @with_root\n      end\n\n      begin\n        if @recursed\n          @current_path << nil\n          @recursed = false\n        end\n\n        idx = @indexer_stack[0].next\n        @current_path[-1] = idx\n        v = @value_stack[0]\n        value = v.is_a?(PuppetObject) ? v.send(idx) : v[idx]\n        indexer = indexer_on(value)\n        if indexer\n          @value_stack << value\n          @indexer_stack << indexer\n          @path_stack << @current_path.dup\n          next unless @with_containers\n        end\n        return [@current_path.dup, value]\n      rescue StopIteration\n        # end of current value's range of content\n        # shift all until out of next values\n        at_the_very_end = false\n        loop do\n          shift_level\n          at_the_very_end = @indexer_stack.empty?\n          break if at_the_very_end || has_next?(@indexer_stack[0])\n        end\n      end\n    end\n    raise StopIteration\n  end\n\n  def shift_level\n    @value_stack.shift\n    @indexer_stack.shift\n    @current_path = @path_stack.shift\n    @recursed = true\n  end\n  private :shift_level\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_acceptor.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# Implements a standard visitor patter for the Puppet Type system.\n\n#\n# An instance of this module is passed as an argument to the {PAnyType#accept}\n# method of a Type instance. That type will then use the {TypeAcceptor#visit} callback\n# on the acceptor and then pass the acceptor to the `accept` method of all contained\n# type instances so that the it gets a visit from each one recursively.\n#\nmodule TypeAcceptor\n  # @param type [PAnyType] the type that we accept a visit from\n  # @param guard [RecursionGuard] the guard against self recursion\n  def visit(type, guard)\n  end\nend\n\n# An acceptor that does nothing\nclass NoopTypeAcceptor\n  include TypeAcceptor\n\n  INSTANCE = NoopTypeAcceptor.new\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_asserter.rb",
    "content": "# frozen_string_literal: true\n\n# Utility module for type assertion\n#\nmodule Puppet::Pops::Types\nmodule TypeAsserter\n  # Asserts that a type_to_check is assignable to required_type and raises\n  # a {Puppet::ParseError} if that's not the case\n  #\n  # @param subject [String,Array] String to be prepended to the exception message or Array where the first element is\n  #   a format string and the rest are arguments to that format string\n  # @param expected_type [PAnyType] Expected type\n  # @param type_to_check [PAnyType] Type to check against the required type\n  # @return The type_to_check argument\n  #\n  # @api public\n  def self.assert_assignable(subject, expected_type, type_to_check, &block)\n    report_type_mismatch(subject, expected_type, type_to_check, 'is incorrect', &block) unless expected_type.assignable?(type_to_check)\n    type_to_check\n  end\n\n  # Asserts that a value is an instance of a given type and raises\n  # a {Puppet::ParseError} if that's not the case\n  #\n  # @param subject [String,Array] String to be prepended to the exception message or Array where the first element is\n  #                               a format string and the rest are arguments to that format string\n  # @param expected_type [PAnyType] Expected type for the value\n  # @param value [Object] Value to check\n  # @param nil_ok [Boolean] Can be true to allow nil value. Optional and defaults to false\n  # @return The value argument\n  #\n  # @api public\n  def self.assert_instance_of(subject, expected_type, value, nil_ok = false, &block)\n    unless value.nil? && nil_ok\n      report_type_mismatch(subject, expected_type, TypeCalculator.singleton.infer_set(value), &block) unless expected_type.instance?(value)\n    end\n    value\n  end\n\n  def self.report_type_mismatch(subject, expected_type, actual_type, what = 'has wrong type')\n    subject = yield(subject) if block_given?\n    subject = subject[0] % subject[1..] if subject.is_a?(Array)\n    raise TypeAssertionError.new(\n      TypeMismatchDescriber.singleton.describe_mismatch(\"#{subject} #{what},\", expected_type, actual_type), expected_type, actual_type\n    )\n  end\n  private_class_method :report_type_mismatch\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_assertion_error.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Types\n  # Raised when an assertion of actual type against an expected type fails.\n  #\n  class TypeAssertionError < Puppet::Error\n    # Returns the expected type\n    # @return [PAnyType] expected type\n    attr_reader :expected_type\n\n    # Returns the actual type\n    # @return [PAnyType] actual type\n    attr_reader :actual_type\n\n    # Creates a new instance with a default message, expected, and actual types,\n    #\n    # @param message [String] The default message\n    # @param expected_type [PAnyType] The expected type\n    # @param actual_type [PAnyType] The actual type\n    #\n    def initialize(message, expected_type, actual_type)\n      super(message)\n      @expected_type = expected_type\n      @actual_type = actual_type\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_calculator.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Types\n# The TypeCalculator can answer questions about puppet types.\n#\n# The Puppet type system is primarily based on sub-classing. When asking the type calculator to infer types from Ruby in general, it\n# may not provide the wanted answer; it does not for instance take module inclusions and extensions into account. In general the type\n# system should be unsurprising for anyone being exposed to the notion of type. The type `Data` may require a bit more explanation; this\n# is an abstract type that includes all scalar types, as well as Array with an element type compatible with Data, and Hash with key\n# compatible with scalar and elements compatible with Data. Expressed differently; Data is what you typically express using JSON (with\n# the exception that the Puppet type system also includes Pattern (regular expression) as a scalar.\n#\n# Inference\n# ---------\n# The `infer(o)` method infers a Puppet type for scalar Ruby objects, and for Arrays and Hashes.\n# The inference result is instance specific for single typed collections\n# and allows answering questions about its embedded type. It does not however preserve multiple types in\n# a collection, and can thus not answer questions like `[1,a].infer() =~ Array[Integer, String]` since the inference\n# computes the common type Scalar when combining Integer and String.\n#\n# The `infer_generic(o)` method infers a generic Puppet type for scalar Ruby object, Arrays and Hashes.\n# This inference result does not contain instance specific information; e.g. Array[Integer] where the integer\n# range is the generic default. Just `infer` it also combines types into a common type.\n#\n# The `infer_set(o)` method works like `infer` but preserves all type information. It does not do any\n# reduction into common types or ranges. This method of inference is best suited for answering questions\n# about an object being an instance of a type. It correctly answers: `[1,a].infer_set() =~ Array[Integer, String]`\n#\n# The `generalize!(t)` method modifies an instance specific inference result to a generic. The method mutates\n# the given argument. Basically, this removes string instances from String, and range from Integer and Float.\n#\n# Assignability\n# -------------\n# The `assignable?(t1, t2)` method answers if t2 conforms to t1. The type t2 may be an instance, in which case\n# its type is inferred, or a type.\n#\n# Instance?\n# ---------\n# The `instance?(t, o)` method answers if the given object (instance) is an instance that is assignable to the given type.\n#\n# String\n# ------\n# Creates a string representation of a type.\n#\n# Creation of Type instances\n# --------------------------\n# Instance of the classes in the {Types type model} are used to denote a specific type. It is most convenient\n# to use the {TypeFactory} when creating instances.\n#\n# @note\n#   In general, new instances of the wanted type should be created as they are assigned to models using containment, and a\n#   contained object can only be in one container at a time. Also, the type system may include more details in each type\n#   instance, such as if it may be nil, be empty, contain a certain count etc. Or put differently, the puppet types are not\n#   singletons.\n#\n# All types support `copy` which should be used when assigning a type where it is unknown if it is bound or not\n# to a parent type. A check can be made with `t.eContainer().nil?`\n#\n# Equality and Hash\n# -----------------\n# Type instances are equal in terms of Ruby eql? and `==` if they describe the same type, but they are not `equal?` if they are not\n# the same type instance. Two types that describe the same type have identical hash - this makes them usable as hash keys.\n#\n# Types and Subclasses\n# --------------------\n# In general, the type calculator should be used to answer questions if a type is a subtype of another (using {#assignable?}, or\n# {#instance?} if the question is if a given object is an instance of a given type (or is a subtype thereof).\n# Many of the types also have a Ruby subtype relationship; e.g. PHashType and PArrayType are both subtypes of PCollectionType, and\n# PIntegerType, PFloatType, PStringType,... are subtypes of PScalarType. Even if it is possible to answer certain questions about\n# type by looking at the Ruby class of the types this is considered an implementation detail, and such checks should in general\n# be performed by the type_calculator which implements the type system semantics.\n#\n# The PRuntimeType\n# -------------\n# The PRuntimeType corresponds to a type in the runtime system (currently only supported runtime is 'ruby'). The\n# type has a runtime_type_name that corresponds to a Ruby Class name.\n# A Runtime[ruby] type can be used to describe any ruby class except for the puppet types that are specialized\n# (i.e. PRuntimeType should not be used for Integer, String, etc. since there are specialized types for those).\n# When the type calculator deals with PRuntimeTypes and checks for assignability, it determines the\n# \"common ancestor class\" of two classes.\n# This check is made based on the superclasses of the two classes being compared. In order to perform this, the\n# classes must be present (i.e. they are resolved from the string form in the PRuntimeType to a\n# loaded, instantiated Ruby Class). In general this is not a problem, since the question to produce the common\n# super type for two objects means that the classes must be present or there would have been\n# no instances present in the first place. If however the classes are not present, the type\n# calculator will fall back and state that the two types at least have Any in common.\n#\n# @see TypeFactory for how to create instances of types\n# @see TypeParser how to construct a type instance from a String\n# @see Types for details about the type model\n#\n# Using the Type Calculator\n# -----\n# The type calculator can be directly used via its class methods. If doing time critical work and doing many\n# calls to the type calculator, it is more performant to create an instance and invoke the corresponding\n# instance methods. Note that inference is an expensive operation, rather than inferring the same thing\n# several times, it is in general better to infer once and then copy the result if mutation to a more generic form is\n# required.\n#\n# @api public\n#\nclass TypeCalculator\n  extend Puppet::Concurrent::ThreadLocalSingleton\n\n  # @api public\n  def self.assignable?(t1, t2)\n    singleton.assignable?(t1, t2)\n  end\n\n  # Answers, does the given callable accept the arguments given in args (an array or a tuple)\n  # @param callable [PCallableType] - the callable\n  # @param args [PArrayType, PTupleType] args optionally including a lambda callable at the end\n  # @return [Boolean] true if the callable accepts the arguments\n  #\n  # @api public\n  def self.callable?(callable, args)\n    singleton.callable?(callable, args)\n  end\n\n  # @api public\n  def self.infer(o)\n    singleton.infer(o)\n  end\n\n  # Infers a type if given object may have callable members, else returns nil.\n  # Caller must check for nil or if returned type supports members.\n  # This is a much cheaper call than doing a call to the general infer(o) method.\n  #\n  # @api private\n  def self.infer_callable_methods_t(o)\n    # If being a value that cannot have Pcore based methods callable from Puppet Language\n    if o.is_a?(String) ||\n       o.is_a?(Numeric) ||\n       o.is_a?(TrueClass) ||\n       o.is_a?(FalseClass) ||\n       o.is_a?(Regexp) ||\n       o.instance_of?(Array) ||\n       o.instance_of?(Hash) ||\n       Types::PUndefType::DEFAULT.instance?(o)\n\n      return nil\n    end\n\n    # For other objects (e.g. PObjectType instances, and runtime types) full inference needed, since that will\n    # cover looking into the runtime type registry.\n    #\n    infer(o)\n  end\n\n  # @api public\n  def self.generalize(o)\n    singleton.generalize(o)\n  end\n\n  # @api public\n  def self.infer_set(o)\n    singleton.infer_set(o)\n  end\n\n  # @api public\n  def self.iterable(t)\n    singleton.iterable(t)\n  end\n\n  # @api public\n  #\n  def initialize\n    @infer_visitor = Visitor.new(nil, 'infer', 0, 0)\n    @extract_visitor = Visitor.new(nil, 'extract', 0, 0)\n  end\n\n  # Answers 'can an instance of type t2 be assigned to a variable of type t'.\n  # Does not accept nil/undef unless the type accepts it.\n  #\n  # @api public\n  #\n  def assignable?(t, t2)\n    if t.is_a?(Module)\n      t = type(t)\n    end\n    t.is_a?(PAnyType) ? t.assignable?(t2) : false\n  end\n\n  # Returns an iterable if the t represents something that can be iterated\n  def iterable(t)\n    # Create an iterable on the type if possible\n    Iterable.on(t)\n  end\n\n  # Answers, does the given callable accept the arguments given in args (an array or a tuple)\n  #\n  def callable?(callable, args)\n    callable.is_a?(PAnyType) && callable.callable?(args)\n  end\n\n  # Answers if the two given types describe the same type\n  def equals(left, right)\n    return false unless left.is_a?(PAnyType) && right.is_a?(PAnyType)\n\n    # Types compare per class only - an extra test must be made if the are mutually assignable\n    # to find all types that represent the same type of instance\n    #\n    left == right || (assignable?(right, left) && assignable?(left, right))\n  end\n\n  # Answers 'what is the Puppet Type corresponding to the given Ruby class'\n  # @param c [Module] the class for which a puppet type is wanted\n  # @api public\n  #\n  def type(c)\n    raise ArgumentError, 'Argument must be a Module' unless c.is_a? Module\n\n    # Can't use a visitor here since we don't have an instance of the class\n    if c <= Integer\n      type = PIntegerType::DEFAULT\n    elsif c == Float\n      type = PFloatType::DEFAULT\n    elsif c == Numeric\n      type = PNumericType::DEFAULT\n    elsif c == String\n      type = PStringType::DEFAULT\n    elsif c == Regexp\n      type = PRegexpType::DEFAULT\n    elsif c == NilClass\n      type = PUndefType::DEFAULT\n    elsif c == FalseClass || c == TrueClass\n      type = PBooleanType::DEFAULT\n    elsif c == Class\n      type = PTypeType::DEFAULT\n    elsif c == Array\n      # Assume array of any\n      type = PArrayType::DEFAULT\n    elsif c == Hash\n      # Assume hash of any\n      type = PHashType::DEFAULT\n    else\n      type = PRuntimeType.new(:ruby, c.name)\n    end\n    type\n  end\n\n  # Generalizes value specific types. The generalized type is returned.\n  # @api public\n  def generalize(o)\n    o.is_a?(PAnyType) ? o.generalize : o\n  end\n\n  # Answers 'what is the single common Puppet Type describing o', or if o is an Array or Hash, what is the\n  # single common type of the elements (or keys and elements for a Hash).\n  # @api public\n  #\n  def infer(o)\n    # Optimize the most common cases into direct calls.\n    # Explicit if/elsif/else is faster than case\n    case o\n    when String\n      infer_String(o)\n    when Integer # need subclasses for Ruby < 2.4\n      infer_Integer(o)\n    when Array\n      infer_Array(o)\n    when Hash\n      infer_Hash(o)\n    when Evaluator::PuppetProc\n      infer_PuppetProc(o)\n    else\n      @infer_visitor.visit_this_0(self, o)\n    end\n  end\n\n  def infer_generic(o)\n    generalize(infer(o))\n  end\n\n  # Answers 'what is the set of Puppet Types of o'\n  # @api public\n  #\n  def infer_set(o)\n    if o.instance_of?(Array)\n      infer_set_Array(o)\n    elsif o.instance_of?(Hash)\n      infer_set_Hash(o)\n    elsif o.instance_of?(SemanticPuppet::Version)\n      infer_set_Version(o)\n    else\n      infer(o)\n    end\n  end\n\n  # Answers 'is o an instance of type t'\n  # @api public\n  #\n  def self.instance?(t, o)\n    singleton.instance?(t, o)\n  end\n\n  # Answers 'is o an instance of type t'\n  # @api public\n  #\n  def instance?(t, o)\n    if t.is_a?(Module)\n      t = type(t)\n    end\n    t.is_a?(PAnyType) ? t.instance?(o) : false\n  end\n\n  # Answers if t is a puppet type\n  # @api public\n  #\n  def is_ptype?(t)\n    t.is_a?(PAnyType)\n  end\n\n  # Answers if t represents the puppet type PUndefType\n  # @api public\n  #\n  def is_pnil?(t)\n    t.nil? || t.is_a?(PUndefType)\n  end\n\n  # Answers, 'What is the common type of t1 and t2?'\n  #\n  # TODO: The current implementation should be optimized for performance\n  #\n  # @api public\n  #\n  def common_type(t1, t2)\n    raise ArgumentError, 'two types expected' unless (is_ptype?(t1) || is_pnil?(t1)) && (is_ptype?(t2) || is_pnil?(t2))\n\n    # TODO: This is not right since Scalar U Undef is Any\n    # if either is nil, the common type is the other\n    if is_pnil?(t1)\n      return t2\n    elsif is_pnil?(t2)\n      return t1\n    end\n\n    # If either side is Unit, it is the other type\n    if t1.is_a?(PUnitType)\n      return t2\n    elsif t2.is_a?(PUnitType)\n      return t1\n    end\n\n    # Simple case, one is assignable to the other\n    if assignable?(t1, t2)\n      return t1\n    elsif assignable?(t2, t1)\n      return t2\n    end\n\n    # when both are arrays, return an array with common element type\n    if t1.is_a?(PArrayType) && t2.is_a?(PArrayType)\n      return PArrayType.new(common_type(t1.element_type, t2.element_type))\n    end\n\n    # when both are hashes, return a hash with common key- and element type\n    if t1.is_a?(PHashType) && t2.is_a?(PHashType)\n      key_type = common_type(t1.key_type, t2.key_type)\n      value_type = common_type(t1.value_type, t2.value_type)\n      return PHashType.new(key_type, value_type)\n    end\n\n    # when both are host-classes, reduce to PHostClass[] (since one was not assignable to the other)\n    if t1.is_a?(PClassType) && t2.is_a?(PClassType)\n      return PClassType::DEFAULT\n    end\n\n    # when both are resources, reduce to Resource[T] or Resource[] (since one was not assignable to the other)\n    if t1.is_a?(PResourceType) && t2.is_a?(PResourceType)\n      # only Resource[] unless the type name is the same\n      return t1.type_name == t2.type_name ? PResourceType.new(t1.type_name, nil) : PResourceType::DEFAULT\n    end\n\n    # Integers have range, expand the range to the common range\n    if t1.is_a?(PIntegerType) && t2.is_a?(PIntegerType)\n      return PIntegerType.new([t1.numeric_from, t2.numeric_from].min, [t1.numeric_to, t2.numeric_to].max)\n    end\n\n    # Floats have range, expand the range to the common range\n    if t1.is_a?(PFloatType) && t2.is_a?(PFloatType)\n      return PFloatType.new([t1.numeric_from, t2.numeric_from].min, [t1.numeric_to, t2.numeric_to].max)\n    end\n\n    if t1.is_a?(PStringType) && (t2.is_a?(PStringType) || t2.is_a?(PEnumType))\n      if t2.is_a?(PEnumType)\n        return t1.value.nil? ? PEnumType::DEFAULT : PEnumType.new(t2.values | [t1.value])\n      end\n\n      if t1.size_type.nil? || t2.size_type.nil?\n        return t1.value.nil? || t2.value.nil? ? PStringType::DEFAULT : PEnumType.new([t1.value, t2.value])\n      end\n\n      return PStringType.new(common_type(t1.size_type, t2.size_type))\n    end\n\n    if t1.is_a?(PPatternType) && t2.is_a?(PPatternType)\n      return PPatternType.new(t1.patterns | t2.patterns)\n    end\n\n    if t1.is_a?(PEnumType) && (t2.is_a?(PStringType) || t2.is_a?(PEnumType))\n      # The common type is one that complies with either set\n      if t2.is_a?(PEnumType)\n        return PEnumType.new(t1.values | t2.values)\n      end\n\n      return t2.value.nil? ? PEnumType::DEFAULT : PEnumType.new(t1.values | [t2.value])\n    end\n\n    if t1.is_a?(PVariantType) && t2.is_a?(PVariantType)\n      # The common type is one that complies with either set\n      return PVariantType.maybe_create(t1.types | t2.types)\n    end\n\n    if t1.is_a?(PRegexpType) && t2.is_a?(PRegexpType)\n      # if they were identical, the general rule would return a parameterized regexp\n      # since they were not, the result is a generic regexp type\n      return PRegexpType::DEFAULT\n    end\n\n    if t1.is_a?(PCallableType) && t2.is_a?(PCallableType)\n      # They do not have the same signature, and one is not assignable to the other,\n      # what remains is the most general form of Callable\n      return PCallableType::DEFAULT\n    end\n\n    # Common abstract types, from most specific to most general\n    if common_numeric?(t1, t2)\n      return PNumericType::DEFAULT\n    end\n\n    if common_scalar_data?(t1, t2)\n      return PScalarDataType::DEFAULT\n    end\n\n    if common_scalar?(t1, t2)\n      return PScalarType::DEFAULT\n    end\n\n    if common_data?(t1, t2)\n      return TypeFactory.data\n    end\n\n    # Meta types Type[Integer] + Type[String] => Type[Data]\n    if t1.is_a?(PTypeType) && t2.is_a?(PTypeType)\n      return PTypeType.new(common_type(t1.type, t2.type))\n    end\n\n    if common_rich_data?(t1, t2)\n      return TypeFactory.rich_data\n    end\n\n    # If both are Runtime types\n    if t1.is_a?(PRuntimeType) && t2.is_a?(PRuntimeType)\n      if t1.runtime == t2.runtime && t1.runtime_type_name == t2.runtime_type_name\n        return t1\n      end\n\n      # finding the common super class requires that names are resolved to class\n      # NOTE: This only supports runtime type of :ruby\n      c1 = ClassLoader.provide_from_type(t1)\n      c2 = ClassLoader.provide_from_type(t2)\n      if c1 && c2\n        c2_superclasses = superclasses(c2)\n        superclasses(c1).each do |c1_super|\n          c2_superclasses.each do |c2_super|\n            if c1_super == c2_super\n              return PRuntimeType.new(:ruby, c1_super.name)\n            end\n          end\n        end\n      end\n    end\n\n    # They better both be Any type, or the wrong thing was asked and nil is returned\n    t1.is_a?(PAnyType) && t2.is_a?(PAnyType) ? PAnyType::DEFAULT : nil\n  end\n\n  # Produces the superclasses of the given class, including the class\n  def superclasses(c)\n    result = [c]\n    while s = c.superclass # rubocop:disable Lint/AssignmentInCondition\n      result << s\n      c = s\n    end\n    result\n  end\n\n  # Reduces an enumerable of types to a single common type.\n  # @api public\n  #\n  def reduce_type(enumerable)\n    enumerable.reduce(nil) { |memo, t| common_type(memo, t) }\n  end\n\n  # Reduce an enumerable of objects to a single common type\n  # @api public\n  #\n  def infer_and_reduce_type(enumerable)\n    reduce_type(enumerable.map { |o| infer(o) })\n  end\n\n  # The type of all modules is PTypeType\n  # @api private\n  #\n  def infer_Module(o)\n    PTypeType.new(PRuntimeType.new(:ruby, o.name))\n  end\n\n  # @api private\n  def infer_Closure(o)\n    o.type\n  end\n\n  # @api private\n  def infer_Iterator(o)\n    PIteratorType.new(o.element_type)\n  end\n\n  # @api private\n  def infer_Function(o)\n    o.class.dispatcher.to_type\n  end\n\n  # @api private\n  def infer_Object(o)\n    if o.is_a?(PuppetObject)\n      o._pcore_type\n    else\n      name = o.class.name\n      return PRuntimeType.new(:ruby, nil) if name.nil? # anonymous class that doesn't implement PuppetObject is impossible to infer\n\n      ir = Loaders.implementation_registry\n      type = ir.nil? ? nil : ir.type_for_module(name)\n      return PRuntimeType.new(:ruby, name) if type.nil?\n\n      if type.is_a?(PObjectType) && type.parameterized?\n        type = PObjectTypeExtension.create_from_instance(type, o)\n      end\n      type\n    end\n  end\n\n  # The type of all types is PTypeType\n  # @api private\n  #\n  def infer_PAnyType(o)\n    PTypeType.new(o)\n  end\n\n  # The type of all types is PTypeType\n  # This is the metatype short circuit.\n  # @api private\n  #\n  def infer_PTypeType(o)\n    PTypeType.new(o)\n  end\n\n  # @api private\n  def infer_String(o)\n    PStringType.new(o)\n  end\n\n  # @api private\n  def infer_Float(o)\n    PFloatType.new(o, o)\n  end\n\n  # @api private\n  def infer_Integer(o)\n    PIntegerType.new(o, o)\n  end\n\n  # @api private\n  def infer_Regexp(o)\n    PRegexpType.new(o)\n  end\n\n  # @api private\n  def infer_NilClass(o)\n    PUndefType::DEFAULT\n  end\n\n  # @api private\n  # @param o [Proc]\n  def infer_Proc(o)\n    min = 0\n    max = 0\n    mapped_types = o.parameters.map do |p|\n      case p[0]\n      when :rest\n        max = :default\n        break PAnyType::DEFAULT\n      when :req\n        min += 1\n      end\n      max += 1\n      PAnyType::DEFAULT\n    end\n    param_types = Types::PTupleType.new(mapped_types, Types::PIntegerType.new(min, max))\n    Types::PCallableType.new(param_types)\n  end\n\n  # @api private\n  def infer_PuppetProc(o)\n    infer_Closure(o.closure)\n  end\n\n  # Inference of :default as PDefaultType, and all other are Ruby[Symbol]\n  # @api private\n  def infer_Symbol(o)\n    case o\n    when :default\n      PDefaultType::DEFAULT\n    when :undef\n      PUndefType::DEFAULT\n    else\n      infer_Object(o)\n    end\n  end\n\n  # @api private\n  def infer_Sensitive(o)\n    PSensitiveType.new(infer(o.unwrap))\n  end\n\n  # @api private\n  def infer_Timespan(o)\n    PTimespanType.new(o, o)\n  end\n\n  # @api private\n  def infer_Timestamp(o)\n    PTimestampType.new(o, o)\n  end\n\n  # @api private\n  def infer_TrueClass(o)\n    PBooleanType::TRUE\n  end\n\n  # @api private\n  def infer_FalseClass(o)\n    PBooleanType::FALSE\n  end\n\n  # @api private\n  def infer_URI(o)\n    PURIType.new(o)\n  end\n\n  # @api private\n  # A Puppet::Parser::Resource, or Puppet::Resource\n  #\n  def infer_Resource(o)\n    # Only Puppet::Resource can have a title that is a symbol :undef, a PResource cannot.\n    # A mapping must be made to empty string. A nil value will result in an error later\n    title = o.title\n    title = '' if :undef == title\n    PTypeType.new(PResourceType.new(o.type.to_s, title))\n  end\n\n  # @api private\n  def infer_Array(o)\n    if o.instance_of?(Array)\n      if o.empty?\n        PArrayType::EMPTY\n      else\n        PArrayType.new(infer_and_reduce_type(o), size_as_type(o))\n      end\n    else\n      infer_Object(o)\n    end\n  end\n\n  # @api private\n  def infer_Binary(o)\n    PBinaryType::DEFAULT\n  end\n\n  # @api private\n  def infer_Version(o)\n    PSemVerType::DEFAULT\n  end\n\n  # @api private\n  def infer_VersionRange(o)\n    PSemVerRangeType::DEFAULT\n  end\n\n  # @api private\n  def infer_Hash(o)\n    if o.instance_of?(Hash)\n      if o.empty?\n        PHashType::EMPTY\n      else\n        ktype = infer_and_reduce_type(o.keys)\n        etype = infer_and_reduce_type(o.values)\n        PHashType.new(ktype, etype, size_as_type(o))\n      end\n    else\n      infer_Object(o)\n    end\n  end\n\n  def size_as_type(collection)\n    size = collection.size\n    PIntegerType.new(size, size)\n  end\n\n  # Common case for everything that intrinsically only has a single type\n  def infer_set_Object(o)\n    infer(o)\n  end\n\n  def infer_set_Array(o)\n    if o.empty?\n      PArrayType::EMPTY\n    else\n      PTupleType.new(o.map { |x| infer_set(x) })\n    end\n  end\n\n  def infer_set_Hash(o)\n    if o.empty?\n      PHashType::EMPTY\n    elsif o.keys.all? { |k| PStringType::NON_EMPTY.instance?(k) }\n      PStructType.new(o.each_pair.map { |k, v| PStructElement.new(PStringType.new(k), infer_set(v)) })\n    else\n      ktype = PVariantType.maybe_create(o.keys.map { |k| infer_set(k) })\n      etype = PVariantType.maybe_create(o.values.map { |e| infer_set(e) })\n      PHashType.new(unwrap_single_variant(ktype), unwrap_single_variant(etype), size_as_type(o))\n    end\n  end\n\n  # @api private\n  def infer_set_Version(o)\n    PSemVerType.new([SemanticPuppet::VersionRange.new(o, o)])\n  end\n\n  def unwrap_single_variant(possible_variant)\n    if possible_variant.is_a?(PVariantType) && possible_variant.types.size == 1\n      possible_variant.types[0]\n    else\n      possible_variant\n    end\n  end\n\n  # Transform int range to a size constraint\n  # if range == nil the constraint is 1,1\n  # if range.from == nil min size = 1\n  # if range.to == nil max size == Infinity\n  #\n  def size_range(range)\n    return [1, 1] if range.nil?\n\n    from = range.from\n    to = range.to\n    x = from.nil? ? 1 : from\n    y = to.nil? ? TheInfinity : to\n    [x, y]\n  end\n\n  # @api private\n  def self.is_kind_of_callable?(t, optional = true)\n    t.is_a?(PAnyType) && t.kind_of_callable?(optional)\n  end\n\n  def max(a, b)\n    a >= b ? a : b\n  end\n\n  def min(a, b)\n    a <= b ? a : b\n  end\n\n  # Produces the tuple entry at the given index given a tuple type, its from/to constraints on the last\n  # type, and an index.\n  # Produces nil if the index is out of bounds\n  # from must be less than to, and from may not be less than 0\n  #\n  # @api private\n  #\n  def tuple_entry_at(tuple_t, from, to, index)\n    regular = (tuple_t.types.size - 1)\n    if index < regular\n      tuple_t.types[index]\n    elsif index < regular + to\n      # in the varargs part\n      tuple_t.types[-1]\n    else\n      nil\n    end\n  end\n\n  # Debugging to_s to reduce the amount of output\n  def to_s\n    '[a TypeCalculator]'\n  end\n\n  private\n\n  def common_rich_data?(t1, t2)\n    d = TypeFactory.rich_data\n    d.assignable?(t1) && d.assignable?(t2)\n  end\n\n  def common_data?(t1, t2)\n    d = TypeFactory.data\n    d.assignable?(t1) && d.assignable?(t2)\n  end\n\n  def common_scalar_data?(t1, t2)\n    PScalarDataType::DEFAULT.assignable?(t1) && PScalarDataType::DEFAULT.assignable?(t2)\n  end\n\n  def common_scalar?(t1, t2)\n    PScalarType::DEFAULT.assignable?(t1) && PScalarType::DEFAULT.assignable?(t2)\n  end\n\n  def common_numeric?(t1, t2)\n    PNumericType::DEFAULT.assignable?(t1) && PNumericType::DEFAULT.assignable?(t2)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_conversion_error.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops::Types\n  # Raised when a conversion of a value to another type failed.\n  #\n  class TypeConversionError < Puppet::Error; end\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_factory.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# Helper module that makes creation of type objects simpler.\n# @api public\n#\nmodule TypeFactory\n  @type_calculator = TypeCalculator.singleton\n\n  # Clears caches - used when testing\n  def self.clear\n    # these types are cached and needs to be nulled as the representation may change if loaders are cleared\n    @data_t = nil\n    @rich_data_t = nil\n    @rich_data_key_t = nil\n    @array_of_data_t = nil\n    @hash_of_data_t = nil\n    @error_t = nil\n    @task_t = nil\n    @deferred_t = nil\n  end\n\n  # Produces the Integer type\n  # @api public\n  #\n  def self.integer\n    PIntegerType::DEFAULT\n  end\n\n  # Produces an Integer range type\n  # @api public\n  #\n  def self.range(from, to)\n    # optimize eq with symbol (faster when it is left)\n    from = :default == from if from == 'default'\n    to = :default if to == 'default'\n    PIntegerType.new(from, to)\n  end\n\n  # Produces a Float range type\n  # @api public\n  #\n  def self.float_range(from, to)\n    # optimize eq with symbol (faster when it is left)\n    from = Float(from) unless :default == from || from.nil?\n    to = Float(to) unless :default == to || to.nil?\n    PFloatType.new(from, to)\n  end\n\n  # Produces the Float type\n  # @api public\n  #\n  def self.float\n    PFloatType::DEFAULT\n  end\n\n  # Produces the Sensitive type\n  # @api public\n  #\n  def self.sensitive(type = nil)\n    PSensitiveType.new(type)\n  end\n\n  # Produces the Numeric type\n  # @api public\n  #\n  def self.numeric\n    PNumericType::DEFAULT\n  end\n\n  # Produces the Init type\n  # @api public\n  def self.init(*args)\n    case args.size\n    when 0\n      PInitType::DEFAULT\n    when 1\n      type = args[0]\n      type.nil? ? PInitType::DEFAULT : PInitType.new(type, EMPTY_ARRAY)\n    else\n      type = args.shift\n      PInitType.new(type, args)\n    end\n  end\n\n  # Produces the Iterable type\n  # @api public\n  #\n  def self.iterable(elem_type = nil)\n    elem_type.nil? ? PIterableType::DEFAULT : PIterableType.new(elem_type)\n  end\n\n  # Produces the Iterator type\n  # @api public\n  #\n  def self.iterator(elem_type = nil)\n    elem_type.nil? ? PIteratorType::DEFAULT : PIteratorType.new(elem_type)\n  end\n\n  # Produces a string representation of the type\n  # @api public\n  #\n  def self.label(t)\n    @type_calculator.string(t)\n  end\n\n  # Produces the String type based on nothing, a string value that becomes an exact match constraint, or a parameterized\n  # Integer type that constraints the size.\n  #\n  # @api public\n  #\n  def self.string(size_type_or_value = nil, *deprecated_second_argument)\n    if deprecated_second_argument.empty?\n      size_type_or_value.nil? ? PStringType::DEFAULT : PStringType.new(size_type_or_value)\n    else\n      if Puppet[:strict] != :off\n        # TRANSLATORS 'TypeFactory#string' is a class and method name and should not be translated\n        message = _(\"Passing more than one argument to TypeFactory#string is deprecated\")\n        Puppet.warn_once('deprecations', \"TypeFactory#string_multi_args\", message)\n      end\n      deprecated_second_argument.size == 1 ? PStringType.new(deprecated_second_argument[0]) : PEnumType.new(*deprecated_second_argument)\n    end\n  end\n\n  # Produces the Optional type, i.e. a short hand for Variant[T, Undef]\n  # If the given 'optional_type' argument is a String, then it will be\n  # converted into a String type that represents that string.\n  #\n  # @param optional_type [String,PAnyType,nil] the optional type\n  # @return [POptionalType] the created type\n  #\n  # @api public\n  #\n  def self.optional(optional_type = nil)\n    if optional_type.nil?\n      POptionalType::DEFAULT\n    else\n      POptionalType.new(type_of(optional_type.is_a?(String) ? string(optional_type) : type_of(optional_type)))\n    end\n  end\n\n  # Produces the Enum type, optionally with specific string values\n  # @api public\n  #\n  def self.enum(*values)\n    last = values.last\n    case_insensitive = false\n    if last == true || last == false\n      case_insensitive = last\n      values = values[0...-1]\n    end\n    PEnumType.new(values, case_insensitive)\n  end\n\n  # Produces the Variant type, optionally with the \"one of\" types\n  # @api public\n  #\n  def self.variant(*types)\n    PVariantType.maybe_create(types.map { |v| type_of(v) })\n  end\n\n  # Produces the Struct type, either a non parameterized instance representing\n  # all structs (i.e. all hashes) or a hash with entries where the key is\n  # either a literal String, an Enum with one entry, or a String representing exactly one value.\n  # The key type may also be wrapped in a NotUndef or an Optional.\n  #\n  # The value can be a ruby class, a String (interpreted as the name of a ruby class) or\n  # a Type.\n  #\n  # @param hash [{String,PAnyType=>PAnyType}] key => value hash\n  # @return [PStructType] the created Struct type\n  #\n  def self.struct(hash = {})\n    tc = @type_calculator\n    elements = hash.map do |key_type, value_type|\n      value_type = type_of(value_type)\n      raise ArgumentError, 'Struct element value_type must be a Type' unless value_type.is_a?(PAnyType)\n\n      # TODO: Should have stricter name rule\n      if key_type.is_a?(String)\n        raise ArgumentError, 'Struct element key cannot be an empty String' if key_type.empty?\n\n        key_type = string(key_type)\n        # Must make key optional if the value can be Undef\n        key_type = optional(key_type) if tc.assignable?(value_type, PUndefType::DEFAULT)\n      else\n        # assert that the key type is one of String[1], NotUndef[String[1]] and Optional[String[1]]\n        case key_type\n        when PNotUndefType\n          # We can loose the NotUndef wrapper here since String[1] isn't optional anyway\n          key_type = key_type.type\n          s = key_type\n        when POptionalType\n          s = key_type.optional_type\n        when PStringType\n          s = key_type\n        when PEnumType\n          s = key_type.values.size == 1 ? PStringType.new(key_type.values[0]) : nil\n        else\n          raise ArgumentError, \"Illegal Struct member key type. Expected NotUndef, Optional, String, or Enum. Got: #{key_type.class.name}\"\n        end\n        unless s.is_a?(PStringType) && !s.value.nil?\n          raise ArgumentError, \"Unable to extract a non-empty literal string from Struct member key type #{tc.string(key_type)}\"\n        end\n      end\n      PStructElement.new(key_type, value_type)\n    end\n    PStructType.new(elements)\n  end\n\n  # Produces an `Object` type from the given _hash_ that represents the features of the object\n  #\n  # @param hash [{String=>Object}] the hash of feature groups\n  # @return [PObjectType] the created type\n  #\n  def self.object(hash = nil, loader = nil)\n    hash.nil? || hash.empty? ? PObjectType::DEFAULT : PObjectType.new(hash, loader)\n  end\n\n  def self.type_set(hash = nil)\n    hash.nil? || hash.empty? ? PTypeSetType::DEFAULT : PTypeSetType.new(hash)\n  end\n\n  def self.timestamp(*args)\n    case args.size\n    when 0\n      PTimestampType::DEFAULT\n    else\n      PTimestampType.new(*args)\n    end\n  end\n\n  def self.timespan(*args)\n    case args.size\n    when 0\n      PTimespanType::DEFAULT\n    else\n      PTimespanType.new(*args)\n    end\n  end\n\n  def self.tuple(types = [], size_type = nil)\n    PTupleType.new(types.map { |elem| type_of(elem) }, size_type)\n  end\n\n  # Produces the Boolean type\n  # @api public\n  #\n  def self.boolean(value = nil)\n    if value.nil?\n      PBooleanType::DEFAULT\n    else\n      value ? PBooleanType::TRUE : PBooleanType::FALSE\n    end\n  end\n\n  # Produces the Any type\n  # @api public\n  #\n  def self.any\n    PAnyType::DEFAULT\n  end\n\n  # Produces the Regexp type\n  # @param pattern [Regexp, String, nil] (nil) The regular expression object or\n  #   a regexp source string, or nil for bare type\n  # @api public\n  #\n  def self.regexp(pattern = nil)\n    pattern ?  PRegexpType.new(pattern) : PRegexpType::DEFAULT\n  end\n\n  def self.pattern(*regular_expressions)\n    patterns = regular_expressions.map do |re|\n      case re\n      when String\n        re_t = PRegexpType.new(re)\n        re_t.regexp # compile it to catch errors\n        re_t\n\n      when Regexp\n        PRegexpType.new(re)\n\n      when PRegexpType\n        re\n\n      when PPatternType\n        re.patterns\n\n      else\n        raise ArgumentError, \"Only String, Regexp, Pattern-Type, and Regexp-Type are allowed: got '#{re.class}\"\n      end\n    end.flatten.uniq\n    PPatternType.new(patterns)\n  end\n\n  # Produces the Scalar type\n  # @api public\n  #\n  def self.scalar\n    PScalarType::DEFAULT\n  end\n\n  # Produces the ScalarData type\n  # @api public\n  #\n  def self.scalar_data\n    PScalarDataType::DEFAULT\n  end\n\n  # Produces a CallableType matching all callables\n  # @api public\n  #\n  def self.all_callables\n    PCallableType::DEFAULT\n  end\n\n  # Produces a Callable type with one signature without support for a block\n  # Use #with_block, or #with_optional_block to add a block to the callable\n  # If no parameters are given, the Callable will describe a signature\n  # that does not accept parameters. To create a Callable that matches all callables\n  # use {#all_callables}.\n  #\n  # The params is a list of types, where the three last entries may be\n  # optionally followed by min, max count, and a Callable which is taken as the\n  # block_type.\n  # If neither min or max are specified the parameters must match exactly.\n  # A min < params.size means that the difference are optional.\n  # If max > params.size means that the last type repeats.\n  # if max is :default, the max value is unbound (infinity).\n  #\n  # Params are given as a sequence of arguments to {#type_of}.\n  #\n  def self.callable(*params)\n    if params.size == 2 && params[0].is_a?(Array)\n      return_t = type_of(params[1])\n      params = params[0]\n    else\n      return_t = nil\n    end\n    last_callable = TypeCalculator.is_kind_of_callable?(params.last)\n    block_t = last_callable ? params.pop : nil\n\n    # compute a size_type for the signature based on the two last parameters\n    if is_range_parameter?(params[-2]) && is_range_parameter?(params[-1])\n      size_type = range(params[-2], params[-1])\n      params = params[0, params.size - 2]\n    elsif is_range_parameter?(params[-1])\n      size_type = range(params[-1], :default)\n      params = params[0, params.size - 1]\n    else\n      size_type = nil\n    end\n\n    types = params.map { |p| type_of(p) }\n\n    # If the specification requires types, and none were given, a Unit type is used\n    if types.empty? && !size_type.nil? && size_type.range[1] > 0\n      types << PUnitType::DEFAULT\n    end\n    # create a signature\n    tuple_t = tuple(types, size_type)\n    PCallableType.new(tuple_t, block_t, return_t)\n  end\n\n  # Produces the abstract type Collection\n  # @api public\n  #\n  def self.collection(size_type = nil)\n    size_type.nil? ? PCollectionType::DEFAULT : PCollectionType.new(size_type)\n  end\n\n  # Produces the Data type\n  # @api public\n  #\n  def self.data\n    @data_t ||= TypeParser.singleton.parse('Data', Loaders.static_loader)\n  end\n\n  # Produces the RichData type\n  # @api public\n  #\n  def self.rich_data\n    @rich_data_t ||= TypeParser.singleton.parse('RichData', Loaders.static_loader)\n  end\n\n  # Produces the RichData type\n  # @api public\n  #\n  def self.rich_data_key\n    @rich_data_key_t ||= TypeParser.singleton.parse('RichDataKey', Loaders.static_loader)\n  end\n\n  # Creates an instance of the Undef type\n  # @api public\n  def self.undef\n    PUndefType::DEFAULT\n  end\n\n  # Creates an instance of the Default type\n  # @api public\n  def self.default\n    PDefaultType::DEFAULT\n  end\n\n  # Creates an instance of the Binary type\n  # @api public\n  def self.binary\n    PBinaryType::DEFAULT\n  end\n\n  # Produces an instance of the abstract type PCatalogEntryType\n  def self.catalog_entry\n    PCatalogEntryType::DEFAULT\n  end\n\n  # Produces an instance of the SemVerRange type\n  def self.sem_ver_range\n    PSemVerRangeType::DEFAULT\n  end\n\n  # Produces an instance of the SemVer type\n  def self.sem_ver(*ranges)\n    ranges.empty? ? PSemVerType::DEFAULT : PSemVerType.new(ranges)\n  end\n\n  # Produces a PResourceType with a String type_name A PResourceType with a nil\n  # or empty name is compatible with any other PResourceType.  A PResourceType\n  # with a given name is only compatible with a PResourceType with the same\n  # name.  (There is no resource-type subtyping in Puppet (yet)).\n  #\n  def self.resource(type_name = nil, title = nil)\n    case type_name\n    when PResourceType\n      PResourceType.new(type_name.type_name, title)\n    when String\n      type_name = TypeFormatter.singleton.capitalize_segments(type_name)\n      raise ArgumentError, \"Illegal type name '#{type_name}'\" unless type_name =~ Patterns::CLASSREF_EXT\n\n      PResourceType.new(type_name, title)\n    when nil\n      raise ArgumentError, 'The type name cannot be nil, if title is given' unless title.nil?\n\n      PResourceType::DEFAULT\n    else\n      raise ArgumentError, \"The type name cannot be a #{type_name.class.name}\"\n    end\n  end\n\n  # Produces PClassType with a string class_name.  A PClassType with\n  # nil or empty name is compatible with any other PClassType.  A\n  # PClassType with a given name is only compatible with a PClassType\n  # with the same name.\n  #\n  def self.host_class(class_name = nil)\n    if class_name.nil?\n      PClassType::DEFAULT\n    else\n      PClassType.new(class_name.sub(/^::/, ''))\n    end\n  end\n\n  # Produces a type for Array[o] where o is either a type, or an instance for\n  # which a type is inferred.\n  # @api public\n  #\n  def self.array_of(o, size_type = nil)\n    PArrayType.new(type_of(o), size_type)\n  end\n\n  # Produces a type for Hash[Scalar, o] where o is either a type, or an\n  # instance for which a type is inferred.\n  # @api public\n  #\n  def self.hash_of(value, key = scalar, size_type = nil)\n    PHashType.new(type_of(key), type_of(value), size_type)\n  end\n\n  # Produces a type for Hash[key,value,size]\n  # @param key_type [PAnyType] the key type\n  # @param value_type [PAnyType] the value type\n  # @param size_type [PIntegerType]\n  # @return [PHashType] the created hash type\n  # @api public\n  #\n  def self.hash_kv(key_type, value_type, size_type = nil)\n    PHashType.new(key_type, value_type, size_type)\n  end\n\n  # Produces a type for Array[Any]\n  # @api public\n  #\n  def self.array_of_any\n    PArrayType::DEFAULT\n  end\n\n  # Produces a type for Array[Data]\n  # @api public\n  #\n  def self.array_of_data\n    @array_of_data_t = PArrayType.new(data)\n  end\n\n  # Produces a type for Hash[Any,Any]\n  # @api public\n  #\n  def self.hash_of_any\n    PHashType::DEFAULT\n  end\n\n  # Produces a type for Hash[String,Data]\n  # @api public\n  #\n  def self.hash_of_data\n    @hash_of_data_t = PHashType.new(string, data)\n  end\n\n  # Produces a type for NotUndef[T]\n  # The given 'inst_type' can be a string in which case it will be converted into\n  # the type String[inst_type].\n  #\n  # @param inst_type [Type,String] the type to qualify\n  # @return [PNotUndefType] the NotUndef type\n  #\n  # @api public\n  #\n  def self.not_undef(inst_type = nil)\n    inst_type = string(inst_type) if inst_type.is_a?(String)\n    PNotUndefType.new(inst_type)\n  end\n\n  # Produces a type for Type[T]\n  # @api public\n  #\n  def self.type_type(inst_type = nil)\n    inst_type.nil? ? PTypeType::DEFAULT : PTypeType.new(inst_type)\n  end\n\n  # Produces a type for Error\n  # @api public\n  #\n  def self.error\n    @error_t ||= TypeParser.singleton.parse('Error', Loaders.loaders.puppet_system_loader)\n  end\n\n  def self.task\n    @task_t ||= TypeParser.singleton.parse('Task')\n  end\n\n  def self.deferred\n    @deferred_t ||= TypeParser.singleton.parse('Deferred')\n  end\n\n  # Produces a type for URI[String or Hash]\n  # @api public\n  #\n  def self.uri(string_uri_or_hash = nil)\n    string_uri_or_hash.nil? ? PURIType::DEFAULT : PURIType.new(string_uri_or_hash)\n  end\n\n  # Produce a type corresponding to the class of given unless given is a\n  # String, Class or a PAnyType.  When a String is given this is taken as\n  # a classname.\n  #\n  def self.type_of(o)\n    case o\n    when Class\n      @type_calculator.type(o)\n    when PAnyType\n      o\n    when String\n      PRuntimeType.new(:ruby, o)\n    else\n      @type_calculator.infer_generic(o)\n    end\n  end\n\n  # Produces a type for a class or infers a type for something that is not a\n  # class\n  # @note\n  #   To get the type for the class' class use `TypeCalculator.infer(c)`\n  #\n  # @overload ruby(o)\n  #   @param o [Class] produces the type corresponding to the class (e.g.\n  #     Integer becomes PIntegerType)\n  # @overload ruby(o)\n  #   @param o [Object] produces the type corresponding to the instance class\n  #     (e.g. 3 becomes PIntegerType)\n  #\n  # @api public\n  #\n  def self.ruby(o)\n    if o.is_a?(Class)\n      @type_calculator.type(o)\n    else\n      PRuntimeType.new(:ruby, o.class.name)\n    end\n  end\n\n  # Generic creator of a RuntimeType[\"ruby\"] - allows creating the Ruby type\n  # with nil name, or String name.  Also see ruby(o) which performs inference,\n  # or mapps a Ruby Class to its name.\n  #\n  def self.ruby_type(class_name = nil)\n    PRuntimeType.new(:ruby, class_name)\n  end\n\n  # Generic creator of a RuntimeType - allows creating the type with nil or\n  # String runtime_type_name.  Also see ruby_type(o) and ruby(o).\n  #\n  def self.runtime(runtime = nil, runtime_type_name = nil)\n    runtime = runtime.to_sym if runtime.is_a?(String)\n    PRuntimeType.new(runtime, runtime_type_name)\n  end\n\n  # Returns the type alias for the given expression\n  # @param name [String] the name of the unresolved type\n  # @param expression [Model::Expression] an expression that will evaluate to a type\n  # @return [PTypeAliasType] the type alias\n  def self.type_alias(name = nil, expression = nil)\n    name.nil? ? PTypeAliasType::DEFAULT : PTypeAliasType.new(name, expression)\n  end\n\n  # Returns the type that represents a type reference with a given name and optional\n  # parameters.\n  # @param type_string [String] the string form of the type\n  # @return [PTypeReferenceType] the type reference\n  def self.type_reference(type_string = nil)\n    type_string.nil? ? PTypeReferenceType::DEFAULT : PTypeReferenceType.new(type_string)\n  end\n\n  # Returns true if the given type t is of valid range parameter type (integer\n  # or literal default).\n  def self.is_range_parameter?(t)\n    t.is_a?(Integer) || t == 'default' || :default == t\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_formatter.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\nmodule Puppet::Pops\nmodule Types\n# String\n# ------\n# Creates a string representation of a type.\n#\n# @api public\n#\nclass TypeFormatter\n  extend Puppet::Concurrent::ThreadLocalSingleton\n\n  # Produces a String representation of the given type.\n  # @param t [PAnyType] the type to produce a string form\n  # @return [String] the type in string form\n  #\n  # @api public\n  #\n  def self.string(t)\n    singleton.string(t)\n  end\n\n  def initialize\n    @string_visitor = Visitor.new(nil, 'string', 0, 0)\n  end\n\n  def expanded\n    tf = clone\n    tf.instance_variable_set(:@expanded, true)\n    tf\n  end\n\n  def indented(indent = 0, indent_width = 2)\n    tf = clone\n    tf.instance_variable_set(:@indent, indent)\n    tf.instance_variable_set(:@indent_width, indent_width)\n    tf\n  end\n\n  def ruby(ref_ctor)\n    tf = clone\n    tf.instance_variable_set(:@ruby, true)\n    tf.instance_variable_set(:@ref_ctor, ref_ctor)\n    tf\n  end\n\n  # Produces a string representing the type\n  # @api public\n  #\n  def string(t)\n    @bld = ''.dup\n    append_string(t)\n    @bld\n  end\n\n  # Produces an string containing newline characters and indentation that represents the given\n  # type or literal _t_.\n  #\n  # @param t [Object] the type or literal to produce a string for\n  # @param indent [Integer] the current indentation level\n  # @param indent_width [Integer] the number of spaces to use for one indentation\n  #\n  # @api public\n  def indented_string(t, indent = 0, indent_width = 2)\n    @bld = ''.dup\n    append_indented_string(t, indent, indent_width)\n    @bld\n  end\n\n  # @api private\n  def append_indented_string(t, indent = 0, indent_width = 2, skip_initial_indent = false)\n    save_indent = @indent\n    save_indent_width = @indent_width\n    @indent = indent\n    @indent_width = indent_width\n    begin\n      (@indent * @indent_width).times { @bld << ' ' } unless skip_initial_indent\n      append_string(t)\n      @bld << \"\\n\"\n    ensure\n      @indent = save_indent\n      @indent_width = save_indent_width\n    end\n  end\n\n  # @api private\n  def ruby_string(ref_ctor, indent, t)\n    @ruby = true\n    @ref_ctor = ref_ctor\n    begin\n      indented_string(t, indent)\n    ensure\n      @ruby = nil\n      @ref_ctor = nil\n    end\n  end\n\n  def append_default\n    @bld << 'default'\n  end\n\n  def append_string(t)\n    if @ruby && t.is_a?(PAnyType)\n      @ruby = false\n      begin\n        @bld << @ref_ctor << '('\n        @string_visitor.visit_this_0(self, TypeFormatter.new.string(t))\n        @bld << ')'\n      ensure\n        @ruby = true\n      end\n    else\n      @string_visitor.visit_this_0(self, t)\n    end\n  end\n\n  # Produces a string representing the type where type aliases have been expanded\n  # @api public\n  #\n  def alias_expanded_string(t)\n    @expanded = true\n    begin\n      string(t)\n    ensure\n      @expanded = false\n    end\n  end\n\n  # Produces a debug string representing the type (possibly with more information that the regular string format)\n  # @api public\n  #\n  def debug_string(t)\n    @debug = true\n    begin\n      string(t)\n    ensure\n      @debug = false\n    end\n  end\n\n  # @api private\n  def string_PAnyType(_); @bld << 'Any'; end\n\n  # @api private\n  def string_PUndefType(_); @bld << 'Undef'; end\n\n  # @api private\n  def string_PDefaultType(_); @bld << 'Default'; end\n\n  # @api private\n  def string_PBooleanType(t)\n    append_array('Boolean', t.value.nil?) { append_string(t.value) }\n  end\n\n  # @api private\n  def string_PScalarType(_); @bld << 'Scalar'; end\n\n  # @api private\n  def string_PScalarDataType(_); @bld << 'ScalarData'; end\n\n  # @api private\n  def string_PNumericType(_); @bld << 'Numeric'; end\n\n  # @api private\n  def string_PBinaryType(_); @bld << 'Binary'; end\n\n  # @api private\n  def string_PIntegerType(t)\n    append_array('Integer', t.unbounded?) { append_elements(range_array_part(t)) }\n  end\n\n  # @api private\n  def string_PTypeType(t)\n    append_array('Type', t.type.nil?) { append_string(t.type) }\n  end\n\n  # @api private\n  def string_PInitType(t)\n    append_array('Init', t.type.nil?) { append_strings([t.type, *t.init_args]) }\n  end\n\n  # @api private\n  def string_PIterableType(t)\n    append_array('Iterable', t.element_type.nil?) { append_string(t.element_type) }\n  end\n\n  # @api private\n  def string_PIteratorType(t)\n    append_array('Iterator', t.element_type.nil?) { append_string(t.element_type) }\n  end\n\n  # @api private\n  def string_PFloatType(t)\n    append_array('Float', t.unbounded?) { append_elements(range_array_part(t)) }\n  end\n\n  # @api private\n  def string_PRegexpType(t)\n    append_array('Regexp', t.pattern.nil?) { append_string(t.regexp) }\n  end\n\n  # @api private\n  def string_PStringType(t)\n    range = range_array_part(t.size_type)\n    append_array('String', range.empty? && !(@debug && !t.value.nil?)) do\n      if @debug\n        append_elements(range, !t.value.nil?)\n        append_string(t.value) unless t.value.nil?\n      else\n        append_elements(range)\n      end\n    end\n  end\n\n  # @api private\n  def string_PEnumType(t)\n    append_array('Enum', t.values.empty?) do\n      append_strings(t.values)\n      if t.case_insensitive?\n        @bld << COMMA_SEP\n        append_string(true)\n      end\n    end\n  end\n\n  # @api private\n  def string_PVariantType(t)\n    append_array('Variant', t.types.empty?) { append_strings(t.types) }\n  end\n\n  # @api private\n  def string_PSemVerType(t)\n    append_array('SemVer', t.ranges.empty?) { append_strings(t.ranges) }\n  end\n\n  # @api private\n  def string_PSemVerRangeType(t)\n    @bld << 'SemVerRange'\n  end\n\n  # @api private\n  def string_PTimestampType(t)\n    min = t.from\n    max = t.to\n    append_array('Timestamp', min.nil? && max.nil?) do\n      min.nil? ? append_default : append_string(min)\n      unless max.nil? || max == min\n        @bld << COMMA_SEP\n        append_string(max)\n      end\n    end\n  end\n\n  # @api private\n  def string_PTimespanType(t)\n    min = t.from\n    max = t.to\n    append_array('Timespan', min.nil? && max.nil?) do\n      min.nil? ? append_default : append_string(min)\n      unless max.nil? || max == min\n        @bld << COMMA_SEP\n        append_string(max)\n      end\n    end\n  end\n\n  # @api private\n  def string_PTupleType(t)\n    append_array('Tuple', t.types.empty?) do\n      append_strings(t.types, true)\n      append_elements(range_array_part(t.size_type), true)\n      chomp_list\n    end\n  end\n\n  # @api private\n  def string_PCallableType(t)\n    if t.return_type.nil?\n      append_array('Callable', t.param_types.nil?) { append_callable_params(t) }\n    elsif t.param_types.nil?\n      append_array('Callable', false) { append_strings([[], t.return_type], false) }\n    else\n      append_array('Callable', false) do\n        append_array('', false) { append_callable_params(t) }\n        @bld << COMMA_SEP\n        append_string(t.return_type)\n      end\n    end\n  end\n\n  def append_callable_params(t)\n    # translate to string, and skip Unit types\n    append_strings(t.param_types.types.reject { |t2| t2.instance_of?(PUnitType) }, true)\n\n    if t.param_types.types.empty?\n      append_strings([0, 0], true)\n    else\n      append_elements(range_array_part(t.param_types.size_type), true)\n    end\n\n    # Add block T last (after min, max) if present)\n    #\n    append_strings([t.block_type], true) unless t.block_type.nil?\n    chomp_list\n  end\n\n  # @api private\n  def string_PStructType(t)\n    append_array('Struct', t.elements.empty?) { append_hash(t.elements.to_h { |e| struct_element_pair(e) }) }\n  end\n\n  # @api private\n  def struct_element_pair(t)\n    k = t.key_type\n    value_optional = t.value_type.assignable?(PUndefType::DEFAULT)\n    if k.is_a?(POptionalType)\n      # Output as literal String\n      k = t.name if value_optional\n    else\n      k = value_optional ? PNotUndefType.new(k) : t.name\n    end\n    [k, t.value_type]\n  end\n\n  # @api private\n  def string_PPatternType(t)\n    append_array('Pattern', t.patterns.empty?) { append_strings(t.patterns.map(&:regexp)) }\n  end\n\n  # @api private\n  def string_PCollectionType(t)\n    range = range_array_part(t.size_type)\n    append_array('Collection', range.empty?) { append_elements(range) }\n  end\n\n  def string_Object(t)\n    type = TypeCalculator.infer(t)\n    if type.is_a?(PObjectTypeExtension)\n      type = type.base_type\n    end\n    if type.is_a?(PObjectType)\n      init_hash = type.extract_init_hash(t)\n      @bld << type.name << '('\n      if @indent\n        append_indented_string(init_hash, @indent, @indent_width, true)\n        @bld.chomp!\n      else\n        append_string(init_hash)\n      end\n      @bld << ')'\n    else\n      @bld << 'Instance of '\n      append_string(type)\n    end\n  end\n\n  def string_PuppetObject(t)\n    @bld << t._pcore_type.name << '('\n    if @indent\n      append_indented_string(t._pcore_init_hash, @indent, @indent_width, true)\n      @bld.chomp!\n    else\n      append_string(t._pcore_init_hash)\n    end\n    @bld << ')'\n  end\n\n  # @api private\n  def string_PURIType(t)\n    append_array('URI', t.parameters.nil?) { append_string(t._pcore_init_hash['parameters']) }\n  end\n\n  def string_URI(t)\n    @bld << 'URI('\n    if @indent\n      append_indented_string(t.to_s, @indent, @indent_width, true)\n      @bld.chomp!\n    else\n      append_string(t.to_s)\n    end\n    @bld << ')'\n  end\n\n  # @api private\n  def string_PUnitType(_)\n    @bld << 'Unit'\n  end\n\n  # @api private\n  def string_PRuntimeType(t)\n    append_array('Runtime', t.runtime.nil? && t.name_or_pattern.nil?) { append_strings([t.runtime, t.name_or_pattern]) }\n  end\n\n  # @api private\n  def string_PArrayType(t)\n    if t.has_empty_range?\n      append_array('Array') { append_strings([0, 0]) }\n    else\n      append_array('Array', t == PArrayType::DEFAULT) do\n        append_strings([t.element_type], true)\n        append_elements(range_array_part(t.size_type), true)\n        chomp_list\n      end\n    end\n  end\n\n  # @api private\n  def string_PHashType(t)\n    if t.has_empty_range?\n      append_array('Hash') { append_strings([0, 0]) }\n    else\n      append_array('Hash', t == PHashType::DEFAULT) do\n        append_strings([t.key_type, t.value_type], true)\n        append_elements(range_array_part(t.size_type), true)\n        chomp_list\n      end\n    end\n  end\n\n  # @api private\n  def string_PCatalogEntryType(_)\n    @bld << 'CatalogEntry'\n  end\n\n  # @api private\n  def string_PClassType(t)\n    append_array('Class', t.class_name.nil?) { append_elements([t.class_name]) }\n  end\n\n  # @api private\n  def string_PResourceType(t)\n    if t.type_name\n      append_array(capitalize_segments(t.type_name), t.title.nil?) { append_string(t.title) }\n    else\n      @bld << 'Resource'\n    end\n  end\n\n  # @api private\n  def string_PNotUndefType(t)\n    contained_type = t.type\n    append_array('NotUndef', contained_type.nil? || contained_type.instance_of?(PAnyType)) do\n      if contained_type.is_a?(PStringType) && !contained_type.value.nil?\n        append_string(contained_type.value)\n      else\n        append_string(contained_type)\n      end\n    end\n  end\n\n  # @api private\n  def string_PAnnotatedMember(m)\n    hash = m._pcore_init_hash\n    if hash.size == 1\n      string(m.type)\n    else\n      string(hash)\n    end\n  end\n\n  # Used when printing names of well known keys in an Object type. Placed in a separate\n  # method to allow override.\n  # @api private\n  def symbolic_key(key)\n    @ruby ? \"'#{key}'\" : key\n  end\n\n  # @api private\n  def string_PTypeSetType(t)\n    append_array('TypeSet') do\n      append_hash(t._pcore_init_hash.each, proc { |k| @bld << symbolic_key(k) }) do |k, v|\n        case k\n        when KEY_TYPES\n          old_ts = @type_set\n          @type_set = t\n          begin\n            append_hash(v, proc { |tk| @bld << symbolic_key(tk) }) do |_tk, tv|\n              if tv.is_a?(Hash)\n                append_object_hash(tv)\n              else\n                append_string(tv)\n              end\n            end\n          rescue\n            @type_set = old_ts\n          end\n        when KEY_REFERENCES\n          append_hash(v, proc { |tk| @bld << symbolic_key(tk) })\n        else\n          append_string(v)\n        end\n      end\n    end\n  end\n\n  # @api private\n  def string_PObjectType(t)\n    if @expanded\n      append_object_hash(t._pcore_init_hash(@type_set.nil? || !@type_set.defines_type?(t)))\n    else\n      @bld << (@type_set ? @type_set.name_for(t, t.label) : t.label)\n    end\n  end\n\n  def string_PObjectTypeExtension(t)\n    append_array(@type_set ? @type_set.name_for(t, t.name) : t.name, false) do\n      ips = t.init_parameters\n      if ips.is_a?(Array)\n        append_strings(ips)\n      else\n        append_string(ips)\n      end\n    end\n  end\n\n  # @api private\n  def string_PSensitiveType(t)\n    append_array('Sensitive', PAnyType::DEFAULT == t.type) { append_string(t.type) }\n  end\n\n  # @api private\n  def string_POptionalType(t)\n    optional_type = t.optional_type\n    append_array('Optional', optional_type.nil?) do\n      if optional_type.is_a?(PStringType) && !optional_type.value.nil?\n        append_string(optional_type.value)\n      else\n        append_string(optional_type)\n      end\n    end\n  end\n\n  # @api private\n  def string_PTypeAliasType(t)\n    expand = @expanded\n    if expand && t.self_recursion?\n      @guard ||= RecursionGuard.new\n      @guard.with_this(t) { |state| format_type_alias_type(t, (state & RecursionGuard::SELF_RECURSION_IN_THIS) == 0) }\n    else\n      format_type_alias_type(t, expand)\n    end\n  end\n\n  # @api private\n  def format_type_alias_type(t, expand)\n    if @type_set.nil?\n      @bld << t.name\n      if expand && !Loader::StaticLoader::BUILTIN_ALIASES.include?(t.name)\n        @bld << ' = '\n        append_string(t.resolved_type)\n      end\n    elsif expand && @type_set.defines_type?(t)\n      append_string(t.resolved_type)\n    else\n      @bld << @type_set.name_for(t, t.name)\n    end\n  end\n\n  # @api private\n  def string_PTypeReferenceType(t)\n    append_array('TypeReference') { append_string(t.type_string) }\n  end\n\n  # @api private\n  def string_Array(t)\n    append_array('') do\n      if @indent && !is_short_array?(t)\n        @indent += 1\n        t.each { |elem| newline; append_string(elem); @bld << COMMA_SEP }\n        chomp_list\n        @indent -= 1\n        newline\n      else\n        append_strings(t)\n      end\n    end\n  end\n\n  # @api private\n  def string_FalseClass(t); @bld << 'false'; end\n\n  # @api private\n  def string_Hash(t)\n    append_hash(t)\n  end\n\n  # @api private\n  def string_Module(t)\n    append_string(TypeCalculator.singleton.type(t))\n  end\n\n  # @api private\n  def string_NilClass(t); @bld << (@ruby ? 'nil' : 'undef'); end\n\n  # @api private\n  def string_Numeric(t); @bld << t.to_s; end\n\n  # @api private\n  def string_Regexp(t); @bld << PRegexpType.regexp_to_s_with_delimiters(t); end\n\n  # @api private\n  def string_String(t)\n    # Use single qoute on strings that does not contain single quotes, control characters, or backslashes.\n    @bld << StringConverter.singleton.puppet_quote(t)\n  end\n\n  # @api private\n  def string_Symbol(t); @bld << t.to_s; end\n\n  # @api private\n  def string_TrueClass(t); @bld << 'true'; end\n\n  # @api private\n  def string_Version(t); @bld << \"'#{t}'\"; end\n\n  # @api private\n  def string_VersionRange(t); @bld << \"'#{t}'\"; end\n\n  # @api private\n  def string_Timespan(t); @bld << \"'#{t}'\"; end\n\n  # @api private\n  def string_Timestamp(t); @bld << \"'#{t}'\"; end\n\n  # Debugging to_s to reduce the amount of output\n  def to_s\n    '[a TypeFormatter]'\n  end\n\n  NAME_SEGMENT_SEPARATOR = '::'\n  STARTS_WITH_ASCII_CAPITAL = /^[A-Z]/\n\n  # Capitalizes each segment in a name separated with the {NAME_SEPARATOR} conditionally. The name\n  # will not be subject to capitalization if it already starts with a capital letter. This to avoid\n  # that existing camel casing is lost.\n  #\n  # @param qualified_name [String] the name to capitalize\n  # @return [String] the capitalized name\n  #\n  # @api private\n  def capitalize_segments(qualified_name)\n    if !qualified_name.is_a?(String) || qualified_name =~ STARTS_WITH_ASCII_CAPITAL\n      qualified_name\n    else\n      segments = qualified_name.split(NAME_SEGMENT_SEPARATOR)\n      if segments.size == 1\n        qualified_name.capitalize\n      else\n        segments.each(&:capitalize!)\n        segments.join(NAME_SEGMENT_SEPARATOR)\n      end\n    end\n  end\n\n  private\n\n  COMMA_SEP = ', '\n\n  HASH_ENTRY_OP = ' => '\n\n  def is_short_array?(t)\n    t.empty? || 100 - @indent * @indent_width > t.inject(0) do |sum, elem|\n      case elem\n      when true, false, nil, Numeric, Symbol\n        sum + elem.inspect.length()\n      when String\n        sum + 2 + elem.length\n      when Hash, Array\n        sum + (elem.empty? ? 2 : 1000)\n      else\n        sum + 1000\n      end\n    end\n  end\n\n  def range_array_part(t)\n    if t.nil? || t.unbounded?\n      EMPTY_ARRAY\n    else\n      result = [t.from.nil? ? 'default' : t.from.to_s]\n      result << t.to.to_s unless t.to.nil?\n      result\n    end\n  end\n\n  def append_object_hash(hash)\n    @expanded = false\n    append_array('Object') do\n      append_hash(hash, proc { |k| @bld << symbolic_key(k) }) do |k, v|\n        case k\n        when KEY_ATTRIBUTES, KEY_FUNCTIONS\n          # Types might need to be output as type references\n          append_hash(v) do |_, fv|\n            if fv.is_a?(Hash)\n              append_hash(fv, proc { |fak| @bld << symbolic_key(fak) }) do |fak, fav|\n                case fak\n                when KEY_KIND\n                  @bld << fav\n                else\n                  append_string(fav)\n                end\n              end\n            else\n              append_string(fv)\n            end\n          end\n        when KEY_EQUALITY\n          append_array('') { append_strings(v) } if v.is_a?(Array)\n        else\n          append_string(v)\n        end\n      end\n    end\n  ensure\n    @expanded = true\n  end\n\n  def append_elements(array, to_be_continued = false)\n    case array.size\n    when 0\n      # do nothing\n    when 1\n      @bld << array[0]\n      @bld << COMMA_SEP if to_be_continued\n    else\n      array.each { |elem| @bld << elem << COMMA_SEP }\n      chomp_list unless to_be_continued\n    end\n  end\n\n  def append_strings(array, to_be_continued = false)\n    case array.size\n    when 0\n      # do nothing\n    when 1\n      append_string(array[0])\n      @bld << COMMA_SEP if to_be_continued\n    else\n      array.each do |elem|\n        append_string(elem)\n        @bld << COMMA_SEP\n      end\n      chomp_list unless to_be_continued\n    end\n  end\n\n  def append_array(start, empty = false)\n    @bld << start\n    unless empty\n      @bld << '['\n      yield\n      @bld << ']'\n    end\n  end\n\n  def append_hash(hash, key_proc = nil)\n    @bld << '{'\n    @indent += 1 if @indent\n    hash.each do |k, v|\n      newline if @indent\n      if key_proc.nil?\n        append_string(k)\n      else\n        key_proc.call(k)\n      end\n      @bld << HASH_ENTRY_OP\n      if block_given?\n        yield(k, v)\n      else\n        append_string(v)\n      end\n      @bld << COMMA_SEP\n    end\n    chomp_list\n    if @indent\n      @indent -= 1\n      newline\n    end\n    @bld << '}'\n  end\n\n  def newline\n    @bld.rstrip!\n    @bld << \"\\n\"\n    (@indent * @indent_width).times { @bld << ' ' }\n  end\n\n  def chomp_list\n    @bld.chomp!(COMMA_SEP)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_mismatch_describer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n  # @api private\n  class TypePathElement\n    attr_reader :key\n\n    def initialize(key)\n      @key = key\n    end\n\n    def hash\n      key.hash\n    end\n\n    def ==(o)\n      self.class == o.class && key == o.key\n    end\n\n    def eql?(o)\n      self == o\n    end\n  end\n\n  # @api private\n  class SubjectPathElement < TypePathElement\n    def to_s\n      key\n    end\n  end\n\n  # @api private\n  class EntryValuePathElement < TypePathElement\n    def to_s\n      \"entry '#{key}'\"\n    end\n  end\n\n  # @api private\n  class EntryKeyPathElement < TypePathElement\n    def to_s\n      \"key of entry '#{key}'\"\n    end\n  end\n\n  # @api private\n  class ParameterPathElement < TypePathElement\n    def to_s\n      \"parameter '#{key}'\"\n    end\n  end\n\n  # @api private\n  class ReturnTypeElement < TypePathElement\n    def initialize(name = 'return')\n      super(name)\n    end\n\n    def to_s\n      key\n    end\n  end\n\n  # @api private\n  class BlockPathElement < ParameterPathElement\n    def initialize(name = 'block')\n      super(name)\n    end\n\n    def to_s\n      key\n    end\n  end\n\n  # @api private\n  class ArrayPathElement < TypePathElement\n    def to_s\n      \"index #{key}\"\n    end\n  end\n\n  # @api private\n  class VariantPathElement < TypePathElement\n    def to_s\n      \"variant #{key}\"\n    end\n  end\n\n  # @api private\n  class SignaturePathElement < VariantPathElement\n    def to_s\n      \"#{key + 1}.\"\n    end\n  end\n\n  # @api private\n  class Mismatch\n    attr_reader :path\n\n    def initialize(path)\n      @path = path || EMPTY_ARRAY\n    end\n\n    def canonical_path\n      @canonical_path ||= @path.reject { |e| e.is_a?(VariantPathElement) }\n    end\n\n    def message(variant, position)\n      \"#{variant}unknown mismatch#{position}\"\n    end\n\n    def merge(path, o)\n      self.class.new(path)\n    end\n\n    def ==(o)\n      self.class == o.class && canonical_path == o.canonical_path\n    end\n\n    def eql?(o)\n      self == o\n    end\n\n    def hash\n      canonical_path.hash\n    end\n\n    def chop_path(element_index)\n      return self if element_index >= @path.size\n\n      chopped_path = @path.clone\n      chopped_path.delete_at(element_index)\n      copy = clone\n      copy.instance_variable_set(:@path, chopped_path)\n      copy\n    end\n\n    def path_string\n      @path.join(' ')\n    end\n\n    def to_s\n      format\n    end\n\n    def format\n      p = @path\n      variant = ''\n      position = ''\n      unless p.empty?\n        f = p.first\n        if f.is_a?(SignaturePathElement)\n          variant = \" #{f}\"\n          p = p.drop(1)\n        end\n        position = \" #{p.join(' ')}\" unless p.empty?\n      end\n      message(variant, position)\n    end\n  end\n\n  # @abstract\n  # @api private\n  class KeyMismatch < Mismatch\n    attr_reader :key\n\n    def initialize(path, key)\n      super(path)\n      @key = key\n    end\n\n    def ==(o)\n      super.==(o) && key == o.key\n    end\n\n    def hash\n      super.hash ^ key.hash\n    end\n  end\n\n  # @api private\n  class MissingKey < KeyMismatch\n    def message(variant, position)\n      \"#{variant}#{position} expects a value for key '#{key}'\"\n    end\n  end\n\n  # @api private\n  class MissingParameter < KeyMismatch\n    def message(variant, position)\n      \"#{variant}#{position} expects a value for parameter '#{key}'\"\n    end\n  end\n\n  # @api private\n  class ExtraneousKey < KeyMismatch\n    def message(variant, position)\n      \"#{variant}#{position} unrecognized key '#{@key}'\"\n    end\n  end\n\n  # @api private\n  class InvalidParameter < ExtraneousKey\n    def message(variant, position)\n      \"#{variant}#{position} has no parameter named '#{@key}'\"\n    end\n  end\n\n  # @api private\n  class UnexpectedBlock < Mismatch\n    def message(variant, position)\n      \"#{variant}#{position} does not expect a block\"\n    end\n  end\n\n  # @api private\n  class MissingRequiredBlock < Mismatch\n    def message(variant, position)\n      \"#{variant}#{position} expects a block\"\n    end\n  end\n\n  # @api private\n  class UnresolvedTypeReference < Mismatch\n    attr_reader :unresolved\n\n    def initialize(path, unresolved)\n      super(path)\n      @unresolved = unresolved\n    end\n\n    def ==(o)\n      super.==(o) && @unresolved == o.unresolved\n    end\n\n    def hash\n      @unresolved.hash\n    end\n\n    def message(variant, position)\n      \"#{variant}#{position} references an unresolved type '#{@unresolved}'\"\n    end\n  end\n\n  # @api private\n  class ExpectedActualMismatch < Mismatch\n    attr_reader :expected, :actual\n\n    def initialize(path, expected, actual)\n      super(path)\n      @expected = (expected.is_a?(Array) ? PVariantType.maybe_create(expected) : expected).normalize\n      @actual = actual.normalize\n    end\n\n    def ==(o)\n      super.==(o) && expected == o.expected && actual == o.actual\n    end\n\n    def hash\n      [canonical_path, expected, actual].hash\n    end\n  end\n\n  # @api private\n  class TypeMismatch < ExpectedActualMismatch\n    include LabelProvider\n\n    # @return A new instance with the least restrictive respective boundaries\n    def merge(path, o)\n      self.class.new(path, [expected, o.expected].flatten.uniq, actual)\n    end\n\n    def message(variant, position)\n      e = expected\n      a = actual\n      multi = false\n      if e.is_a?(POptionalType)\n        e = e.optional_type\n        optional = true\n      end\n\n      if e.is_a?(PVariantType)\n        e = e.types\n      end\n\n      if e.is_a?(Array)\n        if report_detailed?(e, a)\n          a = detailed_actual_to_s(e, a)\n          e = e.map(&:to_alias_expanded_s)\n        else\n          e = e.map { |t| short_name(t) }.uniq\n          a = short_name(a)\n        end\n        e.insert(0, 'Undef') if optional\n        case e.size\n        when 1\n          e = e[0]\n        when 2\n          e = \"#{e[0]} or #{e[1]}\"\n          multi = true\n        else\n          e = \"#{e[0..e.size - 2].join(', ')}, or #{e[e.size - 1]}\"\n          multi = true\n        end\n      else\n        if report_detailed?(e, a)\n          a = detailed_actual_to_s(e, a)\n          e = e.to_alias_expanded_s\n        else\n          e = short_name(e)\n          a = short_name(a)\n        end\n        if optional\n          e = \"Undef or #{e}\"\n          multi = true\n        end\n      end\n      if multi\n        \"#{variant}#{position} expects a value of type #{e}, got #{label(a)}\"\n      else\n        \"#{variant}#{position} expects #{a_an(e)} value, got #{label(a)}\"\n      end\n    end\n\n    def label(o)\n      o.to_s\n    end\n\n    private\n\n    def short_name(t)\n      # Ensure that Optional, NotUndef, Sensitive, and Type are reported with included\n      # type parameter.\n      if t.is_a?(PTypeWithContainedType) && !(t.type.nil? || t.type.instance_of?(PAnyType))\n        \"#{t.name}[#{t.type.name}]\"\n      else\n        t.name.nil? ? t.simple_name : t.name\n      end\n    end\n\n    # Answers the question if `e` is a specialized type of `a`\n    # @param e [PAnyType] the expected type\n    # @param a [PAnyType] the actual type\n    # @return [Boolean] `true` when the _e_ is a specialization of _a_\n    #\n    def specialization(e, a)\n      case e\n      when PStructType\n        a.is_a?(PHashType)\n      when PTupleType\n        a.is_a?(PArrayType)\n      else\n        false\n      end\n    end\n\n    # Decides whether or not the report must be fully detailed, or if generalization can be permitted\n    # in the mismatch report. All comparisons are made using resolved aliases rather than the alias\n    # itself.\n    #\n    # @param e [PAnyType,Array[PAnyType]] the expected type or array of expected types\n    # @param a [PAnyType] the actual type\n    # @return [Boolean] `true` when the class of _a_ equals the class _e_ or,\n    #   in case _e_ is an `Array`, the class of at least one element of _e_\n    def always_fully_detailed?(e, a)\n      if e.is_a?(Array)\n        e.any? { |t| always_fully_detailed?(t, a) }\n      else\n        e.instance_of?(a.class) || e.is_a?(PTypeAliasType) || a.is_a?(PTypeAliasType) || specialization(e, a)\n      end\n    end\n\n    # @param e [PAnyType,Array[PAnyType]] the expected type or array of expected types\n    # @param a [PAnyType] the actual type\n    # @return [Boolean] `true` when _a_ is assignable to _e_ or, in case _e_ is an `Array`,\n    #   to at least one element of _e_\n    def any_assignable?(e, a)\n      e.is_a?(Array) ? e.any? { |t| t.assignable?(a) } : e.assignable?(a)\n    end\n\n    # @param e [PAnyType,Array[PAnyType]] the expected type or array of expected types\n    # @param a [PAnyType] the actual type\n    # @return [Boolean] `true` when _a_ is assignable to the default generalization of _e_ or,\n    #   in case _e_ is an `Array`, to the default generalization of at least one element of _e_\n    def assignable_to_default?(e, a)\n      if e.is_a?(Array)\n        e.any? { |t| assignable_to_default?(t, a) }\n      else\n        e = e.resolved_type if e.is_a?(PTypeAliasType)\n        e.class::DEFAULT.assignable?(a)\n      end\n    end\n\n    # @param e [PAnyType,Array[PAnyType]] the expected type or array of expected types\n    # @param a [PAnyType] the actual type\n    # @return [Boolean] `true` when either #always_fully_detailed or #assignable_to_default returns `true`\n    def report_detailed?(e, a)\n      always_fully_detailed?(e, a) || assignable_to_default?(e, a)\n    end\n\n    # Returns its argument with all type aliases resolved\n    # @param e [PAnyType,Array[PAnyType]] the expected type or array of expected types\n    # @return [PAnyType,Array[PAnyType]] the resolved result\n    def all_resolved(e)\n      if e.is_a?(Array)\n        e.map { |t| all_resolved(t) }\n      else\n        e.is_a?(PTypeAliasType) ? all_resolved(e.resolved_type) : e\n      end\n    end\n\n    # Returns a string that either represents the generalized type _a_ or the type _a_ verbatim. The latter\n    # form is used when at least one of the following conditions are met:\n    #\n    # - #always_fully_detailed returns `true` for the resolved type of _e_ and _a_\n    # - #any_assignable? returns `true` for the resolved type of _e_ and the generalized type of _a_.\n    #\n    # @param e [PAnyType,Array[PAnyType]] the expected type or array of expected types\n    # @param a [PAnyType] the actual type\n    # @return [String] The string representation of the type _a_ or generalized type _a_\n    def detailed_actual_to_s(e, a)\n      e = all_resolved(e)\n      if always_fully_detailed?(e, a)\n        a.to_alias_expanded_s\n      else\n        any_assignable?(e, a.generalize) ? a.to_alias_expanded_s : a.simple_name\n      end\n    end\n  end\n\n  # @api private\n  class PatternMismatch < TypeMismatch\n    def message(variant, position)\n      e = expected\n      value_pfx = ''\n      if e.is_a?(POptionalType)\n        e = e.optional_type\n        value_pfx = 'an undef value or '\n      end\n      \"#{variant}#{position} expects #{value_pfx}a match for #{e.to_alias_expanded_s}, got #{actual_string}\"\n    end\n\n    def actual_string\n      a = actual\n      a.is_a?(PStringType) && !a.value.nil? ? \"'#{a.value}'\" : short_name(a)\n    end\n  end\n\n  # @api private\n  class SizeMismatch < ExpectedActualMismatch\n    def from\n      @expected.from || 0\n    end\n\n    def to\n      @expected.to || Float::INFINITY\n    end\n\n    # @return A new instance with the least restrictive respective boundaries\n    def merge(path, o)\n      range = PIntegerType.new(from < o.from ? from : o.from, to > o.to ? to : o.to)\n      self.class.new(path, range, @actual)\n    end\n\n    def message(variant, position)\n      \"#{variant}#{position} expects size to be #{range_to_s(expected, '0')}, got #{range_to_s(actual, '0')}\"\n    end\n\n    def range_to_s(range, zero_string)\n      min = range.from || 0\n      max = range.to || Float::INFINITY\n      if min == max\n        min == 0 ? zero_string : min.to_s\n      elsif min == 0\n        max == Float::INFINITY ? 'unlimited' : \"at most #{max}\"\n      elsif max == Float::INFINITY\n        \"at least #{min}\"\n      else\n        \"between #{min} and #{max}\"\n      end\n    end\n  end\n\n  # @api private\n  class CountMismatch < SizeMismatch\n    def message(variant, position)\n      min = expected.from || 0\n      max = expected.to || Float::INFINITY\n      suffix = min == 1 && (max == 1 || max == Float::INFINITY) || min == 0 && max == 1 ? '' : 's'\n      \"#{variant}#{position} expects #{range_to_s(expected, 'no')} argument#{suffix}, got #{range_to_s(actual, 'none')}\"\n    end\n  end\n\n  # @api private\n  class TypeMismatchDescriber\n    def self.validate_parameters(subject, params_struct, given_hash, missing_ok = false)\n      singleton.validate_parameters(subject, params_struct, given_hash, missing_ok)\n    end\n\n    def self.validate_default_parameter(subject, param_name, param_type, value)\n      singleton.validate_default_parameter(subject, param_name, param_type, value)\n    end\n\n    def self.describe_signatures(closure, signatures, args_tuple)\n      singleton.describe_signatures(closure, signatures, args_tuple)\n    end\n\n    def self.singleton\n      @singleton ||= new\n    end\n\n    def tense_deprecated\n      # TRANSLATORS TypeMismatchDescriber is a class name and 'tense' is a method name and should not be translated\n      message = _(\"Passing a 'tense' argument to the TypeMismatchDescriber is deprecated and ignored.\")\n      message += ' ' + _(\"Everything is now reported using present tense\")\n      Puppet.warn_once('deprecations', 'typemismatch#tense', message)\n    end\n\n    # Validates that all entries in the give_hash exists in the given param_struct, that their type conforms\n    # with the corresponding param_struct element and that all required values are provided.\n    #\n    # @param subject [String] string to be prepended to the exception message\n    # @param params_struct [PStructType] Struct to use for validation\n    # @param given_hash [Hash<String,Object>] the parameters to validate\n    # @param missing_ok [Boolean] Do not generate errors on missing parameters\n    # @param tense [Symbol] deprecated and ignored\n    #\n    def validate_parameters(subject, params_struct, given_hash, missing_ok = false, tense = :ignored)\n      tense_deprecated unless tense == :ignored\n      errors = describe_struct_signature(params_struct, given_hash, missing_ok).flatten\n      case errors.size\n      when 0\n        # do nothing\n      when 1\n        raise Puppet::ParseError, \"#{subject}:#{errors[0].format}\"\n      else\n        errors_str = errors.map(&:format).join(\"\\n \")\n        raise Puppet::ParseError, \"#{subject}:\\n #{errors_str}\"\n      end\n    end\n\n    # Describe a confirmed mismatch using present tense\n    #\n    # @param name [String] name of mismatch\n    # @param expected [PAnyType] expected type\n    # @param actual [PAnyType] actual type\n    # @param tense [Symbol] deprecated and ignored\n    #\n    def describe_mismatch(name, expected, actual, tense = :ignored)\n      tense_deprecated unless tense == :ignored\n      errors = describe(expected, actual, [SubjectPathElement.new(name)])\n      case errors.size\n      when 0\n        ''\n      when 1\n        errors[0].format.strip\n      else\n        errors.map(&:format).join(\"\\n \")\n      end\n    end\n\n    # @param subject [String] string to be prepended to the exception message\n    # @param param_name [String] parameter name\n    # @param param_type [PAnyType] parameter type\n    # @param value [Object] value to be validated against the given type\n    # @param tense [Symbol] deprecated and ignored\n    #\n    def validate_default_parameter(subject, param_name, param_type, value, tense = :ignored)\n      tense_deprecated unless tense == :ignored\n      unless param_type.instance?(value)\n        errors = describe(param_type, TypeCalculator.singleton.infer_set(value).generalize, [ParameterPathElement.new(param_name)])\n        case errors.size\n        when 0\n          # do nothing\n        when 1\n          raise Puppet::ParseError, \"#{subject}:#{errors[0].format}\"\n        else\n          errors_str = errors.map(&:format).join(\"\\n \")\n          raise Puppet::ParseError, \"#{subject}:\\n #{errors_str}\"\n        end\n      end\n    end\n\n    def get_deferred_function_return_type(value)\n      func = Puppet.lookup(:loaders).private_environment_loader\n                   .load(:function, value.name)\n      dispatcher = func.class.dispatcher.find_matching_dispatcher(value.arguments)\n      raise ArgumentError, \"No matching arity found for #{func.class.name} with arguments #{value.arguments}\" unless dispatcher\n\n      dispatcher.type.return_type\n    end\n    private :get_deferred_function_return_type\n\n    # Validates that all entries in the _param_hash_ exists in the given param_struct, that their type conforms\n    # with the corresponding param_struct element and that all required values are provided.\n    # An error message is created for each problem found.\n    #\n    # @param params_struct [PStructType] Struct to use for validation\n    # @param param_hash [Hash<String,Object>] The parameters to validate\n    # @param missing_ok [Boolean] Do not generate errors on missing parameters\n    # @return [Array<Mismatch>] An array of found errors. An empty array indicates no errors.\n    def describe_struct_signature(params_struct, param_hash, missing_ok = false)\n      param_type_hash = params_struct.hashed_elements\n      result = param_hash.each_key.reject { |name| param_type_hash.include?(name) }.map { |name| InvalidParameter.new(nil, name) }\n\n      params_struct.elements.each do |elem|\n        name = elem.name\n        value = param_hash[name]\n        value_type = elem.value_type\n        if param_hash.include?(name)\n          if Puppet::Pops::Types::TypeFactory.deferred.implementation_class == value.class\n            if (df_return_type = get_deferred_function_return_type(value))\n              result << describe(value_type, df_return_type, [ParameterPathElement.new(name)]) unless value_type.generalize.assignable?(df_return_type.generalize)\n            else\n              warning_text = _(\"Deferred function %{function_name} has no return_type, unable to guarantee value type during compilation.\") %\n                             { function_name: value.name }\n              Puppet.warn_once('deprecations',\n                               \"#{value.name}_deferred_warning\",\n                               warning_text)\n            end\n          else\n            result << describe(value_type, TypeCalculator.singleton.infer_set(value), [ParameterPathElement.new(name)]) unless value_type.instance?(value)\n          end\n        else\n          result << MissingParameter.new(nil, name) unless missing_ok || elem.key_type.is_a?(POptionalType)\n        end\n      end\n      result\n    end\n\n    def describe_signatures(closure, signatures, args_tuple, tense = :ignored)\n      tense_deprecated unless tense == :ignored\n      error_arrays = []\n      signatures.each_with_index do |signature, index|\n        error_arrays << describe_signature_arguments(signature, args_tuple, [SignaturePathElement.new(index)])\n      end\n\n      # Skip block checks if all signatures have argument errors\n      unless error_arrays.all? { |a| !a.empty? }\n        block_arrays = []\n        signatures.each_with_index do |signature, index|\n          block_arrays << describe_signature_block(signature, args_tuple, [SignaturePathElement.new(index)])\n        end\n        bc_count = block_arrays.count { |a| !a.empty? }\n        if bc_count == block_arrays.size\n          # Skip argument errors when all alternatives have block errors\n          error_arrays = block_arrays\n        elsif bc_count > 0\n          # Merge errors giving argument errors precedence over block errors\n          error_arrays.each_with_index { |a, index| error_arrays[index] = block_arrays[index] if a.empty? }\n        end\n      end\n      return nil if error_arrays.empty?\n\n      label = closure == 'lambda' ? 'block' : \"'#{closure}'\"\n      errors = merge_descriptions(0, CountMismatch, error_arrays)\n      if errors.size == 1\n        \"#{label}#{errors[0].format}\"\n      else\n        if signatures.size == 1\n          sig = signatures[0]\n          result = [\"#{label} expects (#{signature_string(sig)})\"]\n          result.concat(error_arrays[0].map { |e| \"  rejected:#{e.chop_path(0).format}\" })\n        else\n          result = [\"The function #{label} was called with arguments it does not accept. It expects one of:\"]\n          signatures.each_with_index do |sg, index|\n            result << \"  (#{signature_string(sg)})\"\n            result.concat(error_arrays[index].map { |e| \"    rejected:#{e.chop_path(0).format}\" })\n          end\n        end\n        result.join(\"\\n\")\n      end\n    end\n\n    def describe_signature_arguments(signature, args_tuple, path)\n      params_tuple = signature.type.param_types\n      params_size_t = params_tuple.size_type || TypeFactory.range(*params_tuple.size_range)\n\n      case args_tuple\n      when PTupleType\n        arg_types = args_tuple.types\n      when PArrayType\n        arg_types = Array.new(params_tuple.types.size, args_tuple.element_type || PUndefType::DEFAULT)\n      else\n        return [TypeMismatch.new(path, params_tuple, args_tuple)]\n      end\n\n      if arg_types.last.kind_of_callable?\n        # Check other arguments\n        arg_count = arg_types.size - 1\n        describe_no_block_arguments(signature, arg_types, path, params_size_t, TypeFactory.range(arg_count, arg_count), arg_count)\n      else\n        args_size_t = TypeFactory.range(*args_tuple.size_range)\n        describe_no_block_arguments(signature, arg_types, path, params_size_t, args_size_t, arg_types.size)\n      end\n    end\n\n    def describe_signature_block(signature, args_tuple, path)\n      param_block_t = signature.block_type\n      arg_block_t = args_tuple.is_a?(PTupleType) ? args_tuple.types.last : nil\n      if TypeCalculator.is_kind_of_callable?(arg_block_t)\n        # Can't pass a block to a callable that doesn't accept one\n        if param_block_t.nil?\n          [UnexpectedBlock.new(path)]\n        else\n          # Check that the block is of the right type\n          describe(param_block_t, arg_block_t, path + [BlockPathElement.new])\n        end\n      elsif param_block_t.nil? || param_block_t.assignable?(PUndefType::DEFAULT)\n        # Check that the block is optional\n        EMPTY_ARRAY\n      else\n        [MissingRequiredBlock.new(path)]\n      end\n    end\n\n    def describe_no_block_arguments(signature, atypes, path, expected_size, actual_size, arg_count)\n      # not assignable if the number of types in actual is outside number of types in expected\n      if expected_size.assignable?(actual_size)\n        etypes = signature.type.param_types.types\n        enames = signature.parameter_names\n        arg_count.times do |index|\n          adx = index >= etypes.size ? etypes.size - 1 : index\n          etype = etypes[adx]\n          unless etype.assignable?(atypes[index])\n            descriptions = describe(etype, atypes[index], path + [ParameterPathElement.new(enames[adx])])\n            return descriptions unless descriptions.empty?\n          end\n        end\n        EMPTY_ARRAY\n      else\n        [CountMismatch.new(path, expected_size, actual_size)]\n      end\n    end\n\n    def describe_PVariantType(expected, original, actual, path)\n      variant_descriptions = []\n      types = expected.types\n      types = [PUndefType::DEFAULT] + types if original.is_a?(POptionalType)\n      types.each_with_index do |vt, index|\n        d = describe(vt, actual, path + [VariantPathElement.new(index)])\n        return EMPTY_ARRAY if d.empty?\n\n        variant_descriptions << d\n      end\n      descriptions = merge_descriptions(path.length, SizeMismatch, variant_descriptions)\n      if original.is_a?(PTypeAliasType) && descriptions.size == 1\n        # All variants failed in this alias so we report it as a mismatch on the alias\n        # rather than reporting individual failures of the variants\n        [TypeMismatch.new(path, original, actual)]\n      else\n        descriptions\n      end\n    end\n\n    def merge_descriptions(varying_path_position, size_mismatch_class, variant_descriptions)\n      descriptions = variant_descriptions.flatten\n      [size_mismatch_class, MissingRequiredBlock, UnexpectedBlock, TypeMismatch].each do |mismatch_class|\n        mismatches = descriptions.select { |desc| desc.is_a?(mismatch_class) }\n        next unless mismatches.size == variant_descriptions.size\n\n        # If they all have the same canonical path, then we can compact this into one\n        generic_mismatch = mismatches.inject do |prev, curr|\n          break nil unless prev.canonical_path == curr.canonical_path\n\n          prev.merge(prev.path, curr)\n        end\n        next if generic_mismatch.nil?\n\n        # Report the generic mismatch and skip the rest\n        descriptions = [generic_mismatch]\n        break\n      end\n      descriptions = descriptions.uniq\n      descriptions.size == 1 ? [descriptions[0].chop_path(varying_path_position)] : descriptions\n    end\n\n    def describe_POptionalType(expected, original, actual, path)\n      return EMPTY_ARRAY if actual.is_a?(PUndefType) || expected.optional_type.nil?\n\n      internal_describe(expected.optional_type, original.is_a?(PTypeAliasType) ? original : expected, actual, path)\n    end\n\n    def describe_PEnumType(expected, original, actual, path)\n      [PatternMismatch.new(path, original, actual)]\n    end\n\n    def describe_PPatternType(expected, original, actual, path)\n      [PatternMismatch.new(path, original, actual)]\n    end\n\n    def describe_PTypeAliasType(expected, original, actual, path)\n      internal_describe(expected.resolved_type.normalize, expected, actual, path)\n    end\n\n    def describe_PArrayType(expected, original, actual, path)\n      descriptions = []\n      element_type = expected.element_type || PAnyType::DEFAULT\n      case actual\n      when PTupleType\n        types = actual.types\n        expected_size = expected.size_type || PCollectionType::DEFAULT_SIZE\n        actual_size = actual.size_type || PIntegerType.new(types.size, types.size)\n        if expected_size.assignable?(actual_size)\n          types.each_with_index do |type, idx|\n            descriptions.concat(describe(element_type, type, path + [ArrayPathElement.new(idx)])) unless element_type.assignable?(type)\n          end\n        else\n          descriptions << SizeMismatch.new(path, expected_size, actual_size)\n        end\n      when PArrayType\n        expected_size = expected.size_type\n        actual_size = actual.size_type || PCollectionType::DEFAULT_SIZE\n        if expected_size.nil? || expected_size.assignable?(actual_size)\n          descriptions << TypeMismatch.new(path, original, PArrayType.new(actual.element_type))\n        else\n          descriptions << SizeMismatch.new(path, expected_size, actual_size)\n        end\n      else\n        descriptions << TypeMismatch.new(path, original, actual)\n      end\n      descriptions\n    end\n\n    def describe_PHashType(expected, original, actual, path)\n      descriptions = []\n      key_type = expected.key_type || PAnyType::DEFAULT\n      value_type = expected.value_type || PAnyType::DEFAULT\n      case actual\n      when PStructType\n        elements = actual.elements\n        expected_size = expected.size_type || PCollectionType::DEFAULT_SIZE\n        actual_size = PIntegerType.new(elements.count { |a| !a.key_type.assignable?(PUndefType::DEFAULT) }, elements.size)\n        if expected_size.assignable?(actual_size)\n          elements.each do |a|\n            descriptions.concat(describe(key_type, a.key_type, path + [EntryKeyPathElement.new(a.name)])) unless key_type.assignable?(a.key_type)\n            descriptions.concat(describe(value_type, a.value_type, path + [EntryValuePathElement.new(a.name)])) unless value_type.assignable?(a.value_type)\n          end\n        else\n          descriptions << SizeMismatch.new(path, expected_size, actual_size)\n        end\n      when PHashType\n        expected_size = expected.size_type\n        actual_size = actual.size_type || PCollectionType::DEFAULT_SIZE\n        if expected_size.nil? || expected_size.assignable?(actual_size)\n          descriptions << TypeMismatch.new(path, original, PHashType.new(actual.key_type, actual.value_type))\n        else\n          descriptions << SizeMismatch.new(path, expected_size, actual_size)\n        end\n      else\n        descriptions << TypeMismatch.new(path, original, actual)\n      end\n      descriptions\n    end\n\n    def describe_PStructType(expected, original, actual, path)\n      elements = expected.elements\n      descriptions = []\n      case actual\n      when PStructType\n        h2 = actual.hashed_elements.clone\n        elements.each do |e1|\n          key = e1.name\n          e2 = h2.delete(key)\n          if e2.nil?\n            descriptions << MissingKey.new(path, key) unless e1.key_type.assignable?(PUndefType::DEFAULT)\n          else\n            descriptions.concat(describe(e1.key_type, e2.key_type, path + [EntryKeyPathElement.new(key)])) unless e1.key_type.assignable?(e2.key_type)\n            descriptions.concat(describe(e1.value_type, e2.value_type, path + [EntryValuePathElement.new(key)])) unless e1.value_type.assignable?(e2.value_type)\n          end\n        end\n        h2.each_key { |key| descriptions << ExtraneousKey.new(path, key) }\n      when PHashType\n        actual_size = actual.size_type || PCollectionType::DEFAULT_SIZE\n        expected_size = PIntegerType.new(elements.count { |e| !e.key_type.assignable?(PUndefType::DEFAULT) }, elements.size)\n        if expected_size.assignable?(actual_size)\n          descriptions << TypeMismatch.new(path, original, PHashType.new(actual.key_type, actual.value_type))\n        else\n          descriptions << SizeMismatch.new(path, expected_size, actual_size)\n        end\n      else\n        descriptions << TypeMismatch.new(path, original, actual)\n      end\n      descriptions\n    end\n\n    def describe_PTupleType(expected, original, actual, path)\n      describe_tuple(expected, original, actual, path, SizeMismatch)\n    end\n\n    def describe_argument_tuple(expected, actual, path)\n      describe_tuple(expected, expected, actual, path, CountMismatch)\n    end\n\n    def describe_tuple(expected, original, actual, path, size_mismatch_class)\n      return EMPTY_ARRAY if expected == actual || expected.types.empty? && actual.is_a?(PArrayType)\n\n      expected_size = expected.size_type || TypeFactory.range(*expected.size_range)\n\n      case actual\n      when PTupleType\n        actual_size = actual.size_type || TypeFactory.range(*actual.size_range)\n\n        # not assignable if the number of types in actual is outside number of types in expected\n        if expected_size.assignable?(actual_size)\n          etypes = expected.types\n          descriptions = []\n          unless etypes.empty?\n            actual.types.each_with_index do |atype, index|\n              adx = index >= etypes.size ? etypes.size - 1 : index\n              descriptions.concat(describe(etypes[adx], atype, path + [ArrayPathElement.new(adx)]))\n            end\n          end\n          descriptions\n        else\n          [size_mismatch_class.new(path, expected_size, actual_size)]\n        end\n      when PArrayType\n        t2_entry = actual.element_type\n\n        if t2_entry.nil?\n          # Array of anything can not be assigned (unless tuple is tuple of anything) - this case\n          # was handled at the top of this method.\n          #\n          [TypeMismatch.new(path, original, actual)]\n        else\n          expected_size = expected.size_type || TypeFactory.range(*expected.size_range)\n          actual_size = actual.size_type || PCollectionType::DEFAULT_SIZE\n          if expected_size.assignable?(actual_size)\n            descriptions = []\n            expected.types.each_with_index do |etype, index|\n              descriptions.concat(describe(etype, actual.element_type, path + [ArrayPathElement.new(index)]))\n            end\n            descriptions\n          else\n            [size_mismatch_class.new(path, expected_size, actual_size)]\n          end\n        end\n      else\n        [TypeMismatch.new(path, original, actual)]\n      end\n    end\n\n    def describe_PCallableType(expected, original, actual, path)\n      if actual.is_a?(PCallableType)\n        # nil param_types means, any other Callable is assignable\n        if expected.param_types.nil? && expected.return_type.nil?\n          EMPTY_ARRAY\n        else\n          # NOTE: these tests are made in reverse as it is calling the callable that is constrained\n          # (it's lower bound), not its upper bound\n          param_errors = describe_argument_tuple(expected.param_types, actual.param_types, path)\n          if param_errors.empty?\n            this_return_t = expected.return_type || PAnyType::DEFAULT\n            that_return_t = actual.return_type || PAnyType::DEFAULT\n            if this_return_t.assignable?(that_return_t)\n              # names are ignored, they are just information\n              # Blocks must be compatible\n              this_block_t = expected.block_type || PUndefType::DEFAULT\n              that_block_t = actual.block_type || PUndefType::DEFAULT\n              if that_block_t.assignable?(this_block_t)\n                EMPTY_ARRAY\n              else\n                [TypeMismatch.new(path + [BlockPathElement.new], this_block_t, that_block_t)]\n              end\n            else\n              [TypeMismatch.new(path + [ReturnTypeElement.new], this_return_t, that_return_t)]\n            end\n          else\n            param_errors\n          end\n        end\n      else\n        [TypeMismatch.new(path, original, actual)]\n      end\n    end\n\n    def describe_PAnyType(expected, original, actual, path)\n      expected.assignable?(actual) ? EMPTY_ARRAY : [TypeMismatch.new(path, original, actual)]\n    end\n\n    class UnresolvedTypeFinder\n      include TypeAcceptor\n\n      attr_reader :unresolved\n\n      def initialize\n        @unresolved = nil\n      end\n\n      def visit(type, guard)\n        if @unresolved.nil? && type.is_a?(PTypeReferenceType)\n          @unresolved = type.type_string\n        end\n      end\n    end\n\n    def describe(expected, actual, path)\n      ures_finder = UnresolvedTypeFinder.new\n      expected.accept(ures_finder, nil)\n      unresolved = ures_finder.unresolved\n      if unresolved\n        [UnresolvedTypeReference.new(path, unresolved)]\n      else\n        internal_describe(expected.normalize, expected, actual, path)\n      end\n    end\n\n    def internal_describe(expected, original, actual, path)\n      case expected\n      when PVariantType\n        describe_PVariantType(expected, original, actual, path)\n      when PStructType\n        describe_PStructType(expected, original, actual, path)\n      when PHashType\n        describe_PHashType(expected, original, actual, path)\n      when PTupleType\n        describe_PTupleType(expected, original, actual, path)\n      when PArrayType\n        describe_PArrayType(expected, original, actual, path)\n      when PCallableType\n        describe_PCallableType(expected, original, actual, path)\n      when POptionalType\n        describe_POptionalType(expected, original, actual, path)\n      when PPatternType\n        describe_PPatternType(expected, original, actual, path)\n      when PEnumType\n        describe_PEnumType(expected, original, actual, path)\n      when PTypeAliasType\n        describe_PTypeAliasType(expected, original, actual, path)\n      else\n        describe_PAnyType(expected, original, actual, path)\n      end\n    end\n    private :internal_describe\n\n    # Produces a string for the signature(s)\n    #\n    # @api private\n    def signature_string(signature)\n      param_types = signature.type.param_types\n      param_names = signature.parameter_names\n\n      from, to = param_types.size_range\n      if from == 0 && to == 0\n        # No parameters function\n        return ''\n      end\n\n      required_count = from\n      types =\n        case param_types\n        when PTupleType\n          param_types.types\n        when PArrayType\n          [param_types.element_type]\n        end\n\n      # join type with names (types are always present, names are optional)\n      # separate entries with comma\n      #\n      param_names = Array.new(types.size, '') if param_names.empty?\n      limit = param_names.size\n      result = param_names.each_with_index.map do |name, index|\n        type = types[index] || types[-1]\n        indicator = ''\n        if to == Float::INFINITY && index == limit - 1\n          # Last is a repeated_param.\n          indicator = from == param_names.size ? '+' : '*'\n        elsif optional(index, required_count)\n          indicator = '?'\n          type = type.optional_type if type.is_a?(POptionalType)\n        end\n        \"#{type} #{name}#{indicator}\"\n      end.join(', ')\n\n      # If there is a block, include it\n      case signature.type.block_type\n      when POptionalType\n        result << ', ' unless result == ''\n        result << \"#{signature.type.block_type.optional_type} #{signature.block_name}?\"\n      when PCallableType\n        result << ', ' unless result == ''\n        result << \"#{signature.type.block_type} #{signature.block_name}\"\n      when NilClass\n        # nothing\n      end\n      result\n    end\n\n    # Why oh why Ruby do you not have a standard Math.max ?\n    # @api private\n    def max(a, b)\n      a >= b ? a : b\n    end\n\n    # @api private\n    def optional(index, required_count)\n      count = index + 1\n      count > required_count\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/concurrent/thread_local_singleton'\n\n# This class provides parsing of Type Specification from a string into the Type\n# Model that is produced by the TypeFactory.\n#\n# The Type Specifications that are parsed are the same as the stringified forms\n# of types produced by the {TypeCalculator TypeCalculator}.\n#\n# @api public\nmodule Puppet::Pops\nmodule Types\nclass TypeParser\n  extend Puppet::Concurrent::ThreadLocalSingleton\n\n  # @api public\n  def initialize\n    @parser = Parser::Parser.new\n    @type_transformer = Visitor.new(nil, 'interpret', 1, 1)\n  end\n\n  # Produces a *puppet type* based on the given string.\n  #\n  # @example\n  #     parser.parse('Integer')\n  #     parser.parse('Array[String]')\n  #     parser.parse('Hash[Integer, Array[String]]')\n  #\n  # @param string [String] a string with the type expressed in stringified form as produced by the\n  #   types {\"#to_s} method.\n  # @param context [Loader::Loader] optional loader used as no adapted loader is found\n  # @return [PAnyType] a specialization of the PAnyType representing the type.\n  #\n  # @api public\n  #\n  def parse(string, context = nil)\n    # quick \"peephole\" optimization of common data types\n    t = self.class.opt_type_map[string]\n    if t\n      return t\n    end\n\n    model = @parser.parse_string(string)\n    interpret(model.model.body, context)\n  end\n\n  # @api private\n  def parse_literal(string, context = nil)\n    factory = @parser.parse_string(string)\n    interpret_any(factory.model.body, context)\n  end\n\n  # @param ast [Puppet::Pops::Model::PopsObject] the ast to interpret\n  # @param context [Loader::Loader] optional loader used when no adapted loader is found\n  # @return [PAnyType] a specialization of the PAnyType representing the type.\n  #\n  # @api public\n  def interpret(ast, context = nil)\n    result = @type_transformer.visit_this_1(self, ast, context)\n    raise_invalid_type_specification_error(ast) unless result.is_a?(PAnyType)\n    result\n  end\n\n  # @api private\n  def interpret_any(ast, context)\n    @type_transformer.visit_this_1(self, ast, context)\n  end\n\n  # @api private\n  def interpret_Object(o, context)\n    raise_invalid_type_specification_error(o)\n  end\n\n  # @api private\n  def interpret_Program(o, context)\n    interpret_any(o.body, context)\n  end\n\n  # @api private\n  def interpret_TypeAlias(o, context)\n    Loader::TypeDefinitionInstantiator.create_type(o.name, o.type_expr, Pcore::RUNTIME_NAME_AUTHORITY).resolve(loader_from_context(o, context))\n  end\n\n  # @api private\n  def interpret_TypeDefinition(o, context)\n    Loader::TypeDefinitionInstantiator.create_runtime_type(o)\n  end\n\n  # @api private\n  def interpret_LambdaExpression(o, context)\n    o\n  end\n\n  # @api private\n  def interpret_HeredocExpression(o, context)\n    interpret_any(o.text_expr, context)\n  end\n\n  # @api private\n  def interpret_QualifiedName(o, context)\n    o.value\n  end\n\n  # @api private\n  def interpret_LiteralBoolean(o, context)\n    o.value\n  end\n\n  # @api private\n  def interpret_LiteralDefault(o, context)\n    :default\n  end\n\n  # @api private\n  def interpret_LiteralFloat(o, context)\n    o.value\n  end\n\n  # @api private\n  def interpret_LiteralHash(o, context)\n    result = {}\n    o.entries.each do |entry|\n      result[@type_transformer.visit_this_1(self, entry.key, context)] = @type_transformer.visit_this_1(self, entry.value, context)\n    end\n    result\n  end\n\n  # @api private\n  def interpret_LiteralInteger(o, context)\n    o.value\n  end\n\n  # @api private\n  def interpret_LiteralList(o, context)\n    o.values.map { |value| @type_transformer.visit_this_1(self, value, context) }\n  end\n\n  # @api private\n  def interpret_LiteralRegularExpression(o, context)\n    o.value\n  end\n\n  # @api private\n  def interpret_LiteralString(o, context)\n    o.value\n  end\n\n  # @api private\n  def interpret_LiteralUndef(o, context)\n    nil\n  end\n\n  # @api private\n  def interpret_String(o, context)\n    o\n  end\n\n  # @api private\n  def interpret_UnaryMinusExpression(o, context)\n    -@type_transformer.visit_this_1(self, o.expr, context)\n  end\n\n  # @api private\n  def self.type_map\n    @type_map ||= {\n      'integer' => TypeFactory.integer,\n      'float' => TypeFactory.float,\n      'numeric' => TypeFactory.numeric,\n      'init' => TypeFactory.init,\n      'iterable' => TypeFactory.iterable,\n      'iterator' => TypeFactory.iterator,\n      'string' => TypeFactory.string,\n      'binary' => TypeFactory.binary,\n      'sensitive' => TypeFactory.sensitive,\n      'enum' => TypeFactory.enum,\n      'boolean' => TypeFactory.boolean,\n      'pattern' => TypeFactory.pattern,\n      'regexp' => TypeFactory.regexp,\n      'array' => TypeFactory.array_of_any,\n      'hash' => TypeFactory.hash_of_any,\n      'class' => TypeFactory.host_class,\n      'resource' => TypeFactory.resource,\n      'collection' => TypeFactory.collection,\n      'scalar' => TypeFactory.scalar,\n      'scalardata' => TypeFactory.scalar_data,\n      'catalogentry' => TypeFactory.catalog_entry,\n      'undef' => TypeFactory.undef,\n      'notundef' => TypeFactory.not_undef,\n      'default' => TypeFactory.default,\n      'any' => TypeFactory.any,\n      'variant' => TypeFactory.variant,\n      'optional' => TypeFactory.optional,\n      'runtime' => TypeFactory.runtime,\n      'type' => TypeFactory.type_type,\n      'tuple' => TypeFactory.tuple,\n      'struct' => TypeFactory.struct,\n      'object' => TypeFactory.object,\n      'typealias' => TypeFactory.type_alias,\n      'typereference' => TypeFactory.type_reference,\n      'typeset' => TypeFactory.type_set,\n      # A generic callable as opposed to one that does not accept arguments\n      'callable' => TypeFactory.all_callables,\n      'semver' => TypeFactory.sem_ver,\n      'semverrange' => TypeFactory.sem_ver_range,\n      'timestamp' => TypeFactory.timestamp,\n      'timespan' => TypeFactory.timespan,\n      'uri' => TypeFactory.uri,\n    }.freeze\n  end\n\n  # @api private\n  def self.opt_type_map\n    # Map of common (and simple to optimize) data types in string form\n    # (Note that some types are the result of evaluation even if they appear to be simple\n    # - for example 'Data' and they cannot be optimized this way since the factory calls\n    # back to the parser for evaluation).\n    #\n    @opt_type_map ||= {\n      'Integer' => TypeFactory.integer,\n      'Float' => TypeFactory.float,\n      'Numeric' => TypeFactory.numeric,\n\n      'String' => TypeFactory.string,\n      'String[1]' => TypeFactory.string(TypeFactory.range(1, :default)),\n\n      'Binary' => TypeFactory.binary,\n\n      'Boolean' => TypeFactory.boolean,\n      'Boolean[true]' => TypeFactory.boolean(true),\n      'Boolean[false]' => TypeFactory.boolean(false),\n\n      'Array' => TypeFactory.array_of_any,\n      'Array[1]' => TypeFactory.array_of(TypeFactory.any, TypeFactory.range(1, :default)),\n\n      'Hash' => TypeFactory.hash_of_any,\n      'Collection' => TypeFactory.collection,\n      'Scalar' => TypeFactory.scalar,\n\n      'Scalardata' => TypeFactory.scalar_data,\n      'ScalarData' => TypeFactory.scalar_data,\n\n      'Catalogentry' => TypeFactory.catalog_entry,\n      'CatalogEntry' => TypeFactory.catalog_entry,\n\n      'Undef' => TypeFactory.undef,\n      'Default' => TypeFactory.default,\n      'Any' => TypeFactory.any,\n      'Type' => TypeFactory.type_type,\n      'Callable' => TypeFactory.all_callables,\n\n      'Semver' => TypeFactory.sem_ver,\n      'SemVer' => TypeFactory.sem_ver,\n\n      'Semverrange' => TypeFactory.sem_ver_range,\n      'SemVerRange' => TypeFactory.sem_ver_range,\n\n      'Timestamp' => TypeFactory.timestamp,\n      'TimeStamp' => TypeFactory.timestamp,\n\n      'Timespan' => TypeFactory.timespan,\n      'TimeSpan' => TypeFactory.timespan,\n\n      'Uri' => TypeFactory.uri,\n      'URI' => TypeFactory.uri,\n\n      'Optional[Integer]' => TypeFactory.optional(TypeFactory.integer),\n      'Optional[String]' => TypeFactory.optional(TypeFactory.string),\n      'Optional[String[1]]' => TypeFactory.optional(TypeFactory.string(TypeFactory.range(1, :default))),\n      'Optional[Array]' => TypeFactory.optional(TypeFactory.array_of_any),\n      'Optional[Hash]' => TypeFactory.optional(TypeFactory.hash_of_any),\n\n    }.freeze\n  end\n\n  # @api private\n  def interpret_QualifiedReference(name_ast, context)\n    name = name_ast.value\n    found = self.class.type_map[name]\n    if found\n      found\n    else\n      loader = loader_from_context(name_ast, context)\n      unless loader.nil?\n        type = loader.load(:type, name)\n        type = type.resolve(loader) unless type.nil?\n      end\n      type || TypeFactory.type_reference(name_ast.cased_value)\n    end\n  end\n\n  # @api private\n  def loader_from_context(ast, context)\n    model_loader = Adapters::LoaderAdapter.loader_for_model_object(ast, nil, context)\n    if context.is_a?(PTypeSetType::TypeSetLoader)\n      # Only swap a given TypeSetLoader for another loader when the other loader is different\n      # from the one associated with the TypeSet expression\n      context.model_loader.equal?(model_loader.parent) ? context : model_loader\n    else\n      model_loader\n    end\n  end\n\n  # @api private\n  def interpret_AccessExpression(ast, context)\n    parameters = ast.keys.collect { |param| interpret_any(param, context) }\n\n    qref = ast.left_expr\n    raise_invalid_type_specification_error(ast) unless qref.is_a?(Model::QualifiedReference)\n\n    type_name = qref.value\n    case type_name\n    when 'array'\n      case parameters.size\n      when 1\n        type = assert_type(ast, parameters[0])\n      when 2\n        if parameters[0].is_a?(PAnyType)\n          type = parameters[0]\n          size_type =\n            if parameters[1].is_a?(PIntegerType)\n              size_type = parameters[1]\n            else\n              assert_range_parameter(ast, parameters[1])\n              TypeFactory.range(parameters[1], :default)\n            end\n        else\n          type = :default\n          assert_range_parameter(ast, parameters[0])\n          assert_range_parameter(ast, parameters[1])\n          size_type = TypeFactory.range(parameters[0], parameters[1])\n        end\n      when 3\n        type = assert_type(ast, parameters[0])\n        assert_range_parameter(ast, parameters[1])\n        assert_range_parameter(ast, parameters[2])\n        size_type = TypeFactory.range(parameters[1], parameters[2])\n      else\n        raise_invalid_parameters_error('Array', '1 to 3', parameters.size)\n      end\n      TypeFactory.array_of(type, size_type)\n\n    when 'hash'\n      case parameters.size\n      when 2\n        if parameters[0].is_a?(PAnyType) && parameters[1].is_a?(PAnyType)\n          TypeFactory.hash_of(parameters[1], parameters[0])\n        else\n          assert_range_parameter(ast, parameters[0])\n          assert_range_parameter(ast, parameters[1])\n          TypeFactory.hash_of(:default, :default, TypeFactory.range(parameters[0], parameters[1]))\n        end\n      when 3\n        size_type =\n          if parameters[2].is_a?(PIntegerType)\n            parameters[2]\n          else\n            assert_range_parameter(ast, parameters[2])\n            TypeFactory.range(parameters[2], :default)\n          end\n        assert_type(ast, parameters[0])\n        assert_type(ast, parameters[1])\n        TypeFactory.hash_of(parameters[1], parameters[0], size_type)\n      when 4\n        assert_range_parameter(ast, parameters[2])\n        assert_range_parameter(ast, parameters[3])\n        assert_type(ast, parameters[0])\n        assert_type(ast, parameters[1])\n        TypeFactory.hash_of(parameters[1], parameters[0], TypeFactory.range(parameters[2], parameters[3]))\n      else\n        raise_invalid_parameters_error('Hash', '2 to 4', parameters.size)\n      end\n\n    when 'collection'\n      size_type = case parameters.size\n                  when 1\n                    if parameters[0].is_a?(PIntegerType)\n                      parameters[0]\n                    else\n                      assert_range_parameter(ast, parameters[0])\n                      TypeFactory.range(parameters[0], :default)\n                    end\n                  when 2\n                    assert_range_parameter(ast, parameters[0])\n                    assert_range_parameter(ast, parameters[1])\n                    TypeFactory.range(parameters[0], parameters[1])\n                  else\n                    raise_invalid_parameters_error('Collection', '1 to 2',\n                                                   parameters.size)\n                  end\n      TypeFactory.collection(size_type)\n\n    when 'class'\n      if parameters.size != 1\n        raise_invalid_parameters_error('Class', 1, parameters.size)\n      end\n      TypeFactory.host_class(parameters[0])\n\n    when 'resource'\n      type = parameters[0]\n      if type.is_a?(PTypeReferenceType)\n        type_str = type.type_string\n        param_start = type_str.index('[')\n        if param_start.nil?\n          type = type_str\n        else\n          tps = interpret_any(@parser.parse_string(type_str[param_start..]).model, context)\n          raise_invalid_parameters_error(type.to_s, '1', tps.size) unless tps.size == 1\n          type = type_str[0..param_start - 1]\n          parameters = [type] + tps\n        end\n      end\n      create_resource(type, parameters)\n\n    when 'regexp'\n      # 1 parameter being a string, or regular expression\n      raise_invalid_parameters_error('Regexp', '1', parameters.size) unless parameters.size == 1\n      TypeFactory.regexp(parameters[0])\n\n    when 'enum'\n      # 1..m parameters being string\n      last = parameters.last\n      case_insensitive = false\n      if last == true || last == false\n        parameters = parameters[0...-1]\n        case_insensitive = last\n      end\n      raise_invalid_parameters_error('Enum', '1 or more', parameters.size) unless parameters.size >= 1\n      parameters.each { |p| raise Puppet::ParseError, _('Enum parameters must be identifiers or strings') unless p.is_a?(String) }\n      PEnumType.new(parameters, case_insensitive)\n\n    when 'pattern'\n      # 1..m parameters being strings or regular expressions\n      raise_invalid_parameters_error('Pattern', '1 or more', parameters.size) unless parameters.size >= 1\n      TypeFactory.pattern(*parameters)\n\n    when 'uri'\n      # 1 parameter which is a string or a URI\n      raise_invalid_parameters_error('URI', '1', parameters.size) unless parameters.size == 1\n      TypeFactory.uri(parameters[0])\n\n    when 'variant'\n      # 1..m parameters being strings or regular expressions\n      raise_invalid_parameters_error('Variant', '1 or more', parameters.size) unless parameters.size >= 1\n      parameters.each { |p| assert_type(ast, p) }\n      TypeFactory.variant(*parameters)\n\n    when 'tuple'\n      # 1..m parameters being types (last two optionally integer or literal default\n      raise_invalid_parameters_error('Tuple', '1 or more', parameters.size) unless parameters.size >= 1\n      length = parameters.size\n      size_type = nil\n      if TypeFactory.is_range_parameter?(parameters[-2])\n        # min, max specification\n        min = parameters[-2]\n        min = (min == :default || min == 'default') ? 0 : min\n        assert_range_parameter(ast, parameters[-1])\n        max = parameters[-1]\n        max = max == :default ? nil : max\n        parameters = parameters[0, length - 2]\n        size_type = TypeFactory.range(min, max)\n      elsif TypeFactory.is_range_parameter?(parameters[-1])\n        min = parameters[-1]\n        min = (min == :default || min == 'default') ? 0 : min\n        max = nil\n        parameters = parameters[0, length - 1]\n        size_type = TypeFactory.range(min, max)\n      end\n      TypeFactory.tuple(parameters, size_type)\n\n    when 'callable'\n      # 1..m parameters being types (last three optionally integer or literal default, and a callable)\n      if parameters.size > 1 && parameters[0].is_a?(Array)\n        raise_invalid_parameters_error('callable', '2 when first parameter is an array', parameters.size) unless parameters.size == 2\n      end\n      TypeFactory.callable(*parameters)\n\n    when 'struct'\n      # 1..m parameters being types (last two optionally integer or literal default\n      raise_invalid_parameters_error('Struct', '1', parameters.size) unless parameters.size == 1\n      h = parameters[0]\n      raise_invalid_type_specification_error(ast) unless h.is_a?(Hash)\n      TypeFactory.struct(h)\n\n    when 'boolean'\n      raise_invalid_parameters_error('Boolean', '1', parameters.size) unless parameters.size == 1\n      p = parameters[0]\n      raise Puppet::ParseError, 'Boolean parameter must be true or false' unless p == true || p == false\n\n      TypeFactory.boolean(p)\n\n    when 'integer'\n      if parameters.size == 1\n        case parameters[0]\n        when Integer\n          TypeFactory.range(parameters[0], :default)\n        when :default\n          TypeFactory.integer # unbound\n        end\n      elsif parameters.size != 2\n        raise_invalid_parameters_error('Integer', '1 or 2', parameters.size)\n      else\n        TypeFactory.range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1])\n      end\n\n    when 'object'\n      raise_invalid_parameters_error('Object', 1, parameters.size) unless parameters.size == 1\n      TypeFactory.object(parameters[0])\n\n    when 'typeset'\n      raise_invalid_parameters_error('Object', 1, parameters.size) unless parameters.size == 1\n      TypeFactory.type_set(parameters[0])\n\n    when 'init'\n      assert_type(ast, parameters[0])\n      TypeFactory.init(*parameters)\n\n    when 'iterable'\n      if parameters.size != 1\n        raise_invalid_parameters_error('Iterable', 1, parameters.size)\n      end\n      assert_type(ast, parameters[0])\n      TypeFactory.iterable(parameters[0])\n\n    when 'iterator'\n      if parameters.size != 1\n        raise_invalid_parameters_error('Iterator', 1, parameters.size)\n      end\n      assert_type(ast, parameters[0])\n      TypeFactory.iterator(parameters[0])\n\n    when 'float'\n      if parameters.size == 1\n        case parameters[0]\n        when Integer, Float\n          TypeFactory.float_range(parameters[0], :default)\n        when :default\n          TypeFactory.float # unbound\n        end\n      elsif parameters.size != 2\n        raise_invalid_parameters_error('Float', '1 or 2', parameters.size)\n      else\n        TypeFactory.float_range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1])\n      end\n\n    when 'string'\n      size_type =\n        case parameters.size\n        when 1\n          if parameters[0].is_a?(PIntegerType)\n            parameters[0]\n          else\n            assert_range_parameter(ast, parameters[0])\n            TypeFactory.range(parameters[0], :default)\n          end\n        when 2\n          assert_range_parameter(ast, parameters[0])\n          assert_range_parameter(ast, parameters[1])\n          TypeFactory.range(parameters[0], parameters[1])\n        else\n          raise_invalid_parameters_error('String', '1 to 2', parameters.size)\n        end\n      TypeFactory.string(size_type)\n\n    when 'sensitive'\n      case parameters.size\n      when 0\n        TypeFactory.sensitive\n      when 1\n        param = parameters[0]\n        assert_type(ast, param)\n        TypeFactory.sensitive(param)\n      else\n        raise_invalid_parameters_error('Sensitive', '0 to 1', parameters.size)\n      end\n\n    when 'optional'\n      if parameters.size != 1\n        raise_invalid_parameters_error('Optional', 1, parameters.size)\n      end\n      param = parameters[0]\n      assert_type(ast, param) unless param.is_a?(String)\n      TypeFactory.optional(param)\n\n    when 'any', 'data', 'catalogentry', 'scalar', 'undef', 'numeric', 'default', 'semverrange'\n      raise_unparameterized_type_error(qref)\n\n    when 'notundef'\n      case parameters.size\n      when 0\n        TypeFactory.not_undef\n      when 1\n        param = parameters[0]\n        assert_type(ast, param) unless param.is_a?(String)\n        TypeFactory.not_undef(param)\n      else\n        raise_invalid_parameters_error(\"NotUndef\", \"0 to 1\", parameters.size)\n      end\n\n    when 'type'\n      if parameters.size != 1\n        raise_invalid_parameters_error('Type', 1, parameters.size)\n      end\n      assert_type(ast, parameters[0])\n      TypeFactory.type_type(parameters[0])\n\n    when 'runtime'\n      raise_invalid_parameters_error('Runtime', '2', parameters.size) unless parameters.size == 2\n      TypeFactory.runtime(*parameters)\n\n    when 'timespan'\n      raise_invalid_parameters_error('Timespan', '0 to 2', parameters.size) unless parameters.size <= 2\n      TypeFactory.timespan(*parameters)\n\n    when 'timestamp'\n      raise_invalid_parameters_error('Timestamp', '0 to 2', parameters.size) unless parameters.size <= 2\n      TypeFactory.timestamp(*parameters)\n\n    when 'semver'\n      raise_invalid_parameters_error('SemVer', '1 or more', parameters.size) unless parameters.size >= 1\n      TypeFactory.sem_ver(*parameters)\n\n    else\n      loader = loader_from_context(qref, context)\n      type = nil\n      unless loader.nil?\n        type = loader.load(:type, type_name)\n        type = type.resolve(loader) unless type.nil?\n      end\n\n      if type.nil?\n        TypeFactory.type_reference(original_text_of(ast))\n      elsif type.is_a?(PResourceType)\n        raise_invalid_parameters_error(qref.cased_value, 1, parameters.size) unless parameters.size == 1\n        TypeFactory.resource(type.type_name, parameters[0])\n      elsif type.is_a?(PObjectType)\n        PObjectTypeExtension.create(type, parameters)\n      else\n        # Must be a type alias. They can't use parameters (yet)\n        raise_unparameterized_type_error(qref)\n      end\n    end\n  end\n\n  private\n\n  def create_resource(name, parameters)\n    case parameters.size\n    when 1\n      TypeFactory.resource(name)\n    when 2\n      TypeFactory.resource(name, parameters[1])\n    else\n      raise_invalid_parameters_error('Resource', '1 or 2', parameters.size)\n    end\n  end\n\n  def assert_type(ast, t)\n    raise_invalid_type_specification_error(ast) unless t.is_a?(PAnyType)\n    t\n  end\n\n  def assert_range_parameter(ast, t)\n    raise_invalid_type_specification_error(ast) unless TypeFactory.is_range_parameter?(t)\n  end\n\n  def raise_invalid_type_specification_error(ast)\n    raise Puppet::ParseError, _(\"The expression <%{expression}> is not a valid type specification.\") %\n                              { expression: original_text_of(ast) }\n  end\n\n  def raise_invalid_parameters_error(type, required, given)\n    raise Puppet::ParseError, _(\"Invalid number of type parameters specified: %{type} requires %{required}, %{given} provided\") %\n                              { type: type, required: required, given: given }\n  end\n\n  def raise_unparameterized_type_error(ast)\n    raise Puppet::ParseError, _(\"Not a parameterized type <%{type}>\") % { type: original_text_of(ast) }\n  end\n\n  def raise_unknown_type_error(ast)\n    raise Puppet::ParseError, _(\"Unknown type <%{type}>\") % { type: original_text_of(ast) }\n  end\n\n  def original_text_of(ast)\n    ast.locator.extract_tree_text(ast)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_set_reference.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\nclass TypeSetReference\n  include Annotatable\n\n  attr_reader :name_authority\n  attr_reader :name\n  attr_reader :version_range\n  attr_reader :type_set\n\n  def initialize(owner, init_hash)\n    @owner = owner\n    @name_authority = (init_hash[KEY_NAME_AUTHORITY] || owner.name_authority).freeze\n    @name = init_hash[KEY_NAME].freeze\n    @version_range = PSemVerRangeType.convert(init_hash[KEY_VERSION_RANGE])\n    init_annotatable(init_hash)\n  end\n\n  def accept(visitor, guard)\n    annotatable_accept(visitor, guard)\n  end\n\n  def eql?(o)\n    self.class == o.class && @name_authority.eql?(o.name_authority) && @name.eql?(o.name) && @version_range.eql?(o.version_range)\n  end\n\n  def hash\n    [@name_authority, @name, @version_range].hash\n  end\n\n  def _pcore_init_hash\n    result = super\n    result[KEY_NAME_AUTHORITY] = @name_authority unless @name_authority == @owner.name_authority\n    result[KEY_NAME] = @name\n    result[KEY_VERSION_RANGE] = @version_range.to_s\n    result\n  end\n\n  def resolve(loader)\n    typed_name = Loader::TypedName.new(:type, @name, @name_authority)\n    loaded_entry = loader.load_typed(typed_name)\n    type_set = loaded_entry.nil? ? nil : loaded_entry.value\n\n    raise ArgumentError, \"#{self} cannot be resolved\" if type_set.nil?\n    raise ArgumentError, \"#{self} resolves to a #{type_set.name}\" unless type_set.is_a?(PTypeSetType)\n\n    @type_set = type_set.resolve(loader)\n    unless @version_range.include?(@type_set.version)\n      raise ArgumentError, \"#{self} resolves to an incompatible version. Expected #{@version_range}, got #{type_set.version}\"\n    end\n\n    nil\n  end\n\n  def to_s\n    \"#{@owner.label} reference to TypeSet named '#{@name}'\"\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/type_with_members.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Types\n# Interface implemented by a type that has InvocableMembers\nmodule TypeWithMembers\n  # @return [InvocableMember,nil] An invocable member if it exists, or `nil`\n  def [](member_name)\n    raise NotImplementedError, \"'#{self.class.name}' should implement #[]\"\n  end\nend\n\n# Interface implemented by attribute and function members\nmodule InvocableMember\n  # Performs type checking of arguments and invokes the method that corresponds to this\n  # method. The result of the invocation is returned\n  #\n  # @param receiver [Object] The receiver of the call\n  # @param scope [Puppet::Parser::Scope] The caller scope\n  # @param args [Array] Array of arguments.\n  # @return [Object] The result returned by the member function or attribute\n  #\n  # @api private\n  def invoke(receiver, scope, args, &block)\n    raise NotImplementedError, \"'#{self.class.name}' should implement #invoke\"\n  end\nend\n\n# Plays the same role as an PAttribute in the PObjectType. Provides\n# access to known attr_readers and plain reader methods.\nclass AttrReader\n  include InvocableMember\n\n  def initialize(message)\n    @message = message.to_sym\n  end\n\n  def invoke(receiver, scope, args, &block)\n    receiver.send(@message)\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/types/types.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'iterable'\nrequire_relative 'recursion_guard'\nrequire_relative 'type_acceptor'\nrequire_relative 'type_asserter'\nrequire_relative 'type_assertion_error'\nrequire_relative 'type_conversion_error'\nrequire_relative 'type_formatter'\nrequire_relative 'type_calculator'\nrequire_relative 'type_factory'\nrequire_relative 'type_parser'\nrequire_relative 'class_loader'\nrequire_relative 'type_mismatch_describer'\nrequire_relative 'puppet_object'\n\nmodule Puppet::Pops\nmodule Types\n# The EMPTY_xxx declarations is for backward compatibility. They should not be explicitly referenced\n\n# @api private\n# @deprecated\nEMPTY_HASH = Puppet::Pops::EMPTY_HASH\n\n# @api private\n# @deprecated\nEMPTY_ARRAY = Puppet::Pops::EMPTY_ARRAY\n\n# @api private\n# @deprecated\nEMPTY_STRING = Puppet::Pops::EMPTY_STRING\n\n# The Types model is a model of Puppet Language types.\n#\n# The {TypeCalculator} should be used to answer questions about types. The {TypeFactory} or {TypeParser} should be used\n# to create an instance of a type whenever one is needed.\n#\n# The implementation of the Types model contains methods that are required for the type objects to behave as\n# expected when comparing them and using them as keys in hashes. (No other logic is, or should be included directly in\n# the model's classes).\n#\n# @api public\n#\nclass TypedModelObject < Object\n  include PuppetObject\n  include Visitable\n  include Adaptable\n\n  def self._pcore_type\n    @type\n  end\n\n  def self.create_ptype(loader, ir, parent_name, attributes_hash = EMPTY_HASH)\n    @type = Pcore.create_object_type(loader, ir, self, \"Pcore::#{simple_name}Type\", \"Pcore::#{parent_name}\", attributes_hash)\n  end\n\n  def self.register_ptypes(loader, ir)\n    types = [\n      Annotation.register_ptype(loader, ir),\n      RubyMethod.register_ptype(loader, ir)\n    ]\n    Types.constants.each do |c|\n      next if c == :PType || c == :PHostClassType\n\n      cls = Types.const_get(c)\n      next unless cls.is_a?(Class) && cls < self\n\n      type = cls.register_ptype(loader, ir)\n      types << type unless type.nil?\n    end\n    types.each { |type| type.resolve(loader) }\n  end\nend\n\n# Base type for all types\n# @api public\n#\nclass PAnyType < TypedModelObject\n  def self.register_ptype(loader, ir)\n    @type = Pcore.create_object_type(loader, ir, self, 'Pcore::AnyType', 'Any', EMPTY_HASH)\n  end\n\n  def self.create(*args)\n    # NOTE! Important to use self::DEFAULT and not just DEFAULT since the latter yields PAnyType::DEFAULT\n    args.empty? ? self::DEFAULT : new(*args)\n  end\n\n  # Accept a visitor that will be sent the message `visit`, once with `self` as the\n  # argument. The visitor will then visit all types that this type contains.\n  #\n  def accept(visitor, guard)\n    visitor.visit(self, guard)\n  end\n\n  # Checks if _o_ is a type that is assignable to this type.\n  # If _o_ is a `Class` then it is first converted to a type.\n  # If _o_ is a Variant, then it is considered assignable when all its types are assignable\n  #\n  # The check for assignable must be guarded against self recursion since `self`, the given type _o_,\n  # or both, might be a `TypeAlias`. The initial caller of this method will typically never care\n  # about this and hence pass only the first argument, but as soon as a check of a contained type\n  # encounters a `TypeAlias`, then a `RecursionGuard` instance is created and passed on in all\n  # subsequent calls. The recursion is allowed to continue until self recursion has been detected in\n  # both `self` and in the given type. At that point the given type is considered to be assignable\n  # to `self` since all checks up to that point were positive.\n  #\n  # @param o [Class,PAnyType] the class or type to test\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [Boolean] `true` when _o_ is assignable to this type\n  # @api public\n  def assignable?(o, guard = nil)\n    case o\n    when Class\n      # Safe to call _assignable directly since a Class never is a Unit or Variant\n      _assignable?(TypeCalculator.singleton.type(o), guard)\n    when PUnitType\n      true\n    when PTypeAliasType\n      # An alias may contain self recursive constructs.\n      if o.self_recursion?\n        guard ||= RecursionGuard.new\n        if guard.add_that(o) == RecursionGuard::SELF_RECURSION_IN_BOTH\n          # Recursion detected both in self and other. This means that other is assignable\n          # to self. This point would not have been reached otherwise\n          true\n        else\n          assignable?(o.resolved_type, guard)\n        end\n      else\n        assignable?(o.resolved_type, guard)\n      end\n    when PVariantType\n      # Assignable if all contained types are assignable, or if this is exactly Any\n      return true if instance_of?(PAnyType)\n      # An empty variant may be assignable to NotUndef[T] if T is assignable to empty variant\n      return _assignable?(o, guard) if is_a?(PNotUndefType) && o.types.empty?\n\n      !o.types.empty? && o.types.all? { |vt| assignable?(vt, guard) }\n    when POptionalType\n      # Assignable if undef and contained type is assignable\n      assignable?(PUndefType::DEFAULT) && (o.type.nil? || assignable?(o.type))\n    when PNotUndefType\n      if !(o.type.nil? || o.type.assignable?(PUndefType::DEFAULT))\n        assignable?(o.type, guard)\n      else\n        _assignable?(o, guard)\n      end\n    else\n      _assignable?(o, guard)\n    end\n  end\n\n  # Returns `true` if this instance is a callable that accepts the given _args_type_ type\n  #\n  # @param args_type [PAnyType] the arguments to test\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [Boolean] `true` if this instance is a callable that accepts the given _args_\n  def callable?(args_type, guard = nil)\n    args_type.is_a?(PAnyType) && kind_of_callable? && args_type.callable_args?(self, guard)\n  end\n\n  # Returns `true` if this instance is a callable that accepts the given _args_\n  #\n  # @param args [Array] the arguments to test\n  # @param block [Proc] block, or nil if not called with a block\n  # @return [Boolean] `true` if this instance is a callable that accepts the given _args_\n  def callable_with?(args, block = nil)\n    false\n  end\n\n  # Returns `true` if this instance is considered valid as arguments to the given `callable`\n  # @param callable [PAnyType] the callable\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [Boolean] `true` if this instance is considered valid as arguments to the given `callable`\n  # @api private\n  def callable_args?(callable, guard)\n    false\n  end\n\n  # Called from the `PTypeAliasType` when it detects self recursion. The default is to do nothing\n  # but some self recursive constructs are illegal such as when a `PObjectType` somehow inherits itself\n  # @param originator [PTypeAliasType] the starting point for the check\n  # @raise Puppet::Error if an illegal self recursion is detected\n  # @api private\n  def check_self_recursion(originator)\n  end\n\n  # Generalizes value specific types. Types that are not value specific will return `self` otherwise\n  # the generalized type is returned.\n  #\n  # @return [PAnyType] The generalized type\n  # @api public\n  def generalize\n    # Applicable to all types that have no variables\n    self\n  end\n\n  # Returns the loader that loaded this type.\n  # @return [Loaders::Loader] the loader\n  def loader\n    Loaders.static_loader\n  end\n\n  # Normalizes the type. This does not change the characteristics of the type but it will remove duplicates\n  # and constructs like NotUndef[T] where T is not assignable from Undef and change Variant[*T] where all\n  # T are enums into an Enum.\n  #\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [PAnyType] The iterable type that this type is assignable to or `nil`\n  # @api public\n  def normalize(guard = nil)\n    self\n  end\n\n  # Called from the TypeParser once it has found a type using the Loader to enable that this type can\n  # resolve internal type expressions using a loader. Presently, this method is a no-op for all types\n  # except the {{PTypeAliasType}}.\n  #\n  # @param loader [Loader::Loader] loader to use\n  # @return [PTypeAliasType] the receiver of the call, i.e. `self`\n  # @api private\n  def resolve(loader)\n    self\n  end\n\n  # Responds `true` for all callables, variants of callables and unless _optional_ is\n  # false, all optional callables.\n  # @param optional [Boolean]\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [Boolean] `true`if this type is considered callable\n  # @api private\n  def kind_of_callable?(optional = true, guard = nil)\n    false\n  end\n\n  # Returns `true` if an instance of this type is iterable, `false` otherwise\n  # The method #iterable_type must produce a `PIterableType` instance when this\n  # method returns `true`\n  #\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [Boolean] flag to indicate if instances of  this type is iterable.\n  def iterable?(guard = nil)\n    false\n  end\n\n  # Returns the `PIterableType` that this type should be assignable to, or `nil` if no such type exists.\n  # A type that returns a `PIterableType` must respond `true` to `#iterable?`.\n  #\n  # @example\n  #     Any Collection[T] is assignable to an Iterable[T]\n  #     A String is assignable to an Iterable[String] iterating over the strings characters\n  #     An Integer is assignable to an Iterable[Integer] iterating over the 'times' enumerator\n  #     A Type[T] is assignable to an Iterable[Type[T]] if T is an Integer or Enum\n  #\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [PIterableType,nil] The iterable type that this type is assignable to or `nil`\n  # @api private\n  def iterable_type(guard = nil)\n    nil\n  end\n\n  def hash\n    self.class.hash\n  end\n\n  # Returns true if the given argument _o_ is an instance of this type\n  # @param guard [RecursionGuard] guard against recursion. Only used by internal calls\n  # @return [Boolean]\n  # @api public\n  def instance?(o, guard = nil)\n    true\n  end\n\n  # An object is considered to really be an instance of a type when something other than a\n  # TypeAlias or a Variant responds true to a call to {#instance?}.\n  #\n  # @return [Integer] -1 = is not instance, 0 = recursion detected, 1 = is instance\n  # @api private\n  def really_instance?(o, guard = nil)\n    instance?(o, guard) ? 1 : -1\n  end\n\n  def eql?(o)\n    self.class == o.class\n  end\n\n  def ==(o)\n    eql?(o)\n  end\n\n  def simple_name\n    self.class.simple_name\n  end\n\n  # Strips the class name from all module prefixes, the leading 'P' and the ending 'Type'. I.e.\n  # an instance of PVariantType will return 'Variant'\n  # @return [String] the simple name of this type\n  def self.simple_name\n    @simple_name ||= (\n      n = name\n      n[n.rindex(DOUBLE_COLON) + 3..n.size - 5].freeze\n    )\n  end\n\n  def to_alias_expanded_s\n    TypeFormatter.new.alias_expanded_string(self)\n  end\n\n  def to_s\n    TypeFormatter.string(self)\n  end\n\n  # Returns the name of the type, without parameters\n  # @return [String] the name of the type\n  # @api public\n  def name\n    simple_name\n  end\n\n  def create(*args)\n    Loaders.find_loader(nil).load(:function, 'new').call({}, self, *args)\n  end\n\n  # Create an instance of this type.\n  # The default implementation will just dispatch the call to the class method with the\n  # same name and pass `self` as the first argument.\n  #\n  # @return [Function] the created function\n  # @raises ArgumentError\n  #\n  def new_function\n    self.class.new_function(self)\n  end\n\n  # This default implementation of of a new_function raises an Argument Error.\n  # Types for which creating a new instance is supported, should create and return\n  # a Puppet Function class by using Puppet:Loaders.create_loaded_function(:new, loader)\n  # and return that result.\n  #\n  # @param type [PAnyType] the type to create a new function for\n  # @return [Function] the created function\n  # @raises ArgumentError\n  #\n  def self.new_function(type)\n    raise ArgumentError, \"Creation of new instance of type '#{type}' is not supported\"\n  end\n\n  # Answers the question if instances of this type can represent themselves as a string that\n  # can then be passed to the create method\n  #\n  # @return [Boolean] whether or not the instance has a canonical string representation\n  def roundtrip_with_string?\n    false\n  end\n\n  # The default instance of this type. Each type in the type system has this constant\n  # declared.\n  #\n  DEFAULT = PAnyType.new\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PAnyType)\n  end\n\n  # Produces the tuple entry at the given index given a tuple type, its from/to constraints on the last\n  # type, and an index.\n  # Produces nil if the index is out of bounds\n  # from must be less than to, and from may not be less than 0\n  #\n  # @api private\n  #\n  def tuple_entry_at(tuple_t, to, index)\n    regular = (tuple_t.types.size - 1)\n    if index < regular\n      tuple_t.types[index]\n    elsif index < regular + to\n      # in the varargs part\n      tuple_t.types[-1]\n    else\n      nil\n    end\n  end\n\n  # Applies a transformation by sending the given _method_ and _method_args_ to each of the types of the given array\n  # and collecting the results in a new array. If all transformation calls returned the type instance itself (i.e. no\n  # transformation took place), then this method will return `self`. If a transformation did occur, then this method\n  # will either return the transformed array or in case a block was given, the result of calling a given block with\n  # the transformed array.\n  #\n  # @param types [Array<PAnyType>] the array of types to transform\n  # @param method [Symbol] The method to call on each type\n  # @param method_args [Object] The arguments to pass to the method, if any\n  # @return [Object] self, the transformed array, or the result of calling a given block with the transformed array\n  # @yieldparam altered_types [Array<PAnyType>] the altered type array\n  # @api private\n  def alter_type_array(types, method, *method_args)\n    modified = false\n    modified_types = types.map do |t|\n      t_mod = t.send(method, *method_args)\n      modified ||= !t.equal?(t_mod)\n      t_mod\n    end\n    if modified\n      block_given? ? yield(modified_types) : modified_types\n    else\n      self\n    end\n  end\nend\n\n# @abstract Encapsulates common behavior for a type that contains one type\n# @api public\nclass PTypeWithContainedType < PAnyType\n  def self.register_ptype(loader, ir)\n    # Abstract type. It doesn't register anything\n  end\n\n  attr_reader :type\n\n  def initialize(type)\n    @type = type\n  end\n\n  def accept(visitor, guard)\n    super\n    @type.accept(visitor, guard) unless @type.nil?\n  end\n\n  def generalize\n    if @type.nil?\n      self.class::DEFAULT\n    else\n      ge_type = @type.generalize\n      @type.equal?(ge_type) ? self : self.class.new(ge_type)\n    end\n  end\n\n  def normalize(guard = nil)\n    if @type.nil?\n      self.class::DEFAULT\n    else\n      ne_type = @type.normalize(guard)\n      @type.equal?(ne_type) ? self : self.class.new(ne_type)\n    end\n  end\n\n  def hash\n    self.class.hash ^ @type.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @type == o.type\n  end\n\n  def resolve(loader)\n    rtype = @type\n    rtype = rtype.resolve(loader) unless rtype.nil?\n    rtype.equal?(@type) ? self : self.class.new(rtype)\n  end\nend\n\n# The type of types.\n# @api public\n#\nclass PTypeType < PTypeWithContainedType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  # Returns a new function that produces a Type instance\n  #\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_type, type.loader) do\n      dispatch :from_string do\n        param 'String[1]', :type_string\n      end\n\n      def from_string(type_string)\n        TypeParser.singleton.parse(type_string, loader)\n      end\n    end\n  end\n\n  def instance?(o, guard = nil)\n    case o\n    when PAnyType\n      type.nil? || type.assignable?(o, guard)\n    when Module, Puppet::Resource, Puppet::Parser::Resource\n      @type.nil? ? true : assignable?(TypeCalculator.infer(o))\n    else\n      false\n    end\n  end\n\n  def iterable?(guard = nil)\n    case @type\n    when PEnumType\n      true\n    when PIntegerType\n      @type.finite_range?\n    else\n      false\n    end\n  end\n\n  def iterable_type(guard = nil)\n    # The types PIntegerType and PEnumType are Iterable\n    case @type\n    when PEnumType\n      # @type describes the element type perfectly since the iteration is made over the\n      # contained choices.\n      PIterableType.new(@type)\n    when PIntegerType\n      # @type describes the element type perfectly since the iteration is made over the\n      # specified range.\n      @type.finite_range? ? PIterableType.new(@type) : nil\n    else\n      nil\n    end\n  end\n\n  def eql?(o)\n    self.class == o.class && @type == o.type\n  end\n\n  DEFAULT = PTypeType.new(nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    return false unless o.is_a?(PTypeType)\n    return true if @type.nil? # wide enough to handle all types\n    return false if o.type.nil? # wider than t\n\n    @type.assignable?(o.type, guard)\n  end\nend\n\n# For backward compatibility\nPType = PTypeType\n\nclass PNotUndefType < PTypeWithContainedType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  def initialize(type = nil)\n    super(type.instance_of?(PAnyType) ? nil : type)\n  end\n\n  def instance?(o, guard = nil)\n    !(o.nil? || o == :undef) && (@type.nil? || @type.instance?(o, guard))\n  end\n\n  def normalize(guard = nil)\n    n = super\n    if n.type.nil?\n      n\n    elsif n.type.is_a?(POptionalType)\n      PNotUndefType.new(n.type.type).normalize\n    # No point in having an optional in a NotUndef\n    elsif !n.type.assignable?(PUndefType::DEFAULT)\n      # THe type is NotUndef anyway, so it can be stripped of\n      n.type\n    else\n      n\n    end\n  end\n\n  def new_function\n    # If only NotUndef, then use Unit's null converter\n    if type.nil?\n      PUnitType.new_function(self)\n    else\n      type.new_function\n    end\n  end\n\n  DEFAULT = PNotUndefType.new\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PAnyType) && !o.assignable?(PUndefType::DEFAULT, guard) && (@type.nil? || @type.assignable?(o, guard))\n  end\nend\n\n# @api public\n#\nclass PUndefType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  def instance?(o, guard = nil)\n    o.nil? || :undef == o\n  end\n\n  # @api private\n  def callable_args?(callable_t, guard)\n    # if callable_t is Optional (or indeed PUndefType), this means that 'missing callable' is accepted\n    callable_t.assignable?(DEFAULT, guard)\n  end\n\n  DEFAULT = PUndefType.new\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PUndefType)\n  end\nend\n\n# A type private to the type system that describes \"ignored type\" - i.e. \"I am what you are\"\n# @api private\n#\nclass PUnitType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  def instance?(o, guard = nil)\n    true\n  end\n\n  # A \"null\" implementation - that simply returns the given argument\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_unit, type.loader) do\n      dispatch :from_args do\n        param 'Any', :from\n      end\n\n      def from_args(from)\n        from\n      end\n    end\n  end\n\n  DEFAULT = PUnitType.new\n\n  def assignable?(o, guard = nil)\n    true\n  end\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    true\n  end\nend\n\n# @api public\n#\nclass PDefaultType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  def instance?(o, guard = nil)\n    # Ensure that Symbol.== is called here instead of something unknown\n    # that is implemented on o\n    :default == o\n  end\n\n  DEFAULT = PDefaultType.new\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PDefaultType)\n  end\nend\n\n# Type that is a Scalar\n# @api public\n#\nclass PScalarType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  def instance?(o, guard = nil)\n    if o.is_a?(String) || o.is_a?(Numeric) || o.is_a?(TrueClass) || o.is_a?(FalseClass) || o.is_a?(Regexp)\n      true\n    elsif o.instance_of?(Array) || o.instance_of?(Hash) || o.is_a?(PAnyType) || o.is_a?(NilClass)\n      false\n    else\n      assignable?(TypeCalculator.infer(o))\n    end\n  end\n\n  def roundtrip_with_string?\n    true\n  end\n\n  DEFAULT = PScalarType.new\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PScalarType) ||\n      PStringType::DEFAULT.assignable?(o, guard) ||\n      PIntegerType::DEFAULT.assignable?(o, guard) ||\n      PFloatType::DEFAULT.assignable?(o, guard) ||\n      PBooleanType::DEFAULT.assignable?(o, guard) ||\n      PRegexpType::DEFAULT.assignable?(o, guard) ||\n      PSemVerType::DEFAULT.assignable?(o, guard) ||\n      PSemVerRangeType::DEFAULT.assignable?(o, guard) ||\n      PTimespanType::DEFAULT.assignable?(o, guard) ||\n      PTimestampType::DEFAULT.assignable?(o, guard)\n  end\nend\n\n# Like Scalar but limited to Json Data.\n# @api public\n#\nclass PScalarDataType < PScalarType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarType')\n  end\n\n  def instance?(o, guard = nil)\n    o.instance_of?(String) || o.is_a?(Integer) || o.is_a?(Float) || o.is_a?(TrueClass) || o.is_a?(FalseClass)\n  end\n\n  DEFAULT = PScalarDataType.new\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PScalarDataType) ||\n      PStringType::DEFAULT.assignable?(o, guard) ||\n      PIntegerType::DEFAULT.assignable?(o, guard) ||\n      PFloatType::DEFAULT.assignable?(o, guard) ||\n      PBooleanType::DEFAULT.assignable?(o, guard)\n  end\nend\n\n# A string type describing the set of strings having one of the given values\n# @api public\n#\nclass PEnumType < PScalarDataType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarDataType',\n                 'values' => PArrayType.new(PStringType::NON_EMPTY),\n                 'case_insensitive' => { 'type' => PBooleanType::DEFAULT, 'value' => false })\n  end\n\n  attr_reader :values, :case_insensitive\n\n  def initialize(values, case_insensitive = false)\n    @values = values.uniq.sort.freeze\n    @case_insensitive = case_insensitive\n  end\n\n  def case_insensitive?\n    @case_insensitive\n  end\n\n  # Returns Enumerator if no block is given, otherwise, calls the given\n  # block with each of the strings for this enum\n  def each(&block)\n    r = Iterable.on(self)\n    block_given? ? r.each(&block) : r\n  end\n\n  def generalize\n    # General form of an Enum is a String\n    if @values.empty?\n      PStringType::DEFAULT\n    else\n      range = @values.map(&:size).minmax\n      PStringType.new(PIntegerType.new(range.min, range.max))\n    end\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    # An instance of an Enum is a String\n    PStringType::ITERABLE_TYPE\n  end\n\n  def hash\n    @values.hash ^ @case_insensitive.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @values == o.values && @case_insensitive == o.case_insensitive?\n  end\n\n  def instance?(o, guard = nil)\n    if o.is_a?(String)\n      @case_insensitive ? @values.any? { |p| p.casecmp(o) == 0 } : @values.any? { |p| p == o }\n    else\n      false\n    end\n  end\n\n  DEFAULT = PEnumType.new(EMPTY_ARRAY)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    return true if self == o\n\n    svalues = values\n    if svalues.empty?\n      return true if o.is_a?(PStringType) || o.is_a?(PEnumType) || o.is_a?(PPatternType)\n    end\n    case o\n    when PStringType\n      # if the contained string is found in the set of enums\n      instance?(o.value, guard)\n    when PEnumType\n      !o.values.empty? && (case_insensitive? || !o.case_insensitive?) && o.values.all? { |s| instance?(s, guard) }\n    else\n      false\n    end\n  end\nend\n\nINTEGER_HEX = '(?:0[xX][0-9A-Fa-f]+)'\nINTEGER_OCT = '(?:0[0-7]+)'\nINTEGER_BIN = '(?:0[bB][01]+)'\nINTEGER_DEC = '(?:0|[1-9]\\d*)'\nINTEGER_DEC_OR_OCT = '(?:\\d+)'\nSIGN_PREFIX = '[+-]?\\s*'\n\nOPTIONAL_FRACTION = '(?:\\.\\d+)?'\nOPTIONAL_EXPONENT = '(?:[eE]-?\\d+)?'\nFLOAT_DEC = '(?:' + INTEGER_DEC + OPTIONAL_FRACTION + OPTIONAL_EXPONENT + ')'\n\nINTEGER_PATTERN = '\\A' + SIGN_PREFIX + '(?:' + INTEGER_DEC + '|' + INTEGER_HEX + '|' + INTEGER_OCT + '|' + INTEGER_BIN + ')\\z'\nINTEGER_PATTERN_LENIENT = '\\A' + SIGN_PREFIX + '(?:' + INTEGER_DEC_OR_OCT + '|' + INTEGER_HEX + '|' + INTEGER_BIN + ')\\z'\nFLOAT_PATTERN = '\\A' + SIGN_PREFIX + '(?:' + FLOAT_DEC + '|' + INTEGER_HEX + '|' + INTEGER_OCT + '|' + INTEGER_BIN + ')\\z'\n\n# @api public\n#\nclass PNumericType < PScalarDataType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarDataType',\n                 'from' => { KEY_TYPE => POptionalType.new(PNumericType::DEFAULT), KEY_VALUE => nil },\n                 'to' => { KEY_TYPE => POptionalType.new(PNumericType::DEFAULT), KEY_VALUE => nil })\n  end\n\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_numeric, type.loader) do\n      local_types do\n        type \"Convertible = Variant[Integer, Float, Boolean, Pattern[/#{FLOAT_PATTERN}/], Timespan, Timestamp]\"\n        type 'NamedArgs   = Struct[{from => Convertible, Optional[abs] => Boolean}]'\n      end\n\n      dispatch :from_args do\n        param          'Convertible',  :from\n        optional_param 'Boolean',      :abs\n      end\n\n      dispatch :from_hash do\n        param          'NamedArgs', :hash_args\n      end\n\n      argument_mismatch :on_error do\n        param          'Any',     :from\n        optional_param 'Boolean', :abs\n      end\n\n      def from_args(from, abs = false)\n        result = from_convertible(from)\n        abs ? result.abs : result\n      end\n\n      def from_hash(args_hash)\n        from_args(args_hash['from'], args_hash['abs'] || false)\n      end\n\n      def from_convertible(from)\n        case from\n        when Float\n          from\n        when Integer\n          from\n        when Time::TimeData\n          from.to_f\n        when TrueClass\n          1\n        when FalseClass\n          0\n        else\n          begin\n            if from[0] == '0'\n              second_char = (from[1] || '').downcase\n              if second_char == 'b' || second_char == 'x'\n                # use built in conversion\n                return Integer(from)\n              end\n            end\n\n            Puppet::Pops::Utils.to_n(from)\n          rescue TypeError => e\n            raise TypeConversionError, e.message\n          rescue ArgumentError => e\n            raise TypeConversionError, e.message\n          end\n        end\n      end\n\n      def on_error(from, abs = false)\n        if from.is_a?(String)\n          _(\"The string '%{str}' cannot be converted to Numeric\") % { str: from }\n        else\n          t = TypeCalculator.singleton.infer(from).generalize\n          _(\"Value of type %{type} cannot be converted to Numeric\") % { type: t }\n        end\n      end\n    end\n  end\n\n  def initialize(from, to = Float::INFINITY)\n    from = -Float::INFINITY if from.nil? || from == :default\n    to = Float::INFINITY if to.nil? || to == :default\n    raise ArgumentError, \"'from' must be less or equal to 'to'. Got (#{from}, #{to}\" if from > to\n\n    @from = from\n    @to = to\n  end\n\n  # Checks if this numeric range intersects with another\n  #\n  # @param o [PNumericType] the range to compare with\n  # @return [Boolean] `true` if this range intersects with the other range\n  # @api public\n  def intersect?(o)\n    instance_of?(o.class) && !(@to < o.numeric_from || o.numeric_to < @from)\n  end\n\n  # Returns the lower bound of the numeric range or `nil` if no lower bound is set.\n  # @return [Float,Integer]\n  def from\n    @from == -Float::INFINITY ? nil : @from\n  end\n\n  # Returns the upper bound of the numeric range or `nil` if no upper bound is set.\n  # @return [Float,Integer]\n  def to\n    @to == Float::INFINITY ? nil : @to\n  end\n\n  # Same as #from but will return `-Float::Infinity` instead of `nil` if no lower bound is set.\n  # @return [Float,Integer]\n  def numeric_from\n    @from\n  end\n\n  # Same as #to but will return `Float::Infinity` instead of `nil` if no lower bound is set.\n  # @return [Float,Integer]\n  def numeric_to\n    @to\n  end\n\n  def hash\n    @from.hash ^ @to.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @from == o.numeric_from && @to == o.numeric_to\n  end\n\n  def instance?(o, guard = nil)\n    (o.is_a?(Float) || o.is_a?(Integer)) && o >= @from && o <= @to\n  end\n\n  def unbounded?\n    @from == -Float::INFINITY && @to == Float::INFINITY\n  end\n\n  protected\n\n  # @api_private\n  def _assignable?(o, guard)\n    return false unless o.is_a?(self.class)\n\n    # If o min and max are within the range of t\n    @from <= o.numeric_from && @to >= o.numeric_to\n  end\n\n  DEFAULT = PNumericType.new(-Float::INFINITY)\nend\n\n# @api public\n#\nclass PIntegerType < PNumericType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'NumericType')\n  end\n\n  # Will respond `true` for any range that is bounded at both ends.\n  #\n  # @return [Boolean] `true` if the type describes a finite range.\n  def finite_range?\n    @from != -Float::INFINITY && @to != Float::INFINITY\n  end\n\n  def generalize\n    DEFAULT\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(Integer) && o >= numeric_from && o <= numeric_to\n  end\n\n  # Checks if this range is adjacent to the given range\n  #\n  # @param o [PIntegerType] the range to compare with\n  # @return [Boolean] `true` if this range is adjacent to the other range\n  # @api public\n  def adjacent?(o)\n    o.is_a?(PIntegerType) && (@to + 1 == o.from || o.to + 1 == @from)\n  end\n\n  # Concatenates this range with another range provided that the ranges intersect or\n  # are adjacent. When that's not the case, this method will return `nil`\n  #\n  # @param o [PIntegerType] the range to concatenate with this range\n  # @return [PIntegerType,nil] the concatenated range or `nil` when the ranges were apart\n  # @api public\n  def merge(o)\n    if intersect?(o) || adjacent?(o)\n      min = @from <= o.numeric_from ? @from : o.numeric_from\n      max = @to >= o.numeric_to ? @to : o.numeric_to\n      PIntegerType.new(min, max)\n    else\n      nil\n    end\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    # It's unknown if the iterable will be a range (min, max) or a \"times\" (0, max)\n    PIterableType.new(PIntegerType::DEFAULT)\n  end\n\n  # Returns Float.Infinity if one end of the range is unbound\n  def size\n    return Float::INFINITY if @from == -Float::INFINITY || @to == Float::INFINITY\n\n    1 + (to - from).abs\n  end\n\n  # Returns the range as an array ordered so the smaller number is always first.\n  # The number may be Infinity or -Infinity.\n  def range\n    [@from, @to]\n  end\n\n  # Returns Enumerator if no block is given\n  # Returns nil if size is infinity (does not yield)\n  def each(&block)\n    r = Iterable.on(self)\n    block_given? ? r.each(&block) : r\n  end\n\n  # Returns a range where both to and from are positive numbers. Negative\n  # numbers are converted to zero\n  # @return [PIntegerType] a positive range\n  def to_size\n    if @from >= 0\n      self\n    else\n      PIntegerType.new(0, @to < 0 ? 0 : @to)\n    end\n  end\n\n  def new_function\n    @@new_function ||= Puppet::Functions.create_loaded_function(:new, loader) do\n      local_types do\n        type 'Radix       = Variant[Default, Integer[2,2], Integer[8,8], Integer[10,10], Integer[16,16]]'\n        type \"Convertible = Variant[Numeric, Boolean, Pattern[/#{INTEGER_PATTERN_LENIENT}/], Timespan, Timestamp]\"\n        type 'NamedArgs   = Struct[{from => Convertible, Optional[radix] => Radix, Optional[abs] => Boolean}]'\n      end\n\n      dispatch :from_args do\n        param          'Convertible', :from\n        optional_param 'Radix',   :radix\n        optional_param 'Boolean', :abs\n      end\n\n      dispatch :from_hash do\n        param          'NamedArgs', :hash_args\n      end\n\n      argument_mismatch :on_error_hash do\n        param          'Hash', :hash_args\n      end\n\n      argument_mismatch :on_error do\n        param          'Any',     :from\n        optional_param 'Integer', :radix\n        optional_param 'Boolean', :abs\n      end\n\n      def from_args(from, radix = :default, abs = false)\n        result = from_convertible(from, radix)\n        abs ? result.abs : result\n      end\n\n      def from_hash(args_hash)\n        from_args(args_hash['from'], args_hash['radix'] || :default, args_hash['abs'] || false)\n      end\n\n      def from_convertible(from, radix)\n        case from\n        when Float, Time::TimeData\n          from.to_i\n        when Integer\n          from\n        when TrueClass\n          1\n        when FalseClass\n          0\n        else\n          begin\n            radix == :default ? Integer(from) : Integer(from, radix)\n          rescue TypeError => e\n            raise TypeConversionError, e.message\n          rescue ArgumentError => e\n            # Test for special case where there is whitespace between sign and number\n            match = Patterns::WS_BETWEEN_SIGN_AND_NUMBER.match(from)\n            if match\n              begin\n                # Try again, this time with whitespace removed\n                return from_args(match[1] + match[2], radix)\n              rescue TypeConversionError\n                # Ignored to retain original error\n              end\n            end\n            raise TypeConversionError, e.message\n          end\n        end\n      end\n\n      def on_error_hash(args_hash)\n        if args_hash.include?('from')\n          from = args_hash['from']\n          return on_error(from) unless loader.load(:type, 'convertible').instance?(from)\n        end\n        radix = args_hash['radix']\n        assert_radix(radix) unless radix.nil? || radix == :default\n        TypeAsserter.assert_instance_of('Integer.new', loader.load(:type, 'namedargs'), args_hash)\n      end\n\n      def on_error(from, radix = :default, abs = nil)\n        assert_radix(radix) unless radix == :default\n        if from.is_a?(String)\n          _(\"The string '%{str}' cannot be converted to Integer\") % { str: from }\n        else\n          t = TypeCalculator.singleton.infer(from).generalize\n          _(\"Value of type %{type} cannot be converted to Integer\") % { type: t }\n        end\n      end\n\n      def assert_radix(radix)\n        case radix\n        when 2, 8, 10, 16\n          # do nothing\n        else\n          raise ArgumentError, _(\"Illegal radix: %{radix}, expected 2, 8, 10, 16, or default\") % { radix: radix }\n        end\n        radix\n      end\n    end\n  end\n\n  DEFAULT = PIntegerType.new(-Float::INFINITY)\nend\n\n# @api public\n#\nclass PFloatType < PNumericType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'NumericType')\n  end\n\n  def generalize\n    DEFAULT\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(Float) && o >= numeric_from && o <= numeric_to\n  end\n\n  # Concatenates this range with another range provided that the ranges intersect. When that's not the case, this\n  # method will return `nil`\n  #\n  # @param o [PFloatType] the range to concatenate with this range\n  # @return [PFloatType,nil] the concatenated range or `nil` when the ranges were apart\n  # @api public\n  def merge(o)\n    if intersect?(o)\n      min = @from <= o.from ? @from : o.from\n      max = @to >= o.to ? @to : o.to\n      PFloatType.new(min, max)\n    else\n      nil\n    end\n  end\n\n  # Returns a new function that produces a Float value\n  #\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_float, type.loader) do\n      local_types do\n        type \"Convertible = Variant[Numeric, Boolean, Pattern[/#{FLOAT_PATTERN}/], Timespan, Timestamp]\"\n        type 'NamedArgs   = Struct[{from => Convertible, Optional[abs] => Boolean}]'\n      end\n\n      dispatch :from_args do\n        param          'Convertible',  :from\n        optional_param 'Boolean',      :abs\n      end\n\n      dispatch :from_hash do\n        param          'NamedArgs', :hash_args\n      end\n\n      argument_mismatch :on_error do\n        param          'Any',     :from\n        optional_param 'Boolean', :abs\n      end\n\n      def from_args(from, abs = false)\n        result = from_convertible(from)\n        abs ? result.abs : result\n      end\n\n      def from_hash(args_hash)\n        from_args(args_hash['from'], args_hash['abs'] || false)\n      end\n\n      def from_convertible(from)\n        case from\n        when Float\n          from\n        when Integer\n          Float(from)\n        when Time::TimeData\n          from.to_f\n        when TrueClass\n          1.0\n        when FalseClass\n          0.0\n        else\n          begin\n            # support a binary as float\n            if from[0] == '0' && from[1].casecmp('b').zero?\n              from = Integer(from)\n            end\n            Float(from)\n          rescue TypeError => e\n            raise TypeConversionError, e.message\n          rescue ArgumentError => e\n            # Test for special case where there is whitespace between sign and number\n            match = Patterns::WS_BETWEEN_SIGN_AND_NUMBER.match(from)\n            if match\n              begin\n                # Try again, this time with whitespace removed\n                return from_args(match[1] + match[2])\n              rescue TypeConversionError\n                # Ignored to retain original error\n              end\n            end\n            raise TypeConversionError, e.message\n          end\n        end\n      end\n\n      def on_error(from, _ = false)\n        if from.is_a?(String)\n          _(\"The string '%{str}' cannot be converted to Float\") % { str: from }\n        else\n          t = TypeCalculator.singleton.infer(from).generalize\n          _(\"Value of type %{type} cannot be converted to Float\") % { type: t }\n        end\n      end\n    end\n  end\n\n  DEFAULT = PFloatType.new(-Float::INFINITY)\nend\n\n# @api public\n#\nclass PCollectionType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'size_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType.new(PIntegerType::DEFAULT)),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  attr_reader :size_type\n\n  def initialize(size_type)\n    @size_type = size_type.nil? ? nil : size_type.to_size\n  end\n\n  def accept(visitor, guard)\n    super\n    @size_type.accept(visitor, guard) unless @size_type.nil?\n  end\n\n  def generalize\n    DEFAULT\n  end\n\n  def normalize(guard = nil)\n    DEFAULT\n  end\n\n  def instance?(o, guard = nil)\n    # The inferred type of a class derived from Array or Hash is either Runtime or Object. It's not assignable to the Collection type.\n    if o.instance_of?(Array) || o.instance_of?(Hash)\n      @size_type.nil? || @size_type.instance?(o.size)\n    else\n      false\n    end\n  end\n\n  # Returns an array with from (min) size to (max) size\n  def size_range\n    (@size_type || DEFAULT_SIZE).range\n  end\n\n  def has_empty_range?\n    from, to = size_range\n    from == 0 && to == 0\n  end\n\n  def hash\n    @size_type.hash\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def eql?(o)\n    self.class == o.class && @size_type == o.size_type\n  end\n\n  DEFAULT_SIZE = PIntegerType.new(0)\n  ZERO_SIZE = PIntegerType.new(0, 0)\n  NOT_EMPTY_SIZE = PIntegerType.new(1)\n  DEFAULT = PCollectionType.new(nil)\n\n  protected\n\n  # @api private\n  #\n  def _assignable?(o, guard)\n    case o\n    when PCollectionType\n      (@size_type || DEFAULT_SIZE).assignable?(o.size_type || DEFAULT_SIZE, guard)\n    when PTupleType\n      # compute the tuple's min/max size, and check if that size matches\n      size_s = size_type || DEFAULT_SIZE\n      size_o = o.size_type\n      if size_o.nil?\n        type_count = o.types.size\n        size_o = PIntegerType.new(type_count, type_count)\n      end\n      size_s.assignable?(size_o)\n    when PStructType\n      from = to = o.elements.size\n      (@size_type || DEFAULT_SIZE).assignable?(PIntegerType.new(from, to), guard)\n    else\n      false\n    end\n  end\nend\n\nclass PIterableType < PTypeWithContainedType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  def element_type\n    @type\n  end\n\n  def instance?(o, guard = nil)\n    if @type.nil? || @type.assignable?(PAnyType::DEFAULT, guard)\n      # Any element_type will do\n      case o\n      when Iterable, String, Hash, Array, Range, PEnumType\n        true\n      when Integer\n        o >= 0\n      when PIntegerType\n        o.finite_range?\n      when PTypeAliasType\n        instance?(o.resolved_type, guard)\n      else\n        false\n      end\n    else\n      assignable?(TypeCalculator.infer(o), guard)\n    end\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    self\n  end\n\n  DEFAULT = PIterableType.new(nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    if @type.nil? || @type.assignable?(PAnyType::DEFAULT, guard)\n      # Don't request the iterable_type. Since this Iterable accepts Any element, it is enough that o is iterable.\n      o.iterable?\n    else\n      o = o.iterable_type\n      o.nil? || o.element_type.nil? ? false : @type.assignable?(o.element_type, guard)\n    end\n  end\nend\n\n# @api public\n#\nclass PIteratorType < PTypeWithContainedType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  def element_type\n    @type\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(Iterable) && (@type.nil? || @type.assignable?(o.element_type, guard))\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    @type.nil? ? PIterableType::DEFAULT : PIterableType.new(@type)\n  end\n\n  DEFAULT = PIteratorType.new(nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PIteratorType) && (@type.nil? || @type.assignable?(o.element_type, guard))\n  end\nend\n\n# @api public\n#\nclass PStringType < PScalarDataType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarDataType',\n                 'size_type_or_value' => {\n                   KEY_TYPE => POptionalType.new(PVariantType.new([PStringType::DEFAULT, PTypeType.new(PIntegerType::DEFAULT)])),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  attr_reader :size_type_or_value\n\n  def initialize(size_type_or_value, deprecated_multi_args = EMPTY_ARRAY)\n    unless deprecated_multi_args.empty?\n      if Puppet[:strict] != :off\n        # TRANSLATORS 'PStringType#initialize' is a class and method name and should not be translated\n        Puppet.warn_once('deprecations', \"PStringType#initialize_multi_args\",\n                         _(\"Passing more than one argument to PStringType#initialize is deprecated\"))\n      end\n      size_type_or_value = deprecated_multi_args[0]\n    end\n    @size_type_or_value = size_type_or_value.is_a?(PIntegerType) ? size_type_or_value.to_size : size_type_or_value\n  end\n\n  def accept(visitor, guard)\n    super\n    @size_type_or_value.accept(visitor, guard) if @size_type_or_value.is_a?(PIntegerType)\n  end\n\n  def generalize\n    DEFAULT\n  end\n\n  def hash\n    @size_type_or_value.hash\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    ITERABLE_TYPE\n  end\n\n  def eql?(o)\n    self.class == o.class && @size_type_or_value == o.size_type_or_value\n  end\n\n  def instance?(o, guard = nil)\n    # true if size compliant\n    if o.is_a?(String)\n      if @size_type_or_value.is_a?(PIntegerType)\n        @size_type_or_value.instance?(o.size, guard)\n      else\n        @size_type_or_value.nil? ? true : o == value\n      end\n    else\n      false\n    end\n  end\n\n  def value\n    @size_type_or_value.is_a?(PIntegerType) ? nil : @size_type_or_value\n  end\n\n  # @deprecated\n  # @api private\n  def values\n    if Puppet[:strict] != :off\n      # TRANSLATORS 'PStringType#values' and '#value' are classes and method names and should not be translated\n      Puppet.warn_once('deprecations', \"PStringType#values\", _(\"Method PStringType#values is deprecated. Use #value instead\"))\n    end\n    @value.is_a?(String) ? [@value] : EMPTY_ARRAY\n  end\n\n  def size_type\n    @size_type_or_value.is_a?(PIntegerType) ? @size_type_or_value : nil\n  end\n\n  def derived_size_type\n    case @size_type_or_value\n    when PIntegerType\n      @size_type_or_value\n    when String\n      sz = @size_type_or_value.size\n      PIntegerType.new(sz, sz)\n    else\n      PCollectionType::DEFAULT_SIZE\n    end\n  end\n\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_string, type.loader) do\n      local_types do\n        type \"Format = Pattern[/#{StringConverter::Format::FMT_PATTERN_STR}/]\"\n        type 'ContainerFormat = Struct[{\n          Optional[format]         => Format,\n          Optional[separator]      => String,\n          Optional[separator2]     => String,\n          Optional[string_formats] => Hash[Type, Format]\n        }]'\n        type 'TypeMap = Hash[Type, Variant[Format, ContainerFormat]]'\n        type 'Convertible = Any'\n        type 'Formats = Variant[Default, String[1], TypeMap]'\n      end\n\n      dispatch :from_args do\n        param           'Convertible',  :from\n        optional_param  'Formats',      :string_formats\n      end\n\n      def from_args(from, formats = :default)\n        StringConverter.singleton.convert(from, formats)\n      end\n    end\n  end\n\n  DEFAULT = PStringType.new(nil)\n  NON_EMPTY = PStringType.new(PCollectionType::NOT_EMPTY_SIZE)\n\n  # Iterates over each character of the string\n  ITERABLE_TYPE = PIterableType.new(PStringType.new(PIntegerType.new(1, 1)))\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    if @size_type_or_value.is_a?(PIntegerType)\n      # A general string is assignable by any other string or pattern restricted string\n      # if the string has a size constraint it does not match since there is no reasonable way\n      # to compute the min/max length a pattern will match. For enum, it is possible to test that\n      # each enumerator value is within range\n      case o\n      when PStringType\n        @size_type_or_value.assignable?(o.derived_size_type, guard)\n\n      when PEnumType\n        if o.values.empty?\n          # enum represents all enums, and thus all strings, a sized constrained string can thus not\n          # be assigned any enum (unless it is max size).\n          @size_type_or_value.assignable?(PCollectionType::DEFAULT_SIZE, guard)\n        else\n          # true if all enum values are within range\n          orange = o.values.map(&:size).minmax\n          srange = @size_type_or_value.range\n          # If o min and max are within the range of t\n          srange[0] <= orange[0] && srange[1] >= orange[1]\n        end\n\n      when PPatternType\n        # true if size constraint is at least 0 to +Infinity (which is the same as the default)\n        @size_type_or_value.assignable?(PCollectionType::DEFAULT_SIZE, guard)\n      else\n        # no other type matches string\n        false\n      end\n    else\n      case o\n      when PStringType\n        # Must match exactly when value is a string\n        @size_type_or_value.nil? || @size_type_or_value == o.size_type_or_value\n      when PEnumType\n        @size_type_or_value.nil? ? true : o.values.size == 1 && !o.case_insensitive? && o.values[0]\n      when PPatternType\n        @size_type_or_value.nil?\n      else\n        # All others are false, since no other type describes the same set of specific strings\n        false\n      end\n    end\n  end\nend\n\n# @api public\n#\nclass PRegexpType < PScalarType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarType',\n                 'pattern' => {\n                   KEY_TYPE => PVariantType.new([PUndefType::DEFAULT, PStringType::DEFAULT, PRegexpType::DEFAULT]),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  # Returns a new function that produces a Regexp instance\n  #\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_float, type.loader) do\n      dispatch :from_string do\n        param 'String', :pattern\n        optional_param 'Boolean', :escape\n      end\n\n      def from_string(pattern, escape = false)\n        Regexp.new(escape ? Regexp.escape(pattern) : pattern)\n      end\n    end\n  end\n\n  attr_reader :pattern\n\n  # @param regexp [Regexp] the regular expression\n  # @return [String] the Regexp as a slash delimited string with slashes escaped\n  def self.regexp_to_s_with_delimiters(regexp)\n    regexp.options == 0 ? regexp.inspect : \"/#{regexp}/\"\n  end\n\n  # @param regexp [Regexp] the regular expression\n  # @return [String] the Regexp as a string without escaped slash\n  def self.regexp_to_s(regexp)\n    append_flags_group(regexp.source, regexp.options)\n  end\n\n  def self.append_flags_group(rx_string, options)\n    if options == 0\n      rx_string\n    else\n      bld = '(?'.dup\n      bld << 'i' if (options & Regexp::IGNORECASE) != 0\n      bld << 'm' if (options & Regexp::MULTILINE) != 0\n      bld << 'x' if (options & Regexp::EXTENDED) != 0\n      unless options == (Regexp::IGNORECASE | Regexp::MULTILINE | Regexp::EXTENDED)\n        bld << '-'\n        bld << 'i' if (options & Regexp::IGNORECASE) == 0\n        bld << 'm' if (options & Regexp::MULTILINE) == 0\n        bld << 'x' if (options & Regexp::EXTENDED) == 0\n      end\n      bld << ':' << rx_string << ')'\n      bld.freeze\n    end\n  end\n\n  def initialize(pattern)\n    if pattern.is_a?(Regexp)\n      @regexp = pattern\n      @pattern = PRegexpType.regexp_to_s(pattern)\n    else\n      @pattern = pattern\n    end\n  end\n\n  def regexp\n    @regexp ||= Regexp.new(@pattern || '')\n  end\n\n  def hash\n    @pattern.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @pattern == o.pattern\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(Regexp) && @pattern.nil? || regexp == o\n  end\n\n  DEFAULT = PRegexpType.new(nil)\n\n  protected\n\n  # @api private\n  #\n  def _assignable?(o, guard)\n    o.is_a?(PRegexpType) && (@pattern.nil? || @pattern == o.pattern)\n  end\nend\n\n# Represents a subtype of String that narrows the string to those matching the patterns\n# If specified without a pattern it is basically the same as the String type.\n#\n# @api public\n#\nclass PPatternType < PScalarDataType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarDataType', 'patterns' => PArrayType.new(PRegexpType::DEFAULT))\n  end\n\n  attr_reader :patterns\n\n  def initialize(patterns)\n    @patterns = patterns.freeze\n  end\n\n  def accept(visitor, guard)\n    super\n    @patterns.each { |p| p.accept(visitor, guard) }\n  end\n\n  def hash\n    @patterns.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @patterns.size == o.patterns.size && (@patterns - o.patterns).empty?\n  end\n\n  def instance?(o, guard = nil)\n    o.is_a?(String) && (@patterns.empty? || @patterns.any? { |p| p.regexp.match(o) })\n  end\n\n  DEFAULT = PPatternType.new(EMPTY_ARRAY)\n\n  protected\n\n  # @api private\n  #\n  def _assignable?(o, guard)\n    return true if self == o\n\n    case o\n    when PStringType\n      v = o.value\n      if v.nil?\n        # Strings cannot all match a pattern, but if there is no pattern it is ok\n        # (There should really always be a pattern, but better safe than sorry).\n        @patterns.empty?\n      else\n        # the string in String type must match one of the patterns in Pattern type,\n        # or Pattern represents all Patterns == all Strings\n        regexps = @patterns.map(&:regexp)\n        regexps.empty? || regexps.any? { |re| re.match(v) }\n      end\n    when PEnumType\n      if o.values.empty?\n        # Enums (unknown which ones) cannot all match a pattern, but if there is no pattern it is ok\n        # (There should really always be a pattern, but better safe than sorry).\n        @patterns.empty?\n      else\n        # all strings in String/Enum type must match one of the patterns in Pattern type,\n        # or Pattern represents all Patterns == all Strings\n        regexps = @patterns.map(&:regexp)\n        regexps.empty? || o.values.all? { |s| regexps.any? { |re| re.match(s) } }\n      end\n    when PPatternType\n      @patterns.empty?\n    else\n      false\n    end\n  end\nend\n\n# @api public\n#\nclass PBooleanType < PScalarDataType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'ScalarDataType')\n  end\n\n  attr_reader :value\n\n  def initialize(value = nil)\n    @value = value\n  end\n\n  def eql?(o)\n    o.is_a?(PBooleanType) && @value == o.value\n  end\n\n  def generalize\n    PBooleanType::DEFAULT\n  end\n\n  def hash\n    31 ^ @value.hash\n  end\n\n  def instance?(o, guard = nil)\n    (o == true || o == false) && (@value.nil? || value == o)\n  end\n\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_boolean, type.loader) do\n      dispatch :from_args do\n        param \"Variant[Integer, Float, Boolean, Enum['false','true','yes','no','y','n',true]]\", :from\n      end\n\n      argument_mismatch :on_error do\n        param  'Any', :from\n      end\n\n      def from_args(from)\n        from = from.downcase if from.is_a?(String)\n        case from\n        when Float, Integer\n          !from.zero?\n        when false, 'false', 'no', 'n'\n          false\n        else\n          true\n        end\n      end\n\n      def on_error(from)\n        if from.is_a?(String)\n          _(\"The string '%{str}' cannot be converted to Boolean\") % { str: from }\n        else\n          t = TypeCalculator.singleton.infer(from).generalize\n          _(\"Value of type %{type} cannot be converted to Boolean\") % { type: t }\n        end\n      end\n    end\n  end\n\n  DEFAULT = PBooleanType.new\n  TRUE = PBooleanType.new(true)\n  FALSE = PBooleanType.new(false)\n\n  protected\n\n  # @api private\n  #\n  def _assignable?(o, guard)\n    o.is_a?(PBooleanType) && (@value.nil? || @value == o.value)\n  end\nend\n\n# @api public\n#\n# @api public\n#\nclass PStructElement < TypedModelObject\n  def self.register_ptype(loader, ir)\n    @type = Pcore.create_object_type(loader, ir, self, 'Pcore::StructElement', nil,\n                                     'key_type' => PTypeType::DEFAULT,\n                                     'value_type' => PTypeType::DEFAULT)\n  end\n\n  attr_accessor :key_type, :value_type\n\n  def accept(visitor, guard)\n    @key_type.accept(visitor, guard)\n    @value_type.accept(visitor, guard)\n  end\n\n  def hash\n    value_type.hash ^ key_type.hash\n  end\n\n  def name\n    k = key_type\n    k = k.optional_type if k.is_a?(POptionalType)\n    k.value\n  end\n\n  def initialize(key_type, value_type)\n    @key_type = key_type\n    @value_type = value_type\n  end\n\n  def generalize\n    gv_type = @value_type.generalize\n    @value_type.equal?(gv_type) ? self : PStructElement.new(@key_type, gv_type)\n  end\n\n  def normalize(guard = nil)\n    nv_type = @value_type.normalize(guard)\n    @value_type.equal?(nv_type) ? self : PStructElement.new(@key_type, nv_type)\n  end\n\n  def resolve(loader)\n    rkey_type = @key_type.resolve(loader)\n    rvalue_type = @value_type.resolve(loader)\n    rkey_type.equal?(@key_type) && rvalue_type.equal?(@value_type) ? self : self.class.new(rkey_type, rvalue_type)\n  end\n\n  def <=>(o)\n    name <=> o.name\n  end\n\n  def eql?(o)\n    self == o\n  end\n\n  def ==(o)\n    self.class == o.class && value_type == o.value_type && key_type == o.key_type\n  end\n\n  # Special boostrap method to overcome the hen and egg problem with the Object initializer that contains\n  # types that are derived from Object (such as Annotation)\n  #\n  # @api private\n  def replace_value_type(new_type)\n    @value_type = new_type\n  end\nend\n\n# @api public\n#\nclass PStructType < PAnyType\n  include Enumerable\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType', 'elements' => PArrayType.new(PTypeReferenceType.new('Pcore::StructElement')))\n  end\n\n  def initialize(elements)\n    @elements = elements.freeze\n  end\n\n  def accept(visitor, guard)\n    super\n    @elements.each { |elem| elem.accept(visitor, guard) }\n  end\n\n  def each\n    if block_given?\n      elements.each { |elem| yield elem }\n    else\n      elements.to_enum\n    end\n  end\n\n  def generalize\n    if @elements.empty?\n      DEFAULT\n    else\n      alter_type_array(@elements, :generalize) { |altered| PStructType.new(altered) }\n    end\n  end\n\n  def normalize(guard = nil)\n    if @elements.empty?\n      DEFAULT\n    else\n      alter_type_array(@elements, :normalize, guard) { |altered| PStructType.new(altered) }\n    end\n  end\n\n  # rubocop:disable Naming/MemoizedInstanceVariableName\n  def hashed_elements\n    @hashed ||= @elements.each_with_object({}) { |e, memo| memo[e.name] = e; }\n  end\n  # rubocop:enable Naming/MemoizedInstanceVariableName\n\n  def hash\n    @elements.hash\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    if self == DEFAULT\n      PIterableType.new(PHashType::DEFAULT_KEY_PAIR_TUPLE)\n    else\n      PIterableType.new(\n        PTupleType.new([\n                         PVariantType.maybe_create(@elements.map(&:key_type)),\n                         PVariantType.maybe_create(@elements.map(&:value_type))\n                       ],\n                       PHashType::KEY_PAIR_TUPLE_SIZE)\n      )\n    end\n  end\n\n  def resolve(loader)\n    changed = false\n    relements = @elements.map do |elem|\n      relem = elem.resolve(loader)\n      changed ||= !relem.equal?(elem)\n      relem\n    end\n    changed ? self.class.new(relements) : self\n  end\n\n  def eql?(o)\n    self.class == o.class && @elements == o.elements\n  end\n\n  def elements\n    @elements\n  end\n\n  def instance?(o, guard = nil)\n    # The inferred type of a class derived from Hash is either Runtime or Object. It's not assignable to the Struct type.\n    return false unless o.instance_of?(Hash)\n\n    matched = 0\n    @elements.all? do |e|\n      key = e.name\n      v = o[key]\n      if v.nil? && !o.include?(key)\n        # Entry is missing. Only OK when key is optional\n        e.key_type.assignable?(PUndefType::DEFAULT, guard)\n      else\n        matched += 1\n        e.value_type.instance?(v, guard)\n      end\n    end && matched == o.size\n  end\n\n  def new_function\n    # Simply delegate to Hash type and let the higher level assertion deal with\n    # compliance with the Struct type regarding the produced result.\n    PHashType.new_function(self)\n  end\n\n  DEFAULT = PStructType.new(EMPTY_ARRAY)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    case o\n    when Types::PStructType\n      h2 = o.hashed_elements\n      matched = 0\n      elements.all? do |e1|\n        e2 = h2[e1.name]\n        if e2.nil?\n          e1.key_type.assignable?(PUndefType::DEFAULT, guard)\n        else\n          matched += 1\n          e1.key_type.assignable?(e2.key_type, guard) && e1.value_type.assignable?(e2.value_type, guard)\n        end\n      end && matched == h2.size\n    when Types::PHashType\n      required = 0\n      required_elements_assignable = elements.all? do |e|\n        key_type = e.key_type\n        if key_type.assignable?(PUndefType::DEFAULT)\n          # Element is optional so Hash does not need to provide it\n          true\n        else\n          required += 1\n          if e.value_type.assignable?(o.value_type, guard)\n            # Hash must have something that is assignable. We don't care about the name or size of the key though\n            # because we have no instance of a hash to compare against.\n            key_type.generalize.assignable?(o.key_type)\n          else\n            false\n          end\n        end\n      end\n      if required_elements_assignable\n        size_o = o.size_type || PCollectionType::DEFAULT_SIZE\n        PIntegerType.new(required, elements.size).assignable?(size_o, guard)\n      else\n        false\n      end\n    else\n      false\n    end\n  end\nend\n\n# @api public\n#\nclass PTupleType < PAnyType\n  include Enumerable\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'types' => PArrayType.new(PTypeType::DEFAULT),\n                 'size_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType.new(PIntegerType::DEFAULT)),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  # If set, describes min and max required of the given types - if max > size of\n  # types, the last type entry repeats\n  #\n  attr_reader :size_type\n\n  attr_reader :types\n\n  def accept(visitor, guard)\n    super\n    @size_type.accept(visitor, guard) unless @size_type.nil?\n    @types.each { |elem| elem.accept(visitor, guard) }\n  end\n\n  # @api private\n  def callable_args?(callable_t, guard)\n    unless size_type.nil?\n      raise ArgumentError, 'Callable tuple may not have a size constraint when used as args'\n    end\n\n    params_tuple = callable_t.param_types\n    param_block_t = callable_t.block_type\n    arg_types = @types\n    arg_block_t = arg_types.last\n    if arg_block_t.kind_of_callable?(true, guard)\n      # Can't pass a block to a callable that doesn't accept one\n      return false if param_block_t.nil?\n\n      # Check that the block is of the right tyṕe\n      return false unless param_block_t.assignable?(arg_block_t, guard)\n\n      # Check other arguments\n      arg_count = arg_types.size - 1\n      params_size_t = params_tuple.size_type || PIntegerType.new(*params_tuple.size_range)\n      return false unless params_size_t.assignable?(PIntegerType.new(arg_count, arg_count), guard)\n\n      ctypes = params_tuple.types\n      arg_count.times do |index|\n        return false unless (ctypes[index] || ctypes[-1]).assignable?(arg_types[index], guard)\n      end\n      return true\n    end\n\n    # Check that tuple is assignable and that the block (if declared) is optional\n    params_tuple.assignable?(self, guard) && (param_block_t.nil? || param_block_t.assignable?(PUndefType::DEFAULT, guard))\n  end\n\n  def initialize(types, size_type = nil)\n    @types = types\n    @size_type = size_type.nil? ? nil : size_type.to_size\n  end\n\n  # Returns Enumerator for the types if no block is given, otherwise, calls the given\n  # block with each of the types in this tuple\n  def each\n    if block_given?\n      types.each { |x| yield x }\n    else\n      types.to_enum\n    end\n  end\n\n  def generalize\n    if self == DEFAULT\n      DEFAULT\n    else\n      alter_type_array(@types, :generalize) { |altered_types| PTupleType.new(altered_types, @size_type) }\n    end\n  end\n\n  def normalize(guard = nil)\n    if self == DEFAULT\n      DEFAULT\n    else\n      alter_type_array(@types, :normalize, guard) { |altered_types| PTupleType.new(altered_types, @size_type) }\n    end\n  end\n\n  def resolve(loader)\n    changed = false\n    rtypes = @types.map do |type|\n      rtype = type.resolve(loader)\n      changed ||= !rtype.equal?(type)\n      rtype\n    end\n    changed ? self.class.new(rtypes, @size_type) : self\n  end\n\n  def instance?(o, guard = nil)\n    # The inferred type of a class derived from Array is either Runtime or Object. It's not assignable to the Tuple type.\n    return false unless o.instance_of?(Array)\n\n    if @size_type\n      return false unless @size_type.instance?(o.size, guard)\n    else\n      return false unless @types.empty? || @types.size == o.size\n    end\n    index = -1\n    @types.empty? || o.all? do |element|\n      @types.fetch(index += 1) { @types.last }.instance?(element, guard)\n    end\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    PIterableType.new(PVariantType.maybe_create(types))\n  end\n\n  # Returns the number of elements accepted [min, max] in the tuple\n  def size_range\n    if @size_type.nil?\n      types_size = @types.size\n      types_size == 0 ? [0, Float::INFINITY] : [types_size, types_size]\n    else\n      @size_type.range\n    end\n  end\n\n  # Returns the number of accepted occurrences [min, max] of the last type in the tuple\n  # The defaults is [1,1]\n  #\n  def repeat_last_range\n    if @size_type.nil?\n      return [1, 1]\n    end\n\n    types_size = @types.size\n    from, to = @size_type.range\n    min = from - (types_size - 1)\n    min = min <= 0 ? 0 : min\n    max = to - (types_size - 1)\n    [min, max]\n  end\n\n  def hash\n    @size_type.hash ^ @types.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @types == o.types && @size_type == o.size_type\n  end\n\n  def new_function\n    # Simply delegate to Array type and let the higher level assertion deal with\n    # compliance with the Tuple type regarding the produced result.\n    PArrayType.new_function(self)\n  end\n\n  DEFAULT = PTupleType.new(EMPTY_ARRAY)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    return true if self == o\n    return false unless o.is_a?(PTupleType) || o.is_a?(PArrayType)\n\n    s_types = types\n    size_s = size_type || PIntegerType.new(*size_range)\n\n    if o.is_a?(PTupleType)\n      size_o = o.size_type || PIntegerType.new(*o.size_range)\n      return false unless size_s.assignable?(size_o, guard)\n\n      unless s_types.empty?\n        o_types = o.types\n        return size_s.numeric_from == 0 if o_types.empty?\n\n        o_types.size.times do |index|\n          return false unless (s_types[index] || s_types[-1]).assignable?(o_types[index], guard)\n        end\n      end\n    else\n      size_o = o.size_type || PCollectionType::DEFAULT_SIZE\n      return false unless size_s.assignable?(size_o, guard)\n\n      unless s_types.empty?\n        o_entry = o.element_type\n        # Array of anything can not be assigned (unless tuple is tuple of anything) - this case\n        # was handled at the top of this method.\n        #\n        return false if o_entry.nil?\n\n        [s_types.size, size_o.range[1]].min.times { |index| return false unless (s_types[index] || s_types[-1]).assignable?(o_entry, guard) }\n      end\n    end\n    true\n  end\nend\n\n# @api public\n#\nclass PCallableType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'param_types' => {\n                   KEY_TYPE => POptionalType.new(PTypeType.new(PTupleType::DEFAULT)),\n                   KEY_VALUE => nil\n                 },\n                 'block_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType.new(PCallableType::DEFAULT)),\n                   KEY_VALUE => nil\n                 },\n                 'return_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => PAnyType::DEFAULT\n                 })\n  end\n\n  # @return [PAnyType] The type for the values returned by this callable. Returns `nil` if return value is unconstrained\n  attr_reader :return_type\n\n  # Types of parameters as a Tuple with required/optional count, or an Integer with min (required), max count\n  # @return [PTupleType] the tuple representing the parameter types\n  attr_reader :param_types\n\n  # Although being an abstract type reference, only Callable, or all Callables wrapped in\n  # Optional or Variant are supported\n  # If not set, the meaning is that block is not supported.\n  # @return [PAnyType|nil] the block type\n  attr_reader :block_type\n\n  # @param param_types [PTupleType]\n  # @param block_type [PAnyType]\n  # @param return_type [PAnyType]\n  def initialize(param_types, block_type = nil, return_type = nil)\n    @param_types = param_types\n    @block_type = block_type\n    @return_type = return_type == PAnyType::DEFAULT ? nil : return_type\n  end\n\n  def accept(visitor, guard)\n    super\n    @param_types.accept(visitor, guard) unless @param_types.nil?\n    @block_type.accept(visitor, guard) unless @block_type.nil?\n    @return_type.accept(visitor, guard) unless @return_type.nil?\n  end\n\n  def generalize\n    if self == DEFAULT\n      DEFAULT\n    else\n      params_t = @param_types.nil? ? nil : @param_types.generalize\n      block_t = @block_type.nil? ? nil : @block_type.generalize\n      return_t = @return_type.nil? ? nil : @return_type.generalize\n      @param_types.equal?(params_t) && @block_type.equal?(block_t) && @return_type.equal?(return_t) ? self : PCallableType.new(params_t, block_t, return_t)\n    end\n  end\n\n  def normalize(guard = nil)\n    if self == DEFAULT\n      DEFAULT\n    else\n      params_t = @param_types.nil? ? nil : @param_types.normalize(guard)\n      block_t = @block_type.nil? ? nil : @block_type.normalize(guard)\n      return_t = @return_type.nil? ? nil : @return_type.normalize(guard)\n      @param_types.equal?(params_t) && @block_type.equal?(block_t) && @return_type.equal?(return_t) ? self : PCallableType.new(params_t, block_t, return_t)\n    end\n  end\n\n  def instance?(o, guard = nil)\n    (o.is_a?(Proc) || o.is_a?(Evaluator::Closure) || o.is_a?(Functions::Function)) && assignable?(TypeCalculator.infer(o), guard)\n  end\n\n  # Returns `true` if this instance is a callable that accepts the given _args_\n  #\n  # @param args [Array] the arguments to test\n  # @return [Boolean] `true` if this instance is a callable that accepts the given _args_\n  def callable_with?(args, block = nil)\n    # nil param_types and compatible return type means other Callable is assignable\n    return true if @param_types.nil?\n    return false unless @param_types.instance?(args)\n\n    if @block_type.nil?\n      block.nil?\n    else\n      @block_type.instance?(block)\n    end\n  end\n\n  # @api private\n  def callable_args?(required_callable_t, guard)\n    # If the required callable is equal or more specific than self, self is acceptable arguments\n    required_callable_t.assignable?(self, guard)\n  end\n\n  def kind_of_callable?(optional = true, guard = nil)\n    true\n  end\n\n  # Returns the number of accepted arguments [min, max]\n  def size_range\n    @param_types.nil? ? nil : @param_types.size_range\n  end\n\n  # Returns the number of accepted arguments for the last parameter type [min, max]\n  #\n  def last_range\n    @param_types.nil? ? nil : @param_types.repeat_last_range\n  end\n\n  # Range [0,0], [0,1], or [1,1] for the block\n  #\n  def block_range\n    case block_type\n    when POptionalType\n      [0, 1]\n    when PVariantType, PCallableType\n      [1, 1]\n    else\n      [0, 0]\n    end\n  end\n\n  def hash\n    [@param_types, @block_type, @return_type].hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @param_types == o.param_types && @block_type == o.block_type && @return_type == o.return_type\n  end\n\n  def resolve(loader)\n    params_t = @param_types.nil? ? nil : @param_types.resolve(loader)\n    block_t = @block_type.nil? ? nil : @block_type.resolve(loader)\n    return_t = @return_type.nil? ? nil : @return_type.resolve(loader)\n    @param_types.equal?(params_t) && @block_type.equal?(block_t) && @return_type.equal?(return_t) ? self : self.class.new(params_t, block_t, return_t)\n  end\n\n  DEFAULT = PCallableType.new(nil, nil, nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    return false unless o.is_a?(PCallableType)\n    return false unless @return_type.nil? || @return_type.assignable?(o.return_type || PAnyType::DEFAULT, guard)\n\n    # nil param_types and compatible return type means other Callable is assignable\n    return true if @param_types.nil?\n\n    # NOTE: these tests are made in reverse as it is calling the callable that is constrained\n    # (it's lower bound), not its upper bound\n    other_param_types = o.param_types\n\n    return false if other_param_types.nil? || !other_param_types.assignable?(@param_types, guard)\n\n    # names are ignored, they are just information\n    # Blocks must be compatible\n    this_block_t = @block_type || PUndefType::DEFAULT\n    that_block_t = o.block_type || PUndefType::DEFAULT\n    that_block_t.assignable?(this_block_t, guard)\n  end\nend\n\n# @api public\n#\nclass PArrayType < PCollectionType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'CollectionType',\n                 'element_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => PAnyType::DEFAULT\n                 })\n  end\n\n  attr_reader :element_type\n\n  def initialize(element_type, size_type = nil)\n    super(size_type)\n    if !size_type.nil? && size_type.from == 0 && size_type.to == 0\n      @element_type = PUnitType::DEFAULT\n    else\n      @element_type = element_type.nil? ? PAnyType::DEFAULT : element_type\n    end\n  end\n\n  def accept(visitor, guard)\n    super\n    @element_type.accept(visitor, guard)\n  end\n\n  # @api private\n  def callable_args?(callable, guard = nil)\n    param_t = callable.param_types\n    block_t = callable.block_type\n    # does not support calling with a block, but have to check that callable is ok with missing block\n    (param_t.nil? || param_t.assignable?(self, guard)) && (block_t.nil? || block_t.assignable?(PUndefType::DEFAULT, guard))\n  end\n\n  def generalize\n    if PAnyType::DEFAULT.eql?(@element_type)\n      DEFAULT\n    else\n      ge_type = @element_type.generalize\n      @size_type.nil? && @element_type.equal?(ge_type) ? self : self.class.new(ge_type, nil)\n    end\n  end\n\n  def eql?(o)\n    super && @element_type == o.element_type\n  end\n\n  def hash\n    super ^ @element_type.hash\n  end\n\n  def normalize(guard = nil)\n    if PAnyType::DEFAULT.eql?(@element_type)\n      DEFAULT\n    else\n      ne_type = @element_type.normalize(guard)\n      @element_type.equal?(ne_type) ? self : self.class.new(ne_type, @size_type)\n    end\n  end\n\n  def resolve(loader)\n    relement_type = @element_type.resolve(loader)\n    relement_type.equal?(@element_type) ? self : self.class.new(relement_type, @size_type)\n  end\n\n  def instance?(o, guard = nil)\n    # The inferred type of a class derived from Array is either Runtime or Object. It's not assignable to the Array type.\n    return false unless o.instance_of?(Array)\n    return false unless o.all? { |element| @element_type.instance?(element, guard) }\n\n    size_t = size_type\n    size_t.nil? || size_t.instance?(o.size, guard)\n  end\n\n  def iterable_type(guard = nil)\n    PAnyType::DEFAULT.eql?(@element_type) ? PIterableType::DEFAULT : PIterableType.new(@element_type)\n  end\n\n  # Returns a new function that produces an Array\n  #\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_array, type.loader) do\n      dispatch :to_array do\n        param           'Variant[Array,Hash,Binary,Iterable]', :from\n        optional_param  'Boolean[false]', :wrap\n      end\n\n      dispatch :wrapped do\n        param  'Any',           :from\n        param  'Boolean[true]', :wrap\n      end\n\n      argument_mismatch :on_error do\n        param  'Any',             :from\n        optional_param 'Boolean', :wrap\n      end\n\n      def wrapped(from, _)\n        from.is_a?(Array) ? from : [from]\n      end\n\n      def to_array(from, _ = false)\n        case from\n        when Array\n          from\n        when Hash\n          from.to_a\n        when PBinaryType::Binary\n          # For older rubies, the #bytes method returns an Enumerator that must be rolled out\n          from.binary_buffer.bytes.to_a\n        else\n          Iterable.on(from).to_a\n        end\n      end\n\n      def on_error(from, _ = false)\n        t = TypeCalculator.singleton.infer(from).generalize\n        _(\"Value of type %{type} cannot be converted to Array\") % { type: t }\n      end\n    end\n  end\n\n  DEFAULT = PArrayType.new(nil)\n  EMPTY = PArrayType.new(PUnitType::DEFAULT, ZERO_SIZE)\n\n  protected\n\n  # Array is assignable if o is an Array and o's element type is assignable, or if o is a Tuple\n  # @api private\n  def _assignable?(o, guard)\n    case o\n    when PTupleType\n      o_types = o.types\n      size_s = size_type || DEFAULT_SIZE\n      size_o = o.size_type\n      if size_o.nil?\n        type_count = o_types.size\n        size_o = PIntegerType.new(type_count, type_count)\n      end\n      size_s.assignable?(size_o) && o_types.all? { |ot| @element_type.assignable?(ot, guard) }\n    when PArrayType\n      super && @element_type.assignable?(o.element_type, guard)\n    else\n      false\n    end\n  end\nend\n\n# @api public\n#\nclass PHashType < PCollectionType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'CollectionType',\n                 'key_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => PAnyType::DEFAULT\n                 },\n                 'value_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => PAnyType::DEFAULT\n                 })\n  end\n\n  attr_accessor :key_type, :value_type\n\n  def initialize(key_type, value_type, size_type = nil)\n    super(size_type)\n    if !size_type.nil? && size_type.from == 0 && size_type.to == 0\n      @key_type = PUnitType::DEFAULT\n      @value_type = PUnitType::DEFAULT\n    else\n      @key_type = key_type.nil? ? PAnyType::DEFAULT : key_type\n      @value_type = value_type.nil? ? PAnyType::DEFAULT : value_type\n    end\n  end\n\n  def accept(visitor, guard)\n    super\n    @key_type.accept(visitor, guard)\n    @value_type.accept(visitor, guard)\n  end\n\n  def element_type\n    if Puppet[:strict] != :off\n      # TRANSLATOR 'Puppet::Pops::Types::PHashType#element_type' and '#value_type' are class and method names and should not be translated\n      Puppet.warn_once('deprecations', 'Puppet::Pops::Types::PHashType#element_type',\n                       _('Puppet::Pops::Types::PHashType#element_type is deprecated, use #value_type instead'))\n    end\n    @value_type\n  end\n\n  def generalize\n    if self == DEFAULT || self == EMPTY\n      self\n    else\n      key_t = @key_type\n      key_t = key_t.generalize\n      value_t = @value_type\n      value_t = value_t.generalize\n      @size_type.nil? && @key_type.equal?(key_t) && @value_type.equal?(value_t) ? self : PHashType.new(key_t, value_t, nil)\n    end\n  end\n\n  def normalize(guard = nil)\n    if self == DEFAULT || self == EMPTY\n      self\n    else\n      key_t = @key_type.normalize(guard)\n      value_t = @value_type.normalize(guard)\n      @size_type.nil? && @key_type.equal?(key_t) && @value_type.equal?(value_t) ? self : PHashType.new(key_t, value_t, @size_type)\n    end\n  end\n\n  def hash\n    super ^ @key_type.hash ^ @value_type.hash\n  end\n\n  def instance?(o, guard = nil)\n    # The inferred type of a class derived from Hash is either Runtime or Object. It's not assignable to the Hash type.\n    return false unless o.instance_of?(Hash)\n\n    if o.keys.all? { |key| @key_type.instance?(key, guard) } && o.values.all? { |value| @value_type.instance?(value, guard) }\n      size_t = size_type\n      size_t.nil? || size_t.instance?(o.size, guard)\n    else\n      false\n    end\n  end\n\n  def iterable?(guard = nil)\n    true\n  end\n\n  def iterable_type(guard = nil)\n    if self == DEFAULT || self == EMPTY\n      PIterableType.new(DEFAULT_KEY_PAIR_TUPLE)\n    else\n      PIterableType.new(PTupleType.new([@key_type, @value_type], KEY_PAIR_TUPLE_SIZE))\n    end\n  end\n\n  def eql?(o)\n    super && @key_type == o.key_type && @value_type == o.value_type\n  end\n\n  def is_the_empty_hash?\n    self == EMPTY\n  end\n\n  def resolve(loader)\n    rkey_type = @key_type.resolve(loader)\n    rvalue_type = @value_type.resolve(loader)\n    rkey_type.equal?(@key_type) && rvalue_type.equal?(@value_type) ? self : self.class.new(rkey_type, rvalue_type, @size_type)\n  end\n\n  def self.array_as_hash(value)\n    return value unless value.is_a?(Array)\n\n    result = {}\n    value.each_with_index { |v, idx| result[idx] = array_as_hash(v) }\n    result\n  end\n\n  # Returns a new function that produces a  Hash\n  #\n  def self.new_function(type)\n    @new_function ||= Puppet::Functions.create_loaded_function(:new_hash, type.loader) do\n      local_types do\n        type 'KeyValueArray = Array[Tuple[Any,Any],1]'\n        type 'TreeArray = Array[Tuple[Array,Any],1]'\n        type 'NewHashOption = Enum[tree, hash_tree]'\n      end\n\n      dispatch :from_tree do\n        param           'TreeArray',       :from\n        optional_param  'NewHashOption',   :build_option\n      end\n\n      dispatch :from_tuples do\n        param           'KeyValueArray', :from\n      end\n\n      dispatch :from_array do\n        param           'Any', :from\n      end\n\n      def from_tuples(tuple_array)\n        tuple_array.to_h\n      end\n\n      def from_tree(tuple_array, build_option = nil)\n        if build_option.nil?\n          return from_tuples(tuple_array)\n        end\n\n        # only remaining possible options is 'tree' or 'hash_tree'\n\n        all_hashes = build_option == 'hash_tree'\n        result = {}\n        tuple_array.each do |entry|\n          path = entry[0]\n          value = entry[1]\n          if path.empty?\n            # root node (index [] was included - values merge into the result)\n            # An array must be changed to a hash first as this is the root\n            # (Cannot return an array from a Hash.new)\n            if value.is_a?(Array)\n              value.each_with_index { |v, idx| result[idx] = v }\n            else\n              result.merge!(value)\n            end\n          else\n            r = path[0..-2].reduce(result) { |memo, idx| (memo.is_a?(Array) || memo.has_key?(idx)) ? memo[idx] : memo[idx] = {} }\n            r[path[-1]] = (all_hashes ? PHashType.array_as_hash(value) : value)\n          end\n        end\n        result\n      end\n\n      def from_array(from)\n        case from\n        when Array\n          if from.size == 0\n            {}\n          else\n            unless from.size.even?\n              raise TypeConversionError, _('odd number of arguments for Hash')\n            end\n\n            Hash[*from]\n          end\n        when Hash\n          from\n        else\n          if PIterableType::DEFAULT.instance?(from)\n            Hash[*Iterable.on(from).to_a]\n          else\n            t = TypeCalculator.singleton.infer(from).generalize\n            raise TypeConversionError, _(\"Value of type %{type} cannot be converted to Hash\") % { type: t }\n          end\n        end\n      end\n    end\n  end\n\n  DEFAULT = PHashType.new(nil, nil)\n  KEY_PAIR_TUPLE_SIZE = PIntegerType.new(2, 2)\n  DEFAULT_KEY_PAIR_TUPLE = PTupleType.new([PUnitType::DEFAULT, PUnitType::DEFAULT], KEY_PAIR_TUPLE_SIZE)\n  EMPTY = PHashType.new(PUnitType::DEFAULT, PUnitType::DEFAULT, PIntegerType.new(0, 0))\n\n  protected\n\n  # Hash is assignable if o is a Hash and o's key and element types are assignable\n  # @api private\n  def _assignable?(o, guard)\n    case o\n    when PHashType\n      size_s = size_type\n      return true if (size_s.nil? || size_s.from == 0) && o.is_the_empty_hash?\n      return false unless @key_type.assignable?(o.key_type, guard) && @value_type.assignable?(o.value_type, guard)\n\n      super\n    when PStructType\n      # hash must accept String as key type\n      # hash must accept all value types\n      # hash must accept the size of the struct\n      o_elements = o.elements\n      (size_type || DEFAULT_SIZE).instance?(o_elements.size, guard) &&\n        o_elements.all? { |e| @key_type.instance?(e.name, guard) && @value_type.assignable?(e.value_type, guard) }\n    else\n      false\n    end\n  end\nend\n\n# A flexible type describing an any? of other types\n# @api public\n#\nclass PVariantType < PAnyType\n  include Enumerable\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType', 'types' => PArrayType.new(PTypeType::DEFAULT))\n  end\n\n  attr_reader :types\n\n  # Checks if the number of unique types in the given array is greater than one, and if so\n  # creates a Variant with those types and returns it. If only one unique type is found,\n  # that type is instead returned.\n  #\n  # @param types [Array<PAnyType>] the variants\n  # @return [PAnyType] the resulting type\n  # @api public\n  def self.maybe_create(types)\n    types = flatten_variants(types).uniq\n    types.size == 1 ? types[0] : new(types)\n  end\n\n  # @param types [Array[PAnyType]] the variants\n  def initialize(types)\n    @types = types.freeze\n  end\n\n  def accept(visitor, guard)\n    super\n    @types.each { |t| t.accept(visitor, guard) }\n  end\n\n  def each\n    if block_given?\n      types.each { |t| yield t }\n    else\n      types.to_enum\n    end\n  end\n\n  def generalize\n    if self == DEFAULT\n      self\n    else\n      alter_type_array(@types, :generalize) { |altered| PVariantType.maybe_create(altered) }\n    end\n  end\n\n  def normalize(guard = nil)\n    if self == DEFAULT || @types.empty?\n      self\n    else\n      # Normalize all contained types\n      modified = false\n      types = alter_type_array(@types, :normalize, guard)\n      if types == self\n        types = @types\n      else\n        modified = true\n      end\n\n      if types.size == 1\n        types[0]\n      elsif types.any? { |t| t.is_a?(PUndefType) || t.is_a?(POptionalType) }\n        # Undef entry present. Use an OptionalType with a normalized Variant without Undefs and Optional wrappers\n        POptionalType.new(PVariantType.maybe_create(types.reject { |t| t.is_a?(PUndefType) }.map { |t| t.is_a?(POptionalType) ? t.type : t })).normalize\n      else\n        # Merge all variants into this one\n        types = PVariantType.flatten_variants(types)\n        size_before_merge = types.size\n\n        types = swap_not_undefs(types)\n        types = merge_enums(types)\n        types = merge_patterns(types)\n        types = merge_version_ranges(types)\n        types = merge_numbers(PIntegerType, types)\n        types = merge_numbers(PFloatType, types)\n        types = merge_numbers(PTimespanType, types)\n        types = merge_numbers(PTimestampType, types)\n\n        if types.size == 1\n          types[0]\n        else\n          modified || types.size != size_before_merge ? PVariantType.maybe_create(types) : self\n        end\n      end\n    end\n  end\n\n  def self.flatten_variants(types)\n    modified = false\n    types = types.map do |t|\n      if t.is_a?(PVariantType)\n        modified = true\n        t.types\n      else\n        t\n      end\n    end\n    types.flatten! if modified\n    types\n  end\n\n  def hash\n    @types.hash\n  end\n\n  def instance?(o, guard = nil)\n    # instance of variant if o is instance? of any of variant's types\n    @types.any? { |type| type.instance?(o, guard) }\n  end\n\n  def really_instance?(o, guard = nil)\n    @types.reduce(-1) do |memo, type|\n      ri = type.really_instance?(o, guard)\n      break ri if ri > 0\n\n      ri > memo ? ri : memo\n    end\n  end\n\n  def kind_of_callable?(optional = true, guard = nil)\n    @types.all? { |type| type.kind_of_callable?(optional, guard) }\n  end\n\n  def eql?(o)\n    self.class == o.class && @types.size == o.types.size && (@types - o.types).empty?\n  end\n\n  DEFAULT = PVariantType.new(EMPTY_ARRAY)\n\n  def assignable?(o, guard = nil)\n    # an empty Variant does not match Undef (it is void - not even undef)\n    if o.is_a?(PUndefType) && types.empty?\n      return false\n    end\n\n    return super unless o.is_a?(PVariantType)\n    # If empty, all Variant types match irrespective of the types they hold (including being empty)\n    return true if types.empty?\n\n    # Since this variant is not empty, an empty Variant cannot match, because it matches nothing\n    # otherwise all types in o must be assignable to this\n    !o.types.empty? && o.types.all? { |vt| super(vt, guard) }\n  end\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    # A variant is assignable if o is assignable to any of its types\n    types.any? { |option_t| option_t.assignable?(o, guard) }\n  end\n\n  # @api private\n  def swap_not_undefs(array)\n    if array.size > 1\n      parts = array.partition { |t| t.is_a?(PNotUndefType) }\n      not_undefs = parts[0]\n      if not_undefs.size > 1\n        others = parts[1]\n        others << PNotUndefType.new(PVariantType.maybe_create(not_undefs.map(&:type)).normalize)\n        array = others\n      end\n    end\n    array\n  end\n\n  # @api private\n  def merge_enums(array)\n    # Merge case sensitive enums and strings\n    if array.size > 1\n      parts = array.partition { |t| t.is_a?(PEnumType) && !t.values.empty? && !t.case_insensitive? || t.is_a?(PStringType) && !t.value.nil? }\n      enums = parts[0]\n      if enums.size > 1\n        others = parts[1]\n        others << PEnumType.new(enums.map { |enum| enum.is_a?(PStringType) ? enum.value : enum.values }.flatten.uniq)\n        array = others\n      end\n    end\n\n    # Merge case insensitive enums\n    if array.size > 1\n      parts = array.partition { |t| t.is_a?(PEnumType) && !t.values.empty? && t.case_insensitive? }\n      enums = parts[0]\n      if enums.size > 1\n        others = parts[1]\n        values = []\n        enums.each { |enum| enum.values.each { |value| values << value.downcase } }\n        values.uniq!\n        others << PEnumType.new(values, true)\n        array = others\n      end\n    end\n    array\n  end\n\n  # @api private\n  def merge_patterns(array)\n    if array.size > 1\n      parts = array.partition { |t| t.is_a?(PPatternType) }\n      patterns = parts[0]\n      if patterns.size > 1\n        others = parts[1]\n        others << PPatternType.new(patterns.map(&:patterns).flatten.uniq)\n        array = others\n      end\n    end\n    array\n  end\n\n  # @api private\n  def merge_numbers(clazz, array)\n    if array.size > 1\n      parts = array.partition { |t| t.is_a?(clazz) }\n      ranges = parts[0]\n      array = merge_ranges(ranges) + parts[1] if ranges.size > 1\n    end\n    array\n  end\n\n  def merge_version_ranges(array)\n    if array.size > 1\n      parts = array.partition { |t| t.is_a?(PSemVerType) }\n      ranges = parts[0]\n      array = [PSemVerType.new(ranges.map(&:ranges).flatten)] + parts[1] if ranges.size > 1\n    end\n    array\n  end\n\n  # @api private\n  def merge_ranges(ranges)\n    result = []\n    until ranges.empty?\n      unmerged = []\n      x = ranges.pop\n      result << ranges.inject(x) do |memo, y|\n        merged = memo.merge(y)\n        if merged.nil?\n          unmerged << y\n        else\n          memo = merged\n        end\n        memo\n      end\n      ranges = unmerged\n    end\n    result\n  end\nend\n\n# Abstract representation of a type that can be placed in a Catalog.\n# @api public\n#\nclass PCatalogEntryType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType')\n  end\n\n  DEFAULT = PCatalogEntryType.new\n\n  def instance?(o, guard = nil)\n    assignable?(TypeCalculator.infer(o), guard)\n  end\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PCatalogEntryType)\n  end\nend\n\n# Represents a (host-) class in the Puppet Language.\n# @api public\n#\nclass PClassType < PCatalogEntryType\n  attr_reader :class_name\n\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'CatalogEntryType',\n                 'class_name' => {\n                   KEY_TYPE => POptionalType.new(PStringType::NON_EMPTY),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  def initialize(class_name)\n    @class_name = class_name\n  end\n\n  def hash\n    11 ^ @class_name.hash\n  end\n\n  def eql?(o)\n    self.class == o.class && @class_name == o.class_name\n  end\n\n  DEFAULT = PClassType.new(nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    return false unless o.is_a?(PClassType)\n    # Class = Class[name}, Class[name] != Class\n    return true if @class_name.nil?\n\n    # Class[name] = Class[name]\n    @class_name == o.class_name\n  end\nend\n\n# For backward compatibility\nPHostClassType = PClassType\n\n# Represents a Resource Type in the Puppet Language\n# @api public\n#\nclass PResourceType < PCatalogEntryType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'CatalogEntryType',\n                 'type_name' => {\n                   KEY_TYPE => POptionalType.new(PStringType::NON_EMPTY),\n                   KEY_VALUE => nil\n                 },\n                 'title' => {\n                   KEY_TYPE => POptionalType.new(PStringType::NON_EMPTY),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  attr_reader :type_name, :title, :downcased_name\n\n  def initialize(type_name, title = nil)\n    @type_name = type_name.freeze\n    @title = title.freeze\n    @downcased_name = type_name.nil? ? nil : @type_name.downcase.freeze\n  end\n\n  def eql?(o)\n    self.class == o.class && @downcased_name == o.downcased_name && @title == o.title\n  end\n\n  def hash\n    @downcased_name.hash ^ @title.hash\n  end\n\n  DEFAULT = PResourceType.new(nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    o.is_a?(PResourceType) && (@downcased_name.nil? || @downcased_name == o.downcased_name && (@title.nil? || @title == o.title))\n  end\nend\n\n# Represents a type that accept PUndefType instead of the type parameter\n# required_type - is a short hand for Variant[T, Undef]\n# @api public\n#\nclass POptionalType < PTypeWithContainedType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  def optional_type\n    @type\n  end\n\n  def kind_of_callable?(optional = true, guard = nil)\n    optional && !@type.nil? && @type.kind_of_callable?(optional, guard)\n  end\n\n  def instance?(o, guard = nil)\n    PUndefType::DEFAULT.instance?(o, guard) || (!@type.nil? && @type.instance?(o, guard))\n  end\n\n  def normalize(guard = nil)\n    n = super\n    if n.type.nil?\n      n\n    elsif n.type.is_a?(PNotUndefType)\n      POptionalType.new(n.type.type).normalize\n    # No point in having an NotUndef in an Optional\n    elsif n.type.assignable?(PUndefType::DEFAULT)\n      # THe type is Optional anyway, so it can be stripped of\n      n.type\n    else\n      n\n    end\n  end\n\n  def new_function\n    optional_type.new_function\n  end\n\n  DEFAULT = POptionalType.new(nil)\n\n  protected\n\n  # @api private\n  def _assignable?(o, guard)\n    return true if o.is_a?(PUndefType)\n    return true if @type.nil?\n\n    if o.is_a?(POptionalType)\n      @type.assignable?(o.optional_type, guard)\n    else\n      @type.assignable?(o, guard)\n    end\n  end\nend\n\nclass PTypeReferenceType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType', 'type_string' => PStringType::NON_EMPTY)\n  end\n\n  attr_reader :type_string\n\n  def initialize(type_string)\n    @type_string = type_string\n  end\n\n  def callable?(args)\n    false\n  end\n\n  def instance?(o, guard = nil)\n    false\n  end\n\n  def hash\n    @type_string.hash\n  end\n\n  def eql?(o)\n    super && o.type_string == @type_string\n  end\n\n  def resolve(loader)\n    TypeParser.singleton.parse(@type_string, loader)\n  end\n\n  protected\n\n  def _assignable?(o, guard)\n    # A type must be assignable to itself or a lot of unit tests will break\n    o == self\n  end\n\n  DEFAULT = PTypeReferenceType.new('UnresolvedReference')\nend\n\n# Describes a named alias for another Type.\n# The alias is created with a name and an unresolved type expression. The type expression may\n# in turn contain other aliases (including the alias that contains it) which means that an alias\n# might contain self recursion. Whether or not that is the case is computed and remembered when the alias\n# is resolved since guarding against self recursive constructs is relatively expensive.\n#\n# @api public\nclass PTypeAliasType < PAnyType\n  def self.register_ptype(loader, ir)\n    create_ptype(loader, ir, 'AnyType',\n                 'name' => PStringType::NON_EMPTY,\n                 'type_expr' => PAnyType::DEFAULT,\n                 'resolved_type' => {\n                   KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),\n                   KEY_VALUE => nil\n                 })\n  end\n\n  attr_reader :loader, :name\n\n  # @param name [String] The name of the type\n  # @param type_expr [Model::PopsObject] The expression that describes the aliased type\n  # @param resolved_type [PAnyType] the resolve type (only used for the DEFAULT initialization)\n  def initialize(name, type_expr, resolved_type = nil)\n    @name = name\n    @type_expr = type_expr\n    @resolved_type = resolved_type\n    @self_recursion = false\n  end\n\n  def assignable?(o, guard = nil)\n    if @self_recursion\n      guard ||= RecursionGuard.new\n      guard.with_this(self) { |state| state == RecursionGuard::SELF_RECURSION_IN_BOTH ? true : super(o, guard) }\n    else\n      super(o, guard)\n    end\n  end\n\n  # Returns the resolved type. The type must have been resolved by a call prior to calls to this\n  # method or an error will be raised.\n  #\n  # @return [PAnyType] The resolved type of this alias.\n  # @raise [Puppet::Error] unless the type has been resolved prior to calling this method\n  def resolved_type\n    raise Puppet::Error, \"Reference to unresolved type #{@name}\" unless @resolved_type\n\n    @resolved_type\n  end\n\n  def callable_args?(callable, guard)\n    guarded_recursion(guard, false) { |g| resolved_type.callable_args?(callable, g) }\n  end\n\n  def check_self_recursion(originator)\n    resolved_type.check_self_recursion(originator) unless originator.equal?(self)\n  end\n\n  def kind_of_callable?(optional = true, guard = nil)\n    guarded_recursion(guard, false) { |g| resolved_type.kind_of_callable?(optional, g) }\n  end\n\n  def instance?(o, guard = nil)\n    really_instance?(o, guard) == 1\n  end\n\n  def iterable?(guard = nil)\n    guarded_recursion(guard, false) { |g| resolved_type.iterable?(g) }\n  end\n\n  def iterable_type(guard = nil)\n    guarded_recursion(guard, nil) { |g| resolved_type.iterable_type(g) }\n  end\n\n  def hash\n    @name.hash\n  end\n\n  # Acceptor used when checking for self recursion and that a type contains\n  # something other than aliases or type references\n  #\n  # @api private\n  class AssertOtherTypeAcceptor\n    def initialize\n      @other_type_detected = false\n    end\n\n    def visit(type, _)\n      unless type.is_a?(PTypeAliasType) || type.is_a?(PVariantType)\n        @other_type_detected = true\n      end\n    end\n\n    def other_type_detected?\n      @other_type_detected\n    end\n  end\n\n  # Acceptor used when re-checking for self recursion after a self recursion has been detected\n  #\n  # @api private\n  class AssertSelfRecursionStatusAcceptor\n    def visit(type, _)\n      type.set_self_recursion_status if type.is_a?(PTypeAliasType)\n    end\n  end\n\n  def set_self_recursion_status\n    return if @self_recursion || @resolved_type.is_a?(PTypeReferenceType)\n\n    @self_recursion = true\n    guard = RecursionGuard.new\n    accept(NoopTypeAcceptor::INSTANCE, guard)\n    @self_recursion = guard.recursive_this?(self)\n    when_self_recursion_detected if @self_recursion # no difference\n  end\n\n  # Called from the TypeParser once it has found a type using the Loader. The TypeParser will\n  # interpret the contained expression and the resolved type is remembered. This method also\n  # checks and remembers if the resolve type contains self recursion.\n  #\n  # @param type_parser [TypeParser] type parser that will interpret the type expression\n  # @param loader [Loader::Loader] loader to use when loading type aliases\n  # @return [PTypeAliasType] the receiver of the call, i.e. `self`\n  # @api private\n  def resolve(loader)\n    @loader = loader\n    if @resolved_type.nil?\n      # resolved to PTypeReferenceType::DEFAULT during resolve to avoid endless recursion\n      @resolved_type = PTypeReferenceType::DEFAULT\n      @self_recursion = true # assumed while it being found out below\n      begin\n        if @type_expr.is_a?(PTypeReferenceType)\n          @resolved_type = @type_expr.resolve(loader)\n        else\n          @resolved_type = TypeParser.singleton.interpret(@type_expr, loader).normalize\n        end\n\n        # Find out if this type is recursive. A recursive type has performance implications\n        # on several methods and this knowledge is used to avoid that for non-recursive\n        # types.\n        guard = RecursionGuard.new\n        real_type_asserter = AssertOtherTypeAcceptor.new\n        accept(real_type_asserter, guard)\n        unless real_type_asserter.other_type_detected?\n          raise ArgumentError, \"Type alias '#{name}' cannot be resolved to a real type\"\n        end\n\n        @self_recursion = guard.recursive_this?(self)\n        # All aliases involved must re-check status since this alias is now resolved\n        if @self_recursion\n          accept(AssertSelfRecursionStatusAcceptor.new, RecursionGuard.new)\n          when_self_recursion_detected\n        end\n      rescue\n        @resolved_type = nil\n        raise\n      end\n    else\n      # An alias may appoint an Object type that isn't resolved yet. The default type\n      # reference is used to prevent endless recursion and should not be resolved here.\n      @resolved_type.resolve(loader) unless @resolved_type.equal?(PTypeReferenceType::DEFAULT)\n    end\n    self\n  end\n\n  def eql?(o)\n    super && o.name == @name\n  end\n\n  def accept(visitor, guard)\n    guarded_recursion(guard, nil) do |g|\n      super(visitor, g)\n      @resolved_type.accept(visitor, g) unless @resolved_type.nil?\n    end\n  end\n\n  def self_recursion?\n    @self_recursion\n  end\n\n  # Returns the expanded string the form of the alias, e.g. <alias name> = <resolved type>\n  #\n  # @return [String] the expanded form of this alias\n  # @api public\n  def to_s\n    TypeFormatter.singleton.alias_expanded_string(self)\n  end\n\n  # Delegates to resolved type\n  def respond_to_missing?(name, include_private)\n    resolved_type.respond_to?(name, include_private)\n  end\n\n  # Delegates to resolved type\n  def method_missing(name, *arguments, &block)\n    super if @resolved_type.equal?(PTypeReferenceType::DEFAULT)\n    resolved_type.send(name, *arguments, &block)\n  end\n\n  # @api private\n  def really_instance?(o, guard = nil)\n    if @self_recursion\n      guard ||= RecursionGuard.new\n      guard.with_that(o) do\n        guard.with_this(self) { |state| state == RecursionGuard::SELF_RECURSION_IN_BOTH ? 0 : resolved_type.really_instance?(o, guard) }\n      end\n    else\n      resolved_type.really_instance?(o, guard)\n    end\n  end\n\n  # @return `nil` to prevent serialization of the type_expr used when first initializing this instance\n  # @api private\n  def type_expr\n    nil\n  end\n\n  protected\n\n  def _assignable?(o, guard)\n    resolved_type.assignable?(o, guard)\n  end\n\n  def new_function\n    resolved_type.new_function\n  end\n\n  private\n\n  def guarded_recursion(guard, dflt)\n    if @self_recursion\n      guard ||= RecursionGuard.new\n      guard.with_this(self) { |state| (state & RecursionGuard::SELF_RECURSION_IN_THIS) == 0 ? yield(guard) : dflt }\n    else\n      yield(guard)\n    end\n  end\n\n  def when_self_recursion_detected\n    if @resolved_type.is_a?(PVariantType)\n      # Drop variants that are not real types\n      resolved_types = @resolved_type.types\n      real_types = resolved_types.select do |type|\n        next false if type == self\n\n        real_type_asserter = AssertOtherTypeAcceptor.new\n        type.accept(real_type_asserter, RecursionGuard.new)\n        real_type_asserter.other_type_detected?\n      end\n      if real_types.size != resolved_types.size\n        if real_types.size == 1\n          @resolved_type = real_types[0]\n        else\n          @resolved_type = PVariantType.maybe_create(real_types)\n        end\n        # Drop self recursion status in case it's not self recursive anymore\n        guard = RecursionGuard.new\n        accept(NoopTypeAcceptor::INSTANCE, guard)\n        @self_recursion = guard.recursive_this?(self)\n      end\n    end\n    @resolved_type.check_self_recursion(self) if @self_recursion\n  end\n\n  DEFAULT = PTypeAliasType.new('UnresolvedAlias', nil, PTypeReferenceType::DEFAULT)\nend\nend\nend\n\nrequire_relative '../../../puppet/pops/pcore'\n\nrequire_relative 'annotatable'\nrequire_relative 'p_meta_type'\nrequire_relative 'p_object_type'\nrequire_relative 'annotation'\nrequire_relative 'ruby_method'\nrequire_relative 'p_runtime_type'\nrequire_relative 'p_sem_ver_type'\nrequire_relative 'p_sem_ver_range_type'\nrequire_relative 'p_sensitive_type'\nrequire_relative 'p_type_set_type'\nrequire_relative 'p_timespan_type'\nrequire_relative 'p_timestamp_type'\nrequire_relative 'p_binary_type'\nrequire_relative 'p_init_type'\nrequire_relative 'p_object_type_extension'\nrequire_relative 'p_uri_type'\nrequire_relative 'type_set_reference'\nrequire_relative 'implementation_registry'\nrequire_relative 'tree_iterators'\n"
  },
  {
    "path": "lib/puppet/pops/utils.rb",
    "content": "# frozen_string_literal: true\n\n# Provides utility methods\nmodule Puppet::Pops\nmodule Utils\n  # Can the given o be converted to numeric? (or is numeric already)\n  # Accepts a leading '::'\n  # Returns a boolean if the value is numeric\n  # If testing if value can be converted it is more efficient to call {#to_n} or {#to_n_with_radix} directly\n  # and check if value is nil.\n  def self.is_numeric?(o)\n    case o\n    when Numeric\n      true\n    else\n      !!Patterns::NUMERIC.match(relativize_name(o.to_s))\n    end\n  end\n\n  # Convert a match from Patterns::NUMERIC to floating point value if\n  # possible\n  def self.match_to_fp(match)\n    if match[5].to_s.length > 0\n      # Use default radix (default is decimal == 10) for floats\n      # Do not convert a value that is 0 raised to 10^somevalue to float - the value is always 0\n      # i.e. 0000.0e1, 0e1, 0.0000e1\n      if Integer(match[4]) == 0 && match[5] =~ /\\A\\.?0*[eE].*\\z/\n        nil\n      else\n        fp_value = Float(match[2])\n        if fp_value != Float::INFINITY\n          match[1] == '-' ? -fp_value : fp_value\n        else\n          nil\n        end\n      end\n    end\n  end\n\n  # To Numeric with radix, or nil if not a number.\n  # If the value is already Numeric it is returned verbatim with a radix of 10.\n  # @param o [String, Number] a string containing a number in octal, hex, integer (decimal) or floating point form\n  #   with optional sign +/-\n  # @return [Array<Number, Integer>, nil] array with converted number and radix, or nil if not possible to convert\n  # @api public\n  #\n  def self.to_n_with_radix o\n    case o\n    when String\n      match = Patterns::NUMERIC.match(relativize_name(o))\n      if !match\n        nil\n      elsif match[5].to_s.length > 0\n        fp_value = match_to_fp(match)\n        fp_value.nil? ? nil : [fp_value, 10]\n      else\n        # Set radix (default is decimal == 10)\n        radix = 10\n        if match[3].to_s.length > 0\n          radix = 16\n        elsif match[4].to_s.length > 1 && match[4][0, 1] == '0'\n          radix = 8\n        end\n        # Ruby 1.8.7 does not have a second argument to Kernel method that creates an\n        # integer from a string, it relies on the prefix 0x, 0X, 0 (and unsupported in puppet binary 'b')\n        # We have the correct string here, match[2] is safe to parse without passing on radix\n        match[1] == '-' ? [-Integer(match[2]), radix] : [Integer(match[2]), radix]\n      end\n    when Numeric\n      # Impossible to calculate radix, assume decimal\n      [o, 10]\n    else\n      nil\n    end\n  rescue ArgumentError\n    nil\n  end\n\n  # To Numeric (or already numeric)\n  # Returns nil if value is not numeric, else an Integer or Float. A String may have an optional sign.\n  #\n  # A leading '::' is accepted (and ignored)\n  #\n  def self.to_n o\n    case o\n    when String\n      match = Patterns::NUMERIC.match(relativize_name(o))\n      if !match\n        nil\n      elsif match[5].to_s.length > 0\n        match_to_fp(match)\n      else\n        match[1] == '-' ? -Integer(match[2]) : Integer(match[2])\n      end\n    when Numeric\n      o\n    else\n      nil\n    end\n  rescue ArgumentError\n    nil\n  end\n\n  # is the name absolute (i.e. starts with ::)\n  def self.is_absolute? name\n    name.start_with? \"::\"\n  end\n\n  def self.name_to_segments name\n    name.split(\"::\")\n  end\n\n  def self.relativize_name name\n    is_absolute?(name) ? name[2..] : name\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/validation/checker4_0.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/pops/evaluator/external_syntax_support'\n\nmodule Puppet::Pops\nmodule Validation\n# A Validator validates a model.\n#\n# Validation is performed on each model element in isolation. Each method should validate the model element's state\n# but not validate its referenced/contained elements except to check their validity in their respective role.\n# The intent is to drive the validation with a tree iterator that visits all elements in a model.\n#\n#\n# TODO: Add validation of multiplicities - this is a general validation that can be checked for all\n#       Model objects via their metamodel. (I.e an extra call to multiplicity check in polymorph check).\n#       This is however mostly valuable when validating model to model transformations, and is therefore T.B.D\n#\nclass Checker4_0 < Evaluator::LiteralEvaluator\n  include Puppet::Pops::Evaluator::ExternalSyntaxSupport\n\n  attr_reader :acceptor\n  attr_reader :migration_checker\n\n  def self.check_visitor\n    # Class instance variable rather than Class variable because methods visited\n    # may be overridden in subclass\n    @check_visitor ||= Visitor.new(nil, 'check', 0, 0)\n  end\n\n  # Initializes the validator with a diagnostics producer. This object must respond to\n  # `:will_accept?` and `:accept`.\n  #\n  def initialize(diagnostics_producer)\n    super()\n    @@rvalue_visitor      ||= Visitor.new(nil, \"rvalue\", 0, 0)\n    @@hostname_visitor    ||= Visitor.new(nil, \"hostname\", 1, 2)\n    @@assignment_visitor  ||= Visitor.new(nil, \"assign\", 0, 1)\n    @@query_visitor       ||= Visitor.new(nil, \"query\", 0, 0)\n    @@relation_visitor    ||= Visitor.new(nil, \"relation\", 0, 0)\n    @@idem_visitor        ||= Visitor.new(nil, \"idem\", 0, 0)\n\n    @check_visitor = self.class.check_visitor\n    @acceptor = diagnostics_producer\n\n    # Use null migration checker unless given in context\n    @migration_checker = (Puppet.lookup(:migration_checker) { Migration::MigrationChecker.new() })\n  end\n\n  # Validates the entire model by visiting each model element and calling `check`.\n  # The result is collected (or acted on immediately) by the configured diagnostic provider/acceptor\n  # given when creating this Checker.\n  #\n  def validate(model)\n    # tree iterate the model, and call check for each element\n    @path = []\n    check(model)\n    internal_check_top_construct_in_module(model)\n    model._pcore_all_contents(@path) { |element| check(element) }\n  end\n\n  def container(index = -1)\n    @path[index]\n  end\n\n  # Performs regular validity check\n  def check(o)\n    @check_visitor.visit_this_0(self, o)\n  end\n\n  # Performs check if this is a vaid hostname expression\n  # @param single_feature_name [String, nil] the name of a single valued hostname feature of the value's container. e.g. 'parent'\n  def hostname(o, semantic)\n    @@hostname_visitor.visit_this_1(self, o, semantic)\n  end\n\n  # Performs check if this is valid as a query\n  def query(o)\n    @@query_visitor.visit_this_0(self, o)\n  end\n\n  # Performs check if this is valid as a relationship side\n  def relation(o)\n    @@relation_visitor.visit_this_0(self, o)\n  end\n\n  # Performs check if this is valid as a rvalue\n  def rvalue(o)\n    @@rvalue_visitor.visit_this_0(self, o)\n  end\n\n  #---TOP CHECK\n  # Performs check if this is valid as a container of a definition (class, define, node)\n  def top(definition, idx = -1)\n    o = container(idx)\n    idx -= 1\n    case o\n    when NilClass, Model::ApplyExpression, Model::HostClassDefinition, Model::Program\n      # ok, stop scanning parents\n    when Model::BlockExpression\n      c = container(idx)\n      if !c.is_a?(Model::Program) &&\n         (definition.is_a?(Model::FunctionDefinition) || definition.is_a?(Model::TypeAlias) || definition.is_a?(Model::TypeDefinition))\n\n        # not ok. These can never be nested in a block\n        acceptor.accept(Issues::NOT_ABSOLUTE_TOP_LEVEL, definition)\n      else\n        # ok, if this is a block representing the body of a class, or is top level\n        top(definition, idx)\n      end\n    when Model::LambdaExpression\n      # A LambdaExpression is a BlockExpression, and this check is needed to prevent the polymorph method for BlockExpression\n      # to accept a lambda.\n      # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure.\n      acceptor.accept(Issues::NOT_TOP_LEVEL, definition)\n    else\n      # fail, reached a container that is not top level\n      acceptor.accept(Issues::NOT_TOP_LEVEL, definition)\n    end\n  end\n\n  # Checks the LHS of an assignment (is it assignable?).\n  # If args[0] is true, assignment via index is checked.\n  #\n  def assign(o, via_index = false)\n    @@assignment_visitor.visit_this_1(self, o, via_index)\n  end\n\n  # Checks if the expression has side effect ('idem' is latin for 'the same', here meaning that the evaluation state\n  # is known to be unchanged after the expression has been evaluated). The result is not 100% authoritative for\n  # negative answers since analysis of function behavior is not possible.\n  # @return [Boolean] true if expression is known to have no effect on evaluation state\n  #\n  def idem(o)\n    @@idem_visitor.visit_this_0(self, o)\n  end\n\n  # Returns the last expression in a block, or the expression, if that expression is idem\n  def ends_with_idem(o)\n    if o.is_a?(Model::BlockExpression)\n      last = o.statements[-1]\n      idem(last) ? last : nil\n    else\n      idem(o) ? o : nil\n    end\n  end\n\n  #---ASSIGNMENT CHECKS\n\n  def assign_VariableExpression(o, via_index)\n    varname_string = varname_to_s(o.expr)\n    if varname_string =~ Patterns::NUMERIC_VAR_NAME\n      acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string)\n    end\n    # Can not assign to something in another namespace (i.e. a '::' in the name is not legal)\n    if acceptor.will_accept? Issues::CROSS_SCOPE_ASSIGNMENT\n      if varname_string =~ /::/\n        acceptor.accept(Issues::CROSS_SCOPE_ASSIGNMENT, o, :name => varname_string)\n      end\n    end\n\n    # TODO: Could scan for reassignment of the same variable if done earlier in the same container\n    #       Or if assigning to a parameter (more work).\n  end\n\n  def assign_AccessExpression(o, via_index)\n    # Are indexed assignments allowed at all ? $x[x] = '...'\n    if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT\n      acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o)\n    else\n      # Then the left expression must be assignable-via-index\n      assign(o.left_expr, true)\n    end\n  end\n\n  def assign_LiteralList(o, via_index)\n    o.values.each { |x| assign(x) }\n  end\n\n  def assign_Object(o, via_index)\n    # Can not assign to anything else (differentiate if this is via index or not)\n    # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases)\n    #\n    acceptor.accept(via_index ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o)\n  end\n\n  #---CHECKS\n\n  def check_Object(o)\n  end\n\n  def check_Factory(o)\n    check(o.model)\n  end\n\n  def check_AccessExpression(o)\n    # Only min range is checked, all other checks are RT checks as they depend on the resulting type\n    # of the LHS.\n    if o.keys.size < 1\n      acceptor.accept(Issues::MISSING_INDEX, o)\n    end\n  end\n\n  def check_AssignmentExpression(o)\n    case o.operator\n    when '='\n      assign(o.left_expr)\n      rvalue(o.right_expr)\n    when '+=', '-='\n      acceptor.accept(Issues::APPENDS_DELETES_NO_LONGER_SUPPORTED, o, { :operator => o.operator })\n    else\n      acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, { :operator => o.operator })\n    end\n  end\n\n  # Checks that operation with :+> is contained in a ResourceOverride or Collector.\n  #\n  # Parent of an AttributeOperation can be one of:\n  # * CollectExpression\n  # * ResourceOverride\n  # * ResourceBody (ILLEGAL this is a regular resource expression)\n  # * ResourceDefaults (ILLEGAL)\n  #\n  def check_AttributeOperation(o)\n    if o.operator == '+>'\n      # Append operator use is constrained\n      p = container\n      unless p.is_a?(Model::CollectExpression) || p.is_a?(Model::ResourceOverrideExpression)\n        acceptor.accept(Issues::ILLEGAL_ATTRIBUTE_APPEND, o, { :name => o.attribute_name, :parent => p })\n      end\n    end\n    rvalue(o.value_expr)\n  end\n\n  def check_AttributesOperation(o)\n    # Append operator use is constrained\n    p = container\n    case p\n    when Model::AbstractResource\n      # do nothing\n    when Model::CollectExpression\n      # do nothing\n    else\n      # protect against just testing a snippet that has no parent, error message will be a bit strange\n      # but it is not for a real program.\n      parent2 = p.nil? ? o : container(-2)\n      unless parent2.is_a?(Model::AbstractResource)\n        acceptor.accept(Issues::UNSUPPORTED_OPERATOR_IN_CONTEXT, parent2, :operator => '* =>')\n      end\n    end\n    rvalue(o.expr)\n  end\n\n  def check_BinaryExpression(o)\n    rvalue(o.left_expr)\n    rvalue(o.right_expr)\n  end\n\n  def resource_without_title?(o)\n    if o.instance_of?(Model::BlockExpression)\n      statements = o.statements\n      statements.length == 2 && statements[0].instance_of?(Model::QualifiedName) && statements[1].instance_of?(Model::LiteralHash)\n    else\n      false\n    end\n  end\n\n  def check_BlockExpression(o)\n    if resource_without_title?(o)\n      acceptor.accept(Issues::RESOURCE_WITHOUT_TITLE, o, :name => o.statements[0].value)\n    else\n      o.statements[0..-2].each do |statement|\n        if idem(statement)\n          acceptor.accept(Issues::IDEM_EXPRESSION_NOT_LAST, statement)\n          break # only flag the first\n        end\n      end\n    end\n  end\n\n  def check_CallNamedFunctionExpression(o)\n    functor = o.functor_expr\n    if functor.is_a?(Model::QualifiedReference) ||\n       functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference)\n      # ok (a call to a type)\n      return nil\n    end\n\n    case functor\n    when Model::QualifiedName\n      # ok\n      nil\n    when Model::RenderStringExpression\n      # helpful to point out this easy to make Epp error\n      acceptor.accept(Issues::ILLEGAL_EPP_PARAMETERS, o)\n    else\n      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, { :feature => 'function name', :container => o })\n    end\n  end\n\n  def check_EppExpression(o)\n    p = container\n    if p.is_a?(Model::LambdaExpression)\n      internal_check_no_capture(p, o)\n      internal_check_parameter_name_uniqueness(p)\n    end\n  end\n\n  def check_HeredocExpression(o)\n    # Only syntax check static text in heredoc during validation - dynamic text is validated by the evaluator.\n    expr = o.text_expr\n    if expr.is_a?(Model::LiteralString)\n      assert_external_syntax(nil, expr.value, o.syntax, o.text_expr)\n    end\n  end\n\n  def check_MethodCallExpression(o)\n    unless o.functor_expr.is_a? Model::QualifiedName\n      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o)\n    end\n  end\n\n  def check_CaseExpression(o)\n    rvalue(o.test)\n    # There can only be one LiteralDefault case option value\n    found_default = false\n    o.options.each do |option|\n      option.values.each do |value|\n        next unless value.is_a?(Model::LiteralDefault)\n\n        # Flag the second default as 'unreachable'\n        acceptor.accept(Issues::DUPLICATE_DEFAULT, value, :container => o) if found_default\n        found_default = true\n      end\n    end\n  end\n\n  def check_CaseOption(o)\n    o.values.each { |v| rvalue(v) }\n  end\n\n  def check_CollectExpression(o)\n    unless o.type_expr.is_a? Model::QualifiedReference\n      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature => 'type name', :container => o)\n    end\n  end\n\n  # Only used for function names, grammar should not be able to produce something faulty, but\n  # check anyway if model is created programmatically (it will fail in transformation to AST for sure).\n  def check_NamedAccessExpression(o)\n    name = o.right_expr\n    unless name.is_a? Model::QualifiedName\n      acceptor.accept(Issues::ILLEGAL_EXPRESSION, name, :feature => 'function name', :container => container)\n    end\n  end\n\n  RESERVED_TYPE_NAMES = {\n    'type' => true,\n    'any' => true,\n    'unit' => true,\n    'scalar' => true,\n    'boolean' => true,\n    'numeric' => true,\n    'integer' => true,\n    'float' => true,\n    'collection' => true,\n    'array' => true,\n    'hash' => true,\n    'tuple' => true,\n    'struct' => true,\n    'variant' => true,\n    'optional' => true,\n    'enum' => true,\n    'regexp' => true,\n    'pattern' => true,\n    'runtime' => true,\n    'init' => true,\n    'object' => true,\n    'sensitive' => true,\n    'semver' => true,\n    'semverrange' => true,\n    'string' => true,\n    'timestamp' => true,\n    'timespan' => true,\n    'typeset' => true,\n  }\n\n  FUTURE_RESERVED_WORDS = {\n    'plan' => true\n  }\n\n  # for 'class', 'define', and function\n  def check_NamedDefinition(o)\n    top(o)\n    if o.name !~ Patterns::CLASSREF_DECL\n      acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, { :name => o.name })\n    end\n\n    internal_check_file_namespace(o)\n    internal_check_reserved_type_name(o, o.name)\n    internal_check_future_reserved_word(o, o.name)\n  end\n\n  def check_TypeAlias(o)\n    top(o)\n    if o.name !~ Patterns::CLASSREF_EXT_DECL\n      acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, { :name => o.name })\n    end\n    internal_check_reserved_type_name(o, o.name)\n    internal_check_type_ref(o, o.type_expr)\n  end\n\n  def check_TypeMapping(o)\n    top(o)\n    lhs = o.type_expr\n    lhs_type = 0 # Not Runtime\n    if lhs.is_a?(Model::AccessExpression)\n      left = lhs.left_expr\n      if left.is_a?(Model::QualifiedReference) && left.cased_value == 'Runtime'\n        lhs_type = 1 # Runtime\n        keys = lhs.keys\n\n        # Must be a literal string or pattern replacement\n        lhs_type = 2 if keys.size == 2 && pattern_with_replacement?(keys[1])\n      end\n    end\n\n    if lhs_type == 0\n      # This is not a TypeMapping. Something other than Runtime[] on LHS\n      acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o)\n    else\n      rhs = o.mapping_expr\n      if pattern_with_replacement?(rhs)\n        acceptor.accept(Issues::ILLEGAL_SINGLE_TYPE_MAPPING, o) if lhs_type == 1\n      elsif type_ref?(rhs)\n        acceptor.accept(Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o) if lhs_type == 2\n      else\n        acceptor.accept(lhs_type == 1 ? Issues::ILLEGAL_SINGLE_TYPE_MAPPING : Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o)\n      end\n    end\n  end\n\n  def pattern_with_replacement?(o)\n    if o.is_a?(Model::LiteralList)\n      v = o.values\n      v.size == 2 && v[0].is_a?(Model::LiteralRegularExpression) && v[1].is_a?(Model::LiteralString)\n    else\n      false\n    end\n  end\n\n  def type_ref?(o)\n    o = o.left_expr if o.is_a?(Model::AccessExpression)\n    o.is_a?(Model::QualifiedReference)\n  end\n\n  def check_TypeDefinition(o)\n    top(o)\n    internal_check_reserved_type_name(o, o.name)\n    # TODO: Check TypeDefinition body. For now, just error out\n    acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o)\n  end\n\n  def check_FunctionDefinition(o)\n    check_NamedDefinition(o)\n    internal_check_return_type(o)\n    internal_check_parameter_name_uniqueness(o)\n  end\n\n  def check_HostClassDefinition(o)\n    check_NamedDefinition(o)\n    internal_check_no_capture(o)\n    internal_check_parameter_name_uniqueness(o)\n    internal_check_reserved_params(o)\n    internal_check_no_idem_last(o)\n    internal_check_parameter_type_literal(o)\n  end\n\n  def check_ResourceTypeDefinition(o)\n    check_NamedDefinition(o)\n    internal_check_no_capture(o)\n    internal_check_parameter_name_uniqueness(o)\n    internal_check_reserved_params(o)\n    internal_check_no_idem_last(o)\n  end\n\n  def internal_check_parameter_type_literal(o)\n    o.parameters.each do |p|\n      next unless p.type_expr\n\n      type = nil\n      catch :not_literal do\n        type = literal(p.type_expr)\n      end\n      acceptor.accept(Issues::ILLEGAL_NONLITERAL_PARAMETER_TYPE, p, { name: p.name, type_class: p.type_expr.class }) if type.nil?\n    end\n  end\n\n  def internal_check_return_type(o)\n    r = o.return_type\n    internal_check_type_ref(o, r) unless r.nil?\n  end\n\n  def internal_check_type_ref(o, r)\n    n = r.is_a?(Model::AccessExpression) ? r.left_expr : r\n    if n.is_a? Model::QualifiedReference\n      internal_check_future_reserved_word(r, n.value)\n    else\n      acceptor.accept(Issues::ILLEGAL_EXPRESSION, r, :feature => 'a type reference', :container => o)\n    end\n  end\n\n  def internal_check_no_idem_last(o)\n    violator = ends_with_idem(o.body)\n    if violator\n      acceptor.accept(Issues::IDEM_NOT_ALLOWED_LAST, violator, { :container => o }) unless resource_without_title?(violator)\n    end\n  end\n\n  def internal_check_capture_last(o)\n    accepted_index = o.parameters.size() - 1\n    o.parameters.each_with_index do |p, index|\n      if p.captures_rest && index != accepted_index\n        acceptor.accept(Issues::CAPTURES_REST_NOT_LAST, p, { :param_name => p.name })\n      end\n    end\n  end\n\n  def internal_check_no_capture(o, container = o)\n    o.parameters.each do |p|\n      if p.captures_rest\n        acceptor.accept(Issues::CAPTURES_REST_NOT_SUPPORTED, p, { :container => container, :param_name => p.name })\n      end\n    end\n  end\n\n  def internal_check_reserved_type_name(o, name)\n    if RESERVED_TYPE_NAMES[name]\n      acceptor.accept(Issues::RESERVED_TYPE_NAME, o, { :name => name })\n    end\n  end\n\n  def internal_check_future_reserved_word(o, name)\n    if FUTURE_RESERVED_WORDS[name]\n      acceptor.accept(Issues::FUTURE_RESERVED_WORD, o, { :word => name })\n    end\n  end\n\n  NO_NAMESPACE = :no_namespace\n  NO_PATH = :no_path\n  BAD_MODULE_FILE = :bad_module_file\n\n  def internal_check_file_namespace(o)\n    file = o.locator.file\n    return if file.nil? || file == '' # e.g. puppet apply -e '...'\n\n    file_namespace = namespace_for_file(file)\n    return if file_namespace == NO_NAMESPACE\n\n    # Downcasing here because check is case-insensitive\n    if file_namespace == BAD_MODULE_FILE || !o.name.downcase.start_with?(file_namespace)\n      acceptor.accept(Issues::ILLEGAL_DEFINITION_LOCATION, o, { :name => o.name, :file => file })\n    end\n  end\n\n  def internal_check_top_construct_in_module(prog)\n    return unless prog.is_a?(Model::Program) && !prog.body.nil?\n\n    # Check that this is a module autoloaded file\n    file = prog.locator.file\n    return if file.nil?\n    return if namespace_for_file(file) == NO_NAMESPACE\n\n    body = prog.body\n    return if prog.body.is_a?(Model::Nop) # Ignore empty or comment-only files\n\n    if body.is_a?(Model::BlockExpression)\n      body.statements.each { |s| acceptor.accept(Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION, s) unless valid_top_construct?(s) }\n    else\n      acceptor.accept(Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION, body) unless valid_top_construct?(body)\n    end\n  end\n\n  def valid_top_construct?(o)\n    o.is_a?(Model::Definition) && !o.is_a?(Model::NodeDefinition)\n  end\n\n  # @api private\n  class Puppet::Util::FileNamespaceAdapter < Puppet::Pops::Adaptable::Adapter\n    attr_accessor :file_to_namespace\n\n    def self.create_adapter(env)\n      adapter = super(env)\n      adapter.file_to_namespace = {}\n      adapter\n    end\n  end\n\n  def namespace_for_file(file)\n    env = Puppet.lookup(:current_environment)\n    return NO_NAMESPACE if env.nil?\n\n    adapter = Puppet::Util::FileNamespaceAdapter.adapt(env)\n\n    file_namespace = adapter.file_to_namespace[file]\n    return file_namespace unless file_namespace.nil? # No cache entry, so we do the calculation\n\n    path = Pathname.new(file)\n\n    return adapter.file_to_namespace[file] = NO_NAMESPACE if path.extname != \".pp\"\n\n    path = path.expand_path\n\n    return adapter.file_to_namespace[file] = NO_NAMESPACE if initial_manifest?(path, env.manifest)\n\n    # All auto-loaded files from modules come from a module search path dir\n    relative_path = get_module_relative_path(path, env.full_modulepath)\n\n    return adapter.file_to_namespace[file] = NO_NAMESPACE if relative_path == NO_PATH\n\n    # If a file comes from a module, but isn't in the right place, always error\n    names = dir_to_names(relative_path)\n\n    adapter.file_to_namespace[file] = (names == BAD_MODULE_FILE ? BAD_MODULE_FILE : names.join(\"::\").freeze)\n  end\n\n  def initial_manifest?(path, manifest_setting)\n    return false if manifest_setting.nil? || manifest_setting == :no_manifest\n\n    string_path = path.to_s\n\n    string_path == manifest_setting || string_path.start_with?(manifest_setting)\n  end\n\n  # Get the path of +file_path+ relative to the first directory in\n  # +modulepath_directories+ that is an ancestor of +file_path+. Return NO_PATH\n  # if none is found.\n  def get_module_relative_path(file_path, modulepath_directories)\n    clean_file = file_path.cleanpath.to_s\n    parent_path = modulepath_directories.find { |path_dir| is_parent_dir_of?(path_dir, clean_file) }\n    return NO_PATH if parent_path.nil?\n\n    file_path.relative_path_from(Pathname.new(parent_path))\n  end\n  private :get_module_relative_path\n\n  def is_parent_dir_of?(parent_dir, child_dir)\n    parent_dir_path = Pathname.new(parent_dir)\n    clean_parent = parent_dir_path.cleanpath.to_s + File::SEPARATOR\n\n    child_dir.start_with?(clean_parent)\n  end\n  private :is_parent_dir_of?\n\n  def dir_to_names(relative_path)\n    # Downcasing here because check is case-insensitive\n    path_components = relative_path.to_s.downcase.split(File::SEPARATOR)\n\n    # Example definition dir: manifests in this path:\n    # <module name>/manifests/<module subdir>/<classfile>.pp\n    dir = path_components[1]\n\n    # How can we get this result?\n    # If it is not an initial manifest, it must come from a module,\n    # and from the manifests dir there.  This may never get used...\n    return BAD_MODULE_FILE unless dir == 'manifests' || dir == 'functions' || dir == 'types' || dir == 'plans'\n\n    names = path_components[2..-2] # Directories inside module\n    names.unshift(path_components[0]) # Name of the module itself\n\n    # Do not include name of module init file at top level of module\n    # e.g. <module name>/manifests/init.pp\n    filename = path_components[-1]\n    unless path_components.length == 3 && filename == 'init.pp'\n      names.push(filename[0..-4]) # Remove .pp from filename\n    end\n\n    names\n  end\n\n  RESERVED_PARAMETERS = {\n    'name' => true,\n    'title' => true,\n  }\n\n  def internal_check_reserved_params(o)\n    o.parameters.each do |p|\n      if RESERVED_PARAMETERS[p.name]\n        acceptor.accept(Issues::RESERVED_PARAMETER, p, { :container => o, :param_name => p.name })\n      end\n    end\n  end\n\n  def internal_check_parameter_name_uniqueness(o)\n    unique = Set.new\n    o.parameters.each do |p|\n      acceptor.accept(Issues::DUPLICATE_PARAMETER, p, { :param_name => p.name }) unless unique.add?(p.name)\n    end\n  end\n\n  def check_IfExpression(o)\n    rvalue(o.test)\n  end\n\n  def check_KeyedEntry(o)\n    rvalue(o.key)\n    rvalue(o.value)\n    # In case there are additional things to forbid than non-rvalues\n    # acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => container)\n  end\n\n  def check_LambdaExpression(o)\n    internal_check_capture_last(o)\n    internal_check_return_type(o)\n  end\n\n  def check_LiteralList(o)\n    o.values.each { |v| rvalue(v) }\n  end\n\n  def check_LiteralInteger(o)\n    v = o.value\n    if v < MIN_INTEGER || v > MAX_INTEGER\n      acceptor.accept(Issues::NUMERIC_OVERFLOW, o, { :value => v })\n    end\n  end\n\n  def check_LiteralHash(o)\n    # the keys of a literal hash may be non-literal expressions. They cannot be checked.\n    unique = Set.new\n    o.entries.each do |entry|\n      catch(:not_literal) do\n        literal_key = literal(entry.key)\n        acceptor.accept(Issues::DUPLICATE_KEY, entry, { :key => literal_key }) if unique.add?(literal_key).nil?\n      end\n    end\n  end\n\n  def check_NodeDefinition(o)\n    # Check that hostnames are valid hostnames (or regular expressions)\n    hostname(o.host_matches, o)\n    top(o)\n    violator = ends_with_idem(o.body)\n    if violator\n      acceptor.accept(Issues::IDEM_NOT_ALLOWED_LAST, violator, { :container => o }) unless resource_without_title?(violator)\n    end\n    unless o.parent.nil?\n      acceptor.accept(Issues::ILLEGAL_NODE_INHERITANCE, o.parent)\n    end\n  end\n\n  # No checking takes place - all expressions using a QualifiedName need to check. This because the\n  # rules are slightly different depending on the container (A variable allows a numeric start, but not\n  # other names). This means that (if the lexer/parser so chooses) a QualifiedName\n  # can be anything when it represents a Bare Word and evaluates to a String.\n  #\n  def check_QualifiedName(o)\n  end\n\n  # Checks that the value is a valid UpperCaseWord (a CLASSREF), and optionally if it contains a hypen.\n  # DOH: QualifiedReferences are created with LOWER CASE NAMES at parse time\n  def check_QualifiedReference(o)\n    # Is this a valid qualified name?\n    if o.cased_value !~ Patterns::CLASSREF_EXT\n      acceptor.accept(Issues::ILLEGAL_CLASSREF, o, { :name => o.cased_value })\n    end\n  end\n\n  def check_QueryExpression(o)\n    query(o.expr) if o.expr # is optional\n  end\n\n  def relation_Object(o)\n    rvalue(o)\n  end\n\n  def relation_CollectExpression(o); end\n\n  def relation_RelationshipExpression(o); end\n\n  def check_Parameter(o)\n    if o.name =~ /^(?:0x)?[0-9]+$/\n      acceptor.accept(Issues::ILLEGAL_NUMERIC_PARAMETER, o, :name => o.name)\n    end\n\n    unless o.name =~ Patterns::PARAM_NAME\n      acceptor.accept(Issues::ILLEGAL_PARAM_NAME, o, :name => o.name)\n    end\n    return unless o.value\n\n    internal_check_illegal_assignment(o.value)\n  end\n\n  def internal_check_illegal_assignment(o)\n    if o.is_a?(Model::AssignmentExpression)\n      acceptor.accept(Issues::ILLEGAL_ASSIGNMENT_CONTEXT, o)\n    else\n      # recursively check all contents unless it's a lambda expression. A lambda may contain\n      # local assignments\n      o._pcore_contents { |model| internal_check_illegal_assignment(model) } unless o.is_a?(Model::LambdaExpression)\n    end\n  end\n\n  # relationship_side: resource\n  #  | resourceref\n  #  | collection\n  #  | variable\n  #  | quotedtext\n  #  | selector\n  #  | casestatement\n  #  | hasharrayaccesses\n\n  def check_RelationshipExpression(o)\n    relation(o.left_expr)\n    relation(o.right_expr)\n  end\n\n  def check_ResourceExpression(o)\n    # The expression for type name cannot be statically checked - this is instead done at runtime\n    # to enable better error message of the result of the expression rather than the static instruction.\n    # (This can be revised as there are static constructs that are illegal, but require updating many\n    # tests that expect the detailed reporting).\n    type_name_expr = o.type_name\n    if o.form && o.form != 'regular' && type_name_expr.is_a?(Model::QualifiedName) && type_name_expr.value == 'class'\n      acceptor.accept(Issues::CLASS_NOT_VIRTUALIZABLE, o)\n    end\n  end\n\n  def check_ResourceBody(o)\n    seenUnfolding = false\n    o.operations.each do |ao|\n      if ao.is_a?(Model::AttributesOperation)\n        if seenUnfolding\n          acceptor.accept(Issues::MULTIPLE_ATTRIBUTES_UNFOLD, ao)\n        else\n          seenUnfolding = true\n        end\n      end\n    end\n  end\n\n  def check_ResourceDefaultsExpression(o)\n    if o.form != 'regular'\n      acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o)\n    end\n  end\n\n  def check_ResourceOverrideExpression(o)\n    if o.form != 'regular'\n      acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o)\n    end\n  end\n\n  def check_ReservedWord(o)\n    if o.future\n      acceptor.accept(Issues::FUTURE_RESERVED_WORD, o, :word => o.word)\n    else\n      acceptor.accept(Issues::RESERVED_WORD, o, :word => o.word)\n    end\n  end\n\n  def check_SelectorExpression(o)\n    rvalue(o.left_expr)\n    # There can only be one LiteralDefault case option value\n    defaults = o.selectors.select { |v| v.matching_expr.is_a?(Model::LiteralDefault) }\n    unless defaults.size <= 1\n      # Flag the second default as 'unreachable'\n      acceptor.accept(Issues::DUPLICATE_DEFAULT, defaults[1].matching_expr, :container => o)\n    end\n  end\n\n  def check_SelectorEntry(o)\n    rvalue(o.matching_expr)\n  end\n\n  def check_UnaryExpression(o)\n    rvalue(o.expr)\n  end\n\n  def check_UnlessExpression(o)\n    rvalue(o.test)\n    # TODO: Unless may not have an else part that is an IfExpression (grammar denies this though)\n  end\n\n  # Checks that variable is either strictly 0, or a non 0 starting decimal number, or a valid VAR_NAME\n  def check_VariableExpression(o)\n    # The expression must be a qualified name or an integer\n    name_expr = o.expr\n    return if name_expr.is_a?(Model::LiteralInteger)\n\n    if !name_expr.is_a?(Model::QualifiedName)\n      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o)\n    else\n      # name must be either a decimal string value, or a valid NAME\n      name = o.expr.value\n      if name[0, 1] =~ /[0-9]/\n        unless name =~ Patterns::NUMERIC_VAR_NAME\n          acceptor.accept(Issues::ILLEGAL_NUMERIC_VAR_NAME, o, :name => name)\n        end\n      else\n        unless name =~ Patterns::VAR_NAME\n          acceptor.accept(Issues::ILLEGAL_VAR_NAME, o, :name => name)\n        end\n      end\n    end\n  end\n\n  #--- HOSTNAME CHECKS\n\n  # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName\n  def hostname_Array(o, semantic)\n    o.each { |x| hostname(x, semantic) }\n  end\n\n  def hostname_String(o, semantic)\n    # The 3.x checker only checks for illegal characters - if matching /[^-\\w.]/ the name is invalid,\n    # but this allows pathological names like \"a..b......c\", \"----\"\n    # TODO: Investigate if more illegal hostnames should be flagged.\n    #\n    if o =~ Patterns::ILLEGAL_HOSTNAME_CHARS\n      acceptor.accept(Issues::ILLEGAL_HOSTNAME_CHARS, semantic, :hostname => o)\n    end\n  end\n\n  def hostname_LiteralValue(o, semantic)\n    hostname_String(o.value.to_s, o)\n  end\n\n  def hostname_ConcatenatedString(o, semantic)\n    # Puppet 3.1. only accepts a concatenated string without interpolated expressions\n    the_expr = o.segments.index { |s| s.is_a?(Model::TextExpression) }\n    if the_expr\n      acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr)\n    elsif o.segments.size() != 1\n      # corner case, bad model, concatenation of several plain strings\n      acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o)\n    else\n      # corner case, may be ok, but lexer may have replaced with plain string, this is\n      # here if it does not\n      hostname_String(o.segments[0], o.segments[0])\n    end\n  end\n\n  def hostname_QualifiedName(o, semantic)\n    hostname_String(o.value.to_s, o)\n  end\n\n  def hostname_QualifiedReference(o, semantic)\n    hostname_String(o.value.to_s, o)\n  end\n\n  def hostname_LiteralNumber(o, semantic)\n    # always ok\n  end\n\n  def hostname_LiteralDefault(o, semantic)\n    # always ok\n  end\n\n  def hostname_LiteralRegularExpression(o, semantic)\n    # always ok\n  end\n\n  def hostname_Object(o, semantic)\n    acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, { :feature => 'hostname', :container => semantic })\n  end\n\n  #---QUERY CHECKS\n\n  # Anything not explicitly allowed is flagged as error.\n  def query_Object(o)\n    acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o)\n  end\n\n  # Puppet AST only allows == and !=\n  #\n  def query_ComparisonExpression(o)\n    acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) unless ['==', '!='].include? o.operator\n  end\n\n  # Allows AND, OR, and checks if left/right are allowed in query.\n  def query_BooleanExpression(o)\n    query o.left_expr\n    query o.right_expr\n  end\n\n  def query_ParenthesizedExpression(o)\n    query(o.expr)\n  end\n\n  def query_VariableExpression(o); end\n\n  def query_QualifiedName(o); end\n\n  def query_LiteralNumber(o); end\n\n  def query_LiteralString(o); end\n\n  def query_LiteralBoolean(o); end\n\n  #---RVALUE CHECKS\n\n  # By default, all expressions are reported as being rvalues\n  # Implement specific rvalue checks for those that are not.\n  #\n  def rvalue_Expression(o); end\n\n  def rvalue_CollectExpression(o)\n    acceptor.accept(Issues::NOT_RVALUE, o)\n  end\n\n  def rvalue_Definition(o)\n    acceptor.accept(Issues::NOT_RVALUE, o)\n  end\n\n  def rvalue_NodeDefinition(o)\n    acceptor.accept(Issues::NOT_RVALUE, o)\n  end\n\n  def rvalue_UnaryExpression(o)\n    rvalue o.expr\n  end\n\n  #--IDEM CHECK\n  def idem_Object(o)\n    false\n  end\n\n  def idem_Nop(o)\n    true\n  end\n\n  def idem_NilClass(o)\n    true\n  end\n\n  def idem_Literal(o)\n    true\n  end\n\n  def idem_LiteralList(o)\n    true\n  end\n\n  def idem_LiteralHash(o)\n    true\n  end\n\n  def idem_Factory(o)\n    idem(o.model)\n  end\n\n  def idem_AccessExpression(o)\n    true\n  end\n\n  def idem_BinaryExpression(o)\n    true\n  end\n\n  def idem_MatchExpression(o)\n    false # can have side effect of setting $n match variables\n  end\n\n  def idem_RelationshipExpression(o)\n    # Always side effect\n    false\n  end\n\n  def idem_AssignmentExpression(o)\n    # Always side effect\n    false\n  end\n\n  # Handles UnaryMinusExpression, NotExpression, VariableExpression\n  def idem_UnaryExpression(o)\n    true\n  end\n\n  # Allow (no-effect parentheses) to be used around a productive expression\n  def idem_ParenthesizedExpression(o)\n    idem(o.expr)\n  end\n\n  def idem_RenderExpression(o)\n    false\n  end\n\n  def idem_RenderStringExpression(o)\n    false\n  end\n\n  def idem_BlockExpression(o)\n    # productive if there is at least one productive expression\n    !o.statements.any? { |expr| !idem(expr) }\n  end\n\n  # Returns true even though there may be interpolated expressions that have side effect.\n  # Report as idem anyway, as it is very bad design to evaluate an interpolated string for its\n  # side effect only.\n  def idem_ConcatenatedString(o)\n    true\n  end\n\n  # Heredoc is just a string, but may contain interpolated string (which may have side effects).\n  # This is still bad design and should be reported as idem.\n  def idem_HeredocExpression(o)\n    true\n  end\n\n  # May technically have side effects inside the Selector, but this is bad design - treat as idem\n  def idem_SelectorExpression(o)\n    true\n  end\n\n  # An apply expression exists purely for the side effect of applying a\n  # catalog somewhere, so it always has side effects\n  def idem_ApplyExpression(o)\n    false\n  end\n\n  def idem_IfExpression(o)\n    [o.test, o.then_expr, o.else_expr].all? { |e| idem(e) }\n  end\n\n  # Case expression is idem, if test, and all options are idem\n  def idem_CaseExpression(o)\n    return false unless idem(o.test)\n\n    !o.options.any? { |opt| !idem(opt) }\n  end\n\n  # An option is idem if values and the then_expression are idem\n  def idem_CaseOption(o)\n    return false if o.values.any? { |value| !idem(value) }\n\n    idem(o.then_expr)\n  end\n\n  #--- NON POLYMORPH, NON CHECKING CODE\n\n  # Produces string part of something named, or nil if not a QualifiedName or QualifiedReference\n  #\n  def varname_to_s(o)\n    case o\n    when Model::QualifiedName\n      o.value\n    when Model::QualifiedReference\n      o.value\n    else\n      nil\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/validation/tasks_checker.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Validation\n# Validator that limits the set of allowed expressions to not include catalog related operations\n# @api private\nclass TasksChecker < Checker4_0\n  def in_ApplyExpression?\n    top = container(0)\n    step = -1\n    until container(step) == top\n      return true if container(step).is_a? Puppet::Pops::Model::ApplyBlockExpression\n\n      step -= 1\n    end\n  end\n\n  def check_CollectExpression(o)\n    # Only virtual resource queries are allowed in apply blocks, not exported\n    # resource queries\n    if in_ApplyExpression?\n      if o.query.is_a?(Puppet::Pops::Model::VirtualQuery)\n        super(o)\n      else\n        acceptor.accept(Issues::EXPRESSION_NOT_SUPPORTED_WHEN_COMPILING, o, { :klass => o })\n      end\n    else\n      illegalTasksExpression(o)\n    end\n  end\n\n  def check_HostClassDefinition(o)\n    illegalTasksExpression(o)\n  end\n\n  def check_NodeDefinition(o)\n    if in_ApplyExpression?\n      super(o)\n    else\n      illegalTasksExpression(o)\n    end\n  end\n\n  def check_RelationshipExpression(o)\n    if in_ApplyExpression?\n      super(o)\n    else\n      illegalTasksExpression(o)\n    end\n  end\n\n  def check_ResourceDefaultsExpression(o)\n    if in_ApplyExpression?\n      super(o)\n    else\n      illegalTasksExpression(o)\n    end\n  end\n\n  def check_ResourceExpression(o)\n    if in_ApplyExpression?\n      super(o)\n    else\n      illegalTasksExpression(o)\n    end\n  end\n\n  def check_ResourceOverrideExpression(o)\n    if in_ApplyExpression?\n      super(o)\n    else\n      illegalTasksExpression(o)\n    end\n  end\n\n  def check_ResourceTypeDefinition(o)\n    illegalTasksExpression(o)\n  end\n\n  def check_ApplyExpression(o)\n    if in_ApplyExpression?\n      acceptor.accept(Issues::EXPRESSION_NOT_SUPPORTED_WHEN_COMPILING, o, { :klass => o })\n    end\n  end\n\n  def illegalTasksExpression(o)\n    acceptor.accept(Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING, o, { :klass => o })\n  end\n\n  def resource_without_title?(o)\n    false\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/validation/validator_factory_4_0.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\nmodule Validation\n# Configures validation suitable for 4.0\n#\nclass ValidatorFactory_4_0 < Factory\n  # Produces the checker to use\n  def checker diagnostic_producer\n    if Puppet[:tasks]\n      require_relative 'tasks_checker'\n      TasksChecker.new(diagnostic_producer)\n    else\n      Checker4_0.new(diagnostic_producer)\n    end\n  end\n\n  # Produces the label provider to use\n  def label_provider\n    Model::ModelLabelProvider.new()\n  end\n\n  # Produces the severity producer to use\n  def severity_producer\n    p = super\n\n    # Configure each issue that should **not** be an error\n    #\n    # Validate as per the current runtime configuration\n    p[Issues::RT_NO_STORECONFIGS_EXPORT]      = Puppet[:storeconfigs] ? :ignore : :warning\n    p[Issues::RT_NO_STORECONFIGS]             = Puppet[:storeconfigs] ? :ignore : :warning\n\n    p[Issues::FUTURE_RESERVED_WORD]           = :deprecation\n\n    p[Issues::DUPLICATE_KEY]                  = Puppet[:strict] == :off ? :ignore : Puppet[:strict]\n    p[Issues::NAME_WITH_HYPHEN]               = :error\n    p[Issues::EMPTY_RESOURCE_SPECIALIZATION]  = :ignore\n    p[Issues::CLASS_NOT_VIRTUALIZABLE]        = :error\n\n    p[Issues::ILLEGAL_NONLITERAL_PARAMETER_TYPE] = :deprecation\n    p\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/validation.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\n# A module with base functionality for validation of a model.\n#\n# * **Factory** - an abstract factory implementation that makes it easier to create a new validation factory.\n# * **SeverityProducer** - produces a severity (:error, :warning, :ignore) for a given Issue\n# * **DiagnosticProducer** - produces a Diagnostic which binds an Issue to an occurrence of that issue\n# * **Acceptor** - the receiver/sink/collector of computed diagnostics\n# * **DiagnosticFormatter** - produces human readable output for a Diagnostic\n#\nmodule Validation\n  # This class is an abstract base implementation of a _model validation factory_ that creates a validator instance\n  # and associates it with a fully configured DiagnosticProducer.\n  #\n  # A _validator_ is responsible for validating a model. There may be different versions of validation available\n  # for one and the same model; e.g. different semantics for different puppet versions, or different types of\n  # validation configuration depending on the context/type of validation that should be performed (static, vs. runtime, etc.).\n  #\n  # This class is abstract and must be subclassed. The subclass must implement the methods\n  # {#label_provider} and {#checker}. It is also expected that the subclass will override\n  # the severity_producer and configure the issues that should be reported as errors (i.e. if they should be ignored, produce\n  # a warning, or a deprecation warning).\n  #\n  # @abstract Subclass must implement {#checker}, and {#label_provider}\n  # @api public\n  #\n  class Factory\n    # Produces a validator with the given acceptor as the recipient of produced diagnostics.\n    # The acceptor is where detected issues are received (and typically collected).\n    #\n    # @param acceptor [Acceptor] the acceptor is the receiver of all detected issues\n    # @return [#validate] a validator responding to `validate(model)`\n    #\n    # @api public\n    #\n    def validator(acceptor)\n      checker(diagnostic_producer(acceptor))\n    end\n\n    # Produces the diagnostics producer to use given an acceptor of issues.\n    #\n    # @param acceptor [Acceptor] the acceptor is the receiver of all detected issues\n    # @return [DiagnosticProducer] a detector of issues\n    #\n    # @api public\n    #\n    def diagnostic_producer(acceptor)\n      DiagnosticProducer.new(acceptor, severity_producer(), label_provider())\n    end\n\n    # Produces the SeverityProducer to use\n    # Subclasses should implement and add specific overrides\n    #\n    # @return [SeverityProducer] a severity producer producing error, warning or ignore per issue\n    #\n    # @api public\n    #\n    def severity_producer\n      SeverityProducer.new\n    end\n\n    # Produces the checker to use.\n    #\n    # @abstract\n    #\n    # @api public\n    #\n    def checker(diagnostic_producer)\n      raise NoMethodError, \"checker\"\n    end\n\n    # Produces the label provider to use.\n    #\n    # @abstract\n    #\n    # @api public\n    #\n    def label_provider\n      raise NoMethodError, \"label_provider\"\n    end\n  end\n\n  # Decides on the severity of a given issue.\n  # The produced severity is one of `:error`, `:warning`, or `:ignore`.\n  # By default, a severity of `:error` is produced for all issues. To configure the severity\n  # of an issue call `#severity=(issue, level)`.\n  #\n  # @return [Symbol] a symbol representing the severity `:error`, `:warning`, or `:ignore`\n  #\n  # @api public\n  #\n  class SeverityProducer\n    SEVERITIES = { ignore: true, warning: true, error: true, deprecation: true }.freeze\n\n    # Creates a new instance where all issues are diagnosed as :error unless overridden.\n    # @param [Symbol] specifies default severity if :error is not wanted as the default\n    # @api public\n    #\n    def initialize(default_severity = :error)\n      # If diagnose is not set, the default is returned by the block\n      @severities = Hash.new default_severity\n    end\n\n    # Returns the severity of the given issue.\n    # @return [Symbol] severity level :error, :warning, or :ignore\n    # @api public\n    #\n    def severity(issue)\n      assert_issue(issue)\n      @severities[issue]\n    end\n\n    # @see {#severity}\n    # @api public\n    #\n    def [] issue\n      severity issue\n    end\n\n    # Override a default severity with the given severity level.\n    #\n    # @param issue [Issues::Issue] the issue for which to set severity\n    # @param level [Symbol] the severity level (:error, :warning, or :ignore).\n    # @api public\n    #\n    def []=(issue, level)\n      unless issue.is_a? Issues::Issue\n        raise Puppet::DevError, _(\"Attempt to set validation severity for something that is not an Issue. (Got %{issue})\") % { issue: issue.class }\n      end\n      unless SEVERITIES[level]\n        raise Puppet::DevError, _(\"Illegal severity level: %{level} for '%{issue_code}'\") % { issue_code: issue.issue_code, level: level }\n      end\n      unless issue.demotable? || level == :error\n        raise Puppet::DevError, _(\"Attempt to demote the hard issue '%{issue_code}' to %{level}\") % { issue_code: issue.issue_code, level: level }\n      end\n\n      @severities[issue] = level\n    end\n\n    # Returns `true` if the issue should be reported or not.\n    # @return [Boolean] this implementation returns true for errors and warnings\n    #\n    # @api public\n    #\n    def should_report? issue\n      diagnose = @severities[issue]\n      diagnose == :error || diagnose == :warning || diagnose == :deprecation\n    end\n\n    # Checks if the given issue is valid.\n    # @api private\n    #\n    def assert_issue issue\n      unless issue.is_a? Issues::Issue\n        raise Puppet::DevError, _(\"Attempt to get validation severity for something that is not an Issue. (Got %{issue})\") % { issue: issue.class }\n      end\n    end\n  end\n\n  # A producer of diagnostics.\n  # An producer of diagnostics is given each issue occurrence as they are found by a diagnostician/validator. It then produces\n  # a Diagnostic, which it passes on to a configured Acceptor.\n  #\n  # This class exists to aid a diagnostician/validator which will typically first check if a particular issue\n  # will be accepted at all (before checking for an occurrence of the issue; i.e. to perform check avoidance for expensive checks).\n  # A validator passes an instance of Issue, the semantic object (the \"culprit\"), a hash with arguments, and an optional\n  # exception. The semantic object is used to determine the location of the occurrence of the issue (file/line), and it\n  # sets keys in the given argument hash that may be used in the formatting of the issue message.\n  #\n  class DiagnosticProducer\n    # A producer of severity for a given issue\n    # @return [SeverityProducer]\n    #\n    attr_reader :severity_producer\n\n    # A producer of labels for objects involved in the issue\n    # @return [LabelProvider]\n    #\n    attr_reader :label_provider\n\n    # Initializes this producer.\n    #\n    # @param acceptor [Acceptor] a sink/collector of diagnostic results\n    # @param severity_producer [SeverityProducer] the severity producer to use to determine severity of a given issue\n    # @param label_provider [LabelProvider] a provider of model element type to human readable label\n    #\n    def initialize(acceptor, severity_producer, label_provider)\n      @acceptor           = acceptor\n      @severity_producer  = severity_producer\n      @label_provider     = label_provider\n    end\n\n    def accept(issue, semantic, arguments = {}, except = nil)\n      return unless will_accept? issue\n\n      # Set label provider unless caller provided a special label provider\n      arguments[:label]    ||= @label_provider\n      arguments[:semantic] ||= semantic\n\n      # A detail message is always provided, but is blank by default.\n      # TODO: this support is questionable, it requires knowledge that :detail is special\n      arguments[:detail] ||= ''\n\n      # Accept an Error as semantic if it supports methods #file(), #line(), and #pos()\n      if semantic.is_a?(StandardError)\n        unless semantic.respond_to?(:file) && semantic.respond_to?(:line) && semantic.respond_to?(:pos)\n          raise Puppet::DevError, _(\"Issue %{issue_code}: Cannot pass a %{class_name} as a semantic object when it does not support #pos(), #file() and #line()\") %\n                                  { issue_code: issue.issue_code, class_name: semantic.class }\n        end\n      end\n\n      source_pos = semantic\n      file = semantic.file unless semantic.nil?\n\n      severity = @severity_producer.severity(issue)\n      @acceptor.accept(Diagnostic.new(severity, issue, file, source_pos, arguments, except))\n    end\n\n    def will_accept? issue\n      @severity_producer.should_report? issue\n    end\n  end\n\n  class Diagnostic\n    attr_reader :severity\n    attr_reader :issue\n    attr_reader :arguments\n    attr_reader :exception\n    attr_reader :file\n    attr_reader :source_pos\n\n    def initialize severity, issue, file, source_pos, arguments = {}, exception = nil\n      @severity = severity\n      @issue = issue\n      @file = file\n      @source_pos = source_pos\n      @arguments = arguments\n      # TODO: Currently unused, the intention is to provide more information (stack backtrace, etc.) when\n      # debugging or similar - this to catch internal problems reported as higher level issues.\n      @exception = exception\n    end\n\n    # Two diagnostics are considered equal if the have the same issue, location and severity\n    # (arguments and exception are ignored)\n    # rubocop:disable Layout\n    def ==(o)\n      self.class            == o.class             &&\n        same_position?(o)                          &&\n        issue.issue_code    == o.issue.issue_code  &&\n        file                == o.file              &&\n        severity            == o.severity\n    end\n    alias eql? ==\n    # rubocop:enable Layout\n\n    def hash\n      @hash ||= [file, source_pos.offset, issue.issue_code, severity].hash\n    end\n\n    # Position is equal if the diagnostic is not located or if referring to the same offset\n    def same_position?(o)\n      source_pos.nil? && o.source_pos.nil? || source_pos.offset == o.source_pos.offset\n    end\n    private :same_position?\n  end\n\n  # Formats a diagnostic for output.\n  # Produces a diagnostic output typical for a compiler (suitable for interpretation by tools)\n  # The format is:\n  # `file:line:pos: Message`, where pos, line and file are included if available.\n  #\n  class DiagnosticFormatter\n    def format diagnostic\n      \"#{format_location(diagnostic)} #{format_severity(diagnostic)}#{format_message(diagnostic)}\"\n    end\n\n    def format_message diagnostic\n      diagnostic.issue.format(diagnostic.arguments)\n    end\n\n    # This produces \"Deprecation notice: \" prefix if the diagnostic has :deprecation severity, otherwise \"\".\n    # The idea is that all other diagnostics are emitted with the methods Puppet.err (or an exception), and\n    # Puppet.warning.\n    # @note Note that it is not a good idea to use Puppet.deprecation_warning as it is for internal deprecation.\n    #\n    def format_severity diagnostic\n      diagnostic.severity == :deprecation ? \"Deprecation notice: \" : \"\"\n    end\n\n    def format_location diagnostic\n      file = diagnostic.file\n      file = (file.is_a?(String) && file.empty?) ? nil : file\n      line = pos = nil\n      if diagnostic.source_pos\n        line = diagnostic.source_pos.line\n        pos = diagnostic.source_pos.pos\n      end\n      if file && line && pos\n        \"#{file}:#{line}:#{pos}:\"\n      elsif file && line\n        \"#{file}:#{line}:\"\n      elsif file\n        \"#{file}:\"\n      else\n        \"\"\n      end\n    end\n  end\n\n  # Produces a diagnostic output in the \"puppet style\", where the location is appended with an \"at ...\" if the\n  # location is known.\n  #\n  class DiagnosticFormatterPuppetStyle < DiagnosticFormatter\n    def format diagnostic\n      if (location = format_location diagnostic) != \"\"\n        \"#{format_severity(diagnostic)}#{format_message(diagnostic)}#{location}\"\n      else\n        format_message(diagnostic)\n      end\n    end\n\n    # The somewhat (machine) unusable format in current use by puppet.\n    # have to be used here for backwards compatibility.\n    def format_location diagnostic\n      file = diagnostic.file\n      file = (file.is_a?(String) && file.empty?) ? nil : file\n      line = pos = nil\n      if diagnostic.source_pos\n        line = diagnostic.source_pos.line\n        pos = diagnostic.source_pos.pos\n      end\n\n      if file && line && pos\n        \" at #{file}:#{line}:#{pos}\"\n      elsif file && line\n        \" at #{file}:#{line}\"\n      elsif line && pos\n        \" at line #{line}:#{pos}\"\n      elsif line\n        \" at line #{line}\"\n      elsif file\n        \" in #{file}\"\n      else\n        \"\"\n      end\n    end\n  end\n\n  # An acceptor of diagnostics.\n  # An acceptor of diagnostics is given each issue as they are found by a diagnostician/validator. An\n  # acceptor can collect all found issues, or decide to collect a few and then report, or give up as the first issue\n  # if found.\n  # This default implementation collects all diagnostics in the order they are produced, and can then\n  # answer questions about what was diagnosed.\n  #\n  class Acceptor\n    # All diagnostic in the order they were issued\n    attr_reader :diagnostics\n\n    # The number of :warning severity issues + number of :deprecation severity issues\n    attr_reader :warning_count\n\n    # The number of :error severity issues\n    attr_reader :error_count\n\n    # Initializes this diagnostics acceptor.\n    # By default, the acceptor is configured with a default severity producer.\n    # @param severity_producer [SeverityProducer] the severity producer to use to determine severity of an issue\n    #\n    # TODO add semantic_label_provider\n    #\n    def initialize\n      @diagnostics = []\n      @error_count = 0\n      @warning_count = 0\n    end\n\n    # Returns true when errors have been diagnosed.\n    def errors?\n      @error_count > 0\n    end\n\n    # Returns true when warnings have been diagnosed.\n    def warnings?\n      @warning_count > 0\n    end\n\n    # Returns true when errors and/or warnings have been diagnosed.\n    def errors_or_warnings?\n      errors? || warnings?\n    end\n\n    # Returns the diagnosed errors in the order they were reported.\n    def errors\n      @diagnostics.select { |d| d.severity == :error }\n    end\n\n    # Returns the diagnosed warnings in the order they were reported.\n    # (This includes :warning and :deprecation severity)\n    def warnings\n      @diagnostics.select { |d| d.severity == :warning || d.severity == :deprecation }\n    end\n\n    def errors_and_warnings\n      @diagnostics.select { |d| d.severity != :ignore }\n    end\n\n    # Returns the ignored diagnostics in the order they were reported (if reported at all)\n    def ignored\n      @diagnostics.select { |d| d.severity == :ignore }\n    end\n\n    # Add a diagnostic, or all diagnostics from another acceptor to the set of diagnostics\n    # @param diagnostic [Diagnostic, Acceptor] diagnostic(s) that should be accepted\n    def accept(diagnostic)\n      if diagnostic.is_a?(Acceptor)\n        diagnostic.diagnostics.each { |d| _accept(d) }\n      else\n        _accept(diagnostic)\n      end\n    end\n\n    # Prunes the contain diagnostics by removing those for which the given block returns true.\n    # The internal statistics is updated as a consequence of removing.\n    # @return [Array<Diagnostic, nil] the removed set of diagnostics or nil if nothing was removed\n    #\n    def prune(&block)\n      removed = []\n      @diagnostics.delete_if do |d|\n        should_remove = yield(d)\n        if should_remove\n          removed << d\n        end\n        should_remove\n      end\n      removed.each do |d|\n        case d.severity\n        when :error\n          @error_count -= 1\n        when :warning\n          @warning_count -= 1\n          # there is not ignore_count\n        end\n      end\n      removed.empty? ? nil : removed\n    end\n\n    private\n\n    def _accept(diagnostic)\n      @diagnostics << diagnostic\n      case diagnostic.severity\n      when :error\n        @error_count += 1\n      when :deprecation, :warning\n        @warning_count += 1\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops/visitable.rb",
    "content": "# frozen_string_literal: true\n\n# Visitable is a mix-in module that makes a class visitable by a Visitor\nmodule Puppet::Pops::Visitable\n  def accept(visitor, *arguments)\n    visitor.visit(self, *arguments)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/pops/visitor.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Pops\n# A Visitor performs delegation to a given receiver based on the configuration of the Visitor.\n# A new visitor is created with a given receiver, a method prefix, min, and max argument counts.\n# e.g.\n#   visitor = Visitor.new(self, \"visit_from\", 1, 1)\n# will make the visitor call \"self.visit_from_CLASS(x)\" where CLASS is resolved to the given\n# objects class, or one of is ancestors, the first class for which there is an implementation of\n# a method will be selected.\n#\n# Raises RuntimeError if there are too few or too many arguments, or if the receiver is not\n# configured to handle a given visiting object.\n#\nclass Visitor\n  attr_reader :receiver, :message, :min_args, :max_args, :cache\n\n  def initialize(receiver, message, min_args = 0, max_args = nil)\n    raise ArgumentError, \"min_args must be >= 0\" if min_args < 0\n    raise ArgumentError, \"max_args must be >= min_args or nil\" if max_args && max_args < min_args\n\n    @receiver = receiver\n    @message = message\n    @min_args = min_args\n    @max_args = max_args\n    @cache = Hash.new\n  end\n\n  # Visit the configured receiver\n  def visit(thing, *args)\n    visit_this(@receiver, thing, args)\n  end\n\n  NO_ARGS = EMPTY_ARRAY\n\n  # Visit an explicit receiver\n  def visit_this(receiver, thing, args)\n    raise \"Visitor Error: Too few arguments passed. min = #{@min_args}\" unless args.length >= @min_args\n\n    if @max_args\n      raise \"Visitor Error: Too many arguments passed. max = #{@max_args}\" unless args.length <= @max_args\n    end\n    method_name = @cache[thing.class]\n    if method_name\n      return receiver.send(method_name, thing, *args)\n    else\n      thing.class.ancestors().each do |ancestor|\n        name = ancestor.name\n        next if name.nil?\n\n        method_name = :\"#{@message}_#{name.split(DOUBLE_COLON).last}\"\n        next unless receiver.respond_to?(method_name, true)\n\n        @cache[thing.class] = method_name\n        return receiver.send(method_name, thing, *args)\n      end\n    end\n\n    raise \"Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}\"\n  end\n\n  # Visit an explicit receiver\n  def visit_this_class(receiver, clazz, args)\n    raise \"Visitor Error: Too few arguments passed. min = #{@min_args}\" unless args.length >= @min_args\n\n    if @max_args\n      raise \"Visitor Error: Too many arguments passed. max = #{@max_args}\" unless args.length <= @max_args\n    end\n    method_name = @cache[clazz]\n    if method_name\n      return receiver.send(method_name, clazz, *args)\n    else\n      clazz.ancestors().each do |ancestor|\n        name = ancestor.name\n        next if name.nil?\n\n        method_name = :\"#{@message}_#{name.split(DOUBLE_COLON).last}\"\n        next unless receiver.respond_to?(method_name, true)\n\n        @cache[clazz] = method_name\n        return receiver.send(method_name, clazz, *args)\n      end\n    end\n\n    raise \"Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{clazz}\"\n  end\n\n  # Visit an explicit receiver with 0 args\n  # (This is ~30% faster than calling the general method)\n  #\n  def visit_this_0(receiver, thing)\n    method_name = @cache[thing.class]\n    if method_name\n      return receiver.send(method_name, thing)\n    end\n\n    visit_this(receiver, thing, NO_ARGS)\n  end\n\n  # Visit an explicit receiver with 1 args\n  # (This is ~30% faster than calling the general method)\n  #\n  def visit_this_1(receiver, thing, arg)\n    method_name = @cache[thing.class]\n    if method_name\n      return receiver.send(method_name, thing, arg)\n    end\n\n    visit_this(receiver, thing, [arg])\n  end\n\n  # Visit an explicit receiver with 2 args\n  # (This is ~30% faster than calling the general method)\n  #\n  def visit_this_2(receiver, thing, arg1, arg2)\n    method_name = @cache[thing.class]\n    if method_name\n      return receiver.send(method_name, thing, arg1, arg2)\n    end\n\n    visit_this(receiver, thing, [arg1, arg2])\n  end\n\n  # Visit an explicit receiver with 3 args\n  # (This is ~30% faster than calling the general method)\n  #\n  def visit_this_3(receiver, thing, arg1, arg2, arg3)\n    method_name = @cache[thing.class]\n    if method_name\n      return receiver.send(method_name, thing, arg1, arg2, arg3)\n    end\n\n    visit_this(receiver, thing, [arg1, arg2, arg3])\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/pops.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  # The Pops language system. This includes the parser, evaluator, AST model, and\n  # Binder.\n  #\n  # @todo Explain how a user should use this to parse and evaluate the puppet\n  #   language.\n  #\n  # @note Warning: Pops is still considered experimental, as such the API may\n  #   change at any time.\n  #\n  # @api public\n  module Pops\n    EMPTY_HASH = {}.freeze\n    EMPTY_ARRAY = [].freeze\n    EMPTY_STRING = ''\n\n    MAX_INTEGER =  0x7fffffffffffffff\n    MIN_INTEGER = -0x8000000000000000\n\n    DOUBLE_COLON = '::'\n    USCORE = '_'\n\n    require 'semantic_puppet'\n\n    require_relative 'pops/patterns'\n    require_relative 'pops/utils'\n    require_relative 'pops/puppet_stack'\n\n    require_relative 'pops/adaptable'\n    require_relative 'pops/adapters'\n\n    require_relative 'pops/visitable'\n    require_relative 'pops/visitor'\n\n    require_relative 'pops/issues'\n    require_relative 'pops/semantic_error'\n    require_relative 'pops/label_provider'\n    require_relative 'pops/validation'\n    require_relative 'pops/issue_reporter'\n\n    require_relative 'pops/time/timespan'\n    require_relative 'pops/time/timestamp'\n\n    # (the Types module initializes itself)\n    require_relative 'pops/types/types'\n    require_relative 'pops/types/string_converter'\n    require_relative 'pops/lookup'\n\n    require_relative 'pops/merge_strategy'\n\n    module Model\n      require_relative 'pops/model/ast'\n      require_relative 'pops/model/tree_dumper'\n      require_relative 'pops/model/ast_transformer'\n      require_relative 'pops/model/factory'\n      require_relative 'pops/model/model_tree_dumper'\n      require_relative 'pops/model/model_label_provider'\n    end\n\n    module Resource\n      require_relative 'pops/resource/resource_type_impl'\n    end\n\n    module Evaluator\n      require_relative 'pops/evaluator/literal_evaluator'\n      require_relative 'pops/evaluator/callable_signature'\n      require_relative 'pops/evaluator/runtime3_converter'\n      require_relative 'pops/evaluator/runtime3_resource_support'\n      require_relative 'pops/evaluator/runtime3_support'\n      require_relative 'pops/evaluator/evaluator_impl'\n      require_relative 'pops/evaluator/epp_evaluator'\n      require_relative 'pops/evaluator/collector_transformer'\n      require_relative 'pops/evaluator/puppet_proc'\n      require_relative 'pops/evaluator/deferred_resolver'\n      module Collectors\n        require_relative 'pops/evaluator/collectors/abstract_collector'\n        require_relative 'pops/evaluator/collectors/fixed_set_collector'\n        require_relative 'pops/evaluator/collectors/catalog_collector'\n        require_relative 'pops/evaluator/collectors/exported_collector'\n      end\n    end\n\n    module Parser\n      require_relative 'pops/parser/eparser'\n      require_relative 'pops/parser/parser_support'\n      require_relative 'pops/parser/locator'\n      require_relative 'pops/parser/locatable'\n      require_relative 'pops/parser/lexer2'\n      require_relative 'pops/parser/evaluating_parser'\n      require_relative 'pops/parser/epp_parser'\n      require_relative 'pops/parser/code_merger'\n    end\n\n    module Validation\n      require_relative 'pops/validation/checker4_0'\n      require_relative 'pops/validation/validator_factory_4_0'\n    end\n\n    # Subsystem for puppet functions defined in ruby.\n    #\n    # @api public\n    module Functions\n      require_relative 'pops/functions/function'\n      require_relative 'pops/functions/dispatch'\n      require_relative 'pops/functions/dispatcher'\n    end\n\n    module Migration\n      require_relative 'pops/migration/migration_checker'\n    end\n\n    module Serialization\n      require_relative 'pops/serialization'\n    end\n  end\n\n  require_relative '../puppet/parser/ast/pops_bridge'\n  require_relative '../puppet/functions'\n  require_relative '../puppet/datatypes'\n\n  Puppet::Pops::Model.register_pcore_types\nend\n"
  },
  {
    "path": "lib/puppet/property/boolean.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/coercion'\n\nclass Puppet::Property::Boolean < Puppet::Property\n  def unsafe_munge(value)\n    Puppet::Coercion.boolean(value)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/property/ensure.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/property'\n\n# This property is automatically added to any {Puppet::Type} that responds\n# to the methods 'exists?', 'create', and 'destroy'.\n#\n# Ensure defaults to having the wanted _(should)_ value `:present`.\n#\n# @api public\n#\nclass Puppet::Property::Ensure < Puppet::Property\n  @name = :ensure\n\n  def self.defaultvalues\n    newvalue(:present) do\n      if @resource.provider and @resource.provider.respond_to?(:create)\n        @resource.provider.create\n      else\n        @resource.create\n      end\n      nil # return nil so the event is autogenerated\n    end\n\n    newvalue(:absent) do\n      if @resource.provider and @resource.provider.respond_to?(:destroy)\n        @resource.provider.destroy\n      else\n        @resource.destroy\n      end\n      nil # return nil so the event is autogenerated\n    end\n\n    defaultto do\n      if @resource.managed?\n        :present\n      else\n        nil\n      end\n    end\n\n    # This doc will probably get overridden\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @doc ||= \"The basic property that the resource should be in.\"\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  def self.inherited(sub)\n    # Add in the two properties that everyone will have.\n    sub.class_eval do\n    end\n  end\n\n  def change_to_s(currentvalue, newvalue)\n    if currentvalue == :absent || currentvalue.nil?\n      _(\"created\")\n    elsif newvalue == :absent\n      _(\"removed\")\n    else\n      _('%{name} changed %{is} to %{should}') % { name: name, is: is_to_s(currentvalue), should: should_to_s(newvalue) }\n    end\n  rescue Puppet::Error\n    raise\n  rescue => detail\n    raise Puppet::DevError, _(\"Could not convert change %{name} to string: %{detail}\") % { name: name, detail: detail }, detail.backtrace\n  end\n\n  # Retrieves the _is_ value for the ensure property.\n  # The existence of the resource is checked by first consulting the provider (if it responds to\n  # `:exists`), and secondly the resource. A a value of `:present` or `:absent` is returned\n  # depending on if the managed entity exists or not.\n  #\n  # @return [Symbol] a value of `:present` or `:absent` depending on if it exists or not\n  # @raise [Puppet::DevError] if neither the provider nor the resource responds to `:exists`\n  #\n  def retrieve\n    # XXX This is a problem -- whether the object exists or not often\n    # depends on the results of other properties, yet we're the first property\n    # to get checked, which means that those other properties do not have\n    # @is values set.  This seems to be the source of quite a few bugs,\n    # although they're mostly logging bugs, not functional ones.\n    prov = @resource.provider\n    if prov && prov.respond_to?(:exists?)\n      result = prov.exists?\n    elsif @resource.respond_to?(:exists?)\n      result = @resource.exists?\n    else\n      raise Puppet::DevError, _(\"No ability to determine if %{name} exists\") % { name: @resource.class.name }\n    end\n    if result\n      :present\n    else\n      :absent\n    end\n  end\n\n  # If they're talking about the thing at all, they generally want to\n  # say it should exist.\n  # defaultto :present\n  defaultto do\n    if @resource.managed?\n      :present\n    else\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/property/keyvalue.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/property'\n\nmodule Puppet\n  class Property\n    # This subclass of {Puppet::Property} manages string key value pairs.\n    # In order to use this property:\n    #\n    # * the _should_ value must be an array of key-value pairs separated by the 'separator'\n    # * the retrieve method should return a hash with the keys as symbols\n    # @note **IMPORTANT**: In order for this property to work there must also be a 'membership' parameter\n    #   The class that inherits from property should override that method with the symbol for the membership\n    # @todo The node with an important message is not very clear.\n    #\n    class KeyValue < Property\n      class << self\n        # This is a class-level variable that child properties can override\n        # if they wish.\n        attr_accessor :log_only_changed_or_new_keys\n      end\n\n      self.log_only_changed_or_new_keys = false\n\n      def hash_to_key_value_s(hash)\n        if self.class.log_only_changed_or_new_keys\n          hash = hash.select { |k, _| @changed_or_new_keys.include?(k) }\n        end\n\n        hash.map { |*pair| pair.join(separator) }.join(delimiter)\n      end\n\n      def should_to_s(should_value)\n        hash_to_key_value_s(should_value)\n      end\n\n      def is_to_s(current_value) # rubocop:disable Naming/PredicateName\n        hash_to_key_value_s(current_value)\n      end\n\n      def membership\n        :key_value_membership\n      end\n\n      def inclusive?\n        @resource[membership] == :inclusive\n      end\n\n      def hashify_should\n        # Puppet casts all should values to arrays. Thus, if the user\n        # passed in a hash for our property's should value, the should_value\n        # parameter will be a single element array so we just extract our value\n        # directly.\n        if !@should.empty? && @should.first.is_a?(Hash)\n          return @should.first\n        end\n\n        # Here, should is an array of key/value pairs.\n        @should.each_with_object({}) do |key_value, hash|\n          tmp = key_value.split(separator)\n          hash[tmp[0].strip.intern] = tmp[1]\n        end\n      end\n\n      def process_current_hash(current)\n        return {} if current == :absent\n\n        # inclusive means we are managing everything so if it isn't in should, its gone\n        current.each_key { |key| current[key] = nil } if inclusive?\n        current\n      end\n\n      def should\n        return nil unless @should\n\n        members = hashify_should\n        current = process_current_hash(retrieve)\n\n        # shared keys will get overwritten by members\n        should_value = current.merge(members)\n\n        # Figure out the keys that will actually change in our Puppet run.\n        # This lets us reduce the verbosity of Puppet's logging for instances\n        # of this class when we want to.\n        #\n        # NOTE: We use ||= here because we only need to compute the\n        # changed_or_new_keys once (since this property will only be synced once).\n        #\n        @changed_or_new_keys ||= should_value.keys.select do |key|\n          !current.key?(key) || current[key] != should_value[key]\n        end\n\n        should_value\n      end\n\n      # @return [String] Returns a default separator of \"=\"\n      def separator\n        \"=\"\n      end\n\n      # @return [String] Returns a default delimiter of \";\"\n      def delimiter\n        \";\"\n      end\n\n      # Retrieves the key-hash from the provider by invoking its method named the same as this property.\n      # @return [Hash] the hash from the provider, or `:absent`\n      #\n      def retrieve\n        # ok, some 'convention' if the keyvalue property is named properties, provider should implement a properties method\n        key_hash = provider.send(name) if provider\n        if key_hash && key_hash != :absent\n          key_hash\n        else\n          :absent\n        end\n      end\n\n      # Returns true if there is no _is_ value, else returns if _is_ is equal to _should_ using == as comparison.\n      # @return [Boolean] whether the property is in sync or not.\n      def insync?(is)\n        return true unless is\n\n        (is == should)\n      end\n\n      # We only accept an array of key/value pairs (strings), a single\n      # key/value pair (string) or a Hash as valid values for our property.\n      # Note that for an array property value, the 'value' passed into the\n      # block corresponds to the array element.\n      validate do |value|\n        unless value.is_a?(String) || value.is_a?(Hash)\n          raise ArgumentError, _(\"The %{name} property must be specified as a hash or an array of key/value pairs (strings)!\") % { name: name }\n        end\n\n        next if value.is_a?(Hash)\n\n        unless value.include?(separator.to_s)\n          raise ArgumentError, _(\"Key/value pairs must be separated by '%{separator}'\") % { separator: separator }\n        end\n      end\n\n      # The validate step ensures that our passed-in value is\n      # either a String or a Hash. If our value's a string,\n      # then nothing else needs to be done. Otherwise, we need\n      # to stringify the hash's keys and values to match our\n      # internal representation of the property's value.\n      munge do |value|\n        next value if value.is_a?(String)\n\n        munged_value = value.to_a.map! do |hash_key, hash_value|\n          [hash_key.to_s.strip.to_sym, hash_value.to_s]\n        end\n\n        munged_value.to_h\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/property/list.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/property'\n\nmodule Puppet\n  class Property\n    # This subclass of {Puppet::Property} manages an unordered list of values.\n    # For an ordered list see {Puppet::Property::OrderedList}.\n    #\n    class List < Property\n      def is_to_s(currentvalue) # rubocop:disable Naming/PredicateName\n        currentvalue == :absent ? super(currentvalue) : currentvalue.join(delimiter)\n      end\n\n      def membership\n        :membership\n      end\n\n      def add_should_with_current(should, current)\n        should += current if current.is_a?(Array)\n        should.uniq\n      end\n\n      def inclusive?\n        @resource[membership] == :inclusive\n      end\n\n      # dearrayify was motivated because to simplify the implementation of the OrderedList property\n      def dearrayify(array)\n        array.sort.join(delimiter)\n      end\n\n      def should\n        return nil unless @should\n\n        members = @should\n        # inclusive means we are managing everything so if it isn't in should, its gone\n        members = add_should_with_current(members, retrieve) unless inclusive?\n\n        dearrayify(members)\n      end\n\n      def delimiter\n        \",\"\n      end\n\n      def retrieve\n        # ok, some 'convention' if the list property is named groups, provider should implement a groups method\n        tmp = provider.send(name) if provider\n        if tmp && tmp != :absent\n          tmp.instance_of?(Array) ? tmp : tmp.split(delimiter)\n        else\n          :absent\n        end\n      end\n\n      def prepare_is_for_comparison(is)\n        if is == :absent\n          is = []\n        end\n        dearrayify(is)\n      end\n\n      def insync?(is)\n        return true unless is\n\n        (prepare_is_for_comparison(is) == should)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/property/ordered_list.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/property/list'\n\nmodule Puppet\n  class Property\n    # This subclass of {Puppet::Property} manages an ordered list of values.\n    # The maintained order is the order defined by the 'current' set of values (i.e. the\n    # original order is not disrupted). Any additions are added after the current values\n    # in their given order).\n    #\n    # For an unordered list see {Puppet::Property::List}.\n    #\n    class OrderedList < List\n      def add_should_with_current(should, current)\n        if current.is_a?(Array)\n          # tricky trick\n          # Preserve all the current items in the list\n          # but move them to the back of the line\n          should += (current - should)\n        end\n        should\n      end\n\n      def dearrayify(array)\n        array.join(delimiter)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/property.rb",
    "content": "# frozen_string_literal: true\n\n# The virtual base class for properties, which are the self-contained building\n# blocks for actually doing work on the system.\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/parameter'\n\n# The Property class is the implementation of a resource's attributes of _property_ kind.\n# A Property is a specialized Resource Type Parameter that has both an 'is' (current) state, and\n# a 'should' (wanted state). However, even if this is conceptually true, the current _is_ value is\n# obtained by asking the associated provider for the value, and hence it is not actually part of a\n# property's state, and only available when a provider has been selected and can obtain the value (i.e. when\n# running on an agent).\n#\n# A Property (also in contrast to a parameter) is intended to describe a managed attribute of\n# some system entity, such as the name or mode of a file.\n#\n# The current value _(is)_ is read and written with the methods {#retrieve} and {#set}, and the wanted\n# value _(should)_ is read and written with the methods {#value} and {#value=} which delegate to\n# {#should} and {#should=}, i.e. when a property is used like any other parameter, it is the _should_ value\n# that is operated on.\n#\n# All resource type properties in the puppet system are derived from this class.\n#\n# The intention is that new parameters are created by using the DSL method {Puppet::Type.newproperty}.\n#\n# @abstract\n# @note Properties of Types are expressed using subclasses of this class. Such a class describes one\n#   named property of a particular Type (as opposed to describing a type of property in general). This\n#   limits the use of one (concrete) property class instance to occur only once for a given type's inheritance\n#   chain. An instance of a Property class is the value holder of one instance of the resource type (e.g. the\n#   mode of a file resource instance).\n#   A Property class may server as the superclass _(parent)_ of another; e.g. a Size property that describes\n#   handling of measurements such as kb, mb, gb. If a type requires two different size measurements it requires\n#   one concrete class per such measure; e.g. MinSize (:parent => Size), and MaxSize (:parent => Size).\n#\n# @see Puppet::Type\n# @see Puppet::Parameter\n#\n# @api public\n#\nclass Puppet::Property < Puppet::Parameter\n  require_relative 'property/ensure'\n\n  # Returns the original wanted value(s) _(should)_ unprocessed by munging/unmunging.\n  # The original values are set by {#value=} or {#should=}.\n  # @return (see #should)\n  #\n  attr_reader :shouldorig\n\n  # The noop mode for this property.\n  # By setting a property's noop mode to `true`, any management of this property is inhibited. Calculation\n  # and reporting still takes place, but if a change of the underlying managed entity's state\n  # should take place it will not be carried out. This noop\n  # setting overrides the overall `Puppet[:noop]` mode as well as the noop mode in the _associated resource_\n  #\n  attr_writer :noop\n\n  class << self\n    # @todo Figure out what this is used for. Can not find any logic in the puppet code base that\n    #   reads or writes this attribute.\n    # ??? Probably Unused\n    attr_accessor :unmanaged\n\n    # @return [Symbol] The name of the property as given when the property was created.\n    #\n    attr_reader :name\n\n    # @!attribute [rw] array_matching\n    # @comment note that $#46; is a period - char code require to not terminate sentence.\n    # The `is` vs&#46; `should` array matching mode; `:first`, or `:all`.\n    #\n    # @comment there are two blank chars after the symbols to cause a break - do not remove these.\n    # * `:first`\n    #   This is primarily used for single value properties. When matched against an array of values\n    #   a match is true if the `is` value matches any of the values in the `should` array. When the `is` value\n    #   is also an array, the matching is performed against the entire array as the `is` value.\n    # * `:all`\n    #   : This is primarily used for multi-valued properties. When matched against an array of\n    #     `should` values, the size of `is` and `should` must be the same, and all values in `is` must match\n    #     a value in `should`.\n    #\n    # @note The semantics of these modes are implemented by the method {#insync?}. That method is the default\n    #   implementation and it has a backwards compatible behavior that imposes additional constraints\n    #   on what constitutes a positive match. A derived property may override that method.\n    # @return [Symbol] (:first) the mode in which matching is performed\n    # @see #insync?\n    # @dsl type\n    # @api public\n    #\n    def array_matching\n      @array_matching ||= :first\n    end\n\n    # @comment This is documented as an attribute - see the {array_matching} method.\n    #\n    def array_matching=(value)\n      value = value.intern if value.is_a?(String)\n      # TRANSLATORS 'Property#array_matching', 'first', and 'all' should not be translated\n      raise ArgumentError, _(\"Supported values for Property#array_matching are 'first' and 'all'\") unless [:first, :all].include?(value)\n\n      @array_matching = value\n    end\n\n    # Used to mark a type property as having or lacking idempotency (on purpose\n    # generally). This is used to avoid marking the property as a\n    # corrective_change when there is known idempotency issues with the property\n    # rendering a corrective_change flag as useless.\n    # @return [Boolean] true if the property is marked as idempotent\n    def idempotent\n      @idempotent.nil? ? @idempotent = true : @idempotent\n    end\n\n    # Attribute setter for the idempotent attribute.\n    # @param [bool] value boolean indicating if the property is idempotent.\n    # @see idempotent\n    def idempotent=(value)\n      @idempotent = value\n    end\n  end\n\n  # Looks up a value's name among valid values, to enable option lookup with result as a key.\n  # @param name [Object] the parameter value to match against valid values (names).\n  # @return {Symbol, Regexp} a value matching predicate\n  # @api private\n  #\n  def self.value_name(name)\n    value = value_collection.match?(name)\n    value.name if value\n  end\n\n  # Returns the value of the given option (set when a valid value with the given \"name\" was defined).\n  # @param name [Symbol, Regexp] the valid value predicate as returned by {value_name}\n  # @param option [Symbol] the name of the wanted option\n  # @return [Object] value of the option\n  # @raise [NoMethodError] if the option is not supported\n  # @todo Guessing on result of passing a non supported option (it performs send(option)).\n  # @api private\n  #\n  def self.value_option(name, option)\n    value = value_collection.value(name)\n    value.send(option) if value\n  end\n\n  # Defines a new valid value for this property.\n  # A valid value is specified as a literal (typically a Symbol), but can also be\n  # specified with a Regexp.\n  #\n  # @param name [Symbol, Regexp] a valid literal value, or a regexp that matches a value\n  # @param options [Hash] a hash with options\n  # @option options [Symbol] :event The event that should be emitted when this value is set.\n  # @todo Option :event original comment says \"event should be returned...\", is \"returned\" the correct word\n  #   to use?\n  # @option options [Symbol] :invalidate_refreshes Indicates a change on this property should invalidate and\n  #   remove any scheduled refreshes (from notify or subscribe) targeted at the same resource. For example, if\n  #   a change in this property takes into account any changes that a scheduled refresh would have performed,\n  #   then the scheduled refresh would be deleted.\n  # @option options [Object] any Any other option is treated as a call to a setter having the given\n  #   option name (e.g. `:required_features` calls `required_features=` with the option's value as an\n  #   argument).\n  #\n  # @dsl type\n  # @api public\n  def self.newvalue(name, options = {}, &block)\n    value = value_collection.newvalue(name, options, &block)\n\n    unless value.method.nil?\n      method = value.method.to_sym\n      if value.block\n        if instance_methods(false).include?(method)\n          raise ArgumentError, _(\"Attempt to redefine method %{method} with block\") % { method: method }\n        end\n\n        define_method(method, &value.block)\n      else\n        # Let the method be an alias for calling the providers setter unless we already have this method\n        alias_method(method, :call_provider) unless method_defined?(method)\n      end\n    end\n    value\n  end\n\n  # Calls the provider setter method for this property with the given value as argument.\n  # @return [Object] what the provider returns when calling a setter for this property's name\n  # @raise [Puppet::Error] when the provider can not handle this property.\n  # @see #set\n  # @api private\n  #\n  def call_provider(value)\n    # We have no idea how to handle this unless our parent have a provider\n    self.fail \"#{self.class.name} cannot handle values of type #{value.inspect}\" unless @resource.provider\n    method = self.class.name.to_s + \"=\"\n    unless provider.respond_to? method\n      self.fail \"The #{provider.class.name} provider can not handle attribute #{self.class.name}\"\n    end\n    provider.send(method, value)\n  end\n\n  # Formats a message for a property change from the given `current_value` to the given `newvalue`.\n  # @return [String] a message describing the property change.\n  # @note If called with equal values, this is reported as a change.\n  # @raise [Puppet::DevError] if there were issues formatting the message\n  #\n  def change_to_s(current_value, newvalue)\n    if current_value == :absent\n      \"defined '#{name}' as #{should_to_s(newvalue)}\"\n    elsif newvalue == :absent or newvalue == [:absent]\n      \"undefined '#{name}' from #{is_to_s(current_value)}\"\n    else\n      \"#{name} changed #{is_to_s(current_value)} to #{should_to_s(newvalue)}\"\n    end\n  rescue Puppet::Error\n    raise\n  rescue => detail\n    message = _(\"Could not convert change '%{name}' to string: %{detail}\") % { name: name, detail: detail }\n    Puppet.log_exception(detail, message)\n    raise Puppet::DevError, message, detail.backtrace\n  end\n\n  # Produces the name of the event to use to describe a change of this property's value.\n  # The produced event name is either the event name configured for this property, or a generic\n  # event based on the name of the property with suffix `_changed`, or if the property is\n  # `:ensure`, the name of the resource type and one of the suffixes `_created`, `_removed`, or `_changed`.\n  # @return [String] the name of the event that describes the change\n  #\n  def event_name\n    value = should\n\n    event_name = self.class.value_option(value, :event) and return event_name\n\n    name == :ensure or return (name.to_s + \"_changed\").to_sym\n\n    (resource.type.to_s + case value\n                          when :present; \"_created\"\n                          when :absent;  \"_removed\"\n                          else \"_changed\"\n                          end).to_sym\n  end\n\n  # Produces an event describing a change of this property.\n  # In addition to the event attributes set by the resource type, this method adds:\n  #\n  # * `:name` - the event_name\n  # * `:desired_value` - a.k.a _should_ or _wanted value_\n  # * `:property` - reference to this property\n  # * `:source_description` - The containment path of this property, indicating what resource this\n  #                           property is associated with and in what stage and class that resource\n  #                           was declared, e.g. \"/Stage[main]/Myclass/File[/tmp/example]/ensure\"\n  # * `:invalidate_refreshes` - if scheduled refreshes should be invalidated\n  # * `:redacted` - if the event will be redacted (due to this property being sensitive)\n  #\n  # @return [Puppet::Transaction::Event] the created event\n  # @see Puppet::Type#event\n  def event(options = {})\n    attrs = { :name => event_name, :desired_value => should, :property => self, :source_description => path }.merge(options)\n    value = self.class.value_collection.match?(should) if should\n\n    attrs[:invalidate_refreshes] = true if value && value.invalidate_refreshes\n    attrs[:redacted] = @sensitive\n    resource.event attrs\n  end\n\n  # Determines whether the property is in-sync or not in a way that is protected against missing value.\n  # @note If the wanted value _(should)_ is not defined or is set to a non-true value then this is\n  #   a state that can not be fixed and the property is reported to be in sync.\n  # @return [Boolean] the protected result of `true` or the result of calling {#insync?}.\n  #\n  # @api private\n  # @note Do not override this method.\n  #\n  def safe_insync?(is)\n    # If there is no @should value, consider the property to be in sync.\n    return true unless @should\n\n    # Otherwise delegate to the (possibly derived) insync? method.\n    insync?(is)\n  end\n\n  # Protects against override of the {#safe_insync?} method.\n  # @raise [RuntimeError] if the added method is `:safe_insync?`\n  # @api private\n  #\n  def self.method_added(sym)\n    raise \"Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead\" if sym == :safe_insync?\n  end\n\n  # Checks if the current _(is)_ value is in sync with the wanted _(should)_ value.\n  # The check if the two values are in sync is controlled by the result of {#match_all?} which\n  # specifies a match of `:first` or `:all`). The matching of the _is_ value against the entire _should_ value\n  # or each of the _should_ values (as controlled by {#match_all?} is performed by {#property_matches?}.\n  #\n  # A derived property typically only needs to override the {#property_matches?} method, but may also\n  # override this method if there is a need to have more control over the array matching logic.\n  #\n  # @note The array matching logic in this method contains backwards compatible logic that performs the\n  #   comparison in `:all` mode by checking equality and equality of _is_ against _should_ converted to array of String,\n  #   and that the lengths are equal, and in `:first` mode by checking if one of the _should_ values\n  #   is included in the _is_ values. This means that the _is_ value needs to be carefully arranged to\n  #   match the _should_.\n  # @todo The implementation should really do return is.zip(@should).all? {|a, b| property_matches?(a, b) }\n  #   instead of using equality check and then check against an array with converted strings.\n  # @param is [Object] The current _(is)_ value to check if it is in sync with the wanted _(should)_ value(s)\n  # @return [Boolean] whether the values are in sync or not.\n  # @raise [Puppet::DevError] if wanted value _(should)_ is not an array.\n  # @api public\n  #\n  def insync?(is)\n    devfail \"#{self.class.name}'s should is not array\" unless @should.is_a?(Array)\n\n    # an empty array is analogous to no should values\n    return true if @should.empty?\n\n    # Look for a matching value, either for all the @should values, or any of\n    # them, depending on the configuration of this property.\n    if match_all? then\n      # Emulate Array#== using our own comparison function.\n      # A non-array was not equal to an array, which @should always is.\n      return false unless is.is_a? Array\n\n      # If they were different lengths, they are not equal.\n      return false unless is.length == @should.length\n\n      # Finally, are all the elements equal?  In order to preserve the\n      # behaviour of previous 2.7.x releases, we need to impose some fun rules\n      # on \"equality\" here.\n      #\n      # Specifically, we need to implement *this* comparison: the two arrays\n      # are identical if the is values are == the should values, or if the is\n      # values are == the should values, stringified.\n      #\n      # This does mean that property equality is not commutative, and will not\n      # work unless the `is` value is carefully arranged to match the should.\n      (is == @should or is == @should.map(&:to_s))\n\n      # When we stop being idiots about this, and actually have meaningful\n      # semantics, this version is the thing we actually want to do.\n      #\n      # return is.zip(@should).all? {|a, b| property_matches?(a, b) }\n    else\n      @should.any? { |want| property_matches?(is, want) }\n    end\n  end\n\n  # This method tests if two values are insync? outside of the properties current\n  # should value. This works around the requirement for corrective_change analysis\n  # that requires two older values to be compared with the properties potentially\n  # custom insync? code.\n  #\n  # @param [Object] should the value it should be\n  # @param [Object] is the value it is\n  # @return [Boolean] whether or not the values are in sync or not\n  # @api private\n  def insync_values?(should, is)\n    # Here be dragons. We're setting the should value of a property purely just to\n    # call its insync? method, as it lacks a way to pass in a should.\n    # Unfortunately there isn't an API compatible way of avoiding this, as both should\n    # an insync? behaviours are part of the public API. Future API work should factor\n    # this kind of arbitrary comparisons into the API to remove this complexity. -ken\n\n    # Backup old should, set it to the new value, then call insync? on the property.\n    old_should = @should\n\n    begin\n      @should = should\n      insync?(is)\n    rescue\n      # Certain operations may fail, but we don't want to fail the transaction if we can\n      # avoid it\n      # TRANSLATORS 'insync_values?' should not be translated\n      msg = _(\"Unknown failure using insync_values? on type: %{type} / property: %{name} to compare values %{should} and %{is}\") %\n            { type: resource.ref, name: name, should: should, is: is }\n      Puppet.info(msg)\n\n      # Return nil, ie. unknown\n      nil\n    ensure\n      # Always restore old should\n      @should = old_should\n    end\n  end\n\n  # Checks if the given current and desired values are equal.\n  # This default implementation performs this check in a backwards compatible way where\n  # the equality of the two values is checked, and then the equality of current with desired\n  # converted to a string.\n  #\n  # A derived implementation may override this method to perform a property specific equality check.\n  #\n  # The intent of this method is to provide an equality check suitable for checking if the property\n  # value is in sync or not. It is typically called from {#insync?}.\n  #\n  def property_matches?(current, desired)\n    # This preserves the older Puppet behaviour of doing raw and string\n    # equality comparisons for all equality.  I am not clear this is globally\n    # desirable, but at least it is not a breaking change. --daniel 2011-11-11\n    current == desired or current == desired.to_s\n  end\n\n  # Produces a pretty printing string for the given value.\n  # This default implementation calls {#format_value_for_display} on the class. A derived\n  # implementation may perform property specific pretty printing when the _is_ values\n  # are not already in suitable form.\n  # @param value [Object] the value to format as a string\n  # @return [String] a pretty printing string\n  def is_to_s(value) # rubocop:disable Naming/PredicateName\n    self.class.format_value_for_display(value)\n  end\n\n  # Emits a log message at the log level specified for the associated resource.\n  # The log entry is associated with this property.\n  # @param msg [String] the message to log\n  # @return [void]\n  #\n  def log(msg)\n    Puppet::Util::Log.create(\n      :level => resource[:loglevel],\n      :message => msg,\n      :source => self\n    )\n  end\n\n  # @return [Boolean] whether the {array_matching} mode is set to `:all` or not\n  def match_all?\n    self.class.array_matching == :all\n  end\n\n  # @return [Boolean] whether the property is marked as idempotent for the purposes\n  #   of calculating corrective change.\n  def idempotent?\n    self.class.idempotent\n  end\n\n  # @return [Symbol] the name of the property as stated when the property was created.\n  # @note A property class (just like a parameter class) describes one specific property and\n  #   can only be used once within one type's inheritance chain.\n  def name\n    self.class.name\n  end\n\n  # @return [Boolean] whether this property is in noop mode or not.\n  # Returns whether this property is in noop mode or not; if a difference between the\n  # _is_ and _should_ values should be acted on or not.\n  # The noop mode is a transitive setting. The mode is checked in this property, then in\n  # the _associated resource_ and finally in Puppet[:noop].\n  # @todo This logic is different than Parameter#noop in that the resource noop mode overrides\n  #   the property's mode - in parameter it is the other way around. Bug or feature?\n  #\n  def noop\n    # This is only here to make testing easier.\n    if @resource.respond_to?(:noop?)\n      @resource.noop?\n    elsif defined?(@noop)\n      @noop\n    else\n      Puppet[:noop]\n    end\n  end\n\n  # Retrieves the current value _(is)_ of this property from the provider.\n  # This implementation performs this operation by calling a provider method with the\n  # same name as this property (i.e. if the property name is 'gid', a call to the\n  # 'provider.gid' is expected to return the current value.\n  # @return [Object] what the provider returns as the current value of the property\n  #\n  def retrieve\n    provider.send(self.class.name)\n  end\n\n  # Sets the current _(is)_ value of this property.\n  # The _name_ associated with the value is first obtained by calling {value_name}. A dynamically created setter\n  # method associated with this _name_ is called if it exists, otherwise the value is set using using the provider's\n  # setter method for this property by calling ({#call_provider}).\n  #\n  # @param value [Object] the value to set\n  # @return [Object] returns the result of calling the setter method or {#call_provider}\n  # @raise [Puppet::Error] if there were problems setting the value using the setter method or when the provider\n  #  setter should be used but there is no provider in the associated resource_\n  # @raise [Puppet::ResourceError] if there was a problem setting the value and it was not raised\n  #   as a Puppet::Error. The original exception is wrapped and logged.\n  # @api public\n  #\n  def set(value)\n    # Set a name for looking up associated options like the event.\n    name = self.class.value_name(value)\n    method = self.class.value_option(name, :method)\n    if method && respond_to?(method)\n      begin\n        send(method)\n      rescue Puppet::Error\n        raise\n      rescue => detail\n        error = Puppet::ResourceError.new(_(\"Could not set '%{value}' on %{class_name}: %{detail}\") %\n                                              { value: value, class_name: self.class.name, detail: detail }, @resource.file, @resource.line, detail)\n        error.set_backtrace detail.backtrace\n        Puppet.log_exception(detail, error.message)\n        raise error\n      end\n    else\n      block = self.class.value_option(name, :block)\n      if block\n        # FIXME It'd be better here to define a method, so that\n        # the blocks could return values.\n        instance_eval(&block)\n      else\n        call_provider(value)\n      end\n    end\n  end\n\n  # Returns the wanted _(should)_ value of this property.\n  # If the _array matching mode_ {#match_all?} is true, an array of the wanted values in unmunged format\n  # is returned, else the first value in the array of wanted values in unmunged format is returned.\n  # @return [Array<Object>, Object, nil] Array of values if {#match_all?} else a single value, or nil if there are no\n  #   wanted values.\n  # @raise [Puppet::DevError] if the wanted value is non nil and not an array\n  #\n  # @note This method will potentially return different values than the original values as they are\n  #   converted via munging/unmunging. If the original values are wanted, call {#shouldorig}.\n  #\n  # @see #shouldorig\n  # @api public\n  #\n  def should\n    return nil unless defined?(@should)\n\n    devfail \"should for #{self.class.name} on #{resource.name} is not an array\" unless @should.is_a?(Array)\n\n    if match_all?\n      @should.collect { |val| unmunge(val) }\n    else\n      unmunge(@should[0])\n    end\n  end\n\n  # Sets the wanted _(should)_ value of this property.\n  # If the given value is not already an Array, it will be wrapped in one before being set.\n  # This method also sets the cached original _should_ values returned by {#shouldorig}.\n  #\n  # @param values [Array<Object>, Object] the value(s) to set as the wanted value(s)\n  # @raise [StandardError] when validation of a value fails (see {#validate}).\n  # @api public\n  #\n  def should=(values)\n    values = [values] unless values.is_a?(Array)\n\n    @shouldorig = values\n\n    values.each { |val| validate(val) }\n    @should = values.collect { |val| munge(val) }\n  end\n\n  # Produces a pretty printing string for the given value.\n  # This default implementation calls {#format_value_for_display} on the class. A derived\n  # implementation may perform property specific pretty printing when the _should_ values\n  # are not already in suitable form.\n  # @param value [Object] the value to format as a string\n  # @return [String] a pretty printing string\n  def should_to_s(value)\n    self.class.format_value_for_display(value)\n  end\n\n  # Synchronizes the current value _(is)_ and the wanted value _(should)_ by calling {#set}.\n  # @raise [Puppet::DevError] if {#should} is nil\n  # @todo The implementation of this method is somewhat inefficient as it computes the should\n  #  array twice.\n  def sync\n    devfail \"Got a nil value for should\" unless should\n    set(should)\n  end\n\n  # Asserts that the given value is valid.\n  # If the developer uses a 'validate' hook, this method will get overridden.\n  # @raise [Exception] if the value is invalid, or value can not be handled.\n  # @return [void]\n  # @api private\n  #\n  def unsafe_validate(value)\n    super\n    validate_features_per_value(value)\n  end\n\n  # Asserts that all required provider features are present for the given property value.\n  # @raise [ArgumentError] if a required feature is not present\n  # @return [void]\n  # @api private\n  #\n  def validate_features_per_value(value)\n    features = self.class.value_option(self.class.value_name(value), :required_features)\n    if features\n      features = Array(features)\n      needed_features = features.collect(&:to_s).join(\", \")\n      unless provider.satisfies?(features)\n        # TRANSLATORS 'Provider' refers to a Puppet provider class\n        raise ArgumentError, _(\"Provider %{provider} must have features '%{needed_features}' to set '%{property}' to '%{value}'\") %\n                             { provider: provider.class.name, needed_features: needed_features, property: self.class.name, value: value }\n      end\n    end\n  end\n\n  # @return [Object, nil] Returns the wanted _(should)_ value of this property.\n  def value\n    should\n  end\n\n  # (see #should=)\n  def value=(values)\n    self.should = values\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/aix_object.rb",
    "content": "# frozen_string_literal: true\n\n# Common code for AIX user/group providers.\nclass Puppet::Provider::AixObject < Puppet::Provider\n  desc \"Generic AIX resource provider\"\n\n  # Class representing a MappedObject, which can either be an\n  # AIX attribute or a Puppet property. This class lets us\n  # write something like:\n  #\n  #   attribute = mappings[:aix_attribute][:uid]\n  #   attribute.name\n  #   attribute.convert_property_value(uid)\n  #\n  #   property = mappings[:puppet_property][:id]\n  #   property.name\n  #   property.convert_attribute_value(id)\n  #\n  # NOTE: This is an internal class specific to AixObject. It is\n  # not meant to be used anywhere else. That's why we do not have\n  # any validation code in here.\n  #\n  # NOTE: See the comments in the class-level mappings method to\n  # understand what we mean by pure and impure conversion functions.\n  #\n  # NOTE: The 'mapping' code, including this class, could possibly\n  # be moved to a separate module so that it can be re-used in some\n  # of our other providers. See PUP-9082.\n  class MappedObject\n    attr_reader :name\n\n    def initialize(name, conversion_fn, conversion_fn_code)\n      @name = name\n      @conversion_fn = conversion_fn\n      @conversion_fn_code = conversion_fn_code\n\n      return unless pure_conversion_fn?\n\n      # Our conversion function is pure, so we can go ahead\n      # and define it. This way, we can use this MappedObject\n      # at the class-level as well as at the instance-level.\n      define_singleton_method(@conversion_fn) do |value|\n        @conversion_fn_code.call(value)\n      end\n    end\n\n    def pure_conversion_fn?\n      @conversion_fn_code.arity == 1\n    end\n\n    # Sets our MappedObject's provider. This only makes sense\n    # if it has an impure conversion function. We will call this\n    # in the instance-level mappings method after the provider\n    # instance has been created to define our conversion function.\n    # Note that a MappedObject with an impure conversion function\n    # cannot be used at the class level.\n    def set_provider(provider)\n      define_singleton_method(@conversion_fn) do |value|\n        @conversion_fn_code.call(provider, value)\n      end\n    end\n  end\n\n  class << self\n    #-------------\n    # Mappings\n    # ------------\n\n    def mappings\n      return @mappings if @mappings\n\n      @mappings = {}\n      @mappings[:aix_attribute] = {}\n      @mappings[:puppet_property] = {}\n\n      @mappings\n    end\n\n    # Add a mapping from a Puppet property to an AIX attribute. The info must include:\n    #\n    #   * :puppet_property       -- The puppet property corresponding to this attribute\n    #   * :aix_attribute         -- The AIX attribute corresponding to this attribute. Defaults\n    #                            to puppet_property if this is not provided.\n    #   * :property_to_attribute -- A lambda that converts a Puppet Property to an AIX attribute\n    #                            value. Defaults to the identity function if not provided.\n    #   * :attribute_to_property -- A lambda that converts an AIX attribute to a Puppet property.\n    #                            Defaults to the identity function if not provided.\n    #\n    # NOTE: The lambdas for :property_to_attribute or :attribute_to_property can be 'pure'\n    # or 'impure'. A 'pure' lambda is one that needs only the value to do the conversion,\n    # while an 'impure' lambda is one that requires the provider instance along with the\n    # value. 'Pure' lambdas have the interface 'do |value| ...' while 'impure' lambdas have\n    # the interface 'do |provider, value| ...'.\n    #\n    # NOTE: 'Impure' lambdas are useful in case we need to generate more specific error\n    # messages or pass-in instance-specific command-line arguments.\n    def mapping(info = {})\n      identity_fn = ->(x) { x }\n      info[:aix_attribute] ||= info[:puppet_property]\n      info[:property_to_attribute] ||= identity_fn\n      info[:attribute_to_property] ||= identity_fn\n\n      mappings[:aix_attribute][info[:puppet_property]] = MappedObject.new(\n        info[:aix_attribute],\n        :convert_property_value,\n        info[:property_to_attribute]\n      )\n      mappings[:puppet_property][info[:aix_attribute]] = MappedObject.new(\n        info[:puppet_property],\n        :convert_attribute_value,\n        info[:attribute_to_property]\n      )\n    end\n\n    # Creates a mapping from a purely numeric Puppet property to\n    # an attribute\n    def numeric_mapping(info = {})\n      property = info[:puppet_property]\n\n      # We have this validation here b/c not all numeric properties\n      # handle this at the property level (e.g. like the UID). Given\n      # that, we might as well go ahead and do this validation for all\n      # of our numeric properties. Doesn't hurt.\n      info[:property_to_attribute] = lambda do |value|\n        unless value.is_a?(Integer)\n          raise ArgumentError, _(\"Invalid value %{value}: %{property} must be an Integer!\") % { value: value, property: property }\n        end\n\n        value.to_s\n      end\n\n      # AIX will do the right validation to ensure numeric attributes\n      # can't be set to non-numeric values, so no need for the extra clutter.\n      info[:attribute_to_property] = lambda do |value| # rubocop:disable Style/SymbolProc\n        value.to_i\n      end\n\n      mapping(info)\n    end\n\n    #-------------\n    # Useful Class Methods\n    # ------------\n\n    # Defines the getter and setter methods for each Puppet property that's mapped\n    # to an AIX attribute. We define only a getter for the :attributes property.\n    #\n    # Provider subclasses should call this method after they've defined all of\n    # their <puppet_property> => <aix_attribute> mappings.\n    def mk_resource_methods\n      # Define the Getter methods for each of our properties + the attributes\n      # property\n      properties = [:attributes]\n      properties += mappings[:aix_attribute].keys\n      properties.each do |property|\n        # Define the getter\n        define_method(property) do\n          get(property)\n        end\n\n        # We have a custom setter for the :attributes property,\n        # so no need to define it.\n        next if property == :attributes\n\n        # Define the setter\n        define_method(\"#{property}=\".to_sym) do |value|\n          set(property, value)\n        end\n      end\n    end\n\n    # This helper splits a list separated by sep into its corresponding\n    # items. Note that a key precondition here is that none of the items\n    # in the list contain sep.\n    #\n    # Let A be the return value. Then one of our postconditions is:\n    #   A.join(sep) == list\n    #\n    # NOTE: This function is only used by the parse_colon_separated_list\n    # function below. It is meant to be an inner lambda. The reason it isn't\n    # here is so we avoid having to create a proc. object for the split_list\n    # lambda each time parse_colon_separated_list is invoked. This will happen\n    # quite often since it is used at the class level and at the instance level.\n    # Since this function is meant to be an inner lambda and thus not exposed\n    # anywhere else, we do not have any unit tests for it. These test cases are\n    # instead covered by the unit tests for parse_colon_separated_list\n    def split_list(list, sep)\n      return [\"\"] if list.empty?\n\n      list.split(sep, -1)\n    end\n\n    # Parses a colon-separated list. Example includes something like:\n    #   <item1>:<item2>:<item3>:<item4>\n    #\n    # Returns an array of the parsed items, e.g.\n    #   [ <item1>, <item2>, <item3>, <item4> ]\n    #\n    # Note that colons inside items are escaped by #!\n    def parse_colon_separated_list(colon_list)\n      # ALGORITHM:\n      # Treat the colon_list as a list separated by '#!:' We will get\n      # something like:\n      #     [ <chunk1>, <chunk2>, ... <chunkn> ]\n      #\n      # Each chunk is now a list separated by ':' and none of the items\n      # in each chunk contains an escaped ':'. Now, split each chunk on\n      # ':' to get:\n      #     [ [<piece11>, ..., <piece1n>], [<piece21>, ..., <piece2n], ... ]\n      #\n      # Now note that <item1> = <piece11>, <item2> = <piece12> in our original\n      # list, and that <itemn> = <piece1n>#!:<piece21>. This is the main idea\n      # behind what our inject method is trying to do at the end, except that\n      # we replace '#!:' with ':' since the colons are no longer escaped.\n      chunks = split_list(colon_list, '#!:')\n      chunks.map! { |chunk| split_list(chunk, ':') }\n\n      chunks.inject do |accum, chunk|\n        left = accum.pop\n        right = chunk.shift\n\n        accum.push(\"#{left}:#{right}\")\n        accum += chunk\n\n        accum\n      end\n    end\n\n    # Parses the AIX objects from the command output, returning an array of\n    # hashes with each hash having the following schema:\n    #   {\n    #     :name       => <object_name>\n    #     :attributes => <object_attributes>\n    #   }\n    #\n    # Output should be of the form\n    #   #name:<attr1>:<attr2> ...\n    #   <name>:<value1>:<value2> ...\n    #   #name:<attr1>:<attr2> ...\n    #   <name>:<value1>:<value2> ...\n    #\n    # NOTE: We need to parse the colon-formatted output in case we have\n    # space-separated attributes (e.g. 'gecos'). \":\" characters are escaped\n    # with a \"#!\".\n    def parse_aix_objects(output)\n      # Object names cannot begin with '#', so we are safe to\n      # split individual users this way. We do not have to worry\n      # about an empty list either since there is guaranteed to be\n      # at least one instance of an AIX object (e.g. at least one\n      # user or one group on the system).\n      _, *objects = output.chomp.split(/^#/)\n\n      objects.map! do |object|\n        attributes_line, values_line = object.chomp.split(\"\\n\")\n\n        attributes = parse_colon_separated_list(attributes_line.chomp)\n        attributes.map!(&:to_sym)\n\n        values = parse_colon_separated_list(values_line.chomp)\n\n        attributes_hash = attributes.zip(values).to_h\n\n        object_name = attributes_hash.delete(:name)\n\n        [[:name, object_name.to_s], [:attributes, attributes_hash]].to_h\n      end\n\n      objects\n    end\n\n    # Lists all instances of the given object, taking in an optional set\n    # of ia_module arguments. Returns an array of hashes, each hash\n    # having the schema\n    #   {\n    #     :name => <object_name>\n    #     :id   => <object_id>\n    #   }\n    def list_all(ia_module_args = [])\n      cmd = [command(:list), '-c', *ia_module_args, '-a', 'id', 'ALL']\n      parse_aix_objects(execute(cmd)).to_a.map do |object|\n        name = object[:name]\n        id = object[:attributes].delete(:id)\n\n        { name: name, id: id }\n      end\n    end\n\n    #-------------\n    # Provider API\n    # ------------\n\n    def instances\n      list_all.to_a.map! do |object|\n        new({ :name => object[:name] })\n      end\n    end\n  end\n\n  # Instantiate our mappings. These need to be at the instance-level\n  # since some of our mapped objects may have impure conversion functions\n  # that need our provider instance.\n  def mappings\n    return @mappings if @mappings\n\n    @mappings = {}\n    self.class.mappings.each do |type, mapped_objects|\n      @mappings[type] = {}\n      mapped_objects.each do |input, mapped_object|\n        if mapped_object.pure_conversion_fn?\n          # Our mapped_object has a pure conversion function so we\n          # can go ahead and use it as-is.\n          @mappings[type][input] = mapped_object\n          next\n        end\n\n        # Otherwise, we need to dup it and set its provider to our\n        # provider instance. The dup is necessary so that we do not\n        # touch the class-level mapped object.\n        @mappings[type][input] = mapped_object.dup\n        @mappings[type][input].set_provider(self)\n      end\n    end\n\n    @mappings\n  end\n\n  # Converts the given attributes hash to CLI args.\n  def attributes_to_args(attributes)\n    attributes.map do |attribute, value|\n      \"#{attribute}=#{value}\"\n    end\n  end\n\n  def ia_module_args\n    raise ArgumentError, _(\"Cannot have both 'forcelocal' and 'ia_load_module' at the same time!\") if @resource[:ia_load_module] && @resource[:forcelocal]\n    return [\"-R\", @resource[:ia_load_module].to_s] if @resource[:ia_load_module]\n    return [\"-R\", \"files\"] if @resource[:forcelocal]\n\n    []\n  end\n\n  def lscmd\n    [self.class.command(:list), '-c'] + ia_module_args + [@resource[:name]]\n  end\n\n  def addcmd(attributes)\n    attribute_args = attributes_to_args(attributes)\n    [self.class.command(:add)] + ia_module_args + attribute_args + [@resource[:name]]\n  end\n\n  def deletecmd\n    [self.class.command(:delete)] + ia_module_args + [@resource[:name]]\n  end\n\n  def modifycmd(new_attributes)\n    attribute_args = attributes_to_args(new_attributes)\n    [self.class.command(:modify)] + ia_module_args + attribute_args + [@resource[:name]]\n  end\n\n  # Modifies the AIX object by setting its new attributes.\n  def modify_object(new_attributes)\n    execute(modifycmd(new_attributes))\n    object_info(true)\n  end\n\n  # Gets a Puppet property's value from object_info\n  def get(property)\n    return :absent unless exists?\n\n    object_info[property] || :absent\n  end\n\n  # Sets a mapped Puppet property's value.\n  def set(property, value)\n    aix_attribute = mappings[:aix_attribute][property]\n    modify_object(\n      { aix_attribute.name => aix_attribute.convert_property_value(value) }\n    )\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, _(\"Could not set %{property} on %{resource}[%{name}]: %{detail}\") % { property: property, resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n  end\n\n  # This routine validates our new attributes property value to ensure\n  # that it does not contain any Puppet properties.\n  def validate_new_attributes(new_attributes)\n    # Gather all of the <puppet property>, <aix attribute> conflicts to print\n    # them all out when we create our error message. This makes it easy for the\n    # user to update their manifest based on our error message.\n    conflicts = {}\n    mappings[:aix_attribute].each do |property, aix_attribute|\n      next unless new_attributes.key?(aix_attribute.name)\n\n      conflicts[:properties] ||= []\n      conflicts[:properties].push(property)\n\n      conflicts[:attributes] ||= []\n      conflicts[:attributes].push(aix_attribute.name)\n    end\n\n    return if conflicts.empty?\n\n    properties, attributes = conflicts.keys.map do |key|\n      conflicts[key].map! { |name| \"'#{name}'\" }.join(', ')\n    end\n\n    detail = _(\"attributes is setting the %{properties} properties via. the %{attributes} attributes, respectively! Please specify these property values in the resource declaration instead.\") % { properties: properties, attributes: attributes }\n\n    raise Puppet::Error, _(\"Could not set attributes on %{resource}[%{name}]: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail }\n  end\n\n  # Modifies the attribute property. Note we raise an error if the user specified\n  # an AIX attribute corresponding to a Puppet property.\n  def attributes=(new_attributes)\n    validate_new_attributes(new_attributes)\n    modify_object(new_attributes)\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, _(\"Could not set attributes on %{resource}[%{name}]: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n  end\n\n  # Collects the current property values of all mapped properties +\n  # the attributes property.\n  def object_info(refresh = false)\n    return @object_info if @object_info && !refresh\n\n    @object_info = nil\n\n    begin\n      output = execute(lscmd)\n    rescue Puppet::ExecutionFailure\n      Puppet.debug(_(\"aix.object_info(): Could not find %{resource}[%{name}]\") % { resource: @resource.class.name, name: @resource.name })\n\n      return @object_info\n    end\n\n    # If lscmd succeeds, then output will contain our object's information.\n    # Thus, .parse_aix_objects will always return a single element array.\n    aix_attributes = self.class.parse_aix_objects(output).first[:attributes]\n    aix_attributes.each do |attribute, value|\n      @object_info ||= {}\n\n      # If our attribute has a Puppet property, then we store that. Else, we store it as part\n      # of our :attributes property hash\n      if (property = mappings[:puppet_property][attribute])\n        @object_info[property.name] = property.convert_attribute_value(value)\n      else\n        @object_info[:attributes] ||= {}\n        @object_info[:attributes][attribute] = value\n      end\n    end\n\n    @object_info\n  end\n\n  #-------------\n  # Methods that manage the ensure property\n  # ------------\n\n  # Check that the AIX object exists\n  def exists?\n    !object_info.nil?\n  end\n\n  # Creates a new instance of the resource\n  def create\n    attributes = @resource.should(:attributes) || {}\n    validate_new_attributes(attributes)\n\n    mappings[:aix_attribute].each do |property, aix_attribute|\n      property_should = @resource.should(property)\n      next if property_should.nil?\n\n      attributes[aix_attribute.name] = aix_attribute.convert_property_value(property_should)\n    end\n\n    execute(addcmd(attributes))\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, _(\"Could not create %{resource} %{name}: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n  end\n\n  # Deletes this instance resource\n  def delete\n    execute(deletecmd)\n\n    # Recollect the object info so that our current properties reflect\n    # the actual state of the system. Otherwise, puppet resource reports\n    # the wrong info. at the end. Note that this should return nil.\n    object_info(true)\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, _(\"Could not delete %{resource} %{name}: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/command.rb",
    "content": "# frozen_string_literal: true\n\n# A command that can be executed on the system\nclass Puppet::Provider::Command\n  attr_reader :executable\n  attr_reader :name\n\n  # @param [String] name A way of referencing the name\n  # @param [String] executable The path to the executable file\n  # @param resolver An object for resolving the executable to an absolute path (usually Puppet::Util)\n  # @param executor An object for performing the actual execution of the command (usually Puppet::Util::Execution)\n  # @param [Hash] options Extra options to be used when executing (see Puppet::Util::Execution#execute)\n  def initialize(name, executable, resolver, executor, options = {})\n    @name = name\n    @executable = executable\n    @resolver = resolver\n    @executor = executor\n    @options = options\n  end\n\n  # @param args [Array<String>] Any command line arguments to pass to the executable\n  # @return The output from the command\n  def execute(*args)\n    resolved_executable = @resolver.which(@executable) or raise Puppet::MissingCommand, _(\"Command %{name} is missing\") % { name: @name }\n    @executor.execute([resolved_executable] + args, @options)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/confine.rb",
    "content": "# frozen_string_literal: true\n\n# Confines have been moved out of the provider as they are also used for other things.\n# This provides backwards compatibility for people still including this old location.\nrequire_relative '../../puppet/provider'\nrequire_relative '../../puppet/confine'\n\nPuppet::Provider::Confine = Puppet::Confine\n"
  },
  {
    "path": "lib/puppet/provider/exec/posix.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/exec'\n\nPuppet::Type.type(:exec).provide :posix, :parent => Puppet::Provider::Exec do\n  has_feature :umask\n  confine :feature => :posix\n  defaultfor :feature => :posix\n\n  desc <<-EOT\n    Executes external binaries by invoking Ruby's `Kernel.exec`.\n    When the command is a string, it will be executed directly,\n    without a shell, if it follows these rules:\n     - no meta characters\n     - no shell reserved word and no special built-in\n\n    When the command is an Array of Strings, passed as `[cmdname, arg1, ...]`\n    it will be executed directly(the first element is taken as a command name\n    and the rest are passed as parameters to command with no shell expansion)\n    This is a safer and more predictable way to execute most commands,\n    but prevents the use of globbing and shell built-ins (including control\n    logic like \"for\" and \"if\" statements).\n\n    If the use of globbing and shell built-ins is desired, please check\n    the `shell` provider\n\n  EOT\n\n  # Verify that we have the executable\n  def checkexe(command)\n    exe = extractexe(command)\n\n    if File.expand_path(exe) == exe\n      if !Puppet::FileSystem.exist?(exe)\n        raise ArgumentError, _(\"Could not find command '%{exe}'\") % { exe: exe }\n      elsif !File.file?(exe)\n        raise ArgumentError, _(\"'%{exe}' is a %{klass}, not a file\") % { exe: exe, klass: File.ftype(exe) }\n      elsif !File.executable?(exe)\n        raise ArgumentError, _(\"'%{exe}' is not executable\") % { exe: exe }\n      end\n\n      return\n    end\n\n    if resource[:path]\n      Puppet::Util.withenv :PATH => resource[:path].join(File::PATH_SEPARATOR) do\n        return if which(exe)\n      end\n    end\n\n    # 'which' will only return the command if it's executable, so we can't\n    # distinguish not found from not executable\n    raise ArgumentError, _(\"Could not find command '%{exe}'\") % { exe: exe }\n  end\n\n  def run(command, check = false)\n    if resource[:umask]\n      Puppet::Util.withumask(resource[:umask]) { super(command, check) }\n    else\n      super(command, check)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/exec/shell.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:exec).provide :shell, :parent => :posix do\n  include Puppet::Util::Execution\n\n  confine :feature => :posix\n\n  desc <<-EOT\n    Passes the provided command through `/bin/sh`; only available on\n    POSIX systems. This allows the use of shell globbing and built-ins, and\n    does not require that the path to a command be fully-qualified. Although\n    this can be more convenient than the `posix` provider, it also means that\n    you need to be more careful with escaping; as ever, with great power comes\n    etc. etc.\n\n    This provider closely resembles the behavior of the `exec` type\n    in Puppet 0.25.x.\n  EOT\n\n  def run(command, check = false)\n    super(['/bin/sh', '-c', command], check)\n  end\n\n  def validatecmd(command)\n    true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/exec/windows.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/exec'\n\nPuppet::Type.type(:exec).provide :windows, :parent => Puppet::Provider::Exec do\n  confine    'os.name' => :windows\n  defaultfor 'os.name' => :windows\n\n  desc <<-'EOT'\n    Execute external binaries on Windows systems. As with the `posix`\n    provider, this provider directly calls the command with the arguments\n    given, without passing it through a shell or performing any interpolation.\n    To use shell built-ins --- that is, to emulate the `shell` provider on\n    Windows --- a command must explicitly invoke the shell:\n\n        exec {'echo foo':\n          command => 'cmd.exe /c echo \"foo\"',\n        }\n\n    If no extension is specified for a command, Windows will use the `PATHEXT`\n    environment variable to locate the executable.\n\n    **Note on PowerShell scripts:** PowerShell's default `restricted`\n    execution policy doesn't allow it to run saved scripts. To run PowerShell\n    scripts, specify the `remotesigned` execution policy as part of the\n    command:\n\n        exec { 'test':\n          path    => 'C:/Windows/System32/WindowsPowerShell/v1.0',\n          command => 'powershell -executionpolicy remotesigned -file C:/test.ps1',\n        }\n\n  EOT\n\n  # Verify that we have the executable\n  def checkexe(command)\n    exe = extractexe(command)\n\n    if absolute_path?(exe)\n      if !Puppet::FileSystem.exist?(exe)\n        raise ArgumentError, _(\"Could not find command '%{exe}'\") % { exe: exe }\n      elsif !File.file?(exe)\n        raise ArgumentError, _(\"'%{exe}' is a %{klass}, not a file\") % { exe: exe, klass: File.ftype(exe) }\n      end\n\n      return\n    end\n\n    if resource[:path]\n      Puppet::Util.withenv('PATH' => resource[:path].join(File::PATH_SEPARATOR)) do\n        return if which(exe)\n      end\n    end\n\n    raise ArgumentError, _(\"Could not find command '%{exe}'\") % { exe: exe }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/exec.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/provider'\nrequire_relative '../../puppet/util/execution'\n\nclass Puppet::Provider::Exec < Puppet::Provider\n  include Puppet::Util::Execution\n\n  def environment\n    env = {}\n\n    if (path = resource[:path])\n      env[:PATH] = path.join(File::PATH_SEPARATOR)\n    end\n\n    return env unless (envlist = resource[:environment])\n\n    envlist = [envlist] unless envlist.is_a? Array\n    envlist.each do |setting|\n      unless (match = /^(\\w+)=((.|\\n)*)$/.match(setting))\n        warning _(\"Cannot understand environment setting %{setting}\") % { setting: setting.inspect }\n        next\n      end\n      var = match[1]\n      value = match[2]\n\n      if env.include?(var) || env.include?(var.to_sym)\n        warning _(\"Overriding environment setting '%{var}' with '%{value}'\") % { var: var, value: value }\n      end\n\n      if value.nil? || value.empty?\n        msg = _(\"Empty environment setting '%{var}'\") % { var: var }\n        Puppet.warn_once('undefined_variables', \"empty_env_var_#{var}\", msg, resource.file, resource.line)\n      end\n\n      env[var] = value\n    end\n\n    env\n  end\n\n  def run(command, check = false)\n    output = nil\n    sensitive = resource.parameters[:command].sensitive\n\n    checkexe(command)\n\n    debug \"Executing#{check ? ' check' : ''} '#{sensitive ? '[redacted]' : command}'\"\n\n    # Ruby 2.1 and later interrupt execution in a way that bypasses error\n    # handling by default. Passing Timeout::Error causes an exception to be\n    # raised that can be rescued inside of the block by cleanup routines.\n    #\n    # This is backwards compatible all the way to Ruby 1.8.7.\n    Timeout.timeout(resource[:timeout], Timeout::Error) do\n      cwd = resource[:cwd]\n      # It's ok if cwd is nil. In that case Puppet::Util::Execution.execute() simply will not attempt to\n      # change the working directory, which is exactly the right behavior when no cwd parameter is\n      # expressed on the resource.  Moreover, attempting to change to the directory that is already\n      # the working directory can fail under some circumstances, so avoiding the directory change attempt\n      # is preferable to defaulting cwd to that directory.\n\n      # note that we are passing \"false\" for the \"override_locale\" parameter, which ensures that the user's\n      # default/system locale will be respected.  Callers may override this behavior by setting locale-related\n      # environment variables (LANG, LC_ALL, etc.) in their 'environment' configuration.\n      output = Puppet::Util::Execution.execute(\n        command,\n        :failonfail => false,\n        :combine => true,\n        :cwd => cwd,\n        :uid => resource[:user], :gid => resource[:group],\n        :override_locale => false,\n        :custom_environment => environment(),\n        :sensitive => sensitive\n      )\n    end\n    # The shell returns 127 if the command is missing.\n    if output.exitstatus == 127\n      raise ArgumentError, output\n    end\n\n    # Return output twice as processstatus was returned before, but only exitstatus was ever called.\n    # Output has the exitstatus on it so it is returned instead. This is here twice as changing this\n    #  would result in a change to the underlying API.\n    [output, output]\n  end\n\n  def extractexe(command)\n    if command.is_a? Array\n      command.first\n    else\n      match = /^\"([^\"]+)\"|^'([^']+)'/.match(command)\n      if match\n        # extract whichever of the two sides matched the content.\n        match[1] or match[2]\n      else\n        command.split(/ /)[0]\n      end\n    end\n  end\n\n  def validatecmd(command)\n    exe = extractexe(command)\n    # if we're not fully qualified, require a path\n    self.fail _(\"'%{exe}' is not qualified and no path was specified. Please qualify the command or specify a path.\") % { exe: exe } if !absolute_path?(exe) and resource[:path].nil?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/file/posix.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:file).provide :posix do\n  desc \"Uses POSIX functionality to manage file ownership and permissions.\"\n\n  confine :feature => :posix\n  has_features :manages_symlinks\n\n  include Puppet::Util::POSIX\n  include Puppet::Util::Warnings\n\n  require 'etc'\n  require_relative '../../../puppet/util/selinux'\n\n  class << self\n    def selinux_handle\n      return nil unless Puppet::Util::SELinux.selinux_support?\n\n      # selabel_open takes 3 args: backend, options, and nopt. The backend param\n      # is a constant, SELABEL_CTX_FILE, which happens to be 0. Since options is\n      # nil, nopt can be 0 since nopt represents the # of options specified.\n      @selinux_handle ||= Selinux.selabel_open(Selinux::SELABEL_CTX_FILE, nil, 0)\n    end\n\n    def post_resource_eval\n      if @selinux_handle\n        Selinux.selabel_close(@selinux_handle)\n        @selinux_handle = nil\n      end\n    end\n  end\n\n  def uid2name(id)\n    return id.to_s if id.is_a?(Symbol) or id.is_a?(String)\n    return nil if id > Puppet[:maximum_uid].to_i\n\n    begin\n      user = Etc.getpwuid(id)\n    rescue TypeError, ArgumentError\n      return nil\n    end\n\n    if user.uid == \"\"\n      nil\n    else\n      user.name\n    end\n  end\n\n  # Determine if the user is valid, and if so, return the UID\n  def name2uid(value)\n    Integer(value)\n  rescue\n    uid(value) || false\n  end\n\n  def gid2name(id)\n    return id.to_s if id.is_a?(Symbol) or id.is_a?(String)\n    return nil if id > Puppet[:maximum_uid].to_i\n\n    begin\n      group = Etc.getgrgid(id)\n    rescue TypeError, ArgumentError\n      return nil\n    end\n\n    if group.gid == \"\"\n      nil\n    else\n      group.name\n    end\n  end\n\n  def name2gid(value)\n    Integer(value)\n  rescue\n    gid(value) || false\n  end\n\n  def owner\n    stat = resource.stat\n    unless stat\n      return :absent\n    end\n\n    currentvalue = stat.uid\n\n    # On OS X, files that are owned by -2 get returned as really\n    # large UIDs instead of negative ones.  This isn't a Ruby bug,\n    # it's an OS X bug, since it shows up in perl, too.\n    if currentvalue > Puppet[:maximum_uid].to_i\n      warning _(\"Apparently using negative UID (%{currentvalue}) on a platform that does not consistently handle them\") % { currentvalue: currentvalue }\n      currentvalue = :silly\n    end\n\n    currentvalue\n  end\n\n  def owner=(should)\n    # Set our method appropriately, depending on links.\n    if resource[:links] == :manage\n      method = :lchown\n    else\n      method = :chown\n    end\n\n    begin\n      File.send(method, should, nil, resource[:path])\n    rescue => detail\n      raise Puppet::Error, _(\"Failed to set owner to '%{should}': %{detail}\") % { should: should, detail: detail }, detail.backtrace\n    end\n  end\n\n  def group\n    stat = resource.stat\n    return :absent unless stat\n\n    currentvalue = stat.gid\n\n    # On OS X, files that are owned by -2 get returned as really\n    # large GIDs instead of negative ones.  This isn't a Ruby bug,\n    # it's an OS X bug, since it shows up in perl, too.\n    if currentvalue > Puppet[:maximum_uid].to_i\n      warning _(\"Apparently using negative GID (%{currentvalue}) on a platform that does not consistently handle them\") % { currentvalue: currentvalue }\n      currentvalue = :silly\n    end\n\n    currentvalue\n  end\n\n  def group=(should)\n    # Set our method appropriately, depending on links.\n    if resource[:links] == :manage\n      method = :lchown\n    else\n      method = :chown\n    end\n\n    begin\n      File.send(method, nil, should, resource[:path])\n    rescue => detail\n      raise Puppet::Error, _(\"Failed to set group to '%{should}': %{detail}\") % { should: should, detail: detail }, detail.backtrace\n    end\n  end\n\n  def mode\n    stat = resource.stat\n    if stat\n      (stat.mode & 0o07777).to_s(8).rjust(4, '0')\n    else\n      :absent\n    end\n  end\n\n  def mode=(value)\n    File.chmod(value.to_i(8), resource[:path])\n  rescue => detail\n    error = Puppet::Error.new(_(\"failed to set mode %{mode} on %{path}: %{message}\") % { mode: mode, path: resource[:path], message: detail.message })\n    error.set_backtrace detail.backtrace\n    raise error\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/file/windows.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:file).provide :windows do\n  desc \"Uses Microsoft Windows functionality to manage file ownership and permissions.\"\n\n  confine 'os.name' => :windows\n  has_feature :manages_symlinks if Puppet.features.manages_symlinks?\n\n  include Puppet::Util::Warnings\n\n  if Puppet::Util::Platform.windows?\n    require_relative '../../../puppet/util/windows'\n    include Puppet::Util::Windows::Security\n  end\n\n  # Determine if the account is valid, and if so, return the UID\n  def name2id(value)\n    Puppet::Util::Windows::SID.name_to_sid(value)\n  end\n\n  # If it's a valid SID, get the name. Otherwise, it's already a name,\n  # so just return it.\n  def id2name(id)\n    if Puppet::Util::Windows::SID.valid_sid?(id)\n      Puppet::Util::Windows::SID.sid_to_name(id)\n    else\n      id\n    end\n  end\n\n  # We use users and groups interchangeably, so use the same methods for both\n  # (the type expects different methods, so we have to oblige).\n  alias :uid2name :id2name\n  alias :gid2name :id2name\n\n  alias :name2gid :name2id\n  alias :name2uid :name2id\n\n  def owner\n    return :absent unless resource.stat\n\n    get_owner(resource[:path])\n  end\n\n  def owner=(should)\n    set_owner(should, resolved_path)\n  rescue => detail\n    raise Puppet::Error, _(\"Failed to set owner to '%{should}': %{detail}\") % { should: should, detail: detail }, detail.backtrace\n  end\n\n  def group\n    return :absent unless resource.stat\n\n    get_group(resource[:path])\n  end\n\n  def group=(should)\n    set_group(should, resolved_path)\n  rescue => detail\n    raise Puppet::Error, _(\"Failed to set group to '%{should}': %{detail}\") % { should: should, detail: detail }, detail.backtrace\n  end\n\n  def mode\n    if resource.stat\n      mode = get_mode(resource[:path])\n      mode ? mode.to_s(8).rjust(4, '0') : :absent\n    else\n      :absent\n    end\n  end\n\n  def mode=(value)\n    managing_owner = !resource[:owner].nil?\n    managing_group = !resource[:group].nil?\n    set_mode(value.to_i(8), resource[:path], true, managing_owner, managing_group)\n  rescue => detail\n    error = Puppet::Error.new(_(\"failed to set mode %{mode} on %{path}: %{message}\") % { mode: mode, path: resource[:path], message: detail.message })\n    error.set_backtrace detail.backtrace\n    raise error\n  end\n\n  def validate\n    if [:owner, :group, :mode].any? { |p| resource[p] } and !supports_acl?(resource[:path])\n      resource.fail(_(\"Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS\"))\n    end\n  end\n\n  # munge the windows group permissions if the user or group are set to SYSTEM\n  #\n  # when SYSTEM user is the group or user and the resoure is not managing them then treat\n  # the resource as insync if System has FullControl access.\n  #\n  # @param [String] current - the current mode returned by the resource\n  # @param [String] should  - what the mode should be\n  #\n  # @return [String, nil] munged mode or nil if the resource should be out of sync\n  def munge_windows_system_group(current, should)\n    [\n      {\n        'type' => 'group',\n        'resource' => resource[:group],\n        'set_to_user' => group,\n        'fullcontrol' => \"070\".to_i(8),\n        'remove_mask' => \"707\".to_i(8),\n        'should_mask' => (should[0].to_i(8) & \"070\".to_i(8)),\n      },\n      {\n        'type' => 'owner',\n        'resource' => resource[:owner],\n        'set_to_user' => owner,\n        'fullcontrol' => \"700\".to_i(8),\n        'remove_mask' => \"077\".to_i(8),\n        'should_mask' => (should[0].to_i(8) & \"700\".to_i(8)),\n      }\n    ].each do |mode_part|\n      if mode_part['resource'].nil? && (mode_part['set_to_user'] == Puppet::Util::Windows::SID::LocalSystem)\n        if (current.to_i(8) & mode_part['fullcontrol']) == mode_part['fullcontrol']\n          # Since the group is LocalSystem, and the permissions are FullControl,\n          # replace the value returned with the value expected. This will treat\n          # this specific situation as \"insync\"\n          current = ((current.to_i(8) & mode_part['remove_mask']) | mode_part['should_mask']).to_s(8).rjust(4, '0')\n        else\n          # If the SYSTEM account does _not_ have FullControl in this scenario, we should\n          # force the resource out of sync no matter what.\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          Puppet.debug { _(\"%{resource_name}: %{mode_part_type} set to SYSTEM. SYSTEM permissions cannot be set below FullControl ('7')\") % { resource_name: resource[:name], mode_part_type: mode_part['type'] } }\n          return nil\n        end\n      end\n    end\n    current\n  end\n\n  attr_reader :file\n\n  private\n\n  def file\n    @file ||= Puppet::FileSystem.pathname(resource[:path])\n  end\n\n  def resolved_path\n    path = file()\n    # under POSIX, :manage means use lchown - i.e. operate on the link\n    return path.to_s if resource[:links] == :manage\n\n    # otherwise, use chown -- that will resolve the link IFF it is a link\n    # otherwise it will operate on the path\n    Puppet::FileSystem.symlink?(path) ? Puppet::FileSystem.readlink(path) : path.to_s\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/group/aix.rb",
    "content": "# frozen_string_literal: true\n\n# Group Puppet provider for AIX. It uses standard commands to manage groups:\n#  mkgroup, rmgroup, lsgroup, chgroup\nrequire_relative '../../../puppet/provider/aix_object'\n\nPuppet::Type.type(:group).provide :aix, :parent => Puppet::Provider::AixObject do\n  desc \"Group management for AIX.\"\n\n  # This will the default provider for this platform\n  defaultfor 'os.name' => :aix\n  confine 'os.name' => :aix\n\n  # Commands that manage the element\n  commands :list      => \"/usr/sbin/lsgroup\"\n  commands :add       => \"/usr/bin/mkgroup\"\n  commands :delete    => \"/usr/sbin/rmgroup\"\n  commands :modify    => \"/usr/bin/chgroup\"\n\n  # Provider features\n  has_features :manages_aix_lam\n  has_features :manages_members\n  has_features :manages_local_users_and_groups\n\n  class << self\n    # Used by the AIX user provider. Returns a hash of:\n    #   {\n    #     :name => <group_name>,\n    #     :gid  => <gid>\n    #   }\n    #\n    # that matches the group, which can either be the group name or\n    # the gid. Takes an optional set of ia_module_args\n    def find(group, ia_module_args = [])\n      groups = list_all(ia_module_args)\n\n      id_property = mappings[:puppet_property][:id]\n\n      if group.is_a?(String)\n        # Find by name\n        group_hash = groups.find { |cur_group| cur_group[:name] == group }\n      else\n        # Find by gid\n        group_hash = groups.find do |cur_group|\n          id_property.convert_attribute_value(cur_group[:id]) == group\n        end\n      end\n\n      unless group_hash\n        raise ArgumentError, _(\"No AIX group exists with a group name or gid of %{group}!\") % { group: group }\n      end\n\n      # Convert :id => :gid\n      id = group_hash.delete(:id)\n      group_hash[:gid] = id_property.convert_attribute_value(id)\n\n      group_hash\n    end\n\n    # Define some Puppet Property => AIX Attribute (and vice versa)\n    # conversion functions here. This is so we can unit test them.\n\n    def members_to_users(provider, members)\n      members = members.split(',') if members.is_a?(String)\n      unless provider.resource[:auth_membership]\n        current_members = provider.members\n        current_members = [] if current_members == :absent\n        members = (members + current_members).uniq\n      end\n\n      members.join(',')\n    end\n\n    def users_to_members(users)\n      users.split(',')\n    end\n  end\n\n  mapping puppet_property: :members,\n          aix_attribute: :users,\n          property_to_attribute: method(:members_to_users),\n          attribute_to_property: method(:users_to_members)\n\n  numeric_mapping puppet_property: :gid,\n                  aix_attribute: :id\n\n  # Now that we have all of our mappings, let's go ahead and make\n  # the resource methods (property getters + setters for our mapped\n  # properties + a getter for the attributes property).\n  mk_resource_methods\n\n  # We could add this to the top-level members property since the\n  # implementation is not platform-specific; however, it is best\n  # to do it this way so that we do not accidentally break something.\n  # This is ok for now, since we do plan on moving this and the\n  # auth_membership management over to the property class in a future\n  # Puppet release.\n  def members_insync?(current, should)\n    current.sort == @resource.parameter(:members).actual_should(current, should)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/group/directoryservice.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice/directoryservice'\n\nPuppet::Type.type(:group).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do\n  desc \"Group management using DirectoryService on OS X.\n\n  \"\n\n  commands :dscl => \"/usr/bin/dscl\"\n  confine 'os.name' => :darwin\n  defaultfor 'os.name' => :darwin\n  has_feature :manages_members\n\n  def members_insync?(current, should)\n    return false unless current\n\n    if current == :absent\n      should.empty?\n    else\n      current.sort.uniq == should.sort.uniq\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/group/groupadd.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice/objectadd'\nrequire_relative '../../../puppet/util/libuser'\n\nPuppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameService::ObjectAdd do\n  desc \"Group management via `groupadd` and its ilk. The default for most platforms.\n\n To use the `forcelocal` parameter, you need to install the `libuser` package (providing\n  `/usr/sbin/lgroupadd` and `/usr/sbin/luseradd`).\"\n\n  commands :add => \"groupadd\", :delete => \"groupdel\", :modify => \"groupmod\"\n\n  has_feature :system_groups unless %w[HP-UX Solaris].include? Puppet.runtime[:facter].value('os.name')\n\n  verify :gid, _(\"GID must be an integer\") do |value|\n    value.is_a? Integer\n  end\n\n  optional_commands :localadd => \"lgroupadd\", :localdelete => \"lgroupdel\", :localmodify => \"lgroupmod\", :purgemember => \"usermod\"\n\n  has_feature :manages_local_users_and_groups if Puppet.features.libuser?\n  has_feature :manages_members if Puppet.features.libuser? ||\n                                  (Puppet.runtime[:facter].value('os.name') == \"Fedora\" &&\n                                  Puppet.runtime[:facter].value('os.release.major').to_i >= 40)\n\n  # Libuser's modify command 'lgroupmod' requires '-M' flag for member additions.\n  # 'groupmod' command requires the '-aU' flags for it.\n  if Puppet.features.libuser?\n    options :members, :flag => '-M', :method => :mem\n  else\n    options :members, :flag => '-aU', :method => :mem\n  end\n\n  def exists?\n    return !!localgid if @resource.forcelocal?\n\n    super\n  end\n\n  def gid\n    return localgid if @resource.forcelocal?\n\n    get(:gid)\n  end\n\n  def localgid\n    group = findgroup(:group_name, resource[:name])\n    return group[:gid] if group\n\n    false\n  end\n\n  def check_allow_dup\n    # We have to manually check for duplicates when using libuser\n    # because by default duplicates are allowed.  This check is\n    # to ensure consistent behaviour of the useradd provider when\n    # using both useradd and luseradd\n    if !@resource.allowdupe? and @resource.forcelocal?\n      if @resource.should(:gid) and findgroup(:gid, @resource.should(:gid).to_s)\n        raise(Puppet::Error, _(\"GID %{resource} already exists, use allowdupe to force group creation\") % { resource: @resource.should(:gid).to_s })\n      end\n    elsif @resource.allowdupe? and !@resource.forcelocal?\n      return [\"-o\"]\n    end\n    []\n  end\n\n  def create\n    super\n    set(:members, @resource[:members]) if @resource[:members]\n  end\n\n  def addcmd\n    # The localadd command (lgroupadd) must only be called when libuser is supported.\n    if Puppet.features.libuser? && @resource.forcelocal?\n      cmd = [command(:localadd)]\n      @custom_environment = Puppet::Util::Libuser.getenv\n    else\n      cmd = [command(:add)]\n    end\n\n    gid = @resource.should(:gid)\n    if gid\n      unless gid == :absent\n        cmd << flag(:gid) << gid\n      end\n    end\n    cmd += check_allow_dup\n    cmd << \"-r\" if @resource.system? and self.class.system_groups?\n    cmd << @resource[:name]\n    cmd\n  end\n\n  def validate_members(members)\n    members.each do |member|\n      member.split(',').each do |user|\n        Etc.getpwnam(user.strip)\n      end\n    end\n  end\n\n  def modifycmd(param, value)\n    # The localmodify command (lgroupmod) must only be called when libuser is supported.\n    if Puppet.features.libuser? && (@resource.forcelocal? || @resource[:members])\n      cmd = [command(:localmodify)]\n      @custom_environment = Puppet::Util::Libuser.getenv\n    else\n      cmd = [command(:modify)]\n    end\n\n    if param == :members\n      validate_members(value)\n      value = members_to_s(value)\n      purge_members if @resource[:auth_membership] && !members.empty?\n    end\n\n    cmd << flag(param) << value\n    # TODO the group type only really manages gid, so there are currently no\n    # tests for this behavior\n    cmd += check_allow_dup if param == :gid\n    cmd << @resource[:name]\n\n    cmd\n  end\n\n  def deletecmd\n    # The localdelete command (lgroupdel) must only be called when libuser is supported.\n    if Puppet.features.libuser? && @resource.forcelocal?\n      @custom_environment = Puppet::Util::Libuser.getenv\n      [command(:localdelete), @resource[:name]]\n    else\n      [command(:delete), @resource[:name]]\n    end\n  end\n\n  def members_insync?(current, should)\n    current.uniq.sort == @resource.parameter(:members).actual_should(current, should)\n  end\n\n  def members_to_s(current)\n    return '' if current.nil? || !current.is_a?(Array)\n\n    current.join(',')\n  end\n\n  def purge_members\n    # The groupadd provider doesn't have the ability currently to remove members from a group, libuser does.\n    # Use libuser's lgroupmod command to achieve purging members if libuser is supported.\n    # Otherwise use the 'usermod' command.\n    if Puppet.features.libuser?\n      localmodify('-m', members_to_s(members), @resource.name)\n    else\n      members.each do |member|\n        purgemember('-rG', @resource.name, member)\n      end\n    end\n  end\n\n  private\n\n  def findgroup(key, value)\n    group_file = '/etc/group'\n    group_keys = [:group_name, :password, :gid, :user_list]\n\n    unless @groups\n      unless Puppet::FileSystem.exist?(group_file)\n        raise Puppet::Error, \"Forcelocal set for group resource '#{resource[:name]}', but #{group_file} does not exist\"\n      end\n\n      @groups = []\n      Puppet::FileSystem.each_line(group_file) do |line|\n        group = line.chomp.split(':')\n        @groups << group_keys.zip(group).to_h\n      end\n    end\n\n    @groups.find { |param| param[key] == value } || false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/group/ldap.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/ldap'\n\nPuppet::Type.type(:group).provide :ldap, :parent => Puppet::Provider::Ldap do\n  desc \"Group management via LDAP.\n\n    This provider requires that you have valid values for all of the\n    LDAP-related settings in `puppet.conf`, including `ldapbase`.  You will\n    almost definitely need settings for `ldapuser` and `ldappassword` in order\n    for your clients to write to LDAP.\n\n    Note that this provider will automatically generate a GID for you if you do\n    not specify one, but it is a potentially expensive operation, as it\n    iterates across all existing groups to pick the appropriate next one.\"\n\n  confine :feature => :ldap, :false => (Puppet[:ldapuser] == \"\")\n\n  # We're mapping 'members' here because we want to make it\n  # easy for the ldap user provider to manage groups.  This\n  # way it can just use the 'update' method in the group manager,\n  # whereas otherwise it would need to replicate that code.\n  manages(:posixGroup).at(\"ou=Groups\").and.maps :name => :cn, :gid => :gidNumber, :members => :memberUid\n\n  # Find the next gid after the current largest gid.\n  provider = self\n  manager.generates(:gidNumber).with do\n    largest = 500\n    existing = provider.manager.search\n    if existing\n      existing.each do |hash|\n        value = hash[:gid]\n        next unless value\n\n        num = value[0].to_i\n        largest = num if num > largest\n      end\n    end\n    largest + 1\n  end\n\n  # Convert a group name to an id.\n  def self.name2id(group)\n    result = manager.search(\"cn=#{group}\")\n    return nil unless result and result.length > 0\n\n    # Only use the first result.\n    group = result[0]\n    group[:gid][0]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/group/pw.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice/pw'\n\nPuppet::Type.type(:group).provide :pw, :parent => Puppet::Provider::NameService::PW do\n  desc \"Group management via `pw` on FreeBSD and DragonFly BSD.\"\n\n  commands :pw => \"pw\"\n  has_features :manages_members\n\n  defaultfor 'os.name' => [:freebsd, :dragonfly]\n  confine    'os.name' => [:freebsd, :dragonfly]\n\n  options :members, :flag => \"-M\", :method => :mem\n\n  verify :gid, _(\"GID must be an integer\") do |value|\n    value.is_a? Integer\n  end\n\n  def addcmd\n    cmd = [command(:pw), \"groupadd\", @resource[:name]]\n\n    gid = @resource.should(:gid)\n    if gid\n      unless gid == :absent\n        cmd << flag(:gid) << gid\n      end\n    end\n\n    members = @resource.should(:members)\n    if members\n      unless members == :absent\n        if members.is_a?(Array)\n          members = members.join(\",\")\n        end\n        cmd << \"-M\" << members\n      end\n    end\n\n    cmd << \"-o\" if @resource.allowdupe?\n\n    cmd\n  end\n\n  def modifycmd(param, value)\n    # members may be an array, need a comma separated list\n    if param == :members and value.is_a?(Array)\n      value = value.join(\",\")\n    end\n    super(param, value)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/group/windows_adsi.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\n\nPuppet::Type.type(:group).provide :windows_adsi do\n  desc \"Local group management for Windows. Group members can be both users and groups.\n    Additionally, local groups can contain domain users.\"\n\n  defaultfor 'os.name' => :windows\n  confine    'os.name' => :windows\n\n  has_features :manages_members\n\n  def initialize(value = {})\n    super(value)\n    @deleted = false\n  end\n\n  def members_insync?(current, should)\n    return false unless current\n\n    # By comparing account SIDs we don't have to worry about case\n    # sensitivity, or canonicalization of account names.\n\n    # Cannot use munge of the group property to canonicalize @should\n    # since the default array_matching comparison is not commutative\n\n    # dupes automatically weeded out when hashes built\n    current_members = Puppet::Util::Windows::ADSI::Group.name_sid_hash(current, true)\n    specified_members = Puppet::Util::Windows::ADSI::Group.name_sid_hash(should)\n\n    current_sids = current_members.keys.to_a\n    specified_sids = specified_members.keys.to_a\n\n    if @resource[:auth_membership]\n      current_sids.sort == specified_sids.sort\n    else\n      (specified_sids & current_sids) == specified_sids\n    end\n  end\n\n  def members_to_s(users)\n    return '' if users.nil? or !users.is_a?(Array)\n\n    users = users.map do |user_name|\n      sid = Puppet::Util::Windows::SID.name_to_principal(user_name)\n      unless sid\n        resource.debug(\"#{user_name} (unresolvable to SID)\")\n        next user_name\n      end\n\n      if sid.account =~ /\\\\/\n        account, _ = Puppet::Util::Windows::ADSI::User.parse_name(sid.account)\n      else\n        account = sid.account\n      end\n      resource.debug(\"#{sid.domain}\\\\#{account} (#{sid.sid})\")\n      sid.domain ? \"#{sid.domain}\\\\#{account}\" : account\n    end\n    users.join(',')\n  end\n\n  def member_valid?(user_name)\n    !Puppet::Util::Windows::SID.name_to_principal(user_name).nil?\n  end\n\n  def group\n    @group ||= Puppet::Util::Windows::ADSI::Group.new(@resource[:name])\n  end\n\n  def members\n    @members ||= Puppet::Util::Windows::ADSI::Group.name_sid_hash(group.members, true)\n\n    # @members.keys returns an array of SIDs. We need to convert those SIDs into\n    # names so that `puppet resource` prints the right output.\n    members_to_s(@members.keys).split(',')\n  end\n\n  def members=(members)\n    group.set_members(members, @resource[:auth_membership])\n  end\n\n  def create\n    @group = Puppet::Util::Windows::ADSI::Group.create(@resource[:name])\n    @group.commit\n\n    self.members = @resource[:members]\n  end\n\n  def exists?\n    Puppet::Util::Windows::ADSI::Group.exists?(@resource[:name])\n  end\n\n  def delete\n    Puppet::Util::Windows::ADSI::Group.delete(@resource[:name])\n\n    @deleted = true\n  end\n\n  # Only flush if we created or modified a group, not deleted\n  def flush\n    @group.commit if @group && !@deleted\n  end\n\n  def gid\n    Puppet::Util::Windows::SID.name_to_sid(@resource[:name])\n  end\n\n  def gid=(value)\n    fail \"gid is read-only\"\n  end\n\n  def self.instances\n    Puppet::Util::Windows::ADSI::Group.map { |g| new(:ensure => :present, :name => g.name) }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/ldap.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/provider'\n\n# The base class for LDAP providers.\nclass Puppet::Provider::Ldap < Puppet::Provider\n  require_relative '../../puppet/util/ldap/manager'\n\n  class << self\n    attr_reader :manager\n  end\n\n  # Look up all instances at our location.  Yay.\n  def self.instances\n    list = manager.search\n    return [] unless list\n\n    list.collect { |entry| new(entry) }\n  end\n\n  # Specify the ldap manager for this provider, which is\n  # used to figure out how we actually interact with ldap.\n  def self.manages(*args)\n    @manager = Puppet::Util::Ldap::Manager.new\n    @manager.manages(*args)\n\n    # Set up our getter/setter methods.\n    mk_resource_methods\n    @manager\n  end\n\n  # Query all of our resources from ldap.\n  def self.prefetch(resources)\n    resources.each do |name, resource|\n      result = manager.find(name)\n      if result\n        result[:ensure] = :present\n        resource.provider = new(result)\n      else\n        resource.provider = new(:ensure => :absent)\n      end\n    end\n  end\n\n  def manager\n    self.class.manager\n  end\n\n  def create\n    @property_hash[:ensure] = :present\n    self.class.resource_type.validproperties.each do |property|\n      val = resource.should(property)\n      if val\n        if property.to_s == 'gid'\n          self.gid = val\n        else\n          @property_hash[property] = val\n        end\n      end\n    end\n  end\n\n  def delete\n    @property_hash[:ensure] = :absent\n  end\n\n  def exists?\n    @property_hash[:ensure] != :absent\n  end\n\n  # Apply our changes to ldap, yo.\n  def flush\n    # Just call the manager's update() method.\n    @property_hash.delete(:groups)\n    @ldap_properties.delete(:groups)\n    manager.update(name, ldap_properties, properties)\n    @property_hash.clear\n    @ldap_properties.clear\n  end\n\n  def initialize(*args)\n    raise(Puppet::DevError, _(\"No LDAP Configuration defined for %{class_name}\") % { class_name: self.class }) unless self.class.manager\n    raise(Puppet::DevError, _(\"Invalid LDAP Configuration defined for %{class_name}\") % { class_name: self.class }) unless self.class.manager.valid?\n\n    super\n\n    @property_hash = @property_hash.each_with_object({}) do |ary, result|\n      param, values = ary\n\n      # Skip any attributes we don't manage.\n      next result unless self.class.resource_type.valid_parameter?(param)\n\n      paramclass = self.class.resource_type.attrclass(param)\n\n      unless values.is_a?(Array)\n        result[param] = values\n        next result\n      end\n\n      # Only use the first value if the attribute class doesn't manage\n      # arrays of values.\n      if paramclass.superclass == Puppet::Parameter or paramclass.array_matching == :first\n        result[param] = values[0]\n      else\n        result[param] = values\n      end\n    end\n\n    # Make a duplicate, so that we have a copy for comparison\n    # at the end.\n    @ldap_properties = @property_hash.dup\n  end\n\n  # Return the current state of ldap.\n  def ldap_properties\n    @ldap_properties.dup\n  end\n\n  # Return (and look up if necessary) the desired state.\n  def properties\n    if @property_hash.empty?\n      @property_hash = query || { :ensure => :absent }\n      @property_hash[:ensure] = :absent if @property_hash.empty?\n    end\n    @property_hash.dup\n  end\n\n  # Collect the current attributes from ldap.  Returns\n  # the results, but also stores the attributes locally,\n  # so we have something to compare against when we update.\n  # LAK:NOTE This is normally not used, because we rely on prefetching.\n  def query\n    # Use the module function.\n    attributes = manager.find(name)\n    unless attributes\n      @ldap_properties = {}\n      return nil\n    end\n\n    @ldap_properties = attributes\n    @ldap_properties.dup\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/nameservice/directoryservice.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet'\nrequire_relative '../../../puppet/provider/nameservice'\nrequire_relative '../../../puppet/util/plist' if Puppet.features.cfpropertylist?\nrequire 'fileutils'\n\nclass Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameService\n  # JJM: Dive into the singleton_class\n  class << self\n    # JJM: This allows us to pass information when calling\n    #      Puppet::Type.type\n    #  e.g. Puppet::Type.type(:user).provide :directoryservice, :ds_path => \"Users\"\n    #  This is referenced in the get_ds_path class method\n    attr_writer :ds_path\n  end\n\n  initvars\n\n  commands :dscl => \"/usr/bin/dscl\"\n  commands :dseditgroup => \"/usr/sbin/dseditgroup\"\n  commands :sw_vers => \"/usr/bin/sw_vers\"\n  confine 'os.name' => :darwin\n  confine :feature => :cfpropertylist\n  defaultfor 'os.name' => :darwin\n\n  # There is no generalized mechanism for provider cache management, but we can\n  # use post_resource_eval, which will be run for each suitable provider at the\n  # end of each transaction. Use this to clear @all_present after each run.\n  def self.post_resource_eval\n    @all_present = nil\n  end\n\n  # JJM 2007-07-25: This map is used to map NameService attributes to their\n  #     corresponding DirectoryService attribute names.\n  #     See: http://images.apple.com/server/docs.Open_Directory_v10.4.pdf\n  # JJM: Note, this is de-coupled from the Puppet::Type, and must\n  #     be actively maintained.  There may also be collisions with different\n  #     types (Users, Groups, Mounts, Hosts, etc...)\n  def ds_to_ns_attribute_map; self.class.ds_to_ns_attribute_map; end\n\n  def self.ds_to_ns_attribute_map\n    {\n      'RecordName' => :name,\n      'PrimaryGroupID' => :gid,\n      'NFSHomeDirectory' => :home,\n      'UserShell' => :shell,\n      'UniqueID' => :uid,\n      'RealName' => :comment,\n      'Password' => :password,\n      'GeneratedUID' => :guid,\n      'IPAddress' => :ip_address,\n      'ENetAddress' => :en_address,\n      'GroupMembership' => :members,\n    }\n  end\n\n  # JJM The same table as above, inverted.\n  def ns_to_ds_attribute_map; self.class.ns_to_ds_attribute_map end\n\n  def self.ns_to_ds_attribute_map\n    @ns_to_ds_attribute_map ||= ds_to_ns_attribute_map.invert\n  end\n\n  def self.password_hash_dir\n    '/var/db/shadow/hash'\n  end\n\n  def self.users_plist_dir\n    '/var/db/dslocal/nodes/Default/users'\n  end\n\n  def self.instances\n    # JJM Class method that provides an array of instance objects of this\n    #     type.\n    # JJM: Properties are dependent on the Puppet::Type we're managing.\n    type_property_array = [:name] + @resource_type.validproperties\n\n    # Create a new instance of this Puppet::Type for each object present\n    #    on the system.\n    list_all_present.collect do |name_string|\n      new(single_report(name_string, *type_property_array))\n    end\n  end\n\n  def self.get_ds_path\n    # JJM: 2007-07-24 This method dynamically returns the DS path we're concerned with.\n    #      For example, if we're working with an user type, this will be /Users\n    #      with a group type, this will be /Groups.\n    #   @ds_path is an attribute of the class itself.\n    return @ds_path if defined?(@ds_path)\n\n    # JJM: \"Users\" or \"Groups\" etc ...  (Based on the Puppet::Type)\n    #       Remember this is a class method, so self.class is Class\n    #       Also, @resource_type seems to be the reference to the\n    #       Puppet::Type this class object is providing for.\n    @resource_type.name.to_s.capitalize + \"s\"\n  end\n\n  def self.list_all_present\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @all_present ||= begin\n      # rubocop:enable Naming/MemoizedInstanceVariableName\n      # JJM: List all objects of this Puppet::Type already present on the system.\n      begin\n        dscl_output = execute(get_exec_preamble(\"-list\"))\n      rescue Puppet::ExecutionFailure\n        fail(_(\"Could not get %{resource} list from DirectoryService\") % { resource: @resource_type.name })\n      end\n      dscl_output.split(\"\\n\")\n    end\n  end\n\n  def self.parse_dscl_plist_data(dscl_output)\n    Puppet::Util::Plist.parse_plist(dscl_output)\n  end\n\n  def self.generate_attribute_hash(input_hash, *type_properties)\n    attribute_hash = {}\n    input_hash.each_key do |key|\n      ds_attribute = key.sub(\"dsAttrTypeStandard:\", \"\")\n      next unless ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? ds_to_ns_attribute_map[ds_attribute]\n\n      ds_value = input_hash[key]\n      case ds_to_ns_attribute_map[ds_attribute]\n      when :members\n        # do nothing, only members uses arrays so far\n      when :gid, :uid\n        # OS X stores objects like uid/gid as strings.\n        # Try casting to an integer for these cases to be\n        # consistent with the other providers and the group type\n        # validation\n        begin\n          ds_value = Integer(ds_value[0])\n        rescue ArgumentError\n          ds_value = ds_value[0]\n        end\n      else ds_value = ds_value[0]\n      end\n      attribute_hash[ds_to_ns_attribute_map[ds_attribute]] = ds_value\n    end\n\n    # NBK: need to read the existing password here as it's not actually\n    # stored in the user record. It is stored at a path that involves the\n    # UUID of the user record for non-Mobile local accounts.\n    # Mobile Accounts are out of scope for this provider for now\n    attribute_hash[:password] = get_password(attribute_hash[:guid], attribute_hash[:name]) if @resource_type.validproperties.include?(:password) and Puppet.features.root?\n    attribute_hash\n  end\n\n  def self.single_report(resource_name, *type_properties)\n    # JJM 2007-07-24:\n    #     Given a the name of an object and a list of properties of that\n    #     object, return all property values in a hash.\n    #\n    #     This class method returns nil if the object doesn't exist\n    #     Otherwise, it returns a hash of the object properties.\n\n    all_present_str_array = list_all_present\n\n    # NBK: shortcut the process if the resource is missing\n    return nil unless all_present_str_array.include? resource_name\n\n    dscl_vector = get_exec_preamble(\"-read\", resource_name)\n    begin\n      dscl_output = execute(dscl_vector)\n    rescue Puppet::ExecutionFailure\n      fail(_(\"Could not get report.  command execution failed.\"))\n    end\n\n    dscl_plist = parse_dscl_plist_data(dscl_output)\n\n    generate_attribute_hash(dscl_plist, *type_properties)\n  end\n\n  def self.get_exec_preamble(ds_action, resource_name = nil)\n    # JJM 2007-07-24\n    #     DSCL commands are often repetitive and contain the same positional\n    #     arguments over and over. See https://developer.apple.com/documentation/Porting/Conceptual/PortingUnix/additionalfeatures/chapter_10_section_9.html\n    #     for an example of what I mean.\n    #     This method spits out proper DSCL commands for us.\n    #     We EXPECT name to be @resource[:name] when called from an instance object.\n\n    command_vector = [command(:dscl), \"-plist\", \".\"]\n\n    # JJM: The actual action to perform. See \"man dscl\".\n    #      Common actions: -create, -delete, -merge, -append, -passwd\n    command_vector << ds_action\n    # JJM: get_ds_path will spit back \"Users\" or \"Groups\",\n    # etc...  Depending on the Puppet::Type of our self.\n    if resource_name\n      command_vector << \"/#{get_ds_path}/#{resource_name}\"\n    else\n      command_vector << \"/#{get_ds_path}\"\n    end\n    # JJM:  This returns most of the preamble of the command.\n    #       e.g. 'dscl / -create /Users/mccune'\n    command_vector\n  end\n\n  def self.set_password(resource_name, guid, password_hash)\n    # 10.7 uses salted SHA512 password hashes which are 128 characters plus\n    # an 8 character salt. Previous versions used a SHA1 hash padded with\n    # zeroes. If someone attempts to use a password hash that worked with\n    # a previous version of OS X, we will fail early and warn them.\n    if password_hash.length != 136\n      # TRANSLATORS 'OS X 10.7' is an operating system and should not be translated, 'Salted SHA512' is the name of a hashing algorithm\n      fail(_(\"OS X 10.7 requires a Salted SHA512 hash password of 136 characters.\") +\n           ' ' + _(\"Please check your password and try again.\"))\n    end\n\n    plist_file = \"#{users_plist_dir}/#{resource_name}.plist\"\n    if Puppet::FileSystem.exist?(plist_file)\n      # If a plist already exists in /var/db/dslocal/nodes/Default/users, then\n      # we will need to extract the binary plist from the 'ShadowHashData'\n      # key, log the new password into the resultant plist's 'SALTED-SHA512'\n      # key, and then save the entire structure back.\n      users_plist = Puppet::Util::Plist.read_plist_file(plist_file)\n\n      # users_plist['ShadowHashData'][0] is actually a binary plist\n      # that's nested INSIDE the user's plist (which itself is a binary\n      # plist). If we encounter a user plist that DOESN'T have a\n      # ShadowHashData field, create one.\n      if users_plist['ShadowHashData']\n        password_hash_plist = users_plist['ShadowHashData'][0]\n        converted_hash_plist = convert_binary_to_hash(password_hash_plist)\n      else\n        users_plist['ShadowHashData'] = ''.dup\n        converted_hash_plist = { 'SALTED-SHA512' => ''.dup }\n      end\n\n      # converted_hash_plist['SALTED-SHA512'] expects a Base64 encoded\n      # string. The password_hash provided as a resource attribute is a\n      # hex value. We need to convert the provided hex value to a Base64\n      # encoded string to nest it in the converted hash plist.\n      converted_hash_plist['SALTED-SHA512'] = \\\n        password_hash.unpack('a2' * (password_hash.size / 2)).collect { |i| i.hex.chr }.join\n\n      # Finally, we can convert the nested plist back to binary, embed it\n      # into the user's plist, and convert the resultant plist back to\n      # a binary plist.\n      changed_plist = convert_hash_to_binary(converted_hash_plist)\n      users_plist['ShadowHashData'][0] = changed_plist\n      Puppet::Util::Plist.write_plist_file(users_plist, plist_file, :binary)\n    end\n  end\n\n  def self.get_password(guid, username)\n    plist_file = \"#{users_plist_dir}/#{username}.plist\"\n    if Puppet::FileSystem.exist?(plist_file)\n      # If a plist exists in /var/db/dslocal/nodes/Default/users, we will\n      # extract the binary plist from the 'ShadowHashData' key, decode the\n      # salted-SHA512 password hash, and then return it.\n      users_plist = Puppet::Util::Plist.read_plist_file(plist_file)\n\n      if users_plist['ShadowHashData']\n        # users_plist['ShadowHashData'][0] is actually a binary plist\n        # that's nested INSIDE the user's plist (which itself is a binary\n        # plist).\n        password_hash_plist = users_plist['ShadowHashData'][0]\n        converted_hash_plist = convert_binary_to_hash(password_hash_plist)\n\n        # converted_hash_plist['SALTED-SHA512'] is a Base64 encoded\n        # string. The password_hash provided as a resource attribute is a\n        # hex value. We need to convert the Base64 encoded string to a\n        # hex value and provide it back to Puppet.\n        converted_hash_plist['SALTED-SHA512'].unpack1(\"H*\")\n\n      end\n    end\n  end\n\n  # This method will accept a hash and convert it to a binary plist (string value).\n  def self.convert_hash_to_binary(plist_data)\n    Puppet.debug('Converting plist hash to binary')\n    Puppet::Util::Plist.dump_plist(plist_data, :binary)\n  end\n\n  # This method will accept a binary plist (as a string) and convert it to a hash.\n  def self.convert_binary_to_hash(plist_data)\n    Puppet.debug('Converting binary plist to hash')\n    Puppet::Util::Plist.parse_plist(plist_data)\n  end\n\n  # Unlike most other *nixes, OS X doesn't provide built in functionality\n  # for automatically assigning uids and gids to accounts, so we set up these\n  # methods for consumption by functionality like --mkusers\n  # By default we restrict to a reasonably sane range for system accounts\n  def self.next_system_id(id_type, min_id = 20)\n    dscl_args = ['.', '-list']\n    case id_type\n    when 'uid'\n      dscl_args << '/Users' << 'uid'\n    when 'gid'\n      dscl_args << '/Groups' << 'gid'\n    else\n      fail(_(\"Invalid id_type %{id_type}. Only 'uid' and 'gid' supported\") % { id_type: id_type })\n    end\n    dscl_out = dscl(dscl_args)\n    # We're ok with throwing away negative uids here.\n    ids = dscl_out.split.compact.collect { |l| l.to_i if l =~ /^\\d+$/ }\n    ids.compact!.sort_by!(&:to_f)\n    # We're just looking for an unused id in our sorted array.\n    ids.each_index do |i|\n      next_id = ids[i] + 1\n      return next_id if ids[i + 1] != next_id and next_id >= min_id\n    end\n  end\n\n  def ensure=(ensure_value)\n    super\n    # We need to loop over all valid properties for the type we're\n    # managing and call the method which sets that property value\n    # dscl can't create everything at once unfortunately.\n    if ensure_value == :present\n      @resource.class.validproperties.each do |name|\n        next if name == :ensure\n\n        # LAK: We use property.sync here rather than directly calling\n        # the settor method because the properties might do some kind\n        # of conversion.  In particular, the user gid property might\n        # have a string and need to convert it to a number\n        if @resource.should(name)\n          @resource.property(name).sync\n        else\n          value = autogen(name)\n          if value\n            send(name.to_s + \"=\", value)\n          else\n            next\n          end\n        end\n      end\n    end\n  end\n\n  def password=(passphrase)\n    exec_arg_vector = self.class.get_exec_preamble(\"-read\", @resource.name)\n    exec_arg_vector << ns_to_ds_attribute_map[:guid]\n    begin\n      guid_output = execute(exec_arg_vector)\n      guid_plist = Puppet::Util::Plist.parse_plist(guid_output)\n      # Although GeneratedUID like all DirectoryService values can be multi-valued\n      # according to the schema, in practice user accounts cannot have multiple UUIDs\n      # otherwise Bad Things Happen, so we just deal with the first value.\n      guid = guid_plist[\"dsAttrTypeStandard:#{ns_to_ds_attribute_map[:guid]}\"][0]\n      self.class.set_password(@resource.name, guid, passphrase)\n    rescue Puppet::ExecutionFailure => detail\n      fail(_(\"Could not set %{param} on %{resource}[%{name}]: %{detail}\") % { param: param, resource: @resource.class.name, name: @resource.name, detail: detail })\n    end\n  end\n\n  # NBK: we override @parent.set as we need to execute a series of commands\n  # to deal with array values, rather than the single command nameservice.rb\n  # expects to be returned by modifycmd. Thus we don't bother defining modifycmd.\n\n  def set(param, value)\n    self.class.validate(param, value)\n    current_members = @property_value_cache_hash[:members]\n    if param == :members\n      # If we are meant to be authoritative for the group membership\n      # then remove all existing members who haven't been specified\n      # in the manifest.\n      remove_unwanted_members(current_members, value) if @resource[:auth_membership] and !current_members.nil?\n\n      # if they're not a member, make them one.\n      add_members(current_members, value)\n    else\n      exec_arg_vector = self.class.get_exec_preamble(\"-create\", @resource[:name])\n      # JJM: The following line just maps the NS name to the DS name\n      #      e.g. { :uid => 'UniqueID' }\n      exec_arg_vector << ns_to_ds_attribute_map[param.intern]\n      # JJM: The following line sends the actual value to set the property to\n      exec_arg_vector << value.to_s\n      begin\n        execute(exec_arg_vector)\n      rescue Puppet::ExecutionFailure => detail\n        fail(_(\"Could not set %{param} on %{resource}[%{name}]: %{detail}\") % { param: param, resource: @resource.class.name, name: @resource.name, detail: detail })\n      end\n    end\n  end\n\n  # NBK: we override @parent.create as we need to execute a series of commands\n  # to create objects with dscl, rather than the single command nameservice.rb\n  # expects to be returned by addcmd. Thus we don't bother defining addcmd.\n  def create\n    if exists?\n      info _(\"already exists\")\n      return nil\n    end\n\n    # NBK: First we create the object with a known guid so we can set the contents\n    # of the password hash if required\n    # Shelling out sucks, but for a single use case it doesn't seem worth\n    # requiring people install a UUID library that doesn't come with the system.\n    # This should be revisited if Puppet starts managing UUIDs for other platform\n    # user records.\n    guid = %x(/usr/bin/uuidgen).chomp\n\n    exec_arg_vector = self.class.get_exec_preamble(\"-create\", @resource[:name])\n    exec_arg_vector << ns_to_ds_attribute_map[:guid] << guid\n    begin\n      execute(exec_arg_vector)\n    rescue Puppet::ExecutionFailure => detail\n      fail(_(\"Could not set GeneratedUID for %{resource} %{name}: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail })\n    end\n\n    value = @resource.should(:password)\n    if value && value != \"\"\n      self.class.set_password(@resource[:name], guid, value)\n    end\n\n    # Now we create all the standard properties\n    Puppet::Type.type(@resource.class.name).validproperties.each do |property|\n      next if property == :ensure\n\n      value = @resource.should(property)\n      if property == :gid and value.nil?\n        value = self.class.next_system_id('gid')\n      end\n      if property == :uid and value.nil?\n        value = self.class.next_system_id('uid')\n      end\n      if value != \"\" and !value.nil?\n        if property == :members\n          add_members(nil, value)\n        else\n          exec_arg_vector = self.class.get_exec_preamble(\"-create\", @resource[:name])\n          exec_arg_vector << ns_to_ds_attribute_map[property.intern]\n          next if property == :password # skip setting the password here\n\n          exec_arg_vector << value.to_s\n          begin\n            execute(exec_arg_vector)\n          rescue Puppet::ExecutionFailure => detail\n            fail(_(\"Could not create %{resource} %{name}: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail })\n          end\n        end\n      end\n    end\n  end\n\n  def remove_unwanted_members(current_members, new_members)\n    current_members.each do |member|\n      next if new_members.flatten.include?(member)\n\n      cmd = [:dseditgroup, \"-o\", \"edit\", \"-n\", \".\", \"-d\", member, @resource[:name]]\n      begin\n        execute(cmd)\n      rescue Puppet::ExecutionFailure\n        # TODO: We're falling back to removing the member using dscl due to rdar://8481241\n        # This bug causes dseditgroup to fail to remove a member if that member doesn't exist\n        cmd = [:dscl, \".\", \"-delete\", \"/Groups/#{@resource.name}\", \"GroupMembership\", member]\n        begin\n          execute(cmd)\n        rescue Puppet::ExecutionFailure => detail\n          fail(_(\"Could not remove %{member} from group: %{resource}, %{detail}\") % { member: member, resource: @resource.name, detail: detail })\n        end\n      end\n    end\n  end\n\n  def add_members(current_members, new_members)\n    new_members.flatten.each do |new_member|\n      next unless current_members.nil? or !current_members.include?(new_member)\n\n      cmd = [:dseditgroup, \"-o\", \"edit\", \"-n\", \".\", \"-a\", new_member, @resource[:name]]\n      begin\n        execute(cmd)\n      rescue Puppet::ExecutionFailure => detail\n        fail(_(\"Could not add %{new_member} to group: %{name}, %{detail}\") % { new_member: new_member, name: @resource.name, detail: detail })\n      end\n    end\n  end\n\n  def deletecmd\n    # JJM: Like addcmd, only called when deleting the object itself\n    #    Note, this isn't used to delete properties of the object,\n    #    at least that's how I understand it...\n    self.class.get_exec_preamble(\"-delete\", @resource[:name])\n  end\n\n  def getinfo(refresh = false)\n    # JJM 2007-07-24:\n    #      Override the getinfo method, which is also defined in nameservice.rb\n    #      This method returns and sets @infohash\n    # I'm not re-factoring the name \"getinfo\" because this method will be\n    # most likely called by nameservice.rb, which I didn't write.\n    if refresh or (!defined?(@property_value_cache_hash) or !@property_value_cache_hash)\n      # JJM 2007-07-24: OK, there's a bit of magic that's about to\n      # happen... Let's see how strong my grip has become... =)\n      #\n      # self is a provider instance of some Puppet::Type, like\n      # Puppet::Type::User::ProviderDirectoryservice for the case of the\n      # user type and this provider.\n      #\n      # self.class looks like \"user provider directoryservice\", if that\n      # helps you ...\n      #\n      # self.class.resource_type is a reference to the Puppet::Type class,\n      # probably Puppet::Type::User or Puppet::Type::Group, etc...\n      #\n      # self.class.resource_type.validproperties is a class method,\n      # returning an Array of the valid properties of that specific\n      # Puppet::Type.\n      #\n      # So... something like [:comment, :home, :password, :shell, :uid,\n      # :groups, :ensure, :gid]\n      #\n      # Ultimately, we add :name to the list, delete :ensure from the\n      # list, then report on the remaining list. Pretty whacky, ehh?\n      type_properties = [:name] + self.class.resource_type.validproperties\n      type_properties.delete(:ensure) if type_properties.include? :ensure\n      type_properties << :guid # append GeneratedUID so we just get the report here\n      @property_value_cache_hash = self.class.single_report(@resource[:name], *type_properties)\n      [:uid, :gid].each do |param|\n        @property_value_cache_hash[param] = @property_value_cache_hash[param].to_i if @property_value_cache_hash and @property_value_cache_hash.include?(param)\n      end\n    end\n    @property_value_cache_hash\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/nameservice/objectadd.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice'\n\nclass Puppet::Provider::NameService\n  class ObjectAdd < Puppet::Provider::NameService\n    def deletecmd\n      [command(:delete), @resource[:name]]\n    end\n\n    # Determine the flag to pass to our command.\n    def flag(name)\n      name = name.intern if name.is_a? String\n      self.class.option(name, :flag) || \"-\" + name.to_s[0, 1]\n    end\n\n    def posixmethod(name)\n      name = name.intern if name.is_a? String\n      self.class.option(name, :method) || name\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/nameservice/pw.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice/objectadd'\n\nclass Puppet::Provider::NameService\n  class PW < ObjectAdd\n    def deletecmd\n      [command(:pw), \"#{@resource.class.name}del\", @resource[:name]]\n    end\n\n    def modifycmd(param, value)\n      [\n        command(:pw),\n        \"#{@resource.class.name}mod\",\n        @resource[:name],\n        flag(param),\n        munge(param, value)\n      ]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/nameservice.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\n\n# This is the parent class of all NSS classes.  They're very different in\n# their backend, but they're pretty similar on the front-end.  This class\n# provides a way for them all to be as similar as possible.\nclass Puppet::Provider::NameService < Puppet::Provider\n  class << self\n    def autogen_default(param)\n      defined?(@autogen_defaults) ? @autogen_defaults[param.intern] : nil\n    end\n\n    def autogen_defaults(hash)\n      @autogen_defaults ||= {}\n      hash.each do |param, value|\n        @autogen_defaults[param.intern] = value\n      end\n    end\n\n    def initvars\n      @checks = {}\n      @options = {}\n      super\n    end\n\n    def instances\n      objects = []\n      begin\n        method = Puppet::Etc.method(:\"get#{section}ent\")\n        while ent = method.call # rubocop:disable Lint/AssignmentInCondition\n          objects << new(:name => ent.name, :canonical_name => ent.canonical_name, :ensure => :present)\n        end\n      ensure\n        Puppet::Etc.send(\"end#{section}ent\")\n      end\n      objects\n    end\n\n    def option(name, option)\n      name = name.intern if name.is_a? String\n      (defined?(@options) and @options.include? name and @options[name].include? option) ? @options[name][option] : nil\n    end\n\n    def options(name, hash)\n      unless resource_type.valid_parameter?(name)\n        raise Puppet::DevError, _(\"%{name} is not a valid attribute for %{resource_type}\") % { name: name, resource_type: resource_type.name }\n      end\n\n      @options ||= {}\n      @options[name] ||= {}\n\n      # Set options individually, so we can call the options method\n      # multiple times.\n      hash.each do |param, value|\n        @options[name][param] = value\n      end\n    end\n\n    def resource_type=(resource_type)\n      super\n      @resource_type.validproperties.each do |prop|\n        next if prop == :ensure\n\n        define_method(prop) { get(prop) || :absent } unless public_method_defined?(prop)\n        define_method(prop.to_s + \"=\") { |*vals| set(prop, *vals) } unless public_method_defined?(prop.to_s + \"=\")\n      end\n    end\n\n    # This is annoying, but there really aren't that many options,\n    # and this *is* built into Ruby.\n    def section\n      unless resource_type\n        raise Puppet::DevError,\n              \"Cannot determine Etc section without a resource type\"\n      end\n\n      if @resource_type.name == :group\n        \"gr\"\n      else\n        \"pw\"\n      end\n    end\n\n    def validate(name, value)\n      name = name.intern if name.is_a? String\n      if @checks.include? name\n        block = @checks[name][:block]\n        raise ArgumentError, _(\"Invalid value %{value}: %{error}\") % { value: value, error: @checks[name][:error] } unless block.call(value)\n      end\n    end\n\n    def verify(name, error, &block)\n      name = name.intern if name.is_a? String\n      @checks[name] = { :error => error, :block => block }\n    end\n\n    private\n\n    def op(property)\n      @ops[property.name] || \"-#{property.name}\"\n    end\n  end\n\n  # Autogenerate a value.  Mostly used for uid/gid, but also used heavily\n  # with DirectoryServices\n  def autogen(field)\n    field = field.intern\n    id_generators = { :user => :uid, :group => :gid }\n    if id_generators[@resource.class.name] == field\n      self.class.autogen_id(field, @resource.class.name)\n    else\n      value = self.class.autogen_default(field)\n      if value\n        value\n      elsif respond_to?(\"autogen_#{field}\")\n        send(\"autogen_#{field}\")\n      else\n        nil\n      end\n    end\n  end\n\n  # Autogenerate either a uid or a gid.  This is not very flexible: we can\n  # only generate one field type per class, and get kind of confused if asked\n  # for both.\n  def self.autogen_id(field, resource_type)\n    # Figure out what sort of value we want to generate.\n    case resource_type\n    when :user;   database = :passwd;  method = :uid\n    when :group;  database = :group;   method = :gid\n    else\n      # TRANSLATORS \"autogen_id()\" is a method name and should not be translated\n      raise Puppet::DevError, _(\"autogen_id() does not support auto generation of id for resource type %{resource_type}\") % { resource_type: resource_type }\n    end\n\n    # Initialize from the data set, if needed.\n    unless @prevauto\n      # Sadly, Etc doesn't return an enumerator, it just invokes the block\n      # given, or returns the first record from the database.  There is no\n      # other, more convenient enumerator for these, so we fake one with this\n      # loop.  Thanks, Ruby, for your awesome abstractions. --daniel 2012-03-23\n      highest = []\n      Puppet::Etc.send(database) { |entry| highest << entry.send(method) }\n      highest = highest.reject { |x| x > 65_000 }.max\n\n      @prevauto = highest || 1000\n    end\n\n    # ...and finally increment and return the next value.\n    @prevauto += 1\n  end\n\n  def create\n    if exists?\n      info _(\"already exists\")\n      # The object already exists\n      return nil\n    end\n\n    begin\n      sensitive = has_sensitive_data?\n      execute(addcmd, { :failonfail => true, :combine => true, :custom_environment => @custom_environment, :sensitive => sensitive })\n      if feature?(:manages_password_age) && (cmd = passcmd)\n        execute(cmd, { :failonfail => true, :combine => true, :custom_environment => @custom_environment, :sensitive => sensitive })\n      end\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, _(\"Could not create %{resource} %{name}: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n    end\n  end\n\n  def delete\n    unless exists?\n      info _(\"already absent\")\n      # the object already doesn't exist\n      return nil\n    end\n\n    begin\n      execute(deletecmd, { :failonfail => true, :combine => true, :custom_environment => @custom_environment })\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, _(\"Could not delete %{resource} %{name}: %{detail}\") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n    end\n  end\n\n  def ensure\n    if exists?\n      :present\n    else\n      :absent\n    end\n  end\n\n  # Does our object exist?\n  def exists?\n    !!getinfo(true)\n  end\n\n  # Retrieve a specific value by name.\n  def get(param)\n    (hash = getinfo(false)) ? unmunge(param, hash[param]) : nil\n  end\n\n  def munge(name, value)\n    block = self.class.option(name, :munge)\n    if block and block.is_a? Proc\n      block.call(value)\n    else\n      value\n    end\n  end\n\n  def unmunge(name, value)\n    block = self.class.option(name, :unmunge)\n    if block and block.is_a? Proc\n      block.call(value)\n    else\n      value\n    end\n  end\n\n  # Retrieve what we can about our object\n  def getinfo(refresh)\n    if @objectinfo.nil? or refresh == true\n      @etcmethod ||= (\"get\" + self.class.section.to_s + \"nam\").intern\n      begin\n        @objectinfo = Puppet::Etc.send(@etcmethod, @canonical_name)\n      rescue ArgumentError\n        @objectinfo = nil\n      end\n    end\n\n    # Now convert our Etc struct into a hash.\n    @objectinfo ? info2hash(@objectinfo) : nil\n  end\n\n  # The list of all groups the user is a member of.  Different\n  # user mgmt systems will need to override this method.\n  def groups\n    Puppet::Util::POSIX.groups_of(@resource[:name]).join(',')\n  end\n\n  # Convert the Etc struct into a hash.\n  def info2hash(info)\n    hash = {}\n    self.class.resource_type.validproperties.each do |param|\n      method = posixmethod(param)\n      hash[param] = info.send(posixmethod(param)) if info.respond_to? method\n    end\n\n    hash\n  end\n\n  def initialize(resource)\n    super\n    @custom_environment = {}\n    @objectinfo = nil\n    if resource.is_a?(Hash) && !resource[:canonical_name].nil?\n      @canonical_name = resource[:canonical_name]\n    else\n      @canonical_name = resource[:name]\n    end\n  end\n\n  def set(param, value)\n    self.class.validate(param, value)\n    cmd = modifycmd(param, munge(param, value))\n    raise Puppet::DevError, _(\"Nameservice command must be an array\") unless cmd.is_a?(Array)\n\n    sensitive = has_sensitive_data?(param)\n    begin\n      execute(cmd, { :failonfail => true, :combine => true, :custom_environment => @custom_environment, :sensitive => sensitive })\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, _(\"Could not set %{param} on %{resource}[%{name}]: %{detail}\") % { param: param, resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace\n    end\n  end\n\n  # Derived classes can override to declare sensitive data so a flag can be passed to execute\n  def has_sensitive_data?(property = nil)\n    false\n  end\n\n  # From overriding Puppet::Property#insync? Ruby Etc::getpwnam < 2.1.0 always\n  # returns a struct with binary encoded string values, and >= 2.1.0 will return\n  # binary encoded strings for values incompatible with current locale charset,\n  # or Encoding.default_external if compatible. Compare a \"should\" value with\n  # encoding of \"current\" value, to avoid unnecessary property syncs and\n  # comparison of strings with different encodings. (PUP-6777)\n  #\n  # return basic string comparison after re-encoding (same as\n  # Puppet::Property#property_matches)\n  def comments_insync?(current, should)\n    # we're only doing comparison here so don't mutate the string\n    desired = should[0].to_s.dup\n    current == desired.force_encoding(current.encoding)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/network_device.rb",
    "content": "# frozen_string_literal: true\n\n# This is the base class of all prefetched network device provider\nclass Puppet::Provider::NetworkDevice < Puppet::Provider\n  def self.device(url)\n    raise \"This provider doesn't implement the necessary device method\"\n  end\n\n  def self.lookup(device, name)\n    raise \"This provider doesn't implement the necessary lookup method\"\n  end\n\n  def self.prefetch(resources)\n    resources.each do |name, resource|\n      device = Puppet::Util::NetworkDevice.current || device(resource[:device_url])\n      result = lookup(device, name)\n      if result\n        result[:ensure] = :present\n        resource.provider = new(device, result)\n      else\n        resource.provider = new(device, :ensure => :absent)\n      end\n    end\n  rescue => detail\n    # Preserving behavior introduced in #6907\n    # TRANSLATORS \"prefetch\" is a program name and should not be translated\n    Puppet.log_exception(detail, _(\"Could not perform network device prefetch: %{detail}\") % { detail: detail })\n  end\n\n  def exists?\n    @property_hash[:ensure] != :absent\n  end\n\n  attr_accessor :device\n\n  def initialize(device, *args)\n    super(*args)\n\n    @device = device\n\n    # Make a duplicate, so that we have a copy for comparison\n    # at the end.\n    @properties = @property_hash.dup\n  end\n\n  def create\n    @property_hash[:ensure] = :present\n    self.class.resource_type.validproperties.each do |property|\n      val = resource.should(property)\n      if val\n        @property_hash[property] = val\n      end\n    end\n  end\n\n  def destroy\n    @property_hash[:ensure] = :absent\n  end\n\n  def flush\n    @property_hash.clear\n  end\n\n  def self.instances\n  end\n\n  def former_properties\n    @properties.dup\n  end\n\n  def properties\n    @property_hash.dup\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/aix.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/util/package'\n\nPuppet::Type.type(:package).provide :aix, :parent => Puppet::Provider::Package do\n  desc \"Installation from an AIX software directory, using the AIX `installp`\n       command.  The `source` parameter is required for this provider, and should\n       be set to the absolute path (on the puppet agent machine) of a directory\n       containing one or more BFF package files.\n\n       The `installp` command will generate a table of contents file (named `.toc`)\n       in this directory, and the `name` parameter (or resource title) that you\n       specify for your `package` resource must match a package name that exists\n       in the `.toc` file.\n\n       Note that package downgrades are *not* supported; if your resource specifies\n       a specific version number and there is already a newer version of the package\n       installed on the machine, the resource will fail with an error message.\"\n\n  # The commands we are using on an AIX box are installed standard\n  # (except nimclient) nimclient needs the bos.sysmgt.nim.client fileset.\n  commands    :lslpp => \"/usr/bin/lslpp\",\n              :installp => \"/usr/sbin/installp\"\n\n  # AIX supports versionable packages with and without a NIM server\n  has_feature :versionable\n\n  confine 'os.name' => [:aix]\n  defaultfor 'os.name' => :aix\n\n  attr_accessor :latest_info\n\n  STATE_CODE = {\n    'A' => :applied,\n    'B' => :broken,\n    'C' => :committed,\n    'E' => :efix_locked,\n    'O' => :obsolete,\n    '?' => :inconsistent,\n  }.freeze\n\n  def self.srclistcmd(source)\n    [command(:installp), \"-L\", \"-d\", source]\n  end\n\n  def self.prefetch(packages)\n    raise Puppet::Error, _(\"The aix provider can only be used by root\") if Process.euid != 0\n\n    return unless packages.detect { |_name, package| package.should(:ensure) == :latest }\n\n    sources = packages.collect { |_name, package| package[:source] }.uniq.compact\n\n    updates = {}\n    sources.each do |source|\n      execute(srclistcmd(source)).each_line do |line|\n        next unless line =~ /^[^#][^:]*:([^:]*):([^:]*)/\n\n        current = {}\n        current[:name]    = Regexp.last_match(1)\n        current[:version] = Regexp.last_match(2)\n        current[:source]  = source\n\n        if updates.key?(current[:name])\n          previous = updates[current[:name]]\n\n          updates[current[:name]] = current unless Puppet::Util::Package.versioncmp(previous[:version], current[:version]) == 1\n\n        else\n          updates[current[:name]] = current\n        end\n      end\n    end\n\n    packages.each do |name, package|\n      if updates.key?(name)\n        package.provider.latest_info = updates[name]\n      end\n    end\n  end\n\n  def uninstall\n    # Automatically process dependencies when installing/uninstalling\n    # with the -g option to installp.\n    installp \"-gu\", @resource[:name]\n\n    # installp will return an exit code of zero even if it didn't uninstall\n    # anything... so let's make sure it worked.\n    unless query().nil?\n      self.fail _(\"Failed to uninstall package '%{name}'\") % { name: @resource[:name] }\n    end\n  end\n\n  def install(useversion = true)\n    source = @resource[:source]\n    unless source\n      self.fail _(\"A directory is required which will be used to find packages\")\n    end\n\n    pkg = @resource[:name]\n\n    pkg += \" #{@resource.should(:ensure)}\" if (!@resource.should(:ensure).is_a? Symbol) and useversion\n\n    output = installp \"-acgwXY\", \"-d\", source, pkg\n\n    # If the package is superseded, it means we're trying to downgrade and we\n    # can't do that.\n    if output =~ /^#{Regexp.escape(@resource[:name])}\\s+.*\\s+Already superseded by.*$/\n      self.fail _(\"aix package provider is unable to downgrade packages\")\n    end\n\n    pkg_info = query\n    if pkg_info && [:broken, :inconsistent].include?(pkg_info[:status])\n      self.fail _(\"Package '%{name}' is in a %{status} state and requires manual intervention\") % { name: @resource[:name], status: pkg_info[:status] }\n    end\n  end\n\n  def self.pkglist(hash = {})\n    cmd = [command(:lslpp), \"-qLc\"]\n\n    name = hash[:pkgname]\n    if name\n      cmd << name\n    end\n\n    begin\n      list = execute(cmd).scan(/^[^#][^:]*:([^:]*):([^:]*):[^:]*:[^:]*:([^:])/).collect { |n, e, s|\n        e = :absent if [:broken, :inconsistent].include?(STATE_CODE[s])\n        { :name => n, :ensure => e, :status => STATE_CODE[s], :provider => self.name }\n      }\n    rescue Puppet::ExecutionFailure => detail\n      if hash[:pkgname]\n        return nil\n      else\n        raise Puppet::Error, _(\"Could not list installed Packages: %{detail}\") % { detail: detail }, detail.backtrace\n      end\n    end\n\n    if hash[:pkgname]\n      list.shift\n    else\n      list\n    end\n  end\n\n  def self.instances\n    pkglist.collect do |hash|\n      new(hash)\n    end\n  end\n\n  def latest\n    upd = latest_info\n\n    if upd.nil?\n      raise Puppet::DevError, _(\"Tried to get latest on a missing package\") if properties[:ensure] == :absent\n\n      properties[:ensure]\n    else\n      (upd[:version]).to_s\n    end\n  end\n\n  def query\n    self.class.pkglist(:pkgname => @resource[:name])\n  end\n\n  def update\n    install(false)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/appdmg.rb",
    "content": "# frozen_string_literal: true\n\n# Jeff McCune <mccune.jeff@gmail.com>\n# Changed to app.dmg by: Udo Waechter <root@zoide.net>\n# Mac OS X Package Installer which handles application (.app)\n# bundles inside an Apple Disk Image.\n#\n# Motivation: DMG files provide a true HFS file system\n# and are easier to manage.\n#\n# Note: the 'apple' Provider checks for the package name\n# in /L/Receipts.  Since we possibly install multiple apps from\n# a single source, we treat the source .app.dmg file as the package name.\n# As a result, we store installed .app.dmg file names\n# in /var/db/.puppet_appdmg_installed_<name>\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/util/plist' if Puppet.features.cfpropertylist?\nPuppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Package) do\n  desc \"Package management which copies application bundles to a target.\"\n\n  confine 'os.name' => :darwin\n  confine :feature => :cfpropertylist\n\n  commands :hdiutil => \"/usr/bin/hdiutil\"\n  commands :curl => \"/usr/bin/curl\"\n  commands :ditto => \"/usr/bin/ditto\"\n\n  # JJM We store a cookie for each installed .app.dmg in /var/db\n  def self.instances_by_name\n    Dir.entries(\"/var/db\").find_all { |f|\n      f =~ /^\\.puppet_appdmg_installed_/\n    }.collect do |f|\n      name = f.sub(/^\\.puppet_appdmg_installed_/, '')\n      yield name if block_given?\n      name\n    end\n  end\n\n  def self.instances\n    instances_by_name.collect do |name|\n      new(:name => name, :provider => :appdmg, :ensure => :installed)\n    end\n  end\n\n  def self.installapp(source, name, orig_source)\n    appname = File.basename(source);\n    ditto \"--rsrc\", source, \"/Applications/#{appname}\"\n    Puppet::FileSystem.open(\"/var/db/.puppet_appdmg_installed_#{name}\", nil, \"w:UTF-8\") do |t|\n      t.print \"name: '#{name}'\\n\"\n      t.print \"source: '#{orig_source}'\\n\"\n    end\n  end\n\n  def self.installpkgdmg(source, name)\n    require 'open-uri'\n    cached_source = source\n    tmpdir = Dir.mktmpdir\n    begin\n      if %r{\\A[A-Za-z][A-Za-z0-9+\\-.]*://} =~ cached_source\n        cached_source = File.join(tmpdir, name)\n        begin\n          curl \"-o\", cached_source, \"-C\", \"-\", \"-L\", \"-s\", \"--url\", source\n          Puppet.debug \"Success: curl transferred [#{name}]\"\n        rescue Puppet::ExecutionFailure\n          Puppet.debug \"curl did not transfer [#{name}].  Falling back to slower open-uri transfer methods.\"\n          cached_source = source\n        end\n      end\n\n      File.open(cached_source) do |dmg|\n        xml_str = hdiutil \"mount\", \"-plist\", \"-nobrowse\", \"-readonly\", \"-mountrandom\", \"/tmp\", dmg.path\n        ptable = Puppet::Util::Plist.parse_plist(xml_str)\n        # JJM Filter out all mount-paths into a single array, discard the rest.\n        mounts = ptable['system-entities'].collect { |entity|\n          entity['mount-point']\n        }.select { |mountloc|; mountloc }\n        begin\n          found_app = false\n          mounts.each do |fspath|\n            Dir.entries(fspath).select { |f|\n              f =~ /\\.app$/i\n            }.each do |pkg|\n              found_app = true\n              installapp(\"#{fspath}/#{pkg}\", name, source)\n            end\n          end\n          Puppet.debug \"Unable to find .app in .appdmg. #{name} will not be installed.\" unless found_app\n        ensure\n          hdiutil \"eject\", mounts[0]\n        end\n      end\n    ensure\n      FileUtils.remove_entry_secure(tmpdir, true)\n    end\n  end\n\n  def query\n    Puppet::FileSystem.exist?(\"/var/db/.puppet_appdmg_installed_#{@resource[:name]}\") ? { :name => @resource[:name], :ensure => :present } : nil\n  end\n\n  def install\n    source = @resource[:source]\n    unless source\n      self.fail _(\"Mac OS X PKG DMGs must specify a package source.\")\n    end\n    name = @resource[:name]\n    unless name\n      self.fail _(\"Mac OS X PKG DMGs must specify a package name.\")\n    end\n    self.class.installpkgdmg(source, name)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/apple.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\n# OS X Packaging sucks.  We can install packages, but that's about it.\nPuppet::Type.type(:package).provide :apple, :parent => Puppet::Provider::Package do\n  desc \"Package management based on OS X's built-in packaging system.  This is\n    essentially the simplest and least functional package system in existence --\n    it only supports installation; no deletion or upgrades.  The provider will\n    automatically add the `.pkg` extension, so leave that off when specifying\n    the package name.\"\n\n  confine 'os.name' => :darwin\n  commands :installer => \"/usr/sbin/installer\"\n\n  def self.instances\n    instance_by_name.collect do |name|\n      new(\n        :name => name,\n        :provider => :apple,\n        :ensure => :installed\n      )\n    end\n  end\n\n  def self.instance_by_name\n    Dir.entries(\"/Library/Receipts\").find_all { |f|\n      f =~ /\\.pkg$/\n    }.collect { |f|\n      name = f.sub(/\\.pkg/, '')\n      yield name if block_given?\n\n      name\n    }\n  end\n\n  def query\n    Puppet::FileSystem.exist?(\"/Library/Receipts/#{@resource[:name]}.pkg\") ? { :name => @resource[:name], :ensure => :present } : nil\n  end\n\n  def install\n    source = @resource[:source]\n    unless source\n      self.fail _(\"Mac OS X packages must specify a package source\")\n    end\n\n    installer \"-pkg\", source, \"-target\", \"/\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/apt.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/package/version/range'\nrequire_relative '../../../puppet/util/package/version/debian'\n\nPuppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do\n  # Provide sorting functionality\n  include Puppet::Util::Package\n  DebianVersion = Puppet::Util::Package::Version::Debian\n  VersionRange  = Puppet::Util::Package::Version::Range\n  desc \"Package management via `apt-get`.\n\n    This provider supports the `install_options` attribute, which allows command-line flags to be passed to apt-get.\n    These options should be specified as an array where each element is either a\n     string or a hash.\"\n\n  has_feature :versionable, :install_options, :virtual_packages, :version_ranges\n\n  commands :aptget => \"/usr/bin/apt-get\"\n  commands :aptcache => \"/usr/bin/apt-cache\"\n  commands :aptmark => \"/usr/bin/apt-mark\"\n  commands :preseed => \"/usr/bin/debconf-set-selections\"\n\n  defaultfor 'os.family' => :debian\n\n  ENV['DEBIAN_FRONTEND'] = \"noninteractive\"\n\n  # disable common apt helpers to allow non-interactive package installs\n  ENV['APT_LISTBUGS_FRONTEND'] = \"none\"\n  ENV['APT_LISTCHANGES_FRONTEND'] = \"none\"\n\n  def self.defaultto_allow_virtual\n    false\n  end\n\n  def self.instances\n    packages = super\n    manual_marks = aptmark('showmanual').split(\"\\n\")\n    packages.each do |package|\n      package.mark = :manual if manual_marks.include?(package.name)\n    end\n    packages\n  end\n\n  def query\n    hash = super\n\n    if !%i[absent purged].include?(hash[:ensure]) && aptmark('showmanual', @resource[:name]).strip == @resource[:name]\n      hash[:mark] = :manual\n    end\n\n    hash\n  end\n\n  def initialize(value = {})\n    super(value)\n    @property_flush = {}\n  end\n\n  def mark\n    @property_flush[:mark]\n  end\n\n  def mark=(value)\n    @property_flush[:mark] = value\n  end\n\n  def flush\n    # unless we are removing the package mark it if it hasn't already been marked\n    if @property_flush\n      unless @property_flush[:mark] || [:purge, :absent].include?(resource[:ensure])\n        aptmark('manual', resource[:name])\n      end\n    end\n  end\n\n  # A derivative of DPKG; this is how most people actually manage\n  # Debian boxes, and the only thing that differs is that it can\n  # install packages from remote sites.\n\n  # Double negation confuses Rubocop's Layout cops\n  # rubocop:disable Layout\n  def checkforcdrom\n    have_cdrom = begin\n                   !!(File.read(\"/etc/apt/sources.list\") =~ /^[^#]*cdrom:/)\n                 rescue\n                   # This is basically pathological...\n                   false\n                 end\n  # rubocop:enable Layout\n\n    if have_cdrom and @resource[:allowcdrom] != :true\n      raise Puppet::Error, _(\"/etc/apt/sources.list contains a cdrom source; not installing.  Use 'allowcdrom' to override this failure.\")\n    end\n  end\n\n  def best_version(should_range)\n    versions = []\n\n    output = aptcache :madison, @resource[:name]\n    output.each_line do |line|\n      is = line.split('|')[1].strip\n      begin\n        is_version = DebianVersion.parse(is)\n        versions << is_version if should_range.include?(is_version)\n      rescue DebianVersion::ValidationFailure\n        Puppet.debug(\"Cannot parse #{is} as a debian version\")\n      end\n    end\n\n    return versions.max if versions.any?\n\n    Puppet.debug(\"No available version for package #{@resource[:name]} is included in range #{should_range}\")\n    should_range\n  end\n\n  # Install a package using 'apt-get'.  This function needs to support\n  # installing a specific version.\n  def install\n    run_preseed if @resource[:responsefile]\n    should = @resource[:ensure]\n\n    if should.is_a?(String)\n      begin\n        should_range = VersionRange.parse(should, DebianVersion)\n\n        unless should_range.is_a?(VersionRange::Eq)\n          should = best_version(should_range)\n        end\n      rescue VersionRange::ValidationFailure, DebianVersion::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a debian version range, falling through\")\n      end\n    end\n\n    checkforcdrom\n    cmd = %w[-q -y]\n\n    config = @resource[:configfiles]\n    if config\n      if config == :keep\n        cmd << \"-o\" << 'DPkg::Options::=--force-confold'\n      else\n        cmd << \"-o\" << 'DPkg::Options::=--force-confnew'\n      end\n    end\n\n    str = @resource[:name]\n    case should\n    when true, false, Symbol\n      # pass\n    else\n      # Add the package version and --force-yes option\n      str += \"=#{should}\"\n      cmd << \"--force-yes\"\n    end\n\n    cmd += install_options if @resource[:install_options]\n    cmd << :install\n\n    # rubocop:disable Style/RedundantCondition\n    if source\n      cmd << source\n    else\n      cmd << str\n    end\n    # rubocop:enable Style/RedundantCondition\n\n    unhold if properties[:mark] == :hold\n    begin\n      aptget(*cmd)\n    ensure\n      hold if @resource[:mark] == :hold\n    end\n\n    # If a source file was specified, we must make sure the expected version was installed from specified file\n    if source && !%i[present installed].include?(should)\n      is = query\n      raise Puppet::Error, _(\"Could not find package %{name}\") % { name: name } unless is\n\n      version = is[:ensure]\n\n      raise Puppet::Error, _(\"Failed to update to version %{should}, got version %{version} instead\") % { should: should, version: version } unless\n        insync?(version)\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    output = aptcache :policy, @resource[:name]\n\n    if output =~ /Candidate:\\s+(\\S+)\\s/\n      Regexp.last_match(1)\n    else\n      err _(\"Could not find latest version\")\n      nil\n    end\n  end\n\n  #\n  # preseeds answers to dpkg-set-selection from the \"responsefile\"\n  #\n  def run_preseed\n    response = @resource[:responsefile]\n    if response && Puppet::FileSystem.exist?(response)\n      info(_(\"Preseeding %{response} to debconf-set-selections\") % { response: response })\n\n      preseed response\n    else\n      info _(\"No responsefile specified or non existent, not preseeding anything\")\n    end\n  end\n\n  def uninstall\n    run_preseed if @resource[:responsefile]\n    args = ['-y', '-q']\n    args << '--allow-change-held-packages' if properties[:mark] == :hold\n    args << :remove << @resource[:name]\n    aptget(*args)\n  end\n\n  def purge\n    run_preseed if @resource[:responsefile]\n    args = ['-y', '-q']\n    args << '--allow-change-held-packages' if properties[:mark] == :hold\n    args << :remove << '--purge' << @resource[:name]\n    aptget(*args)\n    # workaround a \"bug\" in apt, that already removed packages are not purged\n    super\n  end\n\n  def install_options\n    join_options(@resource[:install_options])\n  end\n\n  def insync?(is)\n    # this is called after the generic version matching logic (insync? for the\n    # type), so we only get here if should != is\n\n    return false unless is && is != :absent\n\n    # if 'should' is a range and 'is' a debian version we should check if 'should' includes 'is'\n    should = @resource[:ensure]\n\n    return false unless is.is_a?(String) && should.is_a?(String)\n\n    begin\n      should_range = VersionRange.parse(should, DebianVersion)\n    rescue VersionRange::ValidationFailure, DebianVersion::ValidationFailure\n      Puppet.debug(\"Cannot parse #{should} as a debian version range\")\n      return false\n    end\n\n    begin\n      is_version = DebianVersion.parse(is)\n    rescue DebianVersion::ValidationFailure\n      Puppet.debug(\"Cannot parse #{is} as a debian version\")\n      return false\n    end\n    should_range.include?(is_version)\n  end\n\n  private\n\n  def source\n    @source ||= @resource[:source]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/aptitude.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :aptitude, :parent => :apt, :source => :dpkg do\n  desc \"Package management via `aptitude`.\"\n\n  has_feature :versionable\n\n  commands :aptitude => \"/usr/bin/aptitude\"\n  commands :aptcache => \"/usr/bin/apt-cache\"\n\n  ENV['DEBIAN_FRONTEND'] = \"noninteractive\"\n\n  def aptget(*args)\n    args.flatten!\n    # Apparently aptitude hasn't always supported a -q flag.\n    args.delete(\"-q\") if args.include?(\"-q\")\n    args.delete(\"--force-yes\") if args.include?(\"--force-yes\")\n    output = aptitude(*args)\n\n    # Yay, stupid aptitude doesn't throw an error when the package is missing.\n    if args.include?(:install) and output.to_s =~ /Couldn't find any package/\n      raise Puppet::Error, _(\"Could not find package %{name}\") % { name: name }\n    end\n  end\n\n  def purge\n    aptitude '-y', 'purge', @resource[:name]\n  end\n\n  private\n\n  def source\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/aptrpm.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :aptrpm, :parent => :rpm, :source => :rpm do\n  # Provide sorting functionality\n  include Puppet::Util::Package\n\n  desc \"Package management via `apt-get` ported to `rpm`.\"\n\n  has_feature :versionable\n\n  commands :aptget => \"apt-get\"\n  commands :aptcache => \"apt-cache\"\n  commands :rpm => \"rpm\"\n\n  # Mixing confine statements, control expressions, and exception handling\n  # confuses Rubocop's Layout cops, so we disable them entirely.\n  # rubocop:disable Layout\n  if command('rpm')\n    confine :true => begin\n      rpm('-ql', 'rpm')\n      rescue Puppet::ExecutionFailure\n        false\n      else\n        true\n      end\n  end\n  # rubocop:enable Layout\n\n  # Install a package using 'apt-get'.  This function needs to support\n  # installing a specific version.\n  def install\n    should = @resource.should(:ensure)\n\n    str = @resource[:name]\n    case should\n    when true, false, Symbol\n      # pass\n    else\n      # Add the package version\n      str += \"=#{should}\"\n    end\n    cmd = %w[-q -y]\n\n    cmd << 'install' << str\n\n    aptget(*cmd)\n  end\n\n  # What's the latest package version available?\n  def latest\n    output = aptcache :showpkg, @resource[:name]\n\n    if output =~ /Versions:\\s*\\n((\\n|.)+)^$/\n      versions = Regexp.last_match(1)\n      available_versions = versions.split(/\\n/).filter_map { |version|\n        if version =~ /^([^(]+)\\(/\n          Regexp.last_match(1)\n        else\n          warning _(\"Could not match version '%{version}'\") % { version: version }\n          nil\n        end\n      }.sort { |a, b| versioncmp(a, b) }\n\n      if available_versions.length == 0\n        debug \"No latest version\"\n        print output if Puppet[:debug]\n      end\n\n      # Get the latest and greatest version number\n      available_versions.pop\n    else\n      err _(\"Could not match string\")\n    end\n  end\n\n  def update\n    install\n  end\n\n  def uninstall\n    aptget \"-y\", \"-q\", 'remove', @resource[:name]\n  end\n\n  def purge\n    aptget '-y', '-q', 'remove', '--purge', @resource[:name]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/blastwave.rb",
    "content": "# frozen_string_literal: true\n\n# Packaging using Blastwave's pkg-get program.\nPuppet::Type.type(:package).provide :blastwave, :parent => :sun, :source => :sun do\n  desc \"Package management using Blastwave.org's `pkg-get` command on Solaris.\"\n  pkgget = \"pkg-get\"\n  pkgget = \"/opt/csw/bin/pkg-get\" if FileTest.executable?(\"/opt/csw/bin/pkg-get\")\n\n  confine 'os.family' => :solaris\n\n  commands :pkgget => pkgget\n\n  def pkgget_with_cat(*args)\n    Puppet::Util.withenv(:PAGER => \"/usr/bin/cat\") { pkgget(*args) }\n  end\n\n  def self.extended(mod)\n    unless command(:pkgget) != \"pkg-get\"\n      raise Puppet::Error,\n            _(\"The pkg-get command is missing; blastwave packaging unavailable\")\n    end\n\n    unless Puppet::FileSystem.exist?(\"/var/pkg-get/admin\")\n      Puppet.notice _(\"It is highly recommended you create '/var/pkg-get/admin'.\")\n      Puppet.notice _(\"See /var/pkg-get/admin-fullauto\")\n    end\n  end\n\n  def self.instances(hash = {})\n    blastlist(hash).collect do |bhash|\n      bhash.delete(:avail)\n      new(bhash)\n    end\n  end\n\n  # Turn our blastwave listing into a bunch of hashes.\n  def self.blastlist(hash)\n    command = [\"-c\"]\n\n    command << hash[:justme] if hash[:justme]\n\n    output = Puppet::Util.withenv(:PAGER => \"/usr/bin/cat\") { pkgget command }\n\n    list = output.split(\"\\n\").filter_map do |line|\n      next if line =~ /^#/\n      next if line =~ /^WARNING/\n      next if line =~ /localrev\\s+remoterev/\n\n      blastsplit(line)\n    end\n\n    if hash[:justme]\n      list[0]\n    else\n      list.reject! { |h|\n        h[:ensure] == :absent\n      }\n      list\n    end\n  end\n\n  # Split the different lines into hashes.\n  def self.blastsplit(line)\n    if line =~ /\\s*(\\S+)\\s+((\\[Not installed\\])|(\\S+))\\s+(\\S+)/\n      hash = {}\n      hash[:name] = Regexp.last_match(1)\n      hash[:ensure] = if Regexp.last_match(2) == \"[Not installed]\"\n                        :absent\n                      else\n                        Regexp.last_match(2)\n                      end\n      hash[:avail] = Regexp.last_match(5)\n\n      hash[:avail] = hash[:ensure] if hash[:avail] == \"SAME\"\n\n      # Use the name method, so it works with subclasses.\n      hash[:provider] = name\n\n      hash\n    else\n      Puppet.warning _(\"Cannot match %{line}\") % { line: line }\n      nil\n    end\n  end\n\n  def install\n    pkgget_with_cat \"-f\", :install, @resource[:name]\n  end\n\n  # Retrieve the version from the current package file.\n  def latest\n    hash = self.class.blastlist(:justme => @resource[:name])\n    hash[:avail]\n  end\n\n  def query\n    hash = self.class.blastlist(:justme => @resource[:name])\n    hash || { :ensure => :absent }\n  end\n\n  # Remove the old package, and install the new one\n  def update\n    pkgget_with_cat \"-f\", :upgrade, @resource[:name]\n  end\n\n  def uninstall\n    pkgget_with_cat \"-f\", :remove, @resource[:name]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/dnf.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :dnf, :parent => :yum do\n  desc \"Support via `dnf`.\n\n  Using this provider's `uninstallable` feature will not remove dependent packages. To\n  remove dependent packages with this provider use the `purgeable` feature, but note this\n  feature is destructive and should be used with the utmost care.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to dnf.\n  These options should be specified as an array where each element is either\n   a string or a hash.\"\n\n  has_feature :install_options, :versionable, :virtual_packages, :install_only, :version_ranges\n\n  commands :cmd => \"dnf\", :rpm => \"rpm\"\n\n  # Note: this confine was borrowed from the Yum provider. The\n  # original purpose (from way back in 2007) was to make sure we\n  # never try to use RPM on a machine without it. We think this\n  # has probably become obsolete with the way `commands` work, so\n  # we should investigate removing it at some point.\n  #\n  # Mixing confine statements, control expressions, and exception handling\n  # confuses Rubocop's Layout cops, so we disable them entirely.\n  # rubocop:disable Layout\n  if command('rpm')\n    confine :true => begin\n      rpm('--version')\n      rescue Puppet::ExecutionFailure\n        false\n      else\n        true\n      end\n  end\n  # rubocop:enable Layout\n\n  defaultfor 'os.name' => :fedora\n  notdefaultfor 'os.name' => :fedora, 'os.release.major' => (19..21).to_a\n  defaultfor 'os.family' => :redhat\n  notdefaultfor 'os.family' => :redhat, 'os.release.major' => (4..7).to_a\n  defaultfor 'os.name' => :amazon, 'os.release.major' => [\"2023\"]\n\n  def self.update_command\n    # In DNF, update is deprecated for upgrade\n    'upgrade'\n  end\n\n  # The value to pass to DNF as its error output level.\n  # DNF differs from Yum slightly with regards to error outputting.\n  #\n  # @param None\n  # @return [String]\n  def self.error_level\n    '1'\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/dnfmodule.rb",
    "content": "# frozen_string_literal: true\n\n# dnfmodule - A puppet package provider for DNF modules\n#\n# Installing a module:\n#  package { 'postgresql':\n#   provider => 'dnfmodule',\n#   ensure   => '9.6',  # install a specific stream\n#   flavor   => 'client',  # install a specific profile\n# }\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :dnfmodule, :parent => :dnf do\n  has_feature :installable, :uninstallable, :versionable, :supports_flavors, :disableable\n  # has_feature :upgradeable\n  # it's not (yet) feasible to make this upgradeable since module streams don't\n  # always have matching version types (i.e. idm has streams DL1 and client,\n  # other modules have semver streams, others have string streams... we cannot\n  # programatically determine a latest version for ensure => 'latest'\n\n  commands :dnf => '/usr/bin/dnf'\n\n  def self.current_version\n    @current_version ||= dnf('--version').split.first\n  end\n\n  def self.prefetch(packages)\n    if Puppet::Util::Package.versioncmp(current_version, '3.0.1') < 0\n      raise Puppet::Error, _(\"Modules are not supported on DNF versions lower than 3.0.1\")\n    end\n\n    super\n  end\n\n  def self.instances\n    packages = []\n    cmd = \"#{command(:dnf)} module list -y -d 0 -e #{error_level}\"\n    execute(cmd).each_line do |line|\n      # select only lines with actual packages since DNF clutters the output\n      next unless line =~ /\\[[eix]\\][, ]/\n\n      line.gsub!(/\\[d\\]/, '') # we don't care about the default flag\n\n      flavor = if line.include?('[i]')\n                 line.split('[i]').first.split.last\n               else\n                 :absent\n               end\n\n      packages << new(\n        name: line.split[0],\n        ensure: if line.include?('[x]')\n                  :disabled\n                else\n                  line.split[1]\n                end,\n        flavor: flavor,\n        provider: name\n      )\n    end\n    packages\n  end\n\n  def query\n    pkg = self.class.instances.find do |package|\n      @resource[:name] == package.name\n    end\n    pkg ? pkg.properties : nil\n  end\n\n  # to install specific streams and profiles:\n  # $ dnf module install module-name:stream/profile\n  # $ dnf module install perl:5.24/minimal\n  # if unspecified, they will be defaulted (see [d] param in dnf module list output)\n  def install\n    # ensure we start fresh (remove existing stream)\n    uninstall unless [:absent, :purged].include?(@property_hash[:ensure])\n\n    args = @resource[:name].dup\n    case @resource[:ensure]\n    when true, false, Symbol\n      # pass\n    else\n      args << \":#{@resource[:ensure]}\"\n    end\n    args << \"/#{@resource[:flavor]}\" if @resource[:flavor]\n\n    if @resource[:enable_only] == true\n      enable(args)\n    else\n      begin\n        execute([command(:dnf), 'module', 'install', '-d', '0', '-e', self.class.error_level, '-y', args])\n      rescue Puppet::ExecutionFailure => e\n        # module has no default profile and no profile was requested, so just enable the stream\n        # DNF versions prior to 4.2.8 do not need this workaround\n        # see https://bugzilla.redhat.com/show_bug.cgi?id=1669527\n        if @resource[:flavor].nil? && e.message =~ /^(?:missing|broken) groups or modules: #{Regexp.quote(args)}$/\n          enable(args)\n        else\n          raise\n        end\n      end\n    end\n  end\n\n  # should only get here when @resource[ensure] is :disabled\n  def insync?(is)\n    if resource[:ensure] == :disabled\n      # in sync only if package is already disabled\n      pkg = self.class.instances.find do |package|\n        @resource[:name] == package.name && package.properties[:ensure] == :disabled\n      end\n      return true if pkg\n    end\n    false\n  end\n\n  def enable(args = @resource[:name])\n    execute([command(:dnf), 'module', 'enable', '-d', '0', '-e', self.class.error_level, '-y', args])\n  end\n\n  def uninstall\n    execute([command(:dnf), 'module', 'remove', '-d', '0', '-e', self.class.error_level, '-y', @resource[:name]])\n    reset # reset module to the default stream\n  end\n\n  def disable(args = @resource[:name])\n    execute([command(:dnf), 'module', 'disable', '-d', '0', '-e', self.class.error_level, '-y', args])\n  end\n\n  def reset\n    execute([command(:dnf), 'module', 'reset', '-d', '0', '-e', self.class.error_level, '-y', @resource[:name]])\n  end\n\n  def flavor\n    @property_hash[:flavor]\n  end\n\n  def flavor=(value)\n    install if flavor != @resource.should(:flavor)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/dpkg.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :dpkg, :parent => Puppet::Provider::Package do\n  desc \"Package management via `dpkg`.  Because this only uses `dpkg`\n    and not `apt`, you must specify the source of any packages you want\n    to manage.\"\n\n  has_feature :holdable, :virtual_packages\n  commands :dpkg => \"/usr/bin/dpkg\"\n  commands :dpkg_deb => \"/usr/bin/dpkg-deb\"\n  commands :dpkgquery => \"/usr/bin/dpkg-query\"\n\n  # Performs a dpkgquery call with a pipe so that output can be processed\n  # inline in a passed block.\n  # @param args [Array<String>] any command line arguments to be appended to the command\n  # @param block expected to be passed on to execpipe\n  # @return whatever the block returns\n  # @see Puppet::Util::Execution.execpipe\n  # @api private\n  def self.dpkgquery_piped(*args, &block)\n    cmd = args.unshift(command(:dpkgquery))\n    Puppet::Util::Execution.execpipe(cmd, &block)\n  end\n\n  def self.instances\n    packages = []\n\n    # list out all of the packages\n    dpkgquery_piped('-W', '--showformat', self::DPKG_QUERY_FORMAT_STRING) do |pipe|\n      # now turn each returned line into a package object\n      pipe.each_line do |line|\n        hash = parse_line(line)\n        if hash\n          packages << new(hash)\n        end\n      end\n    end\n\n    packages\n  end\n\n  private\n\n  # Note: self:: is required here to keep these constants in the context of what will\n  # eventually become this Puppet::Type::Package::ProviderDpkg class.\n  self::DPKG_QUERY_FORMAT_STRING = \"'${Status} ${Package} ${Version}\\\\n'\"\n  self::DPKG_QUERY_PROVIDES_FORMAT_STRING = \"'${Status} ${Package} ${Version} [${Provides}]\\\\n'\"\n  self::FIELDS_REGEX = /^'?(\\S+) +(\\S+) +(\\S+) (\\S+) (\\S*)$/\n  self::FIELDS_REGEX_WITH_PROVIDES = /^'?(\\S+) +(\\S+) +(\\S+) (\\S+) (\\S*) \\[.*\\]$/\n  self::FIELDS = [:desired, :error, :status, :name, :ensure]\n\n  def self.defaultto_allow_virtual\n    false\n  end\n\n  # @param line [String] one line of dpkg-query output\n  # @return [Hash,nil] a hash of FIELDS or nil if we failed to match\n  # @api private\n  def self.parse_line(line, regex = self::FIELDS_REGEX)\n    hash = nil\n\n    match = regex.match(line)\n    if match\n      hash = {}\n\n      self::FIELDS.zip(match.captures) do |field, value|\n        hash[field] = value\n      end\n\n      hash[:provider] = name\n\n      if hash[:status] == 'not-installed'\n        hash[:ensure] = :purged\n      elsif %w[config-files half-installed unpacked half-configured].include?(hash[:status])\n        hash[:ensure] = :absent\n      end\n      hash[:mark] = hash[:desired] == 'hold' ? :hold : :none\n    else\n      Puppet.debug(\"Failed to match dpkg-query line #{line.inspect}\")\n    end\n\n    hash\n  end\n\n  public\n\n  def install\n    file = @resource[:source]\n    unless file\n      raise ArgumentError, _(\"You cannot install dpkg packages without a source\")\n    end\n\n    args = []\n\n    if @resource[:configfiles] == :keep\n      args << '--force-confold'\n    else\n      args << '--force-confnew'\n    end\n    args << '-i' << file\n\n    unhold if properties[:mark] == :hold\n    begin\n      dpkg(*args)\n    ensure\n      hold if @resource[:mark] == :hold\n    end\n  end\n\n  def update\n    install\n  end\n\n  # Return the version from the package.\n  def latest\n    source = @resource[:source]\n    unless source\n      @resource.fail _(\"Could not update: You cannot install dpkg packages without a source\")\n    end\n    output = dpkg_deb \"--show\", source\n    matches = /^(\\S+)\\t(\\S+)$/.match(output).captures\n    warning _(\"source doesn't contain named package, but %{name}\") % { name: matches[0] } unless matches[0].match(Regexp.escape(@resource[:name]))\n    matches[1]\n  end\n\n  def query\n    hash = nil\n\n    # list out our specific package\n    begin\n      if @resource.allow_virtual?\n        output = dpkgquery(\n          \"-W\",\n          \"--showformat\",\n          self.class::DPKG_QUERY_PROVIDES_FORMAT_STRING\n          # the regex searches for the resource[:name] in the dpkquery result in which the Provides field is also available\n          # it will search for the packages only in the brackets ex: [rubygems]\n        ).lines.find { |package| package.match(/[\\[ ](#{Regexp.escape(@resource[:name])})[\\],]/) }\n        if output\n          hash = self.class.parse_line(output, self.class::FIELDS_REGEX_WITH_PROVIDES)\n          Puppet.info(\"Package #{@resource[:name]} is virtual, defaulting to #{hash[:name]}\")\n          @resource[:name] = hash[:name]\n        end\n      end\n      output = dpkgquery(\n        \"-W\",\n        \"--showformat\",\n        self.class::DPKG_QUERY_FORMAT_STRING,\n        @resource[:name]\n      )\n      hash = self.class.parse_line(output)\n    rescue Puppet::ExecutionFailure\n      # dpkg-query exits 1 if the package is not found.\n      return { :ensure => :purged, :status => 'missing', :name => @resource[:name], :error => 'ok' }\n    end\n\n    hash ||= { :ensure => :absent, :status => 'missing', :name => @resource[:name], :error => 'ok' }\n\n    if hash[:error] != \"ok\"\n      raise Puppet::Error, \"Package #{hash[:name]}, version #{hash[:ensure]} is in error state: #{hash[:error]}\"\n    end\n\n    hash\n  end\n\n  def uninstall\n    dpkg \"-r\", @resource[:name]\n  end\n\n  def purge\n    dpkg \"--purge\", @resource[:name]\n  end\n\n  def hold\n    Tempfile.open('puppet_dpkg_set_selection') do |tmpfile|\n      tmpfile.write(\"#{@resource[:name]} hold\\n\")\n      tmpfile.flush\n      execute([:dpkg, \"--set-selections\"], :failonfail => false, :combine => false, :stdinfile => tmpfile.path.to_s)\n    end\n  end\n\n  def unhold\n    Tempfile.open('puppet_dpkg_set_selection') do |tmpfile|\n      tmpfile.write(\"#{@resource[:name]} install\\n\")\n      tmpfile.flush\n      execute([:dpkg, \"--set-selections\"], :failonfail => false, :combine => false, :stdinfile => tmpfile.path.to_s)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/fink.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :fink, :parent => :dpkg, :source => :dpkg do\n  # Provide sorting functionality\n  include Puppet::Util::Package\n\n  desc \"Package management via `fink`.\"\n\n  commands :fink => \"/sw/bin/fink\"\n  commands :aptget => \"/sw/bin/apt-get\"\n  commands :aptcache => \"/sw/bin/apt-cache\"\n  commands :dpkgquery => \"/sw/bin/dpkg-query\"\n\n  has_feature :versionable\n\n  # A derivative of DPKG; this is how most people actually manage\n  # Debian boxes, and the only thing that differs is that it can\n  # install packages from remote sites.\n\n  def finkcmd(*args)\n    fink(*args)\n  end\n\n  # Install a package using 'apt-get'.  This function needs to support\n  # installing a specific version.\n  def install\n    run_preseed if @resource[:responsefile]\n    should = @resource.should(:ensure)\n\n    str = @resource[:name]\n    case should\n    when true, false, Symbol\n      # pass\n    else\n      # Add the package version\n      str += \"=#{should}\"\n    end\n    cmd = %w[-b -q -y]\n\n    cmd << :install << str\n\n    unhold if properties[:mark] == :hold\n    begin\n      finkcmd(cmd)\n    ensure\n      hold if @resource[:mark] == :hold\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    output = aptcache :policy, @resource[:name]\n\n    if output =~ /Candidate:\\s+(\\S+)\\s/\n      Regexp.last_match(1)\n    else\n      err _(\"Could not find latest version\")\n      nil\n    end\n  end\n\n  #\n  # preseeds answers to dpkg-set-selection from the \"responsefile\"\n  #\n  def run_preseed\n    response = @resource[:responsefile]\n    if response && Puppet::FileSystem.exist?(response)\n      info(_(\"Preseeding %{response} to debconf-set-selections\") % { response: response })\n\n      preseed response\n    else\n      info _(\"No responsefile specified or non existent, not preseeding anything\")\n    end\n  end\n\n  def update\n    install\n  end\n\n  def uninstall\n    unhold if properties[:mark] == :hold\n    begin\n      finkcmd \"-y\", \"-q\", :remove, @model[:name]\n    rescue StandardError, LoadError => e\n      hold if properties[:mark] == :hold\n      raise e\n    end\n  end\n\n  def purge\n    unhold if properties[:mark] == :hold\n    begin\n      aptget '-y', '-q', 'remove', '--purge', @resource[:name]\n    rescue StandardError, LoadError => e\n      hold if properties[:mark] == :hold\n      raise e\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/freebsd.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :freebsd, :parent => :openbsd do\n  desc \"The specific form of package management on FreeBSD.  This is an\n    extremely quirky packaging system, in that it freely mixes between\n    ports and packages.  Apparently all of the tools are written in Ruby,\n    so there are plans to rewrite this support to directly use those\n    libraries.\"\n\n  commands :pkginfo => \"/usr/sbin/pkg_info\",\n           :pkgadd => \"/usr/sbin/pkg_add\",\n           :pkgdelete => \"/usr/sbin/pkg_delete\"\n\n  confine 'os.name' => :freebsd\n\n  def self.listcmd\n    command(:pkginfo)\n  end\n\n  def install\n    if @resource[:source] =~ %r{/$}\n      if @resource[:source] =~ /^(ftp|https?):/\n        Puppet::Util.withenv :PACKAGESITE => @resource[:source] do\n          pkgadd \"-r\", @resource[:name]\n        end\n      else\n        Puppet::Util.withenv :PKG_PATH => @resource[:source] do\n          pkgadd @resource[:name]\n        end\n      end\n    else\n      Puppet.warning _(\"source is defined but does not have trailing slash, ignoring %{source}\") % { source: @resource[:source] } if @resource[:source]\n      pkgadd \"-r\", @resource[:name]\n    end\n  end\n\n  def query\n    self.class.instances.each do |provider|\n      if provider.name == @resource.name\n        return provider.properties\n      end\n    end\n    nil\n  end\n\n  def uninstall\n    pkgdelete \"#{@resource[:name]}-#{@resource.should(:ensure)}\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/gem.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/package/version/gem'\nrequire_relative '../../../puppet/util/package/version/range'\nrequire_relative '../../../puppet/provider/package_targetable'\nrequire 'uri'\n\n# Ruby gems support.\nPuppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package::Targetable do\n  desc \"Ruby Gem support. If a URL is passed via `source`, then that URL is\n    appended to the list of remote gem repositories; to ensure that only the\n    specified source is used, also pass `--clear-sources` via `install_options`.\n    If source is present but is not a valid URL, it will be interpreted as the\n    path to a local gem file. If source is not present, the gem will be\n    installed from the default gem repositories. Note that to modify this for Windows, it has to be a valid URL.\n\n    This provider supports the `install_options` and `uninstall_options` attributes,\n    which allow command-line flags to be passed to the gem command.\n    These options should be specified as an array where each element is either a\n    string or a hash.\"\n\n  has_feature :versionable, :install_options, :uninstall_options, :targetable, :version_ranges\n\n  GEM_VERSION =       Puppet::Util::Package::Version::Gem\n  GEM_VERSION_RANGE = Puppet::Util::Package::Version::Range\n\n  # Override the specificity method to return 1 if gem is not set as default provider\n  def self.specificity\n    match = default_match\n    length = match ? match.length : 0\n\n    return 1 if length == 0\n\n    super\n  end\n\n  # Define the default provider package command name when the provider is targetable.\n  # Required by Puppet::Provider::Package::Targetable::resource_or_provider_command\n\n  def self.provider_command\n    if Puppet::Util::Platform.windows?\n      Puppet::Util.withenv(PATH: windows_path_without_puppet_bin) { command(:gemcmd) }\n    else\n      command(:gemcmd)\n    end\n  end\n\n  # Define the default provider package command as optional when the provider is targetable.\n  # Doing do defers the evaluation of provider suitability until all commands are evaluated.\n\n  has_command(:gemcmd, 'gem') do\n    is_optional\n  end\n\n  # Having puppet/bin in PATH makes gem provider to use puppet/bin/gem\n  # This is an utility methods that reads the PATH and returns a string\n  # that contains the content of PATH but without puppet/bin dir.\n  # This is used to pass a custom PATH and execute commands in a controlled environment\n  def self.windows_path_without_puppet_bin\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @path ||= ENV['PATH'].split(File::PATH_SEPARATOR)\n                         .reject { |dir| dir =~ /puppet\\\\bin$/ }\n                         .join(File::PATH_SEPARATOR)\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  private_class_method :windows_path_without_puppet_bin\n\n  # CommandDefiner in provider.rb creates convenience execution methods that set failonfail, combine, and optionally, environment.\n  # And when a child provider defines its own command via commands() or has_command(), the provider-specific path is always returned by command().\n  # But when the convenience execution method is invoked, the last convenience method to be defined is executed.\n  # This makes invoking those convenience execution methods unsuitable for inherited providers.\n  #\n  # In this case, causing the puppet_gem provider to inherit the parent gem provider's convenience gemcmd() methods, with the wrong path.\n\n  def self.execute_gem_command(command, command_options, custom_environment = {})\n    validate_command(command)\n    cmd = [command] << command_options\n\n    custom_environment = { 'HOME' => ENV.fetch('HOME', nil) }.merge(custom_environment)\n\n    if Puppet::Util::Platform.windows?\n      custom_environment[:PATH] = windows_path_without_puppet_bin\n    end\n\n    # This uses an unusual form of passing the command and args as [<cmd>, [<arg1>, <arg2>, ...]]\n    execute(cmd, { :failonfail => true, :combine => true, :custom_environment => custom_environment })\n  end\n\n  def self.instances(target_command = nil)\n    if target_command\n      command = target_command\n    else\n      command = provider_command\n      # The default provider package command is optional.\n      return [] unless command\n    end\n\n    gemlist(:command => command, :local => true).collect do |pkg|\n      # Track the command when the provider is targetable.\n      pkg[:command] = command\n      new(pkg)\n    end\n  end\n\n  def self.gemlist(options)\n    command_options = [\"list\"]\n\n    if options[:local]\n      command_options << \"--local\"\n    else\n      command_options << \"--remote\"\n    end\n    if options[:source]\n      command_options << \"--source\" << options[:source]\n    end\n    name = options[:justme]\n    if name\n      command_options << '\\A' + name + '\\z'\n    end\n\n    begin\n      list = execute_gem_command(options[:command], command_options).lines\n                                                                    .filter_map { |set| gemsplit(set) }\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, _(\"Could not list gems: %{detail}\") % { detail: detail }, detail.backtrace\n    end\n\n    if options[:justme]\n      list.shift\n    else\n      list\n    end\n  end\n\n  def self.gemsplit(desc)\n    # `gem list` when output console has a line like:\n    # *** LOCAL GEMS ***\n    # but when it's not to the console that line\n    # and all blank lines are stripped\n    # so we don't need to check for them\n\n    if desc =~ /^(\\S+)\\s+\\((.+)\\)/\n      gem_name = Regexp.last_match(1)\n      versions = Regexp.last_match(2).sub('default: ', '').split(/,\\s*/)\n      {\n        :name => gem_name,\n        :ensure => versions.map { |v| v.split[0] },\n        :provider => name\n      }\n    else\n      Puppet.warning _(\"Could not match %{desc}\") % { desc: desc } unless desc.chomp.empty?\n      nil\n    end\n  end\n\n  def insync?(is)\n    return false unless is && is != :absent\n\n    is = [is] unless is.is_a? Array\n    should = @resource[:ensure]\n\n    unless should =~ Regexp.union(/,/, Gem::Requirement::PATTERN)\n      begin\n        should_range = GEM_VERSION_RANGE.parse(should, GEM_VERSION)\n      rescue GEM_VERSION_RANGE::ValidationFailure, GEM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a ruby gem version range\")\n        return false\n      end\n\n      return is.any? do |version|\n        should_range.include?(GEM_VERSION.parse(version))\n      rescue GEM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{version} as a ruby gem version\")\n        false\n      end\n    end\n\n    begin\n      # Range intersections are not supported by Gem::Requirement, so just split by comma.\n      dependency = Gem::Dependency.new('', should.split(','))\n    rescue ArgumentError\n      # Bad requirements will cause an error during gem command invocation, so just return not in sync\n      return false\n    end\n\n    # Check if any version matches the dependency\n    is.any? { |version| dependency.match?('', version) }\n  end\n\n  def rubygem_version(command)\n    command_options = [\"--version\"]\n    self.class.execute_gem_command(command, command_options)\n  end\n\n  def install(useversion = true)\n    command = resource_or_provider_command\n    command_options = [\"install\"]\n    command_options += install_options if resource[:install_options]\n    should = resource[:ensure]\n\n    unless should =~ Regexp.union(/,/, Gem::Requirement::PATTERN)\n      begin\n        should_range = GEM_VERSION_RANGE.parse(should, GEM_VERSION)\n        should = should_range.to_gem_version\n        useversion = true\n      rescue GEM_VERSION_RANGE::ValidationFailure, GEM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a ruby gem version range. Falling through.\")\n      end\n    end\n\n    if Puppet::Util::Platform.windows?\n      command_options << \"-v\" << %Q(\"#{should}\") if useversion && !should.is_a?(Symbol)\n    elsif useversion && !should.is_a?(Symbol)\n      command_options << \"-v\" << should\n    end\n\n    if Puppet::Util::Package.versioncmp(rubygem_version(command), '2.0.0') == -1\n      command_options << \"--no-rdoc\" << \"--no-ri\"\n    else\n      command_options << \"--no-document\"\n    end\n\n    source = resource[:source]\n    if source\n      begin\n        uri = URI.parse(source)\n      rescue => detail\n        self.fail Puppet::Error, _(\"Invalid source '%{uri}': %{detail}\") % { uri: uri, detail: detail }, detail\n      end\n\n      case uri.scheme\n      when nil\n        # no URI scheme => interpret the source as a local file\n        command_options << source\n      when /file/i\n        command_options << uri.path\n      when 'puppet'\n        # we don't support puppet:// URLs (yet)\n        raise Puppet::Error, _(\"puppet:// URLs are not supported as gem sources\")\n      else\n        # check whether it's an absolute file path to help Windows out\n        if Puppet::Util.absolute_path?(source)\n          command_options << source\n        else\n          # interpret it as a gem repository\n          command_options << \"--source\" << source.to_s << resource[:name]\n        end\n      end\n    else\n      command_options << resource[:name]\n    end\n\n    output = self.class.execute_gem_command(command, command_options)\n    # Apparently some gem versions don't exit non-0 on failure.\n    self.fail _(\"Could not install: %{output}\") % { output: output.chomp } if output.include?(\"ERROR\")\n  end\n\n  def latest\n    command = resource_or_provider_command\n    options = { :command => command, :justme => resource[:name] }\n    options[:source] = resource[:source] unless resource[:source].nil?\n    pkg = self.class.gemlist(options)\n    pkg[:ensure][0]\n  end\n\n  def query\n    command = resource_or_provider_command\n    options = { :command => command, :justme => resource[:name], :local => true }\n    pkg = self.class.gemlist(options)\n    pkg[:command] = command unless pkg.nil?\n    pkg\n  end\n\n  def uninstall\n    command = resource_or_provider_command\n    command_options = [\"uninstall\"]\n    command_options << \"--executables\" << \"--all\" << resource[:name]\n    command_options += uninstall_options if resource[:uninstall_options]\n    output = self.class.execute_gem_command(command, command_options)\n    # Apparently some gem versions don't exit non-0 on failure.\n    self.fail _(\"Could not uninstall: %{output}\") % { output: output.chomp } if output.include?(\"ERROR\")\n  end\n\n  def update\n    install(false)\n  end\n\n  def install_options\n    join_options(resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(resource[:uninstall_options])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/hpux.rb",
    "content": "# frozen_string_literal: true\n\n# HP-UX packaging.\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :hpux, :parent => Puppet::Provider::Package do\n  desc \"HP-UX's packaging system.\"\n\n  commands :swinstall => \"/usr/sbin/swinstall\",\n           :swlist => \"/usr/sbin/swlist\",\n           :swremove => \"/usr/sbin/swremove\"\n\n  confine 'os.name' => \"hp-ux\"\n\n  defaultfor 'os.name' => \"hp-ux\"\n\n  def self.instances\n    # TODO:  This is very hard on HP-UX!\n    []\n  end\n\n  # source and name are required\n  def install\n    raise ArgumentError, _(\"source must be provided to install HP-UX packages\") unless resource[:source]\n\n    args = standard_args + [\"-s\", resource[:source], resource[:name]]\n    swinstall(*args)\n  end\n\n  def query\n    swlist resource[:name]\n    { :ensure => :present }\n  rescue\n    { :ensure => :absent }\n  end\n\n  def uninstall\n    args = standard_args + [resource[:name]]\n    swremove(*args)\n  end\n\n  def standard_args\n    [\"-x\", \"mount_all_filesystems=false\"]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/macports.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/provider/command'\n\nPuppet::Type.type(:package).provide :macports, :parent => Puppet::Provider::Package do\n  desc \"Package management using MacPorts on OS X.\n\n    Supports MacPorts versions and revisions, but not variants.\n    Variant preferences may be specified using\n    [the MacPorts variants.conf file](http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf).\n\n    When specifying a version in the Puppet DSL, only specify the version, not the revision.\n    Revisions are only used internally for ensuring the latest version/revision of a port.\n  \"\n\n  confine 'os.name' => :darwin\n\n  has_command(:port, \"/opt/local/bin/port\") do\n    environment :HOME => \"/opt/local\"\n  end\n\n  has_feature :installable\n  has_feature :uninstallable\n  has_feature :upgradeable\n  has_feature :versionable\n\n  def self.parse_installed_query_line(line)\n    regex = /(\\S+)\\s+@(\\S+)_(\\d+).*\\(active\\)/\n    fields = [:name, :ensure, :revision]\n    hash_from_line(line, regex, fields)\n  end\n\n  def self.parse_info_query_line(line)\n    regex = /(\\S+)\\s+(\\S+)/\n    fields = [:version, :revision]\n    hash_from_line(line, regex, fields)\n  end\n\n  def self.hash_from_line(line, regex, fields)\n    hash = {}\n    match = regex.match(line)\n    if match\n      fields.zip(match.captures) { |field, value|\n        hash[field] = value\n      }\n      hash[:provider] = name\n      return hash\n    end\n    nil\n  end\n\n  def self.instances\n    packages = []\n    port(\"-q\", :installed).each_line do |line|\n      hash = parse_installed_query_line(line)\n      if hash\n        packages << new(hash)\n      end\n    end\n    packages\n  end\n\n  def install\n    should = @resource.should(:ensure)\n    if [:latest, :installed, :present].include?(should)\n      port(\"-q\", :install, @resource[:name])\n    else\n      port(\"-q\", :install, @resource[:name], \"@#{should}\")\n    end\n    # MacPorts now correctly exits non-zero with appropriate errors in\n    # situations where a port cannot be found or installed.\n  end\n\n  def query\n    result = self.class.parse_installed_query_line(execute([command(:port), \"-q\", :installed, @resource[:name]], :failonfail => false, :combine => false))\n    return {} if result.nil?\n\n    result\n  end\n\n  def latest\n    # We need both the version and the revision to be confident\n    # we've got the latest revision of a specific version\n    # Note we're still not doing anything with variants here.\n    info_line = execute([command(:port), \"-q\", :info, \"--line\", \"--version\", \"--revision\", @resource[:name]], :failonfail => false, :combine => false)\n    return nil if info_line == \"\"\n\n    newest = self.class.parse_info_query_line(info_line)\n    if newest\n      current = query\n      # We're doing some fiddling behind the scenes here to cope with updated revisions.\n      # If we're already at the latest version/revision, then just return the version\n      # so the current and desired values match. Otherwise return version and revision\n      # to trigger an upgrade to the latest revision.\n      if newest[:version] == current[:ensure] and newest[:revision] == current[:revision]\n        return current[:ensure]\n      else\n        return \"#{newest[:version]}_#{newest[:revision]}\"\n      end\n    end\n    nil\n  end\n\n  def uninstall\n    port(\"-q\", :uninstall, @resource[:name])\n  end\n\n  def update\n    install\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/nim.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/util/package'\n\nPuppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do\n  desc \"Installation from an AIX NIM LPP source.  The `source` parameter is required\n      for this provider, and should specify the name of a NIM `lpp_source` resource\n      that is visible to the puppet agent machine.  This provider supports the\n      management of both BFF/installp and RPM packages.\n\n      Note that package downgrades are *not* supported; if your resource specifies\n      a specific version number and there is already a newer version of the package\n      installed on the machine, the resource will fail with an error message.\"\n\n  # The commands we are using on an AIX box are installed standard\n  # (except nimclient) nimclient needs the bos.sysmgt.nim.client fileset.\n  commands    :nimclient => \"/usr/sbin/nimclient\",\n              :lslpp => \"/usr/bin/lslpp\",\n              :rpm => \"rpm\"\n\n  # If NIM has not been configured, /etc/niminfo will not be present.\n  # However, we have no way of knowing if the NIM server is not configured\n  # properly.\n  confine :exists => \"/etc/niminfo\"\n\n  has_feature :versionable\n\n  attr_accessor :latest_info\n\n  def self.srclistcmd(source)\n    [command(:nimclient), \"-o\", \"showres\", \"-a\", \"installp_flags=L\", \"-a\", \"resource=#{source}\"]\n  end\n\n  def uninstall\n    output = lslpp(\"-qLc\", @resource[:name]).split(':')\n    # the 6th index in the colon-delimited output contains a \" \" for installp/BFF\n    # packages, and an \"R\" for RPMS.  (duh.)\n    pkg_type = output[6]\n\n    case pkg_type\n    when \" \"\n      installp \"-gu\", @resource[:name]\n    when \"R\"\n      rpm \"-e\", @resource[:name]\n    else\n      self.fail(_(\"Unrecognized AIX package type identifier: '%{pkg_type}'\") % { pkg_type: pkg_type })\n    end\n\n    # installp will return an exit code of zero even if it didn't uninstall\n    # anything... so let's make sure it worked.\n    unless query().nil?\n      self.fail _(\"Failed to uninstall package '%{name}'\") % { name: @resource[:name] }\n    end\n  end\n\n  def install(useversion = true)\n    source = @resource[:source]\n    unless source\n      self.fail _(\"An LPP source location is required in 'source'\")\n    end\n\n    pkg = @resource[:name]\n\n    version_specified = (useversion and (!@resource.should(:ensure).is_a? Symbol))\n\n    # This is unfortunate for a couple of reasons.  First, because of a subtle\n    # difference in the command-line syntax for installing an RPM vs an\n    # installp/BFF package, we need to know ahead of time which type of\n    # package we're trying to install.  This means we have to execute an\n    # extra command.\n    #\n    # Second, the command is easiest to deal with and runs fastest if we\n    # pipe it through grep on the shell.  Unfortunately, the way that\n    # the provider `make_command_methods` metaprogramming works, we can't\n    # use that code path to execute the command (because it treats the arguments\n    # as an array of args that all apply to `nimclient`, which fails when you\n    # hit the `|grep`.)  So here we just call straight through to P::U.execute\n    # with a single string argument for the full command, rather than going\n    # through the metaprogrammed layer.  We could get rid of the grep and\n    # switch back to the metaprogrammed stuff, and just parse all of the output\n    # in Ruby... but we'd be doing an awful lot of unnecessary work.\n    showres_command = \"/usr/sbin/nimclient -o showres -a resource=#{source} |/usr/bin/grep -p -E \"\n    if version_specified\n      version = @resource.should(:ensure)\n      showres_command << \"'#{Regexp.escape(pkg)}( |-)#{Regexp.escape(version)}'\"\n    else\n      version = nil\n      showres_command << \"'#{Regexp.escape(pkg)}'\"\n    end\n    output = Puppet::Util::Execution.execute(showres_command)\n\n    if version_specified\n      package_type = determine_package_type(output, pkg, version)\n    else\n      package_type, version = determine_latest_version(output, pkg)\n    end\n\n    if package_type.nil?\n\n      errmsg = if version_specified\n                 _(\"Unable to find package '%{package}' with version '%{version}' on lpp_source '%{source}'\") %\n                   { package: pkg, version: version, source: source }\n               else\n                 _(\"Unable to find package '%{package}' on lpp_source '%{source}'\") % { package: pkg, source: source }\n               end\n      self.fail errmsg\n    end\n\n    # This part is a bit tricky.  If there are multiple versions of the\n    # package available, then `version` will be set to a value, and we'll need\n    # to add that value to our installation command.  However, if there is only\n    # one version of the package available, `version` will be set to `nil`, and\n    # we don't need to add the version string to the command.\n    if version\n      # Now we know if the package type is RPM or not, and we can adjust our\n      # `pkg` string for passing to the install command accordingly.\n      if package_type == :rpm\n        # RPMs expect a hyphen between the package name and the version number\n        version_separator = \"-\"\n      else\n        # installp/BFF packages expect a space between the package name and the\n        # version number.\n        version_separator = \" \"\n      end\n\n      pkg += version_separator + version\n    end\n\n    # NOTE: the installp flags here are ignored (but harmless) for RPMs\n    output = nimclient \"-o\", \"cust\", \"-a\", \"installp_flags=acgwXY\", \"-a\", \"lpp_source=#{source}\", \"-a\", \"filesets=#{pkg}\"\n\n    # If the package is superseded, it means we're trying to downgrade and we\n    # can't do that.\n    case package_type\n    when :installp\n      if output =~ /^#{Regexp.escape(@resource[:name])}\\s+.*\\s+Already superseded by.*$/\n        self.fail _(\"NIM package provider is unable to downgrade packages\")\n      end\n    when :rpm\n      if output =~ /^#{Regexp.escape(@resource[:name])}.* is superseded by.*$/\n        self.fail _(\"NIM package provider is unable to downgrade packages\")\n      end\n    end\n  end\n\n  private\n\n  ## UTILITY METHODS FOR PARSING `nimclient -o showres` output\n\n  # This makes me very sad.  These regexes seem pretty fragile, but\n  # I spent a lot of time trying to figure out a solution that didn't\n  # require parsing the `nimclient -o showres` output and was unable to\n  # do so.\n  self::HEADER_LINE_REGEX      = /^([^\\s]+)\\s+[^@]+@@(I|R|S):(\\1)\\s+[^\\s]+$/\n  self::PACKAGE_LINE_REGEX     = /^.*@@(I|R|S):(.*)$/\n  self::RPM_PACKAGE_REGEX      = /^(.*)-(.*-\\d+\\w*) \\2$/\n  self::INSTALLP_PACKAGE_REGEX = /^(.*) (.*)$/\n\n  # Here is some sample output that shows what the above regexes will be up\n  # against:\n  # FOR AN INSTALLP(bff) PACKAGE:\n  #\n  #    mypackage.foo                                                           ALL  @@I:mypackage.foo _all_filesets\n  #    + 1.2.3.4  MyPackage Runtime Environment                       @@I:mypackage.foo 1.2.3.4\n  #    + 1.2.3.8  MyPackage Runtime Environment                       @@I:mypackage.foo 1.2.3.8\n  #\n  # FOR AN INSTALLP(bff) PACKAGE with security update:\n  #\n  #    bos.net                                                                 ALL  @@S:bos.net _all_filesets\n  #    + 7.2.0.1  TCP/IP ntp Applications                             @@S:bos.net.tcp.ntp 7.2.0.1\n  #    + 7.2.0.2  TCP/IP ntp Applications                             @@S:bos.net.tcp.ntp 7.2.0.2\n  #\n  # FOR AN RPM PACKAGE:\n  #\n  # mypackage.foo                                                                ALL  @@R:mypackage.foo _all_filesets\n  #   @@R:mypackage.foo-1.2.3-1 1.2.3-1\n  #   @@R:mypackage.foo-1.2.3-4 1.2.3-4\n  #   @@R:mypackage.foo-1.2.3-8 1.2.3-8\n\n  # Parse the output of a `nimclient -o showres` command.  Returns a two-dimensional\n  # hash, where the first-level keys are package names, the second-level keys are\n  # version number strings for all of the available version numbers for a package,\n  # and the values indicate the package type (:rpm / :installp)\n  def parse_showres_output(showres_output)\n    paragraphs = split_into_paragraphs(showres_output)\n    packages = {}\n    paragraphs.each do |para|\n      lines = para.split(/$/)\n      parse_showres_header_line(lines.shift)\n      lines.each do |l|\n        package, version, type = parse_showres_package_line(l)\n        packages[package] ||= {}\n        packages[package][version] = type\n      end\n    end\n    packages\n  end\n\n  # This method basically just splits the multi-line input string into chunks\n  # based on lines that contain nothing but whitespace.  It also strips any\n  # leading or trailing whitespace (including newlines) from the resulting\n  # strings and then returns them as an array.\n  def split_into_paragraphs(showres_output)\n    showres_output.split(/^\\s*$/).map(&:strip!)\n  end\n\n  def parse_showres_header_line(line)\n    # This method doesn't produce any meaningful output; it's basically just\n    # meant to validate that the header line for the package listing output\n    # looks sane, so we know we're dealing with the kind of output that we\n    # are capable of handling.\n    unless line.match(self.class::HEADER_LINE_REGEX)\n      self.fail _(\"Unable to parse output from nimclient showres: line does not match expected package header format:\\n'%{line}'\") % { line: line }\n    end\n  end\n\n  def parse_installp_package_string(package_string)\n    match = package_string.match(self.class::INSTALLP_PACKAGE_REGEX)\n    unless match\n      self.fail _(\"Unable to parse output from nimclient showres: package string does not match expected installp package string format:\\n'%{package_string}'\") % { package_string: package_string }\n    end\n    package_name = match.captures[0]\n    version = match.captures[1]\n    [package_name, version, :installp]\n  end\n\n  def parse_rpm_package_string(package_string)\n    match = package_string.match(self.class::RPM_PACKAGE_REGEX)\n    unless match\n      self.fail _(\"Unable to parse output from nimclient showres: package string does not match expected rpm package string format:\\n'%{package_string}'\") % { package_string: package_string }\n    end\n    package_name = match.captures[0]\n    version = match.captures[1]\n    [package_name, version, :rpm]\n  end\n\n  def parse_showres_package_line(line)\n    match = line.match(self.class::PACKAGE_LINE_REGEX)\n    unless match\n      self.fail _(\"Unable to parse output from nimclient showres: line does not match expected package line format:\\n'%{line}'\") % { line: line }\n    end\n\n    package_type_flag = match.captures[0]\n    package_string = match.captures[1]\n\n    case package_type_flag\n    when \"I\", \"S\"\n      parse_installp_package_string(package_string)\n    when \"R\"\n      parse_rpm_package_string(package_string)\n    else\n      self.fail _(\"Unrecognized package type specifier: '%{package_type_flag}' in package line:\\n'%{line}'\") % { package_type_flag: package_type_flag, line: line }\n    end\n  end\n\n  # Given a blob of output from `nimclient -o showres` and a package name,\n  # this method checks to see if there are multiple versions of the package\n  # available on the lpp_source.  If there are, the method returns\n  # [package_type, latest_version] (where package_type is one of :installp or :rpm).\n  # If there is only one version of the package available, it returns\n  # [package_type, nil], because the caller doesn't need to pass the version\n  # string to the command-line command if there is only one version available.\n  # If the package is not available at all, the method simply returns nil (instead\n  # of a tuple).\n  def determine_latest_version(showres_output, package_name)\n    packages = parse_showres_output(showres_output)\n    unless packages.has_key?(package_name)\n      return nil\n    end\n\n    if packages[package_name].count == 1\n      version = packages[package_name].keys[0]\n      [packages[package_name][version], nil]\n    else\n      versions = packages[package_name].keys\n      latest_version = (versions.sort { |a, b| Puppet::Util::Package.versioncmp(b, a) })[0]\n      [packages[package_name][latest_version], latest_version]\n    end\n  end\n\n  def determine_package_type(showres_output, package_name, version)\n    packages = parse_showres_output(showres_output)\n    unless packages.has_key?(package_name) and packages[package_name].has_key?(version)\n      return nil\n    end\n\n    packages[package_name][version]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/openbsd.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\n# Packaging on OpenBSD.  Doesn't work anywhere else that I know of.\nPuppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Package do\n  desc \"OpenBSD's form of `pkg_add` support.\n\n    This provider supports the `install_options` and `uninstall_options`\n    attributes, which allow command-line flags to be passed to pkg_add and pkg_delete.\n    These options should be specified as an array where each element is either a\n     string or a hash.\"\n\n  commands :pkginfo => \"pkg_info\",\n           :pkgadd => \"pkg_add\",\n           :pkgdelete => \"pkg_delete\"\n\n  defaultfor 'os.name' => :openbsd\n  confine 'os.name' => :openbsd\n\n  has_feature :versionable\n  has_feature :install_options\n  has_feature :uninstall_options\n  has_feature :upgradeable\n  has_feature :supports_flavors\n\n  def self.instances\n    packages = []\n\n    begin\n      execpipe(listcmd) do |process|\n        # our regex for matching pkg_info output\n        regex = /^(.*)-(\\d[^-]*)-?([\\w-]*)(.*)$/\n        fields = [:name, :ensure, :flavor]\n        hash = {}\n\n        # now turn each returned line into a package object\n        process.each_line { |line|\n          match = regex.match(line.split[0])\n          if match\n            fields.zip(match.captures) { |field, value|\n              hash[field] = value\n            }\n\n            hash[:provider] = name\n\n            packages << new(hash)\n            hash = {}\n          else\n            unless line =~ /Updating the pkgdb/\n              # Print a warning on lines we can't match, but move\n              # on, since it should be non-fatal\n              warning(_(\"Failed to match line %{line}\") % { line: line })\n            end\n          end\n        }\n      end\n\n      packages\n    rescue Puppet::ExecutionFailure\n      nil\n    end\n  end\n\n  def self.listcmd\n    [command(:pkginfo), \"-a\"]\n  end\n\n  def latest\n    parse_pkgconf\n\n    if @resource[:source][-1, 1] == ::File::SEPARATOR\n      e_vars = { 'PKG_PATH' => @resource[:source] }\n    else\n      e_vars = {}\n    end\n\n    if @resource[:flavor]\n      query = \"#{@resource[:name]}--#{@resource[:flavor]}\"\n    else\n      query = @resource[:name]\n    end\n\n    output = Puppet::Util.withenv(e_vars) { pkginfo \"-Q\", query }\n    version = properties[:ensure]\n\n    if output.nil? or output.size == 0 or output =~ /Error from /\n      debug \"Failed to query for #{resource[:name]}\"\n      return version\n    else\n      # Remove all fuzzy matches first.\n      output = output.split.select { |p| p =~ /^#{resource[:name]}-(\\d[^-]*)-?(\\w*)/ }.join\n      debug \"pkg_info -Q for #{resource[:name]}: #{output}\"\n    end\n\n    if output =~ /^#{resource[:name]}-(\\d[^-]*)-?(\\w*) \\(installed\\)$/\n      debug \"Package is already the latest available\"\n      version\n    else\n      match = /^(.*)-(\\d[^-]*)-?(\\w*)$/.match(output)\n      debug \"Latest available for #{resource[:name]}: #{match[2]}\"\n\n      if version.to_sym == :absent || version.to_sym == :purged\n        return match[2]\n      end\n\n      vcmp = version.split('.').map(&:to_i) <=> match[2].split('.').map(&:to_i)\n      if vcmp > 0\n        # The locally installed package may actually be newer than what a mirror\n        # has. Log it at debug, but ignore it otherwise.\n        debug \"Package #{resource[:name]} #{version} newer then available #{match[2]}\"\n        version\n      else\n        match[2]\n      end\n    end\n  end\n\n  def update\n    install(true)\n  end\n\n  def parse_pkgconf\n    unless @resource[:source]\n      if Puppet::FileSystem.exist?(\"/etc/pkg.conf\")\n        File.open(\"/etc/pkg.conf\", \"rb\").readlines.each do |line|\n          matchdata = line.match(/^installpath\\s*=\\s*(.+)\\s*$/i)\n          if matchdata\n            @resource[:source] = matchdata[1]\n          else\n            matchdata = line.match(/^installpath\\s*\\+=\\s*(.+)\\s*$/i)\n            if matchdata\n              if @resource[:source].nil?\n                @resource[:source] = matchdata[1]\n              else\n                @resource[:source] += \":\" + matchdata[1]\n              end\n            end\n          end\n        end\n\n        unless @resource[:source]\n          raise Puppet::Error,\n                _(\"No valid installpath found in /etc/pkg.conf and no source was set\")\n        end\n      else\n        raise Puppet::Error,\n              _(\"You must specify a package source or configure an installpath in /etc/pkg.conf\")\n      end\n    end\n  end\n\n  def install(latest = false)\n    cmd = []\n\n    parse_pkgconf\n\n    if @resource[:source][-1, 1] == ::File::SEPARATOR\n      e_vars = { 'PKG_PATH' => @resource[:source] }\n      full_name = get_full_name(latest)\n    else\n      e_vars = {}\n      full_name = @resource[:source]\n    end\n\n    cmd << install_options\n    cmd << full_name\n\n    if latest\n      cmd.unshift('-rz')\n    end\n\n    Puppet::Util.withenv(e_vars) { pkgadd cmd.flatten.compact }\n  end\n\n  def get_full_name(latest = false)\n    # In case of a real update (i.e., the package already exists) then\n    # pkg_add(8) can handle the flavors. However, if we're actually\n    # installing with 'latest', we do need to handle the flavors. This is\n    # done so we can feed pkg_add(8) the full package name to install to\n    # prevent ambiguity.\n    if latest && resource[:flavor]\n      \"#{resource[:name]}--#{resource[:flavor]}\"\n    elsif latest\n      # Don't depend on get_version for updates.\n      @resource[:name]\n    else\n      # If :ensure contains a version, use that instead of looking it up.\n      # This allows for installing packages with the same stem, but multiple\n      # version such as openldap-server.\n      if @resource[:ensure].to_s =~ /(\\d[^-]*)$/\n        use_version = @resource[:ensure]\n      else\n        use_version = get_version\n      end\n\n      [@resource[:name], use_version, @resource[:flavor]].join('-').gsub(/-+$/, '')\n    end\n  end\n\n  def get_version\n    execpipe([command(:pkginfo), \"-I\", @resource[:name]]) do |process|\n      # our regex for matching pkg_info output\n      regex = /^(.*)-(\\d[^-]*)-?(\\w*)(.*)$/\n      master_version = 0\n      version = -1\n\n      process.each_line do |line|\n        match = regex.match(line.split[0])\n        next unless match\n\n        # now we return the first version, unless ensure is latest\n        version = match.captures[1]\n        return version unless @resource[:ensure] == \"latest\"\n\n        master_version = version unless master_version > version\n      end\n\n      return master_version unless master_version == 0\n      return '' if version == -1\n\n      raise Puppet::Error, _(\"%{version} is not available for this package\") % { version: version }\n    end\n  rescue Puppet::ExecutionFailure\n    nil\n  end\n\n  def query\n    # Search for the version info\n    if pkginfo(@resource[:name]) =~ /Information for (inst:)?#{@resource[:name]}-(\\S+)/\n      { :ensure => Regexp.last_match(2) }\n    else\n      nil\n    end\n  end\n\n  def install_options\n    join_options(resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(resource[:uninstall_options])\n  end\n\n  def uninstall\n    pkgdelete uninstall_options.flatten.compact, @resource[:name]\n  end\n\n  def purge\n    pkgdelete \"-c\", \"-q\", @resource[:name]\n  end\n\n  def flavor\n    @property_hash[:flavor]\n  end\n\n  def flavor=(value)\n    if flavor != @resource.should(:flavor)\n      uninstall\n      install\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/opkg.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :opkg, :source => :opkg, :parent => Puppet::Provider::Package do\n  desc \"Opkg packaging support. Common on OpenWrt and OpenEmbedded platforms\"\n\n  commands :opkg => \"opkg\"\n\n  confine     'os.name' => :openwrt\n  defaultfor  'os.name' => :openwrt\n\n  def self.instances\n    packages = []\n    execpipe(\"#{command(:opkg)} list-installed\") do |process|\n      regex = /^(\\S+) - (\\S+)/\n      fields = [:name, :ensure]\n      hash = {}\n\n      process.each_line { |line|\n        match = regex.match(line)\n        if match\n          fields.zip(match.captures) { |field, value| hash[field] = value }\n          hash[:provider] = name\n          packages << new(hash)\n          hash = {}\n        else\n          warning(_(\"Failed to match line %{line}\") % { line: line })\n        end\n      }\n    end\n    packages\n  rescue Puppet::ExecutionFailure\n    nil\n  end\n\n  def latest\n    output = opkg(\"list\", @resource[:name])\n    matches = /^(\\S+) - (\\S+)/.match(output).captures\n    matches[1]\n  end\n\n  def install\n    # OpenWrt package lists are ephemeral, make sure we have at least\n    # some entries in the list directory for opkg to use\n    opkg('update') if package_lists.size <= 2\n\n    if @resource[:source]\n      opkg('--force-overwrite', 'install', @resource[:source])\n    else\n      opkg('--force-overwrite', 'install', @resource[:name])\n    end\n  end\n\n  def uninstall\n    opkg('remove', @resource[:name])\n  end\n\n  def update\n    install\n  end\n\n  def query\n    # list out our specific package\n    output = opkg('list-installed', @resource[:name])\n    if output =~ /^(\\S+) - (\\S+)/\n      return { :ensure => Regexp.last_match(2) }\n    end\n\n    nil\n  rescue Puppet::ExecutionFailure\n    {\n      :ensure => :purged,\n      :status => 'missing',\n      :name => @resource[:name],\n      :error => 'ok',\n    }\n  end\n\n  private\n\n  def package_lists\n    Dir.entries('/var/opkg-lists/')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pacman.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire 'set'\nrequire 'uri'\n\nPuppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Package do\n  desc \"Support for the Package Manager Utility (pacman) used in Archlinux.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pacman.\n  These options should be specified as an array where each element is either a string or a hash.\"\n\n  # If yaourt is installed, we can make use of it\n  def self.yaourt?\n    @yaourt ||= Puppet::FileSystem.exist?('/usr/bin/yaourt')\n  end\n\n  commands :pacman => \"/usr/bin/pacman\"\n  # Yaourt is a common AUR helper which, if installed, we can use to query the AUR\n  commands :yaourt => \"/usr/bin/yaourt\" if yaourt?\n\n  confine     'os.name' => [:archlinux, :manjarolinux, :artix]\n  defaultfor  'os.name' => [:archlinux, :manjarolinux, :artix]\n  has_feature :install_options\n  has_feature :uninstall_options\n  has_feature :upgradeable\n  has_feature :virtual_packages\n  has_feature :purgeable\n\n  # Checks if a given name is a group\n  def self.group?(name)\n    !pacman('--sync', '--groups', name).empty?\n  rescue Puppet::ExecutionFailure\n    # pacman returns an expected non-zero exit code when the name is not a group\n    false\n  end\n\n  # Install a package using 'pacman', or 'yaourt' if available.\n  # Installs quietly, without confirmation or progress bar, updates package\n  # list from servers defined in pacman.conf.\n  def install\n    if @resource[:source]\n      install_from_file\n    else\n      install_from_repo\n    end\n\n    unless query\n      fail(_(\"Could not find package '%{name}'\") % { name: @resource[:name] })\n    end\n  end\n\n  # Fetch the list of packages and package groups that are currently installed on the system.\n  # Only package groups that are fully installed are included. If a group adds packages over time, it will not\n  # be considered as fully installed any more, and we would install the new packages on the next run.\n  # If a group removes packages over time, nothing will happen. This is intended.\n  def self.instances\n    instances = []\n\n    # Get the installed packages\n    installed_packages = get_installed_packages\n    installed_packages.sort_by { |k, _| k }.each do |package, version|\n      instances << new(to_resource_hash(package, version))\n    end\n\n    # Get the installed groups\n    get_installed_groups(installed_packages).each do |group, version|\n      instances << new(to_resource_hash(group, version))\n    end\n\n    instances\n  end\n\n  # returns a hash package => version of installed packages\n  def self.get_installed_packages\n    packages = {}\n    execpipe([command(:pacman), \"--query\"]) do |pipe|\n      # pacman -Q output is 'packagename version-rel'\n      regex = /^(\\S+)\\s(\\S+)/\n      pipe.each_line do |line|\n        match = regex.match(line)\n        if match\n          packages[match.captures[0]] = match.captures[1]\n        else\n          warning(_(\"Failed to match line '%{line}'\") % { line: line })\n        end\n      end\n    end\n    packages\n  rescue Puppet::ExecutionFailure\n    fail(_(\"Error getting installed packages\"))\n  end\n\n  # returns a hash of group => version of installed groups\n  def self.get_installed_groups(installed_packages, filter = nil)\n    groups = {}\n    begin\n      # Build a hash of group name => list of packages\n      command = [command(:pacman), '--sync', '-gg']\n      command << filter if filter\n      execpipe(command) do |pipe|\n        pipe.each_line do |line|\n          name, package = line.split\n          packages = (groups[name] ||= [])\n          packages << package\n        end\n      end\n\n      # Remove any group that doesn't have all its packages installed\n      groups.delete_if do |_, packages|\n        !packages.all? { |package| installed_packages[package] }\n      end\n\n      # Replace the list of packages with a version string consisting of packages that make up the group\n      groups.each do |name, packages|\n        groups[name] = packages.sort.map { |package| \"#{package} #{installed_packages[package]}\" }.join ', '\n      end\n    rescue Puppet::ExecutionFailure\n      # pacman returns an expected non-zero exit code when the filter name is not a group\n      raise unless filter\n    end\n    groups\n  end\n\n  # Because Archlinux is a rolling release based distro, installing a package\n  # should always result in the newest release.\n  def update\n    # Install in pacman can be used for update, too\n    install\n  end\n\n  # We rescue the main check from Pacman with a check on the AUR using yaourt, if installed\n  def latest\n    resource_name = @resource[:name]\n\n    # If target is a group, construct the group version\n    return pacman(\"--sync\", \"--print\", \"--print-format\", \"%n %v\", resource_name).lines.map(&:chomp).sort.join(', ') if self.class.group?(resource_name)\n\n    # Start by querying with pacman first\n    # If that fails, retry using yaourt against the AUR\n    pacman_check = true\n    begin\n      if pacman_check\n        output = pacman \"--sync\", \"--print\", \"--print-format\", \"%v\", resource_name\n        output.chomp\n      else\n        output = yaourt \"-Qma\", resource_name\n        output.split(\"\\n\").each do |line|\n          return line.split[1].chomp if line =~ /^aur/\n        end\n      end\n    rescue Puppet::ExecutionFailure\n      if pacman_check and self.class.yaourt?\n        pacman_check = false # now try the AUR\n        retry\n      else\n        raise\n      end\n    end\n  end\n\n  # Queries information for a package or package group\n  def query\n    installed_packages = self.class.get_installed_packages\n    resource_name = @resource[:name]\n\n    # Check for the resource being a group\n    version = self.class.get_installed_groups(installed_packages, resource_name)[resource_name]\n\n    if version\n      unless @resource.allow_virtual?\n        warning(_(\"%{resource_name} is a group, but allow_virtual is false.\") % { resource_name: resource_name })\n        return nil\n      end\n    else\n      version = installed_packages[resource_name]\n    end\n\n    # Return nil if no package or group found\n    return nil unless version\n\n    self.class.to_resource_hash(resource_name, version)\n  end\n\n  def self.to_resource_hash(name, version)\n    {\n      :name => name,\n      :ensure => version,\n      :provider => self.name\n    }\n  end\n\n  # Removes a package from the system.\n  def uninstall\n    remove_package(false)\n  end\n\n  def purge\n    remove_package(true)\n  end\n\n  private\n\n  def remove_package(purge_configs = false)\n    resource_name = @resource[:name]\n\n    is_group = self.class.group?(resource_name)\n\n    fail(_(\"Refusing to uninstall package group %{resource_name}, because allow_virtual is false.\") % { resource_name: resource_name }) if is_group && !@resource.allow_virtual?\n\n    cmd = %w[--noconfirm --noprogressbar]\n    cmd += uninstall_options if @resource[:uninstall_options]\n    cmd << \"--remove\"\n    cmd << '--recursive' if is_group\n    cmd << '--nosave' if purge_configs\n    cmd << resource_name\n\n    if self.class.yaourt?\n      yaourt(*cmd)\n    else\n      pacman(*cmd)\n    end\n  end\n\n  def install_options\n    join_options(@resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(@resource[:uninstall_options])\n  end\n\n  def install_from_file\n    source = @resource[:source]\n    begin\n      source_uri = URI.parse source\n    rescue => detail\n      self.fail Puppet::Error, _(\"Invalid source '%{source}': %{detail}\") % { source: source, detail: detail }, detail\n    end\n\n    source = case source_uri.scheme\n             when nil       then source\n             when /https?/i then source\n             when /ftp/i    then source\n             when /file/i   then source_uri.path\n             when /puppet/i\n               fail _(\"puppet:// URL is not supported by pacman\")\n             else\n               fail _(\"Source %{source} is not supported by pacman\") % { source: source }\n             end\n    pacman \"--noconfirm\", \"--noprogressbar\", \"--update\", source\n  end\n\n  def install_from_repo\n    resource_name = @resource[:name]\n\n    # Refuse to install if not allowing virtual packages and the resource is a group\n    fail(_(\"Refusing to install package group %{resource_name}, because allow_virtual is false.\") % { resource_name: resource_name }) if self.class.group?(resource_name) && !@resource.allow_virtual?\n\n    cmd = %w[--noconfirm --needed --noprogressbar]\n    cmd += install_options if @resource[:install_options]\n    cmd << \"--sync\" << resource_name\n\n    if self.class.yaourt?\n      yaourt(*cmd)\n    else\n      pacman(*cmd)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pip.rb",
    "content": "# frozen_string_literal: true\n\n# Puppet package provider for Python's `pip` package management frontend.\n# <http://pip.pypa.io/>\n\nrequire_relative '../../../puppet/util/package/version/pip'\nrequire_relative '../../../puppet/util/package/version/range'\nrequire_relative '../../../puppet/provider/package_targetable'\n\nPuppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package::Targetable do\n  desc \"Python packages via `pip`.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pip.\n  These options should be specified as an array where each element is either a string or a hash.\"\n\n  has_feature :installable, :uninstallable, :upgradeable, :versionable, :version_ranges, :install_options, :targetable\n\n  PIP_VERSION       = Puppet::Util::Package::Version::Pip\n  PIP_VERSION_RANGE = Puppet::Util::Package::Version::Range\n\n  # Override the specificity method to return 1 if pip is not set as default provider\n  def self.specificity\n    match = default_match\n    length = match ? match.length : 0\n\n    return 1 if length == 0\n\n    super\n  end\n\n  # Define the default provider package command name when the provider is targetable.\n  # Required by Puppet::Provider::Package::Targetable::resource_or_provider_command\n  def self.provider_command\n    # Ensure pip can upgrade pip, which usually puts pip into a new path /usr/local/bin/pip (compared to /usr/bin/pip)\n    cmd.map { |c| which(c) }.find { |c| !c.nil? }\n  end\n\n  def self.cmd\n    if Puppet::Util::Platform.windows?\n      [\"pip.exe\"]\n    else\n      %w[pip pip-python pip2 pip-2]\n    end\n  end\n\n  def self.pip_version(command)\n    version = nil\n    execpipe [quote(command), '--version'] do |process|\n      process.collect do |line|\n        md = line.strip.match(/^pip (\\d+\\.\\d+\\.?\\d*).*$/)\n        if md\n          version = md[1]\n          break\n        end\n      end\n    end\n\n    raise Puppet::Error, _(\"Cannot resolve pip version\") unless version\n\n    version\n  end\n\n  # Return an array of structured information about every installed package\n  # that's managed by `pip` or an empty array if `pip` is not available.\n  def self.instances(target_command = nil)\n    if target_command\n      command = target_command\n      validate_command(command)\n    else\n      command = provider_command\n    end\n\n    packages = []\n    return packages unless command\n\n    command_options = ['freeze']\n    command_version = pip_version(command)\n    if compare_pip_versions(command_version, '8.1.0') >= 0\n      command_options << '--all'\n    end\n\n    execpipe [quote(command), command_options] do |process|\n      process.collect do |line|\n        pkg = parse(line)\n        next unless pkg\n\n        pkg[:command] = command\n        packages << new(pkg)\n      end\n    end\n\n    # Pip can also upgrade pip, but it's not listed in freeze so need to special case it\n    # Pip list would also show pip installed version, but \"pip list\" doesn't exist for older versions of pip (E.G v1.0)\n    # Not needed when \"pip freeze --all\" is available.\n    if compare_pip_versions(command_version, '8.1.0') == -1\n      packages << new({ :ensure => command_version, :name => File.basename(command), :provider => name, :command => command })\n    end\n\n    packages\n  end\n\n  # Parse lines of output from `pip freeze`, which are structured as:\n  # _package_==_version_ or _package_===_version_\n  # or _package_ @ someURL@_version_\n  def self.parse(line)\n    if line.chomp =~ /^([^=]+)===?([^=]+)$/\n      { :ensure => Regexp.last_match(2), :name => Regexp.last_match(1), :provider => name }\n    elsif line.chomp =~ /^([^@]+) @ [^@]+@(.+)$/\n      { :ensure => Regexp.last_match(2), :name => Regexp.last_match(1), :provider => name }\n    end\n  end\n\n  # Return structured information about a particular package or `nil`\n  # if the package is not installed or `pip` itself is not available.\n  def query\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    self.class.instances(command).each do |pkg|\n      return pkg.properties if @resource[:name].casecmp(pkg.name).zero?\n    end\n    nil\n  end\n\n  # Return latest version available for current package\n  def latest\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    command_version = self.class.pip_version(command)\n    if self.class.compare_pip_versions(command_version, '1.5.4') == -1\n      available_versions_with_old_pip.last\n    else\n      available_versions_with_new_pip(command_version).last\n    end\n  end\n\n  def self.compare_pip_versions(x, y)\n    Puppet::Util::Package::Version::Pip.compare(x, y)\n  rescue PIP_VERSION::ValidationFailure => ex\n    Puppet.debug(\"Cannot compare #{x} and #{y}. #{ex.message} Falling through default comparison mechanism.\")\n    Puppet::Util::Package.versioncmp(x, y)\n  end\n\n  # Use pip CLI to look up versions from PyPI repositories,\n  # honoring local pip config such as custom repositories.\n  def available_versions\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    command_version = self.class.pip_version(command)\n    if self.class.compare_pip_versions(command_version, '1.5.4') == -1\n      available_versions_with_old_pip\n    else\n      available_versions_with_new_pip(command_version)\n    end\n  end\n\n  def available_versions_with_new_pip(command_version)\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    command_and_options = [self.class.quote(command), 'install', \"#{@resource[:name]}==9!0dev0+x\"]\n    extra_arg = list_extra_flags(command_version)\n    command_and_options << extra_arg if extra_arg\n    command_and_options << install_options if @resource[:install_options]\n    execpipe command_and_options do |process|\n      process.collect do |line|\n        # PIP OUTPUT: Could not find a version that satisfies the requirement example==versionplease (from versions: 1.2.3, 4.5.6)\n        next unless line =~ /from versions: (.+)\\)/\n\n        versionList = Regexp.last_match(1).split(', ').sort do |x, y|\n          self.class.compare_pip_versions(x, y)\n        end\n        return versionList\n      end\n    end\n    []\n  end\n\n  def available_versions_with_old_pip\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    Dir.mktmpdir(\"puppet_pip\") do |dir|\n      command_and_options = [self.class.quote(command), 'install', (@resource[:name]).to_s, '-d', dir.to_s, '-v']\n      command_and_options << install_options if @resource[:install_options]\n      execpipe command_and_options do |process|\n        process.collect do |line|\n          # PIP OUTPUT: Using version 0.10.1 (newest of versions: 1.2.3, 4.5.6)\n          next unless line =~ /Using version .+? \\(newest of versions: (.+?)\\)/\n\n          versionList = Regexp.last_match(1).split(', ').sort do |x, y|\n            self.class.compare_pip_versions(x, y)\n          end\n          return versionList\n        end\n      end\n      return []\n    end\n  end\n\n  # Finds the most suitable version available in a given range\n  def best_version(should_range)\n    included_available_versions = []\n    available_versions.each do |version|\n      version = PIP_VERSION.parse(version)\n      included_available_versions.push(version) if should_range.include?(version)\n    end\n\n    included_available_versions.sort!\n    return included_available_versions.last unless included_available_versions.empty?\n\n    Puppet.debug(\"No available version for package #{@resource[:name]} is included in range #{should_range}\")\n    should_range\n  end\n\n  def get_install_command_options\n    should = @resource[:ensure]\n    command_options = %w[install -q]\n    command_options += install_options if @resource[:install_options]\n\n    if @resource[:source]\n      if should.is_a?(String)\n        command_options << \"#{@resource[:source]}@#{should}#egg=#{@resource[:name]}\"\n      else\n        command_options << \"#{@resource[:source]}#egg=#{@resource[:name]}\"\n      end\n\n      return command_options\n    end\n\n    if should == :latest\n      command_options << \"--upgrade\" << @resource[:name]\n\n      return command_options\n    end\n\n    unless should.is_a?(String)\n      command_options << @resource[:name]\n\n      return command_options\n    end\n\n    begin\n      should_range = PIP_VERSION_RANGE.parse(should, PIP_VERSION)\n    rescue PIP_VERSION_RANGE::ValidationFailure, PIP_VERSION::ValidationFailure\n      Puppet.debug(\"Cannot parse #{should} as a pip version range, falling through.\")\n      command_options << \"#{@resource[:name]}==#{should}\"\n\n      return command_options\n    end\n\n    if should_range.is_a?(PIP_VERSION_RANGE::Eq)\n      command_options << \"#{@resource[:name]}==#{should}\"\n\n      return command_options\n    end\n\n    should = best_version(should_range)\n\n    if should == should_range\n      # when no suitable version for the given range was found, let pip handle\n      if should.is_a?(PIP_VERSION_RANGE::MinMax)\n        command_options << \"#{@resource[:name]} #{should.split.join(',')}\"\n      else\n        command_options << \"#{@resource[:name]} #{should}\"\n      end\n    else\n      command_options << \"#{@resource[:name]}==#{should}\"\n    end\n\n    command_options\n  end\n\n  # Install a package.  The ensure parameter may specify installed,\n  # latest, a version number, or, in conjunction with the source\n  # parameter, an SCM revision.  In that case, the source parameter\n  # gives the fully-qualified URL to the repository.\n  def install\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    command_options = get_install_command_options\n    execute([command, command_options])\n  end\n\n  # Uninstall a package. Uninstall won't work reliably on Debian/Ubuntu unless this issue gets fixed.\n  # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562544\n  def uninstall\n    command = resource_or_provider_command\n    self.class.validate_command(command)\n\n    command_options = [\"uninstall\", \"-y\", \"-q\", @resource[:name]]\n\n    execute([command, command_options])\n  end\n\n  def update\n    install\n  end\n\n  def install_options\n    join_options(@resource[:install_options])\n  end\n\n  def insync?(is)\n    return false unless is && is != :absent\n\n    begin\n      should = @resource[:ensure]\n      should_range = PIP_VERSION_RANGE.parse(should, PIP_VERSION)\n    rescue PIP_VERSION_RANGE::ValidationFailure, PIP_VERSION::ValidationFailure\n      Puppet.debug(\"Cannot parse #{should} as a pip version range\")\n      return false\n    end\n\n    begin\n      is_version = PIP_VERSION.parse(is)\n    rescue PIP_VERSION::ValidationFailure\n      Puppet.debug(\"Cannot parse #{is} as a pip version\")\n      return false\n    end\n\n    should_range.include?(is_version)\n  end\n\n  # Quoting is required if the path to the pip command contains spaces.\n  # Required for execpipe() but not execute(), as execute() already does this.\n  def self.quote(path)\n    if path.include?(\" \")\n      \"\\\"#{path}\\\"\"\n    else\n      path\n    end\n  end\n\n  private\n\n  def list_extra_flags(command_version)\n    klass = self.class\n    if klass.compare_pip_versions(command_version, '20.2.4') == 1 &&\n       klass.compare_pip_versions(command_version, '21.1') == -1\n      '--use-deprecated=legacy-resolver'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pip2.rb",
    "content": "# frozen_string_literal: true\n\n# Puppet package provider for Python's `pip2` package management frontend.\n# <http://pip.pypa.io/>\n\nPuppet::Type.type(:package).provide :pip2,\n                                    :parent => :pip do\n  desc \"Python packages via `pip2`.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pip2.\n  These options should be specified as an array where each element is either a string or a hash.\"\n\n  has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options, :targetable\n\n  def self.cmd\n    [\"pip2\"]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pip3.rb",
    "content": "# frozen_string_literal: true\n\n# Puppet package provider for Python's `pip3` package management frontend.\n# <http://pip.pypa.io/>\n\nPuppet::Type.type(:package).provide :pip3,\n                                    :parent => :pip do\n  desc \"Python packages via `pip3`.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pip3.\n  These options should be specified as an array where each element is either a string or a hash.\"\n\n  has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options, :targetable\n\n  def self.cmd\n    [\"pip3\"]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pkg.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package do\n  desc \"OpenSolaris image packaging system. See pkg(5) for more information.\n\n    This provider supports the `install_options` attribute, which allows\n    command-line flags to be passed to pkg. These options should be specified as an\n    array where each element is either a string or a hash.\"\n\n  # https://docs.oracle.com/cd/E19963-01/html/820-6572/managepkgs.html\n  # A few notes before we start:\n  # Opensolaris pkg has two slightly different formats (as of now.)\n  # The first one is what is distributed with the Solaris 11 Express 11/10 dvd\n  # The latest one is what you get when you update package.\n  # To make things more interesting, pkg version just returns a sha sum.\n  # dvd:     pkg version => 052adf36c3f4\n  # updated: pkg version => 630e1ffc7a19\n  # Thankfully, Solaris has not changed the commands to be used.\n  # TODO: We still have to allow packages to specify a preferred publisher.\n\n  has_feature :versionable\n\n  has_feature :upgradable\n\n  has_feature :holdable\n\n  has_feature :install_options\n\n  commands :pkg => \"/usr/bin/pkg\"\n\n  confine 'os.family' => :solaris\n\n  defaultfor 'os.family' => :solaris, :kernelrelease => ['5.11', '5.12']\n\n  def self.instances\n    pkg(:list, '-Hv').split(\"\\n\").map { |l| new(parse_line(l)) }\n  end\n\n  # The IFO flag field is just what it names, the first field can have either\n  # i_nstalled or -, and second field f_rozen or -, and last\n  # o_bsolate or r_rename or -\n  # so this checks if the installed field is present, and also verifies that\n  # if not the field is -, else we don't know what we are doing and exit with\n  # out doing more damage.\n  def self.ifo_flag(flags)\n    (\n      case flags[0..0]\n      when 'i'\n        { :status => 'installed' }\n      when '-'\n        { :status => 'known' }\n      else\n        raise ArgumentError, _('Unknown format %{resource_name}: %{full_flags}[%{bad_flag}]') %\n            { resource_name: name, full_flags: flags, bad_flag: flags[0..0] }\n      end\n    ).merge(\n      case flags[1..1]\n      when 'f'\n        { :mark => :hold }\n      when '-'\n        {}\n      else\n        raise ArgumentError, _('Unknown format %{resource_name}: %{full_flags}[%{bad_flag}]') %\n            { resource_name: name, full_flags: flags, bad_flag: flags[1..1] }\n      end\n    )\n  end\n\n  # The UFOXI field is the field present in the older pkg\n  # (solaris 2009.06 - snv151a)\n  # similar to IFO, UFOXI is also an either letter or -\n  # u_pdate indicates that an update for the package is available.\n  # f_rozen(n/i) o_bsolete x_cluded(n/i) i_constrained(n/i)\n  # note that u_pdate flag may not be trustable due to constraints.\n  # so we dont rely on it\n  # Frozen was never implemented in UFOXI so skipping frozen here.\n  def self.ufoxi_flag(flags)\n    {}\n  end\n\n  # pkg state was present in the older version of pkg (with UFOXI) but is\n  # no longer available with the IFO field version. When it was present,\n  # it was used to indicate that a particular version was present (installed)\n  # and later versions were known. Note that according to the pkg man page,\n  # known never lists older versions of the package. So we can rely on this\n  # field to make sure that if a known is present, then the pkg is upgradable.\n  def self.pkg_state(state)\n    case state\n    when /installed/\n      { :status => 'installed' }\n    when /known/\n      { :status => 'known' }\n    else\n      raise ArgumentError, _('Unknown format %{resource_name}: %{state}') % { resource_name: name, state: state }\n    end\n  end\n\n  # Here is (hopefully) the only place we will have to deal with multiple\n  # formats of output for different pkg versions.\n  def self.parse_line(line)\n    (case line.chomp\n     # FMRI                                                                         IFO\n     # pkg://omnios/SUNWcs@0.5.11,5.11-0.151008:20131204T022241Z                    ---\n     when %r{^pkg://([^/]+)/([^@]+)@(\\S+) +(...)$}\n       { :publisher => Regexp.last_match(1), :name => Regexp.last_match(2), :ensure => Regexp.last_match(3) }.merge ifo_flag(Regexp.last_match(4))\n\n     # FMRI                                                             STATE      UFOXI\n     # pkg://solaris/SUNWcs@0.5.11,5.11-0.151.0.1:20101105T001108Z      installed  u----\n     when %r{^pkg://([^/]+)/([^@]+)@(\\S+) +(\\S+) +(.....)$}\n       { :publisher => Regexp.last_match(1), :name => Regexp.last_match(2), :ensure => Regexp.last_match(3) }.merge pkg_state(Regexp.last_match(4)).merge(ufoxi_flag(Regexp.last_match(5)))\n\n     else\n       raise ArgumentError, _('Unknown line format %{resource_name}: %{parse_line}') % { resource_name: name, parse_line: line }\n     end).merge({ :provider => name })\n  end\n\n  def hold\n    pkg(:freeze, @resource[:name])\n  end\n\n  def unhold\n    r = exec_cmd(command(:pkg), 'unfreeze', @resource[:name])\n    raise Puppet::Error, _(\"Unable to unfreeze %{package}\") % { package: r[:out] } unless [0, 4].include? r[:exit]\n  end\n\n  def insync?(is)\n    # this is called after the generic version matching logic (insync? for the\n    # type), so we only get here if should != is, and 'should' is a version\n    # number. 'is' might not be, though.\n    should = @resource[:ensure]\n    # NB: it is apparently possible for repository administrators to publish\n    # packages which do not include build or branch versions, but component\n    # version must always be present, and the timestamp is added by pkgsend\n    # publish.\n    if /^[0-9.]+(,[0-9.]+)?(-[0-9.]+)?:[0-9]+T[0-9]+Z$/ !~ should\n      # We have a less-than-explicit version string, which we must accept for\n      # backward compatibility. We can find the real version this would match\n      # by asking pkg for the all matching versions, and selecting the first\n      # installable one [0]; this can change over time when remote repositories\n      # are updated, but the principle of least astonishment should still hold:\n      # if we allow users to specify less-than-explicit versions, the\n      # functionality should match that of the package manager.\n      #\n      # [0]: we could simply get the newest matching version with 'pkg list\n      # -n', but that isn't always correct, since it might not be installable.\n      # If that were the case we could potentially end up returning false for\n      # insync? here but not actually changing the package version in install\n      # (ie. if the currently installed version is the latest matching version\n      # that is installable, we would falsely conclude here that since the\n      # installed version is not the latest matching version, we're not in\n      # sync).  'pkg list -a' instead of '-n' would solve this, but\n      # unfortunately it doesn't consider downgrades 'available' (eg. with\n      # installed foo@1.0, list -a foo@0.9 would fail).\n      name = @resource[:name]\n      potential_matches = pkg(:list, '-Hvfa', \"#{name}@#{should}\").split(\"\\n\").map { |l| self.class.parse_line(l) }\n      n = potential_matches.length\n      if n > 1\n        warning(_(\"Implicit version %{should} has %{n} possible matches\") % { should: should, n: n })\n      end\n      potential_matches.each { |p|\n        command = is == :absent ? 'install' : 'update'\n        options = ['-n']\n        options.concat(join_options(@resource[:install_options])) if @resource[:install_options]\n\n        begin\n          unhold if properties[:mark] == :hold\n          status = exec_cmd(command(:pkg), command, *options, \"#{name}@#{p[:ensure]}\")[:exit]\n        ensure\n          hold if properties[:mark] == :hold\n        end\n\n        case status\n        when 4\n          # if the first installable match would cause no changes, we're in sync\n          return true\n        when 0\n          warning(_(\"Selecting version '%{version}' for implicit '%{should}'\") % { version: p[:ensure], should: should })\n          @resource[:ensure] = p[:ensure]\n          return false\n        end\n      }\n      raise Puppet::DevError, _(\"No version of %{name} matching %{should} is installable, even though the package is currently installed\") %\n                              { name: name, should: should }\n    end\n\n    false\n  end\n\n  # Return the version of the package. Note that the bug\n  # http://defect.opensolaris.org/bz/show_bug.cgi?id=19159%\n  # notes that we can't use -Ha for the same even though the manual page reads that way.\n  def latest\n    # Refresh package metadata before looking for latest versions\n    pkg(:refresh)\n\n    lines = pkg(:list, \"-Hvn\", @resource[:name]).split(\"\\n\")\n\n    # remove certificate expiration warnings from the output, but report them\n    cert_warnings = lines.select { |line| line =~ /^Certificate/ }\n    unless cert_warnings.empty?\n      Puppet.warning(_(\"pkg warning: %{warnings}\") % { warnings: cert_warnings.join(', ') })\n    end\n\n    lst = lines.select { |line| line !~ /^Certificate/ }.map { |line| self.class.parse_line(line) }\n\n    # Now we know there is a newer version. But is that installable? (i.e are there any constraints?)\n    # return the first known we find. The only way that is currently available is to do a dry run of\n    # pkg update and see if could get installed (`pkg update -n res`).\n    known = lst.find { |p| p[:status] == 'known' }\n    if known\n      options = ['-n']\n      options.concat(join_options(@resource[:install_options])) if @resource[:install_options]\n      return known[:ensure] if exec_cmd(command(:pkg), 'update', *options, @resource[:name])[:exit].zero?\n    end\n\n    # If not, then return the installed, else nil\n    (lst.find { |p| p[:status] == 'installed' } || {})[:ensure]\n  end\n\n  # install the package and accept all licenses.\n  def install(nofail = false)\n    name = @resource[:name]\n    should = @resource[:ensure]\n    is = query\n    if is[:ensure].to_sym == :absent\n      command = 'install'\n    else\n      command = 'update'\n    end\n    args = ['--accept']\n    if Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value('os.release.full'), '11.2') >= 0\n      args.push('--sync-actuators-timeout', '900')\n    end\n    args.concat(join_options(@resource[:install_options])) if @resource[:install_options]\n    unless should.is_a? Symbol\n      name += \"@#{should}\"\n    end\n    unhold if properties[:mark] == :hold\n    begin\n      tries = 1\n      # pkg install exits with code 7 when the image is currently in use by another process and cannot be modified\n      r = exec_cmd(command(:pkg), command, *args, name)\n      while r[:exit] == 7\n        if tries > 4\n          raise Puppet::Error, _(\"Pkg could not install %{name} after %{tries} tries. Aborting run\") % { name: name, tries: tries }\n        end\n\n        sleep 2**tries\n        tries += 1\n        r = exec_cmd(command(:pkg), command, *args, name)\n      end\n    ensure\n      hold if @resource[:mark] == :hold\n    end\n    return r if nofail\n    raise Puppet::Error, _(\"Unable to update %{package}\") % { package: r[:out] } if r[:exit] != 0\n  end\n\n  # uninstall the package. The complication comes from the -r_ecursive flag which is no longer\n  # present in newer package version.\n  def uninstall\n    cmd = [:uninstall]\n    case (pkg :version).chomp\n    when /052adf36c3f4/\n      cmd << '-r'\n    end\n    cmd << @resource[:name]\n    unhold if properties[:mark] == :hold\n    begin\n      pkg cmd\n    rescue StandardError, LoadError => e\n      hold if properties[:mark] == :hold\n      raise e\n    end\n  end\n\n  # update the package to the latest version available\n  def update\n    r = install(true)\n    # 4 == /No updates available for this image./\n    return if [0, 4].include? r[:exit]\n\n    raise Puppet::Error, _(\"Unable to update %{package}\") % { package: r[:out] }\n  end\n\n  # list a specific package\n  def query\n    r = exec_cmd(command(:pkg), 'list', '-Hv', @resource[:name])\n    return { :ensure => :absent, :name => @resource[:name] } if r[:exit] != 0\n\n    self.class.parse_line(r[:out])\n  end\n\n  def exec_cmd(*cmd)\n    output = Puppet::Util::Execution.execute(cmd, :failonfail => false, :combine => true)\n    { :out => output, :exit => output.exitstatus }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pkgdmg.rb",
    "content": "# frozen_string_literal: true\n\n#\n# Motivation: DMG files provide a true HFS file system\n# and are easier to manage and .pkg bundles.\n#\n# Note: the 'apple' Provider checks for the package name\n# in /L/Receipts.  Since we install multiple pkg's from a single\n# source, we treat the source .pkg.dmg file as the package name.\n# As a result, we store installed .pkg.dmg file names\n# in /var/db/.puppet_pkgdmg_installed_<name>\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/util/plist'\nrequire_relative '../../../puppet/util/http_proxy'\n\nPuppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Package do\n  desc \"Package management based on Apple's Installer.app and DiskUtility.app.\n\n    This provider works by checking the contents of a DMG image for Apple pkg or\n    mpkg files. Any number of pkg or mpkg files may exist in the root directory\n    of the DMG file system, and Puppet will install all of them. Subdirectories\n    are not checked for packages.\n\n    This provider can also accept plain .pkg (but not .mpkg) files in addition\n    to .dmg files.\n\n    Notes:\n\n    * The `source` attribute is mandatory. It must be either a local disk path\n      or an HTTP, HTTPS, or FTP URL to the package.\n    * The `name` of the resource must be the filename (without path) of the DMG file.\n    * When installing the packages from a DMG, this provider writes a file to\n      disk at `/var/db/.puppet_pkgdmg_installed_NAME`. If that file is present,\n      Puppet assumes all packages from that DMG are already installed.\n    * This provider is not versionable and uses DMG filenames to determine\n      whether a package has been installed. Thus, to install new a version of a\n      package, you must create a new DMG with a different filename.\"\n\n  confine 'os.name' => :darwin\n  confine :feature => :cfpropertylist\n  defaultfor 'os.name' => :darwin\n  commands :installer => \"/usr/sbin/installer\"\n  commands :hdiutil => \"/usr/bin/hdiutil\"\n  commands :curl => \"/usr/bin/curl\"\n\n  # JJM We store a cookie for each installed .pkg.dmg in /var/db\n  def self.instance_by_name\n    Dir.entries(\"/var/db\").find_all { |f|\n      f =~ /^\\.puppet_pkgdmg_installed_/\n    }.collect do |f|\n      name = f.sub(/^\\.puppet_pkgdmg_installed_/, '')\n      yield name if block_given?\n      name\n    end\n  end\n\n  def self.instances\n    instance_by_name.collect do |name|\n      new(:name => name, :provider => :pkgdmg, :ensure => :installed)\n    end\n  end\n\n  def self.installpkg(source, name, orig_source)\n    installer \"-pkg\", source, \"-target\", \"/\"\n    # Non-zero exit status will throw an exception.\n    Puppet::FileSystem.open(\"/var/db/.puppet_pkgdmg_installed_#{name}\", nil, \"w:UTF-8\") do |t|\n      t.print \"name: '#{name}'\\n\"\n      t.print \"source: '#{orig_source}'\\n\"\n    end\n  end\n\n  def self.installpkgdmg(source, name)\n    unless Puppet::Util::HttpProxy.no_proxy?(source)\n      http_proxy_host = Puppet::Util::HttpProxy.http_proxy_host\n      http_proxy_port = Puppet::Util::HttpProxy.http_proxy_port\n    end\n\n    unless source =~ /\\.dmg$/i || source =~ /\\.pkg$/i\n      raise Puppet::Error, _(\"Mac OS X PKG DMGs must specify a source string ending in .dmg or flat .pkg file\")\n    end\n\n    require 'open-uri' # Dead code; this is never used. The File.open call 20-ish lines south of here used to be Kernel.open but changed in '09. -NF\n    cached_source = source\n    tmpdir = Dir.mktmpdir\n    ext = /(\\.dmg|\\.pkg)$/i.match(source)[0]\n    begin\n      if %r{\\A[A-Za-z][A-Za-z0-9+\\-.]*://} =~ cached_source\n        cached_source = File.join(tmpdir, \"#{name}#{ext}\")\n        args = [\"-o\", cached_source, \"-C\", \"-\", \"-L\", \"-s\", \"--fail\", \"--url\", source]\n        if http_proxy_host and http_proxy_port\n          args << \"--proxy\" << \"#{http_proxy_host}:#{http_proxy_port}\"\n        elsif http_proxy_host and !http_proxy_port\n          args << \"--proxy\" << http_proxy_host\n        end\n        begin\n          curl(*args)\n          Puppet.debug \"Success: curl transferred [#{name}] (via: curl #{args.join(' ')})\"\n        rescue Puppet::ExecutionFailure\n          Puppet.debug \"curl #{args.join(' ')} did not transfer [#{name}].  Falling back to local file.\" # This used to fall back to open-uri. -NF\n          cached_source = source\n        end\n      end\n\n      if source =~ /\\.dmg$/i\n        # If you fix this to use open-uri again, you must update the docs above. -NF\n        File.open(cached_source) do |dmg|\n          xml_str = hdiutil \"mount\", \"-plist\", \"-nobrowse\", \"-readonly\", \"-mountrandom\", \"/tmp\", dmg.path\n          hdiutil_info = Puppet::Util::Plist.parse_plist(xml_str)\n          raise Puppet::Error, _(\"No disk entities returned by mount at %{path}\") % { path: dmg.path } unless hdiutil_info.has_key?(\"system-entities\")\n\n          mounts = hdiutil_info[\"system-entities\"].filter_map { |entity|\n            entity[\"mount-point\"]\n          }\n          begin\n            mounts.each do |mountpoint|\n              Dir.entries(mountpoint).select { |f|\n                f =~ /\\.m{0,1}pkg$/i\n              }.each do |pkg|\n                installpkg(\"#{mountpoint}/#{pkg}\", name, source)\n              end\n            end\n          ensure\n            mounts.each do |mountpoint|\n              hdiutil \"eject\", mountpoint\n            end\n          end\n        end\n      else\n        installpkg(cached_source, name, source)\n      end\n    ensure\n      FileUtils.remove_entry_secure(tmpdir, true)\n    end\n  end\n\n  def query\n    if Puppet::FileSystem.exist?(\"/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}\")\n      Puppet.debug \"/var/db/.puppet_pkgdmg_installed_#{@resource[:name]} found\"\n      { :name => @resource[:name], :ensure => :present }\n    else\n      nil\n    end\n  end\n\n  def install\n    source = @resource[:source]\n    unless source\n      raise Puppet::Error, _(\"Mac OS X PKG DMGs must specify a package source.\")\n    end\n\n    name = @resource[:name]\n    unless name\n      raise Puppet::Error, _(\"Mac OS X PKG DMGs must specify a package name.\")\n    end\n\n    self.class.installpkgdmg(source, name)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pkgin.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :pkgin, :parent => Puppet::Provider::Package do\n  desc \"Package management using pkgin, a binary package manager for pkgsrc.\"\n\n  commands :pkgin => \"pkgin\"\n\n  defaultfor 'os.name' => [:smartos, :netbsd]\n\n  has_feature :installable, :uninstallable, :upgradeable, :versionable\n\n  def self.parse_pkgin_line(package)\n    # e.g.\n    #   vim-7.2.446;Vim editor (vi clone) without GUI\n    match, name, version, status = *package.match(/([^\\s;]+)-([^\\s;]+)[;\\s](=|>|<)?.+$/)\n    if match\n      {\n        :name => name,\n        :status => status,\n        :ensure => version\n      }\n    end\n  end\n\n  def self.prefetch(packages)\n    super\n    # Without -f, no fresh pkg_summary files are downloaded\n    pkgin(\"-yf\", :update)\n  end\n\n  def self.instances\n    pkgin(:list).split(\"\\n\").map do |package|\n      new(parse_pkgin_line(package))\n    end\n  end\n\n  def query\n    packages = parse_pkgsearch_line\n\n    if packages.empty?\n      if @resource[:ensure] == :absent\n        notice _(\"declared as absent but unavailable %{file}:%{line}\") % { file: @resource.file, line: resource.line }\n        return false\n      else\n        @resource.fail _(\"No candidate to be installed\")\n      end\n    end\n\n    packages.first.update(:ensure => :absent)\n  end\n\n  def parse_pkgsearch_line\n    packages = pkgin(:search, resource[:name]).split(\"\\n\")\n\n    return [] if packages.length == 1\n\n    # Remove the last three lines of help text.\n    packages.slice!(-4, 4)\n\n    pkglist = packages.map { |line| self.class.parse_pkgin_line(line) }\n    pkglist.select { |package| resource[:name] == package[:name] }\n  end\n\n  def install\n    if @resource[:ensure].is_a?(String)\n      pkgin(\"-y\", :install, \"#{resource[:name]}-#{resource[:ensure]}\")\n    else\n      pkgin(\"-y\", :install, resource[:name])\n    end\n  end\n\n  def uninstall\n    pkgin(\"-y\", :remove, resource[:name])\n  end\n\n  def latest\n    package = parse_pkgsearch_line.detect { |p| p[:status] == '<' }\n    return properties[:ensure] unless package\n\n    package[:ensure]\n  end\n\n  def update\n    pkgin(\"-y\", :install, resource[:name])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pkgng.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :pkgng, :parent => Puppet::Provider::Package do\n  desc \"A PkgNG provider for FreeBSD and DragonFly.\"\n\n  commands :pkg => \"/usr/local/sbin/pkg\"\n\n  confine 'os.name' => [:freebsd, :dragonfly]\n\n  defaultfor 'os.name' => [:freebsd, :dragonfly]\n\n  has_feature :versionable\n  has_feature :upgradeable\n  has_feature :install_options\n\n  def self.get_query\n    pkg(['query', '-a', '%n %v %o'])\n  end\n\n  def self.get_resource_info(name)\n    pkg(['query', '%n %v %o', name])\n  end\n\n  def self.cached_version_list\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @version_list ||= get_version_list\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  def self.get_version_list\n    @version_list = pkg(['version', '-voRL='])\n  end\n\n  def self.get_latest_version(origin)\n    latest_version = cached_version_list.lines.find { |l| l =~ /^#{origin} / }\n    if latest_version\n      _name, compare, status = latest_version.chomp.split(' ', 3)\n      if ['!', '?'].include?(compare)\n        return nil\n      end\n\n      latest_version = status.split(' ').last.split(')').first\n      return latest_version\n    end\n    nil\n  end\n\n  def self.parse_pkg_query_line(line)\n    name, version, origin = line.chomp.split(' ', 3)\n    latest_version = get_latest_version(origin) || version\n\n    {\n      :ensure => version,\n      :name => name,\n      :provider => self.name,\n      :origin => origin,\n      :version => version,\n      :latest => latest_version\n    }\n  end\n\n  def self.instances\n    packages = []\n    begin\n      info = get_query\n      get_version_list\n\n      unless info\n        return packages\n      end\n\n      info.lines.each do |line|\n        hash = parse_pkg_query_line(line)\n        packages << new(hash)\n      end\n\n      packages\n    rescue Puppet::ExecutionFailure\n      []\n    end\n  end\n\n  def self.prefetch(resources)\n    packages = instances\n    resources.each_key do |name|\n      provider = packages.find { |p| p.name == name or p.origin == name }\n      if provider\n        resources[name].provider = provider\n      end\n    end\n  end\n\n  def repo_tag_from_urn(urn)\n    # extract repo tag from URN: urn:freebsd:repo:<tag>\n    match = /^urn:freebsd:repo:(.+)$/.match(urn)\n    raise ArgumentError urn.inspect unless match\n\n    match[1]\n  end\n\n  def install\n    source = resource[:source]\n    source = URI(source) unless source.nil?\n\n    # Ensure we handle the version\n    case resource[:ensure]\n    when true, false, Symbol\n      installname = resource[:name]\n    else\n      # If resource[:name] is actually an origin (e.g. 'www/curl' instead of\n      # just 'curl'), drop the category prefix. pkgng doesn't support version\n      # pinning with the origin syntax (pkg install curl-1.2.3 is valid, but\n      # pkg install www/curl-1.2.3 is not).\n      if resource[:name] =~ %r{/}\n        installname = resource[:name].split('/')[1] + '-' + resource[:ensure]\n      else\n        installname = resource[:name] + '-' + resource[:ensure]\n      end\n    end\n\n    if !source # install using default repo logic\n      args = ['install', '-qy']\n    elsif source.scheme == 'urn' # install from repo named in URN\n      tag = repo_tag_from_urn(source.to_s)\n      args = ['install', '-qy', '-r', tag]\n    else # add package located at URL\n      args = ['add', '-q']\n      installname = source.to_s\n    end\n    args += install_options if @resource[:install_options]\n    args << installname\n\n    pkg(args)\n  end\n\n  def uninstall\n    pkg(['remove', '-qy', resource[:name]])\n  end\n\n  def query\n    begin\n      output = self.class.get_resource_info(resource[:name])\n    rescue Puppet::ExecutionFailure\n      return nil\n    end\n\n    self.class.parse_pkg_query_line(output)\n  end\n\n  def version\n    @property_hash[:version]\n  end\n\n  def version=\n    pkg(['install', '-qfy', \"#{resource[:name]}-#{resource[:version]}\"])\n  end\n\n  # Upgrade to the latest version\n  def update\n    install\n  end\n\n  # Return the latest version of the package\n  def latest\n    debug \"returning the latest #{@property_hash[:name].inspect} version #{@property_hash[:latest].inspect}\"\n    @property_hash[:latest]\n  end\n\n  def origin\n    @property_hash[:origin]\n  end\n\n  def install_options\n    join_options(@resource[:install_options])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/pkgutil.rb",
    "content": "# frozen_string_literal: true\n\n# Packaging using Peter Bonivart's pkgutil program.\nPuppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun do\n  desc \"Package management using Peter Bonivart's ``pkgutil`` command on Solaris.\"\n\n  pkgutil_bin = \"pkgutil\"\n  if FileTest.executable?(\"/opt/csw/bin/pkgutil\")\n    pkgutil_bin = \"/opt/csw/bin/pkgutil\"\n  end\n\n  confine 'os.family' => :solaris\n\n  has_command(:pkguti, pkgutil_bin) do\n    environment :HOME => ENV.fetch('HOME', nil)\n  end\n\n  def self.healthcheck\n    unless Puppet::FileSystem.exist?(\"/var/opt/csw/pkgutil/admin\")\n      Puppet.notice _(\"It is highly recommended you create '/var/opt/csw/pkgutil/admin'.\")\n      Puppet.notice _(\"See /var/opt/csw/pkgutil\")\n    end\n\n    correct_wgetopts = false\n    [\"/opt/csw/etc/pkgutil.conf\", \"/etc/opt/csw/pkgutil.conf\"].each do |confpath|\n      File.open(confpath) do |conf|\n        conf.each_line { |line| correct_wgetopts = true if line =~ /^\\s*wgetopts\\s*=.*(-nv|-q|--no-verbose|--quiet)/ }\n      end\n    end\n    unless correct_wgetopts\n      Puppet.notice _(\"It is highly recommended that you set 'wgetopts=-nv' in your pkgutil.conf.\")\n    end\n  end\n\n  def self.instances(hash = {})\n    healthcheck\n\n    # Use the available pkg list (-a) to work out aliases\n    aliases = {}\n    availlist.each do |pkg|\n      aliases[pkg[:name]] = pkg[:alias]\n    end\n\n    # The -c pkglist lists installed packages\n    pkginsts = []\n    output = pkguti([\"-c\"])\n    parse_pkglist(output).each do |pkg|\n      pkg.delete(:avail)\n      pkginsts << new(pkg)\n\n      # Create a second instance with the alias if it's different\n      pkgalias = aliases[pkg[:name]]\n      next unless pkgalias and pkg[:name] != pkgalias\n\n      apkg = pkg.dup\n      apkg[:name] = pkgalias\n      pkginsts << new(apkg)\n    end\n\n    pkginsts\n  end\n\n  # Turns a pkgutil -a listing into hashes with the common alias, full\n  # package name and available version\n  def self.availlist\n    output = pkguti [\"-a\"]\n\n    output.split(\"\\n\").filter_map do |line|\n      next if line =~ /^common\\s+package/ # header of package list\n      next if noise?(line)\n\n      if line =~ /\\s*(\\S+)\\s+(\\S+)\\s+(.*)/\n        { :alias => Regexp.last_match(1), :name => Regexp.last_match(2), :avail => Regexp.last_match(3) }\n      else\n        Puppet.warning _(\"Cannot match %{line}\") % { line: line }\n      end\n    end\n  end\n\n  # Turn our pkgutil -c listing into a hash for a single package.\n  def pkgsingle(resource)\n    # The --single option speeds up the execution, because it queries\n    # the package management system for one package only.\n    command = [\"-c\", \"--single\", resource[:name]]\n    self.class.parse_pkglist(run_pkgutil(resource, command), { :justme => resource[:name] })\n  end\n\n  # Turn our pkgutil -c listing into a bunch of hashes.\n  def self.parse_pkglist(output, hash = {})\n    output = output.split(\"\\n\")\n\n    if output[-1] == \"Not in catalog\"\n      Puppet.warning _(\"Package not in pkgutil catalog: %{package}\") % { package: hash[:justme] }\n      return nil\n    end\n\n    list = output.filter_map do |line|\n      next if line =~ /installed\\s+catalog/ # header of package list\n      next if noise?(line)\n\n      pkgsplit(line)\n    end\n\n    if hash[:justme]\n      # Single queries may have been for an alias so return the name requested\n      if list.any?\n        list[-1][:name] = hash[:justme]\n        list[-1]\n      end\n    else\n      list.reject! { |h| h[:ensure] == :absent }\n      list\n    end\n  end\n\n  # Identify common types of pkgutil noise as it downloads catalogs etc\n  def self.noise?(line)\n    return true if line =~ /^#/\n    return true if line =~ /^Checking integrity / # use_gpg\n    return true if line =~ /^gpg: /               # gpg verification\n    return true if line =~ /^=+> /                # catalog fetch\n    return true if line =~ /\\d+:\\d+:\\d+ URL:/     # wget without -q\n\n    false\n  end\n\n  # Split the different lines into hashes.\n  def self.pkgsplit(line)\n    if line =~ /\\s*(\\S+)\\s+(\\S+)\\s+(.*)/\n      hash = {}\n      hash[:name] = Regexp.last_match(1)\n      hash[:ensure] = if Regexp.last_match(2) == \"notinst\"\n                        :absent\n                      else\n                        Regexp.last_match(2)\n                      end\n      hash[:avail] = Regexp.last_match(3)\n\n      if hash[:avail] =~ /^SAME\\s*$/\n        hash[:avail] = hash[:ensure]\n      end\n\n      # Use the name method, so it works with subclasses.\n      hash[:provider] = name\n\n      hash\n    else\n      Puppet.warning _(\"Cannot match %{line}\") % { line: line }\n      nil\n    end\n  end\n\n  def run_pkgutil(resource, *args)\n    # Allow source to be one or more URLs pointing to a repository that all\n    # get passed to pkgutil via one or more -t options\n    if resource[:source]\n      sources = [resource[:source]].flatten\n      pkguti(*[sources.map { |src| [\"-t\", src] }, *args].flatten)\n    else\n      pkguti(*args.flatten)\n    end\n  end\n\n  def install\n    run_pkgutil @resource, \"-y\", \"-i\", @resource[:name]\n  end\n\n  # Retrieve the version from the current package file.\n  def latest\n    hash = pkgsingle(@resource)\n    hash[:avail] if hash\n  end\n\n  def query\n    hash = pkgsingle(@resource)\n    hash || { :ensure => :absent }\n  end\n\n  def update\n    run_pkgutil @resource, \"-y\", \"-u\", @resource[:name]\n  end\n\n  def uninstall\n    run_pkgutil @resource, \"-y\", \"-r\", @resource[:name]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/portage.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire 'fileutils'\n\nPuppet::Type.type(:package).provide :portage, :parent => Puppet::Provider::Package do\n  desc \"Provides packaging support for Gentoo's portage system.\n\n    This provider supports the `install_options` and `uninstall_options` attributes, which allows command-line\n    flags to be passed to emerge. These options should be specified as an array where each element is either a string or a hash.\"\n\n  has_features :install_options, :purgeable, :reinstallable, :uninstall_options, :versionable, :virtual_packages\n\n  {\n    :emerge => '/usr/bin/emerge',\n    :eix => '/usr/bin/eix',\n    :qatom_bin => '/usr/bin/qatom',\n    :update_eix => '/usr/bin/eix-update',\n  }.each_pair do |name, path|\n    has_command(name, path) do\n      environment :HOME => '/'\n    end\n  end\n\n  confine 'os.family' => :gentoo\n\n  defaultfor 'os.family' => :gentoo\n\n  def self.instances\n    result_format = eix_result_format\n    result_fields = eix_result_fields\n\n    limit = eix_limit\n    version_format = eix_version_format\n    slot_versions_format = eix_slot_versions_format\n    installed_versions_format = eix_installed_versions_format\n    installable_versions_format = eix_install_versions_format\n    begin\n      eix_file = File.directory?('/var/cache/eix') ? '/var/cache/eix/portage.eix' : '/var/cache/eix'\n      update_eix unless FileUtils.uptodate?(eix_file, %w[/usr/bin/eix /usr/portage/metadata/timestamp])\n\n      search_output = nil\n      Puppet::Util.withenv :EIX_LIMIT => limit, :LASTVERSION => version_format, :LASTSLOTVERSIONS => slot_versions_format, :INSTALLEDVERSIONS => installed_versions_format, :STABLEVERSIONS => installable_versions_format do\n        search_output = eix(*(eix_search_arguments + ['--installed']))\n      end\n\n      packages = []\n      search_output.each_line do |search_result|\n        match = result_format.match(search_result)\n\n        next unless match\n\n        package = {}\n        result_fields.zip(match.captures) do |field, value|\n          package[field] = value unless !value or value.empty?\n        end\n        package[:provider] = :portage\n        packages << new(package)\n      end\n\n      packages\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, detail\n    end\n  end\n\n  def install\n    should = @resource.should(:ensure)\n    cmd = %w[]\n    name = qatom[:category] ? \"#{qatom[:category]}/#{qatom[:pn]}\" : qatom[:pn]\n    name = qatom[:pfx] + name if qatom[:pfx]\n    name = name + '-' + qatom[:pv] if qatom[:pv]\n    name = name + '-' + qatom[:pr] if qatom[:pr]\n    name = name + ':' + qatom[:slot] if qatom[:slot]\n    cmd << '--update' if [:latest].include?(should)\n    cmd += install_options if @resource[:install_options]\n    cmd << name\n    emerge(*cmd)\n  end\n\n  def uninstall\n    should = @resource.should(:ensure)\n    cmd = %w[--rage-clean]\n    name = qatom[:category] ? \"#{qatom[:category]}/#{qatom[:pn]}\" : qatom[:pn]\n    name = qatom[:pfx] + name if qatom[:pfx]\n    name = name + '-' + qatom[:pv] if qatom[:pv]\n    name = name + '-' + qatom[:pr] if qatom[:pr]\n    name = name + ':' + qatom[:slot] if qatom[:slot]\n    cmd += uninstall_options if @resource[:uninstall_options]\n    cmd << name\n    if [:purged].include?(should)\n      Puppet::Util.withenv :CONFIG_PROTECT => \"-*\" do\n        emerge(*cmd)\n      end\n    else\n      emerge(*cmd)\n    end\n  end\n\n  def reinstall\n    install\n  end\n\n  def update\n    install\n  end\n\n  def qatom\n    output_format = qatom_output_format\n    result_format = qatom_result_format\n    result_fields = qatom_result_fields\n    @atom ||= begin\n      package_info = {}\n      # do the search\n      should = @resource[:ensure]\n      case should\n      # The terms present, absent, purged, installed, latest in :ensure\n      # resolve as Symbols, and we do not need specific package version in this case\n      when true, false, Symbol\n        search = @resource[:name]\n      else\n        search = '=' + @resource[:name] + '-' + should.to_s\n      end\n      search_output = qatom_bin(*[search, '--format', output_format])\n      # verify if the search found anything\n      match = result_format.match(search_output)\n      if match\n        result_fields.zip(match.captures) do |field, value|\n          # some fields can be empty or (null) (if we are not passed a category in the package name for instance)\n          if value == '(null)' || value == '<unset>'\n            package_info[field] = nil\n          elsif !value or value.empty?\n            package_info[field] = nil\n          else\n            package_info[field] = value\n          end\n        end\n      end\n      @atom = package_info\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, detail\n    end\n  end\n\n  def qatom_output_format\n    '\"[%[CATEGORY]] [%[PN]] [%[PV]] [%[PR]] [%[SLOT]] [%[pfx]] [%[sfx]]\"'\n  end\n\n  def qatom_result_format\n    /^\"\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\](.*)\"$/\n  end\n\n  def qatom_result_fields\n    [:category, :pn, :pv, :pr, :slot, :pfx, :sfx]\n  end\n\n  def self.get_sets\n    @sets ||= @sets = emerge(*['--list-sets'])\n  end\n\n  def query\n    limit = self.class.eix_limit\n    result_format = self.class.eix_result_format\n    result_fields = self.class.eix_result_fields\n\n    version_format = self.class.eix_version_format\n    slot_versions_format = self.class.eix_slot_versions_format\n    installed_versions_format = self.class.eix_installed_versions_format\n    installable_versions_format = self.class.eix_install_versions_format\n    search_field = qatom[:category] ? '--category-name' : '--name'\n    search_value = qatom[:category] ? \"#{qatom[:category]}/#{qatom[:pn]}\" : qatom[:pn]\n\n    @eix_result ||= begin\n      # package sets\n      package_sets = []\n      self.class.get_sets.each_line do |package_set|\n        package_sets << package_set.to_s.strip\n      end\n\n      if @resource[:name] =~ /^@/\n        if package_sets.include?(@resource[:name][1..].to_s)\n          return({ :name => (@resource[:name]).to_s, :ensure => '9999', :version_available => nil, :installed_versions => nil, :installable_versions => \"9999,\" })\n        end\n      end\n\n      eix_file = File.directory?('/var/cache/eix') ? '/var/cache/eix/portage.eix' : '/var/cache/eix'\n      update_eix unless FileUtils.uptodate?(eix_file, %w[/usr/bin/eix /usr/portage/metadata/timestamp])\n\n      search_output = nil\n      Puppet::Util.withenv :EIX_LIMIT => limit, :LASTVERSION => version_format, :LASTSLOTVERSIONS => slot_versions_format, :INSTALLEDVERSIONS => installed_versions_format, :STABLEVERSIONS => installable_versions_format do\n        search_output = eix(*(self.class.eix_search_arguments + ['--exact', search_field, search_value]))\n      end\n\n      packages = []\n      search_output.each_line do |search_result|\n        match = result_format.match(search_result)\n\n        next unless match\n\n        package = {}\n        result_fields.zip(match.captures) do |field, value|\n          package[field] = value unless !value or value.empty?\n        end\n        # dev-lang python [3.4.5] [3.5.2] [2.7.12:2.7,3.4.5:3.4] [2.7.12:2.7,3.4.5:3.4,3.5.2:3.5] https://www.python.org/ An interpreted, interactive, object-oriented programming language\n        # version_available is what we CAN install / update to\n        # ensure is what is currently installed\n        # This DOES NOT choose to install/upgrade or not, just provides current info\n        # prefer checking versions to slots as versions are finer grained\n        search = qatom[:pv]\n        search = search + '-' + qatom[:pr] if qatom[:pr]\n        if search\n          package[:version_available] = eix_get_version_for_versions(package[:installable_versions], search)\n          package[:ensure] = eix_get_version_for_versions(package[:installed_versions], search)\n        elsif qatom[:slot]\n          package[:version_available] = eix_get_version_for_slot(package[:slot_versions_available], qatom[:slot])\n          package[:ensure] = eix_get_version_for_slot(package[:installed_slots], qatom[:slot])\n        end\n\n        package[:ensure] = package[:ensure] || :absent\n        packages << package\n      end\n\n      case packages.size\n      when 0\n        raise Puppet::Error, _(\"No package found with the specified name [%{name}]\") % { name: @resource[:name] }\n      when 1\n        @eix_result = packages[0]\n      else\n        raise Puppet::Error, _(\"More than one package with the specified name [%{search_value}], please use the category parameter to disambiguate\") % { search_value: search_value }\n      end\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, detail\n    end\n  end\n\n  def latest\n    query[:version_available]\n  end\n\n  private\n\n  def eix_get_version_for_versions(versions, target)\n    # [2.7.10-r1,2.7.12,3.4.3-r1,3.4.5,3.5.2] 3.5.2\n    return nil if versions.nil?\n\n    versions = versions.split(',')\n    # [2.7.10-r1 2.7.12 3.4.3-r1 3.4.5 3.5.2]\n    versions.find { |version| version == target }\n    # 3.5.2\n  end\n\n  private\n\n  def eix_get_version_for_slot(versions_and_slots, slot)\n    # [2.7.12:2.7 3.4.5:3.4 3.5.2:3.5] 3.5\n    return nil if versions_and_slots.nil?\n\n    versions_and_slots = versions_and_slots.split(',')\n    # [2.7.12:2.7 3.4.5:3.4 3.5.2:3.5]\n    versions_and_slots.map! { |version_and_slot| version_and_slot.split(':') }\n    # [2.7.12: 2.7\n    #  3.4.5:  3.4\n    #  3.5.2:  3.5]\n    version_for_slot = versions_and_slots.find { |version_and_slot| version_and_slot.last == slot }\n    # [3.5.2:  3.5]\n    version_for_slot.first if version_for_slot\n    # 3.5.2\n  end\n\n  def self.eix_search_format\n    \"'<category> <name> [<installedversions:LASTVERSION>] [<bestversion:LASTVERSION>] [<installedversions:LASTSLOTVERSIONS>] [<installedversions:INSTALLEDVERSIONS>] [<availableversions:STABLEVERSIONS>] [<bestslotversions:LASTSLOTVERSIONS>] <homepage> <description>\\n'\"\n  end\n\n  def self.eix_result_format\n    /^(\\S+)\\s+(\\S+)\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+\\[(\\S*)\\]\\s+(\\S+)\\s+(.*)$/\n  end\n\n  def self.eix_result_fields\n    # ensure:[3.4.5], version_available:[3.5.2], installed_slots:[2.7.12:2.7,3.4.5:3.4], installable_versions:[2.7.10-r1,2.7.12,3.4.3-r1,3.4.5,3.5.2] slot_versions_available:[2.7.12:2.7,3.4.5:3.4,3.5.2:3.5]\n    [:category, :name, :ensure, :version_available, :installed_slots, :installed_versions, :installable_versions, :slot_versions_available, :vendor, :description]\n  end\n\n  def self.eix_version_format\n    '{last}<version>{}'\n  end\n\n  def self.eix_slot_versions_format\n    '{!first},{}<version>:<slot>'\n  end\n\n  def self.eix_installed_versions_format\n    '{!first},{}<version>'\n  end\n\n  def self.eix_install_versions_format\n    '{!first}{!last},{}{}{isstable}<version>{}'\n  end\n\n  def self.eix_limit\n    '0'\n  end\n\n  def self.eix_search_arguments\n    ['--nocolor', '--pure-packages', '--format', eix_search_format]\n  end\n\n  def install_options\n    join_options(@resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(@resource[:uninstall_options])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/ports.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :ports, :parent => :freebsd, :source => :freebsd do\n  desc \"Support for FreeBSD's ports.  Note that this, too, mixes packages and ports.\"\n\n  commands :portupgrade => \"/usr/local/sbin/portupgrade\",\n           :portversion => \"/usr/local/sbin/portversion\",\n           :portuninstall => \"/usr/local/sbin/pkg_deinstall\",\n           :portinfo => \"/usr/sbin/pkg_info\"\n\n  %w[INTERACTIVE UNAME].each do |var|\n    ENV.delete(var) if ENV.include?(var)\n  end\n\n  def install\n    # -N: install if the package is missing, otherwise upgrade\n    # -M: yes, we're a batch, so don't ask any questions\n    cmd = %w[-N -M BATCH=yes] << @resource[:name]\n\n    output = portupgrade(*cmd)\n    if output =~ /\\*\\* No such /\n      raise Puppet::ExecutionFailure, _(\"Could not find package %{name}\") % { name: @resource[:name] }\n    end\n  end\n\n  # If there are multiple packages, we only use the last one\n  def latest\n    cmd = [\"-v\", @resource[:name]]\n\n    begin\n      output = portversion(*cmd)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n    line = output.split(\"\\n\").pop\n\n    unless line =~ /^(\\S+)\\s+(\\S)\\s+(.+)$/\n      # There's no \"latest\" version, so just return a placeholder\n      return :latest\n    end\n\n    pkgstuff = Regexp.last_match(1)\n    match = Regexp.last_match(2)\n    info = Regexp.last_match(3)\n\n    unless pkgstuff =~ /^\\S+-([^-\\s]+)$/\n      raise Puppet::Error,\n            _(\"Could not match package info '%{pkgstuff}'\") % { pkgstuff: pkgstuff }\n    end\n\n    version = Regexp.last_match(1)\n\n    if match == \"=\" or match == \">\"\n      # we're up to date or more recent\n      return version\n    end\n\n    # Else, we need to be updated; we need to pull out the new version\n\n    unless info =~ /\\((\\w+) has (.+)\\)/\n      raise Puppet::Error,\n            _(\"Could not match version info '%{info}'\") % { info: info }\n    end\n\n    source = Regexp.last_match(1)\n    newversion = Regexp.last_match(2)\n\n    debug \"Newer version in #{source}\"\n    newversion\n  end\n\n  def query\n    # support portorigin_glob such as \"mail/postfix\"\n    name = self.name\n    if name =~ %r{/}\n      name = self.name.split(%r{/}).slice(1)\n    end\n    self.class.instances.each do |instance|\n      if instance.name == name\n        return instance.properties\n      end\n    end\n\n    nil\n  end\n\n  def uninstall\n    portuninstall @resource[:name]\n  end\n\n  def update\n    install\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/portupgrade.rb",
    "content": "# frozen_string_literal: true\n\n# Whole new package, so include pack stuff\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::Package do\n  include Puppet::Util::Execution\n\n  desc \"Support for FreeBSD's ports using the portupgrade ports management software.\n    Use the port's full origin as the resource name. eg (ports-mgmt/portupgrade)\n    for the portupgrade port.\"\n\n  ## has_features is usually autodetected based on defs below.\n  # has_features :installable, :uninstallable, :upgradeable\n\n  commands :portupgrade => \"/usr/local/sbin/portupgrade\",\n           :portinstall => \"/usr/local/sbin/portinstall\",\n           :portversion => \"/usr/local/sbin/portversion\",\n           :portuninstall => \"/usr/local/sbin/pkg_deinstall\",\n           :portinfo => \"/usr/sbin/pkg_info\"\n\n  ## Activate this only once approved by someone important.\n  # defaultfor 'os.name' => :freebsd\n\n  # Remove unwanted environment variables.\n  %w[INTERACTIVE UNAME].each do |var|\n    if ENV.include?(var)\n      ENV.delete(var)\n    end\n  end\n\n  ######## instances sub command (builds the installed packages list)\n\n  def self.instances\n    Puppet.debug \"portupgrade.rb Building packages list from installed ports\"\n\n    # regex to match output from pkg_info\n    regex = /^(\\S+)-([^-\\s]+):(\\S+)$/\n    # Corresponding field names\n    fields = [:portname, :ensure, :portorigin]\n    # define Temporary hash used, packages array of hashes\n    hash = Hash.new\n    packages = []\n\n    # exec command\n    cmdline = [\"-aoQ\"]\n    begin\n      output = portinfo(*cmdline)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n\n    # split output and match it and populate temp hash\n    output.split(\"\\n\").each { |data|\n      # reset hash to nil for each line\n      hash.clear\n      match = regex.match(data)\n      if match\n        # Output matched regex\n        fields.zip(match.captures) { |field, value|\n          hash[field] = value\n        }\n\n        # populate the actual :name field from the :portorigin\n        # Set :provider to this object name\n        hash[:name] = hash[:portorigin]\n        hash[:provider] = name\n\n        # Add to the full packages listing\n        packages << new(hash)\n      else\n        # unrecognised output from pkg_info\n        Puppet.debug \"portupgrade.Instances() - unable to match output: #{data}\"\n      end\n    }\n\n    # return the packages array of hashes\n    packages\n  end\n\n  ######## Installation sub command\n\n  def install\n    Puppet.debug \"portupgrade.install() - Installation call on #{@resource[:name]}\"\n    # -M: yes, we're a batch, so don't ask any questions\n    cmdline = [\"-M BATCH=yes\", @resource[:name]]\n\n    # FIXME: it's possible that portinstall prompts for data so locks up.\n    begin\n      output = portinstall(*cmdline)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n\n    if output =~ /\\*\\* No such /\n      raise Puppet::ExecutionFailure, _(\"Could not find package %{name}\") % { name: @resource[:name] }\n    end\n\n    # No return code required, so do nil to be clean\n    nil\n  end\n\n  ######## Latest subcommand (returns the latest version available, or current version if installed is latest)\n\n  def latest\n    Puppet.debug \"portupgrade.latest() - Latest check called on #{@resource[:name]}\"\n    # search for latest version available, or return current version.\n    # cmdline = \"portversion -v <portorigin>\", returns \"<portname> <code> <stuff>\"\n    # or \"** No matching package found: <portname>\"\n    cmdline = [\"-v\", @resource[:name]]\n\n    begin\n      output = portversion(*cmdline)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n\n    # Check: output format.\n    if output =~ /^\\S+-([^-\\s]+)\\s+(\\S)\\s+(.*)/\n      installedversion = Regexp.last_match(1)\n      comparison = Regexp.last_match(2)\n      otherdata = Regexp.last_match(3)\n\n      # Only return a new version number when it's clear that there is a new version\n      # all others return the current version so no unexpected 'upgrades' occur.\n      case comparison\n      when \"=\", \">\"\n        Puppet.debug \"portupgrade.latest() - Installed package is latest (#{installedversion})\"\n        installedversion\n      when \"<\"\n        # \"portpkg-1.7_5 < needs updating (port has 1.14)\"\n        # \"portpkg-1.7_5 < needs updating (port has 1.14) (=> 'newport/pkg')\n        if otherdata =~ /\\(port has (\\S+)\\)/\n          newversion = Regexp.last_match(1)\n          Puppet.debug \"portupgrade.latest() - Installed version needs updating to (#{newversion})\"\n          newversion\n        else\n          Puppet.debug \"portupgrade.latest() - Unable to determine new version from (#{otherdata})\"\n          installedversion\n        end\n      when \"?\", \"!\", \"#\"\n        Puppet.debug \"portupgrade.latest() - Comparison Error reported from portversion (#{output})\"\n        installedversion\n      else\n        Puppet.debug \"portupgrade.latest() - Unknown code from portversion output (#{output})\"\n        installedversion\n      end\n\n    elsif output =~ /^\\*\\* No matching package /\n      # error: output not parsed correctly, error out with nil.\n      # Seriously - this section should never be called in a perfect world.\n      # as verification that the port is installed has already happened in query.\n      raise Puppet::ExecutionFailure, _(\"Could not find package %{name}\") % { name: @resource[:name] }\n    else\n      # Any other error (dump output to log)\n      raise Puppet::ExecutionFailure, _(\"Unexpected output from portversion: %{output}\") % { output: output }\n    end\n  end\n\n  ###### Query subcommand - return a hash of details if exists, or nil if it doesn't.\n  # Used to make sure the package is installed\n\n  def query\n    Puppet.debug \"portupgrade.query() - Called on #{@resource[:name]}\"\n\n    cmdline = [\"-qO\", @resource[:name]]\n    begin\n      output = portinfo(*cmdline)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n\n    # Check: if output isn't in the right format, return nil\n    if output =~ /^(\\S+)-([^-\\s]+)/\n      # Fill in the details\n      hash = Hash.new\n      hash[:portorigin] = name\n      hash[:portname]   = Regexp.last_match(1)\n      hash[:ensure]     = Regexp.last_match(2)\n\n      # If more details are required, then we can do another pkg_info\n      # query here and parse out that output and add to the hash\n      # return the hash to the caller\n      hash\n    else\n      Puppet.debug \"portupgrade.query() - package (#{@resource[:name]}) not installed\"\n      nil\n    end\n  end\n\n  ####### Uninstall command\n\n  def uninstall\n    Puppet.debug \"portupgrade.uninstall() - called on #{@resource[:name]}\"\n    # Get full package name from port origin to uninstall with\n    cmdline = [\"-qO\", @resource[:name]]\n    begin\n      output = portinfo(*cmdline)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n\n    if output =~ /^(\\S+)/\n      # output matches, so uninstall it\n      portuninstall Regexp.last_match(1)\n    end\n  end\n\n  ######## Update/upgrade command\n\n  def update\n    Puppet.debug \"portupgrade.update() - called on (#{@resource[:name]})\"\n\n    cmdline = [\"-qO\", @resource[:name]]\n    begin\n      output = portinfo(*cmdline)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(output, e)\n    end\n\n    if output =~ /^(\\S+)/\n      # output matches, so upgrade the software\n      cmdline = [\"-M BATCH=yes\", Regexp.last_match(1)]\n      begin\n        output = portupgrade(*cmdline)\n      rescue Puppet::ExecutionFailure => e\n        raise Puppet::Error.new(output, e)\n      end\n    end\n  end\n\n  ## EOF\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/puppet_gem.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :puppet_gem, :parent => :gem do\n  desc \"Puppet Ruby Gem support. This provider is useful for managing\n        gems needed by the ruby provided in the puppet-agent package.\"\n\n  has_feature :versionable, :install_options, :uninstall_options\n\n  confine :true => Puppet.runtime[:facter].value(:aio_agent_version)\n\n  commands :gemcmd => Puppet.run_mode.gem_cmd\n\n  def uninstall\n    super\n    Puppet.debug(\"Invalidating rubygems cache after uninstalling gem '#{resource[:name]}'\")\n    Puppet::Util::Autoload.gem_source.clear_paths\n  end\n\n  def self.execute_gem_command(command, command_options, custom_environment = {})\n    if (pkg_config_path = Puppet.run_mode.pkg_config_path)\n      custom_environment['PKG_CONFIG_PATH'] = pkg_config_path\n    end\n    super(command, command_options, custom_environment)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/puppetserver_gem.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'English'\nunless Puppet::Util::Platform.jruby_fips?\n  require 'rubygems/commands/list_command'\nend\nrequire 'stringio'\nrequire 'uri'\n\n# Ruby gems support.\nPuppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do\n  desc \"Puppet Server Ruby Gem support. If a URL is passed via `source`, then\n    that URL is appended to the list of remote gem repositories which by default\n    contains rubygems.org; To ensure that only the specified source is used also\n    pass `--clear-sources` in via `install_options`; if a source is present but\n    is not a valid URL, it will be interpreted as the path to a local gem file.\n    If source is not present at all, the gem will be installed from the default\n    gem repositories.\"\n\n  has_feature :versionable, :install_options, :uninstall_options\n\n  confine :feature => :hocon\n  # see SERVER-2578\n  confine :fips_enabled => false\n\n  # Define the default provider package command name, as the parent 'gem' provider is targetable.\n  # Required by Puppet::Provider::Package::Targetable::resource_or_provider_command\n\n  def self.provider_command\n    command(:puppetservercmd)\n  end\n\n  # The gem command uses HOME to locate a gemrc file.\n  # CommandDefiner in provider.rb will set failonfail, combine, and environment.\n\n  has_command(:puppetservercmd, '/opt/puppetlabs/bin/puppetserver') do\n    environment(HOME: ENV.fetch('HOME', nil))\n  end\n\n  def self.gemlist(options)\n    command_options = %w[gem list]\n\n    if options[:local]\n      command_options << '--local'\n    else\n      command_options << '--remote'\n    end\n\n    if options[:source]\n      command_options << '--source' << options[:source]\n    end\n\n    if options[:justme]\n      gem_regex = '\\A' + options[:justme] + '\\z'\n      command_options << gem_regex\n    end\n\n    if options[:local]\n      list = execute_rubygems_list_command(command_options)\n    else\n      begin\n        list = puppetservercmd(command_options)\n      rescue Puppet::ExecutionFailure => detail\n        raise Puppet::Error, _(\"Could not list gems: %{detail}\") % { detail: detail }, detail.backtrace\n      end\n    end\n\n    # When `/tmp` is mounted `noexec`, `puppetserver gem list` will output:\n    # *** LOCAL GEMS ***\n    # causing gemsplit to output:\n    # Warning: Could not match *** LOCAL GEMS ***\n    gem_list = list\n               .lines\n               .select { |x| x =~ /^(\\S+)\\s+\\((.+)\\)/ }\n               .map { |set| gemsplit(set) }\n\n    if options[:justme]\n      gem_list.shift\n    else\n      gem_list\n    end\n  end\n\n  def install(useversion = true)\n    command_options = %w[gem install]\n    command_options += install_options if resource[:install_options]\n\n    command_options << '-v' << resource[:ensure] if (!resource[:ensure].is_a? Symbol) && useversion\n\n    command_options << '--no-document'\n\n    if resource[:source]\n      begin\n        uri = URI.parse(resource[:source])\n      rescue => detail\n        self.fail Puppet::Error, _(\"Invalid source '%{uri}': %{detail}\") % { uri: uri, detail: detail }, detail\n      end\n\n      case uri.scheme\n      when nil\n        # no URI scheme => interpret the source as a local file\n        command_options << resource[:source]\n      when /file/i\n        command_options << uri.path\n      when 'puppet'\n        # we don't support puppet:// URLs (yet)\n        raise Puppet::Error, _('puppet:// URLs are not supported as gem sources')\n      else\n        # interpret it as a gem repository\n        command_options << '--source' << resource[:source].to_s << resource[:name]\n      end\n    else\n      command_options << resource[:name]\n    end\n\n    output = puppetservercmd(command_options)\n    # Apparently, some gem versions don't exit non-0 on failure.\n    self.fail _(\"Could not install: %{output}\") % { output: output.chomp } if output.include?('ERROR')\n  end\n\n  def uninstall\n    command_options = %w[gem uninstall]\n    command_options << '--executables' << '--all' << resource[:name]\n    command_options += uninstall_options if resource[:uninstall_options]\n\n    output = puppetservercmd(command_options)\n    # Apparently, some gem versions don't exit non-0 on failure.\n    self.fail _(\"Could not uninstall: %{output}\") % { output: output.chomp } if output.include?('ERROR')\n  end\n\n  private\n\n  # The puppetserver gem cli command is very slow, since it starts a JVM.\n  #\n  # Instead, for the list subcommand (which is executed with every puppet run),\n  # use the rubygems library from puppet ruby: setting GEM_HOME and GEM_PATH\n  # to the default values, or the values in the puppetserver configuration file.\n  #\n  # The rubygems library cannot access java platform gems,\n  # for example: json (1.8.3 java)\n  # but java platform gems should not be managed by this (or any) provider.\n\n  def self.execute_rubygems_list_command(command_options)\n    puppetserver_default_gem_home            = '/opt/puppetlabs/server/data/puppetserver/jruby-gems'\n    puppetserver_default_vendored_jruby_gems = '/opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems'\n    puppet_default_vendor_gems               = '/opt/puppetlabs/puppet/lib/ruby/vendor_gems'\n    puppetserver_default_gem_path = [puppetserver_default_gem_home, puppetserver_default_vendored_jruby_gems, puppet_default_vendor_gems].join(':')\n\n    pe_puppetserver_conf_file = '/etc/puppetlabs/puppetserver/conf.d/pe-puppet-server.conf'\n    os_puppetserver_conf_file = '/etc/puppetlabs/puppetserver/puppetserver.conf'\n    puppetserver_conf_file = Puppet.runtime[:facter].value(:pe_server_version) ? pe_puppetserver_conf_file : os_puppetserver_conf_file\n    puppetserver_conf = Hocon.load(puppetserver_conf_file)\n\n    gem_env = {}\n    if puppetserver_conf.empty? || puppetserver_conf.key?('jruby-puppet') == false\n      gem_env['GEM_HOME'] = puppetserver_default_gem_home\n      gem_env['GEM_PATH'] = puppetserver_default_gem_path\n    else\n      gem_env['GEM_HOME'] = puppetserver_conf['jruby-puppet'].key?('gem-home') ? puppetserver_conf['jruby-puppet']['gem-home'] : puppetserver_default_gem_home\n      gem_env['GEM_PATH'] = puppetserver_conf['jruby-puppet'].key?('gem-path') ? puppetserver_conf['jruby-puppet']['gem-path'].join(':') : puppetserver_default_gem_path\n    end\n    gem_env['GEM_SPEC_CACHE'] = \"/tmp/#{$PROCESS_ID}\"\n\n    # Remove the 'gem' from the command_options\n    command_options.shift\n    gem_out = execute_gem_command(Puppet::Type::Package::ProviderPuppet_gem.provider_command, command_options, gem_env)\n\n    # There is no method exclude default gems from the local gem list,\n    # for example: psych (default: 2.2.2)\n    # but default gems should not be managed by this (or any) provider.\n    gem_list = gem_out.lines.reject { |gem| gem =~ / \\(default: / }\n    gem_list.join(\"\\n\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/rpm.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/util/rpm_compare'\n\n# RPM packaging.  Should work anywhere that has rpm installed.\nPuppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Provider::Package do\n  # provides Rpm parsing and comparison\n  include Puppet::Util::RpmCompare\n\n  desc \"RPM packaging support; should work anywhere with a working `rpm`\n    binary.\n\n    This provider supports the `install_options` and `uninstall_options`\n    attributes, which allow command-line flags to be passed to rpm.\n    These options should be specified as an array where each element is either a string or a hash.\"\n\n  has_feature :versionable\n  has_feature :install_options\n  has_feature :uninstall_options\n  has_feature :virtual_packages\n  has_feature :install_only\n\n  # Note: self:: is required here to keep these constants in the context of what will\n  # eventually become this Puppet::Type::Package::ProviderRpm class.\n  # The query format by which we identify installed packages\n  self::NEVRA_FORMAT = '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\\\\n'\n  self::NEVRA_REGEX  = /^'?(\\S+) (\\S+) (\\S+) (\\S+) (\\S+)$/\n  self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]\n  self::MULTIVERSION_SEPARATOR = \"; \"\n\n  commands :rpm => \"rpm\"\n\n  # Mixing confine statements, control expressions, and exception handling\n  # confuses Rubocop's Layout cops, so we disable them entirely.\n  # rubocop:disable Layout\n  if command('rpm')\n    confine :true => begin\n      rpm('--version')\n      rescue Puppet::ExecutionFailure\n        false\n      else\n        true\n      end\n  end\n  # rubocop:enable Layout\n\n  def self.current_version\n    return @current_version unless @current_version.nil?\n\n    output = rpm \"--version\"\n    @current_version = output.gsub('RPM version ', '').strip\n  end\n\n  # rpm < 4.1 does not support --nosignature\n  def self.nosignature\n    '--nosignature' unless Puppet::Util::Package.versioncmp(current_version, '4.1') < 0\n  end\n\n  # rpm < 4.0.2 does not support --nodigest\n  def self.nodigest\n    '--nodigest' unless Puppet::Util::Package.versioncmp(current_version, '4.0.2') < 0\n  end\n\n  def self.instances\n    packages = []\n\n    # list out all of the packages\n    begin\n      execpipe(\"#{command(:rpm)} -qa #{nosignature} #{nodigest} --qf '#{self::NEVRA_FORMAT}' | sort\") { |process|\n        # now turn each returned line into a package object\n        nevra_to_multiversion_hash(process).each { |hash| packages << new(hash) }\n      }\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error, _(\"Failed to list packages\"), e.backtrace\n    end\n\n    packages\n  end\n\n  # Find the fully versioned package name and the version alone. Returns\n  # a hash with entries :instance => fully versioned package name, and\n  # :ensure => version-release\n  def query\n    # NOTE: Prior to a fix for issue 1243, this method potentially returned a cached value\n    # IF YOU CALL THIS METHOD, IT WILL CALL RPM\n    # Use get(:property) to check if cached values are available\n    cmd = [\"-q\", @resource[:name], self.class.nosignature.to_s, self.class.nodigest.to_s, \"--qf\", self.class::NEVRA_FORMAT.to_s]\n\n    begin\n      output = rpm(*cmd)\n    rescue Puppet::ExecutionFailure\n      return nil unless @resource.allow_virtual?\n\n      # rpm -q exits 1 if package not found\n      # retry the query for virtual packages\n      cmd << '--whatprovides'\n      begin\n        output = rpm(*cmd)\n      rescue Puppet::ExecutionFailure\n        # couldn't find a virtual package either\n        return nil\n      end\n    end\n    @property_hash.update(self.class.nevra_to_multiversion_hash(output))\n\n    @property_hash.dup\n  end\n\n  # Here we just retrieve the version from the file specified in the source.\n  def latest\n    source = @resource[:source]\n    unless source\n      @resource.fail _(\"RPMs must specify a package source\")\n    end\n\n    cmd = [command(:rpm), \"-q\", \"--qf\", self.class::NEVRA_FORMAT.to_s, \"-p\", source]\n    h = self.class.nevra_to_multiversion_hash(execute(cmd))\n    h[:ensure]\n  rescue Puppet::ExecutionFailure => e\n    raise Puppet::Error, e.message, e.backtrace\n  end\n\n  def install\n    source = @resource[:source]\n    unless source\n      @resource.fail _(\"RPMs must specify a package source\")\n    end\n\n    version = @property_hash[:ensure]\n\n    # RPM gets upset if you try to install an already installed package\n    return if @resource.should(:ensure) == version || (@resource.should(:ensure) == :latest && version == latest)\n\n    flag = [\"-i\"]\n    flag = [\"-U\", \"--oldpackage\"] if version && (version != :absent && version != :purged)\n    flag += install_options if resource[:install_options]\n    rpm flag, source\n  end\n\n  def uninstall\n    query\n    # If version and release (or only version) is specified in the resource,\n    # uninstall using them, otherwise uninstall using only the name of the package.\n    name    = get(:name)\n    version = get(:version)\n    release = get(:release)\n    nav = \"#{name}-#{version}\"\n    nvr = \"#{nav}-#{release}\"\n    if @resource[:name].start_with? nvr\n      identifier = nvr\n    elsif @resource[:name].start_with? nav\n      identifier = nav\n    elsif @resource[:install_only]\n      identifier = get(:ensure).split(self.class::MULTIVERSION_SEPARATOR).map { |ver| \"#{name}-#{ver}\" }\n    else\n      identifier = name\n    end\n    # If an arch is specified in the resource, uninstall that arch,\n    # otherwise uninstall the arch returned by query.\n    # If multiple arches are installed and arch is not specified,\n    # this will uninstall all of them after successive runs.\n    #\n    # rpm prior to 4.2.1 cannot accept architecture as part of the package name.\n    unless Puppet::Util::Package.versioncmp(self.class.current_version, '4.2.1') < 0\n      arch = \".#{get(:arch)}\"\n      if @resource[:name].end_with? arch\n        identifier += arch\n      end\n    end\n\n    flag = ['-e']\n    flag += uninstall_options if resource[:uninstall_options]\n    rpm flag, identifier\n  end\n\n  def update\n    install\n  end\n\n  def install_options\n    join_options(resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(resource[:uninstall_options])\n  end\n\n  def insync?(is)\n    return false if [:purged, :absent].include?(is)\n    return false if is.include?(self.class::MULTIVERSION_SEPARATOR) && !@resource[:install_only]\n\n    should = resource[:ensure]\n    is.split(self.class::MULTIVERSION_SEPARATOR).any? do |version|\n      0 == rpm_compare_evr(should, version)\n    end\n  end\n\n  private\n\n  # @param line [String] one line of rpm package query information\n  # @return [Hash] of NEVRA_FIELDS strings parsed from package info\n  # or an empty hash if we failed to parse\n  # @api private\n  def self.nevra_to_hash(line)\n    line.strip!\n    hash = {}\n\n    match = self::NEVRA_REGEX.match(line)\n    if match\n      self::NEVRA_FIELDS.zip(match.captures) { |f, v| hash[f] = v }\n      hash[:provider] = name\n      hash[:ensure] = \"#{hash[:version]}-#{hash[:release]}\"\n      hash[:ensure].prepend(\"#{hash[:epoch]}:\") if hash[:epoch] != '0'\n    else\n      Puppet.debug(\"Failed to match rpm line #{line}\")\n    end\n\n    hash\n  end\n\n  # @param line [String] multiple lines of rpm package query information\n  # @return list of [Hash] of NEVRA_FIELDS strings parsed from package info\n  # or an empty list if we failed to parse\n  # @api private\n  def self.nevra_to_multiversion_hash(multiline)\n    list = []\n    multiversion_hash = {}\n    multiline.each_line do |line|\n      hash = nevra_to_hash(line)\n      next if hash.empty?\n\n      if multiversion_hash.empty?\n        multiversion_hash = hash.dup\n        next\n      end\n\n      if multiversion_hash[:name] != hash[:name]\n        list << multiversion_hash\n        multiversion_hash = hash.dup\n        next\n      end\n\n      unless multiversion_hash[:ensure].include?(hash[:ensure])\n        multiversion_hash[:ensure].concat(\"#{self::MULTIVERSION_SEPARATOR}#{hash[:ensure]}\")\n      end\n    end\n    list << multiversion_hash if multiversion_hash\n    if list.size == 1\n      return list[0]\n    end\n\n    list\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/rug.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :rug, :parent => :rpm do\n  desc \"Support for suse `rug` package manager.\"\n\n  has_feature :versionable\n\n  commands :rug => \"/usr/bin/rug\"\n  commands :rpm => \"rpm\"\n  confine  'os.name' => [:suse, :sles]\n\n  # Install a package using 'rug'.\n  def install\n    should = @resource.should(:ensure)\n    debug \"Ensuring => #{should}\"\n    wanted = @resource[:name]\n\n    # XXX: We don't actually deal with epochs here.\n    case should\n    when true, false, Symbol\n      # pass\n    else\n      # Add the package version\n      wanted += \"-#{should}\"\n    end\n    rug \"--quiet\", :install, \"-y\", wanted\n\n    unless query\n      raise Puppet::ExecutionFailure, _(\"Could not find package %{name}\") % { name: name }\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    # rug can only get a list of *all* available packages?\n    output = rug \"list-updates\"\n\n    if output =~ /#{Regexp.escape @resource[:name]}\\s*\\|\\s*([^\\s|]+)/\n      Regexp.last_match(1)\n    else\n      # rug didn't find updates, pretend the current\n      # version is the latest\n      @property_hash[:ensure]\n    end\n  end\n\n  def update\n    # rug install can be used for update, too\n    install\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/sun.rb",
    "content": "# frozen_string_literal: true\n\n# Sun packaging.\n\nrequire_relative '../../../puppet/provider/package'\n\nPuppet::Type.type(:package).provide :sun, :parent => Puppet::Provider::Package do\n  desc \"Sun's packaging system.  Requires that you specify the source for\n    the packages you're managing.\n\n    This provider supports the `install_options` attribute, which allows command-line flags to be passed to pkgadd.\n    These options should be specified as an array where each element is either a string\n     or a hash.\"\n\n  commands :pkginfo => \"/usr/bin/pkginfo\",\n           :pkgadd => \"/usr/sbin/pkgadd\",\n           :pkgrm => \"/usr/sbin/pkgrm\"\n\n  confine 'os.family' => :solaris\n  defaultfor 'os.family' => :solaris\n\n  has_feature :install_options\n\n  self::Namemap = {\n    \"PKGINST\" => :name,\n    \"CATEGORY\" => :category,\n    \"ARCH\" => :platform,\n    \"VERSION\" => :ensure,\n    \"BASEDIR\" => :root,\n    \"VENDOR\" => :vendor,\n    \"DESC\" => :description,\n  }\n\n  def self.namemap(hash)\n    self::Namemap.keys.inject({}) do |hsh, k|\n      hsh.merge(self::Namemap[k] => hash[k])\n    end\n  end\n\n  def self.parse_pkginfo(out)\n    # collect all the lines with : in them, and separate them out by ^$\n    pkgs = []\n    pkg = {}\n    out.each_line do |line|\n      case line.chomp\n      when /^\\s*$/\n        pkgs << pkg unless pkg.empty?\n        pkg = {}\n      when /^\\s*([^:]+):\\s+(.+)$/\n        pkg[Regexp.last_match(1)] = Regexp.last_match(2)\n      end\n    end\n    pkgs << pkg unless pkg.empty?\n    pkgs\n  end\n\n  def self.instances\n    parse_pkginfo(pkginfo('-l')).collect do |p|\n      hash = namemap(p)\n      hash[:provider] = :sun\n      new(hash)\n    end\n  end\n\n  # Get info on a package, optionally specifying a device.\n  def info2hash(device = nil)\n    args = ['-l']\n    args << '-d' << device if device\n    args << @resource[:name]\n    begin\n      pkgs = self.class.parse_pkginfo(pkginfo(*args))\n      errmsg = case pkgs.size\n               when 0\n                 'No message'\n               when 1\n                 pkgs[0]['ERROR']\n               end\n      return self.class.namemap(pkgs[0]) if errmsg.nil?\n\n      # according to commit 41356a7 some errors do not raise an exception\n      # so even though pkginfo passed, we have to check the actual output\n      raise Puppet::Error, _(\"Unable to get information about package %{name} because of: %{errmsg}\") % { name: @resource[:name], errmsg: errmsg }\n    rescue Puppet::ExecutionFailure\n      { :ensure => :absent }\n    end\n  end\n\n  # Retrieve the version from the current package file.\n  def latest\n    info2hash(@resource[:source])[:ensure]\n  end\n\n  def query\n    info2hash\n  end\n\n  # only looking for -G now\n  def install\n    # TRANSLATORS Sun refers to the company name, do not translate\n    raise Puppet::Error, _(\"Sun packages must specify a package source\") unless @resource[:source]\n\n    options = {\n      :adminfile => @resource[:adminfile],\n      :responsefile => @resource[:responsefile],\n      :source => @resource[:source],\n      :cmd_options => @resource[:install_options]\n    }\n    pkgadd prepare_cmd(options)\n  end\n\n  def uninstall\n    pkgrm prepare_cmd(:adminfile => @resource[:adminfile])\n  end\n\n  # Remove the old package, and install the new one.  This will probably\n  # often fail.\n  def update\n    uninstall if (@property_hash[:ensure] || info2hash[:ensure]) != :absent\n    install\n  end\n\n  def prepare_cmd(opt)\n    [if_have_value('-a', opt[:adminfile]),\n     if_have_value('-r', opt[:responsefile]),\n     if_have_value('-d', opt[:source]),\n     opt[:cmd_options] || [],\n     ['-n', @resource[:name]]].flatten\n  end\n\n  def if_have_value(prefix, value)\n    if value\n      [prefix, value]\n    else\n      []\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/sunfreeware.rb",
    "content": "# frozen_string_literal: true\n\n# At this point, it's an exact copy of the Blastwave stuff.\nPuppet::Type.type(:package).provide :sunfreeware, :parent => :blastwave, :source => :sun do\n  desc \"Package management using sunfreeware.com's `pkg-get` command on Solaris.\n    At this point, support is exactly the same as `blastwave` support and\n    has not actually been tested.\"\n  commands :pkgget => \"pkg-get\"\n\n  confine 'os.family' => :solaris\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/tdnf.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :tdnf, :parent => :dnf do\n  desc \"Support via `tdnf`.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to tdnf.\n  These options should be spcified as a string (e.g. '--flag'), a hash (e.g. {'--flag' => 'value'}), or an\n  array where each element is either a string or a hash.\"\n\n  has_feature :install_options, :versionable, :virtual_packages\n\n  commands :cmd => \"tdnf\", :rpm => \"rpm\"\n\n  # Note: this confine was borrowed from the Yum provider. The\n  # original purpose (from way back in 2007) was to make sure we\n  # never try to use RPM on a machine without it. We think this\n  # has probably become obsolete with the way `commands` work, so\n  # we should investigate removing it at some point.\n  #\n  # Mixing confine statements, control expressions, and exception handling\n  # confuses Rubocop's Layout cops, so we disable them entirely.\n  # rubocop:disable Layout\n  if command('rpm')\n    confine :true => begin\n      rpm('--version')\n      rescue Puppet::ExecutionFailure\n        false\n      else\n        true\n      end\n  end\n  # rubocop:enable Layout\n\n  defaultfor 'os.name' => \"PhotonOS\"\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/up2date.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :up2date, :parent => :rpm, :source => :rpm do\n  desc \"Support for Red Hat's proprietary `up2date` package update\n    mechanism.\"\n\n  commands :up2date => \"/usr/sbin/up2date-nox\"\n\n  defaultfor 'os.family' => :redhat, 'os.distro.release.full' => [\"2.1\", \"3\", \"4\"]\n\n  confine    'os.family' => :redhat\n\n  # Install a package using 'up2date'.\n  def install\n    up2date \"-u\", @resource[:name]\n\n    unless query\n      raise Puppet::ExecutionFailure, _(\"Could not find package %{name}\") % { name: name }\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    # up2date can only get a list of *all* available packages?\n    output = up2date \"--showall\"\n\n    if output =~ /^#{Regexp.escape @resource[:name]}-(\\d+.*)\\.\\w+/\n      Regexp.last_match(1)\n    else\n      # up2date didn't find updates, pretend the current\n      # version is the latest\n      @property_hash[:ensure]\n    end\n  end\n\n  def update\n    # Install in up2date can be used for update, too\n    install\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/urpmi.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do\n  desc \"Support via `urpmi`.\"\n  commands :urpmi => \"urpmi\", :urpmq => \"urpmq\", :rpm => \"rpm\", :urpme => \"urpme\"\n\n  defaultfor 'os.name' => [:mandriva, :mandrake]\n\n  has_feature :versionable\n\n  def install\n    should = @resource.should(:ensure)\n    debug \"Ensuring => #{should}\"\n    wanted = @resource[:name]\n\n    # XXX: We don't actually deal with epochs here.\n    case should\n    when true, false, Symbol\n      # pass\n    else\n      # Add the package version\n      wanted += \"-#{should}\"\n    end\n\n    urpmi \"--auto\", wanted\n\n    unless query\n      raise Puppet::Error, _(\"Package %{name} was not present after trying to install it\") % { name: name }\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    output = urpmq \"-S\", @resource[:name]\n\n    if output =~ /^#{Regexp.escape @resource[:name]}\\s+:\\s+.*\\(\\s+(\\S+)\\s+\\)/\n      Regexp.last_match(1)\n    else\n      # urpmi didn't find updates, pretend the current\n      # version is the latest\n      @resource[:ensure]\n    end\n  end\n\n  def update\n    # Install in urpmi can be used for update, too\n    install\n  end\n\n  # For normal package removal the urpmi provider will delegate to the RPM\n  # provider. If the package to remove has dependencies then uninstalling via\n  # rpm will fail, but `urpme` can be used to remove a package and its\n  # dependencies.\n  def purge\n    urpme '--auto', @resource[:name]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/windows/exe_package.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/provider/package/windows/package'\n\nclass Puppet::Provider::Package::Windows\n  class ExePackage < Puppet::Provider::Package::Windows::Package\n    attr_reader :uninstall_string\n\n    # registry values to load under each product entry in\n    # HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\n    # for this provider\n    REG_VALUE_NAMES = [\n      'DisplayVersion',\n      'UninstallString',\n      'ParentKeyName',\n      'Security Update',\n      'Update Rollup',\n      'Hotfix',\n      'WindowsInstaller'\n    ]\n\n    def self.register(path)\n      Puppet::Type::Package::ProviderWindows.paths ||= []\n      Puppet::Type::Package::ProviderWindows.paths << path\n    end\n\n    # Return an instance of the package from the registry, or nil\n    def self.from_registry(name, values)\n      if valid?(name, values)\n        ExePackage.new(\n          get_display_name(values),\n          values['DisplayVersion'],\n          values['UninstallString']\n        )\n      end\n    end\n\n    # Is this a valid executable package we should manage?\n    def self.valid?(name, values)\n      # See http://community.spiceworks.com/how_to/show/2238\n      displayName = get_display_name(values)\n      !!(displayName && displayName.length > 0 &&\n         values['UninstallString'] &&\n         values['UninstallString'].length > 0 &&\n         values['WindowsInstaller'] != 1 && # DWORD\n         name !~ /^KB[0-9]{6}/ &&\n         values['ParentKeyName'].nil? &&\n         values['Security Update'].nil? &&\n         values['Update Rollup'].nil? &&\n         values['Hotfix'].nil?)\n    end\n\n    def initialize(name, version, uninstall_string)\n      super(name, version)\n\n      @uninstall_string = uninstall_string\n    end\n\n    # Does this package match the resource?\n    def match?(resource)\n      resource[:name] == name\n    end\n\n    def self.install_command(resource)\n      file_location = resource[:source]\n      if file_location.start_with?('http://', 'https://')\n        tempfile = Tempfile.new(['', '.exe'])\n        begin\n          uri = URI(Puppet::Util.uri_encode(file_location))\n          client = Puppet.runtime[:http]\n          client.get(uri, options: { include_system_store: true }) do |response|\n            raise Puppet::HTTP::ResponseError, response unless response.success?\n\n            File.open(tempfile.path, 'wb') do |file|\n              response.read_body do |data|\n                file.write(data)\n              end\n            end\n          end\n        rescue => detail\n          raise Puppet::Error.new(_(\"Error when installing %{package}: %{detail}\") % { package: resource[:name], detail: detail.message }, detail)\n        ensure\n          register(tempfile.path)\n          tempfile.close()\n          file_location = tempfile.path\n        end\n      end\n\n      munge(file_location)\n    end\n\n    def uninstall_command\n      # Only quote bare uninstall strings, e.g.\n      #   C:\\Program Files (x86)\\Notepad++\\uninstall.exe\n      # Don't quote uninstall strings that are already quoted, e.g.\n      #   \"c:\\ruby187\\unins000.exe\"\n      # Don't quote uninstall strings that contain arguments:\n      #   \"C:\\Program Files (x86)\\Git\\unins000.exe\" /SILENT\n      if uninstall_string =~ /\\A[^\"]*.exe\\Z/i\n        command = \"\\\"#{uninstall_string}\\\"\"\n      else\n        command = uninstall_string\n      end\n\n      command\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/windows/msi_package.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/provider/package/windows/package'\n\nclass Puppet::Provider::Package::Windows\n  class MsiPackage < Puppet::Provider::Package::Windows::Package\n    attr_reader :productcode, :packagecode\n\n    # From msi.h\n    INSTALLSTATE_DEFAULT = 5 # product is installed for the current user\n    INSTALLUILEVEL_NONE  = 2 # completely silent installation\n\n    # registry values to load under each product entry in\n    # HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\n    # for this provider\n    REG_VALUE_NAMES = %w[\n      DisplayVersion\n      WindowsInstaller\n    ]\n\n    # Get the COM installer object, it's in a separate method for testing\n    def self.installer\n      # REMIND: when does the COM release happen?\n      WIN32OLE.new(\"WindowsInstaller.Installer\")\n    end\n\n    # Return an instance of the package from the registry, or nil\n    def self.from_registry(name, values)\n      if valid?(name, values)\n        inst = installer\n\n        if inst.ProductState(name) == INSTALLSTATE_DEFAULT\n          MsiPackage.new(get_display_name(values),\n                         values['DisplayVersion'],\n                         name, # productcode\n                         inst.ProductInfo(name, 'PackageCode'))\n        end\n      end\n    end\n\n    # Is this a valid MSI package we should manage?\n    def self.valid?(name, values)\n      # See http://community.spiceworks.com/how_to/show/2238\n      displayName = get_display_name(values)\n      !!(displayName && displayName.length > 0 &&\n         values['WindowsInstaller'] == 1 && # DWORD\n         name =~ /\\A\\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\}\\Z/i)\n    end\n\n    def initialize(name, version, productcode, packagecode)\n      super(name, version)\n\n      @productcode = productcode\n      @packagecode = packagecode\n    end\n\n    # Does this package match the resource?\n    def match?(resource)\n      resource[:name].casecmp(packagecode) == 0 ||\n        resource[:name].casecmp(productcode) == 0 ||\n        resource[:name] == name\n    end\n\n    def self.install_command(resource)\n      ['msiexec.exe', '/qn', '/norestart', '/i', munge(resource[:source])]\n    end\n\n    def uninstall_command\n      ['msiexec.exe', '/qn', '/norestart', '/x', productcode]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/windows/package.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/provider/package'\nrequire_relative '../../../../puppet/util/windows'\n\nclass Puppet::Provider::Package::Windows\n  class Package\n    extend Enumerable\n    extend Puppet::Util::Errors\n\n    include Puppet::Util::Windows::Registry\n    extend Puppet::Util::Windows::Registry\n\n    attr_reader :name, :version\n\n    REG_DISPLAY_VALUE_NAMES = %w[DisplayName QuietDisplayName]\n\n    def self.reg_value_names_to_load\n      REG_DISPLAY_VALUE_NAMES |\n        MsiPackage::REG_VALUE_NAMES |\n        ExePackage::REG_VALUE_NAMES\n    end\n\n    # Enumerate each package. The appropriate package subclass\n    # will be yielded.\n    def self.each(&block)\n      with_key do |key, values|\n        name = key.name.match(/^.+\\\\([^\\\\]+)$/).captures[0]\n\n        [MsiPackage, ExePackage].find do |klass|\n          pkg = klass.from_registry(name, values)\n          if pkg\n            yield pkg\n          end\n        end\n      end\n    end\n\n    # Yield each registry key and its values associated with an\n    # installed package. This searches both per-machine and current\n    # user contexts, as well as packages associated with 64 and\n    # 32-bit installers.\n    def self.with_key(&block)\n      %w[HKEY_LOCAL_MACHINE HKEY_CURRENT_USER].each do |hive|\n        [KEY64, KEY32].each do |mode|\n          mode |= KEY_READ\n          begin\n            self.open(hive, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall', mode) do |uninstall|\n              each_key(uninstall) do |name, _wtime|\n                self.open(hive, \"#{uninstall.keyname}\\\\#{name}\", mode) do |key|\n                  yield key, values_by_name(key, reg_value_names_to_load)\n                end\n              end\n            end\n          rescue Puppet::Util::Windows::Error => e\n            raise e unless e.code == Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND\n          end\n        end\n      end\n    end\n\n    # Get the class that knows how to install this resource\n    def self.installer_class(resource)\n      fail(_(\"The source parameter is required when using the Windows provider.\")) unless resource[:source]\n\n      case resource[:source]\n      when /\\.msi\"?\\Z/i\n        # REMIND: can we install from URL?\n        # REMIND: what about msp, etc\n        MsiPackage\n      when /\\.exe\"?\\Z/i\n        fail(_(\"The source does not exist: '%{source}'\") % { source: resource[:source] }) unless\n          Puppet::FileSystem.exist?(resource[:source]) || resource[:source].start_with?('http://', 'https://')\n\n        ExePackage\n      else\n        fail(_(\"Don't know how to install '%{source}'\") % { source: resource[:source] })\n      end\n    end\n\n    def self.munge(value)\n      quote(replace_forward_slashes(value))\n    end\n\n    def self.replace_forward_slashes(value)\n      if value.include?('/')\n        value = value.tr('/', \"\\\\\")\n        Puppet.debug('Package source parameter contained /s - replaced with \\\\s')\n      end\n      value\n    end\n\n    def self.quote(value)\n      value.include?(' ') ? %Q(\"#{value.gsub(/\"/, '\\\"')}\") : value\n    end\n\n    def self.get_display_name(values)\n      return if values.nil?\n      return values['DisplayName'] if values['DisplayName'] && values['DisplayName'].length > 0\n      return values['QuietDisplayName'] if values['QuietDisplayName'] && values['QuietDisplayName'].length > 0\n\n      ''\n    end\n\n    def initialize(name, version)\n      @name = name\n      @version = version\n    end\n  end\nend\n\nrequire_relative '../../../../puppet/provider/package/windows/msi_package'\nrequire_relative '../../../../puppet/provider/package/windows/exe_package'\n"
  },
  {
    "path": "lib/puppet/provider/package/windows.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/package'\nrequire_relative '../../../puppet/util/windows'\nrequire_relative 'windows/package'\n\nPuppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Package) do\n  desc \"Windows package management.\n\n    This provider supports either MSI or self-extracting executable installers.\n\n    This provider requires a `source` attribute when installing the package.\n    It accepts paths to local files, mapped drives, or UNC paths.\n\n    This provider supports the `install_options` and `uninstall_options`\n    attributes, which allow command-line flags to be passed to the installer.\n    These options should be specified as an array where each element is either\n    a string or a hash.\n\n    If the executable requires special arguments to perform a silent install or\n    uninstall, then the appropriate arguments should be specified using the\n    `install_options` or `uninstall_options` attributes, respectively.  Puppet\n    will automatically quote any option that contains spaces.\"\n\n  confine    'os.name' => :windows\n  defaultfor 'os.name' => :windows\n\n  has_feature :installable\n  has_feature :uninstallable\n  has_feature :install_options\n  has_feature :uninstall_options\n  has_feature :versionable\n\n  attr_accessor :package\n\n  class << self\n    attr_accessor :paths\n  end\n\n  def self.post_resource_eval\n    @paths.each do |path|\n      Puppet::FileSystem.unlink(path)\n    rescue => detail\n      raise Puppet::Error.new(_(\"Error when unlinking %{path}: %{detail}\") % { path: path, detail: detail.message }, detail)\n    end if @paths\n  end\n\n  # Return an array of provider instances\n  def self.instances\n    Puppet::Provider::Package::Windows::Package.map do |pkg|\n      provider = new(to_hash(pkg))\n      provider.package = pkg\n      provider\n    end\n  end\n\n  def self.to_hash(pkg)\n    {\n      :name => pkg.name,\n      :ensure => pkg.version || :installed,\n      :provider => :windows\n    }\n  end\n\n  # Query for the provider hash for the current resource. The provider we\n  # are querying, may not have existed during prefetch\n  def query\n    Puppet::Provider::Package::Windows::Package.find do |pkg|\n      if pkg.match?(resource)\n        return self.class.to_hash(pkg)\n      end\n    end\n    nil\n  end\n\n  def install\n    installer = Puppet::Provider::Package::Windows::Package.installer_class(resource)\n\n    command = [installer.install_command(resource), install_options].flatten.compact.join(' ')\n    working_dir = File.dirname(resource[:source])\n    unless Puppet::FileSystem.exist?(working_dir)\n      working_dir = nil\n    end\n    output = execute(command, :failonfail => false, :combine => true, :cwd => working_dir, :suppress_window => true)\n\n    check_result(output.exitstatus)\n  end\n\n  def uninstall\n    command = [package.uninstall_command, uninstall_options].flatten.compact.join(' ')\n    output = execute(command, :failonfail => false, :combine => true, :suppress_window => true)\n\n    check_result(output.exitstatus)\n  end\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa368542(v=vs.85).aspx\n  self::ERROR_SUCCESS                  = 0\n  self::ERROR_SUCCESS_REBOOT_INITIATED = 1641\n  self::ERROR_SUCCESS_REBOOT_REQUIRED  = 3010\n\n  # (Un)install may \"fail\" because the package requested a reboot, the system requested a\n  # reboot, or something else entirely. Reboot requests mean the package was installed\n  # successfully, but we warn since we don't have a good reboot strategy.\n  def check_result(hr)\n    operation = resource[:ensure] == :absent ? 'uninstall' : 'install'\n\n    case hr\n    when self.class::ERROR_SUCCESS\n      # yeah\n    when self.class::ERROR_SUCCESS_REBOOT_INITIATED\n      warning(_(\"The package %{operation}ed successfully and the system is rebooting now.\") % { operation: operation })\n    when self.class::ERROR_SUCCESS_REBOOT_REQUIRED\n      warning(_(\"The package %{operation}ed successfully, but the system must be rebooted.\") % { operation: operation })\n    else\n      raise Puppet::Util::Windows::Error.new(_(\"Failed to %{operation}\") % { operation: operation }, hr)\n    end\n  end\n\n  # This only gets called if there is a value to validate, but not if it's absent\n  def validate_source(value)\n    fail(_(\"The source parameter cannot be empty when using the Windows provider.\")) if value.empty?\n  end\n\n  def install_options\n    join_options(resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(resource[:uninstall_options])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/xbps.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../../puppet/provider/package\"\n\nPuppet::Type.type(:package).provide :xbps, :parent => Puppet::Provider::Package do\n  desc \"Support for the Package Manager Utility (xbps) used in VoidLinux.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to xbps-install.\n  These options should be specified as an array where each element is either a string or a hash.\"\n\n  commands :xbps_install => \"/usr/bin/xbps-install\"\n  commands :xbps_remove => \"/usr/bin/xbps-remove\"\n  commands :xbps_query => \"/usr/bin/xbps-query\"\n  commands :xbps_pkgdb => \"/usr/bin/xbps-pkgdb\"\n\n  confine 'os.name' => :void\n  defaultfor 'os.name' => :void\n  has_feature :install_options, :uninstall_options, :upgradeable, :holdable, :virtual_packages\n\n  def self.defaultto_allow_virtual\n    false\n  end\n\n  # Fetch the list of packages that are currently installed on the system.\n  def self.instances\n    packages = []\n    execpipe([command(:xbps_query), \"-l\"]) do |pipe|\n      # xbps-query -l output is 'ii package-name-version     desc'\n      regex = /^\\S+\\s(\\S+)-(\\S+)\\s+\\S+/\n      pipe.each_line do |line|\n        match = regex.match(line.chomp)\n        if match\n          packages << new({ name: match.captures[0], ensure: match.captures[1], provider: name })\n        else\n          warning(_(\"Failed to match line '%{line}'\") % { line: line })\n        end\n      end\n    end\n\n    packages\n  rescue Puppet::ExecutionFailure\n    fail(_(\"Error getting installed packages\"))\n  end\n\n  # Install a package quietly (without confirmation or progress bar) using 'xbps-install'.\n  def install\n    resource_name = @resource[:name]\n    resource_source = @resource[:source]\n\n    cmd = %w[-S -y]\n    cmd += install_options if @resource[:install_options]\n    cmd << \"--repository=#{resource_source}\" if resource_source\n    cmd << resource_name\n\n    unhold if properties[:mark] == :hold\n    begin\n      xbps_install(*cmd)\n    ensure\n      hold if @resource[:mark] == :hold\n    end\n  end\n\n  # Because Voidlinux is a rolling release based distro, installing a package\n  # should always result in the newest release.\n  def update\n    install\n  end\n\n  # Removes a package from the system.\n  def uninstall\n    resource_name = @resource[:name]\n\n    cmd = %w[-R -y]\n    cmd += uninstall_options if @resource[:uninstall_options]\n    cmd << resource_name\n\n    xbps_remove(*cmd)\n  end\n\n  # The latest version of a given package\n  def latest\n    query&.[] :ensure\n  end\n\n  # Queries information for a package\n  def query\n    resource_name = @resource[:name]\n    installed_packages = self.class.instances\n\n    installed_packages.each do |pkg|\n      return pkg.properties if @resource[:name].casecmp(pkg.name).zero?\n    end\n\n    return nil unless @resource.allow_virtual?\n\n    # Search for virtual package\n    output = xbps_query(\"-Rs\", resource_name).chomp\n\n    # xbps-query -Rs output is '[*] package-name-version     description'\n    regex = /^\\[\\*\\]+\\s(\\S+)-(\\S+)\\s+\\S+/\n    match = regex.match(output)\n\n    return nil unless match\n\n    { name: match.captures[0], ensure: match.captures[1], provider: self.class.name }\n  end\n\n  # Puts a package on hold, so it doesn't update by itself on system update\n  def hold\n    xbps_pkgdb(\"-m\", \"hold\", @resource[:name])\n  end\n\n  # Puts a package out of hold\n  def unhold\n    xbps_pkgdb(\"-m\", \"unhold\", @resource[:name])\n  end\n\n  private\n\n  def install_options\n    join_options(@resource[:install_options])\n  end\n\n  def uninstall_options\n    join_options(@resource[:uninstall_options])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/yum.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/package/version/range'\nrequire_relative '../../../puppet/util/package/version/rpm'\nrequire_relative '../../../puppet/util/rpm_compare'\n\nPuppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do\n  # provides Rpm parsing and comparison\n  include Puppet::Util::RpmCompare\n\n  desc \"Support via `yum`.\n\n  Using this provider's `uninstallable` feature will not remove dependent packages. To\n  remove dependent packages with this provider use the `purgeable` feature, but note this\n  feature is destructive and should be used with the utmost care.\n\n  This provider supports the `install_options` attribute, which allows command-line flags to be passed to yum.\n  These options should be specified as an array where each element is either a string or a hash.\"\n\n  has_feature :install_options, :versionable, :virtual_packages, :install_only, :version_ranges\n\n  RPM_VERSION       = Puppet::Util::Package::Version::Rpm\n  RPM_VERSION_RANGE = Puppet::Util::Package::Version::Range\n\n  commands :cmd => \"yum\", :rpm => \"rpm\"\n\n  # Mixing confine statements, control expressions, and exception handling\n  # confuses Rubocop's Layout cops, so we disable them entirely.\n  # rubocop:disable Layout\n  if command('rpm')\n    confine :true => begin\n      rpm('--version')\n      rescue Puppet::ExecutionFailure\n        false\n      else\n        true\n      end\n  end\n  # rubocop:enable Layout\n\n  defaultfor 'os.name' => :amazon\n  defaultfor 'os.family' => :redhat, 'os.release.major' => (4..7).to_a\n\n  def insync?(is)\n    return false if [:purged, :absent].include?(is)\n    return false if is.include?(self.class::MULTIVERSION_SEPARATOR) && !@resource[:install_only]\n\n    should = @resource[:ensure]\n    if should.is_a?(String)\n      begin\n        should_version = RPM_VERSION_RANGE.parse(should, RPM_VERSION)\n\n        if should_version.is_a?(RPM_VERSION_RANGE::Eq)\n          return super\n        end\n      rescue RPM_VERSION_RANGE::ValidationFailure, RPM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a RPM version range\")\n        return super\n      end\n\n      is.split(self.class::MULTIVERSION_SEPARATOR).any? do |version|\n        is_version = RPM_VERSION.parse(version)\n        should_version.include?(is_version)\n      rescue RPM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{is} as a RPM version\")\n      end\n    end\n  end\n\n  VERSION_REGEX = /^(?:(\\d+):)?(\\S+)-(\\S+)$/\n\n  def self.prefetch(packages)\n    raise Puppet::Error, _(\"The yum provider can only be used as root\") if Process.euid != 0\n\n    super\n  end\n\n  # Retrieve the latest package version information for a given package name\n  # and combination of repos to enable and disable.\n  #\n  # @note If multiple package versions are defined (such as in the case where a\n  #   package is built for multiple architectures), the first package found\n  #   will be used.\n  #\n  # @api private\n  # @param package [String] The name of the package to query\n  # @param disablerepo [Array<String>] A list of repositories to disable for this query\n  # @param enablerepo [Array<String>] A list of repositories to enable for this query\n  # @param disableexcludes [Array<String>] A list of repository excludes to disable for this query\n  # @return [Hash<Symbol, String>]\n  def self.latest_package_version(package, disablerepo, enablerepo, disableexcludes)\n    key = [disablerepo, enablerepo, disableexcludes]\n\n    @latest_versions ||= {}\n    if @latest_versions[key].nil?\n      @latest_versions[key] = check_updates(disablerepo, enablerepo, disableexcludes)\n    end\n\n    if @latest_versions[key][package]\n      @latest_versions[key][package].first\n    end\n  end\n\n  # Search for all installed packages that have newer versions, given a\n  # combination of repositories to enable and disable.\n  #\n  # @api private\n  # @param disablerepo [Array<String>] A list of repositories to disable for this query\n  # @param enablerepo [Array<String>] A list of repositories to enable for this query\n  # @param disableexcludes [Array<String>] A list of repository excludes to disable for this query\n  # @return [Hash<String, Array<Hash<String, String>>>] All packages that were\n  #   found with a list of found versions for each package.\n  # rubocop:disable Layout/SingleLineBlockChain\n  def self.check_updates(disablerepo, enablerepo, disableexcludes)\n    args = [command(:cmd), 'check-update']\n    args.concat(disablerepo.map { |repo| [\"--disablerepo=#{repo}\"] }.flatten)\n    args.concat(enablerepo.map { |repo| [\"--enablerepo=#{repo}\"] }.flatten)\n    args.concat(disableexcludes.map { |repo| [\"--disableexcludes=#{repo}\"] }.flatten)\n\n    output = Puppet::Util::Execution.execute(args, :failonfail => false, :combine => false)\n\n    updates = {}\n    case output.exitstatus\n    when 100\n      updates = parse_updates(output)\n    when 0\n      debug \"#{command(:cmd)} check-update exited with 0; no package updates available.\"\n    else\n      warning _(\"Could not check for updates, '%{cmd} check-update' exited with %{status}\") % { cmd: command(:cmd), status: output.exitstatus }\n    end\n    updates\n  end\n  # rubocop:enable Layout/SingleLineBlockChain\n\n  def self.parse_updates(str)\n    # Strip off all content that contains Obsoleting, Security: or Update\n    body = str.partition(/^(Obsoleting|Security:|Update)/).first\n\n    updates = Hash.new { |h, k| h[k] = [] }\n\n    body.split(/^\\s*\\n/).each do |line|\n      line.split.each_slice(3) do |tuple|\n        next unless tuple[0].include?('.') && tuple[1] =~ VERSION_REGEX\n\n        hash = update_to_hash(*tuple[0..1])\n        # Create entries for both the package name without a version and a\n        # version since yum considers those as mostly interchangeable.\n        short_name = hash[:name]\n        long_name  = \"#{hash[:name]}.#{hash[:arch]}\"\n        updates[short_name] << hash\n        updates[long_name] << hash\n      end\n    end\n    updates\n  end\n\n  def self.update_to_hash(pkgname, pkgversion)\n    # The pkgname string has two parts: name, and architecture. Architecture\n    # is the portion of the string following the last \".\" character. All\n    # characters preceding the final dot are the package name. Parse out\n    # these two pieces of component data.\n    name, _, arch = pkgname.rpartition('.')\n    if name.empty?\n      raise _(\"Failed to parse package name and architecture from '%{pkgname}'\") % { pkgname: pkgname }\n    end\n\n    match = pkgversion.match(VERSION_REGEX)\n    epoch = match[1] || '0'\n    version = match[2]\n    release = match[3]\n\n    {\n      :name => name,\n      :epoch => epoch,\n      :version => version,\n      :release => release,\n      :arch => arch,\n    }\n  end\n\n  def self.clear\n    @latest_versions = nil\n  end\n\n  def self.error_level\n    '0'\n  end\n\n  def self.update_command\n    # In yum both `upgrade` and `update` can be used to update packages\n    # `yum upgrade` == `yum --obsoletes update`\n    # Quote the DNF docs:\n    # \"Yum does this if its obsoletes config option is enabled but\n    # the behavior is not properly documented and can be harmful.\"\n    # So we'll stick with the safer option\n    # If a user wants to remove obsoletes, they can use { :install_options => '--obsoletes' }\n    # More detail here: https://bugzilla.redhat.com/show_bug.cgi?id=1096506\n    'update'\n  end\n\n  def best_version(should)\n    if should.is_a?(String)\n      begin\n        should_range = RPM_VERSION_RANGE.parse(should, RPM_VERSION)\n        if should_range.is_a?(RPM_VERSION_RANGE::Eq)\n          return should\n        end\n      rescue RPM_VERSION_RANGE::ValidationFailure, RPM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a RPM version range\")\n        return should\n      end\n      versions = []\n      available_versions(@resource[:name], disablerepo, enablerepo, disableexcludes).each do |version|\n        rpm_version = RPM_VERSION.parse(version)\n        versions << rpm_version if should_range.include?(rpm_version)\n      rescue RPM_VERSION::ValidationFailure\n        Puppet.debug(\"Cannot parse #{version} as a RPM version\")\n      end\n\n      version = versions.max if versions.any?\n\n      if version\n        version = version.to_s.sub(/^\\d+:/, '')\n        return version\n      end\n\n      Puppet.debug(\"No available version for package #{@resource[:name]} is included in range #{should_range}\")\n      should\n    end\n  end\n\n  # rubocop:disable Layout/SingleLineBlockChain\n  def available_versions(package_name, disablerepo, enablerepo, disableexcludes)\n    args = [command(:cmd), 'list', package_name, '--showduplicates']\n    args.concat(disablerepo.map { |repo| [\"--disablerepo=#{repo}\"] }.flatten)\n    args.concat(enablerepo.map { |repo| [\"--enablerepo=#{repo}\"] }.flatten)\n    args.concat(disableexcludes.map { |repo| [\"--disableexcludes=#{repo}\"] }.flatten)\n\n    output = execute(\"#{args.compact.join(' ')} | sed -e '1,/Available Packages/ d' | awk '{print $2}'\")\n    output.split(\"\\n\")\n  end\n  # rubocop:enable Layout/SingleLineBlockChain\n\n  def install\n    wanted = @resource[:name]\n    error_level = self.class.error_level\n    update_command = self.class.update_command\n    # If not allowing virtual packages, do a query to ensure a real package exists\n    unless @resource.allow_virtual?\n      execute([command(:cmd), '-d', '0', '-e', error_level, '-y', install_options, :list, wanted].compact)\n    end\n\n    should = @resource.should(:ensure)\n    debug \"Ensuring => #{should}\"\n    operation = :install\n\n    case should\n    when :latest\n      current_package = query\n      if current_package && !current_package[:ensure].to_s.empty?\n        operation = update_command\n        debug \"Ensuring latest, so using #{operation}\"\n      else\n        debug \"Ensuring latest, but package is absent, so using install\"\n        operation = :install\n      end\n      should = nil\n    when true, :present, :installed\n      # if we have been given a source and we were not asked for a specific\n      # version feed it to yum directly\n      if @resource[:source]\n        wanted = @resource[:source]\n        debug \"Installing directly from #{wanted}\"\n      end\n      should = nil\n    when false, :absent\n      # pass\n      should = nil\n    else\n      if @resource[:source]\n        # An explicit source was supplied, which means we're ensuring a specific\n        # version, and also supplying the path to a package that supplies that\n        # version.\n        wanted = @resource[:source]\n        debug \"Installing directly from #{wanted}\"\n      else\n        # No explicit source was specified, so add the package version\n        should = best_version(should)\n        wanted += \"-#{should}\"\n        if wanted.scan(self.class::ARCH_REGEX)\n          debug \"Detected Arch argument in package! - Moving arch to end of version string\"\n          wanted.gsub!(/(.+)(#{self.class::ARCH_REGEX})(.+)/, '\\1\\3\\2')\n        end\n      end\n      current_package = query\n      if current_package\n        if @resource[:install_only]\n          debug \"Updating package #{@resource[:name]} from version #{current_package[:ensure]} to #{should} as install_only packages are never downgraded\"\n          operation = update_command\n        elsif rpm_compare_evr(should, current_package[:ensure]) < 0\n          debug \"Downgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}\"\n          operation = :downgrade\n        elsif rpm_compare_evr(should, current_package[:ensure]) > 0\n          debug \"Upgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}\"\n          operation = update_command\n        end\n      end\n    end\n\n    # Yum on el-4 and el-5 returns exit status 0 when trying to install a package it doesn't recognize;\n    # ensure we capture output to check for errors.\n    no_debug = Puppet.runtime[:facter].value('os.release.major').to_i > 5 ? [\"-d\", \"0\"] : []\n    command = [command(:cmd)] + no_debug + [\"-e\", error_level, \"-y\", install_options, operation, wanted].compact\n    output = execute(command)\n\n    if output.to_s =~ /^No package #{wanted} available\\.$/\n      raise Puppet::Error, _(\"Could not find package %{wanted}\") % { wanted: wanted }\n    end\n\n    # If a version was specified, query again to see if it is a matching version\n    if should\n      is = query\n      raise Puppet::Error, _(\"Could not find package %{name}\") % { name: name } unless is\n\n      version = is[:ensure]\n      # FIXME: Should we raise an exception even if should == :latest\n      # and yum updated us to a version other than @param_hash[:ensure] ?\n      raise Puppet::Error, _(\"Failed to update to version %{should}, got version %{version} instead\") % { should: should, version: version } unless\n        insync?(version)\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    upd = self.class.latest_package_version(@resource[:name], disablerepo, enablerepo, disableexcludes)\n    if upd.nil?\n      # Yum didn't find updates, pretend the current version is the latest\n      debug \"Yum didn't find updates, current version (#{properties[:ensure]}) is the latest\"\n      version = properties[:ensure]\n      raise Puppet::DevError, _(\"Tried to get latest on a missing package\") if version == :absent || version == :purged\n\n      version\n    else\n      # FIXME: there could be more than one update for a package\n      # because of multiarch\n      \"#{upd[:epoch]}:#{upd[:version]}-#{upd[:release]}\"\n    end\n  end\n\n  def update\n    # Install in yum can be used for update, too\n    install\n  end\n\n  def purge\n    execute([command(:cmd), \"-y\", :erase, @resource[:name]])\n  end\n\n  private\n\n  def enablerepo\n    scan_options(resource[:install_options], '--enablerepo')\n  end\n\n  def disablerepo\n    scan_options(resource[:install_options], '--disablerepo')\n  end\n\n  def disableexcludes\n    scan_options(resource[:install_options], '--disableexcludes')\n  end\n\n  # Scan a structure that looks like the package type 'install_options'\n  # structure for all hashes that have a specific key.\n  #\n  # @api private\n  # @param options [Array<String | Hash>, nil] The options structure. If the\n  #   options are nil an empty array will be returned.\n  # @param key [String] The key to look for in all contained hashes\n  # @return [Array<String>] All hash values with the given key.\n  def scan_options(options, key)\n    return [] unless options.is_a?(Enumerable)\n\n    values =\n      options.map do |repo|\n        value =\n          if repo.is_a?(String)\n            next unless repo.include?('=')\n\n            Hash[*repo.strip.split('=')] # make it a hash\n          else\n            repo\n          end\n        value[key]\n      end\n    values.compact.uniq\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package/zypper.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:package).provide :zypper, :parent => :rpm, :source => :rpm do\n  desc \"Support for SuSE `zypper` package manager. Found in SLES10sp2+ and SLES11.\n\n    This provider supports the `install_options` attribute, which allows command-line flags to be passed to zypper.\n    These options should be specified as an array where each element is either a\n    string or a hash.\"\n\n  has_feature :versionable, :install_options, :virtual_packages\n\n  commands :zypper => \"/usr/bin/zypper\"\n\n  defaultfor 'os.name' => [:suse, :sles, :sled, :opensuse]\n  confine    'os.name' => [:suse, :sles, :sled, :opensuse]\n\n  # @api private\n  # Reset the latest version hash to nil\n  # needed for spec tests to clear cached value\n  def self.reset!\n    @latest_versions = nil\n  end\n\n  def self.latest_package_version(package)\n    if @latest_versions.nil?\n      @latest_versions = list_updates\n    end\n\n    @latest_versions[package]\n  end\n\n  def self.list_updates\n    output = zypper 'list-updates'\n\n    avail_updates = {}\n\n    # split up columns\n    output.lines.each do |line|\n      pkg_ver = line.split(/\\s*\\|\\s*/)\n      # ignore zypper headers\n      next unless pkg_ver[0] == 'v'\n\n      avail_updates[pkg_ver[2]] = pkg_ver[4]\n    end\n\n    avail_updates\n  end\n\n  # on zypper versions <1.0, the version option returns 1\n  # some versions of zypper output on stderr\n  def zypper_version\n    cmd = [self.class.command(:zypper), \"--version\"]\n    execute(cmd, { :failonfail => false, :combine => true })\n  end\n\n  def best_version(should)\n    if should.is_a?(String)\n      begin\n        should_range = Puppet::Util::Package::Version::Range.parse(should, Puppet::Util::Package::Version::Rpm)\n      rescue Puppet::Util::Package::Version::Range::ValidationFailure, Puppet::Util::Package::Version::Rpm::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a RPM version range\")\n        return should\n      end\n\n      if should_range.is_a?(Puppet::Util::Package::Version::Range::Eq)\n        return should\n      end\n\n      versions = []\n\n      output = zypper('search', '--match-exact', '--type', 'package', '--uninstalled-only', '-s', @resource[:name])\n      output.lines.each do |line|\n        pkg_ver = line.split(/\\s*\\|\\s*/)\n        next unless pkg_ver[1] == @resource[:name]\n\n        begin\n          rpm_version = Puppet::Util::Package::Version::Rpm.parse(pkg_ver[3])\n\n          versions << rpm_version if should_range.include?(rpm_version)\n        rescue Puppet::Util::Package::Version::Rpm::ValidationFailure\n          Puppet.debug(\"Cannot parse #{pkg_ver[3]} as a RPM version\")\n        end\n      end\n\n      return versions.max if versions.any?\n\n      Puppet.debug(\"No available version for package #{@resource[:name]} is included in range #{should_range}\")\n      should\n    end\n  end\n\n  # Install a package using 'zypper'.\n  def install\n    should = @resource.should(:ensure)\n    debug \"Ensuring => #{should}\"\n    wanted = @resource[:name]\n\n    # XXX: We don't actually deal with epochs here.\n    case should\n    when true, false, Symbol\n      should = nil\n    else\n      # Add the package version\n      should = best_version(should)\n      wanted = \"#{wanted}-#{should}\"\n    end\n\n    # This has been tested with following zypper versions\n    # SLE 10.4: 0.6.201\n    # SLE 11.3: 1.6.307\n    # SLE 12.0: 1.11.14\n    # Assume that this will work on newer zypper versions\n\n    # extract version numbers and convert to integers\n    major, minor, patch = zypper_version.scan(/\\d+/).map(&:to_i)\n    debug \"Detected zypper version #{major}.#{minor}.#{patch}\"\n\n    # zypper version < 1.0 does not support --quiet flag\n    if major < 1\n      quiet = '--terse'\n    else\n      quiet = '--quiet'\n    end\n\n    inst_opts = []\n    inst_opts = install_options if resource[:install_options]\n\n    options = []\n    options << quiet\n    options << '--no-gpg-check' unless inst_opts.delete('--no-gpg-check').nil?\n    options << '--no-gpg-checks' unless inst_opts.delete('--no-gpg-checks').nil?\n    options << :install\n\n    # zypper 0.6.13 (OpenSuSE 10.2) does not support auto agree with licenses\n    options << '--auto-agree-with-licenses' unless major < 1 and minor <= 6 and patch <= 13\n    options << '--no-confirm'\n    options += inst_opts unless inst_opts.empty?\n\n    # Zypper 0.6.201 doesn't recognize '--name'\n    # It is unclear where this functionality was introduced, but it\n    # is present as early as 1.0.13\n    options << '--name' unless major < 1 || @resource.allow_virtual? || should\n    options << wanted\n\n    zypper(*options)\n\n    unless query\n      raise Puppet::ExecutionFailure, _(\"Could not find package %{name}\") % { name: name }\n    end\n  end\n\n  # What's the latest package version available?\n  def latest\n    self.class.latest_package_version(@resource[:name]) ||\n      # zypper didn't find updates, pretend the current\n      # version is the latest\n      @property_hash[:ensure]\n  end\n\n  def update\n    # zypper install can be used for update, too\n    install\n  end\n\n  def uninstall\n    # extract version numbers and convert to integers\n    major, minor, _ = zypper_version.scan(/\\d+/).map(&:to_i)\n\n    if major < 1\n      super\n    else\n      options = [:remove, '--no-confirm']\n      if major == 1 && minor < 6\n        options << '--force-resolution'\n      end\n\n      options << @resource[:name]\n\n      zypper(*options)\n    end\n  end\n\n  def insync?(is)\n    return false if [:purged, :absent].include?(is)\n\n    should = @resource[:ensure]\n    if should.is_a?(String)\n      begin\n        should_version = Puppet::Util::Package::Version::Range.parse(should, Puppet::Util::Package::Version::Rpm)\n        if should_version.is_a?(RPM_VERSION_RANGE::Eq)\n          return super\n        end\n      rescue Puppet::Util::Package::Version::Range::ValidationFailure, Puppet::Util::Package::Version::Rpm::ValidationFailure\n        Puppet.debug(\"Cannot parse #{should} as a RPM version range\")\n        return super\n      end\n\n      begin\n        is_version = Puppet::Util::Package::Version::Rpm.parse(is)\n        should_version.include?(is_version)\n      rescue Puppet::Util::Package::Version::Rpm::ValidationFailure\n        Puppet.debug(\"Cannot parse #{is} as a RPM version\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/provider'\n\nclass Puppet::Provider::Package < Puppet::Provider\n  # Prefetch our package list, yo.\n  def self.prefetch(packages)\n    instances.each do |prov|\n      pkg = packages[prov.name]\n      if pkg\n        pkg.provider = prov\n      end\n    end\n  end\n\n  # Clear out the cached values.\n  def flush\n    @property_hash.clear\n  end\n\n  # Look up the current status.\n  def properties\n    if @property_hash.empty?\n      # For providers that support purging, default to purged; otherwise default to absent\n      # Purged is the \"most uninstalled\" a package can be, so a purged package will be in-sync with\n      # either `ensure => absent` or `ensure => purged`; an absent package will be out of sync with `ensure => purged`.\n      default_status = self.class.feature?(:purgeable) ? :purged : :absent\n      @property_hash = query || { :ensure => default_status }\n      @property_hash[:ensure] = default_status if @property_hash.empty?\n    end\n    @property_hash.dup\n  end\n\n  def validate_source(value)\n    true\n  end\n\n  # Turns a array of options into flags to be passed to a command.\n  # The options can be passed as a string or hash. Note that passing a hash\n  # should only be used in case --foo=bar must be passed,\n  # which can be accomplished with:\n  #     install_options => [ { '--foo' => 'bar' } ]\n  # Regular flags like '--foo' must be passed as a string.\n  # @param options [Array]\n  # @return Concatenated list of options\n  # @api private\n  def join_options(options)\n    return unless options\n\n    options.collect do |val|\n      case val\n      when Hash\n        val.keys.sort.collect do |k|\n          \"#{k}=#{val[k]}\"\n        end\n      else\n        val\n      end\n    end.flatten\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/package_targetable.rb",
    "content": "# frozen_string_literal: true\n\n# Targetable package providers implement a `command` attribute.\n#\n# The `packages` hash passed to `Puppet::Provider::Package::prefetch` is deduplicated,\n# as it is keyed only by name in `Puppet::Transaction::prefetch_if_necessary`.\n#\n# (The `packages` hash passed to ``Puppet::Provider::Package::prefetch`` should be keyed by all namevars,\n# possibly via a `prefetchV2` method that could take a better data structure.)\n#\n# In addition, `Puppet::Provider::Package::properties` calls `query` in the provider.\nrequire_relative '../../puppet/provider/package'\n\n# But `query` in the provider depends upon whether a `command` attribute is defined for the resource.\n# This is a Catch-22.\n#\n# Instead ...\n#\n# Inspect any package to access the catalog (every package includes a reference to the catalog).\n# Inspect the catalog to find all of the `command` attributes for all of the packages of this class.\n# Find all of the package instances using each package `command`, including the default provider command.\n# Assign each instance's `provider` by selecting it from the `packages` hash passed to `prefetch`, based upon `name` and `command`.\n#\n# The original `command` parameter in the catalog is not populated by the default (`:default`) for the parameter in type/package.rb.\n# Rather, the result of the `original_parameters` is `nil` when the `command` parameter is undefined in the catalog.\n\nclass Puppet::Provider::Package::Targetable < Puppet::Provider::Package\n  # Prefetch our package list, yo.\n  def self.prefetch(packages)\n    catalog_packages = packages.values.first.catalog.resources.select { |p| p.provider.instance_of?(self) }\n    package_commands = catalog_packages.map { |catalog_package| catalog_package.original_parameters[:command] }.uniq\n    package_commands.each do |command|\n      instances(command).each do |instance|\n        catalog_packages.each do |catalog_package|\n          if catalog_package[:name] == instance.name && catalog_package.original_parameters[:command] == command\n            catalog_package.provider = instance\n            debug \"Prefetched instance: %{name} via command: %{cmd}\" % { name: instance.name, cmd: command || :default }\n          end\n        end\n      end\n    end\n    package_commands\n  end\n\n  # Returns the resource command or provider command.\n\n  def resource_or_provider_command\n    resource.original_parameters[:command] || self.class.provider_command\n  end\n\n  # Targetable providers use has_command/is_optional to defer validation of provider suitability.\n  # Evaluate provider suitability here and now by validating that the command is defined and exists.\n  #\n  # cmd: the full path to the package command.\n\n  def self.validate_command(cmd)\n    unless cmd\n      raise Puppet::Error, _(\"Provider %{name} package command is not functional on this host\") % { name: name }\n    end\n    unless File.file?(cmd)\n      raise Puppet::Error, _(\"Provider %{name} package command '%{cmd}' does not exist on this host\") % { name: name, cmd: cmd }\n    end\n  end\n\n  # Return information about the package, its provider, and its (optional) command.\n\n  def to_s\n    cmd = resource[:command] || :default\n    \"#{@resource}(provider=#{self.class.name})(command=#{cmd})\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/parsedfile.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/util/filetype'\nrequire_relative '../../puppet/util/fileparsing'\n\n# This provider can be used as the parent class for a provider that\n# parses and generates files.  Its content must be loaded via the\n# 'prefetch' method, and the file will be written when 'flush' is called\n# on the provider instance.  At this point, the file is written once\n# for every provider instance.\n#\n# Once the provider prefetches the data, it's the resource's job to copy\n# that data over to the @is variables.\n#\n# NOTE: The prefetch method swallows FileReadErrors by treating the\n# corresponding target as an empty file. If you would like to turn this\n# behavior off, then set the raise_prefetch_errors class variable to\n# true. Doing so will error all resources associated with the failed\n# target.\nclass Puppet::Provider::ParsedFile < Puppet::Provider\n  extend Puppet::Util::FileParsing\n\n  class << self\n    attr_accessor :default_target, :target, :raise_prefetch_errors\n  end\n\n  attr_accessor :property_hash\n\n  def self.clean(hash)\n    newhash = hash.dup\n    [:record_type, :on_disk].each do |p|\n      newhash.delete(p) if newhash.include?(p)\n    end\n\n    newhash\n  end\n\n  def self.clear\n    @target_objects.clear\n    @records.clear\n  end\n\n  def self.filetype\n    @filetype ||= Puppet::Util::FileType.filetype(:flat)\n  end\n\n  def self.filetype=(type)\n    if type.is_a?(Class)\n      @filetype = type\n    else\n      klass = Puppet::Util::FileType.filetype(type)\n      if klass\n        @filetype = klass\n      else\n        raise ArgumentError, _(\"Invalid filetype %{type}\") % { type: type }\n      end\n    end\n  end\n\n  # Flush all of the targets for which there are modified records.  The only\n  # reason we pass a record here is so that we can add it to the stack if\n  # necessary -- it's passed from the instance calling 'flush'.\n  def self.flush(record)\n    # Make sure this record is on the list to be flushed.\n    unless record[:on_disk]\n      record[:on_disk] = true\n      @records << record\n\n      # If we've just added the record, then make sure our\n      # target will get flushed.\n      modified(record[:target] || default_target)\n    end\n\n    return unless defined?(@modified) and !@modified.empty?\n\n    flushed = []\n    begin\n      @modified.sort_by(&:to_s).uniq.each do |target|\n        Puppet.debug \"Flushing #{@resource_type.name} provider target #{target}\"\n        flushed << target\n        flush_target(target)\n      end\n    ensure\n      @modified.reject! { |t| flushed.include?(t) }\n    end\n  end\n\n  # Make sure our file is backed up, but only back it up once per transaction.\n  # We cheat and rely on the fact that @records is created on each prefetch.\n  def self.backup_target(target)\n    return nil unless target_object(target).respond_to?(:backup)\n\n    @backup_stats ||= {}\n    return nil if @backup_stats[target] == @records.object_id\n\n    target_object(target).backup\n    @backup_stats[target] = @records.object_id\n  end\n\n  # Flush all of the records relating to a specific target.\n  def self.flush_target(target)\n    if @raise_prefetch_errors && @failed_prefetch_targets.key?(target)\n      raise Puppet::Error, _(\"Failed to read %{target}'s records when prefetching them. Reason: %{detail}\") % { target: target, detail: @failed_prefetch_targets[target] }\n    end\n\n    backup_target(target)\n\n    records = target_records(target).reject { |r|\n      r[:ensure] == :absent\n    }\n\n    target_object(target).write(to_file(records))\n  end\n\n  # Return the header placed at the top of each generated file, warning\n  # users that modifying this file manually is probably a bad idea.\n  def self.header\n    %(# HEADER: This file was autogenerated at #{Time.now}\n# HEADER: by puppet.  While it can still be managed manually, it\n# HEADER: is definitely not recommended.\\n)\n  end\n\n  # An optional regular expression matched by third party headers.\n  #\n  # For example, this can be used to filter the vixie cron headers as\n  # erroneously exported by older cron versions.\n  #\n  # @api private\n  # @abstract Providers based on ParsedFile may implement this to make it\n  #   possible to identify a header maintained by a third party tool.\n  #   The provider can then allow that header to remain near the top of the\n  #   written file, or remove it after composing the file content.\n  #   If implemented, the function must return a Regexp object.\n  #   The expression must be tailored to match exactly one third party header.\n  # @see drop_native_header\n  # @note When specifying regular expressions in multiline mode, avoid\n  #   greedy repetitions such as '.*' (use .*? instead). Otherwise, the\n  #   provider may drop file content between sparse headers.\n  def self.native_header_regex\n    nil\n  end\n\n  # How to handle third party headers.\n  # @api private\n  # @abstract Providers based on ParsedFile that make use of the support for\n  #   third party headers may override this method to return +true+.\n  #   When this is done, headers that are matched by the native_header_regex\n  #   are not written back to disk.\n  # @see native_header_regex\n  def self.drop_native_header\n    false\n  end\n\n  # Add another type var.\n  def self.initvars\n    @records = []\n    @target_objects = {}\n\n    # Hash of <target> => <failure reason>.\n    @failed_prefetch_targets = {}\n    @raise_prefetch_errors = false\n\n    @target = nil\n\n    # Default to flat files\n    @filetype ||= Puppet::Util::FileType.filetype(:flat)\n    super\n  end\n\n  # Return a list of all of the records we can find.\n  def self.instances\n    targets.collect do |target|\n      prefetch_target(target)\n    end.flatten.reject { |r| skip_record?(r) }.collect do |record|\n      new(record)\n    end\n  end\n\n  # Override the default method with a lot more functionality.\n  def self.mk_resource_methods\n    [resource_type.validproperties, resource_type.parameters].flatten.each do |attr|\n      attr = attr.intern\n      define_method(attr) do\n        # If it's not a valid field for this record type (which can happen\n        # when different platforms support different fields), then just\n        # return the should value, so the resource shuts up.\n        if @property_hash[attr] or self.class.valid_attr?(self.class.name, attr)\n          @property_hash[attr] || :absent\n        elsif defined?(@resource)\n          @resource.should(attr)\n        else\n          nil\n        end\n      end\n\n      define_method(attr.to_s + \"=\") do |val|\n        mark_target_modified\n        @property_hash[attr] = val\n      end\n    end\n  end\n\n  # Always make the resource methods.\n  def self.resource_type=(resource)\n    super\n    mk_resource_methods\n  end\n\n  # Mark a target as modified so we know to flush it.  This only gets\n  # used within the attr= methods.\n  def self.modified(target)\n    @modified ||= []\n    @modified << target unless @modified.include?(target)\n  end\n\n  # Retrieve all of the data from disk.  There are three ways to know\n  # which files to retrieve:  We might have a list of file objects already\n  # set up, there might be instances of our associated resource and they\n  # will have a path parameter set, and we will have a default path\n  # set.  We need to turn those three locations into a list of files,\n  # prefetch each one, and make sure they're associated with each appropriate\n  # resource instance.\n  def self.prefetch(resources = nil)\n    # Reset the record list.\n    @records = prefetch_all_targets(resources)\n\n    match_providers_with_resources(resources)\n  end\n\n  # Match a list of catalog resources with provider instances\n  #\n  # @api private\n  #\n  # @param [Array<Puppet::Resource>] resources A list of resources using this class as a provider\n  def self.match_providers_with_resources(resources)\n    return unless resources\n\n    matchers = resources.dup\n    @records.each do |record|\n      # Skip things like comments and blank lines\n      next if skip_record?(record)\n\n      if (resource = resource_for_record(record, resources))\n        resource.provider = new(record)\n      elsif respond_to?(:match)\n        resource = match(record, matchers)\n        if resource\n          matchers.delete(resource.title)\n          record[:name] = resource[:name]\n          resource.provider = new(record)\n        end\n      end\n    end\n  end\n\n  # Look up a resource based on a parsed file record\n  #\n  # @api private\n  #\n  # @param [Hash<Symbol, Object>] record\n  # @param [Array<Puppet::Resource>] resources\n  #\n  # @return [Puppet::Resource, nil] The resource if found, else nil\n  def self.resource_for_record(record, resources)\n    name = record[:name]\n    if name\n      resources[name]\n    end\n  end\n\n  def self.prefetch_all_targets(resources)\n    records = []\n    targets(resources).each do |target|\n      records += prefetch_target(target)\n    end\n    records\n  end\n\n  # Prefetch an individual target.\n  def self.prefetch_target(target)\n    begin\n      target_records = retrieve(target)\n      unless target_records\n        raise Puppet::DevError, _(\"Prefetching %{target} for provider %{name} returned nil\") % { target: target, name: name }\n      end\n    rescue Puppet::Util::FileType::FileReadError => detail\n      if @raise_prefetch_errors\n        # We will raise an error later in flush_target. This way,\n        # only the resources linked to our target will fail\n        # evaluation.\n        @failed_prefetch_targets[target] = detail.to_s\n      else\n        puts detail.backtrace if Puppet[:trace]\n        Puppet.err _(\"Could not prefetch %{resource} provider '%{name}' target '%{target}': %{detail}. Treating as empty\") % { resource: resource_type.name, name: name, target: target, detail: detail }\n      end\n\n      target_records = []\n    end\n\n    target_records.each do |r|\n      r[:on_disk] = true\n      r[:target] = target\n      r[:ensure] = :present\n    end\n\n    target_records = prefetch_hook(target_records) if respond_to?(:prefetch_hook)\n\n    raise Puppet::DevError, _(\"Prefetching %{target} for provider %{name} returned nil\") % { target: target, name: name } unless target_records\n\n    target_records\n  end\n\n  # Is there an existing record with this name?\n  def self.record?(name)\n    return nil unless @records\n\n    @records.find { |r| r[:name] == name }\n  end\n\n  # Retrieve the text for the file. Returns nil in the unlikely\n  # event that it doesn't exist.\n  def self.retrieve(path)\n    # XXX We need to be doing something special here in case of failure.\n    text = target_object(path).read\n    if text.nil? or text == \"\"\n      # there is no file\n      []\n    else\n      # Set the target, for logging.\n      old = @target\n      begin\n        @target = path\n        parse(text)\n      rescue Puppet::Error => detail\n        detail.file = @target if detail.respond_to?(:file=)\n        raise detail\n      ensure\n        @target = old\n      end\n    end\n  end\n\n  # Should we skip the record?  Basically, we skip text records.\n  # This is only here so subclasses can override it.\n  def self.skip_record?(record)\n    record_type(record[:record_type]).text?\n  end\n\n  # The mode for generated files if they are newly created.\n  # No mode will be set on existing files.\n  #\n  # @abstract Providers inheriting parsedfile can override this method\n  #   to provide a mode. The value should be suitable for File.chmod\n  def self.default_mode\n    nil\n  end\n\n  # Initialize the object if necessary.\n  def self.target_object(target)\n    # only send the default mode if the actual provider defined it,\n    # because certain filetypes (e.g. the crontab variants) do not\n    # expect it in their initialize method\n    if default_mode\n      @target_objects[target] ||= filetype.new(target, default_mode)\n    else\n      @target_objects[target] ||= filetype.new(target)\n    end\n\n    @target_objects[target]\n  end\n\n  # Find all of the records for a given target\n  def self.target_records(target)\n    @records.find_all { |r| r[:target] == target }\n  end\n\n  # Find a list of all of the targets that we should be reading.  This is\n  # used to figure out what targets we need to prefetch.\n  def self.targets(resources = nil)\n    targets = []\n    # First get the default target\n    raise Puppet::DevError, _(\"Parsed Providers must define a default target\") unless default_target\n\n    targets << default_target\n\n    # Then get each of the file objects\n    targets += @target_objects.keys\n\n    # Lastly, check the file from any resource instances\n    if resources\n      resources.each do |_name, resource|\n        value = resource.should(:target)\n        if value\n          targets << value\n        end\n      end\n    end\n\n    targets.uniq.compact\n  end\n\n  # Compose file contents from the set of records.\n  #\n  # If self.native_header_regex is not nil, possible vendor headers are\n  # identified by matching the return value against the expression.\n  # If one (or several consecutive) such headers, are found, they are\n  # either moved in front of the self.header if self.drop_native_header\n  # is false (this is the default), or removed from the return value otherwise.\n  #\n  # @api private\n  def self.to_file(records)\n    text = super\n    if native_header_regex and (match = text.match(native_header_regex))\n      if drop_native_header\n        # concatenate the text in front of and after the native header\n        text = match.pre_match + match.post_match\n      else\n        native_header = match[0]\n        return native_header + header + match.pre_match + match.post_match\n      end\n    end\n    header + text\n  end\n\n  def create\n    @resource.class.validproperties.each do |property|\n      value = @resource.should(property)\n      if value\n        @property_hash[property] = value\n      end\n    end\n    mark_target_modified\n    (@resource.class.name.to_s + \"_created\").intern\n  end\n\n  def destroy\n    # We use the method here so it marks the target as modified.\n    self.ensure = :absent\n    (@resource.class.name.to_s + \"_deleted\").intern\n  end\n\n  def exists?\n    !(@property_hash[:ensure] == :absent or @property_hash[:ensure].nil?)\n  end\n\n  # Write our data to disk.\n  def flush\n    # Make sure we've got a target and name set.\n\n    # If the target isn't set, then this is our first modification, so\n    # mark it for flushing.\n    unless @property_hash[:target]\n      @property_hash[:target] = @resource.should(:target) || self.class.default_target\n      self.class.modified(@property_hash[:target])\n    end\n    @resource.class.key_attributes.each do |attr|\n      @property_hash[attr] ||= @resource[attr]\n    end\n\n    self.class.flush(@property_hash)\n  end\n\n  def initialize(record)\n    super\n\n    # The 'record' could be a resource or a record, depending on how the provider\n    # is initialized.  If we got an empty property hash (probably because the resource\n    # is just being initialized), then we want to set up some defaults.\n    @property_hash = self.class.record?(resource[:name]) || { :record_type => self.class.name, :ensure => :absent } if @property_hash.empty?\n  end\n\n  # Retrieve the current state from disk.\n  def prefetch\n    raise Puppet::DevError, _(\"Somehow got told to prefetch with no resource set\") unless @resource\n\n    self.class.prefetch(@resource[:name] => @resource)\n  end\n\n  def record_type\n    @property_hash[:record_type]\n  end\n\n  private\n\n  # Mark both the resource and provider target as modified.\n  def mark_target_modified\n    restarget = @resource.should(:target) if defined?(@resource)\n    if restarget && restarget != @property_hash[:target]\n      self.class.modified(restarget)\n    end\n    self.class.modified(@property_hash[:target]) if @property_hash[:target] != :absent and @property_hash[:target]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/base.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :base, :parent => :service do\n  desc \"The simplest form of Unix service support.\n\n  You have to specify enough about your service for this to work; the\n  minimum you can specify is a binary for starting the process, and this\n  same binary will be searched for in the process table to stop the\n  service.  As with `init`-style services, it is preferable to specify start,\n  stop, and status commands.\n\n  \"\n\n  commands :kill => \"kill\"\n\n  # get the proper 'ps' invocation for the platform\n  # ported from the facter 2.x implementation, since facter 3.x\n  # is dropping the fact (for which this was the only use)\n  def getps\n    case Puppet.runtime[:facter].value('os.name')\n    when 'OpenWrt'\n      'ps www'\n    when 'FreeBSD', 'NetBSD', 'OpenBSD', 'Darwin', 'DragonFly'\n      'ps auxwww'\n    else\n      'ps -ef'\n    end\n  end\n  private :getps\n\n  # Get the process ID for a running process. Requires the 'pattern'\n  # parameter.\n  def getpid\n    @resource.fail \"Either stop/status commands or a pattern must be specified\" unless @resource[:pattern]\n    regex = Regexp.new(@resource[:pattern])\n    ps = getps\n\n    debug \"Executing '#{ps}'\"\n    table = Puppet::Util::Execution.execute(ps)\n\n    # The output of the PS command can be a mashup of several different\n    # encodings depending on which processes are running and what\n    # arbitrary data has been used to set their name in the process table.\n    #\n    # First, try a polite conversion to in order to match the UTF-8 encoding\n    # of our regular expression.\n    table = Puppet::Util::CharacterEncoding.convert_to_utf_8(table)\n    # If that fails, force to UTF-8 and then scrub as most uses are scanning\n    # for ACII-compatible program names.\n    table.force_encoding(Encoding::UTF_8) unless table.encoding == Encoding::UTF_8\n    table = table.scrub unless table.valid_encoding?\n\n    table.each_line { |line|\n      next unless regex.match(line)\n\n      debug \"Process matched: #{line}\"\n      ary = line.sub(/^[[:space:]]+/u, '').split(/[[:space:]]+/u)\n      return ary[1]\n    }\n\n    nil\n  end\n  private :getpid\n\n  # Check if the process is running.  Prefer the 'status' parameter,\n  # then 'statuscmd' method, then look in the process table.  We give\n  # the object the option to not return a status command, which might\n  # happen if, for instance, it has an init script (and thus responds to\n  # 'statuscmd') but does not have 'hasstatus' enabled.\n  def status\n    if @resource[:status] or statuscmd\n      # Don't fail when the exit status is not 0.\n      status = service_command(:status, false)\n\n      # Explicitly calling exitstatus to facilitate testing\n      if status.exitstatus == 0\n        :running\n      else\n        :stopped\n      end\n    else\n      pid = getpid\n      if pid\n        debug \"PID is #{pid}\"\n        :running\n      else\n        :stopped\n      end\n    end\n  end\n\n  # There is no default command, which causes other methods to be used\n  def statuscmd\n  end\n\n  # Run the 'start' parameter command, or the specified 'startcmd'.\n  def start\n    service_command(:start)\n    nil\n  end\n\n  # The command used to start.  Generated if the 'binary' argument\n  # is passed.\n  def startcmd\n    @resource[:binary] || raise(Puppet::Error, \"Services must specify a start command or a binary\")\n  end\n\n  # Stop the service.  If a 'stop' parameter is specified, it\n  # takes precedence; otherwise checks if the object responds to\n  # a 'stopcmd' method, and if so runs that; otherwise, looks\n  # for the process in the process table.\n  # This method will generally not be overridden by submodules.\n  def stop\n    if @resource[:stop] or stopcmd\n      service_command(:stop)\n      nil\n    else\n      pid = getpid\n      unless pid\n        info _(\"%{name} is not running\") % { name: name }\n        return false\n      end\n      begin\n        output = kill pid\n      rescue Puppet::ExecutionFailure => e\n        @resource.fail Puppet::Error, \"Could not kill #{name}, PID #{pid}: #{output}\", e\n      end\n      true\n    end\n  end\n\n  # There is no default command, which causes other methods to be used\n  def stopcmd\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/bsd.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :bsd, :parent => :init do\n  desc <<-EOT\n    Generic BSD form of `init`-style service management with `rc.d`.\n\n    Uses `rc.conf.d` for service enabling and disabling.\n  EOT\n\n  confine 'os.name' => [:freebsd, :dragonfly]\n\n  def rcconf_dir\n    '/etc/rc.conf.d'\n  end\n\n  def self.defpath\n    superclass.defpath\n  end\n\n  # remove service file from rc.conf.d to disable it\n  def disable\n    rcfile = File.join(rcconf_dir, @resource[:name])\n    File.delete(rcfile) if Puppet::FileSystem.exist?(rcfile)\n  end\n\n  # if the service file exists in rc.conf.d then it's already enabled\n  def enabled?\n    rcfile = File.join(rcconf_dir, @resource[:name])\n    return :true if Puppet::FileSystem.exist?(rcfile)\n\n    :false\n  end\n\n  # enable service by creating a service file under rc.conf.d with the\n  # proper contents\n  def enable\n    Dir.mkdir(rcconf_dir) unless Puppet::FileSystem.exist?(rcconf_dir)\n    rcfile = File.join(rcconf_dir, @resource[:name])\n    File.open(rcfile, File::WRONLY | File::APPEND | File::CREAT, 0o644) { |f|\n      f << \"%s_enable=\\\"YES\\\"\\n\" % @resource[:name]\n    }\n  end\n\n  # Override stop/start commands to use one<cmd>'s and the avoid race condition\n  # where provider tries to stop/start the service before it is enabled\n  def startcmd\n    [initscript, :onestart]\n  end\n\n  def stopcmd\n    [initscript, :onestop]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/daemontools.rb",
    "content": "# frozen_string_literal: true\n\n# Daemontools service management\n#\n# author Brice Figureau <brice-puppet@daysofwonder.com>\nPuppet::Type.type(:service).provide :daemontools, :parent => :base do\n  desc <<-'EOT'\n    Daemontools service management.\n\n    This provider manages daemons supervised by D.J. Bernstein daemontools.\n    When detecting the service directory it will check, in order of preference:\n\n    * `/service`\n    * `/etc/service`\n    * `/var/lib/svscan`\n\n    The daemon directory should be in one of the following locations:\n\n    * `/var/lib/service`\n    * `/etc`\n\n    ...or this can be overridden in the resource's attributes:\n\n        service { 'myservice':\n          provider => 'daemontools',\n          path     => '/path/to/daemons',\n        }\n\n    This provider supports out of the box:\n\n    * start/stop (mapped to enable/disable)\n    * enable/disable\n    * restart\n    * status\n\n    If a service has `ensure => \"running\"`, it will link /path/to/daemon to\n    /path/to/service, which will automatically enable the service.\n\n    If a service has `ensure => \"stopped\"`, it will only shut down the service, not\n    remove the `/path/to/service` link.\n\n  EOT\n\n  commands :svc => \"/usr/bin/svc\", :svstat => \"/usr/bin/svstat\"\n\n  class << self\n    attr_writer :defpath\n\n    # Determine the daemon path.\n    def defpath\n      @defpath ||= [\"/var/lib/service\", \"/etc\"].find do |path|\n        Puppet::FileSystem.exist?(path) && FileTest.directory?(path)\n      end\n      @defpath\n    end\n  end\n\n  attr_writer :servicedir\n\n  # returns all providers for all existing services in @defpath\n  # ie enabled or not\n  def self.instances\n    path = defpath\n    unless path\n      Puppet.info(\"#{name} is unsuitable because service directory is nil\")\n      return\n    end\n    unless FileTest.directory?(path)\n      Puppet.notice \"Service path #{path} does not exist\"\n      return\n    end\n\n    # reject entries that aren't either a directory\n    # or don't contain a run file\n    Dir.entries(path).reject { |e|\n      fullpath = File.join(path, e)\n      e =~ /^\\./ or !FileTest.directory?(fullpath) or !Puppet::FileSystem.exist?(File.join(fullpath, \"run\"))\n    }.collect do |name|\n      new(:name => name, :path => path)\n    end\n  end\n\n  # returns the daemon dir on this node\n  def self.daemondir\n    defpath\n  end\n\n  # find the service dir on this node\n  def servicedir\n    unless @servicedir\n      [\"/service\", \"/etc/service\", \"/var/lib/svscan\"].each do |path|\n        if Puppet::FileSystem.exist?(path)\n          @servicedir = path\n          break\n        end\n      end\n      raise \"Could not find service directory\" unless @servicedir\n    end\n    @servicedir\n  end\n\n  # returns the full path of this service when enabled\n  # (ie in the service directory)\n  def service\n    File.join(servicedir, resource[:name])\n  end\n\n  # returns the full path to the current daemon directory\n  # note that this path can be overridden in the resource\n  # definition\n  def daemon\n    path = resource[:path]\n    raise Puppet::Error, \"#{self.class.name} must specify a path for daemon directory\" unless path\n\n    File.join(path, resource[:name])\n  end\n\n  def status\n    begin\n      output = svstat service\n      if output =~ /:\\s+up \\(/\n        return :running\n      end\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error.new(\"Could not get status for service #{resource.ref}: #{detail}\", detail)\n    end\n    :stopped\n  end\n\n  def setupservice\n    if resource[:manifest]\n      Puppet.notice \"Configuring #{resource[:name]}\"\n      command = [resource[:manifest], resource[:name]]\n      system(command.to_s)\n    end\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error.new(\"Cannot config #{service} to enable it: #{detail}\", detail)\n  end\n\n  def enabled?\n    case status\n    when :running\n      # obviously if the daemon is running then it is enabled\n      :true\n    else\n      # the service is enabled if it is linked\n      Puppet::FileSystem.symlink?(service) ? :true : :false\n    end\n  end\n\n  def enable\n    unless FileTest.directory?(daemon)\n      Puppet.notice \"No daemon dir, calling setupservice for #{resource[:name]}\"\n      setupservice\n    end\n    if daemon\n      unless Puppet::FileSystem.symlink?(service)\n        Puppet.notice \"Enabling #{service}: linking #{daemon} -> #{service}\"\n        Puppet::FileSystem.symlink(daemon, service)\n      end\n    end\n  rescue Puppet::ExecutionFailure => e\n    raise Puppet::Error.new(\"No daemon directory found for #{service}\", e)\n  end\n\n  def disable\n    begin\n      unless FileTest.directory?(daemon)\n        Puppet.notice \"No daemon dir, calling setupservice for #{resource[:name]}\"\n        setupservice\n      end\n      if daemon\n        if Puppet::FileSystem.symlink?(service)\n          Puppet.notice \"Disabling #{service}: removing link #{daemon} -> #{service}\"\n          Puppet::FileSystem.unlink(service)\n        end\n      end\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(\"No daemon directory found for #{service}\", e)\n    end\n    stop\n  end\n\n  def restart\n    svc \"-t\", service\n  end\n\n  def start\n    enable unless enabled? == :true\n    svc \"-u\", service\n  end\n\n  def stop\n    svc \"-d\", service\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/debian.rb",
    "content": "# frozen_string_literal: true\n\n# Manage debian services.  Start/stop is the same as InitSvc, but enable/disable\n# is special.\nPuppet::Type.type(:service).provide :debian, :parent => :init do\n  desc <<-EOT\n    Debian's form of `init`-style management.\n\n    The only differences from `init` are support for enabling and disabling\n    services via `update-rc.d` and the ability to determine enabled status via\n    `invoke-rc.d`.\n\n  EOT\n\n  commands :update_rc => \"/usr/sbin/update-rc.d\"\n  # note this isn't being used as a command until\n  # https://projects.puppetlabs.com/issues/2538\n  # is resolved.\n  commands :invoke_rc => \"/usr/sbin/invoke-rc.d\"\n  commands :service => \"/usr/sbin/service\"\n\n  confine :false => Puppet::FileSystem.exist?('/proc/1/comm') && Puppet::FileSystem.read('/proc/1/comm').include?('systemd')\n\n  defaultfor 'os.name' => :cumuluslinux, 'os.release.major' => %w[1 2]\n  defaultfor 'os.name' => :debian, 'os.release.major' => %w[5 6 7]\n  defaultfor 'os.name' => :devuan\n\n  # Remove the symlinks\n  def disable\n    if `dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?`.to_i == 0\n      update_rc @resource[:name], \"disable\"\n    else\n      update_rc \"-f\", @resource[:name], \"remove\"\n      update_rc @resource[:name], \"stop\", \"00\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \".\"\n    end\n  end\n\n  def enabled?\n    status = execute([\"/usr/sbin/invoke-rc.d\", \"--quiet\", \"--query\", @resource[:name], \"start\"], :failonfail => false)\n\n    # 104 is the exit status when you query start an enabled service.\n    # 106 is the exit status when the policy layer supplies a fallback action\n    # See x-man-page://invoke-rc.d\n    if [104, 106].include?(status.exitstatus)\n      :true\n    elsif [101, 105].include?(status.exitstatus)\n      # 101 is action not allowed, which means we have to do the check manually.\n      # 105 is unknown, which generally means the initscript does not support query\n      # The debian policy states that the initscript should support methods of query\n      # For those that do not, perform the checks manually\n      # http://www.debian.org/doc/debian-policy/ch-opersys.html\n      if get_start_link_count >= 4\n        :true\n      else\n        :false\n      end\n    else\n      :false\n    end\n  end\n\n  def get_start_link_count\n    Dir.glob(\"/etc/rc*.d/S??#{@resource[:name]}\").length\n  end\n\n  def enable\n    update_rc \"-f\", @resource[:name], \"remove\"\n    update_rc @resource[:name], \"defaults\"\n  end\n\n  def statuscmd\n    # /usr/sbin/service provides an abstraction layer which is able to query services\n    # independent of the init system used.\n    # See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=775795\n    (@resource[:hasstatus] == :true) && [command(:service), @resource[:name], \"status\"]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/freebsd.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :freebsd, :parent => :init do\n  desc \"Provider for FreeBSD and DragonFly BSD. Uses the `rcvar` argument of init scripts and parses/edits rc files.\"\n\n  confine 'os.name' => [:freebsd, :dragonfly]\n  defaultfor 'os.name' => [:freebsd, :dragonfly]\n\n  def rcconf()        '/etc/rc.conf' end\n  def rcconf_local()  '/etc/rc.conf.local' end\n  def rcconf_dir()    '/etc/rc.conf.d' end\n\n  def self.defpath\n    superclass.defpath\n  end\n\n  def error(msg)\n    raise Puppet::Error, msg\n  end\n\n  # Executing an init script with the 'rcvar' argument returns\n  # the service name, rcvar name and whether it's enabled/disabled\n  def rcvar\n    rcvar = execute([initscript, :rcvar], :failonfail => true, :combine => false, :squelch => false)\n    rcvar = rcvar.split(\"\\n\")\n    rcvar.delete_if { |str| str =~ /^#\\s*$/ }\n    rcvar[1] = rcvar[1].gsub(/^\\$/, '')\n    rcvar\n  end\n\n  # Extract value name from service or rcvar\n  def extract_value_name(name, rc_index, regex, regex_index)\n    value_name = rcvar[rc_index]\n    error(\"No #{name} name found in rcvar\") if value_name.nil?\n    value_name = value_name.gsub!(regex, regex_index)\n    error(\"#{name} name is empty\") if value_name.nil?\n    debug(\"#{name} name is #{value_name}\")\n    value_name\n  end\n\n  # Extract service name\n  def service_name\n    extract_value_name('service', 0, /# (\\S+).*/, '\\1')\n  end\n\n  # Extract rcvar name\n  def rcvar_name\n    extract_value_name('rcvar', 1, /(.*?)(_enable)?=(.*)/, '\\1')\n  end\n\n  # Extract rcvar value\n  def rcvar_value\n    value = rcvar[1]\n    error(\"No rcvar value found in rcvar\") if value.nil?\n    value = value.gsub!(/(.*)(_enable)?=\"?(\\w+)\"?/, '\\3')\n    error(\"rcvar value is empty\") if value.nil?\n    debug(\"rcvar value is #{value}\")\n    value\n  end\n\n  # Edit rc files and set the service to yes/no\n  def rc_edit(yesno)\n    service = service_name\n    rcvar = rcvar_name\n    debug(\"Editing rc files: setting #{rcvar} to #{yesno} for #{service}\")\n    rc_add(service, rcvar, yesno) unless rc_replace(service, rcvar, yesno)\n  end\n\n  # Try to find an existing setting in the rc files\n  # and replace the value\n  def rc_replace(service, rcvar, yesno)\n    success = false\n    # Replace in all files, not just in the first found with a match\n    [rcconf, rcconf_local, rcconf_dir + \"/#{service}\"].each do |filename|\n      next unless Puppet::FileSystem.exist?(filename)\n\n      s = File.read(filename)\n      next unless s.gsub!(/^(#{rcvar}(_enable)?)=\"?(YES|NO)\"?/, \"\\\\1=\\\"#{yesno}\\\"\")\n\n      Puppet::FileSystem.replace_file(filename) { |f| f << s }\n      debug(\"Replaced in #{filename}\")\n      success = true\n    end\n    success\n  end\n\n  # Add a new setting to the rc files\n  def rc_add(service, rcvar, yesno)\n    append = \"\\# Added by Puppet\\n#{rcvar}_enable=\\\"#{yesno}\\\"\\n\"\n    # First, try the one-file-per-service style\n    if Puppet::FileSystem.exist?(rcconf_dir)\n      File.open(rcconf_dir + \"/#{service}\", File::WRONLY | File::APPEND | File::CREAT, 0o644) { |f|\n        f << append\n        debug(\"Appended to #{f.path}\")\n      }\n    elsif Puppet::FileSystem.exist?(rcconf_local)\n      # Else, check the local rc file first, but don't create it\n      File.open(rcconf_local, File::WRONLY | File::APPEND) { |f|\n        f << append\n        debug(\"Appended to #{f.path}\")\n      }\n    else\n      # At last use the standard rc.conf file\n      File.open(rcconf, File::WRONLY | File::APPEND | File::CREAT, 0o644) { |f|\n        f << append\n        debug(\"Appended to #{f.path}\")\n      }\n    end\n  end\n\n  def enabled?\n    if /YES$/ =~ rcvar_value\n      debug(\"Is enabled\")\n      return :true\n    end\n    debug(\"Is disabled\")\n    :false\n  end\n\n  def enable\n    debug(\"Enabling\")\n    rc_edit(\"YES\")\n  end\n\n  def disable\n    debug(\"Disabling\")\n    rc_edit(\"NO\")\n  end\n\n  def startcmd\n    [initscript, :onestart]\n  end\n\n  def stopcmd\n    [initscript, :onestop]\n  end\n\n  def statuscmd\n    [initscript, :onestatus]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/gentoo.rb",
    "content": "# frozen_string_literal: true\n\n# Manage gentoo services.  Start/stop is the same as InitSvc, but enable/disable\n# is special.\nPuppet::Type.type(:service).provide :gentoo, :parent => :init do\n  desc <<-EOT\n    Gentoo's form of `init`-style service management.\n\n    Uses `rc-update` for service enabling and disabling.\n\n  EOT\n\n  commands :update => \"/sbin/rc-update\"\n\n  confine 'os.name' => :gentoo\n\n  def disable\n    output = update :del, @resource[:name], :default\n  rescue Puppet::ExecutionFailure => e\n    raise Puppet::Error, \"Could not disable #{name}: #{output}\", e.backtrace\n  end\n\n  def enabled?\n    begin\n      output = update :show\n    rescue Puppet::ExecutionFailure\n      return :false\n    end\n\n    line = output.split(/\\n/).find { |l| l.include?(@resource[:name]) }\n\n    return :false unless line\n\n    # If it's enabled then it will print output showing service | runlevel\n    if output =~ /^\\s*#{@resource[:name]}\\s*\\|\\s*(boot|default)/\n      :true\n    else\n      :false\n    end\n  end\n\n  def enable\n    output = update :add, @resource[:name], :default\n  rescue Puppet::ExecutionFailure => e\n    raise Puppet::Error, \"Could not enable #{name}: #{output}\", e.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/init.rb",
    "content": "# frozen_string_literal: true\n\n# The standard init-based service type.  Many other service types are\n# customizations of this module.\nPuppet::Type.type(:service).provide :init, :parent => :base do\n  desc \"Standard `init`-style service management.\"\n\n  def self.defpath\n    case Puppet.runtime[:facter].value('os.name')\n    when \"FreeBSD\", \"DragonFly\"\n      [\"/etc/rc.d\", \"/usr/local/etc/rc.d\"]\n    when \"HP-UX\"\n      \"/sbin/init.d\"\n    when \"Archlinux\"\n      \"/etc/rc.d\"\n    when \"AIX\"\n      \"/etc/rc.d/init.d\"\n    else\n      \"/etc/init.d\"\n    end\n  end\n\n  # Debian and Ubuntu should use the Debian provider.\n  confine :false => %w[Debian Ubuntu].include?(Puppet.runtime[:facter].value('os.name'))\n  # RedHat systems should use the RedHat provider.\n  confine :false => Puppet.runtime[:facter].value('os.family') == 'RedHat'\n\n  # We can't confine this here, because the init path can be overridden.\n  # confine :exists => defpath\n\n  # some init scripts are not safe to execute, e.g. we do not want\n  # to suddenly run /etc/init.d/reboot.sh status and reboot our system. The\n  # exclude list could be platform agnostic but I assume an invalid init script\n  # on system A will never be a valid init script on system B\n  def self.excludes\n    excludes = []\n    # these exclude list was found with grep -L '\\/sbin\\/runscript' /etc/init.d/* on gentoo\n    excludes += %w[functions.sh reboot.sh shutdown.sh]\n    # this exclude list is all from /sbin/service (5.x), but I did not exclude kudzu\n    excludes += %w[functions halt killall single linuxconf reboot boot]\n    # 'wait-for-state' and 'portmap-wait' are excluded from instances here\n    # because they take parameters that have unclear meaning. It looks like\n    # 'wait-for-state' is a generic waiter mainly used internally for other\n    # upstart services as a 'sleep until something happens'\n    # (http://lists.debian.org/debian-devel/2012/02/msg01139.html), while\n    # 'portmap-wait' is a specific instance of a waiter. There is an open\n    # launchpad bug\n    # (https://bugs.launchpad.net/ubuntu/+source/upstart/+bug/962047) that may\n    # eventually explain how to use the wait-for-state service or perhaps why\n    # it should remain excluded. When that bug is addressed this should be\n    # reexamined.\n    excludes += %w[wait-for-state portmap-wait]\n    # these excludes were found with grep -r -L start /etc/init.d\n    excludes += %w[rcS module-init-tools]\n    # Prevent puppet failing on unsafe scripts from Yocto Linux\n    if Puppet.runtime[:facter].value('os.family') == \"cisco-wrlinux\"\n      excludes += %w[banner.sh bootmisc.sh checkroot.sh devpts.sh dmesg.sh\n                     hostname.sh mountall.sh mountnfs.sh populate-volatile.sh\n                     rmnologin.sh save-rtc.sh sendsigs sysfs.sh umountfs\n                     umountnfs.sh]\n    end\n    # Prevent puppet failing to get status of the new service introduced\n    # by the fix for this (bug https://bugs.launchpad.net/ubuntu/+source/lightdm/+bug/982889)\n    # due to puppet's inability to deal with upstart services with instances.\n    excludes += %w[plymouth-ready]\n    # Prevent puppet failing to get status of these services, which need parameters\n    # passed in (see https://bugs.launchpad.net/ubuntu/+source/puppet/+bug/1276766).\n    excludes += %w[idmapd-mounting startpar-bridge]\n    # Prevent puppet failing to get status of these services, additional upstart\n    # service with instances\n    excludes += %w[cryptdisks-udev]\n    excludes += %w[statd-mounting]\n    excludes += %w[gssd-mounting]\n    excludes\n  end\n\n  # List all services of this type.\n  def self.instances\n    get_services(defpath)\n  end\n\n  def self.get_services(defpath, exclude = excludes)\n    defpath = [defpath] unless defpath.is_a? Array\n    instances = []\n    defpath.each do |path|\n      unless Puppet::FileSystem.directory?(path)\n        Puppet.debug \"Service path #{path} does not exist\"\n        next\n      end\n\n      check = [:ensure]\n\n      check << :enable if public_method_defined? :enabled?\n\n      Dir.entries(path).each do |name|\n        fullpath = File.join(path, name)\n        next if name =~ /^\\./\n        next if exclude.include? name\n        next if Puppet::FileSystem.directory?(fullpath)\n        next unless Puppet::FileSystem.executable?(fullpath)\n        next unless is_init?(fullpath)\n\n        instances << new(:name => name, :path => path, :hasstatus => true)\n      end\n    end\n    instances\n  end\n\n  # Mark that our init script supports 'status' commands.\n  def hasstatus=(value)\n    case value\n    when true, \"true\"; @parameters[:hasstatus] = true\n    when false, \"false\"; @parameters[:hasstatus] = false\n    else\n      raise Puppet::Error, \"Invalid 'hasstatus' value #{value.inspect}\"\n    end\n  end\n\n  # Where is our init script?\n  def initscript\n    @initscript ||= search(@resource[:name])\n  end\n\n  def paths\n    @paths ||= @resource[:path].find_all do |path|\n      if Puppet::FileSystem.directory?(path)\n        true\n      else\n        if Puppet::FileSystem.exist?(path)\n          debug \"Search path #{path} is not a directory\"\n        else\n          debug \"Search path #{path} does not exist\"\n        end\n        false\n      end\n    end\n  end\n\n  def search(name)\n    paths.each do |path|\n      fqname = File.join(path, name)\n      if Puppet::FileSystem.exist? fqname\n        return fqname\n      else\n        debug(\"Could not find #{name} in #{path}\")\n      end\n    end\n\n    paths.each do |path|\n      fqname_sh = File.join(path, \"#{name}.sh\")\n      if Puppet::FileSystem.exist? fqname_sh\n        return fqname_sh\n      else\n        debug(\"Could not find #{name}.sh in #{path}\")\n      end\n    end\n    raise Puppet::Error, \"Could not find init script for '#{name}'\"\n  end\n\n  # The start command is just the init script with 'start'.\n  def startcmd\n    [initscript, :start]\n  end\n\n  # The stop command is just the init script with 'stop'.\n  def stopcmd\n    [initscript, :stop]\n  end\n\n  def restartcmd\n    (@resource[:hasrestart] == :true) && [initscript, :restart]\n  end\n\n  def service_execute(type, command, fof = true, squelch = false, combine = true)\n    if type == :start && Puppet.runtime[:facter].value('os.family') == \"Solaris\"\n      command = [\"/usr/bin/ctrun -l child\", command].flatten.join(\" \")\n    end\n    super(type, command, fof, squelch, combine)\n  end\n\n  # If it was specified that the init script has a 'status' command, then\n  # we just return that; otherwise, we return false, which causes it to\n  # fallback to other mechanisms.\n  def statuscmd\n    (@resource[:hasstatus] == :true) && [initscript, :status]\n  end\n\n  private\n\n  def self.is_init?(script = initscript)\n    file = Puppet::FileSystem.pathname(script)\n    !Puppet::FileSystem.symlink?(file) || Puppet::FileSystem.readlink(file) != \"/lib/init/upstart-job\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/launchd.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/plist'\nPuppet::Type.type(:service).provide :launchd, :parent => :base do\n  desc <<-'EOT'\n    This provider manages jobs with `launchd`, which is the default service\n    framework for Mac OS X (and may be available for use on other platforms).\n\n    For more information, see the `launchd` man page:\n\n    * <https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man8/launchd.8.html>\n\n    This provider reads plists out of the following directories:\n\n    * `/System/Library/LaunchDaemons`\n    * `/System/Library/LaunchAgents`\n    * `/Library/LaunchDaemons`\n    * `/Library/LaunchAgents`\n\n    ...and builds up a list of services based upon each plist's \"Label\" entry.\n\n    This provider supports:\n\n    * ensure => running/stopped,\n    * enable => true/false\n    * status\n    * restart\n\n    Here is how the Puppet states correspond to `launchd` states:\n\n    * stopped --- job unloaded\n    * started --- job loaded\n    * enabled --- 'Disable' removed from job plist file\n    * disabled --- 'Disable' added to job plist file\n\n    Note that this allows you to do something `launchctl` can't do, which is to\n    be in a state of \"stopped/enabled\" or \"running/disabled\".\n\n    Note that this provider does not support overriding 'restart'\n\n  EOT\n\n  include Puppet::Util::Warnings\n\n  commands :launchctl => \"/bin/launchctl\"\n\n  defaultfor 'os.name' => :darwin\n  confine 'os.name'    => :darwin\n  confine :feature => :cfpropertylist\n\n  has_feature :enableable\n  has_feature :refreshable\n  mk_resource_methods\n\n  # These are the paths in OS X where a launchd service plist could\n  # exist. This is a helper method, versus a constant, for easy testing\n  # and mocking\n  #\n  # @api private\n  def self.launchd_paths\n    [\n      \"/Library/LaunchAgents\",\n      \"/Library/LaunchDaemons\",\n      \"/System/Library/LaunchAgents\",\n      \"/System/Library/LaunchDaemons\"\n    ]\n  end\n\n  # Gets the current Darwin version, example 10.6 returns 9 and 10.10 returns 14\n  # See https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history\n  # for more information.\n  #\n  # @api private\n  def self.get_os_version\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @os_version ||= Facter.value('os.release.major').to_i\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  # Defines the path to the overrides plist file where service enabling\n  # behavior is defined in 10.6 and greater.\n  #\n  # With the rewrite of launchd in 10.10+, this moves and slightly changes format.\n  #\n  # @api private\n  def self.launchd_overrides\n    if get_os_version < 14\n      \"/var/db/launchd.db/com.apple.launchd/overrides.plist\"\n    else\n      \"/var/db/com.apple.xpc.launchd/disabled.plist\"\n    end\n  end\n\n  # Caching is enabled through the following three methods. Self.prefetch will\n  # call self.instances to create an instance for each service. Self.flush will\n  # clear out our cache when we're done.\n  def self.prefetch(resources)\n    instances.each do |prov|\n      resource = resources[prov.name]\n      if resource\n        resource.provider = prov\n      end\n    end\n  end\n\n  # Self.instances will return an array with each element being a hash\n  # containing the name, provider, path, and status of each service on the\n  # system.\n  def self.instances\n    jobs = jobsearch\n    @job_list ||= job_list\n    jobs.keys.collect do |job|\n      job_status = @job_list.has_key?(job) ? :running : :stopped\n      new(:name => job, :provider => :launchd, :path => jobs[job], :status => job_status)\n    end\n  end\n\n  # This method will return a list of files in the passed directory. This method\n  # does not go recursively down the tree and does not return directories\n  #\n  # @param path [String] The directory to glob\n  #\n  # @api private\n  #\n  # @return [Array] of String instances modeling file paths\n  def self.return_globbed_list_of_file_paths(path)\n    array_of_files = Dir.glob(File.join(path, '*')).collect do |filepath|\n      File.file?(filepath) ? filepath : nil\n    end\n    array_of_files.compact\n  end\n\n  # Get a hash of all launchd plists, keyed by label.  This value is cached, but\n  # the cache will be refreshed if refresh is true.\n  #\n  # @api private\n  def self.make_label_to_path_map(refresh = false)\n    return @label_to_path_map if @label_to_path_map and !refresh\n\n    @label_to_path_map = {}\n    launchd_paths.each do |path|\n      return_globbed_list_of_file_paths(path).each do |filepath|\n        Puppet.debug(\"Reading launchd plist #{filepath}\")\n        job = read_plist(filepath)\n        next if job.nil?\n\n        if job.respond_to?(:key) && job.key?(\"Label\")\n          @label_to_path_map[job[\"Label\"]] = filepath\n        else\n          # TRANSLATORS 'plist' and label' should not be translated\n          Puppet.debug(_(\"The %{file} plist does not contain a 'label' key; Puppet is skipping it\") % { file: filepath })\n          next\n        end\n      end\n    end\n    @label_to_path_map\n  end\n\n  # Sets a class instance variable with a hash of all launchd plist files that\n  # are found on the system. The key of the hash is the job id and the value\n  # is the path to the file. If a label is passed, we return the job id and\n  # path for that specific job.\n  def self.jobsearch(label = nil)\n    by_label = make_label_to_path_map\n\n    if label\n      if by_label.has_key? label\n        { label => by_label[label] }\n      else\n        # try refreshing the map, in case a plist has been added in the interim\n        by_label = make_label_to_path_map(true)\n        if by_label.has_key? label\n          { label => by_label[label] }\n        else\n          raise Puppet::Error, \"Unable to find launchd plist for job: #{label}\"\n        end\n      end\n    else\n      # caller wants the whole map\n      by_label\n    end\n  end\n\n  # This status method lists out all currently running services.\n  # This hash is returned at the end of the method.\n  def self.job_list\n    @job_list = Hash.new\n    begin\n      output = launchctl :list\n      raise Puppet::Error, \"launchctl list failed to return any data.\" if output.nil?\n\n      output.split(\"\\n\").each do |line|\n        @job_list[line.split(/\\s/).last] = :running\n      end\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(\"Unable to determine status of #{resource[:name]}\", e)\n    end\n    @job_list\n  end\n\n  # Read a plist, whether its format is XML or in Apple's \"binary1\"\n  # format.\n  def self.read_plist(path)\n    Puppet::Util::Plist.read_plist_file(path)\n  end\n\n  # Read overrides plist, retrying if necessary\n  def self.read_overrides\n    i = 1\n    overrides = nil\n    loop do\n      Puppet.debug(_(\"Reading overrides plist, attempt %{i}\") % { i: i }) if i > 1\n      overrides = read_plist(launchd_overrides)\n      break unless overrides.nil?\n      raise Puppet::Error, _('Unable to read overrides plist, too many attempts') if i == 20\n\n      Puppet.info(_('Overrides file could not be read, trying again.'))\n      Kernel.sleep(0.1)\n      i += 1\n    end\n    overrides\n  end\n\n  # Clean out the @property_hash variable containing the cached list of services\n  def flush\n    @property_hash.clear\n  end\n\n  def exists?\n    Puppet.debug(\"Puppet::Provider::Launchd:Ensure for #{@property_hash[:name]}: #{@property_hash[:ensure]}\")\n    @property_hash[:ensure] != :absent\n  end\n\n  # finds the path for a given label and returns the path and parsed plist\n  # as an array of [path, plist]. Note plist is really a Hash here.\n  def plist_from_label(label)\n    job = self.class.jobsearch(label)\n    job_path = job[label]\n    if FileTest.file?(job_path)\n      job_plist = self.class.read_plist(job_path)\n    else\n      raise Puppet::Error, \"Unable to parse launchd plist at path: #{job_path}\"\n    end\n    [job_path, job_plist]\n  end\n\n  # when a service includes hasstatus=>false, we override the launchctl\n  # status mechanism and fall back to the base provider status method.\n  def status\n    if @resource && ((@resource[:hasstatus] == :false) || (@resource[:status]))\n      super\n    elsif @property_hash[:status].nil?\n      # property_hash was flushed so the service changed status\n      service_name = @resource[:name]\n      # Updating services with new statuses\n      job_list = self.class.job_list\n      # if job is present in job_list, return its status\n      if job_list.key?(service_name)\n        job_list[service_name]\n      # if job is no longer present in job_list, it was stopped\n      else\n        :stopped\n      end\n    else\n      @property_hash[:status]\n    end\n  end\n\n  # start the service. To get to a state of running/enabled, we need to\n  # conditionally enable at load, then disable by modifying the plist file\n  # directly.\n  def start\n    if resource[:start]\n      service_command(:start)\n      return nil\n    end\n    job_path, _ = plist_from_label(resource[:name])\n    did_enable_job = false\n    cmds = []\n    cmds << :launchctl << :load\n    # always add -w so it always starts the job, it is a noop if it is not needed, this means we do\n    # not have to rescan all launchd plists.\n    cmds << \"-w\"\n    if enabled? == :false || status == :stopped # launchctl won't load disabled jobs\n      did_enable_job = true\n    end\n    cmds << job_path\n    begin\n      execute(cmds)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(\"Unable to start service: #{resource[:name]} at path: #{job_path}\", e)\n    end\n    # As load -w clears the Disabled flag, we need to add it in after\n    disable if did_enable_job and resource[:enable] == :false\n  end\n\n  def stop\n    if resource[:stop]\n      service_command(:stop)\n      return nil\n    end\n    job_path, _ = plist_from_label(resource[:name])\n    did_disable_job = false\n    cmds = []\n    cmds << :launchctl << :unload\n    if enabled? == :true # keepalive jobs can't be stopped without disabling\n      cmds << \"-w\"\n      did_disable_job = true\n    end\n    cmds << job_path\n    begin\n      execute(cmds)\n    rescue Puppet::ExecutionFailure => e\n      raise Puppet::Error.new(\"Unable to stop service: #{resource[:name]} at path: #{job_path}\", e)\n    end\n    # As unload -w sets the Disabled flag, we need to add it in after\n    enable if did_disable_job and resource[:enable] == :true\n  end\n\n  def restart\n    Puppet.debug(\"A restart has been triggered for the #{resource[:name]} service\")\n    Puppet.debug(\"Stopping the #{resource[:name]} service\")\n    stop\n    Puppet.debug(\"Starting the #{resource[:name]} service\")\n    start\n  end\n\n  # launchd jobs are enabled by default. They are only disabled if the key\n  # \"Disabled\" is set to true, but it can also be set to false to enable it.\n  # Starting in 10.6, the Disabled key in the job plist is consulted, but only\n  # if there is no entry in the global overrides plist.  We need to draw a\n  # distinction between undefined, true and false for both locations where the\n  # Disabled flag can be defined.\n  def enabled?\n    job_plist_disabled = nil\n    overrides_disabled = nil\n\n    begin\n      _, job_plist = plist_from_label(resource[:name])\n    rescue Puppet::Error => err\n      # if job does not exist, log the error and return false as on other platforms\n      Puppet.log_exception(err)\n      return :false\n    end\n\n    job_plist_disabled = job_plist[\"Disabled\"] if job_plist.has_key?(\"Disabled\")\n\n    overrides = self.class.read_overrides if FileTest.file?(self.class.launchd_overrides)\n    if overrides\n      if overrides.has_key?(resource[:name])\n        if self.class.get_os_version < 14\n          overrides_disabled = overrides[resource[:name]][\"Disabled\"] if overrides[resource[:name]].has_key?(\"Disabled\")\n        else\n          overrides_disabled = overrides[resource[:name]]\n        end\n      end\n    end\n\n    if overrides_disabled.nil?\n      if job_plist_disabled.nil? or job_plist_disabled == false\n        return :true\n      end\n    elsif overrides_disabled == false\n      return :true\n    end\n    :false\n  end\n\n  # enable and disable are a bit hacky. We write out the plist with the appropriate value\n  # rather than dealing with launchctl as it is unable to change the Disabled flag\n  # without actually loading/unloading the job.\n  def enable\n    overrides = self.class.read_overrides\n    if self.class.get_os_version < 14\n      overrides[resource[:name]] = { \"Disabled\" => false }\n    else\n      overrides[resource[:name]] = false\n    end\n    Puppet::Util::Plist.write_plist_file(overrides, self.class.launchd_overrides)\n  end\n\n  def disable\n    overrides = self.class.read_overrides\n    if self.class.get_os_version < 14\n      overrides[resource[:name]] = { \"Disabled\" => true }\n    else\n      overrides[resource[:name]] = true\n    end\n    Puppet::Util::Plist.write_plist_file(overrides, self.class.launchd_overrides)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/openbsd.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :openbsd, :parent => :init do\n  desc \"Provider for OpenBSD's rc.d daemon control scripts\"\n\n  commands :rcctl => '/usr/sbin/rcctl'\n\n  confine 'os.name' => :openbsd\n  defaultfor 'os.name' => :openbsd\n  has_feature :flaggable\n\n  def startcmd\n    [command(:rcctl), '-f', :start, @resource[:name]]\n  end\n\n  def stopcmd\n    [command(:rcctl), :stop, @resource[:name]]\n  end\n\n  def restartcmd\n    (@resource[:hasrestart] == :true) && [command(:rcctl), '-f', :restart, @resource[:name]]\n  end\n\n  def statuscmd\n    [command(:rcctl), :check, @resource[:name]]\n  end\n\n  # @api private\n  # When storing the name, take into account not everything has\n  # '_flags', like 'multicast_host' and 'pf'.\n  def self.instances\n    instances = []\n\n    begin\n      execpipe([command(:rcctl), :getall]) do |process|\n        process.each_line do |line|\n          match = /^(.*?)(?:_flags)?=(.*)$/.match(line)\n          attributes_hash = {\n            :name => match[1],\n            :flags => match[2],\n            :hasstatus => true,\n            :provider => :openbsd,\n          }\n\n          instances << new(attributes_hash);\n        end\n      end\n      instances\n    rescue Puppet::ExecutionFailure\n      nil\n    end\n  end\n\n  def enabled?\n    output = execute([command(:rcctl), \"get\", @resource[:name], \"status\"],\n                     :failonfail => false, :combine => false, :squelch => false)\n\n    if output.exitstatus == 1\n      debug(\"Is disabled\")\n      :false\n    else\n      debug(\"Is enabled\")\n      :true\n    end\n  end\n\n  def enable\n    debug(\"Enabling\")\n    rcctl(:enable, @resource[:name])\n    if @resource[:flags]\n      rcctl(:set, @resource[:name], :flags, @resource[:flags])\n    end\n  end\n\n  def disable\n    debug(\"Disabling\")\n    rcctl(:disable, @resource[:name])\n  end\n\n  def running?\n    output = execute([command(:rcctl), \"check\", @resource[:name]],\n                     :failonfail => false, :combine => false, :squelch => false).chomp\n    true if output =~ /\\(ok\\)/\n  end\n\n  # Uses the wrapper to prevent failure when the service is not running;\n  # rcctl(8) return non-zero in that case.\n  def flags\n    output = execute([command(:rcctl), \"get\", @resource[:name], \"flags\"],\n                     :failonfail => false, :combine => false, :squelch => false).chomp\n    debug(\"Flags are: \\\"#{output}\\\"\")\n    output\n  end\n\n  def flags=(value)\n    debug(\"Changing flags from #{flags} to #{value}\")\n    rcctl(:set, @resource[:name], :flags, value)\n    # If the service is already running, force a restart as the flags have been changed.\n    rcctl(:restart, @resource[:name]) if running?\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/openrc.rb",
    "content": "# frozen_string_literal: true\n\n# Gentoo OpenRC\nPuppet::Type.type(:service).provide :openrc, :parent => :base do\n  desc <<-EOT\n    Support for Gentoo's OpenRC initskripts\n\n    Uses rc-update, rc-status and rc-service to manage services.\n\n  EOT\n\n  defaultfor 'os.name' => :gentoo\n  defaultfor 'os.name' => :funtoo\n\n  has_command(:rcstatus, '/bin/rc-status') do\n    environment :RC_SVCNAME => nil\n  end\n  commands :rcservice => '/sbin/rc-service'\n  commands :rcupdate  => '/sbin/rc-update'\n\n  self::STATUSLINE = /^\\s+(.*?)\\s*\\[\\s*(.*)\\s*\\]$/\n\n  def enable\n    rcupdate('-C', :add, @resource[:name])\n  end\n\n  def disable\n    rcupdate('-C', :del, @resource[:name])\n  end\n\n  # rc-status -a shows all runlevels and dynamic runlevels which\n  # are not considered as enabled. We have to find out under which\n  # runlevel our service is listed\n  def enabled?\n    enabled = :false\n    rcstatus('-C', '-a').each_line do |line|\n      case line.chomp\n      when /^Runlevel: /\n        enabled = :true\n      when /^\\S+/ # caption of a dynamic runlevel\n        enabled = :false\n      when self.class::STATUSLINE\n        return enabled if @resource[:name] == Regexp.last_match(1)\n      end\n    end\n    :false\n  end\n\n  def self.instances\n    instances = []\n    rcservice('-C', '--list').each_line do |line|\n      instances << new(:name => line.chomp)\n    end\n    instances\n  end\n\n  def restartcmd\n    (@resource[:hasrestart] == :true) && [command(:rcservice), @resource[:name], :restart]\n  end\n\n  def startcmd\n    [command(:rcservice), @resource[:name], :start]\n  end\n\n  def stopcmd\n    [command(:rcservice), @resource[:name], :stop]\n  end\n\n  def statuscmd\n    ((@resource.provider.get(:hasstatus) == true) || (@resource[:hasstatus] == :true)) && [command(:rcservice), @resource[:name], :status]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/openwrt.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :openwrt, :parent => :init, :source => :init do\n  desc <<-EOT\n    Support for OpenWrt flavored init scripts.\n\n    Uses /etc/init.d/service_name enable, disable, and enabled.\n\n  EOT\n\n  defaultfor 'os.name' => :openwrt\n  confine 'os.name' => :openwrt\n\n  has_feature :enableable\n\n  def self.defpath\n    [\"/etc/init.d\"]\n  end\n\n  def enable\n    system(initscript, 'enable')\n  end\n\n  def disable\n    system(initscript, 'disable')\n  end\n\n  def enabled?\n    # We can't define the \"command\" for the init script, so we call system?\n    system(initscript, 'enabled') ? (return :true) : (return :false)\n  end\n\n  # Purposely leave blank so we fail back to ps based status detection\n  # As OpenWrt init script do not have status commands\n  def statuscmd\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/rcng.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :rcng, :parent => :bsd do\n  desc <<-EOT\n    RCng service management with rc.d\n  EOT\n\n  defaultfor 'os.name' => [:netbsd, :cargos]\n  confine 'os.name' => [:netbsd, :cargos]\n\n  def self.defpath\n    \"/etc/rc.d\"\n  end\n\n  # if the service file exists in rc.conf.d AND matches an expected pattern\n  # then it's already enabled\n  def enabled?\n    rcfile = File.join(rcconf_dir, @resource[:name])\n    if Puppet::FileSystem.exist?(rcfile)\n      File.open(rcfile).readlines.each do |line|\n        # Now look for something that looks like \"service=${service:=YES}\" or \"service=YES\"\n        if line =~ /^\\s*#{@resource[:name]}=(?:YES|\\${#{@resource[:name]}:=YES})/\n          return :true\n        end\n      end\n    end\n\n    :false\n  end\n\n  # enable service by creating a service file under rc.conf.d with the\n  # proper contents, or by modifying it's contents to to enable the service.\n  def enable\n    Dir.mkdir(rcconf_dir) unless Puppet::FileSystem.exist?(rcconf_dir)\n    rcfile = File.join(rcconf_dir, @resource[:name])\n    if Puppet::FileSystem.exist?(rcfile)\n      newcontents = []\n      File.open(rcfile).readlines.each do |line|\n        if line =~ /^\\s*#{@resource[:name]}=(NO|\\$\\{#{@resource[:name]}:NO\\})/\n          line = \"#{@resource[:name]}=${#{@resource[:name]}:=YES}\"\n        end\n        newcontents.push(line)\n      end\n      Puppet::Util.replace_file(rcfile, 0o644) do |f|\n        f.puts newcontents\n      end\n    else\n      Puppet::Util.replace_file(rcfile, 0o644) do |f|\n        f.puts \"%s=${%s:=YES}\\n\" % [@resource[:name], @resource[:name]]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/redhat.rb",
    "content": "# frozen_string_literal: true\n\n# Manage Red Hat services.  Start/stop uses /sbin/service and enable/disable uses chkconfig\n\nPuppet::Type.type(:service).provide :redhat, :parent => :init, :source => :init do\n  desc \"Red Hat's (and probably many others') form of `init`-style service\n    management. Uses `chkconfig` for service enabling and disabling.\n\n  \"\n\n  commands :chkconfig => \"/sbin/chkconfig\", :service => \"/sbin/service\"\n\n  defaultfor 'os.name' => :amazon, 'os.release.major' => %w[2017 2018]\n  defaultfor 'os.name' => :redhat, 'os.release.major' => (4..6).to_a\n  defaultfor 'os.family' => :suse, 'os.release.major' => %w[10 11]\n\n  # Remove the symlinks\n  def disable\n    # The off method operates on run levels 2,3,4 and 5 by default We ensure\n    # all run levels are turned off because the reset method may turn on the\n    # service in run levels 0, 1 and/or 6\n    # We're not using --del here because we want to disable the service only,\n    # and --del removes the service from chkconfig management\n    chkconfig(\"--level\", \"0123456\", @resource[:name], :off)\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, \"Could not disable #{name}: #{detail}\", detail.backtrace\n  end\n\n  def enabled?\n    name = @resource[:name]\n\n    begin\n      output = chkconfig name\n    rescue Puppet::ExecutionFailure\n      return :false\n    end\n\n    # For Suse OS family, chkconfig returns 0 even if the service is disabled or non-existent\n    # Therefore, check the output for '<name>  on' (or '<name>  B for boot services)\n    # to see if it is enabled\n    return :false unless Puppet.runtime[:facter].value('os.family') != 'Suse' || output =~ /^#{name}\\s+(on|B)$/\n\n    :true\n  end\n\n  # Don't support them specifying runlevels; always use the runlevels\n  # in the init scripts.\n  def enable\n    chkconfig(\"--add\", @resource[:name])\n    chkconfig(@resource[:name], :on)\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, \"Could not enable #{name}: #{detail}\", detail.backtrace\n  end\n\n  def initscript\n    raise Puppet::Error, \"Do not directly call the init script for '#{@resource[:name]}'; use 'service' instead\"\n  end\n\n  # use hasstatus=>true when its set for the provider.\n  def statuscmd\n    ((@resource.provider.get(:hasstatus) == true) || (@resource[:hasstatus] == :true)) && [command(:service), @resource[:name], \"status\"]\n  end\n\n  def restartcmd\n    (@resource[:hasrestart] == :true) && [command(:service), @resource[:name], \"restart\"]\n  end\n\n  def startcmd\n    [command(:service), @resource[:name], \"start\"]\n  end\n\n  def stopcmd\n    [command(:service), @resource[:name], \"stop\"]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/runit.rb",
    "content": "# frozen_string_literal: true\n\n# Daemontools service management\n#\n# author Brice Figureau <brice-puppet@daysofwonder.com>\nPuppet::Type.type(:service).provide :runit, :parent => :daemontools do\n  desc <<-'EOT'\n    Runit service management.\n\n    This provider manages daemons running supervised by Runit.\n    When detecting the service directory it will check, in order of preference:\n\n    * `/service`\n    * `/etc/service`\n    * `/var/service`\n\n    The daemon directory should be in one of the following locations:\n\n    * `/etc/sv`\n    * `/var/lib/service`\n\n    or this can be overridden in the service resource parameters:\n\n        service { 'myservice':\n          provider => 'runit',\n          path     => '/path/to/daemons',\n        }\n\n    This provider supports out of the box:\n\n    * start/stop\n    * enable/disable\n    * restart\n    * status\n\n\n  EOT\n\n  commands :sv => \"/usr/bin/sv\"\n\n  class << self\n    # this is necessary to autodetect a valid resource\n    # default path, since there is no standard for such directory.\n    def defpath\n      @defpath ||= [\"/var/lib/service\", \"/etc/sv\"].find do |path|\n        Puppet::FileSystem.exist?(path) && FileTest.directory?(path)\n      end\n      @defpath\n    end\n  end\n\n  # find the service dir on this node\n  def servicedir\n    unless @servicedir\n      [\"/service\", \"/etc/service\", \"/var/service\"].each do |path|\n        if Puppet::FileSystem.exist?(path)\n          @servicedir = path\n          break\n        end\n      end\n      raise \"Could not find service directory\" unless @servicedir\n    end\n    @servicedir\n  end\n\n  def status\n    begin\n      output = sv \"status\", daemon\n      return :running if output =~ /^run: /\n    rescue Puppet::ExecutionFailure => detail\n      unless detail.message =~ /(warning: |runsv not running$)/\n        raise Puppet::Error.new(\"Could not get status for service #{resource.ref}: #{detail}\", detail)\n      end\n    end\n    :stopped\n  end\n\n  def stop\n    sv \"stop\", service\n  end\n\n  def start\n    if enabled? != :true\n      enable\n      # Work around issue #4480\n      # runsvdir takes up to 5 seconds to recognize\n      # the symlink created by this call to enable\n      # TRANSLATORS 'runsvdir' is a linux service name and should not be translated\n      Puppet.info _(\"Waiting 5 seconds for runsvdir to discover service %{service}\") % { service: service }\n      sleep 5\n    end\n    sv \"start\", service\n  end\n\n  def restart\n    sv \"restart\", service\n  end\n\n  # disable by removing the symlink so that runit\n  # doesn't restart our service behind our back\n  # note that runit doesn't need to perform a stop\n  # before a disable\n  def disable\n    # unlink the daemon symlink to disable it\n    Puppet::FileSystem.unlink(service) if Puppet::FileSystem.symlink?(service)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/service.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :service do\n  desc \"The simplest form of service support.\"\n\n  def self.instances\n    []\n  end\n\n  # How to restart the process.\n  def restart\n    if @resource[:restart] or restartcmd\n      service_command(:restart)\n      nil\n    else\n      stop\n      start\n    end\n  end\n\n  # There is no default command, which causes other methods to be used\n  def restartcmd\n  end\n\n  # @deprecated because the exit status is not returned, use service_execute instead\n  def texecute(type, command, fof = true, squelch = false, combine = true)\n    begin\n      execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch, :combine => combine)\n    rescue Puppet::ExecutionFailure => detail\n      @resource.fail Puppet::Error, \"Could not #{type} #{@resource.ref}: #{detail}\", detail\n    end\n    nil\n  end\n\n  # @deprecated because the exitstatus is not returned, use service_command instead\n  def ucommand(type, fof = true)\n    c = @resource[type]\n    if c\n      cmd = [c]\n    else\n      cmd = [send(\"#{type}cmd\")].flatten\n    end\n    texecute(type, cmd, fof)\n  end\n\n  # Execute a command, failing the resource if the command fails.\n  #\n  # @return [Puppet::Util::Execution::ProcessOutput]\n  def service_execute(type, command, fof = true, squelch = false, combine = true)\n    execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch, :combine => combine)\n  rescue Puppet::ExecutionFailure => detail\n    @resource.fail Puppet::Error, \"Could not #{type} #{@resource.ref}: #{detail}\", detail\n  end\n\n  # Use either a specified command or the default for our provider.\n  #\n  # @return [Puppet::Util::Execution::ProcessOutput]\n  def service_command(type, fof = true)\n    c = @resource[type]\n    if c\n      cmd = [c]\n    else\n      cmd = [send(\"#{type}cmd\")].flatten\n    end\n    service_execute(type, cmd, fof)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/smf.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'timeout'\n\n# Solaris 10 SMF-style services.\nPuppet::Type.type(:service).provide :smf, :parent => :base do\n  desc <<-EOT\n    Support for Sun's new Service Management Framework.\n\n    When managing the enable property, this provider will try to preserve\n    the previous ensure state per the enableable semantics. On Solaris,\n    enabling a service starts it up while disabling a service stops it. Thus,\n    there's a chance for this provider to execute two operations when managing\n    the enable property. For example, if enable is set to true and the ensure\n    state is stopped, this provider will manage the service using two operations:\n    one to enable the service which will start it up, and another to stop the\n    service (without affecting its enabled status).\n\n    By specifying `manifest => \"/path/to/service.xml\"`, the SMF manifest will\n    be imported if it does not exist.\n  EOT\n\n  defaultfor 'os.family' => :solaris\n\n  confine 'os.family' => :solaris\n\n  commands :adm => \"/usr/sbin/svcadm\",\n           :svcs => \"/usr/bin/svcs\",\n           :svccfg => \"/usr/sbin/svccfg\"\n\n  has_feature :refreshable\n\n  def self.instances\n    service_instances = svcs(\"-H\", \"-o\", \"state,fmri\").split(\"\\n\")\n\n    # Puppet does not manage services in the legacy_run state, so filter those out.\n    service_instances.reject! { |line| line =~ /^legacy_run/ }\n\n    service_instances.collect! do |line|\n      state, fmri = line.split(/\\s+/)\n\n      status =  case state\n                when /online/; :running\n                when /maintenance/; :maintenance\n                when /degraded/; :degraded\n                else :stopped\n                end\n      new({ :name => fmri, :ensure => status })\n    end\n\n    service_instances\n  end\n\n  def initialize(*args)\n    super(*args)\n\n    # This hash contains the properties we need to sync. in our flush method.\n    #\n    # TODO (PUP-9051): Should we use @property_hash here? It seems like\n    # @property_hash should be empty by default and is something we can\n    # control so I think so?\n    @properties_to_sync = {}\n  end\n\n  def service_exists?\n    service_fmri\n    true\n  rescue Puppet::ExecutionFailure\n    false\n  end\n\n  def setup_service\n    return unless @resource[:manifest]\n    return if service_exists?\n\n    Puppet.notice(\"Importing #{@resource[:manifest]} for #{@resource[:name]}\")\n    svccfg(:import, @resource[:manifest])\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error.new(\"Cannot config #{@resource[:name]} to enable it: #{detail}\", detail)\n  end\n\n  # Returns the service's FMRI. We fail if multiple FMRIs correspond to\n  # @resource[:name].\n  #\n  # If the service does not exist or we fail to get any FMRIs from svcs,\n  # this method will raise a Puppet::Error\n  def service_fmri\n    return @fmri if @fmri\n\n    # `svcs -l` is better to use because we can detect service instances\n    # that have not yet been activated or enabled (i.e. it lets us detect\n    # services that svcadm has not yet touched). `svcs -H -o fmri` is a bit\n    # more limited.\n    lines = svcs(\"-l\", @resource[:name]).chomp.lines.to_a\n    lines.select! { |line| line =~ /^fmri/ }\n    fmris = lines.map! { |line| line.split(' ')[-1].chomp }\n    unless fmris.length == 1\n      raise Puppet::Error, _(\"Failed to get the FMRI of the %{service} service: The pattern '%{service}' matches multiple FMRIs! These are the FMRIs it matches: %{all_fmris}\") % { service: @resource[:name], all_fmris: fmris.join(', ') }\n    end\n\n    @fmri = fmris.first\n  end\n\n  # Returns true if the provider supports incomplete services.\n  def supports_incomplete_services?\n    Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value('os.release.full'), '11.1') >= 0\n  end\n\n  # Returns true if the service is complete. A complete service is a service that\n  # has the general/complete property defined.\n  def complete_service?\n    unless supports_incomplete_services?\n      raise Puppet::Error, _(\"Cannot query if the %{service} service is complete: The concept of complete/incomplete services was introduced in Solaris 11.1. You are on a Solaris %{release} machine.\") % { service: @resource[:name], release: Puppet.runtime[:facter].value('os.release.full') }\n    end\n\n    return @complete_service if @complete_service\n\n    # We need to use the service's FMRI when querying its config. because\n    # general/complete is an instance-specific property.\n    fmri = service_fmri\n\n    # Check if the general/complete property is defined. If it is undefined,\n    # then svccfg will not print anything to the console.\n    property_defn = svccfg(\"-s\", fmri, \"listprop\", \"general/complete\").chomp\n    @complete_service = !property_defn.empty?\n  end\n\n  def enable\n    @properties_to_sync[:enable] = true\n  end\n\n  def enabled?\n    return :false unless service_exists?\n\n    _property, _type, value = svccfg(\"-s\", service_fmri, \"listprop\", \"general/enabled\").split(' ')\n    value == 'true' ? :true : :false\n  end\n\n  def disable\n    @properties_to_sync[:enable] = false\n  end\n\n  def restartcmd\n    if Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value('os.release.full'), '11.2') >= 0\n      [command(:adm), :restart, \"-s\", service_fmri]\n    else\n      # Synchronous restart only supported in Solaris 11.2 and above\n      [command(:adm), :restart, service_fmri]\n    end\n  end\n\n  def service_states\n    # Gets the current and next state of the service. We have a next state because SMF\n    # manages services asynchronously. If there is no 'next' state, svcs will put a '-'\n    # to indicate as such.\n    current_state, next_state = svcs(\"-H\", \"-o\", \"state,nstate\", service_fmri).chomp.split(' ')\n\n    {\n      :current => current_state,\n      :next => next_state == \"-\" ? nil : next_state\n    }\n  end\n\n  # Wait for the service to transition into the specified state before returning.\n  # This is necessary due to the asynchronous nature of SMF services.\n  # desired_states should include only online, offline, disabled, or uninitialized.\n  # See PUP-5474 for long-term solution to this issue.\n  def wait(*desired_states)\n    Timeout.timeout(60) do\n      loop do\n        states = service_states\n        break if desired_states.include?(states[:current]) && states[:next].nil?\n\n        Kernel.sleep(1)\n      end\n    end\n  rescue Timeout::Error\n    raise Puppet::Error, \"Timed out waiting for #{@resource[:name]} to transition states\"\n  end\n\n  def start\n    @properties_to_sync[:ensure] = :running\n  end\n\n  def stop\n    @properties_to_sync[:ensure] = :stopped\n  end\n\n  def restart\n    # Wait for the service to actually start before returning.\n    super\n    wait('online')\n  end\n\n  def status\n    return super if @resource[:status]\n\n    begin\n      if supports_incomplete_services?\n        unless complete_service?\n          debug _(\"The %{service} service is incomplete so its status will be reported as :stopped. See `svcs -xv %{fmri}` for more details.\") % { service: @resource[:name], fmri: service_fmri }\n\n          return :stopped\n        end\n      end\n\n      # Get the current state and the next state. If there is a next state,\n      # use that for the state comparison.\n      states = service_states\n      state = states[:next] || states[:current]\n    rescue Puppet::ExecutionFailure => e\n      # TODO (PUP-8957): Should this be set back to INFO ?\n      debug \"Could not get status on service #{name} #{e}\"\n      return :stopped\n    end\n\n    case state\n    when \"online\"\n      :running\n    when \"offline\", \"disabled\", \"uninitialized\"\n      :stopped\n    when \"maintenance\"\n      :maintenance\n    when \"degraded\"\n      :degraded\n    when \"legacy_run\"\n      raise Puppet::Error,\n            \"Cannot manage legacy services through SMF\"\n    else\n      raise Puppet::Error,\n            \"Unmanageable state '#{state}' on service #{name}\"\n    end\n  end\n\n  # Helper that encapsulates the clear + svcadm [enable|disable]\n  # logic in one place. Makes it easy to test things out and also\n  # cleans up flush's code.\n  def maybe_clear_service_then_svcadm(cur_state, subcmd, flags)\n    # If the cur_state is maint or degraded, then we need to clear the service\n    # before we enable or disable it.\n    adm('clear', service_fmri) if [:maintenance, :degraded].include?(cur_state)\n    adm(subcmd, flags, service_fmri)\n  end\n\n  # The flush method is necessary for the SMF provider because syncing the enable and ensure\n  # properties are not independent operations like they are in most of our other service\n  # providers.\n  def flush\n    # We append the \"_\" because ensure is a Ruby keyword, and it is good to keep property\n    # variable names consistent with each other.\n    enable_ = @properties_to_sync[:enable]\n    ensure_ = @properties_to_sync[:ensure]\n\n    # All of the relevant properties are in sync., so we do not need to do\n    # anything here.\n    return if enable_.nil? and ensure_.nil?\n\n    # Set-up our service so that we know it will exist and so we can collect its fmri. Also\n    # simplifies the code. For a nonexistent service, one of enable or ensure will be true\n    # here (since we're syncing them), so we can fail early if setup_service fails.\n    setup_service\n    fmri = service_fmri\n\n    # Useful constants for operations involving multiple states\n    stopped = %w[offline disabled uninitialized]\n\n    # Get the current state of the service.\n    cur_state = status\n\n    if enable_.nil?\n      # Only ensure needs to be syncd. The -t flag tells svcadm to temporarily\n      # enable/disable the service, where the temporary status is gone upon\n      # reboot. This is exactly what we want, because we do not want to touch\n      # the enable property.\n      if ensure_ == :stopped\n        maybe_clear_service_then_svcadm(cur_state, 'disable', '-st')\n        wait(*stopped)\n      else # ensure == :running\n        maybe_clear_service_then_svcadm(cur_state, 'enable', '-rst')\n        wait('online')\n      end\n\n      return\n    end\n\n    # Here, enable is being syncd. svcadm starts the service if we enable it, or shuts it down if we\n    # disable it. However, we want our service to be in a final state, which is either whatever the\n    # new ensured value is, or what our original state was prior to enabling it.\n    #\n    # NOTE: Even if you try to set the general/enabled property with svccfg, SMF will still\n    # try to start or shut down the service. Plus, setting general/enabled with svccfg does not\n    # enable the service's dependencies, while svcadm handles this correctly.\n    #\n    # NOTE: We're treating :running and :degraded the same. The reason is b/c an SMF managed service\n    # can only enter the :degraded state if it is online. Since disabling the service also shuts it\n    # off, we cannot set it back to the :degraded state. Thus, it is best to lump :running and :degraded\n    # into the same category to maintain a consistent postcondition on the service's final state when\n    # enabling and disabling it.\n    final_state = ensure_ || cur_state\n    final_state = :running if final_state == :degraded\n\n    if enable_\n      maybe_clear_service_then_svcadm(cur_state, 'enable', '-rs')\n    else\n      maybe_clear_service_then_svcadm(cur_state, 'disable', '-s')\n    end\n\n    # We're safe with 'whens' here since self.status already errors on any\n    # unmanageable states.\n    case final_state\n    when :running\n      adm('enable', '-rst', fmri) unless enable_\n      wait('online')\n    when :stopped\n      adm('disable', '-st', fmri) if enable_\n      wait(*stopped)\n    when :maintenance\n      adm('mark', '-I', 'maintenance', fmri)\n      wait('maintenance')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/src.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'timeout'\n\n# AIX System Resource controller (SRC)\nPuppet::Type.type(:service).provide :src, :parent => :base do\n  desc \"Support for AIX's System Resource controller.\n\n  Services are started/stopped based on the `stopsrc` and `startsrc`\n  commands, and some services can be refreshed with `refresh` command.\n\n  Enabling and disabling services is not supported, as it requires\n  modifications to `/etc/inittab`. Starting and stopping groups of subsystems\n  is not yet supported.\n  \"\n\n  defaultfor 'os.name' => :aix\n  confine 'os.name' => :aix\n\n  optional_commands :stopsrc => \"/usr/bin/stopsrc\",\n                    :startsrc => \"/usr/bin/startsrc\",\n                    :refresh => \"/usr/bin/refresh\",\n                    :lssrc => \"/usr/bin/lssrc\",\n                    :lsitab => \"/usr/sbin/lsitab\",\n                    :mkitab => \"/usr/sbin/mkitab\",\n                    :rmitab => \"/usr/sbin/rmitab\",\n                    :chitab => \"/usr/sbin/chitab\"\n\n  has_feature :refreshable\n\n  def self.instances\n    services = lssrc('-S')\n    services.split(\"\\n\").reject { |x| x.strip.start_with? '#' }.collect do |line|\n      data = line.split(':')\n      service_name = data[0]\n      new(:name => service_name)\n    end\n  end\n\n  def startcmd\n    [command(:startsrc), \"-s\", @resource[:name]]\n  end\n\n  def stopcmd\n    [command(:stopsrc), \"-s\", @resource[:name]]\n  end\n\n  def default_runlevel\n    \"2\"\n  end\n\n  def default_action\n    \"once\"\n  end\n\n  def enabled?\n    output = execute([command(:lsitab), @resource[:name]], { :failonfail => false, :combine => true })\n    output.exitstatus == 0 ? :true : :false\n  end\n\n  def enable\n    mkitab(\"%s:%s:%s:%s\" % [@resource[:name], default_runlevel, default_action, startcmd.join(\" \")])\n  end\n\n  def disable\n    rmitab(@resource[:name])\n  end\n\n  # Wait for the service to transition into the specified state before returning.\n  # This is necessary due to the asynchronous nature of AIX services.\n  # desired_state should either be :running or :stopped.\n  def wait(desired_state)\n    Timeout.timeout(60) do\n      loop do\n        status = self.status\n        break if status == desired_state.to_sym\n\n        sleep(1)\n      end\n    end\n  rescue Timeout::Error\n    raise Puppet::Error, \"Timed out waiting for #{@resource[:name]} to transition states\"\n  end\n\n  def start\n    super\n    wait(:running)\n  end\n\n  def stop\n    super\n    wait(:stopped)\n  end\n\n  def restart\n    execute([command(:lssrc), \"-Ss\", @resource[:name]]).each_line do |line|\n      args = line.split(\":\")\n\n      next unless args[0] == @resource[:name]\n\n      # Subsystems with the -K flag can get refreshed (HUPed)\n      # While subsystems with -S (signals) must be stopped/started\n      method = args[11]\n      do_refresh = case method\n                   when \"-K\" then :true\n                   when \"-S\" then :false\n                   else self.fail(\"Unknown service communication method #{method}\")\n                   end\n\n      begin\n        if do_refresh == :true\n          execute([command(:refresh), \"-s\", @resource[:name]])\n        else\n          stop\n          start\n        end\n        return :true\n      rescue Puppet::ExecutionFailure => detail\n        raise Puppet::Error.new(\"Unable to restart service #{@resource[:name]}, error was: #{detail}\", detail)\n      end\n    end\n    self.fail(\"No such service found\")\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error.new(\"Cannot get status of #{@resource[:name]}, error was: #{detail}\", detail)\n  end\n\n  def status\n    execute([command(:lssrc), \"-s\", @resource[:name]]).each_line do |line|\n      args = line.split\n\n      # This is the header line\n      next unless args[0] == @resource[:name]\n\n      # PID is the 3rd field, but inoperative subsystems\n      # skip this so split doesn't work right\n      state = case args[-1]\n              when \"active\"      then :running\n              when \"inoperative\" then :stopped\n              end\n      Puppet.debug(\"Service #{@resource[:name]} is #{args[-1]}\")\n      return state\n    end\n  rescue Puppet::ExecutionFailure => detail\n    debug(detail.message)\n    :stopped\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/systemd.rb",
    "content": "# frozen_string_literal: true\n\n# Manage systemd services using systemctl\n\nrequire_relative '../../../puppet/file_system'\n\nPuppet::Type.type(:service).provide :systemd, :parent => :base do\n  desc \"Manages `systemd` services using `systemctl`.\n\n  Because `systemd` defaults to assuming the `.service` unit type, the suffix\n  may be omitted.  Other unit types (such as `.path`) may be managed by\n  providing the proper suffix.\"\n\n  commands :systemctl => \"systemctl\"\n\n  confine :true => Puppet::FileSystem.exist?('/proc/1/comm') && Puppet::FileSystem.read('/proc/1/comm').include?('systemd')\n\n  defaultfor 'os.family' => [:archlinux]\n  defaultfor 'os.family' => :redhat\n  notdefaultfor 'os.name' => :redhat, 'os.release.major' => (4..6).to_a # Use the \"RedHat\" service provider\n  defaultfor 'os.family' => :redhat, 'os.name' => :fedora\n  defaultfor 'os.family' => :suse\n  defaultfor 'os.family' => :coreos\n  defaultfor 'os.family' => :gentoo\n  notdefaultfor 'os.name' => :amazon, 'os.release.major' => %w[2017 2018]\n  defaultfor 'os.name' => :amazon, 'os.release.major' => %w[2 2023]\n  defaultfor 'os.name' => :debian\n  notdefaultfor 'os.name' => :debian, 'os.release.major' => %w[5 6 7] # These are using the \"debian\" method\n  defaultfor 'os.name' => :LinuxMint\n  notdefaultfor 'os.name' => :LinuxMint, 'os.release.major' => %w[10 11 12 13 14 15 16 17] # These are using upstart\n  defaultfor 'os.name' => :ubuntu\n  notdefaultfor 'os.name' => :ubuntu, 'os.release.major' => [\"10.04\", \"12.04\", \"14.04\", \"14.10\"] # These are using upstart\n  defaultfor 'os.name' => :cumuluslinux, 'os.release.major' => %w[3 4]\n  defaultfor 'os.name' => :raspbian, 'os.release.major' => %w[12]\n\n  def self.instances\n    i = []\n    output = systemctl('list-unit-files', '--type', 'service', '--full', '--all', '--no-pager')\n    output.scan(/^(\\S+)\\s+(disabled|enabled|masked|indirect|bad|static)\\s*([^-]\\S+)?\\s*$/i).each do |m|\n      Puppet.debug(\"#{m[0]} marked as bad by `systemctl`. It is recommended to be further checked.\") if m[1] == \"bad\"\n      i << new(:name => m[0])\n    end\n    i\n  rescue Puppet::ExecutionFailure\n    []\n  end\n\n  # Static services cannot be enabled or disabled manually. Indirect services\n  # should not be enabled or disabled due to limitations in systemd (see\n  # https://github.com/systemd/systemd/issues/6681).\n  def enabled_insync?(current)\n    case cached_enabled?[:output]\n    when 'static'\n      # masking static services is OK, but enabling/disabling them is not\n      if @resource[:enable] == :mask\n        current == @resource[:enable]\n      else\n        Puppet.debug(\"Unable to enable or disable static service #{@resource[:name]}\")\n        true\n      end\n    when 'indirect'\n      Puppet.debug(\"Service #{@resource[:name]} is in 'indirect' state and cannot be enabled/disabled\")\n      true\n    else\n      current == @resource[:enable]\n    end\n  end\n\n  # This helper ensures that the enable state cache is always reset\n  # after a systemctl enable operation. A particular service state is not guaranteed\n  # after such an operation, so the cache must be emptied to prevent inconsistencies\n  # in the provider's believed state of the service and the actual state.\n  # @param action [String,Symbol] One of 'enable', 'disable', 'mask' or 'unmask'\n  def systemctl_change_enable(action)\n    output = systemctl(action, '--', @resource[:name])\n  rescue => e\n    raise Puppet::Error, \"Could not #{action} #{name}: #{output}\", e.backtrace\n  ensure\n    @cached_enabled = nil\n  end\n\n  def disable\n    systemctl_change_enable(:disable)\n  end\n\n  def get_start_link_count\n    # Start links don't include '.service'. Just search for the service name.\n    if @resource[:name] =~ /\\.service/\n      link_name = @resource[:name].split('.')[0]\n    else\n      link_name = @resource[:name]\n    end\n\n    Dir.glob(\"/etc/rc*.d/S??#{link_name}\").length\n  end\n\n  def cached_enabled?\n    return @cached_enabled if @cached_enabled\n\n    cmd = [command(:systemctl), 'is-enabled', '--', @resource[:name]]\n    result = execute(cmd, :failonfail => false)\n    @cached_enabled = { output: result.chomp, exitcode: result.exitstatus }\n  end\n\n  def enabled?\n    output = cached_enabled?[:output]\n    code = cached_enabled?[:exitcode]\n\n    # The masked state is equivalent to the disabled state in terms of\n    # comparison so we only care to check if it is masked if we want to keep\n    # it masked.\n    #\n    # We only return :mask if we're trying to mask the service. This prevents\n    # flapping when simply trying to disable a masked service.\n    return :mask if (@resource[:enable] == :mask) && (output == 'masked')\n\n    # The indirect state indicates that the unit is not enabled.\n    return :false if output == 'indirect'\n    return :true if code == 0\n\n    if output.empty? && (code > 0) && Puppet.runtime[:facter].value('os.family').casecmp('debian').zero?\n      ret = debian_enabled?\n      return ret if ret\n    end\n\n    :false\n  end\n\n  # This method is required for Debian systems due to the way the SysVInit-Systemd\n  # compatibility layer works. When we are trying to manage a service which does not\n  # have a Systemd unit file, we need to go through the old init script to determine\n  # whether it is enabled or not. See PUP-5016 for more details.\n  #\n  def debian_enabled?\n    status = execute([\"/usr/sbin/invoke-rc.d\", \"--quiet\", \"--query\", @resource[:name], \"start\"], :failonfail => false)\n    if [104, 106].include?(status.exitstatus)\n      :true\n    elsif [101, 105].include?(status.exitstatus)\n      # 101 is action not allowed, which means we have to do the check manually.\n      # 105 is unknown, which generally means the initscript does not support query\n      # The debian policy states that the initscript should support methods of query\n      # For those that do not, perform the checks manually\n      # http://www.debian.org/doc/debian-policy/ch-opersys.html\n      if get_start_link_count >= 4\n        :true\n      else\n        :false\n      end\n    else\n      :false\n    end\n  end\n\n  # Define the daemon_reload? function to check if the unit is requiring to trigger a \"systemctl daemon-reload\"\n  # If the unit file is flagged with NeedDaemonReload=yes, then a systemd daemon-reload will be run.\n  # If multiple unit files have been updated, the first one flagged will trigger the daemon-reload for all of them.\n  # The others will be then flagged with NeedDaemonReload=no. So the command will run only once in a puppet run.\n  # This function is called only on start & restart unit options.\n  # Reference: (PUP-3483) Systemd provider doesn't scan for changed units\n  def daemon_reload?\n    cmd = [command(:systemctl), 'show', '--property=NeedDaemonReload', '--', @resource[:name]]\n    daemon_reload = execute(cmd, :failonfail => false).strip.split('=').last\n    if daemon_reload == 'yes'\n      daemon_reload_cmd = [command(:systemctl), 'daemon-reload']\n      execute(daemon_reload_cmd, :failonfail => false)\n    end\n  end\n\n  def enable\n    unmask\n    systemctl_change_enable(:enable)\n  end\n\n  def mask\n    disable if exist?\n    systemctl_change_enable(:mask)\n  end\n\n  def exist?\n    result = execute([command(:systemctl), 'cat', '--', @resource[:name]], :failonfail => false)\n    result.exitstatus == 0\n  end\n\n  def unmask\n    systemctl_change_enable(:unmask)\n  end\n\n  def restartcmd\n    [command(:systemctl), \"restart\", '--', @resource[:name]]\n  end\n\n  def startcmd\n    unmask\n    [command(:systemctl), \"start\", '--', @resource[:name]]\n  end\n\n  def stopcmd\n    [command(:systemctl), \"stop\", '--', @resource[:name]]\n  end\n\n  def statuscmd\n    [command(:systemctl), \"is-active\", '--', @resource[:name]]\n  end\n\n  def restart\n    daemon_reload?\n    super\n  rescue Puppet::Error => e\n    raise Puppet::Error, prepare_error_message(@resource[:name], 'restart', e)\n  end\n\n  def start\n    daemon_reload?\n    super\n  rescue Puppet::Error => e\n    raise Puppet::Error, prepare_error_message(@resource[:name], 'start', e)\n  end\n\n  def stop\n    super\n  rescue Puppet::Error => e\n    raise Puppet::Error, prepare_error_message(@resource[:name], 'stop', e)\n  end\n\n  def prepare_error_message(name, action, exception)\n    error_return = \"Systemd #{action} for #{name} failed!\\n\"\n    journalctl_command = \"journalctl -n 50 --since '5 minutes ago' -u #{name} --no-pager\"\n    Puppet.debug(\"Running journalctl command to get logs for systemd #{action} failure: #{journalctl_command}\")\n    journalctl_output = execute(journalctl_command)\n    error_return << \"journalctl log for #{name}:\\n#{journalctl_output}\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/upstart.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:service).provide :upstart, :parent => :debian do\n  START_ON = /^\\s*start\\s+on/\n  COMMENTED_START_ON = /^\\s*#+\\s*start\\s+on/\n  MANUAL = /^\\s*manual\\s*$/\n\n  desc \"Ubuntu service management with `upstart`.\n\n  This provider manages `upstart` jobs on Ubuntu. For `upstart` documentation,\n  see <http://upstart.ubuntu.com/>.\n  \"\n\n  confine :any => [\n    Puppet.runtime[:facter].value('os.name') == 'Ubuntu',\n    (Puppet.runtime[:facter].value('os.family') == 'RedHat' and Puppet.runtime[:facter].value('os.release.full') =~ /^6\\./),\n    (Puppet.runtime[:facter].value('os.name') == 'Amazon' and Puppet.runtime[:facter].value('os.release.major') =~ /\\d{4}/),\n    Puppet.runtime[:facter].value('os.name') == 'LinuxMint'\n  ]\n\n  defaultfor 'os.name' => :ubuntu, 'os.release.major' => [\"10.04\", \"12.04\", \"14.04\", \"14.10\"]\n  defaultfor 'os.name' => :LinuxMint, 'os.release.major' => %w[10 11 12 13 14 15 16 17]\n\n  commands :start => \"/sbin/start\",\n           :stop => \"/sbin/stop\",\n           :restart => \"/sbin/restart\",\n           :status_exec => \"/sbin/status\",\n           :initctl => \"/sbin/initctl\"\n\n  # We only want to use upstart as our provider if the upstart daemon is running.\n  # This can be checked by running `initctl version --quiet` on a machine that has\n  # upstart installed.\n  confine :true => -> { has_initctl? }\n\n  def self.has_initctl?\n    # Puppet::Util::Execution.execute does not currently work on jRuby.\n    # Unfortunately, since this confine is invoked whenever we check for\n    # provider suitability and since provider suitability is still checked\n    # on the master, this confine will still be invoked on the master. Thus\n    # to avoid raising an exception, we do an early return if we're running\n    # on jRuby.\n    return false if Puppet::Util::Platform.jruby?\n\n    begin\n      initctl('version', '--quiet')\n      true\n    rescue\n      false\n    end\n  end\n\n  # upstart developer haven't implemented initctl enable/disable yet:\n  # http://www.linuxplanet.com/linuxplanet/tutorials/7033/2/\n  has_feature :enableable\n\n  def self.instances\n    get_services(excludes) # Take exclude list from init provider\n  end\n\n  def self.excludes\n    excludes = super\n    if Puppet.runtime[:facter].value('os.family') == 'RedHat'\n      # Puppet cannot deal with services that have instances, so we have to\n      # ignore these services using instances on redhat based systems.\n      excludes += %w[serial tty]\n    end\n\n    excludes\n  end\n\n  def self.get_services(exclude = [])\n    instances = []\n    execpipe(\"#{command(:initctl)} list\") { |process|\n      process.each_line { |line|\n        # needs special handling of services such as network-interface:\n        # initctl list:\n        # network-interface (lo) start/running\n        # network-interface (eth0) start/running\n        # network-interface-security start/running\n        matcher = line.match(/^(network-interface)\\s\\(([^)]+)\\)/)\n        name = if matcher\n                 \"#{matcher[1]} INTERFACE=#{matcher[2]}\"\n               else\n                 matcher = line.match(/^(network-interface-security)\\s\\(([^)]+)\\)/)\n                 if matcher\n                   \"#{matcher[1]} JOB=#{matcher[2]}\"\n                 else\n                   line.split.first\n                 end\n               end\n        instances << new(:name => name)\n      }\n    }\n    instances.reject { |instance| exclude.include?(instance.name) }\n  end\n\n  def self.defpath\n    [\"/etc/init\", \"/etc/init.d\"]\n  end\n\n  def upstart_version\n    @upstart_version ||= initctl(\"--version\").match(/initctl \\(upstart ([^)]*)\\)/)[1]\n  end\n\n  # Where is our override script?\n  def overscript\n    @overscript ||= initscript.gsub(/\\.conf$/, \".override\")\n  end\n\n  def search(name)\n    # Search prefers .conf as that is what upstart uses\n    [\".conf\", \"\", \".sh\"].each do |suffix|\n      paths.each do |path|\n        service_name = name.match(/^(\\S+)/)[1]\n        fqname = File.join(path, service_name + suffix)\n        if Puppet::FileSystem.exist?(fqname)\n          return fqname\n        end\n\n        debug(\"Could not find #{name}#{suffix} in #{path}\")\n      end\n    end\n\n    raise Puppet::Error, \"Could not find init script or upstart conf file for '#{name}'\"\n  end\n\n  def enabled?\n    return super unless is_upstart?\n\n    script_contents = read_script_from(initscript)\n    if version_is_pre_0_6_7\n      enabled_pre_0_6_7?(script_contents)\n    elsif version_is_pre_0_9_0\n      enabled_pre_0_9_0?(script_contents)\n    elsif version_is_post_0_9_0\n      enabled_post_0_9_0?(script_contents, read_override_file)\n    end\n  end\n\n  def enable\n    return super unless is_upstart?\n\n    script_text = read_script_from(initscript)\n    if version_is_pre_0_9_0\n      enable_pre_0_9_0(script_text)\n    else\n      enable_post_0_9_0(script_text, read_override_file)\n    end\n  end\n\n  def disable\n    return super unless is_upstart?\n\n    script_text = read_script_from(initscript)\n    if version_is_pre_0_6_7\n      disable_pre_0_6_7(script_text)\n    elsif version_is_pre_0_9_0\n      disable_pre_0_9_0(script_text)\n    elsif version_is_post_0_9_0\n      disable_post_0_9_0(read_override_file)\n    end\n  end\n\n  def startcmd\n    is_upstart? ? [command(:start), @resource[:name]] : super\n  end\n\n  def stopcmd\n    is_upstart? ? [command(:stop),  @resource[:name]] : super\n  end\n\n  def restartcmd\n    is_upstart? ? (@resource[:hasrestart] == :true) && [command(:restart), @resource[:name]] : super\n  end\n\n  def statuscmd\n    is_upstart? ? nil : super # this is because upstart is broken with its return codes\n  end\n\n  def status\n    if (@resource[:hasstatus] == :false) ||\n       @resource[:status] ||\n       !is_upstart?\n      return super\n    end\n\n    output = status_exec(@resource[:name].split)\n    if output =~ %r{start/}\n      :running\n    else\n      :stopped\n    end\n  end\n\n  private\n\n  def is_upstart?(script = initscript)\n    Puppet::FileSystem.exist?(script) && script.match(%r{/etc/init/\\S+\\.conf})\n  end\n\n  def version_is_pre_0_6_7\n    Puppet::Util::Package.versioncmp(upstart_version, \"0.6.7\") == -1\n  end\n\n  def version_is_pre_0_9_0\n    Puppet::Util::Package.versioncmp(upstart_version, \"0.9.0\") == -1\n  end\n\n  def version_is_post_0_9_0\n    Puppet::Util::Package.versioncmp(upstart_version, \"0.9.0\") >= 0\n  end\n\n  def enabled_pre_0_6_7?(script_text)\n    # Upstart version < 0.6.7 means no manual stanza.\n    if script_text.match(START_ON)\n      :true\n    else\n      :false\n    end\n  end\n\n  def enabled_pre_0_9_0?(script_text)\n    # Upstart version < 0.9.0 means no override files\n    # So we check to see if an uncommented start on or manual stanza is the last one in the file\n    # The last one in the file wins.\n    enabled = :false\n    script_text.each_line do |line|\n      if line.match(START_ON)\n        enabled = :true\n      elsif line.match(MANUAL)\n        enabled = :false\n      end\n    end\n    enabled\n  end\n\n  def enabled_post_0_9_0?(script_text, over_text)\n    # This version has manual stanzas and override files\n    # So we check to see if an uncommented start on or manual stanza is the last one in the\n    # conf file and any override files. The last one in the file wins.\n    enabled = :false\n\n    script_text.each_line do |line|\n      if line.match(START_ON)\n        enabled = :true\n      elsif line.match(MANUAL)\n        enabled = :false\n      end\n    end\n    over_text.each_line do |line|\n      if line.match(START_ON)\n        enabled = :true\n      elsif line.match(MANUAL)\n        enabled = :false\n      end\n    end if over_text\n    enabled\n  end\n\n  def enable_pre_0_9_0(text)\n    # We also need to remove any manual stanzas to ensure that it is enabled\n    text = remove_manual_from(text)\n\n    if enabled_pre_0_9_0?(text) == :false\n      enabled_script =\n        if text.match(COMMENTED_START_ON)\n          uncomment_start_block_in(text)\n        else\n          add_default_start_to(text)\n        end\n    else\n      enabled_script = text\n    end\n\n    write_script_to(initscript, enabled_script)\n  end\n\n  def enable_post_0_9_0(script_text, over_text)\n    over_text = remove_manual_from(over_text)\n\n    if enabled_post_0_9_0?(script_text, over_text) == :false\n      if script_text.match(START_ON)\n        over_text << extract_start_on_block_from(script_text)\n      else\n        over_text << \"\\nstart on runlevel [2,3,4,5]\"\n      end\n    end\n\n    write_script_to(overscript, over_text)\n  end\n\n  def disable_pre_0_6_7(script_text)\n    disabled_script = comment_start_block_in(script_text)\n    write_script_to(initscript, disabled_script)\n  end\n\n  def disable_pre_0_9_0(script_text)\n    write_script_to(initscript, ensure_disabled_with_manual(script_text))\n  end\n\n  def disable_post_0_9_0(over_text)\n    write_script_to(overscript, ensure_disabled_with_manual(over_text))\n  end\n\n  def read_override_file\n    if Puppet::FileSystem.exist?(overscript)\n      read_script_from(overscript)\n    else\n      \"\"\n    end\n  end\n\n  def uncomment(line)\n    line.gsub(/^(\\s*)#+/, '\\1')\n  end\n\n  def remove_trailing_comments_from_commented_line_of(line)\n    line.gsub(/^(\\s*#+\\s*[^#]*).*/, '\\1')\n  end\n\n  def remove_trailing_comments_from(line)\n    line.gsub(/^(\\s*[^#]*).*/, '\\1')\n  end\n\n  def unbalanced_parens_on(line)\n    line.count('(') - line.count(')')\n  end\n\n  def remove_manual_from(text)\n    text.gsub(MANUAL, \"\")\n  end\n\n  def comment_start_block_in(text)\n    parens = 0\n    text.lines.map do |line|\n      if line.match(START_ON) || parens > 0\n        # If there are more opening parens than closing parens, we need to comment out a multiline 'start on' stanza\n        parens += unbalanced_parens_on(remove_trailing_comments_from(line))\n        \"#\" + line\n      else\n        line\n      end\n    end.join('')\n  end\n\n  def uncomment_start_block_in(text)\n    parens = 0\n    text.lines.map do |line|\n      if line.match(COMMENTED_START_ON) || parens > 0\n        parens += unbalanced_parens_on(remove_trailing_comments_from_commented_line_of(line))\n        uncomment(line)\n      else\n        line\n      end\n    end.join('')\n  end\n\n  def extract_start_on_block_from(text)\n    parens = 0\n    text.lines.map do |line|\n      if line.match(START_ON) || parens > 0\n        parens += unbalanced_parens_on(remove_trailing_comments_from(line))\n        line\n      end\n    end.join('')\n  end\n\n  def add_default_start_to(text)\n    text + \"\\nstart on runlevel [2,3,4,5]\"\n  end\n\n  def ensure_disabled_with_manual(text)\n    remove_manual_from(text) + \"\\nmanual\"\n  end\n\n  def read_script_from(filename)\n    File.read(filename)\n  end\n\n  def write_script_to(file, text)\n    Puppet::Util.replace_file(file, 0o644) do |f|\n      f.write(text)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/service/windows.rb",
    "content": "# frozen_string_literal: true\n\n# Windows Service Control Manager (SCM) provider\n\nPuppet::Type.type(:service).provide :windows, :parent => :service do\n  desc <<-EOT\n    Support for Windows Service Control Manager (SCM). This provider can\n    start, stop, enable, and disable services, and the SCM provides working\n    status methods for all services.\n\n    Control of service groups (dependencies) is not yet supported, nor is running\n    services as a specific user.\n  EOT\n\n  defaultfor 'os.name' => :windows\n  confine    'os.name' => :windows\n\n  has_feature :refreshable, :configurable_timeout, :manages_logon_credentials\n\n  def enable\n    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: { startup_type: :SERVICE_AUTO_START })\n  rescue => detail\n    raise Puppet::Error.new(_(\"Cannot enable %{resource_name}, error was: %{detail}\") % { resource_name: @resource[:name], detail: detail }, detail)\n  end\n\n  def disable\n    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: { startup_type: :SERVICE_DISABLED })\n  rescue => detail\n    raise Puppet::Error.new(_(\"Cannot disable %{resource_name}, error was: %{detail}\") % { resource_name: @resource[:name], detail: detail }, detail)\n  end\n\n  def manual_start\n    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: { startup_type: :SERVICE_DEMAND_START })\n  rescue => detail\n    raise Puppet::Error.new(_(\"Cannot enable %{resource_name} for manual start, error was: %{detail}\") % { resource_name: @resource[:name], detail: detail }, detail)\n  end\n\n  def delayed_start\n    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: { startup_type: :SERVICE_AUTO_START, delayed: true })\n  rescue => detail\n    raise Puppet::Error.new(_(\"Cannot enable %{resource_name} for delayed start, error was: %{detail}\") % { resource_name: @resource[:name], detail: detail }, detail)\n  end\n\n  def enabled?\n    return :false unless Puppet::Util::Windows::Service.exists?(@resource[:name])\n\n    start_type = Puppet::Util::Windows::Service.service_start_type(@resource[:name])\n    debug(\"Service #{@resource[:name]} start type is #{start_type}\")\n    case start_type\n    when :SERVICE_AUTO_START, :SERVICE_BOOT_START, :SERVICE_SYSTEM_START\n      :true\n    when :SERVICE_DEMAND_START\n      :manual\n    when :SERVICE_DELAYED_AUTO_START\n      :delayed\n    when :SERVICE_DISABLED\n      :false\n    else\n      raise Puppet::Error, _(\"Unknown start type: %{start_type}\") % { start_type: start_type }\n    end\n  rescue => detail\n    raise Puppet::Error.new(_(\"Cannot get start type %{resource_name}, error was: %{detail}\") % { resource_name: @resource[:name], detail: detail }, detail)\n  end\n\n  def start\n    if status == :paused\n      Puppet::Util::Windows::Service.resume(@resource[:name], timeout: @resource[:timeout])\n      return\n    end\n\n    # status == :stopped here\n\n    if enabled? == :false\n      # If disabled and not managing enable, respect disabled and fail.\n      if @resource[:enable].nil?\n        raise Puppet::Error, _(\"Will not start disabled service %{resource_name} without managing enable. Specify 'enable => false' to override.\") % { resource_name: @resource[:name] }\n      # Otherwise start. If enable => false, we will later sync enable and\n      # disable the service again.\n      elsif @resource[:enable] == :true\n        enable\n      else\n        manual_start\n      end\n    end\n    Puppet::Util::Windows::Service.start(@resource[:name], timeout: @resource[:timeout])\n  end\n\n  def stop\n    Puppet::Util::Windows::Service.stop(@resource[:name], timeout: @resource[:timeout])\n  end\n\n  def status\n    return :stopped unless Puppet::Util::Windows::Service.exists?(@resource[:name])\n\n    current_state = Puppet::Util::Windows::Service.service_state(@resource[:name])\n    state = case current_state\n            when :SERVICE_STOPPED, :SERVICE_STOP_PENDING\n              :stopped\n            when :SERVICE_PAUSED, :SERVICE_PAUSE_PENDING\n              :paused\n            when :SERVICE_RUNNING, :SERVICE_CONTINUE_PENDING, :SERVICE_START_PENDING\n              :running\n            else\n              raise Puppet::Error, _(\"Unknown service state '%{current_state}' for service '%{resource_name}'\") % { current_state: current_state, resource_name: @resource[:name] }\n            end\n    debug(\"Service #{@resource[:name]} is #{current_state}\")\n    state\n  rescue => detail\n    Puppet.warning(\"Status for service #{@resource[:name]} could not be retrieved: #{detail}\")\n    :stopped\n  end\n\n  def default_timeout\n    Puppet::Util::Windows::Service::DEFAULT_TIMEOUT\n  end\n\n  # returns all providers for all existing services and startup state\n  def self.instances\n    services = []\n    Puppet::Util::Windows::Service.services.each do |service_name, _|\n      services.push(new(:name => service_name))\n    end\n    services\n  end\n\n  def logonaccount_insync?(current)\n    @normalized_logon_account ||= normalize_logonaccount\n    @resource[:logonaccount] = @normalized_logon_account\n\n    insync = @resource[:logonaccount] == current\n    self.logonpassword = @resource[:logonpassword] if insync\n    insync\n  end\n\n  def logonaccount\n    return unless Puppet::Util::Windows::Service.exists?(@resource[:name])\n\n    Puppet::Util::Windows::Service.logon_account(@resource[:name])\n  end\n\n  def logonaccount=(value)\n    validate_logon_credentials\n    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: { logon_account: value, logon_password: @resource[:logonpassword] })\n    restart if @resource[:ensure] == :running && [:running, :paused].include?(status)\n  end\n\n  def logonpassword=(value)\n    validate_logon_credentials\n    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: { logon_password: value })\n  end\n\n  private\n\n  def normalize_logonaccount\n    logon_account = @resource[:logonaccount].sub(/^\\.\\\\/, \"#{Puppet::Util::Windows::ADSI.computer_name}\\\\\")\n    return 'LocalSystem' if Puppet::Util::Windows::User.localsystem?(logon_account)\n\n    @logonaccount_information ||= Puppet::Util::Windows::SID.name_to_principal(logon_account)\n    return logon_account unless @logonaccount_information\n    return \".\\\\#{@logonaccount_information.account}\" if @logonaccount_information.domain == Puppet::Util::Windows::ADSI.computer_name\n\n    @logonaccount_information.domain_account\n  end\n\n  def validate_logon_credentials\n    unless Puppet::Util::Windows::User.localsystem?(@normalized_logon_account)\n      raise Puppet::Error, \"\\\"#{@normalized_logon_account}\\\" is not a valid account\" unless @logonaccount_information && [:SidTypeUser, :SidTypeWellKnownGroup].include?(@logonaccount_information.account_type)\n\n      user_rights = Puppet::Util::Windows::User.get_rights(@logonaccount_information.domain_account) unless Puppet::Util::Windows::User.default_system_account?(@normalized_logon_account)\n      raise Puppet::Error, \"\\\"#{@normalized_logon_account}\\\" has the 'Log On As A Service' right set to denied.\" if user_rights =~ /SeDenyServiceLogonRight/\n      raise Puppet::Error, \"\\\"#{@normalized_logon_account}\\\" is missing the 'Log On As A Service' right.\" unless user_rights.nil? || user_rights =~ /SeServiceLogonRight/\n    end\n\n    is_a_predefined_local_account = Puppet::Util::Windows::User.default_system_account?(@normalized_logon_account) || @normalized_logon_account == 'LocalSystem'\n    account_info = @normalized_logon_account.split(\"\\\\\")\n    able_to_logon = Puppet::Util::Windows::User.password_is?(account_info[1], @resource[:logonpassword], account_info[0]) unless is_a_predefined_local_account\n    raise Puppet::Error, \"The given password is invalid for user '#{@normalized_logon_account}'.\" unless is_a_predefined_local_account || able_to_logon\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/aix.rb",
    "content": "# frozen_string_literal: true\n\n# User Puppet provider for AIX. It uses standard commands to manage users:\n#  mkuser, rmuser, lsuser, chuser\n#\n# Notes:\n# - AIX users can have expiry date defined with minute granularity,\n#   but Puppet does not allow it. There is a ticket open for that (#5431)\n#\n# - AIX maximum password age is in WEEKs, not days\n#\n# See https://puppet.com/docs/puppet/latest/provider_development.html\n# for more information\nrequire_relative '../../../puppet/provider/aix_object'\nrequire_relative '../../../puppet/util/posix'\nrequire 'tempfile'\nrequire 'date'\n\nPuppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do\n  desc \"User management for AIX.\"\n\n  defaultfor 'os.name' => :aix\n  confine 'os.name' => :aix\n\n  # Commands that manage the element\n  commands :list      => \"/usr/sbin/lsuser\"\n  commands :add       => \"/usr/bin/mkuser\"\n  commands :delete    => \"/usr/sbin/rmuser\"\n  commands :modify    => \"/usr/bin/chuser\"\n\n  commands :chpasswd  => \"/bin/chpasswd\"\n\n  # Provider features\n  has_features :manages_aix_lam\n  has_features :manages_homedir, :manages_passwords, :manages_shell\n  has_features :manages_expiry,  :manages_password_age\n  has_features :manages_local_users_and_groups\n\n  class << self\n    def group_provider\n      @group_provider ||= Puppet::Type.type(:group).provider(:aix)\n    end\n\n    # Define some Puppet Property => AIX Attribute (and vice versa)\n    # conversion functions here.\n\n    def gid_to_pgrp(provider, gid)\n      group = group_provider.find(gid, provider.ia_module_args)\n\n      group[:name]\n    end\n\n    def pgrp_to_gid(provider, pgrp)\n      group = group_provider.find(pgrp, provider.ia_module_args)\n\n      group[:gid]\n    end\n\n    def expiry_to_expires(expiry)\n      return '0' if expiry == \"0000-00-00\" || expiry.to_sym == :absent\n\n      DateTime.parse(expiry, \"%Y-%m-%d %H:%M\")\n              .strftime(\"%m%d%H%M%y\")\n    end\n\n    def expires_to_expiry(provider, expires)\n      return :absent if expires == '0'\n\n      unless (match_obj = /\\A(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)\\z/.match(expires))\n        # TRANSLATORS 'AIX' is the name of an operating system and should not be translated\n        Puppet.warning(_(\"Could not convert AIX expires date '%{expires}' on %{class_name}[%{resource_name}]\") % { expires: expires, class_name: provider.resource.class.name, resource_name: provider.resource.name })\n        return :absent\n      end\n\n      month = match_obj[1]\n      day = match_obj[2]\n      year = match_obj[-1]\n      \"20#{year}-#{month}-#{day}\"\n    end\n\n    # We do some validation before-hand to ensure the value's an Array,\n    # a String, etc. in the property. This routine does a final check to\n    # ensure our value doesn't have whitespace before we convert it to\n    # an attribute.\n    def groups_property_to_attribute(groups)\n      if groups =~ /\\s/\n        raise ArgumentError, _(\"Invalid value %{groups}: Groups must be comma separated!\") % { groups: groups }\n      end\n\n      groups\n    end\n\n    # We do not directly use the groups attribute value because that will\n    # always include the primary group, even if our user is not one of its\n    # members. Instead, we retrieve our property value by parsing the etc/group file,\n    # which matches what we do on our other POSIX platforms like Linux and Solaris.\n    #\n    # See https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.files/group_security.htm\n    def groups_attribute_to_property(provider, _groups)\n      Puppet::Util::POSIX.groups_of(provider.resource[:name]).join(',')\n    end\n  end\n\n  mapping puppet_property: :comment,\n          aix_attribute: :gecos\n\n  mapping puppet_property: :expiry,\n          aix_attribute: :expires,\n          property_to_attribute: method(:expiry_to_expires),\n          attribute_to_property: method(:expires_to_expiry)\n\n  mapping puppet_property: :gid,\n          aix_attribute: :pgrp,\n          property_to_attribute: method(:gid_to_pgrp),\n          attribute_to_property: method(:pgrp_to_gid)\n\n  mapping puppet_property: :groups,\n          property_to_attribute: method(:groups_property_to_attribute),\n          attribute_to_property: method(:groups_attribute_to_property)\n\n  mapping puppet_property: :home\n  mapping puppet_property: :shell\n\n  numeric_mapping puppet_property: :uid,\n                  aix_attribute: :id\n\n  numeric_mapping puppet_property: :password_max_age,\n                  aix_attribute: :maxage\n\n  numeric_mapping puppet_property: :password_min_age,\n                  aix_attribute: :minage\n\n  numeric_mapping puppet_property: :password_warn_days,\n                  aix_attribute: :pwdwarntime\n\n  # Now that we have all of our mappings, let's go ahead and make\n  # the resource methods (property getters + setters for our mapped\n  # properties + a getter for the attributes property).\n  mk_resource_methods\n\n  # Setting the primary group (pgrp attribute) on AIX causes both the\n  # current and new primary groups to be included in our user's groups,\n  # which is undesirable behavior. Thus, this custom setter resets the\n  # 'groups' property back to its previous value after setting the primary\n  # group.\n  def gid=(value)\n    old_pgrp = gid\n    cur_groups = groups\n\n    set(:gid, value)\n\n    begin\n      self.groups = cur_groups\n    rescue Puppet::Error => detail\n      raise Puppet::Error, _(\"Could not reset the groups property back to %{cur_groups} after setting the primary group on %{resource}[%{name}]. This means that the previous primary group of %{old_pgrp} and the new primary group of %{new_pgrp} have been added to %{cur_groups}. You will need to manually reset the groups property if this is undesirable behavior. Detail: %{detail}\") % { cur_groups: cur_groups, resource: @resource.class.name, name: @resource.name, old_pgrp: old_pgrp, new_pgrp: value, detail: detail }, detail.backtrace\n    end\n  end\n\n  # Helper function that parses the password from the given\n  # password filehandle. This is here to make testing easier\n  # for #password since we cannot configure Mocha to mock out\n  # a method and have it return a block's value, meaning we\n  # cannot test #password directly (not in a simple and obvious\n  # way, at least).\n  # @api private\n  def parse_password(f)\n    # From the docs, a user stanza is formatted as (newlines are explicitly\n    # stated here for clarity):\n    #   <user>:\\n\n    #     <attribute1>=<value1>\\n\n    #     <attribute2>=<value2>\\n\n    #\n    # First, find our user stanza\n    stanza = f.each_line.find { |line| line =~ /\\A#{@resource[:name]}:/ }\n    return :absent unless stanza\n\n    # Now find the password line, if it exists. Note our call to each_line here\n    # will pick up right where we left off.\n    match_obj = nil\n    f.each_line.find do |line|\n      # Break if we find another user stanza. This means our user\n      # does not have a password.\n      break if line =~ /^\\S+:$/\n\n      match_obj = /password\\s+=\\s+(\\S+)/.match(line)\n    end\n    return :absent unless match_obj\n\n    match_obj[1]\n  end\n\n  # - **password**\n  #    The user's password, in whatever encrypted format the local machine\n  #    requires. Be sure to enclose any value that includes a dollar sign ($)\n  #    in single quotes (').  Requires features manages_passwords.\n  #\n  # Retrieve the password parsing the /etc/security/passwd file.\n  def password\n    # AIX reference indicates this file is ASCII\n    # https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.files/passwd_security.htm\n    Puppet::FileSystem.open(\"/etc/security/passwd\", nil, \"r:ASCII\") do |f|\n      parse_password(f)\n    end\n  end\n\n  def password=(value)\n    user = @resource[:name]\n\n    begin\n      # Puppet execute does not support strings as input, only files.\n      # The password is expected to be in an encrypted format given -e is specified:\n      # https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.cmds1/chpasswd.htm\n      # /etc/security/passwd is specified as an ASCII file per the AIX documentation\n      tempfile = nil\n      tempfile = Tempfile.new(\"puppet_#{user}_pw\", :encoding => Encoding::ASCII)\n      tempfile << \"#{user}:#{value}\\n\"\n      tempfile.close()\n\n      # Options '-e', '-c', use encrypted password and clear flags\n      # Must receive \"user:enc_password\" as input\n      # command, arguments = {:failonfail => true, :combine => true}\n      # Fix for bugs #11200 and #10915\n      cmd = [self.class.command(:chpasswd), *ia_module_args, '-e', '-c']\n      execute_options = {\n        :failonfail => false,\n        :combine => true,\n        :stdinfile => tempfile.path\n      }\n      output = execute(cmd, execute_options)\n\n      # chpasswd can return 1, even on success (at least on AIX 6.1); empty output\n      # indicates success\n      if output != \"\"\n        raise Puppet::ExecutionFailure, \"chpasswd said #{output}\"\n      end\n    rescue Puppet::ExecutionFailure => detail\n      raise Puppet::Error, \"Could not set password on #{@resource.class.name}[#{@resource.name}]: #{detail}\", detail.backtrace\n    ensure\n      if tempfile\n        # Extra close will noop. This is in case the write to our tempfile\n        # fails.\n        tempfile.close()\n        tempfile.delete()\n      end\n    end\n  end\n\n  def create\n    super\n\n    # We specify the 'groups' AIX attribute in AixObject's create method\n    # when creating our user. However, this does not always guarantee that\n    # our 'groups' property is set to the right value. For example, the\n    # primary group will always be included in the 'groups' property. This is\n    # bad if we're explicitly managing the 'groups' property under inclusive\n    # membership, and we are not specifying the primary group in the 'groups'\n    # property value.\n    #\n    # Setting the groups property here a second time will ensure that our user is\n    # created and in the right state. Note that this is an idempotent operation,\n    # so if AixObject's create method already set it to the right value, then this\n    # will noop.\n    if (groups = @resource.should(:groups))\n      self.groups = groups\n    end\n\n    if (password = @resource.should(:password))\n      self.password = password\n    end\n  end\n\n  # Lists all instances of the given object, taking in an optional set\n  # of ia_module arguments. Returns an array of hashes, each hash\n  # having the schema\n  #   {\n  #     :name => <object_name>\n  #     :home => <object_home>\n  #   }\n  def list_all_homes(ia_module_args = [])\n    cmd = [command(:list), '-c', *ia_module_args, '-a', 'home', 'ALL']\n    parse_aix_objects(execute(cmd)).to_a.map do |object|\n      name = object[:name]\n      home = object[:attributes].delete(:home)\n\n      { name: name, home: home }\n    end\n  rescue => e\n    Puppet.debug(\"Could not list home of all users: #{e.message}\")\n    {}\n  end\n\n  # Deletes this instance resource\n  def delete\n    homedir = home\n    super\n    return unless @resource.managehome?\n\n    if !Puppet::Util.absolute_path?(homedir) || File.realpath(homedir) == '/' || Puppet::FileSystem.symlink?(homedir)\n      Puppet.debug(\"Can not remove home directory '#{homedir}' of user '#{@resource[:name]}'. Please make sure the path is not relative, symlink or '/'.\")\n      return\n    end\n\n    affected_home = list_all_homes.find { |info| info[:home].start_with?(File.realpath(homedir)) }\n    if affected_home\n      Puppet.debug(\"Can not remove home directory '#{homedir}' of user '#{@resource[:name]}' as it would remove the home directory '#{affected_home[:home]}' of user '#{affected_home[:name]}' also.\")\n      return\n    end\n\n    FileUtils.remove_entry_secure(homedir, true)\n  end\n\n  def deletecmd\n    [self.class.command(:delete), '-p'] + ia_module_args + [@resource[:name]]\n  end\n\n  # UNSUPPORTED\n  # - **profile_membership**\n  #    Whether specified roles should be treated as the only roles\n  #    of which the user is a member or whether they should merely\n  #    be treated as the minimum membership list.  Valid values are\n  #    `inclusive`, `minimum`.\n  # UNSUPPORTED\n  # - **profiles**\n  #    The profiles the user has.  Multiple profiles should be\n  #    specified as an array.  Requires features manages_solaris_rbac.\n  # UNSUPPORTED\n  # - **project**\n  #    The name of the project associated with a user  Requires features\n  #    manages_solaris_rbac.\n  # UNSUPPORTED\n  # - **role_membership**\n  #    Whether specified roles should be treated as the only roles\n  #    of which the user is a member or whether they should merely\n  #    be treated as the minimum membership list.  Valid values are\n  #    `inclusive`, `minimum`.\n  # UNSUPPORTED\n  # - **roles**\n  #    The roles the user has.  Multiple roles should be\n  #    specified as an array.  Requires features manages_roles.\n  # UNSUPPORTED\n  # - **key_membership**\n  #    Whether specified key value pairs should be treated as the only\n  #    attributes\n  #    of the user or whether they should merely\n  #    be treated as the minimum list.  Valid values are `inclusive`,\n  #    `minimum`.\n  # UNSUPPORTED\n  # - **keys**\n  #    Specify user attributes in an array of keyvalue pairs  Requires features\n  #    manages_solaris_rbac.\n  # UNSUPPORTED\n  # - **allowdupe**\n  #  Whether to allow duplicate UIDs.  Valid values are `true`, `false`.\n  # UNSUPPORTED\n  # - **auths**\n  #    The auths the user has.  Multiple auths should be\n  #    specified as an array.  Requires features manages_solaris_rbac.\n  # UNSUPPORTED\n  # - **auth_membership**\n  #    Whether specified auths should be treated as the only auths\n  #    of which the user is a member or whether they should merely\n  #    be treated as the minimum membership list.  Valid values are\n  #    `inclusive`, `minimum`.\n  # UNSUPPORTED\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/directoryservice.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet'\nrequire_relative '../../../puppet/util/plist' if Puppet.features.cfpropertylist?\nrequire 'base64'\n\nPuppet::Type.type(:user).provide :directoryservice do\n  desc \"User management on OS X.\"\n\n  ##                   ##\n  ## Provider Settings ##\n  ##                   ##\n\n  # Provider command declarations\n  commands :uuidgen      => '/usr/bin/uuidgen'\n  commands :dsimport     => '/usr/bin/dsimport'\n  commands :dscl         => '/usr/bin/dscl'\n  commands :dscacheutil  => '/usr/bin/dscacheutil'\n\n  # Provider confines and defaults\n  confine    'os.name' => :darwin\n  confine    :feature => :cfpropertylist\n  defaultfor 'os.name' => :darwin\n\n  # Need this to create getter/setter methods automagically\n  # This command creates methods that return @property_hash[:value]\n  mk_resource_methods\n\n  # JJM: OS X can manage passwords.\n  has_feature :manages_passwords\n\n  # 10.8 Passwords use a PBKDF2 salt value\n  has_features :manages_password_salt\n\n  # provider can set the user's shell\n  has_feature :manages_shell\n\n  ##               ##\n  ## Class Methods ##\n  ##               ##\n\n  # This method exists to map the dscl values to the correct Puppet\n  # properties. This stays relatively consistent, but who knows what\n  # Apple will do next year...\n  def self.ds_to_ns_attribute_map\n    {\n      'RecordName' => :name,\n      'PrimaryGroupID' => :gid,\n      'NFSHomeDirectory' => :home,\n      'UserShell' => :shell,\n      'UniqueID' => :uid,\n      'RealName' => :comment,\n      'Password' => :password,\n      'GeneratedUID' => :guid,\n      'IPAddress' => :ip_address,\n      'ENetAddress' => :en_address,\n      'GroupMembership' => :members,\n    }\n  end\n\n  def self.ns_to_ds_attribute_map\n    @ns_to_ds_attribute_map ||= ds_to_ns_attribute_map.invert\n  end\n\n  # Prefetching is necessary to use @property_hash inside any setter methods.\n  # self.prefetch uses self.instances to gather an array of user instances\n  # on the system, and then populates the @property_hash instance variable\n  # with attribute data for the specific instance in question (i.e. it\n  # gathers the 'is' values of the resource into the @property_hash instance\n  # variable so you don't have to read from the system every time you need\n  # to gather the 'is' values for a resource. The downside here is that\n  # populating this instance variable for every resource on the system\n  # takes time and front-loads your Puppet run.\n  def self.prefetch(resources)\n    instances.each do |prov|\n      resource = resources[prov.name]\n      if resource\n        resource.provider = prov\n      end\n    end\n  end\n\n  # This method assembles an array of provider instances containing\n  # information about every instance of the user type on the system (i.e.\n  # every user and its attributes). The `puppet resource` command relies\n  # on self.instances to gather an array of user instances in order to\n  # display its output.\n  def self.instances\n    get_all_users.collect do |user|\n      new(generate_attribute_hash(user))\n    end\n  end\n\n  # Return an array of hashes containing information about every user on\n  # the system.\n  def self.get_all_users\n    Puppet::Util::Plist.parse_plist(dscl('-plist', '.', 'readall', '/Users'))\n  end\n\n  # This method accepts an individual user plist, passed as a hash, and\n  # strips the dsAttrTypeStandard: prefix that dscl adds for each key.\n  # An attribute hash is assembled and returned from the properties\n  # supported by the user type.\n  def self.generate_attribute_hash(input_hash)\n    attribute_hash = {}\n    input_hash.each_key do |key|\n      ds_attribute = key.sub(\"dsAttrTypeStandard:\", \"\")\n      next unless ds_to_ns_attribute_map.keys.include?(ds_attribute)\n\n      ds_value = input_hash[key]\n      case ds_to_ns_attribute_map[ds_attribute]\n      when :gid, :uid\n        # OS X stores objects like uid/gid as strings.\n        # Try casting to an integer for these cases to be\n        # consistent with the other providers and the group type\n        # validation\n        begin\n          ds_value = Integer(ds_value[0])\n        rescue ArgumentError\n          ds_value = ds_value[0]\n        end\n      else ds_value = ds_value[0]\n      end\n      attribute_hash[ds_to_ns_attribute_map[ds_attribute]] = ds_value\n    end\n    attribute_hash[:ensure]         = :present\n    attribute_hash[:provider]       = :directoryservice\n    attribute_hash[:shadowhashdata] = input_hash['dsAttrTypeNative:ShadowHashData']\n\n    ##############\n    # Get Groups #\n    ##############\n    groups_array = []\n    get_list_of_groups.each do |group|\n      if group[\"dsAttrTypeStandard:GroupMembership\"] and group[\"dsAttrTypeStandard:GroupMembership\"].include?(attribute_hash[:name])\n        groups_array << group[\"dsAttrTypeStandard:RecordName\"][0]\n      end\n\n      if group[\"dsAttrTypeStandard:GroupMembers\"] and group[\"dsAttrTypeStandard:GroupMembers\"].include?(attribute_hash[:guid])\n        groups_array << group[\"dsAttrTypeStandard:RecordName\"][0]\n      end\n    end\n    attribute_hash[:groups] = groups_array.uniq.sort.join(',')\n\n    ################################\n    # Get Password/Salt/Iterations #\n    ################################\n    if attribute_hash[:shadowhashdata].nil? or attribute_hash[:shadowhashdata].empty?\n      attribute_hash[:password] = '*'\n    else\n      embedded_binary_plist = get_embedded_binary_plist(attribute_hash[:shadowhashdata])\n      if embedded_binary_plist['SALTED-SHA512-PBKDF2']\n        attribute_hash[:password]   = get_salted_sha512_pbkdf2('entropy', embedded_binary_plist, attribute_hash[:name])\n        attribute_hash[:salt]       = get_salted_sha512_pbkdf2('salt', embedded_binary_plist, attribute_hash[:name])\n        attribute_hash[:iterations] = get_salted_sha512_pbkdf2('iterations', embedded_binary_plist, attribute_hash[:name])\n      elsif embedded_binary_plist['SALTED-SHA512']\n        attribute_hash[:password] = get_salted_sha512(embedded_binary_plist)\n      end\n    end\n\n    attribute_hash\n  end\n\n  def self.get_os_version\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @os_version ||= Puppet.runtime[:facter].value('os.macosx.version.major')\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  # Use dscl to retrieve an array of hashes containing attributes about all\n  # of the local groups on the machine.\n  def self.get_list_of_groups\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @groups ||= Puppet::Util::Plist.parse_plist(dscl('-plist', '.', 'readall', '/Groups'))\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  # Perform a dscl lookup at the path specified for the specific keyname\n  # value. The value returned is the first item within the array returned\n  # from dscl\n  def self.get_attribute_from_dscl(path, username, keyname)\n    Puppet::Util::Plist.parse_plist(dscl('-plist', '.', 'read', \"/#{path}/#{username}\", keyname))\n  end\n\n  # The plist embedded in the ShadowHashData key is a binary plist. The\n  # plist library doesn't read binary plists, so we need to\n  # extract the binary plist, convert it to XML, and return it.\n  def self.get_embedded_binary_plist(shadow_hash_data)\n    embedded_binary_plist = Array(shadow_hash_data[0].delete(' ')).pack('H*')\n    convert_binary_to_hash(embedded_binary_plist)\n  end\n\n  # This method will accept a hash and convert it to a binary plist (string value).\n  def self.convert_hash_to_binary(plist_data)\n    Puppet.debug('Converting plist hash to binary')\n    Puppet::Util::Plist.dump_plist(plist_data, :binary)\n  end\n\n  # This method will accept a binary plist (as a string) and convert it to a hash.\n  def self.convert_binary_to_hash(plist_data)\n    Puppet.debug('Converting binary plist to hash')\n    Puppet::Util::Plist.parse_plist(plist_data)\n  end\n\n  # The salted-SHA512 password hash in 10.7 is stored in the 'SALTED-SHA512'\n  # key as binary data. That data is extracted and converted to a hex string.\n  def self.get_salted_sha512(embedded_binary_plist)\n    embedded_binary_plist['SALTED-SHA512'].unpack1(\"H*\")\n  end\n\n  # This method reads the passed embedded_binary_plist hash and returns values\n  # according to which field is passed.  Arguments passed are the hash\n  # containing the value read from the 'ShadowHashData' key in the User's\n  # plist, and the field to be read (one of 'entropy', 'salt', or 'iterations')\n  def self.get_salted_sha512_pbkdf2(field, embedded_binary_plist, user_name = \"\")\n    case field\n    when 'salt', 'entropy'\n      value = embedded_binary_plist['SALTED-SHA512-PBKDF2'][field]\n      if value.nil?\n        raise Puppet::Error, \"Invalid #{field} given for user #{user_name}\"\n      end\n\n      value.unpack1('H*')\n    when 'iterations'\n      Integer(embedded_binary_plist['SALTED-SHA512-PBKDF2'][field])\n    else\n      raise Puppet::Error, \"Puppet has tried to read an incorrect value from the user #{user_name} in the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'.\"\n    end\n  end\n\n  # In versions 10.5 and 10.6 of OS X, the password hash is stored in a file\n  # in the /var/db/shadow/hash directory that matches the GUID of the user.\n  def self.get_sha1(guid)\n    password_hash = nil\n    password_hash_file = \"#{password_hash_dir}/#{guid}\"\n    if Puppet::FileSystem.exist?(password_hash_file) and File.file?(password_hash_file)\n      raise Puppet::Error, \"Could not read password hash file at #{password_hash_file}\" unless File.readable?(password_hash_file)\n\n      f = File.new(password_hash_file)\n      password_hash = f.read\n      f.close\n    end\n    password_hash\n  end\n\n  ##                   ##\n  ## Ensurable Methods ##\n  ##                   ##\n\n  def exists?\n    begin\n      dscl '.', 'read', \"/Users/#{@resource.name}\"\n    rescue Puppet::ExecutionFailure => e\n      Puppet.debug(\"User was not found, dscl returned: #{e.inspect}\")\n      return false\n    end\n    true\n  end\n\n  # This method is called if ensure => present is passed and the exists?\n  # method returns false. Dscl will directly set most values, but the\n  # setter methods will be used for any exceptions.\n  def create\n    create_new_user(@resource.name)\n\n    # Retrieve the user's GUID\n    @guid = self.class.get_attribute_from_dscl('Users', @resource.name, 'GeneratedUID')['dsAttrTypeStandard:GeneratedUID'][0]\n\n    # Get an array of valid User type properties\n    valid_properties = Puppet::Type.type('User').validproperties\n\n    # Iterate through valid User type properties\n    valid_properties.each do |attribute|\n      next if attribute == :ensure\n\n      value = @resource.should(attribute)\n\n      # Value defaults\n      if value.nil?\n        value = case attribute\n                when :gid\n                  '20'\n                when :uid\n                  next_system_id\n                when :comment\n                  @resource.name\n                when :shell\n                  '/bin/bash'\n                when :home\n                  \"/Users/#{@resource.name}\"\n                else\n                  nil\n                end\n      end\n\n      # Ensure group names are converted to integers.\n      value = Puppet::Util.gid(value) if attribute == :gid\n\n      ## Set values ##\n      # For the :password and :groups properties, call the setter methods\n      # to enforce those values. For everything else, use dscl with the\n      # ns_to_ds_attribute_map to set the appropriate values.\n      next unless value != \"\" and !value.nil?\n\n      case attribute\n      when :password\n        self.password = value\n      when :iterations\n        self.iterations = value\n      when :salt\n        self.salt = value\n      when :groups\n        value.split(',').each do |group|\n          merge_attribute_with_dscl('Groups', group, 'GroupMembership', @resource.name)\n          merge_attribute_with_dscl('Groups', group, 'GroupMembers', @guid)\n        end\n      else\n        create_attribute_with_dscl('Users', @resource.name, self.class.ns_to_ds_attribute_map[attribute], value)\n      end\n    end\n  end\n\n  # This method is called when ensure => absent has been set.\n  # Deleting a user is handled by dscl\n  def delete\n    dscl '.', '-delete', \"/Users/#{@resource.name}\"\n  end\n\n  ##                       ##\n  ## Getter/Setter Methods ##\n  ##                       ##\n\n  # In the setter method we're only going to take action on groups for which\n  # the user is not currently a member.\n  def groups=(value)\n    guid = self.class.get_attribute_from_dscl('Users', @resource.name, 'GeneratedUID')['dsAttrTypeStandard:GeneratedUID'][0]\n    groups_to_add = value.split(',') - groups.split(',')\n    groups_to_add.each do |group|\n      merge_attribute_with_dscl('Groups', group, 'GroupMembership', @resource.name)\n      merge_attribute_with_dscl('Groups', group, 'GroupMembers', guid)\n    end\n  end\n\n  # If you thought GETTING a password was bad, try SETTING it. This method\n  # makes me want to cry. A thousand tears...\n  #\n  # I've been unsuccessful in tracking down a way to set the password for\n  # a user using dscl that DOESN'T require passing it as plaintext. We were\n  # also unable to get dsimport to work like this. Due to these downfalls,\n  # the sanest method requires opening the user's plist, dropping in the\n  # password hash, and serializing it back to disk. The problems with THIS\n  # method revolve around dscl. Any time you directly modify a user's plist,\n  # you need to flush the cache that dscl maintains.\n  def password=(value)\n    if self.class.get_os_version == '10.7'\n      if value.length != 136\n        raise Puppet::Error, \"OS X 10.7 requires a Salted SHA512 hash password of 136 characters.  Please check your password and try again.\"\n      end\n    else\n      if value.length != 256\n        raise Puppet::Error, \"OS X versions > 10.7 require a Salted SHA512 PBKDF2 password hash of 256 characters. Please check your password and try again.\"\n      end\n\n      assert_full_pbkdf2_password\n    end\n\n    # Methods around setting the password on OS X are the ONLY methods that\n    # cannot use dscl (because the only way to set it via dscl is by passing\n    # a plaintext password - which is bad). Because of this, we have to change\n    # the user's plist directly. DSCL has its own caching mechanism, which\n    # means that every time we call dscl in this provider we're not directly\n    # changing values on disk (instead, those calls are cached and written\n    # to disk according to Apple's prioritization algorithms). When Puppet\n    # needs to set the password property on OS X > 10.6, the provider has to\n    # tell dscl to write its cache to disk before modifying the user's\n    # plist. The 'dscacheutil -flushcache' command does this. Another issue\n    # is how fast Puppet makes calls to dscl and how long it takes dscl to\n    # enter those calls into its cache. We have to sleep for 2 seconds before\n    # flushing the dscl cache to allow all dscl calls to get INTO the cache\n    # first. This could be made faster (and avoid a sleep call) by finding\n    # a way to enter calls into the dscl cache faster. A sleep time of 1\n    # second would intermittently require a second Puppet run to set\n    # properties, so 2 seconds seems to be the minimum working value.\n    sleep 2\n    flush_dscl_cache\n    write_password_to_users_plist(value)\n\n    # Since we just modified the user's plist, we need to flush the ds cache\n    # again so dscl can pick up on the changes we made.\n    flush_dscl_cache\n  end\n\n  # The iterations and salt properties, like the password property, can only\n  # be modified by directly changing the user's plist. Because of this fact,\n  # we have to treat the ds cache just like you would in the password=\n  # method.\n  def iterations=(value)\n    if Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.7') > 0\n      assert_full_pbkdf2_password\n\n      sleep 3\n      flush_dscl_cache\n      users_plist = get_users_plist(@resource.name)\n      shadow_hash_data = get_shadow_hash_data(users_plist)\n      set_salted_pbkdf2(users_plist, shadow_hash_data, 'iterations', value)\n      flush_dscl_cache\n    end\n  end\n\n  # The iterations and salt properties, like the password property, can only\n  # be modified by directly changing the user's plist. Because of this fact,\n  # we have to treat the ds cache just like you would in the password=\n  # method.\n  def salt=(value)\n    if Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.15') >= 0\n      if value.length != 64\n        self.fail \"macOS versions 10.15 and higher require the salt to be 32-bytes. Since Puppet's user resource requires the value to be hex encoded, the length of the salt's string must be 64. Please check your salt and try again.\"\n      end\n    end\n    if Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.7') > 0\n      assert_full_pbkdf2_password\n\n      sleep 3\n      flush_dscl_cache\n      users_plist = get_users_plist(@resource.name)\n      shadow_hash_data = get_shadow_hash_data(users_plist)\n      set_salted_pbkdf2(users_plist, shadow_hash_data, 'salt', value)\n      flush_dscl_cache\n    end\n  end\n\n  #####\n  # Dynamically create setter methods for dscl properties\n  #####\n  #\n  # Setter methods are only called when a resource currently has a value for\n  # that property and it needs changed (true here since all of these values\n  # have a default that is set in the create method). We don't want to merge\n  # in additional values if an incorrect value is set, we want to CHANGE it.\n  # When using the -change argument in dscl, the old value needs to be passed\n  # first (followed by the new value). Because of this, we get the current\n  # value from the @property_hash variable and then use the value passed as\n  # the new value. Because we're prefetching instances of the provider, it's\n  # possible that the value determined at the start of the run may be stale\n  # (i.e. someone changed the value by hand during a Puppet run) - if that's\n  # the case we rescue the error from dscl and alert the user.\n  #\n  # In the event that the user doesn't HAVE a value for the attribute, the\n  # provider should use the -create option with dscl to add the attribute value\n  # for the user record\n  %w[home uid gid comment shell].each do |setter_method|\n    define_method(\"#{setter_method}=\") do |value|\n      if @property_hash[setter_method.intern]\n        if %w[home uid].include?(setter_method)\n          raise Puppet::Error, \"OS X version #{self.class.get_os_version} does not allow changing #{setter_method} using puppet\"\n        end\n\n        begin\n          dscl '.', '-change', \"/Users/#{resource.name}\", self.class.ns_to_ds_attribute_map[setter_method.intern], @property_hash[setter_method.intern], value\n        rescue Puppet::ExecutionFailure => e\n          raise Puppet::Error, \"Cannot set the #{setter_method} value of '#{value}' for user \" \\\n                               \"#{@resource.name} due to the following error: #{e.inspect}\", e.backtrace\n        end\n      else\n        begin\n          dscl '.', '-create', \"/Users/#{resource.name}\", self.class.ns_to_ds_attribute_map[setter_method.intern], value\n        rescue Puppet::ExecutionFailure => e\n          raise Puppet::Error, \"Cannot set the #{setter_method} value of '#{value}' for user \" \\\n                               \"#{@resource.name} due to the following error: #{e.inspect}\", e.backtrace\n        end\n      end\n    end\n  end\n\n  ##                ##\n  ## Helper Methods ##\n  ##                ##\n\n  def assert_full_pbkdf2_password\n    missing = [:password, :salt, :iterations].select { |parameter| @resource[parameter].nil? }\n\n    unless missing.empty?\n      raise Puppet::Error, \"OS X versions > 10\\.7 use PBKDF2 password hashes, which requires all three of salt, iterations, and password hash. This resource is missing: #{missing.join(', ')}.\"\n    end\n  end\n\n  def users_plist_dir\n    '/var/db/dslocal/nodes/Default/users'\n  end\n\n  def self.password_hash_dir\n    '/var/db/shadow/hash'\n  end\n\n  # This method will create a given value using dscl\n  def create_attribute_with_dscl(path, username, keyname, value)\n    set_attribute_with_dscl('-create', path, username, keyname, value)\n  end\n\n  # This method will merge in a given value using dscl\n  def merge_attribute_with_dscl(path, username, keyname, value)\n    set_attribute_with_dscl('-merge', path, username, keyname, value)\n  end\n\n  def set_attribute_with_dscl(dscl_command, path, username, keyname, value)\n    dscl '.', dscl_command, \"/#{path}/#{username}\", keyname, value\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, \"Could not set the dscl #{keyname} key with value: #{value} - #{detail.inspect}\", detail.backtrace\n  end\n\n  # Create the new user with dscl\n  def create_new_user(username)\n    dscl '.', '-create', \"/Users/#{username}\"\n  end\n\n  # Get the next available uid on the system by getting a list of user ids,\n  # sorting them, grabbing the last one, and adding a 1. Scientific stuff here.\n  def next_system_id(min_id = 20)\n    dscl_output = dscl '.', '-list', '/Users', 'uid'\n    # We're ok with throwing away negative uids here. Also, remove nil values.\n    user_ids = dscl_output.split.compact.collect { |l| l.to_i if l =~ /^\\d+$/ }\n    ids = user_ids.compact!.sort_by!(&:to_f)\n    # We're just looking for an unused id in our sorted array.\n    ids.each_index do |i|\n      next_id = ids[i] + 1\n      return next_id if ids[i + 1] != next_id and next_id >= min_id\n    end\n  end\n\n  # This method is only called on version 10.7 or greater. On 10.7 machines,\n  # passwords are set using a salted-SHA512 hash, and on 10.8 machines,\n  # passwords are set using PBKDF2. It's possible to have users on 10.8\n  # who have upgraded from 10.7 and thus have a salted-SHA512 password hash.\n  # If we encounter this, do what 10.8 does - remove that key and give them\n  # a 10.8-style PBKDF2 password.\n  def write_password_to_users_plist(value)\n    users_plist = get_users_plist(@resource.name)\n    shadow_hash_data = get_shadow_hash_data(users_plist)\n    if self.class.get_os_version == '10.7'\n      set_salted_sha512(users_plist, shadow_hash_data, value)\n    else\n      # It's possible that a user could exist on the system and NOT have\n      # a ShadowHashData key (especially if the system was upgraded from 10.6).\n      # In this case, a conditional check is needed to determine if the\n      # shadow_hash_data variable is a Hash (it would be false if the key\n      # didn't exist for this user on the system). If the shadow_hash_data\n      # variable IS a Hash and contains the 'SALTED-SHA512' key (indicating an\n      # older 10.7-style password hash), it will be deleted and a newer\n      # 10.8-style (PBKDF2) password hash will be generated.\n      if shadow_hash_data.instance_of?(Hash) && shadow_hash_data.has_key?('SALTED-SHA512')\n        shadow_hash_data.delete('SALTED-SHA512')\n      end\n\n      # Starting with macOS 11 Big Sur, the AuthenticationAuthority field\n      # could be missing entirely and without it the managed user cannot log in\n      if needs_sha512_pbkdf2_authentication_authority_to_be_added?(users_plist)\n        Puppet.debug(\"Adding 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash to user '#{@resource.name}'\")\n        merge_attribute_with_dscl('Users', @resource.name, 'AuthenticationAuthority', ERB::Util.html_escape(SHA512_PBKDF2_AUTHENTICATION_AUTHORITY))\n      end\n\n      set_salted_pbkdf2(users_plist, shadow_hash_data, 'entropy', value)\n    end\n  end\n\n  def flush_dscl_cache\n    dscacheutil '-flushcache'\n  end\n\n  def get_users_plist(username)\n    # This method will retrieve the data stored in a user's plist and\n    # return it as a native Ruby hash.\n    path = \"#{users_plist_dir}/#{username}.plist\"\n    Puppet::Util::Plist.read_plist_file(path)\n  end\n\n  # This method will return the binary plist that's embedded in the\n  # ShadowHashData key of a user's plist, or false if it doesn't exist.\n  def get_shadow_hash_data(users_plist)\n    if users_plist['ShadowHashData']\n      password_hash_plist = users_plist['ShadowHashData'][0]\n      self.class.convert_binary_to_hash(password_hash_plist)\n    else\n      false\n    end\n  end\n\n  # This method will check if authentication_authority key of a user's plist\n  # needs SALTED_SHA512_PBKDF2 to be added. This is a valid case for macOS 11 (Big Sur)\n  # where users created with `dscl` started to have this field missing\n  def needs_sha512_pbkdf2_authentication_authority_to_be_added?(users_plist)\n    authority = users_plist['authentication_authority']\n    return false if Puppet::Util::Package.versioncmp(self.class.get_os_version, '11.0.0') < 0 && authority && authority.include?(SHA512_PBKDF2_AUTHENTICATION_AUTHORITY)\n\n    Puppet.debug(\"User '#{@resource.name}' is missing the 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash\")\n    true\n  end\n\n  # This method will embed the binary plist data comprising the user's\n  # password hash (and Salt/Iterations value if the OS is 10.8 or greater)\n  # into the ShadowHashData key of the user's plist.\n  def set_shadow_hash_data(users_plist, binary_plist)\n    binary_plist = Puppet::Util::Plist.string_to_blob(binary_plist)\n    if users_plist.has_key?('ShadowHashData')\n      users_plist['ShadowHashData'][0] = binary_plist\n    else\n      users_plist['ShadowHashData'] = [binary_plist]\n    end\n    write_and_import_shadow_hash_data(users_plist['ShadowHashData'].first)\n  end\n\n  # This method writes the ShadowHashData plist in a temporary file,\n  # then imports it using dsimport. macOS versions 10.15 and newer do\n  # not support directly managing binary plists, so we have to use an\n  # intermediary.\n  # dsimport is an archaic utilitary with hard-to-find documentation\n  #\n  # See http://web.archive.org/web/20090106120111/http://support.apple.com/kb/TA21305?viewlocale=en_US\n  # for information regarding the dsimport syntax\n  def write_and_import_shadow_hash_data(data_plist)\n    Tempfile.create(\"dsimport_#{@resource.name}\", :encoding => Encoding::ASCII) do |dsimport_file|\n      dsimport_file.write <<~DSIMPORT\n        0x0A 0x5C 0x3A 0x2C dsRecTypeStandard:Users 2 dsAttrTypeStandard:RecordName base64:dsAttrTypeNative:ShadowHashData\n        #{@resource.name}:#{Base64.strict_encode64(data_plist)}\n      DSIMPORT\n      dsimport_file.flush\n      # Delete the user's existing ShadowHashData, since dsimport appends, not replaces\n      dscl('.', 'delete', \"/Users/#{@resource.name}\", 'ShadowHashData')\n      dsimport(dsimport_file.path, '/Local/Default', 'M')\n    end\n  end\n\n  # This method accepts an argument of a hex password hash, and base64\n  # decodes it into a format that OS X 10.7 and 10.8 will store\n  # in the user's plist.\n  def base64_decode_string(value)\n    Base64.decode64([[value].pack(\"H*\")].pack(\"m\").strip)\n  end\n\n  # Puppet requires a salted-sha512 password hash for 10.7 users to be passed\n  # in Hex, but the embedded plist stores that value as a Base64 encoded\n  # string. This method converts the string and calls the\n  # set_shadow_hash_data method to serialize and write the plist to disk.\n  def set_salted_sha512(users_plist, shadow_hash_data, value)\n    unless shadow_hash_data\n      shadow_hash_data = Hash.new\n      shadow_hash_data['SALTED-SHA512'] = ''.dup\n    end\n    shadow_hash_data['SALTED-SHA512'] = base64_decode_string(value)\n    binary_plist = self.class.convert_hash_to_binary(shadow_hash_data)\n    set_shadow_hash_data(users_plist, binary_plist)\n  end\n\n  # This method accepts a passed value and one of three fields: 'salt',\n  # 'entropy', or 'iterations'.  These fields correspond with the fields\n  # utilized in a PBKDF2 password hashing system\n  # (see https://en.wikipedia.org/wiki/PBKDF2 ) where 'entropy' is the\n  # password hash, 'salt' is the password hash salt value, and 'iterations'\n  # is an integer recommended to be > 10,000. The remaining arguments are\n  # the user's plist itself, and the shadow_hash_data hash containing the\n  # existing PBKDF2 values.\n  def set_salted_pbkdf2(users_plist, shadow_hash_data, field, value)\n    shadow_hash_data ||= Hash.new\n    shadow_hash_data['SALTED-SHA512-PBKDF2'] = Hash.new unless shadow_hash_data['SALTED-SHA512-PBKDF2']\n    case field\n    when 'salt', 'entropy'\n      shadow_hash_data['SALTED-SHA512-PBKDF2'][field] = Puppet::Util::Plist.string_to_blob(base64_decode_string(value))\n    when 'iterations'\n      shadow_hash_data['SALTED-SHA512-PBKDF2'][field] = Integer(value)\n    else\n      raise Puppet::Error \"Puppet has tried to set an incorrect field for the 'SALTED-SHA512-PBKDF2' hash. Acceptable fields are 'salt', 'entropy', or 'iterations'.\"\n    end\n\n    # on 10.8, this field *must* contain 8 stars, or authentication will\n    # fail.\n    users_plist['passwd'] = ('*' * 8)\n\n    # Convert shadow_hash_data to a binary plist, and call the\n    # set_shadow_hash_data method to serialize and write the data\n    # back to the user's plist.\n    binary_plist = self.class.convert_hash_to_binary(shadow_hash_data)\n    set_shadow_hash_data(users_plist, binary_plist)\n  end\n\n  private\n\n  SHA512_PBKDF2_AUTHENTICATION_AUTHORITY = ';ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2,SRP-RFC5054-4096-SHA512-PBKDF2>'\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/hpux.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.type(:user).provide :hpuxuseradd, :parent => :useradd do\n  desc \"User management for HP-UX. This provider uses the undocumented `-F`\n    switch to HP-UX's special `usermod` binary to work around the fact that\n    its standard `usermod` cannot make changes while the user is logged in.\n    New functionality provides for changing trusted computing passwords and\n    resetting password expirations under trusted computing.\"\n\n  defaultfor 'os.name' => \"hp-ux\"\n  confine 'os.name' => \"hp-ux\"\n\n  commands :modify => \"/usr/sam/lbin/usermod.sam\", :delete => \"/usr/sam/lbin/userdel.sam\", :add => \"/usr/sam/lbin/useradd.sam\"\n  options :comment, :method => :gecos\n  options :groups, :flag => \"-G\"\n  options :home, :flag => \"-d\", :method => :dir\n\n  verify :gid, \"GID must be an integer\" do |value|\n    value.is_a? Integer\n  end\n\n  verify :groups, \"Groups must be comma-separated\" do |value|\n    value !~ /\\s/\n  end\n\n  has_features :manages_homedir, :allows_duplicates, :manages_passwords\n\n  def deletecmd\n    super.insert(1, \"-F\")\n  end\n\n  def modifycmd(param, value)\n    cmd = super(param, value)\n    cmd.insert(1, \"-F\")\n    if trusted then\n      # Append an additional command to reset the password age to 0\n      # until a workaround with expiry module can be found for trusted\n      # computing.\n      cmd << \";\"\n      cmd << \"/usr/lbin/modprpw\"\n      cmd << \"-v\"\n      cmd << \"-l\"\n      cmd << resource.name.to_s\n    end\n    cmd\n  end\n\n  def password\n    # Password management routine for trusted and non-trusted systems\n    # temp=\"\"\n    while ent = Etc.getpwent() # rubocop:disable Lint/AssignmentInCondition\n      if ent.name == resource.name\n        temp = ent.name\n        break\n      end\n    end\n    Etc.endpwent()\n    unless temp\n      return nil\n    end\n\n    ent = Etc.getpwnam(resource.name)\n    if ent.passwd == \"*\"\n      # Either no password or trusted password, check trusted\n      file_name = \"/tcb/files/auth/#{resource.name.chars.first}/#{resource.name}\"\n      if File.file?(file_name)\n        # Found the tcb user for the specific user, now get passwd\n        File.open(file_name).each do |line|\n          next unless line =~ /u_pwd/\n\n          temp_passwd = line.split(\":\")[1].split(\"=\")[1]\n          ent.passwd = temp_passwd\n          return ent.passwd\n        end\n      else\n        debug \"No trusted computing user file #{file_name} found.\"\n      end\n    else\n      ent.passwd\n    end\n  end\n\n  def trusted\n    # Check to see if the HP-UX box is running in trusted compute mode\n    # UID for root should always be 0\n    trusted_sys = exec_getprpw('root', '-m uid')\n    trusted_sys.chomp == \"uid=0\"\n  end\n\n  def exec_getprpw(user, opts)\n    Puppet::Util::Execution.execute(\"/usr/lbin/getprpw #{opts} #{user}\", { :combine => true })\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/ldap.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/ldap'\n\nPuppet::Type.type(:user).provide :ldap, :parent => Puppet::Provider::Ldap do\n  desc \"User management via LDAP.\n\n    This provider requires that you have valid values for all of the\n    LDAP-related settings in `puppet.conf`, including `ldapbase`.  You will\n    almost definitely need settings for `ldapuser` and `ldappassword` in order\n    for your clients to write to LDAP.\n\n    Note that this provider will automatically generate a UID for you if\n    you do not specify one, but it is a potentially expensive operation,\n    as it iterates across all existing users to pick the appropriate next one.\"\n\n  confine :feature => :ldap, :false => (Puppet[:ldapuser] == \"\")\n\n  has_feature :manages_passwords, :manages_shell\n\n  manages(:posixAccount, :person).at(\"ou=People\").named_by(:uid).and.maps :name => :uid,\n                                                                          :password => :userPassword,\n                                                                          :comment => :cn,\n                                                                          :uid => :uidNumber,\n                                                                          :gid => :gidNumber,\n                                                                          :home => :homeDirectory,\n                                                                          :shell => :loginShell\n\n  # Use the last field of a space-separated array as\n  # the sn.  LDAP requires a surname, for some stupid reason.\n  manager.generates(:sn).from(:cn).with do |cn|\n    cn[0].split(/\\s+/)[-1]\n  end\n\n  # Find the next uid after the current largest uid.\n  provider = self\n  manager.generates(:uidNumber).with do\n    largest = 500\n    existing = provider.manager.search\n    if existing\n      existing.each do |hash|\n        value = hash[:uid]\n        next unless value\n\n        num = value[0].to_i\n        largest = num if num > largest\n      end\n    end\n    largest + 1\n  end\n\n  # Convert our gid to a group name, if necessary.\n  def gid=(value)\n    value = group2id(value) unless value.is_a?(Integer)\n\n    @property_hash[:gid] = value\n  end\n\n  # Find all groups this user is a member of in ldap.\n  def groups\n    # We want to cache the current result, so we know if we\n    # have to remove old values.\n    unless @property_hash[:groups]\n      result = group_manager.search(\"memberUid=#{name}\")\n      unless result\n        return @property_hash[:groups] = :absent\n      end\n\n      return @property_hash[:groups] = result.collect { |r| r[:name] }.sort.join(\",\")\n    end\n    @property_hash[:groups]\n  end\n\n  # Manage the list of groups this user is a member of.\n  def groups=(values)\n    should = values.split(\",\")\n\n    if groups == :absent\n      is = []\n    else\n      is = groups.split(\",\")\n    end\n\n    modes = {}\n    [is, should].flatten.uniq.each do |group|\n      # Skip it when they're in both\n      next if is.include?(group) and should.include?(group)\n\n      # We're adding a group.\n      modes[group] = :add and next unless is.include?(group)\n\n      # We're removing a group.\n      modes[group] = :remove and next unless should.include?(group)\n    end\n\n    modes.each do |group, form|\n      ldap_group = group_manager.find(group)\n      self.fail \"Could not find ldap group #{group}\" unless ldap_group\n\n      current = ldap_group[:members]\n\n      if form == :add\n        if current.is_a?(Array) and !current.empty?\n          new = current + [name]\n        else\n          new = [name]\n        end\n      else\n        new = current - [name]\n        new = :absent if new.empty?\n      end\n\n      group_manager.update(group, { :ensure => :present, :members => current }, { :ensure => :present, :members => new })\n    end\n  end\n\n  # Convert a gropu name to an id.\n  def group2id(group)\n    Puppet::Type.type(:group).provider(:ldap).name2id(group)\n  end\n\n  private\n\n  def group_manager\n    Puppet::Type.type(:group).provider(:ldap).manager\n  end\n\n  def group_properties(values)\n    if values.empty? or values == :absent\n      { :ensure => :present }\n    else\n      { :ensure => :present, :members => values }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/openbsd.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/error'\n\nPuppet::Type.type(:user).provide :openbsd, :parent => :useradd do\n  desc \"User management via `useradd` and its ilk for OpenBSD. Note that you\n    will need to install Ruby's shadow password library (package known as\n    `ruby-shadow`) if you wish to manage user passwords.\"\n\n  commands :add => \"useradd\",\n           :delete => \"userdel\",\n           :modify => \"usermod\",\n           :password => \"passwd\"\n\n  defaultfor 'os.name' => :openbsd\n  confine    'os.name' => :openbsd\n\n  options :home, :flag => \"-d\", :method => :dir\n  options :comment, :method => :gecos\n  options :groups, :flag => \"-G\"\n  options :password, :method => :sp_pwdp\n  options :loginclass, :flag => '-L', :method => :sp_loginclass\n  options :expiry, :method => :sp_expire,\n                   :munge => proc { |value|\n                               if value == :absent\n                                 ''\n                               else\n                                 # OpenBSD uses a format like \"january 1 1970\"\n                                 Time.parse(value).strftime('%B %d %Y')\n                               end\n                             },\n                   :unmunge => proc { |value|\n                                 if value == -1\n                                   :absent\n                                 else\n                                   # Expiry is days after 1970-01-01\n                                   (Date.new(1970, 1, 1) + value).strftime('%Y-%m-%d')\n                                 end\n                               }\n\n  [:expiry, :password, :loginclass].each do |shadow_property|\n    define_method(shadow_property) do\n      if Puppet.features.libshadow?\n        ent = Shadow::Passwd.getspnam(@resource.name)\n        if ent\n          method = self.class.option(shadow_property, :method)\n          # ruby-shadow may not be new enough (< 2.4.1) and therefore lack the\n          # sp_loginclass field.\n          begin\n            return unmunge(shadow_property, ent.send(method))\n          rescue\n            # TRANSLATORS 'ruby-shadow' is a Ruby gem library\n            Puppet.warning _(\"ruby-shadow doesn't support %{method}\") % { method: method }\n          end\n        end\n      end\n      :absent\n    end\n  end\n\n  has_features :manages_homedir, :manages_expiry, :system_users\n  has_features :manages_shell\n  if Puppet.features.libshadow?\n    has_features :manages_passwords, :manages_loginclass\n  end\n\n  def loginclass=(value)\n    set(\"loginclass\", value)\n  end\n\n  def modifycmd(param, value)\n    cmd = super\n    if param == :groups\n      idx = cmd.index('-G')\n      cmd[idx] = '-S'\n    end\n    cmd\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/pw.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice/pw'\nrequire 'open3'\n\nPuppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::PW do\n  desc \"User management via `pw` on FreeBSD and DragonFly BSD.\"\n\n  commands :pw => \"pw\"\n  has_features :manages_homedir, :allows_duplicates, :manages_passwords, :manages_expiry, :manages_shell\n\n  defaultfor 'os.name' => [:freebsd, :dragonfly]\n  confine    'os.name' => [:freebsd, :dragonfly]\n\n  options :home, :flag => \"-d\", :method => :dir\n  options :comment, :method => :gecos\n  options :groups, :flag => \"-G\"\n  options :expiry, :method => :expire, :munge => proc { |value|\n    value = '0000-00-00' if value == :absent\n    value.split(\"-\").reverse.join(\"-\")\n  }\n\n  verify :gid, \"GID must be an integer\" do |value|\n    value.is_a? Integer\n  end\n\n  verify :groups, \"Groups must be comma-separated\" do |value|\n    value !~ /\\s/\n  end\n\n  def addcmd\n    cmd = [command(:pw), \"useradd\", @resource[:name]]\n    @resource.class.validproperties.each do |property|\n      next if property == :ensure or property == :password\n\n      value = @resource.should(property)\n      if value and value != \"\"\n        cmd << flag(property) << munge(property, value)\n      end\n    end\n\n    cmd << \"-o\" if @resource.allowdupe?\n    cmd << \"-m\" if @resource.managehome?\n    cmd\n  end\n\n  def modifycmd(param, value)\n    if param == :expiry\n      # FreeBSD uses DD-MM-YYYY rather than YYYY-MM-DD\n      value = value.split(\"-\").reverse.join(\"-\")\n    end\n    cmd = super(param, value)\n    cmd << \"-m\" if @resource.managehome?\n    cmd\n  end\n\n  def deletecmd\n    cmd = super\n    cmd << \"-r\" if @resource.managehome?\n    cmd\n  end\n\n  def create\n    super\n\n    # Set the password after create if given\n    self.password = @resource[:password] if @resource[:password]\n  end\n\n  # use pw to update password hash\n  def password=(cryptopw)\n    Puppet.debug \"change password for user '#{@resource[:name]}' method called with hash [redacted]\"\n    stdin, _, _ = Open3.popen3(\"pw user mod #{@resource[:name]} -H 0\")\n    stdin.puts(cryptopw)\n    stdin.close\n    Puppet.debug \"finished password for user '#{@resource[:name]}' method called with hash [redacted]\"\n  end\n\n  # get password from /etc/master.passwd\n  def password\n    Puppet.debug \"checking password for user '#{@resource[:name]}' method called\"\n    current_passline = `getent passwd #{@resource[:name]}`\n    current_password = current_passline.chomp.split(':')[1] if current_passline\n    Puppet.debug \"finished password for user '#{@resource[:name]}' method called : [redacted]\"\n    current_password\n  end\n\n  def has_sensitive_data?(property = nil)\n    # Check for sensitive values?\n    properties = property ? [property] : Puppet::Type.type(:user).validproperties\n    properties.any? do |prop|\n      p = @resource.parameter(prop)\n      p && p.respond_to?(:is_sensitive) && p.is_sensitive\n    end\n  end\n\n  # Get expiry from system and convert to Puppet-style date\n  def expiry\n    expiry = get(:expiry)\n    expiry = :absent if expiry == 0\n\n    if expiry != :absent\n      t = Time.at(expiry)\n      expiry = \"%4d-%02d-%02d\" % [t.year, t.month, t.mday]\n    end\n\n    expiry\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/user_role_add.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util'\nrequire_relative '../../../puppet/util/user_attr'\nrequire 'date'\n\nPuppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source => :useradd do\n  desc \"User and role management on Solaris, via `useradd` and `roleadd`.\"\n\n  defaultfor 'os.family' => :solaris\n\n  commands :add => \"useradd\", :delete => \"userdel\", :modify => \"usermod\", :password => \"passwd\", :role_add => \"roleadd\", :role_delete => \"roledel\", :role_modify => \"rolemod\"\n  options :home, :flag => \"-d\", :method => :dir\n  options :comment, :method => :gecos\n  options :groups, :flag => \"-G\"\n  options :shell, :flag => \"-s\"\n  options :roles, :flag => \"-R\"\n  options :auths, :flag => \"-A\"\n  options :profiles, :flag => \"-P\"\n  options :password_min_age, :flag => \"-n\"\n  options :password_max_age, :flag => \"-x\"\n  options :password_warn_days, :flag => \"-w\"\n\n  verify :gid, \"GID must be an integer\" do |value|\n    value.is_a? Integer\n  end\n\n  verify :groups, \"Groups must be comma-separated\" do |value|\n    value !~ /\\s/\n  end\n\n  def shell=(value)\n    check_valid_shell\n    set(\"shell\", value)\n  end\n\n  has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac, :manages_roles, :manages_passwords, :manages_password_age, :manages_shell\n\n  def check_valid_shell\n    unless File.exist?(@resource.should(:shell))\n      raise(Puppet::Error, \"Shell #{@resource.should(:shell)} must exist\")\n    end\n    unless File.executable?(@resource.should(:shell).to_s)\n      raise(Puppet::Error, \"Shell #{@resource.should(:shell)} must be executable\")\n    end\n  end\n\n  # must override this to hand the keyvalue pairs\n  def add_properties\n    cmd = []\n    Puppet::Type.type(:user).validproperties.each do |property|\n      # skip the password because we can't create it with the solaris useradd\n      next if [:ensure, :password, :password_min_age, :password_max_age, :password_warn_days].include?(property)\n\n      # 1680 Now you can set the hashed passwords on solaris:lib/puppet/provider/user/user_role_add.rb\n      # the value needs to be quoted, mostly because -c might\n      # have spaces in it\n      value = @resource.should(property)\n      if value && value != \"\"\n        if property == :keys\n          cmd += build_keys_cmd(value)\n        else\n          cmd << flag(property) << value\n        end\n      end\n    end\n    cmd\n  end\n\n  def user_attributes\n    @user_attributes ||= UserAttr.get_attributes_by_name(@resource[:name])\n  end\n\n  def flush\n    @user_attributes = nil\n  end\n\n  def command(cmd)\n    cmd = \"role_#{cmd}\".intern if is_role? or (!exists? and @resource[:ensure] == :role)\n    super(cmd)\n  end\n\n  def is_role?\n    user_attributes and user_attributes[:type] == \"role\"\n  end\n\n  def run(cmd, msg)\n    execute(cmd)\n  rescue Puppet::ExecutionFailure => detail\n    raise Puppet::Error, \"Could not #{msg} #{@resource.class.name} #{@resource.name}: #{detail}\", detail.backtrace\n  end\n\n  def transition(type)\n    cmd = [command(:modify)]\n    cmd << \"-K\" << \"type=#{type}\"\n    cmd += add_properties\n    cmd << @resource[:name]\n  end\n\n  def create\n    if is_role?\n      run(transition(\"normal\"), \"transition role to\")\n    else\n      run(addcmd, \"create\")\n      cmd = passcmd\n      if cmd\n        run(cmd, \"change password policy for\")\n      end\n    end\n    # added to handle case when password is specified\n    self.password = @resource[:password] if @resource[:password]\n  end\n\n  def destroy\n    run(deletecmd, \"delete \" + (is_role? ? \"role\" : \"user\"))\n  end\n\n  def create_role\n    if exists? and !is_role?\n      run(transition(\"role\"), \"transition user to\")\n    else\n      run(addcmd, \"create role\")\n    end\n  end\n\n  def roles\n    user_attributes[:roles] if user_attributes\n  end\n\n  def auths\n    user_attributes[:auths] if user_attributes\n  end\n\n  def profiles\n    user_attributes[:profiles] if user_attributes\n  end\n\n  def project\n    user_attributes[:project] if user_attributes\n  end\n\n  def managed_attributes\n    [:name, :type, :roles, :auths, :profiles, :project]\n  end\n\n  def remove_managed_attributes\n    managed = managed_attributes\n    user_attributes.select { |k, _v| !managed.include?(k) }.each_with_object({}) { |array, hash| hash[array[0]] = array[1]; }\n  end\n\n  def keys\n    if user_attributes\n      # we have to get rid of all the keys we are managing another way\n      remove_managed_attributes\n    end\n  end\n\n  def build_keys_cmd(keys_hash)\n    cmd = []\n    keys_hash.each do |k, v|\n      cmd << \"-K\" << \"#{k}=#{v}\"\n    end\n    cmd\n  end\n\n  def keys=(keys_hash)\n    run([command(:modify)] + build_keys_cmd(keys_hash) << @resource[:name], \"modify attribute key pairs\")\n  end\n\n  # This helper makes it possible to test this on stub data without having to\n  # do too many crazy things!\n  def target_file_path\n    \"/etc/shadow\"\n  end\n  private :target_file_path\n\n  # Read in /etc/shadow, find the line for this user (skipping comments, because who knows) and return it\n  # No abstraction, all esoteric knowledge of file formats, yay\n  def shadow_entry\n    return @shadow_entry if defined? @shadow_entry\n\n    @shadow_entry =\n      File.readlines(target_file_path)\n          .reject { |r| r =~ /^[^\\w]/ }\n          .collect { |l| l.chomp.split(':', -1) } # Don't suppress empty fields (PUP-229)\n          .find { |user, _| user == @resource[:name] }\n  end\n\n  def password\n    return :absent unless shadow_entry\n\n    shadow_entry[1]\n  end\n\n  def password_min_age\n    return :absent unless shadow_entry\n\n    shadow_entry[3].empty? ? -1 : shadow_entry[3]\n  end\n\n  def password_max_age\n    return :absent unless shadow_entry\n\n    shadow_entry[4].empty? ? -1 : shadow_entry[4]\n  end\n\n  def password_warn_days\n    return :absent unless shadow_entry\n\n    shadow_entry[5].empty? ? -1 : shadow_entry[5]\n  end\n\n  def has_sensitive_data?(property = nil)\n    false\n  end\n\n  # Read in /etc/shadow, find the line for our used and rewrite it with the\n  # new pw.  Smooth like 80 grit sandpaper.\n  #\n  # Now uses the `replace_file` mechanism to minimize the chance that we lose\n  # data, but it is still terrible.  We still skip platform locking, so a\n  # concurrent `vipw -s` session will have no idea we risk data loss.\n  def password=(cryptopw)\n    shadow = File.read(target_file_path)\n\n    # Go Mifune loves the race here where we can lose data because\n    # /etc/shadow changed between reading it and writing it.\n    # --daniel 2012-02-05\n    Puppet::Util.replace_file(target_file_path, 0o640) do |fh|\n      shadow.each_line do |line|\n        line_arr = line.split(':')\n        if line_arr[0] == @resource[:name]\n          line_arr[1] = cryptopw\n          line_arr[2] = (Date.today - Date.new(1970, 1, 1)).to_i.to_s\n          line = line_arr.join(':')\n        end\n        fh.print line\n      end\n    end\n  rescue => detail\n    self.fail Puppet::Error, \"Could not write replace #{target_file_path}: #{detail}\", detail\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/useradd.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/provider/nameservice/objectadd'\nrequire 'date'\nrequire_relative '../../../puppet/util/libuser'\nrequire 'time'\nrequire_relative '../../../puppet/error'\n\nPuppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameService::ObjectAdd do\n  desc \"User management via `useradd` and its ilk.  Note that you will need to\n    install Ruby's shadow password library (often known as `ruby-libshadow`)\n    if you wish to manage user passwords.\n\n    To use the `forcelocal` parameter, you need to install the `libuser` package (providing\n    `/usr/sbin/lgroupadd` and `/usr/sbin/luseradd`).\"\n\n  commands :add => \"useradd\", :delete => \"userdel\", :modify => \"usermod\", :password => \"chage\", :chpasswd => \"chpasswd\"\n\n  options :home, :flag => \"-d\", :method => :dir\n  options :comment, :method => :gecos\n  options :groups, :flag => \"-G\"\n  options :password_min_age, :flag => \"-m\", :method => :sp_min\n  options :password_max_age, :flag => \"-M\", :method => :sp_max\n  options :password_warn_days, :flag => \"-W\", :method => :sp_warn\n  options :password, :method => :sp_pwdp\n  options :expiry, :method => :sp_expire,\n                   :munge => proc { |value|\n                               if value == :absent\n                                 if Puppet.runtime[:facter].value('os.name') == 'SLES' && Puppet.runtime[:facter].value('os.release.major') == \"11\"\n                                   -1\n                                 else\n                                   ''\n                                 end\n                               else\n                                 case Puppet.runtime[:facter].value('os.name')\n                                 when 'Solaris'\n                                   # Solaris uses %m/%d/%Y for useradd/usermod\n                                   expiry_year, expiry_month, expiry_day = value.split('-')\n                                   [expiry_month, expiry_day, expiry_year].join('/')\n                                 else\n                                   value\n                                 end\n                               end\n                             },\n                   :unmunge => proc { |value|\n                                 if value == -1\n                                   :absent\n                                 else\n                                   # Expiry is days after 1970-01-01\n                                   (Date.new(1970, 1, 1) + value).strftime('%Y-%m-%d')\n                                 end\n                               }\n\n  optional_commands :localadd => \"luseradd\", :localdelete => \"luserdel\", :localmodify => \"lusermod\", :localpassword => \"lchage\"\n  has_feature :manages_local_users_and_groups if Puppet.features.libuser?\n\n  def exists?\n    return !!localuid if @resource.forcelocal?\n\n    super\n  end\n\n  def uid\n    return localuid if @resource.forcelocal?\n\n    get(:uid)\n  end\n\n  def gid\n    return localgid if @resource.forcelocal?\n\n    get(:gid)\n  end\n\n  def comment\n    return localcomment if @resource.forcelocal?\n\n    get(:comment)\n  end\n\n  def shell\n    return localshell if @resource.forcelocal?\n\n    get(:shell)\n  end\n\n  def home\n    return localhome if @resource.forcelocal?\n\n    get(:home)\n  end\n\n  def groups\n    return localgroups if @resource.forcelocal?\n\n    super\n  end\n\n  def finduser(key, value)\n    passwd_file = '/etc/passwd'\n    passwd_keys = [:account, :password, :uid, :gid, :gecos, :directory, :shell]\n\n    unless @users\n      unless Puppet::FileSystem.exist?(passwd_file)\n        raise Puppet::Error, \"Forcelocal set for user resource '#{resource[:name]}', but #{passwd_file} does not exist\"\n      end\n\n      @users = []\n      Puppet::FileSystem.each_line(passwd_file) do |line|\n        user = line.chomp.split(':')\n        @users << passwd_keys.zip(user).to_h\n      end\n    end\n    @users.find { |param| param[key] == value } || false\n  end\n\n  def local_username\n    finduser(:uid, @resource.uid)\n  end\n\n  def localuid\n    user = finduser(:account, resource[:name])\n    return user[:uid] if user\n\n    false\n  end\n\n  def localgid\n    user = finduser(:account, resource[:name])\n    if user\n      begin\n        return Integer(user[:gid])\n      rescue ArgumentError\n        Puppet.debug(\"Non-numeric GID found in /etc/passwd for user #{resource[:name]}\")\n        return user[:gid]\n      end\n    end\n    false\n  end\n\n  def localcomment\n    user = finduser(:account, resource[:name])\n    user[:gecos]\n  end\n\n  def localshell\n    user = finduser(:account, resource[:name])\n    user[:shell]\n  end\n\n  def localhome\n    user = finduser(:account, resource[:name])\n    user[:directory]\n  end\n\n  def localgroups\n    @groups_of ||= {}\n    group_file = '/etc/group'\n    user = resource[:name]\n\n    return @groups_of[user] if @groups_of[user]\n\n    @groups_of[user] = []\n\n    unless Puppet::FileSystem.exist?(group_file)\n      raise Puppet::Error, \"Forcelocal set for user resource '#{user}', but #{group_file} does not exist\"\n    end\n\n    Puppet::FileSystem.each_line(group_file) do |line|\n      data = line.chomp.split(':')\n      if !data.empty? && data.last.split(',').include?(user)\n        @groups_of[user] << data.first\n      end\n    end\n\n    @groups_of[user]\n  end\n\n  def shell=(value)\n    check_valid_shell\n    set(:shell, value)\n  end\n\n  def groups=(value)\n    set(:groups, value)\n  end\n\n  def password=(value)\n    user = @resource[:name]\n    tempfile = Tempfile.new('puppet', :encoding => Encoding::UTF_8)\n    begin\n      # Puppet execute does not support strings as input, only files.\n      # The password is expected to be in an encrypted format given -e is specified:\n      tempfile << \"#{user}:#{value}\\n\"\n      tempfile.flush\n\n      # Options '-e' use encrypted password\n      # Must receive \"user:enc_password\" as input\n      # command, arguments = {:failonfail => true, :combine => true}\n      cmd = [command(:chpasswd), '-e']\n      execute_options = {\n        :failonfail => false,\n        :combine => true,\n        :stdinfile => tempfile.path,\n        :sensitive => has_sensitive_data?\n      }\n      output = execute(cmd, execute_options)\n    rescue => detail\n      tempfile.close\n      tempfile.delete\n      raise Puppet::Error, \"Could not set password on #{@resource.class.name}[#{@resource.name}]: #{detail}\", detail.backtrace\n    end\n\n    # chpasswd can return 1, even on success (at least on AIX 6.1); empty output\n    # indicates success\n    raise Puppet::ExecutionFailure, \"chpasswd said #{output}\" if output != ''\n  end\n\n  verify :gid, \"GID must be an integer\" do |value|\n    value.is_a? Integer\n  end\n\n  verify :groups, \"Groups must be comma-separated\" do |value|\n    value !~ /\\s/\n  end\n\n  has_features :manages_homedir, :allows_duplicates, :manages_expiry\n  has_features :system_users unless %w[HP-UX Solaris].include? Puppet.runtime[:facter].value('os.name')\n\n  has_features :manages_passwords, :manages_password_age if Puppet.features.libshadow?\n  has_features :manages_shell\n\n  def check_allow_dup\n    # We have to manually check for duplicates when using libuser\n    # because by default duplicates are allowed.  This check is\n    # to ensure consistent behaviour of the useradd provider when\n    # using both useradd and luseradd\n    if !@resource.allowdupe? && @resource.forcelocal?\n      if @resource.should(:uid) && finduser(:uid, @resource.should(:uid).to_s)\n        raise(Puppet::Error, \"UID #{@resource.should(:uid)} already exists, use allowdupe to force user creation\")\n      end\n    elsif @resource.allowdupe? && !@resource.forcelocal?\n      return [\"-o\"]\n    end\n    []\n  end\n\n  def check_valid_shell\n    unless File.exist?(@resource.should(:shell))\n      raise(Puppet::Error, \"Shell #{@resource.should(:shell)} must exist\")\n    end\n    unless File.executable?(@resource.should(:shell).to_s)\n      raise(Puppet::Error, \"Shell #{@resource.should(:shell)} must be executable\")\n    end\n  end\n\n  def check_manage_home\n    cmd = []\n    if @resource.managehome?\n      # libuser does not implement the -m flag\n      cmd << \"-m\" unless @resource.forcelocal?\n    else\n      osfamily = Puppet.runtime[:facter].value('os.family')\n      osversion = Puppet.runtime[:facter].value('os.release.major').to_i\n      # SLES 11 uses pwdutils instead of shadow, which does not have -M\n      # Solaris and OpenBSD use different useradd flavors\n      unless osfamily =~ /Solaris|OpenBSD/ || osfamily == 'Suse' && osversion <= 11\n        cmd << \"-M\"\n      end\n    end\n    cmd\n  end\n\n  def check_system_users\n    if self.class.system_users? && resource.system?\n      [\"-r\"]\n    else\n      []\n    end\n  end\n\n  # Add properties and flags but skipping password related properties due to\n  # security risks\n  def add_properties\n    cmd = []\n    # validproperties is a list of properties in undefined order\n    # sort them to have a predictable command line in tests\n    Puppet::Type.type(:user).validproperties.sort.each do |property|\n      value = get_value_for_property(property)\n      next if value.nil? || property == :password\n\n      # the value needs to be quoted, mostly because -c might\n      # have spaces in it\n      cmd << flag(property) << munge(property, value)\n    end\n    cmd\n  end\n\n  def get_value_for_property(property)\n    return nil if property == :ensure\n    return nil if property_manages_password_age?(property)\n    return nil if property == :groups and @resource.forcelocal?\n    return nil if property == :expiry and @resource.forcelocal?\n\n    value = @resource.should(property)\n    return nil if !value || value == \"\"\n\n    value\n  end\n\n  def has_sensitive_data?(property = nil)\n    # Check for sensitive values?\n    properties = property ? [property] : Puppet::Type.type(:user).validproperties\n    properties.any? do |prop|\n      p = @resource.parameter(prop)\n      p && p.respond_to?(:is_sensitive) && p.is_sensitive\n    end\n  end\n\n  def addcmd\n    if @resource.forcelocal?\n      cmd = [command(:localadd)]\n      @custom_environment = Puppet::Util::Libuser.getenv\n    else\n      cmd = [command(:add)]\n    end\n    if !@resource.should(:gid) && Puppet::Util.gid(@resource[:name])\n      cmd += [\"-g\", @resource[:name]]\n    end\n    cmd += add_properties\n    cmd += check_allow_dup\n    cmd += check_manage_home\n    cmd += check_system_users\n    cmd << @resource[:name]\n  end\n\n  def modifycmd(param, value)\n    if @resource.forcelocal?\n      case param\n      when :groups, :expiry\n        cmd = [command(:modify)]\n      else\n        cmd = [command(property_manages_password_age?(param) ? :localpassword : :localmodify)]\n      end\n      @custom_environment = Puppet::Util::Libuser.getenv\n    else\n      cmd = [command(property_manages_password_age?(param) ? :password : :modify)]\n    end\n    cmd << flag(param) << value\n    cmd += check_allow_dup if param == :uid\n    cmd << @resource[:name]\n\n    cmd\n  end\n\n  def deletecmd\n    if @resource.forcelocal?\n      cmd = [command(:localdelete)]\n      @custom_environment = Puppet::Util::Libuser.getenv\n    else\n      cmd = [command(:delete)]\n    end\n    # Solaris `userdel -r` will fail if the homedir does not exist.\n    if @resource.managehome? && (('Solaris' != Puppet.runtime[:facter].value('os.name')) || Dir.exist?(Dir.home(@resource[:name])))\n      cmd << '-r'\n    end\n    cmd << @resource[:name]\n  end\n\n  def passcmd\n    if @resource.forcelocal?\n      cmd = command(:localpassword)\n      @custom_environment = Puppet::Util::Libuser.getenv\n    else\n      cmd = command(:password)\n    end\n    age_limits = [:password_min_age, :password_max_age, :password_warn_days].select { |property| @resource.should(property) }\n    if age_limits.empty?\n      nil\n    else\n      [cmd, age_limits.collect { |property| [flag(property), @resource.should(property)] }, @resource[:name]].flatten\n    end\n  end\n\n  [:expiry, :password_min_age, :password_max_age, :password_warn_days, :password].each do |shadow_property|\n    define_method(shadow_property) do\n      if Puppet.features.libshadow?\n        ent = Shadow::Passwd.getspnam(@canonical_name)\n        if ent\n          method = self.class.option(shadow_property, :method)\n          return unmunge(shadow_property, ent.send(method))\n        end\n      end\n      :absent\n    end\n  end\n\n  def create\n    if @resource[:shell]\n      check_valid_shell\n    end\n    super\n    if @resource.forcelocal?\n      set(:groups, @resource[:groups]) if groups?\n      set(:expiry, @resource[:expiry]) if @resource[:expiry]\n    end\n    set(:password, @resource[:password]) if @resource[:password]\n  end\n\n  def groups?\n    !!@resource[:groups]\n  end\n\n  def property_manages_password_age?(property)\n    property.to_s =~ /password_.+_age|password_warn_days/\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider/user/windows_adsi.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\n\nPuppet::Type.type(:user).provide :windows_adsi do\n  desc \"Local user management for Windows.\"\n\n  defaultfor 'os.name' => :windows\n  confine    'os.name' => :windows\n\n  has_features :manages_homedir, :manages_passwords, :manages_roles\n\n  def initialize(value = {})\n    super(value)\n    @deleted = false\n  end\n\n  def user\n    @user ||= Puppet::Util::Windows::ADSI::User.new(@resource[:name])\n  end\n\n  def roles\n    Puppet::Util::Windows::User.get_rights(@resource[:name])\n  end\n\n  def roles=(value)\n    current = roles.split(',')\n    should  = value.split(',')\n\n    add_list = should - current\n    Puppet::Util::Windows::User.set_rights(@resource[:name], add_list) unless add_list.empty?\n\n    if @resource[:role_membership] == :inclusive\n      remove_list = current - should\n      Puppet::Util::Windows::User.remove_rights(@resource[:name], remove_list) unless remove_list.empty?\n    end\n  end\n\n  def groups\n    @groups ||= Puppet::Util::Windows::ADSI::Group.name_sid_hash(user.groups)\n    @groups.keys\n  end\n\n  def groups=(groups)\n    user.set_groups(groups, @resource[:membership] == :minimum)\n  end\n\n  def groups_insync?(current, should)\n    return false unless current\n\n    # By comparing account SIDs we don't have to worry about case\n    # sensitivity, or canonicalization of account names.\n\n    # Cannot use munge of the group property to canonicalize @should\n    # since the default array_matching comparison is not commutative\n\n    # dupes automatically weeded out when hashes built\n    current_groups = Puppet::Util::Windows::ADSI::Group.name_sid_hash(current)\n    specified_groups = Puppet::Util::Windows::ADSI::Group.name_sid_hash(should)\n\n    current_sids = current_groups.keys.to_a\n    specified_sids = specified_groups.keys.to_a\n\n    if @resource[:membership] == :inclusive\n      current_sids.sort == specified_sids.sort\n    else\n      (specified_sids & current_sids) == specified_sids\n    end\n  end\n\n  def groups_to_s(groups)\n    return '' if groups.nil? || !groups.is_a?(Array)\n\n    groups = groups.map do |group_name|\n      sid = Puppet::Util::Windows::SID.name_to_principal(group_name)\n      if sid.account =~ /\\\\/\n        account, _ = Puppet::Util::Windows::ADSI::Group.parse_name(sid.account)\n      else\n        account = sid.account\n      end\n      resource.debug(\"#{sid.domain}\\\\#{account} (#{sid.sid})\")\n      \"#{sid.domain}\\\\#{account}\"\n    end\n    groups.join(',')\n  end\n\n  def create\n    @user = Puppet::Util::Windows::ADSI::User.create(@resource[:name])\n    @user.password = @resource[:password]\n    @user.commit\n\n    [:comment, :home, :groups].each do |prop|\n      send(\"#{prop}=\", @resource[prop]) if @resource[prop]\n    end\n\n    if @resource.managehome?\n      Puppet::Util::Windows::User.load_profile(@resource[:name], @resource[:password])\n    end\n  end\n\n  def exists?\n    Puppet::Util::Windows::ADSI::User.exists?(@resource[:name])\n  end\n\n  def delete\n    # lookup sid before we delete account\n    sid = uid if @resource.managehome?\n\n    Puppet::Util::Windows::ADSI::User.delete(@resource[:name])\n\n    if sid\n      Puppet::Util::Windows::ADSI::UserProfile.delete(sid)\n    end\n\n    @deleted = true\n  end\n\n  # Only flush if we created or modified a user, not deleted\n  def flush\n    @user.commit if @user && !@deleted\n  end\n\n  def comment\n    user['Description']\n  end\n\n  def comment=(value)\n    user['Description'] = value\n  end\n\n  def home\n    user['HomeDirectory']\n  end\n\n  def home=(value)\n    user['HomeDirectory'] = value\n  end\n\n  def password\n    # avoid a LogonUserW style password check when the resource is not yet\n    # populated with a password (as is the case with `puppet resource user`)\n    return nil if @resource[:password].nil?\n\n    user.password_is?(@resource[:password]) ? @resource[:password] : nil\n  end\n\n  def password=(value)\n    if user.disabled?\n      info _(\"The user account '%s' is disabled; The password will still be changed\" % @resource[:name])\n    elsif user.locked_out?\n      info _(\"The user account '%s' is locked out; The password will still be changed\" % @resource[:name])\n    elsif user.expired?\n      info _(\"The user account '%s' is expired; The password will still be changed\" % @resource[:name])\n    end\n    user.password = value\n  end\n\n  def uid\n    Puppet::Util::Windows::SID.name_to_sid(@resource[:name])\n  end\n\n  def uid=(value)\n    fail \"uid is read-only\"\n  end\n\n  [:gid, :shell].each do |prop|\n    define_method(prop) { nil }\n    define_method(\"#{prop}=\") do |_v|\n      fail \"No support for managing property #{prop} of user #{@resource[:name]} on Windows\"\n    end\n  end\n\n  def self.instances\n    Puppet::Util::Windows::ADSI::User.map { |u| new(:ensure => :present, :name => u.name) }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/provider.rb",
    "content": "# frozen_string_literal: true\n\n# A Provider is an implementation of the actions that manage resources (of some type) on a system.\n# This class is the base class for all implementation of a Puppet Provider.\n#\n# Concepts:\n#--\n# * **Confinement** - confinement restricts providers to only be applicable under certain conditions.\n#    It is possible to confine a provider several different ways:\n#    * the included {#confine} method which provides filtering on fact, feature, existence of files, or a free form\n#      predicate.\n#    * the {commands} method that filters on the availability of given system commands.\n# * **Property hash** - the important instance variable `@property_hash` contains all current state values\n#   for properties (it is lazily built). It is important that these values are managed appropriately in the\n#   methods {instances}, {prefetch}, and in methods that alters the current state (those that change the\n#   lifecycle (creates, destroys), or alters some value reflected backed by a property).\n# * **Flush** - is a hook that is called once per resource when everything has been applied. The intent is\n#   that an implementation may defer modification of the current state typically done in property setters\n#   and instead record information that allows flush to perform the changes more efficiently.\n# * **Execution Methods** -  The execution methods provides access to execution of arbitrary commands.\n#   As a convenience execution methods are available on both the instance and the class of a provider since a\n#   lot of provider logic switch between these contexts fairly freely.\n# * **System Entity/Resource** - this documentation uses the term \"system entity\" for system resources to make\n#   it clear if talking about a resource on the system being managed (e.g. a file in the file system)\n#   or about a description of such a resource (e.g. a Puppet Resource).\n# * **Resource Type** - this is an instance of Type that describes a classification of instances of Resource (e.g.\n#   the `File` resource type describes all instances of `file` resources).\n#   (The term is used to contrast with \"type\" in general, and specifically to contrast with the implementation\n#   class of Resource or a specific Type).\n#\n# @note An instance of a Provider is associated with one resource.\n#\n# @note Class level methods are only called once to configure the provider (when the type is created), and not\n#   for each resource the provider is operating on.\n#   The instance methods are however called for each resource.\n#\n# @api public\n#\nclass Puppet::Provider\n  include Puppet::Util\n  include Puppet::Util::Errors\n  include Puppet::Util::Warnings\n  extend Puppet::Util::Warnings\n\n  require_relative '../puppet/confiner'\n  require_relative 'provider/command'\n\n  extend Puppet::Confiner\n\n  Puppet::Util.logmethods(self, true)\n\n  class << self\n    # Include the util module so we have access to things like 'which'\n    include Puppet::Util, Puppet::Util::Docs\n    include Puppet::Util::Logging\n\n    # @return [String] The name of the provider\n    attr_accessor :name\n\n    #\n    # @todo Original = _\"The source parameter exists so that providers using the same\n    #   source can specify this, so reading doesn't attempt to read the\n    #   same package multiple times.\"_ This seems to be a package type specific attribute. Is this really\n    #   used?\n    #\n    # @return [???] The source is WHAT?\n    attr_writer :source\n\n    # @todo What is this type? A reference to a Puppet::Type ?\n    # @return [Puppet::Type] the resource type (that this provider is ... WHAT?)\n    #\n    attr_accessor :resource_type\n\n    # @!attribute [r] doc\n    #   The (full) documentation for this provider class. The documentation for the provider class itself\n    #   should be set with the DSL method {desc=}. Setting the documentation with with {doc=} has the same effect\n    #   as setting it with {desc=} (only the class documentation part is set). In essence this means that\n    #   there is no getter for the class documentation part (since the getter returns the full\n    #   documentation when there are additional contributors).\n    #\n    #   @return [String] Returns the full documentation for the provider.\n    # @see Puppet::Utils::Docs\n    # @comment This is puzzling ... a write only doc attribute??? The generated setter never seems to be\n    #   used, instead the instance variable @doc is set in the `desc` method. This seems wrong. It is instead\n    #   documented as a read only attribute (to get the full documentation). Also see doc below for\n    #   desc.\n    # @!attribute [w] desc\n    #   Sets the documentation of this provider class. (The full documentation is read via the\n    #   {doc} attribute).\n    #\n    #   @dsl type\n    #\n    attr_writer :doc\n  end\n\n  # @return [???] This resource is what? Is an instance of a provider attached to one particular Puppet::Resource?\n  #\n  attr_accessor :resource\n\n  # Convenience methods - see class method with the same name.\n  # @return (see self.execute)\n  def execute(*args)\n    Puppet::Util::Execution.execute(*args)\n  end\n\n  # (see Puppet::Util::Execution.execute)\n  def self.execute(*args)\n    Puppet::Util::Execution.execute(*args)\n  end\n\n  # Convenience methods - see class method with the same name.\n  # @return (see self.execpipe)\n  def execpipe(*args, &block)\n    Puppet::Util::Execution.execpipe(*args, &block)\n  end\n\n  # (see Puppet::Util::Execution.execpipe)\n  def self.execpipe(*args, &block)\n    Puppet::Util::Execution.execpipe(*args, &block)\n  end\n\n  # Returns the absolute path to the executable for the command referenced by the given name.\n  # @raise [Puppet::DevError] if the name does not reference an existing command.\n  # @return [String] the absolute path to the found executable for the command\n  # @see which\n  # @api public\n  def self.command(name)\n    name = name.intern\n\n    command = @commands[name] if defined?(@commands)\n    command = superclass.command(name) if !command && superclass.respond_to?(:command)\n\n    unless command\n      raise Puppet::DevError, _(\"No command %{command} defined for provider %{provider}\") % { command: name, provider: self.name }\n    end\n\n    which(command)\n  end\n\n  # Confines this provider to be suitable only on hosts where the given commands are present.\n  # Also see {Puppet::Confiner#confine} for other types of confinement of a provider by use of other types of\n  # predicates.\n  #\n  # @note It is preferred if the commands are not entered with absolute paths as this allows puppet\n  #   to search for them using the PATH variable.\n  #\n  # @param command_specs [Hash{String => String}] Map of name to command that the provider will\n  #   be executing on the system. Each command is specified with a name and the path of the executable.\n  # @return [void]\n  # @see optional_commands\n  # @api public\n  #\n  def self.commands(command_specs)\n    command_specs.each do |name, path|\n      has_command(name, path)\n    end\n  end\n\n  # Defines optional commands.\n  # Since Puppet 2.7.8 this is typically not needed as evaluation of provider suitability\n  # is lazy (when a resource is evaluated) and the absence of commands\n  # that will be present after other resources have been applied no longer needs to be specified as\n  # optional.\n  # @param [Hash{String => String}] hash Named commands that the provider will\n  #   be executing on the system. Each command is specified with a name and the path of the executable.\n  # (@see #has_command)\n  # @see commands\n  # @api public\n  def self.optional_commands(hash)\n    hash.each do |name, target|\n      has_command(name, target) do\n        is_optional\n      end\n    end\n  end\n\n  # Creates a convenience method for invocation of a command.\n  #\n  # This generates a Provider method that allows easy execution of the command. The generated\n  # method may take arguments that will be passed through to the executable as the command line arguments\n  # when it is invoked.\n  #\n  # @example Use it like this:\n  #   has_command(:echo, \"/bin/echo\")\n  #   def some_method\n  #     echo(\"arg 1\", \"arg 2\")\n  #   end\n  # @comment the . . .  below is intentional to avoid the three dots to become an illegible ellipsis char.\n  # @example . . . or like this\n  #   has_command(:echo, \"/bin/echo\") do\n  #     is_optional\n  #     environment :HOME => \"/var/tmp\", :PWD => \"/tmp\"\n  #   end\n  #\n  # @param name [Symbol] The name of the command (will become the name of the generated method that executes the command)\n  # @param path [String] The path to the executable for the command\n  # @yield [ ] A block that configures the command (see {Puppet::Provider::Command})\n  # @comment a yield [ ] produces { ... } in the signature, do not remove the space.\n  # @note the name ´has_command´ looks odd in an API context, but makes more sense when seen in the internal\n  #   DSL context where a Provider is declaratively defined.\n  # @api public\n  # rubocop:disable Naming/PredicateName\n  def self.has_command(name, path, &block)\n    name = name.intern\n    command = CommandDefiner.define(name, path, self, &block)\n\n    @commands[name] = command.executable\n\n    # Now define the class and instance methods.\n    create_class_and_instance_method(name) do |*args|\n      return command.execute(*args)\n    end\n  end\n  # rubocop:enable Naming/PredicateName\n\n  # Internal helper class when creating commands - undocumented.\n  # @api private\n  class CommandDefiner\n    private_class_method :new\n\n    def self.define(name, path, confiner, &block)\n      definer = new(name, path, confiner)\n      definer.instance_eval(&block) if block\n      definer.command\n    end\n\n    def initialize(name, path, confiner)\n      @name = name\n      @path = path\n      @optional = false\n      @confiner = confiner\n      @custom_environment = {}\n    end\n\n    def is_optional # rubocop:disable Naming/PredicateName\n      @optional = true\n    end\n\n    def environment(env)\n      @custom_environment = @custom_environment.merge(env)\n    end\n\n    def command\n      unless @optional\n        @confiner.confine :exists => @path, :for_binary => true\n      end\n\n      Puppet::Provider::Command.new(@name, @path, Puppet::Util, Puppet::Util::Execution, { :failonfail => true, :combine => true, :custom_environment => @custom_environment })\n    end\n  end\n\n  # @return [Boolean] Return whether the given feature has been declared or not.\n  def self.declared_feature?(name)\n    defined?(@declared_features) and @declared_features.include?(name)\n  end\n\n  # @return [Boolean] Returns whether this implementation satisfies all of the default requirements or not.\n  #   Returns false if there is no matching defaultfor\n  # @see Provider.defaultfor\n  #\n  def self.default?\n    default_match ? true : false\n  end\n\n  # Look through the array of defaultfor hashes and return the first match.\n  # @return [Hash<{String => Object}>] the matching hash specified by a defaultfor\n  # @see Provider.defaultfor\n  # @api private\n  def self.default_match\n    return nil if some_default_match(@notdefaults) # Blacklist means this provider cannot be a default\n\n    some_default_match(@defaults)\n  end\n\n  def self.some_default_match(defaultlist)\n    defaultlist.find do |default|\n      default.all? do |key, values|\n        key == :feature ? feature_match(values) : fact_match(key, values)\n      end\n    end\n  end\n\n  # Compare a fact value against one or more supplied value\n  # @param [Symbol] fact a fact to query to match against one of the given values\n  # @param [Array, Regexp, String] values one or more values to compare to the\n  #   value of the given fact\n  # @return [Boolean] whether or not the fact value matches one of the supplied\n  #   values. Given one or more Regexp instances, fact is compared via the basic\n  #   pattern-matching operator.\n  def self.fact_match(fact, values)\n    fact_val = Puppet.runtime[:facter].value(fact).to_s.downcase\n    if fact_val.empty?\n      false\n    else\n      values = [values] unless values.is_a?(Array)\n      values.any? do |value|\n        if value.is_a?(Regexp)\n          fact_val =~ value\n        else\n          fact_val.intern == value.to_s.downcase.intern\n        end\n      end\n    end\n  end\n\n  def self.feature_match(value)\n    Puppet.features.send(value.to_s + \"?\")\n  end\n\n  # Sets a facts filter that determine which of several suitable providers should be picked by default.\n  # This selection only kicks in if there is more than one suitable provider.\n  # To filter on multiple facts the given hash may contain more than one fact name/value entry.\n  # The filter picks the provider if all the fact/value entries match the current set of facts. (In case\n  # there are still more than one provider after this filtering, the first found is picked).\n  # @param hash [Hash<{String => Object}>] hash of fact name to fact value.\n  # @return [void]\n  #\n  def self.defaultfor(hash)\n    @defaults << hash\n  end\n\n  def self.notdefaultfor(hash)\n    @notdefaults << hash\n  end\n\n  # @return [Integer] Returns a numeric specificity for this provider based on how many requirements it has\n  #  and number of _ancestors_. The higher the number the more specific the provider.\n  # The number of requirements is based on the hash size of the matching {Provider.defaultfor}.\n  #\n  # The _ancestors_ is the Ruby Module::ancestors method and the number of classes returned is used\n  # to boost the score. The intent is that if two providers are equal, but one is more \"derived\" than the other\n  # (i.e. includes more classes), it should win because it is more specific).\n  # @note Because of how this value is\n  #   calculated there could be surprising side effects if a provider included an excessive amount of classes.\n  #\n  def self.specificity\n    # This strange piece of logic attempts to figure out how many parent providers there\n    # are to increase the score. What is will actually do is count all classes that Ruby Module::ancestors\n    # returns (which can be other classes than those the parent chain) - in a way, an odd measure of the\n    # complexity of a provider).\n    match = default_match\n    length = match ? match.length : 0\n\n    (length * 100) + ancestors.select { |a| a.is_a? Class }.length\n  end\n\n  # Initializes defaults and commands (i.e. clears them).\n  # @return [void]\n  def self.initvars\n    @defaults = []\n    @notdefaults = []\n    @commands = {}\n  end\n\n  # Returns a list of system resources (entities) this provider may/can manage.\n  # This is a query mechanism that lists entities that the provider may manage on a given system. It is\n  # is directly used in query services, but is also the foundation for other services; prefetching, and\n  # purging.\n  #\n  # As an example, a package provider lists all installed packages. (In contrast, the File provider does\n  # not list all files on the file-system as that would make execution incredibly slow). An implementation\n  # of this method should be made if it is possible to quickly (with a single system call) provide all\n  # instances.\n  #\n  # An implementation of this method should only cache the values of properties\n  # if they are discovered as part of the process for finding existing resources.\n  # Resource properties that require additional commands (than those used to determine existence/identity)\n  # should be implemented in their respective getter method. (This is important from a performance perspective;\n  # it may be expensive to compute, as well as wasteful as all discovered resources may perhaps not be managed).\n  #\n  # An implementation may return an empty list (naturally with the effect that it is not possible to query\n  # for manageable entities).\n  #\n  # By implementing this method, it is possible to use the `resources´ resource type to specify purging\n  # of all non managed entities.\n  #\n  # @note The returned instances are instance of some subclass of Provider, not resources.\n  # @return [Array<Puppet::Provider>] a list of providers referencing the system entities\n  # @abstract this method must be implemented by a subclass and this super method should never be called as it raises an exception.\n  # @raise [Puppet::DevError] Error indicating that the method should have been implemented by subclass.\n  # @see prefetch\n  def self.instances\n    raise Puppet::DevError, _(\"To support listing resources of this type the '%{provider}' provider needs to implement an 'instances' class method returning the current set of resources. We recommend porting your module to the simpler Resource API instead: https://puppet.com/search/docs?keys=resource+api\") % { provider: name }\n  end\n\n  # Creates getter- and setter- methods for each property supported by the resource type.\n  # Call this method to generate simple accessors for all properties supported by the\n  # resource type. These simple accessors lookup and sets values in the property hash.\n  # The generated methods may be overridden by more advanced implementations if something\n  # else than a straight forward getter/setter pair of methods is required.\n  # (i.e. define such overriding methods after this method has been called)\n  #\n  # An implementor of a provider that makes use of `prefetch` and `flush` can use this method since it uses\n  # the internal `@property_hash` variable to store values. An implementation would then update the system\n  # state on a call to `flush` based on the current values in the `@property_hash`.\n  #\n  # @return [void]\n  #\n  def self.mk_resource_methods\n    [resource_type.validproperties, resource_type.parameters].flatten.each do |attr|\n      attr = attr.intern\n      next if attr == :name\n\n      define_method(attr) do\n        if @property_hash[attr].nil?\n          :absent\n        else\n          @property_hash[attr]\n        end\n      end\n\n      define_method(attr.to_s + \"=\") do |val|\n        @property_hash[attr] = val\n      end\n    end\n  end\n\n  initvars\n\n  # This method is used to generate a method for a command.\n  # @return [void]\n  # @api private\n  #\n  def self.create_class_and_instance_method(name, &block)\n    unless singleton_class.method_defined?(name)\n      meta_def(name, &block)\n    end\n\n    unless method_defined?(name)\n      define_method(name) do |*args|\n        self.class.send(name, *args)\n      end\n    end\n  end\n  private_class_method :create_class_and_instance_method\n\n  # @return [String] Returns the data source, which is the provider name if no other source has been set.\n  # @todo Unclear what \"the source\" is used for?\n  def self.source\n    @source ||= name\n  end\n\n  # Returns true if the given attribute/parameter is supported by the provider.\n  # The check is made that the parameter is a valid parameter for the resource type, and then\n  # if all its required features (if any) are supported by the provider.\n  #\n  # @param param [Class, Puppet::Parameter] the parameter class, or a parameter instance\n  # @return [Boolean] Returns whether this provider supports the given parameter or not.\n  # @raise [Puppet::DevError] if the given parameter is not valid for the resource type\n  #\n  def self.supports_parameter?(param)\n    if param.is_a?(Class)\n      klass = param\n    else\n      klass = resource_type.attrclass(param)\n      unless klass\n        raise Puppet::DevError, _(\"'%{parameter_name}' is not a valid parameter for %{resource_type}\") % { parameter_name: param, resource_type: resource_type.name }\n      end\n    end\n    features = klass.required_features\n    return true unless features\n\n    !!satisfies?(*features)\n  end\n\n  dochook(:defaults) do\n    if @defaults.length > 0\n      return @defaults.collect do |d|\n        \"Default for \" + d.collect do |f, v|\n          \"`#{f}` == `#{[v].flatten.join(', ')}`\"\n        end.sort.join(\" and \") + \".\"\n      end.join(\" \")\n    end\n  end\n\n  dochook(:commands) do\n    if @commands.length > 0\n      return \"Required binaries: \" + @commands.collect do |_n, c|\n        \"`#{c}`\"\n      end.sort.join(\", \") + \".\"\n    end\n  end\n\n  dochook(:features) do\n    if features.length > 0\n      return \"Supported features: \" + features.collect do |f|\n        \"`#{f}`\"\n      end.sort.join(\", \") + \".\"\n    end\n  end\n\n  # Clears this provider instance to allow GC to clean up.\n  def clear\n    @resource = nil\n  end\n\n  # (see command)\n  def command(name)\n    self.class.command(name)\n  end\n\n  # Returns the value of a parameter value, or `:absent` if it is not defined.\n  # @param param [Puppet::Parameter] the parameter to obtain the value of\n  # @return [Object] the value of the parameter or `:absent` if not defined.\n  #\n  def get(param)\n    @property_hash[param.intern] || :absent\n  end\n\n  # Creates a new provider that is optionally initialized from a resource or a hash of properties.\n  # If no argument is specified, a new non specific provider is initialized. If a resource is given\n  # it is remembered for further operations. If a hash is used it becomes the internal `@property_hash`\n  # structure of the provider - this hash holds the current state property values of system entities\n  # as they are being discovered by querying or other operations (typically getters).\n  #\n  # @todo The use of a hash as a parameter needs a better explanation; why is this done? What is the intent?\n  # @param resource [Puppet::Resource, Hash] optional resource or hash\n  #\n  def initialize(resource = nil)\n    if resource.is_a?(Hash)\n      # We don't use a duplicate here, because some providers (ParsedFile, at least)\n      # use the hash here for later events.\n      @property_hash = resource\n    elsif resource\n      @resource = resource\n      @property_hash = {}\n    else\n      @property_hash = {}\n    end\n  end\n\n  # Returns the name of the resource this provider is operating on.\n  # @return [String] the name of the resource instance (e.g. the file path of a File).\n  # @raise [Puppet::DevError] if no resource is set, or no name defined.\n  #\n  def name\n    n = @property_hash[:name]\n    if n\n      n\n    elsif resource\n      resource.name\n    else\n      raise Puppet::DevError, _(\"No resource and no name in property hash in %{class_name} instance\") % { class_name: self.class.name }\n    end\n  end\n\n  # Sets the given parameters values as the current values for those parameters.\n  # Other parameters are unchanged.\n  # @param [Array<Puppet::Parameter>] params the parameters with values that should be set\n  # @return [void]\n  #\n  def set(params)\n    params.each do |param, value|\n      @property_hash[param.intern] = value\n    end\n  end\n\n  # @return [String] Returns a human readable string with information about the resource and the provider.\n  def to_s\n    \"#{@resource}(provider=#{self.class.name})\"\n  end\n\n  # @return [String] Returns a human readable string with information about the resource and the provider.\n  def inspect\n    to_s\n  end\n\n  # Makes providers comparable.\n  include Comparable\n  # Compares this provider against another provider.\n  # Comparison is only possible with another provider (no other class).\n  # The ordering is based on the class name of the two providers.\n  #\n  # @return [-1,0,+1, nil] A comparison result -1, 0, +1 if this is before other, equal or after other. Returns\n  #   nil oif not comparable to other.\n  # @see Comparable\n  def <=>(other)\n    # We can only have ordering against other providers.\n    return nil unless other.is_a? Puppet::Provider\n\n    # Otherwise, order by the providers class name.\n    self.class.name <=> other.class.name\n  end\n\n  # @comment Document prefetch here as it does not exist anywhere else (called from transaction if implemented)\n  # @!method self.prefetch(resource_hash)\n  # @abstract A subclass may implement this - it is not implemented in the Provider class\n  # This method may be implemented by a provider in order to pre-fetch resource properties.\n  # If implemented it should set the provider instance of the managed resources to a provider with the\n  # fetched state (i.e. what is returned from the {instances} method).\n  # @param resources_hash [Hash<{String => Puppet::Resource}>] map from name to resource of resources to prefetch\n  # @return [void]\n  # @api public\n\n  # @comment Document post_resource_eval here as it does not exist anywhere else (called from transaction if implemented)\n  # @!method self.post_resource_eval()\n  # @since 3.4.0\n  # @api public\n  # @abstract A subclass may implement this - it is not implemented in the Provider class\n  # This method may be implemented by a provider in order to perform any\n  # cleanup actions needed.  It will be called at the end of the transaction if\n  # any resources in the catalog make use of the provider, regardless of\n  # whether the resources are changed or not and even if resource failures occur.\n  # @return [void]\n\n  # @comment Document flush here as it does not exist anywhere (called from transaction if implemented)\n  # @!method flush()\n  # @abstract A subclass may implement this - it is not implemented in the Provider class\n  # This method may be implemented by a provider in order to flush properties that has not been individually\n  # applied to the managed entity's current state.\n  # @return [void]\n  # @api public\nend\n"
  },
  {
    "path": "lib/puppet/reference/configuration.rb",
    "content": "# frozen_string_literal: true\n\nconfig = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc => \"A reference for all settings\") do\n  docs = {}\n  Puppet.settings.each do |name, object|\n    docs[name] = object\n  end\n\n  str = ''.dup\n  docs.sort { |a, b|\n    a[0].to_s <=> b[0].to_s\n  }.each do |name, object|\n    # Make each name an anchor\n    header = name.to_s\n    str << markdown_header(header, 3)\n\n    # Print the doc string itself\n    begin\n      str << Puppet::Util::Docs.scrub(object.desc)\n    rescue => detail\n      Puppet.log_exception(detail)\n    end\n    str << \"\\n\\n\"\n\n    # Now print the data about the item.\n    val = object.default\n    case name.to_s\n    when 'vardir'\n      val = 'Unix/Linux: /opt/puppetlabs/puppet/cache -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\cache -- Non-root user: ~/.puppetlabs/opt/puppet/cache'\n    when 'publicdir'\n      val = 'Unix/Linux: /opt/puppetlabs/puppet/public -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\public -- Non-root user: ~/.puppetlabs/opt/puppet/public'\n    when 'confdir'\n      val = 'Unix/Linux: /etc/puppetlabs/puppet -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\etc -- Non-root user: ~/.puppetlabs/etc/puppet'\n    when 'codedir'\n      val = 'Unix/Linux: /etc/puppetlabs/code -- Windows: C:\\ProgramData\\PuppetLabs\\code -- Non-root user: ~/.puppetlabs/etc/code'\n    when 'rundir'\n      val = 'Unix/Linux: /var/run/puppetlabs -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\var\\run -- Non-root user: ~/.puppetlabs/var/run'\n    when 'logdir'\n      val = 'Unix/Linux: /var/log/puppetlabs/puppet -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\var\\log -- Non-root user: ~/.puppetlabs/var/log'\n    when 'hiera_config'\n      val = '$confdir/hiera.yaml. However, for backwards compatibility, if a file exists at $codedir/hiera.yaml, Puppet uses that instead.'\n    when 'certname'\n      val = \"the Host's fully qualified domain name, as determined by Facter\"\n    when 'hostname'\n      val = \"(the system's fully qualified hostname)\"\n    when 'domain'\n      val = \"(the system's own domain)\"\n    when 'srv_domain'\n      val = 'example.com'\n    when 'http_user_agent'\n      val = 'Puppet/<version> Ruby/<version> (<architecture>)'\n    end\n\n    # Leave out the section information; it was apparently confusing people.\n    # str << \"- **Section**: #{object.section}\\n\"\n    unless val == \"\"\n      str << \"- *Default*: `#{val}`\\n\"\n    end\n    str << \"\\n\"\n  end\n\n  return str\nend\n\nconfig.header = <<~EOT\n  ## Configuration settings\n\n  * Each of these settings can be specified in `puppet.conf` or on the\n    command line.\n  * Puppet Enterprise (PE) and open source Puppet share the configuration settings\n    documented here. However, PE defaults differ from open source defaults for some\n    settings, such as `node_terminus`, `storeconfigs`, `always_retry_plugins`,\n    `disable18n`, `environment_timeout` (when Code Manager is enabled), and the\n    Puppet Server JRuby `max-active-instances` setting. To verify PE configuration\n    defaults, check the `puppet.conf` or `pe-puppet-server.conf` file after\n    installation.\n  * When using boolean settings on the command line, use `--setting` and\n    `--no-setting` instead of `--setting (true|false)`. (Using `--setting false`\n    results in \"Error: Could not parse application options: needless argument\".)\n  * Settings can be interpolated as `$variables` in other settings; `$environment`\n    is special, in that puppet master will interpolate each agent node's\n    environment instead of its own.\n  * Multiple values should be specified as comma-separated lists; multiple\n    directories should be separated with the system path separator (usually\n    a colon).\n  * Settings that represent time intervals should be specified in duration format:\n    an integer immediately followed by one of the units 'y' (years of 365 days),\n    'd' (days), 'h' (hours), 'm' (minutes), or 's' (seconds). The unit cannot be\n    combined with other units, and defaults to seconds when omitted. Examples are\n    '3600' which is equivalent to '1h' (one hour), and '1825d' which is equivalent\n    to '5y' (5 years).\n  * If you use the `splay` setting, note that the period that it waits changes\n    each time the Puppet agent is restarted.\n  * Settings that take a single file or directory can optionally set the owner,\n    group, and mode for their value: `rundir = $vardir/run { owner = puppet,\n    group = puppet, mode = 644 }`\n  * The Puppet executables ignores any setting that isn't relevant to\n    their function.\n\n  See the [configuration guide][confguide] for more details.\n\n  [confguide]: https://puppet.com/docs/puppet/latest/config_about_settings.html\n\n\nEOT\n"
  },
  {
    "path": "lib/puppet/reference/function.rb",
    "content": "# frozen_string_literal: true\n\nfunction = Puppet::Util::Reference.newreference :function, :doc => \"All functions available in the parser\" do\n  Puppet::Parser::Functions.functiondocs\nend\nfunction.header = \"\nThere are two types of functions in Puppet: Statements and rvalues.\nStatements stand on their own and do not return arguments; they are used for\nperforming stand-alone work like importing.  Rvalues return values and can\nonly be used in a statement requiring a value, such as an assignment or a case\nstatement.\n\nFunctions execute on the Puppet master.  They do not execute on the Puppet agent.\nHence they only have access to the commands and data available on the Puppet master\nhost.\n\nHere are the functions available in Puppet:\n\n\"\n"
  },
  {
    "path": "lib/puppet/reference/indirection.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/indirector/indirection'\nrequire_relative '../../puppet/util/checksums'\nrequire_relative '../../puppet/file_serving/content'\nrequire_relative '../../puppet/file_serving/metadata'\n\nreference = Puppet::Util::Reference.newreference :indirection, :doc => \"Indirection types and their terminus classes\" do\n  text = ''.dup\n  Puppet::Indirector::Indirection.instances.sort_by(&:to_s).each do |indirection|\n    ind = Puppet::Indirector::Indirection.instance(indirection)\n    name = indirection.to_s.capitalize\n    text << \"## \" + indirection.to_s + \"\\n\\n\"\n\n    text << Puppet::Util::Docs.scrub(ind.doc) + \"\\n\\n\"\n\n    Puppet::Indirector::Terminus.terminus_classes(ind.name).sort_by(&:to_s).each do |terminus|\n      # this is an \"abstract\" terminus, ignore it\n      next if ind.name == :resource && terminus == :validator\n\n      terminus_name = terminus.to_s\n      term_class = Puppet::Indirector::Terminus.terminus_class(ind.name, terminus)\n      if term_class\n        terminus_doc = Puppet::Util::Docs.scrub(term_class.doc)\n        text << markdown_header(\"`#{terminus_name}` terminus\", 3) << terminus_doc << \"\\n\\n\"\n      else\n        Puppet.warning _(\"Could not build docs for indirector %{name}, terminus %{terminus}: could not locate terminus.\") % { name: name.inspect, terminus: terminus_name.inspect }\n      end\n    end\n  end\n\n  text\nend\n\nreference.header = <<~HEADER\n\n  ## About Indirection\n\n  Puppet's indirector support pluggable backends (termini) for a variety of key-value stores (indirections).\n  Each indirection type corresponds to a particular Ruby class (the \"Indirected Class\" below) and values are instances of that class.\n  Each instance's key is available from its `name` method.\n  The termini can be local (e.g., on-disk files) or remote (e.g., using a REST interface to talk to a puppet master).\n\n  An indirector has five methods, which are mapped into HTTP verbs for the REST interface:\n\n  * `find(key)` - get a single value (mapped to GET or POST with a singular endpoint)\n  * `search(key)` - get a list of matching values (mapped to GET with a plural endpoint)\n  * `head(key)` - return true if the key exists (mapped to HEAD)\n  * `destroy(key)` - remove the key and value (mapped to DELETE)\n  * `save(instance)` - write the instance to the store, using the instance's name as the key (mapped to PUT)\n\n  These methods are available via the `indirection` class method on the indirected classes.  For example:\n\n      node = Puppet::Node.indirection.find('foo.example.com')\n\n  At startup, each indirection is configured with a terminus.\n  In most cases, this is the default terminus defined by the indirected class, but it can be overridden by the application or face, or overridden with the `route_file` configuration.\n  The available termini differ for each indirection, and are listed below.\n\n  Indirections can also have a cache, represented by a second terminus.\n  This is a write-through cache: modifications are written both to the cache and to the primary terminus.\n  Values fetched from the terminus are written to the cache.\n\n  ### Interaction with REST\n\n  REST endpoints have the form `/{prefix}/{version}/{indirection}/{key}?environment={environment}`, where the indirection can be singular or plural, following normal English spelling rules.\n  On the server side, REST responses are generated from the locally-configured endpoints.\n\n  ### Indirections and Termini\n\n  Below is the list of all indirections, their associated terminus classes, and how you select between them.\n\n  In general, the appropriate terminus class is selected by the application for you (e.g., `puppet agent` would always use the `rest`\n  terminus for most of its indirected classes), but some classes are tunable via normal settings.  These will have `terminus setting` documentation listed with them.\n\nHEADER\n"
  },
  {
    "path": "lib/puppet/reference/metaparameter.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Util::Reference.newreference :metaparameter, :doc => \"All Puppet metaparameters and all their details\" do\n  str = %{\n\nMetaparameters are attributes that work with any resource type, including custom\ntypes and defined types.\n\nIn general, they affect _Puppet's_ behavior rather than the desired state of the\nresource. Metaparameters do things like add metadata to a resource (`alias`,\n`tag`), set limits on when the resource should be synced (`require`, `schedule`,\netc.), prevent Puppet from making changes (`noop`), and change logging verbosity\n(`loglevel`).\n\n## Available Metaparameters\n\n  }.dup\n  begin\n    params = []\n    Puppet::Type.eachmetaparam { |param|\n      params << param\n    }\n\n    params.sort_by(&:to_s).each { |param|\n      str << markdown_header(param.to_s, 3)\n      str << scrub(Puppet::Type.metaparamdoc(param))\n      str << \"\\n\\n\"\n    }\n  rescue => detail\n    Puppet.log_exception(detail, _(\"incorrect metaparams: %{detail}\") % { detail: detail })\n    exit(1)\n  end\n\n  str\nend\n"
  },
  {
    "path": "lib/puppet/reference/providers.rb",
    "content": "# frozen_string_literal: true\n\nproviders = Puppet::Util::Reference.newreference :providers, :title => \"Provider Suitability Report\", :depth => 1, :dynamic => true, :doc => \"Which providers are valid for this machine\" do\n  types = []\n  Puppet::Type.loadall\n  Puppet::Type.eachtype do |klass|\n    next unless klass && klass.providers.length > 0\n\n    types << klass\n  end\n  types.sort! { |a, b| a.name.to_s <=> b.name.to_s }\n\n  command_line = Puppet::Util::CommandLine.new\n  types.reject! { |type| !command_line.args.include?(type.name.to_s) } unless command_line.args.empty?\n\n  ret = \"Details about this host:\\n\\n\".dup\n\n  # Throw some facts in there, so we know where the report is from.\n  ret << option('Ruby Version', Facter.value('ruby.version'))\n  ret << option('Puppet Version', Facter.value('puppetversion'))\n  ret << option('Operating System', Facter.value('os.name'))\n  ret << option('Operating System Release', Facter.value('os.release.full'))\n  ret << \"\\n\"\n\n  count = 1\n\n  # Produce output for each type.\n  types.each do |type|\n    features = type.features\n    ret << \"\\n\" # add a trailing newline\n\n    # Now build up a table of provider suitability.\n    headers = %w[Provider Suitable?] + features.collect(&:to_s).sort\n\n    table_data = {}\n\n    notes = []\n    default = type.defaultprovider ? type.defaultprovider.name : 'none'\n    type.providers.sort_by(&:to_s).each do |pname|\n      data = []\n      table_data[pname] = data\n      provider = type.provider(pname)\n\n      # Add the suitability note\n      missing = provider.suitable?(false)\n      if missing && missing.empty?\n        data << \"*X*\"\n        suit = true\n      else\n        data << \"[#{count}]_\" # A pointer to the appropriate footnote\n        suit = false\n      end\n\n      # Add a footnote with the details about why this provider is unsuitable, if that's the case\n      unless suit\n        details = \".. [#{count}]\\n\"\n        missing.each do |test, values|\n          case test\n          when :exists\n            details << _(\"  - Missing files %{files}\\n\") % { files: values.join(\", \") }\n          when :variable\n            values.each do |name, facts|\n              if Puppet.settings.valid?(name)\n                details << _(\"  - Setting %{name} (currently %{value}) not in list %{facts}\\n\") % { name: name, value: Puppet.settings.value(name).inspect, facts: facts.join(\", \") }\n              else\n                details << _(\"  - Fact %{name} (currently %{value}) not in list %{facts}\\n\") % { name: name, value: Puppet.runtime[:facter].value(name).inspect, facts: facts.join(\", \") }\n              end\n            end\n          when :true\n            details << _(\"  - Got %{values} true tests that should have been false\\n\") % { values: values }\n          when :false\n            details << _(\"  - Got %{values} false tests that should have been true\\n\") % { values: values }\n          when :feature\n            details << _(\"  - Missing features %{values}\\n\") % { values: values.collect(&:to_s).join(\",\") }\n          end\n        end\n        notes << details\n\n        count += 1\n      end\n\n      # Add a note for every feature\n      features.each do |feature|\n        if provider.features.include?(feature)\n          data << \"*X*\"\n        else\n          data << \"\"\n        end\n      end\n    end\n\n    ret << markdown_header(type.name.to_s + \"_\", 2)\n\n    ret << \"[#{type.name}](https://puppet.com/docs/puppet/latest/type.html##{type.name})\\n\\n\"\n    ret << option(\"Default provider\", default)\n    ret << doctable(headers, table_data)\n\n    notes.each do |note|\n      ret << note + \"\\n\"\n    end\n\n    ret << \"\\n\"\n  end\n\n  ret << \"\\n\"\n\n  ret\nend\nproviders.header = \"\nPuppet resource types are usually backed by multiple implementations called `providers`,\nwhich handle variance between platforms and tools.\n\nDifferent providers are suitable or unsuitable on different platforms based on things\nlike the presence of a given tool.\n\nHere are all of the provider-backed types and their different providers.  Any unmentioned\ntypes do not use providers yet.\n\n\"\n"
  },
  {
    "path": "lib/puppet/reference/report.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/reports'\n\nreport = Puppet::Util::Reference.newreference :report, :doc => \"All available transaction reports\" do\n  Puppet::Reports.reportdocs\nend\n\nreport.header = \"\nPuppet can generate a report after applying a catalog. This report includes\nevents, log messages, resource statuses, and metrics and metadata about the run.\nPuppet agent sends its report to a Puppet master server, and Puppet apply\nprocesses its own reports.\n\nPuppet master and Puppet apply will handle every report with a set of report\nprocessors, configurable with the `reports` setting in puppet.conf. This page\ndocuments the built-in report processors.\n\nSee [About Reporting](https://puppet.com/docs/puppet/latest/reporting_about.html)\nfor more details.\n\n\"\n"
  },
  {
    "path": "lib/puppet/reference/type.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Util::Reference.newreference :type, :doc => \"All Puppet resource types and all their details\" do\n  types = {}\n  Puppet::Type.loadall\n\n  Puppet::Type.eachtype { |type|\n    next if type.name == :component\n    next if type.name == :whit\n\n    types[type.name] = type\n  }\n\n  str = %{\n\n## Resource Types\n\n- The *namevar* is the parameter used to uniquely identify a type instance.\n  This is the parameter that gets assigned when a string is provided before\n  the colon in a type declaration.  In general, only developers will need to\n  worry about which parameter is the `namevar`.\n\n  In the following code:\n\n      file { \"/etc/passwd\":\n        owner => \"root\",\n        group => \"root\",\n        mode  => \"0644\"\n      }\n\n  `/etc/passwd` is considered the title of the file object (used for things like\n  dependency handling), and because `path` is the namevar for `file`, that\n  string is assigned to the `path` parameter.\n\n- *Parameters* determine the specific configuration of the instance.  They either\n  directly modify the system (internally, these are called properties) or they affect\n  how the instance behaves (e.g., adding a search path for `exec` instances or determining recursion on `file` instances).\n\n- *Providers* provide low-level functionality for a given resource type.  This is\n  usually in the form of calling out to external commands.\n\n  When required binaries are specified for providers, fully qualified paths\n  indicate that the binary must exist at that specific path and unqualified\n  binaries indicate that Puppet will search for the binary using the shell\n  path.\n\n- *Features* are abilities that some providers might not support.  You can use the list\n  of supported features to determine how a given provider can be used.\n\n  Resource types define features they can use, and providers can be tested to see\n  which features they provide.\n\n  }.dup\n\n  types.sort_by(&:to_s).each { |name, type|\n    str << \"\n\n----------------\n\n\"\n\n    str << markdown_header(name, 3)\n    str << scrub(type.doc) + \"\\n\\n\"\n\n    # Handle the feature docs.\n    featuredocs = type.featuredocs\n    if featuredocs\n      str << markdown_header(\"Features\", 4)\n      str << featuredocs\n    end\n\n    docs = {}\n    type.validproperties.sort_by(&:to_s).reject { |sname|\n      property = type.propertybyname(sname)\n      property.nodoc\n    }.each { |sname|\n      property = type.propertybyname(sname)\n\n      raise _(\"Could not retrieve property %{sname} on type %{type_name}\") % { sname: sname, type_name: type.name } unless property\n\n      doc = property.doc\n      unless doc\n        $stderr.puts _(\"No docs for %{type}[%{sname}]\") % { type: type, sname: sname }\n        next\n      end\n      doc = doc.dup\n      tmp = doc\n      tmp = scrub(tmp)\n\n      docs[sname] = tmp\n    }\n\n    str << markdown_header(\"Parameters\", 4) + \"\\n\"\n    type.parameters.sort_by(&:to_s).each { |type_name, _param|\n      docs[type_name] = scrub(type.paramdoc(type_name))\n    }\n\n    additional_key_attributes = type.key_attributes - [:name]\n    docs.sort { |a, b|\n      a[0].to_s <=> b[0].to_s\n    }.each { |type_name, doc|\n      if additional_key_attributes.include?(type_name)\n        doc = \"(**Namevar:** If omitted, this parameter's value defaults to the resource's title.)\\n\\n\" + doc\n      end\n      str << markdown_definitionlist(type_name, doc)\n    }\n    str << \"\\n\"\n  }\n\n  str\nend\n"
  },
  {
    "path": "lib/puppet/relationship.rb",
    "content": "# frozen_string_literal: true\n\n# subscriptions are permanent associations determining how different\n# objects react to an event\n\n# This is Puppet's class for modeling edges in its configuration graph.\n# It used to be a subclass of GRATR::Edge, but that class has weird hash\n# overrides that dramatically slow down the graphing.\nclass Puppet::Relationship\n  # FormatSupport for serialization methods\n  include Puppet::Network::FormatSupport\n  include Puppet::Util::PsychSupport\n\n  attr_accessor :source, :target, :callback\n\n  attr_reader :event\n\n  def self.from_data_hash(data)\n    source = data['source']\n    target = data['target']\n\n    args = {}\n    args[:event] = :\"#{data['event']}\" if data[\"event\"]\n    args[:callback] = :\"#{data['callback']}\" if data[\"callback\"]\n\n    new(source, target, args)\n  end\n\n  def event=(event)\n    # TRANSLATORS 'NONE' should not be translated\n    raise ArgumentError, _(\"You must pass a callback for non-NONE events\") if event != :NONE and !callback\n\n    @event = event\n  end\n\n  def initialize(source, target, options = {})\n    @source = source\n    @target = target\n\n    options = (options || {}).each_with_object({}) { |a, h| h[a[0].to_sym] = a[1]; }\n    [:callback, :event].each do |option|\n      value = options[option]\n      send(option.to_s + \"=\", value) if value\n    end\n  end\n\n  # Does the passed event match our event?  This is where the meaning\n  # of :NONE comes from.\n  def match?(event)\n    if self.event.nil? or event == :NONE or self.event == :NONE\n      false\n    else\n      self.event == :ALL_EVENTS or event == self.event\n    end\n  end\n\n  def label\n    result = {}\n    result[:callback] = callback if callback\n    result[:event] = event if event\n    result\n  end\n\n  def ref\n    \"#{source} => #{target}\"\n  end\n\n  def inspect\n    \"{ #{source} => #{target} }\"\n  end\n\n  def to_data_hash\n    data = {\n      'source' => source.to_s,\n      'target' => target.to_s\n    }\n    data['event'] = event.to_s unless event.nil?\n    data['callback'] = callback.to_s unless callback.nil?\n    data\n  end\n\n  def to_s\n    ref\n  end\nend\n"
  },
  {
    "path": "lib/puppet/reports/http.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/network/http_pool'\nrequire 'uri'\n\nPuppet::Reports.register_report(:http) do\n  desc <<-DESC\n    Send reports via HTTP or HTTPS. This report processor submits reports as\n    POST requests to the address in the `reporturl` setting. When a HTTPS URL\n    is used, the remote server must present a certificate issued by the Puppet\n    CA or the connection will fail validation. The body of each POST request\n    is the YAML dump of a Puppet::Transaction::Report object, and the\n    Content-Type is set as `application/x-yaml`.\n  DESC\n\n  def process\n    url = URI.parse(Puppet[:reporturl])\n    headers = { \"Content-Type\" => \"application/x-yaml\" }\n    # This metric_id option is silently ignored by Puppet's http client\n    # (Puppet::Network::HTTP) but is used by Puppet Server's http client\n    # (Puppet::Server::HttpClient) to track metrics on the request made to the\n    # `reporturl` to store a report.\n    options = {\n      :metric_id => [:puppet, :report, :http],\n      :include_system_store => Puppet[:report_include_system_store],\n    }\n\n    # Puppet's http client implementation accepts userinfo in the URL\n    # but puppetserver's does not. So pass credentials explicitly.\n    if url.user && url.password\n      options[:basic_auth] = {\n        user: url.user,\n        password: url.password\n      }\n    end\n\n    client = Puppet.runtime[:http]\n    client.post(url, to_yaml, headers: headers, options: options) do |response|\n      unless response.success?\n        Puppet.err _(\"Unable to submit report to %{url} [%{code}] %{message}\") % { url: Puppet[:reporturl].to_s, code: response.code, message: response.reason }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/reports/log.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/reports'\n\nPuppet::Reports.register_report(:log) do\n  desc \"Send all received logs to the local log destinations.  Usually\n    the log destination is syslog.\"\n\n  def process\n    logs.each do |log|\n      log.source = \"//#{host}/#{log.source}\"\n      Puppet::Util::Log.newmessage(log)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/reports/store.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire 'fileutils'\nrequire_relative '../../puppet/util'\n\nSEPARATOR = [Regexp.escape(File::SEPARATOR.to_s), Regexp.escape(File::ALT_SEPARATOR.to_s)].join\n\nPuppet::Reports.register_report(:store) do\n  desc \"Store the yaml report on disk.  Each host sends its report as a YAML dump\n    and this just stores the file on disk, in the `reportdir` directory.\n\n    These files collect quickly -- one every half hour -- so it is a good idea\n    to perform some maintenance on them if you use this report (it's the only\n    default report).\"\n\n  def process\n    validate_host(host)\n\n    dir = File.join(Puppet[:reportdir], host)\n\n    unless Puppet::FileSystem.exist?(dir)\n      FileUtils.mkdir_p(dir)\n      FileUtils.chmod_R(0o750, dir)\n    end\n\n    # Now store the report.\n    now = Time.now.gmtime\n    name = %w[year month day hour min].collect do |method|\n      # Make sure we're at least two digits everywhere\n      \"%02d\" % now.send(method).to_s\n    end.join(\"\") + \".yaml\"\n\n    file = File.join(dir, name)\n\n    begin\n      Puppet::FileSystem.replace_file(file, 0o640) do |fh|\n        fh.print to_yaml\n      end\n    rescue => detail\n      Puppet.log_exception(detail, \"Could not write report for #{host} at #{file}: #{detail}\")\n    end\n\n    # Only testing cares about the return value\n    file\n  end\n\n  # removes all reports for a given host?\n  def self.destroy(host)\n    validate_host(host)\n\n    dir = File.join(Puppet[:reportdir], host)\n\n    if Puppet::FileSystem.exist?(dir)\n      Dir.entries(dir).each do |file|\n        next if ['.', '..'].include?(file)\n\n        file = File.join(dir, file)\n        Puppet::FileSystem.unlink(file) if File.file?(file)\n      end\n      Dir.rmdir(dir)\n    end\n  end\n\n  def validate_host(host)\n    if host =~ Regexp.union(/[#{SEPARATOR}]/, /\\A\\.\\.?\\Z/)\n      raise ArgumentError, _(\"Invalid node name %{host}\") % { host: host.inspect }\n    end\n  end\n  module_function :validate_host\nend\n"
  },
  {
    "path": "lib/puppet/reports.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/util/instance_loader'\n\n# This class is an implementation of a simple mechanism for loading and returning reports.\n# The intent is that a user registers a report by calling {register_report} and providing a code\n# block that performs setup, and defines a method called `process`. The setup, and the `process` method\n# are called in the context where `self` is an instance of {Puppet::Transaction::Report} which provides the actual\n# data for the report via its methods.\n#\n# @example Minimal scaffolding for a report...\n#   Puppet::Reports::.register_report(:myreport) do\n#     # do setup here\n#     def process\n#       if self.status == 'failed'\n#         msg = \"failed puppet run for #{self.host} #{self.status}\"\n#         . . .\n#       else\n#         . . .\n#       end\n#     end\n#   end\n#\n# Required configuration:\n# --\n# * A .rb file that defines a new report should be placed in the master's directory `lib/puppet/reports`\n# * The `puppet.conf` file must have `report = true` in the `[agent]` section\n#\n# @see Puppet::Transaction::Report\n# @api public\n#\nclass Puppet::Reports\n  extend Puppet::Util::ClassGen\n  extend Puppet::Util::InstanceLoader\n\n  # Set up autoloading and retrieving of reports.\n  instance_load :report, 'puppet/reports'\n\n  class << self\n    # @api private\n    attr_reader :hooks\n  end\n\n  # Adds a new report type.\n  # The block should contain setup, and define a method with the name `process`. The `process` method is\n  # called when the report is executed; the `process` method has access to report data via methods available\n  # in its context where `self` is an instance of {Puppet::Transaction::Report}.\n  #\n  # For an example, see the overview of this class.\n  #\n  # @param name [Symbol] the name of the report (do not use whitespace in the name).\n  # @param options [Hash] a hash of options\n  # @option options [Boolean] :useyaml whether yaml should be used or not\n  # @todo Uncertain what the options :useyaml really does; \"whether yaml should be used or not\", used where/how?\n  #\n  def self.register_report(name, options = {}, &block)\n    name = name.intern\n\n    mod = genmodule(name,\n                    :extend => Puppet::Util::Docs,\n                    :hash => instance_hash(:report),\n                    :overwrite => true,\n                    :block => block)\n\n    mod.useyaml = true if options[:useyaml]\n\n    mod.send(:define_method, :report_name) do\n      name\n    end\n  end\n\n  # Collects the docs for all reports.\n  # @api private\n  def self.reportdocs\n    docs = ''.dup\n\n    # Use this method so they all get loaded\n    instance_loader(:report).loadall(Puppet.lookup(:current_environment))\n    loaded_instances(:report).sort_by(&:to_s).each do |name|\n      mod = report(name)\n      docs << \"#{name}\\n#{'-' * name.to_s.length}\\n\"\n\n      docs << Puppet::Util::Docs.scrub(mod.doc) << \"\\n\\n\"\n    end\n\n    docs\n  end\n\n  # Lists each of the reports.\n  # @api private\n  def self.reports\n    instance_loader(:report).loadall(Puppet.lookup(:current_environment))\n    loaded_instances(:report)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/resource/catalog.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/node'\nrequire_relative '../../puppet/indirector'\nrequire_relative '../../puppet/transaction'\nrequire_relative '../../puppet/util/tagging'\nrequire_relative '../../puppet/graph'\nrequire 'securerandom'\n\n# This class models a node catalog.  It is the thing meant to be passed\n# from server to client, and it contains all of the information in the\n# catalog, including the resources and the relationships between them.\n#\n# @api public\n\nclass Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph\n  class DuplicateResourceError < Puppet::Error\n    include Puppet::ExternalFileError\n  end\n\n  extend Puppet::Indirector\n  indirects :catalog, :terminus_setting => :catalog_terminus\n\n  include Puppet::Util::Tagging\n\n  # The host name this is a catalog for.\n  attr_accessor :name\n\n  # The catalog version.  Used for testing whether a catalog\n  # is up to date.\n  attr_accessor :version\n\n  # The id of the code input to the compiler.\n  attr_accessor :code_id\n\n  # The UUID of the catalog\n  attr_accessor :catalog_uuid\n\n  # @return [Integer] catalog format version number. This value is constant\n  #  for a given version of Puppet; it is incremented when a new release of\n  #  Puppet changes the API for the various objects that make up the catalog.\n  attr_accessor :catalog_format\n\n  # Inlined file metadata for non-recursive find\n  # A hash of title => metadata\n  attr_accessor :metadata\n\n  # Inlined file metadata for recursive search\n  # A hash of title => { source => [metadata, ...] }\n  attr_accessor :recursive_metadata\n\n  # How long this catalog took to retrieve.  Used for reporting stats.\n  attr_accessor :retrieval_duration\n\n  # Whether this is a host catalog, which behaves very differently.\n  # In particular, reports are sent, graphs are made, and state is\n  # stored in the state database.  If this is set incorrectly, then you often\n  # end up in infinite loops, because catalogs are used to make things\n  # that the host catalog needs.\n  attr_accessor :host_config\n\n  # Whether this catalog was retrieved from the cache, which affects\n  # whether it is written back out again.\n  attr_accessor :from_cache\n\n  # Some metadata to help us compile and generally respond to the current state.\n  attr_accessor :client_version, :server_version\n\n  # A String representing the environment for this catalog\n  attr_accessor :environment\n\n  # The actual environment instance that was used during compilation\n  attr_accessor :environment_instance\n\n  # Add classes to our class list.\n  def add_class(*classes)\n    classes.each do |klass|\n      @classes << klass\n    end\n\n    # Add the class names as tags, too.\n    tag(*classes)\n  end\n\n  # Returns [typename, title] when given a String with \"Type[title]\".\n  # Returns [nil, nil] if '[' ']' not detected.\n  #\n  def title_key_for_ref(ref)\n    s = ref.index('[')\n    e = ref.rindex(']')\n    if s && e && e > s\n      a = [ref[0, s], ref[s + 1, e - s - 1]]\n    else\n      a = [nil, nil]\n    end\n    a\n  end\n\n  def add_resource_before(other, *resources)\n    resources.each do |resource|\n      other_title_key = title_key_for_ref(other.ref)\n      idx = @resources.index(other_title_key)\n      if idx.nil?\n        raise ArgumentError, _(\"Cannot add resource %{resource_1} before %{resource_2} because %{resource_2} is not yet in the catalog\") %\n                             { resource_1: resource.ref, resource_2: other.ref }\n      end\n      add_one_resource(resource, idx)\n    end\n  end\n\n  # Add `resources` to the catalog after `other`. WARNING: adding\n  # multiple resources will produce the reverse ordering, e.g. calling\n  # `add_resource_after(A, [B,C])` will result in `[A,C,B]`.\n  def add_resource_after(other, *resources)\n    resources.each do |resource|\n      other_title_key = title_key_for_ref(other.ref)\n      idx = @resources.index(other_title_key)\n      if idx.nil?\n        raise ArgumentError, _(\"Cannot add resource %{resource_1} after %{resource_2} because %{resource_2} is not yet in the catalog\") %\n                             { resource_1: resource.ref, resource_2: other.ref }\n      end\n      add_one_resource(resource, idx + 1)\n    end\n  end\n\n  def add_resource(*resources)\n    resources.each do |resource|\n      add_one_resource(resource)\n    end\n  end\n\n  # @param resource [A Resource] a resource in the catalog\n  # @return [A Resource, nil] the resource that contains the given resource\n  # @api public\n  def container_of(resource)\n    adjacent(resource, :direction => :in)[0]\n  end\n\n  def add_one_resource(resource, idx = -1)\n    title_key = title_key_for_ref(resource.ref)\n    if @resource_table[title_key]\n      fail_on_duplicate_type_and_title(resource, title_key)\n    end\n\n    add_resource_to_table(resource, title_key, idx)\n    create_resource_aliases(resource)\n\n    resource.catalog = self if resource.respond_to?(:catalog=)\n    add_resource_to_graph(resource)\n  end\n  private :add_one_resource\n\n  def add_resource_to_table(resource, title_key, idx)\n    @resource_table[title_key] = resource\n    @resources.insert(idx, title_key)\n  end\n  private :add_resource_to_table\n\n  def add_resource_to_graph(resource)\n    add_vertex(resource)\n    @relationship_graph.add_vertex(resource) if @relationship_graph\n  end\n  private :add_resource_to_graph\n\n  def create_resource_aliases(resource)\n    # Explicit aliases must always be processed\n    # The alias setting logic checks, and does not error if the alias is set to an already set alias\n    # for the same resource (i.e. it is ok if alias == title\n    explicit_aliases = [resource[:alias]].flatten.compact\n    explicit_aliases.each { |given_alias| self.alias(resource, given_alias) }\n\n    # Skip creating uniqueness key alias and checking collisions for non-isomorphic resources.\n    return unless resource.respond_to?(:isomorphic?) and resource.isomorphic?\n\n    # Add an alias if the uniqueness key is valid and not the title, which has already been checked.\n    ukey = resource.uniqueness_key\n    if ukey.any? and ukey != [resource.title]\n      self.alias(resource, ukey)\n    end\n  end\n  private :create_resource_aliases\n\n  # Create an alias for a resource.\n  def alias(resource, key)\n    ref = resource.ref\n    ref =~ /^(.+)\\[/\n    class_name = ::Regexp.last_match(1) || resource.class.name\n\n    newref = [class_name, key].flatten\n\n    if key.is_a? String\n      ref_string = \"#{class_name}[#{key}]\"\n      return if ref_string == ref\n    end\n\n    # LAK:NOTE It's important that we directly compare the references,\n    # because sometimes an alias is created before the resource is\n    # added to the catalog, so comparing inside the below if block\n    # isn't sufficient.\n    existing = @resource_table[newref]\n    if existing\n      return if existing == resource\n\n      resource_declaration = Puppet::Util::Errors.error_location(resource.file, resource.line)\n      msg = if resource_declaration.empty?\n              # TRANSLATORS 'alias' should not be translated\n              _(\"Cannot alias %{resource} to %{key}; resource %{newref} already declared\") %\n                { resource: ref, key: key.inspect, newref: newref.inspect }\n            else\n              # TRANSLATORS 'alias' should not be translated\n              _(\"Cannot alias %{resource} to %{key} at %{resource_declaration}; resource %{newref} already declared\") %\n                { resource: ref, key: key.inspect, resource_declaration: resource_declaration, newref: newref.inspect }\n            end\n      msg += Puppet::Util::Errors.error_location_with_space(existing.file, existing.line)\n      raise ArgumentError, msg\n    end\n    @resource_table[newref] = resource\n    @aliases[ref] ||= []\n    @aliases[ref] << newref\n  end\n\n  # Apply our catalog to the local host.\n  # @param options [Hash{Symbol => Object}] a hash of options\n  # @option options [Puppet::Transaction::Report] :report\n  #   The report object to log this transaction to. This is optional,\n  #   and the resulting transaction will create a report if not\n  #   supplied.\n  #\n  # @return [Puppet::Transaction] the transaction created for this\n  #   application\n  #\n  # @api public\n  def apply(options = {})\n    Puppet::Util::Storage.load if host_config?\n\n    transaction = create_transaction(options)\n\n    begin\n      transaction.report.as_logging_destination do\n        transaction_evaluate_time = Puppet::Util.thinmark do\n          transaction.evaluate\n        end\n        transaction.report.add_times(:transaction_evaluation, transaction_evaluate_time)\n      end\n    ensure\n      # Don't try to store state unless we're a host config\n      # too recursive.\n      Puppet::Util::Storage.store if host_config?\n    end\n\n    yield transaction if block_given?\n\n    transaction\n  end\n\n  # The relationship_graph form of the catalog. This contains all of the\n  # dependency edges that are used for determining order.\n  #\n  # @param given_prioritizer [Puppet::Graph::Prioritizer] The prioritization\n  #   strategy to use when constructing the relationship graph. Defaults the\n  #   being determined by the `ordering` setting.\n  # @return [Puppet::Graph::RelationshipGraph]\n  # @api public\n  def relationship_graph(given_prioritizer = nil)\n    if @relationship_graph.nil?\n      @relationship_graph = Puppet::Graph::RelationshipGraph.new(given_prioritizer || prioritizer)\n      @relationship_graph.populate_from(self)\n    end\n    @relationship_graph\n  end\n\n  def clear(remove_resources = true)\n    super()\n    # We have to do this so that the resources clean themselves up.\n    @resource_table.values.each(&:remove) if remove_resources\n    @resource_table.clear\n    @resources = []\n\n    if @relationship_graph\n      @relationship_graph.clear\n      @relationship_graph = nil\n    end\n  end\n\n  def classes\n    @classes.dup\n  end\n\n  # Create a new resource and register it in the catalog.\n  def create_resource(type, options)\n    klass = Puppet::Type.type(type)\n    unless klass\n      raise ArgumentError, _(\"Unknown resource type %{type}\") % { type: type }\n    end\n\n    resource = klass.new(options)\n    return unless resource\n\n    add_resource(resource)\n    resource\n  end\n\n  # Make sure all of our resources are \"finished\".\n  def finalize\n    make_default_resources\n\n    @resource_table.values.each(&:finish)\n\n    write_graph(:resources)\n  end\n\n  def host_config?\n    host_config\n  end\n\n  def initialize(name = nil, environment = Puppet::Node::Environment::NONE, code_id = nil)\n    super()\n    @name = name\n    @catalog_uuid = SecureRandom.uuid\n    @catalog_format = 2\n    @metadata = {}\n    @recursive_metadata = {}\n    @classes = []\n    @resource_table = {}\n    @resources = []\n    @relationship_graph = nil\n\n    @host_config = true\n    @environment_instance = environment\n    @environment = environment.to_s\n    @code_id = code_id\n\n    @aliases = {}\n\n    if block_given?\n      yield(self)\n      finalize\n    end\n  end\n\n  # Make the default objects necessary for function.\n  def make_default_resources\n    # We have to add the resources to the catalog, or else they won't get cleaned up after\n    # the transaction.\n\n    # First create the default scheduling objects\n    Puppet::Type.type(:schedule).mkdefaultschedules.each { |res| add_resource(res) unless resource(res.ref) }\n\n    # And filebuckets\n    bucket = Puppet::Type.type(:filebucket).mkdefaultbucket\n    if bucket\n      add_resource(bucket) unless resource(bucket.ref)\n    end\n  end\n\n  # Remove the resource from our catalog.  Notice that we also call\n  # 'remove' on the resource, at least until resource classes no longer maintain\n  # references to the resource instances.\n  def remove_resource(*resources)\n    resources.each do |resource|\n      ref = resource.ref\n      title_key = title_key_for_ref(ref)\n      @resource_table.delete(title_key)\n      aliases = @aliases[ref]\n      if aliases\n        aliases.each { |res_alias| @resource_table.delete(res_alias) }\n        @aliases.delete(ref)\n      end\n      remove_vertex!(resource) if vertex?(resource)\n      @relationship_graph.remove_vertex!(resource) if @relationship_graph and @relationship_graph.vertex?(resource)\n      @resources.delete(title_key)\n      # Only Puppet::Type kind of resources respond to :remove, not Puppet::Resource\n      resource.remove if resource.respond_to?(:remove)\n    end\n  end\n\n  # Look a resource up by its reference (e.g., File[/etc/passwd]).\n  def resource(type, title = nil)\n    # Retain type if it's a type\n    type_name = type.is_a?(Puppet::CompilableResourceType) || type.is_a?(Puppet::Resource::Type) ? type.name : type\n    type_name, title = Puppet::Resource.type_and_title(type_name, title)\n    type = type_name if type.is_a?(String)\n    title_key = [type_name, title.to_s]\n    result = @resource_table[title_key]\n    if result.nil?\n      # an instance has to be created in order to construct the unique key used when\n      # searching for aliases\n      res = Puppet::Resource.new(type, title, { :environment => @environment_instance })\n\n      # Must check with uniqueness key because of aliases or if resource transforms title in title\n      # to attribute mappings.\n      result = @resource_table[[type_name, res.uniqueness_key].flatten]\n    end\n    result\n  end\n\n  def resource_refs\n    resource_keys.filter_map { |type, name| name.is_a?(String) ? \"#{type}[#{name}]\" : nil }\n  end\n\n  def resource_keys\n    @resource_table.keys\n  end\n\n  def resources\n    @resources.collect do |key|\n      @resource_table[key]\n    end\n  end\n\n  def self.from_data_hash(data)\n    result = new(data['name'], Puppet::Node::Environment::NONE)\n\n    result.tag(*data['tags']) if data['tags']\n    result.version = data['version'] if data['version']\n    result.code_id = data['code_id'] if data['code_id']\n    result.catalog_uuid = data['catalog_uuid'] if data['catalog_uuid']\n    result.catalog_format = data['catalog_format'] || 0\n\n    environment = data['environment']\n    if environment\n      result.environment = environment\n      result.environment_instance = Puppet::Node::Environment.remote(environment.to_sym)\n    end\n\n    result.add_resource(\n      *data['resources'].collect do |res|\n        Puppet::Resource.from_data_hash(res)\n      end\n    ) if data['resources']\n\n    if data['edges']\n      data['edges'].each do |edge_hash|\n        edge = Puppet::Relationship.from_data_hash(edge_hash)\n        source = result.resource(edge.source)\n        unless source\n          raise ArgumentError, _(\"Could not intern from data: Could not find relationship source %{source} for %{target}\") %\n                               { source: edge.source.inspect, target: edge.target.to_s }\n        end\n        edge.source = source\n\n        target = result.resource(edge.target)\n        unless target\n          raise ArgumentError, _(\"Could not intern from data: Could not find relationship target %{target} for %{source}\") %\n                               { target: edge.target.inspect, source: edge.source.to_s }\n        end\n        edge.target = target\n\n        result.add_edge(edge)\n      end\n    end\n\n    result.add_class(*data['classes']) if data['classes']\n\n    result.metadata = data['metadata'].transform_values { |v| Puppet::FileServing::Metadata.from_data_hash(v); } if data['metadata']\n\n    recursive_metadata = data['recursive_metadata']\n    if recursive_metadata\n      result.recursive_metadata = recursive_metadata.transform_values do |source_to_meta_hash|\n        source_to_meta_hash.transform_values do |metas|\n          metas.map { |meta| Puppet::FileServing::Metadata.from_data_hash(meta) }\n        end\n      end\n    end\n\n    result\n  end\n\n  def to_data_hash\n    metadata_hash = metadata.transform_values(&:to_data_hash)\n    recursive_metadata_hash = recursive_metadata.transform_values do |source_to_meta_hash|\n      source_to_meta_hash.transform_values do |metas|\n        metas.map(&:to_data_hash)\n      end\n    end\n\n    {\n      'tags' => tags.to_a,\n      'name' => name,\n      'version' => version,\n      'code_id' => code_id,\n      'catalog_uuid' => catalog_uuid,\n      'catalog_format' => catalog_format,\n      'environment' => environment.to_s,\n      'resources' => @resources.map { |v| @resource_table[v].to_data_hash },\n      'edges' => edges.map(&:to_data_hash),\n      'classes' => classes,\n    }.merge(metadata_hash.empty? ?\n      {} : { 'metadata' => metadata_hash }).merge(recursive_metadata_hash.empty? ?\n        {} : { 'recursive_metadata' => recursive_metadata_hash })\n  end\n\n  # Convert our catalog into a RAL catalog.\n  def to_ral\n    to_catalog :to_ral\n  end\n\n  # Convert our catalog into a catalog of Puppet::Resource instances.\n  def to_resource\n    to_catalog :to_resource\n  end\n\n  # filter out the catalog, applying +block+ to each resource.\n  # If the block result is false, the resource will\n  # be kept otherwise it will be skipped\n  def filter(&block)\n    # to_catalog must take place in a context where current_environment is set to the same env as the\n    # environment set in the catalog (if it is set)\n    # See PUP-3755\n    if environment_instance\n      Puppet.override({ :current_environment => environment_instance }) do\n        to_catalog :to_resource, &block\n      end\n    else\n      # If catalog has no environment_instance, hope that the caller has made sure the context has the\n      # correct current_environment\n      to_catalog :to_resource, &block\n    end\n  end\n\n  # Store the classes in the classfile.\n  def write_class_file\n    # classfile paths may contain UTF-8\n    # https://puppet.com/docs/puppet/latest/configuration.html#classfile\n    classfile = Puppet.settings.setting(:classfile)\n    Puppet::FileSystem.open(classfile.value, classfile.mode.to_i(8), \"w:UTF-8\") do |f|\n      f.puts classes.join(\"\\n\")\n    end\n  rescue => detail\n    Puppet.err _(\"Could not create class file %{file}: %{detail}\") % { file: Puppet[:classfile], detail: detail }\n  end\n\n  # Store the list of resources we manage\n  def write_resource_file\n    # resourcefile contains resources that may be UTF-8 names\n    # https://puppet.com/docs/puppet/latest/configuration.html#resourcefile\n    resourcefile = Puppet.settings.setting(:resourcefile)\n    Puppet::FileSystem.open(resourcefile.value, resourcefile.mode.to_i(8), \"w:UTF-8\") do |f|\n      to_print = resources.filter_map do |resource|\n        next unless resource.managed?\n\n        resource.ref.downcase.to_s\n      end\n      f.puts to_print.join(\"\\n\")\n    end\n  rescue => detail\n    Puppet.err _(\"Could not create resource file %{file}: %{detail}\") % { file: Puppet[:resourcefile], detail: detail }\n  end\n\n  # Produce the graph files if requested.\n  def write_graph(name)\n    # We only want to graph the main host catalog.\n    return unless host_config?\n\n    super\n  end\n\n  private\n\n  def prioritizer\n    @prioritizer = Puppet::Graph::SequentialPrioritizer.new\n  end\n\n  def create_transaction(options)\n    transaction = Puppet::Transaction.new(self, options[:report], prioritizer)\n    transaction.tags = options[:tags] if options[:tags]\n    transaction.ignoreschedules = true if options[:ignoreschedules]\n    transaction.for_network_device = Puppet.lookup(:network_device) { nil } || options[:network_device]\n\n    transaction\n  end\n\n  # Verify that the given resource isn't declared elsewhere.\n  def fail_on_duplicate_type_and_title(resource, title_key)\n    # Short-circuit the common case,\n    existing_resource = @resource_table[title_key]\n    return unless existing_resource\n\n    # If we've gotten this far, it's a real conflict\n    error_location_str = Puppet::Util::Errors.error_location(existing_resource.file, existing_resource.line)\n    msg = if error_location_str.empty?\n            _(\"Duplicate declaration: %{resource} is already declared; cannot redeclare\") % { resource: resource.ref }\n          else\n            _(\"Duplicate declaration: %{resource} is already declared at %{error_location}; cannot redeclare\") % { resource: resource.ref, error_location: error_location_str }\n          end\n    raise DuplicateResourceError.new(msg, resource.file, resource.line)\n  end\n\n  # An abstracted method for converting one catalog into another type of catalog.\n  # This pretty much just converts all of the resources from one class to another, using\n  # a conversion method.\n  def to_catalog(convert)\n    result = self.class.new(name, environment_instance)\n\n    result.version = version\n    result.code_id = code_id\n    result.catalog_uuid = catalog_uuid\n    result.catalog_format = catalog_format\n    result.metadata = metadata\n    result.recursive_metadata = recursive_metadata\n\n    map = {}\n    resources.each do |resource|\n      next if virtual_not_exported?(resource)\n      next if block_given? and yield resource\n\n      newres = resource.copy_as_resource\n      newres.catalog = result\n\n      if convert != :to_resource\n        newres = newres.to_ral\n      end\n\n      # We can't guarantee that resources don't munge their names\n      # (like files do with trailing slashes), so we have to keep track\n      # of what a resource got converted to.\n      map[resource.ref] = newres\n\n      result.add_resource newres\n    end\n\n    message = convert.to_s.tr \"_\", \" \"\n    edges.each do |edge|\n      # Skip edges between virtual resources.\n      next if virtual_not_exported?(edge.source)\n      next if block_given? and yield edge.source\n\n      next if virtual_not_exported?(edge.target)\n      next if block_given? and yield edge.target\n\n      source = map[edge.source.ref]\n      unless source\n        raise Puppet::DevError, _(\"Could not find resource %{resource} when converting %{message} resources\") % { resource: edge.source.ref, message: message }\n      end\n\n      target = map[edge.target.ref]\n      unless target\n        raise Puppet::DevError, _(\"Could not find resource %{resource} when converting %{message} resources\") % { resource: edge.target.ref, message: message }\n      end\n\n      result.add_edge(source, target, edge.label)\n    end\n\n    map.clear\n\n    result.add_class(*classes)\n    result.merge_tags_from(self)\n\n    result\n  end\n\n  def virtual_not_exported?(resource)\n    resource.virtual && !resource.exported\n  end\nend\n"
  },
  {
    "path": "lib/puppet/resource/status.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'time'\nrequire_relative '../../puppet/network/format_support'\nrequire_relative '../../puppet/util/psych_support'\n\nmodule Puppet\n  class Resource\n    # This class represents the result of evaluating a given resource. It\n    # contains file and line information about the source, events generated\n    # while evaluating the resource, timing information, and the status of the\n    # resource evaluation.\n    #\n    # @api private\n    class Status\n      include Puppet::Util::PsychSupport\n      include Puppet::Util::Tagging\n      include Puppet::Network::FormatSupport\n\n      # @!attribute [rw] file\n      #   @return [String] The file where `@real_resource` was defined.\n      attr_accessor :file\n\n      # @!attribute [rw] line\n      #   @return [Integer] The line number in the file where `@real_resource` was defined.\n      attr_accessor :line\n\n      # @!attribute [rw] evaluation_time\n      #   @return [Float] The time elapsed in sections while evaluating `@real_resource`.\n      #     measured in seconds.\n      attr_accessor :evaluation_time\n\n      # Boolean status types set while evaluating `@real_resource`.\n      STATES = [:skipped, :failed, :failed_to_restart, :restarted, :changed, :out_of_sync, :scheduled, :corrective_change]\n      attr_accessor(*STATES)\n\n      # @!attribute [r] source_description\n      #   @return [String] The textual description of the path to `@real_resource`\n      #     based on the containing structures. This is in contrast to\n      #     `@containment_path` which is a list of containment path components.\n      #   @example\n      #     status.source_description #=> \"/Stage[main]/Myclass/Exec[date]\"\n      attr_reader :source_description\n\n      # @!attribute [r] containment_path\n      #   @return [Array<String>] A list of resource references that contain\n      #     `@real_resource`.\n      #   @example A normal contained type\n      #     status.containment_path #=> [\"Stage[main]\", \"Myclass\", \"Exec[date]\"]\n      #   @example A whit associated with a class\n      #     status.containment_path #=> [\"Whit[Admissible_class[Main]]\"]\n      attr_reader :containment_path\n\n      # @!attribute [r] time\n      #   @return [Time] The time that this status object was created\n      attr_reader :time\n\n      # @!attribute [r] resource\n      #   @return [String] The resource reference for `@real_resource`\n      attr_reader :resource\n\n      # @!attribute [r] change_count\n      #   @return [Integer] A count of the successful changes made while\n      #     evaluating `@real_resource`.\n      attr_reader :change_count\n\n      # @!attribute [r] out_of_sync_count\n      #   @return [Integer] A count of the audited changes made while\n      #     evaluating `@real_resource`.\n      attr_reader :out_of_sync_count\n\n      # @!attribute [r] resource_type\n      #   @example\n      #     status.resource_type #=> 'Notify'\n      #   @return [String] The class name of `@real_resource`\n      attr_reader :resource_type\n\n      # @!attribute [rw] provider_used\n      #   @return [String] The class name of the provider used for the resource\n      attr_accessor :provider_used\n\n      # @!attribute [r] title\n      #   @return [String] The title of `@real_resource`\n      attr_reader :title\n\n      # @!attribute [r] events\n      #   @return [Array<Puppet::Transaction::Event>] A list of events generated\n      #     while evaluating `@real_resource`.\n      attr_reader :events\n\n      # @!attribute [r] corrective_change\n      #   @return [Boolean] true if the resource contained a corrective change.\n      attr_reader :corrective_change\n\n      # @!attribute [rw] failed_dependencies\n      #   @return [Array<Puppet::Resource>] A cache of all\n      #   dependencies of this resource that failed to apply.\n      attr_accessor :failed_dependencies\n\n      def dependency_failed?\n        failed_dependencies && !failed_dependencies.empty?\n      end\n\n      def self.from_data_hash(data)\n        obj = allocate\n        obj.initialize_from_hash(data)\n        obj\n      end\n\n      # Provide a boolean method for each of the states.\n      STATES.each do |attr|\n        define_method(\"#{attr}?\") do\n          !!send(attr)\n        end\n      end\n\n      def <<(event)\n        add_event(event)\n        self\n      end\n\n      def add_event(event)\n        @events << event\n        case event.status\n        when 'failure'\n          self.failed = true\n        when 'success'\n          @change_count += 1\n          @changed = true\n        end\n        if event.status != 'audit'\n          @out_of_sync_count += 1\n          @out_of_sync = true\n        end\n        if event.corrective_change\n          @corrective_change = true\n        end\n      end\n\n      def failed_because(detail)\n        @real_resource.log_exception(detail, _(\"Could not evaluate: %{detail}\") % { detail: detail })\n        # There's a contract (implicit unfortunately) that a status of failed\n        # will always be accompanied by an event with some explanatory power.  This\n        # is useful for reporting/diagnostics/etc.  So synthesize an event here\n        # with the exception detail as the message.\n        fail_with_event(detail.to_s)\n      end\n\n      # Both set the status state to failed and generate a corresponding\n      # Puppet::Transaction::Event failure with the given message.\n      # @param message [String] the reason for a status failure\n      def fail_with_event(message)\n        add_event(@real_resource.event(:name => :resource_error, :status => \"failure\", :message => message))\n      end\n\n      def initialize(resource)\n        @real_resource = resource\n        @source_description = resource.path\n        @containment_path = resource.pathbuilder\n        @resource = resource.to_s\n        @change_count = 0\n        @out_of_sync_count = 0\n        @changed = false\n        @out_of_sync = false\n        @skipped = false\n        @failed = false\n        @corrective_change = false\n\n        @file = resource.file\n        @line = resource.line\n\n        merge_tags_from(resource)\n        @time = Time.now\n        @events = []\n        @resource_type = resource.type.to_s.capitalize\n        @provider_used = resource.provider.class.name.to_s unless resource.provider.nil?\n        @title = resource.title\n      end\n\n      def initialize_from_hash(data)\n        @resource_type = data['resource_type']\n        @provider_used = data['provider_used']\n        @title = data['title']\n        @resource = data['resource']\n        @containment_path = data['containment_path']\n        @file = data['file']\n        @line = data['line']\n        @evaluation_time = data['evaluation_time']\n        @change_count = data['change_count']\n        @out_of_sync_count = data['out_of_sync_count']\n        @tags = Puppet::Util::TagSet.new(data['tags'])\n        @time = data['time']\n        @time = Time.parse(@time) if @time.is_a? String\n        @out_of_sync = data['out_of_sync']\n        @changed = data['changed']\n        @skipped = data['skipped']\n        @failed = data['failed']\n        @failed_to_restart = data['failed_to_restart']\n        @corrective_change = data['corrective_change']\n        @events = data['events'].map do |event|\n          # Older versions contain tags that causes Psych to create instances directly\n          event.is_a?(Puppet::Transaction::Event) ? event : Puppet::Transaction::Event.from_data_hash(event)\n        end\n      end\n\n      def to_data_hash\n        {\n          'title' => @title,\n          'file' => @file,\n          'line' => @line,\n          'resource' => @resource,\n          'resource_type' => @resource_type,\n          'provider_used' => @provider_used,\n          'containment_path' => @containment_path,\n          'evaluation_time' => @evaluation_time,\n          'tags' => @tags.to_a,\n          'time' => @time.iso8601(9),\n          'failed' => @failed,\n          'failed_to_restart' => failed_to_restart?,\n          'changed' => @changed,\n          'out_of_sync' => @out_of_sync,\n          'skipped' => @skipped,\n          'change_count' => @change_count,\n          'out_of_sync_count' => @out_of_sync_count,\n          'events' => @events.map(&:to_data_hash),\n          'corrective_change' => @corrective_change,\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/resource/type.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parser'\nrequire_relative '../../puppet/util/warnings'\nrequire_relative '../../puppet/util/errors'\nrequire_relative '../../puppet/parser/ast/leaf'\n\n# Puppet::Resource::Type represents nodes, classes and defined types.\n#\n# @api public\nclass Puppet::Resource::Type\n  Puppet::ResourceType = self\n  include Puppet::Util::Warnings\n  include Puppet::Util::Errors\n\n  RESOURCE_KINDS = [:hostclass, :node, :definition]\n\n  # Map the names used in our documentation to the names used internally\n  RESOURCE_KINDS_TO_EXTERNAL_NAMES = {\n    :hostclass => \"class\",\n    :node => \"node\",\n    :definition => \"defined_type\"\n  }\n  RESOURCE_EXTERNAL_NAMES_TO_KINDS = RESOURCE_KINDS_TO_EXTERNAL_NAMES.invert\n\n  NAME = 'name'\n  TITLE = 'title'\n  MODULE_NAME = 'module_name'\n  CALLER_MODULE_NAME = 'caller_module_name'\n  PARAMETERS = 'parameters'\n  KIND = 'kind'\n  NODES = 'nodes'\n  DOUBLE_COLON = '::'\n  EMPTY_ARRAY = [].freeze\n\n  LOOKAROUND_OPERATORS = {\n    \"(\" => 'LP',\n    \"?\" => \"QU\",\n    \"<\" => \"LT\",\n    \">\" => \"GT\",\n    \"!\" => \"EX\",\n    \"=\" => \"EQ\",\n    \")\" => 'RP'\n  }.freeze\n\n  attr_accessor :file, :line, :doc, :code, :parent, :resource_type_collection, :override\n  attr_reader :namespace, :arguments, :behaves_like, :module_name\n\n  # Map from argument (aka parameter) names to Puppet Type\n  # @return [Hash<Symbol, Puppet::Pops::Types::PAnyType] map from name to type\n  #\n  attr_reader :argument_types\n\n  # This should probably be renamed to 'kind' eventually, in accordance with the changes\n  #  made for serialization and API usability (#14137).  At the moment that seems like\n  #  it would touch a whole lot of places in the code, though.  --cprice 2012-04-23\n  attr_reader :type\n\n  RESOURCE_KINDS.each do |t|\n    define_method(\"#{t}?\") { type == t }\n  end\n\n  # Are we a child of the passed class?  Do a recursive search up our\n  # parentage tree to figure it out.\n  def child_of?(klass)\n    return true if override\n    return false unless parent\n\n    (klass == parent_type ? true : parent_type.child_of?(klass))\n  end\n\n  # Now evaluate the code associated with this class or definition.\n  def evaluate_code(resource)\n    static_parent = evaluate_parent_type(resource)\n    scope = static_parent || resource.scope\n\n    scope = scope.newscope(:source => self, :resource => resource) unless resource.title == :main\n    scope.compiler.add_class(name) unless definition?\n\n    set_resource_parameters(resource, scope)\n\n    resource.add_edge_to_stage\n\n    if code\n      if @match # Only bother setting up the ephemeral scope if there are match variables to add into it\n        scope.with_guarded_scope do\n          scope.ephemeral_from(@match, file, line)\n          code.safeevaluate(scope)\n        end\n      else\n        code.safeevaluate(scope)\n      end\n    end\n  end\n\n  def initialize(type, name, options = {})\n    @type = type.to_s.downcase.to_sym\n    raise ArgumentError, _(\"Invalid resource supertype '%{type}'\") % { type: type } unless RESOURCE_KINDS.include?(@type)\n\n    name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName)\n\n    set_name_and_namespace(name)\n\n    [:code, :doc, :line, :file, :parent].each do |param|\n      value = options[param]\n      next unless value\n\n      send(param.to_s + '=', value)\n    end\n\n    set_arguments(options[:arguments])\n    set_argument_types(options[:argument_types])\n\n    @match = nil\n\n    @module_name = options[:module_name]\n  end\n\n  # This is only used for node names, and really only when the node name\n  # is a regexp.\n  def match(string)\n    return string.to_s.downcase == name unless name_is_regex?\n\n    @match = @name.match(string)\n  end\n\n  # Add code from a new instance to our code.\n  def merge(other)\n    fail _(\"%{name} is not a class; cannot add code to it\") % { name: name } unless type == :hostclass\n    fail _(\"%{name} is not a class; cannot add code from it\") % { name: other.name } unless other.type == :hostclass\n\n    if name == \"\" && Puppet.settings[:freeze_main]\n      # It is ok to merge definitions into main even if freeze is on (definitions are nodes, classes, defines, functions, and types)\n      unless other.code.is_definitions_only?\n        fail _(\"Cannot have code outside of a class/node/define because 'freeze_main' is enabled\")\n      end\n    end\n    if parent and other.parent and parent != other.parent\n      fail _(\"Cannot merge classes with different parent classes (%{name} => %{parent} vs. %{other_name} => %{other_parent})\") % { name: name, parent: parent, other_name: other.name, other_parent: other.parent }\n    end\n\n    # We know they're either equal or only one is set, so keep whichever parent is specified.\n    self.parent ||= other.parent\n\n    if other.doc\n      self.doc ||= \"\"\n      self.doc += other.doc\n    end\n\n    # This might just be an empty, stub class.\n    return unless other.code\n\n    unless code\n      self.code = other.code\n      return\n    end\n\n    self.code = Puppet::Parser::ParserFactory.code_merger.concatenate([self, other])\n  end\n\n  # Make an instance of the resource type, and place it in the catalog\n  # if it isn't in the catalog already.  This is only possible for\n  # classes and nodes.  No parameters are be supplied--if this is a\n  # parameterized class, then all parameters take on their default\n  # values.\n  def ensure_in_catalog(scope, parameters = nil)\n    resource_type =\n      case type\n      when :definition\n        raise ArgumentError, _('Cannot create resources for defined resource types')\n      when :hostclass\n        :class\n      when :node\n        :node\n      end\n\n    # Do nothing if the resource already exists; this makes sure we don't\n    # get multiple copies of the class resource, which helps provide the\n    # singleton nature of classes.\n    # we should not do this for classes with parameters\n    # if parameters are passed, we should still try to create the resource\n    # even if it exists so that we can fail\n    # this prevents us from being able to combine param classes with include\n    if parameters.nil?\n      resource = scope.catalog.resource(resource_type, name)\n      return resource unless resource.nil?\n    elsif parameters.is_a?(Hash)\n      parameters = parameters.map { |k, v| Puppet::Parser::Resource::Param.new(:name => k, :value => v, :source => self) }\n    end\n    resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self, :parameters => parameters)\n    instantiate_resource(scope, resource)\n    scope.compiler.add_resource(scope, resource)\n    resource\n  end\n\n  def instantiate_resource(scope, resource)\n    # Make sure our parent class has been evaluated, if we have one.\n    if parent && !scope.catalog.resource(resource.type, parent)\n      parent_type(scope).ensure_in_catalog(scope)\n    end\n\n    if %w[Class Node].include? resource.type\n      scope.catalog.merge_tags_from(resource)\n    end\n  end\n\n  def name\n    if type == :node && name_is_regex?\n      # Normalize lookarround regex patthern\n      internal_name = @name.source.downcase.gsub(/\\(\\?[^)]*\\)/) do |str|\n        str.gsub(/./) { |ch| LOOKAROUND_OPERATORS[ch] || ch }\n      end\n      \"__node_regexp__#{internal_name.gsub(/[^-\\w:.]/, '').sub(/^\\.+/, '')}\"\n    else\n      @name\n    end\n  end\n\n  def name_is_regex?\n    @name.is_a?(Regexp)\n  end\n\n  def parent_type(scope = nil)\n    return nil unless parent\n\n    @parent_type ||= scope.environment.known_resource_types.send(\"find_#{type}\", parent) ||\n                     fail(Puppet::ParseError, _(\"Could not find parent resource type '%{parent}' of type %{parent_type} in %{env}\") % { parent: parent, parent_type: type, env: scope.environment })\n  end\n\n  # Validate and set any arguments passed by the resource as variables in the scope.\n  #\n  # This method is known to only be used on the server/compile side.\n  #\n  # @param resource [Puppet::Parser::Resource] the resource\n  # @param scope [Puppet::Parser::Scope] the scope\n  #\n  # @api private\n  def set_resource_parameters(resource, scope)\n    # Inject parameters from using external lookup\n    modname = resource[:module_name] || module_name\n    scope[MODULE_NAME] = modname unless modname.nil?\n    caller_name = resource[:caller_module_name] || scope.parent_module_name\n    scope[CALLER_MODULE_NAME] = caller_name unless caller_name.nil?\n\n    inject_external_parameters(resource, scope)\n\n    if @type == :hostclass\n      scope[TITLE] = resource.title.to_s.downcase\n      scope[NAME] = resource.name.to_s.downcase\n    else\n      scope[TITLE] = resource.title\n      scope[NAME] = resource.name\n    end\n    scope.class_set(name, scope) if hostclass? || node?\n\n    param_hash = scope.with_parameter_scope(resource.to_s, arguments.keys) do |param_scope|\n      # Assign directly to the parameter scope to avoid scope parameter validation at this point. It\n      # will happen anyway when the values are assigned to the scope after the parameter scoped has\n      # been popped.\n      resource.each { |k, v| param_scope[k.to_s] = v.value unless k == :name || k == :title }\n      assign_defaults(resource, param_scope, scope)\n      param_scope.to_hash\n    end\n\n    validate_resource_hash(resource, param_hash)\n\n    # Assign parameter values to current scope\n    param_hash.each { |param, value| exceptwrap { scope[param] = value } }\n  end\n\n  # Lookup and inject parameters from external scope\n  # @param resource [Puppet::Parser::Resource] the resource\n  # @param scope [Puppet::Parser::Scope] the scope\n  def inject_external_parameters(resource, scope)\n    # Only lookup parameters for host classes\n    return unless type == :hostclass\n\n    parameters = resource.parameters\n    arguments.each do |param_name, default|\n      sym_name = param_name.to_sym\n      param = parameters[sym_name]\n      next unless param.nil? || param.value.nil?\n\n      catch(:no_such_key) do\n        bound_value = Puppet::Pops::Lookup.search_and_merge(\"#{name}::#{param_name}\", Puppet::Pops::Lookup::Invocation.new(scope), nil)\n        # Assign bound value but don't let an undef trump a default expression\n        resource[sym_name] = bound_value unless bound_value.nil? && !default.nil?\n      end\n    end\n  end\n  private :inject_external_parameters\n\n  def assign_defaults(resource, param_scope, scope)\n    return unless resource.is_a?(Puppet::Parser::Resource)\n\n    parameters = resource.parameters\n    arguments.each do |param_name, default|\n      next if default.nil?\n\n      name = param_name.to_sym\n      param = parameters[name]\n      next unless param.nil? || param.value.nil?\n\n      value = exceptwrap { param_scope.evaluate3x(param_name, default, scope) }\n      resource[name] = value\n      param_scope[param_name] = value\n    end\n  end\n  private :assign_defaults\n\n  def validate_resource_hash(resource, resource_hash)\n    Puppet::Pops::Types::TypeMismatchDescriber.validate_parameters(resource.to_s, parameter_struct, resource_hash, false)\n  end\n  private :validate_resource_hash\n\n  # Validate that all parameters given to the resource are correct\n  # @param resource [Puppet::Resource] the resource to validate\n  def validate_resource(resource)\n    # Since Sensitive values have special encoding (in a separate parameter) an unwrapped sensitive value must be\n    # recreated as a Sensitive in order to perform correct type checking.\n    sensitives = Set.new(resource.sensitive_parameters)\n    validate_resource_hash(resource,\n                           resource.parameters.to_h do |name, value|\n                             value_to_validate = sensitives.include?(name) ? Puppet::Pops::Types::PSensitiveType::Sensitive.new(value.value) : value.value\n                             [name.to_s, value_to_validate]\n                           end)\n  end\n\n  # Check whether a given argument is valid.\n  def valid_parameter?(param)\n    parameter_struct.hashed_elements.include?(param.to_s)\n  end\n\n  def set_arguments(arguments)\n    @arguments = {}\n    @parameter_struct = nil\n    return if arguments.nil?\n\n    arguments.each do |arg, default|\n      arg = arg.to_s\n      warn_if_metaparam(arg, default)\n      @arguments[arg] = default\n    end\n  end\n\n  # Sets the argument name to Puppet Type hash used for type checking.\n  # Names must correspond to available arguments (they must be defined first).\n  # Arguments not mentioned will not be type-checked.\n  #\n  def set_argument_types(name_to_type_hash)\n    @argument_types = {}\n    @parameter_struct = nil\n    return unless name_to_type_hash\n\n    name_to_type_hash.each do |name, t|\n      # catch internal errors\n      unless @arguments.include?(name)\n        raise Puppet::DevError, _(\"Parameter '%{name}' is given a type, but is not a valid parameter.\") % { name: name }\n      end\n      unless t.is_a? Puppet::Pops::Types::PAnyType\n        raise Puppet::DevError, _(\"Parameter '%{name}' is given a type that is not a Puppet Type, got %{class_name}\") % { name: name, class_name: t.class }\n      end\n\n      @argument_types[name] = t\n    end\n  end\n\n  private\n\n  def convert_from_ast(name)\n    value = name.value\n    if value.is_a?(Puppet::Parser::AST::Regex)\n      value.value\n    else\n      value\n    end\n  end\n\n  def evaluate_parent_type(resource)\n    klass = parent_type(resource.scope)\n    parent_resource = resource.scope.compiler.catalog.resource(:class, klass.name) || resource.scope.compiler.catalog.resource(:node, klass.name) if klass\n    return unless klass && parent_resource\n\n    parent_resource.evaluate unless parent_resource.evaluated?\n    parent_scope(resource.scope, klass)\n  end\n\n  # Split an fq name into a namespace and name\n  def namesplit(fullname)\n    ary = fullname.split(DOUBLE_COLON)\n    n = ary.pop || \"\"\n    ns = ary.join(DOUBLE_COLON)\n    [ns, n]\n  end\n\n  def parent_scope(scope, klass)\n    scope.class_scope(klass) || raise(Puppet::DevError, _(\"Could not find scope for %{class_name}\") % { class_name: klass.name })\n  end\n\n  def set_name_and_namespace(name)\n    if name.is_a?(Regexp)\n      @name = name\n      @namespace = \"\"\n    else\n      @name = name.to_s.downcase\n\n      # Note we're doing something somewhat weird here -- we're setting\n      # the class's namespace to its fully qualified name.  This means\n      # anything inside that class starts looking in that namespace first.\n      @namespace, _ = @type == :hostclass ? [@name, ''] : namesplit(@name)\n    end\n  end\n\n  def warn_if_metaparam(param, default)\n    return unless Puppet::Type.metaparamclass(param)\n\n    if default\n      warnonce _(\"%{param} is a metaparam; this value will inherit to all contained resources in the %{name} definition\") % { param: param, name: name }\n    else\n      raise Puppet::ParseError, _(\"%{param} is a metaparameter; please choose another parameter name in the %{name} definition\") % { param: param, name: name }\n    end\n  end\n\n  def parameter_struct\n    @parameter_struct ||= create_params_struct\n  end\n\n  def create_params_struct\n    arg_types = argument_types\n    type_factory = Puppet::Pops::Types::TypeFactory\n    members = { type_factory.optional(type_factory.string(NAME)) => type_factory.any }\n\n    Puppet::Type.eachmetaparam do |name|\n      # TODO: Once meta parameters are typed, this should change to reflect that type\n      members[name.to_s] = type_factory.any\n    end\n\n    arguments.each_pair do |name, default|\n      key_type = type_factory.string(name.to_s)\n      key_type = type_factory.optional(key_type) unless default.nil?\n\n      arg_type = arg_types[name]\n      arg_type = type_factory.any if arg_type.nil?\n      members[key_type] = arg_type\n    end\n    type_factory.struct(members)\n  end\n  private :create_params_struct\nend\n"
  },
  {
    "path": "lib/puppet/resource/type_collection.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parser/type_loader'\nrequire_relative '../../puppet/util/file_watcher'\nrequire_relative '../../puppet/util/warnings'\nrequire_relative '../../puppet/concurrent/lock'\n\n# @api private\nclass Puppet::Resource::TypeCollection\n  attr_reader :environment\n  attr_accessor :parse_failed\n\n  include Puppet::Util::Warnings\n\n  def clear\n    @hostclasses.clear\n    @definitions.clear\n    @nodes.clear\n    @notfound.clear\n  end\n\n  def initialize(env)\n    @environment = env\n    @hostclasses = {}\n    @definitions = {}\n    @nodes = {}\n    @notfound = {}\n    # always lock the environment before acquiring this lock\n    @lock = Puppet::Concurrent::Lock.new\n\n    # So we can keep a list and match the first-defined regex\n    @node_list = []\n  end\n\n  def import_ast(ast, modname)\n    ast.instantiate(modname).each do |instance|\n      add(instance)\n    end\n  end\n\n  def inspect\n    \"TypeCollection\" + {\n      :hostclasses => @hostclasses.keys,\n      :definitions => @definitions.keys,\n      :nodes => @nodes.keys\n    }.inspect\n  end\n\n  # @api private\n  def <<(thing)\n    add(thing)\n    self\n  end\n\n  def add(instance)\n    # return a merged instance, or the given\n    catch(:merged) {\n      send(\"add_#{instance.type}\", instance)\n      instance.resource_type_collection = self\n      instance\n    }\n  end\n\n  def add_hostclass(instance)\n    handle_hostclass_merge(instance)\n    dupe_check(instance, @hostclasses) { |dupe| _(\"Class '%{klass}' is already defined%{error}; cannot redefine\") % { klass: instance.name, error: dupe.error_context } }\n    dupe_check(instance, @nodes)       { |dupe| _(\"Node '%{klass}' is already defined%{error}; cannot be redefined as a class\") % { klass: instance.name, error: dupe.error_context } }\n    dupe_check(instance, @definitions) { |dupe| _(\"Definition '%{klass}' is already defined%{error}; cannot be redefined as a class\") % { klass: instance.name, error: dupe.error_context } }\n\n    @hostclasses[instance.name] = instance\n    instance\n  end\n\n  def handle_hostclass_merge(instance)\n    # Only main class (named '') can be merged (for purpose of merging top-scopes).\n    return instance unless instance.name == ''\n\n    if instance.type == :hostclass && (other = @hostclasses[instance.name]) && other.type == :hostclass\n      other.merge(instance)\n      # throw is used to signal merge - avoids dupe checks and adding it to hostclasses\n      throw :merged, other\n    end\n  end\n\n  # Replaces the known settings with a new instance (that must be named 'settings').\n  # This is primarily needed for testing purposes. Also see PUP-5954 as it makes\n  # it illegal to merge classes other than the '' (main) class. Prior to this change\n  # settings where always merged rather than being defined from scratch for many testing scenarios\n  # not having a complete side effect free setup for compilation.\n  #\n  def replace_settings(instance)\n    @hostclasses['settings'] = instance\n  end\n\n  def hostclass(name)\n    @hostclasses[munge_name(name)]\n  end\n\n  def add_node(instance)\n    dupe_check(instance, @nodes) { |dupe| _(\"Node '%{name}' is already defined%{error}; cannot redefine\") % { name: instance.name, error: dupe.error_context } }\n    dupe_check(instance, @hostclasses) { |dupe| _(\"Class '%{klass}' is already defined%{error}; cannot be redefined as a node\") % { klass: instance.name, error: dupe.error_context } }\n\n    @node_list << instance\n    @nodes[instance.name] = instance\n    instance\n  end\n\n  def loader\n    @loader ||= Puppet::Parser::TypeLoader.new(environment)\n  end\n\n  def node(name)\n    name = munge_name(name)\n\n    node = @nodes[name]\n    if node\n      return node\n    end\n\n    @node_list.each do |n|\n      next unless n.name_is_regex?\n      return n if n.match(name)\n    end\n    nil\n  end\n\n  def node_exists?(name)\n    @nodes[munge_name(name)]\n  end\n\n  def nodes?\n    @nodes.length > 0\n  end\n\n  def add_definition(instance)\n    dupe_check(instance, @hostclasses) { |dupe| _(\"'%{name}' is already defined%{error} as a class; cannot redefine as a definition\") % { name: instance.name, error: dupe.error_context } }\n    dupe_check(instance, @definitions) { |dupe| _(\"Definition '%{name}' is already defined%{error}; cannot be redefined\") % { name: instance.name, error: dupe.error_context } }\n\n    @definitions[instance.name] = instance\n  end\n\n  def definition(name)\n    @definitions[munge_name(name)]\n  end\n\n  def find_node(name)\n    @nodes[munge_name(name)]\n  end\n\n  def find_hostclass(name)\n    find_or_load(name, :hostclass)\n  end\n\n  def find_definition(name)\n    find_or_load(name, :definition)\n  end\n\n  # TODO: This implementation is wasteful as it creates a copy on each request\n  #\n  [:hostclasses, :nodes, :definitions].each do |m|\n    define_method(m) do\n      instance_variable_get(\"@#{m}\").dup\n    end\n  end\n\n  def parse_failed?\n    @parse_failed\n  end\n\n  def version\n    unless defined?(@version)\n      if environment.config_version.nil? || environment.config_version == \"\"\n        @version = Time.now.to_i\n      else\n        @version = Puppet::Util::Execution.execute([environment.config_version]).to_s.strip\n      end\n    end\n\n    @version\n  rescue Puppet::ExecutionFailure => e\n    raise Puppet::ParseError, _(\"Execution of config_version command `%{cmd}` failed: %{message}\") % { cmd: environment.config_version, message: e.message }, e.backtrace\n  end\n\n  private\n\n  COLON_COLON = \"::\"\n\n  # Resolve namespaces and find the given object.  Autoload it if\n  # necessary.\n  def find_or_load(name, type)\n    # always lock the environment before locking the type collection\n    @environment.lock.synchronize do\n      @lock.synchronize do\n        # Name is always absolute, but may start with :: which must be removed\n        fqname = (name[0, 2] == COLON_COLON ? name[2..] : name)\n\n        result = send(type, fqname)\n        unless result\n          if @notfound[fqname] && Puppet[:ignoremissingtypes]\n            # do not try to autoload if we already tried and it wasn't conclusive\n            # as this is a time consuming operation. Warn the user.\n            # Check first if debugging is on since the call to debug_once is expensive\n            if Puppet[:debug]\n              debug_once _(\"Not attempting to load %{type} %{fqname} as this object was missing during a prior compilation\") % { type: type, fqname: fqname }\n            end\n          else\n            fqname = munge_name(fqname)\n            result = loader.try_load_fqname(type, fqname)\n            @notfound[fqname] = result.nil?\n          end\n        end\n        result\n      end\n    end\n  end\n\n  def munge_name(name)\n    name.to_s.downcase\n  end\n\n  def dupe_check(instance, hash)\n    dupe = hash[instance.name]\n    return unless dupe\n\n    message = yield dupe\n    instance.fail Puppet::ParseError, message\n  end\n\n  def dupe_check_singleton(instance, set)\n    return if set.empty?\n\n    message = yield set[0]\n    instance.fail Puppet::ParseError, message\n  end\nend\n"
  },
  {
    "path": "lib/puppet/resource.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/util/tagging'\nrequire_relative '../puppet/parameter'\n\n# The simplest resource class.  Eventually it will function as the\n# base class for all resource-like behaviour.\n#\n# @api public\nclass Puppet::Resource\n  include Puppet::Util::Tagging\n  include Puppet::Util::PsychSupport\n\n  include Enumerable\n  attr_accessor :file, :line, :catalog, :exported, :virtual, :strict, :kind\n  attr_reader :type, :title, :parameters\n\n  # @!attribute [rw] sensitive_parameters\n  #   @api private\n  #   @return [Array<Symbol>] A list of parameters to be treated as sensitive\n  attr_accessor :sensitive_parameters\n\n  # @deprecated\n  attr_accessor :validate_parameters\n\n  require_relative '../puppet/indirector'\n  extend Puppet::Indirector\n  indirects :resource, :terminus_class => :ral\n\n  EMPTY_ARRAY = [].freeze\n  EMPTY_HASH = {}.freeze\n\n  ATTRIBUTES = [:file, :line, :exported, :kind].freeze\n  TYPE_CLASS = 'Class'\n  TYPE_NODE  = 'Node'\n\n  CLASS_STRING = 'class'\n  DEFINED_TYPE_STRING = 'defined_type'\n  COMPILABLE_TYPE_STRING = 'compilable_type'\n  UNKNOWN_TYPE_STRING = 'unknown'\n\n  PCORE_TYPE_KEY = '__ptype'\n  VALUE_KEY = 'value'\n\n  def self.from_data_hash(data)\n    resource = allocate\n    resource.initialize_from_hash(data)\n    resource\n  end\n\n  def initialize_from_hash(data)\n    type = data['type']\n    raise ArgumentError, _('No resource type provided in serialized data') unless type\n\n    title = data['title']\n    raise ArgumentError, _('No resource title provided in serialized data') unless title\n\n    @type, @title = self.class.type_and_title(type, title)\n\n    params = data['parameters']\n    if params\n      params = Puppet::Pops::Serialization::FromDataConverter.convert(params)\n      @parameters = {}\n      params.each { |param, value| self[param] = value }\n    else\n      @parameters = EMPTY_HASH\n    end\n\n    sensitives = data['sensitive_parameters']\n    if sensitives\n      @sensitive_parameters = sensitives.map(&:to_sym)\n    else\n      @sensitive_parameters = EMPTY_ARRAY\n    end\n\n    tags = data['tags']\n    if tags\n      tag(*tags)\n    end\n\n    ATTRIBUTES.each do |a|\n      value = data[a.to_s]\n      send(\"#{a}=\", value) unless value.nil?\n    end\n  end\n\n  def inspect\n    \"#{@type}[#{@title}]#{to_hash.inspect}\"\n  end\n\n  # Produces a Data compliant hash of the resource.\n  # The result depends on the --rich_data setting, and the context value\n  # for Puppet.lookup(:stringify_rich), that if it is `true` will use the\n  # ToStringifiedConverter to produce the value per parameter.\n  # (Note that the ToStringifiedConverter output is lossy and should not\n  # be used when producing a catalog serialization).\n  #\n  def to_data_hash\n    data = {\n      'type' => type,\n      'title' => title.to_s,\n      'tags' => tags.to_data_hash\n    }\n    ATTRIBUTES.each do |param|\n      value = send(param)\n      data[param.to_s] = value unless value.nil?\n    end\n\n    data['exported'] ||= false\n\n    # To get stringified parameter values the flag :stringify_rich can be set\n    # in the puppet context.\n    #\n    stringify = Puppet.lookup(:stringify_rich)\n    converter = stringify ? Puppet::Pops::Serialization::ToStringifiedConverter.new : nil\n\n    params = {}\n    to_hash.each_pair do |param, value|\n      # Don't duplicate the title as the namevar\n      unless param == namevar && value == title\n        if stringify\n          params[param.to_s] = converter.convert(value)\n        else\n          params[param.to_s] = Puppet::Resource.value_to_json_data(value)\n        end\n      end\n    end\n\n    unless params.empty?\n      data['parameters'] =\n        Puppet::Pops::Serialization::ToDataConverter\n        .convert(params,\n                 {\n                   :rich_data => Puppet.lookup(:rich_data),\n                   :symbol_as_string => true,\n                   :local_reference => false,\n                   :type_by_reference => true,\n                   :message_prefix => ref,\n                   :semantic => self\n                 })\n    end\n\n    data['sensitive_parameters'] = sensitive_parameters.map(&:to_s) unless sensitive_parameters.empty?\n    data\n  end\n\n  def self.value_to_json_data(value)\n    if value.is_a?(Array)\n      value.map { |v| value_to_json_data(v) }\n    elsif value.is_a?(Hash)\n      result = {}\n      value.each_pair { |k, v| result[value_to_json_data(k)] = value_to_json_data(v) }\n      result\n    elsif value.is_a?(Puppet::Resource)\n      value.to_s\n    elsif value.is_a?(Symbol) && value == :undef\n      nil\n    else\n      value\n    end\n  end\n\n  def yaml_property_munge(x)\n    value.to_json_data(x)\n  end\n\n  # Proxy these methods to the parameters hash.  It's likely they'll\n  # be overridden at some point, but this works for now.\n  %w[has_key? keys length delete empty? <<].each do |method|\n    define_method(method) do |*args|\n      parameters.send(method, *args)\n    end\n  end\n\n  # Set a given parameter.  Converts all passed names\n  # to lower-case symbols.\n  def []=(param, value)\n    validate_parameter(param) if validate_parameters\n    parameters[parameter_name(param)] = value\n  end\n\n  # Return a given parameter's value.  Converts all passed names\n  # to lower-case symbols.\n  def [](param)\n    parameters[parameter_name(param)]\n  end\n\n  def ==(other)\n    return false unless other.respond_to?(:title) and type == other.type and title == other.title\n\n    return false unless to_hash == other.to_hash\n\n    true\n  end\n\n  # Compatibility method.\n  def builtin?\n    # TODO: should be deprecated (was only used in one place in puppet codebase)\n    builtin_type?\n  end\n\n  # Is this a builtin resource type?\n  def builtin_type?\n    # Note - old implementation only checked if the resource_type was a Class\n    resource_type.is_a?(Puppet::CompilableResourceType)\n  end\n\n  def self.to_kind(resource_type)\n    if resource_type == CLASS_STRING\n      CLASS_STRING\n    elsif resource_type.is_a?(Puppet::Resource::Type) && resource_type.type == :definition\n      DEFINED_TYPE_STRING\n    elsif resource_type.is_a?(Puppet::CompilableResourceType)\n      COMPILABLE_TYPE_STRING\n    else\n      UNKNOWN_TYPE_STRING\n    end\n  end\n\n  # Iterate over each param/value pair, as required for Enumerable.\n  def each\n    parameters.each { |p, v| yield p, v }\n  end\n\n  def include?(parameter)\n    super || parameters.keys.include?(parameter_name(parameter))\n  end\n\n  %w[exported virtual strict].each do |m|\n    define_method(m + \"?\") do\n      send(m)\n    end\n  end\n\n  def class?\n    @is_class ||= @type == TYPE_CLASS\n  end\n\n  def stage?\n    @is_stage ||= @type.to_s.casecmp(\"stage\").zero?\n  end\n\n  # Construct a resource from data.\n  #\n  # Constructs a resource instance with the given `type` and `title`. Multiple\n  # type signatures are possible for these arguments and most will result in an\n  # expensive call to {Puppet::Node::Environment#known_resource_types} in order\n  # to resolve `String` and `Symbol` Types to actual Ruby classes.\n  #\n  # @param type [Symbol, String] The name of the Puppet Type, as a string or\n  #   symbol. The actual Type will be looked up using\n  #   {Puppet::Node::Environment#known_resource_types}. This lookup is expensive.\n  # @param type [String] The full resource name in the form of\n  #   `\"Type[Title]\"`. This method of calling should only be used when\n  #   `title` is `nil`.\n  # @param type [nil] If a `nil` is passed, the title argument must be a string\n  #   of the form `\"Type[Title]\"`.\n  # @param type [Class] A class that inherits from `Puppet::Type`. This method\n  #   of construction is much more efficient as it skips calls to\n  #   {Puppet::Node::Environment#known_resource_types}.\n  #\n  # @param title [String, :main, nil] The title of the resource. If type is `nil`, may also\n  #   be the full resource name in the form of `\"Type[Title]\"`.\n  #\n  # @api public\n  def initialize(type, title = nil, attributes = EMPTY_HASH)\n    @parameters = {}\n    @sensitive_parameters = []\n    if type.is_a?(Puppet::Resource)\n      # Copy constructor. Let's avoid munging, extracting, tagging, etc\n      src = type\n      self.file = src.file\n      self.line = src.line\n      self.kind = src.kind\n      self.exported = src.exported\n      self.virtual = src.virtual\n      set_tags(src)\n      self.environment = src.environment\n      @rstype = src.resource_type\n      @type = src.type\n      @title = src.title\n\n      src.to_hash.each do |p, v|\n        case v\n        when Puppet::Resource\n          v = v.copy_as_resource\n        when Array\n          # flatten resource references arrays\n          v = v.flatten if v.flatten.find { |av| av.is_a?(Puppet::Resource) }\n          v = v.collect do |av|\n            av = av.copy_as_resource if av.is_a?(Puppet::Resource)\n            av\n          end\n        end\n\n        self[p] = v\n      end\n      @sensitive_parameters.replace(type.sensitive_parameters)\n    else\n      if type.is_a?(Hash)\n        # TRANSLATORS 'Puppet::Resource.new' should not be translated\n        raise ArgumentError, _(\"Puppet::Resource.new does not take a hash as the first argument.\") + ' ' +\n                             _(\"Did you mean (%{type}, %{title}) ?\") %\n                             { type: (type[:type] || type[\"type\"]).inspect, title: (type[:title] || type[\"title\"]).inspect }\n      end\n\n      # In order to avoid an expensive search of 'known_resource_types\" and\n      # to obey/preserve the implementation of the resource's type - if the\n      # given type is a resource type implementation (one of):\n      #   * a \"classic\" 3.x ruby plugin\n      #   * a compatible implementation (e.g. loading from pcore metadata)\n      #   * a resolved user defined type\n      #\n      # ...then, modify the parameters to the \"old\" (agent side compatible) way\n      # of describing the type/title with string/symbols.\n      #\n      # TODO: Further optimizations should be possible as the \"type juggling\" is\n      # not needed when the type implementation is known.\n      #\n      if type.is_a?(Puppet::CompilableResourceType) || type.is_a?(Puppet::Resource::Type)\n        # set the resource type implementation\n        self.resource_type = type\n        # set the type name to the symbolic name\n        type = type.name\n      end\n      @exported = false\n\n      # Set things like environment, strictness first.\n      attributes.each do |attr, value|\n        next if attr == :parameters\n\n        send(attr.to_s + \"=\", value)\n      end\n\n      if environment.is_a?(Puppet::Node::Environment) && environment != Puppet::Node::Environment::NONE\n        self.file = environment.externalize_path(attributes[:file])\n      end\n\n      @type, @title = self.class.type_and_title(type, title)\n\n      rt = resource_type\n\n      self.kind = self.class.to_kind(rt) unless kind\n      if strict? && rt.nil?\n        if class?\n          raise ArgumentError, _(\"Could not find declared class %{title}\") % { title: title }\n        else\n          raise ArgumentError, _(\"Invalid resource type %{type}\") % { type: type }\n        end\n      end\n\n      params = attributes[:parameters]\n      unless params.nil? || params.empty?\n        extract_parameters(params)\n        if rt && rt.respond_to?(:deprecate_params)\n          rt.deprecate_params(title, params)\n        end\n      end\n\n      tag(self.type)\n      tag_if_valid(self.title)\n    end\n  end\n\n  def ref\n    to_s\n  end\n\n  # Find our resource.\n  def resolve\n    catalog ? catalog.resource(to_s) : nil\n  end\n\n  # The resource's type implementation\n  # @return [Puppet::Type, Puppet::Resource::Type]\n  # @api private\n  def resource_type\n    @rstype ||= self.class.resource_type(type, title, environment)\n  end\n\n  # The resource's type implementation\n  # @return [Puppet::Type, Puppet::Resource::Type]\n  # @api private\n  def self.resource_type(type, title, environment)\n    case type\n    when TYPE_CLASS; environment.known_resource_types.hostclass(title == :main ? \"\" : title)\n    when TYPE_NODE; environment.known_resource_types.node(title)\n    else\n      result = Puppet::Type.type(type)\n      unless result\n        krt = environment.known_resource_types\n        result = krt.definition(type)\n      end\n      result\n    end\n  end\n\n  # Set the resource's type implementation\n  # @param type [Puppet::Type, Puppet::Resource::Type]\n  # @api private\n  def resource_type=(type)\n    @rstype = type\n  end\n\n  def environment\n    @environment ||= if catalog\n                       catalog.environment_instance\n                     else\n                       Puppet.lookup(:current_environment) { Puppet::Node::Environment::NONE }\n                     end\n  end\n\n  def environment=(environment)\n    @environment = environment\n  end\n\n  # Produces a hash of attribute to value mappings where the title parsed into its components\n  # acts as the default values overridden by any parameter values explicitly given as parameters.\n  #\n  def to_hash\n    parse_title.merge parameters\n  end\n\n  def to_s\n    \"#{type}[#{title}]\"\n  end\n\n  def uniqueness_key\n    # Temporary kludge to deal with inconsistent use patterns; ensure we don't return nil for namevar/:name\n    h = to_hash\n    name = h[namevar] || h[:name] || self.name\n    h[namevar] ||= name\n    h[:name]   ||= name\n    h.values_at(*key_attributes.sort_by(&:to_s))\n  end\n\n  def key_attributes\n    resource_type.respond_to?(:key_attributes) ? resource_type.key_attributes : [:name]\n  end\n\n  # Convert our resource to yaml for Hiera purposes.\n  #\n  # @deprecated Use {to_hiera_hash} instead.\n  def to_hierayaml\n    # Collect list of attributes to align => and move ensure first\n    attr = parameters.keys\n    attr_max = attr.inject(0) { |max, k| k.to_s.length > max ? k.to_s.length : max }\n\n    attr.sort!\n    if attr.first != :ensure && attr.include?(:ensure)\n      attr.delete(:ensure)\n      attr.unshift(:ensure)\n    end\n\n    # rubocop:disable Lint/FormatParameterMismatch\n    attributes = attr.collect { |k|\n      v = parameters[k]\n      \"    %-#{attr_max}s: %s\\n\" % [k, Puppet::Parameter.format_value_for_display(v)]\n    }.join\n    # rubocop:enable Lint/FormatParameterMismatch\n\n    \"  %s:\\n%s\" % [title, attributes]\n  end\n\n  # Convert our resource to a hiera hash suitable for serialization.\n  def to_hiera_hash\n    # to_data_hash converts to safe Data types, e.g. no symbols, unicode replacement character\n    h = to_data_hash\n\n    params = h['parameters'] || {}\n    value = params.delete('ensure')\n\n    res = {}\n    res['ensure'] = value if value\n    res.merge!(params.sort.to_h)\n\n    { h['title'] => res }\n  end\n\n  # Convert our resource to Puppet code.\n  def to_manifest\n    # Collect list of attributes to align => and move ensure first\n    attr = parameters.keys\n    attr_max = attr.inject(0) { |max, k| k.to_s.length > max ? k.to_s.length : max }\n\n    attr.sort!\n    if attr.first != :ensure && attr.include?(:ensure)\n      attr.delete(:ensure)\n      attr.unshift(:ensure)\n    end\n\n    # rubocop:disable Lint/FormatParameterMismatch\n    attributes = attr.collect { |k|\n      v = parameters[k]\n      \"  %-#{attr_max}s => %s,\\n\" % [k, Puppet::Parameter.format_value_for_display(v)]\n    }.join\n    # rubocop:enable Lint/FormatParameterMismatch\n\n    escaped = title.gsub(/'/, \"\\\\\\\\'\")\n    \"%s { '%s':\\n%s}\" % [type.to_s.downcase, escaped, attributes]\n  end\n\n  def to_ref\n    ref\n  end\n\n  # Convert our resource to a RAL resource instance. Creates component\n  # instances for resource types that are not of a compilable_type kind. In case\n  # the resource doesn’t exist and it’s compilable_type kind, raise an error.\n  # There are certain cases where a resource won't be in a catalog, such as\n  # when we create a resource directly by using Puppet::Resource.new(...), so we\n  # must check its kind before deciding whether the catalog format is of an older\n  # version or not.\n  def to_ral\n    if kind == COMPILABLE_TYPE_STRING\n      typeklass = Puppet::Type.type(type)\n    elsif catalog && catalog.catalog_format >= 2\n      typeklass = Puppet::Type.type(:component)\n    else\n      typeklass = Puppet::Type.type(type) || Puppet::Type.type(:component)\n    end\n\n    raise(Puppet::Error, \"Resource type '#{type}' was not found\") unless typeklass\n\n    typeklass.new(self)\n  end\n\n  def name\n    # this is potential namespace conflict\n    # between the notion of an \"indirector name\"\n    # and a \"resource name\"\n    [type, title].join('/')\n  end\n\n  def missing_arguments\n    resource_type.arguments.select do |param, _default|\n      the_param = parameters[param.to_sym]\n      the_param.nil? || the_param.value.nil? || the_param.value == :undef\n    end\n  end\n  private :missing_arguments\n\n  def copy_as_resource\n    Puppet::Resource.new(self)\n  end\n\n  def valid_parameter?(name)\n    resource_type.valid_parameter?(name)\n  end\n\n  def validate_parameter(name)\n    raise Puppet::ParseError.new(_(\"no parameter named '%{name}'\") % { name: name }, file, line) unless valid_parameter?(name)\n  end\n\n  # This method, together with #file and #line, makes it possible for a Resource to be a 'source_pos' in a reported issue.\n  # @return [Integer] Instances of this class will always return `nil`.\n  def pos\n    nil\n  end\n\n  def prune_parameters(options = EMPTY_HASH)\n    properties = resource_type.properties.map(&:name)\n\n    dup.collect do |attribute, value|\n      if value.to_s.empty? or Array(value).empty?\n        delete(attribute)\n      elsif value.to_s == \"absent\" and attribute.to_s != \"ensure\"\n        delete(attribute)\n      end\n\n      parameters_to_include = resource_type.parameters_to_include\n      parameters_to_include += options[:parameters_to_include] || []\n\n      delete(attribute) unless properties.include?(attribute) || parameters_to_include.include?(attribute)\n    end\n    self\n  end\n\n  # @api private\n  def self.type_and_title(type, title)\n    type, title = extract_type_and_title(type, title)\n    type = munge_type_name(type)\n    if type == TYPE_CLASS\n      title = title == '' ? :main : munge_type_name(title)\n    end\n    [type, title]\n  end\n\n  def self.extract_type_and_title(argtype, argtitle)\n    if (argtype.nil? || argtype == :component || argtype == :whit) &&\n       argtitle =~ /^([^\\[\\]]+)\\[(.+)\\]$/m                  then [::Regexp.last_match(1), ::Regexp.last_match(2)]\n    elsif argtitle.nil? && argtype.is_a?(String) &&\n          argtype =~ /^([^\\[\\]]+)\\[(.+)\\]$/m                   then [::Regexp.last_match(1), ::Regexp.last_match(2)]\n    elsif argtitle                                             then [argtype,            argtitle]\n    elsif argtype.is_a?(Puppet::Type)                          then [argtype.class.name, argtype.title]\n    else  raise ArgumentError, _(\"No title provided and %{type} is not a valid resource reference\") % { type: argtype.inspect } # rubocop:disable Lint/ElseLayout\n    end\n  end\n  private_class_method :extract_type_and_title\n\n  def self.munge_type_name(value)\n    return :main if value == :main\n    return TYPE_CLASS if value == '' || value.nil? || value.to_s.casecmp('component') == 0\n\n    Puppet::Pops::Types::TypeFormatter.singleton.capitalize_segments(value.to_s)\n  end\n  private_class_method :munge_type_name\n\n  private\n\n  # Produce a canonical method name.\n  def parameter_name(param)\n    param = param.to_s.downcase.to_sym\n    if param == :name and namevar\n      param = namevar\n    end\n    param\n  end\n\n  # The namevar for our resource type. If the type doesn't exist,\n  # always use :name.\n  def namevar\n    if builtin_type? && !(t = resource_type).nil? && t.key_attributes.length == 1\n      t.key_attributes.first\n    else\n      :name\n    end\n  end\n\n  def extract_parameters(params)\n    params.each do |param, value|\n      validate_parameter(param) if strict?\n      self[param] = value\n    end\n  end\n\n  # Produces a hash with { :key => part_of_title } for each entry in title_patterns\n  # for the resource type. A typical result for a title of 'example' is {:name => 'example'}.\n  # A resource type with a complex title to attribute mapping returns one entry in the hash\n  # per part.\n  #\n  def parse_title\n    h = {}\n    type = resource_type\n    if type.respond_to?(:title_patterns) && !type.title_patterns.nil?\n      type.title_patterns.each do |regexp, symbols_and_lambdas|\n        captures = regexp.match(title.to_s)\n        next unless captures\n\n        symbols_and_lambdas.zip(captures[1..]).each do |symbol_and_lambda, capture|\n          symbol, proc = symbol_and_lambda\n          # Many types pass \"identity\" as the proc; we might as well give\n          # them a shortcut to delivering that without the extra cost.\n          #\n          # Especially because the global type defines title_patterns and\n          # uses the identity patterns.\n          #\n          # This was worth about 8MB of memory allocation saved in my\n          # testing, so is worth the complexity for the API.\n          if proc then\n            h[symbol] = proc.call(capture)\n          else\n            h[symbol] = capture\n          end\n        end\n        return h\n      end\n      # If we've gotten this far, then none of the provided title patterns\n      # matched. Since there's no way to determine the title then the\n      # resource should fail here.\n      raise Puppet::Error, _(\"No set of title patterns matched the title \\\"%{title}\\\".\") % { title: title }\n    else\n      { :name => title.to_s }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/runtime.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet/http'\nrequire_relative '../puppet/facter_impl'\nrequire 'singleton'\n\n# Provides access to runtime implementations.\n#\n# @api private\nclass Puppet::Runtime\n  include Singleton\n\n  def initialize\n    @runtime_services = {\n      http: proc do\n        klass = Puppet::Network::HttpPool.http_client_class\n        if klass == Puppet::Network::HTTP::Connection\n          Puppet::HTTP::Client.new\n        else\n          Puppet::HTTP::ExternalClient.new(klass)\n        end\n      end,\n      facter: proc { Puppet::FacterImpl.new }\n    }\n  end\n  private :initialize\n\n  # Loads all runtime implementations.\n  #\n  # @return Array[Symbol] the names of loaded implementations\n  # @api private\n  def load_services\n    @runtime_services.keys.each { |key| self[key] }\n  end\n\n  # Get a runtime implementation.\n  #\n  # @param name [Symbol] the name of the implementation\n  # @return [Object] the runtime implementation\n  # @api private\n  def [](name)\n    service = @runtime_services[name]\n    raise ArgumentError, \"Unknown service #{name}\" unless service\n\n    if service.is_a?(Proc)\n      @runtime_services[name] = service.call\n    else\n      service\n    end\n  end\n\n  # Register a runtime implementation.\n  #\n  # @param name [Symbol] the name of the implementation\n  # @param impl [Object] the runtime implementation\n  # @api private\n  def []=(name, impl)\n    @runtime_services[name] = impl\n  end\n\n  # Clears all implementations. This is used for testing.\n  #\n  # @api private\n  def clear\n    initialize\n  end\nend\n"
  },
  {
    "path": "lib/puppet/scheduler/job.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Scheduler\n  class Job\n    attr_reader :run_interval\n    attr_accessor :last_run\n    attr_accessor :start_time\n\n    def initialize(run_interval, &block)\n      self.run_interval = run_interval\n      @last_run = nil\n      @run_proc = block\n      @enabled = true\n    end\n\n    def run_interval=(interval)\n      @run_interval = [interval, 0].max\n    end\n\n    def ready?(time)\n      if @last_run\n        @last_run + @run_interval <= time\n      else\n        true\n      end\n    end\n\n    def enabled?\n      @enabled\n    end\n\n    def enable\n      @enabled = true\n    end\n\n    def disable\n      @enabled = false\n    end\n\n    def interval_to_next_from(time)\n      if ready?(time)\n        0\n      else\n        @run_interval - (time - @last_run)\n      end\n    end\n\n    def run(now)\n      @last_run = now\n      if @run_proc\n        @run_proc.call(self)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/scheduler/scheduler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Scheduler\n  class Scheduler\n    def initialize(timer = Puppet::Scheduler::Timer.new)\n      @timer = timer\n    end\n\n    def run_loop(jobs)\n      mark_start_times(jobs, @timer.now)\n      until enabled(jobs).empty?\n        @timer.wait_for(min_interval_to_next_run_from(jobs, @timer.now))\n        run_ready(jobs, @timer.now)\n      end\n    end\n\n    private\n\n    def enabled(jobs)\n      jobs.select(&:enabled?)\n    end\n\n    def mark_start_times(jobs, start_time)\n      jobs.each do |job|\n        job.start_time = start_time\n      end\n    end\n\n    def min_interval_to_next_run_from(jobs, from_time)\n      enabled(jobs).map do |j|\n        j.interval_to_next_from(from_time)\n      end.min\n    end\n\n    def run_ready(jobs, at_time)\n      enabled(jobs).each do |j|\n        # This check intentionally happens right before each run,\n        # instead of filtering on ready schedulers, since one may adjust\n        # the readiness of a later one\n        if j.ready?(at_time)\n          j.run(at_time)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/scheduler/splay_job.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Scheduler\n  class SplayJob < Job\n    attr_reader :splay, :splay_limit\n\n    def initialize(run_interval, splay_limit, &block)\n      @splay, @splay_limit = calculate_splay(splay_limit)\n      super(run_interval, &block)\n    end\n\n    def interval_to_next_from(time)\n      if last_run\n        super\n      else\n        (start_time + splay) - time\n      end\n    end\n\n    def ready?(time)\n      if last_run\n        super\n      else\n        start_time + splay <= time\n      end\n    end\n\n    # Recalculates splay.\n    #\n    # @param splay_limit [Integer] the maximum time (in seconds) to delay before an agent's first run.\n    # @return @splay [Integer] a random integer less than or equal to the splay limit that represents the seconds to\n    # delay before next agent run.\n    def splay_limit=(splay_limit)\n      if @splay_limit != splay_limit\n        @splay, @splay_limit = calculate_splay(splay_limit)\n      end\n    end\n\n    private\n\n    def calculate_splay(limit)\n      [rand(limit + 1), limit]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/scheduler/timer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Scheduler\n  class Timer\n    def wait_for(seconds)\n      if seconds > 0\n        sleep(seconds)\n      end\n    end\n\n    def now\n      Time.now\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/scheduler.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Scheduler\n  require_relative 'scheduler/job'\n  require_relative 'scheduler/splay_job'\n  require_relative 'scheduler/scheduler'\n  require_relative 'scheduler/timer'\n\n  module_function\n\n  def create_job(interval, splay = false, splay_limit = 0, &block)\n    if splay\n      Puppet::Scheduler::SplayJob.new(interval, splay_limit, &block)\n    else\n      Puppet::Scheduler::Job.new(interval, &block)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/alias_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::AliasSetting\n  attr_reader :name, :alias_name\n\n  def initialize(args = {})\n    @name = args[:name]\n    @alias_name = args[:alias_for]\n    @alias_for = Puppet.settings.setting(alias_name)\n  end\n\n  def optparse_args\n    args = @alias_for.optparse_args\n    args[0].gsub!(alias_name.to_s, name.to_s)\n    args\n  end\n\n  def getopt_args\n    args = @alias_for.getopt_args\n    args[0].gsub!(alias_name.to_s, name.to_s)\n    args\n  end\n\n  def type\n    :alias\n  end\n\n  def method_missing(method, *args)\n    alias_for.send(method, *args)\n  rescue => e\n    Puppet.log_exception(self.class, e.message)\n  end\n\n  private\n\n  attr_reader :alias_for\nend\n"
  },
  {
    "path": "lib/puppet/settings/array_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::ArraySetting < Puppet::Settings::BaseSetting\n  def type\n    :array\n  end\n\n  def munge(value)\n    case value\n    when String\n      value.split(/\\s*,\\s*/)\n    when Array\n      value\n    else\n      raise ArgumentError, _(\"Expected an Array or String, got a %{klass}\") % { klass: value.class }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/autosign_setting.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/settings/base_setting'\n\n# A specialization of the file setting to allow boolean values.\n#\n# The autosign value can be either a boolean or a file path, and if the setting\n# is a file path then it may have a owner/group/mode specified.\n#\n# @api private\nclass Puppet::Settings::AutosignSetting < Puppet::Settings::FileSetting\n  def munge(value)\n    if ['true', true].include? value\n      true\n    elsif ['false', false, nil].include? value\n      false\n    elsif Puppet::Util.absolute_path?(value)\n      value\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid autosign value %{value}: must be 'true'/'false' or an absolute path\") % { value: value }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/base_setting.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'set'\nrequire_relative '../../puppet/settings/errors'\n\n# The base setting type\nclass Puppet::Settings::BaseSetting\n  attr_writer :default\n  attr_accessor :name, :desc, :section\n  attr_reader :short, :deprecated, :call_hook\n\n  # Hooks are called during different parts of the settings lifecycle:\n  #\n  # * :on_write_only - This is the default hook type. The hook will be called\n  #   if its value is set in `main` or programmatically. If its value is set in\n  #   a section that doesn't match the application's run mode, it will be\n  #   ignored entirely. If the section does match the run mode, the value will\n  #   be used, but the hook will not be called!\n  #\n  # * :on_define_and_write - The hook behaves the same as above, except it is\n  #   also called immediately when the setting is defined in\n  #   {Puppet::Settings.define_settings}. In that case, the hook receives the\n  #   default value as specified.\n  #\n  # * :on_initialize_and_write - The hook will be called if the value is set in\n  #   `main`, the section that matches the run mode, or programmatically.\n  #\n  HOOK_TYPES = Set.new([:on_define_and_write, :on_initialize_and_write, :on_write_only]).freeze\n\n  def self.available_call_hook_values\n    HOOK_TYPES.to_a\n  end\n\n  # Registers a hook to be called later based on the type of hook specified in `value`.\n  #\n  # @param value [Symbol] One of {HOOK_TYPES}\n  def call_hook=(value)\n    if value.nil?\n      # TRANSLATORS ':%{name}', ':call_hook', and ':on_write_only' should not be translated\n      Puppet.warning _(\"Setting :%{name} :call_hook is nil, defaulting to :on_write_only\") % { name: name }\n      value = :on_write_only\n    end\n    unless HOOK_TYPES.include?(value)\n      # TRANSLATORS 'call_hook' is a Puppet option name and should not be translated\n      raise ArgumentError, _(\"Invalid option %{value} for call_hook\") % { value: value }\n    end\n\n    @call_hook = value\n  end\n\n  # @see {HOOK_TYPES}\n  def call_hook_on_define?\n    call_hook == :on_define_and_write\n  end\n\n  # @see {HOOK_TYPES}\n  def call_hook_on_initialize?\n    call_hook == :on_initialize_and_write\n  end\n\n  # get the arguments in getopt format\n  def getopt_args\n    if short\n      [[\"--#{name}\", \"-#{short}\", GetoptLong::REQUIRED_ARGUMENT]]\n    else\n      [[\"--#{name}\", GetoptLong::REQUIRED_ARGUMENT]]\n    end\n  end\n\n  # get the arguments in OptionParser format\n  def optparse_args\n    if short\n      [\"--#{name}\", \"-#{short}\", desc, :REQUIRED]\n    else\n      [\"--#{name}\", desc, :REQUIRED]\n    end\n  end\n\n  def hook=(block)\n    @has_hook = true\n    meta_def :handle, &block\n  end\n\n  def has_hook?\n    @has_hook\n  end\n\n  # Create the new element.  Pretty much just sets the name.\n  def initialize(args = {})\n    @settings = args.delete(:settings)\n    unless @settings\n      raise ArgumentError, \"You must refer to a settings object\"\n    end\n\n    # explicitly set name prior to calling other param= methods to provide meaningful feedback during\n    # other warnings\n    @name = args[:name] if args.include? :name\n\n    # set the default value for call_hook\n    @call_hook = :on_write_only if args[:hook] and !(args[:call_hook])\n    @has_hook = false\n\n    if args[:call_hook] and !(args[:hook])\n      # TRANSLATORS ':call_hook' and ':hook' are specific setting names and should not be translated\n      raise ArgumentError, _(\"Cannot reference :call_hook for :%{name} if no :hook is defined\") % { name: @name }\n    end\n\n    args.each do |param, value|\n      method = param.to_s + \"=\"\n      unless respond_to? method\n        raise ArgumentError, _(\"%{class_name} (setting '%{setting}') does not accept %{parameter}\") %\n                             { class_name: self.class, setting: args[:name], parameter: param }\n      end\n\n      send(method, value)\n    end\n    unless desc\n      raise ArgumentError, _(\"You must provide a description for the %{class_name} config option\") % { class_name: name }\n    end\n  end\n\n  def iscreated\n    @iscreated = true\n  end\n\n  def iscreated?\n    @iscreated\n  end\n\n  # short name for the element\n  def short=(value)\n    raise ArgumentError, _(\"Short names can only be one character.\") if value.to_s.length != 1\n\n    @short = value.to_s\n  end\n\n  def default(check_application_defaults_first = false)\n    if @default.is_a? Proc\n      # Give unit tests a chance to reevaluate the call by removing the instance variable\n      unless instance_variable_defined?(:@evaluated_default)\n        @evaluated_default = @default.call\n      end\n      default_value = @evaluated_default\n    else\n      default_value = @default\n    end\n    return default_value unless check_application_defaults_first\n\n    @settings.value(name, :application_defaults, true) || default_value\n  end\n\n  # Convert the object to a config statement.\n  def to_config\n    require_relative '../../puppet/util/docs'\n    # Scrub any funky indentation; comment out description.\n    str = Puppet::Util::Docs.scrub(@desc).gsub(/^/, \"# \") + \"\\n\"\n\n    # Add in a statement about the default.\n    str << \"# The default value is '#{default(true)}'.\\n\" if default(true)\n\n    # If the value has not been overridden, then print it out commented\n    # and unconverted, so it's clear that that's the default and how it\n    # works.\n    value = @settings.value(name)\n\n    if value != @default\n      line = \"#{@name} = #{value}\"\n    else\n      line = \"# #{@name} = #{@default}\"\n    end\n\n    str << (line + \"\\n\")\n\n    # Indent\n    str.gsub(/^/, \"    \")\n  end\n\n  # @param bypass_interpolation [Boolean] Set this true to skip the\n  #   interpolation step, returning the raw setting value.  Defaults to false.\n  # @return [String] Retrieves the value, or if it's not set, retrieves the default.\n  # @api public\n  def value(bypass_interpolation = false)\n    @settings.value(name, nil, bypass_interpolation)\n  end\n\n  # Modify the value when it is first evaluated\n  def munge(value)\n    value\n  end\n\n  # Print the value for the user in a config compatible format\n  def print(value)\n    munge(value)\n  end\n\n  def set_meta(meta)\n    Puppet.notice(\"#{name} does not support meta data. Ignoring.\")\n  end\n\n  def deprecated=(deprecation)\n    unless [:completely, :allowed_on_commandline].include?(deprecation)\n      # TRANSLATORS 'deprecated' is a Puppet setting and ':completely' and ':allowed_on_commandline' are possible values and should not be translated\n      raise ArgumentError, _(\"Unsupported deprecated value '%{deprecation}'.\") % { deprecation: deprecation } +\n                           ' ' + _(\"Supported values for deprecated are ':completely' or ':allowed_on_commandline'\")\n    end\n    @deprecated = deprecation\n  end\n\n  def deprecated?\n    !!@deprecated\n  end\n\n  # True if we should raise a deprecation_warning if the setting is submitted\n  # on the commandline or is set in puppet.conf.\n  def completely_deprecated?\n    @deprecated == :completely\n  end\n\n  # True if we should raise a deprecation_warning if the setting is found in\n  # puppet.conf, but not if the user sets it on the commandline\n  def allowed_on_commandline?\n    @deprecated == :allowed_on_commandline\n  end\n\n  def inspect\n    %Q(<#{self.class}:#{object_id} @name=\"#{@name}\" @section=\"#{@section}\" @default=\"#{@default}\" @call_hook=\"#{@call_hook}\">)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/boolean_setting.rb",
    "content": "# frozen_string_literal: true\n\n# A simple boolean.\nclass Puppet::Settings::BooleanSetting < Puppet::Settings::BaseSetting\n  # get the arguments in getopt format\n  def getopt_args\n    if short\n      [[\"--#{name}\", \"-#{short}\", GetoptLong::NO_ARGUMENT], [\"--no-#{name}\", GetoptLong::NO_ARGUMENT]]\n    else\n      [[\"--#{name}\", GetoptLong::NO_ARGUMENT], [\"--no-#{name}\", GetoptLong::NO_ARGUMENT]]\n    end\n  end\n\n  def optparse_args\n    if short\n      [\"--[no-]#{name}\", \"-#{short}\", desc, :NONE]\n    else\n      [\"--[no-]#{name}\", desc, :NONE]\n    end\n  end\n\n  def munge(value)\n    case value\n    when true, \"true\"; true\n    when false, \"false\"; false\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid value '%{value}' for boolean parameter: %{name}\") % { value: value.inspect, name: @name }\n    end\n  end\n\n  def type\n    :boolean\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/certificate_revocation_setting.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/settings/base_setting'\n\nclass Puppet::Settings::CertificateRevocationSetting < Puppet::Settings::BaseSetting\n  def type\n    :certificate_revocation\n  end\n\n  def munge(value)\n    case value\n    when 'chain', 'true', TrueClass\n      :chain\n    when 'leaf'\n      :leaf\n    when 'false', FalseClass, NilClass\n      false\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid certificate revocation value %{value}: must be one of 'true', 'chain', 'leaf', or 'false'\") % { value: value }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/config_file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/settings/ini_file'\n\n##\n# @api private\n#\n# Parses puppet configuration files\n#\nclass Puppet::Settings::ConfigFile\n  ##\n  # @param value_converter [Proc] a function that will convert strings into ruby types\n  def initialize(value_converter)\n    @value_converter = value_converter\n  end\n\n  # @param file [String, File] pointer to the file whose text we are parsing\n  # @param text [String] the actual text of the inifile to be parsed\n  # @param allowed_section_names [Array] an optional array of accepted section\n  #   names; if this list is non-empty, sections outside of it will raise an\n  #   error.\n  # @return A Struct with a +sections+ array representing each configuration section\n  def parse_file(file, text, allowed_section_names = [])\n    result = Conf.new\n    unless allowed_section_names.empty?\n      allowed_section_names << 'main' unless allowed_section_names.include?('main')\n    end\n\n    ini = Puppet::Settings::IniFile.parse(text.encode(Encoding::UTF_8))\n    unique_sections_in(ini, file, allowed_section_names).each do |section_name|\n      section = Section.new(section_name.to_sym)\n      result.with_section(section)\n\n      ini.lines_in(section_name).each do |line|\n        if line.is_a?(Puppet::Settings::IniFile::SettingLine)\n          parse_setting(line, section)\n        elsif line.text !~ /^\\s*#|^\\s*$/\n          raise Puppet::Settings::ParseError.new(_(\"Could not match line %{text}\") % { text: line.text }, file, line.line_number)\n        end\n      end\n    end\n\n    result\n  end\n\n  Conf = Struct.new(:sections) do\n    def initialize\n      super({})\n    end\n\n    def with_section(section)\n      sections[section.name] = section\n      self\n    end\n  end\n\n  Section = Struct.new(:name, :settings) do\n    def initialize(name)\n      super(name, [])\n    end\n\n    def with_setting(name, value, meta)\n      settings << Setting.new(name, value, meta)\n      self\n    end\n\n    def setting(name)\n      settings.find { |setting| setting.name == name }\n    end\n  end\n\n  Setting = Struct.new(:name, :value, :meta) do\n    def has_metadata?\n      meta != NO_META\n    end\n  end\n\n  Meta = Struct.new(:owner, :group, :mode)\n  NO_META = Meta.new(nil, nil, nil)\n\n  private\n\n  def unique_sections_in(ini, file, allowed_section_names)\n    ini.section_lines.collect do |section|\n      if !allowed_section_names.empty? && !allowed_section_names.include?(section.name)\n        error_location_str = Puppet::Util::Errors.error_location(file, section.line_number)\n        message = _(\"Illegal section '%{name}' in config file at %{error_location}.\") %\n                  { name: section.name, error_location: error_location_str }\n        # TRANSLATORS 'puppet.conf' is the name of the puppet configuration file and should not be translated.\n        message += ' ' + _(\"The only valid puppet.conf sections are: [%{allowed_sections_list}].\") %\n                         { allowed_sections_list: allowed_section_names.join(\", \") }\n        message += ' ' + _(\"Please use the directory environments feature to specify environments.\")\n        message += ' ' + _(\"(See https://puppet.com/docs/puppet/latest/environments_about.html)\")\n        raise(Puppet::Error, message)\n      end\n      section.name\n    end.uniq\n  end\n\n  def parse_setting(setting, section)\n    var = setting.name.intern\n    value = @value_converter[setting.value]\n\n    # Check to see if this is a file argument and it has extra options\n    begin\n      options = extract_fileinfo(value) if value.is_a?(String)\n      if options\n        section.with_setting(var, options[:value], Meta.new(options[:owner],\n                                                            options[:group],\n                                                            options[:mode]))\n      else\n        section.with_setting(var, value, NO_META)\n      end\n    rescue Puppet::Error => detail\n      raise Puppet::Settings::ParseError.new(detail.message, file, setting.line_number, detail)\n    end\n  end\n\n  def empty_section\n    { :_meta => {} }\n  end\n\n  def extract_fileinfo(string)\n    result = {}\n    value = string.sub(/\\{\\s*([^}]+)\\s*\\}/) do\n      params = ::Regexp.last_match(1)\n      params.split(/\\s*,\\s*/).each do |str|\n        if str =~ /^\\s*(\\w+)\\s*=\\s*(\\w+)\\s*$/\n          param = ::Regexp.last_match(1).intern\n          value = ::Regexp.last_match(2)\n          result[param] = value\n          unless [:owner, :mode, :group].include?(param)\n            raise ArgumentError, _(\"Invalid file option '%{parameter}'\") % { parameter: param }\n          end\n\n          if param == :mode and value !~ /^\\d+$/\n            raise ArgumentError, _(\"File modes must be numbers\")\n          end\n        else\n          raise ArgumentError, _(\"Could not parse '%{string}'\") % { string: string }\n        end\n      end\n      ''\n    end\n    result[:value] = value.sub(/\\s*$/, '')\n    result\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/directory_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::DirectorySetting < Puppet::Settings::FileSetting\n  def type\n    :directory\n  end\n\n  # @api private\n  #\n  # @param option [String] Extra file operation mode information to use\n  #   (defaults to read-only mode 'r')\n  #   This is the standard mechanism Ruby uses in the IO class, and therefore\n  #   encoding may be explicitly like fmode : encoding or fmode : \"BOM|UTF-*\"\n  #   for example, a:ASCII or w+:UTF-8\n  def open_file(filename, option = 'r', &block)\n    controlled_access do |mode|\n      Puppet::FileSystem.open(filename, mode, option, &block)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/duration_setting.rb",
    "content": "# frozen_string_literal: true\n\n# A setting that represents a span of time, and evaluates to an integer\n# number of seconds after being parsed\nclass Puppet::Settings::DurationSetting < Puppet::Settings::BaseSetting\n  # How we convert from various units to seconds.\n  UNITMAP = {\n    # 365 days isn't technically a year, but is sufficient for most purposes\n    \"y\" => 365 * 24 * 60 * 60,\n    \"d\" => 24 * 60 * 60,\n    \"h\" => 60 * 60,\n    \"m\" => 60,\n    \"s\" => 1\n  }\n\n  # A regex describing valid formats with groups for capturing the value and units\n  FORMAT = /^(\\d+)(y|d|h|m|s)?$/\n\n  def type\n    :duration\n  end\n\n  # Convert the value to an integer, parsing numeric string with units if necessary.\n  def munge(value)\n    if value.is_a?(Integer) || value.nil?\n      value\n    elsif value.is_a?(String) and value =~ FORMAT\n      ::Regexp.last_match(1).to_i * UNITMAP[::Regexp.last_match(2) || 's']\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid duration format '%{value}' for parameter: %{name}\") % { value: value.inspect, name: @name }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/enum_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::EnumSetting < Puppet::Settings::BaseSetting\n  attr_accessor :values\n\n  def type\n    :enum\n  end\n\n  def munge(value)\n    if values.include?(value)\n      value\n    else\n      raise Puppet::Settings::ValidationError,\n            _(\"Invalid value '%{value}' for parameter %{name}. Allowed values are '%{allowed_values}'\") % { value: value, name: @name, allowed_values: values.join(\"', '\") }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/environment_conf.rb",
    "content": "# frozen_string_literal: true\n\n# Configuration settings for a single directory Environment.\n# @api private\nclass Puppet::Settings::EnvironmentConf\n  ENVIRONMENT_CONF_ONLY_SETTINGS = [:modulepath, :manifest, :config_version].freeze\n  VALID_SETTINGS = (ENVIRONMENT_CONF_ONLY_SETTINGS + [:environment_timeout, :environment_data_provider, :static_catalogs, :rich_data]).freeze\n\n  # Given a path to a directory environment, attempts to load and parse an\n  # environment.conf in ini format, and return an EnvironmentConf instance.\n  #\n  # An environment.conf is optional, so if the file itself is missing, or\n  # empty, an EnvironmentConf with default values will be returned.\n  #\n  # @note logs warnings if the environment.conf contains any ini sections,\n  # or has settings other than the three handled for directory environments\n  # (:manifest, :modulepath, :config_version)\n  #\n  # @param path_to_env [String] path to the directory environment\n  # @param global_module_path [Array<String>] the installation's base modulepath\n  #   setting, appended to default environment modulepaths\n  # @return [EnvironmentConf] the parsed EnvironmentConf object\n  def self.load_from(path_to_env, global_module_path)\n    path_to_env = File.expand_path(path_to_env)\n    conf_file = File.join(path_to_env, 'environment.conf')\n\n    begin\n      config = Puppet.settings.parse_file(conf_file)\n      validate(conf_file, config)\n      section = config.sections[:main]\n    rescue Errno::ENOENT\n      # environment.conf is an optional file\n      Puppet.debug { \"Path to #{path_to_env} does not exist, using default environment.conf\" }\n    end\n\n    new(path_to_env, section, global_module_path)\n  end\n\n  # Provides a configuration object tied directly to the passed environment.\n  # Configuration values are exactly those returned by the environment object,\n  # without interpolation.  This is a special case for the default configured\n  # environment returned by the Puppet::Environments::StaticPrivate loader.\n  def self.static_for(environment, environment_timeout = 0, static_catalogs = false, rich_data = false)\n    Static.new(environment, environment_timeout, static_catalogs, nil, rich_data)\n  end\n\n  attr_reader :section, :path_to_env, :global_modulepath\n\n  # Create through EnvironmentConf.load_from()\n  def initialize(path_to_env, section, global_module_path)\n    @path_to_env = path_to_env\n    @section = section\n    @global_module_path = global_module_path\n  end\n\n  def manifest\n    puppet_conf_manifest = Pathname.new(Puppet.settings.value(:default_manifest))\n    disable_per_environment_manifest = Puppet.settings.value(:disable_per_environment_manifest)\n\n    fallback_manifest_directory =\n      if puppet_conf_manifest.absolute?\n        puppet_conf_manifest.to_s\n      else\n        File.join(@path_to_env, puppet_conf_manifest.to_s)\n      end\n\n    if disable_per_environment_manifest\n      environment_conf_manifest = absolute(raw_setting(:manifest))\n      if environment_conf_manifest && fallback_manifest_directory != environment_conf_manifest\n        # TRANSLATORS 'disable_per_environment_manifest' is a setting and 'environment.conf' is a file name and should not be translated\n        message = _(\"The 'disable_per_environment_manifest' setting is true, but the environment located at %{path_to_env} has a manifest setting in its environment.conf of '%{environment_conf}' which does not match the default_manifest setting '%{puppet_conf}'.\") %\n                  { path_to_env: @path_to_env, environment_conf: environment_conf_manifest, puppet_conf: puppet_conf_manifest }\n        message += ' ' + _(\"If this environment is expecting to find modules in '%{environment_conf}', they will not be available!\") % { environment_conf: environment_conf_manifest }\n        Puppet.err(message)\n      end\n      fallback_manifest_directory.to_s\n    else\n      get_setting(:manifest, fallback_manifest_directory) do |manifest|\n        absolute(manifest)\n      end\n    end\n  end\n\n  def environment_timeout\n    # gen env specific config or use the default value\n    get_setting(:environment_timeout, Puppet.settings.value(:environment_timeout)) do |ttl|\n      # munges the string form statically without really needed the settings system, only\n      # its ability to munge \"4s, 3m, 5d, and 'unlimited' into seconds - if already munged into\n      # numeric form, the TTLSetting handles that.\n      Puppet::Settings::TTLSetting.munge(ttl, 'environment_timeout')\n    end\n  end\n\n  def environment_data_provider\n    get_setting(:environment_data_provider, Puppet.settings.value(:environment_data_provider)) do |value|\n      value\n    end\n  end\n\n  def modulepath\n    default_modulepath = [File.join(@path_to_env, \"modules\")] + @global_module_path\n    get_setting(:modulepath, default_modulepath) do |modulepath|\n      path = modulepath.is_a?(String) ?\n        modulepath.split(File::PATH_SEPARATOR) :\n        modulepath\n      path.map { |p| expand_glob(absolute(p)) }.flatten.join(File::PATH_SEPARATOR)\n    end\n  end\n\n  def rich_data\n    get_setting(:rich_data, Puppet.settings.value(:rich_data)) do |value|\n      value\n    end\n  end\n\n  def static_catalogs\n    get_setting(:static_catalogs, Puppet.settings.value(:static_catalogs)) do |value|\n      value\n    end\n  end\n\n  def config_version\n    get_setting(:config_version) do |config_version|\n      absolute(config_version)\n    end\n  end\n\n  def raw_setting(setting_name)\n    setting = section.setting(setting_name) if section\n    setting.value if setting\n  end\n\n  private\n\n  def self.validate(path_to_conf_file, config)\n    valid = true\n    section_keys = config.sections.keys\n    main = config.sections[:main]\n    if section_keys.size > 1\n      # warn once per config file path\n      Puppet.warn_once(\n        :invalid_settings_section, \"EnvironmentConf-section:#{path_to_conf_file}\",\n        _(\"Invalid sections in environment.conf at '%{path_to_conf_file}'. Environment conf may not have sections. The following sections are being ignored: '%{sections}'\") % {\n          path_to_conf_file: path_to_conf_file,\n          sections: (section_keys - [:main]).join(',')\n        }\n      )\n      valid = false\n    end\n\n    extraneous_settings = main.settings.map(&:name) - VALID_SETTINGS\n    unless extraneous_settings.empty?\n      # warn once per config file path\n      Puppet.warn_once(\n        :invalid_settings, \"EnvironmentConf-settings:#{path_to_conf_file}\",\n        _(\"Invalid settings in environment.conf at '%{path_to_conf_file}'. The following unknown setting(s) are being ignored: %{ignored_settings}\") % {\n          path_to_conf_file: path_to_conf_file,\n          ignored_settings: extraneous_settings.join(', ')\n        }\n      )\n      valid = false\n    end\n\n    valid\n  end\n  private_class_method :validate\n\n  def get_setting(setting_name, default = nil)\n    value = raw_setting(setting_name)\n    value = default if value.nil?\n    yield value\n  end\n\n  def expand_glob(path)\n    return nil if path.nil?\n\n    if path =~ /[*?\\[{]/\n      Dir.glob(path)\n    else\n      path\n    end\n  end\n\n  def absolute(path)\n    return nil if path.nil?\n\n    if path =~ /^\\$/\n      # Path begins with $something interpolatable\n      path\n    else\n      Puppet::FileSystem.expand_path(path, @path_to_env)\n    end\n  end\n\n  # Models configuration for an environment that is not loaded from a directory.\n  #\n  # @api private\n  class Static\n    attr_reader :environment_timeout\n    attr_reader :environment_data_provider\n    attr_reader :rich_data\n    attr_reader :static_catalogs\n\n    def initialize(environment, environment_timeout, static_catalogs, environment_data_provider = nil, rich_data = false)\n      @environment = environment\n      @environment_timeout = environment_timeout\n      @static_catalogs = static_catalogs\n      @environment_data_provider = environment_data_provider\n      @rich_data = rich_data\n    end\n\n    def path_to_env\n      nil\n    end\n\n    def manifest\n      @environment.manifest\n    end\n\n    def modulepath\n      @environment.modulepath.join(File::PATH_SEPARATOR)\n    end\n\n    def config_version\n      @environment.config_version\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/errors.rb",
    "content": "# frozen_string_literal: true\n\n# Exceptions for the settings module\nrequire_relative '../../puppet/error'\n\nclass Puppet::Settings\n  class SettingsError < Puppet::Error; end\n  class ValidationError < SettingsError; end\n  class InterpolationError < SettingsError; end\n\n  class ParseError < SettingsError\n    include Puppet::ExternalFileError\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/file_or_directory_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::FileOrDirectorySetting < Puppet::Settings::FileSetting\n  def type\n    if Puppet::FileSystem.directory?(value) || @path_ends_with_slash\n      :directory\n    else\n      :file\n    end\n  end\n\n  # Overrides munge to be able to read the un-munged value (the FileSetting.munch removes trailing slash)\n  #\n  def munge(value)\n    if value.is_a?(String) && value =~ %r{[\\\\/]$}\n      @path_ends_with_slash = true\n    end\n    super\n  end\n\n  # @api private\n  #\n  # @param option [String] Extra file operation mode information to use\n  #   (defaults to read-only mode 'r')\n  #   This is the standard mechanism Ruby uses in the IO class, and therefore\n  #   encoding may be explicitly like fmode : encoding or fmode : \"BOM|UTF-*\"\n  #   for example, a:ASCII or w+:UTF-8\n  def open_file(filename, option = 'r', &block)\n    if type == :file\n      super\n    else\n      controlled_access do |mode|\n        Puppet::FileSystem.open(filename, mode, option, &block)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/file_setting.rb",
    "content": "# frozen_string_literal: true\n\n# A file.\nclass Puppet::Settings::FileSetting < Puppet::Settings::StringSetting\n  class SettingError < StandardError; end\n\n  # An unspecified user or group\n  #\n  # @api private\n  class Unspecified\n    def value\n      nil\n    end\n  end\n\n  # A \"root\" user or group\n  #\n  # @api private\n  class Root\n    def value\n      \"root\"\n    end\n  end\n\n  # A \"service\" user or group that picks up values from settings when the\n  # referenced user or group is safe to use (it exists or will be created), and\n  # uses the given fallback value when not safe.\n  #\n  # @api private\n  class Service\n    # @param name [Symbol] the name of the setting to use as the service value\n    # @param fallback [String, nil] the value to use when the service value cannot be used\n    # @param settings [Puppet::Settings] the puppet settings object\n    # @param available_method [Symbol] the name of the method to call on\n    #   settings to determine if the value in settings is available on the system\n    #\n    def initialize(name, fallback, settings, available_method)\n      @settings = settings\n      @available_method = available_method\n      @name = name\n      @fallback = fallback\n    end\n\n    def value\n      if safe_to_use_settings_value?\n        @settings[@name]\n      else\n        @fallback\n      end\n    end\n\n    private\n\n    def safe_to_use_settings_value?\n      @settings[:mkusers] or @settings.send(@available_method)\n    end\n  end\n\n  attr_accessor :mode\n\n  def initialize(args)\n    @group = Unspecified.new\n    @owner = Unspecified.new\n    super(args)\n  end\n\n  # @param value [String] the group to use on the created file (can only be \"root\" or \"service\")\n  # @api public\n  def group=(value)\n    @group = case value\n             when \"root\"\n               Root.new\n             when \"service\"\n               # Group falls back to `nil` because we cannot assume that a \"root\" group exists.\n               # Some systems have root group, others have wheel, others have something else.\n               Service.new(:group, nil, @settings, :service_group_available?)\n             else\n               unknown_value(':group', value)\n             end\n  end\n\n  # @param value [String] the owner to use on the created file (can only be \"root\" or \"service\")\n  # @api public\n  def owner=(value)\n    @owner = case value\n             when \"root\"\n               Root.new\n             when \"service\"\n               Service.new(:user, \"root\", @settings, :service_user_available?)\n             else\n               unknown_value(':owner', value)\n             end\n  end\n\n  # @return [String, nil] the name of the group to use for the file or nil if the group should not be managed\n  # @api public\n  def group\n    @group.value\n  end\n\n  # @return [String, nil] the name of the user to use for the file or nil if the user should not be managed\n  # @api public\n  def owner\n    @owner.value\n  end\n\n  def set_meta(meta)\n    self.owner = meta.owner if meta.owner\n    self.group = meta.group if meta.group\n    self.mode = meta.mode if meta.mode\n  end\n\n  def munge(value)\n    if value.is_a?(String) and value != ':memory:' # for sqlite3 in-memory tests\n      value = File.expand_path(value)\n    end\n    value\n  end\n\n  def type\n    :file\n  end\n\n  # Turn our setting thing into a Puppet::Resource instance.\n  def to_resource\n    type = self.type\n    return nil unless type\n\n    path = value\n\n    return nil unless path.is_a?(String)\n\n    # Make sure the paths are fully qualified.\n    path = File.expand_path(path)\n\n    return nil unless type == :directory || Puppet::FileSystem.exist?(path)\n    return nil if path =~ %r{^/dev} || path =~ %r{^[A-Z]:/dev}i\n\n    resource = Puppet::Resource.new(:file, path)\n\n    if Puppet[:manage_internal_file_permissions]\n      if mode\n        # This ends up mimicking the munge method of the mode\n        # parameter to make sure that we're always passing the string\n        # version of the octal number.  If we were setting the\n        # 'should' value for mode rather than the 'is', then the munge\n        # method would be called for us automatically.  Normally, one\n        # wouldn't need to call the munge method manually, since\n        # 'should' gets set by the provider and it should be able to\n        # provide the data in the appropriate format.\n        mode = self.mode\n        mode = mode.to_i(8) if mode.is_a?(String)\n        mode = mode.to_s(8)\n        resource[:mode] = mode\n      end\n\n      # REMIND fails on Windows because chown/chgrp functionality not supported yet\n      if Puppet.features.root? and !Puppet::Util::Platform.windows?\n        resource[:owner] = owner if owner\n        resource[:group] = group if group\n      end\n    end\n\n    resource[:ensure] = type\n    resource[:loglevel] = :debug\n    resource[:links] = :follow\n    resource[:backup] = false\n\n    resource.tag(section, name, \"settings\")\n\n    resource\n  end\n\n  # @api private\n  # @param option [String] Extra file operation mode information to use\n  #   (defaults to read-only mode 'r')\n  #   This is the standard mechanism Ruby uses in the IO class, and therefore\n  #   encoding may be explicitly like fmode : encoding or fmode : \"BOM|UTF-*\"\n  #   for example, a:ASCII or w+:UTF-8\n  def exclusive_open(option = 'r', &block)\n    controlled_access do |mode|\n      Puppet::FileSystem.exclusive_open(file(), mode, option, &block)\n    end\n  end\n\n  # @api private\n  # @param option [String] Extra file operation mode information to use\n  #   (defaults to read-only mode 'r')\n  #   This is the standard mechanism Ruby uses in the IO class, and therefore\n  #   encoding may be explicitly like fmode : encoding or fmode : \"BOM|UTF-*\"\n  #   for example, a:ASCII or w+:UTF-8\n  def open(option = 'r', &block)\n    controlled_access do |mode|\n      Puppet::FileSystem.open(file, mode, option, &block)\n    end\n  end\n\n  private\n\n  def file\n    Puppet::FileSystem.pathname(value)\n  end\n\n  def unknown_value(parameter, value)\n    raise SettingError, _(\"The %{parameter} parameter for the setting '%{name}' must be either 'root' or 'service', not '%{value}'\") % { parameter: parameter, name: name, value: value }\n  end\n\n  def controlled_access(&block)\n    chown = nil\n    if Puppet.features.root?\n      chown = [owner, group]\n    else\n      chown = [nil, nil]\n    end\n\n    Puppet::Util::SUIDManager.asuser(*chown) do\n      # Update the umask to make non-executable files\n      Puppet::Util.withumask(File.umask ^ 0o111) do\n        yielded_value = case mode\n                        when String\n                          mode.to_i(8)\n                        when NilClass\n                          0o640\n                        else\n                          mode\n                        end\n\n        yield yielded_value\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/http_extra_headers_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::HttpExtraHeadersSetting < Puppet::Settings::BaseSetting\n  def type\n    :http_extra_headers\n  end\n\n  def munge(headers)\n    return headers if headers.is_a?(Hash)\n\n    headers = headers.split(/\\s*,\\s*/) if headers.is_a?(String)\n\n    raise ArgumentError, _(\"Expected an Array, String, or Hash, got a %{klass}\") % { klass: headers.class } unless headers.is_a?(Array)\n\n    headers.map! { |header|\n      case header\n      when String\n        header.split(':')\n      when Array\n        header\n      else\n        raise ArgumentError, _(\"Expected an Array or String, got a %{klass}\") % { klass: header.class }\n      end\n    }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/ini_file.rb",
    "content": "# frozen_string_literal: true\n\n# @api private\nclass Puppet::Settings::IniFile\n  DEFAULT_SECTION_NAME = \"main\"\n\n  def self.update(config_fh, &block)\n    config = parse(config_fh)\n    manipulator = Manipulator.new(config)\n    yield manipulator\n    config.write(config_fh)\n  end\n\n  def self.parse(config_fh)\n    config = new([DefaultSection.new])\n    config_fh.each_line do |line|\n      case line.chomp\n      when /^(\\s*)\\[([[:word:]]+)\\](\\s*)$/\n        config.append(SectionLine.new(::Regexp.last_match(1), ::Regexp.last_match(2), ::Regexp.last_match(3)))\n      when /^(\\s*)([[:word:]]+)(\\s*=\\s*)(.*?)(\\s*)$/\n        config.append(SettingLine.new(::Regexp.last_match(1), ::Regexp.last_match(2), ::Regexp.last_match(3), ::Regexp.last_match(4), ::Regexp.last_match(5)))\n      else\n        config.append(Line.new(line))\n      end\n    end\n\n    config\n  end\n\n  def initialize(lines = [])\n    @lines = lines\n  end\n\n  def append(line)\n    line.previous = @lines.last\n    @lines << line\n  end\n\n  def delete(section, name)\n    delete_offset = @lines.index(setting(section, name))\n    next_offset = delete_offset + 1\n    if next_offset < @lines.length\n      @lines[next_offset].previous = @lines[delete_offset].previous\n    end\n    @lines.delete_at(delete_offset)\n  end\n\n  def insert_after(line, new_line)\n    new_line.previous = line\n\n    insertion_point = @lines.index(line)\n    @lines.insert(insertion_point + 1, new_line)\n    if @lines.length > insertion_point + 2\n      @lines[insertion_point + 2].previous = new_line\n    end\n  end\n\n  def section_lines\n    @lines.select { |line| line.is_a?(SectionLine) }\n  end\n\n  def section_line(name)\n    section_lines.find { |section| section.name == name }\n  end\n\n  def setting(section, name)\n    settings_in(lines_in(section)).find do |line|\n      line.name == name\n    end\n  end\n\n  def lines_in(section_name)\n    section_lines = []\n    current_section_name = DEFAULT_SECTION_NAME\n    @lines.each do |line|\n      if line.is_a?(SectionLine)\n        current_section_name = line.name\n      elsif current_section_name == section_name\n        section_lines << line\n      end\n    end\n\n    section_lines\n  end\n\n  def settings_in(lines)\n    lines.select { |line| line.is_a?(SettingLine) }\n  end\n\n  def settings_exist_in_default_section?\n    lines_in(DEFAULT_SECTION_NAME).any? { |line| line.is_a?(SettingLine) }\n  end\n\n  def section_exists_with_default_section_name?\n    section_lines.any? do |section|\n      !section.is_a?(DefaultSection) && section.name == DEFAULT_SECTION_NAME\n    end\n  end\n\n  def set_default_section_write_sectionline(value)\n    index = @lines.find_index { |line| line.is_a?(DefaultSection) }\n    if index\n      @lines[index].write_sectionline = true\n    end\n  end\n\n  def write(fh)\n    # If no real section line for the default section exists, configure the\n    # DefaultSection object to write its section line. (DefaultSection objects\n    # don't write the section line unless explicitly configured to do so)\n    if settings_exist_in_default_section? && !section_exists_with_default_section_name?\n      set_default_section_write_sectionline(true)\n    end\n\n    fh.truncate(0)\n    fh.rewind\n    @lines.each do |line|\n      line.write(fh)\n    end\n    fh.flush\n  end\n\n  class Manipulator\n    def initialize(config)\n      @config = config\n    end\n\n    def set(section, name, value)\n      setting = @config.setting(section, name)\n      if setting\n        setting.value = value\n      else\n        add_setting(section, name, value)\n      end\n    end\n\n    def delete(section_name, name)\n      setting = @config.setting(section_name, name)\n      if setting\n        @config.delete(section_name, name)\n        setting.to_s.chomp\n      end\n    end\n\n    private\n\n    def add_setting(section_name, name, value)\n      section = @config.section_line(section_name)\n      if section.nil?\n        previous_line = SectionLine.new(\"\", section_name, \"\")\n        @config.append(previous_line)\n      else\n        previous_line = @config.settings_in(@config.lines_in(section_name)).last || section\n      end\n\n      @config.insert_after(previous_line, SettingLine.new(\"\", name, \" = \", value, \"\"))\n    end\n  end\n\n  module LineNumber\n    attr_accessor :previous\n\n    def line_number\n      line = 0\n      previous_line = previous\n      while previous_line\n        line += 1\n        previous_line = previous_line.previous\n      end\n      line\n    end\n  end\n\n  Line = Struct.new(:text) do\n    include LineNumber\n\n    def to_s\n      text\n    end\n\n    def write(fh)\n      fh.puts(to_s)\n    end\n  end\n\n  SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do\n    include LineNumber\n\n    def to_s\n      \"#{prefix}#{name}#{infix}#{value}#{suffix}\"\n    end\n\n    def write(fh)\n      fh.puts(to_s)\n    end\n\n    def ==(other)\n      super(other) && line_number == other.line_number\n    end\n  end\n\n  SectionLine = Struct.new(:prefix, :name, :suffix) do\n    include LineNumber\n\n    def to_s\n      \"#{prefix}[#{name}]#{suffix}\"\n    end\n\n    def write(fh)\n      fh.puts(to_s)\n    end\n  end\n\n  class DefaultSection < SectionLine\n    attr_accessor :write_sectionline\n\n    def initialize\n      @write_sectionline = false\n      super(\"\", DEFAULT_SECTION_NAME, \"\")\n    end\n\n    def write(fh)\n      if @write_sectionline\n        super(fh)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/integer_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::IntegerSetting < Puppet::Settings::BaseSetting\n  def munge(value)\n    return value if value.is_a?(Integer)\n\n    begin\n      value = Integer(value)\n    rescue ArgumentError, TypeError\n      raise Puppet::Settings::ValidationError, _(\"Cannot convert '%{value}' to an integer for parameter: %{name}\") % { value: value.inspect, name: @name }\n    end\n\n    value\n  end\n\n  def type\n    :integer\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/path_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::PathSetting < Puppet::Settings::StringSetting\n  def munge(value)\n    if value.is_a?(String)\n      value = value.split(File::PATH_SEPARATOR).map { |d| File.expand_path(d) }.join(File::PATH_SEPARATOR)\n    end\n    value\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/port_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::PortSetting < Puppet::Settings::IntegerSetting\n  def munge(value)\n    value = super(value)\n\n    if value < 0 || value > 65_535\n      raise Puppet::Settings::ValidationError, _(\"Value '%{value}' is not a valid port number for parameter: %{name}\") % { value: value.inspect, name: @name }\n    end\n\n    value\n  end\n\n  def type\n    :port\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/priority_setting.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/settings/base_setting'\n\n# A setting that represents a scheduling priority, and evaluates to an\n# OS-specific priority level.\nclass Puppet::Settings::PrioritySetting < Puppet::Settings::BaseSetting\n  PRIORITY_MAP =\n    if Puppet::Util::Platform.windows?\n      require_relative '../../puppet/util/windows/process'\n      require_relative '../../puppet/ffi/windows/constants'\n      {\n        :high => Puppet::FFI::Windows::Constants::HIGH_PRIORITY_CLASS,\n        :normal => Puppet::FFI::Windows::Constants::NORMAL_PRIORITY_CLASS,\n        :low => Puppet::FFI::Windows::Constants::BELOW_NORMAL_PRIORITY_CLASS,\n        :idle => Puppet::FFI::Windows::Constants::IDLE_PRIORITY_CLASS\n      }\n    else\n      {\n        :high => -10,\n        :normal => 0,\n        :low => 10,\n        :idle => 19\n      }\n    end\n\n  def type\n    :priority\n  end\n\n  def munge(value)\n    return unless value\n\n    if value.is_a?(Integer)\n      value\n    elsif value.is_a?(String) and value =~ /\\d+/\n      value.to_i\n    elsif value.is_a?(String) and PRIORITY_MAP[value.to_sym]\n      PRIORITY_MAP[value.to_sym]\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid priority format '%{value}' for parameter: %{name}\") % { value: value.inspect, name: @name }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/server_list_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::ServerListSetting < Puppet::Settings::ArraySetting\n  def type\n    :server_list\n  end\n\n  def print(value)\n    if value.is_a?(Array)\n      # turn into a string\n      value.map { |item| item.join(\":\") }.join(\",\")\n    else\n      value\n    end\n  end\n\n  def munge(value)\n    servers = super\n    servers.map! { |server|\n      case server\n      when String\n        server.split(':')\n      when Array\n        server\n      else\n        raise ArgumentError, _(\"Expected an Array of String, got a %{klass}\") % { klass: value.class }\n      end\n    }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/string_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::StringSetting < Puppet::Settings::BaseSetting\n  def type\n    :string\n  end\n\n  def validate(value)\n    value.nil? or value.is_a?(String)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/symbolic_enum_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::SymbolicEnumSetting < Puppet::Settings::BaseSetting\n  attr_accessor :values\n\n  def type\n    :symbolic_enum\n  end\n\n  def munge(value)\n    sym = value.to_sym\n    if values.include?(sym)\n      sym\n    else\n      raise Puppet::Settings::ValidationError,\n            _(\"Invalid value '%{value}' for parameter %{name}. Allowed values are '%{allowed_values}'\") % { value: value, name: @name, allowed_values: values.join(\"', '\") }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/terminus_setting.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Settings::TerminusSetting < Puppet::Settings::BaseSetting\n  def munge(value)\n    case value\n    when '', nil\n      nil\n    when String\n      value.intern\n    when Symbol\n      value\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid terminus setting: %{value}\") % { value: value }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/ttl_setting.rb",
    "content": "# frozen_string_literal: true\n\n# A setting that represents a span of time to live, and evaluates to Numeric\n# seconds to live where 0 means shortest possible time to live, a positive numeric value means time\n# to live in seconds, and the symbolic entry 'unlimited' is an infinite amount of time.\n#\nclass Puppet::Settings::TTLSetting < Puppet::Settings::BaseSetting\n  # How we convert from various units to seconds.\n  UNITMAP = {\n    # 365 days isn't technically a year, but is sufficient for most purposes\n    \"y\" => 365 * 24 * 60 * 60,\n    \"d\" => 24 * 60 * 60,\n    \"h\" => 60 * 60,\n    \"m\" => 60,\n    \"s\" => 1\n  }\n\n  # A regex describing valid formats with groups for capturing the value and units\n  FORMAT = /^(\\d+)(y|d|h|m|s)?$/\n\n  def type\n    :ttl\n  end\n\n  # Convert the value to Numeric, parsing numeric string with units if necessary.\n  def munge(value)\n    self.class.munge(value, @name)\n  end\n\n  def print(value)\n    val = munge(value)\n    val == Float::INFINITY ? 'unlimited' : val\n  end\n\n  # Convert the value to Numeric, parsing numeric string with units if necessary.\n  def self.munge(value, param_name)\n    if value.is_a?(Numeric)\n      if value < 0\n        raise Puppet::Settings::ValidationError, _(\"Invalid negative 'time to live' %{value} - did you mean 'unlimited'?\") % { value: value.inspect }\n      end\n\n      value\n\n    elsif value == 'unlimited'\n      Float::INFINITY\n\n    elsif value.is_a?(String) and value =~ FORMAT\n      ::Regexp.last_match(1).to_i * UNITMAP[::Regexp.last_match(2) || 's']\n    else\n      raise Puppet::Settings::ValidationError, _(\"Invalid 'time to live' format '%{value}' for parameter: %{param_name}\") % { value: value.inspect, param_name: param_name }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings/value_translator.rb",
    "content": "# frozen_string_literal: true\n\n# Convert arguments into booleans, integers, or whatever.\nclass Puppet::Settings::ValueTranslator\n  def [](value)\n    # Handle different data types correctly\n    case value\n    when /^false$/i; false\n    when /^true$/i; true\n    when true; true\n    when false; false\n    else\n      value.gsub(/^[\"']|[\"']$/, '').sub(/\\s+$/, '')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/settings.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet'\nrequire 'getoptlong'\nrequire_relative '../puppet/util/watched_file'\nrequire_relative '../puppet/util/command_line/puppet_option_parser'\nrequire 'forwardable'\nrequire 'fileutils'\nrequire 'concurrent'\nrequire_relative 'concurrent/lock'\n\n# The class for handling configuration files.\nclass Puppet::Settings\n  extend Forwardable\n  include Enumerable\n\n  require_relative 'settings/errors'\n  require_relative 'settings/base_setting'\n  require_relative 'settings/string_setting'\n  require_relative 'settings/enum_setting'\n  require_relative 'settings/symbolic_enum_setting'\n  require_relative 'settings/array_setting'\n  require_relative 'settings/file_setting'\n  require_relative 'settings/directory_setting'\n  require_relative 'settings/file_or_directory_setting'\n  require_relative 'settings/path_setting'\n  require_relative 'settings/boolean_setting'\n  require_relative 'settings/integer_setting'\n  require_relative 'settings/port_setting'\n  require_relative 'settings/terminus_setting'\n  require_relative 'settings/duration_setting'\n  require_relative 'settings/ttl_setting'\n  require_relative 'settings/priority_setting'\n  require_relative 'settings/autosign_setting'\n  require_relative 'settings/config_file'\n  require_relative 'settings/value_translator'\n  require_relative 'settings/environment_conf'\n  require_relative 'settings/server_list_setting'\n  require_relative 'settings/http_extra_headers_setting'\n  require_relative 'settings/certificate_revocation_setting'\n  require_relative 'settings/alias_setting'\n\n  # local reference for convenience\n  PuppetOptionParser = Puppet::Util::CommandLine::PuppetOptionParser\n\n  attr_reader :timer\n\n  # These are the settings that every app is required to specify; there are\n  # reasonable defaults defined in application.rb.\n  REQUIRED_APP_SETTINGS = [:logdir, :confdir, :vardir, :codedir]\n\n  # The acceptable sections of the puppet.conf configuration file.\n  ALLOWED_SECTION_NAMES = %w[main server master agent user].freeze\n\n  NONE = 'none'\n\n  # This method is intended for puppet internal use only; it is a convenience method that\n  # returns reasonable application default settings values for a given run_mode.\n  def self.app_defaults_for_run_mode(run_mode)\n    {\n      :name => run_mode.to_s,\n      :run_mode => run_mode.name,\n      :confdir => run_mode.conf_dir,\n      :codedir => run_mode.code_dir,\n      :vardir => run_mode.var_dir,\n      :publicdir => run_mode.public_dir,\n      :rundir => run_mode.run_dir,\n      :logdir => run_mode.log_dir,\n    }\n  end\n\n  def self.default_certname\n    hostname = hostname_fact\n    domain = domain_fact\n    if domain and domain != \"\"\n      fqdn = [hostname, domain].join(\".\")\n    else\n      fqdn = hostname\n    end\n    fqdn.to_s.gsub(/\\.$/, '')\n  end\n\n  def self.hostname_fact\n    Puppet.runtime[:facter].value('networking.hostname')\n  end\n\n  def self.domain_fact\n    Puppet.runtime[:facter].value('networking.domain')\n  end\n\n  def self.default_config_file_name\n    \"puppet.conf\"\n  end\n\n  def stringify_settings(section, settings = :all)\n    values_from_the_selected_section =\n      values(nil, section.to_sym)\n\n    loader_settings = {\n      :environmentpath => values_from_the_selected_section.interpolate(:environmentpath),\n      :basemodulepath => values_from_the_selected_section.interpolate(:basemodulepath),\n    }\n\n    Puppet.override(Puppet.base_context(loader_settings),\n                    _(\"New environment loaders generated from the requested section.\")) do\n      # And now we can lookup values that include those from environments configured from\n      # the requested section\n      values = values(Puppet[:environment].to_sym, section.to_sym)\n\n      to_be_rendered = {}\n      settings = Puppet.settings.to_a.collect(&:first) if settings == :all\n      settings.sort.each do |setting_name|\n        to_be_rendered[setting_name] = values.print(setting_name.to_sym)\n      end\n\n      stringifyhash(to_be_rendered)\n    end\n  end\n\n  def stringifyhash(hash)\n    newhash = {}\n    hash.each do |key, val|\n      key = key.to_s\n      case val\n      when Hash\n        newhash[key] = stringifyhash(val)\n      when Symbol\n        newhash[key] = val.to_s\n      else\n        newhash[key] = val\n      end\n    end\n    newhash\n  end\n\n  # Create a new collection of config settings.\n  def initialize\n    @config = {}\n    @shortnames = {}\n\n    @created = []\n\n    # Keep track of set values.\n    @value_sets = {\n      :cli => Values.new(:cli, @config),\n      :memory => Values.new(:memory, @config),\n      :application_defaults => Values.new(:application_defaults, @config),\n      :overridden_defaults => Values.new(:overridden_defaults, @config),\n    }\n    @configuration_file = nil\n\n    # And keep a per-environment cache\n    # We can't use Concurrent::Map because we want to preserve insertion order\n    @cache_lock = Puppet::Concurrent::Lock.new\n    @cache = Concurrent::Hash.new do |hash, key|\n      @cache_lock.synchronize do\n        break hash[key] if hash.key?(key)\n\n        hash[key] = Concurrent::Hash.new\n      end\n    end\n    @values_lock = Puppet::Concurrent::Lock.new\n    @values = Concurrent::Hash.new do |hash, key|\n      @values_lock.synchronize do\n        break hash[key] if hash.key?(key)\n\n        hash[key] = Concurrent::Hash.new\n      end\n    end\n\n    # The list of sections we've used.\n    @used = []\n\n    @hooks_to_call_on_application_initialization = []\n    @deprecated_setting_names = []\n    @deprecated_settings_that_have_been_configured = []\n\n    @translate = Puppet::Settings::ValueTranslator.new\n    @config_file_parser = Puppet::Settings::ConfigFile.new(@translate)\n  end\n\n  # Retrieve a config value\n  # @param param [Symbol] the name of the setting\n  # @return [Object] the value of the setting\n  # @api private\n  def [](param)\n    if @deprecated_setting_names.include?(param)\n      issue_deprecation_warning(setting(param), \"Accessing '#{param}' as a setting is deprecated.\")\n    end\n    value(param)\n  end\n\n  # Set a config value.  This doesn't set the defaults, it sets the value itself.\n  # @param param [Symbol] the name of the setting\n  # @param value [Object] the new value of the setting\n  # @api private\n  def []=(param, value)\n    if @deprecated_setting_names.include?(param)\n      issue_deprecation_warning(setting(param), \"Modifying '#{param}' as a setting is deprecated.\")\n    end\n    @value_sets[:memory].set(param, value)\n    unsafe_flush_cache\n  end\n\n  # Create a new default value for the given setting. The default overrides are\n  # higher precedence than the defaults given in defaults.rb, but lower\n  # precedence than any other values for the setting. This allows one setting\n  # `a` to change the default of setting `b`, but still allow a user to provide\n  # a value for setting `b`.\n  #\n  # @param param [Symbol] the name of the setting\n  # @param value [Object] the new default value for the setting\n  # @api private\n  def override_default(param, value)\n    @value_sets[:overridden_defaults].set(param, value)\n    unsafe_flush_cache\n  end\n\n  # Generate the list of valid arguments, in a format that GetoptLong can\n  # understand, and add them to the passed option list.\n  def addargs(options)\n    # Add all of the settings as valid options.\n    each { |_name, setting|\n      setting.getopt_args.each { |args| options << args }\n    }\n\n    options\n  end\n\n  # Generate the list of valid arguments, in a format that OptionParser can\n  # understand, and add them to the passed option list.\n  def optparse_addargs(options)\n    # Add all of the settings as valid options.\n    each { |_name, setting|\n      options << setting.optparse_args\n    }\n\n    options\n  end\n\n  # Is our setting a boolean setting?\n  def boolean?(param)\n    param = param.to_sym\n    @config.include?(param) and @config[param].is_a?(BooleanSetting)\n  end\n\n  # Remove all set values, potentially skipping cli values.\n  def clear\n    unsafe_clear\n  end\n\n  # Remove all set values, potentially skipping cli values.\n  def unsafe_clear(clear_cli = true, clear_application_defaults = false)\n    if clear_application_defaults\n      @value_sets[:application_defaults] = Values.new(:application_defaults, @config)\n      @app_defaults_initialized = false\n    end\n\n    if clear_cli\n      @value_sets[:cli] = Values.new(:cli, @config)\n\n      # Only clear the 'used' values if we were explicitly asked to clear out\n      #  :cli values; otherwise, it may be just a config file reparse,\n      #  and we want to retain this cli values.\n      @used = []\n    end\n\n    @value_sets[:memory] = Values.new(:memory, @config)\n    @value_sets[:overridden_defaults] = Values.new(:overridden_defaults, @config)\n\n    @deprecated_settings_that_have_been_configured.clear\n    @values.clear\n    @cache.clear\n  end\n  private :unsafe_clear\n\n  # Clears all cached settings for a particular environment to ensure\n  # that changes to environment.conf are reflected in the settings if\n  # the environment timeout has expired.\n  #\n  # param [String, Symbol] environment the  name of environment to clear settings for\n  #\n  # @api private\n  def clear_environment_settings(environment)\n    if environment.nil?\n      return\n    end\n\n    @cache[environment.to_sym].clear\n    @values[environment.to_sym] = {}\n  end\n\n  # Clear @cache, @used and the Environment.\n  #\n  # Whenever an object is returned by Settings, a copy is stored in @cache.\n  # As long as Setting attributes that determine the content of returned\n  # objects remain unchanged, Settings can keep returning objects from @cache\n  # without re-fetching or re-generating them.\n  #\n  # Whenever a Settings attribute changes, such as @values or @preferred_run_mode,\n  # this method must be called to clear out the caches so that updated\n  # objects will be returned.\n  def flush_cache\n    unsafe_flush_cache\n  end\n\n  def unsafe_flush_cache\n    clearused\n  end\n  private :unsafe_flush_cache\n\n  def clearused\n    @cache.clear\n    @used = []\n  end\n\n  def global_defaults_initialized?\n    @global_defaults_initialized\n  end\n\n  def initialize_global_settings(args = [], require_config = true)\n    raise Puppet::DevError, _(\"Attempting to initialize global default settings more than once!\") if global_defaults_initialized?\n\n    # The first two phases of the lifecycle of a puppet application are:\n    # 1) Parse the command line options and handle any of them that are\n    #    registered, defined \"global\" puppet settings (mostly from defaults.rb).\n    # 2) Parse the puppet config file(s).\n\n    parse_global_options(args)\n    parse_config_files(require_config)\n\n    @global_defaults_initialized = true\n  end\n\n  # This method is called during application bootstrapping.  It is responsible for parsing all of the\n  # command line options and initializing the settings accordingly.\n  #\n  # It will ignore options that are not defined in the global puppet settings list, because they may\n  # be valid options for the specific application that we are about to launch... however, at this point\n  # in the bootstrapping lifecycle, we don't yet know what that application is.\n  def parse_global_options(args)\n    # Create an option parser\n    option_parser = PuppetOptionParser.new\n    option_parser.ignore_invalid_options = true\n\n    # Add all global options to it.\n    optparse_addargs([]).each do |option|\n      option_parser.on(*option) do |arg|\n        opt, val = Puppet::Settings.clean_opt(option[0], arg)\n        handlearg(opt, val)\n      end\n    end\n\n    option_parser.on('--run_mode',\n                     \"The effective 'run mode' of the application: server, agent, or user.\",\n                     :REQUIRED) do |arg|\n      Puppet.settings.preferred_run_mode = arg\n    end\n\n    option_parser.parse(args)\n\n    # remove run_mode options from the arguments so that later parses don't think\n    # it is an unknown option.\n    while option_index = args.index('--run_mode') # rubocop:disable Lint/AssignmentInCondition\n      args.delete_at option_index\n      args.delete_at option_index\n    end\n    args.reject! { |arg| arg.start_with? '--run_mode=' }\n  end\n  private :parse_global_options\n\n  # A utility method (public, is used by application.rb and perhaps elsewhere) that munges a command-line\n  # option string into the format that Puppet.settings expects.  (This mostly has to deal with handling the\n  # \"no-\" prefix on flag/boolean options).\n  #\n  # @param [String] opt the command line option that we are munging\n  # @param [String, TrueClass, FalseClass] val the value for the setting (as determined by the OptionParser)\n  def self.clean_opt(opt, val)\n    # rewrite --[no-]option to --no-option if that's what was given\n    if opt =~ /\\[no-\\]/ and !val\n      opt = opt.gsub(/\\[no-\\]/, 'no-')\n    end\n    # otherwise remove the [no-] prefix to not confuse everybody\n    opt = opt.gsub(/\\[no-\\]/, '')\n    [opt, val]\n  end\n\n  def app_defaults_initialized?\n    @app_defaults_initialized\n  end\n\n  def initialize_app_defaults(app_defaults)\n    REQUIRED_APP_SETTINGS.each do |key|\n      raise SettingsError, \"missing required app default setting '#{key}'\" unless app_defaults.has_key?(key)\n    end\n\n    app_defaults.each do |key, value|\n      if key == :run_mode\n        self.preferred_run_mode = value\n      else\n        @value_sets[:application_defaults].set(key, value)\n        unsafe_flush_cache\n      end\n    end\n    apply_metadata\n    call_hooks_deferred_to_application_initialization\n    issue_deprecations\n\n    REQUIRED_APP_SETTINGS.each do |key|\n      create_ancestors(Puppet[key])\n    end\n\n    @app_defaults_initialized = true\n  end\n\n  # Create ancestor directories.\n  #\n  # @param dir [String] absolute path for a required application default directory\n  # @api private\n\n  def create_ancestors(dir)\n    parent_dir = File.dirname(dir)\n\n    unless File.exist?(parent_dir)\n      FileUtils.mkdir_p(parent_dir)\n    end\n  end\n  private :create_ancestors\n\n  def call_hooks_deferred_to_application_initialization(options = {})\n    @hooks_to_call_on_application_initialization.each do |setting|\n      setting.handle(value(setting.name))\n    rescue InterpolationError => err\n      raise InterpolationError, err.message, err.backtrace unless options[:ignore_interpolation_dependency_errors]\n      # swallow. We're not concerned if we can't call hooks because dependencies don't exist yet\n      # we'll get another chance after application defaults are initialized\n    end\n  end\n  private :call_hooks_deferred_to_application_initialization\n\n  # Return a value's description.\n  def description(name)\n    obj = @config[name.to_sym]\n    if obj\n      obj.desc\n    else\n      nil\n    end\n  end\n\n  def_delegators :@config, :each, :each_pair, :each_key\n\n  # Iterate over each section name.\n  def eachsection\n    yielded = []\n    @config.each_value do |object|\n      section = object.section\n      unless yielded.include? section\n        yield section\n        yielded << section\n      end\n    end\n  end\n\n  # Returns a given setting by name\n  # @param name [Symbol] The name of the setting to fetch\n  # @return [Puppet::Settings::BaseSetting] The setting object\n  def setting(param)\n    param = param.to_sym\n    @config[param]\n  end\n\n  # Handle a command-line argument.\n  def handlearg(opt, value = nil)\n    @cache.clear\n\n    case value\n    when FalseClass\n      value = \"false\"\n    when TrueClass\n      value = \"true\"\n    end\n\n    value &&= @translate[value]\n    str = opt.sub(/^--/, '')\n\n    bool = true\n    newstr = str.sub(/^no-/, '')\n    if newstr != str\n      str = newstr\n      bool = false\n    end\n    str = str.intern\n\n    if @config[str].is_a?(Puppet::Settings::BooleanSetting)\n      if value == \"\" or value.nil?\n        value = bool\n      end\n    end\n\n    s = @config[str]\n    if s\n      @deprecated_settings_that_have_been_configured << s if s.completely_deprecated?\n    end\n\n    @value_sets[:cli].set(str, value)\n    unsafe_flush_cache\n  end\n\n  def include?(name)\n    name = name.intern if name.is_a? String\n    @config.include?(name)\n  end\n\n  # Prints the contents of a config file with the available config settings, or it\n  # prints a single value of a config setting.\n  def print_config_options\n    if Puppet::Util::Log.sendlevel?(:info)\n      Puppet::Util::Log.newdestination(:console)\n      message = _(\"Using --configprint is deprecated. Use 'puppet config <subcommand>' instead.\")\n      Puppet.deprecation_warning(message)\n    end\n\n    env = value(:environment)\n    val = value(:configprint)\n    if val == \"all\"\n      hash = {}\n      each do |name, _obj|\n        val = value(name, env)\n        val = val.inspect if val == \"\"\n        hash[name] = val\n      end\n      hash.sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |name, v|\n        puts \"#{name} = #{v}\"\n      end\n    else\n      val.split(/\\s*,\\s*/).sort.each do |v|\n        if include?(v)\n          # if there is only one value, just print it for back compatibility\n          if v == val\n            puts value(val, env)\n            break\n          end\n          puts \"#{v} = #{value(v, env)}\"\n        else\n          puts \"invalid setting: #{v}\"\n          return false\n        end\n      end\n    end\n    true\n  end\n\n  def generate_config\n    puts to_config\n    true\n  end\n\n  def generate_manifest\n    puts to_manifest\n    true\n  end\n\n  def print_configs\n    return print_config_options if value(:configprint) != \"\"\n    return generate_config if value(:genconfig)\n\n    generate_manifest if value(:genmanifest)\n  end\n\n  def print_configs?\n    (value(:configprint) != \"\" || value(:genconfig) || value(:genmanifest)) && true\n  end\n\n  # The currently configured run mode that is preferred for constructing the application configuration.\n  def preferred_run_mode\n    @preferred_run_mode_name || :user\n  end\n\n  # PRIVATE!  This only exists because we need a hook to validate the run mode when it's being set, and\n  #  it should never, ever, ever, ever be called from outside of this file.\n  # This method is also called when --run_mode MODE is used on the command line to set the default\n  #\n  # @param mode [String|Symbol] the name of the mode to have in effect\n  # @api private\n  def preferred_run_mode=(mode)\n    mode = mode.to_s.downcase.intern\n    raise ValidationError, \"Invalid run mode '#{mode}'\" unless [:server, :master, :agent, :user].include?(mode)\n\n    @preferred_run_mode_name = mode\n    # Changing the run mode has far-reaching consequences. Flush any cached\n    # settings so they will be re-generated.\n    flush_cache\n  end\n\n  def parse_config(text, file = \"text\")\n    begin\n      data = @config_file_parser.parse_file(file, text, ALLOWED_SECTION_NAMES)\n    rescue => detail\n      Puppet.log_exception(detail, \"Could not parse #{file}: #{detail}\")\n      return\n    end\n\n    # If we get here and don't have any data, we just return and don't muck with the current state of the world.\n    return if data.nil?\n\n    # If we get here then we have some data, so we need to clear out any\n    # previous settings that may have come from config files.\n    unsafe_clear(false, false)\n\n    # Screen settings which have been deprecated and removed from puppet.conf\n    # but are still valid on the command line and/or in environment.conf\n    screen_non_puppet_conf_settings(data)\n\n    # Make note of deprecated settings we will warn about later in initialization\n    record_deprecations_from_puppet_conf(data)\n\n    # And now we can repopulate with the values from our last parsing of the config files.\n    @configuration_file = data\n\n    # Determine our environment, if we have one.\n    if @config[:environment]\n      env = value(:environment).to_sym\n    else\n      env = NONE\n    end\n\n    # Call any hooks we should be calling.\n    value_sets = value_sets_for(env, preferred_run_mode)\n    @config.values.select(&:has_hook?).each do |setting|\n      value_sets.each do |source|\n        next unless source.include?(setting.name)\n\n        # We still have to use value to retrieve the value, since\n        # we want the fully interpolated value, not $vardir/lib or whatever.\n        # This results in extra work, but so few of the settings\n        # will have associated hooks that it ends up being less work this\n        # way overall.\n        if setting.call_hook_on_initialize?\n          @hooks_to_call_on_application_initialization |= [setting]\n        else\n          setting.handle(ChainedValues.new(\n            preferred_run_mode,\n            env,\n            value_sets,\n            @config\n          ).interpolate(setting.name))\n        end\n        break\n      end\n    end\n\n    call_hooks_deferred_to_application_initialization :ignore_interpolation_dependency_errors => true\n    apply_metadata\n  end\n\n  # Parse the configuration file.  Just provides thread safety.\n  def parse_config_files(require_config = true)\n    file = which_configuration_file\n    if Puppet::FileSystem.exist?(file)\n      begin\n        text = read_file(file)\n      rescue => detail\n        message = _(\"Could not load %{file}: %{detail}\") % { file: file, detail: detail }\n        if require_config\n          Puppet.log_and_raise(detail, message)\n        else\n          Puppet.log_exception(detail, message)\n          return\n        end\n      end\n    else\n      return\n    end\n\n    parse_config(text, file)\n  end\n  private :parse_config_files\n\n  def main_config_file\n    if explicit_config_file?\n      self[:config]\n    else\n      File.join(Puppet::Util::RunMode[:server].conf_dir, config_file_name)\n    end\n  end\n  private :main_config_file\n\n  def user_config_file\n    File.join(Puppet::Util::RunMode[:user].conf_dir, config_file_name)\n  end\n  private :user_config_file\n\n  # This method is here to get around some life-cycle issues.  We need to be\n  # able to determine the config file name before the settings / defaults are\n  # fully loaded.  However, we also need to respect any overrides of this value\n  # that the user may have specified on the command line.\n  #\n  # The easiest way to do this is to attempt to read the setting, and if we\n  # catch an error (meaning that it hasn't been set yet), we'll fall back to\n  # the default value.\n  def config_file_name\n    begin\n      return self[:config_file_name] if self[:config_file_name]\n    rescue SettingsError\n      # This just means that the setting wasn't explicitly set on the command line, so we will ignore it and\n      #  fall through to the default name.\n    end\n    self.class.default_config_file_name\n  end\n  private :config_file_name\n\n  def apply_metadata\n    # We have to do it in the reverse of the search path,\n    # because multiple sections could set the same value\n    # and I'm too lazy to only set the metadata once.\n    if @configuration_file\n      searchpath(nil, preferred_run_mode).reverse_each do |source|\n        section = @configuration_file.sections[source.name] if source.type == :section\n        if section\n          apply_metadata_from_section(section)\n        end\n      end\n    end\n  end\n  private :apply_metadata\n\n  def apply_metadata_from_section(section)\n    section.settings.each do |setting|\n      type = @config[setting.name] if setting.has_metadata?\n      if type\n        type.set_meta(setting.meta)\n      end\n    end\n  end\n\n  SETTING_TYPES = {\n    :string => StringSetting,\n    :file => FileSetting,\n    :directory => DirectorySetting,\n    :file_or_directory => FileOrDirectorySetting,\n    :path => PathSetting,\n    :boolean => BooleanSetting,\n    :integer => IntegerSetting,\n    :port => PortSetting,\n    :terminus => TerminusSetting,\n    :duration => DurationSetting,\n    :ttl => TTLSetting,\n    :array => ArraySetting,\n    :enum => EnumSetting,\n    :symbolic_enum => SymbolicEnumSetting,\n    :priority => PrioritySetting,\n    :autosign => AutosignSetting,\n    :server_list => ServerListSetting,\n    :http_extra_headers => HttpExtraHeadersSetting,\n    :certificate_revocation => CertificateRevocationSetting,\n    :alias => AliasSetting\n  }\n\n  # Create a new setting.  The value is passed in because it's used to determine\n  # what kind of setting we're creating, but the value itself might be either\n  # a default or a value, so we can't actually assign it.\n  #\n  # See #define_settings for documentation on the legal values for the \":type\" option.\n  def newsetting(hash)\n    klass = nil\n    hash[:section] = hash[:section].to_sym if hash[:section]\n\n    type = hash[:type]\n    if type\n      klass = SETTING_TYPES[type]\n      unless klass\n        raise ArgumentError, _(\"Invalid setting type '%{type}'\") % { type: type }\n      end\n\n      hash.delete(:type)\n    else\n      # The only implicit typing we still do for settings is to fall back to \"String\" type if they didn't explicitly\n      # specify a type.  Personally I'd like to get rid of this too, and make the \"type\" option mandatory... but\n      # there was a little resistance to taking things quite that far for now.  --cprice 2012-03-19\n      klass = StringSetting\n    end\n    hash[:settings] = self\n    klass.new(hash)\n  end\n\n  # This has to be private, because it doesn't add the settings to @config\n  private :newsetting\n\n  # Iterate across all of the objects in a given section.\n  def persection(section)\n    section = section.to_sym\n    each { |_name, obj|\n      if obj.section == section\n        yield obj\n      end\n    }\n  end\n\n  # Reparse our config file, if necessary.\n  def reparse_config_files\n    if files\n      filename = any_files_changed?\n      if filename\n        Puppet.notice \"Config file #{filename} changed; triggering re-parse of all config files.\"\n        parse_config_files\n        reuse\n      end\n    end\n  end\n\n  def files\n    return @files if @files\n\n    @files = []\n    [main_config_file, user_config_file].each do |path|\n      if Puppet::FileSystem.exist?(path)\n        @files << Puppet::Util::WatchedFile.new(path)\n      end\n    end\n    @files\n  end\n  private :files\n\n  # Checks to see if any of the config files have been modified\n  # @return the filename of the first file that is found to have changed, or\n  #   nil if no files have changed\n  def any_files_changed?\n    files.each do |file|\n      return file.to_str if file.changed?\n    end\n    nil\n  end\n  private :any_files_changed?\n\n  def reuse\n    return unless defined?(@used)\n\n    new = @used\n    @used = []\n    use(*new)\n  end\n\n  SearchPathElement = Struct.new(:name, :type)\n\n  # The order in which to search for values, without defaults.\n  #\n  # @param environment [String,Symbol] symbolic reference to an environment name\n  # @param run_mode [Symbol] symbolic reference to a Puppet run mode\n  # @return [Array<SearchPathElement>]\n  # @api private\n  def configsearchpath(environment = nil, run_mode = preferred_run_mode)\n    searchpath = [\n      SearchPathElement.new(:memory, :values),\n      SearchPathElement.new(:cli, :values)\n    ]\n    searchpath << SearchPathElement.new(environment.intern, :environment) if environment\n\n    if run_mode\n      if [:master, :server].include?(run_mode)\n        searchpath << SearchPathElement.new(:server, :section)\n        searchpath << SearchPathElement.new(:master, :section)\n      else\n        searchpath << SearchPathElement.new(run_mode, :section)\n      end\n    end\n\n    searchpath << SearchPathElement.new(:main, :section)\n  end\n\n  # The order in which to search for values.\n  #\n  # @param environment [String,Symbol] symbolic reference to an environment name\n  # @param run_mode [Symbol] symbolic reference to a Puppet run mode\n  # @return [Array<SearchPathElement>]\n  # @api private\n  def searchpath(environment = nil, run_mode = preferred_run_mode)\n    searchpath = configsearchpath(environment, run_mode)\n    searchpath << SearchPathElement.new(:application_defaults, :values)\n    searchpath << SearchPathElement.new(:overridden_defaults, :values)\n  end\n\n  def service_user_available?\n    return @service_user_available if defined?(@service_user_available)\n\n    if self[:user]\n      user = Puppet::Type.type(:user).new :name => self[:user], :audit => :ensure\n\n      if user.suitable?\n        @service_user_available = user.exists?\n      else\n        raise Puppet::Error, (_(\"Cannot manage owner permissions, because the provider for '%{name}' is not functional\") % { name: user })\n      end\n    else\n      @service_user_available = false\n    end\n  end\n\n  def service_group_available?\n    return @service_group_available if defined?(@service_group_available)\n\n    if self[:group]\n      group = Puppet::Type.type(:group).new :name => self[:group], :audit => :ensure\n\n      if group.suitable?\n        @service_group_available = group.exists?\n      else\n        raise Puppet::Error, (_(\"Cannot manage group permissions, because the provider for '%{name}' is not functional\") % { name: group })\n      end\n    else\n      @service_group_available = false\n    end\n  end\n\n  # Allow later inspection to determine if the setting was set on the\n  # command line, or through some other code path.  Used for the\n  # `dns_alt_names` option during cert generate. --daniel 2011-10-18\n  #\n  # @param param [String, Symbol] the setting to look up\n  # @return [Object, nil] the value of the setting or nil if unset\n  def set_by_cli(param)\n    param = param.to_sym\n    @value_sets[:cli].lookup(param)\n  end\n\n  def set_by_cli?(param)\n    !!set_by_cli(param)\n  end\n\n  # Get values from a search path entry.\n  # @api private\n  def searchpath_values(source)\n    case source.type\n    when :values\n      @value_sets[source.name]\n    when :section\n      section = @configuration_file.sections[source.name] if @configuration_file\n      if section\n        ValuesFromSection.new(source.name, section)\n      end\n    when :environment\n      ValuesFromEnvironmentConf.new(source.name)\n    else\n      raise Puppet::DevError, _(\"Unknown searchpath case: %{source_type} for the %{source} settings path element.\") % { source_type: source.type, source: source }\n    end\n  end\n\n  # Allow later inspection to determine if the setting was set by user\n  # config, rather than a default setting.\n  def set_by_config?(param, environment = nil, run_mode = preferred_run_mode)\n    param = param.to_sym\n    configsearchpath(environment, run_mode).any? do |source|\n      vals = searchpath_values(source)\n      if vals\n        vals.lookup(param)\n      end\n    end\n  end\n\n  # Allow later inspection to determine if the setting was set in a specific\n  # section\n  #\n  # @param param [String, Symbol] the setting to look up\n  # @param section [Symbol] the section in which to look up the setting\n  # @return [Object, nil] the value of the setting or nil if unset\n  def set_in_section(param, section)\n    param = param.to_sym\n    vals = searchpath_values(SearchPathElement.new(section, :section))\n    if vals\n      vals.lookup(param)\n    end\n  end\n\n  def set_in_section?(param, section)\n    !!set_in_section(param, section)\n  end\n\n  # Patches the value for a param in a section.\n  # This method is required to support the use case of unifying --dns-alt-names and\n  # --dns_alt_names in the certificate face. Ideally this should be cleaned up.\n  # See PUP-3684 for more information.\n  # For regular use of setting a value, the method `[]=` should be used.\n  # @api private\n  #\n  def patch_value(param, value, type)\n    if @value_sets[type]\n      @value_sets[type].set(param, value)\n      unsafe_flush_cache\n    end\n  end\n\n  # Define a group of settings.\n  #\n  # @param [Symbol] section a symbol to use for grouping multiple settings together into a conceptual unit.  This value\n  #   (and the conceptual separation) is not used very often; the main place where it will have a potential impact\n  #   is when code calls Settings#use method.  See docs on that method for further details, but basically that method\n  #   just attempts to do any preparation that may be necessary before code attempts to leverage the value of a particular\n  #   setting.  This has the most impact for file/directory settings, where #use will attempt to \"ensure\" those\n  #   files / directories.\n  # @param [Hash[Hash]] defs the settings to be defined.  This argument is a hash of hashes; each key should be a symbol,\n  #   which is basically the name of the setting that you are defining.  The value should be another hash that specifies\n  #   the parameters for the particular setting.  Legal values include:\n  #    [:default] => not required; this is the value for the setting if no other value is specified (via cli, config file, etc.)\n  #       For string settings this may include \"variables\", demarcated with $ or ${} which will be interpolated with values of other settings.\n  #       The default value may also be a Proc that will be called only once to evaluate the default when the setting's value is retrieved.\n  #    [:desc] => required; a description of the setting, used in documentation / help generation\n  #    [:type] => not required, but highly encouraged!  This specifies the data type that the setting represents.  If\n  #       you do not specify it, it will default to \"string\".  Legal values include:\n  #       :string - A generic string setting\n  #       :boolean - A boolean setting; values are expected to be \"true\" or \"false\"\n  #       :file - A (single) file path; puppet may attempt to create this file depending on how the settings are used.  This type\n  #           also supports additional options such as \"mode\", \"owner\", \"group\"\n  #       :directory - A (single) directory path; puppet may attempt to create this file depending on how the settings are used.  This type\n  #           also supports additional options such as \"mode\", \"owner\", \"group\"\n  #       :path - This is intended to be used for settings whose value can contain multiple directory paths, represented\n  #           as strings separated by the system path separator (e.g. system path, module path, etc.).\n  #     [:mode] => an (optional) octal value to be used as the permissions/mode for :file and :directory settings\n  #     [:owner] => optional owner username/uid for :file and :directory settings\n  #     [:group] => optional group name/gid for :file and :directory settings\n  #\n  def define_settings(section, defs)\n    section = section.to_sym\n    call = []\n    defs.each do |name, hash|\n      raise ArgumentError, _(\"setting definition for '%{name}' is not a hash!\") % { name: name } unless hash.is_a? Hash\n\n      name = name.to_sym\n      hash[:name] = name\n      hash[:section] = section\n      raise ArgumentError, _(\"Setting %{name} is already defined\") % { name: name } if @config.include?(name)\n\n      tryconfig = newsetting(hash)\n      short = tryconfig.short\n      if short\n        other = @shortnames[short]\n        if other\n          raise ArgumentError, _(\"Setting %{name} is already using short name '%{short}'\") % { name: other.name, short: short }\n        end\n\n        @shortnames[short] = tryconfig\n      end\n      @config[name] = tryconfig\n\n      # Collect the settings that need to have their hooks called immediately.\n      # We have to collect them so that we can be sure we're fully initialized before\n      # the hook is called.\n      if tryconfig.has_hook?\n        if tryconfig.call_hook_on_define?\n          call << tryconfig\n        elsif tryconfig.call_hook_on_initialize?\n          @hooks_to_call_on_application_initialization |= [tryconfig]\n        end\n      end\n\n      @deprecated_setting_names << name if tryconfig.deprecated?\n    end\n\n    call.each do |setting|\n      setting.handle(value(setting.name))\n    end\n  end\n\n  # Convert the settings we manage into a catalog full of resources that model those settings.\n  def to_catalog(*sections)\n    sections = nil if sections.empty?\n\n    catalog = Puppet::Resource::Catalog.new(\"Settings\", Puppet::Node::Environment::NONE)\n    @config.keys.find_all { |key| @config[key].is_a?(FileSetting) }.each do |key|\n      file = @config[key]\n      next if file.value.nil?\n      next unless sections.nil? or sections.include?(file.section)\n\n      resource = file.to_resource\n      next unless resource\n      next if catalog.resource(resource.ref)\n\n      Puppet.debug { \"Using settings: adding file resource '#{key}': '#{resource.inspect}'\" }\n\n      catalog.add_resource(resource)\n    end\n\n    add_user_resources(catalog, sections)\n    add_environment_resources(catalog, sections)\n\n    catalog\n  end\n\n  # Convert our list of config settings into a configuration file.\n  def to_config\n    str = %(The configuration file for #{Puppet.run_mode.name}.  Note that this file\nis likely to have unused settings in it; any setting that's\nvalid anywhere in Puppet can be in any config file, even if it's not used.\n\nEvery section can specify three special parameters: owner, group, and mode.\nThese parameters affect the required permissions of any files specified after\ntheir specification.  Puppet will sometimes use these parameters to check its\nown configured state, so they can be used to make Puppet a bit more self-managing.\n\nThe file format supports octothorpe-commented lines, but not partial-line comments.\n\nGenerated on #{Time.now}.\n).gsub(/^/, \"# \")\n\n    #         Add a section heading that matches our name.\n    str += \"[#{preferred_run_mode}]\\n\"\n    eachsection do |section|\n      persection(section) do |obj|\n        str += obj.to_config + \"\\n\" unless obj.name == :genconfig\n      end\n    end\n\n    str\n  end\n\n  # Convert to a parseable manifest\n  def to_manifest\n    catalog = to_catalog\n    catalog.resource_refs.collect do |ref|\n      catalog.resource(ref).to_manifest\n    end.join(\"\\n\\n\")\n  end\n\n  # Create the necessary objects to use a section.  This is idempotent;\n  # you can 'use' a section as many times as you want.\n  def use(*sections)\n    if Puppet[:settings_catalog]\n      sections = sections.collect(&:to_sym)\n      sections = sections.reject { |s| @used.include?(s) }\n\n      Puppet.warning(\":master section deprecated in favor of :server section\") if sections.include?(:master)\n\n      # add :server if sections include :master or :master if sections include :server\n      sections |= [:master, :server] if (sections & [:master, :server]).any?\n\n      sections = sections.collect(&:to_sym)\n      sections = sections.reject { |s| @used.include?(s) }\n\n      return if sections.empty?\n\n      Puppet.debug { \"Applying settings catalog for sections #{sections.join(', ')}\" }\n\n      begin\n        catalog = to_catalog(*sections).to_ral\n      rescue => detail\n        Puppet.log_and_raise(detail, \"Could not create resources for managing Puppet's files and directories in sections #{sections.inspect}: #{detail}\")\n      end\n\n      catalog.host_config = false\n      catalog.apply do |transaction|\n        if transaction.any_failed?\n          report = transaction.report\n          status_failures = report.resource_statuses.values.select(&:failed?)\n          status_fail_msg = status_failures\n                            .collect(&:events)\n                            .flatten\n                            .select { |event| event.status == 'failure' }\n                            .collect { |event| \"#{event.resource}: #{event.message}\" }.join(\"; \")\n\n          raise \"Got #{status_failures.length} failure(s) while initializing: #{status_fail_msg}\"\n        end\n      end\n\n      sections.each { |s| @used << s }\n      @used.uniq!\n    else\n      Puppet.debug(\"Skipping settings catalog for sections #{sections.join(', ')}\")\n    end\n  end\n\n  def valid?(param)\n    param = param.to_sym\n    @config.has_key?(param)\n  end\n\n  # Retrieve an object that can be used for looking up values of configuration\n  # settings.\n  #\n  # @param environment [Symbol] The name of the environment in which to lookup\n  # @param section [Symbol] The name of the configuration section in which to lookup\n  # @return [Puppet::Settings::ChainedValues] An object to perform lookups\n  # @api public\n  def values(environment, section)\n    @values[environment][section] ||= ChainedValues.new(\n      section,\n      environment,\n      value_sets_for(environment, section),\n      @config\n    )\n  end\n\n  # Find the correct value using our search path.\n  #\n  # @param param [String, Symbol] The value to look up\n  # @param environment [String, Symbol] The environment to check for the value\n  # @param bypass_interpolation [true, false] Whether to skip interpolation\n  #\n  # @return [Object] The looked up value\n  #\n  # @raise [InterpolationError]\n  def value(param, environment = nil, bypass_interpolation = false)\n    environment &&= environment.to_sym\n    value_sym(param.to_sym, environment, bypass_interpolation)\n  end\n\n  # Find the correct value using symbols and our search path.\n  #\n  # @param param [Symbol] The value to look up\n  # @param environment [Symbol] The environment to check for the value\n  # @param bypass_interpolation [true, false] Whether to skip interpolation\n  #\n  # @return [Object] The looked up value\n  #\n  # @raise [InterpolationError]\n  def value_sym(param, environment = nil, bypass_interpolation = false)\n    # Check the cache first.  It needs to be a per-environment\n    # cache so that we don't spread values from one env\n    # to another.\n    cached_env = @cache[environment || NONE]\n\n    # Avoid two lookups in cache_env unless val is nil. When it is, it's important\n    # to check if the key is included so that further processing (that will result\n    # in nil again) is avoided.\n    val = cached_env[param]\n    return val if !val.nil? || cached_env.include?(param)\n\n    # Short circuit to nil for undefined settings.\n    return nil unless @config.include?(param)\n\n    vals = values(environment, preferred_run_mode)\n    val = bypass_interpolation ? vals.lookup(param) : vals.interpolate(param)\n    cached_env[param] = val\n    val\n  end\n\n  ##\n  # (#15337) All of the logic to determine the configuration file to use\n  #   should be centralized into this method.  The simplified approach is:\n  #\n  # 1. If there is an explicit configuration file, use that.  (--confdir or\n  #    --config)\n  # 2. If we're running as a root process, use the system puppet.conf\n  #    (usually /etc/puppetlabs/puppet/puppet.conf)\n  # 3. Otherwise, use the user puppet.conf (usually ~/.puppetlabs/etc/puppet/puppet.conf)\n  #\n  # @api private\n  # @todo this code duplicates {Puppet::Util::RunMode#which_dir} as described\n  #   in {https://projects.puppetlabs.com/issues/16637 #16637}\n  def which_configuration_file\n    if explicit_config_file? or Puppet.features.root? then\n      main_config_file\n    else\n      user_config_file\n    end\n  end\n\n  # This method just turns a file into a new ConfigFile::Conf instance\n  # @param file [String] absolute path to the configuration file\n  # @return [Puppet::Settings::ConfigFile::Conf]\n  # @api private\n  def parse_file(file, allowed_sections = [])\n    @config_file_parser.parse_file(file, read_file(file), allowed_sections)\n  end\n\n  private\n\n  DEPRECATION_REFS = {\n    # intentionally empty. This could be repopulated if we deprecate more settings\n    # and have reference links to associate with them\n  }.freeze\n\n  def screen_non_puppet_conf_settings(puppet_conf)\n    puppet_conf.sections.values.each do |section|\n      forbidden = section.settings.select { |setting| Puppet::Settings::EnvironmentConf::ENVIRONMENT_CONF_ONLY_SETTINGS.include?(setting.name) }\n      raise(SettingsError, \"Cannot set #{forbidden.map(&:name).join(', ')} settings in puppet.conf\") unless forbidden.empty?\n    end\n  end\n\n  # Record that we want to issue a deprecation warning later in the application\n  # initialization cycle when we have settings bootstrapped to the point where\n  # we can read the Puppet[:disable_warnings] setting.\n  #\n  # We are only recording warnings applicable to settings set in puppet.conf\n  # itself.\n  def record_deprecations_from_puppet_conf(puppet_conf)\n    puppet_conf.sections.values.each do |section|\n      section.settings.each do |conf_setting|\n        setting = self.setting(conf_setting.name)\n        if setting\n          @deprecated_settings_that_have_been_configured << setting if setting.deprecated?\n        end\n      end\n    end\n  end\n\n  def issue_deprecations\n    @deprecated_settings_that_have_been_configured.each do |setting|\n      issue_deprecation_warning(setting)\n    end\n  end\n\n  def issue_deprecation_warning(setting, msg = nil)\n    name = setting.name\n    ref = DEPRECATION_REFS.find { |params, _reference| params.include?(name) }\n    ref = ref[1] if ref\n    if msg\n      msg << \" #{ref}\" if ref\n      Puppet.deprecation_warning(msg)\n    elsif setting.completely_deprecated?\n      message = _(\"Setting %{name} is deprecated.\") % { name: name }\n      message += \" #{ref}\"\n      Puppet.deprecation_warning(message, \"setting-#{name}\")\n    elsif setting.allowed_on_commandline?\n      # TRANSLATORS 'puppet.conf' is a file name and should not be translated\n      message = _(\"Setting %{name} is deprecated in puppet.conf.\") % { name: name }\n      message += \" #{ref}\"\n      Puppet.deprecation_warning(message, \"puppet-conf-setting-#{name}\")\n    end\n  end\n\n  def add_environment_resources(catalog, sections)\n    configured_environment = self[:environment]\n\n    if configured_environment == \"production\" && !production_environment_exists?\n      environment_path = self[:environmentpath]\n      first_environment_path = environment_path.split(File::PATH_SEPARATOR).first\n\n      if Puppet::FileSystem.exist?(first_environment_path)\n        production_environment_path = File.join(first_environment_path, configured_environment)\n        parameters = { :ensure => 'directory' }\n        parameters[:mode] = '0750'\n        if Puppet.features.root?\n          parameters[:owner] = Puppet[:user] if service_user_available?\n          parameters[:group] = Puppet[:group] if service_group_available?\n        end\n        catalog.add_resource(Puppet::Resource.new(:file, production_environment_path, :parameters => parameters))\n      end\n    end\n  end\n\n  def production_environment_exists?\n    environment_path = self[:environmentpath]\n    paths = environment_path.split(File::PATH_SEPARATOR)\n\n    paths.any? do |path|\n      # If expected_path is a symlink, assume the source path is being managed\n      # elsewhere, so accept it also as a valid production environment path\n      expected_path = File.join(path, 'production')\n      Puppet::FileSystem.directory?(expected_path) || Puppet::FileSystem.symlink?(expected_path)\n    end\n  end\n\n  def add_user_resources(catalog, sections)\n    return unless Puppet.features.root?\n    return if Puppet::Util::Platform.windows?\n    return unless self[:mkusers]\n\n    @config.each do |_name, setting|\n      next unless setting.respond_to?(:owner)\n      next unless sections.nil? or sections.include?(setting.section)\n\n      user = setting.owner\n      if user && user != \"root\" && catalog.resource(:user, user).nil?\n        resource = Puppet::Resource.new(:user, user, :parameters => { :ensure => :present })\n        resource[:gid] = self[:group] if self[:group]\n        catalog.add_resource resource\n      end\n      group = setting.group\n      if group && !%w[root wheel].include?(group) && catalog.resource(:group, group).nil?\n        catalog.add_resource Puppet::Resource.new(:group, group, :parameters => { :ensure => :present })\n      end\n    end\n  end\n\n  # Yield each search source in turn.\n  def value_sets_for(environment, mode)\n    searchpath(environment, mode).filter_map { |source| searchpath_values(source) }\n  end\n\n  # Read the file in.\n  # @api private\n  def read_file(file)\n    Puppet::FileSystem.read(file, :encoding => 'utf-8')\n  end\n\n  # Private method for internal test use only; allows to do a comprehensive clear of all settings between tests.\n  #\n  # @return nil\n  def clear_everything_for_tests\n    unsafe_clear(true, true)\n    @configuration_file = nil\n    @global_defaults_initialized = false\n    @app_defaults_initialized = false\n  end\n  private :clear_everything_for_tests\n\n  def explicit_config_file?\n    # Figure out if the user has provided an explicit configuration file.  If\n    # so, return the path to the file, if not return nil.\n    #\n    # The easiest way to determine whether an explicit one has been specified\n    #  is to simply attempt to evaluate the value of \":config\".  This will\n    #  obviously be successful if they've passed an explicit value for :config,\n    #  but it will also result in successful interpolation if they've only\n    #  passed an explicit value for :confdir.\n    #\n    # If they've specified neither, then the interpolation will fail and we'll\n    #  get an exception.\n    #\n\n    true if self[:config]\n  rescue InterpolationError\n    # This means we failed to interpolate, which means that they didn't\n    #  explicitly specify either :config or :confdir... so we'll fall out to\n    #  the default value.\n    false\n  end\n  private :explicit_config_file?\n\n  # Lookup configuration setting value through a chain of different value sources.\n  #\n  # @api public\n  class ChainedValues\n    ENVIRONMENT_SETTING = \"environment\"\n    ENVIRONMENT_INTERPOLATION_ALLOWED = ['config_version'].freeze\n\n    # @see Puppet::Settings.values\n    # @api private\n    def initialize(mode, environment, value_sets, defaults)\n      @mode = mode\n      @environment = environment\n      @value_sets = value_sets\n      @defaults = defaults\n    end\n\n    # Lookup the uninterpolated value.\n    #\n    # @param name [Symbol] The configuration setting name to look up\n    # @return [Object] The configuration setting value or nil if the setting is not known\n    # @api public\n    def lookup(name)\n      set = @value_sets.find do |value_set|\n        value_set.include?(name)\n      end\n      if set\n        value = set.lookup(name)\n        unless value.nil?\n          return value\n        end\n      end\n\n      setting = @defaults[name]\n      if setting.respond_to?(:alias_name)\n        val = lookup(setting.alias_name)\n        return val if val\n      end\n\n      @defaults[name].default\n    end\n\n    # Lookup the interpolated value. All instances of `$name` in the value will\n    # be replaced by performing a lookup of `name` and substituting the text\n    # for `$name` in the original value. This interpolation is only performed\n    # if the looked up value is a String.\n    #\n    # @param name [Symbol] The configuration setting name to look up\n    # @return [Object] The configuration setting value or nil if the setting is not known\n    # @api public\n    def interpolate(name)\n      setting = @defaults[name]\n      return nil unless setting\n\n      lookup_and_convert(name) do |val|\n        setting.munge(val)\n      end\n    end\n\n    def print(name)\n      setting = @defaults[name]\n      return nil unless setting\n\n      lookup_and_convert(name) do |val|\n        setting.print(val)\n      end\n    end\n\n    private\n\n    def lookup_and_convert(name, &block)\n      val = lookup(name)\n      # if we interpolate code, all hell breaks loose.\n      if name == :code\n        val\n      else\n        # Convert it if necessary\n        begin\n          val = convert(val, name)\n        rescue InterpolationError => err\n          # This happens because we don't have access to the param name when the\n          # exception is originally raised, but we want it in the message\n          raise InterpolationError, _(\"Error converting value for param '%{name}': %{detail}\") % { name: name, detail: err }, err.backtrace\n        end\n\n        yield val\n      end\n    end\n\n    def convert(value, setting_name)\n      case value\n      when nil\n        nil\n      when String\n        failed_environment_interpolation = false\n        interpolated_value = value.gsub(/\\$(\\w+)|\\$\\{(\\w+)\\}/) do |expression|\n          varname = ::Regexp.last_match(2) || ::Regexp.last_match(1)\n          interpolated_expression =\n            if varname != ENVIRONMENT_SETTING || ok_to_interpolate_environment(setting_name)\n              if varname == ENVIRONMENT_SETTING && @environment\n                @environment\n              elsif varname == \"run_mode\"\n                @mode\n              elsif !(pval = interpolate(varname.to_sym)).nil?\n                pval\n              else\n                raise InterpolationError, _(\"Could not find value for %{expression}\") % { expression: expression }\n              end\n            else\n              failed_environment_interpolation = true\n              expression\n            end\n          interpolated_expression\n        end\n        if failed_environment_interpolation\n          # TRANSLATORS '$environment' is a Puppet specific variable and should not be translated\n          Puppet.warning(_(\"You cannot interpolate $environment within '%{setting_name}' when using directory environments.\") % { setting_name: setting_name } +\n                             ' ' + _(\"Its value will remain %{value}.\") % { value: interpolated_value })\n        end\n        interpolated_value\n      else\n        value\n      end\n    end\n\n    def ok_to_interpolate_environment(setting_name)\n      ENVIRONMENT_INTERPOLATION_ALLOWED.include?(setting_name.to_s)\n    end\n  end\n\n  class Values\n    extend Forwardable\n\n    attr_reader :name\n\n    def initialize(name, defaults)\n      @name = name\n      @values = {}\n      @defaults = defaults\n    end\n\n    def_delegator :@values, :include?\n    def_delegator :@values, :[], :lookup\n\n    def set(name, value)\n      default = @defaults[name]\n\n      unless default\n        raise ArgumentError, _(\"Attempt to assign a value to unknown setting %{name}\") % { name: name.inspect }\n      end\n\n      # This little exception-handling dance ensures that a hook is\n      # able to check whether a value for itself has been explicitly\n      # set, while still preserving the existing value if the hook\n      # throws (as was existing behavior)\n      old_value = @values[name]\n      @values[name] = value\n      begin\n        if default.has_hook?\n          default.handle(value)\n        end\n      rescue Exception => e\n        @values[name] = old_value\n        raise e\n      end\n    end\n\n    def inspect\n      %Q(<#{self.class}:#{object_id} @name=\"#{@name}\" @values=\"#{@values}\">)\n    end\n  end\n\n  class ValuesFromSection\n    attr_reader :name\n\n    def initialize(name, section)\n      @name = name\n      @section = section\n    end\n\n    def include?(name)\n      !@section.setting(name).nil?\n    end\n\n    def lookup(name)\n      setting = @section.setting(name)\n      if setting\n        setting.value\n      end\n    end\n\n    def inspect\n      %Q(<#{self.class}:#{object_id} @name=\"#{@name}\" @section=\"#{@section}\">)\n    end\n  end\n\n  # @api private\n  class ValuesFromEnvironmentConf\n    def initialize(environment_name)\n      @environment_name = environment_name\n    end\n\n    def name\n      @environment_name\n    end\n\n    def include?(name)\n      if Puppet::Settings::EnvironmentConf::VALID_SETTINGS.include?(name) && conf\n        return true\n      end\n\n      false\n    end\n\n    def lookup(name)\n      return nil unless Puppet::Settings::EnvironmentConf::VALID_SETTINGS.include?(name)\n\n      conf.send(name) if conf\n    end\n\n    def conf\n      unless @conf\n        environments = Puppet.lookup(:environments) { nil }\n        @conf = environments.get_conf(@environment_name) if environments\n      end\n      @conf\n    end\n\n    def inspect\n      %Q(<#{self.class}:#{object_id} @environment_name=\"#{@environment_name}\" @conf=\"#{@conf}\">)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl/openssl_loader'\nrequire_relative '../../puppet/ssl'\nrequire_relative '../../puppet/ssl/digest'\n\n# The base class for wrapping SSL instances.\nclass Puppet::SSL::Base\n  # For now, use the YAML separator.\n  SEPARATOR = \"\\n---\\n\"\n\n  # Only allow printing ascii characters, excluding /\n  VALID_CERTNAME = /\\A[ -.0-~]+\\Z/\n\n  def self.from_multiple_s(text)\n    text.split(SEPARATOR).collect { |inst| from_s(inst) }\n  end\n\n  def self.to_multiple_s(instances)\n    instances.collect(&:to_s).join(SEPARATOR)\n  end\n\n  def self.wraps(klass)\n    @wrapped_class = klass\n  end\n\n  def self.wrapped_class\n    raise(Puppet::DevError, _(\"%{name} has not declared what class it wraps\") % { name: self }) unless defined?(@wrapped_class)\n\n    @wrapped_class\n  end\n\n  def self.validate_certname(name)\n    raise _(\"Certname %{name} must not contain unprintable or non-ASCII characters\") % { name: name.inspect } unless name =~ VALID_CERTNAME\n  end\n\n  attr_accessor :name, :content\n\n  def generate\n    raise Puppet::DevError, _(\"%{class_name} did not override 'generate'\") % { class_name: self.class }\n  end\n\n  def initialize(name)\n    @name = name.to_s.downcase\n    self.class.validate_certname(@name)\n  end\n\n  ##\n  # name_from_subject extracts the common name attribute from the subject of an\n  # x.509 certificate certificate\n  #\n  # @api private\n  #\n  # @param [OpenSSL::X509::Name] subject The full subject (distinguished name) of the x.509\n  #   certificate.\n  #\n  # @return [String] the name (CN) extracted from the subject.\n  def self.name_from_subject(subject)\n    if subject.respond_to? :to_a\n      (subject.to_a.assoc('CN') || [])[1]\n    end\n  end\n\n  # Create an instance of our Puppet::SSL::* class using a given instance of the wrapped class\n  def self.from_instance(instance, name = nil)\n    unless instance.is_a?(wrapped_class)\n      raise ArgumentError, _(\"Object must be an instance of %{class_name}, %{actual_class} given\") %\n                           { class_name: wrapped_class, actual_class: instance.class }\n    end\n    if name.nil? and !instance.respond_to?(:subject)\n      raise ArgumentError, _(\"Name must be supplied if it cannot be determined from the instance\")\n    end\n\n    name ||= name_from_subject(instance.subject)\n    result = new(name)\n    result.content = instance\n    result\n  end\n\n  # Convert a string into an instance\n  def self.from_s(string, name = nil)\n    instance = wrapped_class.new(string)\n    from_instance(instance, name)\n  end\n\n  # Read content from disk appropriately.\n  def read(path)\n    # applies to Puppet::SSL::Certificate, Puppet::SSL::CertificateRequest\n    # nothing derives from Puppet::SSL::Certificate, but it is called by a number of other SSL Indirectors:\n    # Puppet::Indirector::CertificateStatus::File (.indirection.find)\n    # Puppet::Network::HTTP::WEBrick (.indirection.find)\n    # Puppet::Network::HTTP::RackREST (.from_instance)\n    # Puppet::Network::HTTP::WEBrickREST (.from_instance)\n    # Puppet::SSL::Inventory (.indirection.search, implements its own add / rebuild / serials with encoding UTF8)\n    @content = wrapped_class.new(Puppet::FileSystem.read(path, :encoding => Encoding::ASCII))\n  end\n\n  # Convert our thing to pem.\n  def to_s\n    return \"\" unless content\n\n    content.to_pem\n  end\n\n  def to_data_hash\n    to_s\n  end\n\n  # Provide the full text of the thing we're dealing with.\n  def to_text\n    return \"\" unless content\n\n    content.to_text\n  end\n\n  def fingerprint(md = :SHA256)\n    mds = md.to_s.upcase\n    digest(mds).to_hex\n  end\n\n  def digest(algorithm = nil)\n    algorithm ||= digest_algorithm\n\n    Puppet::SSL::Digest.new(algorithm, content.to_der)\n  end\n\n  def digest_algorithm\n    # The signature_algorithm on the X509 cert is a combination of the digest\n    # algorithm and the encryption algorithm\n    # e.g. md5WithRSAEncryption, sha256WithRSAEncryption\n    # Unfortunately there isn't a consistent pattern\n    # See RFCs 3279, 5758\n    digest_re = Regexp.union(\n      /ripemd160/i,\n      /md[245]/i,\n      /sha\\d*/i\n    )\n    ln = content.signature_algorithm\n    match = digest_re.match(ln)\n    if match\n      match[0].downcase\n    else\n      raise Puppet::Error, _(\"Unknown signature algorithm '%{ln}'\") % { ln: ln }\n    end\n  end\n\n  private\n\n  def wrapped_class\n    self.class.wrapped_class\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/certificate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl/base'\n\n# Manage certificates themselves.  This class has no\n# 'generate' method because the CA is responsible\n# for turning CSRs into certificates; we can only\n# retrieve them from the CA (or not, as is often\n# the case).\n#\n# @deprecated Use {Puppet::SSL::SSLProvider} instead.\nclass Puppet::SSL::Certificate < Puppet::SSL::Base\n  # This is defined from the base class\n  wraps OpenSSL::X509::Certificate\n\n  # Because of how the format handler class is included, this\n  # can't be in the base class.\n  def self.supported_formats\n    [:s]\n  end\n\n  def self.subject_alt_names_for(cert)\n    alts = cert.extensions.find { |ext| ext.oid == \"subjectAltName\" }\n    return [] unless alts\n\n    alts.value.split(/\\s*,\\s*/)\n  end\n\n  def subject_alt_names\n    self.class.subject_alt_names_for(content)\n  end\n\n  def expiration\n    return nil unless content\n\n    content.not_after\n  end\n\n  # This name is what gets extracted from the subject before being passed\n  # to the constructor, so it's not downcased\n  def unmunged_name\n    self.class.name_from_subject(content.subject.to_utf8)\n  end\n\n  # Any extensions registered with custom OIDs as defined in module\n  # Puppet::SSL::Oids may be looked up here.\n  #\n  # A cert with a 'pp_uuid' extension having the value 'abcd' would return:\n  #\n  # [{ 'oid' => 'pp_uuid', 'value' => 'abcd'}]\n  #\n  # @return [Array<Hash{String => String}>] An array of two element hashes,\n  # with key/value pairs for the extension's oid, and its value.\n  def custom_extensions\n    custom_exts = content.extensions.select do |ext|\n      Puppet::SSL::Oids.subtree_of?('ppRegCertExt', ext.oid) or\n        Puppet::SSL::Oids.subtree_of?('ppPrivCertExt', ext.oid) or\n        Puppet::SSL::Oids.subtree_of?('ppAuthCertExt', ext.oid)\n    end\n\n    custom_exts.map do |ext|\n      { 'oid' => ext.oid, 'value' => get_ext_val(ext.oid) }\n    end\n  end\n\n  private\n\n  # Extract the extensions sequence from the wrapped certificate's raw ASN.1 form\n  def exts_seq\n    # See RFC-2459 section 4.1 (https://tools.ietf.org/html/rfc2459#section-4.1)\n    # to see where this is defined. Essentially this is saying \"in the first\n    # sequence in the certificate, find the item that's tagged with 3. This\n    # is where the extensions are stored.\"\n    @extensions_tag ||= 3\n\n    @exts_seq ||= OpenSSL::ASN1.decode(content.to_der).value[0].value.find do |data|\n      (data.tag == @extensions_tag) && (data.tag_class == :CONTEXT_SPECIFIC)\n    end.value[0]\n  end\n\n  # Get the DER parsed value of an X.509 extension by it's OID, or short name\n  # if one has been registered with OpenSSL.\n  def get_ext_val(oid)\n    ext_obj = exts_seq.value.find do |ext_seq|\n      ext_seq.value[0].value == oid\n    end\n\n    raw_val = ext_obj.value.last.value\n\n    begin\n      OpenSSL::ASN1.decode(raw_val).value\n    rescue OpenSSL::ASN1::ASN1Error\n      # This is required to maintain backward compatibility with the previous\n      # way trusted facts were signed. See PUP-3560\n      raw_val\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/certificate_request.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl/base'\nrequire_relative '../../puppet/ssl/certificate_signer'\n\n# This class creates and manages X509 certificate signing requests.\n#\n# ## CSR attributes\n#\n# CSRs may contain a set of attributes that includes supplementary information\n# about the CSR or information for the signed certificate.\n#\n# PKCS#9/RFC 2985 section 5.4 formally defines the \"Challenge password\",\n# \"Extension request\", and \"Extended-certificate attributes\", but this\n# implementation only handles the \"Extension request\" attribute. Other\n# attributes may be defined on a CSR, but the RFC doesn't define behavior for\n# any other attributes so we treat them as only informational.\n#\n# ## CSR Extension request attribute\n#\n# CSRs may contain an optional set of extension requests, which allow CSRs to\n# include additional information that may be included in the signed\n# certificate. Any additional information that should be copied from the CSR\n# to the signed certificate MUST be included in this attribute.\n#\n# This behavior is dictated by PKCS#9/RFC 2985 section 5.4.2.\n#\n# @see https://tools.ietf.org/html/rfc2985 \"RFC 2985 Section 5.4.2 Extension request\"\n#\nclass Puppet::SSL::CertificateRequest < Puppet::SSL::Base\n  wraps OpenSSL::X509::Request\n\n  # Because of how the format handler class is included, this\n  # can't be in the base class.\n  def self.supported_formats\n    [:s]\n  end\n\n  def extension_factory\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @ef ||= OpenSSL::X509::ExtensionFactory.new\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  # Create a certificate request with our system settings.\n  #\n  # @param key [OpenSSL::X509::Key] The private key associated with this CSR.\n  # @param options [Hash]\n  # @option options [String] :dns_alt_names A comma separated list of\n  #   Subject Alternative Names to include in the CSR extension request.\n  # @option options [Hash<String, String, Array<String>>] :csr_attributes A hash\n  #   of OIDs and values that are either a string or array of strings.\n  # @option options [Array<String, String>] :extension_requests A hash of\n  #   certificate extensions to add to the CSR extReq attribute, excluding\n  #   the Subject Alternative Names extension.\n  #\n  # @raise [Puppet::Error] If the generated CSR signature couldn't be verified\n  #\n  # @return [OpenSSL::X509::Request] The generated CSR\n  def generate(key, options = {})\n    Puppet.info _(\"Creating a new SSL certificate request for %{name}\") % { name: name }\n\n    # If we're a CSR for the CA, then use the real ca_name, rather than the\n    # fake 'ca' name.  This is mostly for backward compatibility with 0.24.x,\n    # but it's also just a good idea.\n    common_name = name == Puppet::SSL::CA_NAME ? Puppet.settings[:ca_name] : name\n\n    csr = OpenSSL::X509::Request.new\n    csr.version = 0\n    csr.subject = OpenSSL::X509::Name.new([[\"CN\", common_name]])\n\n    csr.public_key = if key.is_a?(OpenSSL::PKey::EC)\n                       # EC#public_key doesn't follow the PKey API,\n                       # see https://github.com/ruby/openssl/issues/29\n                       key\n                     else\n                       key.public_key\n                     end\n\n    if options[:csr_attributes]\n      add_csr_attributes(csr, options[:csr_attributes])\n    end\n\n    if (ext_req_attribute = extension_request_attribute(options))\n      csr.add_attribute(ext_req_attribute)\n    end\n\n    signer = Puppet::SSL::CertificateSigner.new\n    signer.sign(csr, key)\n\n    raise Puppet::Error, _(\"CSR sign verification failed; you need to clean the certificate request for %{name} on the server\") % { name: name } unless csr.verify(csr.public_key)\n\n    @content = csr\n\n    # we won't be able to get the digest on jruby\n    if @content.signature_algorithm\n      Puppet.info _(\"Certificate Request fingerprint (%{digest}): %{hex_digest}\") % { digest: digest.name, hex_digest: digest.to_hex }\n    end\n    @content\n  end\n\n  def ext_value_to_ruby_value(asn1_arr)\n    # A list of ASN1 types than can't be directly converted to a Ruby type\n    @non_convertible ||= [OpenSSL::ASN1::EndOfContent,\n                          OpenSSL::ASN1::BitString,\n                          OpenSSL::ASN1::Null,\n                          OpenSSL::ASN1::Enumerated,\n                          OpenSSL::ASN1::UTCTime,\n                          OpenSSL::ASN1::GeneralizedTime,\n                          OpenSSL::ASN1::Sequence,\n                          OpenSSL::ASN1::Set]\n\n    begin\n      # Attempt to decode the extension's DER data located in the original OctetString\n      asn1_val = OpenSSL::ASN1.decode(asn1_arr.last.value)\n    rescue OpenSSL::ASN1::ASN1Error\n      # This is to allow supporting the old-style of not DER encoding trusted facts\n      return asn1_arr.last.value\n    end\n\n    # If the extension value can not be directly converted to an atomic Ruby\n    # type, use the original ASN1 value. This is needed to work around a bug\n    # in Ruby's OpenSSL library which doesn't convert the value of unknown\n    # extension OIDs properly. See PUP-3560\n    if @non_convertible.include?(asn1_val.class) then\n      # Allows OpenSSL to take the ASN1 value and turn it into something Ruby understands\n      OpenSSL::X509::Extension.new(asn1_arr.first.value, asn1_val.to_der).value\n    else\n      asn1_val.value\n    end\n  end\n\n  # Return the set of extensions requested on this CSR, in a form designed to\n  # be useful to Ruby: an array of hashes.  Which, not coincidentally, you can pass\n  # successfully to the OpenSSL constructor later, if you want.\n  #\n  # @return [Array<Hash{String => String}>] An array of two or three element\n  # hashes, with key/value pairs for the extension's oid, its value, and\n  # optionally its critical state.\n  def request_extensions\n    raise Puppet::Error, _(\"CSR needs content to extract fields\") unless @content\n\n    # Prefer the standard extReq, but accept the Microsoft specific version as\n    # a fallback, if the standard version isn't found.\n    attribute   = @content.attributes.find { |x| x.oid == \"extReq\" }\n    attribute ||= @content.attributes.find { |x| x.oid == \"msExtReq\" }\n    return [] unless attribute\n\n    extensions = unpack_extension_request(attribute)\n\n    index = -1\n    extensions.map do |ext_values|\n      index += 1\n\n      value = ext_value_to_ruby_value(ext_values)\n\n      # OK, turn that into an extension, to unpack the content.  Lovely that\n      # we have to swap the order of arguments to the underlying method, or\n      # perhaps that the ASN.1 representation chose to pack them in a\n      # strange order where the optional component comes *earlier* than the\n      # fixed component in the sequence.\n      case ext_values.length\n      when 2\n        { \"oid\" => ext_values[0].value, \"value\" => value }\n      when 3\n        { \"oid\" => ext_values[0].value, \"value\" => value, \"critical\" => ext_values[1].value }\n      else\n        raise Puppet::Error, _(\"In %{attr}, expected extension record %{index} to have two or three items, but found %{count}\") % { attr: attribute.oid, index: index, count: ext_values.length }\n      end\n    end\n  end\n\n  def subject_alt_names\n    @subject_alt_names ||= request_extensions\n                           .select { |x| x[\"oid\"] == \"subjectAltName\" }\n                           .map { |x| x[\"value\"].split(/\\s*,\\s*/) }\n                           .flatten\n                           .sort\n                           .uniq\n  end\n\n  # Return all user specified attributes attached to this CSR as a hash. IF an\n  # OID has a single value it is returned as a string, otherwise all values are\n  # returned as an array.\n  #\n  # The format of CSR attributes is specified in PKCS#10/RFC 2986\n  #\n  # @see https://tools.ietf.org/html/rfc2986 \"RFC 2986 Certification Request Syntax Specification\"\n  #\n  # @api public\n  #\n  # @return [Hash<String, String>]\n  def custom_attributes\n    x509_attributes = @content.attributes.reject do |attr|\n      PRIVATE_CSR_ATTRIBUTES.include? attr.oid\n    end\n\n    x509_attributes.map do |attr|\n      { \"oid\" => attr.oid, \"value\" => attr.value.value.first.value }\n    end\n  end\n\n  private\n\n  # Exclude OIDs that may conflict with how Puppet creates CSRs.\n  #\n  # We only have nominal support for Microsoft extension requests, but since we\n  # ultimately respect that field when looking for extension requests in a CSR\n  # we need to prevent that field from being written to directly.\n  PRIVATE_CSR_ATTRIBUTES = [\n    'extReq',   '1.2.840.113549.1.9.14',\n    'msExtReq', '1.3.6.1.4.1.311.2.1.14'\n  ]\n\n  def add_csr_attributes(csr, csr_attributes)\n    csr_attributes.each do |oid, value|\n      if PRIVATE_CSR_ATTRIBUTES.include? oid\n        raise ArgumentError, _(\"Cannot specify CSR attribute %{oid}: conflicts with internally used CSR attribute\") % { oid: oid }\n      end\n\n      encoded = OpenSSL::ASN1::PrintableString.new(value.to_s)\n\n      attr_set = OpenSSL::ASN1::Set.new([encoded])\n      csr.add_attribute(OpenSSL::X509::Attribute.new(oid, attr_set))\n      Puppet.debug(\"Added csr attribute: #{oid} => #{attr_set.inspect}\")\n    rescue OpenSSL::X509::AttributeError => e\n      raise Puppet::Error, _(\"Cannot create CSR with attribute %{oid}: %{message}\") % { oid: oid, message: e.message }, e.backtrace\n    end\n  end\n\n  PRIVATE_EXTENSIONS = [\n    'subjectAltName', '2.5.29.17'\n  ]\n\n  # @api private\n  def extension_request_attribute(options)\n    extensions = []\n\n    if options[:extension_requests]\n      options[:extension_requests].each_pair do |oid, value|\n        if PRIVATE_EXTENSIONS.include? oid\n          raise Puppet::Error, _(\"Cannot specify CSR extension request %{oid}: conflicts with internally used extension request\") % { oid: oid }\n        end\n\n        ext = OpenSSL::X509::Extension.new(oid, OpenSSL::ASN1::UTF8String.new(value.to_s).to_der, false)\n        extensions << ext\n      rescue OpenSSL::X509::ExtensionError => e\n        raise Puppet::Error, _(\"Cannot create CSR with extension request %{oid}: %{message}\") % { oid: oid, message: e.message }, e.backtrace\n      end\n    end\n\n    if options[:dns_alt_names]\n      raw_names = options[:dns_alt_names].split(/\\s*,\\s*/).map(&:strip) + [name]\n\n      parsed_names = raw_names.map do |name|\n        if !name.start_with?(\"IP:\") && !name.start_with?(\"DNS:\")\n          \"DNS:#{name}\"\n        else\n          name\n        end\n      end.sort.uniq.join(\", \")\n\n      alt_names_ext = extension_factory.create_extension(\"subjectAltName\", parsed_names, false)\n\n      extensions << alt_names_ext\n    end\n\n    unless extensions.empty?\n      seq = OpenSSL::ASN1::Sequence(extensions)\n      ext_req = OpenSSL::ASN1::Set([seq])\n      OpenSSL::X509::Attribute.new(\"extReq\", ext_req)\n    end\n  end\n\n  # Unpack the extReq attribute into an array of Extensions.\n  #\n  # The extension request attribute is structured like\n  # `Set[Sequence[Extensions]]` where the outer Set only contains a single\n  # sequence.\n  #\n  # In addition the Ruby implementation of ASN1 requires that all ASN1 values\n  # contain a single value, so Sets and Sequence have to contain an array\n  # that in turn holds the elements. This is why we have to unpack an array\n  # every time we unpack a Set/Seq.\n  #\n  # @see https://tools.ietf.org/html/rfc2985#ref-10 5.4.2 CSR Extension Request structure\n  # @see https://tools.ietf.org/html/rfc5280 4.1 Certificate Extension structure\n  #\n  # @api private\n  #\n  # @param attribute [OpenSSL::X509::Attribute] The X509 extension request\n  #\n  # @return [Array<Array<Object>>] A array of arrays containing the extension\n  #   OID the critical state if present, and the extension value.\n  def unpack_extension_request(attribute)\n    unless attribute.value.is_a? OpenSSL::ASN1::Set\n      raise Puppet::Error, _(\"In %{attr}, expected Set but found %{klass}\") % { attr: attribute.oid, klass: attribute.value.class }\n    end\n\n    unless attribute.value.value.is_a? Array\n      raise Puppet::Error, _(\"In %{attr}, expected Set[Array] but found %{klass}\") % { attr: attribute.oid, klass: attribute.value.value.class }\n    end\n\n    unless attribute.value.value.size == 1\n      raise Puppet::Error, _(\"In %{attr}, expected Set[Array] with one value but found %{count} elements\") % { attr: attribute.oid, count: attribute.value.value.size }\n    end\n\n    unless attribute.value.value.first.is_a? OpenSSL::ASN1::Sequence\n      raise Puppet::Error, _(\"In %{attr}, expected Set[Array[Sequence[...]]], but found %{klass}\") % { attr: attribute.oid, klass: extension.class }\n    end\n\n    unless attribute.value.value.first.value.is_a? Array\n      raise Puppet::Error, _(\"In %{attr}, expected Set[Array[Sequence[Array[...]]]], but found %{klass}\") % { attr: attribute.oid, klass: extension.value.class }\n    end\n\n    extensions = attribute.value.value.first.value\n\n    extensions.map(&:value)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/certificate_request_attributes.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl'\nrequire_relative '../../puppet/util/yaml'\n\n# This class transforms simple key/value pairs into the equivalent ASN1\n# structures. Values may be strings or arrays of strings.\n#\n# @api private\nclass Puppet::SSL::CertificateRequestAttributes\n  attr_reader :path, :custom_attributes, :extension_requests\n\n  def initialize(path)\n    @path = path\n    @custom_attributes = {}\n    @extension_requests = {}\n  end\n\n  # Attempt to load a yaml file at the given @path.\n  # @return true if we are able to load the file, false otherwise\n  # @raise [Puppet::Error] if there are unexpected attribute keys\n  def load\n    Puppet.info(_(\"csr_attributes file loading from %{path}\") % { path: path })\n    if Puppet::FileSystem.exist?(path)\n      hash = Puppet::Util::Yaml.safe_load_file(path, [Symbol]) || {}\n      unless hash.is_a?(Hash)\n        raise Puppet::Error, _(\"invalid CSR attributes, expected instance of Hash, received instance of %{klass}\") % { klass: hash.class }\n      end\n\n      @custom_attributes = hash.delete('custom_attributes') || {}\n      @extension_requests = hash.delete('extension_requests') || {}\n      unless hash.keys.empty?\n        raise Puppet::Error, _(\"unexpected attributes %{keys} in %{path}\") % { keys: hash.keys.inspect, path: @path.inspect }\n      end\n\n      return true\n    end\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/certificate_signer.rb",
    "content": "# frozen_string_literal: true\n\n# Take care of signing a certificate in a FIPS 140-2 compliant manner.\n#\n# @see https://projects.puppetlabs.com/issues/17295\n#\n# @api private\nclass Puppet::SSL::CertificateSigner\n  # @!attribute [r] digest\n  #   @return [OpenSSL::Digest]\n  attr_reader :digest\n\n  def initialize\n    if OpenSSL::Digest.const_defined?('SHA256')\n      @digest = OpenSSL::Digest::SHA256\n    elsif OpenSSL::Digest.const_defined?('SHA1')\n      @digest = OpenSSL::Digest::SHA1\n    elsif OpenSSL::Digest.const_defined?('SHA512')\n      @digest = OpenSSL::Digest::SHA512\n    elsif OpenSSL::Digest.const_defined?('SHA384')\n      @digest = OpenSSL::Digest::SHA384\n    elsif OpenSSL::Digest.const_defined?('SHA224')\n      @digest = OpenSSL::Digest::SHA224\n    else\n      raise Puppet::Error,\n            \"No FIPS 140-2 compliant digest algorithm in OpenSSL::Digest\"\n    end\n  end\n\n  # Sign a certificate signing request (CSR) with a private key.\n  #\n  # @param [OpenSSL::X509::Request] content The CSR to sign\n  # @param [OpenSSL::X509::PKey] key The private key to sign with\n  #\n  # @api private\n  def sign(content, key)\n    content.sign(key, @digest.new)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/digest.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::SSL::Digest\n  attr_reader :digest\n\n  def initialize(algorithm, content)\n    algorithm ||= 'SHA256'\n    @digest = OpenSSL::Digest.new(algorithm, content)\n  end\n\n  def to_s\n    \"(#{name}) #{to_hex}\"\n  end\n\n  def to_hex\n    @digest.hexdigest.scan(/../).join(':').upcase\n  end\n\n  def name\n    @digest.name.upcase\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/error.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::SSL\n  class SSLError < Puppet::Error; end\n\n  class CertVerifyError < Puppet::SSL::SSLError\n    attr_reader :code, :cert\n\n    def initialize(message, code, cert)\n      super(message)\n      @code = code\n      @cert = cert\n    end\n  end\n\n  class CertMismatchError < Puppet::SSL::SSLError\n    def initialize(peer_cert, host)\n      valid_certnames = [peer_cert.subject.to_utf8.sub(/.*=/, ''),\n                         *Puppet::SSL::Certificate.subject_alt_names_for(peer_cert)].uniq\n      if valid_certnames.size > 1\n        expected_certnames = _(\"expected one of %{certnames}\") % { certnames: valid_certnames.join(', ') }\n      else\n        expected_certnames = _(\"expected %{certname}\") % { certname: valid_certnames.first }\n      end\n\n      super(_(\"Server hostname '%{host}' did not match server certificate; %{expected_certnames}\") % { host: host, expected_certnames: expected_certnames })\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/oids.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl'\n\n# This module defines OIDs for use within Puppet.\n#\n# # ASN.1 Definition\n#\n# The following is the formal definition of OIDs specified in this file.\n#\n# ```\n# puppetCertExtensions OBJECT IDENTIFIER ::= {iso(1) identified-organization(3)\n#    dod(6) internet(1) private(4) enterprise(1) 34380 1}\n#\n# -- the tree under registeredExtensions 'belongs' to puppetlabs\n# -- privateExtensions can be extended by enterprises to suit their own needs\n# registeredExtensions OBJECT IDENTIFIER ::= { puppetCertExtensions 1 }\n# privateExtensions OBJECT IDENTIFIER ::= { puppetCertExtensions 2 }\n# authorizationExtensions OBJECT IDENTIFIER ::= { puppetCertExtensions 3 }\n#\n# -- subtree of common registered extensions\n# -- The short names for these OIDs are intentionally lowercased and formatted\n# -- since they may be exposed inside the Puppet DSL as variables.\n# pp_uuid  OBJECT IDENTIFIER ::= { registeredExtensions 1 }\n# pp_instance_id OBJECT IDENTIFIER ::= { registeredExtensions 2 }\n# pp_image_name OBJECT IDENTIFIER ::= { registeredExtensions 3 }\n# pp_preshared_key OBJECT IDENTIFIER ::= { registeredExtensions 4 }\n# ```\n#\n# @api private\nmodule Puppet::SSL::Oids\n  # Note: When updating the following OIDs make sure to also update the OID\n  # definitions here:\n  # https://github.com/puppetlabs/puppetserver/blob/master/src/clj/puppetlabs/puppetserver/certificate_authority.clj#L122-L159\n\n  PUPPET_OIDS = [\n    [\"1.3.6.1.4.1.34380\", 'puppetlabs', 'Puppet Labs'],\n    [\"1.3.6.1.4.1.34380.1\", 'ppCertExt', 'Puppet Certificate Extension'],\n\n    [\"1.3.6.1.4.1.34380.1.1\", 'ppRegCertExt', 'Puppet Registered Certificate Extension'],\n\n    [\"1.3.6.1.4.1.34380.1.1.1\", 'pp_uuid', 'Puppet Node UUID'],\n    [\"1.3.6.1.4.1.34380.1.1.2\", 'pp_instance_id', 'Puppet Node Instance ID'],\n    [\"1.3.6.1.4.1.34380.1.1.3\", 'pp_image_name', 'Puppet Node Image Name'],\n    [\"1.3.6.1.4.1.34380.1.1.4\", 'pp_preshared_key', 'Puppet Node Preshared Key'],\n    [\"1.3.6.1.4.1.34380.1.1.5\", 'pp_cost_center', 'Puppet Node Cost Center Name'],\n    [\"1.3.6.1.4.1.34380.1.1.6\", 'pp_product', 'Puppet Node Product Name'],\n    [\"1.3.6.1.4.1.34380.1.1.7\", 'pp_project', 'Puppet Node Project Name'],\n    [\"1.3.6.1.4.1.34380.1.1.8\", 'pp_application', 'Puppet Node Application Name'],\n    [\"1.3.6.1.4.1.34380.1.1.9\", 'pp_service', 'Puppet Node Service Name'],\n    [\"1.3.6.1.4.1.34380.1.1.10\", 'pp_employee', 'Puppet Node Employee Name'],\n    [\"1.3.6.1.4.1.34380.1.1.11\", 'pp_created_by', 'Puppet Node created_by Tag'],\n    [\"1.3.6.1.4.1.34380.1.1.12\", 'pp_environment', 'Puppet Node Environment Name'],\n    [\"1.3.6.1.4.1.34380.1.1.13\", 'pp_role', 'Puppet Node Role Name'],\n    [\"1.3.6.1.4.1.34380.1.1.14\", 'pp_software_version', 'Puppet Node Software Version'],\n    [\"1.3.6.1.4.1.34380.1.1.15\", 'pp_department', 'Puppet Node Department Name'],\n    [\"1.3.6.1.4.1.34380.1.1.16\", 'pp_cluster', 'Puppet Node Cluster Name'],\n    [\"1.3.6.1.4.1.34380.1.1.17\", 'pp_provisioner', 'Puppet Node Provisioner Name'],\n    [\"1.3.6.1.4.1.34380.1.1.18\", 'pp_region', 'Puppet Node Region Name'],\n    [\"1.3.6.1.4.1.34380.1.1.19\", 'pp_datacenter', 'Puppet Node Datacenter Name'],\n    [\"1.3.6.1.4.1.34380.1.1.20\", 'pp_zone', 'Puppet Node Zone Name'],\n    [\"1.3.6.1.4.1.34380.1.1.21\", 'pp_network', 'Puppet Node Network Name'],\n    [\"1.3.6.1.4.1.34380.1.1.22\", 'pp_securitypolicy', 'Puppet Node Security Policy Name'],\n    [\"1.3.6.1.4.1.34380.1.1.23\", 'pp_cloudplatform', 'Puppet Node Cloud Platform Name'],\n    [\"1.3.6.1.4.1.34380.1.1.24\", 'pp_apptier', 'Puppet Node Application Tier'],\n    [\"1.3.6.1.4.1.34380.1.1.25\", 'pp_hostname', 'Puppet Node Hostname'],\n    [\"1.3.6.1.4.1.34380.1.1.26\", 'pp_owner', 'Puppet Node Owner'],\n\n    [\"1.3.6.1.4.1.34380.1.2\", 'ppPrivCertExt', 'Puppet Private Certificate Extension'],\n\n    [\"1.3.6.1.4.1.34380.1.3\", 'ppAuthCertExt', 'Puppet Certificate Authorization Extension'],\n\n    [\"1.3.6.1.4.1.34380.1.3.1\", 'pp_authorization', 'Certificate Extension Authorization'],\n    [\"1.3.6.1.4.1.34380.1.3.2\", 'pp_auth_auto_renew', 'Auto-Renew Certificate Attribute'],\n    [\"1.3.6.1.4.1.34380.1.3.13\", 'pp_auth_role', 'Puppet Node Role Name for Authorization'],\n    [\"1.3.6.1.4.1.34380.1.3.39\", 'pp_cli_auth', 'Puppetserver CA CLI Authorization']\n  ]\n\n  @did_register_puppet_oids = false\n\n  # Register our custom Puppet OIDs with OpenSSL so they can be used as CSR\n  # extensions. Without registering these OIDs, OpenSSL will fail when it\n  # encounters such an extension in a CSR.\n  def self.register_puppet_oids\n    unless @did_register_puppet_oids\n      PUPPET_OIDS.each do |oid_defn|\n        OpenSSL::ASN1::ObjectId.register(*oid_defn)\n      end\n\n      @did_register_puppet_oids = true\n    end\n  end\n\n  # Parse custom OID mapping file that enables custom OIDs to be resolved\n  # into user-friendly names.\n  #\n  # @param custom_oid_file [String] File to obtain custom OIDs mapping from\n  # @param map_key [String] Hash key in which custom OIDs mapping is stored\n  #\n  # @example Custom OID mapping file\n  # ---\n  # oid_mapping:\n  #  '1.3.6.1.4.1.34380.1.2.1.1':\n  #    shortname : 'myshortname'\n  #    longname  : 'Long name'\n  #  '1.3.6.1.4.1.34380.1.2.1.2':\n  #    shortname: 'myothershortname'\n  #    longname: 'Other Long name'\n  def self.parse_custom_oid_file(custom_oid_file, map_key = 'oid_mapping')\n    if File.exist?(custom_oid_file) && File.readable?(custom_oid_file)\n      mapping = nil\n      begin\n        mapping = Puppet::Util::Yaml.safe_load_file(custom_oid_file, [Symbol])\n      rescue => err\n        raise Puppet::Error, _(\"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': %{err}\") % { custom_oid_file: custom_oid_file, err: err }, err.backtrace\n      end\n\n      unless mapping.has_key?(map_key)\n        raise Puppet::Error, _(\"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': no such index '%{map_key}'\") % { custom_oid_file: custom_oid_file, map_key: map_key }\n      end\n\n      unless mapping[map_key].is_a?(Hash)\n        raise Puppet::Error, _(\"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': data under index '%{map_key}' must be a Hash\") % { custom_oid_file: custom_oid_file, map_key: map_key }\n      end\n\n      oid_defns = []\n      mapping[map_key].keys.each do |oid|\n        shortname, longname = mapping[map_key][oid].values_at(\"shortname\", \"longname\")\n        if shortname.nil? || longname.nil?\n          raise Puppet::Error, _(\"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': incomplete definition of oid '%{oid}'\") % { custom_oid_file: custom_oid_file, oid: oid }\n        end\n\n        oid_defns << [oid, shortname, longname]\n      end\n\n      oid_defns\n    end\n  end\n\n  # Load custom OID mapping file that enables custom OIDs to be resolved\n  # into user-friendly names.\n  #\n  # @param custom_oid_file [String] File to obtain custom OIDs mapping from\n  # @param map_key [String] Hash key in which custom OIDs mapping is stored\n  #\n  # @example Custom OID mapping file\n  # ---\n  # oid_mapping:\n  #  '1.3.6.1.4.1.34380.1.2.1.1':\n  #    shortname : 'myshortname'\n  #    longname  : 'Long name'\n  #  '1.3.6.1.4.1.34380.1.2.1.2':\n  #    shortname: 'myothershortname'\n  #    longname: 'Other Long name'\n  def self.load_custom_oid_file(custom_oid_file, map_key = 'oid_mapping')\n    oid_defns = parse_custom_oid_file(custom_oid_file, map_key)\n    unless oid_defns.nil?\n      begin\n        oid_defns.each do |oid_defn|\n          OpenSSL::ASN1::ObjectId.register(*oid_defn)\n        end\n      rescue => err\n        raise ArgumentError, _(\"Error registering ssl custom OIDs mapping from file '%{custom_oid_file}': %{err}\") % { custom_oid_file: custom_oid_file, err: err }, err.backtrace\n      end\n    end\n  end\n\n  # Determine if the first OID contains the second OID\n  #\n  # @param first [String] The containing OID, in dotted form or as the short name\n  # @param second [String] The contained OID, in dotted form or as the short name\n  # @param exclusive [true, false] If an OID should not be considered as a subtree of itself\n  #\n  # @example Comparing two dotted OIDs\n  #   Puppet::SSL::Oids.subtree_of?('1.3.6.1', '1.3.6.1.4.1') #=> true\n  #   Puppet::SSL::Oids.subtree_of?('1.3.6.1', '1.3.6') #=> false\n  #\n  # @example Comparing an OID short name with a dotted OID\n  #   Puppet::SSL::Oids.subtree_of?('IANA', '1.3.6.1.4.1') #=> true\n  #   Puppet::SSL::Oids.subtree_of?('1.3.6.1', 'enterprises') #=> true\n  #\n  # @example Comparing an OID against itself\n  #   Puppet::SSL::Oids.subtree_of?('IANA', 'IANA') #=> true\n  #   Puppet::SSL::Oids.subtree_of?('IANA', 'IANA', true) #=> false\n  #\n  # @return [true, false]\n  def self.subtree_of?(first, second, exclusive = false)\n    first_oid = OpenSSL::ASN1::ObjectId.new(first).oid\n    second_oid = OpenSSL::ASN1::ObjectId.new(second).oid\n\n    if exclusive and first_oid == second_oid\n      false\n    else\n      second_oid.index(first_oid) == 0\n    end\n  rescue OpenSSL::ASN1::ASN1Error, TypeError\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/openssl_loader.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/platform'\n\n# This file should be required instead of writing `require 'openssl'`\n# or any library that loads openssl like `net/https`. This allows the\n# core Puppet code to load correctly in JRuby environments that do not\n# have a functioning openssl (eg a FIPS enabled one).\n\nif Puppet::Util::Platform.jruby_fips?\n  # Even in JRuby we need to define the constants that are wrapped in\n  # Indirections: Puppet::SSL::{Key, Certificate, CertificateRequest}\n  module OpenSSL\n    module PKey\n      class RSA; end\n    end\n\n    module X509\n      class Request; end\n      class Certificate; end\n    end\n  end\nelse\n  require 'openssl'\n  require 'net/https'\nend\n"
  },
  {
    "path": "lib/puppet/ssl/ssl_context.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl'\n\nmodule Puppet::SSL\n  # The `keyword_init: true` option is no longer needed in Ruby >= 3.2\n  SSLContext = Struct.new(\n    :store,\n    :cacerts,\n    :crls,\n    :private_key,\n    :client_cert,\n    :client_chain,\n    :revocation,\n    :verify_peer,\n    keyword_init: true\n  ) do\n    def initialize(*)\n      super\n      self[:cacerts] ||= []\n      self[:crls] ||= []\n      self[:client_chain] ||= []\n      self[:revocation] = true if self[:revocation].nil?\n      self[:verify_peer] = true if self[:verify_peer].nil?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/ssl_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl'\n\n# SSL Provider creates `SSLContext` objects that can be used to create\n# secure connections.\n#\n# @example To load an SSLContext from an existing private key and related certs/crls:\n#   ssl_context = provider.load_context\n#\n# @example To load an SSLContext from an existing password-protected private key and related certs/crls:\n#   ssl_context = provider.load_context(password: 'opensesame')\n#\n# @example To create an SSLContext from in-memory certs and keys:\n#   cacerts = [<OpenSSL::X509::Certificate>]\n#   crls = [<OpenSSL::X509::CRL>]\n#   key = <OpenSSL::X509::PKey>\n#   cert = <OpenSSL::X509::Certificate>\n#   ssl_context = provider.create_context(cacerts: cacerts, crls: crls, private_key: key, client_cert: cert)\n#\n# @example To create an SSLContext to connect to non-puppet HTTPS servers:\n#   cacerts = [<OpenSSL::X509::Certificate>]\n#   ssl_context = provider.create_root_context(cacerts: cacerts)\n#\n# @api private\nclass Puppet::SSL::SSLProvider\n  # Create an insecure `SSLContext`. Connections made from the returned context\n  # will not authenticate the server, i.e. `VERIFY_NONE`, and are vulnerable to\n  # MITM. Do not call this method.\n  #\n  # @return [Puppet::SSL::SSLContext] A context to use to create connections\n  # @api private\n  def create_insecure_context\n    store = create_x509_store([], [], false)\n\n    Puppet::SSL::SSLContext.new(store: store, verify_peer: false).freeze\n  end\n\n  # Create an `SSLContext` using the trusted `cacerts` and optional `crls`.\n  # Connections made from the returned context will authenticate the server,\n  # i.e. `VERIFY_PEER`, but will not use a client certificate.\n  #\n  # The `crls` parameter must contain CRLs corresponding to each CA in `cacerts`\n  # depending on the `revocation` mode. See {#create_context}.\n  #\n  # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs\n  # @param crls [Array<OpenSSL::X509::CRL>] Array of CRLs\n  # @param revocation [:chain, :leaf, false] revocation mode\n  # @return [Puppet::SSL::SSLContext] A context to use to create connections\n  # @raise (see #create_context)\n  # @api private\n  def create_root_context(cacerts:, crls: [], revocation: Puppet[:certificate_revocation])\n    store = create_x509_store(cacerts, crls, revocation)\n\n    Puppet::SSL::SSLContext.new(store: store, cacerts: cacerts, crls: crls, revocation: revocation).freeze\n  end\n\n  # Create an `SSLContext` using the trusted `cacerts` and any certs in OpenSSL's\n  # default verify path locations. When running puppet as a gem, the location is\n  # system dependent. When running puppet from puppet-agent packages, the location\n  # refers to the cacerts bundle in the puppet-agent package.\n  #\n  # Connections made from the returned context will authenticate the server,\n  # i.e. `VERIFY_PEER`, but will not use a client certificate (unless requested)\n  # and will not perform revocation checking.\n  #\n  # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs\n  # @param path [String, nil] A file containing additional trusted CA certs.\n  # @param include_client_cert [true, false] If true, the client cert will be added to the context\n  #   allowing mutual TLS authentication. The default is false. If the client cert doesn't exist\n  #   then the option will be ignored.\n  # @return [Puppet::SSL::SSLContext] A context to use to create connections\n  # @raise (see #create_context)\n  # @api private\n  def create_system_context(cacerts:, path: Puppet[:ssl_trust_store], include_client_cert: false)\n    store = create_x509_store(cacerts, [], false, include_system_store: true)\n\n    if path\n      stat = Puppet::FileSystem.stat(path)\n      if stat\n        if stat.ftype == 'file'\n          # don't add empty files as ruby/openssl will raise\n          if stat.size > 0\n            begin\n              store.add_file(path)\n            rescue => e\n              Puppet.err(_(\"Failed to add '%{path}' as a trusted CA file: %{detail}\" % { path: path, detail: e.message }, e))\n            end\n          end\n        else\n          Puppet.warning(_(\"The 'ssl_trust_store' setting does not refer to a file and will be ignored: '%{path}'\" % { path: path }))\n        end\n      end\n    end\n\n    if include_client_cert\n      cert_provider = Puppet::X509::CertProvider.new\n      private_key = cert_provider.load_private_key(Puppet[:certname], required: false)\n      unless private_key\n        Puppet.warning(\"Private key for '#{Puppet[:certname]}' does not exist\")\n      end\n\n      client_cert = cert_provider.load_client_cert(Puppet[:certname], required: false)\n      unless client_cert\n        Puppet.warning(\"Client certificate for '#{Puppet[:certname]}' does not exist\")\n      end\n\n      if private_key && client_cert\n        client_chain = resolve_client_chain(store, client_cert, private_key)\n\n        return Puppet::SSL::SSLContext.new(\n          store: store, cacerts: cacerts, crls: [],\n          private_key: private_key, client_cert: client_cert, client_chain: client_chain,\n          revocation: false\n        ).freeze\n      end\n    end\n\n    Puppet::SSL::SSLContext.new(store: store, cacerts: cacerts, crls: [], revocation: false).freeze\n  end\n\n  # Create an `SSLContext` using the trusted `cacerts`, `crls`, `private_key`,\n  # `client_cert`, and `revocation` mode. Connections made from the returned\n  # context will be mutually authenticated.\n  #\n  # The `crls` parameter must contain CRLs corresponding to each CA in `cacerts`\n  # depending on the `revocation` mode:\n  #\n  # * `:chain` - `crls` must contain a CRL for every CA in `cacerts`\n  # * `:leaf` - `crls` must contain (at least) the CRL for the leaf CA in `cacerts`\n  # * `false` - `crls` can be empty\n  #\n  # The `private_key` and public key from the `client_cert` must match.\n  #\n  # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs\n  # @param crls [Array<OpenSSL::X509::CRL>] Array of CRLs\n  # @param private_key [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] client's private key\n  # @param client_cert [OpenSSL::X509::Certificate] client's cert whose public\n  #   key matches the `private_key`\n  # @param revocation [:chain, :leaf, false] revocation mode\n  # @param include_system_store [true, false] Also trust system CA\n  # @return [Puppet::SSL::SSLContext] A context to use to create connections\n  # @raise [Puppet::SSL::CertVerifyError] There was an issue with\n  #   one of the certs or CRLs.\n  # @raise [Puppet::SSL::SSLError] There was an issue with the\n  #   `private_key`.\n  # @api private\n  def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation], include_system_store: false)\n    raise ArgumentError, _(\"CA certs are missing\") unless cacerts\n    raise ArgumentError, _(\"CRLs are missing\") unless crls\n    raise ArgumentError, _(\"Private key is missing\") unless private_key\n    raise ArgumentError, _(\"Client cert is missing\") unless client_cert\n\n    store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)\n    client_chain = resolve_client_chain(store, client_cert, private_key)\n\n    Puppet::SSL::SSLContext.new(\n      store: store, cacerts: cacerts, crls: crls,\n      private_key: private_key, client_cert: client_cert, client_chain: client_chain,\n      revocation: revocation\n    ).freeze\n  end\n\n  # Load an `SSLContext` using available certs and keys. An exception is raised\n  # if any component is missing or is invalid, such as a mismatched client cert\n  # and private key. Connections made from the returned context will be mutually\n  # authenticated.\n  #\n  # @param certname [String] Which cert & key to load\n  # @param revocation [:chain, :leaf, false] revocation mode\n  # @param password [String, nil] If the private key is encrypted, decrypt\n  #   it using the password. If the key is encrypted, but a password is\n  #   not specified, then the key cannot be loaded.\n  # @param include_system_store [true, false] Also trust system CA\n  # @return [Puppet::SSL::SSLContext] A context to use to create connections\n  # @raise [Puppet::SSL::CertVerifyError] There was an issue with\n  #   one of the certs or CRLs.\n  # @raise [Puppet::Error] There was an issue with one of the required components.\n  # @api private\n  def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil, include_system_store: false)\n    cert = Puppet::X509::CertProvider.new\n    cacerts = cert.load_cacerts(required: true)\n    crls = case revocation\n           when :chain, :leaf\n             cert.load_crls(required: true)\n           else\n             []\n           end\n    private_key = cert.load_private_key(certname, required: true, password: password)\n    client_cert = cert.load_client_cert(certname, required: true)\n\n    create_context(cacerts: cacerts, crls: crls,  private_key: private_key, client_cert: client_cert, revocation: revocation, include_system_store: include_system_store)\n  rescue OpenSSL::PKey::PKeyError => e\n    raise Puppet::SSL::SSLError.new(_(\"Failed to load private key for host '%{name}': %{message}\") % { name: certname, message: e.message }, e)\n  end\n\n  # Verify the `csr` was signed with a private key corresponding to the\n  # `public_key`. This ensures the CSR was signed by someone in possession\n  # of the private key, and that it hasn't been tampered with since.\n  #\n  # @param csr [OpenSSL::X509::Request] certificate signing request\n  # @param public_key [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] public key\n  # @raise [Puppet::SSL:SSLError] The private_key for the given `public_key` was\n  #   not used to sign the CSR.\n  # @api private\n  def verify_request(csr, public_key)\n    unless csr.verify(public_key)\n      raise Puppet::SSL::SSLError, _(\"The CSR for host '%{name}' does not match the public key\") % { name: subject(csr) }\n    end\n\n    csr\n  end\n\n  def print(ssl_context, alg = 'SHA256')\n    if Puppet::Util::Log.sendlevel?(:debug)\n      chain = ssl_context.client_chain\n      # print from root to client\n      chain.reverse.each_with_index do |cert, i|\n        digest = Puppet::SSL::Digest.new(alg, cert.to_der)\n        if i == chain.length - 1\n          Puppet.debug(_(\"Verified client certificate '%{subject}' fingerprint %{digest}\") % { subject: cert.subject.to_utf8, digest: digest })\n        else\n          Puppet.debug(_(\"Verified CA certificate '%{subject}' fingerprint %{digest}\") % { subject: cert.subject.to_utf8, digest: digest })\n        end\n      end\n      ssl_context.crls.each do |crl|\n        oid_values = crl.extensions.to_h { |ext| [ext.oid, ext.value] }\n        crlNumber = oid_values['crlNumber'] || 'unknown'\n        authKeyId = (oid_values['authorityKeyIdentifier'] || 'unknown').chomp\n        Puppet.debug(\"Using CRL '#{crl.issuer.to_utf8}' authorityKeyIdentifier '#{authKeyId}' crlNumber '#{crlNumber}'\")\n      end\n    end\n  end\n\n  private\n\n  def default_flags\n    # checking the signature of the self-signed cert doesn't add any security,\n    # but it's a sanity check to make sure the cert isn't corrupt. This option\n    # is not available in JRuby's OpenSSL library.\n    if defined?(OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE)\n      OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE\n    else\n      0\n    end\n  end\n\n  def create_x509_store(roots, crls, revocation, include_system_store: false)\n    store = OpenSSL::X509::Store.new\n    store.purpose = OpenSSL::X509::PURPOSE_ANY\n    store.flags = default_flags | revocation_mode(revocation)\n\n    roots.each { |cert| store.add_cert(cert) }\n    crls.each { |crl| store.add_crl(crl) }\n\n    store.set_default_paths if include_system_store\n\n    store\n  end\n\n  def subject(x509)\n    x509.subject.to_utf8\n  end\n\n  def issuer(x509)\n    x509.issuer.to_utf8\n  end\n\n  def revocation_mode(mode)\n    case mode\n    when false\n      0\n    when :leaf\n      OpenSSL::X509::V_FLAG_CRL_CHECK\n    else\n      # :chain is the default\n      OpenSSL::X509::V_FLAG_CRL_CHECK | OpenSSL::X509::V_FLAG_CRL_CHECK_ALL\n    end\n  end\n\n  def resolve_client_chain(store, client_cert, private_key)\n    client_chain = verify_cert_with_store(store, client_cert)\n\n    if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)\n      raise Puppet::SSL::SSLError, _(\"Unsupported key '%{type}'\") % { type: private_key.class.name }\n    end\n\n    unless client_cert.check_private_key(private_key)\n      raise Puppet::SSL::SSLError, _(\"The certificate for '%{name}' does not match its private key\") % { name: subject(client_cert) }\n    end\n\n    client_chain\n  end\n\n  def verify_cert_with_store(store, cert)\n    # StoreContext#initialize accepts a chain argument, but it's set to [] because\n    # puppet requires any intermediate CA certs needed to complete the client's\n    # chain to be in the CA bundle that we downloaded from the server, and\n    # they've already been added to the store. See PUP-9500.\n\n    store_context = OpenSSL::X509::StoreContext.new(store, cert, [])\n    unless store_context.verify\n      current_cert = store_context.current_cert\n\n      # If the client cert's intermediate CA is not in the CA bundle, then warn,\n      # but don't error, because SSL allows the client to send an incomplete\n      # chain, and have the server resolve it.\n      if store_context.error == OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY\n        Puppet.warning _(\"The issuer '%{issuer}' of certificate '%{subject}' cannot be found locally\") % {\n          issuer: issuer(current_cert), subject: subject(current_cert)\n        }\n      else\n        raise_cert_verify_error(store_context, current_cert)\n      end\n    end\n\n    # resolved chain from leaf to root\n    store_context.chain\n  end\n\n  def raise_cert_verify_error(store_context, current_cert)\n    message =\n      case store_context.error\n      when OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID\n        _(\"The certificate '%{subject}' is not yet valid, verify time is synchronized\") % { subject: subject(current_cert) }\n      when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED\n        _(\"The certificate '%{subject}' has expired, verify time is synchronized\") % { subject: subject(current_cert) }\n      when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID\n        _(\"The CRL issued by '%{issuer}' is not yet valid, verify time is synchronized\") % { issuer: issuer(current_cert) }\n      when OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED\n        _(\"The CRL issued by '%{issuer}' has expired, verify time is synchronized\") % { issuer: issuer(current_cert) }\n      when OpenSSL::X509::V_ERR_CERT_SIGNATURE_FAILURE\n        _(\"Invalid signature for certificate '%{subject}'\") % { subject: subject(current_cert) }\n      when OpenSSL::X509::V_ERR_CRL_SIGNATURE_FAILURE\n        _(\"Invalid signature for CRL issued by '%{issuer}'\") % { issuer: issuer(current_cert) }\n      when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT\n        _(\"The issuer '%{issuer}' of certificate '%{subject}' is missing\") % {\n          issuer: issuer(current_cert), subject: subject(current_cert)\n        }\n      when OpenSSL::X509::V_ERR_UNABLE_TO_GET_CRL\n        _(\"The CRL issued by '%{issuer}' is missing\") % { issuer: issuer(current_cert) }\n      when OpenSSL::X509::V_ERR_CERT_REVOKED\n        _(\"Certificate '%{subject}' is revoked\") % { subject: subject(current_cert) }\n      else\n        # error_string is labeled ASCII-8BIT, but is encoded based on Encoding.default_external\n        err_utf8 = Puppet::Util::CharacterEncoding.convert_to_utf_8(store_context.error_string)\n        _(\"Certificate '%{subject}' failed verification (%{err}): %{err_utf8}\") % {\n          subject: subject(current_cert), err: store_context.error, err_utf8: err_utf8\n        }\n      end\n\n    raise Puppet::SSL::CertVerifyError.new(message, store_context.error, current_cert)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/state_machine.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl'\nrequire_relative '../../puppet/util/pidlock'\n\n# This class implements a state machine for bootstrapping a host's CA and CRL\n# bundles, private key and signed client certificate. Each state has a frozen\n# SSLContext that it uses to make network connections. If a state makes progress\n# bootstrapping the host, then the state will generate a new frozen SSLContext\n# and pass that to the next state. For example, the NeedCACerts state will load\n# or download a CA bundle, and generate a new SSLContext containing those CA\n# certs. This way we're sure about which SSLContext is being used during any\n# phase of the bootstrapping process.\n#\n# @api private\nclass Puppet::SSL::StateMachine\n  class SSLState\n    attr_reader :ssl_context\n\n    def initialize(machine, ssl_context)\n      @machine = machine\n      @ssl_context = ssl_context\n      @cert_provider = machine.cert_provider\n      @ssl_provider = machine.ssl_provider\n    end\n\n    def to_error(message, cause)\n      detail = Puppet::Error.new(message)\n      detail.set_backtrace(cause.backtrace)\n      Error.new(@machine, message, detail)\n    end\n\n    def log_error(message)\n      # When running daemonized we set stdout to /dev/null, so write to the log instead\n      if Puppet[:daemonize]\n        Puppet.err(message)\n      else\n        $stdout.puts(message)\n      end\n    end\n  end\n\n  # Load existing CA certs or download them. Transition to NeedCRLs.\n  #\n  class NeedCACerts < SSLState\n    def initialize(machine)\n      super(machine, nil)\n      @ssl_context = @ssl_provider.create_insecure_context\n    end\n\n    def next_state\n      Puppet.debug(\"Loading CA certs\")\n\n      force_crl_refresh = false\n\n      cacerts = @cert_provider.load_cacerts\n      if cacerts\n        next_ctx = @ssl_provider.create_root_context(cacerts: cacerts, revocation: false)\n\n        now = Time.now\n        last_update = @cert_provider.ca_last_update\n        if needs_refresh?(now, last_update)\n          # If we refresh the CA, then we need to force the CRL to be refreshed too,\n          # since if there is a new CA in the chain, then we need its CRL to check\n          # the full chain for revocation status.\n          next_ctx, force_crl_refresh = refresh_ca(next_ctx, last_update)\n        end\n      else\n        route = @machine.session.route_to(:ca, ssl_context: @ssl_context)\n        _, pem = route.get_certificate(Puppet::SSL::CA_NAME, ssl_context: @ssl_context)\n        if @machine.ca_fingerprint\n          actual_digest = @machine.digest_as_hex(pem)\n          expected_digest = @machine.ca_fingerprint.scan(/../).join(':').upcase\n          if actual_digest == expected_digest\n            Puppet.info(_(\"Verified CA bundle with digest (%{digest_type}) %{actual_digest}\") %\n                        { digest_type: @machine.digest, actual_digest: actual_digest })\n          else\n            e = Puppet::Error.new(_(\"CA bundle with digest (%{digest_type}) %{actual_digest} did not match expected digest %{expected_digest}\") % { digest_type: @machine.digest, actual_digest: actual_digest, expected_digest: expected_digest })\n            return Error.new(@machine, e.message, e)\n          end\n        end\n\n        cacerts = @cert_provider.load_cacerts_from_pem(pem)\n        # verify cacerts before saving\n        next_ctx = @ssl_provider.create_root_context(cacerts: cacerts, revocation: false)\n        @cert_provider.save_cacerts(cacerts)\n      end\n\n      NeedCRLs.new(@machine, next_ctx, force_crl_refresh)\n    rescue OpenSSL::X509::CertificateError => e\n      Error.new(@machine, e.message, e)\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 404\n        to_error(_('CA certificate is missing from the server'), e)\n      else\n        to_error(_('Could not download CA certificate: %{message}') % { message: e.message }, e)\n      end\n    end\n\n    private\n\n    def needs_refresh?(now, last_update)\n      return true if last_update.nil?\n\n      ca_ttl = Puppet[:ca_refresh_interval]\n      return false unless ca_ttl\n\n      now.to_i > last_update.to_i + ca_ttl\n    end\n\n    def refresh_ca(ssl_ctx, last_update)\n      Puppet.info(_(\"Refreshing CA certificate\"))\n\n      # return the next_ctx containing the updated ca\n      next_ctx = [download_ca(ssl_ctx, last_update), true]\n\n      # After a successful refresh, update ca_last_update\n      @cert_provider.ca_last_update = Time.now\n\n      next_ctx\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 304\n        Puppet.info(_(\"CA certificate is unmodified, using existing CA certificate\"))\n      else\n        Puppet.info(_(\"Failed to refresh CA certificate, using existing CA certificate: %{message}\") % { message: e.message })\n      end\n\n      # return the original ssl_ctx\n      [ssl_ctx, false]\n    rescue Puppet::HTTP::HTTPError => e\n      Puppet.warning(_(\"Failed to refresh CA certificate, using existing CA certificate: %{message}\") % { message: e.message })\n\n      # return the original ssl_ctx\n      [ssl_ctx, false]\n    end\n\n    def download_ca(ssl_ctx, last_update)\n      route = @machine.session.route_to(:ca, ssl_context: ssl_ctx)\n      _, pem = route.get_certificate(Puppet::SSL::CA_NAME, if_modified_since: last_update, ssl_context: ssl_ctx)\n      cacerts = @cert_provider.load_cacerts_from_pem(pem)\n      # verify cacerts before saving\n      next_ctx = @ssl_provider.create_root_context(cacerts: cacerts, revocation: false)\n      @cert_provider.save_cacerts(cacerts)\n\n      Puppet.info(\"Refreshed CA certificate: #{@machine.digest_as_hex(pem)}\")\n\n      next_ctx\n    end\n  end\n\n  # If revocation is enabled, load CRLs or download them, using the CA bundle\n  # from the previous state. Transition to NeedKey. Even if Puppet[:certificate_revocation]\n  # is leaf or chain, disable revocation when downloading the CRL, since 1) we may\n  # not have one yet or 2) the connection will fail if NeedCACerts downloaded a new CA\n  # for which we don't have a CRL\n  #\n  class NeedCRLs < SSLState\n    attr_reader :force_crl_refresh\n\n    def initialize(machine, ssl_context, force_crl_refresh = false)\n      super(machine, ssl_context)\n      @force_crl_refresh = force_crl_refresh\n    end\n\n    def next_state\n      Puppet.debug(\"Loading CRLs\")\n\n      case Puppet[:certificate_revocation]\n      when :chain, :leaf\n        crls = @cert_provider.load_crls\n        if crls\n          next_ctx = @ssl_provider.create_root_context(cacerts: ssl_context[:cacerts], crls: crls)\n\n          now = Time.now\n          last_update = @cert_provider.crl_last_update\n          if needs_refresh?(now, last_update)\n            next_ctx = refresh_crl(next_ctx, last_update)\n          end\n        else\n          next_ctx = download_crl(@ssl_context, nil)\n        end\n      else\n        Puppet.info(\"Certificate revocation is disabled, skipping CRL download\")\n        next_ctx = @ssl_provider.create_root_context(cacerts: ssl_context[:cacerts], crls: [])\n      end\n\n      NeedKey.new(@machine, next_ctx)\n    rescue OpenSSL::X509::CRLError => e\n      Error.new(@machine, e.message, e)\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 404\n        to_error(_('CRL is missing from the server'), e)\n      else\n        to_error(_('Could not download CRLs: %{message}') % { message: e.message }, e)\n      end\n    end\n\n    private\n\n    def needs_refresh?(now, last_update)\n      return true if @force_crl_refresh || last_update.nil?\n\n      crl_ttl = Puppet[:crl_refresh_interval]\n      return false unless crl_ttl\n\n      now.to_i > last_update.to_i + crl_ttl\n    end\n\n    def refresh_crl(ssl_ctx, last_update)\n      Puppet.info(_(\"Refreshing CRL\"))\n\n      # return the next_ctx containing the updated crl\n      next_ctx = download_crl(ssl_ctx, last_update)\n\n      # After a successful refresh, update crl_last_update\n      @cert_provider.crl_last_update = Time.now\n\n      next_ctx\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 304\n        Puppet.info(_(\"CRL is unmodified, using existing CRL\"))\n      else\n        Puppet.info(_(\"Failed to refresh CRL, using existing CRL: %{message}\") % { message: e.message })\n      end\n\n      # return the original ssl_ctx\n      ssl_ctx\n    rescue Puppet::HTTP::HTTPError => e\n      Puppet.warning(_(\"Failed to refresh CRL, using existing CRL: %{message}\") % { message: e.message })\n\n      # return the original ssl_ctx\n      ssl_ctx\n    end\n\n    def download_crl(ssl_ctx, last_update)\n      route = @machine.session.route_to(:ca, ssl_context: ssl_ctx)\n      _, pem = route.get_certificate_revocation_list(if_modified_since: last_update, ssl_context: ssl_ctx)\n      crls = @cert_provider.load_crls_from_pem(pem)\n      # verify crls before saving\n      next_ctx = @ssl_provider.create_root_context(cacerts: ssl_ctx[:cacerts], crls: crls)\n      @cert_provider.save_crls(crls)\n\n      Puppet.info(\"Refreshed CRL: #{@machine.digest_as_hex(pem)}\")\n\n      next_ctx\n    end\n  end\n\n  # Load or generate a private key. If the key exists, try to load the client cert\n  # and transition to Done. If the cert is mismatched or otherwise fails valiation,\n  # raise an error. If the key doesn't exist yet, generate one, and save it. If the\n  # cert doesn't exist yet, transition to NeedSubmitCSR.\n  #\n  class NeedKey < SSLState\n    def next_state\n      Puppet.debug(_(\"Loading/generating private key\"))\n\n      password = @cert_provider.load_private_key_password\n      key = @cert_provider.load_private_key(Puppet[:certname], password: password)\n      if key\n        cert = @cert_provider.load_client_cert(Puppet[:certname])\n        if cert\n          next_ctx = @ssl_provider.create_context(\n            cacerts: @ssl_context.cacerts, crls: @ssl_context.crls, private_key: key, client_cert: cert\n          )\n          if needs_refresh?(cert)\n            return NeedRenewedCert.new(@machine, next_ctx, key)\n          else\n            return Done.new(@machine, next_ctx)\n          end\n        end\n      else\n        if Puppet[:key_type] == 'ec'\n          Puppet.info _(\"Creating a new EC SSL key for %{name} using curve %{curve}\") % { name: Puppet[:certname], curve: Puppet[:named_curve] }\n          key = OpenSSL::PKey::EC.generate(Puppet[:named_curve])\n        else\n          Puppet.info _(\"Creating a new RSA SSL key for %{name}\") % { name: Puppet[:certname] }\n          key = OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)\n        end\n\n        @cert_provider.save_private_key(Puppet[:certname], key, password: password)\n      end\n\n      NeedSubmitCSR.new(@machine, @ssl_context, key)\n    end\n\n    private\n\n    def needs_refresh?(cert)\n      cert_ttl = Puppet[:hostcert_renewal_interval]\n      return false unless cert_ttl\n\n      Time.now.to_i >= (cert.not_after.to_i - cert_ttl)\n    end\n  end\n\n  # Base class for states with a private key.\n  #\n  class KeySSLState < SSLState\n    attr_reader :private_key\n\n    def initialize(machine, ssl_context, private_key)\n      super(machine, ssl_context)\n      @private_key = private_key\n    end\n  end\n\n  # Generate and submit a CSR using the CA cert bundle and optional CRL bundle\n  # from earlier states. If the request is submitted, proceed to NeedCert,\n  # otherwise Wait. This could be due to the server already having a CSR\n  # for this host (either the same or different CSR content), having a\n  # signed certificate, or a revoked certificate.\n  #\n  class NeedSubmitCSR < KeySSLState\n    def next_state\n      Puppet.debug(_(\"Generating and submitting a CSR\"))\n\n      csr = @cert_provider.create_request(Puppet[:certname], @private_key)\n      route = @machine.session.route_to(:ca, ssl_context: @ssl_context)\n      route.put_certificate_request(Puppet[:certname], csr, ssl_context: @ssl_context)\n      @cert_provider.save_request(Puppet[:certname], csr)\n      NeedCert.new(@machine, @ssl_context, @private_key)\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 400\n        NeedCert.new(@machine, @ssl_context, @private_key)\n      else\n        to_error(_(\"Failed to submit the CSR, HTTP response was %{code}\") % { code: e.response.code }, e)\n      end\n    end\n  end\n\n  # Attempt to load or retrieve our signed cert.\n  #\n  class NeedCert < KeySSLState\n    def next_state\n      Puppet.debug(_(\"Downloading client certificate\"))\n\n      route = @machine.session.route_to(:ca, ssl_context: @ssl_context)\n      cert = OpenSSL::X509::Certificate.new(\n        route.get_certificate(Puppet[:certname], ssl_context: @ssl_context)[1]\n      )\n      Puppet.info _(\"Downloaded certificate for %{name} from %{url}\") % { name: Puppet[:certname], url: route.url }\n      # verify client cert before saving\n      next_ctx = @ssl_provider.create_context(\n        cacerts: @ssl_context.cacerts, crls: @ssl_context.crls, private_key: @private_key, client_cert: cert\n      )\n      @cert_provider.save_client_cert(Puppet[:certname], cert)\n      @cert_provider.delete_request(Puppet[:certname])\n      Done.new(@machine, next_ctx)\n    rescue Puppet::SSL::SSLError => e\n      Error.new(@machine, e.message, e)\n    rescue OpenSSL::X509::CertificateError => e\n      Error.new(@machine, _(\"Failed to parse certificate: %{message}\") % { message: e.message }, e)\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 404\n        Puppet.info(_(\"Certificate for %{certname} has not been signed yet\") % { certname: Puppet[:certname] })\n        $stdout.puts _(\"Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}).\") % { name: Puppet[:certname] }\n        Wait.new(@machine)\n      else\n        to_error(_(\"Failed to retrieve certificate for %{certname}: %{message}\") %\n                 { certname: Puppet[:certname], message: e.message }, e)\n      end\n    end\n  end\n\n  # Class to renew a client/host certificate automatically.\n  #\n  class NeedRenewedCert < KeySSLState\n    def next_state\n      Puppet.debug(_(\"Renewing client certificate\"))\n\n      route = @machine.session.route_to(:ca, ssl_context: @ssl_context)\n      cert = OpenSSL::X509::Certificate.new(\n        route.post_certificate_renewal(@ssl_context)[1]\n      )\n\n      # verify client cert before saving\n      next_ctx = @ssl_provider.create_context(\n        cacerts: @ssl_context.cacerts, crls: @ssl_context.crls, private_key: @private_key, client_cert: cert\n      )\n      @cert_provider.save_client_cert(Puppet[:certname], cert)\n\n      Puppet.info(_(\"Renewed client certificate: %{cert_digest}, not before '%{not_before}', not after '%{not_after}'\") % { cert_digest: @machine.digest_as_hex(cert.to_pem), not_before: cert.not_before, not_after: cert.not_after })\n\n      Done.new(@machine, next_ctx)\n    rescue Puppet::HTTP::ResponseError => e\n      if e.response.code == 404\n        Puppet.info(_(\"Certificate autorenewal has not been enabled on the server.\"))\n      else\n        Puppet.warning(_(\"Failed to automatically renew certificate: %{code} %{reason}\") % { code: e.response.code, reason: e.response.reason })\n      end\n      Done.new(@machine, @ssl_context)\n    rescue => e\n      Puppet.warning(_(\"Unable to automatically renew certificate: %{message}\") % { message: e.message })\n      Done.new(@machine, @ssl_context)\n    end\n  end\n\n  # We cannot make progress, so wait if allowed to do so, or exit.\n  #\n  class Wait < SSLState\n    def initialize(machine)\n      super(machine, nil)\n    end\n\n    def next_state\n      time = @machine.waitforcert\n      if time < 1\n        log_error(_(\"Exiting now because the waitforcert setting is set to 0.\"))\n        exit(1)\n      elsif Time.now.to_i > @machine.wait_deadline\n        log_error(_(\"Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}). Exiting now because the maxwaitforcert timeout has been exceeded.\") % { name: Puppet[:certname] })\n        exit(1)\n      else\n        Puppet.info(_(\"Will try again in %{time} seconds.\") % { time: time })\n\n        # close http/tls and session state before sleeping\n        Puppet.runtime[:http].close\n        @machine.session = Puppet.runtime[:http].create_session\n\n        @machine.unlock\n        Kernel.sleep(time)\n        NeedLock.new(@machine)\n      end\n    end\n  end\n\n  # Acquire the ssl lock or return LockFailure causing us to exit.\n  #\n  class NeedLock < SSLState\n    def initialize(machine)\n      super(machine, nil)\n    end\n\n    def next_state\n      if @machine.lock\n        # our ssl directory may have been cleaned while we were\n        # sleeping, start over from the top\n        NeedCACerts.new(@machine)\n      elsif @machine.waitforlock < 1\n        LockFailure.new(@machine, _(\"Another puppet instance is already running and the waitforlock setting is set to 0; exiting\"))\n      elsif Time.now.to_i >= @machine.waitlock_deadline\n        LockFailure.new(@machine, _(\"Another puppet instance is already running and the maxwaitforlock timeout has been exceeded; exiting\"))\n      else\n        Puppet.info _(\"Another puppet instance is already running; waiting for it to finish\")\n        Puppet.info _(\"Will try again in %{time} seconds.\") % { time: @machine.waitforlock }\n        Kernel.sleep @machine.waitforlock\n\n        # try again\n        self\n      end\n    end\n  end\n\n  # We failed to acquire the lock, so exit\n  #\n  class LockFailure < SSLState\n    attr_reader :message\n\n    def initialize(machine, message)\n      super(machine, nil)\n      @message = message\n    end\n  end\n\n  # We cannot make progress due to an error.\n  #\n  class Error < SSLState\n    attr_reader :message, :error\n\n    def initialize(machine, message, error)\n      super(machine, nil)\n      @message = message\n      @error = error\n    end\n\n    def next_state\n      Puppet.log_exception(@error, @message)\n      Wait.new(@machine)\n    end\n  end\n\n  # We have a CA bundle, optional CRL bundle, a private key and matching cert\n  # that chains to one of the root certs in our bundle.\n  #\n  class Done < SSLState; end\n\n  attr_reader :waitforcert, :wait_deadline, :waitforlock, :waitlock_deadline, :cert_provider, :ssl_provider, :ca_fingerprint, :digest\n  attr_accessor :session\n\n  # Construct a state machine to manage the SSL initialization process. By\n  # default, if the state machine encounters an exception, it will log the\n  # exception and wait for `waitforcert` seconds and retry, restarting from the\n  # beginning of the state machine.\n  #\n  # However, if `onetime` is true, then the state machine will raise the first\n  # error it encounters, instead of waiting. Otherwise, if `waitforcert` is 0,\n  # then then state machine will exit instead of wait.\n  #\n  # @param waitforcert [Integer] how many seconds to wait between attempts\n  # @param maxwaitforcert [Integer] maximum amount of seconds to wait for the\n  #   server to sign the certificate request\n  # @param waitforlock [Integer] how many seconds to wait between attempts for\n  #   acquiring the ssl lock\n  # @param maxwaitforlock [Integer] maximum amount of seconds to wait for an\n  #   already running process to release the ssl lock\n  # @param onetime [Boolean] whether to run onetime\n  # @param lockfile [Puppet::Util::Pidlock] lockfile to protect against\n  #   concurrent modification by multiple processes\n  # @param cert_provider [Puppet::X509::CertProvider] cert provider to use\n  #   to load and save X509 objects.\n  # @param ssl_provider [Puppet::SSL::SSLProvider] ssl provider to use\n  #   to construct ssl contexts.\n  # @param digest [String] digest algorithm to use for certificate fingerprinting\n  # @param ca_fingerprint [String] optional fingerprint to verify the\n  #   downloaded CA bundle\n  def initialize(waitforcert: Puppet[:waitforcert],\n                 maxwaitforcert: Puppet[:maxwaitforcert],\n                 waitforlock: Puppet[:waitforlock],\n                 maxwaitforlock: Puppet[:maxwaitforlock],\n                 onetime: Puppet[:onetime],\n                 cert_provider: Puppet::X509::CertProvider.new,\n                 ssl_provider: Puppet::SSL::SSLProvider.new,\n                 lockfile: Puppet::Util::Pidlock.new(Puppet[:ssl_lockfile]),\n                 digest: 'SHA256',\n                 ca_fingerprint: Puppet[:ca_fingerprint])\n    @waitforcert = waitforcert\n    @wait_deadline = Time.now.to_i + maxwaitforcert\n    @waitforlock = waitforlock\n    @waitlock_deadline = Time.now.to_i + maxwaitforlock\n    @onetime = onetime\n    @cert_provider = cert_provider\n    @ssl_provider = ssl_provider\n    @lockfile = lockfile\n    @digest = digest\n    @ca_fingerprint = ca_fingerprint\n    @session = Puppet.runtime[:http].create_session\n  end\n\n  # Run the state machine for CA certs and CRLs.\n  #\n  # @return [Puppet::SSL::SSLContext] initialized SSLContext\n  # @raise [Puppet::Error] If we fail to generate an SSLContext\n  # @api private\n  def ensure_ca_certificates\n    final_state = run_machine(NeedLock.new(self), NeedKey)\n    final_state.ssl_context\n  end\n\n  # Run the state machine for client certs.\n  #\n  # @return [Puppet::SSL::SSLContext] initialized SSLContext\n  # @raise [Puppet::Error] If we fail to generate an SSLContext\n  # @api private\n  def ensure_client_certificate\n    final_state = run_machine(NeedLock.new(self), Done)\n    ssl_context = final_state.ssl_context\n    @ssl_provider.print(ssl_context, @digest)\n    ssl_context\n  end\n\n  def lock\n    @lockfile.lock\n  end\n\n  def unlock\n    @lockfile.unlock\n  end\n\n  def digest_as_hex(str)\n    Puppet::SSL::Digest.new(digest, str).to_hex\n  end\n\n  private\n\n  def run_machine(state, stop)\n    loop do\n      state = run_step(state)\n\n      case state\n      when stop\n        break\n      when LockFailure\n        raise Puppet::Error, state.message\n      when Error\n        if @onetime\n          Puppet.log_exception(state.error)\n          raise state.error\n        end\n      else\n        # fall through\n      end\n    end\n\n    state\n  ensure\n    @lockfile.unlock if @lockfile.locked?\n  end\n\n  def run_step(state)\n    state.next_state\n  rescue => e\n    state.to_error(e.message, e)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl/verifier.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/ssl'\n\n# Verify an SSL connection.\n#\n# @api private\nclass Puppet::SSL::Verifier\n  FIVE_MINUTES_AS_SECONDS = 5 * 60\n\n  attr_reader :ssl_context\n\n  # Create a verifier using an `ssl_context`.\n  #\n  # @param hostname [String] FQDN of the server we're attempting to connect to\n  # @param ssl_context [Puppet::SSL::SSLContext] ssl_context containing CA certs,\n  #   CRLs, etc needed to verify the server's certificate chain\n  # @api private\n  def initialize(hostname, ssl_context)\n    @hostname = hostname\n    @ssl_context = ssl_context\n  end\n\n  # Return true if `self` is reusable with `verifier` meaning they\n  # are using the same `ssl_context`, so there's no loss of security\n  # when using a cached connection.\n  #\n  # @param verifier [Puppet::SSL::Verifier] the verifier to compare against\n  # @return [Boolean] return true if a cached connection can be used, false otherwise\n  # @api private\n  def reusable?(verifier)\n    verifier.instance_of?(self.class) &&\n      verifier.ssl_context.equal?(@ssl_context) # same object?\n  end\n\n  # Configure the `http` connection based on the current `ssl_context`.\n  #\n  # @param http [Net::HTTP] connection\n  # @api private\n  def setup_connection(http)\n    http.cert_store = @ssl_context[:store]\n    http.cert = @ssl_context[:client_cert]\n    http.key = @ssl_context[:private_key]\n    # default to VERIFY_PEER\n    http.verify_mode = if !@ssl_context[:verify_peer]\n                         OpenSSL::SSL::VERIFY_NONE\n                       else\n                         OpenSSL::SSL::VERIFY_PEER\n                       end\n    http.verify_callback = self\n  end\n\n  # This method is called if `Net::HTTP#start` raises an exception, which\n  # could be a result of an openssl error during cert verification, due\n  # to ruby's `Socket#post_connection_check`, or general SSL connection\n  # error.\n  #\n  # @param http [Net::HTTP] connection\n  # @param error [OpenSSL::SSL::SSLError] connection error\n  # @raise [Puppet::SSL::CertVerifyError] SSL connection failed due to a\n  #   verification error with the server's certificate or chain\n  # @raise [Puppet::Error] server hostname does not match certificate\n  # @raise [OpenSSL::SSL::SSLError] low-level SSL connection failure\n  # @api private\n  def handle_connection_error(http, error)\n    raise @last_error if @last_error\n\n    # ruby can pass SSL validation but fail post_connection_check\n    peer_cert = http.peer_cert\n    if peer_cert && !OpenSSL::SSL.verify_certificate_identity(peer_cert, @hostname)\n      raise Puppet::SSL::CertMismatchError.new(peer_cert, @hostname)\n    else\n      raise error\n    end\n  end\n\n  # OpenSSL will call this method with the verification result for each cert in\n  # the server's chain, working from the root CA to the server's cert. If\n  # preverify_ok is `true`, then that cert passed verification. If it's `false`\n  # then the current verification error is contained in `store_context.error`.\n  # and the current cert is in `store_context.current_cert`.\n  #\n  # If this method returns `false`, then verification stops and ruby will raise\n  # an `OpenSSL::SSL::Error` with \"certificate verification failed\". If this\n  # method returns `true`, then verification continues.\n  #\n  # If this method ignores a verification error, such as the cert's CRL will be\n  # valid within the next 5 minutes, then this method may be called with a\n  # different verification error for the same cert.\n  #\n  # WARNING: If `store_context.error` returns `OpenSSL::X509::V_OK`, don't\n  # assume verification passed. Ruby 2.4+ implements certificate hostname\n  # checking by default, and if the cert doesn't match the hostname, then the\n  # error will be V_OK. Always use `preverify_ok` to determine if verification\n  # succeeded or not.\n  #\n  # @param preverify_ok [Boolean] if `true` the current certificate in `store_context`\n  #   was verified. Otherwise, check for the current error in `store_context.error`\n  # @param store_context [OpenSSL::X509::StoreContext] The context holding the\n  #   verification result for one certificate\n  # @return [Boolean] If `true`, continue verifying the chain, even if that means\n  #   ignoring the current verification error. If `false`, abort the connection.\n  #\n  # @api private\n  def call(preverify_ok, store_context)\n    return true if preverify_ok\n\n    peer_cert = store_context.current_cert\n\n    case store_context.error\n    when OpenSSL::X509::V_OK\n      # chain is from leaf to root, opposite of the order that `call` is invoked\n      chain_cert = store_context.chain.first\n\n      # ruby 2.4 doesn't compare certs based on value, so force to DER byte array\n      if peer_cert && chain_cert && peer_cert.to_der == chain_cert.to_der && !OpenSSL::SSL.verify_certificate_identity(peer_cert, @hostname)\n        @last_error = Puppet::SSL::CertMismatchError.new(peer_cert, @hostname)\n        return false\n      end\n\n    # ruby-openssl#74ef8c0cc56b840b772240f2ee2b0fc0aafa2743 now sets the\n    # store_context error when the cert is mismatched\n    when OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH\n      @last_error = Puppet::SSL::CertMismatchError.new(peer_cert, @hostname)\n      return false\n\n    when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID\n      crl = store_context.current_crl\n      if crl && crl.last_update && crl.last_update < Time.now + FIVE_MINUTES_AS_SECONDS\n        Puppet.debug(\"Ignoring CRL not yet valid, current time #{Time.now.utc}, CRL last updated #{crl.last_update.utc}\")\n        return true\n      end\n    end\n\n    # TRANSLATORS: `error` is an untranslated message from openssl describing why a certificate in the server's chain is invalid, and `subject` is the identity/name of the failed certificate\n    @last_error = Puppet::SSL::CertVerifyError.new(\n      _(\"certificate verify failed [%{error} for %{subject}]\") %\n      { error: store_context.error_string, subject: peer_cert.subject.to_utf8 },\n      store_context.error, peer_cert\n    )\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/ssl.rb",
    "content": "# frozen_string_literal: true\n\n# Just to make the constants work out.\nrequire_relative '../puppet'\nrequire_relative 'ssl/openssl_loader'\n\n# Responsible for bootstrapping an agent's certificate and private key, generating\n# SSLContexts for use in making HTTPS connections, and handling CSR attributes and\n# certificate extensions.\n#\n# @see Puppet::SSL::SSLProvider\n# @api private\nmodule Puppet::SSL\n  CA_NAME = \"ca\"\n\n  require_relative 'ssl/oids'\n  require_relative 'ssl/error'\n  require_relative 'ssl/ssl_context'\n  require_relative 'ssl/verifier'\n  require_relative 'ssl/ssl_provider'\n  require_relative 'ssl/state_machine'\n  require_relative 'ssl/certificate'\n  require_relative 'ssl/certificate_request'\n  require_relative 'ssl/certificate_request_attributes'\nend\n"
  },
  {
    "path": "lib/puppet/syntax_checkers/base64.rb",
    "content": "# frozen_string_literal: true\n\n# A syntax checker for Base64.\n# @api public\nrequire_relative '../../puppet/syntax_checkers'\nrequire 'base64'\nclass Puppet::SyntaxCheckers::Base64 < Puppet::Plugins::SyntaxCheckers::SyntaxChecker\n  # Checks the text for BASE64 syntax issues and reports them to the given acceptor.\n  # This checker allows the most relaxed form of Base64, including newlines and missing padding.\n  # It also accept URLsafe input.\n  #\n  # @param text [String] The text to check\n  # @param syntax [String] The syntax identifier in mime style (e.g. 'base64', 'text/xxx+base64')\n  # @param acceptor [#accept] A Diagnostic acceptor\n  # @param source_pos [Puppet::Pops::Adapters::SourcePosAdapter] A source pos adapter with location information\n  # @api public\n  #\n  def check(text, syntax, acceptor, source_pos)\n    raise ArgumentError, _(\"Base64 syntax checker: the text to check must be a String.\") unless text.is_a?(String)\n    raise ArgumentError, _(\"Base64 syntax checker: the syntax identifier must be a String, e.g. json, data+json\") unless syntax.is_a?(String)\n    raise ArgumentError, _(\"Base64 syntax checker: invalid Acceptor, got: '%{klass}'.\") % { klass: acceptor.class.name } unless acceptor.is_a?(Puppet::Pops::Validation::Acceptor)\n\n    cleaned_text = text.gsub(/[\\r?\\n[:blank:]]/, '')\n    begin\n      # Do a strict decode64 on text with all whitespace stripped since the non strict version\n      # simply skips all non base64 characters\n      Base64.strict_decode64(cleaned_text)\n    rescue\n      msg = if (cleaned_text.bytes.to_a.size * 8) % 6 != 0\n              _(\"Base64 syntax checker: Cannot parse invalid Base64 string - padding is not correct\")\n            else\n              _(\"Base64 syntax checker: Cannot parse invalid Base64 string - contains letters outside strict base 64 range (or whitespace)\")\n            end\n\n      # TODO: improve the pops API to allow simpler diagnostic creation while still maintaining capabilities\n      # and the issue code. (In this case especially, where there is only a single error message being issued).\n      #\n      issue = Puppet::Pops::Issues.issue(:ILLEGAL_BASE64) { msg }\n      acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, source_pos.file, source_pos, {}))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/syntax_checkers/epp.rb",
    "content": "# frozen_string_literal: true\n\n# A syntax checker for JSON.\n# @api public\nrequire_relative '../../puppet/syntax_checkers'\nclass Puppet::SyntaxCheckers::EPP < Puppet::Plugins::SyntaxCheckers::SyntaxChecker\n  # Checks the text for Puppet Language EPP syntax issues and reports them to the given acceptor.\n  #\n  # Error messages from the checker are capped at 100 chars from the source text.\n  #\n  # @param text [String] The text to check\n  # @param syntax [String] The syntax identifier in mime style (only accepts 'pp')\n  # @param acceptor [#accept] A Diagnostic acceptor\n  # @param source_pos [Puppet::Pops::Adapters::SourcePosAdapter] A source pos adapter with location information\n  # @api public\n  #\n  def check(text, syntax, acceptor, source_pos)\n    raise ArgumentError, _(\"EPP syntax checker: the text to check must be a String.\") unless text.is_a?(String)\n    raise ArgumentError, _(\"EPP syntax checker: the syntax identifier must be a String, e.g. pp\") unless syntax == 'epp'\n    raise ArgumentError, _(\"EPP syntax checker: invalid Acceptor, got: '%{klass}'.\") % { klass: acceptor.class.name } unless acceptor.is_a?(Puppet::Pops::Validation::Acceptor)\n\n    begin\n      Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.singleton.parse_string(text)\n    rescue => e\n      # Cap the message to 100 chars and replace newlines\n      msg = _(\"EPP syntax checker: \\\"%{message}\\\"\") % { message: e.message().slice(0, 500).gsub(/\\r?\\n/, \"\\\\n\") }\n\n      # TODO: improve the pops API to allow simpler diagnostic creation while still maintaining capabilities\n      # and the issue code. (In this case especially, where there is only a single error message being issued).\n      #\n      issue = Puppet::Pops::Issues.issue(:ILLEGAL_EPP) { msg }\n      acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, source_pos.file, source_pos, {}))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/syntax_checkers/json.rb",
    "content": "# frozen_string_literal: true\n\n# A syntax checker for JSON.\n# @api public\nrequire_relative '../../puppet/syntax_checkers'\nclass Puppet::SyntaxCheckers::Json < Puppet::Plugins::SyntaxCheckers::SyntaxChecker\n  # Checks the text for JSON syntax issues and reports them to the given acceptor.\n  #\n  # Error messages from the checker are capped at 100 chars from the source text.\n  #\n  # @param text [String] The text to check\n  # @param syntax [String] The syntax identifier in mime style (e.g. 'json', 'json-patch+json', 'xml', 'myapp+xml'\n  # @param acceptor [#accept] A Diagnostic acceptor\n  # @param source_pos [Puppet::Pops::Adapters::SourcePosAdapter] A source pos adapter with location information\n  # @api public\n  #\n  def check(text, syntax, acceptor, source_pos)\n    raise ArgumentError, _(\"Json syntax checker: the text to check must be a String.\") unless text.is_a?(String)\n    raise ArgumentError, _(\"Json syntax checker: the syntax identifier must be a String, e.g. json, data+json\") unless syntax.is_a?(String)\n    raise ArgumentError, _(\"Json syntax checker: invalid Acceptor, got: '%{klass}'.\") % { klass: acceptor.class.name } unless acceptor.is_a?(Puppet::Pops::Validation::Acceptor)\n\n    begin\n      Puppet::Util::Json.load(text)\n    rescue => e\n      # Cap the message to 100 chars and replace newlines\n      msg = _(\"JSON syntax checker: Cannot parse invalid JSON string. \\\"%{message}\\\"\") % { message: e.message().slice(0, 100).gsub(/\\r?\\n/, \"\\\\n\") }\n\n      # TODO: improve the pops API to allow simpler diagnostic creation while still maintaining capabilities\n      # and the issue code. (In this case especially, where there is only a single error message being issued).\n      #\n      issue = Puppet::Pops::Issues.issue(:ILLEGAL_JSON) { msg }\n      acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, source_pos.file, source_pos, {}))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/syntax_checkers/pp.rb",
    "content": "# frozen_string_literal: true\n\n# A syntax checker for JSON.\n# @api public\nrequire_relative '../../puppet/syntax_checkers'\nclass Puppet::SyntaxCheckers::PP < Puppet::Plugins::SyntaxCheckers::SyntaxChecker\n  # Checks the text for Puppet Language syntax issues and reports them to the given acceptor.\n  #\n  # Error messages from the checker are capped at 100 chars from the source text.\n  #\n  # @param text [String] The text to check\n  # @param syntax [String] The syntax identifier in mime style (only accepts 'pp')\n  # @param acceptor [#accept] A Diagnostic acceptor\n  # @param source_pos [Puppet::Pops::Adapters::SourcePosAdapter] A source pos adapter with location information\n  # @api public\n  #\n  def check(text, syntax, acceptor, source_pos)\n    raise ArgumentError, _(\"PP syntax checker: the text to check must be a String.\") unless text.is_a?(String)\n    raise ArgumentError, _(\"PP syntax checker: the syntax identifier must be a String, e.g. pp\") unless syntax == 'pp'\n    raise ArgumentError, _(\"PP syntax checker: invalid Acceptor, got: '%{klass}'.\") % { klass: acceptor.class.name } unless acceptor.is_a?(Puppet::Pops::Validation::Acceptor)\n\n    begin\n      Puppet::Pops::Parser::EvaluatingParser.singleton.parse_string(text)\n    rescue => e\n      # Cap the message to 100 chars and replace newlines\n      msg = _(\"PP syntax checker: \\\"%{message}\\\"\") % { message: e.message().slice(0, 500).gsub(/\\r?\\n/, \"\\\\n\") }\n\n      # TODO: improve the pops API to allow simpler diagnostic creation while still maintaining capabilities\n      # and the issue code. (In this case especially, where there is only a single error message being issued).\n      #\n      issue = Puppet::Pops::Issues.issue(:ILLEGAL_PP) { msg }\n      acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, source_pos.file, source_pos, {}))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/syntax_checkers.rb",
    "content": "# frozen_string_literal: true\n\n# A name space for syntax checkers provided by Puppet.\nmodule Puppet::SyntaxCheckers\nend\n"
  },
  {
    "path": "lib/puppet/test/test_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'tmpdir'\nrequire 'fileutils'\n\nmodule Puppet::Test\n  # This class is intended to provide an API to be used by external projects\n  #  when they are running tests that depend on puppet core.  This should\n  #  allow us to vary the implementation details of managing puppet's state\n  #  for testing, from one version of puppet to the next--without forcing\n  #  the external projects to do any of that state management or be aware of\n  #  the implementation details.\n  #\n  # For now, this consists of a few very simple signatures.  The plan is\n  #  that it should be the responsibility of the puppetlabs_spec_helper\n  #  to broker between external projects and this API; thus, if any\n  #  hacks are required (e.g. to determine whether or not a particular)\n  #  version of puppet supports this API, those hacks will be consolidated in\n  #  one place and won't need to be duplicated in every external project.\n  #\n  # This should also alleviate the anti-pattern that we've been following,\n  #  wherein each external project starts off with a copy of puppet core's\n  #  test_helper.rb and is exposed to risk of that code getting out of\n  #  sync with core.\n  #\n  # Since this class will be \"library code\" that ships with puppet, it does\n  #  not use API from any existing test framework such as rspec.  This should\n  #  theoretically allow it to be used with other unit test frameworks in the\n  #  future, if desired.\n  #\n  # Note that in the future this API could potentially be expanded to handle\n  #  other features such as \"around_test\", but we didn't see a compelling\n  #  reason to deal with that right now.\n  class TestHelper\n    # Call this method once, as early as possible, such as before loading tests\n    # that call Puppet.\n    # @return nil\n    def self.initialize\n      # This meta class instance variable is used as a guard to ensure that\n      # before_each, and after_each are only called once. This problem occurs\n      # when there are more than one puppet test infrastructure orchestrator in use.\n      # The use of both puppetabs-spec_helper, and rodjek-rspec_puppet will cause\n      # two resets of the puppet environment, and will cause problem rolling back to\n      # a known point as there is no way to differentiate where the calls are coming\n      # from. See more information in #before_each_test, and #after_each_test\n      # Note that the variable is only initialized to 0 if nil. This is important\n      # as more than one orchestrator will call initialize. A second call can not\n      # simply set it to 0 since that would potentially destroy an active guard.\n      #\n      @@reentry_count ||= 0\n\n      @environmentpath = Dir.mktmpdir('environments')\n      Dir.mkdir(\"#{@environmentpath}/production\")\n      owner = Process.pid\n      Puppet.push_context(Puppet.base_context({\n                                                :environmentpath => @environmentpath,\n                                                :basemodulepath => \"\",\n                                              }), \"Initial for specs\")\n      Puppet::Parser::Functions.reset\n\n      ObjectSpace.define_finalizer(Puppet.lookup(:environments), proc {\n        if Process.pid == owner\n          FileUtils.rm_rf(@environmentpath)\n        end\n      })\n      Puppet::SSL::Oids.register_puppet_oids\n    end\n\n    # Call this method once, when beginning a test run--prior to running\n    #  any individual tests.\n    # @return nil\n    def self.before_all_tests\n      # The process environment is a shared, persistent resource.\n      $old_env = ENV.to_hash\n    end\n\n    # Call this method once, at the end of a test run, when no more tests\n    #  will be run.\n    # @return nil\n    def self.after_all_tests\n    end\n\n    # The name of the rollback mark used in the Puppet.context. This is what\n    # the test infrastructure returns to for each test.\n    #\n    ROLLBACK_MARK = \"initial testing state\"\n\n    # Call this method once per test, prior to execution of each individual test.\n    # @return nil\n    def self.before_each_test\n      # When using both rspec-puppet and puppet-rspec-helper, there are two packages trying\n      # to be helpful and orchestrate the callback sequence. We let only the first win, the\n      # second callback results in a no-op.\n      # Likewise when entering after_each_test(), a check is made to make tear down happen\n      # only once.\n      #\n      return unless @@reentry_count == 0\n\n      @@reentry_count = 1\n\n      Puppet.mark_context(ROLLBACK_MARK)\n\n      # We need to preserve the current state of all our indirection cache and\n      # terminus classes.  This is pretty important, because changes to these\n      # are global and lead to order dependencies in our testing.\n      #\n      # We go direct to the implementation because there is no safe, sane public\n      # API to manage restoration of these to their default values.  This\n      # should, once the value is proved, be moved to a standard API on the\n      # indirector.\n      #\n      # To make things worse, a number of the tests stub parts of the\n      # indirector.  These stubs have very specific expectations that what\n      # little of the public API we could use is, well, likely to explode\n      # randomly in some tests.  So, direct access.  --daniel 2011-08-30\n      $saved_indirection_state = {}\n      indirections = Puppet::Indirector::Indirection.send(:class_variable_get, :@@indirections)\n      indirections.each do |indirector|\n        $saved_indirection_state[indirector.name] = {\n          :@terminus_class => indirector.instance_variable_get(:@terminus_class).value,\n          :@cache_class => indirector.instance_variable_get(:@cache_class).value,\n          # dup the termini hash so termini created and registered during\n          # the test aren't stored in our saved_indirection_state\n          :@termini => indirector.instance_variable_get(:@termini).dup\n        }\n      end\n\n      # So is the load_path\n      $old_load_path = $LOAD_PATH.dup\n\n      initialize_settings_before_each()\n\n      Puppet.push_context(\n        {\n          trusted_information:\n            Puppet::Context::TrustedInformation.new('local', 'testing', {}, { \"trusted_testhelper\" => true }),\n          ssl_context: Puppet::SSL::SSLContext.new(cacerts: []).freeze,\n          http_session: proc { Puppet.runtime[:http].create_session }\n        },\n        \"Context for specs\"\n      )\n\n      # trigger `require 'facter'`\n      Puppet.runtime[:facter]\n\n      Puppet::Parser::Functions.reset\n      Puppet::Application.clear!\n      Puppet::Util::Profiler.clear\n\n      Puppet::Node::Facts.indirection.terminus_class = :memory\n      facts = Puppet::Node::Facts.new(Puppet[:node_name_value])\n      Puppet::Node::Facts.indirection.save(facts)\n\n      Puppet.clear_deprecation_warnings\n    end\n\n    # Call this method once per test, after execution of each individual test.\n    # @return nil\n    def self.after_each_test\n      # Ensure that a matching tear down only happens once per completed setup\n      # (see #before_each_test).\n      return unless @@reentry_count == 1\n\n      @@reentry_count = 0\n\n      Puppet.settings.send(:clear_everything_for_tests)\n\n      Puppet::Util::Storage.clear\n      Puppet::Util::ExecutionStub.reset\n      Puppet.runtime.clear\n\n      Puppet.clear_deprecation_warnings\n\n      # uncommenting and manipulating this can be useful when tracking down calls to deprecated code\n      # Puppet.log_deprecations_to_file(\"deprecations.txt\", /^Puppet::Util.exec/)\n\n      # Restore the indirector configuration.  See before hook.\n      indirections = Puppet::Indirector::Indirection.send(:class_variable_get, :@@indirections)\n      indirections.each do |indirector|\n        $saved_indirection_state.fetch(indirector.name, {}).each do |variable, value|\n          if variable == :@termini\n            indirector.instance_variable_set(variable, value)\n          else\n            indirector.instance_variable_get(variable).value = value\n          end\n        end\n      end\n      $saved_indirection_state = nil\n\n      # Restore the global process environment.\n      ENV.replace($old_env)\n\n      # Clear all environments\n      Puppet.lookup(:environments).clear_all\n\n      # Restore the load_path late, to avoid messing with stubs from the test.\n      $LOAD_PATH.clear\n      $old_load_path.each { |x| $LOAD_PATH << x }\n\n      Puppet.rollback_context(ROLLBACK_MARK)\n    end\n\n    #########################################################################################\n    # PRIVATE METHODS (not part of the public TestHelper API--do not call these from outside\n    #  of this class!)\n    #########################################################################################\n\n    def self.app_defaults_for_tests\n      {\n        :logdir => \"/dev/null\",\n        :confdir => \"/dev/null\",\n        :publicdir => \"/dev/null\",\n        :codedir => \"/dev/null\",\n        :vardir => \"/dev/null\",\n        :rundir => \"/dev/null\",\n        :hiera_config => \"/dev/null\",\n      }\n    end\n    private_class_method :app_defaults_for_tests\n\n    def self.initialize_settings_before_each\n      Puppet.settings.preferred_run_mode = \"user\"\n      # Initialize \"app defaults\" settings to a good set of test values\n      Puppet.settings.initialize_app_defaults(app_defaults_for_tests)\n\n      # We don't want to depend upon the reported domain name of the\n      # machine running the tests, nor upon the DNS setup of that\n      # domain.\n      Puppet.settings[:use_srv_records] = false\n\n      # Longer keys are secure, but they sure make for some slow testing - both\n      # in terms of generating keys, and in terms of anything the next step down\n      # the line doing validation or whatever.  Most tests don't care how long\n      # or secure it is, just that it exists, so these are better and faster\n      # defaults, in testing only.\n      #\n      # I would make these even shorter, but OpenSSL doesn't support anything\n      # below 512 bits.  Sad, really, because a 0 bit key would be just fine.\n      Puppet[:keylength] = 512\n\n      # Although we setup a testing context during initialization, some tests\n      # will end up creating their own context using the real context objects\n      # and use the setting for the environments. In order to avoid those tests\n      # having to deal with a missing environmentpath we can just set it right\n      # here.\n      Puppet[:environmentpath] = @environmentpath\n      Puppet[:environment_timeout] = 0\n    end\n    private_class_method :initialize_settings_before_each\n  end\nend\n"
  },
  {
    "path": "lib/puppet/thread_local.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'concurrent'\n\nclass Puppet::ThreadLocal < Concurrent::ThreadLocalVar\nend\n"
  },
  {
    "path": "lib/puppet/transaction/additional_resource_generator.rb",
    "content": "# frozen_string_literal: true\n\n# Adds additional resources to the catalog and relationship graph that are\n# generated by existing resources. There are two ways that a resource can\n# generate additional resources, either through the #generate method or the\n# #eval_generate method.\n#\n# @api private\nclass Puppet::Transaction::AdditionalResourceGenerator\n  attr_writer :relationship_graph\n  # [boolean] true if any resource has attempted and failed to generate resources\n  attr_reader :resources_failed_to_generate\n\n  def initialize(catalog, relationship_graph, prioritizer)\n    @catalog = catalog\n    @relationship_graph = relationship_graph\n    @prioritizer = prioritizer\n    @resources_failed_to_generate = false\n  end\n\n  def generate_additional_resources(resource)\n    return unless resource.respond_to?(:generate)\n\n    begin\n      generated = resource.generate\n    rescue => detail\n      @resources_failed_to_generate = true\n      resource.log_exception(detail, _(\"Failed to generate additional resources using 'generate': %{detail}\") % { detail: detail })\n    end\n    return unless generated\n\n    generated = [generated] unless generated.is_a?(Array)\n    generated.collect! do |res|\n      @catalog.resource(res.ref) || res\n    end\n    unless resource.depthfirst?\n      # This is reversed because PUP-1963 changed how generated\n      # resources were added to the catalog. It exists for backwards\n      # compatibility only, and can probably be removed in Puppet 5\n      #\n      # Previously, resources were given sequential priorities in the\n      # relationship graph. Post-1963, resources are added to the\n      # catalog one by one adjacent to the parent resource. This\n      # causes an implicit reversal of their application order from\n      # the old code. The reverse makes it all work like it did.\n      generated.reverse!\n    end\n    generated.each do |res|\n      add_resource(res, resource)\n\n      add_generated_directed_dependency(resource, res)\n      generate_additional_resources(res)\n    end\n  end\n\n  def eval_generate(resource)\n    return false unless resource.respond_to?(:eval_generate)\n    raise Puppet::DevError, _(\"Depthfirst resources are not supported by eval_generate\") if resource.depthfirst?\n\n    begin\n      generated = replace_duplicates_with_catalog_resources(resource.eval_generate)\n      return false if generated.empty?\n    rescue => detail\n      @resources_failed_to_generate = true\n      # TRANSLATORS eval_generate is a method name and should be left untranslated\n      resource.log_exception(detail, _(\"Failed to generate additional resources using 'eval_generate': %{detail}\") % { detail: detail })\n      return false\n    end\n    add_resources(generated, resource)\n\n    made = generated.map(&:name).zip(generated).to_h\n    contain_generated_resources_in(resource, made)\n    connect_resources_to_ancestors(resource, made)\n\n    true\n  end\n\n  private\n\n  def replace_duplicates_with_catalog_resources(generated)\n    generated.collect do |generated_resource|\n      @catalog.resource(generated_resource.ref) || generated_resource\n    end\n  end\n\n  def contain_generated_resources_in(resource, made)\n    sentinel = Puppet::Type.type(:whit).new(:name => \"completed_#{resource.title}\", :catalog => resource.catalog)\n    # Tag the completed whit with all of the tags of the generating resource\n    # except the type name to allow event propogation to succeed beyond the whit\n    # \"boundary\" when filtering resources with tags. We include auto-generated\n    # tags such as the type name to support implicit filtering as well as\n    # explicit. Note that resource#tags returns a duplicate of the resource's\n    # tags.\n    sentinel.merge_tags_from(resource)\n    priority = @prioritizer.generate_priority_contained_in(resource, sentinel)\n    @relationship_graph.add_vertex(sentinel, priority)\n\n    redirect_edges_to_sentinel(resource, sentinel, made)\n\n    made.values.each do |res|\n      # This resource isn't 'completed' until each child has run\n      add_conditional_directed_dependency(res, sentinel, Puppet::Graph::RelationshipGraph::Default_label)\n    end\n\n    # This edge allows the resource's events to propagate, though it isn't\n    # strictly necessary for ordering purposes\n    add_conditional_directed_dependency(resource, sentinel, Puppet::Graph::RelationshipGraph::Default_label)\n  end\n\n  def redirect_edges_to_sentinel(resource, sentinel, made)\n    @relationship_graph.adjacent(resource, :direction => :out, :type => :edges).each do |e|\n      next if made[e.target.name]\n\n      @relationship_graph.add_relationship(sentinel, e.target, e.label)\n      @relationship_graph.remove_edge! e\n    end\n  end\n\n  def connect_resources_to_ancestors(resource, made)\n    made.values.each do |res|\n      # Depend on the nearest ancestor we generated, falling back to the\n      # resource if we have none\n      parent_name = res.ancestors.find { |a| made[a] and made[a] != res }\n      parent = made[parent_name] || resource\n\n      add_conditional_directed_dependency(parent, res)\n    end\n  end\n\n  def add_resources(generated, resource)\n    generated.each do |res|\n      priority = @prioritizer.generate_priority_contained_in(resource, res)\n      add_resource(res, resource, priority)\n    end\n  end\n\n  def add_resource(res, parent_resource, priority = nil)\n    if @catalog.resource(res.ref).nil?\n      res.merge_tags_from(parent_resource)\n      if parent_resource.depthfirst?\n        @catalog.add_resource_before(parent_resource, res)\n      else\n        @catalog.add_resource_after(parent_resource, res)\n      end\n      @catalog.add_edge(@catalog.container_of(parent_resource), res) if @catalog.container_of(parent_resource)\n      if @relationship_graph && priority\n        # If we have a relationship_graph we should add the resource\n        # to it (this is an eval_generate). If we don't, then the\n        # relationship_graph has not yet been created and we can skip\n        # adding it.\n        @relationship_graph.add_vertex(res, priority)\n      end\n      res.finish\n    end\n  end\n\n  # add correct edge for depth- or breadth- first traversal of\n  # generated resource. Skip generating the edge if there is already\n  # some sort of edge between the two resources.\n  def add_generated_directed_dependency(parent, child, label = nil)\n    if parent.depthfirst?\n      source = child\n      target = parent\n    else\n      source = parent\n      target = child\n    end\n\n    # For each potential relationship metaparam, check if parent or\n    # child references the other. If there are none, we should add our\n    # edge.\n    edge_exists = Puppet::Type.relationship_params.any? { |param|\n      param_sym = param.name.to_sym\n\n      if parent[param_sym]\n        parent_contains = parent[param_sym].any? { |res|\n          res.ref == child.ref\n        }\n      else\n        parent_contains = false\n      end\n\n      if child[param_sym]\n        child_contains = child[param_sym].any? { |res|\n          res.ref == parent.ref\n        }\n      else\n        child_contains = false\n      end\n\n      parent_contains || child_contains\n    }\n\n    unless edge_exists\n      # We *cannot* use target.to_resource here!\n      #\n      # For reasons that are beyond my (and, perhaps, human)\n      # comprehension, to_resource will call retrieve. This is\n      # problematic if a generated resource needs the system to be\n      # changed by a previous resource (think a file on a path\n      # controlled by a mount resource).\n      #\n      # Instead of using to_resource, we just construct a resource as\n      # if the arguments to the Type instance had been passed to a\n      # Resource instead.\n      resource = ::Puppet::Resource.new(target.class, target.title,\n                                        :parameters => target.original_parameters)\n\n      source[:before] ||= []\n      source[:before] << resource\n    end\n  end\n\n  # Copy an important relationships from the parent to the newly-generated\n  # child resource.\n  def add_conditional_directed_dependency(parent, child, label = nil)\n    @relationship_graph.add_vertex(child)\n    edge = parent.depthfirst? ? [child, parent] : [parent, child]\n    if @relationship_graph.edge?(*edge.reverse)\n      parent.debug \"Skipping automatic relationship to #{child}\"\n    else\n      @relationship_graph.add_relationship(edge[0], edge[1], label)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/transaction/event.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/transaction'\nrequire_relative '../../puppet/util/tagging'\nrequire_relative '../../puppet/util/logging'\nrequire_relative '../../puppet/network/format_support'\n\n# A simple struct for storing what happens on the system.\nclass Puppet::Transaction::Event\n  include Puppet::Util::Tagging\n  include Puppet::Util::Logging\n  include Puppet::Network::FormatSupport\n\n  ATTRIBUTES = [:name, :resource, :property, :previous_value, :desired_value, :historical_value, :status, :message, :file, :line, :source_description, :audited, :invalidate_refreshes, :redacted, :corrective_change]\n  attr_accessor(*ATTRIBUTES)\n  attr_accessor :time\n  attr_reader :default_log_level\n\n  EVENT_STATUSES = %w[noop success failure audit]\n\n  def self.from_data_hash(data)\n    obj = allocate\n    obj.initialize_from_hash(data)\n    obj\n  end\n\n  def initialize(audited: false,\n                 corrective_change: false,\n                 desired_value: nil,\n                 file: nil,\n                 historical_value: nil,\n                 invalidate_refreshes: nil,\n                 line: nil,\n                 message: nil,\n                 name: nil,\n                 previous_value: nil,\n                 property: nil,\n                 redacted: false,\n                 resource: nil,\n                 source_description: nil,\n                 status: nil,\n                 tags: nil)\n\n    @audited = audited\n    @corrective_change = corrective_change\n    @desired_value = desired_value\n    @file = file\n    @historical_value = historical_value\n    @invalidate_refreshes = invalidate_refreshes\n    @line = line\n    @message = message\n    @name = name\n    @previous_value = previous_value\n    @redacted = redacted\n    @source_description = source_description\n    @tags = tags\n\n    self.property = property if property\n    self.resource = resource if resource\n    self.status = status if status\n\n    @time = Time.now\n  end\n\n  def eql?(event)\n    self.class == event.class && ATTRIBUTES.all? { |attr| send(attr).eql?(event.send(attr)) }\n  end\n  alias == eql?\n\n  def initialize_from_hash(data)\n    data = Puppet::Pops::Serialization::FromDataConverter.convert(data, {\n                                                                    :allow_unresolved => true,\n                                                                    :loader => Puppet::Pops::Loaders.static_loader\n                                                                  })\n    @audited = data['audited']\n    @property = data['property']\n    @previous_value = data['previous_value']\n    @desired_value = data['desired_value']\n    @historical_value = data['historical_value']\n    @message = data['message']\n    @name = data['name'].intern if data['name']\n    @status = data['status']\n    @time = data['time']\n    @time = Time.parse(@time) if @time.is_a? String\n    @redacted = data.fetch('redacted', false)\n    @corrective_change = data['corrective_change']\n  end\n\n  def to_data_hash\n    hash = {\n      'audited' => @audited,\n      'property' => @property,\n      'previous_value' => @previous_value,\n      'desired_value' => @desired_value,\n      'historical_value' => @historical_value,\n      'message' => @message,\n      'name' => @name.nil? ? nil : @name.to_s,\n      'status' => @status,\n      'time' => @time.iso8601(9),\n      'redacted' => @redacted,\n      'corrective_change' => @corrective_change,\n    }\n    # Use the stringifying converter since rich data is not possible downstream.\n    # (This will destroy some data type information, but this is expected).\n    Puppet::Pops::Serialization::ToStringifiedConverter.convert(hash, :message_prefix => 'Event')\n  end\n\n  def property=(prop)\n    @property_instance = prop\n    @property = prop.to_s\n  end\n\n  def resource=(res)\n    level = res[:loglevel] if res.respond_to?(:[])\n    if level\n      @default_log_level = level\n    end\n    @resource = res.to_s\n  end\n\n  def send_log\n    super(log_level, message)\n  end\n\n  def status=(value)\n    raise ArgumentError, _(\"Event status can only be %{statuses}\") % { statuses: EVENT_STATUSES.join(', ') } unless EVENT_STATUSES.include?(value)\n\n    @status = value\n  end\n\n  def to_s\n    message\n  end\n\n  def inspect\n    %Q(#<#{self.class.name} @name=\"#{@name.inspect}\" @message=\"#{@message.inspect}\">)\n  end\n\n  # Calculate and set the corrective_change parameter, based on the old_system_value of the property.\n  # @param [Object] old_system_value system_value from last transaction\n  # @return [bool] true if this is a corrective_change\n  def calculate_corrective_change(old_system_value)\n    # Only idempotent properties, and cases where we have an old system_value\n    # are corrective_changes.\n    if @property_instance.idempotent? &&\n       !@property_instance.sensitive &&\n       !old_system_value.nil?\n\n      # If the values aren't insync, we have confirmed a corrective_change\n      insync = @property_instance.insync_values?(old_system_value, previous_value)\n\n      # Preserve the nil state, but flip true/false\n      @corrective_change = insync.nil? ? nil : !insync\n    else\n      @corrective_change = false\n    end\n  end\n\n  private\n\n  # If it's a failure, use 'err', else use either the resource's log level (if available)\n  # or 'notice'.\n  def log_level\n    status == \"failure\" ? :err : (@default_log_level || :notice)\n  end\n\n  # Used by the Logging module\n  def log_source\n    source_description || property || resource\n  end\nend\n"
  },
  {
    "path": "lib/puppet/transaction/event_manager.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/transaction'\n\n# This class stores, routes, and responds to events generated while evaluating\n# a transaction.\n#\n# @api private\nclass Puppet::Transaction::EventManager\n  # @!attribute [r] transaction\n  #   @return [Puppet::Transaction] The transaction associated with this event manager.\n  attr_reader :transaction\n\n  # @!attribute [r] events\n  #   @todo Determine if this instance variable is used for anything aside from testing.\n  #   @return [Array<Puppet::Transaction::Events>] A list of events that can be\n  #     handled by the target resource. Events that cannot be handled by the\n  #     target resource will be discarded.\n  attr_reader :events\n\n  def initialize(transaction)\n    @transaction = transaction\n    @event_queues = {}\n    @events = []\n  end\n\n  def relationship_graph\n    transaction.relationship_graph\n  end\n\n  # Respond to any queued events for this resource.\n  def process_events(resource)\n    restarted = false\n    queued_events(resource) do |callback, events|\n      r = process_callback(resource, callback, events)\n      restarted ||= r\n    end\n\n    if restarted\n      queue_events(resource, [resource.event(:name => :restarted, :status => \"success\")])\n\n      transaction.resource_status(resource).restarted = true\n    end\n  end\n\n  # Queues events for other resources to respond to.  All of these events have\n  # to be from the same resource.\n  #\n  # @param resource [Puppet::Type] The resource generating the given events\n  # @param events [Array<Puppet::Transaction::Event>] All events generated by this resource\n  # @return [void]\n  def queue_events(resource, events)\n    # @events += events\n\n    # Do some basic normalization so we're not doing so many\n    # graph queries for large sets of events.\n    events.each_with_object({}) do |event, collection|\n      collection[event.name] ||= []\n      collection[event.name] << event\n    end.collect do |_name, list|\n      # It doesn't matter which event we use - they all have the same source\n      # and name here.\n      event = list[0]\n\n      # Collect the targets of any subscriptions to those events.  We pass\n      # the parent resource in so it will override the source in the events,\n      # since eval_generated children can't have direct relationships.\n      received = (event.name != :restarted)\n      relationship_graph.matching_edges(event, resource).each do |edge|\n        received ||= true unless edge.target.is_a?(Puppet::Type.type(:whit))\n        method = edge.callback\n        next unless method\n        next unless edge.target.respond_to?(method)\n\n        queue_events_for_resource(resource, edge.target, method, list)\n      end\n      @events << event if received\n\n      queue_events_for_resource(resource, resource, :refresh, [event]) if resource.self_refresh? and !resource.deleting?\n    end\n\n    dequeue_events_for_resource(resource, :refresh) if events.detect(&:invalidate_refreshes)\n  end\n\n  def dequeue_all_events_for_resource(target)\n    callbacks = @event_queues[target]\n    if callbacks && !callbacks.empty?\n      target.info _(\"Unscheduling all events on %{target}\") % { target: target }\n      @event_queues[target] = {}\n    end\n  end\n\n  def dequeue_events_for_resource(target, callback)\n    target.info _(\"Unscheduling %{callback} on %{target}\") % { callback: callback, target: target }\n    @event_queues[target][callback] = [] if @event_queues[target]\n  end\n\n  def queue_events_for_resource(source, target, callback, events)\n    whit = Puppet::Type.type(:whit)\n\n    # The message that a resource is refreshing the completed-whit for its own class\n    # is extremely counter-intuitive. Basically everything else is easy to understand,\n    # if you suppress the whit-lookingness of the whit resources\n    refreshing_c_whit = target.is_a?(whit) && target.name =~ /^completed_/\n\n    if refreshing_c_whit\n      source.debug \"The container #{target} will propagate my #{callback} event\"\n    else\n      source.info _(\"Scheduling %{callback} of %{target}\") % { callback: callback, target: target }\n    end\n\n    @event_queues[target] ||= {}\n    @event_queues[target][callback] ||= []\n    @event_queues[target][callback].concat(events)\n  end\n\n  def queued_events(resource)\n    callbacks = @event_queues[resource]\n    return unless callbacks\n\n    callbacks.each do |callback, events|\n      yield callback, events unless events.empty?\n    end\n  end\n\n  private\n\n  # Should the callback for this resource be invoked?\n  # @param resource [Puppet::Type] The resource to be refreshed\n  # @param events [Array<Puppet::Transaction::Event>] A list of events\n  #   associated with this callback and resource.\n  # @return [true, false] Whether the callback should be run.\n  def process_callback?(resource, events)\n    !(events.all? { |e| e.status == \"noop\" } || resource.noop?)\n  end\n\n  # Processes callbacks for a given resource.\n  #\n  # @param resource [Puppet::Type] The resource receiving the callback.\n  # @param callback [Symbol] The name of the callback method that will be invoked.\n  # @param events [Array<Puppet::Transaction::Event>] A list of events\n  #   associated with this callback and resource.\n  # @return [true, false] Whether the callback was successfully run.\n  def process_callback(resource, callback, events)\n    unless process_callback?(resource, events)\n      process_noop_events(resource, callback, events)\n      return false\n    end\n\n    resource.send(callback)\n\n    unless resource.is_a?(Puppet::Type.type(:whit))\n      message = n_(\"Triggered '%{callback}' from %{count} event\", \"Triggered '%{callback}' from %{count} events\", events.length) % { count: events.length, callback: callback }\n      resource.notice message\n      add_callback_status_event(resource, callback, message, \"success\")\n    end\n\n    true\n  rescue => detail\n    resource_error_message = _(\"Failed to call %{callback}: %{detail}\") % { callback: callback, detail: detail }\n    resource.err(resource_error_message)\n    transaction.resource_status(resource).failed_to_restart = true\n    transaction.resource_status(resource).fail_with_event(resource_error_message)\n    resource.log_exception(detail)\n    false\n  end\n\n  def add_callback_status_event(resource, callback, message, status)\n    options = { message: message, status: status, name: callback.to_s }\n    event = resource.event options\n    transaction.resource_status(resource) << event if event\n  end\n\n  def process_noop_events(resource, callback, events)\n    resource.notice n_(\"Would have triggered '%{callback}' from %{count} event\", \"Would have triggered '%{callback}' from %{count} events\", events.length) % { count: events.length, callback: callback }\n\n    # And then add an event for it.\n    queue_events(resource, [resource.event(:status => \"noop\", :name => :noop_restart)])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/transaction/persistence.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\nrequire_relative '../../puppet/util/yaml'\n\n# A persistence store implementation for storing information between\n# transaction runs for the purposes of information inference (such\n# as calculating corrective_change).\n# @api private\nclass Puppet::Transaction::Persistence\n  def self.allowed_classes\n    @allowed_classes ||= [\n      Symbol,\n      Time,\n      Regexp,\n      # URI is excluded, because it serializes all instance variables including the\n      # URI parser. Better to serialize the URL encoded representation.\n      SemanticPuppet::Version,\n      # SemanticPuppet::VersionRange has many nested classes and is unlikely to be\n      # used directly, so ignore it\n      Puppet::Pops::Time::Timestamp,\n      Puppet::Pops::Time::TimeData,\n      Puppet::Pops::Time::Timespan,\n      Puppet::Pops::Types::PBinaryType::Binary\n      # Puppet::Pops::Types::PSensitiveType::Sensitive values are excluded from\n      # the persistence store, ignore it.\n    ].freeze\n  end\n\n  def initialize\n    @old_data = {}\n    @new_data = { \"resources\" => {} }\n  end\n\n  # Obtain the full raw data from the persistence store.\n  # @return [Hash] hash of data stored in persistence store\n  def data\n    @old_data\n  end\n\n  # Retrieve the system value using the resource and parameter name\n  # @param [String] resource_name name of resource\n  # @param [String] param_name name of the parameter\n  # @return [Object,nil] the system_value\n  def get_system_value(resource_name, param_name)\n    if !@old_data[\"resources\"].nil? &&\n       !@old_data[\"resources\"][resource_name].nil? &&\n       !@old_data[\"resources\"][resource_name][\"parameters\"].nil? &&\n       !@old_data[\"resources\"][resource_name][\"parameters\"][param_name].nil?\n      @old_data[\"resources\"][resource_name][\"parameters\"][param_name][\"system_value\"]\n    else\n      nil\n    end\n  end\n\n  def set_system_value(resource_name, param_name, value)\n    @new_data[\"resources\"] ||= {}\n    @new_data[\"resources\"][resource_name] ||= {}\n    @new_data[\"resources\"][resource_name][\"parameters\"] ||= {}\n    @new_data[\"resources\"][resource_name][\"parameters\"][param_name] ||= {}\n    @new_data[\"resources\"][resource_name][\"parameters\"][param_name][\"system_value\"] = value\n  end\n\n  def copy_skipped(resource_name)\n    @old_data[\"resources\"] ||= {}\n    old_value = @old_data[\"resources\"][resource_name]\n    unless old_value.nil?\n      @new_data[\"resources\"][resource_name] = old_value\n    end\n  end\n\n  # Load data from the persistence store on disk.\n  def load\n    filename = Puppet[:transactionstorefile]\n    unless Puppet::FileSystem.exist?(filename)\n      return\n    end\n\n    unless File.file?(filename)\n      Puppet.warning(_(\"Transaction store file %{filename} is not a file, ignoring\") % { filename: filename })\n      return\n    end\n\n    result = nil\n    Puppet::Util.benchmark(:debug, _(\"Loaded transaction store file in %{seconds} seconds\")) do\n      result = Puppet::Util::Yaml.safe_load_file(filename, self.class.allowed_classes)\n    rescue Puppet::Util::Yaml::YamlLoadError => detail\n      Puppet.log_exception(detail, _(\"Transaction store file %{filename} is corrupt (%{detail}); replacing\") % { filename: filename, detail: detail })\n\n      begin\n        File.rename(filename, filename + \".bad\")\n      rescue => detail\n        Puppet.log_exception(detail, _(\"Unable to rename corrupt transaction store file: %{detail}\") % { detail: detail })\n        raise Puppet::Error, _(\"Could not rename corrupt transaction store file %{filename}; remove manually\") % { filename: filename }, detail.backtrace\n      end\n\n      result = {}\n    end\n\n    unless result.is_a?(Hash)\n      Puppet.err _(\"Transaction store file %{filename} is valid YAML but not returning a hash. Check the file for corruption, or remove it before continuing.\") % { filename: filename }\n      return\n    end\n\n    @old_data = result\n  end\n\n  # Save data from internal class to persistence store on disk.\n  def save\n    Puppet::Util::Yaml.dump(@new_data, Puppet[:transactionstorefile])\n  end\n\n  # Use the catalog and run_mode to determine if persistence should be enabled or not\n  # @param [Puppet::Resource::Catalog] catalog catalog being processed\n  # @return [boolean] true if persistence is enabled\n  def enabled?(catalog)\n    catalog.host_config? && Puppet.run_mode.name == :agent\n  end\nend\n"
  },
  {
    "path": "lib/puppet/transaction/report.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/indirector'\n\n# This class is used to report what happens on a client.\n# There are two types of data in a report; _Logs_ and _Metrics_.\n#\n# * **Logs** - are the output that each change produces.\n# * **Metrics** - are all of the numerical data involved in the transaction.\n#\n# Use {Puppet::Reports} class to create a new custom report type. This class is indirectly used\n# as a source of data to report in such a registered report.\n#\n# ##Metrics\n# There are three types of metrics in each report, and each type of metric has one or more values.\n#\n# * Time: Keeps track of how long things took.\n#   * Total: Total time for the configuration run\n#   * File:\n#   * Exec:\n#   * User:\n#   * Group:\n#   * Config Retrieval: How long the configuration took to retrieve\n#   * Service:\n#   * Package:\n# * Resources: Keeps track of the following stats:\n#   * Total: The total number of resources being managed\n#   * Skipped: How many resources were skipped, because of either tagging or scheduling restrictions\n#   * Scheduled: How many resources met any scheduling restrictions\n#   * Out of Sync: How many resources were out of sync\n#   * Applied: How many resources were attempted to be fixed\n#   * Failed: How many resources were not successfully fixed\n#   * Restarted: How many resources were restarted because their dependencies changed\n#   * Failed Restarts: How many resources could not be restarted\n# * Changes: The total number of changes in the transaction.\n#\n# @api public\nclass Puppet::Transaction::Report\n  include Puppet::Util::PsychSupport\n  extend Puppet::Indirector\n\n  STATES_FOR_EXCLUSION_FROM_REPORT = [:failed, :failed_to_restart, :out_of_sync, :skipped].freeze\n  indirects :report, :terminus_class => :processor\n\n  # The version of the configuration\n  # @todo Uncertain what this is?\n  # @return [???] the configuration version\n  attr_accessor :configuration_version\n\n  # An agent generated transaction uuid, useful for connecting catalog and report\n  # @return [String] uuid\n  attr_accessor :transaction_uuid\n\n  # The id of the code input to the compiler.\n  attr_accessor :code_id\n\n  # The id of the job responsible for this run.\n  attr_accessor :job_id\n\n  # A master generated catalog uuid, useful for connecting a single catalog to multiple reports.\n  attr_accessor :catalog_uuid\n\n  # Whether a cached catalog was used in the run, and if so, the reason that it was used.\n  # @return [String] One of the values: 'not_used', 'explicitly_requested',\n  # or 'on_failure'\n  attr_accessor :cached_catalog_status\n\n  # Contains the name and port of the server that was successfully contacted\n  # @return [String] a string of the format 'servername:port'\n  attr_accessor :server_used\n\n  # The host name for which the report is generated\n  # @return [String] the host name\n  attr_accessor :host\n\n  # The name of the environment the host is in\n  # @return [String] the environment name\n  attr_accessor :environment\n\n  # The name of the environment the agent initially started in\n  # @return [String] the environment name\n  attr_accessor :initial_environment\n\n  # Whether there are changes that we decided not to apply because of noop\n  # @return [Boolean]\n  #\n  attr_accessor :noop_pending\n\n  # A hash with a map from resource to status\n  # @return [Hash{String => Puppet::Resource::Status}] Resource name to status.\n  attr_reader :resource_statuses\n\n  # A list of log messages.\n  # @return [Array<Puppet::Util::Log>] logged messages\n  attr_reader :logs\n\n  # A hash of metric name to metric value.\n  # @return [Hash<{String => Object}>] A map of metric name to value.\n  # @todo Uncertain if all values are numbers - now marked as Object.\n  #\n  attr_reader :metrics\n\n  # The time when the report data was generated.\n  # @return [Time] A time object indicating when the report data was generated\n  #\n  attr_reader :time\n\n  # The status of the client run is an enumeration: 'failed', 'changed' or 'unchanged'\n  # @return [String] the status of the run - one of the values 'failed', 'changed', or 'unchanged'\n  #\n  attr_reader :status\n\n  # @return [String] The Puppet version in String form.\n  # @see Puppet::version()\n  #\n  attr_reader :puppet_version\n\n  # @return [Integer] report format version number.  This value is constant for\n  #    a given version of Puppet; it is incremented when a new release of Puppet\n  #    changes the API for the various objects that make up a report.\n  #\n  attr_reader :report_format\n\n  # Whether the puppet run was started in noop mode\n  # @return [Boolean]\n  #\n  attr_reader :noop\n\n  # @!attribute [r] corrective_change\n  #   @return [Boolean] true if the report contains any events and resources that had\n  #      corrective changes, including noop corrective changes.\n  attr_reader :corrective_change\n\n  # @return [Boolean] true if one or more resources attempted to generate\n  #   resources and failed\n  #\n  attr_accessor :resources_failed_to_generate\n\n  # @return [Boolean] true if the transaction completed it's evaluate\n  #\n  attr_accessor :transaction_completed\n\n  TOTAL = \"total\"\n\n  def self.from_data_hash(data)\n    obj = allocate\n    obj.initialize_from_hash(data)\n    obj\n  end\n\n  def as_logging_destination(&block)\n    Puppet::Util::Log.with_destination(self, &block)\n  end\n\n  # @api private\n  def <<(msg)\n    @logs << msg\n    self\n  end\n\n  # @api private\n  def add_times(name, value, accumulate = true)\n    if @external_times[name] && accumulate\n      @external_times[name] += value\n    else\n      @external_times[name] = value\n    end\n  end\n\n  # @api private\n  def add_metric(name, hash)\n    metric = Puppet::Util::Metric.new(name)\n\n    hash.each do |metric_name, value|\n      metric.newvalue(metric_name, value)\n    end\n\n    @metrics[metric.name] = metric\n    metric\n  end\n\n  # @api private\n  def add_resource_status(status)\n    @resource_statuses[status.resource] = status\n  end\n\n  # @api private\n  def compute_status(resource_metrics, change_metric)\n    if resources_failed_to_generate ||\n       !transaction_completed ||\n       (resource_metrics[\"failed\"] || 0) > 0 ||\n       (resource_metrics[\"failed_to_restart\"] || 0) > 0\n      'failed'\n    elsif change_metric > 0\n      'changed'\n    else\n      'unchanged'\n    end\n  end\n\n  # @api private\n  def has_noop_events?(resource)\n    resource.events.any? { |event| event.status == 'noop' }\n  end\n\n  # @api private\n  def prune_internal_data\n    resource_statuses.delete_if { |_name, res| res.resource_type == 'Whit' }\n  end\n\n  # @api private\n  def finalize_report\n    prune_internal_data\n    calculate_report_corrective_change\n\n    resource_metrics = add_metric(:resources, calculate_resource_metrics)\n    add_metric(:time, calculate_time_metrics)\n    change_metric = calculate_change_metric\n    add_metric(:changes, { TOTAL => change_metric })\n    add_metric(:events, calculate_event_metrics)\n    @status = compute_status(resource_metrics, change_metric)\n    @noop_pending = @resource_statuses.any? { |_name, res| has_noop_events?(res) }\n  end\n\n  # @api private\n  def initialize(configuration_version = nil, environment = nil, transaction_uuid = nil, job_id = nil, start_time = Time.now)\n    @metrics = {}\n    @logs = []\n    @resource_statuses = {}\n    @external_times ||= {}\n    @host = Puppet[:node_name_value]\n    @time = start_time\n    @report_format = 12\n    @puppet_version = Puppet.version\n    @configuration_version = configuration_version\n    @transaction_uuid = transaction_uuid\n    @code_id = nil\n    @job_id = job_id\n    @catalog_uuid = nil\n    @cached_catalog_status = nil\n    @server_used = nil\n    @environment = environment\n    @status = 'failed' # assume failed until the report is finalized\n    @noop = Puppet[:noop]\n    @noop_pending = false\n    @corrective_change = false\n    @transaction_completed = false\n  end\n\n  # @api private\n  def initialize_from_hash(data)\n    @puppet_version = data['puppet_version']\n    @report_format = data['report_format']\n    @configuration_version = data['configuration_version']\n    @transaction_uuid = data['transaction_uuid']\n    @environment = data['environment']\n    @status = data['status']\n    @transaction_completed = data['transaction_completed']\n    @noop = data['noop']\n    @noop_pending = data['noop_pending']\n    @host = data['host']\n    @time = data['time']\n    @corrective_change = data['corrective_change']\n\n    if data['server_used']\n      @server_used = data['server_used']\n    elsif data['master_used']\n      @server_used = data['master_used']\n    end\n\n    if data['catalog_uuid']\n      @catalog_uuid = data['catalog_uuid']\n    end\n\n    if data['job_id']\n      @job_id = data['job_id']\n    end\n\n    if data['code_id']\n      @code_id = data['code_id']\n    end\n\n    if data['cached_catalog_status']\n      @cached_catalog_status = data['cached_catalog_status']\n    end\n\n    if @time.is_a? String\n      @time = Time.parse(@time)\n    end\n\n    @metrics = {}\n    data['metrics'].each do |name, hash|\n      # Older versions contain tags that causes Psych to create instances directly\n      @metrics[name] = hash.is_a?(Puppet::Util::Metric) ? hash : Puppet::Util::Metric.from_data_hash(hash)\n    end\n\n    @logs = data['logs'].map do |record|\n      # Older versions contain tags that causes Psych to create instances directly\n      record.is_a?(Puppet::Util::Log) ? record : Puppet::Util::Log.from_data_hash(record)\n    end\n\n    @resource_statuses = {}\n    data['resource_statuses'].map do |key, rs|\n      @resource_statuses[key] =\n        if rs == Puppet::Resource::EMPTY_HASH\n          nil\n        elsif rs.is_a?(Puppet::Resource::Status)\n          # Older versions contain tags that causes Psych to create instances\n          # directly\n          rs\n        else\n          Puppet::Resource::Status.from_data_hash(rs)\n        end\n    end\n  end\n\n  def resource_unchanged?(rs)\n    STATES_FOR_EXCLUSION_FROM_REPORT.each do |state|\n      return false if rs.send(state)\n    end\n    true\n  end\n\n  def calculate_resource_statuses\n    resource_statuses = if Puppet[:exclude_unchanged_resources]\n                          @resource_statuses.reject { |_key, rs| resource_unchanged?(rs) }\n                        else\n                          @resource_statuses\n                        end\n    resource_statuses.transform_values { |rs| rs.nil? ? nil : rs.to_data_hash }\n  end\n\n  def to_data_hash\n    hash = {\n      'host' => @host,\n      'time' => @time.iso8601(9),\n      'configuration_version' => @configuration_version,\n      'transaction_uuid' => @transaction_uuid,\n      'report_format' => @report_format,\n      'puppet_version' => @puppet_version,\n      'status' => @status,\n      'transaction_completed' => @transaction_completed,\n      'noop' => @noop,\n      'noop_pending' => @noop_pending,\n      'environment' => @environment,\n      'logs' => @logs.map(&:to_data_hash),\n      'metrics' => @metrics.transform_values(&:to_data_hash),\n      'resource_statuses' => calculate_resource_statuses,\n      'corrective_change' => @corrective_change,\n    }\n\n    # The following is include only when set\n    hash['server_used'] = @server_used unless @server_used.nil?\n    hash['catalog_uuid'] = @catalog_uuid unless @catalog_uuid.nil?\n    hash['code_id'] = @code_id unless @code_id.nil?\n    hash['job_id'] = @job_id unless @job_id.nil?\n    hash['cached_catalog_status'] = @cached_catalog_status unless @cached_catalog_status.nil?\n    hash\n  end\n\n  # @return [String] the host name\n  # @api public\n  #\n  def name\n    host\n  end\n\n  # Provide a human readable textual summary of this report.\n  # @note This is intended for debugging purposes\n  # @return [String] A string with a textual summary of this report.\n  # @api public\n  #\n  def summary\n    report = raw_summary\n\n    ret = ''.dup\n    report.keys.sort_by(&:to_s).each do |key|\n      ret += \"#{Puppet::Util::Metric.labelize(key)}:\\n\"\n\n      report[key].keys.sort { |a, b|\n        # sort by label\n        if a == TOTAL\n          1\n        elsif b == TOTAL\n          -1\n        else\n          report[key][a].to_s <=> report[key][b].to_s\n        end\n      }.each do |label|\n        value = report[key][label]\n        next if value == 0\n\n        value = \"%0.2f\" % value if value.is_a?(Float)\n        ret += \"   %15s %s\\n\" % [Puppet::Util::Metric.labelize(label) + \":\", value]\n      end\n    end\n    ret\n  end\n\n  # Provides a raw hash summary of this report.\n  # @return [Hash<{String => Object}>] A hash with metrics key to value map\n  # @api public\n  #\n  def raw_summary\n    report = {\n      \"version\" => {\n        \"config\" => configuration_version,\n        \"puppet\" => Puppet.version\n      },\n      \"application\" => {\n        \"run_mode\" => Puppet.run_mode.name.to_s,\n        \"initial_environment\" => initial_environment,\n        \"converged_environment\" => environment\n      }\n    }\n\n    @metrics.each do |_name, metric|\n      key = metric.name.to_s\n      report[key] = {}\n      metric.values.each do |metric_name, _label, value|\n        report[key][metric_name.to_s] = value\n      end\n      report[key][TOTAL] = 0 unless key == \"time\" or report[key].include?(TOTAL)\n    end\n    (report[\"time\"] ||= {})[\"last_run\"] = Time.now.tv_sec\n    report\n  end\n\n  # Computes a single number that represents the report's status.\n  # The computation is based on the contents of this report's metrics.\n  # The resulting number is a bitmask where\n  # individual bits represent the presence of different metrics.\n  #\n  # * 0x2 set if there are changes\n  # * 0x4 set if there are resource failures or resources that failed to restart\n  # @return [Integer] A bitmask where 0x2 is set if there are changes, and 0x4 is set of there are failures.\n  # @api public\n  #\n  def exit_status\n    status = 0\n    if @metrics[\"changes\"] && @metrics[\"changes\"][TOTAL] &&\n       @metrics[\"resources\"] && @metrics[\"resources\"][\"failed\"] &&\n       @metrics[\"resources\"][\"failed_to_restart\"]\n      status |= 2 if @metrics[\"changes\"][TOTAL] > 0\n      status |= 4 if @metrics[\"resources\"][\"failed\"] > 0\n      status |= 4 if @metrics[\"resources\"][\"failed_to_restart\"] > 0\n    else\n      status = -1\n    end\n    status\n  end\n\n  private\n\n  # Mark the report as corrective, if there are any resource_status marked corrective.\n  def calculate_report_corrective_change\n    @corrective_change = resource_statuses.any? do |_name, status|\n      status.corrective_change\n    end\n  end\n\n  def calculate_change_metric\n    resource_statuses.map { |_name, status| status.change_count || 0 }.inject(0) { |a, b| a + b }\n  end\n\n  def calculate_event_metrics\n    metrics = Hash.new(0)\n    %w[total failure success].each { |m| metrics[m] = 0 }\n    resource_statuses.each do |_name, status|\n      metrics[TOTAL] += status.events.length\n      status.events.each do |event|\n        metrics[event.status] += 1\n      end\n    end\n\n    metrics\n  end\n\n  def calculate_resource_metrics\n    metrics = {}\n    metrics[TOTAL] = resource_statuses.length\n\n    # force every resource key in the report to be present\n    # even if no resources is in this given state\n    Puppet::Resource::Status::STATES.each do |state|\n      metrics[state.to_s] = 0\n    end\n\n    resource_statuses.each do |_name, status|\n      Puppet::Resource::Status::STATES.each do |state|\n        metrics[state.to_s] += 1 if status.send(state)\n      end\n    end\n\n    metrics\n  end\n\n  def calculate_time_metrics\n    metrics = Hash.new(0)\n    resource_statuses.each do |_name, status|\n      metrics[status.resource_type.downcase] += status.evaluation_time if status.evaluation_time\n    end\n\n    @external_times.each do |name, value|\n      metrics[name.to_s.downcase] = value\n    end\n\n    metrics\n  end\nend\n"
  },
  {
    "path": "lib/puppet/transaction/resource_harness.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/resource/status'\n\nclass Puppet::Transaction::ResourceHarness\n  NO_ACTION = Object.new\n\n  extend Forwardable\n  def_delegators :@transaction, :relationship_graph\n\n  attr_reader :transaction\n\n  def initialize(transaction)\n    @transaction = transaction\n    @persistence = transaction.persistence\n  end\n\n  def evaluate(resource)\n    status = Puppet::Resource::Status.new(resource)\n\n    begin\n      context = ResourceApplicationContext.from_resource(resource, status)\n      perform_changes(resource, context)\n\n      if status.changed? && !resource.noop?\n        cache(resource, :synced, Time.now)\n        resource.flush if resource.respond_to?(:flush)\n      end\n    rescue => detail\n      status.failed_because(detail)\n    ensure\n      status.evaluation_time = Time.now - status.time\n    end\n\n    status\n  end\n\n  def scheduled?(resource)\n    return true if Puppet[:ignoreschedules]\n\n    schedule = schedule(resource)\n    return true unless schedule\n\n    # We use 'checked' here instead of 'synced' because otherwise we'll\n    # end up checking most resources most times, because they will generally\n    # have been synced a long time ago (e.g., a file only gets updated\n    # once a month on the server and its schedule is daily; the last sync time\n    # will have been a month ago, so we'd end up checking every run).\n    schedule.match?(cached(resource, :checked).to_i)\n  end\n\n  def schedule(resource)\n    unless resource.catalog\n      resource.warning _(\"Cannot schedule without a schedule-containing catalog\")\n      return nil\n    end\n\n    name = resource[:schedule]\n    return nil unless name\n\n    resource.catalog.resource(:schedule, name) || resource.fail(_(\"Could not find schedule %{name}\") % { name: name })\n  end\n\n  # Used mostly for scheduling and auditing at this point.\n  def cached(resource, name)\n    Puppet::Util::Storage.cache(resource)[name]\n  end\n\n  # Used mostly for scheduling and auditing at this point.\n  def cache(resource, name, value)\n    Puppet::Util::Storage.cache(resource)[name] = value\n  end\n\n  private\n\n  def perform_changes(resource, context)\n    cache(resource, :checked, Time.now)\n\n    # Record the current state in state.yml.\n    context.audited_params.each do |param|\n      cache(resource, param, context.current_values[param])\n    end\n\n    ensure_param = resource.parameter(:ensure)\n    if ensure_param && ensure_param.should\n      ensure_event = sync_if_needed(ensure_param, context)\n    else\n      ensure_event = NO_ACTION\n    end\n\n    if ensure_event == NO_ACTION\n      if context.resource_present?\n        resource.properties.each do |param|\n          sync_if_needed(param, context)\n        end\n      else\n        resource.debug(\"Nothing to manage: no ensure and the resource doesn't exist\")\n      end\n    end\n\n    capture_audit_events(resource, context)\n    persist_system_values(resource, context)\n  end\n\n  # We persist the last known values for the properties of a resource after resource\n  # application.\n  # @param [Puppet::Type] resource resource whose values we are to persist.\n  # @param [ResourceApplicationContext] context the application context to operate on.\n  def persist_system_values(resource, context)\n    param_to_event = {}\n    context.status.events.each do |ev|\n      param_to_event[ev.property] = ev\n    end\n\n    context.system_value_params.each do |pname, param|\n      @persistence.set_system_value(resource.ref, pname.to_s,\n                                    new_system_value(param,\n                                                     param_to_event[pname.to_s],\n                                                     @persistence.get_system_value(resource.ref, pname.to_s)))\n    end\n  end\n\n  def sync_if_needed(param, context)\n    historical_value = context.historical_values[param.name]\n    current_value = context.current_values[param.name]\n    do_audit = context.audited_params.include?(param.name)\n\n    begin\n      if param.should && !param.safe_insync?(current_value)\n        event = create_change_event(param, current_value, historical_value)\n        if do_audit\n          event = audit_event(event, param, context)\n        end\n\n        brief_audit_message = audit_message(param, do_audit, historical_value, current_value)\n\n        if param.noop\n          noop(event, param, current_value, brief_audit_message)\n        else\n          sync(event, param, current_value, brief_audit_message)\n        end\n\n        event\n      else\n        NO_ACTION\n      end\n    rescue => detail\n      # Execution will continue on StandardErrors, just store the event\n      Puppet.log_exception(detail)\n\n      event = create_change_event(param, current_value, historical_value)\n      event.status = \"failure\"\n      event.message = param.format(_(\"change from %s to %s failed: \"),\n                                   param.is_to_s(current_value),\n                                   param.should_to_s(param.should)) + detail.to_s\n      event\n    rescue Exception => detail\n      # Execution will halt on Exceptions, they get raised to the application\n      event = create_change_event(param, current_value, historical_value)\n      event.status = \"failure\"\n      event.message = param.format(_(\"change from %s to %s failed: \"),\n                                   param.is_to_s(current_value),\n                                   param.should_to_s(param.should)) + detail.to_s\n      raise\n    ensure\n      if event\n        name = param.name.to_s\n        event.message ||= _(\"could not create change error message for %{name}\") % { name: name }\n        event.calculate_corrective_change(@persistence.get_system_value(context.resource.ref, name))\n        event.message << ' (corrective)' if event.corrective_change\n        context.record(event)\n        event.send_log\n        context.synced_params << param.name\n      end\n    end\n  end\n\n  def create_change_event(property, current_value, historical_value)\n    options = {}\n    should = property.should\n\n    if property.sensitive\n      options[:previous_value] = current_value.nil? ? nil : '[redacted]'\n      options[:desired_value] = should.nil? ? nil : '[redacted]'\n      options[:historical_value] = historical_value.nil? ? nil : '[redacted]'\n    else\n      options[:previous_value] = current_value\n      options[:desired_value] = should\n      options[:historical_value] = historical_value\n    end\n\n    property.event(options)\n  end\n\n  # This method is an ugly hack because, given a Time object with nanosecond\n  # resolution, roundtripped through YAML serialization, the Time object will\n  # be truncated to microseconds.\n  # For audit purposes, this code special cases this comparison, and compares\n  # the two objects by their second and microsecond components. tv_sec is the\n  # number of seconds since the epoch, and tv_usec is only the microsecond\n  # portion of time.\n  def are_audited_values_equal(a, b)\n    a == b || (a.is_a?(Time) && b.is_a?(Time) && a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec)\n  end\n  private :are_audited_values_equal\n\n  # Populate an existing event with audit information.\n  #\n  # @param event [Puppet::Transaction::Event] The event to be populated.\n  # @param property [Puppet::Property] The property being audited.\n  # @param context [ResourceApplicationContext]\n  #\n  # @return [Puppet::Transaction::Event] The given event, populated with the audit information.\n  def audit_event(event, property, context)\n    event.audited = true\n    event.status = \"audit\"\n\n    # The event we've been provided might have been redacted so we need to use the state stored within\n    # the resource application context to see if an event was actually generated.\n    unless are_audited_values_equal(context.historical_values[property.name], context.current_values[property.name])\n      event.message = property.format(_(\"audit change: previously recorded value %s has been changed to %s\"),\n                                      property.is_to_s(event.historical_value),\n                                      property.is_to_s(event.previous_value))\n    end\n\n    event\n  end\n\n  def audit_message(param, do_audit, historical_value, current_value)\n    if do_audit && historical_value && !are_audited_values_equal(historical_value, current_value)\n      param.format(_(\" (previously recorded value was %s)\"), param.is_to_s(historical_value))\n    else\n      \"\"\n    end\n  end\n\n  def noop(event, param, current_value, audit_message)\n    if param.sensitive\n      event.message = param.format(_(\"current_value %s, should be %s (noop)\"),\n                                   param.is_to_s(current_value),\n                                   param.should_to_s(param.should)) + audit_message.to_s\n    else\n      event.message = \"#{param.change_to_s(current_value, param.should)} (noop)#{audit_message}\"\n    end\n    event.status = \"noop\"\n  end\n\n  def sync(event, param, current_value, audit_message)\n    param.sync\n    if param.sensitive\n      event.message = param.format(_(\"changed %s to %s\"),\n                                   param.is_to_s(current_value),\n                                   param.should_to_s(param.should)) + audit_message.to_s\n    else\n      event.message = \"#{param.change_to_s(current_value, param.should)}#{audit_message}\"\n    end\n    event.status = \"success\"\n  end\n\n  def capture_audit_events(resource, context)\n    context.audited_params.each do |param_name|\n      if context.historical_values.include?(param_name)\n        if !are_audited_values_equal(context.historical_values[param_name], context.current_values[param_name]) && !context.synced_params.include?(param_name)\n          parameter = resource.parameter(param_name)\n          event = audit_event(create_change_event(parameter,\n                                                  context.current_values[param_name],\n                                                  context.historical_values[param_name]),\n                              parameter, context)\n          event.send_log\n          context.record(event)\n        end\n      else\n        property = resource.property(param_name)\n        property.notice(property.format(_(\"audit change: newly-recorded value %s\"), context.current_values[param_name]))\n      end\n    end\n  end\n\n  # Given an event and its property, calculate the system_value to persist\n  # for future calculations.\n  # @param [Puppet::Transaction::Event] event event to use for processing\n  # @param [Puppet::Property] property correlating property\n  # @param [Object] old_system_value system_value from last transaction\n  # @return [Object] system_value to be used for next transaction\n  def new_system_value(property, event, old_system_value)\n    if event && event.status != \"success\"\n      # For non-success events, we persist the old_system_value if it is defined,\n      # or use the event previous_value.\n      # If we're using the event previous_value, we ensure that it's\n      # an array. This is needed because properties assume that their\n      # `should` value is an array, and we will use this value later\n      # on in property insync? logic.\n      event_value = [event.previous_value] unless event.previous_value.is_a?(Array)\n      old_system_value.nil? ? event_value : old_system_value\n    else\n      # For non events, or for success cases, we just want to store\n      # the parameters agent value.\n      # We use instance_variable_get here because we want this process to bypass any\n      # munging/unmunging or validation that the property might try to do, since those\n      # operations may not be correctly implemented for custom types.\n      property.instance_variable_get(:@should)\n    end\n  end\n\n  # @api private\n  ResourceApplicationContext = Struct.new(:resource,\n                                          :current_values,\n                                          :historical_values,\n                                          :audited_params,\n                                          :synced_params,\n                                          :status,\n                                          :system_value_params) do\n    def self.from_resource(resource, status)\n      ResourceApplicationContext.new(resource,\n                                     resource.retrieve_resource.to_hash,\n                                     Puppet::Util::Storage.cache(resource).dup,\n                                     (resource[:audit] || []).map(&:to_sym),\n                                     [],\n                                     status,\n                                     resource.parameters.select { |_n, p| p.is_a?(Puppet::Property) && !p.sensitive })\n    end\n\n    def resource_present?\n      resource.present?(current_values)\n    end\n\n    def record(event)\n      status << event\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/transaction.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/util/tagging'\nrequire_relative '../puppet/util/skip_tags'\nrequire_relative '../puppet/application'\nrequire 'digest/sha1'\nrequire 'set'\n\n# the class that actually walks our resource/property tree, collects the changes,\n# and performs them\n#\n# @api private\nclass Puppet::Transaction\n  require_relative 'transaction/additional_resource_generator'\n  require_relative 'transaction/event'\n  require_relative 'transaction/event_manager'\n  require_relative 'transaction/resource_harness'\n  require_relative '../puppet/resource/status'\n  require_relative 'transaction/persistence'\n\n  attr_accessor :catalog, :ignoreschedules, :for_network_device\n\n  # The report, once generated.\n  attr_reader :report\n\n  # Routes and stores any events and subscriptions.\n  attr_reader :event_manager\n\n  # Handles most of the actual interacting with resources\n  attr_reader :resource_harness\n\n  attr_reader :prefetched_providers, :prefetch_failed_providers\n\n  # @!attribute [r] persistence\n  #   @return [Puppet::Transaction::Persistence] persistence object for cross\n  #      transaction storage.\n  attr_reader :persistence\n\n  include Puppet::Util\n  include Puppet::Util::Tagging\n\n  def initialize(catalog, report, prioritizer)\n    @catalog = catalog\n\n    @persistence = Puppet::Transaction::Persistence.new\n\n    @report = report || Puppet::Transaction::Report.new(catalog.version, catalog.environment)\n\n    @prioritizer = prioritizer\n\n    @report.add_times(:config_retrieval, @catalog.retrieval_duration || 0)\n\n    @event_manager = Puppet::Transaction::EventManager.new(self)\n\n    @resource_harness = Puppet::Transaction::ResourceHarness.new(self)\n\n    @prefetched_providers = Hash.new { |h, k| h[k] = {} }\n\n    @prefetch_failed_providers = Hash.new { |h, k| h[k] = {} }\n\n    # With merge_dependency_warnings, notify and warn about class dependency failures ... just once per class. TJK 2019-09-09\n    @merge_dependency_warnings = Puppet[:merge_dependency_warnings]\n    @failed_dependencies_already_notified = Set.new()\n    @failed_class_dependencies_already_notified = Set.new()\n    @failed_class_dependencies_already_warned = Set.new()\n  end\n\n  # Invoke the pre_run_check hook in every resource in the catalog.\n  # This should (only) be called by Transaction#evaluate before applying\n  # the catalog.\n  #\n  # @see Puppet::Transaction#evaluate\n  # @see Puppet::Type#pre_run_check\n  # @raise [Puppet::Error] If any pre-run checks failed.\n  # @return [void]\n  def perform_pre_run_checks\n    prerun_errors = {}\n\n    @catalog.vertices.each do |res|\n      res.pre_run_check\n    rescue Puppet::Error => detail\n      prerun_errors[res] = detail\n    end\n\n    unless prerun_errors.empty?\n      prerun_errors.each do |res, detail|\n        res.log_exception(detail)\n      end\n      raise Puppet::Error, _(\"Some pre-run checks failed\")\n    end\n  end\n\n  # This method does all the actual work of running a transaction.  It\n  # collects all of the changes, executes them, and responds to any\n  # necessary events.\n  def evaluate(&block)\n    block ||= method(:eval_resource)\n    generator = AdditionalResourceGenerator.new(@catalog, nil, @prioritizer)\n    @catalog.vertices.each { |resource| generator.generate_additional_resources(resource) }\n\n    perform_pre_run_checks\n\n    persistence.load if persistence.enabled?(catalog)\n\n    Puppet.info _(\"Applying configuration version '%{version}'\") % { version: catalog.version } if catalog.version\n\n    continue_while = -> { !stop_processing? }\n\n    post_evalable_providers = Set.new\n    pre_process = lambda do |resource|\n      prov_class = resource.provider.class\n      post_evalable_providers << prov_class if prov_class.respond_to?(:post_resource_eval)\n\n      prefetch_if_necessary(resource)\n\n      # If we generated resources, we don't know what they are now\n      # blocking, so we opt to recompute it, rather than try to track every\n      # change that would affect the number.\n      relationship_graph.clear_blockers if generator.eval_generate(resource)\n    end\n\n    providerless_types = []\n    overly_deferred_resource_handler = lambda do |resource|\n      # We don't automatically assign unsuitable providers, so if there\n      # is one, it must have been selected by the user.\n      return if missing_tags?(resource)\n\n      if resource.provider\n        resource.err _(\"Provider %{name} is not functional on this host\") % { name: resource.provider.class.name }\n      else\n        providerless_types << resource.type\n      end\n\n      resource_status(resource).failed = true\n    end\n\n    canceled_resource_handler = lambda do |resource|\n      resource_status(resource).skipped = true\n      resource.debug \"Transaction canceled, skipping\"\n    end\n\n    teardown = lambda do\n      # Just once per type. No need to punish the user.\n      providerless_types.uniq.each do |type|\n        Puppet.err _(\"Could not find a suitable provider for %{type}\") % { type: type }\n      end\n\n      post_evalable_providers.each do |provider|\n        provider.post_resource_eval\n      rescue => detail\n        Puppet.log_exception(detail, _(\"post_resource_eval failed for provider %{provider}\") % { provider: provider })\n      end\n\n      persistence.save if persistence.enabled?(catalog)\n    end\n\n    # Graph cycles are returned as an array of arrays\n    # - outer array is an array of cycles\n    # - each inner array is an array of resources involved in a cycle\n    # Short circuit resource evaluation if we detect cycle(s) in the graph. Mark\n    # each corresponding resource as failed in the report before we fail to\n    # ensure accurate reporting.\n    graph_cycle_handler = lambda do |cycles|\n      cycles.flatten.uniq.each do |resource|\n        # We add a failed resource event to the status to ensure accurate\n        # reporting through the event manager.\n        resource_status(resource).fail_with_event(_('resource is part of a dependency cycle'))\n      end\n      raise Puppet::Error, _('One or more resource dependency cycles detected in graph')\n    end\n\n    # Generate the relationship graph, set up our generator to use it\n    # for eval_generate, then kick off our traversal.\n    generator.relationship_graph = relationship_graph\n    progress = 0\n    relationship_graph.traverse(:while => continue_while,\n                                :pre_process => pre_process,\n                                :overly_deferred_resource_handler => overly_deferred_resource_handler,\n                                :canceled_resource_handler => canceled_resource_handler,\n                                :graph_cycle_handler => graph_cycle_handler,\n                                :teardown => teardown) do |resource|\n      progress += 1\n      if resource.is_a?(Puppet::Type::Component)\n        Puppet.warning _(\"Somehow left a component in the relationship graph\")\n      else\n        if Puppet[:evaltrace] && @catalog.host_config?\n          resource.info _(\"Starting to evaluate the resource (%{progress} of %{total})\") % { progress: progress, total: relationship_graph.size }\n        end\n        seconds = thinmark { block.call(resource) }\n        resource.info _(\"Evaluated in %{seconds} seconds\") % { seconds: \"%0.2f\" % seconds } if Puppet[:evaltrace] && @catalog.host_config?\n      end\n    end\n\n    # if one or more resources has attempted and failed to generate resources,\n    # report it\n    if generator.resources_failed_to_generate\n      report.resources_failed_to_generate = true\n    end\n\n    # mark the end of transaction evaluate.\n    report.transaction_completed = true\n\n    Puppet.debug { \"Finishing transaction #{object_id}\" }\n  end\n\n  # Wraps application run state check to flag need to interrupt processing\n  def stop_processing?\n    Puppet::Application.stop_requested? && catalog.host_config?\n  end\n\n  # Are there any failed resources in this transaction?\n  def any_failed?\n    report.resource_statuses.values.detect { |status|\n      status.failed? || status.failed_to_restart?\n    }\n  end\n\n  # Find all of the changed resources.\n  def changed?\n    report.resource_statuses.values.find_all(&:changed).collect { |status| catalog.resource(status.resource) }\n  end\n\n  def relationship_graph\n    catalog.relationship_graph(@prioritizer)\n  end\n\n  def resource_status(resource)\n    report.resource_statuses[resource.to_s] || add_resource_status(Puppet::Resource::Status.new(resource))\n  end\n\n  # The tags we should be checking.\n  def tags\n    self.tags = Puppet[:tags] unless defined?(@tags)\n\n    super\n  end\n\n  def skip_tags\n    @skip_tags ||= Puppet::Util::SkipTags.new(Puppet[:skip_tags]).tags\n  end\n\n  def prefetch_if_necessary(resource)\n    provider_class = resource.provider.class\n    if !provider_class.respond_to?(:prefetch) or\n       prefetched_providers[resource.type][provider_class.name] or\n       prefetch_failed_providers[resource.type][provider_class.name]\n      return\n    end\n\n    resources = resources_by_provider(resource.type, provider_class.name)\n\n    if provider_class == resource.class.defaultprovider\n      providerless_resources = resources_by_provider(resource.type, nil)\n      providerless_resources.values.each { |res| res.provider = provider_class.name }\n      resources.merge! providerless_resources\n    end\n\n    prefetch(provider_class, resources)\n  end\n\n  private\n\n  # Apply all changes for a resource\n  def apply(resource, ancestor = nil)\n    status = resource_harness.evaluate(resource)\n    add_resource_status(status)\n    ancestor ||= resource\n    unless status.failed? || status.failed_to_restart?\n      event_manager.queue_events(ancestor, status.events)\n    end\n  rescue => detail\n    resource.err _(\"Could not evaluate: %{detail}\") % { detail: detail }\n  end\n\n  # Evaluate a single resource.\n  def eval_resource(resource, ancestor = nil)\n    resolve_resource(resource)\n    propagate_failure(resource)\n    if skip?(resource)\n      resource_status(resource).skipped = true\n      resource.debug(\"Resource is being skipped, unscheduling all events\")\n      event_manager.dequeue_all_events_for_resource(resource)\n      persistence.copy_skipped(resource.ref)\n    else\n      resource_status(resource).scheduled = true\n      apply(resource, ancestor)\n      event_manager.process_events(resource)\n    end\n  end\n\n  # Does this resource have any failed dependencies?\n  def failed_dependencies?(resource)\n    # When we introduced the :whit into the graph, to reduce the combinatorial\n    # explosion of edges, we also ended up reporting failures for containers\n    # like class and stage.  This is undesirable; while just skipping the\n    # output isn't perfect, it is RC-safe. --daniel 2011-06-07\n    is_puppet_class = resource.instance_of?(Puppet::Type.type(:whit))\n    # With merge_dependency_warnings, notify about class dependency failures ... just once per class. TJK 2019-09-09\n    s = resource_status(resource)\n    if s && s.dependency_failed?\n      if @merge_dependency_warnings && is_puppet_class\n        # Notes: Puppet::Resource::Status.failed_dependencies() is an Array of\n        # Puppet::Resource(s) and Puppet::Resource.ref() calls\n        # Puppet::Resource.to_s() which is: \"#{type}[#{title}]\" and\n        # Puppet::Resource.resource_status(resource) calls\n        # Puppet::Resource.to_s()\n        class_dependencies_to_be_notified = (s.failed_dependencies.map(&:ref) - @failed_class_dependencies_already_notified.to_a)\n        class_dependencies_to_be_notified.each do |dep_ref|\n          resource.notice _(\"Class dependency %{dep} has failures: %{status}\") % { dep: dep_ref, status: resource_status(dep_ref).failed }\n        end\n        @failed_class_dependencies_already_notified.merge(class_dependencies_to_be_notified)\n      else\n        unless @merge_dependency_warnings || is_puppet_class\n          s.failed_dependencies.find_all { |d| !@failed_dependencies_already_notified.include?(d.ref) }.each do |dep|\n            resource.notice _(\"Dependency %{dep} has failures: %{status}\") % { dep: dep, status: resource_status(dep).failed }\n            @failed_dependencies_already_notified.add(dep.ref)\n          end\n        end\n      end\n    end\n\n    s && s.dependency_failed?\n  end\n\n  # We need to know if a resource has any failed dependencies before\n  # we try to process it. We keep track of this by keeping a list on\n  # each resource of the failed dependencies, and incrementally\n  # computing it as the union of the failed dependencies of each\n  # first-order dependency. We have to do this as-we-go instead of\n  # up-front at failure time because the graph may be mutated as we\n  # walk it.\n  def propagate_failure(resource)\n    provider_class = resource.provider.class\n    s = resource_status(resource)\n    if prefetch_failed_providers[resource.type][provider_class.name] && !s.nil?\n      message = _(\"Prefetch failed for %{type_name} provider '%{name}'\") % { type_name: resource.type, name: provider_class.name }\n      s.fail_with_event(message)\n    end\n\n    failed = Set.new\n    relationship_graph.direct_dependencies_of(resource).each do |dep|\n      s = resource_status(dep)\n      next if s.nil?\n\n      failed.merge(s.failed_dependencies) if s.dependency_failed?\n      failed.add(dep) if s.failed? || s.failed_to_restart?\n    end\n    resource_status(resource).failed_dependencies = failed.to_a\n  end\n\n  # Should we ignore tags?\n  def ignore_tags?\n    !@catalog.host_config?\n  end\n\n  def resources_by_provider(type_name, provider_name)\n    unless @resources_by_provider\n      @resources_by_provider = Hash.new { |h, k| h[k] = Hash.new { |h1, k1| h1[k1] = {} } }\n\n      @catalog.vertices.each do |resource|\n        if resource.class.attrclass(:provider)\n          prov = resource.provider && resource.provider.class.name\n          @resources_by_provider[resource.type][prov][resource.name] = resource\n        end\n      end\n    end\n\n    @resources_by_provider[type_name][provider_name] || {}\n  end\n\n  # Prefetch any providers that support it, yo.  We don't support prefetching\n  # types, just providers.\n  def prefetch(provider_class, resources)\n    type_name = provider_class.resource_type.name\n    return if @prefetched_providers[type_name][provider_class.name] ||\n              @prefetch_failed_providers[type_name][provider_class.name]\n\n    Puppet.debug { \"Prefetching #{provider_class.name} resources for #{type_name}\" }\n    begin\n      provider_class.prefetch(resources)\n    rescue LoadError, StandardError => detail\n      # TRANSLATORS `prefetch` is a function name and should not be translated\n      message = _(\"Could not prefetch %{type_name} provider '%{name}': %{detail}\") % { type_name: type_name, name: provider_class.name, detail: detail }\n      Puppet.log_exception(detail, message)\n      @prefetch_failed_providers[type_name][provider_class.name] = true\n    end\n    @prefetched_providers[type_name][provider_class.name] = true\n  end\n\n  def add_resource_status(status)\n    report.add_resource_status(status)\n  end\n\n  # Is the resource currently scheduled?\n  def scheduled?(resource)\n    ignoreschedules || resource_harness.scheduled?(resource)\n  end\n\n  # Should this resource be skipped?\n  def skip?(resource)\n    if skip_tags?(resource)\n      resource.debug \"Skipping with skip tags #{skip_tags.join(', ')}\"\n    elsif missing_tags?(resource)\n      resource.debug \"Not tagged with #{tags.join(', ')}\"\n    elsif !scheduled?(resource)\n      resource.debug \"Not scheduled\"\n    elsif failed_dependencies?(resource)\n      # When we introduced the :whit into the graph, to reduce the combinatorial\n      # explosion of edges, we also ended up reporting failures for containers\n      # like class and stage.  This is undesirable; while just skipping the\n      # output isn't perfect, it is RC-safe. --daniel 2011-06-07\n      # With merge_dependency_warnings, warn about class dependency failures ... just once per class. TJK 2019-09-09\n      unless resource.instance_of?(Puppet::Type.type(:whit))\n        if @merge_dependency_warnings && resource.parent && failed_dependencies?(resource.parent)\n          ps = resource_status(resource.parent)\n          ps.failed_dependencies.find_all { |d| !@failed_class_dependencies_already_warned.include?(d.ref) }.each do |dep|\n            resource.parent.warning _(\"Skipping resources in class because of failed class dependencies\")\n            @failed_class_dependencies_already_warned.add(dep.ref)\n          end\n        else\n          resource.warning _(\"Skipping because of failed dependencies\")\n        end\n      end\n    elsif resource_status(resource).failed? && @prefetch_failed_providers[resource.type][resource.provider.class.name]\n      # Do not try to evaluate a resource with a known failed provider\n      resource.warning _(\"Skipping because provider prefetch failed\")\n    elsif resource.virtual?\n      resource.debug \"Skipping because virtual\"\n    elsif !host_and_device_resource?(resource) && resource.appliable_to_host? && for_network_device\n      resource.debug \"Skipping host resources because running on a device\"\n    elsif !host_and_device_resource?(resource) && resource.appliable_to_device? && !for_network_device\n      resource.debug \"Skipping device resources because running on a posix host\"\n    else\n      return false\n    end\n    true\n  end\n\n  def host_and_device_resource?(resource)\n    resource.appliable_to_host? && resource.appliable_to_device?\n  end\n\n  # Is this resource tagged appropriately?\n  def missing_tags?(resource)\n    return false if ignore_tags?\n    return false if tags.empty?\n\n    !resource.tagged?(*tags)\n  end\n\n  def skip_tags?(resource)\n    return false if ignore_tags?\n    return false if skip_tags.empty?\n\n    resource.tagged?(*skip_tags)\n  end\n\n  def split_qualified_tags?\n    false\n  end\n\n  # These two methods are only made public to enable the existing spec tests to run\n  # under rspec 3 (apparently rspec 2 didn't enforce access controls?). Please do not\n  # treat these as part of a public API.\n  # Possible future improvement: rewrite to not require access to private methods.\n  public :skip?\n  public :missing_tags?\n\n  def resolve_resource(resource)\n    return unless catalog.host_config?\n\n    deferred_validate = false\n\n    resource.eachparameter do |param|\n      if param.value.instance_of?(Puppet::Pops::Evaluator::DeferredValue)\n        # Puppet::Parameter#value= triggers validation and munging. Puppet::Property#value=\n        # overrides the method, but also triggers validation and munging, since we're\n        # setting the desired/should value.\n        resolved = param.value.resolve\n        # resource.notice(\"Resolved deferred value to #{resolved}\")\n        param.value = resolved\n        deferred_validate = true\n      end\n    end\n\n    if deferred_validate\n      resource.validate_resource\n    end\n  end\nend\n\nrequire_relative 'transaction/report'\n"
  },
  {
    "path": "lib/puppet/trusted_external.rb",
    "content": "# frozen_string_literal: true\n\n# A method for retrieving external trusted facts\nmodule Puppet::TrustedExternal\n  def retrieve(certname)\n    command = Puppet[:trusted_external_command]\n    return nil unless command\n\n    Puppet.debug { _(\"Retrieving trusted external data from %{command}\") % { command: command } }\n    setting_type = Puppet.settings.setting(:trusted_external_command).type\n    if setting_type == :file\n      return fetch_data(command, certname)\n    end\n\n    # command is a directory. Thus, data is a hash of <basename> => <data> for\n    # each executable file in command. For example, if the files 'servicenow.rb',\n    # 'unicorn.sh' are in command, then data is the following hash:\n    #   { 'servicenow' => <servicenow.rb output>, 'unicorn' => <unicorn.sh output> }\n    data = {}\n    Puppet::FileSystem.children(command).each do |file|\n      abs_path = Puppet::FileSystem.expand_path(file)\n      executable_file = Puppet::FileSystem.file?(abs_path) && Puppet::FileSystem.executable?(abs_path)\n      unless executable_file\n        Puppet.debug { _(\"Skipping non-executable file %{file}\") % { file: abs_path } }\n        next\n      end\n      basename = file.basename(file.extname).to_s\n      unless data[basename].nil?\n        raise Puppet::Error, _(\"There is more than one '%{basename}' script in %{dir}\") % { basename: basename, dir: command }\n      end\n\n      data[basename] = fetch_data(abs_path, certname)\n    end\n    data\n  end\n  module_function :retrieve\n\n  def fetch_data(command, certname)\n    result = Puppet::Util::Execution.execute([command, certname], {\n                                               :combine => false,\n                                               :failonfail => true,\n                                             })\n    JSON.parse(result)\n  end\n  module_function :fetch_data\nend\n"
  },
  {
    "path": "lib/puppet/type/component.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/type'\nrequire_relative '../../puppet/transaction'\n\nPuppet::Type.newtype(:component) do\n  include Enumerable\n\n  newparam(:name) do\n    desc \"The name of the component.  Generally optional.\"\n    isnamevar\n  end\n\n  # Override how parameters are handled so that we support the extra\n  # parameters that are used with defined resource types.\n  def [](param)\n    if self.class.valid_parameter?(param)\n      super\n    else\n      @extra_parameters[param.to_sym]\n    end\n  end\n\n  # Override how parameters are handled so that we support the extra\n  # parameters that are used with defined resource types.\n  def []=(param, value)\n    if self.class.valid_parameter?(param)\n      super\n    else\n      @extra_parameters[param.to_sym] = value\n    end\n  end\n\n  # Initialize a new component\n  def initialize(*args)\n    @extra_parameters = {}\n    super\n  end\n\n  # Component paths are special because they function as containers.\n  def pathbuilder\n    if reference.type == \"Class\"\n      myname = reference.title\n    else\n      myname = reference.to_s\n    end\n    p = parent\n    if p\n      [p.pathbuilder, myname]\n    else\n      [myname]\n    end\n  end\n\n  def ref\n    reference.to_s\n  end\n\n  # We want our title to just be the whole reference, rather than @title.\n  def title\n    ref\n  end\n\n  def title=(str)\n    @reference = Puppet::Resource.new(str)\n  end\n\n  def refresh\n    catalog.adjacent(self).each do |child|\n      if child.respond_to?(:refresh)\n        child.refresh\n        child.log \"triggering refresh\"\n      end\n    end\n  end\n\n  def to_s\n    reference.to_s\n  end\n\n  # Overrides the default implementation to do nothing.\n  # This type contains data from class/define parameters, but does\n  # not have actual parameters or properties at the Type level. We can\n  # simply ignore anything flagged as sensitive here, since any\n  # contained resources will handle that sensitivity themselves. There\n  # is no risk of this information leaking into reports, since no\n  # Component instances survive the graph transmutation.\n  #\n  def set_sensitive_parameters(sensitive_parameters)\n  end\n\n  private\n\n  attr_reader :reference\nend\n"
  },
  {
    "path": "lib/puppet/type/exec.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Type.newtype(:exec) do\n    include Puppet::Util::Execution\n    require 'timeout'\n\n    @doc = \"Executes external commands.\n\n      Any command in an `exec` resource **must** be able to run multiple times\n      without causing harm --- that is, it must be *idempotent*. There are three\n      main ways for an exec to be idempotent:\n\n      * The command itself is already idempotent. (For example, `apt-get update`.)\n      * The exec has an `onlyif`, `unless`, or `creates` attribute, which prevents\n        Puppet from running the command unless some condition is met. The\n        `onlyif` and `unless` commands of an `exec` are used in the process of\n        determining whether the `exec` is already in sync, therefore they must be run\n        during a noop Puppet run.\n      * The exec has `refreshonly => true`, which allows Puppet to run the\n        command only when some other resource is changed. (See the notes on refreshing\n        below.)\n\n      The state managed by an `exec` resource represents whether the specified command\n      _needs to be_ executed during the catalog run. The target state is always that\n      the command does not need to be executed. If the initial state is that the\n      command _does_ need to be executed, then successfully executing the command\n      transitions it to the target state.\n\n      The `unless`, `onlyif`, and `creates` properties check the initial state of the\n      resource. If one or more of these properties is specified, the exec might not\n      need to run. If the exec does not need to run, then the system is already in\n      the target state. In such cases, the exec is considered successful without\n      actually executing its command.\n\n      A caution: There's a widespread tendency to use collections of execs to\n      manage resources that aren't covered by an existing resource type. This\n      works fine for simple tasks, but once your exec pile gets complex enough\n      that you really have to think to understand what's happening, you should\n      consider developing a custom resource type instead, as it is much\n      more predictable and maintainable.\n\n      **Duplication:** Even though `command` is the namevar, Puppet allows\n      multiple `exec` resources with the same `command` value.\n\n      **Refresh:** `exec` resources can respond to refresh events (via\n      `notify`, `subscribe`, or the `~>` arrow). The refresh behavior of execs\n      is non-standard, and can be affected by the `refresh` and\n      `refreshonly` attributes:\n\n      * If `refreshonly` is set to true, the exec runs _only_ when it receives an\n        event. This is the most reliable way to use refresh with execs.\n      * If the exec has already run and then receives an event, it runs its\n        command **up to two times.** If an `onlyif`, `unless`, or `creates` condition\n        is no longer met after the first run, the second run does not occur.\n      * If the exec has already run, has a `refresh` command, and receives an\n        event, it runs its normal command. Then, if any `onlyif`, `unless`, or `creates`\n        conditions are still met, the exec runs its `refresh` command.\n      * If the exec has an `onlyif`, `unless`, or `creates` attribute that prevents it\n        from running, and it then receives an event, it still will not run.\n      * If the exec has `noop => true`, would otherwise have run, and receives\n        an event from a non-noop resource, it runs once. However, if it has a `refresh`\n        command, it runs that instead of its normal command.\n\n      In short: If there's a possibility of your exec receiving refresh events,\n      it is extremely important to make sure the run conditions are restricted.\n\n      **Autorequires:** If Puppet is managing an exec's cwd or the executable\n      file used in an exec's command, the exec resource autorequires those\n      files. If Puppet is managing the user that an exec should run as, the\n      exec resource autorequires that user.\"\n\n    # Create a new check mechanism.  It's basically a parameter that\n    # provides one extra 'check' method.\n    def self.newcheck(name, options = {}, &block)\n      @checks ||= {}\n\n      check = newparam(name, options, &block)\n      @checks[name] = check\n    end\n\n    def self.checks\n      @checks.keys\n    end\n\n    newproperty(:returns, :array_matching => :all, :event => :executed_command) do |_property|\n      include Puppet::Util::Execution\n      munge(&:to_s)\n\n      def event_name\n        :executed_command\n      end\n\n      defaultto \"0\"\n\n      attr_reader :output\n\n      desc \"The expected exit code(s).  An error will be returned if the\n        executed command has some other exit code. Can be specified as an array\n        of acceptable exit codes or a single value.\n\n        On POSIX systems, exit codes are always integers between 0 and 255.\n\n        On Windows, **most** exit codes should be integers between 0\n        and 2147483647.\n\n        Larger exit codes on Windows can behave inconsistently across different\n        tools. The Win32 APIs define exit codes as 32-bit unsigned integers, but\n        both the cmd.exe shell and the .NET runtime cast them to signed\n        integers. This means some tools will report negative numbers for exit\n        codes above 2147483647. (For example, cmd.exe reports 4294967295 as -1.)\n        Since Puppet uses the plain Win32 APIs, it will report the very large\n        number instead of the negative number, which might not be what you\n        expect if you got the exit code from a cmd.exe session.\n\n        Microsoft recommends against using negative/very large exit codes, and\n        you should avoid them when possible. To convert a negative exit code to\n        the positive one Puppet will use, add it to 4294967296.\"\n\n      # Make output a bit prettier\n      def change_to_s(currentvalue, newvalue)\n        _(\"executed successfully\")\n      end\n\n      # First verify that all of our checks pass.\n      def retrieve\n        # We need to return :notrun to trigger evaluation; when that isn't\n        # true, we *LIE* about what happened and return a \"success\" for the\n        # value, which causes us to be treated as in_sync?, which means we\n        # don't actually execute anything.  I think. --daniel 2011-03-10\n        if @resource.check_all_attributes\n          :notrun\n        else\n          should\n        end\n      end\n\n      # Actually execute the command.\n      def sync\n        event = :executed_command\n        tries = resource[:tries]\n        try_sleep = resource[:try_sleep]\n\n        begin\n          tries.times do |try|\n            # Only add debug messages for tries > 1 to reduce log spam.\n            debug(\"Exec try #{try + 1}/#{tries}\") if tries > 1\n            @output, @status = provider.run(resource[:command])\n            break if should.include?(@status.exitstatus.to_s)\n\n            if try_sleep > 0 and tries > 1\n              debug(\"Sleeping for #{try_sleep} seconds between tries\")\n              sleep try_sleep\n            end\n          end\n        rescue Timeout::Error => e\n          self.fail Puppet::Error, _(\"Command exceeded timeout\"), e\n        end\n\n        log = @resource[:logoutput]\n        if log\n          case log\n          when :true\n            log = @resource[:loglevel]\n          when :on_failure\n            if should.include?(@status.exitstatus.to_s)\n              log = :false\n            else\n              log = @resource[:loglevel]\n            end\n          end\n          unless log == :false\n            if @resource.parameter(:command).sensitive\n              send(log, \"[output redacted]\")\n            else\n              @output.split(/\\n/).each { |line|\n                send(log, line)\n              }\n            end\n          end\n        end\n\n        unless should.include?(@status.exitstatus.to_s)\n          if @resource.parameter(:command).sensitive\n            # Don't print sensitive commands in the clear\n            self.fail(_(\"[command redacted] returned %{status} instead of one of [%{expected}]\") % { status: @status.exitstatus, expected: should.join(\",\") })\n          else\n            self.fail(_(\"'%{cmd}' returned %{status} instead of one of [%{expected}]\") % { cmd: resource[:command], status: @status.exitstatus, expected: should.join(\",\") })\n          end\n        end\n\n        event\n      end\n    end\n\n    newparam(:command) do\n      isnamevar\n      desc \"The actual command to execute.  Must either be fully qualified\n        or a search path for the command must be provided.  If the command\n        succeeds, any output produced will be logged at the instance's\n        normal log level (usually `notice`), but if the command fails\n        (meaning its return code does not match the specified code) then\n        any output is logged at the `err` log level.\n\n        Multiple `exec` resources can use the same `command` value; Puppet\n        only uses the resource title to ensure `exec`s are unique.\n\n        On *nix platforms, the command can be specified as an array of\n        strings and Puppet will invoke it using the more secure method of\n        parameterized system calls. For example, rather than executing the\n        malicious injected code, this command will echo it out:\n\n            command => ['/bin/echo', 'hello world; rm -rf /']\n      \"\n\n      validate do |command|\n        unless command.is_a?(String) || command.is_a?(Array)\n          raise ArgumentError, _(\"Command must be a String or Array<String>, got value of class %{klass}\") % { klass: command.class }\n        end\n      end\n    end\n\n    newparam(:path) do\n      desc \"The search path used for command execution.\n        Commands must be fully qualified if no path is specified.  Paths\n        can be specified as an array or as a '#{File::PATH_SEPARATOR}' separated list.\"\n\n      # Support both arrays and colon-separated fields.\n      def value=(*values)\n        @value = values.flatten.collect { |val|\n          val.split(File::PATH_SEPARATOR)\n        }.flatten\n      end\n    end\n\n    newparam(:user) do\n      desc \"The user to run the command as.\n\n        > **Note:** Puppet cannot execute commands as other users on Windows.\n\n        Note that if you use this attribute, any error output is not captured\n        due to a bug within Ruby. If you use Puppet to create this user, the\n        exec automatically requires the user, as long as it is specified by\n        name.\n\n        The $HOME environment variable is not automatically set when using\n        this attribute.\"\n\n      validate do |user|\n        if Puppet::Util::Platform.windows?\n          self.fail _(\"Unable to execute commands as other users on Windows\")\n        elsif !Puppet.features.root? && resource.current_username() != user\n          self.fail _(\"Only root can execute commands as other users\")\n        end\n      end\n    end\n\n    newparam(:group) do\n      desc \"The group to run the command as.  This seems to work quite\n        haphazardly on different platforms -- it is a platform issue\n        not a Ruby or Puppet one, since the same variety exists when\n        running commands as different users in the shell.\"\n      # Validation is handled by the SUIDManager class.\n    end\n\n    newparam(:cwd, :parent => Puppet::Parameter::Path) do\n      desc \"The directory from which to run the command.  If\n        this directory does not exist, the command will fail.\"\n    end\n\n    newparam(:logoutput) do\n      desc \"Whether to log command output in addition to logging the\n        exit code. Defaults to `on_failure`, which only logs the output\n        when the command has an exit code that does not match any value\n        specified by the `returns` attribute. As with any resource type,\n        the log level can be controlled with the `loglevel` metaparameter.\"\n\n      defaultto :on_failure\n\n      newvalues(:true, :false, :on_failure)\n    end\n\n    newparam(:refresh) do\n      desc \"An alternate command to run when the `exec` receives a refresh event\n        from another resource. By default, Puppet runs the main command again.\n        For more details, see the notes about refresh behavior above, in the\n        description for this resource type.\n\n        Note that this alternate command runs with the same `provider`, `path`,\n        `user`, and `group` as the main command. If the `path` isn't set, you\n        must fully qualify the command's name.\"\n\n      validate do |command|\n        provider.validatecmd(command)\n      end\n    end\n\n    newparam(:environment) do\n      desc \"An array of any additional environment variables you want to set for a\n        command, such as `[ 'HOME=/root', 'MAIL=root@example.com']`.\n        Note that if you use this to set PATH, it will override the `path`\n        attribute. Multiple environment variables should be specified as an\n        array.\"\n\n      validate do |values|\n        values = [values] unless values.is_a? Array\n        values.each do |value|\n          unless value =~ /\\w+=/\n            raise ArgumentError, _(\"Invalid environment setting '%{value}'\") % { value: value }\n          end\n        end\n      end\n    end\n\n    newparam(:umask, :required_feature => :umask) do\n      desc \"Sets the umask to be used while executing this command\"\n\n      munge do |value|\n        if value =~ /^0?[0-7]{1,4}$/\n          return value.to_i(8)\n        else\n          raise Puppet::Error, _(\"The umask specification is invalid: %{value}\") % { value: value.inspect }\n        end\n      end\n    end\n\n    newparam(:timeout) do\n      desc \"The maximum time the command should take.  If the command takes\n        longer than the timeout, the command is considered to have failed\n        and will be stopped. The timeout is specified in seconds. The default\n        timeout is 300 seconds and you can set it to 0 to disable the timeout.\"\n\n      munge do |value|\n        value = value.shift if value.is_a?(Array)\n        begin\n          value = Float(value)\n        rescue ArgumentError => e\n          raise ArgumentError, _(\"The timeout must be a number.\"), e.backtrace\n        end\n        [value, 0.0].max\n      end\n\n      defaultto 300\n    end\n\n    newparam(:tries) do\n      desc \"The number of times execution of the command should be tried.\n        This many attempts will be made to execute the command until an\n        acceptable return code is returned. Note that the timeout parameter\n        applies to each try rather than to the complete set of tries.\"\n\n      munge do |value|\n        if value.is_a?(String)\n          unless value =~ /^\\d+$/\n            raise ArgumentError, _(\"Tries must be an integer\")\n          end\n\n          value = Integer(value)\n        end\n        raise ArgumentError, _(\"Tries must be an integer >= 1\") if value < 1\n\n        value\n      end\n\n      defaultto 1\n    end\n\n    newparam(:try_sleep) do\n      desc \"The time to sleep in seconds between 'tries'.\"\n\n      munge do |value|\n        if value.is_a?(String)\n          unless value =~ /^[-\\d.]+$/\n            raise ArgumentError, _(\"try_sleep must be a number\")\n          end\n\n          value = Float(value)\n        end\n        raise ArgumentError, _(\"try_sleep cannot be a negative number\") if value < 0\n\n        value\n      end\n\n      defaultto 0\n    end\n\n    newcheck(:refreshonly) do\n      desc <<-'EOT'\n        The command should only be run as a\n        refresh mechanism for when a dependent object is changed.  It only\n        makes sense to use this option when this command depends on some\n        other object; it is useful for triggering an action:\n\n            # Pull down the main aliases file\n            file { '/etc/aliases':\n              source => 'puppet://server/module/aliases',\n            }\n\n            # Rebuild the database, but only when the file changes\n            exec { newaliases:\n              path        => ['/usr/bin', '/usr/sbin'],\n              subscribe   => File['/etc/aliases'],\n              refreshonly => true,\n            }\n\n        Note that only `subscribe` and `notify` can trigger actions, not `require`,\n        so it only makes sense to use `refreshonly` with `subscribe` or `notify`.\n      EOT\n\n      newvalues(:true, :false)\n\n      # We always fail this test, because we're only supposed to run\n      # on refresh.\n      def check(value)\n        # We have to invert the values.\n        value != :true\n      end\n    end\n\n    newcheck(:creates, :parent => Puppet::Parameter::Path) do\n      desc <<-'EOT'\n        A file to look for before running the command. The command will\n        only run if the file **doesn't exist.**\n\n        This parameter doesn't cause Puppet to create a file; it is only\n        useful if **the command itself** creates a file.\n\n            exec { 'tar -xf /Volumes/nfs02/important.tar':\n              cwd     => '/var/tmp',\n              creates => '/var/tmp/myfile',\n              path    => ['/usr/bin', '/usr/sbin',],\n            }\n\n        In this example, `myfile` is assumed to be a file inside\n        `important.tar`. If it is ever deleted, the exec will bring it\n        back by re-extracting the tarball. If `important.tar` does **not**\n        actually contain `myfile`, the exec will keep running every time\n        Puppet runs.\n\n        This parameter can also take an array of files, and the command will\n        not run if **any** of these files exist. Consider this example:\n\n            creates => ['/tmp/file1', '/tmp/file2'],\n\n        The command is only run if both files don't exist.\n      EOT\n\n      accept_arrays\n\n      # If the file exists, return false (i.e., don't run the command),\n      # else return true\n      def check(value)\n        # TRANSLATORS 'creates' is a parameter name and should not be translated\n        debug(_(\"Checking that 'creates' path '%{creates_path}' exists\") % { creates_path: value })\n        !Puppet::FileSystem.exist?(value)\n      end\n    end\n\n    newcheck(:unless) do\n      desc <<-'EOT'\n        A test command that checks the state of the target system and restricts\n        when the `exec` can run. If present, Puppet runs this test command\n        first, then runs the main command unless the test has an exit code of 0\n        (success). For example:\n\n            exec { '/bin/echo root >> /usr/lib/cron/cron.allow':\n              path   => '/usr/bin:/usr/sbin:/bin',\n              unless => 'grep ^root$ /usr/lib/cron/cron.allow 2>/dev/null',\n            }\n\n        This would add `root` to the cron.allow file (on Solaris) unless\n        `grep` determines it's already there.\n\n        Note that this test command runs with the same `provider`, `path`,\n        `user`, `cwd`, and `group` as the main command. If the `path` isn't set, you\n        must fully qualify the command's name.\n\n        Since this command is used in the process of determining whether the\n        `exec` is already in sync, it must be run during a noop Puppet run.\n\n        This parameter can also take an array of commands. For example:\n\n            unless => ['test -f /tmp/file1', 'test -f /tmp/file2'],\n\n        or an array of arrays. For example:\n\n            unless => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']\n\n        This `exec` would only run if every command in the array has a\n        non-zero exit code.\n      EOT\n\n      validate do |cmds|\n        cmds = [cmds] unless cmds.is_a? Array\n\n        cmds.each do |command|\n          provider.validatecmd(command)\n        end\n      end\n\n      # Return true if the command does not return 0.\n      def check(value)\n        begin\n          output, status = provider.run(value, true)\n        rescue Timeout::Error\n          err _(\"Check %{value} exceeded timeout\") % { value: value.inspect }\n          return false\n        end\n\n        if sensitive\n          debug(\"[output redacted]\")\n        else\n          output.split(/\\n/).each { |line|\n            debug(line)\n          }\n        end\n\n        status.exitstatus != 0\n      end\n    end\n\n    newcheck(:onlyif) do\n      desc <<-'EOT'\n        A test command that checks the state of the target system and restricts\n        when the `exec` can run. If present, Puppet runs this test command\n        first, and only runs the main command if the test has an exit code of 0\n        (success). For example:\n\n            exec { 'logrotate':\n              path     => '/usr/bin:/usr/sbin:/bin',\n              provider => shell,\n              onlyif   => 'test `du /var/log/messages | cut -f1` -gt 100000',\n            }\n\n        This would run `logrotate` only if that test returns true.\n\n        Note that this test command runs with the same `provider`, `path`,\n        `user`, `cwd`, and `group` as the main command. If the `path` isn't set, you\n        must fully qualify the command's name.\n\n        Since this command is used in the process of determining whether the\n        `exec` is already in sync, it must be run during a noop Puppet run.\n\n        This parameter can also take an array of commands. For example:\n\n            onlyif => ['test -f /tmp/file1', 'test -f /tmp/file2'],\n\n        or an array of arrays. For example:\n\n            onlyif => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']\n\n        This `exec` would only run if every command in the array has an\n        exit code of 0 (success).\n      EOT\n\n      validate do |cmds|\n        cmds = [cmds] unless cmds.is_a? Array\n\n        cmds.each do |command|\n          provider.validatecmd(command)\n        end\n      end\n\n      # Return true if the command returns 0.\n      def check(value)\n        begin\n          output, status = provider.run(value, true)\n        rescue Timeout::Error\n          err _(\"Check %{value} exceeded timeout\") % { value: value.inspect }\n          return false\n        end\n\n        if sensitive\n          debug(\"[output redacted]\")\n        else\n          output.split(/\\n/).each { |line|\n            debug(line)\n          }\n        end\n\n        status.exitstatus == 0\n      end\n    end\n\n    # Exec names are not isomorphic with the objects.\n    @isomorphic = false\n\n    validate do\n      provider.validatecmd(self[:command])\n    end\n\n    # FIXME exec should autorequire any exec that 'creates' our cwd\n    autorequire(:file) do\n      reqs = []\n\n      # Stick the cwd in there if we have it\n      reqs << self[:cwd] if self[:cwd]\n\n      file_regex = Puppet::Util::Platform.windows? ? %r{^([a-zA-Z]:[\\\\/]\\S+)} : %r{^(/\\S+)}\n      cmd = self[:command]\n      cmd = cmd[0] if cmd.is_a? Array\n\n      if cmd.is_a?(Puppet::Pops::Evaluator::DeferredValue)\n        debug(\"The 'command' parameter is deferred and cannot be autorequired\")\n      else\n        cmd.scan(file_regex) { |str|\n          reqs << str\n        }\n\n        cmd.scan(/^\"([^\"]+)\"/) { |str|\n          reqs << str\n        }\n      end\n\n      [:onlyif, :unless].each { |param|\n        tmp = self[param]\n        next unless tmp\n\n        tmp = [tmp] unless tmp.is_a? Array\n\n        tmp.each do |line|\n          # And search the command line for files, adding any we\n          # find.  This will also catch the command itself if it's\n          # fully qualified.  It might not be a bad idea to add\n          # unqualified files, but, well, that's a bit more annoying\n          # to do.\n          line = line[0] if line.is_a? Array\n          if line.is_a?(Puppet::Pops::Evaluator::DeferredValue)\n            debug(\"The '#{param}' parameter is deferred and cannot be autorequired\")\n          else\n            reqs += line.scan(file_regex)\n          end\n        end\n      }\n\n      # For some reason, the += isn't causing a flattening\n      reqs.flatten!\n\n      reqs\n    end\n\n    autorequire(:user) do\n      # Autorequire users if they are specified by name\n      user = self[:user]\n      if user && user !~ /^\\d+$/\n        user\n      end\n    end\n\n    def self.instances\n      []\n    end\n\n    # Verify that we pass all of the checks.  The argument determines whether\n    # we skip the :refreshonly check, which is necessary because we now check\n    # within refresh\n    def check_all_attributes(refreshing = false)\n      self.class.checks.each { |check|\n        next if refreshing and check == :refreshonly\n\n        next unless @parameters.include?(check)\n\n        val = @parameters[check].value\n        val = [val] unless val.is_a? Array\n        val.each do |value|\n          next if @parameters[check].check(value)\n\n          # Give a debug message so users can figure out what command would have been\n          # but don't print sensitive commands or parameters in the clear\n          cmdstring = @parameters[:command].sensitive ? \"[command redacted]\" : @parameters[:command].value\n\n          debug(_(\"'%{cmd}' won't be executed because of failed check '%{check}'\") % { cmd: cmdstring, check: check })\n\n          return false\n        end\n      }\n\n      true\n    end\n\n    def output\n      if property(:returns).nil?\n        nil\n      else\n        property(:returns).output\n      end\n    end\n\n    # Run the command, or optionally run a separately-specified command.\n    def refresh\n      if check_all_attributes(true)\n        cmd = self[:refresh]\n        if cmd\n          provider.run(cmd)\n        else\n          property(:returns).sync\n        end\n      end\n    end\n\n    def current_username\n      Etc.getpwuid(Process.uid).name\n    end\n\n    private\n\n    def set_sensitive_parameters(sensitive_parameters)\n      # If any are sensitive, mark all as sensitive\n      sensitive = false\n      parameters_to_check = [:command, :unless, :onlyif]\n\n      parameters_to_check.each do |p|\n        if sensitive_parameters.include?(p)\n          sensitive_parameters.delete(p)\n          sensitive = true\n        end\n      end\n\n      if sensitive\n        parameters_to_check.each do |p|\n          if parameters.include?(p)\n            parameter(p).sensitive = true\n          end\n        end\n      end\n\n      super(sensitive_parameters)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/checksum.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/checksums'\n\n# Specify which checksum algorithm to use when checksumming\n# files.\nPuppet::Type.type(:file).newparam(:checksum) do\n  include Puppet::Util::Checksums\n\n  # The default is defined in Puppet.default_digest_algorithm\n  desc \"The checksum type to use when determining whether to replace a file's contents.\n\n    The default checksum type is sha256.\"\n\n  # The values are defined in Puppet::Util::Checksums.known_checksum_types\n  newvalues(:sha256, :sha256lite, :md5, :md5lite, :sha1, :sha1lite, :sha512, :sha384, :sha224, :mtime, :ctime, :none)\n\n  defaultto do\n    Puppet[:digest_algorithm].to_sym\n  end\n\n  validate do |value|\n    if Puppet::Util::Platform.fips_enabled? && (value == :md5 || value == :md5lite)\n      raise ArgumentError, _(\"MD5 is not supported in FIPS mode\")\n    end\n  end\n\n  def sum(content)\n    content = content.is_a?(Puppet::Pops::Types::PBinaryType::Binary) ? content.binary_buffer : content\n    type = digest_algorithm\n    \"{#{type}}\" + send(type, content)\n  end\n\n  def sum_file(path)\n    type = digest_algorithm\n    method = type.to_s + \"_file\"\n    \"{#{type}}\" + send(method, path).to_s\n  end\n\n  def sum_stream(&block)\n    type = digest_algorithm\n    method = type.to_s + \"_stream\"\n    checksum = send(method, &block)\n    \"{#{type}}#{checksum}\"\n  end\n\n  private\n\n  # Return the appropriate digest algorithm with fallbacks in case puppet defaults have not\n  # been initialized.\n  def digest_algorithm\n    value || Puppet[:digest_algorithm].to_sym\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/checksum_value.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/checksums'\nrequire_relative '../../../puppet/type/file/data_sync'\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:checksum_value) do\n    include Puppet::Util::Checksums\n    include Puppet::DataSync\n\n    desc \"The checksum of the source contents. Only md5, sha256, sha224, sha384 and sha512\n      are supported when specifying this parameter. If this parameter is set,\n      source_permissions will be assumed to be false, and ownership and permissions\n      will not be read from source.\"\n\n    def insync?(is)\n      # If checksum_value and source are specified, manage the file contents.\n      # Otherwise the content property will manage syncing.\n      if resource.parameter(:source).nil?\n        return true\n      end\n\n      checksum_insync?(resource.parameter(:source), is, true) { |inner| super(inner) }\n    end\n\n    def property_matches?(current, desired)\n      return true if super(current, desired)\n\n      date_matches?(resource.parameter(:checksum).value, current, desired)\n    end\n\n    def retrieve\n      # If checksum_value and source are specified, manage the file contents.\n      # Otherwise the content property will manage syncing. Don't compute the checksum twice.\n      if resource.parameter(:source).nil?\n        return nil\n      end\n\n      result = retrieve_checksum(resource)\n      # If the returned type matches the util/checksums format (prefixed with the type),\n      # strip the checksum type.\n      result = sumdata(result) if checksum?(result)\n      result\n    end\n\n    def sync\n      if resource.parameter(:source).nil?\n        devfail \"checksum_value#sync should not be called without a source parameter\"\n      end\n\n      # insync? only returns false if it expects to manage the file content,\n      # so instruct the resource to write its contents.\n      contents_sync(resource.parameter(:source))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/content.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'net/http'\nrequire 'uri'\nrequire 'tempfile'\n\nrequire_relative '../../../puppet/util/checksums'\nrequire_relative '../../../puppet/type/file/data_sync'\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:content) do\n    include Puppet::Util::Checksums\n    include Puppet::DataSync\n\n    attr_reader :actual_content\n\n    desc <<-'EOT'\n      The desired contents of a file, as a string. This attribute is mutually\n      exclusive with `source` and `target`.\n\n      Newlines and tabs can be specified in double-quoted strings using\n      standard escaped syntax --- \\n for a newline, and \\t for a tab.\n\n      With very small files, you can construct content strings directly in\n      the manifest...\n\n          define resolve($nameserver1, $nameserver2, $domain, $search) {\n              $str = \"search ${search}\n                  domain ${domain}\n                  nameserver ${nameserver1}\n                  nameserver ${nameserver2}\n                  \"\n\n              file { '/etc/resolv.conf':\n                content => $str,\n              }\n          }\n\n      ...but for larger files, this attribute is more useful when combined with the\n      [template](https://puppet.com/docs/puppet/latest/function.html#template)\n      or [file](https://puppet.com/docs/puppet/latest/function.html#file)\n      function.\n    EOT\n\n    # Store a checksum as the value, rather than the actual content.\n    # Simplifies everything.\n    munge do |value|\n      if value == :absent\n        value\n      elsif value.is_a?(String) && checksum?(value)\n        # XXX This is potentially dangerous because it means users can't write a file whose\n        # entire contents are a plain checksum unless it is a Binary content.\n        Puppet.puppet_deprecation_warning([\n          # TRANSLATORS \"content\" is an attribute and should not be translated\n          _('Using a checksum in a file\\'s \"content\" property is deprecated.'),\n          # TRANSLATORS \"filebucket\" is a resource type and should not be translated. The quoted occurrence of \"content\" is an attribute and should not be translated.\n          _('The ability to use a checksum to retrieve content from the filebucket using the \"content\" property will be removed in a future release.'),\n          # TRANSLATORS \"content\" is an attribute and should not be translated.\n          _('The literal value of the \"content\" property will be written to the file.'),\n          # TRANSLATORS \"static catalogs\" should not be translated.\n          _('The checksum retrieval functionality is being replaced by the use of static catalogs.'),\n          _('See https://puppet.com/docs/puppet/latest/static_catalogs.html for more information.')\n        ].join(\" \"),\n                                          :file => @resource.file,\n                                          :line => @resource.line) if !@actual_content && !resource.parameter(:source)\n        value\n      else\n        @actual_content = value.is_a?(Puppet::Pops::Types::PBinaryType::Binary) ? value.binary_buffer : value\n        resource.parameter(:checksum).sum(@actual_content)\n      end\n    end\n\n    # Checksums need to invert how changes are printed.\n    def change_to_s(currentvalue, newvalue)\n      # Our \"new\" checksum value is provided by the source.\n      source = resource.parameter(:source)\n      tmp = source.checksum if source\n      if tmp\n        newvalue = tmp\n      end\n      if currentvalue == :absent\n        \"defined content as '#{newvalue}'\"\n      elsif newvalue == :absent\n        \"undefined content from '#{currentvalue}'\"\n      else\n        \"content changed '#{currentvalue}' to '#{newvalue}'\"\n      end\n    end\n\n    def length\n      (actual_content and actual_content.length) || 0\n    end\n\n    def content\n      should\n    end\n\n    # Override this method to provide diffs if asked for.\n    # Also, fix #872: when content is used, and replace is true, the file\n    # should be insync when it exists\n    def insync?(is)\n      if resource[:source] && resource[:checksum_value]\n        # Asserts that nothing has changed since validate ran.\n        devfail \"content property should not exist if source and checksum_value are specified\"\n      end\n\n      contents_prop = resource.parameter(:source) || self\n      checksum_insync?(contents_prop, is, !resource[:content].nil?) { |inner| super(inner) }\n    end\n\n    def property_matches?(current, desired)\n      # If checksum_value is specified, it overrides comparing the content field.\n      checksum_type = resource.parameter(:checksum).value\n      checksum_value = resource.parameter(:checksum_value)\n      if checksum_value\n        desired = \"{#{checksum_type}}#{checksum_value.value}\"\n      end\n\n      # The inherited equality is always accepted, so use it if valid.\n      return true if super(current, desired)\n\n      date_matches?(checksum_type, current, desired)\n    end\n\n    def retrieve\n      retrieve_checksum(resource)\n    end\n\n    # Make sure we're also managing the checksum property.\n    def should=(value)\n      # treat the value as a bytestring\n      value = value.b if value.is_a?(String)\n      @resource.newattr(:checksum) unless @resource.parameter(:checksum)\n      super\n    end\n\n    # Just write our content out to disk.\n    def sync\n      contents_sync(resource.parameter(:source) || self)\n    end\n\n    def write(file)\n      resource.parameter(:checksum).sum_stream { |sum|\n        each_chunk_from { |chunk|\n          sum << chunk\n          file.print chunk\n        }\n      }\n    end\n\n    private\n\n    # the content is munged so if it's a checksum source_or_content is nil\n    # unless the checksum indirectly comes from source\n    def each_chunk_from\n      if actual_content.is_a?(String)\n        yield actual_content\n      elsif content_is_really_a_checksum? && actual_content.nil?\n        yield read_file_from_filebucket\n      elsif actual_content.nil?\n        yield ''\n      end\n    end\n\n    def content_is_really_a_checksum?\n      checksum?(should)\n    end\n\n    def read_file_from_filebucket\n      dipper = resource.bucket\n      raise \"Could not get filebucket from file\" unless dipper\n\n      sum = should.sub(/\\{\\w+\\}/, '')\n\n      dipper.getfile(sum)\n    rescue => detail\n      self.fail Puppet::Error, \"Could not retrieve content for #{should} from filebucket: #{detail}\", detail\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/ctime.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:ctime) do\n    desc \"A read-only state to check the file ctime. On most modern \\*nix-like\n      systems, this is the time of the most recent change to the owner, group,\n      permissions, or content of the file.\"\n\n    def retrieve\n      current_value = :absent\n      stat = @resource.stat\n      if stat\n        current_value = stat.ctime\n      end\n      current_value.to_s\n    end\n\n    validate do |_val|\n      fail \"ctime is read-only\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/data_sync.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/checksums'\nrequire_relative '../../../puppet/util/diff'\nrequire 'date'\nrequire 'tempfile'\n\nmodule Puppet\n  module DataSync\n    include Puppet::Util::Checksums\n    include Puppet::Util::Diff\n\n    def write_temporarily(param)\n      tempfile = Tempfile.new(\"puppet-file\")\n      tempfile.open\n\n      param.write(tempfile)\n\n      tempfile.close\n\n      yield tempfile.path\n    ensure\n      tempfile.delete if tempfile\n    end\n\n    def checksum_insync?(param, is, has_contents, &block)\n      resource = param.resource\n      if resource.should_be_file?\n        return false if is == :absent\n      else\n        if resource[:ensure] == :present && has_contents && (s = resource.stat)\n          # TRANSLATORS 'Ensure' is an attribute and ':present' is a value and should not be translated\n          resource.warning _(\"Ensure set to :present but file type is %{file_type} so no content will be synced\") % { file_type: s.ftype }\n        end\n        return true\n      end\n\n      return true unless resource.replace?\n\n      is_insync = yield(is)\n\n      if show_diff?(!is_insync)\n        if param.sensitive\n          send resource[:loglevel], \"[diff redacted]\"\n        else\n          write_temporarily(param) do |path|\n            diff_output = diff(resource[:path], path)\n            if diff_output.encoding == Encoding::BINARY || !diff_output.valid_encoding?\n              diff_output = \"Binary files #{resource[:path]} and #{path} differ\"\n            end\n            send resource[:loglevel], \"\\n\" + diff_output\n          end\n        end\n      end\n      is_insync\n    end\n\n    def show_diff?(has_changes)\n      has_changes && Puppet[:show_diff] && resource.show_diff?\n    end\n\n    def date_matches?(checksum_type, current, desired)\n      time_types = [:mtime, :ctime]\n      return false unless time_types.include?(checksum_type)\n      return false unless current && desired\n\n      begin\n        if checksum?(current) || checksum?(desired)\n          raise if !time_types.include?(sumtype(current).to_sym) || !time_types.include?(sumtype(desired).to_sym)\n\n          current = sumdata(current)\n          desired = sumdata(desired)\n        end\n        DateTime.parse(current) >= DateTime.parse(desired)\n      rescue => detail\n        self.fail Puppet::Error, \"Resource with checksum_type #{checksum_type} didn't contain a date in #{current} or #{desired}\", detail.backtrace\n      end\n    end\n\n    def retrieve_checksum(resource)\n      stat = resource.stat\n      return :absent unless stat\n\n      ftype = stat.ftype\n      # Don't even try to manage the content on directories or links\n      return nil if %w[directory link fifo socket].include?(ftype)\n\n      begin\n        resource.parameter(:checksum).sum_file(resource[:path])\n      rescue => detail\n        raise Puppet::Error, \"Could not read #{ftype} #{resource.title}: #{detail}\", detail.backtrace\n      end\n    end\n\n    def contents_sync(param)\n      return_event = param.resource.stat ? :file_changed : :file_created\n      resource.write(param)\n      return_event\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/ensure.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Puppet::Type.type(:file).ensurable do\n    require 'etc'\n    require_relative '../../../puppet/util/symbolic_file_mode'\n    include Puppet::Util::SymbolicFileMode\n\n    desc <<-EOT\n      Whether the file should exist, and if so what kind of file it should be.\n      Possible values are `present`, `absent`, `file`, `directory`, and `link`.\n\n      * `present` accepts any form of file existence, and creates a\n        normal file if the file is missing. (The file will have no content\n        unless the `content` or `source` attribute is used.)\n      * `absent` ensures the file doesn't exist, and deletes it if necessary.\n      * `file` ensures it's a normal file, and enables use of the `content` or\n        `source` attribute.\n      * `directory` ensures it's a directory, and enables use of the `source`,\n        `recurse`, `recurselimit`, `ignore`, and `purge` attributes.\n      * `link` ensures the file is a symlink, and **requires** that you also\n        set the `target` attribute. Symlinks are supported on all Posix\n        systems and on Windows Vista / 2008 and higher. On Windows, managing\n        symlinks requires Puppet agent's user account to have the \"Create\n        Symbolic Links\" privilege; this can be configured in the \"User Rights\n        Assignment\" section in the Windows policy editor. By default, Puppet\n        agent runs as the Administrator account, which has this privilege.\n\n      Puppet avoids destroying directories unless the `force` attribute is set\n      to `true`. This means that if a file is currently a directory, setting\n      `ensure` to anything but `directory` or `present` will cause Puppet to\n      skip managing the resource and log either a notice or an error.\n\n      There is one other non-standard value for `ensure`. If you specify the\n      path to another file as the ensure value, it is equivalent to specifying\n      `link` and using that path as the `target`:\n\n          # Equivalent resources:\n\n          file { '/etc/inetd.conf':\n            ensure => '/etc/inet/inetd.conf',\n          }\n\n          file { '/etc/inetd.conf':\n            ensure => link,\n            target => '/etc/inet/inetd.conf',\n          }\n\n      However, we recommend using `link` and `target` explicitly, since this\n      behavior can be harder to read and is\n      [deprecated](https://docs.puppet.com/puppet/4.3/deprecated_language.html)\n      as of Puppet 4.3.0.\n    EOT\n\n    # Most 'ensure' properties have a default, but with files we, um, don't.\n    nodefault\n\n    newvalue(:absent) do\n      Puppet::FileSystem.unlink(@resource[:path])\n    end\n\n    aliasvalue(:false, :absent)\n\n    newvalue(:file, :event => :file_created) do\n      # Make sure we're not managing the content some other way\n      property = @resource.property(:content) || @resource.property(:checksum_value)\n      if property\n        property.sync\n      else\n        @resource.write\n        @resource.should(:mode)\n      end\n    end\n\n    # aliasvalue(:present, :file)\n    newvalue(:present, :event => :file_created) do\n      # Make a file if they want something, but this will match almost\n      # anything.\n      set_file\n    end\n\n    newvalue(:directory, :event => :directory_created) do\n      mode = @resource.should(:mode)\n      parent = File.dirname(@resource[:path])\n      unless Puppet::FileSystem.exist? parent\n        raise Puppet::Error,\n              \"Cannot create #{@resource[:path]}; parent directory #{parent} does not exist\"\n      end\n      if mode\n        Puppet::Util.withumask(0o00) do\n          Dir.mkdir(@resource[:path], symbolic_mode_to_int(mode, 0o755, true))\n        end\n      else\n        Dir.mkdir(@resource[:path])\n      end\n      @resource.send(:property_fix)\n      return :directory_created\n    end\n\n    newvalue(:link, :event => :link_created, :required_features => :manages_symlinks) do\n      property = resource.property(:target)\n      fail \"Cannot create a symlink without a target\" unless property\n\n      property.retrieve\n      property.mklink\n    end\n\n    # Symlinks.\n    newvalue(/./) do\n      # This code never gets executed.  We need the regex to support\n      # specifying it, but the work is done in the 'symlink' code block.\n    end\n\n    munge do |value|\n      value = super(value)\n      unless value.is_a? Symbol\n        resource[:target] = value\n        value = :link\n      end\n      resource[:links] = :manage if value == :link and resource[:links] != :follow\n      value\n    end\n\n    def change_to_s(currentvalue, newvalue)\n      return super unless [:file, :present].include?(newvalue)\n\n      property = @resource.property(:content)\n      return super unless property\n\n      # We know that content is out of sync if we're here, because\n      # it's essentially equivalent to 'ensure' in the transaction.\n      source = @resource.parameter(:source)\n      if source\n        should = source.checksum\n      else\n        should = property.should\n      end\n      if should == :absent\n        is = property.retrieve\n      else\n        is = :absent\n      end\n\n      property.change_to_s(is, should)\n    end\n\n    # Check that we can actually create anything\n    def check\n      basedir = File.dirname(@resource[:path])\n\n      if !Puppet::FileSystem.exist?(basedir)\n        raise Puppet::Error,\n              \"Can not create #{@resource.title}; parent directory does not exist\"\n      elsif !FileTest.directory?(basedir)\n        raise Puppet::Error,\n              \"Can not create #{@resource.title}; #{dirname} is not a directory\"\n      end\n    end\n\n    # We have to treat :present specially, because it works with any\n    # type of file.\n    def insync?(currentvalue)\n      unless currentvalue == :absent or resource.replace?\n        return true\n      end\n\n      if should == :present\n        !(currentvalue.nil? or currentvalue == :absent)\n      else\n        super(currentvalue)\n      end\n    end\n\n    def retrieve\n      stat = @resource.stat\n      if stat\n        stat.ftype.intern\n      elsif should == :false\n        :false\n      else\n        :absent\n      end\n    end\n\n    def sync\n      @resource.remove_existing(should)\n      if should == :absent\n        return :file_removed\n      end\n\n      super\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/group.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/posix'\n\nmodule Puppet\n  # Manage file group ownership.\n  Puppet::Type.type(:file).newproperty(:group) do\n    desc <<-EOT\n      Which group should own the file.  Argument can be either a group\n      name or a group ID.\n\n      On Windows, a user (such as \"Administrator\") can be set as a file's group\n      and a group (such as \"Administrators\") can be set as a file's owner;\n      however, a file's owner and group shouldn't be the same. (If the owner\n      is also the group, files with modes like `\"0640\"` will cause log churn, as\n      they will always appear out of sync.)\n    EOT\n\n    validate do |group|\n      raise(Puppet::Error, \"Invalid group name '#{group.inspect}'\") unless group and group != \"\"\n    end\n\n    def insync?(current)\n      # We don't want to validate/munge groups until we actually start to\n      # evaluate this property, because they might be added during the catalog\n      # apply.\n      @should.map! do |val|\n        gid = provider.name2gid(val)\n        if gid\n          gid\n        elsif provider.resource.noop?\n          return false\n        else\n          raise \"Could not find group #{val}\"\n        end\n      end\n\n      @should.include?(current)\n    end\n\n    # We want to print names, not numbers\n    def is_to_s(currentvalue) # rubocop:disable Naming/PredicateName\n      super(provider.gid2name(currentvalue) || currentvalue)\n    end\n\n    def should_to_s(newvalue)\n      super(provider.gid2name(newvalue) || newvalue)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/mode.rb",
    "content": "# frozen_string_literal: true\n\n# Manage file modes.  This state should support different formats\n# for specification (e.g., u+rwx, or -0011), but for now only supports\n# specifying the full mode.\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:mode) do\n    require_relative '../../../puppet/util/symbolic_file_mode'\n    include Puppet::Util::SymbolicFileMode\n\n    desc <<-'EOT'\n      The desired permissions mode for the file, in symbolic or numeric\n      notation. This value **must** be specified as a string; do not use\n      un-quoted numbers to represent file modes.\n\n      If the mode is omitted (or explicitly set to `undef`), Puppet does not\n      enforce permissions on existing files and creates new files with\n      permissions of `0644`.\n\n      The `file` type uses traditional Unix permission schemes and translates\n      them to equivalent permissions for systems which represent permissions\n      differently, including Windows. For detailed ACL controls on Windows,\n      you can leave `mode` unmanaged and use\n      [the puppetlabs/acl module.](https://forge.puppetlabs.com/puppetlabs/acl)\n\n      Numeric modes should use the standard octal notation of\n      `<SETUID/SETGID/STICKY><OWNER><GROUP><OTHER>` (for example, \"0644\").\n\n      * Each of the \"owner,\" \"group,\" and \"other\" digits should be a sum of the\n        permissions for that class of users, where read = 4, write = 2, and\n        execute/search = 1.\n      * The setuid/setgid/sticky digit is also a sum, where setuid = 4, setgid = 2,\n        and sticky = 1.\n      * The setuid/setgid/sticky digit is optional. If it is absent, Puppet will\n        clear any existing setuid/setgid/sticky permissions. (So to make your intent\n        clear, you should use at least four digits for numeric modes.)\n      * When specifying numeric permissions for directories, Puppet sets the search\n        permission wherever the read permission is set.\n\n      Symbolic modes should be represented as a string of comma-separated\n      permission clauses, in the form `<WHO><OP><PERM>`:\n\n      * \"Who\" should be any combination of u (user), g (group), and o (other), or a (all)\n      * \"Op\" should be = (set exact permissions), + (add select permissions),\n        or - (remove select permissions)\n      * \"Perm\" should be one or more of:\n          * r (read)\n          * w (write)\n          * x (execute/search)\n          * t (sticky)\n          * s (setuid/setgid)\n          * X (execute/search if directory or if any one user can execute)\n          * u (user's current permissions)\n          * g (group's current permissions)\n          * o (other's current permissions)\n\n      Thus, mode `\"0664\"` could be represented symbolically as either `a=r,ug+w`\n      or `ug=rw,o=r`.  However, symbolic modes are more expressive than numeric\n      modes: a mode only affects the specified bits, so `mode => 'ug+w'` will\n      set the user and group write bits, without affecting any other bits.\n\n      See the manual page for GNU or BSD `chmod` for more details\n      on numeric and symbolic modes.\n\n      On Windows, permissions are translated as follows:\n\n      * Owner and group names are mapped to Windows SIDs\n      * The \"other\" class of users maps to the \"Everyone\" SID\n      * The read/write/execute permissions map to the `FILE_GENERIC_READ`,\n        `FILE_GENERIC_WRITE`, and `FILE_GENERIC_EXECUTE` access rights; a\n        file's owner always has the `FULL_CONTROL` right\n      * \"Other\" users can't have any permissions a file's group lacks,\n        and its group can't have any permissions its owner lacks; that is, \"0644\"\n        is an acceptable mode, but \"0464\" is not.\n    EOT\n\n    validate do |value|\n      unless value.is_a?(String)\n        raise Puppet::Error, \"The file mode specification must be a string, not '#{value.class.name}'\"\n      end\n      unless value.nil? or valid_symbolic_mode?(value)\n        raise Puppet::Error, \"The file mode specification is invalid: #{value.inspect}\"\n      end\n    end\n\n    munge do |value|\n      return nil if value.nil?\n\n      unless valid_symbolic_mode?(value)\n        raise Puppet::Error, \"The file mode specification is invalid: #{value.inspect}\"\n      end\n\n      # normalizes to symbolic form, e.g. u+a, an octal string without leading 0\n      normalize_symbolic_mode(value)\n    end\n\n    unmunge do |value|\n      # return symbolic form or octal string *with* leading 0's\n      display_mode(value) if value\n    end\n\n    def desired_mode_from_current(desired, current)\n      current = current.to_i(8) if current.is_a? String\n      is_a_directory = @resource.stat && @resource.stat.directory?\n      symbolic_mode_to_int(desired, current, is_a_directory)\n    end\n\n    # If we're a directory, we need to be executable for all cases\n    # that are readable.  This should probably be selectable, but eh.\n    def dirmask(value)\n      if FileTest.directory?(resource[:path]) and value =~ /^\\d+$/ then\n        value = value.to_i(8)\n        value |= 0o100 if value & 0o400 != 0\n        value |= 0o10 if value & 0o40 != 0\n        value |= 0o1 if value & 0o4 != 0\n        value = value.to_s(8)\n      end\n\n      value\n    end\n\n    # If we're not following links and we're a link, then we just turn\n    # off mode management entirely.\n    def insync?(currentvalue)\n      if provider.respond_to?(:munge_windows_system_group)\n        munged_mode = provider.munge_windows_system_group(currentvalue, @should)\n        return false if munged_mode.nil?\n\n        currentvalue = munged_mode\n      end\n      stat = @resource.stat\n      if stat && stat.ftype == \"link\" && @resource[:links] != :follow\n        debug _(\"Not managing symlink mode\")\n        true\n      else\n        super(currentvalue)\n      end\n    end\n\n    def property_matches?(current, desired)\n      return false unless current\n\n      current_bits = normalize_symbolic_mode(current)\n      desired_bits = desired_mode_from_current(desired, current).to_s(8)\n      current_bits == desired_bits\n    end\n\n    # Ideally, dirmask'ing could be done at munge time, but we don't know if 'ensure'\n    # will eventually be a directory or something else. And unfortunately, that logic\n    # depends on the ensure, source, and target properties. So rather than duplicate\n    # that logic, and get it wrong, we do dirmask during retrieve, after 'ensure' has\n    # been synced.\n    def retrieve\n      if @resource.stat\n        @should &&= @should.collect { |s| dirmask(s) }\n      end\n\n      super\n    end\n\n    # Finally, when we sync the mode out we need to transform it; since we\n    # don't have access to the calculated \"desired\" value here, or the\n    # \"current\" value, only the \"should\" value we need to retrieve again.\n    def sync\n      current = @resource.stat ? @resource.stat.mode : 0o644\n      set(desired_mode_from_current(@should[0], current).to_s(8))\n    end\n\n    def change_to_s(old_value, desired)\n      return super if desired =~ /^\\d+$/\n\n      old_bits = normalize_symbolic_mode(old_value)\n      new_bits = normalize_symbolic_mode(desired_mode_from_current(desired, old_bits))\n      super(old_bits, new_bits) + \" (#{desired})\"\n    end\n\n    def should_to_s(should_value)\n      \"'#{should_value.rjust(4, '0')}'\"\n    end\n\n    def is_to_s(currentvalue) # rubocop:disable Naming/PredicateName\n      if currentvalue == :absent\n        # This can occur during audits---if a file is transitioning from\n        # present to absent the mode will have a value of `:absent`.\n        super\n      else\n        \"'#{currentvalue.rjust(4, '0')}'\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/mtime.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:mtime) do\n    desc \"A read-only state to check the file mtime. On \\*nix-like systems, this\n      is the time of the most recent change to the content of the file.\"\n\n    def retrieve\n      current_value = :absent\n      stat = @resource.stat\n      if stat\n        current_value = stat.mtime\n      end\n      current_value.to_s\n    end\n\n    validate do |_val|\n      fail \"mtime is read-only\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/owner.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:owner) do\n    include Puppet::Util::Warnings\n\n    desc <<-EOT\n      The user to whom the file should belong.  Argument can be a user name or a\n      user ID.\n\n      On Windows, a group (such as \"Administrators\") can be set as a file's owner\n      and a user (such as \"Administrator\") can be set as a file's group; however,\n      a file's owner and group shouldn't be the same. (If the owner is also\n      the group, files with modes like `\"0640\"` will cause log churn, as they\n      will always appear out of sync.)\n    EOT\n\n    def insync?(current)\n      # We don't want to validate/munge users until we actually start to\n      # evaluate this property, because they might be added during the catalog\n      # apply.\n      @should.map! do |val|\n        uid = provider.name2uid(val)\n        if uid\n          uid\n        elsif provider.resource.noop?\n          return false\n        else\n          raise \"Could not find user #{val}\"\n        end\n      end\n\n      return true if @should.include?(current)\n\n      unless Puppet.features.root?\n        warnonce \"Cannot manage ownership unless running as root\"\n        return true\n      end\n\n      false\n    end\n\n    # We want to print names, not numbers\n    def is_to_s(currentvalue) # rubocop:disable Naming/PredicateName\n      super(provider.uid2name(currentvalue) || currentvalue)\n    end\n\n    def should_to_s(newvalue)\n      super(provider.uid2name(newvalue) || newvalue)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/selcontext.rb",
    "content": "# frozen_string_literal: true\n\n# Manage SELinux context of files.\n#\n# This code actually manages three pieces of data in the context.\n#\n# [root@delenn files]# ls -dZ /\n# drwxr-xr-x  root root system_u:object_r:root_t         /\n#\n# The context of '/' here is 'system_u:object_r:root_t'.  This is\n# three separate fields:\n#\n# system_u is the user context\n# object_r is the role context\n# root_t is the type context\n#\n# All three of these fields are returned in a single string by the\n# output of the stat command, but set individually with the chcon\n# command.  This allows the user to specify a subset of the three\n# values while leaving the others alone.\n#\n# See https://www.nsa.gov/selinux/ for complete docs on SELinux.\n\nmodule Puppet\n  require_relative '../../../puppet/util/selinux'\n\n  class SELFileContext < Puppet::Property\n    include Puppet::Util::SELinux\n\n    def retrieve\n      return :absent unless @resource.stat\n\n      context = get_selinux_current_context(@resource[:path])\n      is = parse_selinux_context(name, context)\n      if name == :selrange and selinux_support?\n        selinux_category_to_label(is)\n      else\n        is\n      end\n    end\n\n    def retrieve_default_context(property)\n      return nil if Puppet::Util::Platform.windows?\n      if @resource[:selinux_ignore_defaults] == :true\n        return nil\n      end\n\n      context = get_selinux_default_context_with_handle(@resource[:path], provider.class.selinux_handle, @resource[:ensure])\n      unless context\n        return nil\n      end\n\n      property_default = parse_selinux_context(property, context)\n      debug \"Found #{property} default '#{property_default}' for #{@resource[:path]}\" unless property_default.nil?\n      property_default\n    end\n\n    def insync?(value)\n      if !selinux_support?\n        debug(\"SELinux bindings not found. Ignoring parameter.\")\n        true\n      elsif !selinux_label_support?(@resource[:path])\n        debug(\"SELinux not available for this filesystem. Ignoring parameter.\")\n        true\n      else\n        super\n      end\n    end\n\n    def unsafe_munge(should)\n      unless selinux_support?\n        return should\n      end\n\n      if name == :selrange\n        selinux_category_to_label(should)\n      else\n        should\n      end\n    end\n\n    def sync\n      set_selinux_context(@resource[:path], @should, name)\n      :file_changed\n    end\n  end\n\n  Puppet::Type.type(:file).newparam(:selinux_ignore_defaults) do\n    desc \"If this is set, Puppet will not call the SELinux function selabel_lookup to\n      supply defaults for the SELinux attributes (seluser, selrole,\n      seltype, and selrange). In general, you should leave this set at its\n      default and only set it to true when you need Puppet to not try to fix\n      SELinux labels automatically.\"\n    newvalues(:true, :false)\n\n    defaultto :false\n  end\n\n  Puppet::Type.type(:file).newproperty(:seluser, :parent => Puppet::SELFileContext) do\n    desc \"What the SELinux user component of the context of the file should be.\n      Any valid SELinux user component is accepted.  For example `user_u`.\n      If not specified, it defaults to the value returned by selabel_lookup for\n      the file, if any exists.  Only valid on systems with SELinux support\n      enabled.\"\n\n    @event = :file_changed\n    defaultto { retrieve_default_context(:seluser) }\n  end\n\n  Puppet::Type.type(:file).newproperty(:selrole, :parent => Puppet::SELFileContext) do\n    desc \"What the SELinux role component of the context of the file should be.\n      Any valid SELinux role component is accepted.  For example `role_r`.\n      If not specified, it defaults to the value returned by selabel_lookup for\n      the file, if any exists.  Only valid on systems with SELinux support\n      enabled.\"\n\n    @event = :file_changed\n    defaultto { retrieve_default_context(:selrole) }\n  end\n\n  Puppet::Type.type(:file).newproperty(:seltype, :parent => Puppet::SELFileContext) do\n    desc \"What the SELinux type component of the context of the file should be.\n      Any valid SELinux type component is accepted.  For example `tmp_t`.\n      If not specified, it defaults to the value returned by selabel_lookup for\n      the file, if any exists.  Only valid on systems with SELinux support\n      enabled.\"\n\n    @event = :file_changed\n    defaultto { retrieve_default_context(:seltype) }\n  end\n\n  Puppet::Type.type(:file).newproperty(:selrange, :parent => Puppet::SELFileContext) do\n    desc \"What the SELinux range component of the context of the file should be.\n      Any valid SELinux range component is accepted.  For example `s0` or\n      `SystemHigh`.  If not specified, it defaults to the value returned by\n      selabel_lookup for the file, if any exists.  Only valid on systems with\n      SELinux support enabled and that have support for MCS (Multi-Category\n      Security).\"\n\n    @event = :file_changed\n    defaultto { retrieve_default_context(:selrange) }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/source.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/file_serving/content'\nrequire_relative '../../../puppet/file_serving/metadata'\nrequire_relative '../../../puppet/file_serving/terminus_helper'\n\nrequire_relative '../../../puppet/http'\n\nmodule Puppet\n  # Copy files from a local or remote source.  This state *only* does any work\n  # when the remote file is an actual file; in that case, this state copies\n  # the file down.  If the remote file is a dir or a link or whatever, then\n  # this state, during retrieval, modifies the appropriate other states\n  # so that things get taken care of appropriately.\n  Puppet::Type.type(:file).newparam(:source) do\n    attr_accessor :source, :local\n\n    desc <<-'EOT'\n      A source file, which will be copied into place on the local system. This\n      attribute is mutually exclusive with `content` and `target`. Allowed\n      values are:\n\n      * `puppet:` URIs, which point to files in modules or Puppet file server\n      mount points.\n      * Fully qualified paths to locally available files (including files on NFS\n      shares or Windows mapped drives).\n      * `file:` URIs, which behave the same as local file paths.\n      * `http(s):` URIs, which point to files served by common web servers.\n\n      The normal form of a `puppet:` URI is:\n\n      `puppet:///modules/<MODULE NAME>/<FILE PATH>`\n\n      This will fetch a file from a module on the Puppet master (or from a\n      local module when using Puppet apply). Given a `modulepath` of\n      `/etc/puppetlabs/code/modules`, the example above would resolve to\n      `/etc/puppetlabs/code/modules/<MODULE NAME>/files/<FILE PATH>`.\n\n      Unlike `content`, the `source` attribute can be used to recursively copy\n      directories if the `recurse` attribute is set to `true` or `remote`. If\n      a source directory contains symlinks, use the `links` attribute to\n      specify whether to recreate links or follow them.\n\n      _HTTP_ URIs cannot be used to recursively synchronize whole directory\n      trees. You cannot use `source_permissions` values other than `ignore`\n      because HTTP servers do not transfer any metadata that translates to\n      ownership or permission details.\n\n      Puppet determines if file content is synchronized by computing a checksum\n      for the local file and comparing it against the `checksum_value`\n      parameter. If the `checksum_value` parameter is not specified for\n      `puppet` and `file` sources, Puppet computes a checksum based on its\n      `Puppet[:digest_algorithm]`. For `http(s)` sources, Puppet uses the\n      first HTTP header it recognizes out of the following list:\n      `X-Checksum-Sha256`, `X-Checksum-Sha1`, `X-Checksum-Md5` or `Content-MD5`.\n      If the server response does not include one of these headers, Puppet\n      defaults to using the `Last-Modified` header. Puppet updates the local\n      file if the header is newer than the modified time (mtime) of the local\n      file.\n\n      _HTTP_ URIs can include a user information component so that Puppet can\n      retrieve file metadata and content from HTTP servers that require HTTP Basic\n      authentication. For example `https://<user>:<pass>@<server>:<port>/path/to/file.`\n\n      When connecting to _HTTPS_ servers, Puppet trusts CA certificates in the\n      puppet-agent certificate bundle and the Puppet CA. You can configure Puppet\n      to trust additional CA certificates using the `Puppet[:ssl_trust_store]`\n      setting.\n\n      Multiple `source` values can be specified as an array, and Puppet will\n      use the first source that exists. This can be used to serve different\n      files to different system types:\n\n          file { '/etc/nfs.conf':\n            source => [\n              \"puppet:///modules/nfs/conf.${host}\",\n              \"puppet:///modules/nfs/conf.${os['name']}\",\n              'puppet:///modules/nfs/conf'\n            ]\n          }\n\n      Alternately, when serving directories recursively, multiple sources can\n      be combined by setting the `sourceselect` attribute to `all`.\n    EOT\n\n    validate do |sources|\n      sources = [sources] unless sources.is_a?(Array)\n      sources.each do |source|\n        next if Puppet::Util.absolute_path?(source)\n\n        begin\n          uri = URI.parse(Puppet::Util.uri_encode(source))\n        rescue => detail\n          self.fail Puppet::Error, \"Could not understand source #{source}: #{detail}\", detail\n        end\n\n        self.fail \"Cannot use relative URLs '#{source}'\" unless uri.absolute?\n        self.fail \"Cannot use opaque URLs '#{source}'\" unless uri.hierarchical?\n        unless %w[file puppet http https].include?(uri.scheme)\n          self.fail \"Cannot use URLs of type '#{uri.scheme}' as source for fileserving\"\n        end\n      end\n    end\n\n    SEPARATOR_REGEX = [Regexp.escape(File::SEPARATOR.to_s), Regexp.escape(File::ALT_SEPARATOR.to_s)].join\n\n    munge do |sources|\n      sources = [sources] unless sources.is_a?(Array)\n      sources.map do |source|\n        source = self.class.normalize(source)\n\n        if Puppet::Util.absolute_path?(source)\n          # CGI.unescape will butcher properly escaped URIs\n          uri_string = Puppet::Util.path_to_uri(source).to_s\n          # Ruby 1.9.3 and earlier have a URI bug in URI\n          # to_s returns an ASCII string despite UTF-8 fragments\n          # since its escaped its safe to universally call encode\n          # Puppet::Util.uri_unescape always returns strings in the original encoding\n          Puppet::Util.uri_unescape(uri_string.encode(Encoding::UTF_8))\n        else\n          source\n        end\n      end\n    end\n\n    def self.normalize(source)\n      source.sub(/[#{SEPARATOR_REGEX}]+$/, '')\n    end\n\n    def change_to_s(currentvalue, newvalue)\n      # newvalue = \"{md5}#{@metadata.checksum}\"\n      if resource.property(:ensure).retrieve == :absent\n        \"creating from source #{metadata.source} with contents #{metadata.checksum}\"\n      else\n        \"replacing from source #{metadata.source} with contents #{metadata.checksum}\"\n      end\n    end\n\n    def checksum\n      metadata && metadata.checksum\n    end\n\n    # Copy the values from the source to the resource.  Yay.\n    def copy_source_values\n      devfail \"Somehow got asked to copy source values without any metadata\" unless metadata\n\n      # conditionally copy :checksum\n      if metadata.ftype != \"directory\" && !(metadata.ftype == \"link\" && metadata.links == :manage)\n        copy_source_value(:checksum)\n      end\n\n      # Take each of the stats and set them as states on the local file\n      # if a value has not already been provided.\n      [:owner, :mode, :group].each do |metadata_method|\n        next if metadata_method == :owner and !Puppet.features.root?\n        next if metadata_method == :group and !Puppet.features.root?\n\n        case resource[:source_permissions]\n        when :ignore, nil\n          next\n        when :use_when_creating\n          next if Puppet::FileSystem.exist?(resource[:path])\n        end\n\n        copy_source_value(metadata_method)\n      end\n\n      if resource[:ensure] == :absent\n        # We know all we need to\n      elsif metadata.ftype != \"link\"\n        resource[:ensure] = metadata.ftype\n      elsif resource[:links] == :follow\n        resource[:ensure] = :present\n      else\n        resource[:ensure] = \"link\"\n        resource[:target] = metadata.destination\n      end\n    end\n\n    attr_writer :metadata\n\n    # Provide, and retrieve if necessary, the metadata for this file.  Fail\n    # if we can't find data about this host, and fail if there are any\n    # problems in our query.\n    def metadata\n      @metadata ||= resource.catalog.metadata[resource.title]\n      return @metadata if @metadata\n\n      return nil unless value\n\n      value.each do |source|\n        options = {\n          :environment => resource.catalog.environment_instance,\n          :links => resource[:links],\n          :checksum_type => resource[:checksum],\n          :source_permissions => resource[:source_permissions]\n        }\n\n        data = Puppet::FileServing::Metadata.indirection.find(source, options)\n        if data\n          @metadata = data\n          @metadata.source = source\n          break\n        end\n      rescue => detail\n        self.fail Puppet::Error, \"Could not retrieve file metadata for #{source}: #{detail}\", detail\n      end\n      self.fail \"Could not retrieve information from environment #{resource.catalog.environment} source(s) #{value.join(', ')}\" unless @metadata\n      @metadata\n    end\n\n    def local?\n      found? and scheme == \"file\"\n    end\n\n    def full_path\n      Puppet::Util.uri_to_path(uri) if found?\n    end\n\n    def server?\n      uri && uri.host && !uri.host.empty?\n    end\n\n    def server\n      server? ? uri.host : Puppet.settings[:server]\n    end\n\n    def port\n      (uri and uri.port) or Puppet.settings[:serverport]\n    end\n\n    def uri\n      @uri ||= URI.parse(Puppet::Util.uri_encode(metadata.source))\n    end\n\n    def write(file)\n      resource.parameter(:checksum).sum_stream { |sum|\n        each_chunk_from { |chunk|\n          sum << chunk\n          file.print chunk\n        }\n      }\n    end\n\n    private\n\n    def scheme\n      (uri and uri.scheme)\n    end\n\n    def found?\n      !(metadata.nil? or metadata.ftype.nil?)\n    end\n\n    def copy_source_value(metadata_method)\n      param_name = (metadata_method == :checksum) ? :content : metadata_method\n      if resource[param_name].nil? or resource[param_name] == :absent\n        if Puppet::Util::Platform.windows? && [:owner, :group, :mode].include?(metadata_method)\n          devfail \"Should not have tried to use source owner/mode/group on Windows\"\n        end\n\n        value = metadata.send(metadata_method)\n        # Force the mode value in file resources to be a string containing octal.\n        value = value.to_s(8) if param_name == :mode && value.is_a?(Numeric)\n        resource[param_name] = value\n\n        if metadata_method == :checksum\n          # If copying checksum, also copy checksum_type\n          resource[:checksum] = metadata.checksum_type\n        end\n      end\n    end\n\n    def each_chunk_from(&block)\n      if Puppet[:default_file_terminus] == :file_server && scheme == 'puppet' && (uri.host.nil? || uri.host.empty?)\n        chunk_file_from_disk(metadata.full_path, &block)\n      elsif local?\n        chunk_file_from_disk(full_path, &block)\n      else\n        chunk_file_from_source(&block)\n      end\n    end\n\n    def chunk_file_from_disk(local_path)\n      File.open(local_path, \"rb\") do |src|\n        while chunk = src.read(8192) # rubocop:disable Lint/AssignmentInCondition\n          yield chunk\n        end\n      end\n    end\n\n    def get_from_content_uri_source(url, &block)\n      session = Puppet.lookup(:http_session)\n      api = session.route_to(:fileserver, url: url)\n\n      api.get_static_file_content(\n        path: Puppet::Util.uri_unescape(url.path),\n        environment: resource.catalog.environment_instance.to_s,\n        code_id: resource.catalog.code_id,\n        &block\n      )\n    end\n\n    def get_from_source_uri_source(url, &block)\n      session = Puppet.lookup(:http_session)\n      api = session.route_to(:fileserver, url: url)\n\n      api.get_file_content(\n        path: Puppet::Util.uri_unescape(url.path),\n        environment: resource.catalog.environment_instance.to_s,\n        &block\n      )\n    end\n\n    def get_from_http_source(url, &block)\n      client = Puppet.runtime[:http]\n      client.get(url, options: { include_system_store: true }) do |response|\n        raise Puppet::HTTP::ResponseError, response unless response.success?\n\n        response.read_body(&block)\n      end\n    end\n\n    def chunk_file_from_source(&block)\n      if uri.scheme =~ /^https?/\n        # Historically puppet has not encoded the http(s) source URL before parsing\n        # it, for example, if the path contains spaces, then it must be URL encoded\n        # as %20 in the manifest. Puppet behaves the same when retrieving file\n        # metadata via http(s), see Puppet::Indirector::FileMetadata::Http#find.\n        url = URI.parse(metadata.source)\n        get_from_http_source(url, &block)\n      elsif metadata.content_uri\n        content_url = URI.parse(Puppet::Util.uri_encode(metadata.content_uri))\n        get_from_content_uri_source(content_url, &block)\n      else\n        get_from_source_uri_source(uri, &block)\n      end\n    rescue Puppet::HTTP::ResponseError => e\n      handle_response_error(e.response)\n    end\n\n    def handle_response_error(response)\n      message = \"Error #{response.code} on SERVER: #{response.body.empty? ? response.reason : response.body}\"\n      raise Net::HTTPError.new(message, Puppet::HTTP::ResponseConverter.to_ruby_response(response))\n    end\n  end\n\n  Puppet::Type.type(:file).newparam(:source_permissions) do\n    desc <<-'EOT'\n      Whether (and how) Puppet should copy owner, group, and mode permissions from\n      the `source` to `file` resources when the permissions are not explicitly\n      specified. (In all cases, explicit permissions will take precedence.)\n      Valid values are `use`, `use_when_creating`, and `ignore`:\n\n      * `ignore` (the default) will never apply the owner, group, or mode from\n        the `source` when managing a file. When creating new files without explicit\n        permissions, the permissions they receive will depend on platform-specific\n        behavior. On POSIX, Puppet will use the umask of the user it is running as.\n        On Windows, Puppet will use the default DACL associated with the user it is\n        running as.\n      * `use` will cause Puppet to apply the owner, group,\n        and mode from the `source` to any files it is managing.\n      * `use_when_creating` will only apply the owner, group, and mode from the\n        `source` when creating a file; existing files will not have their permissions\n        overwritten.\n    EOT\n\n    defaultto :ignore\n    newvalues(:use, :use_when_creating, :ignore)\n    munge do |value|\n      value = value ? value.to_sym : :ignore\n      if @resource.file && @resource.line && value != :ignore\n        # TRANSLATORS \"source_permissions\" is a parameter name and should not be translated\n        Puppet.puppet_deprecation_warning(_(\"The `source_permissions` parameter is deprecated. Explicitly set `owner`, `group`, and `mode`.\"), file: @resource.file, line: @resource.line)\n      end\n\n      value\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/target.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:target) do\n    desc \"The target for creating a link.  Currently, symlinks are the\n      only type supported. This attribute is mutually exclusive with `source`\n      and `content`.\n\n      Symlink targets can be relative, as well as absolute:\n\n          # (Useful on Solaris)\n          file { '/etc/inetd.conf':\n            ensure => link,\n            target => 'inet/inetd.conf',\n          }\n\n      Directories of symlinks can be served recursively by instead using the\n      `source` attribute, setting `ensure` to `directory`, and setting the\n      `links` attribute to `manage`.\"\n\n    newvalue(:notlink) do\n      # We do nothing if the value is absent\n      return :nochange\n    end\n\n    # Anything else, basically\n    newvalue(/./) do\n      @resource[:ensure] = :link unless @resource.should(:ensure)\n\n      # Only call mklink if ensure didn't call us in the first place.\n      currentensure = @resource.property(:ensure).retrieve\n      mklink if @resource.property(:ensure).safe_insync?(currentensure)\n    end\n\n    # Create our link.\n    def mklink\n      raise Puppet::Error, \"Cannot symlink on this platform version\" unless provider.feature?(:manages_symlinks)\n\n      target = should\n\n      # Clean up any existing objects.  The argument is just for logging,\n      # it doesn't determine what's removed.\n      @resource.remove_existing(target)\n\n      raise Puppet::Error, \"Could not remove existing file\" if Puppet::FileSystem.exist?(@resource[:path])\n\n      Puppet::Util::SUIDManager.asuser(@resource.asuser) do\n        mode = @resource.should(:mode)\n        if mode\n          Puppet::Util.withumask(0o00) do\n            Puppet::FileSystem.symlink(target, @resource[:path])\n          end\n        else\n          Puppet::FileSystem.symlink(target, @resource[:path])\n        end\n      end\n\n      @resource.send(:property_fix)\n\n      :link_created\n    end\n\n    def insync?(currentvalue)\n      if [:nochange, :notlink].include?(should) or @resource.recurse?\n        true\n      elsif !@resource.replace? and Puppet::FileSystem.exist?(@resource[:path])\n        true\n      else\n        super(currentvalue)\n      end\n    end\n\n    def retrieve\n      stat = @resource.stat\n      if stat\n        if stat.ftype == \"link\"\n          Puppet::FileSystem.readlink(@resource[:path])\n        else\n          :notlink\n        end\n      else\n        :absent\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file/type.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Puppet::Type.type(:file).newproperty(:type) do\n    require 'etc'\n    desc \"A read-only state to check the file type.\"\n\n    def retrieve\n      current_value = :absent\n      stat = @resource.stat\n      if stat\n        current_value = stat.ftype\n      end\n      current_value\n    end\n\n    validate do |_val|\n      fail \"type is read-only\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/file.rb",
    "content": "# coding: utf-8\n# frozen_string_literal: true\n\nrequire 'digest/md5'\nrequire 'cgi'\nrequire 'etc'\nrequire 'uri'\nrequire 'fileutils'\nrequire 'pathname'\nrequire_relative '../../puppet/parameter/boolean'\nrequire_relative '../../puppet/util/diff'\nrequire_relative '../../puppet/util/checksums'\nrequire_relative '../../puppet/util/backups'\nrequire_relative '../../puppet/util/symbolic_file_mode'\n\nPuppet::Type.newtype(:file) do\n  include Puppet::Util::Checksums\n  include Puppet::Util::Backups\n  include Puppet::Util::SymbolicFileMode\n\n  @doc = \"Manages files, including their content, ownership, and permissions.\n\n    The `file` type can manage normal files, directories, and symlinks; the\n    type should be specified in the `ensure` attribute.\n\n    File contents can be managed directly with the `content` attribute, or\n    downloaded from a remote source using the `source` attribute; the latter\n    can also be used to recursively serve directories (when the `recurse`\n    attribute is set to `true` or `local`). On Windows, note that file\n    contents are managed in binary mode; Puppet never automatically translates\n    line endings.\n\n    **Autorequires:** If Puppet is managing the user or group that owns a\n    file, the file resource will autorequire them. If Puppet is managing any\n    parent directories of a file, the file resource autorequires them.\n\n    Warning: Enabling `recurse` on directories containing large numbers of\n    files slows agent runs. To manage file attributes for many files,\n    consider using alternative methods such as the `chmod_r`, `chown_r`,\n     or `recursive_file_permissions` modules from the Forge.\"\n\n  feature :manages_symlinks,\n          \"The provider can manage symbolic links.\"\n\n  def self.title_patterns\n    # strip trailing slashes from path but allow the root directory, including\n    # for example \"/\" or \"C:/\"\n    [[%r{^(/|.+:/|.*[^/])/*\\Z}m, [[:path]]]]\n  end\n\n  newparam(:path) do\n    desc <<-'EOT'\n      The path to the file to manage.  Must be fully qualified.\n\n      On Windows, the path should include the drive letter and should use `/` as\n      the separator character (rather than `\\\\`).\n    EOT\n    isnamevar\n\n    validate do |value|\n      unless Puppet::Util.absolute_path?(value)\n        fail Puppet::Error, _(\"File paths must be fully qualified, not '%{path}'\") % { path: value }\n      end\n    end\n\n    munge do |value|\n      if value.start_with?('//') and ::File.basename(value) == \"/\"\n        # This is a UNC path pointing to a share, so don't add a trailing slash\n        ::File.expand_path(value)\n      else\n        ::File.join(::File.split(::File.expand_path(value)))\n      end\n    end\n  end\n\n  newparam(:backup) do\n    desc <<-EOT\n      Whether (and how) file content should be backed up before being replaced.\n      This attribute works best as a resource default in the site manifest\n      (`File { backup => main }`), so it can affect all file resources.\n\n      * If set to `false`, file content won't be backed up.\n      * If set to a string beginning with `.`, such as `.puppet-bak`, Puppet will\n        use copy the file in the same directory with that value as the extension\n        of the backup. (A value of `true` is a synonym for `.puppet-bak`.)\n      * If set to any other string, Puppet will try to back up to a filebucket\n        with that title. Puppet automatically creates a **local** filebucket\n        named `puppet` if one doesn't already exist. See the `filebucket` resource\n        type for more details.\n\n      Default value: `false`\n\n      Backing up to a local filebucket isn't particularly useful. If you want\n      to make organized use of backups, you will generally want to use the\n      primary Puppet server's filebucket service. This requires declaring a\n      filebucket resource and a resource default for the `backup` attribute\n      in site.pp:\n\n          # /etc/puppetlabs/puppet/manifests/site.pp\n          filebucket { 'main':\n            path   => false,                # This is required for remote filebuckets.\n            server => 'puppet.example.com', # Optional; defaults to the configured primary Puppet server.\n          }\n\n          File { backup => main, }\n\n      If you are using multiple primary servers, you will want to\n      centralize the contents of the filebucket. Either configure your load\n      balancer to direct all filebucket traffic to a single primary server, or use\n      something like an out-of-band rsync task to synchronize the content on all\n      primary servers.\n\n      > **Note**: Enabling and using the backup option, and by extension the\n        filebucket resource, requires appropriate planning and management to ensure\n        that sufficient disk space is available for the file backups. Generally, you\n        can implement this using one of the following two options:\n        - Use a `find` command and `crontab` entry to retain only the last X days\n        of file backups. For example:\n\n        ```\n        find /opt/puppetlabs/server/data/puppetserver/bucket -type f -mtime +45 -atime +45 -print0 | xargs -0 rm\n        ```\n\n        - Restrict the directory to a maximum size after which the oldest items are removed.\n    EOT\n\n    defaultto false\n\n    munge do |value|\n      # I don't really know how this is happening.\n      value = value.shift if value.is_a?(Array)\n\n      case value\n      when false, \"false\", :false\n        false\n      when true, \"true\", \".puppet-bak\", :true\n        \".puppet-bak\"\n      when String\n        value\n      else\n        self.fail _(\"Invalid backup type %{value}\") % { value: value.inspect }\n      end\n    end\n  end\n\n  newparam(:recurse) do\n    desc \"Whether to recursively manage the _contents_ of a directory. This attribute\n      is only used when `ensure => directory` is set. The allowed values are:\n\n      * `false` --- The default behavior. The contents of the directory will not be\n        automatically managed.\n      * `remote` --- If the `source` attribute is set, Puppet will automatically\n        manage the contents of the source directory (or directories), ensuring\n        that equivalent files and directories exist on the target system and\n        that their contents match.\n\n        Using `remote` will disable the `purge` attribute, but results in faster\n        catalog application than `recurse => true`.\n\n        The `source` attribute is mandatory when `recurse => remote`.\n      * `true` --- If the `source` attribute is set, this behaves similarly to\n        `recurse => remote`, automatically managing files from the source directory.\n\n        This also enables the `purge` attribute, which can delete unmanaged\n        files from a directory. See the description of `purge` for more details.\n\n        The `source` attribute is not mandatory when using `recurse => true`, so you\n        can enable purging in directories where all files are managed individually.\n\n      By default, setting recurse to `remote` or `true` will manage _all_\n      subdirectories. You can use the `recurselimit` attribute to limit the\n      recursion depth.\n    \"\n\n    newvalues(:true, :false, :remote)\n\n    validate { |arg| }\n    munge do |value|\n      newval = super(value)\n      case newval\n      when :true; true\n      when :false; false\n      when :remote; :remote\n      else\n        self.fail _(\"Invalid recurse value %{value}\") % { value: value.inspect }\n      end\n    end\n  end\n\n  newparam(:recurselimit) do\n    desc \"How far Puppet should descend into subdirectories, when using\n      `ensure => directory` and either `recurse => true` or `recurse => remote`.\n      The recursion limit affects which files will be copied from the `source`\n      directory, as well as which files can be purged when `purge => true`.\n\n      Setting `recurselimit => 0` is the same as setting `recurse => false` ---\n      Puppet will manage the directory, but all of its contents will be treated\n      as unmanaged.\n\n      Setting `recurselimit => 1` will manage files and directories that are\n      directly inside the directory, but will not manage the contents of any\n      subdirectories.\n\n      Setting `recurselimit => 2` will manage the direct contents of the\n      directory, as well as the contents of the _first_ level of subdirectories.\n\n      This pattern continues for each incremental value of `recurselimit`.\"\n\n    newvalues(/^[0-9]+$/)\n\n    munge do |value|\n      newval = super(value)\n      case newval\n      when Integer; value\n      when /^\\d+$/; Integer(value)\n      else\n        self.fail _(\"Invalid recurselimit value %{value}\") % { value: value.inspect }\n      end\n    end\n  end\n\n  newparam(:max_files) do\n    desc \"In case the resource is a directory and the recursion is enabled, puppet will\n      generate a new resource for each file file found, possible leading to\n      an excessive number of resources generated without any control.\n\n      Setting `max_files` will check the number of file resources that\n      will eventually be created and will raise a resource argument error if the\n      limit will be exceeded.\n\n      Use value `0` to log a warning instead of raising an error.\n\n      Use value `-1` to disable errors and warnings due to max files.\"\n\n    defaultto 0\n    newvalues(/^[0-9]+$/, /^-1$/)\n  end\n\n  newparam(:replace, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n    desc \"Whether to replace a file or symlink that already exists on the local system but\n      whose content doesn't match what the `source` or `content` attribute\n      specifies.  Setting this to false allows file resources to initialize files\n      without overwriting future changes.  Note that this only affects content;\n      Puppet will still manage ownership and permissions.\"\n    defaultto :true\n  end\n\n  newparam(:force, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n    desc \"Perform the file operation even if it will destroy one or more directories.\n      You must use `force` in order to:\n\n      * `purge` subdirectories\n      * Replace directories with files or links\n      * Remove a directory when `ensure => absent`\"\n    defaultto false\n  end\n\n  newparam(:ignore) do\n    desc \"A parameter which omits action on files matching\n      specified patterns during recursion.  Uses Ruby's builtin globbing\n      engine, so shell metacharacters such as `[a-z]*` are fully supported.\n      Matches that would descend into the directory structure are ignored,\n      such as `*/*`.\"\n\n    validate do |value|\n      unless value.is_a?(Array) or value.is_a?(String) or value == false\n        devfail \"Ignore must be a string or an Array\"\n      end\n    end\n  end\n\n  newparam(:links) do\n    desc \"How to handle links during file actions.  During file copying,\n      `follow` will copy the target file instead of the link and `manage`\n      will copy the link itself. When not copying, `manage` will manage\n      the link, and `follow` will manage the file to which the link points.\"\n\n    newvalues(:follow, :manage)\n\n    defaultto :manage\n  end\n\n  newparam(:purge, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n    desc \"Whether unmanaged files should be purged. This option only makes\n      sense when `ensure => directory` and `recurse => true`.\n\n      * When recursively duplicating an entire directory with the `source`\n        attribute, `purge => true` will automatically purge any files\n        that are not in the source directory.\n      * When managing files in a directory as individual resources,\n        setting `purge => true` will purge any files that aren't being\n        specifically managed.\n\n      If you have a filebucket configured, the purged files will be uploaded,\n      but if you do not, this will destroy data.\n\n      Unless `force => true` is set, purging will **not** delete directories,\n      although it will delete the files they contain.\n\n      If `recurselimit` is set and you aren't using `force => true`, purging\n      will obey the recursion limit; files in any subdirectories deeper than the\n      limit will be treated as unmanaged and left alone.\"\n\n    defaultto :false\n  end\n\n  newparam(:sourceselect) do\n    desc \"Whether to copy all valid sources, or just the first one.  This parameter\n      only affects recursive directory copies; by default, the first valid\n      source is the only one used, but if this parameter is set to `all`, then\n      all valid sources will have all of their contents copied to the local\n      system. If a given file exists in more than one source, the version from\n      the earliest source in the list will be used.\"\n\n    defaultto :first\n\n    newvalues(:first, :all)\n  end\n\n  newparam(:show_diff, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n    desc \"Whether to display differences when the file changes, defaulting to\n        true.  This parameter is useful for files that may contain passwords or\n        other secret data, which might otherwise be included in Puppet reports or\n        other insecure outputs.  If the global `show_diff` setting\n        is false, then no diffs will be shown even if this parameter is true.\"\n\n    defaultto :true\n  end\n\n  newparam(:staging_location) do\n    desc \"When rendering a file first render it to this location. The default\n         location is the same path as the desired location with a unique filename.\n         This parameter is useful in conjuction with validate_cmd to test a\n         file before moving the file to it's final location.\n         WARNING: File replacement is only guaranteed to be atomic if the staging\n         location is on the same filesystem as the final location.\"\n\n    validate do |value|\n      unless Puppet::Util.absolute_path?(value)\n        fail Puppet::Error, \"File paths must be fully qualified, not '#{value}'\"\n      end\n    end\n\n    munge do |value|\n      if value.start_with?('//') and ::File.basename(value) == \"/\"\n        # This is a UNC path pointing to a share, so don't add a trailing slash\n        ::File.expand_path(value)\n      else\n        ::File.join(::File.split(::File.expand_path(value)))\n      end\n    end\n  end\n\n  newparam(:validate_cmd) do\n    desc \"A command for validating the file's syntax before replacing it. If\n      Puppet would need to rewrite a file due to new `source` or `content`, it\n      will check the new content's validity first. If validation fails, the file\n      resource will fail.\n\n      This command must have a fully qualified path, and should contain a\n      percent (`%`) token where it would expect an input file. It must exit `0`\n      if the syntax is correct, and non-zero otherwise. The command will be\n      run on the target system while applying the catalog, not on the primary Puppet server.\n\n      Example:\n\n          file { '/etc/apache2/apache2.conf':\n            content      => 'example',\n            validate_cmd => '/usr/sbin/apache2 -t -f %',\n          }\n\n      This would replace apache2.conf only if the test returned true.\n\n      Note that if a validation command requires a `%` as part of its text,\n      you can specify a different placeholder token with the\n      `validate_replacement` attribute.\"\n  end\n\n  newparam(:validate_replacement) do\n    desc \"The replacement string in a `validate_cmd` that will be replaced\n      with an input file name.\"\n\n    defaultto '%'\n  end\n\n  # Autorequire the nearest ancestor directory found in the catalog.\n  autorequire(:file) do\n    req = []\n    path = Pathname.new(self[:path])\n    unless path.root?\n      # Start at our parent, to avoid autorequiring ourself\n      parents = path.parent.enum_for(:ascend)\n      found = parents.find { |p| catalog.resource(:file, p.to_s) }\n      if found\n        req << found.to_s\n      end\n    end\n    # if the resource is a link, make sure the target is created first\n    req << self[:target] if self[:target]\n    req\n  end\n\n  # Autorequire the owner and group of the file.\n  { :user => :owner, :group => :group }.each do |type, property|\n    autorequire(type) do\n      if @parameters.include?(property)\n        # The user/group property automatically converts to IDs\n        should = @parameters[property].shouldorig\n        next unless should\n\n        val = should[0]\n        if val.is_a?(Integer) or val =~ /^\\d+$/\n          nil\n        else\n          val\n        end\n      end\n    end\n  end\n\n  # mutually exclusive ways to create files\n  CREATORS = [:content, :source, :target].freeze\n\n  # This is both \"checksum types that can't be used with the content property\"\n  # and \"checksum types that are not digest based\"\n  SOURCE_ONLY_CHECKSUMS = [:none, :ctime, :mtime].freeze\n\n  validate do\n    creator_count = 0\n    CREATORS.each do |param|\n      creator_count += 1 if self.should(param)\n    end\n    creator_count += 1 if @parameters.include?(:source)\n\n    self.fail _(\"You cannot specify more than one of %{creators}\") % { creators: CREATORS.collect(&:to_s).join(\", \") } if creator_count > 1\n\n    self.fail _(\"You cannot specify a remote recursion without a source\") if !self[:source] && self[:recurse] == :remote\n\n    self.fail _(\"You cannot specify source when using checksum 'none'\") if self[:checksum] == :none && !self[:source].nil?\n\n    SOURCE_ONLY_CHECKSUMS.each do |checksum_type|\n      self.fail _(\"You cannot specify content when using checksum '%{checksum_type}'\") % { checksum_type: checksum_type } if self[:checksum] == checksum_type && !self[:content].nil?\n    end\n\n    warning _(\"Possible error: recurselimit is set but not recurse, no recursion will happen\") if !self[:recurse] && self[:recurselimit]\n\n    if @parameters[:content] && @parameters[:content].actual_content\n      # Now that we know the checksum, update content (in case it was created before checksum was known).\n      @parameters[:content].value = @parameters[:checksum].sum(@parameters[:content].actual_content)\n    end\n\n    if self[:checksum] && self[:checksum_value] && !valid_checksum?(self[:checksum], self[:checksum_value])\n      self.fail _(\"Checksum value '%{value}' is not a valid checksum type %{checksum}\") % { value: self[:checksum_value], checksum: self[:checksum] }\n    end\n\n    warning _(\"Checksum value is ignored unless content or source are specified\") if self[:checksum_value] && !self[:content] && !self[:source]\n\n    provider.validate if provider.respond_to?(:validate)\n  end\n\n  def self.[](path)\n    return nil unless path\n\n    super(path.gsub(%r{/+}, '/').sub(%r{/$}, ''))\n  end\n\n  def self.instances\n    []\n  end\n\n  # Determine the user to write files as.\n  def asuser\n    if self.should(:owner) && !self.should(:owner).is_a?(Symbol)\n      writeable = Puppet::Util::SUIDManager.asuser(self.should(:owner)) {\n        FileTest.writable?(::File.dirname(self[:path]))\n      }\n\n      # If the parent directory is writeable, then we execute\n      # as the user in question.  Otherwise we'll rely on\n      # the 'owner' property to do things.\n      asuser = self.should(:owner) if writeable\n    end\n\n    asuser\n  end\n\n  def bucket\n    return @bucket if @bucket\n\n    backup = self[:backup]\n    return nil unless backup\n    return nil if backup =~ /^\\./\n\n    unless catalog or backup == \"puppet\"\n      fail _(\"Can not find filebucket for backups without a catalog\")\n    end\n\n    filebucket = catalog.resource(:filebucket, backup) if catalog\n    if !catalog || (!filebucket && backup != 'puppet')\n      fail _(\"Could not find filebucket %{backup} specified in backup\") % { backup: backup }\n    end\n\n    return default_bucket unless filebucket\n\n    @bucket = filebucket.bucket\n\n    @bucket\n  end\n\n  def default_bucket\n    Puppet::Type.type(:filebucket).mkdefaultbucket.bucket\n  end\n\n  # Does the file currently exist?  Just checks for whether\n  # we have a stat\n  def exist?\n    stat ? true : false\n  end\n\n  def present?(current_values)\n    super && current_values[:ensure] != :false\n  end\n\n  # We have to do some extra finishing, to retrieve our bucket if\n  # there is one.\n  def finish\n    # Look up our bucket, if there is one\n    bucket\n    super\n  end\n\n  # Create any children via recursion or whatever.\n  def eval_generate\n    return [] unless recurse?\n\n    recurse\n  end\n\n  def ancestors\n    ancestors = Pathname.new(self[:path]).enum_for(:ascend).map(&:to_s)\n    ancestors.delete(self[:path])\n    ancestors\n  end\n\n  def flush\n    # We want to make sure we retrieve metadata anew on each transaction.\n    @parameters.each do |_name, param|\n      param.flush if param.respond_to?(:flush)\n    end\n    @stat = :needs_stat\n  end\n\n  def initialize(hash)\n    # Used for caching clients\n    @clients = {}\n\n    super\n\n    # If they've specified a source, we get our 'should' values\n    # from it.\n    unless self[:ensure]\n      if self[:target]\n        self[:ensure] = :link\n      elsif self[:content]\n        self[:ensure] = :file\n      end\n    end\n\n    @stat = :needs_stat\n  end\n\n  # Configure discovered resources to be purged.\n  def mark_children_for_purging(children)\n    children.each do |_name, child|\n      next if child[:source]\n\n      child[:ensure] = :absent\n    end\n  end\n\n  # Create a new file or directory object as a child to the current\n  # object.\n  def newchild(path)\n    full_path = ::File.join(self[:path], path)\n\n    # Add some new values to our original arguments -- these are the ones\n    # set at initialization.  We specifically want to exclude any param\n    # values set by the :source property or any default values.\n    # LAK:NOTE This is kind of silly, because the whole point here is that\n    # the values set at initialization should live as long as the resource\n    # but values set by default or by :source should only live for the transaction\n    # or so.  Unfortunately, we don't have a straightforward way to manage\n    # the different lifetimes of this data, so we kludge it like this.\n    # The right-side hash wins in the merge.\n    options = @original_parameters.merge(:path => full_path).compact\n\n    # These should never be passed to our children.\n    [:parent, :ensure, :recurse, :recurselimit, :max_files, :target, :alias, :source].each do |param|\n      options.delete(param) if options.include?(param)\n    end\n\n    self.class.new(options)\n  end\n\n  # Files handle paths specially, because they just lengthen their\n  # path names, rather than including the full parent's title each\n  # time.\n  def pathbuilder\n    # We specifically need to call the method here, so it looks\n    # up our parent in the catalog graph.\n    parent = parent()\n    if parent\n      # We only need to behave specially when our parent is also\n      # a file\n      if parent.is_a?(self.class)\n        # Remove the parent file name\n        list = parent.pathbuilder\n        list.pop # remove the parent's path info\n        list << ref\n      else\n        super\n      end\n    else\n      [ref]\n    end\n  end\n\n  # Recursively generate a list of file resources, which will\n  # be used to copy remote files, manage local files, and/or make links\n  # to map to another directory.\n  def recurse\n    children = (self[:recurse] == :remote) ? {} : recurse_local\n\n    if self[:target]\n      recurse_link(children)\n    elsif self[:source]\n      recurse_remote(children)\n    end\n\n    # If we're purging resources, then delete any resource that isn't on the\n    # remote system.\n    mark_children_for_purging(children) if purge?\n\n    result = children.values.sort_by { |a| a[:path] }\n    remove_less_specific_files(result)\n  end\n\n  def remove_less_specific_files(files)\n    existing_files = catalog.vertices.select { |r| r.is_a?(self.class) }\n    self.class.remove_less_specific_files(files, self[:path], existing_files) do |file|\n      file[:path]\n    end\n  end\n\n  # This is to fix bug #2296, where two files recurse over the same\n  # set of files.  It's a rare case, and when it does happen you're\n  # not likely to have many actual conflicts, which is good, because\n  # this is a pretty inefficient implementation.\n  def self.remove_less_specific_files(files, parent_path, existing_files, &block)\n    # REVISIT: is this Windows safe?  AltSeparator?\n    mypath = parent_path.split(::File::Separator)\n    other_paths = existing_files\n                  .select { |r| (yield r) != parent_path }\n                  .collect { |r| (yield r).split(::File::Separator) }\n                  .select  { |p| p[0, mypath.length] == mypath }\n\n    return files if other_paths.empty?\n\n    files.reject { |file|\n      path = (yield file).split(::File::Separator)\n      other_paths.any? { |p| path[0, p.length] == p }\n    }\n  end\n\n  # A simple method for determining whether we should be recursing.\n  def recurse?\n    self[:recurse] == true or self[:recurse] == :remote\n  end\n\n  # Recurse the target of the link.\n  def recurse_link(children)\n    perform_recursion(self[:target]).each do |meta|\n      if meta.relative_path == \".\"\n        self[:ensure] = :directory\n        next\n      end\n\n      children[meta.relative_path] ||= newchild(meta.relative_path)\n      if meta.ftype == \"directory\"\n        children[meta.relative_path][:ensure] = :directory\n      else\n        children[meta.relative_path][:ensure] = :link\n        children[meta.relative_path][:target] = meta.full_path\n      end\n    end\n    children\n  end\n\n  # Recurse the file itself, returning a Metadata instance for every found file.\n  def recurse_local\n    result = perform_recursion(self[:path])\n    return {} unless result\n\n    result.each_with_object({}) do |meta, hash|\n      next hash if meta.relative_path == \".\"\n\n      hash[meta.relative_path] = newchild(meta.relative_path)\n    end\n  end\n\n  # Recurse against our remote file.\n  def recurse_remote(children)\n    recurse_remote_metadata.each do |meta|\n      if meta.relative_path == \".\"\n        self[:checksum] = meta.checksum_type\n        parameter(:source).metadata = meta\n        next\n      end\n      children[meta.relative_path] ||= newchild(meta.relative_path)\n      children[meta.relative_path][:source] = meta.source\n      children[meta.relative_path][:checksum] = meta.checksum_type\n      children[meta.relative_path].parameter(:source).metadata = meta\n    end\n\n    children\n  end\n\n  def recurse_remote_metadata\n    sourceselect = self[:sourceselect]\n\n    total = self[:source].collect do |source|\n      # For each inlined file resource, the catalog contains a hash mapping\n      # source path to lists of metadata returned by a server-side search.\n      recursive_metadata = catalog.recursive_metadata[title]\n      if recursive_metadata\n        result = recursive_metadata[source]\n      else\n        result = perform_recursion(source)\n      end\n\n      next unless result\n\n      top = result.find { |r| r.relative_path == \".\" }\n      return [] if top && top.ftype != \"directory\"\n\n      result.each do |data|\n        if data.relative_path == '.'\n          data.source = source\n        else\n          # REMIND: appending file paths to URL may not be safe, e.g. foo+bar\n          data.source = \"#{source}/#{data.relative_path}\"\n        end\n      end\n      break result if result and !result.empty? and sourceselect == :first\n\n      result\n    end.flatten.compact\n\n    # This only happens if we have sourceselect == :all\n    unless sourceselect == :first\n      found = []\n      total.reject! do |data|\n        result = found.include?(data.relative_path)\n        found << data.relative_path unless result\n        result\n      end\n    end\n\n    total\n  end\n\n  def perform_recursion(path)\n    Puppet::FileServing::Metadata.indirection.search(\n      path,\n      :links => self[:links],\n      :recurse => (self[:recurse] == :remote ? true : self[:recurse]),\n      :recurselimit => self[:recurselimit],\n      :max_files => self[:max_files],\n      :source_permissions => self[:source_permissions],\n      :ignore => self[:ignore],\n      :checksum_type => (self[:source] || self[:content]) ? self[:checksum] : :none,\n      :environment => catalog.environment_instance\n    )\n  end\n\n  # Back up and remove the file or directory at `self[:path]`.\n  #\n  # @param  [Symbol] should The file type replacing the current content.\n  # @return [Boolean] True if the file was removed, else False\n  # @raises [fail???] If the file could not be backed up or could not be removed.\n  def remove_existing(should)\n    wanted_type = should.to_s\n    current_type = read_current_type\n\n    if current_type.nil?\n      return false\n    end\n\n    if self[:backup]\n      if can_backup?(current_type)\n        backup_existing\n      else\n        warning _(\"Could not back up file of type %{current_type}\") % { current_type: current_type }\n      end\n    end\n\n    if wanted_type != \"link\" and current_type == wanted_type\n      return false\n    end\n\n    case current_type\n    when \"directory\"\n      remove_directory(wanted_type)\n    when \"link\", \"file\", \"fifo\", \"socket\"\n      remove_file(current_type, wanted_type)\n    else\n      # Including: “blockSpecial”, “characterSpecial”, “unknown”\n      self.fail _(\"Could not remove files of type %{current_type}\") % { current_type: current_type }\n    end\n  end\n\n  def retrieve\n    # This check is done in retrieve to ensure it happens before we try to use\n    # metadata in `copy_source_values`, but so it only fails the resource and not\n    # catalog validation (because that would be a breaking change from Puppet 4).\n    if Puppet::Util::Platform.windows? && parameter(:source) &&\n       [:use, :use_when_creating].include?(self[:source_permissions])\n      # TRANSLATORS \"source_permissions => ignore\" should not be translated\n      err_msg = _(\"Copying owner/mode/group from the source file on Windows is not supported; use source_permissions => ignore.\")\n      if self[:owner].nil? || self[:group].nil? || self[:mode].nil?\n        # Fail on Windows if source permissions are being used and the file resource\n        # does not have mode owner, group, and mode all set (which would take precedence).\n        self.fail err_msg\n      else\n        # Warn if use source permissions is specified on Windows\n        warning err_msg\n      end\n    end\n\n    # `checksum_value` implies explicit management of all metadata, so skip metadata\n    # retrieval. Otherwise, if source is set, retrieve metadata for source.\n    if (source = parameter(:source)) && property(:checksum_value).nil?\n      source.copy_source_values\n    end\n    super\n  end\n\n  # Set the checksum, from another property.  There are multiple\n  # properties that modify the contents of a file, and they need the\n  # ability to make sure that the checksum value is in sync.\n  def setchecksum(sum = nil)\n    if @parameters.include? :checksum\n      if sum\n        @parameters[:checksum].checksum = sum\n      else\n        # If they didn't pass in a sum, then tell checksum to\n        # figure it out.\n        currentvalue = @parameters[:checksum].retrieve\n        @parameters[:checksum].checksum = currentvalue\n      end\n    end\n  end\n\n  # Should this thing be a normal file?  This is a relatively complex\n  # way of determining whether we're trying to create a normal file,\n  # and it's here so that the logic isn't visible in the content property.\n  def should_be_file?\n    return true if self[:ensure] == :file\n\n    # I.e., it's set to something like \"directory\"\n    return false if self[:ensure] && self[:ensure] != :present\n\n    # The user doesn't really care, apparently\n    if self[:ensure] == :present\n      return true unless stat\n\n      return(stat.ftype == \"file\")\n    end\n\n    # If we've gotten here, then :ensure isn't set\n    return true if self[:content]\n    return true if stat and stat.ftype == \"file\"\n\n    false\n  end\n\n  # Stat our file.  Depending on the value of the 'links' attribute, we\n  # use either 'stat' or 'lstat', and we expect the properties to use the\n  # resulting stat object accordingly (mostly by testing the 'ftype'\n  # value).\n  #\n  # We use the initial value :needs_stat to ensure we only stat the file once,\n  # but can also keep track of a failed stat (@stat == nil). This also allows\n  # us to re-stat on demand by setting @stat = :needs_stat.\n  def stat\n    return @stat unless @stat == :needs_stat\n\n    method = :stat\n\n    # Files are the only types that support links\n    if instance_of?(Puppet::Type::File) and self[:links] != :follow\n      method = :lstat\n    end\n\n    @stat = begin\n      Puppet::FileSystem.send(method, self[:path])\n    rescue Errno::ENOENT\n      nil\n    rescue Errno::ENOTDIR\n      nil\n    rescue Errno::EACCES\n      warning _(\"Could not stat; permission denied\")\n      nil\n    rescue Errno::EINVAL\n      warning _(\"Could not stat; invalid pathname\")\n      nil\n    end\n  end\n\n  def to_resource\n    resource = super\n    resource.delete(:target) if resource[:target] == :notlink\n    resource\n  end\n\n  # Write out the file. To write content, pass the property as an argument\n  # to delegate writing to; must implement a #write method that takes the file\n  # as an argument.\n  def write(property = nil)\n    remove_existing(:file)\n\n    mode = self.should(:mode) # might be nil\n    mode_int = mode ? symbolic_mode_to_int(mode, Puppet::Util::DEFAULT_POSIX_MODE) : nil\n\n    if write_temporary_file?\n      if self[:validate_cmd]\n        validate_callback = proc { |path|\n          output = Puppet::Util::Execution.execute(self[:validate_cmd].gsub(self[:validate_replacement], path), :failonfail => true, :combine => true)\n          output.split(/\\n/).each { |line|\n            debug(line)\n          }\n        }\n      end\n\n      Puppet::Util.replace_file(self[:path], mode_int, staging_location: self[:staging_location], validate_callback: validate_callback) do |file|\n        file.binmode\n        devfail 'a property should have been provided if write_temporary_file? returned true' if property.nil?\n        content_checksum = property.write(file)\n        file.flush\n        begin\n          file.fsync\n        rescue NotImplementedError\n          # fsync may not be implemented by Ruby on all platforms, but\n          # there is absolutely no recovery path if we detect that.  So, we just\n          # ignore the return code.\n          #\n          # However, don't be fooled: that is accepting that we are running in\n          # an unsafe fashion.  If you are porting to a new platform don't stub\n          # that out.\n        end\n\n        fail_if_checksum_is_wrong(property, file.path, content_checksum)\n      end\n    else\n      umask = mode ? 0o00 : 0o22\n      Puppet::Util.withumask(umask) { ::File.open(self[:path], 'wb', mode_int) { |f| property.write(f) if property } }\n    end\n\n    # make sure all of the modes are actually correct\n    property_fix\n  end\n\n  private\n\n  # Carry the context of sensitive parameters to the properties that will actually handle that\n  # sensitive data.\n  #\n  # The file type can accept file content from a number of origins and depending on the current\n  # state of the system different properties will be responsible for synchronizing the file\n  # content. This method handles the necessary mapping of originating parameters to the\n  # responsible parameters.\n  def set_sensitive_parameters(sensitive_parameters)\n    # If we have content that's marked as sensitive but the file doesn't exist then the ensure\n    # property will be responsible for syncing content, so we have to mark ensure as sensitive as well.\n    if sensitive_parameters.include?(:content)\n      # The `ensure` parameter is not guaranteed to be defined either and will be conditionally set when\n      # the `content` property is set, so we need to force the creation of the `ensure` property to\n      # set the sensitive context.\n      newattr(:ensure).sensitive = true\n    end\n\n    # The source parameter isn't actually a property but works by injecting information into the\n    # content property. In order to preserve the intended sensitive context we need to mark content\n    # as sensitive as well.\n    if sensitive_parameters.include?(:source)\n      sensitive_parameters.delete(:source)\n      parameter(:source).sensitive = true\n      # The `source` parameter will generate the `content` property when the resource state is retrieved\n      # but that's long after we've set the sensitive context. Force the early creation of the `content`\n      # attribute so we can mark it as sensitive.\n      newattr(:content).sensitive = true\n      # As noted above, making the `content` property sensitive requires making the `ensure` property\n      # sensitive as well.\n      newattr(:ensure).sensitive = true\n    end\n\n    super(sensitive_parameters)\n  end\n\n  # @return [String] The type of the current file, cast to a string.\n  def read_current_type\n    stat_info = stat\n    if stat_info\n      stat_info.ftype.to_s\n    else\n      nil\n    end\n  end\n\n  # @return [Boolean] If the current file should be backed up and can be backed up.\n  def can_backup?(type)\n    if type == \"directory\" and force?\n      # (#18110) Directories cannot be removed without :force,\n      # so it doesn't make sense to back them up unless removing with :force.\n      true\n    else\n      type == \"file\" or type == \"link\"\n    end\n  end\n\n  # @return [Boolean] if the directory was removed (which is always true currently)\n  # @api private\n  def remove_directory(wanted_type)\n    if force?\n      debug \"Removing existing directory for replacement with #{wanted_type}\"\n      FileUtils.rmtree(self[:path])\n      stat_needed\n      true\n    else\n      notice _(\"Not removing directory; use 'force' to override\")\n      false\n    end\n  end\n\n  # @return [Boolean] if the file was removed (which is always true currently)\n  # @api private\n  def remove_file(current_type, wanted_type)\n    debug \"Removing existing #{current_type} for replacement with #{wanted_type}\"\n    Puppet::FileSystem.unlink(self[:path])\n    stat_needed\n    true\n  end\n\n  def stat_needed\n    @stat = :needs_stat\n  end\n\n  # Back up the existing file at a given prior to it being removed\n  # @api private\n  # @raise [Puppet::Error] if the file backup failed\n  # @return [void]\n  def backup_existing\n    unless perform_backup\n      # TRANSLATORS refers to a file which could not be backed up\n      raise Puppet::Error, _(\"Could not back up; will not remove\")\n    end\n  end\n\n  # Make sure the file we wrote out is what we think it is.\n  # @param [Puppet::Parameter] property the param or property that wrote the file, or nil\n  # @param [String] path to the file\n  # @param [String] the checksum for the local file\n  #\n  # @api private\n  #\n  def fail_if_checksum_is_wrong(property, path, content_checksum)\n    desired_checksum = desired_checksum(property, path)\n\n    if desired_checksum && content_checksum != desired_checksum\n      self.fail _(\"File written to disk did not match desired checksum; discarding changes (%{content_checksum} vs %{desired_checksum})\") % { content_checksum: content_checksum, desired_checksum: desired_checksum }\n    end\n  end\n\n  # Return the desired checksum or nil\n  def desired_checksum(property, path)\n    return if SOURCE_ONLY_CHECKSUMS.include?(self[:checksum])\n\n    if self[:checksum] && self[:checksum_value]\n      \"{#{self[:checksum]}}#{self[:checksum_value]}\"\n    elsif property && property.name == :source\n      meta = property.metadata\n      return unless meta\n\n      # due to HttpMetadata the checksum type may fallback to mtime, so recheck\n      return if SOURCE_ONLY_CHECKSUMS.include?(meta.checksum_type)\n\n      meta.checksum\n    elsif property && property.name == :content\n      str = property.actual_content\n      str ? parameter(:checksum).sum(str) : nil\n    end\n  end\n\n  def write_temporary_file?\n    # Unfortunately we don't know the source file size before fetching it so\n    # let's assume the file won't be empty. Why isn't it part of the metadata?\n    (c = property(:content) and c.length) || @parameters[:source]\n  end\n\n  # There are some cases where all of the work does not get done on\n  # file creation/modification, so we have to do some extra checking.\n  def property_fix\n    properties.each do |thing|\n      next unless [:mode, :owner, :group, :seluser, :selrole, :seltype, :selrange].include?(thing.name)\n\n      # Make sure we get a new stat object\n      @stat = :needs_stat\n      currentvalue = thing.retrieve\n      thing.sync unless thing.safe_insync?(currentvalue)\n    end\n  end\nend\n\n# We put all of the properties in separate files, because there are so many\n# of them.  The order these are loaded is important, because it determines\n# the order they are in the property lit.\nrequire_relative 'file/checksum'\nrequire_relative 'file/content'     # can create the file\nrequire_relative 'file/source'      # can create the file\nrequire_relative 'file/checksum_value' # can create the file, in place of content\nrequire_relative 'file/target'      # creates a different type of file\nrequire_relative 'file/ensure'      # can create the file\nrequire_relative 'file/owner'\nrequire_relative 'file/group'\nrequire_relative 'file/mode'\nrequire_relative 'file/type'\nrequire_relative 'file/selcontext'  # SELinux file context\nrequire_relative 'file/ctime'\nrequire_relative 'file/mtime'\n"
  },
  {
    "path": "lib/puppet/type/filebucket.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  require_relative '../../puppet/file_bucket/dipper'\n\n  Type.newtype(:filebucket) do\n    @doc = <<-EOT\n      A repository for storing and retrieving file content by cryptographic checksum. Can\n      be local to each agent node, or centralized on a primary Puppet server. All\n      puppet servers provide a filebucket service that agent nodes can access\n      via HTTP, but you must declare a filebucket resource before any agents\n      will do so.\n\n      Filebuckets are used for the following features:\n\n      - **Content backups.** If the `file` type's `backup` attribute is set to\n        the name of a filebucket, Puppet will back up the _old_ content whenever\n        it rewrites a file; see the documentation for the `file` type for more\n        details. These backups can be used for manual recovery of content, but\n        are more commonly used to display changes and differences in a tool like\n        Puppet Dashboard.\n\n      To use a central filebucket for backups, you will usually want to declare\n      a filebucket resource and a resource default for the `backup` attribute\n      in site.pp:\n\n          # /etc/puppetlabs/puppet/manifests/site.pp\n          filebucket { 'main':\n            path   => false,                # This is required for remote filebuckets.\n            server => 'puppet.example.com', # Optional; defaults to the configured primary server.\n          }\n\n          File { backup => main, }\n\n      Puppet Servers automatically provide the filebucket service, so\n      this will work in a default configuration. If you have a heavily\n      restricted Puppet Server `auth.conf` file, you may need to allow access to the\n      `file_bucket_file` endpoint.\n    EOT\n\n    newparam(:name) do\n      desc \"The name of the filebucket.\"\n      isnamevar\n    end\n\n    newparam(:server) do\n      desc \"The server providing the remote filebucket service.\n\n        This setting is _only_ consulted if the `path` attribute is set to `false`.\n\n        If this attribute is not specified, the first entry in the `server_list`\n        configuration setting is used, followed by the value of the `server` setting\n        if `server_list` is not set.\"\n    end\n\n    newparam(:port) do\n      desc \"The port on which the remote server is listening.\n\n        This setting is _only_ consulted if the `path` attribute is set to `false`.\n\n        If this attribute is not specified, the first entry in the `server_list`\n        configuration setting is used, followed by the value of the `serverport`\n        setting if `server_list` is not set.\"\n    end\n\n    newparam(:path) do\n      desc \"The path to the _local_ filebucket; defaults to the value of the\n        `clientbucketdir` setting.  To use a remote filebucket, you _must_ set\n        this attribute to `false`.\"\n\n      defaultto { Puppet[:clientbucketdir] }\n\n      validate do |value|\n        if value.is_a? Array\n          raise ArgumentError, _(\"You can only have one filebucket path\")\n        end\n\n        if value.is_a? String and !Puppet::Util.absolute_path?(value)\n          raise ArgumentError, _(\"Filebucket paths must be absolute\")\n        end\n\n        true\n      end\n    end\n\n    # Create a default filebucket.\n    def self.mkdefaultbucket\n      new(:name => \"puppet\", :path => Puppet[:clientbucketdir])\n    end\n\n    def bucket\n      mkbucket unless defined?(@bucket)\n      @bucket\n    end\n\n    private\n\n    def mkbucket\n      # Default is a local filebucket, if no server is given.\n      # If the default path has been removed, too, then\n      # the puppetmaster is used as default server\n\n      type = \"local\"\n      args = {}\n      if self[:path]\n        args[:Path] = self[:path]\n      else\n        args[:Server] = self[:server]\n        args[:Port] = self[:port]\n      end\n\n      begin\n        @bucket = Puppet::FileBucket::Dipper.new(args)\n      rescue => detail\n        message = _(\"Could not create %{type} filebucket: %{detail}\") % { type: type, detail: detail }\n        log_exception(detail, message)\n        self.fail(message)\n      end\n\n      @bucket.name = name\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/group.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'etc'\nrequire_relative '../../puppet/property/keyvalue'\nrequire_relative '../../puppet/parameter/boolean'\n\nmodule Puppet\n  Type.newtype(:group) do\n    @doc = \"Manage groups. On most platforms this can only create groups.\n      Group membership must be managed on individual users.\n\n      On some platforms such as OS X, group membership is managed as an\n      attribute of the group, not the user record. Providers must have\n      the feature 'manages_members' to manage the 'members' property of\n      a group record.\"\n\n    feature :manages_members,\n            \"For directories where membership is an attribute of groups not users.\"\n\n    feature :manages_aix_lam,\n            \"The provider can manage AIX Loadable Authentication Module (LAM) system.\"\n\n    feature :system_groups,\n            \"The provider allows you to create system groups with lower GIDs.\"\n\n    feature :manages_local_users_and_groups,\n            \"Allows local groups to be managed on systems that also use some other\n       remote Name Switch Service (NSS) method of managing accounts.\"\n\n    ensurable do\n      desc \"Create or remove the group.\"\n\n      newvalue(:present) do\n        provider.create\n      end\n\n      newvalue(:absent) do\n        provider.delete\n      end\n\n      defaultto :present\n    end\n\n    newproperty(:gid) do\n      desc \"The group ID.  Must be specified numerically.  If no group ID is\n        specified when creating a new group, then one will be chosen\n        automatically according to local system standards. This will likely\n        result in the same group having different GIDs on different systems,\n        which is not recommended.\n\n        On Windows, this property is read-only and will return the group's security\n        identifier (SID).\"\n\n      def retrieve\n        provider.gid\n      end\n\n      def sync\n        if should == :absent\n          raise Puppet::DevError, _(\"GID cannot be deleted\")\n        else\n          provider.gid = should\n        end\n      end\n\n      munge do |gid|\n        case gid\n        when String\n          if gid =~ /^[-0-9]+$/\n            gid = Integer(gid)\n          else\n            self.fail _(\"Invalid GID %{gid}\") % { gid: gid }\n          end\n        when Symbol\n          unless gid == :absent\n            devfail \"Invalid GID #{gid}\"\n          end\n        end\n\n        return gid\n      end\n    end\n\n    newproperty(:members, :array_matching => :all, :required_features => :manages_members) do\n      desc \"The members of the group. For platforms or directory services where group\n        membership is stored in the group objects, not the users. This parameter's\n        behavior can be configured with `auth_membership`.\"\n\n      def change_to_s(currentvalue, newvalue)\n        newvalue = actual_should(currentvalue, newvalue)\n\n        currentvalue = currentvalue.join(\",\") if currentvalue != :absent\n        newvalue = newvalue.join(\",\")\n        super(currentvalue, newvalue)\n      end\n\n      def insync?(current)\n        if provider.respond_to?(:members_insync?)\n          return provider.members_insync?(current, @should)\n        end\n\n        super(current)\n      end\n\n      def is_to_s(currentvalue) # rubocop:disable Naming/PredicateName\n        if provider.respond_to?(:members_to_s)\n          currentvalue = '' if currentvalue.nil?\n          currentvalue = currentvalue.is_a?(Array) ? currentvalue : currentvalue.split(',')\n\n          return provider.members_to_s(currentvalue)\n        end\n\n        super(currentvalue)\n      end\n\n      def should_to_s(newvalue)\n        is_to_s(actual_should(retrieve, newvalue))\n      end\n\n      # Calculates the actual should value given the current and\n      # new values. This is only used in should_to_s and change_to_s\n      # to fix the change notification issue reported in PUP-6542.\n      def actual_should(currentvalue, newvalue)\n        currentvalue = munge_members_value(currentvalue)\n        newvalue = munge_members_value(newvalue)\n\n        if @resource[:auth_membership]\n          newvalue.uniq.sort\n        else\n          (currentvalue + newvalue).uniq.sort\n        end\n      end\n\n      # Useful helper to handle the possible property value types that we can\n      # both pass-in and return. It munges the value into an array\n      def munge_members_value(value)\n        return [] if value == :absent\n        return value.split(',') if value.is_a?(String)\n\n        value\n      end\n\n      validate do |value|\n        if provider.respond_to?(:member_valid?)\n          return provider.member_valid?(value)\n        end\n      end\n    end\n\n    newparam(:auth_membership, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n      desc \"Configures the behavior of the `members` parameter.\n\n        * `false` (default) --- The provided list of group members is partial,\n          and Puppet **ignores** any members that aren't listed there.\n        * `true` --- The provided list of of group members is comprehensive, and\n          Puppet **purges** any members that aren't listed there.\"\n      defaultto false\n    end\n\n    newparam(:name) do\n      desc \"The group name. While naming limitations vary by operating system,\n        it is advisable to restrict names to the lowest common denominator,\n        which is a maximum of 8 characters beginning with a letter.\n\n        Note that Puppet considers group names to be case-sensitive, regardless\n        of the platform's own rules; be sure to always use the same case when\n        referring to a given group.\"\n      isnamevar\n    end\n\n    newparam(:allowdupe, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n      desc \"Whether to allow duplicate GIDs.\"\n\n      defaultto false\n    end\n\n    newparam(:ia_load_module, :required_features => :manages_aix_lam) do\n      desc \"The name of the I&A module to use to manage this group.\n        This should be set to `files` if managing local groups.\"\n    end\n\n    newproperty(:attributes, :parent => Puppet::Property::KeyValue, :required_features => :manages_aix_lam) do\n      desc \"Specify group AIX attributes, as an array of `'key=value'` strings. This\n        parameter's behavior can be configured with `attribute_membership`.\"\n\n      self.log_only_changed_or_new_keys = true\n\n      def membership\n        :attribute_membership\n      end\n\n      def delimiter\n        \" \"\n      end\n    end\n\n    newparam(:attribute_membership) do\n      desc \"AIX only. Configures the behavior of the `attributes` parameter.\n\n        * `minimum` (default) --- The provided list of attributes is partial, and Puppet\n          **ignores** any attributes that aren't listed there.\n        * `inclusive` --- The provided list of attributes is comprehensive, and\n          Puppet **purges** any attributes that aren't listed there.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newparam(:system, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n      desc \"Whether the group is a system group with lower GID.\"\n\n      defaultto false\n    end\n\n    newparam(:forcelocal, :boolean => true,\n                          :required_features => :manages_local_users_and_groups,\n                          :parent => Puppet::Parameter::Boolean) do\n      desc \"Forces the management of local accounts when accounts are also\n            being managed by some other Name Switch Service (NSS). For AIX, refer to the `ia_load_module` parameter.\n\n            This option relies on your operating system's implementation of `luser*` commands, such as `luseradd` , `lgroupadd`, and `lusermod`. The `forcelocal` option could behave unpredictably in some circumstances. If the tools it depends on are not available, it might have no effect at all.\"\n      defaultto false\n    end\n\n    # This method has been exposed for puppet to manage users and groups of\n    # files in its settings and should not be considered available outside of\n    # puppet.\n    #\n    # (see Puppet::Settings#service_group_available?)\n    #\n    # @return [Boolean] if the group exists on the system\n    # @api private\n    def exists?\n      provider.exists?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/notify.rb",
    "content": "# frozen_string_literal: true\n\n#\n# Simple module for logging messages on the client-side\n\nmodule Puppet\n  Type.newtype(:notify) do\n    @doc = \"Sends an arbitrary message, specified as a string, to the agent run-time log. It's important to note that the notify resource type is not idempotent. As a result, notifications are shown as a change on every Puppet run.\"\n\n    apply_to_all\n\n    newproperty(:message, :idempotent => false) do\n      desc \"The message to be sent to the log. Note that the value specified must be a string.\"\n      def sync\n        message = @sensitive ? 'Sensitive [value redacted]' : should\n        case @resource[\"withpath\"]\n        when :true\n          send(@resource[:loglevel], message)\n        else\n          Puppet.send(@resource[:loglevel], message)\n        end\n        nil\n      end\n\n      def retrieve\n        :absent\n      end\n\n      def insync?(is)\n        false\n      end\n\n      defaultto { @resource[:name] }\n    end\n\n    newparam(:withpath) do\n      desc \"Whether to show the full object path.\"\n      defaultto :false\n\n      newvalues(:true, :false)\n    end\n\n    newparam(:name) do\n      desc \"An arbitrary tag for your own reference; the name of the message.\"\n      isnamevar\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/package.rb",
    "content": "# coding: utf-8\n# frozen_string_literal: true\n\n# Define the different packaging systems.  Each package system is implemented\n# in a module, which then gets used to individually extend each package object.\n# This allows packages to exist on the same machine using different packaging\n# systems.\n\nrequire_relative '../../puppet/parameter/package_options'\nrequire_relative '../../puppet/parameter/boolean'\n\nmodule Puppet\n  Type.newtype(:package) do\n    @doc = \"Manage packages.  There is a basic dichotomy in package\n      support right now:  Some package types (such as yum and apt) can\n      retrieve their own package files, while others (such as rpm and sun)\n      cannot.  For those package formats that cannot retrieve their own files,\n      you can use the `source` parameter to point to the correct file.\n\n      Puppet will automatically guess the packaging format that you are\n      using based on the platform you are on, but you can override it\n      using the `provider` parameter; each provider defines what it\n      requires in order to function, and you must meet those requirements\n      to use a given provider.\n\n      You can declare multiple package resources with the same `name` as long\n      as they have unique titles, and specify different providers and commands.\n\n      Note that you must use the _title_ to make a reference to a package\n      resource; `Package[<NAME>]` is not a synonym for `Package[<TITLE>]` like\n      it is for many other resource types.\n\n      **Autorequires:** If Puppet is managing the files specified as a\n      package's `adminfile`, `responsefile`, or `source`, the package\n      resource will autorequire those files.\"\n\n    feature :reinstallable, \"The provider can reinstall packages.\",\n            :methods => [:reinstall]\n    feature :installable, \"The provider can install packages.\",\n            :methods => [:install]\n    feature :uninstallable, \"The provider can uninstall packages.\",\n            :methods => [:uninstall]\n    feature :upgradeable, \"The provider can upgrade to the latest version of a\n        package.  This feature is used by specifying `latest` as the\n        desired value for the package.\",\n            :methods => [:update, :latest]\n    feature :purgeable, \"The provider can purge packages.  This generally means\n        that all traces of the package are removed, including\n        existing configuration files.  This feature is thus destructive\n        and should be used with the utmost care.\",\n            :methods => [:purge]\n    feature :versionable, \"The provider is capable of interrogating the\n        package database for installed version(s), and can select\n        which out of a set of available versions of a package to\n        install if asked.\"\n    feature :version_ranges, \"The provider can ensure version ranges.\"\n    feature :holdable, \"The provider is capable of placing packages on hold\n        such that they are not automatically upgraded as a result of\n        other package dependencies unless explicit action is taken by\n        a user or another package.\",\n            :methods => [:hold, :unhold]\n    feature :install_only, \"The provider accepts options to only install packages never update (kernels, etc.)\"\n    feature :install_options, \"The provider accepts options to be\n      passed to the installer command.\"\n    feature :uninstall_options, \"The provider accepts options to be\n      passed to the uninstaller command.\"\n    feature :disableable, \"The provider can disable packages. This feature is used by specifying `disabled` as the\n      desired value for the package.\",\n            :methods => [:disable]\n    feature :supports_flavors, \"The provider accepts flavors, which are specific variants of packages.\"\n    feature :package_settings, \"The provider accepts package_settings to be\n      ensured for the given package. The meaning and format of these settings is\n      provider-specific.\",\n            :methods => [:package_settings_insync?, :package_settings, :package_settings=]\n    feature :virtual_packages, \"The provider accepts virtual package names for install and uninstall.\"\n\n    feature :targetable, \"The provider accepts a targeted package management command.\"\n\n    ensurable do\n      desc <<-EOT\n        What state the package should be in. On packaging systems that can\n        retrieve new packages on their own, you can choose which package to\n        retrieve by specifying a version number or `latest` as the ensure\n        value. On packaging systems that manage configuration files separately\n        from \"normal\" system files, you can uninstall config files by\n        specifying `purged` as the ensure value. This defaults to `installed`.\n\n        Version numbers must match the full version to install, including\n        release if the provider uses a release moniker. For\n        example, to install the bash package from the rpm\n        `bash-4.1.2-29.el6.x86_64.rpm`, use the string `'4.1.2-29.el6'`.\n\n        On supported providers, version ranges can also be ensured. For example,\n        inequalities: `<2.0.0`, or intersections: `>1.0.0 <2.0.0`.\n      EOT\n\n      attr_accessor :latest\n\n      newvalue(:present, :event => :package_installed) do\n        provider.install\n      end\n\n      newvalue(:absent, :event => :package_removed) do\n        provider.uninstall\n      end\n\n      newvalue(:purged, :event => :package_purged, :required_features => :purgeable) do\n        provider.purge\n      end\n\n      newvalue(:disabled, :required_features => :disableable) do\n        provider.disable\n      end\n\n      # Alias the 'present' value.\n      aliasvalue(:installed, :present)\n\n      newvalue(:latest, :required_features => :upgradeable) do\n        # Because yum always exits with a 0 exit code, there's a retrieve\n        # in the \"install\" method.  So, check the current state now,\n        # to compare against later.\n        current = retrieve\n        begin\n          provider.update\n        rescue => detail\n          self.fail Puppet::Error, _(\"Could not update: %{detail}\") % { detail: detail }, detail\n        end\n\n        if current == :absent\n          :package_installed\n        else\n          :package_changed\n        end\n      end\n\n      newvalue(/./, :required_features => :versionable) do\n        begin\n          provider.install\n        rescue => detail\n          self.fail Puppet::Error, _(\"Could not update: %{detail}\") % { detail: detail }, detail\n        end\n\n        if retrieve == :absent\n          :package_installed\n        else\n          :package_changed\n        end\n      end\n\n      defaultto :installed\n\n      # Override the parent method, because we've got all kinds of\n      # funky definitions of 'in sync'.\n      def insync?(is)\n        @lateststamp ||= (Time.now.to_i - 1000)\n        # Iterate across all of the should values, and see how they\n        # turn out.\n\n        @should.each { |should|\n          case should\n          when :present\n            return true unless [:absent, :purged, :disabled].include?(is)\n          when :latest\n            # Short-circuit packages that are not present\n            return false if is == :absent || is == :purged\n\n            # Don't run 'latest' more than about every 5 minutes\n            if @latest and ((Time.now.to_i - @lateststamp) / 60) < 5\n              # self.debug \"Skipping latest check\"\n            else\n              begin\n                @latest = provider.latest\n                @lateststamp = Time.now.to_i\n              rescue => detail\n                error = Puppet::Error.new(_(\"Could not get latest version: %{detail}\") % { detail: detail })\n                error.set_backtrace(detail.backtrace)\n                raise error\n              end\n            end\n\n            case\n            when is.is_a?(Array) && is.include?(@latest)\n              return true\n            when is == @latest\n              return true\n            when is == :present\n              # This will only happen on packaging systems\n              # that can't query versions.\n              return true\n            else\n              debug \"#{@resource.name} #{is.inspect} is installed, latest is #{@latest.inspect}\"\n            end\n\n          when :absent\n            return true if is == :absent || is == :purged\n          when :purged\n            return true if is == :purged\n          # this handles version number matches and\n          # supports providers that can have multiple versions installed\n          when *Array(is)\n            return true\n          else\n            # We have version numbers, and no match. If the provider has\n            # additional logic, run it here.\n            return provider.insync?(is) if provider.respond_to?(:insync?)\n          end\n        }\n\n        false\n      end\n\n      # This retrieves the current state. LAK: I think this method is unused.\n      def retrieve\n        provider.properties[:ensure]\n      end\n\n      # Provide a bit more information when logging upgrades.\n      def should_to_s(newvalue = @should)\n        if @latest\n          super(@latest)\n        else\n          super(newvalue)\n        end\n      end\n\n      def change_to_s(currentvalue, newvalue)\n        # Handle transitioning from any previous state to 'purged'\n        return 'purged' if newvalue == :purged\n\n        # Check for transitions from nil/purged/absent to 'created' (any state that is not absent and not purged)\n        return 'created' if (currentvalue.nil? || currentvalue == :absent || currentvalue == :purged) && (newvalue != :absent && newvalue != :purged)\n\n        # The base should handle the normal property transitions\n        super(currentvalue, newvalue)\n      end\n    end\n\n    newparam(:name) do\n      desc \"The package name.  This is the name that the packaging\n      system uses internally, which is sometimes (especially on Solaris)\n      a name that is basically useless to humans.  If a package goes by\n      several names, you can use a single title and then set the name\n      conditionally:\n\n          # In the 'openssl' class\n          $ssl = $os['name'] ? {\n            solaris => SMCossl,\n            default => openssl\n          }\n\n          package { 'openssl':\n            ensure => installed,\n            name   => $ssl,\n          }\n\n          ...\n\n          $ssh = $os['name'] ? {\n            solaris => SMCossh,\n            default => openssh\n          }\n\n          package { 'openssh':\n            ensure  => installed,\n            name    => $ssh,\n            require => Package['openssl'],\n          }\n\n      \"\n      isnamevar\n\n      validate do |value|\n        unless value.is_a?(String)\n          raise ArgumentError, _(\"Name must be a String not %{klass}\") % { klass: value.class }\n        end\n      end\n    end\n\n    # We call providify here so that we can set provider as a namevar.\n    # Normally this method is called after newtype finishes constructing this\n    # Type class.\n    providify\n    paramclass(:provider).isnamevar\n\n    def self.parameters_to_include\n      [:provider]\n    end\n\n    # Specify a targeted package management command.\n    newparam(:command, :required_features => :targetable) do\n      desc <<-EOT\n        The targeted command to use when managing a package:\n\n          package { 'mysql':\n            provider => gem,\n          }\n\n          package { 'mysql-opt':\n            name     => 'mysql',\n            provider => gem,\n            command  => '/opt/ruby/bin/gem',\n          }\n\n        Each provider defines a package management command and uses the first\n        instance of the command found in the PATH.\n\n        Providers supporting the targetable feature allow you to specify the\n        absolute path of the package management command. Specifying the absolute\n        path is useful when multiple instances of the command are installed, or\n        the command is not in the PATH.\n      EOT\n\n      isnamevar\n      defaultto :default\n    end\n\n    # We have more than one namevar, so we need title_patterns.\n    # However, we cheat and set the patterns to map to name only\n    # and completely ignore provider (and command, for targetable providers).\n    # So far, the logic that determines uniqueness appears to just\n    # \"Do The Right Thing™\" when provider (and command) are explicitly set.\n    #\n    # The following resources will be seen as unique by puppet:\n    #\n    #     # Uniqueness Key: ['mysql', nil]\n    #     package {'mysql': }\n    #\n    #     # Uniqueness Key: ['mysql', 'gem', nil]\n    #     package {'gem-mysql':\n    #       name     => 'mysql,\n    #       provider => gem,\n    #     }\n    #\n    #     # Uniqueness Key: ['mysql', 'gem', '/opt/ruby/bin/gem']\n    #     package {'gem-mysql-opt':\n    #       name     => 'mysql,\n    #       provider => gem\n    #       command  => '/opt/ruby/bin/gem',\n    #     }\n    #\n    # This does not handle the case where providers like 'yum' and 'rpm' should\n    # clash. Also, declarations that implicitly use the default provider will\n    # clash with those that explicitly use the default.\n    def self.title_patterns\n      # This is the default title pattern for all types, except hard-wired to\n      # set only name.\n      [[/(.*)/m, [[:name]]]]\n    end\n\n    newproperty(:package_settings, :required_features => :package_settings) do\n      desc \"Settings that can change the contents or configuration of a package.\n\n        The formatting and effects of package_settings are provider-specific; any\n        provider that implements them must explain how to use them in its\n        documentation. (Our general expectation is that if a package is\n        installed but its settings are out of sync, the provider should\n        re-install that package with the desired settings.)\n\n        An example of how package_settings could be used is FreeBSD's port build\n        options --- a future version of the provider could accept a hash of options,\n        and would reinstall the port if the installed version lacked the correct\n        settings.\n\n            package { 'www/apache22':\n              package_settings => { 'SUEXEC' => false }\n            }\n\n        Again, check the documentation of your platform's package provider to see\n        the actual usage.\"\n\n      validate do |value|\n        if provider.respond_to?(:package_settings_validate)\n          provider.package_settings_validate(value)\n        else\n          super(value)\n        end\n      end\n\n      munge do |value|\n        if provider.respond_to?(:package_settings_munge)\n          provider.package_settings_munge(value)\n        else\n          super(value)\n        end\n      end\n\n      def insync?(is)\n        provider.package_settings_insync?(should, is)\n      end\n\n      def should_to_s(newvalue)\n        if provider.respond_to?(:package_settings_should_to_s)\n          provider.package_settings_should_to_s(should, newvalue)\n        else\n          super(newvalue)\n        end\n      end\n\n      def is_to_s(currentvalue) # rubocop:disable Naming/PredicateName\n        if provider.respond_to?(:package_settings_is_to_s)\n          provider.package_settings_is_to_s(should, currentvalue)\n        else\n          super(currentvalue)\n        end\n      end\n\n      def change_to_s(currentvalue, newvalue)\n        if provider.respond_to?(:package_settings_change_to_s)\n          provider.package_settings_change_to_s(currentvalue, newvalue)\n        else\n          super(currentvalue, newvalue)\n        end\n      end\n    end\n\n    newproperty(:flavor, :required_features => :supports_flavors) do\n      desc \"OpenBSD and DNF modules support 'flavors', which are\n        further specifications for which type of package you want.\"\n      validate do |value|\n        if [:disabled, \"disabled\"].include?(@resource[:ensure]) && value\n          raise ArgumentError, _('Cannot have both `ensure => disabled` and `flavor`')\n        end\n      end\n    end\n\n    newparam(:source) do\n      desc \"Where to find the package file. This is mostly used by providers that don't\n        automatically download packages from a central repository. (For example:\n        the `yum` provider ignores this attribute, `apt` provider uses it if present\n        and the `rpm` and `dpkg` providers require it.)\n\n        Different providers accept different values for `source`. Most providers\n        accept paths to local files stored on the target system. Some providers\n        may also accept URLs or network drive paths. Puppet will not\n        automatically retrieve source files for you, and usually just passes the\n        value of `source` to the package installation command.\n\n        You can use a `file` resource if you need to manually copy package files\n        to the target system.\"\n\n      validate do |value|\n        provider.validate_source(value)\n      end\n    end\n\n    newparam(:instance) do\n      desc \"A read-only parameter set by the package.\"\n    end\n\n    newparam(:status) do\n      desc \"A read-only parameter set by the package.\"\n    end\n\n    newparam(:adminfile) do\n      desc \"A file containing package defaults for installing packages.\n\n        This attribute is only used on Solaris. Its value should be a path to a\n        local file stored on the target system. Solaris's package tools expect\n        either an absolute file path or a relative path to a file in\n        `/var/sadm/install/admin`.\n\n        The value of `adminfile` will be passed directly to the `pkgadd` or\n        `pkgrm` command with the `-a <ADMINFILE>` option.\"\n    end\n\n    newparam(:responsefile) do\n      desc \"A file containing any necessary answers to questions asked by\n        the package.  This is currently used on Solaris and Debian.  The\n        value will be validated according to system rules, but it should\n        generally be a fully qualified path.\"\n    end\n\n    newparam(:configfiles) do\n      desc \"Whether to keep or replace modified config files when installing or\n        upgrading a package. This only affects the `apt` and `dpkg` providers.\"\n\n      defaultto :keep\n\n      newvalues(:keep, :replace)\n    end\n\n    newparam(:category) do\n      desc \"A read-only parameter set by the package.\"\n    end\n    newparam(:platform) do\n      desc \"A read-only parameter set by the package.\"\n    end\n    newparam(:root) do\n      desc \"A read-only parameter set by the package.\"\n    end\n    newparam(:vendor) do\n      desc \"A read-only parameter set by the package.\"\n    end\n    newparam(:description) do\n      desc \"A read-only parameter set by the package.\"\n    end\n\n    newparam(:allowcdrom) do\n      desc \"Tells apt to allow cdrom sources in the sources.list file.\n        Normally apt will bail if you try this.\"\n\n      newvalues(:true, :false)\n    end\n\n    newparam(:enable_only, :boolean => false, :parent => Puppet::Parameter::Boolean) do\n      desc <<-EOT\n        Tells `dnf module` to only enable a specific module, instead\n        of installing its default profile.\n\n        Modules with no default profile will be enabled automatically\n        without the use of this parameter.\n\n        Conflicts with the `flavor` property, which selects a profile\n        to install.\n      EOT\n      defaultto false\n\n      validate do |value|\n        if [true, :true, \"true\"].include?(value) && @resource[:flavor]\n          raise ArgumentError, _('Cannot have both `enable_only => true` and `flavor`')\n        end\n        if [:disabled, \"disabled\"].include?(@resource[:ensure])\n          raise ArgumentError, _('Cannot have both `ensure => disabled` and `enable_only => true`')\n        end\n      end\n    end\n\n    newparam(:install_only, :boolean => false, :parent => Puppet::Parameter::Boolean, :required_features => :install_only) do\n      desc <<-EOT\n        It should be set for packages that should only ever be installed,\n        never updated. Kernels in particular fall into this category.\n      EOT\n      defaultto false\n    end\n\n    newparam(:install_options, :parent => Puppet::Parameter::PackageOptions, :required_features => :install_options) do\n      desc <<-EOT\n        An array of additional options to pass when installing a package. These\n        options are package-specific, and should be documented by the software\n        vendor.  One commonly implemented option is `INSTALLDIR`:\n\n            package { 'mysql':\n              ensure          => installed,\n              source          => 'N:/packages/mysql-5.5.16-winx64.msi',\n              install_options => [ '/S', { 'INSTALLDIR' => 'C:\\\\mysql-5.5' } ],\n            }\n\n        Each option in the array can either be a string or a hash, where each\n        key and value pair are interpreted in a provider specific way.  Each\n        option will automatically be quoted when passed to the install command.\n\n        With Windows packages, note that file paths in an install option must\n        use backslashes. (Since install options are passed directly to the\n        installation command, forward slashes won't be automatically converted\n        like they are in `file` resources.) Note also that backslashes in\n        double-quoted strings _must_ be escaped and backslashes in single-quoted\n        strings _can_ be escaped.\n      EOT\n    end\n\n    newparam(:uninstall_options, :parent => Puppet::Parameter::PackageOptions, :required_features => :uninstall_options) do\n      desc <<-EOT\n        An array of additional options to pass when uninstalling a package. These\n        options are package-specific, and should be documented by the software\n        vendor.  For example:\n\n            package { 'VMware Tools':\n              ensure            => absent,\n              uninstall_options => [ { 'REMOVE' => 'Sync,VSS' } ],\n            }\n\n        Each option in the array can either be a string or a hash, where each\n        key and value pair are interpreted in a provider specific way.  Each\n        option will automatically be quoted when passed to the uninstall\n        command.\n\n        On Windows, this is the **only** place in Puppet where backslash\n        separators should be used.  Note that backslashes in double-quoted\n        strings _must_ be double-escaped and backslashes in single-quoted\n        strings _may_ be double-escaped.\n      EOT\n    end\n\n    newparam(:allow_virtual, :boolean => true, :parent => Puppet::Parameter::Boolean, :required_features => :virtual_packages) do\n      desc 'Specifies if virtual package names are allowed for install and uninstall.'\n\n      defaultto do\n        provider_class = provider.class\n        if provider_class.respond_to?(:defaultto_allow_virtual)\n          provider_class.defaultto_allow_virtual\n        else\n          true\n        end\n      end\n    end\n\n    autorequire(:file) do\n      autos = []\n      [:responsefile, :adminfile].each { |param|\n        val = self[param]\n        if val\n          autos << val\n        end\n      }\n\n      source = self[:source]\n      if source && absolute_path?(source)\n        autos << source\n      end\n      autos\n    end\n\n    # This only exists for testing.\n    def clear\n      obj = @parameters[:ensure]\n      if obj\n        obj.latest = nil\n      end\n    end\n\n    # The 'query' method returns a hash of info if the package\n    # exists and returns nil if it does not.\n    def exists?\n      @provider.get(:ensure) != :absent\n    end\n\n    def present?(current_values)\n      super && current_values[:ensure] != :purged\n    end\n\n    # This parameter exists to ensure backwards compatibility is preserved.\n    # See https://github.com/puppetlabs/puppet/pull/2614 for discussion.\n    # If/when a metaparameter for controlling how arbitrary resources respond\n    # to refreshing is created, that will supersede this, and this will be\n    # deprecated.\n    newparam(:reinstall_on_refresh) do\n      desc \"Whether this resource should respond to refresh events (via `subscribe`,\n        `notify`, or the `~>` arrow) by reinstalling the package. Only works for\n        providers that support the `reinstallable` feature.\n\n        This is useful for source-based distributions, where you may want to\n        recompile a package if the build options change.\n\n        If you use this, be careful of notifying classes when you want to restart\n        services. If the class also contains a refreshable package, doing so could\n        cause unnecessary re-installs.\"\n      newvalues(:true, :false)\n\n      defaultto :false\n    end\n\n    # When a refresh event is triggered, calls reinstall on providers\n    # that support the reinstall_on_refresh parameter.\n    def refresh\n      if provider.reinstallable? &&\n         @parameters[:reinstall_on_refresh].value == :true &&\n         @parameters[:ensure].value != :purged &&\n         @parameters[:ensure].value != :absent\n\n        provider.reinstall\n      end\n    end\n\n    newproperty(:mark, :required_features => :holdable) do\n      mark_doc = 'Valid values are: hold/none'\n      desc <<-EOT\n        Set to hold to tell Debian apt/Solaris pkg to hold the package version\n\n        #{mark_doc}\n        Default is \"none\". Mark can be specified with or without `ensure`,\n        if `ensure` is missing will default to \"present\".\n\n        Mark cannot be specified together with \"purged\", or \"absent\"\n        values for `ensure`.\n      EOT\n      newvalues(:hold, :none)\n      munge do |value|\n        case value\n        when \"hold\", :hold\n          :hold\n        when \"none\", :none\n          :none\n        else\n          raise ArgumentError, _('Invalid hold value %{value}. %{doc}') % { value: value.inspect, doc: mark_doc }\n        end\n      end\n\n      def insync?(is)\n        @should[0] == is\n      end\n\n      def should\n        @should[0] if @should && @should.is_a?(Array) && @should.size == 1\n      end\n\n      def retrieve\n        provider.properties[:mark]\n      end\n\n      def sync\n        if @should[0] == :hold\n          provider.hold\n        else\n          provider.unhold\n        end\n      end\n    end\n\n    validate do\n      if @parameters[:mark] && [:absent, :purged].include?(@parameters[:ensure].should)\n        raise ArgumentError, _('You cannot use \"mark\" property while \"ensure\" is one of [\"absent\", \"purged\"]')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/resources.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/parameter/boolean'\n\nPuppet::Type.newtype(:resources) do\n  @doc = \"This is a metatype that can manage other resource types.  Any\n    metaparams specified here will be passed on to any generated resources,\n    so you can purge unmanaged resources but set `noop` to true so the\n    purging is only logged and does not actually happen.\"\n\n  apply_to_all\n\n  newparam(:name) do\n    desc \"The name of the type to be managed.\"\n\n    validate do |name|\n      raise ArgumentError, _(\"Could not find resource type '%{name}'\") % { name: name } unless Puppet::Type.type(name)\n    end\n\n    munge(&:to_s)\n  end\n\n  newparam(:purge, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n    desc \"Whether to purge unmanaged resources.  When set to `true`, this will\n      delete any resource that is not specified in your configuration and is not\n      autorequired by any managed resources. **Note:** The `ssh_authorized_key`\n      resource type can't be purged this way; instead, see the `purge_ssh_keys`\n      attribute of the `user` type.\"\n\n    defaultto :false\n\n    validate do |value|\n      if munge(value)\n        unless @resource.resource_type.respond_to?(:instances)\n          raise ArgumentError, _(\"Purging resources of type %{res_type} is not supported, since they cannot be queried from the system\") % { res_type: @resource[:name] }\n        end\n        raise ArgumentError, _(\"Purging is only supported on types that accept 'ensure'\") unless @resource.resource_type.validproperty?(:ensure)\n      end\n    end\n  end\n\n  newparam(:unless_system_user) do\n    desc \"This keeps system users from being purged.  By default, it\n      does not purge users whose UIDs are less than the minimum UID for the system (typically 500 or 1000), but you can specify\n      a different UID as the inclusive limit.\"\n\n    newvalues(:true, :false, /^\\d+$/)\n\n    munge do |value|\n      case value\n      when /^\\d+/\n        Integer(value)\n      when :true, true\n        @resource.class.system_users_max_uid\n      when :false, false\n        false\n      when Integer; value\n      else\n        raise ArgumentError, _(\"Invalid value %{value}\") % { value: value.inspect }\n      end\n    end\n\n    defaultto {\n      if @resource[:name] == \"user\"\n        @resource.class.system_users_max_uid\n      else\n        nil\n      end\n    }\n  end\n\n  newparam(:unless_uid) do\n    desc 'This keeps specific uids or ranges of uids from being purged when purge is true.\n      Accepts integers, integer strings, and arrays of integers or integer strings.\n      To specify a range of uids, consider using the range() function from stdlib.'\n\n    munge do |value|\n      value = [value] unless value.is_a? Array\n      value.flatten.collect do |v|\n        case v\n        when Integer\n          v\n        when String\n          Integer(v)\n        else\n          raise ArgumentError, _(\"Invalid value %{value}.\") % { value: v.inspect }\n        end\n      end\n    end\n  end\n\n  WINDOWS_SYSTEM_SID_REGEXES =\n    # Administrator, Guest, Domain Admins, Schema Admins, Enterprise Admins.\n    # https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems\n    [/S-1-5-21.+-500/, /S-1-5-21.+-501/, /S-1-5-21.+-512/, /S-1-5-21.+-518/,\n     /S-1-5-21.+-519/]\n\n  def check(resource)\n    @checkmethod ||= \"#{self[:name]}_check\"\n    @hascheck ||= respond_to?(@checkmethod)\n    if @hascheck\n      send(@checkmethod, resource)\n    else\n      true\n    end\n  end\n\n  def able_to_ensure_absent?(resource)\n    resource[:ensure] = :absent\n  rescue ArgumentError, Puppet::Error\n    err _(\"The 'ensure' attribute on %{name} resources does not accept 'absent' as a value\") % { name: self[:name] }\n    false\n  end\n\n  # Generate any new resources we need to manage.  This is pretty hackish\n  # right now, because it only supports purging.\n  def generate\n    return [] unless purge?\n\n    resource_type.instances\n                 .reject { |r| catalog.resource_refs.include? r.ref }\n                 .select { |r| check(r) }\n                 .select { |r| r.class.validproperty?(:ensure) }\n                 .select { |r| able_to_ensure_absent?(r) }\n                 .each { |resource|\n      resource.copy_metaparams(@parameters)\n      resource.purging\n    }\n  end\n\n  def resource_type\n    unless defined?(@resource_type)\n      type = Puppet::Type.type(self[:name])\n      unless type\n        raise Puppet::DevError, _(\"Could not find resource type\")\n      end\n\n      @resource_type = type\n    end\n    @resource_type\n  end\n\n  # Make sure we don't purge users with specific uids\n  def user_check(resource)\n    return true unless self[:name] == \"user\"\n    return true unless self[:unless_system_user]\n\n    resource[:audit] = :uid\n    current_values = resource.retrieve_resource\n    current_uid = current_values[resource.property(:uid)]\n    unless_uids = self[:unless_uid]\n\n    return false if system_users.include?(resource[:name])\n    return false if unless_uids && unless_uids.include?(current_uid)\n\n    if current_uid.is_a?(String)\n      # Windows user; is a system user if any regex matches.\n      WINDOWS_SYSTEM_SID_REGEXES.none? { |regex| current_uid =~ regex }\n    else\n      current_uid > self[:unless_system_user]\n    end\n  end\n\n  def system_users\n    %w[root nobody bin noaccess daemon sys]\n  end\n\n  def self.system_users_max_uid\n    return @system_users_max_uid if @system_users_max_uid\n\n    # First try to read the minimum user id from login.defs\n    if Puppet::FileSystem.exist?('/etc/login.defs')\n      @system_users_max_uid = Puppet::FileSystem.each_line '/etc/login.defs' do |line|\n        break Regexp.last_match(1).to_i - 1 if line =~ /^\\s*UID_MIN\\s+(\\d+)(\\s*#.*)?$/\n      end\n    end\n\n    # Otherwise, use a sensible default based on the OS family\n    @system_users_max_uid ||=\n      case Puppet.runtime[:facter].value('os.family')\n      when 'OpenBSD', 'FreeBSD' then 999\n      else                           499\n      end\n\n    @system_users_max_uid\n  end\n\n  def self.reset_system_users_max_uid!\n    @system_users_max_uid = nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/schedule.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  Type.newtype(:schedule) do\n    @doc = <<-'EOT'\n      Define schedules for Puppet. Resources can be limited to a schedule by using the\n      [`schedule`](https://puppet.com/docs/puppet/latest/metaparameter.html#schedule)\n      metaparameter.\n\n      Currently, **schedules can only be used to stop a resource from being\n      applied;** they cannot cause a resource to be applied when it otherwise\n      wouldn't be, and they cannot accurately specify a time when a resource\n      should run.\n\n      Every time Puppet applies its configuration, it will apply the\n      set of resources whose schedule does not eliminate them from\n      running right then, but there is currently no system in place to\n      guarantee that a given resource runs at a given time.  If you\n      specify a very  restrictive schedule and Puppet happens to run at a\n      time within that schedule, then the resources will get applied;\n      otherwise, that work may never get done.\n\n      Thus, it is advisable to use wider scheduling (for example, over a couple\n      of hours) combined with periods and repetitions.  For instance, if you\n      wanted to restrict certain resources to only running once, between\n      the hours of two and 4 AM, then you would use this schedule:\n\n          schedule { 'maint':\n            range  => '2 - 4',\n            period => daily,\n            repeat => 1,\n          }\n\n      With this schedule, the first time that Puppet runs between 2 and 4 AM,\n      all resources with this schedule will get applied, but they won't\n      get applied again between 2 and 4 because they will have already\n      run once that day, and they won't get applied outside that schedule\n      because they will be outside the scheduled range.\n\n      Puppet automatically creates a schedule for each of the valid periods\n      with the same name as that period (such as hourly and daily).\n      Additionally, a schedule named `puppet` is created and used as the\n      default, with the following attributes:\n\n          schedule { 'puppet':\n            period => hourly,\n            repeat => 2,\n          }\n\n      This will cause resources to be applied every 30 minutes by default.\n\n      The `statettl` setting on the agent affects the ability of a schedule to\n      determine if a resource has already been checked. If the `statettl` is\n      set lower than the span of the associated schedule resource, then a\n      resource could be checked & applied multiple times in the schedule as\n      the information about when the resource was last checked will have\n      expired from the cache.\n    EOT\n\n    apply_to_all\n\n    newparam(:name) do\n      desc <<-EOT\n        The name of the schedule.  This name is used when assigning the schedule\n        to a resource with the `schedule` metaparameter:\n\n            schedule { 'everyday':\n              period => daily,\n              range  => '2 - 4',\n            }\n\n            exec { '/usr/bin/apt-get update':\n              schedule => 'everyday',\n            }\n\n      EOT\n      isnamevar\n    end\n\n    newparam(:range) do\n      desc <<-EOT\n        The earliest and latest that a resource can be applied.  This is\n        always a hyphen-separated range within a 24 hour period, and hours\n        must be specified in numbers between 0 and 23, inclusive.  Minutes and\n        seconds can optionally be provided, using the normal colon as a\n        separator. For instance:\n\n            schedule { 'maintenance':\n              range => '1:30 - 4:30',\n            }\n\n        This is mostly useful for restricting certain resources to being\n        applied in maintenance windows or during off-peak hours. Multiple\n        ranges can be applied in array context. As a convenience when specifying\n        ranges, you can cross midnight (for example, `range => \"22:00 - 04:00\"`).\n      EOT\n\n      # This is lame; properties all use arrays as values, but parameters don't.\n      # That's going to hurt eventually.\n      validate do |values|\n        values = [values] unless values.is_a?(Array)\n        values.each { |value|\n          unless value.is_a?(String) and\n                 value =~ /\\d+(:\\d+){0,2}\\s*-\\s*\\d+(:\\d+){0,2}/\n            self.fail _(\"Invalid range value '%{value}'\") % { value: value }\n          end\n        }\n      end\n\n      munge do |values|\n        values = [values] unless values.is_a?(Array)\n        ret = []\n\n        values.each { |value|\n          range = []\n          # Split each range value into a hour, minute, second triad\n          value.split(/\\s*-\\s*/).each { |val|\n            # Add the values as an array.\n            range << val.split(\":\").collect(&:to_i)\n          }\n\n          self.fail _(\"Invalid range %{value}\") % { value: value } if range.length != 2\n\n          # Fill out 0s for unspecified minutes and seconds\n          range.each do |time_array|\n            (3 - time_array.length).times { |_| time_array << 0 }\n          end\n\n          # Make sure the hours are valid\n          [range[0][0], range[1][0]].each do |n|\n            raise ArgumentError, _(\"Invalid hour '%{n}'\") % { n: n } if n < 0 or n > 23\n          end\n\n          [range[0][1], range[1][1]].each do |n|\n            raise ArgumentError, _(\"Invalid minute '%{n}'\") % { n: n } if n and (n < 0 or n > 59)\n          end\n          ret << range\n        }\n\n        # Now our array of arrays\n        ret\n      end\n\n      def weekday_match?(day)\n        if @resource[:weekday]\n          @resource[:weekday].has_key?(day)\n        else\n          true\n        end\n      end\n\n      def match?(previous, now)\n        # The lowest-level array is of the hour, minute, second triad\n        # then it's an array of two of those, to present the limits\n        # then it's an array of those ranges\n        @value = [@value] unless @value[0][0].is_a?(Array)\n        @value.any? do |range|\n          limit_start = Time.local(now.year, now.month, now.day, *range[0])\n          limit_end = Time.local(now.year, now.month, now.day, *range[1])\n\n          if limit_start < limit_end\n            # The whole range is in one day, simple case\n            now.between?(limit_start, limit_end) && weekday_match?(now.wday)\n          else\n            # The range spans days. We have to test against a range starting\n            # today, and a range that started yesterday.\n            today = Date.new(now.year, now.month, now.day)\n            tomorrow = today.next_day\n            yesterday = today.prev_day\n\n            # First check a range starting today\n            if now.between?(limit_start, Time.local(tomorrow.year, tomorrow.month, tomorrow.day, *range[1]))\n              weekday_match?(today.wday)\n            else\n              # Then check a range starting yesterday\n              now.between?(Time.local(yesterday.year, yesterday.month, yesterday.day, *range[0]),\n                           limit_end) &&\n                weekday_match?(yesterday.wday)\n            end\n          end\n        end\n      end\n    end\n\n    newparam(:periodmatch) do\n      desc \"Whether periods should be matched by a numeric value (for instance,\n        whether two times are in the same hour) or by their chronological\n        distance apart (whether two times are 60 minutes apart).\"\n\n      newvalues(:number, :distance)\n\n      defaultto :distance\n    end\n\n    newparam(:period) do\n      desc <<-EOT\n        The period of repetition for resources on this schedule. The default is\n        for resources to get applied every time Puppet runs.\n\n        Note that the period defines how often a given resource will get\n        applied but not when; if you would like to restrict the hours\n        that a given resource can be applied (for instance, only at night\n        during a maintenance window), then use the `range` attribute.\n\n        If the provided periods are not sufficient, you can provide a\n        value to the *repeat* attribute, which will cause Puppet to\n        schedule the affected resources evenly in the period the\n        specified number of times.  Take this schedule:\n\n            schedule { 'veryoften':\n              period => hourly,\n              repeat => 6,\n            }\n\n        This can cause Puppet to apply that resource up to every 10 minutes.\n\n        At the moment, Puppet cannot guarantee that level of repetition; that\n        is, the resource can applied _up to_ every 10 minutes, but internal\n        factors might prevent it from actually running that often (for instance,\n        if a Puppet run is still in progress when the next run is scheduled to\n        start, that next run will be suppressed).\n\n        See the `periodmatch` attribute for tuning whether to match\n        times by their distance apart or by their specific value.\n\n        > **Tip**: You can use `period => never,` to prevent a resource from being applied\n        in the given `range`. This is useful if you need to create a blackout window to\n        perform sensitive operations without interruption.\n      EOT\n\n      newvalues(:hourly, :daily, :weekly, :monthly, :never)\n\n      ScheduleScales = {\n        :hourly => 3600,\n        :daily => 86_400,\n        :weekly => 604_800,\n        :monthly => 2_592_000\n      }\n      ScheduleMethods = {\n        :hourly => :hour,\n        :daily => :day,\n        :monthly => :month,\n        :weekly => proc do |prev, now|\n          # Run the resource if the previous day was after this weekday (e.g., prev is wed, current is tue)\n          # or if it's been more than a week since we ran\n          prev.wday > now.wday or (now - prev) > (24 * 3600 * 7)\n        end\n      }\n\n      def match?(previous, now)\n        return false if value == :never\n\n        value = self.value\n        case @resource[:periodmatch]\n        when :number\n          method = ScheduleMethods[value]\n          if method.is_a?(Proc)\n            method.call(previous, now)\n          else\n            # We negate it, because if they're equal we don't run\n            now.send(method) != previous.send(method)\n          end\n        when :distance\n          scale = ScheduleScales[value]\n\n          # If the number of seconds between the two times is greater\n          # than the unit of time, we match.  We divide the scale\n          # by the repeat, so that we'll repeat that often within\n          # the scale.\n          (now.to_i - previous.to_i) >= (scale / @resource[:repeat])\n        end\n      end\n    end\n\n    newparam(:repeat) do\n      desc \"How often a given resource may be applied in this schedule's `period`.\n        Must be an integer.\"\n\n      defaultto 1\n\n      validate do |value|\n        unless value.is_a?(Integer) or value =~ /^\\d+$/\n          raise Puppet::Error,\n                _(\"Repeat must be a number\")\n        end\n\n        # This implicitly assumes that 'periodmatch' is distance -- that\n        # is, if there's no value, we assume it's a valid value.\n        return unless @resource[:periodmatch]\n\n        if value != 1 and @resource[:periodmatch] != :distance\n          raise Puppet::Error,\n                _(\"Repeat must be 1 unless periodmatch is 'distance', not '%{period}'\") % { period: @resource[:periodmatch] }\n        end\n      end\n\n      munge do |value|\n        value = Integer(value) unless value.is_a?(Integer)\n\n        value\n      end\n\n      def match?(previous, now)\n        true\n      end\n    end\n\n    newparam(:weekday) do\n      desc <<-EOT\n        The days of the week in which the schedule should be valid.\n        You may specify the full day name 'Tuesday', the three character\n        abbreviation 'Tue', or a number (as a string or as an integer) corresponding to the day of the\n        week where 0 is Sunday, 1 is Monday, and so on. Multiple days can be specified\n        as an array. If not specified, the day of the week will not be\n        considered in the schedule.\n\n        If you are also using a range match that spans across midnight\n        then this parameter will match the day that it was at the start\n        of the range, not necessarily the day that it is when it matches.\n        For example, consider this schedule:\n\n            schedule { 'maintenance_window':\n              range   => '22:00 - 04:00',\n              weekday => 'Saturday',\n            }\n\n        This will match at 11 PM on Saturday and 2 AM on Sunday, but not\n        at 2 AM on Saturday.\n      EOT\n\n      validate do |values|\n        values = [values] unless values.is_a?(Array)\n        values.each { |value|\n          if weekday_integer?(value) || weekday_string?(value)\n            value\n          else\n            raise ArgumentError, _(\"%{value} is not a valid day of the week\") % { value: value }\n          end\n        }\n      end\n\n      def weekday_integer?(value)\n        value.is_a?(Integer) && (0..6).cover?(value)\n      end\n\n      def weekday_string?(value)\n        value.is_a?(String) && (value =~ /^[0-6]$/ || value =~ /^(Mon|Tues?|Wed(?:nes)?|Thu(?:rs)?|Fri|Sat(?:ur)?|Sun)(day)?$/i)\n      end\n\n      weekdays = {\n        'sun' => 0,\n        'mon' => 1,\n        'tue' => 2,\n        'wed' => 3,\n        'thu' => 4,\n        'fri' => 5,\n        'sat' => 6,\n      }\n\n      munge do |values|\n        values = [values] unless values.is_a?(Array)\n        ret = {}\n\n        values.each do |value|\n          case value\n          when /^[0-6]$/\n            index = value.to_i\n          when 0..6\n            index = value\n          else\n            index = weekdays[value[0, 3].downcase]\n          end\n          ret[index] = true\n        end\n        ret\n      end\n\n      def match?(previous, now)\n        # Special case weekday matching with ranges to a no-op here.\n        # If the ranges span days then we can't simply match the current\n        # weekday, as we want to match the weekday as it was when the range\n        # started.  As a result, all of that logic is in range, not here.\n        return true if @resource[:range]\n\n        return true if value.has_key?(now.wday)\n\n        false\n      end\n    end\n\n    def self.instances\n      []\n    end\n\n    def self.mkdefaultschedules\n      result = []\n      unless Puppet[:default_schedules]\n        Puppet.debug \"Not creating default schedules: default_schedules is false\"\n        return result\n      end\n\n      Puppet.debug \"Creating default schedules\"\n\n      result << new(\n        :name => \"puppet\",\n        :period => :hourly,\n\n        :repeat => \"2\"\n      )\n\n      # And then one for every period\n      @parameters.find { |p| p.name == :period }.value_collection.values.each { |value|\n        result << new(\n          :name => value.to_s,\n          :period => value\n        )\n      }\n\n      result\n    end\n\n    def match?(previous = nil, now = nil)\n      # If we've got a value, then convert it to a Time instance\n      previous &&= Time.at(previous)\n\n      now ||= Time.now\n\n      # Pull them in order\n      self.class.allattrs.each { |param|\n        if @parameters.include?(param) and\n           @parameters[param].respond_to?(:match?)\n          return false unless @parameters[param].match?(previous, now)\n        end\n      }\n\n      # If we haven't returned false, then return true; in other words,\n      # any provided schedules need to all match\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/service.rb",
    "content": "# frozen_string_literal: true\n\n# This is our main way of managing processes right now.\n#\n# a service is distinct from a process in that services\n# can only be managed through the interface of an init script\n# which is why they have a search path for initscripts and such\n\nmodule Puppet\n  Type.newtype(:service) do\n    @doc = \"Manage running services.  Service support unfortunately varies\n      widely by platform --- some platforms have very little if any concept of a\n      running service, and some have a very codified and powerful concept.\n      Puppet's service support is usually capable of doing the right thing, but\n      the more information you can provide, the better behaviour you will get.\n\n      Puppet 2.7 and newer expect init scripts to have a working status command.\n      If this isn't the case for any of your services' init scripts, you will\n      need to set `hasstatus` to false and possibly specify a custom status\n      command in the `status` attribute. As a last resort, Puppet will attempt to\n      search the process table by calling whatever command is listed in the `ps`\n      fact. The default search pattern is the name of the service, but you can\n      specify it with the `pattern` attribute.\n\n      **Refresh:** `service` resources can respond to refresh events (via\n      `notify`, `subscribe`, or the `~>` arrow). If a `service` receives an\n      event from another resource, Puppet will restart the service it manages.\n      The actual command used to restart the service depends on the platform and\n      can be configured:\n\n      * If you set `hasrestart` to true, Puppet will use the init script's restart command.\n      * You can provide an explicit command for restarting with the `restart` attribute.\n      * If you do neither, the service's stop and start commands will be used.\"\n\n    feature :refreshable, \"The provider can restart the service.\",\n            :methods => [:restart]\n\n    feature :enableable, \"The provider can enable and disable the service.\",\n            :methods => [:disable, :enable, :enabled?]\n\n    feature :delayed_startable, \"The provider can set service to delayed start\",\n            :methods => [:delayed_start]\n\n    feature :manual_startable, \"The provider can set service to manual start\",\n            :methods => [:manual_start]\n\n    feature :controllable, \"The provider uses a control variable.\"\n\n    feature :flaggable, \"The provider can pass flags to the service.\"\n\n    feature :maskable, \"The provider can 'mask' the service.\",\n            :methods => [:mask]\n\n    feature :configurable_timeout, \"The provider can specify a minumum timeout for syncing service properties\"\n\n    feature :manages_logon_credentials, \"The provider can specify the logon credentials used for a service\"\n\n    newproperty(:enable, :required_features => :enableable) do\n      desc \"Whether a service should be enabled to start at boot.\n        This property behaves differently depending on the platform;\n        wherever possible, it relies on local tools to enable or disable\n        a given service. Default values depend on the platform.\n\n        If you don't specify a value for the `enable` attribute, Puppet leaves\n        that aspect of the service alone and your operating system determines\n        the behavior.\"\n\n      newvalue(:true, :event => :service_enabled) do\n        provider.enable\n      end\n\n      newvalue(:false, :event => :service_disabled) do\n        provider.disable\n      end\n\n      newvalue(:manual, :event => :service_manual_start, :required_features => :manual_startable) do\n        provider.manual_start\n      end\n\n      # This only makes sense on systemd systems. Otherwise, it just defaults\n      # to disable.\n      newvalue(:mask, :event => :service_disabled, :required_features => :maskable) do\n        provider.mask\n      end\n\n      def retrieve\n        provider.enabled?\n      end\n\n      newvalue(:delayed, :event => :service_delayed_start, :required_features => :delayed_startable) do\n        provider.delayed_start\n      end\n\n      def insync?(current)\n        return provider.enabled_insync?(current) if provider.respond_to?(:enabled_insync?)\n\n        super(current)\n      end\n    end\n\n    # Handle whether the service should actually be running right now.\n    newproperty(:ensure) do\n      desc \"Whether a service should be running. Default values depend on the platform.\"\n\n      newvalue(:stopped, :event => :service_stopped) do\n        provider.stop\n      end\n\n      newvalue(:running, :event => :service_started, :invalidate_refreshes => true) do\n        provider.start\n      end\n\n      aliasvalue(:false, :stopped)\n      aliasvalue(:true, :running)\n\n      def retrieve\n        provider.status\n      end\n\n      def sync\n        property = @resource.property(:logonaccount)\n        if property\n          val = property.retrieve\n          property.sync unless property.safe_insync?(val)\n        end\n\n        event = super()\n\n        property = @resource.property(:enable)\n        if property\n          val = property.retrieve\n          property.sync unless property.safe_insync?(val)\n        end\n\n        event\n      end\n    end\n\n    newproperty(:logonaccount, :required_features => :manages_logon_credentials) do\n      desc \"Specify an account for service logon\"\n\n      def insync?(current)\n        return provider.logonaccount_insync?(current) if provider.respond_to?(:logonaccount_insync?)\n\n        super(current)\n      end\n    end\n\n    newparam(:logonpassword, :required_features => :manages_logon_credentials) do\n      desc \"Specify a password for service logon. Default value is an empty string (when logonaccount is specified).\"\n\n      validate do |value|\n        raise ArgumentError, _(\"Passwords cannot include ':'\") if value.is_a?(String) && value.include?(\":\")\n      end\n\n      sensitive true\n      defaultto { @resource[:logonaccount] ? \"\" : nil }\n    end\n\n    newproperty(:flags, :required_features => :flaggable) do\n      desc \"Specify a string of flags to pass to the startup script.\"\n    end\n\n    newparam(:binary) do\n      desc \"The path to the daemon.  This is only used for\n        systems that do not support init scripts.  This binary will be\n        used to start the service if no `start` parameter is\n        provided.\"\n    end\n\n    newparam(:hasstatus) do\n      desc \"Declare whether the service's init script has a functional status\n        command. This attribute's default value changed in Puppet 2.7.0.\n\n        The init script's status command must return 0 if the service is\n        running and a nonzero value otherwise. Ideally, these exit codes\n        should conform to [the LSB's specification][lsb-exit-codes] for init\n        script status actions, but Puppet only considers the difference\n        between 0 and nonzero to be relevant.\n\n        If a service's init script does not support any kind of status command,\n        you should set `hasstatus` to false and either provide a specific\n        command using the `status` attribute or expect that Puppet will look for\n        the service name in the process table. Be aware that 'virtual' init\n        scripts (like 'network' under Red Hat systems) will respond poorly to\n        refresh events from other resources if you override the default behavior\n        without providing a status command.\"\n\n      newvalues(:true, :false)\n\n      defaultto :true\n    end\n    newparam(:name) do\n      desc <<-EOT\n        The name of the service to run.\n\n        This name is used to find the service; on platforms where services\n        have short system names and long display names, this should be the\n        short name. (To take an example from Windows, you would use \"wuauserv\"\n        rather than \"Automatic Updates.\")\n      EOT\n      isnamevar\n    end\n\n    newparam(:path) do\n      desc \"The search path for finding init scripts.  Multiple values should\n        be separated by colons or provided as an array.\"\n\n      munge do |value|\n        value = [value] unless value.is_a?(Array)\n        value.flatten.collect { |p| p.split(File::PATH_SEPARATOR) }.flatten\n      end\n\n      defaultto { provider.class.defpath if provider.class.respond_to?(:defpath) }\n    end\n    newparam(:pattern) do\n      desc \"The pattern to search for in the process table.\n        This is used for stopping services on platforms that do not\n        support init scripts, and is also used for determining service\n        status on those service whose init scripts do not include a status\n        command.\n\n        Defaults to the name of the service. The pattern can be a simple string\n        or any legal Ruby pattern, including regular expressions (which should\n        be quoted without enclosing slashes).\"\n\n      defaultto { @resource[:binary] || @resource[:name] }\n    end\n    newparam(:restart) do\n      desc \"Specify a *restart* command manually.  If left\n        unspecified, the service will be stopped and then started.\"\n    end\n    newparam(:start) do\n      desc \"Specify a *start* command manually.  Most service subsystems\n        support a `start` command, so this will not need to be\n        specified.\"\n    end\n    newparam(:status) do\n      desc \"Specify a *status* command manually.  This command must\n        return 0 if the service is running and a nonzero value otherwise.\n        Ideally, these exit codes should conform to [the LSB's\n        specification][lsb-exit-codes] for init script status actions, but\n        Puppet only considers the difference between 0 and nonzero to be\n        relevant.\n\n        If left unspecified, the status of the service will be determined\n        automatically, usually by looking for the service in the process\n        table.\n\n        [lsb-exit-codes]: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html\"\n    end\n\n    newparam(:stop) do\n      desc \"Specify a *stop* command manually.\"\n    end\n\n    newparam(:control) do\n      desc \"The control variable used to manage services (originally for HP-UX).\n        Defaults to the upcased service name plus `START` replacing dots with\n        underscores, for those providers that support the `controllable` feature.\"\n      defaultto { resource.name.tr(\".\", \"_\").upcase + \"_START\" if resource.provider.controllable? }\n    end\n\n    newparam :hasrestart do\n      desc \"Specify that an init script has a `restart` command.  If this is\n        false and you do not specify a command in the `restart` attribute,\n        the init script's `stop` and `start` commands will be used.\"\n      newvalues(:true, :false)\n    end\n\n    newparam(:manifest) do\n      desc \"Specify a command to config a service, or a path to a manifest to do so.\"\n    end\n\n    newparam(:timeout, :required_features => :configurable_timeout) do\n      desc \"Specify an optional minimum timeout (in seconds) for puppet to wait when syncing service properties\"\n      defaultto { provider.respond_to?(:default_timeout) ? provider.default_timeout : 10 }\n\n      munge do |value|\n        value = value.to_i\n        raise if value < 1\n\n        value\n      rescue\n        raise Puppet::Error, _(\"\\\"%{value}\\\" is not a positive integer: the timeout parameter must be specified as a positive integer\") % { value: value }\n      end\n    end\n\n    # Basically just a synonym for restarting.  Used to respond\n    # to events.\n    def refresh\n      # Only restart if we're actually running\n      if (@parameters[:ensure] || newattr(:ensure)).retrieve == :running\n        provider.restart\n      else\n        debug \"Skipping restart; service is not running\"\n      end\n    end\n\n    def self.needs_ensure_retrieved\n      false\n    end\n\n    validate do\n      if @parameters[:logonpassword] && @parameters[:logonaccount].nil?\n        raise Puppet::Error, _(\"The 'logonaccount' parameter is mandatory when setting 'logonpassword'.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/stage.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.newtype(:stage) do\n  desc \"A resource type for creating new run stages.  Once a stage is available,\n    classes can be assigned to it by declaring them with the resource-like syntax\n    and using\n    [the `stage` metaparameter](https://puppet.com/docs/puppet/latest/metaparameter.html#stage).\n\n    Note that new stages are not useful unless you also declare their order\n    in relation to the default `main` stage.\n\n    A complete run stage example:\n\n        stage { 'pre':\n          before => Stage['main'],\n        }\n\n        class { 'apt-updates':\n          stage => 'pre',\n        }\n\n    Individual resources cannot be assigned to run stages; you can only set stages\n    for classes.\"\n\n  newparam :name do\n    desc \"The name of the stage. Use this as the value for the `stage` metaparameter\n      when assigning classes to this stage.\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/tidy.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/parameter/boolean'\n\nPuppet::Type.newtype(:tidy) do\n  require_relative '../../puppet/file_serving/fileset'\n  require_relative '../../puppet/file_bucket/dipper'\n\n  @doc = \"Remove unwanted files based on specific criteria.  Multiple\n    criteria are OR'd together, so a file that is too large but is not\n    old enough will still get tidied. Ignores managed resources.\n\n    If you don't specify either `age` or `size`, then all files will\n    be removed.\n\n    This resource type works by generating a file resource for every file\n    that should be deleted and then letting that resource perform the\n    actual deletion.\n    \"\n\n  # Tidy names are not isomorphic with the objects.\n  @isomorphic = false\n\n  newparam(:path) do\n    desc \"The path to the file or directory to manage.  Must be fully\n      qualified.\"\n    isnamevar\n    munge do |value|\n      File.expand_path(value)\n    end\n  end\n\n  newparam(:recurse) do\n    desc \"If target is a directory, recursively descend\n      into the directory looking for files to tidy. Numeric values\n      specify a limit for the recursion depth, `true` means\n      unrestricted recursion.\"\n\n    newvalues(:true, :false, :inf, /^[0-9]+$/)\n\n    # Replace the validation so that we allow numbers in\n    # addition to string representations of them.\n    validate { |arg| }\n    munge do |value|\n      newval = super(value)\n      case newval\n      when :true, :inf; true\n      when :false; false\n      when Integer; value\n      when /^\\d+$/; Integer(value)\n      else\n        raise ArgumentError, _(\"Invalid recurse value %{value}\") % { value: value.inspect }\n      end\n    end\n  end\n\n  newparam(:max_files) do\n    desc \"In case the resource is a directory and the recursion is enabled, puppet will\n      generate a new resource for each file file found, possible leading to\n      an excessive number of resources generated without any control.\n\n      Setting `max_files` will check the number of file resources that\n      will eventually be created and will raise a resource argument error if the\n      limit will be exceeded.\n\n      Use value `0` to disable the check. In this case, a warning is logged if\n      the number of files exceeds 1000.\"\n\n    defaultto 0\n    newvalues(/^[0-9]+$/)\n  end\n\n  newparam(:matches) do\n    desc <<-'EOT'\n      One or more (shell type) file glob patterns, which restrict\n      the list of files to be tidied to those whose basenames match\n      at least one of the patterns specified. Multiple patterns can\n      be specified using an array.\n\n      Example:\n\n          tidy { '/tmp':\n            age     => '1w',\n            recurse => 1,\n            matches => [ '[0-9]pub*.tmp', '*.temp', 'tmpfile?' ],\n          }\n\n      This removes files from `/tmp` if they are one week old or older,\n      are not in a subdirectory and match one of the shell globs given.\n\n      Note that the patterns are matched against the basename of each\n      file -- that is, your glob patterns should not have any '/'\n      characters in them, since you are only specifying against the last\n      bit of the file.\n\n      Finally, note that you must now specify a non-zero/non-false value\n      for recurse if matches is used, as matches only apply to files found\n      by recursion (there's no reason to use static patterns match against\n      a statically determined path).  Requiring explicit recursion clears\n      up a common source of confusion.\n    EOT\n\n    # Make sure we convert to an array.\n    munge do |value|\n      fail _(\"Tidy can't use matches with recurse 0, false, or undef\") if (@resource[:recurse]).to_s =~ /^(0|false|)$/\n\n      [value].flatten\n    end\n\n    # Does a given path match our glob patterns, if any?  Return true\n    # if no patterns have been provided.\n    def tidy?(path, stat)\n      basename = File.basename(path)\n      flags = File::FNM_DOTMATCH | File::FNM_PATHNAME\n      (value.find { |pattern| File.fnmatch(pattern, basename, flags) } ? true : false)\n    end\n  end\n\n  newparam(:backup) do\n    desc \"Whether tidied files should be backed up.  Any values are passed\n      directly to the file resources used for actual file deletion, so consult\n      the `file` type's backup documentation to determine valid values.\"\n  end\n\n  newparam(:age) do\n    desc \"Tidy files whose age is equal to or greater than\n      the specified time.  You can choose seconds, minutes,\n      hours, days, or weeks by specifying the first letter of any\n      of those words (for example, '1w' represents one week).\n\n      Specifying 0 will remove all files.\"\n\n    AgeConvertors = {\n      :s => 1,\n      :m => 60,\n      :h => 60 * 60,\n      :d => 60 * 60 * 24,\n      :w => 60 * 60 * 24 * 7,\n    }\n\n    def convert(unit, multi)\n      num = AgeConvertors[unit]\n      if num\n        num * multi\n      else\n        self.fail _(\"Invalid age unit '%{unit}'\") % { unit: unit }\n      end\n    end\n\n    def tidy?(path, stat)\n      # If the file's older than we allow, we should get rid of it.\n      (Time.now.to_i - stat.send(resource[:type]).to_i) >= value\n    end\n\n    munge do |age|\n      unit = multi = nil\n      case age\n      when /^([0-9]+)(\\w)\\w*$/\n        multi = Integer(Regexp.last_match(1))\n        unit = Regexp.last_match(2).downcase.intern\n      when /^([0-9]+)$/\n        multi = Integer(Regexp.last_match(1))\n        unit = :d\n      else\n        # TRANSLATORS tidy is the name of a program and should not be translated\n        self.fail _(\"Invalid tidy age %{age}\") % { age: age }\n      end\n\n      convert(unit, multi)\n    end\n  end\n\n  newparam(:size) do\n    desc \"Tidy files whose size is equal to or greater than\n      the specified size.  Unqualified values are in kilobytes, but\n      *b*, *k*, *m*, *g*, and *t* can be appended to specify *bytes*,\n      *kilobytes*, *megabytes*, *gigabytes*, and *terabytes*, respectively.\n      Only the first character is significant, so the full word can also\n      be used.\"\n\n    def convert(unit, multi)\n      num = { :b => 0, :k => 1, :m => 2, :g => 3, :t => 4 }[unit]\n      if num\n        result = multi\n        num.times do result *= 1024 end\n        result\n      else\n        self.fail _(\"Invalid size unit '%{unit}'\") % { unit: unit }\n      end\n    end\n\n    def tidy?(path, stat)\n      stat.size >= value\n    end\n\n    munge do |size|\n      case size\n      when /^([0-9]+)(\\w)\\w*$/\n        multi = Integer(Regexp.last_match(1))\n        unit = Regexp.last_match(2).downcase.intern\n      when /^([0-9]+)$/\n        multi = Integer(Regexp.last_match(1))\n        unit = :k\n      else\n        # TRANSLATORS tidy is the name of a program and should not be translated\n        self.fail _(\"Invalid tidy size %{age}\") % { age: age }\n      end\n\n      convert(unit, multi)\n    end\n  end\n\n  newparam(:type) do\n    desc \"Set the mechanism for determining age.\"\n\n    newvalues(:atime, :mtime, :ctime)\n\n    defaultto :atime\n  end\n\n  newparam(:rmdirs, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n    desc \"Tidy directories in addition to files; that is, remove\n      directories whose age is older than the specified criteria.\n      This will only remove empty directories, so all contained\n      files must also be tidied before a directory gets removed.\"\n  end\n\n  # Erase PFile's validate method\n  validate do\n  end\n\n  def self.instances\n    []\n  end\n\n  def depthfirst?\n    true\n  end\n\n  def initialize(hash)\n    super\n\n    # only allow backing up into filebuckets\n    self[:backup] = false unless self[:backup].is_a? Puppet::FileBucket::Dipper\n  end\n\n  # Make a file resource to remove a given file.\n  def mkfile(path)\n    # Force deletion, so directories actually get deleted.\n    parameters = {\n      :path => path, :backup => self[:backup],\n      :ensure => :absent, :force => true\n    }\n\n    new_file = Puppet::Type.type(:file).new(parameters)\n    new_file.copy_metaparams(@parameters)\n\n    new_file\n  end\n\n  def retrieve\n    # Our ensure property knows how to retrieve everything for us.\n    obj = @parameters[:ensure]\n    if obj\n      obj.retrieve\n    else\n      {}\n    end\n  end\n\n  # Hack things a bit so we only ever check the ensure property.\n  def properties\n    []\n  end\n\n  def generate\n    return [] unless stat(self[:path])\n\n    case self[:recurse]\n    when Integer, /^\\d+$/\n      parameter = { :max_files => self[:max_files],\n                    :recurse => true,\n                    :recurselimit => self[:recurse] }\n    when true, :true, :inf\n      parameter = { :max_files => self[:max_files],\n                    :recurse => true }\n    end\n\n    if parameter\n      files = Puppet::FileServing::Fileset.new(self[:path], parameter).files.collect do |f|\n        f == \".\" ? self[:path] : ::File.join(self[:path], f)\n      end\n    else\n      files = [self[:path]]\n    end\n    found_files = files.find_all { |path| tidy?(path) }.collect { |path| mkfile(path) }\n    result = found_files.each { |file| debug \"Tidying #{file.ref}\" }.sort { |a, b| b[:path] <=> a[:path] }\n    if found_files.size > 0\n      # TRANSLATORS \"Tidy\" is a program name and should not be translated\n      notice _(\"Tidying %{count} files\") % { count: found_files.size }\n    end\n\n    # No need to worry about relationships if we don't have rmdirs; there won't be\n    # any directories.\n    return result unless rmdirs?\n\n    # Now make sure that all directories require the files they contain, if all are available,\n    # so that a directory is emptied before we try to remove it.\n    files_by_name = result.each_with_object({}) { |file, hash| hash[file[:path]] = file; }\n\n    files_by_name.keys.sort { |a, b| b <=> a }.each do |path|\n      dir = ::File.dirname(path)\n      resource = files_by_name[dir]\n      next unless resource\n\n      if resource[:require]\n        resource[:require] << Puppet::Resource.new(:file, path)\n      else\n        resource[:require] = [Puppet::Resource.new(:file, path)]\n      end\n    end\n\n    result\n  end\n\n  # Does a given path match our glob patterns, if any?  Return true\n  # if no patterns have been provided.\n  def matches?(path)\n    return true unless self[:matches]\n\n    basename = File.basename(path)\n    flags = File::FNM_DOTMATCH | File::FNM_PATHNAME\n    if self[:matches].find { |pattern| File.fnmatch(pattern, basename, flags) }\n      true\n    else\n      debug \"No specified patterns match #{path}, not tidying\"\n      false\n    end\n  end\n\n  # Should we remove the specified file?\n  def tidy?(path)\n    # ignore files that are already managed, since we can't tidy\n    # those files anyway\n    return false if catalog.resource(:file, path)\n\n    stat = self.stat(path)\n    return false unless stat\n\n    return false if stat.ftype == \"directory\" and !rmdirs?\n\n    # The 'matches' parameter isn't OR'ed with the other tests --\n    # it's just used to reduce the list of files we can match.\n    param = parameter(:matches)\n    return false if param && !param.tidy?(path, stat)\n\n    tested = false\n    [:age, :size].each do |name|\n      param = parameter(name)\n      next unless param\n\n      tested = true\n      return true if param.tidy?(path, stat)\n    end\n\n    # If they don't specify either, then the file should always be removed.\n    return true unless tested\n\n    false\n  end\n\n  def stat(path)\n    Puppet::FileSystem.lstat(path)\n  rescue Errno::ENOENT\n    debug _(\"File does not exist\")\n    nil\n  rescue Errno::EACCES\n    # TRANSLATORS \"stat\" is a program name and should not be translated\n    warning _(\"Could not stat; permission denied\")\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/user.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'etc'\nrequire_relative '../../puppet/parameter/boolean'\nrequire_relative '../../puppet/property/list'\nrequire_relative '../../puppet/property/ordered_list'\nrequire_relative '../../puppet/property/keyvalue'\n\nmodule Puppet\n  Type.newtype(:user) do\n    @doc = \"Manage users.  This type is mostly built to manage system\n      users, so it is lacking some features useful for managing normal\n      users.\n\n      This resource type uses the prescribed native tools for creating\n      groups and generally uses POSIX APIs for retrieving information\n      about them.  It does not directly modify `/etc/passwd` or anything.\n\n      **Autorequires:** If Puppet is managing the user's primary group (as\n      provided in the `gid` attribute) or any group listed in the `groups`\n      attribute then the user resource will autorequire that group. If Puppet\n      is managing any role accounts corresponding to the user's roles, the\n      user resource will autorequire those role accounts.\"\n\n    feature :allows_duplicates,\n            \"The provider supports duplicate users with the same UID.\"\n\n    feature :manages_homedir,\n            \"The provider can create and remove home directories.\"\n\n    feature :manages_passwords,\n            \"The provider can modify user passwords, by accepting a password\n      hash.\"\n\n    feature :manages_password_age,\n            \"The provider can set age requirements and restrictions for\n      passwords.\"\n\n    feature :manages_password_salt,\n            \"The provider can set a password salt. This is for providers that\n       implement PBKDF2 passwords with salt properties.\"\n\n    feature :manages_solaris_rbac,\n            \"The provider can manage normal users\"\n\n    feature :manages_roles,\n            \"The provider can manage roles\"\n\n    feature :manages_expiry,\n            \"The provider can manage the expiry date for a user.\"\n\n    feature :system_users,\n            \"The provider allows you to create system users with lower UIDs.\"\n\n    feature :manages_aix_lam,\n            \"The provider can manage AIX Loadable Authentication Module (LAM) system.\"\n\n    feature :manages_local_users_and_groups,\n            \"Allows local users to be managed on systems that also use some other\n       remote Name Service Switch (NSS) method of managing accounts.\"\n\n    feature :manages_shell,\n            \"The provider allows for setting shell and validates if possible\"\n\n    feature :manages_loginclass,\n            \"The provider can manage the login class for a user.\"\n\n    newproperty(:ensure, :parent => Puppet::Property::Ensure) do\n      newvalue(:present, :event => :user_created) do\n        provider.create\n      end\n\n      newvalue(:absent, :event => :user_removed) do\n        provider.delete\n      end\n\n      newvalue(:role, :event => :role_created, :required_features => :manages_solaris_rbac) do\n        provider.create_role\n      end\n\n      desc \"The basic state that the object should be in.\"\n\n      # If they're talking about the thing at all, they generally want to\n      # say it should exist.\n      defaultto do\n        if @resource.managed?\n          :present\n        else\n          nil\n        end\n      end\n\n      def retrieve\n        if provider.exists?\n          if provider.respond_to?(:is_role?) and provider.is_role?\n            :role\n          else\n            :present\n          end\n        else\n          :absent\n        end\n      end\n\n      def sync\n        event = super\n\n        property = @resource.property(:roles)\n        if property\n          val = property.retrieve\n          property.sync unless property.safe_insync?(val)\n        end\n\n        event\n      end\n    end\n\n    newproperty(:home) do\n      desc \"The home directory of the user.  The directory must be created\n        separately and is not currently checked for existence.\"\n    end\n\n    newproperty(:uid) do\n      desc \"The user ID; must be specified numerically. If no user ID is\n        specified when creating a new user, then one will be chosen\n        automatically. This will likely result in the same user having\n        different UIDs on different systems, which is not recommended. This is\n        especially noteworthy when managing the same user on both Darwin and\n        other platforms, since Puppet does UID generation on Darwin, but\n        the underlying tools do so on other platforms.\n\n        On Windows, this property is read-only and will return the user's\n        security identifier (SID).\"\n\n      munge do |value|\n        case value\n        when String\n          if value =~ /^[-0-9]+$/\n            value = Integer(value)\n          end\n        end\n\n        return value\n      end\n    end\n\n    newproperty(:gid) do\n      desc \"The user's primary group.  Can be specified numerically or by name.\n\n        This attribute is not supported on Windows systems; use the `groups`\n        attribute instead. (On Windows, designating a primary group is only\n        meaningful for domain accounts, which Puppet does not currently manage.)\"\n\n      munge do |value|\n        if value.is_a?(String) and value =~ /^[-0-9]+$/\n          Integer(value)\n        else\n          value\n        end\n      end\n\n      def insync?(is)\n        # We know the 'is' is a number, so we need to convert the 'should' to a number,\n        # too.\n        @should.each do |value|\n          return true if is == Puppet::Util.gid(value)\n        end\n\n        false\n      end\n\n      def sync\n        found = false\n        @should.each do |value|\n          number = Puppet::Util.gid(value)\n          next unless number\n\n          provider.gid = number\n          found = true\n          break\n        end\n\n        fail _(\"Could not find group(s) %{groups}\") % { groups: @should.join(\",\") } unless found\n\n        # Use the default event.\n      end\n    end\n\n    newproperty(:comment) do\n      desc \"A description of the user.  Generally the user's full name.\"\n      def insync?(is)\n        # nameservice provider requires special attention to encoding\n        # Overrides Puppet::Property#insync?\n        if !@should.empty? && provider.respond_to?(:comments_insync?)\n          return provider.comments_insync?(is, @should)\n        end\n\n        super(is)\n      end\n\n      # In the case that our comments have incompatible encodings, set external\n      # encoding to support concatenation for display.\n      # overrides Puppet::Property#change_to_s\n      def change_to_s(currentvalue, newvalue)\n        if newvalue.is_a?(String) && !Encoding.compatible?(currentvalue, newvalue)\n          return super(currentvalue, newvalue.dup.force_encoding(currentvalue.encoding))\n        end\n\n        super(currentvalue, newvalue)\n      end\n    end\n\n    newproperty(:shell, :required_features => :manages_shell) do\n      desc \"The user's login shell.  The shell must exist and be\n        executable.\n\n        This attribute cannot be managed on Windows systems.\"\n    end\n\n    newproperty(:password, :required_features => :manages_passwords) do\n      desc %q{The user's password, in whatever encrypted format the local system\n        requires. Consult your operating system's documentation for acceptable password\n        encryption formats and requirements.\n\n        * Mac OS X 10.5 and 10.6, and some older Linux distributions, use salted SHA1\n          hashes. You can use Puppet's built-in `sha1` function to generate a salted SHA1\n          hash from a password.\n        * Mac OS X 10.7 (Lion), and many recent Linux distributions, use salted SHA512\n          hashes. The Puppet Labs [stdlib][] module contains a `str2saltedsha512` function\n          which can generate password hashes for these operating systems.\n        * OS X 10.8 and higher use salted SHA512 PBKDF2 hashes. When managing passwords\n          on these systems, the `salt` and `iterations` attributes need to be specified as\n          well as the password.\n        * macOS 10.15 and later require the salt to be 32 bytes. Because Puppet's user\n          resource requires the value to be hex encoded, the length of the salt's\n          string must be 64.\n        * Windows passwords can be managed only in cleartext, because there is no Windows\n          API for setting the password hash.\n\n        [stdlib]: https://github.com/puppetlabs/puppetlabs-stdlib/\n\n        Enclose any value that includes a dollar sign ($) in single quotes (') to avoid\n        accidental variable interpolation.\n\n        To redact passwords from reports to PuppetDB, use the `Sensitive` data type. For\n        example, this resource protects the password:\n\n        ```puppet\n        user { 'foo':\n          ensure   => present,\n          password => Sensitive(\"my secret password\")\n        }\n        ```\n\n        This results in the password being redacted from the report, as in the\n        `previous_value`, `desired_value`, and `message` fields below.\n\n        ```yaml\n            events:\n            - !ruby/object:Puppet::Transaction::Event\n              audited: false\n              property: password\n              previous_value: \"[redacted]\"\n              desired_value: \"[redacted]\"\n              historical_value:\n              message: changed [redacted] to [redacted]\n              name: :password_changed\n              status: success\n              time: 2017-05-17 16:06:02.934398293 -07:00\n              redacted: true\n              corrective_change: false\n            corrective_change: false\n        ```\n        }\n\n      validate do |value|\n        raise ArgumentError, _(\"Passwords cannot include ':'\") if value.is_a?(String) and value.include?(\":\")\n      end\n\n      sensitive true\n    end\n\n    newproperty(:password_min_age, :required_features => :manages_password_age) do\n      desc \"The minimum number of days a password must be used before it may be changed.\"\n\n      munge do |value|\n        case value\n        when String\n          Integer(value)\n        else\n          value\n        end\n      end\n\n      validate do |value|\n        if value.to_s !~ /^-?\\d+$/\n          raise ArgumentError, _(\"Password minimum age must be provided as a number.\")\n        end\n      end\n    end\n\n    newproperty(:password_max_age, :required_features => :manages_password_age) do\n      desc \"The maximum number of days a password may be used before it must be changed.\"\n\n      munge do |value|\n        case value\n        when String\n          Integer(value)\n        else\n          value\n        end\n      end\n\n      validate do |value|\n        if value.to_s !~ /^-?\\d+$/\n          raise ArgumentError, _(\"Password maximum age must be provided as a number.\")\n        end\n      end\n    end\n\n    newproperty(:password_warn_days, :required_features => :manages_password_age) do\n      desc \"The number of days before a password is going to expire (see the maximum password age) during which the user should be warned.\"\n\n      munge do |value|\n        case value\n        when String\n          Integer(value)\n        else\n          value\n        end\n      end\n\n      validate do |value|\n        if value.to_s !~ /^-?\\d+$/\n          raise ArgumentError, \"Password warning days must be provided as a number.\"\n        end\n      end\n    end\n\n    newproperty(:groups, :parent => Puppet::Property::List) do\n      desc \"The groups to which the user belongs.  The primary group should\n        not be listed, and groups should be identified by name rather than by\n        GID.  Multiple groups should be specified as an array.\"\n\n      validate do |value|\n        if value =~ /^\\d+$/\n          raise ArgumentError, _(\"Group names must be provided, not GID numbers.\")\n        end\n        raise ArgumentError, _(\"Group names must be provided as an array, not a comma-separated list.\") if value.include?(\",\")\n        raise ArgumentError, _(\"Group names must not be empty. If you want to specify \\\"no groups\\\" pass an empty array\") if value.empty?\n      end\n\n      def change_to_s(currentvalue, newvalue)\n        newvalue = newvalue.split(\",\") if newvalue != :absent\n\n        if provider.respond_to?(:groups_to_s)\n          # for Windows ADSI\n          # de-dupe the \"newvalue\" when the sync event message is generated,\n          # due to final retrieve called after the resource has been modified\n          newvalue = provider.groups_to_s(newvalue).split(',').uniq\n        end\n\n        super(currentvalue, newvalue)\n      end\n\n      # override Puppet::Property::List#retrieve\n      def retrieve\n        if provider.respond_to?(:groups_to_s)\n          # Windows ADSI groups returns SIDs, but retrieve needs names\n          # must return qualified names for SIDs for \"is\" value and puppet resource\n          return provider.groups_to_s(provider.groups).split(',')\n        end\n\n        super\n      end\n\n      def insync?(current)\n        if provider.respond_to?(:groups_insync?)\n          return provider.groups_insync?(current, @should)\n        end\n\n        super(current)\n      end\n    end\n\n    newparam(:name) do\n      desc \"The user name. While naming limitations vary by operating system,\n        it is advisable to restrict names to the lowest common denominator,\n        which is a maximum of 8 characters beginning with a letter.\n\n        Note that Puppet considers user names to be case-sensitive, regardless\n        of the platform's own rules; be sure to always use the same case when\n        referring to a given user.\"\n      isnamevar\n    end\n\n    newparam(:membership) do\n      desc \"If `minimum` is specified, Puppet will ensure that the user is a\n        member of all specified groups, but will not remove any other groups\n        that the user is a part of.\n\n        If `inclusive` is specified, Puppet will ensure that the user is a\n        member of **only** specified groups.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newparam(:system, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n      desc \"Whether the user is a system user, according to the OS's criteria;\n      on most platforms, a UID less than or equal to 500 indicates a system\n      user. This parameter is only used when the resource is created and will\n      not affect the UID when the user is present.\"\n\n      defaultto false\n    end\n\n    newparam(:allowdupe, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n      desc \"Whether to allow duplicate UIDs.\"\n\n      defaultto false\n    end\n\n    newparam(:managehome, :boolean => true, :parent => Puppet::Parameter::Boolean) do\n      desc \"Whether to manage the home directory when Puppet creates or removes the user.\n        This creates the home directory if Puppet also creates the user account, and deletes the\n        home directory if Puppet also removes the user account.\n\n        This parameter has no effect unless Puppet is also creating or removing the user in the\n        resource at the same time. For instance, Puppet creates a home directory for a managed\n        user if `ensure => present` and the user does not exist at the time of the Puppet run.\n        If the home directory is then deleted manually, Puppet will not recreate it on the next\n        run.\n\n        Note that on Windows, this manages creation/deletion of the user profile instead of the\n        home directory. The user profile is stored in the `C:\\\\Users\\\\<username>` directory.\"\n\n      defaultto false\n\n      validate do |val|\n        if munge(val)\n          raise ArgumentError, _(\"User provider %{name} can not manage home directories\") % { name: provider.class.name } if provider and !provider.class.manages_homedir?\n        end\n      end\n    end\n\n    newproperty(:expiry, :required_features => :manages_expiry) do\n      desc \"The expiry date for this user. Provide as either the special\n           value `absent` to ensure that the account never expires, or as\n           a zero-padded YYYY-MM-DD format -- for example, 2010-02-19.\"\n\n      newvalues :absent\n      newvalues(/^\\d{4}-\\d{2}-\\d{2}$/)\n\n      validate do |value|\n        if value.intern != :absent and value !~ /^\\d{4}-\\d{2}-\\d{2}$/\n          # TRANSLATORS YYYY-MM-DD represents a date with a four-digit year, a two-digit month, and a two-digit day,\n          # TRANSLATORS separated by dashes.\n          raise ArgumentError, _(\"Expiry dates must be YYYY-MM-DD or the string \\\"absent\\\"\")\n        end\n      end\n    end\n\n    # Autorequire the group, if it's around\n    autorequire(:group) do\n      autos = []\n\n      # autorequire primary group, if managed\n      obj = @parameters[:gid]\n      groups = obj.shouldorig if obj\n      if groups\n        groups = groups.collect { |group|\n          if group.is_a?(String) && group =~ /^\\d+$/\n            Integer(group)\n          else\n            group\n          end\n        }\n        groups.each { |group|\n          case group\n          when Integer\n            resource = catalog.resources.find { |r| r.is_a?(Puppet::Type.type(:group)) && r.should(:gid) == group }\n            if resource\n              autos << resource\n            end\n          else\n            autos << group\n          end\n        }\n      end\n\n      # autorequire groups, excluding primary group, if managed\n      obj = @parameters[:groups]\n      if obj\n        groups = obj.should\n        if groups\n          autos += groups.split(\",\")\n        end\n      end\n\n      autos\n    end\n\n    # This method has been exposed for puppet to manage users and groups of\n    # files in its settings and should not be considered available outside of\n    # puppet.\n    #\n    # (see Puppet::Settings#service_user_available?)\n    #\n    # @return [Boolean] if the user exists on the system\n    # @api private\n    def exists?\n      provider.exists?\n    end\n\n    newproperty(:roles, :parent => Puppet::Property::List, :required_features => :manages_roles) do\n      desc \"The roles the user has.  Multiple roles should be\n        specified as an array.\"\n\n      def membership\n        :role_membership\n      end\n\n      validate do |value|\n        if value =~ /^\\d+$/\n          raise ArgumentError, _(\"Role names must be provided, not numbers\")\n        end\n        raise ArgumentError, _(\"Role names must be provided as an array, not a comma-separated list\") if value.include?(\",\")\n      end\n    end\n\n    # autorequire the roles that the user has\n    autorequire(:user) do\n      reqs = []\n\n      roles_property = @parameters[:roles]\n      roles = roles_property.should if roles_property\n      if roles\n        reqs += roles.split(',')\n      end\n\n      reqs\n    end unless Puppet::Util::Platform.windows?\n\n    newparam(:role_membership) do\n      desc \"Whether specified roles should be considered the **complete list**\n        (`inclusive`) or the **minimum list** (`minimum`) of roles the user\n        has.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newproperty(:auths, :parent => Puppet::Property::List, :required_features => :manages_solaris_rbac) do\n      desc \"The auths the user has.  Multiple auths should be\n        specified as an array.\"\n\n      def membership\n        :auth_membership\n      end\n\n      validate do |value|\n        if value =~ /^\\d+$/\n          raise ArgumentError, _(\"Auth names must be provided, not numbers\")\n        end\n        raise ArgumentError, _(\"Auth names must be provided as an array, not a comma-separated list\") if value.include?(\",\")\n      end\n    end\n\n    newparam(:auth_membership) do\n      desc \"Whether specified auths should be considered the **complete list**\n        (`inclusive`) or the **minimum list** (`minimum`) of auths the user\n        has. This setting is specific to managing Solaris authorizations.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newproperty(:profiles, :parent => Puppet::Property::OrderedList, :required_features => :manages_solaris_rbac) do\n      desc \"The profiles the user has.  Multiple profiles should be\n        specified as an array.\"\n\n      def membership\n        :profile_membership\n      end\n\n      validate do |value|\n        if value =~ /^\\d+$/\n          raise ArgumentError, _(\"Profile names must be provided, not numbers\")\n        end\n        raise ArgumentError, _(\"Profile names must be provided as an array, not a comma-separated list\") if value.include?(\",\")\n      end\n    end\n\n    newparam(:profile_membership) do\n      desc \"Whether specified roles should be treated as the **complete list**\n        (`inclusive`) or the **minimum list** (`minimum`) of roles\n        of which the user is a member.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newproperty(:keys, :parent => Puppet::Property::KeyValue, :required_features => :manages_solaris_rbac) do\n      desc \"Specify user attributes in an array of key = value pairs.\"\n\n      def membership\n        :key_membership\n      end\n    end\n\n    newparam(:key_membership) do\n      desc \"Whether specified key/value pairs should be considered the\n        **complete list** (`inclusive`) or the **minimum list** (`minimum`) of\n        the user's attributes.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newproperty(:project, :required_features => :manages_solaris_rbac) do\n      desc \"The name of the project associated with a user.\"\n    end\n\n    newparam(:ia_load_module, :required_features => :manages_aix_lam) do\n      desc \"The name of the I&A module to use to manage this user.\n        This should be set to `files` if managing local users.\"\n    end\n\n    newproperty(:attributes, :parent => Puppet::Property::KeyValue, :required_features => :manages_aix_lam) do\n      desc \"Specify AIX attributes for the user in an array or hash of attribute = value pairs.\n\n      For example:\n\n      ```\n      ['minage=0', 'maxage=5', 'SYSTEM=compat']\n      ```\n\n      or\n\n     ```\n     attributes => { 'minage' => '0', 'maxage' => '5', 'SYSTEM' => 'compat' }\n     ```\n     \"\n\n      self.log_only_changed_or_new_keys = true\n\n      def membership\n        :attribute_membership\n      end\n\n      def delimiter\n        \" \"\n      end\n    end\n\n    newparam(:attribute_membership) do\n      desc \"Whether specified attribute value pairs should be treated as the\n        **complete list** (`inclusive`) or the **minimum list** (`minimum`) of\n        attribute/value pairs for the user.\"\n\n      newvalues(:inclusive, :minimum)\n\n      defaultto :minimum\n    end\n\n    newproperty(:salt, :required_features => :manages_password_salt) do\n      desc \"This is the 32-byte salt used to generate the PBKDF2 password used in\n            OS X. This field is required for managing passwords on OS X >= 10.8.\"\n    end\n\n    newproperty(:iterations, :required_features => :manages_password_salt) do\n      desc \"This is the number of iterations of a chained computation of the\n            [PBKDF2 password hash](https://en.wikipedia.org/wiki/PBKDF2). This parameter\n            is used in OS X, and is required for managing passwords on OS X 10.8 and\n            newer.\"\n\n      munge do |value|\n        if value.is_a?(String) and value =~ /^[-0-9]+$/\n          Integer(value)\n        else\n          value\n        end\n      end\n    end\n\n    newparam(:forcelocal, :boolean => true,\n                          :required_features => :manages_local_users_and_groups,\n                          :parent => Puppet::Parameter::Boolean) do\n      desc \"Forces the management of local accounts when accounts are also\n            being managed by some other Name Service Switch (NSS). For AIX, refer to the `ia_load_module` parameter.\n\n            This option relies on your operating system's implementation of `luser*` commands, such as `luseradd` , and `lgroupadd`, `lusermod`. The `forcelocal` option could behave unpredictably in some circumstances. If the tools it depends on are not available, it might have no effect at all.\"\n      defaultto false\n    end\n\n    def generate\n      unless self[:purge_ssh_keys].empty?\n        if Puppet::Type.type(:ssh_authorized_key).nil?\n          warning _(\"Ssh_authorized_key type is not available. Cannot purge SSH keys.\")\n        else\n          return find_unmanaged_keys\n        end\n      end\n\n      []\n    end\n\n    newparam(:purge_ssh_keys) do\n      desc \"Whether to purge authorized SSH keys for this user if they are not managed\n        with the `ssh_authorized_key` resource type. This parameter is a noop if the\n        ssh_authorized_key type is not available.\n\n        Allowed values are:\n\n        * `false` (default) --- don't purge SSH keys for this user.\n        * `true` --- look for keys in the `.ssh/authorized_keys` file in the user's\n          home directory. Purge any keys that aren't managed as `ssh_authorized_key`\n          resources.\n        * An array of file paths --- look for keys in all of the files listed. Purge\n          any keys that aren't managed as `ssh_authorized_key` resources. If any of\n          these paths starts with `~` or `%h`, that token will be replaced with\n          the user's home directory.\"\n\n      defaultto :false\n\n      # Use Symbols instead of booleans until PUP-1967 is resolved.\n      newvalues(:true, :false)\n\n      validate do |value|\n        if [:true, :false].include? value.to_s.intern\n          return\n        end\n\n        value = [value] if value.is_a?(String)\n        if value.is_a?(Array)\n          value.each do |entry|\n            raise ArgumentError, _(\"Each entry for purge_ssh_keys must be a string, not a %{klass}\") % { klass: entry.class } unless entry.is_a?(String)\n\n            valid_home = Puppet::Util.absolute_path?(entry) || entry =~ %r{^~/|^%h/}\n            raise ArgumentError, _(\"Paths to keyfiles must be absolute, not %{entry}\") % { entry: entry } unless valid_home\n          end\n          return\n        end\n        raise ArgumentError, _(\"purge_ssh_keys must be true, false, or an array of file names, not %{value}\") % { value: value.inspect }\n      end\n\n      munge do |value|\n        # Resolve string, boolean and symbol forms of true and false to a\n        # single representation.\n        case value\n        when :false, false, \"false\"\n          []\n        when :true, true, \"true\"\n          home = homedir\n          home ? [\"#{home}/.ssh/authorized_keys\"] : []\n        else\n          # value can be a string or array - munge each value\n          [value].flatten.filter_map do |entry|\n            authorized_keys_path(entry)\n          end\n        end\n      end\n\n      private\n\n      def homedir\n        resource[:home] || Dir.home(resource[:name])\n      rescue ArgumentError\n        Puppet.debug(\"User '#{resource[:name]}' does not exist\")\n        nil\n      end\n\n      def authorized_keys_path(entry)\n        return entry unless entry.match?(%r{^(?:~|%h)/})\n\n        # if user doesn't exist (yet), ignore nonexistent homedir\n        home = homedir\n        return nil unless home\n\n        # compiler freezes \"value\" so duplicate using a gsub, second mutating gsub! is then ok\n        entry = entry.gsub(%r{^~/}, \"#{home}/\")\n        entry.gsub!(%r{^%h/}, \"#{home}/\")\n        entry\n      end\n    end\n\n    newproperty(:loginclass, :required_features => :manages_loginclass) do\n      desc \"The name of login class to which the user belongs.\"\n\n      validate do |value|\n        if value =~ /^\\d+$/\n          raise ArgumentError, _(\"Class name must be provided.\")\n        end\n      end\n    end\n\n    # Generate ssh_authorized_keys resources for purging. The key files are\n    # taken from the purge_ssh_keys parameter. The generated resources inherit\n    # all metaparameters from the parent user resource.\n    #\n    # @return [Array<Puppet::Type::Ssh_authorized_key] a list of resources\n    #   representing the found keys\n    # @see generate\n    # @api private\n    def find_unmanaged_keys\n      self[:purge_ssh_keys]\n        .select { |f| File.readable?(f) }\n        .map { |f| unknown_keys_in_file(f) }\n        .flatten.each do |res|\n          res[:ensure] = :absent\n          res[:user] = self[:name]\n          res.copy_metaparams(@parameters)\n        end\n    end\n\n    # Parse an ssh authorized keys file superficially, extract the comments\n    # on the keys. These are considered names of possible ssh_authorized_keys\n    # resources. Keys that are managed by the present catalog are ignored.\n    #\n    # @see generate\n    # @api private\n    # @return [Array<Puppet::Type::Ssh_authorized_key] a list of resources\n    #   representing the found keys\n    def unknown_keys_in_file(keyfile)\n      # The ssh_authorized_key type is distributed as a module on the Forge,\n      # so we shouldn't rely on it being available.\n      return [] unless Puppet::Type.type(:ssh_authorized_key)\n\n      names = []\n      name_index = 0\n      # RFC 4716 specifies UTF-8 allowed in public key files per https://www.ietf.org/rfc/rfc4716.txt\n      # the authorized_keys file may contain UTF-8 comments\n      Puppet::FileSystem.open(keyfile, nil, 'r:UTF-8').each do |line|\n        next unless line =~ Puppet::Type.type(:ssh_authorized_key).keyline_regex\n\n        # the name is stored in the 4th capture of the regex\n        name = ::Regexp.last_match(4)\n        if name.empty?\n          ::Regexp.last_match(3).delete(\"\\n\")\n          # If no comment is specified for this key, generate a unique internal\n          # name. This uses the same rules as\n          # provider/ssh_authorized_key/parsed (PUP-3357)\n          name = \"#{keyfile}:unnamed-#{name_index += 1}\"\n        end\n        names << name\n        Puppet.debug \"#{ref} parsed for purging Ssh_authorized_key[#{name}]\"\n      end\n\n      names.map { |keyname|\n        Puppet::Type.type(:ssh_authorized_key).new(\n          :name => keyname,\n          :target => keyfile\n        )\n      }.reject { |res|\n        catalog.resource_refs.include? res.ref\n      }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type/whit.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Type.newtype(:whit) do\n  desc \"Whits are internal artifacts of Puppet's current implementation, and\n    Puppet suppresses their appearance in all logs. We make no guarantee of\n    the whit's continued existence, and it should never be used in an actual\n    manifest. Use the `anchor` type from the puppetlabs-stdlib module if you\n    need arbitrary whit-like no-op resources.\"\n\n  newparam :name do\n    desc \"The name of the whit, because it must have one.\"\n  end\n\n  # Hide the fact that we're a whit from logs.\n  #\n  # I hate you, milkman whit.  You are so painful, so often.\n  #\n  # In this case the memoized version means we generate a new string about 1.9\n  # percent of the time, and we allocate about 1.6MB less memory, and generate\n  # a whole lot less GC churn.\n  #\n  # That number probably goes up at least O(n) with the complexity of your\n  # catalog, and I suspect beyond that, because that is, like, 10,000 calls\n  # for 200 distinct objects.  Even with just linear, that is a constant\n  # factor of, like, 50n. --daniel 2012-07-17\n  def to_s\n    @to_s ||= name.sub(/^completed_|^admissible_/, \"\")\n  end\n  alias path to_s\n\n  def refresh\n    # We don't do anything with them, but we need this to show that we are\n    # \"refresh aware\" and not break the chain of propagation.\n  end\nend\n"
  },
  {
    "path": "lib/puppet/type.rb",
    "content": "# -*- coding: utf-8 -*-\n# frozen_string_literal: true\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/util/log'\nrequire_relative '../puppet/util/metric'\nrequire_relative '../puppet/property'\nrequire_relative '../puppet/parameter'\nrequire_relative '../puppet/util'\nrequire_relative '../puppet/util/autoload'\nrequire_relative '../puppet/metatype/manager'\nrequire_relative '../puppet/util/errors'\nrequire_relative '../puppet/util/logging'\nrequire_relative '../puppet/util/tagging'\nrequire_relative '../puppet/concurrent/lock'\n\n# see the bottom of the file for the rest of the inclusions\n\nmodule Puppet\n# The base class for all Puppet types.\n#\n# A type describes:\n#--\n# * **Attributes** - properties, parameters, and meta-parameters are different types of attributes of a type.\n#   * **Properties** - these are the properties of the managed resource (attributes of the entity being managed; like\n#     a file's owner, group and mode). A property describes two states; the 'is' (current state) and the 'should' (wanted\n#     state).\n#       * **Ensurable** - a set of traits that control the lifecycle (create, remove, etc.) of a managed entity.\n#         There is a default set of operations associated with being _ensurable_, but this can be changed.\n#       * **Name/Identity** - one property is the name/identity of a resource, the _namevar_ that uniquely identifies\n#         one instance of a type from all others.\n#   * **Parameters** - additional attributes of the type (that does not directly related to an instance of the managed\n#     resource; if an operation is recursive or not, where to look for things, etc.). A Parameter (in contrast to Property)\n#     has one current value where a Property has two (current-state and wanted-state).\n#   * **Meta-Parameters** - parameters that are available across all types. A meta-parameter typically has\n#     additional semantics; like the `require` meta-parameter. A new type typically does not add new meta-parameters,\n#     but you need to be aware of their existence so you do not inadvertently shadow an existing meta-parameters.\n# * **Parent** - a type can have a super type (that it inherits from).\n# * **Validation** - If not just a basic data type, or an enumeration of symbolic values, it is possible to provide\n#     validation logic for a type, properties and parameters.\n# * **Munging** - munging/unmunging is the process of turning a value in external representation (as used\n#     by a provider) into an internal representation and vice versa. A Type supports adding custom logic for these.\n# * **Auto Requirements** - a type can specify automatic relationships to resources to ensure that if they are being\n#     managed, they will be processed before this type.\n# * **Providers** - a provider is an implementation of a type's behavior - the management of a resource in the\n#     system being managed. A provider is often platform specific and is selected at runtime based on\n#     criteria/predicates specified in the configured providers. See {Puppet::Provider} for details.\n# * **Device Support** - A type has some support for being applied to a device; i.e. something that is managed\n#     by running logic external to the device itself. There are several methods that deals with type\n#     applicability for these special cases such as {apply_to_device}.\n#\n# Additional Concepts:\n# --\n# * **Resource-type** - A _resource type_ is a term used to denote the type of a resource; internally a resource\n#     is really an instance of a Ruby class i.e. {Puppet::Resource} which defines its behavior as \"resource data\".\n#     Conceptually however, a resource is an instance of a subclass of Type (e.g. File), where such a class describes\n#     its interface (what can be said/what is known about a resource of this type),\n# * **Managed Entity** - This is not a term in general use, but is used here when there is a need to make\n#     a distinction between a resource (a description of what/how something should be managed), and what it is\n#     managing (a file in the file system). The term _managed entity_ is a reference to the \"file in the file system\"\n# * **Isomorphism** - the quality of being _isomorphic_ means that two resource instances with the same name\n#     refers to the same managed entity. Or put differently; _an isomorphic name is the identity of a resource_.\n#     As an example, `exec` resources (that executes some command) have the command (i.e. the command line string) as\n#     their name, and these resources are said to be non-isomorphic.\n#\n# @note The Type class deals with multiple concerns; some methods provide an internal DSL for convenient definition\n#   of types, other methods deal with various aspects while running; wiring up a resource (expressed in Puppet DSL)\n#   with its _resource type_ (i.e. an instance of Type) to enable validation, transformation of values\n#   (munge/unmunge), etc. Lastly, Type is also responsible for dealing with Providers; the concrete implementations\n#   of the behavior that constitutes how a particular Type behaves on a particular type of system (e.g. how\n#   commands are executed on a flavor of Linux, on Windows, etc.). This means that as you are reading through the\n#   documentation of this class, you will be switching between these concepts, as well as switching between\n#   the conceptual level \"a resource is an instance of a resource-type\" and the actual implementation classes\n#   (Type, Resource, Provider, and various utility and helper classes).\n#\n# @api public\n#\n#\nclass Type\n  extend Puppet::CompilableResourceType\n  include Puppet::Util\n  include Puppet::Util::Errors\n  include Puppet::Util::Logging\n  include Puppet::Util::Tagging\n\n  # Comparing type instances.\n  include Comparable\n\n  # These variables are used in Metatype::Manager for managing types\n  @types = {}\n  @manager_lock = Puppet::Concurrent::Lock.new\n  extend Puppet::MetaType::Manager\n\n  # Compares this type against the given _other_ (type) and returns -1, 0, or +1 depending on the order.\n  # @param other [Object] the object to compare against (produces nil, if not kind of Type}\n  # @return [-1, 0, +1, nil] produces -1 if this type is before the given _other_ type, 0 if equals, and 1 if after.\n  #   Returns nil, if the given _other_ is not a kind of Type.\n  # @see Comparable\n  #\n  def <=>(other)\n    # Order is only maintained against other types, not arbitrary objects.\n    # The natural order is based on the reference name used when comparing\n    return nil unless other.is_a?(Puppet::CompilableResourceType) || other.class.is_a?(Puppet::CompilableResourceType)\n\n    # against other type instances.\n    ref <=> other.ref\n  end\n\n  # Code related to resource type attributes.\n  class << self\n    include Puppet::Util::ClassGen\n    include Puppet::Util::Warnings\n\n    # @return [Array<Puppet::Property>] The list of declared properties for the resource type.\n    # The returned lists contains instances if Puppet::Property or its subclasses.\n    attr_reader :properties\n  end\n\n  # Returns all the attribute names of the type in the appropriate order.\n  # The {key_attributes} come first, then the {provider}, then the {properties}, and finally\n  # the {parameters} and {metaparams},\n  # all in the order they were specified in the respective files.\n  # @return [Array<String>] all type attribute names in a defined order.\n  #\n  def self.allattrs\n    key_attributes | (parameters & [:provider]) | properties.collect(&:name) | parameters | metaparams\n  end\n\n  # Returns the class associated with the given attribute name.\n  # @param name [String] the name of the attribute to obtain the class for\n  # @return [Class, nil] the class for the given attribute, or nil if the name does not refer to an existing attribute\n  #\n  def self.attrclass(name)\n    @attrclasses ||= {}\n\n    # We cache the value, since this method gets called such a huge number\n    # of times (as in, hundreds of thousands in a given run).\n    unless @attrclasses.include?(name)\n      @attrclasses[name] = case attrtype(name)\n                           when :property; @validproperties[name]\n                           when :meta; @@metaparamhash[name]\n                           when :param; @paramhash[name]\n                           end\n    end\n    @attrclasses[name]\n  end\n\n  # Returns the attribute type (`:property`, `;param`, `:meta`).\n  # @comment What type of parameter are we dealing with? Cache the results, because\n  #   this method gets called so many times.\n  # @return [Symbol] a symbol describing the type of attribute (`:property`, `;param`, `:meta`)\n  #\n  def self.attrtype(attr)\n    @attrtypes ||= {}\n    unless @attrtypes.include?(attr)\n      @attrtypes[attr] = case\n                         when @validproperties.include?(attr); :property\n                         when @paramhash.include?(attr); :param\n                         when @@metaparamhash.include?(attr); :meta\n                         end\n    end\n\n    @attrtypes[attr]\n  end\n\n  # Provides iteration over meta-parameters.\n  # @yieldparam p [Puppet::Parameter] each meta parameter\n  # @return [void]\n  #\n  def self.eachmetaparam\n    @@metaparams.each { |p| yield p.name }\n  end\n\n  # Creates a new `ensure` property with configured default values or with configuration by an optional block.\n  # This method is a convenience method for creating a property `ensure` with default accepted values.\n  # If no block is specified, the new `ensure` property will accept the default symbolic\n  # values `:present`, and `:absent` - see {Puppet::Property::Ensure}.\n  # If something else is wanted, pass a block and make calls to {Puppet::Property.newvalue} from this block\n  # to define each possible value. If a block is passed, the defaults are not automatically added to the set of\n  # valid values.\n  #\n  # @note This method will be automatically called without a block if the type implements the methods\n  #   specified by {ensurable?}. It is recommended to always call this method and not rely on this automatic\n  #   specification to clearly state that the type is ensurable.\n  #\n  # @overload ensurable()\n  # @overload ensurable({ ... })\n  # @yield [ ] A block evaluated in scope of the new Parameter\n  # @yieldreturn [void]\n  # @return [void]\n  # @dsl type\n  # @api public\n  #\n  def self.ensurable(&block)\n    if block_given?\n      newproperty(:ensure, :parent => Puppet::Property::Ensure, &block)\n    else\n      newproperty(:ensure, :parent => Puppet::Property::Ensure) do\n        defaultvalues\n      end\n    end\n  end\n\n  # Returns true if the type implements the default behavior expected by being _ensurable_ \"by default\".\n  # A type is _ensurable_ by default if it responds to `:exists`, `:create`, and `:destroy`.\n  # If a type implements these methods and have not already specified that it is _ensurable_, it will be\n  # made so with the defaults specified in {ensurable}.\n  # @return [Boolean] whether the type is _ensurable_ or not.\n  #\n  def self.ensurable?\n    # If the class has all three of these methods defined, then it's\n    # ensurable.\n    [:exists?, :create, :destroy].all? { |method|\n      public_method_defined?(method)\n    }\n  end\n\n  # @comment These `apply_to` methods are horrible.  They should really be implemented\n  #   as part of the usual system of constraints that apply to a type and\n  #   provider pair, but were implemented as a separate shadow system.\n  #\n  # @comment We should rip them out in favour of a real constraint pattern around the\n  #   target device - whatever that looks like - and not have this additional\n  #   magic here. --daniel 2012-03-08\n  #\n  # Makes this type applicable to `:device`.\n  # @return [Symbol] Returns `:device`\n  # @api private\n  #\n  def self.apply_to_device\n    @apply_to = :device\n  end\n\n  # Makes this type applicable to `:host`.\n  # @return [Symbol] Returns `:host`\n  # @api private\n  #\n  def self.apply_to_host\n    @apply_to = :host\n  end\n\n  # Makes this type applicable to `:both` (i.e. `:host` and `:device`).\n  # @return [Symbol] Returns `:both`\n  # @api private\n  #\n  def self.apply_to_all\n    @apply_to = :both\n  end\n\n  # Makes this type apply to `:host` if not already applied to something else.\n  # @return [Symbol] a `:device`, `:host`, or `:both` enumeration\n  # @api private\n  def self.apply_to\n    @apply_to ||= :host\n  end\n\n  # Returns true if this type is applicable to the given target.\n  # @param target [Symbol] should be :device, :host or :target, if anything else, :host is enforced\n  # @return [Boolean] true\n  # @api private\n  #\n  def self.can_apply_to(target)\n    [target == :device ? :device : :host, :both].include?(apply_to)\n  end\n\n  # Processes the options for a named parameter.\n  # @param name [String] the name of a parameter\n  # @param options [Hash] a hash of options\n  # @option options [Boolean] :boolean if option set to true, an access method on the form _name_? is added for the param\n  # @return [void]\n  #\n  def self.handle_param_options(name, options)\n    # If it's a boolean parameter, create a method to test the value easily\n    if options[:boolean]\n      define_method(name.to_s + \"?\") do\n        val = self[name]\n        if val == :true or val == true\n          true\n        end\n      end\n    end\n  end\n\n  # Is the given parameter a meta-parameter?\n  # @return [Boolean] true if the given parameter is a meta-parameter.\n  #\n  def self.metaparam?(param)\n    @@metaparamhash.include?(param.intern)\n  end\n\n  # Returns the meta-parameter class associated with the given meta-parameter name.\n  # Accepts a `nil` name, and return nil.\n  # @param name [String, nil] the name of a meta-parameter\n  # @return [Class,nil] the class for the given meta-parameter, or `nil` if no such meta-parameter exists, (or if\n  #   the given meta-parameter name is `nil`.\n  #\n  def self.metaparamclass(name)\n    return nil if name.nil?\n\n    @@metaparamhash[name.intern]\n  end\n\n  # Returns all meta-parameter names.\n  # @return [Array<String>] all meta-parameter names\n  #\n  def self.metaparams\n    @@metaparams.collect(&:name)\n  end\n\n  # Returns the documentation for a given meta-parameter of this type.\n  # @param metaparam [Puppet::Parameter] the meta-parameter to get documentation for.\n  # @return [String] the documentation associated with the given meta-parameter, or nil of no such documentation\n  #   exists.\n  # @raise if the given metaparam is not a meta-parameter in this type\n  #\n  def self.metaparamdoc(metaparam)\n    @@metaparamhash[metaparam].doc\n  end\n\n  # Creates a new meta-parameter.\n  # This creates a new meta-parameter that is added to this and all inheriting types.\n  # @param name [Symbol] the name of the parameter\n  # @param options [Hash] a hash with options.\n  # @option options [Class<inherits Puppet::Parameter>] :parent (Puppet::Parameter) the super class of this parameter\n  # @option options [Hash{String => Object}] :attributes a hash that is applied to the generated class\n  #   by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given\n  #   block is evaluated.\n  # @option options [Boolean] :boolean (false) specifies if this is a boolean parameter\n  # @option options [Boolean] :namevar  (false) specifies if this parameter is the namevar\n  # @option options [Symbol, Array<Symbol>] :required_features  specifies required provider features by name\n  # @return [Class<inherits Puppet::Parameter>] the created parameter\n  # @yield [ ] a required block that is evaluated in the scope of the new meta-parameter\n  # @api public\n  # @dsl type\n  # @todo Verify that this description is ok\n  #\n  def self.newmetaparam(name, options = {}, &block)\n    @@metaparams ||= []\n    @@metaparamhash ||= {}\n    name = name.intern\n\n    param = genclass(\n      name,\n      :parent => options[:parent] || Puppet::Parameter,\n      :prefix => \"MetaParam\",\n      :hash => @@metaparamhash,\n      :array => @@metaparams,\n      :attributes => options[:attributes],\n      &block\n    )\n\n    # Grr.\n    param.required_features = options[:required_features] if options[:required_features]\n\n    handle_param_options(name, options)\n\n    param.metaparam = true\n\n    param\n  end\n\n  # Copies all of a resource's metaparameters (except `alias`) to a generated child resource\n  # @param parameters [Hash] of a resource's parameters\n  # @return [Void]\n  def copy_metaparams(parameters)\n    parameters.each do |name, param|\n      self[name] = param.value if param.metaparam? && name != :alias\n    end\n    nil\n  end\n\n  # Returns the list of parameters that comprise the composite key / \"uniqueness key\".\n  # All parameters that return true from #isnamevar? or is named `:name` are included in the returned result.\n  # @see uniqueness_key\n  # @return [Array<Puppet::Parameter>] WARNING: this return type is uncertain\n  def self.key_attribute_parameters\n    @key_attribute_parameters ||= (\n      @parameters.find_all { |param|\n        param.isnamevar? or param.name == :name\n      }\n    )\n  end\n\n  # Returns cached {key_attribute_parameters} names.\n  # Key attributes are properties and parameters that comprise a composite key\n  # or \"uniqueness key\".\n  # @return [Array<String>] cached key_attribute names\n  #\n  def self.key_attributes\n    # This is a cache miss around 0.05 percent of the time. --daniel 2012-07-17\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @key_attributes_cache ||= key_attribute_parameters.collect(&:name)\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  # Returns any parameters that should be included by default in puppet resource's output\n  # @return [Array<Symbol>] the parameters to include\n  def self.parameters_to_include\n    []\n  end\n\n  # Returns a mapping from the title string to setting of attribute values.\n  # This default implementation provides a mapping of title to the one and only _namevar_ present\n  # in the type's definition.\n  # @note Advanced: some logic requires this mapping to be done differently, using a different\n  #   validation/pattern, breaking up the title\n  #   into several parts assigning each to an individual attribute, or even use a composite identity where\n  #   all namevars are seen as part of the unique identity (such computation is done by the {#uniqueness} method.\n  #   These advanced options are rarely used (only one of the built in puppet types use this, and then only\n  #   a small part of the available functionality), and the support for these advanced mappings is not\n  #   implemented in a straight forward way. For these reasons, this method has been marked as private).\n  #\n  # @raise [Puppet::DevError] if there is no title pattern and there are two or more key attributes\n  # @return [Array<Array<Regexp, Array<Array <Symbol, Proc>>>>, nil] a structure with a regexp and the first key_attribute ???\n  # @comment This wonderful piece of logic creates a structure used by Resource.parse_title which\n  #   has the capability to assign parts of the title to one or more attributes; It looks like an implementation\n  #   of a composite identity key (all parts of the key_attributes array are in the key). This can also\n  #   be seen in the method uniqueness_key.\n  #   The implementation in this method simply assigns the title to the one and only namevar (which is name\n  #   or a variable marked as namevar).\n  #   If there are multiple namevars (any in addition to :name?) then this method MUST be implemented\n  #   as it raises an exception if there is more than 1. Note that in puppet, it is only File that uses this\n  #   to create a different pattern for assigning to the :path attribute\n  #   This requires further digging.\n  #   The entire construct is somewhat strange, since resource checks if the method \"title_patterns\" is\n  #   implemented (it seems it always is) - why take this more expensive regexp mathching route for all\n  #   other types?\n  # @api private\n  #\n  def self.title_patterns\n    case key_attributes.length\n    when 0; []\n    when 1;\n      [[/(.*)/m, [[key_attributes.first]]]]\n    else\n      raise Puppet::DevError, _(\"you must specify title patterns when there are two or more key attributes\")\n    end\n  end\n\n  # Produces a resource's _uniqueness_key_ (or composite key).\n  # This key is an array of all key attributes' values. Each distinct tuple must be unique for each resource type.\n  # @see key_attributes\n  # @return [Object] an object that is a _uniqueness_key_ for this object\n  #\n  def uniqueness_key\n    self.class.key_attributes.sort_by(&:to_s).map { |attribute_name| self[attribute_name] }\n  end\n\n  # Creates a new parameter.\n  # @param name [Symbol] the name of the parameter\n  # @param options [Hash] a hash with options.\n  # @option options [Class<inherits Puppet::Parameter>] :parent (Puppet::Parameter) the super class of this parameter\n  # @option options [Hash{String => Object}] :attributes a hash that is applied to the generated class\n  #   by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given\n  #   block is evaluated.\n  # @option options [Boolean] :boolean (false) specifies if this is a boolean parameter\n  # @option options [Boolean] :namevar  (false) specifies if this parameter is the namevar\n  # @option options [Symbol, Array<Symbol>] :required_features  specifies required provider features by name\n  # @return [Class<inherits Puppet::Parameter>] the created parameter\n  # @yield [ ] a required block that is evaluated in the scope of the new parameter\n  # @api public\n  # @dsl type\n  #\n  def self.newparam(name, options = {}, &block)\n    options[:attributes] ||= {}\n\n    param = genclass(\n      name,\n      :parent => options[:parent] || Puppet::Parameter,\n      :attributes => options[:attributes],\n      :block => block,\n      :prefix => \"Parameter\",\n      :array => @parameters,\n      :hash => @paramhash\n    )\n\n    handle_param_options(name, options)\n\n    # Grr.\n    param.required_features = options[:required_features] if options[:required_features]\n\n    param.isnamevar if options[:namevar]\n\n    param\n  end\n\n  # Creates a new property.\n  # @param name [Symbol] the name of the property\n  # @param options [Hash] a hash with options.\n  # @option options [Symbol] :array_matching (:first) specifies how the current state is matched against\n  #   the wanted state. Use `:first` if the property is single valued, and (`:all`) otherwise.\n  # @option options [Class<inherits Puppet::Property>] :parent (Puppet::Property) the super class of this property\n  # @option options [Hash{String => Object}] :attributes a hash that is applied to the generated class\n  #   by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given\n  #   block is evaluated.\n  # @option options [Boolean] :boolean (false) specifies if this is a boolean parameter\n  # @option options [Symbol] :retrieve the method to call on the provider (or `parent` if `provider` is not set)\n  #   to retrieve the current value of this property.\n  # @option options [Symbol, Array<Symbol>] :required_features  specifies required provider features by name\n  # @return [Class<inherits Puppet::Property>] the created property\n  # @yield [ ] a required block that is evaluated in the scope of the new property\n  # @api public\n  # @dsl type\n  #\n  def self.newproperty(name, options = {}, &block)\n    name = name.intern\n\n    # This is here for types that might still have the old method of defining\n    # a parent class.\n    unless options.is_a? Hash\n      raise Puppet::DevError, _(\"Options must be a hash, not %{type}\") % { type: options.inspect }\n    end\n\n    raise Puppet::DevError, _(\"Class %{class_name} already has a property named %{property}\") % { class_name: self.name, property: name } if @validproperties.include?(name)\n\n    parent = options[:parent]\n    if parent\n      options.delete(:parent)\n    else\n      parent = Puppet::Property\n    end\n\n    # We have to create our own, new block here because we want to define\n    # an initial :retrieve method, if told to, and then eval the passed\n    # block if available.\n    prop = genclass(name, :parent => parent, :hash => @validproperties, :attributes => options) do\n      # If they've passed a retrieve method, then override the retrieve\n      # method on the class.\n      if options[:retrieve]\n        define_method(:retrieve) do\n          provider.send(options[:retrieve])\n        end\n      end\n\n      class_eval(&block) if block\n    end\n\n    # If it's the 'ensure' property, always put it first.\n    if name == :ensure\n      @properties.unshift prop\n    else\n      @properties << prop\n    end\n\n    prop\n  end\n\n  def self.paramdoc(param)\n    @paramhash[param].doc\n  end\n\n  # @return [Array<String>] Returns the parameter names\n  def self.parameters\n    return [] unless defined?(@parameters)\n\n    @parameters.collect(&:name)\n  end\n\n  # @return [Puppet::Parameter] Returns the parameter class associated with the given parameter name.\n  def self.paramclass(name)\n    @paramhash[name]\n  end\n\n  # @return [Puppet::Property] Returns the property class ??? associated with the given property name\n  def self.propertybyname(name)\n    @validproperties[name]\n  end\n\n  # Returns whether or not the given name is the name of a property, parameter or meta-parameter\n  # @return [Boolean] true if the given attribute name is the name of an existing property, parameter or meta-parameter\n  #\n  def self.validattr?(name)\n    name = name.intern\n    return true if name == :name\n\n    @validattrs ||= {}\n\n    unless @validattrs.include?(name)\n      @validattrs[name] = !!(validproperty?(name) or validparameter?(name) or metaparam?(name))\n    end\n\n    @validattrs[name]\n  end\n\n  # @return [Boolean] Returns true if the given name is the name of an existing property\n  def self.validproperty?(name)\n    name = name.intern\n    @validproperties.include?(name) && @validproperties[name]\n  end\n\n  # @return [Array<Symbol>, {}] Returns a list of valid property names, or an empty hash if there are none.\n  # @todo An empty hash is returned if there are no defined parameters (not an empty array). This looks like\n  #   a bug.\n  #\n  def self.validproperties\n    return {} unless defined?(@parameters)\n\n    @validproperties.keys\n  end\n\n  # @return [Boolean] Returns true if the given name is the name of an existing parameter\n  def self.validparameter?(name)\n    raise Puppet::DevError, _(\"Class %{class_name} has not defined parameters\") % { class_name: self } unless defined?(@parameters)\n\n    !!(@paramhash.include?(name) or @@metaparamhash.include?(name))\n  end\n\n  # (see validattr?)\n  # @note see comment in code - how should this be documented? Are some of the other query methods deprecated?\n  #   (or should be).\n  # @comment This is a forward-compatibility method - it's the validity interface we'll use in Puppet::Resource.\n  def self.valid_parameter?(name)\n    validattr?(name)\n  end\n\n  # @return [Boolean] Returns true if the wanted state of the resource is that it should be absent (i.e. to be deleted).\n  def deleting?\n    obj = @parameters[:ensure] and obj.should == :absent\n  end\n\n  # Creates a new property value holder for the resource if it is valid and does not already exist\n  # @return [Boolean] true if a new parameter was added, false otherwise\n  def add_property_parameter(prop_name)\n    if self.class.validproperty?(prop_name) && !@parameters[prop_name]\n      newattr(prop_name)\n      return true\n    end\n    false\n  end\n\n  # @return [Symbol, Boolean] Returns the name of the namevar if there is only one or false otherwise.\n  # @comment This is really convoluted and part of the support for multiple namevars (?).\n  #   If there is only one namevar, the produced value is naturally this namevar, but if there are several?\n  #   The logic caches the name of the namevar if it is a single name, but otherwise always\n  #   calls key_attributes, and then caches the first if there was only one, otherwise it returns\n  #   false and caches this (which is then subsequently returned as a cache hit).\n  #\n  def name_var\n    return @name_var_cache unless @name_var_cache.nil?\n\n    key_attributes = self.class.key_attributes\n    @name_var_cache = (key_attributes.length == 1) && key_attributes.first\n  end\n\n  # Gets the 'should' (wanted state) value of a parameter or property by name.\n  # To explicitly get the 'is' (current state) value use `o.is(:name)`, and to explicitly get the 'should' value\n  # use `o.should(:name)`\n  # @param name [String] the name of the attribute to obtain the 'should' value for.\n  # @return [Object] 'should'/wanted value of the given attribute\n  def [](name)\n    name = name.intern\n    fail(\"Invalid parameter #{name}(#{name.inspect})\") unless self.class.validattr?(name)\n\n    if name == :name\n      nv = name_var\n      name = nv if nv\n    end\n\n    obj = @parameters[name]\n    if obj\n      # Note that if this is a property, then the value is the \"should\" value,\n      # not the current value.\n      obj.value\n    else\n      nil\n    end\n  end\n\n  # Sets the 'should' (wanted state) value of a property, or the value of a parameter.\n  #\n  # @raise [Puppet::Error] if the setting of the value fails, or if the given name is nil.\n  # @raise [Puppet::ResourceError] when the parameter validation raises Puppet::Error or\n  #   ArgumentError\n  def []=(name, value)\n    name = name.intern\n\n    fail(\"no parameter named '#{name}'\") unless self.class.validattr?(name)\n\n    if name == :name\n      nv = name_var\n      name = nv if nv\n    end\n    raise Puppet::Error, \"Got nil value for #{name}\" if value.nil?\n\n    property = newattr(name)\n\n    if property\n      begin\n        # make sure the parameter doesn't have any errors\n        property.value = value\n      rescue Puppet::Error, ArgumentError => detail\n        error = Puppet::ResourceError.new(_(\"Parameter %{name} failed on %{ref}: %{detail}\") %\n                                              { name: name, ref: ref, detail: detail })\n        adderrorcontext(error, detail)\n        raise error\n      end\n    end\n  end\n\n  # Removes an attribute from the object; useful in testing or in cleanup\n  # when an error has been encountered\n  # @todo Don't know what the attr is (name or Property/Parameter?). Guessing it is a String name...\n  # @todo Is it possible to delete a meta-parameter?\n  # @todo What does delete mean? Is it deleted from the type or is its value state 'is'/'should' deleted?\n  # @param attr [String] the attribute to delete from this object. WHAT IS THE TYPE?\n  # @raise [Puppet::DecError] when an attempt is made to delete an attribute that does not exists.\n  #\n  def delete(attr)\n    attr = attr.intern\n    if @parameters.has_key?(attr)\n      @parameters.delete(attr)\n    else\n      raise Puppet::DevError, _(\"Undefined attribute '%{attribute}' in %{name}\") % { attribute: attr, name: self }\n    end\n  end\n\n  # Iterates over the properties that were set on this resource.\n  # @yieldparam property [Puppet::Property] each property\n  # @return [void]\n  def eachproperty\n    # properties is a private method\n    properties.each { |property|\n      yield property\n    }\n  end\n\n  # Return the parameters, metaparams, and properties that have a value or were set by a default. Properties are\n  # included since they are a subclass of parameter.\n  # @return [Array<Puppet::Parameter>] Array of parameter objects ( or subclass thereof )\n  def parameters_with_value\n    self.class.allattrs.filter_map { |attr| parameter(attr) }\n  end\n\n  # Iterates over all parameters with value currently set.\n  # @yieldparam parameter [Puppet::Parameter] or a subclass thereof\n  # @return [void]\n  def eachparameter\n    parameters_with_value.each { |parameter| yield parameter }\n  end\n\n  # Creates a transaction event.\n  # Called by Transaction or by a property.\n  # Merges the given options with the options `:resource`, `:file`, `:line`, and `:tags`, initialized from\n  # values in this object. For possible options to pass (if any ????) see {Puppet::Transaction::Event}.\n  # @todo Needs a better explanation \"Why should I care who is calling this method?\", What do I need to know\n  #   about events and how they work? Where can I read about them?\n  # @param options [Hash] options merged with a fixed set of options defined by this method, passed on to {Puppet::Transaction::Event}.\n  # @return [Puppet::Transaction::Event] the created event\n  def event(options = {})\n    Puppet::Transaction::Event.new(**{ :resource => self, :file => file, :line => line, :tags => tags }.merge(options))\n  end\n\n  # @return [Object, nil] Returns the 'should' (wanted state) value for a specified property, or nil if the\n  #   given attribute name is not a property (i.e. if it is a parameter, meta-parameter, or does not exist).\n  def should(name)\n    prop = @parameters[name.intern]\n    if prop && prop.is_a?(Puppet::Property)\n      prop.should\n    else\n      nil\n    end\n  end\n\n  # Registers an attribute to this resource type instance.\n  # Requires either the attribute name or class as its argument.\n  # This is a noop if the named property/parameter is not supported\n  # by this resource. Otherwise, an attribute instance is created\n  # and kept in this resource's parameters hash.\n  # @overload newattr(name)\n  #   @param name [Symbol] symbolic name of the attribute\n  # @overload newattr(klass)\n  #   @param klass [Class] a class supported as an attribute class, i.e. a subclass of\n  #     Parameter or Property\n  # @return [Object] An instance of the named Parameter or Property class associated\n  #   to this resource type instance, or nil if the attribute is not supported\n  #\n  def newattr(name)\n    if name.is_a?(Class)\n      klass = name\n      name = klass.name\n    end\n\n    klass = self.class.attrclass(name)\n    unless klass\n      raise Puppet::Error, \"Resource type #{self.class.name} does not support parameter #{name}\"\n    end\n\n    if provider and !provider.class.supports_parameter?(klass)\n      missing = klass.required_features.find_all { |f| !provider.class.feature?(f) }\n      debug \"Provider %s does not support features %s; not managing attribute %s\" % [provider.class.name, missing.join(\", \"), name]\n      return nil\n    end\n\n    return @parameters[name] if @parameters.include?(name)\n\n    @parameters[name] = klass.new(:resource => self)\n  end\n\n  # Returns a string representation of the resource's containment path in\n  # the catalog.\n  # @return [String]\n  def path\n    @path ||= '/' + pathbuilder.join('/')\n  end\n\n  # Returns the value of this object's parameter given by name\n  # @param name [String] the name of the parameter\n  # @return [Object] the value\n  def parameter(name)\n    @parameters[name.to_sym]\n  end\n\n  # Returns a shallow copy of this object's hash of attributes by name.\n  # Note that his not only comprises parameters, but also properties and metaparameters.\n  # Changes to the contained parameters will have an effect on the parameters of this type, but changes to\n  # the returned hash does not.\n  # @return [Hash{String => Object}] a new hash being a shallow copy of the parameters map name to parameter\n  def parameters\n    @parameters.dup\n  end\n\n  # @return [Boolean] Returns whether the attribute given by name has been added\n  #   to this resource or not.\n  def propertydefined?(name)\n    name = name.intern unless name.is_a? Symbol\n    @parameters.include?(name)\n  end\n\n  # Returns a {Puppet::Property} instance by name.\n  # To return the value, use 'resource[param]'\n  # @todo LAK:NOTE(20081028) Since the 'parameter' method is now a superset of this method,\n  #   this one should probably go away at some point. - Does this mean it should be deprecated ?\n  # @return [Puppet::Property] the property with the given name, or nil if not a property or does not exist.\n  def property(name)\n    obj = @parameters[name.intern]\n    if obj && obj.is_a?(Puppet::Property)\n      obj\n    else\n      nil\n    end\n  end\n\n  # @todo comment says \"For any parameters or properties that have defaults and have not yet been\n  #   set, set them now.  This method can be handed a list of attributes,\n  #   and if so it will only set defaults for those attributes.\"\n  # @todo Needs a better explanation, and investigation about the claim an array can be passed (it is passed\n  #   to self.class.attrclass to produce a class on which a check is made if it has a method class :default (does\n  #   not seem to support an array...\n  # @return [void]\n  #\n  def set_default(attr)\n    klass = self.class.attrclass(attr)\n    return unless klass\n    # TODO this is not a necessary check, as we define a class level attr_reader\n    return unless klass.method_defined?(:default)\n    return if @parameters.include?(klass.name)\n\n    parameter = newattr(klass.name)\n    return unless parameter\n\n    value = parameter.default\n    if value and !value.nil?\n      parameter.value = value\n    else\n      @parameters.delete(parameter.name)\n    end\n  end\n\n  # @todo the comment says: \"Convert our object to a hash.  This just includes properties.\"\n  # @todo this is confused, again it is the @parameters instance variable that is consulted, and\n  #   each value is copied - does it contain \"properties\" and \"parameters\" or both? Does it contain\n  #   meta-parameters?\n  #\n  # @return [Hash{ ??? => ??? }] a hash of WHAT?. The hash is a shallow copy, any changes to the\n  #  objects returned in this hash will be reflected in the original resource having these attributes.\n  #\n  def to_hash\n    rethash = {}\n\n    @parameters.each do |name, obj|\n      rethash[name] = obj.value\n    end\n\n    rethash\n  end\n\n  # @return [String] the name of this object's class\n  # @todo Would that be \"file\" for the \"File\" resource type? of \"File\" or something else?\n  #\n  def type\n    self.class.name\n  end\n\n  # @todo Comment says \"Return a specific value for an attribute.\", as opposed to what \"An unspecific value\"???\n  # @todo is this the 'is' or the 'should' value?\n  # @todo why is the return restricted to things that respond to :value? (Only non structural basic data types\n  #   supported?\n  #\n  # @return [Object, nil] the value of the attribute having the given name, or nil if the given name is not\n  #   an attribute, or the referenced attribute does not respond to `:value`.\n  def value(name)\n    name = name.intern\n\n    obj = @parameters[name]\n    if obj && obj.respond_to?(:value)\n      obj.value\n    else\n      nil\n    end\n  end\n\n  # @todo What is this used for? Needs a better explanation.\n  # @return [???] the version of the catalog or 0 if there is no catalog.\n  def version\n    return 0 unless catalog\n\n    catalog.version\n  end\n\n  # @return [Array<Puppet::Property>] Returns all of the property objects, in the order specified in the\n  #   class.\n  # @todo \"what does the 'order specified in the class' mean? The order the properties where added in the\n  #   ruby file adding a new type with new properties?\n  #\n  def properties\n    self.class.properties.filter_map { |prop| @parameters[prop.name] }\n  end\n\n  # Returns true if the type's notion of name is the identity of a resource.\n  # See the overview of this class for a longer explanation of the concept _isomorphism_.\n  # Defaults to true.\n  #\n  # @return [Boolean] true, if this type's name is isomorphic with the object\n  def self.isomorphic?\n    if defined?(@isomorphic)\n      @isomorphic\n    else\n      true\n    end\n  end\n\n  # @todo check that this gets documentation (it is at the class level as well as instance).\n  # (see isomorphic?)\n  def isomorphic?\n    self.class.isomorphic?\n  end\n\n  # Returns true if the instance is a managed instance.\n  # A 'yes' here means that the instance was created from the language, vs. being created\n  # in order resolve other questions, such as finding a package in a list.\n  # @note An object that is managed always stays managed, but an object that is not managed\n  #   may become managed later in its lifecycle.\n  # @return [Boolean] true if the object is managed\n  def managed?\n    # Once an object is managed, it always stays managed; but an object\n    # that is listed as unmanaged might become managed later in the process,\n    # so we have to check that every time\n    unless @managed\n      @managed = false\n      properties.each { |property|\n        s = property.should\n        if s and !property.class.unmanaged\n          @managed = true\n          break\n        end\n      }\n    end\n    @managed\n  end\n\n  ###############################\n  # Code related to the container behaviour.\n\n  # Returns true if the search should be done in depth-first order.\n  # This implementation always returns false.\n  # @todo What is this used for?\n  #\n  # @return [Boolean] true if the search should be done in depth first order.\n  #\n  def depthfirst?\n    false\n  end\n\n  # Removes this object (FROM WHERE?)\n  # @todo removes if from where?\n  # @return [void]\n  def remove\n    # This is hackish (mmm, cut and paste), but it works for now, and it's\n    # better than warnings.\n    @parameters.each do |_name, obj|\n      obj.remove\n    end\n    @parameters.clear\n\n    @parent = nil\n\n    # Remove the reference to the provider.\n    if provider\n      @provider.clear\n      @provider = nil\n    end\n  end\n\n  ###############################\n  # Code related to evaluating the resources.\n\n  # Returns the ancestors - WHAT?\n  # This implementation always returns an empty list.\n  # @todo WHAT IS THIS ?\n  # @return [Array<???>] returns a list of ancestors.\n  def ancestors\n    []\n  end\n\n  # Lifecycle method for a resource. This is called during graph creation.\n  # It should perform any consistency checking of the catalog and raise a\n  # Puppet::Error if the transaction should be aborted.\n  #\n  # It differs from the validate method, since it is called later during\n  # initialization and can rely on self.catalog to have references to all\n  # resources that comprise the catalog.\n  #\n  # @see Puppet::Transaction#add_vertex\n  # @raise [Puppet::Error] If the pre-run check failed.\n  # @return [void]\n  # @abstract a resource type may implement this method to perform\n  #   validation checks that can query the complete catalog\n  def pre_run_check\n  end\n\n  # Flushes the provider if supported by the provider, else no action.\n  # This is called by the transaction.\n  # @todo What does Flushing the provider mean? Why is it interesting to know that this is\n  #   called by the transaction? (It is not explained anywhere what a transaction is).\n  #\n  # @return [???, nil] WHAT DOES IT RETURN? GUESS IS VOID\n  def flush\n    provider.flush if provider and provider.respond_to?(:flush)\n  end\n\n  # Says if the ensure property should be retrieved if the resource is ensurable\n  # Defaults to true. Some resource type classes can override it\n  def self.needs_ensure_retrieved\n    true\n  end\n\n  # Retrieves the current value of all contained properties.\n  # Parameters and meta-parameters are not included in the result.\n  # @todo As opposed to all non contained properties? How is this different than any of the other\n  #   methods that also \"gets\" properties/parameters/etc. ?\n  # @return [Puppet::Resource] array of all property values (mix of types)\n  # @raise [fail???] if there is a provider and it is not suitable for the host this is evaluated for.\n  def retrieve\n    fail \"Provider #{provider.class.name} is not functional on this host\" if provider.is_a?(Puppet::Provider) and !provider.class.suitable?\n\n    result = Puppet::Resource.new(self.class, title)\n\n    # Provide the name, so we know we'll always refer to a real thing\n    result[:name] = self[:name] unless self[:name] == title\n\n    ensure_prop = property(:ensure)\n    if !ensure_prop && self.class.needs_ensure_retrieved && self.class.validattr?(:ensure)\n      ensure_prop = newattr(:ensure)\n    end\n\n    if ensure_prop\n      result[:ensure] = ensure_state = ensure_prop.retrieve\n    else\n      ensure_state = nil\n    end\n\n    properties.each do |property|\n      next if property.name == :ensure\n\n      if ensure_state == :absent\n        result[property] = :absent\n      else\n        result[property] = property.retrieve\n      end\n    end\n\n    result\n  end\n\n  # Retrieve the current state of the system as a Puppet::Resource. For\n  # the base Puppet::Type this does the same thing as #retrieve, but\n  # specific types are free to implement #retrieve as returning a hash,\n  # and this will call #retrieve and convert the hash to a resource.\n  # This is used when determining when syncing a resource.\n  #\n  # @return [Puppet::Resource] A resource representing the current state\n  #   of the system.\n  #\n  # @api private\n  def retrieve_resource\n    resource = retrieve\n    resource = Resource.new(self.class, title, :parameters => resource) if resource.is_a? Hash\n    resource\n  end\n\n  # Given the hash of current properties, should this resource be treated as if it\n  # currently exists on the system. May need to be overridden by types that offer up\n  # more than just :absent and :present.\n  def present?(current_values)\n    current_values[:ensure] != :absent\n  end\n\n  # Returns a hash of the current properties and their values.\n  # If a resource is absent, its value is the symbol `:absent`\n  # @return [Hash{Puppet::Property => Object}] mapping of property instance to its value\n  #\n  def currentpropvalues\n    # It's important to use the 'properties' method here, as it follows the order\n    # in which they're defined in the class.  It also guarantees that 'ensure'\n    # is the first property, which is important for skipping 'retrieve' on\n    # all the properties if the resource is absent.\n    ensure_state = false\n    properties.each_with_object({}) do |property, prophash|\n      if property.name == :ensure\n        ensure_state = property.retrieve\n        prophash[property] = ensure_state\n      elsif ensure_state == :absent\n        prophash[property] = :absent\n      else\n        prophash[property] = property.retrieve\n      end\n    end\n  end\n\n  # Returns the `noop` run mode status of this.\n  # @return [Boolean] true if running in noop mode.\n  def noop?\n    # If we're not a host_config, we're almost certainly part of\n    # Settings, and we want to ignore 'noop'\n    return false if catalog and !catalog.host_config?\n\n    if defined?(@noop)\n      @noop\n    else\n      Puppet[:noop]\n    end\n  end\n\n  # (see #noop?)\n  def noop\n    noop?\n  end\n\n  # Retrieves all known instances.\n  # @todo Retrieves them from where? Known to whom?\n  # Either requires providers or must be overridden.\n  # @raise [Puppet::DevError] when there are no providers and the implementation has not overridden this method.\n  def self.instances\n    raise Puppet::DevError, _(\"%{name} has no providers and has not overridden 'instances'\") % { name: name } if provider_hash.empty?\n\n    # Put the default provider first, then the rest of the suitable providers.\n    provider_instances = {}\n    providers_by_source.collect do |provider|\n      provider.instances.collect do |instance|\n        # We always want to use the \"first\" provider instance we find, unless the resource\n        # is already managed and has a different provider set\n        title = instance.respond_to?(:title) ? instance.title : instance.name\n        other = provider_instances[title]\n        if other\n          Puppet.debug {\n            \"%s %s found in both %s and %s; skipping the %s version\" % [name.to_s.capitalize, title, other.class.name, instance.class.name, instance.class.name]\n          }\n          next\n        end\n        provider_instances[title] = instance\n\n        result = new(:name => instance.name, :provider => instance, :title => title)\n        properties.each { |name| result.newattr(name) }\n        result\n      end\n    end.flatten.compact\n  end\n\n  # Returns a list of one suitable provider per source, with the default provider first.\n  # @todo Needs better explanation; what does \"source\" mean in this context?\n  # @return [Array<Puppet::Provider>] list of providers\n  #\n  def self.providers_by_source\n    # Put the default provider first (can be nil), then the rest of the suitable providers.\n    sources = []\n    [defaultprovider, suitableprovider].flatten.uniq.filter_map do |provider|\n      next if provider.nil?\n      next if sources.include?(provider.source)\n\n      sources << provider.source\n      provider\n    end\n  end\n\n  # Converts a simple hash into a Resource instance.\n  # @todo as opposed to a complex hash? Other raised exceptions?\n  # @param [Hash{Symbol, String => Object}] hash resource attribute to value map to initialize the created resource from\n  # @return [Puppet::Resource] the resource created from the hash\n  # @raise [Puppet::Error] if a title is missing in the given hash\n  def self.hash2resource(hash)\n    hash = hash.each_with_object({}) { |ary, result| result[ary[0].to_sym] = ary[1]; }\n\n    title = hash.delete(:title)\n    title ||= hash[:name]\n    title ||= hash[key_attributes.first] if key_attributes.length == 1\n\n    raise Puppet::Error, \"Title or name must be provided\" unless title\n\n    # Now create our resource.\n    resource = Puppet::Resource.new(self, title)\n    resource.catalog = hash.delete(:catalog)\n\n    sensitive = hash.delete(:sensitive_parameters)\n    if sensitive\n      resource.sensitive_parameters = sensitive\n    end\n\n    hash.each do |param, value|\n      resource[param] = value\n    end\n    resource\n  end\n\n  # Returns an array of strings representing the containment hierarchy\n  # (types/classes) that make up the path to the resource from the root\n  # of the catalog.  This is mostly used for logging purposes.\n  #\n  # @api private\n  def pathbuilder\n    p = parent\n    if p\n      [p.pathbuilder, ref].flatten\n    else\n      [ref]\n    end\n  end\n\n  ###############################\n  # Add all of the meta-parameters.\n  newmetaparam(:noop) do\n    desc \"Whether to apply this resource in noop mode.\n\n      When applying a resource in noop mode, Puppet will check whether it is in sync,\n      like it does when running normally. However, if a resource attribute is not in\n      the desired state (as declared in the catalog), Puppet will take no\n      action, and will instead report the changes it _would_ have made. These\n      simulated changes will appear in the report sent to the primary Puppet server, or\n      be shown on the console if running puppet agent or puppet apply in the\n      foreground. The simulated changes will not send refresh events to any\n      subscribing or notified resources, although Puppet will log that a refresh\n      event _would_ have been sent.\n\n      **Important note:**\n      [The `noop` setting](https://puppet.com/docs/puppet/latest/configuration.html#noop)\n      allows you to globally enable or disable noop mode, but it will _not_ override\n      the `noop` metaparameter on individual resources. That is, the value of the\n      global `noop` setting will _only_ affect resources that do not have an explicit\n      value set for their `noop` attribute.\"\n\n    newvalues(:true, :false)\n    munge do |value|\n      case value\n      when true, :true, \"true\"; @resource.noop = true\n      when false, :false, \"false\"; @resource.noop = false\n      end\n    end\n  end\n\n  newmetaparam(:schedule) do\n    desc \"A schedule to govern when Puppet is allowed to manage this resource.\n      The value of this metaparameter must be the `name` of a `schedule`\n      resource. This means you must declare a schedule resource, then\n      refer to it by name; see\n      [the docs for the `schedule` type](https://puppet.com/docs/puppet/latest/type.html#schedule)\n      for more info.\n\n          schedule { 'everyday':\n            period => daily,\n            range  => \\\"2-4\\\"\n          }\n\n          exec { \\\"/usr/bin/apt-get update\\\":\n            schedule => 'everyday'\n          }\n\n      Note that you can declare the schedule resource anywhere in your\n      manifests, as long as it ends up in the final compiled catalog.\"\n  end\n\n  newmetaparam(:audit) do\n    desc \"Marks a subset of this resource's unmanaged attributes for auditing. Accepts an\n      attribute name, an array of attribute names, or `all`.\n\n      Auditing a resource attribute has two effects: First, whenever a catalog\n      is applied with puppet apply or puppet agent, Puppet will check whether\n      that attribute of the resource has been modified, comparing its current\n      value to the previous run; any change will be logged alongside any actions\n      performed by Puppet while applying the catalog.\n\n      Secondly, marking a resource attribute for auditing will include that\n      attribute in inspection reports generated by puppet inspect; see the\n      puppet inspect documentation for more details.\n\n      Managed attributes for a resource can also be audited, but note that\n      changes made by Puppet will be logged as additional modifications. (I.e.\n      if a user manually edits a file whose contents are audited and managed,\n      puppet agent's next two runs will both log an audit notice: the first run\n      will log the user's edit and then revert the file to the desired state,\n      and the second run will log the edit made by Puppet.)\"\n\n    validate do |list|\n      list = Array(list).collect(&:to_sym)\n      unless list == [:all]\n        list.each do |param|\n          next if @resource.class.validattr?(param)\n\n          fail \"Cannot audit #{param}: not a valid attribute for #{resource}\"\n        end\n      end\n    end\n\n    munge do |args|\n      properties_to_audit(args).each do |param|\n        next unless resource.class.validproperty?(param)\n\n        resource.newattr(param)\n      end\n    end\n\n    def all_properties\n      resource.class.properties.find_all do |property|\n        resource.provider.nil? or resource.provider.class.supports_parameter?(property)\n      end.collect(&:name)\n    end\n\n    def properties_to_audit(list)\n      if !list.is_a?(Array) && list.to_sym == :all\n        all_properties\n      else\n        Array(list).collect(&:to_sym)\n      end\n    end\n  end\n\n  newmetaparam(:loglevel) do\n    desc \"Sets the level that information will be logged.\n      The log levels have the biggest impact when logs are sent to\n      syslog (which is currently the default).\n\n      The order of the log levels, in decreasing priority, is:\n\n      * `emerg`\n      * `alert`\n      * `crit`\n      * `err`\n      * `warning`\n      * `notice`\n      * `info` / `verbose`\n      * `debug`\n      \"\n    defaultto :notice\n\n    newvalues(*Puppet::Util::Log.levels)\n    newvalues(:verbose)\n\n    munge do |loglevel|\n      val = super(loglevel)\n      if val == :verbose\n        val = :info\n      end\n      val\n    end\n  end\n\n  newmetaparam(:alias) do\n    desc \"Creates an alias for the resource.  Puppet uses this internally when you\n      provide a symbolic title and an explicit namevar value:\n\n          file { 'sshdconfig':\n            path => $os['name'] ? {\n              solaris => '/usr/local/etc/ssh/sshd_config',\n              default => '/etc/ssh/sshd_config',\n            },\n            source => '...'\n          }\n\n          service { 'sshd':\n            subscribe => File['sshdconfig'],\n          }\n\n      When you use this feature, the parser sets `sshdconfig` as the title,\n      and the library sets that as an alias for the file so the dependency\n      lookup in `Service['sshd']` works.  You can use this metaparameter yourself,\n      but note that aliases generally only work for creating relationships; anything\n      else that refers to an existing resource (such as amending or overriding\n      resource attributes in an inherited class) must use the resource's exact\n      title. For example, the following code will not work:\n\n          file { '/etc/ssh/sshd_config':\n            owner => root,\n            group => root,\n            alias => 'sshdconfig',\n          }\n\n          File['sshdconfig'] {\n            mode => '0644',\n          }\n\n      There's no way here for the Puppet parser to know that these two stanzas\n      should be affecting the same file.\n\n      \"\n\n    munge do |aliases|\n      aliases = [aliases] unless aliases.is_a?(Array)\n\n      raise(ArgumentError, _(\"Cannot add aliases without a catalog\")) unless @resource.catalog\n\n      aliases.each do |other|\n        obj = @resource.catalog.resource(@resource.class.name, other)\n        if obj\n          unless obj.equal?(@resource) # same object?\n            self.fail(\"#{@resource.title} can not create alias #{other}: object already exists\")\n          end\n          next\n        end\n\n        # Newschool, add it to the catalog.\n        @resource.catalog.alias(@resource, other)\n      end\n    end\n  end\n\n  newmetaparam(:tag) do\n    desc \"Add the specified tags to the associated resource.  While all resources\n      are automatically tagged with as much information as possible\n      (e.g., each class and definition containing the resource), it can\n      be useful to add your own tags to a given resource.\n\n      Multiple tags can be specified as an array:\n\n          file {'/etc/hosts':\n            ensure => file,\n            source => 'puppet:///modules/site/hosts',\n            mode   => '0644',\n            tag    => ['bootstrap', 'minimumrun', 'mediumrun'],\n          }\n\n      Tags are useful for things like applying a subset of a host's configuration\n      with [the `tags` setting](https://puppet.com/docs/puppet/latest/configuration.html#tags)\n      (e.g. `puppet agent --test --tags bootstrap`).\"\n\n    munge do |tags|\n      tags = [tags] unless tags.is_a? Array\n\n      tags.each do |tag|\n        @resource.tag(tag)\n      end\n    end\n  end\n\n  # RelationshipMetaparam is an implementation supporting the meta-parameters `:require`, `:subscribe`,\n  # `:notify`, and `:before`.\n  #\n  #\n  class RelationshipMetaparam < Puppet::Parameter\n    class << self\n      attr_accessor :direction, :events, :callback, :subclasses\n    end\n\n    @subclasses = []\n\n    def self.inherited(sub)\n      @subclasses << sub\n    end\n\n    # @return [Array<Puppet::Resource>] turns attribute values into list of resources\n    def munge(references)\n      references = [references] unless references.is_a?(Array)\n      references.collect do |ref|\n        if ref.is_a?(Puppet::Resource)\n          ref\n        else\n          Puppet::Resource.new(ref)\n        end\n      end\n    end\n\n    # Checks each reference to assert that what it references exists in the catalog.\n    #\n    # @raise [???fail] if the referenced resource can not be found\n    # @return [void]\n    def validate_relationship\n      @value.each do |ref|\n        next if @resource.catalog.resource(ref.to_s)\n\n        description = self.class.direction == :in ? \"dependency\" : \"dependent\"\n        fail ResourceError, _(\"Could not find %{description} %{ref} for %{resource}\") %\n                            { description: description, ref: ref, resource: resource.ref }\n      end\n    end\n\n    # Creates edges for all relationships.\n    # The `:in` relationships are specified by the event-receivers, and `:out`\n    # relationships are specified by the event generator.\n    # @todo references to \"event-receivers\" and \"event generator\" means in this context - are those just\n    #   the resources at the two ends of the relationship?\n    # This way 'source' and 'target' are consistent terms in both edges\n    # and events, i.e. an event targets edges whose source matches\n    # the event's source. The direction of the relationship determines\n    # which resource is applied first and which resource is considered\n    # to be the event generator.\n    # @return [Array<Puppet::Relationship>]\n    # @raise [???fail] when a reference can not be resolved\n    #\n    def to_edges\n      @value.collect do |reference|\n        reference.catalog = resource.catalog\n\n        # Either of the two retrieval attempts could have returned\n        # nil.\n        related_resource = reference.resolve\n        unless related_resource\n          self.fail \"Could not retrieve dependency '#{reference}' of #{@resource.ref}\"\n        end\n\n        # Are we requiring them, or vice versa?  See the method docs\n        # for further info on this.\n        if self.class.direction == :in\n          source = related_resource\n          target = @resource\n        else\n          source = @resource\n          target = related_resource\n        end\n\n        method = self.class.callback\n        if method\n          subargs = {\n            :event => self.class.events,\n            :callback => method\n          }\n        else\n          # If there's no callback, there's no point in even adding\n          # a label.\n          subargs = nil\n        end\n\n        ## Corrected syntax of debug statement to reflect the way this was called.\n        # i.e. before, after, subscribe, notify\n        debug do\n          relation = case self.class.name\n                     when \"subscribe\"\n                       \"subscribes\"\n                     when \"notify\"\n                       \"notifies\"\n                     else\n                       self.class.name\n                     end\n\n          \"#{relation} to #{related_resource.ref}\"\n        end\n\n        Puppet::Relationship.new(source, target, subargs)\n      end\n    end\n  end\n\n  # @todo document this, have no clue what this does... it returns \"RelationshipMetaparam.subclasses\"\n  #\n  def self.relationship_params\n    RelationshipMetaparam.subclasses\n  end\n\n  # Note that the order in which the relationships params is defined\n  # matters.  The labeled params (notify and subscribe) must be later,\n  # so that if both params are used, those ones win.  It's a hackish\n  # solution, but it works.\n\n  newmetaparam(:require, :parent => RelationshipMetaparam, :attributes => { :direction => :in, :events => :NONE }) do\n    desc \"One or more resources that this resource depends on, expressed as\n      [resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\n      Multiple resources can be specified as an array of references. When this\n      attribute is present:\n\n      * The required resources will be applied **before** this resource.\n\n      This is one of the four relationship metaparameters, along with\n      `before`, `notify`, and `subscribe`. For more context, including the\n      alternate chaining arrow (`->` and `~>`) syntax, see\n      [the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\"\n  end\n\n  newmetaparam(:subscribe, :parent => RelationshipMetaparam, :attributes => { :direction => :in, :events => :ALL_EVENTS, :callback => :refresh }) do\n    desc \"One or more resources that this resource depends on, expressed as\n      [resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\n      Multiple resources can be specified as an array of references. When this\n      attribute is present:\n\n      * The subscribed resources will be applied _before_ this resource.\n      * If Puppet makes changes to any of the subscribed resources, it will cause\n        this resource to _refresh._ (Refresh behavior varies by resource\n        type: services will restart, mounts will unmount and re-mount, etc. Not\n        all types can refresh.)\n\n      This is one of the four relationship metaparameters, along with\n      `before`, `require`, and `notify`. For more context, including the\n      alternate chaining arrow (`->` and `~>`) syntax, see\n      [the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\"\n  end\n\n  newmetaparam(:before, :parent => RelationshipMetaparam, :attributes => { :direction => :out, :events => :NONE }) do\n    desc \"One or more resources that depend on this resource, expressed as\n      [resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\n      Multiple resources can be specified as an array of references. When this\n      attribute is present:\n\n      * This resource will be applied _before_ the dependent resources.\n\n      This is one of the four relationship metaparameters, along with\n      `require`, `notify`, and `subscribe`. For more context, including the\n      alternate chaining arrow (`->` and `~>`) syntax, see\n      [the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\"\n  end\n\n  newmetaparam(:notify, :parent => RelationshipMetaparam, :attributes => { :direction => :out, :events => :ALL_EVENTS, :callback => :refresh }) do\n    desc \"One or more resources that depend on this resource, expressed as\n      [resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\n      Multiple resources can be specified as an array of references. When this\n      attribute is present:\n\n      * This resource will be applied _before_ the notified resources.\n      * If Puppet makes changes to this resource, it will cause all of the\n        notified resources to _refresh._ (Refresh behavior varies by resource\n        type: services will restart, mounts will unmount and re-mount, etc. Not\n        all types can refresh.)\n\n      This is one of the four relationship metaparameters, along with\n      `before`, `require`, and `subscribe`. For more context, including the\n      alternate chaining arrow (`->` and `~>`) syntax, see\n      [the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\"\n  end\n\n  newmetaparam(:stage) do\n    desc %{Which run stage this class should reside in.\n\n      **Note: This metaparameter can only be used on classes,** and only when\n      declaring them with the resource-like syntax. It cannot be used on normal\n      resources or on classes declared with `include`.\n\n      By default, all classes are declared in the `main` stage. To assign a class\n      to a different stage, you must:\n\n      * Declare the new stage as a [`stage` resource](https://puppet.com/docs/puppet/latest/type.html#stage).\n      * Declare an order relationship between the new stage and the `main` stage.\n      * Use the resource-like syntax to declare the class, and set the `stage`\n        metaparameter to the name of the desired stage.\n\n      For example:\n\n          stage { 'pre':\n            before => Stage['main'],\n          }\n\n          class { 'apt-updates':\n            stage => 'pre',\n          }\n    }\n  end\n\n  ###############################\n  # All of the provider plumbing for the resource types.\n  require_relative '../puppet/provider'\n  require_relative '../puppet/util/provider_features'\n\n  # Add the feature handling module.\n  extend Puppet::Util::ProviderFeatures\n\n  # The provider that has been selected for the instance of the resource type.\n  # @return [Puppet::Provider,nil] the selected provider or nil, if none has been selected\n  #\n  attr_reader :provider\n\n  # the Type class attribute accessors\n  class << self\n    # The loader of providers to use when loading providers from disk.\n    # Although it looks like this attribute provides a way to operate with different loaders of\n    # providers that is not the case; the attribute is written when a new type is created,\n    # and should not be changed thereafter.\n    # @api private\n    #\n    attr_accessor :providerloader\n\n    # @todo Don't know if this is a name, or a reference to a Provider instance (now marked up as an instance\n    #   of Provider.\n    # @return [Puppet::Provider, nil] The default provider for this type, or nil if non is defines\n    #\n    attr_writer :defaultprovider\n  end\n\n  # The default provider, or the most suitable provider if no default provider was set.\n  # @note a warning will be issued if no default provider has been configured and a search for the most\n  #   suitable provider returns more than one equally suitable provider.\n  # @return [Puppet::Provider, nil] the default or most suitable provider, or nil if no provider was found\n  #\n  def self.defaultprovider\n    return @defaultprovider if @defaultprovider\n\n    suitable = suitableprovider\n\n    # Find which providers are a default for this system.\n    defaults = suitable.find_all(&:default?)\n\n    # If we don't have any default we use suitable providers\n    defaults = suitable if defaults.empty?\n    max = defaults.collect(&:specificity).max\n    defaults = defaults.find_all { |provider| provider.specificity == max }\n\n    if defaults.length > 1\n      Puppet.warning(_(\"Found multiple default providers for %{name}: %{provider_list}; using %{selected_provider}\") %\n                         { name: name, provider_list: defaults.collect { |i| i.name.to_s }.join(\", \"), selected_provider: defaults[0].name })\n    end\n\n    @defaultprovider = defaults.shift unless defaults.empty?\n  end\n\n  # @return [Hash{??? => Puppet::Provider}] Returns a hash of WHAT EXACTLY for the given type\n  # @todo what goes into this hash?\n  def self.provider_hash_by_type(type)\n    @provider_hashes ||= {}\n    @provider_hashes[type] ||= {}\n  end\n\n  # @return [Hash{ ??? => Puppet::Provider}] Returns a hash of WHAT EXACTLY for this type.\n  # @see provider_hash_by_type method to get the same for some other type\n  def self.provider_hash\n    Puppet::Type.provider_hash_by_type(name)\n  end\n\n  # Returns the provider having the given name.\n  # This will load a provider if it is not already loaded. The returned provider is the first found provider\n  # having the given name, where \"first found\" semantics is defined by the {providerloader} in use.\n  #\n  # @param name [String] the name of the provider to get\n  # @return [Puppet::Provider, nil] the found provider, or nil if no provider of the given name was found\n  #\n  def self.provider(name)\n    name = name.intern\n\n    # If we don't have it yet, try loading it.\n    @providerloader.load(name, Puppet.lookup(:current_environment)) unless provider_hash.has_key?(name)\n    provider_hash[name]\n  end\n\n  # Returns a list of loaded providers by name.\n  # This method will not load/search for available providers.\n  # @return [Array<String>] list of loaded provider names\n  #\n  def self.providers\n    provider_hash.keys\n  end\n\n  # Returns true if the given name is a reference to a provider and if this is a suitable provider for\n  # this type.\n  # @todo How does the provider know if it is suitable for the type? Is it just suitable for the platform/\n  #   environment where this method is executing?\n  # @param name [String] the name of the provider for which validity is checked\n  # @return [Boolean] true if the given name references a provider that is suitable\n  #\n  def self.validprovider?(name)\n    name = name.intern\n\n    provider_hash.has_key?(name) && provider_hash[name].suitable?\n  end\n\n  # Creates a new provider of a type.\n  # This method must be called directly on the type that it's implementing.\n  # @todo Fix Confusing Explanations!\n  #   Is this a new provider of a Type (metatype), or a provider of an instance of Type (a resource), or\n  #   a Provider (the implementation of a Type's behavior). CONFUSED. It calls magically named methods like\n  #   \"providify\" ...\n  # @param name [String, Symbol] the name of the WHAT? provider? type?\n  # @param options [Hash{Symbol => Object}] a hash of options, used by this method, and passed on to {#genclass}, (see\n  #   it for additional options to pass).\n  # @option options [Puppet::Provider] :parent the parent provider (what is this?)\n  # @option options [Puppet::Type] :resource_type the resource type, defaults to this type if unspecified\n  # @return [Puppet::Provider] a provider ???\n  # @raise [Puppet::DevError] when the parent provider could not be found.\n  #\n  def self.provide(name, options = {}, &block)\n    name = name.intern\n\n    if unprovide(name)\n      Puppet.debug { \"Reloading #{name} #{self.name} provider\" }\n    end\n\n    pname = options[:parent]\n    parent = if pname\n               options.delete(:parent)\n               if pname.is_a? Class\n                 pname\n               else\n                 provider = self.provider(pname)\n                 provider || raise(Puppet::DevError, _(\"Could not find parent provider %{parent} of %{name}\") % { parent: pname, name: name })\n               end\n             else\n               Puppet::Provider\n             end\n\n    options[:resource_type] ||= self\n\n    providify\n\n    genclass(\n      name,\n      :parent => parent,\n      :hash => provider_hash,\n      :prefix => \"Provider\",\n      :block => block,\n      :include => feature_module,\n      :extend => feature_module,\n      :attributes => options\n    )\n  end\n\n  # Ensures there is a `:provider` parameter defined.\n  # Should only be called if there are providers.\n  # @return [void]\n  def self.providify\n    return if @paramhash.has_key? :provider\n\n    param = newparam(:provider) do\n      # We're using a hacky way to get the name of our type, since there doesn't\n      # seem to be a correct way to introspect this at the time this code is run.\n      # We expect that the class in which this code is executed will be something\n      # like Puppet::Type::Ssh_authorized_key::ParameterProvider.\n      desc <<-EOT\n        The specific backend to use for this `#{to_s.split('::')[2].downcase}`\n        resource. You will seldom need to specify this --- Puppet will usually\n        discover the appropriate provider for your platform.\n      EOT\n\n      # This is so we can refer back to the type to get a list of\n      # providers for documentation.\n      class << self\n        # The reference to a parent type for the parameter `:provider` used to get a list of\n        # providers for documentation purposes.\n        #\n        attr_accessor :parenttype\n      end\n\n      # Provides the ability to add documentation to a provider.\n      #\n      def self.doc\n        # Since we're mixing @doc with text from other sources, we must normalize\n        # its indentation with scrub. But we don't need to manually scrub the\n        # provider's doc string, since markdown_definitionlist sanitizes its inputs.\n        scrub(@doc) + \"Available providers are:\\n\\n\" + parenttype.providers.sort_by(&:to_s).collect { |i|\n          markdown_definitionlist(i, scrub(parenttype().provider(i).doc))\n        }.join\n      end\n\n      # For each resource, the provider param defaults to\n      # the type's default provider\n      defaultto {\n        prov = @resource.class.defaultprovider\n        prov.name if prov\n      }\n\n      validate do |provider_class|\n        provider_class = provider_class[0] if provider_class.is_a? Array\n        provider_class = provider_class.class.name if provider_class.is_a?(Puppet::Provider)\n\n        unless @resource.class.provider(provider_class)\n          raise ArgumentError, _(\"Invalid %{resource} provider '%{provider_class}'\") % { resource: @resource.class.name, provider_class: provider_class }\n        end\n      end\n\n      munge do |provider|\n        provider = provider[0] if provider.is_a? Array\n        provider = provider.intern if provider.is_a? String\n        @resource.provider = provider\n\n        if provider.is_a?(Puppet::Provider)\n          provider.class.name\n        else\n          provider\n        end\n      end\n    end\n    param.parenttype = self\n  end\n\n  # @todo this needs a better explanation\n  # Removes the implementation class of a given provider.\n  # @return [Object] returns what {Puppet::Util::ClassGen#rmclass} returns\n  def self.unprovide(name)\n    if @defaultprovider and @defaultprovider.name == name\n      @defaultprovider = nil\n    end\n\n    rmclass(name, :hash => provider_hash, :prefix => \"Provider\")\n  end\n\n  # Returns a list of suitable providers for the given type.\n  # A call to this method will load all providers if not already loaded and ask each if it is\n  # suitable - those that are included in the result.\n  # @note This method also does some special processing which rejects a provider named `:fake` (for testing purposes).\n  # @return [Array<Puppet::Provider>] Returns an array of all suitable providers.\n  #\n  def self.suitableprovider\n    providerloader.loadall(Puppet.lookup(:current_environment)) if provider_hash.empty?\n    provider_hash.find_all { |_name, provider|\n      provider.suitable?\n    }.collect { |_name, provider|\n      provider\n    }.reject { |p| p.name == :fake } # For testing\n  end\n\n  # @return [Boolean] Returns true if this is something else than a `:provider`, or if it\n  #   is a provider and it is suitable, or if there is a default provider. Otherwise, false is returned.\n  #\n  def suitable?\n    # If we don't use providers, then we consider it suitable.\n    return true unless self.class.paramclass(:provider)\n\n    # We have a provider and it is suitable.\n    return true if provider && provider.class.suitable?\n\n    # We're using the default provider and there is one.\n    if !provider and self.class.defaultprovider\n      self.provider = self.class.defaultprovider.name\n      return true\n    end\n\n    # We specified an unsuitable provider, or there isn't any suitable\n    # provider.\n    false\n  end\n\n  # Sets the provider to the given provider/name.\n  # @overload provider=(name)\n  #   Sets the provider to the result of resolving the name to an instance of Provider.\n  #   @param name [String] the name of the provider\n  # @overload provider=(provider)\n  #   Sets the provider to the given instances of Provider.\n  #   @param provider [Puppet::Provider] the provider to set\n  # @return [Puppet::Provider] the provider set\n  # @raise [ArgumentError] if the provider could not be found/resolved.\n  #\n  def provider=(name)\n    if name.is_a?(Puppet::Provider)\n      @provider = name\n      @provider.resource = self\n    else\n      klass = self.class.provider(name)\n      if klass\n        @provider = klass.new(self)\n      else\n        raise ArgumentError, _(\"Could not find %{name} provider of %{provider}\") % { name: name, provider: self.class.name }\n      end\n    end\n  end\n\n  ###############################\n  # All of the relationship code.\n\n  # Adds a block producing a single name (or list of names) of the given\n  # resource type name to autorelate.\n  #\n  # The four relationship types require, before, notify, and subscribe are all\n  # supported.\n  #\n  # Be *careful* with notify and subscribe as they may have unintended\n  # consequences.\n  #\n  # Resources in the catalog that have the named type and a title that is\n  # included in the result will be linked to the calling resource as a\n  # requirement.\n  #\n  # @example Autorequire the files File['foo', 'bar']\n  #   autorequire( 'file', { ['foo', 'bar'] })\n  #\n  # @example Autobefore the files File['foo', 'bar']\n  #   autobefore( 'file', { ['foo', 'bar'] })\n  #\n  # @example Autosubscribe the files File['foo', 'bar']\n  #   autosubscribe( 'file', { ['foo', 'bar'] })\n  #\n  # @example Autonotify the files File['foo', 'bar']\n  #   autonotify( 'file', { ['foo', 'bar'] })\n  #\n  # @param name [String] the name of a type of which one or several resources should be autorelated e.g. \"file\"\n  # @yield [ ] a block returning list of names of given type to auto require\n  # @yieldreturn [String, Array<String>] one or several resource names for the named type\n  # @return [void]\n  # @dsl type\n  # @api public\n  #\n  def self.autorequire(name, &block)\n    @autorequires ||= {}\n    @autorequires[name] = block\n  end\n\n  def self.autobefore(name, &block)\n    @autobefores ||= {}\n    @autobefores[name] = block\n  end\n\n  def self.autosubscribe(name, &block)\n    @autosubscribes ||= {}\n    @autosubscribes[name] = block\n  end\n\n  def self.autonotify(name, &block)\n    @autonotifies ||= {}\n    @autonotifies[name] = block\n  end\n\n  # Provides iteration over added auto-requirements (see {autorequire}).\n  # @yieldparam type [String] the name of the type to autorequire an instance of\n  # @yieldparam block [Proc] a block producing one or several dependencies to auto require (see {autorequire}).\n  # @yieldreturn [void]\n  # @return [void]\n  def self.eachautorequire\n    @autorequires ||= {}\n    @autorequires.each { |type, block|\n      yield(type, block)\n    }\n  end\n\n  # Provides iteration over added auto-requirements (see {autobefore}).\n  # @yieldparam type [String] the name of the type to autorequire an instance of\n  # @yieldparam block [Proc] a block producing one or several dependencies to auto require (see {autobefore}).\n  # @yieldreturn [void]\n  # @return [void]\n  def self.eachautobefore\n    @autobefores ||= {}\n    @autobefores.each { |type, block|\n      yield(type, block)\n    }\n  end\n\n  # Provides iteration over added auto-requirements (see {autosubscribe}).\n  # @yieldparam type [String] the name of the type to autorequire an instance of\n  # @yieldparam block [Proc] a block producing one or several dependencies to auto require (see {autosubscribe}).\n  # @yieldreturn [void]\n  # @return [void]\n  def self.eachautosubscribe\n    @autosubscribes ||= {}\n    @autosubscribes.each { |type, block|\n      yield(type, block)\n    }\n  end\n\n  # Provides iteration over added auto-requirements (see {autonotify}).\n  # @yieldparam type [String] the name of the type to autorequire an instance of\n  # @yieldparam block [Proc] a block producing one or several dependencies to auto require (see {autonotify}).\n  # @yieldreturn [void]\n  # @return [void]\n  def self.eachautonotify\n    @autonotifies ||= {}\n    @autonotifies.each { |type, block|\n      yield(type, block)\n    }\n  end\n\n  # Adds dependencies to the catalog from added autorelations.\n  # See {autorequire} for how to add an auto-requirement.\n  # @todo needs details - see the param rel_catalog, and type of this param\n  # @param rel_catalog [Puppet::Resource::Catalog, nil] the catalog to\n  #   add dependencies to. Defaults to the current catalog (set when the\n  #   type instance was added to a catalog)\n  # @raise [Puppet::DevError] if there is no catalog\n  #\n  def autorelation(rel_type, rel_catalog = nil)\n    rel_catalog ||= catalog\n    raise Puppet::DevError, _(\"You cannot add relationships without a catalog\") unless rel_catalog\n\n    reqs = []\n\n    auto_rel = \"eachauto#{rel_type}\".to_sym\n\n    self.class.send(auto_rel) { |type, block|\n      # Ignore any types we can't find, although that would be a bit odd.\n      next unless Puppet::Type.type(type)\n\n      # Retrieve the list of names from the block.\n      list = instance_eval(&block)\n      next unless list\n\n      list = [list] unless list.is_a?(Array)\n\n      # Collect the current prereqs\n      list.each { |dep|\n        next if dep.nil?\n\n        # Support them passing objects directly, to save some effort.\n        unless dep.is_a?(Puppet::Type)\n          # Skip autorelation that we aren't managing\n          dep = rel_catalog.resource(type, dep)\n          next unless dep\n        end\n\n        if [:require, :subscribe].include?(rel_type)\n          reqs << Puppet::Relationship.new(dep, self)\n        else\n          reqs << Puppet::Relationship.new(self, dep)\n        end\n      }\n    }\n    reqs\n  end\n\n  def autorequire(rel_catalog = nil)\n    autorelation(:require, rel_catalog)\n  end\n\n  def autobefore(rel_catalog = nil)\n    autorelation(:before, rel_catalog)\n  end\n\n  def autosubscribe(rel_catalog = nil)\n    autorelation(:subscribe, rel_catalog)\n  end\n\n  def autonotify(rel_catalog = nil)\n    autorelation(:notify, rel_catalog)\n  end\n\n  # Builds the dependencies associated with this resource.\n  #\n  # @return [Array<Puppet::Relationship>] list of relationships to other resources\n  def builddepends\n    # Handle the requires\n    self.class.relationship_params.collect do |klass|\n      param = @parameters[klass.name]\n      param.to_edges if param\n    end.flatten.compact\n  end\n\n  # Sets the initial list of tags to associate to this resource.\n  #\n  # @return [void] ???\n  def tags=(list)\n    tag(self.class.name)\n    tag(*list)\n  end\n\n  # @comment - these two comments were floating around here, and turned up as documentation\n  #  for the attribute \"title\", much to my surprise and amusement. Clearly these comments\n  #  are orphaned ... I think they can just be removed as what they say should be covered\n  #  by the now added yardoc. <irony>(Yo! to quote some of the other actual awesome specific comments applicable\n  #  to objects called from elsewhere, or not. ;-)</irony>\n  #\n  # @comment Types (which map to resources in the languages) are entirely composed of\n  #   attribute value pairs.  Generally, Puppet calls any of these things an\n  #   'attribute', but these attributes always take one of three specific\n  #   forms:  parameters, metaparams, or properties.\n\n  # @comment In naming methods, I have tried to consistently name the method so\n  #   that it is clear whether it operates on all attributes (thus has 'attr' in\n  #   the method name, or whether it operates on a specific type of attributes.\n\n  # The title attribute of WHAT ???\n  # @todo Figure out what this is the title attribute of (it appears on line 1926 currently).\n  # @return [String] the title\n  attr_writer :title\n\n  # The noop attribute of WHAT ??? does WHAT???\n  # @todo Figure out what this is the noop attribute of (it appears on line 1931 currently).\n  # @return [???] the noop WHAT ??? (mode? if so of what, or noop for an instance of the type, or for all\n  #   instances of a type, or for what???\n  #\n  attr_writer :noop\n\n  include Enumerable\n\n  # class methods dealing with Type management\n\n  # The Type class attribute accessors\n  class << self\n    # @return [String] the name of the resource type; e.g., \"File\"\n    #\n    attr_reader :name\n\n    # @return [Boolean] true if the type should send itself a refresh event on change.\n    #\n    attr_accessor :self_refresh\n\n    include Enumerable, Puppet::Util::ClassGen\n\n    include Puppet::Util\n    include Puppet::Util::Logging\n  end\n\n  # Initializes all of the variables that must be initialized for each subclass.\n  # @todo Does the explanation make sense?\n  # @return [void]\n  def self.initvars\n    # all of the instances of this class\n    @objects = Hash.new\n    @aliases = Hash.new\n\n    @defaults = {}\n\n    @parameters ||= []\n\n    @validproperties = {}\n    @properties = []\n    @parameters = []\n    @paramhash = {}\n\n    @paramdoc = Hash.new { |hash, key|\n      key = key.intern if key.is_a?(String)\n      if hash.include?(key)\n        hash[key]\n      else\n        \"Param Documentation for #{key} not found\"\n      end\n    }\n    # rubocop:disable Naming/MemoizedInstanceVariableName\n    @doc ||= \"\"\n    # rubocop:enable Naming/MemoizedInstanceVariableName\n  end\n\n  # Returns the name of this type (if specified) or the parent type #to_s.\n  # The returned name is on the form \"Puppet::Type::<name>\", where the first letter of name is\n  # capitalized.\n  # @return [String] the fully qualified name Puppet::Type::<name> where the first letter of name is capitalized\n  #\n  def self.to_s\n    if defined?(@name)\n      \"Puppet::Type::#{@name.to_s.capitalize}\"\n    else\n      super\n    end\n  end\n\n  # Creates a `validate` method that is used to validate a resource before it is operated on.\n  # The validation should raise exceptions if the validation finds errors. (It is not recommended to\n  # issue warnings as this typically just ends up in a logfile - you should fail if a validation fails).\n  # The easiest way to raise an appropriate exception is to call the method {Puppet::Util::Errors.fail} with\n  # the message as an argument.\n  #\n  # @yield [ ] a required block called with self set to the instance of a Type class representing a resource.\n  # @return [void]\n  # @dsl type\n  # @api public\n  #\n  def self.validate(&block)\n    define_method(:unsafe_validate, &block)\n\n    define_method(:validate) do\n      return if enum_for(:eachparameter).any? { |p| p.value.instance_of?(Puppet::Pops::Evaluator::DeferredValue) }\n\n      unsafe_validate\n    end\n  end\n\n  # @return [String] The file from which this type originates from\n  attr_accessor :file\n\n  # @return [Integer] The line in {#file} from which this type originates from\n  attr_accessor :line\n\n  # @todo what does this mean \"this resource\" (sounds like this if for an instance of the type, not the meta Type),\n  #   but not sure if this is about the catalog where the meta Type is included)\n  # @return [??? TODO] The catalog that this resource is stored in.\n  attr_accessor :catalog\n\n  # @return [Boolean] Flag indicating if this type is exported\n  attr_accessor :exported\n\n  # @return [Boolean] Flag indicating if the type is virtual (it should not be).\n  attr_accessor :virtual\n\n  # Creates a log entry with the given message at the log level specified by the parameter `loglevel`\n  # @return [void]\n  #\n  def log(msg)\n    Puppet::Util::Log.create(\n      :level => @parameters[:loglevel].value,\n      :message => msg,\n\n      :source => self\n    )\n  end\n\n  # instance methods related to instance intrinsics\n  # e.g., initialize and name\n\n  # @return [Hash] hash of parameters originally defined\n  # @api private\n  attr_reader :original_parameters\n\n  # Creates an instance of Type from a hash or a {Puppet::Resource}.\n  # @todo Unclear if this is a new Type or a new instance of a given type (the initialization ends\n  #   with calling validate - which seems like validation of an instance of a given type, not a new\n  #   meta type.\n  #\n  # @todo Explain what the Hash and Resource are. There seems to be two different types of\n  #   resources; one that causes the title to be set to resource.title, and one that\n  #   causes the title to be resource.ref (\"for components\") - what is a component?\n  #\n  # @overload initialize(hash)\n  #   @param [Hash] hash\n  #   @raise [Puppet::ResourceError] when the type validation raises\n  #     Puppet::Error or ArgumentError\n  # @overload initialize(resource)\n  #   @param resource [Puppet:Resource]\n  #   @raise [Puppet::ResourceError] when the type validation raises\n  #     Puppet::Error or ArgumentError\n  #\n  def initialize(resource)\n    resource = self.class.hash2resource(resource) unless resource.is_a?(Puppet::Resource)\n\n    # The list of parameter/property instances.\n    @parameters = {}\n\n    # Set the title first, so any failures print correctly.\n    if resource.type.to_s.downcase.to_sym == self.class.name\n      self.title = resource.title\n    else\n      # This should only ever happen for components\n      self.title = resource.ref\n    end\n\n    [:file, :line, :catalog, :exported, :virtual].each do |getter|\n      setter = getter.to_s + \"=\"\n      val = resource.send(getter)\n      send(setter, val) if val\n    end\n\n    merge_tags_from(resource)\n\n    @original_parameters = resource.to_hash\n\n    set_name(@original_parameters)\n\n    set_default(:provider)\n\n    set_parameters(@original_parameters)\n\n    validate_resource\n\n    set_sensitive_parameters(resource.sensitive_parameters)\n  end\n\n  # Optionally validate the resource. This method is a noop if the type has not defined\n  # a `validate` method using the puppet DSL. If validation fails, then an exception will\n  # be raised with this resources as the context.\n  #\n  # @api public\n  #\n  # @return [void]\n  def validate_resource\n    validate if respond_to?(:validate)\n  rescue Puppet::Error, ArgumentError => detail\n    error = Puppet::ResourceError.new(\"Validation of #{ref} failed: #{detail}\")\n    adderrorcontext(error, detail)\n    raise error\n  end\n\n  protected\n\n  # Mark parameters associated with this type as sensitive, based on the associated resource.\n  #\n  # Currently, only instances of `Puppet::Property` can be easily marked for sensitive data handling\n  # and information redaction is limited to redacting events generated while synchronizing\n  # properties. While support for redaction will be broadened in the future we can't automatically\n  # deduce how to redact arbitrary parameters, so if a parameter is marked for redaction the best\n  # we can do is warn that we can't handle treating that parameter as sensitive and move on.\n  #\n  # In some unusual cases a given parameter will be marked as sensitive but that sensitive context\n  # needs to be transferred to another parameter. In this case resource types may need to override\n  # this method in order to copy the sensitive context from one parameter to another (and in the\n  # process force the early generation of a parameter that might otherwise be lazily generated.)\n  # See `Puppet::Type.type(:file)#set_sensitive_parameters` for an example of this.\n  #\n  # @note This method visibility is protected since it should only be called by #initialize, but is\n  #   marked as public as subclasses may need to override this method.\n  #\n  # @api public\n  #\n  # @param sensitive_parameters [Array<Symbol>] A list of parameters to mark as sensitive.\n  #\n  # @return [void]\n  def set_sensitive_parameters(sensitive_parameters)\n    sensitive_parameters.each do |name|\n      p = parameter(name)\n      if p.is_a?(Puppet::Property)\n        p.sensitive = true\n      elsif p.is_a?(Puppet::Parameter)\n        warning(_(\"Unable to mark '%{name}' as sensitive: %{name} is a parameter and not a property, and cannot be automatically redacted.\") %\n                    { name: name })\n      elsif self.class.attrclass(name)\n        warning(_(\"Unable to mark '%{name}' as sensitive: the property itself was not assigned a value.\") % { name: name })\n      else\n        err(_(\"Unable to mark '%{name}' as sensitive: the property itself is not defined on %{type}.\") % { name: name, type: type })\n      end\n    end\n\n    parameters.each do |_name, param|\n      next if param.sensitive\n\n      if param.is_a?(Puppet::Parameter)\n        param.sensitive = param.is_sensitive if param.respond_to?(:is_sensitive)\n      end\n    end\n  end\n\n  private\n\n  # Sets the name of the resource from a hash containing a mapping of `name_var` to value.\n  # Sets the value of the property/parameter appointed by the `name_var` (if it is defined). The value set is\n  # given by the corresponding entry in the given hash - e.g. if name_var appoints the name `:path` the value\n  # of `:path` is set to the value at the key `:path` in the given hash. As a side effect this key/value is then\n  # removed from the given hash.\n  #\n  # @note This method mutates the given hash by removing the entry with a key equal to the value\n  #   returned from name_var!\n  # @param hash [Hash] a hash of what\n  # @return [void]\n  def set_name(hash)\n    self[name_var] = hash.delete(name_var) if name_var\n  end\n\n  # Sets parameters from the given hash.\n  # Values are set in _attribute order_ i.e. higher priority attributes before others, otherwise in\n  # the order they were specified (as opposed to just setting them in the order they happen to appear in\n  # when iterating over the given hash).\n  #\n  # Attributes that are not included in the given hash are set to their default value.\n  #\n  # @todo Is this description accurate? Is \"ensure\" an example of such a higher priority attribute?\n  # @return [void]\n  # @raise [Puppet::DevError] when impossible to set the value due to some problem\n  # @raise [ArgumentError, TypeError, Puppet::Error] when faulty arguments have been passed\n  #\n  def set_parameters(hash)\n    # Use the order provided by allattrs, but add in any\n    # extra attributes from the resource so we get failures\n    # on invalid attributes.\n    no_values = []\n    (self.class.allattrs + hash.keys).uniq.each do |attr|\n      # Set any defaults immediately.  This is mostly done so\n      # that the default provider is available for any other\n      # property validation.\n      if hash.has_key?(attr)\n        self[attr] = hash[attr]\n      else\n        no_values << attr\n      end\n    rescue ArgumentError, Puppet::Error, TypeError\n      raise\n    rescue => detail\n      error = Puppet::DevError.new(_(\"Could not set %{attribute} on %{class_name}: %{detail}\") % { attribute: attr, class_name: self.class.name, detail: detail })\n      error.set_backtrace(detail.backtrace)\n      raise error\n    end\n    no_values.each do |attr|\n      set_default(attr)\n    end\n  end\n\n  public\n\n  # Finishes any outstanding processing.\n  # This method should be called as a final step in setup,\n  # to allow the parameters that have associated auto-require needs to be processed.\n  #\n  # @todo what is the expected sequence here - who is responsible for calling this? When?\n  #   Is the returned type correct?\n  # @return [Array<Puppet::Parameter>] the validated list/set of attributes\n  #\n  def finish\n    # Call post_compile hook on every parameter that implements it. This includes all subclasses\n    # of parameter including, but not limited to, regular parameters, metaparameters, relationship\n    # parameters, and properties.\n    eachparameter do |parameter|\n      parameter.post_compile if parameter.respond_to? :post_compile\n    end\n\n    # Make sure all of our relationships are valid.  Again, must be done\n    # when the entire catalog is instantiated.\n    self.class.relationship_params.collect do |klass|\n      param = @parameters[klass.name]\n      param.validate_relationship if param\n    end.flatten.compact\n  end\n\n  # @comment For now, leave the 'name' method functioning like it used to.  Once 'title'\n  #   works everywhere, I'll switch it.\n  # Returns the resource's name\n  # @todo There is a comment in source that this is not quite the same as ':title' and that a switch should\n  #   be made...\n  # @return [String] the name of a resource\n  def name\n    self[:name]\n  end\n\n  # Returns the parent of this in the catalog.  In case of an erroneous catalog\n  # where multiple parents have been produced, the first found (non\n  # deterministic) parent is returned.\n  # @return [Puppet::Type, nil] the\n  #   containing resource or nil if there is no catalog or no containing\n  #   resource.\n  def parent\n    return nil unless catalog\n    return @parent if @parent\n\n    parents = catalog.adjacent(self, :direction => :in)\n    @parent = if parents\n                parents.shift\n              else\n                nil\n              end\n  end\n\n  # Returns a reference to this as a string in \"Type[name]\" format.\n  # @return [String] a reference to this object on the form 'Type[name]'\n  #\n  def ref\n    # memoizing this is worthwhile ~ 3 percent of calls are the \"first time\n    # around\" in an average run of Puppet. --daniel 2012-07-17\n    @ref ||= \"#{self.class.name.to_s.capitalize}[#{title}]\"\n  end\n\n  # (see self_refresh)\n  # @todo check that meaningful yardoc is produced - this method delegates to \"self.class.self_refresh\"\n  # @return [Boolean] - ??? returns true when ... what?\n  #\n  def self_refresh?\n    self.class.self_refresh\n  end\n\n  # Marks the object as \"being purged\".\n  # This method is used by transactions to forbid deletion when there are dependencies.\n  # @todo what does this mean; \"mark that we are purging\" (purging what from where). How to use/when?\n  #   Is this internal API in transactions?\n  # @see purging?\n  def purging\n    @purging = true\n  end\n\n  # Returns whether this resource is being purged or not.\n  # This method is used by transactions to forbid deletion when there are dependencies.\n  # @return [Boolean] the current \"purging\" state\n  #\n  def purging?\n    if defined?(@purging)\n      @purging\n    else\n      false\n    end\n  end\n\n  # Returns the title of this object, or its name if title was not explicitly set.\n  # If the title is not already set, it will be computed by looking up the {#name_var} and using\n  # that value as the title.\n  # @todo it is somewhat confusing that if the name_var is a valid parameter, it is assumed to\n  #  be the name_var called :name, but if it is a property, it uses the name_var.\n  #  It is further confusing as Type in some respects supports multiple namevars.\n  #\n  # @return [String] Returns the title of this object, or its name if title was not explicitly set.\n  # @raise [??? devfail] if title is not set, and name_var can not be found.\n  def title\n    unless @title\n      if self.class.validparameter?(name_var)\n        @title = self[:name]\n      elsif self.class.validproperty?(name_var)\n        @title = should(name_var)\n      else\n        devfail \"Could not find namevar #{name_var} for #{self.class.name}\"\n      end\n    end\n\n    @title\n  end\n\n  # Produces a reference to this in reference format.\n  # @see #ref\n  #\n  def to_s\n    ref\n  end\n\n  # Convert this resource type instance to a Puppet::Resource.\n  # @return [Puppet::Resource] Returns a serializable representation of this resource\n  #\n  def to_resource\n    resource = retrieve_resource\n    resource.merge_tags_from(self)\n\n    @parameters.each do |name, param|\n      # Avoid adding each instance name twice\n      next if param.class.isnamevar? and param.value == title\n\n      # We've already got property values\n      next if param.is_a?(Puppet::Property)\n\n      resource[name] = param.value\n    end\n\n    resource\n  end\n\n  # @return [Boolean] Returns whether the resource is virtual or not\n  def virtual?;  !!@virtual;  end\n  # @return [Boolean] Returns whether the resource is exported or not\n  def exported?; !!@exported; end\n\n  # @return [Boolean] Returns whether the resource is applicable to `:device`\n  # Returns true if a resource of this type can be evaluated on a 'network device' kind\n  # of hosts.\n  # @api private\n  def appliable_to_device?\n    self.class.can_apply_to(:device)\n  end\n\n  # @return [Boolean] Returns whether the resource is applicable to `:host`\n  # Returns true if a resource of this type can be evaluated on a regular generalized computer (ie not an appliance like a network device)\n  # @api private\n  def appliable_to_host?\n    self.class.can_apply_to(:host)\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/util/at_fork/noop.rb",
    "content": "# frozen_string_literal: true\n\n# A noop implementation of the Puppet::Util::AtFork handler\nclass Puppet::Util::AtFork::Noop\n  class << self\n    def new\n      # no need to instantiate every time, return the class object itself\n      self\n    end\n\n    def prepare\n    end\n\n    def parent\n    end\n\n    def child\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/at_fork/solaris.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet'\nrequire 'fiddle'\n\n# Early versions of Fiddle relied on the deprecated DL module and used\n# classes defined in the namespace of that module instead of classes defined\n# in the Fiddle's own namespace e.g. DL::Handle instead of Fiddle::Handle.\n# We don't support those.\nraise LoadError, _('The loaded Fiddle version is not supported.') unless defined?(Fiddle::Handle)\n\n# Solaris implementation of the Puppet::Util::AtFork handler.\n# The callbacks defined in this implementation ensure the forked process runs\n# in a different contract than the parent process. This is necessary in order\n# for the child process to be able to survive termination of the contract its\n# parent process runs in. This is needed notably for an agent run executed\n# by a puppet agent service to be able to restart that service without being\n# killed in the process as a consequence of running in the same contract as\n# the service, as all processes in the contract are killed when the contract\n# is terminated during the service restart.\nclass Puppet::Util::AtFork::Solaris\n  private\n\n  {\n    'libcontract.so.1' => [\n      # function name,            return value type, parameter types, ...\n      [:ct_ctl_abandon,          Fiddle::TYPE_INT,  Fiddle::TYPE_INT],\n      [:ct_tmpl_activate,        Fiddle::TYPE_INT,  Fiddle::TYPE_INT],\n      [:ct_tmpl_clear,           Fiddle::TYPE_INT,  Fiddle::TYPE_INT],\n\n      [:ct_tmpl_set_informative, Fiddle::TYPE_INT,  Fiddle::TYPE_INT, Fiddle::TYPE_INT],\n      [:ct_tmpl_set_critical,    Fiddle::TYPE_INT,  Fiddle::TYPE_INT, Fiddle::TYPE_INT],\n      [:ct_pr_tmpl_set_param,    Fiddle::TYPE_INT,  Fiddle::TYPE_INT, Fiddle::TYPE_INT],\n      [:ct_pr_tmpl_set_fatal,    Fiddle::TYPE_INT,  Fiddle::TYPE_INT, Fiddle::TYPE_INT],\n\n      [:ct_status_read,          Fiddle::TYPE_INT,  Fiddle::TYPE_INT, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP],\n\n      [:ct_status_get_id,        Fiddle::TYPE_INT,  Fiddle::TYPE_VOIDP],\n      [:ct_status_free,          Fiddle::TYPE_VOID, Fiddle::TYPE_VOIDP]\n    ],\n  }.each do |library, functions|\n    libhandle = Fiddle::Handle.new(library)\n\n    functions.each do |f|\n      define_method f[0], Fiddle::Function.new(libhandle[f[0].to_s], f[2..], f[1]).method(:call).to_proc\n    end\n  end\n\n  CTFS_PR_ROOT = File.join('', %w[system contract process])\n  CTFS_PR_TEMPLATE = File.join(CTFS_PR_ROOT, 'template')\n  CTFS_PR_LATEST = File.join(CTFS_PR_ROOT, 'latest')\n\n  CT_PR_PGRPONLY = 0x4\n  CT_PR_EV_HWERR = 0x20\n\n  CTD_COMMON = 0\n\n  def raise_if_error(&block)\n    unless (e = yield) == 0\n      e = SystemCallError.new(nil, e)\n      raise e, e.message, caller\n    end\n  end\n\n  def activate_new_contract_template\n    tmpl = File.new(CTFS_PR_TEMPLATE, File::RDWR)\n\n    begin\n      tmpl_fd = tmpl.fileno\n\n      raise_if_error { ct_pr_tmpl_set_param(tmpl_fd, CT_PR_PGRPONLY) }\n      raise_if_error { ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR) }\n      raise_if_error { ct_tmpl_set_critical(tmpl_fd, 0) }\n      raise_if_error { ct_tmpl_set_informative(tmpl_fd, CT_PR_EV_HWERR) }\n\n      raise_if_error { ct_tmpl_activate(tmpl_fd) }\n    rescue\n      tmpl.close\n      raise\n    end\n\n    @tmpl = tmpl\n  rescue => detail\n    Puppet.log_exception(detail, _('Failed to activate a new process contract template'))\n  end\n\n  def deactivate_contract_template(parent)\n    return if @tmpl.nil?\n\n    tmpl = @tmpl\n    @tmpl = nil\n\n    begin\n      raise_if_error { ct_tmpl_clear(tmpl.fileno) }\n    rescue => detail\n      msg = if parent\n              _('Failed to deactivate process contract template in the parent process')\n            else\n              _('Failed to deactivate process contract template in the child process')\n            end\n      Puppet.log_exception(detail, msg)\n      exit(1)\n    ensure\n      tmpl.close\n    end\n  end\n\n  def get_latest_child_contract_id\n    stat = File.new(CTFS_PR_LATEST, File::RDONLY)\n\n    begin\n      stathdl = Fiddle::Pointer.new(0)\n\n      raise_if_error { ct_status_read(stat.fileno, CTD_COMMON, stathdl.ref) }\n      ctid = ct_status_get_id(stathdl)\n      ct_status_free(stathdl)\n    ensure\n      stat.close\n    end\n\n    ctid\n  rescue => detail\n    Puppet.log_exception(detail, _('Failed to get latest child process contract id'))\n    nil\n  end\n\n  def abandon_latest_child_contract\n    ctid = get_latest_child_contract_id\n    return if ctid.nil?\n\n    begin\n      ctl = File.new(File.join(CTFS_PR_ROOT, ctid.to_s, 'ctl'), File::WRONLY)\n\n      begin\n        raise_if_error { ct_ctl_abandon(ctl.fileno) }\n      ensure\n        ctl.close\n      end\n    rescue => detail\n      Puppet.log_exception(detail, _('Failed to abandon a child process contract'))\n    end\n  end\n\n  public\n\n  def prepare\n    activate_new_contract_template\n  end\n\n  def parent\n    deactivate_contract_template(true)\n    abandon_latest_child_contract\n  end\n\n  def child\n    deactivate_contract_template(false)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/at_fork.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\n\n# A module for building AtFork handlers. These handlers are objects providing\n# pre/post fork callbacks modeled after those registered by the `pthread_atfork`\n# function.\n# Currently there are two AtFork handler implementations:\n# - a noop implementation used on all platforms except Solaris (and possibly\n#   even there as a fallback)\n# - a Solaris implementation which ensures the forked process runs in a different\n#   contract than the parent process. This is necessary for agent runs started by\n#   the puppet agent service to be able to restart that service without being\n#   killed in the process as a consequence of running in the same contract as the\n#   service.\nmodule Puppet::Util::AtFork\n  @handler_class = loop do # rubocop:disable Lint/UnreachableLoop\n    if Puppet::Util::Platform.solaris?\n      begin\n        require_relative 'at_fork/solaris'\n        # using break to return a value from the loop block\n        break Puppet::Util::AtFork::Solaris\n      rescue LoadError => detail\n        Puppet.log_exception(detail, _('Failed to load Solaris implementation of the Puppet::Util::AtFork handler. Child process contract management will be unavailable, which means that agent runs executed by the puppet agent service will be killed when they attempt to restart the service.'))\n        # fall through to use the no-op implementation\n      end\n    end\n\n    require_relative 'at_fork/noop'\n    # using break to return a value from the loop block\n    break Puppet::Util::AtFork::Noop\n  end\n\n  def self.get_handler\n    @handler_class.new\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/autoload.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire_relative '../../puppet/util/rubygems'\nrequire_relative '../../puppet/util/warnings'\nrequire_relative '../../puppet/pops/adaptable'\nrequire_relative '../../puppet/concurrent/synchronized'\n\n# An adapter that ties the module_directories cache to the environment where the modules are parsed. This\n# adapter ensures that the life-cycle of this cache doesn't exceed  the life-cycle of the environment.\n#\n# @api private\nclass Puppet::Util::ModuleDirectoriesAdapter < Puppet::Pops::Adaptable::Adapter\n  attr_accessor :directories\n\n  def self.create_adapter(env)\n    adapter = super(env)\n    adapter.directories = env.modulepath.flat_map do |dir|\n      Dir.glob(File.join(dir, '*', 'lib'))\n    end\n    adapter\n  end\nend\n\n# Autoload paths, either based on names or all at once.\nclass Puppet::Util::Autoload\n  include Puppet::Concurrent::Synchronized\n  extend Puppet::Concurrent::Synchronized\n\n  @loaded = {}\n\n  class << self\n    attr_accessor :loaded\n\n    def gem_source\n      @gem_source ||= Puppet::Util::RubyGems::Source.new\n    end\n\n    # Has a given path been loaded?  This is used for testing whether a\n    # changed file should be loaded or just ignored.  This is only\n    # used in network/client/master, when downloading plugins, to\n    # see if a given plugin is currently loaded and thus should be\n    # reloaded.\n    def loaded?(path)\n      path = cleanpath(path).chomp('.rb')\n      loaded.include?(path)\n    end\n\n    # Save the fact that a given path has been loaded.  This is so\n    # we can load downloaded plugins if they've already been loaded\n    # into memory.\n    # @api private\n    def mark_loaded(name, file)\n      name = cleanpath(name).chomp('.rb')\n      file = File.expand_path(file)\n      $LOADED_FEATURES << file unless $LOADED_FEATURES.include?(file)\n      loaded[name] = [file, File.mtime(file)]\n    end\n\n    # @api private\n    def changed?(name, env)\n      name = cleanpath(name).chomp('.rb')\n      return true unless loaded.include?(name)\n\n      file, old_mtime = loaded[name]\n      return true unless file == get_file(name, env)\n\n      begin\n        old_mtime.to_i != File.mtime(file).to_i\n      rescue Errno::ENOENT\n        true\n      end\n    end\n\n    # Load a single plugin by name.  We use 'load' here so we can reload a\n    # given plugin.\n    def load_file(name, env)\n      file = get_file(name.to_s, env)\n      return false unless file\n\n      begin\n        mark_loaded(name, file)\n        Kernel.load file\n        true\n      rescue SystemExit, NoMemoryError\n        raise\n      rescue Exception => detail\n        message = _(\"Could not autoload %{name}: %{detail}\") % { name: name, detail: detail }\n        Puppet.log_exception(detail, message)\n        raise Puppet::Error, message, detail.backtrace\n      end\n    end\n\n    def loadall(path, env)\n      # Load every instance of everything we can find.\n      files_to_load(path, env).each do |file|\n        name = file.chomp(\".rb\")\n        load_file(name, env) unless loaded?(name)\n      end\n    end\n\n    def reload_changed(env)\n      loaded.keys.each do |file|\n        if changed?(file, env)\n          load_file(file, env)\n        end\n      end\n    end\n\n    # Get the correct file to load for a given path\n    # returns nil if no file is found\n    # @api private\n    def get_file(name, env)\n      name += '.rb' unless name =~ /\\.rb$/\n      path = search_directories(env).find { |dir| Puppet::FileSystem.exist?(File.join(dir, name)) }\n      path and File.join(path, name)\n    end\n\n    def files_to_load(path, env)\n      search_directories(env).map { |dir| files_in_dir(dir, path) }.flatten.uniq\n    end\n\n    # @api private\n    def files_in_dir(dir, path)\n      dir = Pathname.new(Puppet::FileSystem.expand_path(dir))\n      Dir.glob(File.join(dir, path, \"*.rb\")).collect do |file|\n        Pathname.new(file).relative_path_from(dir).to_s\n      end\n    end\n\n    # @api private\n    def module_directories(env)\n      raise ArgumentError, \"Autoloader requires an environment\" unless env\n\n      Puppet::Util::ModuleDirectoriesAdapter.adapt(env).directories\n    end\n\n    # @api private\n    def gem_directories\n      gem_source.directories\n    end\n\n    # @api private\n    def search_directories(env)\n      # This is a little bit of a hack.  Basically, the autoloader is being\n      # called indirectly during application bootstrapping when we do things\n      # such as check \"features\".  However, during bootstrapping, we haven't\n      # yet parsed all of the command line parameters nor the config files,\n      # and thus we don't yet know with certainty what the module path is.\n      # This should be irrelevant during bootstrapping, because anything that\n      # we are attempting to load during bootstrapping should be something\n      # that we ship with puppet, and thus the module path is irrelevant.\n      #\n      # In the long term, I think the way that we want to handle this is to\n      # have the autoloader ignore the module path in all cases where it is\n      # not specifically requested (e.g., by a constructor param or\n      # something)... because there are very few cases where we should\n      # actually be loading code from the module path.  However, until that\n      # happens, we at least need a way to prevent the autoloader from\n      # attempting to access the module path before it is initialized.  For\n      # now we are accomplishing that by calling the\n      # \"app_defaults_initialized?\" method on the main puppet Settings object.\n      # --cprice 2012-03-16\n      if Puppet.settings.app_defaults_initialized?\n        gem_directories + module_directories(env) + $LOAD_PATH\n      else\n        gem_directories + $LOAD_PATH\n      end\n    end\n\n    # Normalize a path. This converts ALT_SEPARATOR to SEPARATOR on Windows\n    # and eliminates unnecessary parts of a path.\n    def cleanpath(path)\n      Pathname.new(path).cleanpath.to_s\n    end\n  end\n\n  attr_accessor :object, :path\n\n  def initialize(obj, path)\n    @path = path.to_s\n    raise ArgumentError, _(\"Autoload paths cannot be fully qualified\") if Puppet::Util.absolute_path?(@path)\n\n    @object = obj\n  end\n\n  def load(name, env)\n    self.class.load_file(expand(name), env)\n  end\n\n  # Load all instances from a path of Autoload.search_directories matching the\n  # relative path this Autoloader was initialized with.  For example, if we\n  # have created a Puppet::Util::Autoload for Puppet::Type::User with a path of\n  # 'puppet/provider/user', the search_directories path will be searched for\n  # all ruby files matching puppet/provider/user/*.rb and they will then be\n  # loaded from the first directory in the search path providing them.  So\n  # earlier entries in the search path may shadow later entries.\n  #\n  # This uses require, rather than load, so that already-loaded files don't get\n  # reloaded unnecessarily.\n  def loadall(env)\n    self.class.loadall(@path, env)\n  end\n\n  def loaded?(name)\n    self.class.loaded?(expand(name))\n  end\n\n  # @api private\n  def changed?(name, env)\n    self.class.changed?(expand(name), env)\n  end\n\n  def files_to_load(env)\n    self.class.files_to_load(@path, env)\n  end\n\n  def expand(name)\n    ::File.join(@path, name.to_s)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/backups.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'find'\nrequire 'fileutils'\nmodule Puppet::Util::Backups\n  # Deal with backups.\n  def perform_backup(file = nil)\n    # if they specifically don't want a backup, then just say\n    # we're good\n    return true unless self[:backup]\n\n    # let the path be specified\n    file ||= self[:path]\n    return true unless Puppet::FileSystem.exist?(file)\n\n    (bucket ? perform_backup_with_bucket(file) : perform_backup_with_backuplocal(file, self[:backup]))\n  end\n\n  private\n\n  def perform_backup_with_bucket(fileobj)\n    file = fileobj.instance_of?(String) ? fileobj : fileobj.name\n    case Puppet::FileSystem.lstat(file).ftype\n    when \"directory\"\n      # we don't need to backup directories when recurse is on\n      return true if self[:recurse]\n\n      info _(\"Recursively backing up to filebucket\")\n      Find.find(self[:path]) { |f| backup_file_with_filebucket(f) if File.file?(f) }\n    when \"file\"; backup_file_with_filebucket(file)\n    when \"link\"; # do nothing\n    end\n    true\n  end\n\n  def perform_backup_with_backuplocal(fileobj, backup)\n    file = fileobj.instance_of?(String) ? fileobj : fileobj.name\n    newfile = file + backup\n\n    remove_backup(newfile)\n\n    begin\n      bfile = file + backup\n\n      # N.B. cp_r works on both files and directories\n      FileUtils.cp_r(file, bfile, :preserve => true)\n      true\n    rescue => detail\n      # since they said they want a backup, let's error out\n      # if we couldn't make one\n      self.fail Puppet::Error, _(\"Could not back %{file} up: %{message}\") % { file: file, message: detail.message }, detail\n    end\n  end\n\n  def remove_backup(newfile)\n    if instance_of?(Puppet::Type::File) and self[:links] != :follow\n      method = :lstat\n    else\n      method = :stat\n    end\n\n    begin\n      stat = Puppet::FileSystem.send(method, newfile)\n    rescue Errno::ENOENT\n      return\n    end\n\n    if stat.ftype == \"directory\"\n      raise Puppet::Error, _(\"Will not remove directory backup %{newfile}; use a filebucket\") % { newfile: newfile }\n    end\n\n    info _(\"Removing old backup of type %{file_type}\") % { file_type: stat.ftype }\n\n    begin\n      Puppet::FileSystem.unlink(newfile)\n    rescue => detail\n      message = _(\"Could not remove old backup: %{detail}\") % { detail: detail }\n      log_exception(detail, message)\n      self.fail Puppet::Error, message, detail\n    end\n  end\n\n  def backup_file_with_filebucket(f)\n    sum = bucket.backup(f)\n    info _(\"Filebucketed %{f} to %{filebucket} with sum %{sum}\") % { f: f, filebucket: bucket.name, sum: sum }\n    sum\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/character_encoding.rb",
    "content": "# frozen_string_literal: true\n\n# A module to centralize heuristics/practices for managing character encoding in Puppet\n\nmodule Puppet::Util::CharacterEncoding\n  class << self\n    # Given a string, attempts to convert a copy of the string to UTF-8. Conversion uses\n    # encode - the string's internal byte representation is modifed to UTF-8.\n    #\n    # This method is intended for situations where we generally trust that the\n    # string's bytes are a faithful representation of the current encoding\n    # associated with it, and can use it as a starting point for transcoding\n    # (conversion) to UTF-8.\n    #\n    # @api public\n    # @param [String] string a string to transcode\n    # @return [String] copy of the original string, in UTF-8 if transcodable\n    def convert_to_utf_8(string)\n      original_encoding = string.encoding\n      string_copy = string.dup\n      begin\n        if original_encoding == Encoding::UTF_8\n          unless string_copy.valid_encoding?\n            Puppet.debug {\n              _(\"%{value} is already labeled as UTF-8 but this encoding is invalid. It cannot be transcoded by Puppet.\") % { value: string.dump }\n            }\n          end\n          # String is already valid UTF-8 - noop\n          string_copy\n        else\n          # If the string comes to us as BINARY encoded, we don't know what it\n          # started as. However, to encode! we need a starting place, and our\n          # best guess is whatever the system currently is (default_external).\n          # So set external_encoding to default_external before we try to\n          # transcode to UTF-8.\n          string_copy.force_encoding(Encoding.default_external) if original_encoding == Encoding::BINARY\n          string_copy.encode(Encoding::UTF_8)\n        end\n      rescue EncodingError => detail\n        # Set the encoding on our copy back to its original if we modified it\n        string_copy.force_encoding(original_encoding) if original_encoding == Encoding::BINARY\n\n        # Catch both our own self-determined failure to transcode as well as any\n        # error on ruby's part, ie Encoding::UndefinedConversionError on a\n        # failure to encode!.\n        Puppet.debug {\n          _(\"%{error}: %{value} cannot be transcoded by Puppet.\") % { error: detail.inspect, value: string.dump }\n        }\n        string_copy\n      end\n    end\n\n    # Given a string, tests if that string's bytes represent valid UTF-8, and if\n    # so return a copy of the string with external encoding set to UTF-8. Does\n    # not modify the byte representation of the string. If the string does not\n    # represent valid UTF-8, does not set the external encoding.\n    #\n    # This method is intended for situations where we do not believe that the\n    # encoding associated with a string is an accurate reflection of its actual\n    # bytes, i.e., effectively when we believe Ruby is incorrect in its\n    # assertion of the encoding of the string.\n    #\n    # @api public\n    # @param [String] string to set external encoding (re-label) to utf-8\n    # @return [String] a copy of string with external encoding set to utf-8, or\n    # a copy of the original string if override would result in invalid encoding.\n    def override_encoding_to_utf_8(string)\n      string_copy = string.dup\n      original_encoding = string_copy.encoding\n      return string_copy if original_encoding == Encoding::UTF_8\n\n      if string_copy.force_encoding(Encoding::UTF_8).valid_encoding?\n        string_copy\n      else\n        Puppet.debug {\n          _(\"%{value} is not valid UTF-8 and result of overriding encoding would be invalid.\") % { value: string.dump }\n        }\n        # Set copy back to its original encoding before returning\n        string_copy.force_encoding(original_encoding)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/checksums.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest/md5'\nrequire 'digest/sha1'\nrequire 'time'\n\n# A stand-alone module for calculating checksums\n# in a generic way.\nmodule Puppet::Util::Checksums\n  module_function\n\n  # If you modify this, update puppet/type/file/checksum.rb too\n  KNOWN_CHECKSUMS = [\n    :sha256, :sha256lite,\n    :md5, :md5lite,\n    :sha1, :sha1lite,\n    :sha512,\n    :sha384,\n    :sha224,\n    :mtime, :ctime, :none\n  ].freeze\n\n  # It's not a good idea to use some of these in some contexts: for example, I\n  # wouldn't try bucketing a file using the :none checksum type.\n  def known_checksum_types\n    KNOWN_CHECKSUMS\n  end\n\n  def valid_checksum?(type, value)\n    !!send(\"#{type}?\", value)\n  rescue NoMethodError\n    false\n  end\n\n  class FakeChecksum\n    def <<(*args)\n      self\n    end\n  end\n\n  # Is the provided string a checksum?\n  def checksum?(string)\n    # 'sha256lite'.length == 10\n    string =~ /^\\{(\\w{3,10})\\}\\S+/\n  end\n\n  # Strip the checksum type from an existing checksum\n  def sumdata(checksum)\n    checksum =~ /^\\{(\\w+)\\}(.+)/ ? ::Regexp.last_match(2) : nil\n  end\n\n  # Strip the checksum type from an existing checksum\n  def sumtype(checksum)\n    checksum =~ /^\\{(\\w+)\\}/ ? ::Regexp.last_match(1) : nil\n  end\n\n  # Calculate a checksum using Digest::SHA256.\n  def sha256(content)\n    require 'digest/sha2'\n    Digest::SHA256.hexdigest(content)\n  end\n\n  def sha256?(string)\n    string =~ /^\\h{64}$/\n  end\n\n  def sha256_file(filename, lite = false)\n    require 'digest/sha2'\n\n    digest = Digest::SHA256.new\n    checksum_file(digest, filename, lite)\n  end\n\n  def sha256_stream(lite = false, &block)\n    require 'digest/sha2'\n    digest = Digest::SHA256.new\n    checksum_stream(digest, block, lite)\n  end\n\n  def sha256_hex_length\n    64\n  end\n\n  def sha256lite(content)\n    sha256(content[0..511])\n  end\n\n  def sha256lite?(string)\n    sha256?(string)\n  end\n\n  def sha256lite_file(filename)\n    sha256_file(filename, true)\n  end\n\n  def sha256lite_stream(&block)\n    sha256_stream(true, &block)\n  end\n\n  def sha256lite_hex_length\n    sha256_hex_length\n  end\n\n  # Calculate a checksum using Digest::SHA384.\n  def sha384(content)\n    require 'digest/sha2'\n    Digest::SHA384.hexdigest(content)\n  end\n\n  def sha384?(string)\n    string =~ /^\\h{96}$/\n  end\n\n  def sha384_file(filename, lite = false)\n    require 'digest/sha2'\n\n    digest = Digest::SHA384.new\n    checksum_file(digest, filename, lite)\n  end\n\n  def sha384_stream(lite = false, &block)\n    require 'digest/sha2'\n    digest = Digest::SHA384.new\n    checksum_stream(digest, block, lite)\n  end\n\n  def sha384_hex_length\n    96\n  end\n\n  # Calculate a checksum using Digest::SHA512.\n  def sha512(content)\n    require 'digest/sha2'\n    Digest::SHA512.hexdigest(content)\n  end\n\n  def sha512?(string)\n    string =~ /^\\h{128}$/\n  end\n\n  def sha512_file(filename, lite = false)\n    require 'digest/sha2'\n\n    digest = Digest::SHA512.new\n    checksum_file(digest, filename, lite)\n  end\n\n  def sha512_stream(lite = false, &block)\n    require 'digest/sha2'\n    digest = Digest::SHA512.new\n    checksum_stream(digest, block, lite)\n  end\n\n  def sha512_hex_length\n    128\n  end\n\n  # Calculate a checksum using Digest::SHA224.\n  def sha224(content)\n    require_relative '../../puppet/ssl/openssl_loader'\n    OpenSSL::Digest.new('SHA224').hexdigest(content)\n  end\n\n  def sha224?(string)\n    string =~ /^\\h{56}$/\n  end\n\n  def sha224_file(filename, lite = false)\n    require_relative '../../puppet/ssl/openssl_loader'\n\n    digest = OpenSSL::Digest.new('SHA224')\n    checksum_file(digest, filename, lite)\n  end\n\n  def sha224_stream(lite = false, &block)\n    require_relative '../../puppet/ssl/openssl_loader'\n    digest = OpenSSL::Digest.new('SHA224')\n    checksum_stream(digest, block, lite)\n  end\n\n  def sha224_hex_length\n    56\n  end\n\n  # Calculate a checksum using Digest::MD5.\n  def md5(content)\n    Digest::MD5.hexdigest(content)\n  end\n\n  def md5?(string)\n    string =~ /^\\h{32}$/\n  end\n\n  # Calculate a checksum of a file's content using Digest::MD5.\n  def md5_file(filename, lite = false)\n    digest = Digest::MD5.new\n    checksum_file(digest, filename, lite)\n  end\n\n  def md5_stream(lite = false, &block)\n    digest = Digest::MD5.new\n    checksum_stream(digest, block, lite)\n  end\n\n  def md5_hex_length\n    32\n  end\n\n  # Calculate a checksum of the first 500 chars of the content using Digest::MD5.\n  def md5lite(content)\n    md5(content[0..511])\n  end\n\n  def md5lite?(string)\n    md5?(string)\n  end\n\n  # Calculate a checksum of the first 500 chars of a file's content using Digest::MD5.\n  def md5lite_file(filename)\n    md5_file(filename, true)\n  end\n\n  def md5lite_stream(&block)\n    md5_stream(true, &block)\n  end\n\n  def md5lite_hex_length\n    md5_hex_length\n  end\n\n  def mtime(content)\n    \"\"\n  end\n\n  def mtime?(string)\n    return true if string.is_a? Time\n\n    !!DateTime.parse(string)\n  rescue\n    false\n  end\n\n  # Return the :mtime timestamp of a file.\n  def mtime_file(filename)\n    Puppet::FileSystem.stat(filename).mtime\n  end\n\n  # by definition this doesn't exist\n  # but we still need to execute the block given\n  def mtime_stream(&block)\n    noop_digest = FakeChecksum.new\n    yield noop_digest\n    nil\n  end\n\n  # Calculate a checksum using Digest::SHA1.\n  def sha1(content)\n    Digest::SHA1.hexdigest(content)\n  end\n\n  def sha1?(string)\n    string =~ /^\\h{40}$/\n  end\n\n  # Calculate a checksum of a file's content using Digest::SHA1.\n  def sha1_file(filename, lite = false)\n    digest = Digest::SHA1.new\n    checksum_file(digest, filename, lite)\n  end\n\n  def sha1_stream(lite = false, &block)\n    digest = Digest::SHA1.new\n    checksum_stream(digest, block, lite)\n  end\n\n  def sha1_hex_length\n    40\n  end\n\n  # Calculate a checksum of the first 500 chars of the content using Digest::SHA1.\n  def sha1lite(content)\n    sha1(content[0..511])\n  end\n\n  def sha1lite?(string)\n    sha1?(string)\n  end\n\n  # Calculate a checksum of the first 500 chars of a file's content using Digest::SHA1.\n  def sha1lite_file(filename)\n    sha1_file(filename, true)\n  end\n\n  def sha1lite_stream(&block)\n    sha1_stream(true, &block)\n  end\n\n  def sha1lite_hex_length\n    sha1_hex_length\n  end\n\n  def ctime(content)\n    \"\"\n  end\n\n  def ctime?(string)\n    return true if string.is_a? Time\n\n    !!DateTime.parse(string)\n  rescue\n    false\n  end\n\n  # Return the :ctime of a file.\n  def ctime_file(filename)\n    Puppet::FileSystem.stat(filename).ctime\n  end\n\n  def ctime_stream(&block)\n    mtime_stream(&block)\n  end\n\n  def none(content)\n    \"\"\n  end\n\n  def none?(string)\n    string.empty?\n  end\n\n  # Return a \"no checksum\"\n  def none_file(filename)\n    \"\"\n  end\n\n  def none_stream\n    noop_digest = FakeChecksum.new\n    yield noop_digest\n    \"\"\n  end\n\n  class DigestLite\n    def initialize(digest, lite = false)\n      @digest = digest\n      @lite = lite\n      @bytes = 0\n    end\n\n    # Provide an interface for digests. If lite, only digest the first 512 bytes\n    def <<(str)\n      if @lite\n        if @bytes < 512\n          buf = str[0, 512 - @bytes]\n          @digest << buf\n          @bytes += buf.length\n        end\n      else\n        @digest << str\n      end\n    end\n  end\n\n  # Perform an incremental checksum on a file.\n  def checksum_file(digest, filename, lite = false)\n    buffer = lite ? 512 : 4096\n    File.open(filename, 'rb') do |file|\n      while content = file.read(buffer) # rubocop:disable Lint/AssignmentInCondition\n        digest << content\n        break if lite\n      end\n    end\n\n    digest.hexdigest\n  end\n\n  def checksum_stream(digest, block, lite = false)\n    block.call(DigestLite.new(digest, lite))\n    digest.hexdigest\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/classgen.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  class ConstantAlreadyDefined < Error; end\n  class SubclassAlreadyDefined < Error; end\nend\n\n# This is a utility module for generating classes.\n# @api public\n#\nmodule Puppet::Util::ClassGen\n  include Puppet::Util\n\n  # Create a new class.\n  # @param name [String] the name of the generated class\n  # @param options [Hash] a hash of options\n  # @option options [Array<Class>] :array if specified, the generated class is appended to this array\n  # @option options [Hash<{String => Object}>] :attributes a hash that is applied to the generated class\n  #   by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given\n  #   block is evaluated.\n  # @option options [Proc] :block a block to evaluate in the context of the class (this block can be provided\n  #   this way, or as a normal yield block).\n  # @option options [String] :constant (name with first letter capitalized) what to set the constant that references\n  #   the generated class to.\n  # @option options [Hash] :hash a hash of existing classes that this class is appended to (name => class).\n  #   This hash must be specified if the `:overwrite` option is set to `true`.\n  # @option options [Boolean] :overwrite whether an overwrite of an existing class should be allowed (requires also\n  #   defining the `:hash` with existing classes as the test is based on the content of this hash).\n  # @option options [Class] :parent (self) the parent class of the generated class.\n  # @option options [String] ('') :prefix the constant prefix to prepend to the constant name referencing the\n  #   generated class.\n  # @return [Class] the generated class\n  #\n  def genclass(name, options = {}, &block)\n    genthing(name, Class, options, block)\n  end\n\n  # Creates a new module.\n  # @param name [String] the name of the generated module\n  # @param options [Hash] hash with options\n  # @option options [Array<Class>] :array if specified, the generated class is appended to this array\n  # @option options [Hash<{String => Object}>] :attributes a hash that is applied to the generated class\n  #   by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given\n  #   block is evaluated.\n  # @option options [Proc] :block a block to evaluate in the context of the class (this block can be provided\n  #   this way, or as a normal yield block).\n  # @option options [String] :constant (name with first letter capitalized) what to set the constant that references\n  #   the generated class to.\n  # @option options [Hash] :hash a hash of existing classes that this class is appended to (name => class).\n  #   This hash must be specified if the `:overwrite` option is set to `true`.\n  # @option options [Boolean] :overwrite whether an overwrite of an existing class should be allowed (requires also\n  #   defining the `:hash` with existing classes as the test is based on the content of this hash).\n  #   the capitalized name is appended and the result is set as the constant.\n  # @option options [String] ('') :prefix the constant prefix to prepend to the constant name referencing the\n  #   generated class.\n  # @return [Module] the generated Module\n  def genmodule(name, options = {}, &block)\n    genthing(name, Module, options, block)\n  end\n\n  # Removes an existing class.\n  # @param name [String] the name of the class to remove\n  # @param options [Hash] options\n  # @option options [Hash] :hash a hash of existing classes from which the class to be removed is also removed\n  # @return [Boolean] whether the class was removed or not\n  #\n  def rmclass(name, options)\n    const = genconst_string(name, options)\n    retval = false\n    if const_defined?(const, false)\n      remove_const(const)\n      retval = true\n    end\n\n    hash = options[:hash]\n    if hash && hash.include?(name)\n      hash.delete(name)\n      retval = true\n    end\n\n    # Let them know whether we did actually delete a subclass.\n    retval\n  end\n\n  private\n\n  # Generates the constant to create or remove.\n  # @api private\n  def genconst_string(name, options)\n    const = options[:constant]\n    unless const\n      prefix = options[:prefix] || \"\"\n      const = prefix + name2const(name)\n    end\n\n    const\n  end\n\n  # This does the actual work of creating our class or module.  It's just a\n  # slightly abstract version of genclass.\n  # @api private\n  def genthing(name, type, options, block)\n    name = name.to_s.downcase.intern\n\n    if type == Module\n      # evalmethod = :module_eval\n      evalmethod = :class_eval\n      # Create the class, with the correct name.\n      klass = Module.new do\n        class << self\n          attr_reader :name\n        end\n        @name = name\n      end\n    else\n      options[:parent] ||= self\n      evalmethod = :class_eval\n      # Create the class, with the correct name.\n      klass = Class.new(options[:parent]) do\n        @name = name\n      end\n    end\n\n    # Create the constant as appropriation.\n    handleclassconst(klass, name, options)\n\n    # Initialize any necessary variables.\n    initclass(klass, options)\n\n    block ||= options[:block]\n\n    # Evaluate the passed block if there is one.  This should usually\n    # define all of the work.\n    klass.send(evalmethod, &block) if block\n\n    klass.postinit if klass.respond_to? :postinit\n\n    # Store the class in hashes or arrays or whatever.\n    storeclass(klass, name, options)\n\n    klass\n  end\n\n  # Handle the setting and/or removing of the associated constant.\n  # @api private\n  #\n  def handleclassconst(klass, name, options)\n    const = genconst_string(name, options)\n\n    if const_defined?(const, false)\n      if options[:overwrite]\n        Puppet.info _(\"Redefining %{name} in %{klass}\") % { name: name, klass: self }\n        remove_const(const)\n      else\n        raise Puppet::ConstantAlreadyDefined,\n              _(\"Class %{const} is already defined in %{klass}\") % { const: const, klass: self }\n      end\n    end\n    const_set(const, klass)\n\n    const\n  end\n\n  # Perform the initializations on the class.\n  # @api private\n  #\n  def initclass(klass, options)\n    klass.initvars if klass.respond_to? :initvars\n\n    attrs = options[:attributes]\n    if attrs\n      attrs.each do |param, value|\n        method = param.to_s + \"=\"\n        klass.send(method, value) if klass.respond_to? method\n      end\n    end\n\n    [:include, :extend].each do |method|\n      set = options[method]\n      next unless set\n\n      set = [set] unless set.is_a?(Array)\n      set.each do |mod|\n        klass.send(method, mod)\n      end\n    end\n\n    klass.preinit if klass.respond_to? :preinit\n  end\n\n  # Convert our name to a constant.\n  # @api private\n  def name2const(name)\n    name.to_s.capitalize\n  end\n\n  # Store the class in the appropriate places.\n  # @api private\n  def storeclass(klass, klassname, options)\n    hash = options[:hash]\n    if hash\n      if hash.include? klassname and !options[:overwrite]\n        raise Puppet::SubclassAlreadyDefined,\n              _(\"Already a generated class named %{klassname}\") % { klassname: klassname }\n      end\n\n      hash[klassname] = klass\n    end\n\n    # If we were told to stick it in a hash, then do so\n    array = options[:array]\n    if array\n      if klass.respond_to? :name and\n         array.find { |c| c.name == klassname } and\n         !options[:overwrite]\n        raise Puppet::SubclassAlreadyDefined,\n              _(\"Already a generated class named %{klassname}\") % { klassname: klassname }\n      end\n\n      array << klass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/colors.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/platform'\n\nmodule Puppet::Util::Colors\n  BLACK       = { :console => \"\\e[0;30m\", :html => \"color: #FFA0A0\"     }\n  RED         = { :console => \"\\e[0;31m\", :html => \"color: #FFA0A0\"     }\n  GREEN       = { :console => \"\\e[0;32m\", :html => \"color: #00CD00\"     }\n  YELLOW      = { :console => \"\\e[0;33m\", :html => \"color: #FFFF60\"     }\n  BLUE        = { :console => \"\\e[0;34m\", :html => \"color: #80A0FF\"     }\n  MAGENTA     = { :console => \"\\e[0;35m\", :html => \"color: #FFA500\"     }\n  CYAN        = { :console => \"\\e[0;36m\", :html => \"color: #40FFFF\"     }\n  WHITE       = { :console => \"\\e[0;37m\", :html => \"color: #FFFFFF\"     }\n  HBLACK      = { :console => \"\\e[1;30m\", :html => \"color: #FFA0A0\"     }\n  HRED        = { :console => \"\\e[1;31m\", :html => \"color: #FFA0A0\"     }\n  HGREEN      = { :console => \"\\e[1;32m\", :html => \"color: #00CD00\"     }\n  HYELLOW     = { :console => \"\\e[1;33m\", :html => \"color: #FFFF60\"     }\n  HBLUE       = { :console => \"\\e[1;34m\", :html => \"color: #80A0FF\"     }\n  HMAGENTA    = { :console => \"\\e[1;35m\", :html => \"color: #FFA500\"     }\n  HCYAN       = { :console => \"\\e[1;36m\", :html => \"color: #40FFFF\"     }\n  HWHITE      = { :console => \"\\e[1;37m\", :html => \"color: #FFFFFF\"     }\n  BG_RED      = { :console => \"\\e[0;41m\", :html => \"background: #FFA0A0\" }\n  BG_GREEN    = { :console => \"\\e[0;42m\", :html => \"background: #00CD00\" }\n  BG_YELLOW   = { :console => \"\\e[0;43m\", :html => \"background: #FFFF60\" }\n  BG_BLUE     = { :console => \"\\e[0;44m\", :html => \"background: #80A0FF\" }\n  BG_MAGENTA  = { :console => \"\\e[0;45m\", :html => \"background: #FFA500\" }\n  BG_CYAN     = { :console => \"\\e[0;46m\", :html => \"background: #40FFFF\" }\n  BG_WHITE    = { :console => \"\\e[0;47m\", :html => \"background: #FFFFFF\" }\n  BG_HRED     = { :console => \"\\e[1;41m\", :html => \"background: #FFA0A0\" }\n  BG_HGREEN   = { :console => \"\\e[1;42m\", :html => \"background: #00CD00\" }\n  BG_HYELLOW  = { :console => \"\\e[1;43m\", :html => \"background: #FFFF60\" }\n  BG_HBLUE    = { :console => \"\\e[1;44m\", :html => \"background: #80A0FF\" }\n  BG_HMAGENTA = { :console => \"\\e[1;45m\", :html => \"background: #FFA500\" }\n  BG_HCYAN    = { :console => \"\\e[1;46m\", :html => \"background: #40FFFF\" }\n  BG_HWHITE   = { :console => \"\\e[1;47m\", :html => \"background: #FFFFFF\" }\n  RESET       = { :console => \"\\e[0m\",    :html => \"\" }\n\n  Colormap = {\n    :debug => WHITE,\n    :info => GREEN,\n    :notice => CYAN,\n    :warning => YELLOW,\n    :err => HMAGENTA,\n    :alert => RED,\n    :emerg => HRED,\n    :crit => HRED,\n\n    :black => BLACK,\n    :red => RED,\n    :green => GREEN,\n    :yellow => YELLOW,\n    :blue => BLUE,\n    :magenta => MAGENTA,\n    :cyan => CYAN,\n    :white => WHITE,\n    :hblack => HBLACK,\n    :hred => HRED,\n    :hgreen => HGREEN,\n    :hyellow => HYELLOW,\n    :hblue => HBLUE,\n    :hmagenta => HMAGENTA,\n    :hcyan => HCYAN,\n    :hwhite => HWHITE,\n    :bg_red => BG_RED,\n    :bg_green => BG_GREEN,\n    :bg_yellow => BG_YELLOW,\n    :bg_blue => BG_BLUE,\n    :bg_magenta => BG_MAGENTA,\n    :bg_cyan => BG_CYAN,\n    :bg_white => BG_WHITE,\n    :bg_hred => BG_HRED,\n    :bg_hgreen => BG_HGREEN,\n    :bg_hyellow => BG_HYELLOW,\n    :bg_hblue => BG_HBLUE,\n    :bg_hmagenta => BG_HMAGENTA,\n    :bg_hcyan => BG_HCYAN,\n    :bg_hwhite => BG_HWHITE,\n    :reset => { :console => \"\\e[m\", :html => \"\" }\n  }\n\n  def colorize(color, str)\n    case Puppet[:color]\n    when true, :ansi, \"ansi\", \"yes\"\n      console_color(color, str)\n    when :html, \"html\"\n      html_color(color, str)\n    else\n      str\n    end\n  end\n\n  def console_color(color, str)\n    Colormap[color][:console] +\n      str.gsub(RESET[:console], Colormap[color][:console]) +\n      RESET[:console]\n  end\n\n  def html_color(color, str)\n    span = '<span style=\"%s\">' % Colormap[color][:html]\n    \"#{span}%s</span>\" % str.gsub(%r{<span .*?</span>}, \"</span>\\\\0#{span}\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/command_line/puppet_option_parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/command_line/trollop'\n\nmodule Puppet\n  module Util\n    class CommandLine\n      class PuppetOptionError < Puppet::Error\n      end\n\n      class TrollopCommandlineError < Puppet::Util::CommandLine::Trollop::CommandlineError; end\n\n      # This is a command line option parser.  It is intended to have an API that is very similar to\n      #  the ruby stdlib 'OptionParser' API, for ease of integration into our existing code... however,\n      #  However, we've removed the OptionParser-based implementation and are only maintaining the\n      #  it's implemented based on the third-party \"trollop\" library.  This was done because there\n      #  are places where the stdlib OptionParser is not flexible enough to meet our needs.\n\n      class PuppetOptionParser\n        def initialize(usage_msg = nil)\n          require_relative '../../../puppet/util/command_line/trollop'\n\n          @create_default_short_options = false\n\n          @parser = Trollop::Parser.new do\n            banner usage_msg\n          end\n        end\n\n        # This parameter, if set, will tell the underlying option parser not to throw an\n        #  exception if we pass it options that weren't explicitly registered.  We need this\n        #  capability because we need to be able to pass all of the command-line options before\n        #  we know which application/face they are going to be running, but the app/face\n        #  may specify additional command-line arguments that are valid for that app/face.\n        attr_reader :ignore_invalid_options\n\n        def ignore_invalid_options=(value)\n          @parser.ignore_invalid_options = value\n        end\n\n        def on(*args, &block)\n          # The 2nd element is an optional \"short\" representation.\n          case args.length\n          when 3\n            long, desc, type = args\n          when 4\n            long, short, desc, type = args\n          else\n            raise ArgumentError, _(\"this method only takes 3 or 4 arguments. Given: %{args}\") % { args: args.inspect }\n          end\n\n          options = {\n            :long => long,\n            :short => short,\n            :required => false,\n            :callback => pass_only_last_value_on_to(block),\n            :multi => true,\n          }\n\n          case type\n          when :REQUIRED\n            options[:type] = :string\n          when :NONE\n            options[:type] = :flag\n          else\n            raise PuppetOptionError, _(\"Unsupported type: '%{type}'\") % { type: type }\n          end\n\n          @parser.opt long.sub(\"^--\", \"\").intern, desc, options\n        end\n\n        def parse(*args)\n          args = args[0] if args.size == 1 and args[0].is_a?(Array)\n          args_copy = args.dup\n          begin\n            @parser.parse args_copy\n          rescue Puppet::Util::CommandLine::Trollop::CommandlineError => err\n            raise PuppetOptionError.new(_(\"Error parsing arguments\"), err)\n          end\n        end\n\n        def pass_only_last_value_on_to(block)\n          ->(values) { block.call(values.is_a?(Array) ? values.last : values) }\n        end\n        private :pass_only_last_value_on_to\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/command_line/trollop.rb",
    "content": "# frozen_string_literal: true\n\n## lib/trollop.rb -- trollop command-line processing library\n## Author::    William Morgan (mailto: wmorgan-trollop@masanjin.net)\n## Copyright:: Copyright 2007 William Morgan\n## License::   the same terms as ruby itself\n##\n## 2012-03: small changes made by cprice (chris@puppetlabs.com);\n##           patch submitted for upstream consideration:\n##           https://gitorious.org/trollop/mainline/merge_requests/9\n## 2012-08: namespace changes made by Jeff McCune (jeff@puppetlabs.com)\n##           moved Trollop into Puppet::Util::CommandLine to prevent monkey\n##           patching the upstream trollop library if also loaded.\n\nrequire 'date'\n\nmodule Puppet\nmodule Util\nclass CommandLine\n  module Trollop\n  VERSION = \"1.16.2\"\n\n  ## Thrown by Parser in the event of a commandline error. Not needed if\n  ## you're using the Trollop::options entry.\n  class CommandlineError < StandardError; end\n\n  ## Thrown by Parser if the user passes in '-h' or '--help'. Handled\n  ## automatically by Trollop#options.\n  class HelpNeeded < StandardError; end\n\n  ## Thrown by Parser if the user passes in '-h' or '--version'. Handled\n  ## automatically by Trollop#options.\n  class VersionNeeded < StandardError; end\n\n  ## Regex for floating point numbers\n  FLOAT_RE = /^-?((\\d+(\\.\\d+)?)|(\\.\\d+))([eE][-+]?\\d+)?$/\n\n  ## Regex for parameters\n  PARAM_RE = /^-(-|\\.$|[^\\d.])/\n\n  ## The commandline parser. In typical usage, the methods in this class\n  ## will be handled internally by Trollop::options. In this case, only the\n  ## #opt, #banner and #version, #depends, and #conflicts methods will\n  ## typically be called.\n  ##\n  ## If you want to instantiate this class yourself (for more complicated\n  ## argument-parsing logic), call #parse to actually produce the output hash,\n  ## and consider calling it from within\n  ## Trollop::with_standard_exception_handling.\n  class Parser\n    ## The set of values that indicate a flag option when passed as the\n    ## +:type+ parameter of #opt.\n    FLAG_TYPES = [:flag, :bool, :boolean]\n\n    ## The set of values that indicate a single-parameter (normal) option when\n    ## passed as the +:type+ parameter of #opt.\n    ##\n    ## A value of +io+ corresponds to a readable IO resource, including\n    ## a filename, URI, or the strings 'stdin' or '-'.\n    SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io, :date]\n\n    ## The set of values that indicate a multiple-parameter option (i.e., that\n    ## takes multiple space-separated values on the commandline) when passed as\n    ## the +:type+ parameter of #opt.\n    MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios, :dates]\n\n    ## The complete set of legal values for the +:type+ parameter of #opt.\n    TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES\n\n    INVALID_SHORT_ARG_REGEX = /[\\d-]/ # :nodoc:\n\n    ## The values from the commandline that were not interpreted by #parse.\n    attr_reader :leftovers\n\n    ## The complete configuration hashes for each option. (Mainly useful\n    ## for testing.)\n    attr_reader :specs\n\n    ## A flag that determines whether or not to attempt to automatically generate \"short\" options if they are not\n    ##  explicitly specified.\n    attr_accessor :create_default_short_options\n\n    ## A flag that determines whether or not to raise an error if the parser is passed one or more\n    ##  options that were not registered ahead of time.  If 'true', then the parser will simply\n    ##  ignore options that it does not recognize.\n    attr_accessor :ignore_invalid_options\n\n    ## A flag indicating whether or not the parser should attempt to handle \"--help\" and\n    ##  \"--version\" specially.  If 'false', it will treat them just like any other option.\n    attr_accessor :handle_help_and_version\n\n    ## Initializes the parser, and instance-evaluates any block given.\n    def initialize *a, &b\n      @version = nil\n      @leftovers = []\n      @specs = {}\n      @long = {}\n      @short = {}\n      @order = []\n      @constraints = []\n      @stop_words = []\n      @stop_on_unknown = false\n\n      # instance_eval(&b) if b # can't take arguments\n      cloaker(&b).bind_call(self, *a) if b\n    end\n\n    ## Define an option. +name+ is the option name, a unique identifier\n    ## for the option that you will use internally, which should be a\n    ## symbol or a string. +desc+ is a string description which will be\n    ## displayed in help messages.\n    ##\n    ## Takes the following optional arguments:\n    ##\n    ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.\n    ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+.\n    ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.\n    ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the commandline the value will be +false+.\n    ## [+:required+] If set to +true+, the argument must be provided on the commandline.\n    ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)\n    ##\n    ## Note that there are two types of argument multiplicity: an argument\n    ## can take multiple values, e.g. \"--arg 1 2 3\". An argument can also\n    ## be allowed to occur multiple times, e.g. \"--arg 1 --arg 2\".\n    ##\n    ## Arguments that take multiple values should have a +:type+ parameter\n    ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+\n    ## value of an array of the correct type (e.g. [String]). The\n    ## value of this argument will be an array of the parameters on the\n    ## commandline.\n    ##\n    ## Arguments that can occur multiple times should be marked with\n    ## +:multi+ => +true+. The value of this argument will also be an array.\n    ## In contrast with regular non-multi options, if not specified on\n    ## the commandline, the default value will be [], not nil.\n    ##\n    ## These two attributes can be combined (e.g. +:type+ => +:strings+,\n    ## +:multi+ => +true+), in which case the value of the argument will be\n    ## an array of arrays.\n    ##\n    ## There's one ambiguous case to be aware of: when +:multi+: is true and a\n    ## +:default+ is set to an array (of something), it's ambiguous whether this\n    ## is a multi-value argument as well as a multi-occurrence argument.\n    ## In this case, Trollop assumes that it's not a multi-value argument.\n    ## If you want a multi-value, multi-occurrence argument with a default\n    ## value, you must specify +:type+ as well.\n\n    def opt name, desc = \"\", opts = {}\n      raise ArgumentError, _(\"you already have an argument named '%{name}'\") % { name: name } if @specs.member? name\n\n      ## fill in :type\n      opts[:type] = # normalize\n        case opts[:type]\n        when :boolean, :bool; :flag\n        when :integer; :int\n        when :integers; :ints\n        when :double; :float\n        when :doubles; :floats\n        when Class\n          case opts[:type].name\n          when 'TrueClass', 'FalseClass'; :flag\n          when 'String'; :string\n          when 'Integer'; :int\n          when 'Float'; :float\n          when 'IO'; :io\n          when 'Date'; :date\n          else\n            raise ArgumentError, _(\"unsupported argument type '%{type}'\") % { type: opts[:type].class.name }\n          end\n        when nil; nil\n        else\n          raise ArgumentError, _(\"unsupported argument type '%{type}'\") % { type: opts[:type] } unless TYPES.include?(opts[:type])\n\n          opts[:type]\n        end\n\n      ## for options with :multi => true, an array default doesn't imply\n      ## a multi-valued argument. for that you have to specify a :type\n      ## as well. (this is how we disambiguate an ambiguous situation;\n      ## see the docs for Parser#opt for details.)\n      disambiguated_default =\n        if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]\n          opts[:default].first\n        else\n          opts[:default]\n        end\n\n      type_from_default =\n        case disambiguated_default\n        when Integer; :int\n        when Numeric; :float\n        when TrueClass, FalseClass; :flag\n        when String; :string\n        when IO; :io\n        when Date; :date\n        when Array\n          if opts[:default].empty?\n            raise ArgumentError, _(\"multiple argument type cannot be deduced from an empty array for '%{value0}'\") % { value0: opts[:default][0].class.name }\n          end\n\n          case opts[:default][0] # the first element determines the types\n          when Integer; :ints\n          when Numeric; :floats\n          when String; :strings\n          when IO; :ios\n          when Date; :dates\n          else\n            raise ArgumentError, _(\"unsupported multiple argument type '%{value0}'\") % { value0: opts[:default][0].class.name }\n          end\n        when nil; nil\n        else\n          raise ArgumentError, _(\"unsupported argument type '%{value0}'\") % { value0: opts[:default].class.name }\n        end\n\n      raise ArgumentError, _(\":type specification and default type don't match (default type is %{type_from_default})\") % { type_from_default: type_from_default } if opts[:type] && type_from_default && opts[:type] != type_from_default\n\n      opts[:type] = opts[:type] || type_from_default || :flag\n\n      ## fill in :long\n      opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.tr(\"_\", \"-\")\n      opts[:long] =\n        case opts[:long]\n        when /^--([^-].*)$/\n          ::Regexp.last_match(1)\n        when /^[^-]/\n          opts[:long]\n        else\n          raise ArgumentError, _(\"invalid long option name %{name}\") % { name: opts[:long].inspect }\n        end\n      raise ArgumentError, _(\"long option name %{value0} is already taken; please specify a (different) :long\") % { value0: opts[:long].inspect } if @long[opts[:long]]\n\n      ## fill in :short\n      unless opts[:short] == :none\n        opts[:short] = opts[:short].to_s if opts[:short]\n      end\n      opts[:short] = case opts[:short]\n                     when /^-(.)$/; ::Regexp.last_match(1)\n                     when nil, :none, /^.$/; opts[:short]\n                     else raise ArgumentError, _(\"invalid short option name '%{name}'\") % { name: opts[:short].inspect }\n                     end\n\n      if opts[:short]\n        raise ArgumentError, _(\"short option name %{value0} is already taken; please specify a (different) :short\") % { value0: opts[:short].inspect } if @short[opts[:short]]\n        raise ArgumentError, _(\"a short option name can't be a number or a dash\") if opts[:short] =~ INVALID_SHORT_ARG_REGEX\n      end\n\n      ## fill in :default for flags\n      opts[:default] = false if opts[:type] == :flag && opts[:default].nil?\n\n      ## autobox :default for :multi (multi-occurrence) arguments\n      opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)\n\n      ## fill in :multi\n      opts[:multi] ||= false\n\n      opts[:desc] ||= desc\n      @long[opts[:long]] = name\n      @short[opts[:short]] = name if opts[:short] && opts[:short] != :none\n      @specs[name] = opts\n      @order << [:opt, name]\n    end\n\n    ## Sets the version string. If set, the user can request the version\n    ## on the commandline. Should probably be of the form \"<program name>\n    ## <version number>\".\n    def version s = nil; @version = s if s; @version end\n\n    ## Adds text to the help display. Can be interspersed with calls to\n    ## #opt to build a multi-section help page.\n    def banner s; @order << [:text, s] end\n    alias :text :banner\n\n    ## Marks two (or more!) options as requiring each other. Only handles\n    ## undirected (i.e., mutual) dependencies. Directed dependencies are\n    ## better modeled with Trollop::die.\n    def depends *syms\n      syms.each { |sym| raise ArgumentError, _(\"unknown option '%{sym}'\") % { sym: sym } unless @specs[sym] }\n      @constraints << [:depends, syms]\n    end\n\n    ## Marks two (or more!) options as conflicting.\n    def conflicts *syms\n      syms.each { |sym| raise ArgumentError, _(\"unknown option '%{sym}'\") % { sym: sym } unless @specs[sym] }\n      @constraints << [:conflicts, syms]\n    end\n\n    ## Defines a set of words which cause parsing to terminate when\n    ## encountered, such that any options to the left of the word are\n    ## parsed as usual, and options to the right of the word are left\n    ## intact.\n    ##\n    ## A typical use case would be for subcommand support, where these\n    ## would be set to the list of subcommands. A subsequent Trollop\n    ## invocation would then be used to parse subcommand options, after\n    ## shifting the subcommand off of ARGV.\n    def stop_on *words\n      @stop_words = [*words].flatten\n    end\n\n    ## Similar to #stop_on, but stops on any unknown word when encountered\n    ## (unless it is a parameter for an argument). This is useful for\n    ## cases where you don't know the set of subcommands ahead of time,\n    ## i.e., without first parsing the global options.\n    def stop_on_unknown\n      @stop_on_unknown = true\n    end\n\n    ## Parses the commandline. Typically called by Trollop::options,\n    ## but you can call it directly if you need more control.\n    ##\n    ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.\n    def parse cmdline = ARGV\n      vals = {}\n      required = {}\n\n      if handle_help_and_version\n        unless @specs[:version] || @long[\"version\"]\n          opt :version, _(\"Print version and exit\") if @version\n        end\n        opt :help, _(\"Show this message\") unless @specs[:help] || @long[\"help\"]\n      end\n\n      @specs.each do |sym, opts|\n        required[sym] = true if opts[:required]\n        vals[sym] = opts[:default]\n        vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil\n      end\n\n      resolve_default_short_options if create_default_short_options\n\n      ## resolve symbols\n      given_args = {}\n      @leftovers = each_arg cmdline do |arg, params|\n        sym = case arg\n              when /^-([^-])$/\n                @short[::Regexp.last_match(1)]\n              when /^--no-([^-]\\S*)$/\n                possible_match = @long[\"[no-]#{::Regexp.last_match(1)}\"]\n                if !possible_match\n                  partial_match = @long[\"[no-]#{::Regexp.last_match(1).tr('-', '_')}\"] || @long[\"[no-]#{::Regexp.last_match(1).tr('_', '-')}\"]\n                  if partial_match\n                    Puppet.deprecation_warning _(\"Partial argument match detected: correct argument is %{partial_match}, got %{arg}. Partial argument matching is deprecated and will be removed in a future release.\") % { arg: arg, partial_match: partial_match }\n                  end\n                  partial_match\n                else\n                  possible_match\n                end\n              when /^--([^-]\\S*)$/\n                possible_match = @long[::Regexp.last_match(1)] || @long[\"[no-]#{::Regexp.last_match(1)}\"]\n                if !possible_match\n                  partial_match = @long[::Regexp.last_match(1).tr('-', '_')] || @long[::Regexp.last_match(1).tr('_', '-')] || @long[\"[no-]#{::Regexp.last_match(1).tr('-', '_')}\"] || @long[\"[no-]#{::Regexp.last_match(1).tr('_', '-')}\"]\n                  if partial_match\n                    Puppet.deprecation_warning _(\"Partial argument match detected: correct argument is %{partial_match}, got %{arg}. Partial argument matching is deprecated and will be removed in a future release.\") % { arg: arg, partial_match: partial_match }\n                  end\n                  partial_match\n                else\n                  possible_match\n                end\n              else\n                raise CommandlineError, _(\"invalid argument syntax: '%{arg}'\") % { arg: arg }\n              end\n\n        unless sym\n          next 0 if ignore_invalid_options\n          raise CommandlineError, _(\"unknown argument '%{arg}'\") % { arg: arg } unless sym\n        end\n\n        if given_args.include?(sym) && !@specs[sym][:multi]\n          raise CommandlineError, _(\"option '%{arg}' specified multiple times\") % { arg: arg }\n        end\n\n        given_args[sym] ||= {}\n\n        given_args[sym][:arg] = arg\n        given_args[sym][:params] ||= []\n\n        # The block returns the number of parameters taken.\n        num_params_taken = 0\n\n        unless params.nil?\n          if SINGLE_ARG_TYPES.include?(@specs[sym][:type])\n            given_args[sym][:params] << params[0, 1]  # take the first parameter\n            num_params_taken = 1\n          elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])\n            given_args[sym][:params] << params        # take all the parameters\n            num_params_taken = params.size\n          end\n        end\n\n        num_params_taken\n      end\n\n      if handle_help_and_version\n        ## check for version and help args\n        raise VersionNeeded if given_args.include? :version\n        raise HelpNeeded if given_args.include? :help\n      end\n\n      ## check constraint satisfaction\n      @constraints.each do |type, syms|\n        constraint_sym = syms.find { |sym| given_args[sym] }\n        next unless constraint_sym\n\n        case type\n        when :depends\n          syms.each { |sym| raise CommandlineError, _(\"--%{value0} requires --%{value1}\") % { value0: @specs[constraint_sym][:long], value1: @specs[sym][:long] } unless given_args.include? sym }\n        when :conflicts\n          syms.each { |sym| raise CommandlineError, _(\"--%{value0} conflicts with --%{value1}\") % { value0: @specs[constraint_sym][:long], value1: @specs[sym][:long] } if given_args.include?(sym) && (sym != constraint_sym) }\n        end\n      end\n\n      required.each do |sym, _val|\n        raise CommandlineError, _(\"option --%{opt} must be specified\") % { opt: @specs[sym][:long] } unless given_args.include? sym\n      end\n\n      ## parse parameters\n      given_args.each do |sym, given_data|\n        arg = given_data[:arg]\n        params = given_data[:params]\n\n        opts = @specs[sym]\n        raise CommandlineError, _(\"option '%{arg}' needs a parameter\") % { arg: arg } if params.empty? && opts[:type] != :flag\n\n        vals[\"#{sym}_given\".intern] = true # mark argument as specified on the commandline\n\n        case opts[:type]\n        when :flag\n          if arg =~ /^--no-/ and sym.to_s =~ /^--\\[no-\\]/\n            vals[sym] = opts[:default]\n          else\n            vals[sym] = !opts[:default]\n          end\n        when :int, :ints\n          vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }\n        when :float, :floats\n          vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }\n        when :string, :strings\n          vals[sym] = params.map { |pg| pg.map(&:to_s) }\n        when :io, :ios\n          vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }\n        when :date, :dates\n          vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }\n        end\n\n        if SINGLE_ARG_TYPES.include?(opts[:type])\n          if opts[:multi] # multiple options, each with a single parameter\n            vals[sym] = vals[sym].map { |p| p[0] }\n          else # single parameter\n            vals[sym] = vals[sym][0][0]\n          end\n        elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]\n          vals[sym] = vals[sym][0] # single option, with multiple parameters\n        end\n        # else: multiple options, with multiple parameters\n\n        opts[:callback].call(vals[sym]) if opts.has_key?(:callback)\n      end\n\n      ## modify input in place with only those\n      ## arguments we didn't process\n      cmdline.clear\n      @leftovers.each { |l| cmdline << l }\n\n      ## allow openstruct-style accessors\n      class << vals\n        def method_missing(m, *args)\n          self[m] || self[m.to_s]\n        end\n      end\n      vals\n    end\n\n    def parse_date_parameter param, arg # :nodoc:\n      begin\n        time = Chronic.parse(param)\n      rescue NameError\n        # chronic is not available\n      end\n      time ? Date.new(time.year, time.month, time.day) : Date.parse(param)\n    rescue ArgumentError => e\n      raise CommandlineError, _(\"option '%{arg}' needs a date\") % { arg: arg }, e.backtrace\n    end\n\n    ## Print the help message to +stream+.\n    def educate stream = $stdout\n      width # just calculate it now; otherwise we have to be careful not to\n      # call this unless the cursor's at the beginning of a line.\n\n      left = {}\n      @specs.each do |name, spec|\n        left[name] = \"--#{spec[:long]}\" +\n                     (spec[:short] && spec[:short] != :none ? \", -#{spec[:short]}\" : \"\") +\n                     case spec[:type]\n                     when :flag; \"\"\n                     when :int; \" <i>\"\n                     when :ints; \" <i+>\"\n                     when :string; \" <s>\"\n                     when :strings; \" <s+>\"\n                     when :float; \" <f>\"\n                     when :floats; \" <f+>\"\n                     when :io; \" <filename/uri>\"\n                     when :ios; \" <filename/uri+>\"\n                     when :date; \" <date>\"\n                     when :dates; \" <date+>\"\n                     end\n      end\n\n      leftcol_width = left.values.map(&:length).max || 0\n      rightcol_start = leftcol_width + 6 # spaces\n\n      unless @order.size > 0 && @order.first.first == :text\n        stream.puts \"#{@version}\\n\" if @version\n        stream.puts _(\"Options:\")\n      end\n\n      @order.each do |what, opt|\n        if what == :text\n          stream.puts wrap(opt)\n          next\n        end\n\n        spec = @specs[opt]\n        stream.printf \"  %#{leftcol_width}s:   \", left[opt]\n        desc = spec[:desc] + begin\n          default_s = case spec[:default]\n                      when $stdout; \"<stdout>\"\n                      when $stdin; \"<stdin>\"\n                      when $stderr; \"<stderr>\"\n                      when Array\n                        spec[:default].join(\", \")\n                      else\n                        spec[:default].to_s\n                      end\n\n          if spec[:default]\n            if spec[:desc] =~ /\\.$/\n              _(\" (Default: %{default_s})\") % { default_s: default_s }\n            else\n              _(\" (default: %{default_s})\") % { default_s: default_s }\n            end\n          else\n            \"\"\n          end\n        end\n        stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)\n      end\n    end\n\n    def width # :nodoc:\n      @width ||= if $stdout.tty?\n                   begin\n                     require 'curses'\n                     Curses.init_screen\n                     x = Curses.cols\n                     Curses.close_screen\n                     x\n                   rescue Exception\n                     80\n                   end\n                 else\n                   80\n                 end\n    end\n\n    def wrap str, opts = {} # :nodoc:\n      if str == \"\"\n        [\"\"]\n      else\n        str.split(\"\\n\").map { |s| wrap_line s, opts }.flatten\n      end\n    end\n\n    ## The per-parser version of Trollop::die (see that for documentation).\n    def die arg, msg\n      if msg\n        $stderr.puts _(\"Error: argument --%{value0} %{msg}.\") % { value0: @specs[arg][:long], msg: msg }\n      else\n        $stderr.puts _(\"Error: %{arg}.\") % { arg: arg }\n      end\n      $stderr.puts _(\"Try --help for help.\")\n      exit(-1)\n    end\n\n    private\n\n    ## yield successive arg, parameter pairs\n    def each_arg args\n      remains = []\n      i = 0\n\n      until i >= args.length\n        if @stop_words.member? args[i]\n          remains += args[i..]\n          return remains\n        end\n        case args[i]\n        when /^--$/ # arg terminator\n          remains += args[(i + 1)..]\n          return remains\n        when /^--(\\S+?)=(.*)$/ # long argument with equals\n          yield \"--#{::Regexp.last_match(1)}\", [::Regexp.last_match(2)]\n          i += 1\n        when /^--(\\S+)$/ # long argument\n          params = collect_argument_parameters(args, i + 1)\n          if params.empty? # long argument no parameter\n            yield args[i], nil\n            i += 1\n          else\n            num_params_taken = yield args[i], params\n            unless num_params_taken\n              if @stop_on_unknown\n                remains += args[i + 1..]\n                return remains\n              else\n                remains += params\n              end\n            end\n            i += 1 + num_params_taken\n          end\n        when /^-(\\S+)$/ # one or more short arguments\n          shortargs = ::Regexp.last_match(1).split(//)\n          shortargs.each_with_index do |a, j|\n            if j == (shortargs.length - 1)\n              params = collect_argument_parameters(args, i + 1)\n              if params.empty? # argument no parameter\n                yield \"-#{a}\", nil\n                i += 1\n              else\n                num_params_taken = yield \"-#{a}\", params\n                unless num_params_taken\n                  if @stop_on_unknown\n                    remains += args[i + 1..]\n                    return remains\n                  else\n                    remains += params\n                  end\n                end\n                i += 1 + num_params_taken\n              end\n            else\n              yield \"-#{a}\", nil\n            end\n          end\n        else\n          if @stop_on_unknown\n            remains += args[i..]\n            return remains\n          else\n            remains << args[i]\n            i += 1\n          end\n        end\n      end\n\n      remains\n    end\n\n    def parse_integer_parameter param, arg\n      raise CommandlineError, _(\"option '%{arg}' needs an integer\") % { arg: arg } unless param =~ /^\\d+$/\n\n      param.to_i\n    end\n\n    def parse_float_parameter param, arg\n      raise CommandlineError, _(\"option '%{arg}' needs a floating-point number\") % { arg: arg } unless param =~ FLOAT_RE\n\n      param.to_f\n    end\n\n    def parse_io_parameter param, arg\n      case param\n      when /^(stdin|-)$/i; $stdin\n      else\n        require 'open-uri'\n        begin\n          URI.parse(param).open\n        rescue SystemCallError => e\n          raise CommandlineError, _(\"file or url for option '%{arg}' cannot be opened: %{value0}\") % { arg: arg, value0: e.message }, e.backtrace\n        end\n      end\n    end\n\n    def collect_argument_parameters args, start_at\n      params = []\n      pos = start_at\n      while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos])\n        params << args[pos]\n        pos += 1\n      end\n      params\n    end\n\n    def resolve_default_short_options\n      @order.each do |type, name|\n        next unless type == :opt\n\n        opts = @specs[name]\n        next if opts[:short]\n\n        c = opts[:long].split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }\n        if c # found a character to use\n          opts[:short] = c\n          @short[c] = name\n        end\n      end\n    end\n\n    def wrap_line str, opts = {}\n      prefix = opts[:prefix] || 0\n      width = opts[:width] || (self.width - 1)\n      start = 0\n      ret = []\n      until start > str.length\n        nextt =\n          if start + width >= str.length\n            str.length\n          else\n            x = str.rindex(/\\s/, start + width)\n            x = str.index(/\\s/, start) if x && x < start\n            x || str.length\n          end\n        ret << (ret.empty? ? \"\" : \" \" * prefix) + str[start...nextt]\n        start = nextt + 1\n      end\n      ret\n    end\n\n    ## instance_eval but with ability to handle block arguments\n    ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html\n    def cloaker &b\n      (class << self; self; end).class_eval do\n        define_method :cloaker_, &b\n        meth = instance_method :cloaker_\n        remove_method :cloaker_\n        meth\n      end\n    end\n  end\n\n  ## The easy, syntactic-sugary entry method into Trollop. Creates a Parser,\n  ## passes the block to it, then parses +args+ with it, handling any errors or\n  ## requests for help or version information appropriately (and then exiting).\n  ## Modifies +args+ in place. Returns a hash of option values.\n  ##\n  ## The block passed in should contain zero or more calls to +opt+\n  ## (Parser#opt), zero or more calls to +text+ (Parser#text), and\n  ## probably a call to +version+ (Parser#version).\n  ##\n  ## The returned block contains a value for every option specified with\n  ## +opt+.  The value will be the value given on the commandline, or the\n  ## default value if the option was not specified on the commandline. For\n  ## every option specified on the commandline, a key \"<option\n  ## name>_given\" will also be set in the hash.\n  ##\n  ## Example:\n  ##\n  ##   require 'trollop'\n  ##   opts = Trollop::options do\n  ##     opt :monkey, \"Use monkey mode\"                     # a flag --monkey, defaulting to false\n  ##     opt :goat, \"Use goat mode\", :default => true       # a flag --goat, defaulting to true\n  ##     opt :num_limbs, \"Number of limbs\", :default => 4   # an integer --num-limbs <i>, defaulting to 4\n  ##     opt :num_thumbs, \"Number of thumbs\", :type => :int # an integer --num-thumbs <i>, defaulting to nil\n  ##   end\n  ##\n  ##   ## if called with no arguments\n  ##   p opts # => { :monkey => false, :goat => true, :num_limbs => 4, :num_thumbs => nil }\n  ##\n  ##   ## if called with --monkey\n  ##   p opts # => {:monkey_given=>true, :monkey=>true, :goat=>true, :num_limbs=>4, :help=>false, :num_thumbs=>nil}\n  ##\n  ## See more examples at http://trollop.rubyforge.org.\n  def options args = ARGV, *a, &b\n    @last_parser = Parser.new(*a, &b)\n    with_standard_exception_handling(@last_parser) { @last_parser.parse args }\n  end\n\n  ## If Trollop::options doesn't do quite what you want, you can create a Parser\n  ## object and call Parser#parse on it. That method will throw CommandlineError,\n  ## HelpNeeded and VersionNeeded exceptions when necessary; if you want to\n  ## have these handled for you in the standard manner (e.g. show the help\n  ## and then exit upon an HelpNeeded exception), call your code from within\n  ## a block passed to this method.\n  ##\n  ## Note that this method will call System#exit after handling an exception!\n  ##\n  ## Usage example:\n  ##\n  ##   require 'trollop'\n  ##   p = Trollop::Parser.new do\n  ##     opt :monkey, \"Use monkey mode\"                     # a flag --monkey, defaulting to false\n  ##     opt :goat, \"Use goat mode\", :default => true       # a flag --goat, defaulting to true\n  ##   end\n  ##\n  ##   opts = Trollop::with_standard_exception_handling p do\n  ##     o = p.parse ARGV\n  ##     raise Trollop::HelpNeeded if ARGV.empty? # show help screen\n  ##     o\n  ##   end\n  ##\n  ## Requires passing in the parser object.\n\n  def with_standard_exception_handling parser\n    yield\n  rescue CommandlineError => e\n    $stderr.puts _(\"Error: %{value0}.\") % { value0: e.message }\n    $stderr.puts _(\"Try --help for help.\")\n    exit(-1)\n  rescue HelpNeeded\n    parser.educate\n    exit\n  rescue VersionNeeded\n    puts parser.version\n    exit\n  end\n\n  ## Informs the user that their usage of 'arg' was wrong, as detailed by\n  ## 'msg', and dies. Example:\n  ##\n  ##   options do\n  ##     opt :volume, :default => 0.0\n  ##   end\n  ##\n  ##   die :volume, \"too loud\" if opts[:volume] > 10.0\n  ##   die :volume, \"too soft\" if opts[:volume] < 0.1\n  ##\n  ## In the one-argument case, simply print that message, a notice\n  ## about -h, and die. Example:\n  ##\n  ##   options do\n  ##     opt :whatever # ...\n  ##   end\n  ##\n  ##   Trollop::die \"need at least one filename\" if ARGV.empty?\n  def die arg, msg = nil\n    if @last_parser\n      @last_parser.die arg, msg\n    else\n      # TRANSLATORS 'Trollop' is the name of a module and 'die' and 'options' are methods in it and should not be translated.\n      raise ArgumentError, _(\"Trollop::die can only be called after Trollop::options\")\n    end\n  end\n\n  module_function :options, :die, :with_standard_exception_handling\n  end # module\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/util/command_line.rb",
    "content": "# frozen_string_literal: true\n\n# Bundler and rubygems maintain a set of directories from which to\n# load gems. If Bundler is loaded, let it determine what can be\n# loaded. If it's not loaded, then use rubygems. But do this before\n# loading any puppet code, so that our gem loading system is sane.\nunless defined? ::Bundler\n  begin\n    require 'rubygems'\n  rescue LoadError\n  end\nend\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/util'\nrequire_relative '../../puppet/util/rubygems'\nrequire_relative '../../puppet/util/limits'\nrequire_relative '../../puppet/util/colors'\nrequire_relative '../../puppet/gettext/module_translations'\n\nmodule Puppet\n  module Util\n    # This is the main entry point for all puppet applications / faces; it\n    # is basically where the bootstrapping process / lifecycle of an app\n    # begins.\n    class CommandLine\n      include Puppet::Util::Limits\n\n      OPTION_OR_MANIFEST_FILE = /^-|\\.pp$/\n\n      # @param zero [String] the name of the executable\n      # @param argv [Array<String>] the arguments passed on the command line\n      # @param stdin [IO] (unused)\n      def initialize(zero = $PROGRAM_NAME, argv = ARGV, stdin = STDIN)\n        @command = File.basename(zero, '.rb')\n        @argv = argv\n      end\n\n      # @return [String] name of the subcommand is being executed\n      # @api public\n      def subcommand_name\n        return @command if @command != 'puppet'\n\n        if @argv.first =~ OPTION_OR_MANIFEST_FILE\n          nil\n        else\n          @argv.first\n        end\n      end\n\n      # @return [Array<String>] the command line arguments being passed to the subcommand\n      # @api public\n      def args\n        return @argv if @command != 'puppet'\n\n        if subcommand_name.nil?\n          @argv\n        else\n          @argv[1..]\n        end\n      end\n\n      # Run the puppet subcommand. If the subcommand is determined to be an\n      # external executable, this method will never return and the current\n      # process will be replaced via {Kernel#exec}.\n      #\n      # @return [void]\n      def execute\n        require_config = true\n        if @argv.first =~ /help|-h|--help|-V|--version/\n          require_config = false\n        end\n        Puppet::Util.exit_on_fail(_(\"Could not initialize global default settings\")) do\n          Puppet.initialize_settings(args, require_config)\n        end\n\n        setpriority(Puppet[:priority])\n\n        find_subcommand.run\n      end\n\n      # @api private\n      def external_subcommand\n        Puppet::Util.which(\"puppet-#{subcommand_name}\")\n      end\n\n      private\n\n      def find_subcommand\n        if subcommand_name.nil?\n          if args.include?(\"--help\") || args.include?(\"-h\")\n            ApplicationSubcommand.new(\"help\", CommandLine.new(\"puppet\", [\"help\"]))\n          else\n            NilSubcommand.new(self)\n          end\n        elsif Puppet::Application.available_application_names.include?(subcommand_name)\n          ApplicationSubcommand.new(subcommand_name, self)\n        else\n          path_to_subcommand = external_subcommand\n          if path_to_subcommand\n            ExternalSubcommand.new(path_to_subcommand, self)\n          else\n            UnknownSubcommand.new(subcommand_name, self)\n          end\n        end\n      end\n\n      # @api private\n      class ApplicationSubcommand\n        def initialize(subcommand_name, command_line)\n          @subcommand_name = subcommand_name\n          @command_line = command_line\n        end\n\n        def run\n          # For most applications, we want to be able to load code from the modulepath,\n          # such as apply, describe, resource, and faces.\n          # For agent and device in agent mode, we only want to load pluginsync'ed code from libdir.\n          # For master, we shouldn't ever be loading per-environment code into the master's\n          # ruby process, but that requires fixing (#17210, #12173, #8750). So for now\n          # we try to restrict to only code that can be autoloaded from the node's\n          # environment.\n\n          # PUP-2114 - at this point in the bootstrapping process we do not\n          # have an appropriate application-wide current_environment set.\n          # If we cannot find the configured environment, which may not exist,\n          # we do not attempt to add plugin directories to the load path.\n          unless @subcommand_name == 'master' || @subcommand_name == 'agent' || (@subcommand_name == 'device' && (['--apply', '--facts', '--resource'] - @command_line.args).empty?)\n            configured_environment = Puppet.lookup(:environments).get(Puppet[:environment])\n            if configured_environment\n              configured_environment.each_plugin_directory do |dir|\n                $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)\n              end\n\n              Puppet::ModuleTranslations.load_from_modulepath(configured_environment.modules)\n              Puppet::ModuleTranslations.load_from_vardir(Puppet[:vardir])\n\n              # Puppet requires Facter, which initializes its lookup paths. Reset Facter to\n              # pickup the new $LOAD_PATH.\n              Puppet.runtime[:facter].reset\n            end\n          end\n\n          app = Puppet::Application.find(@subcommand_name).new(@command_line)\n          app.run\n        end\n      end\n\n      # @api private\n      class ExternalSubcommand\n        def initialize(path_to_subcommand, command_line)\n          @path_to_subcommand = path_to_subcommand\n          @command_line = command_line\n        end\n\n        def run\n          Kernel.exec(@path_to_subcommand, *@command_line.args)\n        end\n      end\n\n      # @api private\n      class NilSubcommand\n        include Puppet::Util::Colors\n\n        def initialize(command_line)\n          @command_line = command_line\n        end\n\n        def run\n          args = @command_line.args\n          if args.include? \"--version\" or args.include? \"-V\"\n            puts Puppet.version\n          elsif @command_line.subcommand_name.nil? && args.count > 0\n            # If the subcommand is truly nil and there is an arg, it's an option; print out the invalid option message\n            puts colorize(:hred, _(\"Error: Could not parse application options: invalid option: %{opt}\") % { opt: args[0] })\n            exit 1\n          else\n            puts _(\"See 'puppet help' for help on available puppet subcommands\")\n          end\n        end\n      end\n\n      # @api private\n      class UnknownSubcommand < NilSubcommand\n        def initialize(subcommand_name, command_line)\n          @subcommand_name = subcommand_name\n          super(command_line)\n        end\n\n        def run\n          puts colorize(:hred, _(\"Error: Unknown Puppet subcommand '%{cmd}'\") % { cmd: @subcommand_name })\n          super\n          exit 1\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/constant_inflector.rb",
    "content": "# frozen_string_literal: true\n\n# Created on 2008-02-12\n# Copyright Luke Kanies\n\n# NOTE: I think it might be worth considering moving these methods directly into Puppet::Util.\n\n# A common module for converting between constants and\n# file names.\n\nmodule Puppet\n  module Util\n    module ConstantInflector\n      def file2constant(file)\n        file.split(\"/\").collect(&:capitalize).join(\"::\").gsub(/_+(.)/) { |_term| ::Regexp.last_match(1).capitalize }\n      end\n      module_function :file2constant\n\n      def constant2file(constant)\n        constant.to_s.gsub(/([a-z])([A-Z])/) { |_term| ::Regexp.last_match(1) + \"_#{::Regexp.last_match(2)}\" }.gsub(\"::\", \"/\").downcase\n      end\n      module_function :constant2file\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/diff.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'tempfile'\n\n# Provide a diff between two strings.\nmodule Puppet::Util::Diff\n  include Puppet::Util::Execution\n  require 'tempfile'\n\n  def diff(old, new)\n    diff_cmd = Puppet[:diff]\n    return '' unless diff_cmd && diff_cmd != \"\"\n\n    command = [diff_cmd]\n    args = Puppet[:diff_args]\n    if args && args != \"\"\n      args.split(' ').each do |arg|\n        command << arg\n      end\n    end\n    command << old << new\n    Puppet::Util::Execution.execute(command, :failonfail => false, :combine => false)\n  end\n\n  module_function :diff\n\n  # return diff string of two input strings\n  # format defaults to unified\n  # context defaults to 3 lines\n  def lcs_diff(data_old, data_new, format = :unified, context_lines = 3)\n    unless Puppet.features.diff?\n      Puppet.warning _(\"Cannot provide diff without the diff/lcs Ruby library\")\n      return \"\"\n    end\n    data_old = data_old.split(/\\n/).map!(&:chomp)\n    data_new = data_new.split(/\\n/).map!(&:chomp)\n\n    output = ''.dup\n\n    diffs = ::Diff::LCS.diff(data_old, data_new)\n    return output if diffs.empty?\n\n    oldhunk = hunk = nil\n    file_length_difference = 0\n\n    diffs.each do |piece|\n      hunk = ::Diff::LCS::Hunk.new(\n        data_old, data_new, piece,\n        context_lines,\n        file_length_difference\n      )\n      file_length_difference = hunk.file_length_difference\n      next unless oldhunk\n\n      # Hunks may overlap, which is why we need to be careful when our\n      # diff includes lines of context. Otherwise, we might print\n      # redundant lines.\n      if (context_lines > 0) and hunk.overlaps?(oldhunk)\n        hunk.unshift(oldhunk)\n      else\n        output << oldhunk.diff(format)\n      end\n    ensure\n      oldhunk = hunk\n      output << \"\\n\"\n    end\n\n    # Handle the last remaining hunk\n    output << oldhunk.diff(format) << \"\\n\"\n  end\n\n  def string_file_diff(path, string)\n    tempfile = Tempfile.new(\"puppet-diffing\")\n    tempfile.open\n    tempfile.print string\n    tempfile.close\n    notice \"\\n\" + diff(path, tempfile.path)\n    tempfile.delete\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/docs.rb",
    "content": "# frozen_string_literal: true\n\n# Some simple methods for helping manage automatic documentation generation.\nmodule Puppet::Util::Docs\n  # Specify the actual doc string.\n  def desc(str)\n    @doc = str\n  end\n\n  # Add a new autodoc block.  We have to define these as class methods,\n  # rather than just sticking them in a hash, because otherwise they're\n  # too difficult to do inheritance with.\n  def dochook(name, &block)\n    method = \"dochook_#{name}\"\n\n    meta_def method, &block\n  end\n\n  attr_writer :doc\n\n  # Generate the full doc string.\n  def doc\n    extra = methods.find_all { |m| m.to_s =~ /^dochook_.+/ }.sort.filter_map { |m|\n      send(m)\n    }.collect { |r| \"* #{r}\" }.join(\"\\n\")\n\n    if @doc\n      scrub(@doc) + (extra.empty? ? '' : \"\\n\\n#{extra}\")\n    else\n      extra\n    end\n  end\n\n  # Build a table\n  def doctable(headers, data)\n    str = \"\\n\\n\"\n\n    lengths = []\n    # Figure out the longest field for all columns\n    data.each do |name, values|\n      [name, values].flatten.each_with_index do |value, i|\n        lengths[i] ||= 0\n        lengths[i] = value.to_s.length if value.to_s.length > lengths[i]\n      end\n    end\n\n    # The headers could also be longest\n    headers.each_with_index do |value, i|\n      lengths[i] = value.to_s.length if value.to_s.length > lengths[i]\n    end\n\n    # Add the header names\n    str += headers.zip(lengths).collect { |value, num| pad(value, num) }.join(\" | \") + \" |\" + \"\\n\"\n\n    # And the header row\n    str += lengths.collect { |num| \"-\" * num }.join(\" | \") + \" |\" + \"\\n\"\n\n    # Now each data row\n    data.sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |name, rows|\n      str += [name, rows].flatten.zip(lengths).collect do |value, length|\n        pad(value, length)\n      end.join(\" | \") + \" |\" + \"\\n\"\n    end\n\n    str + \"\\n\"\n  end\n\n  # There is nothing that would ever set this. It gets read in reference/type.rb, but will never have any value but nil.\n  attr_reader :nodoc\n\n  def nodoc?\n    nodoc\n  end\n\n  # Pad a field with spaces\n  def pad(value, length)\n    value.to_s + (\" \" * (length - value.to_s.length))\n  end\n\n  HEADER_LEVELS = [nil, \"#\", \"##\", \"###\", \"####\", \"#####\"]\n\n  def markdown_header(name, level)\n    \"#{HEADER_LEVELS[level]} #{name}\\n\\n\"\n  end\n\n  def markdown_definitionlist(term, definition)\n    lines = scrub(definition).split(\"\\n\")\n    str = \"#{term}\\n: #{lines.shift}\\n\"\n    lines.each do |line|\n      str << \"  \" if line =~ /\\S/\n      str << \"#{line}\\n\"\n    end\n    str << \"\\n\"\n  end\n\n  # Strip indentation and trailing whitespace from embedded doc fragments.\n  #\n  # Multi-line doc fragments are sometimes indented in order to preserve the\n  # formatting of the code they're embedded in. Since indents are syntactic\n  # elements in Markdown, we need to make sure we remove any indent that was\n  # added solely to preserve surrounding code formatting, but LEAVE any indent\n  # that delineates a Markdown element (code blocks, multi-line bulleted list\n  # items). We can do this by removing the *least common indent* from each line.\n  #\n  # Least common indent is defined as follows:\n  #\n  # * Find the smallest amount of leading space on any line...\n  # * ...excluding the first line (which may have zero indent without affecting\n  #   the common indent)...\n  # * ...and excluding lines that consist solely of whitespace.\n  # * The least common indent may be a zero-length string, if the fragment is\n  #   not indented to match code.\n  # * If there are hard tabs for some dumb reason, we assume they're at least\n  #   consistent within this doc fragment.\n  #\n  # See tests in spec/unit/util/docs_spec.rb for examples.\n  def scrub(text)\n    # One-liners are easy! (One-liners may be buffered with extra newlines.)\n    return text.strip if text.strip !~ /\\n/\n\n    excluding_first_line = text.partition(\"\\n\").last\n    indent = excluding_first_line.scan(/^[ \\t]*(?=\\S)/).min || '' # prevent nil\n    # Clean hanging indent, if any\n    if indent.length > 0\n      text = text.gsub(/^#{indent}/, '')\n    end\n    # Clean trailing space\n    text.lines.map(&:rstrip).join(\"\\n\").rstrip\n  end\n\n  module_function :scrub\nend\n"
  },
  {
    "path": "lib/puppet/util/errors.rb",
    "content": "# frozen_string_literal: true\n\n# Some helper methods for throwing and populating errors.\n#\n# @api public\nmodule Puppet::Util::Errors\n  # Throw a Puppet::DevError with the specified message.  Used for unknown or\n  # internal application failures.\n  #\n  # @param msg [String] message used in raised error\n  # @raise [Puppet::DevError] always raised with the supplied message\n  def devfail(msg)\n    self.fail(Puppet::DevError, msg)\n  end\n\n  # Add line and file info to the supplied exception if info is available from\n  # this object, is appropriately populated and the supplied exception supports\n  # it.  When other is supplied, the backtrace will be copied to the error\n  # object and the 'original' will be dropped from the error.\n  #\n  # @param error [Exception] exception that is populated with info\n  # @param other [Exception] original exception, source of backtrace info\n  # @return [Exception] error parameter\n  def adderrorcontext(error, other = nil)\n    error.line ||= line if error.respond_to?(:line=) and respond_to?(:line) and line\n    error.file ||= file if error.respond_to?(:file=) and respond_to?(:file) and file\n    error.original ||= other if error.respond_to?(:original=)\n\n    error.set_backtrace(other.backtrace) if other and other.respond_to?(:backtrace)\n    # It is not meaningful to keep the wrapped exception since its backtrace has already\n    # been adopted by the error. (The instance variable is private for good reasons).\n    error.instance_variable_set(:@original, nil)\n    error\n  end\n\n  # Return a human-readable string of this object's file, line, and pos attributes,\n  # if set.\n  #\n  # @param file [String] the file path for the error (nil or \"\", for not known)\n  # @param line [String] the line number for the error (nil or \"\", for not known)\n  # @param column [String] the column number for the error (nil or \"\",  for not known)\n  # @return [String] description of file, line, and column\n  #\n  def self.error_location(file, line = nil, column = nil)\n    file = nil if file.is_a?(String) && file.empty?\n    line = nil if line.is_a?(String) && line.empty?\n    column = nil if column.is_a?(String) && column.empty?\n    if file and line and column\n      _(\"(file: %{file}, line: %{line}, column: %{column})\") % { file: file, line: line, column: column }\n    elsif file and line\n      _(\"(file: %{file}, line: %{line})\") % { file: file, line: line }\n    elsif line and column\n      _(\"(line: %{line}, column: %{column})\") % { line: line, column: column }\n    elsif line\n      _(\"(line: %{line})\") % { line: line }\n    elsif file\n      _(\"(file: %{file})\") % { file: file }\n    else\n      ''\n    end\n  end\n\n  # Return a human-readable string of this object's file, line, and pos attributes,\n  # with a proceeding space in the output\n  # if set.\n  #\n  # @param file [String] the file path for the error (nil or \"\", for not known)\n  # @param line [String] the line number for the error (nil or \"\", for not known)\n  # @param column [String] the column number for the error (nil or \"\",  for not known)\n  # @return [String] description of file, line, and column\n  #\n  def self.error_location_with_space(file, line = nil, column = nil)\n    error_location_str = error_location(file, line, column)\n    if error_location_str.empty?\n      ''\n    else\n      ' ' + error_location_str\n    end\n  end\n\n  # Return a human-readable string of this object's file and line\n  # where unknown entries are listed as 'unknown'\n  #\n  # @param file [String] the file path for the error (nil or \"\", for not known)\n  # @param line [String] the line number for the error (nil or \"\", for not known)\n  # @return [String] description of file, and line\n  def self.error_location_with_unknowns(file, line)\n    file = nil if file.is_a?(String) && file.empty?\n    line = nil if line.is_a?(String) && line.empty?\n    file ||= _('unknown')\n    line ||= _('unknown')\n    error_location(file, line)\n  end\n\n  # Return a human-readable string of this object's file and line attributes,\n  # if set.\n  #\n  # @return [String] description of file and line with a leading space\n  def error_context\n    Puppet::Util::Errors.error_location_with_space(file, line)\n  end\n\n  # Wrap a call in such a way that we always throw the right exception and keep\n  # as much context as possible.\n  #\n  # @param options [Hash<Symbol,Object>] options used to create error\n  # @option options [Class] :type error type to raise, defaults to\n  #   Puppet::DevError\n  # @option options [String] :message message to use in error, default mentions\n  #   the name of this class\n  # @raise [Puppet::Error] re-raised with extra context if the block raises it\n  # @raise [Error] of type options[:type], when the block raises other\n  #   exceptions\n  def exceptwrap(options = {})\n    options[:type] ||= Puppet::DevError\n    begin\n      return yield\n    rescue Puppet::Error => detail\n      raise adderrorcontext(detail)\n    rescue => detail\n      message = options[:message] || _(\"%{klass} failed with error %{error_type}: %{detail}\") % { klass: self.class, error_type: detail.class, detail: detail }\n\n      error = options[:type].new(message)\n      # We can't use self.fail here because it always expects strings,\n      # not exceptions.\n      raise adderrorcontext(error, detail)\n    end\n\n    retval\n  end\n\n  # Throw an error, defaulting to a Puppet::Error.\n  #\n  # @overload fail(message, ..)\n  #   Throw a Puppet::Error with a message concatenated from the given\n  #   arguments.\n  #   @param [String] message error message(s)\n  # @overload fail(error_klass, message, ..)\n  #   Throw an exception of type error_klass with a message concatenated from\n  #   the given arguments.\n  #   @param [Class] type of error\n  #   @param [String] message error message(s)\n  # @overload fail(error_klass, message, ..)\n  #   Throw an exception of type error_klass with a message concatenated from\n  #   the given arguments.\n  #   @param [Class] type of error\n  #   @param [String] message error message(s)\n  #   @param [Exception] original exception, source of backtrace info\n  def fail(*args)\n    if args[0].is_a?(Class)\n      type = args.shift\n    else\n      type = Puppet::Error\n    end\n\n    other = args.count > 1 ? args.pop : nil\n    error = adderrorcontext(type.new(args.join(\" \")), other)\n\n    raise error\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/execution.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'timeout'\nrequire_relative '../../puppet/file_system/uniquefile'\n\nmodule Puppet\n  require 'rbconfig'\n\n  require_relative '../../puppet/error'\n  # A command failed to execute.\n  # @api public\n  class ExecutionFailure < Puppet::Error\n  end\nend\n\n# This module defines methods for execution of system commands. It is intended for inclusion\n# in classes that needs to execute system commands.\n# @api public\nmodule Puppet::Util::Execution\n  # This is the full output from a process. The object itself (a String) is the\n  # stdout of the process.\n  #\n  # @api public\n  class ProcessOutput < String\n    # @return [Integer] The exit status of the process\n    # @api public\n    attr_reader :exitstatus\n\n    # @api private\n    def initialize(value, exitstatus)\n      super(value)\n      @exitstatus = exitstatus\n    end\n  end\n\n  # The command can be a simple string, which is executed as-is, or an Array,\n  # which is treated as a set of command arguments to pass through.\n  #\n  # In either case, the command is passed directly to the shell, STDOUT and\n  # STDERR are connected together, and STDOUT will be streamed to the yielded\n  # pipe.\n  #\n  # @param command [String, Array<String>] the command to execute as one string,\n  #   or as parts in an array. The parts of the array are joined with one\n  #   separating space between each entry when converting to the command line\n  #   string to execute.\n  # @param failonfail [Boolean] (true) if the execution should fail with\n  #   Exception on failure or not.\n  # @yield [pipe] to a block executing a subprocess\n  # @yieldparam pipe [IO] the opened pipe\n  # @yieldreturn [String] the output to return\n  # @raise [Puppet::ExecutionFailure] if the executed child process did not\n  #   exit with status == 0 and `failonfail` is `true`.\n  # @return [String] a string with the output from the subprocess executed by\n  #   the given block\n  #\n  # @see Kernel#open for `mode` values\n  # @api public\n  def self.execpipe(command, failonfail = true)\n    # Paste together an array with spaces.  We used to paste directly\n    # together, no spaces, which made for odd invocations; the user had to\n    # include whitespace between arguments.\n    #\n    # Having two spaces is really not a big drama, since this passes to the\n    # shell anyhow, while no spaces makes for a small developer cost every\n    # time this is invoked. --daniel 2012-02-13\n    command_str = command.respond_to?(:join) ? command.join(' ') : command\n\n    if respond_to? :debug\n      debug \"Executing '#{command_str}'\"\n    else\n      Puppet.debug { \"Executing '#{command_str}'\" }\n    end\n\n    # force the run of the command with\n    # the user/system locale to \"C\" (via environment variables LANG and LC_*)\n    # it enables to have non localized output for some commands and therefore\n    # a predictable output\n    english_env = ENV.to_hash.merge({ 'LANG' => 'C', 'LC_ALL' => 'C' })\n    output = Puppet::Util.withenv(english_env) do\n      # We are intentionally using 'pipe' with open to launch a process\n      open(\"| #{command_str} 2>&1\") do |pipe| # rubocop:disable Security/Open\n        yield pipe\n      end\n    end\n\n    if failonfail && exitstatus != 0\n      raise Puppet::ExecutionFailure, output.to_s\n    end\n\n    output\n  end\n\n  def self.exitstatus\n    $CHILD_STATUS.exitstatus\n  end\n  private_class_method :exitstatus\n\n  # Default empty options for {execute}\n  NoOptionsSpecified = {}\n\n  # Executes the desired command, and return the status and output.\n  # def execute(command, options)\n  # @param command [Array<String>, String] the command to execute. If it is\n  #   an Array the first element should be the executable and the rest of the\n  #   elements should be the individual arguments to that executable.\n  # @param options [Hash] a Hash of options\n  # @option options [String] :cwd the directory from which to run the command. Raises an error if the directory does not exist.\n  #   This option is only available on the agent. It cannot be used on the master, meaning it cannot be used in, for example,\n  #   regular functions, hiera backends, or report processors.\n  # @option options [Boolean]  :failonfail if this value is set to true, then this method will raise an error if the\n  #   command is not executed successfully.\n  # @option options [Integer, String] :uid (nil) the user id of the user that the process should be run as. Will be ignored if the\n  #   user id matches the effective user id of the current process.\n  # @option options [Integer, String] :gid (nil) the group id of the group that the process should be run as. Will be ignored if the\n  #   group id matches the effective group id of the current process.\n  # @option options [Boolean] :combine sets whether or not to combine stdout/stderr in the output, if false stderr output is discarded\n  # @option options [String] :stdinfile (nil) sets a file that can be used for stdin. Passing a string for stdin is not currently\n  #   supported.\n  # @option options [Boolean] :squelch (false) if true, ignore stdout / stderr completely.\n  # @option options [Boolean] :override_locale (true) by default (and if this option is set to true), we will temporarily override\n  #   the user/system locale to \"C\" (via environment variables LANG and LC_*) while we are executing the command.\n  #   This ensures that the output of the command will be formatted consistently, making it predictable for parsing.\n  #   Passing in a value of false for this option will allow the command to be executed using the user/system locale.\n  # @option options [Hash<{String => String}>] :custom_environment ({}) a hash of key/value pairs to set as environment variables for the duration\n  #   of the command.\n  # @return [Puppet::Util::Execution::ProcessOutput] output as specified by options\n  # @raise [Puppet::ExecutionFailure] if the executed chiled process did not exit with status == 0 and `failonfail` is\n  #   `true`.\n  # @note Unfortunately, the default behavior for failonfail and combine (since\n  #   0.22.4 and 0.24.7, respectively) depend on whether options are specified\n  #   or not. If specified, then failonfail and combine default to false (even\n  #   when the options specified are neither failonfail nor combine). If no\n  #   options are specified, then failonfail and combine default to true.\n  # @comment See commits efe9a833c and d32d7f30\n  # @api public\n  #\n  def self.execute(command, options = NoOptionsSpecified)\n    # specifying these here rather than in the method signature to allow callers to pass in a partial\n    # set of overrides without affecting the default values for options that they don't pass in\n    default_options = {\n      :failonfail => NoOptionsSpecified.equal?(options),\n      :uid => nil,\n      :gid => nil,\n      :combine => NoOptionsSpecified.equal?(options),\n      :stdinfile => nil,\n      :squelch => false,\n      :override_locale => true,\n      :custom_environment => {},\n      :sensitive => false,\n      :suppress_window => false,\n    }\n\n    options = default_options.merge(options)\n\n    case command\n    when Array\n      command = command.flatten.map(&:to_s)\n      command_str = command.join(\" \")\n    when String\n      command_str = command\n    end\n\n    # do this after processing 'command' array or string\n    command_str = '[redacted]' if options[:sensitive]\n\n    user_log_s = ''.dup\n    if options[:uid]\n      user_log_s << \" uid=#{options[:uid]}\"\n    end\n    if options[:gid]\n      user_log_s << \" gid=#{options[:gid]}\"\n    end\n    if user_log_s != ''\n      user_log_s.prepend(' with')\n    end\n\n    if respond_to? :debug\n      debug \"Executing#{user_log_s}: '#{command_str}'\"\n    else\n      Puppet.debug { \"Executing#{user_log_s}: '#{command_str}'\" }\n    end\n\n    null_file = Puppet::Util::Platform.windows? ? 'NUL' : '/dev/null'\n\n    cwd = options[:cwd]\n    if cwd && !Puppet::FileSystem.directory?(cwd)\n      raise ArgumentError, _(\"Working directory %{cwd} does not exist!\") % { cwd: cwd }\n    end\n\n    begin\n      stdin = Puppet::FileSystem.open(options[:stdinfile] || null_file, nil, 'r')\n      # On Windows, continue to use the file-based approach to avoid breaking people's existing\n      # manifests. If they use a script that doesn't background cleanly, such as\n      # `start /b ping 127.0.0.1`, we couldn't handle it with pipes as there's no non-blocking\n      # read available.\n      if options[:squelch]\n        stdout = Puppet::FileSystem.open(null_file, nil, 'w')\n      elsif Puppet.features.posix?\n        reader, stdout = IO.pipe\n      else\n        stdout = Puppet::FileSystem::Uniquefile.new('puppet')\n      end\n      stderr = options[:combine] ? stdout : Puppet::FileSystem.open(null_file, nil, 'w')\n\n      exec_args = [command, options, stdin, stdout, stderr]\n      output = ''.dup\n\n      # We close stdin/stdout/stderr immediately after fork/exec as they're no longer needed by\n      # this process. In most cases they could be closed later, but when `stdout` is the \"writer\"\n      # pipe we must close it or we'll never reach eof on the `reader` pipe.\n      execution_stub = Puppet::Util::ExecutionStub.current_value\n      if execution_stub\n        child_pid = execution_stub.call(*exec_args)\n        [stdin, stdout, stderr].each { |io|\n          begin\n            io.close\n          rescue\n            nil\n          end\n        }\n        return child_pid\n      elsif Puppet.features.posix?\n        child_pid = nil\n        begin\n          child_pid = execute_posix(*exec_args)\n          [stdin, stdout, stderr].each { |io|\n            begin\n              io.close\n            rescue\n              nil\n            end\n          }\n          if options[:squelch]\n            exit_status = Process.waitpid2(child_pid).last.exitstatus\n          else\n            # Use non-blocking read to check for data. After each attempt,\n            # check whether the child is done. This is done in case the child\n            # forks and inherits stdout, as happens in `foo &`.\n            # If we encounter EOF, though, then switch to a blocking wait for\n            # the child; after EOF, IO.select will never block and the loop\n            # below will use maximum CPU available.\n\n            wait_flags = Process::WNOHANG\n            until results = Process.waitpid2(child_pid, wait_flags) # rubocop:disable Lint/AssignmentInCondition\n\n              # If not done, wait for data to read with a timeout\n              # This timeout is selected to keep activity low while waiting on\n              # a long process, while not waiting too long for the pathological\n              # case where stdout is never closed.\n              ready = IO.select([reader], [], [], 0.1)\n              begin\n                output << reader.read_nonblock(4096) if ready\n              rescue Errno::EAGAIN\n              rescue EOFError\n                wait_flags = 0\n              end\n            end\n\n            # Read any remaining data. Allow for but don't expect EOF.\n            begin\n              loop do\n                output << reader.read_nonblock(4096)\n              end\n            rescue Errno::EAGAIN\n            rescue EOFError\n            end\n\n            # Force to external encoding to preserve prior behavior when reading a file.\n            # Wait until after reading all data so we don't encounter corruption when\n            # reading part of a multi-byte unicode character if default_external is UTF-8.\n            output.force_encoding(Encoding.default_external)\n            exit_status = results.last.exitstatus\n          end\n          child_pid = nil\n        rescue Timeout::Error => e\n          # NOTE: For Ruby 2.1+, an explicit Timeout::Error class has to be\n          # passed to Timeout.timeout in order for there to be something for\n          # this block to rescue.\n          unless child_pid.nil?\n            Process.kill(:TERM, child_pid)\n            # Spawn a thread to reap the process if it dies.\n            Thread.new { Process.waitpid(child_pid) }\n          end\n\n          raise e\n        end\n      elsif Puppet::Util::Platform.windows?\n        process_info = execute_windows(*exec_args)\n        begin\n          [stdin, stderr].each { |io|\n            begin\n              io.close\n            rescue\n              nil\n            end\n          }\n          exit_status = Puppet::Util::Windows::Process.wait_process(process_info.process_handle)\n\n          # read output in if required\n          unless options[:squelch]\n            output = wait_for_output(stdout)\n            Puppet.warning _(\"Could not get output\") unless output\n          end\n        ensure\n          FFI::WIN32.CloseHandle(process_info.process_handle)\n          FFI::WIN32.CloseHandle(process_info.thread_handle)\n        end\n      end\n\n      if options[:failonfail] and exit_status != 0\n        raise Puppet::ExecutionFailure, _(\"Execution of '%{str}' returned %{exit_status}: %{output}\") % { str: command_str, exit_status: exit_status, output: output.strip }\n      end\n    ensure\n      # Make sure all handles are closed in case an exception was thrown attempting to execute.\n      [stdin, stdout, stderr].each { |io|\n        begin\n          io.close\n        rescue\n          nil\n        end\n      }\n      unless options[:squelch]\n        # if we opened a pipe, we need to clean it up.\n        reader.close if reader\n        stdout.close! if stdout && Puppet::Util::Platform.windows?\n      end\n    end\n\n    Puppet::Util::Execution::ProcessOutput.new(output || '', exit_status)\n  end\n\n  # Returns the path to the ruby executable (available via Config object, even if\n  # it's not in the PATH... so this is slightly safer than just using Puppet::Util.which)\n  # @return [String] the path to the Ruby executable\n  # @api private\n  #\n  def self.ruby_path\n    File.join(RbConfig::CONFIG['bindir'],\n              RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT'])\n        .sub(/.*\\s.*/m, '\"\\&\"')\n  end\n\n  # Because some modules provide their own version of this method.\n  class << self\n    alias util_execute execute\n  end\n\n  # This is private method.\n  # @comment see call to private_class_method after method definition\n  # @api private\n  #\n  def self.execute_posix(command, options, stdin, stdout, stderr)\n    Puppet::Util.safe_posix_fork(stdin, stdout, stderr) do\n      # We can't just call Array(command), and rely on it returning\n      # things like ['foo'], when passed ['foo'], because\n      # Array(command) will call command.to_a internally, which when\n      # given a string can end up doing Very Bad Things(TM), such as\n      # turning \"/tmp/foo;\\r\\n /bin/echo\" into [\"/tmp/foo;\\r\\n\", \" /bin/echo\"]\n      command = [command].flatten\n      Process.setsid\n      begin\n        # We need to chdir to our cwd before changing privileges as there's a\n        # chance that the user may not have permissions to access the cwd, which\n        # would cause execute_posix to fail.\n        cwd = options[:cwd]\n        Dir.chdir(cwd) if cwd\n\n        Puppet::Util::SUIDManager.change_privileges(options[:uid], options[:gid], true)\n\n        # if the caller has requested that we override locale environment variables,\n        if options[:override_locale] then\n          # loop over them and clear them\n          Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |name| ENV.delete(name) }\n          # set LANG and LC_ALL to 'C' so that the command will have consistent, predictable output\n          # it's OK to manipulate these directly rather than, e.g., via \"withenv\", because we are in\n          # a forked process.\n          ENV['LANG'] = 'C'\n          ENV['LC_ALL'] = 'C'\n        end\n\n        # unset all of the user-related environment variables so that different methods of starting puppet\n        # (automatic start during boot, via 'service', via /etc/init.d, etc.) won't have unexpected side\n        # effects relating to user / home dir environment vars.\n        # it's OK to manipulate these directly rather than, e.g., via \"withenv\", because we are in\n        # a forked process.\n        Puppet::Util::POSIX::USER_ENV_VARS.each { |name| ENV.delete(name) }\n\n        options[:custom_environment] ||= {}\n        Puppet::Util.withenv(options[:custom_environment]) do\n          Kernel.exec(*command)\n        end\n      rescue => detail\n        Puppet.log_exception(detail, _(\"Could not execute posix command: %{detail}\") % { detail: detail })\n        exit!(1)\n      end\n    end\n  end\n  private_class_method :execute_posix\n\n  # This is private method.\n  # @comment see call to private_class_method after method definition\n  # @api private\n  #\n  def self.execute_windows(command, options, stdin, stdout, stderr)\n    command = command.map do |part|\n      part.include?(' ') ? %Q(\"#{part.gsub(/\"/, '\\\"')}\") : part\n    end.join(\" \") if command.is_a?(Array)\n\n    options[:custom_environment] ||= {}\n    Puppet::Util.withenv(options[:custom_environment]) do\n      Puppet::Util::Windows::Process.execute(command, options, stdin, stdout, stderr)\n    end\n  end\n  private_class_method :execute_windows\n\n  # This is private method.\n  # @comment see call to private_class_method after method definition\n  # @api private\n  #\n  def self.wait_for_output(stdout)\n    # Make sure the file's actually been written.  This is basically a race\n    # condition, and is probably a horrible way to handle it, but, well, oh\n    # well.\n    # (If this method were treated as private / inaccessible from outside of this file, we shouldn't have to worry\n    #  about a race condition because all of the places that we call this from are preceded by a call to \"waitpid2\",\n    #  meaning that the processes responsible for writing the file have completed before we get here.)\n    2.times do |try|\n      if Puppet::FileSystem.exist?(stdout.path)\n        stdout.open\n        begin\n          return stdout.read\n        ensure\n          stdout.close\n          stdout.unlink\n        end\n      else\n        time_to_sleep = try / 2.0\n        Puppet.warning _(\"Waiting for output; will sleep %{time_to_sleep} seconds\") % { time_to_sleep: time_to_sleep }\n        sleep(time_to_sleep)\n      end\n    end\n    nil\n  end\n  private_class_method :wait_for_output\nend\n"
  },
  {
    "path": "lib/puppet/util/execution_stub.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util\n  class ExecutionStub\n    class << self\n      # Set a stub block that Puppet::Util::Execution.execute() should invoke instead\n      # of actually executing commands on the target machine.  Intended\n      # for spec testing.\n      #\n      # The arguments passed to the block are |command, options|, where\n      # command is an array of strings and options is an options hash.\n      def set(&block)\n        @value = block\n      end\n\n      # Uninstall any execution stub, so that calls to\n      # Puppet::Util::Execution.execute() behave normally again.\n      def reset\n        @value = nil\n      end\n\n      # Retrieve the current execution stub, or nil if there is no stub.\n      def current_value\n        @value\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/feature.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/util/warnings'\n\nclass Puppet::Util::Feature\n  include Puppet::Util::Warnings\n\n  attr_reader :path\n\n  # Create a new feature test. You have to pass the feature name, and it must be\n  # unique. You can pass a block to determine if the feature is present:\n  #\n  #     Puppet.features.add(:myfeature) do\n  #       # return true or false if feature is available\n  #       # return nil if feature may become available later\n  #     end\n  #\n  # The block should return true if the feature is available, false if it is\n  # not, or nil if the state is unknown. True and false values will be cached. A\n  # nil value will not be cached, and should be used if the feature may become\n  # true in the future.\n  #\n  # Features are often used to detect if a ruby library is installed. To support\n  # that common case, you can pass one or more ruby libraries, and the feature\n  # will be true if all of the libraries load successfully:\n  #\n  #     Puppet.features.add(:myfeature, libs: 'mylib')\n  #     Puppet.features.add(:myfeature, libs: ['mylib', 'myotherlib'])\n  #\n  # If the ruby library is not installed, then the failure is not cached, as\n  # it's assumed puppet may install the gem during catalog application.\n  #\n  # If a feature is defined using `:libs` and a block, then the block is\n  # used and the `:libs` are ignored.\n  #\n  # Puppet evaluates the feature test when the `Puppet.features.myfeature?`\n  # method is called. If the feature test was defined using a block and the\n  # block returns nil, then the feature test will be re-evaluated the next time\n  # `Puppet.features.myfeature?` is called.\n  #\n  # @param [Symbol] name The unique feature name\n  # @param [Hash<Symbol,Array<String>>] options The libraries to load\n  def add(name, options = {}, &block)\n    method = name.to_s + \"?\"\n    @results.delete(name)\n\n    meta_def(method) do\n      # we return a cached result if:\n      #  * if we've tested this feature before\n      #  AND\n      #    * the result was true/false\n      #    OR\n      #    * we're configured to never retry\n      unless @results.has_key?(name) &&\n             (!@results[name].nil? || !Puppet[:always_retry_plugins])\n        @results[name] = test(name, options, &block)\n      end\n      !!@results[name]\n    end\n  end\n\n  # Create a new feature collection.\n  def initialize(path)\n    @path = path\n    @results = {}\n    @loader = Puppet::Util::Autoload.new(self, @path)\n  end\n\n  def load\n    @loader.loadall(Puppet.lookup(:current_environment))\n  end\n\n  def method_missing(method, *args)\n    return super unless method.to_s =~ /\\?$/\n\n    feature = method.to_s.sub(/\\?$/, '')\n    @loader.load(feature, Puppet.lookup(:current_environment))\n\n    respond_to?(method) && send(method)\n  end\n\n  # Actually test whether the feature is present.  We only want to test when\n  # someone asks for the feature, so we don't unnecessarily load\n  # files.\n  def test(name, options, &block)\n    if block_given?\n      begin\n        result = yield\n      rescue StandardError, ScriptError => detail\n        warn _(\"Failed to load feature test for %{name}: %{detail}\") % { name: name, detail: detail }\n        result = nil\n      end\n      @results[name] = result\n      result\n    else\n      libs = options[:libs]\n      if libs\n        libs = [libs] unless libs.is_a?(Array)\n        libs.all? { |lib| load_library(lib, name) } ? true : nil\n      else\n        true\n      end\n    end\n  end\n\n  private\n\n  def load_library(lib, name)\n    raise ArgumentError, _(\"Libraries must be passed as strings not %{klass}\") % { klass: lib.class } unless lib.is_a?(String)\n\n    @rubygems ||= Puppet::Util::RubyGems::Source.new\n    @rubygems.clear_paths\n\n    begin\n      require lib\n      true\n    rescue LoadError\n      # Expected case. Required library insn't installed.\n      debug_once(_(\"Could not find library '%{lib}' required to enable feature '%{name}'\") %\n        { lib: lib, name: name })\n      false\n    rescue StandardError, ScriptError => detail\n      debug_once(_(\"Exception occurred while loading library '%{lib}' required to enable feature '%{name}': %{detail}\") %\n        { lib: lib, name: name, detail: detail })\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/file_watcher.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Util::FileWatcher\n  include Enumerable\n\n  def each(&blk)\n    @files.keys.each(&blk)\n  end\n\n  def initialize\n    @files = {}\n  end\n\n  def changed?\n    @files.values.any?(&:changed?)\n  end\n\n  def watch(filename)\n    return if watching?(filename)\n\n    @files[filename] = Puppet::Util::WatchedFile.new(filename)\n  end\n\n  def watching?(filename)\n    @files.has_key?(filename)\n  end\n\n  def clear\n    @files.clear\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/fileparsing.rb",
    "content": "# frozen_string_literal: true\n\n# A mini-language for parsing files.  This is only used file the ParsedFile\n# provider, but it makes more sense to split it out so it's easy to maintain\n# in one place.\n#\n# You can use this module to create simple parser/generator classes.  For instance,\n# the following parser should go most of the way to parsing /etc/passwd:\n#\n#   class Parser\n#       include Puppet::Util::FileParsing\n#       record_line :user, :fields => %w{name password uid gid gecos home shell},\n#           :separator => \":\"\n#   end\n#\n# You would use it like this:\n#\n#   parser = Parser.new\n#   lines = parser.parse(File.read(\"/etc/passwd\"))\n#\n#   lines.each do |type, hash| # type will always be :user, since we only have one\n#       p hash\n#   end\n#\n# Each line in this case would be a hash, with each field set appropriately.\n# You could then call 'parser.to_line(hash)' on any of those hashes to generate\n# the text line again.\n\nmodule Puppet::Util::FileParsing\n  include Puppet::Util\n  attr_writer :line_separator, :trailing_separator\n\n  class FileRecord\n    include Puppet::Util\n    attr_accessor :absent, :joiner, :rts, :separator, :rollup, :name, :match, :block_eval\n\n    attr_reader :fields, :optional, :type\n\n    INVALID_FIELDS = [:record_type, :target, :on_disk]\n\n    # Customize this so we can do a bit of validation.\n    def fields=(fields)\n      @fields = fields.collect do |field|\n        r = field.intern\n        raise ArgumentError, _(\"Cannot have fields named %{name}\") % { name: r } if INVALID_FIELDS.include?(r)\n\n        r\n      end\n    end\n\n    def initialize(type,\n                   absent: nil,\n                   block_eval: nil,\n                   fields: nil,\n                   joiner: nil,\n                   match: nil,\n                   optional: nil,\n                   post_parse: nil,\n                   pre_gen: nil,\n                   rollup: nil,\n                   rts: nil,\n                   separator: nil,\n                   to_line: nil,\n                   &block)\n      @type = type.intern\n      raise ArgumentError, _(\"Invalid record type %{record_type}\") % { record_type: @type } unless [:record, :text].include?(@type)\n\n      @absent = absent\n      @block_eval = block_eval\n      @joiner = joiner\n      @match = match\n      @rollup = rollup if rollup\n      @rts = rts\n      @separator = separator\n\n      self.fields = fields if fields\n      self.optional = optional if optional\n      self.post_parse = post_parse if post_parse\n      self.pre_gen = pre_gen if pre_gen\n      self.to_line = to_line if to_line\n\n      if self.type == :record\n        # Now set defaults.\n        self.absent ||= \"\"\n        self.separator ||= /\\s+/\n        self.joiner ||= \" \"\n        self.optional ||= []\n        @rollup = true unless defined?(@rollup)\n      end\n\n      if block_given?\n        @block_eval ||= :process\n\n        # Allow the developer to specify that a block should be instance-eval'ed.\n        if @block_eval == :instance\n          instance_eval(&block)\n        else\n          meta_def(@block_eval, &block)\n        end\n      end\n    end\n\n    # Convert a record into a line by joining the fields together appropriately.\n    # This is pulled into a separate method so it can be called by the hooks.\n    def join(details)\n      joinchar = self.joiner\n\n      fields.filter_map { |field|\n        # If the field is marked absent, use the appropriate replacement\n        if details[field] == :absent or details[field] == [:absent] or details[field].nil?\n          if self.optional.include?(field)\n            self.absent\n          else\n            raise ArgumentError, _(\"Field '%{field}' is required\") % { field: field }\n          end\n        else\n          details[field].to_s\n        end\n      }.join(joinchar)\n    end\n\n    # Customize this so we can do a bit of validation.\n    def optional=(optional)\n      @optional = optional.collect(&:intern)\n    end\n\n    # Create a hook that modifies the hash resulting from parsing.\n    def post_parse=(block)\n      meta_def(:post_parse, &block)\n    end\n\n    # Create a hook that modifies the hash just prior to generation.\n    def pre_gen=(block)\n      meta_def(:pre_gen, &block)\n    end\n\n    # Are we a text type?\n    def text?\n      type == :text\n    end\n\n    def to_line=(block)\n      meta_def(:to_line, &block)\n    end\n  end\n\n  # Clear all existing record definitions.  Only used for testing.\n  def clear_records\n    @record_types.clear\n    @record_order.clear\n  end\n\n  def fields(type)\n    record = record_type(type)\n    if record\n      record.fields.dup\n    else\n      nil\n    end\n  end\n\n  # Try to match a specific text line.\n  def handle_text_line(line, record)\n    line =~ record.match ? { :record_type => record.name, :line => line } : nil\n  end\n\n  # Try to match a record.\n  #\n  # @param [String] line The line to be parsed\n  # @param [Puppet::Util::FileType] record The filetype to use for parsing\n  #\n  # @return [Hash<Symbol, Object>] The parsed elements of the line\n  def handle_record_line(line, record)\n    ret = nil\n    if record.respond_to?(:process)\n      ret = record.send(:process, line.dup)\n      if ret\n        unless ret.is_a?(Hash)\n          raise Puppet::DevError, _(\"Process record type %{record_name} returned non-hash\") % { record_name: record.name }\n        end\n      else\n        return nil\n      end\n    else\n      regex = record.match\n      if regex\n        # In this case, we try to match the whole line and then use the\n        # match captures to get our fields.\n        match = regex.match(line)\n        if match\n          ret = {}\n          record.fields.zip(match.captures).each do |field, value|\n            if value == record.absent\n              ret[field] = :absent\n            else\n              ret[field] = value\n            end\n          end\n        else\n          nil\n        end\n      else\n        ret = {}\n        sep = record.separator\n\n        # String \"helpfully\" replaces ' ' with /\\s+/ in splitting, so we\n        # have to work around it.\n        if sep == \" \"\n          sep = / /\n        end\n        line_fields = line.split(sep)\n        record.fields.each do |param|\n          value = line_fields.shift\n          if value and value != record.absent\n            ret[param] = value\n          else\n            ret[param] = :absent\n          end\n        end\n\n        if record.rollup and !line_fields.empty?\n          last_field = record.fields[-1]\n          val = ([ret[last_field]] + line_fields).join(record.joiner)\n          ret[last_field] = val\n        end\n      end\n    end\n\n    if ret\n      ret[:record_type] = record.name\n      ret\n    else\n      nil\n    end\n  end\n\n  def line_separator\n    @line_separator ||= \"\\n\"\n\n    @line_separator\n  end\n\n  # Split text into separate lines using the record separator.\n  def lines(text)\n    # NOTE: We do not have to remove trailing separators because split will ignore\n    # them by default (unless you pass -1 as a second parameter)\n    text.split(line_separator)\n  end\n\n  # Split a bunch of text into lines and then parse them individually.\n  def parse(text)\n    count = 1\n    lines(text).collect do |line|\n      count += 1\n      val = parse_line(line)\n      if val\n        val\n      else\n        error = Puppet::ResourceError.new(_(\"Could not parse line %{line}\") % { line: line.inspect })\n        error.line = count\n        raise error\n      end\n    end\n  end\n\n  # Handle parsing a single line.\n  def parse_line(line)\n    raise Puppet::DevError, _(\"No record types defined; cannot parse lines\") unless records?\n\n    @record_order.each do |record|\n      # These are basically either text or record lines.\n      method = \"handle_#{record.type}_line\"\n      if respond_to?(method)\n        result = send(method, line, record)\n        if result\n          record.send(:post_parse, result) if record.respond_to?(:post_parse)\n          return result\n        end\n      else\n        raise Puppet::DevError, _(\"Somehow got invalid line type %{record_type}\") % { record_type: record.type }\n      end\n    end\n\n    nil\n  end\n\n  # Define a new type of record.  These lines get split into hashes.  Valid\n  # options are:\n  # * <tt>:absent</tt>: What to use as value within a line, when a field is\n  #   absent.  Note that in the record object, the literal :absent symbol is\n  #   used, and not this value.  Defaults to \"\".\n  # * <tt>:fields</tt>: The list of fields, as an array.  By default, all\n  #   fields are considered required.\n  # * <tt>:joiner</tt>: How to join fields together.  Defaults to '\\t'.\n  # * <tt>:optional</tt>: Which fields are optional.  If these are missing,\n  #   you'll just get the 'absent' value instead of an ArgumentError.\n  # * <tt>:rts</tt>: Whether to remove trailing whitespace.  Defaults to false.\n  #   If true, whitespace will be removed; if a regex, then whatever matches\n  #   the regex will be removed.\n  # * <tt>:separator</tt>: The record separator.  Defaults to /\\s+/.\n  def record_line(name, options, &block)\n    raise ArgumentError, _(\"Must include a list of fields\") unless options.include?(:fields)\n\n    record = FileRecord.new(:record, **options, &block)\n    record.name = name.intern\n\n    new_line_type(record)\n  end\n\n  # Are there any record types defined?\n  def records?\n    defined?(@record_types) and !@record_types.empty?\n  end\n\n  # Define a new type of text record.\n  def text_line(name, options, &block)\n    raise ArgumentError, _(\"You must provide a :match regex for text lines\") unless options.include?(:match)\n\n    record = FileRecord.new(:text, **options, &block)\n    record.name = name.intern\n\n    new_line_type(record)\n  end\n\n  # Generate a file from a bunch of hash records.\n  def to_file(records)\n    text = records.collect { |record| to_line(record) }.join(line_separator)\n\n    text += line_separator if trailing_separator\n\n    text\n  end\n\n  # Convert our parsed record into a text record.\n  def to_line(details)\n    record = record_type(details[:record_type])\n    unless record\n      raise ArgumentError, _(\"Invalid record type %{record_type}\") % { record_type: details[:record_type].inspect }\n    end\n\n    if record.respond_to?(:pre_gen)\n      details = details.dup\n      record.send(:pre_gen, details)\n    end\n\n    case record.type\n    when :text; details[:line]\n    else\n      return record.to_line(details) if record.respond_to?(:to_line)\n\n      line = record.join(details)\n\n      regex = record.rts\n      if regex\n        # If they say true, then use whitespace; else, use their regex.\n        if regex == true\n          regex = /\\s+$/\n        end\n        line.sub(regex, '')\n      else\n        line\n      end\n    end\n  end\n\n  # Whether to add a trailing separator to the file.  Defaults to true\n  def trailing_separator\n    if defined?(@trailing_separator)\n      @trailing_separator\n    else\n      true\n    end\n  end\n\n  def valid_attr?(type, attr)\n    type = type.intern\n    record = record_type(type)\n    if record && record.fields.include?(attr.intern)\n      true\n    else\n      attr.intern == :ensure\n    end\n  end\n\n  private\n\n  # Define a new type of record.\n  def new_line_type(record)\n    @record_types ||= {}\n    @record_order ||= []\n\n    raise ArgumentError, _(\"Line type %{name} is already defined\") % { name: record.name } if @record_types.include?(record.name)\n\n    @record_types[record.name] = record\n    @record_order << record\n\n    record\n  end\n\n  # Retrieve the record object.\n  def record_type(type)\n    @record_types[type.intern]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/filetype.rb",
    "content": "# frozen_string_literal: true\n\n# Basic classes for reading, writing, and emptying files.  Not much\n# to see here.\n\nrequire_relative '../../puppet/util/selinux'\nrequire 'tempfile'\nrequire 'fileutils'\n\nclass Puppet::Util::FileType\n  attr_accessor :loaded, :path, :synced\n\n  class FileReadError < Puppet::Error; end\n\n  include Puppet::Util::SELinux\n\n  class << self\n    attr_accessor :name\n\n    include Puppet::Util::ClassGen\n  end\n\n  # Create a new filetype.\n  def self.newfiletype(name, &block)\n    @filetypes ||= {}\n\n    klass = genclass(\n      name,\n      :block => block,\n      :prefix => \"FileType\",\n      :hash => @filetypes\n    )\n\n    # Rename the read and write methods, so that we're sure they\n    # maintain the stats.\n    klass.class_eval do\n      # Rename the read method\n      define_method(:real_read, instance_method(:read))\n      define_method(:read) do\n        val = real_read\n        @loaded = Time.now\n        if val\n          val.gsub(/# HEADER.*\\n/, '')\n        else\n          \"\"\n        end\n      rescue Puppet::Error\n        raise\n      rescue => detail\n        message = _(\"%{klass} could not read %{path}: %{detail}\") % { klass: self.class, path: @path, detail: detail }\n        Puppet.log_exception(detail, message)\n        raise Puppet::Error, message, detail.backtrace\n      end\n\n      # And then the write method\n      define_method(:real_write, instance_method(:write))\n      define_method(:write) do |text|\n        val = real_write(text)\n        @synced = Time.now\n        val\n      rescue Puppet::Error\n        raise\n      rescue => detail\n        message = _(\"%{klass} could not write %{path}: %{detail}\") % { klass: self.class, path: @path, detail: detail }\n        Puppet.log_exception(detail, message)\n        raise Puppet::Error, message, detail.backtrace\n      end\n    end\n  end\n\n  def self.filetype(type)\n    @filetypes[type]\n  end\n\n  # Pick or create a filebucket to use.\n  def bucket\n    @bucket ||= Puppet::Type.type(:filebucket).mkdefaultbucket.bucket\n  end\n\n  def initialize(path, default_mode = nil)\n    raise ArgumentError, _(\"Path is nil\") if path.nil?\n\n    @path = path\n    @default_mode = default_mode\n  end\n\n  # Arguments that will be passed to the execute method. Will set the uid\n  # to the target user if the target user and the current user are not\n  # the same\n  def cronargs\n    uid = Puppet::Util.uid(@path)\n    if uid && uid == Puppet::Util::SUIDManager.uid\n      { :failonfail => true, :combine => true }\n    else\n      { :failonfail => true, :combine => true, :uid => @path }\n    end\n  end\n\n  # Operate on plain files.\n  newfiletype(:flat) do\n    # Back the file up before replacing it.\n    def backup\n      bucket.backup(@path) if Puppet::FileSystem.exist?(@path)\n    end\n\n    # Read the file.\n    def read\n      if Puppet::FileSystem.exist?(@path)\n        # this code path is used by many callers so the original default is\n        # being explicitly preserved\n        Puppet::FileSystem.read(@path, :encoding => Encoding.default_external)\n      else\n        nil\n      end\n    end\n\n    # Remove the file.\n    def remove\n      Puppet::FileSystem.unlink(@path) if Puppet::FileSystem.exist?(@path)\n    end\n\n    # Overwrite the file.\n    def write(text)\n      # this file is managed by the OS and should be using system encoding\n      tf = Tempfile.new(\"puppet\", :encoding => Encoding.default_external)\n      tf.print text; tf.flush\n      File.chmod(@default_mode, tf.path) if @default_mode\n      FileUtils.cp(tf.path, @path)\n      tf.close\n      # If SELinux is present, we need to ensure the file has its expected context\n      set_selinux_default_context(@path)\n    end\n  end\n\n  # Operate on plain files.\n  newfiletype(:ram) do\n    @@tabs = {}\n\n    def self.clear\n      @@tabs.clear\n    end\n\n    def initialize(path, default_mode = nil)\n      # default_mode is meaningless for this filetype,\n      # supported only for compatibility with :flat\n      super\n      @@tabs[@path] ||= \"\"\n    end\n\n    # Read the file.\n    def read\n      Puppet.info _(\"Reading %{path} from RAM\") % { path: @path }\n      @@tabs[@path]\n    end\n\n    # Remove the file.\n    def remove\n      Puppet.info _(\"Removing %{path} from RAM\") % { path: @path }\n      @@tabs[@path] = \"\"\n    end\n\n    # Overwrite the file.\n    def write(text)\n      Puppet.info _(\"Writing %{path} to RAM\") % { path: @path }\n      @@tabs[@path] = text\n    end\n  end\n\n  # Handle Linux-style cron tabs.\n  #\n  # TODO: We can possibly eliminate the \"-u <username>\" option in cmdbase\n  # by just running crontab under <username>'s uid (like we do for suntab\n  # and aixtab). It may be worth investigating this alternative\n  # implementation in the future. This way, we can refactor all three of\n  # our cron file types into a common crontab file type.\n  newfiletype(:crontab) do\n    def initialize(user)\n      self.path = user\n    end\n\n    def path=(user)\n      begin\n        @uid = Puppet::Util.uid(user)\n      rescue Puppet::Error => detail\n        raise FileReadError, _(\"Could not retrieve user %{user}: %{detail}\") % { user: user, detail: detail }, detail.backtrace\n      end\n\n      # XXX We have to have the user name, not the uid, because some\n      # systems *cough*linux*cough* require it that way\n      @path = user\n    end\n\n    # Read a specific @path's cron tab.\n    def read\n      unless Puppet::Util.uid(@path)\n        Puppet.debug _(\"The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.\") % { path: @path }\n\n        return \"\"\n      end\n\n      Puppet::Util::Execution.execute(\"#{cmdbase} -l\", failonfail: true, combine: true)\n    rescue => detail\n      case detail.to_s\n      when /no crontab for/\n        \"\"\n      when /are not allowed to/\n        Puppet.debug _(\"The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).\") % { path: @path }\n\n        \"\"\n      else\n        raise FileReadError, _(\"Could not read crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n      end\n    end\n\n    # Remove a specific @path's cron tab.\n    def remove\n      cmd = \"#{cmdbase} -r\"\n      if %w[Darwin FreeBSD DragonFly].include?(Puppet.runtime[:facter].value('os.name'))\n        cmd = \"/bin/echo yes | #{cmd}\"\n      end\n\n      Puppet::Util::Execution.execute(cmd, failonfail: true, combine: true)\n    end\n\n    # Overwrite a specific @path's cron tab; must be passed the @path name\n    # and the text with which to create the cron tab.\n    #\n    # TODO: We should refactor this at some point to make it identical to the\n    # :aixtab and :suntab's write methods so that, at the very least, the pipe\n    # is not created and the crontab command's errors are not swallowed.\n    def write(text)\n      unless Puppet::Util.uid(@path)\n        raise Puppet::Error, _(\"Cannot write the %{path} user's crontab: The user does not exist\") % { path: @path }\n      end\n\n      # this file is managed by the OS and should be using system encoding\n      IO.popen(\"#{cmdbase()} -\", \"w\", :encoding => Encoding.default_external) { |p|\n        p.print text\n      }\n    end\n\n    private\n\n    # Only add the -u flag when the @path is different.  Fedora apparently\n    # does not think I should be allowed to set the @path to my own user name\n    def cmdbase\n      if @uid == Puppet::Util::SUIDManager.uid || Puppet.runtime[:facter].value('os.name') == \"HP-UX\"\n        \"crontab\"\n      else\n        \"crontab -u #{@path}\"\n      end\n    end\n  end\n\n  # SunOS has completely different cron commands; this class implements\n  # its versions.\n  newfiletype(:suntab) do\n    # Read a specific @path's cron tab.\n    def read\n      unless Puppet::Util.uid(@path)\n        Puppet.debug _(\"The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.\") % { path: @path }\n\n        return \"\"\n      end\n\n      Puppet::Util::Execution.execute(%w[crontab -l], cronargs)\n    rescue => detail\n      case detail.to_s\n      when /can't open your crontab/\n        \"\"\n      when /you are not authorized to use cron/\n        Puppet.debug _(\"The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).\") % { path: @path }\n\n        \"\"\n      else\n        raise FileReadError, _(\"Could not read crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n      end\n    end\n\n    # Remove a specific @path's cron tab.\n    def remove\n      Puppet::Util::Execution.execute(%w[crontab -r], cronargs)\n    rescue => detail\n      raise FileReadError, _(\"Could not remove crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n    end\n\n    # Overwrite a specific @path's cron tab; must be passed the @path name\n    # and the text with which to create the cron tab.\n    def write(text)\n      # this file is managed by the OS and should be using system encoding\n      output_file = Tempfile.new(\"puppet_suntab\", :encoding => Encoding.default_external)\n      begin\n        output_file.print text\n        output_file.close\n        # We have to chown the stupid file to the user.\n        File.chown(Puppet::Util.uid(@path), nil, output_file.path)\n        Puppet::Util::Execution.execute([\"crontab\", output_file.path], cronargs)\n      rescue => detail\n        raise FileReadError, _(\"Could not write crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n      ensure\n        output_file.close\n        output_file.unlink\n      end\n    end\n  end\n\n  #  Support for AIX crontab with output different than suntab's crontab command.\n  newfiletype(:aixtab) do\n    # Read a specific @path's cron tab.\n    def read\n      unless Puppet::Util.uid(@path)\n        Puppet.debug _(\"The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.\") % { path: @path }\n\n        return \"\"\n      end\n\n      Puppet::Util::Execution.execute(%w[crontab -l], cronargs)\n    rescue => detail\n      case detail.to_s\n      when /open.*in.*directory/\n        \"\"\n      when /not.*authorized.*cron/\n        Puppet.debug _(\"The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).\") % { path: @path }\n\n        \"\"\n      else\n        raise FileReadError, _(\"Could not read crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n      end\n    end\n\n    # Remove a specific @path's cron tab.\n    def remove\n      Puppet::Util::Execution.execute(%w[crontab -r], cronargs)\n    rescue => detail\n      raise FileReadError, _(\"Could not remove crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n    end\n\n    # Overwrite a specific @path's cron tab; must be passed the @path name\n    # and the text with which to create the cron tab.\n    def write(text)\n      # this file is managed by the OS and should be using system encoding\n      output_file = Tempfile.new(\"puppet_aixtab\", :encoding => Encoding.default_external)\n\n      begin\n        output_file.print text\n        output_file.close\n        # We have to chown the stupid file to the user.\n        File.chown(Puppet::Util.uid(@path), nil, output_file.path)\n        Puppet::Util::Execution.execute([\"crontab\", output_file.path], cronargs)\n      rescue => detail\n        raise FileReadError, _(\"Could not write crontab for %{path}: %{detail}\") % { path: @path, detail: detail }, detail.backtrace\n      ensure\n        output_file.close\n        output_file.unlink\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/http_proxy.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/http'\n\n# for backwards compatibility\nPuppet::Util::HttpProxy = Puppet::HTTP::Proxy\n"
  },
  {
    "path": "lib/puppet/util/inifile.rb",
    "content": "# frozen_string_literal: true\n\n# Module Puppet::IniConfig\n# A generic way to parse .ini style files and manipulate them in memory\n# One 'file' can be made up of several physical files. Changes to sections\n# on the file are tracked so that only the physical files in which\n# something has changed are written back to disk\n# Great care is taken to preserve comments and blank lines from the original\n# files\n#\n# The parsing tries to stay close to python's ConfigParser\n\nrequire_relative '../../puppet/util/filetype'\nrequire_relative '../../puppet/error'\n\nmodule Puppet::Util::IniConfig\n  # A section in a .ini file\n  class Section\n    attr_reader :name, :file, :entries\n    attr_writer :destroy\n\n    def initialize(name, file)\n      @name = name\n      @file = file\n      @dirty = false\n      @entries = []\n      @destroy = false\n    end\n\n    # Does this section need to be updated in/removed from the associated file?\n    #\n    # @note This section is dirty if a key has been modified _or_ if the\n    #   section has been modified so the associated file can be rewritten\n    #   without this section.\n    def dirty?\n      @dirty or @destroy\n    end\n\n    def mark_dirty\n      @dirty = true\n    end\n\n    # Should only be used internally\n    def mark_clean\n      @dirty = false\n    end\n\n    # Should the file be destroyed?\n    def destroy?\n      @destroy\n    end\n\n    # Add a line of text (e.g., a comment) Such lines\n    # will be written back out in exactly the same\n    # place they were read in\n    def add_line(line)\n      @entries << line\n    end\n\n    # Set the entry 'key=value'. If no entry with the\n    # given key exists, one is appended to the end of the section\n    def []=(key, value)\n      entry = find_entry(key)\n      @dirty = true\n      if entry.nil?\n        @entries << [key, value]\n      else\n        entry[1] = value\n      end\n    end\n\n    # Return the value associated with KEY. If no such entry\n    # exists, return nil\n    def [](key)\n      entry = find_entry(key)\n      (entry.nil? ? nil : entry[1])\n    end\n\n    # Format the section as text in the way it should be\n    # written to file\n    def format\n      if @destroy\n        text = ''.dup\n      else\n        text = \"[#{name}]\\n\"\n        @entries.each do |entry|\n          if entry.is_a?(Array)\n            key, value = entry\n            text << \"#{key}=#{value}\\n\" unless value.nil?\n          else\n            text << entry\n          end\n        end\n      end\n      text\n    end\n\n    private\n\n    def find_entry(key)\n      @entries.each do |entry|\n        return entry if entry.is_a?(Array) && entry[0] == key\n      end\n      nil\n    end\n  end\n\n  class PhysicalFile\n    # @!attribute [r] filetype\n    #   @api private\n    #   @return [Puppet::Util::FileType::FileTypeFlat]\n    attr_reader :filetype\n\n    # @!attribute [r] contents\n    #   @api private\n    #   @return [Array<String, Puppet::Util::IniConfig::Section>]\n    attr_reader :contents\n\n    # @!attribute [rw] destroy_empty\n    #   Whether empty files should be removed if no sections are defined.\n    #   Defaults to false\n    attr_accessor :destroy_empty\n\n    # @!attribute [rw] file_collection\n    #   @return [Puppet::Util::IniConfig::FileCollection]\n    attr_accessor :file_collection\n\n    def initialize(file, options = {})\n      @file = file\n      @contents = []\n      @filetype = Puppet::Util::FileType.filetype(:flat).new(file)\n\n      @destroy_empty = options.fetch(:destroy_empty, false)\n    end\n\n    # Read and parse the on-disk file associated with this object\n    def read\n      text = @filetype.read\n      if text.nil?\n        raise IniParseError, _(\"Cannot read nonexistent file %{file}\") % { file: @file.inspect }\n      end\n\n      parse(text)\n    end\n\n    INI_COMMENT = Regexp.union(\n      /^\\s*$/,\n      /^[#;]/,\n      /^\\s*rem\\s/i\n    )\n    INI_CONTINUATION = /^[ \\t\\r\\n\\f]/\n    INI_SECTION_NAME = /^\\[([^\\]]+)\\]/\n    INI_PROPERTY     = /^\\s*([^\\s=]+)\\s*=\\s*(.*)$/\n\n    # @api private\n    def parse(text)\n      section = nil   # The name of the current section\n      optname = nil   # The name of the last option in section\n      line_num = 0\n\n      text.each_line do |l|\n        line_num += 1\n        if l.match(INI_COMMENT)\n          # Whitespace or comment\n          if section.nil?\n            @contents << l\n          else\n            section.add_line(l)\n          end\n        elsif l.match(INI_CONTINUATION) && section && optname\n          # continuation line\n          section[optname] += \"\\n#{l.chomp}\"\n        elsif (match = l.match(INI_SECTION_NAME))\n          # section heading\n          section.mark_clean if section\n\n          section_name = match[1]\n\n          section = add_section(section_name)\n          optname = nil\n        elsif (match = l.match(INI_PROPERTY))\n          # the regex strips leading white space from the value, and here we strip the trailing white space as well\n          key = match[1]\n          val = match[2].rstrip\n\n          if section.nil?\n            raise IniParseError, _(\"Property with key %{key} outside of a section\") % { key: key.inspect }\n          end\n\n          section[key] = val\n          optname = key\n        else\n          raise IniParseError.new(_(\"Can't parse line '%{line}'\") % { line: l.chomp }, @file, line_num)\n        end\n      end\n      section.mark_clean unless section.nil?\n    end\n\n    # @return [Array<Puppet::Util::IniConfig::Section>] All sections defined in\n    #   this file.\n    def sections\n      @contents.select { |entry| entry.is_a? Section }\n    end\n\n    # @return [Puppet::Util::IniConfig::Section, nil] The section with the\n    #   given name if it exists, else nil.\n    def get_section(name)\n      @contents.find { |entry| entry.is_a? Section and entry.name == name }\n    end\n\n    def format\n      text = ''.dup\n\n      @contents.each do |content|\n        if content.is_a? Section\n          text << content.format\n        else\n          text << content\n        end\n      end\n\n      text\n    end\n\n    def store\n      if @destroy_empty and (sections.empty? or sections.all?(&:destroy?))\n        ::File.unlink(@file)\n      elsif sections.any?(&:dirty?)\n        text = self.format\n        @filetype.write(text)\n      end\n      sections.each(&:mark_clean)\n    end\n\n    # Create a new section and store it in the file contents\n    #\n    # @api private\n    # @param name [String] The name of the section to create\n    # @return [Puppet::Util::IniConfig::Section]\n    def add_section(name)\n      if section_exists?(name)\n        raise IniParseError.new(_(\"Section %{name} is already defined, cannot redefine\") % { name: name.inspect }, @file)\n      end\n\n      section = Section.new(name, @file)\n      @contents << section\n\n      section\n    end\n\n    private\n\n    def section_exists?(name)\n      if get_section(name)\n        true\n      elsif @file_collection and @file_collection.get_section(name)\n        true\n      else\n        false\n      end\n    end\n  end\n\n  class FileCollection\n    attr_reader :files\n\n    def initialize\n      @files = {}\n    end\n\n    # Read and parse a file and store it in the collection. If the file has\n    # already been read it will be destroyed and re-read.\n    def read(file)\n      new_physical_file(file).read\n    end\n\n    def store\n      @files.values.each(&:store)\n    end\n\n    def each_section(&block)\n      @files.values.each do |file|\n        file.sections.each do |section|\n          yield section\n        end\n      end\n    end\n\n    def each_file(&block)\n      @files.keys.each do |path|\n        yield path\n      end\n    end\n\n    def get_section(name)\n      sect = nil\n      @files.values.each do |file|\n        if (current = file.get_section(name))\n          sect = current\n        end\n      end\n      sect\n    end\n    alias [] get_section\n\n    def include?(name)\n      !!get_section(name)\n    end\n\n    def add_section(name, file)\n      get_physical_file(file).add_section(name)\n    end\n\n    private\n\n    # Return a file if it's already been defined, create a new file if it hasn't\n    # been defined.\n    def get_physical_file(file)\n      @files[file] || new_physical_file(file)\n    end\n\n    # Create a new physical file and set required attributes on that file.\n    def new_physical_file(file)\n      @files[file] = PhysicalFile.new(file)\n      @files[file].file_collection = self\n      @files[file]\n    end\n  end\n\n  File = FileCollection\n\n  class IniParseError < Puppet::Error\n    include Puppet::ExternalFileError\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/instance_loader.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/autoload'\nrequire_relative '../../puppet/util'\nrequire_relative '../../puppet/concurrent/lock'\n\n# A module that can easily autoload things for us.  Uses an instance\n# of Puppet::Util::Autoload\nmodule Puppet::Util::InstanceLoader\n  include Puppet::Util\n\n  # Are we instance-loading this type?\n  def instance_loading?(type)\n    defined?(@autoloaders) and @autoloaders.include?(type.intern)\n  end\n\n  # Define a new type of autoloading.\n  def instance_load(type, path)\n    @autoloaders ||= {}\n    @instances ||= {}\n    type = type.intern\n    @instances[type] = {}\n    @autoloaders[type] = Puppet::Util::Autoload.new(self, path)\n    @instance_loader_lock = Puppet::Concurrent::Lock.new\n\n    # Now define our new simple methods\n    unless respond_to?(type)\n      meta_def(type) do |name|\n        loaded_instance(type, name)\n      end\n    end\n  end\n\n  # Return a list of the names of all instances\n  def loaded_instances(type)\n    @instances[type].keys\n  end\n\n  # Return the instance hash for our type.\n  def instance_hash(type)\n    @instances[type.intern]\n  end\n\n  # Return the Autoload object for a given type.\n  def instance_loader(type)\n    @autoloaders[type.intern]\n  end\n\n  # Retrieve an already-loaded instance, or attempt to load our instance.\n  def loaded_instance(type, name)\n    @instance_loader_lock.synchronize do\n      name = name.intern\n      instances = instance_hash(type)\n      return nil unless instances\n\n      unless instances.include? name\n        if instance_loader(type).load(name, Puppet.lookup(:current_environment))\n          unless instances.include? name\n            Puppet.warning(_(\"Loaded %{type} file for %{name} but %{type} was not defined\") % { type: type, name: name })\n            return nil\n          end\n        else\n          return nil\n        end\n      end\n      instances[name]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/json.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util\n  module Json\n    class ParseError < StandardError\n      attr_reader :cause, :data\n\n      def self.build(original_exception, data)\n        new(original_exception.message).tap do |exception|\n          exception.instance_eval do\n            @cause = original_exception\n            set_backtrace original_exception.backtrace\n            @data = data\n          end\n        end\n      end\n    end\n\n    begin\n      require 'multi_json'\n      # Force backend detection before attempting to use the library\n      # or load any other JSON libraries\n      MultiJson.default_adapter\n\n      # Preserve core type monkey-patching done by the built-in JSON gem\n      require 'json'\n    rescue LoadError\n      require 'json'\n    end\n\n    # Load the content from a file as JSON if\n    # contents are in valid format. This method does not\n    # raise error but returns `nil` when invalid file is\n    # given.\n    def self.load_file_if_valid(filename, options = {})\n      load_file(filename, options)\n    rescue Puppet::Util::Json::ParseError, ArgumentError, Errno::ENOENT => detail\n      Puppet.debug(\"Could not retrieve JSON content from '#{filename}': #{detail.message}\")\n      nil\n    end\n\n    # Load the content from a file as JSON.\n    def self.load_file(filename, options = {})\n      json = Puppet::FileSystem.read(filename, :encoding => 'utf-8')\n      load(json, options)\n    end\n\n    # These methods do similar processing to the fallback implemented by MultiJson\n    # when using the built-in JSON backend, to ensure consistent behavior\n    # whether or not MultiJson can be loaded.\n    def self.load(string, options = {})\n      if defined? MultiJson\n        begin\n          # This ensures that JrJackson and Oj will parse very large or very small\n          # numbers as floats rather than BigDecimals, which are serialized as\n          # strings by the built-in JSON gem and therefore can cause schema errors,\n          # for example, when we are rendering reports to JSON using `to_pson` in\n          # PuppetDB.\n          case MultiJson.adapter.name\n          when \"MultiJson::Adapters::JrJackson\"\n            options[:use_bigdecimal] = false\n          when \"MultiJson::Adapters::Oj\"\n            options[:bigdecimal_load] = :float\n          end\n\n          MultiJson.load(string, options)\n        rescue MultiJson::ParseError => e\n          raise Puppet::Util::Json::ParseError.build(e, string)\n        end\n      else\n        begin\n          string = string.read if string.respond_to?(:read)\n\n          options[:symbolize_names] = true if options.delete(:symbolize_keys)\n          ::JSON.parse(string, options)\n        rescue JSON::ParserError => e\n          raise Puppet::Util::Json::ParseError.build(e, string)\n        end\n      end\n    end\n\n    def self.dump(object, options = {})\n      if defined? MultiJson\n        MultiJson.dump(object, options)\n      elsif options.is_a?(JSON::State)\n        # we're being called recursively\n        object.to_json(options)\n      else\n        options.merge!(::JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)\n        object.to_json(options)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/json_lockfile.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/lockfile'\n\n# This class provides a simple API for managing a lock file\n# whose contents are a serialized JSON object.  In addition\n# to querying the basic state (#locked?) of the lock, managing\n# the lock (#lock, #unlock), the contents can be retrieved at\n# any time while the lock is held (#lock_data).  This can be\n# used to store structured data (state messages, etc.) about\n# the lock.\n#\n# @see Puppet::Util::Lockfile\nclass Puppet::Util::JsonLockfile < Puppet::Util::Lockfile\n  # Lock the lockfile.  You may optionally pass a data object, which will be\n  # retrievable for the duration of time during which the file is locked.\n  #\n  # @param [Hash] lock_data an optional Hash of data to associate with the lock.\n  #   This may be used to store pids, descriptive messages, etc.  The\n  #   data may be retrieved at any time while the lock is held by\n  #   calling the #lock_data method. <b>NOTE</b> that the JSON serialization\n  #   does NOT support Symbol objects--if you pass them in, they will be\n  #   serialized as Strings, so you should plan accordingly.\n  # @return [boolean] true if lock is successfully acquired, false otherwise.\n  def lock(lock_data = nil)\n    return false if locked?\n\n    super(Puppet::Util::Json.dump(lock_data))\n  end\n\n  # Retrieve the (optional) lock data that was specified at the time the file\n  #  was locked.\n  # @return [Object] the data object.  Remember that the serialization does not\n  #  support Symbol objects, so if your data Object originally contained symbols,\n  #  they will be converted to Strings.\n  def lock_data\n    return nil unless file_locked?\n\n    file_contents = super\n    return nil if file_contents.nil? or file_contents.empty?\n\n    Puppet::Util::Json.load(file_contents)\n  rescue Puppet::Util::Json::ParseError\n    Puppet.warning _(\"Unable to read lockfile data from %{path}: not in JSON\") % { path: @file_path }\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/ldap/connection.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/ldap'\n\nclass Puppet::Util::Ldap::Connection\n  attr_accessor :host, :port, :user, :password, :reset, :ssl\n\n  attr_reader :connection\n\n  # Return a default connection, using our default settings.\n  def self.instance\n    ssl = if Puppet[:ldaptls]\n            :tls\n          elsif Puppet[:ldapssl]\n            true\n          else\n            false\n          end\n\n    options = {}\n    options[:ssl] = ssl\n    user = Puppet.settings[:ldapuser]\n    if user && user != \"\"\n      options[:user] = user\n      pass = Puppet.settings[:ldappassword]\n      if pass && pass != \"\"\n        options[:password] = pass\n      end\n    end\n\n    new(Puppet[:ldapserver], Puppet[:ldapport], options)\n  end\n\n  def close\n    connection.unbind if connection.bound?\n  end\n\n  def initialize(host, port, user: nil, password: nil, reset: nil, ssl: nil)\n    raise Puppet::Error, _(\"Could not set up LDAP Connection: Missing ruby/ldap libraries\") unless Puppet.features.ldap?\n\n    @host = host\n    @port = port\n    @user = user\n    @password = password\n    @reset = reset\n    @ssl = ssl\n  end\n\n  # Create a per-connection unique name.\n  def name\n    [host, port, user, password, ssl].collect(&:to_s).join(\"/\")\n  end\n\n  # Should we reset the connection?\n  def reset?\n    reset\n  end\n\n  # Start our ldap connection.\n  def start\n    case ssl\n    when :tls\n      @connection = LDAP::SSLConn.new(host, port, true)\n    when true\n      @connection = LDAP::SSLConn.new(host, port)\n    else\n      @connection = LDAP::Conn.new(host, port)\n    end\n    @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)\n    @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)\n    @connection.simple_bind(user, password)\n  rescue => detail\n    raise Puppet::Error, _(\"Could not connect to LDAP: %{detail}\") % { detail: detail }, detail.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/ldap/generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/ldap'\n\nclass Puppet::Util::Ldap::Generator\n  # Declare the attribute we'll use to generate the value.\n  def from(source)\n    @source = source\n    self\n  end\n\n  # Actually do the generation.\n  def generate(value = nil)\n    if value.nil?\n      @generator.call\n    else\n      @generator.call(value)\n    end\n  end\n\n  # Initialize our generator with the name of the parameter\n  # being generated.\n  def initialize(name)\n    @name = name\n  end\n\n  def name\n    @name.to_s\n  end\n\n  def source\n    if @source\n      @source.to_s\n    else\n      nil\n    end\n  end\n\n  # Provide the code that does the generation.\n  def with(&block)\n    @generator = block\n    self\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/ldap/manager.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/ldap'\nrequire_relative '../../../puppet/util/ldap/connection'\nrequire_relative '../../../puppet/util/ldap/generator'\n\n# The configuration class for LDAP providers, plus\n# connection handling for actually interacting with ldap.\nclass Puppet::Util::Ldap::Manager\n  attr_reader :objectclasses, :puppet2ldap, :location, :rdn\n\n  # A null-op that just returns the config.\n  def and\n    self\n  end\n\n  # Set the offset from the search base and return the config.\n  def at(location)\n    @location = location\n    self\n  end\n\n  # The basic search base.\n  def base\n    [location, Puppet[:ldapbase]].join(\",\")\n  end\n\n  # Convert the name to a dn, then pass the args along to\n  # our connection.\n  def create(name, attributes)\n    attributes = attributes.dup\n\n    # Add the objectclasses\n    attributes[\"objectClass\"] = objectclasses.collect(&:to_s)\n    attributes[\"objectClass\"] << \"top\" unless attributes[\"objectClass\"].include?(\"top\")\n\n    attributes[rdn.to_s] = [name]\n\n    # Generate any new values we might need.\n    generate(attributes)\n\n    # And create our resource.\n    connect { |conn| conn.add dn(name), attributes }\n  end\n\n  # Open, yield, and close the connection.  Cannot be left\n  # open, at this point.\n  def connect\n    # TRANSLATORS '#connect' is a method name and and should not be translated, 'block' refers to a Ruby code block\n    raise ArgumentError, _(\"You must pass a block to #connect\") unless block_given?\n\n    unless @connection\n      if Puppet[:ldaptls]\n        ssl = :tls\n      elsif Puppet[:ldapssl]\n        ssl = true\n      else\n        ssl = false\n      end\n      options = { :ssl => ssl }\n      user = Puppet[:ldapuser]\n      if user && user != \"\"\n        options[:user] = user\n      end\n      password = Puppet[:ldappassword]\n      if password && password != \"\"\n        options[:password] = password\n      end\n      @connection = Puppet::Util::Ldap::Connection.new(Puppet[:ldapserver], Puppet[:ldapport], options)\n    end\n    @connection.start\n    begin\n      yield @connection.connection\n    ensure\n      @connection.close\n    end\n    nil\n  end\n\n  # Convert the name to a dn, then pass the args along to\n  # our connection.\n  def delete(name)\n    connect { |connection| connection.delete dn(name) }\n  end\n\n  # Calculate the dn for a given resource.\n  def dn(name)\n    [\"#{rdn}=#{name}\", base].join(\",\")\n  end\n\n  # Convert an ldap-style entry hash to a provider-style hash.\n  def entry2provider(entry)\n    # TRANSLATOR 'dn' refers to a 'distinguished name' in LDAP (Lightweight Directory Access Protocol) and they should not be translated\n    raise ArgumentError, _(\"Could not get dn from ldap entry\") unless entry[\"dn\"]\n\n    # DN is always a single-entry array.  Strip off the bits before the\n    # first comma, then the bits after the remaining equal sign.  This is the\n    # name.\n    name = entry[\"dn\"].dup.pop.split(\",\").shift.split(\"=\").pop\n\n    result = { :name => name }\n\n    @ldap2puppet.each do |ldap, puppet|\n      result[puppet] = entry[ldap.to_s] || :absent\n    end\n\n    result\n  end\n\n  # Create our normal search filter.\n  def filter\n    (objectclasses.length == 1 ? \"objectclass=#{objectclasses[0]}\" : \"(&(objectclass=\" + objectclasses.join(\")(objectclass=\") + \"))\")\n  end\n\n  # Find the associated entry for a resource.  Returns a hash, minus\n  # 'dn', or nil if the entry cannot be found.\n  def find(name)\n    connect do |conn|\n      conn.search2(dn(name), 0, \"objectclass=*\") do |result|\n        # Convert to puppet-appropriate attributes\n        return entry2provider(result)\n      end\n    rescue\n      return nil\n    end\n  end\n\n  # Declare a new attribute generator.\n  def generates(parameter)\n    @generators << Puppet::Util::Ldap::Generator.new(parameter)\n    @generators[-1]\n  end\n\n  # Generate any extra values we need to make the ldap entry work.\n  def generate(values)\n    return unless @generators.length > 0\n\n    @generators.each do |generator|\n      # Don't override any values that might exist.\n      next if values[generator.name]\n\n      if generator.source\n        value = values[generator.source]\n        unless value\n          raise ArgumentError, _(\"%{source} must be defined to generate %{name}\") %\n                               { source: generator.source, name: generator.name }\n        end\n        result = generator.generate(value)\n      else\n        result = generator.generate\n      end\n\n      result = [result] unless result.is_a?(Array)\n      result = result.collect(&:to_s)\n\n      values[generator.name] = result\n    end\n  end\n\n  def initialize\n    @rdn = :cn\n    @generators = []\n  end\n\n  # Specify what classes this provider models.\n  def manages(*classes)\n    @objectclasses = classes\n    self\n  end\n\n  # Specify the attribute map.  Assumes the keys are the puppet\n  # attributes, and the values are the ldap attributes, and creates a map\n  # for each direction.\n  def maps(attributes)\n    # The map with the puppet attributes as the keys\n    @puppet2ldap = attributes\n\n    # and the ldap attributes as the keys.\n    @ldap2puppet = attributes.each_with_object({}) { |ary, map| map[ary[1]] = ary[0]; }\n\n    self\n  end\n\n  # Return the ldap name for a puppet attribute.\n  def ldap_name(attribute)\n    @puppet2ldap[attribute].to_s\n  end\n\n  # Convert the name to a dn, then pass the args along to\n  # our connection.\n  def modify(name, mods)\n    connect { |connection| connection.modify dn(name), mods }\n  end\n\n  # Specify the rdn that we use to build up our dn.\n  def named_by(attribute)\n    @rdn = attribute\n    self\n  end\n\n  # Return the puppet name for an ldap attribute.\n  def puppet_name(attribute)\n    @ldap2puppet[attribute]\n  end\n\n  # Search for all entries at our base.  A potentially expensive search.\n  def search(sfilter = nil)\n    sfilter ||= filter\n\n    result = []\n    connect do |conn|\n      conn.search2(base, 1, sfilter) do |entry|\n        result << entry2provider(entry)\n      end\n    end\n    (result.empty? ? nil : result)\n  end\n\n  # Update the ldap entry with the desired state.\n  def update(name, is, should)\n    if should[:ensure] == :absent\n      Puppet.info _(\"Removing %{name} from ldap\") % { name: dn(name) }\n      delete(name)\n      return\n    end\n\n    # We're creating a new entry\n    if is.empty? or is[:ensure] == :absent\n      Puppet.info _(\"Creating %{name} in ldap\") % { name: dn(name) }\n      # Remove any :absent params and :ensure, then convert the names to ldap names.\n      attrs = ldap_convert(should)\n      create(name, attrs)\n      return\n    end\n\n    # We're modifying an existing entry.  Yuck.\n\n    mods = []\n    # For each attribute we're deleting that is present, create a\n    # modify instance for deletion.\n    [is.keys, should.keys].flatten.uniq.each do |property|\n      # They're equal, so do nothing.\n      next if is[property] == should[property]\n\n      attributes = ldap_convert(should)\n\n      prop_name = ldap_name(property).to_s\n\n      # We're creating it.\n      if is[property] == :absent or is[property].nil?\n        mods << LDAP::Mod.new(LDAP::LDAP_MOD_ADD, prop_name, attributes[prop_name])\n        next\n      end\n\n      # We're deleting it\n      if should[property] == :absent or should[property].nil?\n        mods << LDAP::Mod.new(LDAP::LDAP_MOD_DELETE, prop_name, [])\n        next\n      end\n\n      # We're replacing an existing value\n      mods << LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE, prop_name, attributes[prop_name])\n    end\n\n    modify(name, mods)\n  end\n\n  # Is this a complete ldap configuration?\n  def valid?\n    location and objectclasses and !objectclasses.empty? and puppet2ldap\n  end\n\n  private\n\n  # Convert a hash of attributes to ldap-like forms.  This mostly means\n  # getting rid of :ensure and making sure everything's an array of strings.\n  def ldap_convert(attributes)\n    attributes.reject { |param, value| value == :absent or param == :ensure }.each_with_object({}) do |ary, result|\n      value = (ary[1].is_a?(Array) ? ary[1] : [ary[1]]).collect(&:to_s)\n      result[ldap_name(ary[0])] = value\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/ldap.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Ldap\nend\n"
  },
  {
    "path": "lib/puppet/util/libuser.conf",
    "content": "[import]\nlogin_defs = /etc/login.defs\ndefault_useradd = /etc/default/useradd\n\n[defaults]\ncrypt_style = md5\nmodules = files shadow\ncreate_modules = files shadow\n\n[userdefaults]\nLU_USERNAME = %n\nLU_GIDNUMBER = %u\n\n[groupdefaults]\nLU_GROUPNAME = %n\n"
  },
  {
    "path": "lib/puppet/util/libuser.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Libuser\n   def self.getconf\n     File.expand_path('libuser.conf', __dir__)\n   end\n\n   def self.getenv\n     newenv = {}\n     newenv['LIBUSER_CONF'] = getconf\n     newenv\n   end\nend\n"
  },
  {
    "path": "lib/puppet/util/limits.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util'\n\nmodule Puppet::Util::Limits\n  # @api private\n  def setpriority(priority)\n    return unless priority\n\n    Process.setpriority(0, Process.pid, priority)\n  rescue Errno::EACCES, NotImplementedError\n    Puppet.warning(_(\"Failed to set process priority to '%{priority}'\") % { priority: priority })\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/lockfile.rb",
    "content": "# frozen_string_literal: true\n\n# This class provides a simple API for managing a lock file\n# whose contents are an (optional) String.  In addition\n# to querying the basic state (#locked?) of the lock, managing\n# the lock (#lock, #unlock), the contents can be retrieved at\n# any time while the lock is held (#lock_data).  This can be\n# used to store pids, messages, etc.\n#\n# @see Puppet::Util::JsonLockfile\nclass Puppet::Util::Lockfile\n  attr_reader :file_path\n\n  def initialize(file_path)\n    @file_path = file_path\n  end\n\n  # Lock the lockfile.  You may optionally pass a data object, which will be\n  # retrievable for the duration of time during which the file is locked.\n  #\n  # @param [String] lock_data an optional String data object to associate\n  #   with the lock.  This may be used to store pids, descriptive messages,\n  #   etc.  The data may be retrieved at any time while the lock is held by\n  #   calling the #lock_data method.\n\n  # @return [boolean] true if lock is successfully acquired, false otherwise.\n  def lock(lock_data = nil)\n    Puppet::FileSystem.exclusive_create(@file_path, nil) do |fd|\n      fd.print(lock_data)\n    end\n    true\n  rescue Errno::EEXIST\n    false\n  end\n\n  def unlock\n    if locked?\n      Puppet::FileSystem.unlink(@file_path)\n      true\n    else\n      false\n    end\n  end\n\n  def locked?\n    # delegate logic to a more explicit private method\n    file_locked?\n  end\n\n  # Retrieve the (optional) lock data that was specified at the time the file\n  #  was locked.\n  # @return [String] the data object.\n  def lock_data\n    File.read(@file_path) if file_locked?\n  end\n\n  # Private, internal utility method for encapsulating the logic about\n  #  whether or not the file is locked.  This method can be called\n  #  by other methods in this class without as much risk of accidentally\n  #  being overridden by child classes.\n  # @return [boolean] true if the file is locked, false if it is not.\n  def file_locked?\n    Puppet::FileSystem.exist? @file_path\n  end\n  private :file_locked?\nend\n"
  },
  {
    "path": "lib/puppet/util/log/destination.rb",
    "content": "# frozen_string_literal: true\n\n# A type of log destination.\nclass Puppet::Util::Log::Destination\n  class << self\n    attr_accessor :name\n  end\n\n  def self.initvars\n    @matches = []\n  end\n\n  # Mark the things we're supposed to match.\n  def self.match(obj)\n    @matches ||= []\n    @matches << obj\n  end\n\n  # See whether we match a given thing.\n  def self.match?(obj)\n    # Convert single-word strings into symbols like :console and :syslog\n    if obj.is_a? String and obj =~ /^\\w+$/\n      obj = obj.downcase.intern\n    end\n\n    @matches.each do |thing|\n      # Search for direct matches or class matches\n      return true if thing === obj or thing == obj.class.to_s\n    end\n    false\n  end\n\n  def name\n    if defined?(@name)\n      @name\n    else\n      self.class.name\n    end\n  end\n\n  # Set how to handle a message.\n  def self.sethandler(&block)\n    define_method(:handle, &block)\n  end\n\n  # Mark how to initialize our object.\n  def self.setinit(&block)\n    define_method(:initialize, &block)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/log/destinations.rb",
    "content": "# frozen_string_literal: true\n\nPuppet::Util::Log.newdesttype :syslog do\n  def self.suitable?(obj)\n    Puppet.features.syslog?\n  end\n\n  def close\n    Syslog.close\n  end\n\n  def initialize\n    Syslog.close if Syslog.opened?\n    name = \"puppet-#{Puppet.run_mode.name}\"\n\n    options = Syslog::LOG_PID | Syslog::LOG_NDELAY\n\n    # XXX This should really be configurable.\n    str = Puppet[:syslogfacility]\n    begin\n      facility = Syslog.const_get(\"LOG_#{str.upcase}\")\n    rescue NameError => e\n      raise Puppet::Error, _(\"Invalid syslog facility %{str}\") % { str: str }, e.backtrace\n    end\n\n    @syslog = Syslog.open(name, options, facility)\n  end\n\n  def handle(msg)\n    # XXX Syslog currently has a bug that makes it so you\n    # cannot log a message with a '%' in it.  So, we get rid\n    # of them.\n    if msg.source == \"Puppet\"\n      msg.to_s.split(\"\\n\").each do |line|\n        @syslog.send(msg.level, line.gsub(\"%\", '%%'))\n      end\n    else\n      msg.to_s.split(\"\\n\").each do |line|\n        @syslog.send(msg.level, \"(%s) %s\" % [msg.source.to_s.delete(\"%\"),\n                                             line.gsub(\"%\", '%%')])\n      end\n    end\n  end\nend\n\nPuppet::Util::Log.newdesttype :file do\n  require 'fileutils'\n\n  def self.match?(obj)\n    obj.is_a?(String) && Puppet::Util.absolute_path?(obj)\n  end\n\n  def close\n    if defined?(@file)\n      @file.close\n      @file = nil\n    end\n  end\n\n  def flush\n    @file.flush if defined?(@file)\n  end\n\n  attr_accessor :autoflush\n\n  def initialize(path)\n    @name = path\n    @json = path.end_with?('.json') ? 1 : 0\n    @jsonl = path.end_with?('.jsonl')\n\n    # first make sure the directory exists\n    # We can't just use 'Config.use' here, because they've\n    # specified a \"special\" destination.\n    unless Puppet::FileSystem.exist?(Puppet::FileSystem.dir(path))\n      FileUtils.mkdir_p(File.dirname(path), :mode => 0o755)\n      Puppet.info _(\"Creating log directory %{dir}\") % { dir: File.dirname(path) }\n    end\n\n    # create the log file, if it doesn't already exist\n    need_array_start = false\n    file_exists = Puppet::FileSystem.exist?(path)\n    if @json == 1\n      need_array_start = true\n      if file_exists\n        sz = File.size(path)\n        need_array_start = sz == 0\n\n        # Assume that entries have been written and that a comma\n        # is needed before next entry\n        @json = 2 if sz > 2\n      end\n    end\n\n    file = File.new(path, File::WRONLY | File::CREAT | File::APPEND)\n    file.puts('[') if need_array_start\n\n    @file = file\n\n    @autoflush = Puppet[:autoflush]\n  end\n\n  def handle(msg)\n    if @json > 0\n      @json > 1 ? @file.puts(',') : @json = 2\n      @file.puts(Puppet::Util::Json.dump(msg.to_structured_hash))\n    elsif @jsonl\n      @file.puts(Puppet::Util::Json.dump(msg.to_structured_hash))\n    else\n      @file.puts(\"#{msg.time} #{msg.source} (#{msg.level}): #{msg}\")\n    end\n\n    @file.flush if @autoflush\n  end\nend\n\nPuppet::Util::Log.newdesttype :logstash_event do\n  require 'time'\n\n  def format(msg)\n    # logstash_event format is documented at\n    # https://logstash.jira.com/browse/LOGSTASH-675\n\n    data = msg.to_hash\n    data['version'] = 1\n    data['@timestamp'] = data['time']\n    data.delete('time')\n\n    data\n  end\n\n  def handle(msg)\n    message = format(msg)\n    $stdout.puts Puppet::Util::Json.dump(message)\n  end\nend\n\nPuppet::Util::Log.newdesttype :console do\n  require_relative '../../../puppet/util/colors'\n  include Puppet::Util::Colors\n\n  def initialize\n    # Flush output immediately.\n    $stderr.sync = true\n    $stdout.sync = true\n  end\n\n  def handle(msg)\n    levels = {\n      :emerg => { :name => 'Emergency', :color => :hred,     :stream => $stderr },\n      :alert => { :name => 'Alert',     :color => :hred,     :stream => $stderr },\n      :crit => { :name => 'Critical', :color => :hred, :stream => $stderr },\n      :err => { :name => 'Error', :color => :hred, :stream => $stderr },\n      :warning => { :name => 'Warning', :color => :hyellow, :stream => $stderr },\n\n      :notice => { :name => 'Notice', :color => :reset, :stream => $stdout },\n      :info => { :name => 'Info', :color => :green, :stream => $stdout },\n      :debug => { :name => 'Debug', :color => :cyan, :stream => $stdout },\n    }\n\n    str = msg.respond_to?(:multiline) ? msg.multiline : msg.to_s\n    str = msg.source == \"Puppet\" ? str : \"#{msg.source}: #{str}\"\n\n    level = levels[msg.level]\n    level[:stream].puts colorize(level[:color], \"#{level[:name]}: #{str}\")\n  end\nend\n\n# Log to a transaction report.\nPuppet::Util::Log.newdesttype :report do\n  attr_reader :report\n\n  match \"Puppet::Transaction::Report\"\n\n  def initialize(report)\n    @report = report\n  end\n\n  def handle(msg)\n    @report << msg\n  end\nend\n\n# Log to an array, just for testing.\nmodule Puppet::Test\n  class LogCollector\n    def initialize(logs)\n      @logs = logs\n    end\n\n    def <<(value)\n      @logs << value\n    end\n  end\nend\n\nPuppet::Util::Log.newdesttype :array do\n  match \"Puppet::Test::LogCollector\"\n\n  def initialize(messages)\n    @messages = messages\n  end\n\n  def handle(msg)\n    @messages << msg\n  end\nend\n\nPuppet::Util::Log.newdesttype :eventlog do\n  # Leaving these in for backwards compatibility - duplicates the same in\n  # Puppet::Util::Windows::EventLog\n  Puppet::Util::Log::DestEventlog::EVENTLOG_ERROR_TYPE       = 0x0001\n  Puppet::Util::Log::DestEventlog::EVENTLOG_WARNING_TYPE     = 0x0002\n  Puppet::Util::Log::DestEventlog::EVENTLOG_INFORMATION_TYPE = 0x0004\n  Puppet::Util::Log::DestEventlog::EVENTLOG_CHARACTER_LIMIT  = 31_838\n\n  def self.suitable?(obj)\n    Puppet::Util::Platform.windows?\n  end\n\n  def initialize\n    @eventlog = Puppet::Util::Windows::EventLog.open(\"Puppet\")\n  end\n\n  def to_native(level)\n    Puppet::Util::Windows::EventLog.to_native(level)\n  end\n\n  def handle(msg)\n    native_type, native_id = to_native(msg.level)\n\n    stringified_msg = msg.message.to_s\n    if stringified_msg.length > self.class::EVENTLOG_CHARACTER_LIMIT\n      warning = \"...Message exceeds character length limit, truncating.\"\n      truncated_message_length = self.class::EVENTLOG_CHARACTER_LIMIT - warning.length\n      stringified_truncated_msg = stringified_msg[0..truncated_message_length]\n      stringified_truncated_msg << warning\n      msg.message = stringified_truncated_msg\n    end\n\n    @eventlog.report_event(\n      :event_type => native_type,\n      :event_id => native_id,\n      :data => (msg.source && msg.source != 'Puppet' ? \"#{msg.source}: \" : '') + msg.to_s\n    )\n  end\n\n  def close\n    if @eventlog\n      @eventlog.close\n      @eventlog = nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/log.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/tagging'\nrequire_relative '../../puppet/util/classgen'\nrequire_relative '../../puppet/util/psych_support'\nrequire_relative '../../puppet/network/format_support'\n\n# Pass feedback to the user.  Log levels are modeled after syslog's, and it is\n# expected that that will be the most common log destination.  Supports\n# multiple destinations, one of which is a remote server.\nclass Puppet::Util::Log\n  include Puppet::Util\n  extend Puppet::Util::ClassGen\n  include Puppet::Util::PsychSupport\n  include Puppet::Util::Tagging\n  include Puppet::Network::FormatSupport\n\n  @levels = [:debug, :info, :notice, :warning, :err, :alert, :emerg, :crit]\n  @loglevel = 2\n\n  @desttypes = {}\n\n  # Create a new destination type.\n  def self.newdesttype(name, options = {}, &block)\n    dest = genclass(\n      name,\n      :parent => Puppet::Util::Log::Destination,\n      :prefix => \"Dest\",\n      :block => block,\n      :hash => @desttypes,\n      :attributes => options\n    )\n    dest.match(dest.name)\n\n    dest\n  end\n\n  require_relative 'log/destination'\n  require_relative 'log/destinations'\n\n  @destinations = {}\n\n  @queued = []\n\n  class << self\n    include Puppet::Util\n    include Puppet::Util::ClassGen\n\n    attr_reader :desttypes\n  end\n\n  # Reset log to basics.  Basically just flushes and closes files and\n  # undefs other objects.\n  def Log.close(destination)\n    if @destinations.include?(destination)\n      @destinations[destination].flush if @destinations[destination].respond_to?(:flush)\n      @destinations[destination].close if @destinations[destination].respond_to?(:close)\n      @destinations.delete(destination)\n    end\n  end\n\n  def self.close_all\n    @destinations.keys.each { |dest|\n      close(dest)\n    }\n    # TRANSLATORS \"Log.close_all\" is a method name and should not be translated\n    raise Puppet::DevError, _(\"Log.close_all failed to close %{destinations}\") % { destinations: @destinations.keys.inspect } unless @destinations.empty?\n  end\n\n  # Flush any log destinations that support such operations.\n  def Log.flush\n    @destinations.each { |_type, dest|\n      dest.flush if dest.respond_to?(:flush)\n    }\n  end\n\n  def Log.autoflush=(v)\n    @destinations.each do |_type, dest|\n      dest.autoflush = v if dest.respond_to?(:autoflush=)\n    end\n  end\n\n  # Create a new log message.  The primary role of this method is to\n  # avoid creating log messages below the loglevel.\n  def Log.create(hash)\n    raise Puppet::DevError, _(\"Logs require a level\") unless hash.include?(:level)\n    raise Puppet::DevError, _(\"Invalid log level %{level}\") % { level: hash[:level] } unless @levels.index(hash[:level])\n\n    @levels.index(hash[:level]) >= @loglevel ? Puppet::Util::Log.new(hash) : nil\n  end\n\n  def Log.destinations\n    @destinations\n  end\n\n  # Yield each valid level in turn\n  def Log.eachlevel\n    @levels.each { |level| yield level }\n  end\n\n  # Return the current log level.\n  def Log.level\n    @levels[@loglevel]\n  end\n\n  # Set the current log level.\n  def Log.level=(level)\n    level = level.intern unless level.is_a?(Symbol)\n\n    # loglevel is a 0-based index\n    loglevel = @levels.index(level)\n    raise Puppet::DevError, _(\"Invalid loglevel %{level}\") % { level: level } unless loglevel\n\n    return if @loglevel == loglevel\n\n    # loglevel changed\n    @loglevel = loglevel\n\n    # Enable or disable Facter debugging\n    Puppet.runtime[:facter].debugging(level == :debug)\n  end\n\n  def Log.levels\n    @levels.dup\n  end\n\n  # Create a new log destination.\n  def Log.newdestination(dest)\n    # Each destination can only occur once.\n    if @destinations.find { |_name, obj| obj.name == dest }\n      return\n    end\n\n    _, type = @desttypes.find do |_name, klass|\n      klass.match?(dest)\n    end\n\n    if type.respond_to?(:suitable?) and !type.suitable?(dest)\n      return\n    end\n\n    raise Puppet::DevError, _(\"Unknown destination type %{dest}\") % { dest: dest } unless type\n\n    begin\n      if type.instance_method(:initialize).arity == 1\n        @destinations[dest] = type.new(dest)\n      else\n        @destinations[dest] = type.new\n      end\n      flushqueue\n      @destinations[dest]\n    rescue => detail\n      Puppet.log_exception(detail)\n\n      # If this was our only destination, then add the console back in.\n      if destinations.empty? && dest.intern != :console\n        newdestination(:console)\n      end\n\n      # Re-raise (end exit Puppet) because we could not set up logging correctly.\n      raise detail\n    end\n  end\n\n  def Log.with_destination(destination, &block)\n    if @destinations.include?(destination)\n      yield\n    else\n      newdestination(destination)\n      begin\n        yield\n      ensure\n        close(destination)\n      end\n    end\n  end\n\n  def Log.coerce_string(str)\n    return Puppet::Util::CharacterEncoding.convert_to_utf_8(str) if str.valid_encoding?\n\n    # We only select the last 10 callers in the stack to avoid being spammy\n    message = _(\"Received a Log attribute with invalid encoding:%{log_message}\") %\n              { log_message: Puppet::Util::CharacterEncoding.convert_to_utf_8(str.dump) }\n    message += '\\n' + _(\"Backtrace:\\n%{backtrace}\") % { backtrace: caller(1, 10).join(\"\\n\") }\n    message\n  end\n  private_class_method :coerce_string\n\n  # Route the actual message. FIXME There are lots of things this method\n  # should do, like caching and a bit more.  It's worth noting that there's\n  # a potential for a loop here, if the machine somehow gets the destination set as\n  # itself.\n  def Log.newmessage(msg)\n    return if @levels.index(msg.level) < @loglevel\n\n    msg.message = coerce_string(msg.message)\n    msg.source = coerce_string(msg.source)\n\n    queuemessage(msg) if @destinations.length == 0\n\n    @destinations.each do |_name, dest|\n      dest.handle(msg)\n    end\n  end\n\n  def Log.queuemessage(msg)\n    @queued.push(msg)\n  end\n\n  def Log.flushqueue\n    return unless @destinations.size >= 1\n\n    @queued.each do |msg|\n      Log.newmessage(msg)\n    end\n    @queued.clear\n  end\n\n  # Flush the logging queue.  If there are no destinations available,\n  #  adds in a console logger before flushing the queue.\n  # This is mainly intended to be used as a last-resort attempt\n  #  to ensure that logging messages are not thrown away before\n  #  the program is about to exit--most likely in a horrific\n  #  error scenario.\n  # @return nil\n  def Log.force_flushqueue\n    if @destinations.empty? and !@queued.empty?\n      newdestination(:console)\n    end\n    flushqueue\n  end\n\n  def Log.sendlevel?(level)\n    @levels.index(level) >= @loglevel\n  end\n\n  # Reopen all of our logs.\n  def Log.reopen\n    Puppet.notice _(\"Reopening log files\")\n    types = @destinations.keys\n    @destinations.each { |_type, dest|\n      dest.close if dest.respond_to?(:close)\n    }\n    @destinations.clear\n    # We need to make sure we always end up with some kind of destination\n    begin\n      types.each { |type|\n        Log.newdestination(type)\n      }\n    rescue => detail\n      if @destinations.empty?\n        Log.setup_default\n        Puppet.err detail.to_s\n      end\n    end\n  end\n\n  def self.setup_default\n    Log.newdestination(\n      if Puppet.features.syslog?\n        :syslog\n      else\n        Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog]\n      end\n    )\n  end\n\n  # Is the passed level a valid log level?\n  def self.validlevel?(level)\n    @levels.include?(level)\n  end\n\n  def self.from_data_hash(data)\n    obj = allocate\n    obj.initialize_from_hash(data)\n    obj\n  end\n\n  # Log output using scope and level\n  #\n  # @param [Puppet::Parser::Scope] scope\n  # @param [Symbol] level log level\n  # @param [Array<Object>] vals the values to log (will be converted to string and joined with space)\n  #\n  def self.log_func(scope, level, vals)\n    # NOTE: 3x, does this: vals.join(\" \")\n    # New implementation uses the evaluator to get proper formatting per type\n    vals = vals.map { |v| Puppet::Pops::Evaluator::EvaluatorImpl.new.string(v, scope) }\n\n    # Bypass Puppet.<level> call since it picks up source from \"self\" which is not applicable in the 4x\n    # Function API.\n    # TODO: When a function can obtain the file, line, pos of the call merge those in (3x supports\n    #       options :file, :line. (These were never output when calling the 3x logging functions since\n    #       3x scope does not know about the calling location at that detailed level, nor do they\n    #       appear in a report to stdout/error when included). Now, the output simply uses scope (like 3x)\n    #       as this is good enough, but does not reflect the true call-stack, but is a rough estimate\n    #       of where the logging call originates from).\n    #\n    Puppet::Util::Log.create({ :level => level, :source => scope, :message => vals.join(\" \") })\n    nil\n  end\n\n  attr_accessor :time, :remote, :file, :line, :pos, :issue_code, :environment, :node, :backtrace\n  attr_reader :level, :message, :source\n\n  def initialize(args)\n    self.level = args[:level]\n    self.message = args[:message]\n    self.source = args[:source] || \"Puppet\"\n\n    @time = Time.now\n\n    tags = args[:tags]\n    if tags\n      tags.each { |t| tag(t) }\n    end\n\n    # Don't add these unless defined (preserve 3.x API as much as possible)\n    [:file, :line, :pos, :issue_code, :environment, :node, :backtrace].each do |attr|\n      value = args[attr]\n      next unless value\n\n      send(attr.to_s + '=', value)\n    end\n\n    Log.newmessage(self)\n  end\n\n  def initialize_from_hash(data)\n    @level = data['level'].intern\n    @message = data['message']\n    @source = data['source']\n    @tags = Puppet::Util::TagSet.new(data['tags'])\n    @time = data['time']\n    if @time.is_a? String\n      @time = Time.parse(@time)\n    end\n    # Don't add these unless defined (preserve 3.x API as much as possible)\n    %w[file line pos issue_code environment node backtrace].each do |name|\n      value = data[name]\n      next unless value\n\n      send(name + '=', value)\n    end\n  end\n\n  def to_hash\n    to_data_hash\n  end\n\n  def to_data_hash\n    {\n      'level' => @level.to_s,\n      'message' => to_s,\n      'source' => @source,\n      'tags' => @tags.to_a,\n      'time' => @time.iso8601(9),\n      'file' => @file,\n      'line' => @line,\n    }\n  end\n\n  def to_structured_hash\n    hash = {\n      'level' => @level,\n      'message' => @message,\n      'source' => @source,\n      'tags' => @tags.to_a,\n      'time' => @time.iso8601(9),\n    }\n    %w[file line pos issue_code environment node backtrace].each do |name|\n      attr_name = \"@#{name}\"\n      hash[name] = instance_variable_get(attr_name) if instance_variable_defined?(attr_name)\n    end\n    hash\n  end\n\n  def message=(msg)\n    # TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class\n    raise ArgumentError, _(\"Puppet::Util::Log requires a message\") unless msg\n\n    @message = msg.to_s\n  end\n\n  def level=(level)\n    # TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class\n    raise ArgumentError, _(\"Puppet::Util::Log requires a log level\") unless level\n    # TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class\n    raise ArgumentError, _(\"Puppet::Util::Log requires a symbol or string\") unless level.respond_to? \"to_sym\"\n\n    @level = level.to_sym\n    raise ArgumentError, _(\"Invalid log level %{level}\") % { level: @level } unless self.class.validlevel?(@level)\n\n    # Tag myself with my log level\n    tag(level)\n  end\n\n  # If they pass a source in to us, we make sure it is a string, and\n  # we retrieve any tags we can.\n  def source=(source)\n    if defined?(Puppet::Type) && source.is_a?(Puppet::Type)\n      @source = source.path\n      merge_tags_from(source)\n      self.file = source.file\n      self.line = source.line\n    else\n      @source = source.to_s\n    end\n  end\n\n  def to_report\n    \"#{time} #{source} (#{level}): #{self}\"\n  end\n\n  def to_s\n    msg = message\n\n    # Issue based messages do not have details in the message. It\n    # must be appended here\n    unless issue_code.nil?\n      msg = _(\"Could not parse for environment %{environment}: %{msg}\") % { environment: environment, msg: msg } unless environment.nil?\n      msg += Puppet::Util::Errors.error_location_with_space(file, line, pos)\n      msg = _(\"%{msg} on node %{node}\") % { msg: msg, node: node } unless node.nil?\n      if @backtrace.is_a?(Array)\n        msg += \"\\n\"\n        msg += @backtrace.join(\"\\n\")\n      end\n    end\n    msg\n  end\nend\n\n# This is for backward compatibility from when we changed the constant to\n# Puppet::Util::Log because the reports include the constant name. It was\n# considered for removal but left in due to risk of breakage (PUP-7502).\nPuppet::Log = Puppet::Util::Log\n"
  },
  {
    "path": "lib/puppet/util/logging.rb",
    "content": "# frozen_string_literal: true\n\n# A module to make logging a bit easier.\nrequire_relative '../../puppet/util/log'\nrequire_relative '../../puppet/error'\n\nmodule Puppet::Util\nmodule Logging\n  def send_log(level, message)\n    Puppet::Util::Log.create({ :level => level, :source => log_source, :message => message }.merge(log_metadata))\n  end\n\n  # Create a method for each log level.\n  Puppet::Util::Log.eachlevel do |level|\n    # handle debug a special way for performance reasons\n    next if level == :debug\n\n    define_method(level) do |args|\n      args = args.join(\" \") if args.is_a?(Array)\n      send_log(level, args)\n    end\n  end\n\n  # Output a debug log message if debugging is on (but only then)\n  # If the output is anything except a static string, give the debug\n  # a block - it will be called with all other arguments, and is expected\n  # to return the single string result.\n  #\n  # Use a block at all times for increased performance.\n  #\n  # @example This takes 40% of the time compared to not using a block\n  #  Puppet.debug { \"This is a string that interpolated #{x} and #{y} }\"\n  #\n  def debug(*args)\n    return nil unless Puppet::Util::Log.level == :debug\n\n    if block_given?\n      send_log(:debug, yield(*args))\n    else\n      send_log(:debug, args.join(\" \"))\n    end\n  end\n\n  # Log an exception via Puppet.err.  Will also log the backtrace if Puppet[:trace] is set.\n  # Parameters:\n  # [exception] an Exception to log\n  # [message] an optional String overriding the message to be logged; by default, we log Exception.message.\n  #    If you pass a String here, your string will be logged instead.  You may also pass nil if you don't\n  #    wish to log a message at all; in this case it is likely that you are only calling this method in order\n  #    to take advantage of the backtrace logging.\n  def log_exception(exception, message = :default, options = {})\n    level = options[:level] || :err\n    combined_trace = Puppet[:trace] || options[:trace]\n    puppet_trace = Puppet[:puppet_trace] || options[:puppet_trace]\n\n    if message == :default && exception.is_a?(Puppet::ParseErrorWithIssue)\n      # Retain all detailed info and keep plain message and stacktrace separate\n      backtrace = build_exception_trace(exception, combined_trace, puppet_trace)\n      Puppet::Util::Log.create({\n        :level => level,\n        :source => log_source,\n        :message => exception.basic_message,\n        :issue_code => exception.issue_code,\n        :backtrace => backtrace.empty? ? nil : backtrace,\n        :file => exception.file,\n        :line => exception.line,\n        :pos => exception.pos,\n        :environment => exception.environment,\n        :node => exception.node\n      }.merge(log_metadata))\n    else\n      send_log(level, format_exception(exception, message, combined_trace, puppet_trace))\n    end\n  end\n\n  def build_exception_trace(exception, combined_trace = true, puppet_trace = false)\n    built_trace = format_backtrace(exception, combined_trace, puppet_trace)\n\n    if exception.respond_to?(:original)\n      original = exception.original\n      unless original.nil?\n        built_trace << _('Wrapped exception:')\n        built_trace << original.message\n        built_trace += build_exception_trace(original, combined_trace, puppet_trace)\n      end\n    end\n\n    built_trace\n  end\n  private :build_exception_trace\n\n  def format_exception(exception, message = :default, combined_trace = true, puppet_trace = false)\n    arr = []\n    case message\n    when :default\n      arr << exception.message\n    when nil\n      # don't log anything if they passed a nil; they are just calling for the optional backtrace logging\n    else\n      arr << message\n    end\n\n    arr += format_backtrace(exception, combined_trace, puppet_trace)\n\n    if exception.respond_to?(:original) and exception.original\n      arr << _(\"Wrapped exception:\")\n      arr << format_exception(exception.original, :default, combined_trace, puppet_trace)\n    end\n\n    arr.flatten.join(\"\\n\")\n  end\n\n  def format_backtrace(exception, combined_trace, puppet_trace)\n    puppetstack = exception.respond_to?(:puppetstack) ? exception.puppetstack : []\n\n    if combined_trace and exception.backtrace\n      Puppet::Util.format_backtrace_array(exception.backtrace, puppetstack)\n    elsif puppet_trace && !puppetstack.empty?\n      Puppet::Util.format_backtrace_array(puppetstack)\n    else\n      []\n    end\n  end\n\n  def log_and_raise(exception, message)\n    log_exception(exception, message)\n    raise exception, message + \"\\n\" + exception.to_s, exception.backtrace\n  end\n\n  class DeprecationWarning < Exception; end # rubocop:disable Lint/InheritException\n\n  # Logs a warning indicating that the Ruby code path is deprecated.  Note that\n  # this method keeps track of the offending lines of code that triggered the\n  # deprecation warning, and will only log a warning once per offending line of\n  # code.  It will also stop logging deprecation warnings altogether after 100\n  # unique deprecation warnings have been logged.  Finally, if\n  # Puppet[:disable_warnings] includes 'deprecations', it will squelch all\n  # warning calls made via this method.\n  #\n  # @param message [String] The message to log (logs via warning)\n  # @param key [String] Optional key to mark the message as unique. If not\n  #   passed in, the originating call line will be used instead.\n  def deprecation_warning(message, key = nil)\n    issue_deprecation_warning(message, key, nil, nil, true)\n  end\n\n  # Logs a warning whose origin comes from Puppet source rather than somewhere\n  # internal within Puppet.  Otherwise the same as deprecation_warning()\n  #\n  # @param message [String] The message to log (logs via warning)\n  # @param options [Hash]\n  # @option options [String] :file File we are warning from\n  # @option options [Integer] :line Line number we are warning from\n  # @option options [String] :key (:file + :line) Alternative key used to mark\n  #   warning as unique\n  #\n  # Either :file and :line and/or :key must be passed.\n  def puppet_deprecation_warning(message, options = {})\n    key = options[:key]\n    file = options[:file]\n    line = options[:line]\n    # TRANSLATORS the literals \":file\", \":line\", and \":key\" should not be translated\n    raise Puppet::DevError, _(\"Need either :file and :line, or :key\") if key.nil? && (file.nil? || line.nil?)\n\n    key ||= \"#{file}:#{line}\"\n    issue_deprecation_warning(message, key, file, line, false)\n  end\n\n  # Logs a (non deprecation) warning once for a given key.\n  #\n  # @param kind [String] The kind of warning. The\n  #   kind must be one of the defined kinds for the Puppet[:disable_warnings] setting.\n  # @param message [String] The message to log (logs via warning)\n  # @param key [String] Key used to make this warning unique\n  # @param file [String,:default,nil] the File related to the warning\n  # @param line [Integer,:default,nil] the Line number related to the warning\n  #   warning as unique\n  # @param level [Symbol] log level to use, defaults to :warning\n  #\n  # Either :file and :line and/or :key must be passed.\n  def warn_once(kind, key, message, file = nil, line = nil, level = :warning)\n    return if Puppet[:disable_warnings].include?(kind)\n\n    $unique_warnings ||= {}\n    if $unique_warnings.length < 100 then\n      unless $unique_warnings.has_key?(key) then\n        $unique_warnings[key] = message\n        call_trace = if file == :default and line == :default\n                       # Suppress the file and line number output\n                       ''\n                     else\n                       error_location_str = Puppet::Util::Errors.error_location(file, line)\n                       if error_location_str.empty?\n                         \"\\n   \" + _('(file & line not available)')\n                       else\n                         \"\\n   %{error_location}\" % { error_location: error_location_str }\n                       end\n                     end\n        send_log(level, \"#{message}#{call_trace}\")\n      end\n    end\n  end\n\n  def get_deprecation_offender\n    # we have to put this in its own method to simplify testing; we need to be able to mock the offender results in\n    # order to test this class, and our framework does not appear to enjoy it if you try to mock Kernel.caller\n    #\n    # let's find the offending line;  we need to jump back up the stack a few steps to find the method that called\n    #  the deprecated method\n    if Puppet[:trace]\n      caller(3)\n    else\n      [caller(3, 1).first]\n    end\n  end\n\n  def clear_deprecation_warnings\n    $unique_warnings.clear if $unique_warnings\n    $deprecation_warnings.clear if $deprecation_warnings\n  end\n\n  # TODO: determine whether there might be a potential use for adding a puppet configuration option that would\n  # enable this deprecation logging.\n\n  # utility method that can be called, e.g., from spec_helper config.after, when tracking down calls to deprecated\n  # code.\n  # Parameters:\n  # [deprecations_file] relative or absolute path of a file to log the deprecations to\n  # [pattern] (default nil) if specified, will only log deprecations whose message matches the provided pattern\n  def log_deprecations_to_file(deprecations_file, pattern = nil)\n    # this method may get called lots and lots of times (e.g., from spec_helper config.after) without the global\n    # list of deprecation warnings being cleared out.  We don't want to keep logging the same offenders over and over,\n    # so, we need to keep track of what we've logged.\n    #\n    # It'd be nice if we could just clear out the list of deprecation warnings, but then the very next spec might\n    # find the same offender, and we'd end up logging it again.\n    $logged_deprecation_warnings ||= {}\n\n    # Deprecation messages are UTF-8 as they are produced by Ruby\n    Puppet::FileSystem.open(deprecations_file, nil, \"a:UTF-8\") do |f|\n      if $deprecation_warnings then\n        $deprecation_warnings.each do |offender, message|\n          next if $logged_deprecation_warnings.has_key?(offender)\n\n          $logged_deprecation_warnings[offender] = true\n          next unless pattern.nil? || (message =~ pattern)\n\n          f.puts(message)\n          f.puts(offender)\n          f.puts()\n        end\n      end\n    end\n  end\n\n  # Sets up Facter logging.\n  # This method causes Facter output to be forwarded to Puppet.\n  def self.setup_facter_logging!\n    Puppet.runtime[:facter]\n    true\n  end\n\n  private\n\n  def issue_deprecation_warning(message, key, file, line, use_caller)\n    return if Puppet[:disable_warnings].include?('deprecations')\n\n    $deprecation_warnings ||= {}\n    if $deprecation_warnings.length < 100\n      key ||= (offender = get_deprecation_offender)\n      unless $deprecation_warnings.has_key?(key)\n        $deprecation_warnings[key] = message\n        # split out to allow translation\n        call_trace = if use_caller\n                       _(\"(location: %{location})\") % { location: (offender || get_deprecation_offender).join('; ') }\n                     else\n                       Puppet::Util::Errors.error_location_with_unknowns(file, line)\n                     end\n        warning(\"%{message}\\n   %{call_trace}\" % { message: message, call_trace: call_trace })\n      end\n    end\n  end\n\n  def is_resource?\n    defined?(Puppet::Type) && is_a?(Puppet::Type)\n  end\n\n  def is_resource_parameter?\n    defined?(Puppet::Parameter) && is_a?(Puppet::Parameter)\n  end\n\n  def log_metadata\n    [:file, :line, :tags].each_with_object({}) do |attr, result|\n      result[attr] = send(attr) if respond_to?(attr)\n    end\n  end\n\n  def log_source\n    # We need to guard the existence of the constants, since this module is used by the base Puppet module.\n    (is_resource? or is_resource_parameter?) and respond_to?(:path) and return path.to_s\n    to_s\n  end\nend\nend\n"
  },
  {
    "path": "lib/puppet/util/metaid.rb",
    "content": "# frozen_string_literal: true\n\nclass Object\n  # The hidden singleton lurks behind everyone\n  def singleton_class; class << self; self; end; end\n  def meta_eval(&blk); singleton_class.instance_eval(&blk); end\n\n  # Adds methods to a singleton_class\n  def meta_def(name, &blk)\n    meta_eval { define_method name, &blk }\n  end\n\n  # Remove singleton_class methods.\n  def meta_undef(name, &blk)\n    meta_eval { remove_method name }\n  end\n\n  # Defines an instance method within a class\n  def class_def(name, &blk)\n    class_eval { define_method name, &blk }\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/metric.rb",
    "content": "# frozen_string_literal: true\n\n# included so we can test object types\nrequire_relative '../../puppet'\nrequire_relative '../../puppet/network/format_support'\n\n# A class for handling metrics.  This is currently ridiculously hackish.\nclass Puppet::Util::Metric\n  include Puppet::Util::PsychSupport\n  include Puppet::Network::FormatSupport\n\n  attr_accessor :type, :name, :value, :label\n  attr_writer :values\n\n  def self.from_data_hash(data)\n    metric = allocate\n    metric.initialize_from_hash(data)\n    metric\n  end\n\n  def initialize_from_hash(data)\n    @name = data['name']\n    @label = data['label'] || self.class.labelize(@name)\n    @values = data['values']\n  end\n\n  def to_data_hash\n    {\n      'name' => @name,\n      'label' => @label,\n      'values' => @values\n    }\n  end\n\n  # Return a specific value\n  def [](name)\n    value = @values.find { |v| v[0] == name }\n    if value\n      value[2]\n    else\n      0\n    end\n  end\n\n  def initialize(name, label = nil)\n    @name = name.to_s\n\n    @label = label || self.class.labelize(name)\n\n    @values = []\n  end\n\n  def newvalue(name, value, label = nil)\n    raise ArgumentError, \"metric name #{name.inspect} is not a string\" unless name.is_a? String\n\n    label ||= self.class.labelize(name)\n    @values.push [name, label, value]\n  end\n\n  def values\n    @values.sort_by { |a| a[1] }\n  end\n\n  # Convert a name into a label.\n  def self.labelize(name)\n    name.to_s.capitalize.tr(\"_\", \" \")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/monkey_patches.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/platform'\n\nmodule Puppet::Util::MonkeyPatches\nend\n\nbegin\n  Process.maxgroups = 1024\nrescue NotImplementedError\n  # Actually, I just want to ignore it, since various platforms - JRuby,\n  # Windows, and so forth - don't support it, but only because it isn't a\n  # meaningful or implementable concept there.\nend\n\nmodule RDoc\n  def self.caller(skip = nil)\n    in_gem_wrapper = false\n    Kernel.caller.reject { |call|\n      in_gem_wrapper ||= call =~ /#{Regexp.escape $PROGRAM_NAME}:\\d+:in `load'/\n    }\n  end\nend\n\nclass Object\n  # ActiveSupport 2.3.x mixes in a dangerous method\n  # that can cause rspec to fork bomb\n  # and other strange things like that.\n  def daemonize\n    raise NotImplementedError, \"Kernel.daemonize is too dangerous, please don't try to use it.\"\n  end\nend\n\nunless Dir.singleton_methods.include?(:exists?)\n  class Dir\n    def self.exists?(file_name)\n      warn(\"Dir.exists?('#{file_name}') is deprecated, use Dir.exist? instead\") if $VERBOSE\n      Dir.exist?(file_name)\n    end\n  end\nend\n\nunless File.singleton_methods.include?(:exists?)\n  class File\n    def self.exists?(file_name)\n      warn(\"File.exists?('#{file_name}') is deprecated, use File.exist? instead\") if $VERBOSE\n      File.exist?(file_name)\n    end\n  end\nend\n\nrequire_relative '../../puppet/ssl/openssl_loader'\nunless Puppet::Util::Platform.jruby_fips?\n  class OpenSSL::SSL::SSLContext\n    if DEFAULT_PARAMS[:options]\n      DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_NO_SSLv3\n    else\n      DEFAULT_PARAMS[:options] = OpenSSL::SSL::OP_NO_SSLv3\n    end\n\n    alias __original_initialize initialize\n    private :__original_initialize\n\n    def initialize(*args)\n      __original_initialize(*args)\n      params = {\n        :options => DEFAULT_PARAMS[:options],\n        :ciphers => DEFAULT_PARAMS[:ciphers],\n      }\n      set_params(params)\n    end\n  end\nend\n\nif Puppet::Util::Platform.windows?\n  class OpenSSL::X509::Store\n    @puppet_certs_loaded = false\n    alias __original_set_default_paths set_default_paths\n    def set_default_paths\n      # This can be removed once openssl integrates with windows\n      # cert store, see https://rt.openssl.org/Ticket/Display.html?id=2158\n      unless @puppet_certs_loaded\n        @puppet_certs_loaded = true\n\n        Puppet::Util::Windows::RootCerts.instance.to_a.uniq(&:to_der).each do |x509|\n          add_cert(x509)\n        rescue OpenSSL::X509::StoreError\n          warn \"Failed to add #{x509.subject.to_utf8}\"\n        end\n      end\n\n      __original_set_default_paths\n    end\n  end\nend\n\nunless Puppet::Util::Platform.jruby_fips?\n  unless defined?(OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH)\n    module OpenSSL::X509\n      OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH = 0x3E\n    end\n  end\n\n  # jruby-openssl doesn't support this\n  unless OpenSSL::X509::Name.instance_methods.include?(:to_utf8)\n    class OpenSSL::X509::Name\n      def to_utf8\n        # https://github.com/ruby/ruby/blob/v2_5_5/ext/openssl/ossl_x509name.c#L317\n        str = to_s(OpenSSL::X509::Name::RFC2253)\n        str.force_encoding(Encoding::UTF_8)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/multi_match.rb",
    "content": "# frozen_string_literal: true\n\n# MultiMatch allows multiple values to be tested at once in a case expression.\n# This class is needed since Array does not implement the === operator to mean\n# \"each v === other.each v\".\n#\n# This class is useful in situations when the Puppet Type System cannot be used\n# (e.g. in Logging, since it needs to be able to log very early in the initialization\n# cycle of puppet)\n#\n# Typically used with the constants\n# NOT_NIL\n# TUPLE\n# TRIPLE\n#\n# which test against single NOT_NIL value, Array with two NOT_NIL, and Array with three NOT_NIL\n#\nmodule Puppet::Util\nclass MultiMatch\n  attr_reader :values\n\n  def initialize(*values)\n    @values = values\n  end\n\n  def ===(other)\n    lv = @values # local var is faster than instance var\n    case other\n    when MultiMatch\n      return false unless other.values.size == values.size\n\n      other.values.each_with_index { |v, i| return false unless lv[i] === v || v === lv[i] }\n    when Array\n      return false unless other.size == values.size\n\n      other.each_with_index { |v, i| return false unless lv[i] === v || v === lv[i] }\n    else\n      false\n    end\n    true\n  end\n\n  # Matches any value that is not nil using the === operator.\n  #\n  class MatchNotNil\n    def ===(v)\n      !v.nil?\n    end\n  end\n\n  NOT_NIL = MatchNotNil.new().freeze\n  TUPLE = MultiMatch.new(NOT_NIL, NOT_NIL).freeze\n  TRIPLE = MultiMatch.new(NOT_NIL, NOT_NIL, NOT_NIL).freeze\nend\nend\n"
  },
  {
    "path": "lib/puppet/util/network_device/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/autoload'\nrequire 'uri'\nrequire_relative '../../../puppet/util/network_device/transport'\nrequire_relative '../../../puppet/util/network_device/transport/base'\n\nclass Puppet::Util::NetworkDevice::Base\n  attr_accessor :url, :transport\n\n  def initialize(url, options = {})\n    @url = URI.parse(url)\n\n    @autoloader = Puppet::Util::Autoload.new(self, \"puppet/util/network_device/transport\")\n\n    if @autoloader.load(@url.scheme, Puppet.lookup(:current_environment))\n      @transport = Puppet::Util::NetworkDevice::Transport.const_get(@url.scheme.capitalize).new(options[:debug])\n      @transport.host = @url.host\n      @transport.port = @url.port || case @url.scheme; when \"ssh\"; 22; when \"telnet\"; 23; end\n      @transport.user = @url.user\n      @transport.password = @url.password\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/network_device/config.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'ostruct'\nrequire_relative '../../../puppet/util/watched_file'\nrequire_relative '../../../puppet/util/network_device'\n\nclass Puppet::Util::NetworkDevice::Config\n  def self.main\n    @main ||= new\n  end\n\n  def self.devices\n    main.devices || []\n  end\n\n  attr_reader :devices\n\n  def exists?\n    Puppet::FileSystem.exist?(@file.to_str)\n  end\n\n  def initialize\n    @file = Puppet::Util::WatchedFile.new(Puppet[:deviceconfig])\n\n    @devices = {}\n\n    read(true) # force reading at start\n  end\n\n  # Read the configuration file.\n  def read(force = false)\n    return unless exists?\n\n    parse if force or @file.changed?\n  end\n\n  private\n\n  def parse\n    begin\n      devices = {}\n      device = nil\n      File.open(@file) do |f|\n        file_line_count = 1\n        f.each do |line|\n          case line\n          when /^\\s*#/ # skip comments\n            file_line_count += 1\n            next\n          when /^\\s*$/ # skip blank lines\n            file_line_count += 1\n            next\n          when /^\\[([\\w.-]+)\\]\\s*$/ # [device.fqdn]\n            name = ::Regexp.last_match(1)\n            name.chomp!\n            if devices.include?(name)\n              file_error_location = Puppet::Util::Errors.error_location(nil, file_line_count)\n              device_error_location = Puppet::Util::Errors.error_location(nil, device.line)\n              raise Puppet::Error, _(\"Duplicate device found at %{file_error_location}, already found at %{device_error_location}\") %\n                                   { file_error_location: file_error_location, device_error_location: device_error_location }\n            end\n            device = OpenStruct.new\n            device.name = name\n            device.line = file_line_count\n            device.options = { :debug => false }\n            Puppet.debug \"found device: #{device.name} at #{device.line}\"\n            devices[name] = device\n          when /^\\s*(type|url|debug)(\\s+(.+)\\s*)*$/\n            parse_directive(device, ::Regexp.last_match(1), ::Regexp.last_match(3), file_line_count)\n          else\n            error_location_str = Puppet::Util::Errors.error_location(nil, file_line_count)\n            raise Puppet::Error, _(\"Invalid entry at %{error_location}: %{file_text}\") %\n                                 { error_location: error_location_str, file_text: line }\n          end\n        end\n      end\n    rescue Errno::EACCES\n      Puppet.err _(\"Configuration error: Cannot read %{file}; cannot serve\") % { file: @file }\n      # raise Puppet::Error, \"Cannot read #{@config}\"\n    rescue Errno::ENOENT\n      Puppet.err _(\"Configuration error: '%{file}' does not exit; cannot serve\") % { file: @file }\n    end\n\n    @devices = devices\n  end\n\n  def parse_directive(device, var, value, count)\n    case var\n    when \"type\"\n      device.provider = value\n    when \"url\"\n      begin\n        URI.parse(value)\n      rescue URI::InvalidURIError\n        raise Puppet::Error, _(\"%{value} is an invalid url\") % { value: value }\n      end\n      device.url = value\n    when \"debug\"\n      device.options[:debug] = true\n    else\n      error_location_str = Puppet::Util::Errors.error_location(nil, count)\n      raise Puppet::Error, _(\"Invalid argument '%{var}' at %{error_location}\") % { var: var, error_location: error_location_str }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/network_device/transport/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/util/network_device'\nrequire_relative '../../../../puppet/util/network_device/transport'\n\nclass Puppet::Util::NetworkDevice::Transport::Base\n  attr_accessor :user, :password, :host, :port\n  attr_accessor :default_prompt, :timeout\n\n  def initialize\n    @timeout = 10\n  end\n\n  def send(cmd)\n  end\n\n  def expect(prompt)\n  end\n\n  def command(cmd, options = {})\n    send(cmd)\n    expect(options[:prompt] || default_prompt) do |output|\n      yield output if block_given?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/network_device/transport.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/network_device'\n\n# stub\nmodule Puppet::Util::NetworkDevice::Transport\nend\n"
  },
  {
    "path": "lib/puppet/util/network_device.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Util::NetworkDevice\n  class << self\n    attr_reader :current\n  end\n\n  def self.init(device)\n    require \"puppet/util/network_device/#{device.provider}/device\"\n    @current = Puppet::Util::NetworkDevice.const_get(device.provider.capitalize).const_get(:Device).new(device.url, device.options)\n  rescue => detail\n    raise detail, _(\"Can't load %{provider} for %{device}: %{detail}\") % { provider: device.provider, device: device.name, detail: detail }, detail.backtrace\n  end\n\n  # Should only be used in tests\n  def self.teardown\n    @current = nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/debian.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Package::Version\n  class Debian < Numeric\n    include Comparable\n\n    # Version string matching regexes\n    REGEX_EPOCH = '(?:([0-9]+):)?'\n    # alphanumerics and the characters . + - ~ , starts with a digit, ~ only of debian_revision is present\n    REGEX_UPSTREAM_VERSION = '([\\.\\+~0-9a-zA-Z-]+?)'\n    # alphanumerics and the characters + . ~\n    REGEX_DEBIAN_REVISION = '(?:-([\\.\\+~0-9a-zA-Z]*))?'\n\n    REGEX_FULL    = REGEX_EPOCH + REGEX_UPSTREAM_VERSION + REGEX_DEBIAN_REVISION.freeze\n    REGEX_FULL_RX = /\\A#{REGEX_FULL}\\Z/\n\n    class ValidationFailure < ArgumentError; end\n\n    def self.parse(ver)\n      raise ValidationFailure, \"Unable to parse '#{ver}' as a string\" unless ver.is_a?(String)\n\n      match, epoch, upstream_version, debian_revision = *ver.match(REGEX_FULL_RX)\n\n      raise ValidationFailure, \"Unable to parse '#{ver}' as a debian version identifier\" unless match\n\n      new(epoch.to_i, upstream_version, debian_revision).freeze\n    end\n\n    def to_s\n      s = @upstream_version\n      s = \"#{@epoch}:#{s}\" if @epoch != 0\n      s = \"#{s}-#{@debian_revision}\" if @debian_revision\n      s\n    end\n    alias inspect to_s\n\n    def eql?(other)\n      other.is_a?(self.class) &&\n        @epoch.eql?(other.epoch) &&\n        @upstream_version.eql?(other.upstream_version) &&\n        @debian_revision.eql?(other.debian_revision)\n    end\n    alias == eql?\n\n    def <=>(other)\n      return nil unless other.is_a?(self.class)\n\n      cmp = @epoch <=> other.epoch\n      if cmp == 0\n        cmp = compare_upstream_version(other)\n        if cmp == 0\n          cmp = compare_debian_revision(other)\n        end\n      end\n      cmp\n    end\n\n    attr_reader :epoch, :upstream_version, :debian_revision\n\n    private\n\n    def initialize(epoch, upstream_version, debian_revision)\n      @epoch            = epoch\n      @upstream_version = upstream_version\n      @debian_revision  = debian_revision\n    end\n\n    def compare_upstream_version(other)\n      mine = @upstream_version\n      yours = other.upstream_version\n      compare_debian_versions(mine, yours)\n    end\n\n    def compare_debian_revision(other)\n      mine = @debian_revision\n      yours = other.debian_revision\n      compare_debian_versions(mine, yours)\n    end\n\n    def compare_debian_versions(mine, yours)\n      #   First the initial part of each string consisting entirely of non-digit characters is determined.\n      # These two parts (one of which may be empty) are compared lexically. If a difference is found it is\n      # returned. The lexical comparison is a comparison of ASCII values modified so that all the letters\n      # sort earlier than all the non-letters and so that a tilde sorts before anything, even the end of a\n      # part. For example, the following parts are in sorted order from earliest to latest: ~~, ~~a, ~, the\n      # empty part, a.\n      #\n      #   Then the initial part of the remainder of each string which consists entirely of digit characters\n      # is determined. The numerical values of these two parts are compared, and any difference found is\n      # returned as the result of the comparison. For these purposes an empty string (which can only occur\n      # at the end of one or both version strings being compared) counts as zero.\n      #\n      #   These two steps (comparing and removing initial non-digit strings and initial digit strings) are\n      # repeated until a difference is found or both strings are exhausted.\n\n      mine_index = 0\n      yours_index = 0\n      cmp = 0\n      mine ||= ''\n      yours ||= ''\n      while mine_index < mine.length && yours_index < yours.length && cmp == 0\n        # handle ~\n        _mymatch, mytilde = *match_tildes(mine.slice(mine_index..-1))\n        mytilde ||= ''\n\n        _yoursmatch, yourstilde = *match_tildes(yours.slice(yours_index..-1))\n        yourstilde ||= ''\n\n        cmp = -1 * (mytilde.length <=> yourstilde.length)\n        mine_index += mytilde.length\n        yours_index += yourstilde.length\n\n        next unless cmp == 0 # handle letters\n\n        _mymatch, myletters = *match_letters(mine.slice(mine_index..-1))\n        myletters ||= ''\n\n        _yoursmatch, yoursletters = *match_letters(yours.slice(yours_index..-1))\n        yoursletters ||= ''\n\n        cmp = myletters <=> yoursletters\n        mine_index += myletters.length\n        yours_index += yoursletters.length\n\n        next unless cmp == 0 # handle nonletters except tilde\n\n        _mymatch, mynon_letters = *match_non_letters(mine.slice(mine_index..-1))\n        mynon_letters ||= ''\n\n        _yoursmatch, yoursnon_letters = *match_non_letters(yours.slice(yours_index..-1))\n        yoursnon_letters ||= ''\n\n        cmp = mynon_letters <=> yoursnon_letters\n        mine_index += mynon_letters.length\n        yours_index += yoursnon_letters.length\n\n        next unless cmp == 0 # handle digits\n\n        _mymatch, mydigits = *match_digits(mine.slice(mine_index..-1))\n        mydigits ||= ''\n\n        _yoursmatch, yoursdigits = *match_digits(yours.slice(yours_index..-1))\n        yoursdigits ||= ''\n\n        cmp = mydigits.to_i <=> yoursdigits.to_i\n        mine_index += mydigits.length\n        yours_index += yoursdigits.length\n      end\n      if cmp == 0\n        if mine_index < mine.length && match_tildes(mine[mine_index])\n          cmp = -1\n        elsif yours_index < yours.length && match_tildes(yours[yours_index])\n          cmp = 1\n        else\n          cmp = mine.length <=> yours.length\n        end\n      end\n      cmp\n    end\n\n    def match_digits(a)\n      a.match(/^([0-9]+)/)\n    end\n\n    def match_non_letters(a)\n      a.match(/^([.+-]+)/)\n    end\n\n    def match_tildes(a)\n      a.match(/^(~+)/)\n    end\n\n    def match_letters(a)\n      a.match(/^([A-Za-z]+)/)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/gem.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Package::Version\n  class Gem < ::Gem::Version\n    def self.parse(version)\n      raise ValidationFailure, version unless version.is_a? String\n      raise ValidationFailure, version unless version =~ ANCHORED_VERSION_PATTERN\n\n      new(version)\n    end\n\n    class ValidationFailure < ArgumentError\n      def initialize(version)\n        super(\"#{version} is not a valid ruby gem version.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/pip.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Package::Version\n  class Pip\n    include Comparable\n\n    VERSION_PATTERN = \"\n      v?\n      (?:\n        (?:(?<epoch>[0-9]+)!)?                              # epoch\n        (?<release>[0-9]+(?:\\\\.[0-9]+)*)                    # release segment\n        (?<pre>                                             # pre-release\n          [-_\\\\.]?\n          (?<pre_l>(a|b|c|rc|alpha|beta|pre|preview))\n          [-_\\\\.]?\n          (?<pre_n>[0-9]+)?\n        )?\n        (?<post>                                            # post release\n          (?:-(?<post_n1>[0-9]+))\n          |\n          (?:\n            [-_\\\\.]?\n            (?<post_l>post|rev|r)\n            [-_\\\\.]?\n            (?<post_n2>[0-9]+)?\n          )\n        )?\n        (?<dev>                                             # dev release\n          [-_\\\\.]?\n          (?<dev_l>dev)\n          [-_\\\\.]?\n          (?<dev_n>[0-9]+)?\n        )?\n      )\n      (?:\\\\+(?<local>[a-z0-9]+(?:[-_\\\\.][a-z0-9]+)*))?      # local version\n    \"\n\n    def self.parse(version)\n      raise ValidationFailure, version.to_s unless version.is_a? String\n\n      matched = version.match(Regexp.new(\"^\\\\s*\" + VERSION_PATTERN + \"\\\\s*$\", Regexp::EXTENDED | Regexp::MULTILINE | Regexp::IGNORECASE))\n      raise ValidationFailure, version unless matched\n\n      new(matched)\n    end\n\n    def self.compare(version_a, version_b)\n      version_a = parse(version_a) unless version_a.is_a?(self)\n      version_b = parse(version_b) unless version_b.is_a?(self)\n\n      version_a <=> version_b\n    end\n\n    def to_s\n      parts = []\n\n      parts.push(\"#{@epoch_data}!\")           if @epoch_data && @epoch_data != 0\n      parts.push(@release_data.join(\".\"))     if @release_data\n      parts.push(@pre_data.join)              if @pre_data\n      parts.push(\".post#{@post_data[1]}\")     if @post_data\n      parts.push(\".dev#{@dev_data[1]}\")       if @dev_data\n      parts.push(\"+#{@local_data.join('.')}\") if @local_data\n\n      parts.join\n    end\n    alias inspect to_s\n\n    def eql?(other)\n      other.is_a?(self.class) && key.eql?(other.key)\n    end\n    alias == eql?\n\n    def <=>(other)\n      raise ValidationFailure, other.to_s unless other.is_a?(self.class)\n\n      compare(key, other.key)\n    end\n\n    attr_reader :key\n\n    private\n\n    def initialize(matched)\n      @epoch_data   = matched[:epoch].to_i\n      @release_data = matched[:release].split('.').map(&:to_i)                                       if matched[:release]\n      @pre_data     = parse_letter_version(matched[:pre_l], matched[:pre_n])                         if matched[:pre_l]  || matched[:pre_n]\n      @post_data    = parse_letter_version(matched[:post_l], matched[:post_n1] || matched[:post_n2]) if matched[:post_l] || matched[:post_n1] || matched[:post_n2]\n      @dev_data     = parse_letter_version(matched[:dev_l], matched[:dev_n])                         if matched[:dev_l]  || matched[:dev_n]\n      @local_data   = parse_local_version(matched[:local])                                           if matched[:local]\n\n      @key = compose_key(@epoch_data, @release_data, @pre_data, @post_data, @dev_data, @local_data)\n    end\n\n    def parse_letter_version(letter, number)\n      if letter\n        number ||= 0\n        letter.downcase!\n\n        if letter == \"alpha\"\n          letter = \"a\"\n        elsif letter == \"beta\"\n          letter = \"b\"\n        elsif %w[c pre preview].include?(letter)\n          letter = \"rc\"\n        elsif %w[rev r].include?(letter)\n          letter = \"post\"\n        end\n\n        return [letter, number.to_i]\n      end\n\n      [\"post\", number.to_i] if !letter && number\n    end\n\n    def parse_local_version(local_version)\n      local_version.split(/[\\\\._-]/).map { |part| part =~ /[0-9]+/ && part !~ /[a-zA-Z]+/ ? part.to_i : part.downcase } if local_version\n    end\n\n    def compose_key(epoch, release, pre, post, dev, local)\n      release_key = release.reverse\n      release_key.each_with_index do |element, index|\n        break unless element == 0\n\n        release_key.delete_at(index) unless release_key.at(index + 1) != 0\n      end\n      release_key.reverse!\n\n      if !pre && !post && dev\n        pre_key = -Float::INFINITY\n      else\n        pre_key = pre || Float::INFINITY\n      end\n\n      post_key = post || -Float::INFINITY\n\n      dev_key = dev || Float::INFINITY\n\n      if !local\n        local_key = [[-Float::INFINITY, \"\"]]\n      else\n        local_key = local.map { |i| (i.is_a? Integer) ? [i, \"\"] : [-Float::INFINITY, i] }\n      end\n\n      [epoch, release_key, pre_key, post_key, dev_key, local_key]\n    end\n\n    def compare(this, other)\n      if (this.is_a? Array) && (other.is_a? Array)\n        this  << -Float::INFINITY if this.length < other.length\n        other << -Float::INFINITY if this.length > other.length\n\n        this.each_with_index do |element, index|\n          return compare(element, other.at(index)) if element != other.at(index)\n        end\n      elsif (this.is_a? Array) && !(other.is_a? Array)\n        raise Puppet::Error, \"Cannot compare #{this} (Array) with #{other} (#{other.class}). Only ±Float::INFINITY accepted.\" unless other.abs == Float::INFINITY\n\n        return other == -Float::INFINITY ? 1 : -1\n      elsif !(this.is_a? Array) && (other.is_a? Array)\n        raise Puppet::Error, \"Cannot compare #{this} (#{this.class}) with #{other} (Array). Only ±Float::INFINITY accepted.\" unless this.abs == Float::INFINITY\n\n        return this == -Float::INFINITY ? -1 : 1\n      end\n      this <=> other\n    end\n\n    class ValidationFailure < ArgumentError\n      def initialize(version)\n        super(\"#{version} is not a valid python package version. Please refer to https://www.python.org/dev/peps/pep-0440/.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/eq.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range/simple'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class Eq < Simple\n      def to_s\n        @version.to_s\n      end\n\n      def include?(version)\n        version == @version\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/gt.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range/simple'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class Gt < Simple\n      def to_s\n        \">#{@version}\"\n      end\n\n      def include?(version)\n        version > @version\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/gt_eq.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range/simple'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class GtEq < Simple\n      def to_s\n        \">=#{@version}\"\n      end\n\n      def include?(version)\n        version >= @version\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/lt.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range/simple'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class Lt < Simple\n      def to_s\n        \"<#{@version}\"\n      end\n\n      def include?(version)\n        version < @version\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/lt_eq.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range/simple'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class LtEq < Simple\n      def to_s\n        \"<=#{@version}\"\n      end\n\n      def include?(version)\n        version <= @version\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/min_max.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class MinMax\n      def initialize(min, max)\n        @min = min\n        @max = max\n      end\n\n      def to_s\n        \"#{@min} #{@max}\"\n      end\n\n      def to_gem_version\n        \"#{@min}, #{@max}\"\n      end\n\n      def include?(version)\n        @min.include?(version) && @max.include?(version)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range/simple.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../../puppet/util/package/version/range'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class Simple\n      def initialize(version)\n        @version = version\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/range.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'range/lt'\nrequire_relative 'range/lt_eq'\nrequire_relative 'range/gt'\nrequire_relative 'range/gt_eq'\nrequire_relative 'range/eq'\nrequire_relative 'range/min_max'\n\nmodule Puppet::Util::Package::Version\n  class Range\n    class ValidationFailure < ArgumentError; end\n    # Parses a version range string into a comparable {Range} instance.\n    #\n    # Currently parsed version range string may take any of the following\n    # forms:\n    #\n    # * Regular Version strings\n    #   * ex. `\"1.0.0\"`, `\"1.2.3-pre\"`\n    # * Inequalities\n    #   * ex. `\">1.0.0\"`, `\"<3.2.0\"`, `\">=4.0.0\"`\n    # * Range Intersections (min is always first)\n    #   * ex. `\">1.0.0 <=2.3.0\"`\n    #\n    RANGE_SPLIT = /\\s+/\n    FULL_REGEX = /\\A((?:[<>=])*)(.+)\\Z/\n\n    # @param range_string [String] the version range string to parse\n    # @param version_class [Version] a version class implementing comparison operators and parse method\n    # @return [Range] a new {Range} instance\n    # @api public\n    def self.parse(range_string, version_class)\n      raise ValidationFailure, \"Unable to parse '#{range_string}' as a string\" unless range_string.is_a?(String)\n\n      simples = range_string.split(RANGE_SPLIT).map do |simple|\n        match, operator, version = *simple.match(FULL_REGEX)\n        raise ValidationFailure, \"Unable to parse '#{simple}' as a version range identifier\" unless match\n\n        case operator\n        when '>'\n          Gt.new(version_class.parse(version))\n        when '>='\n          GtEq.new(version_class.parse(version))\n        when '<'\n          Lt.new(version_class.parse(version))\n        when '<='\n          LtEq.new(version_class.parse(version))\n        when ''\n          Eq.new(version_class.parse(version))\n        else\n          raise ValidationFailure, \"Operator '#{operator}' is not implemented\"\n        end\n      end\n      simples.size == 1 ? simples[0] : MinMax.new(simples[0], simples[1])\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package/version/rpm.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/util/rpm_compare'\n\nmodule Puppet::Util::Package::Version\n  class Rpm < Numeric\n    # provides Rpm parsing and comparison\n    extend Puppet::Util::RpmCompare\n    include Puppet::Util::RpmCompare\n    include Comparable\n\n    class ValidationFailure < ArgumentError; end\n\n    attr_reader :epoch, :version, :release, :arch\n\n    def self.parse(ver)\n      raise ValidationFailure unless ver.is_a?(String)\n\n      version = rpm_parse_evr(ver)\n      new(version[:epoch], version[:version], version[:release], version[:arch]).freeze\n    end\n\n    def to_s\n      version_found = ''.dup\n      version_found += \"#{@epoch}:\" if @epoch\n      version_found += @version\n      version_found += \"-#{@release}\" if @release\n      version_found\n    end\n    alias inspect to_s\n\n    def eql?(other)\n      other.is_a?(self.class) &&\n        @epoch.eql?(other.epoch) &&\n        @version.eql?(other.version) &&\n        @release.eql?(other.release) &&\n        @arch.eql?(other.arch)\n    end\n    alias == eql?\n\n    def <=>(other)\n      raise ArgumentError, _(\"Cannot compare, as %{other} is not a Rpm Version\") % { other: other } unless other.is_a?(self.class)\n\n      rpm_compare_evr(to_s, other.to_s)\n    end\n\n    private\n\n    # overwrite rpm_compare_evr to treat no epoch as zero epoch\n    # in order to compare version correctly\n    #\n    # returns 1 if a is newer than b,\n    #         0 if they are identical\n    #        -1 if a is older than b\n    def rpm_compare_evr(a, b)\n      a_hash = rpm_parse_evr(a)\n      b_hash = rpm_parse_evr(b)\n\n      a_hash[:epoch] ||= '0'\n      b_hash[:epoch] ||= '0'\n\n      rc = compare_values(a_hash[:epoch], b_hash[:epoch])\n      return rc unless rc == 0\n\n      super(a, b)\n    end\n\n    def initialize(epoch, version, release, arch)\n      @epoch   = epoch\n      @version = version\n      @release = release\n      @arch    = arch\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/package.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Package\n  def versioncmp(version_a, version_b, ignore_trailing_zeroes = false)\n    vre = /[-.]|\\d+|[^-.\\d]+/\n\n    if ignore_trailing_zeroes\n      version_a = normalize(version_a)\n      version_b = normalize(version_b)\n    end\n\n    ax = version_a.scan(vre)\n    bx = version_b.scan(vre)\n\n    while ax.length > 0 && bx.length > 0\n      a = ax.shift\n      b = bx.shift\n\n      next      if a == b\n      return -1 if a == '-'\n      return 1  if b == '-'\n      return -1 if a == '.'\n      return 1  if b == '.'\n\n      if a =~ /^\\d+$/ && b =~ /^\\d+$/\n        return a.to_s.upcase <=> b.to_s.upcase if a =~ /^0/ || b =~ /^0/\n\n        return a.to_i <=> b.to_i\n      end\n      return a.upcase <=> b.upcase\n    end\n    version_a <=> version_b\n  end\n  module_function :versioncmp\n\n  def self.normalize(version)\n    version = version.split('-')\n    version.first.sub!(/([.0]+)$/, '')\n\n    version.join('-')\n  end\n  private_class_method :normalize\nend\n"
  },
  {
    "path": "lib/puppet/util/pidlock.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'fileutils'\nrequire_relative '../../puppet/util/lockfile'\n\nclass Puppet::Util::Pidlock\n  def initialize(lockfile)\n    @lockfile = Puppet::Util::Lockfile.new(lockfile)\n  end\n\n  def locked?\n    clear_if_stale\n    @lockfile.locked?\n  end\n\n  def mine?\n    Process.pid == lock_pid\n  end\n\n  def lock\n    return mine? if locked?\n\n    @lockfile.lock(Process.pid)\n  end\n\n  def unlock\n    if mine?\n      @lockfile.unlock\n    else\n      false\n    end\n  end\n\n  def lock_pid\n    pid = @lockfile.lock_data\n    begin\n      Integer(pid)\n    rescue ArgumentError, TypeError\n      nil\n    end\n  end\n\n  def file_path\n    @lockfile.file_path\n  end\n\n  private\n\n  def ps_argument_for_current_kernel\n    case Puppet.runtime[:facter].value(:kernel)\n    when \"Linux\"\n      \"-eq\"\n    when \"AIX\"\n      \"-T\"\n    else\n      \"-p\"\n    end\n  end\n\n  def clear_if_stale\n    pid = lock_pid\n    return @lockfile.unlock if pid.nil?\n    return if Process.pid == pid\n\n    errors = [Errno::ESRCH]\n    # Win32::Process now throws SystemCallError. Since this could be\n    # defined anywhere, only add when on Windows.\n    errors << SystemCallError if Puppet::Util::Platform.windows?\n\n    begin\n      Process.kill(0, pid)\n    rescue *errors\n      return @lockfile.unlock\n    end\n\n    # Ensure the process associated with this pid is our process. If\n    # not, we can unlock the lockfile. CLI arguments used for identifying\n    # on POSIX depend on the os and sometimes even version.\n    if Puppet.features.posix?\n      ps_argument = ps_argument_for_current_kernel\n\n      # Check, obtain and use the right ps argument\n      begin\n        procname = Puppet::Util::Execution.execute([\"ps\", ps_argument, pid, \"-o\", \"comm=\"]).strip\n      rescue Puppet::ExecutionFailure\n        ps_argument = \"-p\"\n        procname = Puppet::Util::Execution.execute([\"ps\", ps_argument, pid, \"-o\", \"comm=\"]).strip\n      end\n\n      args = Puppet::Util::Execution.execute([\"ps\", ps_argument, pid, \"-o\", \"args=\"]).strip\n      @lockfile.unlock unless procname =~ /ruby/ && args =~ /puppet/ || procname =~ /puppet(-.*)?$/\n    elsif Puppet.features.microsoft_windows?\n      # On Windows, we're checking if the filesystem path name of the running\n      # process is our vendored ruby:\n      begin\n        exe_path = Puppet::Util::Windows::Process.get_process_image_name_by_pid(pid)\n        @lockfile.unlock unless exe_path =~ /\\\\bin\\\\ruby.exe$/\n      rescue Puppet::Util::Windows::Error => e\n        Puppet.debug(\"Failed to read pidfile #{file_path}: #{e.message}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/platform.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  module Util\n    module Platform\n      FIPS_STATUS_FILE = \"/proc/sys/crypto/fips_enabled\"\n      WINDOWS_FIPS_REGISTRY_KEY = 'System\\\\CurrentControlSet\\\\Control\\\\Lsa\\\\FipsAlgorithmPolicy'\n\n      def windows?\n        # Ruby only sets File::ALT_SEPARATOR on Windows and the Ruby standard\n        # library uses that to test what platform it's on. In some places we\n        # would use Puppet.features.microsoft_windows?, but this method can be\n        # used to determine the behavior of the underlying system without\n        # requiring features to be initialized and without side effect.\n        !!File::ALT_SEPARATOR\n      end\n      module_function :windows?\n\n      def solaris?\n        RUBY_PLATFORM.include?('solaris')\n      end\n      module_function :solaris?\n\n      def default_paths\n        return [] if windows?\n\n        %w[/usr/sbin /sbin]\n      end\n      module_function :default_paths\n\n      @fips_enabled = if windows?\n                        require 'win32/registry'\n\n                        begin\n                          Win32::Registry::HKEY_LOCAL_MACHINE.open(WINDOWS_FIPS_REGISTRY_KEY) do |reg|\n                            reg.values.first == 1\n                          end\n                        rescue Win32::Registry::Error\n                          false\n                        end\n                      else\n                        File.exist?(FIPS_STATUS_FILE) &&\n                          File.read(FIPS_STATUS_FILE, 1) == '1'\n                      end\n\n      def fips_enabled?\n        @fips_enabled\n      end\n      module_function :fips_enabled?\n\n      def self.jruby?\n        RUBY_PLATFORM == 'java'\n      end\n\n      def jruby_fips?\n        @@jruby_fips ||= if RUBY_PLATFORM == 'java'\n                           require 'java'\n\n                           begin\n                             require 'openssl'\n                             false\n                           rescue LoadError, NameError\n                             true\n                           end\n                         else\n                           false\n                         end\n      end\n      module_function :jruby_fips?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/plist.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'cfpropertylist' if Puppet.features.cfpropertylist?\nrequire_relative '../../puppet/util/execution'\n\nmodule Puppet::Util::Plist\n  class FormatError < RuntimeError; end\n\n  # So I don't have to prepend every method name with 'self.' Most of the\n  # methods are going to be Provider methods (as opposed to methods of the\n  # INSTANCE of the provider).\n  class << self\n    # Defines the magic number for binary plists\n    #\n    # @api private\n    def binary_plist_magic_number\n      \"bplist00\"\n    end\n\n    # Defines a default doctype string that should be at the top of most plist\n    # files. Useful if we need to modify an invalid doctype string in memory.\n    # In version 10.9 and lower of OS X the plist at\n    # /System/Library/LaunchDaemons/org.ntp.ntpd.plist had an invalid doctype\n    # string. This corrects for that.\n    def plist_xml_doctype\n      '<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">'\n    end\n\n    # Read a plist file, whether its format is XML or in Apple's \"binary1\"\n    # format, using the CFPropertyList gem.\n    def read_plist_file(file_path)\n      # We can't really read the file until we know the source encoding in\n      # Ruby 1.9.x, so we use the magic number to detect it.\n      # NOTE: We used IO.read originally to be Ruby 1.8.x compatible.\n      if read_file_with_offset(file_path, binary_plist_magic_number.length) == binary_plist_magic_number\n        plist_obj = new_cfpropertylist(:file => file_path)\n        return convert_cfpropertylist_to_native_types(plist_obj)\n      else\n        plist_data = open_file_with_args(file_path, \"r:UTF-8\")\n        plist = parse_plist(plist_data, file_path)\n        return plist if plist\n\n        Puppet.debug \"Plist #{file_path} ill-formatted, converting with plutil\"\n        begin\n          plist = Puppet::Util::Execution.execute(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', file_path],\n                                                  { :failonfail => true, :combine => true })\n          return parse_plist(plist)\n        rescue Puppet::ExecutionFailure => detail\n          message = _(\"Cannot read file %{file_path}; Puppet is skipping it.\") % { file_path: file_path }\n          message += '\\n' + _(\"Details: %{detail}\") % { detail: detail }\n          Puppet.warning(message)\n        end\n      end\n      nil\n    end\n\n    # Read plist text using the CFPropertyList gem.\n    def parse_plist(plist_data, file_path = '')\n      bad_xml_doctype = %r{^.*<!DOCTYPE plist PUBLIC -//Apple Computer.*$}\n      # Depending on where parse_plist is called from, plist_data can be either XML or binary.\n      # If we get XML, make sure ruby knows it's UTF-8 so we avoid invalid byte sequence errors.\n      if plist_data.include?('encoding=\"UTF-8\"') && plist_data.encoding != Encoding::UTF_8\n        plist_data.force_encoding(Encoding::UTF_8)\n      end\n\n      begin\n        if plist_data =~ bad_xml_doctype\n          plist_data.gsub!(bad_xml_doctype, plist_xml_doctype)\n          Puppet.debug(\"Had to fix plist with incorrect DOCTYPE declaration: #{file_path}\")\n        end\n      rescue ArgumentError => e\n        Puppet.debug \"Failed with #{e.class} on #{file_path}: #{e.inspect}\"\n        return nil\n      end\n\n      begin\n        plist_obj = new_cfpropertylist(:data => plist_data)\n      # CFPropertyList library will raise NoMethodError for invalid data\n      rescue CFFormatError, NoMethodError => e\n        Puppet.debug \"Failed with #{e.class} on #{file_path}: #{e.inspect}\"\n        return nil\n      end\n      convert_cfpropertylist_to_native_types(plist_obj)\n    end\n\n    # Helper method to assist in reading a file. It's its own method for\n    # stubbing purposes\n    #\n    # @api private\n    #\n    # @param args [String] Extra file operation mode information to use\n    #   (defaults to read-only mode 'r')\n    #   This is the standard mechanism Ruby uses in the IO class, and therefore\n    #   encoding may be explicitly like fmode : encoding or fmode : \"BOM|UTF-*\"\n    #   for example, a:ASCII or w+:UTF-8\n    def open_file_with_args(file, args)\n      File.open(file, args).read\n    end\n\n    # Helper method to assist in generating a new CFPropertyList Plist. It's\n    # its own method for stubbing purposes\n    #\n    # @api private\n    def new_cfpropertylist(plist_opts)\n      CFPropertyList::List.new(plist_opts)\n    end\n\n    # Helper method to assist in converting a native CFPropertyList object to a\n    # native Ruby object (hash). It's its own method for stubbing purposes\n    #\n    # @api private\n    def convert_cfpropertylist_to_native_types(plist_obj)\n      CFPropertyList.native_types(plist_obj.value)\n    end\n\n    # Helper method to convert a string into a CFProperty::Blob, which is\n    # needed to properly handle binary strings\n    #\n    # @api private\n    def string_to_blob(str)\n      CFPropertyList::Blob.new(str)\n    end\n\n    # Helper method to assist in reading a file with an offset value. It's its\n    # own method for stubbing purposes\n    #\n    # @api private\n    def read_file_with_offset(file_path, offset)\n      IO.read(file_path, offset)\n    end\n\n    def to_format(format)\n      case format.to_sym\n      when :xml\n        CFPropertyList::List::FORMAT_XML\n      when :binary\n        CFPropertyList::List::FORMAT_BINARY\n      when :plain\n        CFPropertyList::List::FORMAT_PLAIN\n      else\n        raise FormatError, \"Unknown plist format #{format}\"\n      end\n    end\n\n    # This method will write a plist file using a specified format (or XML\n    # by default)\n    def write_plist_file(plist, file_path, format = :xml)\n      plist_to_save       = CFPropertyList::List.new\n      plist_to_save.value = CFPropertyList.guess(plist)\n      plist_to_save.save(file_path, to_format(format), :formatted => true)\n    rescue IOError => e\n      Puppet.err(_(\"Unable to write the file %{file_path}. %{error}\") % { file_path: file_path, error: e.inspect })\n    end\n\n    def dump_plist(plist_data, format = :xml)\n      plist_to_save       = CFPropertyList::List.new\n      plist_to_save.value = CFPropertyList.guess(plist_data)\n      plist_to_save.to_str(to_format(format), :formatted => true)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/posix.rb",
    "content": "# frozen_string_literal: true\n\n# Utility methods for interacting with POSIX objects; mostly user and group\nmodule Puppet::Util::POSIX\n  # This is a list of environment variables that we will set when we want to override the POSIX locale\n  LOCALE_ENV_VARS = %w[LANG LC_ALL LC_MESSAGES LANGUAGE\n                       LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME]\n\n  # This is a list of user-related environment variables that we will unset when we want to provide a pristine\n  # environment for \"exec\" runs\n  USER_ENV_VARS = %w[HOME USER LOGNAME]\n\n  class << self\n    # Returns an array of all the groups that the user's a member of.\n    def groups_of(user)\n      begin\n        require_relative '../../puppet/ffi/posix'\n        groups = get_groups_list(user)\n      rescue StandardError, LoadError => e\n        Puppet.debug(\"Falling back to Puppet::Etc.group: #{e.message}\")\n\n        groups = []\n        Puppet::Etc.group do |group|\n          groups << group.name if group.mem.include?(user)\n        end\n      end\n\n      uniq_groups = groups.uniq\n      if uniq_groups != groups\n        Puppet.debug(_('Removing any duplicate group entries'))\n      end\n\n      uniq_groups\n    end\n\n    private\n\n    def get_groups_list(user)\n      raise LoadError, \"The 'getgrouplist' method is not available\" unless Puppet::FFI::POSIX::Functions.respond_to?(:getgrouplist)\n\n      user_gid = Puppet::Etc.getpwnam(user).gid\n      ngroups = Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS\n\n      loop do\n        FFI::MemoryPointer.new(:int) do |ngroups_ptr|\n          FFI::MemoryPointer.new(:uint, ngroups) do |groups_ptr|\n            old_ngroups = ngroups\n            ngroups_ptr.write_int(ngroups)\n\n            if Puppet::FFI::POSIX::Functions.getgrouplist(user, user_gid, groups_ptr, ngroups_ptr) != -1\n              groups_gids = groups_ptr.get_array_of_uint(0, ngroups_ptr.read_int)\n\n              result = []\n              groups_gids.each do |group_gid|\n                group_info = Puppet::Etc.getgrgid(group_gid)\n                result |= [group_info.name] if group_info.mem.include?(user)\n              end\n              return result\n            end\n\n            ngroups = ngroups_ptr.read_int\n            if ngroups <= old_ngroups\n              ngroups *= 2\n            end\n          end\n        end\n      end\n    end\n  end\n\n  # Retrieve a field from a POSIX Etc object.  The id can be either an integer\n  # or a name.  This only works for users and groups.  It's also broken on\n  # some platforms, unfortunately, which is why we fall back to the other\n  # method search_posix_field in the gid and uid methods if a sanity check\n  # fails\n  def get_posix_field(space, field, id)\n    raise Puppet::DevError, _(\"Did not get id from caller\") unless id\n\n    if id.is_a?(Integer)\n      if id > Puppet[:maximum_uid].to_i\n        Puppet.err _(\"Tried to get %{field} field for silly id %{id}\") % { field: field, id: id }\n        return nil\n      end\n      method = methodbyid(space)\n    else\n      method = methodbyname(space)\n    end\n\n    begin\n      Etc.send(method, id).send(field)\n    rescue NoMethodError, ArgumentError\n      # ignore it; we couldn't find the object\n      nil\n    end\n  end\n\n  # A degenerate method of retrieving name/id mappings.  The job of this method is\n  # to retrieve all objects of a certain type, search for a specific entry\n  # and then return a given field from that entry.\n  def search_posix_field(type, field, id)\n    idmethod = idfield(type)\n    integer = false\n    if id.is_a?(Integer)\n      integer = true\n      if id > Puppet[:maximum_uid].to_i\n        Puppet.err _(\"Tried to get %{field} field for silly id %{id}\") % { field: field, id: id }\n        return nil\n      end\n    end\n\n    Etc.send(type) do |object|\n      if integer and object.send(idmethod) == id\n        return object.send(field)\n      elsif object.name == id\n        return object.send(field)\n      end\n    end\n\n    # Apparently the group/passwd methods need to get reset; if we skip\n    # this call, then new users aren't found.\n    case type\n    when :passwd; Etc.send(:endpwent)\n    when :group; Etc.send(:endgrent)\n    end\n    nil\n  end\n\n  # Determine what the field name is for users and groups.\n  def idfield(space)\n    case space.intern\n    when :gr, :group; :gid\n    when :pw, :user, :passwd; :uid\n    else\n      raise ArgumentError, _(\"Can only handle users and groups\")\n    end\n  end\n\n  # Determine what the method is to get users and groups by id\n  def methodbyid(space)\n    case space.intern\n    when :gr, :group; :getgrgid\n    when :pw, :user, :passwd; :getpwuid\n    else\n      raise ArgumentError, _(\"Can only handle users and groups\")\n    end\n  end\n\n  # Determine what the method is to get users and groups by name\n  def methodbyname(space)\n    case space.intern\n    when :gr, :group; :getgrnam\n    when :pw, :user, :passwd; :getpwnam\n    else\n      raise ArgumentError, _(\"Can only handle users and groups\")\n    end\n  end\n\n  # Get the GID\n  def gid(group)\n    get_posix_value(:group, :gid, group)\n  end\n\n  # Get the UID\n  def uid(user)\n    get_posix_value(:passwd, :uid, user)\n  end\n\n  private\n\n  # Get the specified id_field of a given field (user or group),\n  # whether an ID name is provided\n  def get_posix_value(location, id_field, field)\n    begin\n      field = Integer(field)\n    rescue ArgumentError\n      # pass\n    end\n    if field.is_a?(Integer)\n      name = get_posix_field(location, :name, field)\n      return nil unless name\n\n      id = get_posix_field(location, id_field, name)\n      check_value = id\n    else\n      id = get_posix_field(location, id_field, field)\n      return nil unless id\n\n      name = get_posix_field(location, :name, id)\n      check_value = name\n    end\n\n    if check_value != field\n      check_value_id = get_posix_field(location, id_field, check_value) if check_value\n\n      if id == check_value_id\n        Puppet.debug(\"Multiple entries found for resource: '#{location}' with #{id_field}: #{id}\")\n        id\n      else\n        Puppet.debug(\"The value retrieved: '#{check_value}' is different than the required state: '#{field}', searching in all entries\")\n        search_posix_field(location, id_field, field)\n      end\n    else\n      id\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/profiler/aggregate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/profiler'\nrequire_relative '../../../puppet/util/profiler/wall_clock'\n\nclass Puppet::Util::Profiler::Aggregate < Puppet::Util::Profiler::WallClock\n  def initialize(logger, identifier)\n    super(logger, identifier)\n    @metrics_hash = Metric.new\n  end\n\n  def shutdown\n    super\n    @logger.call(\"AGGREGATE PROFILING RESULTS:\")\n    @logger.call(\"----------------------------\")\n    print_metrics(@metrics_hash, \"\")\n    @logger.call(\"----------------------------\")\n  end\n\n  def do_finish(context, description, metric_id)\n    result = super(context, description, metric_id)\n    update_metric(@metrics_hash, metric_id, result[:time])\n    result\n  end\n\n  def update_metric(metrics_hash, metric_id, time)\n    first, *rest = *metric_id\n    if first\n      m = metrics_hash[first]\n      m.increment\n      m.add_time(time)\n      if rest.count > 0\n        update_metric(m, rest, time)\n      end\n    end\n  end\n\n  def values\n    @metrics_hash\n  end\n\n  def print_metrics(metrics_hash, prefix)\n    metrics_hash.sort_by { |_k, v| v.time }.reverse_each do |k, v|\n      @logger.call(\"#{prefix}#{k}: #{v.time} s (#{v.count} calls)\")\n      print_metrics(metrics_hash[k], \"#{prefix}#{k} -> \")\n    end\n  end\n\n  class Metric < Hash\n    def initialize\n      super\n      @count = 0\n      @time = 0\n    end\n    attr_reader :count, :time\n\n    def [](key)\n      unless has_key?(key)\n        self[key] = Metric.new\n      end\n      super(key)\n    end\n\n    def increment\n      @count += 1\n    end\n\n    def add_time(time)\n      @time += time\n    end\n  end\n\n  class Timer\n    def initialize\n      @start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)\n    end\n\n    def stop\n      Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) - @start\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/profiler/around_profiler.rb",
    "content": "# frozen_string_literal: true\n\n# A Profiler that can be used to wrap around blocks of code. It is configured\n# with other profilers and controls them to start before the block is executed\n# and finish after the block is executed.\n#\n# @api private\nclass Puppet::Util::Profiler::AroundProfiler\n  def initialize\n    @profilers = []\n  end\n\n  # Reset the profiling system to the original state\n  #\n  # @api private\n  def clear\n    @profilers = []\n  end\n\n  # Retrieve the current list of profilers\n  #\n  # @api private\n  def current\n    @profilers\n  end\n\n  # @param profiler [#profile] A profiler for the current thread\n  # @api private\n  def add_profiler(profiler)\n    @profilers << profiler\n    profiler\n  end\n\n  # @param profiler [#profile] A profiler to remove from the current thread\n  # @api private\n  def remove_profiler(profiler)\n    @profilers.delete(profiler)\n  end\n\n  # Profile a block of code and log the time it took to execute.\n  #\n  # This outputs logs entries to the Puppet masters logging destination\n  # providing the time it took, a message describing the profiled code\n  # and a leaf location marking where the profile method was called\n  # in the profiled hierarchy.\n  #\n  # @param message [String] A description of the profiled event\n  # @param metric_id [Array] A list of strings making up the ID of a metric to profile\n  # @param block [Block] The segment of code to profile\n  # @api private\n  def profile(message, metric_id)\n    retval = nil\n    contexts = {}\n    @profilers.each do |profiler|\n      contexts[profiler] = profiler.start(message, metric_id)\n    end\n\n    begin\n      retval = yield\n    ensure\n      @profilers.each do |profiler|\n        profiler.finish(contexts[profiler], message, metric_id)\n      end\n    end\n\n    retval\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/profiler/logging.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Util::Profiler::Logging\n  def initialize(logger, identifier)\n    @logger = logger\n    @identifier = identifier\n    @sequence = Sequence.new\n  end\n\n  def start(description, metric_id)\n    @sequence.next\n    @sequence.down\n    do_start(description, metric_id)\n  end\n\n  def finish(context, description, metric_id)\n    profile_explanation = do_finish(context, description, metric_id)[:msg]\n    @sequence.up\n    @logger.call(\"PROFILE [#{@identifier}] #{@sequence} #{description}: #{profile_explanation}\")\n  end\n\n  def shutdown\n    # nothing to do\n  end\n\n  class Sequence\n    INITIAL = 0\n    SEPARATOR = '.'\n\n    def initialize\n      @elements = [INITIAL]\n    end\n\n    def next\n      @elements[-1] += 1\n    end\n\n    def down\n      @elements << INITIAL\n    end\n\n    def up\n      @elements.pop\n    end\n\n    def to_s\n      @elements.join(SEPARATOR)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/profiler/object_counts.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/profiler/logging'\n\nclass Puppet::Util::Profiler::ObjectCounts < Puppet::Util::Profiler::Logging\n  def start\n    ObjectSpace.count_objects\n  end\n\n  def finish(before)\n    after = ObjectSpace.count_objects\n\n    diff = before.collect do |type, count|\n      [type, after[type] - count]\n    end\n\n    diff.sort.collect { |pair| pair.join(': ') }.join(', ')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/profiler/wall_clock.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/profiler/logging'\n\n# A profiler implementation that measures the number of seconds a segment of\n# code takes to execute and provides a callback with a string representation of\n# the profiling information.\n#\n# @api private\nclass Puppet::Util::Profiler::WallClock < Puppet::Util::Profiler::Logging\n  def do_start(description, metric_id)\n    Timer.new\n  end\n\n  def do_finish(context, description, metric_id)\n    { :time => context.stop,\n      :msg => _(\"took %{context} seconds\") % { context: context } }\n  end\n\n  class Timer\n    FOUR_DECIMAL_DIGITS = '%0.4f'\n\n    def initialize\n      @start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)\n    end\n\n    def stop\n      @time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) - @start\n      @time\n    end\n\n    def to_s\n      format(FOUR_DECIMAL_DIGITS, @time)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/profiler.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'benchmark'\n\n# A simple profiling callback system.\n#\n# @api public\nmodule Puppet::Util::Profiler\n  require_relative 'profiler/wall_clock'\n  require_relative 'profiler/object_counts'\n  require_relative 'profiler/around_profiler'\n\n  @profiler = Puppet::Util::Profiler::AroundProfiler.new\n\n  # Reset the profiling system to the original state\n  #\n  # @api private\n  def self.clear\n    @profiler.clear\n  end\n\n  # Retrieve the current list of profilers\n  #\n  # @api private\n  def self.current\n    @profiler.current\n  end\n\n  # @param profiler [#profile] A profiler for the current thread\n  # @api private\n  def self.add_profiler(profiler)\n    @profiler.add_profiler(profiler)\n  end\n\n  # @param profiler [#profile] A profiler to remove from the current thread\n  # @api private\n  def self.remove_profiler(profiler)\n    @profiler.remove_profiler(profiler)\n  end\n\n  # Profile a block of code and log the time it took to execute.\n  #\n  # This outputs logs entries to the Puppet masters logging destination\n  # providing the time it took, a message describing the profiled code\n  # and a leaf location marking where the profile method was called\n  # in the profiled hierarchy.\n  #\n  # @param message [String] A description of the profiled event\n  # @param metric_id [Array] A list of strings making up the ID of a metric to profile\n  # @param block [Block] The segment of code to profile\n  # @api public\n  def self.profile(message, metric_id, &block)\n    @profiler.profile(message, metric_id, &block)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/provider_features.rb",
    "content": "# frozen_string_literal: true\n\n# Provides feature definitions.\nrequire_relative '../../puppet/util/docs'\nrequire_relative '../../puppet/util'\n# This module models provider features and handles checking whether the features\n# are present.\n# @todo Unclear what is api and what is private in this module.\n#\nmodule Puppet::Util::ProviderFeatures\n  include Puppet::Util::Docs\n  # This class models provider features and handles checking whether the features\n  # are present.\n  # @todo Unclear what is api and what is private in this class\n  class ProviderFeature\n    include Puppet::Util\n    include Puppet::Util::Docs\n    attr_accessor :name, :docs, :methods\n\n    # Are all of the requirements met?\n    # Requirements are checked by checking if feature predicate methods have been generated - see {#methods_available?}.\n    # @param obj [Object, Class] the object or class to check if requirements are met\n    # @return [Boolean] whether all requirements for this feature are met or not.\n    def available?(obj)\n      if methods\n        !!methods_available?(obj)\n      else\n        # In this case, the provider has to declare support for this\n        # feature, and that's been checked before we ever get to the\n        # method checks.\n        false\n      end\n    end\n\n    def initialize(name, docs, methods: nil)\n      self.name = name.intern\n      self.docs = docs\n      @methods = methods\n    end\n\n    private\n\n    # Checks whether all feature predicate methods are available.\n    # @param obj [Object, Class] the object or class to check if feature predicates are available or not.\n    # @return [Boolean] Returns whether all of the required methods are available or not in the given object.\n    def methods_available?(obj)\n      methods.each do |m|\n        if obj.is_a?(Class)\n          return false unless obj.public_method_defined?(m)\n        else\n          return false unless obj.respond_to?(m)\n        end\n      end\n      true\n    end\n  end\n\n  # Defines one feature.\n  # At a minimum, a feature requires a name\n  # and docs, and at this point they should also specify a list of methods\n  # required to determine if the feature is present.\n  # @todo How methods that determine if the feature is present are specified.\n  def feature(name, docs, hash = {})\n    @features ||= {}\n    raise Puppet::DevError, _(\"Feature %{name} is already defined\") % { name: name } if @features.include?(name)\n\n    begin\n      obj = ProviderFeature.new(name, docs, **hash)\n      @features[obj.name] = obj\n    rescue ArgumentError => detail\n      error = ArgumentError.new(\n        _(\"Could not create feature %{name}: %{detail}\") % { name: name, detail: detail }\n      )\n      error.set_backtrace(detail.backtrace)\n      raise error\n    end\n  end\n\n  # @return [String] Returns a string with documentation covering all features.\n  def featuredocs\n    str = ''.dup\n    @features ||= {}\n    return nil if @features.empty?\n\n    names = @features.keys.sort_by(&:to_s)\n    names.each do |name|\n      doc = @features[name].docs.gsub(/\\n\\s+/, \" \")\n      str << \"- *#{name}*: #{doc}\\n\"\n    end\n\n    if providers.length > 0\n      headers = [\"Provider\", names].flatten\n      data = {}\n      providers.each do |provname|\n        data[provname] = []\n        prov = provider(provname)\n        names.each do |name|\n          if prov.feature?(name)\n            data[provname] << \"*X*\"\n          else\n            data[provname] << \"\"\n          end\n        end\n      end\n      str << doctable(headers, data)\n    end\n    str\n  end\n\n  # @return [Array<String>] Returns a list of features.\n  def features\n    @features ||= {}\n    @features.keys\n  end\n\n  # Generates a module that sets up the boolean predicate methods to test for given features.\n  #\n  def feature_module\n    unless defined?(@feature_module)\n      @features ||= {}\n      @feature_module = ::Module.new\n      const_set(\"FeatureModule\", @feature_module)\n      features = @features\n      # Create a feature? method that can be passed a feature name and\n      # determine if the feature is present.\n      @feature_module.send(:define_method, :feature?) do |name|\n        method = name.to_s + \"?\"\n        return !!(respond_to?(method) and send(method))\n      end\n\n      # Create a method that will list all functional features.\n      @feature_module.send(:define_method, :features) do\n        return false unless defined?(features)\n\n        features.keys.find_all { |n| feature?(n) }.sort_by(&:to_s)\n      end\n\n      # Create a method that will determine if a provided list of\n      # features are satisfied by the curred provider.\n      @feature_module.send(:define_method, :satisfies?) do |*needed|\n        ret = true\n        needed.flatten.each do |feature|\n          unless feature?(feature)\n            ret = false\n            break\n          end\n        end\n        ret\n      end\n\n      # Create a boolean method for each feature so you can test them\n      # individually as you might need.\n      @features.each do |name, feature|\n        method = name.to_s + \"?\"\n        @feature_module.send(:define_method, method) do\n          (is_a?(Class) ? declared_feature?(name) : self.class.declared_feature?(name)) or feature.available?(self)\n        end\n      end\n\n      # Allow the provider to declare that it has a given feature.\n      @feature_module.send(:define_method, :has_features) do |*names|\n        @declared_features ||= []\n        names.each do |name|\n          @declared_features << name.intern\n        end\n      end\n      # Aaah, grammatical correctness\n      @feature_module.send(:alias_method, :has_feature, :has_features)\n    end\n    @feature_module\n  end\n\n  # @return [ProviderFeature] Returns a provider feature instance by name.\n  # @param name [String] the name of the feature to return\n  # @note Should only be used for testing.\n  # @api private\n  #\n  def provider_feature(name)\n    return nil unless defined?(@features)\n\n    @features[name]\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/psych_support.rb",
    "content": "# frozen_string_literal: true\n\n# This module should be included when a class can be serialized to yaml and\n# needs to handle the deserialization from Psych with more control. Psych normally\n# pokes values directly into an instance using `instance_variable_set` which completely\n# bypasses encapsulation.\n#\n# The class that includes this module must implement an instance method `initialize_from_hash`\n# that is given a hash with attribute to value mappings.\n#\nmodule Puppet::Util::PsychSupport\n  # This method is called from the Psych Yaml deserializer when it encounters a tag\n  # in the form !ruby/object:<class name>.\n  #\n  def init_with(psych_coder)\n    # The PsychCoder has a hashmap of instance variable name (sans the @ symbol) to values\n    # to set, and can thus directly be fed to initialize_from_hash.\n    #\n    initialize_from_hash(psych_coder.map)\n  end\n\n  # This method is called from the Psych Yaml serializer\n  # The serializer will call this method to create a hash that will be serialized to YAML.\n  # Instead of using the object itself during the mapping process we use what is\n  # returned by calling `to_data_hash` on the object itself since some of the\n  # objects we manage have asymmetrical serialization and deserialization.\n  #\n  def encode_with(psych_encoder)\n    tag = Psych.dump_tags[self.class] || \"!ruby/object:#{self.class.name}\"\n    psych_encoder.represent_map(tag, to_data_hash)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rdoc/code_objects.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rdoc/code_objects'\n\nmodule RDoc\n  # This modules contains various class that are used to hold information\n  # about the various Puppet language structures we found while parsing.\n  #\n  # Those will be mapped to their html counterparts which are defined in\n  # PuppetGenerator.\n\n  # PuppetTopLevel is a top level (usually a .pp/.rb file)\n  module PuppetTopLevel\n    attr_accessor :module_name, :global\n  end\n\n  # Add top level comments to a class or module\n  # @api private\n  module AddClassModuleComment\n    def add_comment(comment, location = nil)\n      super\n    end\n  end\n\n  # PuppetModule holds a Puppet Module\n  # This is mapped to an HTMLPuppetModule\n  # it leverage the RDoc (ruby) module infrastructure\n  class PuppetModule < NormalModule\n    include AddClassModuleComment\n\n    attr_accessor :facts, :plugins\n\n    def initialize(name, superclass = nil)\n      @facts = []\n      @plugins = []\n      @nodes = {}\n      super(name, superclass)\n    end\n\n    def add_plugin(plugin)\n      name = plugin.name\n      type = plugin.type\n      meth = AnyMethod.new(\"*args\", name)\n      meth.params = \"(*args)\"\n      meth.visibility = :public\n      meth.document_self = true\n      meth.singleton = false\n      meth.comment = plugin.comment\n      case type\n      when 'function'\n        @function_container ||= add_module(NormalModule, \"__functions__\")\n        @function_container.add_method(meth)\n      when 'type'\n        @type_container ||= add_module(NormalModule, \"__types__\")\n        @type_container.add_method(meth)\n      end\n    end\n\n    def add_fact(fact)\n      @fact_container ||= add_module(NormalModule, \"__facts__\")\n      confine_str = fact.confine.empty? ? '' : fact.confine.to_s\n      const = Constant.new(fact.name, confine_str, fact.comment)\n      @fact_container.add_constant(const)\n    end\n\n    # Adds a module called __nodes__ and adds nodes to it as classes\n    #\n    def add_node(name, superclass)\n      cls = @nodes[name]\n      if cls\n        return cls\n      end\n\n      @node_container ||= add_module(NormalModule, \"__nodes__\")\n      cls = @node_container.add_class(PuppetNode, name, superclass)\n      @nodes[name] = cls unless @done_documenting\n      cls\n    end\n\n    def each_fact\n      @facts.each { |c| yield c }\n    end\n\n    def each_plugin\n      @plugins.each { |c| yield c }\n    end\n\n    def each_node\n      @nodes.each { |c| yield c }\n    end\n\n    def nodes\n      @nodes.values\n    end\n  end\n\n  # PuppetClass holds a puppet class\n  # It is mapped to a HTMLPuppetClass for display\n  # It leverages RDoc (ruby) Class\n  class PuppetClass < ClassModule\n    include AddClassModuleComment\n\n    attr_accessor :resource_list, :requires, :childs, :realizes\n\n    def initialize(name, superclass)\n      super(name, superclass)\n      @resource_list = []\n      @requires = []\n      @realizes = []\n      @childs = []\n    end\n\n    def aref_prefix\n      'puppet_class'\n    end\n\n    def add_resource(resource)\n      add_to(@resource_list, resource)\n    end\n\n    def is_module?\n      false\n    end\n\n    def superclass=(superclass)\n      @superclass = superclass\n    end\n\n    # we're (ab)using the RDoc require system here.\n    # we're adding a required Puppet class, overriding\n    # the RDoc add_require method which sees ruby required files.\n    def add_require(required)\n      add_to(@requires, required)\n    end\n\n    def add_realize(realized)\n      add_to(@realizes, realized)\n    end\n\n    def add_child(child)\n      @childs << child\n    end\n\n    # Look up the given symbol. RDoc only looks for class1::class2.method\n    # or class1::class2#method. Since our definitions are mapped to RDoc methods\n    # but are written class1::class2::define we need to perform the lookup by\n    # ourselves.\n    def find_symbol(symbol, method = nil)\n      result = super(symbol)\n      if !result and symbol =~ /::/\n        modules = symbol.split(/::/)\n        unless modules.empty?\n          module_name = modules.shift\n          result = find_module_named(module_name)\n          if result\n            last_name = \"\"\n            previous = nil\n            modules.each do |mod|\n              previous = result\n              last_name = mod\n              result = result.find_module_named(mod)\n              break unless result\n            end\n            unless result\n              result = previous\n              method = last_name\n            end\n          end\n        end\n        if result && method\n          unless result.respond_to?(:find_local_symbol)\n            p result.name\n            p method\n            fail\n          end\n          result = result.find_local_symbol(method)\n        end\n      end\n      result\n    end\n  end\n\n  # PuppetNode holds a puppet node\n  # It is mapped to a HTMLPuppetNode for display\n  # A node is just a variation of a class\n  class PuppetNode < PuppetClass\n    include AddClassModuleComment\n\n    def is_module?\n      false\n    end\n  end\n\n  # Plugin holds a native puppet plugin (function,type...)\n  # It is mapped to a HTMLPuppetPlugin for display\n  class Plugin < Context\n    attr_accessor :name, :type\n\n    def initialize(name, type)\n      super()\n      @name = name\n      @type = type\n      @comment = \"\"\n    end\n\n    def <=>(other)\n      @name <=> other.name\n    end\n\n    def full_name\n      @name\n    end\n\n    def http_url(prefix)\n      path = full_name.split(\"::\")\n      File.join(prefix, *path) + \".html\"\n    end\n\n    def is_fact?\n      false\n    end\n\n    def to_s\n      res = self.class.name + \": #{@name} (#{@type})\\n\"\n      res << @comment.to_s\n      res\n    end\n  end\n\n  # Fact holds a custom fact\n  # It is mapped to a HTMLPuppetPlugin for display\n  class Fact < Context\n    attr_accessor :name, :confine\n\n    def initialize(name, confine)\n      super()\n      @name = name\n      @confine = confine\n      @comment = \"\"\n    end\n\n    def <=>(other)\n      @name <=> other.name\n    end\n\n    def is_fact?\n      true\n    end\n\n    def full_name\n      @name\n    end\n\n    def to_s\n      res = self.class.name + \": #{@name}\\n\"\n      res << @comment.to_s\n      res\n    end\n  end\n\n  # PuppetResource holds a puppet resource\n  # It is mapped to a HTMLPuppetResource for display\n  # A resource is defined by its \"normal\" form Type[title]\n  class PuppetResource < CodeObject\n    attr_accessor :type, :title, :params\n\n    def initialize(type, title, comment, params)\n      super()\n      @type = type\n      @title = title\n      @comment = comment\n      @params = params\n    end\n\n    def <=>(other)\n      full_name <=> other.full_name\n    end\n\n    def full_name\n      @type + \"[#{@title}]\"\n    end\n\n    def name\n      full_name\n    end\n\n    def to_s\n      res = @type + \"[#{@title}]\\n\"\n      res << @comment.to_s\n      res\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rdoc/generators/puppet_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rdoc/generators/html_generator'\nrequire_relative '../../../../puppet/util/rdoc/code_objects'\nrequire 'digest/md5'\n\nmodule Generators\n  # This module holds all the classes needed to generate the HTML documentation\n  # of a bunch of puppet manifests.\n  #\n  # It works by traversing all the code objects defined by the Puppet RDoc::Parser\n  # and produces HTML counterparts objects that in turns are used by RDoc template engine\n  # to produce the final HTML.\n  #\n  # It is also responsible of creating the whole directory hierarchy, and various index\n  # files.\n  #\n  # It is to be noted that the whole system is built on top of ruby RDoc. As such there\n  # is an implicit mapping of puppet entities to ruby entitites:\n  #\n  #         Puppet    =>    Ruby\n  #         ------------------------\n  #         Module          Module\n  #         Class           Class\n  #         Definition      Method\n  #         Resource\n  #         Node\n  #         Plugin\n  #         Fact\n\n  MODULE_DIR = \"modules\"\n  NODE_DIR = \"nodes\"\n  PLUGIN_DIR = \"plugins\"\n\n  # We're monkey patching RDoc markup to allow\n  # lowercase class1::class2::class3 crossref hyperlinking\n  module MarkUp\n    alias :old_markup :markup\n\n    def new_markup(str, remove_para = false)\n      first = @markup.nil?\n      res = old_markup(str, remove_para)\n      if first and !@markup.nil?\n        @markup.add_special(/\\b([a-z]\\w+(::\\w+)*)/, :CROSSREF)\n        # we need to call it again, since we added a rule\n        res = old_markup(str, remove_para)\n      end\n      res\n    end\n    alias :markup :new_markup\n  end\n\n  # This is a specialized HTMLGenerator tailored to Puppet manifests\n  class PuppetGenerator < HTMLGenerator\n    def self.for(options)\n      AllReferences.reset\n      HtmlMethod.reset\n\n      if options.all_one_file\n        PuppetGeneratorInOne.new(options)\n      else\n        PuppetGenerator.new(options)\n      end\n    end\n\n    def initialize(options) # :not-new:\n      @options = options\n      load_html_template\n    end\n\n    # loads our own html template file\n    def load_html_template\n      require_relative '../../../../puppet/util/rdoc/generators/template/puppet/puppet'\n      extend RDoc::Page\n    rescue LoadError\n      $stderr.puts \"Could not find Puppet template '#{template}'\"\n      exit 99\n    end\n\n    def gen_method_index\n      # we don't generate an all define index\n      # as the presentation is per module/per class\n    end\n\n    # This is the central method, it generates the whole structures\n    # along with all the indices.\n    def generate_html\n      super\n      gen_into(@nodes)\n      gen_into(@plugins)\n    end\n\n    ##\n    # Generate:\n    #  the list of modules\n    #  the list of classes and definitions of a specific module\n    #  the list of all classes\n    #  the list of nodes\n    #  the list of resources\n    def build_indices\n      @allfiles = []\n      @nodes = []\n      @plugins = []\n\n      # contains all the seen modules\n      @modules = {}\n      @allclasses = {}\n\n      # remove unknown toplevels\n      # it can happen that RDoc triggers a different parser for some files (ie .c, .cc or .h)\n      # in this case RDoc generates a RDoc::TopLevel which we do not support in this generator\n      # So let's make sure we don't generate html for those.\n      @toplevels = @toplevels.select { |tl| tl.is_a? RDoc::PuppetTopLevel }\n\n      # build the modules, classes and per modules classes and define list\n      @toplevels.each do |toplevel|\n        next unless toplevel.document_self\n\n        file = HtmlFile.new(toplevel, @options, FILE_DIR)\n        classes = []\n        methods = []\n        modules = []\n        nodes = []\n\n        # find all classes of this toplevel\n        # store modules if we find one\n        toplevel.each_classmodule do |k|\n          generate_class_list(classes, modules, k, toplevel, CLASS_DIR)\n        end\n\n        # find all defines belonging to this toplevel\n        HtmlMethod.all_methods.each do |m|\n          # find parent module, check this method is not already\n          # defined.\n          if m.context.parent.toplevel === toplevel\n            methods << m\n          end\n        end\n\n        classes.each do |k|\n          @allclasses[k.index_name] = k unless @allclasses.has_key?(k.index_name)\n        end\n\n        # generate nodes and plugins found\n        classes.each do |k|\n          next unless k.context.is_module?\n\n          k.context.each_node do |_name, node|\n            nodes << HTMLPuppetNode.new(node, toplevel, NODE_DIR, @options)\n            @nodes << nodes.last\n          end\n          k.context.each_plugin do |plugin|\n            @plugins << HTMLPuppetPlugin.new(plugin, toplevel, PLUGIN_DIR, @options)\n          end\n          k.context.each_fact do |fact|\n            @plugins << HTMLPuppetPlugin.new(fact, toplevel, PLUGIN_DIR, @options)\n          end\n        end\n\n        @files << file\n        @allfiles << { \"file\" => file, \"modules\" => modules, \"classes\" => classes, \"methods\" => methods, \"nodes\" => nodes }\n      end\n\n      # scan all classes to create the child's references\n      @allclasses.values.each do |klass|\n        superklass = klass.context.superclass\n        next unless superklass\n\n        superklass = AllReferences[superklass]\n        if superklass && (superklass.is_a?(HTMLPuppetClass) || superklass.is_a?(HTMLPuppetNode))\n          superklass.context.add_child(klass.context)\n        end\n      end\n\n      @classes = @allclasses.values\n    end\n\n    # produce a class/module list of HTMLPuppetModule/HTMLPuppetClass\n    # based on the code object traversal.\n    def generate_class_list(classes, modules, from, html_file, class_dir)\n      if from.is_module? and !@modules.has_key?(from.name)\n        k = HTMLPuppetModule.new(from, html_file, class_dir, @options)\n        classes << k\n        @modules[from.name] = k\n        modules << @modules[from.name]\n      elsif from.is_module?\n        modules << @modules[from.name]\n      elsif !from.is_module?\n        k = HTMLPuppetClass.new(from, html_file, class_dir, @options)\n        classes << k\n      end\n      from.each_classmodule do |mod|\n        generate_class_list(classes, modules, mod, html_file, class_dir)\n      end\n    end\n\n    # generate all the subdirectories, modules, classes and files\n    def gen_sub_directories\n      super\n      File.makedirs(MODULE_DIR)\n      File.makedirs(NODE_DIR)\n      File.makedirs(PLUGIN_DIR)\n    rescue\n      $stderr.puts $ERROR_INFO.message\n      exit 1\n    end\n\n    # generate the index of modules\n    def gen_file_index\n      gen_top_index(@modules.values, 'All Modules', RDoc::Page::TOP_INDEX, \"fr_modules_index.html\")\n    end\n\n    # generate a top index\n    def gen_top_index(collection, title, template, filename)\n      template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)\n      res = []\n      collection.sort.each do |f|\n        if f.document_self\n          res << { \"classlist\" => CGI.escapeHTML(\"#{MODULE_DIR}/fr_#{f.index_name}.html\"), \"module\" => CGI.escapeHTML(\"#{CLASS_DIR}/#{f.index_name}.html\"), \"name\" => CGI.escapeHTML(f.index_name) }\n        end\n      end\n\n      values = {\n        \"entries\" => res,\n        'list_title' => CGI.escapeHTML(title),\n        'index_url' => main_url,\n        'charset' => @options.charset,\n        'style_url' => style_url('', @options.css),\n      }\n\n      Puppet::FileSystem.open(filename, nil, \"w:UTF-8\") do |f|\n        template.write_html_on(f, values)\n      end\n    end\n\n    # generate the all classes index file and the combo index\n    def gen_class_index\n      gen_an_index(@classes, 'All Classes', RDoc::Page::CLASS_INDEX, \"fr_class_index.html\")\n      @allfiles.each do |file|\n        next if file['file'].context.file_relative_name =~ /\\.rb$/\n\n        gen_composite_index(\n          file,\n          RDoc::Page::COMBO_INDEX,\n          \"#{MODULE_DIR}/fr_#{file['file'].context.module_name}.html\"\n        )\n      end\n    end\n\n    def gen_composite_index(collection, template, filename)\n      return if Puppet::FileSystem.exist?(filename)\n\n      template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)\n      res1 = []\n      collection['classes'].sort.each do |f|\n        if f.document_self\n          res1 << { \"href\" => \"../\" + CGI.escapeHTML(f.path), \"name\" => CGI.escapeHTML(f.index_name) } unless f.context.is_module?\n        end\n      end\n\n      res2 = []\n      collection['methods'].sort.each do |f|\n        res2 << { \"href\" => \"../#{f.path}\", \"name\" => f.index_name.sub(/\\(.*\\)$/, '') } if f.document_self\n      end\n\n      module_name = []\n      res3 = []\n      res4 = []\n      collection['modules'].sort.each do |f|\n        module_name << { \"href\" => \"../\" + CGI.escapeHTML(f.path), \"name\" => CGI.escapeHTML(f.index_name) }\n        unless f.facts.nil?\n          f.facts.each do |fact|\n            res3 << { \"href\" => \"../\" + CGI.escapeHTML(AllReferences[\"PLUGIN(#{fact.name})\"].path), \"name\" => CGI.escapeHTML(fact.name) }\n          end\n        end\n        next if f.plugins.nil?\n\n        f.plugins.each do |plugin|\n          res4 << { \"href\" => \"../\" + CGI.escapeHTML(AllReferences[\"PLUGIN(#{plugin.name})\"].path), \"name\" => CGI.escapeHTML(plugin.name) }\n        end\n      end\n\n      res5 = []\n      collection['nodes'].sort.each do |f|\n        res5 << { \"href\" => \"../\" + CGI.escapeHTML(f.path), \"name\" => CGI.escapeHTML(f.name) } if f.document_self\n      end\n\n      values = {\n        \"module\" => module_name,\n        \"classes\" => res1,\n        'classes_title' => CGI.escapeHTML(\"Classes\"),\n        'defines_title' => CGI.escapeHTML(\"Defines\"),\n        'facts_title' => CGI.escapeHTML(\"Custom Facts\"),\n        'plugins_title' => CGI.escapeHTML(\"Plugins\"),\n        'nodes_title' => CGI.escapeHTML(\"Nodes\"),\n        'index_url' => main_url,\n        'charset' => @options.charset,\n        'style_url' => style_url('', @options.css),\n      }\n\n      values[\"defines\"] = res2 if res2.size > 0\n      values[\"facts\"] = res3 if res3.size > 0\n      values[\"plugins\"] = res4 if res4.size > 0\n      values[\"nodes\"] = res5 if res5.size > 0\n\n      Puppet::FileSystem.open(filename, nil, \"w:UTF-8\") do |f|\n        template.write_html_on(f, values)\n      end\n    end\n\n    # returns the initial_page url\n    def main_url\n      main_page = @options.main_page\n      ref = nil\n      if main_page\n        ref = AllReferences[main_page]\n        if ref\n          ref = ref.path\n        else\n          $stderr.puts \"Could not find main page #{main_page}\"\n        end\n      end\n\n      unless ref\n        @files.each do |file|\n          if file.document_self and file.context.global\n            ref = CGI.escapeHTML(\"#{CLASS_DIR}/#{file.context.module_name}.html\")\n            break\n          end\n        end\n      end\n\n      unless ref\n        @files.each do |file|\n          if file.document_self and !file.context.global\n            ref = CGI.escapeHTML(\"#{CLASS_DIR}/#{file.context.module_name}.html\")\n            break\n          end\n        end\n      end\n\n      unless ref\n        $stderr.puts \"Couldn't find anything to document\"\n        $stderr.puts \"Perhaps you've used :stopdoc: in all classes\"\n        exit(1)\n      end\n\n      ref\n    end\n  end\n\n  # This module is used to generate a referenced full name list of ContextUser\n  module ReferencedListBuilder\n    def build_referenced_list(list)\n      res = []\n      list.each do |i|\n        ref = AllReferences[i.name] || @context.find_symbol(i.name)\n        ref = ref.viewer if ref and ref.respond_to?(:viewer)\n        name = i.respond_to?(:full_name) ? i.full_name : i.name\n        h_name = CGI.escapeHTML(name)\n        if ref and ref.document_self\n          path = url(ref.path)\n          res << { \"name\" => h_name, \"aref\" => path }\n        else\n          res << { \"name\" => h_name }\n        end\n      end\n      res\n    end\n  end\n\n  # This module is used to hold/generate a list of puppet resources\n  # this is used in HTMLPuppetClass and HTMLPuppetNode\n  module ResourceContainer\n    def collect_resources\n      list = @context.resource_list\n      @resources = list.collect { |m| HTMLPuppetResource.new(m, self, @options) }\n    end\n\n    def build_resource_summary_list(path_prefix = '')\n      collect_resources unless @resources\n      resources = @resources.sort\n      res = []\n      resources.each do |r|\n        res << {\n          \"name\" => CGI.escapeHTML(r.name),\n          \"aref\" => Puppet::Util.uri_encode(path_prefix) + \"\\#\" + Puppet::Util.uri_query_encode(r.aref)\n        }\n      end\n      res\n    end\n\n    def build_resource_detail_list(section)\n      outer = []\n      resources = @resources.sort\n      resources.each do |r|\n        row = {}\n        next unless r.section == section and r.document_self\n\n        row[\"name\"] = CGI.escapeHTML(r.name)\n        desc = r.description.strip\n        row[\"m_desc\"]      = desc unless desc.empty?\n        row[\"aref\"]        = r.aref\n        row[\"params\"]      = r.params\n        outer << row\n      end\n      outer\n    end\n  end\n\n  class HTMLPuppetClass < HtmlClass\n    include ResourceContainer, ReferencedListBuilder\n\n    def value_hash\n      super\n      rl = build_resource_summary_list\n      @values[\"resources\"] = rl unless rl.empty?\n\n      @context.sections.each do |section|\n        secdata = @values[\"sections\"].select { |s| s[\"secsequence\"] == section.sequence }\n        next unless secdata.size == 1\n\n        secdata = secdata[0]\n\n        rdl = build_resource_detail_list(section)\n        secdata[\"resource_list\"] = rdl unless rdl.empty?\n      end\n\n      rl = build_require_list(@context)\n      @values[\"requires\"] = rl unless rl.empty?\n\n      rl = build_realize_list(@context)\n      @values[\"realizes\"] = rl unless rl.empty?\n\n      cl = build_child_list(@context)\n      @values[\"childs\"] = cl unless cl.empty?\n\n      @values\n    end\n\n    def build_require_list(context)\n      build_referenced_list(context.requires)\n    end\n\n    def build_realize_list(context)\n      build_referenced_list(context.realizes)\n    end\n\n    def build_child_list(context)\n      build_referenced_list(context.childs)\n    end\n  end\n\n  class HTMLPuppetNode < ContextUser\n    include ResourceContainer, ReferencedListBuilder\n\n    attr_reader :path\n\n    def initialize(context, html_file, prefix, options)\n      super(context, options)\n\n      @html_file = html_file\n      @is_module = context.is_module?\n      @values    = {}\n\n      context.viewer = self\n\n      if options.all_one_file\n        @path = context.full_name\n      else\n        @path = http_url(context.full_name, prefix)\n      end\n\n      AllReferences.add(\"NODE(#{@context.full_name})\", self)\n    end\n\n    def name\n      @context.name\n    end\n\n    # return the relative file name to store this class in,\n    # which is also its url\n    def http_url(full_name, prefix)\n      path = full_name.dup\n      path.gsub!(/<<\\s*(\\w*)/) { \"from-#{::Regexp.last_match(1)}\" } if path['<<']\n      File.join(prefix, path.split(\"::\").collect { |p| Digest::MD5.hexdigest(p) }) + \".html\"\n    end\n\n    def parent_name\n      @context.parent.full_name\n    end\n\n    def index_name\n      name\n    end\n\n    def write_on(f)\n      value_hash\n\n      template = TemplatePage.new(\n        RDoc::Page::BODYINC,\n        RDoc::Page::NODE_PAGE,\n        RDoc::Page::METHOD_LIST\n      )\n      template.write_html_on(f, @values)\n    end\n\n    def value_hash\n      class_attribute_values\n      add_table_of_sections\n\n      @values[\"charset\"] = @options.charset\n      @values[\"style_url\"] = style_url(path, @options.css)\n\n      d = markup(@context.comment)\n      @values[\"description\"] = d unless d.empty?\n\n      ml = build_method_summary_list\n      @values[\"methods\"] = ml unless ml.empty?\n\n      rl = build_resource_summary_list\n      @values[\"resources\"] = rl unless rl.empty?\n\n      il = build_include_list(@context)\n      @values[\"includes\"] = il unless il.empty?\n\n      rl = build_require_list(@context)\n      @values[\"requires\"] = rl unless rl.empty?\n\n      rl = build_realize_list(@context)\n      @values[\"realizes\"] = rl unless rl.empty?\n\n      cl = build_child_list(@context)\n      @values[\"childs\"] = cl unless cl.empty?\n\n      @values[\"sections\"] = @context.sections.map do |section|\n        secdata = {\n          \"sectitle\" => section.title,\n          \"secsequence\" => section.sequence,\n          \"seccomment\" => markup(section.comment)\n        }\n\n        al = build_alias_summary_list(section)\n        secdata[\"aliases\"] = al unless al.empty?\n\n        co = build_constants_summary_list(section)\n        secdata[\"constants\"] = co unless co.empty?\n\n        al = build_attribute_list(section)\n        secdata[\"attributes\"] = al unless al.empty?\n\n        cl = build_class_list(0, @context, section)\n        secdata[\"classlist\"] = cl unless cl.empty?\n\n        mdl = build_method_detail_list(section)\n        secdata[\"method_list\"] = mdl unless mdl.empty?\n\n        rdl = build_resource_detail_list(section)\n        secdata[\"resource_list\"] = rdl unless rdl.empty?\n\n        secdata\n      end\n\n      @values\n    end\n\n    def build_attribute_list(section)\n      atts = @context.attributes.sort\n      res = []\n      atts.each do |att|\n        next unless att.section == section\n\n        next unless att.visibility == :public || att.visibility == :protected || @options.show_all\n\n        entry = {\n          \"name\" => CGI.escapeHTML(att.name),\n          \"rw\" => att.rw,\n          \"a_desc\" => markup(att.comment, true)\n        }\n        unless att.visibility == :public || att.visibility == :protected\n          entry[\"rw\"] << \"-\"\n        end\n        res << entry\n      end\n      res\n    end\n\n    def class_attribute_values\n      h_name = CGI.escapeHTML(name)\n\n      @values[\"classmod\"]  = \"Node\"\n      @values[\"title\"]     = CGI.escapeHTML(\"#{@values['classmod']}: #{h_name}\")\n\n      c = @context\n      c = c.parent while c and !c.diagram\n\n      @values[\"diagram\"] = diagram_reference(c.diagram) if c && c.diagram\n\n      @values[\"full_name\"] = h_name\n\n      parent_class = @context.superclass\n\n      if parent_class\n        @values[\"parent\"] = CGI.escapeHTML(parent_class)\n\n        if parent_name\n          lookup = parent_name + \"::#{parent_class}\"\n        else\n          lookup = parent_class\n        end\n        lookup = \"NODE(#{lookup})\"\n        parent_url = AllReferences[lookup] || AllReferences[parent_class]\n        @values[\"par_url\"] = aref_to(parent_url.path) if parent_url and parent_url.document_self\n      end\n\n      files = []\n      @context.in_files.each do |f|\n        res = {}\n        full_path = CGI.escapeHTML(f.file_absolute_name)\n\n        res[\"full_path\"]     = full_path\n        res[\"full_path_url\"] = aref_to(f.viewer.path) if f.document_self\n\n        res[\"cvsurl\"] = cvs_url(@options.webcvs, full_path) if @options.webcvs\n\n        files << res\n      end\n\n      @values['infiles'] = files\n    end\n\n    def build_require_list(context)\n      build_referenced_list(context.requires)\n    end\n\n    def build_realize_list(context)\n      build_referenced_list(context.realizes)\n    end\n\n    def build_child_list(context)\n      build_referenced_list(context.childs)\n    end\n\n    def <=>(other)\n      name <=> other.name\n    end\n  end\n\n  class HTMLPuppetModule < HtmlClass\n    def value_hash\n      @values = super\n\n      fl = build_facts_summary_list\n      @values[\"facts\"] = fl unless fl.empty?\n\n      pl = build_plugins_summary_list\n      @values[\"plugins\"] = pl unless pl.empty?\n\n      nl = build_nodes_list(0, @context)\n      @values[\"nodelist\"] = nl unless nl.empty?\n\n      @values\n    end\n\n    def build_nodes_list(level, context)\n      res = \"\"\n      prefix = \"&nbsp;&nbsp;::\" * level;\n\n      context.nodes.sort.each do |node|\n        next unless node.document_self\n\n        res <<\n          prefix <<\n          \"Node \" <<\n          href(url(node.viewer.path), \"link\", node.full_name) <<\n          \"<br />\\n\"\n      end\n      res\n    end\n\n    def build_facts_summary_list\n      potentially_referenced_list(context.facts) { |fn| [\"PLUGIN(#{fn})\"] }\n    end\n\n    def build_plugins_summary_list\n      potentially_referenced_list(context.plugins) { |fn| [\"PLUGIN(#{fn})\"] }\n    end\n\n    def facts\n      @context.facts\n    end\n\n    def plugins\n      @context.plugins\n    end\n  end\n\n  class HTMLPuppetPlugin < ContextUser\n    attr_reader :path\n\n    def initialize(context, html_file, prefix, options)\n      super(context, options)\n\n      @html_file = html_file\n      @is_module = false\n      @values    = {}\n\n      context.viewer = self\n\n      if options.all_one_file\n        @path = context.full_name\n      else\n        @path = http_url(context.full_name, prefix)\n      end\n\n      AllReferences.add(\"PLUGIN(#{@context.full_name})\", self)\n    end\n\n    def name\n      @context.name\n    end\n\n    # return the relative file name to store this class in,\n    # which is also its url\n    def http_url(full_name, prefix)\n      path = full_name.dup\n      path.gsub!(/<<\\s*(\\w*)/) { \"from-#{::Regexp.last_match(1)}\" } if path['<<']\n      File.join(prefix, path.split(\"::\")) + \".html\"\n    end\n\n    def parent_name\n      @context.parent.full_name\n    end\n\n    def index_name\n      name\n    end\n\n    def write_on(f)\n      value_hash\n\n      template = TemplatePage.new(\n        RDoc::Page::BODYINC,\n        RDoc::Page::PLUGIN_PAGE,\n        RDoc::Page::PLUGIN_LIST\n      )\n      template.write_html_on(f, @values)\n    end\n\n    def value_hash\n      attribute_values\n      add_table_of_sections\n\n      @values[\"charset\"] = @options.charset\n      @values[\"style_url\"] = style_url(path, @options.css)\n\n      d = markup(@context.comment)\n      @values[\"description\"] = d unless d.empty?\n\n      if context.is_fact?\n        unless context.confine.empty?\n          res = {}\n          res[\"type\"] = context.confine[:type]\n          res[\"value\"] = context.confine[:value]\n          @values[\"confine\"] = [res]\n        end\n      else\n        @values[\"type\"] = context.type\n      end\n\n      @values[\"sections\"] = @context.sections.map do |section|\n        secdata = {\n          \"sectitle\" => section.title,\n          \"secsequence\" => section.sequence,\n          \"seccomment\" => markup(section.comment)\n        }\n        secdata\n      end\n\n      @values\n    end\n\n    def attribute_values\n      h_name = CGI.escapeHTML(name)\n\n      if @context.is_fact?\n        @values[\"classmod\"]  = \"Fact\"\n      else\n        @values[\"classmod\"]  = \"Plugin\"\n      end\n      @values[\"title\"]     = \"#{@values['classmod']}: #{h_name}\"\n\n      @values[\"full_name\"] = h_name\n\n      files = []\n      @context.in_files.each do |f|\n        res = {}\n        full_path = CGI.escapeHTML(f.file_absolute_name)\n\n        res[\"full_path\"]     = full_path\n        res[\"full_path_url\"] = aref_to(f.viewer.path) if f.document_self\n\n        res[\"cvsurl\"] = cvs_url(@options.webcvs, full_path) if @options.webcvs\n\n        files << res\n      end\n\n      @values['infiles'] = files\n    end\n\n    def <=>(other)\n      name <=> other.name\n    end\n  end\n\n  class HTMLPuppetResource\n    include MarkUp\n\n    attr_reader :context\n\n    @@seq = \"R000000\"\n\n    def initialize(context, html_class, options)\n      @context    = context\n      @html_class = html_class\n      @options    = options\n      @@seq       = @@seq.succ\n      @seq        = @@seq\n\n      context.viewer = self\n\n      AllReferences.add(name, self)\n    end\n\n    def as_href(from_path)\n      if @options.all_one_file\n        \"##{path}\"\n      else\n        HTMLGenerator.gen_url(from_path, path)\n      end\n    end\n\n    def name\n      @context.name\n    end\n\n    def section\n      @context.section\n    end\n\n    def index_name\n      @context.name.to_s\n    end\n\n    def params\n      @context.params\n    end\n\n    def parent_name\n      if @context.parent.parent\n        @context.parent.parent.full_name\n      else\n        nil\n      end\n    end\n\n    def aref\n      @seq\n    end\n\n    def path\n      if @options.all_one_file\n        aref\n      else\n        @html_class.path + \"##{aref}\"\n      end\n    end\n\n    def description\n      markup(@context.comment)\n    end\n\n    def <=>(other)\n      @context <=> other.context\n    end\n\n    def document_self\n      @context.document_self\n    end\n\n    def find_symbol(symbol, method = nil)\n      res = @context.parent.find_symbol(symbol, method)\n      res && res.viewer\n    end\n  end\n\n  class PuppetGeneratorInOne < HTMLGeneratorInOne\n    def gen_method_index\n      gen_an_index(HtmlMethod.all_methods, 'Defines')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rdoc/generators/template/puppet/puppet.rb",
    "content": "# frozen_string_literal: true\n\n#\n# = CSS2 RDoc HTML template\n#\n# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a\n# bit more of the appearance of the output to cascading stylesheets than the\n# default. It was designed for clean inline code display, and uses DHTMl to\n# toggle the visbility of each method's source with each click on the '[source]'\n# link.\n#\n# == Authors\n#\n# * Michael Granger <ged@FaerieMUD.org>\n#\n# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.\n#\n# This work is licensed under the Creative Commons Attribution License. To view\n# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or\n# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California\n# 94305, USA.\n#\n\nmodule RDoc\n  module Page\n    FONTS = \"Verdana,Arial,Helvetica,sans-serif\"\n\n    STYLE = %(\n/* Reset */\nhtml,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;}\n:focus{outline:0;}\nbody{line-height:1;color:#282828;background:#fff;}\nol,ul{list-style:none;}\ntable{border-collapse:separate;border-spacing:0;}\ncaption,th,td{text-align:left;font-weight:normal;}\nblockquote:before,blockquote:after,q:before,q:after{content:\"\";}\nblockquote,q{quotes:\"\"\"\";}\n\nbody {\n    font-family: Verdana,Arial,Helvetica,sans-serif;\n    font-size: 0.9em;\n}\n\npre {\n    background: none repeat scroll 0 0 #F7F7F7;\n    border: 1px dashed #DDDDDD;\n    color: #555555;\n    font-family: courier;\n    margin: 10px 19px;\n    padding: 10px;\n }\n\nh1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }\nh1 { font-size: 1.2em; }\nh2,h3,h4 { margin-top: 1em; color:#558; }\nh2,h3 { font-size: 1.1em; }\n\na { color: #037; text-decoration: none; }\na:hover { color: #04d; }\n\n/* Override the base stylesheet's Anchor inside a table cell */\ntd > a {\n  background: transparent;\n  color: #039;\n  text-decoration: none;\n}\n\n/* and inside a section title */\n.section-title > a {\n  background: transparent;\n  color: #eee;\n  text-decoration: none;\n}\n\n/* === Structural elements =================================== */\n\ndiv#index {\n    padding: 0;\n}\n\n\ndiv#index a {\n\tdisplay:inline-block;\n\tpadding:2px 10px;\n}\n\n\ndiv#index .section-bar {\n\tbackground: #ffe;\n\tpadding:10px;\n}\n\n\ndiv#classHeader, div#fileHeader {\n    border-bottom: 1px solid #ddd;\n\tpadding:10px;\n\tfont-size:0.9em;\n}\n\ndiv#classHeader a, div#fileHeader a {\n    background: inherit;\n    color: white;\n}\n\ndiv#classHeader td, div#fileHeader td {\n    color: white;\n\tpadding:3px;\n\tfont-size:0.9em;\n}\n\n\ndiv#fileHeader {\n    background: #057;\n}\n\ndiv#classHeader {\n    background: #048;\n}\n\ndiv#nodeHeader {\n    background: #7f7f7f;\n}\n\n.class-name-in-header {\n  font-weight: bold;\n}\n\n\ndiv#bodyContent {\n    padding: 10px;\n}\n\ndiv#description {\n    padding: 10px;\n    background: #f5f5f5;\n    border: 1px dotted #ddd;\n\tline-height:1.2em;\n}\n\ndiv#description h1,h2,h3,h4,h5,h6 {\n    color: #125;;\n    background: transparent;\n}\n\ndiv#validator-badges {\n    text-align: center;\n}\ndiv#validator-badges img { border: 0; }\n\ndiv#copyright {\n    color: #333;\n    background: #efefef;\n    font: 0.75em sans-serif;\n    margin-top: 5em;\n    margin-bottom: 0;\n    padding: 0.5em 2em;\n}\n\n\n/* === Classes =================================== */\n\ntable.header-table {\n    color: white;\n    font-size: small;\n}\n\n.type-note {\n    font-size: small;\n    color: #DEDEDE;\n}\n\n.xxsection-bar {\n    background: #eee;\n    color: #333;\n    padding: 3px;\n}\n\n.section-bar {\n   color: #333;\n   border-bottom: 1px solid #ddd;\n   padding:10px 0;\n   margin:5px 0 10px 0;\n}\n\ndiv#class-list, div#methods, div#includes, div#resources, div#requires, div#realizes, div#attribute-list { padding:10px; }\n\n.section-title {\n    background: #79a;\n    color: #eee;\n    padding: 3px;\n    margin-top: 2em;\n    border: 1px solid #999;\n}\n\n.top-aligned-row {  vertical-align: top }\n.bottom-aligned-row { vertical-align: bottom }\n\n/* --- Context section classes ----------------------- */\n\n.context-row { }\n.context-item-name { font-family: monospace; font-weight: bold; color: black; }\n.context-item-value { font-size: small; color: #448; }\n.context-item-desc { color: #333; padding-left: 2em; }\n\n/* --- Method classes -------------------------- */\n.method-detail {\n    background: #f5f5f5;\n}\n.method-heading {\n  color: #333;\n  font-style:italic;\n  background: #ddd;\n  padding:5px 10px;\n}\n.method-signature { color: black; background: inherit; }\n.method-name { font-weight: bold; }\n.method-args { font-style: italic; }\n.method-description { padding: 10px 10px 20px 10px; }\n\n/* --- Source code sections -------------------- */\n\na.source-toggle { font-size: 90%; }\ndiv.method-source-code {\n    background: #262626;\n    color: #ffdead;\n    margin: 1em;\n    padding: 0.5em;\n    border: 1px dashed #999;\n    overflow: hidden;\n}\n\ndiv.method-source-code pre { color: #ffdead; overflow: hidden; }\n\n/* --- Ruby keyword styles --------------------- */\n\n.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }\n\n.ruby-constant  { color: #7fffd4; background: transparent; }\n.ruby-keyword { color: #00ffff; background: transparent; }\n.ruby-ivar    { color: #eedd82; background: transparent; }\n.ruby-operator  { color: #00ffee; background: transparent; }\n.ruby-identifier { color: #ffdead; background: transparent; }\n.ruby-node    { color: #ffa07a; background: transparent; }\n.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }\n.ruby-regexp  { color: #ffa07a; background: transparent; }\n.ruby-value   { color: #7fffd4; background: transparent; }\n)\n\n    #####################################################################\n    ### H E A D E R   T E M P L A T E\n    #####################################################################\n\n    XHTML_PREAMBLE = %(<?xml version=\"1.0\" encoding=\"%charset%\"?>\n<!DOCTYPE html\n     PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n     \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n)\n\n    HEADER = XHTML_PREAMBLE + %{\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <title>%title%</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%charset%\" />\n  <meta http-equiv=\"Content-Script-Type\" content=\"text/javascript\" />\n  <link rel=\"stylesheet\" href=\"%style_url%\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\">\n  // <![CDATA[\n\n  function popupCode( url ) {\n    window.open(url, \"Code\", \"resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400\")\n  }\n\n  function toggleCode( id ) {\n    if ( document.getElementById )\n      elem = document.getElementById( id );\n    else if ( document.all )\n      elem = eval( \"document.all.\" + id );\n    else\n      return false;\n\n    elemStyle = elem.style;\n\n    if ( elemStyle.display != \"block\" ) {\n      elemStyle.display = \"block\"\n    } else {\n      elemStyle.display = \"none\"\n    }\n\n    return true;\n  }\n\n  // Make codeblocks hidden by default\n  document.writeln( \"<style type=\\\\\"text/css\\\\\">div.method-source-code { display: none }</style>\" )\n\n  // ]]>\n  </script>\n\n</head>\n<body>\n}\n\n    #####################################################################\n    ### C O N T E X T   C O N T E N T   T E M P L A T E\n    #####################################################################\n\n    CONTEXT_CONTENT = %(\n)\n\n    #####################################################################\n    ### F O O T E R   T E M P L A T E\n    #####################################################################\n    FOOTER = %(\n<div id=\"validator-badges\">\n  <p><small><a href=\"http://validator.w3.org/check/referer\">[Validate]</a></small></p>\n</div>\n\n</body>\n</html>\n)\n\n    #####################################################################\n    ### F I L E   P A G E   H E A D E R   T E M P L A T E\n    #####################################################################\n\n    FILE_PAGE = %{\n  <div id=\"fileHeader\">\n    <h1>%short_name%</h1>\n    <table class=\"header-table\">\n    <tr class=\"top-aligned-row\">\n      <td><strong>Path:</strong></td>\n      <td>%full_path%\nIF:cvsurl\n        &nbsp;(<a href=\"%cvsurl%\"><acronym title=\"Concurrent Versioning System\">CVS</acronym></a>)\nENDIF:cvsurl\n      </td>\n    </tr>\n    <tr class=\"top-aligned-row\">\n      <td><strong>Last Update:</strong></td>\n      <td>%dtm_modified%</td>\n    </tr>\n    </table>\n  </div>\n}\n\n    #####################################################################\n    ### C L A S S   P A G E   H E A D E R   T E M P L A T E\n    #####################################################################\n\n    CLASS_PAGE = %{\n    <div id=\"classHeader\">\n        <table class=\"header-table\">\n        <tr class=\"top-aligned-row\">\n          <td><strong>%classmod%</strong></td>\n          <td class=\"class-name-in-header\">%full_name%</td>\n        </tr>\n        <tr class=\"top-aligned-row\">\n            <td><strong>In:</strong></td>\n            <td>\nSTART:infiles\nIF:full_path_url\n                <a href=\"%full_path_url%\">\nENDIF:full_path_url\n                %full_path%\nIF:full_path_url\n                </a>\nENDIF:full_path_url\nIF:cvsurl\n        &nbsp;(<a href=\"%cvsurl%\"><acronym title=\"Concurrent Versioning System\">CVS</acronym></a>)\nENDIF:cvsurl\n        <br />\nEND:infiles\n            </td>\n        </tr>\n\nIF:parent\n        <tr class=\"top-aligned-row\">\n            <td><strong>Parent:</strong></td>\n            <td>\nIF:par_url\n                <a href=\"%par_url%\">\nENDIF:par_url\n                %parent%\nIF:par_url\n               </a>\nENDIF:par_url\n            </td>\n        </tr>\nENDIF:parent\n        </table>\n    </div>\n}\n\n    NODE_PAGE = %{\n    <div id=\"nodeHeader\">\n        <table class=\"header-table\">\n        <tr class=\"top-aligned-row\">\n          <td><strong>%classmod%</strong></td>\n          <td class=\"class-name-in-header\">%full_name%</td>\n        </tr>\n        <tr class=\"top-aligned-row\">\n            <td><strong>In:</strong></td>\n            <td>\nSTART:infiles\nIF:full_path_url\n                <a href=\"%full_path_url%\">\nENDIF:full_path_url\n                %full_path%\nIF:full_path_url\n                </a>\nENDIF:full_path_url\nIF:cvsurl\n        &nbsp;(<a href=\"%cvsurl%\"><acronym title=\"Concurrent Versioning System\">CVS</acronym></a>)\nENDIF:cvsurl\n        <br />\nEND:infiles\n            </td>\n        </tr>\n\nIF:parent\n        <tr class=\"top-aligned-row\">\n            <td><strong>Parent:</strong></td>\n            <td>\nIF:par_url\n                <a href=\"%par_url%\">\nENDIF:par_url\n                %parent%\nIF:par_url\n               </a>\nENDIF:par_url\n            </td>\n        </tr>\nENDIF:parent\n        </table>\n    </div>\n}\n\n    PLUGIN_PAGE = %{\n    <div id=\"classHeader\">\n        <table class=\"header-table\">\n        <tr class=\"top-aligned-row\">\n          <td><strong>%classmod%</strong></td>\n          <td class=\"class-name-in-header\">%full_name%</td>\n        </tr>\n        <tr class=\"top-aligned-row\">\n            <td><strong>In:</strong></td>\n            <td>\nSTART:infiles\nIF:full_path_url\n                <a href=\"%full_path_url%\">\nENDIF:full_path_url\n                %full_path%\nIF:full_path_url\n                </a>\nENDIF:full_path_url\nIF:cvsurl\n        &nbsp;(<a href=\"%cvsurl%\"><acronym title=\"Concurrent Versioning System\">CVS</acronym></a>)\nENDIF:cvsurl\n        <br />\nEND:infiles\n            </td>\n        </tr>\n        </table>\n    </div>\n}\n\n    #####################################################################\n    ### M E T H O D   L I S T   T E M P L A T E\n    #####################################################################\n\n    PLUGIN_LIST = %(\n\n  <div id=\"contextContent\">\nIF:description\n    <div id=\"description\">\n      %description%\n    </div>\nENDIF:description\n\n\nIF:toc\n    <div id=\"contents-list\">\n      <h3 class=\"section-bar\">Contents</h3>\n      <ul>\nSTART:toc\n      <li><a href=\"#%href%\">%secname%</a></li>\nEND:toc\n     </ul>\nENDIF:toc\n   </div>\n\n  </div>\n\n<!-- Confine -->\nIF:confine\nSTART:confine\n  <div id=\"attribute-list\">\n    <h3 class=\"section-bar\">Confine</h3>\n    %type%&nbsp;%value%\n    <div class=\"name-list\">\n    </div>\n  </div>\nEND:confine\nENDIF:confine\n\n<!-- Type -->\nIF:type\n  <div id=\"attribute-list\">\n    <h3 class=\"section-bar\">Type</h3>\n    %type%\n    <div class=\"name-list\">\n    </div>\n  </div>\nENDIF:type\n\nSTART:sections\n    <div id=\"section\">\nIF:sectitle\n      <h2 class=\"section-title\"><a name=\"%secsequence%\">%sectitle%</a></h2>\nIF:seccomment\n      <div class=\"section-comment\">\n        %seccomment%\n      </div>\nENDIF:seccomment\nENDIF:sectitle\nEND:sections\n)\n\n    METHOD_LIST = %{\n\n  <div id=\"contextContent\">\nIF:diagram\n    <div id=\"diagram\">\n      %diagram%\n    </div>\nENDIF:diagram\n\nIF:description\n    <div id=\"description\">\n      %description%\n    </div>\nENDIF:description\n\n\nIF:toc\n    <div id=\"contents-list\">\n      <h3 class=\"section-bar\">Contents</h3>\n      <ul>\nSTART:toc\n      <li><a href=\"#%href%\">%secname%</a></li>\nEND:toc\n     </ul>\nENDIF:toc\n   </div>\n\n<!-- if childs -->\nIF:childs\n       <div id=\"childs\">\n         <h3 class=\"section-bar\">Inherited by</h3>\n         <div id=\"childs-list\">\nSTART:childs\n           <span class=\"child-name\">HREF:aref:name:</span>\nEND:childs\n         </div>\n       </div>\nENDIF:childs\n\nIF:methods\n    <div id=\"method-list\">\n      <h3 class=\"section-bar\">Defines</h3>\n\n      <div class=\"name-list\">\nSTART:methods\n      HREF:aref:name:&nbsp;&nbsp;\nEND:methods\n      </div>\n    </div>\nENDIF:methods\n\nIF:resources\n    <div id=\"method-list\">\n      <h3 class=\"section-bar\">Resources</h3>\n\n      <div class=\"name-list\">\nSTART:resources\n      HREF:aref:name:&nbsp;&nbsp;\nEND:resources\n      </div>\n    </div>\nENDIF:resources\n\n  </div>\n\n\n    <!-- if includes -->\nIF:includes\n    <div id=\"includes\">\n      <h3 class=\"section-bar\">Included Classes</h3>\n\n      <div id=\"includes-list\">\nSTART:includes\n        <span class=\"include-name\">HREF:aref:name:</span>\nEND:includes\n      </div>\n    </div>\nENDIF:includes\n\n    <!-- if requires -->\nIF:requires\n    <div id=\"requires\">\n      <h3 class=\"section-bar\">Required Classes</h3>\n\n      <div id=\"requires-list\">\nSTART:requires\n        <span class=\"require-name\">HREF:aref:name:</span>\nEND:requires\n      </div>\n    </div>\nENDIF:requires\n\n    <!-- if realizes -->\nIF:realizes\n    <div id=\"realizes\">\n      <h3 class=\"section-bar\">Realized Resources</h3>\n\n      <div id=\"realizes-list\">\nSTART:realizes\n        <span class=\"realizes-name\">HREF:aref:name:</span>\nEND:realizes\n      </div>\n    </div>\nENDIF:realizes\n\nSTART:sections\n    <div id=\"section\">\nIF:sectitle\n      <h2 class=\"section-title\"><a name=\"%secsequence%\">%sectitle%</a></h2>\nIF:seccomment\n      <div class=\"section-comment\">\n        %seccomment%\n      </div>\nENDIF:seccomment\nENDIF:sectitle\n\n<!-- if facts -->\nIF:facts\n    <div id=\"class-list\">\n      <h3 class=\"section-bar\">Custom Facts</h3>\nSTART:facts\n            HREF:aref:name:&nbsp;&nbsp;\nEND:facts\n    </div>\nENDIF:facts\n\n<!-- if plugins -->\nIF:plugins\n    <div id=\"class-list\">\n      <h3 class=\"section-bar\">Plugins</h3>\nSTART:plugins\nHREF:aref:name:&nbsp;&nbsp;\nEND:plugins\n    </div>\nENDIF:plugins\n\n<!-- if nodes -->\nIF:nodelist\n    <div id=\"class-list\">\n      <h3 class=\"section-bar\">Nodes</h3>\n\n      %nodelist%\n    </div>\nENDIF:nodelist\n\n<!-- if class -->\nIF:classlist\n    <div id=\"class-list\">\n      <h3 class=\"section-bar\">Classes and Modules</h3>\n\n      %classlist%\n    </div>\nENDIF:classlist\n\nIF:constants\n    <div id=\"constants-list\">\n      <h3 class=\"section-bar\">Global Variables</h3>\n\n      <div class=\"name-list\">\n        <table summary=\"Variables\">\nSTART:constants\n        <tr class=\"top-aligned-row context-row\">\n          <td class=\"context-item-name\">%name%</td>\n          <td>=</td>\n          <td class=\"context-item-value\">%value%</td>\nIF:desc\n          <td width=\"3em\">&nbsp;</td>\n          <td class=\"context-item-desc\">%desc%</td>\nENDIF:desc\n        </tr>\nEND:constants\n        </table>\n      </div>\n    </div>\nENDIF:constants\n\nIF:aliases\n    <div id=\"aliases-list\">\n      <h3 class=\"section-bar\">External Aliases</h3>\n\n      <div class=\"name-list\">\n                        <table summary=\"aliases\">\nSTART:aliases\n        <tr class=\"top-aligned-row context-row\">\n          <td class=\"context-item-name\">%old_name%</td>\n          <td>-&gt;</td>\n          <td class=\"context-item-value\">%new_name%</td>\n        </tr>\nIF:desc\n      <tr class=\"top-aligned-row context-row\">\n        <td>&nbsp;</td>\n        <td colspan=\"2\" class=\"context-item-desc\">%desc%</td>\n      </tr>\nENDIF:desc\nEND:aliases\n                        </table>\n      </div>\n    </div>\nENDIF:aliases\n\n\nIF:attributes\n    <div id=\"attribute-list\">\n      <h3 class=\"section-bar\">Attributes</h3>\n\n      <div class=\"name-list\">\n        <table>\nSTART:attributes\n        <tr class=\"top-aligned-row context-row\">\n          <td class=\"context-item-name\">%name%</td>\nIF:rw\n          <td class=\"context-item-value\">&nbsp;[%rw%]&nbsp;</td>\nENDIF:rw\nIFNOT:rw\n          <td class=\"context-item-value\">&nbsp;&nbsp;</td>\nENDIF:rw\n          <td class=\"context-item-desc\">%a_desc%</td>\n        </tr>\nEND:attributes\n        </table>\n      </div>\n    </div>\nENDIF:attributes\n\n\n\n    <!-- if method_list -->\nIF:method_list\n    <div id=\"methods\">\nSTART:method_list\nIF:methods\n      <h3 class=\"section-bar\">Defines</h3>\n\nSTART:methods\n      <div id=\"method-%aref%\" class=\"method-detail\">\n        <a name=\"%aref%\"></a>\n\n        <div class=\"method-heading\">\nIF:codeurl\n          <a href=\"%codeurl%\" target=\"Code\" class=\"method-signature\"\n            onclick=\"popupCode('%codeurl%');return false;\">\nENDIF:codeurl\nIF:sourcecode\n          <a href=\"#%aref%\" class=\"method-signature\">\nENDIF:sourcecode\nIF:callseq\n          <span class=\"method-name\">%callseq%</span>\nENDIF:callseq\nIFNOT:callseq\n          <span class=\"method-name\">%name%</span><span class=\"method-args\">%params%</span>\nENDIF:callseq\nIF:codeurl\n          </a>\nENDIF:codeurl\nIF:sourcecode\n          </a>\nENDIF:sourcecode\n        </div>\n\n        <div class=\"method-description\">\nIF:m_desc\n          %m_desc%\nENDIF:m_desc\nIF:sourcecode\n          <p><a class=\"source-toggle\" href=\"#\"\n            onclick=\"toggleCode('%aref%-source');return false;\">[Source]</a></p>\n          <div class=\"method-source-code\" id=\"%aref%-source\">\n<pre>\n%sourcecode%\n</pre>\n          </div>\nENDIF:sourcecode\n        </div>\n      </div>\n\nEND:methods\nENDIF:methods\nEND:method_list\n\n    </div>\nENDIF:method_list\n\n\n    <!-- if resource_list -->\nIF:resource_list\n    <div id=\"resources\">\n    <h3 class=\"section-bar\">Resources</h3>\nSTART:resource_list\n\n      <div id=\"method-%aref%\" class=\"method-detail\">\n        <a name=\"%aref%\"></a>\n\n        <div class=\"method-heading\">\n          <span class=\"method-name\">%name%</span><br />\nIF:params\nSTART:params\n          &nbsp;&nbsp;&nbsp;<span class=\"method-args\">%name% => %value%</span><br />\nEND:params\nENDIF:params\n        </div>\n\n        <div class=\"method-description\">\nIF:m_desc\n          %m_desc%\nENDIF:m_desc\n        </div>\n      </div>\nEND:resource_list\n\n    </div>\nENDIF:resource_list\n\nEND:sections\n}\n\n    #####################################################################\n    ### B O D Y   T E M P L A T E\n    #####################################################################\n\n    BODY = HEADER + %(\n\n!INCLUDE!  <!-- banner header -->\n\n  <div id=\"bodyContent\">\n) + METHOD_LIST + %(\n\n  </div>\n) + FOOTER\n\n    BODYINC = HEADER + %(\n\n!INCLUDE!  <!-- banner header -->\n\n  <div id=\"bodyContent\">\n\n!INCLUDE!\n\n  </div>\n) + FOOTER\n\n    #####################################################################\n    ### S O U R C E   C O D E   T E M P L A T E\n    #####################################################################\n\n    SRC_PAGE = XHTML_PREAMBLE + %(\n<html>\n<head>\n  <title>%title%</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%charset%\" />\n  <link rel=\"stylesheet\" href=\"%style_url%\" type=\"text/css\" media=\"screen\" />\n</head>\n<body class=\"standalone-code\">\n  <pre>%code%</pre>\n</body>\n</html>\n)\n\n    #####################################################################\n    ### I N D E X   F I L E   T E M P L A T E S\n    #####################################################################\n\n    FR_INDEX_BODY = %(\n!INCLUDE!\n)\n\n    FILE_INDEX = XHTML_PREAMBLE + %(\n<!--\n\n    %list_title%\n\n  -->\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <title>%list_title%</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%charset%\" />\n  <link rel=\"stylesheet\" href=\"%style_url%\" type=\"text/css\" />\n  <base target=\"docwin\" />\n</head>\n<body>\n<div id=\"index\">\n  <h1 class=\"section-bar\">%list_title%</h1>\n  <div id=\"index-entries\">\nSTART:entries\n    <a href=\"%href%\">%name%</a><br />\nEND:entries\n  </div>\n</div>\n</body>\n</html>\n)\n\n    TOP_INDEX = XHTML_PREAMBLE + %{\n<!--\n\n    %list_title%\n\n  -->\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <title>%list_title%</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%charset%\" />\n  <link rel=\"stylesheet\" href=\"%style_url%\" type=\"text/css\" />\n  <base target=\"classes\" />\n  <SCRIPT LANGUAGE=\"JavaScript\">\n  <!--\n  function load(classlist,module) {\n      parent.classes.location.href = classlist;\n      parent.docwin.location.href = module;\n  }\n  //--></SCRIPT>\n</head>\n<body>\n<div id=\"index\">\n  <h1 class=\"section-bar\">%list_title%</h1>\n  <div id=\"index-entries\">\nSTART:entries\n    <a href=\"%classlist%\" onclick=\"load('%classlist%','%module%'); return true;\">%name%</a><br />\nEND:entries\n  </div>\n</div>\n</body>\n</html>\n}\n\n    CLASS_INDEX = FILE_INDEX\n    METHOD_INDEX = FILE_INDEX\n\n    COMBO_INDEX = XHTML_PREAMBLE + %{\n<!--\n\n    %classes_title% &amp; %defines_title%\n\n  -->\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <title>%classes_title% &amp; %defines_title%</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%charset%\" />\n  <link rel=\"stylesheet\" href=\"../%style_url%\" type=\"text/css\" />\n  <base target=\"docwin\" />\n  <SCRIPT LANGUAGE=\"JavaScript\">\n  <!--\n  function load(url) {\n      parent.docwin.location.href = url;\n  }\n  //--></SCRIPT>\n\n</head>\n<body>\n<div id=\"index\">\n\n    <a href=\"../fr_class_index.html\" target=\"classes\">All Classes</a><br />\n\n\n<h1 class=\"section-bar\">Module</h1>\n  <div id=\"index-entries\">\nSTART:module\n    <a href=\"%href%\" onclick=\"load('%href%'); return true;\">%name%</a><br />\nEND:module\n  </div>\n  </div>\n<div id=\"index\">\n\nIF:nodes\n  <h1 class=\"section-bar\">%nodes_title%</h1>\n  <div id=\"index-entries\">\nSTART:nodes\n<a href=\"%href%\" onclick=\"load('%href%'); return true;\">%name%</a><br />\nEND:nodes\n  </div>\nENDIF:nodes\n\nIF:classes\n  <h1 class=\"section-bar\">%classes_title%</h1>\n  <div id=\"index-entries\">\nSTART:classes\n<a href=\"%href%\" onclick=\"load('%href%'); return true;\">%name%</a><br />\nEND:classes\n  </div>\nENDIF:classes\n\nIF:defines\n  <h1 class=\"section-bar\">%defines_title%</h1>\n    <div id=\"index-entries\">\nSTART:defines\n<a href=\"%href%\" onclick=\"load('%href%'); return true;\">%name%</a><br />\nEND:defines\n    </div>\nENDIF:defines\n\nIF:facts\n  <h1 class=\"section-bar\">%facts_title%</h1>\n    <div id=\"index-entries\">\nSTART:facts\n<a href=\"%href%\" onclick=\"load('%href%'); return true;\">%name%</a><br />\nEND:facts\n    </div>\nENDIF:facts\n\n\nIF:plugins\n  <h1 class=\"section-bar\">%plugins_title%</h1>\n    <div id=\"index-entries\">\nSTART:plugins\n<a href=\"%href%\" onclick=\"load('%href%'); return true;\">%name%</a><br />\nEND:plugins\n    </div>\nENDIF:plugins\n\n</div>\n</body>\n</html>\n}\n\n    INDEX = %(<?xml version=\"1.0\" encoding=\"%charset%\"?>\n<!DOCTYPE html\n     PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\"\n     \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\n\n<!--\n\n    %title%\n\n  -->\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <title>%title%</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%charset%\" />\n</head>\n<frameset cols=\"20%, 80%\">\n    <frameset rows=\"30%,70%\">\n        <frame src=\"fr_modules_index.html\"  title=\"All Modules\" />\n        <frame src=\"fr_class_index.html\" name=\"classes\" title=\"Classes & Defines\" />\n    </frameset>\n    <frame src=\"%initial_page%\" name=\"docwin\" />\n</frameset>\n</html>\n)\n  end # module Page\nend # class RDoc\n\nrequire 'rdoc/generators/template/html/one_page_html'\n"
  },
  {
    "path": "lib/puppet/util/rdoc/parser/puppet_parser_core.rb",
    "content": "# frozen_string_literal: true\n\n# Functionality common to both our RDoc version 1 and 2 parsers.\nmodule RDoc::PuppetParserCore\n  SITE = \"__site__\"\n\n  def self.included(base)\n    base.class_eval do\n      attr_accessor :input_file_name, :top_level\n\n      # parser registration into RDoc\n      parse_files_matching(/\\.(rb)$/)\n    end\n  end\n\n  # called with the top level file\n  def initialize(top_level, file_name, body, options, stats)\n    @options = options\n    @stats   = stats\n    @input_file_name = file_name\n    @top_level = top_level\n    @top_level.extend(RDoc::PuppetTopLevel)\n    @progress = $stderr unless options.quiet\n  end\n\n  # main entry point\n  def scan\n    environment = Puppet.lookup(:current_environment)\n    scan_top_level(@top_level, environment)\n    @top_level\n  end\n\n  # Due to a bug in RDoc, we need to roll our own find_module_named\n  # The issue is that RDoc tries harder by asking the parent for a class/module\n  # of the name. But by doing so, it can mistakenly use a module of same name\n  # but from which we are not descendant.\n  def find_object_named(container, name)\n    return container if container.name == name\n\n    container.each_classmodule do |m|\n      return m if m.name == name\n    end\n    nil\n  end\n\n  # walk down the namespace and lookup/create container as needed\n  def get_class_or_module(container, name)\n    # class ::A -> A is in the top level\n    if name =~ /^::/\n      container = @top_level\n    end\n\n    names = name.split('::')\n\n    final_name = names.pop\n    names.each do |n|\n      prev_container = container\n      container = find_object_named(container, n)\n      container ||= prev_container.add_class(RDoc::PuppetClass, n, nil)\n    end\n    [container, final_name]\n  end\n\n  # split_module tries to find if +path+ belongs to the module path\n  # if it does, it returns the module name, otherwise if we are sure\n  # it is part of the global manifest path, \"__site__\" is returned.\n  # And finally if this path couldn't be mapped anywhere, nil is returned.\n  def split_module(path, environment)\n    # find a module\n    fullpath = File.expand_path(path)\n    Puppet.debug \"rdoc: testing #{fullpath}\"\n    if fullpath =~ %r{(.*)/([^/]+)/(?:manifests|plugins|lib)/.+\\.(rb)$}\n      modpath = ::Regexp.last_match(1)\n      name = ::Regexp.last_match(2)\n      Puppet.debug \"rdoc: module #{name} into #{modpath} ?\"\n      environment.modulepath.each do |mp|\n        if File.identical?(modpath, mp)\n          Puppet.debug \"rdoc: found module #{name}\"\n          return name\n        end\n      end\n    end\n    if fullpath =~ /\\.(rb)$/\n      # there can be paths we don't want to scan under modules\n      # imagine a ruby or manifest that would be distributed as part as a module\n      # but we don't want those to be hosted under <site>\n      environment.modulepath.each do |mp|\n        # check that fullpath is a descendant of mp\n        dirname = fullpath\n        previous = dirname\n        while (dirname = File.dirname(previous)) != previous\n          previous = dirname\n          return nil if File.identical?(dirname, mp)\n        end\n      end\n    end\n    # we are under a global manifests\n    Puppet.debug \"rdoc: global manifests\"\n    SITE\n  end\n\n  # create documentation for the top level +container+\n  def scan_top_level(container, environment)\n    # use the module README as documentation for the module\n    comment = \"\"\n    %w[README README.rdoc].each do |rfile|\n      readme = File.join(File.dirname(File.dirname(@input_file_name)), rfile)\n      # module README should be UTF-8, not default system encoding\n      comment = File.open(readme, \"r:UTF-8\", &:read) if FileTest.readable?(readme)\n    end\n    look_for_directives_in(container, comment) unless comment.empty?\n\n    # infer module name from directory\n    name = split_module(@input_file_name, environment)\n    if name.nil?\n      # skip .pp files that are not in manifests directories as we can't guarantee they're part\n      # of a module or the global configuration.\n      # PUP-3638, keeping this while it should have no effect since no .pp files are now processed\n      container.document_self = false\n      return\n    end\n\n    Puppet.debug \"rdoc: scanning for #{name}\"\n\n    container.module_name = name\n    container.global = true if name == SITE\n\n    container, name = get_class_or_module(container, name)\n    mod = container.add_module(RDoc::PuppetModule, name)\n    mod.record_location(@top_level)\n    mod.add_comment(comment, @top_level)\n\n    if @input_file_name =~ /\\.rb$/\n      parse_plugins(mod)\n    end\n  end\n\n  # create documentation for plugins\n  def parse_plugins(container)\n    Puppet.debug \"rdoc: scanning plugin or fact\"\n    if @input_file_name =~ %r{/facter/[^/]+\\.rb$}\n      parse_fact(container)\n    else\n      parse_puppet_plugin(container)\n    end\n  end\n\n  # this is a poor man custom fact parser :-)\n  def parse_fact(container)\n    comments = \"\"\n    current_fact = nil\n    parsed_facts = []\n    File.open(@input_file_name) do |of|\n      of.each do |line|\n        # fetch comments\n        case line\n        when /^[ \\t]*# ?(.*)$/\n          comments += ::Regexp.last_match(1) + \"\\n\"\n        when /^[ \\t]*(Facter.add|Puppet\\.runtime\\[:facter\\].add)\\(['\"](.*?)['\"]\\)/\n          current_fact = RDoc::Fact.new(::Regexp.last_match(1), {})\n          look_for_directives_in(container, comments) unless comments.empty?\n          current_fact.comment = comments\n          parsed_facts << current_fact\n          comments = \"\"\n          Puppet.debug \"rdoc: found custom fact #{current_fact.name}\"\n        when /^[ \\t]*confine[ \\t]*:(.*?)[ \\t]*=>[ \\t]*(.*)$/\n          current_fact.confine = { :type => ::Regexp.last_match(1), :value => ::Regexp.last_match(2) } unless current_fact.nil?\n        else # unknown line type\n          comments = \"\"\n        end\n      end\n    end\n    parsed_facts.each do |f|\n      container.add_fact(f)\n      f.record_location(@top_level)\n    end\n  end\n\n  # this is a poor man puppet plugin parser :-)\n  # it doesn't extract doc nor desc :-(\n  def parse_puppet_plugin(container)\n    comments = \"\"\n    current_plugin = nil\n\n    File.open(@input_file_name) do |of|\n      of.each do |line|\n        # fetch comments\n        case line\n        when /^[ \\t]*# ?(.*)$/\n          comments += ::Regexp.last_match(1) + \"\\n\"\n        when /^[ \\t]*(?:Puppet::Parser::Functions::)?newfunction[ \\t]*\\([ \\t]*:(.*?)[ \\t]*,[ \\t]*:type[ \\t]*=>[ \\t]*(:rvalue|:lvalue)/\n          current_plugin = RDoc::Plugin.new(::Regexp.last_match(1), \"function\")\n          look_for_directives_in(container, comments) unless comments.empty?\n          current_plugin.comment = comments\n          current_plugin.record_location(@top_level)\n          container.add_plugin(current_plugin)\n          comments = \"\"\n          Puppet.debug \"rdoc: found new function plugins #{current_plugin.name}\"\n        when /^[ \\t]*Puppet::Type.newtype[ \\t]*\\([ \\t]*:(.*?)\\)/\n          current_plugin = RDoc::Plugin.new(::Regexp.last_match(1), \"type\")\n          look_for_directives_in(container, comments) unless comments.empty?\n          current_plugin.comment = comments\n          current_plugin.record_location(@top_level)\n          container.add_plugin(current_plugin)\n          comments = \"\"\n          Puppet.debug \"rdoc: found new type plugins #{current_plugin.name}\"\n        when /module Puppet::Parser::Functions/\n          # skip\n        else # unknown line type\n          comments = \"\"\n        end\n      end\n    end\n  end\n\n  # New instance of the appropriate PreProcess for our RDoc version.\n  def create_rdoc_preprocess\n    raise(NotImplementedError, \"This method must be overwritten for whichever version of RDoc this parser is working with\")\n  end\n\n  # look_for_directives_in scans the current +comment+ for RDoc directives\n  def look_for_directives_in(context, comment)\n    preprocess = create_rdoc_preprocess\n\n    preprocess.handle(comment) do |directive, param|\n      case directive\n      when \"stopdoc\"\n        context.stop_doc\n        \"\"\n      when \"startdoc\"\n        context.start_doc\n        context.force_documentation = true\n        \"\"\n      when \"enddoc\"\n        # context.done_documenting = true\n        # \"\"\n        throw :enddoc\n      when \"main\"\n        options = Options.instance\n        options.main_page = param\n        \"\"\n      when \"title\"\n        options = Options.instance\n        options.title = param\n        \"\"\n      when \"section\"\n        context.set_current_section(param, comment)\n        comment.replace(\"\") # 1.8 doesn't support #clear\n        break\n      else\n        warn \"Unrecognized directive '#{directive}'\"\n        break\n      end\n    end\n    remove_private_comments(comment)\n  end\n\n  def remove_private_comments(comment)\n    comment.gsub!(/^#--.*?^#\\+\\+/m, '')\n    comment.sub!(/^#--.*/m, '')\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rdoc/parser/puppet_parser_rdoc2.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../../puppet/util/rdoc/parser/puppet_parser_core'\n\nmodule RDoc\n  PUPPET_RDOC_VERSION = 2\n\n  # @api private\n  class PuppetParserRDoc2 < Parser\n    include PuppetParserCore\n\n    def create_rdoc_preprocess\n      Markup::PreProcess.new(@input_file_name, @options.rdoc_include)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rdoc/parser.rb",
    "content": "# frozen_string_literal: true\n\n# Puppet \"parser\" for the rdoc system\n# The parser uses puppet parser and traverse the AST to instruct RDoc about\n# our current structures. It also parses ruby files that could contain\n# either custom facts or puppet plugins (functions, types...)\n\n# rdoc2 includes\nrequire 'rdoc/code_objects'\nrequire_relative '../../../puppet/util/rdoc/code_objects'\nrequire 'rdoc/token_stream'\nrequire 'rdoc/markup/pre_process'\nrequire 'rdoc/parser'\nrequire_relative 'parser/puppet_parser_rdoc2'\n"
  },
  {
    "path": "lib/puppet/util/rdoc.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util'\nmodule Puppet::Util::RDoc\n  module_function\n\n  # launch a rdoc documentation process\n  # with the files/dir passed in +files+\n  def rdoc(outputdir, files, charset = nil)\n    # then rdoc\n    require 'rdoc/rdoc'\n    require 'rdoc/options'\n\n    # load our parser\n    require_relative 'rdoc/parser'\n\n    r = RDoc::RDoc.new\n\n    # specify our own format & where to output\n    options = [\"--fmt\", \"puppet\",\n               \"--quiet\",\n               \"--exclude\", \"/modules/[^/]*/spec/.*$\",\n               \"--exclude\", \"/modules/[^/]*/files/.*$\",\n               \"--exclude\", \"/modules/[^/]*/tests/.*$\",\n               \"--exclude\", \"/modules/[^/]*/templates/.*$\",\n               \"--op\", outputdir]\n\n    options << \"--force-update\"\n    options += [\"--charset\", charset] if charset\n    options += files\n\n    # launch the documentation process\n    r.document(options)\n  end\n\n  # launch an output to console manifest doc\n  def manifestdoc(files)\n    raise _(\"RDOC SUPPORT FOR MANIFEST HAS BEEN REMOVED - See PUP-3638\")\n  end\n\n  # Outputs to the console the documentation\n  # of a manifest\n  def output(file, ast)\n    raise _(\"RDOC SUPPORT FOR MANIFEST HAS BEEN REMOVED - See PUP-3638\")\n  end\n\n  def output_astnode_doc(ast)\n    raise _(\"RDOC SUPPORT FOR MANIFEST HAS BEEN REMOVED - See PUP-3638\")\n  end\n\n  def output_resource_doc(code)\n    raise _(\"RDOC SUPPORT FOR MANIFEST HAS BEEN REMOVED - See PUP-3638\")\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/reference.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/instance_loader'\nrequire 'fileutils'\n\n# Manage Reference Documentation.\nclass Puppet::Util::Reference\n  include Puppet::Util\n  include Puppet::Util::Docs\n\n  extend Puppet::Util::InstanceLoader\n\n  instance_load(:reference, 'puppet/reference')\n\n  def self.modes\n    %w[text]\n  end\n\n  def self.newreference(name, options = {}, &block)\n    ref = new(name, **options, &block)\n    instance_hash(:reference)[name.intern] = ref\n\n    ref\n  end\n\n  def self.page(*sections)\n    depth = 4\n    # Use the minimum depth\n    sections.each do |name|\n      section = reference(name) or raise _(\"Could not find section %{name}\") % { name: name }\n      depth = section.depth if section.depth < depth\n    end\n  end\n\n  def self.references(environment)\n    instance_loader(:reference).loadall(environment)\n    loaded_instances(:reference).sort_by(&:to_s)\n  end\n\n  attr_accessor :page, :depth, :header, :title, :dynamic\n  attr_writer :doc\n\n  def doc\n    if defined?(@doc)\n      \"#{@name} - #{@doc}\"\n    else\n      @title\n    end\n  end\n\n  def dynamic?\n    dynamic\n  end\n\n  def initialize(name, title: nil, depth: nil, dynamic: nil, doc: nil, &block)\n    @name = name\n    @title = title\n    @depth = depth\n    @dynamic = dynamic\n    @doc = doc\n\n    meta_def(:generate, &block)\n\n    # Now handle the defaults\n    @title ||= _(\"%{name} Reference\") % { name: @name.to_s.capitalize }\n    @page ||= @title.gsub(/\\s+/, '')\n    @depth ||= 2\n    @header ||= \"\"\n  end\n\n  # Indent every line in the chunk except those which begin with '..'.\n  def indent(text, tab)\n    text.gsub(/(^|\\A)/, tab).gsub(/^ +\\.\\./, \"..\")\n  end\n\n  def option(name, value)\n    \":#{name.to_s.capitalize}: #{value}\\n\"\n  end\n\n  def text\n    puts output\n  end\n\n  def to_markdown(withcontents = true)\n    # First the header\n    text = markdown_header(@title, 1)\n\n    text << @header\n\n    text << generate\n\n    text\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/resource_template.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util'\nrequire_relative '../../puppet/util/logging'\nrequire 'erb'\n\n# A template wrapper that evaluates a template in the\n# context of a resource, allowing the resource attributes\n# to be looked up from within the template.\n#  This provides functionality essentially equivalent to\n# the language's template() function.  You pass your file\n# path and the resource you want to use into the initialization\n# method, then call result on the instance, and you get back\n# a chunk of text.\n#  The resource's parameters are available as instance variables\n# (as opposed to the language, where we use a method_missing trick).\n#  For example, say you have a resource that generates a file.  You would\n# need to implement the following style of `generate` method:\n#\n#   def generate\n#       template = Puppet::Util::ResourceTemplate.new(\"/path/to/template\", self)\n#\n#       return Puppet::Type.type(:file).new :path => \"/my/file\",\n#           :content => template.evaluate\n#   end\n#\n# This generated file gets added to the catalog (which is what `generate` does),\n# and its content is the result of the template.  You need to use instance\n# variables in your template, so if your template just needs to have the name\n# of the generating resource, it would just have:\n#\n#   <%= @name %>\n#\n# Since the ResourceTemplate class sets as instance variables all of the resource's\n# parameters.\n#\n# Note that this example uses the generating resource as its source of\n# parameters, which is generally most useful, since it allows you to configure\n# the generated resource via the generating resource.\nclass Puppet::Util::ResourceTemplate\n  include Puppet::Util::Logging\n\n  def evaluate\n    set_resource_variables\n    Puppet::Util.create_erb(Puppet::FileSystem.read(@file, :encoding => 'utf-8')).result(binding)\n  end\n\n  def initialize(file, resource)\n    raise ArgumentError, _(\"Template %{file} does not exist\") % { file: file } unless Puppet::FileSystem.exist?(file)\n\n    @file = file\n    @resource = resource\n  end\n\n  private\n\n  def set_resource_variables\n    @resource.to_hash.each do |param, value|\n      var = \"@#{param}\"\n      instance_variable_set(var, value)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/retry_action.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::RetryAction\n  class RetryException < Exception; end # rubocop:disable Lint/InheritException\n  class RetryException::NoBlockGiven < RetryException; end\n  class RetryException::NoRetriesGiven < RetryException; end\n  class RetryException::RetriesExceeded < RetryException; end\n\n  # Execute the supplied block retrying with exponential backoff.\n  #\n  # @param [Hash] options the retry options\n  # @option options [Integer] :retries Maximum number of times to retry.\n  # @option options [Array<Exception>] :retry_exceptions ([StandardError]) Optional array of exceptions that are allowed to be retried.\n  # @yield The block to be executed.\n  def self.retry_action(options = {})\n    # Retry actions for a specified amount of time. This method will allow the final\n    # retry to complete even if that extends beyond the timeout period.\n    unless block_given?\n      raise RetryException::NoBlockGiven\n    end\n\n    retries = options[:retries]\n    if retries.nil?\n      raise RetryException::NoRetriesGiven\n    end\n\n    retry_exceptions = options[:retry_exceptions] || [StandardError]\n    failures = 0\n    begin\n      yield\n    rescue *retry_exceptions => e\n      if failures >= retries\n        raise RetryException::RetriesExceeded, _(\"%{retries} exceeded\") % { retries: retries }, e.backtrace\n      end\n\n      Puppet.info(_(\"Caught exception %{klass}:%{error} retrying\") % { klass: e.class, error: e })\n\n      failures += 1\n\n      # Increase the amount of time that we sleep after every\n      # failed retry attempt.\n      sleep(((2**failures) - 1) * 0.1)\n\n      retry\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rpm_compare.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'English'\nmodule Puppet::Util::RpmCompare\n  ARCH_LIST = %w[\n    noarch i386 i686 ppc ppc64 armv3l armv4b armv4l armv4tl armv5tel\n    armv5tejl armv6l armv7l m68kmint s390 s390x ia64 x86_64 sh3 sh4\n  ].freeze\n\n  ARCH_REGEX = Regexp.new(ARCH_LIST.map { |arch| \"\\\\.#{arch}\" }.join('|'))\n\n  # This is an attempt at implementing RPM's\n  # lib/rpmvercmp.c rpmvercmp(a, b) in Ruby.\n  #\n  # Some of the things in here look REALLY\n  # UGLY and/or arbitrary. Our goal is to\n  # match how RPM compares versions, quirks\n  # and all.\n  #\n  # I've kept a lot of C-like string processing\n  # in an effort to keep this as identical to RPM\n  # as possible.\n  #\n  # returns 1 if str1 is newer than str2,\n  #         0 if they are identical\n  #        -1 if str1 is older than str2\n  def rpmvercmp(str1, str2)\n    return 0 if str1 == str2\n\n    front_strip_re = /^[^A-Za-z0-9~]+/\n\n    while str1.length > 0 or str2.length > 0\n      # trim anything that's in front_strip_re and != '~' off the beginning of each string\n      str1 = str1.gsub(front_strip_re, '')\n      str2 = str2.gsub(front_strip_re, '')\n\n      # \"handle the tilde separator, it sorts before everything else\"\n      if str1 =~ /^~/ && str2 =~ /^~/\n        # if they both have ~, strip it\n        str1 = str1[1..]\n        str2 = str2[1..]\n        next\n      elsif str1 =~ /^~/\n        return -1\n      elsif str2 =~ /^~/\n        return 1\n      end\n\n      break if str1.length == 0 or str2.length == 0\n\n      # \"grab first completely alpha or completely numeric segment\"\n      isnum = false\n      # if the first char of str1 is a digit, grab the chunk of continuous digits from each string\n      if str1 =~ /^[0-9]+/\n        if str1 =~ /^[0-9]+/\n          segment1 = $LAST_MATCH_INFO.to_s\n          str1 = $LAST_MATCH_INFO.post_match\n        else\n          segment1 = ''\n        end\n        if str2 =~ /^[0-9]+/\n          segment2 = $LAST_MATCH_INFO.to_s\n          str2 = $LAST_MATCH_INFO.post_match\n        else\n          segment2 = ''\n        end\n        isnum = true\n      # else grab the chunk of continuous alphas from each string (which may be '')\n      else\n        if str1 =~ /^[A-Za-z]+/\n          segment1 = $LAST_MATCH_INFO.to_s\n          str1 = $LAST_MATCH_INFO.post_match\n        else\n          segment1 = ''\n        end\n        if str2 =~ /^[A-Za-z]+/\n          segment2 = $LAST_MATCH_INFO.to_s\n          str2 = $LAST_MATCH_INFO.post_match\n        else\n          segment2 = ''\n        end\n      end\n\n      # if the segments we just grabbed from the strings are different types (i.e. one numeric one alpha),\n      # where alpha also includes ''; \"numeric segments are always newer than alpha segments\"\n      if segment2.length == 0\n        return 1 if isnum\n\n        return -1\n      end\n\n      if isnum\n        # \"throw away any leading zeros - it's a number, right?\"\n        segment1 = segment1.gsub(/^0+/, '')\n        segment2 = segment2.gsub(/^0+/, '')\n        # \"whichever number has more digits wins\"\n        return 1 if segment1.length > segment2.length\n        return -1 if segment1.length < segment2.length\n      end\n\n      # \"strcmp will return which one is greater - even if the two segments are alpha\n      # or if they are numeric. don't return if they are equal because there might\n      # be more segments to compare\"\n      rc = segment1 <=> segment2\n      return rc if rc != 0\n    end # end while loop\n\n    # if we haven't returned anything yet, \"whichever version still has characters left over wins\"\n    return 1 if str1.length > str2.length\n    return -1 if str1.length < str2.length\n\n    0\n  end\n\n  # parse a rpm \"version\" specification\n  # this re-implements rpm's\n  # rpmUtils.miscutils.stringToVersion() in ruby\n  def rpm_parse_evr(full_version)\n    epoch_index = full_version.index(':')\n    if epoch_index\n      epoch = full_version[0, epoch_index]\n      full_version = full_version[epoch_index + 1, full_version.length]\n    else\n      epoch = nil\n    end\n    begin\n      epoch = String(Integer(epoch))\n    rescue\n      # If there are non-digits in the epoch field, default to nil\n      epoch = nil\n    end\n    release_index = full_version.index('-')\n    if release_index\n      version = full_version[0, release_index]\n      release = full_version[release_index + 1, full_version.length]\n      arch = release.scan(ARCH_REGEX)[0]\n      if arch\n        architecture = arch.delete('.')\n        release.gsub!(ARCH_REGEX, '')\n      end\n    else\n      version = full_version\n      release = nil\n    end\n    { :epoch => epoch, :version => version, :release => release, :arch => architecture }\n  end\n\n  # this method is a native implementation of the\n  # compare_values function in rpm's python bindings,\n  # found in python/header-py.c, as used by rpm.\n  def compare_values(s1, s2)\n    return 0 if s1.nil? && s2.nil?\n    return 1 if !s1.nil? && s2.nil?\n    return -1 if s1.nil? && !s2.nil?\n\n    rpmvercmp(s1, s2)\n  end\n\n  # how rpm compares two package versions:\n  # rpmUtils.miscutils.compareEVR(), which massages data types and then calls\n  # rpm.labelCompare(), found in rpm.git/python/header-py.c, which\n  # sets epoch to 0 if null, then compares epoch, then ver, then rel\n  # using compare_values() and returns the first non-0 result, else 0.\n  # This function combines the logic of compareEVR() and labelCompare().\n  #\n  # \"version_should\" can be v, v-r, or e:v-r.\n  # \"version_is\" will always be at least v-r, can be e:v-r\n  #\n  # return 1: a is newer than b\n  #        0: a and b are the same version\n  #       -1: b is newer than a\n  def rpm_compare_evr(should, is)\n    # pass on to rpm labelCompare\n    should_hash = rpm_parse_evr(should)\n    is_hash = rpm_parse_evr(is)\n\n    unless should_hash[:epoch].nil?\n      rc = compare_values(should_hash[:epoch], is_hash[:epoch])\n      return rc unless rc == 0\n    end\n\n    rc = compare_values(should_hash[:version], is_hash[:version])\n    return rc unless rc == 0\n\n    # here is our special case, PUP-1244.\n    # if should_hash[:release] is nil (not specified by the user),\n    # and comparisons up to here are equal, return equal. We need to\n    # evaluate to whatever level of detail the user specified, so we\n    # don't end up upgrading or *downgrading* when not intended.\n    #\n    # This should NOT be triggered if we're trying to ensure latest.\n    return 0 if should_hash[:release].nil?\n\n    compare_values(should_hash[:release], is_hash[:release])\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/rubygems.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util'\n\nmodule Puppet::Util::RubyGems\n  # Base/factory class for rubygems source. These classes introspec into\n  # rubygems to in order to list where the rubygems system will look for files\n  # to load.\n  class Source\n    class << self\n      # @api private\n      def has_rubygems?\n        # Gems are not actually available when Bundler is loaded, even\n        # though the Gem constant is defined. This is because Bundler\n        # loads in rubygems, but then removes the custom require that\n        # rubygems installs. So when Bundler is around we have to act\n        # as though rubygems is not, e.g. we shouldn't be able to load\n        # a gem that Bundler doesn't want us to see.\n        defined? ::Gem and !defined? ::Bundler\n      end\n\n      # @api private\n      def source\n        if has_rubygems?\n          Gems18Source\n        else\n          NoGemsSource\n        end\n      end\n\n      def new(*args)\n        object = source.allocate\n        object.send(:initialize, *args)\n        object\n      end\n    end\n  end\n\n  # For RubyGems >= 1.8.0\n  # @api private\n  class Gems18Source < Source\n    def directories\n      # `require 'mygem'` will consider and potentially load\n      # prerelease gems, so we need to match that behavior.\n      #\n      # Just load the stub which points to the gem path, and\n      # delay loading the full specification until if/when the\n      # gem is required.\n      Gem::Specification.stubs.collect do |spec|\n        File.join(spec.full_gem_path, 'lib')\n      end\n    end\n\n    def clear_paths\n      Gem.clear_paths\n    end\n  end\n\n  # @api private\n  class NoGemsSource < Source\n    def directories\n      []\n    end\n\n    def clear_paths; end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/run_mode.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'etc'\n\nmodule Puppet\n  module Util\n    class RunMode\n      def initialize(name)\n        @name = name.to_sym\n      end\n\n      attr_reader :name\n\n      def self.[](name)\n        @run_modes ||= {}\n        if Puppet::Util::Platform.windows?\n          @run_modes[name] ||= WindowsRunMode.new(name)\n        else\n          @run_modes[name] ||= UnixRunMode.new(name)\n        end\n      end\n\n      def server?\n        name == :master || name == :server\n      end\n\n      def master?\n        name == :master || name == :server\n      end\n\n      def agent?\n        name == :agent\n      end\n\n      def user?\n        name == :user\n      end\n\n      def run_dir\n        RunMode[name].run_dir\n      end\n\n      def log_dir\n        RunMode[name].log_dir\n      end\n\n      private\n\n      ##\n      # select the system or the user directory depending on the context of\n      # this process.  The most common use is determining filesystem path\n      # values for confdir and vardir.  The intended semantics are:\n      # {https://projects.puppetlabs.com/issues/16637 #16637} for Puppet 3.x\n      #\n      # @todo this code duplicates {Puppet::Settings#which\\_configuration\\_file}\n      #   as described in {https://projects.puppetlabs.com/issues/16637 #16637}\n      def which_dir(system, user)\n        if Puppet.features.root?\n          File.expand_path(system)\n        else\n          File.expand_path(user)\n        end\n      end\n    end\n\n    class UnixRunMode < RunMode\n      def conf_dir\n        which_dir(\"/etc/puppetlabs/puppet\", \"~/.puppetlabs/etc/puppet\")\n      end\n\n      def code_dir\n        which_dir(\"/etc/puppetlabs/code\", \"~/.puppetlabs/etc/code\")\n      end\n\n      def var_dir\n        which_dir(\"/opt/puppetlabs/puppet/cache\", \"~/.puppetlabs/opt/puppet/cache\")\n      end\n\n      def public_dir\n        which_dir(\"/opt/puppetlabs/puppet/public\", \"~/.puppetlabs/opt/puppet/public\")\n      end\n\n      def run_dir\n        which_dir(\"/var/run/puppetlabs\", \"~/.puppetlabs/var/run\")\n      end\n\n      def log_dir\n        which_dir(\"/var/log/puppetlabs/puppet\", \"~/.puppetlabs/var/log\")\n      end\n\n      def pkg_config_path\n        '/opt/puppetlabs/puppet/lib/pkgconfig'\n      end\n\n      def gem_cmd\n        '/opt/puppetlabs/puppet/bin/gem'\n      end\n\n      def common_module_dir\n        '/opt/puppetlabs/puppet/modules'\n      end\n\n      def vendor_module_dir\n        '/opt/puppetlabs/puppet/vendor_modules'\n      end\n    end\n\n    class WindowsRunMode < RunMode\n      def conf_dir\n        which_dir(File.join(windows_common_base(\"puppet/etc\")), \"~/.puppetlabs/etc/puppet\")\n      end\n\n      def code_dir\n        which_dir(File.join(windows_common_base(\"code\")), \"~/.puppetlabs/etc/code\")\n      end\n\n      def var_dir\n        which_dir(File.join(windows_common_base(\"puppet/cache\")), \"~/.puppetlabs/opt/puppet/cache\")\n      end\n\n      def public_dir\n        which_dir(File.join(windows_common_base(\"puppet/public\")), \"~/.puppetlabs/opt/puppet/public\")\n      end\n\n      def run_dir\n        which_dir(File.join(windows_common_base(\"puppet/var/run\")), \"~/.puppetlabs/var/run\")\n      end\n\n      def log_dir\n        which_dir(File.join(windows_common_base(\"puppet/var/log\")), \"~/.puppetlabs/var/log\")\n      end\n\n      def pkg_config_path\n        nil\n      end\n\n      def gem_cmd\n        if (puppet_dir = ENV.fetch('PUPPET_DIR', nil))\n          File.join(puppet_dir.to_s, 'bin', 'gem.bat')\n        else\n          File.join(Gem.default_bindir, 'gem.bat')\n        end\n      end\n\n      def common_module_dir\n        \"#{installdir}/puppet/modules\" if installdir\n      end\n\n      def vendor_module_dir\n        \"#{installdir}\\\\puppet\\\\vendor_modules\" if installdir\n      end\n\n      private\n\n      def installdir\n        ENV.fetch('FACTER_env_windows_installdir', nil)\n      end\n\n      def windows_common_base(*extra)\n        [ENV.fetch('ALLUSERSPROFILE', nil), \"PuppetLabs\"] + extra\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/selinux.rb",
    "content": "# frozen_string_literal: true\n\n# Provides utility functions to help interface Puppet to SELinux.\n#\n# This requires the very new SELinux Ruby bindings.  These bindings closely\n# mirror the SELinux C library interface.\n#\n# Support for the command line tools is not provided because the performance\n# was abysmal.  At this time (2008-11-02) the only distribution providing\n# these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby).\n\nPuppet.features.selinux? # check, but continue even if it's not\n\nrequire 'pathname'\n\nmodule Puppet::Util::SELinux\n  S_IFREG = 0o100000\n  S_IFDIR = 0o040000\n  S_IFLNK = 0o120000\n\n  def self.selinux_support?\n    return false unless defined?(Selinux)\n    if Selinux.is_selinux_enabled == 1\n      return true\n    end\n\n    false\n  end\n\n  def selinux_support?\n    Puppet::Util::SELinux.selinux_support?\n  end\n\n  # Retrieve and return the full context of the file.  If we don't have\n  # SELinux support or if the SELinux call fails then return nil.\n  def get_selinux_current_context(file)\n    return nil unless selinux_support?\n\n    retval = Selinux.lgetfilecon(file)\n    if retval == -1\n      return nil\n    end\n\n    retval[1]\n  end\n\n  # Retrieve and return the default context of the file.  If we don't have\n  # SELinux support or if the SELinux call fails to file a default then return nil.\n  # @deprecated matchpathcon is a deprecated method, selabel_lookup is preferred\n  def get_selinux_default_context(file, resource_ensure = nil)\n    return nil unless selinux_support?\n    # If the filesystem has no support for SELinux labels, return a default of nil\n    # instead of what matchpathcon would return\n    return nil unless selinux_label_support?(file)\n\n    # If the file exists we should pass the mode to matchpathcon for the most specific\n    # matching.  If not, we can pass a mode of 0.\n    mode = file_mode(file, resource_ensure)\n\n    retval = Selinux.matchpathcon(file, mode)\n    retval == -1 ? nil : retval[1]\n  end\n\n  # Retrieve and return the default context of the file using an selinux handle.\n  # If we don't have SELinux support or if the SELinux call fails to file a\n  # default then return nil.\n  def get_selinux_default_context_with_handle(file, handle, resource_ensure = nil)\n    return nil unless selinux_support?\n    # If the filesystem has no support for SELinux labels, return a default of nil\n    # instead of what selabel_lookup would return\n    return nil unless selinux_label_support?(file)\n\n    # Handle is needed for selabel_lookup\n    raise ArgumentError, _(\"Cannot get default context with nil handle\") unless handle\n\n    # If the file exists we should pass the mode to selabel_lookup for the most specific\n    # matching.  If not, we can pass a mode of 0.\n    mode = file_mode(file, resource_ensure)\n\n    retval = Selinux.selabel_lookup(handle, file, mode)\n    retval == -1 ? nil : retval[1]\n  end\n\n  # Take the full SELinux context returned from the tools and parse it\n  # out to the three (or four) component parts.  Supports :seluser, :selrole,\n  # :seltype, and on systems with range support, :selrange.\n  def parse_selinux_context(component, context)\n    if context.nil? or context == \"unlabeled\"\n      return nil\n    end\n\n    components = /^([^\\s:]+):([^\\s:]+):([^\\s:]+)(?::([\\sa-zA-Z0-9:,._-]+))?$/.match(context)\n    unless components\n      raise Puppet::Error, _(\"Invalid context to parse: %{context}\") % { context: context }\n    end\n\n    case component\n    when :seluser\n      components[1]\n    when :selrole\n      components[2]\n    when :seltype\n      components[3]\n    when :selrange\n      components[4]\n    else\n      raise Puppet::Error, _(\"Invalid SELinux parameter type\")\n    end\n  end\n\n  # This updates the actual SELinux label on the file.  You can update\n  # only a single component or update the entire context.\n  # The caveat is that since setting a partial context makes no sense the\n  # file has to already exist.  Puppet (via the File resource) will always\n  # just try to set components, even if all values are specified by the manifest.\n  # I believe that the OS should always provide at least a fall-through context\n  # though on any well-running system.\n  def set_selinux_context(file, value, component = false)\n    return nil unless selinux_support? && selinux_label_support?(file)\n\n    if component\n      # Must first get existing context to replace a single component\n      context = Selinux.lgetfilecon(file)[1]\n      if context == -1\n        # We can't set partial context components when no context exists\n        # unless/until we can find a way to make Puppet call this method\n        # once for all selinux file label attributes.\n        Puppet.warning _(\"Can't set SELinux context on file unless the file already has some kind of context\")\n        return nil\n      end\n      context = context.split(':')\n      case component\n      when :seluser\n        context[0] = value\n      when :selrole\n        context[1] = value\n      when :seltype\n        context[2] = value\n      when :selrange\n        context[3] = value\n      else\n        raise ArgumentError, _(\"set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange\")\n      end\n      context = context.join(':')\n    else\n      context = value\n    end\n\n    retval = Selinux.lsetfilecon(file, context)\n    if retval == 0\n      true\n    else\n      Puppet.warning _(\"Failed to set SELinux context %{context} on %{file}\") % { context: context, file: file }\n      false\n    end\n  end\n\n  # Since this call relies on get_selinux_default_context it also needs a\n  # full non-relative path to the file.  Fortunately, that seems to be all\n  # Puppet uses.  This will set the file's SELinux context to the policy's\n  # default context (if any) if it differs from the context currently on\n  # the file.\n  def set_selinux_default_context(file, resource_ensure = nil)\n    new_context = get_selinux_default_context(file, resource_ensure)\n    return nil unless new_context\n\n    cur_context = get_selinux_current_context(file)\n    if new_context != cur_context\n      set_selinux_context(file, new_context)\n      return new_context\n    end\n    nil\n  end\n\n  ##\n  # selinux_category_to_label is an internal method that converts all\n  # selinux categories to their internal representation, avoiding\n  # potential issues when mcstransd is not functional.\n  #\n  # It is not marked private because it is needed by File's\n  # selcontext.rb, but it is not intended for use outside of Puppet's\n  # code.\n  #\n  # @param category [String] An selinux category, such as \"s0\" or \"SystemLow\"\n  #\n  # @return [String] the numeric category name, such as \"s0\"\n  def selinux_category_to_label(category)\n    # We don't cache this, but there's already a ton of duplicate work\n    # in the selinux handling code.\n\n    path = Selinux.selinux_translations_path\n    begin\n      File.open(path).each do |line|\n        line.strip!\n        next if line.empty?\n        next if line[0] == \"#\" # skip comments\n\n        line.gsub!(/[[:space:]]+/m, '')\n        mapping = line.split(\"=\", 2)\n        if category == mapping[1]\n          return mapping[0]\n        end\n      end\n    rescue SystemCallError => ex\n      log_exception(ex)\n      raise Puppet::Error, _(\"Could not open SELinux category translation file %{path}.\") % { context: context }\n    end\n\n    category\n  end\n\n  ########################################################################\n  # Internal helper methods from here on in, kids.  Don't fiddle.\n  private\n\n  # Check filesystem a path resides on for SELinux support against\n  # whitelist of known-good filesystems.\n  # Returns true if the filesystem can support SELinux labels and\n  # false if not.\n  def selinux_label_support?(file)\n    fstype = find_fs(file)\n    return false if fstype.nil?\n\n    filesystems = %w[ext2 ext3 ext4 gfs gfs2 xfs jfs btrfs tmpfs zfs]\n    filesystems.include?(fstype)\n  end\n\n  # Get mode file type bits set based on ensure on\n  # the file resource. This helps SELinux determine\n  # what context a new resource being created should have.\n  def get_create_mode(resource_ensure)\n    mode = 0\n    case resource_ensure\n    when :present, :file\n      mode |= S_IFREG\n    when :directory\n      mode |= S_IFDIR\n    when :link\n      mode |= S_IFLNK\n    end\n    mode\n  end\n\n  # If the file/directory/symlink exists, return its mode. Otherwise, get the default mode\n  # that should be used to create the file/directory/symlink taking into account the desired\n  # file type specified in +resource_ensure+.\n  def file_mode(file, resource_ensure)\n    filestat = file_lstat(file)\n    filestat.mode\n  rescue Errno::EACCES\n    0\n  rescue Errno::ENOENT\n    if resource_ensure\n      get_create_mode(resource_ensure)\n    else\n      0\n    end\n  end\n\n  # Internal helper function to read and parse /proc/mounts\n  def read_mounts\n    mounts = ''.dup\n    begin\n      if File.method_defined? \"read_nonblock\"\n        # If possible we use read_nonblock in a loop rather than read to work-\n        # a linux kernel bug.  See ticket #1963 for details.\n        mountfh = File.new(\"/proc/mounts\")\n        loop do\n          mounts += mountfh.read_nonblock(1024)\n        end\n      else\n        # Otherwise we shell out and let cat do it for us\n        mountfh = IO.popen(\"/bin/cat /proc/mounts\")\n        mounts = mountfh.read\n      end\n    rescue EOFError\n      # that's expected\n    rescue\n      return nil\n    ensure\n      mountfh.close if mountfh\n    end\n\n    mntpoint = {}\n\n    # Read all entries in /proc/mounts.  The second column is the\n    # mountpoint and the third column is the filesystem type.\n    # We skip rootfs because it is always mounted at /\n    mounts.each_line do |line|\n      params = line.split(' ')\n      next if params[2] == 'rootfs'\n\n      mntpoint[params[1]] = params[2]\n    end\n    mntpoint\n  end\n\n  # Internal helper function to return which type of filesystem a given file\n  # path resides on\n  def find_fs(path)\n    mounts = read_mounts\n    return nil unless mounts\n\n    # cleanpath eliminates useless parts of the path (like '.', or '..', or\n    # multiple slashes), without touching the filesystem, and without\n    # following symbolic links.  This gives the right (logical) tree to follow\n    # while we try and figure out what file-system the target lives on.\n    path = Pathname(path).cleanpath\n    unless path.absolute?\n      raise Puppet::DevError, _(\"got a relative path in SELinux find_fs: %{path}\") % { path: path }\n    end\n\n    # Now, walk up the tree until we find a match for that path in the hash.\n    path.ascend do |segment|\n      return mounts[segment.to_s] if mounts.has_key?(segment.to_s)\n    end\n\n    # Should never be reached...\n    mounts['/']\n  end\n\n  ##\n  # file_lstat is an internal, private method to allow precise stubbing and\n  # mocking without affecting the rest of the system.\n  #\n  # @return [File::Stat] File.lstat result\n  def file_lstat(path)\n    Puppet::FileSystem.lstat(path)\n  end\n  private :file_lstat\nend\n"
  },
  {
    "path": "lib/puppet/util/skip_tags.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/tagging'\n\nclass Puppet::Util::SkipTags\n  include Puppet::Util::Tagging\n\n  def initialize(stags)\n    self.tags = stags unless defined?(@tags)\n  end\n\n  def split_qualified_tags?\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/splayer.rb",
    "content": "# frozen_string_literal: true\n\n# Handle splay options (sleeping for a random interval before executing)\nmodule Puppet::Util::Splayer\n  # Have we splayed already?\n  def splayed?\n    !!@splayed\n  end\n\n  # Sleep when splay is enabled; else just return.\n  def splay(do_splay = Puppet[:splay])\n    return unless do_splay\n    return if splayed?\n\n    time = rand(Puppet[:splaylimit] + 1)\n    Puppet.info _(\"Sleeping for %{time} seconds (splay is enabled)\") % { time: time }\n    sleep(time)\n    @splayed = true\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/storage.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\nrequire 'singleton'\nrequire_relative '../../puppet/util/yaml'\n\n# a class for storing state\nclass Puppet::Util::Storage\n  include Singleton\n  include Puppet::Util\n\n  def self.state\n    @@state\n  end\n\n  def initialize\n    self.class.load\n  end\n\n  # Return a hash that will be stored to disk.  It's worth noting\n  # here that we use the object's full path, not just the name/type\n  # combination.  At the least, this is useful for those non-isomorphic\n  # types like exec, but it also means that if an object changes locations\n  # in the configuration it will lose its cache.\n  def self.cache(object)\n    if object.is_a?(Symbol)\n      name = object\n    else\n      name = object.to_s\n    end\n\n    @@state[name] ||= {}\n  end\n\n  def self.clear\n    @@state.clear\n  end\n\n  def self.init\n    @@state = {}\n  end\n\n  init\n\n  def self.load\n    Puppet.settings.use(:main) unless FileTest.directory?(Puppet[:statedir])\n    filename = Puppet[:statefile]\n\n    unless Puppet::FileSystem.exist?(filename)\n      init if @@state.nil?\n      return\n    end\n    unless File.file?(filename)\n      Puppet.warning(_(\"Checksumfile %{filename} is not a file, ignoring\") % { filename: filename })\n      return\n    end\n    Puppet::Util.benchmark(:debug, \"Loaded state in %{seconds} seconds\") do\n      @@state = Puppet::Util::Yaml.safe_load_file(filename, [Symbol, Time])\n    rescue Puppet::Util::Yaml::YamlLoadError => detail\n      Puppet.err _(\"Checksumfile %{filename} is corrupt (%{detail}); replacing\") % { filename: filename, detail: detail }\n\n      begin\n        File.rename(filename, filename + \".bad\")\n      rescue\n        raise Puppet::Error, _(\"Could not rename corrupt %{filename}; remove manually\") % { filename: filename }, detail.backtrace\n      end\n    end\n\n    unless @@state.is_a?(Hash)\n      Puppet.err _(\"State got corrupted\")\n      init\n    end\n  end\n\n  def self.stateinspect\n    @@state.inspect\n  end\n\n  def self.store\n    Puppet.debug \"Storing state\"\n\n    Puppet.info _(\"Creating state file %{file}\") % { file: Puppet[:statefile] } unless Puppet::FileSystem.exist?(Puppet[:statefile])\n\n    if Puppet[:statettl] == 0 || Puppet[:statettl] == Float::INFINITY\n      Puppet.debug \"Not pruning old state cache entries\"\n    else\n      Puppet::Util.benchmark(:debug, \"Pruned old state cache entries in %{seconds} seconds\") do\n        ttl_cutoff = Time.now - Puppet[:statettl]\n\n        @@state.reject! do |k, _v|\n          @@state[k][:checked] && @@state[k][:checked] < ttl_cutoff\n        end\n      end\n    end\n\n    Puppet::Util.benchmark(:debug, \"Stored state in %{seconds} seconds\") do\n      Puppet::Util::Yaml.dump(@@state, Puppet[:statefile])\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/suidmanager.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/warnings'\nrequire 'forwardable'\nrequire 'etc'\n\nmodule Puppet::Util::SUIDManager\n  include Puppet::Util::Warnings\n  extend Forwardable\n\n  # Note groups= is handled specially due to a bug in OS X 10.6, 10.7,\n  # and probably upcoming releases...\n  to_delegate_to_process = [:euid=, :euid, :egid=, :egid, :uid=, :uid, :gid=, :gid, :groups]\n\n  to_delegate_to_process.each do |method|\n    def_delegator Process, method\n    module_function method\n  end\n\n  def osx_maj_ver\n    return @osx_maj_ver unless @osx_maj_ver.nil?\n\n    @osx_maj_ver = Puppet.runtime[:facter].value('os.macosx.version.major') || false\n  end\n  module_function :osx_maj_ver\n\n  def groups=(grouplist)\n    Process.groups = grouplist\n  rescue Errno::EINVAL => e\n    # We catch Errno::EINVAL as some operating systems (OS X in particular) can\n    # cause troubles when using Process#groups= to change *this* user / process\n    # list of supplementary groups membership.  This is done via Ruby's function\n    # \"static VALUE proc_setgroups(VALUE obj, VALUE ary)\" which is effectively\n    # a wrapper for \"int setgroups(size_t size, const gid_t *list)\" (part of SVr4\n    # and 4.3BSD but not in POSIX.1-2001) that fails and sets errno to EINVAL.\n    #\n    # This does not appear to be a problem with Ruby but rather an issue on the\n    # operating system side.  Therefore we catch the exception and look whether\n    # we run under OS X or not -- if so, then we acknowledge the problem and\n    # re-throw the exception otherwise.\n    if !osx_maj_ver || osx_maj_ver.empty?\n      raise e\n    end\n  end\n  module_function :groups=\n\n  def self.root?\n    if Puppet::Util::Platform.windows?\n      require_relative '../../puppet/util/windows/user'\n      Puppet::Util::Windows::User.admin?\n    else\n      Process.uid == 0\n    end\n  end\n\n  # Methods to handle changing uid/gid of the running process. In general,\n  # these will noop or fail on Windows, and require root to change to anything\n  # but the current uid/gid (which is a noop).\n\n  # Runs block setting euid and egid if provided then restoring original ids.\n  # If running on Windows or without root, the block will be run with the\n  # current euid/egid.\n  def asuser(new_uid = nil, new_gid = nil)\n    return yield if Puppet::Util::Platform.windows?\n    return yield unless root?\n    return yield unless new_uid or new_gid\n\n    old_euid = euid\n    old_egid = egid\n    begin\n      change_privileges(new_uid, new_gid, false)\n\n      yield\n    ensure\n      change_privileges(new_uid ? old_euid : nil, old_egid, false)\n    end\n  end\n  module_function :asuser\n\n  # If `permanently` is set, will permanently change the uid/gid of the\n  # process. If not, it will only set the euid/egid. If only uid is supplied,\n  # the primary group of the supplied gid will be used. If only gid is\n  # supplied, only gid will be changed. This method will fail if used on\n  # Windows.\n  def change_privileges(uid = nil, gid = nil, permanently = false)\n    return unless uid or gid\n\n    unless gid\n      uid = convert_xid(:uid, uid)\n      gid = Etc.getpwuid(uid).gid\n    end\n\n    change_group(gid, permanently)\n    change_user(uid, permanently) if uid\n  end\n  module_function :change_privileges\n\n  # Changes the egid of the process if `permanently` is not set, otherwise\n  # changes gid. This method will fail if used on Windows, or attempting to\n  # change to a different gid without root.\n  def change_group(group, permanently = false)\n    gid = convert_xid(:gid, group)\n    raise Puppet::Error, _(\"No such group %{group}\") % { group: group } unless gid\n\n    return if Process.egid == gid\n\n    if permanently\n      Process::GID.change_privilege(gid)\n    else\n      Process.egid = gid\n    end\n  end\n  module_function :change_group\n\n  # As change_group, but operates on uids. If changing user permanently,\n  # supplementary groups will be set the to default groups for the new uid.\n  def change_user(user, permanently = false)\n    uid = convert_xid(:uid, user)\n    raise Puppet::Error, _(\"No such user %{user}\") % { user: user } unless uid\n\n    return if Process.euid == uid\n\n    if permanently\n      # If changing uid, we must be root. So initgroups first here.\n      initgroups(uid)\n\n      Process::UID.change_privilege(uid)\n    elsif Process.euid == 0\n      # We must be root to initgroups, so initgroups before dropping euid if\n      # we're root, otherwise elevate euid before initgroups.\n      # change euid (to root) first.\n      initgroups(uid)\n      Process.euid = uid\n    else\n      Process.euid = uid\n      initgroups(uid)\n    end\n  end\n  module_function :change_user\n\n  # Make sure the passed argument is a number.\n  def convert_xid(type, id)\n    return id if id.is_a? Integer\n\n    map = { :gid => :group, :uid => :user }\n    raise ArgumentError, _(\"Invalid id type %{type}\") % { type: type } unless map.include?(type)\n\n    ret = Puppet::Util.send(type, id)\n    if ret.nil?\n      raise Puppet::Error, _(\"Invalid %{klass}: %{id}\") % { klass: map[type], id: id }\n    end\n\n    ret\n  end\n  module_function :convert_xid\n\n  # Initialize primary and supplemental groups to those of the target user.  We\n  # take the UID and manually look up their details in the system database,\n  # including username and primary group. This method will fail on Windows, or\n  # if used without root to initgroups of another user.\n  def initgroups(uid)\n    pwent = Etc.getpwuid(uid)\n    Process.initgroups(pwent.name, pwent.gid)\n  end\n\n  module_function :initgroups\nend\n"
  },
  {
    "path": "lib/puppet/util/symbolic_file_mode.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util'\n\nmodule Puppet\nmodule Util\nmodule SymbolicFileMode\n  SetUIDBit = ReadBit  = 4\n  SetGIDBit = WriteBit = 2\n  StickyBit = ExecBit  = 1\n  SymbolicMode = { 'x' => ExecBit, 'w' => WriteBit, 'r' => ReadBit }\n  SymbolicSpecialToBit = {\n    't' => { 'u' => StickyBit, 'g' => StickyBit, 'o' => StickyBit },\n    's' => { 'u' => SetUIDBit, 'g' => SetGIDBit, 'o' => StickyBit }\n  }\n\n  def valid_symbolic_mode?(value)\n    value = normalize_symbolic_mode(value)\n    return true if value =~ /^0?[0-7]{1,4}$/\n    return true if value =~ /^([ugoa]*[-=+][-=+rstwxXugo]*)(,[ugoa]*[-=+][-=+rstwxXugo]*)*$/\n\n    false\n  end\n\n  def display_mode(value)\n    if value =~ /^0?[0-7]{1,4}$/\n      value.rjust(4, \"0\")\n    else\n      value\n    end\n  end\n\n  def normalize_symbolic_mode(value)\n    return nil if value.nil?\n\n    # We need to treat integers as octal numbers.\n    #\n    # \"A numeric mode is from one to four octal digits (0-7), derived by adding\n    # up the bits with values 4, 2, and 1. Omitted digits are assumed to be\n    # leading zeros.\"\n    case value\n    when Numeric\n      value.to_s(8)\n    when /^0?[0-7]{1,4}$/\n      value.to_i(8).to_s(8) # strip leading 0's\n    else\n      value\n    end\n  end\n\n  def symbolic_mode_to_int(modification, to_mode = 0, is_a_directory = false)\n    if modification.nil? or modification == ''\n      raise Puppet::Error, _(\"An empty mode string is illegal\")\n    elsif modification =~ /^[0-7]+$/\n      return modification.to_i(8)\n    elsif modification =~ /^\\d+$/\n      raise Puppet::Error, _(\"Numeric modes must be in octal, not decimal!\")\n    end\n\n    fail _(\"non-numeric current mode (%{mode})\") % { mode: to_mode.inspect } unless to_mode.is_a?(Numeric)\n\n    original_mode = {\n      's' => (to_mode & 0o7000) >> 9,\n      'u' => (to_mode & 0o0700) >> 6,\n      'g' => (to_mode & 0o0070) >> 3,\n      'o' => (to_mode & 0o0007) >> 0,\n      # Are there any execute bits set in the original mode?\n      'any x?' => (to_mode & 0o0111) != 0\n    }\n    final_mode = {\n      's' => original_mode['s'],\n      'u' => original_mode['u'],\n      'g' => original_mode['g'],\n      'o' => original_mode['o'],\n    }\n\n    modification.split(/\\s*,\\s*/).each do |part|\n      _, to, dsl = /^([ugoa]*)([-+=].*)$/.match(part).to_a\n      if dsl.nil? then raise Puppet::Error, _('Missing action') end\n\n      to = \"a\" unless to and to.length > 0\n\n      # We want a snapshot of the mode before we start messing with it to\n      # make actions like 'a-g' atomic.  Various parts of the DSL refer to\n      # the original mode, the final mode, or the current snapshot of the\n      # mode, for added fun.\n      snapshot_mode = {}\n      final_mode.each { |k, v| snapshot_mode[k] = v }\n\n      to.gsub('a', 'ugo').split('').uniq.each do |who|\n        value = snapshot_mode[who]\n\n        action = '!'\n        actions = {\n          '!' => ->(_, _) { raise Puppet::Error, _('Missing operation (-, =, or +)') },\n          '=' => ->(m, v) { m | v },\n          '+' => ->(m, v) { m | v },\n          '-' => ->(m, v) { m & ~v },\n        }\n\n        dsl.split('').each do |op|\n          case op\n          when /[-+=]/\n            action = op\n            # Clear all bits, if this is assignment\n            value  = 0 if op == '='\n\n          when /[ugo]/\n            value = actions[action].call(value, snapshot_mode[op])\n\n          when /[rwx]/\n            value = actions[action].call(value, SymbolicMode[op])\n\n          when 'X'\n            # Only meaningful in combination with \"set\" actions.\n            if action != '+'\n              raise Puppet::Error, _(\"X only works with the '+' operator\")\n            end\n\n            # As per the BSD manual page, set if this is a directory, or if\n            # any execute bit is set on the original (unmodified) mode.\n            # Ignored otherwise; it is \"add if\", not \"add or clear\".\n            if is_a_directory or original_mode['any x?']\n              value = actions[action].call(value, ExecBit)\n            end\n\n          when /[st]/\n            bit = SymbolicSpecialToBit[op][who] or fail _(\"internal error\")\n            final_mode['s'] = actions[action].call(final_mode['s'], bit)\n\n          else\n            raise Puppet::Error, _('Unknown operation')\n          end\n        end\n\n        # Now, assign back the value.\n        final_mode[who] = value\n      end\n    rescue Puppet::Error => e\n      if part.inspect != modification.inspect\n        rest = \" at #{part.inspect}\"\n      else\n        rest = ''\n      end\n\n      raise Puppet::Error, _(\"%{error}%{rest} in symbolic mode %{modification}\") % { error: e, rest: rest, modification: modification.inspect }, e.backtrace\n    end\n\n    final_mode['s'] << 9 |\n      final_mode['u'] << 6 |\n      final_mode['g'] << 3 |\n      final_mode['o'] << 0\n  end\nend\nend\nend\n"
  },
  {
    "path": "lib/puppet/util/tag_set.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'set'\nrequire_relative '../../puppet/network/format_support'\n\nclass Puppet::Util::TagSet < Set\n  include Puppet::Network::FormatSupport\n\n  def self.from_yaml(yaml)\n    new(Puppet::Util::Yaml.safe_load(yaml, [Symbol]))\n  end\n\n  def to_yaml\n    @hash.keys.to_yaml\n  end\n\n  def self.from_data_hash(data)\n    new(data)\n  end\n\n  # TODO: A method named #to_data_hash should not return an array\n  def to_data_hash\n    to_a\n  end\n\n  def join(*args)\n    to_a.join(*args)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/tagging.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/tag_set'\n\nmodule Puppet::Util::Tagging\n  ValidTagRegex = /\\A[[:alnum:]_][[:alnum:]_:.-]*\\Z/u\n\n  # Add a tag to the current tag set.\n  # When a tag set is used for a scope, these tags will be added to all of\n  # the objects contained in this scope when the objects are finished.\n  #\n  def tag(*ary)\n    @tags ||= new_tags\n\n    ary.flatten.compact.each do |tag|\n      name = tag.to_s.downcase\n      # Add the tag before testing if it's valid since this means that\n      # we never need to test the same valid tag twice. This speeds things\n      # up since we get a lot of duplicates and rarely fail on bad tags\n      if @tags.add?(name)\n        # not seen before, so now we test if it is valid\n        if valid_tag?(name)\n          if split_qualified_tags?\n            # avoid adding twice by first testing if the string contains '::'\n            @tags.merge(name.split('::')) if name.include?('::')\n          end\n        else\n          @tags.delete(name)\n          fail(Puppet::ParseError, _(\"Invalid tag '%{name}'\") % { name: name })\n        end\n      end\n    end\n  end\n\n  # Add a name to the current tag set. Silently ignore names that does not\n  # represent valid tags.\n  #\n  # Use this method instead of doing this:\n  #\n  #  tag(name) if is_valid?(name)\n  #\n  # since that results in testing the same string twice\n  #\n  def tag_if_valid(name)\n    if name.is_a?(String) && valid_tag?(name)\n      name = name.downcase\n      @tags ||= new_tags\n      if @tags.add?(name) && name.include?('::')\n        @tags.merge(name.split('::'))\n      end\n    end\n  end\n\n  # Answers if this resource is tagged with at least one of the given tags.\n  #\n  # The given tags are converted to downcased strings before the match is performed.\n  #\n  # @param *tags [String] splat of tags to look for\n  # @return [Boolean] true if this instance is tagged with at least one of the provided tags\n  #\n  def tagged?(*tags)\n    raw_tagged?(tags.collect { |t| t.to_s.downcase })\n  end\n\n  # Answers if this resource is tagged with at least one of the tags given in downcased string form.\n  #\n  # The method is a faster variant of the tagged? method that does no conversion of its\n  # arguments.\n  #\n  # @param tag_array [Array[String]] array of tags to look for\n  # @return [Boolean] true if this instance is tagged with at least one of the provided tags\n  #\n  def raw_tagged?(tag_array)\n    my_tags = tags\n    !tag_array.index { |t| my_tags.include?(t) }.nil?\n  end\n\n  # Only use this method when copying known tags from one Tagging instance to another\n  def set_tags(tag_source)\n    @tags = tag_source.tags\n  end\n\n  # Return a copy of the tag list, so someone can't ask for our tags\n  # and then modify them.\n  def tags\n    @tags ||= new_tags\n    @tags.dup\n  end\n\n  # Merge tags from a tagged instance with no attempts to split, downcase\n  # or verify the tags\n  def merge_tags_from(tag_source)\n    @tags ||= new_tags\n    tag_source.merge_into(@tags)\n  end\n\n  # Merge the tags of this instance into the provide TagSet\n  def merge_into(tag_set)\n    tag_set.merge(@tags) unless @tags.nil?\n  end\n\n  def tags=(tags)\n    @tags = new_tags\n\n    return if tags.nil?\n\n    tags = tags.strip.split(/\\s*,\\s*/) if tags.is_a?(String)\n    tag(*tags)\n  end\n\n  def valid_tag?(maybe_tag)\n    tag_enc = maybe_tag.encoding\n    if tag_enc == Encoding::UTF_8 || tag_enc == Encoding::ASCII\n      maybe_tag =~ ValidTagRegex\n    else\n      maybe_tag.encode(Encoding::UTF_8) =~ ValidTagRegex\n    end\n  rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError\n    false\n  end\n\n  private\n\n  def split_qualified_tags?\n    true\n  end\n\n  def new_tags\n    Puppet::Util::TagSet.new\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/terminal.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Terminal\n  # Attempts to determine the width of the terminal.  This is currently only\n  # supported on POSIX systems, and relies on the claims of `stty` (or `tput`).\n  #\n  # Inspired by code from Thor; thanks wycats!\n  # @return [Number] The column width of the terminal.  Defaults to 80 columns.\n  def self.width\n    if Puppet.features.posix?\n      result = %x(stty size 2>/dev/null).split[1] ||\n               %x(tput cols 2>/dev/null).split[0]\n    end\n    (result || '80').to_i\n  rescue\n    80\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/user_attr.rb",
    "content": "# frozen_string_literal: true\n\nclass UserAttr\n  def self.get_attributes_by_name(name)\n    attributes = nil\n\n    File.readlines('/etc/user_attr').each do |line|\n      next if line =~ /^#/\n\n      token = line.split(':')\n\n      next unless token[0] == name\n\n      attributes = { :name => name }\n      token[4].split(';').each do |attr|\n        key_value = attr.split('=')\n        attributes[key_value[0].intern] = key_value[1].strip\n      end\n      break\n    end\n    attributes\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/warnings.rb",
    "content": "# frozen_string_literal: true\n\n# Methods to help with handling warnings.\nmodule Puppet::Util::Warnings\n  module_function\n\n  def notice_once(msg)\n    Puppet::Util::Warnings.maybe_log(msg, self.class) { Puppet.notice msg }\n  end\n\n  def debug_once(msg)\n    return nil unless Puppet[:debug]\n\n    Puppet::Util::Warnings.maybe_log(msg, self.class) { Puppet.debug msg }\n  end\n\n  def warnonce(msg)\n    Puppet::Util::Warnings.maybe_log(msg, self.class) { Puppet.warning msg }\n  end\n\n  def clear_warnings\n    @stampwarnings = {}\n    nil\n  end\n\n  def self.maybe_log(message, klass)\n    @stampwarnings ||= {}\n    @stampwarnings[klass] ||= []\n    return nil if @stampwarnings[klass].include? message\n\n    yield\n    @stampwarnings[klass] << message\n    nil\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/watched_file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/watcher'\n\n# Monitor a given file for changes on a periodic interval. Changes are detected\n# by looking for a change in the file ctime.\nclass Puppet::Util::WatchedFile\n  # @!attribute [r] filename\n  #   @return [String] The fully qualified path to the file.\n  attr_reader :filename\n\n  # @param filename [String] The fully qualified path to the file.\n  # @param timer [Puppet::Util::Watcher::Timer] The polling interval for checking for file\n  #   changes. Setting the timeout to a negative value will treat the file as\n  #   always changed. Defaults to `Puppet[:filetimeout]`\n  def initialize(filename, timer = Puppet::Util::Watcher::Timer.new(Puppet[:filetimeout]))\n    @filename = filename\n    @timer = timer\n\n    @info = Puppet::Util::Watcher::PeriodicWatcher.new(\n      Puppet::Util::Watcher::Common.file_ctime_change_watcher(@filename),\n      timer\n    )\n  end\n\n  # @return [true, false] If the file has changed since it was last checked.\n  def changed?\n    @info.changed?\n  end\n\n  # Allow this to be used as the name of the file being watched in various\n  # other methods (such as Puppet::FileSystem.exist?)\n  def to_str\n    @filename\n  end\n\n  def to_s\n    \"<WatchedFile: filename = #{@filename}, timeout = #{@timer.timeout}>\"\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/watcher/change_watcher.rb",
    "content": "# frozen_string_literal: true\n\n# Watches for changes over time. It only re-examines the values when it is requested to update readings.\n# @api private\nclass Puppet::Util::Watcher::ChangeWatcher\n  def self.watch(reader)\n    Puppet::Util::Watcher::ChangeWatcher.new(nil, nil, reader).next_reading\n  end\n\n  def initialize(previous, current, value_reader)\n    @previous = previous\n    @current = current\n    @value_reader = value_reader\n  end\n\n  def changed?\n    if uncertain?\n      false\n    else\n      @previous != @current\n    end\n  end\n\n  def uncertain?\n    @previous.nil? || @current.nil?\n  end\n\n  def change_current_reading_to(new_value)\n    Puppet::Util::Watcher::ChangeWatcher.new(@current, new_value, @value_reader)\n  end\n\n  def next_reading\n    change_current_reading_to(@value_reader.call)\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/watcher/periodic_watcher.rb",
    "content": "# frozen_string_literal: true\n\n# Monitor a given watcher for changes on a periodic interval.\nclass Puppet::Util::Watcher::PeriodicWatcher\n  # @param watcher [Puppet::Util::Watcher::ChangeWatcher] a watcher for the value to watch\n  # @param timer [Puppet::Util::Watcher::Timer] A timer to determine when to\n  #   recheck the watcher. If the timeout of the timer is negative, then the\n  #   watched value is always considered to be changed\n  def initialize(watcher, timer)\n    @watcher = watcher\n    @timer = timer\n\n    @timer.start\n  end\n\n  # @return [true, false] If the file has changed since it was last checked.\n  def changed?\n    return true if always_consider_changed?\n\n    @watcher = examine_watched_info(@watcher)\n    @watcher.changed?\n  end\n\n  private\n\n  def always_consider_changed?\n    @timer.timeout < 0\n  end\n\n  def examine_watched_info(known)\n    if @timer.expired?\n      @timer.start\n      known.next_reading\n    else\n      known\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/watcher/timer.rb",
    "content": "# frozen_string_literal: true\n\nclass Puppet::Util::Watcher::Timer\n  attr_reader :timeout\n\n  def initialize(timeout)\n    @timeout = timeout\n  end\n\n  def start\n    @start_time = now\n  end\n\n  def expired?\n    (now - @start_time) >= @timeout\n  end\n\n  def now\n    Time.now.to_i\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/watcher.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Watcher\n  require_relative 'watcher/timer'\n  require_relative 'watcher/change_watcher'\n  require_relative 'watcher/periodic_watcher'\n\n  module Common\n    def self.file_ctime_change_watcher(filename)\n      Puppet::Util::Watcher::ChangeWatcher.watch(lambda do\n        Puppet::FileSystem.stat(filename).ctime\n      rescue Errno::ENOENT, Errno::ENOTDIR\n        :absent\n      end)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/access_control_entry.rb",
    "content": "# frozen_string_literal: true\n\n# Windows Access Control Entry\n#\n# Represents an access control entry, which grants or denies a subject,\n# identified by a SID, rights to a securable object.\n#\n# @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa374868(v=vs.85).aspx\n# @api private\nclass Puppet::Util::Windows::AccessControlEntry\n  require_relative '../../../puppet/util/windows/security'\n  include Puppet::Util::Windows::SID\n\n  attr_accessor :sid\n  attr_reader :mask, :flags, :type\n\n  OBJECT_INHERIT_ACE                      = 0x1\n  CONTAINER_INHERIT_ACE                   = 0x2\n  NO_PROPAGATE_INHERIT_ACE                = 0x4\n  INHERIT_ONLY_ACE                        = 0x8\n  INHERITED_ACE                           = 0x10\n\n  ACCESS_ALLOWED_ACE_TYPE                 = 0x0\n  ACCESS_DENIED_ACE_TYPE                  = 0x1\n\n  def initialize(sid, mask, flags = 0, type = ACCESS_ALLOWED_ACE_TYPE)\n    @sid = sid\n    @mask = mask\n    @flags = flags\n    @type = type\n  end\n\n  # Returns true if this ACE is inherited from a parent. If false,\n  # then the ACE is set directly on the object to which it refers.\n  #\n  # @return [Boolean] true if the ACE is inherited\n  def inherited?\n    (@flags & INHERITED_ACE) == INHERITED_ACE\n  end\n\n  # Returns true if this ACE only applies to children of the object.\n  # If false, it applies to the object.\n  #\n  # @return [Boolean] true if the ACE only applies to children and\n  # not the object itself.\n  def inherit_only?\n    (@flags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE\n  end\n\n  # Returns true if this ACE applies to child directories.\n  #\n  # @return [Boolean] true if the ACE applies to child directories\n  def container_inherit?\n    (@flags & CONTAINER_INHERIT_ACE) == CONTAINER_INHERIT_ACE\n  end\n\n  # Returns true if this ACE applies to child files.\n  #\n  # @return [Boolean] true if the ACE applies to child files.\n  def object_inherit?\n    (@flags & OBJECT_INHERIT_ACE) == OBJECT_INHERIT_ACE\n  end\n\n  def inspect\n    inheritance = ''.dup\n    inheritance << '(I)' if inherited?\n    inheritance << '(OI)' if object_inherit?\n    inheritance << '(CI)' if container_inherit?\n    inheritance << '(IO)' if inherit_only?\n\n    left = \"#{sid_to_name(sid)}:#{inheritance}\"\n    left = left.ljust(45)\n    \"#{left} 0x#{mask.to_s(16)}\"\n  end\n\n  # Returns true if this ACE is equal to +other+\n  def ==(other)\n    self.class == other.class &&\n      sid == other.sid &&\n      mask == other.mask &&\n      flags == other.flags &&\n      type == other.type\n  end\n\n  alias eql? ==\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/access_control_list.rb",
    "content": "# frozen_string_literal: true\n\n# Windows Access Control List\n#\n# Represents a list of access control entries (ACEs).\n#\n# @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa374872(v=vs.85).aspx\n# @api private\nclass Puppet::Util::Windows::AccessControlList\n  include Enumerable\n\n  ACCESS_ALLOWED_ACE_TYPE                 = 0x0\n  ACCESS_DENIED_ACE_TYPE                  = 0x1\n\n  # Construct an ACL.\n  #\n  # @param acl [Enumerable] A list of aces to copy from.\n  def initialize(acl = nil)\n    if acl\n      @aces = acl.map(&:dup)\n    else\n      @aces = []\n    end\n  end\n\n  # Enumerate each ACE in the list.\n  #\n  # @yieldparam ace [Hash] the ace\n  def each\n    @aces.each { |ace| yield ace }\n  end\n\n  # Allow the +sid+ to access a resource with the specified access +mask+.\n  #\n  # @param sid [String] The SID that the ACE is granting access to\n  # @param mask [int] The access mask granted to the SID\n  # @param flags [int] The flags assigned to the ACE, e.g. +INHERIT_ONLY_ACE+\n  def allow(sid, mask, flags = 0)\n    @aces << Puppet::Util::Windows::AccessControlEntry.new(sid, mask, flags, ACCESS_ALLOWED_ACE_TYPE)\n  end\n\n  # Deny the +sid+ access to a resource with the specified access +mask+.\n  #\n  # @param sid [String] The SID that the ACE is denying access to\n  # @param mask [int] The access mask denied to the SID\n  # @param flags [int] The flags assigned to the ACE, e.g. +INHERIT_ONLY_ACE+\n  def deny(sid, mask, flags = 0)\n    @aces << Puppet::Util::Windows::AccessControlEntry.new(sid, mask, flags, ACCESS_DENIED_ACE_TYPE)\n  end\n\n  # Reassign all ACEs currently assigned to +old_sid+ to +new_sid+ instead.\n  # If an ACE is inherited or is not assigned to +old_sid+, then it will\n  # be copied as-is to the new ACL, preserving its order within the ACL.\n  #\n  # @param old_sid [String] The old SID, e.g. 'S-1-5-18'\n  # @param new_sid [String] The new SID\n  # @return [AccessControlList] The copied ACL.\n  def reassign!(old_sid, new_sid)\n    new_aces = []\n    prepend_needed = false\n    aces_to_prepend = []\n\n    @aces.each do |ace|\n      new_ace = ace.dup\n\n      if ace.sid == old_sid\n        if ace.inherited?\n          # create an explicit ACE granting or denying the\n          # new_sid the rights that the inherited ACE\n          # granted or denied the old_sid. We mask off all\n          # flags except those affecting inheritance of the\n          # ACE we're creating.\n          inherit_mask = Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE |\n                         Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |\n                         Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE\n          explicit_ace = Puppet::Util::Windows::AccessControlEntry.new(new_sid, ace.mask, ace.flags & inherit_mask, ace.type)\n          aces_to_prepend << explicit_ace\n        else\n          new_ace.sid = new_sid\n\n          prepend_needed = old_sid == Puppet::Util::Windows::SID::LocalSystem\n        end\n      end\n      new_aces << new_ace\n    end\n\n    @aces = []\n\n    if prepend_needed\n      mask = Puppet::Util::Windows::File::STANDARD_RIGHTS_ALL | Puppet::Util::Windows::File::SPECIFIC_RIGHTS_ALL\n      ace = Puppet::Util::Windows::AccessControlEntry.new(\n        Puppet::Util::Windows::SID::LocalSystem,\n        mask\n      )\n      @aces << ace\n    end\n\n    @aces.concat(aces_to_prepend)\n    @aces.concat(new_aces)\n  end\n\n  def inspect\n    str = ''.dup\n    @aces.each do |ace|\n      str << \"  #{ace.inspect}\\n\"\n    end\n    str\n  end\n\n  def ==(other)\n    self.class == other.class &&\n      to_a == other.to_a\n  end\n\n  alias eql? ==\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/adsi.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet::Util::Windows::ADSI\n  require 'ffi'\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/dsrole/ne-dsrole-dsrole_machine_role\n  STANDALONE_WORKSTATION = 0\n  MEMBER_WORKSTATION = 1\n  STANDALONE_SERVER = 2\n  MEMBER_SERVER = 3\n  BACKUP_DOMAIN_CONTROLLER = 4\n  PRIMARY_DOMAIN_CONTROLLER = 5\n\n  DOMAIN_ROLES = {\n    STANDALONE_WORKSTATION => :STANDALONE_WORKSTATION,\n    MEMBER_WORKSTATION => :MEMBER_WORKSTATION,\n    STANDALONE_SERVER => :STANDALONE_SERVER,\n    MEMBER_SERVER => :MEMBER_SERVER,\n    BACKUP_DOMAIN_CONTROLLER => :BACKUP_DOMAIN_CONTROLLER,\n    PRIMARY_DOMAIN_CONTROLLER => :PRIMARY_DOMAIN_CONTROLLER,\n  }\n\n  class << self\n    extend FFI::Library\n\n    def connectable?(uri)\n      !!connect(uri)\n    rescue\n      false\n    end\n\n    def connect(uri)\n      WIN32OLE.connect(uri)\n    rescue WIN32OLERuntimeError => e\n      raise Puppet::Error.new(_(\"ADSI connection error: %{e}\") % { e: e }, e)\n    end\n\n    def create(name, resource_type)\n      Puppet::Util::Windows::ADSI.connect(computer_uri).Create(resource_type, name)\n    end\n\n    def delete(name, resource_type)\n      Puppet::Util::Windows::ADSI.connect(computer_uri).Delete(resource_type, name)\n    end\n\n    # taken from winbase.h\n    MAX_COMPUTERNAME_LENGTH = 31\n\n    def computer_name\n      unless @computer_name\n        max_length = MAX_COMPUTERNAME_LENGTH + 1 # NULL terminated\n        FFI::MemoryPointer.new(max_length * 2) do |buffer| # wide string\n          FFI::MemoryPointer.new(:dword, 1) do |buffer_size|\n            buffer_size.write_dword(max_length) # length in TCHARs\n\n            if GetComputerNameW(buffer, buffer_size) == FFI::WIN32_FALSE\n              raise Puppet::Util::Windows::Error, _(\"Failed to get computer name\")\n            end\n\n            @computer_name = buffer.read_wide_string(buffer_size.read_dword)\n          end\n        end\n      end\n      @computer_name\n    end\n\n    def computer_uri(host = '.')\n      \"WinNT://#{host}\"\n    end\n\n    def wmi_resource_uri(host = '.')\n      \"winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2\"\n    end\n\n    # This method should *only* be used to generate WinNT://<SID> style monikers\n    # used for IAdsGroup::Add / IAdsGroup::Remove.  These URIs are not usable\n    # to resolve an account with WIN32OLE.connect\n    # Valid input is a SID::Principal, S-X-X style SID string or any valid\n    # account name with or without domain prefix\n    # @api private\n    def sid_uri_safe(sid)\n      return sid_uri(sid) if sid.is_a?(Puppet::Util::Windows::SID::Principal)\n\n      begin\n        sid = Puppet::Util::Windows::SID.name_to_principal(sid)\n        sid_uri(sid)\n      rescue Puppet::Util::Windows::Error, Puppet::Error\n        nil\n      end\n    end\n\n    # This method should *only* be used to generate WinNT://<SID> style monikers\n    # used for IAdsGroup::Add / IAdsGroup::Remove.  These URIs are not useable\n    # to resolve an account with WIN32OLE.connect\n    def sid_uri(sid)\n      raise Puppet::Error, _(\"Must use a valid SID::Principal\") unless sid.is_a?(Puppet::Util::Windows::SID::Principal)\n\n      \"WinNT://#{sid.sid}\"\n    end\n\n    def uri(resource_name, resource_type, host = '.')\n      \"#{computer_uri(host)}/#{resource_name},#{resource_type}\"\n    end\n\n    def wmi_connection\n      connect(wmi_resource_uri)\n    end\n\n    def execquery(query)\n      wmi_connection.execquery(query)\n    end\n\n    def domain_role\n      unless @domain_role\n        query_result = Puppet::Util::Windows::ADSI.execquery('select DomainRole from Win32_ComputerSystem').to_enum.first\n        @domain_role = DOMAIN_ROLES[query_result.DomainRole] if query_result\n      end\n      @domain_role\n    end\n\n    ffi_convention :stdcall\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724295(v=vs.85).aspx\n    # BOOL WINAPI GetComputerName(\n    #   _Out_    LPTSTR lpBuffer,\n    #   _Inout_  LPDWORD lpnSize\n    # );\n    ffi_lib :kernel32\n    attach_function_private :GetComputerNameW,\n                            [:lpwstr, :lpdword], :win32_bool\n  end\n\n  # Common base class shared by the User and Group\n  # classes below.\n  class ADSIObject\n    extend Enumerable\n\n    # Define some useful class-level methods\n    class << self\n      # Is either 'user' or 'group'\n      attr_reader :object_class\n\n      def localized_domains\n        @localized_domains ||= [\n          # localized version of BUILTIN\n          # for instance VORDEFINIERT on German Windows\n          Puppet::Util::Windows::SID.sid_to_name('S-1-5-32').upcase,\n          # localized version of NT AUTHORITY (can't use S-1-5)\n          # for instance AUTORITE NT on French Windows\n          Puppet::Util::Windows::SID.name_to_principal('SYSTEM').domain.upcase\n        ]\n      end\n\n      def uri(name, host = '.')\n        host = '.' if (localized_domains << Socket.gethostname.upcase).include?(host.upcase)\n        Puppet::Util::Windows::ADSI.uri(name, @object_class, host)\n      end\n\n      def parse_name(name)\n        if name =~ %r{/}\n          raise Puppet::Error, _(\"Value must be in DOMAIN\\\\%{object_class} style syntax\") % { object_class: @object_class }\n        end\n\n        matches = name.scan(/((.*)\\\\)?(.*)/)\n        domain = matches[0][1] || '.'\n        account = matches[0][2]\n\n        [account, domain]\n      end\n\n      # returns Puppet::Util::Windows::SID::Principal[]\n      # may contain objects that represent unresolvable SIDs\n      def get_sids(adsi_child_collection)\n        sids = []\n        adsi_child_collection.each do |m|\n          sids << Puppet::Util::Windows::SID.ads_to_principal(m)\n        rescue Puppet::Util::Windows::Error => e\n          case e.code\n          when Puppet::Util::Windows::SID::ERROR_TRUSTED_RELATIONSHIP_FAILURE, Puppet::Util::Windows::SID::ERROR_TRUSTED_DOMAIN_FAILURE\n            sids << Puppet::Util::Windows::SID.unresolved_principal(m.name, m.sid)\n          else\n            raise e\n          end\n        end\n\n        sids\n      end\n\n      def name_sid_hash(names, allow_unresolved = false)\n        return {} if names.nil? || names.empty?\n\n        sids = names.map do |name|\n          sid = Puppet::Util::Windows::SID.name_to_principal(name, allow_unresolved)\n          raise Puppet::Error, _(\"Could not resolve name: %{name}\") % { name: name } unless sid\n\n          [sid.sid, sid]\n        end\n\n        sids.to_h\n      end\n\n      def delete(name)\n        Puppet::Util::Windows::ADSI.delete(name, @object_class)\n      end\n\n      def exists?(name_or_sid)\n        well_known = false\n        if (sid = Puppet::Util::Windows::SID.name_to_principal(name_or_sid))\n          # Examples of SidType include SidTypeUser, SidTypeGroup\n          if sid.account_type == \"SidType#{@object_class.capitalize}\".to_sym\n            # Check if we're getting back a local user when domain-joined\n            return true unless [:MEMBER_WORKSTATION, :MEMBER_SERVER].include?(Puppet::Util::Windows::ADSI.domain_role)\n\n            # The resource domain and the computer name are not always case-matching\n            return sid.domain.casecmp(Puppet::Util::Windows::ADSI.computer_name) == 0\n          end\n\n          # 'well known group' is special as it can be a group like Everyone OR a user like SYSTEM\n          # so try to resolve it\n          # https://msdn.microsoft.com/en-us/library/cc234477.aspx\n          well_known = sid.account_type == :SidTypeWellKnownGroup\n          return false if sid.account_type != :SidTypeAlias && !well_known\n\n          name_or_sid = \"#{sid.domain}\\\\#{sid.account}\"\n        end\n\n        object = Puppet::Util::Windows::ADSI.connect(uri(*parse_name(name_or_sid)))\n        object.Class.downcase == @object_class\n      rescue\n        # special accounts like SYSTEM or special groups like Authenticated Users cannot\n        # resolve via monikers like WinNT://./SYSTEM,user or WinNT://./Authenticated Users,group\n        # -- they'll fail to connect. thus, given a validly resolved SID, this failure is\n        # ambiguous as it may indicate either a group like Service or an account like SYSTEM\n        well_known\n      end\n\n      def list_all\n        raise NotImplementedError, _(\"Subclass must implement class-level method 'list_all'!\")\n      end\n\n      def each(&block)\n        objects = []\n        list_all.each do |o|\n          # Setting WIN32OLE.codepage in the microsoft_windows feature ensures\n          # values are returned as UTF-8\n          objects << new(o.name)\n        end\n\n        objects.each(&block)\n      end\n    end\n\n    attr_reader :name\n\n    def initialize(name, native_object = nil)\n      @name = name\n      @native_object = native_object\n    end\n\n    def object_class\n      self.class.object_class\n    end\n\n    def uri\n      self.class.uri(sid.account, sid.domain)\n    end\n\n    def native_object\n      @native_object ||= Puppet::Util::Windows::ADSI.connect(self.class.uri(*self.class.parse_name(name)))\n    end\n\n    def sid\n      @sid ||= Puppet::Util::Windows::SID.octet_string_to_principal(native_object.objectSID)\n    end\n\n    def [](attribute)\n      # Setting WIN32OLE.codepage ensures values are returned as UTF-8\n      native_object.Get(attribute)\n    end\n\n    def []=(attribute, value)\n      native_object.Put(attribute, value)\n    end\n\n    def commit\n      begin\n        native_object.SetInfo\n      rescue WIN32OLERuntimeError => e\n        # ERROR_BAD_USERNAME 2202L from winerror.h\n        if e.message =~ /8007089A/m\n          raise Puppet::Error, _(\"Puppet is not able to create/delete domain %{object_class} objects with the %{object_class} resource.\") % { object_class: object_class }\n        end\n\n        raise Puppet::Error.new(_(\"%{object_class} update failed: %{error}\") % { object_class: object_class.capitalize, error: e }, e)\n      end\n      self\n    end\n  end\n\n  class User < ADSIObject\n    extend FFI::Library\n\n    require_relative '../../../puppet/util/windows/sid'\n\n    # https://msdn.microsoft.com/en-us/library/aa746340.aspx\n    # IADsUser interface\n    @object_class = 'user'\n\n    class << self\n      def list_all\n        Puppet::Util::Windows::ADSI.execquery('select name from win32_useraccount where localaccount = \"TRUE\"')\n      end\n\n      def logon(name, password)\n        Puppet::Util::Windows::User.password_is?(name, password)\n      end\n\n      def create(name)\n        # Windows error 1379: The specified local group already exists.\n        raise Puppet::Error, _(\"Cannot create user if group '%{name}' exists.\") % { name: name } if Puppet::Util::Windows::ADSI::Group.exists? name\n\n        new(name, Puppet::Util::Windows::ADSI.create(name, @object_class))\n      end\n    end\n\n    def password_is?(password)\n      self.class.logon(name, password)\n    end\n\n    def add_flag(flag_name, value)\n      flag = begin\n        native_object.Get(flag_name)\n      rescue\n        0\n      end\n\n      native_object.Put(flag_name, flag | value)\n\n      commit\n    end\n\n    def password=(password)\n      unless password.nil?\n        native_object.SetPassword(password)\n        commit\n      end\n\n      fADS_UF_DONT_EXPIRE_PASSWD = 0x10000\n      add_flag(\"UserFlags\", fADS_UF_DONT_EXPIRE_PASSWD)\n    end\n\n    def groups\n      # https://msdn.microsoft.com/en-us/library/aa746342.aspx\n      # WIN32OLE objects aren't enumerable, so no map\n      groups = []\n      # Setting WIN32OLE.codepage ensures values are returned as UTF-8\n      begin\n        native_object.Groups.each { |g| groups << g.Name }\n      rescue\n        nil\n      end\n      groups\n    end\n\n    def add_to_groups(*group_names)\n      group_names.each do |group_name|\n        Puppet::Util::Windows::ADSI::Group.new(group_name).add_member_sids(sid)\n      end\n    end\n    alias add_to_group add_to_groups\n\n    def remove_from_groups(*group_names)\n      group_names.each do |group_name|\n        Puppet::Util::Windows::ADSI::Group.new(group_name).remove_member_sids(sid)\n      end\n    end\n    alias remove_from_group remove_from_groups\n\n    def add_group_sids(*sids)\n      group_names = sids.map(&:domain_account)\n      add_to_groups(*group_names)\n    end\n\n    def remove_group_sids(*sids)\n      group_names = sids.map(&:domain_account)\n      remove_from_groups(*group_names)\n    end\n\n    def group_sids\n      self.class.get_sids(native_object.Groups)\n    end\n\n    # TODO: This code's pretty similar to set_members in the Group class. Would be nice\n    # to refactor them into the ADSIObject class at some point. This was not done originally\n    # because these use different methods to do stuff that are also aliased to other methods,\n    # so the shared code isn't exactly a 1:1 mapping.\n    def set_groups(desired_groups, minimum = true)\n      return if desired_groups.nil?\n\n      desired_groups = desired_groups.split(',').map(&:strip)\n\n      current_hash = group_sids.to_h { |sid| [sid.sid, sid] }\n      desired_hash = self.class.name_sid_hash(desired_groups)\n\n      # First we add the user to all the groups it should be in but isn't\n      unless desired_groups.empty?\n        groups_to_add = (desired_hash.keys - current_hash.keys).map { |sid| desired_hash[sid] }\n        add_group_sids(*groups_to_add)\n      end\n\n      # Then we remove the user from all groups it is in but shouldn't be, if\n      # that's been requested\n      unless minimum\n        if desired_hash.empty?\n          groups_to_remove = current_hash.values\n        else\n          groups_to_remove = (current_hash.keys - desired_hash.keys).map { |sid| current_hash[sid] }\n        end\n\n        remove_group_sids(*groups_to_remove)\n      end\n    end\n\n    # Declare all of the available user flags on the system. Note that\n    # ADS_UF is read as ADS_UserFlag\n    #   https://docs.microsoft.com/en-us/windows/desktop/api/iads/ne-iads-ads_user_flag\n    # and\n    #   https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro\n    # for the flag values.\n    ADS_USERFLAGS = {\n      ADS_UF_SCRIPT: 0x0001,\n      ADS_UF_ACCOUNTDISABLE: 0x0002,\n      ADS_UF_HOMEDIR_REQUIRED: 0x0008,\n      ADS_UF_LOCKOUT: 0x0010,\n      ADS_UF_PASSWD_NOTREQD: 0x0020,\n      ADS_UF_PASSWD_CANT_CHANGE: 0x0040,\n      ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED: 0x0080,\n      ADS_UF_TEMP_DUPLICATE_ACCOUNT: 0x0100,\n      ADS_UF_NORMAL_ACCOUNT: 0x0200,\n      ADS_UF_INTERDOMAIN_TRUST_ACCOUNT: 0x0800,\n      ADS_UF_WORKSTATION_TRUST_ACCOUNT: 0x1000,\n      ADS_UF_SERVER_TRUST_ACCOUNT: 0x2000,\n      ADS_UF_DONT_EXPIRE_PASSWD: 0x10000,\n      ADS_UF_MNS_LOGON_ACCOUNT: 0x20000,\n      ADS_UF_SMARTCARD_REQUIRED: 0x40000,\n      ADS_UF_TRUSTED_FOR_DELEGATION: 0x80000,\n      ADS_UF_NOT_DELEGATED: 0x100000,\n      ADS_UF_USE_DES_KEY_ONLY: 0x200000,\n      ADS_UF_DONT_REQUIRE_PREAUTH: 0x400000,\n      ADS_UF_PASSWORD_EXPIRED: 0x800000,\n      ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 0x1000000\n    }\n\n    def userflag_set?(flag)\n      flag_value = ADS_USERFLAGS[flag] || 0\n      !(self['UserFlags'] & flag_value).zero?\n    end\n\n    # Common helper for set_userflags and unset_userflags.\n    #\n    # @api private\n    def op_userflags(*flags, &block)\n      # Avoid an unnecessary set + commit operation.\n      return if flags.empty?\n\n      unrecognized_flags = flags.reject { |flag| ADS_USERFLAGS.keys.include?(flag) }\n      unless unrecognized_flags.empty?\n        raise ArgumentError, _(\"Unrecognized ADS UserFlags: %{unrecognized_flags}\") % { unrecognized_flags: unrecognized_flags.join(', ') }\n      end\n\n      self['UserFlags'] = flags.inject(self['UserFlags'], &block)\n    end\n\n    def set_userflags(*flags)\n      op_userflags(*flags) { |userflags, flag| userflags | ADS_USERFLAGS[flag] }\n    end\n\n    def unset_userflags(*flags)\n      op_userflags(*flags) { |userflags, flag| userflags & ~ADS_USERFLAGS[flag] }\n    end\n\n    def disabled?\n      userflag_set?(:ADS_UF_ACCOUNTDISABLE)\n    end\n\n    def locked_out?\n      # Note that the LOCKOUT flag is known to be inaccurate when using the\n      # LDAP IADsUser provider, but this class consistently uses the WinNT\n      # provider, which is expected to be accurate.\n      userflag_set?(:ADS_UF_LOCKOUT)\n    end\n\n    def expired?\n      expires = native_object.Get('AccountExpirationDate')\n      expires && expires < Time.now\n    rescue WIN32OLERuntimeError => e\n      # This OLE error code indicates the property can't be found in the cache\n      raise e unless e.message =~ /8000500D/m\n\n      false\n    end\n\n    # UNLEN from lmcons.h - https://stackoverflow.com/a/2155176\n    MAX_USERNAME_LENGTH = 256\n    def self.current_user_name\n      user_name = ''.dup\n      max_length = MAX_USERNAME_LENGTH + 1 # NULL terminated\n      FFI::MemoryPointer.new(max_length * 2) do |buffer| # wide string\n        FFI::MemoryPointer.new(:dword, 1) do |buffer_size|\n          buffer_size.write_dword(max_length) # length in TCHARs\n\n          if GetUserNameW(buffer, buffer_size) == FFI::WIN32_FALSE\n            raise Puppet::Util::Windows::Error, _(\"Failed to get user name\")\n          end\n\n          # buffer_size includes trailing NULL\n          user_name = buffer.read_wide_string(buffer_size.read_dword - 1)\n        end\n      end\n\n      user_name\n    end\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/secext/ne-secext-extended_name_format\n    NameUnknown           = 0\n    NameFullyQualifiedDN  = 1\n    NameSamCompatible     = 2\n    NameDisplay           = 3\n    NameUniqueId          = 6\n    NameCanonical         = 7\n    NameUserPrincipal     = 8\n    NameCanonicalEx       = 9\n    NameServicePrincipal  = 10\n    NameDnsDomain         = 12\n    NameGivenName         = 13\n    NameSurname           = 14\n\n    def self.current_user_name_with_format(format)\n      user_name = ''.dup\n      max_length = 1024\n\n      FFI::MemoryPointer.new(:lpwstr, max_length * 2 + 1) do |buffer|\n        FFI::MemoryPointer.new(:dword, 1) do |buffer_size|\n          buffer_size.write_dword(max_length + 1)\n\n          if GetUserNameExW(format.to_i, buffer, buffer_size) == FFI::WIN32_FALSE\n            raise Puppet::Util::Windows::Error.new(_(\"Failed to get user name\"), FFI.errno)\n          end\n\n          user_name = buffer.read_wide_string(buffer_size.read_dword).chomp\n        end\n      end\n\n      user_name\n    end\n\n    def self.current_sam_compatible_user_name\n      current_user_name_with_format(NameSamCompatible)\n    end\n\n    def self.current_user_sid\n      Puppet::Util::Windows::SID.name_to_principal(current_user_name)\n    end\n\n    ffi_convention :stdcall\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724432(v=vs.85).aspx\n    # BOOL WINAPI GetUserName(\n    #   _Out_    LPTSTR lpBuffer,\n    #   _Inout_  LPDWORD lpnSize\n    # );\n    ffi_lib :advapi32\n    attach_function_private :GetUserNameW,\n                            [:lpwstr, :lpdword], :win32_bool\n\n    # https://docs.microsoft.com/en-us/windows/win32/api/secext/nf-secext-getusernameexa\n    # BOOLEAN SEC_ENTRY GetUserNameExA(\n    #   EXTENDED_NAME_FORMAT NameFormat,\n    #   LPSTR                lpNameBuffer,\n    #   PULONG               nSize\n    # );type\n    ffi_lib :secur32\n    attach_function_private :GetUserNameExW, [:uint16, :lpwstr, :pointer], :win32_bool\n  end\n\n  class UserProfile\n    def self.delete(sid)\n      Puppet::Util::Windows::ADSI.wmi_connection.Delete(\"Win32_UserProfile.SID='#{sid}'\")\n    rescue WIN32OLERuntimeError => e\n      # https://social.technet.microsoft.com/Forums/en/ITCG/thread/0f190051-ac96-4bf1-a47f-6b864bfacee5\n      # Prior to Vista SP1, there's no built-in way to programmatically\n      # delete user profiles (except for delprof.exe). So try to delete\n      # but warn if we fail\n      raise e unless e.message.include?('80041010')\n\n      Puppet.warning _(\"Cannot delete user profile for '%{sid}' prior to Vista SP1\") % { sid: sid }\n    end\n  end\n\n  class Group < ADSIObject\n    # https://msdn.microsoft.com/en-us/library/aa706021.aspx\n    # IADsGroup interface\n    @object_class = 'group'\n\n    class << self\n      def list_all\n        Puppet::Util::Windows::ADSI.execquery('select name from win32_group where localaccount = \"TRUE\"')\n      end\n\n      def create(name)\n        # Windows error 2224: The account already exists.\n        raise Puppet::Error, _(\"Cannot create group if user '%{name}' exists.\") % { name: name } if Puppet::Util::Windows::ADSI::User.exists?(name)\n\n        new(name, Puppet::Util::Windows::ADSI.create(name, @object_class))\n      end\n    end\n\n    def add_member_sids(*sids)\n      sids.each do |sid|\n        native_object.Add(Puppet::Util::Windows::ADSI.sid_uri(sid))\n      end\n    end\n\n    def remove_member_sids(*sids)\n      sids.each do |sid|\n        native_object.Remove(Puppet::Util::Windows::ADSI.sid_uri(sid))\n      end\n    end\n\n    # returns Puppet::Util::Windows::SID::Principal[]\n    # may contain objects that represent unresolvable SIDs\n    # qualified account names are returned by calling #domain_account\n    def members\n      self.class.get_sids(native_object.Members)\n    end\n    alias member_sids members\n\n    def set_members(desired_members, inclusive = true)\n      return if desired_members.nil?\n\n      current_hash = member_sids.to_h { |sid| [sid.sid, sid] }\n      desired_hash = self.class.name_sid_hash(desired_members)\n\n      # First we add all missing members\n      unless desired_hash.empty?\n        members_to_add = (desired_hash.keys - current_hash.keys).map { |sid| desired_hash[sid] }\n        add_member_sids(*members_to_add)\n      end\n\n      # Then we remove all extra members if inclusive\n      if inclusive\n        if desired_hash.empty?\n          members_to_remove = current_hash.values\n        else\n          members_to_remove = (current_hash.keys - desired_hash.keys).map { |sid| current_hash[sid] }\n        end\n\n        remove_member_sids(*members_to_remove)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/com.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'ffi'\n\nmodule Puppet::Util::Windows::COM\n  extend FFI::Library\n\n  ffi_convention :stdcall\n\n  S_OK = 0\n  S_FALSE = 1\n\n  def SUCCEEDED(hr) hr >= 0 end\n  def FAILED(hr) hr < 0 end\n\n  module_function :SUCCEEDED, :FAILED\n\n  def raise_if_hresult_failed(name, *args)\n    failed = FAILED(result = send(name, *args)) and raise _(\"%{name} failed (hresult %{result}).\") % { name: name, result: format('%#08x', result) }\n\n    result\n  ensure\n    yield failed if block_given?\n  end\n\n  module_function :raise_if_hresult_failed\n\n  CLSCTX_INPROC_SERVER = 0x1\n  CLSCTX_INPROC_HANDLER = 0x2\n  CLSCTX_LOCAL_SERVER = 0x4\n  CLSCTX_INPROC_SERVER16 = 0x8\n  CLSCTX_REMOTE_SERVER = 0x10\n  CLSCTX_INPROC_HANDLER16 = 0x20\n  CLSCTX_RESERVED1 = 0x40\n  CLSCTX_RESERVED2 = 0x80\n  CLSCTX_RESERVED3 = 0x100\n  CLSCTX_RESERVED4 = 0x200\n  CLSCTX_NO_CODE_DOWNLOAD = 0x400\n  CLSCTX_RESERVED5 = 0x800\n  CLSCTX_NO_CUSTOM_MARSHAL = 0x1000\n  CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000\n  CLSCTX_NO_FAILURE_LOG = 0x4000\n  CLSCTX_DISABLE_AAA = 0x8000\n  CLSCTX_ENABLE_AAA = 0x10000\n  CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000\n  CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000\n  CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000\n  CLSCTX_ENABLE_CLOAKING = 0x100000\n  CLSCTX_PS_DLL = -0x80000000\n  CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER\n  CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER\n  CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx\n  # HRESULT CoCreateInstance(\n  #   _In_   REFCLSID rclsid,\n  #   _In_   LPUNKNOWN pUnkOuter,\n  #   _In_   DWORD dwClsContext,\n  #   _In_   REFIID riid,\n  #   _Out_  LPVOID *ppv\n  # );\n  ffi_lib :ole32\n  attach_function_private :CoCreateInstance,\n                          [:pointer, :lpunknown, :dword, :pointer, :lpvoid], :hresult\n\n  # code modified from Unknownr project https://github.com/rpeev/Unknownr\n  # licensed under MIT\n  module Interface\n    def self.[](*args)\n      spec, iid, *ifaces = args.reverse\n\n      spec.each { |_name, signature| signature[0].unshift(:pointer) }\n\n      Class.new(FFI::Struct) do\n        const_set(:IID, iid)\n\n        vtable = Class.new(FFI::Struct) do\n          vtable_hash = (ifaces.map { |iface| iface::VTBL::SPEC.to_a } << spec.to_a).flatten(1).to_h\n          const_set(:SPEC, vtable_hash)\n\n          layout(\n            *self::SPEC.map { |name, signature| [name, callback(*signature)] }.flatten\n          )\n        end\n\n        const_set(:VTBL, vtable)\n\n        layout \\\n          :lpVtbl, :pointer\n      end\n    end\n  end\n\n  module Helpers\n    def QueryInstance(klass)\n      instance = nil\n\n      FFI::MemoryPointer.new(:pointer) do |ppv|\n        QueryInterface(klass::IID, ppv)\n\n        instance = klass.new(ppv.read_pointer)\n      end\n\n      begin\n        yield instance\n        return self\n      ensure\n        instance.Release\n      end if block_given?\n\n      instance\n    end\n\n    def UseInstance(klass, name, *args)\n      instance = nil\n\n      FFI::MemoryPointer.new(:pointer) do |ppv|\n        send(name, *args, ppv)\n\n        yield instance = klass.new(ppv.read_pointer)\n      end\n\n      self\n    ensure\n      instance.Release if instance && !instance.null?\n    end\n  end\n\n  module Instance\n    def self.[](iface)\n      Class.new(iface) do\n        send(:include, Helpers)\n\n        def initialize(pointer)\n          self.pointer = pointer\n\n          @vtbl = self.class::VTBL.new(self[:lpVtbl])\n        end\n\n        attr_reader :vtbl\n\n        self::VTBL.members.each do |name|\n          define_method(name) do |*args|\n            if Puppet::Util::Windows::COM.FAILED((result = @vtbl[name].call(self, *args)))\n              raise Puppet::Util::Windows::Error.new(_(\"Failed to call %{klass}::%{name} with HRESULT: %{result}.\") % { klass: self, name: name, result: result }, result)\n            end\n\n            result\n          end\n        end\n\n        layout \\\n          :lpVtbl, :pointer\n      end\n    end\n  end\n\n  module Factory\n    def self.[](iface, clsid)\n      Class.new(iface) do\n        send(:include, Helpers)\n\n        const_set(:CLSID, clsid)\n\n        def initialize(opts = {})\n          @opts = opts\n\n          @opts[:clsctx] ||= CLSCTX_INPROC_SERVER\n\n          FFI::MemoryPointer.new(:pointer) do |ppv|\n            hr = Puppet::Util::Windows::COM.CoCreateInstance(self.class::CLSID, FFI::Pointer::NULL, @opts[:clsctx], self.class::IID, ppv)\n            if Puppet::Util::Windows::COM.FAILED(hr)\n              raise _(\"CoCreateInstance failed (%{klass}).\") % { klass: self.class }\n            end\n\n            self.pointer = ppv.read_pointer\n          end\n\n          @vtbl = self.class::VTBL.new(self[:lpVtbl])\n        end\n\n        attr_reader :vtbl\n\n        self::VTBL.members.each do |name|\n          define_method(name) do |*args|\n            if Puppet::Util::Windows::COM.FAILED((result = @vtbl[name].call(self, *args)))\n              raise Puppet::Util::Windows::Error.new(_(\"Failed to call %{klass}::%{name} with HRESULT: %{result}.\") % { klass: self, name: name, result: result }, result)\n            end\n\n            result\n          end\n        end\n\n        layout \\\n          :lpVtbl, :pointer\n      end\n    end\n  end\n\n  IUnknown = Interface[\n    FFI::WIN32::GUID['00000000-0000-0000-C000-000000000046'],\n    QueryInterface: [[:pointer, :pointer], :hresult],\n    AddRef: [[], :win32_ulong],\n    Release: [[], :win32_ulong]\n  ]\n\n  Unknown = Instance[IUnknown]\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx\n  # HRESULT CoInitialize(\n  #   _In_opt_  LPVOID pvReserved\n  # );\n  ffi_lib :ole32\n  attach_function_private :CoInitialize, [:lpvoid], :hresult\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms688715(v=vs.85).aspx\n  # void CoUninitialize(void);\n  ffi_lib :ole32\n  attach_function_private :CoUninitialize, [], :void\n\n  def InitializeCom\n    raise_if_hresult_failed(:CoInitialize, FFI::Pointer::NULL)\n\n    at_exit { CoUninitialize() }\n  end\n\n  module_function :InitializeCom\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/daemon.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::Util::Windows\n  # The Daemon class, based on the chef/win32-service implementation\n  class Daemon\n    include Puppet::FFI::Windows::Constants\n    extend Puppet::FFI::Windows::Constants\n\n    include Puppet::FFI::Windows::Structs\n    extend Puppet::FFI::Windows::Structs\n\n    include Puppet::FFI::Windows::Functions\n    extend Puppet::FFI::Windows::Functions\n\n    # Service is not running\n    STOPPED = SERVICE_STOPPED\n\n    # Service has received a start signal but is not yet running\n    START_PENDING = SERVICE_START_PENDING\n\n    # Service has received a stop signal but is not yet stopped\n    STOP_PENDING  = SERVICE_STOP_PENDING\n\n    # Service is running\n    RUNNING = SERVICE_RUNNING\n\n    # Service has received a signal to resume but is not yet running\n    CONTINUE_PENDING = SERVICE_CONTINUE_PENDING\n\n    # Service has received a signal to pause but is not yet paused\n    PAUSE_PENDING = SERVICE_PAUSE_PENDING\n\n    # Service is paused\n    PAUSED = SERVICE_PAUSED\n\n    # Service controls\n\n    # Notifies service that it should stop\n    CONTROL_STOP = SERVICE_CONTROL_STOP\n\n    # Notifies service that it should pause\n    CONTROL_PAUSE = SERVICE_CONTROL_PAUSE\n\n    # Notifies service that it should resume\n    CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE\n\n    # Notifies service that it should return its current status information\n    CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE\n\n    # Notifies a service that its parameters have changed\n    CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE\n\n    # Notifies a service that there is a new component for binding\n    CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD\n\n    # Notifies a service that a component for binding has been removed\n    CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE\n\n    # Notifies a service that a component for binding has been enabled\n    CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE\n\n    # Notifies a service that a component for binding has been disabled\n    CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE\n\n    IDLE = 0\n\n    # Misc\n    IDLE_CONTROL_CODE = 0\n    WAIT_OBJECT_0 = 0\n    WAIT_TIMEOUT = 0x00000102\n    WAIT_FAILED = 0xFFFFFFFF\n    NO_ERROR = 0\n\n    # Wraps SetServiceStatus.\n    SetTheServiceStatus = proc do |dwCurrentState, dwWin32ExitCode, dwCheckPoint, dwWaitHint|\n      ss = SERVICE_STATUS.new # Current status of the service.\n\n      # Disable control requests until the service is started.\n      if dwCurrentState == SERVICE_START_PENDING\n        ss[:dwControlsAccepted] = 0\n      else\n        ss[:dwControlsAccepted] =\n          SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN |\n          SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_PARAMCHANGE\n      end\n\n      # Initialize ss structure.\n      ss[:dwServiceType]             = SERVICE_WIN32_OWN_PROCESS\n      ss[:dwServiceSpecificExitCode] = 0\n      ss[:dwCurrentState]            = dwCurrentState\n      ss[:dwWin32ExitCode]           = dwWin32ExitCode\n      ss[:dwCheckPoint]              = dwCheckPoint\n      ss[:dwWaitHint]                = dwWaitHint\n\n      @@dwServiceState = dwCurrentState\n\n      # Send status of the service to the Service Controller.\n      unless SetServiceStatus(@@ssh, ss)\n        SetEvent(@@hStopEvent)\n      end\n    end\n\n    ERROR_CALL_NOT_IMPLEMENTED = 0x78\n\n    # Handles control signals from the service control manager.\n    Service_Ctrl_ex = proc do |dwCtrlCode, _dwEventType, _lpEventData, _lpContext|\n      @@waiting_control_code = dwCtrlCode;\n      return_value = NO_ERROR\n\n      begin\n        dwState = SERVICE_RUNNING\n\n        case dwCtrlCode\n        when SERVICE_CONTROL_STOP\n          dwState = SERVICE_STOP_PENDING\n        when SERVICE_CONTROL_SHUTDOWN\n          dwState = SERVICE_STOP_PENDING\n        when SERVICE_CONTROL_PAUSE\n          dwState = SERVICE_PAUSED\n        when SERVICE_CONTROL_CONTINUE\n          dwState = SERVICE_RUNNING\n          # else\n          # TODO: Handle other control codes? Retain the current state?\n        end\n\n        # Set the status of the service except on interrogation.\n        unless dwCtrlCode == SERVICE_CONTROL_INTERROGATE\n          SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)\n        end\n\n        # Tell service_main thread to stop.\n        if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN\n          if SetEvent(@@hStopEvent) == 0\n            SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)\n          end\n        end\n      rescue\n        return_value = ERROR_CALL_NOT_IMPLEMENTED\n      end\n\n      return_value\n    end\n\n    # Called by the service control manager after the call to StartServiceCtrlDispatcher.\n    Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc, lpszArgv|\n      # Obtain the name of the service.\n      if lpszArgv.address != 0\n        argv = lpszArgv.get_array_of_string(0, dwArgc)\n        lpszServiceName = argv[0]\n      else\n        lpszServiceName = ''\n      end\n\n      # Args passed to Service.start\n      if dwArgc > 1\n        @@Argv = argv[1..]\n      else\n        @@Argv = nil\n      end\n\n      # Register the service ctrl handler.\n      @@ssh = RegisterServiceCtrlHandlerExW(\n        lpszServiceName,\n        Service_Ctrl_ex,\n        nil\n      )\n\n      # No service to stop, no service handle to notify, nothing to do but exit.\n      break if @@ssh == 0\n\n      # The service has started.\n      SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)\n\n      SetEvent(@@hStartEvent)\n\n      # Main loop for the service.\n      while WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0\n      end\n\n      # Main loop for the service.\n      while WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0\n      end\n    ensure\n      # Stop the service.\n      SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)\n    end\n\n    # This is a shortcut for Daemon.new + Daemon#mainloop.\n    #\n    def self.mainloop\n      new.mainloop\n    end\n\n    # This is the method that actually puts your code into a loop and allows it\n    # to run as a service.  The code that is actually run while in the mainloop\n    # is what you defined in your own Daemon#service_main method.\n    #\n    def mainloop\n      @@waiting_control_code = IDLE_CONTROL_CODE\n      @@dwServiceState = 0\n\n      # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still\n      # associated with a tty. This helps newbs avoid Errno::EBADF errors.\n      STDIN.reopen('NUL') if STDIN.isatty\n      STDOUT.reopen('NUL') if STDOUT.isatty\n      STDERR.reopen('NUL') if STDERR.isatty\n\n      # Calling init here so that init failures never even tries to start the\n      # service. Of course that means that init methods must be very quick\n      # because the SCM will be receiving no START_PENDING messages while\n      # init's running.\n      #\n      # TODO: Fix?\n      service_init() if respond_to?('service_init')\n\n      # Create the event to signal the service to start.\n      @@hStartEvent = CreateEventW(nil, 1, 0, nil)\n\n      if @@hStartEvent == 0\n        raise SystemCallError.new('CreateEvent', FFI.errno)\n      end\n\n      # Create the event to signal the service to stop.\n      @@hStopEvent = CreateEventW(nil, 1, 0, nil)\n\n      if @@hStopEvent == 0\n        raise SystemCallError.new('CreateEvent', FFI.errno)\n      end\n\n      # Create the event to signal the service that stop has completed\n      @@hStopCompletedEvent = CreateEventW(nil, 1, 0, nil)\n\n      if @@hStopCompletedEvent == 0\n        raise SystemCallError.new('CreateEvent', FFI.errno)\n      end\n\n      hThread = Thread.new do\n        ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRYW, 2)\n\n        s = SERVICE_TABLE_ENTRYW.new(ste[0])\n        s[:lpServiceName] = FFI::MemoryPointer.from_string(\"\")\n        s[:lpServiceProc] = Service_Main\n\n        s = SERVICE_TABLE_ENTRYW.new(ste[1])\n        s[:lpServiceName] = nil\n        s[:lpServiceProc] = nil\n\n        # No service to step, no service handle, no ruby exceptions, just terminate the thread..\n        StartServiceCtrlDispatcherW(ste)\n      end\n\n      while (index = WaitForSingleObject(@@hStartEvent, 1000)) == WAIT_TIMEOUT\n        # The thread exited, so the show is off.\n        raise \"Service_Main thread exited abnormally\" unless hThread.alive?\n      end\n\n      if index == WAIT_FAILED\n        raise SystemCallError.new(\"WaitForSingleObject\", FFI.errno)\n      end\n\n      thr = Thread.new do\n        while WaitForSingleObject(@@hStopEvent, 1000) == WAIT_TIMEOUT\n          # Check to see if anything interesting has been signaled\n          case @@waiting_control_code\n          when SERVICE_CONTROL_PAUSE\n            service_pause() if respond_to?('service_pause')\n          when SERVICE_CONTROL_CONTINUE\n            service_resume() if respond_to?('service_resume')\n          when SERVICE_CONTROL_INTERROGATE\n            service_interrogate() if respond_to?('service_interrogate')\n          when SERVICE_CONTROL_SHUTDOWN\n            service_shutdown() if respond_to?('service_shutdown')\n          when SERVICE_CONTROL_PARAMCHANGE\n            service_paramchange() if respond_to?('service_paramchange')\n          when SERVICE_CONTROL_NETBINDADD\n            service_netbindadd() if respond_to?('service_netbindadd')\n          when SERVICE_CONTROL_NETBINDREMOVE\n            service_netbindremove() if respond_to?('service_netbindremove')\n          when SERVICE_CONTROL_NETBINDENABLE\n            service_netbindenable() if respond_to?('service_netbindenable')\n          when SERVICE_CONTROL_NETBINDDISABLE\n            service_netbinddisable() if respond_to?('service_netbinddisable')\n          end\n          @@waiting_control_code = IDLE_CONTROL_CODE\n        end\n\n        service_stop() if respond_to?('service_stop')\n      ensure\n        SetEvent(@@hStopCompletedEvent)\n      end\n\n      if respond_to?('service_main')\n        service_main(*@@Argv)\n      end\n\n      thr.join\n    end\n\n    # Returns the state of the service (as an constant integer) which can be any\n    # of the service status constants, e.g. RUNNING, PAUSED, etc.\n    #\n    # This method is typically used within your service_main method to setup the\n    # loop. For example:\n    #\n    #   class MyDaemon < Daemon\n    #     def service_main\n    #       while state == RUNNING || state == PAUSED || state == IDLE\n    #         # Your main loop here\n    #       end\n    #     end\n    #   end\n    #\n    # See the Daemon#running? method for an abstraction of the above code.\n    #\n    def state\n      @@dwServiceState\n    end\n\n    #\n    # Returns whether or not the service is in a running state, i.e. the service\n    # status is either RUNNING, PAUSED or IDLE.\n    #\n    # This is typically used within your service_main method to setup the main\n    # loop. For example:\n    #\n    #    class MyDaemon < Daemon\n    #       def service_main\n    #          while running?\n    #             # Your main loop here\n    #          end\n    #       end\n    #    end\n    #\n    def running?\n      [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/error.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\nrequire_relative '../../../puppet/error'\n\n# represents an error resulting from a Win32 error code\nclass Puppet::Util::Windows::Error < Puppet::Error\n  require 'ffi'\n  extend FFI::Library\n\n  attr_reader :code\n\n  # NOTE: FFI.errno only works properly when prior Win32 calls have been made\n  # through FFI bindings.  Calls made through Win32API do not have their error\n  # codes captured by FFI.errno\n  def initialize(message, code = FFI.errno, original = nil)\n    super(message + \":  #{self.class.format_error_code(code)}\", original)\n\n    @code = code\n  end\n\n  # Helper method that wraps FormatMessage that returns a human readable string.\n  def self.format_error_code(code)\n    # specifying 0 will look for LANGID in the following order\n    # 1.Language neutral\n    # 2.Thread LANGID, based on the thread's locale value\n    # 3.User default LANGID, based on the user's default locale value\n    # 4.System default LANGID, based on the system default locale value\n    # 5.US English\n    dwLanguageId = 0\n    flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |\n            FORMAT_MESSAGE_FROM_SYSTEM |\n            FORMAT_MESSAGE_ARGUMENT_ARRAY |\n            FORMAT_MESSAGE_IGNORE_INSERTS |\n            FORMAT_MESSAGE_MAX_WIDTH_MASK\n    error_string = ''.dup\n\n    # this pointer actually points to a :lpwstr (pointer) since we're letting Windows allocate for us\n    FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr|\n      length = FormatMessageW(flags, FFI::Pointer::NULL, code, dwLanguageId,\n                              buffer_ptr, 0, FFI::Pointer::NULL)\n\n      if length == FFI::WIN32_FALSE\n        # can't raise same error type here or potentially recurse infinitely\n        raise Puppet::Error, _(\"FormatMessageW could not format code %{code}\") % { code: code }\n      end\n\n      # returns an FFI::Pointer with autorelease set to false, which is what we want\n      buffer_ptr.read_win32_local_pointer do |wide_string_ptr|\n        if wide_string_ptr.null?\n          raise Puppet::Error, _(\"FormatMessageW failed to allocate buffer for code %{code}\") % { code: code }\n        end\n\n        error_string = wide_string_ptr.read_wide_string(length)\n      end\n    end\n\n    error_string\n  end\n\n  ERROR_FILE_NOT_FOUND      = 2\n  ERROR_ACCESS_DENIED       = 5\n\n  FORMAT_MESSAGE_ALLOCATE_BUFFER   = 0x00000100\n  FORMAT_MESSAGE_IGNORE_INSERTS    = 0x00000200\n  FORMAT_MESSAGE_FROM_SYSTEM       = 0x00001000\n  FORMAT_MESSAGE_ARGUMENT_ARRAY    = 0x00002000\n  FORMAT_MESSAGE_MAX_WIDTH_MASK    = 0x000000FF\n\n  ffi_convention :stdcall\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx\n  # DWORD WINAPI FormatMessage(\n  #   _In_      DWORD dwFlags,\n  #   _In_opt_  LPCVOID lpSource,\n  #   _In_      DWORD dwMessageId,\n  #   _In_      DWORD dwLanguageId,\n  #   _Out_     LPTSTR lpBuffer,\n  #   _In_      DWORD nSize,\n  #   _In_opt_  va_list *Arguments\n  # );\n  # NOTE: since we're not preallocating the buffer, use a :pointer for lpBuffer\n  ffi_lib :kernel32\n  attach_function_private :FormatMessageW,\n                          [:dword, :lpcvoid, :dword, :dword, :pointer, :dword, :pointer], :dword\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/eventlog.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'ffi'\n\n# Puppet::Util::Windows::EventLog needs to be requirable without having loaded\n# any other parts of Puppet so it can be leveraged independently by the code\n# that runs Puppet as a service on Windows.\n#\n# For this reason we:\n# - Define Puppet::Util::Windows\n# - Replicate logic that exists elsewhere in puppet/util/windows\n# - Raise generic RuntimeError instead of Puppet::Util::Windows::Error if its not defined\nmodule Puppet; module Util; module Windows; end; end; end\n\nclass Puppet::Util::Windows::EventLog\n  extend FFI::Library\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx\n  EVENTLOG_ERROR_TYPE       = 0x0001\n  EVENTLOG_WARNING_TYPE     = 0x0002\n  EVENTLOG_INFORMATION_TYPE = 0x0004\n\n  # These are duplicate definitions from Puppet::Util::Windows::ApiTypes,\n  # established here so this class can be standalone from Puppet, and public so\n  # we can reference them in tests.\n  NULL_HANDLE = 0\n  WIN32_FALSE = 0\n\n  # Register an event log handle for the application\n  # @param source_name [String] the name of the event source to retrieve a handle for\n  # @return [void]\n  # @api public\n  def initialize(source_name = 'Puppet')\n    @eventlog_handle = RegisterEventSourceW(FFI::Pointer::NULL, wide_string(source_name))\n    if @eventlog_handle == NULL_HANDLE\n      # TRANSLATORS 'Windows' is the operating system and 'RegisterEventSourceW' is a API call and should not be translated\n      raise EventLogError.new(_(\"RegisterEventSourceW failed to open Windows eventlog\"), FFI.errno)\n    end\n  end\n\n  # Close this instance's event log handle\n  # @return [void]\n  # @api public\n  def close\n    DeregisterEventSource(@eventlog_handle)\n  ensure\n    @eventlog_handle = nil\n  end\n\n  # Report an event to this instance's event log handle. Accepts a string to\n  #   report (:data => <string>) and event type (:event_type => Integer) and id\n  # (:event_id => Integer) as returned by #to_native. The additional arguments to\n  # ReportEventW seen in this method aren't exposed - though ReportEventW\n  # technically can accept multiple strings as well as raw binary data to log,\n  # we accept a single string from Puppet::Util::Log\n  #\n  # @param args [Hash{Symbol=>Object}] options to the associated log event\n  # @return [void]\n  # @api public\n  def report_event(args = {})\n    unless args[:data].is_a?(String)\n      raise ArgumentError, _(\"data must be a string, not %{class_name}\") % { class_name: args[:data].class }\n    end\n\n    from_string_to_wide_string(args[:data]) do |message_ptr|\n      FFI::MemoryPointer.new(:pointer) do |message_array_ptr|\n        message_array_ptr.write_pointer(message_ptr)\n        user_sid = FFI::Pointer::NULL\n        raw_data = FFI::Pointer::NULL\n        raw_data_size = 0\n        num_strings = 1\n        eventlog_category = 0\n        report_result = ReportEventW(@eventlog_handle, args[:event_type],\n                                     eventlog_category, args[:event_id], user_sid,\n                                     num_strings, raw_data_size, message_array_ptr, raw_data)\n\n        if report_result == WIN32_FALSE\n          # TRANSLATORS 'Windows' is the operating system and 'ReportEventW' is a API call and should not be translated\n          raise EventLogError.new(_(\"ReportEventW failed to report event to Windows eventlog\"), FFI.errno)\n        end\n      end\n    end\n  end\n\n  class << self\n    # Feels more natural to do Puppet::Util::Window::EventLog.open(\"MyApplication\")\n    alias :open :new\n\n    # Query event identifier info for a given log level\n    # @param level [Symbol] an event log level\n    # @return [Array] Win API Event ID, Puppet Event ID\n    # @api public\n    def to_native(level)\n      case level\n      when :debug, :info, :notice\n        [EVENTLOG_INFORMATION_TYPE, 0x01]\n      when :warning\n        [EVENTLOG_WARNING_TYPE, 0x02]\n      when :err, :alert, :emerg, :crit\n        [EVENTLOG_ERROR_TYPE, 0x03]\n      else\n        raise ArgumentError, _(\"Invalid log level %{level}\") % { level: level }\n      end\n    end\n  end\n\n  private\n\n  # For the purposes of allowing this class to be standalone, the following are\n  # duplicate definitions from elsewhere in Puppet:\n\n  # If we're loaded via Puppet we should keep the previous behavior of raising\n  # Puppet::Util::Windows::Error on errors. If we aren't, at least concatenate\n  # the error code to the exception message to pass this information on to the\n  # user\n  if defined?(Puppet::Util::Windows::Error)\n    EventLogError = Puppet::Util::Windows::Error\n  else\n    class EventLogError < RuntimeError\n      def initialize(msg, code)\n        # TRANSLATORS 'Win32' is the Windows API and should not be translated\n        super(msg + ' ' + _(\"(Win32 error: %{detail})\") % { detail: code })\n      end\n    end\n  end\n\n  # Private duplicate of Puppet::Util::Windows::String::wide_string\n  # Not for use outside of EventLog! - use Puppet::Util::Windows instead\n  # @api private\n  def wide_string(str)\n    # if given a nil string, assume caller wants to pass a nil pointer to win32\n    return nil if str.nil?\n\n    str.encode('UTF-16LE')\n  end\n\n  # Private duplicate of Puppet::Util::Windows::ApiTypes::from_string_to_wide_string\n  # Not for use outside of EventLog! - Use Puppet::Util::Windows instead\n  # @api private\n  def from_string_to_wide_string(str, &block)\n    str = wide_string(str)\n    FFI::MemoryPointer.from_wide_string(str) { |ptr| yield ptr }\n\n    # ptr has already had free called, so nothing to return\n    nil\n  end\n\n  ffi_convention :stdcall\n\n  # The following are typedefs in Puppet::Util::Winodws::ApiTypes, but here we\n  # use their original FFI counterparts:\n  # :uintptr_t for :handle\n  # :int32 for :win32_bool\n  # :uint16 for :word\n  # :uint32 for :dword\n  # :pointer for :lpvoid\n  # :uchar for :byte\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363678(v=vs.85).aspx\n  # HANDLE RegisterEventSource(\n  # _In_ LPCTSTR lpUNCServerName,\n  # _In_ LPCTSTR lpSourceName\n  # );\n  ffi_lib :advapi32\n  attach_function :RegisterEventSourceW, [:buffer_in, :buffer_in], :uintptr_t\n  private :RegisterEventSourceW\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363642(v=vs.85).aspx\n  # BOOL DeregisterEventSource(\n  # _Inout_ HANDLE hEventLog\n  # );\n  ffi_lib :advapi32\n  attach_function :DeregisterEventSource, [:uintptr_t], :int32\n  private :DeregisterEventSource\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx\n  # BOOL ReportEvent(\n  #   _In_ HANDLE  hEventLog,\n  #   _In_ WORD    wType,\n  #   _In_ WORD    wCategory,\n  #   _In_ DWORD   dwEventID,\n  #   _In_ PSID    lpUserSid,\n  #   _In_ WORD    wNumStrings,\n  #   _In_ DWORD   dwDataSize,\n  #   _In_ LPCTSTR *lpStrings,\n  #   _In_ LPVOID  lpRawData\n  # );\n  ffi_lib :advapi32\n  attach_function :ReportEventW, [:uintptr_t, :uint16, :uint16, :uint32, :pointer, :uint16, :uint32, :pointer, :pointer], :int32\n  private :ReportEventW\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/file.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::Util::Windows::File\n  extend Puppet::Util::Windows::String\n\n  include Puppet::FFI::Windows::Constants\n\n  extend Puppet::FFI::Windows::Structs\n  include Puppet::FFI::Windows::Structs\n\n  include Puppet::FFI::Windows::Functions\n  extend Puppet::FFI::Windows::Functions\n\n  def replace_file(target, source)\n    target_encoded = wide_string(target.to_s)\n    source_encoded = wide_string(source.to_s)\n\n    flags = REPLACEFILE_IGNORE_MERGE_ERRORS\n    backup_file = nil\n    result = ReplaceFileW(\n      target_encoded,\n      source_encoded,\n      backup_file,\n      flags,\n      FFI::Pointer::NULL,\n      FFI::Pointer::NULL\n    )\n\n    return true if result != FFI::WIN32_FALSE\n\n    raise Puppet::Util::Windows::Error, \"ReplaceFile(#{target}, #{source})\"\n  end\n  module_function :replace_file\n\n  def move_file_ex(source, target, flags = 0)\n    result = MoveFileExW(wide_string(source.to_s),\n                         wide_string(target.to_s),\n                         flags)\n\n    return true if result != FFI::WIN32_FALSE\n\n    raise Puppet::Util::Windows::Error, \"MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})\"\n  end\n  module_function :move_file_ex\n\n  def symlink(target, symlink)\n    flags = File.directory?(target) ? 0x1 : 0x0\n    result = CreateSymbolicLinkW(wide_string(symlink.to_s),\n                                 wide_string(target.to_s), flags)\n    return true if result != FFI::WIN32_FALSE\n\n    raise Puppet::Util::Windows::Error, \"CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})\"\n  end\n  module_function :symlink\n\n  def exist?(path)\n    path = path.to_str if path.respond_to?(:to_str) # support WatchedFile\n    path = path.to_s # support String and Pathname\n\n    seen_paths = []\n    # follow up to 64 symlinks before giving up\n    0.upto(64) do |_depth|\n      # return false if this path has been seen before.  This is protection against circular symlinks\n      return false if seen_paths.include?(path.downcase)\n\n      result = get_attributes(path, false)\n\n      # return false for path not found\n      return false if result == INVALID_FILE_ATTRIBUTES\n\n      # return true if path exists and it's not a symlink\n      # Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx\n      reparse_point = (result & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT\n      if reparse_point && symlink_reparse_point?(path)\n        # walk the symlink and try again...\n        seen_paths << path.downcase\n        path = readlink(path)\n      else\n        # file was found and its not a symlink\n        return true\n      end\n    end\n\n    false\n  end\n  module_function :exist?\n\n  def get_attributes(file_name, raise_on_invalid = true)\n    result = GetFileAttributesW(wide_string(file_name.to_s))\n    if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES\n      raise Puppet::Util::Windows::Error, \"GetFileAttributes(#{file_name})\"\n    end\n\n    result\n  end\n  module_function :get_attributes\n\n  def add_attributes(path, flags)\n    oldattrs = get_attributes(path)\n\n    if (oldattrs | flags) != oldattrs\n      set_attributes(path, oldattrs | flags)\n    end\n  end\n  module_function :add_attributes\n\n  def remove_attributes(path, flags)\n    oldattrs = get_attributes(path)\n\n    if (oldattrs & ~flags) != oldattrs\n      set_attributes(path, oldattrs & ~flags)\n    end\n  end\n  module_function :remove_attributes\n\n  def set_attributes(path, flags)\n    success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE\n    raise Puppet::Util::Windows::Error, _(\"Failed to set file attributes\") unless success\n\n    success\n  end\n  module_function :set_attributes\n\n  # define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)\n  INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address\n  def self.create_file(file_name, desired_access, share_mode, security_attributes,\n                       creation_disposition, flags_and_attributes, template_file_handle)\n\n    result = CreateFileW(wide_string(file_name.to_s),\n                         desired_access, share_mode, security_attributes, creation_disposition,\n                         flags_and_attributes, template_file_handle)\n\n    return result unless result == INVALID_HANDLE_VALUE\n\n    raise Puppet::Util::Windows::Error, \"CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, \" \\\n                                        \"#{security_attributes}, #{creation_disposition.to_s(8)}, \" \\\n                                        \"#{flags_and_attributes.to_s(8)}, #{template_file_handle})\"\n  end\n\n  def self.get_reparse_point_data(handle, &block)\n    # must be multiple of 1024, min 10240\n    FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr|\n      device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr)\n\n      reparse_tag = reparse_data_buffer_ptr.read_win32_ulong\n      buffer_type = case reparse_tag\n                    when IO_REPARSE_TAG_SYMLINK\n                      SYMLINK_REPARSE_DATA_BUFFER\n                    when IO_REPARSE_TAG_MOUNT_POINT\n                      MOUNT_POINT_REPARSE_DATA_BUFFER\n                    when IO_REPARSE_TAG_NFS\n                      raise Puppet::Util::Windows::Error, \"Retrieving NFS reparse point data is unsupported\"\n                    else\n                      raise Puppet::Util::Windows::Error, \"DeviceIoControl(#{handle}, \" \\\n                                                          \"FSCTL_GET_REPARSE_POINT) returned unknown tag 0x#{reparse_tag.to_s(16).upcase}\"\n                    end\n\n      yield buffer_type.new(reparse_data_buffer_ptr)\n    end\n\n    # underlying struct MemoryPointer has been cleaned up by this point, nothing to return\n    nil\n  end\n\n  def self.get_reparse_point_tag(handle)\n    reparse_tag = nil\n\n    # must be multiple of 1024, min 10240\n    FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr|\n      device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr)\n\n      # DWORD ReparseTag is the first member of the struct\n      reparse_tag = reparse_data_buffer_ptr.read_win32_ulong\n    end\n\n    reparse_tag\n  end\n\n  def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)\n    if out_buffer.nil?\n      raise Puppet::Util::Windows::Error, _(\"out_buffer is required\")\n    end\n\n    FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr|\n      result = DeviceIoControl(\n        handle,\n        io_control_code,\n        in_buffer, in_buffer.nil? ? 0 : in_buffer.size,\n        out_buffer, out_buffer.size,\n        bytes_returned_ptr,\n        nil\n      )\n\n      if result == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, \"DeviceIoControl(#{handle}, #{io_control_code}, \" \\\n                                            \"#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, \" \\\n                                            \"#{out_buffer}, #{out_buffer ? out_buffer.size : ''}\"\n      end\n    end\n\n    out_buffer\n  end\n\n  def reparse_point?(file_name)\n    attributes = get_attributes(file_name, false)\n\n    return false if attributes == INVALID_FILE_ATTRIBUTES\n\n    (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT\n  end\n  module_function :reparse_point?\n\n  def symlink?(file_name)\n    # Puppet currently only handles mount point and symlink reparse points, ignores others\n    reparse_point?(file_name) && symlink_reparse_point?(file_name)\n  end\n  module_function :symlink?\n\n  def self.open_symlink(link_name)\n    begin\n      yield handle = create_file(\n        link_name,\n        GENERIC_READ,\n        FILE_SHARE_READ,\n        nil, # security_attributes\n        OPEN_EXISTING,\n        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,\n        0\n      ) # template_file\n    ensure\n      FFI::WIN32.CloseHandle(handle) if handle\n    end\n\n    # handle has had CloseHandle called against it, so nothing to return\n    nil\n  end\n\n  def readlink(link_name)\n    link = nil\n    open_symlink(link_name) do |handle|\n      link = resolve_symlink(handle)\n    end\n\n    link\n  end\n  module_function :readlink\n\n  def get_long_pathname(path)\n    converted = ''.dup\n    FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|\n      # includes terminating NULL\n      buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0)\n      FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|\n        if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to call GetLongPathName\")\n        end\n\n        converted = converted_ptr.read_wide_string(buffer_size - 1)\n      end\n    end\n\n    converted\n  end\n  module_function :get_long_pathname\n\n  def get_short_pathname(path)\n    converted = ''.dup\n    FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|\n      # includes terminating NULL\n      buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0)\n      FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|\n        if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, \"Failed to call GetShortPathName\"\n        end\n\n        converted = converted_ptr.read_wide_string(buffer_size - 1)\n      end\n    end\n\n    converted\n  end\n  module_function :get_short_pathname\n\n  def stat(file_name)\n    file_name = file_name.to_s # accommodate PathName or String\n    stat = File.stat(file_name)\n    singleton_class = class << stat; self; end\n    target_path = file_name\n\n    if symlink?(file_name)\n      target_path = readlink(file_name)\n      link_ftype = File.stat(target_path).ftype\n\n      # sigh, monkey patch instance method for instance, and close over link_ftype\n      singleton_class.send(:define_method, :ftype) do\n        link_ftype\n      end\n    end\n\n    singleton_class.send(:define_method, :mode) do\n      Puppet::Util::Windows::Security.get_mode(target_path)\n    end\n\n    stat\n  end\n  module_function :stat\n\n  def lstat(file_name)\n    file_name = file_name.to_s # accommodate PathName or String\n    # monkey'ing around!\n    stat = File.lstat(file_name)\n\n    singleton_class = class << stat; self; end\n    singleton_class.send(:define_method, :mode) do\n      Puppet::Util::Windows::Security.get_mode(file_name)\n    end\n\n    if symlink?(file_name)\n      def stat.ftype\n        \"link\"\n      end\n    end\n    stat\n  end\n  module_function :lstat\n\n  def self.resolve_symlink(handle)\n    path = nil\n    get_reparse_point_data(handle) do |reparse_data|\n      offset = reparse_data[:PrintNameOffset]\n      length = reparse_data[:PrintNameLength]\n\n      ptr = reparse_data.pointer + reparse_data.offset_of(:PathBuffer) + offset\n      path = ptr.read_wide_string(length / 2) # length is bytes, need UTF-16 wchars\n    end\n\n    path\n  end\n  private_class_method :resolve_symlink\n\n  # these reparse point types are the only ones Puppet currently understands\n  # so rather than raising an exception in readlink, prefer to not consider\n  # the path a symlink when stat'ing later\n  def self.symlink_reparse_point?(path)\n    symlink = false\n\n    open_symlink(path) do |handle|\n      symlink = [\n        IO_REPARSE_TAG_SYMLINK,\n        IO_REPARSE_TAG_MOUNT_POINT\n      ].include?(get_reparse_point_tag(handle))\n    end\n\n    symlink\n  end\n  private_class_method :symlink_reparse_point?\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/monkey_patches/process.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'ffi'\nrequire_relative '../../../../puppet/ffi/windows'\nrequire_relative '../../../../puppet/util/windows/string'\n\nmodule Process\n  extend FFI::Library\n  extend Puppet::Util::Windows::String\n\n  extend Puppet::FFI::Windows::APITypes\n  extend Puppet::FFI::Windows::Functions\n  extend Puppet::FFI::Windows::Structs\n\n  include Puppet::FFI::Windows::Constants\n  include Puppet::FFI::Windows::Structs\n\n  ProcessInfo = Struct.new(\n    'ProcessInfo',\n    :process_handle,\n    :thread_handle,\n    :process_id,\n    :thread_id\n  )\n\n  private_constant :ProcessInfo\n\n  # Disable popups. This mostly affects the Process.kill method.\n  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX)\n\n  class << self\n    private :SetHandleInformation, :SetErrorMode, :CreateProcessW, :OpenProcess,\n            :SetPriorityClass, :CreateProcessWithLogonW, :get_osfhandle, :get_errno\n\n    # Process.create(key => value, ...) => ProcessInfo\n    #\n    # This is a wrapper for the CreateProcess() function. It executes a process,\n    # returning a ProcessInfo struct. It accepts a hash as an argument.\n    # There are several primary keys:\n    #\n    # * command_line     (this or app_name must be present)\n    # * app_name         (default: nil)\n    # * inherit          (default: false)\n    # * process_inherit  (default: false)\n    # * thread_inherit   (default: false)\n    # * creation_flags   (default: 0)\n    # * cwd              (default: Dir.pwd)\n    # * startup_info     (default: nil)\n    # * environment      (default: nil)\n    # * close_handles    (default: true)\n    # * with_logon       (default: nil)\n    # * domain           (default: nil)\n    # * password         (default: nil, mandatory if with_logon)\n    #\n    # Of these, the 'command_line' or 'app_name' must be specified or an\n    # error is raised. Both may be set individually, but 'command_line' should\n    # be preferred if only one of them is set because it does not (necessarily)\n    # require an explicit path or extension to work.\n    #\n    # The 'domain' and 'password' options are only relevent in the context\n    # of 'with_logon'. If 'with_logon' is set, then the 'password' option is\n    # mandatory.\n    #\n    # The startup_info key takes a hash. Its keys are attributes that are\n    # part of the StartupInfo struct, and are generally only meaningful for\n    # GUI or console processes. See the documentation on CreateProcess()\n    # and the StartupInfo struct on MSDN for more information.\n    #\n    # * desktop\n    # * title\n    # * x\n    # * y\n    # * x_size\n    # * y_size\n    # * x_count_chars\n    # * y_count_chars\n    # * fill_attribute\n    # * sw_flags\n    # * startf_flags\n    # * stdin\n    # * stdout\n    # * stderr\n    #\n    # Note that the 'stdin', 'stdout' and 'stderr' options can be either Ruby\n    # IO objects or file descriptors (i.e. a fileno). However, StringIO objects\n    # are not currently supported. Unfortunately, setting these is not currently\n    # an option for JRuby.\n    #\n    # If 'stdin', 'stdout' or 'stderr' are specified, then the +inherit+ value\n    # is automatically set to true and the Process::STARTF_USESTDHANDLES flag is\n    # automatically OR'd to the +startf_flags+ value.\n    #\n    # The ProcessInfo struct contains the following members:\n    #\n    # * process_handle - The handle to the newly created process.\n    # * thread_handle  - The handle to the primary thread of the process.\n    # * process_id     - Process ID.\n    # * thread_id      - Thread ID.\n    #\n    # If the 'close_handles' option is set to true (the default) then the\n    # process_handle and the thread_handle are automatically closed for you\n    # before the ProcessInfo struct is returned.\n    #\n    # If the 'with_logon' option is set, then the process runs the specified\n    # executable file in the security context of the specified credentials.\n\n    VALID_KEYS = %i[\n      app_name command_line inherit creation_flags cwd environment\n      startup_info thread_inherit process_inherit close_handles with_logon\n      domain password\n    ].freeze\n\n    VALID_SI_KEYS = %i[\n      startf_flags desktop title x y x_size y_size x_count_chars\n      y_count_chars fill_attribute sw_flags stdin stdout stderr\n    ].freeze\n\n    private_constant :VALID_KEYS, :VALID_SI_KEYS\n\n    def create(args)\n      # Validate that args is a Hash\n      validate_args(args)\n\n      initialize_defaults\n\n      # Validate the keys, and convert symbols and case to lowercase strings.\n      validate_keys(args)\n\n      # If the startup_info key is present, validate its subkeys\n      validate_startup_info if hash[:startup_info]\n\n      # validates that 'app_name' or 'command_line' is set\n      validate_command_line\n\n      if hash[:app_name] && !hash[:command_line]\n        hash[:command_line] = hash[:app_name]\n        hash[:app_name] = nil\n      end\n\n      # Setup stdin, stdout and stderr handlers\n      setup_std_handlers\n\n      if logon\n        create_process_with_logon\n      else\n        create_process\n      end\n\n      # Automatically close the process and thread handles in the\n      # PROCESS_INFORMATION struct unless explicitly told not to.\n      if hash[:close_handles]\n        FFI::WIN32.CloseHandle(procinfo[:hProcess])\n        FFI::WIN32.CloseHandle(procinfo[:hThread])\n      end\n\n      ProcessInfo.new(\n        procinfo[:hProcess],\n        procinfo[:hThread],\n        procinfo[:dwProcessId],\n        procinfo[:dwThreadId]\n      )\n    end\n\n    remove_method :setpriority\n\n    # Sets the priority class for the specified process id +int+.\n    #\n    # The +kind+ parameter is ignored but present for API compatibility.\n    # You can only retrieve process information, not process group or user\n    # information, so it is effectively always Process::PRIO_PROCESS.\n    #\n    # Possible +int_priority+ values are:\n    #\n    # * Process::NORMAL_PRIORITY_CLASS\n    # * Process::IDLE_PRIORITY_CLASS\n    # * Process::HIGH_PRIORITY_CLASS\n    # * Process::REALTIME_PRIORITY_CLASS\n    # * Process::BELOW_NORMAL_PRIORITY_CLASS\n    # * Process::ABOVE_NORMAL_PRIORITY_CLASS\n\n    def setpriority(kind, int, int_priority)\n      raise TypeError unless kind.is_a?(Integer)\n      raise TypeError unless int.is_a?(Integer)\n      raise TypeError unless int_priority.is_a?(Integer)\n\n      int = Process.pid if int == 0\n      handle = OpenProcess(PROCESS_SET_INFORMATION, 0, int)\n\n      if handle == 0\n        raise SystemCallError, FFI.errno, \"OpenProcess\"\n      end\n\n      begin\n        result = SetPriorityClass(handle, int_priority)\n        raise SystemCallError, FFI.errno, \"SetPriorityClass\" unless result\n      ensure\n        FFI::WIN32.CloseHandle(handle)\n      end\n\n      0\n    end\n\n    private\n\n    def initialize_defaults\n      @hash = {\n        app_name: nil,\n        creation_flags: 0,\n        close_handles: true\n      }\n      @si_hash = nil\n      @procinfo = nil\n    end\n\n    def validate_args(args)\n      raise TypeError, 'hash keyword arguments expected' unless args.is_a?(Hash)\n    end\n\n    def validate_keys(args)\n      args.each do |key, val|\n        key = key.to_s.to_sym\n        raise ArgumentError, \"invalid key '#{key}'\" unless VALID_KEYS.include?(key)\n\n        hash[key] = val\n      end\n    end\n\n    def validate_startup_info\n      hash[:startup_info].each do |key, val|\n        key = key.to_s.to_sym\n        raise ArgumentError, \"invalid startup_info key '#{key}'\" unless VALID_SI_KEYS.include?(key)\n\n        si_hash[key] = val\n      end\n    end\n\n    def validate_command_line\n      raise ArgumentError, 'command_line or app_name must be specified' unless hash[:app_name] || hash[:command_line]\n    end\n\n    def procinfo\n      @procinfo ||= PROCESS_INFORMATION.new\n    end\n\n    def hash\n      @hash ||= {}\n    end\n\n    def si_hash\n      @si_hash ||= {}\n    end\n\n    def app\n      wide_string(hash[:app_name])\n    end\n\n    def cmd\n      wide_string(hash[:command_line])\n    end\n\n    def cwd\n      wide_string(hash[:cwd])\n    end\n\n    def password\n      wide_string(hash[:password])\n    end\n\n    def logon\n      wide_string(hash[:with_logon])\n    end\n\n    def domain\n      wide_string(hash[:domain])\n    end\n\n    def env\n      env = hash[:environment]\n      return unless env\n\n      env = env.split(File::PATH_SEPARATOR) unless env.respond_to?(:join)\n      env = env.map { |e| e + 0.chr }.join('') + 0.chr\n      env = wide_string(env) if hash[:with_logon]\n      env\n    end\n\n    def process_security\n      return unless hash[:process_inherit]\n\n      process_security = SECURITY_ATTRIBUTES.new\n      process_security[:nLength] = SECURITY_ATTRIBUTES.size\n      process_security[:bInheritHandle] = 1\n      process_security\n    end\n\n    def thread_security\n      return unless hash[:thread_inherit]\n\n      thread_security = SECURITY_ATTRIBUTES.new\n      thread_security[:nLength] = SECURITY_ATTRIBUTES.size\n      thread_security[:bInheritHandle] = 1\n      thread_security\n    end\n\n    # Automatically handle stdin, stdout and stderr as either IO objects\n    # or file descriptors. This won't work for StringIO, however. It also\n    # will not work on JRuby because of the way it handles internal file\n    # descriptors.\n    def setup_std_handlers\n      %i[stdin stdout stderr].each do |io|\n        next unless si_hash[io]\n\n        handle = if si_hash[io].respond_to?(:fileno)\n                   get_osfhandle(si_hash[io].fileno)\n                 else\n                   get_osfhandle(si_hash[io])\n                 end\n\n        if handle == INVALID_HANDLE_VALUE\n          ptr = FFI::MemoryPointer.new(:int)\n\n          errno = if get_errno(ptr).zero?\n                    ptr.read_int\n                  else\n                    FFI.errno\n                  end\n\n          raise SystemCallError.new('get_osfhandle', errno)\n        end\n\n        # Most implementations of Ruby on Windows create inheritable\n        # handles by default, but some do not. RF bug #26988.\n        bool = SetHandleInformation(\n          handle,\n          HANDLE_FLAG_INHERIT,\n          HANDLE_FLAG_INHERIT\n        )\n\n        raise SystemCallError.new('SetHandleInformation', FFI.errno) unless bool\n\n        si_hash[io] = handle\n        si_hash[:startf_flags] ||= 0\n        si_hash[:startf_flags] |= STARTF_USESTDHANDLES\n        hash[:inherit] = true\n      end\n    end\n\n    def startinfo\n      startinfo = STARTUPINFO.new\n\n      return startinfo if si_hash.empty?\n\n      startinfo[:cb]              = startinfo.size\n      startinfo[:lpDesktop]       = si_hash[:desktop] if si_hash[:desktop]\n      startinfo[:lpTitle]         = si_hash[:title] if si_hash[:title]\n      startinfo[:dwX]             = si_hash[:x] if si_hash[:x]\n      startinfo[:dwY]             = si_hash[:y] if si_hash[:y]\n      startinfo[:dwXSize]         = si_hash[:x_size] if si_hash[:x_size]\n      startinfo[:dwYSize]         = si_hash[:y_size] if si_hash[:y_size]\n      startinfo[:dwXCountChars]   = si_hash[:x_count_chars] if si_hash[:x_count_chars]\n      startinfo[:dwYCountChars]   = si_hash[:y_count_chars] if si_hash[:y_count_chars]\n      startinfo[:dwFillAttribute] = si_hash[:fill_attribute] if si_hash[:fill_attribute]\n      startinfo[:dwFlags]         = si_hash[:startf_flags] if si_hash[:startf_flags]\n      startinfo[:wShowWindow]     = si_hash[:sw_flags] if si_hash[:sw_flags]\n      startinfo[:cbReserved2]     = 0\n      startinfo[:hStdInput]       = si_hash[:stdin] if si_hash[:stdin]\n      startinfo[:hStdOutput]      = si_hash[:stdout] if si_hash[:stdout]\n      startinfo[:hStdError]       = si_hash[:stderr] if si_hash[:stderr]\n      startinfo\n    end\n\n    def create_process_with_logon\n      raise ArgumentError, 'password must be specified if with_logon is used' unless password\n\n      hash[:creation_flags] |= CREATE_UNICODE_ENVIRONMENT\n\n      bool = CreateProcessWithLogonW(\n        logon,                  # User\n        domain,                 # Domain\n        password,               # Password\n        LOGON_WITH_PROFILE,     # Logon flags\n        app,                    # App name\n        cmd,                    # Command line\n        hash[:creation_flags],  # Creation flags\n        env,                    # Environment\n        cwd,                    # Working directory\n        startinfo,              # Startup Info\n        procinfo                # Process Info\n      )\n\n      raise SystemCallError.new('CreateProcessWithLogonW', FFI.errno) unless bool\n    end\n\n    def create_process\n      inherit = hash[:inherit] ? 1 : 0\n\n      bool = CreateProcessW(\n        app,                    # App name\n        cmd,                    # Command line\n        process_security,       # Process attributes\n        thread_security,        # Thread attributes\n        inherit,                # Inherit handles?\n        hash[:creation_flags],  # Creation flags\n        env,                    # Environment\n        cwd,                    # Working directory\n        startinfo,              # Startup Info\n        procinfo                # Process Info\n      )\n\n      raise SystemCallError.new('CreateProcess', FFI.errno) unless bool\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/principal.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\n\nmodule Puppet::Util::Windows::SID\n  class Principal\n    extend FFI::Library\n    attr_reader :account, :sid_bytes, :sid, :domain, :domain_account, :account_type\n\n    def initialize(account, sid_bytes, sid, domain, account_type)\n      # This is only ever called from lookup_account_sid which has already\n      # removed the potential for passing in an account like host\\user\n      @account = account\n      @sid_bytes = sid_bytes\n      @sid = sid\n      @domain = domain\n      @account_type = account_type\n      # When domain is available and it is a Domain principal, use domain only\n      #   otherwise if domain is available then combine it with parsed account\n      #   otherwise when the domain is not available, use the account value directly\n      # WinNT naming standard https://msdn.microsoft.com/en-us/library/windows/desktop/aa746534(v=vs.85).aspx\n      if domain && !domain.empty? && @account_type == :SidTypeDomain\n        @domain_account = @domain\n      elsif domain && !domain.empty?\n        @domain_account = \"#{domain}\\\\#{@account}\"\n      else\n        @domain_account = account\n      end\n    end\n\n    # added for backward compatibility\n    def ==(compare)\n      compare.is_a?(Puppet::Util::Windows::SID::Principal) &&\n        @sid_bytes == compare.sid_bytes\n    end\n\n    # returns authority qualified account name\n    # prefer to compare Principal instances with == operator or by #sid\n    def to_s\n      @domain_account\n    end\n\n    # = 8 + max sub identifiers (15) * 4\n    MAXIMUM_SID_BYTE_LENGTH = 68\n\n    ERROR_INVALID_PARAMETER   = 87\n    ERROR_INSUFFICIENT_BUFFER = 122\n\n    def self.lookup_account_name(system_name = nil, sanitize = true, account_name)\n      account_name = sanitize_account_name(account_name) if sanitize\n      system_name_ptr = FFI::Pointer::NULL\n      begin\n        if system_name\n          system_name_wide = Puppet::Util::Windows::String.wide_string(system_name)\n          system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide)\n        end\n\n        FFI::MemoryPointer.from_string_to_wide_string(account_name) do |account_name_ptr|\n          FFI::MemoryPointer.new(:byte, MAXIMUM_SID_BYTE_LENGTH) do |sid_ptr|\n            FFI::MemoryPointer.new(:dword, 1) do |sid_length_ptr|\n              FFI::MemoryPointer.new(:dword, 1) do |domain_length_ptr|\n                FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr|\n                  sid_length_ptr.write_dword(MAXIMUM_SID_BYTE_LENGTH)\n                  success = LookupAccountNameW(system_name_ptr, account_name_ptr, sid_ptr, sid_length_ptr,\n                                               FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr)\n                  last_error = FFI.errno\n\n                  if success == FFI::WIN32_FALSE && last_error != ERROR_INSUFFICIENT_BUFFER\n                    raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountNameW with account: %{account_name}') % { account_name: account_name }, last_error)\n                  end\n\n                  FFI::MemoryPointer.new(:lpwstr, domain_length_ptr.read_dword) do |domain_ptr|\n                    if LookupAccountNameW(system_name_ptr, account_name_ptr,\n                                          sid_ptr, sid_length_ptr,\n                                          domain_ptr, domain_length_ptr, name_use_enum_ptr) == FFI::WIN32_FALSE\n                      raise Puppet::Util::Windows::Error, _('Failed to call LookupAccountNameW with account: %{account_name}') % { account_name: account_name }\n                    end\n\n                    # with a SID returned, loop back through lookup_account_sid to retrieve official name\n                    # necessary when accounts like . or '' are passed in\n                    return lookup_account_sid(\n                      system_name,\n                      sid_ptr.read_bytes(sid_length_ptr.read_dword).unpack('C*')\n                    )\n                  end\n                end\n              end\n            end\n          end\n        end\n      ensure\n        system_name_ptr.free if system_name_ptr != FFI::Pointer::NULL\n      end\n    end\n\n    def self.lookup_account_sid(system_name = nil, sid_bytes)\n      system_name_ptr = FFI::Pointer::NULL\n      if sid_bytes.nil? || (!sid_bytes.is_a? Array) || (sid_bytes.length == 0)\n        # TRANSLATORS `lookup_account_sid` is a variable name and should not be translated\n        raise Puppet::Util::Windows::Error, _('Byte array for lookup_account_sid must not be nil and must be at least 1 byte long')\n      end\n\n      begin\n        if system_name\n          system_name_wide = Puppet::Util::Windows::String.wide_string(system_name)\n          system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide)\n        end\n\n        FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|\n          FFI::MemoryPointer.new(:dword, 1) do |name_length_ptr|\n            FFI::MemoryPointer.new(:dword, 1) do |domain_length_ptr|\n              FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr|\n                sid_ptr.write_array_of_uchar(sid_bytes)\n\n                if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE\n                  raise Puppet::Util::Windows::Error.new(_('Byte array for lookup_account_sid is invalid: %{sid_bytes}') % { sid_bytes: sid_bytes }, ERROR_INVALID_PARAMETER)\n                end\n\n                success = LookupAccountSidW(system_name_ptr, sid_ptr, FFI::Pointer::NULL, name_length_ptr,\n                                            FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr)\n                last_error = FFI.errno\n\n                if success == FFI::WIN32_FALSE && last_error != ERROR_INSUFFICIENT_BUFFER\n                  raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountSidW with bytes: %{sid_bytes}') % { sid_bytes: sid_bytes }, last_error)\n                end\n\n                FFI::MemoryPointer.new(:lpwstr, name_length_ptr.read_dword) do |name_ptr|\n                  FFI::MemoryPointer.new(:lpwstr, domain_length_ptr.read_dword) do |domain_ptr|\n                    if LookupAccountSidW(system_name_ptr, sid_ptr, name_ptr, name_length_ptr,\n                                         domain_ptr, domain_length_ptr, name_use_enum_ptr) == FFI::WIN32_FALSE\n                      raise Puppet::Util::Windows::Error, _('Failed to call LookupAccountSidW with bytes: %{sid_bytes}') % { sid_bytes: sid_bytes }\n                    end\n\n                    return new(\n                      name_ptr.read_wide_string(name_length_ptr.read_dword),\n                      sid_bytes,\n                      Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr),\n                      domain_ptr.read_wide_string(domain_length_ptr.read_dword),\n                      SID_NAME_USE[name_use_enum_ptr.read_uint32]\n                    )\n                  end\n                end\n              end\n            end\n          end\n        end\n      ensure\n        system_name_ptr.free if system_name_ptr != FFI::Pointer::NULL\n      end\n    end\n\n    # Sanitize the given account name for lookup to avoid known issues\n    def self.sanitize_account_name(account_name)\n      return account_name unless account_name.start_with?('APPLICATION PACKAGE AUTHORITY\\\\')\n\n      account_name.split('\\\\').last\n    end\n    private_class_method :sanitize_account_name\n\n    ffi_convention :stdcall\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379601(v=vs.85).aspx\n    SID_NAME_USE = enum(\n      :SidTypeUser, 1,\n      :SidTypeGroup, 2,\n      :SidTypeDomain, 3,\n      :SidTypeAlias, 4,\n      :SidTypeWellKnownGroup, 5,\n      :SidTypeDeletedAccount, 6,\n      :SidTypeInvalid, 7,\n      :SidTypeUnknown, 8,\n      :SidTypeComputer, 9,\n      :SidTypeLabel, 10\n    )\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379159(v=vs.85).aspx\n    # BOOL WINAPI LookupAccountName(\n    #   _In_opt_  LPCTSTR       lpSystemName,\n    #   _In_      LPCTSTR       lpAccountName,\n    #   _Out_opt_ PSID          Sid,\n    #   _Inout_   LPDWORD       cbSid,\n    #   _Out_opt_ LPTSTR        ReferencedDomainName,\n    #   _Inout_   LPDWORD       cchReferencedDomainName,\n    #   _Out_     PSID_NAME_USE peUse\n    # );\n    ffi_lib :advapi32\n    attach_function_private :LookupAccountNameW,\n                            [:lpcwstr, :lpcwstr, :pointer, :lpdword, :lpwstr, :lpdword, :pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379166(v=vs.85).aspx\n    # BOOL WINAPI LookupAccountSid(\n    #   _In_opt_  LPCTSTR       lpSystemName,\n    #   _In_      PSID          lpSid,\n    #   _Out_opt_ LPTSTR        lpName,\n    #   _Inout_   LPDWORD       cchName,\n    #   _Out_opt_ LPTSTR        lpReferencedDomainName,\n    #   _Inout_   LPDWORD       cchReferencedDomainName,\n    #   _Out_     PSID_NAME_USE peUse\n    # );\n    ffi_lib :advapi32\n    attach_function_private :LookupAccountSidW,\n                            [:lpcwstr, :pointer, :lpwstr, :lpdword, :lpwstr, :lpdword, :pointer], :win32_bool\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/process.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows/monkey_patches/process'\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::Util::Windows::Process\n  extend Puppet::FFI::Windows::Functions\n  include Puppet::FFI::Windows::Structs\n  extend Puppet::Util::Windows::String\n\n  WAIT_TIMEOUT = 0x102\n  WAIT_INTERVAL = 200\n  # https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-creation-flags\n  CREATE_NO_WINDOW = 0x08000000\n  # https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-security-and-access-rights\n  PROCESS_QUERY_INFORMATION = 0x0400\n  # https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation\n  MAX_PATH_LENGTH = 32_767\n\n  def execute(command, arguments, stdin, stdout, stderr)\n    create_args = {\n      :command_line => command,\n      :startup_info => {\n        :stdin => stdin,\n        :stdout => stdout,\n        :stderr => stderr\n      },\n      :close_handles => false,\n    }\n    if arguments[:suppress_window]\n      create_args[:creation_flags] = CREATE_NO_WINDOW\n    end\n    if arguments[:cwd]\n      create_args[:cwd] = arguments[:cwd]\n    end\n    Process.create(create_args)\n  end\n  module_function :execute\n\n  def wait_process(handle)\n    while WaitForSingleObject(handle, WAIT_INTERVAL) == WAIT_TIMEOUT\n      sleep(0)\n    end\n\n    exit_status = -1\n    FFI::MemoryPointer.new(:dword, 1) do |exit_status_ptr|\n      if GetExitCodeProcess(handle, exit_status_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Failed to get child process exit code\")\n      end\n\n      exit_status = exit_status_ptr.read_dword\n\n      # $CHILD_STATUS is not set when calling win32/process Process.create\n      # and since it's read-only, we can't set it. But we can execute a\n      # a shell that simply returns the desired exit status, which has the\n      # desired effect.\n      %x(#{ENV.fetch('COMSPEC', nil)} /c exit #{exit_status})\n    end\n\n    exit_status\n  end\n  module_function :wait_process\n\n  def get_current_process\n    # this pseudo-handle does not require closing per MSDN docs\n    GetCurrentProcess()\n  end\n  module_function :get_current_process\n\n  def open_process(desired_access, inherit_handle, process_id, &block)\n    phandle = nil\n    inherit = inherit_handle ? FFI::WIN32_TRUE : FFI::WIN32_FALSE\n    begin\n      phandle = OpenProcess(desired_access, inherit, process_id)\n      if phandle == FFI::Pointer::NULL_HANDLE\n        raise Puppet::Util::Windows::Error, \"OpenProcess(#{desired_access.to_s(8)}, #{inherit}, #{process_id})\"\n      end\n\n      yield phandle\n    ensure\n      FFI::WIN32.CloseHandle(phandle) if phandle\n    end\n\n    # phandle has had CloseHandle called against it, so nothing to return\n    nil\n  end\n  module_function :open_process\n\n  def open_process_token(handle, desired_access, &block)\n    token_handle = nil\n    begin\n      FFI::MemoryPointer.new(:handle, 1) do |token_handle_ptr|\n        result = OpenProcessToken(handle, desired_access, token_handle_ptr)\n        if result == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, \"OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr})\"\n        end\n\n        yield token_handle = token_handle_ptr.read_handle\n      end\n\n      token_handle\n    ensure\n      FFI::WIN32.CloseHandle(token_handle) if token_handle\n    end\n\n    # token_handle has had CloseHandle called against it, so nothing to return\n    nil\n  end\n  module_function :open_process_token\n\n  # Execute a block with the current process token\n  def with_process_token(access, &block)\n    handle = get_current_process\n    open_process_token(handle, access) do |token_handle|\n      yield token_handle\n    end\n\n    # all handles have been closed, so nothing to safely return\n    nil\n  end\n  module_function :with_process_token\n\n  def get_process_image_name_by_pid(pid)\n    image_name = ''.dup\n\n    Puppet::Util::Windows::Security.with_privilege(Puppet::Util::Windows::Security::SE_DEBUG_NAME) do\n      open_process(PROCESS_QUERY_INFORMATION, false, pid) do |phandle|\n        FFI::MemoryPointer.new(:dword, 1) do |exe_name_length_ptr|\n          # UTF is 2 bytes/char:\n          max_chars = MAX_PATH_LENGTH + 1\n          exe_name_length_ptr.write_dword(max_chars)\n          FFI::MemoryPointer.new(:wchar, max_chars) do |exe_name_ptr|\n            use_win32_path_format = 0\n            result = QueryFullProcessImageNameW(phandle, use_win32_path_format, exe_name_ptr, exe_name_length_ptr)\n            if result == FFI::WIN32_FALSE\n              raise Puppet::Util::Windows::Error, \"QueryFullProcessImageNameW(phandle, #{use_win32_path_format}, \" \\\n                                                  \"exe_name_ptr, #{max_chars}\"\n            end\n            image_name = exe_name_ptr.read_wide_string(exe_name_length_ptr.read_dword)\n          end\n        end\n      end\n    end\n\n    image_name\n  end\n  module_function :get_process_image_name_by_pid\n\n  def lookup_privilege_value(name, system_name = '', &block)\n    FFI::MemoryPointer.new(LUID.size) do |luid_ptr|\n      result = LookupPrivilegeValueW(\n        wide_string(system_name),\n        wide_string(name.to_s),\n        luid_ptr\n      )\n\n      if result == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, \"LookupPrivilegeValue(#{system_name}, #{name}, #{luid_ptr})\"\n      end\n\n      yield LUID.new(luid_ptr)\n    end\n\n    # the underlying MemoryPointer for LUID is cleaned up by this point\n    nil\n  end\n  module_function :lookup_privilege_value\n\n  def get_token_information(token_handle, token_information, &block)\n    # to determine buffer size\n    FFI::MemoryPointer.new(:dword, 1) do |return_length_ptr|\n      result = GetTokenInformation(token_handle, token_information, nil, 0, return_length_ptr)\n      return_length = return_length_ptr.read_dword\n\n      if return_length <= 0\n        raise Puppet::Util::Windows::Error, \"GetTokenInformation(#{token_handle}, #{token_information}, nil, 0, #{return_length_ptr})\"\n      end\n\n      # re-call API with properly sized buffer for all results\n      FFI::MemoryPointer.new(return_length) do |token_information_buf|\n        result = GetTokenInformation(token_handle, token_information,\n                                     token_information_buf, return_length, return_length_ptr)\n\n        if result == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, \"GetTokenInformation(#{token_handle}, #{token_information}, #{token_information_buf}, \" \\\n                                              \"#{return_length}, #{return_length_ptr})\"\n        end\n\n        yield token_information_buf\n      end\n    end\n\n    # GetTokenInformation buffer has been cleaned up by this point, nothing to return\n    nil\n  end\n  module_function :get_token_information\n\n  def parse_token_information_as_token_privileges(token_information_buf)\n    raw_privileges = TOKEN_PRIVILEGES.new(token_information_buf)\n    privileges = { :count => raw_privileges[:PrivilegeCount], :privileges => [] }\n\n    offset = token_information_buf + TOKEN_PRIVILEGES.offset_of(:Privileges)\n    privilege_ptr = FFI::Pointer.new(LUID_AND_ATTRIBUTES, offset)\n\n    # extract each instance of LUID_AND_ATTRIBUTES\n    0.upto(privileges[:count] - 1) do |i|\n      privileges[:privileges] << LUID_AND_ATTRIBUTES.new(privilege_ptr[i])\n    end\n\n    privileges\n  end\n  module_function :parse_token_information_as_token_privileges\n\n  def parse_token_information_as_token_elevation(token_information_buf)\n    TOKEN_ELEVATION.new(token_information_buf)\n  end\n  module_function :parse_token_information_as_token_elevation\n\n  TOKEN_ALL_ACCESS = 0xF01FF\n  ERROR_NO_SUCH_PRIVILEGE = 1313\n  def process_privilege_symlink?\n    privilege_symlink = false\n    handle = get_current_process\n    open_process_token(handle, TOKEN_ALL_ACCESS) do |token_handle|\n      lookup_privilege_value('SeCreateSymbolicLinkPrivilege') do |luid|\n        get_token_information(token_handle, :TokenPrivileges) do |token_info|\n          token_privileges = parse_token_information_as_token_privileges(token_info)\n          privilege_symlink = token_privileges[:privileges].any? { |p| p[:Luid].values == luid.values }\n        end\n      end\n    end\n\n    privilege_symlink\n  rescue Puppet::Util::Windows::Error => e\n    if e.code == ERROR_NO_SUCH_PRIVILEGE\n      false # pre-Vista\n    else\n      raise e\n    end\n  end\n  module_function :process_privilege_symlink?\n\n  TOKEN_QUERY = 0x0008\n  # Returns whether or not the owner of the current process is running\n  # with elevated security privileges.\n  #\n  # Only supported on Windows Vista or later.\n  #\n  def elevated_security?\n    # default / pre-Vista\n    elevated = false\n    handle = nil\n\n    begin\n      handle = get_current_process\n      open_process_token(handle, TOKEN_QUERY) do |token_handle|\n        get_token_information(token_handle, :TokenElevation) do |token_info|\n          token_elevation = parse_token_information_as_token_elevation(token_info)\n          # TokenIsElevated member of the TOKEN_ELEVATION struct\n          elevated = token_elevation[:TokenIsElevated] != 0\n        end\n      end\n\n      elevated\n    rescue Puppet::Util::Windows::Error => e\n      raise e if e.code != ERROR_NO_SUCH_PRIVILEGE\n    ensure\n      FFI::WIN32.CloseHandle(handle) if handle\n    end\n  end\n  module_function :elevated_security?\n\n  def windows_major_version\n    ver = 0\n\n    FFI::MemoryPointer.new(OSVERSIONINFO.size) do |os_version_ptr|\n      os_version = OSVERSIONINFO.new(os_version_ptr)\n      os_version[:dwOSVersionInfoSize] = OSVERSIONINFO.size\n\n      result = GetVersionExW(os_version_ptr)\n\n      if result == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"GetVersionEx failed\")\n      end\n\n      ver = os_version[:dwMajorVersion]\n    end\n\n    ver\n  end\n  module_function :windows_major_version\n\n  # Returns a hash of the current environment variables encoded as UTF-8\n  # The memory block returned from GetEnvironmentStringsW is double-null terminated and the vars are paired as below;\n  # Var1=Value1\\0\n  # Var2=Value2\\0\n  # VarX=ValueX\\0\\0\n  # Note - Some env variable names start with '=' and are excluded from the return value\n  # Note - The env_ptr MUST be freed using the FreeEnvironmentStringsW function\n  # Note - There is no technical limitation to the size of the environment block returned.\n  #   However a practical limit of 64K is used as no single environment variable can exceed 32KB\n  def get_environment_strings\n    env_ptr = GetEnvironmentStringsW()\n\n    # pass :invalid => :replace to the Ruby String#encode to use replacement characters\n    pairs = env_ptr.read_arbitrary_wide_string_up_to(65_534, :double_null, { :invalid => :replace })\n                   .split(?\\x00)\n                   .reject { |env_str| env_str.nil? || env_str.empty? || env_str[0] == '=' }\n                   .reject do |env_str|\n                     # reject any string containing the Unicode replacement character\n                     if env_str.include?(\"\\uFFFD\")\n                       Puppet.warning(_(\"Discarding environment variable %{string} which contains invalid bytes\") % { string: env_str })\n                       true\n                     end\n                   end\n                   .map { |env_pair| env_pair.split('=', 2) }\n    pairs.to_h\n  ensure\n    if env_ptr && !env_ptr.null?\n      if FreeEnvironmentStringsW(env_ptr) == FFI::WIN32_FALSE\n        Puppet.debug \"FreeEnvironmentStringsW memory leak\"\n      end\n    end\n  end\n  module_function :get_environment_strings\n\n  def set_environment_variable(name, val)\n    raise Puppet::Util::Windows::Error(_('environment variable name must not be nil or empty')) if !name || name.empty?\n\n    FFI::MemoryPointer.from_string_to_wide_string(name) do |name_ptr|\n      if val.nil?\n        if SetEnvironmentVariableW(name_ptr, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to remove environment variable: %{name}\") % { name: name }\n        end\n      else\n        FFI::MemoryPointer.from_string_to_wide_string(val) do |val_ptr|\n          if SetEnvironmentVariableW(name_ptr, val_ptr) == FFI::WIN32_FALSE\n            raise Puppet::Util::Windows::Error, _(\"Failed to set environment variable: %{name}\") % { name: name }\n          end\n        end\n      end\n    end\n  end\n  module_function :set_environment_variable\n\n  def get_system_default_ui_language\n    GetSystemDefaultUILanguage()\n  end\n  module_function :get_system_default_ui_language\n\n  # Returns whether or not the OS has the ability to set elevated\n  # token information.\n  #\n  # Returns true on Windows Vista or later, otherwise false\n  #\n  def supports_elevated_security?\n    windows_major_version >= 6\n  end\n  module_function :supports_elevated_security?\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/registry.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\n\nmodule Puppet::Util::Windows\n  module Registry\n    require 'ffi'\n    extend FFI::Library\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa384129(v=vs.85).aspx\n    KEY64 = 0x100\n    KEY32 = 0x200\n\n    KEY_READ       = 0x20019\n    KEY_WRITE      = 0x20006\n    KEY_ALL_ACCESS = 0x2003f\n\n    ERROR_NO_MORE_ITEMS = 259\n\n    WCHAR_SIZE = FFI.type_size(:wchar)\n\n    def root(name)\n      Win32::Registry.const_get(name)\n    rescue NameError => e\n      raise Puppet::Error, _(\"Invalid registry key '%{name}'\") % { name: name }, e.backtrace\n    end\n\n    def open(name, path, mode = KEY_READ | KEY64, &block)\n      hkey = root(name)\n      begin\n        hkey.open(path, mode) do |subkey|\n          return yield subkey\n        end\n      rescue Win32::Registry::Error => error\n        raise Puppet::Util::Windows::Error.new(_(\"Failed to open registry key '%{key}\\\\%{path}'\") % { key: hkey.keyname, path: path }, error.code, error)\n      end\n    end\n\n    def keys(key)\n      keys = {}\n      each_key(key) { |subkey, filetime| keys[subkey] = filetime }\n      keys\n    end\n\n    # subkey is String which contains name of subkey.\n    # wtime is last write time as FILETIME (64-bit integer). (see Registry.wtime2time)\n    def each_key(key, &block)\n      index = 0\n      subkey = nil\n\n      subkey_max_len, _ = reg_query_info_key_max_lengths(key)\n\n      loop do\n        subkey, filetime = reg_enum_key(key, index, subkey_max_len)\n        yield subkey, filetime unless subkey.nil?\n        index += 1\n        break if subkey.nil?\n      end\n\n      index\n    end\n\n    def delete_key(key, subkey_name, mode = KEY64)\n      reg_delete_key_ex(key, subkey_name, mode)\n    end\n\n    def values(key)\n      vals = {}\n      each_value(key) { |subkey, _type, data| vals[subkey] = data }\n      vals\n    end\n\n    # Retrieve a set of values from a registry key given their names\n    # Value names listed but not found in the registry will not be added to the\n    # resultant Hashtable\n    #\n    # @param key [RegistryKey] An open handle to a Registry Key\n    # @param names [String[]] An array of names of registry values to return if they exist\n    # @return [Hashtable<String, Object>] A hashtable of all of the found values in the registry key\n    def values_by_name(key, names)\n      vals = {}\n      names.each do |name|\n        FFI::Pointer.from_string_to_wide_string(name) do |subkeyname_ptr|\n          _, vals[name] = read(key, subkeyname_ptr)\n        rescue Puppet::Util::Windows::Error => e\n          # ignore missing names, but raise other errors\n          raise e unless e.code == Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND\n        end\n      end\n      vals\n    end\n\n    def each_value(key, &block)\n      index = 0\n      subkey = nil\n\n      _, value_max_len = reg_query_info_key_max_lengths(key)\n\n      loop do\n        subkey, type, data = reg_enum_value(key, index, value_max_len)\n        yield subkey, type, data unless subkey.nil?\n        index += 1\n        break if subkey.nil?\n      end\n\n      index\n    end\n\n    def delete_value(key, subkey_name)\n      reg_delete_value(key, subkey_name)\n    end\n\n    private\n\n    # max number of wide characters including NULL terminator\n    MAX_KEY_CHAR_LENGTH = 255 + 1\n\n    def reg_enum_key(key, index, max_key_char_length = MAX_KEY_CHAR_LENGTH)\n      subkey = nil\n      filetime = nil\n\n      FFI::MemoryPointer.new(:dword) do |subkey_length_ptr|\n        FFI::MemoryPointer.new(FFI::WIN32::FILETIME.size) do |filetime_ptr|\n          FFI::MemoryPointer.new(:wchar, max_key_char_length) do |subkey_ptr|\n            subkey_length_ptr.write_dword(max_key_char_length)\n\n            # RegEnumKeyEx cannot be called twice to properly size the buffer\n            result = RegEnumKeyExW(key.hkey, index,\n                                   subkey_ptr, subkey_length_ptr,\n                                   FFI::Pointer::NULL, FFI::Pointer::NULL,\n                                   FFI::Pointer::NULL, filetime_ptr)\n\n            break if result == ERROR_NO_MORE_ITEMS\n\n            if result != FFI::ERROR_SUCCESS\n              msg = _(\"Failed to enumerate %{key} registry keys at index %{index}\") % { key: key.keyname, index: index }\n              raise Puppet::Util::Windows::Error.new(msg, result)\n            end\n\n            filetime = FFI::WIN32::FILETIME.new(filetime_ptr)\n            subkey_length = subkey_length_ptr.read_dword\n            subkey = subkey_ptr.read_wide_string(subkey_length)\n          end\n        end\n      end\n\n      [subkey, filetime]\n    end\n\n    # max number of wide characters including NULL terminator\n    MAX_VALUE_CHAR_LENGTH = 16_383 + 1\n\n    def reg_enum_value(key, index, max_value_length = MAX_VALUE_CHAR_LENGTH)\n      subkey = nil\n      type = nil\n      data = nil\n\n      FFI::MemoryPointer.new(:dword) do |subkey_length_ptr|\n        FFI::MemoryPointer.new(:wchar, max_value_length) do |subkey_ptr|\n          # RegEnumValueW cannot be called twice to properly size the buffer\n          subkey_length_ptr.write_dword(max_value_length)\n\n          result = RegEnumValueW(key.hkey, index,\n                                 subkey_ptr, subkey_length_ptr,\n                                 FFI::Pointer::NULL, FFI::Pointer::NULL,\n                                 FFI::Pointer::NULL, FFI::Pointer::NULL)\n\n          break if result == ERROR_NO_MORE_ITEMS\n\n          if result != FFI::ERROR_SUCCESS\n            msg = _(\"Failed to enumerate %{key} registry values at index %{index}\") % { key: key.keyname, index: index }\n            raise Puppet::Util::Windows::Error.new(msg, result)\n          end\n\n          subkey_length = subkey_length_ptr.read_dword\n          subkey = subkey_ptr.read_wide_string(subkey_length)\n\n          type, data = read(key, subkey_ptr)\n        end\n      end\n\n      [subkey, type, data]\n    end\n\n    def reg_query_info_key_max_lengths(key)\n      result = nil\n\n      FFI::MemoryPointer.new(:dword) do |max_subkey_name_length_ptr|\n        FFI::MemoryPointer.new(:dword) do |max_value_name_length_ptr|\n          status = RegQueryInfoKeyW(key.hkey,\n                                    FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL,\n                                    FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL,\n                                    max_subkey_name_length_ptr, FFI::MemoryPointer::NULL,\n                                    FFI::MemoryPointer::NULL, max_value_name_length_ptr,\n                                    FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL,\n                                    FFI::MemoryPointer::NULL)\n\n          if status != FFI::ERROR_SUCCESS\n            msg = _(\"Failed to query registry %{key} for sizes\") % { key: key.keyname }\n            raise Puppet::Util::Windows::Error.new(msg, status)\n          end\n\n          result = [\n            # Unicode characters *not* including trailing NULL\n            max_subkey_name_length_ptr.read_dword + 1,\n            max_value_name_length_ptr.read_dword + 1\n          ]\n        end\n      end\n\n      result\n    end\n\n    # Read a registry value named name and return array of\n    # [ type, data ].\n    # When name is nil, the `default' value is read.\n    # type is value type. (see Win32::Registry::Constants module)\n    # data is value data, its class is:\n    # :REG_SZ, REG_EXPAND_SZ\n    #    String\n    # :REG_MULTI_SZ\n    #    Array of String\n    # :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD\n    #    Integer\n    # :REG_BINARY\n    #    String (contains binary data)\n    #\n    # When rtype is specified, the value type must be included by\n    # rtype array, or TypeError is raised.\n    def read(key, name_ptr, *rtype)\n      result = nil\n\n      query_value_ex(key, name_ptr) do |type, data_ptr, byte_length|\n        unless rtype.empty? or rtype.include?(type)\n          raise TypeError, _(\"Type mismatch (expect %{rtype} but %{type} present)\") % { rtype: rtype.inspect, type: type }\n        end\n\n        string_length = 0\n        # buffer is raw bytes, *not* chars - less a NULL terminator\n        string_length = (byte_length / WCHAR_SIZE) - 1 if byte_length > 0\n\n        begin\n          result = case type\n                   when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ\n                     [type, data_ptr.read_wide_string(string_length, Encoding::UTF_8, true)]\n                   when Win32::Registry::REG_MULTI_SZ\n                     [type, data_ptr.read_wide_string(string_length).split(/\\0/)]\n                   when Win32::Registry::REG_BINARY\n                     [type, data_ptr.read_bytes(byte_length)]\n                   when Win32::Registry::REG_DWORD\n                     [type, data_ptr.read_dword]\n                   when Win32::Registry::REG_DWORD_BIG_ENDIAN\n                     [type, data_ptr.order(:big).read_dword]\n                   when Win32::Registry::REG_QWORD\n                     [type, data_ptr.read_qword]\n                   else\n                     raise TypeError, _(\"Type %{type} is not supported.\") % { type: type }\n                   end\n        rescue IndexError => ex\n          raise if ex.message !~ /^Memory access .* is out of bounds$/i\n\n          parent_key_name = key.parent ? \"#{key.parent.keyname}\\\\\" : \"\"\n          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 }\n        end\n      end\n\n      result\n    end\n\n    def query_value_ex(key, name_ptr, &block)\n      FFI::MemoryPointer.new(:dword) do |type_ptr|\n        FFI::MemoryPointer.new(:dword) do |length_ptr|\n          result = RegQueryValueExW(key.hkey, name_ptr,\n                                    FFI::Pointer::NULL, type_ptr,\n                                    FFI::Pointer::NULL, length_ptr)\n\n          # The call to RegQueryValueExW below is potentially unsafe:\n          # https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw\n          #\n          #   \"If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,\n          #   the string may not have been stored with the proper terminating\n          #   null characters. Therefore, even if the function returns\n          #   ERROR_SUCCESS, the application should ensure that the string is\n          #   properly terminated before using it; otherwise, it may overwrite a\n          #   buffer. (Note that REG_MULTI_SZ strings should have two\n          #   terminating null characters.)\"\n          #\n          # Since we don't know if the values will be properly null terminated,\n          # extend the buffer to guarantee we can append one or two wide null\n          # characters, without overwriting any data.\n          base_bytes_len = length_ptr.read_dword\n          pad_bytes_len = case type_ptr.read_dword\n                          when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ\n                            WCHAR_SIZE\n                          when Win32::Registry::REG_MULTI_SZ\n                            WCHAR_SIZE * 2\n                          else\n                            0\n                          end\n\n          FFI::MemoryPointer.new(:byte, base_bytes_len + pad_bytes_len) do |buffer_ptr|\n            result = RegQueryValueExW(key.hkey, name_ptr,\n                                      FFI::Pointer::NULL, type_ptr,\n                                      buffer_ptr, length_ptr)\n\n            # Ensure buffer is null terminated with 1 or 2 wchar nulls, depending on the type\n            if result == FFI::ERROR_SUCCESS\n              case type_ptr.read_dword\n              when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ\n                buffer_ptr.put_uint16(base_bytes_len, 0)\n              when Win32::Registry::REG_MULTI_SZ\n                buffer_ptr.put_uint16(base_bytes_len, 0)\n                buffer_ptr.put_uint16(base_bytes_len + WCHAR_SIZE, 0)\n              end\n            else\n              # buffer is raw bytes, *not* chars - less a NULL terminator\n              name_length = (name_ptr.size / WCHAR_SIZE) - 1 if name_ptr.size > 0\n              msg = _(\"Failed to read registry value %{value} at %{key}\") % { value: name_ptr.read_wide_string(name_length), key: key.keyname }\n              raise Puppet::Util::Windows::Error.new(msg, result)\n            end\n\n            # allows caller to use FFI MemoryPointer helpers to read / shape\n            yield [type_ptr.read_dword, buffer_ptr, length_ptr.read_dword]\n          end\n        end\n      end\n    end\n\n    def reg_delete_value(key, name)\n      result = 0\n\n      FFI::Pointer.from_string_to_wide_string(name) do |name_ptr|\n        result = RegDeleteValueW(key.hkey, name_ptr)\n\n        if result != FFI::ERROR_SUCCESS\n          msg = _(\"Failed to delete registry value %{name} at %{key}\") % { name: name, key: key.keyname }\n          raise Puppet::Util::Windows::Error.new(msg, result)\n        end\n      end\n\n      result\n    end\n\n    def reg_delete_key_ex(key, name, regsam = KEY64)\n      result = 0\n\n      FFI::Pointer.from_string_to_wide_string(name) do |name_ptr|\n        result = RegDeleteKeyExW(key.hkey, name_ptr, regsam, 0)\n\n        if result != FFI::ERROR_SUCCESS\n          msg = _(\"Failed to delete registry key %{name} at %{key}\") % { name: name, key: key.keyname }\n          raise Puppet::Util::Windows::Error.new(msg, result)\n        end\n      end\n\n      result\n    end\n\n    ffi_convention :stdcall\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724862(v=vs.85).aspx\n    # LONG WINAPI RegEnumKeyEx(\n    #   _In_         HKEY hKey,\n    #   _In_         DWORD dwIndex,\n    #   _Out_        LPTSTR lpName,\n    #   _Inout_      LPDWORD lpcName,\n    #   _Reserved_   LPDWORD lpReserved,\n    #   _Inout_      LPTSTR lpClass,\n    #   _Inout_opt_  LPDWORD lpcClass,\n    #   _Out_opt_    PFILETIME lpftLastWriteTime\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegEnumKeyExW,\n                            [:handle, :dword, :lpwstr, :lpdword, :lpdword, :lpwstr, :lpdword, :pointer], :win32_long\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724865(v=vs.85).aspx\n    # LONG WINAPI RegEnumValue(\n    #   _In_         HKEY hKey,\n    #   _In_         DWORD dwIndex,\n    #   _Out_        LPTSTR lpValueName,\n    #   _Inout_      LPDWORD lpcchValueName,\n    #   _Reserved_   LPDWORD lpReserved,\n    #   _Out_opt_    LPDWORD lpType,\n    #   _Out_opt_    LPBYTE lpData,\n    #   _Inout_opt_  LPDWORD lpcbData\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegEnumValueW,\n                            [:handle, :dword, :lpwstr, :lpdword, :lpdword, :lpdword, :lpbyte, :lpdword], :win32_long\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx\n    # LONG WINAPI RegQueryValueExW(\n    #   _In_         HKEY hKey,\n    #   _In_opt_     LPCTSTR lpValueName,\n    #   _Reserved_   LPDWORD lpReserved,\n    #   _Out_opt_    LPDWORD lpType,\n    #   _Out_opt_    LPBYTE lpData,\n    #   _Inout_opt_  LPDWORD lpcbData\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegQueryValueExW,\n                            [:handle, :lpcwstr, :lpdword, :lpdword, :lpbyte, :lpdword], :win32_long\n\n    # LONG WINAPI RegDeleteValue(\n    #   _In_      HKEY hKey,\n    #   _In_opt_  LPCTSTR lpValueName\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegDeleteValueW,\n                            [:handle, :lpcwstr], :win32_long\n\n    # LONG WINAPI RegDeleteKeyEx(\n    #   _In_        HKEY hKey,\n    #   _In_        LPCTSTR lpSubKey,\n    #   _In_        REGSAM samDesired,\n    #   _Reserved_  DWORD Reserved\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegDeleteKeyExW,\n                            [:handle, :lpcwstr, :win32_ulong, :dword], :win32_long\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724902(v=vs.85).aspx\n    # LONG WINAPI RegQueryInfoKey(\n    #   _In_         HKEY hKey,\n    #   _Out_opt_    LPTSTR lpClass,\n    #   _Inout_opt_  LPDWORD lpcClass,\n    #   _Reserved_   LPDWORD lpReserved,\n    #   _Out_opt_    LPDWORD lpcSubKeys,\n    #   _Out_opt_    LPDWORD lpcMaxSubKeyLen,\n    #   _Out_opt_    LPDWORD lpcMaxClassLen,\n    #   _Out_opt_    LPDWORD lpcValues,\n    #   _Out_opt_    LPDWORD lpcMaxValueNameLen,\n    #   _Out_opt_    LPDWORD lpcMaxValueLen,\n    #   _Out_opt_    LPDWORD lpcbSecurityDescriptor,\n    #   _Out_opt_    PFILETIME lpftLastWriteTime\n    # );\n    ffi_lib :advapi32\n    attach_function_private :RegQueryInfoKeyW,\n                            [:handle, :lpwstr, :lpdword, :lpdword, :lpdword,\n                             :lpdword, :lpdword, :lpdword, :lpdword, :lpdword,\n                             :lpdword, :pointer], :win32_long\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/root_certs.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\nrequire_relative '../../../puppet/ssl/openssl_loader'\nrequire 'ffi'\n\n# Represents a collection of trusted root certificates.\n#\n# @api public\nclass Puppet::Util::Windows::RootCerts\n  include Enumerable\n  extend FFI::Library\n\n  def initialize(roots)\n    @roots = roots\n  end\n\n  # Enumerates each root certificate.\n  # @yieldparam cert [OpenSSL::X509::Certificate] each root certificate\n  # @api public\n  def each\n    @roots.each { |cert| yield cert }\n  end\n\n  # Returns a new instance.\n  # @return [Puppet::Util::Windows::RootCerts] object constructed from current root certificates\n  def self.instance\n    new(load_certs)\n  end\n\n  # Returns an array of root certificates.\n  #\n  # @return [Array<[OpenSSL::X509::Certificate]>] an array of root certificates\n  # @api private\n  def self.load_certs\n    certs = []\n\n    # This is based on a patch submitted to openssl:\n    # https://www.mail-archive.com/openssl-dev@openssl.org/msg26958.html\n    ptr = FFI::Pointer::NULL\n    store = CertOpenSystemStoreA(nil, \"ROOT\")\n    begin\n      while (ptr = CertEnumCertificatesInStore(store, ptr)) and !ptr.null?\n        context = CERT_CONTEXT.new(ptr)\n        cert_buf = context[:pbCertEncoded].read_bytes(context[:cbCertEncoded])\n        begin\n          certs << OpenSSL::X509::Certificate.new(cert_buf)\n        rescue => detail\n          Puppet.warning(_(\"Failed to import root certificate: %{detail}\") % { detail: detail.inspect })\n        end\n      end\n    ensure\n      CertCloseStore(store, 0)\n    end\n\n    certs\n  end\n\n  ffi_convention :stdcall\n  # typedef void *HCERTSTORE;\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa377189(v=vs.85).aspx\n  # typedef struct _CERT_CONTEXT {\n  #   DWORD      dwCertEncodingType;\n  #   BYTE       *pbCertEncoded;\n  #   DWORD      cbCertEncoded;\n  #   PCERT_INFO pCertInfo;\n  #   HCERTSTORE hCertStore;\n  # } CERT_CONTEXT, *PCERT_CONTEXT;typedef const CERT_CONTEXT *PCCERT_CONTEXT;\n  class CERT_CONTEXT < FFI::Struct\n    layout(\n      :dwCertEncodingType, :dword,\n      :pbCertEncoded,      :pointer,\n      :cbCertEncoded,      :dword,\n      :pCertInfo,          :pointer,\n      :hCertStore,         :handle\n    )\n  end\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376560(v=vs.85).aspx\n  # HCERTSTORE\n  # WINAPI\n  # CertOpenSystemStoreA(\n  #   __in_opt HCRYPTPROV_LEGACY hProv,\n  #   __in LPCSTR szSubsystemProtocol\n  #   );\n  # typedef ULONG_PTR HCRYPTPROV_LEGACY;\n  ffi_lib :crypt32\n  attach_function_private :CertOpenSystemStoreA, [:ulong_ptr, :lpcstr], :handle\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376050(v=vs.85).aspx\n  # PCCERT_CONTEXT\n  # WINAPI\n  # CertEnumCertificatesInStore(\n  #   __in HCERTSTORE hCertStore,\n  #   __in_opt PCCERT_CONTEXT pPrevCertContext\n  #   );\n  ffi_lib :crypt32\n  attach_function_private :CertEnumCertificatesInStore, [:handle, :pointer], :pointer\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376026(v=vs.85).aspx\n  # BOOL\n  # WINAPI\n  # CertCloseStore(\n  #   __in_opt HCERTSTORE hCertStore,\n  #   __in DWORD dwFlags\n  #   );\n  ffi_lib :crypt32\n  attach_function_private :CertCloseStore, [:handle, :dword], :win32_bool\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/security.rb",
    "content": "# frozen_string_literal: true\n\n# This class maps POSIX owner, group, and modes to the Windows\n# security model, and back.\n#\n# The primary goal of this mapping is to ensure that owner, group, and\n# modes can be round-tripped in a consistent and deterministic\n# way. Otherwise, Puppet might think file resources are out-of-sync\n# every time it runs. A secondary goal is to provide equivalent\n# permissions for common use-cases. For example, setting the owner to\n# \"Administrators\", group to \"Users\", and mode to 750 (which also\n# denies access to everyone else.\n#\n# There are some well-known problems mapping windows and POSIX\n# permissions due to differences between the two security\n# models. Search for \"POSIX permission mapping leak\". In POSIX, access\n# to a file is determined solely based on the most specific class\n# (user, group, other). So a mode of 460 would deny write access to\n# the owner even if they are a member of the group. But in Windows,\n# the entire access control list is walked until the user is\n# explicitly denied or allowed (denied take precedence, and if neither\n# occurs they are denied). As a result, a user could be allowed access\n# based on their group membership. To solve this problem, other people\n# have used deny access control entries to more closely model POSIX,\n# but this introduces a lot of complexity.\n#\n# In general, this implementation only supports \"typical\" permissions,\n# where group permissions are a subset of user, and other permissions\n# are a subset of group, e.g. 754, but not 467.  However, there are\n# some Windows quirks to be aware of.\n#\n# * The owner can be either a user or group SID, and most system files\n#   are owned by the Administrators group.\n# * The group can be either a user or group SID.\n# * Unexpected results can occur if the owner and group are the\n#   same, but the user and group classes are different, e.g. 750. In\n#   this case, it is not possible to allow write access to the owner,\n#   but not the group. As a result, the actual permissions set on the\n#   file would be 770.\n# * In general, only privileged users can set the owner, group, or\n#   change the mode for files they do not own. In 2003, the user must\n#   be a member of the Administrators group. In Vista/2008, the user\n#   must be running with elevated privileges.\n# * A file/dir can be deleted by anyone with the DELETE access right\n#   OR by anyone that has the FILE_DELETE_CHILD access right for the\n#   parent. See https://support.microsoft.com/kb/238018. But on Unix,\n#   the user must have write access to the file/dir AND execute access\n#   to all of the parent path components.\n# * Many access control entries are inherited from parent directories,\n#   and it is common for file/dirs to have more than 3 entries,\n#   e.g. Users, Power Users, Administrators, SYSTEM, etc, which cannot\n#   be mapped into the 3 class POSIX model. The get_mode method will\n#   set the S_IEXTRA bit flag indicating that an access control entry\n#   was found whose SID is neither the owner, group, or other. This\n#   enables Puppet to detect when file/dirs are out-of-sync,\n#   especially those that Puppet did not create, but is attempting\n#   to manage.\n# * A special case of this is S_ISYSTEM_MISSING, which is set when the\n#   SYSTEM permissions are *not* present on the DACL.\n# * On Unix, the owner and group can be modified without changing the\n#   mode. But on Windows, an access control entry specifies which SID\n#   it applies to. As a result, the set_owner and set_group methods\n#   automatically rebuild the access control list based on the new\n#   (and different) owner or group.\n\nrequire_relative '../../../puppet/util/windows'\nrequire 'pathname'\nrequire 'ffi'\n\nmodule Puppet::Util::Windows::Security\n  include Puppet::Util::Windows::String\n\n  extend Puppet::Util::Windows::Security\n  extend FFI::Library\n\n  # file modes\n  S_IRUSR = 0o000400\n  S_IRGRP = 0o000040\n  S_IROTH = 0o000004\n  S_IWUSR = 0o000200\n  S_IWGRP = 0o000020\n  S_IWOTH = 0o000002\n  S_IXUSR = 0o000100\n  S_IXGRP = 0o000010\n  S_IXOTH = 0o000001\n  S_IRWXU = 0o000700\n  S_IRWXG = 0o000070\n  S_IRWXO = 0o000007\n  S_ISVTX = 0o001000\n  S_IEXTRA = 0o2000000 # represents an extra ace\n  S_ISYSTEM_MISSING = 0o4000000\n\n  # constants that are missing from Windows::Security\n  PROTECTED_DACL_SECURITY_INFORMATION   = 0x80000000\n  UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000\n  NO_INHERITANCE = 0x0\n  SE_DACL_PROTECTED = 0x1000\n\n  FILE = Puppet::Util::Windows::File\n\n  SE_BACKUP_NAME              = 'SeBackupPrivilege'\n  SE_DEBUG_NAME               = 'SeDebugPrivilege'\n  SE_RESTORE_NAME             = 'SeRestorePrivilege'\n\n  DELETE                      = 0x00010000\n  READ_CONTROL                = 0x20000\n  WRITE_DAC                   = 0x40000\n  WRITE_OWNER                 = 0x80000\n\n  OWNER_SECURITY_INFORMATION  = 1\n  GROUP_SECURITY_INFORMATION  = 2\n  DACL_SECURITY_INFORMATION   = 4\n\n  # Set the owner of the object referenced by +path+ to the specified\n  # +owner_sid+.  The owner sid should be of the form \"S-1-5-32-544\"\n  # and can either be a user or group.  Only a user with the\n  # SE_RESTORE_NAME privilege in their process token can overwrite the\n  # object's owner to something other than the current user.\n  def set_owner(owner_sid, path)\n    sd = get_security_descriptor(path)\n\n    if owner_sid != sd.owner\n      sd.owner = owner_sid\n      set_security_descriptor(path, sd)\n    end\n  end\n\n  # Get the owner of the object referenced by +path+.  The returned\n  # value is a SID string, e.g. \"S-1-5-32-544\".  Any user with read\n  # access to an object can get the owner. Only a user with the\n  # SE_BACKUP_NAME privilege in their process token can get the owner\n  # for objects they do not have read access to.\n  def get_owner(path)\n    return unless supports_acl?(path)\n\n    get_security_descriptor(path).owner\n  end\n\n  # Set the owner of the object referenced by +path+ to the specified\n  # +group_sid+.  The group sid should be of the form \"S-1-5-32-544\"\n  # and can either be a user or group.  Any user with WRITE_OWNER\n  # access to the object can change the group (regardless of whether\n  # the current user belongs to that group or not).\n  def set_group(group_sid, path)\n    sd = get_security_descriptor(path)\n\n    if group_sid != sd.group\n      sd.group = group_sid\n      set_security_descriptor(path, sd)\n    end\n  end\n\n  # Get the group of the object referenced by +path+.  The returned\n  # value is a SID string, e.g. \"S-1-5-32-544\".  Any user with read\n  # access to an object can get the group. Only a user with the\n  # SE_BACKUP_NAME privilege in their process token can get the group\n  # for objects they do not have read access to.\n  def get_group(path)\n    return unless supports_acl?(path)\n\n    get_security_descriptor(path).group\n  end\n\n  FILE_PERSISTENT_ACLS = 0x00000008\n\n  def supports_acl?(path)\n    supported = false\n    root = Pathname.new(path).enum_for(:ascend).to_a.last.to_s\n    # 'A trailing backslash is required'\n    root = \"#{root}\\\\\" unless root =~ %r{[/\\\\]$}\n\n    FFI::MemoryPointer.new(:pointer, 1) do |flags_ptr|\n      if GetVolumeInformationW(wide_string(root), FFI::Pointer::NULL, 0,\n                               FFI::Pointer::NULL, FFI::Pointer::NULL,\n                               flags_ptr, FFI::Pointer::NULL, 0) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Failed to get volume information\")\n      end\n\n      supported = flags_ptr.read_dword & FILE_PERSISTENT_ACLS == FILE_PERSISTENT_ACLS\n    end\n\n    supported\n  end\n\n  MASK_TO_MODE = {\n    FILE::FILE_GENERIC_READ => S_IROTH,\n    FILE::FILE_GENERIC_WRITE => S_IWOTH,\n    (FILE::FILE_GENERIC_EXECUTE & ~FILE::FILE_READ_ATTRIBUTES) => S_IXOTH\n  }\n\n  def get_aces_for_path_by_sid(path, sid)\n    get_security_descriptor(path).dacl.select { |ace| ace.sid == sid }\n  end\n\n  # Get the mode of the object referenced by +path+.  The returned\n  # integer value represents the POSIX-style read, write, and execute\n  # modes for the user, group, and other classes, e.g. 0640.  Any user\n  # with read access to an object can get the mode. Only a user with\n  # the SE_BACKUP_NAME privilege in their process token can get the\n  # mode for objects they do not have read access to.\n  def get_mode(path)\n    return unless supports_acl?(path)\n\n    well_known_world_sid = Puppet::Util::Windows::SID::Everyone\n    well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody\n    well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem\n    well_known_app_packages_sid = Puppet::Util::Windows::SID::AllAppPackages\n\n    mode = S_ISYSTEM_MISSING\n\n    sd = get_security_descriptor(path)\n    sd.dacl.each do |ace|\n      next if ace.inherit_only?\n\n      case ace.sid\n      when sd.owner\n        MASK_TO_MODE.each_pair do |k, v|\n          if (ace.mask & k) == k\n            mode |= (v << 6)\n          end\n        end\n      when sd.group\n        MASK_TO_MODE.each_pair do |k, v|\n          if (ace.mask & k) == k\n            mode |= (v << 3)\n          end\n        end\n      when well_known_world_sid\n        MASK_TO_MODE.each_pair do |k, v|\n          if (ace.mask & k) == k\n            mode |= (v << 6) | (v << 3) | v\n          end\n        end\n        if File.directory?(path) &&\n           (ace.mask & (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE | FILE::FILE_DELETE_CHILD)) == (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE)\n          mode |= S_ISVTX;\n        end\n      when well_known_nobody_sid\n        if (ace.mask & FILE::FILE_APPEND_DATA).nonzero?\n          mode |= S_ISVTX\n        end\n      when well_known_app_packages_sid, well_known_system_sid\n        # do nothing\n      else\n        # puts \"Warning, unable to map SID into POSIX mode: #{ace.sid}\"\n        mode |= S_IEXTRA\n      end\n\n      if ace.sid == well_known_system_sid\n        mode &= ~S_ISYSTEM_MISSING\n      end\n\n      # if owner and group the same, then user and group modes are the OR of both\n      if sd.owner == sd.group\n        mode |= ((mode & S_IRWXG) << 3) | ((mode & S_IRWXU) >> 3)\n        # puts \"owner: #{sd.group}, 0x#{ace.mask.to_s(16)}, #{mode.to_s(8)}\"\n      end\n    end\n\n    # puts \"get_mode: #{mode.to_s(8)}\"\n    mode\n  end\n\n  MODE_TO_MASK = {\n    S_IROTH => FILE::FILE_GENERIC_READ,\n    S_IWOTH => FILE::FILE_GENERIC_WRITE,\n    S_IXOTH => (FILE::FILE_GENERIC_EXECUTE & ~FILE::FILE_READ_ATTRIBUTES),\n  }\n\n  # Set the mode of the object referenced by +path+ to the specified\n  # +mode+. The mode should be specified as POSIX-style read, write,\n  # and execute modes for the user, group, and other classes,\n  # e.g. 0640. The sticky bit, S_ISVTX, is supported, but is only\n  # meaningful for directories. If set, group and others are not\n  # allowed to delete child objects for which they are not the owner.\n  # By default, the DACL is set to protected, meaning it does not\n  # inherit access control entries from parent objects. This can be\n  # changed by setting +protected+ to false. The owner of the object\n  # (with READ_CONTROL and WRITE_DACL access) can always change the\n  # mode. Only a user with the SE_BACKUP_NAME and SE_RESTORE_NAME\n  # privileges in their process token can change the mode for objects\n  # that they do not have read and write access to.\n  def set_mode(mode, path, protected = true, managing_owner = false, managing_group = false)\n    sd = get_security_descriptor(path)\n    well_known_world_sid = Puppet::Util::Windows::SID::Everyone\n    well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody\n    well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem\n\n    owner_allow = FILE::STANDARD_RIGHTS_ALL  |\n                  FILE::FILE_READ_ATTRIBUTES |\n                  FILE::FILE_WRITE_ATTRIBUTES\n    # this prevents a mode that is not 7 from taking ownership of a file based\n    # on group membership and rewriting it / making it executable\n    group_allow = FILE::STANDARD_RIGHTS_READ |\n                  FILE::FILE_READ_ATTRIBUTES |\n                  FILE::SYNCHRONIZE\n    other_allow = FILE::STANDARD_RIGHTS_READ |\n                  FILE::FILE_READ_ATTRIBUTES |\n                  FILE::SYNCHRONIZE\n    nobody_allow = 0\n    system_allow = 0\n\n    MODE_TO_MASK.each do |k, v|\n      if ((mode >> 6) & k) == k\n        owner_allow |= v\n      end\n      if ((mode >> 3) & k) == k\n        group_allow |= v\n      end\n      if (mode & k) == k\n        other_allow |= v\n      end\n    end\n\n    # With a mode value of '7' for group / other, the value must then include\n    # additional perms beyond STANDARD_RIGHTS_READ to allow DACL modification\n    if (mode & S_IRWXG) == S_IRWXG\n      group_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER\n    end\n    if (mode & S_IRWXO) == S_IRWXO\n      other_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER\n    end\n\n    if (mode & S_ISVTX).nonzero?\n      nobody_allow |= FILE::FILE_APPEND_DATA;\n    end\n\n    isownergroup = sd.owner == sd.group\n\n    # caller is NOT managing SYSTEM by using group or owner, so set to FULL\n    if ![sd.owner, sd.group].include? well_known_system_sid\n      # we don't check S_ISYSTEM_MISSING bit, but automatically carry over existing SYSTEM perms\n      # by default set SYSTEM perms to full\n      system_allow = FILE::FILE_ALL_ACCESS\n    else\n      # It is possible to set SYSTEM with a mode other than Full Control (7) however this makes no sense and in practical terms\n      # should not be done.  We can trap these instances and correct them before being applied.\n      if (sd.owner == well_known_system_sid) && (owner_allow != FILE::FILE_ALL_ACCESS)\n        # If owner and group are both SYSTEM but group is unmanaged the control rights of system will be set to FullControl by\n        # the unmanaged group, so there is no need for the warning\n        if managing_owner && (!isownergroup || managing_group)\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          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 }\n        elsif managing_owner && isownergroup\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          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 } }\n        else\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          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 } }\n          owner_allow = FILE::FILE_ALL_ACCESS\n        end\n      end\n\n      if (sd.group == well_known_system_sid) && (group_allow != FILE::FILE_ALL_ACCESS)\n        # If owner and group are both SYSTEM but owner is unmanaged the control rights of system will be set to FullControl by\n        # the unmanaged owner, so there is no need for the warning.\n        if managing_group && (!isownergroup || managing_owner)\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          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 }\n        elsif managing_group && isownergroup\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          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 } }\n        else\n          # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n          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 } }\n          group_allow = FILE::FILE_ALL_ACCESS\n        end\n      end\n    end\n\n    # even though FILE_DELETE_CHILD only applies to directories, it can be set on files\n    # this is necessary to do to ensure a file ends up with (F) FullControl\n    if (mode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)\n      owner_allow |= FILE::FILE_DELETE_CHILD\n    end\n    if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) && (mode & S_ISVTX) == 0\n      group_allow |= FILE::FILE_DELETE_CHILD\n    end\n    if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) && (mode & S_ISVTX) == 0\n      other_allow |= FILE::FILE_DELETE_CHILD\n    end\n\n    # if owner and group the same, then map group permissions to the one owner ACE\n    if isownergroup\n      owner_allow |= group_allow\n    end\n\n    # if any ACE allows write, then clear readonly bit, but do this before we overwrite\n    # the DACl and lose our ability to set the attribute\n    if ((owner_allow | group_allow | other_allow) & FILE::FILE_WRITE_DATA) == FILE::FILE_WRITE_DATA\n      FILE.remove_attributes(path, FILE::FILE_ATTRIBUTE_READONLY)\n    end\n\n    isdir = File.directory?(path)\n    dacl = Puppet::Util::Windows::AccessControlList.new\n    dacl.allow(sd.owner, owner_allow)\n    unless isownergroup\n      dacl.allow(sd.group, group_allow)\n    end\n    dacl.allow(well_known_world_sid, other_allow)\n    dacl.allow(well_known_nobody_sid, nobody_allow)\n\n    # TODO: system should be first?\n    flags = !isdir ? 0 :\n      Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE |\n        Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE\n    dacl.allow(well_known_system_sid, system_allow, flags)\n\n    # add inherit-only aces for child dirs and files that are created within the dir\n    inherit_only = Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE\n    if isdir\n      inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE\n      dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow, inherit)\n      dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow, inherit)\n\n      inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE\n      # allow any previously set bits *except* for these\n      perms_to_strip = ~(FILE::FILE_EXECUTE + FILE::WRITE_OWNER + FILE::WRITE_DAC)\n      dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow & perms_to_strip, inherit)\n      dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow & perms_to_strip, inherit)\n    end\n\n    new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, dacl, protected)\n    set_security_descriptor(path, new_sd)\n\n    nil\n  end\n\n  ACL_REVISION = 2\n\n  def add_access_allowed_ace(acl, mask, sid, inherit = nil)\n    inherit ||= NO_INHERITANCE\n\n    Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr|\n      if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Invalid SID\")\n      end\n\n      if AddAccessAllowedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Failed to add access control entry\")\n      end\n    end\n\n    # ensure this method is void if it doesn't raise\n    nil\n  end\n\n  def add_access_denied_ace(acl, mask, sid, inherit = nil)\n    inherit ||= NO_INHERITANCE\n\n    Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr|\n      if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Invalid SID\")\n      end\n\n      if AddAccessDeniedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Failed to add access control entry\")\n      end\n    end\n\n    # ensure this method is void if it doesn't raise\n    nil\n  end\n\n  def parse_dacl(dacl_ptr)\n    # REMIND: need to handle NULL DACL\n    if IsValidAcl(dacl_ptr) == FFI::WIN32_FALSE\n      raise Puppet::Util::Windows::Error, _(\"Invalid DACL\")\n    end\n\n    dacl_struct = ACL.new(dacl_ptr)\n    ace_count = dacl_struct[:AceCount]\n\n    dacl = Puppet::Util::Windows::AccessControlList.new\n\n    # deny all\n    return dacl if ace_count == 0\n\n    0.upto(ace_count - 1) do |i|\n      FFI::MemoryPointer.new(:pointer, 1) do |ace_ptr|\n        next if GetAce(dacl_ptr, i, ace_ptr) == FFI::WIN32_FALSE\n\n        # ACE structures vary depending on the type. We are only concerned with\n        # ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACEs, which have the same layout\n        ace = GENERIC_ACCESS_ACE.new(ace_ptr.get_pointer(0)) # deref LPVOID *\n\n        ace_type = ace[:Header][:AceType]\n        if ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE &&\n           ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE\n          Puppet.warning _(\"Unsupported access control entry type: 0x%{type}\") % { type: ace_type.to_s(16) }\n          next\n        end\n\n        # using pointer addition gives the FFI::Pointer a size, but that's OK here\n        sid = Puppet::Util::Windows::SID.sid_ptr_to_string(ace.pointer + GENERIC_ACCESS_ACE.offset_of(:SidStart))\n        mask = ace[:Mask]\n        ace_flags = ace[:Header][:AceFlags]\n\n        case ace_type\n        when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE\n          dacl.allow(sid, mask, ace_flags)\n        when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE\n          dacl.deny(sid, mask, ace_flags)\n        end\n      end\n    end\n\n    dacl\n  end\n\n  # Open an existing file with the specified access mode, and execute a\n  # block with the opened file HANDLE.\n  def open_file(path, access, &block)\n    handle = CreateFileW(\n      wide_string(path),\n      access,\n      FILE::FILE_SHARE_READ | FILE::FILE_SHARE_WRITE,\n      FFI::Pointer::NULL, # security_attributes\n      FILE::OPEN_EXISTING,\n      FILE::FILE_FLAG_OPEN_REPARSE_POINT | FILE::FILE_FLAG_BACKUP_SEMANTICS,\n      FFI::Pointer::NULL_HANDLE\n    ) # template\n\n    if handle == Puppet::Util::Windows::File::INVALID_HANDLE_VALUE\n      raise Puppet::Util::Windows::Error, _(\"Failed to open '%{path}'\") % { path: path }\n    end\n\n    begin\n      yield handle\n    ensure\n      FFI::WIN32.CloseHandle(handle) if handle\n    end\n\n    # handle has already had CloseHandle called against it, nothing to return\n    nil\n  end\n\n  # Execute a block with the specified privilege enabled\n  def with_privilege(privilege, &block)\n    set_privilege(privilege, true)\n    yield\n  ensure\n    set_privilege(privilege, false)\n  end\n\n  SE_PRIVILEGE_ENABLED    = 0x00000002\n  TOKEN_ADJUST_PRIVILEGES = 0x0020\n\n  # Enable or disable a privilege. Note this doesn't add any privileges the\n  # user doesn't already has, it just enables privileges that are disabled.\n  def set_privilege(privilege, enable)\n    return unless Puppet.features.root?\n\n    Puppet::Util::Windows::Process.with_process_token(TOKEN_ADJUST_PRIVILEGES) do |token|\n      Puppet::Util::Windows::Process.lookup_privilege_value(privilege) do |luid|\n        FFI::MemoryPointer.new(Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.size) do |luid_and_attributes_ptr|\n          # allocate unmanaged memory for structs that we clean up afterwards\n          luid_and_attributes = Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.new(luid_and_attributes_ptr)\n          luid_and_attributes[:Luid] = luid\n          luid_and_attributes[:Attributes] = enable ? SE_PRIVILEGE_ENABLED : 0\n\n          FFI::MemoryPointer.new(Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.size) do |token_privileges_ptr|\n            token_privileges = Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.new(token_privileges_ptr)\n            token_privileges[:PrivilegeCount] = 1\n            token_privileges[:Privileges][0] = luid_and_attributes\n\n            # size is correct given we only have 1 LUID, otherwise would be:\n            # [:PrivilegeCount].size + [:PrivilegeCount] * LUID_AND_ATTRIBUTES.size\n            if AdjustTokenPrivileges(token, FFI::WIN32_FALSE,\n                                     token_privileges, token_privileges.size,\n                                     FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE\n              raise Puppet::Util::Windows::Error, _(\"Failed to adjust process privileges\")\n            end\n          end\n        end\n      end\n    end\n\n    # token / luid structs freed by this point, so return true as nothing raised\n    true\n  end\n\n  def get_security_descriptor(path)\n    sd = nil\n\n    with_privilege(SE_BACKUP_NAME) do\n      open_file(path, READ_CONTROL) do |handle|\n        FFI::MemoryPointer.new(:pointer, 1) do |owner_sid_ptr_ptr|\n          FFI::MemoryPointer.new(:pointer, 1) do |group_sid_ptr_ptr|\n            FFI::MemoryPointer.new(:pointer, 1) do |dacl_ptr_ptr|\n              FFI::MemoryPointer.new(:pointer, 1) do |sd_ptr_ptr|\n                rv = GetSecurityInfo(\n                  handle,\n                  :SE_FILE_OBJECT,\n                  OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,\n                  owner_sid_ptr_ptr,\n                  group_sid_ptr_ptr,\n                  dacl_ptr_ptr,\n                  FFI::Pointer::NULL, # sacl\n                  sd_ptr_ptr\n                ) # sec desc\n                raise Puppet::Util::Windows::Error, _(\"Failed to get security information\") if rv != FFI::ERROR_SUCCESS\n\n                # these 2 convenience params are not freed since they point inside sd_ptr\n                owner = Puppet::Util::Windows::SID.sid_ptr_to_string(owner_sid_ptr_ptr.get_pointer(0))\n                group = Puppet::Util::Windows::SID.sid_ptr_to_string(group_sid_ptr_ptr.get_pointer(0))\n\n                FFI::MemoryPointer.new(:word, 1) do |control|\n                  FFI::MemoryPointer.new(:dword, 1) do |revision|\n                    sd_ptr_ptr.read_win32_local_pointer do |sd_ptr|\n                      if GetSecurityDescriptorControl(sd_ptr, control, revision) == FFI::WIN32_FALSE\n                        raise Puppet::Util::Windows::Error, _(\"Failed to get security descriptor control\")\n                      end\n\n                      protect = (control.read_word & SE_DACL_PROTECTED) == SE_DACL_PROTECTED\n                      dacl = parse_dacl(dacl_ptr_ptr.get_pointer(0))\n                      sd = Puppet::Util::Windows::SecurityDescriptor.new(owner, group, dacl, protect)\n                    end\n                  end\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n\n    sd\n  end\n\n  def get_max_generic_acl_size(ace_count)\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx\n    # To calculate the initial size of an ACL, add the following together, and then align the result to the nearest DWORD:\n    # * Size of the ACL structure.\n    # * Size of each ACE structure that the ACL is to contain minus the SidStart member (DWORD) of the ACE.\n    # * Length of the SID that each ACE is to contain.\n    ACL.size + ace_count * MAXIMUM_GENERIC_ACE_SIZE\n  end\n\n  # setting DACL requires both READ_CONTROL and WRITE_DACL access rights,\n  # and their respective privileges, SE_BACKUP_NAME and SE_RESTORE_NAME.\n  def set_security_descriptor(path, sd)\n    FFI::MemoryPointer.new(:byte, get_max_generic_acl_size(sd.dacl.count)) do |acl_ptr|\n      if InitializeAcl(acl_ptr, acl_ptr.size, ACL_REVISION) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Failed to initialize ACL\")\n      end\n\n      if IsValidAcl(acl_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Invalid DACL\")\n      end\n\n      with_privilege(SE_BACKUP_NAME) do\n        with_privilege(SE_RESTORE_NAME) do\n          open_file(path, READ_CONTROL | WRITE_DAC | WRITE_OWNER) do |handle|\n            Puppet::Util::Windows::SID.string_to_sid_ptr(sd.owner) do |owner_sid_ptr|\n              Puppet::Util::Windows::SID.string_to_sid_ptr(sd.group) do |group_sid_ptr|\n                sd.dacl.each do |ace|\n                  case ace.type\n                  when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE\n                    # puts \"ace: allow, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}\"\n                    add_access_allowed_ace(acl_ptr, ace.mask, ace.sid, ace.flags)\n                  when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE\n                    # puts \"ace: deny, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}\"\n                    add_access_denied_ace(acl_ptr, ace.mask, ace.sid, ace.flags)\n                  else\n                    raise \"We should never get here\"\n                    # TODO: this should have been a warning in an earlier commit\n                  end\n                end\n\n                # protected means the object does not inherit aces from its parent\n                flags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION\n                flags |= sd.protect ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION\n\n                rv = SetSecurityInfo(handle,\n                                     :SE_FILE_OBJECT,\n                                     flags,\n                                     owner_sid_ptr,\n                                     group_sid_ptr,\n                                     acl_ptr,\n                                     FFI::MemoryPointer::NULL)\n\n                if rv != FFI::ERROR_SUCCESS\n                  raise Puppet::Util::Windows::Error, _(\"Failed to set security information\")\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n  end\n\n  ffi_convention :stdcall\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx\n  # HANDLE WINAPI CreateFile(\n  #   _In_      LPCTSTR lpFileName,\n  #   _In_      DWORD dwDesiredAccess,\n  #   _In_      DWORD dwShareMode,\n  #   _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,\n  #   _In_      DWORD dwCreationDisposition,\n  #   _In_      DWORD dwFlagsAndAttributes,\n  #   _In_opt_  HANDLE hTemplateFile\n  # );\n  ffi_lib :kernel32\n  attach_function_private :CreateFileW,\n                          [:lpcwstr, :dword, :dword, :pointer, :dword, :dword, :handle], :handle\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx\n  # BOOL WINAPI GetVolumeInformation(\n  #   _In_opt_   LPCTSTR lpRootPathName,\n  #   _Out_opt_  LPTSTR lpVolumeNameBuffer,\n  #   _In_       DWORD nVolumeNameSize,\n  #   _Out_opt_  LPDWORD lpVolumeSerialNumber,\n  #   _Out_opt_  LPDWORD lpMaximumComponentLength,\n  #   _Out_opt_  LPDWORD lpFileSystemFlags,\n  #   _Out_opt_  LPTSTR lpFileSystemNameBuffer,\n  #   _In_       DWORD nFileSystemNameSize\n  # );\n  ffi_lib :kernel32\n  attach_function_private :GetVolumeInformationW,\n                          [:lpcwstr, :lpwstr, :dword, :lpdword, :lpdword, :lpdword, :lpwstr, :dword], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374951(v=vs.85).aspx\n  # BOOL WINAPI AddAccessAllowedAceEx(\n  #   _Inout_  PACL pAcl,\n  #   _In_     DWORD dwAceRevision,\n  #   _In_     DWORD AceFlags,\n  #   _In_     DWORD AccessMask,\n  #   _In_     PSID pSid\n  # );\n  ffi_lib :advapi32\n  attach_function_private :AddAccessAllowedAceEx,\n                          [:pointer, :dword, :dword, :dword, :pointer], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374964(v=vs.85).aspx\n  # BOOL WINAPI AddAccessDeniedAceEx(\n  #   _Inout_  PACL pAcl,\n  #   _In_     DWORD dwAceRevision,\n  #   _In_     DWORD AceFlags,\n  #   _In_     DWORD AccessMask,\n  #   _In_     PSID pSid\n  # );\n  ffi_lib :advapi32\n  attach_function_private :AddAccessDeniedAceEx,\n                          [:pointer, :dword, :dword, :dword, :pointer], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374931(v=vs.85).aspx\n  # typedef struct _ACL {\n  #   BYTE AclRevision;\n  #   BYTE Sbz1;\n  #   WORD AclSize;\n  #   WORD AceCount;\n  #   WORD Sbz2;\n  # } ACL, *PACL;\n  class ACL < FFI::Struct\n    layout :AclRevision, :byte,\n           :Sbz1, :byte,\n           :AclSize, :word,\n           :AceCount, :word,\n           :Sbz2, :word\n  end\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374912(v=vs.85).aspx\n  # ACE types\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374919(v=vs.85).aspx\n  # typedef struct _ACE_HEADER {\n  #   BYTE AceType;\n  #   BYTE AceFlags;\n  #   WORD AceSize;\n  # } ACE_HEADER, *PACE_HEADER;\n  class ACE_HEADER < FFI::Struct\n    layout :AceType, :byte,\n           :AceFlags, :byte,\n           :AceSize,  :word\n  end\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374892(v=vs.85).aspx\n  # ACCESS_MASK\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374847(v=vs.85).aspx\n  # typedef struct _ACCESS_ALLOWED_ACE {\n  #   ACE_HEADER  Header;\n  #   ACCESS_MASK Mask;\n  #   DWORD       SidStart;\n  # } ACCESS_ALLOWED_ACE, *PACCESS_ALLOWED_ACE;\n  #\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374879(v=vs.85).aspx\n  # typedef struct _ACCESS_DENIED_ACE {\n  #   ACE_HEADER  Header;\n  #   ACCESS_MASK Mask;\n  #   DWORD       SidStart;\n  # } ACCESS_DENIED_ACE, *PACCESS_DENIED_ACE;\n  class GENERIC_ACCESS_ACE < FFI::Struct\n    # ACE structures must be aligned on DWORD boundaries. All Windows\n    # memory-management functions return DWORD-aligned handles to memory\n    pack 4\n    layout :Header, ACE_HEADER,\n           :Mask, :dword,\n           :SidStart, :dword\n  end\n\n  # https://stackoverflow.com/a/1792930\n  MAXIMUM_SID_BYTES_LENGTH = 68\n  MAXIMUM_GENERIC_ACE_SIZE = GENERIC_ACCESS_ACE.offset_of(:SidStart) +\n                             MAXIMUM_SID_BYTES_LENGTH\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446634(v=vs.85).aspx\n  # BOOL WINAPI GetAce(\n  #   _In_   PACL pAcl,\n  #   _In_   DWORD dwAceIndex,\n  #   _Out_  LPVOID *pAce\n  # );\n  ffi_lib :advapi32\n  attach_function_private :GetAce,\n                          [:pointer, :dword, :pointer], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa375202(v=vs.85).aspx\n  # BOOL WINAPI AdjustTokenPrivileges(\n  #   _In_       HANDLE TokenHandle,\n  #   _In_       BOOL DisableAllPrivileges,\n  #   _In_opt_   PTOKEN_PRIVILEGES NewState,\n  #   _In_       DWORD BufferLength,\n  #   _Out_opt_  PTOKEN_PRIVILEGES PreviousState,\n  #   _Out_opt_  PDWORD ReturnLength\n  # );\n  ffi_lib :advapi32\n  attach_function_private :AdjustTokenPrivileges,\n                          [:handle, :win32_bool, :pointer, :dword, :pointer, :pdword], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/hardware/ff556610(v=vs.85).aspx\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379561(v=vs.85).aspx\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446647(v=vs.85).aspx\n  # typedef WORD SECURITY_DESCRIPTOR_CONTROL, *PSECURITY_DESCRIPTOR_CONTROL;\n  # BOOL WINAPI GetSecurityDescriptorControl(\n  #   _In_   PSECURITY_DESCRIPTOR pSecurityDescriptor,\n  #   _Out_  PSECURITY_DESCRIPTOR_CONTROL pControl,\n  #   _Out_  LPDWORD lpdwRevision\n  # );\n  ffi_lib :advapi32\n  attach_function_private :GetSecurityDescriptorControl,\n                          [:pointer, :lpword, :lpdword], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx\n  # BOOL WINAPI InitializeAcl(\n  #   _Out_  PACL pAcl,\n  #   _In_   DWORD nAclLength,\n  #   _In_   DWORD dwAclRevision\n  # );\n  ffi_lib :advapi32\n  attach_function_private :InitializeAcl,\n                          [:pointer, :dword, :dword], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379142(v=vs.85).aspx\n  # BOOL WINAPI IsValidAcl(\n  #   _In_  PACL pAcl\n  # );\n  ffi_lib :advapi32\n  attach_function_private :IsValidAcl,\n                          [:pointer], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379593(v=vs.85).aspx\n  SE_OBJECT_TYPE = enum(\n    :SE_UNKNOWN_OBJECT_TYPE, 0,\n    :SE_FILE_OBJECT,\n    :SE_SERVICE,\n    :SE_PRINTER,\n    :SE_REGISTRY_KEY,\n    :SE_LMSHARE,\n    :SE_KERNEL_OBJECT,\n    :SE_WINDOW_OBJECT,\n    :SE_DS_OBJECT,\n    :SE_DS_OBJECT_ALL,\n    :SE_PROVIDER_DEFINED_OBJECT,\n    :SE_WMIGUID_OBJECT,\n    :SE_REGISTRY_WOW64_32KEY\n  )\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446654(v=vs.85).aspx\n  # DWORD WINAPI GetSecurityInfo(\n  #   _In_       HANDLE handle,\n  #   _In_       SE_OBJECT_TYPE ObjectType,\n  #   _In_       SECURITY_INFORMATION SecurityInfo,\n  #   _Out_opt_  PSID *ppsidOwner,\n  #   _Out_opt_  PSID *ppsidGroup,\n  #   _Out_opt_  PACL *ppDacl,\n  #   _Out_opt_  PACL *ppSacl,\n  #   _Out_opt_  PSECURITY_DESCRIPTOR *ppSecurityDescriptor\n  # );\n  ffi_lib :advapi32\n  attach_function_private :GetSecurityInfo,\n                          [:handle, SE_OBJECT_TYPE, :dword, :pointer, :pointer, :pointer, :pointer, :pointer], :dword\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379588(v=vs.85).aspx\n  # DWORD WINAPI SetSecurityInfo(\n  #   _In_      HANDLE handle,\n  #   _In_      SE_OBJECT_TYPE ObjectType,\n  #   _In_      SECURITY_INFORMATION SecurityInfo,\n  #   _In_opt_  PSID psidOwner,\n  #   _In_opt_  PSID psidGroup,\n  #   _In_opt_  PACL pDacl,\n  #   _In_opt_  PACL pSacl\n  # );\n  ffi_lib :advapi32\n  # TODO: SECURITY_INFORMATION is actually a bitmask the size of a DWORD\n  attach_function_private :SetSecurityInfo,\n                          [:handle, SE_OBJECT_TYPE, :dword, :pointer, :pointer, :pointer, :pointer], :dword\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/security_descriptor.rb",
    "content": "# frozen_string_literal: true\n\n# Windows Security Descriptor\n#\n# Represents a security descriptor that can be applied to any Windows securable\n# object, e.g. file, registry key, service, etc. It consists of an owner, group,\n# flags, DACL, and SACL. The SACL is not currently supported, though it has the\n# same layout as a DACL.\n#\n# @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa379563(v=vs.85).aspx\n# @api private\nclass Puppet::Util::Windows::SecurityDescriptor\n  require_relative '../../../puppet/util/windows/security'\n  include Puppet::Util::Windows::SID\n\n  attr_reader :owner, :group, :dacl\n  attr_accessor :protect\n\n  # Construct a security descriptor\n  #\n  # @param owner [String] The SID of the owner, e.g. 'S-1-5-18'\n  # @param group [String] The SID of the group\n  # @param dacl [AccessControlList] The ACL specifying the rights granted to\n  # each user for accessing the object that the security descriptor refers to.\n  # @param protect [Boolean] If true, then inheritable access control\n  # entries will be blocked, and not applied to the object.\n  def initialize(owner, group, dacl, protect = false)\n    @owner = owner\n    @group = group\n    @dacl = dacl\n    @protect = protect\n  end\n\n  # Set the owner. Non-inherited access control entries assigned to the\n  # current owner will be assigned to the new owner.\n  #\n  # @param new_owner [String] The SID of the new owner, e.g. 'S-1-5-18'\n  def owner=(new_owner)\n    if @owner != new_owner\n      @dacl.reassign!(@owner, new_owner)\n      @owner = new_owner\n    end\n  end\n\n  # Set the group. Non-inherited access control entries assigned to the\n  # current group will be assigned to the new group.\n  #\n  # @param new_group [String] The SID of the new group, e.g. 'S-1-0-0'\n  def group=(new_group)\n    if @group != new_group\n      @dacl.reassign!(@group, new_group)\n      @group = new_group\n    end\n  end\n\n  def inspect\n    str = sid_to_name(owner)\n    str << \"\\n\"\n    str << sid_to_name(group)\n    str << \"\\n\"\n    str << @dacl.inspect\n    str\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/service.rb",
    "content": "# coding: utf-8\n# frozen_string_literal: true\n\nrequire_relative '../../../puppet/ffi/windows'\n\nmodule Puppet::Util::Windows\n  # This module is designed to provide an API between the windows system and puppet for\n  # service management.\n  #\n  # for an overview of the service state transitions see: https://docs.microsoft.com/en-us/windows/desktop/Services/service-status-transitions\n  module Service\n    extend Puppet::Util::Windows::String\n\n    include Puppet::FFI::Windows::Constants\n    extend Puppet::FFI::Windows::Constants\n\n    include Puppet::FFI::Windows::Structs\n    extend Puppet::FFI::Windows::Structs\n\n    include Puppet::FFI::Windows::Functions\n    extend Puppet::FFI::Windows::Functions\n\n    # Returns true if the service exists, false otherwise.\n    #\n    # @param [String] service_name name of the service\n    def exists?(service_name)\n      open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS) do |_|\n        true\n      end\n    rescue Puppet::Util::Windows::Error => e\n      return false if e.code == ERROR_SERVICE_DOES_NOT_EXIST\n\n      raise e\n    end\n    module_function :exists?\n\n    # Start a windows service\n    #\n    # @param [String] service_name name of the service to start\n    # @param optional [Integer] timeout the minumum number of seconds to wait before timing out\n    def start(service_name, timeout: DEFAULT_TIMEOUT)\n      Puppet.debug _(\"Starting the %{service_name} service. Timeout set to: %{timeout} seconds\") % { service_name: service_name, timeout: timeout }\n\n      valid_initial_states = [\n        SERVICE_STOP_PENDING,\n        SERVICE_STOPPED,\n        SERVICE_START_PENDING\n      ]\n\n      transition_service_state(service_name, valid_initial_states, SERVICE_RUNNING, timeout) do |service|\n        if StartServiceW(service, 0, FFI::Pointer::NULL) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to start the service\")\n        end\n      end\n\n      Puppet.debug _(\"Successfully started the %{service_name} service\") % { service_name: service_name }\n    end\n    module_function :start\n\n    # Stop a windows service\n    #\n    # @param [String] service_name name of the service to stop\n    # @param optional [Integer] timeout the minumum number of seconds to wait before timing out\n    def stop(service_name, timeout: DEFAULT_TIMEOUT)\n      Puppet.debug _(\"Stopping the %{service_name} service. Timeout set to: %{timeout} seconds\") % { service_name: service_name, timeout: timeout }\n\n      valid_initial_states = SERVICE_STATES.keys - [SERVICE_STOPPED]\n\n      transition_service_state(service_name, valid_initial_states, SERVICE_STOPPED, timeout) do |service|\n        send_service_control_signal(service, SERVICE_CONTROL_STOP)\n      end\n\n      Puppet.debug _(\"Successfully stopped the %{service_name} service\") % { service_name: service_name }\n    end\n    module_function :stop\n\n    # Resume a paused windows service\n    #\n    # @param [String] service_name name of the service to resume\n    # @param optional [Integer] :timeout the minumum number of seconds to wait before timing out\n    def resume(service_name, timeout: DEFAULT_TIMEOUT)\n      Puppet.debug _(\"Resuming the %{service_name} service. Timeout set to: %{timeout} seconds\") % { service_name: service_name, timeout: timeout }\n\n      valid_initial_states = [\n        SERVICE_PAUSE_PENDING,\n        SERVICE_PAUSED,\n        SERVICE_CONTINUE_PENDING\n      ]\n\n      transition_service_state(service_name, valid_initial_states, SERVICE_RUNNING, timeout) do |service|\n        # The SERVICE_CONTROL_CONTINUE signal can only be sent when\n        # the service is in the SERVICE_PAUSED state\n        wait_on_pending_state(service, SERVICE_PAUSE_PENDING, timeout)\n\n        send_service_control_signal(service, SERVICE_CONTROL_CONTINUE)\n      end\n\n      Puppet.debug _(\"Successfully resumed the %{service_name} service\") % { service_name: service_name }\n    end\n    module_function :resume\n\n    # Query the state of a service using QueryServiceStatusEx\n    #\n    # @param [string] service_name name of the service to query\n    # @return [string] the status of the service\n    def service_state(service_name)\n      state = nil\n      open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS) do |service|\n        query_status(service) do |status|\n          state = SERVICE_STATES[status[:dwCurrentState]]\n        end\n      end\n      if state.nil?\n        raise Puppet::Error, _(\"Unknown Service state '%{current_state}' for '%{service_name}'\") % { current_state: state.to_s, service_name: service_name }\n      end\n\n      state\n    end\n    module_function :service_state\n\n    # Query the configuration of a service using QueryServiceConfigW\n    # or QueryServiceConfig2W\n    #\n    # @param [String] service_name name of the service to query\n    # @return [QUERY_SERVICE_CONFIGW.struct] the configuration of the service\n    def service_start_type(service_name)\n      start_type = nil\n      open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service|\n        query_config(service) do |config|\n          start_type = SERVICE_START_TYPES[config[:dwStartType]]\n        end\n      end\n      # if the service has type AUTO_START, check if it's a delayed service\n      if start_type == :SERVICE_AUTO_START\n        open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service|\n          query_config2(service, SERVICE_CONFIG_DELAYED_AUTO_START_INFO) do |config|\n            return :SERVICE_DELAYED_AUTO_START if config[:fDelayedAutostart] == 1\n          end\n        end\n      end\n      if start_type.nil?\n        raise Puppet::Error, _(\"Unknown start type '%{start_type}' for '%{service_name}'\") % { start_type: start_type.to_s, service_name: service_name }\n      end\n\n      start_type\n    end\n    module_function :service_start_type\n\n    # Query the configuration of a service using QueryServiceConfigW\n    # to find its current logon account\n    #\n    # @return [String] logon_account account currently set for the service's logon\n    #  in the format \"DOMAIN\\Account\" or \".\\Account\" if it's a local account\n    def logon_account(service_name)\n      open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service|\n        query_config(service) do |config|\n          return config[:lpServiceStartName].read_arbitrary_wide_string_up_to(Puppet::Util::Windows::ADSI::User::MAX_USERNAME_LENGTH)\n        end\n      end\n    end\n    module_function :logon_account\n\n    # Set the startup configuration of a windows service\n    #\n    # @param [String] service_name the name of the service to modify\n    # @param [Hash] options the configuration to be applied. Expected option keys:\n    #   - [Integer] startup_type a code corresponding to a start type for\n    #       windows service, see the \"Service start type codes\" section in the\n    #       Puppet::Util::Windows::Service file for the list of available codes\n    #   - [String] logon_account the account to be used by the service for logon\n    #   - [String] logon_password the provided logon_account's password to be used by the service for logon\n    #   - [Bool] delayed whether the service should be started with a delay\n    def set_startup_configuration(service_name, options: {})\n      options[:startup_type] = SERVICE_START_TYPES.key(options[:startup_type]) || SERVICE_NO_CHANGE\n      options[:logon_account] = wide_string(options[:logon_account]) || FFI::Pointer::NULL\n      options[:logon_password] = wide_string(options[:logon_password]) || FFI::Pointer::NULL\n\n      open_service(service_name, SC_MANAGER_CONNECT, SERVICE_CHANGE_CONFIG) do |service|\n        success = ChangeServiceConfigW(\n          service,\n          SERVICE_NO_CHANGE,        # dwServiceType\n          options[:startup_type],   # dwStartType\n          SERVICE_NO_CHANGE,        # dwErrorControl\n          FFI::Pointer::NULL,       # lpBinaryPathName\n          FFI::Pointer::NULL,       # lpLoadOrderGroup\n          FFI::Pointer::NULL,       # lpdwTagId\n          FFI::Pointer::NULL,       # lpDependencies\n          options[:logon_account],  # lpServiceStartName\n          options[:logon_password], # lpPassword\n          FFI::Pointer::NULL        # lpDisplayName\n        )\n        if success == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to update service configuration\")\n        end\n      end\n\n      if options[:startup_type]\n        options[:delayed] ||= false\n        set_startup_mode_delayed(service_name, options[:delayed])\n      end\n    end\n    module_function :set_startup_configuration\n\n    # enumerate over all services in all states and return them as a hash\n    #\n    # @return [Hash] a hash containing services:\n    #   { 'service name' => {\n    #                         'display_name' => 'display name',\n    #                         'service_status_process' => SERVICE_STATUS_PROCESS struct\n    #                       }\n    #   }\n    def services\n      services = {}\n      open_scm(SC_MANAGER_ENUMERATE_SERVICE) do |scm|\n        size_required = 0\n        services_returned = 0\n        FFI::MemoryPointer.new(:dword) do |bytes_pointer|\n          FFI::MemoryPointer.new(:dword) do |svcs_ret_ptr|\n            FFI::MemoryPointer.new(:dword) do |resume_ptr|\n              resume_ptr.write_dword(0)\n              # Fetch the bytes of memory required to be allocated\n              # for QueryServiceConfigW to return succesfully. This\n              # is done by sending NULL and 0 for the pointer and size\n              # respectively, letting the command fail, then reading the\n              # value of pcbBytesNeeded\n              #\n              # return value will be false from this call, since it's designed\n              # to fail. Just ignore it\n              EnumServicesStatusExW(\n                scm,\n                :SC_ENUM_PROCESS_INFO,\n                ALL_SERVICE_TYPES,\n                SERVICE_STATE_ALL,\n                FFI::Pointer::NULL,\n                0,\n                bytes_pointer,\n                svcs_ret_ptr,\n                resume_ptr,\n                FFI::Pointer::NULL\n              )\n              size_required = bytes_pointer.read_dword\n              FFI::MemoryPointer.new(size_required) do |buffer_ptr|\n                resume_ptr.write_dword(0)\n                svcs_ret_ptr.write_dword(0)\n                success = EnumServicesStatusExW(\n                  scm,\n                  :SC_ENUM_PROCESS_INFO,\n                  ALL_SERVICE_TYPES,\n                  SERVICE_STATE_ALL,\n                  buffer_ptr,\n                  buffer_ptr.size,\n                  bytes_pointer,\n                  svcs_ret_ptr,\n                  resume_ptr,\n                  FFI::Pointer::NULL\n                )\n                if success == FFI::WIN32_FALSE\n                  raise Puppet::Util::Windows::Error, _(\"Failed to fetch services\")\n                end\n\n                # Now that the buffer is populated with services\n                # we pull the data from memory using pointer arithmetic:\n                # the number of services returned by the function is\n                # available to be read from svcs_ret_ptr, and we iterate\n                # that many times moving the cursor pointer the length of\n                # ENUM_SERVICE_STATUS_PROCESSW.size. This should iterate\n                # over the buffer and extract each struct.\n                services_returned = svcs_ret_ptr.read_dword\n                cursor_ptr = FFI::Pointer.new(ENUM_SERVICE_STATUS_PROCESSW, buffer_ptr)\n                0.upto(services_returned - 1) do |index|\n                  service = ENUM_SERVICE_STATUS_PROCESSW.new(cursor_ptr[index])\n                  services[service[:lpServiceName].read_arbitrary_wide_string_up_to(SERVICENAME_MAX)] = {\n                    :display_name => service[:lpDisplayName].read_arbitrary_wide_string_up_to(SERVICENAME_MAX),\n                    :service_status_process => service[:ServiceStatusProcess]\n                  }\n                end\n              end # buffer_ptr\n            end # resume_ptr\n          end # scvs_ret_ptr\n        end # bytes_ptr\n      end # open_scm\n      services\n    end\n    module_function :services\n\n    class << self\n      # @api private\n      # Opens a connection to the SCManager on windows then uses that\n      # handle to create a handle to a specific service in windows\n      # corresponding to service_name\n      #\n      # this function takes a block that executes within the context of\n      # the open service handler, and will close the service and SCManager\n      # handles once the block finishes\n      #\n      # @param [string] service_name the name of the service to open\n      # @param [Integer] scm_access code corresponding to the access type requested for the scm\n      # @param [Integer] service_access code corresponding to the access type requested for the service\n      # @yieldparam [:handle] service the windows native handle used to access\n      #   the service\n      # @return the result of the block\n      def open_service(service_name, scm_access, service_access, &block)\n        service = FFI::Pointer::NULL_HANDLE\n\n        result = nil\n        open_scm(scm_access) do |scm|\n          service = OpenServiceW(scm, wide_string(service_name), service_access)\n          raise Puppet::Util::Windows::Error, _(\"Failed to open a handle to the service\") if service == FFI::Pointer::NULL_HANDLE\n\n          result = yield service\n        end\n\n        result\n      ensure\n        CloseServiceHandle(service)\n      end\n      private :open_service\n\n      # @api private\n      #\n      # Opens a handle to the service control manager\n      #\n      # @param [Integer] scm_access code corresponding to the access type requested for the scm\n      def open_scm(scm_access, &block)\n        scm = OpenSCManagerW(FFI::Pointer::NULL, FFI::Pointer::NULL, scm_access)\n        raise Puppet::Util::Windows::Error, _(\"Failed to open a handle to the service control manager\") if scm == FFI::Pointer::NULL_HANDLE\n\n        yield scm\n      ensure\n        CloseServiceHandle(scm)\n      end\n      private :open_scm\n\n      # @api private\n      # Transition the service to the specified state. The block should perform\n      # the actual transition.\n      #\n      # @param [String] service_name the name of the service to transition\n      # @param [[Integer]] valid_initial_states an array of valid states that the service can transition from\n      # @param [Integer] final_state the state that the service will transition to\n      # @param [Integer] timeout the minumum number of seconds to wait before timing out\n      def transition_service_state(service_name, valid_initial_states, final_state, timeout, &block)\n        service_access = SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS\n        open_service(service_name, SC_MANAGER_CONNECT, service_access) do |service|\n          query_status(service) do |status|\n            initial_state = status[:dwCurrentState]\n            # If the service is already in the final_state, then\n            # no further work needs to be done\n            if initial_state == final_state\n              Puppet.debug _(\"The service is already in the %{final_state} state. No further work needs to be done.\") % { final_state: SERVICE_STATES[final_state] }\n\n              next\n            end\n\n            # Check that initial_state corresponds to a valid\n            # initial state\n            unless valid_initial_states.include?(initial_state)\n              valid_initial_states_str = valid_initial_states.map do |state|\n                SERVICE_STATES[state]\n              end.join(\", \")\n\n              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] }\n            end\n\n            # Check if there's a pending transition to the final_state. If so, then wait for\n            # that transition to finish.\n            possible_pending_states = FINAL_STATES.keys.select do |pending_state|\n              # SERVICE_RUNNING has two pending states, SERVICE_START_PENDING and\n              # SERVICE_CONTINUE_PENDING. That is why we need the #select here\n              FINAL_STATES[pending_state] == final_state\n            end\n            if possible_pending_states.include?(initial_state)\n              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 }\n              wait_on_pending_state(service, initial_state, timeout)\n\n              next\n            end\n\n            # If we are in an unsafe pending state like SERVICE_START_PENDING\n            # or SERVICE_STOP_PENDING, then we want to wait for that pending\n            # transition to finish before transitioning the service state.\n            # The reason we do this is because SERVICE_START_PENDING is when\n            # the service thread is being created and initialized, while\n            # SERVICE_STOP_PENDING is when the service thread is being cleaned\n            # up and destroyed. Thus there is a chance that when the service is\n            # in either of these states, its service thread may not yet be ready\n            # to perform the state transition (it may not even exist).\n            if UNSAFE_PENDING_STATES.include?(initial_state)\n              Puppet.debug _(\"The service is in the %{pending_state} state, which is an unsafe pending state.\") % { pending_state: SERVICE_STATES[initial_state] }\n              wait_on_pending_state(service, initial_state, timeout)\n              initial_state = FINAL_STATES[initial_state]\n            end\n\n            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] }\n\n            yield service\n\n            Puppet.debug _(\"Waiting for the transition to finish\")\n            wait_on_state_transition(service, initial_state, final_state, timeout)\n          end\n        end\n      rescue => detail\n        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\n      end\n      private :transition_service_state\n\n      # @api private\n      # perform QueryServiceStatusEx on a windows service and return the\n      # result\n      #\n      # @param [:handle] service handle of the service to query\n      # @return [SERVICE_STATUS_PROCESS struct] the result of the query\n      def query_status(service)\n        size_required = nil\n        status = nil\n        # Fetch the bytes of memory required to be allocated\n        # for QueryServiceConfigW to return succesfully. This\n        # is done by sending NULL and 0 for the pointer and size\n        # respectively, letting the command fail, then reading the\n        # value of pcbBytesNeeded\n        FFI::MemoryPointer.new(:lpword) do |bytes_pointer|\n          # return value will be false from this call, since it's designed\n          # to fail. Just ignore it\n          QueryServiceStatusEx(\n            service,\n            :SC_STATUS_PROCESS_INFO,\n            FFI::Pointer::NULL,\n            0,\n            bytes_pointer\n          )\n          size_required = bytes_pointer.read_dword\n          FFI::MemoryPointer.new(size_required) do |ssp_ptr|\n            status = SERVICE_STATUS_PROCESS.new(ssp_ptr)\n            success = QueryServiceStatusEx(\n              service,\n              :SC_STATUS_PROCESS_INFO,\n              ssp_ptr,\n              size_required,\n              bytes_pointer\n            )\n            if success == FFI::WIN32_FALSE\n              raise Puppet::Util::Windows::Error, _(\"Service query failed\")\n            end\n\n            yield status\n          end\n        end\n      end\n      private :query_status\n\n      # @api private\n      # perform QueryServiceConfigW on a windows service and return the\n      # result\n      #\n      # @param [:handle] service handle of the service to query\n      # @return [QUERY_SERVICE_CONFIGW struct] the result of the query\n      def query_config(service, &block)\n        config = nil\n        size_required = nil\n        # Fetch the bytes of memory required to be allocated\n        # for QueryServiceConfigW to return succesfully. This\n        # is done by sending NULL and 0 for the pointer and size\n        # respectively, letting the command fail, then reading the\n        # value of pcbBytesNeeded\n        FFI::MemoryPointer.new(:lpword) do |bytes_pointer|\n          # return value will be false from this call, since it's designed\n          # to fail. Just ignore it\n          QueryServiceConfigW(service, FFI::Pointer::NULL, 0, bytes_pointer)\n          size_required = bytes_pointer.read_dword\n          FFI::MemoryPointer.new(size_required) do |ssp_ptr|\n            config = QUERY_SERVICE_CONFIGW.new(ssp_ptr)\n            success = QueryServiceConfigW(\n              service,\n              ssp_ptr,\n              size_required,\n              bytes_pointer\n            )\n            if success == FFI::WIN32_FALSE\n              raise Puppet::Util::Windows::Error, _(\"Service query failed\")\n            end\n\n            yield config\n          end\n        end\n      end\n      private :query_config\n\n      # @api private\n      # perform QueryServiceConfig2W on a windows service and return the\n      # result\n      #\n      # @param [:handle] service handle of the service to query\n      # @param [Integer] info_level the configuration information to be queried\n      # @return [QUERY_SERVICE_CONFIG2W struct] the result of the query\n      def query_config2(service, info_level, &block)\n        config = nil\n        size_required = nil\n        # Fetch the bytes of memory required to be allocated\n        # for QueryServiceConfig2W to return succesfully. This\n        # is done by sending NULL and 0 for the pointer and size\n        # respectively, letting the command fail, then reading the\n        # value of pcbBytesNeeded\n        FFI::MemoryPointer.new(:lpword) do |bytes_pointer|\n          # return value will be false from this call, since it's designed\n          # to fail. Just ignore it\n          QueryServiceConfig2W(service, info_level, FFI::Pointer::NULL, 0, bytes_pointer)\n          size_required = bytes_pointer.read_dword\n          FFI::MemoryPointer.new(size_required) do |ssp_ptr|\n            # We need to supply the appropriate struct to be created based on\n            # the info_level\n            case info_level\n            when SERVICE_CONFIG_DELAYED_AUTO_START_INFO\n              config = SERVICE_DELAYED_AUTO_START_INFO.new(ssp_ptr)\n            end\n            success = QueryServiceConfig2W(\n              service,\n              info_level,\n              ssp_ptr,\n              size_required,\n              bytes_pointer\n            )\n            if success == FFI::WIN32_FALSE\n              raise Puppet::Util::Windows::Error, _(\"Service query for %{parameter_name} failed\") % { parameter_name: SERVICE_CONFIG_TYPES[info_level] }\n            end\n\n            yield config\n          end\n        end\n      end\n      private :query_config2\n\n      # @api private\n      # Sets an optional parameter on a service by calling\n      # ChangeServiceConfig2W\n      #\n      # @param [String] service_name name of service\n      # @param [Integer] change parameter to change\n      # @param [struct] value appropriate struct based on the parameter to change\n      def set_optional_parameter(service_name, change, value)\n        open_service(service_name, SC_MANAGER_CONNECT, SERVICE_CHANGE_CONFIG) do |service|\n          success = ChangeServiceConfig2W(\n            service,\n            change, # dwInfoLevel\n            value # lpInfo\n          )\n          if success == FFI::WIN32_FALSE\n            raise Puppet::Util.windows::Error, _(\"Failed to update service %{change} configuration\") % { change: change }\n          end\n        end\n      end\n      private :set_optional_parameter\n\n      # @api private\n      # Controls the delayed auto-start setting of a service\n      #\n      # @param [String] service_name name of service\n      # @param [Bool] delayed whether the service should be started with a delay or not\n      def set_startup_mode_delayed(service_name, delayed)\n        delayed_start = SERVICE_DELAYED_AUTO_START_INFO.new\n        delayed_start[:fDelayedAutostart] = delayed\n        set_optional_parameter(service_name, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayed_start)\n      end\n      private :set_startup_mode_delayed\n\n      # @api private\n      # Sends a service control signal to a service\n      #\n      # @param [:handle] service handle to the service\n      # @param [Integer] signal the service control signal to send\n      def send_service_control_signal(service, signal)\n        FFI::MemoryPointer.new(SERVICE_STATUS.size) do |status_ptr|\n          status = SERVICE_STATUS.new(status_ptr)\n          if ControlService(service, signal, status) == FFI::WIN32_FALSE\n            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]] }\n          end\n        end\n      end\n\n      # @api private\n      # Waits for a service to transition from one state to\n      # another state.\n      #\n      # @param [:handle] service handle to the service to wait on\n      # @param [Integer] initial_state the state that the service is transitioning from.\n      # @param [Integer] final_state the state that the service is transitioning to\n      # @param [Integer] timeout the minumum number of seconds to wait before timing out\n      def wait_on_state_transition(service, initial_state, final_state, timeout)\n        # Get the pending state for this transition. Note that SERVICE_RUNNING\n        # has two possible pending states, which is why we need this logic.\n        if final_state != SERVICE_RUNNING\n          pending_state = FINAL_STATES.key(final_state)\n        elsif initial_state == SERVICE_STOPPED\n          # SERVICE_STOPPED => SERVICE_RUNNING\n          pending_state = SERVICE_START_PENDING\n        else\n          # SERVICE_PAUSED => SERVICE_RUNNING\n          pending_state = SERVICE_CONTINUE_PENDING\n        end\n\n        # Wait for the transition to finish\n        state = nil\n        elapsed_time = 0\n        while elapsed_time <= timeout\n\n          query_status(service) do |status|\n            state = status[:dwCurrentState]\n            return if state == final_state\n\n            if state == pending_state\n              Puppet.debug _(\"The service transitioned to the %{pending_state} state.\") % { pending_state: SERVICE_STATES[pending_state] }\n              wait_on_pending_state(service, pending_state, timeout)\n              return\n            end\n            sleep(1)\n            elapsed_time += 1\n          end\n        end\n        # Timed out while waiting for the transition to finish. Raise an error\n        # We can still use the state variable read from the FFI struct because\n        # FFI creates new Integer objects during an assignment of an integer value\n        # stored in an FFI struct. We verified that the '=' operater is safe\n        # from the freed memory since the new ruby object created during the\n        # assignment will remain in ruby memory and remain immutable and constant.\n        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] }\n      end\n      private :wait_on_state_transition\n\n      # @api private\n      # Waits for a service to finish transitioning from\n      # a pending state. The service must be in the pending state\n      # before invoking this routine.\n      #\n      # @param [:handle] service handle to the service to wait on\n      # @param [Integer] pending_state the pending state\n      # @param [Integer] timeout the minumum number of seconds to wait before timing out\n      def wait_on_pending_state(service, pending_state, timeout)\n        final_state = FINAL_STATES[pending_state]\n\n        Puppet.debug _(\"Waiting for the pending transition to the %{final_state} state to finish.\") % { final_state: SERVICE_STATES[final_state] }\n\n        elapsed_time = 0\n        last_checkpoint = -1\n        loop do\n          query_status(service) do |status|\n            state = status[:dwCurrentState]\n            checkpoint = status[:dwCheckPoint]\n            wait_hint = status[:dwWaitHint]\n            # Check if our service has finished transitioning to\n            # the final_state OR if an unexpected transition\n            # has occurred\n            return if state == final_state\n            unless state == pending_state\n              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] }\n            end\n\n            # Check if any progress has been made since our last sleep\n            # using the dwCheckPoint. If no progress has been made then\n            # check if we've timed out, and raise an error if so\n            if checkpoint > last_checkpoint\n              elapsed_time = 0\n              last_checkpoint = checkpoint\n            else\n              wait_hint = milliseconds_to_seconds(status[:dwWaitHint])\n              timeout = wait_hint < timeout ? timeout : wait_hint\n\n              if elapsed_time >= timeout\n                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] }\n              end\n            end\n            wait_time = wait_hint_to_wait_time(wait_hint)\n            # Wait a bit before rechecking the service's state\n            sleep(wait_time)\n            elapsed_time += wait_time\n          end\n        end\n      end\n      private :wait_on_pending_state\n\n      # @api private\n      #\n      # create a usable wait time to wait between querying the service.\n      #\n      # @param [Integer] wait_hint the wait hint of a service in milliseconds\n      # @return [Integer] the time to wait in seconds between querying the service\n      def wait_hint_to_wait_time(wait_hint)\n        # Wait 1/10th the wait_hint, but no less than 1 and\n        # no more than 10 seconds\n        wait_time = milliseconds_to_seconds(wait_hint) / 10;\n        wait_time = 1 if wait_time < 1\n        wait_time = 10 if wait_time > 10\n        wait_time\n      end\n      private :wait_hint_to_wait_time\n\n      # @api private\n      #\n      # process the wait hint listed by a service to something\n      # usable by ruby sleep\n      #\n      # @param [Integer] wait_hint the wait hint of a service in milliseconds\n      # @return [Integer] wait_hint in seconds\n      def milliseconds_to_seconds(wait_hint)\n        wait_hint / 1000;\n      end\n      private :milliseconds_to_seconds\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/sid.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\n\nmodule Puppet::Util::Windows\n  module SID\n    require 'ffi'\n    extend FFI::Library\n\n    # missing from Windows::Error\n    ERROR_NONE_MAPPED                  = 1332\n    ERROR_INVALID_SID_STRUCTURE        = 1337\n    ERROR_TRUSTED_DOMAIN_FAILURE       = 1788\n    ERROR_TRUSTED_RELATIONSHIP_FAILURE = 1789\n\n    # Well Known SIDs\n    Null                        = 'S-1-0'\n    Nobody                      = 'S-1-0-0'\n    World                       = 'S-1-1'\n    Everyone                    = 'S-1-1-0'\n    Local                       = 'S-1-2'\n    Creator                     = 'S-1-3'\n    CreatorOwner                = 'S-1-3-0'\n    CreatorGroup                = 'S-1-3-1'\n    CreatorOwnerServer          = 'S-1-3-2'\n    CreatorGroupServer          = 'S-1-3-3'\n    NonUnique                   = 'S-1-4'\n    Nt                          = 'S-1-5'\n    Dialup                      = 'S-1-5-1'\n    Network                     = 'S-1-5-2'\n    Batch                       = 'S-1-5-3'\n    Interactive                 = 'S-1-5-4'\n    Service                     = 'S-1-5-6'\n    Anonymous                   = 'S-1-5-7'\n    Proxy                       = 'S-1-5-8'\n    EnterpriseDomainControllers = 'S-1-5-9'\n    PrincipalSelf               = 'S-1-5-10'\n    AuthenticatedUsers          = 'S-1-5-11'\n    RestrictedCode              = 'S-1-5-12'\n    TerminalServerUsers         = 'S-1-5-13'\n    LocalSystem                 = 'S-1-5-18'\n    NtLocal                     = 'S-1-5-19'\n    NtNetwork                   = 'S-1-5-20'\n    BuiltinAdministrators       = 'S-1-5-32-544'\n    BuiltinUsers                = 'S-1-5-32-545'\n    Guests                      = 'S-1-5-32-546'\n    PowerUsers                  = 'S-1-5-32-547'\n    AccountOperators            = 'S-1-5-32-548'\n    ServerOperators             = 'S-1-5-32-549'\n    PrintOperators              = 'S-1-5-32-550'\n    BackupOperators             = 'S-1-5-32-551'\n    Replicators                 = 'S-1-5-32-552'\n    AllAppPackages              = 'S-1-15-2-1'\n\n    # Convert an account name, e.g. 'Administrators' into a SID string,\n    # e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators',\n    # 'BUILTIN\\Administrators', or 'S-1-5-32-544', and will return the\n    # SID. Returns nil if the account doesn't exist.\n    def name_to_sid(name)\n      sid = name_to_principal(name)\n\n      sid ? sid.sid : nil\n    end\n    module_function :name_to_sid\n\n    # Convert an account name, e.g. 'Administrators' into a Principal::SID object,\n    # e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators',\n    # 'BUILTIN\\Administrators', or 'S-1-5-32-544', and will return the\n    # SID object. Returns nil if the account doesn't exist.\n    # This method returns a SID::Principal with the account, domain, SID, etc\n    def name_to_principal(name, allow_unresolved = false)\n      # Apparently, we accept a symbol..\n      name = name.to_s.strip if name\n\n      # if name is a SID string, convert it to raw bytes for use with lookup_account_sid\n      raw_sid_bytes = nil\n      begin\n        string_to_sid_ptr(name) do |sid_ptr|\n          raw_sid_bytes = sid_ptr.read_array_of_uchar(get_length_sid(sid_ptr))\n        end\n      rescue => e\n        # Avoid debug logs pollution with valid account names\n        # https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsidtosidw#return-value\n        Puppet.debug(\"Could not retrieve raw SID bytes from '#{name}': #{e.message}\") unless e.code == ERROR_INVALID_SID_STRUCTURE\n      end\n\n      raw_sid_bytes ? Principal.lookup_account_sid(raw_sid_bytes) : Principal.lookup_account_name(name)\n    rescue => e\n      Puppet.debug(e.message.to_s)\n      (allow_unresolved && raw_sid_bytes) ? unresolved_principal(name, raw_sid_bytes) : nil\n    end\n    module_function :name_to_principal\n    class << self; alias name_to_sid_object name_to_principal; end\n\n    # Converts an octet string array of bytes to a SID::Principal object,\n    # e.g. [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0] is the representation for\n    # S-1-5-18, the local 'SYSTEM' account.\n    # Raises an Error for nil or non-array input.\n    # This method returns a SID::Principal with the account, domain, SID, etc\n    def octet_string_to_principal(bytes)\n      if !bytes || !bytes.respond_to?('pack') || bytes.empty?\n        raise Puppet::Util::Windows::Error, _(\"Octet string must be an array of bytes\")\n      end\n\n      Principal.lookup_account_sid(bytes)\n    end\n    module_function :octet_string_to_principal\n    class << self; alias octet_string_to_sid_object octet_string_to_principal; end\n\n    # Converts a COM instance of IAdsUser or IAdsGroup to a SID::Principal object,\n    # Raises an Error for nil or an object without an objectSID / Name property.\n    # This method returns a SID::Principal with the account, domain, SID, etc\n    # This method will return instances even when the SID is unresolvable, as\n    # may be the case when domain users have been added to local groups, but\n    # removed from the domain\n    def ads_to_principal(ads_object)\n      if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||\n         !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)\n        raise Puppet::Error, \"ads_object must be an IAdsUser or IAdsGroup instance\"\n      end\n\n      octet_string_to_principal(ads_object.objectSID)\n    rescue Puppet::Util::Windows::Error => e\n      # if the error is not a lookup / mapping problem, immediately re-raise\n      raise if e.code != ERROR_NONE_MAPPED\n\n      # if the Name property isn't formatted like a SID, OR\n      if !valid_sid?(ads_object.Name) ||\n         # if the objectSID doesn't match the Name property, also raise\n         ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)\n        raise Puppet::Error.new(\"ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})\", e)\n      end\n\n      unresolved_principal(ads_object.Name, ads_object.objectSID)\n    end\n    module_function :ads_to_principal\n\n    # Convert a SID string, e.g. \"S-1-5-32-544\" to a name,\n    # e.g. 'BUILTIN\\Administrators'. Returns nil if an account\n    # for that SID does not exist.\n    def sid_to_name(value)\n      sid_bytes = []\n      begin\n        string_to_sid_ptr(value) do |ptr|\n          sid_bytes = ptr.read_array_of_uchar(get_length_sid(ptr))\n        end\n      rescue Puppet::Util::Windows::Error => e\n        raise if e.code != ERROR_INVALID_SID_STRUCTURE\n      end\n\n      Principal.lookup_account_sid(sid_bytes).domain_account\n    rescue\n      nil\n    end\n    module_function :sid_to_name\n\n    # https://stackoverflow.com/a/1792930 - 68 bytes, 184 characters in a string\n    MAXIMUM_SID_STRING_LENGTH = 184\n\n    # Convert a SID pointer to a SID string, e.g. \"S-1-5-32-544\".\n    def sid_ptr_to_string(psid)\n      if !psid.is_a?(FFI::Pointer) || IsValidSid(psid) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Invalid SID\")\n      end\n\n      sid_string = nil\n      FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr|\n        if ConvertSidToStringSidW(psid, buffer_ptr) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to convert binary SID\")\n        end\n\n        buffer_ptr.read_win32_local_pointer do |wide_string_ptr|\n          if wide_string_ptr.null?\n            raise Puppet::Error, _(\"ConvertSidToStringSidW failed to allocate buffer for sid\")\n          end\n\n          sid_string = wide_string_ptr.read_arbitrary_wide_string_up_to(MAXIMUM_SID_STRING_LENGTH)\n        end\n      end\n\n      sid_string\n    end\n    module_function :sid_ptr_to_string\n\n    # Convert a SID string, e.g. \"S-1-5-32-544\" to a pointer (containing the\n    # address of the binary SID structure). The returned value can be used in\n    # Win32 APIs that expect a PSID, e.g. IsValidSid. The account for this\n    # SID may or may not exist.\n    def string_to_sid_ptr(string_sid, &block)\n      FFI::MemoryPointer.from_string_to_wide_string(string_sid) do |lpcwstr|\n        FFI::MemoryPointer.new(:pointer, 1) do |sid_ptr_ptr|\n          if ConvertStringSidToSidW(lpcwstr, sid_ptr_ptr) == FFI::WIN32_FALSE\n            raise Puppet::Util::Windows::Error, _(\"Failed to convert string SID: %{string_sid}\") % { string_sid: string_sid }\n          end\n\n          sid_ptr_ptr.read_win32_local_pointer do |sid_ptr|\n            yield sid_ptr\n          end\n        end\n      end\n\n      # yielded sid_ptr has already had LocalFree called, nothing to return\n      nil\n    end\n    module_function :string_to_sid_ptr\n\n    # Return true if the string is a valid SID, e.g. \"S-1-5-32-544\", false otherwise.\n    def valid_sid?(string_sid)\n      valid = false\n\n      begin\n        string_to_sid_ptr(string_sid) { |ptr| valid = !ptr.nil? && !ptr.null? }\n      rescue Puppet::Util::Windows::Error => e\n        raise if e.code != ERROR_INVALID_SID_STRUCTURE\n      end\n\n      valid\n    end\n    module_function :valid_sid?\n\n    def get_length_sid(sid_ptr)\n      # MSDN states IsValidSid should be called on pointer first\n      if !sid_ptr.is_a?(FFI::Pointer) || IsValidSid(sid_ptr) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Invalid SID\")\n      end\n\n      GetLengthSid(sid_ptr)\n    end\n    module_function :get_length_sid\n\n    def octet_string_to_sid_string(sid_bytes)\n      sid_string = nil\n\n      FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|\n        sid_ptr.write_array_of_uchar(sid_bytes)\n        sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr)\n      end\n\n      sid_string\n    end\n    module_function :octet_string_to_sid_string\n\n    # @api private\n    def self.unresolved_principal(name, sid_bytes)\n      Principal.new(\n        name, # account\n        sid_bytes, # sid_bytes\n        name, # sid string\n        nil, # domain\n        # https://msdn.microsoft.com/en-us/library/cc245534.aspx?f=255&MSPPError=-2147217396\n        # Indicates that the type of object could not be determined. For example, no object with that SID exists.\n        :SidTypeUnknown\n      )\n    end\n\n    ffi_convention :stdcall\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379151(v=vs.85).aspx\n    # BOOL WINAPI IsValidSid(\n    #   _In_  PSID pSid\n    # );\n    ffi_lib :advapi32\n    attach_function_private :IsValidSid,\n                            [:pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376399(v=vs.85).aspx\n    # BOOL ConvertSidToStringSid(\n    #   _In_   PSID Sid,\n    #   _Out_  LPTSTR *StringSid\n    # );\n    ffi_lib :advapi32\n    attach_function_private :ConvertSidToStringSidW,\n                            [:pointer, :pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376402(v=vs.85).aspx\n    # BOOL WINAPI ConvertStringSidToSid(\n    #   _In_   LPCTSTR StringSid,\n    #   _Out_  PSID *Sid\n    # );\n    ffi_lib :advapi32\n    attach_function_private :ConvertStringSidToSidW,\n                            [:lpcwstr, :pointer], :win32_bool\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446642(v=vs.85).aspx\n    # DWORD WINAPI GetLengthSid(\n    #   _In_ PSID pSid\n    # );\n    ffi_lib :advapi32\n    attach_function_private :GetLengthSid, [:pointer], :dword\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/string.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  module Util\n    module Windows\n      module String\n        def wide_string(str)\n          # if given a nil string, assume caller wants to pass a nil pointer to win32\n          return nil if str.nil?\n\n          str.encode('UTF-16LE')\n        end\n        module_function :wide_string\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/windows/user.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../../puppet/util/windows'\n\nrequire 'ffi'\n\nmodule Puppet::Util::Windows::User\n  extend Puppet::Util::Windows::String\n  extend FFI::Library\n\n  def admin?\n    return false unless check_token_membership\n\n    # if Vista or later, check for unrestricted process token\n    elevated_supported = Puppet::Util::Windows::Process.supports_elevated_security?\n    elevated_supported ? Puppet::Util::Windows::Process.elevated_security? : true\n  end\n  module_function :admin?\n\n  # The name of the account in all locales is `LocalSystem`. `.\\LocalSystem` or `ComputerName\\LocalSystem' can also be used.\n  # This account is not recognized by the security subsystem, so you cannot specify its name in a call to the `LookupAccountName` function.\n  # https://docs.microsoft.com/en-us/windows/win32/services/localsystem-account\n  def localsystem?(name)\n    [\"LocalSystem\", \".\\\\LocalSystem\", \"#{Puppet::Util::Windows::ADSI.computer_name}\\\\LocalSystem\"].any? { |s| s.casecmp(name) == 0 }\n  end\n  module_function :localsystem?\n\n  # Check if a given user is one of the default system accounts\n  # These accounts do not have a password and all checks done through logon attempt will fail\n  # https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/local-accounts#default-local-system-accounts\n  def default_system_account?(name)\n    user_sid = Puppet::Util::Windows::SID.name_to_sid(name)\n    [Puppet::Util::Windows::SID::LocalSystem, Puppet::Util::Windows::SID::NtLocal, Puppet::Util::Windows::SID::NtNetwork].include?(user_sid)\n  end\n  module_function :default_system_account?\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/ee207397(v=vs.85).aspx\n  SECURITY_MAX_SID_SIZE = 68\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx\n  # These error codes indicate successful authentication but failure to\n  # logon for a separate reason\n  ERROR_ACCOUNT_RESTRICTION = 1327\n  ERROR_INVALID_LOGON_HOURS = 1328\n  ERROR_INVALID_WORKSTATION = 1329\n  ERROR_ACCOUNT_DISABLED    = 1331\n\n  def check_token_membership\n    is_admin = false\n    FFI::MemoryPointer.new(:byte, SECURITY_MAX_SID_SIZE) do |sid_pointer|\n      FFI::MemoryPointer.new(:dword, 1) do |size_pointer|\n        size_pointer.write_uint32(SECURITY_MAX_SID_SIZE)\n\n        if CreateWellKnownSid(:WinBuiltinAdministratorsSid, FFI::Pointer::NULL, sid_pointer, size_pointer) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to create administrators SID\")\n        end\n      end\n\n      if IsValidSid(sid_pointer) == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, _(\"Invalid SID\")\n      end\n\n      FFI::MemoryPointer.new(:win32_bool, 1) do |ismember_pointer|\n        if CheckTokenMembership(FFI::Pointer::NULL_HANDLE, sid_pointer, ismember_pointer) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to check membership\")\n        end\n\n        # Is administrators SID enabled in calling thread's access token?\n        is_admin = ismember_pointer.read_win32_bool\n      end\n    end\n\n    is_admin\n  end\n  module_function :check_token_membership\n\n  def password_is?(name, password, domain = '.')\n    logon_user(name, password, domain) { |token| }\n  rescue Puppet::Util::Windows::Error => detail\n    authenticated_error_codes = Set[\n      ERROR_ACCOUNT_RESTRICTION,\n      ERROR_INVALID_LOGON_HOURS,\n      ERROR_INVALID_WORKSTATION,\n      ERROR_ACCOUNT_DISABLED,\n    ]\n\n    authenticated_error_codes.include?(detail.code)\n  end\n  module_function :password_is?\n\n  def logon_user(name, password, domain = '.', &block)\n    fLOGON32_PROVIDER_DEFAULT = 0\n    fLOGON32_LOGON_INTERACTIVE = 2\n    fLOGON32_LOGON_NETWORK = 3\n\n    token = nil\n    begin\n      FFI::MemoryPointer.new(:handle, 1) do |token_pointer|\n        # try logon using network else try logon using interactive mode\n        if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE\n          if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_INTERACTIVE, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE\n            raise Puppet::Util::Windows::Error, _(\"Failed to logon user %{name}\") % { name: name.inspect }\n          end\n        end\n\n        yield token = token_pointer.read_handle\n      end\n    ensure\n      FFI::WIN32.CloseHandle(token) if token\n    end\n\n    # token has been closed by this point\n    true\n  end\n  module_function :logon_user\n\n  def self.logon_user_by_logon_type(name, domain, password, logon_type, logon_provider, token)\n    LogonUserW(wide_string(name), wide_string(domain), password.nil? ? FFI::Pointer::NULL : wide_string(password), logon_type, logon_provider, token)\n  end\n\n  private_class_method :logon_user_by_logon_type\n\n  def load_profile(user, password)\n    logon_user(user, password) do |token|\n      FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName|\n        pi = PROFILEINFO.new\n        pi[:dwSize] = PROFILEINFO.size\n        pi[:dwFlags] = 1 # PI_NOUI - prevents display of profile error msgs\n        pi[:lpUserName] = lpUserName\n\n        # Load the profile. Since it doesn't exist, it will be created\n        if LoadUserProfileW(token, pi.pointer) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to load user profile %{user}\") % { user: user.inspect }\n        end\n\n        Puppet.debug(\"Loaded profile for #{user}\")\n\n        if UnloadUserProfile(token, pi[:hProfile]) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to unload user profile %{user}\") % { user: user.inspect }\n        end\n      end\n    end\n  end\n  module_function :load_profile\n\n  def get_rights(name)\n    user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\\.\\\\/, \"#{Puppet::Util::Windows::ADSI.computer_name}\\\\\"))\n    return \"\" unless user_info\n\n    rights = []\n    rights_pointer = FFI::MemoryPointer.new(:pointer)\n    number_of_rights = FFI::MemoryPointer.new(:ulong)\n    sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes)\n\n    new_lsa_policy_handle do |policy_handle|\n      result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, number_of_rights)\n      check_lsa_nt_status_and_raise_failures(result, \"LsaEnumerateAccountRights\")\n    end\n\n    number_of_rights.read_ulong.times do |index|\n      right = LSA_UNICODE_STRING.new(rights_pointer.read_pointer + index * LSA_UNICODE_STRING.size)\n      rights << right[:Buffer].read_arbitrary_wide_string_up_to\n    end\n\n    result = LsaFreeMemory(rights_pointer.read_pointer)\n    check_lsa_nt_status_and_raise_failures(result, \"LsaFreeMemory\")\n\n    rights.join(\",\")\n  end\n  module_function :get_rights\n\n  def set_rights(name, rights)\n    rights_pointer = new_lsa_unicode_strings_pointer(rights)\n    user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\\.\\\\/, \"#{Puppet::Util::Windows::ADSI.computer_name}\\\\\"))\n    sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes)\n\n    new_lsa_policy_handle do |policy_handle|\n      result = LsaAddAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, rights.size)\n      check_lsa_nt_status_and_raise_failures(result, \"LsaAddAccountRights\")\n    end\n  end\n  module_function :set_rights\n\n  def remove_rights(name, rights)\n    rights_pointer = new_lsa_unicode_strings_pointer(rights)\n    user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\\.\\\\/, \"#{Puppet::Util::Windows::ADSI.computer_name}\\\\\"))\n    sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes)\n\n    new_lsa_policy_handle do |policy_handle|\n      result = LsaRemoveAccountRights(policy_handle.read_pointer, sid_pointer, false, rights_pointer, rights.size)\n      check_lsa_nt_status_and_raise_failures(result, \"LsaRemoveAccountRights\")\n    end\n  end\n  module_function :remove_rights\n\n  # ACCESS_MASK flags for Policy Objects\n  # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/b61b7268-987a-420b-84f9-6c75f8dc8558\n  POLICY_VIEW_LOCAL_INFORMATION   = 0x00000001\n  POLICY_VIEW_AUDIT_INFORMATION   = 0x00000002\n  POLICY_GET_PRIVATE_INFORMATION  = 0x00000004\n  POLICY_TRUST_ADMIN              = 0x00000008\n  POLICY_CREATE_ACCOUNT           = 0x00000010\n  POLICY_CREATE_SECRET            = 0x00000020\n  POLICY_CREATE_PRIVILEGE         = 0x00000040\n  POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080\n  POLICY_SET_AUDIT_REQUIREMENTS   = 0x00000100\n  POLICY_AUDIT_LOG_ADMIN          = 0x00000200\n  POLICY_SERVER_ADMIN             = 0x00000400\n  POLICY_LOOKUP_NAMES             = 0x00000800\n  POLICY_NOTIFICATION             = 0x00001000\n\n  def self.new_lsa_policy_handle\n    access = 0\n    access |= POLICY_LOOKUP_NAMES\n    access |= POLICY_CREATE_ACCOUNT\n    policy_handle = FFI::MemoryPointer.new(:pointer)\n\n    result = LsaOpenPolicy(nil, LSA_OBJECT_ATTRIBUTES.new, access, policy_handle)\n    check_lsa_nt_status_and_raise_failures(result, \"LsaOpenPolicy\")\n\n    begin\n      yield policy_handle\n    ensure\n      result = LsaClose(policy_handle.read_pointer)\n      check_lsa_nt_status_and_raise_failures(result, \"LsaClose\")\n    end\n  end\n  private_class_method :new_lsa_policy_handle\n\n  def self.new_lsa_unicode_strings_pointer(strings)\n    lsa_unicode_strings_pointer = FFI::MemoryPointer.new(LSA_UNICODE_STRING, strings.size)\n\n    strings.each_with_index do |string, index|\n      lsa_string = LSA_UNICODE_STRING.new(lsa_unicode_strings_pointer + index * LSA_UNICODE_STRING.size)\n      lsa_string[:Buffer] = FFI::MemoryPointer.from_string(wide_string(string))\n      lsa_string[:Length] = string.length * 2\n      lsa_string[:MaximumLength] = lsa_string[:Length] + 2\n    end\n\n    lsa_unicode_strings_pointer\n  end\n  private_class_method :new_lsa_unicode_strings_pointer\n\n  # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d\n  def self.check_lsa_nt_status_and_raise_failures(status, method_name)\n    error_code = LsaNtStatusToWinError(status)\n\n    error_reason = case error_code.to_s(16)\n                   when '0' # ERROR_SUCCESS\n                     return # Method call succeded\n                   when '2' # ERROR_FILE_NOT_FOUND\n                     return # No rights/privilleges assigned to given user\n                   when '5' # ERROR_ACCESS_DENIED\n                     \"Access is denied. Please make sure that puppet is running as administrator.\"\n                   when '521' # ERROR_NO_SUCH_PRIVILEGE\n                     \"One or more of the given rights/privilleges are incorrect.\"\n                   when '6ba' # RPC_S_SERVER_UNAVAILABLE\n                     \"The RPC server is unavailable or given domain name is invalid.\"\n                   end\n\n    raise Puppet::Error, \"Calling `#{method_name}` returned 'Win32 Error Code 0x%08X'. #{error_reason}\" % error_code\n  end\n  private_class_method :check_lsa_nt_status_and_raise_failures\n\n  ffi_convention :stdcall\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx\n  # BOOL LogonUser(\n  #   _In_      LPTSTR lpszUsername,\n  #   _In_opt_  LPTSTR lpszDomain,\n  #   _In_opt_  LPTSTR lpszPassword,\n  #   _In_      DWORD dwLogonType,\n  #   _In_      DWORD dwLogonProvider,\n  #   _Out_     PHANDLE phToken\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LogonUserW,\n                          [:lpwstr, :lpwstr, :lpwstr, :dword, :dword, :phandle], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/bb773378(v=vs.85).aspx\n  # typedef struct _PROFILEINFO {\n  #   DWORD  dwSize;\n  #   DWORD  dwFlags;\n  #   LPTSTR lpUserName;\n  #   LPTSTR lpProfilePath;\n  #   LPTSTR lpDefaultPath;\n  #   LPTSTR lpServerName;\n  #   LPTSTR lpPolicyPath;\n  #   HANDLE hProfile;\n  # } PROFILEINFO, *LPPROFILEINFO;\n  # technically\n  # NOTE: that for structs, buffer_* (lptstr alias) cannot be used\n  class PROFILEINFO < FFI::Struct\n    layout :dwSize, :dword,\n           :dwFlags, :dword,\n           :lpUserName, :pointer,\n           :lpProfilePath, :pointer,\n           :lpDefaultPath, :pointer,\n           :lpServerName, :pointer,\n           :lpPolicyPath, :pointer,\n           :hProfile, :handle\n  end\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/bb762281(v=vs.85).aspx\n  # BOOL WINAPI LoadUserProfile(\n  #   _In_     HANDLE hToken,\n  #   _Inout_  LPPROFILEINFO lpProfileInfo\n  # );\n  ffi_lib :userenv\n  attach_function_private :LoadUserProfileW,\n                          [:handle, :pointer], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/bb762282(v=vs.85).aspx\n  # BOOL WINAPI UnloadUserProfile(\n  #   _In_  HANDLE hToken,\n  #   _In_  HANDLE hProfile\n  # );\n  ffi_lib :userenv\n  attach_function_private :UnloadUserProfile,\n                          [:handle, :handle], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376389(v=vs.85).aspx\n  # BOOL WINAPI CheckTokenMembership(\n  #   _In_opt_  HANDLE TokenHandle,\n  #   _In_      PSID SidToCheck,\n  #   _Out_     PBOOL IsMember\n  # );\n  ffi_lib :advapi32\n  attach_function_private :CheckTokenMembership,\n                          [:handle, :pointer, :pbool], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379650(v=vs.85).aspx\n  # rubocop:disable Layout/SpaceBeforeComma\n  WELL_KNOWN_SID_TYPE = enum(\n    :WinNullSid                                   , 0,\n    :WinWorldSid                                  , 1,\n    :WinLocalSid                                  , 2,\n    :WinCreatorOwnerSid                           , 3,\n    :WinCreatorGroupSid                           , 4,\n    :WinCreatorOwnerServerSid                     , 5,\n    :WinCreatorGroupServerSid                     , 6,\n    :WinNtAuthoritySid                            , 7,\n    :WinDialupSid                                 , 8,\n    :WinNetworkSid                                , 9,\n    :WinBatchSid                                  , 10,\n    :WinInteractiveSid                            , 11,\n    :WinServiceSid                                , 12,\n    :WinAnonymousSid                              , 13,\n    :WinProxySid                                  , 14,\n    :WinEnterpriseControllersSid                  , 15,\n    :WinSelfSid                                   , 16,\n    :WinAuthenticatedUserSid                      , 17,\n    :WinRestrictedCodeSid                         , 18,\n    :WinTerminalServerSid                         , 19,\n    :WinRemoteLogonIdSid                          , 20,\n    :WinLogonIdsSid                               , 21,\n    :WinLocalSystemSid                            , 22,\n    :WinLocalServiceSid                           , 23,\n    :WinNetworkServiceSid                         , 24,\n    :WinBuiltinDomainSid                          , 25,\n    :WinBuiltinAdministratorsSid                  , 26,\n    :WinBuiltinUsersSid                           , 27,\n    :WinBuiltinGuestsSid                          , 28,\n    :WinBuiltinPowerUsersSid                      , 29,\n    :WinBuiltinAccountOperatorsSid                , 30,\n    :WinBuiltinSystemOperatorsSid                 , 31,\n    :WinBuiltinPrintOperatorsSid                  , 32,\n    :WinBuiltinBackupOperatorsSid                 , 33,\n    :WinBuiltinReplicatorSid                      , 34,\n    :WinBuiltinPreWindows2000CompatibleAccessSid  , 35,\n    :WinBuiltinRemoteDesktopUsersSid              , 36,\n    :WinBuiltinNetworkConfigurationOperatorsSid   , 37,\n    :WinAccountAdministratorSid                   , 38,\n    :WinAccountGuestSid                           , 39,\n    :WinAccountKrbtgtSid                          , 40,\n    :WinAccountDomainAdminsSid                    , 41,\n    :WinAccountDomainUsersSid                     , 42,\n    :WinAccountDomainGuestsSid                    , 43,\n    :WinAccountComputersSid                       , 44,\n    :WinAccountControllersSid                     , 45,\n    :WinAccountCertAdminsSid                      , 46,\n    :WinAccountSchemaAdminsSid                    , 47,\n    :WinAccountEnterpriseAdminsSid                , 48,\n    :WinAccountPolicyAdminsSid                    , 49,\n    :WinAccountRasAndIasServersSid                , 50,\n    :WinNTLMAuthenticationSid                     , 51,\n    :WinDigestAuthenticationSid                   , 52,\n    :WinSChannelAuthenticationSid                 , 53,\n    :WinThisOrganizationSid                       , 54,\n    :WinOtherOrganizationSid                      , 55,\n    :WinBuiltinIncomingForestTrustBuildersSid     , 56,\n    :WinBuiltinPerfMonitoringUsersSid             , 57,\n    :WinBuiltinPerfLoggingUsersSid                , 58,\n    :WinBuiltinAuthorizationAccessSid             , 59,\n    :WinBuiltinTerminalServerLicenseServersSid    , 60,\n    :WinBuiltinDCOMUsersSid                       , 61,\n    :WinBuiltinIUsersSid                          , 62,\n    :WinIUserSid                                  , 63,\n    :WinBuiltinCryptoOperatorsSid                 , 64,\n    :WinUntrustedLabelSid                         , 65,\n    :WinLowLabelSid                               , 66,\n    :WinMediumLabelSid                            , 67,\n    :WinHighLabelSid                              , 68,\n    :WinSystemLabelSid                            , 69,\n    :WinWriteRestrictedCodeSid                    , 70,\n    :WinCreatorOwnerRightsSid                     , 71,\n    :WinCacheablePrincipalsGroupSid               , 72,\n    :WinNonCacheablePrincipalsGroupSid            , 73,\n    :WinEnterpriseReadonlyControllersSid          , 74,\n    :WinAccountReadonlyControllersSid             , 75,\n    :WinBuiltinEventLogReadersGroup               , 76,\n    :WinNewEnterpriseReadonlyControllersSid       , 77,\n    :WinBuiltinCertSvcDComAccessGroup             , 78,\n    :WinMediumPlusLabelSid                        , 79,\n    :WinLocalLogonSid                             , 80,\n    :WinConsoleLogonSid                           , 81,\n    :WinThisOrganizationCertificateSid            , 82,\n    :WinApplicationPackageAuthoritySid            , 83,\n    :WinBuiltinAnyPackageSid                      , 84,\n    :WinCapabilityInternetClientSid               , 85,\n    :WinCapabilityInternetClientServerSid         , 86,\n    :WinCapabilityPrivateNetworkClientServerSid   , 87,\n    :WinCapabilityPicturesLibrarySid              , 88,\n    :WinCapabilityVideosLibrarySid                , 89,\n    :WinCapabilityMusicLibrarySid                 , 90,\n    :WinCapabilityDocumentsLibrarySid             , 91,\n    :WinCapabilitySharedUserCertificatesSid       , 92,\n    :WinCapabilityEnterpriseAuthenticationSid     , 93,\n    :WinCapabilityRemovableStorageSid             , 94\n  )\n  # rubocop:enable Layout/SpaceBeforeComma\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446585(v=vs.85).aspx\n  # BOOL WINAPI CreateWellKnownSid(\n  #   _In_       WELL_KNOWN_SID_TYPE WellKnownSidType,\n  #   _In_opt_   PSID DomainSid,\n  #   _Out_opt_  PSID pSid,\n  #   _Inout_    DWORD *cbSid\n  # );\n  ffi_lib :advapi32\n  attach_function_private :CreateWellKnownSid,\n                          [WELL_KNOWN_SID_TYPE, :pointer, :pointer, :lpdword], :win32_bool\n\n  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379151(v=vs.85).aspx\n  # BOOL WINAPI IsValidSid(\n  #   _In_  PSID pSid\n  # );\n  ffi_lib :advapi32\n  attach_function_private :IsValidSid,\n                          [:pointer], :win32_bool\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_object_attributes\n  # typedef struct _LSA_OBJECT_ATTRIBUTES {\n  #   ULONG               Length;\n  #   HANDLE              RootDirectory;\n  #   PLSA_UNICODE_STRING ObjectName;\n  #   ULONG               Attributes;\n  #   PVOID               SecurityDescriptor;\n  #   PVOID               SecurityQualityOfService;\n  # } LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES;\n  class LSA_OBJECT_ATTRIBUTES < FFI::Struct\n    layout :Length, :ulong,\n           :RootDirectory, :handle,\n           :ObjectName, :plsa_unicode_string,\n           :Attributes, :ulong,\n           :SecurityDescriptor, :pvoid,\n           :SecurityQualityOfService, :pvoid\n  end\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_unicode_string\n  # typedef struct _LSA_UNICODE_STRING {\n  #   USHORT Length;\n  #   USHORT MaximumLength;\n  #   PWSTR  Buffer;\n  # } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING;\n  class LSA_UNICODE_STRING < FFI::Struct\n    layout :Length, :ushort,\n           :MaximumLength, :ushort,\n           :Buffer, :pwstr\n  end\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaenumerateaccountrights\n  # https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-rights-assignment\n  # NTSTATUS LsaEnumerateAccountRights(\n  #   LSA_HANDLE          PolicyHandle,\n  #   PSID                AccountSid,\n  #   PLSA_UNICODE_STRING *UserRights,\n  #   PULONG              CountOfRights\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaEnumerateAccountRights,\n                          [:lsa_handle, :psid, :plsa_unicode_string, :pulong], :ntstatus\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaaddaccountrights\n  # NTSTATUS LsaAddAccountRights(\n  #   LSA_HANDLE          PolicyHandle,\n  #   PSID                AccountSid,\n  #   PLSA_UNICODE_STRING UserRights,\n  #   ULONG               CountOfRights\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaAddAccountRights,\n                          [:lsa_handle, :psid, :plsa_unicode_string, :ulong], :ntstatus\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaremoveaccountrights\n  # NTSTATUS LsaRemoveAccountRights(\n  #   LSA_HANDLE          PolicyHandle,\n  #   PSID                AccountSid,\n  #   BOOLEAN             AllRights,\n  #   PLSA_UNICODE_STRING UserRights,\n  #   ULONG               CountOfRights\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaRemoveAccountRights,\n                          [:lsa_handle, :psid, :bool, :plsa_unicode_string, :ulong], :ntstatus\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaopenpolicy\n  # NTSTATUS LsaOpenPolicy(\n  #   PLSA_UNICODE_STRING    SystemName,\n  #   PLSA_OBJECT_ATTRIBUTES ObjectAttributes,\n  #   ACCESS_MASK            DesiredAccess,\n  #   PLSA_HANDLE            PolicyHandle\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaOpenPolicy,\n                          [:plsa_unicode_string, :plsa_object_attributes, :access_mask, :plsa_handle], :ntstatus\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaclose\n  # NTSTATUS LsaClose(\n  #   LSA_HANDLE ObjectHandle\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaClose,\n                          [:lsa_handle], :ntstatus\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsafreememory\n  # NTSTATUS LsaFreeMemory(\n  #   PVOID Buffer\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaFreeMemory,\n                          [:pvoid], :ntstatus\n\n  # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsantstatustowinerror\n  # ULONG LsaNtStatusToWinError(\n  #   NTSTATUS Status\n  # );\n  ffi_lib :advapi32\n  attach_function_private :LsaNtStatusToWinError,\n                          [:ntstatus], :ulong\nend\n"
  },
  {
    "path": "lib/puppet/util/windows.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/util/platform'\n\nmodule Puppet::Util::Windows\n  module ADSI\n    class ADSIObject; end\n    class User < ADSIObject; end\n    class UserProfile; end\n    class Group < ADSIObject; end\n  end\n\n  module File; end\n\n  module Registry\n  end\n\n  module Service\n    DEFAULT_TIMEOUT = 30\n  end\n\n  module SID\n    class Principal; end\n  end\n\n  class EventLog; end\n\n  if Puppet::Util::Platform.windows?\n    # Note: Setting codepage here globally ensures all strings returned via\n    # WIN32OLE (Ruby's late-bound COM support) are encoded in Encoding::UTF_8\n    #\n    # Also, this does not modify the value of WIN32OLE.locale - which defaults\n    # to 2048 (at least on US English Windows) and is not listed in the MS\n    # locales table, here: https://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10).aspx\n    require 'win32ole'; WIN32OLE.codepage = WIN32OLE::CP_UTF8\n\n    # these reference platform specific gems\n    require_relative '../../puppet/ffi/windows'\n    require_relative 'windows/string'\n    require_relative 'windows/error'\n    require_relative 'windows/com'\n    require_relative 'windows/sid'\n    require_relative 'windows/principal'\n    require_relative 'windows/file'\n    require_relative 'windows/security'\n    require_relative 'windows/user'\n    require_relative 'windows/process'\n    require_relative 'windows/root_certs'\n    require_relative 'windows/access_control_entry'\n    require_relative 'windows/access_control_list'\n    require_relative 'windows/security_descriptor'\n    require_relative 'windows/adsi'\n    require_relative 'windows/registry'\n    require_relative 'windows/eventlog'\n    require_relative 'windows/service'\n    require_relative 'windows/monkey_patches/process'\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util/yaml.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\n\nmodule Puppet::Util::Yaml\n  YamlLoadExceptions = [::StandardError, ::Psych::Exception]\n\n  class YamlLoadError < Puppet::Error; end\n\n  # Safely load the content as YAML. By default only the following\n  # classes can be deserialized:\n  #\n  # * TrueClass\n  # * FalseClass\n  # * NilClass\n  # * Numeric\n  # * String\n  # * Array\n  # * Hash\n  #\n  # Attempting to deserialize other classes will raise an YamlLoadError\n  # exception unless they are specified in the array of *allowed_classes*.\n  # @param [String] yaml The yaml content to parse.\n  # @param [Array] allowed_classes Additional list of classes that can be deserialized.\n  # @param [String] filename The filename to load from, used if an exception is raised.\n  # @raise [YamlLoadException] If deserialization fails.\n  # @return The parsed YAML, which can be Hash, Array or scalar types.\n  def self.safe_load(yaml, allowed_classes = [], filename = nil)\n    if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')\n      data = YAML.safe_load(yaml, permitted_classes: allowed_classes, aliases: true, filename: filename)\n    else\n      data = YAML.safe_load(yaml, allowed_classes, [], true, filename)\n    end\n    data = false if data.nil?\n    data\n  rescue ::Psych::DisallowedClass => detail\n    path = filename ? \"(#{filename})\" : \"(<unknown>)\"\n    raise YamlLoadError.new(\"#{path}: #{detail.message}\", detail)\n  rescue *YamlLoadExceptions => detail\n    raise YamlLoadError.new(detail.message, detail)\n  end\n\n  # Safely load the content from a file as YAML.\n  #\n  # @see Puppet::Util::Yaml.safe_load\n  def self.safe_load_file(filename, allowed_classes = [])\n    yaml = Puppet::FileSystem.read(filename, :encoding => 'bom|utf-8')\n    safe_load(yaml, allowed_classes, filename)\n  end\n\n  # Safely load the content from a file as YAML if\n  # contents are in valid format. This method does not\n  # raise error but returns `nil` when invalid file is\n  # given.\n  def self.safe_load_file_if_valid(filename, allowed_classes = [])\n    safe_load_file(filename, allowed_classes)\n  rescue YamlLoadError, ArgumentError, Errno::ENOENT => detail\n    Puppet.debug(\"Could not retrieve YAML content from '#{filename}': #{detail.message}\")\n    nil\n  end\n\n  def self.dump(structure, filename)\n    Puppet::FileSystem.replace_file(filename, 0o660) do |fh|\n      YAML.dump(structure, fh)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/util.rb",
    "content": "# frozen_string_literal: true\n\n# A module to collect utility functions.\n\nrequire 'English'\nrequire_relative '../puppet/error'\nrequire_relative 'util/execution_stub'\nrequire 'uri'\nrequire 'pathname'\nrequire 'ostruct'\nrequire_relative 'util/platform'\nrequire_relative 'util/windows'\nrequire_relative 'util/symbolic_file_mode'\nrequire_relative '../puppet/file_system/uniquefile'\nrequire 'securerandom'\nrequire_relative 'util/character_encoding'\n\nmodule Puppet\nmodule Util\n  require_relative 'util/monkey_patches'\n  require 'benchmark'\n\n  # These are all for backward compatibility -- these are methods that used\n  # to be in Puppet::Util but have been moved into external modules.\n  require_relative 'util/posix'\n  extend Puppet::Util::POSIX\n\n  require_relative 'util/windows/process' if Puppet::Util::Platform.windows?\n\n  extend Puppet::Util::SymbolicFileMode\n\n  def default_env\n    Puppet.features.microsoft_windows? ?\n      :windows :\n      :posix\n  end\n  module_function :default_env\n\n  def create_erb(content)\n    ERB.new(content, trim_mode: '-')\n  end\n  module_function :create_erb\n\n  # @deprecated Use ENV instead\n  # @api private\n  def get_env(name, mode = default_env)\n    ENV.fetch(name, nil)\n  end\n  module_function :get_env\n\n  # @deprecated Use ENV instead\n  # @api private\n  def get_environment(mode = default_env)\n    ENV.to_hash\n  end\n  module_function :get_environment\n\n  # @deprecated Use ENV instead\n  # @api private\n  def clear_environment(mode = default_env)\n    ENV.clear\n  end\n  module_function :clear_environment\n\n  # @deprecated Use ENV instead\n  # @api private\n  def set_env(name, value = nil, mode = default_env)\n    ENV[name] = value\n  end\n  module_function :set_env\n\n  # @deprecated Use ENV instead\n  # @api private\n  def merge_environment(env_hash, mode = default_env)\n    ENV.merge!(hash.transform_keys(&:to_s))\n  end\n  module_function :merge_environment\n\n  # Run some code with a specific environment.  Resets the environment back to\n  # what it was at the end of the code.\n  #\n  # @param hash [Hash{String,Symbol => String}] Environment variables to override the current environment.\n  # @param mode [Symbol] ignored\n  def withenv(hash, mode = :posix)\n    saved = ENV.to_hash\n    begin\n      ENV.merge!(hash.transform_keys(&:to_s))\n      yield\n    ensure\n      ENV.replace(saved)\n    end\n  end\n  module_function :withenv\n\n  # Execute a given chunk of code with a new umask.\n  def self.withumask(mask)\n    cur = File.umask(mask)\n\n    begin\n      yield\n    ensure\n      File.umask(cur)\n    end\n  end\n\n  # Change the process to a different user\n  def self.chuser\n    group = Puppet[:group]\n    if group\n      begin\n        Puppet::Util::SUIDManager.change_group(group, true)\n      rescue => detail\n        Puppet.warning _(\"could not change to group %{group}: %{detail}\") % { group: group.inspect, detail: detail }\n        $stderr.puts _(\"could not change to group %{group}\") % { group: group.inspect }\n\n        # Don't exit on failed group changes, since it's\n        # not fatal\n        # exit(74)\n      end\n    end\n\n    user = Puppet[:user]\n    if user\n      begin\n        Puppet::Util::SUIDManager.change_user(user, true)\n      rescue => detail\n        $stderr.puts _(\"Could not change to user %{user}: %{detail}\") % { user: user, detail: detail }\n        exit(74)\n      end\n    end\n  end\n\n  # Create instance methods for each of the log levels.  This allows\n  # the messages to be a little richer.  Most classes will be calling this\n  # method.\n  def self.logmethods(klass, useself = true)\n    Puppet::Util::Log.eachlevel { |level|\n      klass.send(:define_method, level, proc { |args|\n        args = args.join(\" \") if args.is_a?(Array)\n        if useself\n\n          Puppet::Util::Log.create(\n            :level => level,\n            :source => self,\n            :message => args\n          )\n        else\n\n          Puppet::Util::Log.create(\n            :level => level,\n            :message => args\n          )\n        end\n      })\n    }\n  end\n\n  # execute a block of work and based on the logging level provided, log the provided message with the seconds taken\n  # The message 'msg' should include string ' in %{seconds} seconds' as part of the message and any content should escape\n  # any percent signs '%' so that they are not interpreted as formatting commands\n  #     escaped_str = str.gsub(/%/, '%%')\n  #\n  # @param msg [String] the message to be formated to assigned the %{seconds} seconds take to execute,\n  #                     other percent signs '%' need to be escaped\n  # @param level [Symbol] the logging level for this message\n  # @param object [Object] The object use for logging the message\n  def benchmark(*args)\n    msg = args.pop\n    level = args.pop\n    object = if args.empty?\n               if respond_to?(level)\n                 self\n               else\n                 Puppet\n               end\n             else\n               args.pop\n             end\n\n    # TRANSLATORS 'benchmark' is a method name and should not be translated\n    raise Puppet::DevError, _(\"Failed to provide level to benchmark\") unless level\n\n    unless level == :none or object.respond_to? level\n      raise Puppet::DevError, _(\"Benchmarked object does not respond to %{value}\") % { value: level }\n    end\n\n    # Only benchmark if our log level is high enough\n    if level != :none and Puppet::Util::Log.sendlevel?(level)\n      seconds = Benchmark.realtime {\n        yield\n      }\n      object.send(level, msg % { seconds: \"%0.2f\" % seconds })\n      seconds\n    else\n      yield\n    end\n  end\n  module_function :benchmark\n\n  # Resolve a path for an executable to the absolute path. This tries to behave\n  # in the same manner as the unix `which` command and uses the `PATH`\n  # environment variable.\n  #\n  # @api public\n  # @param bin [String] the name of the executable to find.\n  # @return [String] the absolute path to the found executable.\n  def which(bin)\n    if absolute_path?(bin)\n      return bin if FileTest.file? bin and FileTest.executable? bin\n    else\n      exts = ENV.fetch('PATHEXT', nil)\n      exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]\n      ENV.fetch('PATH').split(File::PATH_SEPARATOR).each do |dir|\n        dest = File.expand_path(File.join(dir, bin))\n      rescue ArgumentError => e\n        # if the user's PATH contains a literal tilde (~) character and HOME is not set, we may get\n        # an ArgumentError here.  Let's check to see if that is the case; if not, re-raise whatever error\n        # was thrown.\n        if e.to_s =~ /HOME/ and (ENV['HOME'].nil? || ENV.fetch('HOME', nil) == \"\")\n          # if we get here they have a tilde in their PATH.  We'll issue a single warning about this and then\n          # ignore this path element and carry on with our lives.\n          # TRANSLATORS PATH and HOME are environment variables and should not be translated\n          Puppet::Util::Warnings.warnonce(_(\"PATH contains a ~ character, and HOME is not set; ignoring PATH element '%{dir}'.\") % { dir: dir })\n        elsif e.to_s =~ /doesn't exist|can't find user/\n          # ...otherwise, we just skip the non-existent entry, and do nothing.\n          # TRANSLATORS PATH is an environment variable and should not be translated\n          Puppet::Util::Warnings.warnonce(_(\"Couldn't expand PATH containing a ~ character; ignoring PATH element '%{dir}'.\") % { dir: dir })\n        else\n          raise\n        end\n      else\n        if Puppet::Util::Platform.windows? && File.extname(dest).empty?\n          exts.each do |ext|\n            destext = File.expand_path(dest + ext)\n            return destext if FileTest.file? destext and FileTest.executable? destext\n          end\n        end\n        return dest if FileTest.file? dest and FileTest.executable? dest\n      end\n    end\n    nil\n  end\n  module_function :which\n\n  # Determine in a platform-specific way whether a path is absolute. This\n  # defaults to the local platform if none is specified.\n  #\n  # Escape once for the string literal, and once for the regex.\n  slash = '[\\\\\\\\/]'\n  label = '[^\\\\\\\\/]+'\n  AbsolutePathWindows = /^(?:(?:[A-Z]:#{slash})|(?:#{slash}#{slash}#{label}#{slash}#{label})|(?:#{slash}#{slash}\\?#{slash}#{label}))/io\n  AbsolutePathPosix   = %r{^/}\n  def absolute_path?(path, platform = nil)\n    unless path.is_a?(String)\n      Puppet.warning(\"Cannot check if #{path} is an absolute path because it is a '#{path.class}' and not a String'\")\n      return false\n    end\n\n    # Ruby only sets File::ALT_SEPARATOR on Windows and the Ruby standard\n    # library uses that to test what platform it's on.  Normally in Puppet we\n    # would use Puppet.features.microsoft_windows?, but this method needs to\n    # be called during the initialization of features so it can't depend on\n    # that.\n    #\n    # @deprecated Use ruby's built-in methods to determine if a path is absolute.\n    platform ||= Puppet::Util::Platform.windows? ? :windows : :posix\n    regex = case platform\n            when :windows\n              AbsolutePathWindows\n            when :posix\n              AbsolutePathPosix\n            else\n              raise Puppet::DevError, _(\"unknown platform %{platform} in absolute_path\") % { platform: platform }\n            end\n\n    !!(path =~ regex)\n  end\n  module_function :absolute_path?\n\n  # Convert a path to a file URI\n  def path_to_uri(path)\n    return unless path\n\n    params = { :scheme => 'file' }\n\n    if Puppet::Util::Platform.windows?\n      path = path.tr('\\\\', '/')\n\n      unc = %r{^//([^/]+)(/.+)}.match(path)\n      if unc\n        params[:host] = unc[1]\n        path = unc[2]\n      elsif path =~ %r{^[a-z]:/}i\n        path = '/' + path\n      end\n    end\n\n    # have to split *after* any relevant escaping\n    params[:path], params[:query] = uri_encode(path).split('?')\n    search_for_fragment = params[:query] ? :query : :path\n    if params[search_for_fragment].include?('#')\n      params[search_for_fragment], _, params[:fragment] = params[search_for_fragment].rpartition('#')\n    end\n\n    begin\n      URI::Generic.build(params)\n    rescue => detail\n      raise Puppet::Error, _(\"Failed to convert '%{path}' to URI: %{detail}\") % { path: path, detail: detail }, detail.backtrace\n    end\n  end\n  module_function :path_to_uri\n\n  # Get the path component of a URI\n  def uri_to_path(uri)\n    return unless uri.is_a?(URI)\n\n    # CGI.unescape doesn't handle space rules properly in uri paths\n    # URI.unescape does, but returns strings in their original encoding\n    path = uri_unescape(uri.path.encode(Encoding::UTF_8))\n\n    if Puppet::Util::Platform.windows? && uri.scheme == 'file'\n      if uri.host && !uri.host.empty?\n        path = \"//#{uri.host}\" + path # UNC\n      else\n        path.sub!(%r{^/}, '')\n      end\n    end\n\n    path\n  end\n  module_function :uri_to_path\n\n  RFC_3986_URI_REGEX = %r{^(?<scheme>(?:[^:/?#]+):)?(?<authority>//(?:[^/?#]*))?(?<path>[^?#]*)(?:\\?(?<query>[^#]*))?(?:#(?<fragment>.*))?$}\n\n  # Percent-encodes a URI query parameter per RFC3986 - https://tools.ietf.org/html/rfc3986\n  #\n  # The output will correctly round-trip through URI.unescape\n  #\n  # @param [String query_string] A URI query parameter that may contain reserved\n  #   characters that must be percent encoded for the key or value to be\n  #   properly decoded as part of a larger query string:\n  #\n  #   query\n  #   encodes as : query\n  #\n  #   query_with_special=chars like&and * and# plus+this\n  #   encodes as:\n  #   query_with_special%3Dchars%20like%26and%20%2A%20and%23%20plus%2Bthis\n  #\n  #   Note: Also usable by fragments, but not suitable for paths\n  #\n  # @return [String] a new string containing an encoded query string per the\n  #   rules of RFC3986.\n  #\n  #   In particular,\n  #   query will encode + as %2B and space as %20\n  def uri_query_encode(query_string)\n    return nil if query_string.nil?\n\n    # query can encode space to %20 OR +\n    # + MUST be encoded as %2B\n    # in RFC3968 both query and fragment are defined as:\n    # = *( pchar / \"/\" / \"?\" )\n    # CGI.escape turns space into + which is the most backward compatible\n    # however it doesn't roundtrip through URI.unescape which prefers %20\n    CGI.escape(query_string).gsub('+', '%20')\n  end\n  module_function :uri_query_encode\n\n  # Percent-encodes a URI string per RFC3986 - https://tools.ietf.org/html/rfc3986\n  #\n  # Properly handles escaping rules for paths, query strings and fragments\n  # independently\n  #\n  # The output is safe to pass to URI.parse or URI::Generic.build and will\n  # correctly round-trip through URI.unescape\n  #\n  # @param [String path] A URI string that may be in the form of:\n  #\n  #   http://foo.com/bar?query\n  #   file://tmp/foo bar\n  #   //foo.com/bar?query\n  #   /bar?query\n  #   bar?query\n  #   bar\n  #   .\n  #   C:\\Windows\\Temp\n  #\n  #   Note that with no specified scheme, authority or query parameter delimiter\n  #   ? that a naked string will be treated as a path.\n  #\n  #   Note that if query parameters need to contain data such as & or =\n  #   that this method should not be used, as there is no way to differentiate\n  #   query parameter data from query delimiters when multiple parameters\n  #   are specified\n  #\n  # @param [Hash{Symbol=>String} opts] Options to alter encoding\n  # @option opts [Array<Symbol>] :allow_fragment defaults to false. When false\n  #   will treat # as part of a path or query and not a fragment delimiter\n  #\n  # @return [String] a new string containing appropriate portions of the URI\n  #   encoded per the rules of RFC3986.\n  #   In particular,\n  #   path will not encode +, but will encode space as %20\n  #   query will encode + as %2B and space as %20\n  #   fragment behaves like query\n  def uri_encode(path, opts = { :allow_fragment => false })\n    raise ArgumentError, _('path may not be nil') if path.nil?\n\n    encoded = ''.dup\n\n    # parse uri into named matches, then reassemble properly encoded\n    parts = path.match(RFC_3986_URI_REGEX)\n\n    encoded += parts[:scheme] unless parts[:scheme].nil?\n    encoded += parts[:authority] unless parts[:authority].nil?\n\n    # path requires space to be encoded as %20 (NEVER +)\n    # + should be left unencoded\n    # URI::parse and URI::Generic.build don't like paths encoded with CGI.escape\n    # URI.escape does not change / to %2F and : to %3A like CGI.escape\n    #\n    encoded += rfc2396_escape(parts[:path]) unless parts[:path].nil?\n\n    # each query parameter\n    unless parts[:query].nil?\n      query_string = parts[:query].split('&').map do |pair|\n        # can optionally be separated by an =\n        pair.split('=').map do |v|\n          uri_query_encode(v)\n        end.join('=')\n      end.join('&')\n      encoded += '?' + query_string\n    end\n\n    encoded += ((opts[:allow_fragment] ? '#' : '%23') + uri_query_encode(parts[:fragment])) unless parts[:fragment].nil?\n\n    encoded\n  end\n  module_function :uri_encode\n\n  # From https://github.com/ruby/ruby/blob/v2_7_3/lib/uri/rfc2396_parser.rb#L24-L46\n  ALPHA = \"a-zA-Z\"\n  ALNUM = \"#{ALPHA}\\\\d\"\n  UNRESERVED = \"\\\\-_.!~*'()#{ALNUM}\"\n  RESERVED = \";/?:@&=+$,\\\\[\\\\]\"\n  UNSAFE = Regexp.new(\"[^#{UNRESERVED}#{RESERVED}]\").freeze\n\n  HEX = \"a-fA-F\\\\d\"\n  ESCAPED = Regexp.new(\"%[#{HEX}]{2}\").freeze\n\n  def rfc2396_escape(str)\n    str.gsub(UNSAFE) do |match|\n      tmp = ''.dup\n      match.each_byte do |uc|\n        tmp << sprintf('%%%02X', uc)\n      end\n      tmp\n    end.force_encoding(Encoding::US_ASCII)\n  end\n  module_function :rfc2396_escape\n\n  def uri_unescape(str)\n    enc = str.encoding\n    enc = Encoding::UTF_8 if enc == Encoding::US_ASCII\n    str.gsub(ESCAPED) { [::Regexp.last_match(0)[1, 2]].pack('H2').force_encoding(enc) }\n  end\n  module_function :uri_unescape\n\n  def safe_posix_fork(stdin = $stdin, stdout = $stdout, stderr = $stderr, &block)\n    Kernel.fork do\n      STDIN.reopen(stdin)\n      STDOUT.reopen(stdout)\n      STDERR.reopen(stderr)\n\n      $stdin = STDIN\n      $stdout = STDOUT\n      $stderr = STDERR\n\n      begin\n        Dir.foreach('/proc/self/fd') do |f|\n          if f != '.' && f != '..' && f.to_i >= 3\n            begin\n              IO.new(f.to_i).close\n            rescue\n              nil\n            end\n          end\n        end\n      rescue Errno::ENOENT, Errno::ENOTDIR # /proc/self/fd not found, /proc/self not a dir\n        3.upto(256) { |fd|\n          begin\n            IO.new(fd).close\n          rescue\n            nil\n          end\n        }\n      end\n\n      block.call if block\n    end\n  end\n  module_function :safe_posix_fork\n\n  def symbolizehash(hash)\n    newhash = {}\n    hash.each do |name, val|\n      name = name.intern if name.respond_to? :intern\n      newhash[name] = val\n    end\n    newhash\n  end\n  module_function :symbolizehash\n\n  # Just benchmark, with no logging.\n  def thinmark\n    Benchmark.realtime {\n      yield\n    }\n  end\n\n  module_function :thinmark\n\n  PUPPET_STACK_INSERTION_FRAME = /.*puppet_stack\\.rb.*in.*`stack'/\n\n  # utility method to get the current call stack and format it to a human-readable string (which some IDEs/editors\n  # will recognize as links to the line numbers in the trace)\n  def self.pretty_backtrace(backtrace = caller(1), puppetstack = [])\n    format_backtrace_array(backtrace, puppetstack).join(\"\\n\")\n  end\n\n  # arguments may be a Ruby stack, with an optional Puppet stack argument,\n  # or just a Puppet stack.\n  # stacks may be an Array of Strings \"/foo.rb:0 in `blah'\" or\n  # an Array of Arrays that represent a frame: [\"/foo.pp\", 0]\n  def self.format_backtrace_array(primary_stack, puppetstack = [])\n    primary_stack.flat_map do |frame|\n      frame = format_puppetstack_frame(frame) if frame.is_a?(Array)\n      primary_frame = resolve_stackframe(frame)\n\n      if primary_frame =~ PUPPET_STACK_INSERTION_FRAME && !puppetstack.empty?\n        [resolve_stackframe(format_puppetstack_frame(puppetstack.shift)),\n         primary_frame]\n      else\n        primary_frame\n      end\n    end\n  end\n\n  def self.resolve_stackframe(frame)\n    _, path, rest = /^(.*):(\\d+.*)$/.match(frame).to_a\n    if path\n      path = begin\n        Pathname(path).realpath\n      rescue\n        path\n      end\n      \"#{path}:#{rest}\"\n    else\n      frame\n    end\n  end\n\n  def self.format_puppetstack_frame(file_and_lineno)\n    file_and_lineno.join(':')\n  end\n\n  # Replace a file, securely.  This takes a block, and passes it the file\n  # handle of a file open for writing.  Write the replacement content inside\n  # the block and it will safely replace the target file.\n  #\n  # This method will make no changes to the target file until the content is\n  # successfully written and the block returns without raising an error.\n  #\n  # As far as possible the state of the existing file, such as mode, is\n  # preserved.  This works hard to avoid loss of any metadata, but will result\n  # in an inode change for the file.\n  #\n  # Arguments: `filename`, `default_mode`, `staging_location`\n  #\n  # The filename is the file we are going to replace.\n  #\n  # The default_mode is the mode to use when the target file doesn't already\n  # exist; if the file is present we copy the existing mode/owner/group values\n  # across. The default_mode can be expressed as an octal integer, a numeric string (ie '0664')\n  # or a symbolic file mode.\n  #\n  # The staging_location is a location to render the temporary file before\n  # moving the file to it's final location.\n\n  DEFAULT_POSIX_MODE = 0o644\n  DEFAULT_WINDOWS_MODE = nil\n\n  def replace_file(file, default_mode, staging_location: nil, validate_callback: nil, &block)\n    raise Puppet::DevError, _(\"replace_file requires a block\") unless block_given?\n\n    if default_mode\n      unless valid_symbolic_mode?(default_mode)\n        raise Puppet::DevError, _(\"replace_file default_mode: %{default_mode} is invalid\") % { default_mode: default_mode }\n      end\n\n      mode = symbolic_mode_to_int(normalize_symbolic_mode(default_mode))\n    elsif Puppet::Util::Platform.windows?\n      mode = DEFAULT_WINDOWS_MODE\n    else\n      mode = DEFAULT_POSIX_MODE\n    end\n\n    begin\n      file = Puppet::FileSystem.pathname(file)\n\n      # encoding for Uniquefile is not important here because the caller writes to it as it sees fit\n      if staging_location\n        tempfile = Puppet::FileSystem::Uniquefile.new(Puppet::FileSystem.basename_string(file), staging_location)\n      else\n        tempfile = Puppet::FileSystem::Uniquefile.new(Puppet::FileSystem.basename_string(file), Puppet::FileSystem.dir_string(file))\n      end\n\n      effective_mode =\n        unless Puppet::Util::Platform.windows?\n          # Grab the current file mode, and fall back to the defaults.\n\n          if Puppet::FileSystem.exist?(file)\n            stat = Puppet::FileSystem.lstat(file)\n            tempfile.chown(stat.uid, stat.gid)\n            stat.mode\n          else\n            mode\n          end\n        end\n\n      # OK, now allow the caller to write the content of the file.\n      yield tempfile\n\n      if effective_mode\n        # We only care about the bottom four slots, which make the real mode,\n        # and not the rest of the platform stat call fluff and stuff.\n        tempfile.chmod(effective_mode & 0o7777)\n      end\n\n      # Now, make sure the data (which includes the mode) is safe on disk.\n      tempfile.flush\n      begin\n        tempfile.fsync\n      rescue NotImplementedError\n        # fsync may not be implemented by Ruby on all platforms, but\n        # there is absolutely no recovery path if we detect that.  So, we just\n        # ignore the return code.\n        #\n        # However, don't be fooled: that is accepting that we are running in\n        # an unsafe fashion.  If you are porting to a new platform don't stub\n        # that out.\n      end\n\n      tempfile.close\n\n      if validate_callback\n        validate_callback.call(tempfile.path)\n      end\n\n      if Puppet::Util::Platform.windows?\n        # Windows ReplaceFile needs a file to exist, so touch handles this\n        unless Puppet::FileSystem.exist?(file)\n          Puppet::FileSystem.touch(file)\n          if mode\n            Puppet::Util::Windows::Security.set_mode(mode, Puppet::FileSystem.path_string(file))\n          end\n        end\n        # Yes, the arguments are reversed compared to the rename in the rest\n        # of the world.\n        Puppet::Util::Windows::File.replace_file(FileSystem.path_string(file), tempfile.path)\n\n      else\n        # MRI Ruby checks for this and raises an error, while JRuby removes the directory\n        # and replaces it with a file. This makes the our version of replace_file() consistent\n        if Puppet::FileSystem.exist?(file) && Puppet::FileSystem.directory?(file)\n          raise Errno::EISDIR, _(\"Is a directory: %{directory}\") % { directory: file }\n        end\n\n        File.rename(tempfile.path, Puppet::FileSystem.path_string(file))\n      end\n    ensure\n      # in case an error occurred before we renamed the temp file, make sure it\n      # gets deleted\n      if tempfile\n        tempfile.close!\n      end\n    end\n\n    # Ideally, we would now fsync the directory as well, but Ruby doesn't\n    # have support for that, and it doesn't matter /that/ much...\n\n    # Return something true, and possibly useful.\n    file\n  end\n  module_function :replace_file\n\n  # Executes a block of code, wrapped with some special exception handling.  Causes the ruby interpreter to\n  #  exit if the block throws an exception.\n  #\n  # @api public\n  # @param [String] message a message to log if the block fails\n  # @param [Integer] code the exit code that the ruby interpreter should return if the block fails\n  # @yield\n  def exit_on_fail(message, code = 1)\n    yield\n  # First, we need to check and see if we are catching a SystemExit error.  These will be raised\n  #  when we daemonize/fork, and they do not necessarily indicate a failure case.\n  rescue SystemExit => err\n    raise err\n\n  # Now we need to catch *any* other kind of exception, because we may be calling third-party\n  #  code (e.g. webrick), and we have no idea what they might throw.\n  rescue Exception => err\n    ## NOTE: when debugging spec failures, these two lines can be very useful\n    # puts err.inspect\n    # puts Puppet::Util.pretty_backtrace(err.backtrace)\n    Puppet.log_exception(err, \"#{message}: #{err}\")\n    Puppet::Util::Log.force_flushqueue()\n    exit(code)\n  end\n  module_function :exit_on_fail\n\n  def deterministic_rand(seed, max)\n    deterministic_rand_int(seed, max).to_s\n  end\n  module_function :deterministic_rand\n\n  def deterministic_rand_int(seed, max)\n    Random.new(seed).rand(max)\n  end\n  module_function :deterministic_rand_int\n\n  # Executes a block of code, wrapped around Facter.load_external(false) and\n  # Facter.load_external(true) which will cause Facter to not evaluate external facts.\n  def skip_external_facts\n    return yield unless Puppet.runtime[:facter].load_external?\n\n    begin\n      Puppet.runtime[:facter].load_external(false)\n      yield\n    ensure\n      Puppet.runtime[:facter].load_external(true)\n    end\n  end\n  module_function :skip_external_facts\nend\nend\n\nrequire_relative 'util/errors'\nrequire_relative 'util/metaid'\nrequire_relative 'util/classgen'\nrequire_relative 'util/docs'\nrequire_relative 'util/execution'\nrequire_relative 'util/logging'\nrequire_relative 'util/package'\nrequire_relative 'util/warnings'\n"
  },
  {
    "path": "lib/puppet/vendor/require_vendored.rb",
    "content": "# This adds upfront requirements on vendored code found under lib/vendor/x\n# Add one requirement per vendored package (or a comment if it is loaded on demand).\n\n# The vendored library 'rgen' is loaded on demand.\n"
  },
  {
    "path": "lib/puppet/vendor.rb",
    "content": "# frozen_string_literal: true\n\nmodule Puppet\n  # Simple module to manage vendored code.\n  #\n  # To vendor a library:\n  #\n  # * Download its whole git repo or untar into `lib/puppet/vendor/<libname>`\n  # * Create a vendor/puppetload_libraryname.rb file to add its libdir into the $:.\n  #   (Look at existing load_xxx files, they should all follow the same pattern).\n  # * Add a <libname>/PUPPET_README.md file describing what the library is for\n  #   and where it comes from.\n  # * To load the vendored lib upfront, add a `require '<vendorlib>'`line to\n  #   `vendor/require_vendored.rb`.\n  # * To load the vendored lib on demand, add a comment to `vendor/require_vendored.rb`\n  #    to make it clear it should not be loaded upfront.\n  #\n  # At runtime, the #load_vendored method should be called. It will ensure\n  # all vendored libraries are added to the global `$:` path, and\n  # will then call execute the up-front loading specified in `vendor/require_vendored.rb`.\n  #\n  # The intention is to not change vendored libraries and to eventually\n  # make adding them in optional so that distros can simply adjust their\n  # packaging to exclude this directory and the various load_xxx.rb scripts\n  # if they wish to install these gems as native packages.\n  #\n  class Vendor\n    class << self\n      # @api private\n      def vendor_dir\n        File.join([File.dirname(File.expand_path(__FILE__)), \"vendor\"])\n      end\n\n      # @api private\n      def load_entry(entry)\n        Puppet.debug(\"Loading vendored #{entry}\")\n        load \"#{vendor_dir}/#{entry}\"\n      end\n\n      # @api private\n      def require_libs\n        require_relative 'vendor/require_vendored'\n      end\n\n      # Configures the path for all vendored libraries and loads required libraries.\n      # (This is the entry point for loading vendored libraries).\n      #\n      def load_vendored\n        Dir.entries(vendor_dir).each do |entry|\n          if entry =~ /load_(\\w+?)\\.rb$/\n            load_entry entry\n          end\n        end\n\n        require_libs\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/puppet/version.rb",
    "content": "# frozen_string_literal: true\n\n# The version method and constant are isolated in puppet/version.rb so that a\n# simple `require 'puppet/version'` allows a rubygems gemspec or bundler\n# Gemfile to get the Puppet version of the gem install.\n#\n# The version can be set programmatically because we want to allow the\n# Raketasks and such to set the version based on the output of `git describe`\n\nmodule Puppet\n  PUPPETVERSION = '8.11.0'\n\n  ##\n  # version is a public API method intended to always provide a fast and\n  # lightweight way to determine the version of Puppet.\n  #\n  # The intent is that software external to Puppet be able to determine the\n  # Puppet version with no side-effects.  The expected use is:\n  #\n  #     require 'puppet/version'\n  #     version = Puppet.version\n  #\n  # This function has the following ordering precedence.  This precedence list\n  # is designed to facilitate automated packaging tasks by simply writing to\n  # the VERSION file in the same directory as this source file.\n  #\n  #  1. If a version has been explicitly assigned using the Puppet.version=\n  #     method, return that version.\n  #  2. If there is a VERSION file, read the contents, trim any\n  #     trailing whitespace, and return that version string.\n  #  3. Return the value of the Puppet::PUPPETVERSION constant hard-coded into\n  #     the source code.\n  #\n  # If there is no VERSION file, the method must return the version string of\n  # the nearest parent version that is an officially released version.  That is\n  # to say, if a branch named 3.1.x contains 25 patches on top of the most\n  # recent official release of 3.1.1, then the version method must return the\n  # string \"3.1.1\" if no \"VERSION\" file is present.\n  #\n  # By design the version identifier is _not_ intended to vary during the life\n  # a process.  There is no guarantee provided that writing to the VERSION file\n  # while a Puppet process is running will cause the version string to be\n  # updated.  On the contrary, the contents of the VERSION are cached to reduce\n  # filesystem accesses.\n  #\n  # The VERSION file is intended to be used by package maintainers who may be\n  # applying patches or otherwise changing the software version in a manner\n  # that warrants a different software version identifier.  The VERSION file is\n  # intended to be managed and owned by the release process and packaging\n  # related tasks, and as such should not reside in version control.  The\n  # PUPPETVERSION constant is intended to be version controlled in history.\n  #\n  # Ideally, this behavior will allow package maintainers to precisely specify\n  # the version of the software they're packaging as in the following example:\n  #\n  #     $ git describe --match \"3.0.*\" > lib/puppet/VERSION\n  #     $ ruby -r puppet -e 'puts Puppet.version'\n  #     3.0.1-260-g9ca4e54\n  #\n  # @api public\n  #\n  # @return [String] containing the puppet version, e.g. \"3.0.1\"\n  def self.version\n    version_file = File.join(File.dirname(__FILE__), 'VERSION')\n    return @puppet_version if @puppet_version\n\n    @puppet_version = read_version_file(version_file) || PUPPETVERSION\n  end\n\n  # @return [String] containing the puppet version to minor specificity, e.g. \"3.0\"\n  def self.minor_version\n    version.split('.')[0..1].join('.')\n  end\n\n  def self.version=(version)\n    @puppet_version = version\n  end\n\n  ##\n  # read_version_file reads the content of the \"VERSION\" file that lives in the\n  # same directory as this source code file.\n  #\n  # @api private\n  #\n  # @return [String] for example: \"1.6.14-6-gea42046\" or nil if the VERSION\n  #   file does not exist.\n  def self.read_version_file(path)\n    if File.exist?(path)\n      File.read(path).chomp\n    end\n  end\n  private_class_method :read_version_file\nend\n"
  },
  {
    "path": "lib/puppet/x509/cert_provider.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/x509'\n\n# Class for loading and saving cert related objects. By default the provider\n# loads and saves based on puppet's default settings, such as `Puppet[:localcacert]`.\n# The providers sets the permissions on files it saves, such as the private key.\n# All of the `load_*` methods take an optional `required` parameter. If an object\n# doesn't exist, then by default the provider returns `nil`. However, if the\n# `required` parameter is true, then an exception will be raised instead.\n#\n# @api private\nclass Puppet::X509::CertProvider\n  include Puppet::X509::PemStore\n\n  # Only allow printing ascii characters, excluding /\n  VALID_CERTNAME = /\\A[ -.0-~]+\\Z/\n  CERT_DELIMITERS = /-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m\n  CRL_DELIMITERS = /-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m\n\n  def initialize(capath: Puppet[:localcacert],\n                 crlpath: Puppet[:hostcrl],\n                 privatekeydir: Puppet[:privatekeydir],\n                 certdir: Puppet[:certdir],\n                 requestdir: Puppet[:requestdir],\n                 hostprivkey: Puppet.settings.set_by_config?(:hostprivkey) ? Puppet[:hostprivkey] : nil,\n                 hostcert: Puppet.settings.set_by_config?(:hostcert) ? Puppet[:hostcert] : nil)\n    @capath = capath\n    @crlpath = crlpath\n    @privatekeydir = privatekeydir\n    @certdir = certdir\n    @requestdir = requestdir\n    @hostprivkey = hostprivkey\n    @hostcert = hostcert\n  end\n\n  # Save `certs` to the configured `capath`.\n  #\n  # @param certs [Array<OpenSSL::X509::Certificate>] Array of CA certs to save\n  # @raise [Puppet::Error] if the certs cannot be saved\n  #\n  # @api private\n  def save_cacerts(certs)\n    save_pem(certs.map(&:to_pem).join, @capath, **permissions_for_setting(:localcacert))\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to save CA certificates to '%{capath}'\") % { capath: @capath }, e)\n  end\n\n  # Load CA certs from the configured `capath`.\n  #\n  # @param required [Boolean] If true, raise if they are missing\n  # @return (see #load_cacerts_from_pem)\n  # @raise (see #load_cacerts_from_pem)\n  # @raise [Puppet::Error] if the certs cannot be loaded\n  #\n  # @api private\n  def load_cacerts(required: false)\n    pem = load_pem(@capath)\n    if !pem && required\n      raise Puppet::Error, _(\"The CA certificates are missing from '%{path}'\") % { path: @capath }\n    end\n\n    pem ? load_cacerts_from_pem(pem) : nil\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to load CA certificates from '%{capath}'\") % { capath: @capath }, e)\n  end\n\n  # Load PEM encoded CA certificates.\n  #\n  # @param pem [String] PEM encoded certificate(s)\n  # @return [Array<OpenSSL::X509::Certificate>] Array of CA certs\n  # @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert\n  #\n  # @api private\n  def load_cacerts_from_pem(pem)\n    # TRANSLATORS 'PEM' is an acronym and shouldn't be translated\n    raise OpenSSL::X509::CertificateError, _(\"Failed to parse CA certificates as PEM\") if pem !~ CERT_DELIMITERS\n\n    pem.scan(CERT_DELIMITERS).map do |text|\n      OpenSSL::X509::Certificate.new(text)\n    end\n  end\n\n  # Save `crls` to the configured `crlpath`.\n  #\n  # @param crls [Array<OpenSSL::X509::CRL>] Array of CRLs to save\n  # @raise [Puppet::Error] if the CRLs cannot be saved\n  #\n  # @api private\n  def save_crls(crls)\n    save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl))\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to save CRLs to '%{crlpath}'\") % { crlpath: @crlpath }, e)\n  end\n\n  # Load CRLs from the configured `crlpath` path.\n  #\n  # @param required [Boolean] If true, raise if they are missing\n  # @return (see #load_crls_from_pem)\n  # @raise (see #load_crls_from_pem)\n  # @raise [Puppet::Error] if the CRLs cannot be loaded\n  #\n  # @api private\n  def load_crls(required: false)\n    pem = load_pem(@crlpath)\n    if !pem && required\n      raise Puppet::Error, _(\"The CRL is missing from '%{path}'\") % { path: @crlpath }\n    end\n\n    pem ? load_crls_from_pem(pem) : nil\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to load CRLs from '%{crlpath}'\") % { crlpath: @crlpath }, e)\n  end\n\n  # Load PEM encoded CRL(s).\n  #\n  # @param pem [String] PEM encoded CRL(s)\n  # @return [Array<OpenSSL::X509::CRL>] Array of CRLs\n  # @raise [OpenSSL::X509::CRLError] The `pem` text does not contain a valid CRL\n  #\n  # @api private\n  def load_crls_from_pem(pem)\n    # TRANSLATORS 'PEM' is an acronym and shouldn't be translated\n    raise OpenSSL::X509::CRLError, _(\"Failed to parse CRLs as PEM\") if pem !~ CRL_DELIMITERS\n\n    pem.scan(CRL_DELIMITERS).map do |text|\n      OpenSSL::X509::CRL.new(text)\n    end\n  end\n\n  # Return the time when the CRL was last updated.\n  #\n  # @return [Time, nil] Time when the CRL was last updated, or nil if we don't\n  #   have a CRL\n  #\n  # @api private\n  def crl_last_update\n    stat = Puppet::FileSystem.stat(@crlpath)\n    Time.at(stat.mtime)\n  rescue Errno::ENOENT\n    nil\n  end\n\n  # Set the CRL last updated time.\n  #\n  # @param time [Time] The last updated time\n  #\n  # @api private\n  def crl_last_update=(time)\n    Puppet::FileSystem.touch(@crlpath, mtime: time)\n  end\n\n  # Return the time when the CA bundle was last updated.\n  #\n  # @return [Time, nil] Time when the CA bundle was last updated, or nil if we don't\n  #   have a CA bundle\n  #\n  # @api private\n  def ca_last_update\n    stat = Puppet::FileSystem.stat(@capath)\n    Time.at(stat.mtime)\n  rescue Errno::ENOENT\n    nil\n  end\n\n  # Set the CA bundle last updated time.\n  #\n  # @param time [Time] The last updated time\n  #\n  # @api private\n  def ca_last_update=(time)\n    Puppet::FileSystem.touch(@capath, mtime: time)\n  end\n\n  # Save named private key in the configured `privatekeydir`. For\n  # historical reasons, names are case insensitive.\n  #\n  # @param name [String] The private key identity\n  # @param key [OpenSSL::PKey::RSA] private key\n  # @param password [String, nil] If non-nil, derive an encryption key\n  #   from the password, and use that to encrypt the private key. If nil,\n  #   save the private key unencrypted.\n  # @raise [Puppet::Error] if the private key cannot be saved\n  #\n  # @api private\n  def save_private_key(name, key, password: nil)\n    pem = if password\n            cipher = OpenSSL::Cipher.new('aes-128-cbc')\n            key.export(cipher, password)\n          else\n            key.to_pem\n          end\n    path = @hostprivkey || to_path(@privatekeydir, name)\n    save_pem(pem, path, **permissions_for_setting(:hostprivkey))\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to save private key for '%{name}'\") % { name: name }, e)\n  end\n\n  # Load a private key from the configured `privatekeydir`. For\n  # historical reasons, names are case-insensitive.\n  #\n  # @param name [String] The private key identity\n  # @param required [Boolean] If true, raise if it is missing\n  # @param password [String, nil] If the private key is encrypted, decrypt\n  #   it using the password. If the key is encrypted, but a password is\n  #   not specified, then the key cannot be loaded.\n  # @return (see #load_private_key_from_pem)\n  # @raise (see #load_private_key_from_pem)\n  # @raise [Puppet::Error] if the private key cannot be loaded\n  #\n  # @api private\n  def load_private_key(name, required: false, password: nil)\n    path = @hostprivkey || to_path(@privatekeydir, name)\n    pem = load_pem(path)\n    if !pem && required\n      raise Puppet::Error, _(\"The private key is missing from '%{path}'\") % { path: path }\n    end\n\n    pem ? load_private_key_from_pem(pem, password: password) : nil\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to load private key for '%{name}'\") % { name: name }, e)\n  end\n\n  # Load a PEM encoded private key.\n  #\n  # @param pem [String] PEM encoded private key\n  # @param password [String, nil] If the private key is encrypted, decrypt\n  #   it using the password. If the key is encrypted, but a password is\n  #   not specified, then the key cannot be loaded.\n  # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] The private key\n  # @raise [OpenSSL::PKey::PKeyError] The `pem` text does not contain a valid key\n  #\n  # @api private\n  def load_private_key_from_pem(pem, password: nil)\n    # set a non-nil password to ensure openssl doesn't prompt\n    password ||= ''\n\n    OpenSSL::PKey.read(pem, password)\n  end\n\n  # Load the private key password.\n  #\n  # @return [String, nil] The private key password as a binary string or nil\n  #   if there is none.\n  #\n  # @api private\n  def load_private_key_password\n    Puppet::FileSystem.read(Puppet[:passfile], :encoding => Encoding::BINARY)\n  rescue Errno::ENOENT\n    nil\n  end\n\n  # Save a named client cert to the configured `certdir`.\n  #\n  # @param name [String] The client cert identity\n  # @param cert [OpenSSL::X509::Certificate] The cert to save\n  # @raise [Puppet::Error] if the client cert cannot be saved\n  #\n  # @api private\n  def save_client_cert(name, cert)\n    path = @hostcert || to_path(@certdir, name)\n    save_pem(cert.to_pem, path, **permissions_for_setting(:hostcert))\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to save client certificate for '%{name}'\") % { name: name }, e)\n  end\n\n  # Load a named client cert from the configured `certdir`.\n  #\n  # @param name [String] The client cert identity\n  # @param required [Boolean] If true, raise it is missing\n  # @return (see #load_request_from_pem)\n  # @raise (see #load_client_cert_from_pem)\n  # @raise [Puppet::Error] if the client cert cannot be loaded\n  #\n  # @api private\n  def load_client_cert(name, required: false)\n    path = @hostcert || to_path(@certdir, name)\n    pem = load_pem(path)\n    if !pem && required\n      raise Puppet::Error, _(\"The client certificate is missing from '%{path}'\") % { path: path }\n    end\n\n    pem ? load_client_cert_from_pem(pem) : nil\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to load client certificate for '%{name}'\") % { name: name }, e)\n  end\n\n  # Load a PEM encoded certificate.\n  #\n  # @param pem [String] PEM encoded cert\n  # @return [OpenSSL::X509::Certificate] the certificate\n  # @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert\n  #\n  # @api private\n  def load_client_cert_from_pem(pem)\n    OpenSSL::X509::Certificate.new(pem)\n  end\n\n  # Create a certificate signing request (CSR).\n  #\n  # @param name [String] the request identity\n  # @param private_key [OpenSSL::PKey::RSA] private key\n  # @return [Puppet::X509::Request] The request\n  #\n  # @api private\n  def create_request(name, private_key)\n    options = {}\n\n    if Puppet[:dns_alt_names] && Puppet[:dns_alt_names] != ''\n      options[:dns_alt_names] = Puppet[:dns_alt_names]\n    end\n\n    csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])\n    if csr_attributes.load\n      options[:csr_attributes] = csr_attributes.custom_attributes\n      options[:extension_requests] = csr_attributes.extension_requests\n    end\n\n    # Adds auto-renew attribute to CSR if the agent supports auto-renewal of\n    # certificates\n    if Puppet[:hostcert_renewal_interval] && Puppet[:hostcert_renewal_interval] > 0\n      options[:csr_attributes] ||= {}\n      options[:csr_attributes].merge!({ '1.3.6.1.4.1.34380.1.3.2' => 'true' })\n    end\n\n    csr = Puppet::SSL::CertificateRequest.new(name)\n    csr.generate(private_key, options)\n  end\n\n  # Save a certificate signing request (CSR) to the configured `requestdir`.\n  #\n  # @param name [String] the request identity\n  # @param csr [OpenSSL::X509::Request] the request\n  # @raise [Puppet::Error] if the cert request cannot be saved\n  #\n  # @api private\n  def save_request(name, csr)\n    path = to_path(@requestdir, name)\n    save_pem(csr.to_pem, path, **permissions_for_setting(:hostcsr))\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to save certificate request for '%{name}'\") % { name: name }, e)\n  end\n\n  # Load a named certificate signing request (CSR) from the configured `requestdir`.\n  #\n  # @param name [String] The request identity\n  # @return (see #load_request_from_pem)\n  # @raise (see #load_request_from_pem)\n  # @raise [Puppet::Error] if the cert request cannot be saved\n  #\n  # @api private\n  def load_request(name)\n    path = to_path(@requestdir, name)\n    pem = load_pem(path)\n    pem ? load_request_from_pem(pem) : nil\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to load certificate request for '%{name}'\") % { name: name }, e)\n  end\n\n  # Delete a named certificate signing request (CSR) from the configured `requestdir`.\n  #\n  # @param name [String] The request identity\n  # @return [Boolean] true if the CSR was deleted\n  #\n  # @api private\n  def delete_request(name)\n    path = to_path(@requestdir, name)\n    delete_pem(path)\n  rescue SystemCallError => e\n    raise Puppet::Error.new(_(\"Failed to delete certificate request for '%{name}'\") % { name: name }, e)\n  end\n\n  # Load a PEM encoded certificate signing request (CSR).\n  #\n  # @param pem [String] PEM encoded request\n  # @return [OpenSSL::X509::Request] the request\n  # @raise [OpenSSL::X509::RequestError] The `pem` text does not contain a valid request\n  #\n  # @api private\n  def load_request_from_pem(pem)\n    OpenSSL::X509::Request.new(pem)\n  end\n\n  # Return the path to the cert related object (key, CSR, cert, etc).\n  #\n  # @param base [String] base directory\n  # @param name [String] the name associated with the cert related object\n  def to_path(base, name)\n    raise _(\"Certname %{name} must not contain unprintable or non-ASCII characters\") % { name: name.inspect } unless name =~ VALID_CERTNAME\n\n    File.join(base, \"#{name.downcase}.pem\")\n  end\n\n  private\n\n  def permissions_for_setting(name)\n    setting = Puppet.settings.setting(name)\n    perm = { mode: setting.mode.to_i(8) }\n    if Puppet.features.root? && !Puppet::Util::Platform.windows?\n      perm[:owner] = setting.owner\n      perm[:group] = setting.group\n    end\n    perm\n  end\nend\n"
  },
  {
    "path": "lib/puppet/x509/pem_store.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../../puppet/x509'\n\n# Methods for managing PEM encoded files. While PEM encoded strings are\n# always ASCII, the files may contain user specified comments, so they are\n# UTF-8 encoded.\n#\n# @api private\nmodule Puppet::X509::PemStore\n  # Load a pem encoded object.\n  #\n  # @param path [String] file path\n  # @return [String, nil] The PEM encoded object or nil if the\n  #  path does not exist\n  # @raise [Errno::EACCES] if permission is denied\n  # @api private\n  def load_pem(path)\n    Puppet::FileSystem.read(path, encoding: 'UTF-8')\n  rescue Errno::ENOENT\n    nil\n  end\n\n  # Save pem encoded content to a file. If the file doesn't exist, it\n  # will be created. Otherwise, the file will be overwritten. In both\n  # cases the contents will be overwritten atomically so other\n  # processes don't see a partially written file.\n  #\n  # @param pem [String] The PEM encoded object to write\n  # @param path [String] The file path to write to\n  # @raise [Errno::EACCES] if permission is denied\n  # @raise [Errno::EPERM] if the operation cannot be completed\n  # @api private\n  def save_pem(pem, path, owner: nil, group: nil, mode: 0o644)\n    Puppet::FileSystem.replace_file(path, mode) do |f|\n      f.set_encoding('UTF-8')\n      f.write(pem.encode('UTF-8'))\n    end\n\n    if !Puppet::Util::Platform.windows? && Puppet.features.root? && (owner || group)\n      FileUtils.chown(owner, group, path)\n    end\n  end\n\n  # Delete a pem encoded object, if it exists.\n  #\n  # @param path [String] The file path to delete\n  # @return [Boolean] Returns true if the file was deleted, false otherwise\n  # @raise [Errno::EACCES] if permission is denied\n  # @api private\n  def delete_pem(path)\n    Puppet::FileSystem.unlink(path)\n    true\n  rescue Errno::ENOENT\n    false\n  end\nend\n"
  },
  {
    "path": "lib/puppet/x509.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative '../puppet'\nrequire_relative '../puppet/ssl/openssl_loader'\n\n# Responsible for loading and saving certificates and private keys.\n#\n# @see Puppet::X509::CertProvider\n# @api private\nmodule Puppet::X509\n  require_relative 'x509/pem_store'\n  require_relative 'x509/cert_provider'\nend\n"
  },
  {
    "path": "lib/puppet.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'puppet/version'\nrequire_relative 'puppet/concurrent/synchronized'\n\nPuppet::OLDEST_RECOMMENDED_RUBY_VERSION = '3.1.0'\nif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new(Puppet::OLDEST_RECOMMENDED_RUBY_VERSION)\n  raise LoadError, \"Puppet #{Puppet.version} requires Ruby #{Puppet::OLDEST_RECOMMENDED_RUBY_VERSION} or greater, found Ruby #{RUBY_VERSION.dup}.\"\nend\n\n$LOAD_PATH.extend(Puppet::Concurrent::Synchronized)\n\n# see the bottom of the file for further inclusions\n# Also see the new Vendor support - towards the end\n#\nrequire_relative 'puppet/error'\nrequire_relative 'puppet/util'\nrequire_relative 'puppet/util/autoload'\nrequire_relative 'puppet/settings'\nrequire_relative 'puppet/util/feature'\nrequire_relative 'puppet/util/suidmanager'\nrequire_relative 'puppet/util/run_mode'\nrequire_relative 'puppet/gettext/config'\nrequire_relative 'puppet/defaults'\n\n# Defines the `Puppet` module. There are different entry points into Puppet\n# depending on your use case.\n#\n# To use puppet as a library, see {Puppet::Pal}.\n#\n# To create a new application, see {Puppet::Application}.\n#\n# To create a new function, see {Puppet::Functions}.\n#\n# To access puppet's REST APIs, see https://puppet.com/docs/puppet/latest/http_api/http_api_index.html.\n#\n# @api public\nmodule Puppet\n  require_relative 'puppet/file_system'\n  require_relative 'puppet/etc'\n  require_relative 'puppet/context'\n  require_relative 'puppet/environments'\n\n  class << self\n    Puppet::GettextConfig.setup_locale\n    Puppet::GettextConfig.create_default_text_domain\n\n    include Puppet::Util\n    attr_reader :features\n  end\n\n  # the hash that determines how our system behaves\n  @@settings = Puppet::Settings.new\n\n  # Note: It's important that these accessors (`self.settings`, `self.[]`) are\n  # defined before we try to load any \"features\" (which happens a few lines below),\n  # because the implementation of the features loading may examine the values of\n  # settings.\n  def self.settings\n    @@settings\n  end\n\n  # The puppetserver project has its own settings class that is thread-aware; this\n  # method is here to allow the puppetserver to define its own custom settings class\n  # for multithreaded puppet. It is not intended for use outside of the puppetserver\n  # implmentation.\n  def self.replace_settings_object(new_settings)\n    @@settings = new_settings\n  end\n\n  # Get the value for a setting\n  #\n  # @param [Symbol] param the setting to retrieve\n  #\n  # @api public\n  def self.[](param)\n    if param == :debug\n      Puppet::Util::Log.level == :debug\n    else\n      @@settings[param]\n    end\n  end\n\n  require_relative 'puppet/util/logging'\n  extend Puppet::Util::Logging\n\n  # The feature collection\n  @features = Puppet::Util::Feature.new('puppet/feature')\n\n  # Load the base features.\n  require_relative 'puppet/feature/base'\n\n  # setting access and stuff\n  def self.[]=(param, value)\n    @@settings[param] = value\n  end\n\n  def self.clear\n    @@settings.clear\n  end\n\n  def self.debug=(value)\n    if value\n      Puppet::Util::Log.level = (:debug)\n    else\n      Puppet::Util::Log.level = (:notice)\n    end\n  end\n\n  def self.run_mode\n    # This sucks (the existence of this method); there are a lot of places in our code that branch based the value of\n    # \"run mode\", but there used to be some really confusing code paths that made it almost impossible to determine\n    # when during the lifecycle of a puppet application run the value would be set properly.  A lot of the lifecycle\n    # stuff has been cleaned up now, but it still seems frightening that we rely so heavily on this value.\n    #\n    # I'd like to see about getting rid of the concept of \"run_mode\" entirely, but there are just too many places in\n    # the code that call this method at the moment... so I've settled for isolating it inside of the Settings class\n    # (rather than using a global variable, as we did previously...).  Would be good to revisit this at some point.\n    #\n    # --cprice 2012-03-16\n    Puppet::Util::RunMode[@@settings.preferred_run_mode]\n  end\n\n  # Modify the settings with defaults defined in `initialize_default_settings` method in puppet/defaults.rb. This can\n  # be used in the initialization of new Puppet::Settings objects in the puppetserver project.\n  Puppet.initialize_default_settings!(settings)\n\n  # Now that settings are loaded we have the code loaded to be able to issue\n  # deprecation warnings. Warn if we're on a deprecated ruby version.\n  # Update JRuby version constraints in PUP-11716\n  if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new(Puppet::OLDEST_RECOMMENDED_RUBY_VERSION)\n    Puppet.deprecation_warning(_(\"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.\") % { version: RUBY_VERSION })\n  end\n\n  # Initialize puppet's settings. This is intended only for use by external tools that are not\n  #  built off of the Faces API or the Puppet::Util::Application class. It may also be used\n  #  to initialize state so that a Face may be used programatically, rather than as a stand-alone\n  #  command-line tool.\n  #\n  # @api public\n  # @param args [Array<String>] the command line arguments to use for initialization\n  # @param require_config [Boolean] controls loading of Puppet configuration files\n  # @param global_settings [Boolean] controls push to global context after settings object initialization\n  # @param runtime_implementations [Hash<Symbol, Object>] runtime implementations to register\n  # @return [void]\n  def self.initialize_settings(args = [], require_config = true, push_settings_globally = true, runtime_implementations = {})\n    do_initialize_settings_for_run_mode(:user, args, require_config, push_settings_globally, runtime_implementations)\n  end\n\n  def self.vendored_modules\n    dir = Puppet[:vendormoduledir]\n    if dir && File.directory?(dir)\n      Dir.entries(dir)\n         .reject { |f| f =~ /^\\./ }\n         .map { |f| File.join(dir, f, \"lib\") }\n         .select { |d| FileTest.directory?(d) }\n    else\n      []\n    end\n  end\n  private_class_method :vendored_modules\n\n  def self.initialize_load_path\n    $LOAD_PATH.unshift(Puppet[:libdir])\n    $LOAD_PATH.concat(vendored_modules)\n  end\n  private_class_method :initialize_load_path\n\n  # private helper method to provide the implementation details of initializing for a run mode,\n  #  but allowing us to control where the deprecation warning is issued\n  def self.do_initialize_settings_for_run_mode(run_mode, args, require_config, push_settings_globally, runtime_implementations)\n    Puppet.settings.initialize_global_settings(args, require_config)\n    run_mode = Puppet::Util::RunMode[run_mode]\n    Puppet.settings.initialize_app_defaults(Puppet::Settings.app_defaults_for_run_mode(run_mode))\n    if push_settings_globally\n      initialize_load_path\n      push_context_global(Puppet.base_context(Puppet.settings), \"Initial context after settings initialization\")\n      Puppet::Parser::Functions.reset\n    end\n    runtime_implementations.each_pair do |name, impl|\n      Puppet.runtime[name] = impl\n    end\n  end\n  private_class_method :do_initialize_settings_for_run_mode\n\n  # Initialize puppet's core facts. It should not be called before initialize_settings.\n  def self.initialize_facts\n    # Add the puppetversion fact; this is done before generating the hash so it is\n    # accessible to custom facts.\n    Puppet.runtime[:facter].add(:puppetversion) do\n      setcode { Puppet.version.to_s }\n    end\n\n    Puppet.runtime[:facter].add(:agent_specified_environment) do\n      setcode do\n        Puppet.settings.set_by_cli(:environment) ||\n          Puppet.settings.set_in_section(:environment, :agent) ||\n          Puppet.settings.set_in_section(:environment, :main)\n      end\n    end\n  end\n\n  # Load vendored (setup paths, and load what is needed upfront).\n  # See the Vendor class for how to add additional vendored gems/code\n  require_relative 'puppet/vendor'\n  Puppet::Vendor.load_vendored\n\n  # The bindings used for initialization of puppet\n  #\n  # @param settings [Puppet::Settings,Hash<Symbol,String>] either a Puppet::Settings instance\n  #   or a Hash of settings key/value pairs.\n  # @api private\n  def self.base_context(settings)\n    environmentpath = settings[:environmentpath]\n    basemodulepath = Puppet::Node::Environment.split_path(settings[:basemodulepath])\n\n    if environmentpath.nil? || environmentpath.empty?\n      raise(Puppet::Error, _(\"The environmentpath setting cannot be empty or nil.\"))\n    else\n      loaders = Puppet::Environments::Directories.from_path(environmentpath, basemodulepath)\n      # in case the configured environment (used for the default sometimes)\n      # doesn't exist\n      default_environment = Puppet[:environment].to_sym\n      if default_environment == :production\n        modulepath = settings[:modulepath]\n        modulepath = (modulepath.nil? || '' == modulepath) ? basemodulepath : Puppet::Node::Environment.split_path(modulepath)\n        loaders << Puppet::Environments::StaticPrivate.new(\n          Puppet::Node::Environment.create(default_environment,\n                                           modulepath,\n                                           Puppet::Node::Environment::NO_MANIFEST)\n        )\n      end\n    end\n\n    {\n      :environments => Puppet::Environments::Cached.new(Puppet::Environments::Combined.new(*loaders)),\n      :ssl_context => proc { Puppet.runtime[:http].default_ssl_context },\n      :http_session => proc { Puppet.runtime[:http].create_session },\n      :plugins => proc { Puppet::Plugins::Configuration.load_plugins },\n      :rich_data => Puppet[:rich_data],\n      # `stringify_rich` controls whether `rich_data` is stringified into a lossy format\n      # instead of a lossless format. Catalogs should not be stringified, though to_yaml\n      # and the resource application have uses for a lossy, user friendly format.\n      :stringify_rich => false\n    }\n  end\n\n  # A simple set of bindings that is just enough to limp along to\n  # initialization where the {base_context} bindings are put in place\n  # @api private\n  def self.bootstrap_context\n    root_environment = Puppet::Node::Environment.create(:'*root*', [], Puppet::Node::Environment::NO_MANIFEST)\n    {\n      :current_environment => root_environment,\n      :root_environment => root_environment\n    }\n  end\n\n  # @param overrides [Hash] A hash of bindings to be merged with the parent context.\n  # @param description [String] A description of the context.\n  # @api private\n  def self.push_context(overrides, description = \"\")\n    @context.push(overrides, description)\n  end\n\n  # Push something onto the context and make it global across threads. This\n  # has the potential to convert threadlocal overrides earlier on the stack into\n  # global overrides.\n  # @api private\n  def self.push_context_global(overrides, description = '')\n    @context.unsafe_push_global(overrides, description)\n  end\n\n  # Return to the previous context.\n  # @raise [StackUnderflow] if the current context is the root\n  # @api private\n  def self.pop_context\n    @context.pop\n  end\n\n  # Lookup a binding by name or return a default value provided by a passed block (if given).\n  # @api private\n  def self.lookup(name, &block)\n    @context.lookup(name, &block)\n  end\n\n  # @param bindings [Hash] A hash of bindings to be merged with the parent context.\n  # @param description [String] A description of the context.\n  # @yield [] A block executed in the context of the temporarily pushed bindings.\n  # @api private\n  def self.override(bindings, description = \"\", &block)\n    @context.override(bindings, description, &block)\n  end\n\n  # @param name The name of a context key to ignore; intended for test usage.\n  # @api private\n  def self.ignore(name)\n    @context.ignore(name)\n  end\n\n  # @param name The name of a previously ignored context key to restore; intended for test usage.\n  # @api private\n  def self.restore(name)\n    @context.restore(name)\n  end\n\n  # @api private\n  def self.mark_context(name)\n    @context.mark(name)\n  end\n\n  # @api private\n  def self.rollback_context(name)\n    @context.rollback(name)\n  end\n\n  def self.runtime\n    @runtime\n  end\n\n  require_relative 'puppet/node'\n\n  # The single instance used for normal operation\n  @context = Puppet::Context.new(bootstrap_context)\n\n  require_relative 'puppet/runtime'\n  @runtime = Puppet::Runtime.instance\nend\n\n# This feels weird to me; I would really like for us to get to a state where there is never a \"require\" statement\n#  anywhere besides the very top of a file.  That would not be possible at the moment without a great deal of\n#  effort, but I think we should strive for it and revisit this at some point.  --cprice 2012-03-16\n\nrequire_relative 'puppet/indirector'\nrequire_relative 'puppet/compilable_resource_type'\nrequire_relative 'puppet/type'\nrequire_relative 'puppet/resource'\nrequire_relative 'puppet/parser'\nrequire_relative 'puppet/network'\nrequire_relative 'puppet/x509'\nrequire_relative 'puppet/ssl'\nrequire_relative 'puppet/module'\nrequire_relative 'puppet/data_binding'\nrequire_relative 'puppet/util/storage'\nrequire_relative 'puppet/file_bucket/file'\nrequire_relative 'puppet/plugins/configuration'\nrequire_relative 'puppet/pal/pal_api'\nrequire_relative 'puppet/node/facts'\n"
  },
  {
    "path": "lib/puppet_pal.rb",
    "content": "# frozen_string_literal: true\n\n# Puppet as a Library \"PAL\"\n# This is the entry point when using PAL as a standalone library.\n#\n# This requires all of puppet because 'settings' and many other things are still required in PAL.\n# Eventually that will not be the case.\n#\nrequire_relative 'puppet'\nrequire_relative 'puppet/pal/pal_api'\n"
  },
  {
    "path": "lib/puppet_x.rb",
    "content": "# frozen_string_literal: true\n\n# The Puppet Extensions Module.\n#\n# Submodules of this module should be named after the publisher (e.g.\n# 'user' part of a Puppet Module name). You should also add the module\n# name to avoid further conflicts in the same namespace. So the\n# structure should be\n# `lib/puppet_x/<namespace>/<module_name>/<extension.rb>`.\n# A Puppet Extension for 'puppetlabs-powershell' would live at\n# `lib/puppet_x/puppetlabs/powershell/<extension.rb>`.\n#\n# @api public\n#\nmodule PuppetX\nend\n"
  },
  {
    "path": "locales/config.yaml",
    "content": "---\n# This is the project-specific configuration file for setting up\n# fast_gettext for your project.\ngettext:\n  # This is used for the name of the .pot and .po files; they will be\n  # called <project_name>.pot\n  project_name: 'puppet'\n  # This is used in comments in the .pot and .po files to indicate what\n  # project the files belong to and should be a little more descriptive than\n  # <project_name>\n  package_name: Puppet automation framework\n  # The locale that the default messages in the .pot file are in\n  default_locale: en\n  # The address for sending bug reports.\n  bugs_address: https://tickets.puppetlabs.com\n  # The holder of the copyright.\n  copyright_holder: Puppet, Inc.\n  # Patterns for +Dir.glob+ used to find all files that might contain\n  # translatable content, relative to the project root directory\n  source_files:\n    - 'lib/**/*.rb'\n  # Patterns for +Dir.glob+ used to find all files contained in\n  # `source_files` that should be ignored when searching for translatable\n  # content, relative to the project root directory\n  exclude_files:\n    - 'lib/puppet/pops/types/type_formatter.rb'\n    # The semantic_puppet gem is temporarily vendored (PUP-7114), and it\n    # handles its own translation.\n    - 'lib/puppet/vendor/**/*.rb'\n"
  },
  {
    "path": "locales/en/puppet.po",
    "content": "# English translations for Puppet automation framework package.\n# Copyright (C) 2017 Puppet, Inc.\n# This file is distributed under the same license as the Puppet automation framework package.\n# Automatically generated, 2017.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Puppet automation framework 5.3.3-51-gc11ddbe\\n\"\n\"\\n\"\n\"Report-Msgid-Bugs-To: https://tickets.puppetlabs.com\\n\"\n\"POT-Creation-Date: 2017-11-17 12:09+0000\\n\"\n\"PO-Revision-Date: 2017-11-17 12:09+0000\\n\"\n\"Last-Translator: Automatically generated\\n\"\n\"Language-Team: none\\n\"\n\"Language: en\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n"
  },
  {
    "path": "locales/puppet.pot",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2024 Puppet, Inc.\n# This file is distributed under the same license as the Puppet automation framework package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Puppet automation framework 8.10.0-24-g7685918b19\\n\"\n\"\\n\"\n\"Report-Msgid-Bugs-To: https://tickets.puppetlabs.com\\n\"\n\"POT-Creation-Date: 2024-11-05 23:06+0000\\n\"\n\"PO-Revision-Date: 2024-11-05 23:06+0000\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n#. TRANSLATORS 'lookup' is a puppet function and should not be translated\n#: ../lib/hiera/puppet_function.rb:66\nmsgid \"The function '%{class_name}' is deprecated in favor of using 'lookup'.\"\nmsgstr \"\"\n\n#: ../lib/hiera/puppet_function.rb:67\nmsgid \"See https://puppet.com/docs/puppet/%{minor_version}/deprecated_language.html\"\nmsgstr \"\"\n\n#: ../lib/hiera/scope.rb:49 ../lib/puppet/parser/scope.rb:535 ../lib/puppet/parser/scope.rb:539\nmsgid \"Variable: %{name}\"\nmsgstr \"\"\n\n#: ../lib/hiera/scope.rb:50 ../lib/hiera/scope.rb:52 ../lib/puppet/parser/scope.rb:536 ../lib/puppet/parser/scope.rb:542\nmsgid \"Undefined variable '%{name}'; %{reason}\"\nmsgstr \"\"\n\n#: ../lib/hiera_puppet.rb:16\nmsgid \"Could not find data item %{key} in any Hiera data file and no default supplied\"\nmsgstr \"\"\n\n#: ../lib/hiera_puppet.rb:43\nmsgid \"Please supply a parameter to perform a Hiera lookup\"\nmsgstr \"\"\n\n#: ../lib/hiera_puppet.rb:74 ../lib/puppet/indirector/hiera.rb:87\nmsgid \"Config file %{hiera_config} not found, using Hiera defaults\"\nmsgstr \"\"\n\n#: ../lib/puppet.rb:132\nmsgid \"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.\"\nmsgstr \"\"\n\n#: ../lib/puppet.rb:218\nmsgid \"The environmentpath setting cannot be empty or nil.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:95\nmsgid \"Run of %{client_class} already in progress; skipping  (%{lockfile_path} exists)\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:98\nmsgid \"Exiting now because the maxwaitforlock timeout has been exceeded.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:101\nmsgid \"Another puppet instance is already running; --waitforlock flag used, waiting for running instance to finish.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:102 ../lib/puppet/ssl/state_machine.rb:415 ../lib/puppet/ssl/state_machine.rb:446\nmsgid \"Will try again in %{time} seconds.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:107\nmsgid \"Execution of %{client_class} did not complete within %{runtimeout} seconds and was terminated.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:111\nmsgid \"Could not run %{client_class}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:120\nmsgid \"Shutdown/restart in progress (%{status}); skipping run\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:138\nmsgid \"puppet agent: applying configuration\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:161\nmsgid \"Could not create instance of %{client_class}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent.rb:176\nmsgid \"\"\n\"Skipping run of %{client_class}; administratively disabled (Reason: '%{disable_message}');\\n\"\n\"Use 'puppet agent --enable' to re-enable.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent/disabler.rb:21\nmsgid \"Enabling Puppet.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent/disabler.rb:28\nmsgid \"Disabling Puppet.\"\nmsgstr \"\"\n\n#: ../lib/puppet/agent/locker.rb:28\nmsgid \"Failed to acquire lock\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:233\nmsgid \"Unable to find application '%{application_name}'. %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:254\nmsgid \"Unable to load application class '%{class_name}' from file 'puppet/application/%{application_name}.rb'\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:307\nmsgid \"Invalid environment mode '%{mode_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:407\nmsgid \"Could not get application-specific default settings\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:413\nmsgid \"Could not initialize\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:414\nmsgid \"Could not parse application options\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:415\nmsgid \"Could not prepare for execution\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:418\nmsgid \"`puppet %{name}` is deprecated and will be removed in a future release.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:421\nmsgid \"Could not configure routes from %{route_file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:422\nmsgid \"Could not log runtime debug info\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:423\nmsgid \"Could not run\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:432\nmsgid \"No valid command or main\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:487\nmsgid \"Could not set logdest to %{dest}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application.rb:584\nmsgid \"No help available for puppet %{app_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/agent.rb:25 ../lib/puppet/application/device.rb:25\nmsgid \"Cancelling startup\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/agent.rb:84\nmsgid \"The puppet agent daemon\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/agent.rb:425\nmsgid \"Fingerprint asked but neither the certificate, nor the certificate request have been issued\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/agent.rb:430\nmsgid \"Failed to generate fingerprint: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/agent.rb:454\nmsgid \"Starting Puppet client version %{version}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/agent.rb:470\nmsgid \"The puppet agent command does not take parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:39\nmsgid \"Apply Puppet manifests locally\"\nmsgstr \"\"\n\n#. TRANSLATORS \"puppet apply\" is a program command and should not be translated\n#. TRANSLATORS \"puppet apply\" is a program command and should not be translated\n#: ../lib/puppet/application/apply.rb:217 ../lib/puppet/application/apply.rb:344\nmsgid \"For puppet apply\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:225\nmsgid \"%{file} is not readable\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:311 ../lib/puppet/application/script.rb:241\nmsgid \"Exiting\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:354\nmsgid \"Could not deserialize catalog from %{format}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:375 ../lib/puppet/application/script.rb:148\nmsgid \"Could not find facts for %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:387 ../lib/puppet/application/script.rb:156\nmsgid \"Could not find node %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:401 ../lib/puppet/application/script.rb:140\nmsgid \"Could not find file %{manifest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/apply.rb:403\nmsgid \"Only one file can be applied per run.  Skipping %{files}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/describe.rb:177\nmsgid \"Display help about resource types\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:84\nmsgid \"Manage remote network devices\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:232\nmsgid \"resource command requires target\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:235\nmsgid \"facts command requires target\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:239\nmsgid \"missing argument: --target is required when using --apply\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:240\nmsgid \"%{file} does not exist, cannot apply\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:258\nmsgid \"Target device / certificate '%{target}' not found in %{config}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:260\nmsgid \"No device found in %{config}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:317\nmsgid \"retrieving resource: %{resource} from %{target} at %{scheme}%{url_host}%{port}%{url_path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:332\nmsgid \"retrieving facts from %{target} at %{scheme}%{url_host}%{port}%{url_path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:355\nmsgid \"starting applying configuration to %{target} at %{scheme}%{url_host}%{port}%{url_path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:390 ../lib/puppet/application/resource.rb:212\nmsgid \"You must specify the type to display\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/device.rb:391 ../lib/puppet/application/resource.rb:213\nmsgid \"Could not find type %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:30\nmsgid \"Invalid output format %{arg}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:39\nmsgid \"Invalid output mode %{arg}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:55\nmsgid \"Generate Puppet references\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:137\nmsgid \"scanning: %{files}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:149\nmsgid \"Could not generate documentation: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:162\nmsgid \"Could not find reference %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/doc.rb:168\nmsgid \"Could not generate reference %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/face_base.rb:36\nmsgid \"I don't know how to render '%{format}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/face_base.rb:58\nmsgid \"Cancelling Face\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/face_base.rb:137\nmsgid \"'%{face}' has no %{action} action.  See `puppet help %{face}`.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/face_base.rb:217\nmsgid \"%{face} does not respond to action %{arg}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/face_base.rb:250\nmsgid \"puppet %{face} %{action} takes %{arg_count} argument, but you gave %{given_count}\"\nmsgid_plural \"puppet %{face} %{action} takes %{arg_count} arguments, but you gave %{given_count}\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: ../lib/puppet/application/face_base.rb:255\nmsgid \"'puppet %{face}' is deprecated and will be removed in a future release\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/face_base.rb:273\nmsgid \"Try 'puppet help %{face} %{action}' for usage\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:19\nmsgid \"Store and retrieve files in a filebucket\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:230\nmsgid \"You must specify a file to back up\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:234\nmsgid \"%{file}: no such file\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:238\nmsgid \"%{file}: cannot read file\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:260 ../lib/puppet/application/filebucket.rb:284\nmsgid \"Need exactly two arguments: filebucket diff <file_a> <file_b>\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:281\nmsgid \"Comparing %{checksum_a} %{checksum_b} %{file_a} %{file_b}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/filebucket.rb:295\nmsgid \"Cancelling\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:10\nmsgid \"Run 'puppet lookup --help' for more details\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:104\nmsgid \"Interactive Hiera lookup\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:277\nmsgid \"\"\n\"The options %{deep_merge_opts} are only available with '--merge deep'\\n\"\n\"%{run_help}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:288\nmsgid \"\"\n\"The --merge option only accepts %{strategies}, or %{last_strategy}\\n\"\n\"%{run_help}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:313\nmsgid \"No keys were given to lookup.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:321\nmsgid \"Unknown rendering format '%{format}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:362\nmsgid \"Incorrectly formatted data in %{fact_file} given via the --facts flag (only accepts yaml and json files)\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:367\nmsgid \"When overriding any of the %{trusted_facts_list} facts with %{fact_file} given via the --facts flag, they must all be overridden.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:396\nmsgid \"CA is not available, the operation will continue without using trusted facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/lookup.rb:426\nmsgid \"No facts available for target node: %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/resource.rb:38\nmsgid \"The resource abstraction layer shell\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/resource.rb:156\nmsgid \"Editing with Yaml output is not supported\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/resource.rb:220\nmsgid \"Invalid parameter setting %{setting}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/resource.rb:255\nmsgid \"Listing all file instances is not supported.  Please specify a file or directory, e.g. puppet resource file /etc\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/script.rb:21\nmsgid \"Run a puppet manifests as a script without compiling a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/script.rb:127\nmsgid \"Bolt must be installed to use the script application\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/script.rb:142\nmsgid \"Only one file can be used per run. Skipping %{files}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:10\nmsgid \"Manage SSL keys and certificates for puppet SSL clients\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:112\nmsgid \"An action must be specified.\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:136 ../lib/puppet/application/ssl.rb:143\nmsgid \"The certificate for '%{name}' has not yet been signed\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:167\nmsgid \"Completed SSL initialization\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:171\nmsgid \"Unknown action '%{action}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:192\nmsgid \"Submitted certificate request for '%{name}' to %{url}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:195\nmsgid \"Could not submit certificate request for '%{name}' to %{url} due to a conflict on the server\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:197 ../lib/puppet/application/ssl.rb:200\nmsgid \"Failed to submit certificate request: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:212\nmsgid \"Generated certificate request in '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:214\nmsgid \"Failed to generate certificate request: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:222\nmsgid \"Downloading certificate '%{name}' from %{url}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:226\nmsgid \"Downloaded certificate '%{name}' with fingerprint %{fingerprint}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:239 ../lib/puppet/application/ssl.rb:242\nmsgid \"Failed to download certificate: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:271 ../lib/puppet/application/ssl.rb:274\nmsgid \"Failed to connect to the CA to determine if certificate %{certname} has been cleaned\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:301\nmsgid \"Removed %{label} %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:318 ../lib/puppet/ssl/state_machine.rb:274\nmsgid \"Creating a new EC SSL key for %{name} using curve %{curve}\"\nmsgstr \"\"\n\n#: ../lib/puppet/application/ssl.rb:321\nmsgid \"Creating a new SSL key for %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:23\nmsgid \"Puppet configuration client\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:46\nmsgid \"Removing corrupt state file %{file}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:51\nmsgid \"Cannot remove %{file}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:82\nmsgid \"Using cached catalog from environment '%{environment}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:88\nmsgid \"Not using cache on failed catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:97\nmsgid \"Not using cached catalog because its environment '%{catalog_env}' does not match '%{local_env}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:102 ../lib/puppet/configurer.rb:278\nmsgid \"Using cached catalog from environment '%{catalog_env}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:135\nmsgid \"The current total number of fact values: %{size} exceeds the fact values limit: %{max_size}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:139\nmsgid \"Fact %{name} with length: %{length} exceeds the fact name length limit: %{limit}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:143\nmsgid \"The current number of top level facts: %{size} exceeds the top facts limit: %{max_size}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:147\nmsgid \"Fact %{name} with value %{value} with the value length: %{length} exceeds the value length limit: %{max_length}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:151\nmsgid \"Payload with the current size of: %{payload} exceeds the payload size limit: %{max_size}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:190\nmsgid \"The size of the payload is %{payload}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:220\nmsgid \"The total number of facts registered is %{number_of_facts}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:271\nmsgid \"Could not retrieve catalog; skipping run\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:287\nmsgid \"Applied catalog in %{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:321\nmsgid \"Could not select a functional puppet server from server_list: '%{server_list}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'server_list' is the name of a setting and should not be translated\n#: ../lib/puppet/configurer.rb:335\nmsgid \"Selected puppet server from the `server_list` setting: %{server}:%{port}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:372\nmsgid \"Local environment: '%{local_env}' doesn't match the environment of the cached catalog '%{catalog_env}', switching agent to '%{catalog_env}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:395\nmsgid \"Environment not passed via CLI and no catalog was given, attempting to find out the last server-specified environment\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:399\nmsgid \"Requesting environment from the server\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:409\nmsgid \"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.\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:413\nmsgid \"Using environment '%{env}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:437\nmsgid \"Not using catalog because its environment '%{catalog_env}' does not match agent specified environment '%{local_env}' and strict_environment_mode is set\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:448\nmsgid \"Catalog environment didn't stabilize after %{tries} fetches, aborting run\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:451\nmsgid \"Local environment: '%{local_env}' doesn't match server specified environment '%{catalog_env}', restarting agent run with environment '%{catalog_env}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:508\nmsgid \"Failed to apply catalog: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:540\nmsgid \"Environment '%{environment}' not found on server, aborting run.\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:542\nmsgid \"Environment '%{environment}' not found on server, skipping initial pluginsync.\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:560 ../lib/puppet/http/resolver/server_list.rb:65 ../lib/puppet/http/resolver/server_list.rb:69\nmsgid \"Puppet server %{host}:%{port} is unavailable: %{code} %{reason}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'server_list' is the name of a setting and should not be translated\n#: ../lib/puppet/configurer.rb:564 ../lib/puppet/http/resolver/server_list.rb:74 ../lib/puppet/http/resolver/server_list.rb:77\nmsgid \"Unable to connect to server from server_list setting: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:590\nmsgid \"Successfully loaded last environment from the lastrunfile\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:594\nmsgid \"Found last server-specified environment: %{environment}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:597\nmsgid \"Could not find last server-specified environment: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:617\nmsgid \"Local environment: '%{local_env}' doesn't match server specified node environment '%{node_env}', switching agent to '%{node_env}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:625\nmsgid \"Unable to fetch my node definition, but the agent run will continue:\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:644 ../lib/puppet/face/report.rb:48\nmsgid \"Could not send report: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:653\nmsgid \"Could not save last run local report: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:672\nmsgid \"Uploading facts for %{node} to %{server}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:681\nmsgid \"Failed to submit facts: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:696\nmsgid \"Could not run command from %{setting}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:725\nmsgid \"Could not retrieve catalog from cache: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer.rb:747\nmsgid \"Could not retrieve catalog from remote server: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer/downloader.rb:11\nmsgid \"Retrieving %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer/downloader.rb:23\nmsgid \"Failed to retrieve %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer/downloader.rb:34\nmsgid \"Could not retrieve %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/configurer/fact_handler.rb:26\nmsgid \"Could not retrieve local facts: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/context.rb:79\nmsgid \"Mark for '%{name}' already exists\"\nmsgstr \"\"\n\n#: ../lib/puppet/context.rb:94\nmsgid \"Unknown mark '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/context.rb:113 ../lib/puppet/context.rb:171\nmsgid \"Unable to lookup '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/context.rb:122\nmsgid \"Attempted to pop, but already at root of the context stack.\"\nmsgstr \"\"\n\n#: ../lib/puppet/context/trusted_information.rb:55\nmsgid \"TrustedInformation expected a certificate, but none was given.\"\nmsgstr \"\"\n\n#: ../lib/puppet/context/trusted_information.rb:106 ../lib/puppet/parser/scope.rb:848\nmsgid \"Unsupported data type: '%{klass}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/daemon.rb:28\nmsgid \"Daemons must have an agent\"\nmsgstr \"\"\n\n#: ../lib/puppet/daemon.rb:84\nmsgid \"Cannot reexec unless ARGV arguments are set\"\nmsgstr \"\"\n\n#: ../lib/puppet/datatypes.rb:135\nmsgid \"Data Type Load Error for type '%{type_name}': %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/datatypes.rb:156\nmsgid \"a data type must have an interface\"\nmsgstr \"\"\n\n#: ../lib/puppet/datatypes.rb:197\nmsgid \"a data type can only have one interface\"\nmsgstr \"\"\n\n#: ../lib/puppet/datatypes.rb:203 ../lib/puppet/datatypes.rb:209\nmsgid \"a data type can only have one implementation\"\nmsgstr \"\"\n\n#: ../lib/puppet/defaults.rb:157\nmsgid \"Cannot disable unrecognized warning types '%{invalid}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/defaults.rb:158\nmsgid \"Valid values are '%{values}'.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'data_binding_terminus' is a setting and should not be translated\n#: ../lib/puppet/defaults.rb:558\nmsgid \"Setting 'data_binding_terminus' is deprecated.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'hiera' should not be translated\n#: ../lib/puppet/defaults.rb:560\nmsgid \"Convert custom terminus to hiera 5 API.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'environment_data_provider' is a setting and should not be translated\n#: ../lib/puppet/defaults.rb:754\nmsgid \"Setting 'environment_data_provider' is deprecated.\"\nmsgstr \"\"\n\n#: ../lib/puppet/defaults.rb:844\nmsgid \"Certificate names must be lower case\"\nmsgstr \"\"\n\n#: ../lib/puppet/defaults.rb:1103 ../lib/puppet/settings/enum_setting.rb:15 ../lib/puppet/settings/symbolic_enum_setting.rb:16\nmsgid \"Invalid value '%{value}' for parameter %{name}. Allowed values are '%{allowed_values}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'pluginsync' is a setting and should not be translated\n#: ../lib/puppet/defaults.rb:2044\nmsgid \"Setting 'pluginsync' is deprecated.\"\nmsgstr \"\"\n\n#: ../lib/puppet/error.rb:80\nmsgid \"Could not parse for environment %{environment}: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/error.rb:81 ../lib/puppet/parser/compiler.rb:42\nmsgid \"%{message} on node %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog.rb:9\nmsgid \"Compile, save, view, and convert catalogs.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog.rb:29\nmsgid \"Retrieve the catalog for the node from which the command is run.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog.rb:32\nmsgid \"Not implemented for the CLI; facts are collected internally.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog.rb:103\nmsgid \"Compile a catalog.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog/select.rb:6\nmsgid \"Retrieve a catalog and filter it for resources of a given type.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog/select.rb:7\nmsgid \"<host> <resource_type>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/catalog/select.rb:45\nmsgid \"no matching resources found\"\nmsgstr \"\"\n\n#: ../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\nmsgid \"Apache 2 license; see COPYING\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:11\nmsgid \"Interact with Puppet's settings.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:19\nmsgid \"SECTION_NAME\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:21\nmsgid \"The section of the configuration file to interact with.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:39\nmsgid \"Examine Puppet's current settings.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:40\nmsgid \"all | <setting> [<setting> ...]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:101\nmsgid \"No section specified; defaulting to '%{section_name}'.\"\nmsgstr \"\"\n\n#. TRANSLATORS '--section' is a command line option and should not be translated\n#: ../lib/puppet/face/config.rb:104\nmsgid \"Set the config section by using the `--section` flag.\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet config --section user print foo` is a command line example and should not be translated\n#: ../lib/puppet/face/config.rb:106\nmsgid \"For example, `puppet config --section user print foo`.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:107\nmsgid \"For more information, see https://puppet.com/docs/puppet/latest/configuration.html\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:114\nmsgid \"Resolving settings from section '%{section_name}' in environment '%{environment_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:119\nmsgid \"Set Puppet's settings.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:120\nmsgid \"[setting_name] [setting_value]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:187\nmsgid \"Deleted setting from '%{section_name}': '%{setting_string}', and adding it to 'server' section\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:202\nmsgid \"Delete a Puppet setting.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:203\nmsgid \"<setting>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:235 ../lib/puppet/face/config.rb:239 ../lib/puppet/face/config.rb:250\nmsgid \"Deleted setting from '%{section_name}': '%{setting_string}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/config.rb:253\nmsgid \"No setting found in configuration file for section '%{section_name}' setting name '%{name}'\"\nmsgstr \"\"\n\n#. TRANSLATORS the 'puppet.conf' is a specific file and should not be translated\n#: ../lib/puppet/face/config.rb:261\nmsgid \"The puppet.conf file does not exist %{puppet_conf}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:12\nmsgid \"Interact directly with the EPP template parser/renderer.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:15\nmsgid \"Validate the syntax of one or more EPP templates.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:16\nmsgid \"[<template>] [<template> ...]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:17\nmsgid \"Nothing, or encountered syntax errors.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:36\nmsgid \"Whether or not to continue after errors are reported for a template.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:80\nmsgid \"No template specified. No action taken\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:97 ../lib/puppet/face/epp.rb:198\nmsgid \"\"\n\"One or more file(s) specified did not exist:\\n\"\n\"%{missing_files_list}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:101\nmsgid \"Errors while validating epp\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:107\nmsgid \"Outputs a dump of the internal template parse tree for debugging\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:109 ../lib/puppet/face/parser.rb:107\nmsgid \"A dump of the resulting AST model unless there are syntax or validation errors.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:138 ../lib/puppet/face/epp.rb:309 ../lib/puppet/face/parser.rb:128\nmsgid \"<source>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:140\nmsgid \"Dump one epp source expression given on the command line.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:144\nmsgid \"Whether or not to validate the parsed result, if no-validate only syntax errors are reported.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:147 ../lib/puppet/face/parser.rb:137\nmsgid \"<old, pn, or json>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:148 ../lib/puppet/face/parser.rb:138\nmsgid \"Get result in 'old' (deprecated format), 'pn' (new format), or 'json' (new format in JSON).\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:152 ../lib/puppet/face/parser.rb:142\nmsgid \"Pretty print output. Only applicable together with --format pn or json\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:156\nmsgid \"Whether or not to show a file name header between files.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:180 ../lib/puppet/face/parser.rb:154\nmsgid \"No input to parse given on command line or stdin\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:207\nmsgid \"Renders an epp template as text\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:209\nmsgid \"A rendered result of one or more given templates.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:305\nmsgid \"<node_name>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:306\nmsgid \"The name of the node for which facts are obtained. Defaults to facts for the local node.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:311\nmsgid \"Render one inline epp template given on the command line.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:314\nmsgid \"<values_hash>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:315\nmsgid \"A Hash in Puppet DSL form given as arguments to the template being rendered.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:318\nmsgid \"<pp_or_yaml_file>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:319\nmsgid \"A .pp or .yaml file that is processed to produce a hash of values for the template.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:322\nmsgid \"<facts_file>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:323\nmsgid \"A .yaml or .json file containing a hash of facts made available in $facts and $trusted\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:327\nmsgid \"Whether or not to show a file name header between rendered results.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:347\nmsgid \"No input to process given on command line or stdin\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:359\nmsgid \"error while rendering epp\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:413\nmsgid \"Only .yaml or .pp can be used as a --values_file\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:416\nmsgid \"Could not load --values_file %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:419\nmsgid \"--values_file option must evaluate to a Hash or undef/nil, got: '%{template_class}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:433\nmsgid \"--values option must evaluate to a Hash or undef, got: '%{values_class}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/epp.rb:529\nmsgid \"Incorrect formatted data in %{fact_file} given via the --facts flag\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:10\nmsgid \"Retrieve and store facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:18\nmsgid \"Retrieve a node's facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:19\nmsgid \"[<node_certname>]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:41\nmsgid \"Upload local facts to the puppet master.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:79\nmsgid \"Uploading facts for '%{node}' to '%{server}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:90\nmsgid \"Retrieve current node's facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:91\nmsgid \"[<facts>]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:107 ../lib/puppet/face/facts.rb:112 ../lib/puppet/face/facts.rb:117 ../lib/puppet/face/module/changes.rb:23\nmsgid \"<path>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:109\nmsgid \"The location of the config file for Facter.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:114\nmsgid \"The path to a directory that contains custom facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:119\nmsgid \"The path to a directory that contains external facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:123\nmsgid \"Disable fact blocking mechanism.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:127\nmsgid \"Disable fact caching mechanism.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:131\nmsgid \"Show legacy facts when querying all facts.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:135\nmsgid \"Show only the value when the action is called with a single query\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/facts.rb:139\nmsgid \"Show how much time it took to resolve each fact.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:11\nmsgid \"Generates Puppet code from Ruby definitions.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:14\nmsgid \"Generates Puppet code for custom types\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:33\nmsgid \"<format>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:34\nmsgid \"The generation output format to use. Supported formats: pcore.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:38\nmsgid \"'%{format}' is not a supported format for type generation.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:43\nmsgid \"Forces the generation of output files (skips up-to-date checks).\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/generate.rb:58\nmsgid \"The output directory '%{outputdir}' exists and is not a directory\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:13\nmsgid \"Display Puppet help.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:16\nmsgid \"Display help about Puppet subcommands and their actions.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:17\nmsgid \"[<subcommand>] [<action>]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:18\nmsgid \"Short help text for the specified subcommand or action.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:25\nmsgid \"VERSION\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:26\nmsgid \"The version of the subcommand for which to show help.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:30\nmsgid \"Whether to render the help text in ronn format.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'puppet help' is a command line and should not be translated\n#: ../lib/puppet/face/help.rb:46\nmsgid \"The 'puppet help' command takes two (optional) arguments: a subcommand and an action\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:54\nmsgid \"Supplying a '--version' only makes sense when a Faces subcommand is given\"\nmsgstr \"\"\n\n#. TRANSLATORS '--version' is a command line option and should not be translated\n#: ../lib/puppet/face/help.rb:62\nmsgid \"The legacy subcommand '%{sub_command}' does not support supplying an action\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:101\nmsgid \"Could not load help for the application %{application_name}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:102 ../lib/puppet/face/help.rb:114\nmsgid \"Please check the error logs for more information.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:104 ../lib/puppet/face/help.rb:116\nmsgid \"Detail: \\\"%{detail}\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:113\nmsgid \"Could not load help for the face %{face_name}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:125\nmsgid \"Unable to load action %{actionname} from %{face}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:159\nmsgid \"(Deprecated)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:162 ../lib/puppet/face/help.rb:174\nmsgid \"!%{sub_command}! Subcommand unavailable due to error.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/help.rb:163 ../lib/puppet/face/help.rb:175\nmsgid \"Check error logs.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module.rb:13\nmsgid \"Creates, installs and searches for modules on the Puppet Forge.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/changes.rb:5\nmsgid \"Show modified files of an installed module.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/changes.rb:13\nmsgid \"Array of strings representing paths of modified files.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/changes.rb:29\nmsgid \"Could not find a valid module at %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/changes.rb:37\nmsgid \"No modified files\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/changes.rb:39\nmsgid \"%{count} files modified\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:10\nmsgid \"Install a module from the Puppet Forge or a release archive.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:20\nmsgid \"Pathname object representing the path to the installed module.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:87 ../lib/puppet/face/module/uninstall.rb:36 ../lib/puppet/face/module/upgrade.rb:34\nmsgid \"<name>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:90\nmsgid \"Force overwrite of existing module, if any. (Implies --ignore-dependencies.)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:98\nmsgid \"The directory into which modules are installed.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:111 ../lib/puppet/face/module/upgrade.rb:46\nmsgid \"Do not attempt to install dependencies. (Implied by --force.)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:118\nmsgid \"Module version to install.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:127\nmsgid \"Preparing to install into %{dir} ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/install.rb:136\nmsgid \"Module %{name} %{version} is already installed.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:6\nmsgid \"List installed modules\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:15\nmsgid \"hash of paths to module objects\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:18\nmsgid \"Whether to show dependencies as a tree view\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:83\nmsgid \" (no modules installed)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:135\nmsgid \"'%{parent_name}' (%{parent_version}) requires '%{dependency_name}' (%{dependency_version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:163\nmsgid \"\"\n\"Module '%{name}' (v%{version}) fails to meet some dependencies:\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:165\nmsgid \"\"\n\"Non semantic version dependency %{name} (v%{version}):\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:167\nmsgid \"\"\n\"Missing dependency '%{name}':\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:225\nmsgid \"UNMET DEPENDENCY\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/list.rb:259 ../lib/puppet/face/module/list.rb:264\nmsgid \"invalid\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:5\nmsgid \"Uninstall a puppet module.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:12\nmsgid \"Hash of module objects representing uninstalled modules and related errors.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:39\nmsgid \"Force uninstall of an installed module.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:47\nmsgid \"Ignore any local changes made. (Implied by --force.)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:54\nmsgid \"The version of the module to uninstall\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:67\nmsgid \"Preparing to uninstall '%{name}' (%{module_version}) ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:69\nmsgid \"Preparing to uninstall '%{name}' ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:83\nmsgid \"Removed '%{name}' (%{module_version}) from %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/uninstall.rb:85\nmsgid \"Removed '%{name}' from %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/upgrade.rb:6\nmsgid \"Upgrade a puppet module.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/upgrade.rb:37\nmsgid \"Force upgrade of an installed module. (Implies --ignore-dependencies.)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/upgrade.rb:53\nmsgid \"Ignore and overwrite any local changes made. (Implied by --force.)\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/upgrade.rb:60\nmsgid \"The version of the module to upgrade to.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/module/upgrade.rb:68\nmsgid \"Preparing to upgrade '%{name}' ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node.rb:8\nmsgid \"View and manage node definitions.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node.rb:20\nmsgid \"Retrieve a node object.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node.rb:21\nmsgid \"<host>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:5\nmsgid \"Clean up signed certs, cached facts, node objects, and reports for a node stored by the puppetmaster\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:6\nmsgid \"<host1> [<host2> ...]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:24\nmsgid \"At least one node should be passed\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:73\nmsgid \"Not managing %{node} certs as this host is not a CA\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:80\nmsgid \"%{node}'s facts removed\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:86\nmsgid \"%{node}'s cached node removed\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/node/clean.rb:92\nmsgid \"%{node}'s reports removed\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:10\nmsgid \"Interact directly with the parser.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:13\nmsgid \"Validate the syntax of one or more Puppet manifests.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:14\nmsgid \"[<manifest>] [<manifest> ...]\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:15\nmsgid \"Nothing, or the first syntax error encountered.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:52\nmsgid \"No manifest specified. Validating the default manifest %{manifest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:68\nmsgid \"\"\n\"One or more file(s) specified did not exist:\\n\"\n\"%{files}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:105\nmsgid \"Outputs a dump of the internal parse tree for debugging\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:130\nmsgid \"dump one source expression given on the command line.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:134\nmsgid \"Whether or not to validate the parsed result, if no-validate only syntax errors are reported\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:170\nmsgid \"\"\n\"One or more file(s) specified did not exist:\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/parser.rb:216\nmsgid \"For puppet parser validate\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/plugin.rb:10\nmsgid \"Interact with the Puppet plugin system.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/plugin.rb:22\nmsgid \"Download plugins from the puppet master.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/plugin.rb:56\nmsgid \"No plugins downloaded.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/plugin.rb:58\nmsgid \"Downloaded these plugins: %{plugins}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/report.rb:9\nmsgid \"Create, display, and submit reports.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/report.rb:12\nmsgid \"API only: submit a report.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/report.rb:13 ../lib/puppet/face/report.rb:34\nmsgid \"<report>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/report.rb:14\nmsgid \"Nothing.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/report.rb:28\nmsgid \"API only: submit a report with error handling.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/report.rb:46\nmsgid \"Uploaded report for %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:9\nmsgid \"API only: interact directly with resources via the RAL.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:18\nmsgid \"API only: get all resources of a single type.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:19\nmsgid \"<resource_type>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:20\nmsgid \"An array of Puppet::Resource objects.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:28\nmsgid \"API only: get a single resource.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:29\nmsgid \"<type>/<title>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:30\nmsgid \"A Puppet::Resource object.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:38\nmsgid \"API only: create a new resource.\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:42\nmsgid \"<resource_object>\"\nmsgstr \"\"\n\n#: ../lib/puppet/face/resource.rb:43\nmsgid \"The same resource object passed as an argument.\"\nmsgstr \"\"\n\n#: ../lib/puppet/feature/base.rb:21\nmsgid \"Cannot determine basic system flavour\"\nmsgstr \"\"\n\n#: ../lib/puppet/ffi/windows/api_types.rb:88\nmsgid \"Unable to read wide strings with %{null_terminator} terminal nulls\"\nmsgstr \"\"\n\n#: ../lib/puppet/ffi/windows/api_types.rb:233\nmsgid \"Bad GUID format.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:41\nmsgid \"File %{file} does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:57\nmsgid \"Could not back up %{file}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:65\nmsgid \"Diff is not supported on this platform\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:85\nmsgid \"Please provide a file or checksum to diff with\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:101\nmsgid \"Failed to diff files\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:116 ../lib/puppet/file_bucket/dipper.rb:173\nmsgid \"File not found\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:152\nmsgid \"Could not find file with checksum %{sum}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/dipper.rb:163 ../lib/puppet/indirector/file_bucket_file/file.rb:47\nmsgid \"Listing remote file buckets is not allowed\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/file.rb:29\nmsgid \"contents must be a String or Pathname, got a %{contents_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/file.rb:34\nmsgid \"Unknown option(s): %{opts}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/file.rb:98\nmsgid \"Computing checksum on string\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_bucket/file.rb:123\nmsgid \"Computing checksum on file %{path}\"\nmsgstr \"\"\n\n#. TRANSLATORS ':link', ':manage', ':follow' should not be translated\n#: ../lib/puppet/file_serving/base.rb:53\nmsgid \":links can only be set to :manage or :follow\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/base.rb:62\nmsgid \"Paths must be fully qualified\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/base.rb:72\nmsgid \"Relative paths must not be fully qualified\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration.rb:59\nmsgid \"Cannot find file: Invalid mount '%{mount_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration.rb:60\nmsgid \"Cannot find file: Invalid relative path '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration.rb:110\nmsgid \"Error parsing fileserver configuration: %{detail}; using old configuration\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:12\nmsgid \"File server configuration %{config_file} does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:13\nmsgid \"Cannot read file server configuration %{config_file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:33\nmsgid \"Fileserver configuration file does not use '=' as a separator\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:46 ../lib/puppet/util/network_device/config.rb:102\nmsgid \"Invalid argument '%{var}' at %{error_location}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:51\nmsgid \"Invalid entry at %{error_location}: '%{file_text}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:76\nmsgid \"%{mount} is already mounted at %{name} at %{error_location}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:103\nmsgid \"Removing mount \\\"%{mount}\\\": %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/configuration/parser.rb:107\nmsgid \"The '%{mount}' module can not have a path. Ignoring attempt to set it\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/content.rb:35\nmsgid \"Cannot read the contents of links unless following links\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/fileset.rb:36\nmsgid \"Fileset paths must be fully qualified: %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/fileset.rb:53\nmsgid \"Fileset paths must exist\"\nmsgstr \"\"\n\n#. TRANSLATORS \"recurse\" and \"recurselimit\" are parameter names and should not be translated\n#: ../lib/puppet/file_serving/fileset.rb:55\nmsgid \"Fileset recurse parameter must not be a number anymore, please use recurselimit\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/fileset.rb:71\nmsgid \"The directory '%{path}' contains %{entries} entries, which exceeds the limit of %{munged_max_files} specified by the max_files parameter for this resource. The limit may be increased, but be aware that large number of file resources can result in excessive resource consumption and degraded performance. Consider using an alternate method to manage large directory trees\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/fileset.rb:73\nmsgid \"The directory '%{path}' contains %{entries} entries, which exceeds the default soft limit %{soft_max_files} and may cause excessive resource consumption and degraded performance. To remove this warning set a value for `max_files` parameter or consider using an alternate method to manage large directory trees\"\nmsgstr \"\"\n\n#. TRANSLATORS \":links\" is a parameter name and should not be translated\n#: ../lib/puppet/file_serving/fileset.rb:94\nmsgid \"Invalid :links value '%{links}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/fileset.rb:108\nmsgid \"Invalid option '%{option}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:22\nmsgid \"Unsupported checksum type %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:28\nmsgid \"Unsupported source_permission %{source_permissions}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:37\nmsgid \"Could not understand URI %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:39\nmsgid \"Cannot use opaque URLs '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:40\nmsgid \"Must use URLs of type puppet as content URI\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:76\nmsgid \"Unsupported Windows source permissions option %{source_permissions}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/metadata.rb:129\nmsgid \"Cannot manage files of type %{file_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount.rb:22\nmsgid \"Invalid mount name format '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/file.rb:20 ../lib/puppet/file_serving/mount/file.rb:75\nmsgid \"Mounts without paths are not usable\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/file.rb:28\nmsgid \"File does not exist or is not accessible: %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/file.rb:57\nmsgid \"%{path} does not exist or is not a directory\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/file.rb:58\nmsgid \"%{path} is not readable\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/file.rb:97\nmsgid \"No client; expanding '%{path}' with local host\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/modules.rb:10 ../lib/puppet/file_serving/mount/scripts.rb:8\nmsgid \"No module specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/mount/tasks.rb:7\nmsgid \"No task specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_serving/terminus_selector.rb:30\nmsgid \"URI protocol '%{protocol}' is not currently supported for file serving\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/file_impl.rb:18\nmsgid \"FileSystem implementation expected Pathname, got: '%{klass}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/file_impl.rb:58\nmsgid \"Locked '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/file_impl.rb:61\nmsgid \"Unlocked '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/file_impl.rb:68\nmsgid \"Timeout waiting for exclusive lock on %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/jruby.rb:20 ../lib/puppet/file_system/windows.rb:140 ../lib/puppet/util.rb:677\nmsgid \"Is a directory: %{directory}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/memory_impl.rb:85\nmsgid \"Unable to find registered object for %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/path_pattern.rb:51\nmsgid \"PathPatterns cannot be created with directory traversals.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/path_pattern.rb:53\nmsgid \"A PathPattern cannot be a Windows current drive relative path.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/path_pattern.rb:62\nmsgid \"PathPatterns cannot be created with a zero byte.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/path_pattern.rb:76\nmsgid \"A relative PathPattern cannot be prefixed with a drive.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/path_pattern.rb:78\nmsgid \"A relative PathPattern cannot be an absolute path.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/path_pattern.rb:91\nmsgid \"An absolute PathPattern cannot be a relative path.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/uniquefile.rb:111\nmsgid \"unexpected prefix_suffix: %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/uniquefile.rb:138\nmsgid \"cannot generate temporary name using `%{basename}' under `%{tmpdir}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/windows.rb:52\nmsgid \"%{dest} already exists and the :force option was not specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/windows.rb:216\nmsgid \"This version of Windows does not support symlinks.  Windows Vista / 2008 or higher is required.\"\nmsgstr \"\"\n\n#: ../lib/puppet/file_system/windows.rb:221\nmsgid \"The current user does not have the necessary permission to manage symlinks.\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:140\nmsgid \"Malformed dependency: %{name}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:141\nmsgid \"Exception was: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:183\nmsgid \"Forge module is missing SHA256 and MD5 checksums\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:217\nmsgid \"Module install using MD5 is prohibited in FIPS mode.\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:221\nmsgid \"Downloaded release for %{name} did not match expected checksum %{checksum}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:228 ../lib/puppet/module_tool/applications/unpacker.rb:59 ../lib/puppet/module_tool/local_tarball.rb:89\nmsgid \"Could not extract contents of module archive: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge.rb:244\nmsgid \"Cannot consider release %{name}-%{version}: %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:29\nmsgid \"Unable to verify the SSL certificate at %{uri}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:37\nmsgid \"Could not connect via HTTPS to %{uri}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:38\nmsgid \"  Unable to verify the SSL certificate\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:39\nmsgid \"    The certificate may not be signed by a valid CA\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:40\nmsgid \"    The CA bundle included with OpenSSL may not be valid or up to date\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:55\nmsgid \"Unable to connect to the server at %{uri}. Detail: %{detail}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:64\nmsgid \"Could not connect to %{uri}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:65\nmsgid \"  There was a network communications problem\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:66\nmsgid \"    The error we caught said '%{detail}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:67\nmsgid \"    Check your network connection and try again\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:94 ../lib/puppet/forge/errors.rb:96 ../lib/puppet/forge/errors.rb:106\nmsgid \"Request to Puppet Forge failed.\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:94 ../lib/puppet/forge/errors.rb:96\nmsgid \"Detail: %{detail}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:107\nmsgid \"  The server being queried was %{uri}\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:108\nmsgid \"  The HTTP response we received was '%{response}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/forge/errors.rb:109\nmsgid \"  The message we received said '%{message}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:195\nmsgid \"Function Load Error for function '%{function_name}': %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:213\nmsgid \"Functions must be based on Puppet::Pops::Functions::Function. Got %{function_base}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:273\nmsgid \"Function Creation Error, cannot create a default dispatcher for function '%{func_name}', no method with this name found\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:351\nmsgid \"No loader present. Call create_loaded_function(:myname, loader,...), instead of 'create_function' if running tests\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:415\nmsgid \"A required parameter cannot be added after an optional parameter\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:465\nmsgid \"A required repeated parameter cannot be added after an optional parameter\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:488\nmsgid \"block_param accepts max 2 arguments (type, name), got %{size}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:492\nmsgid \"Expected PCallableType or PVariantType thereof, got %{type_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:496\nmsgid \"Expected block_param name to be a Symbol, got %{name_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:503\nmsgid \"Attempt to redefine block\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:525\nmsgid \"Argument to 'return_type' must be a String reference to a Puppet Data Type. Got %{type_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:535\nmsgid \"Parameters cannot be added after a block parameter\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:536\nmsgid \"Parameters cannot be added after a repeated parameter\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:539\nmsgid \"Parameter name argument must be a Symbol. Got %{name_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:552\nmsgid \"Parameter 'type' must be a String reference to a Puppet Data Type. Got %{type_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:595\nmsgid \"\"\n\"Parsing of type string '\\\"%{type_string}\\\"' failed with message: <%{message}>.\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:635\nmsgid \"\"\n\"Parsing of 'type \\\"%{assignment_string}\\\"' failed with message: <%{message}>.\\n\"\n\"Called from <%{ruby_file_location}>\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions.rb:644\nmsgid \"\"\n\"Expected a type alias assignment on the form 'AliasType = T', got '%{assignment_string}'.\\n\"\n\"Called from <%{ruby_file_location}>\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/abs.rb:50\nmsgid \"The abs() function's auto conversion of String to Numeric is deprecated - change to convert input before calling\"\nmsgstr \"\"\n\n#. TRANSLATORS the string \"binary_file()\" should not be translated\n#: ../lib/puppet/functions/binary_file.rb:30\nmsgid \"binary_file(): The given file '%{unresolved_path}' does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/ceiling.rb:29\nmsgid \"The ceiling() function's auto conversion of String to Float is deprecated - change to convert input before calling\"\nmsgstr \"\"\n\n#. TRANSLATORS: 'ceiling' is a name and should not be translated\n#: ../lib/puppet/functions/ceiling.rb:35\nmsgid \"ceiling(): cannot convert given value to a floating point value.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'compare' is a name\n#: ../lib/puppet/functions/compare.rb:95\nmsgid \"compare(): Non comparable type. Only values of the types Numeric, String, Semver, Timestamp and Timestamp can be compared. Got %{type_a} and %{type_b}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'compare' is a name\n#: ../lib/puppet/functions/compare.rb:104\nmsgid \"compare(): The third argument (ignore case) can only be used when comparing strings\"\nmsgstr \"\"\n\n#. TRANSLATORS 'compare' is a name\n#: ../lib/puppet/functions/compare.rb:108\nmsgid \"compare(): Accepts at most 3 arguments, got %{actual_number}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'compare' is a name\n#: ../lib/puppet/functions/compare.rb:112\nmsgid \"compare(): The third argument (ignore case) must be a Boolean. Got %{type}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'compare' is a name\n#: ../lib/puppet/functions/compare.rb:118\nmsgid \"compare(): Can only compare values of the same type (or for Timestamp/Timespan also against Numeric). Got %{type_a} and %{type_b}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/defined.rb:133\nmsgid \"The given resource type is a reference to all kind of types\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/defined.rb:139 ../lib/puppet/functions/defined.rb:154\nmsgid \"The given class type is a reference to all classes\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/defined.rb:159\nmsgid \"Invalid argument of type '%{value_class}' to 'defined'\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/dig.rb:45\nmsgid \"The given data does not contain a Collection at %{walked_path}, got '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/dig.rb:58\nmsgid \"The given data requires an Integer index at %{walked_path}, got '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/empty.rb:84\nmsgid \"Calling function empty() with %{arg_type} value is deprecated.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'eyaml_lookup_key':, 'path', 'paths' 'glob', 'globs', 'mapped_paths', and lookup_key should not be translated\n#: ../lib/puppet/functions/eyaml_lookup_key.rb:33\nmsgid \"'eyaml_lookup_key': one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this lookup_key function\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/eyaml_lookup_key.rb:54 ../lib/puppet/functions/yaml_data.rb:30\nmsgid \"%{path}: file does not contain a valid yaml hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/eyaml_lookup_key.rb:62 ../lib/puppet/functions/yaml_data.rb:38\nmsgid \"Unable to parse %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/eyaml_lookup_key.rb:94\nmsgid \"hiera-eyaml backend error decrypting %{data} when looking up %{key} in %{path}. Error was %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/floor.rb:29\nmsgid \"The floor() function's auto conversion of String to Float is deprecated - change to convert input before calling\"\nmsgstr \"\"\n\n#. TRANSLATORS: 'floor' is a name and should not be translated\n#: ../lib/puppet/functions/floor.rb:35\nmsgid \"floor(): cannot convert given value to a floating point value.\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/get.rb:136\nmsgid \"Syntax error in dotted-navigation string\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/getvar.rb:62\nmsgid \"The given string does not start with a valid variable name\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/getvar.rb:75\nmsgid \"First character after var name in get string must be a '.' - got %{char}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/hiera_include.rb:103\nmsgid \"Could not find data item %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/hocon_data.rb:13\nmsgid \"Lookup using Hocon data_hash function is not supported without hocon library\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/hocon_data.rb:34\nmsgid \"Unable to parse (%{path}): %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/match.rb:83\nmsgid \"match() expects pattern of T, where T is String, Regexp, Regexp[r], Pattern[p], or Array[T]. Got %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/match.rb:107\nmsgid \"Given Regexp Type has no regular expression\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/max.rb:151 ../lib/puppet/functions/max.rb:237\nmsgid \"The max() function's auto conversion of String to Numeric is deprecated - change to convert input before calling, or use lambda\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/max.rb:241\nmsgid \"The max() function's auto conversion of Any to String is deprecated - change to convert input before calling, or use lambda\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/min.rb:150 ../lib/puppet/functions/min.rb:236\nmsgid \"The min() function's auto conversion of String to Numeric is deprecated - change to convert input before calling, or use lambda\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/min.rb:240\nmsgid \"The min() function's auto conversion of Any to String is deprecated - change to convert input before calling, or use lambda\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/regsubst.rb:72\nmsgid \"The regsubst() function's encoding argument has been ignored since Ruby 1.9 and will be removed in a future release\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/require.rb:73\nmsgid \"Could not find class %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/slice.rb:118\nmsgid \"slice(): block must define at least one parameter. Block has 0.\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/slice.rb:122\nmsgid \"slice(): block must define one parameter, or the same number of parameters as the given size of the slice (%{slice_size}). Block has %{serving_size}; %{parameter_names}\"\nmsgstr \"\"\n\n#: ../lib/puppet/functions/strftime.rb:210\nmsgid \"The argument signature (String format, [String timezone]) is deprecated for #strftime. See #strftime documentation and Timespan type for more info\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/models/type/type.rb:48\nmsgid \"title patterns that use procs are not supported.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:42 ../lib/puppet/generate/type.rb:63 ../lib/puppet/generate/type.rb:75\nmsgid \"unsupported format '%{format}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:164\nmsgid \"Removed output '%{files_to_remove}' for non existing inputs\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:168\nmsgid \"No custom types were found.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:174\nmsgid \"template was not found at '%{key}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:184\nmsgid \"Generating Puppet resource types.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:202\nmsgid \"Failed to load custom type '%{type_name}' from '%{input}': %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:212\nmsgid \"Custom type '%{type_name}' was not defined in '%{input}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:238\nmsgid \"Generating '%{effective_output_path}' using '%{format}' format.\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:245\nmsgid \"Failed to generate '%{effective_output_path}': %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/generate/type.rb:251\nmsgid \"No files were generated because all inputs were up-to-date.\"\nmsgstr \"\"\n\n#: ../lib/puppet/graph/relationship_graph.rb:75\nmsgid \"appears to have a negative number of dependencies\"\nmsgstr \"\"\n\n#: ../lib/puppet/graph/simple_graph.rb:69\nmsgid \"Got an event from invalid vertex %{source}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"negative or zero\" refers to the count of paths\n#: ../lib/puppet/graph/simple_graph.rb:201\nmsgid \"negative or zero max_paths\"\nmsgstr \"\"\n\n#: ../lib/puppet/graph/simple_graph.rb:233\nmsgid \"\"\n\"Found %{num} dependency cycle:\\n\"\nmsgid_plural \"\"\n\"Found %{num} dependency cycles:\\n\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: ../lib/puppet/graph/simple_graph.rb:242\nmsgid \"Cycle graph written to %{filename}.\"\nmsgstr \"\"\n\n#. TRANSLATORS '--graph' refers to a command line option and OmniGraffle and GraphViz are program names and should not be translated\n#: ../lib/puppet/graph/simple_graph.rb:245\nmsgid \"Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/client.rb:157\nmsgid \"Request to %{uri} timed out connect operation after %{elapsed} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/client.rb:159\nmsgid \"Request to %{uri} timed out read operation after %{elapsed} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/client.rb:161\nmsgid \"Request to %{uri} interrupted after %{elapsed} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/client.rb:167\nmsgid \"Request to %{uri} failed after %{elapsed} seconds: %{message}\"\nmsgstr \"\"\n\n#. TRANSLATORS: `message` is an already translated string of why SSL failed to initialize\n#: ../lib/puppet/http/client.rb:320\nmsgid \"Failed to initialize SSL: %{message}\"\nmsgstr \"\"\n\n#. TRANSLATORS: `puppet agent -t` is a command and should not be translated\n#: ../lib/puppet/http/client.rb:322\nmsgid \"Run `puppet agent -t`\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/client.rb:392\nmsgid \"Sleeping for %{interval} seconds before retrying the request\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/client.rb:447\nmsgid \"HTTP REST queries cannot handle values of type '%{klass}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/errors.rb:39\nmsgid \"Too many HTTP redirections for %{addr}\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/errors.rb:47\nmsgid \"Too many HTTP retries for %{addr}\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/pool.rb:86\nmsgid \"Failed to close connection for %{site}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/redirector.rb:81\nmsgid \"Location response header is missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/redirector.rb:85\nmsgid \"Location URI is invalid: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/resolver/server_list.rb:67 ../lib/puppet/http/resolver/server_list.rb:75\nmsgid \"Trying with next server from server_list.\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/retry_after_handler.rb:75\nmsgid \"Failed to parse Retry-After header '%{retry_after}' as an integer or RFC 2822 date\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/service.rb:109\nmsgid \"Ignoring extra header \\\"%{name}\\\" as it was previously set.\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/service.rb:111\nmsgid \"Ignoring extra header \\\"%{name}\\\" as it has no value.\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/service.rb:132 ../lib/puppet/indirector/rest.rb:55\nmsgid \"No content type in http response; cannot parse\"\nmsgstr \"\"\n\n#: ../lib/puppet/http/service/ca.rb:127\nmsgid \"SSL context must contain a client certificate.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector.rb:27\nmsgid \"Indirection %{indirection_name} does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector.rb:40\nmsgid \"Already handling indirection for %{current}; cannot also handle %{next}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:30\nmsgid \"Facts but no fact format provided for %{request}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:33\nmsgid \"Found facts\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:38\nmsgid \"Catalog for %{request} was requested with fact definition for the wrong node (%{fact_name}).\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:72\nmsgid \"Requested environment '%{request_env}' did not match server specified environment '%{server_env}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:99\nmsgid \"Setup server facts for compiling\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:128\nmsgid \"Unsupported facts format\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n#: ../lib/puppet/indirector/catalog/compiler.rb:168\nmsgid \"Not inlining absent resource\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n#: ../lib/puppet/indirector/catalog/compiler.rb:171\nmsgid \"Not inlining resource without sources\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n#: ../lib/puppet/indirector/catalog/compiler.rb:174\nmsgid \"Not inlining unsupported source scheme\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlining refers to adding additional metadata (in this case we are not inlining)\n#: ../lib/puppet/indirector/catalog/compiler.rb:196\nmsgid \"Not inlining file outside environment\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlining refers to adding additional metadata\n#: ../lib/puppet/indirector/catalog/compiler.rb:202\nmsgid \"Inlining file metadata\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:292\nmsgid \"Could not get metadata for %{resource}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:313\nmsgid \"Unable to find a common checksum type between agent '%{agent_type}' and master '%{master_type}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:320\nmsgid \"Compiled static catalog for %{node} in environment %{environment} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:321\nmsgid \"Compiled static catalog for %{node} in environment %{environment}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:323\nmsgid \"Compiled static catalog for %{node} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:324\nmsgid \"Compiled static catalog for %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:328\nmsgid \"Compiled catalog for %{node} in environment %{environment} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:329\nmsgid \"Compiled catalog for %{node} in environment %{environment}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:331\nmsgid \"Compiled catalog for %{node} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:332\nmsgid \"Compiled catalog for %{node}\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlined refers to adding additional metadata\n#: ../lib/puppet/indirector/catalog/compiler.rb:353\nmsgid \"Inlined resource metadata into static catalog for %{node} in environment %{environment} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlined refers to adding additional metadata\n#: ../lib/puppet/indirector/catalog/compiler.rb:355\nmsgid \"Inlined resource metadata into static catalog for %{node} in environment %{environment}\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlined refers to adding additional metadata\n#: ../lib/puppet/indirector/catalog/compiler.rb:358\nmsgid \"Inlined resource metadata into static catalog for %{node} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#. TRANSLATORS Inlined refers to adding additional metadata\n#: ../lib/puppet/indirector/catalog/compiler.rb:360\nmsgid \"Inlined resource metadata into static catalog for %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:376\nmsgid \"Found node information\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:384\nmsgid \"Failed when searching for node %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:403\nmsgid \"Invalid option use_node for a remote request\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/compiler.rb:422\nmsgid \"Could not find node '%{name}'; cannot compile\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/json.rb:15\nmsgid \"Unable to deserialize catalog from json, retrying with pson\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/json.rb:24\nmsgid \"Unable to serialize catalog to json, retrying with pson. PSON is deprecated and will be removed in a future release\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/json.rb:28\nmsgid \"Unable to serialize catalog to json, no other acceptable format\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/catalog/rest.rb:45 ../lib/puppet/indirector/facts/rest.rb:22 ../lib/puppet/indirector/file_content/rest.rb:31 ../lib/puppet/indirector/file_metadata/rest.rb:28 ../lib/puppet/indirector/node/rest.rb:25\nmsgid \"Find %{uri} resulted in 404 with the message: %{body}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/exec.rb:13\nmsgid \"Exec commands must be an array\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/exec.rb:16\nmsgid \"You must set the exec parameter to a fully qualified command\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/exec.rb:23\nmsgid \"Failed to find %{name} via exec: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:6\nmsgid \"TERMINUS\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:7\nmsgid \"The indirector terminus to use.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:51\nmsgid \"Could not call '%{method}' on '%{indirection}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:60\nmsgid \"Delete an object.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:61 ../lib/puppet/indirector/face.rb:81\nmsgid \"<key>\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:66\nmsgid \"Retrieve an object by name.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:67\nmsgid \"[<key>]\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:80\nmsgid \"API only: create or overwrite an object.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:91\nmsgid \"Search for an object or retrieve multiple objects.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:92\nmsgid \"<query>\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:98\nmsgid \"Print the default terminus class for this face.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:107\nmsgid \"Run mode '%{mode}': %{terminus}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:109\nmsgid \"No default terminus class for run mode '%{mode}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:131\nmsgid \"Could not find terminus for %{indirection}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/face.rb:139\nmsgid \"Could not set '%{indirection}' terminus to '%{from}' (%{detail}); valid terminus types are %{types}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/facter.rb:16\nmsgid \"You cannot destroy facts in the code store; it is only used for getting facts from Facter\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/facter.rb:20\nmsgid \"You cannot save facts to the code store; it is only used for getting facts from Facter\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/facter.rb:38\nmsgid \"puppet facts show requires version 4.0.40 or greater of Facter.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/facter.rb:72\nmsgid \"Loading facts\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/network_device.rb:23\nmsgid \"You cannot destroy facts in the code store; it is only used for getting facts from a remote device\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/network_device.rb:27\nmsgid \"You cannot save facts to the code store; it is only used for getting facts from a remote device\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/facts/rest.rb:30\nmsgid \"PUT does not accept options\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:31\nmsgid \"could not find diff_with %{diff}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:32\nmsgid \"Unable to diff on this platform\"\nmsgstr \"\"\n\n#. TRANSLATORS \"FileBucket\" should not be translated\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:37\nmsgid \"FileBucket read %{checksum}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:55\nmsgid \"Error while parsing 'todate'\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:60\nmsgid \"Error while parsing 'fromdate'\"\nmsgstr \"\"\n\n#. TRANSLATORS \"FileBucket\" should not be translated\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:168\nmsgid \"FileBucket got a duplicate file %{file_checksum}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"FileBucket\" should not be translated\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:182\nmsgid \"Unable to verify existing FileBucket backup at '%{path}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:183\nmsgid \"Existing backup and new file have different content but same checksum, %{value}. Verify existing backup and remove if incorrect.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:189\nmsgid \"Existing backup does not match its expected sum, %{sum}. Overwriting corrupted backup.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:209\nmsgid \"Unsupported checksum type %{checksum_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_bucket_file/file.rb:212\nmsgid \"Invalid checksum %{checksum}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_metadata/http.rb:34\nmsgid \"cannot lookup multiple files\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/file_server.rb:45\nmsgid \"Could not find filesystem info for file '%{request}' in environment %{env}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Hiera\" is the name of a code library and should not be translated\n#: ../lib/puppet/indirector/hiera.rb:14\nmsgid \"Hiera terminus not supported without hiera library\"\nmsgstr \"\"\n\n#. TRANSLATORS \"merge\" is a parameter name and should not be translated\n#: ../lib/puppet/indirector/hiera.rb:74\nmsgid \"Unrecognized value for request 'merge' parameter: '%{merge}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:43\nmsgid \"Tried to cache when no cache class was set\"\nmsgstr \"\"\n\n#. TRANSLATORS \"TTL\" stands for \"time to live\" and refers to a duration of time\n#: ../lib/puppet/indirector/indirection.rb:71\nmsgid \"Indirection TTL must be an integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:107\nmsgid \"Indirection %{name} is already defined\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:132\nmsgid \"The setting %{setting} is not a valid indirection setting.\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:144\nmsgid \"No terminus specified for %{name}; cannot redirect\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:165\nmsgid \"No terminus class nor terminus setting was provided for indirection %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:184\nmsgid \"Invalid terminus name %{terminus_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:188\nmsgid \"Could not find terminus %{terminus_class} for indirection %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:204\nmsgid \"Expiring the %{cache} cache of %{instance}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:234\nmsgid \"Caching %{indirection} for %{request}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:245\nmsgid \"Filtered result for %{indirection} %{request}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:274\nmsgid \"Not using expired %{indirection} for %{request} from cache; expired at %{expiration}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:281\nmsgid \"Cached %{indirection} for %{request} failed: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:307\nmsgid \"Search results from terminus %{terminus_name} are not an array\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:346\nmsgid \"Not authorized to call %{method} on %{description}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:349\nmsgid \"Not authorized to call %{method} on %{description} with %{option}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/indirection.rb:376\nmsgid \"Could not find terminus %{terminus_class} for indirection %{indirection}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/json.rb:21\nmsgid \"Could not save %{json} %{request}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/json.rb:28\nmsgid \"Could not destroy %{json} %{request}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/json.rb:43\nmsgid \"directory traversal detected in %{json}: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/json.rb:44 ../lib/puppet/indirector/msgpack.rb:52 ../lib/puppet/indirector/yaml.rb:42\nmsgid \"invalid key\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/json.rb:65\nmsgid \"Could not read JSON data for %{name} %{key}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/json.rb:71\nmsgid \"Could not parse JSON data for %{name} %{key}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/memory.rb:16\nmsgid \"Could not find %{request} to destroy\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/msgpack.rb:13\nmsgid \"MessagePack terminus not supported without msgpack library\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/msgpack.rb:29\nmsgid \"Could not save %{name} %{request}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/msgpack.rb:36\nmsgid \"Could not destroy %{name} %{request}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/msgpack.rb:51 ../lib/puppet/indirector/yaml.rb:41\nmsgid \"directory traversal detected in %{indirection}: %{name}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"MessagePack\" is a program name and should not be translated\n#: ../lib/puppet/indirector/msgpack.rb:70\nmsgid \"Could not read MessagePack data for %{indirection} %{key}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/msgpack.rb:76\nmsgid \"Could not parse MessagePack data for %{indirection} %{key}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/node/exec.rb:13\nmsgid \"You must set the 'external_nodes' parameter to use the external node terminus\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/node/exec.rb:13\nmsgid \"none\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/node/exec.rb:64\nmsgid \"key is a %{klass}, not a string or symbol\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/node/exec.rb:68\nmsgid \"Could not load external node results for %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/report/json.rb:21 ../lib/puppet/indirector/report/yaml.rb:21\nmsgid \"replace_file mode: %{mode} is invalid\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/report/json.rb:33 ../lib/puppet/indirector/report/yaml.rb:33 ../lib/puppet/indirector/yaml.rb:34\nmsgid \"Could not save %{indirection} %{request}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/report/processor.rb:41\nmsgid \"Report %{report} failed: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/report/processor.rb:59\nmsgid \"No report named '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/request.rb:104\nmsgid \"Could not find indirection '%{indirection}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/request.rb:168\nmsgid \"Could not understand URL %{key}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/resource/ral.rb:64\nmsgid \"Could not find type %{request_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/resource/validator.rb:7\nmsgid \"Resource instance does not match request key\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/rest.rb:45\nmsgid \"Error %{code} on SERVER: %{returned_message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/terminus.rb:39\nmsgid \"Could not find indirection instance %{name} for %{terminus}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/terminus.rb:54\nmsgid \"Terminus subclasses must have associated constants\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/terminus.rb:81\nmsgid \"Could not discern indirection model from class constant\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/terminus.rb:139\nmsgid \"Cannot create instances of abstract terminus types\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/terminus.rb:171\nmsgid \"Instance name %{name} does not match requested key %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/terminus.rb:177\nmsgid \"Invalid instance type %{klass}, expected %{model_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/yaml.rb:16\nmsgid \"Could not parse YAML data for %{indirection} %{request}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/indirector/yaml.rb:22\nmsgid \"You can only save objects that respond to :name\"\nmsgstr \"\"\n\n#: ../lib/puppet/info_service/class_information_service.rb:19\nmsgid \"Given argument must be a Hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/info_service/class_information_service.rb:56\nmsgid \"The file %{f} does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/info_service/plan_information_service.rb:22 ../lib/puppet/info_service/task_information_service.rb:29\nmsgid \"Module %{module_name} not found in environment %{environment_name}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface.rb:152\nmsgid \"Cannot create face %{name} with invalid version number '%{version}'!\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:81 ../lib/puppet/interface/action.rb:125\nmsgid \"The rendering format must be a symbol, not %{class_name}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'set_rendering_method_for' and 'Proc' should not be translated\n#: ../lib/puppet/interface/action.rb:99\nmsgid \"The second argument to set_rendering_method_for must be a Proc\"\nmsgstr \"\"\n\n#. TRANSLATORS 'set_rendering_method_for' and 'Proc' should not be translated\n#: ../lib/puppet/interface/action.rb:102\nmsgid \"The second argument to set_rendering_method_for must be a Proc, not %{class_name}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'when_rendering', 'when_invoked' are method names and should not be translated\n#: ../lib/puppet/interface/action.rb:111\nmsgid \"The when_rendering method for the %{face} face %{name} action takes either just one argument, the result of when_invoked, or the result plus the %{arg_count} arguments passed to the when_invoked block, not a variable number\"\nmsgstr \"\"\n\n#. TRANSLATORS 'when_rendering', 'when_invoked' are method names and should not be translated\n#: ../lib/puppet/interface/action.rb:117\nmsgid \"The when_rendering method for the %{face} face %{name} action takes either just one argument, the result of when_invoked, or the result plus the %{arg_count} arguments passed to the when_invoked block, not %{string}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:128\nmsgid \"You can't define a rendering method for %{type} twice\"\nmsgstr \"\"\n\n#. TRANSLATORS 'when_invoked' should not be translated\n#: ../lib/puppet/interface/action.rb:237\nmsgid \"when_invoked requires at least one argument (options) for action %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:286 ../lib/puppet/interface/option_manager.rb:60\nmsgid \"Option %{option} conflicts with existing option %{conflict}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:291\nmsgid \"Option %{option} conflicts with existing option %{conflict} on %{face}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Puppet.settings' should not be translated\n#. TRANSLATORS 'Puppet.settings' references to the Puppet settings options and should not be translated\n#: ../lib/puppet/interface/action.rb:319 ../lib/puppet/interface/option_manager.rb:15\nmsgid \"Global option %{option} does not exist in Puppet.settings\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:369\nmsgid \"Multiple aliases for the same option passed: %{overlap_list}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:375\nmsgid \"Unknown options passed: %{unknown_list}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action.rb:393\nmsgid \"The following options are required: %{missing_list}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'when_rendering' is a method name and should not be translated\n#: ../lib/puppet/interface/action_builder.rb:66\nmsgid \"You must give a rendering format to when_rendering\"\nmsgstr \"\"\n\n#. TRANSLATORS 'when_rendering' is a method name and should not be translated\n#: ../lib/puppet/interface/action_builder.rb:70\nmsgid \"You must give a block to when_rendering\"\nmsgstr \"\"\n\n#. TRANSLATORS 'render_as' is a method name and should not be translated\n#: ../lib/puppet/interface/action_builder.rb:132\nmsgid \"You must give a rendering format to render_as\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action_builder.rb:137\nmsgid \"%{value} is not a valid rendering format: %{formats_list}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'when_invoked' is a method name and should not be translated and 'block' is a Ruby code block\n#: ../lib/puppet/interface/action_builder.rb:164\nmsgid \"actions need to know what to do when_invoked; please add the block\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/action_manager.rb:22\nmsgid \"Redefining action %{name} for %{self}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Face' refers to a programming API in Puppet, 'summary' and 'description' are specifc attribute names and should not be translated\n#: ../lib/puppet/interface/documentation.rb:62\nmsgid \"Face summary should be a single line; put the long text in 'description' instead.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'author' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:207\nmsgid \"author must be a string; use multiple statements for multiple authors\"\nmsgstr \"\"\n\n#. TRANSLATORS 'author' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:212\nmsgid \"author should be a single line; use multiple statements for multiple authors\"\nmsgstr \"\"\n\n#. TRANSLATORS 'author' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:233\nmsgid \"author should be a single line; use multiple statements\"\nmsgstr \"\"\n\n#. TRANSLATORS 'copyright' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:255\nmsgid \"copyright takes the owners names, then the years covered\"\nmsgstr \"\"\n\n#. TRANSLATORS 'copyright' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:281\nmsgid \"copyright owner must be a string or an array of strings\"\nmsgstr \"\"\n\n#. TRANSLATORS 'copyright' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:318\nmsgid \"copyright with a year %{value} is very strange; did you accidentally add or subtract two years?\"\nmsgstr \"\"\n\n#. TRANSLATORS 'copyright' is an attribute name and should not be translated\n#. TRANSLATORS 'copyright' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:334 ../lib/puppet/interface/documentation.rb:340\nmsgid \"%{value} is not a good copyright year or range\"\nmsgstr \"\"\n\n#. TRANSLATORS 'copyright' is an attribute name and should not be translated\n#: ../lib/puppet/interface/documentation.rb:359\nmsgid \"%{value} is not a good copyright year, set, or range\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/face_collection.rb:123\nmsgid \"\"\n\"Failed to load face %{name}:\\n\"\n\"%{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'face' refers to a programming API in Puppet\n#: ../lib/puppet/interface/face_collection.rb:135\nmsgid \"%{name} (%{class_name}) is not a valid face name\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:23\nmsgid \"%{option}: long options need two dashes (--)\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:39\nmsgid \"%{option}: already defined in puppet\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:44\nmsgid \"%{option}: duplicates existing alias %{duplicate} in %{parent}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:50\nmsgid \"%{option} is not valid for an option argument\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:55\nmsgid \"No option declarations found while building\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:68\nmsgid \"Option %{name} is inconsistent about taking an argument\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:75\nmsgid \"Options with optional arguments are not supported\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:78\nmsgid \"Option %{name} is inconsistent about the argument being optional\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:94\nmsgid \"Can't find a name in the declaration %{declaration}\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:104\nmsgid \"%{name} is an invalid option name\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option.rb:128 ../lib/puppet/interface/option.rb:147\nmsgid \"%{name} can't be optional and have a default value\"\nmsgstr \"\"\n\n#. TRANSLATORS 'proc' is a Ruby block of code\n#: ../lib/puppet/interface/option.rb:133\nmsgid \"default value for %{name} is a %{class_name}, not a proc\"\nmsgstr \"\"\n\n#. TRANSLATORS 'proc' is a Ruby block of code\n#: ../lib/puppet/interface/option.rb:158\nmsgid \"before action hook for %{name} is a %{class_name}, not a proc\"\nmsgstr \"\"\n\n#. TRANSLATORS 'proc' is a Ruby block of code\n#: ../lib/puppet/interface/option.rb:170\nmsgid \"after action hook for %{name} is a %{class_name}, not a proc\"\nmsgstr \"\"\n\n#. TRANSLATORS 'before_action' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:47\nmsgid \"%{option} before_action requires a block\"\nmsgstr \"\"\n\n#. TRANSLATORS 'before_action' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:51\nmsgid \"%{option} already has a before_action set\"\nmsgstr \"\"\n\n#. TRANSLATORS 'before_action' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:55\nmsgid \"before_action takes three arguments, action, args, and options\"\nmsgstr \"\"\n\n#. TRANSLATORS 'after_action' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:68\nmsgid \"%{option} after_action requires a block\"\nmsgstr \"\"\n\n#. TRANSLATORS 'after_action' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:72\nmsgid \"%{option} already has an after_action set\"\nmsgstr \"\"\n\n#. TRANSLATORS 'after_action' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:76\nmsgid \"after_action takes three arguments, action, args, and options\"\nmsgstr \"\"\n\n#. TRANSLATORS 'default_to' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:98\nmsgid \"%{option} default_to requires a block\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option_builder.rb:101\nmsgid \"%{option} already has a default value\"\nmsgstr \"\"\n\n#. TRANSLATORS 'default_to' is a method name and should not be translated\n#: ../lib/puppet/interface/option_builder.rb:105\nmsgid \"%{option} default_to block should not take any arguments\"\nmsgstr \"\"\n\n#: ../lib/puppet/interface/option_manager.rb:68\nmsgid \"Option %{option} conflicts with existing option %{conflict} on %{action}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Puppet::Type.newtype' should not be translated\n#: ../lib/puppet/metatype/manager.rb:79\nmsgid \"Puppet::Type.newtype(%{name}) now expects a hash as the second argument, not %{argument}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'new%{method}' will become a method name, do not translate this string\n#: ../lib/puppet/metatype/manager.rb:112\nmsgid \"'new%{method}' method already exists; skipping\"\nmsgstr \"\"\n\n#. TRANSLATORS 'puppet/type/%{name}' should not be translated\n#: ../lib/puppet/metatype/manager.rb:175\nmsgid \"Loaded puppet/type/%{name} but no class was created\"\nmsgstr \"\"\n\n#. TRANSLATORS 'metadata.json' is a specific file name and should not be translated.\n#: ../lib/puppet/module.rb:231\nmsgid \"%{name} has an invalid and unparsable metadata.json file. The parse error: %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/plan.rb:45\nmsgid \"Plan %{plan_name} not found in module %{module_name}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/plan.rb:69\nmsgid \"Plan names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/plan.rb:71\nmsgid \"Plan name cannot have extension %{ext}, must be .pp or .yaml\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/plan.rb:74\nmsgid \"Plan name cannot be a reserved word, but was '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/plan.rb:77\nmsgid \"Plan name cannot be a Puppet data type, but was '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:27 ../lib/puppet/module/task.rb:230\nmsgid \"Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:46\nmsgid \"Task %{task_name} not found in module %{module_name}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:93 ../lib/puppet/module/task.rb:99\nmsgid \"The 'files' task metadata expects an array, got %{files}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:114\nmsgid \"Could not find module %{module_name} containing task file %{filename}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:120\nmsgid \"Files must be saved in module directories that Puppet makes available via mount points: %{mounts}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:127\nmsgid \"File pathnames cannot include relative paths\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:132\nmsgid \"Could not find %{path} on disk\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:139\nmsgid \"Directories specified in task metadata must include a trailing slash: %{dir}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:146\nmsgid \"Files specified in task metadata cannot include a trailing slash: %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:163\nmsgid \"Task metadata for task %{name} does not specify implementations as an array\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:169\nmsgid \"Task metadata for task %{name} does not specify requirements as an array\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:174\nmsgid \"Task metadata for task %{name} specifies missing implementation %{implementation}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:186\nmsgid \"No source besides task metadata was found in directory %{directory} for task %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:190\nmsgid \"Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them\"\nmsgstr \"\"\n\n#: ../lib/puppet/module/task.rb:247\nmsgid \"Error reading metadata: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool.rb:40\nmsgid \"Not a valid full name: %{full_module_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/application.rb:46\nmsgid \"Could not determine module path\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/application.rb:50\nmsgid \"Unable to find metadata.json in module root at %{path} See https://puppet.com/docs/puppet/latest/modules_publishing.html for required file format.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/application.rb:59\nmsgid \"Could not parse JSON %{metadata_path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/application.rb:64\nmsgid \"A Modulefile was found in the root directory of the module. This file will be ignored and can safely be removed.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/application.rb:80\nmsgid \"Could not parse filename to obtain the username, module name and version.  (%{release_name})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/application.rb:84\nmsgid \"Invalid version format: %{version} (Semantic Versions are acceptable: http://semver.org)\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/checksummer.rb:47 ../lib/puppet/module_tool/applications/checksummer.rb:49\nmsgid \"No file containing checksums found.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/installer.rb:93 ../lib/puppet/module_tool/applications/upgrader.rb:107\nmsgid \"Downloading from %{host} ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/installer.rb:141 ../lib/puppet/module_tool/applications/upgrader.rb:145\nmsgid \"Resolving dependencies ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/installer.rb:220\nmsgid \"Preparing to install ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/installer.rb:223\nmsgid \"Installing -- do not interrupt ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/uninstaller.rb:94\nmsgid \"Either the `--ignore_changes` or `--force` argument must be specified to uninstall modules when running in FIPS mode.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/unpacker.rb:51\nmsgid \"Symlinks in modules are unsupported. Please investigate symlink %{from}->%{to}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/unpacker.rb:72\nmsgid \"No valid metadata.json found!\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/upgrader.rb:31\nmsgid \"Module upgrade is prohibited in FIPS mode.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/upgrader.rb:77\nmsgid \"Found '%{name}' (%{version}) in %{dir} ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/upgrader.rb:192\nmsgid \"Preparing to upgrade ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/applications/upgrader.rb:195\nmsgid \"Upgrading -- do not interrupt ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/contents_description.rb:56\nmsgid \"Could not find/load type: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:12\nmsgid \"'%{module_name}' (%{version}) requested; '%{module_name}' (%{installed_version}) already installed\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:17 ../lib/puppet/module_tool/errors/installer.rb:55 ../lib/puppet/module_tool/errors/installer.rb:73 ../lib/puppet/module_tool/errors/shared.rb:80 ../lib/puppet/module_tool/errors/shared.rb:122\nmsgid \"Could not install module '%{module_name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:18\nmsgid \"  Module '%{module_name}' (%{installed_version}) is already installed\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:19\nmsgid \"    Installed module has had changes made locally\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module upgrade` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/installer.rb:21\nmsgid \"    Use `puppet module upgrade` to install a different version\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module install --force` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/installer.rb:23\nmsgid \"    Use `puppet module install --force` to re-install only this module\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:33\nmsgid \"Could not install '%{requested_package}'; no releases are available from %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:38\nmsgid \"Could not install '%{requested_package}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:39 ../lib/puppet/module_tool/errors/shared.rb:59\nmsgid \"  No releases are available from %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:40\nmsgid \"    Does '%{requested_package}' have at least one published release?\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:50\nmsgid \"'%{module_name}' (%{version}) requested; Path %{dir} is not a directory.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:56\nmsgid \"  Path '%{directory}' exists but is not a directory.\"\nmsgstr \"\"\n\n#. TRANSLATORS \"mkdir -p '%{directory}'\" is a command line example and should not be translated\n#: ../lib/puppet/module_tool/errors/installer.rb:58\nmsgid \"  A potential solution is to rename the path and then \\\"mkdir -p '%{directory}'\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:68\nmsgid \"'%{module_name}' (%{version}) requested; Permission is denied to create %{dir}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:74\nmsgid \"  Permission is denied when trying to create directory '%{directory}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:75\nmsgid \"  A potential solution is to check the ownership and permissions of parent directories.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:84\nmsgid \"Attempt to install file with an invalid path into %{path} under %{dir}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:89\nmsgid \"Could not install package with an invalid path.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/installer.rb:90\nmsgid \"  Package attempted to install file into %{path} under %{directory}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:13\nmsgid \"Could not %{action} '%{module_name}' (%{version}); no version satisfies all dependencies\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:18 ../lib/puppet/module_tool/errors/shared.rb:204 ../lib/puppet/module_tool/errors/upgrader.rb:59\nmsgid \"Could not %{action} module '%{module_name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:21\nmsgid \"  The requested version cannot satisfy one or more of the following installed modules:\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:23\nmsgid \"    %{name}, installed: %{current_version}, expected: %{constraints}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:26\nmsgid \"    %{mod}, expects '%{name}': %{range}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:31\nmsgid \"  The requested version cannot satisfy all dependencies\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module %{action} --ignore-dependencies` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:35\nmsgid \"  Use `puppet module %{action} '%{module_name}' --ignore-dependencies` to %{action} only this module\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:49\nmsgid \"Could not %{action} '%{module_name}'; no releases are available from %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:51\nmsgid \"Could not %{action} '%{module_name}'; no releases matching '%{version}' are available from %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:57\nmsgid \"Could not %{action} '%{module_name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:60\nmsgid \"    Does '%{module_name}' have at least one published release?\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:62\nmsgid \"  No releases matching '%{requested_version}' are available from %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:75\nmsgid \"'%{module_name}' (%{version}) requested; installation conflict\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:83\nmsgid \"  Dependency '%{name}' (%{version}) would overwrite %{directory}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:85\nmsgid \"  Installation would overwrite %{directory}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:89\nmsgid \"    Currently, '%{current_name}' (%{current_version}) is installed to that directory\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module install --ignore-dependencies` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:94\nmsgid \"    Use `puppet module install --ignore-dependencies` to install only this module\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module install --force` is a command line and should not be translated\n#. TRANSLATORS `puppet module install --force` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:97 ../lib/puppet/module_tool/errors/shared.rb:126\nmsgid \"    Use `puppet module install --force` to install this module anyway\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:111\nmsgid \"'%{module_name}' (%{version}) requested; Invalid dependency cycle\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:116\nmsgid \"You specified '%{name}' (%{version})\"\nmsgstr \"\"\n\n#. TRANSLATORS This message repeats as separate lines as a list under the heading \"You specified '%{name}' (%{version})\\n\"\n#: ../lib/puppet/module_tool/errors/shared.rb:119\nmsgid \"This depends on '%{name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:123\nmsgid \"  No version of '%{module_name}' will satisfy dependencies\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:136\nmsgid \"Could not %{action} '%{module_name}', did you mean '%{suggestion}'?\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:141 ../lib/puppet/module_tool/errors/shared.rb:158 ../lib/puppet/module_tool/errors/shared.rb:181 ../lib/puppet/module_tool/errors/shared.rb:222\nmsgid \"Could not %{action} module '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:142\nmsgid \"  The name '%{module_name}' is invalid\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:143\nmsgid \"    Did you mean `puppet module %{action} %{suggestion}`?\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:153\nmsgid \"Could not %{action} '%{module_name}'; module is not installed\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:159\nmsgid \"  Module '%{module_name}' is not installed\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module %{action} %{suggestion}` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:162\nmsgid \"    You may have meant `puppet module %{action} %{suggestion}`\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module install` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:165\nmsgid \"    Use `puppet module install` to install this module\"\nmsgstr \"\"\n\n#. TRANSLATORS \"module path\" refers to a set of directories where modules may be installed\n#: ../lib/puppet/module_tool/errors/shared.rb:176\nmsgid \"Could not %{action} '%{module_name}'; module appears in multiple places in the module path\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:182\nmsgid \"  Module '%{module_name}' appears multiple places in the module path\"\nmsgstr \"\"\n\n#. TRANSLATORS This is repeats as separate lines as a list under \"Module '%{module_name}' appears multiple places in the module path\"\n#: ../lib/puppet/module_tool/errors/shared.rb:185\nmsgid \"    '%{module_name}' (%{version}) was found in %{path}\"\nmsgstr \"\"\n\n#. TRANSLATORS `--modulepath` is command line option and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:188\nmsgid \"    Use the `--modulepath` option to limit the search to specific directories\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:199\nmsgid \"Could not %{action} '%{module_name}'; module has had changes made locally\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:205\nmsgid \"  Installed module has had changes made locally\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module %{action} --ignore-changes` is a command line and should not be translated\n#: ../lib/puppet/module_tool/errors/shared.rb:207\nmsgid \"    Use `puppet module %{action} --ignore-changes` to %{action} this module anyway\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:217\nmsgid \"Could not %{action} '%{module_name}'; %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:223\nmsgid \"  Failure trying to parse metadata\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/shared.rb:224\nmsgid \"    Original message was: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:11\nmsgid \"Could not uninstall '%{module_name}'; no installed version matches\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:16\nmsgid \"Could not uninstall module '%{module_name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:17\nmsgid \"  No installed version of '%{module_name}' matches (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:19\nmsgid \"    '%{module_name}' (%{version}) is installed in %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:32\nmsgid \"Could not uninstall '%{module_name}'; installed modules still depend upon it\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:38\nmsgid \"Could not uninstall module '%{module_name}' (v%{requested_version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:40\nmsgid \"Could not uninstall module '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:42\nmsgid \"  Other installed modules have dependencies on '%{module_name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:44\nmsgid \"    '%{module_name}' (%{version}) requires '%{module_dep}' (%{dep_version})\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module uninstall --force` is a command line option that should not be translated\n#: ../lib/puppet/module_tool/errors/uninstaller.rb:47\nmsgid \"    Use `puppet module uninstall --force` to uninstall this module anyway\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:21\nmsgid \"Could not upgrade '%{module_name}'; more recent versions not found\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:26\nmsgid \"Could not upgrade module '%{module_name}' (%{version})\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:28\nmsgid \"  The installed version is already the latest version matching %{version}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:30\nmsgid \"  There are %{count} newer versions\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:31\nmsgid \"    No combination of dependency upgrades would satisfy all dependencies\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:33\nmsgid \"    Dependencies will not be automatically upgraded across major versions\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:34\nmsgid \"    Upgrading one or more of these modules may permit the upgrade to succeed:\"\nmsgstr \"\"\n\n#. TRANSLATORS `puppet module upgrade --force` is a command line option that should not be translated\n#: ../lib/puppet/module_tool/errors/upgrader.rb:41\nmsgid \"    Use `puppet module upgrade --force` to upgrade only this module\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:54\nmsgid \"Could not %{action} '%{module_name}' (%{version}); downgrades are not allowed\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/errors/upgrader.rb:60\nmsgid \"  Downgrading is not allowed.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/install_directory.rb:25\nmsgid \"Created target directory %{dir}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/installed_modules.rb:67\nmsgid \"%{module_name} (%{path}) has an invalid version number (%{version}). The version has been set to 0.0.0. If you are the maintainer for this module, please update the metadata.json with a valid Semantic Version (http://semver.org).\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:72\nmsgid \"Dependency conflict for %{module_name}: Dependency %{name} was given conflicting version requirements %{version_requirement} and %{dup_version}. Verify that there are no duplicates in the metadata.json.\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:181\nmsgid \"the field must be a namespaced module name\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:183\nmsgid \"the module name contains non-alphanumeric (or underscore) characters\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:185\nmsgid \"the module name must begin with a letter\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:187\nmsgid \"the namespace contains non-alphanumeric characters\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:190\nmsgid \"Invalid 'name' field in metadata.json: %{err}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:197\nmsgid \"version string cannot be parsed as a valid Semantic Version\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:198\nmsgid \"Invalid 'version' field in metadata.json: %{err}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:210\nmsgid \"field 'data_provider' contains non-alphanumeric characters\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:212\nmsgid \"field 'data_provider' must begin with a letter\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:216\nmsgid \"field 'data_provider' must be a string\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/metadata.rb:224\nmsgid \"Invalid 'version_range' field in metadata.json: %{err}\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/shared_behaviors.rb:34\nmsgid \"Downloading from %{uri} ...\"\nmsgstr \"\"\n\n#: ../lib/puppet/module_tool/shared_behaviors.rb:173\nmsgid \"Could not download module: %{message}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"tar\" is a program name and should not be translated\n#: ../lib/puppet/module_tool/tar.rb:17\nmsgid \"No suitable tar implementation found\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/client_request.rb:17\nmsgid \"Request is not set up; cannot build call\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/format.rb:45\nmsgid \"Unsupported option(s) %{options_list}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/format.rb:78\nmsgid \"%{klass} does not respond to %{method}; can not render multiple instances to %{mime}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/format_handler.rb:69\nmsgid \"No format matches the given format name or mime-type (%{format})\"\nmsgstr \"\"\n\n#. TRANSLATORS \"intern\" is a function name and should not be translated\n#: ../lib/puppet/network/format_support.rb:17\nmsgid \"Could not intern from %{format}: %{err}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"intern_multiple\" is a function name and should not be translated\n#: ../lib/puppet/network/format_support.rb:24\nmsgid \"Could not intern_multiple from %{format}: %{err}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"render_multiple\" is a function name and should not be translated\n#: ../lib/puppet/network/format_support.rb:31\nmsgid \"Could not render_multiple to %{format}: %{err}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"render\" is a function name and should not be translated\n#: ../lib/puppet/network/format_support.rb:116\nmsgid \"Could not render to %{format}: %{err}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"mime\" is a function name and should not be translated\n#: ../lib/puppet/network/format_support.rb:125\nmsgid \"Could not mime to %{format}: %{err}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/formats.rb:42 ../lib/puppet/network/formats.rb:55\nmsgid \"Serialized YAML did not contain a valid instance of %{klass}: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/formats.rb:48\nmsgid \"Serialized YAML did not contain a collection of instances when calling intern_multiple\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/formats.rb:62\nmsgid \"Serialized YAML did not contain a valid instance of %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:43\nmsgid \"No handler for %{indirection}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:65\nmsgid \"The indirection name must be purely alphanumeric, not '%{indirection_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:74\nmsgid \"Indirection '%{indirection_name}' does not match url prefix '%{url_prefix}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:80\nmsgid \"Could not find indirection '%{indirection_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:86\nmsgid \"An environment parameter must be specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:90\nmsgid \"The environment must be purely alphanumeric, not '%{environment}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:100\nmsgid \"Could not find environment '%{environment}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:106\nmsgid \"No request key specified in %{uri}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:118\nmsgid \"Could not find %{value0} %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:125\nmsgid \"Rendered result in %{format}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:131\nmsgid \"Sent response\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:139\nmsgid \"Could not find %{indirection} %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:151\nmsgid \"Could not find instances in %{indirection} with '%{key}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:190\nmsgid \"Failed to serialize %{model} for '%{key}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:203 ../lib/puppet/network/http/request.rb:72\nmsgid \"No supported formats are acceptable (Accept: %{accepted_formats})\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:228\nmsgid \"The request body is invalid: %{message}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"mime-type\" is a keyword and should not be translated\n#. TRANSLATORS \"mime-type\" is a keyword and should not be translated\n#: ../lib/puppet/network/http/api/indirected_routes.rb:234 ../lib/puppet/network/http/request.rb:40\nmsgid \"Client sent a mime-type (%{header}) that doesn't correspond to a format we support\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:240\nmsgid \"No support for http method %{http_method}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirected_routes.rb:244\nmsgid \"No support for plurality %{indirection} for %{http_method} operations\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/api/indirection_type.rb:30\nmsgid \"Not a valid indirection type\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/connection.rb:49\nmsgid \"Unrecognized option(s): %{opts}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/connection.rb:55\nmsgid \"Expected an instance of Puppet::SSL::Verifier but was passed a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/connection.rb:278\nmsgid \"Too many HTTP redirections for %{host}:%{port}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:25\nmsgid \"Not Acceptable: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:32\nmsgid \"Not Found: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:39\nmsgid \"Not Authorized: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:46\nmsgid \"Bad Request: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:53\nmsgid \"Method Not Allowed: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:60\nmsgid \"Unsupported Media Type: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/error.rb:68\nmsgid \"Server Error: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/handler.rb:24\nmsgid \"Given multiple routes with identical path regexes: %{regexes}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/handler.rb:68\nmsgid \"Processed request %{request_method} %{request_path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/handler.rb:111\nmsgid \"No route for %{request} %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/handler.rb:136\nmsgid \"Could not resolve %{ip}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/request.rb:22\nmsgid \"Unknown arguments: %{args}\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/request.rb:46\nmsgid \"No Content-Type header was received, it isn't possible to unserialize the request\"\nmsgstr \"\"\n\n#: ../lib/puppet/network/http/request.rb:55\nmsgid \"Missing required Accept header\"\nmsgstr \"\"\n\n#. TRANSLATORS 'ssl_context' is an argument and should not be translated\n#: ../lib/puppet/network/http_pool.rb:64\nmsgid \"An ssl_context is required when connecting to 'https://%{host}:%{port}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'ssl_context' is an argument and should not be translated\n#: ../lib/puppet/network/http_pool.rb:72\nmsgid \"An ssl_context is unnecessary when connecting to 'http://%{host}:%{port}' and will be ignored\"\nmsgstr \"\"\n\n#: ../lib/puppet/node.rb:29\nmsgid \"No name provided in serialized data\"\nmsgstr \"\"\n\n#: ../lib/puppet/node.rb:101\nmsgid \"Node names cannot be nil\"\nmsgstr \"\"\n\n#: ../lib/puppet/node.rb:140\nmsgid \"Could not retrieve facts for %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/node.rb:158\nmsgid \"The node parameter '%{param_name}' for node '%{node_name}' was already set to '%{value}'. It could not be set to '%{desired_value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/node.rb:201\nmsgid \"Trusted node data modified for node %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/node/environment.rb:252\nmsgid \"The 'disable_per_environment_manifest' setting is true, and the '%{env_name}' environment has an environment.conf manifest that conflicts with the 'default_manifest' setting.\"\nmsgstr \"\"\n\n#: ../lib/puppet/node/environment.rb:616 ../lib/puppet/pops/loaders.rb:311\nmsgid \"Could not parse for environment %{env}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/node/server_facts.rb:29\nmsgid \"Could not retrieve fact servername\"\nmsgstr \"\"\n\n#: ../lib/puppet/node/server_facts.rb:38\nmsgid \"Could not retrieve either serverip or serverip6 fact\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/compiler.rb:89\nmsgid \"The argument 'puppet_code' must be a String, got %{type}\"\nmsgstr \"\"\n\n#. TRANSLATORS, the 'ast' is the name of a parameter, do not translate\n#: ../lib/puppet/pal/compiler.rb:132\nmsgid \"The given 'ast' does not represent a literal value\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/compiler.rb:144\nmsgid \"The argument 'code_string' must be a String, got %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/compiler.rb:158\nmsgid \"The argument 'file' must be a String, got %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/compiler.rb:196\nmsgid \"Given data_type value is not a data type, got '%{type}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'type' and 'title' are internal parameter names - do not translate\n#: ../lib/puppet/pal/json_catalog_encoder.rb:56\nmsgid \"Both type and title must be given\"\nmsgstr \"\"\n\n#. TRANSLATORS: do not translate the variable names in this error message\n#. TRANSLATORS: do not translate the variable names in this error message\n#: ../lib/puppet/pal/pal_impl.rb:77 ../lib/puppet/pal/pal_impl.rb:182\nmsgid \"manifest_file or code_string cannot be given when configured_by_env is true\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:236\nmsgid \"temporary environment name\"\nmsgstr \"\"\n\n#. TRANSLATORS: do not translate variable name string in these assertions\n#: ../lib/puppet/pal/pal_impl.rb:241\nmsgid \"A block must be given to 'in_tmp_environment'\"\nmsgstr \"\"\n\n#. TRANSLATORS terms in the assertions below are names of terms in code\n#: ../lib/puppet/pal/pal_impl.rb:295\nmsgid \"A block must be given to 'in_environment'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'in_environment' is a name, do not translate\n#: ../lib/puppet/pal/pal_impl.rb:300\nmsgid \"The environment directory '%{env_dir}' does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:323\nmsgid \"No directory found for the environment '%{env_name}' on the path '%{envpath}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:378\nmsgid \"Given variables must be a hash, got %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:384\nmsgid \"Given variable '%{varname}' has illegal name\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:388\nmsgid \"Given value for '%{varname}' has illegal type - got: %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:559\nmsgid \"Puppet Pal: %{what}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:573\nmsgid \"Cannot use '%{a_term}' and '%{b_term}' at the same time\"\nmsgstr \"\"\n\n#: ../lib/puppet/pal/pal_impl.rb:580\nmsgid \"A block must be given\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter.rb:350\nmsgid \"No resource set for %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter.rb:449\nmsgid \"Munging failed for value %{value} in class %{class_name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter.rb:485\nmsgid \"Validate method failed for class %{class_name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/package_options.rb:24\nmsgid \"Expected either a string or hash of options\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/path.rb:30 ../lib/puppet/parameter/path.rb:56\nmsgid \"%{name} only accepts a single path, not an array of paths\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/path.rb:33\nmsgid \"%{name} must be a fully qualified path\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/value_collection.rb:23\nmsgid \"Cannot alias nonexistent value %{value}\"\nmsgstr \"\"\n\n#. TRANSLATORS ':call' is a property and should not be translated\n#: ../lib/puppet/parameter/value_collection.rb:138\nmsgid \"Property option :call is deprecated and no longer used.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/value_collection.rb:139\nmsgid \"Please remove it.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/value_collection.rb:188 ../lib/puppet/type/resources.rb:87\nmsgid \"Invalid value %{value}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/value_collection.rb:189\nmsgid \"Valid values are %{value_list}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parameter/value_collection.rb:190\nmsgid \"Valid values match %{pattern}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/leaf.rb:39\nmsgid \"'%{value}' is not a valid hostname\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/node.rb:7\nmsgid \"names should be an array\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/node.rb:9\nmsgid \"Node inheritance is removed in Puppet 4.0.0. See http://links.puppet.com/puppet-node-inheritance-deprecation\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/pops_bridge.rb:181\nmsgid \"Instantiating Resource with type checked parameters - scope is missing, skipping type checking.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/resource.rb:10\nmsgid \"Use of Puppet::Parser::AST::Resource is deprecated and not fully functional\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/resource_instance.rb:10\nmsgid \"Use of Puppet::Parser::AST::ResourceInstance is deprecated\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/ast/resourceparam.rb:9\nmsgid \"Use of Puppet::Parser::AST::ResourceParam is deprecated and not fully functional\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:30\nmsgid \"Compilation has been halted because: %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:31\nmsgid \"For more information, see https://puppet.com/docs/puppet/latest/environments_about.html\"\nmsgstr \"\"\n\n#. TRANSLATORS \"stage\" is a keyword in Puppet and should not be translated\n#: ../lib/puppet/parser/compiler.rb:84\nmsgid \"Only classes can set 'stage'; normal resources like %{resource} cannot change run stage\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:125\nmsgid \"For compiling %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:129\nmsgid \"Compile: Set node parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:131\nmsgid \"Compile: Created settings scope\"\nmsgstr \"\"\n\n#. TRANSLATORS \"main\" is a function name and should not be translated\n#: ../lib/puppet/parser/compiler.rb:134\nmsgid \"Compile: Evaluated main\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:136\nmsgid \"Compile: Evaluated AST node\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:138\nmsgid \"Compile: Evaluated node classes\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:140\nmsgid \"Compile: Evaluated generators\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:142\nmsgid \"Compile: Validate Catalog pre-finish\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:146\nmsgid \"Compile: Finished catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:150\nmsgid \"Compile: Validate Catalog final\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:220\nmsgid \"Could not find node statement with name 'default' or '%{names}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:238\nmsgid \"No source for scope passed to evaluate_classes\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:249\nmsgid \"Could not find class %{name} for %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:331\nmsgid \"Evaluated collections\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:346\nmsgid \"Evaluated definitions\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:372\nmsgid \"Iterated (%{count}) on generators\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:383\nmsgid \"Somehow looped more than 1000 times while evaluating host catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:415\nmsgid \"Could not find resource(s) %{resources} for overriding\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:426\nmsgid \"Failed to realize virtual resources %{resources}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"main\" is a function name and should not be translated\n#: ../lib/puppet/parser/compiler.rb:459\nmsgid \"Couldn't find main\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler.rb:532\nmsgid \"For initializing compiler\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/compiler/catalog_validator/relationship_validator.rb:35\nmsgid \"Could not find resource '%{res}' in parameter '%{param}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:45\nmsgid \"The method 'Puppet::Parser::Functions.autoloader#loadall' is deprecated in favor of using 'Scope#call_function'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:53\nmsgid \"The method 'Puppet::Parser::Functions.autoloader#load(\\\"%{name}\\\")' is deprecated in favor of using 'Scope#call_function'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:61\nmsgid \"The method 'Puppet::Parser::Functions.autoloader#loaded?(\\\"%{name}\\\")' is deprecated in favor of using 'Scope#call_function'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:192\nmsgid \"Overwriting previous definition for function %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:198\nmsgid \"Invalid statement type %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:210\nmsgid \"Called %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:213\nmsgid \"%{name}(): Wrong number of arguments given (%{arg_count} for %{arity})\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:215\nmsgid \"%{name}(): Wrong number of arguments given (%{arg_count} for minimum %{min_arg_count})\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:222\nmsgid \"custom functions must be called with a single array that contains the arguments. For example, function_example([1]) instead of function_example(1)\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions.rb:321\nmsgid \"%{name}() can only be called using the 4.x function API. See Scope#call_function\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/contain.rb:30\nmsgid \"Calling function_contain via the Scope class is deprecated. Use Scope#call_function instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/create_resources.rb:63\nmsgid \"create_resources(): wrong number of arguments (%{count}; must be 2 or 3)\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/create_resources.rb:64\nmsgid \"create_resources(): second argument must be a hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/create_resources.rb:67\nmsgid \"create_resources(): third argument, if provided, must be a hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/file.rb:33\nmsgid \"Could not find any files from %{values}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"fully qualified\" refers to a fully qualified file system path\n#: ../lib/puppet/parser/functions/generate.rb:17\nmsgid \"Generators must be fully qualified\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/generate.rb:26\nmsgid \"Generators can only contain alphanumerics, file separators, and dashes\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/generate.rb:30\nmsgid \"Can not use generators with '..' in them.\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/generate.rb:37\nmsgid \"Failed to execute generator %{generator}: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"function_include\", \"Scope\", and \"Scope#call_function\" refer to Puppet internals and should not be translated\n#: ../lib/puppet/parser/functions/include.rb:35\nmsgid \"Calling function_include via the Scope class is deprecated. Use Scope#call_function instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/inline_template.rb:25\nmsgid \"Failed to parse inline template: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/require.rb:42\nmsgid \"Calling function_require via the Scope class is deprecated. Use Scope#call_function instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/template.rb:35\nmsgid \"Failed to parse template %{file}:\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/template.rb:36\nmsgid \"  Filepath: %{file_path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/template.rb:37\nmsgid \"  Line: %{line}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/functions/template.rb:38\nmsgid \"  Detail: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/relationship.rb:35\nmsgid \"Invalid relationship type %{relationship_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/relationship.rb:45\nmsgid \"Could not find resource '%{source}' for relationship on '%{target}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/relationship.rb:48\nmsgid \"Could not find resource '%{target}' for relationship from '%{source}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:64\nmsgid \"Could not find stage %{stage} specified by %{resource}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:75\nmsgid \"Evaluated resource %{res}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:130\nmsgid \"Resources require a hash as last argument\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:131\nmsgid \"Resources require a scope\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:165\nmsgid \"Only subclasses can override parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:171\nmsgid \"Attempt to override an already evaluated resource with new values\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:173\nmsgid \"Attempt to override an already evaluated resource, defined at %{error_location}, with new values\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:300\nmsgid \"Parameter '%{name}' is already set on %{resource}; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:303\nmsgid \"Parameter '%{name}' is already set on %{resource} at %{error_location}; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:307\nmsgid \"Parameter '%{name}' is already set on %{resource} by %{source}; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:310\nmsgid \"Parameter '%{name}' is already set on %{resource} by %{source} at %{error_location}; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/resource.rb:348\nmsgid \"Duplicate parameter '%{param}' for on %{resource}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:154\nmsgid \"Numerical variables cannot be changed. Attempt to set $%{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:159\nmsgid \"Numerical variables cannot be deleted: Attempt to delete: $%{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:212\nmsgid \"%{callee}: expects a value for parameter $%{to}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:214\nmsgid \"%{callee}: default expression for $%{from} tries to illegally access not yet evaluated $%{to}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:234\nmsgid \"Attempt to assign variable %{name} when evaluating parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:377\nmsgid \"you must pass a compiler instance to a new scope object\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:493\nmsgid \"Scope variable name %{name} is a %{klass}, not a string\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:540\nmsgid \"Interpolation failed with '%{name}', but compilation continuing; %{reason}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:662\nmsgid \"class %{classname} could not be found\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:665\nmsgid \"class %{classname} has not been evaluated\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:713\nmsgid \"Default already defined for %{type} { %{param} }; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:776\nmsgid \"Cannot assign to a numeric match result variable '$%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:779\nmsgid \"Scope variable name %{name} is a %{class_type}, not a string\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:784 ../lib/puppet/parser/scope.rb:789\nmsgid \"Attempt to assign to a reserved variable name: '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:794\nmsgid \"Cannot reassign variable '$%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1025\nmsgid \"Invalid regex match data. Got a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1034\nmsgid \"Scope#find_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1039\nmsgid \"Scope#find_builtin_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1044\nmsgid \"Scope#find_defined_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1057\nmsgid \"Function %{name} not defined despite being loaded!\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1064\nmsgid \"Scope#resolve_type_and_title() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1084\nmsgid \"Cannot use undef as a class name\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/scope.rb:1086\nmsgid \"Cannot use empty string as a class name\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Class\" and \"Type\" are Puppet keywords and should not be translated\n#: ../lib/puppet/parser/scope.rb:1096\nmsgid \"Cannot use an unspecific Class[] Type\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Resource\" is a class name and should not be translated\n#: ../lib/puppet/parser/scope.rb:1124\nmsgid \"Cannot use an unspecific Resource[] where a Resource['class', name] is expected\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Resource\" is a class name and should not be translated\n#: ../lib/puppet/parser/scope.rb:1128\nmsgid \"Cannot use a Resource[%{type_name}] where a Resource['class', name] is expected\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Resource\" is a class name and should not be translated\n#: ../lib/puppet/parser/scope.rb:1132\nmsgid \"Cannot use an unspecific Resource['class'] where a Resource['class', name] is expected\"\nmsgstr \"\"\n\n#. TRANSLATORS, \"For running script\" is not user facing\n#. TRANSLATORS \"main\" is a function name and should not be translated\n#: ../lib/puppet/parser/script_compiler.rb:46\nmsgid \"Script: Evaluated main\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/script_compiler.rb:112\nmsgid \"having multiple named scopes is not supported when scripting\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/templatewrapper.rb:68\nmsgid \"Could not find template '%{filename}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/templatewrapper.rb:85\nmsgid \"Bound template variables for %{template_source} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/templatewrapper.rb:93\nmsgid \"Interpolated template %{template_source} in %%{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/parser/type_loader.rb:103\nmsgid \"No file(s) found for import of '%{pattern}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/access_operator.rb:293\nmsgid \"Cannot use %{key} where %{expected} is expected\"\nmsgstr \"\"\n\n#. TRANSLATORS 'CollectExpression' is a class name and should not be translated\n#: ../lib/puppet/pops/evaluator/collector_transformer.rb:15\nmsgid \"Expected CollectExpression\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/collector_transformer.rb:107\nmsgid \"Cannot transform a number to a tag\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/collector_transformer.rb:171 ../lib/puppet/pops/evaluator/collector_transformer.rb:233\nmsgid \"Cannot transform object of class %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/collectors/abstract_collector.rb:32\nmsgid \"Exported resource try to override without parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/collectors/exported_collector.rb:52\nmsgid \"A duplicate resource was found while collecting exported resources, with the type and title %{title}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:53\nmsgid \"A String is not comparable to a non String\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:70\nmsgid \"A Numeric is not comparable to non Numeric\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:100\nmsgid \"Symbol not comparable to non Symbol\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:105\nmsgid \"Timespans are only comparable to Timespans, Integers, and Floats\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:111\nmsgid \"Timestamps are only comparable to Timestamps, Integers, and Floats\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:117\nmsgid \"Versions not comparable to non Versions\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/compare_operator.rb:123\nmsgid \"Only Strings, Numbers, Timespans, Timestamps, and Versions are comparable\"\nmsgstr \"\"\n\n#. TRANSLATORS 'inline_epp()' is a method name and 'epp' refers to 'Embedded Puppet (EPP) template' and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:9\nmsgid \"inline_epp(): the first argument must be a String with the epp source text, got a %{class_name}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'inline_epp()' is a method name and 'EPP' refers to 'Embedded Puppet (EPP) template' and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:19\nmsgid \"inline_epp(): Invalid EPP: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'epp()' is a method name and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:29\nmsgid \"epp(): the first argument must be a String with the filename, got a %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:41\nmsgid \"Could not find template '%{file}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'epp()' is a method name and 'EPP' refers to 'Embedded Puppet (EPP) template' and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:50\nmsgid \"epp(): Invalid EPP: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'LambdaExpression' is a class name and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:63\nmsgid \"%{function_name}(): the parser did not produce a LambdaExpression, got '%{class_name}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'EppExpression' is a class name and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:68\nmsgid \"%{function_name}(): the parser did not produce an EppExpression, got '%{class_name}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'EPP' refers to 'Embedded Puppet (EPP) template'\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:73\nmsgid \"%{function_name}(): The EPP template contains illegal expressions (definitions)\"\nmsgstr \"\"\n\n#. TRANSLATORS 'template_args' is a variable name and should not be translated\n#: ../lib/puppet/pops/evaluator/epp_evaluator.rb:114\nmsgid \"%{function_name}(): the template_args must be a Hash, got a %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/evaluator_impl.rb:235\nmsgid \"multi var assignment from class\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/evaluator_impl.rb:755\nmsgid \"break() from context where this is illegal\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/evaluator_impl.rb:1235\nmsgid \"Can only append Array or Hash to a Hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/evaluator_impl.rb:1239\nmsgid \"An URI can only be merged with an URI or String\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/evaluator_impl.rb:1243\nmsgid \"Can only append Binary to a Binary\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/evaluator_impl.rb:1275\nmsgid \"Can only delete from an Array or Hash.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/external_syntax_support.rb:24 ../lib/puppet/pops/evaluator/runtime3_support.rb:24 ../lib/puppet/pops/evaluator/runtime3_support.rb:519\nmsgid \"Internal Error: Configuration of runtime error handling wrong: should have raised exception\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/runtime3_converter.rb:108\nmsgid \"Use of an Iterator is not supported here\"\nmsgstr \"\"\n\n#. TRANSLATORS 'PClassType' and 'PResourceType' are Puppet types and should not be translated\n#: ../lib/puppet/pops/evaluator/runtime3_converter.rb:155\nmsgid \"Cannot split the type %{class_name}, it represents neither a PClassType, nor a PResourceType.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/runtime3_resource_support.rb:31\nmsgid \"Unknown resource type: '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/runtime3_support.rb:149\nmsgid \"Internal error - attempt to create a local scope without a hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/evaluator/runtime3_support.rb:292\nmsgid \"Unknown function '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/functions/dispatch.rb:91\nmsgid \"Unknown injection %{injection_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/functions/function.rb:109\nmsgid \"Function %{class_name}(): cannot call function '%{function_name}' - no loader specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/functions/function.rb:125\nmsgid \"Function %{class_name}(): Unknown function: '%{function_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issue_reporter.rb:74\nmsgid \"Language validation logged %{error_count} errors, and %{warning_count} warnings. Giving up\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issue_reporter.rb:77\nmsgid \"Language validation logged %{error_count} errors. Giving up\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:55\nmsgid \"Error while reporting issue: %{code}. %{message}\"\nmsgstr \"\"\n\n#. TRANSLATORS \":label\" is a keyword and should not be translated\n#: ../lib/puppet/pops/issues.rb:96\nmsgid \"Label provider key :label must be set to produce the text of the message!\"\nmsgstr \"\"\n\n#. TRANSLATORS \":semantic\" is a keyword and should not be translated\n#: ../lib/puppet/pops/issues.rb:105\nmsgid \"Label provider key :semantic must be set to produce the text of the message!\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:151\nmsgid \"%{issue} may not have a name containing a hyphen. The name '%{name}' is not legal\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:161\nmsgid \"A variable name may not contain a hyphen. The name '%{name}' is not legal\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:169\nmsgid \"Classes, definitions, and nodes may only appear at toplevel or inside other classes\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:173\nmsgid \"%{value} may only appear at toplevel\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:177\nmsgid \"Illegal attempt to assign to '%{name}'. Cannot assign to variables in other namespaces\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:182\nmsgid \"Illegal attempt to assign to '%{value}'. Not an assignable reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:188\nmsgid \"Cannot reassign built in (or already assigned) variable '$%{var}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:190\nmsgid \"Cannot reassign variable '$%{var}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:196\nmsgid \"Attempt to assign to a reserved variable name: '$%{var}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:201\nmsgid \"Illegal attempt to assign to the numeric match result variable '$%{var}'. Numeric variables are not assignable\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:206\nmsgid \"Assignment not allowed here\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:211\nmsgid \"The numeric parameter name '$%{name}' cannot be used (clashes with numeric match result variables)\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:215\nmsgid \"The parameter '$%{name}' must be a literal type, not %{type_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:223\nmsgid \"Illegal attempt to assign via [index/key]. Not an assignable reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:230\nmsgid \"Illegal attempt to assign to %{value} via [index/key]. Not an assignable reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:234\nmsgid \"Mismatched number of assignable entries and values, expected %{expected}, got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:238\nmsgid \"No value for required key '%{key}' in assignment to variables from hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:242\nmsgid \"No value for required variable '$%{name}' in assignment to variables from class reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:246\nmsgid \"The operator '%{operator}' is no longer supported. See http://links.puppet.com/remove-plus-equals\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:252\nmsgid \"The operator '%{operator}' is not supported.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:259\nmsgid \"The operator '%{operator}' in %{value} is not supported.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:265\nmsgid \"Operator '%{operator}' is not applicable to %{left}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:269\nmsgid \"Operator '%{operator}' is not applicable to %{left} when right side is %{right}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:273\nmsgid \"Comparison of: %{left} %{operator} %{right}, is not possible. Caused by '%{detail}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:277\nmsgid \"Can not convert right match operand to a regular expression. Caused by '%{detail}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:281\nmsgid \"Left match operand must result in a String value. Got %{left}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:288\nmsgid \"Invalid use of expression. %{value} does not produce a value\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:294\nmsgid \"Illegal +> operation on attribute %{attr}. This operator can not be used in %{expression}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:298\nmsgid \"Illegal name. The given name '%{name}' does not conform to the naming rule /^((::)?[a-z_]\\\\w*)(::[a-z]\\\\w*)*$/\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:302\nmsgid \"Illegal type mapping. Expected a Type on the left side, got %{expression}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:306\nmsgid \"Illegal type mapping. Expected a Tuple[Regexp,String] on the left side, got %{expression}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:310\nmsgid \"Illegal parameter name. The given name '%{name}' does not conform to the naming rule /^[a-z_]\\\\w*$/\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:314\nmsgid \"Illegal variable name, The given name '%{name}' does not conform to the naming rule /^((::)?[a-z]\\\\w*)*((::)?[a-z_]\\\\w*)$/\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:318\nmsgid \"Illegal numeric variable name, The given name '%{name}' must be a decimal value if it starts with a digit 0-9\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:324\nmsgid \"Illegal type reference. The given name '%{name}' does not conform to the naming rule\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:332\nmsgid \"You cannot collect exported resources without storeconfigs being set; the collection will be ignored\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:340\nmsgid \"You cannot collect exported resources without storeconfigs being set; the export is ignored\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:346\nmsgid \"The hostname '%{hostname}' contains illegal characters (only letters, digits, '_', '-', and '.' are allowed)\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:352\nmsgid \"An interpolated expression is not allowed in a hostname of a node\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:359\nmsgid \"Illegal expression. %{expression} is unacceptable as %{feature} in %{container}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:365\nmsgid \"Illegal variable expression. %{expression} did not produce a variable name (String or Numeric).\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:372\nmsgid \"Illegal query expression. %{expression} cannot be used in a query\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:378\nmsgid \"Resource Defaults are not virtualizable\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:382\nmsgid \"Classes are not virtualizable\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:389\nmsgid \"Attempt to use unsupported range in %{expression}, %{count} values given for max 1\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:395\nmsgid \"Expressions of type %{expression} are not supported in this version of Puppet\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:399\nmsgid \"Illegal relationship operand, can not form a relationship with %{expression}. A Catalog type is required.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:403\nmsgid \"Illegal relationship operand, can not form a relationship with something of type %{expression_type}. A Catalog type is required.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:407\nmsgid \"String supports [] with one or two arguments. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:411\nmsgid \"String-Type [] requires all arguments to be integers (or default). Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:415\nmsgid \"Array supports [] with one or two arguments. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:419\nmsgid \"Hash supports [] with one or more arguments. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:423\nmsgid \"Integer-Type supports [] with one or two arguments (from, to). Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:427\nmsgid \"Integer-Type [] requires all arguments to be integers (or default). Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:431\nmsgid \"A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:435\nmsgid \"Float-Type supports [] with one or two arguments (from, to). Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:439\nmsgid \"Float-Type [] requires all arguments to be floats, or integers (or default). Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:444\nmsgid \"%{expression}[] cannot use %{actual} where one of the following is expected: %{expected}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:446\nmsgid \"%{expression}[] cannot use %{actual} where %{expected} is expected\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:451\nmsgid \"A substring operation does not accept %{label_article} %{actual_type} as a character index. Expected an Integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:455\nmsgid \"%{expression}[] argument must be a Type or a String. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:459\nmsgid \"%{base_type}[] arguments must be types. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:465\nmsgid \"%{base_type_label}[] accepts %{min} or more arguments. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:467\nmsgid \"%{base_type_label}[] accepts %{min} to %{max} arguments. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:469\nmsgid \"%{base_type_label}[] accepts %{min} %{label}. Got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:469\nmsgid \"argument\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:474\nmsgid \"Error creating type specialization of %{base_type}, %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:478\nmsgid \"Cannot specialize an already specialized %{kind} type\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:482\nmsgid \"First argument to Resource[] must be a resource type or a String. Got %{actual}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:486\nmsgid \"Arguments to Resource[] are all empty/undefined\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:490\nmsgid \"Illegal Class name in class reference. %{expression} cannot be used where a String is expected\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:494\nmsgid \"Unacceptable name. The name '%{name}' is unacceptable as the name of %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:498\nmsgid \"Unacceptable location. The name '%{name}' is unacceptable in file '%{file}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:503\nmsgid \"The %{value} '%{name}' is unacceptable as a top level construct in this location\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:505\nmsgid \"This %{value} is unacceptable as a top level construct in this location\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:510\nmsgid \"Parameter $%{param} is not last, and has 'captures rest'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:514\nmsgid \"Parameter $%{param} has 'captures rest' - not supported in %{container}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:518\nmsgid \"Parameter $%{param} is required but appears after optional parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:522\nmsgid \"Parameter $%{param} is required but no value was given\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:526\nmsgid \"The value '%{value}' cannot be converted to Numeric.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:530\nmsgid \"The string '%{before}' was automatically coerced to the numerical value %{after}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:534\nmsgid \"Unknown function: '%{name}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:538\nmsgid \"Unknown variable: '%{name}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:542\nmsgid \"Error while evaluating %{expression}, %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:546\nmsgid \"Resource type not found: %{res_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:550\nmsgid \"Illegal Resource Type expression, expected result to be a type name, or untitled Resource, got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:554\nmsgid \"The title '%{title}' has already been used in this resource expression\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:558\nmsgid \"The attribute '%{attribute}' has already been set\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:562\nmsgid \"Missing title. The title expression resulted in undef\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:566\nmsgid \"Missing title at index %{index}. The title expression resulted in an undef title\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:570\nmsgid \"Illegal title type at index %{index}. Expected String, got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:574\nmsgid \"Empty string title at %{index}. Title strings must have a length greater than zero.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:578\nmsgid \"Resource not found: %{type_name}['%{title}']\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:582\nmsgid \"The resource %{type_name}['%{title}'] does not have a parameter called '%{param}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:586\nmsgid \"Division by 0\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:590\nmsgid \"The result of the %{operator} expression is Infinity\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:595\nmsgid \"Heredoc syntax specification has empty segment between '+' : '%{syntax}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:599\nmsgid \"Ambiguous EPP parameter expression. Probably missing '<%-' before parameters to remove leading whitespace\"\nmsgstr \"\"\n\n#. TRANSLATORS \"import\" is a function name and should not be translated\n#: ../lib/puppet/pops/issues.rb:604\nmsgid \"Use of 'import' has been discontinued in favor of a manifest directory. See http://links.puppet.com/puppet-import-deprecation\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:608\nmsgid \"This %{expression} has no effect. A value was produced and then forgotten (one or more preceding expressions may have the wrong form)\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:612\nmsgid \"This expression is invalid. Did you try declaring a '%{name}' resource without a title?\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:616\nmsgid \"This %{expression} has no effect. %{container} can not end with a value-producing expression without other effect\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:620\nmsgid \"Use of reserved word: %{word}, must be quoted if intended to be a String value\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:624\nmsgid \"Use of future reserved word: '%{word}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:628\nmsgid \"The name: '%{name}' is already defined by Puppet and can not be used as the name of %{expression}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:632\nmsgid \"No matching entry for selector parameter with value '%{param}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:636\nmsgid \"Node inheritance is not supported in Puppet >= 4.0.0. See http://links.puppet.com/puppet-node-inheritance-deprecation\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:640\nmsgid \"Resource Override can only operate on resources, got: %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:644\nmsgid \"The parameter '%{param}' is declared more than once in the parameter list\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:648\nmsgid \"The key '%{key}' is declared more than once\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:652\nmsgid \"This %{container} already has a 'default' entry - this is a duplicate\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:656\nmsgid \"The parameter $%{param} redefines a built in parameter in %{container}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:660\nmsgid \"Expected value of type %{expected}, got %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:664\nmsgid \"Unfolding of attributes from Hash can only be used once per resource body\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:668\nmsgid \"This %{expression} appears in a context where catalog related expressions are not allowed\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:672\nmsgid \"Syntax error at %{location}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:676\nmsgid \"Illegal class reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:680\nmsgid \"Illegal fully qualified class reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:684\nmsgid \"Illegal fully qualified name\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:688\nmsgid \"Illegal name or bare word\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:692\nmsgid \"Illegal number '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:696\nmsgid \"Unicode escape '\\\\u' was not followed by 4 hex digits or 1-6 hex digits in {} or was > 10ffff\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:700\nmsgid \"Not a valid hex number %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:704\nmsgid \"Not a valid octal number %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:708\nmsgid \"Not a valid decimal number %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:712\nmsgid \"Internal Error: No string or file given to lexer to process.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:716\nmsgid \"Unrecognized escape sequence '\\\\%{ch}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:720\nmsgid \"Unclosed quote after %{after} followed by '%{followed_by}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:724\nmsgid \"Unclosed multiline comment\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:728\nmsgid \"Internal error: %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:732\nmsgid \"Unbalanced epp tag, reached <eof> without closing tag.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:736\nmsgid \"Reaching end after opening <%# without seeing %>\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:740\nmsgid \"Unbalanced embedded expression - opening <% and reaching end of input\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:744\nmsgid \"Unclosed parenthesis after '@(' followed by '%{followed_by}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:748\nmsgid \"Heredoc without end-tagged line\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:752\nmsgid \"Invalid heredoc escape char. Only t, r, n, s,  u, L, $ allowed. Got '%{actual}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:756\nmsgid \"Invalid syntax in heredoc expected @(endtag[:syntax][/escapes])\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:760\nmsgid \"Heredoc without any following lines of text\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:764\nmsgid \"Heredoc with an empty endtag\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:768\nmsgid \"An escape char for @() may only appear once. Got '%{escapes}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:772\nmsgid \"Heredoc with text in the margin is not allowed (line %{heredoc_line} in this heredoc)\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:776\nmsgid \"Illegal %{format} Byte Order mark at beginning of input: %{bom} - remove these from the puppet source\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:780\nmsgid \"No such file or directory: %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:784\nmsgid \"%{file} is not a file\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:789\nmsgid \"%{expression} resulted in a value outside of Puppet Integer max range, got '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:791\nmsgid \"%{expression} resulted in a value outside of Puppet Integer min range, got '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:796\nmsgid \"This runtime does not support hiera.yaml version %{version}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:800\nmsgid \"hiera.yaml version 3 cannot be used in %{location}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:804\nmsgid \"hiera.yaml version 4 cannot be used in the global layer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:808\nmsgid \"Undefined variable '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:812\nmsgid \"Backend '%{name}' is defined more than once.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:815 ../lib/puppet/pops/issues.rb:828\nmsgid \"First defined at %{error_location}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:821\nmsgid \"No data provider is registered for backend '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:825\nmsgid \"Hierarchy name '%{name}' defined more than once.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:834\nmsgid \"'hiera3_backend' is only allowed in the global layer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:838\nmsgid \"'default_hierarchy' is only allowed in the module layer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:842\nmsgid \"Use \\\"data_hash: %{function_name}_data\\\" instead of \\\"hiera3_backend: %{function_name}\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:846\nmsgid \"One of %{keys} must be defined in hierarchy '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:850 ../lib/puppet/pops/issues.rb:858\nmsgid \"Only one of %{keys} can be defined in hierarchy '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:854\nmsgid \"Only one of %{keys} can be defined in defaults\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:862\nmsgid \"Option key '%{key}' used in hierarchy '%{name}' is reserved by Puppet\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:866\nmsgid \"Option key '%{key}' used in defaults is reserved by Puppet\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:870\nmsgid \"Unable to find '%{function_type}' function named '%{function_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:874\nmsgid \"'alias' interpolation is only permitted if the expression is equal to the entire string\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:878\nmsgid \"Unknown interpolation method '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:882\nmsgid \"Interpolation using method syntax is not allowed in this context\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:886\nmsgid \"Endless recursion detected when attempting to serialize value of class %{type_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:890\nmsgid \"%{path} contains the special value default. It will be converted to the String 'default'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:894\nmsgid \"%{path} contains %{klass} value. It will be converted to the String '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:898\nmsgid \"%{path} contains a hash with %{klass} key. It will be converted to the String '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:902\nmsgid \"The feature '%{feature}' is only available when compiling a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:906\nmsgid \"The catalog operation '%{operation}' is only available when compiling a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:910\nmsgid \"%{expr} is only available when compiling a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:914\nmsgid \"The task operation '%{operation}' is not available when compiling a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:918\nmsgid \"%{expr} is not available when compiling a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:922\nmsgid \"The 'bolt' library is required to %{action}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:926\nmsgid \"Task not found: %{type_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/issues.rb:930\nmsgid \"Failed to load: %{type_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/label_provider.rb:78\nmsgid \"<%{string}> does not appear to contain a word\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/base_loader.rb:135\nmsgid \"Originally set %{original}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/base_loader.rb:135\nmsgid \"Set at unknown location\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/base_loader.rb:136\nmsgid \"Attempt to redefine entity '%{name}'. %{origin_info}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/gem_support.rb:32\nmsgid \"Gem not found %{uri}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/gem_support.rb:49\nmsgid \"Gem not found '%{gem_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/generic_plan_instantiator.rb:12\nmsgid \"Found multiple files for plan '%{plan_name}' but only one is allowed\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/generic_plan_instantiator.rb:22\nmsgid \"No instantiator is available to load plan from %{source_ref}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'loadables' is a variable containing loadable modules and should not be translated\n#: ../lib/puppet/pops/loader/module_loaders.rb:133\nmsgid \"given loadables are not of supported loadable kind\"\nmsgstr \"\"\n\n#. TRANSLATORS 'TypeSet' should not be translated\n#: ../lib/puppet/pops/loader/module_loaders.rb:241\nmsgid \"The code loaded from %{origin} does not define the TypeSet '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_function_instantiator.rb:27\nmsgid \"The code loaded from %{source_ref} does not define the function '%{func_name}' - it is empty.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_function_instantiator.rb:31\nmsgid \"The code loaded from %{source_ref} must contain only the function '%{type_name}' - it has additional definitions.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_function_instantiator.rb:36\nmsgid \"The code loaded from %{source_ref} does not define the function '%{type_name}' - no function found.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_function_instantiator.rb:42\nmsgid \"The code loaded from %{source_ref} produced function with the wrong name, expected %{expected}, actual %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_function_instantiator.rb:45\nmsgid \"The code loaded from %{source} contains additional logic - can only contain the function %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_plan_instantiator.rb:36\nmsgid \"The code loaded from %{source_ref} does not define the plan '%{plan_name}' - it is empty.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_plan_instantiator.rb:40\nmsgid \"The code loaded from %{source_ref} must contain only the plan '%{plan_name}' - it has additional definitions.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_plan_instantiator.rb:45\nmsgid \"The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_plan_instantiator.rb:51\nmsgid \"The code loaded from %{source_ref} produced plan with the wrong name, expected %{expected}, actual %{actual}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_plan_instantiator.rb:54\nmsgid \"The code loaded from %{source} contains additional logic - can only contain the plan %{plan_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_resource_type_impl_instantiator.rb:35\nmsgid \"The code loaded from %{source_ref} does not create the resource type '%{type_name}' - it is empty\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_resource_type_impl_instantiator.rb:51\nmsgid \"The code loaded from %{source_ref} does not create the resource type '%{type_name}' - no call to %{rname}.new found.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_resource_type_impl_instantiator.rb:55\nmsgid \"The code loaded from %{source_ref} must contain only the creation of resource type '%{type_name}' - it has additional logic.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_resource_type_impl_instantiator.rb:63\nmsgid \"The code loaded from %{source_ref} does not define the resource type '%{type_name}' - got '%{got}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/puppet_resource_type_impl_instantiator.rb:69\nmsgid \"The code loaded from %{source_ref} produced resource type with the wrong name, expected '%{expected}', actual '%{actual}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_data_type_instantiator.rb:19\nmsgid \"The code loaded from %{source_ref} does not seem to be a Puppet 5x API data type - no create_type call.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_data_type_instantiator.rb:27\nmsgid \"The code loaded from %{source_ref} did not produce a data type when evaluated. Got '%{klass}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_data_type_instantiator.rb:30 ../lib/puppet/pops/loader/ruby_function_instantiator.rb:30\nmsgid \"The code loaded from %{source_ref} produced mis-matched name, expected '%{type_name}', got %{created_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_function_instantiator.rb:19\nmsgid \"The code loaded from %{source_ref} does not seem to be a Puppet 4x API function - no create_function call.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_function_instantiator.rb:27\nmsgid \"The code loaded from %{source_ref} did not produce a Function class when evaluated. Got '%{klass}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb:25\nmsgid \"The code loaded from %{source_ref} does not seem to be a Puppet 3x API function - no 'newfunction' call.\"\nmsgstr \"\"\n\n#. TRANSLATORS - the word 'newfunction' should not be translated as it is a method name.\n#: ../lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb:47\nmsgid \"Illegal legacy function definition! The code loaded from %{source_ref} did not return the result of calling 'newfunction'. Got '%{klass}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb:51\nmsgid \"The code loaded from %{source_ref} produced mis-matched name, expected 'function_%{type_name}', got '%{created_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb:98\nmsgid \"Illegal method definition of method '%{method_name}' in source %{source_ref} on line %{line} in legacy function. See %{url} for more information\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/task_instantiator.rb:24\nmsgid \"Failed to load metadata for task %{name}: 'parameters' must be a hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/task_instantiator.rb:33\nmsgid \"Failed to load metadata for task %{name}: %{reason}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/type_definition_instantiator.rb:17\nmsgid \"The code loaded from %{source_ref} does not define the type '%{name}' - it is empty.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/type_definition_instantiator.rb:22\nmsgid \"The code loaded from %{source_ref} must contain only the type '%{name}' - it has additional definitions.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/type_definition_instantiator.rb:28\nmsgid \"The code loaded from %{source_ref} does not define the type '%{name}' - no type alias or type definition found.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/type_definition_instantiator.rb:34\nmsgid \"The code loaded from %{source_ref} produced type with the wrong name, expected '%{name}', actual '%{actual_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loader/type_definition_instantiator.rb:39\nmsgid \"The code loaded from %{source_ref} contains additional logic - can only contain the type '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loaders.rb:40\nmsgid \"Attempt to redefine already initialized loaders for environment\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loaders.rb:184\nmsgid \"Internal Error: Puppet Context ':loaders' missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loaders.rb:199\nmsgid \"Unable to find loader named '%{loader_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loaders.rb:220\nmsgid \"Internal Error: did not find public loader for module: '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/loaders.rb:267\nmsgid \"Internal Error: Attempt to redefine loader named '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup.rb:94\nmsgid \"Function lookup() did not find a value for the name '%{name}'\"\nmsgid_plural \"Function lookup() did not find a value for any of the names [%{name_list}]\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: ../lib/puppet/pops/lookup/environment_data_provider.rb:22\nmsgid \"hiera.yaml version 3 found at the environment root was ignored\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Explain' is referring to the 'Explainer' class and should not be translated\n#: ../lib/puppet/pops/lookup/explainer.rb:497\nmsgid \"Unknown Explain type %{qualifier_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/hiera_config.rb:87\nmsgid \"Using of legacy data provider function '%{function_name}'. Please convert to a 'data_hash' function\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/hiera_config.rb:143\nmsgid \"%{config_path}: File exists but does not contain a valid YAML hash. Falling back to Hiera version 3 default config\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/hiera_config.rb:203\nmsgid \"Hiera configuration recreated due to change of scope variables used in interpolation expressions\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/hiera_config.rb:442\nmsgid \"%{config_path}: Use of 'hiera.yaml' version 3 is deprecated. It should be converted to version 5\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/hiera_config.rb:556\nmsgid \"%{config_path}: Use of 'hiera.yaml' version 4 is deprecated. It should be converted to version 5\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/invocation.rb:93\nmsgid \"Recursive lookup detected in [%{name_stack}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:109\nmsgid \"Invalid data type in lookup_options for key '%{key}' could not parse '%{source}', error: '%{msg}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'lookup_options', 'convert_to' and args_string variable should not be translated,\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:117\nmsgid \"Applying convert_to lookup_option with arguments %{args}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:120\nmsgid \"The convert_to lookup_option for key '%{key}' raised error: %{msg}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:151\nmsgid \"Lookup of key '%{key}' failed: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:300\nmsgid \"value of %{opts} must be a hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:307\nmsgid \"all %{opts} patterns must match a key starting with module name '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:311\nmsgid \"all %{opts} keys must start with module name '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:437\nmsgid \"Defining \\\"data_provider\\\": \\\"%{name}\\\" in metadata.json is deprecated. It is ignored since a '%{config}' with version >= 5 is present\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:447\nmsgid \"Defining \\\"data_provider\\\": \\\"%{name}\\\" in metadata.json is deprecated.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:448 ../lib/puppet/pops/lookup/lookup_adapter.rb:502\nmsgid \"A '%{hiera_config}' file should be used instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:462\nmsgid \"Environment '%{env}', cannot find module_data_provider '%{provider}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:486\nmsgid \"Defining environment_data_provider='%{provider_name}' in environment.conf is deprecated\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:490\nmsgid \"The environment_data_provider='%{provider_name}' setting is ignored since '%{config_path}' version >= 5\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:501\nmsgid \"Defining environment_data_provider='%{provider_name}' in environment.conf is deprecated.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_adapter.rb:517\nmsgid \"Environment '%{env}', cannot find environment_data_provider '%{provider}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/lookup_key.rb:14\nmsgid \"%{problem} in key: '%{key}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/module_data_provider.rb:71\nmsgid \"hiera.yaml version 3 found at module root was ignored\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/module_data_provider.rb:86\nmsgid \"Environment '%{env}', cannot find module '%{module_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/lookup/sub_lookup.rb:79\nmsgid \"Data Provider type mismatch: Got %{klass} when a hash-like object was expected to access value using '%{segment}' from key '%{key}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'merge' is a variable name and 'strategy' is a key and should not be translated\n#: ../lib/puppet/pops/merge_strategy.rb:30\nmsgid \"The hash given as 'merge' must contain the name of a strategy in string form for the key 'strategy'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/merge_strategy.rb:40\nmsgid \"Unknown merge strategy: '%{strategy}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'MergeStrategies.add_strategy' is a method, 'stratgey_class' is a variable and 'MergeStrategy' is a class name and should not be translated\n#: ../lib/puppet/pops/merge_strategy.rb:60\nmsgid \"MergeStrategies.add_strategy 'strategy_class' must be a 'MergeStrategy' class. Got %{strategy_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/migration/migration_checker.rb:24 ../lib/puppet/pops/migration/migration_checker.rb:29 ../lib/puppet/pops/migration/migration_checker.rb:34 ../lib/puppet/pops/migration/migration_checker.rb:39 ../lib/puppet/pops/migration/migration_checker.rb:44 ../lib/puppet/pops/migration/migration_checker.rb:49 ../lib/puppet/pops/migration/migration_checker.rb:54 ../lib/puppet/pops/migration/migration_checker.rb:59\nmsgid \"Unsupported migration method called\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/model/ast_transformer.rb:68\nmsgid \"Error while transforming to Puppet 3 AST: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/model/ast_transformer.rb:85\nmsgid \"Not a valid expression in a collection query: %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/model/ast_transformer.rb:114\nmsgid \"Illegal expression - unacceptable as a node name\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/model/ast_transformer.rb:118\nmsgid \"Unacceptable transform - found an Object without a rule: %{klass}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'NUMBER' refers to a method name and the 'name_or_numeric' was the passed in value and should not be translated\n#: ../lib/puppet/pops/model/factory.rb:814\nmsgid \"Internal Error, NUMBER token does not contain a valid number, %{name_or_numeric}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/model/factory.rb:1124\nmsgid \"can only concatenate strings, got %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/parser_support.rb:202\nmsgid \"attempt to pass argument list to the function '%{name}' which cannot be called without parentheses\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/parser_support.rb:204\nmsgid \"illegal comma separated argument list\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:82\nmsgid \"unexpected end of input\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:84\nmsgid \"unexpected %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:121\nmsgid \"map key expected\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:132\nmsgid \"expected identifier to follow '('\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:143\nmsgid \"missing '%{token}' to end list\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:188\nmsgid \"expected identifier after ':'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:227\nmsgid \"unterminated quote\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:253\nmsgid \"malformed octal quote\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/parser/pn_parser.rb:268 ../lib/puppet/pops/parser/pn_parser.rb:273 ../lib/puppet/pops/parser/pn_parser.rb:277\nmsgid \"digit expected\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/resource/resource_type_impl.rb:167 ../lib/puppet/type.rb:436\nmsgid \"you must specify title patterns when there are two or more key attributes\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/abstract_writer.rb:60\nmsgid \"Integer out of bounds\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Integers' is a Ruby class for numbers and should not be translated\n#: ../lib/puppet/pops/serialization/abstract_writer.rb:104\nmsgid \"Internal error. Integers cannot be tabulated in extension payload\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/deserializer.rb:48 ../lib/puppet/pops/serialization/from_data_converter.rb:138\nmsgid \"No implementation mapping found for Puppet Type %{type_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/from_data_converter.rb:128\nmsgid \"Unable to deserialize type from %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/from_data_converter.rb:225\nmsgid \"Cannot create a %{type_name} from a %{arg_class}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/json.rb:205\nmsgid \"Unable to serialize a %{obj}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/json.rb:252\nmsgid \"JSON stream is not an array. It is a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/json.rb:260\nmsgid \"Unexpected end of input\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/json.rb:275\nmsgid \"Invalid input. %{ext_no} is not a valid extension number\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/json_path.rb:123\nmsgid \"Unable to parse jsonpath \\\"%{path}\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/object.rb:17\nmsgid \"Feature count mismatch for %{value0}. Expected %{required_count} - %{max}, actual %{value_count}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/object.rb:29\nmsgid \"Missing default value for %{type_name}[%{name}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/serialization/serializer.rb:137\nmsgid \"No Puppet Type found for %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:117 ../lib/puppet/pops/time/timestamp.rb:87\nmsgid \"Unable to parse '%{str}' using any of the formats %{formats}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:138\nmsgid \"%{klass} cannot be added to a Timespan\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:150\nmsgid \"%{klass} cannot be subtracted from a Timespan\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:163\nmsgid \"A Timestamp cannot be multiplied by %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:174\nmsgid \"Can not do modulus on a Timespan using a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:194\nmsgid \"A Timespan cannot be divided by %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:481\nmsgid \"Format specifiers %L and %N denotes fractions and must be used together with a specifier of higher magnitude\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:558 ../lib/puppet/pops/time/timespan.rb:566\nmsgid \"Unable to parse '%{timespan}' using format '%{format}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:617\nmsgid \"Bad format specifier '%{expression}' in '%{format}', at position %{position}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timespan.rb:635\nmsgid \"Format must be a String\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timestamp.rb:42\nmsgid \"Illegal timezone '%{timezone}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timestamp.rb:94\nmsgid \"Unable to parse '%{str}' using format '%{format}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timestamp.rb:98\nmsgid \"Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timestamp.rb:135\nmsgid \"%{klass} cannot be added to a Timestamp\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/time/timestamp.rb:150\nmsgid \"%{klass} cannot be subtracted from a Timestamp\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_binary_type.rb:77\nmsgid \"The given string in encoding '%{enc}' is invalid. Cannot create a Binary UTF-8 representation\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_init_type.rb:26\nmsgid \"Init cannot be parameterized with an undefined type and additional arguments\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_init_type.rb:160\nmsgid \"Creation of new instance of type '%{type_name}' is not supported\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_init_type.rb:187\nmsgid \"The type '%{type}' does not represent a valid set of parameters for %{subject}.new()\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:157\nmsgid \"expected %{label} to override an inherited %{feature_type}, but no such %{feature_type} was found\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:174\nmsgid \"%{member} attempts to override %{label}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:177\nmsgid \"%{member} attempts to override final %{label}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:181\nmsgid \"%{member} attempts to override %{label} without having override => true\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:184\nmsgid \"%{member} attempts to override %{label} with a type that does not match\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:299\nmsgid \"The attribute '%{name}' is reserved and cannot be used\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:306\nmsgid \"%{label} of kind 'constant' cannot be combined with final => false\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:314\nmsgid \"%{label} of kind '%{kind}' cannot be combined with an attribute value\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:320\nmsgid \"%{label} of kind 'constant' requires a value\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:705\nmsgid \"reference to unresolved type '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:742\nmsgid \"attribute %{label}[%{key}] is defined as both a constant and an attribute\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:775\nmsgid \"%{label} conflicts with attribute with the same name\"\nmsgstr \"\"\n\n#. TRANSLATORS equality_include_type = false should not be translated\n#: ../lib/puppet/pops/types/p_object_type.rb:789\nmsgid \"equality_include_type = false cannot be combined with non empty equality specification\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:801\nmsgid \"%{label} equality is referencing %{attribute} which is included in equality of %{including_parent}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:808\nmsgid \"%{label} equality is referencing non existent attribute '%{attribute}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:811\nmsgid \"%{label} equality is referencing %{attribute}. Only attribute references are allowed\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:815\nmsgid \"%{label} equality is referencing constant %{attribute}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type.rb:816\nmsgid \"Reference to constant is not allowed in equality\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type_extension.rb:55\nmsgid \"The %{label}-Type cannot be parameterized using []\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type_extension.rb:71\nmsgid \"'%{pn}' is not a known type parameter for %{label}-Type\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_object_type_extension.rb:85\nmsgid \"The %{label}-Type cannot be parameterized using an empty parameter list\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/p_sem_ver_type.rb:106\nmsgid \"The string '%{str}' cannot be converted to a SemVer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/tree_iterators.rb:39\nmsgid \"Only Array, Hash, and Object types can be used as container types. Got %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/tree_iterators.rb:47\nmsgid \"Options 'include_containers' and 'include_values' cannot both be false\"\nmsgstr \"\"\n\n#. TRANSLATORS 'TypeFactory#string' is a class and method name and should not be translated\n#: ../lib/puppet/pops/types/type_factory.rb:119\nmsgid \"Passing more than one argument to TypeFactory#string is deprecated\"\nmsgstr \"\"\n\n#. TRANSLATORS TypeMismatchDescriber is a class name and 'tense' is a method name and should not be translated\n#: ../lib/puppet/pops/types/type_mismatch_describer.rb:515\nmsgid \"Passing a 'tense' argument to the TypeMismatchDescriber is deprecated and ignored.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_mismatch_describer.rb:516\nmsgid \"Everything is now reported using present tense\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_mismatch_describer.rb:616\nmsgid \"Deferred function %{function_name} has no return_type, unable to guarantee value type during compilation.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_parser.rb:429\nmsgid \"Enum parameters must be identifiers or strings\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_parser.rb:668\nmsgid \"The expression <%{expression}> is not a valid type specification.\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_parser.rb:673\nmsgid \"Invalid number of type parameters specified: %{type} requires %{required}, %{given} provided\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_parser.rb:678\nmsgid \"Not a parameterized type <%{type}>\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/type_parser.rb:682\nmsgid \"Unknown type <%{type}>\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:931\nmsgid \"The string '%{str}' cannot be converted to Numeric\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:934\nmsgid \"Value of type %{type} cannot be converted to Numeric\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1179\nmsgid \"The string '%{str}' cannot be converted to Integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1182\nmsgid \"Value of type %{type} cannot be converted to Integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1191\nmsgid \"Illegal radix: %{radix}, expected 2, 8, 10, 16, or default\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1303\nmsgid \"The string '%{str}' cannot be converted to Float\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1306\nmsgid \"Value of type %{type} cannot be converted to Float\"\nmsgstr \"\"\n\n#. TRANSLATORS 'PStringType#initialize' is a class and method name and should not be translated\n#: ../lib/puppet/pops/types/types.rb:1519\nmsgid \"Passing more than one argument to PStringType#initialize is deprecated\"\nmsgstr \"\"\n\n#. TRANSLATORS 'PStringType#values' and '#value' are classes and method names and should not be translated\n#: ../lib/puppet/pops/types/types.rb:1573\nmsgid \"Method PStringType#values is deprecated. Use #value instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1898\nmsgid \"The string '%{str}' cannot be converted to Boolean\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:1901\nmsgid \"Value of type %{type} cannot be converted to Boolean\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:2640\nmsgid \"Value of type %{type} cannot be converted to Array\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:2709\nmsgid \"Puppet::Pops::Types::PHashType#element_type is deprecated, use #value_type instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:2849\nmsgid \"odd number of arguments for Hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/types/types.rb:2861\nmsgid \"Value of type %{type} cannot be converted to Hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/validation.rb:129\nmsgid \"Attempt to set validation severity for something that is not an Issue. (Got %{issue})\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/validation.rb:132\nmsgid \"Illegal severity level: %{level} for '%{issue_code}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/validation.rb:135\nmsgid \"Attempt to demote the hard issue '%{issue_code}' to %{level}\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/validation.rb:156\nmsgid \"Attempt to get validation severity for something that is not an Issue. (Got %{issue})\"\nmsgstr \"\"\n\n#: ../lib/puppet/pops/validation.rb:208\nmsgid \"Issue %{issue_code}: Cannot pass a %{class_name} as a semantic object when it does not support #pos(), #file() and #line()\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Property#array_matching', 'first', and 'all' should not be translated\n#: ../lib/puppet/property.rb:101\nmsgid \"Supported values for Property#array_matching are 'first' and 'all'\"\nmsgstr \"\"\n\n#: ../lib/puppet/property.rb:172\nmsgid \"Attempt to redefine method %{method} with block\"\nmsgstr \"\"\n\n#: ../lib/puppet/property.rb:216\nmsgid \"Could not convert change '%{name}' to string: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'insync_values?' should not be translated\n#: ../lib/puppet/property.rb:371\nmsgid \"Unknown failure using insync_values? on type: %{type} / property: %{name} to compare values %{should} and %{is}\"\nmsgstr \"\"\n\n#: ../lib/puppet/property.rb:493\nmsgid \"Could not set '%{value}' on %{class_name}: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Provider' refers to a Puppet provider class\n#: ../lib/puppet/property.rb:595\nmsgid \"Provider %{provider} must have features '%{needed_features}' to set '%{property}' to '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/ensure.rb:56\nmsgid \"created\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/ensure.rb:58\nmsgid \"removed\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/ensure.rb:60\nmsgid \"%{name} changed %{is} to %{should}\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/ensure.rb:65\nmsgid \"Could not convert change %{name} to string: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/ensure.rb:88\nmsgid \"No ability to determine if %{name} exists\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/keyvalue.rb:133\nmsgid \"The %{name} property must be specified as a hash or an array of key/value pairs (strings)!\"\nmsgstr \"\"\n\n#: ../lib/puppet/property/keyvalue.rb:139\nmsgid \"Key/value pairs must be separated by '%{separator}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider.rb:134\nmsgid \"No command %{command} defined for provider %{provider}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider.rb:383\nmsgid \"To support listing resources of this type the '%{provider}' provider needs to implement an 'instances' class method returning the current set of resources. We recommend porting your module to the simpler Resource API instead: https://puppet.com/search/docs?keys=resource+api\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider.rb:457\nmsgid \"'%{parameter_name}' is not a valid parameter for %{resource_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider.rb:543\nmsgid \"No resource and no name in property hash in %{class_name} instance\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:126\nmsgid \"Invalid value %{value}: %{property} must be an Integer!\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:335\nmsgid \"Cannot have both 'forcelocal' and 'ia_load_module' at the same time!\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:380\nmsgid \"Could not set %{property} on %{resource}[%{name}]: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:406\nmsgid \"attributes is setting the %{properties} properties via. the %{attributes} attributes, respectively! Please specify these property values in the resource declaration instead.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:408 ../lib/puppet/provider/aix_object.rb:417\nmsgid \"Could not set attributes on %{resource}[%{name}]: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:430\nmsgid \"aix.object_info(): Could not find %{resource}[%{name}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:477 ../lib/puppet/provider/nameservice.rb:168 ../lib/puppet/provider/nameservice/directoryservice.rb:436\nmsgid \"Could not create %{resource} %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/aix_object.rb:489 ../lib/puppet/provider/nameservice.rb:182\nmsgid \"Could not delete %{resource} %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/command.rb:24\nmsgid \"Command %{name} is missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec.rb:21\nmsgid \"Cannot understand environment setting %{setting}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec.rb:28\nmsgid \"Overriding environment setting '%{var}' with '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec.rb:32\nmsgid \"Empty environment setting '%{var}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec.rb:105\nmsgid \"'%{exe}' is not qualified and no path was specified. Please qualify the command or specify a path.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec/posix.rb:35 ../lib/puppet/provider/exec/posix.rb:53 ../lib/puppet/provider/exec/windows.rb:41 ../lib/puppet/provider/exec/windows.rb:55\nmsgid \"Could not find command '%{exe}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec/posix.rb:37 ../lib/puppet/provider/exec/windows.rb:43\nmsgid \"'%{exe}' is a %{klass}, not a file\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/exec/posix.rb:39\nmsgid \"'%{exe}' is not executable\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/file/posix.rb:92\nmsgid \"Apparently using negative UID (%{currentvalue}) on a platform that does not consistently handle them\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/file/posix.rb:110 ../lib/puppet/provider/file/windows.rb:48\nmsgid \"Failed to set owner to '%{should}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/file/posix.rb:124\nmsgid \"Apparently using negative GID (%{currentvalue}) on a platform that does not consistently handle them\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/file/posix.rb:142 ../lib/puppet/provider/file/windows.rb:60\nmsgid \"Failed to set group to '%{should}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/file/posix.rb:158 ../lib/puppet/provider/file/windows.rb:77\nmsgid \"failed to set mode %{mode} on %{path}: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/file/windows.rb:84\nmsgid \"Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/provider/file/windows.rb:126\nmsgid \"%{resource_name}: %{mode_part_type} set to SYSTEM. SYSTEM permissions cannot be set below FullControl ('7')\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/group/aix.rb:50\nmsgid \"No AIX group exists with a group name or gid of %{group}!\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/group/groupadd.rb:16 ../lib/puppet/provider/group/pw.rb:16\nmsgid \"GID must be an integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/group/groupadd.rb:61\nmsgid \"GID %{resource} already exists, use allowdupe to force group creation\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/ldap.rb:82\nmsgid \"No LDAP Configuration defined for %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/ldap.rb:83\nmsgid \"Invalid LDAP Configuration defined for %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice.rb:47\nmsgid \"%{name} is not a valid attribute for %{resource_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice.rb:89\nmsgid \"Invalid value %{value}: %{error}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"autogen_id()\" is a method name and should not be translated\n#: ../lib/puppet/provider/nameservice.rb:134\nmsgid \"autogen_id() does not support auto generation of id for resource type %{resource_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice.rb:156 ../lib/puppet/provider/nameservice/directoryservice.rb:388\nmsgid \"already exists\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice.rb:174\nmsgid \"already absent\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice.rb:268\nmsgid \"Nameservice command must be an array\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice.rb:274 ../lib/puppet/provider/nameservice/directoryservice.rb:349 ../lib/puppet/provider/nameservice/directoryservice.rb:378\nmsgid \"Could not set %{param} on %{resource}[%{name}]: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:108\nmsgid \"Could not get %{resource} list from DirectoryService\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:168\nmsgid \"Could not get report.  command execution failed.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'OS X 10.7' is an operating system and should not be translated, 'Salted SHA512' is the name of a hashing algorithm\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:208\nmsgid \"OS X 10.7 requires a Salted SHA512 hash password of 136 characters.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:209\nmsgid \"Please check your password and try again.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:297\nmsgid \"Invalid id_type %{id_type}. Only 'uid' and 'gid' supported\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:405\nmsgid \"Could not set GeneratedUID for %{resource} %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:457\nmsgid \"Could not remove %{member} from group: %{resource}, %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/nameservice/directoryservice.rb:471\nmsgid \"Could not add %{new_member} to group: %{name}, %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"prefetch\" is a program name and should not be translated\n#: ../lib/puppet/provider/network_device.rb:27\nmsgid \"Could not perform network device prefetch: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:48\nmsgid \"The aix provider can only be used by root\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:90 ../lib/puppet/provider/package/nim.rb:53\nmsgid \"Failed to uninstall package '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:97\nmsgid \"A directory is required which will be used to find packages\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:109\nmsgid \"aix package provider is unable to downgrade packages\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:114\nmsgid \"Package '%{name}' is in a %{status} state and requires manual intervention\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:135\nmsgid \"Could not list installed Packages: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aix.rb:156 ../lib/puppet/provider/package/yum.rb:340\nmsgid \"Tried to get latest on a missing package\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/appdmg.rb:105 ../lib/puppet/provider/package/pkgdmg.rb:149\nmsgid \"Mac OS X PKG DMGs must specify a package source.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/appdmg.rb:109 ../lib/puppet/provider/package/pkgdmg.rb:154\nmsgid \"Mac OS X PKG DMGs must specify a package name.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apple.rb:44\nmsgid \"Mac OS X packages must specify a package source\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apt.rb:93\nmsgid \"/etc/apt/sources.list contains a cdrom source; not installing.  Use 'allowcdrom' to override this failure.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apt.rb:178 ../lib/puppet/provider/package/aptitude.rb:22 ../lib/puppet/provider/package/ports.rb:22 ../lib/puppet/provider/package/portupgrade.rb:96 ../lib/puppet/provider/package/portupgrade.rb:153 ../lib/puppet/provider/package/rug.rb:29 ../lib/puppet/provider/package/up2date.rb:18 ../lib/puppet/provider/package/yum.rb:323 ../lib/puppet/provider/package/zypper.rb:148\nmsgid \"Could not find package %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apt.rb:182 ../lib/puppet/provider/package/yum.rb:328\nmsgid \"Failed to update to version %{should}, got version %{version} instead\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apt.rb:194 ../lib/puppet/provider/package/fink.rb:57\nmsgid \"Could not find latest version\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apt.rb:205 ../lib/puppet/provider/package/fink.rb:68\nmsgid \"Preseeding %{response} to debconf-set-selections\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/apt.rb:209 ../lib/puppet/provider/package/fink.rb:72\nmsgid \"No responsefile specified or non existent, not preseeding anything\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aptrpm.rb:59\nmsgid \"Could not match version '%{version}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/aptrpm.rb:72\nmsgid \"Could not match string\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/blastwave.rb:20\nmsgid \"The pkg-get command is missing; blastwave packaging unavailable\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/blastwave.rb:24\nmsgid \"It is highly recommended you create '/var/pkg-get/admin'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/blastwave.rb:25\nmsgid \"See /var/pkg-get/admin-fullauto\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/blastwave.rb:81 ../lib/puppet/provider/package/pkgutil.rb:75 ../lib/puppet/provider/package/pkgutil.rb:148\nmsgid \"Cannot match %{line}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/dnfmodule.rb:30\nmsgid \"Modules are not supported on DNF versions lower than 3.0.1\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/dpkg.rb:92\nmsgid \"You cannot install dpkg packages without a source\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/dpkg.rb:120\nmsgid \"Could not update: You cannot install dpkg packages without a source\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/dpkg.rb:124\nmsgid \"source doesn't contain named package, but %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/freebsd.rb:32\nmsgid \"source is defined but does not have trailing slash, ignoring %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/gem.rb:126 ../lib/puppet/provider/package/puppetserver_gem.rb:64\nmsgid \"Could not list gems: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/gem.rb:152\nmsgid \"Could not match %{desc}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/gem.rb:229 ../lib/puppet/provider/package/puppetserver_gem.rb:96\nmsgid \"Invalid source '%{uri}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/gem.rb:240 ../lib/puppet/provider/package/puppetserver_gem.rb:107\nmsgid \"puppet:// URLs are not supported as gem sources\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/gem.rb:256 ../lib/puppet/provider/package/puppetserver_gem.rb:118\nmsgid \"Could not install: %{output}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/gem.rb:282 ../lib/puppet/provider/package/puppetserver_gem.rb:128\nmsgid \"Could not uninstall: %{output}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/hpux.rb:25\nmsgid \"source must be provided to install HP-UX packages\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:47\nmsgid \"Unrecognized AIX package type identifier: '%{pkg_type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:60\nmsgid \"An LPP source location is required in 'source'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:102\nmsgid \"Unable to find package '%{package}' with version '%{version}' on lpp_source '%{source}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:105\nmsgid \"Unable to find package '%{package}' on lpp_source '%{source}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:138 ../lib/puppet/provider/package/nim.rb:142\nmsgid \"NIM package provider is unable to downgrade packages\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:214\nmsgid \"\"\n\"Unable to parse output from nimclient showres: line does not match expected package header format:\\n\"\n\"'%{line}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:221\nmsgid \"\"\n\"Unable to parse output from nimclient showres: package string does not match expected installp package string format:\\n\"\n\"'%{package_string}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:231\nmsgid \"\"\n\"Unable to parse output from nimclient showres: package string does not match expected rpm package string format:\\n\"\n\"'%{package_string}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:241\nmsgid \"\"\n\"Unable to parse output from nimclient showres: line does not match expected package line format:\\n\"\n\"'%{line}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/nim.rb:253\nmsgid \"\"\n\"Unrecognized package type specifier: '%{package_type_flag}' in package line:\\n\"\n\"'%{line}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/openbsd.rb:53 ../lib/puppet/provider/package/opkg.rb:28\nmsgid \"Failed to match line %{line}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/openbsd.rb:144\nmsgid \"No valid installpath found in /etc/pkg.conf and no source was set\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/openbsd.rb:148\nmsgid \"You must specify a package source or configure an installpath in /etc/pkg.conf\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/openbsd.rb:222\nmsgid \"%{version} is not available for this package\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:49\nmsgid \"Could not find package '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:85 ../lib/puppet/provider/package/xbps.rb:35\nmsgid \"Failed to match line '%{line}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:91 ../lib/puppet/provider/package/xbps.rb:42\nmsgid \"Error getting installed packages\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:172\nmsgid \"%{resource_name} is a group, but allow_virtual is false.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:209\nmsgid \"Refusing to uninstall package group %{resource_name}, because allow_virtual is false.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:238\nmsgid \"Invalid source '%{source}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:247\nmsgid \"puppet:// URL is not supported by pacman\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:249\nmsgid \"Source %{source} is not supported by pacman\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pacman.rb:258\nmsgid \"Refusing to install package group %{resource_name}, because allow_virtual is false.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pip.rb:58\nmsgid \"Cannot resolve pip version\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:55 ../lib/puppet/provider/package/pkg.rb:65\nmsgid \"Unknown format %{resource_name}: %{full_flags}[%{bad_flag}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:96\nmsgid \"Unknown format %{resource_name}: %{state}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:115\nmsgid \"Unknown line format %{resource_name}: %{parse_line}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:125\nmsgid \"Unable to unfreeze %{package}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:160\nmsgid \"Implicit version %{should} has %{n} possible matches\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:179\nmsgid \"Selecting version '%{version}' for implicit '%{should}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:184\nmsgid \"No version of %{name} matching %{should} is installable, even though the package is currently installed\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:203\nmsgid \"pkg warning: %{warnings}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:247\nmsgid \"Pkg could not install %{name} after %{tries} tries. Aborting run\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkg.rb:258 ../lib/puppet/provider/package/pkg.rb:285\nmsgid \"Unable to update %{package}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgdmg.rb:80\nmsgid \"Mac OS X PKG DMGs must specify a source string ending in .dmg or flat .pkg file\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgdmg.rb:110\nmsgid \"No disk entities returned by mount at %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgin.rb:44\nmsgid \"declared as absent but unavailable %{file}:%{line}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgin.rb:47\nmsgid \"No candidate to be installed\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgutil.rb:20\nmsgid \"It is highly recommended you create '/var/opt/csw/pkgutil/admin'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgutil.rb:21\nmsgid \"See /var/opt/csw/pkgutil\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgutil.rb:31\nmsgid \"It is highly recommended that you set 'wgetopts=-nv' in your pkgutil.conf.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/pkgutil.rb:93\nmsgid \"Package not in pkgutil catalog: %{package}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/portage.rb:225\nmsgid \"No package found with the specified name [%{name}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/portage.rb:229\nmsgid \"More than one package with the specified name [%{search_value}], please use the category parameter to disambiguate\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/ports.rb:48\nmsgid \"Could not match package info '%{pkgstuff}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/ports.rb:62\nmsgid \"Could not match version info '%{info}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/portupgrade.rb:156\nmsgid \"Unexpected output from portversion: %{output}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/rpm.rb:75\nmsgid \"Failed to list packages\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/rpm.rb:114 ../lib/puppet/provider/package/rpm.rb:127\nmsgid \"RPMs must specify a package source\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/sun.rb:82\nmsgid \"Unable to get information about package %{name} because of: %{errmsg}\"\nmsgstr \"\"\n\n#. TRANSLATORS Sun refers to the company name, do not translate\n#: ../lib/puppet/provider/package/sun.rb:100\nmsgid \"Sun packages must specify a package source\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/urpmi.rb:28\nmsgid \"Package %{name} was not present after trying to install it\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows.rb:44\nmsgid \"Error when unlinking %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows.rb:111\nmsgid \"The package %{operation}ed successfully and the system is rebooting now.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows.rb:113\nmsgid \"The package %{operation}ed successfully, but the system must be rebooted.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows.rb:115\nmsgid \"Failed to %{operation}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows.rb:121\nmsgid \"The source parameter cannot be empty when using the Windows provider.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows/exe_package.rb:81\nmsgid \"Error when installing %{package}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows/package.rb:64\nmsgid \"The source parameter is required when using the Windows provider.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows/package.rb:72\nmsgid \"The source does not exist: '%{source}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/windows/package.rb:77\nmsgid \"Don't know how to install '%{source}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/yum.rb:73\nmsgid \"The yum provider can only be used as root\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/yum.rb:129\nmsgid \"Could not check for updates, '%{cmd} check-update' exited with %{status}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/yum.rb:164\nmsgid \"Failed to parse package name and architecture from '%{pkgname}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package/yum.rb:317\nmsgid \"Could not find package %{wanted}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package_targetable.rb:58\nmsgid \"Provider %{name} package command is not functional on this host\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/package_targetable.rb:61\nmsgid \"Provider %{name} package command '%{cmd}' does not exist on this host\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/parsedfile.rb:56\nmsgid \"Invalid filetype %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/parsedfile.rb:104\nmsgid \"Failed to read %{target}'s records when prefetching them. Reason: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/parsedfile.rb:285 ../lib/puppet/provider/parsedfile.rb:309\nmsgid \"Prefetching %{target} for provider %{name} returned nil\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/parsedfile.rb:295\nmsgid \"Could not prefetch %{resource} provider '%{name}' target '%{target}': %{detail}. Treating as empty\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/parsedfile.rb:383\nmsgid \"Parsed Providers must define a default target\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/parsedfile.rb:475\nmsgid \"Somehow got told to prefetch with no resource set\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/base.rb:120\nmsgid \"%{name} is not running\"\nmsgstr \"\"\n\n#. TRANSLATORS 'plist' and label' should not be translated\n#: ../lib/puppet/provider/service/launchd.rb:151\nmsgid \"The %{file} plist does not contain a 'label' key; Puppet is skipping it\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/launchd.rb:212\nmsgid \"Reading overrides plist, attempt %{i}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/launchd.rb:215\nmsgid \"Unable to read overrides plist, too many attempts\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/launchd.rb:217\nmsgid \"Overrides file could not be read, trying again.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'runsvdir' is a linux service name and should not be translated\n#: ../lib/puppet/provider/service/runit.rb:89\nmsgid \"Waiting 5 seconds for runsvdir to discover service %{service}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/smf.rb:98\nmsgid \"Failed to get the FMRI of the %{service} service: The pattern '%{service}' matches multiple FMRIs! These are the FMRIs it matches: %{all_fmris}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/smf.rb:113\nmsgid \"Cannot query if the %{service} service is complete: The concept of complete/incomplete services was introduced in Solaris 11.1. You are on a Solaris %{release} machine.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/smf.rb:201\nmsgid \"The %{service} service is incomplete so its status will be reported as :stopped. See `svcs -xv %{fmri}` for more details.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:23\nmsgid \"Cannot enable %{resource_name}, error was: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:29\nmsgid \"Cannot disable %{resource_name}, error was: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:35\nmsgid \"Cannot enable %{resource_name} for manual start, error was: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:41\nmsgid \"Cannot enable %{resource_name} for delayed start, error was: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:59\nmsgid \"Unknown start type: %{start_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:62\nmsgid \"Cannot get start type %{resource_name}, error was: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:76\nmsgid \"Will not start disabled service %{resource_name} without managing enable. Specify 'enable => false' to override.\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/service/windows.rb:104\nmsgid \"Unknown service state '%{current_state}' for service '%{resource_name}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'AIX' is the name of an operating system and should not be translated\n#: ../lib/puppet/provider/user/aix.rb:71\nmsgid \"Could not convert AIX expires date '%{expires}' on %{class_name}[%{resource_name}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/user/aix.rb:87\nmsgid \"Invalid value %{groups}: Groups must be comma separated!\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/user/aix.rb:155\nmsgid \"Could not reset the groups property back to %{cur_groups} after setting the primary group on %{resource}[%{name}]. This means that the previous primary group of %{old_pgrp} and the new primary group of %{new_pgrp} have been added to %{cur_groups}. You will need to manually reset the groups property if this is undesirable behavior. Detail: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'ruby-shadow' is a Ruby gem library\n#: ../lib/puppet/provider/user/openbsd.rb:53\nmsgid \"ruby-shadow doesn't support %{method}\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/user/windows_adsi.rb:149\nmsgid \"The user account '%s' is disabled; The password will still be changed\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/user/windows_adsi.rb:151\nmsgid \"The user account '%s' is locked out; The password will still be changed\"\nmsgstr \"\"\n\n#: ../lib/puppet/provider/user/windows_adsi.rb:153\nmsgid \"The user account '%s' is expired; The password will still be changed\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/indirection.rb:27\nmsgid \"Could not build docs for indirector %{name}, terminus %{terminus}: could not locate terminus.\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/metaparameter.rb:30\nmsgid \"incorrect metaparams: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/providers.rb:60\nmsgid \"\"\n\"  - Missing files %{files}\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/providers.rb:64\nmsgid \"\"\n\"  - Setting %{name} (currently %{value}) not in list %{facts}\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/providers.rb:66\nmsgid \"\"\n\"  - Fact %{name} (currently %{value}) not in list %{facts}\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/providers.rb:70\nmsgid \"\"\n\"  - Got %{values} true tests that should have been false\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/providers.rb:72\nmsgid \"\"\n\"  - Got %{values} false tests that should have been true\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/providers.rb:74\nmsgid \"\"\n\"  - Missing features %{values}\\n\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/type.rb:79\nmsgid \"Could not retrieve property %{sname} on type %{type_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/reference/type.rb:83\nmsgid \"No docs for %{type}[%{sname}]\"\nmsgstr \"\"\n\n#. TRANSLATORS 'NONE' should not be translated\n#: ../lib/puppet/relationship.rb:31\nmsgid \"You must pass a callback for non-NONE events\"\nmsgstr \"\"\n\n#: ../lib/puppet/reports/http.rb:41\nmsgid \"Unable to submit report to %{url} [%{code}] %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/reports/store.rb:67\nmsgid \"Invalid node name %{host}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:54\nmsgid \"No resource type provided in serialized data\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:57\nmsgid \"No resource title provided in serialized data\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Puppet::Resource.new' should not be translated\n#: ../lib/puppet/resource.rb:303\nmsgid \"Puppet::Resource.new does not take a hash as the first argument.\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:304\nmsgid \"Did you mean (%{type}, %{title}) ?\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:347\nmsgid \"Could not find declared class %{title}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:349\nmsgid \"Invalid resource type %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:553\nmsgid \"no parameter named '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:597\nmsgid \"No title provided and %{type} is not a valid resource reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource.rb:672\nmsgid \"No set of title patterns matched the title \\\"%{title}\\\".\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:104\nmsgid \"Cannot add resource %{resource_1} before %{resource_2} because %{resource_2} is not yet in the catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:119\nmsgid \"Cannot add resource %{resource_1} after %{resource_2} because %{resource_2} is not yet in the catalog\"\nmsgstr \"\"\n\n#. TRANSLATORS 'alias' should not be translated\n#: ../lib/puppet/resource/catalog.rb:207\nmsgid \"Cannot alias %{resource} to %{key}; resource %{newref} already declared\"\nmsgstr \"\"\n\n#. TRANSLATORS 'alias' should not be translated\n#: ../lib/puppet/resource/catalog.rb:211\nmsgid \"Cannot alias %{resource} to %{key} at %{resource_declaration}; resource %{newref} already declared\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:293\nmsgid \"Unknown resource type %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:437\nmsgid \"Could not intern from data: Could not find relationship source %{source} for %{target}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:444\nmsgid \"Could not intern from data: Could not find relationship target %{target} for %{source}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:530\nmsgid \"Could not create class file %{file}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:547\nmsgid \"Could not create resource file %{file}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:582\nmsgid \"Duplicate declaration: %{resource} is already declared; cannot redeclare\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:584\nmsgid \"Duplicate declaration: %{resource} is already declared at %{error_location}; cannot redeclare\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/catalog.rb:633 ../lib/puppet/resource/catalog.rb:638\nmsgid \"Could not find resource %{resource} when converting %{message} resources\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/status.rb:141 ../lib/puppet/transaction.rb:273\nmsgid \"Could not evaluate: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:98\nmsgid \"Invalid resource supertype '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:129\nmsgid \"%{name} is not a class; cannot add code to it\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:130\nmsgid \"%{name} is not a class; cannot add code from it\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:135\nmsgid \"Cannot have code outside of a class/node/define because 'freeze_main' is enabled\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:139\nmsgid \"Cannot merge classes with different parent classes (%{name} => %{parent} vs. %{other_name} => %{other_parent})\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:170\nmsgid \"Cannot create resources for defined resource types\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:227\nmsgid \"Could not find parent resource type '%{parent}' of type %{parent_type} in %{env}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:358\nmsgid \"Parameter '%{name}' is given a type, but is not a valid parameter.\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:361\nmsgid \"Parameter '%{name}' is given a type that is not a Puppet Type, got %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:397\nmsgid \"Could not find scope for %{class_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:418\nmsgid \"%{param} is a metaparam; this value will inherit to all contained resources in the %{name} definition\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type.rb:420\nmsgid \"%{param} is a metaparameter; please choose another parameter name in the %{name} definition\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:66\nmsgid \"Class '%{klass}' is already defined%{error}; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:67\nmsgid \"Node '%{klass}' is already defined%{error}; cannot be redefined as a class\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:68\nmsgid \"Definition '%{klass}' is already defined%{error}; cannot be redefined as a class\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:100\nmsgid \"Node '%{name}' is already defined%{error}; cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:101\nmsgid \"Class '%{klass}' is already defined%{error}; cannot be redefined as a node\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:136\nmsgid \"'%{name}' is already defined%{error} as a class; cannot redefine as a definition\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:137\nmsgid \"Definition '%{name}' is already defined%{error}; cannot be redefined\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:181\nmsgid \"Execution of config_version command `%{cmd}` failed: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/resource/type_collection.rb:204\nmsgid \"Not attempting to load %{type} %{fqname} as this object was missing during a prior compilation\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:105\nmsgid \"New environment loaders generated from the requested section.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:322\nmsgid \"Attempting to initialize global default settings more than once!\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:520\nmsgid \"Using --configprint is deprecated. Use 'puppet config <subcommand>' instead.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:664\nmsgid \"Could not load %{file}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:773\nmsgid \"Invalid setting type '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:892\nmsgid \"Cannot manage owner permissions, because the provider for '%{name}' is not functional\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:908\nmsgid \"Cannot manage group permissions, because the provider for '%{name}' is not functional\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:944\nmsgid \"Unknown searchpath case: %{source_type} for the %{source} settings path element.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1025\nmsgid \"setting definition for '%{name}' is not a hash!\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1030\nmsgid \"Setting %{name} is already defined\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1037\nmsgid \"Setting %{name} is already using short name '%{short}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1309\nmsgid \"Setting %{name} is deprecated.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'puppet.conf' is a file name and should not be translated\n#: ../lib/puppet/settings.rb:1314\nmsgid \"Setting %{name} is deprecated in puppet.conf.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1500\nmsgid \"Error converting value for param '%{name}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1524\nmsgid \"Could not find value for %{expression}\"\nmsgstr \"\"\n\n#. TRANSLATORS '$environment' is a Puppet specific variable and should not be translated\n#: ../lib/puppet/settings.rb:1534\nmsgid \"You cannot interpolate $environment within '%{setting_name}' when using directory environments.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1535\nmsgid \"Its value will remain %{value}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings.rb:1566\nmsgid \"Attempt to assign a value to unknown setting %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/array_setting.rb:15 ../lib/puppet/settings/http_extra_headers_setting.rb:22\nmsgid \"Expected an Array or String, got a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/autosign_setting.rb:20\nmsgid \"Invalid autosign value %{value}: must be 'true'/'false' or an absolute path\"\nmsgstr \"\"\n\n#. TRANSLATORS ':%{name}', ':call_hook', and ':on_write_only' should not be translated\n#: ../lib/puppet/settings/base_setting.rb:40\nmsgid \"Setting :%{name} :call_hook is nil, defaulting to :on_write_only\"\nmsgstr \"\"\n\n#. TRANSLATORS 'call_hook' is a Puppet option name and should not be translated\n#: ../lib/puppet/settings/base_setting.rb:45\nmsgid \"Invalid option %{value} for call_hook\"\nmsgstr \"\"\n\n#. TRANSLATORS ':call_hook' and ':hook' are specific setting names and should not be translated\n#: ../lib/puppet/settings/base_setting.rb:105\nmsgid \"Cannot reference :call_hook for :%{name} if no :hook is defined\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/base_setting.rb:111\nmsgid \"%{class_name} (setting '%{setting}') does not accept %{parameter}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/base_setting.rb:118\nmsgid \"You must provide a description for the %{class_name} config option\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/base_setting.rb:132\nmsgid \"Short names can only be one character.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'deprecated' is a Puppet setting and ':completely' and ':allowed_on_commandline' are possible values and should not be translated\n#: ../lib/puppet/settings/base_setting.rb:203\nmsgid \"Unsupported deprecated value '%{deprecation}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/base_setting.rb:204\nmsgid \"Supported values for deprecated are ':completely' or ':allowed_on_commandline'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/boolean_setting.rb:27\nmsgid \"Invalid value '%{value}' for boolean parameter: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/certificate_revocation_setting.rb:19\nmsgid \"Invalid certificate revocation value %{value}: must be one of 'true', 'chain', 'leaf', or 'false'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:38\nmsgid \"Could not match line %{text}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:87\nmsgid \"Illegal section '%{name}' in config file at %{error_location}.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'puppet.conf' is the name of the puppet configuration file and should not be translated.\n#: ../lib/puppet/settings/config_file.rb:90\nmsgid \"The only valid puppet.conf sections are: [%{allowed_sections_list}].\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:92\nmsgid \"Please use the directory environments feature to specify environments.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:93\nmsgid \"(See https://puppet.com/docs/puppet/latest/environments_about.html)\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:133\nmsgid \"Invalid file option '%{parameter}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:137\nmsgid \"File modes must be numbers\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/config_file.rb:140\nmsgid \"Could not parse '%{string}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/duration_setting.rb:30\nmsgid \"Invalid duration format '%{value}' for parameter: %{name}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'disable_per_environment_manifest' is a setting and 'environment.conf' is a file name and should not be translated\n#: ../lib/puppet/settings/environment_conf.rb:71\nmsgid \"The 'disable_per_environment_manifest' setting is true, but the environment located at %{path_to_env} has a manifest setting in its environment.conf of '%{environment_conf}' which does not match the default_manifest setting '%{puppet_conf}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/environment_conf.rb:73\nmsgid \"If this environment is expecting to find modules in '%{environment_conf}', they will not be available!\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/environment_conf.rb:143\nmsgid \"Invalid sections in environment.conf at '%{path_to_conf_file}'. Environment conf may not have sections. The following sections are being ignored: '%{sections}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/environment_conf.rb:156\nmsgid \"Invalid settings in environment.conf at '%{path_to_conf_file}'. The following unknown setting(s) are being ignored: %{ignored_settings}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/file_setting.rb:205\nmsgid \"The %{parameter} parameter for the setting '%{name}' must be either 'root' or 'service', not '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/http_extra_headers_setting.rb:13\nmsgid \"Expected an Array, String, or Hash, got a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/integer_setting.rb:10\nmsgid \"Cannot convert '%{value}' to an integer for parameter: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/port_setting.rb:8\nmsgid \"Value '%{value}' is not a valid port number for parameter: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/priority_setting.rb:41\nmsgid \"Invalid priority format '%{value}' for parameter: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/server_list_setting.rb:26\nmsgid \"Expected an Array of String, got a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/terminus_setting.rb:13\nmsgid \"Invalid terminus setting: %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/ttl_setting.rb:39\nmsgid \"Invalid negative 'time to live' %{value} - did you mean 'unlimited'?\"\nmsgstr \"\"\n\n#: ../lib/puppet/settings/ttl_setting.rb:50\nmsgid \"Invalid 'time to live' format '%{value}' for parameter: %{param_name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/base.rb:28\nmsgid \"%{name} has not declared what class it wraps\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/base.rb:34 ../lib/puppet/x509/cert_provider.rb:389\nmsgid \"Certname %{name} must not contain unprintable or non-ASCII characters\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/base.rb:40\nmsgid \"%{class_name} did not override 'generate'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/base.rb:67\nmsgid \"Object must be an instance of %{class_name}, %{actual_class} given\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/base.rb:71\nmsgid \"Name must be supplied if it cannot be determined from the instance\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/base.rb:143\nmsgid \"Unknown signature algorithm '%{ln}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:61\nmsgid \"Creating a new SSL certificate request for %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:91\nmsgid \"CSR sign verification failed; you need to clean the certificate request for %{name} on the server\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:97\nmsgid \"Certificate Request fingerprint (%{digest}): %{hex_digest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:141\nmsgid \"CSR needs content to extract fields\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:168\nmsgid \"In %{attr}, expected extension record %{index} to have two or three items, but found %{count}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:218\nmsgid \"Cannot specify CSR attribute %{oid}: conflicts with internally used CSR attribute\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:227\nmsgid \"Cannot create CSR with attribute %{oid}: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:242\nmsgid \"Cannot specify CSR extension request %{oid}: conflicts with internally used extension request\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:248\nmsgid \"Cannot create CSR with extension request %{oid}: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:297\nmsgid \"In %{attr}, expected Set but found %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:301\nmsgid \"In %{attr}, expected Set[Array] but found %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:305\nmsgid \"In %{attr}, expected Set[Array] with one value but found %{count} elements\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:309\nmsgid \"In %{attr}, expected Set[Array[Sequence[...]]], but found %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request.rb:313\nmsgid \"In %{attr}, expected Set[Array[Sequence[Array[...]]]], but found %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request_attributes.rb:23\nmsgid \"csr_attributes file loading from %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request_attributes.rb:27\nmsgid \"invalid CSR attributes, expected instance of Hash, received instance of %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/certificate_request_attributes.rb:33\nmsgid \"unexpected attributes %{keys} in %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/error.rb:21\nmsgid \"expected one of %{certnames}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/error.rb:23\nmsgid \"expected %{certname}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/error.rb:26\nmsgid \"Server hostname '%{host}' did not match server certificate; %{expected_certnames}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/oids.rb:115\nmsgid \"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': %{err}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/oids.rb:119\nmsgid \"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': no such index '%{map_key}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/oids.rb:123\nmsgid \"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': data under index '%{map_key}' must be a Hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/oids.rb:130\nmsgid \"Error loading ssl custom OIDs mapping file from '%{custom_oid_file}': incomplete definition of oid '%{oid}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/oids.rb:163\nmsgid \"Error registering ssl custom OIDs mapping from file '%{custom_oid_file}': %{err}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:87\nmsgid \"Failed to add '%{path}' as a trusted CA file: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:91\nmsgid \"The 'ssl_trust_store' setting does not refer to a file and will be ignored: '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:149\nmsgid \"CA certs are missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:150\nmsgid \"CRLs are missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:151\nmsgid \"Private key is missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:152\nmsgid \"Client cert is missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:194\nmsgid \"Failed to load private key for host '%{name}': %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:208\nmsgid \"The CSR for host '%{name}' does not match the public key\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:221\nmsgid \"Verified client certificate '%{subject}' fingerprint %{digest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:223\nmsgid \"Verified CA certificate '%{subject}' fingerprint %{digest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:285\nmsgid \"Unsupported key '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:289\nmsgid \"The certificate for '%{name}' does not match its private key\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:309\nmsgid \"The issuer '%{issuer}' of certificate '%{subject}' cannot be found locally\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:325\nmsgid \"The certificate '%{subject}' is not yet valid, verify time is synchronized\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:327\nmsgid \"The certificate '%{subject}' has expired, verify time is synchronized\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:329\nmsgid \"The CRL issued by '%{issuer}' is not yet valid, verify time is synchronized\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:331\nmsgid \"The CRL issued by '%{issuer}' has expired, verify time is synchronized\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:333\nmsgid \"Invalid signature for certificate '%{subject}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:335\nmsgid \"Invalid signature for CRL issued by '%{issuer}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:337\nmsgid \"The issuer '%{issuer}' of certificate '%{subject}' is missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:341\nmsgid \"The CRL issued by '%{issuer}' is missing\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:343\nmsgid \"Certificate '%{subject}' is revoked\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/ssl_provider.rb:347\nmsgid \"Certificate '%{subject}' failed verification (%{err}): %{err_utf8}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:75\nmsgid \"Verified CA bundle with digest (%{digest_type}) %{actual_digest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:78\nmsgid \"CA bundle with digest (%{digest_type}) %{actual_digest} did not match expected digest %{expected_digest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:94\nmsgid \"CA certificate is missing from the server\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:96\nmsgid \"Could not download CA certificate: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:112\nmsgid \"Refreshing CA certificate\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:123\nmsgid \"CA certificate is unmodified, using existing CA certificate\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:125 ../lib/puppet/ssl/state_machine.rb:131\nmsgid \"Failed to refresh CA certificate, using existing CA certificate: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:192\nmsgid \"CRL is missing from the server\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:194\nmsgid \"Could not download CRLs: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:210\nmsgid \"Refreshing CRL\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:221\nmsgid \"CRL is unmodified, using existing CRL\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:223 ../lib/puppet/ssl/state_machine.rb:229\nmsgid \"Failed to refresh CRL, using existing CRL: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:256\nmsgid \"Loading/generating private key\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:277\nmsgid \"Creating a new RSA SSL key for %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:316\nmsgid \"Generating and submitting a CSR\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:327\nmsgid \"Failed to submit the CSR, HTTP response was %{code}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:336\nmsgid \"Downloading client certificate\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:342\nmsgid \"Downloaded certificate for %{name} from %{url}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:353\nmsgid \"Failed to parse certificate: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:356\nmsgid \"Certificate for %{certname} has not been signed yet\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:357\nmsgid \"Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}).\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:360\nmsgid \"Failed to retrieve certificate for %{certname}: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:370\nmsgid \"Renewing client certificate\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:383\nmsgid \"Renewed client certificate: %{cert_digest}, not before '%{not_before}', not after '%{not_after}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:388\nmsgid \"Certificate autorenewal has not been enabled on the server.\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:390\nmsgid \"Failed to automatically renew certificate: %{code} %{reason}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:394\nmsgid \"Unable to automatically renew certificate: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:409\nmsgid \"Exiting now because the waitforcert setting is set to 0.\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:412\nmsgid \"Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}). Exiting now because the maxwaitforcert timeout has been exceeded.\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:441\nmsgid \"Another puppet instance is already running and the waitforlock setting is set to 0; exiting\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:443\nmsgid \"Another puppet instance is already running and the maxwaitforlock timeout has been exceeded; exiting\"\nmsgstr \"\"\n\n#: ../lib/puppet/ssl/state_machine.rb:445\nmsgid \"Another puppet instance is already running; waiting for it to finish\"\nmsgstr \"\"\n\n#. TRANSLATORS: `error` is an untranslated message from openssl describing why a certificate in the server's chain is invalid, and `subject` is the identity/name of the failed certificate\n#: ../lib/puppet/ssl/verifier.rb:137\nmsgid \"certificate verify failed [%{error} for %{subject}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/base64.rb:19\nmsgid \"Base64 syntax checker: the text to check must be a String.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/base64.rb:20\nmsgid \"Base64 syntax checker: the syntax identifier must be a String, e.g. json, data+json\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/base64.rb:21\nmsgid \"Base64 syntax checker: invalid Acceptor, got: '%{klass}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/base64.rb:30\nmsgid \"Base64 syntax checker: Cannot parse invalid Base64 string - padding is not correct\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/base64.rb:32\nmsgid \"Base64 syntax checker: Cannot parse invalid Base64 string - contains letters outside strict base 64 range (or whitespace)\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/epp.rb:18\nmsgid \"EPP syntax checker: the text to check must be a String.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/epp.rb:19\nmsgid \"EPP syntax checker: the syntax identifier must be a String, e.g. pp\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/epp.rb:20\nmsgid \"EPP syntax checker: invalid Acceptor, got: '%{klass}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/epp.rb:26\nmsgid \"EPP syntax checker: \\\"%{message}\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/json.rb:18\nmsgid \"Json syntax checker: the text to check must be a String.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/json.rb:19\nmsgid \"Json syntax checker: the syntax identifier must be a String, e.g. json, data+json\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/json.rb:20\nmsgid \"Json syntax checker: invalid Acceptor, got: '%{klass}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/json.rb:26\nmsgid \"JSON syntax checker: Cannot parse invalid JSON string. \\\"%{message}\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/pp.rb:18\nmsgid \"PP syntax checker: the text to check must be a String.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/pp.rb:19\nmsgid \"PP syntax checker: the syntax identifier must be a String, e.g. pp\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/pp.rb:20\nmsgid \"PP syntax checker: invalid Acceptor, got: '%{klass}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/syntax_checkers/pp.rb:26\nmsgid \"PP syntax checker: \\\"%{message}\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:90\nmsgid \"Some pre-run checks failed\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:106\nmsgid \"Applying configuration version '%{version}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:130\nmsgid \"Provider %{name} is not functional on this host\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:146\nmsgid \"Could not find a suitable provider for %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:152\nmsgid \"post_resource_eval failed for provider %{provider}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:168\nmsgid \"resource is part of a dependency cycle\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:170\nmsgid \"One or more resource dependency cycles detected in graph\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:185\nmsgid \"Somehow left a component in the relationship graph\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:188\nmsgid \"Starting to evaluate the resource (%{progress} of %{total})\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:191\nmsgid \"Evaluated in %{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:310\nmsgid \"Class dependency %{dep} has failures: %{status}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:316\nmsgid \"Dependency %{dep} has failures: %{status}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:337\nmsgid \"Prefetch failed for %{type_name} provider '%{name}'\"\nmsgstr \"\"\n\n#. TRANSLATORS `prefetch` is a function name and should not be translated\n#: ../lib/puppet/transaction.rb:384\nmsgid \"Could not prefetch %{type_name} provider '%{name}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:418\nmsgid \"Skipping resources in class because of failed class dependencies\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:422\nmsgid \"Skipping because of failed dependencies\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction.rb:427\nmsgid \"Skipping because provider prefetch failed\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/additional_resource_generator.rb:28\nmsgid \"Failed to generate additional resources using 'generate': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/additional_resource_generator.rb:58\nmsgid \"Depthfirst resources are not supported by eval_generate\"\nmsgstr \"\"\n\n#. TRANSLATORS eval_generate is a method name and should be left untranslated\n#: ../lib/puppet/transaction/additional_resource_generator.rb:66\nmsgid \"Failed to generate additional resources using 'eval_generate': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/event.rb:126\nmsgid \"Event status can only be %{statuses}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/event_manager.rb:88\nmsgid \"Unscheduling all events on %{target}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/event_manager.rb:94\nmsgid \"Unscheduling %{callback} on %{target}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/event_manager.rb:109\nmsgid \"Scheduling %{callback} of %{target}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/event_manager.rb:153\nmsgid \"Triggered '%{callback}' from %{count} event\"\nmsgid_plural \"Triggered '%{callback}' from %{count} events\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: ../lib/puppet/transaction/event_manager.rb:160\nmsgid \"Failed to call %{callback}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/event_manager.rb:175\nmsgid \"Would have triggered '%{callback}' from %{count} event\"\nmsgid_plural \"Would have triggered '%{callback}' from %{count} events\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: ../lib/puppet/transaction/persistence.rb:80\nmsgid \"Transaction store file %{filename} is not a file, ignoring\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/persistence.rb:85\nmsgid \"Loaded transaction store file in %{seconds} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/persistence.rb:88\nmsgid \"Transaction store file %{filename} is corrupt (%{detail}); replacing\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/persistence.rb:93\nmsgid \"Unable to rename corrupt transaction store file: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/persistence.rb:94\nmsgid \"Could not rename corrupt transaction store file %{filename}; remove manually\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/persistence.rb:101\nmsgid \"Transaction store file %{filename} is valid YAML but not returning a hash. Check the file for corruption, or remove it before continuing.\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:54\nmsgid \"Cannot schedule without a schedule-containing catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:61\nmsgid \"Could not find schedule %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:153 ../lib/puppet/transaction/resource_harness.rb:161\nmsgid \"change from %s to %s failed: \"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:168\nmsgid \"could not create change error message for %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:221\nmsgid \"audit change: previously recorded value %s has been changed to %s\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:231\nmsgid \" (previously recorded value was %s)\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:239\nmsgid \"current_value %s, should be %s (noop)\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:251\nmsgid \"changed %s to %s\"\nmsgstr \"\"\n\n#: ../lib/puppet/transaction/resource_harness.rb:274\nmsgid \"audit change: newly-recorded value %s\"\nmsgstr \"\"\n\n#: ../lib/puppet/trusted_external.rb:9\nmsgid \"Retrieving trusted external data from %{command}\"\nmsgstr \"\"\n\n#: ../lib/puppet/trusted_external.rb:24\nmsgid \"Skipping non-executable file %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/trusted_external.rb:29\nmsgid \"There is more than one '%{basename}' script in %{dir}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:511\nmsgid \"Options must be a hash, not %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:514\nmsgid \"Class %{class_name} already has a property named %{property}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:603\nmsgid \"Class %{class_name} has not defined parameters\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:692\nmsgid \"Parameter %{name} failed on %{ref}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:713\nmsgid \"Undefined attribute '%{attribute}' in %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1154\nmsgid \"%{name} has no providers and has not overridden 'instances'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1417\nmsgid \"Cannot add aliases without a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1498\nmsgid \"Could not find %{description} %{ref} for %{resource}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1720\nmsgid \"Found multiple default providers for %{name}: %{provider_list}; using %{selected_provider}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1804\nmsgid \"Could not find parent provider %{parent} of %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1875\nmsgid \"Invalid %{resource} provider '%{provider_class}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:1960\nmsgid \"Could not find %{name} provider of %{provider}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:2078\nmsgid \"You cannot add relationships without a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:2395\nmsgid \"Unable to mark '%{name}' as sensitive: %{name} is a parameter and not a property, and cannot be automatically redacted.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:2398\nmsgid \"Unable to mark '%{name}' as sensitive: the property itself was not assigned a value.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:2400\nmsgid \"Unable to mark '%{name}' as sensitive: the property itself is not defined on %{type}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type.rb:2458\nmsgid \"Could not set %{attribute} on %{class_name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:122\nmsgid \"executed successfully\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:157\nmsgid \"Command exceeded timeout\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:186\nmsgid \"[command redacted] returned %{status} instead of one of [%{expected}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:188\nmsgid \"'%{cmd}' returned %{status} instead of one of [%{expected}]\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:218\nmsgid \"Command must be a String or Array<String>, got value of class %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:251\nmsgid \"Unable to execute commands as other users on Windows\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:253\nmsgid \"Only root can execute commands as other users\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:309\nmsgid \"Invalid environment setting '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:322\nmsgid \"The umask specification is invalid: %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:338\nmsgid \"The timeout must be a number.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:355\nmsgid \"Tries must be an integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:360\nmsgid \"Tries must be an integer >= 1\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:374\nmsgid \"try_sleep must be a number\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:379\nmsgid \"try_sleep cannot be a negative number\"\nmsgstr \"\"\n\n#. TRANSLATORS 'creates' is a parameter name and should not be translated\n#: ../lib/puppet/type/exec.rb:454\nmsgid \"Checking that 'creates' path '%{creates_path}' exists\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:506 ../lib/puppet/type/exec.rb:569\nmsgid \"Check %{value} exceeded timeout\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/exec.rb:672\nmsgid \"'%{cmd}' won't be executed because of failed check '%{check}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:62\nmsgid \"File paths must be fully qualified, not '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:141\nmsgid \"Invalid backup type %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:185 ../lib/puppet/type/tidy.rb:52\nmsgid \"Invalid recurse value %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:217\nmsgid \"Invalid recurselimit value %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:435\nmsgid \"You cannot specify more than one of %{creators}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:437\nmsgid \"You cannot specify a remote recursion without a source\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:439\nmsgid \"You cannot specify source when using checksum 'none'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:442\nmsgid \"You cannot specify content when using checksum '%{checksum_type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:445\nmsgid \"Possible error: recurselimit is set but not recurse, no recursion will happen\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:453\nmsgid \"Checksum value '%{value}' is not a valid checksum type %{checksum}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:456\nmsgid \"Checksum value is ignored unless content or source are specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:495\nmsgid \"Can not find filebucket for backups without a catalog\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:500\nmsgid \"Could not find filebucket %{backup} specified in backup\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:803\nmsgid \"Could not back up file of type %{current_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:818\nmsgid \"Could not remove files of type %{current_type}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"source_permissions => ignore\" should not be translated\n#: ../lib/puppet/type/file.rb:829\nmsgid \"Copying owner/mode/group from the source file on Windows is not supported; use source_permissions => ignore.\"\nmsgstr \"\"\n\n#. TRANSLATORS \"stat\" is a program name and should not be translated\n#: ../lib/puppet/type/file.rb:912 ../lib/puppet/type/tidy.rb:379\nmsgid \"Could not stat; permission denied\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:915\nmsgid \"Could not stat; invalid pathname\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:1040\nmsgid \"Not removing directory; use 'force' to override\"\nmsgstr \"\"\n\n#. TRANSLATORS refers to a file which could not be backed up\n#: ../lib/puppet/type/file.rb:1065\nmsgid \"Could not back up; will not remove\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file.rb:1080\nmsgid \"File written to disk did not match desired checksum; discarding changes (%{content_checksum} vs %{desired_checksum})\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file/checksum.rb:24\nmsgid \"MD5 is not supported in FIPS mode\"\nmsgstr \"\"\n\n#. TRANSLATORS \"content\" is an attribute and should not be translated\n#: ../lib/puppet/type/file/content.rb:55\nmsgid \"Using a checksum in a file's \\\"content\\\" property is deprecated.\"\nmsgstr \"\"\n\n#. TRANSLATORS \"filebucket\" is a resource type and should not be translated. The quoted occurrence of \"content\" is an attribute and should not be translated.\n#: ../lib/puppet/type/file/content.rb:57\nmsgid \"The ability to use a checksum to retrieve content from the filebucket using the \\\"content\\\" property will be removed in a future release.\"\nmsgstr \"\"\n\n#. TRANSLATORS \"content\" is an attribute and should not be translated.\n#: ../lib/puppet/type/file/content.rb:59\nmsgid \"The literal value of the \\\"content\\\" property will be written to the file.\"\nmsgstr \"\"\n\n#. TRANSLATORS \"static catalogs\" should not be translated.\n#: ../lib/puppet/type/file/content.rb:61\nmsgid \"The checksum retrieval functionality is being replaced by the use of static catalogs.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file/content.rb:62\nmsgid \"See https://puppet.com/docs/puppet/latest/static_catalogs.html for more information.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Ensure' is an attribute and ':present' is a value and should not be translated\n#: ../lib/puppet/type/file/data_sync.rb:33\nmsgid \"Ensure set to :present but file type is %{file_type} so no content will be synced\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/file/mode.rb:134\nmsgid \"Not managing symlink mode\"\nmsgstr \"\"\n\n#. TRANSLATORS \"source_permissions\" is a parameter name and should not be translated\n#: ../lib/puppet/type/file/source.rb:374\nmsgid \"The `source_permissions` parameter is deprecated. Explicitly set `owner`, `group`, and `mode`.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/filebucket.rb:75\nmsgid \"You can only have one filebucket path\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/filebucket.rb:79\nmsgid \"Filebucket paths must be absolute\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/filebucket.rb:115\nmsgid \"Could not create %{type} filebucket: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/group.rb:60\nmsgid \"GID cannot be deleted\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/group.rb:72\nmsgid \"Invalid GID %{gid}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:126 ../lib/puppet/type/package.rb:140\nmsgid \"Could not update: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:175\nmsgid \"Could not get latest version: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:274\nmsgid \"Name must be a String not %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:421\nmsgid \"Cannot have both `ensure => disabled` and `flavor`\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:520\nmsgid \"Cannot have both `enable_only => true` and `flavor`\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:523\nmsgid \"Cannot have both `ensure => disabled` and `enable_only => true`\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:684\nmsgid \"Invalid hold value %{value}. %{doc}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/package.rb:711\nmsgid \"You cannot use \\\"mark\\\" property while \\\"ensure\\\" is one of [\\\"absent\\\", \\\"purged\\\"]\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/resources.rb:18\nmsgid \"Could not find resource type '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/resources.rb:36\nmsgid \"Purging resources of type %{res_type} is not supported, since they cannot be queried from the system\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/resources.rb:38\nmsgid \"Purging is only supported on types that accept 'ensure'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/resources.rb:60\nmsgid \"Invalid value %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/resources.rb:112\nmsgid \"The 'ensure' attribute on %{name} resources does not accept 'absent' as a value\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/resources.rb:136\nmsgid \"Could not find resource type\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:105\nmsgid \"Invalid range value '%{value}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:122\nmsgid \"Invalid range %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:131\nmsgid \"Invalid hour '%{n}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:135\nmsgid \"Invalid minute '%{n}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:284\nmsgid \"Repeat must be a number\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:293\nmsgid \"Repeat must be 1 unless periodmatch is 'distance', not '%{period}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/schedule.rb:337\nmsgid \"%{value} is not a valid day of the week\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/service.rb:153 ../lib/puppet/type/user.rb:277\nmsgid \"Passwords cannot include ':'\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/service.rb:285\nmsgid \"\\\"%{value}\\\" is not a positive integer: the timeout parameter must be specified as a positive integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/service.rb:306\nmsgid \"The 'logonaccount' parameter is mandatory when setting 'logonpassword'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/tidy.rb:105\nmsgid \"Tidy can't use matches with recurse 0, false, or undef\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/tidy.rb:146\nmsgid \"Invalid age unit '%{unit}'\"\nmsgstr \"\"\n\n#. TRANSLATORS tidy is the name of a program and should not be translated\n#: ../lib/puppet/type/tidy.rb:166\nmsgid \"Invalid tidy age %{age}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/tidy.rb:188\nmsgid \"Invalid size unit '%{unit}'\"\nmsgstr \"\"\n\n#. TRANSLATORS tidy is the name of a program and should not be translated\n#: ../lib/puppet/type/tidy.rb:206\nmsgid \"Invalid tidy size %{age}\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Tidy\" is a program name and should not be translated\n#: ../lib/puppet/type/tidy.rb:300\nmsgid \"Tidying %{count} files\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/tidy.rb:375\nmsgid \"File does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:183\nmsgid \"Could not find group(s) %{groups}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:297\nmsgid \"Password minimum age must be provided as a number.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:316\nmsgid \"Password maximum age must be provided as a number.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:347\nmsgid \"Group names must be provided, not GID numbers.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:349\nmsgid \"Group names must be provided as an array, not a comma-separated list.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:350\nmsgid \"Group names must not be empty. If you want to specify \\\"no groups\\\" pass an empty array\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:443\nmsgid \"User provider %{name} can not manage home directories\"\nmsgstr \"\"\n\n#. TRANSLATORS YYYY-MM-DD represents a date with a four-digit year, a two-digit month, and a two-digit day,\n#. TRANSLATORS separated by dashes.\n#: ../lib/puppet/type/user.rb:460\nmsgid \"Expiry dates must be YYYY-MM-DD or the string \\\"absent\\\"\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:527\nmsgid \"Role names must be provided, not numbers\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:529\nmsgid \"Role names must be provided as an array, not a comma-separated list\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:566\nmsgid \"Auth names must be provided, not numbers\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:568\nmsgid \"Auth names must be provided as an array, not a comma-separated list\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:592\nmsgid \"Profile names must be provided, not numbers\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:594\nmsgid \"Profile names must be provided as an array, not a comma-separated list\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:705\nmsgid \"Ssh_authorized_key type is not available. Cannot purge SSH keys.\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:743\nmsgid \"Each entry for purge_ssh_keys must be a string, not a %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:746\nmsgid \"Paths to keyfiles must be absolute, not %{entry}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:750\nmsgid \"purge_ssh_keys must be true, false, or an array of file names, not %{value}\"\nmsgstr \"\"\n\n#: ../lib/puppet/type/user.rb:798\nmsgid \"Class name must be provided.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:113\nmsgid \"could not change to group %{group}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:114\nmsgid \"could not change to group %{group}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:127\nmsgid \"Could not change to user %{user}: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'benchmark' is a method name and should not be translated\n#: ../lib/puppet/util.rb:181\nmsgid \"Failed to provide level to benchmark\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:184\nmsgid \"Benchmarked object does not respond to %{value}\"\nmsgstr \"\"\n\n#. TRANSLATORS PATH and HOME are environment variables and should not be translated\n#: ../lib/puppet/util.rb:223\nmsgid \"PATH contains a ~ character, and HOME is not set; ignoring PATH element '%{dir}'.\"\nmsgstr \"\"\n\n#. TRANSLATORS PATH is an environment variable and should not be translated\n#: ../lib/puppet/util.rb:227\nmsgid \"Couldn't expand PATH containing a ~ character; ignoring PATH element '%{dir}'.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:273\nmsgid \"unknown platform %{platform} in absolute_path\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:308\nmsgid \"Failed to convert '%{path}' to URI: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:408\nmsgid \"path may not be nil\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:595\nmsgid \"replace_file requires a block\"\nmsgstr \"\"\n\n#: ../lib/puppet/util.rb:599\nmsgid \"replace_file default_mode: %{default_mode} is invalid\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork.rb:24\nmsgid \"Failed to load Solaris implementation of the Puppet::Util::AtFork handler. Child process contract management will be unavailable, which means that agent runs executed by the puppet agent service will be killed when they attempt to restart the service.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork/solaris.rb:10\nmsgid \"The loaded Fiddle version is not supported.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork/solaris.rb:84\nmsgid \"Failed to activate a new process contract template\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork/solaris.rb:97\nmsgid \"Failed to deactivate process contract template in the parent process\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork/solaris.rb:99\nmsgid \"Failed to deactivate process contract template in the child process\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork/solaris.rb:123\nmsgid \"Failed to get latest child process contract id\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/at_fork/solaris.rb:140\nmsgid \"Failed to abandon a child process contract\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/autoload.rb:88\nmsgid \"Could not autoload %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/autoload.rb:182\nmsgid \"Autoload paths cannot be fully qualified\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/backups.rb:28\nmsgid \"Recursively backing up to filebucket\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/backups.rb:51\nmsgid \"Could not back %{file} up: %{message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/backups.rb:69\nmsgid \"Will not remove directory backup %{newfile}; use a filebucket\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/backups.rb:72\nmsgid \"Removing old backup of type %{file_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/backups.rb:77\nmsgid \"Could not remove old backup: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/backups.rb:85\nmsgid \"Filebucketed %{f} to %{filebucket} with sum %{sum}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/character_encoding.rb:25\nmsgid \"%{value} is already labeled as UTF-8 but this encoding is invalid. It cannot be transcoded by Puppet.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/character_encoding.rb:47\nmsgid \"%{error}: %{value} cannot be transcoded by Puppet.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/character_encoding.rb:76\nmsgid \"%{value} is not valid UTF-8 and result of overriding encoding would be invalid.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/classgen.rb:152\nmsgid \"Redefining %{name} in %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/classgen.rb:156\nmsgid \"Class %{const} is already defined in %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/classgen.rb:204 ../lib/puppet/util/classgen.rb:217\nmsgid \"Already a generated class named %{klassname}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line.rb:73\nmsgid \"Could not initialize global default settings\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line.rb:175\nmsgid \"Error: Could not parse application options: invalid option: %{opt}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line.rb:178\nmsgid \"See 'puppet help' for help on available puppet subcommands\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line.rb:191\nmsgid \"Error: Unknown Puppet subcommand '%{cmd}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/puppet_option_parser.rb:49\nmsgid \"this method only takes 3 or 4 arguments. Given: %{args}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/puppet_option_parser.rb:66\nmsgid \"Unsupported type: '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/puppet_option_parser.rb:78\nmsgid \"Error parsing arguments\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:149\nmsgid \"you already have an argument named '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:168 ../lib/puppet/util/command_line/trollop.rb:172\nmsgid \"unsupported argument type '%{type}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:198\nmsgid \"multiple argument type cannot be deduced from an empty array for '%{value0}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:208\nmsgid \"unsupported multiple argument type '%{value0}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:212\nmsgid \"unsupported argument type '%{value0}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:215\nmsgid \":type specification and default type don't match (default type is %{type_from_default})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:228\nmsgid \"invalid long option name %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:230\nmsgid \"long option name %{value0} is already taken; please specify a (different) :long\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:239\nmsgid \"invalid short option name '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:243\nmsgid \"short option name %{value0} is already taken; please specify a (different) :short\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:244\nmsgid \"a short option name can't be a number or a dash\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:277 ../lib/puppet/util/command_line/trollop.rb:283\nmsgid \"unknown option '%{sym}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:318\nmsgid \"Print version and exit\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:320\nmsgid \"Show this message\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:342 ../lib/puppet/util/command_line/trollop.rb:353\nmsgid \"Partial argument match detected: correct argument is %{partial_match}, got %{arg}. Partial argument matching is deprecated and will be removed in a future release.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:360\nmsgid \"invalid argument syntax: '%{arg}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:365\nmsgid \"unknown argument '%{arg}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:369\nmsgid \"option '%{arg}' specified multiple times\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:406\nmsgid \"--%{value0} requires --%{value1}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:408\nmsgid \"--%{value0} conflicts with --%{value1}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:413\nmsgid \"option --%{opt} must be specified\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:422\nmsgid \"option '%{arg}' needs a parameter\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:481\nmsgid \"option '%{arg}' needs a date\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:513\nmsgid \"Options:\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:537\nmsgid \" (Default: %{default_s})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:539\nmsgid \" (default: %{default_s})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:576\nmsgid \"Error: argument --%{value0} %{msg}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:578\nmsgid \"Error: %{arg}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:580 ../lib/puppet/util/command_line/trollop.rb:806\nmsgid \"Try --help for help.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:659\nmsgid \"option '%{arg}' needs an integer\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:665\nmsgid \"option '%{arg}' needs a floating-point number\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:678\nmsgid \"file or url for option '%{arg}' cannot be opened: %{value0}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/command_line/trollop.rb:805\nmsgid \"Error: %{value0}.\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Trollop' is the name of a module and 'die' and 'options' are methods in it and should not be translated.\n#: ../lib/puppet/util/command_line/trollop.rb:839\nmsgid \"Trollop::die can only be called after Trollop::options\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/diff.rb:32\nmsgid \"Cannot provide diff without the diff/lcs Ruby library\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:49\nmsgid \"(file: %{file}, line: %{line}, column: %{column})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:51\nmsgid \"(file: %{file}, line: %{line})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:53\nmsgid \"(line: %{line}, column: %{column})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:55\nmsgid \"(line: %{line})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:57\nmsgid \"(file: %{file})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:90 ../lib/puppet/util/errors.rb:91\nmsgid \"unknown\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/errors.rb:121\nmsgid \"%{klass} failed with error %{error_type}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/execution.rb:188\nmsgid \"Working directory %{cwd} does not exist!\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/execution.rb:303\nmsgid \"Could not get output\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/execution.rb:312\nmsgid \"Execution of '%{str}' returned %{exit_status}: %{output}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/execution.rb:394\nmsgid \"Could not execute posix command: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/execution.rb:439\nmsgid \"Waiting for output; will sleep %{time_to_sleep} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/feature.rb:91\nmsgid \"Failed to load feature test for %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/feature.rb:110\nmsgid \"Libraries must be passed as strings not %{klass}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/feature.rb:120\nmsgid \"Could not find library '%{lib}' required to enable feature '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/feature.rb:124\nmsgid \"Exception occurred while loading library '%{lib}' required to enable feature '%{name}': %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:45\nmsgid \"Cannot have fields named %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:66 ../lib/puppet/util/fileparsing.rb:338\nmsgid \"Invalid record type %{record_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:114\nmsgid \"Field '%{field}' is required\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:179\nmsgid \"Process record type %{record_name} returned non-hash\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:259\nmsgid \"Could not parse line %{line}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:268\nmsgid \"No record types defined; cannot parse lines\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:280\nmsgid \"Somehow got invalid line type %{record_type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:302\nmsgid \"Must include a list of fields\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:317\nmsgid \"You must provide a :match regex for text lines\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/fileparsing.rb:392\nmsgid \"Line type %{name} is already defined\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:50\nmsgid \"%{klass} could not read %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:64\nmsgid \"%{klass} could not write %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:81\nmsgid \"Path is nil\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:152\nmsgid \"Reading %{path} from RAM\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:158\nmsgid \"Removing %{path} from RAM\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:164\nmsgid \"Writing %{path} to RAM\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:185\nmsgid \"Could not retrieve user %{user}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:196 ../lib/puppet/util/filetype.rb:261 ../lib/puppet/util/filetype.rb:312\nmsgid \"The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:207 ../lib/puppet/util/filetype.rb:272 ../lib/puppet/util/filetype.rb:323\nmsgid \"The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:211 ../lib/puppet/util/filetype.rb:276 ../lib/puppet/util/filetype.rb:327\nmsgid \"Could not read crontab for %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:233\nmsgid \"Cannot write the %{path} user's crontab: The user does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:284 ../lib/puppet/util/filetype.rb:335\nmsgid \"Could not remove crontab for %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/filetype.rb:299 ../lib/puppet/util/filetype.rb:351\nmsgid \"Could not write crontab for %{path}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/inifile.rb:140\nmsgid \"Cannot read nonexistent file %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/inifile.rb:187\nmsgid \"Property with key %{key} outside of a section\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/inifile.rb:193\nmsgid \"Can't parse line '%{line}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/inifile.rb:242\nmsgid \"Section %{name} is already defined, cannot redefine\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/instance_loader.rb:59\nmsgid \"Loaded %{type} file for %{name} but %{type} was not defined\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/json_lockfile.rb:44\nmsgid \"Unable to read lockfile data from %{path}: not in JSON\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/ldap/connection.rb:39\nmsgid \"Could not set up LDAP Connection: Missing ruby/ldap libraries\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/ldap/connection.rb:73\nmsgid \"Could not connect to LDAP: %{detail}\"\nmsgstr \"\"\n\n#. TRANSLATORS '#connect' is a method name and and should not be translated, 'block' refers to a Ruby code block\n#: ../lib/puppet/util/ldap/manager.rb:50\nmsgid \"You must pass a block to #connect\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/ldap/manager.rb:94\nmsgid \"Could not get dn from ldap entry\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/ldap/manager.rb:145\nmsgid \"%{source} must be defined to generate %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/ldap/manager.rb:222\nmsgid \"Removing %{name} from ldap\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/ldap/manager.rb:229\nmsgid \"Creating %{name} in ldap\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/limits.rb:12\nmsgid \"Failed to set process priority to '%{priority}'\"\nmsgstr \"\"\n\n#. TRANSLATORS \"Log.close_all\" is a method name and should not be translated\n#: ../lib/puppet/util/log.rb:67\nmsgid \"Log.close_all failed to close %{destinations}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:86\nmsgid \"Logs require a level\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:87 ../lib/puppet/util/log.rb:392 ../lib/puppet/util/windows/eventlog.rb:102\nmsgid \"Invalid log level %{level}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:112\nmsgid \"Invalid loglevel %{level}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:142\nmsgid \"Unknown destination type %{dest}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:182\nmsgid \"Received a Log attribute with invalid encoding:%{log_message}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:184\nmsgid \"\"\n\"Backtrace:\\n\"\n\"%{backtrace}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:239\nmsgid \"Reopening log files\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class\n#: ../lib/puppet/util/log.rb:380\nmsgid \"Puppet::Util::Log requires a message\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class\n#: ../lib/puppet/util/log.rb:387\nmsgid \"Puppet::Util::Log requires a log level\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class\n#: ../lib/puppet/util/log.rb:389\nmsgid \"Puppet::Util::Log requires a symbol or string\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:421\nmsgid \"Could not parse for environment %{environment}: %{msg}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log.rb:423\nmsgid \"%{msg} on node %{node}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log/destinations.rb:23\nmsgid \"Invalid syslog facility %{str}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/log/destinations.rb:76\nmsgid \"Creating log directory %{dir}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/logging.rb:82 ../lib/puppet/util/logging.rb:106\nmsgid \"Wrapped exception:\"\nmsgstr \"\"\n\n#. TRANSLATORS the literals \":file\", \":line\", and \":key\" should not be translated\n#: ../lib/puppet/util/logging.rb:163\nmsgid \"Need either :file and :line, or :key\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/logging.rb:194\nmsgid \"(file & line not available)\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/logging.rb:275\nmsgid \"(location: %{location})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/network_device.rb:12\nmsgid \"Can't load %{provider} for %{device}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/network_device/config.rb:59\nmsgid \"Duplicate device found at %{file_error_location}, already found at %{device_error_location}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/network_device/config.rb:72\nmsgid \"Invalid entry at %{error_location}: %{file_text}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/network_device/config.rb:78\nmsgid \"Configuration error: Cannot read %{file}; cannot serve\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/network_device/config.rb:81\nmsgid \"Configuration error: '%{file}' does not exit; cannot serve\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/network_device/config.rb:95\nmsgid \"%{value} is an invalid url\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/package/version/rpm.rb:42\nmsgid \"Cannot compare, as %{other} is not a Rpm Version\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/plist.rb:49\nmsgid \"Cannot read file %{file_path}; Puppet is skipping it.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/plist.rb:50\nmsgid \"Details: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/plist.rb:152\nmsgid \"Unable to write the file %{file_path}. %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/posix.rb:30\nmsgid \"Removing any duplicate group entries\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/posix.rb:77\nmsgid \"Did not get id from caller\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/posix.rb:81 ../lib/puppet/util/posix.rb:106\nmsgid \"Tried to get %{field} field for silly id %{id}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/posix.rb:134 ../lib/puppet/util/posix.rb:144 ../lib/puppet/util/posix.rb:154\nmsgid \"Can only handle users and groups\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/profiler/wall_clock.rb:17\nmsgid \"took %{context} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/provider_features.rb:65\nmsgid \"Feature %{name} is already defined\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/provider_features.rb:72\nmsgid \"Could not create feature %{name}: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/rdoc.rb:38 ../lib/puppet/util/rdoc.rb:44 ../lib/puppet/util/rdoc.rb:48 ../lib/puppet/util/rdoc.rb:52\nmsgid \"RDOC SUPPORT FOR MANIFEST HAS BEEN REMOVED - See PUP-3638\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/reference.rb:30\nmsgid \"Could not find section %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/reference.rb:65\nmsgid \"%{name} Reference\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/resource_template.rb:49\nmsgid \"Template %{file} does not exist\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/retry_action.rb:33\nmsgid \"%{retries} exceeded\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/retry_action.rb:36\nmsgid \"Caught exception %{klass}:%{error} retrying\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:74\nmsgid \"Cannot get default context with nil handle\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:94\nmsgid \"Invalid context to parse: %{context}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:107\nmsgid \"Invalid SELinux parameter type\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:128\nmsgid \"Can't set SELinux context on file unless the file already has some kind of context\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:142\nmsgid \"set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:153\nmsgid \"Failed to set SELinux context %{context} on %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:206\nmsgid \"Could not open SELinux category translation file %{path}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/selinux.rb:310\nmsgid \"got a relative path in SELinux find_fs: %{path}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/splayer.rb:16\nmsgid \"Sleeping for %{time} seconds (splay is enabled)\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/storage.rb:54\nmsgid \"Checksumfile %{filename} is not a file, ignoring\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/storage.rb:60\nmsgid \"Checksumfile %{filename} is corrupt (%{detail}); replacing\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/storage.rb:65\nmsgid \"Could not rename corrupt %{filename}; remove manually\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/storage.rb:70\nmsgid \"State got corrupted\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/storage.rb:82\nmsgid \"Creating state file %{file}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/suidmanager.rb:103\nmsgid \"No such group %{group}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/suidmanager.rb:119\nmsgid \"No such user %{user}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/suidmanager.rb:146\nmsgid \"Invalid id type %{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/suidmanager.rb:150\nmsgid \"Invalid %{klass}: %{id}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:53\nmsgid \"An empty mode string is illegal\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:57\nmsgid \"Numeric modes must be in octal, not decimal!\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:60\nmsgid \"non-numeric current mode (%{mode})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:79\nmsgid \"Missing action\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:95\nmsgid \"Missing operation (-, =, or +)\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:117\nmsgid \"X only works with the '+' operator\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:128\nmsgid \"internal error\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:132\nmsgid \"Unknown operation\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/symbolic_file_mode.rb:146\nmsgid \"%{error}%{rest} in symbolic mode %{modification}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/tagging.rb:29\nmsgid \"Invalid tag '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:35\nmsgid \"ADSI connection error: %{e}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:57\nmsgid \"Failed to get computer name\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:96\nmsgid \"Must use a valid SID::Principal\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:161\nmsgid \"Value must be in DOMAIN\\\\%{object_class} style syntax\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:194\nmsgid \"Could not resolve name: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:238\nmsgid \"Subclass must implement class-level method 'list_all'!\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:291\nmsgid \"Puppet is not able to create/delete domain %{object_class} objects with the %{object_class} resource.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:294\nmsgid \"%{object_class} update failed: %{error}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:320\nmsgid \"Cannot create user if group '%{name}' exists.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:468\nmsgid \"Unrecognized ADS UserFlags: %{unrecognized_flags}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:513 ../lib/puppet/util/windows/adsi.rb:547\nmsgid \"Failed to get user name\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:596\nmsgid \"Cannot delete user profile for '%{sid}' prior to Vista SP1\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/adsi.rb:612\nmsgid \"Cannot create group if user '%{name}' exists.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/com.rb:19\nmsgid \"%{name} failed (hresult %{result}).\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/com.rb:145 ../lib/puppet/util/windows/com.rb:187\nmsgid \"Failed to call %{klass}::%{name} with HRESULT: %{result}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/com.rb:173\nmsgid \"CoCreateInstance failed (%{klass}).\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/error.rb:45\nmsgid \"FormatMessageW could not format code %{code}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/error.rb:51\nmsgid \"FormatMessageW failed to allocate buffer for code %{code}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Windows' is the operating system and 'RegisterEventSourceW' is a API call and should not be translated\n#: ../lib/puppet/util/windows/eventlog.rb:37\nmsgid \"RegisterEventSourceW failed to open Windows eventlog\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/eventlog.rb:62\nmsgid \"data must be a string, not %{class_name}\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Windows' is the operating system and 'ReportEventW' is a API call and should not be translated\n#: ../lib/puppet/util/windows/eventlog.rb:79\nmsgid \"ReportEventW failed to report event to Windows eventlog\"\nmsgstr \"\"\n\n#. TRANSLATORS 'Win32' is the Windows API and should not be translated\n#: ../lib/puppet/util/windows/eventlog.rb:122\nmsgid \"(Win32 error: %{detail})\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/file.rb:120\nmsgid \"Failed to set file attributes\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/file.rb:183\nmsgid \"out_buffer is required\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/file.rb:257\nmsgid \"Failed to call GetLongPathName\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/principal.rb:69 ../lib/puppet/util/windows/principal.rb:76\nmsgid \"Failed to call LookupAccountNameW with account: %{account_name}\"\nmsgstr \"\"\n\n#. TRANSLATORS `lookup_account_sid` is a variable name and should not be translated\n#: ../lib/puppet/util/windows/principal.rb:100\nmsgid \"Byte array for lookup_account_sid must not be nil and must be at least 1 byte long\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/principal.rb:116\nmsgid \"Byte array for lookup_account_sid is invalid: %{sid_bytes}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/principal.rb:124 ../lib/puppet/util/windows/principal.rb:131\nmsgid \"Failed to call LookupAccountSidW with bytes: %{sid_bytes}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/process.rb:48\nmsgid \"Failed to get child process exit code\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/process.rb:283\nmsgid \"GetVersionEx failed\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/process.rb:312\nmsgid \"Discarding environment variable %{string} which contains invalid bytes\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/process.rb:328\nmsgid \"environment variable name must not be nil or empty\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/process.rb:333\nmsgid \"Failed to remove environment variable: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/process.rb:338\nmsgid \"Failed to set environment variable: %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:25\nmsgid \"Invalid registry key '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:35\nmsgid \"Failed to open registry key '%{key}\\\\%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:136\nmsgid \"Failed to enumerate %{key} registry keys at index %{index}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:171\nmsgid \"Failed to enumerate %{key} registry values at index %{index}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:199\nmsgid \"Failed to query registry %{key} for sizes\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:235\nmsgid \"Type mismatch (expect %{rtype} but %{type} present)\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:257\nmsgid \"Type %{type} is not supported.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:263\nmsgid \"A value in the registry key %{parent_key_name}%{key} is corrupt or invalid\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:318\nmsgid \"Failed to read registry value %{value} at %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:336\nmsgid \"Failed to delete registry value %{name} at %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/registry.rb:351\nmsgid \"Failed to delete registry key %{name} at %{key}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/root_certs.rb:49\nmsgid \"Failed to import root certificate: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:176\nmsgid \"Failed to get volume information\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/util/windows/security.rb:343\nmsgid \"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\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/util/windows/security.rb:346\nmsgid \"%{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\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/util/windows/security.rb:349\nmsgid \"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\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/util/windows/security.rb:359\nmsgid \"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\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/util/windows/security.rb:362\nmsgid \"%{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\"\nmsgstr \"\"\n\n#. TRANSLATORS 'SYSTEM' is a Windows name and should not be translated\n#: ../lib/puppet/util/windows/security.rb:365\nmsgid \"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\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:436 ../lib/puppet/util/windows/security.rb:453 ../lib/puppet/util/windows/sid.rb:163 ../lib/puppet/util/windows/sid.rb:224 ../lib/puppet/util/windows/user.rb:60\nmsgid \"Invalid SID\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:440 ../lib/puppet/util/windows/security.rb:457\nmsgid \"Failed to add access control entry\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:468 ../lib/puppet/util/windows/security.rb:649\nmsgid \"Invalid DACL\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:490\nmsgid \"Unsupported access control entry type: 0x%{type}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:525\nmsgid \"Failed to open '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:572\nmsgid \"Failed to adjust process privileges\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:602\nmsgid \"Failed to get security information\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:612\nmsgid \"Failed to get security descriptor control\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:645\nmsgid \"Failed to initialize ACL\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/security.rb:684\nmsgid \"Failed to set security information\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:42\nmsgid \"Starting the %{service_name} service. Timeout set to: %{timeout} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:52\nmsgid \"Failed to start the service\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:56\nmsgid \"Successfully started the %{service_name} service\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:65\nmsgid \"Stopping the %{service_name} service. Timeout set to: %{timeout} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:73\nmsgid \"Successfully stopped the %{service_name} service\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:82\nmsgid \"Resuming the %{service_name} service. Timeout set to: %{timeout} seconds\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:98\nmsgid \"Successfully resumed the %{service_name} service\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:114\nmsgid \"Unknown Service state '%{current_state}' for '%{service_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:142\nmsgid \"Unknown start type '%{start_type}' for '%{service_name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:193\nmsgid \"Failed to update service configuration\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:258\nmsgid \"Failed to fetch services\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:308\nmsgid \"Failed to open a handle to the service\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:326\nmsgid \"Failed to open a handle to the service control manager\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:350\nmsgid \"The service is already in the %{final_state} state. No further work needs to be done.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:362\nmsgid \"The service must be in one of the %{valid_initial_states} states to perform this transition. It is currently in the %{current_state} state.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:373\nmsgid \"There is already a pending transition to the %{final_state} state for the %{service_name} service.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:389\nmsgid \"The service is in the %{pending_state} state, which is an unsafe pending state.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:394\nmsgid \"Transitioning the %{service_name} service from %{initial_state} to %{final_state}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:398\nmsgid \"Waiting for the transition to finish\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:403\nmsgid \"Failed to transition the %{service_name} service to the %{final_state} state. Detail: %{detail}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:442 ../lib/puppet/util/windows/service.rb:479\nmsgid \"Service query failed\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:523\nmsgid \"Service query for %{parameter_name} failed\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:547\nmsgid \"Failed to update service %{change} configuration\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:574\nmsgid \"Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Reason for failure:\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:610\nmsgid \"The service transitioned to the %{pending_state} state.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:624\nmsgid \"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}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:639\nmsgid \"Waiting for the pending transition to the %{final_state} state to finish.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:653\nmsgid \"Unexpected transition to the %{current_state} state while waiting for the pending transition from %{pending_state} to %{final_state} to finish.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/service.rb:667\nmsgid \"Timed out while waiting for the pending transition from %{pending_state} to %{final_state} to finish. The current state is %{current_state}.\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/sid.rb:102\nmsgid \"Octet string must be an array of bytes\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/sid.rb:169\nmsgid \"Failed to convert binary SID\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/sid.rb:174\nmsgid \"ConvertSidToStringSidW failed to allocate buffer for sid\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/sid.rb:193\nmsgid \"Failed to convert string SID: %{string_sid}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/user.rb:55\nmsgid \"Failed to create administrators SID\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/user.rb:65\nmsgid \"Failed to check membership\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/user.rb:102\nmsgid \"Failed to logon user %{name}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/user.rb:133\nmsgid \"Failed to load user profile %{user}\"\nmsgstr \"\"\n\n#: ../lib/puppet/util/windows/user.rb:139\nmsgid \"Failed to unload user profile %{user}\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:46\nmsgid \"Failed to save CA certificates to '%{capath}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:60\nmsgid \"The CA certificates are missing from '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:65\nmsgid \"Failed to load CA certificates from '%{capath}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'PEM' is an acronym and shouldn't be translated\n#: ../lib/puppet/x509/cert_provider.rb:77\nmsgid \"Failed to parse CA certificates as PEM\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:93\nmsgid \"Failed to save CRLs to '%{crlpath}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:107\nmsgid \"The CRL is missing from '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:112\nmsgid \"Failed to load CRLs from '%{crlpath}'\"\nmsgstr \"\"\n\n#. TRANSLATORS 'PEM' is an acronym and shouldn't be translated\n#: ../lib/puppet/x509/cert_provider.rb:124\nmsgid \"Failed to parse CRLs as PEM\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:196\nmsgid \"Failed to save private key for '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:216\nmsgid \"The private key is missing from '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:221\nmsgid \"Failed to load private key for '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:264\nmsgid \"Failed to save client certificate for '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:280\nmsgid \"The client certificate is missing from '%{path}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:285\nmsgid \"Failed to load client certificate for '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:341\nmsgid \"Failed to save certificate request for '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:357\nmsgid \"Failed to load certificate request for '%{name}'\"\nmsgstr \"\"\n\n#: ../lib/puppet/x509/cert_provider.rb:370\nmsgid \"Failed to delete certificate request for '%{name}'\"\nmsgstr \"\"\n"
  },
  {
    "path": "man/man5/puppet.conf.5",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPETCONF\" \"5\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"Configuration settings\"\n.\n.IP \"\\(bu\" 4\nEach of these settings can be specified in \\fBpuppet\\.conf\\fR or on the command line\\.\n.\n.IP \"\\(bu\" 4\nPuppet Enterprise (PE) and open source Puppet share the configuration settings documented here\\. However, PE defaults differ from open source defaults for some settings, such as \\fBnode_terminus\\fR, \\fBstoreconfigs\\fR, \\fBalways_retry_plugins\\fR, \\fBdisable18n\\fR, \\fBenvironment_timeout\\fR (when Code Manager is enabled), and the Puppet Server JRuby \\fBmax\\-active\\-instances\\fR setting\\. To verify PE configuration defaults, check the \\fBpuppet\\.conf\\fR or \\fBpe\\-puppet\\-server\\.conf\\fR file after installation\\.\n.\n.IP \"\\(bu\" 4\nWhen using boolean settings on the command line, use \\fB\\-\\-setting\\fR and \\fB\\-\\-no\\-setting\\fR instead of \\fB\\-\\-setting (true|false)\\fR\\. (Using \\fB\\-\\-setting false\\fR results in \"Error: Could not parse application options: needless argument\"\\.)\n.\n.IP \"\\(bu\" 4\nSettings can be interpolated as \\fB$variables\\fR in other settings; \\fB$environment\\fR is special, in that puppet master will interpolate each agent node\\'s environment instead of its own\\.\n.\n.IP \"\\(bu\" 4\nMultiple values should be specified as comma\\-separated lists; multiple directories should be separated with the system path separator (usually a colon)\\.\n.\n.IP \"\\(bu\" 4\nSettings that represent time intervals should be specified in duration format: an integer immediately followed by one of the units \\'y\\' (years of 365 days), \\'d\\' (days), \\'h\\' (hours), \\'m\\' (minutes), or \\'s\\' (seconds)\\. The unit cannot be combined with other units, and defaults to seconds when omitted\\. Examples are \\'3600\\' which is equivalent to \\'1h\\' (one hour), and \\'1825d\\' which is equivalent to \\'5y\\' (5 years)\\.\n.\n.IP \"\\(bu\" 4\nIf you use the \\fBsplay\\fR setting, note that the period that it waits changes each time the Puppet agent is restarted\\.\n.\n.IP \"\\(bu\" 4\nSettings that take a single file or directory can optionally set the owner, group, and mode for their value: \\fBrundir = $vardir/run { owner = puppet, group = puppet, mode = 644 }\\fR\n.\n.IP \"\\(bu\" 4\nThe Puppet executables ignores any setting that isn\\'t relevant to their function\\.\n.\n.IP \"\" 0\n.\n.P\nSee the configuration guide \\fIhttps://puppet\\.com/docs/puppet/latest/config_about_settings\\.html\\fR for more details\\.\n.\n.SS \"agent_catalog_run_lockfile\"\nA lock file to indicate that a puppet agent catalog run is currently in progress\\. The file contains the pid of the process that holds the lock on the catalog run\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/agent_catalog_run\\.lock\\fR\n.\n.IP \"\" 0\n.\n.SS \"agent_disabled_lockfile\"\nA lock file to indicate that puppet agent runs have been administratively disabled\\. File contains a JSON object with state information\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/agent_disabled\\.lock\\fR\n.\n.IP \"\" 0\n.\n.SS \"allow_duplicate_certs\"\nWhether to allow a new certificate request to overwrite an existing certificate request\\. If true, then the old certificate must be cleaned using \\fBpuppetserver ca clean\\fR, and the new request signed using \\fBpuppetserver ca sign\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"allow_pson_serialization\"\nWhether to allow PSON serialization\\. When unable to serialize to JSON or other formats, Puppet falls back to PSON\\. This option affects the configuration management service responses of Puppet Server and the process by which the agent saves its cached catalog\\. With a default value of \\fBfalse\\fR, this option is useful in preventing the loss of data because rich data cannot be serialized via PSON\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"always_retry_plugins\"\nAffects how we cache attempts to load Puppet resource types and features\\. If true, then calls to \\fBPuppet\\.type\\.<type>?\\fR \\fBPuppet\\.feature\\.<feature>?\\fR will always attempt to load the type or feature (which can be an expensive operation) unless it has already been loaded successfully\\. This makes it possible for a single agent run to, e\\.g\\., install a package that provides the underlying capabilities for a type or feature, and then later load that type or feature during the same run (even if the type or feature had been tested earlier and had not been available)\\.\n.\n.P\nIf this setting is set to false, then types and features will only be checked once, and if they are not available, the negative result is cached and returned for all subsequent attempts to load the type or feature\\. This behavior is almost always appropriate for the server, and can result in a significant performance improvement for types and features that are checked frequently\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"autoflush\"\nWhether log files should always flush to disk\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"autosign\"\nWhether (and how) to autosign certificate requests\\. This setting is only relevant on a Puppet Server acting as a certificate authority (CA)\\.\n.\n.P\nValid values are true (autosigns all certificate requests; not recommended), false (disables autosigning certificates), or the absolute path to a file\\.\n.\n.P\nThe file specified in this setting may be either a \\fBconfiguration file\\fR or a \\fBcustom policy executable\\.\\fR Puppet will automatically determine what it is: If the Puppet user (see the \\fBuser\\fR setting) can execute the file, it will be treated as a policy executable; otherwise, it will be treated as a config file\\.\n.\n.P\nIf a custom policy executable is configured, the CA Puppet Server will run it every time it receives a CSR\\. The executable will be passed the subject CN of the request \\fIas a command line argument,\\fR and the contents of the CSR in PEM format \\fIon stdin\\.\\fR It should exit with a status of 0 if the cert should be autosigned and non\\-zero if the cert should not be autosigned\\.\n.\n.P\nIf a certificate request is not autosigned, it will persist for review\\. An admin user can use the \\fBpuppetserver ca sign\\fR command to manually sign it, or can delete the request\\.\n.\n.P\nFor info on autosign configuration files, see the guide to Puppet\\'s config files \\fIhttps://puppet\\.com/docs/puppet/latest/config_file_autosign\\.html\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/autosign\\.conf\\fR\n.\n.IP \"\" 0\n.\n.SS \"basemodulepath\"\nThe search path for \\fBglobal\\fR modules\\. Should be specified as a list of directories separated by the system path separator character\\. (The POSIX path separator is \\':\\', and the Windows path separator is \\';\\'\\.)\n.\n.P\nThese are the modules that will be used by \\fIall\\fR environments\\. Note that the \\fBmodules\\fR directory of the active environment will have priority over any global directories\\. For more info, see \\fIhttps://puppet\\.com/docs/puppet/latest/environments_about\\.html\\fR\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$codedir/modules:/opt/puppetlabs/puppet/modules\\fR\n.\n.IP \"\" 0\n.\n.SS \"binder_config\"\nThe binder configuration file\\. Puppet reads this file on each request to configure the bindings system\\. If set to nil (the default), a $confdir/binder_config\\.yaml is optionally loaded\\. If it does not exists, a default configuration is used\\. If the setting :binding_config is specified, it must reference a valid and existing yaml file\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"bucketdir\"\nWhere FileBucket files are stored\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/bucket\\fR\n.\n.IP \"\" 0\n.\n.SS \"ca_fingerprint\"\nThe expected fingerprint of the CA certificate\\. If specified, the agent will compare the CA certificate fingerprint that it downloads against this value and reject the CA certificate if the values do not match\\. This only applies during the first download of the CA certificate\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"ca_name\"\nThe name to use the Certificate Authority certificate\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBPuppet CA: $certname\\fR\n.\n.IP \"\" 0\n.\n.SS \"ca_port\"\nThe port to use for the certificate authority\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$serverport\\fR\n.\n.IP \"\" 0\n.\n.SS \"ca_refresh_interval\"\nHow often the Puppet agent refreshes its local CA certificates\\. By default, CA certificates are refreshed every 24 hours\\. If a different interval is specified, the agent refreshes its CA certificates during the next agent run if the elapsed time since the certificates were last refreshed exceeds the specified duration\\.\n.\n.P\nIn general, the interval should be greater than the \\fBruninterval\\fR value\\. Setting the \\fBca_refresh_interval\\fR value to 0 or an equal or lesser value than \\fBruninterval\\fR causes the CA certificates to be refreshed on every run\\.\n.\n.P\nIf the agent downloads new CA certs, the agent uses those for subsequent network requests\\. If the refresh request fails or if the CA certs are unchanged on the server, then the agent run will continue using the local CA certs it already has\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB1d\\fR\n.\n.IP \"\" 0\n.\n.SS \"ca_server\"\nThe server to use for certificate authority requests\\. It\\'s a separate server because it cannot and does not need to horizontally scale\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$server\\fR\n.\n.IP \"\" 0\n.\n.SS \"ca_ttl\"\nThe default TTL for new certificates\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB5y\\fR\n.\n.IP \"\" 0\n.\n.SS \"cacert\"\nThe CA certificate\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/ca_crt\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"cacrl\"\nThe certificate revocation list (CRL) for the CA\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/ca_crl\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"cadir\"\nThe root directory for the certificate authority\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB/etc/puppetlabs/puppetserver/ca\\fR\n.\n.IP \"\" 0\n.\n.SS \"cakey\"\nThe CA private key\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/ca_key\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"capub\"\nThe CA public key\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/ca_pub\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"catalog_cache_terminus\"\nHow to store cached catalogs\\. Valid values are \\'json\\', \\'msgpack\\' and \\'yaml\\'\\. The agent application defaults to \\'json\\'\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"catalog_terminus\"\nWhere to get node catalogs\\. This is useful to change if, for instance, you\\'d like to pre\\-compile catalogs and store them in memcached or some other easily\\-accessed store\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBcompiler\\fR\n.\n.IP \"\" 0\n.\n.SS \"cert_inventory\"\nThe inventory file\\. This is a text file to which the CA writes a complete listing of all certificates\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/inventory\\.txt\\fR\n.\n.IP \"\" 0\n.\n.SS \"certdir\"\nThe certificate directory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/certs\\fR\n.\n.IP \"\" 0\n.\n.SS \"certificate_revocation\"\nWhether certificate revocation checking should be enabled, and what level of checking should be performed\\.\n.\n.P\nWhen certificate revocation is enabled, Puppet expects the contents of its CRL to be one or more PEM\\-encoded CRLs concatenated together\\. When using a cert bundle, CRLs for all CAs in the chain of trust must be included in the crl file\\. The chain should be ordered from least to most authoritative, with the first CRL listed being for the root of the chain and the last being for the leaf CA\\.\n.\n.P\nWhen certificate_revocation is set to \\'true\\' or \\'chain\\', Puppet ensures that each CA in the chain of trust has not been revoked by its issuing CA\\.\n.\n.P\nWhen certificate_revocation is set to \\'leaf\\', Puppet verifies certs against the issuing CA\\'s revocation list, but it does not verify the revocation status of the issuing CA or any CA above it within the chain of trust\\.\n.\n.P\nWhen certificate_revocation is set to \\'false\\', Puppet disables all certificate revocation checking and does not attempt to download the CRL\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBchain\\fR\n.\n.IP \"\" 0\n.\n.SS \"certname\"\nThe name to use when handling certificates\\. When a node requests a certificate from the CA Puppet Server, it uses the value of the \\fBcertname\\fR setting as its requested Subject CN\\.\n.\n.P\nThis is the name used when managing a node\\'s permissions in Puppet Server\\'s auth\\.conf \\fIhttps://puppet\\.com/docs/puppetserver/latest/config_file_auth\\.html\\fR\\. In most cases, it is also used as the node\\'s name when matching node definitions \\fIhttps://puppet\\.com/docs/puppet/latest/lang_node_definitions\\.html\\fR and requesting data from an ENC\\. (This can be changed with the \\fBnode_name_value\\fR and \\fBnode_name_fact\\fR settings, although you should only do so if you have a compelling reason\\.)\n.\n.P\nA node\\'s certname is available in Puppet manifests as \\fB$trusted[\\'certname\\']\\fR\\. (See Facts and Built\\-In Variables \\fIhttps://puppet\\.com/docs/puppet/latest/lang_facts_and_builtin_vars\\.html\\fR for more details\\.)\n.\n.IP \"\\(bu\" 4\nFor best compatibility, you should limit the value of \\fBcertname\\fR to only use lowercase letters, numbers, periods, underscores, and dashes\\. (That is, it should match \\fB/A[a\\-z0\\-9\\._\\-]+Z/\\fR\\.)\n.\n.IP \"\\(bu\" 4\nThe special value \\fBca\\fR is reserved, and can\\'t be used as the certname for a normal node\\.\n.\n.IP\n\\fBNote:\\fR You must set the certname in the main section of the puppet\\.conf file\\. Setting it in a different section causes errors\\.\n.\n.IP \"\" 0\n.\n.P\nDefaults to the node\\'s fully qualified domain name\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBthe Host\\'s fully qualified domain name, as determined by Facter\\fR\n.\n.IP \"\" 0\n.\n.SS \"ciphers\"\nThe list of ciphersuites for TLS connections initiated by puppet\\. The default value is chosen to support TLS 1\\.0 and up, but can be made more restrictive if needed\\. The ciphersuites must be specified in OpenSSL format, not IANA\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBECDHE\\-ECDSA\\-AES128\\-GCM\\-SHA256:ECDHE\\-RSA\\-AES128\\-GCM\\-SHA256:ECDHE\\-ECDSA\\-AES256\\-GCM\\-SHA384:ECDHE\\-RSA\\-AES256\\-GCM\\-SHA384:ECDHE\\-ECDSA\\-CHACHA20\\-POLY1305:ECDHE\\-RSA\\-CHACHA20\\-POLY1305:DHE\\-RSA\\-AES128\\-GCM\\-SHA256:DHE\\-RSA\\-AES256\\-GCM\\-SHA384:DHE\\-RSA\\-CHACHA20\\-POLY1305:ECDHE\\-ECDSA\\-AES128\\-SHA256:ECDHE\\-RSA\\-AES128\\-SHA256:ECDHE\\-ECDSA\\-AES128\\-SHA:ECDHE\\-RSA\\-AES128\\-SHA:ECDHE\\-ECDSA\\-AES256\\-SHA384:ECDHE\\-RSA\\-AES256\\-SHA384:ECDHE\\-ECDSA\\-AES256\\-SHA:ECDHE\\-RSA\\-AES256\\-SHA:DHE\\-RSA\\-AES128\\-SHA256:DHE\\-RSA\\-AES256\\-SHA256:AES128\\-GCM\\-SHA256:AES256\\-GCM\\-SHA384:AES128\\-SHA256:AES256\\-SHA256\\fR\n.\n.IP \"\" 0\n.\n.SS \"classfile\"\nThe file in which puppet agent stores a list of the classes associated with the retrieved configuration\\. Can be loaded in the separate \\fBpuppet\\fR executable using the \\fB\\-\\-loadclasses\\fR option\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/classes\\.txt\\fR\n.\n.IP \"\" 0\n.\n.SS \"client_datadir\"\nThe directory in which serialized data is stored on the client\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/client_data\\fR\n.\n.IP \"\" 0\n.\n.SS \"clientbucketdir\"\nWhere FileBucket files are stored locally\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/clientbucket\\fR\n.\n.IP \"\" 0\n.\n.SS \"clientyamldir\"\nThe directory in which client\\-side YAML data is stored\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/client_yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"code\"\nCode to parse directly\\. This is essentially only used by \\fBpuppet\\fR, and should only be set if you\\'re writing your own Puppet executable\\.\n.\n.SS \"codedir\"\nThe main Puppet code directory\\. The default for this setting is calculated based on the user\\. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it\\'s running as any other user, it defaults to being in the user\\'s home directory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBUnix/Linux: /etc/puppetlabs/code \\-\\- Windows: C:\\eProgramData\\ePuppetLabs\\ecode \\-\\- Non\\-root user: ~/\\.puppetlabs/etc/code\\fR\n.\n.IP \"\" 0\n.\n.SS \"color\"\nWhether to use colors when logging to the console\\. Valid values are \\fBansi\\fR (equivalent to \\fBtrue\\fR), \\fBhtml\\fR, and \\fBfalse\\fR, which produces no color\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBansi\\fR\n.\n.IP \"\" 0\n.\n.SS \"confdir\"\nThe main Puppet configuration directory\\. The default for this setting is calculated based on the user\\. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it\\'s running as any other user, it defaults to being in the user\\'s home directory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBUnix/Linux: /etc/puppetlabs/puppet \\-\\- Windows: C:\\eProgramData\\ePuppetLabs\\epuppet\\eetc \\-\\- Non\\-root user: ~/\\.puppetlabs/etc/puppet\\fR\n.\n.IP \"\" 0\n.\n.SS \"config\"\nThe configuration file for the current puppet application\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/${config_file_name}\\fR\n.\n.IP \"\" 0\n.\n.SS \"config_file_name\"\nThe name of the puppet config file\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet\\.conf\\fR\n.\n.IP \"\" 0\n.\n.SS \"config_version\"\nHow to determine the configuration version\\. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined\\. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server\\.\n.\n.P\nSetting a global value for config_version in puppet\\.conf is not allowed (but it can be overridden from the commandline)\\. Please set a per\\-environment value in environment\\.conf instead\\. For more info, see \\fIhttps://puppet\\.com/docs/puppet/latest/environments_about\\.html\\fR\n.\n.SS \"configprint\"\nPrints the value of a specific configuration setting\\. If the name of a setting is provided for this, then the value is printed and puppet exits\\. Comma\\-separate multiple values\\. For a list of all values, specify \\'all\\'\\. This setting is deprecated, the \\'puppet config\\' command replaces this functionality\\.\n.\n.SS \"crl_refresh_interval\"\nHow often the Puppet agent refreshes its local Certificate Revocation List (CRL)\\. By default, the CRL is refreshed every 24 hours\\. If a different interval is specified, the agent refreshes its CRL on the next Puppet agent run if the elapsed time since the CRL was last refreshed exceeds the specified interval\\.\n.\n.P\nIn general, the interval should be greater than the \\fBruninterval\\fR value\\. Setting the \\fBcrl_refresh_interval\\fR value to 0 or an equal or lesser value than \\fBruninterval\\fR causes the CRL to be refreshed on every run\\.\n.\n.P\nIf the agent downloads a new CRL, the agent will use it for subsequent network requests\\. If the refresh request fails or if the CRL is unchanged on the server, then the agent run will continue using the local CRL it already has\\.This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB1d\\fR\n.\n.IP \"\" 0\n.\n.SS \"csr_attributes\"\nAn optional file containing custom attributes to add to certificate signing requests (CSRs)\\. You should ensure that this file does not exist on your CA Puppet Server; if it does, unwanted certificate extensions may leak into certificates created with the \\fBpuppetserver ca generate\\fR command\\.\n.\n.P\nIf present, this file must be a YAML hash containing a \\fBcustom_attributes\\fR key and/or an \\fBextension_requests\\fR key\\. The value of each key must be a hash, where each key is a valid OID and each value is an object that can be cast to a string\\.\n.\n.P\nCustom attributes can be used by the CA when deciding whether to sign the certificate, but are then discarded\\. Attribute OIDs can be any OID value except the standard CSR attributes (i\\.e\\. attributes described in RFC 2985 section 5\\.4)\\. This is useful for embedding a pre\\-shared key for autosigning policy executables (see the \\fBautosign\\fR setting), often by using the \\fB1\\.2\\.840\\.113549\\.1\\.9\\.7\\fR (\"challenge password\") OID\\.\n.\n.P\nExtension requests will be permanently embedded in the final certificate\\. Extension OIDs must be in the \"ppRegCertExt\" (\\fB1\\.3\\.6\\.1\\.4\\.1\\.34380\\.1\\.1\\fR), \"ppPrivCertExt\" (\\fB1\\.3\\.6\\.1\\.4\\.1\\.34380\\.1\\.2\\fR), or \"ppAuthCertExt\" (\\fB1\\.3\\.6\\.1\\.4\\.1\\.34380\\.1\\.3\\fR) OID arcs\\. The ppRegCertExt arc is reserved for four of the most common pieces of data to embed: \\fBpp_uuid\\fR (\\fB\\.1\\fR), \\fBpp_instance_id\\fR (\\fB\\.2\\fR), \\fBpp_image_name\\fR (\\fB\\.3\\fR), and \\fBpp_preshared_key\\fR (\\fB\\.4\\fR) \\-\\-\\- in the YAML file, these can be referred to by their short descriptive names instead of their full OID\\. The ppPrivCertExt arc is unregulated, and can be used for site\\-specific extensions\\. The ppAuthCert arc is reserved for two pieces of data to embed: \\fBpp_authorization\\fR (\\fB\\.1\\fR) and \\fBpp_auth_role\\fR (\\fB\\.13\\fR)\\. As with ppRegCertExt, in the YAML file, these can be referred to by their short descriptive name instead of their full OID\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/csr_attributes\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"csrdir\"\nWhere the CA stores certificate requests\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/requests\\fR\n.\n.IP \"\" 0\n.\n.SS \"daemonize\"\nWhether to send the process into the background\\. This defaults to true on POSIX systems, and to false on Windows (where Puppet currently cannot daemonize)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"data_binding_terminus\"\nThis setting has been deprecated\\. Use of any value other than \\'hiera\\' should instead be configured in a version 5 hiera\\.yaml\\. Until this setting is removed, it controls which data binding terminus to use for global automatic data binding (across all environments)\\. By default this value is \\'hiera\\'\\. A value of \\'none\\' turns off the global binding\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBhiera\\fR\n.\n.IP \"\" 0\n.\n.SS \"default_file_terminus\"\nThe default source for files if no server is given in a uri, e\\.g\\. puppet:///file\\. The default of \\fBrest\\fR causes the file to be retrieved using the \\fBserver\\fR setting\\. When running \\fBapply\\fR the default is \\fBfile_server\\fR, causing requests to be filled locally\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBrest\\fR\n.\n.IP \"\" 0\n.\n.SS \"default_manifest\"\nThe default main manifest for directory environments\\. Any environment that doesn\\'t set the \\fBmanifest\\fR setting in its \\fBenvironment\\.conf\\fR file will use this manifest\\.\n.\n.P\nThis setting\\'s value can be an absolute or relative path\\. An absolute path will make all environments default to the same main manifest; a relative path will allow each environment to use its own manifest, and Puppet will resolve the path relative to each environment\\'s main directory\\.\n.\n.P\nIn either case, the path can point to a single file or to a directory of manifests to be evaluated in alphabetical order\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB\\./manifests\\fR\n.\n.IP \"\" 0\n.\n.SS \"default_schedules\"\nBoolean; whether to generate the default schedule resources\\. Setting this to false is useful for keeping external report processors clean of skipped schedule resources\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"deviceconfdir\"\nThe root directory of devices\\' $confdir\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/devices\\fR\n.\n.IP \"\" 0\n.\n.SS \"deviceconfig\"\nPath to the device config file for puppet device\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/device\\.conf\\fR\n.\n.IP \"\" 0\n.\n.SS \"devicedir\"\nThe root directory of devices\\' $vardir\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/devices\\fR\n.\n.IP \"\" 0\n.\n.SS \"diff\"\nWhich diff command to use when printing differences between files\\. This setting has no default value on Windows, as standard \\fBdiff\\fR is not available, but Puppet can use many third\\-party diff tools\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBdiff\\fR\n.\n.IP \"\" 0\n.\n.SS \"diff_args\"\nWhich arguments to pass to the diff command when printing differences between files\\. The command to use can be chosen with the \\fBdiff\\fR setting\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB\\-u\\fR\n.\n.IP \"\" 0\n.\n.SS \"digest_algorithm\"\nWhich digest algorithm to use for file resources and the filebucket\\. Valid values are sha256, sha384, sha512, sha224, md5\\. Default is sha256\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBsha256\\fR\n.\n.IP \"\" 0\n.\n.SS \"disable_i18n\"\nIf true, turns off all translations of Puppet and module log messages, which affects error, warning, and info log messages, as well as any translations in the report and CLI\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"disable_per_environment_manifest\"\nWhether to disallow an environment\\-specific main manifest\\. When set to \\fBtrue\\fR, Puppet will use the manifest specified in the \\fBdefault_manifest\\fR setting for all environments\\. If an environment specifies a different main manifest in its \\fBenvironment\\.conf\\fR file, catalog requests for that environment will fail with an error\\.\n.\n.P\nThis setting requires \\fBdefault_manifest\\fR to be set to an absolute path\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"disable_warnings\"\nA comma\\-separated list of warning types to suppress\\. If large numbers of warnings are making Puppet\\'s logs too large or difficult to use, you can temporarily silence them with this setting\\.\n.\n.P\nIf you are preparing to upgrade Puppet to a new major version, you should re\\-enable all warnings for a while\\.\n.\n.P\nValid values for this setting are:\n.\n.IP \"\\(bu\" 4\n\\fBdeprecations\\fR \\-\\-\\- disables deprecation warnings\\.\n.\n.IP \"\\(bu\" 4\n\\fBundefined_variables\\fR \\-\\-\\- disables warnings about non existing variables\\.\n.\n.IP \"\\(bu\" 4\n\\fBundefined_resources\\fR \\-\\-\\- disables warnings about non existing resources\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB[]\\fR\n.\n.IP \"\" 0\n.\n.SS \"dns_alt_names\"\nA comma\\-separated list of alternate DNS names for Puppet Server\\. These are extra hostnames (in addition to its \\fBcertname\\fR) that the server is allowed to use when serving agents\\. Puppet checks this setting when automatically creating a certificate for Puppet agent or Puppet Server\\. These can be either IP or DNS, and the type should be specified and followed with a colon\\. Untyped inputs will default to DNS\\.\n.\n.P\nIn order to handle agent requests at a given hostname (like \"puppet\\.example\\.com\"), Puppet Server needs a certificate that proves it\\'s allowed to use that name; if a server shows a certificate that doesn\\'t include its hostname, Puppet agents will refuse to trust it\\. If you use a single hostname for Puppet traffic but load\\-balance it to multiple Puppet Servers, each of those servers needs to include the official hostname in its list of extra names\\.\n.\n.P\n\\fBNote:\\fR The list of alternate names is locked in when the server\\'s certificate is signed\\. If you need to change the list later, you can\\'t just change this setting; you also need to regenerate the certificate\\. For more information on that process, see the cert regen docs \\fIhttps://puppet\\.com/docs/puppet/latest/ssl_regenerate_certificates\\.html\\fR\\.\n.\n.P\nTo see all the alternate names your servers are using, log into your CA server and run \\fBpuppetserver ca list \\-\\-all\\fR, then check the output for \\fB(alt names: \\.\\.\\.)\\fR\\. Most agent nodes should NOT have alternate names; the only certs that should have them are Puppet Server nodes that you want other agents to trust\\.\n.\n.SS \"document_all\"\nWhether to document all resources when using \\fBpuppet doc\\fR to generate manifest documentation\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"environment\"\nThe environment in which Puppet is running\\. For clients, such as \\fBpuppet agent\\fR, this determines the environment itself, which Puppet uses to find modules and much more\\. For servers, such as \\fBpuppet server\\fR, this provides the default environment for nodes that Puppet knows nothing about\\.\n.\n.P\nWhen defining an environment in the \\fB[agent]\\fR section, this refers to the environment that the agent requests from the primary server\\. The environment doesn\\'t have to exist on the local filesystem because the agent fetches it from the primary server\\. This definition is used when running \\fBpuppet agent\\fR\\.\n.\n.P\nWhen defined in the \\fB[user]\\fR section, the environment refers to the path that Puppet uses to search for code and modules related to its execution\\. This requires the environment to exist locally on the filesystem where puppet is being executed\\. Puppet subcommands, including \\fBpuppet module\\fR and \\fBpuppet apply\\fR, use this definition\\.\n.\n.P\nGiven that the context and effects vary depending on the config section \\fIhttps://puppet\\.com/docs/puppet/latest/config_file_main\\.html#config\\-sections\\fR in which the \\fBenvironment\\fR setting is defined, do not set it globally\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBproduction\\fR\n.\n.IP \"\" 0\n.\n.SS \"environment_data_provider\"\nThe name of a registered environment data provider used when obtaining environment specific data\\. The three built in and registered providers are \\'none\\' (no data), \\'function\\' (data obtained by calling the function \\'environment::data()\\') and \\'hiera\\' (data obtained using a data provider configured using a hiera\\.yaml file in root of the environment)\\. Other environment data providers may be registered in modules on the module path\\. For such custom data providers see the respective module documentation\\. This setting is deprecated\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"environment_timeout\"\nHow long the Puppet server should cache data it loads from an environment\\.\n.\n.P\nA value of \\fB0\\fR will disable caching\\. This setting can also be set to \\fBunlimited\\fR, which will cache environments until the server is restarted or told to refresh the cache\\. All other values will result in Puppet server evicting environments that haven\\'t been used within the last \\fBenvironment_timeout\\fR seconds\\.\n.\n.P\nYou should change this setting once your Puppet deployment is doing non\\-trivial work\\. We chose the default value of \\fB0\\fR because it lets new users update their code without any extra steps, but it lowers the performance of your Puppet server\\. We recommend either:\n.\n.IP \"\\(bu\" 4\nSetting this to \\fBunlimited\\fR and explicitly refreshing your Puppet server as part of your code deployment process\\.\n.\n.IP \"\\(bu\" 4\nSetting this to a number that will keep your most actively used environments cached, but allow testing environments to fall out of the cache and reduce memory usage\\. A value of 3 minutes (3m) is a reasonable value\\.\n.\n.IP \"\" 0\n.\n.P\nOnce you set \\fBenvironment_timeout\\fR to a non\\-zero value, you need to tell Puppet server to read new code from disk using the \\fBenvironment\\-cache\\fR API endpoint after you deploy new code\\. See the docs for the Puppet Server administrative API \\fIhttps://puppet\\.com/docs/puppetserver/latest/admin\\-api/v1/environment\\-cache\\.html\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB0\\fR\n.\n.IP \"\" 0\n.\n.SS \"environmentpath\"\nA search path for directory environments, as a list of directories separated by the system path separator character\\. (The POSIX path separator is \\':\\', and the Windows path separator is \\';\\'\\.)\n.\n.P\nThis setting must have a value set to enable \\fBdirectory environments\\.\\fR The recommended value is \\fB$codedir/environments\\fR\\. For more details, see \\fIhttps://puppet\\.com/docs/puppet/latest/environments_about\\.html\\fR\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$codedir/environments\\fR\n.\n.IP \"\" 0\n.\n.SS \"evaltrace\"\nWhether each resource should log when it is being evaluated\\. This allows you to interactively see exactly what is being done\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"exclude_unchanged_resources\"\nSpecifies how unchanged resources are listed in reports\\. When set to \\fBtrue\\fR, resources that have had no changes after catalog application will not have corresponding unchanged resource status updates listed in a report\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"external_nodes\"\nThe external node classifier (ENC) script to use for node data\\. Puppet combines this data with the main manifest to produce node catalogs\\.\n.\n.P\nTo enable this setting, set the \\fBnode_terminus\\fR setting to \\fBexec\\fR\\.\n.\n.P\nThis setting\\'s value must be the path to an executable command that can produce node information\\. The command must:\n.\n.IP \"\\(bu\" 4\nTake the name of a node as a command\\-line argument\\.\n.\n.IP \"\\(bu\" 4\nReturn a YAML hash with up to three keys:\n.\n.IP \"\\(bu\" 4\n\\fBclasses\\fR \\-\\-\\- A list of classes, as an array or hash\\.\n.\n.IP \"\\(bu\" 4\n\\fBenvironment\\fR \\-\\-\\- A string\\.\n.\n.IP \"\\(bu\" 4\n\\fBparameters\\fR \\-\\-\\- A list of top\\-scope variables to set, as a hash\\.\n.\n.IP \"\" 0\n\n.\n.IP \"\\(bu\" 4\nFor unknown nodes, exit with a non\\-zero exit code\\.\n.\n.IP \"\" 0\n.\n.P\nGenerally, an ENC script makes requests to an external data source\\.\n.\n.P\nFor more info, see the ENC documentation \\fIhttps://puppet\\.com/docs/puppet/latest/nodes_external\\.html\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBnone\\fR\n.\n.IP \"\" 0\n.\n.SS \"fact_name_length_soft_limit\"\nThe soft limit for the length of a fact name\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB2560\\fR\n.\n.IP \"\" 0\n.\n.SS \"fact_value_length_soft_limit\"\nThe soft limit for the length of a fact value\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB4096\\fR\n.\n.IP \"\" 0\n.\n.SS \"factpath\"\nWhere Puppet should look for facts\\. Multiple directories should be separated by the system path separator character\\. (The POSIX path separator is \\':\\', and the Windows path separator is \\';\\'\\.)\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/lib/facter:$vardir/facts\\fR\n.\n.IP \"\" 0\n.\n.SS \"facts_terminus\"\nThe node facts terminus\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfacter\\fR\n.\n.IP \"\" 0\n.\n.SS \"fileserverconfig\"\nWhere the fileserver configuration is stored\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/fileserver\\.conf\\fR\n.\n.IP \"\" 0\n.\n.SS \"filetimeout\"\nThe minimum time to wait between checking for updates in configuration files\\. This timeout determines how quickly Puppet checks whether a file (such as manifests or puppet\\.conf) has changed on disk\\. The default will change in a future release to be \\'unlimited\\', requiring a reload of the Puppet service to pick up changes to its internal configuration\\. Currently we do not accept a value of \\'unlimited\\'\\. To reparse files within an environment in Puppet Server please use the environment_cache endpoint\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB15s\\fR\n.\n.IP \"\" 0\n.\n.SS \"forge_authorization\"\nThe authorization key to connect to the Puppet Forge\\. Leave blank for unauthorized or license based connections\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"freeze_main\"\nFreezes the \\'main\\' class, disallowing any code to be added to it\\. This essentially means that you can\\'t have any code outside of a node, class, or definition other than in the site manifest\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"genconfig\"\nWhen true, causes Puppet applications to print an example config file to stdout and exit\\. The example will include descriptions of each setting, and the current (or default) value of each setting, incorporating any settings overridden on the CLI (with the exception of \\fBgenconfig\\fR itself)\\. This setting only makes sense when specified on the command line as \\fB\\-\\-genconfig\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"genmanifest\"\nWhether to just print a manifest to stdout and exit\\. Only makes sense when specified on the command line as \\fB\\-\\-genmanifest\\fR\\. Takes into account arguments specified on the CLI\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"graph\"\nWhether to create \\.dot graph files, which let you visualize the dependency and containment relationships in Puppet\\'s catalog\\. You can load and view these files with tools like OmniGraffle \\fIhttp://www\\.omnigroup\\.com/applications/omnigraffle/\\fR (OS X) or graphviz \\fIhttp://www\\.graphviz\\.org/\\fR (multi\\-platform)\\.\n.\n.P\nGraph files are created when \\fIapplying\\fR a catalog, so this setting should be used on nodes running \\fBpuppet agent\\fR or \\fBpuppet apply\\fR\\.\n.\n.P\nThe \\fBgraphdir\\fR setting determines where Puppet will save graphs\\. Note that we don\\'t save graphs for historical runs; Puppet will replace the previous \\.dot files with new ones every time it applies a catalog\\.\n.\n.P\nSee your graphing software\\'s documentation for details on opening \\.dot files\\. If you\\'re using GraphViz\\'s \\fBdot\\fR command, you can do a quick PNG render with \\fBdot \\-Tpng <DOT FILE> \\-o <OUTPUT FILE>\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"graphdir\"\nWhere to save \\.dot\\-format graphs (when the \\fBgraph\\fR setting is enabled)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/graphs\\fR\n.\n.IP \"\" 0\n.\n.SS \"group\"\nThe group Puppet Server will run as\\. Used to ensure the agent side processes (agent, apply, etc) create files and directories readable by Puppet Server when necessary\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet\\fR\n.\n.IP \"\" 0\n.\n.SS \"hiera_config\"\nThe hiera configuration file\\. Puppet only reads this file on startup, so you must restart the puppet server every time you edit it\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/hiera\\.yaml\\. However, for backwards compatibility, if a file exists at $codedir/hiera\\.yaml, Puppet uses that instead\\.\\fR\n.\n.IP \"\" 0\n.\n.SS \"hostcert\"\nWhere individual hosts store and look for their certificates\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$certdir/$certname\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"hostcert_renewal_interval\"\nHow often the Puppet agent renews its client certificate\\. By default, the client certificate is renewed 30 days before the certificate expires\\. If a different interval is specified, the agent renews its client certificate during the next agent run, assuming that the client certificate has expired within the specified duration\\.\n.\n.P\nIn general, the \\fBhostcert_renewal_interval\\fR value should be greater than the \\fBruninterval\\fR value\\. Setting the \\fBhostcert_renewal_interval\\fR value to 0 disables automatic renewal\\.\n.\n.P\nIf the agent downloads a new certificate, the agent will use it for subsequent network requests\\. If the refresh request fails, the agent run continues to use its existing certificate\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB30d\\fR\n.\n.IP \"\" 0\n.\n.SS \"hostcrl\"\nWhere the host\\'s certificate revocation list can be found\\. This is distinct from the certificate authority\\'s CRL\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/crl\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"hostcsr\"\nWhere individual hosts store their certificate request (CSR) while waiting for the CA to issue their certificate\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$requestdir/$certname\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"hostprivkey\"\nWhere individual hosts store and look for their private key\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$privatekeydir/$certname\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"hostpubkey\"\nWhere individual hosts store and look for their public key\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$publickeydir/$certname\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_connect_timeout\"\nThe maximum amount of time to wait when establishing an HTTP connection\\. The default value is 2 minutes\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB2m\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_debug\"\nWhether to write HTTP request and responses to stderr\\. This should never be used in a production environment\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_extra_headers\"\nThe list of extra headers that will be sent with http requests to the primary server\\. The header definition consists of a name and a value separated by a colon\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB[]\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_keepalive_timeout\"\nThe maximum amount of time a persistent HTTP connection can remain idle in the connection pool, before it is closed\\. This timeout should be shorter than the keepalive timeout used on the HTTP server, e\\.g\\. Apache KeepAliveTimeout directive\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB4s\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_proxy_host\"\nThe HTTP proxy host to use for outgoing connections\\. The proxy will be bypassed if the server\\'s hostname matches the NO_PROXY environment variable or \\fBno_proxy\\fR setting\\. Note: You may need to use a FQDN for the server hostname when using a proxy\\. Environment variable http_proxy or HTTP_PROXY will override this value\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBnone\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_proxy_password\"\nThe password for the user of an authenticated HTTP proxy\\. Requires the \\fBhttp_proxy_user\\fR setting\\.\n.\n.P\nNote that passwords must be valid when used as part of a URL\\. If a password contains any characters with special meanings in URLs (as specified by RFC 3986 section 2\\.2), they must be URL\\-encoded\\. (For example, \\fB#\\fR would become \\fB%23\\fR\\.)\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBnone\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_proxy_port\"\nThe HTTP proxy port to use for outgoing connections\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB3128\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_proxy_user\"\nThe user name for an authenticated HTTP proxy\\. Requires the \\fBhttp_proxy_host\\fR setting\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBnone\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_read_timeout\"\nThe time to wait for data to be read from an HTTP connection\\. If nothing is read after the elapsed interval then the connection will be closed\\. The default value is 10 minutes\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB10m\\fR\n.\n.IP \"\" 0\n.\n.SS \"http_user_agent\"\nThe HTTP User\\-Agent string to send when making network requests\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBPuppet/<version> Ruby/<version> (<architecture>)\\fR\n.\n.IP \"\" 0\n.\n.SS \"ignore_plugin_errors\"\nWhether the puppet run should ignore errors during pluginsync\\. If the setting is false and there are errors during pluginsync, then the agent will abort the run and submit a report containing information about the failed run\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"ignoremissingtypes\"\nSkip searching for classes and definitions that were missing during a prior compilation\\. The list of missing objects is maintained per\\-environment and persists until the environment is cleared or the primary server is restarted\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"ignoreschedules\"\nBoolean; whether puppet agent should ignore schedules\\. This is useful for initial puppet agent runs\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"include_legacy_facts\"\nWhether to include legacy facts when requesting a catalog\\. This option can be set to \\fBfalse\\fR if all puppet manifests, hiera\\.yaml, and hiera configuration layers no longer access legacy facts, such as \\fB$osfamily\\fR, and instead access structured facts, such as \\fB$facts[\\'os\\'][\\'family\\']\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"key_type\"\nThe type of private key\\. Valid values are \\fBrsa\\fR and \\fBec\\fR\\. Default is \\fBrsa\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBrsa\\fR\n.\n.IP \"\" 0\n.\n.SS \"keylength\"\nThe bit length of keys\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB4096\\fR\n.\n.IP \"\" 0\n.\n.SS \"lastrunfile\"\nWhere puppet agent stores the last run report summary in yaml format\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$publicdir/last_run_summary\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"lastrunreport\"\nWhere Puppet Agent stores the last run report, by default, in yaml format\\. The format of the report can be changed by setting the \\fBcache\\fR key of the \\fBreport\\fR terminus in the routes\\.yaml \\fIhttps://puppet\\.com/docs/puppet/latest/config_file_routes\\.html\\fR file\\. To avoid mismatches between content and file extension, this setting needs to be manually updated to reflect the terminus changes\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/last_run_report\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapattrs\"\nThe LDAP attributes to include when querying LDAP for nodes\\. All returned attributes are set as variables in the top\\-level scope\\. Multiple values should be comma\\-separated\\. The value \\'all\\' returns all attributes\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBall\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapbase\"\nThe search base for LDAP searches\\. It\\'s impossible to provide a meaningful default here, although the LDAP libraries might have one already set\\. Generally, it should be the \\'ou=Hosts\\' branch under your main directory\\.\n.\n.SS \"ldapclassattrs\"\nThe LDAP attributes to use to define Puppet classes\\. Values should be comma\\-separated\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppetclass\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapparentattr\"\nThe attribute to use to define the parent node\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBparentnode\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldappassword\"\nThe password to use to connect to LDAP\\.\n.\n.SS \"ldapport\"\nThe LDAP port\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB389\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapserver\"\nThe LDAP server\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBldap\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapssl\"\nWhether SSL should be used when searching for nodes\\. Defaults to false because SSL usually requires certificates to be set up on the client side\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapstackedattrs\"\nThe LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree\\. Values should be comma\\-separated\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppetvar\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapstring\"\nThe search string used to find an LDAP node\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB(&(objectclass=puppetClient)(cn=%s))\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldaptls\"\nWhether TLS should be used when searching for nodes\\. Defaults to false because TLS usually requires certificates to be set up on the client side\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"ldapuser\"\nThe user to use to connect to LDAP\\. Must be specified as a full DN\\.\n.\n.SS \"libdir\"\nAn extra search path for Puppet\\. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases\\. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby\\'s search path\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/lib\\fR\n.\n.IP \"\" 0\n.\n.SS \"localcacert\"\nWhere each client stores the CA certificate\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$certdir/ca\\.pem\\fR\n.\n.IP \"\" 0\n.\n.SS \"localedest\"\nWhere Puppet should store translation files that it pulls down from the central server\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/locales\\fR\n.\n.IP \"\" 0\n.\n.SS \"localesource\"\nFrom where to retrieve translation files\\. The standard Puppet \\fBfile\\fR type is used for retrieval, so anything that is a valid file source can be used here\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet:///locales\\fR\n.\n.IP \"\" 0\n.\n.SS \"location_trusted\"\nThis will allow sending the name + password and the cookie header to all hosts that puppet may redirect to\\. This may or may not introduce a security breach if puppet redirects you to a site to which you\\'ll send your authentication info and cookies\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"log_level\"\nDefault logging level for messages from Puppet\\. Allowed values are:\n.\n.IP \"\\(bu\" 4\ndebug\n.\n.IP \"\\(bu\" 4\ninfo\n.\n.IP \"\\(bu\" 4\nnotice\n.\n.IP \"\\(bu\" 4\nwarning\n.\n.IP \"\\(bu\" 4\nerr\n.\n.IP \"\\(bu\" 4\nalert\n.\n.IP \"\\(bu\" 4\nemerg\n.\n.IP \"\\(bu\" 4\ncrit\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBnotice\\fR\n.\n.IP \"\" 0\n.\n.SS \"logdest\"\nWhere to send log messages\\. Choose between \\'syslog\\' (the POSIX syslog service), \\'eventlog\\' (the Windows Event Log), \\'console\\', or the path to a log file\\. Multiple destinations can be set using a comma separated list (eg: \\fB/path/file1,console,/path/file2\\fR)\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"logdir\"\nThe directory in which to store log files\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBUnix/Linux: /var/log/puppetlabs/puppet \\-\\- Windows: C:\\eProgramData\\ePuppetLabs\\epuppet\\evar\\elog \\-\\- Non\\-root user: ~/\\.puppetlabs/var/log\\fR\n.\n.IP \"\" 0\n.\n.SS \"manage_internal_file_permissions\"\nWhether Puppet should manage the owner, group, and mode of files it uses internally\\. \\fBNote\\fR: For Windows agents, the default is \\fBfalse\\fR for versions 4\\.10\\.13 and greater, versions 5\\.5\\.6 and greater, and versions 6\\.0 and greater\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"manifest\"\nThe entry\\-point manifest for the primary server\\. This can be one file or a directory of manifests to be evaluated in alphabetical order\\. Puppet manages this path as a directory if one exists or if the path ends with a / or \\.\n.\n.P\nSetting a global value for \\fBmanifest\\fR in puppet\\.conf is not allowed (but it can be overridden from the commandline)\\. Please use directory environments instead\\. If you need to use something other than the environment\\'s \\fBmanifests\\fR directory as the main manifest, you can set \\fBmanifest\\fR in environment\\.conf\\. For more info, see \\fIhttps://puppet\\.com/docs/puppet/latest/environments_about\\.html\\fR\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"masterport\"\nThe default port puppet subcommands use to communicate with Puppet Server\\. (eg \\fBpuppet facts upload\\fR, \\fBpuppet agent\\fR)\\. May be overridden by more specific settings (see \\fBca_port\\fR, \\fBreport_port\\fR)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB8140\\fR\n.\n.IP \"\" 0\n.\n.SS \"max_deprecations\"\nSets the max number of logged/displayed parser validation deprecation warnings in case multiple deprecation warnings have been detected\\. A value of 0 blocks the logging of deprecation warnings\\. The count is per manifest\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB10\\fR\n.\n.IP \"\" 0\n.\n.SS \"max_errors\"\nSets the max number of logged/displayed parser validation errors in case multiple errors have been detected\\. A value of 0 is the same as a value of 1; a minimum of one error is always raised\\. The count is per manifest\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB10\\fR\n.\n.IP \"\" 0\n.\n.SS \"max_warnings\"\nSets the max number of logged/displayed parser validation warnings in case multiple warnings have been detected\\. A value of 0 blocks logging of warnings\\. The count is per manifest\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB10\\fR\n.\n.IP \"\" 0\n.\n.SS \"maximum_uid\"\nThe maximum allowed UID\\. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system\\. This is a hackish way to fail in a slightly more useful way when that happens\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB4294967290\\fR\n.\n.IP \"\" 0\n.\n.SS \"maxwaitforcert\"\nThe maximum amount of time the Puppet agent should wait for its certificate request to be signed\\. A value of \\fBunlimited\\fR will cause puppet agent to ask for a signed certificate indefinitely\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBunlimited\\fR\n.\n.IP \"\" 0\n.\n.SS \"maxwaitforlock\"\nThe maximum amount of time the puppet agent should wait for an already running puppet agent to finish before starting a new one\\. This is set by default to 1 minute\\. A value of \\fBunlimited\\fR will cause puppet agent to wait indefinitely\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB1m\\fR\n.\n.IP \"\" 0\n.\n.SS \"merge_dependency_warnings\"\nWhether to merge class\\-level dependency failure warnings\\.\n.\n.P\nWhen a class has a failed dependency, every resource in the class generates a notice level message about the dependency failure, and a warning level message about skipping the resource\\.\n.\n.P\nIf true, all messages caused by a class dependency failure are merged into one message associated with the class\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"mkusers\"\nWhether to create the necessary user and group that puppet agent will run as\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"module_groups\"\nExtra module groups to request from the Puppet Forge\\. This is an internal setting, and users should never change it\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"module_repository\"\nThe module repository\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBhttps://forgeapi\\.puppet\\.com\\fR\n.\n.IP \"\" 0\n.\n.SS \"module_working_dir\"\nThe directory into which module tool data is stored\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/puppet\\-module\\fR\n.\n.IP \"\" 0\n.\n.SS \"modulepath\"\nThe search path for modules, as a list of directories separated by the system path separator character\\. (The POSIX path separator is \\':\\', and the Windows path separator is \\';\\'\\.)\n.\n.P\nSetting a global value for \\fBmodulepath\\fR in puppet\\.conf is not allowed (but it can be overridden from the commandline)\\. Please use directory environments instead\\. If you need to use something other than the default modulepath of \\fB<ACTIVE ENVIRONMENT\\'S MODULES DIR>:$basemodulepath\\fR, you can set \\fBmodulepath\\fR in environment\\.conf\\. For more info, see \\fIhttps://puppet\\.com/docs/puppet/latest/environments_about\\.html\\fR\n.\n.SS \"name\"\nThe name of the application, if we are running as one\\. The default is essentially $0 without the path or \\fB\\.rb\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"named_curve\"\nThe short name for the EC curve used to generate the EC private key\\. Valid values must be one of the curves in \\fBOpenSSL::PKey::EC\\.builtin_curves\\fR\\. Default is \\fBprime256v1\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBprime256v1\\fR\n.\n.IP \"\" 0\n.\n.SS \"no_proxy\"\nList of host or domain names that should not go through \\fBhttp_proxy_host\\fR\\. Environment variable no_proxy or NO_PROXY will override this value\\. Names can be specified as an FQDN \\fBhost\\.example\\.com\\fR, wildcard \\fB*\\.example\\.com\\fR, dotted domain \\fB\\.example\\.com\\fR, or suffix \\fBexample\\.com\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBlocalhost, 127\\.0\\.0\\.1\\fR\n.\n.IP \"\" 0\n.\n.SS \"node_cache_terminus\"\nHow to store cached nodes\\. Valid values are (none), \\'json\\', \\'msgpack\\', or \\'yaml\\'\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"node_name_fact\"\nThe fact name used to determine the node name used for all requests the agent makes to the primary server\\. WARNING: This setting is mutually exclusive with node_name_value\\. Changing this setting also requires changes to Puppet Server\\'s default auth\\.conf \\fIhttps://puppet\\.com/docs/puppetserver/latest/config_file_auth\\.html\\fR\\.\n.\n.SS \"node_name_value\"\nThe explicit value used for the node name for all requests the agent makes to the primary server\\. WARNING: This setting is mutually exclusive with node_name_fact\\. Changing this setting also requires changes to Puppet Server\\'s default auth\\.conf \\fIhttps://puppet\\.com/docs/puppetserver/latest/config_file_auth\\.html\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$certname\\fR\n.\n.IP \"\" 0\n.\n.SS \"node_terminus\"\nWhich node data plugin to use when compiling node catalogs\\.\n.\n.P\nWhen Puppet compiles a catalog, it combines two primary sources of info: the main manifest, and a node data plugin (often called a \"node terminus,\" for historical reasons)\\. Node data plugins provide three things for a given node name:\n.\n.IP \"1.\" 4\nA list of classes to add to that node\\'s catalog (and, optionally, values for their parameters)\\.\n.\n.IP \"2.\" 4\nWhich Puppet environment the node should use\\.\n.\n.IP \"3.\" 4\nA list of additional top\\-scope variables to set\\.\n.\n.IP \"\" 0\n.\n.P\nThe three main node data plugins are:\n.\n.IP \"\\(bu\" 4\n\\fBplain\\fR \\-\\-\\- Returns no data, so that the main manifest controls all node configuration\\.\n.\n.IP \"\\(bu\" 4\n\\fBexec\\fR \\-\\-\\- Uses an external node classifier (ENC) \\fIhttps://puppet\\.com/docs/puppet/latest/nodes_external\\.html\\fR, configured by the \\fBexternal_nodes\\fR setting\\. This lets you pull a list of Puppet classes from any external system, using a small glue script to perform the request and format the result as YAML\\.\n.\n.IP \"\\(bu\" 4\n\\fBclassifier\\fR (formerly \\fBconsole\\fR) \\-\\-\\- Specific to Puppet Enterprise\\. Uses the PE console for node data\\.\"\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBplain\\fR\n.\n.IP \"\" 0\n.\n.SS \"noop\"\nWhether to apply catalogs in noop mode, which allows Puppet to partially simulate a normal run\\. This setting affects puppet agent and puppet apply\\.\n.\n.P\nWhen running in noop mode, Puppet will check whether each resource is in sync, like it does when running normally\\. However, if a resource attribute is not in the desired state (as declared in the catalog), Puppet will take no action, and will instead report the changes it \\fIwould\\fR have made\\. These simulated changes will appear in the report sent to the primary Puppet server, or be shown on the console if running puppet agent or puppet apply in the foreground\\. The simulated changes will not send refresh events to any subscribing or notified resources, although Puppet will log that a refresh event \\fIwould\\fR have been sent\\.\n.\n.P\n\\fBImportant note:\\fR The \\fBnoop\\fR metaparameter \\fIhttps://puppet\\.com/docs/puppet/latest/metaparameter\\.html#noop\\fR allows you to apply individual resources in noop mode, and will override the global value of the \\fBnoop\\fR setting\\. This means a resource with \\fBnoop => false\\fR \\fIwill\\fR be changed if necessary, even when running puppet agent with \\fBnoop = true\\fR or \\fB\\-\\-noop\\fR\\. (Conversely, a resource with \\fBnoop => true\\fR will only be simulated, even when noop mode is globally disabled\\.)\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"number_of_facts_soft_limit\"\nThe soft limit for the total number of fact values\\. This counts the child elements of all facts (e\\.g\\. all items of an array or a hash), not just top level facts\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB10240\\fR\n.\n.IP \"\" 0\n.\n.SS \"onetime\"\nPerform one configuration run and exit, rather than spawning a long\\-running daemon\\. This is useful for interactively running puppet agent, or running puppet agent from cron\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"passfile\"\nWhere puppet agent stores the password for its private key\\. Generally unused\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$privatedir/password\\fR\n.\n.IP \"\" 0\n.\n.SS \"path\"\nThe shell search path\\. Defaults to whatever is inherited from the parent process\\.\n.\n.P\nThis setting can only be set in the \\fB[main]\\fR section of puppet\\.conf; it cannot be set in \\fB[server]\\fR, \\fB[agent]\\fR, or an environment config section\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBnone\\fR\n.\n.IP \"\" 0\n.\n.SS \"payload_soft_limit\"\nThe soft limit for the size of the payload\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB16777216\\fR\n.\n.IP \"\" 0\n.\n.SS \"pidfile\"\nThe file containing the PID of a running process\\. This file is intended to be used by service management frameworks and monitoring systems to determine if a puppet process is still in the process table\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$rundir/${run_mode}\\.pid\\fR\n.\n.IP \"\" 0\n.\n.SS \"plugindest\"\nWhere Puppet should store plugins that it pulls down from the central server\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$libdir\\fR\n.\n.IP \"\" 0\n.\n.SS \"pluginfactdest\"\nWhere Puppet should store external facts that are being handled by pluginsync\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/facts\\.d\\fR\n.\n.IP \"\" 0\n.\n.SS \"pluginfactsource\"\nWhere to retrieve external facts for pluginsync\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet:///pluginfacts\\fR\n.\n.IP \"\" 0\n.\n.SS \"pluginsignore\"\nWhat files to ignore when pulling down plugins\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB\\.svn CVS \\.git \\.hg\\fR\n.\n.IP \"\" 0\n.\n.SS \"pluginsource\"\nFrom where to retrieve plugins\\. The standard Puppet \\fBfile\\fR type is used for retrieval, so anything that is a valid file source can be used here\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet:///plugins\\fR\n.\n.IP \"\" 0\n.\n.SS \"pluginsync\"\nWhether plugins should be synced with the central server\\. This setting is deprecated\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"postrun_command\"\nA command to run after every agent run\\. If this command returns a non\\-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run\\.\n.\n.SS \"preferred_serialization_format\"\nThe preferred means of serializing ruby instances for passing over the wire\\. This won\\'t guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBjson\\fR\n.\n.IP \"\" 0\n.\n.SS \"preprocess_deferred\"\nWhether Puppet should call deferred functions before applying the catalog\\. If set to \\fBtrue\\fR, all prerequisites required for the deferred function must be satisfied before the Puppet run\\. If set to \\fBfalse\\fR, deferred functions follow Puppet relationships and ordering\\. In this way, Puppet can install the prerequisites required for a deferred function and call the deferred function in the same run\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"prerun_command\"\nA command to run before every agent run\\. If this command returns a non\\-zero return code, the entire Puppet run will fail\\.\n.\n.SS \"preview_outputdir\"\nThe directory where catalog previews per node are generated\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/preview\\fR\n.\n.IP \"\" 0\n.\n.SS \"priority\"\nThe scheduling priority of the process\\. Valid values are \\'high\\', \\'normal\\', \\'low\\', or \\'idle\\', which are mapped to platform\\-specific values\\. The priority can also be specified as an integer value and will be passed as is, e\\.g\\. \\-5\\. Puppet must be running as a privileged user in order to increase scheduling priority\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"privatedir\"\nWhere the client stores private certificate information\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/private\\fR\n.\n.IP \"\" 0\n.\n.SS \"privatekeydir\"\nThe private key directory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/private_keys\\fR\n.\n.IP \"\" 0\n.\n.SS \"profile\"\nWhether to enable experimental performance profiling\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"publicdir\"\nWhere Puppet stores public files\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBUnix/Linux: /opt/puppetlabs/puppet/public \\-\\- Windows: C:\\eProgramData\\ePuppetLabs\\epuppet\\epublic \\-\\- Non\\-root user: ~/\\.puppetlabs/opt/puppet/public\\fR\n.\n.IP \"\" 0\n.\n.SS \"publickeydir\"\nThe public key directory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/public_keys\\fR\n.\n.IP \"\" 0\n.\n.SS \"puppet_trace\"\nWhether to print the Puppet stack trace on some errors\\. This is a noop if \\fBtrace\\fR is also set\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"puppetdlog\"\nThe fallback log file\\. This is only used when the \\fB\\-\\-logdest\\fR option is not specified AND Puppet is running on an operating system where both the POSIX syslog service and the Windows Event Log are unavailable\\. (Currently, no supported operating systems match that description\\.)\n.\n.P\nDespite the name, both puppet agent and puppet server will use this file as the fallback logging destination\\.\n.\n.P\nFor control over logging destinations, see the \\fB\\-\\-logdest\\fR command line option in the manual pages for puppet server, puppet agent, and puppet apply\\. You can see man pages by running \\fBpuppet <SUBCOMMAND> \\-\\-help\\fR, or read them online at https://puppet\\.com/docs/puppet/latest/man/\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$logdir/puppetd\\.log\\fR\n.\n.IP \"\" 0\n.\n.SS \"report\"\nWhether to send reports after every transaction\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"report_configured_environmentpath\"\nSpecifies how environment paths are reported\\. When the value of \\fBversioned_environment_dirs\\fR is \\fBtrue\\fR, Puppet applies the readlink function to the \\fBenvironmentpath\\fR setting when constructing the environment\\'s modulepath\\. The full readlinked path is referred to as the \"resolved path,\" and the configured path potentially containing symlinks is the \"configured path\\.\" When reporting where resources come from, users may choose between the configured and resolved path\\.\n.\n.P\nWhen set to \\fBfalse\\fR, the resolved paths are reported instead of the configured paths\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"report_include_system_store\"\nWhether the \\'http\\' report processor should include the system certificate store when submitting reports to HTTPS URLs\\. If false, then the \\'http\\' processor will only trust HTTPS report servers whose certificates are issued by the puppet CA or one of its intermediate CAs\\. If true, the processor will additionally trust CA certificates in the system\\'s certificate store\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"report_port\"\nThe port to communicate with the report_server\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$serverport\\fR\n.\n.IP \"\" 0\n.\n.SS \"report_server\"\nThe server to send transaction reports to\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$server\\fR\n.\n.IP \"\" 0\n.\n.SS \"reportdir\"\nThe directory in which to store reports\\. Each node gets a separate subdirectory in this directory\\. This setting is only used when the \\fBstore\\fR report processor is enabled (see the \\fBreports\\fR setting)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/reports\\fR\n.\n.IP \"\" 0\n.\n.SS \"reports\"\nThe list of report handlers to use\\. When using multiple report handlers, their names should be comma\\-separated, with whitespace allowed\\. (For example, \\fBreports = http, store\\fR\\.)\n.\n.P\nThis setting is relevant to puppet server and puppet apply\\. The primary Puppet server will call these report handlers with the reports it receives from agent nodes, and puppet apply will call them with its own report\\. (In all cases, the node applying the catalog must have \\fBreport = true\\fR\\.)\n.\n.P\nSee the report reference for information on the built\\-in report handlers; custom report handlers can also be loaded from modules\\. (Report handlers are loaded from the lib directory, at \\fBpuppet/reports/NAME\\.rb\\fR\\.)\n.\n.P\nTo turn off reports entirely, set this to \\fBnone\\fR\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBstore\\fR\n.\n.IP \"\" 0\n.\n.SS \"reporturl\"\nThe URL that reports should be forwarded to\\. This setting is only used when the \\fBhttp\\fR report processor is enabled (see the \\fBreports\\fR setting)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBhttp://localhost:3000/reports/upload\\fR\n.\n.IP \"\" 0\n.\n.SS \"requestdir\"\nWhere host certificate requests are stored\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/certificate_requests\\fR\n.\n.IP \"\" 0\n.\n.SS \"resourcefile\"\nThe file in which puppet agent stores a list of the resources associated with the retrieved configuration\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/resources\\.txt\\fR\n.\n.IP \"\" 0\n.\n.SS \"resubmit_facts\"\nWhether to send updated facts after every transaction\\. By default puppet only submits facts at the beginning of the transaction before applying a catalog\\. Since puppet can modify the state of the system, the value of the facts may change after puppet finishes\\. Therefore, any facts stored in puppetdb may not be consistent until the agent next runs, typically in 30 minutes\\. If this feature is enabled, puppet will resubmit facts after applying its catalog, ensuring facts for the node stored in puppetdb are current\\. However, this will double the fact submission load on puppetdb, so it is disabled by default\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"rich_data\"\nEnables having extended data in the catalog by storing them as a hash with the special key \\fB__ptype\\fR\\. When enabled, resource containing values of the data types \\fBBinary\\fR, \\fBRegexp\\fR, \\fBSemVer\\fR, \\fBSemVerRange\\fR, \\fBTimespan\\fR and \\fBTimestamp\\fR, as well as instances of types derived from \\fBObject\\fR retain their data type\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"route_file\"\nThe YAML file containing indirector route configuration\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/routes\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"rundir\"\nWhere Puppet PID files are kept\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBUnix/Linux: /var/run/puppetlabs \\-\\- Windows: C:\\eProgramData\\ePuppetLabs\\epuppet\\evar\\erun \\-\\- Non\\-root user: ~/\\.puppetlabs/var/run\\fR\n.\n.IP \"\" 0\n.\n.SS \"runinterval\"\nHow often puppet agent applies the catalog\\. Note that a runinterval of 0 means \"run continuously\" rather than \"never run\\.\" This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB30m\\fR\n.\n.IP \"\" 0\n.\n.SS \"runtimeout\"\nThe maximum amount of time an agent run is allowed to take\\. A Puppet agent run that exceeds this timeout will be aborted\\. A value of 0 disables the timeout\\. Defaults to 1 hour\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB1h\\fR\n.\n.IP \"\" 0\n.\n.SS \"serial\"\nWhere the serial number for certificates is stored\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/serial\\fR\n.\n.IP \"\" 0\n.\n.SS \"server\"\nThe primary Puppet server to which the Puppet agent should connect\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet\\fR\n.\n.IP \"\" 0\n.\n.SS \"server_datadir\"\nThe directory in which serialized data is stored, usually in a subdirectory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/server_data\\fR\n.\n.IP \"\" 0\n.\n.SS \"server_list\"\nThe list of primary Puppet servers to which the Puppet agent should connect, in the order that they will be tried\\. Each value should be a fully qualified domain name, followed by an optional \\':\\' and port number\\. If a port is omitted, Puppet uses masterport for that host\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB[]\\fR\n.\n.IP \"\" 0\n.\n.SS \"serverport\"\nThe default port puppet subcommands use to communicate with Puppet Server\\. (eg \\fBpuppet facts upload\\fR, \\fBpuppet agent\\fR)\\. May be overridden by more specific settings (see \\fBca_port\\fR, \\fBreport_port\\fR)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB8140\\fR\n.\n.IP \"\" 0\n.\n.SS \"settings_catalog\"\nWhether to compile and apply the settings catalog\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"show_diff\"\nWhether to log and report a contextual diff when files are being replaced\\. This causes partial file contents to pass through Puppet\\'s normal logging and reporting system, so this setting should be used with caution if you are sending Puppet\\'s reports to an insecure destination\\. This feature currently requires the \\fBdiff/lcs\\fR Ruby library\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"signeddir\"\nWhere the CA stores signed certificates\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$cadir/signed\\fR\n.\n.IP \"\" 0\n.\n.SS \"skip_logging_catalog_request_destination\"\nSpecifies whether to suppress the notice of which compiler supplied the catalog\\. A value of \\fBtrue\\fR suppresses the notice\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"skip_tags\"\nTags to use to filter resources\\. If this is set, then only resources not tagged with the specified tags will be applied\\. Values must be comma\\-separated\\.\n.\n.SS \"sourceaddress\"\nThe address the agent should use to initiate requests\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"splay\"\nWhether to sleep for a random amount of time, ranging from immediately up to its \\fB$splaylimit\\fR, before performing its first agent run after a service restart\\. After this period, the agent runs periodically on its \\fB$runinterval\\fR\\.\n.\n.P\nFor example, assume a default 30\\-minute \\fB$runinterval\\fR, \\fBsplay\\fR set to its default of \\fBfalse\\fR, and an agent starting at :00 past the hour\\. The agent would check in every 30 minutes at :01 and :31 past the hour\\.\n.\n.P\nWith \\fBsplay\\fR enabled, it waits any amount of time up to its \\fB$splaylimit\\fR before its first run\\. For example, it might randomly wait 8 minutes, then start its first run at :08 past the hour\\. With the \\fB$runinterval\\fR at its default 30 minutes, its next run will be at :38 past the hour\\.\n.\n.P\nIf you restart an agent\\'s puppet service with \\fBsplay\\fR enabled, it recalculates its splay period and delays its first agent run after restarting for this new period\\. If you simultaneously restart a group of puppet agents with \\fBsplay\\fR enabled, their checkins to your primary servers can be distributed more evenly\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"splaylimit\"\nThe maximum time to delay before an agent\\'s first run when \\fBsplay\\fR is enabled\\. Defaults to the agent\\'s \\fB$runinterval\\fR\\. The \\fBsplay\\fR interval is random and recalculated each time the agent is started or restarted\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$runinterval\\fR\n.\n.IP \"\" 0\n.\n.SS \"srv_domain\"\nThe domain which will be queried to find the SRV records of servers to use\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBexample\\.com\\fR\n.\n.IP \"\" 0\n.\n.SS \"ssl_client_header\"\nThe header containing an authenticated client\\'s SSL DN\\. This header must be set by the proxy to the authenticated client\\'s SSL DN (e\\.g\\., \\fB/CN=puppet\\.puppetlabs\\.com\\fR)\\. Puppet will parse out the Common Name (CN) from the Distinguished Name (DN) and use the value of the CN field for authorization\\.\n.\n.P\nNote that the name of the HTTP header gets munged by the web server common gateway interface: an \\fBHTTP_\\fR prefix is added, dashes are converted to underscores, and all letters are uppercased\\. Thus, to use the \\fBX\\-Client\\-DN\\fR header, this setting should be \\fBHTTP_X_CLIENT_DN\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBHTTP_X_CLIENT_DN\\fR\n.\n.IP \"\" 0\n.\n.SS \"ssl_client_verify_header\"\nThe header containing the status message of the client verification\\. This header must be set by the proxy to \\'SUCCESS\\' if the client successfully authenticated, and anything else otherwise\\.\n.\n.P\nNote that the name of the HTTP header gets munged by the web server common gateway interface: an \\fBHTTP_\\fR prefix is added, dashes are converted to underscores, and all letters are uppercased\\. Thus, to use the \\fBX\\-Client\\-Verify\\fR header, this setting should be \\fBHTTP_X_CLIENT_VERIFY\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBHTTP_X_CLIENT_VERIFY\\fR\n.\n.IP \"\" 0\n.\n.SS \"ssl_lockfile\"\nA lock file to indicate that the ssl bootstrap process is currently in progress\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$ssldir/ssl\\.lock\\fR\n.\n.IP \"\" 0\n.\n.SS \"ssl_trust_store\"\nA file containing CA certificates in PEM format that puppet should trust when making HTTPS requests\\. This \\fBonly\\fR applies to https requests to non\\-puppet infrastructure, such as retrieving file metadata and content from https file sources, puppet module tool and the \\'http\\' report processor\\. This setting is ignored when making requests to puppet:// URLs such as catalog and report requests\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"ssldir\"\nWhere SSL certificates are kept\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/ssl\\fR\n.\n.IP \"\" 0\n.\n.SS \"statedir\"\nThe directory where Puppet state is stored\\. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/state\\fR\n.\n.IP \"\" 0\n.\n.SS \"statefile\"\nWhere Puppet agent and Puppet Server store state associated with the running configuration\\. In the case of Puppet Server, this file reflects the state discovered through interacting with clients\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/state\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"statettl\"\nHow long the Puppet agent should cache when a resource was last checked or synced\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\. A value of \\fB0\\fR or \\fBunlimited\\fR will disable cache pruning\\.\n.\n.P\nThis setting affects the usage of \\fBschedule\\fR resources, as the information about when a resource was last checked (and therefore when it needs to be checked again) is stored in the \\fBstatefile\\fR\\. The \\fBstatettl\\fR needs to be large enough to ensure that a resource will not trigger multiple times during a schedule due to its entry expiring from the cache\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB32d\\fR\n.\n.IP \"\" 0\n.\n.SS \"static_catalogs\"\nWhether to compile a static catalog \\fIhttps://puppet\\.com/docs/puppet/latest/static_catalogs\\.html#enabling\\-or\\-disabling\\-static\\-catalogs\\fR, which occurs only on Puppet Server when the \\fBcode\\-id\\-command\\fR and \\fBcode\\-content\\-command\\fR settings are configured in its \\fBpuppetserver\\.conf\\fR file\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"storeconfigs\"\nWhether to store each client\\'s configuration, including catalogs, facts, and related data\\. This also enables the import and export of resources in the Puppet language \\- a mechanism for exchange resources between nodes\\.\n.\n.P\nBy default this uses the \\'puppetdb\\' backend\\.\n.\n.P\nYou can adjust the backend using the storeconfigs_backend setting\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"storeconfigs_backend\"\nConfigure the backend terminus used for StoreConfigs\\. By default, this uses the PuppetDB store, which must be installed and configured before turning on StoreConfigs\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppetdb\\fR\n.\n.IP \"\" 0\n.\n.SS \"strict\"\nThe strictness level of puppet\\. Allowed values are:\n.\n.IP \"\\(bu\" 4\noff \\- do not perform extra validation, do not report\n.\n.IP \"\\(bu\" 4\nwarning \\- perform extra validation, report as warning\n.\n.IP \"\\(bu\" 4\nerror \\- perform extra validation, fail with error (default)\n.\n.IP \"\" 0\n.\n.P\nThe strictness level is for both language semantics and runtime evaluation validation\\. In addition to controlling the behavior with this primary server switch some individual warnings may also be controlled by the disable_warnings setting\\.\n.\n.P\nNo new validations will be added to a micro (x\\.y\\.z) release, but may be added in minor releases (x\\.y\\.0)\\. In major releases it expected that most (if not all) strictness validation become standard behavior\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBerror\\fR\n.\n.IP \"\" 0\n.\n.SS \"strict_environment_mode\"\nWhether the agent specified environment should be considered authoritative, causing the run to fail if the retrieved catalog does not match it\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"strict_variables\"\nCauses an evaluation error when referencing unknown variables\\. (This does not affect referencing variables that are explicitly set to undef)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"summarize\"\nWhether to print a transaction summary\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"supported_checksum_types\"\nChecksum types supported by this agent for use in file resources of a static catalog\\. Values must be comma\\-separated\\. Valid types are sha256, sha256lite, sha384, sha512, sha224, sha1, sha1lite, md5, md5lite, mtime, ctime\\. Default is sha256, sha384, sha512, sha224, md5\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB[\"sha256\", \"sha384\", \"sha512\", \"sha224\", \"md5\"]\\fR\n.\n.IP \"\" 0\n.\n.SS \"syslogfacility\"\nWhat syslog facility to use when logging to syslog\\. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBdaemon\\fR\n.\n.IP \"\" 0\n.\n.SS \"tags\"\nTags to use to find resources\\. If this is set, then only resources tagged with the specified tags will be applied\\. Values must be comma\\-separated\\.\n.\n.SS \"tasks\"\nTurns on experimental support for tasks and plans in the puppet language\\. This is for internal API use only\\. Do not change this setting\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"top_level_facts_soft_limit\"\nThe soft limit for the number of top level facts\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB512\\fR\n.\n.IP \"\" 0\n.\n.SS \"trace\"\nWhether to print stack traces on some errors\\. Will print internal Ruby stack trace interleaved with Puppet function frames\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"transactionstorefile\"\nTransactional storage file for persisting data between transactions for the purposes of inferring information (such as corrective_change) on new data received\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$statedir/transactionstore\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"trusted_external_command\"\nThe external trusted facts script or directory to use\\. This setting\\'s value can be set to the path to an executable command that can produce external trusted facts or to a directory containing those executable commands\\. The command(s) must:\n.\n.IP \"\\(bu\" 4\nTake the name of a node as a command\\-line argument\\.\n.\n.IP \"\\(bu\" 4\nReturn a JSON hash with the external trusted facts for this node\\.\n.\n.IP \"\\(bu\" 4\nFor unknown or invalid nodes, exit with a non\\-zero exit code\\.\n.\n.IP \"\" 0\n.\n.P\nIf the setting points to an executable command, then the external trusted facts will be stored in the \\'external\\' key of the trusted facts hash\\. Otherwise for each executable file in the directory, the external trusted facts will be stored in the \\fB<basename>\\fR key of the \\fBtrusted[\\'external\\']\\fR hash\\. For example, if the files foo\\.rb and bar\\.sh are in the directory, then \\fBtrusted[\\'external\\']\\fR will be the hash \\fB{ \\'foo\\' => <foo\\.rb output>, \\'bar\\' => <bar\\.sh output> }\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: ``\n.\n.IP \"\" 0\n.\n.SS \"trusted_oid_mapping_file\"\nFile that provides mapping between custom SSL oids and user\\-friendly names\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$confdir/custom_trusted_oid_mapping\\.yaml\\fR\n.\n.IP \"\" 0\n.\n.SS \"use_cached_catalog\"\nWhether to only use the cached catalog rather than compiling a new catalog on every run\\. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired\\. Because a Puppet agent using cached catalogs does not contact the primary server for a new catalog, it also does not upload facts at the beginning of the Puppet run\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"use_last_environment\"\nPuppet saves both the initial and converged environment in the last_run_summary file\\. If they differ, and this setting is set to true, we will use the last converged environment and skip the node request\\.\n.\n.P\nWhen set to false, we will do the node request and ignore the environment data from the last_run_summary file\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"use_srv_records\"\nWhether the server will search for SRV records in DNS for the current domain\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"usecacheonfailure\"\nWhether to use the cached configuration when the remote configuration will not compile\\. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known\\-good one\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"user\"\nThe user Puppet Server will run as\\. Used to ensure the agent side processes (agent, apply, etc) create files and directories readable by Puppet Server when necessary\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBpuppet\\fR\n.\n.IP \"\" 0\n.\n.SS \"vardir\"\nWhere Puppet stores dynamic and growing data\\. The default for this setting is calculated specially, like \\fBconfdir\\fR_\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBUnix/Linux: /opt/puppetlabs/puppet/cache \\-\\- Windows: C:\\eProgramData\\ePuppetLabs\\epuppet\\ecache \\-\\- Non\\-root user: ~/\\.puppetlabs/opt/puppet/cache\\fR\n.\n.IP \"\" 0\n.\n.SS \"vendormoduledir\"\nThe directory containing \\fBvendored\\fR modules\\. These modules will be used by \\fIall\\fR environments like those in the \\fBbasemodulepath\\fR\\. The only difference is that modules in the \\fBbasemodulepath\\fR are pluginsynced, while vendored modules are not\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB/opt/puppetlabs/puppet/vendor_modules\\fR\n.\n.IP \"\" 0\n.\n.SS \"versioned_environment_dirs\"\nWhether or not to look for versioned environment directories, symlinked from \\fB$environmentpath/<environment>\\fR\\. This is an experimental feature and should be used with caution\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBfalse\\fR\n.\n.IP \"\" 0\n.\n.SS \"waitforcert\"\nHow frequently puppet agent should ask for a signed certificate\\.\n.\n.P\nWhen starting for the first time, puppet agent will submit a certificate signing request (CSR) to the server named in the \\fBca_server\\fR setting (usually the primary Puppet server); this may be autosigned, or may need to be approved by a human, depending on the CA server\\'s configuration\\.\n.\n.P\nPuppet agent cannot apply configurations until its approved certificate is available\\. Since the certificate may or may not be available immediately, puppet agent will repeatedly try to fetch it at this interval\\. You can turn off waiting for certificates by specifying a time of 0, or a maximum amount of time to wait in the \\fBmaxwaitforcert\\fR setting, in which case puppet agent will exit if it cannot get a cert\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB2m\\fR\n.\n.IP \"\" 0\n.\n.SS \"waitforlock\"\nHow frequently puppet agent should try running when there is an already ongoing puppet agent instance\\.\n.\n.P\nThis argument is by default disabled (value set to 0)\\. In this case puppet agent will immediately exit if it cannot run at that moment\\. When a value other than 0 is set, this can also be used in combination with the \\fBmaxwaitforlock\\fR argument\\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB0\\fR\n.\n.IP \"\" 0\n.\n.SS \"write_catalog_summary\"\nWhether to write the \\fBclassfile\\fR and \\fBresourcefile\\fR after applying the catalog\\. It is enabled by default, except when running \\fBpuppet apply\\fR\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fBtrue\\fR\n.\n.IP \"\" 0\n.\n.SS \"yamldir\"\nThe directory in which YAML data is stored, usually in a subdirectory\\.\n.\n.IP \"\\(bu\" 4\n\\fIDefault\\fR: \\fB$vardir/yaml\\fR\n.\n.IP \"\" 0\n\n"
  },
  {
    "path": "man/man8/puppet-agent.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-AGENT\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-agent\\fR \\- The puppet agent daemon\n.\n.SH \"SYNOPSIS\"\nRetrieves the client configuration from the Puppet master and applies it to the local host\\.\n.\n.P\nThis service may be run as a daemon, run periodically using cron (or something similar), or run interactively for testing purposes\\.\n.\n.SH \"USAGE\"\npuppet agent [\\-\\-certname \\fINAME\\fR] [\\-D|\\-\\-daemonize|\\-\\-no\\-daemonize] [\\-d|\\-\\-debug] [\\-\\-detailed\\-exitcodes] [\\-\\-digest \\fIDIGEST\\fR] [\\-\\-disable [MESSAGE]] [\\-\\-enable] [\\-\\-fingerprint] [\\-h|\\-\\-help] [\\-l|\\-\\-logdest syslog|eventlog|\\fIABS FILEPATH\\fR|console] [\\-\\-serverport \\fIPORT\\fR] [\\-\\-noop] [\\-o|\\-\\-onetime] [\\-\\-sourceaddress \\fIIP_ADDRESS\\fR] [\\-t|\\-\\-test] [\\-v|\\-\\-verbose] [\\-V|\\-\\-version] [\\-w|\\-\\-waitforcert \\fISECONDS\\fR]\n.\n.SH \"DESCRIPTION\"\nThis is the main puppet client\\. Its job is to retrieve the local machine\\'s configuration from a remote server and apply it\\. In order to successfully communicate with the remote server, the client must have a certificate signed by a certificate authority that the server trusts; the recommended method for this, at the moment, is to run a certificate authority as part of the puppet server (which is the default)\\. The client will connect and request a signed certificate, and will continue connecting until it receives one\\.\n.\n.P\nOnce the client has a signed certificate, it will retrieve its configuration and apply it\\.\n.\n.SH \"USAGE NOTES\"\n\\'puppet agent\\' does its best to find a compromise between interactive use and daemon use\\. If you run it with no arguments and no configuration, it goes into the background, attempts to get a signed certificate, and retrieves and applies its configuration every 30 minutes\\.\n.\n.P\nSome flags are meant specifically for interactive use \\-\\-\\- in particular, \\'test\\', \\'tags\\' and \\'fingerprint\\' are useful\\.\n.\n.P\n\\'\\-\\-test\\' runs once in the foreground with verbose logging, then exits\\. It also exits if it can\\'t get a valid catalog\\. \\fB\\-\\-test\\fR includes the \\'\\-\\-detailed\\-exitcodes\\' option by default and exits with one of the following exit codes:\n.\n.IP \"\\(bu\" 4\n0: The run succeeded with no changes or failures; the system was already in the desired state\\.\n.\n.IP \"\\(bu\" 4\n1: The run failed, or wasn\\'t attempted due to another run already in progress\\.\n.\n.IP \"\\(bu\" 4\n2: The run succeeded, and some resources were changed\\.\n.\n.IP \"\\(bu\" 4\n4: The run succeeded, and some resources failed\\.\n.\n.IP \"\\(bu\" 4\n6: The run succeeded, and included both changes and failures\\.\n.\n.IP \"\" 0\n.\n.P\n\\'\\-\\-tags\\' allows you to specify what portions of a configuration you want to apply\\. Puppet elements are tagged with all of the class or definition names that contain them, and you can use the \\'tags\\' flag to specify one of these names, causing only configuration elements contained within that class or definition to be applied\\. This is very useful when you are testing new configurations \\-\\-\\- for instance, if you are just starting to manage \\'ntpd\\', you would put all of the new elements into an \\'ntpd\\' class, and call puppet with \\'\\-\\-tags ntpd\\', which would only apply that small portion of the configuration during your testing, rather than applying the whole thing\\.\n.\n.P\n\\'\\-\\-fingerprint\\' is a one\\-time flag\\. In this mode \\'puppet agent\\' runs once and displays on the console (and in the log) the current certificate (or certificate request) fingerprint\\. Providing the \\'\\-\\-digest\\' option allows you to use a different digest algorithm to generate the fingerprint\\. The main use is to verify that before signing a certificate request on the master, the certificate request the master received is the same as the one the client sent (to prevent against man\\-in\\-the\\-middle attacks when signing certificates)\\.\n.\n.P\n\\'\\-\\-skip_tags\\' is a flag used to filter resources\\. If this is set, then only resources not tagged with the specified tags will be applied\\. Values must be comma\\-separated\\.\n.\n.SH \"OPTIONS\"\nNote that any Puppet setting that\\'s valid in the configuration file is also a valid long argument\\. For example, \\'server\\' is a valid setting, so you can specify \\'\\-\\-server \\fIservername\\fR\\' as an argument\\. Boolean settings accept a \\'\\-\\-no\\-\\' prefix to turn off a behavior, translating into \\'\\-\\-setting\\' and \\'\\-\\-no\\-setting\\' pairs, such as \\fB\\-\\-daemonize\\fR and \\fB\\-\\-no\\-daemonize\\fR\\.\n.\n.P\nSee the configuration file documentation at https://puppet\\.com/docs/puppet/latest/configuration\\.html for the full list of acceptable settings\\. A commented list of all settings can also be generated by running puppet agent with \\'\\-\\-genconfig\\'\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-certname: Set the certname (unique ID) of the client\\. The master reads this unique identifying string, which is usually set to the node\\'s fully\\-qualified domain name, to determine which configurations the node will receive\\. Use this option to debug setup problems or implement unusual node identification schemes\\. (This is a Puppet setting, and can go in puppet\\.conf\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-daemonize: Send the process into the background\\. This is the default\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Note the special \\'no\\-\\' prefix for boolean settings on the command line\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-no\\-daemonize: Do not send the process into the background\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Note the special \\'no\\-\\' prefix for boolean settings on the command line\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-debug: Enable full debugging\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-detailed\\-exitcodes: Provide extra information about the run via exit codes; works only if \\'\\-\\-test\\' or \\'\\-\\-onetime\\' is also specified\\. If enabled, \\'puppet agent\\' uses the following exit codes:\n.\n.IP\n0: The run succeeded with no changes or failures; the system was already in the desired state\\.\n.\n.IP\n1: The run failed, or wasn\\'t attempted due to another run already in progress\\.\n.\n.IP\n2: The run succeeded, and some resources were changed\\.\n.\n.IP\n4: The run succeeded, and some resources failed\\.\n.\n.IP\n6: The run succeeded, and included both changes and failures\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-digest: Change the certificate fingerprinting digest algorithm\\. The default is SHA256\\. Valid values depends on the version of OpenSSL installed, but will likely contain MD5, MD2, SHA1 and SHA256\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-disable: Disable working on the local system\\. This puts a lock file in place, causing \\'puppet agent\\' not to work on the system until the lock file is removed\\. This is useful if you are testing a configuration and do not want the central configuration to override the local state until everything is tested and committed\\.\n.\n.IP\nDisable can also take an optional message that will be reported by the \\'puppet agent\\' at the next disabled run\\.\n.\n.IP\n\\'puppet agent\\' uses the same lock file while it is running, so no more than one \\'puppet agent\\' process is working at a time\\.\n.\n.IP\n\\'puppet agent\\' exits after executing this\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-enable: Enable working on the local system\\. This removes any lock file, causing \\'puppet agent\\' to start managing the local system again However, it continues to use its normal scheduling, so it might not start for another half hour\\.\n.\n.IP\n\\'puppet agent\\' exits after executing this\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-evaltrace: Logs each resource as it is being evaluated\\. This allows you to interactively see exactly what is being done\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Note the special \\'no\\-\\' prefix for boolean settings on the command line\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-fingerprint: Display the current certificate or certificate signing request fingerprint and then exit\\. Use the \\'\\-\\-digest\\' option to change the digest algorithm used\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-help: Print this help message\n.\n.IP \"\\(bu\" 4\n\\-\\-job\\-id: Attach the specified job id to the catalog request and the report used for this agent run\\. This option only works when \\'\\-\\-onetime\\' is used\\. When using Puppet Enterprise this flag should not be used as the orchestrator sets the job\\-id for you and it must be unique\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-logdest: Where to send log messages\\. Choose between \\'syslog\\' (the POSIX syslog service), \\'eventlog\\' (the Windows Event Log), \\'console\\', or the path to a log file\\. If debugging or verbosity is enabled, this defaults to \\'console\\'\\. Otherwise, it defaults to \\'syslog\\' on POSIX systems and \\'eventlog\\' on Windows\\. Multiple destinations can be set using a comma separated list (eg: \\fB/path/file1,console,/path/file2\\fR)\"\n.\n.IP\nA path ending with \\'\\.json\\' will receive structured output in JSON format\\. The log file will not have an ending \\']\\' automatically written to it due to the appending nature of logging\\. It must be appended manually to make the content valid JSON\\.\n.\n.IP\nA path ending with \\'\\.jsonl\\' will receive structured output in JSON Lines format\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-masterport: The port on which to contact the Puppet Server\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Deprecated in favor of the \\'serverport\\' setting\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-noop: Use \\'noop\\' mode where the daemon runs in a no\\-op or dry\\-run mode\\. This is useful for seeing what changes Puppet would make without actually executing the changes\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Note the special \\'no\\-\\' prefix for boolean settings on the command line\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-onetime: Run the configuration once\\. Runs a single (normally daemonized) Puppet run\\. Useful for interactively running puppet agent when used in conjunction with the \\-\\-no\\-daemonize option\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Note the special \\'no\\-\\' prefix for boolean settings on the command line\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-serverport: The port on which to contact the Puppet Server\\. (This is a Puppet setting, and can go in puppet\\.conf\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-sourceaddress: Set the source IP address for transactions\\. This defaults to automatically selected\\. (This is a Puppet setting, and can go in puppet\\.conf\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-test: Enable the most common options used for testing\\. These are \\'onetime\\', \\'verbose\\', \\'no\\-daemonize\\', \\'no\\-usecacheonfailure\\', \\'detailed\\-exitcodes\\', \\'no\\-splay\\', and \\'show_diff\\'\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-trace Prints stack traces on some errors\\. (This is a Puppet setting, and can go in puppet\\.conf\\. Note the special \\'no\\-\\' prefix for boolean settings on the command line\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-verbose: Turn on verbose reporting\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-version: Print the puppet version number and exit\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-waitforcert: This option only matters for daemons that do not yet have certificates and it is enabled by default, with a value of 120 (seconds)\\. This causes \\'puppet agent\\' to connect to the server every 2 minutes and ask it to sign a certificate request\\. This is useful for the initial setup of a puppet client\\. You can turn off waiting for certificates by specifying a time of 0\\. (This is a Puppet setting, and can go in puppet\\.conf\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-write_catalog_summary After compiling the catalog saves the resource list and classes list to the node in the state directory named classes\\.txt and resources\\.txt (This is a Puppet setting, and can go in puppet\\.conf\\.)\n.\n.IP \"\" 0\n.\n.SH \"EXAMPLE\"\n.\n.nf\n\n$ puppet agent \\-\\-server puppet\\.domain\\.com\n.\n.fi\n.\n.SH \"DIAGNOSTICS\"\nPuppet agent accepts the following signals:\n.\n.TP\nSIGHUP\nRestart the puppet agent daemon\\.\n.\n.TP\nSIGINT and SIGTERM\nShut down the puppet agent daemon\\.\n.\n.TP\nSIGUSR1\nImmediately retrieve and apply configurations from the puppet master\\.\n.\n.TP\nSIGUSR2\nClose file descriptors for log files and reopen them\\. Used with logrotate\\.\n.\n.SH \"AUTHOR\"\nLuke Kanies\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-apply.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-APPLY\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-apply\\fR \\- Apply Puppet manifests locally\n.\n.SH \"SYNOPSIS\"\nApplies a standalone Puppet manifest to the local system\\.\n.\n.SH \"USAGE\"\npuppet apply [\\-h|\\-\\-help] [\\-V|\\-\\-version] [\\-d|\\-\\-debug] [\\-v|\\-\\-verbose] [\\-e|\\-\\-execute] [\\-\\-detailed\\-exitcodes] [\\-L|\\-\\-loadclasses] [\\-l|\\-\\-logdest syslog|eventlog|\\fIABS FILEPATH\\fR|console] [\\-\\-noop] [\\-\\-catalog \\fIcatalog\\fR] [\\-\\-write\\-catalog\\-summary] \\fIfile\\fR\n.\n.SH \"DESCRIPTION\"\nThis is the standalone puppet execution tool; use it to apply individual manifests\\.\n.\n.P\nWhen provided with a modulepath, via command line or config file, puppet apply can effectively mimic the catalog that would be served by puppet master with access to the same modules, although there are some subtle differences\\. When combined with scheduling and an automated system for pushing manifests, this can be used to implement a serverless Puppet site\\.\n.\n.P\nMost users should use \\'puppet agent\\' and \\'puppet master\\' for site\\-wide manifests\\.\n.\n.SH \"OPTIONS\"\nAny setting that\\'s valid in the configuration file is a valid long argument for puppet apply\\. For example, \\'tags\\' is a valid setting, so you can specify \\'\\-\\-tags \\fIclass\\fR,\\fItag\\fR\\' as an argument\\.\n.\n.P\nSee the configuration file documentation at https://puppet\\.com/docs/puppet/latest/configuration\\.html for the full list of acceptable parameters\\. You can generate a commented list of all configuration options by running puppet with \\'\\-\\-genconfig\\'\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-debug: Enable full debugging\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-detailed\\-exitcodes: Provide extra information about the run via exit codes\\. If enabled, \\'puppet apply\\' will use the following exit codes:\n.\n.IP\n0: The run succeeded with no changes or failures; the system was already in the desired state\\.\n.\n.IP\n1: The run failed\\.\n.\n.IP\n2: The run succeeded, and some resources were changed\\.\n.\n.IP\n4: The run succeeded, and some resources failed\\.\n.\n.IP\n6: The run succeeded, and included both changes and failures\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-help: Print this help message\n.\n.IP \"\\(bu\" 4\n\\-\\-loadclasses: Load any stored classes\\. \\'puppet agent\\' caches configured classes (usually at /etc/puppetlabs/puppet/classes\\.txt), and setting this option causes all of those classes to be set in your puppet manifest\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-logdest: Where to send log messages\\. Choose between \\'syslog\\' (the POSIX syslog service), \\'eventlog\\' (the Windows Event Log), \\'console\\', or the path to a log file\\. Defaults to \\'console\\'\\. Multiple destinations can be set using a comma separated list (eg: \\fB/path/file1,console,/path/file2\\fR)\"\n.\n.IP\nA path ending with \\'\\.json\\' will receive structured output in JSON format\\. The log file will not have an ending \\']\\' automatically written to it due to the appending nature of logging\\. It must be appended manually to make the content valid JSON\\.\n.\n.IP\nA path ending with \\'\\.jsonl\\' will receive structured output in JSON Lines format\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-noop: Use \\'noop\\' mode where Puppet runs in a no\\-op or dry\\-run mode\\. This is useful for seeing what changes Puppet will make without actually executing the changes\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-execute: Execute a specific piece of Puppet code\n.\n.IP \"\\(bu\" 4\n\\-\\-test: Enable the most common options used for testing\\. These are \\'verbose\\', \\'detailed\\-exitcodes\\' and \\'show_diff\\'\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-verbose: Print extra information\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-catalog: Apply a JSON catalog (such as one generated with \\'puppet master \\-\\-compile\\')\\. You can either specify a JSON file or pipe in JSON from standard input\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-write\\-catalog\\-summary After compiling the catalog saves the resource list and classes list to the node in the state directory named classes\\.txt and resources\\.txt\n.\n.IP \"\" 0\n.\n.SH \"EXAMPLE\"\n.\n.nf\n\n$ puppet apply \\-e \\'notify { \"hello world\": }\\'\n$ puppet apply \\-l /tmp/manifest\\.log manifest\\.pp\n$ puppet apply \\-\\-modulepath=/root/dev/modules \\-e \"include ntpd::server\"\n$ puppet apply \\-\\-catalog catalog\\.json\n.\n.fi\n.\n.SH \"AUTHOR\"\nLuke Kanies\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-catalog.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-CATALOG\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-catalog\\fR \\- Compile, save, view, and convert catalogs\\.\n.\n.SH \"SYNOPSIS\"\npuppet catalog \\fIaction\\fR [\\-\\-terminus _TERMINUS]\n.\n.SH \"DESCRIPTION\"\nThis subcommand deals with catalogs, which are compiled per\\-node artifacts generated from a set of Puppet manifests\\. By default, it interacts with the compiling subsystem and compiles a catalog using the default manifest and \\fBcertname\\fR, but you can change the source of the catalog with the \\fB\\-\\-terminus\\fR option\\. You can also choose to print any catalog in \\'dot\\' format (for easy graph viewing with OmniGraffle or Graphviz) with \\'\\-\\-render\\-as dot\\'\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.TP\n\\-\\-terminus _TERMINUS\nIndirector faces expose indirected subsystems of Puppet\\. These subsystems are each able to retrieve and alter a specific type of data (with the familiar actions of \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR) from an arbitrary number of pluggable backends\\. In Puppet parlance, these backends are called terminuses\\.\n.\n.IP\nAlmost all indirected subsystems have a \\fBrest\\fR terminus that interacts with the puppet master\\'s data\\. Most of them have additional terminuses for various local data models, which are in turn used by the indirected subsystem on the puppet master whenever it receives a remote request\\.\n.\n.IP\nThe terminus for an action is often determined by context, but occasionally needs to be set explicitly\\. See the \"Notes\" section of this face\\'s manpage for more details\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBapply\\fR \\- Find and apply a catalog\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog apply [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nFinds and applies a catalog\\. This action takes no arguments, but the source of the catalog can be managed with the \\fB\\-\\-terminus\\fR option\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nNothing\\. When used from the Ruby API, returns a Puppet::Transaction::Report object\\.\n.\n.TP\n\\fBcompile\\fR \\- Compile a catalog\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog compile [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nCompiles a catalog locally for a node, requiring access to modules, node classifier, etc\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA serialized catalog\\.\n.\n.TP\n\\fBdownload\\fR \\- Download this node\\'s catalog from the puppet master server\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog download [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nRetrieves a catalog from the puppet master and saves it to the local yaml cache\\. This action always contacts the puppet master and will ignore alternate termini\\.\n.\n.IP\nThe saved catalog can be used in any subsequent catalog action by specifying \\'\\-\\-terminus yaml\\' for that action\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nNothing\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nWhen used from the Ruby API, this action has a side effect of leaving Puppet::Resource::Catalog\\.indirection\\.terminus_class set to yaml\\. The terminus must be explicitly re\\-set for subsequent catalog actions\\.\n.\n.TP\n\\fBfind\\fR \\- Retrieve the catalog for the node from which the command is run\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog find [\\-\\-terminus _TERMINUS] [\\-\\-facts_for_catalog] \\fIcertname\\fR, \\fIfacts\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nRetrieve the catalog for the node from which the command is run\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-facts_for_catalog\\fR \\- Not implemented for the CLI; facts are collected internally\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA serialized catalog\\. When used from the Ruby API, returns a Puppet::Resource::Catalog object\\.\n.\n.TP\n\\fBinfo\\fR \\- Print the default terminus class for this face\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog info [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nPrints the default terminus class for this subcommand\\. Note that different run modes may have different default termini; when in doubt, specify the run mode with the \\'\\-\\-run_mode\\' option\\.\n.\n.TP\n\\fBsave\\fR \\- API only: create or overwrite an object\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog save [\\-\\-terminus _TERMINUS] \\fIkey\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nAPI only: create or overwrite an object\\. As the Faces framework does not currently accept data from STDIN, save actions cannot currently be invoked from the command line\\.\n.\n.TP\n\\fBselect\\fR \\- Retrieve a catalog and filter it for resources of a given type\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet catalog select [\\-\\-terminus _TERMINUS] \\fIhost\\fR \\fIresource_type\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nRetrieves a catalog for the specified host, then searches it for all resources of the requested type\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA list of resource references (\"Type[title]\")\\. When used from the API, returns an array of Puppet::Resource objects excised from a catalog\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nBy default, this action will retrieve a catalog from Puppet\\'s compiler subsystem; you must call the action with \\fB\\-\\-terminus rest\\fR if you wish to retrieve a catalog from the puppet master\\.\n.\n.IP\nFORMATTING ISSUES: This action cannot currently render useful yaml; instead, it returns an entire catalog\\. Use json instead\\.\n.\n.SH \"EXAMPLES\"\n\\fBapply\\fR\n.\n.P\nApply the locally cached catalog:\n.\n.P\n$ puppet catalog apply \\-\\-terminus yaml\n.\n.P\nRetrieve a catalog from the master and apply it, in one step:\n.\n.P\n$ puppet catalog apply \\-\\-terminus rest\n.\n.P\nAPI example:\n.\n.IP \"\" 4\n.\n.nf\n\n# \\.\\.\\.\nPuppet::Face[:catalog, \\'0\\.0\\.1\\']\\.download\n# (Termini are singletons; catalog\\.download has a side effect of\n# setting the catalog terminus to yaml)\nreport  = Puppet::Face[:catalog, \\'0\\.0\\.1\\']\\.apply\n# \\.\\.\\.\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\n\\fBcompile\\fR\n.\n.P\nCompile catalog for node \\'mynode\\':\n.\n.P\n$ puppet catalog compile mynode \\-\\-codedir \\.\\.\\.\n.\n.P\n\\fBdownload\\fR\n.\n.P\nRetrieve and store a catalog:\n.\n.P\n$ puppet catalog download\n.\n.P\nAPI example:\n.\n.IP \"\" 4\n.\n.nf\n\nPuppet::Face[:plugin, \\'0\\.0\\.1\\']\\.download\nPuppet::Face[:facts, \\'0\\.0\\.1\\']\\.upload\nPuppet::Face[:catalog, \\'0\\.0\\.1\\']\\.download\n# \\.\\.\\.\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\n\\fBselect\\fR\n.\n.P\nAsk the puppet master for a list of managed file resources for a node:\n.\n.P\n$ puppet catalog select \\-\\-terminus rest somenode\\.magpie\\.lan file\n.\n.SH \"NOTES\"\nThis subcommand is an indirector face, which exposes \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR actions for an indirected subsystem of Puppet\\. Valid termini for this face include:\n.\n.IP \"\\(bu\" 4\n\\fBcompiler\\fR\n.\n.IP \"\\(bu\" 4\n\\fBjson\\fR\n.\n.IP \"\\(bu\" 4\n\\fBmsgpack\\fR\n.\n.IP \"\\(bu\" 4\n\\fBrest\\fR\n.\n.IP \"\\(bu\" 4\n\\fBstore_configs\\fR\n.\n.IP \"\\(bu\" 4\n\\fByaml\\fR\n.\n.IP \"\" 0\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-config.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-CONFIG\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-config\\fR \\- Interact with Puppet\\'s settings\\.\n.\n.SH \"SYNOPSIS\"\npuppet config \\fIaction\\fR [\\-\\-section SECTION_NAME]\n.\n.SH \"DESCRIPTION\"\nThis subcommand can inspect and modify settings from Puppet\\'s \\'puppet\\.conf\\' configuration file\\. For documentation about individual settings, see https://puppet\\.com/docs/puppet/latest/configuration\\.html\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.TP\n\\-\\-section SECTION_NAME\nThe section of the puppet\\.conf configuration file to interact with\\.\n.\n.IP\nThe three most commonly used sections are \\'main\\', \\'server\\', and \\'agent\\'\\. \\'Main\\' is the default, and is used by all Puppet applications\\. Other sections can override \\'main\\' values for specific applications \\-\\-\\- the \\'server\\' section affects Puppet Server, and the \\'agent\\' section affects puppet agent\\.\n.\n.IP\nLess commonly used is the \\'user\\' section, which affects puppet apply\\. Any other section will be treated as the name of a legacy environment (a deprecated feature), and can only include the \\'manifest\\' and \\'modulepath\\' settings\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBdelete\\fR \\- Delete a Puppet setting\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet config delete [\\-\\-section SECTION_NAME] \\fIsetting\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nDeletes a setting from the specified section\\. (The default is the section \\'main\\')\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nBy default, this action deletes the configuration setting from the \\'main\\' configuration domain\\. Use the \\'\\-\\-section\\' flags to delete settings from other configuration domains\\.\n.\n.TP\n\\fBprint\\fR \\- Examine Puppet\\'s current settings\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet config print [\\-\\-section SECTION_NAME] all | \\fIsetting\\fR [\\fIsetting\\fR \\.\\.\\.]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nPrints the value of a single setting or a list of settings\\.\n.\n.IP\nThis action is a replacement interface to the information available with \\fBpuppet <subcommand> \\-\\-configprint\\fR\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nBy default, this action reads the general configuration in the \\'main\\' section\\. Use the \\'\\-\\-section\\' and \\'\\-\\-environment\\' flags to examine other configuration domains\\.\n.\n.TP\n\\fBset\\fR \\- Set Puppet\\'s settings\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet config set [\\-\\-section SECTION_NAME] [setting_name] [setting_value]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nUpdates values in the \\fBpuppet\\.conf\\fR configuration file\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nBy default, this action manipulates the configuration in the \\'main\\' section\\. Use the \\'\\-\\-section\\' flag to manipulate other configuration domains\\.\n.\n.SH \"EXAMPLES\"\n\\fBdelete\\fR\n.\n.P\nDelete the setting \\'setting_name\\' from the \\'main\\' configuration domain:\n.\n.P\n$ puppet config delete setting_name\n.\n.P\nDelete the setting \\'setting_name\\' from the \\'server\\' configuration domain:\n.\n.P\n$ puppet config delete setting_name \\-\\-section server\n.\n.P\n\\fBprint\\fR\n.\n.P\nGet puppet\\'s runfile directory:\n.\n.P\n$ puppet config print rundir\n.\n.P\nGet a list of important directories from the server\\'s config:\n.\n.P\n$ puppet config print all \\-\\-section server | grep \\-E \"(path|dir)\"\n.\n.P\n\\fBset\\fR\n.\n.P\nSet puppet\\'s runfile directory:\n.\n.P\n$ puppet config set rundir /var/run/puppetlabs\n.\n.P\nSet the vardir for only the agent:\n.\n.P\n$ puppet config set vardir /opt/puppetlabs/puppet/cache \\-\\-section agent\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-describe.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-DESCRIBE\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-describe\\fR \\- Display help about resource types\n.\n.SH \"SYNOPSIS\"\nPrints help about Puppet resource types, providers, and metaparameters\\.\n.\n.SH \"USAGE\"\npuppet describe [\\-h|\\-\\-help] [\\-s|\\-\\-short] [\\-p|\\-\\-providers] [\\-l|\\-\\-list] [\\-m|\\-\\-meta]\n.\n.SH \"OPTIONS\"\n.\n.TP\n\\-\\-help\nPrint this help text\n.\n.TP\n\\-\\-providers\nDescribe providers in detail for each type\n.\n.TP\n\\-\\-list\nList all types\n.\n.TP\n\\-\\-meta\nList all metaparameters\n.\n.TP\n\\-\\-short\nList only parameters without detail\n.\n.SH \"EXAMPLE\"\n.\n.nf\n\n$ puppet describe \\-\\-list\n$ puppet describe file \\-\\-providers\n$ puppet describe user \\-s \\-m\n.\n.fi\n.\n.SH \"AUTHOR\"\nDavid Lutterkort\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-device.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-DEVICE\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-device\\fR \\- Manage remote network devices\n.\n.SH \"SYNOPSIS\"\nRetrieves catalogs from the Puppet master and applies them to remote devices\\.\n.\n.P\nThis subcommand can be run manually; or periodically using cron, a scheduled task, or a similar tool\\.\n.\n.SH \"USAGE\"\npuppet device [\\-h|\\-\\-help] [\\-v|\\-\\-verbose] [\\-d|\\-\\-debug] [\\-l|\\-\\-logdest syslog|\\fIfile\\fR|console] [\\-\\-detailed\\-exitcodes] [\\-\\-deviceconfig \\fIfile\\fR] [\\-w|\\-\\-waitforcert \\fIseconds\\fR] [\\-\\-libdir \\fIdirectory\\fR] [\\-a|\\-\\-apply \\fIfile\\fR] [\\-f|\\-\\-facts] [\\-r|\\-\\-resource \\fItype\\fR [name]] [\\-t|\\-\\-target \\fIdevice\\fR] [\\-\\-user=\\fIuser\\fR] [\\-V|\\-\\-version]\n.\n.SH \"DESCRIPTION\"\nDevices require a proxy Puppet agent to request certificates, collect facts, retrieve and apply catalogs, and store reports\\.\n.\n.SH \"USAGE NOTES\"\nDevices managed by the puppet\\-device subcommand on a Puppet agent are configured in device\\.conf, which is located at $confdir/device\\.conf by default, and is configurable with the $deviceconfig setting\\.\n.\n.P\nThe device\\.conf file is an INI\\-like file, with one section per device:\n.\n.P\n[\\fIDEVICE_CERTNAME\\fR] type \\fITYPE\\fR url \\fIURL\\fR debug\n.\n.P\nThe section name specifies the certname of the device\\.\n.\n.P\nThe values for the type and url properties are specific to each type of device\\.\n.\n.P\nThe optional debug property specifies transport\\-level debugging, and is limited to telnet and ssh transports\\.\n.\n.P\nSee https://puppet\\.com/docs/puppet/latest/config_file_device\\.html for details\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument\\. For example, \\'server\\' is a valid configuration parameter, so you can specify \\'\\-\\-server \\fIservername\\fR\\' as an argument\\.\n.\n.TP\n\\-\\-help, \\-h\nPrint this help message\n.\n.TP\n\\-\\-verbose, \\-v\nTurn on verbose reporting\\.\n.\n.TP\n\\-\\-debug, \\-d\nEnable full debugging\\.\n.\n.TP\n\\-\\-logdest, \\-l\nWhere to send log messages\\. Choose between \\'syslog\\' (the POSIX syslog service), \\'console\\', or the path to a log file\\. If debugging or verbosity is enabled, this defaults to \\'console\\'\\. Otherwise, it defaults to \\'syslog\\'\\. Multiple destinations can be set using a comma separated list (eg: \\fB/path/file1,console,/path/file2\\fR)\"\n.\n.IP\nA path ending with \\'\\.json\\' will receive structured output in JSON format\\. The log file will not have an ending \\']\\' automatically written to it due to the appending nature of logging\\. It must be appended manually to make the content valid JSON\\.\n.\n.TP\n\\-\\-detailed\\-exitcodes\nProvide transaction information via exit codes\\. If this is enabled, an exit code of \\'1\\' means at least one device had a compile failure, an exit code of \\'2\\' means at least one device had resource changes, and an exit code of \\'4\\' means at least one device had resource failures\\. Exit codes of \\'3\\', \\'5\\', \\'6\\', or \\'7\\' means that a bitwise combination of the preceding exit codes happened\\.\n.\n.TP\n\\-\\-deviceconfig\nPath to the device config file for puppet device\\. Default: $confdir/device\\.conf\n.\n.TP\n\\-\\-waitforcert, \\-w\nThis option only matters for targets that do not yet have certificates and it is enabled by default, with a value of 120 (seconds)\\. This causes +puppet device+ to poll the server every 2 minutes and ask it to sign a certificate request\\. This is useful for the initial setup of a target\\. You can turn off waiting for certificates by specifying a time of 0\\.\n.\n.TP\n\\-\\-libdir\nOverride the per\\-device libdir with a local directory\\. Specifying a libdir also disables pluginsync\\. This is useful for testing\\.\n.\n.IP\nA path ending with \\'\\.jsonl\\' will receive structured output in JSON Lines format\\.\n.\n.TP\n\\-\\-apply\nApply a manifest against a remote target\\. Target must be specified\\.\n.\n.TP\n\\-\\-facts\nDisplays the facts of a remote target\\. Target must be specified\\.\n.\n.TP\n\\-\\-resource\nDisplays a resource state as Puppet code, roughly equivalent to \\fBpuppet resource\\fR\\. Can be filtered by title\\. Requires \\-\\-target be specified\\.\n.\n.TP\n\\-\\-target\nTarget a specific device/certificate in the device\\.conf\\. Doing so will perform a device run against only that device/certificate\\.\n.\n.TP\n\\-\\-to_yaml\nOutput found resources in yaml format, suitable to use with Hiera and create_resources\\.\n.\n.TP\n\\-\\-user\nThe user to run as\\.\n.\n.SH \"EXAMPLE\"\n.\n.nf\n\n  $ puppet device \\-\\-target remotehost \\-\\-verbose\n.\n.fi\n.\n.SH \"AUTHOR\"\nBrice Figureau\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011\\-2018 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-doc.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-DOC\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-doc\\fR \\- Generate Puppet references\n.\n.SH \"SYNOPSIS\"\nGenerates a reference for all Puppet types\\. Largely meant for internal Puppet Inc\\. use\\. (Deprecated)\n.\n.SH \"USAGE\"\npuppet doc [\\-h|\\-\\-help] [\\-l|\\-\\-list] [\\-r|\\-\\-reference \\fIreference\\-name\\fR]\n.\n.SH \"DESCRIPTION\"\nThis deprecated command generates a Markdown document to stdout describing all installed Puppet types or all allowable arguments to puppet executables\\. It is largely meant for internal use and is used to generate the reference document available on the Puppet Inc\\. web site\\.\n.\n.P\nFor Puppet module documentation (and all other use cases) this command has been superseded by the \"puppet\\-strings\" module \\- see https://github\\.com/puppetlabs/puppetlabs\\-strings for more information\\.\n.\n.P\nThis command (puppet\\-doc) will be removed once the puppetlabs internal documentation processing pipeline is completely based on puppet\\-strings\\.\n.\n.SH \"OPTIONS\"\n.\n.TP\n\\-\\-help\nPrint this help message\n.\n.TP\n\\-\\-reference\nBuild a particular reference\\. Get a list of references by running \\'puppet doc \\-\\-list\\'\\.\n.\n.SH \"EXAMPLE\"\n.\n.nf\n\n$ puppet doc \\-r type > /tmp/type_reference\\.markdown\n.\n.fi\n.\n.SH \"AUTHOR\"\nLuke Kanies\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-epp.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-EPP\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-epp\\fR \\- Interact directly with the EPP template parser/renderer\\.\n.\n.SH \"SYNOPSIS\"\npuppet epp \\fIaction\\fR\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.SH \"ACTIONS\"\n.\n.IP \"\\(bu\" 4\n\\fBdump\\fR \\- Outputs a dump of the internal template parse tree for debugging: \\fBSYNOPSIS\\fR\n.\n.IP\npuppet epp dump [\\-\\-e \\fIsource\\fR] [\\-\\-[no\\-]validate] [\\-\\-format \\fIold, pn, or json\\fR] [\\-\\-pretty] [\\-\\-[no\\-]header] [\\-\\-format \\fIold|pn|json\\fR] [\\-\\-pretty] { \\-e \\fIsource\\fR | [\\fItemplates\\fR \\.\\.\\.] }\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nThe dump action parses and validates the EPP syntax and dumps the resulting AST model in a human readable (but not necessarily an easy to understand) format\\.\n.\n.IP\nThe output format can be controlled using the \\-\\-format \\fIold|pn|json\\fR where:\n.\n.IP \"\\(bu\" 4\n\\'old\\' is the default, but now deprecated format which is not API\\.\n.\n.IP \"\\(bu\" 4\n\\'pn\\' is the Puppet Extended S\\-Expression Notation\\.\n.\n.IP \"\\(bu\" 4\n\\'json\\' outputs the same graph as \\'pn\\' but with JSON syntax\\.\n.\n.IP \"\" 0\n.\n.IP\nThe output will be \"pretty printed\" when the option \\-\\-pretty is given together with \\-\\-format \\'pn\\' or \\'json\\'\\. This option has no effect on the \\'old\\' format\\.\n.\n.IP\nThe command accepts one or more templates (\\.epp) files, or an \\-e followed by the template source text\\. The given templates can be paths to template files, or references to templates in modules when given on the form \\fImodulename\\fR/\\fItemplate\\-name\\fR\\.epp\\. If no arguments are given, the stdin is read (unless it is attached to a terminal)\n.\n.IP\nIf multiple templates are given, they are separated with a header indicating the name of the template\\. This can be suppressed with the option \\-\\-no\\-header\\. The option \\-\\-[no\\-]header has no effect when a single template is dumped\\.\n.\n.IP\nWhen debugging the epp parser itself, it may be useful to suppress the validation step with the \\fB\\-\\-no\\-validate\\fR option to observe what the parser produced from the given source\\.\n.\n.IP\nThis command ignores the \\-\\-render\\-as setting/option\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-e <source\\fR> \\- Dump one epp source expression given on the command line\\.\n.\n.IP\n\\fI\\-\\-format <old, pn, or json\\fR> \\- Get result in \\'old\\' (deprecated format), \\'pn\\' (new format), or \\'json\\' (new format in JSON)\\.\n.\n.IP\n\\fI\\-\\-[no\\-]header\\fR \\- Whether or not to show a file name header between files\\.\n.\n.IP\n\\fI\\-\\-pretty\\fR \\- Pretty print output\\. Only applicable together with \\-\\-format pn or json\n.\n.IP\n\\fI\\-\\-[no\\-]validate\\fR \\- Whether or not to validate the parsed result, if no\\-validate only syntax errors are reported\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA dump of the resulting AST model unless there are syntax or validation errors\\.\n.\n.IP \"\\(bu\" 4\n\\fBrender\\fR \\- Renders an epp template as text: \\fBSYNOPSIS\\fR\n.\n.IP\npuppet epp render [\\-\\-node \\fInode_name\\fR] [\\-\\-e \\fIsource\\fR] [\\-\\-values \\fIvalues_hash\\fR] [\\-\\-values_file \\fIpp_or_yaml_file\\fR] [\\-\\-facts \\fIfacts_file\\fR] [\\-\\-[no\\-]header] \\-e \\fIsource\\fR | [\\fItemplates\\fR \\.\\.\\.]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nThis action renders one or more EPP templates\\.\n.\n.IP\nThe command accepts one or more templates (\\.epp files), given the same way as templates are given to the puppet \\fBepp\\fR function (a full path, or a relative reference on the form \\'\\fImodulename\\fR/\\fItemplate\\-name\\fR\\.epp\\'), or as a relative path\\.args In case the given path matches both a modulename/template and a file, the template from the module is used\\.\n.\n.IP\nAn inline_epp equivalent can also be performed by giving the template after an \\-e, or by piping the EPP source text to the command\\.\n.\n.IP\nValues to the template can be defined using the Puppet Language on the command line with \\fB\\-\\-values\\fR or in a \\.pp or \\.yaml file referenced with \\fB\\-\\-values_file\\fR\\. If specifying both the result is merged with \\-\\-values having higher precedence\\.\n.\n.IP\nThe \\-\\-values option allows a Puppet Language sequence of expressions to be defined on the command line the same way as it may be given in a \\.pp file referenced with \\fB\\-\\-values_file\\fR\\. It may set variable values (that become available in the template), and must produce either \\fBundef\\fR or a \\fBHash\\fR of values (the hash may be empty)\\. Producing \\fBundef\\fR simulates that the template is called without an arguments hash and thus only references variables in its outer scope\\. When a hash is given, a template is limited to seeing only the global scope\\. It is thus possible to simulate the different types of calls to the \\fBepp\\fR and \\fBinline_epp\\fR functions, with or without a given hash\\. Note that if variables are given, they are always available in this simulation \\- to test that the template only references variables given as arguments, produce a hash in \\-\\-values or the \\-\\-values_file, do not specify any variables that are not global, and turn on \\-\\-strict_variables setting\\.\n.\n.IP\nIf multiple templates are given, the same set of values are given to each template\\. If both \\-\\-values and \\-\\-value_file are used, the \\-\\-values are merged on top of those given in the file\\.\n.\n.IP\nWhen multiple templates are rendered, a separating header is output between the templates showing the name of the template before the output\\. The header output can be turned off with \\fB\\-\\-no\\-header\\fR\\. This also concatenates the template results without any added newline separators\\.\n.\n.IP\nFacts from the node where the command is being run are used by default\\.args Facts can be obtained for other nodes if they have called in, and reported their facts by using the \\fB\\-\\-node <nodename>\\fR flag\\.\n.\n.IP\nOverriding node facts as well as additional facts can be given in a \\.yaml or \\.json file and referencing it with the \\-\\-facts option\\. (Values can be obtained in yaml format directly from \\fBfacter\\fR, or from puppet for a given node)\\. Note that it is not possible to simulate the reserved variable name \\fB$facts\\fR in any other way\\.\n.\n.IP\nNote that it is not possible to set variables using the Puppet Language that have the same names as facts as this result in an error; \"attempt to redefine a variable\" since facts are set first\\.\n.\n.IP\nExits with 0 if there were no validation errors\\. On errors, no rendered output is produced for that template file\\.\n.\n.IP\nWhen designing EPP templates, it is strongly recommended to define all template arguments in the template, and to give them in a hash when calling \\fBepp\\fR or \\fBinline_epp\\fR and to use as few global variables as possible, preferably only the $facts hash\\. This makes templates more free standing and are easier to reuse, and to test\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-e <source\\fR> \\- Render one inline epp template given on the command line\\.\n.\n.IP\n\\fI\\-\\-facts <facts_file\\fR> \\- A \\.yaml or \\.json file containing a hash of facts made available in $facts and $trusted\n.\n.IP\n\\fI\\-\\-[no\\-]header\\fR \\- Whether or not to show a file name header between rendered results\\.\n.\n.IP\n\\fI\\-\\-node <node_name\\fR> \\- The name of the node for which facts are obtained\\. Defaults to facts for the local node\\.\n.\n.IP\n\\fI\\-\\-values <values_hash\\fR> \\- A Hash in Puppet DSL form given as arguments to the template being rendered\\.\n.\n.IP\n\\fI\\-\\-values_file <pp_or_yaml_file\\fR> \\- A \\.pp or \\.yaml file that is processed to produce a hash of values for the template\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA rendered result of one or more given templates\\.\n.\n.IP \"\\(bu\" 4\n\\fBvalidate\\fR \\- Validate the syntax of one or more EPP templates\\.: \\fBSYNOPSIS\\fR\n.\n.IP\npuppet epp validate [\\-\\-[no\\-]continue_on_error] [\\fItemplate\\fR] [\\fItemplate\\fR \\.\\.\\.]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nThis action validates EPP syntax without producing any output\\.\n.\n.IP\nWhen validating, multiple issues per file are reported up to the settings of max_error, and max_warnings\\. The processing stops after having reported issues for the first encountered file with errors unless the option \\-\\-continue_on_error is given\\.\n.\n.IP\nFiles can be given using the \\fBmodulename/template\\.epp\\fR style to lookup the template from a module, or be given as a reference to a file\\. If the reference to a file can be resolved against a template in a module, the module version wins \\- in this case use an absolute path to reference the template file if the module version is not wanted\\.\n.\n.IP\nExits with 0 if there were no validation errors\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-[no\\-]continue_on_error\\fR \\- Whether or not to continue after errors are reported for a template\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nNothing, or encountered syntax errors\\.\n.\n.IP \"\" 0\n.\n.SH \"EXAMPLES\"\n\\fBrender\\fR\n.\n.P\nRender the template in module \\'mymodule\\' called \\'mytemplate\\.epp\\', and give it two arguments \\fBa\\fR and \\fBb\\fR:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp render mymodule/mytemplate\\.epp \\-\\-values \\'{a => 10, b => 20}\\'\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nRender a template using an absolute path:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp render /tmp/testing/mytemplate\\.epp \\-\\-values \\'{a => 10, b => 20}\\'\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nRender a template with data from a \\.pp file:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp render /tmp/testing/mytemplate\\.epp \\-\\-values_file mydata\\.pp\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nRender a template with data from a \\.pp file and override one value on the command line:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp render /tmp/testing/mytemplate\\.epp \\-\\-values_file mydata\\.pp \\-\\-values \\'{a=>10}\\'\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nRender from STDIN:\n.\n.IP \"\" 4\n.\n.nf\n\n$ cat template\\.epp | puppet epp render \\-\\-values \\'{a => 10, b => 20}\\'\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nSet variables in a \\.pp file and render a template that uses variable references:\n.\n.IP \"\" 4\n.\n.nf\n\n# data\\.pp file\n$greeted = \\'a global var\\'\nundef\n\n$ puppet epp render \\-e \\'hello <%= $greeted %>\\' \\-\\-values_file data\\.pp\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nRender a template that outputs a fact:\n.\n.IP \"\" 4\n.\n.nf\n\n$ facter \\-\\-yaml > data\\.yaml\n$ puppet epp render \\-e \\'<% $facts[osfamily] %>\\' \\-\\-facts data\\.yaml\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\n\\fBvalidate\\fR\n.\n.P\nValidate the template \\'template\\.epp\\' in module \\'mymodule\\':\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp validate mymodule/template\\.epp\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nValidate two arbitrary template files:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp validate mymodule/template1\\.epp yourmodule/something\\.epp\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nValidate a template somewhere in the file system:\n.\n.IP \"\" 4\n.\n.nf\n\n  $ puppet epp validate /tmp/testing/template1\\.epp\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nValidate a template against a file relative to the current directory:\n.\n.IP \"\" 4\n.\n.nf\n\n $ puppet epp validate template1\\.epp\n $ puppet epp validate \\./template1\\.epp\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nValidate from STDIN:\n.\n.IP \"\" 4\n.\n.nf\n\n$ cat template\\.epp | puppet epp validate\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nContinue on error to see errors for all templates:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet epp validate mymodule/template1\\.epp mymodule/template2\\.epp \\-\\-continue_on_error\n.\n.fi\n.\n.IP \"\" 0\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2014 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-facts.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-FACTS\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-facts\\fR \\- Retrieve and store facts\\.\n.\n.SH \"SYNOPSIS\"\npuppet facts \\fIaction\\fR [\\-\\-terminus _TERMINUS]\n.\n.SH \"DESCRIPTION\"\nThis subcommand manages facts, which are collections of normalized system information used by Puppet\\. It can read facts directly from the local system (with the default \\fBfacter\\fR terminus)\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.TP\n\\-\\-terminus _TERMINUS\nIndirector faces expose indirected subsystems of Puppet\\. These subsystems are each able to retrieve and alter a specific type of data (with the familiar actions of \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR) from an arbitrary number of pluggable backends\\. In Puppet parlance, these backends are called terminuses\\.\n.\n.IP\nAlmost all indirected subsystems have a \\fBrest\\fR terminus that interacts with the puppet master\\'s data\\. Most of them have additional terminuses for various local data models, which are in turn used by the indirected subsystem on the puppet master whenever it receives a remote request\\.\n.\n.IP\nThe terminus for an action is often determined by context, but occasionally needs to be set explicitly\\. See the \"Notes\" section of this face\\'s manpage for more details\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBfind\\fR \\- Retrieve a node\\'s facts\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet facts find [\\-\\-terminus _TERMINUS] [\\fInode_certname\\fR]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nRetrieve a node\\'s facts\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA hash containing some metadata and (under the \"values\" key) the set of facts for the requested node\\. When used from the Ruby API: A Puppet::Node::Facts object\\.\n.\n.IP\nRENDERING ISSUES: Facts cannot currently be rendered as a string; use yaml or json\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nWhen using the \\fBfacter\\fR terminus, the host argument is ignored\\.\n.\n.TP\n\\fBinfo\\fR \\- Print the default terminus class for this face\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet facts info [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nPrints the default terminus class for this subcommand\\. Note that different run modes may have different default termini; when in doubt, specify the run mode with the \\'\\-\\-run_mode\\' option\\.\n.\n.TP\n\\fBsave\\fR \\- API only: create or overwrite an object\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet facts save [\\-\\-terminus _TERMINUS] \\fIkey\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nAPI only: create or overwrite an object\\. As the Faces framework does not currently accept data from STDIN, save actions cannot currently be invoked from the command line\\.\n.\n.TP\n\\fBshow\\fR \\- Retrieve current node\\'s facts\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet facts [\\-\\-terminus _TERMINUS] [\\-\\-config\\-file \\fIpath\\fR] [\\-\\-custom\\-dir \\fIpath\\fR] [\\-\\-external\\-dir \\fIpath\\fR] [\\-\\-no\\-block] [\\-\\-no\\-cache] [\\-\\-show\\-legacy] [\\-\\-value\\-only] [\\-\\-timing] [\\fIfacts\\fR]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nReads facts from the local system using \\fBfacter\\fR terminus\\. A query can be provided to retrieve just a specific fact or a set of facts\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-config\\-file <path\\fR> \\- The location of the config file for Facter\\.\n.\n.IP\n\\fI\\-\\-custom\\-dir <path\\fR> \\- The path to a directory that contains custom facts\\.\n.\n.IP\n\\fI\\-\\-external\\-dir <path\\fR> \\- The path to a directory that contains external facts\\.\n.\n.IP\n\\fI\\-\\-no\\-block\\fR \\- Disable fact blocking mechanism\\.\n.\n.IP\n\\fI\\-\\-no\\-cache\\fR \\- Disable fact caching mechanism\\.\n.\n.IP\n\\fI\\-\\-show\\-legacy\\fR \\- Show legacy facts when querying all facts\\.\n.\n.IP\n\\fI\\-\\-timing\\fR \\- Show how much time it took to resolve each fact\\.\n.\n.IP\n\\fI\\-\\-value\\-only\\fR \\- Show only the value when the action is called with a single query\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nThe output of facter with added puppet specific facts\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.TP\n\\fBupload\\fR \\- Upload local facts to the puppet master\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet facts upload [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nReads facts from the local system using the \\fBfacter\\fR terminus, then saves the returned facts using the rest terminus\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nNothing\\.\n.\n.IP\n\\fBNOTES\\fR\n.\n.IP\nThis action requires that the Puppet Server\\'s \\fBauth\\.conf\\fR file allow \\fBPUT\\fR or \\fBsave\\fR access to the \\fB/puppet/v3/facts\\fR API endpoint\\.\n.\n.IP\nFor details on configuring Puppet Server\\'s \\fBauth\\.conf\\fR, see:\n.\n.IP\n\\fIhttps://puppet\\.com/docs/puppetserver/latest/config_file_auth\\.html\\fR\n.\n.SH \"EXAMPLES\"\n\\fBfind\\fR\n.\n.P\nGet facts from the local system:\n.\n.P\n$ puppet facts find\n.\n.P\n\\fBshow\\fR\n.\n.P\nretrieve facts:\n.\n.P\n$ puppet facts show os\n.\n.P\n\\fBupload\\fR\n.\n.P\nUpload facts:\n.\n.P\n$ puppet facts upload\n.\n.SH \"NOTES\"\nThis subcommand is an indirector face, which exposes \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR actions for an indirected subsystem of Puppet\\. Valid termini for this face include:\n.\n.IP \"\\(bu\" 4\n\\fBfacter\\fR\n.\n.IP \"\\(bu\" 4\n\\fBjson\\fR\n.\n.IP \"\\(bu\" 4\n\\fBmemory\\fR\n.\n.IP \"\\(bu\" 4\n\\fBnetwork_device\\fR\n.\n.IP \"\\(bu\" 4\n\\fBrest\\fR\n.\n.IP \"\\(bu\" 4\n\\fBstore_configs\\fR\n.\n.IP \"\\(bu\" 4\n\\fByaml\\fR\n.\n.IP \"\" 0\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-filebucket.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-FILEBUCKET\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-filebucket\\fR \\- Store and retrieve files in a filebucket\n.\n.SH \"SYNOPSIS\"\nA stand\\-alone Puppet filebucket client\\.\n.\n.SH \"USAGE\"\npuppet filebucket \\fImode\\fR [\\-h|\\-\\-help] [\\-V|\\-\\-version] [\\-d|\\-\\-debug] [\\-v|\\-\\-verbose] [\\-l|\\-\\-local] [\\-r|\\-\\-remote] [\\-s|\\-\\-server \\fIserver\\fR] [\\-f|\\-\\-fromdate \\fIdate\\fR] [\\-t|\\-\\-todate \\fIdate\\fR] [\\-b|\\-\\-bucket \\fIdirectory\\fR] \\fIfile\\fR \\fIfile\\fR \\.\\.\\.\n.\n.P\nPuppet filebucket can operate in three modes, with only one mode per call:\n.\n.P\nbackup: Send one or more files to the specified file bucket\\. Each sent file is printed with its resulting sha256 sum\\.\n.\n.P\nget: Return the text associated with an sha256 sum\\. The text is printed to stdout, and only one file can be retrieved at a time\\.\n.\n.P\nrestore: Given a file path and an sha256 sum, store the content associated with the sum into the specified file path\\. You can specify an entirely new path to this argument; you are not restricted to restoring the content to its original location\\.\n.\n.P\ndiff: Print a diff in unified format between two checksums in the filebucket or between a checksum and its matching file\\.\n.\n.P\nlist: List all files in the current local filebucket\\. Listing remote filebuckets is not allowed\\.\n.\n.SH \"DESCRIPTION\"\nThis is a stand\\-alone filebucket client for sending files to a local or central filebucket\\.\n.\n.P\nNote that \\'filebucket\\' defaults to using a network\\-based filebucket available on the server named \\'puppet\\'\\. To use this, you\\'ll have to be running as a user with valid Puppet certificates\\. Alternatively, you can use your local file bucket by specifying \\'\\-\\-local\\', or by specifying \\'\\-\\-bucket\\' with a local path\\.\n.\n.P\n\\fBImportant\\fR: When you enable and use the backup option, and by extension the filebucket resource, you must ensure that sufficient disk space is available for the file backups\\. Generally, you can provide the disk space by using one of the following two options:\n.\n.IP \"\\(bu\" 4\nUse a \\fBfind\\fR command and \\fBcrontab\\fR entry to retain only the last X days of file backups\\. For example:\n.\n.IP \"\" 0\n.\n.IP \"\" 4\n.\n.nf\n\n  find /opt/puppetlabs/server/data/puppetserver/bucket \\-type f \\-mtime +45 \\-atime +45 \\-print0 | xargs \\-0 rm\n.\n.fi\n.\n.IP \"\" 0\n.\n.IP \"\\(bu\" 4\nRestrict the directory to a maximum size after which the oldest items are removed\\.\n.\n.IP \"\" 0\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument\\. For example, \\'ssldir\\' is a valid setting, so you can specify \\'\\-\\-ssldir \\fIdirectory\\fR\\' as an argument\\.\n.\n.P\nSee the configuration file documentation at https://puppet\\.com/docs/puppet/latest/configuration\\.html for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\'\\-\\-genconfig\\'\\.\n.\n.TP\n\\-\\-bucket\nSpecify a local filebucket path\\. This overrides the default path set in \\'$clientbucketdir\\'\\.\n.\n.TP\n\\-\\-debug\nEnable full debugging\\.\n.\n.TP\n\\-\\-fromdate\n(list only) Select bucket files from \\'fromdate\\'\\.\n.\n.TP\n\\-\\-help\nPrint this help message\\.\n.\n.TP\n\\-\\-local\nUse the local filebucket\\. This uses the default configuration information and the bucket located at the \\'$clientbucketdir\\' setting by default\\. If \\'\\-\\-bucket\\' is set, puppet uses that path instead\\.\n.\n.TP\n\\-\\-remote\nUse a remote filebucket\\. This uses the default configuration information and the bucket located at the \\'$bucketdir\\' setting by default\\.\n.\n.TP\n\\-\\-server_list\nA list of comma separated servers; only the first entry is used for file storage\\. This setting takes precidence over \\fBserver\\fR\\.\n.\n.TP\n\\-\\-server\nThe server to use for file storage\\. This setting is only used if \\fBserver_list\\fR is not set\\.\n.\n.TP\n\\-\\-todate\n(list only) Select bucket files until \\'todate\\'\\.\n.\n.TP\n\\-\\-verbose\nPrint extra information\\.\n.\n.TP\n\\-\\-version\nPrint version information\\.\n.\n.SH \"EXAMPLES\"\n.\n.nf\n\n## Backup a file to the filebucket, then restore it to a temporary directory\n$ puppet filebucket backup /etc/passwd\n/etc/passwd: 429b225650b912a2ee067b0a4cf1e949\n$ puppet filebucket restore /tmp/passwd 429b225650b912a2ee067b0a4cf1e949\n\n## Diff between two files in the filebucket\n$ puppet filebucket \\-l diff d43a6ecaa892a1962398ac9170ea9bf2 7ae322f5791217e031dc60188f4521ef\n1a2\n> again\n\n## Diff between the file in the filebucket and a local file\n$ puppet filebucket \\-l diff d43a6ecaa892a1962398ac9170ea9bf2 /tmp/testFile\n1a2\n> again\n\n## Backup a file to the filebucket and observe that it keeps each backup separate\n$ puppet filebucket \\-l list\nd43a6ecaa892a1962398ac9170ea9bf2 2015\\-05\\-11 09:27:56 /tmp/TestFile\n\n$ echo again >> /tmp/TestFile\n\n$ puppet filebucket \\-l backup /tmp/TestFile\n/tmp/TestFile: 7ae322f5791217e031dc60188f4521ef\n\n$ puppet filebucket \\-l list\nd43a6ecaa892a1962398ac9170ea9bf2 2015\\-05\\-11 09:27:56 /tmp/TestFile\n7ae322f5791217e031dc60188f4521ef 2015\\-05\\-11 09:52:15 /tmp/TestFile\n\n## List files in a filebucket within date ranges\n$ puppet filebucket \\-l \\-f 2015\\-01\\-01 \\-t 2015\\-01\\-11 list\n<Empty Output>\n\n$ puppet filebucket \\-l \\-f 2015\\-05\\-10 list\nd43a6ecaa892a1962398ac9170ea9bf2 2015\\-05\\-11 09:27:56 /tmp/TestFile\n7ae322f5791217e031dc60188f4521ef 2015\\-05\\-11 09:52:15 /tmp/TestFile\n\n$ puppet filebucket \\-l \\-f \"2015\\-05\\-11 09:30:00\" list\n7ae322f5791217e031dc60188f4521ef 2015\\-05\\-11 09:52:15 /tmp/TestFile\n\n$ puppet filebucket \\-l \\-t \"2015\\-05\\-11 09:30:00\" list\nd43a6ecaa892a1962398ac9170ea9bf2 2015\\-05\\-11 09:27:56 /tmp/TestFile\n\n## Manage files in a specific local filebucket\n$ puppet filebucket \\-b /tmp/TestBucket backup /tmp/TestFile2\n/tmp/TestFile2: d41d8cd98f00b204e9800998ecf8427e\n$ puppet filebucket \\-b /tmp/TestBucket list\nd41d8cd98f00b204e9800998ecf8427e 2015\\-05\\-11 09:33:22 /tmp/TestFile2\n\n## From a Puppet Server, list files in the server bucketdir\n$ puppet filebucket \\-b $(puppet config print bucketdir \\-\\-section server) list\nd43a6ecaa892a1962398ac9170ea9bf2 2015\\-05\\-11 09:27:56 /tmp/TestFile\n7ae322f5791217e031dc60188f4521ef 2015\\-05\\-11 09:52:15 /tmp/TestFile\n.\n.fi\n.\n.SH \"AUTHOR\"\nLuke Kanies\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-generate.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-GENERATE\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-generate\\fR \\- Generates Puppet code from Ruby definitions\\.\n.\n.SH \"SYNOPSIS\"\npuppet generate \\fIaction\\fR\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBtypes\\fR \\- Generates Puppet code for custom types\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet generate types [\\-\\-format \\fIformat\\fR] [\\-\\-force]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nGenerates definitions for custom resource types using Puppet code\\.\n.\n.IP\nTypes defined in Puppet code can be used to isolate custom type definitions between different environments\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-force\\fR \\- Forces the generation of output files (skips up\\-to\\-date checks)\\.\n.\n.IP\n\\fI\\-\\-format <format\\fR> \\- The generation output format to use\\. Supported formats: pcore\\.\n.\n.SH \"EXAMPLES\"\n\\fBtypes\\fR\n.\n.P\nGenerate Puppet type definitions for all custom resource types in the current environment:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet generate types\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nGenerate Puppet type definitions for all custom resource types in the specified environment:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet generate types \\-\\-environment development\n.\n.fi\n.\n.IP \"\" 0\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2016 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-help.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-HELP\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-help\\fR \\- Display Puppet help\\.\n.\n.SH \"SYNOPSIS\"\npuppet help \\fIaction\\fR\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBhelp\\fR \\- Display help about Puppet subcommands and their actions\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet help [\\-\\-version VERSION] [\\-\\-ronn] [\\fIsubcommand\\fR] [\\fIaction\\fR]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nDisplay help about Puppet subcommands and their actions\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-ronn\\fR \\- Whether to render the help text in ronn format\\.\n.\n.IP\n\\fI\\-\\-version VERSION\\fR \\- The version of the subcommand for which to show help\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nShort help text for the specified subcommand or action\\.\n.\n.SH \"EXAMPLES\"\n\\fBhelp\\fR\n.\n.P\nGet help for an action:\n.\n.P\n$ puppet help\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-lookup.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-LOOKUP\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-lookup\\fR \\- Interactive Hiera lookup\n.\n.SH \"SYNOPSIS\"\nDoes Hiera lookups from the command line\\.\n.\n.P\nSince this command needs access to your Hiera data, make sure to run it on a node that has a copy of that data\\. This usually means logging into a Puppet Server node and running \\'puppet lookup\\' with sudo\\.\n.\n.P\nThe most common version of this command is:\n.\n.P\n\\'puppet lookup \\fIKEY\\fR \\-\\-node \\fINAME\\fR \\-\\-environment \\fIENV\\fR \\-\\-explain\\'\n.\n.SH \"USAGE\"\npuppet lookup [\\-\\-help] [\\-\\-type \\fITYPESTRING\\fR] [\\-\\-merge first|unique|hash|deep] [\\-\\-knock\\-out\\-prefix \\fIPREFIX\\-STRING\\fR] [\\-\\-sort\\-merged\\-arrays] [\\-\\-merge\\-hash\\-arrays] [\\-\\-explain] [\\-\\-environment \\fIENV\\fR] [\\-\\-default \\fIVALUE\\fR] [\\-\\-node \\fINODE\\-NAME\\fR] [\\-\\-facts \\fIFILE\\fR] [\\-\\-compile] [\\-\\-render\\-as s|json|yaml|binary|msgpack] \\fIkeys\\fR\n.\n.SH \"DESCRIPTION\"\nThe lookup command is a CLI for Puppet\\'s \\'lookup()\\' function\\. It searches your Hiera data and returns a value for the requested lookup key, so you can test and explore your data\\. It is a modern replacement for the \\'hiera\\' command\\. Lookup uses the setting for global hiera\\.yaml from puppet\\'s config, and the environment to find the environment level hiera\\.yaml as well as the resulting modulepath for the environment (for hiera\\.yaml files in modules)\\. Hiera usually relies on a node\\'s facts to locate the relevant data sources\\. By default, \\'puppet lookup\\' uses facts from the node you run the command on, but you can get data for any other node with the \\'\\-\\-node \\fINAME\\fR\\' option\\. If possible, the lookup command will use the requested node\\'s real stored facts from PuppetDB; if PuppetDB isn\\'t configured or you want to provide arbitrary fact values, you can pass alternate facts as a JSON or YAML file with \\'\\-\\-facts \\fIFILE\\fR\\'\\.\n.\n.P\nIf you\\'re debugging your Hiera data and want to see where values are coming from, use the \\'\\-\\-explain\\' option\\.\n.\n.P\nIf \\'\\-\\-explain\\' isn\\'t specified, lookup exits with 0 if a value was found and 1 otherwise\\. With \\'\\-\\-explain\\', lookup always exits with 0 unless there is a major error\\.\n.\n.P\nYou can provide multiple lookup keys to this command, but it only returns a value for the first found key, omitting the rest\\.\n.\n.P\nFor more details about how Hiera works, see the Hiera documentation: https://puppet\\.com/docs/puppet/latest/hiera_intro\\.html\n.\n.SH \"OPTIONS\"\n.\n.IP \"\\(bu\" 4\n\\-\\-help: Print this help message\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-explain Explain the details of how the lookup was performed and where the final value came from (or the reason no value was found)\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-node \\fINODE\\-NAME\\fR Specify which node to look up data for; defaults to the node where the command is run\\. Since Hiera\\'s purpose is to provide different values for different nodes (usually based on their facts), you\\'ll usually want to use some specific node\\'s facts to explore your data\\. If the node where you\\'re running this command is configured to talk to PuppetDB, the command will use the requested node\\'s most recent facts\\. Otherwise, you can override facts with the \\'\\-\\-facts\\' option\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-facts \\fIFILE\\fR Specify a \\.json or \\.yaml file of key => value mappings to override the facts for this lookup\\. Any facts not specified in this file maintain their original value\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-environment \\fIENV\\fR Like with most Puppet commands, you can specify an environment on the command line\\. This is important for lookup because different environments can have different Hiera data\\. This environment will be always be the one used regardless of any other factors\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-merge first|unique|hash|deep: Specify the merge behavior, overriding any merge behavior from the data\\'s lookup_options\\. \\'first\\' returns the first value found\\. \\'unique\\' appends everything to a merged, deduplicated array\\. \\'hash\\' performs a simple hash merge by overwriting keys of lower lookup priority\\. \\'deep\\' performs a deep merge on values of Array and Hash type\\. There are additional options that can be used with \\'deep\\'\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-knock\\-out\\-prefix \\fIPREFIX\\-STRING\\fR Can be used with the \\'deep\\' merge strategy\\. Specifies a prefix to indicate a value should be removed from the final result\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-sort\\-merged\\-arrays Can be used with the \\'deep\\' merge strategy\\. When this flag is used, all merged arrays are sorted\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-merge\\-hash\\-arrays Can be used with the \\'deep\\' merge strategy\\. When this flag is used, hashes WITHIN arrays are deep\\-merged with their counterparts by position\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-explain\\-options Explain whether a lookup_options hash affects this lookup, and how that hash was assembled\\. (lookup_options is how Hiera configures merge behavior in data\\.)\n.\n.IP \"\\(bu\" 4\n\\-\\-default \\fIVALUE\\fR A value to return if Hiera can\\'t find a value in data\\. For emulating calls to the \\'lookup()\\' function that include a default\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-type \\fITYPESTRING\\fR: Assert that the value has the specified type\\. For emulating calls to the \\'lookup()\\' function that include a data type\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-compile Perform a full catalog compilation prior to the lookup\\. If your hierarchy and data only use the $facts, $trusted, and $server_facts variables, you don\\'t need this option; however, if your Hiera configuration uses arbitrary variables set by a Puppet manifest, you might need this option to get accurate data\\. No catalog compilation takes place unless this flag is given\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-render\\-as s|json|yaml|binary|msgpack Specify the output format of the results; \"s\" means plain text\\. The default when producing a value is yaml and the default when producing an explanation is s\\.\n.\n.IP \"\" 0\n.\n.SH \"EXAMPLE\"\nTo look up \\'key_name\\' using the Puppet Server node\\'s facts: $ puppet lookup key_name\n.\n.P\nTo look up \\'key_name\\' using the Puppet Server node\\'s arbitrary variables from a manifest, and classify the node if applicable: $ puppet lookup key_name \\-\\-compile\n.\n.P\nTo look up \\'key_name\\' using the Puppet Server node\\'s facts, overridden by facts given in a file: $ puppet lookup key_name \\-\\-facts fact_file\\.yaml\n.\n.P\nTo look up \\'key_name\\' with agent\\.local\\'s facts: $ puppet lookup \\-\\-node agent\\.local key_name\n.\n.P\nTo get the first value found for \\'key_name_one\\' and \\'key_name_two\\' with agent\\.local\\'s facts while merging values and knocking out the prefix \\'foo\\' while merging: $ puppet lookup \\-\\-node agent\\.local \\-\\-merge deep \\-\\-knock\\-out\\-prefix foo key_name_one key_name_two\n.\n.P\nTo lookup \\'key_name\\' with agent\\.local\\'s facts, and return a default value of \\'bar\\' if nothing was found: $ puppet lookup \\-\\-node agent\\.local \\-\\-default bar key_name\n.\n.P\nTo see an explanation of how the value for \\'key_name\\' would be found, using agent\\.local\\'s facts: $ puppet lookup \\-\\-node agent\\.local \\-\\-explain key_name\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2015 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-module.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-MODULE\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-module\\fR \\- Creates, installs and searches for modules on the Puppet Forge\\.\n.\n.SH \"SYNOPSIS\"\npuppet module \\fIaction\\fR [\\-\\-environment production ] [\\-\\-modulepath ]\n.\n.SH \"DESCRIPTION\"\nThis subcommand can find, install, and manage modules from the Puppet Forge, a repository of user\\-contributed Puppet code\\. It can also generate empty modules, and prepare locally developed modules for release on the Forge\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.TP\n\\-\\-environment production\nThe environment in which Puppet is running\\. For clients, such as \\fBpuppet agent\\fR, this determines the environment itself, which Puppet uses to find modules and much more\\. For servers, such as \\fBpuppet server\\fR, this provides the default environment for nodes that Puppet knows nothing about\\.\n.\n.IP\nWhen defining an environment in the \\fB[agent]\\fR section, this refers to the environment that the agent requests from the primary server\\. The environment doesn\\'t have to exist on the local filesystem because the agent fetches it from the primary server\\. This definition is used when running \\fBpuppet agent\\fR\\.\n.\n.IP\nWhen defined in the \\fB[user]\\fR section, the environment refers to the path that Puppet uses to search for code and modules related to its execution\\. This requires the environment to exist locally on the filesystem where puppet is being executed\\. Puppet subcommands, including \\fBpuppet module\\fR and \\fBpuppet apply\\fR, use this definition\\.\n.\n.IP\nGiven that the context and effects vary depending on the config section \\fIhttps://puppet\\.com/docs/puppet/latest/config_file_main\\.html#config\\-sections\\fR in which the \\fBenvironment\\fR setting is defined, do not set it globally\\.\n.\n.TP\n\\-\\-modulepath\nThe search path for modules, as a list of directories separated by the system path separator character\\. (The POSIX path separator is \\':\\', and the Windows path separator is \\';\\'\\.)\n.\n.IP\nSetting a global value for \\fBmodulepath\\fR in puppet\\.conf is not allowed (but it can be overridden from the commandline)\\. Please use directory environments instead\\. If you need to use something other than the default modulepath of \\fB<ACTIVE ENVIRONMENT\\'S MODULES DIR>:$basemodulepath\\fR, you can set \\fBmodulepath\\fR in environment\\.conf\\. For more info, see \\fIhttps://puppet\\.com/docs/puppet/latest/environments_about\\.html\\fR\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBchanges\\fR \\- Show modified files of an installed module\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet module changes \\fIpath\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nShows any files in a module that have been modified since it was installed\\. This action compares the files on disk to the md5 checksums included in the module\\'s checksums\\.json or, if that is missing, in metadata\\.json\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nArray of strings representing paths of modified files\\.\n.\n.TP\n\\fBinstall\\fR \\- Install a module from the Puppet Forge or a release archive\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet module install [\\-\\-force | \\-f] [\\-\\-target\\-dir DIR | \\-i DIR] [\\-\\-ignore\\-dependencies] [\\-\\-version VER | \\-v VER] \\fIname\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nInstalls a module from the Puppet Forge or from a release archive file\\. Note: Module install uses MD5 checksums, which are prohibited on FIPS enabled systems\\.\n.\n.IP\nThe specified module will be installed into the directory specified with the \\fB\\-\\-target\\-dir\\fR option, which defaults to the first directory in the modulepath\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-force\\fR | \\fI\\-f\\fR \\- Force overwrite of existing module, if any\\. Implies \\-\\-ignore\\-dependencies\\.\n.\n.IP\n\\fI\\-\\-ignore\\-dependencies\\fR \\- Do not attempt to install dependencies\\. Implied by \\-\\-force\\.\n.\n.IP\n\\fI\\-\\-target\\-dir DIR\\fR | \\fI\\-i DIR\\fR \\- The directory into which modules are installed; defaults to the first directory in the modulepath\\.\n.\n.IP\nSpecifying this option will change the installation directory, and will use the existing modulepath when checking for dependencies\\. If you wish to check a different set of directories for dependencies, you must also use the \\fB\\-\\-environment\\fR or \\fB\\-\\-modulepath\\fR options\\.\n.\n.IP\n\\fI\\-\\-version VER\\fR | \\fI\\-v VER\\fR \\- Module version to install; can be an exact version or a requirement string, eg \\'>= 1\\.0\\.3\\'\\. Defaults to latest version\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nPathname object representing the path to the installed module\\.\n.\n.TP\n\\fBlist\\fR \\- List installed modules\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet module list [\\-\\-tree]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nLists the installed puppet modules\\. By default, this action scans the modulepath from puppet\\.conf\\'s \\fB[main]\\fR block; use the \\-\\-modulepath option to change which directories are scanned\\.\n.\n.IP\nThe output of this action includes information from the module\\'s metadata, including version numbers and unmet module dependencies\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-tree\\fR \\- Whether to show dependencies as a tree view\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nhash of paths to module objects\n.\n.TP\n\\fBuninstall\\fR \\- Uninstall a puppet module\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet module uninstall [\\-\\-force | \\-f] [\\-\\-ignore\\-changes | \\-c] [\\-\\-version=] \\fIname\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nUninstalls a puppet module from the modulepath (or a specific target directory)\\. Note: Module uninstall uses MD5 checksums, which are prohibited on FIPS enabled systems\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-force\\fR | \\fI\\-f\\fR \\- Force the uninstall of an installed module even if there are local changes or the possibility of causing broken dependencies\\.\n.\n.IP\n\\fI\\-\\-ignore\\-changes\\fR | \\fI\\-c\\fR \\- Uninstall an installed module even if there are local changes to it\\. (Implied by \\-\\-force\\.)\n.\n.IP\n\\fI\\-\\-version=\\fR \\- The version of the module to uninstall\\. When using this option, a module matching the specified version must be installed or else an error is raised\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nHash of module objects representing uninstalled modules and related errors\\.\n.\n.TP\n\\fBupgrade\\fR \\- Upgrade a puppet module\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet module upgrade [\\-\\-force | \\-f] [\\-\\-ignore\\-dependencies] [\\-\\-ignore\\-changes | \\-c] [\\-\\-version=] \\fIname\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nUpgrades a puppet module\\. Note: Module upgrade uses MD5 checksums, which are prohibited on FIPS enabled systems\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-force\\fR | \\fI\\-f\\fR \\- Force the upgrade of an installed module even if there are local changes or the possibility of causing broken dependencies\\. Implies \\-\\-ignore\\-dependencies\\.\n.\n.IP\n\\fI\\-\\-ignore\\-changes\\fR | \\fI\\-c\\fR \\- Upgrade an installed module even if there are local changes to it\\. (Implied by \\-\\-force\\.)\n.\n.IP\n\\fI\\-\\-ignore\\-dependencies\\fR \\- Do not attempt to install dependencies\\. Implied by \\-\\-force\\.\n.\n.IP\n\\fI\\-\\-version=\\fR \\- The version of the module to upgrade to\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nHash\n.\n.SH \"EXAMPLES\"\n\\fBchanges\\fR\n.\n.P\nShow modified files of an installed module:\n.\n.P\n$ puppet module changes /etc/puppetlabs/code/modules/vcsrepo/ warning: 1 files modified lib/puppet/provider/vcsrepo\\.rb\n.\n.P\n\\fBinstall\\fR\n.\n.P\nInstall a module:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo Preparing to install into /etc/puppetlabs/code/modules \\.\\.\\. Downloading from https://forgeapi\\.puppet\\.com \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /etc/puppetlabs/code/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\nInstall a module to a specific environment:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo \\-\\-environment development Preparing to install into /etc/puppetlabs/code/environments/development/modules \\.\\.\\. Downloading from https://forgeapi\\.puppet\\.com \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /etc/puppetlabs/code/environments/development/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\nInstall a specific module version:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo \\-v 0\\.0\\.4 Preparing to install into /etc/puppetlabs/modules \\.\\.\\. Downloading from https://forgeapi\\.puppet\\.com \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /etc/puppetlabs/code/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\nInstall a module into a specific directory:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo \\-\\-target\\-dir=/opt/puppetlabs/puppet/modules Preparing to install into /opt/puppetlabs/puppet/modules \\.\\.\\. Downloading from https://forgeapi\\.puppet\\.com \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /opt/puppetlabs/puppet/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\nInstall a module into a specific directory and check for dependencies in other directories:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo \\-\\-target\\-dir=/opt/puppetlabs/puppet/modules \\-\\-modulepath /etc/puppetlabs/code/modules Preparing to install into /opt/puppetlabs/puppet/modules \\.\\.\\. Downloading from https://forgeapi\\.puppet\\.com \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /opt/puppetlabs/puppet/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\nInstall a module from a release archive:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo\\-0\\.0\\.4\\.tar\\.gz Preparing to install into /etc/puppetlabs/code/modules \\.\\.\\. Downloading from https://forgeapi\\.puppet\\.com \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /etc/puppetlabs/code/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\nInstall a module from a release archive and ignore dependencies:\n.\n.P\n$ puppet module install puppetlabs\\-vcsrepo\\-0\\.0\\.4\\.tar\\.gz \\-\\-ignore\\-dependencies Preparing to install into /etc/puppetlabs/code/modules \\.\\.\\. Installing \\-\\- do not interrupt \\.\\.\\. /etc/puppetlabs/code/modules └── puppetlabs\\-vcsrepo (v0\\.0\\.4)\n.\n.P\n\\fBlist\\fR\n.\n.P\nList installed modules:\n.\n.P\n$ puppet module list /etc/puppetlabs/code/modules ├── bodepd\\-create_resources (v0\\.0\\.1) ├── puppetlabs\\-bacula (v0\\.0\\.2) ├── puppetlabs\\-mysql (v0\\.0\\.1) ├── puppetlabs\\-sqlite (v0\\.0\\.1) └── puppetlabs\\-stdlib (v2\\.2\\.1) /opt/puppetlabs/puppet/modules (no modules installed)\n.\n.P\nList installed modules in a tree view:\n.\n.P\n$ puppet module list \\-\\-tree /etc/puppetlabs/code/modules └─┬ puppetlabs\\-bacula (v0\\.0\\.2) ├── puppetlabs\\-stdlib (v2\\.2\\.1) ├─┬ puppetlabs\\-mysql (v0\\.0\\.1) │ └── bodepd\\-create_resources (v0\\.0\\.1) └── puppetlabs\\-sqlite (v0\\.0\\.1) /opt/puppetlabs/puppet/modules (no modules installed)\n.\n.P\nList installed modules from a specified environment:\n.\n.P\n$ puppet module list \\-\\-environment production /etc/puppetlabs/code/modules ├── bodepd\\-create_resources (v0\\.0\\.1) ├── puppetlabs\\-bacula (v0\\.0\\.2) ├── puppetlabs\\-mysql (v0\\.0\\.1) ├── puppetlabs\\-sqlite (v0\\.0\\.1) └── puppetlabs\\-stdlib (v2\\.2\\.1) /opt/puppetlabs/puppet/modules (no modules installed)\n.\n.P\nList installed modules from a specified modulepath:\n.\n.P\n$ puppet module list \\-\\-modulepath /opt/puppetlabs/puppet/modules /opt/puppetlabs/puppet/modules (no modules installed)\n.\n.P\n\\fBuninstall\\fR\n.\n.P\nUninstall a module:\n.\n.P\n$ puppet module uninstall puppetlabs\\-ssh Removed /etc/puppetlabs/code/modules/ssh (v1\\.0\\.0)\n.\n.P\nUninstall a module from a specific directory:\n.\n.P\n$ puppet module uninstall puppetlabs\\-ssh \\-\\-modulepath /opt/puppetlabs/puppet/modules Removed /opt/puppetlabs/puppet/modules/ssh (v1\\.0\\.0)\n.\n.P\nUninstall a module from a specific environment:\n.\n.P\n$ puppet module uninstall puppetlabs\\-ssh \\-\\-environment development Removed /etc/puppetlabs/code/environments/development/modules/ssh (v1\\.0\\.0)\n.\n.P\nUninstall a specific version of a module:\n.\n.P\n$ puppet module uninstall puppetlabs\\-ssh \\-\\-version 2\\.0\\.0 Removed /etc/puppetlabs/code/modules/ssh (v2\\.0\\.0)\n.\n.P\n\\fBupgrade\\fR\n.\n.P\nupgrade an installed module to the latest version\n.\n.P\n$ puppet module upgrade puppetlabs\\-apache /etc/puppetlabs/puppet/modules └── puppetlabs\\-apache (v1\\.0\\.0 \\-> v2\\.4\\.0)\n.\n.P\nupgrade an installed module to a specific version\n.\n.P\n$ puppet module upgrade puppetlabs\\-apache \\-\\-version 2\\.1\\.0 /etc/puppetlabs/puppet/modules └── puppetlabs\\-apache (v1\\.0\\.0 \\-> v2\\.1\\.0)\n.\n.P\nupgrade an installed module for a specific environment\n.\n.P\n$ puppet module upgrade puppetlabs\\-apache \\-\\-environment test /etc/puppetlabs/code/environments/test/modules └── puppetlabs\\-apache (v1\\.0\\.0 \\-> v2\\.4\\.0)\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2012 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-node.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-NODE\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-node\\fR \\- View and manage node definitions\\.\n.\n.SH \"SYNOPSIS\"\npuppet node \\fIaction\\fR [\\-\\-terminus _TERMINUS]\n.\n.SH \"DESCRIPTION\"\nThis subcommand interacts with node objects, which are used by Puppet to build a catalog\\. A node object consists of the node\\'s facts, environment, node parameters (exposed in the parser as top\\-scope variables), and classes\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.TP\n\\-\\-terminus _TERMINUS\nIndirector faces expose indirected subsystems of Puppet\\. These subsystems are each able to retrieve and alter a specific type of data (with the familiar actions of \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR) from an arbitrary number of pluggable backends\\. In Puppet parlance, these backends are called terminuses\\.\n.\n.IP\nAlmost all indirected subsystems have a \\fBrest\\fR terminus that interacts with the puppet master\\'s data\\. Most of them have additional terminuses for various local data models, which are in turn used by the indirected subsystem on the puppet master whenever it receives a remote request\\.\n.\n.IP\nThe terminus for an action is often determined by context, but occasionally needs to be set explicitly\\. See the \"Notes\" section of this face\\'s manpage for more details\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBclean\\fR \\- Clean up signed certs, cached facts, node objects, and reports for a node stored by the puppetmaster\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet node clean [\\-\\-terminus _TERMINUS] \\fIhost1\\fR [\\fIhost2\\fR \\.\\.\\.]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nCleans up the following information a puppet master knows about a node:\n.\n.IP\n\\fISigned certificates\\fR \\- ($vardir/ssl/ca/signed/node\\.domain\\.pem)\n.\n.IP\n\\fICached facts\\fR \\- ($vardir/yaml/facts/node\\.domain\\.yaml)\n.\n.IP\n\\fICached node objects\\fR \\- ($vardir/yaml/node/node\\.domain\\.yaml)\n.\n.IP\n\\fIReports\\fR \\- ($vardir/reports/node\\.domain)\n.\n.IP\nNOTE: this action now cleans up certs via Puppet Server\\'s CA API\\. A running server is required for certs to be cleaned\\.\n.\n.TP\n\\fBfind\\fR \\- Retrieve a node object\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet node find [\\-\\-terminus _TERMINUS] \\fIhost\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nRetrieve a node object\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA hash containing the node\\'s \\fBclasses\\fR, \\fBenvironment\\fR, \\fBexpiration\\fR, \\fBname\\fR, \\fBparameters\\fR (its facts, combined with any ENC\\-set parameters), and \\fBtime\\fR\\. When used from the Ruby API: a Puppet::Node object\\.\n.\n.IP\nRENDERING ISSUES: Rendering as string and json are currently broken; node objects can only be rendered as yaml\\.\n.\n.TP\n\\fBinfo\\fR \\- Print the default terminus class for this face\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet node info [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nPrints the default terminus class for this subcommand\\. Note that different run modes may have different default termini; when in doubt, specify the run mode with the \\'\\-\\-run_mode\\' option\\.\n.\n.SH \"EXAMPLES\"\n\\fBfind\\fR\n.\n.P\nRetrieve an \"empty\" (no classes, no ENC\\-imposed parameters, and an environment of \"production\") node:\n.\n.P\n$ puppet node find somenode\\.puppetlabs\\.lan \\-\\-terminus plain \\-\\-render\\-as yaml\n.\n.P\nRetrieve a node using the Puppet Server\\'s configured ENC:\n.\n.P\n$ puppet node find somenode\\.puppetlabs\\.lan \\-\\-terminus exec \\-\\-run_mode server \\-\\-render\\-as yaml\n.\n.P\nRetrieve the same node from the Puppet Server:\n.\n.P\n$ puppet node find somenode\\.puppetlabs\\.lan \\-\\-terminus rest \\-\\-render\\-as yaml\n.\n.SH \"NOTES\"\nThis subcommand is an indirector face, which exposes \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR actions for an indirected subsystem of Puppet\\. Valid termini for this face include:\n.\n.IP \"\\(bu\" 4\n\\fBexec\\fR\n.\n.IP \"\\(bu\" 4\n\\fBjson\\fR\n.\n.IP \"\\(bu\" 4\n\\fBmemory\\fR\n.\n.IP \"\\(bu\" 4\n\\fBmsgpack\\fR\n.\n.IP \"\\(bu\" 4\n\\fBplain\\fR\n.\n.IP \"\\(bu\" 4\n\\fBrest\\fR\n.\n.IP \"\\(bu\" 4\n\\fBstore_configs\\fR\n.\n.IP \"\\(bu\" 4\n\\fByaml\\fR\n.\n.IP \"\" 0\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-parser.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-PARSER\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-parser\\fR \\- Interact directly with the parser\\.\n.\n.SH \"SYNOPSIS\"\npuppet parser \\fIaction\\fR\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.SH \"ACTIONS\"\n.\n.IP \"\\(bu\" 4\n\\fBdump\\fR \\- Outputs a dump of the internal parse tree for debugging: \\fBSYNOPSIS\\fR\n.\n.IP\npuppet parser dump [\\-\\-e \\fIsource\\fR] [\\-\\-[no\\-]validate] [\\-\\-format \\fIold, pn, or json\\fR] [\\-\\-pretty] [\\-\\-format \\fIold|pn|json\\fR] [\\-\\-pretty] { \\-e \\fIsource\\fR | [\\fItemplates\\fR \\.\\.\\.] }\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nThis action parses and validates the Puppet DSL syntax without compiling a catalog or syncing any resources\\.\n.\n.IP\nThe output format can be controlled using the \\-\\-format \\fIold|pn|json\\fR where:\n.\n.IP \"\\(bu\" 4\n\\'old\\' is the default, but now deprecated format which is not API\\.\n.\n.IP \"\\(bu\" 4\n\\'pn\\' is the Puppet Extended S\\-Expression Notation\\.\n.\n.IP \"\\(bu\" 4\n\\'json\\' outputs the same graph as \\'pn\\' but with JSON syntax\\.\n.\n.IP \"\" 0\n.\n.IP\nThe output will be \"pretty printed\" when the option \\-\\-pretty is given together with \\-\\-format \\'pn\\' or \\'json\\'\\. This option has no effect on the \\'old\\' format\\.\n.\n.IP\nThe command accepts one or more manifests (\\.pp) files, or an \\-e followed by the puppet source text\\. If no arguments are given, the stdin is read (unless it is attached to a terminal)\n.\n.IP\nThe output format of the dumped tree is intended for debugging purposes and is not API, it may change from time to time\\.\n.\n.IP\n\\fBOPTIONS\\fR \\fI\\-\\-e <source\\fR> \\- dump one source expression given on the command line\\.\n.\n.IP\n\\fI\\-\\-format <old, pn, or json\\fR> \\- Get result in \\'old\\' (deprecated format), \\'pn\\' (new format), or \\'json\\' (new format in JSON)\\.\n.\n.IP\n\\fI\\-\\-pretty\\fR \\- Pretty print output\\. Only applicable together with \\-\\-format pn or json\n.\n.IP\n\\fI\\-\\-[no\\-]validate\\fR \\- Whether or not to validate the parsed result, if no\\-validate only syntax errors are reported\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA dump of the resulting AST model unless there are syntax or validation errors\\.\n.\n.IP \"\\(bu\" 4\n\\fBvalidate\\fR \\- Validate the syntax of one or more Puppet manifests\\.: \\fBSYNOPSIS\\fR\n.\n.IP\npuppet parser validate [\\fImanifest\\fR] [\\fImanifest\\fR \\.\\.\\.]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nThis action validates Puppet DSL syntax without compiling a catalog or syncing any resources\\. If no manifest files are provided, it will validate the default site manifest\\.\n.\n.IP\nWhen validating multiple issues per file are reported up to the settings of max_error, and max_warnings\\. The processing stops after having reported issues for the first encountered file with errors\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nNothing, or the first syntax error encountered\\.\n.\n.IP \"\" 0\n.\n.SH \"EXAMPLES\"\n\\fBvalidate\\fR\n.\n.P\nValidate the default site manifest at /etc/puppetlabs/puppet/manifests/site\\.pp:\n.\n.P\n$ puppet parser validate\n.\n.P\nValidate two arbitrary manifest files:\n.\n.P\n$ puppet parser validate init\\.pp vhost\\.pp\n.\n.P\nValidate from STDIN:\n.\n.P\n$ cat init\\.pp | puppet parser validate\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2014 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-plugin.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-PLUGIN\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-plugin\\fR \\- Interact with the Puppet plugin system\\.\n.\n.SH \"SYNOPSIS\"\npuppet plugin \\fIaction\\fR\n.\n.SH \"DESCRIPTION\"\nThis subcommand provides network access to the puppet master\\'s store of plugins\\.\n.\n.P\nThe puppet master serves Ruby code collected from the \\fBlib\\fR directories of its modules\\. These plugins can be used on agent nodes to extend Facter and implement custom types and providers\\. Plugins are normally downloaded by puppet agent during the course of a run\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBdownload\\fR \\- Download plugins from the puppet master\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet plugin download\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nDownloads plugins from the configured puppet master\\. Any plugins downloaded in this way will be used in all subsequent Puppet activity\\. This action modifies files on disk\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nA list of the files downloaded, or a confirmation that no files were downloaded\\. When used from the Ruby API, this action returns an array of the files downloaded, which will be empty if none were retrieved\\.\n.\n.SH \"EXAMPLES\"\n\\fBdownload\\fR\n.\n.P\nRetrieve plugins from the puppet master:\n.\n.P\n$ puppet plugin download\n.\n.P\nRetrieve plugins from the puppet master (API example):\n.\n.P\n$ Puppet::Face[:plugin, \\'0\\.0\\.1\\']\\.download\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-report.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-REPORT\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-report\\fR \\- Create, display, and submit reports\\.\n.\n.SH \"SYNOPSIS\"\npuppet report \\fIaction\\fR [\\-\\-terminus _TERMINUS]\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action\\. For example, \\fBserver\\fR and \\fBrun_mode\\fR are valid settings, so you can specify \\fB\\-\\-server <servername>\\fR, or \\fB\\-\\-run_mode <runmode>\\fR as an argument\\.\n.\n.P\nSee the configuration file documentation at \\fIhttps://puppet\\.com/docs/puppet/latest/configuration\\.html\\fR for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\fB\\-\\-genconfig\\fR\\.\n.\n.TP\n\\-\\-render\\-as FORMAT\nThe format in which to render output\\. The most common formats are \\fBjson\\fR, \\fBs\\fR (string), \\fByaml\\fR, and \\fBconsole\\fR, but other options such as \\fBdot\\fR are sometimes available\\.\n.\n.TP\n\\-\\-verbose\nWhether to log verbosely\\.\n.\n.TP\n\\-\\-debug\nWhether to log debug information\\.\n.\n.TP\n\\-\\-terminus _TERMINUS\nIndirector faces expose indirected subsystems of Puppet\\. These subsystems are each able to retrieve and alter a specific type of data (with the familiar actions of \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR) from an arbitrary number of pluggable backends\\. In Puppet parlance, these backends are called terminuses\\.\n.\n.IP\nAlmost all indirected subsystems have a \\fBrest\\fR terminus that interacts with the puppet master\\'s data\\. Most of them have additional terminuses for various local data models, which are in turn used by the indirected subsystem on the puppet master whenever it receives a remote request\\.\n.\n.IP\nThe terminus for an action is often determined by context, but occasionally needs to be set explicitly\\. See the \"Notes\" section of this face\\'s manpage for more details\\.\n.\n.SH \"ACTIONS\"\n.\n.TP\n\\fBinfo\\fR \\- Print the default terminus class for this face\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet report info [\\-\\-terminus _TERMINUS]\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nPrints the default terminus class for this subcommand\\. Note that different run modes may have different default termini; when in doubt, specify the run mode with the \\'\\-\\-run_mode\\' option\\.\n.\n.TP\n\\fBsave\\fR \\- API only: submit a report\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet report save [\\-\\-terminus _TERMINUS] \\fIreport\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nAPI only: create or overwrite an object\\. As the Faces framework does not currently accept data from STDIN, save actions cannot currently be invoked from the command line\\.\n.\n.IP\n\\fBRETURNS\\fR\n.\n.IP\nNothing\\.\n.\n.TP\n\\fBsubmit\\fR \\- API only: submit a report with error handling\\.\n\\fBSYNOPSIS\\fR\n.\n.IP\npuppet report submit [\\-\\-terminus _TERMINUS] \\fIreport\\fR\n.\n.IP\n\\fBDESCRIPTION\\fR\n.\n.IP\nAPI only: Submits a report to the puppet master\\. This action is essentially a shortcut and wrapper for the \\fBsave\\fR action with the \\fBrest\\fR terminus, and provides additional details in the event of a failure\\.\n.\n.SH \"EXAMPLES\"\n\\fBsave\\fR\n.\n.P\nFrom the implementation of \\fBpuppet report submit\\fR (API example):\n.\n.P\nbegin Puppet::Transaction::Report\\.indirection\\.terminus_class = :rest Puppet::Face[:report, \"0\\.0\\.1\"]\\.save(report) Puppet\\.notice \"Uploaded report for #{report\\.name}\" rescue => detail Puppet\\.log_exception(detail, \"Could not send report: #{detail}\") end\n.\n.P\n\\fBsubmit\\fR\n.\n.P\nAPI example:report = Puppet::Face[:catalog, \\'0\\.0\\.1\\']\\.apply Puppet::Face[:report, \\'0\\.0\\.1\\']\\.submit(report) return report\n.\n.SH \"NOTES\"\nThis subcommand is an indirector face, which exposes \\fBfind\\fR, \\fBsearch\\fR, \\fBsave\\fR, and \\fBdestroy\\fR actions for an indirected subsystem of Puppet\\. Valid termini for this face include:\n.\n.IP \"\\(bu\" 4\n\\fBjson\\fR\n.\n.IP \"\\(bu\" 4\n\\fBmsgpack\\fR\n.\n.IP \"\\(bu\" 4\n\\fBprocessor\\fR\n.\n.IP \"\\(bu\" 4\n\\fBrest\\fR\n.\n.IP \"\\(bu\" 4\n\\fByaml\\fR\n.\n.IP \"\" 0\n.\n.SH \"COPYRIGHT AND LICENSE\"\nCopyright 2011 by Puppet Inc\\. Apache 2 license; see COPYING\n"
  },
  {
    "path": "man/man8/puppet-resource.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-RESOURCE\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-resource\\fR \\- The resource abstraction layer shell\n.\n.SH \"SYNOPSIS\"\nUses the Puppet RAL to directly interact with the system\\.\n.\n.SH \"USAGE\"\npuppet resource [\\-h|\\-\\-help] [\\-d|\\-\\-debug] [\\-v|\\-\\-verbose] [\\-e|\\-\\-edit] [\\-p|\\-\\-param \\fIparameter\\fR] [\\-t|\\-\\-types] [\\-y|\\-\\-to_yaml] \\fItype\\fR [\\fIname\\fR] [\\fIattribute\\fR=\\fIvalue\\fR \\.\\.\\.]\n.\n.SH \"DESCRIPTION\"\nThis command provides simple facilities for converting current system state into Puppet code, along with some ability to modify the current state using Puppet\\'s RAL\\.\n.\n.P\nBy default, you must at least provide a type to list, in which case puppet resource will tell you everything it knows about all resources of that type\\. You can optionally specify an instance name, and puppet resource will only describe that single instance\\.\n.\n.P\nIf given a type, a name, and a series of \\fIattribute\\fR=\\fIvalue\\fR pairs, puppet resource will modify the state of the specified resource\\. Alternately, if given a type, a name, and the \\'\\-\\-edit\\' flag, puppet resource will write its output to a file, open that file in an editor, and then apply the saved file as a Puppet transaction\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument\\. For example, \\'ssldir\\' is a valid setting, so you can specify \\'\\-\\-ssldir \\fIdirectory\\fR\\' as an argument\\.\n.\n.P\nSee the configuration file documentation at https://puppet\\.com/docs/puppet/latest/configuration\\.html for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\'\\-\\-genconfig\\'\\.\n.\n.TP\n\\-\\-debug\nEnable full debugging\\.\n.\n.TP\n\\-\\-edit\nWrite the results of the query to a file, open the file in an editor, and read the file back in as an executable Puppet manifest\\.\n.\n.TP\n\\-\\-help\nPrint this help message\\.\n.\n.TP\n\\-\\-param\nAdd more parameters to be outputted from queries\\.\n.\n.TP\n\\-\\-types\nList all available types\\.\n.\n.TP\n\\-\\-verbose\nPrint extra information\\.\n.\n.TP\n\\-\\-to_yaml\nOutput found resources in yaml format, suitable to use with Hiera and create_resources\\.\n.\n.TP\n\\-\\-fail\nFails and returns an exit code of 1 if the resource could not be modified\\.\n.\n.SH \"EXAMPLE\"\nThis example uses \\fBpuppet resource\\fR to return a Puppet configuration for the user \\fBluke\\fR:\n.\n.IP \"\" 4\n.\n.nf\n\n$ puppet resource user luke\nuser { \\'luke\\':\n home => \\'/home/luke\\',\n uid => \\'100\\',\n ensure => \\'present\\',\n comment => \\'Luke Kanies,,,\\',\n gid => \\'1000\\',\n shell => \\'/bin/bash\\',\n groups => [\\'sysadmin\\',\\'audio\\',\\'video\\',\\'puppet\\']\n}\n.\n.fi\n.\n.IP \"\" 0\n.\n.SH \"AUTHOR\"\nLuke Kanies\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2011 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-script.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-SCRIPT\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-script\\fR \\- Run a puppet manifests as a script without compiling a catalog\n.\n.SH \"SYNOPSIS\"\nRuns a puppet language script without compiling a catalog\\.\n.\n.SH \"USAGE\"\npuppet script [\\-h|\\-\\-help] [\\-V|\\-\\-version] [\\-d|\\-\\-debug] [\\-v|\\-\\-verbose] [\\-e|\\-\\-execute] [\\-l|\\-\\-logdest syslog|eventlog|\\fIFILE\\fR|console] [\\-\\-noop] \\fIfile\\fR\n.\n.SH \"DESCRIPTION\"\nThis is a standalone puppet script runner tool; use it to run puppet code without compiling a catalog\\.\n.\n.P\nWhen provided with a modulepath, via command line or config file, puppet script can load functions, types, tasks and plans from modules\\.\n.\n.SH \"OPTIONS\"\nNote that any setting that\\'s valid in the configuration file is also a valid long argument\\. For example, \\'environment\\' is a valid setting, so you can specify \\'\\-\\-environment mytest\\' as an argument\\.\n.\n.P\nSee the configuration file documentation at https://puppet\\.com/docs/puppet/latest/configuration\\.html for the full list of acceptable parameters\\. A commented list of all configuration options can also be generated by running puppet with \\'\\-\\-genconfig\\'\\.\n.\n.TP\n\\-\\-debug\nEnable full debugging\\.\n.\n.TP\n\\-\\-help\nPrint this help message\n.\n.TP\n\\-\\-logdest\nWhere to send log messages\\. Choose between \\'syslog\\' (the POSIX syslog service), \\'eventlog\\' (the Windows Event Log), \\'console\\', or the path to a log file\\. Defaults to \\'console\\'\\. Multiple destinations can be set using a comma separated list (eg: \\fB/path/file1,console,/path/file2\\fR)\"\n.\n.IP\nA path ending with \\'\\.json\\' will receive structured output in JSON format\\. The log file will not have an ending \\']\\' automatically written to it due to the appending nature of logging\\. It must be appended manually to make the content valid JSON\\.\n.\n.IP\nA path ending with \\'\\.jsonl\\' will receive structured output in JSON Lines format\\.\n.\n.TP\n\\-\\-noop\nUse \\'noop\\' mode where Puppet runs in a no\\-op or dry\\-run mode\\. This is useful for seeing what changes Puppet will make without actually executing the changes\\. Applies to tasks only\\.\n.\n.TP\n\\-\\-execute\nExecute a specific piece of Puppet code\n.\n.TP\n\\-\\-verbose\nPrint extra information\\.\n.\n.SH \"EXAMPLE\"\n.\n.nf\n\n$ puppet script \\-l /tmp/manifest\\.log manifest\\.pp\n$ puppet script \\-\\-modulepath=/root/dev/modules \\-e \\'notice(\"hello world\")\\'\n.\n.fi\n.\n.SH \"AUTHOR\"\nHenrik Lindberg\n.\n.SH \"COPYRIGHT\"\nCopyright (c) 2017 Puppet Inc\\., LLC Licensed under the Apache 2\\.0 License\n"
  },
  {
    "path": "man/man8/puppet-ssl.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\\-SSL\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\-ssl\\fR \\- Manage SSL keys and certificates for puppet SSL clients\n.\n.SH \"SYNOPSIS\"\nManage SSL keys and certificates for SSL clients needing to communicate with a puppet infrastructure\\.\n.\n.SH \"USAGE\"\npuppet ssl \\fIaction\\fR [\\-h|\\-\\-help] [\\-v|\\-\\-verbose] [\\-d|\\-\\-debug] [\\-\\-localca] [\\-\\-target CERTNAME]\n.\n.SH \"OPTIONS\"\n.\n.IP \"\\(bu\" 4\n\\-\\-help: Print this help message\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-verbose: Print extra information\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-debug: Enable full debugging\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-localca Also clean the local CA certificate and CRL\\.\n.\n.IP \"\\(bu\" 4\n\\-\\-target CERTNAME Clean the specified device certificate instead of this host\\'s certificate\\.\n.\n.IP \"\" 0\n.\n.SH \"ACTIONS\"\n.\n.TP\nbootstrap\nPerform all of the steps necessary to request and download a client certificate\\. If autosigning is disabled, then puppet will wait every \\fBwaitforcert\\fR seconds for its certificate to be signed\\. To only attempt once and never wait, specify a time of 0\\. Since \\fBwaitforcert\\fR is a Puppet setting, it can be specified as a time interval, such as 30s, 5m, 1h\\.\n.\n.TP\nsubmit_request\nGenerate a certificate signing request (CSR) and submit it to the CA\\. If a private and public key pair already exist, they will be used to generate the CSR\\. Otherwise, a new key pair will be generated\\. If a CSR has already been submitted with the given \\fBcertname\\fR, then the operation will fail\\.\n.\n.TP\ngenerate_request\nGenerate a certificate signing request (CSR)\\. If a private and public key pair exist, they will be used to generate the CSR\\. Otherwise a new key pair will be generated\\.\n.\n.TP\ndownload_cert\nDownload a certificate for this host\\. If the current private key matches the downloaded certificate, then the certificate will be saved and used for subsequent requests\\. If there is already an existing certificate, it will be overwritten\\.\n.\n.TP\nverify\nVerify the private key and certificate are present and match, verify the certificate is issued by a trusted CA, and check revocation status\\.\n.\n.TP\nclean\nRemove the private key and certificate related files for this host\\. If \\fB\\-\\-localca\\fR is specified, then also remove this host\\'s local copy of the CA certificate(s) and CRL bundle\\. if \\fB\\-\\-target CERTNAME\\fR is specified, then remove the files for the specified device on this host instead of this host\\.\n.\n.TP\nshow\nPrint the full\\-text version of this host\\'s certificate\\.\n\n"
  },
  {
    "path": "man/man8/puppet.8",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"PUPPET\" \"8\" \"November 2024\" \"Puppet, Inc.\" \"Puppet manual\"\n.\n.SH \"NAME\"\n\\fBpuppet\\fR \\- an automated configuration management tool\n.\n.SH \"SYNOPSIS\"\n\\fBpuppet\\fR \\fIsubcommand\\fR [options] \\fIaction\\fR [options]\n.\n.SH \"DESCRIPTION\"\nPuppet, 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\\.\n.\n.SH \"COMMANDS\"\n.\n.SS \"Common\"\n\\fBapply\\fR\n.\n.br\n\\~\\~\\~\\~Apply Puppet manifests locally\n.\n.P\n\\fBagent\\fR\n.\n.br\n\\~\\~\\~\\~The puppet agent daemon\n.\n.P\n\\fBconfig\\fR\n.\n.br\n\\~\\~\\~\\~Interact with Puppet\\'s settings\\.\n.\n.P\n\\fBhelp\\fR\n.\n.br\n\\~\\~\\~\\~Display Puppet help\\.\n.\n.P\n\\fBlookup\\fR\n.\n.br\n\\~\\~\\~\\~Interactive Hiera lookup\n.\n.P\n\\fBmodule\\fR\n.\n.br\n\\~\\~\\~\\~Creates, installs and searches for modules on the Puppet Forge\\.\n.\n.P\n\\fBresource\\fR\n.\n.br\n\\~\\~\\~\\~The resource abstraction layer shell\n.\n.SS \"Specialized\"\n\\fBstrings\\fR\n.\n.br\n\\~\\~\\~\\~Generate Puppet documentation with YARD\\.\n.\n.P\n\\fBcatalog\\fR\n.\n.br\n\\~\\~\\~\\~Compile, save, view, and convert catalogs\\.\n.\n.P\n\\fBdescribe\\fR\n.\n.br\n\\~\\~\\~\\~Display help about resource types\n.\n.P\n\\fBdevice\\fR\n.\n.br\n\\~\\~\\~\\~Manage remote network devices\n.\n.P\n\\fBdoc\\fR\n.\n.br\n\\~\\~\\~\\~Generate Puppet references\n.\n.P\n\\fBepp\\fR\n.\n.br\n\\~\\~\\~\\~Interact directly with the EPP template parser/renderer\\.\n.\n.P\n\\fBfacts\\fR\n.\n.br\n\\~\\~\\~\\~Retrieve and store facts\\.\n.\n.P\n\\fBfilebucket\\fR\n.\n.br\n\\~\\~\\~\\~Store and retrieve files in a filebucket\n.\n.P\n\\fBgenerate\\fR\n.\n.br\n\\~\\~\\~\\~Generates Puppet code from Ruby definitions\\.\n.\n.P\n\\fBnode\\fR\n.\n.br\n\\~\\~\\~\\~View and manage node definitions\\.\n.\n.P\n\\fBparser\\fR\n.\n.br\n\\~\\~\\~\\~Interact directly with the parser\\.\n.\n.P\n\\fBplugin\\fR\n.\n.br\n\\~\\~\\~\\~Interact with the Puppet plugin system\\.\n.\n.P\n\\fBscript\\fR\n.\n.br\n\\~\\~\\~\\~Run a puppet manifests as a script without compiling a catalog\n.\n.P\n\\fBssl\\fR\n.\n.br\n\\~\\~\\~\\~Manage SSL keys and certificates for puppet SSL clients\n.\n.SH \"SEE ALSO\"\nSee \\fBpuppet help <subcommand>\\fR for help on a specific subcommand\\.\n.\n.P\nSee \\fBpuppet help <subcommand> <action>\\fR for help on a specific subcommand action\\.\n"
  },
  {
    "path": "puppet.gemspec",
    "content": "Gem::Specification.new do |spec|\n  spec.name = \"puppet\"\n  spec.version = \"8.11.0\"\n  spec.licenses = ['Apache-2.0']\n\n  spec.required_rubygems_version = Gem::Requirement.new(\"> 1.3.1\")\n  spec.required_ruby_version = Gem::Requirement.new(\">= 3.1.0\")\n  spec.authors = [\"Puppet Labs\"]\n  spec.date = \"2012-08-17\"\n  spec.description = <<~EOF\n    Puppet, an automated administrative engine for your Linux, Unix, and Windows systems, performs administrative tasks\n    (such as adding users, installing packages, and updating server configurations) based on a centralized specification.\n  EOF\n  spec.email = \"info@puppetlabs.com\"\n  spec.executables = [\"puppet\"]\n  spec.files = Dir['[A-Z]*'] + Dir['install.rb'] + Dir['bin/*'] + Dir['lib/**/*'] + Dir['conf/*'] + Dir['man/**/*'] + Dir['tasks/*'] + Dir['locales/**/*'] + Dir['ext/**/*'] + Dir['examples/**/*']\n  spec.license = \"Apache-2.0\"\n  spec.homepage = \"https://github.com/puppetlabs/puppet\"\n  spec.rdoc_options = [\"--title\", \"Puppet - Configuration Management\", \"--main\", \"README\", \"--line-numbers\"]\n  spec.require_paths = [\"lib\"]\n  spec.summary = \"Puppet, an automated configuration management tool\"\n  spec.specification_version = 4\n  spec.add_runtime_dependency('concurrent-ruby', '~> 1.0')\n  spec.add_runtime_dependency('deep_merge', '~> 1.0')\n  spec.add_runtime_dependency('facter', ['>= 4.3.0', '< 5'])\n  spec.add_runtime_dependency('fast_gettext', '>= 2.1', '< 4')\n  spec.add_runtime_dependency('getoptlong', '~> 0.2.0')\n  spec.add_runtime_dependency('locale', '~> 2.1')\n  spec.add_runtime_dependency('multi_json', '~> 1.13')\n  spec.add_runtime_dependency('puppet-resource_api', '~> 1.5')\n  spec.add_runtime_dependency('scanf', '~> 1.0')\n  spec.add_runtime_dependency('semantic_puppet', '~> 1.0')\n\n  platform = spec.platform.to_s\n  if platform == 'universal-darwin'\n    spec.add_runtime_dependency('CFPropertyList', ['>= 3.0.6', '< 4'])\n  end\n\n  if platform == 'x64-mingw32' || platform == 'x86-mingw32'\n    # ffi 1.16.0 - 1.16.2 are broken on Windows\n    spec.add_runtime_dependency('ffi', '>= 1.15.5', '< 1.17.0', '!= 1.16.0', '!= 1.16.1', '!= 1.16.2')\n    spec.add_runtime_dependency('minitar', '~> 0.9')\n  end\nend\n"
  },
  {
    "path": "rakelib/benchmark.rake",
    "content": "require 'benchmark'\nrequire 'tmpdir'\nrequire 'csv'\nrequire 'objspace'\n\nnamespace :benchmark do\n  def generate_scenario_tasks(location, name)\n    desc File.read(File.join(location, 'description'))\n    task name => \"#{name}:run\"\n    # Load a BenchmarkerTask to handle config of the benchmark\n    task_handler_file = File.expand_path(File.join(location, 'benchmarker_task.rb'))\n    if File.exist?(task_handler_file)\n      require task_handler_file\n      run_args = BenchmarkerTask.run_args\n    else\n      run_args = []\n    end\n\n    namespace name do\n      task :setup do\n        ENV['ITERATIONS'] ||= '10'\n        ENV['SIZE'] ||= '100'\n        ENV['TARGET'] ||= Dir.mktmpdir(name)\n        ENV['TARGET'] = File.expand_path(ENV['TARGET'])\n\n        mkdir_p(ENV['TARGET'])\n\n        require File.expand_path(File.join(location, 'benchmarker.rb'))\n\n        @benchmark = Benchmarker.new(ENV['TARGET'], ENV['SIZE'].to_i)\n      end\n\n      task :generate => :setup do\n        @benchmark.generate\n        @benchmark.setup\n      end\n\n      desc \"Run the #{name} scenario.\"\n      task :run, [*run_args] =>  :generate do |_, args|\n        report = []\n        details = []\n        Benchmark.benchmark(Benchmark::CAPTION, 10, Benchmark::FORMAT, \"> total:\", \"> avg:\") do |b|\n          times = []\n          ENV['ITERATIONS'].to_i.times do |i|\n            start_time = Time.now.to_i\n            times << b.report(\"Run #{i + 1}\") do\n              details << @benchmark.run(args)\n            end\n            report << [to_millis(start_time), to_millis(times.last.real), 200, true, name]\n          end\n\n          sum = times.inject(Benchmark::Tms.new, &:+)\n\n          [sum, sum / times.length]\n        end\n\n        write_csv(\"#{name}.samples\",\n                  %w{timestamp elapsed responsecode success name},\n                  report)\n\n        # report details, if any were produced\n        if details[0].is_a?(Array) && details[0][0].is_a?(Benchmark::Tms)\n          # assume all entries are Tms if the first is\n          # turn each into a hash of label => tms (since labels are lost when doing arithmetic on Tms)\n          hashed = details.reduce([]) do |memo, measures|\n            memo << measures.reduce({}) {|memo2, measure| memo2[measure.label] = measure; memo2}\n            memo\n          end\n          # sum across all hashes\n          result = {}\n\n          hashed_totals = hashed.reduce {|memo, h| memo.merge(h) {|k, old, new| old + new }}\n          # average the totals\n          hashed_totals.keys.each {|k| hashed_totals[k] /= details.length }\n          min_width = 14\n          max_width = (hashed_totals.keys.map(&:length) << min_width).max\n          puts \"\\n\"\n          puts sprintf(\"%2$*1$s %3$s\", -max_width, 'Details (avg)', \"      user     system      total        real\")\n          puts \"-\" * (46 + max_width)\n          hashed_totals.sort.each {|k,v| puts sprintf(\"%2$*1$s %3$s\", -max_width, k, v.format) }\n        end\n      end\n\n      desc \"Profile a single run of the #{name} scenario.\"\n      task :profile, [:warm_up_runs, *run_args] => :generate do |_, args|\n        warm_up_runs = (args[:warm_up_runs] || '0').to_i\n        warm_up_runs.times do\n          @benchmark.run(args)\n        end\n\n        require 'ruby-prof'\n\n        result = RubyProf.profile do\n          @benchmark.run(args)\n        end\n\n        printer = RubyProf::CallTreePrinter.new(result)\n        printer.print(:profile => name, :path => ENV['TARGET'])\n        path = File.join(ENV['TARGET'], \"#{name}.callgrind.out.#{$$}\")\n        puts \"Generated callgrind file: #{path}\"\n      end\n\n      desc \"Print a memory profile of the #{name} scenario.\"\n      task :memory_profile, [*run_args] => :generate do |_, args|\n        begin\n          require 'memory_profiler'\n        rescue LoadError\n          abort(\"Run `bundle install --with development` to install the 'memory_profiler' gem.\")\n        end\n\n        report = MemoryProfiler.report do\n          @benchmark.run(args)\n        end\n\n        path = \"mem_profile_#{$PID}\"\n        report.pretty_print(to_file: path)\n\n        puts \"Generated memory profile: #{File.absolute_path(path)}\"\n      end\n\n      desc \"Generate a heap dump with object allocation tracing of the #{name} scenario.\"\n      task :heap_dump, [*run_args] => :generate do |_, args|\n        ObjectSpace.trace_object_allocations_start\n\n        if ENV['DISABLE_GC']\n          GC.disable\n        end\n\n        @benchmark.run(args)\n\n        unless ENV['DISABLE_GC']\n          GC.start\n        end\n\n        path = \"heap_#{$PID}.json\"\n        File.open(path, 'w') do |file|\n          ObjectSpace.dump_all(output: file)\n        end\n\n        puts \"Generated heap dump: #{File.absolute_path(path)}\"\n      end\n\n      def to_millis(seconds)\n        (seconds * 1000).round\n      end\n\n      def write_csv(file, header, data)\n        CSV.open(file, 'w') do |csv|\n          csv << header\n          data.each do |line|\n            csv << line\n          end\n        end\n      end\n    end\n  end\n\n  scenarios = []\n  Dir.glob('benchmarks/*') do |location|\n    name = File.basename(location)\n    scenarios << name\n    generate_scenario_tasks(location, File.basename(location))\n  end\n\n  namespace :all do\n    desc \"Profile all of the scenarios. (#{scenarios.join(', ')})\"\n    task :profile do\n      scenarios.each do |name|\n        sh \"rake benchmark:#{name}:profile\"\n      end\n    end\n\n    desc \"Run all of the scenarios. (#{scenarios.join(', ')})\"\n    task :run do\n      scenarios.each do |name|\n        sh \"rake benchmark:#{name}:run\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "rakelib/ci.rake",
    "content": "require 'yaml'\nrequire 'time'\n\nnamespace \"ci\" do\n  desc \"Tar up the acceptance/ directory so that package test runs have tests to run against.\"\n  task :acceptance_artifacts => :tag_creator do\n    Dir.chdir(\"acceptance\") do\n      rm_f \"acceptance-artifacts.tar.gz\"\n      sh \"tar -czv --exclude .bundle -f acceptance-artifacts.tar.gz *\"\n    end\n  end\n\n  task :tag_creator do\n    Dir.chdir(\"acceptance\") do\n      File.open('creator.txt', 'w') do |fh|\n        YAML.dump({\n          'creator_id' => ENV['CREATOR'] || ENV['BUILD_URL'] || 'unknown',\n          'created_on' => Time.now.iso8601,\n          'commit' => (`git log -1 --oneline` rescue \"unknown: #{$!}\")\n        }, fh)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "rakelib/generate_ast_model.rake",
    "content": "begin\n  require 'puppet'\nrescue LoadError\n  #nothing to see here\nelse\n  desc \"Generate the Pcore model that represents the AST for the Puppet Language\"\n  task :gen_pcore_ast do\n    Puppet::Pops.generate_ast\n  end\n\n  module Puppet::Pops\n    def self.generate_ast\n      Puppet.initialize_settings\n      env = Puppet.lookup(:environments).get(Puppet[:environment])\n      loaders = Loaders.new(env)\n      ast_pp = Pathname(__FILE__).parent.parent + 'lib/puppet/pops/model/ast.pp'\n      Puppet.override(:current_environment => env, :loaders => loaders) do\n        ast_factory = Parser::Parser.new.parse_file(ast_pp.expand_path.to_s)\n        ast_model = Types::TypeParser.singleton.interpret(\n          ast_factory.model.body, Loader::PredefinedLoader.new(loaders.find_loader(nil), 'TypeSet loader'))\n\n        ruby = Types::RubyGenerator.new.module_definition_from_typeset(ast_model)\n\n        # Replace ref() constructs to known Pcore types with directly initialized types. ref() cannot be used\n        # since it requires a parser (chicken-and-egg problem)\n        ruby.gsub!(/^module Parser\\nmodule Locator\\n.*\\nend\\nend\\nmodule Model\\n/m, \"module Model\\n\")\n\n        # Remove generated RubyMethod annotations. The ruby methods are there now, no need to also have\n        # the annotations present.\n        ruby.gsub!(/^\\s+'annotations' => \\{\\n\\s+ref\\('RubyMethod'\\) => \\{\\n.*\\n\\s+\\}\\n\\s+\\},\\n/, '')\n\n        ruby.gsub!(/ref\\('([A-Za-z]+)'\\)/, 'Types::P\\1Type::DEFAULT')\n        ruby.gsub!(/ref\\('Optional\\[([0-9A-Za-z_]+)\\]'\\)/, 'Types::POptionalType.new(Types::P\\1Type::DEFAULT)')\n        ruby.gsub!(/ref\\('Array\\[([0-9A-Za-z_]+)\\]'\\)/, 'Types::PArrayType.new(Types::P\\1Type::DEFAULT)')\n        ruby.gsub!(/ref\\('Optional\\[Array\\[([0-9A-Za-z_]+)\\]\\]'\\)/,\n            'Types::POptionalType.new(Types::PArrayType.new(Types::P\\1Type::DEFAULT))')\n        ruby.gsub!(/ref\\('Enum(\\[[^\\]]+\\])'\\)/) do |match|\n          params = $1\n          params.gsub!(/\\\\'/, '\\'')\n          \"Types::PEnumType.new(#{params})\"\n        end\n\n        # Replace ref() constructs with references to _pcore_type of the types in the module namespace\n        ruby.gsub!(/ref\\('Puppet::AST::Locator'\\)/, 'Parser::Locator::Locator19._pcore_type')\n        ruby.gsub!(/ref\\('Puppet::AST::([0-9A-Za-z_]+)'\\)/, '\\1._pcore_type')\n        ruby.gsub!(/ref\\('Optional\\[Puppet::AST::([0-9A-Za-z_]+)\\]'\\)/, 'Types::POptionalType.new(\\1._pcore_type)')\n        ruby.gsub!(/ref\\('Array\\[Puppet::AST::([0-9A-Za-z_]+)\\]'\\)/, 'Types::PArrayType.new(\\1._pcore_type)')\n        ruby.gsub!(/ref\\('Array\\[Puppet::AST::([0-9A-Za-z_]+), 1, default\\]'\\)/,\n            'Types::PArrayType.new(\\1._pcore_type, Types::PCollectionType::NOT_EMPTY_SIZE)')\n\n        # Remove the generated ref() method. It's not needed by this model\n        ruby.gsub!(/  def self\\.ref\\(type_string\\)\\n.*\\n  end\\n\\n/, '')\n\n        # Add Program#current method for backward compatibility\n        ruby.gsub!(/(attr_reader :body\\n  attr_reader :definitions\\n  attr_reader :locator)/, \"\\\\1\\n\\n  def current\\n    self\\n  end\")\n\n        # Replace the generated registration with a registration that uses the static loader. This will\n        # become part of the Puppet bootstrap code and there will be no other loader until we have a\n        # parser.\n        ruby.gsub!(/^Puppet::Pops::Pcore.register_implementations\\((\\[[^\\]]+\\])\\)/, <<-RUBY)\n\nmodule Model\n@@pcore_ast_initialized = false\ndef self.register_pcore_types\n  return if @@pcore_ast_initialized\n  @@pcore_ast_initialized = true\n  all_types = \\\\1\n\n  # Create and register a TypeSet that corresponds to all types in the AST model\n  types_map = {}\n  all_types.each do |type|\n    types_map[type._pcore_type.simple_name] = type._pcore_type\n  end\n  type_set = Types::PTypeSetType.new({\n    'name' => 'Puppet::AST',\n    'pcore_version' => '1.0.0',\n    'types' => types_map\n  })\n  loc = Puppet::Util.path_to_uri(\"\\#{__FILE__}\")\n  Loaders.static_loader.set_entry(Loader::TypedName.new(:type, 'puppet::ast', Pcore::RUNTIME_NAME_AUTHORITY), type_set, URI(\"\\#{loc}?line=1\"))\n  Loaders.register_static_implementations(all_types)\nend\nend\nRUBY\n        ast_rb = Pathname(__FILE__).parent.parent + 'lib/puppet/pops/model/ast.rb'\n        File.open(ast_rb.to_s, 'w') { |f| f.write(ruby) }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "rakelib/generate_cert_fixtures.rake",
    "content": "# Run this rake task to generate cert fixtures used in unit tests. This should\n# be run whenever new fixtures are required that derive from the existing ones\n# such as to add an extension to client certs, change expiration, etc. All\n# regenerated fixtures should be committed together.\ndesc \"Generate cert test fixtures\"\ntask(:gen_cert_fixtures) do\n  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '../spec/lib'))\n  require 'puppet/test_ca'\n\n  def save(dir, name, x509)\n    path = File.join(dir, name)\n    puts \"Generating #{path}\"\n    File.open(path, 'w') do |f|\n      f.write(x509.to_text)\n      text = if block_given?\n               yield x509\n             else\n               x509.to_pem\n             end\n\n      f.write(text)\n    end\n  end\n\n  # This task generates a PKI consisting of a root CA, intermediate CA and\n  # several leaf certs. A CRL is generated for each CA. The root CA CRL is\n  # empty, while the intermediate CA CRL contains the revoked cert's serial\n  # number. A textual representation of each X509 object is included in the\n  # fixture as a comment.\n  #\n  # Certs\n  # =====\n  #\n  # ca.pem                           /CN=Test CA\n  #                                   |\n  # intermediate.pem                  +- /CN=Test CA Subauthority\n  #                                   |   |\n  # signed.pem                        |   +- /CN=signed\n  # revoked.pem                       |   +- /CN=revoked\n  # tampered-cert.pem                 |   +- /CN=signed (with different public key)\n  # ec.pem                            |   +- /CN=ec (with EC private key)\n  # oid.pem                           |   +- /CN=oid (with custom oid)\n  #                                   |\n  # 127.0.0.1.pem                     +- /CN=127.0.0.1 (with dns alt names)\n  #                                   |\n  # intermediate-agent.pem            +- /CN=Test CA Agent Subauthority\n  #                                   |   |\n  # pluto.pem                         |   +- /CN=pluto\n  #                                   |\n  # bad-int-basic-constraints.pem     +- /CN=Test CA Subauthority (bad isCA constraint)\n  #\n  # bad-basic-constraints.pem        /CN=Test CA (bad isCA constraint)\n  #\n  # unknown-ca.pem                   /CN=Unknown CA\n  #                                   |\n  # unknown-127.0.0.1.pem             +- /CN=127.0.0.1\n  #\n  # Keys\n  # ====\n  #\n  # The RSA private key for each leaf cert is also generated. In addition,\n  # `encrypted-key.pem` contains the private key for the `signed` cert.\n  #\n  # Requests\n  # ========\n  #\n  # `request.pem` contains a valid CSR for /CN=pending, while `tampered_csr.pem`\n  # is the same as `request.pem`, but it's public key has been replaced.\n  #\n  dir = File.join(RAKE_ROOT, 'spec/fixtures/ssl')\n\n  # Create self-signed CA & key\n  unknown_ca = Puppet::TestCa.new('Unknown CA')\n  save(dir, 'unknown-ca.pem', unknown_ca.ca_cert)\n  save(dir, 'unknown-ca-key.pem', unknown_ca.key)\n\n  # Create an SSL cert for 127.0.0.1\n  signed = unknown_ca.create_cert('127.0.0.1', unknown_ca.ca_cert, unknown_ca.key, subject_alt_names: 'DNS:127.0.0.1,DNS:127.0.0.2')\n  save(dir, 'unknown-127.0.0.1.pem', signed[:cert])\n  save(dir, 'unknown-127.0.0.1-key.pem', signed[:private_key])\n\n  # Create Test CA & CRL\n  ca = Puppet::TestCa.new\n  save(dir, 'ca.pem', ca.ca_cert)\n  save(dir, 'crl.pem', ca.ca_crl)\n\n  # Create Intermediate CA & CRL \"Test CA Subauthority\" issued by \"Test CA\"\n  inter = ca.create_intermediate_cert('Test CA Subauthority', ca.ca_cert, ca.key)\n  save(dir, 'intermediate.pem', inter[:cert])\n  save(dir, 'intermediate-key.pem', inter[:private_key])\n  inter_crl = ca.create_crl(inter[:cert], inter[:private_key])\n\n  # Create a leaf/entity key and cert for host \"signed\" and issued by \"Test CA Subauthority\"\n  signed = ca.create_cert('signed', inter[:cert], inter[:private_key])\n  save(dir, 'signed.pem', signed[:cert])\n  save(dir, 'signed-key.pem', signed[:private_key])\n\n  # Create a cert for host \"renewed\" and issued by \"Test CA Subauthority\"\n  renewed = ca.create_cert('renewed', inter[:cert], inter[:private_key], reuse_key: signed[:private_key])\n  save(dir, 'renewed.pem', renewed[:cert])\n\n  # Create an encrypted version of the above private key for host \"signed\"\n  save(dir, 'encrypted-key.pem', signed[:private_key]) do |x509|\n    # private key password was chosen at random\n    x509.to_pem(OpenSSL::Cipher::AES.new(128, :CBC), '74695716c8b6')\n  end\n\n  # Create an SSL cert for 127.0.0.1 with dns_alt_names\n  signed = ca.create_cert('127.0.0.1', ca.ca_cert, ca.key, subject_alt_names: 'DNS:127.0.0.1,DNS:127.0.0.2')\n  save(dir, '127.0.0.1.pem', signed[:cert])\n  save(dir, '127.0.0.1-key.pem', signed[:private_key])\n\n  # Create an SSL cert with extensions containing custom oids\n  extensions = [\n    ['1.3.6.1.4.1.34380.1.2.1.1', OpenSSL::ASN1::UTF8String.new('somevalue'), false],\n  ]\n  oid = ca.create_cert('oid', inter[:cert], inter[:private_key], extensions: extensions)\n  save(dir, 'oid.pem', oid[:cert])\n  save(dir, 'oid-key.pem', oid[:private_key])\n\n  # Create a leaf/entity key and cert for host \"revoked\", issued by \"Test CA Subauthority\"\n  # and revoke the cert\n  revoked = ca.create_cert('revoked', inter[:cert], inter[:private_key])\n  ca.revoke(revoked[:cert], inter_crl, inter[:private_key])\n  save(dir, 'revoked.pem', revoked[:cert])\n  save(dir, 'revoked-key.pem', revoked[:private_key])\n\n  # Create an EC key and cert, issued by \"Test CA Subauthority\"\n  ec = ca.create_cert('ec', inter[:cert], inter[:private_key], key_type: :ec)\n  save(dir, 'ec.pem', ec[:cert])\n  save(dir, 'ec-key.pem', ec[:private_key])\n\n  # Create an encrypted version of the above private key for host \"ec\"\n  save(dir, 'encrypted-ec-key.pem', ec[:private_key]) do |x509|\n    # private key password was chosen at random\n    x509.to_pem(OpenSSL::Cipher::AES.new(128, :CBC), '74695716c8b6')\n  end\n\n  # Update intermediate CRL now that we've revoked\n  save(dir, 'intermediate-crl.pem', inter_crl)\n\n  # Create a pending request (CSR) and private key for host \"pending\"\n  request = ca.create_request('pending')\n  save(dir, 'request.pem', request[:csr])\n  save(dir, 'request-key.pem', request[:private_key])\n\n  # Create an intermediate for agent certs\n  inter_agent = ca.create_intermediate_cert('Test CA Agent Subauthority', ca.ca_cert, ca.key)\n  save(dir, 'intermediate-agent.pem', inter_agent[:cert])\n  inter_agent_crl = ca.create_crl(inter_agent[:cert], inter_agent[:private_key])\n  save(dir, 'intermediate-agent-crl.pem', inter_agent_crl)\n\n  # Create a leaf/entity key and cert for host \"pluto\" and issued by \"Test CA Agent Subauthority\"\n  pluto = ca.create_cert('pluto', inter_agent[:cert], inter_agent[:private_key])\n  save(dir, 'pluto.pem', pluto[:cert])\n  save(dir, 'pluto-key.pem', pluto[:private_key])\n\n  # Create a new root CA cert, but change the \"isCA\" basic constraint.\n  # It should not be trusted to act as a CA.\n  badconstraints = ca.create_cacert('Test CA')[:cert]\n  badconstraints.public_key = ca.ca_cert.public_key\n  badconstraints.extensions = []\n  ca.ca_cert.extensions.each do |ext|\n    if ext.oid == 'basicConstraints'\n      ef = OpenSSL::X509::ExtensionFactory.new\n      badconstraints.add_extension(ef.create_extension(\"basicConstraints\",\"CA:FALSE\", true))\n    else\n      badconstraints.add_extension(ext)\n    end\n  end\n  badconstraints.sign(ca.key, OpenSSL::Digest::SHA256.new)\n  save(dir, 'bad-basic-constraints.pem', badconstraints)\n\n  # Same as above, but create a new intermediate CA\n  badintconstraints = inter[:cert].dup\n  badintconstraints.public_key = inter[:cert].public_key\n  badintconstraints.extensions = []\n  inter[:cert].extensions.each do |ext|\n    if ext.oid == 'basicConstraints'\n      ef = OpenSSL::X509::ExtensionFactory.new\n      badintconstraints.add_extension(ef.create_extension(\"basicConstraints\",\"CA:FALSE\", true))\n    else\n      badintconstraints.add_extension(ext)\n    end\n  end\n  badintconstraints.sign(ca.key, OpenSSL::Digest::SHA256.new)\n  save(dir, 'bad-int-basic-constraints.pem', badintconstraints)\n\n  # Create a request, but replace its public key after it's signed\n  tampered_csr = ca.create_request('signed')[:csr]\n  tampered_csr.public_key = OpenSSL::PKey::RSA.new(2048).public_key\n  save(dir, 'tampered-csr.pem', tampered_csr)\n\n  # Create a cert issued from the real intermediate CA, but replace its\n  # public key\n  tampered_cert = ca.create_cert('signed', inter[:cert], inter[:private_key])[:cert]\n  tampered_cert.public_key = OpenSSL::PKey::RSA.new(2048).public_key\n  save(dir, 'tampered-cert.pem', tampered_cert)\nend\n"
  },
  {
    "path": "rakelib/generate_references.rake",
    "content": "require 'tempfile'\n\nOUTPUT_DIR = 'references'\nMANDIR = File.join(OUTPUT_DIR, 'man')\nTYPES_DIR = File.join(OUTPUT_DIR, 'types')\n\nCONFIGURATION_ERB = File.join(__dir__, 'references/configuration.erb')\nCONFIGURATION_MD  = File.join(OUTPUT_DIR, 'configuration.md')\nMETAPARAMETER_ERB = File.join(__dir__, 'references/metaparameter.erb')\nMETAPARAMETER_MD  = File.join(OUTPUT_DIR, 'metaparameter.md')\nREPORT_ERB        = File.join(__dir__, 'references/report.erb')\nREPORT_MD         = File.join(OUTPUT_DIR, 'report.md')\nFUNCTIONS_TEMPLATE_ERB = File.join(__dir__, 'references/functions_template.erb')\nFUNCTION_ERB      = File.join(__dir__, 'references/function.erb')\nFUNCTION_MD       = File.join(OUTPUT_DIR, 'function.md')\nMAN_OVERVIEW_ERB  = File.join(__dir__, 'references/man/overview.erb')\nMAN_OVERVIEW_MD   = File.join(MANDIR, \"overview.md\")\nMAN_ERB           = File.join(__dir__, 'references/man.erb')\nTYPES_OVERVIEW_ERB = File.join(__dir__, 'references/types/overview.erb')\nTYPES_OVERVIEW_MD  = File.join(TYPES_DIR, 'overview.md')\nUNIFIED_TYPE_ERB = File.join(__dir__, 'references/unified_type.erb')\nUNIFIED_TYPE_MD  = File.join(OUTPUT_DIR, 'type.md')\nSINGLE_TYPE_ERB  = File.join(__dir__, 'references/types/single_type.erb')\n\ndef render_erb(erb_file, variables)\n  # Create a binding so only the variables we specify will be visible\n  template_binding = OpenStruct.new(variables).instance_eval {binding}\n  ERB.new(File.read(erb_file), trim_mode: '-').result(template_binding)\nend\n\ndef puppet_doc(reference)\n  body = %x{bundle exec puppet doc -r #{reference}}\n  # Remove the first H1 with the title, like \"# Metaparameter Reference\"\n  body.sub!(/^# \\w+ Reference *$/, '')\n  body.chomp\nend\n\n# This is adapted from https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/puppet_doc.rb#L22-L36\ndef generate_reference(reference, erb, body, output)\n  sha = %x{git rev-parse HEAD}.chomp\n  now = Time.now\n  variables = {\n    sha: sha,\n    now: now,\n    body: body\n  }\n  content = render_erb(erb, variables)\n  File.write(output, content)\n  puts \"Generated #{output}\"\nend\n\n# Render type information for the specified resource type\n# Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L87-L112\ndef render_resource_type(name, this_type)\n  sorted_attribute_list = this_type['attributes'].keys.sort {|a,b|\n    # Float namevar(s) to the top and ensure after\n    # followed by the others in sort order\n    if this_type['attributes'][a]['namevar']\n      -1\n    elsif this_type['attributes'][b]['namevar']\n      1\n    elsif a == 'ensure'\n      -1\n    elsif b == 'ensure'\n      1\n    else\n      a <=> b\n    end\n  }\n\n  variables = {\n    name: name,\n    this_type: this_type,\n    sorted_attribute_list: sorted_attribute_list,\n    sorted_feature_list: this_type['features'].keys.sort,\n    longest_attribute_name: sorted_attribute_list.collect{|attr| attr.length}.max\n  }\n  erb = File.join(__dir__, 'references/types/type.erb')\n  render_erb(erb, variables)\nend\n\n# Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type_strings.rb#L19-L99\ndef extract_resource_types(strings_data)\n  strings_data['resource_types'].reduce(Hash.new) do |memo, type|\n    memo[ type['name'] ] = {\n      'description' => type['docstring']['text'],\n      'features' => (type['features'] || []).reduce(Hash.new) {|memo, feature|\n        memo[feature['name']] = feature['description']\n        memo\n      },\n      'providers' => strings_data['providers'].select {|provider|\n        provider['type_name'] == type['name']\n      }.reduce(Hash.new) {|memo, provider|\n        description = provider['docstring']['text']\n        if provider['commands'] || provider['confines'] || provider['defaults']\n          description = description + \"\\n\"\n        end\n        if provider['commands']\n          description = description + \"\\n* Required binaries: `#{provider['commands'].values.sort.join('`, `')}`\"\n        end\n        if provider['confines']\n          description = description + \"\\n* Confined to: `#{provider['confines'].map{|fact,val| \"#{fact} == #{val}\"}.join('`, `')}`\"\n        end\n        if provider['defaults']\n          description = description + \"\\n* Default for: `#{provider['defaults'].map{|fact,val| \"#{fact} == #{val}\"}.join('`, `')}`\"\n        end\n        if provider['features']\n          description = description + \"\\n* Supported features: `#{provider['features'].sort.join('`, `')}`\"\n        end\n        memo[provider['name']] = {\n          'features' => (provider['features'] || []),\n          'description' => description\n        }\n        memo\n      },\n      'attributes' => (type['parameters'] || []).reduce(Hash.new) {|memo, attribute|\n        description = attribute['description'] || ''\n        if attribute['default']\n          description = description + \"\\n\\nDefault: `#{attribute['default']}`\"\n        end\n        if attribute['values']\n          description = description + \"\\n\\nAllowed values:\\n\\n\" + attribute['values'].map{|val| \"* `#{val}`\"}.join(\"\\n\")\n        end\n        memo[attribute['name']] = {\n          'description' => description,\n          'kind' => 'parameter',\n          'namevar' => attribute['isnamevar'] ? true : false,\n          'required_features' => attribute['required_features'],\n        }\n        memo\n      }.merge( (type['properties'] || []).reduce(Hash.new) {|memo, attribute|\n          description = attribute['description'] || ''\n          if attribute['default']\n            description = description + \"\\n\\nDefault: `#{attribute['default']}`\"\n          end\n          if attribute['values']\n            description = description + \"\\n\\nAllowed values:\\n\\n\" + attribute['values'].map{|val| \"* `#{val}`\"}.join(\"\\n\")\n          end\n          memo[attribute['name']] = {\n            'description' => description,\n            'kind' => 'property',\n            'namevar' => false,\n            'required_features' => attribute['required_features'],\n          }\n          memo\n        }).merge( (type['checks'] || []).reduce(Hash.new) {|memo, attribute|\n            description = attribute['description'] || ''\n            if attribute['default']\n              description = description + \"\\n\\nDefault: `#{attribute['default']}`\"\n            end\n            if attribute['values']\n              description = description + \"\\n\\nAllowed values:\\n\\n\" + attribute['values'].map{|val| \"* `#{val}`\"}.join(\"\\n\")\n            end\n            memo[attribute['name']] = {\n              'description' => description,\n              'kind' => 'check',\n              'namevar' => false,\n              'required_features' => attribute['required_features'],\n            }\n            memo\n          })\n    }\n    memo\n  end\nend\n\n# Extract type documentation from the current version of puppet. Based on\n# https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L52\n#\n# REMIND This is kind of convoluted and means we're using two completely different\n# code paths to generate the overview and unified page of types.\ndef unified_page_resource_types\n  type_json = %x{ruby #{File.join(__dir__, 'references/get_typedocs.rb')}}\n  type_data = JSON.load(type_json)\n  type_data.keys.sort.map do |name|\n    render_resource_type(name, type_data[name])\n  end\nend\n\nnamespace :references do\n  desc \"Generate configuration reference\"\n  task :configuration do\n    body = puppet_doc('configuration')\n    generate_reference('configuration', CONFIGURATION_ERB, body, CONFIGURATION_MD)\n  end\n\n  desc \"Generate metaparameter reference\"\n  task :metaparameter do\n    body = puppet_doc('metaparameter')\n    generate_reference('metaparameter', METAPARAMETER_ERB, body, METAPARAMETER_MD)\n  end\n\n  desc \"Generate report reference\"\n  task :report do\n    body = puppet_doc('report')\n    generate_reference('report', REPORT_ERB, body, REPORT_MD)\n  end\n\n  desc \"Generate function reference\"\n  task :function do\n    # Locate puppet-strings\n    begin\n      require 'puppet-strings'\n      require 'puppet-strings/version'\n    rescue LoadError\n      abort(\"Run `bundle config set with documentation` and `bundle update` to install the `puppet-strings` gem.\")\n    end\n\n    strings_data = {}\n    Tempfile.create do |tmpfile|\n      puts \"Running puppet strings #{PuppetStrings::VERSION}\"\n      PuppetStrings.generate(['lib/puppet/{functions,parser/functions}/**/*.rb'], json: true, path: tmpfile.path)\n      strings_data = JSON.load_file(tmpfile.path)\n    end\n\n    # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/functions.rb#L24-L56\n    functions = strings_data['puppet_functions']\n\n    # Deal with the duplicate 3.x and 4.x functions\n    # 1. Figure out which functions are duplicated.\n    names = functions.map { |func| func['name'] }\n    duplicates = names.uniq.select { |name| names.count(name) > 1 }\n    # 2. Reject the 3.x version of any dupes.\n    functions = functions.reject do |func|\n      duplicates.include?(func['name']) && func['type'] != 'ruby4x'\n    end\n\n    # renders the list of functions\n    body = render_erb(FUNCTIONS_TEMPLATE_ERB, functions: functions)\n\n    # This substitution could potentially make things a bit brittle, but it has to be done because the jump\n    # From H2s to H4s is causing issues with the DITA-OT, which sees this as a rule violation. If it\n    # Does become an issue, we should return to this and figure out a better way to generate the functions doc.\n    body.gsub!(/#####\\s(.*?:)/,'**\\1**').gsub!(/####\\s/,'### ').chomp!\n\n    # renders the preamble and list of functions\n    generate_reference('function', FUNCTION_ERB, body, FUNCTION_MD)\n  end\n\n  desc \"Generate man as markdown references\"\n  task :man do\n    FileUtils.mkdir_p(MANDIR)\n\n    begin\n      require 'pandoc-ruby'\n    rescue LoadError\n      abort(\"Run `bundle config set with documentation` and `bundle update` to install the `pandoc-ruby` gem.\")\n    end\n\n    pandoc = %x{which pandoc}.chomp\n    unless File.executable?(pandoc)\n      abort(\"Please install the `pandoc` package.\")\n    end\n\n    sha = %x{git rev-parse HEAD}.chomp\n    now = Time.now\n\n    # This is based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/man.rb#L24-L108\n    core_apps = %w(\n      agent\n      apply\n      lookup\n      module\n      resource\n    )\n    occasional_apps = %w(\n      config\n      describe\n      device\n      doc\n      epp\n      generate\n      help\n      node\n      parser\n      plugin\n      script\n      ssl\n    )\n    weird_apps = %w(\n      catalog\n      facts\n      filebucket\n      report\n    )\n\n    variables = {\n      sha: sha,\n      now: now,\n      title: 'Puppet Man Pages',\n      core_apps: core_apps,\n      occasional_apps: occasional_apps,\n      weird_apps: weird_apps\n    }\n\n    content = render_erb(MAN_OVERVIEW_ERB, variables)\n    File.write(MAN_OVERVIEW_MD, content)\n    puts \"Generated #{MAN_OVERVIEW_MD}\"\n\n    # Generate manpages in roff\n    Rake::Task[:gen_manpages].invoke\n\n    # Convert the roff formatted man pages to markdown, based on\n    # https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/man.rb#L119-L128\n    files = Pathname.glob(File.join(__dir__, '../man/man8/*.8'))\n    files.each do |f|\n      next if File.basename(f) == \"puppet.8\"\n\n      app = File.basename(f).delete_prefix('puppet-').delete_suffix(\".8\")\n\n      body =\n        PandocRuby.convert([f], from: :man, to: :markdown)\n        .gsub(/#(.*?)\\n/, '##\\1')\n        .gsub(/:\\s\\s\\s\\n\\n```\\{=html\\}\\n<!--\\s-->\\n```/, '')\n        .gsub(/\\n:\\s\\s\\s\\s/, '')\n        .chomp\n\n      variables = {\n        sha: sha,\n        now: now,\n        title: \"Man Page: puppet #{app}\",\n        canonical: \"/puppet/latest/man/#{app}.html\",\n        body: body\n      }\n\n      content = render_erb(MAN_ERB, variables)\n      output = File.join(MANDIR, \"#{app}.md\")\n      File.write(output, content)\n      puts \"Generated #{output}\"\n    end\n  end\n\n  desc \"Generate resource type references\"\n  task :type do\n    FileUtils.mkdir_p(TYPES_DIR)\n\n    # Locate puppet-strings\n    begin\n      require 'puppet-strings'\n      require 'puppet-strings/version'\n    rescue LoadError\n      abort(\"Run `bundle config set with documentation` and `bundle update` to install the `puppet-strings` gem.\")\n    end\n\n    sha = %x{git rev-parse HEAD}.chomp\n    now = Time.now\n\n    # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/strings.rb#L25-L26\n    Tempfile.create do |tmpfile|\n      puts \"Running puppet strings #{PuppetStrings::VERSION}\"\n      PuppetStrings.generate(['lib/puppet/type/*.rb'], json: true, path: tmpfile.path)\n      strings_data = JSON.load_file(tmpfile.path)\n\n      # convert strings output to data the overview ERB template expects\n      type_data = extract_resource_types(strings_data)\n\n      # Generate overview.md\n      # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L40-L47\n      types = type_data.keys.reject do |type|\n        type == 'component' || type == 'whit'\n      end\n\n      variables = {\n        title: 'Resource types overview',\n        sha: sha,\n        now: now,\n        types: types\n      }\n\n      # Render overview page\n      content = render_erb(TYPES_OVERVIEW_ERB, variables)\n      File.write(TYPES_OVERVIEW_MD, content)\n      puts \"Generated #{TYPES_OVERVIEW_MD}\"\n\n      # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L55-L70\n      # unified page of types\n      variables = {\n        title: 'Resource Type Reference (Single-Page)',\n        sha: sha,\n        now: now,\n        types: unified_page_resource_types\n      }\n\n      content = render_erb(UNIFIED_TYPE_ERB, variables)\n      File.write(UNIFIED_TYPE_MD, content)\n      puts \"Generated #{UNIFIED_TYPE_MD}\"\n\n      # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L78-L85\n      # one type per page\n      types.each do |type|\n        variables = {\n          title: \"Resource Type: #{type}\",\n          type: type,\n          sha: sha,\n          now: now,\n          canonical: \"/puppet/latest/types/#{type}.html\",\n          body: render_resource_type(type, type_data[type])\n        }\n\n        content = render_erb(SINGLE_TYPE_ERB, variables)\n        output = File.join(TYPES_DIR, \"#{type}.md\")\n        File.write(output, content)\n        puts \"Generated #{output}\"\n      end\n    end\n  end\n\n  desc \"Generate all reference documentation\"\n  task :all do\n    %w[configuration function report metaparameter man type].each do |ref|\n      Rake::Task[\"references:#{ref}\"].invoke\n    end\n  end\nend\n"
  },
  {
    "path": "rakelib/man/puppet.erb",
    "content": "puppet(8) - an automated configuration management tool\n=========\n\n## SYNOPSIS\n\n`puppet` <subcommand> [options] <action> [options]\n\n## DESCRIPTION\n\nPuppet, an automated administrative engine for your Linux, Unix, and Windows systems, performs administrative tasks\n(such as adding users, installing packages, and updating server configurations) based on a centralized specification.\n\n## COMMANDS\n\n### Common\n\n<% common.each do |appname, summary, _| -%>\n`<%= appname.to_s %>`<br>\n&nbsp;&nbsp;&nbsp;&nbsp;<%= summary %>\n\n<% end -%>\n\n### Specialized\n\n<% specialized.each do |appname, summary, _| -%>\n`<%= appname.to_s %>`<br>\n&nbsp;&nbsp;&nbsp;&nbsp;<%= summary %>\n\n<% end -%>\n\n## SEE ALSO\nSee `puppet help <subcommand>` for help on a specific subcommand.\n\nSee `puppet help <subcommand> <action>` for help on a specific subcommand action.\n"
  },
  {
    "path": "rakelib/manpages.rake",
    "content": "desc \"Build Puppet manpages\"\ntask :gen_manpages do\n  require 'puppet/face'\n  require 'fileutils'\n\n  Puppet.initialize_settings\n  helpface = Puppet::Face[:help, '0.0.1']\n\n  non_face_applications = helpface.legacy_applications\n  faces = Puppet::Face.faces.map(&:to_s)\n  # exclude puppet-strings\n  faces.delete('strings')\n  apps = non_face_applications + faces\n\n  ronn_args = '--manual=\"Puppet manual\" --organization=\"Puppet, Inc.\" --roff'\n\n  unless ENV['SOURCE_DATE_EPOCH'].nil?\n    source_date = Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).strftime('%Y-%m-%d')\n    ronn_args += \" --date=#{source_date}\"\n  end\n\n  # Locate ronn\n  begin\n    require 'ronn'\n  rescue LoadError\n    abort(\"Run `bundle install --with documentation` to install the `ronn` gem.\")\n  end\n\n  ronn = %x{which ronn}.chomp\n  unless File.executable?(ronn)\n    abort(\"Ronn does not appear to be installed\")\n  end\n\n  %x{mkdir -p ./man/man5 ./man/man8}\n  %x{RUBYLIB=./lib:$RUBYLIB bin/puppet doc --reference configuration > ./man/man5/puppetconf.5.ronn}\n  %x{#{ronn} #{ronn_args} ./man/man5/puppetconf.5.ronn}\n  FileUtils.mv(\"./man/man5/puppetconf.5\", \"./man/man5/puppet.conf.5\")\n  FileUtils.rm(\"./man/man5/puppetconf.5.ronn\")\n\n  # Create puppet binary man page\n  # puppet --help outputs raw text, not ronn, so trying to convert that to roff\n  # fails miserably. Render valid ronn so we can convert to roff\n  common = helpface.common_app_summaries\n  specialized = helpface.specialized_app_summaries\n  template_binding = OpenStruct.new(common: common, specialized: specialized).instance_eval {binding}\n  content = ERB.new(File.read(File.join(__dir__, 'man/puppet.erb')), trim_mode: '-').result(template_binding)\n  File.write(\"./man/man8/puppet.8.ronn\", content)\n  %x{#{ronn} #{ronn_args} ./man/man8/puppet.8.ronn}\n  FileUtils.rm(\"./man/man8/puppet.8.ronn\")\n\n  apps.each do |app|\n    %x{RUBYLIB=./lib:$RUBYLIB bin/puppet help #{app} --ronn > ./man/man8/puppet-#{app}.8.ronn}\n    %x{#{ronn} #{ronn_args} ./man/man8/puppet-#{app}.8.ronn}\n    FileUtils.rm(\"./man/man8/puppet-#{app}.8.ronn\")\n  end\n\n  # Delete orphaned manpages if binary was deleted\n  Dir.glob(%w{./man/man8/puppet-*.8}) do |app|\n    appname = app.match(/puppet-(.*)\\.8/)[1]\n    FileUtils.rm(\"./man/man8/puppet-#{appname}.8\") unless apps.include?(appname)\n  end\n\n  # Vile hack: create puppet resource man page\n  # Currently, the useless resource face wins against puppet resource in puppet\n  # man. (And actually, it even gets removed from the list of legacy\n  # applications.) So we overwrite it with the correct man page at the end.\n  %x{RUBYLIB=./lib:$RUBYLIB bin/puppet resource --help > ./man/man8/puppet-resource.8.ronn}\n  %x{#{ronn} #{ronn_args} ./man/man8/puppet-resource.8.ronn}\n  FileUtils.rm(\"./man/man8/puppet-resource.8.ronn\")\n\nend\n"
  },
  {
    "path": "rakelib/memwalk.rake",
    "content": "# Walks the memory dumped into heap.json, and produces a graph of the memory dumped in diff.json\n# If a single argument (a hex address to one object) is given, the graph is limited to this object and what references it\n# The heap dumps should be in the format produced by Ruby ObjectSpace in Ruby version 2.1.0 or later.\n#\n# The command produces a .dot file that can be rendered with graphwiz dot into SVG. If a memwalk is performed for all\n# objects in the diff.json, the output file name is memwalk.dot. If it is produced for a single address, the name of the\n# output file is memwalk-<address>.dot\n#\n# The dot file can be rendered with something like: dot -Tsvg -omemwalk.svg memwalk.dot\n#\ndesc \"Process a diff.json of object ids, and a heap.json of a Ruby 2.1.0 ObjectSpace dump and produce a graph\"\ntask :memwalk, [:id] do |t, args|\n  puts \"Memwalk\"\n  puts \"Computing for #{args[:id] ? args[:id] : 'all'}\"\n  @single_id = args[:id] ? args[:id].to_i(16) : nil\n\n  require 'json'\n  #require 'debug'\n\n  TYPE = \"type\".freeze\n  ROOT = \"root\".freeze\n  ROOT_UC = \"ROOT\".freeze\n  ADDR = \"address\".freeze\n  NODE = \"NODE\".freeze\n  STRING = \"STRING\".freeze\n  DATA = \"DATA\".freeze\n  HASH = \"HASH\".freeze\n  ARRAY = \"ARRAY\".freeze\n  OBJECT = \"OBJECT\".freeze\n  CLASS = \"CLASS\".freeze\n\n  allocations = {}\n  # An array of integer addresses of the objects to trace bindings for\n  diff_index = {}\n  puts \"Reading data\"\n  begin\n    puts \"Reading diff\"\n    lines = 0;\n    File.readlines(\"diff.json\").each do | line |\n      lines += 1\n      diff = JSON.parse(line)\n      case diff[ TYPE ]\n      when STRING, DATA, HASH, ARRAY\n        # skip the strings\n      else\n        diff_index[ diff[ ADDR ].to_i(16) ] = diff\n      end\n    end\n    puts \"Read #{lines} number of diffs\"\n  rescue => e\n    raise \"ERROR READING DIFF at line #{lines} #{e.message[0, 200]}\"\n  end\n\n  begin\n    puts \"Reading heap\"\n    lines = 0\n    allocation = nil\n    File.readlines(\"heap.json\").each do | line |\n      lines += 1\n      allocation = JSON.parse(line)\n      case allocation[ TYPE ]\n      when ROOT_UC\n        # Graph for single id must include roots, as it may be a root that holds on to the reference\n        # a global variable, thread, etc.\n        #\n        if @single_id\n          allocations[ allocation[ ROOT ] ] = allocation\n        end\n      when NODE\n        # skip the NODE objects - they represent the loaded ruby code\n      when STRING\n        # skip all strings - they are everywhere\n      else\n        allocations[ allocation[ ADDR ].to_i(16) ] = allocation\n      end\n    end\n    puts \"Read #{lines} number of entries\"\n  rescue => e\n    require 'debug'\n    puts \"ERROR READING HEAP #{e.message[0, 200]}\"\n    raise e\n  end\n  @heap = allocations\n\n  puts \"Building reference index\"\n  # References is an index from a referenced object to an array with addresses to the objects that references it\n  @references = Hash.new { |h, k| h[k] = [] }\n  REFERENCES = \"references\".freeze\n  allocations.each do |k,v|\n    refs = v[ REFERENCES ]\n    if refs.is_a?(Array)\n      refs.each {|addr| @references[ addr.to_i(16) ] << k }\n    end\n  end\n\n  @printed = Set.new()\n\n  def print_object(addr, entry)\n    # only print each node once\n    return unless @printed.add?(addr)\n    begin\n    if addr.is_a?(String)\n      @output.write( \"x#{node_name(addr)} [label=\\\"#{node_label(addr, entry)}\\\\n#{addr}\\\"];\\n\")\n    else\n      @output.write( \"x#{node_name(addr)} [label=\\\"#{node_label(addr, entry)}\\\\n#{addr.to_s(16)}\\\"];\\n\")\n    end\n    rescue => e\n      require 'debug'\n      raise e\n    end\n  end\n\n  def node_label(addr, entry)\n    if entry[ TYPE ] == OBJECT\n      class_ref = entry[ \"class\" ].to_i(16)\n      @heap[ class_ref ][ \"name\" ]\n    elsif entry[ TYPE ] == CLASS\n      \"CLASS #{entry[ \"name\"]}\"\n    else\n      entry[TYPE]\n    end\n  end\n\n  def node_name(addr)\n    return addr if addr.is_a? String\n    addr.to_s(16)\n  end\n\n  def print_edge(from_addr, to_addr)\n    @output.write(\"x#{node_name(from_addr)}->x#{node_name(to_addr)};\\n\")\n  end\n\n  def closure_and_edges(diff)\n    edges = Set.new()\n    walked = Set.new()\n    puts \"Number of diffs referenced = #{diff.count {|k,_| @references[k].is_a?(Array) && @references[k].size() > 0 }}\"\n    diff.each {|k,_| walk(k, edges, walked) }\n    edges.each {|e| print_edge(*e) }\n  end\n\n  def walk(addr, edges, walked)\n    if !@heap[ addr ].nil?\n      print_object(addr, @heap[addr])\n\n      @references [ addr ].each do |r|\n        walk_to_object(addr, r, edges, walked)\n      end\n    end\n  end\n\n  def walk_to_object(to_addr, cursor, edges, walked)\n    return unless walked\n    # if walked to an object, or everything if a single_id is the target\n    if @heap[ cursor ][ TYPE ] == OBJECT || (@single_id && @heap[ cursor ][ TYPE ] == ROOT_UC || @heap[ cursor ][ TYPE ] == CLASS )\n      # and the edge is unique\n      if edges.add?( [ cursor, to_addr ] )\n        # then we may not have visited objects this objects is being referred from\n        print_object(cursor, @heap[ cursor ])\n        # Do not follow what binds a class\n        if @heap[ cursor ][ TYPE ] != CLASS\n          @references[ cursor ].each do |r|\n            walk_to_object(cursor, r, edges, walked.add?(r))\n            walked.delete(r)\n          end\n        end\n      end\n    else\n      # continue search until Object\n      @references[cursor].each do |r|\n        walk_to_object(to_addr, r, edges, walked.add?(r))\n      end\n    end\n  end\n\n  def single_closure_and_edges(the_target)\n    edges = Set.new()\n    walked = Set.new()\n    walk(the_target, edges, walked)\n    edges.each {|e| print_edge(*e) }\n  end\n\n  puts \"creating graph\"\n  if @single_id\n    @output = File.open(\"memwalk-#{@single_id.to_s(16)}.dot\", \"w\")\n    @output.write(\"digraph root {\\n\")\n    single_closure_and_edges(@single_id)\n  else\n    @output = File.open(\"memwalk.dot\", \"w\")\n    @output.write(\"digraph root {\\n\")\n    closure_and_edges(diff_index)\n  end\n  @output.write(\"}\\n\")\n  @output.close\n  puts \"done\"\nend\n"
  },
  {
    "path": "rakelib/parallel.rake",
    "content": "# encoding: utf-8\n\nrequire 'rubygems'\nrequire 'thread'\nbegin\n  require 'rspec'\n  require 'rspec/core/formatters/helpers'\n  require 'etc'\nrescue LoadError\n  # Don't define the task if we don't have rspec present\nelse\n  module Parallel\n    module RSpec\n      #\n      # Responsible for buffering the output of RSpec's progress formatter.\n      #\n      class ProgressFormatBuffer\n        attr_reader :pending_lines\n        attr_reader :failure_lines\n        attr_reader :examples\n        attr_reader :failures\n        attr_reader :pending\n        attr_reader :failed_example_lines\n        attr_reader :state\n\n        module OutputState\n          HEADER = 1\n          PROGRESS = 2\n          SUMMARY = 3\n          PENDING = 4\n          FAILURES = 5\n          DURATION = 6\n          COUNTS = 7\n          FAILED_EXAMPLES = 8\n        end\n\n        def initialize(io, color)\n          @io = io\n          @color = color\n          @state = OutputState::HEADER\n          @pending_lines = []\n          @failure_lines = []\n          @examples = 0\n          @failures = 0\n          @pending = 0\n          @failed_example_lines = []\n        end\n\n        def color?\n          @color\n        end\n\n        def read\n          # Parse and ignore the one line header\n          if @state == OutputState::HEADER\n            begin\n              @io.readline\n            rescue EOFError\n              return nil\n            end\n            @state = OutputState::PROGRESS\n            return ''\n          end\n\n          # If the progress has been read, parse the summary\n          if @state == OutputState::SUMMARY\n            parse_summary\n            return nil\n          end\n\n          # Read the progress output up to 128 bytes at a time\n          # 128 is a small enough number to show some progress, but not too small that\n          # we're constantly writing synchronized output\n          data = @io.read(128)\n          return nil unless data\n\n          data = @remainder + data if @remainder\n\n          # Check for the end of the progress line\n          if (index = data.index \"\\n\")\n            @state = OutputState::SUMMARY\n            @remainder = data[(index+1)..-1]\n            data = data[0...index]\n          # Check for partial ANSI escape codes in colorized output\n          elsif @color && !data.end_with?(\"\\e[0m\") && (index = data.rindex(\"\\e[\", -6))\n            @remainder = data[index..-1]\n            data = data[0...index]\n          else\n            @remainder = nil\n          end\n\n          data\n        end\n\n        private\n\n        def parse_summary\n          # If there is a remainder, concat it with the next line and handle each line\n          unless @remainder.empty?\n            lines = @remainder\n            eof = false\n            begin\n              lines += @io.readline\n            rescue EOFError\n              eof = true\n            end\n            lines.each_line do |line|\n              parse_summary_line line\n            end\n            return if eof\n          end\n\n          # Process the rest of the lines\n          begin\n            @io.each_line do |line|\n              parse_summary_line line\n            end\n          rescue EOFError\n          end\n        end\n\n        def parse_summary_line(line)\n          line.chomp!\n          return if line.empty?\n\n          if line == 'Pending:'\n            @status = OutputState::PENDING\n            return\n          elsif line == 'Failures:'\n            @status = OutputState::FAILURES\n            return\n          elsif line == 'Failed examples:'\n            @status = OutputState::FAILED_EXAMPLES\n            return\n          elsif (line.match /^Finished in ((\\d+\\.?\\d*) minutes?)? ?(\\d+\\.?\\d*) seconds?$/)\n            @status = OutputState::DURATION\n            return\n          elsif (match = line.gsub(/\\e\\[\\d+m/, '').match /^(\\d+) examples?, (\\d+) failures?(, (\\d+) pending)?$/)\n            @status = OutputState::COUNTS\n            @examples = match[1].to_i\n            @failures = match[2].to_i\n            @pending = (match[4] || 0).to_i\n            return\n          end\n\n          case @status\n            when OutputState::PENDING\n              @pending_lines << line\n            when OutputState::FAILURES\n              @failure_lines << line\n            when OutputState::FAILED_EXAMPLES\n              @failed_example_lines << line\n          end\n        end\n      end\n\n      #\n      # Responsible for parallelizing spec testing.\n      # Optional options list will be passed to rspec.\n      #\n      class Parallelizer\n        # Number of processes to use\n        attr_reader :process_count\n        # Approximate size of each group of tests\n        attr_reader :group_size\n        # Options list for rspec\n        attr_reader :options\n\n        def initialize(process_count, group_size, color, options = [])\n          @process_count = process_count\n          @group_size = group_size\n          @color = color\n          @options = options\n        end\n\n        def color?\n          @color\n        end\n\n        def run\n          @start_time = Time.now\n\n          groups = group_specs\n          fail red('error: no specs were found') if groups.length == 0\n\n          begin\n            run_specs(groups, options)\n          ensure\n            groups.each do |file|\n              File.unlink(file)\n            end\n          end\n        end\n\n        private\n\n        def group_specs\n          # Spawn the rspec_grouper utility to perform the test grouping\n          # We do this in a separate process to limit this processes' long-running footprint\n          io = IO.popen(\"ruby util/rspec_grouper #{@group_size}\")\n\n          header = true\n          spec_group_files = []\n          io.each_line do |line|\n            line.chomp!\n            header = false if line.empty?\n            next if header || line.empty?\n            spec_group_files << line\n          end\n\n          _, status = Process.waitpid2(io.pid)\n          io.close\n\n          fail red('error: no specs were found.') unless status.success?\n          spec_group_files\n        end\n\n        def run_specs(groups, options)\n          puts \"Processing #{groups.length} spec group(s) with #{@process_count} worker(s)\"\n\n          interrupted = false\n          success = true\n          worker_threads = []\n          group_index = -1\n          pids = Array.new(@process_count)\n          mutex = Mutex.new\n\n          # Handle SIGINT by killing child processes\n          original_handler = Signal.trap :SIGINT do\n            break if interrupted\n            interrupted = true\n\n            # Can't synchronize in a trap context, so read dirty\n            pids.each do |pid|\n              begin\n                Process.kill(:SIGKILL, pid) if pid\n              rescue Errno::ESRCH\n              end\n            end\n            puts yellow(\"\\nshutting down...\")\n          end\n\n          buffers = []\n\n          process_count.times do |thread_id|\n            worker_threads << Thread.new do\n              while !interrupted do\n                # Get the spec file for this rspec run\n                group = mutex.synchronize { if group_index < groups.length then groups[group_index += 1] else nil end }\n                break unless group && !interrupted\n\n                # Spawn the worker process with redirected output\n                options_string = options ? options.join(' ') : ''\n                io = IO.popen(\"ruby util/rspec_runner #{group} #{options_string}\")\n                pids[thread_id] = io.pid\n\n                # TODO: make the buffer pluggable to handle other output formats like documentation\n                buffer = ProgressFormatBuffer.new(io, @color)\n\n                # Process the output\n                while !interrupted\n                  output = buffer.read\n                  break unless output && !interrupted\n                  next if output.empty?\n                  mutex.synchronize { print output }\n                end\n\n                # Kill the process if we were interrupted, just to be sure\n                if interrupted\n                  begin\n                    Process.kill(:SIGKILL, pids[thread_id])\n                  rescue Errno::ESRCH\n                  end\n                end\n\n                # Reap the process\n                result = Process.waitpid2(pids[thread_id])[1].success?\n                io.close\n                pids[thread_id] = nil\n                mutex.synchronize do\n                  buffers << buffer\n                  success &= result\n                end\n              end\n            end\n          end\n\n          # Join all worker threads\n          worker_threads.each do |thread|\n            thread.join\n          end\n\n          Signal.trap :SIGINT, original_handler\n          fail yellow('execution was interrupted') if interrupted\n\n          dump_summary buffers\n          success\n        end\n\n        def colorize(text, color_code)\n          if @color\n            \"#{color_code}#{text}\\e[0m\"\n          else\n            text\n          end\n        end\n\n        def red(text)\n          colorize(text, \"\\e[31m\")\n        end\n\n        def green(text)\n          colorize(text, \"\\e[32m\")\n        end\n\n        def yellow(text)\n          colorize(text, \"\\e[33m\")\n        end\n\n        def dump_summary(buffers)\n          puts\n\n          # Print out the pending tests\n          print_header = true\n          buffers.each do |buffer|\n            next if buffer.pending_lines.empty?\n            if print_header\n              puts \"\\nPending:\"\n              print_header = false\n            end\n            puts buffer.pending_lines\n          end\n\n          # Print out the failures\n          print_header = true\n          buffers.each do |buffer|\n            next if buffer.failure_lines.empty?\n            if print_header\n              puts \"\\nFailures:\"\n              print_header = false\n            end\n            puts\n            puts buffer.failure_lines\n          end\n\n          # Print out the run time\n          puts \"\\nFinished in #{::RSpec::Core::Formatters::Helpers.format_duration(Time.now - @start_time)}\"\n\n          # Count all of the examples\n          examples = 0\n          failures = 0\n          pending = 0\n          buffers.each do |buffer|\n            examples += buffer.examples\n            failures += buffer.failures\n            pending += buffer.pending\n          end\n          if failures > 0\n            puts red(summary_count_line(examples, failures, pending))\n          elsif pending > 0\n            puts yellow(summary_count_line(examples, failures, pending))\n          else\n            puts green(summary_count_line(examples, failures, pending))\n          end\n\n          # Print out the failed examples\n          print_header = true\n          buffers.each do |buffer|\n            next if buffer.failed_example_lines.empty?\n            if print_header\n              puts \"\\nFailed examples:\"\n              print_header = false\n            end\n            puts buffer.failed_example_lines\n          end\n        end\n\n        def summary_count_line(examples, failures, pending)\n          summary = ::RSpec::Core::Formatters::Helpers.pluralize(examples, \"example\")\n          summary << \", \" << ::RSpec::Core::Formatters::Helpers.pluralize(failures, \"failure\")\n          summary << \", #{pending} pending\" if pending > 0\n          summary\n        end\n      end\n    end\n  end\n\n  namespace 'parallel' do\n    def color_output?\n      # Check with RSpec to see if color is enabled\n      config = ::RSpec::Core::Configuration.new\n      config.error_stream = $stderr\n      config.output_stream = $stdout\n      options = ::RSpec::Core::ConfigurationOptions.new []\n      options.configure config\n      config.color\n    end\n\n    desc 'Runs specs in parallel. Extra args are passed to rspec.'\n    task 'spec', [:process_count, :group_size] do |_, args|\n      # Default group size in rspec examples\n      DEFAULT_GROUP_SIZE = 1000\n\n      process_count = [(args[:process_count] || Etc.nprocessors).to_i, 1].max\n      group_size = [(args[:group_size] || DEFAULT_GROUP_SIZE).to_i, 1].max\n\n      abort unless Parallel::RSpec::Parallelizer.new(process_count, group_size, color_output?, args.extras).run\n    end\n  end\nend\n"
  },
  {
    "path": "rakelib/parser.rake",
    "content": "desc \"Generate the 4.x 'future' parser\"\ntask :gen_eparser => :require_racc do\n  %x{racc -olib/puppet/pops/parser/eparser.rb lib/puppet/pops/parser/egrammar.ra}\nend\n\ndesc \"Generate the 4.x 'future' parser with egrammar.output\"\ntask :gen_eparser_output => :require_racc do\n  %x{racc -v -olib/puppet/pops/parser/eparser.rb lib/puppet/pops/parser/egrammar.ra}\nend\n\ndesc \"Generate the 4.x 'future' parser with debugging output\"\ntask :gen_eparser_debug => :require_racc do\n  %x{racc -t -olib/puppet/pops/parser/eparser.rb lib/puppet/pops/parser/egrammar.ra}\nend\n\ntask :require_racc do\n  begin\n    require 'racc'\n  rescue LoadError\n    abort(\"Run `bundle install --with development` to install the `racc` gem.\")\n  end\nend\n"
  },
  {
    "path": "rakelib/references/configuration.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: Configuration Reference\ntoc: columns\ncanonical: \"/puppet/latest/configuration.html\"\n---\n\n# Configuration Reference\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n<%= body %>\n"
  },
  {
    "path": "rakelib/references/function.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: Built-in function reference\ncanonical: \"/puppet/latest/function.html\"\ntoc_levels: 2\ntoc: columns\n---\n\n# Built-in function reference\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n\n\nThis page is a list of Puppet's built-in functions, with descriptions of what they do and how to use them.\n\nFunctions are plugins you can call during catalog compilation. A call to any function is an expression that resolves to a value. For more information on how to call functions, see [the language reference page about function calls.](lang_functions.dita) \n\nMany of these function descriptions include auto-detected _signatures,_ which are short reminders of the function's allowed arguments. These signatures aren't identical to the syntax you use to call the function; instead, they resemble a parameter list from a Puppet [class](lang_classes.dita), [defined resource type](lang_defined_types.dita), [function](lang_write_functions_in_puppet.dita), or [lambda](lang_lambdas.dita). The syntax of a signature is:\n\n```\n<FUNCTION NAME>(<DATA TYPE> <ARGUMENT NAME>, ...)\n```\n\nThe `<DATA TYPE>` is a [Puppet data type value](lang_data_type.dita), like `String` or `Optional[Array[String]]`. The `<ARGUMENT NAME>` is a descriptive name chosen by the function's author to indicate what the argument is used for.\n\n* Any arguments with an `Optional` data type can be omitted from the function call.\n* Arguments that start with an asterisk (like `*$values`) can be repeated any number of times.\n* Arguments that start with an ampersand (like `&$block`) aren't normal arguments; they represent a code block, provided with [Puppet's lambda syntax.](lang_lambdas.dita)\n\n## `undef` values in Puppet 6\n\nIn Puppet 6, many Puppet types were moved out of the Puppet codebase, and into modules on the Puppet Forge. The new functions handle `undef` values more strictly than their stdlib counterparts. In Puppet 6, code that relies on `undef` values being implicitly treated as other types will return an evaluation error. For more information on which types were moved into modules, see the [Puppet 6 release notes](https://puppet.com/docs/puppet/6.0/release_notes_puppet.html#select-types-moved-to-modules).\n\n\n<%= body %>\n"
  },
  {
    "path": "rakelib/references/functions_template.erb",
    "content": "<% functions.sort{|a,b| a['name'] <=> b['name'] }.each do |func| -%>\n## `<%= func['name'] %>`\n\n<%= func['docstring']['text'] %>\n\n<% func_signatures = func['signatures']\nmultiple_signatures = func_signatures.count > 1\nif func_signatures\nfunc['signatures'].each_with_index do |signature, index| -%>\n\n<% if multiple_signatures -%>\nSignature <%= index+1 %>\n\n<% end -%>\n`<%= signature['signature'] %>`\n<% has_parameters = signature.dig('docstring', 'tags')&.detect {|tag| tag['tag_name'] == 'param' && tag['text'] != '' && tag['text'] != nil } || false\nif has_parameters -%>\n\n### Parameters\n\n<% signature['docstring']['tags'].select {|tag| tag['tag_name'] == 'param' && tag['text'] != '' && tag['text'] != nil}.each do |param| -%>\n\n* `<%= param['name'] %>` --- <%= param['text'] %>\n<% end # each param\n\nreturn_types = signature['docstring']['tags'].detect {|tag| tag['tag_name'] == 'return'}\nif return_types -%>\n\nReturn type(s): <%= return_types['types'].map {|t| \"`#{t}`\"}.join(', ') %>. <%= return_types['text'] %>\n<% end # if return_types\nhas_examples = signature['docstring']['tags'].detect {|tag| tag['tag_name'] == 'example' && tag['text'] != '' && tag['text'] != nil }\nif has_examples %>\n\n### Examples\n\n<% signature['docstring']['tags'].select {|tag| tag['tag_name'] == 'example' && tag['text'] != '' && tag['text'] != nil}.each do |example| -%>\n<%= example['name'] %>\n\n<%= example['text'] %>\n\n<% end # each example\nend # if has_examples\nend-%>\n<% end # each signature\nend -%>\n\n<% end -%>\n"
  },
  {
    "path": "rakelib/references/get_typedocs.rb",
    "content": "# This script will print the Puppet type docs to stdout in JSON format.\n\n# There are some subtleties that make this a pain to run. Basically: Even if you\n# 'require' a specific copy of the Puppet code, the autoloader will grab bits\n# and pieces of Puppet code from other copies of Puppet scattered about the Ruby\n# load path. This causes a mixture of docs from different versions: although I\n# think the version you require will usually win for things that exist in both\n# versions, providers or attributes that only exist in one version will leak\n# through and you'll get an amalgamation.\n\n# So the only safe thing to do is run this in a completely separate process, and\n# ruthlessly control the Ruby load path. We expect that when you're executing\n# this code, your $RUBYLIB contains the version of Puppet you want to load, and\n# there are no other versions of Puppet available as gems, installed via system\n# packages, etc. etc. etc.\n\nrequire 'json'\nrequire 'puppet'\nrequire 'puppet/util/docs'\nextend Puppet::Util::Docs\n# We use scrub().\n\n\n  # The schema of the typedocs object:\n\n  # { :name_of_type => {\n  #     :description => 'Markdown fragment: description of type',\n  #     :features    => { :feature_name => 'feature description', ... }\n  #       # If there are no features, the value of :features will be an empty hash.\n  #     :providers   => { # If there are no providers, the value of :providers will be an empty hash.\n  #       :name_of_provider => {\n  #         :description => 'Markdown fragment: docs for this provider',\n  #         :features    => [:feature_name, :other_feature, ...]\n  #           # If provider has no features, the value of :features will be an empty array.\n  #       },\n  #       ...etc...\n  #     }\n  #     :attributes  => { # Puppet dictates that there will ALWAYS be at least one attribute.\n  #       :name_of_attribute => {\n  #         :description => 'Markdown fragment: docs for this attribute',\n  #         :kind        => (:property || :parameter),\n  #         :namevar     => (true || false), # always false if :kind => :property\n  #       },\n  #       ...etc...\n  #     },\n  #   },\n  #   ...etc...\n  # }\ntypedocs = {}\n\nPuppet::Type.loadall\n\nPuppet::Type.eachtype { |type|\n  # List of types to ignore:\n  next if type.name == :puppet\n  next if type.name == :component\n  next if type.name == :whit\n\n  # Initialize the documentation object for this type\n  docobject = {\n    :description => scrub(type.doc),\n    :attributes  => {}\n  }\n\n  # Handle features:\n  # inject will return empty hash if type.features is empty.\n  docobject[:features] = type.features.inject( {} ) { |allfeatures, name|\n    allfeatures[name] = scrub( type.provider_feature(name).docs )\n    allfeatures\n  }\n\n  # Handle providers:\n  # inject will return empty hash if type.providers is empty.\n  docobject[:providers] = type.providers.inject( {} ) { |allproviders, name|\n    allproviders[name] = {\n      :description => scrub( type.provider(name).doc ),\n      :features    => type.provider(name).features\n    }\n    allproviders\n  }\n\n  # Override several features missing due to bug #18426:\n  if type.name == :user\n    docobject[:providers][:useradd][:features] << :manages_passwords << :manages_password_age << :libuser\n    if docobject[:providers][:openbsd]\n      docobject[:providers][:openbsd][:features] << :manages_passwords << :manages_loginclass\n    end\n  end\n  if type.name == :group\n    docobject[:providers][:groupadd][:features] << :libuser\n  end\n\n\n  # Handle properties:\n  docobject[:attributes].merge!(\n    type.validproperties.inject( {} ) { |allproperties, name|\n      property = type.propertybyname(name)\n      raise \"Could not retrieve property #{propertyname} on type #{type.name}\" unless property\n      description = property.doc\n      $stderr.puts \"No docs for property #{name} of #{type.name}\" unless description and !description.empty?\n\n      allproperties[name] = {\n        :description => scrub(description),\n        :kind        => :property,\n        :namevar     => false # Properties can't be namevars.\n      }\n      allproperties\n    }\n  )\n\n  # Handle parameters:\n  docobject[:attributes].merge!(\n    type.parameters.inject( {} ) { |allparameters, name|\n      description = type.paramdoc(name)\n      $stderr.puts \"No docs for parameter #{name} of #{type.name}\" unless description and !description.empty?\n\n      # Strip off the too-huge provider list. The question of what to do about\n      # providers is a decision for the formatter, not the fragment collector.\n      description = description.split('Available providers are')[0] if name == :provider\n\n      allparameters[name] = {\n        :description => scrub(description),\n        :kind        => :parameter,\n        :namevar     => type.key_attributes.include?(name) # returns a boolean\n      }\n      allparameters\n    }\n  )\n\n  # Finally:\n  typedocs[type.name] = docobject\n}\n\nprint JSON.dump(typedocs)\n"
  },
  {
    "path": "rakelib/references/man/overview.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: <%= title %>\ncanonical: \"/puppet/latest/man/overview.html\"\n---\n\n# <%= title %>\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n\n\nPuppet's command line tools consist of a single `puppet` binary with many subcommands. The following subcommands are available in this version of Puppet:\n\nCore Tools\n-----\n\nThese subcommands form the core of Puppet's tool set, and every user should understand what they do.\n\n<% core_apps.each do |app| -%>\n- [puppet <%= app %>](<%= app %>.md)\n<% end -%>\n\n\n> Note: The `puppet cert` command is available only in Puppet versions prior to 6.0. For 6.0 and later, use the [`puppetserver cert`command](https://puppet.com/docs/puppet/6/puppet_server_ca_cli.html).\n\nSecondary subcommands\n-----\n\nMany or most users need to use these subcommands at some point, but they aren't needed for daily use the way the core tools are.\n\n<% occasional_apps.each do |app| -%>\n- [puppet <%= app %>](<%= app %>.md)\n<% end -%>\n\n\nNiche subcommands\n-----\n\nMost users can ignore these subcommands. They're only useful for certain niche workflows, and most of them are interfaces to Puppet's internal subsystems.\n\n<% weird_apps.each do |app| -%>\n- [puppet <%= app %>](<%= app %>.md)\n<% end -%>\n\n\n"
  },
  {
    "path": "rakelib/references/man.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: '<%= title %>'\ncanonical: \"<%= canonical %>\"\n---\n\n# <%= title %>\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n<%= body %>\n"
  },
  {
    "path": "rakelib/references/metaparameter.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: Metaparameter Reference\ntoc: columns\ncanonical: \"/puppet/latest/metaparameter.html\"\n---\n\n# Metaparameter Reference\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n<%= body %>\n"
  },
  {
    "path": "rakelib/references/report.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: Report Reference\ntoc: columns\ncanonical: \"/puppet/latest/report.html\"\n---\n\n# Report Reference\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n<%= body %>\n"
  },
  {
    "path": "rakelib/references/types/overview.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: <%= title %>\ncanonical: \"/puppet/latest/types/overview.md\"\n---\n\n# <%= title %>\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n## List of resource types\n\n\n<% types.each do |type| -%>\n* [<%= type %>](./<%= type %>.md)\n<% end -%>\n\n\n## About resource types\n\n### Built-in types and custom types\n\nThis is the documentation for Puppet's built-in resource types and providers. Additional resource types are distributed in Puppet modules.\n\nYou can find and install modules by browsing the\n[Puppet Forge](http://forge.puppet.com). See each module's documentation for\ninformation on how to use its custom resource types. For more information about creating custom types, see [Custom resources](/docs/puppet/latest/custom_resources.html).\n\n> As of Puppet 6.0, some resource types were removed from Puppet and repackaged as individual modules. These supported type modules are still included in the `puppet-agent` package, so you don't have to download them from the Forge. See the complete list of affected types in the [supported type modules](#supported-type-modules-in-puppet-agent) section.\n\n### Declaring resources\n\nTo manage resources on a target system, declare them in Puppet\nmanifests. For more details, see\n[the resources page of the Puppet language reference.](/docs/puppet/latest/lang_resources.html)\n\nYou can also browse and manage resources interactively using the\n`puppet resource` subcommand; run `puppet resource --help` for more information.\n\n### Namevars and titles\n\nAll types have a special attribute called the _namevar_. This is the attribute\nused to uniquely identify a resource on the target system.\n\nEach resource has a specific namevar attribute, which is listed on this page in\neach resource's reference. If you don't specify a value for the namevar, its\nvalue defaults to the resource's _title_.\n\n**Example of a title as a default namevar:**\n\n```puppet\nfile { '/etc/passwd':\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nIn this code, `/etc/passwd` is the _title_ of the file resource.\n\nThe file type's namevar is `path`. Because we didn't provide a `path` value in\nthis example, the value defaults to the title, `/etc/passwd`.\n\n**Example of a namevar:**\n\n```puppet\nfile { 'passwords':\n  path  => '/etc/passwd',\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nThis example is functionally similar to the previous example. Its `path`\nnamevar attribute has an explicitly set value separate from the title, so\nits name is still `/etc/passwd`.\n\nOther Puppet code can refer to this resource as `File['/etc/passwd']` to\ndeclare relationships.\n\n### Attributes, parameters, properties\n\nThe _attributes_ (sometimes called _parameters_) of a resource determine its\ndesired state. They either directly modify the system (internally, these are\ncalled \"properties\") or they affect how the resource behaves (for instance,\nadding a search path for `exec` resources or controlling directory recursion\non `file` resources).\n\n### Providers\n\n_Providers_ implement the same resource type on different kinds of systems.\nThey usually do this by calling out to external commands.\n\nAlthough Puppet automatically selects an appropriate default provider, you\ncan override the default with the `provider` attribute. (For example, `package`\nresources on Red Hat systems default to the `yum` provider, but you can specify\n`provider => gem` to install Ruby libraries with the `gem` command.)\n\nProviders often specify binaries that they require. Fully qualified binary\npaths indicate that the binary must exist at that specific path, and\nunqualified paths indicate that Puppet searches for the binary using the\nshell path.\n\n### Features\n\n_Features_ are abilities that some providers might not support. Generally, a\nfeature corresponds to some allowed values for a resource attribute.\n\nThis is often the case with the `ensure` attribute. In most types, Puppet\ndoesn't create new resources when omitting `ensure` but still modifies existing\nresources to match specifications in the manifest. However, in some types this\nisn't always the case, or additional values provide more granular control. For\nexample, if a `package` provider supports the `purgeable` feature, you can\nspecify `ensure => purged` to delete configuration files installed by the\npackage.\n\nResource types define the set of features they can use, and providers can\ndeclare which features they provide.\n\n## Puppet 6.0 type changes\n\nIn Puppet 6.0, we removed some of Puppet's built-in types and moved them into individual modules.\n\n### Supported type modules in `puppet-agent`\n\nThe following types are included in supported modules on the Forge. However, they are also included in the `puppet-agent` package, so you do not have to install them separately. See each module's README for detailed information about that type.\n\n- [`augeas`](https://forge.puppet.com/puppetlabs/augeas_core)\n- [`cron`](https://forge.puppet.com/puppetlabs/cron_core)\n- [`host`](https://forge.puppet.com/puppetlabs/host_core)\n- [`mount`](https://forge.puppet.com/puppetlabs/mount_core)\n- [`scheduled_task`](https://forge.puppet.com/puppetlabs/scheduled_task)\n- [`selboolean`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`selmodule`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`ssh_authorized_key`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`sshkey`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`yumrepo`](https://forge.puppet.com/puppetlabs/yumrepo_core)\n- [`zfs`](https://forge.puppet.com/puppetlabs/zfs_core)\n- [`zone`](https://forge.puppet.com/puppetlabs/zone_core)\n- [`zpool`](https://forge.puppet.com/puppetlabs/zfs_core)\n\n### Type modules available on the Forge\n\nThe following types are contained in modules that are maintained, but are not repackaged into Puppet agent. If you need to use them, you must install the modules separately. \n\n- [`k5login`](https://forge.puppet.com/puppetlabs/k5login_core)\n- [`mailalias`](https://forge.puppet.com/puppetlabs/mailalias_core)\n- [`maillist`](https://forge.puppet.com/puppetlabs/maillist_core)\n\n### Deprecated types\n\nThe following types were deprecated with Puppet 6.0.0. They are available in modules, but are not updated. If you need to use them, you must install the modules separately.\n\n- [`computer`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`interface`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`macauthorization`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`mcx`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [The Nagios types](https://forge.puppet.com/puppetlabs/nagios_core)\n- [`router`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`vlan`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n\n## Puppet core types\n\nFor a list of core Puppet types, see the [core types cheat sheet][core-types-cheatsheet].\n"
  },
  {
    "path": "rakelib/references/types/single_type.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: '<%= title %>'\ncanonical: \"/puppet/latest/types/<%= type %>.html\"\n---\n\n# <%= title %>\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n\n\n<%= body %>\n\n"
  },
  {
    "path": "rakelib/references/types/type.erb",
    "content": "## <%= name %>\n\n* [Attributes](#<%= name %>-attributes)\n<% if !this_type['providers'].empty? -%>\n* [Providers](#<%= name %>-providers)\n<% end -%>\n<% if !this_type['features'].empty? -%>\n* [Provider Features](#<%= name %>-provider-features)\n<% end -%>\n\n### Description {#<%= name %>-description}\n\n<%= this_type['description'] %>\n\n### Attributes {#<%= name %>-attributes}\n\n<pre><code><%= name %> { 'resource title':\n<% sorted_attribute_list.each do |attribute_name| -%>\n  <a href=\"#<%= name %>-attribute-<%= attribute_name %>\"><%= attribute_name %></a><%= ' ' * (longest_attribute_name - attribute_name.length) %> =&gt; <em># <% if this_type['attributes'][attribute_name]['namevar'] %><strong>(namevar)</strong> <% end %><%= this_type['attributes'][attribute_name]['description'][0,49].gsub(\"\\n\", ' ').gsub('<', '&lt;').sub(/\\W? \\S+$/, '...') %></em>\n<% end -%>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n<% sorted_attribute_list.each do |attribute_name| -%>\n\n#### <%= attribute_name %> {#<%= name %>-attribute-<%= attribute_name %>}\n\n<% if this_type['attributes'][attribute_name]['namevar'] -%>\n<% if attribute_name != 'provider' %>_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_<%= \"\\n\\n\" -%>\n<% elsif attribute_name == 'provider' %>_(**Secondary namevar:** This resource type allows you to manage multiple resources with the same name as long as their providers are different.)_<%= \"\\n\\n\" -%>\n<% end -%>\n<% end -%>\n<% if this_type['attributes'][attribute_name]['kind'] == 'property' %>_(**Property:** This attribute represents concrete state on the target system.)_<%= \"\\n\\n\" %><% end -%>\n<%= this_type['attributes'][attribute_name]['description'] %>\n<% if this_type['attributes'][attribute_name]['required_features'] -%>\n\nRequires features <%= this_type['attributes'][attribute_name]['required_features'] %>.\n<% end -%>\n\n<% if attribute_name == 'provider' and !this_type['providers'].empty? -%>Available providers are:<%= \"\\n\\n\" %><%= this_type['providers'].keys.sort.collect {|prov| \"* [`#{prov}`](##{name}-provider-#{prov})\"}.sort.join(\"\\n\") %><%= \"\\n\\n\" %><% end -%>\n([↑ Back to <%= name %> attributes](#<%= name %>-attributes))\n\n<% end # of attribute details\n-%>\n\n<% if !this_type['providers'].empty? -%>\n### Providers {#<%= name %>-providers}\n\n<% end -%>\n<% this_type['providers'].keys.sort.each do |provider_name| -%>\n#### <%= provider_name %> {#<%= name %>-provider-<%= provider_name %>}\n\n<%= this_type['providers'][provider_name]['description'] %>\n\n<% end -%>\n<% if !this_type['features'].empty? -%>\n### Provider Features {#<%= name %>-provider-features}\n\nAvailable features:\n\n<% sorted_feature_list.each do |feature| -%>\n* `<%= feature %>` --- <%= this_type['features'][feature].gsub(\"\\n\", ' ') %>\n<% end -%>\n\n<% if !this_type['providers'].empty? -%>\nProvider support:\n\n<%- this_type['providers'].keys.sort.each do |provider_name| -%>\n* **<%= provider_name %>** - <% if !this_type['providers'][provider_name]['features'].empty? -%>\n_<%=this_type['providers'][provider_name]['features'].join(', ').gsub('_', ' ') %>_\n<% else %>No supported Provider features\n<% end %><%- end -%>  \n\n<% end # provider support table\n-%>\n<% end # features section\n-%>\n\n"
  },
  {
    "path": "rakelib/references/unified_type.erb",
    "content": "---\nlayout: default\nbuilt_from_commit: <%= sha %>\ntitle: <%= title %>\ncanonical: \"/puppet/latest/type.html\"\ntoc_levels: 2\ntoc: columns\n---\n\n# <%= title %>\n\n> **NOTE:** This page was generated from the Puppet source code on <%= now %>\n\n\n\n## About resource types\n\n### Built-in types and custom types\n\nThis is the documentation for Puppet's built-in resource types and providers. Additional resource types are distributed in Puppet modules.\n\nYou can find and install modules by browsing the\n[Puppet Forge](http://forge.puppet.com). See each module's documentation for\ninformation on how to use its custom resource types. For more information about creating custom types, see [Custom resources](/docs/puppet/latest/custom_resources.html).\n\n> As of Puppet 6.0, some resource types were removed from Puppet and repackaged as individual modules. These supported type modules are still included in the `puppet-agent` package, so you don't have to download them from the Forge. See the complete list of affected types in the [supported type modules](#supported-type-modules-in-puppet-agent) section.\n\n### Declaring resources\n\nTo manage resources on a target system, declare them in Puppet\nmanifests. For more details, see\n[the resources page of the Puppet language reference.](/docs/puppet/latest/lang_resources.html)\n\nYou can also browse and manage resources interactively using the\n`puppet resource` subcommand; run `puppet resource --help` for more information.\n\n### Namevars and titles\n\nAll types have a special attribute called the _namevar_. This is the attribute\nused to uniquely identify a resource on the target system.\n\nEach resource has a specific namevar attribute, which is listed on this page in\neach resource's reference. If you don't specify a value for the namevar, its\nvalue defaults to the resource's _title_.\n\n**Example of a title as a default namevar:**\n\n```puppet\nfile { '/etc/passwd':\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nIn this code, `/etc/passwd` is the _title_ of the file resource.\n\nThe file type's namevar is `path`. Because we didn't provide a `path` value in\nthis example, the value defaults to the title, `/etc/passwd`.\n\n**Example of a namevar:**\n\n```puppet\nfile { 'passwords':\n  path  => '/etc/passwd',\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nThis example is functionally similar to the previous example. Its `path`\nnamevar attribute has an explicitly set value separate from the title, so\nits name is still `/etc/passwd`.\n\nOther Puppet code can refer to this resource as `File['/etc/passwd']` to\ndeclare relationships.\n\n### Attributes, parameters, properties\n\nThe _attributes_ (sometimes called _parameters_) of a resource determine its\ndesired state. They either directly modify the system (internally, these are\ncalled \"properties\") or they affect how the resource behaves (for instance,\nadding a search path for `exec` resources or controlling directory recursion\non `file` resources).\n\n### Providers\n\n_Providers_ implement the same resource type on different kinds of systems.\nThey usually do this by calling out to external commands.\n\nAlthough Puppet automatically selects an appropriate default provider, you\ncan override the default with the `provider` attribute. (For example, `package`\nresources on Red Hat systems default to the `yum` provider, but you can specify\n`provider => gem` to install Ruby libraries with the `gem` command.)\n\nProviders often specify binaries that they require. Fully qualified binary\npaths indicate that the binary must exist at that specific path, and\nunqualified paths indicate that Puppet searches for the binary using the\nshell path.\n\n### Features\n\n_Features_ are abilities that some providers might not support. Generally, a\nfeature corresponds to some allowed values for a resource attribute.\n\nThis is often the case with the `ensure` attribute. In most types, Puppet\ndoesn't create new resources when omitting `ensure` but still modifies existing\nresources to match specifications in the manifest. However, in some types this\nisn't always the case, or additional values provide more granular control. For\nexample, if a `package` provider supports the `purgeable` feature, you can\nspecify `ensure => purged` to delete configuration files installed by the\npackage.\n\nResource types define the set of features they can use, and providers can\ndeclare which features they provide.\n\n## Puppet 6.0 type changes\n\nIn Puppet 6.0, we removed some of Puppet's built-in types and moved them into individual modules.\n\n### Supported type modules in `puppet-agent`\n\nThe following types are included in supported modules on the Forge. However, they are also included in the `puppet-agent` package, so you do not have to install them separately. See each module's README for detailed information about that type.\n\n- [`augeas`](https://forge.puppet.com/puppetlabs/augeas_core)\n- [`cron`](https://forge.puppet.com/puppetlabs/cron_core)\n- [`host`](https://forge.puppet.com/puppetlabs/host_core)\n- [`mount`](https://forge.puppet.com/puppetlabs/mount_core)\n- [`scheduled_task`](https://forge.puppet.com/puppetlabs/scheduled_task)\n- [`selboolean`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`selmodule`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`ssh_authorized_key`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`sshkey`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`yumrepo`](https://forge.puppet.com/puppetlabs/yumrepo_core)\n- [`zfs`](https://forge.puppet.com/puppetlabs/zfs_core)\n- [`zone`](https://forge.puppet.com/puppetlabs/zone_core)\n- [`zpool`](https://forge.puppet.com/puppetlabs/zfs_core)\n\n### Type modules available on the Forge\n\nThe following types are contained in modules that are maintained, but are not repackaged into Puppet agent. If you need to use them, you must install the modules separately. \n\n- [`k5login`](https://forge.puppet.com/puppetlabs/k5login_core)\n- [`mailalias`](https://forge.puppet.com/puppetlabs/mailalias_core)\n- [`maillist`](https://forge.puppet.com/puppetlabs/maillist_core)\n\n### Deprecated types\n\nThe following types were deprecated with Puppet 6.0.0. They are available in modules, but are not updated. If you need to use them, you must install the modules separately.\n\n- [`computer`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`interface`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`macauthorization`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`mcx`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [The Nagios types](https://forge.puppet.com/puppetlabs/nagios_core)\n- [`router`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`vlan`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n\n## Puppet core types\n\nFor a list of core Puppet types, see the [core types cheat sheet][core-types-cheatsheet].\n<% types.each_with_index do |type, index| -%>\n<% if index != 0 -%>\n---------\n<% end -%>\n\n<%= type %>\n\n<% end -%>\n"
  },
  {
    "path": "rakelib/yard.rake",
    "content": "begin\n  require 'yard'\n\n  namespace :doc do\n    desc \"Clean up generated documentation\"\n    task :clean do\n      rm_rf \"doc\"\n    end\n\n    desc \"Generate public documentation pages for the API\"\n    YARD::Rake::YardocTask.new(:api) do |t|\n      t.files = ['lib/**/*.rb']\n      t.options = %w{\n        --protected\n        --private\n        --verbose\n        --markup markdown\n        --readme README.md\n        --tag status\n        --transitive-tag status\n        --tag comment\n        --hide-tag comment\n        --tag dsl:\"DSL\"\n        --no-transitive-tag api\n        --template-path yardoc/templates\n        --files README_DEVELOPER.md,CO*.md,api/**/*.md\n        --api public\n        --api private\n        --hide-void-return\n      }\n    end\n\n    desc \"Generate documentation pages for all of the code\"\n    YARD::Rake::YardocTask.new(:all) do |t|\n      t.files = ['lib/**/*.rb']\n      t.options = %w{\n        --verbose\n        --markup markdown\n        --readme README.md\n        --tag status\n        --transitive-tag status\n        --tag comment\n        --hide-tag comment\n        --tag dsl:\"DSL\"\n        --no-transitive-tag api\n        --template-path yardoc/templates\n        --files README_DEVELOPER.md,CO*.md,api/**/*.md\n        --api public\n        --api private\n        --no-api\n        --hide-void-return\n      }\n    end\n  end\nrescue LoadError => e\n  if verbose\n    STDERR.puts \"Document generation not available without yard. #{e.message}\"\n  end\nend\n"
  },
  {
    "path": "references/configuration.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Configuration Reference\ntoc: columns\ncanonical: \"/puppet/latest/configuration.html\"\n---\n\n# Configuration Reference\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:21 +0000\n\n\n\n## Configuration settings\n\n* Each of these settings can be specified in `puppet.conf` or on the\n  command line.\n* Puppet Enterprise (PE) and open source Puppet share the configuration settings\n  documented here. However, PE defaults differ from open source defaults for some\n  settings, such as `node_terminus`, `storeconfigs`, `always_retry_plugins`,\n  `disable18n`, `environment_timeout` (when Code Manager is enabled), and the\n  Puppet Server JRuby `max-active-instances` setting. To verify PE configuration\n  defaults, check the `puppet.conf` or `pe-puppet-server.conf` file after\n  installation.\n* When using boolean settings on the command line, use `--setting` and\n  `--no-setting` instead of `--setting (true|false)`. (Using `--setting false`\n  results in \"Error: Could not parse application options: needless argument\".)\n* Settings can be interpolated as `$variables` in other settings; `$environment`\n  is special, in that puppet master will interpolate each agent node's\n  environment instead of its own.\n* Multiple values should be specified as comma-separated lists; multiple\n  directories should be separated with the system path separator (usually\n  a colon).\n* Settings that represent time intervals should be specified in duration format:\n  an integer immediately followed by one of the units 'y' (years of 365 days),\n  'd' (days), 'h' (hours), 'm' (minutes), or 's' (seconds). The unit cannot be\n  combined with other units, and defaults to seconds when omitted. Examples are\n  '3600' which is equivalent to '1h' (one hour), and '1825d' which is equivalent\n  to '5y' (5 years).\n* If you use the `splay` setting, note that the period that it waits changes\n  each time the Puppet agent is restarted.\n* Settings that take a single file or directory can optionally set the owner,\n  group, and mode for their value: `rundir = $vardir/run { owner = puppet,\n  group = puppet, mode = 644 }`\n* The Puppet executables ignores any setting that isn't relevant to\n  their function.\n\nSee the [configuration guide][confguide] for more details.\n\n[confguide]: https://puppet.com/docs/puppet/latest/config_about_settings.html\n\n\n### agent_catalog_run_lockfile\n\nA lock file to indicate that a puppet agent catalog run is currently in progress.\nThe file contains the pid of the process that holds the lock on the catalog run.\n\n- *Default*: `$statedir/agent_catalog_run.lock`\n\n### agent_disabled_lockfile\n\nA lock file to indicate that puppet agent runs have been administratively\ndisabled.  File contains a JSON object with state information.\n\n- *Default*: `$statedir/agent_disabled.lock`\n\n### allow_duplicate_certs\n\nWhether to allow a new certificate request to overwrite an existing\ncertificate request. If true, then the old certificate must be cleaned using\n`puppetserver ca clean`, and the new request signed using `puppetserver ca sign`.\n\n- *Default*: `false`\n\n### allow_pson_serialization\n\nWhether to allow PSON serialization. When unable to serialize to\nJSON or other formats, Puppet falls back to PSON. This option affects the\nconfiguration management service responses of Puppet Server and the process by\nwhich the agent saves its cached catalog. With a default value of `false`, this\noption is useful in preventing the loss of data because rich data cannot be\nserialized via PSON.\n\n- *Default*: `false`\n\n### always_retry_plugins\n\nAffects how we cache attempts to load Puppet resource types and features.  If\ntrue, then calls to `Puppet.type.<type>?` `Puppet.feature.<feature>?`\nwill always attempt to load the type or feature (which can be an\nexpensive operation) unless it has already been loaded successfully.\nThis makes it possible for a single agent run to, e.g., install a\npackage that provides the underlying capabilities for a type or feature,\nand then later load that type or feature during the same run (even if\nthe type or feature had been tested earlier and had not been available).\n\nIf this setting is set to false, then types and features will only be\nchecked once, and if they are not available, the negative result is\ncached and returned for all subsequent attempts to load the type or\nfeature.  This behavior is almost always appropriate for the server,\nand can result in a significant performance improvement for types and\nfeatures that are checked frequently.\n\n- *Default*: `true`\n\n### autoflush\n\nWhether log files should always flush to disk.\n\n- *Default*: `true`\n\n### autosign\n\nWhether (and how) to autosign certificate requests. This setting\nis only relevant on a Puppet Server acting as a certificate authority (CA).\n\nValid values are true (autosigns all certificate requests; not recommended),\nfalse (disables autosigning certificates), or the absolute path to a file.\n\nThe file specified in this setting may be either a **configuration file**\nor a **custom policy executable.** Puppet will automatically determine\nwhat it is: If the Puppet user (see the `user` setting) can execute the\nfile, it will be treated as a policy executable; otherwise, it will be\ntreated as a config file.\n\nIf a custom policy executable is configured, the CA Puppet Server will run it\nevery time it receives a CSR. The executable will be passed the subject CN of the\nrequest _as a command line argument,_ and the contents of the CSR in PEM format\n_on stdin._ It should exit with a status of 0 if the cert should be autosigned\nand non-zero if the cert should not be autosigned.\n\nIf a certificate request is not autosigned, it will persist for review. An admin\nuser can use the `puppetserver ca sign` command to manually sign it, or can delete\nthe request.\n\nFor info on autosign configuration files, see\n[the guide to Puppet's config files](https://puppet.com/docs/puppet/latest/config_file_autosign.html).\n\n- *Default*: `$confdir/autosign.conf`\n\n### basemodulepath\n\nThe search path for **global** modules. Should be specified as a\nlist of directories separated by the system path separator character. (The\nPOSIX path separator is ':', and the Windows path separator is ';'.)\n\nThese are the modules that will be used by _all_ environments. Note that\nthe `modules` directory of the active environment will have priority over\nany global directories. For more info, see\n<https://puppet.com/docs/puppet/latest/environments_about.html>\n\n- *Default*: `$codedir/modules:/opt/puppetlabs/puppet/modules`\n\n### binder_config\n\nThe binder configuration file. Puppet reads this file on each request to configure the bindings system.\nIf set to nil (the default), a $confdir/binder_config.yaml is optionally loaded. If it does not exists, a default configuration\nis used. If the setting :binding_config is specified, it must reference a valid and existing yaml file.\n\n- *Default*: ``\n\n### bucketdir\n\nWhere FileBucket files are stored.\n\n- *Default*: `$vardir/bucket`\n\n### ca_fingerprint\n\nThe expected fingerprint of the CA certificate. If specified, the agent\nwill compare the CA certificate fingerprint that it downloads against this value\nand reject the CA certificate if the values do not match. This only applies\nduring the first download of the CA certificate.\n\n- *Default*: ``\n\n### ca_name\n\nThe name to use the Certificate Authority certificate.\n\n- *Default*: `Puppet CA: $certname`\n\n### ca_port\n\nThe port to use for the certificate authority.\n\n- *Default*: `$serverport`\n\n### ca_refresh_interval\n\nHow often the Puppet agent refreshes its local CA\ncertificates. By default, CA certificates are refreshed every 24 hours. If a\ndifferent interval is specified, the agent refreshes its CA certificates during\nthe next agent run if the elapsed time since the certificates were last\nrefreshed exceeds the specified duration.\n\nIn general, the interval should be greater than the `runinterval`\nvalue. Setting the `ca_refresh_interval` value to 0 or an equal or\nlesser value than `runinterval` causes the CA certificates to be\nrefreshed on every run.\n\nIf the agent downloads new CA certs, the agent uses those for subsequent\nnetwork requests. If the refresh request fails or if the CA certs are\nunchanged on the server, then the agent run will continue using the\nlocal CA certs it already has. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `1d`\n\n### ca_server\n\nThe server to use for certificate\nauthority requests.  It's a separate server because it cannot\nand does not need to horizontally scale.\n\n- *Default*: `$server`\n\n### ca_ttl\n\nThe default TTL for new certificates.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `5y`\n\n### cacert\n\nThe CA certificate.\n\n- *Default*: `$cadir/ca_crt.pem`\n\n### cacrl\n\nThe certificate revocation list (CRL) for the CA.\n\n- *Default*: `$cadir/ca_crl.pem`\n\n### cadir\n\nThe root directory for the certificate authority.\n\n- *Default*: `/etc/puppetlabs/puppetserver/ca`\n\n### cakey\n\nThe CA private key.\n\n- *Default*: `$cadir/ca_key.pem`\n\n### capub\n\nThe CA public key.\n\n- *Default*: `$cadir/ca_pub.pem`\n\n### catalog_cache_terminus\n\nHow to store cached catalogs. Valid values are 'json', 'msgpack' and 'yaml'. The agent application defaults to 'json'.\n\n- *Default*: ``\n\n### catalog_terminus\n\nWhere to get node catalogs.  This is useful to change if, for instance,\nyou'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store.\n\n- *Default*: `compiler`\n\n### cert_inventory\n\nThe inventory file. This is a text file to which the CA writes a\ncomplete listing of all certificates.\n\n- *Default*: `$cadir/inventory.txt`\n\n### certdir\n\nThe certificate directory.\n\n- *Default*: `$ssldir/certs`\n\n### certificate_revocation\n\nWhether certificate revocation checking should be enabled, and what level of\nchecking should be performed.\n\nWhen certificate revocation is enabled, Puppet expects the contents of its CRL\nto be one or more PEM-encoded CRLs concatenated together. When using a cert\nbundle, CRLs for all CAs in the chain of trust must be included in the crl file.\nThe chain should be ordered from least to most authoritative, with the first CRL\nlisted being for the root of the chain and the last being for the leaf CA.\n\nWhen certificate_revocation is set to 'true' or 'chain', Puppet ensures\nthat each CA in the chain of trust has not been revoked by its issuing CA.\n\nWhen certificate_revocation is set to 'leaf', Puppet verifies certs against\nthe issuing CA's revocation list, but it does not verify the revocation status\nof the issuing CA or any CA above it within the chain of trust.\n\nWhen certificate_revocation is set to 'false', Puppet disables all\ncertificate revocation checking and does not attempt to download the CRL.\n\n- *Default*: `chain`\n\n### certname\n\nThe name to use when handling certificates. When a node\nrequests a certificate from the CA Puppet Server, it uses the value of the\n`certname` setting as its requested Subject CN.\n\nThis is the name used when managing a node's permissions in\nPuppet Server's [auth.conf](https://puppet.com/docs/puppetserver/latest/config_file_auth.html).\nIn most cases, it is also used as the node's name when matching\n[node definitions](https://puppet.com/docs/puppet/latest/lang_node_definitions.html)\nand requesting data from an ENC. (This can be changed with the `node_name_value`\nand `node_name_fact` settings, although you should only do so if you have\na compelling reason.)\n\nA node's certname is available in Puppet manifests as `$trusted['certname']`. (See\n[Facts and Built-In Variables](https://puppet.com/docs/puppet/latest/lang_facts_and_builtin_vars.html)\nfor more details.)\n\n* For best compatibility, you should limit the value of `certname` to\n  only use lowercase letters, numbers, periods, underscores, and dashes. (That is,\n  it should match `/A[a-z0-9._-]+Z/`.)\n* The special value `ca` is reserved, and can't be used as the certname\n  for a normal node.\n\n  **Note:** You must set the certname in the main section of the puppet.conf file. Setting it in a different section causes errors.\n\nDefaults to the node's fully qualified domain name.\n\n- *Default*: `the Host's fully qualified domain name, as determined by Facter`\n\n### ciphers\n\nThe list of ciphersuites for TLS connections initiated by puppet. The\ndefault value is chosen to support TLS 1.0 and up, but can be made\nmore restrictive if needed. The ciphersuites must be specified in OpenSSL\nformat, not IANA.\n\n- *Default*: `ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256`\n\n### classfile\n\nThe file in which puppet agent stores a list of the classes\nassociated with the retrieved configuration.  Can be loaded in\nthe separate `puppet` executable using the `--loadclasses`\noption.\n\n- *Default*: `$statedir/classes.txt`\n\n### client_datadir\n\nThe directory in which serialized data is stored on the client.\n\n- *Default*: `$vardir/client_data`\n\n### clientbucketdir\n\nWhere FileBucket files are stored locally.\n\n- *Default*: `$vardir/clientbucket`\n\n### clientyamldir\n\nThe directory in which client-side YAML data is stored.\n\n- *Default*: `$vardir/client_yaml`\n\n### code\n\nCode to parse directly.  This is essentially only used\nby `puppet`, and should only be set if you're writing your own Puppet\nexecutable.\n\n\n### codedir\n\nThe main Puppet code directory.  The default for this setting\nis calculated based on the user.  If the process is running as root or\nthe user that Puppet is supposed to run as, it defaults to a system\ndirectory, but if it's running as any other user, it defaults to being\nin the user's home directory.\n\n- *Default*: `Unix/Linux: /etc/puppetlabs/code -- Windows: C:\\ProgramData\\PuppetLabs\\code -- Non-root user: ~/.puppetlabs/etc/code`\n\n### color\n\nWhether to use colors when logging to the console.  Valid values are\n`ansi` (equivalent to `true`), `html`, and `false`, which produces no color.\n\n- *Default*: `ansi`\n\n### confdir\n\nThe main Puppet configuration directory.  The default for this setting\nis calculated based on the user.  If the process is running as root or\nthe user that Puppet is supposed to run as, it defaults to a system\ndirectory, but if it's running as any other user, it defaults to being\nin the user's home directory.\n\n- *Default*: `Unix/Linux: /etc/puppetlabs/puppet -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\etc -- Non-root user: ~/.puppetlabs/etc/puppet`\n\n### config\n\nThe configuration file for the current puppet application.\n\n- *Default*: `$confdir/${config_file_name}`\n\n### config_file_name\n\nThe name of the puppet config file.\n\n- *Default*: `puppet.conf`\n\n### config_version\n\nHow to determine the configuration version.  By default, it will be the\ntime that the configuration is parsed, but you can provide a shell script to override how the\nversion is determined.  The output of this script will be added to every log message in the\nreports, allowing you to correlate changes on your hosts to the source version on the server.\n\nSetting a global value for config_version in puppet.conf is not allowed\n(but it can be overridden from the commandline). Please set a\nper-environment value in environment.conf instead. For more info, see\n<https://puppet.com/docs/puppet/latest/environments_about.html>\n\n\n### configprint\n\nPrints the value of a specific configuration setting.  If the name of a\nsetting is provided for this, then the value is printed and puppet\nexits.  Comma-separate multiple values.  For a list of all values,\nspecify 'all'. This setting is deprecated, the 'puppet config' command replaces this functionality.\n\n\n### crl_refresh_interval\n\nHow often the Puppet agent refreshes its local Certificate\nRevocation List (CRL). By default, the CRL is refreshed every 24 hours. If\na different interval is specified, the agent refreshes its CRL on the next\nPuppet agent run if the elapsed time since the CRL was last refreshed\nexceeds the specified interval.\n\nIn general, the interval should be greater than the `runinterval` value.\nSetting the `crl_refresh_interval` value to 0 or an equal or lesser value\nthan `runinterval` causes the CRL to be refreshed on every run.\n\nIf the agent downloads a new CRL, the agent will use it for subsequent\nnetwork requests. If the refresh request fails or if the CRL is\nunchanged on the server, then the agent run will continue using the\nlocal CRL it already has.This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `1d`\n\n### csr_attributes\n\nAn optional file containing custom attributes to add to certificate signing\nrequests (CSRs). You should ensure that this file does not exist on your CA\nPuppet Server; if it does, unwanted certificate extensions may leak into\ncertificates created with the `puppetserver ca generate` command.\n\nIf present, this file must be a YAML hash containing a `custom_attributes` key\nand/or an `extension_requests` key. The value of each key must be a hash, where\neach key is a valid OID and each value is an object that can be cast to a string.\n\nCustom attributes can be used by the CA when deciding whether to sign the\ncertificate, but are then discarded. Attribute OIDs can be any OID value except\nthe standard CSR attributes (i.e. attributes described in RFC 2985 section 5.4).\nThis is useful for embedding a pre-shared key for autosigning policy executables\n(see the `autosign` setting), often by using the `1.2.840.113549.1.9.7`\n(\"challenge password\") OID.\n\nExtension requests will be permanently embedded in the final certificate.\nExtension OIDs must be in the \"ppRegCertExt\" (`1.3.6.1.4.1.34380.1.1`),\n\"ppPrivCertExt\" (`1.3.6.1.4.1.34380.1.2`), or\n\"ppAuthCertExt\" (`1.3.6.1.4.1.34380.1.3`) OID arcs. The ppRegCertExt arc is\nreserved for four of the most common pieces of data to embed: `pp_uuid` (`.1`),\n`pp_instance_id` (`.2`), `pp_image_name` (`.3`), and `pp_preshared_key` (`.4`)\n--- in the YAML file, these can be referred to by their short descriptive names\ninstead of their full OID. The ppPrivCertExt arc is unregulated, and can be used\nfor site-specific extensions. The ppAuthCert arc is reserved for two pieces of\ndata to embed: `pp_authorization` (`.1`) and `pp_auth_role` (`.13`). As with\nppRegCertExt, in the YAML file, these can be referred to by their short\ndescriptive name instead of their full OID.\n\n- *Default*: `$confdir/csr_attributes.yaml`\n\n### csrdir\n\nWhere the CA stores certificate requests.\n\n- *Default*: `$cadir/requests`\n\n### daemonize\n\nWhether to send the process into the background.  This defaults\nto true on POSIX systems, and to false on Windows (where Puppet\ncurrently cannot daemonize).\n\n- *Default*: `true`\n\n### data_binding_terminus\n\nThis setting has been deprecated. Use of any value other than 'hiera' should instead be configured\nin a version 5 hiera.yaml. Until this setting is removed, it controls which data binding terminus\nto use for global automatic data binding (across all environments). By default this value is 'hiera'.\nA value of 'none' turns off the global binding.\n\n- *Default*: `hiera`\n\n### default_file_terminus\n\nThe default source for files if no server is given in a\nuri, e.g. puppet:///file. The default of `rest` causes the file to be\nretrieved using the `server` setting. When running `apply` the default\nis `file_server`, causing requests to be filled locally.\n\n- *Default*: `rest`\n\n### default_manifest\n\nThe default main manifest for directory environments. Any environment that\ndoesn't set the `manifest` setting in its `environment.conf` file will use\nthis manifest.\n\nThis setting's value can be an absolute or relative path. An absolute path\nwill make all environments default to the same main manifest; a relative\npath will allow each environment to use its own manifest, and Puppet will\nresolve the path relative to each environment's main directory.\n\nIn either case, the path can point to a single file or to a directory of\nmanifests to be evaluated in alphabetical order.\n\n- *Default*: `./manifests`\n\n### default_schedules\n\nBoolean; whether to generate the default schedule resources. Setting this to\nfalse is useful for keeping external report processors clean of skipped schedule resources.\n\n- *Default*: `true`\n\n### deviceconfdir\n\nThe root directory of devices' $confdir.\n\n- *Default*: `$confdir/devices`\n\n### deviceconfig\n\nPath to the device config file for puppet device.\n\n- *Default*: `$confdir/device.conf`\n\n### devicedir\n\nThe root directory of devices' $vardir.\n\n- *Default*: `$vardir/devices`\n\n### diff\n\nWhich diff command to use when printing differences between files. This setting\nhas no default value on Windows, as standard `diff` is not available, but Puppet can use many\nthird-party diff tools.\n\n- *Default*: `diff`\n\n### diff_args\n\nWhich arguments to pass to the diff command when printing differences between\nfiles. The command to use can be chosen with the `diff` setting.\n\n- *Default*: `-u`\n\n### digest_algorithm\n\nWhich digest algorithm to use for file resources and the filebucket.\nValid values are sha256, sha384, sha512, sha224, md5. Default is\nsha256.\n\n- *Default*: `sha256`\n\n### disable_i18n\n\nIf true, turns off all translations of Puppet and module\nlog messages, which affects error, warning, and info log messages,\nas well as any translations in the report and CLI.\n\n- *Default*: `true`\n\n### disable_per_environment_manifest\n\nWhether to disallow an environment-specific main manifest. When set\nto `true`, Puppet will use the manifest specified in the `default_manifest` setting\nfor all environments. If an environment specifies a different main manifest in its\n`environment.conf` file, catalog requests for that environment will fail with an error.\n\nThis setting requires `default_manifest` to be set to an absolute path.\n\n- *Default*: `false`\n\n### disable_warnings\n\nA comma-separated list of warning types to suppress. If large numbers\nof warnings are making Puppet's logs too large or difficult to use, you\ncan temporarily silence them with this setting.\n\nIf you are preparing to upgrade Puppet to a new major version, you\nshould re-enable all warnings for a while.\n\nValid values for this setting are:\n\n* `deprecations` --- disables deprecation warnings.\n* `undefined_variables` --- disables warnings about non existing variables.\n* `undefined_resources` --- disables warnings about non existing resources.\n\n- *Default*: `[]`\n\n### dns_alt_names\n\nA comma-separated list of alternate DNS names for Puppet Server. These are extra\nhostnames (in addition to its `certname`) that the server is allowed to use when\nserving agents. Puppet checks this setting when automatically creating a\ncertificate for Puppet agent or Puppet Server. These can be either IP or DNS, and the type\nshould be specified and followed with a colon. Untyped inputs will default to DNS.\n\nIn order to handle agent requests at a given hostname (like\n\"puppet.example.com\"), Puppet Server needs a certificate that proves it's\nallowed to use that name; if a server shows a certificate that doesn't include\nits hostname, Puppet agents will refuse to trust it. If you use a single\nhostname for Puppet traffic but load-balance it to multiple Puppet Servers, each\nof those servers needs to include the official hostname in its list of extra\nnames.\n\n**Note:** The list of alternate names is locked in when the server's\ncertificate is signed. If you need to change the list later, you can't just\nchange this setting; you also need to regenerate the certificate. For more\ninformation on that process, see the\n[cert regen docs](https://puppet.com/docs/puppet/latest/ssl_regenerate_certificates.html).\n\nTo see all the alternate names your servers are using, log into your CA server\nand run `puppetserver ca list --all`, then check the output for `(alt names: ...)`.\nMost agent nodes should NOT have alternate names; the only certs that should\nhave them are Puppet Server nodes that you want other agents to trust.\n\n\n### document_all\n\nWhether to document all resources when using `puppet doc` to\ngenerate manifest documentation.\n\n- *Default*: `false`\n\n### environment\n\nThe environment in which Puppet is running. For clients,\nsuch as `puppet agent`, this determines the environment itself, which\nPuppet uses to find modules and much more. For servers, such as `puppet server`,\nthis provides the default environment for nodes that Puppet knows nothing about.\n\nWhen defining an environment in the `[agent]` section, this refers to the\nenvironment that the agent requests from the primary server. The environment doesn't\nhave to exist on the local filesystem because the agent fetches it from the\nprimary server. This definition is used when running `puppet agent`.\n\nWhen defined in the `[user]` section, the environment refers to the path that\nPuppet uses to search for code and modules related to its execution. This\nrequires the environment to exist locally on the filesystem where puppet is\nbeing executed. Puppet subcommands, including `puppet module` and\n`puppet apply`, use this definition.\n\nGiven that the context and effects vary depending on the\n[config section](https://puppet.com/docs/puppet/latest/config_file_main.html#config-sections)\nin which the `environment` setting is defined, do not set it globally.\n\n- *Default*: `production`\n\n### environment_data_provider\n\nThe name of a registered environment data provider used when obtaining environment\nspecific data. The three built in and registered providers are 'none' (no data), 'function' (data\nobtained by calling the function 'environment::data()') and 'hiera' (data obtained using a data\nprovider configured using a hiera.yaml file in root of the environment).\nOther environment data providers may be registered in modules on the module path. For such\ncustom data providers see the respective module documentation. This setting is deprecated.\n\n- *Default*: ``\n\n### environment_timeout\n\nHow long the Puppet server should cache data it loads from an\nenvironment.\n\nA value of `0` will disable caching. This setting can also be set to\n`unlimited`, which will cache environments until the server is restarted\nor told to refresh the cache. All other values will result in Puppet\nserver evicting environments that haven't been used within the last\n`environment_timeout` seconds.\n\nYou should change this setting once your Puppet deployment is doing\nnon-trivial work. We chose the default value of `0` because it lets new\nusers update their code without any extra steps, but it lowers the\nperformance of your Puppet server. We recommend either:\n\n* Setting this to `unlimited` and explicitly refreshing your Puppet server\n  as part of your code deployment process.\n\n* Setting this to a number that will keep your most actively used\n  environments cached, but allow testing environments to fall out of the\n  cache and reduce memory usage. A value of 3 minutes (3m) is a reasonable\n  value.\n\nOnce you set `environment_timeout` to a non-zero value, you need to tell\nPuppet server to read new code from disk using the `environment-cache` API\nendpoint after you deploy new code. See the docs for the Puppet Server\n[administrative API](https://puppet.com/docs/puppetserver/latest/admin-api/v1/environment-cache.html).\n\n- *Default*: `0`\n\n### environmentpath\n\nA search path for directory environments, as a list of directories\nseparated by the system path separator character. (The POSIX path separator\nis ':', and the Windows path separator is ';'.)\n\nThis setting must have a value set to enable **directory environments.** The\nrecommended value is `$codedir/environments`. For more details, see\n<https://puppet.com/docs/puppet/latest/environments_about.html>\n\n- *Default*: `$codedir/environments`\n\n### evaltrace\n\nWhether each resource should log when it is\nbeing evaluated.  This allows you to interactively see exactly\nwhat is being done.\n\n- *Default*: `false`\n\n### exclude_unchanged_resources\n\nSpecifies how unchanged resources are listed in reports. When\nset to `true`, resources that have had no changes after catalog application\nwill not have corresponding unchanged resource status updates listed in a\nreport.\n\n- *Default*: `true`\n\n### external_nodes\n\nThe external node classifier (ENC) script to use for node data.\nPuppet combines this data with the main manifest to produce node catalogs.\n\nTo enable this setting, set the `node_terminus` setting to `exec`.\n\nThis setting's value must be the path to an executable command that\ncan produce node information. The command must:\n\n* Take the name of a node as a command-line argument.\n\n* Return a YAML hash with up to three keys:\n  * `classes` --- A list of classes, as an array or hash.\n  * `environment` --- A string.\n  * `parameters` --- A list of top-scope variables to set, as a hash.\n\n* For unknown nodes, exit with a non-zero exit code.\n\nGenerally, an ENC script makes requests to an external data source.\n\nFor more info, see [the ENC documentation](https://puppet.com/docs/puppet/latest/nodes_external.html).\n\n- *Default*: `none`\n\n### fact_name_length_soft_limit\n\nThe soft limit for the length of a fact name.\n\n- *Default*: `2560`\n\n### fact_value_length_soft_limit\n\nThe soft limit for the length of a fact value.\n\n- *Default*: `4096`\n\n### factpath\n\nWhere Puppet should look for facts.  Multiple directories should\nbe separated by the system path separator character. (The POSIX path\nseparator is ':', and the Windows path separator is ';'.)\n\n- *Default*: `$vardir/lib/facter:$vardir/facts`\n\n### facts_terminus\n\nThe node facts terminus.\n\n- *Default*: `facter`\n\n### fileserverconfig\n\nWhere the fileserver configuration is stored.\n\n- *Default*: `$confdir/fileserver.conf`\n\n### filetimeout\n\nThe minimum time to wait between checking for updates in\nconfiguration files.  This timeout determines how quickly Puppet checks whether\na file (such as manifests or puppet.conf) has changed on disk. The default will\nchange in a future release to be 'unlimited', requiring a reload of the Puppet\nservice to pick up changes to its internal configuration. Currently we do not\naccept a value of 'unlimited'. To reparse files within an environment in\nPuppet Server please use the environment_cache endpoint\n\n- *Default*: `15s`\n\n### forge_authorization\n\nThe authorization key to connect to the Puppet Forge. Leave blank for unauthorized or license based connections\n\n- *Default*: ``\n\n### freeze_main\n\nFreezes the 'main' class, disallowing any code to be added to it.  This\nessentially means that you can't have any code outside of a node,\nclass, or definition other than in the site manifest.\n\n- *Default*: `false`\n\n### genconfig\n\nWhen true, causes Puppet applications to print an example config file\nto stdout and exit. The example will include descriptions of each\nsetting, and the current (or default) value of each setting,\nincorporating any settings overridden on the CLI (with the exception\nof `genconfig` itself). This setting only makes sense when specified\non the command line as `--genconfig`.\n\n- *Default*: `false`\n\n### genmanifest\n\nWhether to just print a manifest to stdout and exit.  Only makes\nsense when specified on the command line as `--genmanifest`.  Takes into account arguments specified\non the CLI.\n\n- *Default*: `false`\n\n### graph\n\nWhether to create .dot graph files, which let you visualize the\ndependency and containment relationships in Puppet's catalog. You\ncan load and view these files with tools like\n[OmniGraffle](http://www.omnigroup.com/applications/omnigraffle/) (OS X)\nor [graphviz](http://www.graphviz.org/) (multi-platform).\n\nGraph files are created when _applying_ a catalog, so this setting\nshould be used on nodes running `puppet agent` or `puppet apply`.\n\nThe `graphdir` setting determines where Puppet will save graphs. Note\nthat we don't save graphs for historical runs; Puppet will replace the\nprevious .dot files with new ones every time it applies a catalog.\n\nSee your graphing software's documentation for details on opening .dot\nfiles. If you're using GraphViz's `dot` command, you can do a quick PNG\nrender with `dot -Tpng <DOT FILE> -o <OUTPUT FILE>`.\n\n- *Default*: `false`\n\n### graphdir\n\nWhere to save .dot-format graphs (when the `graph` setting is enabled).\n\n- *Default*: `$statedir/graphs`\n\n### group\n\nThe group Puppet Server will run as. Used to ensure\nthe agent side processes (agent, apply, etc) create files and\ndirectories readable by Puppet Server when necessary.\n\n- *Default*: `puppet`\n\n### hiera_config\n\nThe hiera configuration file. Puppet only reads this file on startup, so you must restart the puppet server every time you edit it.\n\n- *Default*: `$confdir/hiera.yaml. However, for backwards compatibility, if a file exists at $codedir/hiera.yaml, Puppet uses that instead.`\n\n### hostcert\n\nWhere individual hosts store and look for their certificates.\n\n- *Default*: `$certdir/$certname.pem`\n\n### hostcert_renewal_interval\n\nHow often the Puppet agent renews its client certificate. By\ndefault, the client certificate is renewed 30 days before the certificate\nexpires. If a different interval is specified, the agent renews its client\ncertificate during the next agent run, assuming that the client certificate has\nexpired within the specified duration.\n\nIn general, the `hostcert_renewal_interval` value should be greater than the\n`runinterval` value. Setting the `hostcert_renewal_interval` value to 0 disables\nautomatic renewal.\n\nIf the agent downloads a new certificate, the agent will use it\nfor subsequent network requests. If the refresh request fails, the agent run\ncontinues to use its existing certificate. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `30d`\n\n### hostcrl\n\nWhere the host's certificate revocation list can be found.\nThis is distinct from the certificate authority's CRL.\n\n- *Default*: `$ssldir/crl.pem`\n\n### hostcsr\n\nWhere individual hosts store their certificate request (CSR)\nwhile waiting for the CA to issue their certificate.\n\n- *Default*: `$requestdir/$certname.pem`\n\n### hostprivkey\n\nWhere individual hosts store and look for their private key.\n\n- *Default*: `$privatekeydir/$certname.pem`\n\n### hostpubkey\n\nWhere individual hosts store and look for their public key.\n\n- *Default*: `$publickeydir/$certname.pem`\n\n### http_connect_timeout\n\nThe maximum amount of time to wait when establishing an HTTP connection. The default\nvalue is 2 minutes.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `2m`\n\n### http_debug\n\nWhether to write HTTP request and responses to stderr. This should never be used in a production environment.\n\n- *Default*: `false`\n\n### http_extra_headers\n\nThe list of extra headers that will be sent with http requests to the primary server.\nThe header definition consists of a name and a value separated by a colon.\n\n- *Default*: `[]`\n\n### http_keepalive_timeout\n\nThe maximum amount of time a persistent HTTP connection can remain idle in the connection pool, before it is closed.  This timeout should be shorter than the keepalive timeout used on the HTTP server, e.g. Apache KeepAliveTimeout directive.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `4s`\n\n### http_proxy_host\n\nThe HTTP proxy host to use for outgoing connections. The proxy will be bypassed if\nthe server's hostname matches the NO_PROXY environment variable or `no_proxy` setting. Note: You\nmay need to use a FQDN for the server hostname when using a proxy. Environment variable\nhttp_proxy or HTTP_PROXY will override this value.\n\n- *Default*: `none`\n\n### http_proxy_password\n\nThe password for the user of an authenticated HTTP proxy.\nRequires the `http_proxy_user` setting.\n\nNote that passwords must be valid when used as part of a URL. If a password\ncontains any characters with special meanings in URLs (as specified by RFC 3986\nsection 2.2), they must be URL-encoded. (For example, `#` would become `%23`.)\n\n- *Default*: `none`\n\n### http_proxy_port\n\nThe HTTP proxy port to use for outgoing connections\n\n- *Default*: `3128`\n\n### http_proxy_user\n\nThe user name for an authenticated HTTP proxy. Requires the `http_proxy_host` setting.\n\n- *Default*: `none`\n\n### http_read_timeout\n\nThe time to wait for data to be read from an HTTP connection. If nothing is\nread after the elapsed interval then the connection will be closed. The default value is 10 minutes.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `10m`\n\n### http_user_agent\n\nThe HTTP User-Agent string to send when making network requests.\n\n- *Default*: `Puppet/<version> Ruby/<version> (<architecture>)`\n\n### ignore_plugin_errors\n\nWhether the puppet run should ignore errors during pluginsync. If the setting\nis false and there are errors during pluginsync, then the agent will abort the run and\nsubmit a report containing information about the failed run.\n\n- *Default*: `false`\n\n### ignoremissingtypes\n\nSkip searching for classes and definitions that were missing during a\nprior compilation. The list of missing objects is maintained per-environment and\npersists until the environment is cleared or the primary server is restarted.\n\n- *Default*: `false`\n\n### ignoreschedules\n\nBoolean; whether puppet agent should ignore schedules.  This is useful\nfor initial puppet agent runs.\n\n- *Default*: `false`\n\n### include_legacy_facts\n\nWhether to include legacy facts when requesting a catalog. This\noption can be set to `false` if all puppet manifests, hiera.yaml, and hiera\nconfiguration layers no longer access legacy facts, such as `$osfamily`, and\ninstead access structured facts, such as `$facts['os']['family']`.\n\n- *Default*: `false`\n\n### key_type\n\nThe type of private key. Valid values are `rsa` and `ec`. Default is `rsa`.\n\n- *Default*: `rsa`\n\n### keylength\n\nThe bit length of keys.\n\n- *Default*: `4096`\n\n### lastrunfile\n\nWhere puppet agent stores the last run report summary in yaml format.\n\n- *Default*: `$publicdir/last_run_summary.yaml`\n\n### lastrunreport\n\nWhere Puppet Agent stores the last run report, by default, in yaml format.\nThe format of the report can be changed by setting the `cache` key of the `report` terminus\nin the [routes.yaml](https://puppet.com/docs/puppet/latest/config_file_routes.html) file.\nTo avoid mismatches between content and file extension, this setting needs to be\nmanually updated to reflect the terminus changes.\n\n- *Default*: `$statedir/last_run_report.yaml`\n\n### ldapattrs\n\nThe LDAP attributes to include when querying LDAP for nodes.  All\nreturned attributes are set as variables in the top-level scope.\nMultiple values should be comma-separated.  The value 'all' returns\nall attributes.\n\n- *Default*: `all`\n\n### ldapbase\n\nThe search base for LDAP searches.  It's impossible to provide\na meaningful default here, although the LDAP libraries might\nhave one already set.  Generally, it should be the 'ou=Hosts'\nbranch under your main directory.\n\n\n### ldapclassattrs\n\nThe LDAP attributes to use to define Puppet classes.  Values\nshould be comma-separated.\n\n- *Default*: `puppetclass`\n\n### ldapparentattr\n\nThe attribute to use to define the parent node.\n\n- *Default*: `parentnode`\n\n### ldappassword\n\nThe password to use to connect to LDAP.\n\n\n### ldapport\n\nThe LDAP port.\n\n- *Default*: `389`\n\n### ldapserver\n\nThe LDAP server.\n\n- *Default*: `ldap`\n\n### ldapssl\n\nWhether SSL should be used when searching for nodes.\nDefaults to false because SSL usually requires certificates\nto be set up on the client side.\n\n- *Default*: `false`\n\n### ldapstackedattrs\n\nThe LDAP attributes that should be stacked to arrays by adding\nthe values in all hierarchy elements of the tree.  Values\nshould be comma-separated.\n\n- *Default*: `puppetvar`\n\n### ldapstring\n\nThe search string used to find an LDAP node.\n\n- *Default*: `(&(objectclass=puppetClient)(cn=%s))`\n\n### ldaptls\n\nWhether TLS should be used when searching for nodes.\nDefaults to false because TLS usually requires certificates\nto be set up on the client side.\n\n- *Default*: `false`\n\n### ldapuser\n\nThe user to use to connect to LDAP.  Must be specified as a\nfull DN.\n\n\n### libdir\n\nAn extra search path for Puppet.  This is only useful\nfor those files that Puppet will load on demand, and is only\nguaranteed to work for those cases.  In fact, the autoload\nmechanism is responsible for making sure this directory\nis in Ruby's search path\n\n- *Default*: `$vardir/lib`\n\n### localcacert\n\nWhere each client stores the CA certificate.\n\n- *Default*: `$certdir/ca.pem`\n\n### localedest\n\nWhere Puppet should store translation files that it pulls down from the central\nserver.\n\n- *Default*: `$vardir/locales`\n\n### localesource\n\nFrom where to retrieve translation files.  The standard Puppet `file` type\nis used for retrieval, so anything that is a valid file source can\nbe used here.\n\n- *Default*: `puppet:///locales`\n\n### location_trusted\n\nThis will allow sending the name + password and the cookie header to all hosts that puppet may redirect to.\nThis may or may not introduce a security breach if puppet redirects you to a site to which you'll send your authentication info and cookies.\n\n- *Default*: `false`\n\n### log_level\n\nDefault logging level for messages from Puppet. Allowed values are:\n\n* debug\n* info\n* notice\n* warning\n* err\n* alert\n* emerg\n* crit\n\n- *Default*: `notice`\n\n### logdest\n\nWhere to send log messages. Choose between 'syslog' (the POSIX syslog\nservice), 'eventlog' (the Windows Event Log), 'console', or the path to a log\nfile. Multiple destinations can be set using a comma separated list (eg: `/path/file1,console,/path/file2`)\n\n- *Default*: ``\n\n### logdir\n\nThe directory in which to store log files\n\n- *Default*: `Unix/Linux: /var/log/puppetlabs/puppet -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\var\\log -- Non-root user: ~/.puppetlabs/var/log`\n\n### manage_internal_file_permissions\n\nWhether Puppet should manage the owner, group, and mode of files it uses internally.\n**Note**: For Windows agents, the default is `false` for versions 4.10.13 and greater, versions 5.5.6 and greater, and versions 6.0 and greater.\n\n- *Default*: `true`\n\n### manifest\n\nThe entry-point manifest for the primary server. This can be one file\nor a directory of manifests to be evaluated in alphabetical order. Puppet manages\nthis path as a directory if one exists or if the path ends with a / or \\.\n\nSetting a global value for `manifest` in puppet.conf is not allowed\n(but it can be overridden from the commandline). Please use\ndirectory environments instead. If you need to use something other than the\nenvironment's `manifests` directory as the main manifest, you can set\n`manifest` in environment.conf. For more info, see\n<https://puppet.com/docs/puppet/latest/environments_about.html>\n\n- *Default*: ``\n\n### masterport\n\nThe default port puppet subcommands use to communicate\nwith Puppet Server. (eg `puppet facts upload`, `puppet agent`). May be\noverridden by more specific settings (see `ca_port`, `report_port`).\n\n- *Default*: `8140`\n\n### max_deprecations\n\nSets the max number of logged/displayed parser validation deprecation\nwarnings in case multiple deprecation warnings have been detected. A value of 0\nblocks the logging of deprecation warnings.  The count is per manifest.\n\n- *Default*: `10`\n\n### max_errors\n\nSets the max number of logged/displayed parser validation errors in case\nmultiple errors have been detected. A value of 0 is the same as a value of 1; a\nminimum of one error is always raised.  The count is per manifest.\n\n- *Default*: `10`\n\n### max_warnings\n\nSets the max number of logged/displayed parser validation warnings in\ncase multiple warnings have been detected. A value of 0 blocks logging of\nwarnings.  The count is per manifest.\n\n- *Default*: `10`\n\n### maximum_uid\n\nThe maximum allowed UID.  Some platforms use negative UIDs\nbut then ship with tools that do not know how to handle signed ints,\nso the UIDs show up as huge numbers that can then not be fed back into\nthe system.  This is a hackish way to fail in a slightly more useful\nway when that happens.\n\n- *Default*: `4294967290`\n\n### maxwaitforcert\n\nThe maximum amount of time the Puppet agent should wait for its\ncertificate request to be signed. A value of `unlimited` will cause puppet agent\nto ask for a signed certificate indefinitely.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `unlimited`\n\n### maxwaitforlock\n\nThe maximum amount of time the puppet agent should wait for an\nalready running puppet agent to finish before starting a new one. This is set by default to 1 minute.\nA value of `unlimited` will cause puppet agent to wait indefinitely.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `1m`\n\n### merge_dependency_warnings\n\nWhether to merge class-level dependency failure warnings.\n\nWhen a class has a failed dependency, every resource in the class\ngenerates a notice level message about the dependency failure,\nand a warning level message about skipping the resource.\n\nIf true, all messages caused by a class dependency failure are merged\ninto one message associated with the class.\n\n- *Default*: `false`\n\n### mkusers\n\nWhether to create the necessary user and group that puppet agent will run as.\n\n- *Default*: `false`\n\n### module_groups\n\nExtra module groups to request from the Puppet Forge. This is an internal setting, and users should never change it.\n\n- *Default*: ``\n\n### module_repository\n\nThe module repository\n\n- *Default*: `https://forgeapi.puppet.com`\n\n### module_working_dir\n\nThe directory into which module tool data is stored\n\n- *Default*: `$vardir/puppet-module`\n\n### modulepath\n\nThe search path for modules, as a list of directories separated by the system\npath separator character. (The POSIX path separator is ':', and the\nWindows path separator is ';'.)\n\nSetting a global value for `modulepath` in puppet.conf is not allowed\n(but it can be overridden from the commandline). Please use\ndirectory environments instead. If you need to use something other than the\ndefault modulepath of `<ACTIVE ENVIRONMENT'S MODULES DIR>:$basemodulepath`,\nyou can set `modulepath` in environment.conf. For more info, see\n<https://puppet.com/docs/puppet/latest/environments_about.html>\n\n\n### name\n\nThe name of the application, if we are running as one.  The\ndefault is essentially $0 without the path or `.rb`.\n\n- *Default*: ``\n\n### named_curve\n\nThe short name for the EC curve used to generate the EC private key. Valid\nvalues must be one of the curves in `OpenSSL::PKey::EC.builtin_curves`.\nDefault is `prime256v1`.\n\n- *Default*: `prime256v1`\n\n### no_proxy\n\nList of host or domain names that should not go through `http_proxy_host`. Environment variable no_proxy or NO_PROXY will override this value. Names can be specified as an FQDN `host.example.com`, wildcard `*.example.com`, dotted domain `.example.com`, or suffix `example.com`.\n\n- *Default*: `localhost, 127.0.0.1`\n\n### node_cache_terminus\n\nHow to store cached nodes.\nValid values are (none), 'json', 'msgpack', or 'yaml'.\n\n- *Default*: ``\n\n### node_name_fact\n\nThe fact name used to determine the node name used for all requests the agent\nmakes to the primary server. WARNING: This setting is mutually exclusive with\nnode_name_value.  Changing this setting also requires changes to\nPuppet Server's default [auth.conf](https://puppet.com/docs/puppetserver/latest/config_file_auth.html).\n\n\n### node_name_value\n\nThe explicit value used for the node name for all requests the agent\nmakes to the primary server. WARNING: This setting is mutually exclusive with\nnode_name_fact.  Changing this setting also requires changes to\nPuppet Server's default [auth.conf](https://puppet.com/docs/puppetserver/latest/config_file_auth.html).\n\n- *Default*: `$certname`\n\n### node_terminus\n\nWhich node data plugin to use when compiling node catalogs.\n\nWhen Puppet compiles a catalog, it combines two primary sources of info: the main manifest,\nand a node data plugin (often called a \"node terminus,\" for historical reasons). Node data\nplugins provide three things for a given node name:\n\n1. A list of classes to add to that node's catalog (and, optionally, values for their\n   parameters).\n2. Which Puppet environment the node should use.\n3. A list of additional top-scope variables to set.\n\nThe three main node data plugins are:\n\n* `plain` --- Returns no data, so that the main manifest controls all node configuration.\n* `exec` --- Uses an\n  [external node classifier (ENC)](https://puppet.com/docs/puppet/latest/nodes_external.html),\n  configured by the `external_nodes` setting. This lets you pull a list of Puppet classes\n  from any external system, using a small glue script to perform the request and format the\n  result as YAML.\n* `classifier` (formerly `console`) --- Specific to Puppet Enterprise. Uses the PE console\n  for node data.\"\n\n- *Default*: `plain`\n\n### noop\n\nWhether to apply catalogs in noop mode, which allows Puppet to\npartially simulate a normal run. This setting affects puppet agent and\npuppet apply.\n\nWhen running in noop mode, Puppet will check whether each resource is in sync,\nlike it does when running normally. However, if a resource attribute is not in\nthe desired state (as declared in the catalog), Puppet will take no\naction, and will instead report the changes it _would_ have made. These\nsimulated changes will appear in the report sent to the primary Puppet server, or\nbe shown on the console if running puppet agent or puppet apply in the\nforeground. The simulated changes will not send refresh events to any\nsubscribing or notified resources, although Puppet will log that a refresh\nevent _would_ have been sent.\n\n**Important note:**\n[The `noop` metaparameter](https://puppet.com/docs/puppet/latest/metaparameter.html#noop)\nallows you to apply individual resources in noop mode, and will override\nthe global value of the `noop` setting. This means a resource with\n`noop => false` _will_ be changed if necessary, even when running puppet\nagent with `noop = true` or `--noop`. (Conversely, a resource with\n`noop => true` will only be simulated, even when noop mode is globally disabled.)\n\n- *Default*: `false`\n\n### number_of_facts_soft_limit\n\nThe soft limit for the total number of fact values. This counts the\nchild elements of all facts (e.g. all items of an array or a hash), not just top\nlevel facts.\n\n- *Default*: `10240`\n\n### onetime\n\nPerform one configuration run and exit, rather than spawning a long-running\ndaemon. This is useful for interactively running puppet agent, or\nrunning puppet agent from cron.\n\n- *Default*: `false`\n\n### passfile\n\nWhere puppet agent stores the password for its private key.\nGenerally unused.\n\n- *Default*: `$privatedir/password`\n\n### path\n\nThe shell search path.  Defaults to whatever is inherited\nfrom the parent process.\n\nThis setting can only be set in the `[main]` section of puppet.conf; it cannot\nbe set in `[server]`, `[agent]`, or an environment config section.\n\n- *Default*: `none`\n\n### payload_soft_limit\n\nThe soft limit for the size of the payload.\n\n- *Default*: `16777216`\n\n### pidfile\n\nThe file containing the PID of a running process.\nThis file is intended to be used by service management frameworks\nand monitoring systems to determine if a puppet process is still in\nthe process table.\n\n- *Default*: `$rundir/${run_mode}.pid`\n\n### plugindest\n\nWhere Puppet should store plugins that it pulls down from the central\nserver.\n\n- *Default*: `$libdir`\n\n### pluginfactdest\n\nWhere Puppet should store external facts that are being handled by pluginsync\n\n- *Default*: `$vardir/facts.d`\n\n### pluginfactsource\n\nWhere to retrieve external facts for pluginsync\n\n- *Default*: `puppet:///pluginfacts`\n\n### pluginsignore\n\nWhat files to ignore when pulling down plugins.\n\n- *Default*: `.svn CVS .git .hg`\n\n### pluginsource\n\nFrom where to retrieve plugins.  The standard Puppet `file` type\nis used for retrieval, so anything that is a valid file source can\nbe used here.\n\n- *Default*: `puppet:///plugins`\n\n### pluginsync\n\nWhether plugins should be synced with the central server. This setting is\ndeprecated.\n\n- *Default*: `true`\n\n### postrun_command\n\nA command to run after every agent run.  If this command returns a non-zero\nreturn code, the entire Puppet run will be considered to have failed, even though it might have\nperformed work during the normal run.\n\n\n### preferred_serialization_format\n\nThe preferred means of serializing\nruby instances for passing over the wire.  This won't guarantee that all\ninstances will be serialized using this method, since not all classes\ncan be guaranteed to support this format, but it will be used for all\nclasses that support it.\n\n- *Default*: `json`\n\n### preprocess_deferred\n\nWhether Puppet should call deferred functions before applying\nthe catalog. If set to `true`, all prerequisites required for the\ndeferred function must be satisfied before the Puppet run. If set to\n`false`, deferred functions follow Puppet relationships and\nordering. In this way, Puppet can install the prerequisites required for a\ndeferred function and call the deferred function in the same run.\n\n- *Default*: `false`\n\n### prerun_command\n\nA command to run before every agent run.  If this command returns a non-zero\nreturn code, the entire Puppet run will fail.\n\n\n### preview_outputdir\n\nThe directory where catalog previews per node are generated.\n\n- *Default*: `$vardir/preview`\n\n### priority\n\nThe scheduling priority of the process.  Valid values are 'high',\n'normal', 'low', or 'idle', which are mapped to platform-specific\nvalues.  The priority can also be specified as an integer value and\nwill be passed as is, e.g. -5.  Puppet must be running as a privileged\nuser in order to increase scheduling priority.\n\n- *Default*: ``\n\n### privatedir\n\nWhere the client stores private certificate information.\n\n- *Default*: `$ssldir/private`\n\n### privatekeydir\n\nThe private key directory.\n\n- *Default*: `$ssldir/private_keys`\n\n### profile\n\nWhether to enable experimental performance profiling\n\n- *Default*: `false`\n\n### publicdir\n\nWhere Puppet stores public files.\n\n- *Default*: `Unix/Linux: /opt/puppetlabs/puppet/public -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\public -- Non-root user: ~/.puppetlabs/opt/puppet/public`\n\n### publickeydir\n\nThe public key directory.\n\n- *Default*: `$ssldir/public_keys`\n\n### puppet_trace\n\nWhether to print the Puppet stack trace on some errors.\nThis is a noop if `trace` is also set.\n\n- *Default*: `false`\n\n### puppetdlog\n\nThe fallback log file. This is only used when the `--logdest` option\nis not specified AND Puppet is running on an operating system where both\nthe POSIX syslog service and the Windows Event Log are unavailable. (Currently,\nno supported operating systems match that description.)\n\nDespite the name, both puppet agent and puppet server will use this file\nas the fallback logging destination.\n\nFor control over logging destinations, see the `--logdest` command line\noption in the manual pages for puppet server, puppet agent, and puppet\napply. You can see man pages by running `puppet <SUBCOMMAND> --help`,\nor read them online at https://puppet.com/docs/puppet/latest/man/.\n\n- *Default*: `$logdir/puppetd.log`\n\n### report\n\nWhether to send reports after every transaction.\n\n- *Default*: `true`\n\n### report_configured_environmentpath\n\nSpecifies how environment paths are reported. When the value of\n`versioned_environment_dirs` is `true`, Puppet applies the readlink function to\nthe `environmentpath` setting when constructing the environment's modulepath. The\nfull readlinked path is referred to as the \"resolved path,\" and the configured\npath potentially containing symlinks is the \"configured path.\" When reporting\nwhere resources come from, users may choose between the configured and resolved\npath.\n\nWhen set to `false`, the resolved paths are reported instead of the configured paths.\n\n- *Default*: `true`\n\n### report_include_system_store\n\nWhether the 'http' report processor should include the system\ncertificate store when submitting reports to HTTPS URLs. If false, then\nthe 'http' processor will only trust HTTPS report servers whose certificates\nare issued by the puppet CA or one of its intermediate CAs. If true, the\nprocessor will additionally trust CA certificates in the system's\ncertificate store.\n\n- *Default*: `false`\n\n### report_port\n\nThe port to communicate with the report_server.\n\n- *Default*: `$serverport`\n\n### report_server\n\nThe server to send transaction reports to.\n\n- *Default*: `$server`\n\n### reportdir\n\nThe directory in which to store reports. Each node gets\na separate subdirectory in this directory. This setting is only\nused when the `store` report processor is enabled (see the\n`reports` setting).\n\n- *Default*: `$vardir/reports`\n\n### reports\n\nThe list of report handlers to use. When using multiple report handlers,\ntheir names should be comma-separated, with whitespace allowed. (For example,\n`reports = http, store`.)\n\nThis setting is relevant to puppet server and puppet apply. The primary Puppet\nserver will call these report handlers with the reports it receives from\nagent nodes, and puppet apply will call them with its own report. (In\nall cases, the node applying the catalog must have `report = true`.)\n\nSee the report reference for information on the built-in report\nhandlers; custom report handlers can also be loaded from modules.\n(Report handlers are loaded from the lib directory, at\n`puppet/reports/NAME.rb`.)\n\nTo turn off reports entirely, set this to `none`\n\n- *Default*: `store`\n\n### reporturl\n\nThe URL that reports should be forwarded to. This setting\nis only used when the `http` report processor is enabled (see the\n`reports` setting).\n\n- *Default*: `http://localhost:3000/reports/upload`\n\n### requestdir\n\nWhere host certificate requests are stored.\n\n- *Default*: `$ssldir/certificate_requests`\n\n### resourcefile\n\nThe file in which puppet agent stores a list of the resources\nassociated with the retrieved configuration.\n\n- *Default*: `$statedir/resources.txt`\n\n### resubmit_facts\n\nWhether to send updated facts after every transaction. By default\npuppet only submits facts at the beginning of the transaction before applying a\ncatalog. Since puppet can modify the state of the system, the value of the facts\nmay change after puppet finishes. Therefore, any facts stored in puppetdb may not\nbe consistent until the agent next runs, typically in 30 minutes. If this feature\nis enabled, puppet will resubmit facts after applying its catalog, ensuring facts\nfor the node stored in puppetdb are current. However, this will double the fact\nsubmission load on puppetdb, so it is disabled by default.\n\n- *Default*: `false`\n\n### rich_data\n\nEnables having extended data in the catalog by storing them as a hash with the special key\n`__ptype`. When enabled, resource containing values of the data types `Binary`, `Regexp`,\n`SemVer`, `SemVerRange`, `Timespan` and `Timestamp`, as well as instances of types derived\nfrom `Object` retain their data type.\n\n- *Default*: `true`\n\n### route_file\n\nThe YAML file containing indirector route configuration.\n\n- *Default*: `$confdir/routes.yaml`\n\n### rundir\n\nWhere Puppet PID files are kept.\n\n- *Default*: `Unix/Linux: /var/run/puppetlabs -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\var\\run -- Non-root user: ~/.puppetlabs/var/run`\n\n### runinterval\n\nHow often puppet agent applies the catalog.\nNote that a runinterval of 0 means \"run continuously\" rather than\n\"never run.\" This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `30m`\n\n### runtimeout\n\nThe maximum amount of time an agent run is allowed to take.\nA Puppet agent run that exceeds this timeout will be aborted. A value\nof 0 disables the timeout. Defaults to 1 hour. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `1h`\n\n### serial\n\nWhere the serial number for certificates is stored.\n\n- *Default*: `$cadir/serial`\n\n### server\n\nThe primary Puppet server to which the Puppet agent should connect.\n\n- *Default*: `puppet`\n\n### server_datadir\n\nThe directory in which serialized data is stored, usually in a subdirectory.\n\n- *Default*: `$vardir/server_data`\n\n### server_list\n\nThe list of primary Puppet servers to which the Puppet agent should connect,\nin the order that they will be tried. Each value should be a fully qualified domain name, followed by an optional ':' and port number. If a port is omitted, Puppet uses masterport for that host.\n\n- *Default*: `[]`\n\n### serverport\n\nThe default port puppet subcommands use to communicate\nwith Puppet Server. (eg `puppet facts upload`, `puppet agent`). May be\noverridden by more specific settings (see `ca_port`, `report_port`).\n\n- *Default*: `8140`\n\n### settings_catalog\n\nWhether to compile and apply the settings catalog\n\n- *Default*: `true`\n\n### show_diff\n\nWhether to log and report a contextual diff when files are being replaced.\nThis causes partial file contents to pass through Puppet's normal\nlogging and reporting system, so this setting should be used with\ncaution if you are sending Puppet's reports to an insecure\ndestination. This feature currently requires the `diff/lcs` Ruby\nlibrary.\n\n- *Default*: `false`\n\n### signeddir\n\nWhere the CA stores signed certificates.\n\n- *Default*: `$cadir/signed`\n\n### skip_logging_catalog_request_destination\n\nSpecifies whether to suppress the notice of which compiler\nsupplied the catalog. A value of `true` suppresses the notice.\n\n- *Default*: `false`\n\n### skip_tags\n\nTags to use to filter resources.  If this is set, then\nonly resources not tagged with the specified tags will be applied.\nValues must be comma-separated.\n\n\n### sourceaddress\n\nThe address the agent should use to initiate requests.\n\n- *Default*: ``\n\n### splay\n\nWhether to sleep for a random amount of time, ranging from\nimmediately up to its `$splaylimit`, before performing its first agent run\nafter a service restart. After this period, the agent runs periodically\non its `$runinterval`.\n\nFor example, assume a default 30-minute `$runinterval`, `splay` set to its\ndefault of `false`, and an agent starting at :00 past the hour. The agent\nwould check in every 30 minutes at :01 and :31 past the hour.\n\nWith `splay` enabled, it waits any amount of time up to its `$splaylimit`\nbefore its first run. For example, it might randomly wait 8 minutes,\nthen start its first run at :08 past the hour. With the `$runinterval`\nat its default 30 minutes, its next run will be at :38 past the hour.\n\nIf you restart an agent's puppet service with `splay` enabled, it\nrecalculates its splay period and delays its first agent run after\nrestarting for this new period. If you simultaneously restart a group of\npuppet agents with `splay` enabled, their checkins to your primary servers\ncan be distributed more evenly.\n\n- *Default*: `false`\n\n### splaylimit\n\nThe maximum time to delay before an agent's first run when\n`splay` is enabled. Defaults to the agent's `$runinterval`. The\n`splay` interval is random and recalculated each time the agent is started or\nrestarted. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `$runinterval`\n\n### srv_domain\n\nThe domain which will be queried to find the SRV records of servers to use.\n\n- *Default*: `example.com`\n\n### ssl_client_header\n\nThe header containing an authenticated client's SSL DN.\nThis header must be set by the proxy to the authenticated client's SSL\nDN (e.g., `/CN=puppet.puppetlabs.com`).  Puppet will parse out the Common\nName (CN) from the Distinguished Name (DN) and use the value of the CN\nfield for authorization.\n\nNote that the name of the HTTP header gets munged by the web server\ncommon gateway interface: an `HTTP_` prefix is added, dashes are converted\nto underscores, and all letters are uppercased.  Thus, to use the\n`X-Client-DN` header, this setting should be `HTTP_X_CLIENT_DN`.\n\n- *Default*: `HTTP_X_CLIENT_DN`\n\n### ssl_client_verify_header\n\nThe header containing the status message of the client\nverification. This header must be set by the proxy to 'SUCCESS' if the\nclient successfully authenticated, and anything else otherwise.\n\nNote that the name of the HTTP header gets munged by the web server\ncommon gateway interface: an `HTTP_` prefix is added, dashes are converted\nto underscores, and all letters are uppercased.  Thus, to use the\n`X-Client-Verify` header, this setting should be\n`HTTP_X_CLIENT_VERIFY`.\n\n- *Default*: `HTTP_X_CLIENT_VERIFY`\n\n### ssl_lockfile\n\nA lock file to indicate that the ssl bootstrap process is currently in progress.\n\n- *Default*: `$ssldir/ssl.lock`\n\n### ssl_trust_store\n\nA file containing CA certificates in PEM format that puppet should trust\nwhen making HTTPS requests. This **only** applies to https requests to non-puppet\ninfrastructure, such as retrieving file metadata and content from https file sources,\npuppet module tool and the 'http' report processor. This setting is ignored when\nmaking requests to puppet:// URLs such as catalog and report requests.\n\n- *Default*: ``\n\n### ssldir\n\nWhere SSL certificates are kept.\n\n- *Default*: `$confdir/ssl`\n\n### statedir\n\nThe directory where Puppet state is stored.  Generally,\nthis directory can be removed without causing harm (although it\nmight result in spurious service restarts).\n\n- *Default*: `$vardir/state`\n\n### statefile\n\nWhere Puppet agent and Puppet Server store state associated\nwith the running configuration.  In the case of Puppet Server,\nthis file reflects the state discovered through interacting\nwith clients.\n\n- *Default*: `$statedir/state.yaml`\n\n### statettl\n\nHow long the Puppet agent should cache when a resource was last checked or synced.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\nA value of `0` or `unlimited` will disable cache pruning.\n\nThis setting affects the usage of `schedule` resources, as the information\nabout when a resource was last checked (and therefore when it needs to be\nchecked again) is stored in the `statefile`. The `statettl` needs to be\nlarge enough to ensure that a resource will not trigger multiple times\nduring a schedule due to its entry expiring from the cache.\n\n- *Default*: `32d`\n\n### static_catalogs\n\nWhether to compile a [static catalog](https://puppet.com/docs/puppet/latest/static_catalogs.html#enabling-or-disabling-static-catalogs),\nwhich occurs only on Puppet Server when the `code-id-command` and\n`code-content-command` settings are configured in its `puppetserver.conf` file.\n\n- *Default*: `true`\n\n### storeconfigs\n\nWhether to store each client's configuration, including catalogs, facts,\nand related data. This also enables the import and export of resources in\nthe Puppet language - a mechanism for exchange resources between nodes.\n\nBy default this uses the 'puppetdb' backend.\n\nYou can adjust the backend using the storeconfigs_backend setting.\n\n- *Default*: `false`\n\n### storeconfigs_backend\n\nConfigure the backend terminus used for StoreConfigs.\nBy default, this uses the PuppetDB store, which must be installed\nand configured before turning on StoreConfigs.\n\n- *Default*: `puppetdb`\n\n### strict\n\nThe strictness level of puppet. Allowed values are:\n\n* off     - do not perform extra validation, do not report\n* warning - perform extra validation, report as warning\n* error   - perform extra validation, fail with error (default)\n\nThe strictness level is for both language semantics and runtime\nevaluation validation. In addition to controlling the behavior with\nthis primary server switch some individual warnings may also be controlled\nby the disable_warnings setting.\n\nNo new validations will be added to a micro (x.y.z) release,\nbut may be added in minor releases (x.y.0). In major releases\nit expected that most (if not all) strictness validation become\nstandard behavior.\n\n- *Default*: `error`\n\n### strict_environment_mode\n\nWhether the agent specified environment should be considered authoritative,\ncausing the run to fail if the retrieved catalog does not match it.\n\n- *Default*: `false`\n\n### strict_variables\n\nCauses an evaluation error when referencing unknown variables. (This does not affect\nreferencing variables that are explicitly set to undef).\n\n- *Default*: `true`\n\n### summarize\n\nWhether to print a transaction summary.\n\n- *Default*: `false`\n\n### supported_checksum_types\n\nChecksum types supported by this agent for use in file resources of a\nstatic catalog. Values must be comma-separated. Valid types are\nsha256, sha256lite, sha384, sha512, sha224, sha1, sha1lite, md5, md5lite, mtime, ctime. Default is\nsha256, sha384, sha512, sha224, md5.\n\n- *Default*: `[\"sha256\", \"sha384\", \"sha512\", \"sha224\", \"md5\"]`\n\n### syslogfacility\n\nWhat syslog facility to use when logging to syslog.\nSyslog has a fixed list of valid facilities, and you must\nchoose one of those; you cannot just make one up.\n\n- *Default*: `daemon`\n\n### tags\n\nTags to use to find resources.  If this is set, then\nonly resources tagged with the specified tags will be applied.\nValues must be comma-separated.\n\n\n### tasks\n\nTurns on experimental support for tasks and plans in the puppet language. This is for internal API use only.\nDo not change this setting.\n\n- *Default*: `false`\n\n### top_level_facts_soft_limit\n\nThe soft limit for the number of top level facts.\n\n- *Default*: `512`\n\n### trace\n\nWhether to print stack traces on some errors. Will print\ninternal Ruby stack trace interleaved with Puppet function frames.\n\n- *Default*: `false`\n\n### transactionstorefile\n\nTransactional storage file for persisting data between\ntransactions for the purposes of inferring information (such as\ncorrective_change) on new data received.\n\n- *Default*: `$statedir/transactionstore.yaml`\n\n### trusted_external_command\n\nThe external trusted facts script or directory to use.\nThis setting's value can be set to the path to an executable command that\ncan produce external trusted facts or to a directory containing those\nexecutable commands. The command(s) must:\n\n* Take the name of a node as a command-line argument.\n* Return a JSON hash with the external trusted facts for this node.\n* For unknown or invalid nodes, exit with a non-zero exit code.\n\nIf the setting points to an executable command, then the external trusted\nfacts will be stored in the 'external' key of the trusted facts hash. Otherwise\nfor each executable file in the directory, the external trusted facts will be\nstored in the `<basename>` key of the `trusted['external']` hash. For example,\nif the files foo.rb and bar.sh are in the directory, then `trusted['external']`\nwill be the hash `{ 'foo' => <foo.rb output>, 'bar' => <bar.sh output> }`.\n\n- *Default*: ``\n\n### trusted_oid_mapping_file\n\nFile that provides mapping between custom SSL oids and user-friendly names\n\n- *Default*: `$confdir/custom_trusted_oid_mapping.yaml`\n\n### use_cached_catalog\n\nWhether to only use the cached catalog rather than compiling a new catalog\non every run.  Puppet can be run with this enabled by default and then selectively\ndisabled when a recompile is desired. Because a Puppet agent using cached catalogs\ndoes not contact the primary server for a new catalog, it also does not upload facts at\nthe beginning of the Puppet run.\n\n- *Default*: `false`\n\n### use_last_environment\n\nPuppet saves both the initial and converged environment in the last_run_summary file.\nIf they differ, and this setting is set to true, we will use the last converged\nenvironment and skip the node request.\n\nWhen set to false, we will do the node request and ignore the environment data from the last_run_summary file.\n\n- *Default*: `true`\n\n### use_srv_records\n\nWhether the server will search for SRV records in DNS for the current domain.\n\n- *Default*: `false`\n\n### usecacheonfailure\n\nWhether to use the cached configuration when the remote\nconfiguration will not compile.  This option is useful for testing\nnew configurations, where you want to fix the broken configuration\nrather than reverting to a known-good one.\n\n- *Default*: `true`\n\n### user\n\nThe user Puppet Server will run as. Used to ensure\nthe agent side processes (agent, apply, etc) create files and\ndirectories readable by Puppet Server when necessary.\n\n- *Default*: `puppet`\n\n### vardir\n\nWhere Puppet stores dynamic and growing data.  The default for this\nsetting is calculated specially, like `confdir`_.\n\n- *Default*: `Unix/Linux: /opt/puppetlabs/puppet/cache -- Windows: C:\\ProgramData\\PuppetLabs\\puppet\\cache -- Non-root user: ~/.puppetlabs/opt/puppet/cache`\n\n### vendormoduledir\n\nThe directory containing **vendored** modules. These modules will\nbe used by _all_ environments like those in the `basemodulepath`. The only\ndifference is that modules in the `basemodulepath` are pluginsynced, while\nvendored modules are not\n\n- *Default*: `/opt/puppetlabs/puppet/vendor_modules`\n\n### versioned_environment_dirs\n\nWhether or not to look for versioned environment directories,\nsymlinked from `$environmentpath/<environment>`. This is an experimental\nfeature and should be used with caution.\n\n- *Default*: `false`\n\n### waitforcert\n\nHow frequently puppet agent should ask for a signed certificate.\n\nWhen starting for the first time, puppet agent will submit a certificate\nsigning request (CSR) to the server named in the `ca_server` setting\n(usually the primary Puppet server); this may be autosigned, or may need to be\napproved by a human, depending on the CA server's configuration.\n\nPuppet agent cannot apply configurations until its approved certificate is\navailable. Since the certificate may or may not be available immediately,\npuppet agent will repeatedly try to fetch it at this interval. You can\nturn off waiting for certificates by specifying a time of 0, or a maximum\namount of time to wait in the `maxwaitforcert` setting, in which case\npuppet agent will exit if it cannot get a cert.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `2m`\n\n### waitforlock\n\nHow frequently puppet agent should try running when there is an\nalready ongoing puppet agent instance.\n\nThis argument is by default disabled (value set to 0). In this case puppet agent will\nimmediately exit if it cannot run at that moment. When a value other than 0 is set, this\ncan also be used in combination with the `maxwaitforlock` argument.\nThis setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).\n\n- *Default*: `0`\n\n### write_catalog_summary\n\nWhether to write the `classfile` and `resourcefile` after applying\nthe catalog. It is enabled by default, except when running `puppet apply`.\n\n- *Default*: `true`\n\n### yamldir\n\nThe directory in which YAML data is stored, usually in a subdirectory.\n\n- *Default*: `$vardir/yaml`\n\n"
  },
  {
    "path": "references/function.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Built-in function reference\ncanonical: \"/puppet/latest/function.html\"\ntoc_levels: 2\ntoc: columns\n---\n\n# Built-in function reference\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:36 +0000\n\n\n\nThis page is a list of Puppet's built-in functions, with descriptions of what they do and how to use them.\n\nFunctions are plugins you can call during catalog compilation. A call to any function is an expression that resolves to a value. For more information on how to call functions, see [the language reference page about function calls.](lang_functions.dita) \n\nMany of these function descriptions include auto-detected _signatures,_ which are short reminders of the function's allowed arguments. These signatures aren't identical to the syntax you use to call the function; instead, they resemble a parameter list from a Puppet [class](lang_classes.dita), [defined resource type](lang_defined_types.dita), [function](lang_write_functions_in_puppet.dita), or [lambda](lang_lambdas.dita). The syntax of a signature is:\n\n```\n<FUNCTION NAME>(<DATA TYPE> <ARGUMENT NAME>, ...)\n```\n\nThe `<DATA TYPE>` is a [Puppet data type value](lang_data_type.dita), like `String` or `Optional[Array[String]]`. The `<ARGUMENT NAME>` is a descriptive name chosen by the function's author to indicate what the argument is used for.\n\n* Any arguments with an `Optional` data type can be omitted from the function call.\n* Arguments that start with an asterisk (like `*$values`) can be repeated any number of times.\n* Arguments that start with an ampersand (like `&$block`) aren't normal arguments; they represent a code block, provided with [Puppet's lambda syntax.](lang_lambdas.dita)\n\n## `undef` values in Puppet 6\n\nIn Puppet 6, many Puppet types were moved out of the Puppet codebase, and into modules on the Puppet Forge. The new functions handle `undef` values more strictly than their stdlib counterparts. In Puppet 6, code that relies on `undef` values being implicitly treated as other types will return an evaluation error. For more information on which types were moved into modules, see the [Puppet 6 release notes](https://puppet.com/docs/puppet/6.0/release_notes_puppet.html#select-types-moved-to-modules).\n\n\n## `abs`\n\nReturns the absolute value of a Numeric value, for example -34.56 becomes\n34.56. Takes a single `Integer` or `Float` value as an argument.\n\n*Deprecated behavior*\n\nFor backwards compatibility reasons this function also works when given a\nnumber in `String` format such that it first attempts to covert it to either a `Float` or\nan `Integer` and then taking the absolute value of the result. Only strings representing\na number in decimal format is supported - an error is raised if\nvalue is not decimal (using base 10). Leading 0 chars in the string\nare ignored. A floating point value in string form can use some forms of\nscientific notation but not all.\n\nCallers should convert strings to `Numeric` before calling\nthis function to have full control over the conversion.\n\n```puppet\nabs(Numeric($str_val))\n```\n\nIt is worth noting that `Numeric` can convert to absolute value\ndirectly as in the following examples:\n\n```puppet\nNumeric($strval, true)     # Converts to absolute Integer or Float\nInteger($strval, 10, true) # Converts to absolute Integer using base 10 (decimal)\nInteger($strval, 16, true) # Converts to absolute Integer using base 16 (hex)\nFloat($strval, true)       # Converts to absolute Float\n```\n\n\nSignature 1\n\n`abs(Numeric $val)`\n\nSignature 2\n\n`abs(String $val)`\n\n## `alert`\n\nLogs a message on the server at level `alert`.\n\n\n`alert(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `all`\n\nRuns a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nrepeatedly using each value in a data structure until the lambda returns a non \"truthy\" value which\nmakes the function return `false`, or if the end of the iteration is reached, `true` is returned.\n\nThis function takes two mandatory arguments, in this order:\n\n1. An array, hash, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It can\nrequest one or two parameters.\n\n`$data.all |$parameter| { <PUPPET CODE BLOCK> }`\n\nor\n\n`all($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\n```puppet\n# For the array $data, run a lambda that checks that all values are multiples of 10\n$data = [10, 20, 30]\nnotice $data.all |$item| { $item % 10 == 0 }\n```\n\nWould notice `true`.\n\nWhen the first argument is a `Hash`, Puppet passes each key and value pair to the lambda\nas an array in the form `[key, value]`.\n\n```puppet\n# For the hash $data, run a lambda using each item as a key-value array\n$data = { 'a_0'=> 10, 'b_1' => 20 }\nnotice $data.all |$item| { $item[1] % 10 == 0  }\n```\n\nWould notice `true` if all values in the hash are multiples of 10.\n\nWhen the lambda accepts two arguments, the first argument gets the index in an array\nor the key from a hash, and the second argument the value.\n\n\n```puppet\n# Check that all values are a multiple of 10 and keys start with 'abc'\n$data = {abc_123 => 10, abc_42 => 20, abc_blue => 30}\nnotice $data.all |$key, $value| { $value % 10 == 0  and $key =~ /^abc/ }\n```\n\nWould notice `true`.\n\nFor an general examples that demonstrates iteration, see the Puppet\n[iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\ndocumentation.\n\n\nSignature 1\n\n`all(Hash[Any, Any] $hash, Callable[2,2] &$block)`\n\nSignature 2\n\n`all(Hash[Any, Any] $hash, Callable[1,1] &$block)`\n\nSignature 3\n\n`all(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 4\n\n`all(Iterable $enumerable, Callable[1,1] &$block)`\n\n## `annotate`\n\nHandles annotations on objects. The function can be used in four different ways.\n\nWith two arguments, an `Annotation` type and an object, the function returns the annotation\nfor the object of the given type, or `undef` if no such annotation exists.\n\n```puppet\n$annotation = Mod::NickNameAdapter.annotate(o)\n\n$annotation = annotate(Mod::NickNameAdapter.annotate, o)\n```\n\nWith three arguments, an `Annotation` type, an object, and a block, the function returns the\nannotation for the object of the given type, or annotates it with a new annotation initialized\nfrom the hash returned by the given block when no such annotation exists. The block will not\nbe called when an annotation of the given type is already present.\n\n```puppet\n$annotation = Mod::NickNameAdapter.annotate(o) || { { 'nick_name' => 'Buddy' } }\n\n$annotation = annotate(Mod::NickNameAdapter.annotate, o) || { { 'nick_name' => 'Buddy' } }\n```\n\nWith three arguments, an `Annotation` type, an object, and an `Hash`, the function will annotate\nthe given object with a new annotation of the given type that is initialized from the given hash.\nAn existing annotation of the given type is discarded.\n\n```puppet\n$annotation = Mod::NickNameAdapter.annotate(o, { 'nick_name' => 'Buddy' })\n\n$annotation = annotate(Mod::NickNameAdapter.annotate, o, { 'nick_name' => 'Buddy' })\n```\n\nWith three arguments, an `Annotation` type, an object, and an the string `clear`, the function will\nclear the annotation of the given type in the given object. The old annotation is returned if\nit existed.\n\n```puppet\n$annotation = Mod::NickNameAdapter.annotate(o, clear)\n\n$annotation = annotate(Mod::NickNameAdapter.annotate, o, clear)\n```\n\nWith three arguments, the type `Pcore`, an object, and a Hash of hashes keyed by `Annotation` types,\nthe function will annotate the given object with all types used as keys in the given hash. Each annotation\nis initialized with the nested hash for the respective type. The annotated object is returned.\n\n```puppet\n  $person = Pcore.annotate(Mod::Person({'name' => 'William'}), {\n    Mod::NickNameAdapter >= { 'nick_name' => 'Bill' },\n    Mod::HobbiesAdapter => { 'hobbies' => ['Ham Radio', 'Philatelist'] }\n  })\n```\n\n\nSignature 1\n\n`annotate(Type[Annotation] $type, Any $value, Optional[Callable[0, 0]] &$block)`\n\nSignature 2\n\n`annotate(Type[Annotation] $type, Any $value, Variant[Enum[clear],Hash[Pcore::MemberName,Any]] $annotation_hash)`\n\nSignature 3\n\n`annotate(Type[Pcore] $type, Any $value, Hash[Type[Annotation], Hash[Pcore::MemberName,Any]] $annotations)`\n\n## `any`\n\nRuns a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nrepeatedly using each value in a data structure until the lambda returns a \"truthy\" value which\nmakes the function return `true`, or if the end of the iteration is reached, false is returned.\n\nThis function takes two mandatory arguments, in this order:\n\n1. An array, hash, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It can\nrequest one or two parameters.\n\n`$data.any |$parameter| { <PUPPET CODE BLOCK> }`\n\nor\n\n`any($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\n```puppet\n# For the array $data, run a lambda that checks if an unknown hash contains those keys\n$data = [\"routers\", \"servers\", \"workstations\"]\n$looked_up = lookup('somekey', Hash)\nnotice $data.any |$item| { $looked_up[$item] }\n```\n\nWould notice `true` if the looked up hash had a value that is neither `false` nor `undef` for at least\none of the keys. That is, it is equivalent to the expression\n`$looked_up[routers] || $looked_up[servers] || $looked_up[workstations]`.\n\nWhen the first argument is a `Hash`, Puppet passes each key and value pair to the lambda\nas an array in the form `[key, value]`.\n\n```puppet\n# For the hash $data, run a lambda using each item as a key-value array.\n$data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n$looked_up = lookup('somekey', Hash)\nnotice $data.any |$item| { $looked_up[$item[0]] }\n```\n\nWould notice `true` if the looked up hash had a value for one of the wanted key that is\nneither `false` nor `undef`.\n\nWhen the lambda accepts two arguments, the first argument gets the index in an array\nor the key from a hash, and the second argument the value.\n\n\n```puppet\n# Check if there is an even numbered index that has a non String value\n$data = [key1, 1, 2, 2]\nnotice $data.any |$index, $value| { $index % 2 == 0 and $value !~ String }\n```\n\nWould notice true as the index `2` is even and not a `String`\n\nFor an general examples that demonstrates iteration, see the Puppet\n[iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\ndocumentation.\n\n\nSignature 1\n\n`any(Hash[Any, Any] $hash, Callable[2,2] &$block)`\n\nSignature 2\n\n`any(Hash[Any, Any] $hash, Callable[1,1] &$block)`\n\nSignature 3\n\n`any(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 4\n\n`any(Iterable $enumerable, Callable[1,1] &$block)`\n\n## `assert_type`\n\nReturns the given value if it is of the given\n[data type](https://puppet.com/docs/puppet/latest/lang_data.html), or\notherwise either raises an error or executes an optional two-parameter\n[lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html).\n\nThe function takes two mandatory arguments, in this order:\n\n1. The expected data type.\n2. A value to compare against the expected data type.\n\n```puppet\n$raw_username = 'Amy Berry'\n\n# Assert that $raw_username is a non-empty string and assign it to $valid_username.\n$valid_username = assert_type(String[1], $raw_username)\n\n# $valid_username contains \"Amy Berry\".\n# If $raw_username was an empty string or a different data type, the Puppet run would\n# fail with an \"Expected type does not match actual\" error.\n```\n\nYou can use an optional lambda to provide enhanced feedback. The lambda takes two\nmandatory parameters, in this order:\n\n1. The expected data type as described in the function's first argument.\n2. The actual data type of the value.\n\n```puppet\n$raw_username = 'Amy Berry'\n\n# Assert that $raw_username is a non-empty string and assign it to $valid_username.\n# If it isn't, output a warning describing the problem and use a default value.\n$valid_username = assert_type(String[1], $raw_username) |$expected, $actual| {\n  warning( \"The username should be \\'${expected}\\', not \\'${actual}\\'. Using 'anonymous'.\" )\n  'anonymous'\n}\n\n# $valid_username contains \"Amy Berry\".\n# If $raw_username was an empty string, the Puppet run would set $valid_username to\n# \"anonymous\" and output a warning: \"The username should be 'String[1, default]', not\n# 'String[0, 0]'. Using 'anonymous'.\"\n```\n\nFor more information about data types, see the\n[documentation](https://puppet.com/docs/puppet/latest/lang_data.html).\n\n\nSignature 1\n\n`assert_type(Type $type, Any $value, Optional[Callable[Type, Type]] &$block)`\n\nSignature 2\n\n`assert_type(String $type_string, Any $value, Optional[Callable[Type, Type]] &$block)`\n\n## `binary_file`\n\nLoads a binary file from a module or file system and returns its contents as a `Binary`.\nThe argument to this function should be a `<MODULE NAME>/<FILE>`\nreference, which will load `<FILE>` from a module's `files`\ndirectory. (For example, the reference `mysql/mysqltuner.pl` will load the\nfile `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n\nThis function also accepts an absolute file path that allows reading\nbinary file content from anywhere on disk.\n\nAn error is raised if the given file does not exists.\n\nTo search for the existence of files, use the `find_file()` function.\n\n- since 4.8.0\n\n\n`binary_file(String $path)`\n\n## `break`\n\nBreaks an innermost iteration as if it encountered an end of input.\nThis function does not return to the caller.\n\nThe signal produced to stop the iteration bubbles up through\nthe call stack until either terminating the innermost iteration or\nraising an error if the end of the call stack is reached.\n\nThe break() function does not accept an argument.\n\n```puppet\n$data = [1,2,3]\nnotice $data.map |$x| { if $x == 3 { break() } $x*10 }\n```\n\nWould notice the value `[10, 20]`\n\n```puppet\nfunction break_if_even($x) {\n  if $x % 2 == 0 { break() }\n}\n$data = [1,2,3]\nnotice $data.map |$x| { break_if_even($x); $x*10 }\n```\nWould notice the value `[10]`\n\n* Also see functions `next` and `return`\n\n\n`break()`\n\n## `call`\n\nCalls an arbitrary Puppet function by name.\n\nThis function takes one mandatory argument and one or more optional arguments:\n\n1. A string corresponding to a function name.\n2. Any number of arguments to be passed to the called function.\n3. An optional lambda, if the function being called supports it.\n\nThis function can also be used to resolve a `Deferred` given as\nthe only argument to the function (does not accept arguments nor\na block).\n\n```puppet\n$a = 'notice'\ncall($a, 'message')\n```\n\n```puppet\n$a = 'each'\n$b = [1,2,3]\ncall($a, $b) |$item| {\n notify { $item: }\n}\n```\n\nThe `call` function can be used to call either Ruby functions or Puppet language\nfunctions.\n\nWhen used with `Deferred` values, the deferred value can either describe\na function call, or a dig into a variable.\n\n```puppet\n$d = Deferred('join', [[1,2,3], ':']) # A future call to join that joins the arguments 1,2,3 with ':'\nnotice($d.call())\n```\n\nWould notice the string \"1:2:3\".\n\n```puppet\n$d = Deferred('$facts', ['processors', 'count'])\nnotice($d.call())\n```\n\nWould notice the value of `$facts['processors']['count']` at the time when the `call` is made.\n\n* Deferred values supported since Puppet 6.0\n\n\nSignature 1\n\n`call(String $function_name, Any *$arguments, Optional[Callable] &$block)`\n\nSignature 2\n\n`call(Deferred $deferred)`\n\n## `camelcase`\n\nCreates a Camel Case version of a String\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String` the conversion replaces all combinations of `*_<char>*` with an upcased version of the\n  character following the _.  This is done using Ruby system locale which handles some, but not all\n  special international up-casing rules (for example German double-s ß is upcased to \"Ss\").\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is capitalized and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n* The result will not contain any underscore characters.\n\nPlease note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\nTo ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n\n```puppet\n'hello_friend'.camelcase()\ncamelcase('hello_friend')\n```\nWould both result in `\"HelloFriend\"`\n\n```puppet\n['abc_def', 'bcd_xyz'].camelcase()\ncamelcase(['abc_def', 'bcd_xyz'])\n```\nWould both result in `['AbcDef', 'BcdXyz']`\n\n\nSignature 1\n\n`camelcase(Numeric $arg)`\n\nSignature 2\n\n`camelcase(String $arg)`\n\nSignature 3\n\n`camelcase(Iterable[Variant[String, Numeric]] $arg)`\n\n## `capitalize`\n\nCapitalizes the first character of a String, or the first character of every String in an Iterable value (such as an Array).\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String`, a string is returned in which the first character is uppercase.\n  This is done using Ruby system locale which handles some, but not all\n  special international up-casing rules (for example German double-s ß is capitalized to \"Ss\").\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is capitalized and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\nPlease note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\nTo ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n\n```puppet\n'hello'.capitalize()\ncapitalize('hello')\n```\nWould both result in `\"Hello\"`\n\n```puppet\n['abc', 'bcd'].capitalize()\ncapitalize(['abc', 'bcd'])\n```\nWould both result in `['Abc', 'Bcd']`\n\n\nSignature 1\n\n`capitalize(Numeric $arg)`\n\nSignature 2\n\n`capitalize(String $arg)`\n\nSignature 3\n\n`capitalize(Iterable[Variant[String, Numeric]] $arg)`\n\n## `ceiling`\n\nReturns the smallest `Integer` greater or equal to the argument.\nTakes a single numeric value as an argument.\n\nThis function is backwards compatible with the same function in stdlib\nand accepts a `Numeric` value. A `String` that can be converted\nto a floating point number can also be used in this version - but this\nis deprecated.\n\nIn general convert string input to `Numeric` before calling this function\nto have full control over how the conversion is done.\n\n\nSignature 1\n\n`ceiling(Numeric $val)`\n\nSignature 2\n\n`ceiling(String $val)`\n\n## `chomp`\n\nReturns a new string with the record separator character(s) removed.\nThe record separator is the line ending characters `\\r` and `\\n`.\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String` the conversion removes `\\r\\n`, `\\n` or `\\r` from the end of a string.\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\n```puppet\n\"hello\\r\\n\".chomp()\nchomp(\"hello\\r\\n\")\n```\nWould both result in `\"hello\"`\n\n```puppet\n[\"hello\\r\\n\", \"hi\\r\\n\"].chomp()\nchomp([\"hello\\r\\n\", \"hi\\r\\n\"])\n```\nWould both result in `['hello', 'hi']`\n\n\nSignature 1\n\n`chomp(Numeric $arg)`\n\nSignature 2\n\n`chomp(String $arg)`\n\nSignature 3\n\n`chomp(Iterable[Variant[String, Numeric]] $arg)`\n\n## `chop`\n\nReturns a new string with the last character removed.\nIf the string ends with `\\r\\n`, both characters are removed. Applying chop to an empty\nstring returns an empty string. If you wish to merely remove record\nseparators then you should use the `chomp` function.\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String` the conversion removes the last character, or if it ends with \\r\\n` it removes both. If String is empty\n  an empty string is returned.\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\n```puppet\n\"hello\\r\\n\".chop()\nchop(\"hello\\r\\n\")\n```\nWould both result in `\"hello\"`\n\n```puppet\n\"hello\".chop()\nchop(\"hello\")\n```\nWould both result in `\"hell\"`\n\n```puppet\n[\"hello\\r\\n\", \"hi\\r\\n\"].chop()\nchop([\"hello\\r\\n\", \"hi\\r\\n\"])\n```\nWould both result in `['hello', 'hi']`\n\n\nSignature 1\n\n`chop(Numeric $arg)`\n\nSignature 2\n\n`chop(String $arg)`\n\nSignature 3\n\n`chop(Iterable[Variant[String, Numeric]] $arg)`\n\n## `compare`\n\nCompares two values and returns -1, 0 or 1 if first value is smaller, equal or larger than the second value.\nThe compare function accepts arguments of the data types `String`, `Numeric`, `Timespan`, `Timestamp`, and `Semver`,\nsuch that:\n\n* two of the same data type can be compared\n* `Timespan` and `Timestamp` can be compared with each other and with `Numeric`\n\nWhen comparing two `String` values the comparison can be made to consider case by passing a third (optional)\nboolean `false` value - the default is `true` which ignores case as the comparison operators\nin the Puppet Language.\n\n\nSignature 1\n\n`compare(Numeric $a, Numeric $b)`\n\nSignature 2\n\n`compare(String $a, String $b, Optional[Boolean] $ignore_case)`\n\nSignature 3\n\n`compare(Semver $a, Semver $b)`\n\nSignature 4\n\n`compare(Numeric $a, Variant[Timespan, Timestamp] $b)`\n\nSignature 5\n\n`compare(Timestamp $a, Variant[Timestamp, Numeric] $b)`\n\nSignature 6\n\n`compare(Timespan $a, Variant[Timespan, Numeric] $b)`\n\n## `contain`\n\nMakes one or more classes be contained inside the current class.\nIf any of these classes are undeclared, they will be declared as if\nthere were declared with the `include` function.\nAccepts a class name, an array of class names, or a comma-separated\nlist of class names.\n\nA contained class will not be applied before the containing class is\nbegun, and will be finished before the containing class is finished.\n\nYou must use the class's full name;\nrelative names are not allowed. In addition to names in string form,\nyou may also directly use `Class` and `Resource` `Type`-values that are produced by\nevaluating resource and relationship expressions.\n\nThe function returns an array of references to the classes that were contained thus\nallowing the function call to `contain` to directly continue.\n\n- Since 4.0.0 support for `Class` and `Resource` `Type`-values, absolute names\n- Since 4.7.0 a value of type `Array[Type[Class[n]]]` is returned with all the contained classes\n\n\n`contain(Any *$names)`\n\n## `convert_to`\n\nThe `convert_to(value, type)` is a convenience function that does the same as `new(type, value)`.\nThe difference in the argument ordering allows it to be used in chained style for\nimproved readability \"left to right\".\n\nWhen the function is given a lambda, it is called with the converted value, and the function\nreturns what the lambda returns, otherwise the converted value.\n\n```puppet\n  # The harder to read variant:\n  # Using new operator - that is \"calling the type\" with operator ()\n  Hash(Array(\"abc\").map |$i,$v| { [$i, $v] })\n\n  # The easier to read variant:\n  # using 'convert_to'\n  \"abc\".convert_to(Array).map |$i,$v| { [$i, $v] }.convert_to(Hash)\n```\n\n\n`convert_to(Any $value, Type $type, Optional[Any] *$args, Optional[Callable[1,1]] &$block)`\n\n## `create_resources`\n\nConverts a hash into a set of resources and adds them to the catalog.\n\n**Note**: Use this function selectively. It's generally better to write resources in\n [Puppet](https://puppet.com/docs/puppet/latest/lang_resources.html), as\n resources created with `create_resource` are difficult to read and troubleshoot.\n\nThis function takes two mandatory arguments: a resource type, and a hash describing\na set of resources. The hash should be in the form `{title => {parameters} }`:\n\n    # A hash of user resources:\n    $myusers = {\n      'nick' => { uid    => '1330',\n                  gid    => allstaff,\n                  groups => ['developers', 'operations', 'release'], },\n      'dan'  => { uid    => '1308',\n                  gid    => allstaff,\n                  groups => ['developers', 'prosvc', 'release'], },\n    }\n\n    create_resources(user, $myusers)\n\nA third, optional parameter may be given, also as a hash:\n\n    $defaults = {\n      'ensure'   => present,\n      'provider' => 'ldap',\n    }\n\n    create_resources(user, $myusers, $defaults)\n\nThe values given on the third argument are added to the parameters of each resource\npresent in the set given on the second argument. If a parameter is present on both\nthe second and third arguments, the one on the second argument takes precedence.\n\nThis function can be used to create defined resources and classes, as well\nas native resources.\n\nVirtual and Exported resources may be created by prefixing the type name\nwith @ or @@ respectively. For example, the $myusers hash may be exported\nin the following manner:\n\n    create_resources(\"@@user\", $myusers)\n\nThe $myusers may be declared as virtual resources using:\n\n    create_resources(\"@user\", $myusers)\n\nNote that `create_resources` filters out parameter values that are `undef` so that normal\ndata binding and Puppet default value expressions are considered (in that order) for the\nfinal value of a parameter (just as when setting a parameter to `undef` in a Puppet language\nresource declaration).\n\n\n`create_resources()`\n\n## `crit`\n\nLogs a message on the server at level `crit`.\n\n\n`crit(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `debug`\n\nLogs a message on the server at level `debug`.\n\n\n`debug(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `defined`\n\nDetermines whether a given class or resource type is defined and returns a Boolean\nvalue. You can also use `defined` to determine whether a specific resource is defined,\nor whether a variable has a value (including `undef`, as opposed to the variable never\nbeing declared or assigned).\n\nThis function takes at least one string argument, which can be a class name, type name,\nresource reference, or variable reference of the form `'$name'`. (Note that the `$` sign\nis included in the string which must be in single quotes to prevent the `$` character\nto be interpreted as interpolation.\n\nThe `defined` function checks both native and defined types, including types\nprovided by modules. Types and classes are matched by their names. The function matches\nresource declarations by using resource references.\n\n```puppet\n# Matching resource types\ndefined(\"file\")\ndefined(\"customtype\")\n\n# Matching defines and classes\ndefined(\"foo\")\ndefined(\"foo::bar\")\n\n# Matching variables (note the single quotes)\ndefined('$name')\n\n# Matching declared resources\ndefined(File['/tmp/file'])\n```\n\nPuppet depends on the configuration's evaluation order when checking whether a resource\nis declared.\n\n```puppet\n# Assign values to $is_defined_before and $is_defined_after using identical `defined`\n# functions.\n\n$is_defined_before = defined(File['/tmp/file'])\n\nfile { \"/tmp/file\":\n  ensure => present,\n}\n\n$is_defined_after = defined(File['/tmp/file'])\n\n# $is_defined_before returns false, but $is_defined_after returns true.\n```\n\nThis order requirement only refers to evaluation order. The order of resources in the\nconfiguration graph (e.g. with `before` or `require`) does not affect the `defined`\nfunction's behavior.\n\n> **Warning:** Avoid relying on the result of the `defined` function in modules, as you\n> might not be able to guarantee the evaluation order well enough to produce consistent\n> results. This can cause other code that relies on the function's result to behave\n> inconsistently or fail.\n\nIf you pass more than one argument to `defined`, the function returns `true` if _any_\nof the arguments are defined. You can also match resources by type, allowing you to\nmatch conditions of different levels of specificity, such as whether a specific resource\nis of a specific data type.\n\n```puppet\nfile { \"/tmp/file1\":\n  ensure => file,\n}\n\n$tmp_file = file { \"/tmp/file2\":\n  ensure => file,\n}\n\n# Each of these statements return `true` ...\ndefined(File['/tmp/file1'])\ndefined(File['/tmp/file1'],File['/tmp/file2'])\ndefined(File['/tmp/file1'],File['/tmp/file2'],File['/tmp/file3'])\n# ... but this returns `false`.\ndefined(File['/tmp/file3'])\n\n# Each of these statements returns `true` ...\ndefined(Type[Resource['file','/tmp/file2']])\ndefined(Resource['file','/tmp/file2'])\ndefined(File['/tmp/file2'])\ndefined('$tmp_file')\n# ... but each of these returns `false`.\ndefined(Type[Resource['exec','/tmp/file2']])\ndefined(Resource['exec','/tmp/file2'])\ndefined(File['/tmp/file3'])\ndefined('$tmp_file2')\n```\n\n\n`defined(Variant[String, Type[CatalogEntry], Type[Type[CatalogEntry]]] *$vals)`\n\n## `dig`\n\nReturns a value for a sequence of given keys/indexes into a structure, such as\nan array or hash.\n\nThis function is used to \"dig into\" a complex data structure by\nusing a sequence of keys / indexes to access a value from which\nthe next key/index is accessed recursively.\n\nThe first encountered `undef` value or key stops the \"dig\" and `undef` is returned.\n\nAn error is raised if an attempt is made to \"dig\" into\nsomething other than an `undef` (which immediately returns `undef`), an `Array` or a `Hash`.\n\n```puppet\n$data = {a => { b => [{x => 10, y => 20}, {x => 100, y => 200}]}}\nnotice $data.dig('a', 'b', 1, 'x')\n```\n\nWould notice the value 100.\n\nThis is roughly equivalent to `$data['a']['b'][1]['x']`. However, a standard\nindex will return an error and cause catalog compilation failure if any parent\nof the final key (`'x'`) is `undef`. The `dig` function will return `undef`,\nrather than failing catalog compilation. This allows you to check if data\nexists in a structure without mandating that it always exists.\n\n\n`dig(Optional[Collection] $data, Any *$arg)`\n\n## `digest`\n\nReturns a hash value from a provided string using the digest_algorithm setting from the Puppet config file.\n\n\n`digest()`\n\n## `downcase`\n\nConverts a String, Array or Hash (recursively) into lower case.\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String`, its lower case version is returned. This is done using Ruby system locale which handles some, but not all\n  special international up-casing rules (for example German double-s ß is upcased to \"SS\", whereas upper case double-s\n  is downcased to ß).\n* For `Array` and `Hash` the conversion to lower case is recursive and each key and value must be convertible by\n  this function.\n* When a `Hash` is converted, some keys could result in the same key - in those cases, the\n  latest key-value wins. For example if keys \"aBC\", and \"abC\" where both present, after downcase there would only be one\n  key \"abc\".\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\nPlease note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\nTo ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n\n```puppet\n'HELLO'.downcase()\ndowncase('HEllO')\n```\nWould both result in `\"hello\"`\n\n```puppet\n['A', 'B'].downcase()\ndowncase(['A', 'B'])\n```\nWould both result in `['a', 'b']`\n\n```puppet\n{'A' => 'HEllO', 'B' => 'GOODBYE'}.downcase()\n```\nWould result in `{'a' => 'hello', 'b' => 'goodbye'}`\n\n```puppet\n['A', 'B', ['C', ['D']], {'X' => 'Y'}].downcase\n```\nWould result in `['a', 'b', ['c', ['d']], {'x' => 'y'}]`\n\n\nSignature 1\n\n`downcase(Numeric $arg)`\n\nSignature 2\n\n`downcase(String $arg)`\n\nSignature 3\n\n`downcase(Array[StringData] $arg)`\n\nSignature 4\n\n`downcase(Hash[StringData, StringData] $arg)`\n\n## `each`\n\nRuns a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nrepeatedly using each value in a data structure, then returns the values unchanged.\n\nThis function takes two mandatory arguments, in this order:\n\n1. An array, hash, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It can\nrequest one or two parameters.\n\n`$data.each |$parameter| { <PUPPET CODE BLOCK> }`\n\nor\n\n`each($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\nWhen the first argument (`$data` in the above example) is an array, Puppet passes each\nvalue in turn to the lambda, then returns the original values.\n\n```puppet\n# For the array $data, run a lambda that creates a resource for each item.\n$data = [\"routers\", \"servers\", \"workstations\"]\n$data.each |$item| {\n notify { $item:\n   message => $item\n }\n}\n# Puppet creates one resource for each of the three items in $data. Each resource is\n# named after the item's value and uses the item's value in a parameter.\n```\n\nWhen the first argument is a hash, Puppet passes each key and value pair to the lambda\nas an array in the form `[key, value]` and returns the original hash.\n\n```puppet\n# For the hash $data, run a lambda using each item as a key-value array that creates a\n# resource for each item.\n$data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n$data.each |$items| {\n notify { $items[0]:\n   message => $items[1]\n }\n}\n# Puppet creates one resource for each of the three items in $data, each named after the\n# item's key and containing a parameter using the item's value.\n```\n\nWhen the first argument is an array and the lambda has two parameters, Puppet passes the\narray's indexes (enumerated from 0) in the first parameter and its values in the second\nparameter.\n\n```puppet\n# For the array $data, run a lambda using each item's index and value that creates a\n# resource for each item.\n$data = [\"routers\", \"servers\", \"workstations\"]\n$data.each |$index, $value| {\n notify { $value:\n   message => $index\n }\n}\n# Puppet creates one resource for each of the three items in $data, each named after the\n# item's value and containing a parameter using the item's index.\n```\n\nWhen the first argument is a hash, Puppet passes its keys to the first parameter and its\nvalues to the second parameter.\n\n```puppet\n# For the hash $data, run a lambda using each item's key and value to create a resource\n# for each item.\n$data = {\"rtr\" => \"Router\", \"svr\" => \"Server\", \"wks\" => \"Workstation\"}\n$data.each |$key, $value| {\n notify { $key:\n   message => $value\n }\n}\n# Puppet creates one resource for each of the three items in $data, each named after the\n# item's key and containing a parameter using the item's value.\n```\n\nFor an example that demonstrates how to create multiple `file` resources using `each`,\nsee the Puppet\n[iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\ndocumentation.\n\n\nSignature 1\n\n`each(Hash[Any, Any] $hash, Callable[2,2] &$block)`\n\nSignature 2\n\n`each(Hash[Any, Any] $hash, Callable[1,1] &$block)`\n\nSignature 3\n\n`each(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 4\n\n`each(Iterable $enumerable, Callable[1,1] &$block)`\n\n## `emerg`\n\nLogs a message on the server at level `emerg`.\n\n\n`emerg(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `empty`\n\nReturns `true` if the given argument is an empty collection of values.\n\nThis function can answer if one of the following is empty:\n* `Array`, `Hash` - having zero entries\n* `String`, `Binary` - having zero length\n\nFor backwards compatibility with the stdlib function with the same name the\nfollowing data types are also accepted by the function instead of raising an error.\nUsing these is deprecated and will raise a warning:\n\n* `Numeric` - `false` is returned for all `Numeric` values.\n* `Undef` - `true` is returned for all `Undef` values.\n\n```puppet\nnotice([].empty)\nnotice(empty([]))\n# would both notice 'true'\n```\n\n\nSignature 1\n\n`empty(Collection $coll)`\n\nSignature 2\n\n`empty(Sensitive[String] $str)`\n\nSignature 3\n\n`empty(String $str)`\n\nSignature 4\n\n`empty(Numeric $num)`\n\nSignature 5\n\n`empty(Binary $bin)`\n\nSignature 6\n\n`empty(Undef $x)`\n\n## `epp`\n\nEvaluates an Embedded Puppet (EPP) template file and returns the rendered text\nresult as a String.\n\n`epp('<MODULE NAME>/<TEMPLATE FILE>', <PARAMETER HASH>)`\n\nThe first argument to this function should be a `<MODULE NAME>/<TEMPLATE FILE>`\nreference, which loads `<TEMPLATE FILE>` from `<MODULE NAME>`'s `templates`\ndirectory. In most cases, the last argument is optional; if used, it should be a\n[hash](https://puppet.com/docs/puppet/latest/lang_data_hash.html) that contains parameters to\npass to the template.\n\n- See the [template](https://puppet.com/docs/puppet/latest/lang_template.html)\ndocumentation for general template usage information.\n- See the [EPP syntax](https://puppet.com/docs/puppet/latest/lang_template_epp.html)\ndocumentation for examples of EPP.\n\nFor example, to call the apache module's `templates/vhost/_docroot.epp`\ntemplate and pass the `docroot` and `virtual_docroot` parameters, call the `epp`\nfunction like this:\n\n`epp('apache/vhost/_docroot.epp', { 'docroot' => '/var/www/html',\n'virtual_docroot' => '/var/www/example' })`\n\nThis function can also accept an absolute path, which can load a template file\nfrom anywhere on disk.\n\nPuppet produces a syntax error if you pass more parameters than are declared in\nthe template's parameter tag. When passing parameters to a template that\ncontains a parameter tag, use the same names as the tag's declared parameters.\n\nParameters are required only if they are declared in the called template's\nparameter tag without default values. Puppet produces an error if the `epp`\nfunction fails to pass any required parameter.\n\n\n`epp(String $path, Optional[Hash[Pattern[/^\\w+$/], Any]] $parameters)`\n\n## `err`\n\nLogs a message on the server at level `err`.\n\n\n`err(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `eyaml_lookup_key`\n\nThe `eyaml_lookup_key` is a hiera 5 `lookup_key` data provider function.\nSee [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-hiera-eyaml) for\nhow to use this function.\n\n\n`eyaml_lookup_key(String[1] $key, Hash[String[1],Any] $options, Puppet::LookupContext $context)`\n\n## `fail`\n\nFail with a parse error. Any parameters will be stringified,\nconcatenated, and passed to the exception-handler.\n\n\n`fail()`\n\n## `file`\n\nLoads a file from a module and returns its contents as a string.\n\nThe argument to this function should be a `<MODULE NAME>/<FILE>`\nreference, which will load `<FILE>` from a module's `files`\ndirectory. (For example, the reference `mysql/mysqltuner.pl` will load the\nfile `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n\nThis function can also accept:\n\n* An absolute path, which can load a file from anywhere on disk.\n* Multiple arguments, which will return the contents of the **first** file\nfound, skipping any files that don't exist.\n\n\n`file()`\n\n## `filter`\n\nApplies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nto every value in a data structure and returns an array or hash containing any elements\nfor which the lambda evaluates to a truthy value (not `false` or `undef`).\n\nThis function takes two mandatory arguments, in this order:\n\n1. An array, hash, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It can\nrequest one or two parameters.\n\n`$filtered_data = $data.filter |$parameter| { <PUPPET CODE BLOCK> }`\n\nor\n\n`$filtered_data = filter($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\nWhen the first argument (`$data` in the above example) is an array, Puppet passes each\nvalue in turn to the lambda and returns an array containing the results.\n\n```puppet\n# For the array $data, return an array containing the values that end with \"berry\"\n$data = [\"orange\", \"blueberry\", \"raspberry\"]\n$filtered_data = $data.filter |$items| { $items =~ /berry$/ }\n# $filtered_data = [blueberry, raspberry]\n```\n\nWhen the first argument is a hash, Puppet passes each key and value pair to the lambda\nas an array in the form `[key, value]` and returns a hash containing the results.\n\n```puppet\n# For the hash $data, return a hash containing all values of keys that end with \"berry\"\n$data = { \"orange\" => 0, \"blueberry\" => 1, \"raspberry\" => 2 }\n$filtered_data = $data.filter |$items| { $items[0] =~ /berry$/ }\n# $filtered_data = {blueberry => 1, raspberry => 2}\n```\n\nWhen the first argument is an array and the lambda has two parameters, Puppet passes the\narray's indexes (enumerated from 0) in the first parameter and its values in the second\nparameter.\n\n```puppet\n# For the array $data, return an array of all keys that both end with \"berry\" and have\n# an even-numbered index\n$data = [\"orange\", \"blueberry\", \"raspberry\"]\n$filtered_data = $data.filter |$indexes, $values| { $indexes % 2 == 0 and $values =~ /berry$/ }\n# $filtered_data = [raspberry]\n```\n\nWhen the first argument is a hash, Puppet passes its keys to the first parameter and its\nvalues to the second parameter.\n\n```puppet\n# For the hash $data, return a hash of all keys that both end with \"berry\" and have\n# values less than or equal to 1\n$data = { \"orange\" => 0, \"blueberry\" => 1, \"raspberry\" => 2 }\n$filtered_data = $data.filter |$keys, $values| { $keys =~ /berry$/ and $values <= 1 }\n# $filtered_data = {blueberry => 1}\n```\n\n\nSignature 1\n\n`filter(Hash[Any, Any] $hash, Callable[2,2] &$block)`\n\nSignature 2\n\n`filter(Hash[Any, Any] $hash, Callable[1,1] &$block)`\n\nSignature 3\n\n`filter(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 4\n\n`filter(Iterable $enumerable, Callable[1,1] &$block)`\n\n## `find_file`\n\nFinds an existing file from a module and returns its path.\n\nThis function accepts an argument that is a String as a `<MODULE NAME>/<FILE>`\nreference, which searches for `<FILE>` relative to a module's `files`\ndirectory. (For example, the reference `mysql/mysqltuner.pl` will search for the\nfile `<MODULES DIRECTORY>/mysql/files/mysqltuner.pl`.)\n\nIf this function is run via puppet agent, it checks for file existence on the\nPuppet Primary server. If run via puppet apply, it checks on the local host.\nIn both cases, the check is performed before any resources are changed.\n\nThis function can also accept:\n\n* An absolute String path, which checks for the existence of a file from anywhere on disk.\n* Multiple String arguments, which returns the path of the **first** file\n  found, skipping nonexistent files.\n* An array of string paths, which returns the path of the **first** file\n  found from the given paths in the array, skipping nonexistent files.\n\nThe function returns `undef` if none of the given paths were found.\n\n\nSignature 1\n\n`find_file(String *$paths)`\n\nSignature 2\n\n`find_file(Array[String] *$paths_array)`\n\n## `find_template`\n\nFinds an existing template from a module and returns its path.\n\nThis function accepts an argument that is a String as a `<MODULE NAME>/<TEMPLATE>`\nreference, which searches for `<TEMPLATE>` relative to a module's `templates`\ndirectory on the primary server. (For example, the reference `mymod/secret.conf.epp`\nwill search for the file `<MODULES DIRECTORY>/mymod/templates/secret.conf.epp`.)\n\nThe primary use case is for agent-side template rendering with late-bound variables\nresolved, such as from secret stores inaccessible to the primary server, such as\n\n```\n$variables = {\n  'password' => Deferred('vault_lookup::lookup',\n                  ['secret/mymod', 'https://vault.example.com:8200']),\n}\n\n# compile the template source into the catalog\nfile { '/etc/secrets.conf':\n  ensure  => file,\n  content => Deferred('inline_epp',\n               [find_template('mymod/secret.conf.epp').file, $variables]),\n}\n```\n\n\n\nThis function can also accept:\n\n* An absolute String path, which checks for the existence of a template from anywhere on disk.\n* Multiple String arguments, which returns the path of the **first** template\n  found, skipping nonexistent files.\n* An array of string paths, which returns the path of the **first** template\n  found from the given paths in the array, skipping nonexistent files.\n\nThe function returns `undef` if none of the given paths were found.\n\n\nSignature 1\n\n`find_template(String *$paths)`\n\nSignature 2\n\n`find_template(Array[String] *$paths_array)`\n\n## `flatten`\n\nReturns a flat Array produced from its possibly deeply nested given arguments.\n\nOne or more arguments of any data type can be given to this function.\nThe result is always a flat array representation where any nested arrays are recursively flattened.\n\n```puppet\nflatten(['a', ['b', ['c']]])\n# Would return: ['a','b','c']\n```\n\nTo flatten other kinds of iterables (for example hashes, or intermediate results like from a `reverse_each`)\nfirst convert the result to an array using `Array($x)`, or `$x.convert_to(Array)`. See the `new` function\nfor details and options when performing a conversion.\n\n```puppet\n$hsh = { a => 1, b => 2}\n\n# -- without conversion\n$hsh.flatten()\n# Would return [{a => 1, b => 2}]\n\n# -- with conversion\n$hsh.convert_to(Array).flatten()\n# Would return [a,1,b,2]\n\nflatten(Array($hsh))\n# Would also return [a,1,b,2]\n```\n\n```puppet\n$a1 = [1, [2, 3]]\n$a2 = [[4,[5,6]]\n$x = 7\nflatten($a1, $a2, $x)\n# would return [1,2,3,4,5,6,7]\n```\n\n```puppet\nflatten(42)\n# Would return [42]\n\nflatten([42])\n# Would also return [42]\n```\n\n\n`flatten(Any *$args)`\n\n## `floor`\n\nReturns the largest `Integer` less or equal to the argument.\nTakes a single numeric value as an argument.\n\nThis function is backwards compatible with the same function in stdlib\nand accepts a `Numeric` value. A `String` that can be converted\nto a floating point number can also be used in this version - but this\nis deprecated.\n\nIn general convert string input to `Numeric` before calling this function\nto have full control over how the conversion is done.\n\n\nSignature 1\n\n`floor(Numeric $val)`\n\nSignature 2\n\n`floor(String $val)`\n\n## `fqdn_rand`\n\nUsage: `fqdn_rand(MAX, [SEED], [DOWNCASE])`. MAX is required and must be a positive\ninteger; SEED is optional and may be any number or string; DOWNCASE is optional\nand should be a boolean true or false.\n\nGenerates a random Integer number greater than or equal to 0 and less than MAX,\ncombining the `$fqdn` fact and the value of SEED for repeatable randomness.\n(That is, each node will get a different random number from this function, but\na given node's result will be the same every time unless its hostname changes.) If\nDOWNCASE is true, then the `fqdn` fact will be downcased when computing the value\nso that the result is not sensitive to the case of the `fqdn` fact.\n\nThis function is usually used for spacing out runs of resource-intensive cron\ntasks that run on many nodes, which could cause a thundering herd or degrade\nother services if they all fire at once. Adding a SEED can be useful when you\nhave more than one such task and need several unrelated random numbers per\nnode. (For example, `fqdn_rand(30)`, `fqdn_rand(30, 'expensive job 1')`, and\n`fqdn_rand(30, 'expensive job 2')` will produce totally different numbers.)\n\n\n`fqdn_rand()`\n\n## `generate`\n\nCalls an external command on the Puppet master and returns\nthe results of the command. Any arguments are passed to the external command as\narguments. If the generator does not exit with return code of 0,\nthe generator is considered to have failed and a parse error is\nthrown. Generators can only have file separators, alphanumerics, dashes,\nand periods in them. This function will attempt to protect you from\nmalicious generator calls (e.g., those with '..' in them), but it can\nnever be entirely safe. No subshell is used to execute\ngenerators, so all shell metacharacters are passed directly to\nthe generator, and all metacharacters are returned by the function.\nConsider cleaning white space from any string generated.\n\n\n`generate()`\n\n## `get`\n\nDigs into a value with dot notation to get a value from within a structure.\n\n**To dig into a given value**, call the function with (at least) two arguments:\n\n* The **first** argument must be an Array, or Hash. Value can also be `undef`\n  (which also makes the result `undef` unless a _default value_ is given).\n* The **second** argument must be a _dot notation navigation string_.\n* The **optional third** argument can be any type of value and it is used\n  as the _default value_ if the function would otherwise return `undef`.\n* An **optional lambda** for error handling taking one `Error` argument.\n\n**Dot notation navigation string** -\nThe dot string consists of period `.` separated segments where each\nsegment is either the index into an array or the value of a hash key.\nIf a wanted key contains a period it must be quoted to avoid it being\ntaken as a segment separator. Quoting can be done with either\nsingle quotes `'` or double quotes `\"`. If a segment is\na decimal number it is converted to an Integer index. This conversion\ncan be prevented by quoting the value.\n\n```puppet\n#get($facts, 'os.family')\n$facts.get('os.family')\n```\nWould both result in the value of `$facts['os']['family']`\n\n```puppet\nget([1,2,[{'name' =>'waldo'}]], '2.0.name')\n```\nWould result in `'waldo'`\n\n```puppet\nget([1,2,[{'name' =>'waldo'}]], '2.1.name', 'not waldo')\n\n```\nWould result in `'not waldo'`\n\n```puppet\n$x = [1, 2, { 'readme.md' => \"This is a readme.\"}]\n$x.get('2.\"readme.md\"')\n```\n\n```puppet\n$x = [1, 2, { '10' => \"ten\"}]\n$x.get('2.\"0\"')\n```\n\n**Error Handling** - There are two types of common errors that can\nbe handled by giving the function a code block to execute.\n(A third kind or error; when the navigation string has syntax errors\n(for example an empty segment or unbalanced quotes) will always raise\nan error).\n\nThe given block will be given an instance of the `Error` data type,\nand it has methods to extract `msg`, `issue_code`, `kind`, and\n`details`.\n\nThe `msg` will be a preformatted message describing the error.\nThis is the error message that would have surfaced if there was\nno block to handle the error.\n\nThe `kind` is the string `'SLICE_ERROR'` for both kinds of errors,\nand the `issue_code` is either the string `'EXPECTED_INTEGER_INDEX'`\nfor an attempt to index into an array with a String,\nor `'EXPECTED_COLLECTION'` for an attempt to index into something that\nis not a Collection.\n\nThe `details` is a Hash that for both issue codes contain the\nentry `'walked_path'` which is an Array with each key in the\nprogression of the dig up to the place where the error occurred.\n\nFor an `EXPECTED_INTEGER_INDEX`-issue the detail `'index_type'` is\nset to the data type of the index value and for an\n`'EXPECTED_COLLECTION'`-issue the detail `'value_type'` is set\nto the type of the value.\n\nThe logic in the error handling block can inspect the details,\nand either call `fail()` with a custom error message or produce\nthe wanted value.\n\nIf the block produces `undef` it will not be replaced with a\ngiven default value.\n\n```puppet\n$x = 'blue'\n$x.get('0.color', 'green') |$error| { undef } # result is undef\n\n$y = ['blue']\n$y.get('color', 'green') |$error| { undef } # result is undef\n```\n\n```puppet\n$x = [1, 2, ['blue']]\n$x.get('2.color') |$error| {\n  notice(\"Walked path is ${error.details['walked_path']}\")\n}\n```\nWould notice `Walked path is [2, color]`\n\nAlso see:\n* `getvar()` that takes the first segment to be the name of a variable\n  and then delegates to this function.\n* `dig()` function which is similar but uses an\n  array of navigation values instead of a dot notation string.\n\n\n`get(Any $value, String $dotted_string, Optional[Any] $default_value, Optional[Callable[1,1]] &$block)`\n\n## `getvar`\n\nDigs into a variable with dot notation to get a value from a structure.\n\n**To get the value from a variable** (that may or may not exist), call the function with\none or two arguments:\n\n* The **first** argument must be a string, and must start with a variable name without leading `$`,\n  for example `get('facts')`. The variable name can be followed\n  by a _dot notation navigation string_ to dig out a value in the array or hash value\n  of the variable.\n* The **optional second** argument can be any type of value and it is used as the\n  _default value_ if the function would otherwise return `undef`.\n* An **optional lambda** for error handling taking one `Error` argument.\n\n**Dot notation navigation string** -\nThe dot string consists of period `.` separated segments where each\nsegment is either the index into an array or the value of a hash key.\nIf a wanted key contains a period it must be quoted to avoid it being\ntaken as a segment separator. Quoting can be done with either\nsingle quotes `'` or double quotes `\"`. If a segment is\na decimal number it is converted to an Integer index. This conversion\ncan be prevented by quoting the value.\n\n```puppet\ngetvar('facts') # results in the value of $facts\n```\n\n```puppet\ngetvar('facts.os.family') # results in the value of $facts['os']['family']\n```\n\n```puppet\n$x = [1,2,[{'name' =>'waldo'}]]\ngetvar('x.2.1.name', 'not waldo')\n# results in 'not waldo'\n```\n\nFor further examples and how to perform error handling, see the `get()` function\nwhich this function delegates to after having resolved the variable value.\n\n\n`getvar(Pattern[/\\A(?:::)?(?:[a-z]\\w*::)*[a-z_]\\w*(?:\\.|\\Z)/] $get_string, Optional[Any] $default_value, Optional[Callable[1,1]] &$block)`\n\n## `group_by`\n\nGroups the collection by result of the block. Returns a hash where the keys are the evaluated result from the block\nand the values are arrays of elements in the collection that correspond to the key.\n\n\nSignature 1\n\n`group_by(Collection $collection, Callable[1,1] &$block)`\n\n### Parameters\n\n\n* `collection` --- A collection of things to group.\n\nReturn type(s): `Hash`. \n\n\n### Examples\n\nGroup array of strings by length, results in e.g. `{ 1 => [a, b], 2 => [ab] }`\n\n```puppet\n[a, b, ab].group_by |$s| { $s.length }\n```\n\nGroup array of strings by length and index, results in e.g. `{1 => ['a'], 2 => ['b', 'ab']}`\n\n```puppet\n[a, b, ab].group_by |$i, $s| { $i%2 + $s.length }\n```\n\nGroup hash iterating by key-value pair, results in e.g. `{ 2 => [['a', [1, 2]]], 1 => [['b', [1]]] }`\n\n```puppet\n{ a => [1, 2], b => [1] }.group_by |$kv| { $kv[1].length }\n```\n\nGroup hash iterating by key and value, results in e.g. `{ 2 => [['a', [1, 2]]], 1 => [['b', [1]]] }`\n\n```puppet\n { a => [1, 2], b => [1] }.group_by |$k, $v| { $v.length }\n```\n\n\nSignature 2\n\n`group_by(Array $array, Callable[2,2] &$block)`\n\nSignature 3\n\n`group_by(Collection $collection, Callable[2,2] &$block)`\n\n## `hiera`\n\nPerforms a standard priority lookup of the hierarchy and returns the most specific value\nfor a given key. The returned value can be any type of data.\n\nThis function is deprecated in favor of the `lookup` function. While this function\ncontinues to work, it does **not** support:\n* `lookup_options` stored in the data\n* lookup across global, environment, and module layers\n\nThe function takes up to three arguments, in this order:\n\n1. A string key that Hiera searches for in the hierarchy. **Required**.\n2. An optional default value to return if Hiera doesn't find anything matching the key.\n    * If this argument isn't provided and this function results in a lookup failure, Puppet\n    fails with a compilation error.\n3. The optional name of an arbitrary\n[hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\ntop of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n    * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n    searching the rest of the hierarchy.\n\nThe `hiera` function does **not** find all matches throughout a hierarchy, instead\nreturning the first specific value starting at the top of the hierarchy. To search\nthroughout a hierarchy, use the `hiera_array` or `hiera_hash` functions.\n\n```yaml\n# Assuming hiera.yaml\n# :hierarchy:\n#   - web01.example.com\n#   - common\n\n# Assuming web01.example.com.yaml:\n# users:\n#   - \"Amy Barry\"\n#   - \"Carrie Douglas\"\n\n# Assuming common.yaml:\nusers:\n  admins:\n    - \"Edith Franklin\"\n    - \"Ginny Hamilton\"\n  regular:\n    - \"Iris Jackson\"\n    - \"Kelly Lambert\"\n```\n\n```puppet\n# Assuming we are not web01.example.com:\n\n$users = hiera('users', undef)\n\n# $users contains {admins  => [\"Edith Franklin\", \"Ginny Hamilton\"],\n#                  regular => [\"Iris Jackson\", \"Kelly Lambert\"]}\n```\n\nYou can optionally generate the default value with a\n[lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\ntakes one parameter.\n\n```puppet\n# Assuming the same Hiera data as the previous example:\n\n$users = hiera('users') | $key | { \"Key \\'${key}\\' not found\" }\n\n# $users contains {admins  => [\"Edith Franklin\", \"Ginny Hamilton\"],\n#                  regular => [\"Iris Jackson\", \"Kelly Lambert\"]}\n# If hiera couldn't match its key, it would return the lambda result,\n# \"Key 'users' not found\".\n```\n\nThe returned value's data type depends on the types of the results. In the example\nabove, Hiera matches the 'users' key and returns it as a hash.\n\nSee\n[the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\nAlso see\n[the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\nfor more information about the Hiera 3 functions.\n\n\n`hiera()`\n\n## `hiera_array`\n\nFinds all matches of a key throughout the hierarchy and returns them as a single flattened\narray of unique values. If any of the matched values are arrays, they're flattened and\nincluded in the results. This is called an\n[array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge).\n\nThis function is deprecated in favor of the `lookup` function. While this function\ncontinues to work, it does **not** support:\n* `lookup_options` stored in the data\n* lookup across global, environment, and module layers\n\nThe `hiera_array` function takes up to three arguments, in this order:\n\n1. A string key that Hiera searches for in the hierarchy. **Required**.\n2. An optional default value to return if Hiera doesn't find anything matching the key.\n    * If this argument isn't provided and this function results in a lookup failure, Puppet\n    fails with a compilation error.\n3. The optional name of an arbitrary\n[hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\ntop of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n    * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n    searching the rest of the hierarchy.\n\n```yaml\n# Assuming hiera.yaml\n# :hierarchy:\n#   - web01.example.com\n#   - common\n\n# Assuming common.yaml:\n# users:\n#   - 'cdouglas = regular'\n#   - 'efranklin = regular'\n\n# Assuming web01.example.com.yaml:\n# users: 'abarry = admin'\n```\n\n```puppet\n$allusers = hiera_array('users', undef)\n\n# $allusers contains [\"cdouglas = regular\", \"efranklin = regular\", \"abarry = admin\"].\n```\n\nYou can optionally generate the default value with a\n[lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\ntakes one parameter.\n\n```puppet\n# Assuming the same Hiera data as the previous example:\n\n$allusers = hiera_array('users') | $key | { \"Key \\'${key}\\' not found\" }\n\n# $allusers contains [\"cdouglas = regular\", \"efranklin = regular\", \"abarry = admin\"].\n# If hiera_array couldn't match its key, it would return the lambda result,\n# \"Key 'users' not found\".\n```\n\n`hiera_array` expects that all values returned will be strings or arrays. If any matched\nvalue is a hash, Puppet raises a type mismatch error.\n\nSee\n[the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\nAlso see\n[the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\nfor more information about the Hiera 3 functions.\n\n\n`hiera_array()`\n\n## `hiera_hash`\n\nFinds all matches of a key throughout the hierarchy and returns them in a merged hash.\n\nThis function is deprecated in favor of the `lookup` function. While this function\ncontinues to work, it does **not** support:\n* `lookup_options` stored in the data\n* lookup across global, environment, and module layers\n\nIf any of the matched hashes share keys, the final hash uses the value from the\nhighest priority match. This is called a\n[hash merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#hash-merge).\n\nThe merge strategy is determined by Hiera's\n[`:merge_behavior`](https://puppet.com/docs/hiera/latest/configuring.html#mergebehavior)\nsetting.\n\nThe `hiera_hash` function takes up to three arguments, in this order:\n\n1. A string key that Hiera searches for in the hierarchy. **Required**.\n2. An optional default value to return if Hiera doesn't find anything matching the key.\n    * If this argument isn't provided and this function results in a lookup failure, Puppet\n    fails with a compilation error.\n3. The optional name of an arbitrary\n[hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\ntop of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n    * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n    searching the rest of the hierarchy.\n\n```yaml\n# Assuming hiera.yaml\n# :hierarchy:\n#   - web01.example.com\n#   - common\n\n# Assuming common.yaml:\n# users:\n#   regular:\n#     'cdouglas': 'Carrie Douglas'\n\n# Assuming web01.example.com.yaml:\n# users:\n#   administrators:\n#     'aberry': 'Amy Berry'\n```\n\n```puppet\n# Assuming we are not web01.example.com:\n\n$allusers = hiera_hash('users', undef)\n\n# $allusers contains {regular => {\"cdouglas\" => \"Carrie Douglas\"},\n#                     administrators => {\"aberry\" => \"Amy Berry\"}}\n```\n\nYou can optionally generate the default value with a\n[lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\ntakes one parameter.\n\n```puppet\n# Assuming the same Hiera data as the previous example:\n\n$allusers = hiera_hash('users') | $key | { \"Key \\'${key}\\' not found\" }\n\n# $allusers contains {regular => {\"cdouglas\" => \"Carrie Douglas\"},\n#                     administrators => {\"aberry\" => \"Amy Berry\"}}\n# If hiera_hash couldn't match its key, it would return the lambda result,\n# \"Key 'users' not found\".\n```\n\n`hiera_hash` expects that all values returned will be hashes. If any of the values\nfound in the data sources are strings or arrays, Puppet raises a type mismatch error.\n\nSee\n[the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\nAlso see\n[the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\nfor more information about the Hiera 3 functions.\n\n\n`hiera_hash()`\n\n## `hiera_include`\n\nAssigns classes to a node using an\n[array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge)\nthat retrieves the value for a user-specified key from Hiera's data.\n\nThis function is deprecated in favor of the `lookup` function in combination with `include`.\nWhile this function continues to work, it does **not** support:\n* `lookup_options` stored in the data\n* lookup across global, environment, and module layers\n\n```puppet\n# In site.pp, outside of any node definitions and below any top-scope variables:\nlookup('classes', Array[String], 'unique').include\n```\n\nThe `hiera_include` function requires:\n\n- A string key name to use for classes.\n- A call to this function (i.e. `hiera_include('classes')`) in your environment's\n`sites.pp` manifest, outside of any node definitions and below any top-scope variables\nthat Hiera uses in lookups.\n- `classes` keys in the appropriate Hiera data sources, with an array for each\n`classes` key and each value of the array containing the name of a class.\n\nThe function takes up to three arguments, in this order:\n\n1. A string key that Hiera searches for in the hierarchy. **Required**.\n2. An optional default value to return if Hiera doesn't find anything matching the key.\n    * If this argument isn't provided and this function results in a lookup failure, Puppet\n    fails with a compilation error.\n3. The optional name of an arbitrary\n[hierarchy level](https://puppet.com/docs/hiera/latest/hierarchy.html) to insert at the\ntop of the hierarchy. This lets you temporarily modify the hierarchy for a single lookup.\n    * If Hiera doesn't find a matching key in the overriding hierarchy level, it continues\n    searching the rest of the hierarchy.\n\nThe function uses an\n[array merge lookup](https://puppet.com/docs/hiera/latest/lookup_types.html#array-merge)\nto retrieve the `classes` array, so every node gets every class from the hierarchy.\n\n```yaml\n# Assuming hiera.yaml\n# :hierarchy:\n#   - web01.example.com\n#   - common\n\n# Assuming web01.example.com.yaml:\n# classes:\n#   - apache::mod::php\n\n# Assuming common.yaml:\n# classes:\n#   - apache\n```\n\n```puppet\n# In site.pp, outside of any node definitions and below any top-scope variables:\nhiera_include('classes', undef)\n\n# Puppet assigns the apache and apache::mod::php classes to the web01.example.com node.\n```\n\nYou can optionally generate the default value with a\n[lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) that\ntakes one parameter.\n\n```puppet\n# Assuming the same Hiera data as the previous example:\n\n# In site.pp, outside of any node definitions and below any top-scope variables:\nhiera_include('classes') | $key | {\"Key \\'${key}\\' not found\" }\n\n# Puppet assigns the apache and apache::mod::php classes to the web01.example.com node.\n# If hiera_include couldn't match its key, it would return the lambda result,\n# \"Key 'classes' not found\".\n```\n\nSee\n[the 'Using the lookup function' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html) for how to perform lookup of data.\nAlso see\n[the 'Using the deprecated hiera functions' documentation](https://puppet.com/docs/puppet/latest/hiera_automatic.html)\nfor more information about the Hiera 3 functions.\n\n\n`hiera_include()`\n\n## `hocon_data`\n\nThe `hocon_data` is a hiera 5 `data_hash` data provider function.\nSee [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-built-in-backends) for\nhow to use this function.\n\nNote that this function is not supported without a hocon library being present.\n\n\n`hocon_data(Struct[{path=>String[1]}] $options, Puppet::LookupContext $context)`\n\n## `import`\n\nThe import function raises an error when called to inform the user that import is no longer supported.\n\n\n`import(Any *$args)`\n\n## `include`\n\nDeclares one or more classes, causing the resources in them to be\nevaluated and added to the catalog. Accepts a class name, an array of class\nnames, or a comma-separated list of class names.\n\nThe `include` function can be used multiple times on the same class and will\nonly declare a given class once. If a class declared with `include` has any\nparameters, Puppet will automatically look up values for them in Hiera, using\n`<class name>::<parameter name>` as the lookup key.\n\nContrast this behavior with resource-like class declarations\n(`class {'name': parameter => 'value',}`), which must be used in only one place\nper class and can directly set parameters. You should avoid using both `include`\nand resource-like declarations with the same class.\n\nThe `include` function does not cause classes to be contained in the class\nwhere they are declared. For that, see the `contain` function. It also\ndoes not create a dependency relationship between the declared class and the\nsurrounding class; for that, see the `require` function.\n\nYou must use the class's full name;\nrelative names are not allowed. In addition to names in string form,\nyou may also directly use `Class` and `Resource` `Type`-values that are produced by\nthe resource and relationship expressions.\n\n- Since < 3.0.0\n- Since 4.0.0 support for class and resource type values, absolute names\n- Since 4.7.0 returns an `Array[Type[Class]]` of all included classes\n\n\n`include(Any *$names)`\n\n## `index`\n\nReturns the index (or key in a hash) to a first-found value in an `Iterable` value.\n\nWhen called with a  [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nthe lambda is called repeatedly using each value in a data structure until the lambda returns a \"truthy\" value which\nmakes the function return the index or key, or if the end of the iteration is reached, undef is returned.\n\nThis function can be called in two different ways; with a value to be searched for, or with\na lambda that determines if an entry in the iterable matches.\n\nWhen called with a lambda the function takes two mandatory arguments, in this order:\n\n1. An array, hash, string, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It can request one (value) or two (index/key, value) parameters.\n\n`$data.index |$parameter| { <PUPPET CODE BLOCK> }`\n\nor\n\n`index($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\n```puppet\n$data = [\"routers\", \"servers\", \"workstations\"]\nnotice $data.index |$value| { $value == 'servers' } # notices 1\nnotice $data.index |$value| { $value == 'hosts'  }  # notices undef\n```\n\n```puppet\n$data = {types => [\"routers\", \"servers\", \"workstations\"], colors => ['red', 'blue', 'green']}\nnotice $data.index |$value| { 'servers' in $value } # notices 'types'\nnotice $data.index |$value| { 'red' in $value }     # notices 'colors'\n```\nNote that the lambda gets the value and not an array with `[key, value]` as in other\niterative functions.\n\nUsing a lambda that accepts two values works the same way. The lambda gets the index/key\nas the first parameter and the value as the second parameter.\n\n```puppet\n# Find the first even numbered index that has a non String value\n$data = [key1, 1, 3, 5]\nnotice $data.index |$idx, $value| { $idx % 2 == 0 and $value !~ String } # notices 2\n```\n\nWhen called on a `String`, the lambda is given each character as a value. What is typically wanted is to\nfind a sequence of characters which is achieved by calling the function with a value to search for instead\nof giving a lambda.\n\n\n```puppet\n# Find first occurrence of 'ah'\n$data = \"blablahbleh\"\nnotice $data.index('ah') # notices 5\n```\n\n```puppet\n# Find first occurrence of 'la' or 'le'\n$data = \"blablahbleh\"\nnotice $data.index(/l(a|e)/ # notices 1\n```\n\nWhen searching in a `String` with a given value that is neither `String` nor `Regexp` the answer is always `undef`.\nWhen searching in any other iterable, the value is matched against each value in the iteration using strict\nRuby `==` semantics. If Puppet Language semantics are wanted (where string compare is case insensitive) use a\nlambda and the `==` operator in Puppet.\n\n```puppet\n$data = ['routers', 'servers', 'WORKstations']\nnotice $data.index('servers')      # notices 1\nnotice $data.index('workstations') # notices undef (not matching case)\n```\n\nFor an general examples that demonstrates iteration, see the Puppet\n[iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\ndocumentation.\n\n\nSignature 1\n\n`index(Hash[Any, Any] $hash, Callable[2,2] &$block)`\n\nSignature 2\n\n`index(Hash[Any, Any] $hash, Callable[1,1] &$block)`\n\nSignature 3\n\n`index(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 4\n\n`index(Iterable $enumerable, Callable[1,1] &$block)`\n\nSignature 5\n\n`index(String $str, Variant[String,Regexp] $match)`\n\nSignature 6\n\n`index(Iterable $enumerable, Any $match)`\n\n## `info`\n\nLogs a message on the server at level `info`.\n\n\n`info(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `inline_epp`\n\nEvaluates an Embedded Puppet (EPP) template string and returns the rendered\ntext result as a String.\n\n`inline_epp('<EPP TEMPLATE STRING>', <PARAMETER HASH>)`\n\nThe first argument to this function should be a string containing an EPP\ntemplate. In most cases, the last argument is optional; if used, it should be a\n[hash](https://puppet.com/docs/puppet/latest/lang_data_hash.html) that contains parameters to\npass to the template.\n\n- See the [template](https://puppet.com/docs/puppet/latest/lang_template.html)\ndocumentation for general template usage information.\n- See the [EPP syntax](https://puppet.com/docs/puppet/latest/lang_template_epp.html)\ndocumentation for examples of EPP.\n\nFor example, to evaluate an inline EPP template and pass it the `docroot` and\n`virtual_docroot` parameters, call the `inline_epp` function like this:\n\n`inline_epp('docroot: <%= $docroot %> Virtual docroot: <%= $virtual_docroot %>',\n{ 'docroot' => '/var/www/html', 'virtual_docroot' => '/var/www/example' })`\n\nPuppet produces a syntax error if you pass more parameters than are declared in\nthe template's parameter tag. When passing parameters to a template that\ncontains a parameter tag, use the same names as the tag's declared parameters.\n\nParameters are required only if they are declared in the called template's\nparameter tag without default values. Puppet produces an error if the\n`inline_epp` function fails to pass any required parameter.\n\nAn inline EPP template should be written as a single-quoted string or\n[heredoc](https://puppet.com/docs/puppet/latest/lang_data_string.html#heredocs).\nA double-quoted string is subject to expression interpolation before the string\nis parsed as an EPP template.\n\nFor example, to evaluate an inline EPP template using a heredoc, call the\n`inline_epp` function like this:\n\n```puppet\n# Outputs 'Hello given argument planet!'\ninline_epp(@(END), { x => 'given argument' })\n<%- | $x, $y = planet | -%>\nHello <%= $x %> <%= $y %>!\nEND\n```\n\n\n`inline_epp(String $template, Optional[Hash[Pattern[/^\\w+$/], Any]] $parameters)`\n\n## `inline_template`\n\nEvaluate a template string and return its value.  See\n[the templating docs](https://puppet.com/docs/puppet/latest/lang_template.html) for\nmore information. Note that if multiple template strings are specified, their\noutput is all concatenated and returned as the output of the function.\n\n\n`inline_template()`\n\n## `join`\n\nJoins the values of an Array into a string with elements separated by a delimiter.\n\nSupports up to two arguments\n* **values** - first argument is required and must be an an `Array`\n* **delimiter** - second arguments is the delimiter between elements, must be a `String` if given, and defaults to an empty string.\n\n```puppet\njoin(['a','b','c'], \",\")\n# Would result in: \"a,b,c\"\n```\n\nNote that array is flattened before elements are joined, but flattening does not extend to arrays nested in hashes or other objects.\n\n```puppet\n$a = [1,2, undef, 'hello', [x,y,z], {a => 2, b => [3, 4]}]\nnotice join($a, ', ')\n\n# would result in noticing:\n# 1, 2, , hello, x, y, z, {\"a\"=>2, \"b\"=>[3, 4]}\n```\n\nFor joining iterators and other containers of elements a conversion must first be made to\nan `Array`. The reason for this is that there are many options how such a conversion should\nbe made.\n\n```puppet\n[1,2,3].reverse_each.convert_to(Array).join(', ')\n# would result in: \"3, 2, 1\"\n```\n```puppet\n{a => 1, b => 2}.convert_to(Array).join(', ')\n# would result in \"a, 1, b, 2\"\n```\n\nFor more detailed control over the formatting (including indentations and line breaks, delimiters around arrays\nand hash entries, between key/values in hash entries, and individual formatting of values in the array)\nsee the `new` function for `String` and its formatting options for `Array` and `Hash`.\n\n\n`join(Array $arg, Optional[String] $delimiter)`\n\n## `json_data`\n\nThe `json_data` is a hiera 5 `data_hash` data provider function.\nSee [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-built-in-backends) for\nhow to use this function.\n\n\n`json_data(Struct[{path=>String[1]}] $options, Puppet::LookupContext $context)`\n\n## `keys`\n\nReturns the keys of a hash as an Array\n\n```puppet\n$hsh = {\"apples\" => 3, \"oranges\" => 4 }\n$hsh.keys()\nkeys($hsh)\n# both results in the array [\"apples\", \"oranges\"]\n```\n\n* Note that a hash in the puppet language accepts any data value (including `undef`) unless\n  it is constrained with a `Hash` data type that narrows the allowed data types.\n* For an empty hash, an empty array is returned.\n* The order of the keys is the same as the order in the hash (typically the order in which they were added).\n\n\n`keys(Hash $hsh)`\n\n## `length`\n\nReturns the length of an Array, Hash, String, or Binary value.\n\nThe returned value is a positive integer indicating the number\nof elements in the container; counting (possibly multibyte) characters for a `String`,\nbytes in a `Binary`, number of elements in an `Array`, and number of\nkey-value associations in a Hash.\n\n```puppet\n\"roses\".length()        # 5\nlength(\"violets\")       # 7\n[10, 20].length         # 2\n{a => 1, b => 3}.length # 2\n```\n\n\nSignature 1\n\n`length(Collection $arg)`\n\nSignature 2\n\n`length(String $arg)`\n\nSignature 3\n\n`length(Binary $arg)`\n\n## `lest`\n\nCalls a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nwithout arguments if the value given to `lest` is `undef`.\nReturns the result of calling the lambda if the argument is `undef`, otherwise the\ngiven argument.\n\nThe `lest` function is useful in a chain of `then` calls, or in general\nas a guard against `undef` values. The function can be used to call `fail`, or to\nreturn a default value.\n\nThese two expressions are equivalent:\n\n```puppet\nif $x == undef { do_things() }\nlest($x) || { do_things() }\n```\n\n```puppet\n$data = {a => [ b, c ] }\nnotice $data.dig(a, b, c)\n .then |$x| { $x * 2 }\n .lest || { fail(\"no value for $data[a][b][c]\" }\n```\n\nWould fail the operation because `$data[a][b][c]` results in `undef`\n(there is no `b` key in `a`).\n\nIn contrast - this example:\n\n```puppet\n$data = {a => { b => { c => 10 } } }\nnotice $data.dig(a, b, c)\n .then |$x| { $x * 2 }\n .lest || { fail(\"no value for $data[a][b][c]\" }\n```\n\nWould notice the value `20`\n\n\n`lest(Any $arg, Callable[0,0] &$block)`\n\n## `lookup`\n\nUses the Puppet lookup system to retrieve a value for a given key. By default,\nthis returns the first value found (and fails compilation if no values are\navailable), but you can configure it to merge multiple values into one, fail\ngracefully, and more.\n\nWhen looking up a key, Puppet will search up to three tiers of data, in the\nfollowing order:\n\n1. Hiera.\n2. The current environment's data provider.\n3. The indicated module's data provider, if the key is of the form\n   `<MODULE NAME>::<SOMETHING>`.\n\n### Arguments\n\nYou must provide the name of a key to look up, and can optionally provide other\narguments. You can combine these arguments in the following ways:\n\n* `lookup( <NAME>, [<VALUE TYPE>], [<MERGE BEHAVIOR>], [<DEFAULT VALUE>] )`\n* `lookup( [<NAME>], <OPTIONS HASH> )`\n* `lookup( as above ) |$key| { # lambda returns a default value }`\n\nArguments in `[square brackets]` are optional.\n\nThe arguments accepted by `lookup` are as follows:\n\n1. `<NAME>` (string or array) --- The name of the key to look up.\n    * This can also be an array of keys. If Puppet doesn't find anything for the\n    first key, it will try again with the subsequent ones, only resorting to a\n    default value if none of them succeed.\n2. `<VALUE TYPE>` (data type) --- A\n[data type](https://puppet.com/docs/puppet/latest/lang_data_type.html)\nthat must match the retrieved value; if not, the lookup (and catalog\ncompilation) will fail. Defaults to `Data` (accepts any normal value).\n3. `<MERGE BEHAVIOR>` (string or hash; see **\"Merge Behaviors\"** below) ---\nWhether (and how) to combine multiple values. If present, this overrides any\nmerge behavior specified in the data sources. Defaults to no value; Puppet will\nuse merge behavior from the data sources if present, and will otherwise do a\nfirst-found lookup.\n4. `<DEFAULT VALUE>` (any normal value) --- If present, `lookup` returns this\nwhen it can't find a normal value. Default values are never merged with found\nvalues. Like a normal value, the default must match the value type. Defaults to\nno value; if Puppet can't find a normal value, the lookup (and compilation) will\nfail.\n5. `<OPTIONS HASH>` (hash) --- Alternate way to set the arguments above, plus\nsome less-common extra options. If you pass an options hash, you can't combine\nit with any regular arguments (except `<NAME>`). An options hash can have the\nfollowing keys:\n    * `'name'` --- Same as `<NAME>` (argument 1). You can pass this as an\n    argument or in the hash, but not both.\n    * `'value_type'` --- Same as `<VALUE TYPE>` (argument 2).\n    * `'merge'` --- Same as `<MERGE BEHAVIOR>` (argument 3).\n    * `'default_value'` --- Same as `<DEFAULT VALUE>` (argument 4).\n    * `'default_values_hash'` (hash) --- A hash of lookup keys and default\n    values. If Puppet can't find a normal value, it will check this hash for the\n    requested key before giving up. You can combine this with `default_value` or\n    a lambda, which will be used if the key isn't present in this hash. Defaults\n    to an empty hash.\n    * `'override'` (hash) --- A hash of lookup keys and override values. Puppet\n    will check for the requested key in the overrides hash _first;_ if found, it\n    returns that value as the _final_ value, ignoring merge behavior. Defaults\n    to an empty hash.\n\nFinally, `lookup` can take a lambda, which must accept a single parameter.\nThis is yet another way to set a default value for the lookup; if no results are\nfound, Puppet will pass the requested key to the lambda and use its result as\nthe default value.\n\n### Merge Behaviors\n\nPuppet lookup uses a hierarchy of data sources, and a given key might have\nvalues in multiple sources. By default, Puppet returns the first value it finds,\nbut it can also continue searching and merge all the values together.\n\n> **Note:** Data sources can use the special `lookup_options` metadata key to\nrequest a specific merge behavior for a key. The `lookup` function will use that\nrequested behavior unless you explicitly specify one.\n\nThe valid merge behaviors are:\n\n* `'first'` --- Returns the first value found, with no merging. Puppet lookup's\ndefault behavior.\n* `'unique'` (called \"array merge\" in classic Hiera) --- Combines any number of\narrays and scalar values to return a merged, flattened array with all duplicate\nvalues removed. The lookup will fail if any hash values are found.\n* `'hash'` --- Combines the keys and values of any number of hashes to return a\nmerged hash. If the same key exists in multiple source hashes, Puppet will use\nthe value from the highest-priority data source; it won't recursively merge the\nvalues.\n* `'deep'` --- Combines the keys and values of any number of hashes to return a\nmerged hash. If the same key exists in multiple source hashes, Puppet will\nrecursively merge hash or array values (with duplicate values removed from\narrays). For conflicting scalar values, the highest-priority value will win.\n* `{'strategy' => 'first'}`, `{'strategy' => 'unique'}`,\nor `{'strategy' => 'hash'}` --- Same as the string versions of these merge behaviors.\n* `{'strategy' => 'deep', <DEEP OPTION> => <VALUE>, ...}` --- Same as `'deep'`,\nbut can adjust the merge with additional options. The available options are:\n    * `'knockout_prefix'` (string) --- A string prefix to indicate a\n    value should be _removed_ from the final result. If a value is exactly equal\n    to the prefix, it will knockout the entire element. Defaults to `undef`, which\n    disables this feature.\n    * `'sort_merged_arrays'` (boolean) --- Whether to sort all arrays that are\n    merged together. Defaults to `false`.\n    * `'merge_hash_arrays'` (boolean) --- Whether to merge hashes within arrays.\n    Defaults to `false`.\n\n\nSignature 1\n\n`lookup(NameType $name, Optional[ValueType] $value_type, Optional[MergeType] $merge)`\n\nSignature 2\n\n`lookup(NameType $name, Optional[ValueType] $value_type, Optional[MergeType] $merge, DefaultValueType $default_value)`\n\nSignature 3\n\n`lookup(NameType $name, Optional[ValueType] $value_type, Optional[MergeType] $merge, BlockType &$block)`\n\nSignature 4\n\n`lookup(OptionsWithName $options_hash, Optional[BlockType] &$block)`\n\nSignature 5\n\n`lookup(Variant[String,Array[String]] $name, OptionsWithoutName $options_hash, Optional[BlockType] &$block)`\n\n## `lstrip`\n\nStrips leading spaces from a String\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String` the conversion removes all leading ASCII white space characters such as space, tab, newline, and return.\n  It does not remove other space-like characters like hard space (Unicode U+00A0). (Tip, `/^[[:space:]]/` regular expression\n  matches all space-like characters).\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\n```puppet\n\"\\n\\thello \".lstrip()\nlstrip(\"\\n\\thello \")\n```\nWould both result in `\"hello\"`\n\n```puppet\n[\"\\n\\thello \", \"\\n\\thi \"].lstrip()\nlstrip([\"\\n\\thello \", \"\\n\\thi \"])\n```\nWould both result in `['hello', 'hi']`\n\n\nSignature 1\n\n`lstrip(Numeric $arg)`\n\nSignature 2\n\n`lstrip(String $arg)`\n\nSignature 3\n\n`lstrip(Iterable[Variant[String, Numeric]] $arg)`\n\n## `map`\n\nApplies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nto every value in a data structure and returns an array containing the results.\n\nThis function takes two mandatory arguments, in this order:\n\n1. An array, hash, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It can\nrequest one or two parameters.\n\n`$transformed_data = $data.map |$parameter| { <PUPPET CODE BLOCK> }`\n\nor\n\n`$transformed_data = map($data) |$parameter| { <PUPPET CODE BLOCK> }`\n\nWhen the first argument (`$data` in the above example) is an array, Puppet passes each\nvalue in turn to the lambda.\n\n```puppet\n# For the array $data, return an array containing each value multiplied by 10\n$data = [1,2,3]\n$transformed_data = $data.map |$items| { $items * 10 }\n# $transformed_data contains [10,20,30]\n```\n\nWhen the first argument is a hash, Puppet passes each key and value pair to the lambda\nas an array in the form `[key, value]`.\n\n```puppet\n# For the hash $data, return an array containing the keys\n$data = {'a'=>1,'b'=>2,'c'=>3}\n$transformed_data = $data.map |$items| { $items[0] }\n# $transformed_data contains ['a','b','c']\n```\n\nWhen the first argument is an array and the lambda has two parameters, Puppet passes the\narray's indexes (enumerated from 0) in the first parameter and its values in the second\nparameter.\n\n```puppet\n# For the array $data, return an array containing the indexes\n$data = [1,2,3]\n$transformed_data = $data.map |$index,$value| { $index }\n# $transformed_data contains [0,1,2]\n```\n\nWhen the first argument is a hash, Puppet passes its keys to the first parameter and its\nvalues to the second parameter.\n\n```puppet\n# For the hash $data, return an array containing each value\n$data = {'a'=>1,'b'=>2,'c'=>3}\n$transformed_data = $data.map |$key,$value| { $value }\n# $transformed_data contains [1,2,3]\n```\n\n\nSignature 1\n\n`map(Hash[Any, Any] $hash, Callable[2,2] &$block)`\n\nSignature 2\n\n`map(Hash[Any, Any] $hash, Callable[1,1] &$block)`\n\nSignature 3\n\n`map(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 4\n\n`map(Iterable $enumerable, Callable[1,1] &$block)`\n\n## `match`\n\nMatches a regular expression against a string and returns an array containing the match\nand any matched capturing groups.\n\nThe first argument is a string or array of strings. The second argument is either a\nregular expression, regular expression represented as a string, or Regex or Pattern\ndata type that the function matches against the first argument.\n\nThe returned array contains the entire match at index 0, and each captured group at\nsubsequent index values. If the value or expression being matched is an array, the\nfunction returns an array with mapped match results.\n\nIf the function doesn't find a match, it returns 'undef'.\n\n```puppet\n$matches = \"abc123\".match(/[a-z]+[1-9]+/)\n# $matches contains [abc123]\n```\n\n```puppet\n$matches = \"abc123\".match(/([a-z]+)([1-9]+)/)\n# $matches contains [abc123, abc, 123]\n```\n\n```puppet\n$matches = [\"abc123\",\"def456\"].match(/([a-z]+)([1-9]+)/)\n# $matches contains [[abc123, abc, 123], [def456, def, 456]]\n```\n\n\nSignature 1\n\n`match(String $string, Variant[Any, Type] $pattern)`\n\nSignature 2\n\n`match(Array[String] $string, Variant[Any, Type] $pattern)`\n\n## `max`\n\nReturns the highest value among a variable number of arguments.\nTakes at least one argument.\n\nThis function is (with one exception) compatible with the stdlib function\nwith the same name and performs deprecated type conversion before\ncomparison as follows:\n\n* If a value converted to String is an optionally '-' prefixed,\n  string of digits, one optional decimal point, followed by optional\n  decimal digits - then the comparison is performed on the values\n  converted to floating point.\n* If a value is not considered convertible to float, it is converted\n  to a `String` and the comparison is a lexical compare where min is\n  the lexicographical later value.\n* A lexicographical compare is performed in a system locale - international\n  characters may therefore not appear in what a user thinks is the correct order.\n* The conversion rules apply to values in pairs - the rule must hold for both\n  values - a value may therefore be compared using different rules depending\n  on the \"other value\".\n* The returned result found to be the \"highest\" is the original unconverted value.\n\nThe above rules have been deprecated in Puppet 6.0.0 as they produce strange results when\ngiven values of mixed data types. In general, either convert values to be\nall `String` or all `Numeric` values before calling the function, or call the\nfunction with a lambda that performs type conversion and comparison. This because one\nsimply cannot compare `Boolean` with `Regexp` and with any arbitrary `Array`, `Hash` or\n`Object` and getting a meaningful result.\n\nThe one change in the function's behavior is when the function is given a single\narray argument. The stdlib implementation would return that array as the result where\nit now instead returns the max value from that array.\n\n```puppet\nnotice(max(1)) # would notice 1\nnotice(max(1,2)) # would notice 2\nnotice(max(\"1\", 2)) # would notice 2\nnotice(max(\"0777\", 512)) # would notice \"0777\", since \"0777\" is not converted from octal form\nnotice(max(0777, 512)) # would notice 512, since 0777 is decimal 511\nnotice(max('aa', 'ab')) # would notice 'ab'\nnotice(max(['a'], ['b'])) # would notice ['b'], since \"['b']\" is after \"['a']\"\n```\n\n```puppet\n$x = [1,2,3,4]\nnotice(max(*$x)) # would notice 4\n```\n\n```puppet\n$x = [1,2,3,4]\nnotice(max($x)) # would notice 4\nnotice($x.max) # would notice 4\n```\nThis example shows that a single array argument is used as the set of values\nas opposed to being a single returned value.\n\nWhen calling with a lambda, it must accept two variables and it must return\none of -1, 0, or 1 depending on if first argument is before/lower than, equal to,\nor higher/after the second argument.\n\n```puppet\nnotice(max(\"2\", \"10\", \"100\") |$a, $b| { compare($a, $b) })\n```\n\nWould notice \"2\" as higher since it is lexicographically higher/after the other values. Without the\nlambda the stdlib compatible (deprecated) behavior would have been to return \"100\" since number conversion\nkicks in.\n\n\nSignature 1\n\n`max(Numeric *$values)`\n\nSignature 2\n\n`max(String *$values)`\n\nSignature 3\n\n`max(Semver *$values)`\n\nSignature 4\n\n`max(Timespan *$values)`\n\nSignature 5\n\n`max(Timestamp *$values)`\n\nSignature 6\n\n`max(Array[Numeric] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 7\n\n`max(Array[String] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 8\n\n`max(Array[Semver] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 9\n\n`max(Array[Timespan] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 10\n\n`max(Array[Timestamp] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 11\n\n`max(Array $values, Optional[Callable[2,2]] &$block)`\n\nSignature 12\n\n`max(Any *$values, Callable[2,2] &$block)`\n\nSignature 13\n\n`max(Any *$values)`\n\n## `md5`\n\nReturns a MD5 hash value from a provided string.\n\n\n`md5()`\n\n## `min`\n\nReturns the lowest value among a variable number of arguments.\nTakes at least one argument.\n\nThis function is (with one exception) compatible with the stdlib function\nwith the same name and performs deprecated type conversion before\ncomparison as follows:\n\n* If a value converted to String is an optionally '-' prefixed,\n  string of digits, one optional decimal point, followed by optional\n  decimal digits - then the comparison is performed on the values\n  converted to floating point.\n* If a value is not considered convertible to float, it is converted\n  to a `String` and the comparison is a lexical compare where min is\n  the lexicographical earlier value.\n* A lexicographical compare is performed in a system locale - international\n  characters may therefore not appear in what a user thinks is the correct order.\n* The conversion rules apply to values in pairs - the rule must hold for both\n  values - a value may therefore be compared using different rules depending\n  on the \"other value\".\n* The returned result found to be the \"lowest\" is the original unconverted value.\n\nThe above rules have been deprecated in Puppet 6.0.0 as they produce strange results when\ngiven values of mixed data types. In general, either convert values to be\nall `String` or all `Numeric` values before calling the function, or call the\nfunction with a lambda that performs type conversion and comparison. This because one\nsimply cannot compare `Boolean` with `Regexp` and with any arbitrary `Array`, `Hash` or\n`Object` and getting a meaningful result.\n\nThe one change in the function's behavior is when the function is given a single\narray argument. The stdlib implementation would return that array as the result where\nit now instead returns the max value from that array.\n\n```puppet\nnotice(min(1)) # would notice 1\nnotice(min(1,2)) # would notice 1\nnotice(min(\"1\", 2)) # would notice 1\nnotice(min(\"0777\", 512)) # would notice 512, since \"0777\" is not converted from octal form\nnotice(min(0777, 512)) # would notice 511, since 0777 is decimal 511\nnotice(min('aa', 'ab')) # would notice 'aa'\nnotice(min(['a'], ['b'])) # would notice ['a'], since \"['a']\" is before \"['b']\"\n```\n\n```puppet\n$x = [1,2,3,4]\nnotice(min(*$x)) # would notice 1\n```\n\n```puppet\n$x = [1,2,3,4]\nnotice(min($x)) # would notice 1\nnotice($x.min) # would notice 1\n```\nThis example shows that a single array argument is used as the set of values\nas opposed to being a single returned value.\n\nWhen calling with a lambda, it must accept two variables and it must return\none of -1, 0, or 1 depending on if first argument is before/lower than, equal to,\nor higher/after the second argument.\n\n```puppet\nnotice(min(\"2\", \"10\", \"100\") |$a, $b| { compare($a, $b) })\n```\n\nWould notice \"10\" as lower since it is lexicographically lower/before the other values. Without the\nlambda the stdlib compatible (deprecated) behavior would have been to return \"2\" since number conversion kicks in.\n\n\nSignature 1\n\n`min(Numeric *$values)`\n\nSignature 2\n\n`min(String *$values)`\n\nSignature 3\n\n`min(Semver *$values)`\n\nSignature 4\n\n`min(Timespan *$values)`\n\nSignature 5\n\n`min(Timestamp *$values)`\n\nSignature 6\n\n`min(Array[Numeric] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 7\n\n`min(Array[Semver] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 8\n\n`min(Array[Timespan] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 9\n\n`min(Array[Timestamp] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 10\n\n`min(Array[String] $values, Optional[Callable[2,2]] &$block)`\n\nSignature 11\n\n`min(Array $values, Optional[Callable[2,2]] &$block)`\n\nSignature 12\n\n`min(Any *$values, Callable[2,2] &$block)`\n\nSignature 13\n\n`min(Any *$values)`\n\n## `module_directory`\n\nFinds an existing module and returns the path to its root directory.\n\nThe argument to this function should be a module name String\nFor example, the reference `mysql` will search for the\ndirectory `<MODULES DIRECTORY>/mysql` and return the first\nfound on the modulepath.\n\nThis function can also accept:\n\n* Multiple String arguments, which will return the path of the **first** module\n found, skipping non existing modules.\n* An array of module names, which will return the path of the **first** module\n found from the given names in the array, skipping non existing modules.\n\nThe function returns `undef` if none of the given modules were found\n\n\nSignature 1\n\n`module_directory(String *$names)`\n\nSignature 2\n\n`module_directory(Array[String] *$names)`\n\n## `new`\n\nCreates a new instance/object of a given data type.\n\nThis function makes it possible to create new instances of\nconcrete data types. If a block is given it is called with the\njust created instance as an argument.\n\nCalling this function is equivalent to directly\ncalling the data type:\n\n```puppet\n$a = Integer.new(\"42\")\n$b = Integer(\"42\")\n```\n\nThese would both convert the string `\"42\"` to the decimal value `42`.\n\n```puppet\n$a = Integer.new(\"42\", 8)\n$b = Integer({from => \"42\", radix => 8})\n```\n\nThis would convert the octal (radix 8) number `\"42\"` in string form\nto the decimal value `34`.\n\nThe new function supports two ways of giving the arguments:\n\n* by name (using a hash with property to value mapping)\n* by position (as regular arguments)\n\nNote that it is not possible to create new instances of\nsome abstract data types (for example `Variant`). The data type `Optional[T]` is an\nexception as it will create an instance of `T` or `undef` if the\nvalue to convert is `undef`.\n\nThe arguments that can be given is determined by the data type.\n\n> An assertion is always made that the produced value complies with the given type constraints.\n\n```puppet\nInteger[0].new(\"-100\")\n```\n\nWould fail with an assertion error (since value is less than 0).\n\nThe following sections show the arguments and conversion rules\nper data type built into the Puppet Type System.\n\n### Conversion to `Optional[T]` and `NotUndef[T]`\n\nConversion to these data types is the same as a conversion to the type argument `T`.\nIn the case of `Optional[T]` it is accepted that the argument to convert may be `undef`.\nIt is however not acceptable to give other arguments (than `undef`) that cannot be\nconverted to `T`.\n\n### Conversion to Integer\n\nA new `Integer` can be created from `Integer`, `Float`, `Boolean`, and `String` values.\nFor conversion from `String` it is possible to specify the radix (base).\n\n```puppet\ntype Radix = Variant[Default, Integer[2,2], Integer[8,8], Integer[10,10], Integer[16,16]]\n\nfunction Integer.new(\n  String $value,\n  Radix $radix = 10,\n  Boolean $abs = false\n)\n\nfunction Integer.new(\n  Variant[Numeric, Boolean] $value,\n  Boolean $abs = false\n)\n```\n\n* When converting from `String` the default radix is 10.\n* If radix is not specified an attempt is made to detect the radix from the start of the string:\n  * `0b` or `0B` is taken as radix 2.\n  * `0x` or `0X` is taken as radix 16.\n  * `0` as radix 8.\n  * All others are decimal.\n* Conversion from `String` accepts an optional sign in the string.\n* For hexadecimal (radix 16) conversion an optional leading `\"0x\"`, or `\"0X\"` is accepted.\n* For octal (radix 8) an optional leading `\"0\"` is accepted.\n* For binary (radix 2) an optional leading `\"0b\"` or `\"0B\"` is accepted.\n* When `radix` is set to `default`, the conversion is based on the leading.\n  characters in the string. A leading `\"0\"` for radix 8, a leading `\"0x\"`, or `\"0X\"` for\n  radix 16, and leading `\"0b\"` or `\"0B\"` for binary.\n* Conversion from `Boolean` results in `0` for `false` and `1` for `true`.\n* Conversion from `Integer`, `Float`, and `Boolean` ignores the radix.\n* `Float` value fractions are truncated (no rounding).\n* When `abs` is set to `true`, the result will be an absolute integer.\n\n```puppet\n$a_number = Integer(\"0xFF\", 16)    # results in 255\n$a_number = Integer(\"010\")         # results in 8\n$a_number = Integer(\"010\", 10)     # results in 10\n$a_number = Integer(true)          # results in 1\n$a_number = Integer(-38, 10, true) # results in 38\n```\n\n### Conversion to Float\n\nA new `Float` can be created from `Integer`, `Float`, `Boolean`, and `String` values.\nFor conversion from `String` both float and integer formats are supported.\n\n```puppet\nfunction Float.new(\n  Variant[Numeric, Boolean, String] $value,\n  Boolean $abs = true\n)\n```\n\n* For an integer, the floating point fraction of `.0` is added to the value.\n* A `Boolean` `true` is converted to `1.0`, and a `false` to `0.0`.\n* In `String` format, integer prefixes for hex and binary are understood (but not octal since\n  floating point in string format may start with a `'0'`).\n* When `abs` is set to `true`, the result will be an absolute floating point value.\n\n### Conversion to Numeric\n\nA new `Integer` or `Float` can be created from `Integer`, `Float`, `Boolean` and\n`String` values.\n\n```puppet\nfunction Numeric.new(\n  Variant[Numeric, Boolean, String] $value,\n  Boolean $abs = true\n)\n```\n\n* If the value has a decimal period, or if given in scientific notation\n  (e/E), the result is a `Float`, otherwise the value is an `Integer`. The\n  conversion from `String` always uses a radix based on the prefix of the string.\n* Conversion from `Boolean` results in `0` for `false` and `1` for `true`.\n* When `abs` is set to `true`, the result will be an absolute `Float`or `Integer` value.\n\n```puppet\n$a_number = Numeric(true)        # results in 1\n$a_number = Numeric(\"0xFF\")      # results in 255\n$a_number = Numeric(\"010\")       # results in 8\n$a_number = Numeric(\"3.14\")      # results in 3.14 (a float)\n$a_number = Numeric(-42.3, true) # results in 42.3\n$a_number = Numeric(-42, true)   # results in 42\n```\n\n### Conversion to Timespan\n\nA new `Timespan` can be created from `Integer`, `Float`, `String`, and `Hash` values. Several variants of the constructor are provided.\n\n**Timespan from seconds**\n\nWhen a Float is used, the decimal part represents fractions of a second.\n\n```puppet\nfunction Timespan.new(\n  Variant[Float, Integer] $value\n)\n```\n\n**Timespan from days, hours, minutes, seconds, and fractions of a second**\n\nThe arguments can be passed separately in which case the first four, days, hours, minutes, and seconds are mandatory and the rest are optional.\nAll values may overflow and/or be negative. The internal 128-bit nano-second integer is calculated as:\n\n```\n(((((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000 + milliseconds) * 1000 + microseconds) * 1000 + nanoseconds\n```\n\n```puppet\nfunction Timespan.new(\n  Integer $days, Integer $hours, Integer $minutes, Integer $seconds,\n  Integer $milliseconds = 0, Integer $microseconds = 0, Integer $nanoseconds = 0\n)\n```\n\nor, all arguments can be passed as a `Hash`, in which case all entries are optional:\n\n```puppet\nfunction Timespan.new(\n  Struct[{\n    Optional[negative] => Boolean,\n    Optional[days] => Integer,\n    Optional[hours] => Integer,\n    Optional[minutes] => Integer,\n    Optional[seconds] => Integer,\n    Optional[milliseconds] => Integer,\n    Optional[microseconds] => Integer,\n    Optional[nanoseconds] => Integer\n  }] $hash\n)\n```\n\n**Timespan from String and format directive patterns**\n\nThe first argument is parsed using the format optionally passed as a string or array of strings. When an array is used, an attempt\nwill be made to parse the string using the first entry and then with each entry in succession until parsing succeeds. If the second\nargument is omitted, an array of default formats will be used.\n\nAn exception is raised when no format was able to parse the given string.\n\n```puppet\nfunction Timespan.new(\n  String $string, Variant[String[2],Array[String[2], 1]] $format = <default format>)\n)\n```\n\nthe arguments may also be passed as a `Hash`:\n\n```puppet\nfunction Timespan.new(\n  Struct[{\n    string => String[1],\n    Optional[format] => Variant[String[2],Array[String[2], 1]]\n  }] $hash\n)\n```\n\nThe directive consists of a percent (`%`) character, zero or more flags, optional minimum field width and\na conversion specifier as follows:\n```\n%[Flags][Width]Conversion\n```\n\n**Flags:**\n\n| Flag  | Meaning\n| ----  | ---------------\n| -     | Don't pad numerical output\n| _     | Use spaces for padding\n| 0     | Use zeros for padding\n\n**Format directives:**\n\n| Format | Meaning |\n| ------ | ------- |\n| D | Number of Days |\n| H | Hour of the day, 24-hour clock |\n| M | Minute of the hour (00..59) |\n| S | Second of the minute (00..59) |\n| L | Millisecond of the second (000..999) |\n| N | Fractional seconds digits |\n\nThe format directive that represents the highest magnitude in the format will be allowed to\noverflow. I.e. if no \"%D\" is used but a \"%H\" is present, then the hours may be more than 23.\n\nThe default array contains the following patterns:\n\n```\n['%D-%H:%M:%S', '%D-%H:%M', '%H:%M:%S', '%H:%M']\n```\n\nExamples - Converting to Timespan\n\n```puppet\n$duration = Timespan(13.5)       # 13 seconds and 500 milliseconds\n$duration = Timespan({days=>4})  # 4 days\n$duration = Timespan(4, 0, 0, 2) # 4 days and 2 seconds\n$duration = Timespan('13:20')    # 13 hours and 20 minutes (using default pattern)\n$duration = Timespan('10:03.5', '%M:%S.%L') # 10 minutes, 3 seconds, and 5 milli-seconds\n$duration = Timespan('10:03.5', '%M:%S.%N') # 10 minutes, 3 seconds, and 5 nano-seconds\n```\n\n### Conversion to Timestamp\n\nA new `Timestamp` can be created from `Integer`, `Float`, `String`, and `Hash` values. Several variants of the constructor are provided.\n\n**Timestamp from seconds since epoch (1970-01-01 00:00:00 UTC)**\n\nWhen a Float is used, the decimal part represents fractions of a second.\n\n```puppet\nfunction Timestamp.new(\n  Variant[Float, Integer] $value\n)\n```\n\n**Timestamp from String and patterns consisting of format directives**\n\nThe first argument is parsed using the format optionally passed as a string or array of strings. When an array is used, an attempt\nwill be made to parse the string using the first entry and then with each entry in succession until parsing succeeds. If the second\nargument is omitted, an array of default formats will be used.\n\nA third optional timezone argument can be provided. The first argument will then be parsed as if it represents a local time in that\ntimezone. The timezone can be any timezone that is recognized when using the `'%z'` or `'%Z'` formats, or the word `'current'`, in which\ncase the current timezone of the evaluating process will be used. The timezone argument is case insensitive.\n\nThe default timezone, when no argument is provided, or when using the keyword `default`, is 'UTC'.\n\nIt is illegal to provide a timezone argument other than `default` in combination with a format that contains '%z' or '%Z' since that\nwould introduce an ambiguity as to which timezone to use. The one extracted from the string, or the one provided as an argument.\n\nAn exception is raised when no format was able to parse the given string.\n\n```puppet\nfunction Timestamp.new(\n  String $string,\n  Variant[String[2],Array[String[2], 1]] $format = <default format>,\n  String $timezone = default)\n)\n```\n\nthe arguments may also be passed as a `Hash`:\n\n```puppet\nfunction Timestamp.new(\n  Struct[{\n    string => String[1],\n    Optional[format] => Variant[String[2],Array[String[2], 1]],\n    Optional[timezone] => String[1]\n  }] $hash\n)\n```\n\nThe directive consists of a percent (%) character, zero or more flags, optional minimum field width and\na conversion specifier as follows:\n```\n%[Flags][Width]Conversion\n```\n\n**Flags:**\n\n| Flag  | Meaning\n| ----  | ---------------\n| -     | Don't pad numerical output\n| _     | Use spaces for padding\n| 0     | Use zeros for padding\n| #     | Change names to upper-case or change case of am/pm\n| ^     | Use uppercase\n| :     | Use colons for `%z`\n\n**Format directives (names and padding can be altered using flags):**\n\n**Date (Year, Month, Day):**\n\n| Format | Meaning |\n| ------ | ------- |\n| Y | Year with century, zero-padded to at least 4 digits |\n| C | year / 100 (rounded down such as `20` in `2009`) |\n| y | year % 100 (`00..99`) |\n| m | Month of the year, zero-padded (`01..12`) |\n| B | The full month name (`\"January\"`) |\n| b | The abbreviated month name (`\"Jan\"`) |\n| h | Equivalent to `%b` |\n| d | Day of the month, zero-padded (`01..31`) |\n| e | Day of the month, blank-padded (`1..31`) |\n| j | Day of the year (`001..366`) |\n\n**Time (Hour, Minute, Second, Subsecond):**\n\n| Format | Meaning |\n| ------ | ------- |\n| H | Hour of the day, 24-hour clock, zero-padded (`00..23`) |\n| k | Hour of the day, 24-hour clock, blank-padded (`0..23`) |\n| I | Hour of the day, 12-hour clock, zero-padded (`01..12`) |\n| l | Hour of the day, 12-hour clock, blank-padded (`1..12`) |\n| P | Meridian indicator, lowercase (`\"am\"` or `\"pm\"`) |\n| p | Meridian indicator, uppercase (`\"AM\"` or `\"PM\"`) |\n| M | Minute of the hour (`00..59`) |\n| S | Second of the minute (`00..60`) |\n| L | Millisecond of the second (`000..999`). Digits under millisecond are truncated to not produce 1000 |\n| N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified width are truncated to avoid carry up |\n\n**Time (Hour, Minute, Second, Subsecond):**\n\n| Format | Meaning |\n| ------ | ------- |\n| z   | Time zone as hour and minute offset from UTC (e.g. `+0900`) |\n| :z  | hour and minute offset from UTC with a colon (e.g. `+09:00`) |\n| ::z | hour, minute and second offset from UTC (e.g. `+09:00:00`) |\n| Z   | Abbreviated time zone name or similar information.  (OS dependent) |\n\n**Weekday:**\n\n| Format | Meaning |\n| ------ | ------- |\n| A | The full weekday name (`\"Sunday\"`) |\n| a | The abbreviated name (`\"Sun\"`) |\n| u | Day of the week (Monday is `1`, `1..7`) |\n| w | Day of the week (Sunday is `0`, `0..6`) |\n\n**ISO 8601 week-based year and week number:**\n\nThe first week of YYYY starts with a Monday and includes YYYY-01-04.\nThe days in the year before the first week are in the last week of\nthe previous year.\n\n| Format | Meaning |\n| ------ | ------- |\n| G | The week-based year |\n| g | The last 2 digits of the week-based year (`00..99`) |\n| V | Week number of the week-based year (`01..53`) |\n\n**Week number:**\n\nThe first week of YYYY that starts with a Sunday or Monday (according to %U\nor %W). The days in the year before the first week are in week 0.\n\n| Format | Meaning |\n| ------ | ------- |\n| U | Week number of the year. The week starts with Sunday. (`00..53`) |\n| W | Week number of the year. The week starts with Monday. (`00..53`) |\n\n**Seconds since the Epoch:**\n\n| Format | Meaning |\n| s | Number of seconds since 1970-01-01 00:00:00 UTC. |\n\n**Literal string:**\n\n| Format | Meaning |\n| ------ | ------- |\n| n | Newline character (`\\n`) |\n| t | Tab character (`\\t`) |\n| % | Literal `%` character |\n\n**Combination:**\n\n| Format | Meaning |\n| ------ | ------- |\n| c | date and time (`%a %b %e %T %Y`) |\n| D | Date (`%m/%d/%y`) |\n| F | The ISO 8601 date format (`%Y-%m-%d`) |\n| v | VMS date (`%e-%^b-%4Y`) |\n| x | Same as `%D` |\n| X | Same as `%T` |\n| r | 12-hour time (`%I:%M:%S %p`) |\n| R | 24-hour time (`%H:%M`) |\n| T | 24-hour time (`%H:%M:%S`) |\n\nThe default array contains the following patterns:\n\nWhen a timezone argument (other than `default`) is explicitly provided:\n\n```\n['%FT%T.L', '%FT%T', '%F']\n```\n\notherwise:\n\n```\n['%FT%T.%L %Z', '%FT%T %Z', '%F %Z', '%FT%T.L', '%FT%T', '%F']\n```\n\nExamples - Converting to Timestamp\n\n```puppet\n$ts = Timestamp(1473150899)                              # 2016-09-06 08:34:59 UTC\n$ts = Timestamp({string=>'2015', format=>'%Y'})          # 2015-01-01 00:00:00.000 UTC\n$ts = Timestamp('Wed Aug 24 12:13:14 2016', '%c')        # 2016-08-24 12:13:14 UTC\n$ts = Timestamp('Wed Aug 24 12:13:14 2016 PDT', '%c %Z') # 2016-08-24 19:13:14.000 UTC\n$ts = Timestamp('2016-08-24 12:13:14', '%F %T', 'PST')   # 2016-08-24 20:13:14.000 UTC\n$ts = Timestamp('2016-08-24T12:13:14', default, 'PST')   # 2016-08-24 20:13:14.000 UTC\n\n```\n\n### Conversion to Type\n\nA new `Type` can be created from its `String` representation.\n\n```puppet\n$t = Type.new('Integer[10]')\n```\n\n### Conversion to String\n\nConversion to `String` is the most comprehensive conversion as there are many\nuse cases where a string representation is wanted. The defaults for the many options\nhave been chosen with care to be the most basic \"value in textual form\" representation.\nThe more advanced forms of formatting are intended to enable writing special purposes formatting\nfunctions in the Puppet language.\n\nA new string can be created from all other data types. The process is performed in\nseveral steps - first the data type of the given value is inferred, then the resulting data type\nis used to find the most significant format specified for that data type. And finally,\nthe found format is used to convert the given value.\n\nThe mapping from data type to format is referred to as the *format map*. This map\nallows different formatting depending on type.\n\n```puppet\n$format_map = {\n  Integer[default, 0] => \"%d\",\n  Integer[1, default] => \"%#x\"\n}\nString(\"-1\", $format_map)  # produces '-1'\nString(\"10\", $format_map)  # produces '0xa'\n```\n\nA format is specified on the form:\n\n```\n%[Flags][Width][.Precision]Format\n```\n\n`Width` is the number of characters into which the value should be fitted. This allocated space is\npadded if value is shorter. By default it is space padded, and the flag `0` will cause padding with `0`\nfor numerical formats.\n\n`Precision` is the number of fractional digits to show for floating point, and the maximum characters\nincluded in a string format.\n\nNote that all data type supports the formats `s` and `p` with the meaning \"default string representation\" and\n\"default programmatic string representation\" (which for example means that a String is quoted in 'p' format).\n\n**Signatures of String conversion**\n\n```puppet\ntype Format = Pattern[/^%([\\s\\+\\-#0\\[\\{<\\(\\|]*)([1-9][0-9]*)?(?:\\.([0-9]+))?([a-zA-Z])/]\ntype ContainerFormat = Struct[{\n  format         => Optional[String],\n  separator      => Optional[String],\n  separator2     => Optional[String],\n  string_formats => Hash[Type, Format]\n  }]\ntype TypeMap = Hash[Type, Variant[Format, ContainerFormat]]\ntype Formats = Variant[Default, String[1], TypeMap]\n\nfunction String.new(\n  Any $value,\n  Formats $string_formats\n)\n```\n\nWhere:\n\n* `separator` is the string used to separate entries in an array, or hash (extra space should not be included at\n  the end), defaults to `\",\"`\n* `separator2` is the separator between key and value in a hash entry (space padding should be included as\n  wanted), defaults to `\" => \"`.\n* `string_formats` is a data type to format map for values contained in arrays and hashes - defaults to `{Any => \"%p\"}`. Note that\n  these nested formats are not applicable to data types that are containers; they are always formatted as per the top level\n  format specification.\n\n```puppet\n$str = String(10)      # produces '10'\n$str = String([10])    # produces '[\"10\"]'\n```\n\n```puppet\n$str = String(10, \"%#x\")    # produces '0xa'\n$str = String([10], \"%(a\")  # produces '(\"10\")'\n```\n\n```puppet\n$formats = {\n  Array => {\n    format => '%(a',\n    string_formats => { Integer => '%#x' }\n  }\n}\n$str = String([1,2,3], $formats) # produces '(0x1, 0x2, 0x3)'\n```\n\nThe given formats are merged with the default formats, and matching of values to convert against format is based on\nthe specificity of the mapped type; for example, different formats can be used for short and long arrays.\n\n**Integer to String**\n\n| Format  | Integer Formats\n| ------  | ---------------\n| d       | Decimal, negative values produces leading `-`.\n| x X     | Hexadecimal in lower or upper case. Uses `..f/..F` for negative values unless `+` is also used. A `#` adds prefix `0x/0X`.\n| o       | Octal. Uses `..0` for negative values unless `+` is also used. A `#` adds prefix `0`.\n| b B     | Binary with prefix `b` or `B`. Uses `..1/..1` for negative values unless `+` is also used.\n| c       | Numeric value representing a Unicode value, result is a one unicode character string, quoted if alternative flag `#` is used\n| s       | Same as `d`, or `d` in quotes if alternative flag `#` is used.\n| p       | Same as `d`.\n| eEfgGaA | Converts integer to float and formats using the floating point rules.\n\nDefaults to `d`.\n\n**Float to String**\n\n| Format  | Float formats\n| ------  | -------------\n| f       | Floating point in non exponential notation.\n| e E     | Exponential notation with `e` or `E`.\n| g G     | Conditional exponential with `e` or `E` if exponent `< -4` or `>=` the precision.\n| a A     | Hexadecimal exponential form, using `x`/`X` as prefix and `p`/`P` before exponent.\n| s       | Converted to string using format `p`, then applying string formatting rule, alternate form `#`` quotes result.\n| p       | Same as `f` format with minimum significant number of fractional digits, prec has no effect.\n| dxXobBc | Converts float to integer and formats using the integer rules.\n\nDefaults to `p`.\n\n**String to String**\n\n| Format | String\n| ------ | ------\n| s      | Unquoted string, verbatim output of control chars.\n| p      | Programmatic representation - strings are quoted, interior quotes and control chars are escaped. Selects single or double quotes based on content, or uses double quotes if alternative flag `#` is used.\n| C      | Each `::` name segment capitalized, quoted if alternative flag `#` is used.\n| c      | Capitalized string, quoted if alternative flag `#` is used.\n| d      | Downcased string, quoted if alternative flag `#` is used.\n| u      | Upcased string, quoted if alternative flag `#` is used.\n| t      | Trims leading and trailing whitespace from the string, quoted if alternative flag `#` is used.\n\nDefaults to `s` at top level and `p` inside array or hash.\n\n**Boolean to String**\n\n| Format    | Boolean Formats\n| ----      | -------------------\n| t T       | String `'true'/'false'` or `'True'/'False'`, first char if alternate form is used (i.e. `'t'/'f'` or `'T'/'F'`).\n| y Y       | String `'yes'/'no'`, `'Yes'/'No'`, `'y'/'n'` or `'Y'/'N'` if alternative flag `#` is used.\n| dxXobB    | Numeric value `0/1` in accordance with the given format which must be valid integer format.\n| eEfgGaA   | Numeric value `0.0/1.0` in accordance with the given float format and flags.\n| s         | String `'true'` / `'false'`.\n| p         | String `'true'` / `'false'`.\n\n**Regexp to String**\n\n| Format    | Regexp Formats\n| ----      | --------------\n| s         | No delimiters, quoted if alternative flag `#` is used.\n| p         | Delimiters `/ /`.\n\n**Undef to String**\n\n| Format    | Undef formats\n| ------    | -------------\n| s         | Empty string, or quoted empty string if alternative flag `#` is used.\n| p         | String `'undef'`, or quoted `'\"undef\"'` if alternative flag `#` is used.\n| n         | String `'nil'`, or `'null'` if alternative flag `#` is used.\n| dxXobB    | String `'NaN'`.\n| eEfgGaA   | String `'NaN'`.\n| v         | String `'n/a'`.\n| V         | String `'N/A'`.\n| u         | String `'undef'`, or `'undefined'` if alternative `#` flag is used.\n\n**Default value to String**\n\n| Format    | Default formats\n| ------    | ---------------\n| d D       | String `'default'` or `'Default'`, alternative form `#` causes value to be quoted.\n| s         | Same as `d`.\n| p         | Same as `d`.\n\n**Binary value to String**\n\n| Format    | Default formats\n| ------    | ---------------\n| s         | binary as unquoted UTF-8 characters (errors if byte sequence is invalid UTF-8). Alternate form escapes non ascii bytes.\n| p         | `'Binary(\"<base64strict>\")'`\n| b         | `'<base64>'` - base64 string with newlines inserted\n| B         | `'<base64strict>'` - base64 strict string (without newlines inserted)\n| u         | `'<base64urlsafe>'` - base64 urlsafe string\n| t         | `'Binary'` - outputs the name of the type only\n| T         | `'BINARY'` - output the name of the type in all caps only\n\n* The alternate form flag `#` will quote the binary or base64 text output.\n* The format `%#s` allows invalid UTF-8 characters and outputs all non ascii bytes\n  as hex escaped characters on the form `\\\\xHH` where `H` is a hex digit.\n* The width and precision values are applied to the text part only in `%p` format.\n\n**Array & Tuple to String**\n\n| Format    | Array/Tuple Formats\n| ------    | -------------\n| a         | Formats with `[ ]` delimiters and `,`, alternate form `#` indents nested arrays/hashes.\n| s         | Same as `a`.\n| p         | Same as `a`.\n\nSee \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\nmore information about options.\n\nThe alternate form flag `#` will cause indentation of nested array or hash containers. If width is also set\nit is taken as the maximum allowed length of a sequence of elements (not including delimiters). If this max length\nis exceeded, each element will be indented.\n\n**Hash & Struct to String**\n\n| Format    | Hash/Struct Formats\n| ------    | -------------\n| h         | Formats with `{ }` delimiters, `,` element separator and ` => ` inner element separator unless overridden by flags.\n| s         | Same as h.\n| p         | Same as h.\n| a         | Converts the hash to an array of `[k,v]` tuples and formats it using array rule(s).\n\nSee \"Flags\" `<[({\\|` for formatting of delimiters, and \"Additional parameters for containers; Array and Hash\" for\nmore information about options.\n\nThe alternate form flag `#` will format each hash key/value entry indented on a separate line.\n\n**Type to String**\n\n| Format    | Array/Tuple Formats\n| ------    | -------------\n| s         | The same as `p`, quoted if alternative flag `#` is used.\n| p         | Outputs the type in string form as specified by the Puppet Language.\n\n**Flags**\n\n| Flag     | Effect\n| ------   | ------\n| (space)  | A space instead of `+` for numeric output (`-` is shown), for containers skips delimiters.\n| #        | Alternate format; prefix `0x/0x`, `0` (octal) and `0b/0B` for binary, Floats force decimal '.'. For g/G keep trailing `0`.\n| +        | Show sign `+/-` depending on value's sign, changes `x`, `X`, `o`, `b`, `B` format to not use 2's complement form.\n| -        | Left justify the value in the given width.\n| 0        | Pad with `0` instead of space for widths larger than value.\n| <[({\\|   | Defines an enclosing pair `<> [] () {} or \\| \\|` when used with a container type.\n\n### Conversion to Boolean\n\nAccepts a single value as argument:\n\n* Float `0.0` is `false`, all other float values are `true`\n* Integer `0` is `false`, all other integer values are `true`\n* Strings\n  * `true` if 'true', 'yes', 'y' (case independent compare)\n  * `false` if 'false', 'no', 'n' (case independent compare)\n* Boolean is already boolean and is simply returned\n\n### Conversion to Array and Tuple\n\nWhen given a single value as argument:\n\n* A non empty `Hash` is converted to an array matching `Array[Tuple[Any,Any], 1]`.\n* An empty `Hash` becomes an empty array.\n* An `Array` is simply returned.\n* An `Iterable[T]` is turned into an array of `T` instances.\n* A `Binary` is converted to an `Array[Integer[0,255]]` of byte values\n\nWhen given a second Boolean argument:\n\n* if `true`, a value that is not already an array is returned as a one element array.\n* if `false`, (the default), converts the first argument as shown above.\n\n```puppet\n$arr = Array($value, true)\n```\n\nConversion to a `Tuple` works exactly as conversion to an `Array`, only that the constructed array is\nasserted against the given tuple type.\n\n### Conversion to Hash and Struct\n\nAccepts a single value as argument:\n\n* An empty `Array` becomes an empty `Hash`\n* An `Array` matching `Array[Tuple[Any,Any], 1]` is converted to a hash where each tuple describes a key/value entry\n* An `Array` with an even number of entries is interpreted as `[key1, val1, key2, val2, ...]`\n* An `Iterable` is turned into an `Array` and then converted to hash as per the array rules\n* A `Hash` is simply returned\n\nAlternatively, a tree can be constructed by giving two values; an array of tuples on the form `[path, value]`\n(where the `path` is the path from the root of a tree, and `value` the value at that position in the tree), and\neither the option `'tree'` (do not convert arrays to hashes except the top level), or\n`'hash_tree'` (convert all arrays to hashes).\n\nThe tree/hash_tree forms of Hash creation are suited for transforming the result of an iteration\nusing `tree_each` and subsequent filtering or mapping.\n\nMapping an arbitrary structure in a way that keeps the structure, but where some values are replaced\ncan be done by using the `tree_each` function, mapping, and then constructing a new Hash from the result:\n\n```puppet\n# A hash tree with 'water' at different locations\n$h = { a => { b => { x => 'water'}}, b => { y => 'water'} }\n# a helper function that turns water into wine\nfunction make_wine($x) { if $x == 'water' { 'wine' } else { $x } }\n# create a flattened tree with water turned into wine\n$flat_tree = $h.tree_each.map |$entry| { [$entry[0], make_wine($entry[1])] }\n# create a new Hash and log it\nnotice Hash($flat_tree, 'hash_tree')\n```\n\nWould notice the hash `{a => {b => {x => wine}}, b => {y => wine}}`\n\nConversion to a `Struct` works exactly as conversion to a `Hash`, only that the constructed hash is\nasserted against the given struct type.\n\n### Conversion to a Regexp\n\nA `String` can be converted into a `Regexp`\n\n**Example**: Converting a String into a Regexp\n```puppet\n$s = '[a-z]+\\.com'\n$r = Regexp($s)\nif('foo.com' =~ $r) {\n  ...\n}\n```\n\n### Creating a SemVer\n\nA SemVer object represents a single [Semantic Version](http://semver.org/).\nIt can be created from a String, individual values for its parts, or a hash specifying the value per part.\nSee the specification at [semver.org](http://semver.org/) for the meaning of the SemVer's parts.\n\nThe signatures are:\n\n```puppet\ntype PositiveInteger = Integer[0,default]\ntype SemVerQualifier = Pattern[/\\A(?<part>[0-9A-Za-z-]+)(?:\\.\\g<part>)*\\Z/]\ntype SemVerString = String[1]\ntype SemVerHash =Struct[{\n  major                => PositiveInteger,\n  minor                => PositiveInteger,\n  patch                => PositiveInteger,\n  Optional[prerelease] => SemVerQualifier,\n  Optional[build]      => SemVerQualifier\n}]\n\nfunction SemVer.new(SemVerString $str)\n\nfunction SemVer.new(\n        PositiveInteger           $major\n        PositiveInteger           $minor\n        PositiveInteger           $patch\n        Optional[SemVerQualifier] $prerelease = undef\n        Optional[SemVerQualifier] $build = undef\n        )\n\nfunction SemVer.new(SemVerHash $hash_args)\n```\n\n```puppet\n# As a type, SemVer can describe disjunct ranges which versions can be\n# matched against - here the type is constructed with two\n# SemVerRange objects.\n#\n$t = SemVer[\n  SemVerRange('>=1.0.0 <2.0.0'),\n  SemVerRange('>=3.0.0 <4.0.0')\n]\nnotice(SemVer('1.2.3') =~ $t) # true\nnotice(SemVer('2.3.4') =~ $t) # false\nnotice(SemVer('3.4.5') =~ $t) # true\n```\n\n### Creating a `SemVerRange`\n\nA `SemVerRange` object represents a range of `SemVer`. It can be created from\na `String`, or from two `SemVer` instances, where either end can be given as\na literal `default` to indicate infinity. The string format of a `SemVerRange` is specified by\nthe [Semantic Version Range Grammar](https://github.com/npm/node-semver#ranges).\n\n> Use of the comparator sets described in the grammar (joining with `||`) is not supported.\n\nThe signatures are:\n\n```puppet\ntype SemVerRangeString = String[1]\ntype SemVerRangeHash = Struct[{\n  min                   => Variant[Default, SemVer],\n  Optional[max]         => Variant[Default, SemVer],\n  Optional[exclude_max] => Boolean\n}]\n\nfunction SemVerRange.new(\n  SemVerRangeString $semver_range_string\n)\n\nfunction SemVerRange.new(\n  Variant[Default,SemVer] $min\n  Variant[Default,SemVer] $max\n  Optional[Boolean]       $exclude_max = undef\n)\n\nfunction SemVerRange.new(\n  SemVerRangeHash $semver_range_hash\n)\n```\n\nFor examples of `SemVerRange` use see \"Creating a SemVer\"\n\n### Creating a Binary\n\nA `Binary` object represents a sequence of bytes and it can be created from a String in Base64 format,\nan Array containing byte values. A Binary can also be created from a Hash containing the value to convert to\na `Binary`.\n\nThe signatures are:\n\n```puppet\ntype ByteInteger = Integer[0,255]\ntype Base64Format = Enum[\"%b\", \"%u\", \"%B\", \"%s\"]\ntype StringHash = Struct[{value => String, \"format\" => Optional[Base64Format]}]\ntype ArrayHash = Struct[{value => Array[ByteInteger]}]\ntype BinaryArgsHash = Variant[StringHash, ArrayHash]\n\nfunction Binary.new(\n  String $base64_str,\n  Optional[Base64Format] $format\n)\n\n\nfunction Binary.new(\n  Array[ByteInteger] $byte_array\n}\n\n# Same as for String, or for Array, but where arguments are given in a Hash.\nfunction Binary.new(BinaryArgsHash $hash_args)\n```\n\nThe formats have the following meaning:\n\n| format | explanation |\n| ----   | ----        |\n| B | The data is in base64 strict encoding\n| u | The data is in URL safe base64 encoding\n| b | The data is in base64 encoding, padding as required by base64 strict, is added by default\n| s | The data is a puppet string. The string must be valid UTF-8, or convertible to UTF-8 or an error is raised.\n| r | (Ruby Raw) the byte sequence in the given string is used verbatim irrespective of possible encoding errors\n\n* The default format is `%B`.\n* Note that the format `%r` should be used sparingly, or not at all. It exists for backwards compatibility reasons when someone receiving\n  a string from some function and that string should be treated as Binary. Such code should be changed to return a Binary instead of a String.\n\n```puppet\n# create the binary content \"abc\"\n$a = Binary('YWJj')\n\n# create the binary content from content in a module's file\n$b = binary_file('mymodule/mypicture.jpg')\n```\n\n* Since 4.5.0\n* Binary type since 4.8.0\n\n### Creating an instance of a `Type` using the `Init` type\n\nThe type `Init[T]` describes a value that can be used when instantiating a type. When used as the first argument in a call to `new`, it\nwill dispatch the call to its contained type and optionally augment the parameter list with additional arguments.\n\n```puppet\n# The following declaration\n$x = Init[Integer].new('128')\n# is exactly the same as\n$x = Integer.new('128')\n```\n\nor, with base 16 and using implicit new\n\n```puppet\n# The following declaration\n$x = Init[Integer,16]('80')\n# is exactly the same as\n$x = Integer('80', 16)\n```\n\n```puppet\n$fmt = Init[String,'%#x']\nnotice($fmt(256)) # will notice '0x100'\n```\n\n\n`new(Type $type, Any *$args, Optional[Callable] &$block)`\n\n## `next`\n\nMakes iteration continue with the next value, optionally with a given value for this iteration.\nIf a value is not given it defaults to `undef`\n\n```puppet\n$data = ['a','b','c']\n$data.each |Integer $index, String $value| {\n  if $index == 1 {\n    next()\n  }\n  notice (\"${index} = ${value}\")\n}\n```\n\nWould notice:\n```\nNotice: Scope(Class[main]): 0 = a\nNotice: Scope(Class[main]): 2 = c\n```\n\n\n`next(Optional[Any] $value)`\n\n## `notice`\n\nLogs a message on the server at level `notice`.\n\n\n`notice(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `partition`\n\nReturns two arrays, the first containing the elements of enum for which the block evaluates to true,\nthe second containing the rest.\n\n\nSignature 1\n\n`partition(Collection $collection, Callable[1,1] &$block)`\n\n### Parameters\n\n\n* `collection` --- A collection of things to partition.\n\nReturn type(s): `Tuple[Array, Array]`. \n\n\n### Examples\n\nPartition array of empty strings, results in e.g. `[[''], [b, c]]`\n\n```puppet\n['', b, c].partition |$s| { $s.empty }\n```\n\nPartition array of strings using index, results in e.g. `[['', 'ab'], ['b']]`\n\n```puppet\n['', b, ab].partition |$i, $s| { $i == 2 or $s.empty }\n```\n\nPartition hash of strings by key-value pair, results in e.g. `[[['b', []]], [['a', [1, 2]]]]`\n\n```puppet\n{ a => [1, 2], b => [] }.partition |$kv| { $kv[1].empty }\n```\n\nPartition hash of strings by key and value, results in e.g. `[[['b', []]], [['a', [1, 2]]]]`\n\n```puppet\n{ a => [1, 2], b => [] }.partition |$k, $v| { $v.empty }\n```\n\n\nSignature 2\n\n`partition(Array $array, Callable[2,2] &$block)`\n\nSignature 3\n\n`partition(Collection $collection, Callable[2,2] &$block)`\n\n## `realize`\n\nMake a virtual object real.  This is useful\nwhen you want to know the name of the virtual object and don't want to\nbother with a full collection.  It is slightly faster than a collection,\nand, of course, is a bit shorter.  You must pass the object using a\nreference; e.g.: `realize User[luke]`.\n\n\n`realize()`\n\n## `reduce`\n\nApplies a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nto every value in a data structure from the first argument, carrying over the returned\nvalue of each iteration, and returns the result of the lambda's final iteration. This\nlets you create a new value or data structure by combining values from the first\nargument's data structure.\n\nThis function takes two mandatory arguments, in this order:\n\n1. An array, hash, or other iterable object that the function will iterate over.\n2. A lambda, which the function calls for each element in the first argument. It takes\ntwo mandatory parameters:\n    1. A memo value that is overwritten after each iteration with the iteration's result.\n    2. A second value that is overwritten after each iteration with the next value in the\n    function's first argument.\n\n`$data.reduce |$memo, $value| { ... }`\n\nor\n\n`reduce($data) |$memo, $value| { ... }`\n\nYou can also pass an optional \"start memo\" value as an argument, such as `start` below:\n\n`$data.reduce(start) |$memo, $value| { ... }`\n\nor\n\n`reduce($data, start) |$memo, $value| { ... }`\n\nWhen the first argument (`$data` in the above example) is an array, Puppet passes each\nof the data structure's values in turn to the lambda's parameters. When the first\nargument is a hash, Puppet converts each of the hash's values to an array in the form\n`[key, value]`.\n\nIf you pass a start memo value, Puppet executes the lambda with the provided memo value\nand the data structure's first value. Otherwise, Puppet passes the structure's first two\nvalues to the lambda.\n\nPuppet calls the lambda for each of the data structure's remaining values. For each\ncall, it passes the result of the previous call as the first parameter (`$memo` in the\nabove examples) and the next value from the data structure as the second parameter\n(`$value`).\n\n```puppet\n# Reduce the array $data, returning the sum of all values in the array.\n$data = [1, 2, 3]\n$sum = $data.reduce |$memo, $value| { $memo + $value }\n# $sum contains 6\n\n# Reduce the array $data, returning the sum of a start memo value and all values in the\n# array.\n$data = [1, 2, 3]\n$sum = $data.reduce(4) |$memo, $value| { $memo + $value }\n# $sum contains 10\n\n# Reduce the hash $data, returning the sum of all values and concatenated string of all\n# keys.\n$data = {a => 1, b => 2, c => 3}\n$combine = $data.reduce |$memo, $value| {\n  $string = \"${memo[0]}${value[0]}\"\n  $number = $memo[1] + $value[1]\n  [$string, $number]\n}\n# $combine contains [abc, 6]\n```\n\n```puppet\n# Reduce the array $data, returning the sum of all values in the array and starting\n# with $memo set to an arbitrary value instead of $data's first value.\n$data = [1, 2, 3]\n$sum = $data.reduce(4) |$memo, $value| { $memo + $value }\n# At the start of the lambda's first iteration, $memo contains 4 and $value contains 1.\n# After all iterations, $sum contains 10.\n\n# Reduce the hash $data, returning the sum of all values and concatenated string of\n# all keys, and starting with $memo set to an arbitrary array instead of $data's first\n# key-value pair.\n$data = {a => 1, b => 2, c => 3}\n$combine = $data.reduce( [d, 4] ) |$memo, $value| {\n  $string = \"${memo[0]}${value[0]}\"\n  $number = $memo[1] + $value[1]\n  [$string, $number]\n}\n# At the start of the lambda's first iteration, $memo contains [d, 4] and $value\n# contains [a, 1].\n# $combine contains [dabc, 10]\n```\n\n```puppet\n# Reduce a hash of hashes $data, merging defaults into the inner hashes.\n$data = {\n  'connection1' => {\n    'username' => 'user1',\n    'password' => 'pass1',\n  },\n  'connection_name2' => {\n    'username' => 'user2',\n    'password' => 'pass2',\n  },\n}\n\n$defaults = {\n  'maxActive' => '20',\n  'maxWait'   => '10000',\n  'username'  => 'defaultuser',\n  'password'  => 'defaultpass',\n}\n\n$merged = $data.reduce( {} ) |$memo, $x| {\n  $memo + { $x[0] => $defaults + $data[$x[0]] }\n}\n# At the start of the lambda's first iteration, $memo is set to {}, and $x is set to\n# the first [key, value] tuple. The key in $data is, therefore, given by $x[0]. In\n# subsequent rounds, $memo retains the value returned by the expression, i.e.\n# $memo + { $x[0] => $defaults + $data[$x[0]] }.\n```\n\n\nSignature 1\n\n`reduce(Iterable $enumerable, Callable[2,2] &$block)`\n\nSignature 2\n\n`reduce(Iterable $enumerable, Any $memo, Callable[2,2] &$block)`\n\n## `regsubst`\n\nPerforms regexp replacement on a string or array of strings.\n\n\nSignature 1\n\n`regsubst(Variant[Array[Variant[String,Sensitive[String]]],Sensitive[Array[Variant[String,Sensitive[String]]]],Variant[String,Sensitive[String]]] $target, String $pattern, Variant[String,Hash[String,String]] $replacement, Optional[Optional[Pattern[/^[GEIM]*$/]]] $flags, Optional[Enum['N','E','S','U']] $encoding)`\n\n### Parameters\n\n\n* `target` --- The string or array of strings to operate on.  If an array, the replacement will be\nperformed on each of the elements in the array, and the return value will be an array.\n\n* `pattern` --- The regular expression matching the target string.  If you want it anchored at the start\nand or end of the string, you must do that with ^ and $ yourself.\n\n* `replacement` --- Replacement string. Can contain backreferences to what was matched using \\\\0 (whole match),\n\\\\1 (first set of parentheses), and so on.\nIf the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.\n\n* `flags` --- Optional. String of single letter flags for how the regexp is interpreted (E, I, and M cannot be used\nif pattern is a precompiled regexp):\n  - *E*         Extended regexps\n  - *I*         Ignore case in regexps\n  - *M*         Multiline regexps\n  - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.\n\n* `encoding` --- Deprecated and ignored parameter, included only for compatibility.\n\nReturn type(s): `Array[String]`, `String`. The result of the substitution. Result type is the same as for the target parameter.\n\n\n### Examples\n\nGet the third octet from the node's IP address:\n\n```puppet\n$i3 = regsubst($ipaddress,'^(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)$','\\\\3')\n```\n\n\nSignature 2\n\n`regsubst(Variant[Array[Variant[String,Sensitive[String]]],Sensitive[Array[Variant[String,Sensitive[String]]]],Variant[String,Sensitive[String]]] $target, Variant[Regexp,Type[Regexp]] $pattern, Variant[String,Hash[String,String]] $replacement, Optional[Pattern[/^G?$/]] $flags)`\n\n### Parameters\n\n\n* `target` --- The string or array of strings to operate on.  If an array, the replacement will be\nperformed on each of the elements in the array, and the return value will be an array.\n\n* `pattern` --- The regular expression matching the target string.  If you want it anchored at the start\nand or end of the string, you must do that with ^ and $ yourself.\n\n* `replacement` --- Replacement string. Can contain backreferences to what was matched using \\\\0 (whole match),\n\\\\1 (first set of parentheses), and so on.\nIf the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.\n\n* `flags` --- Optional. String of single letter flags for how the regexp is interpreted (E, I, and M cannot be used\nif pattern is a precompiled regexp):\n  - *E*         Extended regexps\n  - *I*         Ignore case in regexps\n  - *M*         Multiline regexps\n  - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.\n\nReturn type(s): `Array[String]`, `String`. The result of the substitution. Result type is the same as for the target parameter.\n\n\n### Examples\n\nPut angle brackets around each octet in the node's IP address:\n\n```puppet\n$x = regsubst($ipaddress, /([0-9]+)/, '<\\\\1>', 'G')\n```\n\n\n## `require`\n\nRequires the specified classes.\nEvaluate one or more classes, adding the required class as a dependency.\n\nThe relationship metaparameters work well for specifying relationships\nbetween individual resources, but they can be clumsy for specifying\nrelationships between classes.  This function is a superset of the\n`include` function, adding a class relationship so that the requiring\nclass depends on the required class.\n\nWarning: using `require` in place of `include` can lead to unwanted dependency cycles.\n\nFor instance, the following manifest, with `require` instead of `include`, would produce a nasty\ndependence cycle, because `notify` imposes a `before` between `File[/foo]` and `Service[foo]`:\n\n```puppet\nclass myservice {\n  service { foo: ensure => running }\n}\n\nclass otherstuff {\n   include myservice\n   file { '/foo': notify => Service[foo] }\n}\n```\n\nNote that this function only works with clients 0.25 and later, and it will\nfail if used with earlier clients.\n\nYou must use the class's full name;\nrelative names are not allowed. In addition to names in string form,\nyou may also directly use Class and Resource Type values that are produced when evaluating\nresource and relationship expressions.\n\n- Since 4.0.0 Class and Resource types, absolute names\n- Since 4.7.0 Returns an `Array[Type[Class]]` with references to the required classes\n\n\n`require(Any *$names)`\n\n## `return`\n\nMakes iteration continue with the next value, optionally with a given value for this iteration.\nIf a value is not given it defaults to `undef`\n\n\n`return(Optional[Any] $value)`\n\n## `reverse_each`\n\nReverses the order of the elements of something that is iterable and optionally runs a\n[lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html) for each\nelement.\n\nThis function takes one to two arguments:\n\n1. An `Iterable` that the function will iterate over.\n2. An optional lambda, which the function calls for each element in the first argument. It must\n   request one parameter.\n\n```puppet\n$data.reverse_each |$parameter| { <PUPPET CODE BLOCK> }\n```\n\nor\n\n```puppet\n$reverse_data = $data.reverse_each\n```\n\nor\n\n```puppet\nreverse_each($data) |$parameter| { <PUPPET CODE BLOCK> }\n```\n\nor\n\n```puppet\n$reverse_data = reverse_each($data)\n```\n\nWhen no second argument is present, Puppet returns an `Iterable` that represents the reverse\norder of its first argument. This allows methods on `Iterable` to be chained.\n\nWhen a lambda is given as the second argument, Puppet iterates the first argument in reverse\norder and passes each value in turn to the lambda, then returns `undef`.\n\n```puppet\n# Puppet will log a notice for each of the three items\n# in $data in reverse order.\n$data = [1,2,3]\n$data.reverse_each |$item| { notice($item) }\n```\n\nWhen no second argument is present, Puppet returns a new `Iterable` which allows it to\nbe directly chained into another function that takes an `Iterable` as an argument.\n\n```puppet\n# For the array $data, return an array containing each\n# value multiplied by 10 in reverse order\n$data = [1,2,3]\n$transformed_data = $data.reverse_each.map |$item| { $item * 10 }\n# $transformed_data is set to [30,20,10]\n```\n\n```puppet\n# For the array $data, return an array containing each\n# value multiplied by 10 in reverse order\n$data = [1,2,3]\n$transformed_data = map(reverse_each($data)) |$item| { $item * 10 }\n# $transformed_data is set to [30,20,10]\n```\n\n\nSignature 1\n\n`reverse_each(Iterable $iterable)`\n\nSignature 2\n\n`reverse_each(Iterable $iterable, Callable[1,1] &$block)`\n\n## `round`\n\nReturns an `Integer` value rounded to the nearest value.\nTakes a single `Numeric` value as an argument.\n\n```puppet\nnotice(round(2.9)) # would notice 3\nnotice(round(2.1)) # would notice 2\nnotice(round(-2.9)) # would notice -3\n```\n\n\n`round(Numeric $val)`\n\n## `rstrip`\n\nStrips trailing spaces from a String\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String` the conversion removes all trailing ASCII white space characters such as space, tab, newline, and return.\n  It does not remove other space-like characters like hard space (Unicode U+00A0). (Tip, `/^[[:space:]]/` regular expression\n  matches all space-like characters).\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\n```puppet\n\" hello\\n\\t\".rstrip()\nrstrip(\" hello\\n\\t\")\n```\nWould both result in `\"hello\"`\n\n```puppet\n[\" hello\\n\\t\", \" hi\\n\\t\"].rstrip()\nrstrip([\" hello\\n\\t\", \" hi\\n\\t\"])\n```\nWould both result in `['hello', 'hi']`\n\n\nSignature 1\n\n`rstrip(Numeric $arg)`\n\nSignature 2\n\n`rstrip(String $arg)`\n\nSignature 3\n\n`rstrip(Iterable[Variant[String, Numeric]] $arg)`\n\n## `scanf`\n\nScans a string and returns an array of one or more converted values based on the given format string.\nSee the documentation of Ruby's String#scanf method for details about the supported formats (which\nare similar but not identical to the formats used in Puppet's `sprintf` function.)\n\nThis function takes two mandatory arguments: the first is the string to convert, and the second is\nthe format string. The result of the scan is an array, with each successfully scanned and transformed value.\nThe scanning stops if a scan is unsuccessful, and the scanned result up to that point is returned. If there\nwas no successful scan, the result is an empty array.\n\n   \"42\".scanf(\"%i\")\n\nYou can also optionally pass a lambda to scanf, to do additional validation or processing.\n\n\n    \"42\".scanf(\"%i\") |$x| {\n      unless $x[0] =~ Integer {\n        fail \"Expected a well formed integer value, got '$x[0]'\"\n      }\n      $x[0]\n    }\n\n\n`scanf(String $data, String $format, Optional[Callable] &$block)`\n\n## `sha1`\n\nReturns a SHA1 hash value from a provided string.\n\n\n`sha1()`\n\n## `sha256`\n\nReturns a SHA256 hash value from a provided string.\n\n\n`sha256()`\n\n## `shellquote`\n\n\\\nQuote and concatenate arguments for use in Bourne shell.\n\nEach argument is quoted separately, and then all are concatenated\nwith spaces.  If an argument is an array, the elements of that\narray is interpolated within the rest of the arguments; this makes\nit possible to have an array of arguments and pass that array to\nshellquote instead of having to specify each argument\nindividually in the call.\n\n\n`shellquote()`\n\n## `size`\n\nThe same as length() - returns the size of an Array, Hash, String, or Binary value.\n\n\n`size(Variant[Collection, String, Binary] $arg)`\n\n## `slice`\n\nSlices an array or hash into pieces of a given size.\n\nThis function takes two mandatory arguments: the first should be an array or hash, and the second specifies\nthe number of elements to include in each slice.\n\nWhen the first argument is a hash, each key value pair is counted as one. For example, a slice size of 2 will produce\nan array of two arrays with key, and value.\n\n```puppet\n$a.slice(2) |$entry|          { notice \"first ${$entry[0]}, second ${$entry[1]}\" }\n$a.slice(2) |$first, $second| { notice \"first ${first}, second ${second}\" }\n```\nThe function produces a concatenated result of the slices.\n\n```puppet\nslice([1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]]\nslice(Integer[1,6], 2)  # produces [[1,2], [3,4], [5,6]]\nslice(4,2)              # produces [[0,1], [2,3]]\nslice('hello',2)        # produces [[h, e], [l, l], [o]]\n```\n\n```puppet\n $a.slice($n) |$x| { ... }\n slice($a) |$x| { ... }\n```\n\nThe lambda should have either one parameter (receiving an array with the slice), or the same number\nof parameters as specified by the slice size (each parameter receiving its part of the slice).\nIf there are fewer remaining elements than the slice size for the last slice, it will contain the remaining\nelements. If the lambda has multiple parameters, excess parameters are set to undef for an array, or\nto empty arrays for a hash.\n\n```puppet\n    $a.slice(2) |$first, $second| { ... }\n```\n\n\nSignature 1\n\n`slice(Hash[Any, Any] $hash, Integer[1, default] $slice_size, Optional[Callable] &$block)`\n\nSignature 2\n\n`slice(Iterable $enumerable, Integer[1, default] $slice_size, Optional[Callable] &$block)`\n\n## `sort`\n\nSorts an Array numerically or lexicographically or the characters of a String lexicographically.\nPlease note: This function is based on Ruby String comparison and as such may not be entirely UTF8 compatible.\nTo ensure compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n\nThis function is compatible with the function `sort()` in `stdlib`.\n* Comparison of characters in a string always uses a system locale and may not be what is expected for a particular locale\n* Sorting is based on Ruby's `<=>` operator unless a lambda is given that performs the comparison.\n  * comparison of strings is case dependent (use lambda with `compare($a,$b)` to ignore case)\n  * comparison of mixed data types raises an error (if there is the need to sort mixed data types use a lambda)\n\nAlso see the `compare()` function for information about comparable data types in general.\n\n```puppet\nnotice(sort(\"xadb\")) # notices 'abdx'\n```\n\n```puppet\nnotice(sort([3,6,2])) # notices [2, 3, 6]\n```\n\n```puppet\nnotice(sort([3,6,2]) |$a,$b| { compare($a, $b) }) # notices [2, 3, 6]\nnotice(sort([3,6,2]) |$a,$b| { compare($b, $a) }) # notices [6, 3, 2]\n```\n\n```puppet\nnotice(sort(['A','b','C']))                                    # notices ['A', 'C', 'b']\nnotice(sort(['A','b','C']) |$a,$b| { compare($a, $b) })        # notices ['A', 'b', 'C']\nnotice(sort(['A','b','C']) |$a,$b| { compare($a, $b, true) })  # notices ['A', 'b', 'C']\nnotice(sort(['A','b','C']) |$a,$b| { compare($a, $b, false) }) # notices ['A','C', 'b']\n```\n\n```puppet\nnotice(sort(['b', 3, 'a', 2]) |$a, $b| {\n  case [$a, $b] {\n    [String, Numeric] : { 1 }\n    [Numeric, String] : { -1 }\n    default:            { compare($a, $b) }\n  }\n})\n```\nWould notice `[2,3,'a','b']`\n\n\nSignature 1\n\n`sort(String $string_value, Optional[Callable[2,2]] &$block)`\n\nSignature 2\n\n`sort(Array $array_value, Optional[Callable[2,2]] &$block)`\n\n## `split`\n\nSplits a string into an array using a given pattern.\nThe pattern can be a string, regexp or regexp type.\n\n```puppet\n$string     = 'v1.v2:v3.v4'\n$array_var1 = split($string, /:/)\n$array_var2 = split($string, '[.]')\n$array_var3 = split($string, Regexp['[.:]'])\n\n#`$array_var1` now holds the result `['v1.v2', 'v3.v4']`,\n# while `$array_var2` holds `['v1', 'v2:v3', 'v4']`, and\n# `$array_var3` holds `['v1', 'v2', 'v3', 'v4']`.\n```\n\nNote that in the second example, we split on a literal string that contains\na regexp meta-character (`.`), which must be escaped.  A simple\nway to do that for a single character is to enclose it in square\nbrackets; a backslash will also escape a single character.\n\n\nSignature 1\n\n`split(String $str, String $pattern)`\n\nSignature 2\n\n`split(String $str, Regexp $pattern)`\n\nSignature 3\n\n`split(String $str, Type[Regexp] $pattern)`\n\nSignature 4\n\n`split(Sensitive[String] $sensitive, String $pattern)`\n\nSignature 5\n\n`split(Sensitive[String] $sensitive, Regexp $pattern)`\n\nSignature 6\n\n`split(Sensitive[String] $sensitive, Type[Regexp] $pattern)`\n\n## `sprintf`\n\nPerform printf-style formatting of text.\n\nThe first parameter is format string describing how the rest of the parameters should be formatted.\nSee the documentation for the [`Kernel::sprintf` function](https://ruby-doc.org/core/Kernel.html)\nin Ruby for details.\n\nTo use [named format](https://idiosyncratic-ruby.com/49-what-the-format.html) arguments, provide a\nhash containing the target string values as the argument to be formatted. For example:\n\n```puppet\nnotice sprintf(\\\"%<x>s : %<y>d\\\", { 'x' => 'value is', 'y' => 42 })\n```\n\nThis statement produces a notice of `value is : 42`.\n\n\n`sprintf()`\n\n## `step`\n\nWhen no block is given, Puppet returns a new `Iterable` which allows it to be directly chained into\nanother function that takes an `Iterable` as an argument.\n\n```puppet\n# For the array $data, return an array, set to the first element and each 5th successor element, in reverse\n# order multiplied by 10\n$data = Integer[0,20]\n$transformed_data = $data.step(5).map |$item| { $item * 10 }\n$transformed_data contains [0,50,100,150,200]\n```\n\n```puppet\n# For the array $data, return an array, set to the first and each 5th\n# successor, in reverse order, multiplied by 10\n$data = Integer[0,20]\n$transformed_data = map(step($data, 5)) |$item| { $item * 10 }\n$transformed_data contains [0,50,100,150,200]\n```\n\n\nSignature 1\n\n`step(Iterable $iterable, Integer[1] $step)`\n\nSignature 2\n\n`step(Iterable $iterable, Integer[1] $step, Callable[1,1] &$block)`\n\n## `strftime`\n\nFormats timestamp or timespan according to the directives in the given format string. The directives begins with a percent (%) character.\nAny text not listed as a directive will be passed through to the output string.\n\nA third optional timezone argument can be provided. The first argument will then be formatted to represent a local time in that\ntimezone. The timezone can be any timezone that is recognized when using the '%z' or '%Z' formats, or the word 'current', in which\ncase the current timezone of the evaluating process will be used. The timezone argument is case insensitive.\n\nThe default timezone, when no argument is provided, or when using the keyword `default`, is 'UTC'.\n\nThe directive consists of a percent (%) character, zero or more flags, optional minimum field width and\na conversion specifier as follows:\n\n```\n%[Flags][Width]Conversion\n```\n\n### Flags that controls padding\n\n| Flag  | Meaning\n| ----  | ---------------\n| -     | Don't pad numerical output\n| _     | Use spaces for padding\n| 0     | Use zeros for padding\n\n### `Timestamp` specific flags\n\n| Flag  | Meaning\n| ----  | ---------------\n| #     | Change case\n| ^     | Use uppercase\n| :     | Use colons for %z\n\n### Format directives applicable to `Timestamp` (names and padding can be altered using flags):\n\n**Date (Year, Month, Day):**\n\n| Format | Meaning |\n| ------ | ------- |\n| Y | Year with century, zero-padded to at least 4 digits |\n| C | year / 100 (rounded down such as 20 in 2009) |\n| y | year % 100 (00..99) |\n| m | Month of the year, zero-padded (01..12) |\n| B | The full month name (\"January\") |\n| b | The abbreviated month name (\"Jan\") |\n| h | Equivalent to %b |\n| d | Day of the month, zero-padded (01..31) |\n| e | Day of the month, blank-padded ( 1..31) |\n| j | Day of the year (001..366) |\n\n**Time (Hour, Minute, Second, Subsecond):**\n\n| Format | Meaning |\n| ------ | ------- |\n| H | Hour of the day, 24-hour clock, zero-padded (00..23) |\n| k | Hour of the day, 24-hour clock, blank-padded ( 0..23) |\n| I | Hour of the day, 12-hour clock, zero-padded (01..12) |\n| l | Hour of the day, 12-hour clock, blank-padded ( 1..12) |\n| P | Meridian indicator, lowercase (\"am\" or \"pm\") |\n| p | Meridian indicator, uppercase (\"AM\" or \"PM\") |\n| M | Minute of the hour (00..59) |\n| S | Second of the minute (00..60) |\n| L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000 |\n| N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified width are truncated to avoid carry up |\n\n**Time (Hour, Minute, Second, Subsecond):**\n\n| Format | Meaning |\n| ------ | ------- |\n| z   | Time zone as hour and minute offset from UTC (e.g. +0900) |\n| :z  | hour and minute offset from UTC with a colon (e.g. +09:00) |\n| ::z | hour, minute and second offset from UTC (e.g. +09:00:00) |\n| Z   | Abbreviated time zone name or similar information.  (OS dependent) |\n\n**Weekday:**\n\n| Format | Meaning |\n| ------ | ------- |\n| A | The full weekday name (\"Sunday\") |\n| a | The abbreviated name (\"Sun\") |\n| u | Day of the week (Monday is 1, 1..7) |\n| w | Day of the week (Sunday is 0, 0..6) |\n\n**ISO 8601 week-based year and week number:**\n\nThe first week of YYYY starts with a Monday and includes YYYY-01-04.\nThe days in the year before the first week are in the last week of\nthe previous year.\n\n| Format | Meaning |\n| ------ | ------- |\n| G | The week-based year |\n| g | The last 2 digits of the week-based year (00..99) |\n| V | Week number of the week-based year (01..53) |\n\n**Week number:**\n\nThe first week of YYYY that starts with a Sunday or Monday (according to %U\nor %W). The days in the year before the first week are in week 0.\n\n| Format | Meaning |\n| ------ | ------- |\n| U | Week number of the year. The week starts with Sunday. (00..53) |\n| W | Week number of the year. The week starts with Monday. (00..53) |\n\n**Seconds since the Epoch:**\n\n| Format | Meaning |\n| ------ | ------- |\n| s | Number of seconds since 1970-01-01 00:00:00 UTC. |\n\n**Literal string:**\n\n| Format | Meaning |\n| ------ | ------- |\n| n | Newline character (\\n) |\n| t | Tab character (\\t) |\n| % | Literal \"%\" character |\n\n**Combination:**\n\n| Format | Meaning |\n| ------ | ------- |\n| c | date and time (%a %b %e %T %Y) |\n| D | Date (%m/%d/%y) |\n| F | The ISO 8601 date format (%Y-%m-%d) |\n| v | VMS date (%e-%^b-%4Y) |\n| x | Same as %D |\n| X | Same as %T |\n| r | 12-hour time (%I:%M:%S %p) |\n| R | 24-hour time (%H:%M) |\n| T | 24-hour time (%H:%M:%S) |\n\n```puppet\n$timestamp = Timestamp('2016-08-24T12:13:14')\n\n# Notice the timestamp using a format that notices the ISO 8601 date format\nnotice($timestamp.strftime('%F')) # outputs '2016-08-24'\n\n# Notice the timestamp using a format that notices weekday, month, day, time (as UTC), and year\nnotice($timestamp.strftime('%c')) # outputs 'Wed Aug 24 12:13:14 2016'\n\n# Notice the timestamp using a specific timezone\nnotice($timestamp.strftime('%F %T %z', 'PST')) # outputs '2016-08-24 04:13:14 -0800'\n\n# Notice the timestamp using timezone that is current for the evaluating process\nnotice($timestamp.strftime('%F %T', 'current')) # outputs the timestamp using the timezone for the current process\n```\n\n### Format directives applicable to `Timespan`:\n\n| Format | Meaning |\n| ------ | ------- |\n| D | Number of Days |\n| H | Hour of the day, 24-hour clock |\n| M | Minute of the hour (00..59) |\n| S | Second of the minute (00..59) |\n| L | Millisecond of the second (000..999). Digits under millisecond are truncated to not produce 1000. |\n| N | Fractional seconds digits, default is 9 digits (nanosecond). Digits under a specified length are truncated to avoid carry up |\n\nThe format directive that represents the highest magnitude in the format will be allowed to overflow.\nI.e. if no \"%D\" is used but a \"%H\" is present, then the hours will be more than 23 in case the\ntimespan reflects more than a day.\n\n```puppet\n$duration = Timespan({ hours => 3, minutes => 20, seconds => 30 })\n\n# Notice the duration using a format that outputs <hours>:<minutes>:<seconds>\nnotice($duration.strftime('%H:%M:%S')) # outputs '03:20:30'\n\n# Notice the duration using a format that outputs <minutes>:<seconds>\nnotice($duration.strftime('%M:%S')) # outputs '200:30'\n```\n\n- Since 4.8.0\n\n\nSignature 1\n\n`strftime(Timespan $time_object, String $format)`\n\nSignature 2\n\n`strftime(Timestamp $time_object, String $format, Optional[String] $timezone)`\n\nSignature 3\n\n`strftime(String $format, Optional[String] $timezone)`\n\n## `strip`\n\nStrips leading and trailing spaces from a String\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String` the conversion removes all leading and trailing ASCII white space characters such as space, tab, newline, and return.\n  It does not remove other space-like characters like hard space (Unicode U+00A0). (Tip, `/^[[:space:]]/` regular expression\n  matches all space-like characters).\n* For an `Iterable[Variant[String, Numeric]]` (for example an `Array`) each value is processed and the conversion is not recursive.\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\n```puppet\n\" hello\\n\\t\".strip()\nstrip(\" hello\\n\\t\")\n```\nWould both result in `\"hello\"`\n\n```puppet\n[\" hello\\n\\t\", \" hi\\n\\t\"].strip()\nstrip([\" hello\\n\\t\", \" hi\\n\\t\"])\n```\nWould both result in `['hello', 'hi']`\n\n\nSignature 1\n\n`strip(Numeric $arg)`\n\nSignature 2\n\n`strip(String $arg)`\n\nSignature 3\n\n`strip(Iterable[Variant[String, Numeric]] $arg)`\n\n## `tag`\n\nAdd the specified tags to the containing class\nor definition.  All contained objects will then acquire that tag, also.\n\n\n`tag()`\n\n## `tagged`\n\nA boolean function that\ntells you whether the current container is tagged with the specified tags.\nThe tags are ANDed, so that all of the specified tags must be included for\nthe function to return true.\n\n\n`tagged()`\n\n## `template`\n\nLoads an ERB template from a module, evaluates it, and returns the resulting\nvalue as a string.\n\nThe argument to this function should be a `<MODULE NAME>/<TEMPLATE FILE>`\nreference, which will load `<TEMPLATE FILE>` from a module's `templates`\ndirectory. (For example, the reference `apache/vhost.conf.erb` will load the\nfile `<MODULES DIRECTORY>/apache/templates/vhost.conf.erb`.)\n\nThis function can also accept:\n\n* An absolute path, which can load a template file from anywhere on disk.\n* Multiple arguments, which will evaluate all of the specified templates and\nreturn their outputs concatenated into a single string.\n\n\n`template()`\n\n## `then`\n\nCalls a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nwith the given argument unless the argument is `undef`.\nReturns `undef` if the argument is `undef`, and otherwise the result of giving the\nargument to the lambda.\n\nThis is useful to process a sequence of operations where an intermediate\nresult may be `undef` (which makes the entire sequence `undef`).\nThe `then` function is especially useful with the function `dig` which\nperforms in a similar way \"digging out\" a value in a complex structure.\n\n```puppet\n$data = {a => { b => [{x => 10, y => 20}, {x => 100, y => 200}]}}\nnotice $data.dig(a, b, 1, x).then |$x| { $x * 2 }\n```\n\nWould notice the value 200\n\nContrast this with:\n\n```puppet\n$data = {a => { b => [{x => 10, y => 20}, {not_x => 100, why => 200}]}}\nnotice $data.dig(a, b, 1, x).then |$x| { $x * 2 }\n```\n\nWhich would notice `undef` since the last lookup of 'x' results in `undef` which\nis returned (without calling the lambda given to the `then` function).\n\nAs a result there is no need for conditional logic or a temporary (non local)\nvariable as the result is now either the wanted value (`x`) multiplied\nby 2 or `undef`.\n\nCalls to `then` can be chained. In the next example, a structure is using an offset based on\nusing 1 as the index to the first element (instead of 0 which is used in the language).\nWe are not sure if user input actually contains an index at all, or if it is\noutside the range of available names.args.\n\n```puppet\n# Names to choose from\n$names = ['Ringo', 'Paul', 'George', 'John']\n\n# Structure where 'beatle 2' is wanted (but where the number refers\n# to 'Paul' because input comes from a source using 1 for the first\n# element).\n\n$data = ['singer', { beatle => 2 }]\n$picked = assert_type(String,\n  # the data we are interested in is the second in the array,\n  # a hash, where we want the value of the key 'beatle'\n  $data.dig(1, 'beatle')\n    # and we want the index in $names before the given index\n    .then |$x| { $names[$x-1] }\n    # so we can construct a string with that beatle's name\n    .then |$x| { \"Picked Beatle '${x}'\" }\n)\nnotice $picked\n```\n\nWould notice \"Picked Beatle 'Paul'\", and would raise an error if the result\nwas not a String.\n\n* Since 4.5.0\n\n\n`then(Any $arg, Callable[1,1] &$block)`\n\n## `tree_each`\n\nRuns a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nrecursively and repeatedly using values from a data structure, then returns the unchanged data structure, or if\na lambda is not given, returns an `Iterator` for the tree.\n\nThis function takes one mandatory argument, one optional, and an optional block in this order:\n\n1. An `Array`, `Hash`, `Iterator`, or `Object` that the function will iterate over.\n2. An optional hash with the options:\n   * `include_containers` => `Optional[Boolean]` # default `true` - if containers should be given to the lambda\n   * `include_values` => `Optional[Boolean]` # default `true` - if non containers should be given to the lambda\n   * `include_root` => `Optional[Boolean]` # default `true` - if the root container should be given to the lambda\n   * `container_type` => `Optional[Type[Variant[Array, Hash, Object]]]` # a type that determines what a container is - can only\n      be set to a type that matches the default `Variant[Array, Hash, Object]`.\n   * `order` => `Enum[depth_first, breadth_first]` # default ´depth_first`, the order in which elements are visited\n   * `include_refs` => `Optional[Boolean]` # default `false`, if attributes in objects marked as bing of `reference` kind\n      should be included.\n3. An optional lambda, which the function calls for each element in the first argument. It must\n   accept one or two arguments; either `$path`, and `$value`, or just `$value`.\n\n`$data.tree_each |$path, $value| { <PUPPET CODE BLOCK> }`\n`$data.tree_each |$value| { <PUPPET CODE BLOCK> }`\n\nor\n\n`tree_each($data) |$path, $value| { <PUPPET CODE BLOCK> }`\n`tree_each($data) |$value| { <PUPPET CODE BLOCK> }`\n\nThe parameter `$path` is always given as an `Array` containing the path that when applied to\nthe tree as `$data.dig(*$path) yields the `$value`.\nThe `$value` is the value at that path.\n\nFor `Array` values, the path will contain `Integer` entries with the array index,\nand for `Hash` values, the path will contain the hash key, which may be `Any` value.\nFor `Object` containers, the entry is the name of the attribute (a `String`).\n\nThe tree is walked in either depth-first order, or in breadth-first order under the control of the\n`order` option, yielding each `Array`, `Hash`, `Object`, and each entry/attribute.\nThe default is `depth_first` which means that children are processed before siblings.\nAn order of `breadth_first` means that siblings are processed before children.\n\n```puppet\n[1, [2, 3], 4]\n```\n\nIf containers are skipped, results in:\n\n* `depth_first` order `1`, `2`, `3`, `4`\n* `breadth_first` order `1`, `4`,`2`, `3`\n\nIf containers and root are included, results in:\n\n* `depth_first` order `[1, [2, 3], 4]`, `1`, `[2, 3]`, `2`, `3`, `4`\n* `breadth_first` order `[1, [2, 3], 4]`, `1`, `[2, 3]`, `4`, `2`, `3`\n\nTypical use of the `tree_each` function include:\n* a more efficient way to iterate over a tree than first using `flatten` on an array\n  as that requires a new (potentially very large) array to be created\n* when a tree needs to be transformed and 'pretty printed' in a template\n* avoiding having to write a special recursive function when tree contains hashes (flatten does\n  not work on hashes)\n\n```puppet\n$data = [1, 2, [3, [4, 5]]]\n$data.tree_each({include_containers => false}) |$v| { notice \"$v\" }\n```\n\nThis would call the lambda 5 times with with the following values in sequence: `1`, `2`, `3`, `4`, `5`\n\n```puppet\n$data = [1, 2, [3, [4, 5]]]\n$data.tree_each |$v| { notice \"$v\" }\n```\n\nThis would call the lambda 7 times with the following values in sequence:\n`1`, `2`, `[3, [4, 5]]`, `3`, `[4, 5]`, `4`, `5`\n\n```puppet\n$data = [1, 2, [3, [4, 5]]]\n$data.tree_each({include_values => false, include_root => false}) |$v| { notice \"$v\" }\n```\n\nThis would call the lambda 2 times with the following values in sequence:\n`[3, [4, 5]]`, `[4, 5]`\n\nAny Puppet Type system data type can be used to filter what is\nconsidered to be a container, but it must be a narrower type than one of\nthe default `Array`, `Hash`, `Object` types - for example it is not possible to make a\n`String` be a container type.\n\n```puppet\n$data = [1, {a => 'hello', b => [100, 200]}, [3, [4, 5]]]\n$data.tree_each({container_type => Array, include_containers => false} |$v| { notice \"$v\" }\n```\n\nWould call the lambda 5 times with `1`, `{a => 'hello', b => [100, 200]}`, `3`, `4`, `5`\n\n**Chaining** When calling `tree_each` without a lambda the function produces an `Iterator`\nthat can be chained into another iteration. Thus it is easy to use one of:\n\n* `reverse_each` - get \"leaves before root\"\n* `filter` - prune the tree\n* `map` - transform each element\n\nNote than when chaining, the value passed on is a `Tuple` with `[path, value]`.\n\n```puppet\n# A tree of some complexity (here very simple for readability)\n$tree = [\n { name => 'user1', status => 'inactive', id => '10'},\n { name => 'user2', status => 'active', id => '20'}\n]\nnotice $tree.tree_each.filter |$v| {\n $value = $v[1]\n $value =~ Hash and $value[status] == active\n}\n```\n\nWould notice `[[[1], {name => user2, status => active, id => 20}]]`, which can then be processed\nfurther as each filtered result appears as a `Tuple` with `[path, value]`.\n\n\nFor general examples that demonstrates iteration see the Puppet\n[iteration](https://puppet.com/docs/puppet/latest/lang_iteration.html)\ndocumentation.\n\n\nSignature 1\n\n`tree_each(Variant[Iterator, Array, Hash, Object] $tree, Optional[OptionsType] $options, Callable[2,2] &$block)`\n\nSignature 2\n\n`tree_each(Variant[Iterator, Array, Hash, Object] $tree, Optional[OptionsType] $options, Callable[1,1] &$block)`\n\nSignature 3\n\n`tree_each(Variant[Iterator, Array, Hash, Object] $tree, Optional[OptionsType] $options)`\n\n## `type`\n\nReturns the data type of a given value with a given degree of generality.\n\n```puppet\ntype InferenceFidelity = Enum[generalized, reduced, detailed]\n\nfunction type(Any $value, InferenceFidelity $fidelity = 'detailed') # returns Type\n```\n\n``` puppet\nnotice type(42) =~ Type[Integer]\n```\n\nWould notice `true`.\n\nBy default, the best possible inference is made where all details are retained.\nThis is good when the type is used for further type calculations but is overwhelmingly\nrich in information if it is used in a error message.\n\nThe optional argument `$fidelity` may be given as (from lowest to highest fidelity):\n\n* `generalized` - reduces to common type and drops size constraints\n* `reduced` - reduces to common type in collections\n* `detailed` - (default) all details about inferred types is retained\n\n``` puppet\nnotice type([3.14, 42], 'generalized')\nnotice type([3.14, 42], 'reduced'')\nnotice type([3.14, 42], 'detailed')\nnotice type([3.14, 42])\n```\n\nWould notice the four values:\n\n1. `Array[Numeric]`\n2. `Array[Numeric, 2, 2]`\n3. `Tuple[Float[3.14], Integer[42,42]]]`\n4. `Tuple[Float[3.14], Integer[42,42]]]`\n\n\nSignature 1\n\n`type(Any $value, Optional[Enum[detailed]] $inference_method)`\n\nSignature 2\n\n`type(Any $value, Enum[reduced] $inference_method)`\n\nSignature 3\n\n`type(Any $value, Enum[generalized] $inference_method)`\n\n## `unique`\n\nProduces a unique set of values from an `Iterable` argument.\n\n* If the argument is a `String`, the unique set of characters are returned as a new `String`.\n* If the argument is a `Hash`, the resulting hash associates a set of keys with a set of unique values.\n* For all other types of `Iterable` (`Array`, `Iterator`) the result is an `Array` with\n  a unique set of entries.\n* Comparison of all `String` values are case sensitive.\n* An optional code block can be given - if present it is given each candidate value and its return is used instead of the given value. This\n  enables transformation of the value before comparison. The result of the lambda is only used for comparison.\n* The optional code block when used with a hash is given each value (not the keys).\n\n```puppet\n# will produce 'abc'\n\"abcaabb\".unique\n```\n\n```puppet\n# will produce ['a', 'b', 'c']\n['a', 'b', 'c', 'a', 'a', 'b'].unique\n```\n\n```puppet\n# will produce { ['a', 'b'] => [10], ['c'] => [20]}\n{'a' => 10, 'b' => 10, 'c' => 20}.unique\n\n# will produce { 'a' => 10, 'c' => 20 } (use first key with first value)\nHash.new({'a' => 10, 'b' => 10, 'c' => 20}.unique.map |$k, $v| { [ $k[0] , $v[0]] })\n\n# will produce { 'b' => 10, 'c' => 20 } (use last key with first value)\nHash.new({'a' => 10, 'b' => 10, 'c' => 20}.unique.map |$k, $v| { [ $k[-1] , $v[0]] })\n```\n\n```\n# will produce [3, 2, 1]\n[1,2,2,3,3].reverse_each.unique\n```\n\n```puppet\n# will produce [['sam', 'smith'], ['sue', 'smith']]\n[['sam', 'smith'], ['sam', 'brown'], ['sue', 'smith']].unique |$x| { $x[0] }\n\n# will produce [['sam', 'smith'], ['sam', 'brown']]\n[['sam', 'smith'], ['sam', 'brown'], ['sue', 'smith']].unique |$x| { $x[1] }\n\n# will produce ['aBc', 'bbb'] (using a lambda to make comparison using downcased (%d) strings)\n['aBc', 'AbC', 'bbb'].unique |$x| { String($x,'%d') }\n\n# will produce {[a] => [10], [b, c, d, e] => [11, 12, 100]}\n{a => 10, b => 11, c => 12, d => 100, e => 11}.unique |$v| { if $v > 10 { big } else { $v } }\n```\n\nNote that for `Hash` the result is slightly different than for the other data types. For those the result contains the\n*first-found* unique value, but for `Hash` it contains associations from a set of keys to the set of values clustered by the\nequality lambda (or the default value equality if no lambda was given). This makes the `unique` function more versatile for hashes\nin general, while requiring that the simple computation of \"hash's unique set of values\" is performed as `$hsh.map |$k, $v| { $v }.unique`.\n(Generally, it's meaningless to compute the unique set of hash keys because they are unique by definition. However, the\nsituation can change if the hash keys are processed with a different lambda for equality. For this unique computation,\nfirst map the hash to an array of its keys.)\nIf the more advanced clustering is wanted for one of the other data types, simply transform it into a `Hash` as shown in the\nfollowing example.\n\n```puppet\n# Array ['a', 'b', 'c'] to Hash with index results in\n# {0 => 'a', 1 => 'b', 2 => 'c'}\nHash(['a', 'b', 'c'].map |$i, $v| { [$i, $v]})\n\n# String \"abc\" to Hash with index results in\n# {0 => 'a', 1 => 'b', 2 => 'c'}\nHash(Array(\"abc\").map |$i,$v| { [$i, $v]})\n\"abc\".to(Array).map |$i,$v| { [$i, $v]}.to(Hash)\n```\n\n\nSignature 1\n\n`unique(String $string, Optional[Callable[String]] &$block)`\n\nSignature 2\n\n`unique(Hash $hash, Optional[Callable[Any]] &$block)`\n\nSignature 3\n\n`unique(Array $array, Optional[Callable[Any]] &$block)`\n\nSignature 4\n\n`unique(Iterable $iterable, Optional[Callable[Any]] &$block)`\n\n## `unwrap`\n\nUnwraps a Sensitive value and returns the wrapped object.\nReturns the Value itself, if it is not Sensitive.\n\n```puppet\n$plaintext = 'hunter2'\n$pw = Sensitive.new($plaintext)\nnotice(\"Wrapped object is $pw\") #=> Prints \"Wrapped object is Sensitive [value redacted]\"\n$unwrapped = $pw.unwrap\nnotice(\"Unwrapped object is $unwrapped\") #=> Prints \"Unwrapped object is hunter2\"\n```\n\nYou can optionally pass a block to unwrap in order to limit the scope where the\nunwrapped value is visible.\n\n```puppet\n$pw = Sensitive.new('hunter2')\nnotice(\"Wrapped object is $pw\") #=> Prints \"Wrapped object is Sensitive [value redacted]\"\n$pw.unwrap |$unwrapped| {\n  $conf = inline_template(\"password: ${unwrapped}\\n\")\n  Sensitive.new($conf)\n} #=> Returns a new Sensitive object containing an interpolated config file\n# $unwrapped is now out of scope\n```\n\n\nSignature 1\n\n`unwrap(Sensitive $arg, Optional[Callable] &$block)`\n\nSignature 2\n\n`unwrap(Any $arg, Optional[Callable] &$block)`\n\n## `upcase`\n\nConverts a String, Array or Hash (recursively) into upper case.\n\nThis function is compatible with the stdlib function with the same name.\n\nThe function does the following:\n* For a `String`, its upper case version is returned. This is done using Ruby system locale which handles some, but not all\n  special international up-casing rules (for example German double-s ß is upcased to \"SS\", whereas upper case double-s\n  is downcased to ß).\n* For `Array` and `Hash` the conversion to upper case is recursive and each key and value must be convertible by\n  this function.\n* When a `Hash` is converted, some keys could result in the same key - in those cases, the\n  latest key-value wins. For example if keys \"aBC\", and \"abC\" where both present, after upcase there would only be one\n  key \"ABC\".\n* If the value is `Numeric` it is simply returned (this is for backwards compatibility).\n* An error is raised for all other data types.\n\nPlease note: This function relies directly on Ruby's String implementation and as such may not be entirely UTF8 compatible.\nTo ensure best compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.\n\n```puppet\n'hello'.upcase()\nupcase('hello')\n```\nWould both result in `\"HELLO\"`\n\n```puppet\n['a', 'b'].upcase()\nupcase(['a', 'b'])\n```\nWould both result in `['A', 'B']`\n\n```puppet\n{'a' => 'hello', 'b' => 'goodbye'}.upcase()\n```\nWould result in `{'A' => 'HELLO', 'B' => 'GOODBYE'}`\n\n```puppet\n['a', 'b', ['c', ['d']], {'x' => 'y'}].upcase\n```\nWould result in `['A', 'B', ['C', ['D']], {'X' => 'Y'}]`\n\n\nSignature 1\n\n`upcase(Numeric $arg)`\n\nSignature 2\n\n`upcase(String $arg)`\n\nSignature 3\n\n`upcase(Array[StringData] $arg)`\n\nSignature 4\n\n`upcase(Hash[StringData, StringData] $arg)`\n\n## `values`\n\nReturns the values of a hash as an Array\n\n```puppet\n$hsh = {\"apples\" => 3, \"oranges\" => 4 }\n$hsh.values()\nvalues($hsh)\n# both results in the array [3, 4]\n```\n\n* Note that a hash in the puppet language accepts any data value (including `undef`) unless\n  it is constrained with a `Hash` data type that narrows the allowed data types.\n* For an empty hash, an empty array is returned.\n* The order of the values is the same as the order in the hash (typically the order in which they were added).\n\n\n`values(Hash $hsh)`\n\n## `versioncmp`\n\nCompares two version numbers.\n\nPrototype:\n\n    $result = versioncmp(a, b)\n\nWhere a and b are arbitrary version strings.\n\nOptional parameter ignore_trailing_zeroes is used to ignore unnecessary\ntrailing version numbers like .0 or .0.00\n\nThis function returns:\n\n* `1` if version a is greater than version b\n* `0` if the versions are equal\n* `-1` if version a is less than version b\n\nThis function uses the same version comparison algorithm used by Puppet's\n`package` type.\n\n\n`versioncmp(String $a, String $b, Optional[Boolean] $ignore_trailing_zeroes)`\n\n## `warning`\n\nLogs a message on the server at level `warning`.\n\n\n`warning(Any *$values)`\n\n### Parameters\n\n\n* `*values` --- The values to log.\n\nReturn type(s): `Undef`. \n\n## `with`\n\nCalls a [lambda](https://puppet.com/docs/puppet/latest/lang_lambdas.html)\nwith the given arguments and returns the result.\n\nSince a lambda's scope is\n[local](https://puppet.com/docs/puppet/latest/lang_lambdas.html#lambda-scope)\nto the lambda, you can use the `with` function to create private blocks of code within a\nclass using variables whose values cannot be accessed outside of the lambda.\n\n```puppet\n# Concatenate three strings into a single string formatted as a list.\n$fruit = with(\"apples\", \"oranges\", \"bananas\") |$x, $y, $z| {\n  \"${x}, ${y}, and ${z}\"\n}\n$check_var = $x\n# $fruit contains \"apples, oranges, and bananas\"\n# $check_var is undefined, as the value of $x is local to the lambda.\n```\n\n\n`with(Any *$arg, Callable &$block)`\n\n## `yaml_data`\n\nThe `yaml_data` is a hiera 5 `data_hash` data provider function.\nSee [the configuration guide documentation](https://puppet.com/docs/puppet/latest/hiera_config_yaml_5.html#configuring-a-hierarchy-level-built-in-backends) for\nhow to use this function.\n\n\n`yaml_data(Struct[{path=>String[1]}] $options, Puppet::LookupContext $context)`\n\n"
  },
  {
    "path": "references/man/agent.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet agent'\ncanonical: \"/puppet/latest/man/agent.html\"\n---\n\n# Man Page: puppet agent\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-agent** - The puppet agent daemon\n\n## SYNOPSIS\nRetrieves the client configuration from the Puppet master and applies it\nto the local host.\n\nThis service may be run as a daemon, run periodically using cron (or\nsomething similar), or run interactively for testing purposes.\n\n## USAGE\npuppet agent \\[\\--certname *NAME*\\]\n\\[-D\\|\\--daemonize\\|\\--no-daemonize\\] \\[-d\\|\\--debug\\]\n\\[\\--detailed-exitcodes\\] \\[\\--digest *DIGEST*\\] \\[\\--disable\n\\[MESSAGE\\]\\] \\[\\--enable\\] \\[\\--fingerprint\\] \\[-h\\|\\--help\\]\n\\[-l\\|\\--logdest syslog\\|eventlog\\|*ABS FILEPATH*\\|console\\]\n\\[\\--serverport *PORT*\\] \\[\\--noop\\] \\[-o\\|\\--onetime\\]\n\\[\\--sourceaddress *IP_ADDRESS*\\] \\[-t\\|\\--test\\] \\[-v\\|\\--verbose\\]\n\\[-V\\|\\--version\\] \\[-w\\|\\--waitforcert *SECONDS*\\]\n\n## DESCRIPTION\nThis is the main puppet client. Its job is to retrieve the local\nmachine\\'s configuration from a remote server and apply it. In order to\nsuccessfully communicate with the remote server, the client must have a\ncertificate signed by a certificate authority that the server trusts;\nthe recommended method for this, at the moment, is to run a certificate\nauthority as part of the puppet server (which is the default). The\nclient will connect and request a signed certificate, and will continue\nconnecting until it receives one.\n\nOnce the client has a signed certificate, it will retrieve its\nconfiguration and apply it.\n\n## USAGE NOTES\n\\'puppet agent\\' does its best to find a compromise between interactive\nuse and daemon use. If you run it with no arguments and no\nconfiguration, it goes into the background, attempts to get a signed\ncertificate, and retrieves and applies its configuration every 30\nminutes.\n\nSome flags are meant specifically for interactive use \\-\\-- in\nparticular, \\'test\\', \\'tags\\' and \\'fingerprint\\' are useful.\n\n\\'\\--test\\' runs once in the foreground with verbose logging, then\nexits. It also exits if it can\\'t get a valid catalog. **\\--test**\nincludes the \\'\\--detailed-exitcodes\\' option by default and exits with\none of the following exit codes:\n\n-   0: The run succeeded with no changes or failures; the system was\n    already in the desired state.\n\n-   1: The run failed, or wasn\\'t attempted due to another run already\n    in progress.\n\n-   2: The run succeeded, and some resources were changed.\n\n-   4: The run succeeded, and some resources failed.\n\n-   6: The run succeeded, and included both changes and failures.\n\n\\'\\--tags\\' allows you to specify what portions of a configuration you\nwant to apply. Puppet elements are tagged with all of the class or\ndefinition names that contain them, and you can use the \\'tags\\' flag to\nspecify one of these names, causing only configuration elements\ncontained within that class or definition to be applied. This is very\nuseful when you are testing new configurations \\-\\-- for instance, if\nyou are just starting to manage \\'ntpd\\', you would put all of the new\nelements into an \\'ntpd\\' class, and call puppet with \\'\\--tags ntpd\\',\nwhich would only apply that small portion of the configuration during\nyour testing, rather than applying the whole thing.\n\n\\'\\--fingerprint\\' is a one-time flag. In this mode \\'puppet agent\\'\nruns once and displays on the console (and in the log) the current\ncertificate (or certificate request) fingerprint. Providing the\n\\'\\--digest\\' option allows you to use a different digest algorithm to\ngenerate the fingerprint. The main use is to verify that before signing\na certificate request on the master, the certificate request the master\nreceived is the same as the one the client sent (to prevent against\nman-in-the-middle attacks when signing certificates).\n\n\\'\\--skip_tags\\' is a flag used to filter resources. If this is set,\nthen only resources not tagged with the specified tags will be applied.\nValues must be comma-separated.\n\n## OPTIONS\nNote that any Puppet setting that\\'s valid in the configuration file is\nalso a valid long argument. For example, \\'server\\' is a valid setting,\nso you can specify \\'\\--server *servername*\\' as an argument. Boolean\nsettings accept a \\'\\--no-\\' prefix to turn off a behavior, translating\ninto \\'\\--setting\\' and \\'\\--no-setting\\' pairs, such as\n**\\--daemonize** and **\\--no-daemonize**.\n\nSee the configuration file documentation at\nhttps://puppet.com/docs/puppet/latest/configuration.html for the full\nlist of acceptable settings. A commented list of all settings can also\nbe generated by running puppet agent with \\'\\--genconfig\\'.\n\n-   \\--certname: Set the certname (unique ID) of the client. The master\n    reads this unique identifying string, which is usually set to the\n    node\\'s fully-qualified domain name, to determine which\n    configurations the node will receive. Use this option to debug setup\n    problems or implement unusual node identification schemes. (This is\n    a Puppet setting, and can go in puppet.conf.)\n\n-   \\--daemonize: Send the process into the background. This is the\n    default. (This is a Puppet setting, and can go in puppet.conf. Note\n    the special \\'no-\\' prefix for boolean settings on the command\n    line.)\n\n-   \\--no-daemonize: Do not send the process into the background. (This\n    is a Puppet setting, and can go in puppet.conf. Note the special\n    \\'no-\\' prefix for boolean settings on the command line.)\n\n-   \\--debug: Enable full debugging.\n\n-   \\--detailed-exitcodes: Provide extra information about the run via\n    exit codes; works only if \\'\\--test\\' or \\'\\--onetime\\' is also\n    specified. If enabled, \\'puppet agent\\' uses the following exit\n    codes:\n\n    0: The run succeeded with no changes or failures; the system was\n    already in the desired state.\n\n    1: The run failed, or wasn\\'t attempted due to another run already\n    in progress.\n\n    2: The run succeeded, and some resources were changed.\n\n    4: The run succeeded, and some resources failed.\n\n    6: The run succeeded, and included both changes and failures.\n\n-   \\--digest: Change the certificate fingerprinting digest algorithm.\n    The default is SHA256. Valid values depends on the version of\n    OpenSSL installed, but will likely contain MD5, MD2, SHA1 and\n    SHA256.\n\n-   \\--disable: Disable working on the local system. This puts a lock\n    file in place, causing \\'puppet agent\\' not to work on the system\n    until the lock file is removed. This is useful if you are testing a\n    configuration and do not want the central configuration to override\n    the local state until everything is tested and committed.\n\n    Disable can also take an optional message that will be reported by\n    the \\'puppet agent\\' at the next disabled run.\n\n    \\'puppet agent\\' uses the same lock file while it is running, so no\n    more than one \\'puppet agent\\' process is working at a time.\n\n    \\'puppet agent\\' exits after executing this.\n\n-   \\--enable: Enable working on the local system. This removes any lock\n    file, causing \\'puppet agent\\' to start managing the local system\n    again However, it continues to use its normal scheduling, so it\n    might not start for another half hour.\n\n    \\'puppet agent\\' exits after executing this.\n\n-   \\--evaltrace: Logs each resource as it is being evaluated. This\n    allows you to interactively see exactly what is being done. (This is\n    a Puppet setting, and can go in puppet.conf. Note the special\n    \\'no-\\' prefix for boolean settings on the command line.)\n\n-   \\--fingerprint: Display the current certificate or certificate\n    signing request fingerprint and then exit. Use the \\'\\--digest\\'\n    option to change the digest algorithm used.\n\n-   \\--help: Print this help message\n\n-   \\--job-id: Attach the specified job id to the catalog request and\n    the report used for this agent run. This option only works when\n    \\'\\--onetime\\' is used. When using Puppet Enterprise this flag\n    should not be used as the orchestrator sets the job-id for you and\n    it must be unique.\n\n-   \\--logdest: Where to send log messages. Choose between \\'syslog\\'\n    (the POSIX syslog service), \\'eventlog\\' (the Windows Event Log),\n    \\'console\\', or the path to a log file. If debugging or verbosity is\n    enabled, this defaults to \\'console\\'. Otherwise, it defaults to\n    \\'syslog\\' on POSIX systems and \\'eventlog\\' on Windows. Multiple\n    destinations can be set using a comma separated list (eg:\n    **/path/file1,console,/path/file2**)\\\"\n\n    A path ending with \\'.json\\' will receive structured output in JSON\n    format. The log file will not have an ending \\'\\]\\' automatically\n    written to it due to the appending nature of logging. It must be\n    appended manually to make the content valid JSON.\n\n    A path ending with \\'.jsonl\\' will receive structured output in JSON\n    Lines format.\n\n-   \\--masterport: The port on which to contact the Puppet Server. (This\n    is a Puppet setting, and can go in puppet.conf. Deprecated in favor\n    of the \\'serverport\\' setting.)\n\n-   \\--noop: Use \\'noop\\' mode where the daemon runs in a no-op or\n    dry-run mode. This is useful for seeing what changes Puppet would\n    make without actually executing the changes. (This is a Puppet\n    setting, and can go in puppet.conf. Note the special \\'no-\\' prefix\n    for boolean settings on the command line.)\n\n-   \\--onetime: Run the configuration once. Runs a single (normally\n    daemonized) Puppet run. Useful for interactively running puppet\n    agent when used in conjunction with the \\--no-daemonize option.\n    (This is a Puppet setting, and can go in puppet.conf. Note the\n    special \\'no-\\' prefix for boolean settings on the command line.)\n\n-   \\--serverport: The port on which to contact the Puppet Server. (This\n    is a Puppet setting, and can go in puppet.conf.)\n\n-   \\--sourceaddress: Set the source IP address for transactions. This\n    defaults to automatically selected. (This is a Puppet setting, and\n    can go in puppet.conf.)\n\n-   \\--test: Enable the most common options used for testing. These are\n    \\'onetime\\', \\'verbose\\', \\'no-daemonize\\',\n    \\'no-usecacheonfailure\\', \\'detailed-exitcodes\\', \\'no-splay\\', and\n    \\'show_diff\\'.\n\n-   \\--trace Prints stack traces on some errors. (This is a Puppet\n    setting, and can go in puppet.conf. Note the special \\'no-\\' prefix\n    for boolean settings on the command line.)\n\n-   \\--verbose: Turn on verbose reporting.\n\n-   \\--version: Print the puppet version number and exit.\n\n-   \\--waitforcert: This option only matters for daemons that do not yet\n    have certificates and it is enabled by default, with a value of 120\n    (seconds). This causes \\'puppet agent\\' to connect to the server\n    every 2 minutes and ask it to sign a certificate request. This is\n    useful for the initial setup of a puppet client. You can turn off\n    waiting for certificates by specifying a time of 0. (This is a\n    Puppet setting, and can go in puppet.conf.)\n\n-   \\--write_catalog_summary After compiling the catalog saves the\n    resource list and classes list to the node in the state directory\n    named classes.txt and resources.txt (This is a Puppet setting, and\n    can go in puppet.conf.)\n\n## EXAMPLE\n\n    $ puppet agent --server puppet.domain.com\n\n## DIAGNOSTICS\nPuppet agent accepts the following signals:\n\nSIGHUP\n\n:   Restart the puppet agent daemon.\n\nSIGINT and SIGTERM\n\n:   Shut down the puppet agent daemon.\n\nSIGUSR1\n\n:   Immediately retrieve and apply configurations from the puppet\n    master.\n\nSIGUSR2\n\n:   Close file descriptors for log files and reopen them. Used with\n    logrotate.\n\n## AUTHOR\nLuke Kanies\n\n## COPYRIGHT\nCopyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/apply.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet apply'\ncanonical: \"/puppet/latest/man/apply.html\"\n---\n\n# Man Page: puppet apply\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-apply** - Apply Puppet manifests locally\n\n## SYNOPSIS\nApplies a standalone Puppet manifest to the local system.\n\n## USAGE\npuppet apply \\[-h\\|\\--help\\] \\[-V\\|\\--version\\] \\[-d\\|\\--debug\\]\n\\[-v\\|\\--verbose\\] \\[-e\\|\\--execute\\] \\[\\--detailed-exitcodes\\]\n\\[-L\\|\\--loadclasses\\] \\[-l\\|\\--logdest syslog\\|eventlog\\|*ABS\nFILEPATH*\\|console\\] \\[\\--noop\\] \\[\\--catalog *catalog*\\]\n\\[\\--write-catalog-summary\\] *file*\n\n## DESCRIPTION\nThis is the standalone puppet execution tool; use it to apply individual\nmanifests.\n\nWhen provided with a modulepath, via command line or config file, puppet\napply can effectively mimic the catalog that would be served by puppet\nmaster with access to the same modules, although there are some subtle\ndifferences. When combined with scheduling and an automated system for\npushing manifests, this can be used to implement a serverless Puppet\nsite.\n\nMost users should use \\'puppet agent\\' and \\'puppet master\\' for\nsite-wide manifests.\n\n## OPTIONS\nAny setting that\\'s valid in the configuration file is a valid long\nargument for puppet apply. For example, \\'tags\\' is a valid setting, so\nyou can specify \\'\\--tags *class*,*tag*\\' as an argument.\n\nSee the configuration file documentation at\nhttps://puppet.com/docs/puppet/latest/configuration.html for the full\nlist of acceptable parameters. You can generate a commented list of all\nconfiguration options by running puppet with \\'\\--genconfig\\'.\n\n-   \\--debug: Enable full debugging.\n\n-   \\--detailed-exitcodes: Provide extra information about the run via\n    exit codes. If enabled, \\'puppet apply\\' will use the following exit\n    codes:\n\n    0: The run succeeded with no changes or failures; the system was\n    already in the desired state.\n\n    1: The run failed.\n\n    2: The run succeeded, and some resources were changed.\n\n    4: The run succeeded, and some resources failed.\n\n    6: The run succeeded, and included both changes and failures.\n\n-   \\--help: Print this help message\n\n-   \\--loadclasses: Load any stored classes. \\'puppet agent\\' caches\n    configured classes (usually at /etc/puppetlabs/puppet/classes.txt),\n    and setting this option causes all of those classes to be set in\n    your puppet manifest.\n\n-   \\--logdest: Where to send log messages. Choose between \\'syslog\\'\n    (the POSIX syslog service), \\'eventlog\\' (the Windows Event Log),\n    \\'console\\', or the path to a log file. Defaults to \\'console\\'.\n    Multiple destinations can be set using a comma separated list (eg:\n    **/path/file1,console,/path/file2**)\\\"\n\n    A path ending with \\'.json\\' will receive structured output in JSON\n    format. The log file will not have an ending \\'\\]\\' automatically\n    written to it due to the appending nature of logging. It must be\n    appended manually to make the content valid JSON.\n\n    A path ending with \\'.jsonl\\' will receive structured output in JSON\n    Lines format.\n\n-   \\--noop: Use \\'noop\\' mode where Puppet runs in a no-op or dry-run\n    mode. This is useful for seeing what changes Puppet will make\n    without actually executing the changes.\n\n-   \\--execute: Execute a specific piece of Puppet code\n\n-   \\--test: Enable the most common options used for testing. These are\n    \\'verbose\\', \\'detailed-exitcodes\\' and \\'show_diff\\'.\n\n-   \\--verbose: Print extra information.\n\n-   \\--catalog: Apply a JSON catalog (such as one generated with\n    \\'puppet master \\--compile\\'). You can either specify a JSON file or\n    pipe in JSON from standard input.\n\n-   \\--write-catalog-summary After compiling the catalog saves the\n    resource list and classes list to the node in the state directory\n    named classes.txt and resources.txt\n\n## EXAMPLE\n\n    $ puppet apply -e 'notify { \"hello world\": }'\n    $ puppet apply -l /tmp/manifest.log manifest.pp\n    $ puppet apply --modulepath=/root/dev/modules -e \"include ntpd::server\"\n    $ puppet apply --catalog catalog.json\n\n## AUTHOR\nLuke Kanies\n\n## COPYRIGHT\nCopyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/catalog.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet catalog'\ncanonical: \"/puppet/latest/man/catalog.html\"\n---\n\n# Man Page: puppet catalog\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-catalog** - Compile, save, view, and convert catalogs.\n\n## SYNOPSIS\npuppet catalog *action* \\[\\--terminus \\_TERMINUS\\]\n\n## DESCRIPTION\nThis subcommand deals with catalogs, which are compiled per-node\nartifacts generated from a set of Puppet manifests. By default, it\ninteracts with the compiling subsystem and compiles a catalog using the\ndefault manifest and **certname**, but you can change the source of the\ncatalog with the **\\--terminus** option. You can also choose to print\nany catalog in \\'dot\\' format (for easy graph viewing with OmniGraffle\nor Graphviz) with \\'\\--render-as dot\\'.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n\\--terminus \\_TERMINUS\n\n:   Indirector faces expose indirected subsystems of Puppet. These\n    subsystems are each able to retrieve and alter a specific type of\n    data (with the familiar actions of **find**, **search**, **save**,\n    and **destroy**) from an arbitrary number of pluggable backends. In\n    Puppet parlance, these backends are called terminuses.\n\n    Almost all indirected subsystems have a **rest** terminus that\n    interacts with the puppet master\\'s data. Most of them have\n    additional terminuses for various local data models, which are in\n    turn used by the indirected subsystem on the puppet master whenever\n    it receives a remote request.\n\n    The terminus for an action is often determined by context, but\n    occasionally needs to be set explicitly. See the \\\"Notes\\\" section\n    of this face\\'s manpage for more details.\n\n## ACTIONS\n**apply** - Find and apply a catalog.\n\n:   **SYNOPSIS**\n\n    puppet catalog apply \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Finds and applies a catalog. This action takes no arguments, but the\n    source of the catalog can be managed with the **\\--terminus**\n    option.\n\n    **RETURNS**\n\n    Nothing. When used from the Ruby API, returns a\n    Puppet::Transaction::Report object.\n\n**compile** - Compile a catalog.\n\n:   **SYNOPSIS**\n\n    puppet catalog compile \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Compiles a catalog locally for a node, requiring access to modules,\n    node classifier, etc.\n\n    **RETURNS**\n\n    A serialized catalog.\n\n**download** - Download this node\\'s catalog from the puppet master server.\n\n:   **SYNOPSIS**\n\n    puppet catalog download \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Retrieves a catalog from the puppet master and saves it to the local\n    yaml cache. This action always contacts the puppet master and will\n    ignore alternate termini.\n\n    The saved catalog can be used in any subsequent catalog action by\n    specifying \\'\\--terminus yaml\\' for that action.\n\n    **RETURNS**\n\n    Nothing.\n\n    **NOTES**\n\n    When used from the Ruby API, this action has a side effect of\n    leaving Puppet::Resource::Catalog.indirection.terminus_class set to\n    yaml. The terminus must be explicitly re-set for subsequent catalog\n    actions.\n\n**find** - Retrieve the catalog for the node from which the command is run.\n\n:   **SYNOPSIS**\n\n    puppet catalog find \\[\\--terminus \\_TERMINUS\\]\n    \\[\\--facts_for_catalog\\] *certname*, *facts*\n\n    **DESCRIPTION**\n\n    Retrieve the catalog for the node from which the command is run.\n\n    **OPTIONS** *\\--facts_for_catalog* - Not implemented for the CLI;\n    facts are collected internally.\n\n    **RETURNS**\n\n    A serialized catalog. When used from the Ruby API, returns a\n    Puppet::Resource::Catalog object.\n\n**info** - Print the default terminus class for this face.\n\n:   **SYNOPSIS**\n\n    puppet catalog info \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Prints the default terminus class for this subcommand. Note that\n    different run modes may have different default termini; when in\n    doubt, specify the run mode with the \\'\\--run_mode\\' option.\n\n**save** - API only: create or overwrite an object.\n\n:   **SYNOPSIS**\n\n    puppet catalog save \\[\\--terminus \\_TERMINUS\\] *key*\n\n    **DESCRIPTION**\n\n    API only: create or overwrite an object. As the Faces framework does\n    not currently accept data from STDIN, save actions cannot currently\n    be invoked from the command line.\n\n**select** - Retrieve a catalog and filter it for resources of a given type.\n\n:   **SYNOPSIS**\n\n    puppet catalog select \\[\\--terminus \\_TERMINUS\\] *host*\n    *resource_type*\n\n    **DESCRIPTION**\n\n    Retrieves a catalog for the specified host, then searches it for all\n    resources of the requested type.\n\n    **RETURNS**\n\n    A list of resource references (\\\"Type\\[title\\]\\\"). When used from\n    the API, returns an array of Puppet::Resource objects excised from a\n    catalog.\n\n    **NOTES**\n\n    By default, this action will retrieve a catalog from Puppet\\'s\n    compiler subsystem; you must call the action with **\\--terminus\n    rest** if you wish to retrieve a catalog from the puppet master.\n\n    FORMATTING ISSUES: This action cannot currently render useful yaml;\n    instead, it returns an entire catalog. Use json instead.\n\n## EXAMPLES\n**apply**\n\nApply the locally cached catalog:\n\n\\$ puppet catalog apply \\--terminus yaml\n\nRetrieve a catalog from the master and apply it, in one step:\n\n\\$ puppet catalog apply \\--terminus rest\n\nAPI example:\n\n\n\n    ## ...    Puppet::Face[:catalog, '0.0.1'].download\n    ## (Termini are singletons; catalog.download has a side effect of    ## setting the catalog terminus to yaml)    report  = Puppet::Face[:catalog, '0.0.1'].apply\n    ## ...\n**compile**\n\nCompile catalog for node \\'mynode\\':\n\n\\$ puppet catalog compile mynode \\--codedir \\...\n\n**download**\n\nRetrieve and store a catalog:\n\n\\$ puppet catalog download\n\nAPI example:\n\n\n\n    Puppet::Face[:plugin, '0.0.1'].download\n    Puppet::Face[:facts, '0.0.1'].upload\n    Puppet::Face[:catalog, '0.0.1'].download\n    ## ...\n**select**\n\nAsk the puppet master for a list of managed file resources for a node:\n\n\\$ puppet catalog select \\--terminus rest somenode.magpie.lan file\n\n## NOTES\nThis subcommand is an indirector face, which exposes **find**,\n**search**, **save**, and **destroy** actions for an indirected\nsubsystem of Puppet. Valid termini for this face include:\n\n-   **compiler**\n\n-   **json**\n\n-   **msgpack**\n\n-   **rest**\n\n-   **store_configs**\n\n-   **yaml**\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/config.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet config'\ncanonical: \"/puppet/latest/man/config.html\"\n---\n\n# Man Page: puppet config\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-config** - Interact with Puppet\\'s settings.\n\n## SYNOPSIS\npuppet config *action* \\[\\--section SECTION_NAME\\]\n\n## DESCRIPTION\nThis subcommand can inspect and modify settings from Puppet\\'s\n\\'puppet.conf\\' configuration file. For documentation about individual\nsettings, see https://puppet.com/docs/puppet/latest/configuration.html.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n\\--section SECTION_NAME\n\n:   The section of the puppet.conf configuration file to interact with.\n\n    The three most commonly used sections are \\'main\\', \\'server\\', and\n    \\'agent\\'. \\'Main\\' is the default, and is used by all Puppet\n    applications. Other sections can override \\'main\\' values for\n    specific applications \\-\\-- the \\'server\\' section affects Puppet\n    Server, and the \\'agent\\' section affects puppet agent.\n\n    Less commonly used is the \\'user\\' section, which affects puppet\n    apply. Any other section will be treated as the name of a legacy\n    environment (a deprecated feature), and can only include the\n    \\'manifest\\' and \\'modulepath\\' settings.\n\n## ACTIONS\n**delete** - Delete a Puppet setting.\n\n:   **SYNOPSIS**\n\n    puppet config delete \\[\\--section SECTION_NAME\\] *setting*\n\n    **DESCRIPTION**\n\n    Deletes a setting from the specified section. (The default is the\n    section \\'main\\').\n\n    **NOTES**\n\n    By default, this action deletes the configuration setting from the\n    \\'main\\' configuration domain. Use the \\'\\--section\\' flags to\n    delete settings from other configuration domains.\n\n**print** - Examine Puppet\\'s current settings.\n\n:   **SYNOPSIS**\n\n    puppet config print \\[\\--section SECTION_NAME\\] all \\| *setting*\n    \\[*setting* \\...\\]\n\n    **DESCRIPTION**\n\n    Prints the value of a single setting or a list of settings.\n\n    This action is a replacement interface to the information available\n    with **puppet \\<subcommand\\> \\--configprint**.\n\n    **NOTES**\n\n    By default, this action reads the general configuration in the\n    \\'main\\' section. Use the \\'\\--section\\' and \\'\\--environment\\'\n    flags to examine other configuration domains.\n\n**set** - Set Puppet\\'s settings.\n\n:   **SYNOPSIS**\n\n    puppet config set \\[\\--section SECTION_NAME\\] \\[setting_name\\]\n    \\[setting_value\\]\n\n    **DESCRIPTION**\n\n    Updates values in the **puppet.conf** configuration file.\n\n    **NOTES**\n\n    By default, this action manipulates the configuration in the\n    \\'main\\' section. Use the \\'\\--section\\' flag to manipulate other\n    configuration domains.\n\n## EXAMPLES\n**delete**\n\nDelete the setting \\'setting_name\\' from the \\'main\\' configuration\ndomain:\n\n\\$ puppet config delete setting_name\n\nDelete the setting \\'setting_name\\' from the \\'server\\' configuration\ndomain:\n\n\\$ puppet config delete setting_name \\--section server\n\n**print**\n\nGet puppet\\'s runfile directory:\n\n\\$ puppet config print rundir\n\nGet a list of important directories from the server\\'s config:\n\n\\$ puppet config print all \\--section server \\| grep -E \\\"(path\\|dir)\\\"\n\n**set**\n\nSet puppet\\'s runfile directory:\n\n\\$ puppet config set rundir /var/run/puppetlabs\n\nSet the vardir for only the agent:\n\n\\$ puppet config set vardir /opt/puppetlabs/puppet/cache \\--section\nagent\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/describe.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet describe'\ncanonical: \"/puppet/latest/man/describe.html\"\n---\n\n# Man Page: puppet describe\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-describe** - Display help about resource types\n\n## SYNOPSIS\nPrints help about Puppet resource types, providers, and metaparameters.\n\n## USAGE\npuppet describe \\[-h\\|\\--help\\] \\[-s\\|\\--short\\] \\[-p\\|\\--providers\\]\n\\[-l\\|\\--list\\] \\[-m\\|\\--meta\\]\n\n## OPTIONS\n\\--help\n\n:   Print this help text\n\n\\--providers\n\n:   Describe providers in detail for each type\n\n\\--list\n\n:   List all types\n\n\\--meta\n\n:   List all metaparameters\n\n\\--short\n\n:   List only parameters without detail\n\n## EXAMPLE\n\n    $ puppet describe --list\n    $ puppet describe file --providers\n    $ puppet describe user -s -m\n\n## AUTHOR\nDavid Lutterkort\n\n## COPYRIGHT\nCopyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/device.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet device'\ncanonical: \"/puppet/latest/man/device.html\"\n---\n\n# Man Page: puppet device\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-device** - Manage remote network devices\n\n## SYNOPSIS\nRetrieves catalogs from the Puppet master and applies them to remote\ndevices.\n\nThis subcommand can be run manually; or periodically using cron, a\nscheduled task, or a similar tool.\n\n## USAGE\npuppet device \\[-h\\|\\--help\\] \\[-v\\|\\--verbose\\] \\[-d\\|\\--debug\\]\n\\[-l\\|\\--logdest syslog\\|*file*\\|console\\] \\[\\--detailed-exitcodes\\]\n\\[\\--deviceconfig *file*\\] \\[-w\\|\\--waitforcert *seconds*\\] \\[\\--libdir\n*directory*\\] \\[-a\\|\\--apply *file*\\] \\[-f\\|\\--facts\\] \\[-r\\|\\--resource\n*type* \\[name\\]\\] \\[-t\\|\\--target *device*\\] \\[\\--user=*user*\\]\n\\[-V\\|\\--version\\]\n\n## DESCRIPTION\nDevices require a proxy Puppet agent to request certificates, collect\nfacts, retrieve and apply catalogs, and store reports.\n\n## USAGE NOTES\nDevices managed by the puppet-device subcommand on a Puppet agent are\nconfigured in device.conf, which is located at \\$confdir/device.conf by\ndefault, and is configurable with the \\$deviceconfig setting.\n\nThe device.conf file is an INI-like file, with one section per device:\n\n\\[*DEVICE_CERTNAME*\\] type *TYPE* url *URL* debug\n\nThe section name specifies the certname of the device.\n\nThe values for the type and url properties are specific to each type of\ndevice.\n\nThe optional debug property specifies transport-level debugging, and is\nlimited to telnet and ssh transports.\n\nSee https://puppet.com/docs/puppet/latest/config_file_device.html for\ndetails.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument. For example, \\'server\\' is a valid configuration\nparameter, so you can specify \\'\\--server *servername*\\' as an argument.\n\n\\--help, -h\n\n:   Print this help message\n\n\\--verbose, -v\n\n:   Turn on verbose reporting.\n\n\\--debug, -d\n\n:   Enable full debugging.\n\n\\--logdest, -l\n\n:   Where to send log messages. Choose between \\'syslog\\' (the POSIX\n    syslog service), \\'console\\', or the path to a log file. If\n    debugging or verbosity is enabled, this defaults to \\'console\\'.\n    Otherwise, it defaults to \\'syslog\\'. Multiple destinations can be\n    set using a comma separated list (eg:\n    **/path/file1,console,/path/file2**)\\\"\n\n    A path ending with \\'.json\\' will receive structured output in JSON\n    format. The log file will not have an ending \\'\\]\\' automatically\n    written to it due to the appending nature of logging. It must be\n    appended manually to make the content valid JSON.\n\n\\--detailed-exitcodes\n\n:   Provide transaction information via exit codes. If this is enabled,\n    an exit code of \\'1\\' means at least one device had a compile\n    failure, an exit code of \\'2\\' means at least one device had\n    resource changes, and an exit code of \\'4\\' means at least one\n    device had resource failures. Exit codes of \\'3\\', \\'5\\', \\'6\\', or\n    \\'7\\' means that a bitwise combination of the preceding exit codes\n    happened.\n\n\\--deviceconfig\n\n:   Path to the device config file for puppet device. Default:\n    \\$confdir/device.conf\n\n\\--waitforcert, -w\n\n:   This option only matters for targets that do not yet have\n    certificates and it is enabled by default, with a value of 120\n    (seconds). This causes +puppet device+ to poll the server every 2\n    minutes and ask it to sign a certificate request. This is useful for\n    the initial setup of a target. You can turn off waiting for\n    certificates by specifying a time of 0.\n\n\\--libdir\n\n:   Override the per-device libdir with a local directory. Specifying a\n    libdir also disables pluginsync. This is useful for testing.\n\n    A path ending with \\'.jsonl\\' will receive structured output in JSON\n    Lines format.\n\n\\--apply\n\n:   Apply a manifest against a remote target. Target must be specified.\n\n\\--facts\n\n:   Displays the facts of a remote target. Target must be specified.\n\n\\--resource\n\n:   Displays a resource state as Puppet code, roughly equivalent to\n    **puppet resource**. Can be filtered by title. Requires \\--target be\n    specified.\n\n\\--target\n\n:   Target a specific device/certificate in the device.conf. Doing so\n    will perform a device run against only that device/certificate.\n\n\\--to_yaml\n\n:   Output found resources in yaml format, suitable to use with Hiera\n    and create_resources.\n\n\\--user\n\n:   The user to run as.\n\n## EXAMPLE\n\n      $ puppet device --target remotehost --verbose\n\n## AUTHOR\nBrice Figureau\n\n## COPYRIGHT\nCopyright (c) 2011-2018 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/doc.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet doc'\ncanonical: \"/puppet/latest/man/doc.html\"\n---\n\n# Man Page: puppet doc\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-doc** - Generate Puppet references\n\n## SYNOPSIS\nGenerates a reference for all Puppet types. Largely meant for internal\nPuppet Inc. use. (Deprecated)\n\n## USAGE\npuppet doc \\[-h\\|\\--help\\] \\[-l\\|\\--list\\] \\[-r\\|\\--reference\n*reference-name*\\]\n\n## DESCRIPTION\nThis deprecated command generates a Markdown document to stdout\ndescribing all installed Puppet types or all allowable arguments to\npuppet executables. It is largely meant for internal use and is used to\ngenerate the reference document available on the Puppet Inc. web site.\n\nFor Puppet module documentation (and all other use cases) this command\nhas been superseded by the \\\"puppet-strings\\\" module - see\nhttps://github.com/puppetlabs/puppetlabs-strings for more information.\n\nThis command (puppet-doc) will be removed once the puppetlabs internal\ndocumentation processing pipeline is completely based on puppet-strings.\n\n## OPTIONS\n\\--help\n\n:   Print this help message\n\n\\--reference\n\n:   Build a particular reference. Get a list of references by running\n    \\'puppet doc \\--list\\'.\n\n## EXAMPLE\n\n    $ puppet doc -r type > /tmp/type_reference.markdown\n\n## AUTHOR\nLuke Kanies\n\n## COPYRIGHT\nCopyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/epp.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet epp'\ncanonical: \"/puppet/latest/man/epp.html\"\n---\n\n# Man Page: puppet epp\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-epp** - Interact directly with the EPP template\nparser/renderer.\n\n## SYNOPSIS\npuppet epp *action*\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n## ACTIONS\n-   **dump** - Outputs a dump of the internal template parse tree for\n    debugging: **SYNOPSIS**\n\n    puppet epp dump \\[\\--e *source*\\] \\[\\--\\[no-\\]validate\\] \\[\\--format\n    *old, pn, or json*\\] \\[\\--pretty\\] \\[\\--\\[no-\\]header\\] \\[\\--format\n    *old\\|pn\\|json*\\] \\[\\--pretty\\] { -e *source* \\| \\[*templates*\n    \\...\\] }\n\n    **DESCRIPTION**\n\n    The dump action parses and validates the EPP syntax and dumps the\n    resulting AST model in a human readable (but not necessarily an easy\n    to understand) format.\n\n    The output format can be controlled using the \\--format\n    *old\\|pn\\|json* where:\n\n-   \\'old\\' is the default, but now deprecated format which is not API.\n\n-   \\'pn\\' is the Puppet Extended S-Expression Notation.\n\n-   \\'json\\' outputs the same graph as \\'pn\\' but with JSON syntax.\n\n    The output will be \\\"pretty printed\\\" when the option \\--pretty is\n    given together with \\--format \\'pn\\' or \\'json\\'. This option has no\n    effect on the \\'old\\' format.\n\n    The command accepts one or more templates (.epp) files, or an -e\n    followed by the template source text. The given templates can be\n    paths to template files, or references to templates in modules when\n    given on the form *modulename*/*template-name*.epp. If no arguments\n    are given, the stdin is read (unless it is attached to a terminal)\n\n    If multiple templates are given, they are separated with a header\n    indicating the name of the template. This can be suppressed with the\n    option \\--no-header. The option \\--\\[no-\\]header has no effect when\n    a single template is dumped.\n\n    When debugging the epp parser itself, it may be useful to suppress\n    the validation step with the **\\--no-validate** option to observe\n    what the parser produced from the given source.\n\n    This command ignores the \\--render-as setting/option.\n\n    **OPTIONS** *\\--e \\<source*\\> - Dump one epp source expression given\n    on the command line.\n\n    *\\--format \\<old, pn, or json*\\> - Get result in \\'old\\' (deprecated\n    format), \\'pn\\' (new format), or \\'json\\' (new format in JSON).\n\n    *\\--\\[no-\\]header* - Whether or not to show a file name header\n    between files.\n\n    *\\--pretty* - Pretty print output. Only applicable together with\n    \\--format pn or json\n\n    *\\--\\[no-\\]validate* - Whether or not to validate the parsed result,\n    if no-validate only syntax errors are reported.\n\n    **RETURNS**\n\n    A dump of the resulting AST model unless there are syntax or\n    validation errors.\n\n-   **render** - Renders an epp template as text: **SYNOPSIS**\n\n    puppet epp render \\[\\--node *node_name*\\] \\[\\--e *source*\\]\n    \\[\\--values *values_hash*\\] \\[\\--values_file *pp_or_yaml_file*\\]\n    \\[\\--facts *facts_file*\\] \\[\\--\\[no-\\]header\\] -e *source* \\|\n    \\[*templates* \\...\\]\n\n    **DESCRIPTION**\n\n    This action renders one or more EPP templates.\n\n    The command accepts one or more templates (.epp files), given the\n    same way as templates are given to the puppet **epp** function (a\n    full path, or a relative reference on the form\n    \\'*modulename*/*template-name*.epp\\'), or as a relative path.args In\n    case the given path matches both a modulename/template and a file,\n    the template from the module is used.\n\n    An inline_epp equivalent can also be performed by giving the\n    template after an -e, or by piping the EPP source text to the\n    command.\n\n    Values to the template can be defined using the Puppet Language on\n    the command line with **\\--values** or in a .pp or .yaml file\n    referenced with **\\--values_file**. If specifying both the result is\n    merged with \\--values having higher precedence.\n\n    The \\--values option allows a Puppet Language sequence of\n    expressions to be defined on the command line the same way as it may\n    be given in a .pp file referenced with **\\--values_file**. It may\n    set variable values (that become available in the template), and\n    must produce either **undef** or a **Hash** of values (the hash may\n    be empty). Producing **undef** simulates that the template is called\n    without an arguments hash and thus only references variables in its\n    outer scope. When a hash is given, a template is limited to seeing\n    only the global scope. It is thus possible to simulate the different\n    types of calls to the **epp** and **inline_epp** functions, with or\n    without a given hash. Note that if variables are given, they are\n    always available in this simulation - to test that the template only\n    references variables given as arguments, produce a hash in \\--values\n    or the \\--values_file, do not specify any variables that are not\n    global, and turn on \\--strict_variables setting.\n\n    If multiple templates are given, the same set of values are given to\n    each template. If both \\--values and \\--value_file are used, the\n    \\--values are merged on top of those given in the file.\n\n    When multiple templates are rendered, a separating header is output\n    between the templates showing the name of the template before the\n    output. The header output can be turned off with **\\--no-header**.\n    This also concatenates the template results without any added\n    newline separators.\n\n    Facts from the node where the command is being run are used by\n    default.args Facts can be obtained for other nodes if they have\n    called in, and reported their facts by using the **\\--node\n    \\<nodename\\>** flag.\n\n    Overriding node facts as well as additional facts can be given in a\n    .yaml or .json file and referencing it with the \\--facts option.\n    (Values can be obtained in yaml format directly from **facter**, or\n    from puppet for a given node). Note that it is not possible to\n    simulate the reserved variable name **\\$facts** in any other way.\n\n    Note that it is not possible to set variables using the Puppet\n    Language that have the same names as facts as this result in an\n    error; \\\"attempt to redefine a variable\\\" since facts are set first.\n\n    Exits with 0 if there were no validation errors. On errors, no\n    rendered output is produced for that template file.\n\n    When designing EPP templates, it is strongly recommended to define\n    all template arguments in the template, and to give them in a hash\n    when calling **epp** or **inline_epp** and to use as few global\n    variables as possible, preferably only the \\$facts hash. This makes\n    templates more free standing and are easier to reuse, and to test.\n\n    **OPTIONS** *\\--e \\<source*\\> - Render one inline epp template given\n    on the command line.\n\n    *\\--facts \\<facts_file*\\> - A .yaml or .json file containing a hash\n    of facts made available in \\$facts and \\$trusted\n\n    *\\--\\[no-\\]header* - Whether or not to show a file name header\n    between rendered results.\n\n    *\\--node \\<node_name*\\> - The name of the node for which facts are\n    obtained. Defaults to facts for the local node.\n\n    *\\--values \\<values_hash*\\> - A Hash in Puppet DSL form given as\n    arguments to the template being rendered.\n\n    *\\--values_file \\<pp_or_yaml_file*\\> - A .pp or .yaml file that is\n    processed to produce a hash of values for the template.\n\n    **RETURNS**\n\n    A rendered result of one or more given templates.\n\n-   **validate** - Validate the syntax of one or more EPP templates.:\n    **SYNOPSIS**\n\n    puppet epp validate \\[\\--\\[no-\\]continue_on_error\\] \\[*template*\\]\n    \\[*template* \\...\\]\n\n    **DESCRIPTION**\n\n    This action validates EPP syntax without producing any output.\n\n    When validating, multiple issues per file are reported up to the\n    settings of max_error, and max_warnings. The processing stops after\n    having reported issues for the first encountered file with errors\n    unless the option \\--continue_on_error is given.\n\n    Files can be given using the **modulename/template.epp** style to\n    lookup the template from a module, or be given as a reference to a\n    file. If the reference to a file can be resolved against a template\n    in a module, the module version wins - in this case use an absolute\n    path to reference the template file if the module version is not\n    wanted.\n\n    Exits with 0 if there were no validation errors.\n\n    **OPTIONS** *\\--\\[no-\\]continue_on_error* - Whether or not to\n    continue after errors are reported for a template.\n\n    **RETURNS**\n\n    Nothing, or encountered syntax errors.\n\n## EXAMPLES\n**render**\n\nRender the template in module \\'mymodule\\' called \\'mytemplate.epp\\',\nand give it two arguments **a** and **b**:\n\n\n\n    $ puppet epp render mymodule/mytemplate.epp --values '{a => 10, b => 20}'\n\nRender a template using an absolute path:\n\n\n\n    $ puppet epp render /tmp/testing/mytemplate.epp --values '{a => 10, b => 20}'\n\nRender a template with data from a .pp file:\n\n\n\n    $ puppet epp render /tmp/testing/mytemplate.epp --values_file mydata.pp\n\nRender a template with data from a .pp file and override one value on\nthe command line:\n\n\n\n    $ puppet epp render /tmp/testing/mytemplate.epp --values_file mydata.pp --values '{a=>10}'\n\nRender from STDIN:\n\n\n\n    $ cat template.epp | puppet epp render --values '{a => 10, b => 20}'\n\nSet variables in a .pp file and render a template that uses variable\nreferences:\n\n\n\n    ## data.pp file    $greeted = 'a global var'\n    undef\n\n    $ puppet epp render -e 'hello <%= $greeted %>' --values_file data.pp\n\nRender a template that outputs a fact:\n\n\n\n    $ facter --yaml > data.yaml\n    $ puppet epp render -e '<% $facts[osfamily] %>' --facts data.yaml\n\n**validate**\n\nValidate the template \\'template.epp\\' in module \\'mymodule\\':\n\n\n\n    $ puppet epp validate mymodule/template.epp\n\nValidate two arbitrary template files:\n\n\n\n    $ puppet epp validate mymodule/template1.epp yourmodule/something.epp\n\nValidate a template somewhere in the file system:\n\n\n\n      $ puppet epp validate /tmp/testing/template1.epp\n\nValidate a template against a file relative to the current directory:\n\n\n\n     $ puppet epp validate template1.epp\n     $ puppet epp validate ./template1.epp\n\nValidate from STDIN:\n\n\n\n    $ cat template.epp | puppet epp validate\n\nContinue on error to see errors for all templates:\n\n\n\n    $ puppet epp validate mymodule/template1.epp mymodule/template2.epp --continue_on_error\n\n## COPYRIGHT AND LICENSE\nCopyright 2014 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/facts.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet facts'\ncanonical: \"/puppet/latest/man/facts.html\"\n---\n\n# Man Page: puppet facts\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-facts** - Retrieve and store facts.\n\n## SYNOPSIS\npuppet facts *action* \\[\\--terminus \\_TERMINUS\\]\n\n## DESCRIPTION\nThis subcommand manages facts, which are collections of normalized\nsystem information used by Puppet. It can read facts directly from the\nlocal system (with the default **facter** terminus).\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n\\--terminus \\_TERMINUS\n\n:   Indirector faces expose indirected subsystems of Puppet. These\n    subsystems are each able to retrieve and alter a specific type of\n    data (with the familiar actions of **find**, **search**, **save**,\n    and **destroy**) from an arbitrary number of pluggable backends. In\n    Puppet parlance, these backends are called terminuses.\n\n    Almost all indirected subsystems have a **rest** terminus that\n    interacts with the puppet master\\'s data. Most of them have\n    additional terminuses for various local data models, which are in\n    turn used by the indirected subsystem on the puppet master whenever\n    it receives a remote request.\n\n    The terminus for an action is often determined by context, but\n    occasionally needs to be set explicitly. See the \\\"Notes\\\" section\n    of this face\\'s manpage for more details.\n\n## ACTIONS\n**find** - Retrieve a node\\'s facts.\n\n:   **SYNOPSIS**\n\n    puppet facts find \\[\\--terminus \\_TERMINUS\\] \\[*node_certname*\\]\n\n    **DESCRIPTION**\n\n    Retrieve a node\\'s facts.\n\n    **RETURNS**\n\n    A hash containing some metadata and (under the \\\"values\\\" key) the\n    set of facts for the requested node. When used from the Ruby API: A\n    Puppet::Node::Facts object.\n\n    RENDERING ISSUES: Facts cannot currently be rendered as a string;\n    use yaml or json.\n\n    **NOTES**\n\n    When using the **facter** terminus, the host argument is ignored.\n\n**info** - Print the default terminus class for this face.\n\n:   **SYNOPSIS**\n\n    puppet facts info \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Prints the default terminus class for this subcommand. Note that\n    different run modes may have different default termini; when in\n    doubt, specify the run mode with the \\'\\--run_mode\\' option.\n\n**save** - API only: create or overwrite an object.\n\n:   **SYNOPSIS**\n\n    puppet facts save \\[\\--terminus \\_TERMINUS\\] *key*\n\n    **DESCRIPTION**\n\n    API only: create or overwrite an object. As the Faces framework does\n    not currently accept data from STDIN, save actions cannot currently\n    be invoked from the command line.\n\n**show** - Retrieve current node\\'s facts.\n\n:   **SYNOPSIS**\n\n    puppet facts \\[\\--terminus \\_TERMINUS\\] \\[\\--config-file *path*\\]\n    \\[\\--custom-dir *path*\\] \\[\\--external-dir *path*\\] \\[\\--no-block\\]\n    \\[\\--no-cache\\] \\[\\--show-legacy\\] \\[\\--value-only\\] \\[\\--timing\\]\n    \\[*facts*\\]\n\n    **DESCRIPTION**\n\n    Reads facts from the local system using **facter** terminus. A query\n    can be provided to retrieve just a specific fact or a set of facts.\n\n    **OPTIONS** *\\--config-file \\<path*\\> - The location of the config\n    file for Facter.\n\n    *\\--custom-dir \\<path*\\> - The path to a directory that contains\n    custom facts.\n\n    *\\--external-dir \\<path*\\> - The path to a directory that contains\n    external facts.\n\n    *\\--no-block* - Disable fact blocking mechanism.\n\n    *\\--no-cache* - Disable fact caching mechanism.\n\n    *\\--show-legacy* - Show legacy facts when querying all facts.\n\n    *\\--timing* - Show how much time it took to resolve each fact.\n\n    *\\--value-only* - Show only the value when the action is called with\n    a single query\n\n    **RETURNS**\n\n    The output of facter with added puppet specific facts.\n\n    **NOTES**\n\n**upload** - Upload local facts to the puppet master.\n\n:   **SYNOPSIS**\n\n    puppet facts upload \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Reads facts from the local system using the **facter** terminus,\n    then saves the returned facts using the rest terminus.\n\n    **RETURNS**\n\n    Nothing.\n\n    **NOTES**\n\n    This action requires that the Puppet Server\\'s **auth.conf** file\n    allow **PUT** or **save** access to the **/puppet/v3/facts** API\n    endpoint.\n\n    For details on configuring Puppet Server\\'s **auth.conf**, see:\n\n    *https://puppet.com/docs/puppetserver/latest/config_file_auth.html*\n\n## EXAMPLES\n**find**\n\nGet facts from the local system:\n\n\\$ puppet facts find\n\n**show**\n\nretrieve facts:\n\n\\$ puppet facts show os\n\n**upload**\n\nUpload facts:\n\n\\$ puppet facts upload\n\n## NOTES\nThis subcommand is an indirector face, which exposes **find**,\n**search**, **save**, and **destroy** actions for an indirected\nsubsystem of Puppet. Valid termini for this face include:\n\n-   **facter**\n\n-   **json**\n\n-   **memory**\n\n-   **network_device**\n\n-   **rest**\n\n-   **store_configs**\n\n-   **yaml**\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/filebucket.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet filebucket'\ncanonical: \"/puppet/latest/man/filebucket.html\"\n---\n\n# Man Page: puppet filebucket\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-filebucket** - Store and retrieve files in a filebucket\n\n## SYNOPSIS\nA stand-alone Puppet filebucket client.\n\n## USAGE\npuppet filebucket *mode* \\[-h\\|\\--help\\] \\[-V\\|\\--version\\]\n\\[-d\\|\\--debug\\] \\[-v\\|\\--verbose\\] \\[-l\\|\\--local\\] \\[-r\\|\\--remote\\]\n\\[-s\\|\\--server *server*\\] \\[-f\\|\\--fromdate *date*\\] \\[-t\\|\\--todate\n*date*\\] \\[-b\\|\\--bucket *directory*\\] *file* *file* \\...\n\nPuppet filebucket can operate in three modes, with only one mode per\ncall:\n\nbackup: Send one or more files to the specified file bucket. Each sent\nfile is printed with its resulting sha256 sum.\n\nget: Return the text associated with an sha256 sum. The text is printed\nto stdout, and only one file can be retrieved at a time.\n\nrestore: Given a file path and an sha256 sum, store the content\nassociated with the sum into the specified file path. You can specify an\nentirely new path to this argument; you are not restricted to restoring\nthe content to its original location.\n\ndiff: Print a diff in unified format between two checksums in the\nfilebucket or between a checksum and its matching file.\n\nlist: List all files in the current local filebucket. Listing remote\nfilebuckets is not allowed.\n\n## DESCRIPTION\nThis is a stand-alone filebucket client for sending files to a local or\ncentral filebucket.\n\nNote that \\'filebucket\\' defaults to using a network-based filebucket\navailable on the server named \\'puppet\\'. To use this, you\\'ll have to\nbe running as a user with valid Puppet certificates. Alternatively, you\ncan use your local file bucket by specifying \\'\\--local\\', or by\nspecifying \\'\\--bucket\\' with a local path.\n\n**Important**: When you enable and use the backup option, and by\nextension the filebucket resource, you must ensure that sufficient disk\nspace is available for the file backups. Generally, you can provide the\ndisk space by using one of the following two options:\n\n-   Use a **find** command and **crontab** entry to retain only the last\n    X days of file backups. For example:\n\n\n\n      find /opt/puppetlabs/server/data/puppetserver/bucket -type f -mtime +45 -atime +45 -print0 | xargs -0 rm\n\n-   Restrict the directory to a maximum size after which the oldest\n    items are removed.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument. For example, \\'ssldir\\' is a valid setting, so you\ncan specify \\'\\--ssldir *directory*\\' as an argument.\n\nSee the configuration file documentation at\nhttps://puppet.com/docs/puppet/latest/configuration.html for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with \\'\\--genconfig\\'.\n\n\\--bucket\n\n:   Specify a local filebucket path. This overrides the default path set\n    in \\'\\$clientbucketdir\\'.\n\n\\--debug\n\n:   Enable full debugging.\n\n\\--fromdate\n\n:   (list only) Select bucket files from \\'fromdate\\'.\n\n\\--help\n\n:   Print this help message.\n\n\\--local\n\n:   Use the local filebucket. This uses the default configuration\n    information and the bucket located at the \\'\\$clientbucketdir\\'\n    setting by default. If \\'\\--bucket\\' is set, puppet uses that path\n    instead.\n\n\\--remote\n\n:   Use a remote filebucket. This uses the default configuration\n    information and the bucket located at the \\'\\$bucketdir\\' setting by\n    default.\n\n\\--server_list\n\n:   A list of comma separated servers; only the first entry is used for\n    file storage. This setting takes precidence over **server**.\n\n\\--server\n\n:   The server to use for file storage. This setting is only used if\n    **server_list** is not set.\n\n\\--todate\n\n:   (list only) Select bucket files until \\'todate\\'.\n\n\\--verbose\n\n:   Print extra information.\n\n\\--version\n\n:   Print version information.\n\n## EXAMPLES\n\n    ### Backup a file to the filebucket, then restore it to a temporary directory    $ puppet filebucket backup /etc/passwd\n    /etc/passwd: 429b225650b912a2ee067b0a4cf1e949\n    $ puppet filebucket restore /tmp/passwd 429b225650b912a2ee067b0a4cf1e949\n\n    ### Diff between two files in the filebucket    $ puppet filebucket -l diff d43a6ecaa892a1962398ac9170ea9bf2 7ae322f5791217e031dc60188f4521ef\n    1a2\n    > again\n\n    ### Diff between the file in the filebucket and a local file    $ puppet filebucket -l diff d43a6ecaa892a1962398ac9170ea9bf2 /tmp/testFile\n    1a2\n    > again\n\n    ### Backup a file to the filebucket and observe that it keeps each backup separate    $ puppet filebucket -l list\n    d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n\n    $ echo again >> /tmp/TestFile\n\n    $ puppet filebucket -l backup /tmp/TestFile\n    /tmp/TestFile: 7ae322f5791217e031dc60188f4521ef\n\n    $ puppet filebucket -l list\n    d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n    7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n    ### List files in a filebucket within date ranges    $ puppet filebucket -l -f 2015-01-01 -t 2015-01-11 list\n    <Empty Output>\n\n    $ puppet filebucket -l -f 2015-05-10 list\n    d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n    7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n    $ puppet filebucket -l -f \"2015-05-11 09:30:00\" list\n    7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n    $ puppet filebucket -l -t \"2015-05-11 09:30:00\" list\n    d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n\n    ### Manage files in a specific local filebucket    $ puppet filebucket -b /tmp/TestBucket backup /tmp/TestFile2\n    /tmp/TestFile2: d41d8cd98f00b204e9800998ecf8427e\n    $ puppet filebucket -b /tmp/TestBucket list\n    d41d8cd98f00b204e9800998ecf8427e 2015-05-11 09:33:22 /tmp/TestFile2\n\n    ### From a Puppet Server, list files in the server bucketdir    $ puppet filebucket -b $(puppet config print bucketdir --section server) list\n    d43a6ecaa892a1962398ac9170ea9bf2 2015-05-11 09:27:56 /tmp/TestFile\n    7ae322f5791217e031dc60188f4521ef 2015-05-11 09:52:15 /tmp/TestFile\n\n## AUTHOR\nLuke Kanies\n\n## COPYRIGHT\nCopyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/generate.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet generate'\ncanonical: \"/puppet/latest/man/generate.html\"\n---\n\n# Man Page: puppet generate\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-generate** - Generates Puppet code from Ruby definitions.\n\n## SYNOPSIS\npuppet generate *action*\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n## ACTIONS\n**types** - Generates Puppet code for custom types\n\n:   **SYNOPSIS**\n\n    puppet generate types \\[\\--format *format*\\] \\[\\--force\\]\n\n    **DESCRIPTION**\n\n    Generates definitions for custom resource types using Puppet code.\n\n    Types defined in Puppet code can be used to isolate custom type\n    definitions between different environments.\n\n    **OPTIONS** *\\--force* - Forces the generation of output files\n    (skips up-to-date checks).\n\n    *\\--format \\<format*\\> - The generation output format to use.\n    Supported formats: pcore.\n\n## EXAMPLES\n**types**\n\nGenerate Puppet type definitions for all custom resource types in the\ncurrent environment:\n\n\n\n    $ puppet generate types\n\nGenerate Puppet type definitions for all custom resource types in the\nspecified environment:\n\n\n\n    $ puppet generate types --environment development\n\n## COPYRIGHT AND LICENSE\nCopyright 2016 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/help.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet help'\ncanonical: \"/puppet/latest/man/help.html\"\n---\n\n# Man Page: puppet help\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-help** - Display Puppet help.\n\n## SYNOPSIS\npuppet help *action*\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n## ACTIONS\n**help** - Display help about Puppet subcommands and their actions.\n\n:   **SYNOPSIS**\n\n    puppet help \\[\\--version VERSION\\] \\[\\--ronn\\] \\[*subcommand*\\]\n    \\[*action*\\]\n\n    **DESCRIPTION**\n\n    Display help about Puppet subcommands and their actions.\n\n    **OPTIONS** *\\--ronn* - Whether to render the help text in ronn\n    format.\n\n    *\\--version VERSION* - The version of the subcommand for which to\n    show help.\n\n    **RETURNS**\n\n    Short help text for the specified subcommand or action.\n\n## EXAMPLES\n**help**\n\nGet help for an action:\n\n\\$ puppet help\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/lookup.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet lookup'\ncanonical: \"/puppet/latest/man/lookup.html\"\n---\n\n# Man Page: puppet lookup\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-lookup** - Interactive Hiera lookup\n\n## SYNOPSIS\nDoes Hiera lookups from the command line.\n\nSince this command needs access to your Hiera data, make sure to run it\non a node that has a copy of that data. This usually means logging into\na Puppet Server node and running \\'puppet lookup\\' with sudo.\n\nThe most common version of this command is:\n\n\\'puppet lookup *KEY* \\--node *NAME* \\--environment *ENV* \\--explain\\'\n\n## USAGE\npuppet lookup \\[\\--help\\] \\[\\--type *TYPESTRING*\\] \\[\\--merge\nfirst\\|unique\\|hash\\|deep\\] \\[\\--knock-out-prefix *PREFIX-STRING*\\]\n\\[\\--sort-merged-arrays\\] \\[\\--merge-hash-arrays\\] \\[\\--explain\\]\n\\[\\--environment *ENV*\\] \\[\\--default *VALUE*\\] \\[\\--node *NODE-NAME*\\]\n\\[\\--facts *FILE*\\] \\[\\--compile\\] \\[\\--render-as\ns\\|json\\|yaml\\|binary\\|msgpack\\] *keys*\n\n## DESCRIPTION\nThe lookup command is a CLI for Puppet\\'s \\'lookup()\\' function. It\nsearches your Hiera data and returns a value for the requested lookup\nkey, so you can test and explore your data. It is a modern replacement\nfor the \\'hiera\\' command. Lookup uses the setting for global hiera.yaml\nfrom puppet\\'s config, and the environment to find the environment level\nhiera.yaml as well as the resulting modulepath for the environment (for\nhiera.yaml files in modules). Hiera usually relies on a node\\'s facts to\nlocate the relevant data sources. By default, \\'puppet lookup\\' uses\nfacts from the node you run the command on, but you can get data for any\nother node with the \\'\\--node *NAME*\\' option. If possible, the lookup\ncommand will use the requested node\\'s real stored facts from PuppetDB;\nif PuppetDB isn\\'t configured or you want to provide arbitrary fact\nvalues, you can pass alternate facts as a JSON or YAML file with\n\\'\\--facts *FILE*\\'.\n\nIf you\\'re debugging your Hiera data and want to see where values are\ncoming from, use the \\'\\--explain\\' option.\n\nIf \\'\\--explain\\' isn\\'t specified, lookup exits with 0 if a value was\nfound and 1 otherwise. With \\'\\--explain\\', lookup always exits with 0\nunless there is a major error.\n\nYou can provide multiple lookup keys to this command, but it only\nreturns a value for the first found key, omitting the rest.\n\nFor more details about how Hiera works, see the Hiera documentation:\nhttps://puppet.com/docs/puppet/latest/hiera_intro.html\n\n## OPTIONS\n-   \\--help: Print this help message.\n\n-   \\--explain Explain the details of how the lookup was performed and\n    where the final value came from (or the reason no value was found).\n\n-   \\--node *NODE-NAME* Specify which node to look up data for; defaults\n    to the node where the command is run. Since Hiera\\'s purpose is to\n    provide different values for different nodes (usually based on their\n    facts), you\\'ll usually want to use some specific node\\'s facts to\n    explore your data. If the node where you\\'re running this command is\n    configured to talk to PuppetDB, the command will use the requested\n    node\\'s most recent facts. Otherwise, you can override facts with\n    the \\'\\--facts\\' option.\n\n-   \\--facts *FILE* Specify a .json or .yaml file of key =\\> value\n    mappings to override the facts for this lookup. Any facts not\n    specified in this file maintain their original value.\n\n-   \\--environment *ENV* Like with most Puppet commands, you can specify\n    an environment on the command line. This is important for lookup\n    because different environments can have different Hiera data. This\n    environment will be always be the one used regardless of any other\n    factors.\n\n-   \\--merge first\\|unique\\|hash\\|deep: Specify the merge behavior,\n    overriding any merge behavior from the data\\'s lookup_options.\n    \\'first\\' returns the first value found. \\'unique\\' appends\n    everything to a merged, deduplicated array. \\'hash\\' performs a\n    simple hash merge by overwriting keys of lower lookup priority.\n    \\'deep\\' performs a deep merge on values of Array and Hash type.\n    There are additional options that can be used with \\'deep\\'.\n\n-   \\--knock-out-prefix *PREFIX-STRING* Can be used with the \\'deep\\'\n    merge strategy. Specifies a prefix to indicate a value should be\n    removed from the final result.\n\n-   \\--sort-merged-arrays Can be used with the \\'deep\\' merge strategy.\n    When this flag is used, all merged arrays are sorted.\n\n-   \\--merge-hash-arrays Can be used with the \\'deep\\' merge strategy.\n    When this flag is used, hashes WITHIN arrays are deep-merged with\n    their counterparts by position.\n\n-   \\--explain-options Explain whether a lookup_options hash affects\n    this lookup, and how that hash was assembled. (lookup_options is how\n    Hiera configures merge behavior in data.)\n\n-   \\--default *VALUE* A value to return if Hiera can\\'t find a value in\n    data. For emulating calls to the \\'lookup()\\' function that include\n    a default.\n\n-   \\--type *TYPESTRING*: Assert that the value has the specified type.\n    For emulating calls to the \\'lookup()\\' function that include a data\n    type.\n\n-   \\--compile Perform a full catalog compilation prior to the lookup.\n    If your hierarchy and data only use the \\$facts, \\$trusted, and\n    \\$server_facts variables, you don\\'t need this option; however, if\n    your Hiera configuration uses arbitrary variables set by a Puppet\n    manifest, you might need this option to get accurate data. No\n    catalog compilation takes place unless this flag is given.\n\n-   \\--render-as s\\|json\\|yaml\\|binary\\|msgpack Specify the output\n    format of the results; \\\"s\\\" means plain text. The default when\n    producing a value is yaml and the default when producing an\n    explanation is s.\n\n## EXAMPLE\nTo look up \\'key_name\\' using the Puppet Server node\\'s facts: \\$ puppet\nlookup key_name\n\nTo look up \\'key_name\\' using the Puppet Server node\\'s arbitrary\nvariables from a manifest, and classify the node if applicable: \\$\npuppet lookup key_name \\--compile\n\nTo look up \\'key_name\\' using the Puppet Server node\\'s facts,\noverridden by facts given in a file: \\$ puppet lookup key_name \\--facts\nfact_file.yaml\n\nTo look up \\'key_name\\' with agent.local\\'s facts: \\$ puppet lookup\n\\--node agent.local key_name\n\nTo get the first value found for \\'key_name_one\\' and \\'key_name_two\\'\nwith agent.local\\'s facts while merging values and knocking out the\nprefix \\'foo\\' while merging: \\$ puppet lookup \\--node agent.local\n\\--merge deep \\--knock-out-prefix foo key_name_one key_name_two\n\nTo lookup \\'key_name\\' with agent.local\\'s facts, and return a default\nvalue of \\'bar\\' if nothing was found: \\$ puppet lookup \\--node\nagent.local \\--default bar key_name\n\nTo see an explanation of how the value for \\'key_name\\' would be found,\nusing agent.local\\'s facts: \\$ puppet lookup \\--node agent.local\n\\--explain key_name\n\n## COPYRIGHT\nCopyright (c) 2015 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/module.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet module'\ncanonical: \"/puppet/latest/man/module.html\"\n---\n\n# Man Page: puppet module\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-module** - Creates, installs and searches for modules on the\nPuppet Forge.\n\n## SYNOPSIS\npuppet module *action* \\[\\--environment production \\] \\[\\--modulepath \\]\n\n## DESCRIPTION\nThis subcommand can find, install, and manage modules from the Puppet\nForge, a repository of user-contributed Puppet code. It can also\ngenerate empty modules, and prepare locally developed modules for\nrelease on the Forge.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n\\--environment production\n\n:   The environment in which Puppet is running. For clients, such as\n    **puppet agent**, this determines the environment itself, which\n    Puppet uses to find modules and much more. For servers, such as\n    **puppet server**, this provides the default environment for nodes\n    that Puppet knows nothing about.\n\n    When defining an environment in the **\\[agent\\]** section, this\n    refers to the environment that the agent requests from the primary\n    server. The environment doesn\\'t have to exist on the local\n    filesystem because the agent fetches it from the primary server.\n    This definition is used when running **puppet agent**.\n\n    When defined in the **\\[user\\]** section, the environment refers to\n    the path that Puppet uses to search for code and modules related to\n    its execution. This requires the environment to exist locally on the\n    filesystem where puppet is being executed. Puppet subcommands,\n    including **puppet module** and **puppet apply**, use this\n    definition.\n\n    Given that the context and effects vary depending on the config\n    section\n    *https://puppet.com/docs/puppet/latest/config_file_main.html##config-sections*    in which the **environment** setting is defined, do not set it\n    globally.\n\n\\--modulepath\n\n:   The search path for modules, as a list of directories separated by\n    the system path separator character. (The POSIX path separator is\n    \\':\\', and the Windows path separator is \\';\\'.)\n\n    Setting a global value for **modulepath** in puppet.conf is not\n    allowed (but it can be overridden from the commandline). Please use\n    directory environments instead. If you need to use something other\n    than the default modulepath of **\\<ACTIVE ENVIRONMENT\\'S MODULES\n    DIR\\>:\\$basemodulepath**, you can set **modulepath** in\n    environment.conf. For more info, see\n    *https://puppet.com/docs/puppet/latest/environments_about.html*\n\n## ACTIONS\n**changes** - Show modified files of an installed module.\n\n:   **SYNOPSIS**\n\n    puppet module changes *path*\n\n    **DESCRIPTION**\n\n    Shows any files in a module that have been modified since it was\n    installed. This action compares the files on disk to the md5\n    checksums included in the module\\'s checksums.json or, if that is\n    missing, in metadata.json.\n\n    **RETURNS**\n\n    Array of strings representing paths of modified files.\n\n**install** - Install a module from the Puppet Forge or a release archive.\n\n:   **SYNOPSIS**\n\n    puppet module install \\[\\--force \\| -f\\] \\[\\--target-dir DIR \\| -i\n    DIR\\] \\[\\--ignore-dependencies\\] \\[\\--version VER \\| -v VER\\] *name*\n\n    **DESCRIPTION**\n\n    Installs a module from the Puppet Forge or from a release archive\n    file. Note: Module install uses MD5 checksums, which are prohibited\n    on FIPS enabled systems.\n\n    The specified module will be installed into the directory specified\n    with the **\\--target-dir** option, which defaults to the first\n    directory in the modulepath.\n\n    **OPTIONS** *\\--force* \\| *-f* - Force overwrite of existing module,\n    if any. Implies \\--ignore-dependencies.\n\n    *\\--ignore-dependencies* - Do not attempt to install dependencies.\n    Implied by \\--force.\n\n    *\\--target-dir DIR* \\| *-i DIR* - The directory into which modules\n    are installed; defaults to the first directory in the modulepath.\n\n    Specifying this option will change the installation directory, and\n    will use the existing modulepath when checking for dependencies. If\n    you wish to check a different set of directories for dependencies,\n    you must also use the **\\--environment** or **\\--modulepath**\n    options.\n\n    *\\--version VER* \\| *-v VER* - Module version to install; can be an\n    exact version or a requirement string, eg \\'\\>= 1.0.3\\'. Defaults to\n    latest version.\n\n    **RETURNS**\n\n    Pathname object representing the path to the installed module.\n\n**list** - List installed modules\n\n:   **SYNOPSIS**\n\n    puppet module list \\[\\--tree\\]\n\n    **DESCRIPTION**\n\n    Lists the installed puppet modules. By default, this action scans\n    the modulepath from puppet.conf\\'s **\\[main\\]** block; use the\n    \\--modulepath option to change which directories are scanned.\n\n    The output of this action includes information from the module\\'s\n    metadata, including version numbers and unmet module dependencies.\n\n    **OPTIONS** *\\--tree* - Whether to show dependencies as a tree view\n\n    **RETURNS**\n\n    hash of paths to module objects\n\n**uninstall** - Uninstall a puppet module.\n\n:   **SYNOPSIS**\n\n    puppet module uninstall \\[\\--force \\| -f\\] \\[\\--ignore-changes \\|\n    -c\\] \\[\\--version=\\] *name*\n\n    **DESCRIPTION**\n\n    Uninstalls a puppet module from the modulepath (or a specific target\n    directory). Note: Module uninstall uses MD5 checksums, which are\n    prohibited on FIPS enabled systems.\n\n    **OPTIONS** *\\--force* \\| *-f* - Force the uninstall of an installed\n    module even if there are local changes or the possibility of causing\n    broken dependencies.\n\n    *\\--ignore-changes* \\| *-c* - Uninstall an installed module even if\n    there are local changes to it. (Implied by \\--force.)\n\n    *\\--version=* - The version of the module to uninstall. When using\n    this option, a module matching the specified version must be\n    installed or else an error is raised.\n\n    **RETURNS**\n\n    Hash of module objects representing uninstalled modules and related\n    errors.\n\n**upgrade** - Upgrade a puppet module.\n\n:   **SYNOPSIS**\n\n    puppet module upgrade \\[\\--force \\| -f\\] \\[\\--ignore-dependencies\\]\n    \\[\\--ignore-changes \\| -c\\] \\[\\--version=\\] *name*\n\n    **DESCRIPTION**\n\n    Upgrades a puppet module. Note: Module upgrade uses MD5 checksums,\n    which are prohibited on FIPS enabled systems.\n\n    **OPTIONS** *\\--force* \\| *-f* - Force the upgrade of an installed\n    module even if there are local changes or the possibility of causing\n    broken dependencies. Implies \\--ignore-dependencies.\n\n    *\\--ignore-changes* \\| *-c* - Upgrade an installed module even if\n    there are local changes to it. (Implied by \\--force.)\n\n    *\\--ignore-dependencies* - Do not attempt to install dependencies.\n    Implied by \\--force.\n\n    *\\--version=* - The version of the module to upgrade to.\n\n    **RETURNS**\n\n    Hash\n\n## EXAMPLES\n**changes**\n\nShow modified files of an installed module:\n\n\\$ puppet module changes /etc/puppetlabs/code/modules/vcsrepo/ warning:\n1 files modified lib/puppet/provider/vcsrepo.rb\n\n**install**\n\nInstall a module:\n\n\\$ puppet module install puppetlabs-vcsrepo Preparing to install into\n/etc/puppetlabs/code/modules \\... Downloading from\nhttps://forgeapi.puppet.com \\... Installing \\-- do not interrupt \\...\n/etc/puppetlabs/code/modules └── puppetlabs-vcsrepo (v0.0.4)\n\nInstall a module to a specific environment:\n\n\\$ puppet module install puppetlabs-vcsrepo \\--environment development\nPreparing to install into\n/etc/puppetlabs/code/environments/development/modules \\... Downloading\nfrom https://forgeapi.puppet.com \\... Installing \\-- do not interrupt\n\\... /etc/puppetlabs/code/environments/development/modules └──\npuppetlabs-vcsrepo (v0.0.4)\n\nInstall a specific module version:\n\n\\$ puppet module install puppetlabs-vcsrepo -v 0.0.4 Preparing to\ninstall into /etc/puppetlabs/modules \\... Downloading from\nhttps://forgeapi.puppet.com \\... Installing \\-- do not interrupt \\...\n/etc/puppetlabs/code/modules └── puppetlabs-vcsrepo (v0.0.4)\n\nInstall a module into a specific directory:\n\n\\$ puppet module install puppetlabs-vcsrepo\n\\--target-dir=/opt/puppetlabs/puppet/modules Preparing to install into\n/opt/puppetlabs/puppet/modules \\... Downloading from\nhttps://forgeapi.puppet.com \\... Installing \\-- do not interrupt \\...\n/opt/puppetlabs/puppet/modules └── puppetlabs-vcsrepo (v0.0.4)\n\nInstall a module into a specific directory and check for dependencies in\nother directories:\n\n\\$ puppet module install puppetlabs-vcsrepo\n\\--target-dir=/opt/puppetlabs/puppet/modules \\--modulepath\n/etc/puppetlabs/code/modules Preparing to install into\n/opt/puppetlabs/puppet/modules \\... Downloading from\nhttps://forgeapi.puppet.com \\... Installing \\-- do not interrupt \\...\n/opt/puppetlabs/puppet/modules └── puppetlabs-vcsrepo (v0.0.4)\n\nInstall a module from a release archive:\n\n\\$ puppet module install puppetlabs-vcsrepo-0.0.4.tar.gz Preparing to\ninstall into /etc/puppetlabs/code/modules \\... Downloading from\nhttps://forgeapi.puppet.com \\... Installing \\-- do not interrupt \\...\n/etc/puppetlabs/code/modules └── puppetlabs-vcsrepo (v0.0.4)\n\nInstall a module from a release archive and ignore dependencies:\n\n\\$ puppet module install puppetlabs-vcsrepo-0.0.4.tar.gz\n\\--ignore-dependencies Preparing to install into\n/etc/puppetlabs/code/modules \\... Installing \\-- do not interrupt \\...\n/etc/puppetlabs/code/modules └── puppetlabs-vcsrepo (v0.0.4)\n\n**list**\n\nList installed modules:\n\n\\$ puppet module list /etc/puppetlabs/code/modules ├──\nbodepd-create_resources (v0.0.1) ├── puppetlabs-bacula (v0.0.2) ├──\npuppetlabs-mysql (v0.0.1) ├── puppetlabs-sqlite (v0.0.1) └──\npuppetlabs-stdlib (v2.2.1) /opt/puppetlabs/puppet/modules (no modules\ninstalled)\n\nList installed modules in a tree view:\n\n\\$ puppet module list \\--tree /etc/puppetlabs/code/modules └─┬\npuppetlabs-bacula (v0.0.2) ├── puppetlabs-stdlib (v2.2.1) ├─┬\npuppetlabs-mysql (v0.0.1) │ └── bodepd-create_resources (v0.0.1) └──\npuppetlabs-sqlite (v0.0.1) /opt/puppetlabs/puppet/modules (no modules\ninstalled)\n\nList installed modules from a specified environment:\n\n\\$ puppet module list \\--environment production\n/etc/puppetlabs/code/modules ├── bodepd-create_resources (v0.0.1) ├──\npuppetlabs-bacula (v0.0.2) ├── puppetlabs-mysql (v0.0.1) ├──\npuppetlabs-sqlite (v0.0.1) └── puppetlabs-stdlib (v2.2.1)\n/opt/puppetlabs/puppet/modules (no modules installed)\n\nList installed modules from a specified modulepath:\n\n\\$ puppet module list \\--modulepath /opt/puppetlabs/puppet/modules\n/opt/puppetlabs/puppet/modules (no modules installed)\n\n**uninstall**\n\nUninstall a module:\n\n\\$ puppet module uninstall puppetlabs-ssh Removed\n/etc/puppetlabs/code/modules/ssh (v1.0.0)\n\nUninstall a module from a specific directory:\n\n\\$ puppet module uninstall puppetlabs-ssh \\--modulepath\n/opt/puppetlabs/puppet/modules Removed\n/opt/puppetlabs/puppet/modules/ssh (v1.0.0)\n\nUninstall a module from a specific environment:\n\n\\$ puppet module uninstall puppetlabs-ssh \\--environment development\nRemoved /etc/puppetlabs/code/environments/development/modules/ssh\n(v1.0.0)\n\nUninstall a specific version of a module:\n\n\\$ puppet module uninstall puppetlabs-ssh \\--version 2.0.0 Removed\n/etc/puppetlabs/code/modules/ssh (v2.0.0)\n\n**upgrade**\n\nupgrade an installed module to the latest version\n\n\\$ puppet module upgrade puppetlabs-apache\n/etc/puppetlabs/puppet/modules └── puppetlabs-apache (v1.0.0 -\\> v2.4.0)\n\nupgrade an installed module to a specific version\n\n\\$ puppet module upgrade puppetlabs-apache \\--version 2.1.0\n/etc/puppetlabs/puppet/modules └── puppetlabs-apache (v1.0.0 -\\> v2.1.0)\n\nupgrade an installed module for a specific environment\n\n\\$ puppet module upgrade puppetlabs-apache \\--environment test\n/etc/puppetlabs/code/environments/test/modules └── puppetlabs-apache\n(v1.0.0 -\\> v2.4.0)\n\n## COPYRIGHT AND LICENSE\nCopyright 2012 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/node.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet node'\ncanonical: \"/puppet/latest/man/node.html\"\n---\n\n# Man Page: puppet node\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-node** - View and manage node definitions.\n\n## SYNOPSIS\npuppet node *action* \\[\\--terminus \\_TERMINUS\\]\n\n## DESCRIPTION\nThis subcommand interacts with node objects, which are used by Puppet to\nbuild a catalog. A node object consists of the node\\'s facts,\nenvironment, node parameters (exposed in the parser as top-scope\nvariables), and classes.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n\\--terminus \\_TERMINUS\n\n:   Indirector faces expose indirected subsystems of Puppet. These\n    subsystems are each able to retrieve and alter a specific type of\n    data (with the familiar actions of **find**, **search**, **save**,\n    and **destroy**) from an arbitrary number of pluggable backends. In\n    Puppet parlance, these backends are called terminuses.\n\n    Almost all indirected subsystems have a **rest** terminus that\n    interacts with the puppet master\\'s data. Most of them have\n    additional terminuses for various local data models, which are in\n    turn used by the indirected subsystem on the puppet master whenever\n    it receives a remote request.\n\n    The terminus for an action is often determined by context, but\n    occasionally needs to be set explicitly. See the \\\"Notes\\\" section\n    of this face\\'s manpage for more details.\n\n## ACTIONS\n**clean** - Clean up signed certs, cached facts, node objects, and reports for a node stored by the puppetmaster\n\n:   **SYNOPSIS**\n\n    puppet node clean \\[\\--terminus \\_TERMINUS\\] *host1* \\[*host2*\n    \\...\\]\n\n    **DESCRIPTION**\n\n    Cleans up the following information a puppet master knows about a\n    node:\n\n    *Signed certificates* - (\\$vardir/ssl/ca/signed/node.domain.pem)\n\n    *Cached facts* - (\\$vardir/yaml/facts/node.domain.yaml)\n\n    *Cached node objects* - (\\$vardir/yaml/node/node.domain.yaml)\n\n    *Reports* - (\\$vardir/reports/node.domain)\n\n    NOTE: this action now cleans up certs via Puppet Server\\'s CA API. A\n    running server is required for certs to be cleaned.\n\n**find** - Retrieve a node object.\n\n:   **SYNOPSIS**\n\n    puppet node find \\[\\--terminus \\_TERMINUS\\] *host*\n\n    **DESCRIPTION**\n\n    Retrieve a node object.\n\n    **RETURNS**\n\n    A hash containing the node\\'s **classes**, **environment**,\n    **expiration**, **name**, **parameters** (its facts, combined with\n    any ENC-set parameters), and **time**. When used from the Ruby API:\n    a Puppet::Node object.\n\n    RENDERING ISSUES: Rendering as string and json are currently broken;\n    node objects can only be rendered as yaml.\n\n**info** - Print the default terminus class for this face.\n\n:   **SYNOPSIS**\n\n    puppet node info \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Prints the default terminus class for this subcommand. Note that\n    different run modes may have different default termini; when in\n    doubt, specify the run mode with the \\'\\--run_mode\\' option.\n\n## EXAMPLES\n**find**\n\nRetrieve an \\\"empty\\\" (no classes, no ENC-imposed parameters, and an\nenvironment of \\\"production\\\") node:\n\n\\$ puppet node find somenode.puppetlabs.lan \\--terminus plain\n\\--render-as yaml\n\nRetrieve a node using the Puppet Server\\'s configured ENC:\n\n\\$ puppet node find somenode.puppetlabs.lan \\--terminus exec \\--run_mode\nserver \\--render-as yaml\n\nRetrieve the same node from the Puppet Server:\n\n\\$ puppet node find somenode.puppetlabs.lan \\--terminus rest\n\\--render-as yaml\n\n## NOTES\nThis subcommand is an indirector face, which exposes **find**,\n**search**, **save**, and **destroy** actions for an indirected\nsubsystem of Puppet. Valid termini for this face include:\n\n-   **exec**\n\n-   **json**\n\n-   **memory**\n\n-   **msgpack**\n\n-   **plain**\n\n-   **rest**\n\n-   **store_configs**\n\n-   **yaml**\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/overview.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Puppet Man Pages\ncanonical: \"/puppet/latest/man/overview.html\"\n---\n\n# Puppet Man Pages\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n\n\nPuppet's command line tools consist of a single `puppet` binary with many subcommands. The following subcommands are available in this version of Puppet:\n\nCore Tools\n-----\n\nThese subcommands form the core of Puppet's tool set, and every user should understand what they do.\n\n- [puppet agent](agent.md)\n- [puppet apply](apply.md)\n- [puppet lookup](lookup.md)\n- [puppet module](module.md)\n- [puppet resource](resource.md)\n\n\n> Note: The `puppet cert` command is available only in Puppet versions prior to 6.0. For 6.0 and later, use the [`puppetserver cert`command](https://puppet.com/docs/puppet/6/puppet_server_ca_cli.html).\n\nSecondary subcommands\n-----\n\nMany or most users need to use these subcommands at some point, but they aren't needed for daily use the way the core tools are.\n\n- [puppet config](config.md)\n- [puppet describe](describe.md)\n- [puppet device](device.md)\n- [puppet doc](doc.md)\n- [puppet epp](epp.md)\n- [puppet generate](generate.md)\n- [puppet help](help.md)\n- [puppet node](node.md)\n- [puppet parser](parser.md)\n- [puppet plugin](plugin.md)\n- [puppet script](script.md)\n- [puppet ssl](ssl.md)\n\n\nNiche subcommands\n-----\n\nMost users can ignore these subcommands. They're only useful for certain niche workflows, and most of them are interfaces to Puppet's internal subsystems.\n\n- [puppet catalog](catalog.md)\n- [puppet facts](facts.md)\n- [puppet filebucket](filebucket.md)\n- [puppet report](report.md)\n\n\n"
  },
  {
    "path": "references/man/parser.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet parser'\ncanonical: \"/puppet/latest/man/parser.html\"\n---\n\n# Man Page: puppet parser\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-parser** - Interact directly with the parser.\n\n## SYNOPSIS\npuppet parser *action*\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n## ACTIONS\n-   **dump** - Outputs a dump of the internal parse tree for debugging:\n    **SYNOPSIS**\n\n    puppet parser dump \\[\\--e *source*\\] \\[\\--\\[no-\\]validate\\]\n    \\[\\--format *old, pn, or json*\\] \\[\\--pretty\\] \\[\\--format\n    *old\\|pn\\|json*\\] \\[\\--pretty\\] { -e *source* \\| \\[*templates*\n    \\...\\] }\n\n    **DESCRIPTION**\n\n    This action parses and validates the Puppet DSL syntax without\n    compiling a catalog or syncing any resources.\n\n    The output format can be controlled using the \\--format\n    *old\\|pn\\|json* where:\n\n-   \\'old\\' is the default, but now deprecated format which is not API.\n\n-   \\'pn\\' is the Puppet Extended S-Expression Notation.\n\n-   \\'json\\' outputs the same graph as \\'pn\\' but with JSON syntax.\n\n    The output will be \\\"pretty printed\\\" when the option \\--pretty is\n    given together with \\--format \\'pn\\' or \\'json\\'. This option has no\n    effect on the \\'old\\' format.\n\n    The command accepts one or more manifests (.pp) files, or an -e\n    followed by the puppet source text. If no arguments are given, the\n    stdin is read (unless it is attached to a terminal)\n\n    The output format of the dumped tree is intended for debugging\n    purposes and is not API, it may change from time to time.\n\n    **OPTIONS** *\\--e \\<source*\\> - dump one source expression given on\n    the command line.\n\n    *\\--format \\<old, pn, or json*\\> - Get result in \\'old\\' (deprecated\n    format), \\'pn\\' (new format), or \\'json\\' (new format in JSON).\n\n    *\\--pretty* - Pretty print output. Only applicable together with\n    \\--format pn or json\n\n    *\\--\\[no-\\]validate* - Whether or not to validate the parsed result,\n    if no-validate only syntax errors are reported\n\n    **RETURNS**\n\n    A dump of the resulting AST model unless there are syntax or\n    validation errors.\n\n-   **validate** - Validate the syntax of one or more Puppet manifests.:\n    **SYNOPSIS**\n\n    puppet parser validate \\[*manifest*\\] \\[*manifest* \\...\\]\n\n    **DESCRIPTION**\n\n    This action validates Puppet DSL syntax without compiling a catalog\n    or syncing any resources. If no manifest files are provided, it will\n    validate the default site manifest.\n\n    When validating multiple issues per file are reported up to the\n    settings of max_error, and max_warnings. The processing stops after\n    having reported issues for the first encountered file with errors.\n\n    **RETURNS**\n\n    Nothing, or the first syntax error encountered.\n\n## EXAMPLES\n**validate**\n\nValidate the default site manifest at\n/etc/puppetlabs/puppet/manifests/site.pp:\n\n\\$ puppet parser validate\n\nValidate two arbitrary manifest files:\n\n\\$ puppet parser validate init.pp vhost.pp\n\nValidate from STDIN:\n\n\\$ cat init.pp \\| puppet parser validate\n\n## COPYRIGHT AND LICENSE\nCopyright 2014 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/plugin.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet plugin'\ncanonical: \"/puppet/latest/man/plugin.html\"\n---\n\n# Man Page: puppet plugin\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-plugin** - Interact with the Puppet plugin system.\n\n## SYNOPSIS\npuppet plugin *action*\n\n## DESCRIPTION\nThis subcommand provides network access to the puppet master\\'s store of\nplugins.\n\nThe puppet master serves Ruby code collected from the **lib**\ndirectories of its modules. These plugins can be used on agent nodes to\nextend Facter and implement custom types and providers. Plugins are\nnormally downloaded by puppet agent during the course of a run.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n## ACTIONS\n**download** - Download plugins from the puppet master.\n\n:   **SYNOPSIS**\n\n    puppet plugin download\n\n    **DESCRIPTION**\n\n    Downloads plugins from the configured puppet master. Any plugins\n    downloaded in this way will be used in all subsequent Puppet\n    activity. This action modifies files on disk.\n\n    **RETURNS**\n\n    A list of the files downloaded, or a confirmation that no files were\n    downloaded. When used from the Ruby API, this action returns an\n    array of the files downloaded, which will be empty if none were\n    retrieved.\n\n## EXAMPLES\n**download**\n\nRetrieve plugins from the puppet master:\n\n\\$ puppet plugin download\n\nRetrieve plugins from the puppet master (API example):\n\n\\$ Puppet::Face\\[:plugin, \\'0.0.1\\'\\].download\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/report.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet report'\ncanonical: \"/puppet/latest/man/report.html\"\n---\n\n# Man Page: puppet report\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-report** - Create, display, and submit reports.\n\n## SYNOPSIS\npuppet report *action* \\[\\--terminus \\_TERMINUS\\]\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument, although it may or may not be relevant to the\npresent action. For example, **server** and **run_mode** are valid\nsettings, so you can specify **\\--server \\<servername\\>**, or\n**\\--run_mode \\<runmode\\>** as an argument.\n\nSee the configuration file documentation at\n*https://puppet.com/docs/puppet/latest/configuration.html* for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with **\\--genconfig**.\n\n\\--render-as FORMAT\n\n:   The format in which to render output. The most common formats are\n    **json**, **s** (string), **yaml**, and **console**, but other\n    options such as **dot** are sometimes available.\n\n\\--verbose\n\n:   Whether to log verbosely.\n\n\\--debug\n\n:   Whether to log debug information.\n\n\\--terminus \\_TERMINUS\n\n:   Indirector faces expose indirected subsystems of Puppet. These\n    subsystems are each able to retrieve and alter a specific type of\n    data (with the familiar actions of **find**, **search**, **save**,\n    and **destroy**) from an arbitrary number of pluggable backends. In\n    Puppet parlance, these backends are called terminuses.\n\n    Almost all indirected subsystems have a **rest** terminus that\n    interacts with the puppet master\\'s data. Most of them have\n    additional terminuses for various local data models, which are in\n    turn used by the indirected subsystem on the puppet master whenever\n    it receives a remote request.\n\n    The terminus for an action is often determined by context, but\n    occasionally needs to be set explicitly. See the \\\"Notes\\\" section\n    of this face\\'s manpage for more details.\n\n## ACTIONS\n**info** - Print the default terminus class for this face.\n\n:   **SYNOPSIS**\n\n    puppet report info \\[\\--terminus \\_TERMINUS\\]\n\n    **DESCRIPTION**\n\n    Prints the default terminus class for this subcommand. Note that\n    different run modes may have different default termini; when in\n    doubt, specify the run mode with the \\'\\--run_mode\\' option.\n\n**save** - API only: submit a report.\n\n:   **SYNOPSIS**\n\n    puppet report save \\[\\--terminus \\_TERMINUS\\] *report*\n\n    **DESCRIPTION**\n\n    API only: create or overwrite an object. As the Faces framework does\n    not currently accept data from STDIN, save actions cannot currently\n    be invoked from the command line.\n\n    **RETURNS**\n\n    Nothing.\n\n**submit** - API only: submit a report with error handling.\n\n:   **SYNOPSIS**\n\n    puppet report submit \\[\\--terminus \\_TERMINUS\\] *report*\n\n    **DESCRIPTION**\n\n    API only: Submits a report to the puppet master. This action is\n    essentially a shortcut and wrapper for the **save** action with the\n    **rest** terminus, and provides additional details in the event of a\n    failure.\n\n## EXAMPLES\n**save**\n\nFrom the implementation of **puppet report submit** (API example):\n\nbegin Puppet::Transaction::Report.indirection.terminus_class = :rest\nPuppet::Face\\[:report, \\\"0.0.1\\\"\\].save(report) Puppet.notice \\\"Uploaded\nreport for ##{report.name}\\\" rescue =\\> detailPuppet.log_exception(detail, \\\"Could not send report: ##{detail}\\\") end\n**submit**\n\nAPI example:report = Puppet::Face\\[:catalog, \\'0.0.1\\'\\].apply\nPuppet::Face\\[:report, \\'0.0.1\\'\\].submit(report) return report\n\n## NOTES\nThis subcommand is an indirector face, which exposes **find**,\n**search**, **save**, and **destroy** actions for an indirected\nsubsystem of Puppet. Valid termini for this face include:\n\n-   **json**\n\n-   **msgpack**\n\n-   **processor**\n\n-   **rest**\n\n-   **yaml**\n\n## COPYRIGHT AND LICENSE\nCopyright 2011 by Puppet Inc. Apache 2 license; see COPYING\n"
  },
  {
    "path": "references/man/resource.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet resource'\ncanonical: \"/puppet/latest/man/resource.html\"\n---\n\n# Man Page: puppet resource\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-resource** - The resource abstraction layer shell\n\n## SYNOPSIS\nUses the Puppet RAL to directly interact with the system.\n\n## USAGE\npuppet resource \\[-h\\|\\--help\\] \\[-d\\|\\--debug\\] \\[-v\\|\\--verbose\\]\n\\[-e\\|\\--edit\\] \\[-p\\|\\--param *parameter*\\] \\[-t\\|\\--types\\]\n\\[-y\\|\\--to_yaml\\] *type* \\[*name*\\] \\[*attribute*=*value* \\...\\]\n\n## DESCRIPTION\nThis command provides simple facilities for converting current system\nstate into Puppet code, along with some ability to modify the current\nstate using Puppet\\'s RAL.\n\nBy default, you must at least provide a type to list, in which case\npuppet resource will tell you everything it knows about all resources of\nthat type. You can optionally specify an instance name, and puppet\nresource will only describe that single instance.\n\nIf given a type, a name, and a series of *attribute*=*value* pairs,\npuppet resource will modify the state of the specified resource.\nAlternately, if given a type, a name, and the \\'\\--edit\\' flag, puppet\nresource will write its output to a file, open that file in an editor,\nand then apply the saved file as a Puppet transaction.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument. For example, \\'ssldir\\' is a valid setting, so you\ncan specify \\'\\--ssldir *directory*\\' as an argument.\n\nSee the configuration file documentation at\nhttps://puppet.com/docs/puppet/latest/configuration.html for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with \\'\\--genconfig\\'.\n\n\\--debug\n\n:   Enable full debugging.\n\n\\--edit\n\n:   Write the results of the query to a file, open the file in an\n    editor, and read the file back in as an executable Puppet manifest.\n\n\\--help\n\n:   Print this help message.\n\n\\--param\n\n:   Add more parameters to be outputted from queries.\n\n\\--types\n\n:   List all available types.\n\n\\--verbose\n\n:   Print extra information.\n\n\\--to_yaml\n\n:   Output found resources in yaml format, suitable to use with Hiera\n    and create_resources.\n\n\\--fail\n\n:   Fails and returns an exit code of 1 if the resource could not be\n    modified.\n\n## EXAMPLE\nThis example uses **puppet resource** to return a Puppet configuration\nfor the user **luke**:\n\n\n\n    $ puppet resource user luke\n    user { 'luke':\n     home => '/home/luke',\n     uid => '100',\n     ensure => 'present',\n     comment => 'Luke Kanies,,,',\n     gid => '1000',\n     shell => '/bin/bash',\n     groups => ['sysadmin','audio','video','puppet']\n    }\n\n## AUTHOR\nLuke Kanies\n\n## COPYRIGHT\nCopyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/script.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet script'\ncanonical: \"/puppet/latest/man/script.html\"\n---\n\n# Man Page: puppet script\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-script** - Run a puppet manifests as a script without compiling\na catalog\n\n## SYNOPSIS\nRuns a puppet language script without compiling a catalog.\n\n## USAGE\npuppet script \\[-h\\|\\--help\\] \\[-V\\|\\--version\\] \\[-d\\|\\--debug\\]\n\\[-v\\|\\--verbose\\] \\[-e\\|\\--execute\\] \\[-l\\|\\--logdest\nsyslog\\|eventlog\\|*FILE*\\|console\\] \\[\\--noop\\] *file*\n\n## DESCRIPTION\nThis is a standalone puppet script runner tool; use it to run puppet\ncode without compiling a catalog.\n\nWhen provided with a modulepath, via command line or config file, puppet\nscript can load functions, types, tasks and plans from modules.\n\n## OPTIONS\nNote that any setting that\\'s valid in the configuration file is also a\nvalid long argument. For example, \\'environment\\' is a valid setting, so\nyou can specify \\'\\--environment mytest\\' as an argument.\n\nSee the configuration file documentation at\nhttps://puppet.com/docs/puppet/latest/configuration.html for the full\nlist of acceptable parameters. A commented list of all configuration\noptions can also be generated by running puppet with \\'\\--genconfig\\'.\n\n\\--debug\n\n:   Enable full debugging.\n\n\\--help\n\n:   Print this help message\n\n\\--logdest\n\n:   Where to send log messages. Choose between \\'syslog\\' (the POSIX\n    syslog service), \\'eventlog\\' (the Windows Event Log), \\'console\\',\n    or the path to a log file. Defaults to \\'console\\'. Multiple\n    destinations can be set using a comma separated list (eg:\n    **/path/file1,console,/path/file2**)\\\"\n\n    A path ending with \\'.json\\' will receive structured output in JSON\n    format. The log file will not have an ending \\'\\]\\' automatically\n    written to it due to the appending nature of logging. It must be\n    appended manually to make the content valid JSON.\n\n    A path ending with \\'.jsonl\\' will receive structured output in JSON\n    Lines format.\n\n\\--noop\n\n:   Use \\'noop\\' mode where Puppet runs in a no-op or dry-run mode. This\n    is useful for seeing what changes Puppet will make without actually\n    executing the changes. Applies to tasks only.\n\n\\--execute\n\n:   Execute a specific piece of Puppet code\n\n\\--verbose\n\n:   Print extra information.\n\n## EXAMPLE\n\n    $ puppet script -l /tmp/manifest.log manifest.pp\n    $ puppet script --modulepath=/root/dev/modules -e 'notice(\"hello world\")'\n\n## AUTHOR\nHenrik Lindberg\n\n## COPYRIGHT\nCopyright (c) 2017 Puppet Inc., LLC Licensed under the Apache 2.0\nLicense\n"
  },
  {
    "path": "references/man/ssl.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Man Page: puppet ssl'\ncanonical: \"/puppet/latest/man/ssl.html\"\n---\n\n# Man Page: puppet ssl\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n## NAME\n**puppet-ssl** - Manage SSL keys and certificates for puppet SSL clients\n\n## SYNOPSIS\nManage SSL keys and certificates for SSL clients needing to communicate\nwith a puppet infrastructure.\n\n## USAGE\npuppet ssl *action* \\[-h\\|\\--help\\] \\[-v\\|\\--verbose\\] \\[-d\\|\\--debug\\]\n\\[\\--localca\\] \\[\\--target CERTNAME\\]\n\n## OPTIONS\n-   \\--help: Print this help message.\n\n-   \\--verbose: Print extra information.\n\n-   \\--debug: Enable full debugging.\n\n-   \\--localca Also clean the local CA certificate and CRL.\n\n-   \\--target CERTNAME Clean the specified device certificate instead of\n    this host\\'s certificate.\n\n## ACTIONS\nbootstrap\n\n:   Perform all of the steps necessary to request and download a client\n    certificate. If autosigning is disabled, then puppet will wait every\n    **waitforcert** seconds for its certificate to be signed. To only\n    attempt once and never wait, specify a time of 0. Since\n    **waitforcert** is a Puppet setting, it can be specified as a time\n    interval, such as 30s, 5m, 1h.\n\nsubmit_request\n\n:   Generate a certificate signing request (CSR) and submit it to the\n    CA. If a private and public key pair already exist, they will be\n    used to generate the CSR. Otherwise, a new key pair will be\n    generated. If a CSR has already been submitted with the given\n    **certname**, then the operation will fail.\n\ngenerate_request\n\n:   Generate a certificate signing request (CSR). If a private and\n    public key pair exist, they will be used to generate the CSR.\n    Otherwise a new key pair will be generated.\n\ndownload_cert\n\n:   Download a certificate for this host. If the current private key\n    matches the downloaded certificate, then the certificate will be\n    saved and used for subsequent requests. If there is already an\n    existing certificate, it will be overwritten.\n\nverify\n\n:   Verify the private key and certificate are present and match, verify\n    the certificate is issued by a trusted CA, and check revocation\n    status.\n\nclean\n\n:   Remove the private key and certificate related files for this host.\n    If **\\--localca** is specified, then also remove this host\\'s local\n    copy of the CA certificate(s) and CRL bundle. if **\\--target\n    CERTNAME** is specified, then remove the files for the specified\n    device on this host instead of this host.\n\nshow\n\n:   Print the full-text version of this host\\'s certificate.\n"
  },
  {
    "path": "references/metaparameter.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Metaparameter Reference\ntoc: columns\ncanonical: \"/puppet/latest/metaparameter.html\"\n---\n\n# Metaparameter Reference\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:39 +0000\n\n\n\n\n\nMetaparameters are attributes that work with any resource type, including custom\ntypes and defined types.\n\nIn general, they affect _Puppet's_ behavior rather than the desired state of the\nresource. Metaparameters do things like add metadata to a resource (`alias`,\n`tag`), set limits on when the resource should be synced (`require`, `schedule`,\netc.), prevent Puppet from making changes (`noop`), and change logging verbosity\n(`loglevel`).\n\n## Available Metaparameters\n\n  ### alias\n\nCreates an alias for the resource.  Puppet uses this internally when you\nprovide a symbolic title and an explicit namevar value:\n\n    file { 'sshdconfig':\n      path => $os['name'] ? {\n        solaris => '/usr/local/etc/ssh/sshd_config',\n        default => '/etc/ssh/sshd_config',\n      },\n      source => '...'\n    }\n\n    service { 'sshd':\n      subscribe => File['sshdconfig'],\n    }\n\nWhen you use this feature, the parser sets `sshdconfig` as the title,\nand the library sets that as an alias for the file so the dependency\nlookup in `Service['sshd']` works.  You can use this metaparameter yourself,\nbut note that aliases generally only work for creating relationships; anything\nelse that refers to an existing resource (such as amending or overriding\nresource attributes in an inherited class) must use the resource's exact\ntitle. For example, the following code will not work:\n\n    file { '/etc/ssh/sshd_config':\n      owner => root,\n      group => root,\n      alias => 'sshdconfig',\n    }\n\n    File['sshdconfig'] {\n      mode => '0644',\n    }\n\nThere's no way here for the Puppet parser to know that these two stanzas\nshould be affecting the same file.\n\n### audit\n\nMarks a subset of this resource's unmanaged attributes for auditing. Accepts an\nattribute name, an array of attribute names, or `all`.\n\nAuditing a resource attribute has two effects: First, whenever a catalog\nis applied with puppet apply or puppet agent, Puppet will check whether\nthat attribute of the resource has been modified, comparing its current\nvalue to the previous run; any change will be logged alongside any actions\nperformed by Puppet while applying the catalog.\n\nSecondly, marking a resource attribute for auditing will include that\nattribute in inspection reports generated by puppet inspect; see the\npuppet inspect documentation for more details.\n\nManaged attributes for a resource can also be audited, but note that\nchanges made by Puppet will be logged as additional modifications. (I.e.\nif a user manually edits a file whose contents are audited and managed,\npuppet agent's next two runs will both log an audit notice: the first run\nwill log the user's edit and then revert the file to the desired state,\nand the second run will log the edit made by Puppet.)\n\n### before\n\nOne or more resources that depend on this resource, expressed as\n[resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\nMultiple resources can be specified as an array of references. When this\nattribute is present:\n\n* This resource will be applied _before_ the dependent resources.\n\nThis is one of the four relationship metaparameters, along with\n`require`, `notify`, and `subscribe`. For more context, including the\nalternate chaining arrow (`->` and `~>`) syntax, see\n[the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\n\n### loglevel\n\nSets the level that information will be logged.\nThe log levels have the biggest impact when logs are sent to\nsyslog (which is currently the default).\n\nThe order of the log levels, in decreasing priority, is:\n\n* `emerg`\n* `alert`\n* `crit`\n* `err`\n* `warning`\n* `notice`\n* `info` / `verbose`\n* `debug`\n\nValid values are `debug`, `info`, `notice`, `warning`, `err`, `alert`, `emerg`, `crit`, `verbose`.\n\n### noop\n\nWhether to apply this resource in noop mode.\n\nWhen applying a resource in noop mode, Puppet will check whether it is in sync,\nlike it does when running normally. However, if a resource attribute is not in\nthe desired state (as declared in the catalog), Puppet will take no\naction, and will instead report the changes it _would_ have made. These\nsimulated changes will appear in the report sent to the primary Puppet server, or\nbe shown on the console if running puppet agent or puppet apply in the\nforeground. The simulated changes will not send refresh events to any\nsubscribing or notified resources, although Puppet will log that a refresh\nevent _would_ have been sent.\n\n**Important note:**\n[The `noop` setting](https://puppet.com/docs/puppet/latest/configuration.html#noop)\nallows you to globally enable or disable noop mode, but it will _not_ override\nthe `noop` metaparameter on individual resources. That is, the value of the\nglobal `noop` setting will _only_ affect resources that do not have an explicit\nvalue set for their `noop` attribute.\n\nValid values are `true`, `false`.\n\n### notify\n\nOne or more resources that depend on this resource, expressed as\n[resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\nMultiple resources can be specified as an array of references. When this\nattribute is present:\n\n* This resource will be applied _before_ the notified resources.\n* If Puppet makes changes to this resource, it will cause all of the\n  notified resources to _refresh._ (Refresh behavior varies by resource\n  type: services will restart, mounts will unmount and re-mount, etc. Not\n  all types can refresh.)\n\nThis is one of the four relationship metaparameters, along with\n`before`, `require`, and `subscribe`. For more context, including the\nalternate chaining arrow (`->` and `~>`) syntax, see\n[the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\n\n### require\n\nOne or more resources that this resource depends on, expressed as\n[resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\nMultiple resources can be specified as an array of references. When this\nattribute is present:\n\n* The required resources will be applied **before** this resource.\n\nThis is one of the four relationship metaparameters, along with\n`before`, `notify`, and `subscribe`. For more context, including the\nalternate chaining arrow (`->` and `~>`) syntax, see\n[the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\n\n### schedule\n\nA schedule to govern when Puppet is allowed to manage this resource.\nThe value of this metaparameter must be the `name` of a `schedule`\nresource. This means you must declare a schedule resource, then\nrefer to it by name; see\n[the docs for the `schedule` type](https://puppet.com/docs/puppet/latest/type.html#schedule)\nfor more info.\n\n    schedule { 'everyday':\n      period => daily,\n      range  => \"2-4\"\n    }\n\n    exec { \"/usr/bin/apt-get update\":\n      schedule => 'everyday'\n    }\n\nNote that you can declare the schedule resource anywhere in your\nmanifests, as long as it ends up in the final compiled catalog.\n\n### stage\n\nWhich run stage this class should reside in.\n\n**Note: This metaparameter can only be used on classes,** and only when\ndeclaring them with the resource-like syntax. It cannot be used on normal\nresources or on classes declared with `include`.\n\nBy default, all classes are declared in the `main` stage. To assign a class\nto a different stage, you must:\n\n* Declare the new stage as a [`stage` resource](https://puppet.com/docs/puppet/latest/type.html#stage).\n* Declare an order relationship between the new stage and the `main` stage.\n* Use the resource-like syntax to declare the class, and set the `stage`\n  metaparameter to the name of the desired stage.\n\nFor example:\n\n    stage { 'pre':\n      before => Stage['main'],\n    }\n\n    class { 'apt-updates':\n      stage => 'pre',\n    }\n\n### subscribe\n\nOne or more resources that this resource depends on, expressed as\n[resource references](https://puppet.com/docs/puppet/latest/lang_data_resource_reference.html).\nMultiple resources can be specified as an array of references. When this\nattribute is present:\n\n* The subscribed resources will be applied _before_ this resource.\n* If Puppet makes changes to any of the subscribed resources, it will cause\n  this resource to _refresh._ (Refresh behavior varies by resource\n  type: services will restart, mounts will unmount and re-mount, etc. Not\n  all types can refresh.)\n\nThis is one of the four relationship metaparameters, along with\n`before`, `require`, and `notify`. For more context, including the\nalternate chaining arrow (`->` and `~>`) syntax, see\n[the language page on relationships](https://puppet.com/docs/puppet/latest/lang_relationships.html).\n\n### tag\n\nAdd the specified tags to the associated resource.  While all resources\nare automatically tagged with as much information as possible\n(e.g., each class and definition containing the resource), it can\nbe useful to add your own tags to a given resource.\n\nMultiple tags can be specified as an array:\n\n    file {'/etc/hosts':\n      ensure => file,\n      source => 'puppet:///modules/site/hosts',\n      mode   => '0644',\n      tag    => ['bootstrap', 'minimumrun', 'mediumrun'],\n    }\n\nTags are useful for things like applying a subset of a host's configuration\nwith [the `tags` setting](https://puppet.com/docs/puppet/latest/configuration.html#tags)\n(e.g. `puppet agent --test --tags bootstrap`).\n\n"
  },
  {
    "path": "references/report.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Report Reference\ntoc: columns\ncanonical: \"/puppet/latest/report.html\"\n---\n\n# Report Reference\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:37:38 +0000\n\n\n\n\nPuppet can generate a report after applying a catalog. This report includes\nevents, log messages, resource statuses, and metrics and metadata about the run.\nPuppet agent sends its report to a Puppet master server, and Puppet apply\nprocesses its own reports.\n\nPuppet master and Puppet apply will handle every report with a set of report\nprocessors, configurable with the `reports` setting in puppet.conf. This page\ndocuments the built-in report processors.\n\nSee [About Reporting](https://puppet.com/docs/puppet/latest/reporting_about.html)\nfor more details.\n\nhttp\n----\nSend reports via HTTP or HTTPS. This report processor submits reports as\nPOST requests to the address in the `reporturl` setting. When a HTTPS URL\nis used, the remote server must present a certificate issued by the Puppet\nCA or the connection will fail validation. The body of each POST request\nis the YAML dump of a Puppet::Transaction::Report object, and the\nContent-Type is set as `application/x-yaml`.\n\nlog\n---\nSend all received logs to the local log destinations.  Usually\nthe log destination is syslog.\n\nstore\n-----\nStore the yaml report on disk.  Each host sends its report as a YAML dump\nand this just stores the file on disk, in the `reportdir` directory.\n\nThese files collect quickly -- one every half hour -- so it is a good idea\nto perform some maintenance on them if you use this report (it's the only\ndefault report).\n\n"
  },
  {
    "path": "references/type.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Resource Type Reference (Single-Page)\ncanonical: \"/puppet/latest/type.html\"\ntoc_levels: 2\ntoc: columns\n---\n\n# Resource Type Reference (Single-Page)\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## About resource types\n\n### Built-in types and custom types\n\nThis is the documentation for Puppet's built-in resource types and providers. Additional resource types are distributed in Puppet modules.\n\nYou can find and install modules by browsing the\n[Puppet Forge](http://forge.puppet.com). See each module's documentation for\ninformation on how to use its custom resource types. For more information about creating custom types, see [Custom resources](/docs/puppet/latest/custom_resources.html).\n\n> As of Puppet 6.0, some resource types were removed from Puppet and repackaged as individual modules. These supported type modules are still included in the `puppet-agent` package, so you don't have to download them from the Forge. See the complete list of affected types in the [supported type modules](#supported-type-modules-in-puppet-agent) section.\n\n### Declaring resources\n\nTo manage resources on a target system, declare them in Puppet\nmanifests. For more details, see\n[the resources page of the Puppet language reference.](/docs/puppet/latest/lang_resources.html)\n\nYou can also browse and manage resources interactively using the\n`puppet resource` subcommand; run `puppet resource --help` for more information.\n\n### Namevars and titles\n\nAll types have a special attribute called the _namevar_. This is the attribute\nused to uniquely identify a resource on the target system.\n\nEach resource has a specific namevar attribute, which is listed on this page in\neach resource's reference. If you don't specify a value for the namevar, its\nvalue defaults to the resource's _title_.\n\n**Example of a title as a default namevar:**\n\n```puppet\nfile { '/etc/passwd':\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nIn this code, `/etc/passwd` is the _title_ of the file resource.\n\nThe file type's namevar is `path`. Because we didn't provide a `path` value in\nthis example, the value defaults to the title, `/etc/passwd`.\n\n**Example of a namevar:**\n\n```puppet\nfile { 'passwords':\n  path  => '/etc/passwd',\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nThis example is functionally similar to the previous example. Its `path`\nnamevar attribute has an explicitly set value separate from the title, so\nits name is still `/etc/passwd`.\n\nOther Puppet code can refer to this resource as `File['/etc/passwd']` to\ndeclare relationships.\n\n### Attributes, parameters, properties\n\nThe _attributes_ (sometimes called _parameters_) of a resource determine its\ndesired state. They either directly modify the system (internally, these are\ncalled \"properties\") or they affect how the resource behaves (for instance,\nadding a search path for `exec` resources or controlling directory recursion\non `file` resources).\n\n### Providers\n\n_Providers_ implement the same resource type on different kinds of systems.\nThey usually do this by calling out to external commands.\n\nAlthough Puppet automatically selects an appropriate default provider, you\ncan override the default with the `provider` attribute. (For example, `package`\nresources on Red Hat systems default to the `yum` provider, but you can specify\n`provider => gem` to install Ruby libraries with the `gem` command.)\n\nProviders often specify binaries that they require. Fully qualified binary\npaths indicate that the binary must exist at that specific path, and\nunqualified paths indicate that Puppet searches for the binary using the\nshell path.\n\n### Features\n\n_Features_ are abilities that some providers might not support. Generally, a\nfeature corresponds to some allowed values for a resource attribute.\n\nThis is often the case with the `ensure` attribute. In most types, Puppet\ndoesn't create new resources when omitting `ensure` but still modifies existing\nresources to match specifications in the manifest. However, in some types this\nisn't always the case, or additional values provide more granular control. For\nexample, if a `package` provider supports the `purgeable` feature, you can\nspecify `ensure => purged` to delete configuration files installed by the\npackage.\n\nResource types define the set of features they can use, and providers can\ndeclare which features they provide.\n\n## Puppet 6.0 type changes\n\nIn Puppet 6.0, we removed some of Puppet's built-in types and moved them into individual modules.\n\n### Supported type modules in `puppet-agent`\n\nThe following types are included in supported modules on the Forge. However, they are also included in the `puppet-agent` package, so you do not have to install them separately. See each module's README for detailed information about that type.\n\n- [`augeas`](https://forge.puppet.com/puppetlabs/augeas_core)\n- [`cron`](https://forge.puppet.com/puppetlabs/cron_core)\n- [`host`](https://forge.puppet.com/puppetlabs/host_core)\n- [`mount`](https://forge.puppet.com/puppetlabs/mount_core)\n- [`scheduled_task`](https://forge.puppet.com/puppetlabs/scheduled_task)\n- [`selboolean`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`selmodule`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`ssh_authorized_key`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`sshkey`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`yumrepo`](https://forge.puppet.com/puppetlabs/yumrepo_core)\n- [`zfs`](https://forge.puppet.com/puppetlabs/zfs_core)\n- [`zone`](https://forge.puppet.com/puppetlabs/zone_core)\n- [`zpool`](https://forge.puppet.com/puppetlabs/zfs_core)\n\n### Type modules available on the Forge\n\nThe following types are contained in modules that are maintained, but are not repackaged into Puppet agent. If you need to use them, you must install the modules separately. \n\n- [`k5login`](https://forge.puppet.com/puppetlabs/k5login_core)\n- [`mailalias`](https://forge.puppet.com/puppetlabs/mailalias_core)\n- [`maillist`](https://forge.puppet.com/puppetlabs/maillist_core)\n\n### Deprecated types\n\nThe following types were deprecated with Puppet 6.0.0. They are available in modules, but are not updated. If you need to use them, you must install the modules separately.\n\n- [`computer`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`interface`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`macauthorization`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`mcx`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [The Nagios types](https://forge.puppet.com/puppetlabs/nagios_core)\n- [`router`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`vlan`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n\n## Puppet core types\n\nFor a list of core Puppet types, see the [core types cheat sheet][core-types-cheatsheet].\n\n## exec\n\n* [Attributes](#exec-attributes)\n* [Providers](#exec-providers)\n\n### Description {#exec-description}\n\nExecutes external commands.\n\nAny command in an `exec` resource **must** be able to run multiple times\nwithout causing harm --- that is, it must be *idempotent*. There are three\nmain ways for an exec to be idempotent:\n\n* The command itself is already idempotent. (For example, `apt-get update`.)\n* The exec has an `onlyif`, `unless`, or `creates` attribute, which prevents\n  Puppet from running the command unless some condition is met. The\n  `onlyif` and `unless` commands of an `exec` are used in the process of\n  determining whether the `exec` is already in sync, therefore they must be run\n  during a noop Puppet run.\n* The exec has `refreshonly => true`, which allows Puppet to run the\n  command only when some other resource is changed. (See the notes on refreshing\n  below.)\n\nThe state managed by an `exec` resource represents whether the specified command\n_needs to be_ executed during the catalog run. The target state is always that\nthe command does not need to be executed. If the initial state is that the\ncommand _does_ need to be executed, then successfully executing the command\ntransitions it to the target state.\n\nThe `unless`, `onlyif`, and `creates` properties check the initial state of the\nresource. If one or more of these properties is specified, the exec might not\nneed to run. If the exec does not need to run, then the system is already in\nthe target state. In such cases, the exec is considered successful without\nactually executing its command.\n\nA caution: There's a widespread tendency to use collections of execs to\nmanage resources that aren't covered by an existing resource type. This\nworks fine for simple tasks, but once your exec pile gets complex enough\nthat you really have to think to understand what's happening, you should\nconsider developing a custom resource type instead, as it is much\nmore predictable and maintainable.\n\n**Duplication:** Even though `command` is the namevar, Puppet allows\nmultiple `exec` resources with the same `command` value.\n\n**Refresh:** `exec` resources can respond to refresh events (via\n`notify`, `subscribe`, or the `~>` arrow). The refresh behavior of execs\nis non-standard, and can be affected by the `refresh` and\n`refreshonly` attributes:\n\n* If `refreshonly` is set to true, the exec runs _only_ when it receives an\n  event. This is the most reliable way to use refresh with execs.\n* If the exec has already run and then receives an event, it runs its\n  command **up to two times.** If an `onlyif`, `unless`, or `creates` condition\n  is no longer met after the first run, the second run does not occur.\n* If the exec has already run, has a `refresh` command, and receives an\n  event, it runs its normal command. Then, if any `onlyif`, `unless`, or `creates`\n  conditions are still met, the exec runs its `refresh` command.\n* If the exec has an `onlyif`, `unless`, or `creates` attribute that prevents it\n  from running, and it then receives an event, it still will not run.\n* If the exec has `noop => true`, would otherwise have run, and receives\n  an event from a non-noop resource, it runs once. However, if it has a `refresh`\n  command, it runs that instead of its normal command.\n\nIn short: If there's a possibility of your exec receiving refresh events,\nit is extremely important to make sure the run conditions are restricted.\n\n**Autorequires:** If Puppet is managing an exec's cwd or the executable\nfile used in an exec's command, the exec resource autorequires those\nfiles. If Puppet is managing the user that an exec should run as, the\nexec resource autorequires that user.\n\n### Attributes {#exec-attributes}\n\n<pre><code>exec { 'resource title':\n  <a href=\"#exec-attribute-command\">command</a>     =&gt; <em># <strong>(namevar)</strong> The actual command to execute.  Must either be...</em>\n  <a href=\"#exec-attribute-creates\">creates</a>     =&gt; <em># A file to look for before running the command...</em>\n  <a href=\"#exec-attribute-cwd\">cwd</a>         =&gt; <em># The directory from which to run the command.  If </em>\n  <a href=\"#exec-attribute-environment\">environment</a> =&gt; <em># An array of any additional environment variables </em>\n  <a href=\"#exec-attribute-group\">group</a>       =&gt; <em># The group to run the command as.  This seems to...</em>\n  <a href=\"#exec-attribute-logoutput\">logoutput</a>   =&gt; <em># Whether to log command output in addition to...</em>\n  <a href=\"#exec-attribute-onlyif\">onlyif</a>      =&gt; <em># A test command that checks the state of the...</em>\n  <a href=\"#exec-attribute-path\">path</a>        =&gt; <em># The search path used for command execution...</em>\n  <a href=\"#exec-attribute-provider\">provider</a>    =&gt; <em># The specific backend to use for this `exec...</em>\n  <a href=\"#exec-attribute-refresh\">refresh</a>     =&gt; <em># An alternate command to run when the `exec...</em>\n  <a href=\"#exec-attribute-refreshonly\">refreshonly</a> =&gt; <em># The command should only be run as a refresh...</em>\n  <a href=\"#exec-attribute-returns\">returns</a>     =&gt; <em># The expected exit code(s).  An error will be...</em>\n  <a href=\"#exec-attribute-timeout\">timeout</a>     =&gt; <em># The maximum time the command should take.  If...</em>\n  <a href=\"#exec-attribute-tries\">tries</a>       =&gt; <em># The number of times execution of the command...</em>\n  <a href=\"#exec-attribute-try_sleep\">try_sleep</a>   =&gt; <em># The time to sleep in seconds between...</em>\n  <a href=\"#exec-attribute-umask\">umask</a>       =&gt; <em># Sets the umask to be used while executing this...</em>\n  <a href=\"#exec-attribute-unless\">unless</a>      =&gt; <em># A test command that checks the state of the...</em>\n  <a href=\"#exec-attribute-user\">user</a>        =&gt; <em># The user to run the command as.  > **Note:*...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### command {#exec-attribute-command}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe actual command to execute.  Must either be fully qualified\nor a search path for the command must be provided.  If the command\nsucceeds, any output produced will be logged at the instance's\nnormal log level (usually `notice`), but if the command fails\n(meaning its return code does not match the specified code) then\nany output is logged at the `err` log level.\n\nMultiple `exec` resources can use the same `command` value; Puppet\nonly uses the resource title to ensure `exec`s are unique.\n\nOn *nix platforms, the command can be specified as an array of\nstrings and Puppet will invoke it using the more secure method of\nparameterized system calls. For example, rather than executing the\nmalicious injected code, this command will echo it out:\n\n    command => ['/bin/echo', 'hello world; rm -rf /']\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### creates {#exec-attribute-creates}\n\nA file to look for before running the command. The command will\nonly run if the file **doesn't exist.**\n\nThis parameter doesn't cause Puppet to create a file; it is only\nuseful if **the command itself** creates a file.\n\n    exec { 'tar -xf /Volumes/nfs02/important.tar':\n      cwd     => '/var/tmp',\n      creates => '/var/tmp/myfile',\n      path    => ['/usr/bin', '/usr/sbin',],\n    }\n\nIn this example, `myfile` is assumed to be a file inside\n`important.tar`. If it is ever deleted, the exec will bring it\nback by re-extracting the tarball. If `important.tar` does **not**\nactually contain `myfile`, the exec will keep running every time\nPuppet runs.\n\nThis parameter can also take an array of files, and the command will\nnot run if **any** of these files exist. Consider this example:\n\n    creates => ['/tmp/file1', '/tmp/file2'],\n\nThe command is only run if both files don't exist.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### cwd {#exec-attribute-cwd}\n\nThe directory from which to run the command.  If\nthis directory does not exist, the command will fail.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### environment {#exec-attribute-environment}\n\nAn array of any additional environment variables you want to set for a\ncommand, such as `[ 'HOME=/root', 'MAIL=root@example.com']`.\nNote that if you use this to set PATH, it will override the `path`\nattribute. Multiple environment variables should be specified as an\narray.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### group {#exec-attribute-group}\n\nThe group to run the command as.  This seems to work quite\nhaphazardly on different platforms -- it is a platform issue\nnot a Ruby or Puppet one, since the same variety exists when\nrunning commands as different users in the shell.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### logoutput {#exec-attribute-logoutput}\n\nWhether to log command output in addition to logging the\nexit code. Defaults to `on_failure`, which only logs the output\nwhen the command has an exit code that does not match any value\nspecified by the `returns` attribute. As with any resource type,\nthe log level can be controlled with the `loglevel` metaparameter.\n\nValid values are `true`, `false`, `on_failure`.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### onlyif {#exec-attribute-onlyif}\n\nA test command that checks the state of the target system and restricts\nwhen the `exec` can run. If present, Puppet runs this test command\nfirst, and only runs the main command if the test has an exit code of 0\n(success). For example:\n\n    exec { 'logrotate':\n      path     => '/usr/bin:/usr/sbin:/bin',\n      provider => shell,\n      onlyif   => 'test `du /var/log/messages | cut -f1` -gt 100000',\n    }\n\nThis would run `logrotate` only if that test returns true.\n\nNote that this test command runs with the same `provider`, `path`,\n`user`, `cwd`, and `group` as the main command. If the `path` isn't set, you\nmust fully qualify the command's name.\n\nSince this command is used in the process of determining whether the\n`exec` is already in sync, it must be run during a noop Puppet run.\n\nThis parameter can also take an array of commands. For example:\n\n    onlyif => ['test -f /tmp/file1', 'test -f /tmp/file2'],\n\nor an array of arrays. For example:\n\n    onlyif => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']\n\nThis `exec` would only run if every command in the array has an\nexit code of 0 (success).\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### path {#exec-attribute-path}\n\nThe search path used for command execution.\nCommands must be fully qualified if no path is specified.  Paths\ncan be specified as an array or as a ':' separated list.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### provider {#exec-attribute-provider}\n\nThe specific backend to use for this `exec`\nresource. You will seldom need to specify this --- Puppet will usually\ndiscover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`posix`](#exec-provider-posix)\n* [`shell`](#exec-provider-shell)\n* [`windows`](#exec-provider-windows)\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### refresh {#exec-attribute-refresh}\n\nAn alternate command to run when the `exec` receives a refresh event\nfrom another resource. By default, Puppet runs the main command again.\nFor more details, see the notes about refresh behavior above, in the\ndescription for this resource type.\n\nNote that this alternate command runs with the same `provider`, `path`,\n`user`, and `group` as the main command. If the `path` isn't set, you\nmust fully qualify the command's name.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### refreshonly {#exec-attribute-refreshonly}\n\nThe command should only be run as a\nrefresh mechanism for when a dependent object is changed.  It only\nmakes sense to use this option when this command depends on some\nother object; it is useful for triggering an action:\n\n    # Pull down the main aliases file\n    file { '/etc/aliases':\n      source => 'puppet://server/module/aliases',\n    }\n\n    # Rebuild the database, but only when the file changes\n    exec { newaliases:\n      path        => ['/usr/bin', '/usr/sbin'],\n      subscribe   => File['/etc/aliases'],\n      refreshonly => true,\n    }\n\nNote that only `subscribe` and `notify` can trigger actions, not `require`,\nso it only makes sense to use `refreshonly` with `subscribe` or `notify`.\n\nValid values are `true`, `false`.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### returns {#exec-attribute-returns}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe expected exit code(s).  An error will be returned if the\nexecuted command has some other exit code. Can be specified as an array\nof acceptable exit codes or a single value.\n\nOn POSIX systems, exit codes are always integers between 0 and 255.\n\nOn Windows, **most** exit codes should be integers between 0\nand 2147483647.\n\nLarger exit codes on Windows can behave inconsistently across different\ntools. The Win32 APIs define exit codes as 32-bit unsigned integers, but\nboth the cmd.exe shell and the .NET runtime cast them to signed\nintegers. This means some tools will report negative numbers for exit\ncodes above 2147483647. (For example, cmd.exe reports 4294967295 as -1.)\nSince Puppet uses the plain Win32 APIs, it will report the very large\nnumber instead of the negative number, which might not be what you\nexpect if you got the exit code from a cmd.exe session.\n\nMicrosoft recommends against using negative/very large exit codes, and\nyou should avoid them when possible. To convert a negative exit code to\nthe positive one Puppet will use, add it to 4294967296.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### timeout {#exec-attribute-timeout}\n\nThe maximum time the command should take.  If the command takes\nlonger than the timeout, the command is considered to have failed\nand will be stopped. The timeout is specified in seconds. The default\ntimeout is 300 seconds and you can set it to 0 to disable the timeout.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### tries {#exec-attribute-tries}\n\nThe number of times execution of the command should be tried.\nThis many attempts will be made to execute the command until an\nacceptable return code is returned. Note that the timeout parameter\napplies to each try rather than to the complete set of tries.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### try_sleep {#exec-attribute-try_sleep}\n\nThe time to sleep in seconds between 'tries'.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### umask {#exec-attribute-umask}\n\nSets the umask to be used while executing this command\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### unless {#exec-attribute-unless}\n\nA test command that checks the state of the target system and restricts\nwhen the `exec` can run. If present, Puppet runs this test command\nfirst, then runs the main command unless the test has an exit code of 0\n(success). For example:\n\n    exec { '/bin/echo root >> /usr/lib/cron/cron.allow':\n      path   => '/usr/bin:/usr/sbin:/bin',\n      unless => 'grep ^root$ /usr/lib/cron/cron.allow 2>/dev/null',\n    }\n\nThis would add `root` to the cron.allow file (on Solaris) unless\n`grep` determines it's already there.\n\nNote that this test command runs with the same `provider`, `path`,\n`user`, `cwd`, and `group` as the main command. If the `path` isn't set, you\nmust fully qualify the command's name.\n\nSince this command is used in the process of determining whether the\n`exec` is already in sync, it must be run during a noop Puppet run.\n\nThis parameter can also take an array of commands. For example:\n\n    unless => ['test -f /tmp/file1', 'test -f /tmp/file2'],\n\nor an array of arrays. For example:\n\n    unless => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']\n\nThis `exec` would only run if every command in the array has a\nnon-zero exit code.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### user {#exec-attribute-user}\n\nThe user to run the command as.\n\n> **Note:** Puppet cannot execute commands as other users on Windows.\n\nNote that if you use this attribute, any error output is not captured\ndue to a bug within Ruby. If you use Puppet to create this user, the\nexec automatically requires the user, as long as it is specified by\nname.\n\nThe $HOME environment variable is not automatically set when using\nthis attribute.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n### Providers {#exec-providers}\n\n#### posix {#exec-provider-posix}\n\nExecutes external binaries by invoking Ruby's `Kernel.exec`.\nWhen the command is a string, it will be executed directly,\nwithout a shell, if it follows these rules:\n - no meta characters\n - no shell reserved word and no special built-in\n\nWhen the command is an Array of Strings, passed as `[cmdname, arg1, ...]`\nit will be executed directly(the first element is taken as a command name\nand the rest are passed as parameters to command with no shell expansion)\nThis is a safer and more predictable way to execute most commands,\nbut prevents the use of globbing and shell built-ins (including control\nlogic like \"for\" and \"if\" statements).\n\nIf the use of globbing and shell built-ins is desired, please check\nthe `shell` provider\n\n* Default for `feature` == `posix`.\n\n#### shell {#exec-provider-shell}\n\nPasses the provided command through `/bin/sh`; only available on\nPOSIX systems. This allows the use of shell globbing and built-ins, and\ndoes not require that the path to a command be fully-qualified. Although\nthis can be more convenient than the `posix` provider, it also means that\nyou need to be more careful with escaping; as ever, with great power comes\netc. etc.\n\nThis provider closely resembles the behavior of the `exec` type\nin Puppet 0.25.x.\n\n#### windows {#exec-provider-windows}\n\nExecute external binaries on Windows systems. As with the `posix`\nprovider, this provider directly calls the command with the arguments\ngiven, without passing it through a shell or performing any interpolation.\nTo use shell built-ins --- that is, to emulate the `shell` provider on\nWindows --- a command must explicitly invoke the shell:\n\n    exec {'echo foo':\n      command => 'cmd.exe /c echo \"foo\"',\n    }\n\nIf no extension is specified for a command, Windows will use the `PATHEXT`\nenvironment variable to locate the executable.\n\n**Note on PowerShell scripts:** PowerShell's default `restricted`\nexecution policy doesn't allow it to run saved scripts. To run PowerShell\nscripts, specify the `remotesigned` execution policy as part of the\ncommand:\n\n    exec { 'test':\n      path    => 'C:/Windows/System32/WindowsPowerShell/v1.0',\n      command => 'powershell -executionpolicy remotesigned -file C:/test.ps1',\n    }\n\n* Default for `os.name` == `windows`.\n\n\n\n\n---------\n\n## file\n\n* [Attributes](#file-attributes)\n* [Providers](#file-providers)\n* [Provider Features](#file-provider-features)\n\n### Description {#file-description}\n\nManages files, including their content, ownership, and permissions.\n\nThe `file` type can manage normal files, directories, and symlinks; the\ntype should be specified in the `ensure` attribute.\n\nFile contents can be managed directly with the `content` attribute, or\ndownloaded from a remote source using the `source` attribute; the latter\ncan also be used to recursively serve directories (when the `recurse`\nattribute is set to `true` or `local`). On Windows, note that file\ncontents are managed in binary mode; Puppet never automatically translates\nline endings.\n\n**Autorequires:** If Puppet is managing the user or group that owns a\nfile, the file resource will autorequire them. If Puppet is managing any\nparent directories of a file, the file resource autorequires them.\n\nWarning: Enabling `recurse` on directories containing large numbers of\nfiles slows agent runs. To manage file attributes for many files,\nconsider using alternative methods such as the `chmod_r`, `chown_r`,\n or `recursive_file_permissions` modules from the Forge.\n\n### Attributes {#file-attributes}\n\n<pre><code>file { 'resource title':\n  <a href=\"#file-attribute-path\">path</a>                    =&gt; <em># <strong>(namevar)</strong> The path to the file to manage.  Must be fully...</em>\n  <a href=\"#file-attribute-ensure\">ensure</a>                  =&gt; <em># Whether the file should exist, and if so what...</em>\n  <a href=\"#file-attribute-backup\">backup</a>                  =&gt; <em># Whether (and how) file content should be backed...</em>\n  <a href=\"#file-attribute-checksum\">checksum</a>                =&gt; <em># The checksum type to use when determining...</em>\n  <a href=\"#file-attribute-checksum_value\">checksum_value</a>          =&gt; <em># The checksum of the source contents. Only md5...</em>\n  <a href=\"#file-attribute-content\">content</a>                 =&gt; <em># The desired contents of a file, as a string...</em>\n  <a href=\"#file-attribute-ctime\">ctime</a>                   =&gt; <em># A read-only state to check the file ctime. On...</em>\n  <a href=\"#file-attribute-force\">force</a>                   =&gt; <em># Perform the file operation even if it will...</em>\n  <a href=\"#file-attribute-group\">group</a>                   =&gt; <em># Which group should own the file.  Argument can...</em>\n  <a href=\"#file-attribute-ignore\">ignore</a>                  =&gt; <em># A parameter which omits action on files matching </em>\n  <a href=\"#file-attribute-links\">links</a>                   =&gt; <em># How to handle links during file actions.  During </em>\n  <a href=\"#file-attribute-max_files\">max_files</a>               =&gt; <em># In case the resource is a directory and the...</em>\n  <a href=\"#file-attribute-mode\">mode</a>                    =&gt; <em># The desired permissions mode for the file, in...</em>\n  <a href=\"#file-attribute-mtime\">mtime</a>                   =&gt; <em># A read-only state to check the file mtime. On...</em>\n  <a href=\"#file-attribute-owner\">owner</a>                   =&gt; <em># The user to whom the file should belong....</em>\n  <a href=\"#file-attribute-provider\">provider</a>                =&gt; <em># The specific backend to use for this `file...</em>\n  <a href=\"#file-attribute-purge\">purge</a>                   =&gt; <em># Whether unmanaged files should be purged. This...</em>\n  <a href=\"#file-attribute-recurse\">recurse</a>                 =&gt; <em># Whether to recursively manage the _contents_ of...</em>\n  <a href=\"#file-attribute-recurselimit\">recurselimit</a>            =&gt; <em># How far Puppet should descend into...</em>\n  <a href=\"#file-attribute-replace\">replace</a>                 =&gt; <em># Whether to replace a file or symlink that...</em>\n  <a href=\"#file-attribute-selinux_ignore_defaults\">selinux_ignore_defaults</a> =&gt; <em># If this is set, Puppet will not call the SELinux </em>\n  <a href=\"#file-attribute-selrange\">selrange</a>                =&gt; <em># What the SELinux range component of the context...</em>\n  <a href=\"#file-attribute-selrole\">selrole</a>                 =&gt; <em># What the SELinux role component of the context...</em>\n  <a href=\"#file-attribute-seltype\">seltype</a>                 =&gt; <em># What the SELinux type component of the context...</em>\n  <a href=\"#file-attribute-seluser\">seluser</a>                 =&gt; <em># What the SELinux user component of the context...</em>\n  <a href=\"#file-attribute-show_diff\">show_diff</a>               =&gt; <em># Whether to display differences when the file...</em>\n  <a href=\"#file-attribute-source\">source</a>                  =&gt; <em># A source file, which will be copied into place...</em>\n  <a href=\"#file-attribute-source_permissions\">source_permissions</a>      =&gt; <em># Whether (and how) Puppet should copy owner...</em>\n  <a href=\"#file-attribute-sourceselect\">sourceselect</a>            =&gt; <em># Whether to copy all valid sources, or just the...</em>\n  <a href=\"#file-attribute-staging_location\">staging_location</a>        =&gt; <em># When rendering a file first render it to this...</em>\n  <a href=\"#file-attribute-target\">target</a>                  =&gt; <em># The target for creating a link.  Currently...</em>\n  <a href=\"#file-attribute-type\">type</a>                    =&gt; <em># A read-only state to check the file...</em>\n  <a href=\"#file-attribute-validate_cmd\">validate_cmd</a>            =&gt; <em># A command for validating the file's syntax...</em>\n  <a href=\"#file-attribute-validate_replacement\">validate_replacement</a>    =&gt; <em># The replacement string in a `validate_cmd` that...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### path {#file-attribute-path}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe path to the file to manage.  Must be fully qualified.\n\nOn Windows, the path should include the drive letter and should use `/` as\nthe separator character (rather than `\\\\`).\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### ensure {#file-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhether the file should exist, and if so what kind of file it should be.\nPossible values are `present`, `absent`, `file`, `directory`, and `link`.\n\n* `present` accepts any form of file existence, and creates a\n  normal file if the file is missing. (The file will have no content\n  unless the `content` or `source` attribute is used.)\n* `absent` ensures the file doesn't exist, and deletes it if necessary.\n* `file` ensures it's a normal file, and enables use of the `content` or\n  `source` attribute.\n* `directory` ensures it's a directory, and enables use of the `source`,\n  `recurse`, `recurselimit`, `ignore`, and `purge` attributes.\n* `link` ensures the file is a symlink, and **requires** that you also\n  set the `target` attribute. Symlinks are supported on all Posix\n  systems and on Windows Vista / 2008 and higher. On Windows, managing\n  symlinks requires Puppet agent's user account to have the \"Create\n  Symbolic Links\" privilege; this can be configured in the \"User Rights\n  Assignment\" section in the Windows policy editor. By default, Puppet\n  agent runs as the Administrator account, which has this privilege.\n\nPuppet avoids destroying directories unless the `force` attribute is set\nto `true`. This means that if a file is currently a directory, setting\n`ensure` to anything but `directory` or `present` will cause Puppet to\nskip managing the resource and log either a notice or an error.\n\nThere is one other non-standard value for `ensure`. If you specify the\npath to another file as the ensure value, it is equivalent to specifying\n`link` and using that path as the `target`:\n\n    # Equivalent resources:\n\n    file { '/etc/inetd.conf':\n      ensure => '/etc/inet/inetd.conf',\n    }\n\n    file { '/etc/inetd.conf':\n      ensure => link,\n      target => '/etc/inet/inetd.conf',\n    }\n\nHowever, we recommend using `link` and `target` explicitly, since this\nbehavior can be harder to read and is\n[deprecated](https://docs.puppet.com/puppet/4.3/deprecated_language.html)\nas of Puppet 4.3.0.\n\nValid values are `absent` (also called `false`), `file`, `present`, `directory`, `link`. Values can match `/./`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### backup {#file-attribute-backup}\n\nWhether (and how) file content should be backed up before being replaced.\nThis attribute works best as a resource default in the site manifest\n(`File { backup => main }`), so it can affect all file resources.\n\n* If set to `false`, file content won't be backed up.\n* If set to a string beginning with `.`, such as `.puppet-bak`, Puppet will\n  use copy the file in the same directory with that value as the extension\n  of the backup. (A value of `true` is a synonym for `.puppet-bak`.)\n* If set to any other string, Puppet will try to back up to a filebucket\n  with that title. Puppet automatically creates a **local** filebucket\n  named `puppet` if one doesn't already exist. See the `filebucket` resource\n  type for more details.\n\nDefault value: `false`\n\nBacking up to a local filebucket isn't particularly useful. If you want\nto make organized use of backups, you will generally want to use the\nprimary Puppet server's filebucket service. This requires declaring a\nfilebucket resource and a resource default for the `backup` attribute\nin site.pp:\n\n    # /etc/puppetlabs/puppet/manifests/site.pp\n    filebucket { 'main':\n      path   => false,                # This is required for remote filebuckets.\n      server => 'puppet.example.com', # Optional; defaults to the configured primary Puppet server.\n    }\n\n    File { backup => main, }\n\nIf you are using multiple primary servers, you will want to\ncentralize the contents of the filebucket. Either configure your load\nbalancer to direct all filebucket traffic to a single primary server, or use\nsomething like an out-of-band rsync task to synchronize the content on all\nprimary servers.\n\n> **Note**: Enabling and using the backup option, and by extension the\n  filebucket resource, requires appropriate planning and management to ensure\n  that sufficient disk space is available for the file backups. Generally, you\n  can implement this using one of the following two options:\n  - Use a `find` command and `crontab` entry to retain only the last X days\n  of file backups. For example:\n\n  ```\n  find /opt/puppetlabs/server/data/puppetserver/bucket -type f -mtime +45 -atime +45 -print0 | xargs -0 rm\n  ```\n\n  - Restrict the directory to a maximum size after which the oldest items are removed.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### checksum {#file-attribute-checksum}\n\nThe checksum type to use when determining whether to replace a file's contents.\n\nThe default checksum type is sha256.\n\nValid values are `sha256`, `sha256lite`, `md5`, `md5lite`, `sha1`, `sha1lite`, `sha512`, `sha384`, `sha224`, `mtime`, `ctime`, `none`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### checksum_value {#file-attribute-checksum_value}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe checksum of the source contents. Only md5, sha256, sha224, sha384 and sha512\nare supported when specifying this parameter. If this parameter is set,\nsource_permissions will be assumed to be false, and ownership and permissions\nwill not be read from source.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### content {#file-attribute-content}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe desired contents of a file, as a string. This attribute is mutually\nexclusive with `source` and `target`.\n\nNewlines and tabs can be specified in double-quoted strings using\nstandard escaped syntax --- \\n for a newline, and \\t for a tab.\n\nWith very small files, you can construct content strings directly in\nthe manifest...\n\n    define resolve($nameserver1, $nameserver2, $domain, $search) {\n        $str = \"search ${search}\n            domain ${domain}\n            nameserver ${nameserver1}\n            nameserver ${nameserver2}\n            \"\n\n        file { '/etc/resolv.conf':\n          content => $str,\n        }\n    }\n\n...but for larger files, this attribute is more useful when combined with the\n[template](https://puppet.com/docs/puppet/latest/function.html#template)\nor [file](https://puppet.com/docs/puppet/latest/function.html#file)\nfunction.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### ctime {#file-attribute-ctime}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA read-only state to check the file ctime. On most modern *nix-like\nsystems, this is the time of the most recent change to the owner, group,\npermissions, or content of the file.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### force {#file-attribute-force}\n\nPerform the file operation even if it will destroy one or more directories.\nYou must use `force` in order to:\n\n* `purge` subdirectories\n* Replace directories with files or links\n* Remove a directory when `ensure => absent`\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### group {#file-attribute-group}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhich group should own the file.  Argument can be either a group\nname or a group ID.\n\nOn Windows, a user (such as \"Administrator\") can be set as a file's group\nand a group (such as \"Administrators\") can be set as a file's owner;\nhowever, a file's owner and group shouldn't be the same. (If the owner\nis also the group, files with modes like `\"0640\"` will cause log churn, as\nthey will always appear out of sync.)\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### ignore {#file-attribute-ignore}\n\nA parameter which omits action on files matching\nspecified patterns during recursion.  Uses Ruby's builtin globbing\nengine, so shell metacharacters such as `[a-z]*` are fully supported.\nMatches that would descend into the directory structure are ignored,\nsuch as `*/*`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### links {#file-attribute-links}\n\nHow to handle links during file actions.  During file copying,\n`follow` will copy the target file instead of the link and `manage`\nwill copy the link itself. When not copying, `manage` will manage\nthe link, and `follow` will manage the file to which the link points.\n\nValid values are `follow`, `manage`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### max_files {#file-attribute-max_files}\n\nIn case the resource is a directory and the recursion is enabled, puppet will\ngenerate a new resource for each file file found, possible leading to\nan excessive number of resources generated without any control.\n\nSetting `max_files` will check the number of file resources that\nwill eventually be created and will raise a resource argument error if the\nlimit will be exceeded.\n\nUse value `0` to log a warning instead of raising an error.\n\nUse value `-1` to disable errors and warnings due to max files.\n\nValues can match `/^[0-9]+$/`, `/^-1$/`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### mode {#file-attribute-mode}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe desired permissions mode for the file, in symbolic or numeric\nnotation. This value **must** be specified as a string; do not use\nun-quoted numbers to represent file modes.\n\nIf the mode is omitted (or explicitly set to `undef`), Puppet does not\nenforce permissions on existing files and creates new files with\npermissions of `0644`.\n\nThe `file` type uses traditional Unix permission schemes and translates\nthem to equivalent permissions for systems which represent permissions\ndifferently, including Windows. For detailed ACL controls on Windows,\nyou can leave `mode` unmanaged and use\n[the puppetlabs/acl module.](https://forge.puppetlabs.com/puppetlabs/acl)\n\nNumeric modes should use the standard octal notation of\n`<SETUID/SETGID/STICKY><OWNER><GROUP><OTHER>` (for example, \"0644\").\n\n* Each of the \"owner,\" \"group,\" and \"other\" digits should be a sum of the\n  permissions for that class of users, where read = 4, write = 2, and\n  execute/search = 1.\n* The setuid/setgid/sticky digit is also a sum, where setuid = 4, setgid = 2,\n  and sticky = 1.\n* The setuid/setgid/sticky digit is optional. If it is absent, Puppet will\n  clear any existing setuid/setgid/sticky permissions. (So to make your intent\n  clear, you should use at least four digits for numeric modes.)\n* When specifying numeric permissions for directories, Puppet sets the search\n  permission wherever the read permission is set.\n\nSymbolic modes should be represented as a string of comma-separated\npermission clauses, in the form `<WHO><OP><PERM>`:\n\n* \"Who\" should be any combination of u (user), g (group), and o (other), or a (all)\n* \"Op\" should be = (set exact permissions), + (add select permissions),\n  or - (remove select permissions)\n* \"Perm\" should be one or more of:\n    * r (read)\n    * w (write)\n    * x (execute/search)\n    * t (sticky)\n    * s (setuid/setgid)\n    * X (execute/search if directory or if any one user can execute)\n    * u (user's current permissions)\n    * g (group's current permissions)\n    * o (other's current permissions)\n\nThus, mode `\"0664\"` could be represented symbolically as either `a=r,ug+w`\nor `ug=rw,o=r`.  However, symbolic modes are more expressive than numeric\nmodes: a mode only affects the specified bits, so `mode => 'ug+w'` will\nset the user and group write bits, without affecting any other bits.\n\nSee the manual page for GNU or BSD `chmod` for more details\non numeric and symbolic modes.\n\nOn Windows, permissions are translated as follows:\n\n* Owner and group names are mapped to Windows SIDs\n* The \"other\" class of users maps to the \"Everyone\" SID\n* The read/write/execute permissions map to the `FILE_GENERIC_READ`,\n  `FILE_GENERIC_WRITE`, and `FILE_GENERIC_EXECUTE` access rights; a\n  file's owner always has the `FULL_CONTROL` right\n* \"Other\" users can't have any permissions a file's group lacks,\n  and its group can't have any permissions its owner lacks; that is, \"0644\"\n  is an acceptable mode, but \"0464\" is not.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### mtime {#file-attribute-mtime}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA read-only state to check the file mtime. On *nix-like systems, this\nis the time of the most recent change to the content of the file.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### owner {#file-attribute-owner}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user to whom the file should belong.  Argument can be a user name or a\nuser ID.\n\nOn Windows, a group (such as \"Administrators\") can be set as a file's owner\nand a user (such as \"Administrator\") can be set as a file's group; however,\na file's owner and group shouldn't be the same. (If the owner is also\nthe group, files with modes like `\"0640\"` will cause log churn, as they\nwill always appear out of sync.)\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### provider {#file-attribute-provider}\n\nThe specific backend to use for this `file`\nresource. You will seldom need to specify this --- Puppet will usually\ndiscover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`posix`](#file-provider-posix)\n* [`windows`](#file-provider-windows)\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### purge {#file-attribute-purge}\n\nWhether unmanaged files should be purged. This option only makes\nsense when `ensure => directory` and `recurse => true`.\n\n* When recursively duplicating an entire directory with the `source`\n  attribute, `purge => true` will automatically purge any files\n  that are not in the source directory.\n* When managing files in a directory as individual resources,\n  setting `purge => true` will purge any files that aren't being\n  specifically managed.\n\nIf you have a filebucket configured, the purged files will be uploaded,\nbut if you do not, this will destroy data.\n\nUnless `force => true` is set, purging will **not** delete directories,\nalthough it will delete the files they contain.\n\nIf `recurselimit` is set and you aren't using `force => true`, purging\nwill obey the recursion limit; files in any subdirectories deeper than the\nlimit will be treated as unmanaged and left alone.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### recurse {#file-attribute-recurse}\n\nWhether to recursively manage the _contents_ of a directory. This attribute\nis only used when `ensure => directory` is set. The allowed values are:\n\n* `false` --- The default behavior. The contents of the directory will not be\n  automatically managed.\n* `remote` --- If the `source` attribute is set, Puppet will automatically\n  manage the contents of the source directory (or directories), ensuring\n  that equivalent files and directories exist on the target system and\n  that their contents match.\n\n  Using `remote` will disable the `purge` attribute, but results in faster\n  catalog application than `recurse => true`.\n\n  The `source` attribute is mandatory when `recurse => remote`.\n* `true` --- If the `source` attribute is set, this behaves similarly to\n  `recurse => remote`, automatically managing files from the source directory.\n\n  This also enables the `purge` attribute, which can delete unmanaged\n  files from a directory. See the description of `purge` for more details.\n\n  The `source` attribute is not mandatory when using `recurse => true`, so you\n  can enable purging in directories where all files are managed individually.\n\nBy default, setting recurse to `remote` or `true` will manage _all_\nsubdirectories. You can use the `recurselimit` attribute to limit the\nrecursion depth.\n\nValid values are `true`, `false`, `remote`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### recurselimit {#file-attribute-recurselimit}\n\nHow far Puppet should descend into subdirectories, when using\n`ensure => directory` and either `recurse => true` or `recurse => remote`.\nThe recursion limit affects which files will be copied from the `source`\ndirectory, as well as which files can be purged when `purge => true`.\n\nSetting `recurselimit => 0` is the same as setting `recurse => false` ---\nPuppet will manage the directory, but all of its contents will be treated\nas unmanaged.\n\nSetting `recurselimit => 1` will manage files and directories that are\ndirectly inside the directory, but will not manage the contents of any\nsubdirectories.\n\nSetting `recurselimit => 2` will manage the direct contents of the\ndirectory, as well as the contents of the _first_ level of subdirectories.\n\nThis pattern continues for each incremental value of `recurselimit`.\n\nValues can match `/^[0-9]+$/`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### replace {#file-attribute-replace}\n\nWhether to replace a file or symlink that already exists on the local system but\nwhose content doesn't match what the `source` or `content` attribute\nspecifies.  Setting this to false allows file resources to initialize files\nwithout overwriting future changes.  Note that this only affects content;\nPuppet will still manage ownership and permissions.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### selinux_ignore_defaults {#file-attribute-selinux_ignore_defaults}\n\nIf this is set, Puppet will not call the SELinux function selabel_lookup to\nsupply defaults for the SELinux attributes (seluser, selrole,\nseltype, and selrange). In general, you should leave this set at its\ndefault and only set it to true when you need Puppet to not try to fix\nSELinux labels automatically.\n\nValid values are `true`, `false`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### selrange {#file-attribute-selrange}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux range component of the context of the file should be.\nAny valid SELinux range component is accepted.  For example `s0` or\n`SystemHigh`.  If not specified, it defaults to the value returned by\nselabel_lookup for the file, if any exists.  Only valid on systems with\nSELinux support enabled and that have support for MCS (Multi-Category\nSecurity).\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### selrole {#file-attribute-selrole}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux role component of the context of the file should be.\nAny valid SELinux role component is accepted.  For example `role_r`.\nIf not specified, it defaults to the value returned by selabel_lookup for\nthe file, if any exists.  Only valid on systems with SELinux support\nenabled.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### seltype {#file-attribute-seltype}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux type component of the context of the file should be.\nAny valid SELinux type component is accepted.  For example `tmp_t`.\nIf not specified, it defaults to the value returned by selabel_lookup for\nthe file, if any exists.  Only valid on systems with SELinux support\nenabled.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### seluser {#file-attribute-seluser}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux user component of the context of the file should be.\nAny valid SELinux user component is accepted.  For example `user_u`.\nIf not specified, it defaults to the value returned by selabel_lookup for\nthe file, if any exists.  Only valid on systems with SELinux support\nenabled.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### show_diff {#file-attribute-show_diff}\n\nWhether to display differences when the file changes, defaulting to\ntrue.  This parameter is useful for files that may contain passwords or\nother secret data, which might otherwise be included in Puppet reports or\nother insecure outputs.  If the global `show_diff` setting\nis false, then no diffs will be shown even if this parameter is true.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### source {#file-attribute-source}\n\nA source file, which will be copied into place on the local system. This\nattribute is mutually exclusive with `content` and `target`. Allowed\nvalues are:\n\n* `puppet:` URIs, which point to files in modules or Puppet file server\nmount points.\n* Fully qualified paths to locally available files (including files on NFS\nshares or Windows mapped drives).\n* `file:` URIs, which behave the same as local file paths.\n* `http(s):` URIs, which point to files served by common web servers.\n\nThe normal form of a `puppet:` URI is:\n\n`puppet:///modules/<MODULE NAME>/<FILE PATH>`\n\nThis will fetch a file from a module on the Puppet master (or from a\nlocal module when using Puppet apply). Given a `modulepath` of\n`/etc/puppetlabs/code/modules`, the example above would resolve to\n`/etc/puppetlabs/code/modules/<MODULE NAME>/files/<FILE PATH>`.\n\nUnlike `content`, the `source` attribute can be used to recursively copy\ndirectories if the `recurse` attribute is set to `true` or `remote`. If\na source directory contains symlinks, use the `links` attribute to\nspecify whether to recreate links or follow them.\n\n_HTTP_ URIs cannot be used to recursively synchronize whole directory\ntrees. You cannot use `source_permissions` values other than `ignore`\nbecause HTTP servers do not transfer any metadata that translates to\nownership or permission details.\n\nPuppet determines if file content is synchronized by computing a checksum\nfor the local file and comparing it against the `checksum_value`\nparameter. If the `checksum_value` parameter is not specified for\n`puppet` and `file` sources, Puppet computes a checksum based on its\n`Puppet[:digest_algorithm]`. For `http(s)` sources, Puppet uses the\nfirst HTTP header it recognizes out of the following list:\n`X-Checksum-Sha256`, `X-Checksum-Sha1`, `X-Checksum-Md5` or `Content-MD5`.\nIf the server response does not include one of these headers, Puppet\ndefaults to using the `Last-Modified` header. Puppet updates the local\nfile if the header is newer than the modified time (mtime) of the local\nfile.\n\n_HTTP_ URIs can include a user information component so that Puppet can\nretrieve file metadata and content from HTTP servers that require HTTP Basic\nauthentication. For example `https://<user>:<pass>@<server>:<port>/path/to/file.`\n\nWhen connecting to _HTTPS_ servers, Puppet trusts CA certificates in the\npuppet-agent certificate bundle and the Puppet CA. You can configure Puppet\nto trust additional CA certificates using the `Puppet[:ssl_trust_store]`\nsetting.\n\nMultiple `source` values can be specified as an array, and Puppet will\nuse the first source that exists. This can be used to serve different\nfiles to different system types:\n\n    file { '/etc/nfs.conf':\n      source => [\n        \"puppet:///modules/nfs/conf.${host}\",\n        \"puppet:///modules/nfs/conf.${os['name']}\",\n        'puppet:///modules/nfs/conf'\n      ]\n    }\n\nAlternately, when serving directories recursively, multiple sources can\nbe combined by setting the `sourceselect` attribute to `all`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### source_permissions {#file-attribute-source_permissions}\n\nWhether (and how) Puppet should copy owner, group, and mode permissions from\nthe `source` to `file` resources when the permissions are not explicitly\nspecified. (In all cases, explicit permissions will take precedence.)\nValid values are `use`, `use_when_creating`, and `ignore`:\n\n* `ignore` (the default) will never apply the owner, group, or mode from\n  the `source` when managing a file. When creating new files without explicit\n  permissions, the permissions they receive will depend on platform-specific\n  behavior. On POSIX, Puppet will use the umask of the user it is running as.\n  On Windows, Puppet will use the default DACL associated with the user it is\n  running as.\n* `use` will cause Puppet to apply the owner, group,\n  and mode from the `source` to any files it is managing.\n* `use_when_creating` will only apply the owner, group, and mode from the\n  `source` when creating a file; existing files will not have their permissions\n  overwritten.\n\nValid values are `use`, `use_when_creating`, `ignore`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### sourceselect {#file-attribute-sourceselect}\n\nWhether to copy all valid sources, or just the first one.  This parameter\nonly affects recursive directory copies; by default, the first valid\nsource is the only one used, but if this parameter is set to `all`, then\nall valid sources will have all of their contents copied to the local\nsystem. If a given file exists in more than one source, the version from\nthe earliest source in the list will be used.\n\nValid values are `first`, `all`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### staging_location {#file-attribute-staging_location}\n\nWhen rendering a file first render it to this location. The default\nlocation is the same path as the desired location with a unique filename.\nThis parameter is useful in conjuction with validate_cmd to test a\nfile before moving the file to it's final location.\nWARNING: File replacement is only guaranteed to be atomic if the staging\nlocation is on the same filesystem as the final location.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### target {#file-attribute-target}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe target for creating a link.  Currently, symlinks are the\nonly type supported. This attribute is mutually exclusive with `source`\nand `content`.\n\nSymlink targets can be relative, as well as absolute:\n\n    # (Useful on Solaris)\n    file { '/etc/inetd.conf':\n      ensure => link,\n      target => 'inet/inetd.conf',\n    }\n\nDirectories of symlinks can be served recursively by instead using the\n`source` attribute, setting `ensure` to `directory`, and setting the\n`links` attribute to `manage`.\n\nValid values are `notlink`. Values can match `/./`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### type {#file-attribute-type}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA read-only state to check the file type.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### validate_cmd {#file-attribute-validate_cmd}\n\nA command for validating the file's syntax before replacing it. If\nPuppet would need to rewrite a file due to new `source` or `content`, it\nwill check the new content's validity first. If validation fails, the file\nresource will fail.\n\nThis command must have a fully qualified path, and should contain a\npercent (`%`) token where it would expect an input file. It must exit `0`\nif the syntax is correct, and non-zero otherwise. The command will be\nrun on the target system while applying the catalog, not on the primary Puppet server.\n\nExample:\n\n    file { '/etc/apache2/apache2.conf':\n      content      => 'example',\n      validate_cmd => '/usr/sbin/apache2 -t -f %',\n    }\n\nThis would replace apache2.conf only if the test returned true.\n\nNote that if a validation command requires a `%` as part of its text,\nyou can specify a different placeholder token with the\n`validate_replacement` attribute.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### validate_replacement {#file-attribute-validate_replacement}\n\nThe replacement string in a `validate_cmd` that will be replaced\nwith an input file name.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n### Providers {#file-providers}\n\n#### posix {#file-provider-posix}\n\nUses POSIX functionality to manage file ownership and permissions.\n\n* Supported features: `manages_symlinks`.\n\n#### windows {#file-provider-windows}\n\nUses Microsoft Windows functionality to manage file ownership and permissions.\n\n* Supported features: `manages_symlinks`.\n\n### Provider Features {#file-provider-features}\n\nAvailable features:\n\n* `manages_symlinks` --- The provider can manage symbolic links.\n\nProvider support:\n\n* **posix** - _manages symlinks_\n* **windows** - _manages symlinks_\n  \n\n\n\n\n---------\n\n## filebucket\n\n* [Attributes](#filebucket-attributes)\n\n### Description {#filebucket-description}\n\nA repository for storing and retrieving file content by cryptographic checksum. Can\nbe local to each agent node, or centralized on a primary Puppet server. All\npuppet servers provide a filebucket service that agent nodes can access\nvia HTTP, but you must declare a filebucket resource before any agents\nwill do so.\n\nFilebuckets are used for the following features:\n\n- **Content backups.** If the `file` type's `backup` attribute is set to\n  the name of a filebucket, Puppet will back up the _old_ content whenever\n  it rewrites a file; see the documentation for the `file` type for more\n  details. These backups can be used for manual recovery of content, but\n  are more commonly used to display changes and differences in a tool like\n  Puppet Dashboard.\n\nTo use a central filebucket for backups, you will usually want to declare\na filebucket resource and a resource default for the `backup` attribute\nin site.pp:\n\n    # /etc/puppetlabs/puppet/manifests/site.pp\n    filebucket { 'main':\n      path   => false,                # This is required for remote filebuckets.\n      server => 'puppet.example.com', # Optional; defaults to the configured primary server.\n    }\n\n    File { backup => main, }\n\nPuppet Servers automatically provide the filebucket service, so\nthis will work in a default configuration. If you have a heavily\nrestricted Puppet Server `auth.conf` file, you may need to allow access to the\n`file_bucket_file` endpoint.\n\n### Attributes {#filebucket-attributes}\n\n<pre><code>filebucket { 'resource title':\n  <a href=\"#filebucket-attribute-name\">name</a>   =&gt; <em># <strong>(namevar)</strong> The name of the...</em>\n  <a href=\"#filebucket-attribute-path\">path</a>   =&gt; <em># The path to the _local_ filebucket; defaults to...</em>\n  <a href=\"#filebucket-attribute-port\">port</a>   =&gt; <em># The port on which the remote server is...</em>\n  <a href=\"#filebucket-attribute-server\">server</a> =&gt; <em># The server providing the remote filebucket...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#filebucket-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the filebucket.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n#### path {#filebucket-attribute-path}\n\nThe path to the _local_ filebucket; defaults to the value of the\n`clientbucketdir` setting.  To use a remote filebucket, you _must_ set\nthis attribute to `false`.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n#### port {#filebucket-attribute-port}\n\nThe port on which the remote server is listening.\n\nThis setting is _only_ consulted if the `path` attribute is set to `false`.\n\nIf this attribute is not specified, the first entry in the `server_list`\nconfiguration setting is used, followed by the value of the `serverport`\nsetting if `server_list` is not set.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n#### server {#filebucket-attribute-server}\n\nThe server providing the remote filebucket service.\n\nThis setting is _only_ consulted if the `path` attribute is set to `false`.\n\nIf this attribute is not specified, the first entry in the `server_list`\nconfiguration setting is used, followed by the value of the `server` setting\nif `server_list` is not set.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n\n\n\n---------\n\n## group\n\n* [Attributes](#group-attributes)\n* [Providers](#group-providers)\n* [Provider Features](#group-provider-features)\n\n### Description {#group-description}\n\nManage groups. On most platforms this can only create groups.\nGroup membership must be managed on individual users.\n\nOn some platforms such as OS X, group membership is managed as an\nattribute of the group, not the user record. Providers must have\nthe feature 'manages_members' to manage the 'members' property of\na group record.\n\n### Attributes {#group-attributes}\n\n<pre><code>group { 'resource title':\n  <a href=\"#group-attribute-name\">name</a>                 =&gt; <em># <strong>(namevar)</strong> The group name. While naming limitations vary by </em>\n  <a href=\"#group-attribute-ensure\">ensure</a>               =&gt; <em># Create or remove the group.  Valid values are...</em>\n  <a href=\"#group-attribute-allowdupe\">allowdupe</a>            =&gt; <em># Whether to allow duplicate GIDs.  Valid values...</em>\n  <a href=\"#group-attribute-attribute_membership\">attribute_membership</a> =&gt; <em># AIX only. Configures the behavior of the...</em>\n  <a href=\"#group-attribute-attributes\">attributes</a>           =&gt; <em># Specify group AIX attributes, as an array of...</em>\n  <a href=\"#group-attribute-auth_membership\">auth_membership</a>      =&gt; <em># Configures the behavior of the `members...</em>\n  <a href=\"#group-attribute-forcelocal\">forcelocal</a>           =&gt; <em># Forces the management of local accounts when...</em>\n  <a href=\"#group-attribute-gid\">gid</a>                  =&gt; <em># The group ID.  Must be specified numerically....</em>\n  <a href=\"#group-attribute-ia_load_module\">ia_load_module</a>       =&gt; <em># The name of the I&A module to use to manage this </em>\n  <a href=\"#group-attribute-members\">members</a>              =&gt; <em># The members of the group. For platforms or...</em>\n  <a href=\"#group-attribute-provider\">provider</a>             =&gt; <em># The specific backend to use for this `group...</em>\n  <a href=\"#group-attribute-system\">system</a>               =&gt; <em># Whether the group is a system group with lower...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#group-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe group name. While naming limitations vary by operating system,\nit is advisable to restrict names to the lowest common denominator,\nwhich is a maximum of 8 characters beginning with a letter.\n\nNote that Puppet considers group names to be case-sensitive, regardless\nof the platform's own rules; be sure to always use the same case when\nreferring to a given group.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### ensure {#group-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nCreate or remove the group.\n\nValid values are `present`, `absent`.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### allowdupe {#group-attribute-allowdupe}\n\nWhether to allow duplicate GIDs.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### attribute_membership {#group-attribute-attribute_membership}\n\nAIX only. Configures the behavior of the `attributes` parameter.\n\n* `minimum` (default) --- The provided list of attributes is partial, and Puppet\n  **ignores** any attributes that aren't listed there.\n* `inclusive` --- The provided list of attributes is comprehensive, and\n  Puppet **purges** any attributes that aren't listed there.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### attributes {#group-attribute-attributes}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify group AIX attributes, as an array of `'key=value'` strings. This\nparameter's behavior can be configured with `attribute_membership`.\n\n\n\nRequires features manages_aix_lam.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### auth_membership {#group-attribute-auth_membership}\n\nConfigures the behavior of the `members` parameter.\n\n* `false` (default) --- The provided list of group members is partial,\n  and Puppet **ignores** any members that aren't listed there.\n* `true` --- The provided list of of group members is comprehensive, and\n  Puppet **purges** any members that aren't listed there.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### forcelocal {#group-attribute-forcelocal}\n\nForces the management of local accounts when accounts are also\nbeing managed by some other Name Switch Service (NSS). For AIX, refer to the `ia_load_module` parameter.\n\nThis option relies on your operating system's implementation of `luser*` commands, such as `luseradd` , `lgroupadd`, and `lusermod`. The `forcelocal` option could behave unpredictably in some circumstances. If the tools it depends on are not available, it might have no effect at all.\n\nValid values are `true`, `false`, `yes`, `no`.\n\nRequires features manages_local_users_and_groups.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### gid {#group-attribute-gid}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe group ID.  Must be specified numerically.  If no group ID is\nspecified when creating a new group, then one will be chosen\nautomatically according to local system standards. This will likely\nresult in the same group having different GIDs on different systems,\nwhich is not recommended.\n\nOn Windows, this property is read-only and will return the group's security\nidentifier (SID).\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### ia_load_module {#group-attribute-ia_load_module}\n\nThe name of the I&A module to use to manage this group.\nThis should be set to `files` if managing local groups.\n\n\n\nRequires features manages_aix_lam.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### members {#group-attribute-members}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe members of the group. For platforms or directory services where group\nmembership is stored in the group objects, not the users. This parameter's\nbehavior can be configured with `auth_membership`.\n\n\n\nRequires features manages_members.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### provider {#group-attribute-provider}\n\nThe specific backend to use for this `group`\nresource. You will seldom need to specify this --- Puppet will usually\ndiscover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`aix`](#group-provider-aix)\n* [`directoryservice`](#group-provider-directoryservice)\n* [`groupadd`](#group-provider-groupadd)\n* [`ldap`](#group-provider-ldap)\n* [`pw`](#group-provider-pw)\n* [`windows_adsi`](#group-provider-windows_adsi)\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### system {#group-attribute-system}\n\nWhether the group is a system group with lower GID.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n### Providers {#group-providers}\n\n#### aix {#group-provider-aix}\n\nGroup management for AIX.\n\n* Required binaries: `/usr/bin/chgroup`, `/usr/bin/mkgroup`, `/usr/sbin/lsgroup`, `/usr/sbin/rmgroup`.\n* Default for `os.name` == `aix`.\n* Supported features: `manages_aix_lam`, `manages_local_users_and_groups`, `manages_members`.\n\n#### directoryservice {#group-provider-directoryservice}\n\nGroup management using DirectoryService on OS X.\n\n* Required binaries: `/usr/bin/dscl`.\n* Default for `os.name` == `darwin`.\n* Supported features: `manages_members`.\n\n#### groupadd {#group-provider-groupadd}\n\nGroup management via `groupadd` and its ilk. The default for most platforms.\n\nTo use the `forcelocal` parameter, you need to install the `libuser` package (providing\n `/usr/sbin/lgroupadd` and `/usr/sbin/luseradd`).\n\n* Required binaries: `groupadd`, `groupdel`, `groupmod`, `lgroupadd`, `lgroupdel`, `lgroupmod`, `usermod`.\n* Supported features: `system_groups`.\n\n#### ldap {#group-provider-ldap}\n\nGroup management via LDAP.\n\nThis provider requires that you have valid values for all of the\nLDAP-related settings in `puppet.conf`, including `ldapbase`.  You will\nalmost definitely need settings for `ldapuser` and `ldappassword` in order\nfor your clients to write to LDAP.\n\nNote that this provider will automatically generate a GID for you if you do\nnot specify one, but it is a potentially expensive operation, as it\niterates across all existing groups to pick the appropriate next one.\n\n#### pw {#group-provider-pw}\n\nGroup management via `pw` on FreeBSD and DragonFly BSD.\n\n* Required binaries: `pw`.\n* Default for `os.name` == `freebsd, dragonfly`.\n* Supported features: `manages_members`.\n\n#### windows_adsi {#group-provider-windows_adsi}\n\nLocal group management for Windows. Group members can be both users and groups.\nAdditionally, local groups can contain domain users.\n\n* Default for `os.name` == `windows`.\n* Supported features: `manages_members`.\n\n### Provider Features {#group-provider-features}\n\nAvailable features:\n\n* `manages_aix_lam` --- The provider can manage AIX Loadable Authentication Module (LAM) system.\n* `manages_local_users_and_groups` --- Allows local groups to be managed on systems that also use some other remote Name Switch Service (NSS) method of managing accounts.\n* `manages_members` --- For directories where membership is an attribute of groups not users.\n* `system_groups` --- The provider allows you to create system groups with lower GIDs.\n\nProvider support:\n\n* **aix** - _manages aix lam, manages local users and groups, manages members_\n* **directoryservice** - _manages members_\n* **groupadd** - _system groups, libuser_\n* **ldap** - No supported Provider features\n* **pw** - _manages members_\n* **windows_adsi** - _manages members_\n  \n\n\n\n\n---------\n\n## notify\n\n* [Attributes](#notify-attributes)\n\n### Description {#notify-description}\n\nSends an arbitrary message, specified as a string, to the agent run-time log. It's important to note that the notify resource type is not idempotent. As a result, notifications are shown as a change on every Puppet run.\n\n### Attributes {#notify-attributes}\n\n<pre><code>notify { 'resource title':\n  <a href=\"#notify-attribute-name\">name</a>     =&gt; <em># <strong>(namevar)</strong> An arbitrary tag for your own reference; the...</em>\n  <a href=\"#notify-attribute-message\">message</a>  =&gt; <em># The message to be sent to the log. Note that the </em>\n  <a href=\"#notify-attribute-withpath\">withpath</a> =&gt; <em># Whether to show the full object path.  Valid...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#notify-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nAn arbitrary tag for your own reference; the name of the message.\n\n([↑ Back to notify attributes](#notify-attributes))\n\n\n#### message {#notify-attribute-message}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe message to be sent to the log. Note that the value specified must be a string.\n\n([↑ Back to notify attributes](#notify-attributes))\n\n\n#### withpath {#notify-attribute-withpath}\n\nWhether to show the full object path.\n\nValid values are `true`, `false`.\n\n([↑ Back to notify attributes](#notify-attributes))\n\n\n\n\n\n---------\n\n## package\n\n* [Attributes](#package-attributes)\n* [Providers](#package-providers)\n* [Provider Features](#package-provider-features)\n\n### Description {#package-description}\n\nManage packages.  There is a basic dichotomy in package\nsupport right now:  Some package types (such as yum and apt) can\nretrieve their own package files, while others (such as rpm and sun)\ncannot.  For those package formats that cannot retrieve their own files,\nyou can use the `source` parameter to point to the correct file.\n\nPuppet will automatically guess the packaging format that you are\nusing based on the platform you are on, but you can override it\nusing the `provider` parameter; each provider defines what it\nrequires in order to function, and you must meet those requirements\nto use a given provider.\n\nYou can declare multiple package resources with the same `name` as long\nas they have unique titles, and specify different providers and commands.\n\nNote that you must use the _title_ to make a reference to a package\nresource; `Package[<NAME>]` is not a synonym for `Package[<TITLE>]` like\nit is for many other resource types.\n\n**Autorequires:** If Puppet is managing the files specified as a\npackage's `adminfile`, `responsefile`, or `source`, the package\nresource will autorequire those files.\n\n### Attributes {#package-attributes}\n\n<pre><code>package { 'resource title':\n  <a href=\"#package-attribute-name\">name</a>                 =&gt; <em># <strong>(namevar)</strong> The package name.  This is the name that the...</em>\n  <a href=\"#package-attribute-provider\">provider</a>             =&gt; <em># <strong>(namevar)</strong> The specific backend to use for this `package...</em>\n  <a href=\"#package-attribute-command\">command</a>              =&gt; <em># <strong>(namevar)</strong> The targeted command to use when managing a...</em>\n  <a href=\"#package-attribute-ensure\">ensure</a>               =&gt; <em># What state the package should be in. On...</em>\n  <a href=\"#package-attribute-adminfile\">adminfile</a>            =&gt; <em># A file containing package defaults for...</em>\n  <a href=\"#package-attribute-allow_virtual\">allow_virtual</a>        =&gt; <em># Specifies if virtual package names are allowed...</em>\n  <a href=\"#package-attribute-allowcdrom\">allowcdrom</a>           =&gt; <em># Tells apt to allow cdrom sources in the...</em>\n  <a href=\"#package-attribute-category\">category</a>             =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-configfiles\">configfiles</a>          =&gt; <em># Whether to keep or replace modified config files </em>\n  <a href=\"#package-attribute-description\">description</a>          =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-enable_only\">enable_only</a>          =&gt; <em># Tells `dnf module` to only enable a specific...</em>\n  <a href=\"#package-attribute-flavor\">flavor</a>               =&gt; <em># OpenBSD and DNF modules support 'flavors', which </em>\n  <a href=\"#package-attribute-install_only\">install_only</a>         =&gt; <em># It should be set for packages that should only...</em>\n  <a href=\"#package-attribute-install_options\">install_options</a>      =&gt; <em># An array of additional options to pass when...</em>\n  <a href=\"#package-attribute-instance\">instance</a>             =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-mark\">mark</a>                 =&gt; <em># Set to hold to tell Debian apt/Solaris pkg to...</em>\n  <a href=\"#package-attribute-package_settings\">package_settings</a>     =&gt; <em># Settings that can change the contents or...</em>\n  <a href=\"#package-attribute-platform\">platform</a>             =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-reinstall_on_refresh\">reinstall_on_refresh</a> =&gt; <em># Whether this resource should respond to refresh...</em>\n  <a href=\"#package-attribute-responsefile\">responsefile</a>         =&gt; <em># A file containing any necessary answers to...</em>\n  <a href=\"#package-attribute-root\">root</a>                 =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-source\">source</a>               =&gt; <em># Where to find the package file. This is mostly...</em>\n  <a href=\"#package-attribute-status\">status</a>               =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-uninstall_options\">uninstall_options</a>    =&gt; <em># An array of additional options to pass when...</em>\n  <a href=\"#package-attribute-vendor\">vendor</a>               =&gt; <em># A read-only parameter set by the...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#package-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe package name.  This is the name that the packaging\nsystem uses internally, which is sometimes (especially on Solaris)\na name that is basically useless to humans.  If a package goes by\nseveral names, you can use a single title and then set the name\nconditionally:\n\n    # In the 'openssl' class\n    $ssl = $os['name'] ? {\n      solaris => SMCossl,\n      default => openssl\n    }\n\n    package { 'openssl':\n      ensure => installed,\n      name   => $ssl,\n    }\n\n    ...\n\n    $ssh = $os['name'] ? {\n      solaris => SMCossh,\n      default => openssh\n    }\n\n    package { 'openssh':\n      ensure  => installed,\n      name    => $ssh,\n      require => Package['openssl'],\n    }\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### provider {#package-attribute-provider}\n\n_(**Secondary namevar:** This resource type allows you to manage multiple resources with the same name as long as their providers are different.)_\n\nThe specific backend to use for this `package`\nresource. You will seldom need to specify this --- Puppet will usually\ndiscover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`aix`](#package-provider-aix)\n* [`appdmg`](#package-provider-appdmg)\n* [`apple`](#package-provider-apple)\n* [`apt`](#package-provider-apt)\n* [`aptitude`](#package-provider-aptitude)\n* [`aptrpm`](#package-provider-aptrpm)\n* [`blastwave`](#package-provider-blastwave)\n* [`dnf`](#package-provider-dnf)\n* [`dnfmodule`](#package-provider-dnfmodule)\n* [`dpkg`](#package-provider-dpkg)\n* [`fink`](#package-provider-fink)\n* [`freebsd`](#package-provider-freebsd)\n* [`gem`](#package-provider-gem)\n* [`hpux`](#package-provider-hpux)\n* [`macports`](#package-provider-macports)\n* [`nim`](#package-provider-nim)\n* [`openbsd`](#package-provider-openbsd)\n* [`opkg`](#package-provider-opkg)\n* [`pacman`](#package-provider-pacman)\n* [`pip2`](#package-provider-pip2)\n* [`pip3`](#package-provider-pip3)\n* [`pip`](#package-provider-pip)\n* [`pkg`](#package-provider-pkg)\n* [`pkgdmg`](#package-provider-pkgdmg)\n* [`pkgin`](#package-provider-pkgin)\n* [`pkgng`](#package-provider-pkgng)\n* [`pkgutil`](#package-provider-pkgutil)\n* [`portage`](#package-provider-portage)\n* [`ports`](#package-provider-ports)\n* [`portupgrade`](#package-provider-portupgrade)\n* [`puppet_gem`](#package-provider-puppet_gem)\n* [`puppetserver_gem`](#package-provider-puppetserver_gem)\n* [`rpm`](#package-provider-rpm)\n* [`rug`](#package-provider-rug)\n* [`sun`](#package-provider-sun)\n* [`sunfreeware`](#package-provider-sunfreeware)\n* [`tdnf`](#package-provider-tdnf)\n* [`up2date`](#package-provider-up2date)\n* [`urpmi`](#package-provider-urpmi)\n* [`windows`](#package-provider-windows)\n* [`xbps`](#package-provider-xbps)\n* [`yum`](#package-provider-yum)\n* [`zypper`](#package-provider-zypper)\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### command {#package-attribute-command}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe targeted command to use when managing a package:\n\n  package { 'mysql':\n    provider => gem,\n  }\n\n  package { 'mysql-opt':\n    name     => 'mysql',\n    provider => gem,\n    command  => '/opt/ruby/bin/gem',\n  }\n\nEach provider defines a package management command and uses the first\ninstance of the command found in the PATH.\n\nProviders supporting the targetable feature allow you to specify the\nabsolute path of the package management command. Specifying the absolute\npath is useful when multiple instances of the command are installed, or\nthe command is not in the PATH.\n\n\n\nRequires features targetable.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### ensure {#package-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat state the package should be in. On packaging systems that can\nretrieve new packages on their own, you can choose which package to\nretrieve by specifying a version number or `latest` as the ensure\nvalue. On packaging systems that manage configuration files separately\nfrom \"normal\" system files, you can uninstall config files by\nspecifying `purged` as the ensure value. This defaults to `installed`.\n\nVersion numbers must match the full version to install, including\nrelease if the provider uses a release moniker. For\nexample, to install the bash package from the rpm\n`bash-4.1.2-29.el6.x86_64.rpm`, use the string `'4.1.2-29.el6'`.\n\nOn supported providers, version ranges can also be ensured. For example,\ninequalities: `<2.0.0`, or intersections: `>1.0.0 <2.0.0`.\n\nValid values are `present` (also called `installed`), `absent`, `purged`, `disabled`, `latest`. Values can match `/./`.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### adminfile {#package-attribute-adminfile}\n\nA file containing package defaults for installing packages.\n\nThis attribute is only used on Solaris. Its value should be a path to a\nlocal file stored on the target system. Solaris's package tools expect\neither an absolute file path or a relative path to a file in\n`/var/sadm/install/admin`.\n\nThe value of `adminfile` will be passed directly to the `pkgadd` or\n`pkgrm` command with the `-a <ADMINFILE>` option.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### allow_virtual {#package-attribute-allow_virtual}\n\nSpecifies if virtual package names are allowed for install and uninstall.\n\nValid values are `true`, `false`, `yes`, `no`.\n\nRequires features virtual_packages.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### allowcdrom {#package-attribute-allowcdrom}\n\nTells apt to allow cdrom sources in the sources.list file.\nNormally apt will bail if you try this.\n\nValid values are `true`, `false`.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### category {#package-attribute-category}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### configfiles {#package-attribute-configfiles}\n\nWhether to keep or replace modified config files when installing or\nupgrading a package. This only affects the `apt` and `dpkg` providers.\n\nValid values are `keep`, `replace`.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### description {#package-attribute-description}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### enable_only {#package-attribute-enable_only}\n\nTells `dnf module` to only enable a specific module, instead\nof installing its default profile.\n\nModules with no default profile will be enabled automatically\nwithout the use of this parameter.\n\nConflicts with the `flavor` property, which selects a profile\nto install.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### flavor {#package-attribute-flavor}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nOpenBSD and DNF modules support 'flavors', which are\nfurther specifications for which type of package you want.\n\n\n\nRequires features supports_flavors.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### install_only {#package-attribute-install_only}\n\nIt should be set for packages that should only ever be installed,\nnever updated. Kernels in particular fall into this category.\n\nValid values are `true`, `false`, `yes`, `no`.\n\nRequires features install_only.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### install_options {#package-attribute-install_options}\n\nAn array of additional options to pass when installing a package. These\noptions are package-specific, and should be documented by the software\nvendor.  One commonly implemented option is `INSTALLDIR`:\n\n    package { 'mysql':\n      ensure          => installed,\n      source          => 'N:/packages/mysql-5.5.16-winx64.msi',\n      install_options => [ '/S', { 'INSTALLDIR' => 'C:\\mysql-5.5' } ],\n    }\n\nEach option in the array can either be a string or a hash, where each\nkey and value pair are interpreted in a provider specific way.  Each\noption will automatically be quoted when passed to the install command.\n\nWith Windows packages, note that file paths in an install option must\nuse backslashes. (Since install options are passed directly to the\ninstallation command, forward slashes won't be automatically converted\nlike they are in `file` resources.) Note also that backslashes in\ndouble-quoted strings _must_ be escaped and backslashes in single-quoted\nstrings _can_ be escaped.\n\n\n\nRequires features install_options.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### instance {#package-attribute-instance}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### mark {#package-attribute-mark}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSet to hold to tell Debian apt/Solaris pkg to hold the package version\n\nValid values are: hold/none\nDefault is \"none\". Mark can be specified with or without `ensure`,\nif `ensure` is missing will default to \"present\".\n\nMark cannot be specified together with \"purged\", or \"absent\"\nvalues for `ensure`.\n\nValid values are `hold`, `none`.\n\nRequires features holdable.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### package_settings {#package-attribute-package_settings}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSettings that can change the contents or configuration of a package.\n\nThe formatting and effects of package_settings are provider-specific; any\nprovider that implements them must explain how to use them in its\ndocumentation. (Our general expectation is that if a package is\ninstalled but its settings are out of sync, the provider should\nre-install that package with the desired settings.)\n\nAn example of how package_settings could be used is FreeBSD's port build\noptions --- a future version of the provider could accept a hash of options,\nand would reinstall the port if the installed version lacked the correct\nsettings.\n\n    package { 'www/apache22':\n      package_settings => { 'SUEXEC' => false }\n    }\n\nAgain, check the documentation of your platform's package provider to see\nthe actual usage.\n\n\n\nRequires features package_settings.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### platform {#package-attribute-platform}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### reinstall_on_refresh {#package-attribute-reinstall_on_refresh}\n\nWhether this resource should respond to refresh events (via `subscribe`,\n`notify`, or the `~>` arrow) by reinstalling the package. Only works for\nproviders that support the `reinstallable` feature.\n\nThis is useful for source-based distributions, where you may want to\nrecompile a package if the build options change.\n\nIf you use this, be careful of notifying classes when you want to restart\nservices. If the class also contains a refreshable package, doing so could\ncause unnecessary re-installs.\n\nValid values are `true`, `false`.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### responsefile {#package-attribute-responsefile}\n\nA file containing any necessary answers to questions asked by\nthe package.  This is currently used on Solaris and Debian.  The\nvalue will be validated according to system rules, but it should\ngenerally be a fully qualified path.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### root {#package-attribute-root}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### source {#package-attribute-source}\n\nWhere to find the package file. This is mostly used by providers that don't\nautomatically download packages from a central repository. (For example:\nthe `yum` provider ignores this attribute, `apt` provider uses it if present\nand the `rpm` and `dpkg` providers require it.)\n\nDifferent providers accept different values for `source`. Most providers\naccept paths to local files stored on the target system. Some providers\nmay also accept URLs or network drive paths. Puppet will not\nautomatically retrieve source files for you, and usually just passes the\nvalue of `source` to the package installation command.\n\nYou can use a `file` resource if you need to manually copy package files\nto the target system.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### status {#package-attribute-status}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### uninstall_options {#package-attribute-uninstall_options}\n\nAn array of additional options to pass when uninstalling a package. These\noptions are package-specific, and should be documented by the software\nvendor.  For example:\n\n    package { 'VMware Tools':\n      ensure            => absent,\n      uninstall_options => [ { 'REMOVE' => 'Sync,VSS' } ],\n    }\n\nEach option in the array can either be a string or a hash, where each\nkey and value pair are interpreted in a provider specific way.  Each\noption will automatically be quoted when passed to the uninstall\ncommand.\n\nOn Windows, this is the **only** place in Puppet where backslash\nseparators should be used.  Note that backslashes in double-quoted\nstrings _must_ be double-escaped and backslashes in single-quoted\nstrings _may_ be double-escaped.\n\n\n\nRequires features uninstall_options.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### vendor {#package-attribute-vendor}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n### Providers {#package-providers}\n\n#### aix {#package-provider-aix}\n\nInstallation from an AIX software directory, using the AIX `installp`\ncommand.  The `source` parameter is required for this provider, and should\nbe set to the absolute path (on the puppet agent machine) of a directory\ncontaining one or more BFF package files.\n\nThe `installp` command will generate a table of contents file (named `.toc`)\nin this directory, and the `name` parameter (or resource title) that you\nspecify for your `package` resource must match a package name that exists\nin the `.toc` file.\n\nNote that package downgrades are *not* supported; if your resource specifies\na specific version number and there is already a newer version of the package\ninstalled on the machine, the resource will fail with an error message.\n\n* Required binaries: `/usr/bin/lslpp`, `/usr/sbin/installp`.\n* Default for `os.name` == `aix`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### appdmg {#package-provider-appdmg}\n\nPackage management which copies application bundles to a target.\n\n* Required binaries: `/usr/bin/curl`, `/usr/bin/ditto`, `/usr/bin/hdiutil`.\n* Supported features: `installable`.\n\n#### apple {#package-provider-apple}\n\nPackage management based on OS X's built-in packaging system.  This is\nessentially the simplest and least functional package system in existence --\nit only supports installation; no deletion or upgrades.  The provider will\nautomatically add the `.pkg` extension, so leave that off when specifying\nthe package name.\n\n* Required binaries: `/usr/sbin/installer`.\n* Supported features: `installable`.\n\n#### apt {#package-provider-apt}\n\nPackage management via `apt-get`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to apt-get.\nThese options should be specified as an array where each element is either a\n string or a hash.\n\n* Required binaries: `/usr/bin/apt-cache`, `/usr/bin/apt-get`, `/usr/bin/apt-mark`, `/usr/bin/debconf-set-selections`.\n* Default for `os.family` == `debian`.\n* Supported features: `holdable`, `install_options`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `version_ranges`, `versionable`, `virtual_packages`.\n\n#### aptitude {#package-provider-aptitude}\n\nPackage management via `aptitude`.\n\n* Required binaries: `/usr/bin/apt-cache`, `/usr/bin/aptitude`.\n* Supported features: `holdable`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### aptrpm {#package-provider-aptrpm}\n\nPackage management via `apt-get` ported to `rpm`.\n\n* Required binaries: `apt-cache`, `apt-get`, `rpm`.\n* Supported features: `installable`, `purgeable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### blastwave {#package-provider-blastwave}\n\nPackage management using Blastwave.org's `pkg-get` command on Solaris.\n\n* Required binaries: `pkg-get`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`.\n\n#### dnf {#package-provider-dnf}\n\nSupport via `dnf`.\n\nUsing this provider's `uninstallable` feature will not remove dependent packages. To\nremove dependent packages with this provider use the `purgeable` feature, but note this\nfeature is destructive and should be used with the utmost care.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to dnf.\nThese options should be specified as an array where each element is either\n a string or a hash.\n\n* Required binaries: `dnf`, `rpm`.\n* Default for `os.name` == `fedora`. Default for `os.family` == `redhat`. Default for `os.name` == `amazon` and `os.release.major` == `2023`.\n* Supported features: `install_only`, `install_options`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `version_ranges`, `versionable`, `virtual_packages`.\n\n#### dnfmodule {#package-provider-dnfmodule}\n\n* Required binaries: `/usr/bin/dnf`.\n* Supported features: `disableable`, `installable`, `purgeable`, `supports_flavors`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### dpkg {#package-provider-dpkg}\n\nPackage management via `dpkg`.  Because this only uses `dpkg`\nand not `apt`, you must specify the source of any packages you want\nto manage.\n\n* Required binaries: `/usr/bin/dpkg-deb`, `/usr/bin/dpkg-query`, `/usr/bin/dpkg`.\n* Supported features: `holdable`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `virtual_packages`.\n\n#### fink {#package-provider-fink}\n\nPackage management via `fink`.\n\n* Required binaries: `/sw/bin/apt-cache`, `/sw/bin/apt-get`, `/sw/bin/dpkg-query`, `/sw/bin/fink`.\n* Supported features: `holdable`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### freebsd {#package-provider-freebsd}\n\nThe specific form of package management on FreeBSD.  This is an\nextremely quirky packaging system, in that it freely mixes between\nports and packages.  Apparently all of the tools are written in Ruby,\nso there are plans to rewrite this support to directly use those\nlibraries.\n\n* Required binaries: `/usr/sbin/pkg_add`, `/usr/sbin/pkg_delete`, `/usr/sbin/pkg_info`.\n* Supported features: `installable`, `purgeable`, `uninstallable`, `upgradeable`.\n\n#### gem {#package-provider-gem}\n\nRuby Gem support. If a URL is passed via `source`, then that URL is\nappended to the list of remote gem repositories; to ensure that only the\nspecified source is used, also pass `--clear-sources` via `install_options`.\nIf source is present but is not a valid URL, it will be interpreted as the\npath to a local gem file. If source is not present, the gem will be\ninstalled from the default gem repositories. Note that to modify this for Windows, it has to be a valid URL.\n\nThis provider supports the `install_options` and `uninstall_options` attributes,\nwhich allow command-line flags to be passed to the gem command.\nThese options should be specified as an array where each element is either a\nstring or a hash.\n\n* Required binaries: `gem`.\n* Supported features: `install_options`, `installable`, `targetable`, `uninstall_options`, `uninstallable`, `upgradeable`, `version_ranges`, `versionable`.\n\n#### hpux {#package-provider-hpux}\n\nHP-UX's packaging system.\n\n* Required binaries: `/usr/sbin/swinstall`, `/usr/sbin/swlist`, `/usr/sbin/swremove`.\n* Default for `os.name` == `hp-ux`.\n* Supported features: `installable`, `uninstallable`.\n\n#### macports {#package-provider-macports}\n\nPackage management using MacPorts on OS X.\n\nSupports MacPorts versions and revisions, but not variants.\nVariant preferences may be specified using\n[the MacPorts variants.conf file](http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf).\n\nWhen specifying a version in the Puppet DSL, only specify the version, not the revision.\nRevisions are only used internally for ensuring the latest version/revision of a port.\n\n* Required binaries: `/opt/local/bin/port`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### nim {#package-provider-nim}\n\nInstallation from an AIX NIM LPP source.  The `source` parameter is required\nfor this provider, and should specify the name of a NIM `lpp_source` resource\nthat is visible to the puppet agent machine.  This provider supports the\nmanagement of both BFF/installp and RPM packages.\n\nNote that package downgrades are *not* supported; if your resource specifies\na specific version number and there is already a newer version of the package\ninstalled on the machine, the resource will fail with an error message.\n\n* Required binaries: `/usr/bin/lslpp`, `/usr/sbin/nimclient`, `rpm`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### openbsd {#package-provider-openbsd}\n\nOpenBSD's form of `pkg_add` support.\n\nThis provider supports the `install_options` and `uninstall_options`\nattributes, which allow command-line flags to be passed to pkg_add and pkg_delete.\nThese options should be specified as an array where each element is either a\n string or a hash.\n\n* Required binaries: `pkg_add`, `pkg_delete`, `pkg_info`.\n* Default for `os.name` == `openbsd`.\n* Supported features: `install_options`, `installable`, `purgeable`, `supports_flavors`, `uninstall_options`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### opkg {#package-provider-opkg}\n\nOpkg packaging support. Common on OpenWrt and OpenEmbedded platforms\n\n* Required binaries: `opkg`.\n* Default for `os.name` == `openwrt`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`.\n\n#### pacman {#package-provider-pacman}\n\nSupport for the Package Manager Utility (pacman) used in Archlinux.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pacman.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/pacman`.\n* Default for `os.name` == `archlinux, manjarolinux, artix`.\n* Supported features: `install_options`, `installable`, `purgeable`, `uninstall_options`, `uninstallable`, `upgradeable`, `virtual_packages`.\n\n#### pip {#package-provider-pip}\n\nPython packages via `pip`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pip.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Supported features: `install_options`, `installable`, `targetable`, `uninstallable`, `upgradeable`, `version_ranges`, `versionable`.\n\n#### pip2 {#package-provider-pip2}\n\nPython packages via `pip2`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pip2.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Supported features: `install_options`, `installable`, `targetable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### pip3 {#package-provider-pip3}\n\nPython packages via `pip3`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pip3.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Supported features: `install_options`, `installable`, `targetable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### pkg {#package-provider-pkg}\n\nOpenSolaris image packaging system. See pkg(5) for more information.\n\nThis provider supports the `install_options` attribute, which allows\ncommand-line flags to be passed to pkg. These options should be specified as an\narray where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/pkg`.\n* Default for `kernelrelease` == `5.11, 5.12` and `os.family` == `solaris`.\n* Supported features: `holdable`, `install_options`, `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### pkgdmg {#package-provider-pkgdmg}\n\nPackage management based on Apple's Installer.app and DiskUtility.app.\n\nThis provider works by checking the contents of a DMG image for Apple pkg or\nmpkg files. Any number of pkg or mpkg files may exist in the root directory\nof the DMG file system, and Puppet will install all of them. Subdirectories\nare not checked for packages.\n\nThis provider can also accept plain .pkg (but not .mpkg) files in addition\nto .dmg files.\n\nNotes:\n\n* The `source` attribute is mandatory. It must be either a local disk path\n  or an HTTP, HTTPS, or FTP URL to the package.\n* The `name` of the resource must be the filename (without path) of the DMG file.\n* When installing the packages from a DMG, this provider writes a file to\n  disk at `/var/db/.puppet_pkgdmg_installed_NAME`. If that file is present,\n  Puppet assumes all packages from that DMG are already installed.\n* This provider is not versionable and uses DMG filenames to determine\n  whether a package has been installed. Thus, to install new a version of a\n  package, you must create a new DMG with a different filename.\n\n* Required binaries: `/usr/bin/curl`, `/usr/bin/hdiutil`, `/usr/sbin/installer`.\n* Default for `os.name` == `darwin`.\n* Supported features: `installable`.\n\n#### pkgin {#package-provider-pkgin}\n\nPackage management using pkgin, a binary package manager for pkgsrc.\n\n* Required binaries: `pkgin`.\n* Default for `os.name` == `smartos, netbsd`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### pkgng {#package-provider-pkgng}\n\nA PkgNG provider for FreeBSD and DragonFly.\n\n* Required binaries: `/usr/local/sbin/pkg`.\n* Default for `os.name` == `freebsd, dragonfly`.\n* Supported features: `install_options`, `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### pkgutil {#package-provider-pkgutil}\n\nPackage management using Peter Bonivart's ``pkgutil`` command on Solaris.\n\n* Required binaries: `pkgutil`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`.\n\n#### portage {#package-provider-portage}\n\nProvides packaging support for Gentoo's portage system.\n\nThis provider supports the `install_options` and `uninstall_options` attributes, which allows command-line\nflags to be passed to emerge. These options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/eix-update`, `/usr/bin/eix`, `/usr/bin/emerge`, `/usr/bin/qatom`.\n* Default for `os.family` == `gentoo`.\n* Supported features: `install_options`, `installable`, `purgeable`, `reinstallable`, `uninstall_options`, `uninstallable`, `upgradeable`, `versionable`, `virtual_packages`.\n\n#### ports {#package-provider-ports}\n\nSupport for FreeBSD's ports.  Note that this, too, mixes packages and ports.\n\n* Required binaries: `/usr/local/sbin/pkg_deinstall`, `/usr/local/sbin/portupgrade`, `/usr/local/sbin/portversion`, `/usr/sbin/pkg_info`.\n* Supported features: `installable`, `purgeable`, `uninstallable`, `upgradeable`.\n\n#### portupgrade {#package-provider-portupgrade}\n\nSupport for FreeBSD's ports using the portupgrade ports management software.\nUse the port's full origin as the resource name. eg (ports-mgmt/portupgrade)\nfor the portupgrade port.\n\n* Required binaries: `/usr/local/sbin/pkg_deinstall`, `/usr/local/sbin/portinstall`, `/usr/local/sbin/portupgrade`, `/usr/local/sbin/portversion`, `/usr/sbin/pkg_info`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`.\n\n#### puppet_gem {#package-provider-puppet_gem}\n\nPuppet Ruby Gem support. This provider is useful for managing\ngems needed by the ruby provided in the puppet-agent package.\n\n* Required binaries: `/opt/puppetlabs/puppet/bin/gem`.\n* Supported features: `install_options`, `installable`, `uninstall_options`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### puppetserver_gem {#package-provider-puppetserver_gem}\n\nPuppet Server Ruby Gem support. If a URL is passed via `source`, then\nthat URL is appended to the list of remote gem repositories which by default\ncontains rubygems.org; To ensure that only the specified source is used also\npass `--clear-sources` in via `install_options`; if a source is present but\nis not a valid URL, it will be interpreted as the path to a local gem file.\nIf source is not present at all, the gem will be installed from the default\ngem repositories.\n\n* Required binaries: `/opt/puppetlabs/bin/puppetserver`.\n* Supported features: `install_options`, `installable`, `uninstall_options`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### rpm {#package-provider-rpm}\n\nRPM packaging support; should work anywhere with a working `rpm`\nbinary.\n\nThis provider supports the `install_options` and `uninstall_options`\nattributes, which allow command-line flags to be passed to rpm.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `rpm`.\n* Supported features: `install_only`, `install_options`, `installable`, `uninstall_options`, `uninstallable`, `upgradeable`, `versionable`, `virtual_packages`.\n\n#### rug {#package-provider-rug}\n\nSupport for suse `rug` package manager.\n\n* Required binaries: `/usr/bin/rug`, `rpm`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### sun {#package-provider-sun}\n\nSun's packaging system.  Requires that you specify the source for\nthe packages you're managing.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pkgadd.\nThese options should be specified as an array where each element is either a string\n or a hash.\n\n* Required binaries: `/usr/bin/pkginfo`, `/usr/sbin/pkgadd`, `/usr/sbin/pkgrm`.\n* Default for `os.family` == `solaris`.\n* Supported features: `install_options`, `installable`, `uninstallable`, `upgradeable`.\n\n#### sunfreeware {#package-provider-sunfreeware}\n\nPackage management using sunfreeware.com's `pkg-get` command on Solaris.\nAt this point, support is exactly the same as `blastwave` support and\nhas not actually been tested.\n\n* Required binaries: `pkg-get`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`.\n\n#### tdnf {#package-provider-tdnf}\n\nSupport via `tdnf`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to tdnf.\nThese options should be spcified as a string (e.g. '--flag'), a hash (e.g. {'--flag' => 'value'}), or an\narray where each element is either a string or a hash.\n\n* Required binaries: `rpm`, `tdnf`.\n* Default for `os.name` == `PhotonOS`.\n* Supported features: `install_options`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `versionable`, `virtual_packages`.\n\n#### up2date {#package-provider-up2date}\n\nSupport for Red Hat's proprietary `up2date` package update\nmechanism.\n\n* Required binaries: `/usr/sbin/up2date-nox`.\n* Default for `os.distro.release.full` == `2.1, 3, 4` and `os.family` == `redhat`.\n* Supported features: `installable`, `uninstallable`, `upgradeable`.\n\n#### urpmi {#package-provider-urpmi}\n\nSupport via `urpmi`.\n\n* Required binaries: `rpm`, `urpme`, `urpmi`, `urpmq`.\n* Default for `os.name` == `mandriva, mandrake`.\n* Supported features: `installable`, `purgeable`, `uninstallable`, `upgradeable`, `versionable`.\n\n#### windows {#package-provider-windows}\n\nWindows package management.\n\nThis provider supports either MSI or self-extracting executable installers.\n\nThis provider requires a `source` attribute when installing the package.\nIt accepts paths to local files, mapped drives, or UNC paths.\n\nThis provider supports the `install_options` and `uninstall_options`\nattributes, which allow command-line flags to be passed to the installer.\nThese options should be specified as an array where each element is either\na string or a hash.\n\nIf the executable requires special arguments to perform a silent install or\nuninstall, then the appropriate arguments should be specified using the\n`install_options` or `uninstall_options` attributes, respectively.  Puppet\nwill automatically quote any option that contains spaces.\n\n* Default for `os.name` == `windows`.\n* Supported features: `install_options`, `installable`, `uninstall_options`, `uninstallable`, `versionable`.\n\n#### xbps {#package-provider-xbps}\n\nSupport for the Package Manager Utility (xbps) used in VoidLinux.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to xbps-install.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/xbps-install`, `/usr/bin/xbps-pkgdb`, `/usr/bin/xbps-query`, `/usr/bin/xbps-remove`.\n* Default for `os.name` == `void`.\n* Supported features: `holdable`, `install_options`, `installable`, `uninstall_options`, `uninstallable`, `upgradeable`, `virtual_packages`.\n\n#### yum {#package-provider-yum}\n\nSupport via `yum`.\n\nUsing this provider's `uninstallable` feature will not remove dependent packages. To\nremove dependent packages with this provider use the `purgeable` feature, but note this\nfeature is destructive and should be used with the utmost care.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to yum.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `rpm`, `yum`.\n* Default for `os.name` == `amazon`. Default for `os.family` == `redhat` and `os.release.major` == `4, 5, 6, 7`.\n* Supported features: `install_only`, `install_options`, `installable`, `purgeable`, `uninstallable`, `upgradeable`, `version_ranges`, `versionable`, `virtual_packages`.\n\n#### zypper {#package-provider-zypper}\n\nSupport for SuSE `zypper` package manager. Found in SLES10sp2+ and SLES11.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to zypper.\nThese options should be specified as an array where each element is either a\nstring or a hash.\n\n* Required binaries: `/usr/bin/zypper`.\n* Default for `os.name` == `suse, sles, sled, opensuse`.\n* Supported features: `install_options`, `installable`, `uninstallable`, `upgradeable`, `versionable`, `virtual_packages`.\n\n### Provider Features {#package-provider-features}\n\nAvailable features:\n\n* `disableable` --- The provider can disable packages. This feature is used by specifying `disabled` as the desired value for the package.\n* `holdable` --- The provider is capable of placing packages on hold such that they are not automatically upgraded as a result of other package dependencies unless explicit action is taken by a user or another package.\n* `install_only` --- The provider accepts options to only install packages never update (kernels, etc.)\n* `install_options` --- The provider accepts options to be passed to the installer command.\n* `installable` --- The provider can install packages.\n* `package_settings` --- The provider accepts package_settings to be ensured for the given package. The meaning and format of these settings is provider-specific.\n* `purgeable` --- The provider can purge packages.  This generally means that all traces of the package are removed, including existing configuration files.  This feature is thus destructive and should be used with the utmost care.\n* `reinstallable` --- The provider can reinstall packages.\n* `supports_flavors` --- The provider accepts flavors, which are specific variants of packages.\n* `targetable` --- The provider accepts a targeted package management command.\n* `uninstall_options` --- The provider accepts options to be passed to the uninstaller command.\n* `uninstallable` --- The provider can uninstall packages.\n* `upgradeable` --- The provider can upgrade to the latest version of a package.  This feature is used by specifying `latest` as the desired value for the package.\n* `version_ranges` --- The provider can ensure version ranges.\n* `versionable` --- The provider is capable of interrogating the package database for installed version(s), and can select which out of a set of available versions of a package to install if asked.\n* `virtual_packages` --- The provider accepts virtual package names for install and uninstall.\n\nProvider support:\n\n* **aix** - _installable, uninstallable, upgradeable, versionable_\n* **appdmg** - _installable_\n* **apple** - _installable_\n* **apt** - _holdable, install options, installable, purgeable, uninstallable, upgradeable, version ranges, versionable, virtual packages_\n* **aptitude** - _holdable, installable, purgeable, uninstallable, upgradeable, versionable_\n* **aptrpm** - _installable, purgeable, uninstallable, upgradeable, versionable_\n* **blastwave** - _installable, uninstallable, upgradeable_\n* **dnf** - _install only, install options, installable, purgeable, uninstallable, upgradeable, version ranges, versionable, virtual packages_\n* **dnfmodule** - _disableable, installable, purgeable, supports flavors, uninstallable, upgradeable, versionable_\n* **dpkg** - _holdable, installable, purgeable, uninstallable, upgradeable, virtual packages_\n* **fink** - _holdable, installable, purgeable, uninstallable, upgradeable, versionable_\n* **freebsd** - _installable, purgeable, uninstallable, upgradeable_\n* **gem** - _install options, installable, targetable, uninstall options, uninstallable, upgradeable, version ranges, versionable_\n* **hpux** - _installable, uninstallable_\n* **macports** - _installable, uninstallable, upgradeable, versionable_\n* **nim** - _installable, uninstallable, upgradeable, versionable_\n* **openbsd** - _install options, installable, purgeable, supports flavors, uninstall options, uninstallable, upgradeable, versionable_\n* **opkg** - _installable, uninstallable, upgradeable_\n* **pacman** - _install options, installable, purgeable, uninstall options, uninstallable, upgradeable, virtual packages_\n* **pip** - _install options, installable, targetable, uninstallable, upgradeable, version ranges, versionable_\n* **pip2** - _install options, installable, targetable, uninstallable, upgradeable, versionable_\n* **pip3** - _install options, installable, targetable, uninstallable, upgradeable, versionable_\n* **pkg** - _holdable, install options, installable, uninstallable, upgradeable, versionable_\n* **pkgdmg** - _installable_\n* **pkgin** - _installable, uninstallable, upgradeable, versionable_\n* **pkgng** - _install options, installable, uninstallable, upgradeable, versionable_\n* **pkgutil** - _installable, uninstallable, upgradeable_\n* **portage** - _install options, installable, purgeable, reinstallable, uninstall options, uninstallable, upgradeable, versionable, virtual packages_\n* **ports** - _installable, purgeable, uninstallable, upgradeable_\n* **portupgrade** - _installable, uninstallable, upgradeable_\n* **puppet_gem** - _install options, installable, uninstall options, uninstallable, upgradeable, versionable_\n* **puppetserver_gem** - _install options, installable, uninstall options, uninstallable, upgradeable, versionable_\n* **rpm** - _install only, install options, installable, uninstall options, uninstallable, upgradeable, versionable, virtual packages_\n* **rug** - _installable, uninstallable, upgradeable, versionable_\n* **sun** - _install options, installable, uninstallable, upgradeable_\n* **sunfreeware** - _installable, uninstallable, upgradeable_\n* **tdnf** - _install options, installable, purgeable, uninstallable, upgradeable, versionable, virtual packages_\n* **up2date** - _installable, uninstallable, upgradeable_\n* **urpmi** - _installable, purgeable, uninstallable, upgradeable, versionable_\n* **windows** - _install options, installable, uninstall options, uninstallable, versionable_\n* **xbps** - _holdable, install options, installable, uninstall options, uninstallable, upgradeable, virtual packages_\n* **yum** - _install only, install options, installable, purgeable, uninstallable, upgradeable, version ranges, versionable, virtual packages_\n* **zypper** - _install options, installable, uninstallable, upgradeable, versionable, virtual packages_\n  \n\n\n\n\n---------\n\n## resources\n\n* [Attributes](#resources-attributes)\n\n### Description {#resources-description}\n\nThis is a metatype that can manage other resource types.  Any\nmetaparams specified here will be passed on to any generated resources,\nso you can purge unmanaged resources but set `noop` to true so the\npurging is only logged and does not actually happen.\n\n### Attributes {#resources-attributes}\n\n<pre><code>resources { 'resource title':\n  <a href=\"#resources-attribute-name\">name</a>               =&gt; <em># <strong>(namevar)</strong> The name of the type to be...</em>\n  <a href=\"#resources-attribute-purge\">purge</a>              =&gt; <em># Whether to purge unmanaged resources.  When set...</em>\n  <a href=\"#resources-attribute-unless_system_user\">unless_system_user</a> =&gt; <em># This keeps system users from being purged.  By...</em>\n  <a href=\"#resources-attribute-unless_uid\">unless_uid</a>         =&gt; <em># This keeps specific uids or ranges of uids from...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#resources-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the type to be managed.\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n#### purge {#resources-attribute-purge}\n\nWhether to purge unmanaged resources.  When set to `true`, this will\ndelete any resource that is not specified in your configuration and is not\nautorequired by any managed resources. **Note:** The `ssh_authorized_key`\nresource type can't be purged this way; instead, see the `purge_ssh_keys`\nattribute of the `user` type.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n#### unless_system_user {#resources-attribute-unless_system_user}\n\nThis keeps system users from being purged.  By default, it\ndoes not purge users whose UIDs are less than the minimum UID for the system (typically 500 or 1000), but you can specify\na different UID as the inclusive limit.\n\nValid values are `true`, `false`. Values can match `/^\\d+$/`.\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n#### unless_uid {#resources-attribute-unless_uid}\n\nThis keeps specific uids or ranges of uids from being purged when purge is true.\nAccepts integers, integer strings, and arrays of integers or integer strings.\nTo specify a range of uids, consider using the range() function from stdlib.\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n\n\n\n---------\n\n## schedule\n\n* [Attributes](#schedule-attributes)\n\n### Description {#schedule-description}\n\nDefine schedules for Puppet. Resources can be limited to a schedule by using the\n[`schedule`](https://puppet.com/docs/puppet/latest/metaparameter.html#schedule)\nmetaparameter.\n\nCurrently, **schedules can only be used to stop a resource from being\napplied;** they cannot cause a resource to be applied when it otherwise\nwouldn't be, and they cannot accurately specify a time when a resource\nshould run.\n\nEvery time Puppet applies its configuration, it will apply the\nset of resources whose schedule does not eliminate them from\nrunning right then, but there is currently no system in place to\nguarantee that a given resource runs at a given time.  If you\nspecify a very  restrictive schedule and Puppet happens to run at a\ntime within that schedule, then the resources will get applied;\notherwise, that work may never get done.\n\nThus, it is advisable to use wider scheduling (for example, over a couple\nof hours) combined with periods and repetitions.  For instance, if you\nwanted to restrict certain resources to only running once, between\nthe hours of two and 4 AM, then you would use this schedule:\n\n    schedule { 'maint':\n      range  => '2 - 4',\n      period => daily,\n      repeat => 1,\n    }\n\nWith this schedule, the first time that Puppet runs between 2 and 4 AM,\nall resources with this schedule will get applied, but they won't\nget applied again between 2 and 4 because they will have already\nrun once that day, and they won't get applied outside that schedule\nbecause they will be outside the scheduled range.\n\nPuppet automatically creates a schedule for each of the valid periods\nwith the same name as that period (such as hourly and daily).\nAdditionally, a schedule named `puppet` is created and used as the\ndefault, with the following attributes:\n\n    schedule { 'puppet':\n      period => hourly,\n      repeat => 2,\n    }\n\nThis will cause resources to be applied every 30 minutes by default.\n\nThe `statettl` setting on the agent affects the ability of a schedule to\ndetermine if a resource has already been checked. If the `statettl` is\nset lower than the span of the associated schedule resource, then a\nresource could be checked & applied multiple times in the schedule as\nthe information about when the resource was last checked will have\nexpired from the cache.\n\n### Attributes {#schedule-attributes}\n\n<pre><code>schedule { 'resource title':\n  <a href=\"#schedule-attribute-name\">name</a>        =&gt; <em># <strong>(namevar)</strong> The name of the schedule.  This name is used...</em>\n  <a href=\"#schedule-attribute-period\">period</a>      =&gt; <em># The period of repetition for resources on this...</em>\n  <a href=\"#schedule-attribute-periodmatch\">periodmatch</a> =&gt; <em># Whether periods should be matched by a numeric...</em>\n  <a href=\"#schedule-attribute-range\">range</a>       =&gt; <em># The earliest and latest that a resource can be...</em>\n  <a href=\"#schedule-attribute-repeat\">repeat</a>      =&gt; <em># How often a given resource may be applied in...</em>\n  <a href=\"#schedule-attribute-weekday\">weekday</a>     =&gt; <em># The days of the week in which the schedule...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#schedule-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the schedule.  This name is used when assigning the schedule\nto a resource with the `schedule` metaparameter:\n\n    schedule { 'everyday':\n      period => daily,\n      range  => '2 - 4',\n    }\n\n    exec { '/usr/bin/apt-get update':\n      schedule => 'everyday',\n    }\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### period {#schedule-attribute-period}\n\nThe period of repetition for resources on this schedule. The default is\nfor resources to get applied every time Puppet runs.\n\nNote that the period defines how often a given resource will get\napplied but not when; if you would like to restrict the hours\nthat a given resource can be applied (for instance, only at night\nduring a maintenance window), then use the `range` attribute.\n\nIf the provided periods are not sufficient, you can provide a\nvalue to the *repeat* attribute, which will cause Puppet to\nschedule the affected resources evenly in the period the\nspecified number of times.  Take this schedule:\n\n    schedule { 'veryoften':\n      period => hourly,\n      repeat => 6,\n    }\n\nThis can cause Puppet to apply that resource up to every 10 minutes.\n\nAt the moment, Puppet cannot guarantee that level of repetition; that\nis, the resource can applied _up to_ every 10 minutes, but internal\nfactors might prevent it from actually running that often (for instance,\nif a Puppet run is still in progress when the next run is scheduled to\nstart, that next run will be suppressed).\n\nSee the `periodmatch` attribute for tuning whether to match\ntimes by their distance apart or by their specific value.\n\n> **Tip**: You can use `period => never,` to prevent a resource from being applied\nin the given `range`. This is useful if you need to create a blackout window to\nperform sensitive operations without interruption.\n\nValid values are `hourly`, `daily`, `weekly`, `monthly`, `never`.\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### periodmatch {#schedule-attribute-periodmatch}\n\nWhether periods should be matched by a numeric value (for instance,\nwhether two times are in the same hour) or by their chronological\ndistance apart (whether two times are 60 minutes apart).\n\nValid values are `number`, `distance`.\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### range {#schedule-attribute-range}\n\nThe earliest and latest that a resource can be applied.  This is\nalways a hyphen-separated range within a 24 hour period, and hours\nmust be specified in numbers between 0 and 23, inclusive.  Minutes and\nseconds can optionally be provided, using the normal colon as a\nseparator. For instance:\n\n    schedule { 'maintenance':\n      range => '1:30 - 4:30',\n    }\n\nThis is mostly useful for restricting certain resources to being\napplied in maintenance windows or during off-peak hours. Multiple\nranges can be applied in array context. As a convenience when specifying\nranges, you can cross midnight (for example, `range => \"22:00 - 04:00\"`).\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### repeat {#schedule-attribute-repeat}\n\nHow often a given resource may be applied in this schedule's `period`.\nMust be an integer.\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### weekday {#schedule-attribute-weekday}\n\nThe days of the week in which the schedule should be valid.\nYou may specify the full day name 'Tuesday', the three character\nabbreviation 'Tue', or a number (as a string or as an integer) corresponding to the day of the\nweek where 0 is Sunday, 1 is Monday, and so on. Multiple days can be specified\nas an array. If not specified, the day of the week will not be\nconsidered in the schedule.\n\nIf you are also using a range match that spans across midnight\nthen this parameter will match the day that it was at the start\nof the range, not necessarily the day that it is when it matches.\nFor example, consider this schedule:\n\n    schedule { 'maintenance_window':\n      range   => '22:00 - 04:00',\n      weekday => 'Saturday',\n    }\n\nThis will match at 11 PM on Saturday and 2 AM on Sunday, but not\nat 2 AM on Saturday.\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n\n\n\n---------\n\n## service\n\n* [Attributes](#service-attributes)\n* [Providers](#service-providers)\n* [Provider Features](#service-provider-features)\n\n### Description {#service-description}\n\nManage running services.  Service support unfortunately varies\nwidely by platform --- some platforms have very little if any concept of a\nrunning service, and some have a very codified and powerful concept.\nPuppet's service support is usually capable of doing the right thing, but\nthe more information you can provide, the better behaviour you will get.\n\nPuppet 2.7 and newer expect init scripts to have a working status command.\nIf this isn't the case for any of your services' init scripts, you will\nneed to set `hasstatus` to false and possibly specify a custom status\ncommand in the `status` attribute. As a last resort, Puppet will attempt to\nsearch the process table by calling whatever command is listed in the `ps`\nfact. The default search pattern is the name of the service, but you can\nspecify it with the `pattern` attribute.\n\n**Refresh:** `service` resources can respond to refresh events (via\n`notify`, `subscribe`, or the `~>` arrow). If a `service` receives an\nevent from another resource, Puppet will restart the service it manages.\nThe actual command used to restart the service depends on the platform and\ncan be configured:\n\n* If you set `hasrestart` to true, Puppet will use the init script's restart command.\n* You can provide an explicit command for restarting with the `restart` attribute.\n* If you do neither, the service's stop and start commands will be used.\n\n### Attributes {#service-attributes}\n\n<pre><code>service { 'resource title':\n  <a href=\"#service-attribute-name\">name</a>          =&gt; <em># <strong>(namevar)</strong> The name of the service to run.  This name is...</em>\n  <a href=\"#service-attribute-ensure\">ensure</a>        =&gt; <em># Whether a service should be running. Default...</em>\n  <a href=\"#service-attribute-binary\">binary</a>        =&gt; <em># The path to the daemon.  This is only used for...</em>\n  <a href=\"#service-attribute-control\">control</a>       =&gt; <em># The control variable used to manage services...</em>\n  <a href=\"#service-attribute-enable\">enable</a>        =&gt; <em># Whether a service should be enabled to start at...</em>\n  <a href=\"#service-attribute-flags\">flags</a>         =&gt; <em># Specify a string of flags to pass to the startup </em>\n  <a href=\"#service-attribute-hasrestart\">hasrestart</a>    =&gt; <em># Specify that an init script has a `restart...</em>\n  <a href=\"#service-attribute-hasstatus\">hasstatus</a>     =&gt; <em># Declare whether the service's init script has a...</em>\n  <a href=\"#service-attribute-logonaccount\">logonaccount</a>  =&gt; <em># Specify an account for service logon    Requires </em>\n  <a href=\"#service-attribute-logonpassword\">logonpassword</a> =&gt; <em># Specify a password for service logon. Default...</em>\n  <a href=\"#service-attribute-manifest\">manifest</a>      =&gt; <em># Specify a command to config a service, or a path </em>\n  <a href=\"#service-attribute-path\">path</a>          =&gt; <em># The search path for finding init scripts....</em>\n  <a href=\"#service-attribute-pattern\">pattern</a>       =&gt; <em># The pattern to search for in the process table...</em>\n  <a href=\"#service-attribute-provider\">provider</a>      =&gt; <em># The specific backend to use for this `service...</em>\n  <a href=\"#service-attribute-restart\">restart</a>       =&gt; <em># Specify a *restart* command manually.  If left...</em>\n  <a href=\"#service-attribute-start\">start</a>         =&gt; <em># Specify a *start* command manually.  Most...</em>\n  <a href=\"#service-attribute-status\">status</a>        =&gt; <em># Specify a *status* command manually.  This...</em>\n  <a href=\"#service-attribute-stop\">stop</a>          =&gt; <em># Specify a *stop* command...</em>\n  <a href=\"#service-attribute-timeout\">timeout</a>       =&gt; <em># Specify an optional minimum timeout (in seconds) </em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#service-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the service to run.\n\nThis name is used to find the service; on platforms where services\nhave short system names and long display names, this should be the\nshort name. (To take an example from Windows, you would use \"wuauserv\"\nrather than \"Automatic Updates.\")\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### ensure {#service-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhether a service should be running. Default values depend on the platform.\n\nValid values are `stopped` (also called `false`), `running` (also called `true`).\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### binary {#service-attribute-binary}\n\nThe path to the daemon.  This is only used for\nsystems that do not support init scripts.  This binary will be\nused to start the service if no `start` parameter is\nprovided.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### control {#service-attribute-control}\n\nThe control variable used to manage services (originally for HP-UX).\nDefaults to the upcased service name plus `START` replacing dots with\nunderscores, for those providers that support the `controllable` feature.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### enable {#service-attribute-enable}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhether a service should be enabled to start at boot.\nThis property behaves differently depending on the platform;\nwherever possible, it relies on local tools to enable or disable\na given service. Default values depend on the platform.\n\nIf you don't specify a value for the `enable` attribute, Puppet leaves\nthat aspect of the service alone and your operating system determines\nthe behavior.\n\nValid values are `true`, `false`, `manual`, `mask`, `delayed`.\n\nRequires features enableable.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### flags {#service-attribute-flags}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify a string of flags to pass to the startup script.\n\n\n\nRequires features flaggable.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### hasrestart {#service-attribute-hasrestart}\n\nSpecify that an init script has a `restart` command.  If this is\nfalse and you do not specify a command in the `restart` attribute,\nthe init script's `stop` and `start` commands will be used.\n\nValid values are `true`, `false`.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### hasstatus {#service-attribute-hasstatus}\n\nDeclare whether the service's init script has a functional status\ncommand. This attribute's default value changed in Puppet 2.7.0.\n\nThe init script's status command must return 0 if the service is\nrunning and a nonzero value otherwise. Ideally, these exit codes\nshould conform to [the LSB's specification][lsb-exit-codes] for init\nscript status actions, but Puppet only considers the difference\nbetween 0 and nonzero to be relevant.\n\nIf a service's init script does not support any kind of status command,\nyou should set `hasstatus` to false and either provide a specific\ncommand using the `status` attribute or expect that Puppet will look for\nthe service name in the process table. Be aware that 'virtual' init\nscripts (like 'network' under Red Hat systems) will respond poorly to\nrefresh events from other resources if you override the default behavior\nwithout providing a status command.\n\nValid values are `true`, `false`.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### logonaccount {#service-attribute-logonaccount}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify an account for service logon\n\n\n\nRequires features manages_logon_credentials.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### logonpassword {#service-attribute-logonpassword}\n\nSpecify a password for service logon. Default value is an empty string (when logonaccount is specified).\n\n\n\nRequires features manages_logon_credentials.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### manifest {#service-attribute-manifest}\n\nSpecify a command to config a service, or a path to a manifest to do so.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### path {#service-attribute-path}\n\nThe search path for finding init scripts.  Multiple values should\nbe separated by colons or provided as an array.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### pattern {#service-attribute-pattern}\n\nThe pattern to search for in the process table.\nThis is used for stopping services on platforms that do not\nsupport init scripts, and is also used for determining service\nstatus on those service whose init scripts do not include a status\ncommand.\n\nDefaults to the name of the service. The pattern can be a simple string\nor any legal Ruby pattern, including regular expressions (which should\nbe quoted without enclosing slashes).\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### provider {#service-attribute-provider}\n\nThe specific backend to use for this `service`\nresource. You will seldom need to specify this --- Puppet will usually\ndiscover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`base`](#service-provider-base)\n* [`bsd`](#service-provider-bsd)\n* [`daemontools`](#service-provider-daemontools)\n* [`debian`](#service-provider-debian)\n* [`freebsd`](#service-provider-freebsd)\n* [`gentoo`](#service-provider-gentoo)\n* [`init`](#service-provider-init)\n* [`launchd`](#service-provider-launchd)\n* [`openbsd`](#service-provider-openbsd)\n* [`openrc`](#service-provider-openrc)\n* [`openwrt`](#service-provider-openwrt)\n* [`rcng`](#service-provider-rcng)\n* [`redhat`](#service-provider-redhat)\n* [`runit`](#service-provider-runit)\n* [`service`](#service-provider-service)\n* [`smf`](#service-provider-smf)\n* [`src`](#service-provider-src)\n* [`systemd`](#service-provider-systemd)\n* [`upstart`](#service-provider-upstart)\n* [`windows`](#service-provider-windows)\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### restart {#service-attribute-restart}\n\nSpecify a *restart* command manually.  If left\nunspecified, the service will be stopped and then started.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### start {#service-attribute-start}\n\nSpecify a *start* command manually.  Most service subsystems\nsupport a `start` command, so this will not need to be\nspecified.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### status {#service-attribute-status}\n\nSpecify a *status* command manually.  This command must\nreturn 0 if the service is running and a nonzero value otherwise.\nIdeally, these exit codes should conform to [the LSB's\nspecification][lsb-exit-codes] for init script status actions, but\nPuppet only considers the difference between 0 and nonzero to be\nrelevant.\n\nIf left unspecified, the status of the service will be determined\nautomatically, usually by looking for the service in the process\ntable.\n\n[lsb-exit-codes]: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### stop {#service-attribute-stop}\n\nSpecify a *stop* command manually.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### timeout {#service-attribute-timeout}\n\nSpecify an optional minimum timeout (in seconds) for puppet to wait when syncing service properties\n\n\n\nRequires features configurable_timeout.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n### Providers {#service-providers}\n\n#### base {#service-provider-base}\n\nThe simplest form of Unix service support.\n\nYou have to specify enough about your service for this to work; the\nminimum you can specify is a binary for starting the process, and this\nsame binary will be searched for in the process table to stop the\nservice.  As with `init`-style services, it is preferable to specify start,\nstop, and status commands.\n\n* Required binaries: `kill`.\n* Supported features: `refreshable`.\n\n#### bsd {#service-provider-bsd}\n\nGeneric BSD form of `init`-style service management with `rc.d`.\n\nUses `rc.conf.d` for service enabling and disabling.\n\n* Supported features: `enableable`, `refreshable`.\n\n#### daemontools {#service-provider-daemontools}\n\nDaemontools service management.\n\nThis provider manages daemons supervised by D.J. Bernstein daemontools.\nWhen detecting the service directory it will check, in order of preference:\n\n* `/service`\n* `/etc/service`\n* `/var/lib/svscan`\n\nThe daemon directory should be in one of the following locations:\n\n* `/var/lib/service`\n* `/etc`\n\n...or this can be overridden in the resource's attributes:\n\n    service { 'myservice':\n      provider => 'daemontools',\n      path     => '/path/to/daemons',\n    }\n\nThis provider supports out of the box:\n\n* start/stop (mapped to enable/disable)\n* enable/disable\n* restart\n* status\n\nIf a service has `ensure => \"running\"`, it will link /path/to/daemon to\n/path/to/service, which will automatically enable the service.\n\nIf a service has `ensure => \"stopped\"`, it will only shut down the service, not\nremove the `/path/to/service` link.\n\n* Required binaries: `/usr/bin/svc`, `/usr/bin/svstat`.\n* Supported features: `enableable`, `refreshable`.\n\n#### debian {#service-provider-debian}\n\nDebian's form of `init`-style management.\n\nThe only differences from `init` are support for enabling and disabling\nservices via `update-rc.d` and the ability to determine enabled status via\n`invoke-rc.d`.\n\n* Required binaries: `/usr/sbin/invoke-rc.d`, `/usr/sbin/service`, `/usr/sbin/update-rc.d`.\n* Default for `os.name` == `cumuluslinux` and `os.release.major` == `1, 2`. Default for `os.name` == `debian` and `os.release.major` == `5, 6, 7`. Default for `os.name` == `devuan`.\n* Supported features: `enableable`, `refreshable`.\n\n#### freebsd {#service-provider-freebsd}\n\nProvider for FreeBSD and DragonFly BSD. Uses the `rcvar` argument of init scripts and parses/edits rc files.\n\n* Default for `os.name` == `freebsd, dragonfly`.\n* Supported features: `enableable`, `refreshable`.\n\n#### gentoo {#service-provider-gentoo}\n\nGentoo's form of `init`-style service management.\n\nUses `rc-update` for service enabling and disabling.\n\n* Required binaries: `/sbin/rc-update`.\n* Supported features: `enableable`, `refreshable`.\n\n#### init {#service-provider-init}\n\nStandard `init`-style service management.\n\n* Supported features: `refreshable`.\n\n#### launchd {#service-provider-launchd}\n\nThis provider manages jobs with `launchd`, which is the default service\nframework for Mac OS X (and may be available for use on other platforms).\n\nFor more information, see the `launchd` man page:\n\n* <https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man8/launchd.8.html>\n\nThis provider reads plists out of the following directories:\n\n* `/System/Library/LaunchDaemons`\n* `/System/Library/LaunchAgents`\n* `/Library/LaunchDaemons`\n* `/Library/LaunchAgents`\n\n...and builds up a list of services based upon each plist's \"Label\" entry.\n\nThis provider supports:\n\n* ensure => running/stopped,\n* enable => true/false\n* status\n* restart\n\nHere is how the Puppet states correspond to `launchd` states:\n\n* stopped --- job unloaded\n* started --- job loaded\n* enabled --- 'Disable' removed from job plist file\n* disabled --- 'Disable' added to job plist file\n\nNote that this allows you to do something `launchctl` can't do, which is to\nbe in a state of \"stopped/enabled\" or \"running/disabled\".\n\nNote that this provider does not support overriding 'restart'\n\n* Required binaries: `/bin/launchctl`.\n* Default for `os.name` == `darwin`.\n* Supported features: `enableable`, `refreshable`.\n\n#### openbsd {#service-provider-openbsd}\n\nProvider for OpenBSD's rc.d daemon control scripts\n\n* Required binaries: `/usr/sbin/rcctl`.\n* Default for `os.name` == `openbsd`.\n* Supported features: `enableable`, `flaggable`, `refreshable`.\n\n#### openrc {#service-provider-openrc}\n\nSupport for Gentoo's OpenRC initskripts\n\nUses rc-update, rc-status and rc-service to manage services.\n\n* Required binaries: `/bin/rc-status`, `/sbin/rc-service`, `/sbin/rc-update`.\n* Default for `os.name` == `gentoo`. Default for `os.name` == `funtoo`.\n* Supported features: `enableable`, `refreshable`.\n\n#### openwrt {#service-provider-openwrt}\n\nSupport for OpenWrt flavored init scripts.\n\nUses /etc/init.d/service_name enable, disable, and enabled.\n\n* Default for `os.name` == `openwrt`.\n* Supported features: `enableable`, `refreshable`.\n\n#### rcng {#service-provider-rcng}\n\nRCng service management with rc.d\n\n* Default for `os.name` == `netbsd, cargos`.\n* Supported features: `enableable`, `refreshable`.\n\n#### redhat {#service-provider-redhat}\n\nRed Hat's (and probably many others') form of `init`-style service\nmanagement. Uses `chkconfig` for service enabling and disabling.\n\n* Required binaries: `/sbin/chkconfig`, `/sbin/service`.\n* Default for `os.name` == `amazon` and `os.release.major` == `2017, 2018`. Default for `os.name` == `redhat` and `os.release.major` == `4, 5, 6`. Default for `os.family` == `suse` and `os.release.major` == `10, 11`.\n* Supported features: `enableable`, `refreshable`.\n\n#### runit {#service-provider-runit}\n\nRunit service management.\n\nThis provider manages daemons running supervised by Runit.\nWhen detecting the service directory it will check, in order of preference:\n\n* `/service`\n* `/etc/service`\n* `/var/service`\n\nThe daemon directory should be in one of the following locations:\n\n* `/etc/sv`\n* `/var/lib/service`\n\nor this can be overridden in the service resource parameters:\n\n    service { 'myservice':\n      provider => 'runit',\n      path     => '/path/to/daemons',\n    }\n\nThis provider supports out of the box:\n\n* start/stop\n* enable/disable\n* restart\n* status\n\n* Required binaries: `/usr/bin/sv`.\n* Supported features: `enableable`, `refreshable`.\n\n#### service {#service-provider-service}\n\nThe simplest form of service support.\n\n* Supported features: `refreshable`.\n\n#### smf {#service-provider-smf}\n\nSupport for Sun's new Service Management Framework.\n\nWhen managing the enable property, this provider will try to preserve\nthe previous ensure state per the enableable semantics. On Solaris,\nenabling a service starts it up while disabling a service stops it. Thus,\nthere's a chance for this provider to execute two operations when managing\nthe enable property. For example, if enable is set to true and the ensure\nstate is stopped, this provider will manage the service using two operations:\none to enable the service which will start it up, and another to stop the\nservice (without affecting its enabled status).\n\nBy specifying `manifest => \"/path/to/service.xml\"`, the SMF manifest will\nbe imported if it does not exist.\n\n* Required binaries: `/usr/bin/svcs`, `/usr/sbin/svcadm`, `/usr/sbin/svccfg`.\n* Default for `os.family` == `solaris`.\n* Supported features: `enableable`, `refreshable`.\n\n#### src {#service-provider-src}\n\nSupport for AIX's System Resource controller.\n\nServices are started/stopped based on the `stopsrc` and `startsrc`\ncommands, and some services can be refreshed with `refresh` command.\n\nEnabling and disabling services is not supported, as it requires\nmodifications to `/etc/inittab`. Starting and stopping groups of subsystems\nis not yet supported.\n\n* Required binaries: `/usr/bin/lssrc`, `/usr/bin/refresh`, `/usr/bin/startsrc`, `/usr/bin/stopsrc`, `/usr/sbin/chitab`, `/usr/sbin/lsitab`, `/usr/sbin/mkitab`, `/usr/sbin/rmitab`.\n* Default for `os.name` == `aix`.\n* Supported features: `enableable`, `refreshable`.\n\n#### systemd {#service-provider-systemd}\n\nManages `systemd` services using `systemctl`.\n\nBecause `systemd` defaults to assuming the `.service` unit type, the suffix\nmay be omitted.  Other unit types (such as `.path`) may be managed by\nproviding the proper suffix.\n\n* Required binaries: `systemctl`.\n* Default for `os.family` == `archlinux`. Default for `os.family` == `redhat`. Default for `os.family` == `redhat` and `os.name` == `fedora`. Default for `os.family` == `suse`. Default for `os.family` == `coreos`. Default for `os.family` == `gentoo`. Default for `os.name` == `amazon` and `os.release.major` == `2, 2023`. Default for `os.name` == `debian`. Default for `os.name` == `LinuxMint`. Default for `os.name` == `ubuntu`. Default for `os.name` == `cumuluslinux` and `os.release.major` == `3, 4`. Default for `os.name` == `raspbian` and `os.release.major` == `12`.\n* Supported features: `enableable`, `maskable`, `refreshable`.\n\n#### upstart {#service-provider-upstart}\n\nUbuntu service management with `upstart`.\n\nThis provider manages `upstart` jobs on Ubuntu. For `upstart` documentation,\nsee <http://upstart.ubuntu.com/>.\n\n* Required binaries: `/sbin/initctl`, `/sbin/restart`, `/sbin/start`, `/sbin/status`, `/sbin/stop`.\n* Default for `os.name` == `ubuntu` and `os.release.major` == `10.04, 12.04, 14.04, 14.10`. Default for `os.name` == `LinuxMint` and `os.release.major` == `10, 11, 12, 13, 14, 15, 16, 17`.\n* Supported features: `enableable`, `refreshable`.\n\n#### windows {#service-provider-windows}\n\nSupport for Windows Service Control Manager (SCM). This provider can\nstart, stop, enable, and disable services, and the SCM provides working\nstatus methods for all services.\n\nControl of service groups (dependencies) is not yet supported, nor is running\nservices as a specific user.\n\n* Default for `os.name` == `windows`.\n* Supported features: `configurable_timeout`, `delayed_startable`, `enableable`, `manages_logon_credentials`, `manual_startable`, `refreshable`.\n\n### Provider Features {#service-provider-features}\n\nAvailable features:\n\n* `configurable_timeout` --- The provider can specify a minumum timeout for syncing service properties\n* `controllable` --- The provider uses a control variable.\n* `delayed_startable` --- The provider can set service to delayed start\n* `enableable` --- The provider can enable and disable the service.\n* `flaggable` --- The provider can pass flags to the service.\n* `manages_logon_credentials` --- The provider can specify the logon credentials used for a service\n* `manual_startable` --- The provider can set service to manual start\n* `maskable` --- The provider can 'mask' the service.\n* `refreshable` --- The provider can restart the service.\n\nProvider support:\n\n* **base** - _refreshable_\n* **bsd** - _enableable, refreshable_\n* **daemontools** - _enableable, refreshable_\n* **debian** - _enableable, refreshable_\n* **freebsd** - _enableable, refreshable_\n* **gentoo** - _enableable, refreshable_\n* **init** - _refreshable_\n* **launchd** - _enableable, refreshable_\n* **openbsd** - _enableable, flaggable, refreshable_\n* **openrc** - _enableable, refreshable_\n* **openwrt** - _enableable, refreshable_\n* **rcng** - _enableable, refreshable_\n* **redhat** - _enableable, refreshable_\n* **runit** - _enableable, refreshable_\n* **service** - _refreshable_\n* **smf** - _enableable, refreshable_\n* **src** - _enableable, refreshable_\n* **systemd** - _enableable, maskable, refreshable_\n* **upstart** - _enableable, refreshable_\n* **windows** - _configurable timeout, delayed startable, enableable, manages logon credentials, manual startable, refreshable_\n  \n\n\n\n\n---------\n\n## stage\n\n* [Attributes](#stage-attributes)\n\n### Description {#stage-description}\n\nA resource type for creating new run stages.  Once a stage is available,\nclasses can be assigned to it by declaring them with the resource-like syntax\nand using\n[the `stage` metaparameter](https://puppet.com/docs/puppet/latest/metaparameter.html#stage).\n\nNote that new stages are not useful unless you also declare their order\nin relation to the default `main` stage.\n\nA complete run stage example:\n\n    stage { 'pre':\n      before => Stage['main'],\n    }\n\n    class { 'apt-updates':\n      stage => 'pre',\n    }\n\nIndividual resources cannot be assigned to run stages; you can only set stages\nfor classes.\n\n### Attributes {#stage-attributes}\n\n<pre><code>stage { 'resource title':\n  <a href=\"#stage-attribute-name\">name</a> =&gt; <em># <strong>(namevar)</strong> The name of the stage. Use this as the value for </em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#stage-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the stage. Use this as the value for the `stage` metaparameter\nwhen assigning classes to this stage.\n\n([↑ Back to stage attributes](#stage-attributes))\n\n\n\n\n\n---------\n\n## tidy\n\n* [Attributes](#tidy-attributes)\n\n### Description {#tidy-description}\n\nRemove unwanted files based on specific criteria.  Multiple\ncriteria are OR'd together, so a file that is too large but is not\nold enough will still get tidied. Ignores managed resources.\n\nIf you don't specify either `age` or `size`, then all files will\nbe removed.\n\nThis resource type works by generating a file resource for every file\nthat should be deleted and then letting that resource perform the\nactual deletion.\n\n### Attributes {#tidy-attributes}\n\n<pre><code>tidy { 'resource title':\n  <a href=\"#tidy-attribute-path\">path</a>      =&gt; <em># <strong>(namevar)</strong> The path to the file or directory to manage....</em>\n  <a href=\"#tidy-attribute-age\">age</a>       =&gt; <em># Tidy files whose age is equal to or greater than </em>\n  <a href=\"#tidy-attribute-backup\">backup</a>    =&gt; <em># Whether tidied files should be backed up.  Any...</em>\n  <a href=\"#tidy-attribute-matches\">matches</a>   =&gt; <em># One or more (shell type) file glob patterns...</em>\n  <a href=\"#tidy-attribute-max_files\">max_files</a> =&gt; <em># In case the resource is a directory and the...</em>\n  <a href=\"#tidy-attribute-recurse\">recurse</a>   =&gt; <em># If target is a directory, recursively descend...</em>\n  <a href=\"#tidy-attribute-rmdirs\">rmdirs</a>    =&gt; <em># Tidy directories in addition to files; that is...</em>\n  <a href=\"#tidy-attribute-size\">size</a>      =&gt; <em># Tidy files whose size is equal to or greater...</em>\n  <a href=\"#tidy-attribute-type\">type</a>      =&gt; <em># Set the mechanism for determining age.  Valid...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### path {#tidy-attribute-path}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe path to the file or directory to manage.  Must be fully\nqualified.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### age {#tidy-attribute-age}\n\nTidy files whose age is equal to or greater than\nthe specified time.  You can choose seconds, minutes,\nhours, days, or weeks by specifying the first letter of any\nof those words (for example, '1w' represents one week).\n\nSpecifying 0 will remove all files.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### backup {#tidy-attribute-backup}\n\nWhether tidied files should be backed up.  Any values are passed\ndirectly to the file resources used for actual file deletion, so consult\nthe `file` type's backup documentation to determine valid values.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### matches {#tidy-attribute-matches}\n\nOne or more (shell type) file glob patterns, which restrict\nthe list of files to be tidied to those whose basenames match\nat least one of the patterns specified. Multiple patterns can\nbe specified using an array.\n\nExample:\n\n    tidy { '/tmp':\n      age     => '1w',\n      recurse => 1,\n      matches => [ '[0-9]pub*.tmp', '*.temp', 'tmpfile?' ],\n    }\n\nThis removes files from `/tmp` if they are one week old or older,\nare not in a subdirectory and match one of the shell globs given.\n\nNote that the patterns are matched against the basename of each\nfile -- that is, your glob patterns should not have any '/'\ncharacters in them, since you are only specifying against the last\nbit of the file.\n\nFinally, note that you must now specify a non-zero/non-false value\nfor recurse if matches is used, as matches only apply to files found\nby recursion (there's no reason to use static patterns match against\na statically determined path).  Requiring explicit recursion clears\nup a common source of confusion.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### max_files {#tidy-attribute-max_files}\n\nIn case the resource is a directory and the recursion is enabled, puppet will\ngenerate a new resource for each file file found, possible leading to\nan excessive number of resources generated without any control.\n\nSetting `max_files` will check the number of file resources that\nwill eventually be created and will raise a resource argument error if the\nlimit will be exceeded.\n\nUse value `0` to disable the check. In this case, a warning is logged if\nthe number of files exceeds 1000.\n\nValues can match `/^[0-9]+$/`.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### recurse {#tidy-attribute-recurse}\n\nIf target is a directory, recursively descend\ninto the directory looking for files to tidy. Numeric values\nspecify a limit for the recursion depth, `true` means\nunrestricted recursion.\n\nValid values are `true`, `false`, `inf`. Values can match `/^[0-9]+$/`.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### rmdirs {#tidy-attribute-rmdirs}\n\nTidy directories in addition to files; that is, remove\ndirectories whose age is older than the specified criteria.\nThis will only remove empty directories, so all contained\nfiles must also be tidied before a directory gets removed.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### size {#tidy-attribute-size}\n\nTidy files whose size is equal to or greater than\nthe specified size.  Unqualified values are in kilobytes, but\n*b*, *k*, *m*, *g*, and *t* can be appended to specify *bytes*,\n*kilobytes*, *megabytes*, *gigabytes*, and *terabytes*, respectively.\nOnly the first character is significant, so the full word can also\nbe used.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### type {#tidy-attribute-type}\n\nSet the mechanism for determining age.\n\nValid values are `atime`, `mtime`, `ctime`.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n\n\n\n---------\n\n## user\n\n* [Attributes](#user-attributes)\n* [Providers](#user-providers)\n* [Provider Features](#user-provider-features)\n\n### Description {#user-description}\n\nManage users.  This type is mostly built to manage system\nusers, so it is lacking some features useful for managing normal\nusers.\n\nThis resource type uses the prescribed native tools for creating\ngroups and generally uses POSIX APIs for retrieving information\nabout them.  It does not directly modify `/etc/passwd` or anything.\n\n**Autorequires:** If Puppet is managing the user's primary group (as\nprovided in the `gid` attribute) or any group listed in the `groups`\nattribute then the user resource will autorequire that group. If Puppet\nis managing any role accounts corresponding to the user's roles, the\nuser resource will autorequire those role accounts.\n\n### Attributes {#user-attributes}\n\n<pre><code>user { 'resource title':\n  <a href=\"#user-attribute-name\">name</a>                 =&gt; <em># <strong>(namevar)</strong> The user name. While naming limitations vary by...</em>\n  <a href=\"#user-attribute-ensure\">ensure</a>               =&gt; <em># The basic state that the object should be in....</em>\n  <a href=\"#user-attribute-allowdupe\">allowdupe</a>            =&gt; <em># Whether to allow duplicate UIDs.  Valid values...</em>\n  <a href=\"#user-attribute-attribute_membership\">attribute_membership</a> =&gt; <em># Whether specified attribute value pairs should...</em>\n  <a href=\"#user-attribute-attributes\">attributes</a>           =&gt; <em># Specify AIX attributes for the user in an array...</em>\n  <a href=\"#user-attribute-auth_membership\">auth_membership</a>      =&gt; <em># Whether specified auths should be considered the </em>\n  <a href=\"#user-attribute-auths\">auths</a>                =&gt; <em># The auths the user has.  Multiple auths should...</em>\n  <a href=\"#user-attribute-comment\">comment</a>              =&gt; <em># A description of the user.  Generally the user's </em>\n  <a href=\"#user-attribute-expiry\">expiry</a>               =&gt; <em># The expiry date for this user. Provide as either </em>\n  <a href=\"#user-attribute-forcelocal\">forcelocal</a>           =&gt; <em># Forces the management of local accounts when...</em>\n  <a href=\"#user-attribute-gid\">gid</a>                  =&gt; <em># The user's primary group.  Can be specified...</em>\n  <a href=\"#user-attribute-groups\">groups</a>               =&gt; <em># The groups to which the user belongs.  The...</em>\n  <a href=\"#user-attribute-home\">home</a>                 =&gt; <em># The home directory of the user.  The directory...</em>\n  <a href=\"#user-attribute-ia_load_module\">ia_load_module</a>       =&gt; <em># The name of the I&A module to use to manage this </em>\n  <a href=\"#user-attribute-iterations\">iterations</a>           =&gt; <em># This is the number of iterations of a chained...</em>\n  <a href=\"#user-attribute-key_membership\">key_membership</a>       =&gt; <em># Whether specified key/value pairs should be...</em>\n  <a href=\"#user-attribute-keys\">keys</a>                 =&gt; <em># Specify user attributes in an array of key ...</em>\n  <a href=\"#user-attribute-loginclass\">loginclass</a>           =&gt; <em># The name of login class to which the user...</em>\n  <a href=\"#user-attribute-managehome\">managehome</a>           =&gt; <em># Whether to manage the home directory when Puppet </em>\n  <a href=\"#user-attribute-membership\">membership</a>           =&gt; <em># If `minimum` is specified, Puppet will ensure...</em>\n  <a href=\"#user-attribute-password\">password</a>             =&gt; <em># The user's password, in whatever encrypted...</em>\n  <a href=\"#user-attribute-password_max_age\">password_max_age</a>     =&gt; <em># The maximum number of days a password may be...</em>\n  <a href=\"#user-attribute-password_min_age\">password_min_age</a>     =&gt; <em># The minimum number of days a password must be...</em>\n  <a href=\"#user-attribute-password_warn_days\">password_warn_days</a>   =&gt; <em># The number of days before a password is going to </em>\n  <a href=\"#user-attribute-profile_membership\">profile_membership</a>   =&gt; <em># Whether specified roles should be treated as the </em>\n  <a href=\"#user-attribute-profiles\">profiles</a>             =&gt; <em># The profiles the user has.  Multiple profiles...</em>\n  <a href=\"#user-attribute-project\">project</a>              =&gt; <em># The name of the project associated with a user.  </em>\n  <a href=\"#user-attribute-provider\">provider</a>             =&gt; <em># The specific backend to use for this `user...</em>\n  <a href=\"#user-attribute-purge_ssh_keys\">purge_ssh_keys</a>       =&gt; <em># Whether to purge authorized SSH keys for this...</em>\n  <a href=\"#user-attribute-role_membership\">role_membership</a>      =&gt; <em># Whether specified roles should be considered the </em>\n  <a href=\"#user-attribute-roles\">roles</a>                =&gt; <em># The roles the user has.  Multiple roles should...</em>\n  <a href=\"#user-attribute-salt\">salt</a>                 =&gt; <em># This is the 32-byte salt used to generate the...</em>\n  <a href=\"#user-attribute-shell\">shell</a>                =&gt; <em># The user's login shell.  The shell must exist...</em>\n  <a href=\"#user-attribute-system\">system</a>               =&gt; <em># Whether the user is a system user, according to...</em>\n  <a href=\"#user-attribute-uid\">uid</a>                  =&gt; <em># The user ID; must be specified numerically. If...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#user-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe user name. While naming limitations vary by operating system,\nit is advisable to restrict names to the lowest common denominator,\nwhich is a maximum of 8 characters beginning with a letter.\n\nNote that Puppet considers user names to be case-sensitive, regardless\nof the platform's own rules; be sure to always use the same case when\nreferring to a given user.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### ensure {#user-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe basic state that the object should be in.\n\nValid values are `present`, `absent`, `role`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### allowdupe {#user-attribute-allowdupe}\n\nWhether to allow duplicate UIDs.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### attribute_membership {#user-attribute-attribute_membership}\n\nWhether specified attribute value pairs should be treated as the\n**complete list** (`inclusive`) or the **minimum list** (`minimum`) of\nattribute/value pairs for the user.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### attributes {#user-attribute-attributes}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify AIX attributes for the user in an array or hash of attribute = value pairs.\n\n For example:\n\n ```\n ['minage=0', 'maxage=5', 'SYSTEM=compat']\n ```\n\n or\n\n```\nattributes => { 'minage' => '0', 'maxage' => '5', 'SYSTEM' => 'compat' }\n```\n\n\n\nRequires features manages_aix_lam.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### auth_membership {#user-attribute-auth_membership}\n\nWhether specified auths should be considered the **complete list**\n(`inclusive`) or the **minimum list** (`minimum`) of auths the user\nhas. This setting is specific to managing Solaris authorizations.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### auths {#user-attribute-auths}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe auths the user has.  Multiple auths should be\nspecified as an array.\n\n\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### comment {#user-attribute-comment}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA description of the user.  Generally the user's full name.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### expiry {#user-attribute-expiry}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe expiry date for this user. Provide as either the special\nvalue `absent` to ensure that the account never expires, or as\na zero-padded YYYY-MM-DD format -- for example, 2010-02-19.\n\nValid values are `absent`. Values can match `/^\\d{4}-\\d{2}-\\d{2}$/`.\n\nRequires features manages_expiry.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### forcelocal {#user-attribute-forcelocal}\n\nForces the management of local accounts when accounts are also\nbeing managed by some other Name Service Switch (NSS). For AIX, refer to the `ia_load_module` parameter.\n\nThis option relies on your operating system's implementation of `luser*` commands, such as `luseradd` , and `lgroupadd`, `lusermod`. The `forcelocal` option could behave unpredictably in some circumstances. If the tools it depends on are not available, it might have no effect at all.\n\nValid values are `true`, `false`, `yes`, `no`.\n\nRequires features manages_local_users_and_groups.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### gid {#user-attribute-gid}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user's primary group.  Can be specified numerically or by name.\n\nThis attribute is not supported on Windows systems; use the `groups`\nattribute instead. (On Windows, designating a primary group is only\nmeaningful for domain accounts, which Puppet does not currently manage.)\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### groups {#user-attribute-groups}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe groups to which the user belongs.  The primary group should\nnot be listed, and groups should be identified by name rather than by\nGID.  Multiple groups should be specified as an array.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### home {#user-attribute-home}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe home directory of the user.  The directory must be created\nseparately and is not currently checked for existence.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### ia_load_module {#user-attribute-ia_load_module}\n\nThe name of the I&A module to use to manage this user.\nThis should be set to `files` if managing local users.\n\n\n\nRequires features manages_aix_lam.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### iterations {#user-attribute-iterations}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThis is the number of iterations of a chained computation of the\n[PBKDF2 password hash](https://en.wikipedia.org/wiki/PBKDF2). This parameter\nis used in OS X, and is required for managing passwords on OS X 10.8 and\nnewer.\n\n\n\nRequires features manages_password_salt.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### key_membership {#user-attribute-key_membership}\n\nWhether specified key/value pairs should be considered the\n**complete list** (`inclusive`) or the **minimum list** (`minimum`) of\nthe user's attributes.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### keys {#user-attribute-keys}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify user attributes in an array of key = value pairs.\n\n\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### loginclass {#user-attribute-loginclass}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe name of login class to which the user belongs.\n\n\n\nRequires features manages_loginclass.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### managehome {#user-attribute-managehome}\n\nWhether to manage the home directory when Puppet creates or removes the user.\nThis creates the home directory if Puppet also creates the user account, and deletes the\nhome directory if Puppet also removes the user account.\n\nThis parameter has no effect unless Puppet is also creating or removing the user in the\nresource at the same time. For instance, Puppet creates a home directory for a managed\nuser if `ensure => present` and the user does not exist at the time of the Puppet run.\nIf the home directory is then deleted manually, Puppet will not recreate it on the next\nrun.\n\nNote that on Windows, this manages creation/deletion of the user profile instead of the\nhome directory. The user profile is stored in the `C:\\Users\\<username>` directory.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### membership {#user-attribute-membership}\n\nIf `minimum` is specified, Puppet will ensure that the user is a\nmember of all specified groups, but will not remove any other groups\nthat the user is a part of.\n\nIf `inclusive` is specified, Puppet will ensure that the user is a\nmember of **only** specified groups.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password {#user-attribute-password}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user's password, in whatever encrypted format the local system\nrequires. Consult your operating system's documentation for acceptable password\nencryption formats and requirements.\n\n* Mac OS X 10.5 and 10.6, and some older Linux distributions, use salted SHA1\n  hashes. You can use Puppet's built-in `sha1` function to generate a salted SHA1\n  hash from a password.\n* Mac OS X 10.7 (Lion), and many recent Linux distributions, use salted SHA512\n  hashes. The Puppet Labs [stdlib][] module contains a `str2saltedsha512` function\n  which can generate password hashes for these operating systems.\n* OS X 10.8 and higher use salted SHA512 PBKDF2 hashes. When managing passwords\n  on these systems, the `salt` and `iterations` attributes need to be specified as\n  well as the password.\n* macOS 10.15 and later require the salt to be 32 bytes. Because Puppet's user\n  resource requires the value to be hex encoded, the length of the salt's\n  string must be 64.\n* Windows passwords can be managed only in cleartext, because there is no Windows\n  API for setting the password hash.\n\n[stdlib]: https://github.com/puppetlabs/puppetlabs-stdlib/\n\nEnclose any value that includes a dollar sign ($) in single quotes (') to avoid\naccidental variable interpolation.\n\nTo redact passwords from reports to PuppetDB, use the `Sensitive` data type. For\nexample, this resource protects the password:\n\n```puppet\nuser { 'foo':\n  ensure   => present,\n  password => Sensitive(\"my secret password\")\n}\n```\n\nThis results in the password being redacted from the report, as in the\n`previous_value`, `desired_value`, and `message` fields below.\n\n```yaml\n    events:\n    - !ruby/object:Puppet::Transaction::Event\n      audited: false\n      property: password\n      previous_value: \"[redacted]\"\n      desired_value: \"[redacted]\"\n      historical_value:\n      message: changed [redacted] to [redacted]\n      name: :password_changed\n      status: success\n      time: 2017-05-17 16:06:02.934398293 -07:00\n      redacted: true\n      corrective_change: false\n    corrective_change: false\n```\n\n\n\nRequires features manages_passwords.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password_max_age {#user-attribute-password_max_age}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe maximum number of days a password may be used before it must be changed.\n\n\n\nRequires features manages_password_age.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password_min_age {#user-attribute-password_min_age}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe minimum number of days a password must be used before it may be changed.\n\n\n\nRequires features manages_password_age.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password_warn_days {#user-attribute-password_warn_days}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe number of days before a password is going to expire (see the maximum password age) during which the user should be warned.\n\n\n\nRequires features manages_password_age.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### profile_membership {#user-attribute-profile_membership}\n\nWhether specified roles should be treated as the **complete list**\n(`inclusive`) or the **minimum list** (`minimum`) of roles\nof which the user is a member.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### profiles {#user-attribute-profiles}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe profiles the user has.  Multiple profiles should be\nspecified as an array.\n\n\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### project {#user-attribute-project}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe name of the project associated with a user.\n\n\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### provider {#user-attribute-provider}\n\nThe specific backend to use for this `user`\nresource. You will seldom need to specify this --- Puppet will usually\ndiscover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`aix`](#user-provider-aix)\n* [`directoryservice`](#user-provider-directoryservice)\n* [`hpuxuseradd`](#user-provider-hpuxuseradd)\n* [`ldap`](#user-provider-ldap)\n* [`openbsd`](#user-provider-openbsd)\n* [`pw`](#user-provider-pw)\n* [`user_role_add`](#user-provider-user_role_add)\n* [`useradd`](#user-provider-useradd)\n* [`windows_adsi`](#user-provider-windows_adsi)\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### purge_ssh_keys {#user-attribute-purge_ssh_keys}\n\nWhether to purge authorized SSH keys for this user if they are not managed\nwith the `ssh_authorized_key` resource type. This parameter is a noop if the\nssh_authorized_key type is not available.\n\nAllowed values are:\n\n* `false` (default) --- don't purge SSH keys for this user.\n* `true` --- look for keys in the `.ssh/authorized_keys` file in the user's\n  home directory. Purge any keys that aren't managed as `ssh_authorized_key`\n  resources.\n* An array of file paths --- look for keys in all of the files listed. Purge\n  any keys that aren't managed as `ssh_authorized_key` resources. If any of\n  these paths starts with `~` or `%h`, that token will be replaced with\n  the user's home directory.\n\nValid values are `true`, `false`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### role_membership {#user-attribute-role_membership}\n\nWhether specified roles should be considered the **complete list**\n(`inclusive`) or the **minimum list** (`minimum`) of roles the user\nhas.\n\nValid values are `inclusive`, `minimum`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### roles {#user-attribute-roles}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe roles the user has.  Multiple roles should be\nspecified as an array.\n\n\n\nRequires features manages_roles.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### salt {#user-attribute-salt}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThis is the 32-byte salt used to generate the PBKDF2 password used in\nOS X. This field is required for managing passwords on OS X >= 10.8.\n\n\n\nRequires features manages_password_salt.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### shell {#user-attribute-shell}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user's login shell.  The shell must exist and be\nexecutable.\n\nThis attribute cannot be managed on Windows systems.\n\n\n\nRequires features manages_shell.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### system {#user-attribute-system}\n\nWhether the user is a system user, according to the OS's criteria;\non most platforms, a UID less than or equal to 500 indicates a system\nuser. This parameter is only used when the resource is created and will\nnot affect the UID when the user is present.\n\nValid values are `true`, `false`, `yes`, `no`.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### uid {#user-attribute-uid}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user ID; must be specified numerically. If no user ID is\nspecified when creating a new user, then one will be chosen\nautomatically. This will likely result in the same user having\ndifferent UIDs on different systems, which is not recommended. This is\nespecially noteworthy when managing the same user on both Darwin and\nother platforms, since Puppet does UID generation on Darwin, but\nthe underlying tools do so on other platforms.\n\nOn Windows, this property is read-only and will return the user's\nsecurity identifier (SID).\n\n([↑ Back to user attributes](#user-attributes))\n\n\n### Providers {#user-providers}\n\n#### aix {#user-provider-aix}\n\nUser management for AIX.\n\n* Required binaries: `/bin/chpasswd`, `/usr/bin/chuser`, `/usr/bin/mkuser`, `/usr/sbin/lsuser`, `/usr/sbin/rmuser`.\n* Default for `os.name` == `aix`.\n* Supported features: `manages_aix_lam`, `manages_expiry`, `manages_homedir`, `manages_local_users_and_groups`, `manages_password_age`, `manages_passwords`, `manages_shell`.\n\n#### directoryservice {#user-provider-directoryservice}\n\nUser management on OS X.\n\n* Required binaries: `/usr/bin/dscacheutil`, `/usr/bin/dscl`, `/usr/bin/dsimport`, `/usr/bin/uuidgen`.\n* Default for `os.name` == `darwin`.\n* Supported features: `manages_password_salt`, `manages_passwords`, `manages_shell`.\n\n#### hpuxuseradd {#user-provider-hpuxuseradd}\n\nUser management for HP-UX. This provider uses the undocumented `-F`\nswitch to HP-UX's special `usermod` binary to work around the fact that\nits standard `usermod` cannot make changes while the user is logged in.\nNew functionality provides for changing trusted computing passwords and\nresetting password expirations under trusted computing.\n\n* Required binaries: `/usr/sam/lbin/useradd.sam`, `/usr/sam/lbin/userdel.sam`, `/usr/sam/lbin/usermod.sam`.\n* Default for `os.name` == `hp-ux`.\n* Supported features: `allows_duplicates`, `manages_homedir`, `manages_passwords`.\n\n#### ldap {#user-provider-ldap}\n\nUser management via LDAP.\n\nThis provider requires that you have valid values for all of the\nLDAP-related settings in `puppet.conf`, including `ldapbase`.  You will\nalmost definitely need settings for `ldapuser` and `ldappassword` in order\nfor your clients to write to LDAP.\n\nNote that this provider will automatically generate a UID for you if\nyou do not specify one, but it is a potentially expensive operation,\nas it iterates across all existing users to pick the appropriate next one.\n\n* Supported features: `manages_passwords`, `manages_shell`.\n\n#### openbsd {#user-provider-openbsd}\n\nUser management via `useradd` and its ilk for OpenBSD. Note that you\nwill need to install Ruby's shadow password library (package known as\n`ruby-shadow`) if you wish to manage user passwords.\n\n* Required binaries: `passwd`, `useradd`, `userdel`, `usermod`.\n* Default for `os.name` == `openbsd`.\n* Supported features: `manages_expiry`, `manages_homedir`, `manages_shell`, `system_users`.\n\n#### pw {#user-provider-pw}\n\nUser management via `pw` on FreeBSD and DragonFly BSD.\n\n* Required binaries: `pw`.\n* Default for `os.name` == `freebsd, dragonfly`.\n* Supported features: `allows_duplicates`, `manages_expiry`, `manages_homedir`, `manages_passwords`, `manages_shell`.\n\n#### user_role_add {#user-provider-user_role_add}\n\nUser and role management on Solaris, via `useradd` and `roleadd`.\n\n* Required binaries: `passwd`, `roleadd`, `roledel`, `rolemod`, `useradd`, `userdel`, `usermod`.\n* Default for `os.family` == `solaris`.\n* Supported features: `allows_duplicates`, `manages_homedir`, `manages_password_age`, `manages_passwords`, `manages_roles`, `manages_shell`, `manages_solaris_rbac`.\n\n#### useradd {#user-provider-useradd}\n\nUser management via `useradd` and its ilk.  Note that you will need to\ninstall Ruby's shadow password library (often known as `ruby-libshadow`)\nif you wish to manage user passwords.\n\nTo use the `forcelocal` parameter, you need to install the `libuser` package (providing\n`/usr/sbin/lgroupadd` and `/usr/sbin/luseradd`).\n\n* Required binaries: `chage`, `chpasswd`, `lchage`, `luseradd`, `luserdel`, `lusermod`, `useradd`, `userdel`, `usermod`.\n* Supported features: `allows_duplicates`, `manages_expiry`, `manages_homedir`, `manages_shell`, `system_users`.\n\n#### windows_adsi {#user-provider-windows_adsi}\n\nLocal user management for Windows.\n\n* Default for `os.name` == `windows`.\n* Supported features: `manages_homedir`, `manages_passwords`, `manages_roles`.\n\n### Provider Features {#user-provider-features}\n\nAvailable features:\n\n* `allows_duplicates` --- The provider supports duplicate users with the same UID.\n* `manages_aix_lam` --- The provider can manage AIX Loadable Authentication Module (LAM) system.\n* `manages_expiry` --- The provider can manage the expiry date for a user.\n* `manages_homedir` --- The provider can create and remove home directories.\n* `manages_local_users_and_groups` --- Allows local users to be managed on systems that also use some other remote Name Service Switch (NSS) method of managing accounts.\n* `manages_loginclass` --- The provider can manage the login class for a user.\n* `manages_password_age` --- The provider can set age requirements and restrictions for passwords.\n* `manages_password_salt` --- The provider can set a password salt. This is for providers that implement PBKDF2 passwords with salt properties.\n* `manages_passwords` --- The provider can modify user passwords, by accepting a password hash.\n* `manages_roles` --- The provider can manage roles\n* `manages_shell` --- The provider allows for setting shell and validates if possible\n* `manages_solaris_rbac` --- The provider can manage normal users\n* `system_users` --- The provider allows you to create system users with lower UIDs.\n\nProvider support:\n\n* **aix** - _manages aix lam, manages expiry, manages homedir, manages local users and groups, manages password age, manages passwords, manages shell_\n* **directoryservice** - _manages password salt, manages passwords, manages shell_\n* **hpuxuseradd** - _allows duplicates, manages homedir, manages passwords_\n* **ldap** - _manages passwords, manages shell_\n* **openbsd** - _manages expiry, manages homedir, manages shell, system users, manages passwords, manages loginclass_\n* **pw** - _allows duplicates, manages expiry, manages homedir, manages passwords, manages shell_\n* **user_role_add** - _allows duplicates, manages homedir, manages password age, manages passwords, manages roles, manages shell, manages solaris rbac_\n* **useradd** - _allows duplicates, manages expiry, manages homedir, manages shell, system users, manages passwords, manages password age, libuser_\n* **windows_adsi** - _manages homedir, manages passwords, manages roles_\n  \n\n\n\n\n"
  },
  {
    "path": "references/types/exec.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: exec'\ncanonical: \"/puppet/latest/types/exec.html\"\n---\n\n# Resource Type: exec\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## exec\n\n* [Attributes](#exec-attributes)\n* [Providers](#exec-providers)\n\n### Description {#exec-description}\n\nExecutes external commands.\n\nAny command in an `exec` resource **must** be able to run multiple times\nwithout causing harm --- that is, it must be *idempotent*. There are three\nmain ways for an exec to be idempotent:\n\n* The command itself is already idempotent. (For example, `apt-get update`.)\n* The exec has an `onlyif`, `unless`, or `creates` attribute, which prevents\n  Puppet from running the command unless some condition is met. The\n  `onlyif` and `unless` commands of an `exec` are used in the process of\n  determining whether the `exec` is already in sync, therefore they must be run\n  during a noop Puppet run.\n* The exec has `refreshonly => true`, which allows Puppet to run the\n  command only when some other resource is changed. (See the notes on refreshing\n  below.)\n\nThe state managed by an `exec` resource represents whether the specified command\n_needs to be_ executed during the catalog run. The target state is always that\nthe command does not need to be executed. If the initial state is that the\ncommand _does_ need to be executed, then successfully executing the command\ntransitions it to the target state.\n\nThe `unless`, `onlyif`, and `creates` properties check the initial state of the\nresource. If one or more of these properties is specified, the exec might not\nneed to run. If the exec does not need to run, then the system is already in\nthe target state. In such cases, the exec is considered successful without\nactually executing its command.\n\nA caution: There's a widespread tendency to use collections of execs to\nmanage resources that aren't covered by an existing resource type. This\nworks fine for simple tasks, but once your exec pile gets complex enough\nthat you really have to think to understand what's happening, you should\nconsider developing a custom resource type instead, as it is much\nmore predictable and maintainable.\n\n**Duplication:** Even though `command` is the namevar, Puppet allows\nmultiple `exec` resources with the same `command` value.\n\n**Refresh:** `exec` resources can respond to refresh events (via\n`notify`, `subscribe`, or the `~>` arrow). The refresh behavior of execs\nis non-standard, and can be affected by the `refresh` and\n`refreshonly` attributes:\n\n* If `refreshonly` is set to true, the exec runs _only_ when it receives an\n  event. This is the most reliable way to use refresh with execs.\n* If the exec has already run and then receives an event, it runs its\n  command **up to two times.** If an `onlyif`, `unless`, or `creates` condition\n  is no longer met after the first run, the second run does not occur.\n* If the exec has already run, has a `refresh` command, and receives an\n  event, it runs its normal command. Then, if any `onlyif`, `unless`, or `creates`\n  conditions are still met, the exec runs its `refresh` command.\n* If the exec has an `onlyif`, `unless`, or `creates` attribute that prevents it\n  from running, and it then receives an event, it still will not run.\n* If the exec has `noop => true`, would otherwise have run, and receives\n  an event from a non-noop resource, it runs once. However, if it has a `refresh`\n  command, it runs that instead of its normal command.\n\nIn short: If there's a possibility of your exec receiving refresh events,\nit is extremely important to make sure the run conditions are restricted.\n\n**Autorequires:** If Puppet is managing an exec's cwd or the executable\nfile used in an exec's command, the exec resource autorequires those\nfiles. If Puppet is managing the user that an exec should run as, the\nexec resource autorequires that user.\n\n### Attributes {#exec-attributes}\n\n<pre><code>exec { 'resource title':\n  <a href=\"#exec-attribute-command\">command</a>     =&gt; <em># <strong>(namevar)</strong> The actual command to execute.  Must either be...</em>\n  <a href=\"#exec-attribute-creates\">creates</a>     =&gt; <em># A file to look for before running the command...</em>\n  <a href=\"#exec-attribute-cwd\">cwd</a>         =&gt; <em># The directory from which to run the command.  If </em>\n  <a href=\"#exec-attribute-environment\">environment</a> =&gt; <em># An array of any additional environment variables </em>\n  <a href=\"#exec-attribute-group\">group</a>       =&gt; <em># The group to run the command as.  This seems to...</em>\n  <a href=\"#exec-attribute-logoutput\">logoutput</a>   =&gt; <em># Whether to log command output in addition to...</em>\n  <a href=\"#exec-attribute-onlyif\">onlyif</a>      =&gt; <em># A test command that checks the state of the...</em>\n  <a href=\"#exec-attribute-path\">path</a>        =&gt; <em># The search path used for command execution...</em>\n  <a href=\"#exec-attribute-provider\">provider</a>    =&gt; <em># The specific backend to use for this `exec...</em>\n  <a href=\"#exec-attribute-refresh\">refresh</a>     =&gt; <em># An alternate command to run when the `exec...</em>\n  <a href=\"#exec-attribute-refreshonly\">refreshonly</a> =&gt; <em># The command should only be run as a refresh...</em>\n  <a href=\"#exec-attribute-returns\">returns</a>     =&gt; <em># The expected exit code(s).  An error will be...</em>\n  <a href=\"#exec-attribute-timeout\">timeout</a>     =&gt; <em># The maximum time the command should take.  If...</em>\n  <a href=\"#exec-attribute-tries\">tries</a>       =&gt; <em># The number of times execution of the command...</em>\n  <a href=\"#exec-attribute-try_sleep\">try_sleep</a>   =&gt; <em># The time to sleep in seconds between 'tries'....</em>\n  <a href=\"#exec-attribute-umask\">umask</a>       =&gt; <em># Sets the umask to be used while executing this...</em>\n  <a href=\"#exec-attribute-unless\">unless</a>      =&gt; <em># A test command that checks the state of the...</em>\n  <a href=\"#exec-attribute-user\">user</a>        =&gt; <em># The user to run the command as.  > **Note:*...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### command {#exec-attribute-command}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe actual command to execute.  Must either be fully qualified\nor a search path for the command must be provided.  If the command\nsucceeds, any output produced will be logged at the instance's\nnormal log level (usually `notice`), but if the command fails\n(meaning its return code does not match the specified code) then\nany output is logged at the `err` log level.\n\nMultiple `exec` resources can use the same `command` value; Puppet\nonly uses the resource title to ensure `exec`s are unique.\n\nOn *nix platforms, the command can be specified as an array of\nstrings and Puppet will invoke it using the more secure method of\nparameterized system calls. For example, rather than executing the\nmalicious injected code, this command will echo it out:\n\n    command => ['/bin/echo', 'hello world; rm -rf /']\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### creates {#exec-attribute-creates}\n\nA file to look for before running the command. The command will\nonly run if the file **doesn't exist.**\n\nThis parameter doesn't cause Puppet to create a file; it is only\nuseful if **the command itself** creates a file.\n\n    exec { 'tar -xf /Volumes/nfs02/important.tar':\n      cwd     => '/var/tmp',\n      creates => '/var/tmp/myfile',\n      path    => ['/usr/bin', '/usr/sbin',],\n    }\n\nIn this example, `myfile` is assumed to be a file inside\n`important.tar`. If it is ever deleted, the exec will bring it\nback by re-extracting the tarball. If `important.tar` does **not**\nactually contain `myfile`, the exec will keep running every time\nPuppet runs.\n\nThis parameter can also take an array of files, and the command will\nnot run if **any** of these files exist. Consider this example:\n\n    creates => ['/tmp/file1', '/tmp/file2'],\n\nThe command is only run if both files don't exist.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### cwd {#exec-attribute-cwd}\n\nThe directory from which to run the command.  If\nthis directory does not exist, the command will fail.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### environment {#exec-attribute-environment}\n\nAn array of any additional environment variables you want to set for a\ncommand, such as `[ 'HOME=/root', 'MAIL=root@example.com']`.\nNote that if you use this to set PATH, it will override the `path`\nattribute. Multiple environment variables should be specified as an\narray.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### group {#exec-attribute-group}\n\nThe group to run the command as.  This seems to work quite\nhaphazardly on different platforms -- it is a platform issue\nnot a Ruby or Puppet one, since the same variety exists when\nrunning commands as different users in the shell.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### logoutput {#exec-attribute-logoutput}\n\nWhether to log command output in addition to logging the\nexit code. Defaults to `on_failure`, which only logs the output\nwhen the command has an exit code that does not match any value\nspecified by the `returns` attribute. As with any resource type,\nthe log level can be controlled with the `loglevel` metaparameter.\n\nDefault: `on_failure`\n\nAllowed values:\n\n* `true`\n* `false`\n* `on_failure`\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### onlyif {#exec-attribute-onlyif}\n\nA test command that checks the state of the target system and restricts\nwhen the `exec` can run. If present, Puppet runs this test command\nfirst, and only runs the main command if the test has an exit code of 0\n(success). For example:\n\n    exec { 'logrotate':\n      path     => '/usr/bin:/usr/sbin:/bin',\n      provider => shell,\n      onlyif   => 'test `du /var/log/messages | cut -f1` -gt 100000',\n    }\n\nThis would run `logrotate` only if that test returns true.\n\nNote that this test command runs with the same `provider`, `path`,\n`user`, `cwd`, and `group` as the main command. If the `path` isn't set, you\nmust fully qualify the command's name.\n\nSince this command is used in the process of determining whether the\n`exec` is already in sync, it must be run during a noop Puppet run.\n\nThis parameter can also take an array of commands. For example:\n\n    onlyif => ['test -f /tmp/file1', 'test -f /tmp/file2'],\n\nor an array of arrays. For example:\n\n    onlyif => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']\n\nThis `exec` would only run if every command in the array has an\nexit code of 0 (success).\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### path {#exec-attribute-path}\n\nThe search path used for command execution.\nCommands must be fully qualified if no path is specified.  Paths\ncan be specified as an array or as a '\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### provider {#exec-attribute-provider}\n\nThe specific backend to use for this `exec` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`posix`](#exec-provider-posix)\n* [`shell`](#exec-provider-shell)\n* [`windows`](#exec-provider-windows)\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### refresh {#exec-attribute-refresh}\n\nAn alternate command to run when the `exec` receives a refresh event\nfrom another resource. By default, Puppet runs the main command again.\nFor more details, see the notes about refresh behavior above, in the\ndescription for this resource type.\n\nNote that this alternate command runs with the same `provider`, `path`,\n`user`, and `group` as the main command. If the `path` isn't set, you\nmust fully qualify the command's name.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### refreshonly {#exec-attribute-refreshonly}\n\nThe command should only be run as a\nrefresh mechanism for when a dependent object is changed.  It only\nmakes sense to use this option when this command depends on some\nother object; it is useful for triggering an action:\n\n    # Pull down the main aliases file\n    file { '/etc/aliases':\n      source => 'puppet://server/module/aliases',\n    }\n\n    # Rebuild the database, but only when the file changes\n    exec { newaliases:\n      path        => ['/usr/bin', '/usr/sbin'],\n      subscribe   => File['/etc/aliases'],\n      refreshonly => true,\n    }\n\nNote that only `subscribe` and `notify` can trigger actions, not `require`,\nso it only makes sense to use `refreshonly` with `subscribe` or `notify`.\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### returns {#exec-attribute-returns}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe expected exit code(s).  An error will be returned if the\nexecuted command has some other exit code. Can be specified as an array\nof acceptable exit codes or a single value.\n\nOn POSIX systems, exit codes are always integers between 0 and 255.\n\nOn Windows, **most** exit codes should be integers between 0\nand 2147483647.\n\nLarger exit codes on Windows can behave inconsistently across different\ntools. The Win32 APIs define exit codes as 32-bit unsigned integers, but\nboth the cmd.exe shell and the .NET runtime cast them to signed\nintegers. This means some tools will report negative numbers for exit\ncodes above 2147483647. (For example, cmd.exe reports 4294967295 as -1.)\nSince Puppet uses the plain Win32 APIs, it will report the very large\nnumber instead of the negative number, which might not be what you\nexpect if you got the exit code from a cmd.exe session.\n\nMicrosoft recommends against using negative/very large exit codes, and\nyou should avoid them when possible. To convert a negative exit code to\nthe positive one Puppet will use, add it to 4294967296.\n\nDefault: `0`\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### timeout {#exec-attribute-timeout}\n\nThe maximum time the command should take.  If the command takes\nlonger than the timeout, the command is considered to have failed\nand will be stopped. The timeout is specified in seconds. The default\ntimeout is 300 seconds and you can set it to 0 to disable the timeout.\n\nDefault: `300`\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### tries {#exec-attribute-tries}\n\nThe number of times execution of the command should be tried.\nThis many attempts will be made to execute the command until an\nacceptable return code is returned. Note that the timeout parameter\napplies to each try rather than to the complete set of tries.\n\nDefault: `1`\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### try_sleep {#exec-attribute-try_sleep}\n\nThe time to sleep in seconds between 'tries'.\n\nDefault: `0`\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### umask {#exec-attribute-umask}\n\nSets the umask to be used while executing this command\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### unless {#exec-attribute-unless}\n\nA test command that checks the state of the target system and restricts\nwhen the `exec` can run. If present, Puppet runs this test command\nfirst, then runs the main command unless the test has an exit code of 0\n(success). For example:\n\n    exec { '/bin/echo root >> /usr/lib/cron/cron.allow':\n      path   => '/usr/bin:/usr/sbin:/bin',\n      unless => 'grep ^root$ /usr/lib/cron/cron.allow 2>/dev/null',\n    }\n\nThis would add `root` to the cron.allow file (on Solaris) unless\n`grep` determines it's already there.\n\nNote that this test command runs with the same `provider`, `path`,\n`user`, `cwd`, and `group` as the main command. If the `path` isn't set, you\nmust fully qualify the command's name.\n\nSince this command is used in the process of determining whether the\n`exec` is already in sync, it must be run during a noop Puppet run.\n\nThis parameter can also take an array of commands. For example:\n\n    unless => ['test -f /tmp/file1', 'test -f /tmp/file2'],\n\nor an array of arrays. For example:\n\n    unless => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']\n\nThis `exec` would only run if every command in the array has a\nnon-zero exit code.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n#### user {#exec-attribute-user}\n\nThe user to run the command as.\n\n> **Note:** Puppet cannot execute commands as other users on Windows.\n\nNote that if you use this attribute, any error output is not captured\ndue to a bug within Ruby. If you use Puppet to create this user, the\nexec automatically requires the user, as long as it is specified by\nname.\n\nThe $HOME environment variable is not automatically set when using\nthis attribute.\n\n([↑ Back to exec attributes](#exec-attributes))\n\n\n### Providers {#exec-providers}\n\n#### posix {#exec-provider-posix}\n\nExecutes external binaries by invoking Ruby's `Kernel.exec`.\nWhen the command is a string, it will be executed directly,\nwithout a shell, if it follows these rules:\n - no meta characters\n - no shell reserved word and no special built-in\n\nWhen the command is an Array of Strings, passed as `[cmdname, arg1, ...]`\nit will be executed directly(the first element is taken as a command name\nand the rest are passed as parameters to command with no shell expansion)\nThis is a safer and more predictable way to execute most commands,\nbut prevents the use of globbing and shell built-ins (including control\nlogic like \"for\" and \"if\" statements).\n\nIf the use of globbing and shell built-ins is desired, please check\nthe `shell` provider\n\n* Confined to: `feature == posix`\n* Default for: `[\"feature\", \"posix\"] == `\n* Supported features: `umask`\n\n#### shell {#exec-provider-shell}\n\nPasses the provided command through `/bin/sh`; only available on\nPOSIX systems. This allows the use of shell globbing and built-ins, and\ndoes not require that the path to a command be fully-qualified. Although\nthis can be more convenient than the `posix` provider, it also means that\nyou need to be more careful with escaping; as ever, with great power comes\netc. etc.\n\nThis provider closely resembles the behavior of the `exec` type\nin Puppet 0.25.x.\n\n* Confined to: `feature == posix`\n\n#### windows {#exec-provider-windows}\n\nExecute external binaries on Windows systems. As with the `posix`\nprovider, this provider directly calls the command with the arguments\ngiven, without passing it through a shell or performing any interpolation.\nTo use shell built-ins --- that is, to emulate the `shell` provider on\nWindows --- a command must explicitly invoke the shell:\n\n    exec {'echo foo':\n      command => 'cmd.exe /c echo \"foo\"',\n    }\n\nIf no extension is specified for a command, Windows will use the `PATHEXT`\nenvironment variable to locate the executable.\n\n**Note on PowerShell scripts:** PowerShell's default `restricted`\nexecution policy doesn't allow it to run saved scripts. To run PowerShell\nscripts, specify the `remotesigned` execution policy as part of the\ncommand:\n\n    exec { 'test':\n      path    => 'C:/Windows/System32/WindowsPowerShell/v1.0',\n      command => 'powershell -executionpolicy remotesigned -file C:/test.ps1',\n    }\n\n* Confined to: `os.name == windows`\n* Default for: `[\"os.name\", \"windows\"] == `\n\n\n\n\n"
  },
  {
    "path": "references/types/file.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: file'\ncanonical: \"/puppet/latest/types/file.html\"\n---\n\n# Resource Type: file\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## file\n\n* [Attributes](#file-attributes)\n* [Providers](#file-providers)\n* [Provider Features](#file-provider-features)\n\n### Description {#file-description}\n\nManages files, including their content, ownership, and permissions.\n\nThe `file` type can manage normal files, directories, and symlinks; the\ntype should be specified in the `ensure` attribute.\n\nFile contents can be managed directly with the `content` attribute, or\ndownloaded from a remote source using the `source` attribute; the latter\ncan also be used to recursively serve directories (when the `recurse`\nattribute is set to `true` or `local`). On Windows, note that file\ncontents are managed in binary mode; Puppet never automatically translates\nline endings.\n\n**Autorequires:** If Puppet is managing the user or group that owns a\nfile, the file resource will autorequire them. If Puppet is managing any\nparent directories of a file, the file resource autorequires them.\n\nWarning: Enabling `recurse` on directories containing large numbers of\nfiles slows agent runs. To manage file attributes for many files,\nconsider using alternative methods such as the `chmod_r`, `chown_r`,\n or `recursive_file_permissions` modules from the Forge.\n\n### Attributes {#file-attributes}\n\n<pre><code>file { 'resource title':\n  <a href=\"#file-attribute-path\">path</a>                    =&gt; <em># <strong>(namevar)</strong> The path to the file to manage.  Must be fully...</em>\n  <a href=\"#file-attribute-ensure\">ensure</a>                  =&gt; <em># Whether the file should exist, and if so what...</em>\n  <a href=\"#file-attribute-backup\">backup</a>                  =&gt; <em># Whether (and how) file content should be backed...</em>\n  <a href=\"#file-attribute-checksum\">checksum</a>                =&gt; <em># The checksum type to use when determining...</em>\n  <a href=\"#file-attribute-checksum_value\">checksum_value</a>          =&gt; <em># The checksum of the source contents. Only md5...</em>\n  <a href=\"#file-attribute-content\">content</a>                 =&gt; <em># The desired contents of a file, as a string...</em>\n  <a href=\"#file-attribute-ctime\">ctime</a>                   =&gt; <em># A read-only state to check the file ctime. On...</em>\n  <a href=\"#file-attribute-force\">force</a>                   =&gt; <em># Perform the file operation even if it will...</em>\n  <a href=\"#file-attribute-group\">group</a>                   =&gt; <em># Which group should own the file.  Argument can...</em>\n  <a href=\"#file-attribute-ignore\">ignore</a>                  =&gt; <em># A parameter which omits action on files matching </em>\n  <a href=\"#file-attribute-links\">links</a>                   =&gt; <em># How to handle links during file actions.  During </em>\n  <a href=\"#file-attribute-max_files\">max_files</a>               =&gt; <em># In case the resource is a directory and the...</em>\n  <a href=\"#file-attribute-mode\">mode</a>                    =&gt; <em># The desired permissions mode for the file, in...</em>\n  <a href=\"#file-attribute-mtime\">mtime</a>                   =&gt; <em># A read-only state to check the file mtime. On...</em>\n  <a href=\"#file-attribute-owner\">owner</a>                   =&gt; <em># The user to whom the file should belong....</em>\n  <a href=\"#file-attribute-provider\">provider</a>                =&gt; <em># The specific backend to use for this `file...</em>\n  <a href=\"#file-attribute-purge\">purge</a>                   =&gt; <em># Whether unmanaged files should be purged. This...</em>\n  <a href=\"#file-attribute-recurse\">recurse</a>                 =&gt; <em># Whether to recursively manage the _contents_ of...</em>\n  <a href=\"#file-attribute-recurselimit\">recurselimit</a>            =&gt; <em># How far Puppet should descend into...</em>\n  <a href=\"#file-attribute-replace\">replace</a>                 =&gt; <em># Whether to replace a file or symlink that...</em>\n  <a href=\"#file-attribute-selinux_ignore_defaults\">selinux_ignore_defaults</a> =&gt; <em># If this is set, Puppet will not call the SELinux </em>\n  <a href=\"#file-attribute-selrange\">selrange</a>                =&gt; <em># What the SELinux range component of the context...</em>\n  <a href=\"#file-attribute-selrole\">selrole</a>                 =&gt; <em># What the SELinux role component of the context...</em>\n  <a href=\"#file-attribute-seltype\">seltype</a>                 =&gt; <em># What the SELinux type component of the context...</em>\n  <a href=\"#file-attribute-seluser\">seluser</a>                 =&gt; <em># What the SELinux user component of the context...</em>\n  <a href=\"#file-attribute-show_diff\">show_diff</a>               =&gt; <em># Whether to display differences when the file...</em>\n  <a href=\"#file-attribute-source\">source</a>                  =&gt; <em># A source file, which will be copied into place...</em>\n  <a href=\"#file-attribute-source_permissions\">source_permissions</a>      =&gt; <em># Whether (and how) Puppet should copy owner...</em>\n  <a href=\"#file-attribute-sourceselect\">sourceselect</a>            =&gt; <em># Whether to copy all valid sources, or just the...</em>\n  <a href=\"#file-attribute-staging_location\">staging_location</a>        =&gt; <em># When rendering a file first render it to this...</em>\n  <a href=\"#file-attribute-target\">target</a>                  =&gt; <em># The target for creating a link.  Currently...</em>\n  <a href=\"#file-attribute-type\">type</a>                    =&gt; <em># A read-only state to check the file...</em>\n  <a href=\"#file-attribute-validate_cmd\">validate_cmd</a>            =&gt; <em># A command for validating the file's syntax...</em>\n  <a href=\"#file-attribute-validate_replacement\">validate_replacement</a>    =&gt; <em># The replacement string in a `validate_cmd` that...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### path {#file-attribute-path}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe path to the file to manage.  Must be fully qualified.\n\nOn Windows, the path should include the drive letter and should use `/` as\nthe separator character (rather than `\\\\`).\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### ensure {#file-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhether the file should exist, and if so what kind of file it should be.\nPossible values are `present`, `absent`, `file`, `directory`, and `link`.\n\n* `present` accepts any form of file existence, and creates a\n  normal file if the file is missing. (The file will have no content\n  unless the `content` or `source` attribute is used.)\n* `absent` ensures the file doesn't exist, and deletes it if necessary.\n* `file` ensures it's a normal file, and enables use of the `content` or\n  `source` attribute.\n* `directory` ensures it's a directory, and enables use of the `source`,\n  `recurse`, `recurselimit`, `ignore`, and `purge` attributes.\n* `link` ensures the file is a symlink, and **requires** that you also\n  set the `target` attribute. Symlinks are supported on all Posix\n  systems and on Windows Vista / 2008 and higher. On Windows, managing\n  symlinks requires Puppet agent's user account to have the \"Create\n  Symbolic Links\" privilege; this can be configured in the \"User Rights\n  Assignment\" section in the Windows policy editor. By default, Puppet\n  agent runs as the Administrator account, which has this privilege.\n\nPuppet avoids destroying directories unless the `force` attribute is set\nto `true`. This means that if a file is currently a directory, setting\n`ensure` to anything but `directory` or `present` will cause Puppet to\nskip managing the resource and log either a notice or an error.\n\nThere is one other non-standard value for `ensure`. If you specify the\npath to another file as the ensure value, it is equivalent to specifying\n`link` and using that path as the `target`:\n\n    # Equivalent resources:\n\n    file { '/etc/inetd.conf':\n      ensure => '/etc/inet/inetd.conf',\n    }\n\n    file { '/etc/inetd.conf':\n      ensure => link,\n      target => '/etc/inet/inetd.conf',\n    }\n\nHowever, we recommend using `link` and `target` explicitly, since this\nbehavior can be harder to read and is\n[deprecated](https://docs.puppet.com/puppet/4.3/deprecated_language.html)\nas of Puppet 4.3.0.\n\nAllowed values:\n\n* `absent`\n* `false`\n* `file`\n* `present`\n* `directory`\n* `link`\n* `/./`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### backup {#file-attribute-backup}\n\nWhether (and how) file content should be backed up before being replaced.\nThis attribute works best as a resource default in the site manifest\n(`File { backup => main }`), so it can affect all file resources.\n\n* If set to `false`, file content won't be backed up.\n* If set to a string beginning with `.`, such as `.puppet-bak`, Puppet will\n  use copy the file in the same directory with that value as the extension\n  of the backup. (A value of `true` is a synonym for `.puppet-bak`.)\n* If set to any other string, Puppet will try to back up to a filebucket\n  with that title. Puppet automatically creates a **local** filebucket\n  named `puppet` if one doesn't already exist. See the `filebucket` resource\n  type for more details.\n\nDefault value: `false`\n\nBacking up to a local filebucket isn't particularly useful. If you want\nto make organized use of backups, you will generally want to use the\nprimary Puppet server's filebucket service. This requires declaring a\nfilebucket resource and a resource default for the `backup` attribute\nin site.pp:\n\n    # /etc/puppetlabs/puppet/manifests/site.pp\n    filebucket { 'main':\n      path   => false,                # This is required for remote filebuckets.\n      server => 'puppet.example.com', # Optional; defaults to the configured primary Puppet server.\n    }\n\n    File { backup => main, }\n\nIf you are using multiple primary servers, you will want to\ncentralize the contents of the filebucket. Either configure your load\nbalancer to direct all filebucket traffic to a single primary server, or use\nsomething like an out-of-band rsync task to synchronize the content on all\nprimary servers.\n\n> **Note**: Enabling and using the backup option, and by extension the\n  filebucket resource, requires appropriate planning and management to ensure\n  that sufficient disk space is available for the file backups. Generally, you\n  can implement this using one of the following two options:\n  - Use a `find` command and `crontab` entry to retain only the last X days\n  of file backups. For example:\n\n  ```\n  find /opt/puppetlabs/server/data/puppetserver/bucket -type f -mtime +45 -atime +45 -print0 | xargs -0 rm\n  ```\n\n  - Restrict the directory to a maximum size after which the oldest items are removed.\n\nDefault: `false`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### checksum {#file-attribute-checksum}\n\nThe checksum type to use when determining whether to replace a file's contents.\n\nThe default checksum type is sha256.\n\nAllowed values:\n\n* `sha256`\n* `sha256lite`\n* `md5`\n* `md5lite`\n* `sha1`\n* `sha1lite`\n* `sha512`\n* `sha384`\n* `sha224`\n* `mtime`\n* `ctime`\n* `none`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### checksum_value {#file-attribute-checksum_value}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe checksum of the source contents. Only md5, sha256, sha224, sha384 and sha512\nare supported when specifying this parameter. If this parameter is set,\nsource_permissions will be assumed to be false, and ownership and permissions\nwill not be read from source.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### content {#file-attribute-content}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe desired contents of a file, as a string. This attribute is mutually\nexclusive with `source` and `target`.\n\nNewlines and tabs can be specified in double-quoted strings using\nstandard escaped syntax --- \\n for a newline, and \\t for a tab.\n\nWith very small files, you can construct content strings directly in\nthe manifest...\n\n    define resolve($nameserver1, $nameserver2, $domain, $search) {\n        $str = \"search ${search}\n            domain ${domain}\n            nameserver ${nameserver1}\n            nameserver ${nameserver2}\n            \"\n\n        file { '/etc/resolv.conf':\n          content => $str,\n        }\n    }\n\n...but for larger files, this attribute is more useful when combined with the\n[template](https://puppet.com/docs/puppet/latest/function.html#template)\nor [file](https://puppet.com/docs/puppet/latest/function.html#file)\nfunction.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### ctime {#file-attribute-ctime}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA read-only state to check the file ctime. On most modern \\*nix-like\nsystems, this is the time of the most recent change to the owner, group,\npermissions, or content of the file.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### force {#file-attribute-force}\n\nPerform the file operation even if it will destroy one or more directories.\nYou must use `force` in order to:\n\n* `purge` subdirectories\n* Replace directories with files or links\n* Remove a directory when `ensure => absent`\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### group {#file-attribute-group}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhich group should own the file.  Argument can be either a group\nname or a group ID.\n\nOn Windows, a user (such as \"Administrator\") can be set as a file's group\nand a group (such as \"Administrators\") can be set as a file's owner;\nhowever, a file's owner and group shouldn't be the same. (If the owner\nis also the group, files with modes like `\"0640\"` will cause log churn, as\nthey will always appear out of sync.)\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### ignore {#file-attribute-ignore}\n\nA parameter which omits action on files matching\nspecified patterns during recursion.  Uses Ruby's builtin globbing\nengine, so shell metacharacters such as `[a-z]*` are fully supported.\nMatches that would descend into the directory structure are ignored,\nsuch as `*/*`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### links {#file-attribute-links}\n\nHow to handle links during file actions.  During file copying,\n`follow` will copy the target file instead of the link and `manage`\nwill copy the link itself. When not copying, `manage` will manage\nthe link, and `follow` will manage the file to which the link points.\n\nDefault: `manage`\n\nAllowed values:\n\n* `follow`\n* `manage`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### max_files {#file-attribute-max_files}\n\nIn case the resource is a directory and the recursion is enabled, puppet will\ngenerate a new resource for each file file found, possible leading to\nan excessive number of resources generated without any control.\n\nSetting `max_files` will check the number of file resources that\nwill eventually be created and will raise a resource argument error if the\nlimit will be exceeded.\n\nUse value `0` to log a warning instead of raising an error.\n\nUse value `-1` to disable errors and warnings due to max files.\n\nDefault: `0`\n\nAllowed values:\n\n* `/^[0-9]+$/`\n* `/^-1$/`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### mode {#file-attribute-mode}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe desired permissions mode for the file, in symbolic or numeric\nnotation. This value **must** be specified as a string; do not use\nun-quoted numbers to represent file modes.\n\nIf the mode is omitted (or explicitly set to `undef`), Puppet does not\nenforce permissions on existing files and creates new files with\npermissions of `0644`.\n\nThe `file` type uses traditional Unix permission schemes and translates\nthem to equivalent permissions for systems which represent permissions\ndifferently, including Windows. For detailed ACL controls on Windows,\nyou can leave `mode` unmanaged and use\n[the puppetlabs/acl module.](https://forge.puppetlabs.com/puppetlabs/acl)\n\nNumeric modes should use the standard octal notation of\n`<SETUID/SETGID/STICKY><OWNER><GROUP><OTHER>` (for example, \"0644\").\n\n* Each of the \"owner,\" \"group,\" and \"other\" digits should be a sum of the\n  permissions for that class of users, where read = 4, write = 2, and\n  execute/search = 1.\n* The setuid/setgid/sticky digit is also a sum, where setuid = 4, setgid = 2,\n  and sticky = 1.\n* The setuid/setgid/sticky digit is optional. If it is absent, Puppet will\n  clear any existing setuid/setgid/sticky permissions. (So to make your intent\n  clear, you should use at least four digits for numeric modes.)\n* When specifying numeric permissions for directories, Puppet sets the search\n  permission wherever the read permission is set.\n\nSymbolic modes should be represented as a string of comma-separated\npermission clauses, in the form `<WHO><OP><PERM>`:\n\n* \"Who\" should be any combination of u (user), g (group), and o (other), or a (all)\n* \"Op\" should be = (set exact permissions), + (add select permissions),\n  or - (remove select permissions)\n* \"Perm\" should be one or more of:\n    * r (read)\n    * w (write)\n    * x (execute/search)\n    * t (sticky)\n    * s (setuid/setgid)\n    * X (execute/search if directory or if any one user can execute)\n    * u (user's current permissions)\n    * g (group's current permissions)\n    * o (other's current permissions)\n\nThus, mode `\"0664\"` could be represented symbolically as either `a=r,ug+w`\nor `ug=rw,o=r`.  However, symbolic modes are more expressive than numeric\nmodes: a mode only affects the specified bits, so `mode => 'ug+w'` will\nset the user and group write bits, without affecting any other bits.\n\nSee the manual page for GNU or BSD `chmod` for more details\non numeric and symbolic modes.\n\nOn Windows, permissions are translated as follows:\n\n* Owner and group names are mapped to Windows SIDs\n* The \"other\" class of users maps to the \"Everyone\" SID\n* The read/write/execute permissions map to the `FILE_GENERIC_READ`,\n  `FILE_GENERIC_WRITE`, and `FILE_GENERIC_EXECUTE` access rights; a\n  file's owner always has the `FULL_CONTROL` right\n* \"Other\" users can't have any permissions a file's group lacks,\n  and its group can't have any permissions its owner lacks; that is, \"0644\"\n  is an acceptable mode, but \"0464\" is not.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### mtime {#file-attribute-mtime}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA read-only state to check the file mtime. On \\*nix-like systems, this\nis the time of the most recent change to the content of the file.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### owner {#file-attribute-owner}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user to whom the file should belong.  Argument can be a user name or a\nuser ID.\n\nOn Windows, a group (such as \"Administrators\") can be set as a file's owner\nand a user (such as \"Administrator\") can be set as a file's group; however,\na file's owner and group shouldn't be the same. (If the owner is also\nthe group, files with modes like `\"0640\"` will cause log churn, as they\nwill always appear out of sync.)\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### provider {#file-attribute-provider}\n\nThe specific backend to use for this `file` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`posix`](#file-provider-posix)\n* [`windows`](#file-provider-windows)\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### purge {#file-attribute-purge}\n\nWhether unmanaged files should be purged. This option only makes\nsense when `ensure => directory` and `recurse => true`.\n\n* When recursively duplicating an entire directory with the `source`\n  attribute, `purge => true` will automatically purge any files\n  that are not in the source directory.\n* When managing files in a directory as individual resources,\n  setting `purge => true` will purge any files that aren't being\n  specifically managed.\n\nIf you have a filebucket configured, the purged files will be uploaded,\nbut if you do not, this will destroy data.\n\nUnless `force => true` is set, purging will **not** delete directories,\nalthough it will delete the files they contain.\n\nIf `recurselimit` is set and you aren't using `force => true`, purging\nwill obey the recursion limit; files in any subdirectories deeper than the\nlimit will be treated as unmanaged and left alone.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### recurse {#file-attribute-recurse}\n\nWhether to recursively manage the _contents_ of a directory. This attribute\nis only used when `ensure => directory` is set. The allowed values are:\n\n* `false` --- The default behavior. The contents of the directory will not be\n  automatically managed.\n* `remote` --- If the `source` attribute is set, Puppet will automatically\n  manage the contents of the source directory (or directories), ensuring\n  that equivalent files and directories exist on the target system and\n  that their contents match.\n\n  Using `remote` will disable the `purge` attribute, but results in faster\n  catalog application than `recurse => true`.\n\n  The `source` attribute is mandatory when `recurse => remote`.\n* `true` --- If the `source` attribute is set, this behaves similarly to\n  `recurse => remote`, automatically managing files from the source directory.\n\n  This also enables the `purge` attribute, which can delete unmanaged\n  files from a directory. See the description of `purge` for more details.\n\n  The `source` attribute is not mandatory when using `recurse => true`, so you\n  can enable purging in directories where all files are managed individually.\n\nBy default, setting recurse to `remote` or `true` will manage _all_\nsubdirectories. You can use the `recurselimit` attribute to limit the\nrecursion depth.\n\nAllowed values:\n\n* `true`\n* `false`\n* `remote`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### recurselimit {#file-attribute-recurselimit}\n\nHow far Puppet should descend into subdirectories, when using\n`ensure => directory` and either `recurse => true` or `recurse => remote`.\nThe recursion limit affects which files will be copied from the `source`\ndirectory, as well as which files can be purged when `purge => true`.\n\nSetting `recurselimit => 0` is the same as setting `recurse => false` ---\nPuppet will manage the directory, but all of its contents will be treated\nas unmanaged.\n\nSetting `recurselimit => 1` will manage files and directories that are\ndirectly inside the directory, but will not manage the contents of any\nsubdirectories.\n\nSetting `recurselimit => 2` will manage the direct contents of the\ndirectory, as well as the contents of the _first_ level of subdirectories.\n\nThis pattern continues for each incremental value of `recurselimit`.\n\nAllowed values:\n\n* `/^[0-9]+$/`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### replace {#file-attribute-replace}\n\nWhether to replace a file or symlink that already exists on the local system but\nwhose content doesn't match what the `source` or `content` attribute\nspecifies.  Setting this to false allows file resources to initialize files\nwithout overwriting future changes.  Note that this only affects content;\nPuppet will still manage ownership and permissions.\n\nDefault: `true`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### selinux_ignore_defaults {#file-attribute-selinux_ignore_defaults}\n\nIf this is set, Puppet will not call the SELinux function selabel_lookup to\nsupply defaults for the SELinux attributes (seluser, selrole,\nseltype, and selrange). In general, you should leave this set at its\ndefault and only set it to true when you need Puppet to not try to fix\nSELinux labels automatically.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### selrange {#file-attribute-selrange}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux range component of the context of the file should be.\nAny valid SELinux range component is accepted.  For example `s0` or\n`SystemHigh`.  If not specified, it defaults to the value returned by\nselabel_lookup for the file, if any exists.  Only valid on systems with\nSELinux support enabled and that have support for MCS (Multi-Category\nSecurity).\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### selrole {#file-attribute-selrole}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux role component of the context of the file should be.\nAny valid SELinux role component is accepted.  For example `role_r`.\nIf not specified, it defaults to the value returned by selabel_lookup for\nthe file, if any exists.  Only valid on systems with SELinux support\nenabled.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### seltype {#file-attribute-seltype}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux type component of the context of the file should be.\nAny valid SELinux type component is accepted.  For example `tmp_t`.\nIf not specified, it defaults to the value returned by selabel_lookup for\nthe file, if any exists.  Only valid on systems with SELinux support\nenabled.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### seluser {#file-attribute-seluser}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat the SELinux user component of the context of the file should be.\nAny valid SELinux user component is accepted.  For example `user_u`.\nIf not specified, it defaults to the value returned by selabel_lookup for\nthe file, if any exists.  Only valid on systems with SELinux support\nenabled.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### show_diff {#file-attribute-show_diff}\n\nWhether to display differences when the file changes, defaulting to\ntrue.  This parameter is useful for files that may contain passwords or\nother secret data, which might otherwise be included in Puppet reports or\nother insecure outputs.  If the global `show_diff` setting\nis false, then no diffs will be shown even if this parameter is true.\n\nDefault: `true`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### source {#file-attribute-source}\n\nA source file, which will be copied into place on the local system. This\nattribute is mutually exclusive with `content` and `target`. Allowed\nvalues are:\n\n* `puppet:` URIs, which point to files in modules or Puppet file server\nmount points.\n* Fully qualified paths to locally available files (including files on NFS\nshares or Windows mapped drives).\n* `file:` URIs, which behave the same as local file paths.\n* `http(s):` URIs, which point to files served by common web servers.\n\nThe normal form of a `puppet:` URI is:\n\n`puppet:///modules/<MODULE NAME>/<FILE PATH>`\n\nThis will fetch a file from a module on the Puppet master (or from a\nlocal module when using Puppet apply). Given a `modulepath` of\n`/etc/puppetlabs/code/modules`, the example above would resolve to\n`/etc/puppetlabs/code/modules/<MODULE NAME>/files/<FILE PATH>`.\n\nUnlike `content`, the `source` attribute can be used to recursively copy\ndirectories if the `recurse` attribute is set to `true` or `remote`. If\na source directory contains symlinks, use the `links` attribute to\nspecify whether to recreate links or follow them.\n\n_HTTP_ URIs cannot be used to recursively synchronize whole directory\ntrees. You cannot use `source_permissions` values other than `ignore`\nbecause HTTP servers do not transfer any metadata that translates to\nownership or permission details.\n\nPuppet determines if file content is synchronized by computing a checksum\nfor the local file and comparing it against the `checksum_value`\nparameter. If the `checksum_value` parameter is not specified for\n`puppet` and `file` sources, Puppet computes a checksum based on its\n`Puppet[:digest_algorithm]`. For `http(s)` sources, Puppet uses the\nfirst HTTP header it recognizes out of the following list:\n`X-Checksum-Sha256`, `X-Checksum-Sha1`, `X-Checksum-Md5` or `Content-MD5`.\nIf the server response does not include one of these headers, Puppet\ndefaults to using the `Last-Modified` header. Puppet updates the local\nfile if the header is newer than the modified time (mtime) of the local\nfile.\n\n_HTTP_ URIs can include a user information component so that Puppet can\nretrieve file metadata and content from HTTP servers that require HTTP Basic\nauthentication. For example `https://<user>:<pass>@<server>:<port>/path/to/file.`\n\nWhen connecting to _HTTPS_ servers, Puppet trusts CA certificates in the\npuppet-agent certificate bundle and the Puppet CA. You can configure Puppet\nto trust additional CA certificates using the `Puppet[:ssl_trust_store]`\nsetting.\n\nMultiple `source` values can be specified as an array, and Puppet will\nuse the first source that exists. This can be used to serve different\nfiles to different system types:\n\n    file { '/etc/nfs.conf':\n      source => [\n        \"puppet:///modules/nfs/conf.${host}\",\n        \"puppet:///modules/nfs/conf.${os['name']}\",\n        'puppet:///modules/nfs/conf'\n      ]\n    }\n\nAlternately, when serving directories recursively, multiple sources can\nbe combined by setting the `sourceselect` attribute to `all`.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### source_permissions {#file-attribute-source_permissions}\n\nWhether (and how) Puppet should copy owner, group, and mode permissions from\nthe `source` to `file` resources when the permissions are not explicitly\nspecified. (In all cases, explicit permissions will take precedence.)\nValid values are `use`, `use_when_creating`, and `ignore`:\n\n* `ignore` (the default) will never apply the owner, group, or mode from\n  the `source` when managing a file. When creating new files without explicit\n  permissions, the permissions they receive will depend on platform-specific\n  behavior. On POSIX, Puppet will use the umask of the user it is running as.\n  On Windows, Puppet will use the default DACL associated with the user it is\n  running as.\n* `use` will cause Puppet to apply the owner, group,\n  and mode from the `source` to any files it is managing.\n* `use_when_creating` will only apply the owner, group, and mode from the\n  `source` when creating a file; existing files will not have their permissions\n  overwritten.\n\nDefault: `ignore`\n\nAllowed values:\n\n* `use`\n* `use_when_creating`\n* `ignore`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### sourceselect {#file-attribute-sourceselect}\n\nWhether to copy all valid sources, or just the first one.  This parameter\nonly affects recursive directory copies; by default, the first valid\nsource is the only one used, but if this parameter is set to `all`, then\nall valid sources will have all of their contents copied to the local\nsystem. If a given file exists in more than one source, the version from\nthe earliest source in the list will be used.\n\nDefault: `first`\n\nAllowed values:\n\n* `first`\n* `all`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### staging_location {#file-attribute-staging_location}\n\nWhen rendering a file first render it to this location. The default\nlocation is the same path as the desired location with a unique filename.\nThis parameter is useful in conjuction with validate_cmd to test a\nfile before moving the file to it's final location.\nWARNING: File replacement is only guaranteed to be atomic if the staging\nlocation is on the same filesystem as the final location.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### target {#file-attribute-target}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe target for creating a link.  Currently, symlinks are the\nonly type supported. This attribute is mutually exclusive with `source`\nand `content`.\n\nSymlink targets can be relative, as well as absolute:\n\n    # (Useful on Solaris)\n    file { '/etc/inetd.conf':\n      ensure => link,\n      target => 'inet/inetd.conf',\n    }\n\nDirectories of symlinks can be served recursively by instead using the\n`source` attribute, setting `ensure` to `directory`, and setting the\n`links` attribute to `manage`.\n\nAllowed values:\n\n* `notlink`\n* `/./`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### type {#file-attribute-type}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA read-only state to check the file type.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### validate_cmd {#file-attribute-validate_cmd}\n\nA command for validating the file's syntax before replacing it. If\nPuppet would need to rewrite a file due to new `source` or `content`, it\nwill check the new content's validity first. If validation fails, the file\nresource will fail.\n\nThis command must have a fully qualified path, and should contain a\npercent (`%`) token where it would expect an input file. It must exit `0`\nif the syntax is correct, and non-zero otherwise. The command will be\nrun on the target system while applying the catalog, not on the primary Puppet server.\n\nExample:\n\n    file { '/etc/apache2/apache2.conf':\n      content      => 'example',\n      validate_cmd => '/usr/sbin/apache2 -t -f %',\n    }\n\nThis would replace apache2.conf only if the test returned true.\n\nNote that if a validation command requires a `%` as part of its text,\nyou can specify a different placeholder token with the\n`validate_replacement` attribute.\n\n([↑ Back to file attributes](#file-attributes))\n\n\n#### validate_replacement {#file-attribute-validate_replacement}\n\nThe replacement string in a `validate_cmd` that will be replaced\nwith an input file name.\n\nDefault: `%`\n\n([↑ Back to file attributes](#file-attributes))\n\n\n### Providers {#file-providers}\n\n#### posix {#file-provider-posix}\n\nUses POSIX functionality to manage file ownership and permissions.\n\n* Confined to: `feature == posix`\n* Supported features: `manages_symlinks`\n\n#### windows {#file-provider-windows}\n\nUses Microsoft Windows functionality to manage file ownership and permissions.\n\n* Confined to: `os.name == windows`\n\n### Provider Features {#file-provider-features}\n\nAvailable features:\n\n* `manages_symlinks` --- The provider can manage symbolic links.\n\nProvider support:\n\n* **posix** - _manages symlinks_\n* **windows** - No supported Provider features\n  \n\n\n\n\n"
  },
  {
    "path": "references/types/filebucket.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: filebucket'\ncanonical: \"/puppet/latest/types/filebucket.html\"\n---\n\n# Resource Type: filebucket\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## filebucket\n\n* [Attributes](#filebucket-attributes)\n\n### Description {#filebucket-description}\n\nA repository for storing and retrieving file content by cryptographic checksum. Can\nbe local to each agent node, or centralized on a primary Puppet server. All\npuppet servers provide a filebucket service that agent nodes can access\nvia HTTP, but you must declare a filebucket resource before any agents\nwill do so.\n\nFilebuckets are used for the following features:\n\n- **Content backups.** If the `file` type's `backup` attribute is set to\n  the name of a filebucket, Puppet will back up the _old_ content whenever\n  it rewrites a file; see the documentation for the `file` type for more\n  details. These backups can be used for manual recovery of content, but\n  are more commonly used to display changes and differences in a tool like\n  Puppet Dashboard.\n\nTo use a central filebucket for backups, you will usually want to declare\na filebucket resource and a resource default for the `backup` attribute\nin site.pp:\n\n    # /etc/puppetlabs/puppet/manifests/site.pp\n    filebucket { 'main':\n      path   => false,                # This is required for remote filebuckets.\n      server => 'puppet.example.com', # Optional; defaults to the configured primary server.\n    }\n\n    File { backup => main, }\n\nPuppet Servers automatically provide the filebucket service, so\nthis will work in a default configuration. If you have a heavily\nrestricted Puppet Server `auth.conf` file, you may need to allow access to the\n`file_bucket_file` endpoint.\n\n### Attributes {#filebucket-attributes}\n\n<pre><code>filebucket { 'resource title':\n  <a href=\"#filebucket-attribute-name\">name</a>   =&gt; <em># <strong>(namevar)</strong> The name of the...</em>\n  <a href=\"#filebucket-attribute-path\">path</a>   =&gt; <em># The path to the _local_ filebucket; defaults to...</em>\n  <a href=\"#filebucket-attribute-port\">port</a>   =&gt; <em># The port on which the remote server is...</em>\n  <a href=\"#filebucket-attribute-server\">server</a> =&gt; <em># The server providing the remote filebucket...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#filebucket-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the filebucket.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n#### path {#filebucket-attribute-path}\n\nThe path to the _local_ filebucket; defaults to the value of the\n`clientbucketdir` setting.  To use a remote filebucket, you _must_ set\nthis attribute to `false`.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n#### port {#filebucket-attribute-port}\n\nThe port on which the remote server is listening.\n\nThis setting is _only_ consulted if the `path` attribute is set to `false`.\n\nIf this attribute is not specified, the first entry in the `server_list`\nconfiguration setting is used, followed by the value of the `serverport`\nsetting if `server_list` is not set.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n#### server {#filebucket-attribute-server}\n\nThe server providing the remote filebucket service.\n\nThis setting is _only_ consulted if the `path` attribute is set to `false`.\n\nIf this attribute is not specified, the first entry in the `server_list`\nconfiguration setting is used, followed by the value of the `server` setting\nif `server_list` is not set.\n\n([↑ Back to filebucket attributes](#filebucket-attributes))\n\n\n\n\n\n"
  },
  {
    "path": "references/types/group.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: group'\ncanonical: \"/puppet/latest/types/group.html\"\n---\n\n# Resource Type: group\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## group\n\n* [Attributes](#group-attributes)\n* [Providers](#group-providers)\n* [Provider Features](#group-provider-features)\n\n### Description {#group-description}\n\nManage groups. On most platforms this can only create groups.\nGroup membership must be managed on individual users.\n\nOn some platforms such as OS X, group membership is managed as an\nattribute of the group, not the user record. Providers must have\nthe feature 'manages_members' to manage the 'members' property of\na group record.\n\n### Attributes {#group-attributes}\n\n<pre><code>group { 'resource title':\n  <a href=\"#group-attribute-name\">name</a>                 =&gt; <em># <strong>(namevar)</strong> The group name. While naming limitations vary by </em>\n  <a href=\"#group-attribute-ensure\">ensure</a>               =&gt; <em># Create or remove the group.  Default: `present`  </em>\n  <a href=\"#group-attribute-allowdupe\">allowdupe</a>            =&gt; <em># Whether to allow duplicate GIDs.  Default...</em>\n  <a href=\"#group-attribute-attribute_membership\">attribute_membership</a> =&gt; <em># AIX only. Configures the behavior of the...</em>\n  <a href=\"#group-attribute-attributes\">attributes</a>           =&gt; <em># Specify group AIX attributes, as an array of...</em>\n  <a href=\"#group-attribute-auth_membership\">auth_membership</a>      =&gt; <em># Configures the behavior of the `members...</em>\n  <a href=\"#group-attribute-forcelocal\">forcelocal</a>           =&gt; <em># Forces the management of local accounts when...</em>\n  <a href=\"#group-attribute-gid\">gid</a>                  =&gt; <em># The group ID.  Must be specified numerically....</em>\n  <a href=\"#group-attribute-ia_load_module\">ia_load_module</a>       =&gt; <em># The name of the I&A module to use to manage this </em>\n  <a href=\"#group-attribute-members\">members</a>              =&gt; <em># The members of the group. For platforms or...</em>\n  <a href=\"#group-attribute-provider\">provider</a>             =&gt; <em># The specific backend to use for this `group...</em>\n  <a href=\"#group-attribute-system\">system</a>               =&gt; <em># Whether the group is a system group with lower...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#group-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe group name. While naming limitations vary by operating system,\nit is advisable to restrict names to the lowest common denominator,\nwhich is a maximum of 8 characters beginning with a letter.\n\nNote that Puppet considers group names to be case-sensitive, regardless\nof the platform's own rules; be sure to always use the same case when\nreferring to a given group.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### ensure {#group-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nCreate or remove the group.\n\nDefault: `present`\n\nAllowed values:\n\n* `present`\n* `absent`\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### allowdupe {#group-attribute-allowdupe}\n\nWhether to allow duplicate GIDs.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### attribute_membership {#group-attribute-attribute_membership}\n\nAIX only. Configures the behavior of the `attributes` parameter.\n\n* `minimum` (default) --- The provided list of attributes is partial, and Puppet\n  **ignores** any attributes that aren't listed there.\n* `inclusive` --- The provided list of attributes is comprehensive, and\n  Puppet **purges** any attributes that aren't listed there.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### attributes {#group-attribute-attributes}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify group AIX attributes, as an array of `'key=value'` strings. This\nparameter's behavior can be configured with `attribute_membership`.\n\nRequires features manages_aix_lam.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### auth_membership {#group-attribute-auth_membership}\n\nConfigures the behavior of the `members` parameter.\n\n* `false` (default) --- The provided list of group members is partial,\n  and Puppet **ignores** any members that aren't listed there.\n* `true` --- The provided list of of group members is comprehensive, and\n  Puppet **purges** any members that aren't listed there.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### forcelocal {#group-attribute-forcelocal}\n\nForces the management of local accounts when accounts are also\nbeing managed by some other Name Switch Service (NSS). For AIX, refer to the `ia_load_module` parameter.\n\nThis option relies on your operating system's implementation of `luser*` commands, such as `luseradd` , `lgroupadd`, and `lusermod`. The `forcelocal` option could behave unpredictably in some circumstances. If the tools it depends on are not available, it might have no effect at all.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\nRequires features manages_local_users_and_groups.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### gid {#group-attribute-gid}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe group ID.  Must be specified numerically.  If no group ID is\nspecified when creating a new group, then one will be chosen\nautomatically according to local system standards. This will likely\nresult in the same group having different GIDs on different systems,\nwhich is not recommended.\n\nOn Windows, this property is read-only and will return the group's security\nidentifier (SID).\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### ia_load_module {#group-attribute-ia_load_module}\n\nThe name of the I&A module to use to manage this group.\nThis should be set to `files` if managing local groups.\n\nRequires features manages_aix_lam.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### members {#group-attribute-members}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe members of the group. For platforms or directory services where group\nmembership is stored in the group objects, not the users. This parameter's\nbehavior can be configured with `auth_membership`.\n\nRequires features manages_members.\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### provider {#group-attribute-provider}\n\nThe specific backend to use for this `group` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`aix`](#group-provider-aix)\n* [`directoryservice`](#group-provider-directoryservice)\n* [`groupadd`](#group-provider-groupadd)\n* [`ldap`](#group-provider-ldap)\n* [`pw`](#group-provider-pw)\n* [`windows_adsi`](#group-provider-windows_adsi)\n\n([↑ Back to group attributes](#group-attributes))\n\n\n#### system {#group-attribute-system}\n\nWhether the group is a system group with lower GID.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to group attributes](#group-attributes))\n\n\n### Providers {#group-providers}\n\n#### aix {#group-provider-aix}\n\nGroup management for AIX.\n\n* Required binaries: `/usr/bin/chgroup`, `/usr/bin/mkgroup`, `/usr/sbin/lsgroup`, `/usr/sbin/rmgroup`\n* Confined to: `os.name == aix`\n* Default for: `[\"os.name\", \"aix\"] == `\n* Supported features: `manages_aix_lam`, `manages_local_users_and_groups`, `manages_members`\n\n#### directoryservice {#group-provider-directoryservice}\n\nGroup management using DirectoryService on OS X.\n\n* Required binaries: `/usr/bin/dscl`\n* Confined to: `os.name == darwin`\n* Default for: `[\"os.name\", \"darwin\"] == `\n* Supported features: `manages_members`\n\n#### groupadd {#group-provider-groupadd}\n\nGroup management via `groupadd` and its ilk. The default for most platforms.\n\nTo use the `forcelocal` parameter, you need to install the `libuser` package (providing\n `/usr/sbin/lgroupadd` and `/usr/sbin/luseradd`).\n\n* Required binaries: `groupadd`, `groupdel`, `groupmod`\n\n#### ldap {#group-provider-ldap}\n\nGroup management via LDAP.\n\nThis provider requires that you have valid values for all of the\nLDAP-related settings in `puppet.conf`, including `ldapbase`.  You will\nalmost definitely need settings for `ldapuser` and `ldappassword` in order\nfor your clients to write to LDAP.\n\nNote that this provider will automatically generate a GID for you if you do\nnot specify one, but it is a potentially expensive operation, as it\niterates across all existing groups to pick the appropriate next one.\n\n* Confined to: `feature == ldap`, `false == (Puppet[:ldapuser] == \"\")`\n\n#### pw {#group-provider-pw}\n\nGroup management via `pw` on FreeBSD and DragonFly BSD.\n\n* Required binaries: `pw`\n* Confined to: `os.name == [:freebsd, :dragonfly]`\n* Default for: `[\"os.name\", \"[:freebsd, :dragonfly]\"] == `\n* Supported features: `manages_members`\n\n#### windows_adsi {#group-provider-windows_adsi}\n\nLocal group management for Windows. Group members can be both users and groups.\nAdditionally, local groups can contain domain users.\n\n* Confined to: `os.name == windows`\n* Default for: `[\"os.name\", \"windows\"] == `\n* Supported features: `manages_members`\n\n### Provider Features {#group-provider-features}\n\nAvailable features:\n\n* `manages_aix_lam` --- The provider can manage AIX Loadable Authentication Module (LAM) system.\n* `manages_local_users_and_groups` --- Allows local groups to be managed on systems that also use some other remote Name Switch Service (NSS) method of managing accounts.\n* `manages_members` --- For directories where membership is an attribute of groups not users.\n* `system_groups` --- The provider allows you to create system groups with lower GIDs.\n\nProvider support:\n\n* **aix** - _manages aix lam, manages members, manages local users and groups_\n* **directoryservice** - _manages members_\n* **groupadd** - No supported Provider features\n* **ldap** - No supported Provider features\n* **pw** - _manages members_\n* **windows_adsi** - _manages members_\n  \n\n\n\n\n"
  },
  {
    "path": "references/types/notify.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: notify'\ncanonical: \"/puppet/latest/types/notify.html\"\n---\n\n# Resource Type: notify\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## notify\n\n* [Attributes](#notify-attributes)\n\n### Description {#notify-description}\n\nSends an arbitrary message, specified as a string, to the agent run-time log. It's important to note that the notify resource type is not idempotent. As a result, notifications are shown as a change on every Puppet run.\n\n### Attributes {#notify-attributes}\n\n<pre><code>notify { 'resource title':\n  <a href=\"#notify-attribute-name\">name</a>     =&gt; <em># <strong>(namevar)</strong> An arbitrary tag for your own reference; the...</em>\n  <a href=\"#notify-attribute-message\">message</a>  =&gt; <em># The message to be sent to the log. Note that the </em>\n  <a href=\"#notify-attribute-withpath\">withpath</a> =&gt; <em># Whether to show the full object path.  Default...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#notify-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nAn arbitrary tag for your own reference; the name of the message.\n\n([↑ Back to notify attributes](#notify-attributes))\n\n\n#### message {#notify-attribute-message}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe message to be sent to the log. Note that the value specified must be a string.\n\n([↑ Back to notify attributes](#notify-attributes))\n\n\n#### withpath {#notify-attribute-withpath}\n\nWhether to show the full object path.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to notify attributes](#notify-attributes))\n\n\n\n\n\n"
  },
  {
    "path": "references/types/overview.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: Resource types overview\ncanonical: \"/puppet/latest/types/overview.md\"\n---\n\n# Resource types overview\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n## List of resource types\n\n\n* [exec](./exec.md)\n* [file](./file.md)\n* [filebucket](./filebucket.md)\n* [group](./group.md)\n* [notify](./notify.md)\n* [package](./package.md)\n* [resources](./resources.md)\n* [schedule](./schedule.md)\n* [service](./service.md)\n* [stage](./stage.md)\n* [tidy](./tidy.md)\n* [user](./user.md)\n\n\n## About resource types\n\n### Built-in types and custom types\n\nThis is the documentation for Puppet's built-in resource types and providers. Additional resource types are distributed in Puppet modules.\n\nYou can find and install modules by browsing the\n[Puppet Forge](http://forge.puppet.com). See each module's documentation for\ninformation on how to use its custom resource types. For more information about creating custom types, see [Custom resources](/docs/puppet/latest/custom_resources.html).\n\n> As of Puppet 6.0, some resource types were removed from Puppet and repackaged as individual modules. These supported type modules are still included in the `puppet-agent` package, so you don't have to download them from the Forge. See the complete list of affected types in the [supported type modules](#supported-type-modules-in-puppet-agent) section.\n\n### Declaring resources\n\nTo manage resources on a target system, declare them in Puppet\nmanifests. For more details, see\n[the resources page of the Puppet language reference.](/docs/puppet/latest/lang_resources.html)\n\nYou can also browse and manage resources interactively using the\n`puppet resource` subcommand; run `puppet resource --help` for more information.\n\n### Namevars and titles\n\nAll types have a special attribute called the _namevar_. This is the attribute\nused to uniquely identify a resource on the target system.\n\nEach resource has a specific namevar attribute, which is listed on this page in\neach resource's reference. If you don't specify a value for the namevar, its\nvalue defaults to the resource's _title_.\n\n**Example of a title as a default namevar:**\n\n```puppet\nfile { '/etc/passwd':\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nIn this code, `/etc/passwd` is the _title_ of the file resource.\n\nThe file type's namevar is `path`. Because we didn't provide a `path` value in\nthis example, the value defaults to the title, `/etc/passwd`.\n\n**Example of a namevar:**\n\n```puppet\nfile { 'passwords':\n  path  => '/etc/passwd',\n  owner => 'root',\n  group => 'root',\n  mode  => '0644',\n}\n```\n\nThis example is functionally similar to the previous example. Its `path`\nnamevar attribute has an explicitly set value separate from the title, so\nits name is still `/etc/passwd`.\n\nOther Puppet code can refer to this resource as `File['/etc/passwd']` to\ndeclare relationships.\n\n### Attributes, parameters, properties\n\nThe _attributes_ (sometimes called _parameters_) of a resource determine its\ndesired state. They either directly modify the system (internally, these are\ncalled \"properties\") or they affect how the resource behaves (for instance,\nadding a search path for `exec` resources or controlling directory recursion\non `file` resources).\n\n### Providers\n\n_Providers_ implement the same resource type on different kinds of systems.\nThey usually do this by calling out to external commands.\n\nAlthough Puppet automatically selects an appropriate default provider, you\ncan override the default with the `provider` attribute. (For example, `package`\nresources on Red Hat systems default to the `yum` provider, but you can specify\n`provider => gem` to install Ruby libraries with the `gem` command.)\n\nProviders often specify binaries that they require. Fully qualified binary\npaths indicate that the binary must exist at that specific path, and\nunqualified paths indicate that Puppet searches for the binary using the\nshell path.\n\n### Features\n\n_Features_ are abilities that some providers might not support. Generally, a\nfeature corresponds to some allowed values for a resource attribute.\n\nThis is often the case with the `ensure` attribute. In most types, Puppet\ndoesn't create new resources when omitting `ensure` but still modifies existing\nresources to match specifications in the manifest. However, in some types this\nisn't always the case, or additional values provide more granular control. For\nexample, if a `package` provider supports the `purgeable` feature, you can\nspecify `ensure => purged` to delete configuration files installed by the\npackage.\n\nResource types define the set of features they can use, and providers can\ndeclare which features they provide.\n\n## Puppet 6.0 type changes\n\nIn Puppet 6.0, we removed some of Puppet's built-in types and moved them into individual modules.\n\n### Supported type modules in `puppet-agent`\n\nThe following types are included in supported modules on the Forge. However, they are also included in the `puppet-agent` package, so you do not have to install them separately. See each module's README for detailed information about that type.\n\n- [`augeas`](https://forge.puppet.com/puppetlabs/augeas_core)\n- [`cron`](https://forge.puppet.com/puppetlabs/cron_core)\n- [`host`](https://forge.puppet.com/puppetlabs/host_core)\n- [`mount`](https://forge.puppet.com/puppetlabs/mount_core)\n- [`scheduled_task`](https://forge.puppet.com/puppetlabs/scheduled_task)\n- [`selboolean`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`selmodule`](https://forge.puppet.com/puppetlabs/selinux_core)\n- [`ssh_authorized_key`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`sshkey`](https://forge.puppet.com/puppetlabs/sshkeys_core)\n- [`yumrepo`](https://forge.puppet.com/puppetlabs/yumrepo_core)\n- [`zfs`](https://forge.puppet.com/puppetlabs/zfs_core)\n- [`zone`](https://forge.puppet.com/puppetlabs/zone_core)\n- [`zpool`](https://forge.puppet.com/puppetlabs/zfs_core)\n\n### Type modules available on the Forge\n\nThe following types are contained in modules that are maintained, but are not repackaged into Puppet agent. If you need to use them, you must install the modules separately. \n\n- [`k5login`](https://forge.puppet.com/puppetlabs/k5login_core)\n- [`mailalias`](https://forge.puppet.com/puppetlabs/mailalias_core)\n- [`maillist`](https://forge.puppet.com/puppetlabs/maillist_core)\n\n### Deprecated types\n\nThe following types were deprecated with Puppet 6.0.0. They are available in modules, but are not updated. If you need to use them, you must install the modules separately.\n\n- [`computer`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`interface`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`macauthorization`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [`mcx`](https://forge.puppet.com/puppetlabs/macdslocal_core)\n- [The Nagios types](https://forge.puppet.com/puppetlabs/nagios_core)\n- [`router`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n- [`vlan`](https://github.com/puppetlabs/puppetlabs-network_device_core) (Use the updated [`cisco_ios module`](https://forge.puppet.com/puppetlabs/cisco_ios/readme) instead.\n\n## Puppet core types\n\nFor a list of core Puppet types, see the [core types cheat sheet][core-types-cheatsheet].\n"
  },
  {
    "path": "references/types/package.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: package'\ncanonical: \"/puppet/latest/types/package.html\"\n---\n\n# Resource Type: package\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## package\n\n* [Attributes](#package-attributes)\n* [Providers](#package-providers)\n* [Provider Features](#package-provider-features)\n\n### Description {#package-description}\n\nManage packages.  There is a basic dichotomy in package\nsupport right now:  Some package types (such as yum and apt) can\nretrieve their own package files, while others (such as rpm and sun)\ncannot.  For those package formats that cannot retrieve their own files,\nyou can use the `source` parameter to point to the correct file.\n\nPuppet will automatically guess the packaging format that you are\nusing based on the platform you are on, but you can override it\nusing the `provider` parameter; each provider defines what it\nrequires in order to function, and you must meet those requirements\nto use a given provider.\n\nYou can declare multiple package resources with the same `name` as long\nas they have unique titles, and specify different providers and commands.\n\nNote that you must use the _title_ to make a reference to a package\nresource; `Package[<NAME>]` is not a synonym for `Package[<TITLE>]` like\nit is for many other resource types.\n\n**Autorequires:** If Puppet is managing the files specified as a\npackage's `adminfile`, `responsefile`, or `source`, the package\nresource will autorequire those files.\n\n### Attributes {#package-attributes}\n\n<pre><code>package { 'resource title':\n  <a href=\"#package-attribute-command\">command</a>              =&gt; <em># <strong>(namevar)</strong> The targeted command to use when managing a...</em>\n  <a href=\"#package-attribute-name\">name</a>                 =&gt; <em># <strong>(namevar)</strong> The package name.  This is the name that the...</em>\n  <a href=\"#package-attribute-ensure\">ensure</a>               =&gt; <em># What state the package should be in. On...</em>\n  <a href=\"#package-attribute-adminfile\">adminfile</a>            =&gt; <em># A file containing package defaults for...</em>\n  <a href=\"#package-attribute-allow_virtual\">allow_virtual</a>        =&gt; <em># Specifies if virtual package names are allowed...</em>\n  <a href=\"#package-attribute-allowcdrom\">allowcdrom</a>           =&gt; <em># Tells apt to allow cdrom sources in the...</em>\n  <a href=\"#package-attribute-category\">category</a>             =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-configfiles\">configfiles</a>          =&gt; <em># Whether to keep or replace modified config files </em>\n  <a href=\"#package-attribute-description\">description</a>          =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-enable_only\">enable_only</a>          =&gt; <em># Tells `dnf module` to only enable a specific...</em>\n  <a href=\"#package-attribute-flavor\">flavor</a>               =&gt; <em># OpenBSD and DNF modules support 'flavors', which </em>\n  <a href=\"#package-attribute-install_only\">install_only</a>         =&gt; <em># It should be set for packages that should only...</em>\n  <a href=\"#package-attribute-install_options\">install_options</a>      =&gt; <em># An array of additional options to pass when...</em>\n  <a href=\"#package-attribute-instance\">instance</a>             =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-mark\">mark</a>                 =&gt; <em># Set to hold to tell Debian apt/Solaris pkg to...</em>\n  <a href=\"#package-attribute-package_settings\">package_settings</a>     =&gt; <em># Settings that can change the contents or...</em>\n  <a href=\"#package-attribute-platform\">platform</a>             =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-provider\">provider</a>             =&gt; <em># The specific backend to use for this `package...</em>\n  <a href=\"#package-attribute-reinstall_on_refresh\">reinstall_on_refresh</a> =&gt; <em># Whether this resource should respond to refresh...</em>\n  <a href=\"#package-attribute-responsefile\">responsefile</a>         =&gt; <em># A file containing any necessary answers to...</em>\n  <a href=\"#package-attribute-root\">root</a>                 =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-source\">source</a>               =&gt; <em># Where to find the package file. This is mostly...</em>\n  <a href=\"#package-attribute-status\">status</a>               =&gt; <em># A read-only parameter set by the...</em>\n  <a href=\"#package-attribute-uninstall_options\">uninstall_options</a>    =&gt; <em># An array of additional options to pass when...</em>\n  <a href=\"#package-attribute-vendor\">vendor</a>               =&gt; <em># A read-only parameter set by the...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### command {#package-attribute-command}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe targeted command to use when managing a package:\n\n  package { 'mysql':\n    provider => gem,\n  }\n\n  package { 'mysql-opt':\n    name     => 'mysql',\n    provider => gem,\n    command  => '/opt/ruby/bin/gem',\n  }\n\nEach provider defines a package management command and uses the first\ninstance of the command found in the PATH.\n\nProviders supporting the targetable feature allow you to specify the\nabsolute path of the package management command. Specifying the absolute\npath is useful when multiple instances of the command are installed, or\nthe command is not in the PATH.\n\nDefault: `default`\n\nRequires features targetable.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### name {#package-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe package name.  This is the name that the packaging\nsystem uses internally, which is sometimes (especially on Solaris)\na name that is basically useless to humans.  If a package goes by\nseveral names, you can use a single title and then set the name\nconditionally:\n\n    # In the 'openssl' class\n    $ssl = $os['name'] ? {\n      solaris => SMCossl,\n      default => openssl\n    }\n\n    package { 'openssl':\n      ensure => installed,\n      name   => $ssl,\n    }\n\n    ...\n\n    $ssh = $os['name'] ? {\n      solaris => SMCossh,\n      default => openssh\n    }\n\n    package { 'openssh':\n      ensure  => installed,\n      name    => $ssh,\n      require => Package['openssl'],\n    }\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### ensure {#package-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhat state the package should be in. On packaging systems that can\nretrieve new packages on their own, you can choose which package to\nretrieve by specifying a version number or `latest` as the ensure\nvalue. On packaging systems that manage configuration files separately\nfrom \"normal\" system files, you can uninstall config files by\nspecifying `purged` as the ensure value. This defaults to `installed`.\n\nVersion numbers must match the full version to install, including\nrelease if the provider uses a release moniker. For\nexample, to install the bash package from the rpm\n`bash-4.1.2-29.el6.x86_64.rpm`, use the string `'4.1.2-29.el6'`.\n\nOn supported providers, version ranges can also be ensured. For example,\ninequalities: `<2.0.0`, or intersections: `>1.0.0 <2.0.0`.\n\nDefault: `installed`\n\nAllowed values:\n\n* `present`\n* `absent`\n* `purged`\n* `disabled`\n* `installed`\n* `latest`\n* `/./`\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### adminfile {#package-attribute-adminfile}\n\nA file containing package defaults for installing packages.\n\nThis attribute is only used on Solaris. Its value should be a path to a\nlocal file stored on the target system. Solaris's package tools expect\neither an absolute file path or a relative path to a file in\n`/var/sadm/install/admin`.\n\nThe value of `adminfile` will be passed directly to the `pkgadd` or\n`pkgrm` command with the `-a <ADMINFILE>` option.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### allow_virtual {#package-attribute-allow_virtual}\n\nSpecifies if virtual package names are allowed for install and uninstall.\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\nRequires features virtual_packages.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### allowcdrom {#package-attribute-allowcdrom}\n\nTells apt to allow cdrom sources in the sources.list file.\nNormally apt will bail if you try this.\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### category {#package-attribute-category}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### configfiles {#package-attribute-configfiles}\n\nWhether to keep or replace modified config files when installing or\nupgrading a package. This only affects the `apt` and `dpkg` providers.\n\nDefault: `keep`\n\nAllowed values:\n\n* `keep`\n* `replace`\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### description {#package-attribute-description}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### enable_only {#package-attribute-enable_only}\n\nTells `dnf module` to only enable a specific module, instead\nof installing its default profile.\n\nModules with no default profile will be enabled automatically\nwithout the use of this parameter.\n\nConflicts with the `flavor` property, which selects a profile\nto install.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### flavor {#package-attribute-flavor}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nOpenBSD and DNF modules support 'flavors', which are\nfurther specifications for which type of package you want.\n\nRequires features supports_flavors.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### install_only {#package-attribute-install_only}\n\nIt should be set for packages that should only ever be installed,\nnever updated. Kernels in particular fall into this category.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\nRequires features install_only.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### install_options {#package-attribute-install_options}\n\nAn array of additional options to pass when installing a package. These\noptions are package-specific, and should be documented by the software\nvendor.  One commonly implemented option is `INSTALLDIR`:\n\n    package { 'mysql':\n      ensure          => installed,\n      source          => 'N:/packages/mysql-5.5.16-winx64.msi',\n      install_options => [ '/S', { 'INSTALLDIR' => 'C:\\\\mysql-5.5' } ],\n    }\n\nEach option in the array can either be a string or a hash, where each\nkey and value pair are interpreted in a provider specific way.  Each\noption will automatically be quoted when passed to the install command.\n\nWith Windows packages, note that file paths in an install option must\nuse backslashes. (Since install options are passed directly to the\ninstallation command, forward slashes won't be automatically converted\nlike they are in `file` resources.) Note also that backslashes in\ndouble-quoted strings _must_ be escaped and backslashes in single-quoted\nstrings _can_ be escaped.\n\nRequires features install_options.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### instance {#package-attribute-instance}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### mark {#package-attribute-mark}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSet to hold to tell Debian apt/Solaris pkg to hold the package version\n\n#{mark_doc}\nDefault is \"none\". Mark can be specified with or without `ensure`,\nif `ensure` is missing will default to \"present\".\n\nMark cannot be specified together with \"purged\", or \"absent\"\nvalues for `ensure`.\n\nAllowed values:\n\n* `hold`\n* `none`\n\nRequires features holdable.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### package_settings {#package-attribute-package_settings}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSettings that can change the contents or configuration of a package.\n\nThe formatting and effects of package_settings are provider-specific; any\nprovider that implements them must explain how to use them in its\ndocumentation. (Our general expectation is that if a package is\ninstalled but its settings are out of sync, the provider should\nre-install that package with the desired settings.)\n\nAn example of how package_settings could be used is FreeBSD's port build\noptions --- a future version of the provider could accept a hash of options,\nand would reinstall the port if the installed version lacked the correct\nsettings.\n\n    package { 'www/apache22':\n      package_settings => { 'SUEXEC' => false }\n    }\n\nAgain, check the documentation of your platform's package provider to see\nthe actual usage.\n\nRequires features package_settings.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### platform {#package-attribute-platform}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### provider {#package-attribute-provider}\n\nThe specific backend to use for this `package` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`aix`](#package-provider-aix)\n* [`appdmg`](#package-provider-appdmg)\n* [`apple`](#package-provider-apple)\n* [`apt`](#package-provider-apt)\n* [`aptitude`](#package-provider-aptitude)\n* [`aptrpm`](#package-provider-aptrpm)\n* [`blastwave`](#package-provider-blastwave)\n* [`dnf`](#package-provider-dnf)\n* [`dnfmodule`](#package-provider-dnfmodule)\n* [`dpkg`](#package-provider-dpkg)\n* [`fink`](#package-provider-fink)\n* [`freebsd`](#package-provider-freebsd)\n* [`gem`](#package-provider-gem)\n* [`hpux`](#package-provider-hpux)\n* [`macports`](#package-provider-macports)\n* [`nim`](#package-provider-nim)\n* [`openbsd`](#package-provider-openbsd)\n* [`opkg`](#package-provider-opkg)\n* [`pacman`](#package-provider-pacman)\n* [`pip2`](#package-provider-pip2)\n* [`pip3`](#package-provider-pip3)\n* [`pip`](#package-provider-pip)\n* [`pkg`](#package-provider-pkg)\n* [`pkgdmg`](#package-provider-pkgdmg)\n* [`pkgin`](#package-provider-pkgin)\n* [`pkgng`](#package-provider-pkgng)\n* [`pkgutil`](#package-provider-pkgutil)\n* [`portage`](#package-provider-portage)\n* [`ports`](#package-provider-ports)\n* [`portupgrade`](#package-provider-portupgrade)\n* [`puppet_gem`](#package-provider-puppet_gem)\n* [`puppetserver_gem`](#package-provider-puppetserver_gem)\n* [`rpm`](#package-provider-rpm)\n* [`rug`](#package-provider-rug)\n* [`sun`](#package-provider-sun)\n* [`sunfreeware`](#package-provider-sunfreeware)\n* [`tdnf`](#package-provider-tdnf)\n* [`up2date`](#package-provider-up2date)\n* [`urpmi`](#package-provider-urpmi)\n* [`windows`](#package-provider-windows)\n* [`xbps`](#package-provider-xbps)\n* [`yum`](#package-provider-yum)\n* [`zypper`](#package-provider-zypper)\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### reinstall_on_refresh {#package-attribute-reinstall_on_refresh}\n\nWhether this resource should respond to refresh events (via `subscribe`,\n`notify`, or the `~>` arrow) by reinstalling the package. Only works for\nproviders that support the `reinstallable` feature.\n\nThis is useful for source-based distributions, where you may want to\nrecompile a package if the build options change.\n\nIf you use this, be careful of notifying classes when you want to restart\nservices. If the class also contains a refreshable package, doing so could\ncause unnecessary re-installs.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### responsefile {#package-attribute-responsefile}\n\nA file containing any necessary answers to questions asked by\nthe package.  This is currently used on Solaris and Debian.  The\nvalue will be validated according to system rules, but it should\ngenerally be a fully qualified path.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### root {#package-attribute-root}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### source {#package-attribute-source}\n\nWhere to find the package file. This is mostly used by providers that don't\nautomatically download packages from a central repository. (For example:\nthe `yum` provider ignores this attribute, `apt` provider uses it if present\nand the `rpm` and `dpkg` providers require it.)\n\nDifferent providers accept different values for `source`. Most providers\naccept paths to local files stored on the target system. Some providers\nmay also accept URLs or network drive paths. Puppet will not\nautomatically retrieve source files for you, and usually just passes the\nvalue of `source` to the package installation command.\n\nYou can use a `file` resource if you need to manually copy package files\nto the target system.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### status {#package-attribute-status}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### uninstall_options {#package-attribute-uninstall_options}\n\nAn array of additional options to pass when uninstalling a package. These\noptions are package-specific, and should be documented by the software\nvendor.  For example:\n\n    package { 'VMware Tools':\n      ensure            => absent,\n      uninstall_options => [ { 'REMOVE' => 'Sync,VSS' } ],\n    }\n\nEach option in the array can either be a string or a hash, where each\nkey and value pair are interpreted in a provider specific way.  Each\noption will automatically be quoted when passed to the uninstall\ncommand.\n\nOn Windows, this is the **only** place in Puppet where backslash\nseparators should be used.  Note that backslashes in double-quoted\nstrings _must_ be double-escaped and backslashes in single-quoted\nstrings _may_ be double-escaped.\n\nRequires features uninstall_options.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n#### vendor {#package-attribute-vendor}\n\nA read-only parameter set by the package.\n\n([↑ Back to package attributes](#package-attributes))\n\n\n### Providers {#package-providers}\n\n#### aix {#package-provider-aix}\n\nInstallation from an AIX software directory, using the AIX `installp`\ncommand.  The `source` parameter is required for this provider, and should\nbe set to the absolute path (on the puppet agent machine) of a directory\ncontaining one or more BFF package files.\n\nThe `installp` command will generate a table of contents file (named `.toc`)\nin this directory, and the `name` parameter (or resource title) that you\nspecify for your `package` resource must match a package name that exists\nin the `.toc` file.\n\nNote that package downgrades are *not* supported; if your resource specifies\na specific version number and there is already a newer version of the package\ninstalled on the machine, the resource will fail with an error message.\n\n* Required binaries: `/usr/bin/lslpp`, `/usr/sbin/installp`\n* Confined to: `os.name == [:aix]`\n* Default for: `[\"os.name\", \"aix\"] == `\n* Supported features: `versionable`\n\n#### appdmg {#package-provider-appdmg}\n\nPackage management which copies application bundles to a target.\n\n* Required binaries: `/usr/bin/curl`, `/usr/bin/ditto`, `/usr/bin/hdiutil`\n* Confined to: `os.name == darwin`, `feature == cfpropertylist`\n\n#### apple {#package-provider-apple}\n\nPackage management based on OS X's built-in packaging system.  This is\nessentially the simplest and least functional package system in existence --\nit only supports installation; no deletion or upgrades.  The provider will\nautomatically add the `.pkg` extension, so leave that off when specifying\nthe package name.\n\n* Required binaries: `/usr/sbin/installer`\n* Confined to: `os.name == darwin`\n\n#### apt {#package-provider-apt}\n\nPackage management via `apt-get`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to apt-get.\nThese options should be specified as an array where each element is either a\n string or a hash.\n\n* Required binaries: `/usr/bin/apt-cache`, `/usr/bin/apt-get`, `/usr/bin/apt-mark`, `/usr/bin/debconf-set-selections`\n* Default for: `[\"os.family\", \"debian\"] == `\n* Supported features: `install_options`, `version_ranges`, `versionable`, `virtual_packages`\n\n#### aptitude {#package-provider-aptitude}\n\nPackage management via `aptitude`.\n\n* Required binaries: `/usr/bin/apt-cache`, `/usr/bin/aptitude`\n* Supported features: `versionable`\n\n#### aptrpm {#package-provider-aptrpm}\n\nPackage management via `apt-get` ported to `rpm`.\n\n* Required binaries: `apt-cache`, `apt-get`, `rpm`\n* Supported features: `versionable`\n\n#### blastwave {#package-provider-blastwave}\n\nPackage management using Blastwave.org's `pkg-get` command on Solaris.\n\n* Required binaries: `pkgget`\n* Confined to: `os.family == solaris`\n\n#### dnf {#package-provider-dnf}\n\nSupport via `dnf`.\n\nUsing this provider's `uninstallable` feature will not remove dependent packages. To\nremove dependent packages with this provider use the `purgeable` feature, but note this\nfeature is destructive and should be used with the utmost care.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to dnf.\nThese options should be specified as an array where each element is either\n a string or a hash.\n\n* Required binaries: `dnf`, `rpm`\n* Default for: `[\"os.name\", \"fedora\"] == `, `[\"os.family\", \"redhat\"] == `, `[\"os.name\", \"amazon\"] == [\"os.release.major\", \"[\\\"2023\\\"]\"]`\n* Supported features: `install_only`, `install_options`, `version_ranges`, `versionable`, `virtual_packages`\n\n#### dnfmodule {#package-provider-dnfmodule}\n\n\n\n* Required binaries: `/usr/bin/dnf`\n* Supported features: `disableable`, `installable`, `supports_flavors`, `uninstallable`, `versionable`\n\n#### dpkg {#package-provider-dpkg}\n\nPackage management via `dpkg`.  Because this only uses `dpkg`\nand not `apt`, you must specify the source of any packages you want\nto manage.\n\n* Required binaries: `/usr/bin/dpkg`, `/usr/bin/dpkg-deb`, `/usr/bin/dpkg-query`\n* Supported features: `holdable`, `virtual_packages`\n\n#### fink {#package-provider-fink}\n\nPackage management via `fink`.\n\n* Required binaries: `/sw/bin/apt-cache`, `/sw/bin/apt-get`, `/sw/bin/dpkg-query`, `/sw/bin/fink`\n* Supported features: `versionable`\n\n#### freebsd {#package-provider-freebsd}\n\nThe specific form of package management on FreeBSD.  This is an\nextremely quirky packaging system, in that it freely mixes between\nports and packages.  Apparently all of the tools are written in Ruby,\nso there are plans to rewrite this support to directly use those\nlibraries.\n\n* Required binaries: `/usr/sbin/pkg_add`, `/usr/sbin/pkg_delete`, `/usr/sbin/pkg_info`\n* Confined to: `os.name == freebsd`\n\n#### gem {#package-provider-gem}\n\nRuby Gem support. If a URL is passed via `source`, then that URL is\nappended to the list of remote gem repositories; to ensure that only the\nspecified source is used, also pass `--clear-sources` via `install_options`.\nIf source is present but is not a valid URL, it will be interpreted as the\npath to a local gem file. If source is not present, the gem will be\ninstalled from the default gem repositories. Note that to modify this for Windows, it has to be a valid URL.\n\nThis provider supports the `install_options` and `uninstall_options` attributes,\nwhich allow command-line flags to be passed to the gem command.\nThese options should be specified as an array where each element is either a\nstring or a hash.\n* Supported features: `install_options`, `targetable`, `uninstall_options`, `version_ranges`, `versionable`\n\n#### hpux {#package-provider-hpux}\n\nHP-UX's packaging system.\n\n* Required binaries: `/usr/sbin/swinstall`, `/usr/sbin/swlist`, `/usr/sbin/swremove`\n* Confined to: `os.name == hp-ux`\n* Default for: `[\"os.name\", \"hp-ux\"] == `\n\n#### macports {#package-provider-macports}\n\nPackage management using MacPorts on OS X.\n\nSupports MacPorts versions and revisions, but not variants.\nVariant preferences may be specified using\n[the MacPorts variants.conf file](http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf).\n\nWhen specifying a version in the Puppet DSL, only specify the version, not the revision.\nRevisions are only used internally for ensuring the latest version/revision of a port.\n\n* Confined to: `os.name == darwin`\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`\n\n#### nim {#package-provider-nim}\n\nInstallation from an AIX NIM LPP source.  The `source` parameter is required\nfor this provider, and should specify the name of a NIM `lpp_source` resource\nthat is visible to the puppet agent machine.  This provider supports the\nmanagement of both BFF/installp and RPM packages.\n\nNote that package downgrades are *not* supported; if your resource specifies\na specific version number and there is already a newer version of the package\ninstalled on the machine, the resource will fail with an error message.\n\n* Required binaries: `/usr/bin/lslpp`, `/usr/sbin/nimclient`, `rpm`\n* Confined to: `exists == /etc/niminfo`\n* Supported features: `versionable`\n\n#### openbsd {#package-provider-openbsd}\n\nOpenBSD's form of `pkg_add` support.\n\nThis provider supports the `install_options` and `uninstall_options`\nattributes, which allow command-line flags to be passed to pkg_add and pkg_delete.\nThese options should be specified as an array where each element is either a\n string or a hash.\n\n* Required binaries: `pkg_add`, `pkg_delete`, `pkg_info`\n* Confined to: `os.name == openbsd`\n* Default for: `[\"os.name\", \"openbsd\"] == `\n* Supported features: `install_options`, `supports_flavors`, `uninstall_options`, `upgradeable`, `versionable`\n\n#### opkg {#package-provider-opkg}\n\nOpkg packaging support. Common on OpenWrt and OpenEmbedded platforms\n\n* Required binaries: `opkg`\n* Confined to: `os.name == openwrt`\n* Default for: `[\"os.name\", \"openwrt\"] == `\n\n#### pacman {#package-provider-pacman}\n\nSupport for the Package Manager Utility (pacman) used in Archlinux.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pacman.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/pacman`\n* Confined to: `os.name == [:archlinux, :manjarolinux, :artix]`\n* Default for: `[\"os.name\", \"[:archlinux, :manjarolinux, :artix]\"] == `\n* Supported features: `install_options`, `purgeable`, `uninstall_options`, `upgradeable`, `virtual_packages`\n\n#### pip {#package-provider-pip}\n\nPython packages via `pip`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pip.\nThese options should be specified as an array where each element is either a string or a hash.\n* Supported features: `install_options`, `installable`, `targetable`, `uninstallable`, `upgradeable`, `version_ranges`, `versionable`\n\n#### pip2 {#package-provider-pip2}\n\nPython packages via `pip2`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pip2.\nThese options should be specified as an array where each element is either a string or a hash.\n* Supported features: `install_options`, `installable`, `targetable`, `uninstallable`, `upgradeable`, `versionable`\n\n#### pip3 {#package-provider-pip3}\n\nPython packages via `pip3`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pip3.\nThese options should be specified as an array where each element is either a string or a hash.\n* Supported features: `install_options`, `installable`, `targetable`, `uninstallable`, `upgradeable`, `versionable`\n\n#### pkg {#package-provider-pkg}\n\nOpenSolaris image packaging system. See pkg(5) for more information.\n\nThis provider supports the `install_options` attribute, which allows\ncommand-line flags to be passed to pkg. These options should be specified as an\narray where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/pkg`\n* Confined to: `os.family == solaris`\n* Default for: `[\"os.family\", \"solaris\"] == [\"kernelrelease\", \"['5.11', '5.12']\"]`\n* Supported features: `holdable`, `install_options`, `upgradable`, `versionable`\n\n#### pkgdmg {#package-provider-pkgdmg}\n\nPackage management based on Apple's Installer.app and DiskUtility.app.\n\nThis provider works by checking the contents of a DMG image for Apple pkg or\nmpkg files. Any number of pkg or mpkg files may exist in the root directory\nof the DMG file system, and Puppet will install all of them. Subdirectories\nare not checked for packages.\n\nThis provider can also accept plain .pkg (but not .mpkg) files in addition\nto .dmg files.\n\nNotes:\n\n* The `source` attribute is mandatory. It must be either a local disk path\n  or an HTTP, HTTPS, or FTP URL to the package.\n* The `name` of the resource must be the filename (without path) of the DMG file.\n* When installing the packages from a DMG, this provider writes a file to\n  disk at `/var/db/.puppet_pkgdmg_installed_NAME`. If that file is present,\n  Puppet assumes all packages from that DMG are already installed.\n* This provider is not versionable and uses DMG filenames to determine\n  whether a package has been installed. Thus, to install new a version of a\n  package, you must create a new DMG with a different filename.\n\n* Required binaries: `/usr/bin/curl`, `/usr/bin/hdiutil`, `/usr/sbin/installer`\n* Confined to: `os.name == darwin`, `feature == cfpropertylist`\n* Default for: `[\"os.name\", \"darwin\"] == `\n\n#### pkgin {#package-provider-pkgin}\n\nPackage management using pkgin, a binary package manager for pkgsrc.\n\n* Required binaries: `pkgin`\n* Default for: `[\"os.name\", \"[:smartos, :netbsd]\"] == `\n* Supported features: `installable`, `uninstallable`, `upgradeable`, `versionable`\n\n#### pkgng {#package-provider-pkgng}\n\nA PkgNG provider for FreeBSD and DragonFly.\n\n* Required binaries: `/usr/local/sbin/pkg`\n* Confined to: `os.name == [:freebsd, :dragonfly]`\n* Default for: `[\"os.name\", \"[:freebsd, :dragonfly]\"] == `\n* Supported features: `install_options`, `upgradeable`, `versionable`\n\n#### pkgutil {#package-provider-pkgutil}\n\nPackage management using Peter Bonivart's ``pkgutil`` command on Solaris.\n\n* Confined to: `os.family == solaris`\n\n#### portage {#package-provider-portage}\n\nProvides packaging support for Gentoo's portage system.\n\nThis provider supports the `install_options` and `uninstall_options` attributes, which allows command-line\nflags to be passed to emerge. These options should be specified as an array where each element is either a string or a hash.\n\n* Confined to: `os.family == gentoo`\n* Default for: `[\"os.family\", \"gentoo\"] == `\n* Supported features: `install_options`, `purgeable`, `reinstallable`, `uninstall_options`, `versionable`, `virtual_packages`\n\n#### ports {#package-provider-ports}\n\nSupport for FreeBSD's ports.  Note that this, too, mixes packages and ports.\n\n* Required binaries: `/usr/local/sbin/pkg_deinstall`, `/usr/local/sbin/portupgrade`, `/usr/local/sbin/portversion`, `/usr/sbin/pkg_info`\n\n#### portupgrade {#package-provider-portupgrade}\n\nSupport for FreeBSD's ports using the portupgrade ports management software.\nUse the port's full origin as the resource name. eg (ports-mgmt/portupgrade)\nfor the portupgrade port.\n\n* Required binaries: `/usr/local/sbin/pkg_deinstall`, `/usr/local/sbin/portinstall`, `/usr/local/sbin/portupgrade`, `/usr/local/sbin/portversion`, `/usr/sbin/pkg_info`\n\n#### puppet_gem {#package-provider-puppet_gem}\n\nPuppet Ruby Gem support. This provider is useful for managing\ngems needed by the ruby provided in the puppet-agent package.\n\n* Required binaries: `Puppet.run_mode.gem_cmd`\n* Confined to: `true == Puppet.runtime[:facter].value(:aio_agent_version)`\n* Supported features: `install_options`, `uninstall_options`, `versionable`\n\n#### puppetserver_gem {#package-provider-puppetserver_gem}\n\nPuppet Server Ruby Gem support. If a URL is passed via `source`, then\nthat URL is appended to the list of remote gem repositories which by default\ncontains rubygems.org; To ensure that only the specified source is used also\npass `--clear-sources` in via `install_options`; if a source is present but\nis not a valid URL, it will be interpreted as the path to a local gem file.\nIf source is not present at all, the gem will be installed from the default\ngem repositories.\n\n* Confined to: `feature == hocon`, `fips_enabled == false`\n* Supported features: `install_options`, `uninstall_options`, `versionable`\n\n#### rpm {#package-provider-rpm}\n\nRPM packaging support; should work anywhere with a working `rpm`\nbinary.\n\nThis provider supports the `install_options` and `uninstall_options`\nattributes, which allow command-line flags to be passed to rpm.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `rpm`\n* Supported features: `install_only`, `install_options`, `uninstall_options`, `versionable`, `virtual_packages`\n\n#### rug {#package-provider-rug}\n\nSupport for suse `rug` package manager.\n\n* Required binaries: `/usr/bin/rug`, `rpm`\n* Confined to: `os.name == [:suse, :sles]`\n* Supported features: `versionable`\n\n#### sun {#package-provider-sun}\n\nSun's packaging system.  Requires that you specify the source for\nthe packages you're managing.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to pkgadd.\nThese options should be specified as an array where each element is either a string\n or a hash.\n\n* Required binaries: `/usr/bin/pkginfo`, `/usr/sbin/pkgadd`, `/usr/sbin/pkgrm`\n* Confined to: `os.family == solaris`\n* Default for: `[\"os.family\", \"solaris\"] == `\n* Supported features: `install_options`\n\n#### sunfreeware {#package-provider-sunfreeware}\n\nPackage management using sunfreeware.com's `pkg-get` command on Solaris.\nAt this point, support is exactly the same as `blastwave` support and\nhas not actually been tested.\n\n* Required binaries: `pkg-get`\n* Confined to: `os.family == solaris`\n\n#### tdnf {#package-provider-tdnf}\n\nSupport via `tdnf`.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to tdnf.\nThese options should be spcified as a string (e.g. '--flag'), a hash (e.g. {'--flag' => 'value'}), or an\narray where each element is either a string or a hash.\n\n* Required binaries: `rpm`, `tdnf`\n* Default for: `[\"os.name\", \"PhotonOS\"] == `\n* Supported features: `install_options`, `versionable`, `virtual_packages`\n\n#### up2date {#package-provider-up2date}\n\nSupport for Red Hat's proprietary `up2date` package update\nmechanism.\n\n* Required binaries: `/usr/sbin/up2date-nox`\n* Confined to: `os.family == redhat`\n* Default for: `[\"os.family\", \"redhat\"] == [\"os.distro.release.full\", \"[\\\"2.1\\\", \\\"3\\\", \\\"4\\\"]\"]`\n\n#### urpmi {#package-provider-urpmi}\n\nSupport via `urpmi`.\n\n* Required binaries: `rpm`, `urpme`, `urpmi`, `urpmq`\n* Default for: `[\"os.name\", \"[:mandriva, :mandrake]\"] == `\n* Supported features: `versionable`\n\n#### windows {#package-provider-windows}\n\nWindows package management.\n\nThis provider supports either MSI or self-extracting executable installers.\n\nThis provider requires a `source` attribute when installing the package.\nIt accepts paths to local files, mapped drives, or UNC paths.\n\nThis provider supports the `install_options` and `uninstall_options`\nattributes, which allow command-line flags to be passed to the installer.\nThese options should be specified as an array where each element is either\na string or a hash.\n\nIf the executable requires special arguments to perform a silent install or\nuninstall, then the appropriate arguments should be specified using the\n`install_options` or `uninstall_options` attributes, respectively.  Puppet\nwill automatically quote any option that contains spaces.\n\n* Confined to: `os.name == windows`\n* Default for: `[\"os.name\", \"windows\"] == `\n* Supported features: `install_options`, `installable`, `uninstall_options`, `uninstallable`, `versionable`\n\n#### xbps {#package-provider-xbps}\n\nSupport for the Package Manager Utility (xbps) used in VoidLinux.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to xbps-install.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `/usr/bin/xbps-install`, `/usr/bin/xbps-pkgdb`, `/usr/bin/xbps-query`, `/usr/bin/xbps-remove`\n* Confined to: `os.name == void`\n* Default for: `[\"os.name\", \"void\"] == `\n* Supported features: `holdable`, `install_options`, `uninstall_options`, `upgradeable`, `virtual_packages`\n\n#### yum {#package-provider-yum}\n\nSupport via `yum`.\n\nUsing this provider's `uninstallable` feature will not remove dependent packages. To\nremove dependent packages with this provider use the `purgeable` feature, but note this\nfeature is destructive and should be used with the utmost care.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to yum.\nThese options should be specified as an array where each element is either a string or a hash.\n\n* Required binaries: `rpm`, `yum`\n* Default for: `[\"os.name\", \"amazon\"] == `, `[\"os.family\", \"redhat\"] == [\"os.release.major\", \"(4..7).to_a\"]`\n* Supported features: `install_only`, `install_options`, `version_ranges`, `versionable`, `virtual_packages`\n\n#### zypper {#package-provider-zypper}\n\nSupport for SuSE `zypper` package manager. Found in SLES10sp2+ and SLES11.\n\nThis provider supports the `install_options` attribute, which allows command-line flags to be passed to zypper.\nThese options should be specified as an array where each element is either a\nstring or a hash.\n\n* Required binaries: `/usr/bin/zypper`\n* Confined to: `os.name == [:suse, :sles, :sled, :opensuse]`\n* Default for: `[\"os.name\", \"[:suse, :sles, :sled, :opensuse]\"] == `\n* Supported features: `install_options`, `versionable`, `virtual_packages`\n\n### Provider Features {#package-provider-features}\n\nAvailable features:\n\n* `disableable` --- The provider can disable packages. This feature is used by specifying `disabled` as the desired value for the package.\n* `holdable` --- The provider is capable of placing packages on hold such that they are not automatically upgraded as a result of other package dependencies unless explicit action is taken by a user or another package.\n* `install_only` --- The provider accepts options to only install packages never update (kernels, etc.)\n* `install_options` --- The provider accepts options to be passed to the installer command.\n* `installable` --- The provider can install packages.\n* `package_settings` --- The provider accepts package_settings to be ensured for the given package. The meaning and format of these settings is provider-specific.\n* `purgeable` --- The provider can purge packages.  This generally means that all traces of the package are removed, including existing configuration files.  This feature is thus destructive and should be used with the utmost care.\n* `reinstallable` --- The provider can reinstall packages.\n* `supports_flavors` --- The provider accepts flavors, which are specific variants of packages.\n* `targetable` --- The provider accepts a targeted package management command.\n* `uninstall_options` --- The provider accepts options to be passed to the uninstaller command.\n* `uninstallable` --- The provider can uninstall packages.\n* `upgradeable` --- The provider can upgrade to the latest version of a package.  This feature is used by specifying `latest` as the desired value for the package.\n* `version_ranges` --- The provider can ensure version ranges.\n* `versionable` --- The provider is capable of interrogating the package database for installed version(s), and can select which out of a set of available versions of a package to install if asked.\n* `virtual_packages` --- The provider accepts virtual package names for install and uninstall.\n\nProvider support:\n\n* **aix** - _versionable_\n* **appdmg** - No supported Provider features\n* **apple** - No supported Provider features\n* **apt** - _versionable, install options, virtual packages, version ranges_\n* **aptitude** - _versionable_\n* **aptrpm** - _versionable_\n* **blastwave** - No supported Provider features\n* **dnf** - _install options, versionable, virtual packages, install only, version ranges_\n* **dnfmodule** - _installable, uninstallable, versionable, supports flavors, disableable_\n* **dpkg** - _holdable, virtual packages_\n* **fink** - _versionable_\n* **freebsd** - No supported Provider features\n* **gem** - _versionable, install options, uninstall options, targetable, version ranges_\n* **hpux** - No supported Provider features\n* **macports** - _installable, uninstallable, upgradeable, versionable_\n* **nim** - _versionable_\n* **openbsd** - _versionable, install options, uninstall options, upgradeable, supports flavors_\n* **opkg** - No supported Provider features\n* **pacman** - _install options, uninstall options, upgradeable, virtual packages, purgeable_\n* **pip** - _installable, uninstallable, upgradeable, versionable, version ranges, install options, targetable_\n* **pip2** - _installable, uninstallable, upgradeable, versionable, install options, targetable_\n* **pip3** - _installable, uninstallable, upgradeable, versionable, install options, targetable_\n* **pkg** - _versionable, upgradable, holdable, install options_\n* **pkgdmg** - No supported Provider features\n* **pkgin** - _installable, uninstallable, upgradeable, versionable_\n* **pkgng** - _versionable, upgradeable, install options_\n* **pkgutil** - No supported Provider features\n* **portage** - _install options, purgeable, reinstallable, uninstall options, versionable, virtual packages_\n* **ports** - No supported Provider features\n* **portupgrade** - No supported Provider features\n* **puppet_gem** - _versionable, install options, uninstall options_\n* **puppetserver_gem** - _versionable, install options, uninstall options_\n* **rpm** - _versionable, install options, uninstall options, virtual packages, install only_\n* **rug** - _versionable_\n* **sun** - _install options_\n* **sunfreeware** - No supported Provider features\n* **tdnf** - _install options, versionable, virtual packages_\n* **up2date** - No supported Provider features\n* **urpmi** - _versionable_\n* **windows** - _installable, uninstallable, install options, uninstall options, versionable_\n* **xbps** - _install options, uninstall options, upgradeable, holdable, virtual packages_\n* **yum** - _install options, versionable, virtual packages, install only, version ranges_\n* **zypper** - _versionable, install options, virtual packages_\n  \n\n\n\n\n"
  },
  {
    "path": "references/types/resources.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: resources'\ncanonical: \"/puppet/latest/types/resources.html\"\n---\n\n# Resource Type: resources\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## resources\n\n* [Attributes](#resources-attributes)\n\n### Description {#resources-description}\n\nThis is a metatype that can manage other resource types.  Any\nmetaparams specified here will be passed on to any generated resources,\nso you can purge unmanaged resources but set `noop` to true so the\npurging is only logged and does not actually happen.\n\n### Attributes {#resources-attributes}\n\n<pre><code>resources { 'resource title':\n  <a href=\"#resources-attribute-name\">name</a>               =&gt; <em># <strong>(namevar)</strong> The name of the type to be...</em>\n  <a href=\"#resources-attribute-purge\">purge</a>              =&gt; <em># Whether to purge unmanaged resources.  When set...</em>\n  <a href=\"#resources-attribute-unless_system_user\">unless_system_user</a> =&gt; <em># This keeps system users from being purged.  By...</em>\n  <a href=\"#resources-attribute-unless_uid\">unless_uid</a>         =&gt; <em># This keeps specific uids or ranges of uids from...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#resources-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the type to be managed.\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n#### purge {#resources-attribute-purge}\n\nWhether to purge unmanaged resources.  When set to `true`, this will\ndelete any resource that is not specified in your configuration and is not\nautorequired by any managed resources. **Note:** The `ssh_authorized_key`\nresource type can't be purged this way; instead, see the `purge_ssh_keys`\nattribute of the `user` type.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n#### unless_system_user {#resources-attribute-unless_system_user}\n\nThis keeps system users from being purged.  By default, it\ndoes not purge users whose UIDs are less than the minimum UID for the system (typically 500 or 1000), but you can specify\na different UID as the inclusive limit.\n\nAllowed values:\n\n* `true`\n* `false`\n* `/^\\d+$/`\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n#### unless_uid {#resources-attribute-unless_uid}\n\nThis keeps specific uids or ranges of uids from being purged when purge is true.\nAccepts integers, integer strings, and arrays of integers or integer strings.\nTo specify a range of uids, consider using the range() function from stdlib.\n\n([↑ Back to resources attributes](#resources-attributes))\n\n\n\n\n\n"
  },
  {
    "path": "references/types/schedule.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: schedule'\ncanonical: \"/puppet/latest/types/schedule.html\"\n---\n\n# Resource Type: schedule\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## schedule\n\n* [Attributes](#schedule-attributes)\n\n### Description {#schedule-description}\n\nDefine schedules for Puppet. Resources can be limited to a schedule by using the\n[`schedule`](https://puppet.com/docs/puppet/latest/metaparameter.html#schedule)\nmetaparameter.\n\nCurrently, **schedules can only be used to stop a resource from being\napplied;** they cannot cause a resource to be applied when it otherwise\nwouldn't be, and they cannot accurately specify a time when a resource\nshould run.\n\nEvery time Puppet applies its configuration, it will apply the\nset of resources whose schedule does not eliminate them from\nrunning right then, but there is currently no system in place to\nguarantee that a given resource runs at a given time.  If you\nspecify a very  restrictive schedule and Puppet happens to run at a\ntime within that schedule, then the resources will get applied;\notherwise, that work may never get done.\n\nThus, it is advisable to use wider scheduling (for example, over a couple\nof hours) combined with periods and repetitions.  For instance, if you\nwanted to restrict certain resources to only running once, between\nthe hours of two and 4 AM, then you would use this schedule:\n\n    schedule { 'maint':\n      range  => '2 - 4',\n      period => daily,\n      repeat => 1,\n    }\n\nWith this schedule, the first time that Puppet runs between 2 and 4 AM,\nall resources with this schedule will get applied, but they won't\nget applied again between 2 and 4 because they will have already\nrun once that day, and they won't get applied outside that schedule\nbecause they will be outside the scheduled range.\n\nPuppet automatically creates a schedule for each of the valid periods\nwith the same name as that period (such as hourly and daily).\nAdditionally, a schedule named `puppet` is created and used as the\ndefault, with the following attributes:\n\n    schedule { 'puppet':\n      period => hourly,\n      repeat => 2,\n    }\n\nThis will cause resources to be applied every 30 minutes by default.\n\nThe `statettl` setting on the agent affects the ability of a schedule to\ndetermine if a resource has already been checked. If the `statettl` is\nset lower than the span of the associated schedule resource, then a\nresource could be checked & applied multiple times in the schedule as\nthe information about when the resource was last checked will have\nexpired from the cache.\n\n### Attributes {#schedule-attributes}\n\n<pre><code>schedule { 'resource title':\n  <a href=\"#schedule-attribute-name\">name</a>        =&gt; <em># <strong>(namevar)</strong> The name of the schedule.  This name is used...</em>\n  <a href=\"#schedule-attribute-period\">period</a>      =&gt; <em># The period of repetition for resources on this...</em>\n  <a href=\"#schedule-attribute-periodmatch\">periodmatch</a> =&gt; <em># Whether periods should be matched by a numeric...</em>\n  <a href=\"#schedule-attribute-range\">range</a>       =&gt; <em># The earliest and latest that a resource can be...</em>\n  <a href=\"#schedule-attribute-repeat\">repeat</a>      =&gt; <em># How often a given resource may be applied in...</em>\n  <a href=\"#schedule-attribute-weekday\">weekday</a>     =&gt; <em># The days of the week in which the schedule...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#schedule-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the schedule.  This name is used when assigning the schedule\nto a resource with the `schedule` metaparameter:\n\n    schedule { 'everyday':\n      period => daily,\n      range  => '2 - 4',\n    }\n\n    exec { '/usr/bin/apt-get update':\n      schedule => 'everyday',\n    }\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### period {#schedule-attribute-period}\n\nThe period of repetition for resources on this schedule. The default is\nfor resources to get applied every time Puppet runs.\n\nNote that the period defines how often a given resource will get\napplied but not when; if you would like to restrict the hours\nthat a given resource can be applied (for instance, only at night\nduring a maintenance window), then use the `range` attribute.\n\nIf the provided periods are not sufficient, you can provide a\nvalue to the *repeat* attribute, which will cause Puppet to\nschedule the affected resources evenly in the period the\nspecified number of times.  Take this schedule:\n\n    schedule { 'veryoften':\n      period => hourly,\n      repeat => 6,\n    }\n\nThis can cause Puppet to apply that resource up to every 10 minutes.\n\nAt the moment, Puppet cannot guarantee that level of repetition; that\nis, the resource can applied _up to_ every 10 minutes, but internal\nfactors might prevent it from actually running that often (for instance,\nif a Puppet run is still in progress when the next run is scheduled to\nstart, that next run will be suppressed).\n\nSee the `periodmatch` attribute for tuning whether to match\ntimes by their distance apart or by their specific value.\n\n> **Tip**: You can use `period => never,` to prevent a resource from being applied\nin the given `range`. This is useful if you need to create a blackout window to\nperform sensitive operations without interruption.\n\nAllowed values:\n\n* `hourly`\n* `daily`\n* `weekly`\n* `monthly`\n* `never`\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### periodmatch {#schedule-attribute-periodmatch}\n\nWhether periods should be matched by a numeric value (for instance,\nwhether two times are in the same hour) or by their chronological\ndistance apart (whether two times are 60 minutes apart).\n\nDefault: `distance`\n\nAllowed values:\n\n* `number`\n* `distance`\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### range {#schedule-attribute-range}\n\nThe earliest and latest that a resource can be applied.  This is\nalways a hyphen-separated range within a 24 hour period, and hours\nmust be specified in numbers between 0 and 23, inclusive.  Minutes and\nseconds can optionally be provided, using the normal colon as a\nseparator. For instance:\n\n    schedule { 'maintenance':\n      range => '1:30 - 4:30',\n    }\n\nThis is mostly useful for restricting certain resources to being\napplied in maintenance windows or during off-peak hours. Multiple\nranges can be applied in array context. As a convenience when specifying\nranges, you can cross midnight (for example, `range => \"22:00 - 04:00\"`).\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### repeat {#schedule-attribute-repeat}\n\nHow often a given resource may be applied in this schedule's `period`.\nMust be an integer.\n\nDefault: `1`\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n#### weekday {#schedule-attribute-weekday}\n\nThe days of the week in which the schedule should be valid.\nYou may specify the full day name 'Tuesday', the three character\nabbreviation 'Tue', or a number (as a string or as an integer) corresponding to the day of the\nweek where 0 is Sunday, 1 is Monday, and so on. Multiple days can be specified\nas an array. If not specified, the day of the week will not be\nconsidered in the schedule.\n\nIf you are also using a range match that spans across midnight\nthen this parameter will match the day that it was at the start\nof the range, not necessarily the day that it is when it matches.\nFor example, consider this schedule:\n\n    schedule { 'maintenance_window':\n      range   => '22:00 - 04:00',\n      weekday => 'Saturday',\n    }\n\nThis will match at 11 PM on Saturday and 2 AM on Sunday, but not\nat 2 AM on Saturday.\n\n([↑ Back to schedule attributes](#schedule-attributes))\n\n\n\n\n\n"
  },
  {
    "path": "references/types/service.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: service'\ncanonical: \"/puppet/latest/types/service.html\"\n---\n\n# Resource Type: service\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## service\n\n* [Attributes](#service-attributes)\n* [Providers](#service-providers)\n* [Provider Features](#service-provider-features)\n\n### Description {#service-description}\n\nManage running services.  Service support unfortunately varies\nwidely by platform --- some platforms have very little if any concept of a\nrunning service, and some have a very codified and powerful concept.\nPuppet's service support is usually capable of doing the right thing, but\nthe more information you can provide, the better behaviour you will get.\n\nPuppet 2.7 and newer expect init scripts to have a working status command.\nIf this isn't the case for any of your services' init scripts, you will\nneed to set `hasstatus` to false and possibly specify a custom status\ncommand in the `status` attribute. As a last resort, Puppet will attempt to\nsearch the process table by calling whatever command is listed in the `ps`\nfact. The default search pattern is the name of the service, but you can\nspecify it with the `pattern` attribute.\n\n**Refresh:** `service` resources can respond to refresh events (via\n`notify`, `subscribe`, or the `~>` arrow). If a `service` receives an\nevent from another resource, Puppet will restart the service it manages.\nThe actual command used to restart the service depends on the platform and\ncan be configured:\n\n* If you set `hasrestart` to true, Puppet will use the init script's restart command.\n* You can provide an explicit command for restarting with the `restart` attribute.\n* If you do neither, the service's stop and start commands will be used.\n\n### Attributes {#service-attributes}\n\n<pre><code>service { 'resource title':\n  <a href=\"#service-attribute-name\">name</a>          =&gt; <em># <strong>(namevar)</strong> The name of the service to run.  This name is...</em>\n  <a href=\"#service-attribute-ensure\">ensure</a>        =&gt; <em># Whether a service should be running. Default...</em>\n  <a href=\"#service-attribute-binary\">binary</a>        =&gt; <em># The path to the daemon.  This is only used for...</em>\n  <a href=\"#service-attribute-control\">control</a>       =&gt; <em># The control variable used to manage services...</em>\n  <a href=\"#service-attribute-enable\">enable</a>        =&gt; <em># Whether a service should be enabled to start at...</em>\n  <a href=\"#service-attribute-flags\">flags</a>         =&gt; <em># Specify a string of flags to pass to the startup </em>\n  <a href=\"#service-attribute-hasrestart\">hasrestart</a>    =&gt; <em># Specify that an init script has a `restart...</em>\n  <a href=\"#service-attribute-hasstatus\">hasstatus</a>     =&gt; <em># Declare whether the service's init script has a...</em>\n  <a href=\"#service-attribute-logonaccount\">logonaccount</a>  =&gt; <em># Specify an account for service...</em>\n  <a href=\"#service-attribute-logonpassword\">logonpassword</a> =&gt; <em># Specify a password for service logon. Default...</em>\n  <a href=\"#service-attribute-manifest\">manifest</a>      =&gt; <em># Specify a command to config a service, or a path </em>\n  <a href=\"#service-attribute-path\">path</a>          =&gt; <em># The search path for finding init scripts....</em>\n  <a href=\"#service-attribute-pattern\">pattern</a>       =&gt; <em># The pattern to search for in the process table...</em>\n  <a href=\"#service-attribute-provider\">provider</a>      =&gt; <em># The specific backend to use for this `service...</em>\n  <a href=\"#service-attribute-restart\">restart</a>       =&gt; <em># Specify a *restart* command manually.  If left...</em>\n  <a href=\"#service-attribute-start\">start</a>         =&gt; <em># Specify a *start* command manually.  Most...</em>\n  <a href=\"#service-attribute-status\">status</a>        =&gt; <em># Specify a *status* command manually.  This...</em>\n  <a href=\"#service-attribute-stop\">stop</a>          =&gt; <em># Specify a *stop* command...</em>\n  <a href=\"#service-attribute-timeout\">timeout</a>       =&gt; <em># Specify an optional minimum timeout (in seconds) </em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#service-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the service to run.\n\nThis name is used to find the service; on platforms where services\nhave short system names and long display names, this should be the\nshort name. (To take an example from Windows, you would use \"wuauserv\"\nrather than \"Automatic Updates.\")\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### ensure {#service-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhether a service should be running. Default values depend on the platform.\n\nAllowed values:\n\n* `stopped`\n* `running`\n* `false`\n* `true`\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### binary {#service-attribute-binary}\n\nThe path to the daemon.  This is only used for\nsystems that do not support init scripts.  This binary will be\nused to start the service if no `start` parameter is\nprovided.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### control {#service-attribute-control}\n\nThe control variable used to manage services (originally for HP-UX).\nDefaults to the upcased service name plus `START` replacing dots with\nunderscores, for those providers that support the `controllable` feature.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### enable {#service-attribute-enable}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nWhether a service should be enabled to start at boot.\nThis property behaves differently depending on the platform;\nwherever possible, it relies on local tools to enable or disable\na given service. Default values depend on the platform.\n\nIf you don't specify a value for the `enable` attribute, Puppet leaves\nthat aspect of the service alone and your operating system determines\nthe behavior.\n\nAllowed values:\n\n* `true`\n* `false`\n* `manual`\n* `mask`\n* `delayed`\n\nRequires features enableable.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### flags {#service-attribute-flags}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify a string of flags to pass to the startup script.\n\nRequires features flaggable.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### hasrestart {#service-attribute-hasrestart}\n\nSpecify that an init script has a `restart` command.  If this is\nfalse and you do not specify a command in the `restart` attribute,\nthe init script's `stop` and `start` commands will be used.\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### hasstatus {#service-attribute-hasstatus}\n\nDeclare whether the service's init script has a functional status\ncommand. This attribute's default value changed in Puppet 2.7.0.\n\nThe init script's status command must return 0 if the service is\nrunning and a nonzero value otherwise. Ideally, these exit codes\nshould conform to [the LSB's specification][lsb-exit-codes] for init\nscript status actions, but Puppet only considers the difference\nbetween 0 and nonzero to be relevant.\n\nIf a service's init script does not support any kind of status command,\nyou should set `hasstatus` to false and either provide a specific\ncommand using the `status` attribute or expect that Puppet will look for\nthe service name in the process table. Be aware that 'virtual' init\nscripts (like 'network' under Red Hat systems) will respond poorly to\nrefresh events from other resources if you override the default behavior\nwithout providing a status command.\n\nDefault: `true`\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### logonaccount {#service-attribute-logonaccount}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify an account for service logon\n\nRequires features manages_logon_credentials.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### logonpassword {#service-attribute-logonpassword}\n\nSpecify a password for service logon. Default value is an empty string (when logonaccount is specified).\n\nRequires features manages_logon_credentials.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### manifest {#service-attribute-manifest}\n\nSpecify a command to config a service, or a path to a manifest to do so.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### path {#service-attribute-path}\n\nThe search path for finding init scripts.  Multiple values should\nbe separated by colons or provided as an array.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### pattern {#service-attribute-pattern}\n\nThe pattern to search for in the process table.\nThis is used for stopping services on platforms that do not\nsupport init scripts, and is also used for determining service\nstatus on those service whose init scripts do not include a status\ncommand.\n\nDefaults to the name of the service. The pattern can be a simple string\nor any legal Ruby pattern, including regular expressions (which should\nbe quoted without enclosing slashes).\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### provider {#service-attribute-provider}\n\nThe specific backend to use for this `service` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`base`](#service-provider-base)\n* [`bsd`](#service-provider-bsd)\n* [`daemontools`](#service-provider-daemontools)\n* [`debian`](#service-provider-debian)\n* [`freebsd`](#service-provider-freebsd)\n* [`gentoo`](#service-provider-gentoo)\n* [`init`](#service-provider-init)\n* [`launchd`](#service-provider-launchd)\n* [`openbsd`](#service-provider-openbsd)\n* [`openrc`](#service-provider-openrc)\n* [`openwrt`](#service-provider-openwrt)\n* [`rcng`](#service-provider-rcng)\n* [`redhat`](#service-provider-redhat)\n* [`runit`](#service-provider-runit)\n* [`service`](#service-provider-service)\n* [`smf`](#service-provider-smf)\n* [`src`](#service-provider-src)\n* [`systemd`](#service-provider-systemd)\n* [`upstart`](#service-provider-upstart)\n* [`windows`](#service-provider-windows)\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### restart {#service-attribute-restart}\n\nSpecify a *restart* command manually.  If left\nunspecified, the service will be stopped and then started.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### start {#service-attribute-start}\n\nSpecify a *start* command manually.  Most service subsystems\nsupport a `start` command, so this will not need to be\nspecified.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### status {#service-attribute-status}\n\nSpecify a *status* command manually.  This command must\nreturn 0 if the service is running and a nonzero value otherwise.\nIdeally, these exit codes should conform to [the LSB's\nspecification][lsb-exit-codes] for init script status actions, but\nPuppet only considers the difference between 0 and nonzero to be\nrelevant.\n\nIf left unspecified, the status of the service will be determined\nautomatically, usually by looking for the service in the process\ntable.\n\n[lsb-exit-codes]: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### stop {#service-attribute-stop}\n\nSpecify a *stop* command manually.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n#### timeout {#service-attribute-timeout}\n\nSpecify an optional minimum timeout (in seconds) for puppet to wait when syncing service properties\n\nRequires features configurable_timeout.\n\n([↑ Back to service attributes](#service-attributes))\n\n\n### Providers {#service-providers}\n\n#### base {#service-provider-base}\n\nThe simplest form of Unix service support.\n\nYou have to specify enough about your service for this to work; the\nminimum you can specify is a binary for starting the process, and this\nsame binary will be searched for in the process table to stop the\nservice.  As with `init`-style services, it is preferable to specify start,\nstop, and status commands.\n\n* Required binaries: `kill`\n\n#### bsd {#service-provider-bsd}\n\nGeneric BSD form of `init`-style service management with `rc.d`.\n\nUses `rc.conf.d` for service enabling and disabling.\n\n* Confined to: `os.name == [:freebsd, :dragonfly]`\n\n#### daemontools {#service-provider-daemontools}\n\nDaemontools service management.\n\nThis provider manages daemons supervised by D.J. Bernstein daemontools.\nWhen detecting the service directory it will check, in order of preference:\n\n* `/service`\n* `/etc/service`\n* `/var/lib/svscan`\n\nThe daemon directory should be in one of the following locations:\n\n* `/var/lib/service`\n* `/etc`\n\n...or this can be overridden in the resource's attributes:\n\n    service { 'myservice':\n      provider => 'daemontools',\n      path     => '/path/to/daemons',\n    }\n\nThis provider supports out of the box:\n\n* start/stop (mapped to enable/disable)\n* enable/disable\n* restart\n* status\n\nIf a service has `ensure => \"running\"`, it will link /path/to/daemon to\n/path/to/service, which will automatically enable the service.\n\nIf a service has `ensure => \"stopped\"`, it will only shut down the service, not\nremove the `/path/to/service` link.\n\n* Required binaries: `/usr/bin/svc`, `/usr/bin/svstat`\n\n#### debian {#service-provider-debian}\n\nDebian's form of `init`-style management.\n\nThe only differences from `init` are support for enabling and disabling\nservices via `update-rc.d` and the ability to determine enabled status via\n`invoke-rc.d`.\n\n* Required binaries: `/usr/sbin/invoke-rc.d`, `/usr/sbin/service`, `/usr/sbin/update-rc.d`\n* Confined to: `false == Puppet::FileSystem.exist?('/proc/1/comm') && Puppet::FileSystem.read('/proc/1/comm').include?('systemd')`\n* Default for: `[\"os.name\", \"cumuluslinux\"] == [\"os.release.major\", \"%w[1 2]\"]`, `[\"os.name\", \"debian\"] == [\"os.release.major\", \"%w[5 6 7]\"]`, `[\"os.name\", \"devuan\"] == `\n\n#### freebsd {#service-provider-freebsd}\n\nProvider for FreeBSD and DragonFly BSD. Uses the `rcvar` argument of init scripts and parses/edits rc files.\n\n* Confined to: `os.name == [:freebsd, :dragonfly]`\n* Default for: `[\"os.name\", \"[:freebsd, :dragonfly]\"] == `\n\n#### gentoo {#service-provider-gentoo}\n\nGentoo's form of `init`-style service management.\n\nUses `rc-update` for service enabling and disabling.\n\n* Required binaries: `/sbin/rc-update`\n* Confined to: `os.name == gentoo`\n\n#### init {#service-provider-init}\n\nStandard `init`-style service management.\n\n* Confined to: `false == Puppet.runtime[:facter].value('os.family') == 'RedHat'`\n\n#### launchd {#service-provider-launchd}\n\nThis provider manages jobs with `launchd`, which is the default service\nframework for Mac OS X (and may be available for use on other platforms).\n\nFor more information, see the `launchd` man page:\n\n* <https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man8/launchd.8.html>\n\nThis provider reads plists out of the following directories:\n\n* `/System/Library/LaunchDaemons`\n* `/System/Library/LaunchAgents`\n* `/Library/LaunchDaemons`\n* `/Library/LaunchAgents`\n\n...and builds up a list of services based upon each plist's \"Label\" entry.\n\nThis provider supports:\n\n* ensure => running/stopped,\n* enable => true/false\n* status\n* restart\n\nHere is how the Puppet states correspond to `launchd` states:\n\n* stopped --- job unloaded\n* started --- job loaded\n* enabled --- 'Disable' removed from job plist file\n* disabled --- 'Disable' added to job plist file\n\nNote that this allows you to do something `launchctl` can't do, which is to\nbe in a state of \"stopped/enabled\" or \"running/disabled\".\n\nNote that this provider does not support overriding 'restart'\n\n* Required binaries: `/bin/launchctl`\n* Confined to: `os.name == darwin`, `feature == cfpropertylist`\n* Default for: `[\"os.name\", \"darwin\"] == `\n* Supported features: `enableable`, `refreshable`\n\n#### openbsd {#service-provider-openbsd}\n\nProvider for OpenBSD's rc.d daemon control scripts\n\n* Required binaries: `/usr/sbin/rcctl`\n* Confined to: `os.name == openbsd`\n* Default for: `[\"os.name\", \"openbsd\"] == `\n* Supported features: `flaggable`\n\n#### openrc {#service-provider-openrc}\n\nSupport for Gentoo's OpenRC initskripts\n\nUses rc-update, rc-status and rc-service to manage services.\n\n* Required binaries: `/sbin/rc-service`, `/sbin/rc-update`\n* Default for: `[\"os.name\", \"gentoo\"] == `, `[\"os.name\", \"funtoo\"] == `\n\n#### openwrt {#service-provider-openwrt}\n\nSupport for OpenWrt flavored init scripts.\n\nUses /etc/init.d/service_name enable, disable, and enabled.\n\n* Confined to: `os.name == openwrt`\n* Default for: `[\"os.name\", \"openwrt\"] == `\n* Supported features: `enableable`\n\n#### rcng {#service-provider-rcng}\n\nRCng service management with rc.d\n\n* Confined to: `os.name == [:netbsd, :cargos]`\n* Default for: `[\"os.name\", \"[:netbsd, :cargos]\"] == `\n\n#### redhat {#service-provider-redhat}\n\nRed Hat's (and probably many others') form of `init`-style service\nmanagement. Uses `chkconfig` for service enabling and disabling.\n\n* Required binaries: `/sbin/chkconfig`, `/sbin/service`\n* Default for: `[\"os.name\", \"amazon\"] == [\"os.release.major\", \"%w[2017 2018]\"]`, `[\"os.name\", \"redhat\"] == [\"os.release.major\", \"(4..6).to_a\"]`, `[\"os.family\", \"suse\"] == [\"os.release.major\", \"%w[10 11]\"]`\n\n#### runit {#service-provider-runit}\n\nRunit service management.\n\nThis provider manages daemons running supervised by Runit.\nWhen detecting the service directory it will check, in order of preference:\n\n* `/service`\n* `/etc/service`\n* `/var/service`\n\nThe daemon directory should be in one of the following locations:\n\n* `/etc/sv`\n* `/var/lib/service`\n\nor this can be overridden in the service resource parameters:\n\n    service { 'myservice':\n      provider => 'runit',\n      path     => '/path/to/daemons',\n    }\n\nThis provider supports out of the box:\n\n* start/stop\n* enable/disable\n* restart\n* status\n\n* Required binaries: `/usr/bin/sv`\n\n#### service {#service-provider-service}\n\nThe simplest form of service support.\n\n#### smf {#service-provider-smf}\n\nSupport for Sun's new Service Management Framework.\n\nWhen managing the enable property, this provider will try to preserve\nthe previous ensure state per the enableable semantics. On Solaris,\nenabling a service starts it up while disabling a service stops it. Thus,\nthere's a chance for this provider to execute two operations when managing\nthe enable property. For example, if enable is set to true and the ensure\nstate is stopped, this provider will manage the service using two operations:\none to enable the service which will start it up, and another to stop the\nservice (without affecting its enabled status).\n\nBy specifying `manifest => \"/path/to/service.xml\"`, the SMF manifest will\nbe imported if it does not exist.\n\n* Required binaries: `/usr/bin/svcs`, `/usr/sbin/svcadm`, `/usr/sbin/svccfg`\n* Confined to: `os.family == solaris`\n* Default for: `[\"os.family\", \"solaris\"] == `\n* Supported features: `refreshable`\n\n#### src {#service-provider-src}\n\nSupport for AIX's System Resource controller.\n\nServices are started/stopped based on the `stopsrc` and `startsrc`\ncommands, and some services can be refreshed with `refresh` command.\n\nEnabling and disabling services is not supported, as it requires\nmodifications to `/etc/inittab`. Starting and stopping groups of subsystems\nis not yet supported.\n\n* Confined to: `os.name == aix`\n* Default for: `[\"os.name\", \"aix\"] == `\n* Supported features: `refreshable`\n\n#### systemd {#service-provider-systemd}\n\nManages `systemd` services using `systemctl`.\n\nBecause `systemd` defaults to assuming the `.service` unit type, the suffix\nmay be omitted.  Other unit types (such as `.path`) may be managed by\nproviding the proper suffix.\n\n* Required binaries: `systemctl`\n* Confined to: `true == Puppet::FileSystem.exist?('/proc/1/comm') && Puppet::FileSystem.read('/proc/1/comm').include?('systemd')`\n* Default for: `[\"os.family\", \"[:archlinux]\"] == `, `[\"os.family\", \"redhat\"] == `, `[\"os.family\", \"redhat\"] == [\"os.name\", \"fedora\"]`, `[\"os.family\", \"suse\"] == `, `[\"os.family\", \"coreos\"] == `, `[\"os.family\", \"gentoo\"] == `, `[\"os.name\", \"amazon\"] == [\"os.release.major\", \"%w[2 2023]\"]`, `[\"os.name\", \"debian\"] == `, `[\"os.name\", \"LinuxMint\"] == `, `[\"os.name\", \"ubuntu\"] == `, `[\"os.name\", \"cumuluslinux\"] == [\"os.release.major\", \"%w[3 4]\"]`, `[\"os.name\", \"raspbian\"] == [\"os.release.major\", \"%w[12]\"]`\n\n#### upstart {#service-provider-upstart}\n\nUbuntu service management with `upstart`.\n\nThis provider manages `upstart` jobs on Ubuntu. For `upstart` documentation,\nsee <http://upstart.ubuntu.com/>.\n\n* Required binaries: `/sbin/initctl`, `/sbin/restart`, `/sbin/start`, `/sbin/status`, `/sbin/stop`\n* Confined to: `any == [\n    Puppet.runtime[:facter].value('os.name') == 'Ubuntu',\n    (Puppet.runtime[:facter].value('os.family') == 'RedHat' and Puppet.runtime[:facter].value('os.release.full') =~ /^6\\./),\n    (Puppet.runtime[:facter].value('os.name') == 'Amazon' and Puppet.runtime[:facter].value('os.release.major') =~ /\\d{4}/),\n    Puppet.runtime[:facter].value('os.name') == 'LinuxMint'\n  ]`, `true == -> { has_initctl? }`\n* Default for: `[\"os.name\", \"ubuntu\"] == [\"os.release.major\", \"[\\\"10.04\\\", \\\"12.04\\\", \\\"14.04\\\", \\\"14.10\\\"]\"]`, `[\"os.name\", \"LinuxMint\"] == [\"os.release.major\", \"%w[10 11 12 13 14 15 16 17]\"]`\n* Supported features: `enableable`\n\n#### windows {#service-provider-windows}\n\nSupport for Windows Service Control Manager (SCM). This provider can\nstart, stop, enable, and disable services, and the SCM provides working\nstatus methods for all services.\n\nControl of service groups (dependencies) is not yet supported, nor is running\nservices as a specific user.\n\n* Confined to: `os.name == windows`\n* Default for: `[\"os.name\", \"windows\"] == `\n* Supported features: `configurable_timeout`, `manages_logon_credentials`, `refreshable`\n\n### Provider Features {#service-provider-features}\n\nAvailable features:\n\n* `configurable_timeout` --- The provider can specify a minumum timeout for syncing service properties\n* `controllable` --- The provider uses a control variable.\n* `delayed_startable` --- The provider can set service to delayed start\n* `enableable` --- The provider can enable and disable the service.\n* `flaggable` --- The provider can pass flags to the service.\n* `manages_logon_credentials` --- The provider can specify the logon credentials used for a service\n* `manual_startable` --- The provider can set service to manual start\n* `maskable` --- The provider can 'mask' the service.\n* `refreshable` --- The provider can restart the service.\n\nProvider support:\n\n* **base** - No supported Provider features\n* **bsd** - No supported Provider features\n* **daemontools** - No supported Provider features\n* **debian** - No supported Provider features\n* **freebsd** - No supported Provider features\n* **gentoo** - No supported Provider features\n* **init** - No supported Provider features\n* **launchd** - _enableable, refreshable_\n* **openbsd** - _flaggable_\n* **openrc** - No supported Provider features\n* **openwrt** - _enableable_\n* **rcng** - No supported Provider features\n* **redhat** - No supported Provider features\n* **runit** - No supported Provider features\n* **service** - No supported Provider features\n* **smf** - _refreshable_\n* **src** - _refreshable_\n* **systemd** - No supported Provider features\n* **upstart** - _enableable_\n* **windows** - _refreshable, configurable timeout, manages logon credentials_\n  \n\n\n\n\n"
  },
  {
    "path": "references/types/stage.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: stage'\ncanonical: \"/puppet/latest/types/stage.html\"\n---\n\n# Resource Type: stage\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## stage\n\n* [Attributes](#stage-attributes)\n\n### Description {#stage-description}\n\nA resource type for creating new run stages.  Once a stage is available,\nclasses can be assigned to it by declaring them with the resource-like syntax\nand using\n[the `stage` metaparameter](https://puppet.com/docs/puppet/latest/metaparameter.html#stage).\n\nNote that new stages are not useful unless you also declare their order\nin relation to the default `main` stage.\n\nA complete run stage example:\n\n    stage { 'pre':\n      before => Stage['main'],\n    }\n\n    class { 'apt-updates':\n      stage => 'pre',\n    }\n\nIndividual resources cannot be assigned to run stages; you can only set stages\nfor classes.\n\n### Attributes {#stage-attributes}\n\n<pre><code>stage { 'resource title':\n  <a href=\"#stage-attribute-name\">name</a> =&gt; <em># <strong>(namevar)</strong> The name of the stage. Use this as the value for </em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#stage-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe name of the stage. Use this as the value for the `stage` metaparameter\nwhen assigning classes to this stage.\n\n([↑ Back to stage attributes](#stage-attributes))\n\n\n\n\n\n"
  },
  {
    "path": "references/types/tidy.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: tidy'\ncanonical: \"/puppet/latest/types/tidy.html\"\n---\n\n# Resource Type: tidy\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## tidy\n\n* [Attributes](#tidy-attributes)\n\n### Description {#tidy-description}\n\nRemove unwanted files based on specific criteria.  Multiple\ncriteria are OR'd together, so a file that is too large but is not\nold enough will still get tidied. Ignores managed resources.\n\nIf you don't specify either `age` or `size`, then all files will\nbe removed.\n\nThis resource type works by generating a file resource for every file\nthat should be deleted and then letting that resource perform the\nactual deletion.\n\n### Attributes {#tidy-attributes}\n\n<pre><code>tidy { 'resource title':\n  <a href=\"#tidy-attribute-path\">path</a>      =&gt; <em># <strong>(namevar)</strong> The path to the file or directory to manage....</em>\n  <a href=\"#tidy-attribute-age\">age</a>       =&gt; <em># Tidy files whose age is equal to or greater than </em>\n  <a href=\"#tidy-attribute-backup\">backup</a>    =&gt; <em># Whether tidied files should be backed up.  Any...</em>\n  <a href=\"#tidy-attribute-matches\">matches</a>   =&gt; <em># One or more (shell type) file glob patterns...</em>\n  <a href=\"#tidy-attribute-max_files\">max_files</a> =&gt; <em># In case the resource is a directory and the...</em>\n  <a href=\"#tidy-attribute-recurse\">recurse</a>   =&gt; <em># If target is a directory, recursively descend...</em>\n  <a href=\"#tidy-attribute-rmdirs\">rmdirs</a>    =&gt; <em># Tidy directories in addition to files; that is...</em>\n  <a href=\"#tidy-attribute-size\">size</a>      =&gt; <em># Tidy files whose size is equal to or greater...</em>\n  <a href=\"#tidy-attribute-type\">type</a>      =&gt; <em># Set the mechanism for determining age.  Default: </em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### path {#tidy-attribute-path}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe path to the file or directory to manage.  Must be fully\nqualified.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### age {#tidy-attribute-age}\n\nTidy files whose age is equal to or greater than\nthe specified time.  You can choose seconds, minutes,\nhours, days, or weeks by specifying the first letter of any\nof those words (for example, '1w' represents one week).\n\nSpecifying 0 will remove all files.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### backup {#tidy-attribute-backup}\n\nWhether tidied files should be backed up.  Any values are passed\ndirectly to the file resources used for actual file deletion, so consult\nthe `file` type's backup documentation to determine valid values.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### matches {#tidy-attribute-matches}\n\nOne or more (shell type) file glob patterns, which restrict\nthe list of files to be tidied to those whose basenames match\nat least one of the patterns specified. Multiple patterns can\nbe specified using an array.\n\nExample:\n\n    tidy { '/tmp':\n      age     => '1w',\n      recurse => 1,\n      matches => [ '[0-9]pub*.tmp', '*.temp', 'tmpfile?' ],\n    }\n\nThis removes files from `/tmp` if they are one week old or older,\nare not in a subdirectory and match one of the shell globs given.\n\nNote that the patterns are matched against the basename of each\nfile -- that is, your glob patterns should not have any '/'\ncharacters in them, since you are only specifying against the last\nbit of the file.\n\nFinally, note that you must now specify a non-zero/non-false value\nfor recurse if matches is used, as matches only apply to files found\nby recursion (there's no reason to use static patterns match against\na statically determined path).  Requiring explicit recursion clears\nup a common source of confusion.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### max_files {#tidy-attribute-max_files}\n\nIn case the resource is a directory and the recursion is enabled, puppet will\ngenerate a new resource for each file file found, possible leading to\nan excessive number of resources generated without any control.\n\nSetting `max_files` will check the number of file resources that\nwill eventually be created and will raise a resource argument error if the\nlimit will be exceeded.\n\nUse value `0` to disable the check. In this case, a warning is logged if\nthe number of files exceeds 1000.\n\nDefault: `0`\n\nAllowed values:\n\n* `/^[0-9]+$/`\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### recurse {#tidy-attribute-recurse}\n\nIf target is a directory, recursively descend\ninto the directory looking for files to tidy. Numeric values\nspecify a limit for the recursion depth, `true` means\nunrestricted recursion.\n\nAllowed values:\n\n* `true`\n* `false`\n* `inf`\n* `/^[0-9]+$/`\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### rmdirs {#tidy-attribute-rmdirs}\n\nTidy directories in addition to files; that is, remove\ndirectories whose age is older than the specified criteria.\nThis will only remove empty directories, so all contained\nfiles must also be tidied before a directory gets removed.\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### size {#tidy-attribute-size}\n\nTidy files whose size is equal to or greater than\nthe specified size.  Unqualified values are in kilobytes, but\n*b*, *k*, *m*, *g*, and *t* can be appended to specify *bytes*,\n*kilobytes*, *megabytes*, *gigabytes*, and *terabytes*, respectively.\nOnly the first character is significant, so the full word can also\nbe used.\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n#### type {#tidy-attribute-type}\n\nSet the mechanism for determining age.\n\nDefault: `atime`\n\nAllowed values:\n\n* `atime`\n* `mtime`\n* `ctime`\n\n([↑ Back to tidy attributes](#tidy-attributes))\n\n\n\n\n\n"
  },
  {
    "path": "references/types/user.md",
    "content": "---\nlayout: default\nbuilt_from_commit: a0909f4eae7490d52cb1e7dc81010592ba607679\ntitle: 'Resource Type: user'\ncanonical: \"/puppet/latest/types/user.html\"\n---\n\n# Resource Type: user\n\n> **NOTE:** This page was generated from the Puppet source code on 2024-11-04 23:38:25 +0000\n\n\n\n## user\n\n* [Attributes](#user-attributes)\n* [Providers](#user-providers)\n* [Provider Features](#user-provider-features)\n\n### Description {#user-description}\n\nManage users.  This type is mostly built to manage system\nusers, so it is lacking some features useful for managing normal\nusers.\n\nThis resource type uses the prescribed native tools for creating\ngroups and generally uses POSIX APIs for retrieving information\nabout them.  It does not directly modify `/etc/passwd` or anything.\n\n**Autorequires:** If Puppet is managing the user's primary group (as\nprovided in the `gid` attribute) or any group listed in the `groups`\nattribute then the user resource will autorequire that group. If Puppet\nis managing any role accounts corresponding to the user's roles, the\nuser resource will autorequire those role accounts.\n\n### Attributes {#user-attributes}\n\n<pre><code>user { 'resource title':\n  <a href=\"#user-attribute-name\">name</a>                 =&gt; <em># <strong>(namevar)</strong> The user name. While naming limitations vary by...</em>\n  <a href=\"#user-attribute-ensure\">ensure</a>               =&gt; <em># The basic state that the object should be in....</em>\n  <a href=\"#user-attribute-allowdupe\">allowdupe</a>            =&gt; <em># Whether to allow duplicate UIDs.  Default...</em>\n  <a href=\"#user-attribute-attribute_membership\">attribute_membership</a> =&gt; <em># Whether specified attribute value pairs should...</em>\n  <a href=\"#user-attribute-attributes\">attributes</a>           =&gt; <em># Specify AIX attributes for the user in an array...</em>\n  <a href=\"#user-attribute-auth_membership\">auth_membership</a>      =&gt; <em># Whether specified auths should be considered the </em>\n  <a href=\"#user-attribute-auths\">auths</a>                =&gt; <em># The auths the user has.  Multiple auths should...</em>\n  <a href=\"#user-attribute-comment\">comment</a>              =&gt; <em># A description of the user.  Generally the user's </em>\n  <a href=\"#user-attribute-expiry\">expiry</a>               =&gt; <em># The expiry date for this user. Provide as either </em>\n  <a href=\"#user-attribute-forcelocal\">forcelocal</a>           =&gt; <em># Forces the management of local accounts when...</em>\n  <a href=\"#user-attribute-gid\">gid</a>                  =&gt; <em># The user's primary group.  Can be specified...</em>\n  <a href=\"#user-attribute-groups\">groups</a>               =&gt; <em># The groups to which the user belongs.  The...</em>\n  <a href=\"#user-attribute-home\">home</a>                 =&gt; <em># The home directory of the user.  The directory...</em>\n  <a href=\"#user-attribute-ia_load_module\">ia_load_module</a>       =&gt; <em># The name of the I&A module to use to manage this </em>\n  <a href=\"#user-attribute-iterations\">iterations</a>           =&gt; <em># This is the number of iterations of a chained...</em>\n  <a href=\"#user-attribute-key_membership\">key_membership</a>       =&gt; <em># Whether specified key/value pairs should be...</em>\n  <a href=\"#user-attribute-keys\">keys</a>                 =&gt; <em># Specify user attributes in an array of key ...</em>\n  <a href=\"#user-attribute-loginclass\">loginclass</a>           =&gt; <em># The name of login class to which the user...</em>\n  <a href=\"#user-attribute-managehome\">managehome</a>           =&gt; <em># Whether to manage the home directory when Puppet </em>\n  <a href=\"#user-attribute-membership\">membership</a>           =&gt; <em># If `minimum` is specified, Puppet will ensure...</em>\n  <a href=\"#user-attribute-password\">password</a>             =&gt; <em># The user's password, in whatever encrypted...</em>\n  <a href=\"#user-attribute-password_max_age\">password_max_age</a>     =&gt; <em># The maximum number of days a password may be...</em>\n  <a href=\"#user-attribute-password_min_age\">password_min_age</a>     =&gt; <em># The minimum number of days a password must be...</em>\n  <a href=\"#user-attribute-password_warn_days\">password_warn_days</a>   =&gt; <em># The number of days before a password is going to </em>\n  <a href=\"#user-attribute-profile_membership\">profile_membership</a>   =&gt; <em># Whether specified roles should be treated as the </em>\n  <a href=\"#user-attribute-profiles\">profiles</a>             =&gt; <em># The profiles the user has.  Multiple profiles...</em>\n  <a href=\"#user-attribute-project\">project</a>              =&gt; <em># The name of the project associated with a...</em>\n  <a href=\"#user-attribute-provider\">provider</a>             =&gt; <em># The specific backend to use for this `user...</em>\n  <a href=\"#user-attribute-purge_ssh_keys\">purge_ssh_keys</a>       =&gt; <em># Whether to purge authorized SSH keys for this...</em>\n  <a href=\"#user-attribute-role_membership\">role_membership</a>      =&gt; <em># Whether specified roles should be considered the </em>\n  <a href=\"#user-attribute-roles\">roles</a>                =&gt; <em># The roles the user has.  Multiple roles should...</em>\n  <a href=\"#user-attribute-salt\">salt</a>                 =&gt; <em># This is the 32-byte salt used to generate the...</em>\n  <a href=\"#user-attribute-shell\">shell</a>                =&gt; <em># The user's login shell.  The shell must exist...</em>\n  <a href=\"#user-attribute-system\">system</a>               =&gt; <em># Whether the user is a system user, according to...</em>\n  <a href=\"#user-attribute-uid\">uid</a>                  =&gt; <em># The user ID; must be specified numerically. If...</em>\n  # ...plus any applicable <a href=\"https://puppet.com/docs/puppet/latest/metaparameter.html\">metaparameters</a>.\n}</code></pre>\n\n\n#### name {#user-attribute-name}\n\n_(**Namevar:** If omitted, this attribute's value defaults to the resource's title.)_\n\nThe user name. While naming limitations vary by operating system,\nit is advisable to restrict names to the lowest common denominator,\nwhich is a maximum of 8 characters beginning with a letter.\n\nNote that Puppet considers user names to be case-sensitive, regardless\nof the platform's own rules; be sure to always use the same case when\nreferring to a given user.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### ensure {#user-attribute-ensure}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe basic state that the object should be in.\n\nAllowed values:\n\n* `present`\n* `absent`\n* `role`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### allowdupe {#user-attribute-allowdupe}\n\nWhether to allow duplicate UIDs.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### attribute_membership {#user-attribute-attribute_membership}\n\nWhether specified attribute value pairs should be treated as the\n**complete list** (`inclusive`) or the **minimum list** (`minimum`) of\nattribute/value pairs for the user.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### attributes {#user-attribute-attributes}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify AIX attributes for the user in an array or hash of attribute = value pairs.\n\n For example:\n\n ```\n ['minage=0', 'maxage=5', 'SYSTEM=compat']\n ```\n\n or\n\n```\nattributes => { 'minage' => '0', 'maxage' => '5', 'SYSTEM' => 'compat' }\n```\n\nRequires features manages_aix_lam.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### auth_membership {#user-attribute-auth_membership}\n\nWhether specified auths should be considered the **complete list**\n(`inclusive`) or the **minimum list** (`minimum`) of auths the user\nhas. This setting is specific to managing Solaris authorizations.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### auths {#user-attribute-auths}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe auths the user has.  Multiple auths should be\nspecified as an array.\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### comment {#user-attribute-comment}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nA description of the user.  Generally the user's full name.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### expiry {#user-attribute-expiry}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe expiry date for this user. Provide as either the special\nvalue `absent` to ensure that the account never expires, or as\na zero-padded YYYY-MM-DD format -- for example, 2010-02-19.\n\nAllowed values:\n\n* `absent`\n* `/^\\d{4}-\\d{2}-\\d{2}$/`\n\nRequires features manages_expiry.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### forcelocal {#user-attribute-forcelocal}\n\nForces the management of local accounts when accounts are also\nbeing managed by some other Name Service Switch (NSS). For AIX, refer to the `ia_load_module` parameter.\n\nThis option relies on your operating system's implementation of `luser*` commands, such as `luseradd` , and `lgroupadd`, `lusermod`. The `forcelocal` option could behave unpredictably in some circumstances. If the tools it depends on are not available, it might have no effect at all.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\nRequires features manages_local_users_and_groups.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### gid {#user-attribute-gid}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user's primary group.  Can be specified numerically or by name.\n\nThis attribute is not supported on Windows systems; use the `groups`\nattribute instead. (On Windows, designating a primary group is only\nmeaningful for domain accounts, which Puppet does not currently manage.)\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### groups {#user-attribute-groups}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe groups to which the user belongs.  The primary group should\nnot be listed, and groups should be identified by name rather than by\nGID.  Multiple groups should be specified as an array.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### home {#user-attribute-home}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe home directory of the user.  The directory must be created\nseparately and is not currently checked for existence.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### ia_load_module {#user-attribute-ia_load_module}\n\nThe name of the I&A module to use to manage this user.\nThis should be set to `files` if managing local users.\n\nRequires features manages_aix_lam.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### iterations {#user-attribute-iterations}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThis is the number of iterations of a chained computation of the\n[PBKDF2 password hash](https://en.wikipedia.org/wiki/PBKDF2). This parameter\nis used in OS X, and is required for managing passwords on OS X 10.8 and\nnewer.\n\nRequires features manages_password_salt.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### key_membership {#user-attribute-key_membership}\n\nWhether specified key/value pairs should be considered the\n**complete list** (`inclusive`) or the **minimum list** (`minimum`) of\nthe user's attributes.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### keys {#user-attribute-keys}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nSpecify user attributes in an array of key = value pairs.\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### loginclass {#user-attribute-loginclass}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe name of login class to which the user belongs.\n\nRequires features manages_loginclass.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### managehome {#user-attribute-managehome}\n\nWhether to manage the home directory when Puppet creates or removes the user.\nThis creates the home directory if Puppet also creates the user account, and deletes the\nhome directory if Puppet also removes the user account.\n\nThis parameter has no effect unless Puppet is also creating or removing the user in the\nresource at the same time. For instance, Puppet creates a home directory for a managed\nuser if `ensure => present` and the user does not exist at the time of the Puppet run.\nIf the home directory is then deleted manually, Puppet will not recreate it on the next\nrun.\n\nNote that on Windows, this manages creation/deletion of the user profile instead of the\nhome directory. The user profile is stored in the `C:\\\\Users\\\\<username>` directory.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### membership {#user-attribute-membership}\n\nIf `minimum` is specified, Puppet will ensure that the user is a\nmember of all specified groups, but will not remove any other groups\nthat the user is a part of.\n\nIf `inclusive` is specified, Puppet will ensure that the user is a\nmember of **only** specified groups.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password {#user-attribute-password}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user's password, in whatever encrypted format the local system\nrequires. Consult your operating system's documentation for acceptable password\nencryption formats and requirements.\n\n* Mac OS X 10.5 and 10.6, and some older Linux distributions, use salted SHA1\n  hashes. You can use Puppet's built-in `sha1` function to generate a salted SHA1\n  hash from a password.\n* Mac OS X 10.7 (Lion), and many recent Linux distributions, use salted SHA512\n  hashes. The Puppet Labs [stdlib][] module contains a `str2saltedsha512` function\n  which can generate password hashes for these operating systems.\n* OS X 10.8 and higher use salted SHA512 PBKDF2 hashes. When managing passwords\n  on these systems, the `salt` and `iterations` attributes need to be specified as\n  well as the password.\n* macOS 10.15 and later require the salt to be 32 bytes. Because Puppet's user\n  resource requires the value to be hex encoded, the length of the salt's\n  string must be 64.\n* Windows passwords can be managed only in cleartext, because there is no Windows\n  API for setting the password hash.\n\n[stdlib]: https://github.com/puppetlabs/puppetlabs-stdlib/\n\nEnclose any value that includes a dollar sign ($) in single quotes (') to avoid\naccidental variable interpolation.\n\nTo redact passwords from reports to PuppetDB, use the `Sensitive` data type. For\nexample, this resource protects the password:\n\n```puppet\nuser { 'foo':\n  ensure   => present,\n  password => Sensitive(\"my secret password\")\n}\n```\n\nThis results in the password being redacted from the report, as in the\n`previous_value`, `desired_value`, and `message` fields below.\n\n```yaml\n    events:\n    - !ruby/object:Puppet::Transaction::Event\n      audited: false\n      property: password\n      previous_value: \"[redacted]\"\n      desired_value: \"[redacted]\"\n      historical_value:\n      message: changed [redacted] to [redacted]\n      name: :password_changed\n      status: success\n      time: 2017-05-17 16:06:02.934398293 -07:00\n      redacted: true\n      corrective_change: false\n    corrective_change: false\n```\n\nRequires features manages_passwords.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password_max_age {#user-attribute-password_max_age}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe maximum number of days a password may be used before it must be changed.\n\nRequires features manages_password_age.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password_min_age {#user-attribute-password_min_age}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe minimum number of days a password must be used before it may be changed.\n\nRequires features manages_password_age.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### password_warn_days {#user-attribute-password_warn_days}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe number of days before a password is going to expire (see the maximum password age) during which the user should be warned.\n\nRequires features manages_password_age.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### profile_membership {#user-attribute-profile_membership}\n\nWhether specified roles should be treated as the **complete list**\n(`inclusive`) or the **minimum list** (`minimum`) of roles\nof which the user is a member.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### profiles {#user-attribute-profiles}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe profiles the user has.  Multiple profiles should be\nspecified as an array.\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### project {#user-attribute-project}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe name of the project associated with a user.\n\nRequires features manages_solaris_rbac.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### provider {#user-attribute-provider}\n\nThe specific backend to use for this `user` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform.\n\nAvailable providers are:\n\n* [`aix`](#user-provider-aix)\n* [`directoryservice`](#user-provider-directoryservice)\n* [`hpuxuseradd`](#user-provider-hpuxuseradd)\n* [`ldap`](#user-provider-ldap)\n* [`openbsd`](#user-provider-openbsd)\n* [`pw`](#user-provider-pw)\n* [`user_role_add`](#user-provider-user_role_add)\n* [`useradd`](#user-provider-useradd)\n* [`windows_adsi`](#user-provider-windows_adsi)\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### purge_ssh_keys {#user-attribute-purge_ssh_keys}\n\nWhether to purge authorized SSH keys for this user if they are not managed\nwith the `ssh_authorized_key` resource type. This parameter is a noop if the\nssh_authorized_key type is not available.\n\nAllowed values are:\n\n* `false` (default) --- don't purge SSH keys for this user.\n* `true` --- look for keys in the `.ssh/authorized_keys` file in the user's\n  home directory. Purge any keys that aren't managed as `ssh_authorized_key`\n  resources.\n* An array of file paths --- look for keys in all of the files listed. Purge\n  any keys that aren't managed as `ssh_authorized_key` resources. If any of\n  these paths starts with `~` or `%h`, that token will be replaced with\n  the user's home directory.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### role_membership {#user-attribute-role_membership}\n\nWhether specified roles should be considered the **complete list**\n(`inclusive`) or the **minimum list** (`minimum`) of roles the user\nhas.\n\nDefault: `minimum`\n\nAllowed values:\n\n* `inclusive`\n* `minimum`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### roles {#user-attribute-roles}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe roles the user has.  Multiple roles should be\nspecified as an array.\n\nRequires features manages_roles.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### salt {#user-attribute-salt}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThis is the 32-byte salt used to generate the PBKDF2 password used in\nOS X. This field is required for managing passwords on OS X >= 10.8.\n\nRequires features manages_password_salt.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### shell {#user-attribute-shell}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user's login shell.  The shell must exist and be\nexecutable.\n\nThis attribute cannot be managed on Windows systems.\n\nRequires features manages_shell.\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### system {#user-attribute-system}\n\nWhether the user is a system user, according to the OS's criteria;\non most platforms, a UID less than or equal to 500 indicates a system\nuser. This parameter is only used when the resource is created and will\nnot affect the UID when the user is present.\n\nDefault: `false`\n\nAllowed values:\n\n* `true`\n* `false`\n* `yes`\n* `no`\n\n([↑ Back to user attributes](#user-attributes))\n\n\n#### uid {#user-attribute-uid}\n\n_(**Property:** This attribute represents concrete state on the target system.)_\n\nThe user ID; must be specified numerically. If no user ID is\nspecified when creating a new user, then one will be chosen\nautomatically. This will likely result in the same user having\ndifferent UIDs on different systems, which is not recommended. This is\nespecially noteworthy when managing the same user on both Darwin and\nother platforms, since Puppet does UID generation on Darwin, but\nthe underlying tools do so on other platforms.\n\nOn Windows, this property is read-only and will return the user's\nsecurity identifier (SID).\n\n([↑ Back to user attributes](#user-attributes))\n\n\n### Providers {#user-providers}\n\n#### aix {#user-provider-aix}\n\nUser management for AIX.\n\n* Required binaries: `/bin/chpasswd`, `/usr/bin/chuser`, `/usr/bin/mkuser`, `/usr/sbin/lsuser`, `/usr/sbin/rmuser`\n* Confined to: `os.name == aix`\n* Default for: `[\"os.name\", \"aix\"] == `\n* Supported features: `manages_aix_lam`, `manages_expiry`, `manages_homedir`, `manages_local_users_and_groups`, `manages_password_age`, `manages_passwords`, `manages_shell`\n\n#### directoryservice {#user-provider-directoryservice}\n\nUser management on OS X.\n\n* Required binaries: `/usr/bin/dscacheutil`, `/usr/bin/dscl`, `/usr/bin/dsimport`, `/usr/bin/uuidgen`\n* Confined to: `os.name == darwin`, `feature == cfpropertylist`\n* Default for: `[\"os.name\", \"darwin\"] == `\n* Supported features: `manages_password_salt`, `manages_passwords`, `manages_shell`\n\n#### hpuxuseradd {#user-provider-hpuxuseradd}\n\nUser management for HP-UX. This provider uses the undocumented `-F`\nswitch to HP-UX's special `usermod` binary to work around the fact that\nits standard `usermod` cannot make changes while the user is logged in.\nNew functionality provides for changing trusted computing passwords and\nresetting password expirations under trusted computing.\n\n* Required binaries: `/usr/sam/lbin/useradd.sam`, `/usr/sam/lbin/userdel.sam`, `/usr/sam/lbin/usermod.sam`\n* Confined to: `os.name == hp-ux`\n* Default for: `[\"os.name\", \"hp-ux\"] == `\n* Supported features: `allows_duplicates`, `manages_homedir`, `manages_passwords`\n\n#### ldap {#user-provider-ldap}\n\nUser management via LDAP.\n\nThis provider requires that you have valid values for all of the\nLDAP-related settings in `puppet.conf`, including `ldapbase`.  You will\nalmost definitely need settings for `ldapuser` and `ldappassword` in order\nfor your clients to write to LDAP.\n\nNote that this provider will automatically generate a UID for you if\nyou do not specify one, but it is a potentially expensive operation,\nas it iterates across all existing users to pick the appropriate next one.\n\n* Confined to: `feature == ldap`, `false == (Puppet[:ldapuser] == \"\")`\n* Supported features: `manages_passwords`, `manages_shell`\n\n#### openbsd {#user-provider-openbsd}\n\nUser management via `useradd` and its ilk for OpenBSD. Note that you\nwill need to install Ruby's shadow password library (package known as\n`ruby-shadow`) if you wish to manage user passwords.\n\n* Required binaries: `passwd`, `useradd`, `userdel`, `usermod`\n* Confined to: `os.name == openbsd`\n* Default for: `[\"os.name\", \"openbsd\"] == `\n* Supported features: `manages_expiry`, `manages_homedir`, `manages_shell`, `system_users`\n\n#### pw {#user-provider-pw}\n\nUser management via `pw` on FreeBSD and DragonFly BSD.\n\n* Required binaries: `pw`\n* Confined to: `os.name == [:freebsd, :dragonfly]`\n* Default for: `[\"os.name\", \"[:freebsd, :dragonfly]\"] == `\n* Supported features: `allows_duplicates`, `manages_expiry`, `manages_homedir`, `manages_passwords`, `manages_shell`\n\n#### user_role_add {#user-provider-user_role_add}\n\nUser and role management on Solaris, via `useradd` and `roleadd`.\n\n* Required binaries: `passwd`, `roleadd`, `roledel`, `rolemod`, `useradd`, `userdel`, `usermod`\n* Default for: `[\"os.family\", \"solaris\"] == `\n* Supported features: `allows_duplicates`, `manages_homedir`, `manages_password_age`, `manages_passwords`, `manages_roles`, `manages_shell`, `manages_solaris_rbac`\n\n#### useradd {#user-provider-useradd}\n\nUser management via `useradd` and its ilk.  Note that you will need to\ninstall Ruby's shadow password library (often known as `ruby-libshadow`)\nif you wish to manage user passwords.\n\nTo use the `forcelocal` parameter, you need to install the `libuser` package (providing\n`/usr/sbin/lgroupadd` and `/usr/sbin/luseradd`).\n\n* Required binaries: `chage`, `chpasswd`, `useradd`, `userdel`, `usermod`\n* Supported features: `allows_duplicates`, `manages_expiry`, `manages_homedir`, `manages_shell`\n\n#### windows_adsi {#user-provider-windows_adsi}\n\nLocal user management for Windows.\n\n* Confined to: `os.name == windows`\n* Default for: `[\"os.name\", \"windows\"] == `\n* Supported features: `manages_homedir`, `manages_passwords`, `manages_roles`\n\n### Provider Features {#user-provider-features}\n\nAvailable features:\n\n* `allows_duplicates` --- The provider supports duplicate users with the same UID.\n* `manages_aix_lam` --- The provider can manage AIX Loadable Authentication Module (LAM) system.\n* `manages_expiry` --- The provider can manage the expiry date for a user.\n* `manages_homedir` --- The provider can create and remove home directories.\n* `manages_local_users_and_groups` --- Allows local users to be managed on systems that also use some other remote Name Service Switch (NSS) method of managing accounts.\n* `manages_loginclass` --- The provider can manage the login class for a user.\n* `manages_password_age` --- The provider can set age requirements and restrictions for passwords.\n* `manages_password_salt` --- The provider can set a password salt. This is for providers that implement PBKDF2 passwords with salt properties.\n* `manages_passwords` --- The provider can modify user passwords, by accepting a password hash.\n* `manages_roles` --- The provider can manage roles\n* `manages_shell` --- The provider allows for setting shell and validates if possible\n* `manages_solaris_rbac` --- The provider can manage normal users\n* `system_users` --- The provider allows you to create system users with lower UIDs.\n\nProvider support:\n\n* **aix** - _manages aix lam, manages homedir, manages passwords, manages shell, manages expiry, manages password age, manages local users and groups_\n* **directoryservice** - _manages passwords, manages password salt, manages shell_\n* **hpuxuseradd** - _manages homedir, allows duplicates, manages passwords_\n* **ldap** - _manages passwords, manages shell_\n* **openbsd** - _manages homedir, manages expiry, system users, manages shell_\n* **pw** - _manages homedir, allows duplicates, manages passwords, manages expiry, manages shell_\n* **user_role_add** - _manages homedir, allows duplicates, manages solaris rbac, manages roles, manages passwords, manages password age, manages shell_\n* **useradd** - _manages homedir, allows duplicates, manages expiry, manages shell_\n* **windows_adsi** - _manages homedir, manages passwords, manages roles_\n  \n\n\n\n\n"
  },
  {
    "path": "spec/fixtures/faulty_face/puppet/face/syntax.rb",
    "content": "Puppet::Face.define(:syntax, '1.0.0') do\n  action :foo do\n    when_invoked do |whom|\n      \"hello, #{whom}\"\n    end\n  # This 'end' is deliberately omitted, to induce a syntax error.\n  # Please don't fix that, as it is used for testing. --daniel 2011-05-02\nend\n"
  },
  {
    "path": "spec/fixtures/hiera.yaml",
    "content": "---\n:hierarchy:\n  - %{test_suite}\n  - spec_hiera\n:backends:\n  - yaml\n  - puppet\n:yaml:\n  :datadir: './spec/fixtures'\n"
  },
  {
    "path": "spec/fixtures/integration/application/agent/cached_deferred_catalog.json",
    "content": "{\n  \"tags\": [\n    \"settings\"\n  ],\n  \"name\": \"127.0.0.1\",\n  \"version\": 1607629733,\n  \"code_id\": null,\n  \"catalog_uuid\": \"afc8472a-306b-4b24-b060-e956dffb79b8\",\n  \"catalog_format\": 2,\n  \"environment\": \"production\",\n  \"resources\": [\n    {\n      \"type\": \"Stage\",\n      \"title\": \"main\",\n      \"tags\": [\n        \"stage\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"Settings\",\n      \"tags\": [\n        \"class\",\n        \"settings\"\n      ],\n      \"exported\": false\n    },\n    {\n      \"type\": \"Class\",\n      \"title\": \"main\",\n      \"tags\": [\n        \"class\"\n      ],\n      \"exported\": false,\n      \"parameters\": {\n        \"name\": \"main\"\n      }\n    },\n    {\n      \"type\": \"Notify\",\n      \"title\": \"deferred\",\n      \"tags\": [\n        \"notify\",\n        \"deferred\",\n        \"class\"\n      ],\n      \"file\": \"\",\n      \"line\": 1,\n      \"kind\": \"compilable_type\",\n      \"exported\": false,\n      \"parameters\": {\n        \"message\": {\n          \"__ptype\": \"Deferred\",\n          \"name\": \"new\",\n          \"arguments\": [\n            {\n              \"__ptype\": \"Pcore::StringType\"\n            },\n            {\n              \"__ptype\": \"Deferred\",\n              \"name\": \"binary_file\",\n              \"arguments\": [\n                \"__SOURCE_PATH__\"\n              ]\n            }\n          ]\n        }\n      }\n    }\n  ],\n  \"edges\": [\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[Settings]\"\n    },\n    {\n      \"source\": \"Stage[main]\",\n      \"target\": \"Class[main]\"\n    },\n    {\n      \"source\": \"Class[main]\",\n      \"target\": \"Notify[deferred]\"\n    }\n  ],\n  \"classes\": [\n    \"settings\"\n  ]\n}\n"
  },
  {
    "path": "spec/fixtures/integration/application/agent/lib/facter/agent_spec_role.rb",
    "content": "Facter.add(:agent_spec_role) do\n  setcode { 'web' }\nend\n"
  },
  {
    "path": "spec/fixtures/integration/application/apply/environments/spec/.resource_types/applytest.pp",
    "content": "# This file was automatically generated on 2020-07-09 16:08:52 -0700.\n# Use the 'puppet generate types' command to regenerate this file.\n\nPuppet::Resource::ResourceType3.new(\n  'applytest',\n  [\n    Puppet::Resource::Param(Any, 'message')\n  ],\n  [\n    # An arbitrary tag for your own reference; the name of the message.\n    Puppet::Resource::Param(Any, 'name', true),\n\n    # The specific backend to use for this `applytest`\n    # resource. You will seldom need to specify this --- Puppet will usually\n    # discover the appropriate provider for your platform.Available providers are:\n    # \n    # ruby\n    # :\n    Puppet::Resource::Param(Any, 'provider')\n  ],\n  {\n    /(?m-ix:(.*))/ => ['name']\n  },\n  true,\n  false)\n"
  },
  {
    "path": "spec/fixtures/integration/application/apply/environments/spec/modules/amod/lib/puppet/provider/applytest/applytest.rb",
    "content": "Puppet::Type.type(:applytest).provide(:applytest) do\nend\n"
  },
  {
    "path": "spec/fixtures/integration/application/apply/environments/spec/modules/amod/lib/puppet/type/applytest.rb",
    "content": "# If you make changes to this file, regenerate the pcore resource type using\n# bundle exec puppet generate types --environmentpath spec/fixtures/integration/application/apply/environments -E spec\nPuppet::Type.newtype(:applytest) do\n  newproperty(:message) do\n    def sync\n      Puppet.send(@resource[:loglevel], self.should)\n    end\n\n    def retrieve\n      :absent\n    end\n\n    def insync?(is)\n      false\n    end\n\n    defaultto { @resource[:name] }\n  end\n\n  newparam(:name) do\n    desc \"An arbitrary tag for your own reference; the name of the message.\"\n    Puppet.notice('the Puppet::Type says hello')\n    isnamevar\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/integration/application/module/environments/direnv/modules/nginx/README",
    "content": "nginx\n\nThis is the nginx module.\n"
  },
  {
    "path": "spec/fixtures/integration/application/module/environments/direnv/modules/nginx/manifests/init.pp",
    "content": "# Class: nginx\n#\n# This module manages nginx\n#\n# Parameters:\n#\n# Actions:\n#\n# Requires:\n#\n# Sample Usage:\n#\n# [Remember: No empty lines between comments and class definition]\nclass nginx {\n\n\n}\n"
  },
  {
    "path": "spec/fixtures/integration/application/module/environments/direnv/modules/nginx/metadata.json",
    "content": "{\n  \"name\": \"pmtacceptance/nginx\",\n  \"version\": \"0.0.1\",\n  \"source\": \"UNKNOWN\",\n  \"author\": \"pmtacceptance\",\n  \"license\": \"UNKNOWN\",\n  \"summary\": \"UNKNOWN\",\n  \"description\": \"UNKNOWN\",\n  \"project_page\": \"UNKNOWN\",\n  \"dependencies\": [\n\n  ],\n  \"types\": [\n\n  ],\n  \"checksums\": {\n    \"README\": \"5c4fbf4812d8a4d4b0fe1c13ad640b0e\",\n    \"manifests/init.pp\": \"3a8ab171fd609a527ae0b293ced9a014\"\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/Gemfile",
    "content": "source ENV['GEM_SOURCE'] || \"https://rubygems.org\"\n\ngem 'gettext-setup', '~> 0.28', require: false, platforms: [:ruby]\ngem \"rake\"\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/Rakefile",
    "content": "spec = Gem::Specification.find_by_name 'gettext-setup'\nload \"#{spec.gem_dir}/lib/tasks/gettext.rake\"\nGettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__)))\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/lib/puppet/functions/l10n.rb",
    "content": "Puppet::Functions.create_function(:l10n) do\n  dispatch :l10n_impl do\n  end\n\n  def l10n_impl\n    _(\"IT'S HAPPY FUN TIME\")\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/locales/config.yaml",
    "content": "---\n# This is the project-specific configuration file for setting up\n# fast_gettext for your project.\ngettext:\n  # This is used for the name of the .pot and .po files; they will be\n  # called <project_name>.pot?\n  project_name: 'puppet-l10n'\n  # This is used in comments in the .pot and .po files to indicate what\n  # project the files belong to and should bea little more descriptive than\n  # <project_name>\n  package_name: puppet l10n demo\n  # The locale that the default messages in the .pot file are in\n  default_locale: en\n  # The email used for sending bug reports.\n  bugs_address: docs@puppetlabs.com\n  # The holder of the copyright.\n  copyright_holder: Puppet Labs, LLC.\n  # This determines which comments in code should be eligible for translation.\n  # Any comments that start with this string will be externalized. (Leave\n  # empty to include all.)\n  comments_tag: TRANSLATORS\n  # Patterns for +Dir.glob+ used to find all files that might contain\n  # translatable content, relative to the project root directory\n  source_files:\n    - 'lib/**/*.rb'\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/locales/ja/puppet-l10n.po",
    "content": "# Puppet, 2021\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: 1.0\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2018-03-29 12:41+0000\\n\"\n\"PO-Revision-Date: 2018-03-29 12:43+0000\\n\"\n\"Last-Translator: Puppet\\n\"\n\"Language-Team: English\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: en\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\n#: ../lib/puppet/functions/l10n.rb:5\nmsgid \"IT'S HAPPY FUN TIME\"\nmsgstr \"それは楽しい時間です\"\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/locales/puppet-l10n.pot",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2021 Puppet Labs, LLC.\n# This file is distributed under the same license as the puppet l10n demo package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: puppet l10n demo 6.23.0-100-gdc4e95bd86\\n\"\n\"\\n\"\n\"Report-Msgid-Bugs-To: docs@puppetlabs.com\\n\"\n\"POT-Creation-Date: 2021-07-16 16:48-0700\\n\"\n\"PO-Revision-Date: 2021-07-16 16:48-0700\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n"
  },
  {
    "path": "spec/fixtures/integration/l10n/envs/prod/modules/demo/metadata.json",
    "content": "{\n    \"name\": \"puppet-l10n\",\n    \"version\": \"0.0.1\",\n    \"author\": \"puppet\",\n    \"source\": \"\",\n    \"license\": \"Apache-2.0\",\n    \"dependencies\": []\n}\n"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir/00_a.pp",
    "content": "class a {}\n$a = 10"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir/01_b.pp",
    "content": "class b {}\n\n# if the files are evaluated in the wrong order, the file 'b' has a reference\n# to $a (set in file 'a') and with strict variable lookup should raise an error\n# and fail this test.\n$b = $a # error if $a not set in strict mode\n"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir/03_empty.pp",
    "content": ""
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir/04_include.pp",
    "content": "include a, b\nnotify { \"variables\": message => \"a: $a, b: $b\" }\n"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir2/00_a.pp",
    "content": "class a {}\n$a = 10"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir2/02_folder/01_b.pp",
    "content": "class b {}\n\n# if the files are evaluated in the wrong order, the file 'b' has a reference\n# to $a (set in file 'a') and with strict variable lookup should raise an error\n# and fail this test.\n$b = $a # error if $a not set in strict mode\n"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir2/03_c.pp",
    "content": "$c = $a + $b"
  },
  {
    "path": "spec/fixtures/integration/node/environment/sitedir2/04_include.pp",
    "content": "include a, b\nnotify { \"variables\": message => \"a: $a, b: $b c: $c\" }\n"
  },
  {
    "path": "spec/fixtures/manifests/site.pp",
    "content": ""
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/Modulefile",
    "content": "name 'jamtur01-apache'\nversion '0.0.1'\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/files/httpd",
    "content": "# Configuration file for the httpd service.\n\n#\n# The default processing model (MPM) is the process-based\n# 'prefork' model.  A thread-based model, 'worker', is also\n# available, but does not work with some modules (such as PHP).\n# The service must be stopped before changing this variable.\n#\n#HTTPD=/usr/sbin/httpd.worker\n\n#\n# To pass additional options (for instance, -D definitions) to the\n# httpd binary at startup, set OPTIONS here.\n#\n#OPTIONS=\n#OPTIONS=-DDOWN\n\n#\n# By default, the httpd process is started in the C locale; to\n# change the locale in which the server runs, the HTTPD_LANG\n# variable can be set.\n#\n#HTTPD_LANG=C\nexport SHORTHOST=`hostname -s`\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/files/test.vhost",
    "content": "#\n# Test vhost\n#\nNameVirtualHost *:80\n<VirtualHost *:80>\n  ServerName testvhost\n  DocumentRoot /tmp/testvhost\n  <Directory /tmp/testvhost>\n    Options Indexes FollowSymLinks MultiViews\n    AllowOverride None\n    Order allow,deny\n    allow from all\n  </Directory>\n  ErrorLog /var/log/apache2/error.log\n  LogLevel warn\n  CustomLog /var/log/apache2/access.log combined\n  ServerSignature On\n</VirtualHost>\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb",
    "content": "Puppet::Type.type(:a2mod).provide(:debian) do\n    desc \"Manage Apache 2 modules on Debian-like OSes (e.g. Ubuntu)\"\n\n    commands :encmd => \"a2enmod\"\n    commands :discmd => \"a2dismod\"\n\n    defaultfor 'os.name' => [:debian, :ubuntu]\n\n    def create\n        encmd resource[:name]\n    end\n\n    def destroy\n        discmd resource[:name]\n    end\n\n    def exists?\n        mod= \"/etc/apache2/mods-enabled/\" + resource[:name] + \".load\"\n        Puppet::FileSystem.exist?(mod)\n    end\nend\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/lib/puppet/type/a2mod.rb",
    "content": "Puppet::Type.newtype(:a2mod) do\n    @doc = \"Manage Apache 2 modules\"\n\n    ensurable\n\n    newparam(:name) do\n       desc \"The name of the module to be managed\"\n\n       isnamevar\n\n    end\nend\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/manifests/dev.pp",
    "content": "class apache::dev {\n  include apache::params\n\n  package{$apache::params::apache_dev: ensure => installed}\n}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/manifests/init.pp",
    "content": "# ensure apache is installed\nclass apache {\n  include apache::params\n  package{'httpd':\n    name   => $apache::params::apache_name,\n    ensure => present,\n  }\n  service { 'httpd':\n    name      => $apache::params::apache_name,\n    ensure    => running,\n    enable    => true,\n    subscribe => Package['httpd'],\n  }\n  #\n  # May want to purge all none realize modules using the resources resource type.\n  # A2mod resource type is broken.  Look into fixing it and moving it into apache.\n  #\n  A2mod { require => Package['httpd'], notify => Service['httpd']}\n  @a2mod {\n   'rewrite' : ensure => present;\n   'headers' : ensure => present;\n   'expires' : ensure => present;\n  }\n  $vdir = $os['name'] ? {\n    'ubuntu' => '/etc/apache2/sites-enabled/',\n    default => '/etc/httpd/conf.d',\n  }\n  file { $vdir:\n    ensure => directory,\n    recurse => true,\n    purge => true,\n    notify => Service['httpd'],\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/manifests/params.pp",
    "content": "class apache::params{\n  $user  = 'www-data'\n  $group = 'www-data'\n\n  case $os['name'] {\n    \"centos\": {\n       $apache_name = httpd\n       $ssl_package = mod_ssl\n       $apache_dev  = httpd-devel\n    }\n    \"ubuntu\": {\n       $apache_name = apache2\n       $ssl_package = apache-ssl\n       $apache_dev  = [ libaprutil1-dev, libapr1-dev, apache2-prefork-dev ]\n    }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/manifests/php.pp",
    "content": "class apache::php{\n  package{'libapache2-mod-php5':\n    ensure => present,\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/manifests/ssl.pp",
    "content": "class apache::ssl {\n  include apache\n\n\n  case $facts['os']['name'] {\n     \"centos\": {\n        package { $apache::params::ssl_package:\n           require => Package['httpd'],\n        }\n     }\n     \"ubuntu\": {\n        a2mod { \"ssl\": ensure => present, }\n     }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/manifests/vhost.pp",
    "content": "define apache::vhost( $port, $docroot, $ssl=true, $template='apache/vhost-default.conf.erb', $priority, $serveraliases = '' ) {\n  include apache\n  $vdir = $os['name']? {\n    'ubuntu' => '/etc/apache2/sites-enabled/',\n    default => '/etc/httpd/conf.d',\n  }\n  file{\"${vdir}/${priority}-${name}\":\n    content => template($template),\n    owner => 'root',\n    group => 'root',\n    mode => '0777',\n    require => Package['httpd'],\n    notify => Service['httpd'],\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/metadata.json",
    "content": "{\"dependencies\":[],\"types\":[{\"providers\":[{\"name\":\"a2mod\",\"doc\":\"Manage Apache 2 modules on Debian and Ubuntu  Required binaries: ``a2enmod``, ``a2dismod``.    Default for ``operatingsystem`` == ``debianubuntu``.  \"}],\"parameters\":[{\"name\":\"name\",\"doc\":\"The name of the module to be managed\"}],\"properties\":[{\"name\":\"ensure\",\"doc\":\"The basic property that the resource should be in.  Valid values are ``present``, ``absent``.\"}],\"name\":\"a2mod\",\"doc\":\"Manage Apache 2 modules on Debian and Ubuntu\"}],\"checksums\":{\"manifests/params.pp\":\"71734796921dbdbfd58f503622527616\",\"tests/ssl.pp\":\"191912535199531fd631f911c6329e56\",\"tests/vhost.pp\":\"1b91e03c8ef89a7ecb6793831ac18399\",\"manifests/php.pp\":\"b78cc593f1c4cd800c906e0891c9b11f\",\"files/httpd\":\"295f5e924afe6f752d29327e73fe6d0a\",\"tests/php.pp\":\"ce7bb9eef69d32b42a32ce32d9653625\",\"lib/puppet/provider/a2mod/a2mod.rb\":\"18c5bb180b75a2375e95e07f88a94257\",\"files/test.vhost\":\"0602022c19a7b6b289f218c7b93c1aea\",\"manifests/ssl.pp\":\"b4334a161a2ba5fa8a62cf7b38f352c8\",\"manifests/dev.pp\":\"510813942246cc9a7786d8f2d8874a35\",\"manifests/vhost.pp\":\"cbc4657b0cce5cd432057393d5f6b0c2\",\"tests/init.pp\":\"4eac4a7ef68499854c54a78879e25535\",\"lib/puppet/type/a2mod.rb\":\"0e1b4843431413a10320ac1f6a055d15\",\"tests/apache.pp\":\"4eac4a7ef68499854c54a78879e25535\",\"tests/dev.pp\":\"4cf15c1fecea3ca86009f182b402c7ab\",\"templates/vhost-default.conf.erb\":\"9055aed946e1111c30ab81fedac2c8b0\",\"manifests/init.pp\":\"dc503e26e8021351078813b541c4bd3d\",\"Modulefile\":\"d43334b4072cd1744121b3b25cd9ed15\"},\"version\":\"0.0.1\",\"name\":\"jamtur01-apache\"}\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/templates/vhost-default.conf.erb",
    "content": "NameVirtualHost *:<%= port %>\n<VirtualHost *:<%= port %>>\n  ServerName <%= name %>\n<%if serveraliases.is_a? Array -%>\n<% serveraliases.each do |name| -%><%= \"  ServerAlias #{name}\\n\" %><% end -%>\n<% elsif serveraliases != '' -%>\n<%= \"  ServerAlias #{serveraliases}\" -%>\n<% end -%>\n  DocumentRoot <%= docroot %>\n  <Directory <%= docroot %>>\n    Options Indexes FollowSymLinks MultiViews\n    AllowOverride None\n    Order allow,deny\n    allow from all\n  </Directory>\n  ErrorLog /var/log/apache2/<%= name %>_error.log\n  LogLevel warn\n  CustomLog /var/log/apache2/<%= name %>_access.log combined\n  ServerSignature On\n</VirtualHost>\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/tests/apache.pp",
    "content": "include apache\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/tests/dev.pp",
    "content": "include apache::dev\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/tests/init.pp",
    "content": "include apache\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/tests/php.pp",
    "content": "include apache::php\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/tests/ssl.pp",
    "content": "include apache::ssl\n"
  },
  {
    "path": "spec/fixtures/releases/jamtur01-apache/tests/vhost.pp",
    "content": "include apache\napache::vhost { 'test.vhost': source => 'puppet:///modules/apache/test.vhost' }\n"
  },
  {
    "path": "spec/fixtures/ssl/127.0.0.1-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:b9:04:03:80:8d:8f:96:d7:90:83:c1:09:31:1e:\n    2b:03:c7:e3:98:28:27:ff:63:e9:03:6e:94:73:9e:\n    de:47:d4:c0:66:33:e6:e7:29:91:d4:4b:63:d3:8b:\n    ed:18:04:05:84:be:bc:97:6f:56:64:b7:03:f2:cd:\n    ff:fc:f5:1f:1b:72:82:62:fb:db:cd:84:99:a2:28:\n    a5:d7:fb:0a:e4:5c:22:7c:85:f3:2e:d7:e9:6f:4e:\n    4d:29:35:a2:bd:ae:15:7c:87:96:d5:51:23:57:8e:\n    cf:ba:51:e5:56:cb:3e:b5:cf:20:51:66:84:88:ec:\n    c8:96:2a:07:bf:db:be:cc:51:d4:21:6e:66:25:2f:\n    29:5a:a2:84:83:23:b1:2b:ed:93:b0:68:f3:e6:61:\n    0d:2b:e9:7b:58:f7:9a:b8:8a:d8:83:d7:7c:35:48:\n    e9:f0:cf:c4:c8:7d:36:30:2b:ab:e7:58:c9:f4:ea:\n    e5:35:5c:23:55:bd:84:a6:7b:ce:1d:e0:9a:0e:d8:\n    d3:41:52:24:10:a8:76:f7:58:3b:98:64:8b:a7:60:\n    ca:32:41:40:a4:28:bd:55:09:01:a7:32:3f:48:d7:\n    e1:39:14:32:45:1d:00:b4:a0:8b:0e:b2:0e:85:7f:\n    ae:8c:66:e6:8b:6f:6f:ee:c5:c5:ec:2c:65:62:e8:\n    83:0f\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    00:a1:84:34:7e:7c:96:b6:eb:b7:38:9e:43:a5:02:\n    9e:30:ad:c7:2d:e5:18:8d:e7:2d:db:96:24:b9:0f:\n    1f:23:fc:42:7e:b6:9e:c4:7d:50:d6:d1:7d:f3:87:\n    3b:53:74:e7:50:6c:a6:fd:58:f0:45:fa:53:d3:1b:\n    2d:78:2a:91:9e:87:87:f6:5b:c3:16:96:c4:fc:80:\n    99:cf:84:54:8c:e9:36:1e:19:5e:24:2d:cf:97:20:\n    f7:51:9b:86:58:2a:ea:8d:0d:5d:8b:1a:4b:4d:3b:\n    da:72:e0:dd:e2:b8:8a:25:74:0d:d5:a2:36:df:c6:\n    3e:92:81:5c:c4:8c:54:f3:ce:98:f7:aa:1f:33:b7:\n    b3:98:0d:31:cd:14:af:35:e2:03:78:fd:73:37:06:\n    9d:70:c5:16:4a:7c:5e:68:6c:72:d7:ed:06:ee:0e:\n    0e:dc:8e:27:67:d5:05:bb:41:06:6b:15:3a:81:6c:\n    15:de:0d:e8:44:33:f6:81:e3:cd:e5:bd:26:08:98:\n    a0:f7:c4:2e:31:a4:11:f0:33:7d:a3:bc:d7:ec:0d:\n    5c:41:de:12:ca:43:56:eb:61:db:af:42:93:5e:95:\n    18:3a:ab:77:71:6f:31:eb:6f:e8:65:6d:b2:43:7c:\n    06:6a:5a:fc:f0:bf:22:e2:30:66:a8:55:68:b0:df:\n    a2:a9\nprime1:\n    00:f6:a5:33:76:e8:15:c6:98:f6:46:93:b7:1a:8b:\n    83:13:a4:7e:60:ea:cd:29:1e:4f:95:4f:f4:86:2c:\n    d0:e0:83:80:32:80:bf:ce:98:93:f3:d1:ff:d4:7f:\n    90:d3:3e:92:bc:db:30:c4:f5:56:78:e8:6e:6e:3d:\n    12:bf:e8:43:16:2d:c8:66:3c:00:61:ae:96:9a:c8:\n    4d:3a:e0:cb:7e:53:fa:66:bf:97:65:d7:85:a0:e6:\n    55:c5:d4:81:d3:68:08:ba:ae:30:ac:46:54:04:01:\n    f6:d9:51:28:cf:e9:32:33:bd:55:53:6a:73:6d:aa:\n    12:c9:01:58:89:76:24:47:a3\nprime2:\n    00:c0:08:6b:ad:09:ff:09:d9:63:7e:fd:27:7d:e2:\n    70:db:5b:99:43:32:16:3f:c1:a6:05:0d:ed:33:4c:\n    ce:7a:8c:42:67:f1:8d:f2:d7:d8:b7:0d:10:f3:eb:\n    85:94:98:d8:91:8b:02:8e:d9:af:ca:1f:b8:4e:90:\n    21:a1:40:b3:f2:66:90:07:00:0f:a8:57:9d:bb:09:\n    0f:ba:85:24:22:7b:51:fb:bb:76:56:34:6c:3a:fc:\n    50:97:c6:0b:03:91:57:00:7e:0c:5c:9b:43:c5:3e:\n    14:91:93:7b:f9:7c:d0:72:f2:08:fd:c5:5e:57:a0:\n    88:5e:74:2c:1a:ef:01:bd:a5\nexponent1:\n    76:74:62:e2:21:96:8b:b9:dc:d5:8a:8d:ee:e6:bf:\n    fe:08:0b:56:1a:8e:8b:c7:ed:ea:c4:ea:a8:22:0f:\n    f3:33:d4:b6:ec:94:b1:f1:1f:65:83:1e:bd:fc:c2:\n    1e:62:37:f1:11:c8:3b:5f:a4:b7:0e:d2:32:89:8c:\n    5e:b4:7a:bb:c5:23:30:ce:72:54:77:98:07:20:59:\n    cf:04:35:57:27:97:e4:0e:f1:f4:4c:6c:f6:18:89:\n    6b:28:a3:6d:57:d2:91:6d:a5:1c:a7:ee:23:ba:99:\n    c6:47:2f:35:a3:46:a4:08:b3:59:0b:90:02:44:23:\n    1f:7c:50:fc:3b:cc:32:c7\nexponent2:\n    00:85:4c:53:12:06:82:56:9f:e4:04:de:4d:6f:80:\n    a3:be:60:d2:fe:65:e2:33:d7:84:1a:b6:14:15:2c:\n    17:97:d1:8c:b7:02:61:fa:54:02:46:ee:76:fa:1a:\n    5e:db:4b:4d:e9:99:88:e9:08:0a:92:4f:7a:6c:6e:\n    78:29:aa:f4:3e:2b:1b:87:00:6f:dd:f7:13:b2:25:\n    14:19:f1:19:a8:25:da:3e:d7:5d:c9:71:12:3f:cf:\n    ad:51:ed:52:ef:e3:0f:75:74:09:b8:ae:be:58:48:\n    43:96:d9:bd:90:ed:26:f4:e3:35:82:92:62:6e:89:\n    2c:a0:04:1c:29:86:06:bb:51\ncoefficient:\n    00:db:05:92:d8:a8:8d:77:ff:94:17:39:5b:3c:da:\n    b2:44:e6:11:98:c0:a0:fc:02:3a:92:01:e5:db:40:\n    98:51:87:09:4e:40:d1:8d:f0:3c:b9:83:0b:d6:c6:\n    20:f7:08:0c:c3:95:aa:cf:8f:8c:e4:21:cd:63:9d:\n    b9:81:88:25:00:fe:36:0b:f2:30:3b:de:09:e6:ff:\n    28:66:46:20:18:65:14:c0:f2:b7:07:f5:f5:92:be:\n    4e:f1:2c:4d:40:0f:a4:7f:61:25:c4:e1:ed:29:68:\n    38:ab:33:d2:2f:88:1a:22:f3:42:a8:99:65:cd:bb:\n    d0:26:58:c3:50:39:9e:bb:78\n-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAuQQDgI2PlteQg8EJMR4rA8fjmCgn/2PpA26Uc57eR9TAZjPm\n5ymR1Etj04vtGAQFhL68l29WZLcD8s3//PUfG3KCYvvbzYSZoiil1/sK5FwifIXz\nLtfpb05NKTWiva4VfIeW1VEjV47PulHlVss+tc8gUWaEiOzIlioHv9u+zFHUIW5m\nJS8pWqKEgyOxK+2TsGjz5mENK+l7WPeauIrYg9d8NUjp8M/EyH02MCur51jJ9Orl\nNVwjVb2EpnvOHeCaDtjTQVIkEKh291g7mGSLp2DKMkFApCi9VQkBpzI/SNfhORQy\nRR0AtKCLDrIOhX+ujGbmi29v7sXF7CxlYuiDDwIDAQABAoIBAQChhDR+fJa267c4\nnkOlAp4wrcct5RiN5y3bliS5Dx8j/EJ+tp7EfVDW0X3zhztTdOdQbKb9WPBF+lPT\nGy14KpGeh4f2W8MWlsT8gJnPhFSM6TYeGV4kLc+XIPdRm4ZYKuqNDV2LGktNO9py\n4N3iuIoldA3Vojbfxj6SgVzEjFTzzpj3qh8zt7OYDTHNFK814gN4/XM3Bp1wxRZK\nfF5obHLX7QbuDg7cjidn1QW7QQZrFTqBbBXeDehEM/aB483lvSYImKD3xC4xpBHw\nM32jvNfsDVxB3hLKQ1brYduvQpNelRg6q3dxbzHrb+hlbbJDfAZqWvzwvyLiMGao\nVWiw36KpAoGBAPalM3boFcaY9kaTtxqLgxOkfmDqzSkeT5VP9IYs0OCDgDKAv86Y\nk/PR/9R/kNM+krzbMMT1Vnjobm49Er/oQxYtyGY8AGGulprITTrgy35T+ma/l2XX\nhaDmVcXUgdNoCLquMKxGVAQB9tlRKM/pMjO9VVNqc22qEskBWIl2JEejAoGBAMAI\na60J/wnZY379J33icNtbmUMyFj/BpgUN7TNMznqMQmfxjfLX2LcNEPPrhZSY2JGL\nAo7Zr8ofuE6QIaFAs/JmkAcAD6hXnbsJD7qFJCJ7Ufu7dlY0bDr8UJfGCwORVwB+\nDFybQ8U+FJGTe/l80HLyCP3FXlegiF50LBrvAb2lAoGAdnRi4iGWi7nc1YqN7ua/\n/ggLVhqOi8ft6sTqqCIP8zPUtuyUsfEfZYMevfzCHmI38RHIO1+ktw7SMomMXrR6\nu8UjMM5yVHeYByBZzwQ1VyeX5A7x9Exs9hiJayijbVfSkW2lHKfuI7qZxkcvNaNG\npAizWQuQAkQjH3xQ/DvMMscCgYEAhUxTEgaCVp/kBN5Nb4CjvmDS/mXiM9eEGrYU\nFSwXl9GMtwJh+lQCRu52+hpe20tN6ZmI6QgKkk96bG54Kar0PisbhwBv3fcTsiUU\nGfEZqCXaPtddyXESP8+tUe1S7+MPdXQJuK6+WEhDltm9kO0m9OM1gpJiboksoAQc\nKYYGu1ECgYEA2wWS2KiNd/+UFzlbPNqyROYRmMCg/AI6kgHl20CYUYcJTkDRjfA8\nuYML1sYg9wgMw5Wqz4+M5CHNY525gYglAP42C/IwO94J5v8oZkYgGGUUwPK3B/X1\nkr5O8SxNQA+kf2ElxOHtKWg4qzPSL4gaIvNCqJllzbvQJljDUDmeu3g=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/127.0.0.1.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 6 (0x6)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=127.0.0.1\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:b9:04:03:80:8d:8f:96:d7:90:83:c1:09:31:1e:\n                    2b:03:c7:e3:98:28:27:ff:63:e9:03:6e:94:73:9e:\n                    de:47:d4:c0:66:33:e6:e7:29:91:d4:4b:63:d3:8b:\n                    ed:18:04:05:84:be:bc:97:6f:56:64:b7:03:f2:cd:\n                    ff:fc:f5:1f:1b:72:82:62:fb:db:cd:84:99:a2:28:\n                    a5:d7:fb:0a:e4:5c:22:7c:85:f3:2e:d7:e9:6f:4e:\n                    4d:29:35:a2:bd:ae:15:7c:87:96:d5:51:23:57:8e:\n                    cf:ba:51:e5:56:cb:3e:b5:cf:20:51:66:84:88:ec:\n                    c8:96:2a:07:bf:db:be:cc:51:d4:21:6e:66:25:2f:\n                    29:5a:a2:84:83:23:b1:2b:ed:93:b0:68:f3:e6:61:\n                    0d:2b:e9:7b:58:f7:9a:b8:8a:d8:83:d7:7c:35:48:\n                    e9:f0:cf:c4:c8:7d:36:30:2b:ab:e7:58:c9:f4:ea:\n                    e5:35:5c:23:55:bd:84:a6:7b:ce:1d:e0:9a:0e:d8:\n                    d3:41:52:24:10:a8:76:f7:58:3b:98:64:8b:a7:60:\n                    ca:32:41:40:a4:28:bd:55:09:01:a7:32:3f:48:d7:\n                    e1:39:14:32:45:1d:00:b4:a0:8b:0e:b2:0e:85:7f:\n                    ae:8c:66:e6:8b:6f:6f:ee:c5:c5:ec:2c:65:62:e8:\n                    83:0f\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Subject Alternative Name: \n                DNS:127.0.0.1, DNS:127.0.0.2\n    Signature Algorithm: sha256WithRSAEncryption\n         0c:9f:98:df:3d:2c:3c:4a:09:fa:79:f1:02:b4:42:1b:c6:33:\n         78:68:b7:65:1a:f7:17:2d:ca:26:0e:cd:cd:ec:95:ee:35:6b:\n         44:9b:04:b8:60:e2:a5:1c:61:e6:2d:26:c4:fd:c4:43:48:d0:\n         99:8d:8d:bb:50:33:77:49:45:e3:88:2a:2d:9b:08:24:19:29:\n         12:bd:da:f4:bb:58:b3:cb:de:8a:32:28:7a:e9:44:27:a7:8d:\n         c3:ad:94:41:57:4c:18:6c:a6:39:c4:4c:d2:7f:80:71:b4:af:\n         2c:a7:fb:6d:61:ca:42:1d:22:b0:0b:0e:9b:cf:e2:38:35:74:\n         dc:24:82:c3:df:89:ad:8c:5c:1e:0e:fe:0a:70:2b:2d:8a:d1:\n         cf:3b:f4:e6:8c:c6:74:fc:ea:7e:a1:80:d2:55:4d:11:87:55:\n         1f:65:73:9b:9d:36:17:e1:42:b1:78:a2:e8:39:da:f6:8f:8d:\n         a0:f5:8d:b4:39:02:d6:d3:c5:5c:11:28:71:8e:c2:65:37:b2:\n         f3:c5:3c:df:79:23:e7:8c:ff:3e:8a:46:14:de:d3:d7:b5:f5:\n         cb:68:46:0a:8a:c9:e2:ae:d4:1b:8b:c3:19:a4:43:e7:84:c9:\n         41:a2:14:34:4f:7e:80:c6:dd:48:5a:54:69:69:b3:af:86:6b:\n         26:0c:20:83\n-----BEGIN CERTIFICATE-----\nMIICxDCCAaygAwIBAgIBBjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0\nIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowFDESMBAGA1UEAwwJ\nMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuQQDgI2P\nlteQg8EJMR4rA8fjmCgn/2PpA26Uc57eR9TAZjPm5ymR1Etj04vtGAQFhL68l29W\nZLcD8s3//PUfG3KCYvvbzYSZoiil1/sK5FwifIXzLtfpb05NKTWiva4VfIeW1VEj\nV47PulHlVss+tc8gUWaEiOzIlioHv9u+zFHUIW5mJS8pWqKEgyOxK+2TsGjz5mEN\nK+l7WPeauIrYg9d8NUjp8M/EyH02MCur51jJ9OrlNVwjVb2EpnvOHeCaDtjTQVIk\nEKh291g7mGSLp2DKMkFApCi9VQkBpzI/SNfhORQyRR0AtKCLDrIOhX+ujGbmi29v\n7sXF7CxlYuiDDwIDAQABoyMwITAfBgNVHREEGDAWggkxMjcuMC4wLjGCCTEyNy4w\nLjAuMjANBgkqhkiG9w0BAQsFAAOCAQEADJ+Y3z0sPEoJ+nnxArRCG8YzeGi3ZRr3\nFy3KJg7NzeyV7jVrRJsEuGDipRxh5i0mxP3EQ0jQmY2Nu1Azd0lF44gqLZsIJBkp\nEr3a9LtYs8veijIoeulEJ6eNw62UQVdMGGymOcRM0n+AcbSvLKf7bWHKQh0isAsO\nm8/iODV03CSCw9+JrYxcHg7+CnArLYrRzzv05ozGdPzqfqGA0lVNEYdVH2Vzm502\nF+FCsXii6Dna9o+NoPWNtDkC1tPFXBEocY7CZTey88U833kj54z/PopGFN7T17X1\ny2hGCorJ4q7UG4vDGaRD54TJQaIUNE9+gMbdSFpUaWmzr4ZrJgwggw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/bad-basic-constraints.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 12 (0xc)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=Test CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:bf:58:f9:d6:80:6e:15:94:3c:42:eb:4c:9a:74:\n                    a6:70:16:0e:31:aa:3b:94:06:d9:28:5e:21:5b:da:\n                    fc:b7:cc:3e:06:fb:f9:fe:4c:84:dd:d1:b0:a3:1a:\n                    68:20:99:2e:28:02:59:dc:03:12:5c:21:8b:2a:0b:\n                    92:8f:87:61:d0:2c:bd:4b:84:84:c2:e8:4b:cf:09:\n                    28:86:65:89:9d:f5:85:70:a0:58:29:aa:bd:f5:3d:\n                    b0:fe:98:5d:f2:50:f3:f1:1f:94:d5:f8:ff:25:92:\n                    14:a4:81:19:bc:1a:03:5b:b3:1b:ea:7b:9c:99:9a:\n                    f4:3f:2a:a5:96:b0:07:96:48:79:b0:52:d4:fc:f2:\n                    51:62:94:fa:b0:69:b8:86:11:ab:b8:02:4d:93:e5:\n                    2b:01:50:ef:fa:06:f6:91:4d:56:58:a3:11:f3:0f:\n                    16:87:de:42:0a:53:b0:07:73:07:6d:ed:cf:91:52:\n                    2b:b4:e8:93:7e:57:aa:39:a4:47:1f:f6:ff:7d:8f:\n                    73:16:b3:2e:cb:4c:5f:40:c0:0c:02:2c:c8:6c:1b:\n                    8c:ec:54:d9:e1:60:98:de:77:e7:ee:a9:95:80:83:\n                    39:de:44:c2:83:d6:46:a2:f7:ac:63:6e:f5:b8:30:\n                    c2:5c:63:8f:0a:47:3a:6f:be:ef:3e:21:0a:52:69:\n                    8f:35\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Subject Key Identifier: \n                58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n            Netscape Comment: \n                Puppet Server Internal Certificate\n            X509v3 Authority Key Identifier: \n                keyid:58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n\n    Signature Algorithm: sha256WithRSAEncryption\n         18:57:51:5d:eb:63:93:8d:25:5b:5d:8e:5c:67:b0:d2:c6:71:\n         63:82:73:2e:d0:14:32:28:f3:cc:27:13:bc:4c:55:66:6b:28:\n         78:b8:cb:5e:f8:95:29:9a:78:55:59:3e:e4:de:d1:ae:f0:68:\n         7e:bc:ff:32:d2:54:85:b2:eb:0c:4d:99:6a:28:a5:d8:a3:6e:\n         8a:71:86:70:fd:3e:36:2b:ad:fc:29:f9:de:c5:1f:d7:4c:66:\n         9d:4b:bc:ee:4d:95:60:a4:3a:ce:75:7c:5e:3b:42:f5:21:18:\n         42:43:f4:15:62:7a:c9:7f:3b:a1:dc:38:e5:f2:12:db:62:b6:\n         22:91:b6:06:90:e8:26:2a:c6:3e:2e:80:9d:c2:fe:1d:67:b6:\n         72:2b:37:aa:4a:1d:19:bb:74:80:0b:c8:bc:c1:d3:3c:51:34:\n         1c:30:0b:65:86:e9:96:38:01:b2:d7:ad:8f:37:eb:54:1a:83:\n         2a:ee:fb:e8:e3:fb:eb:c2:df:27:df:49:34:64:bb:8a:d9:f3:\n         35:58:f7:b6:81:d8:df:21:30:bd:6d:af:6e:b2:89:4a:1a:3f:\n         56:3c:e4:24:94:4b:f3:1f:2b:e6:06:78:f1:34:68:cd:c5:c1:\n         5f:23:da:c6:c9:95:82:a5:eb:3f:d2:d3:58:03:4b:df:63:13:\n         00:be:86:00\n-----BEGIN CERTIFICATE-----\nMIIDNDCCAhygAwIBAgIBDDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0\nIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowEjEQMA4GA1UEAwwH\nVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9Y+daAbhWU\nPELrTJp0pnAWDjGqO5QG2SheIVva/LfMPgb7+f5MhN3RsKMaaCCZLigCWdwDElwh\niyoLko+HYdAsvUuEhMLoS88JKIZliZ31hXCgWCmqvfU9sP6YXfJQ8/EflNX4/yWS\nFKSBGbwaA1uzG+p7nJma9D8qpZawB5ZIebBS1PzyUWKU+rBpuIYRq7gCTZPlKwFQ\n7/oG9pFNVlijEfMPFofeQgpTsAdzB23tz5FSK7Tok35XqjmkRx/2/32PcxazLstM\nX0DADAIsyGwbjOxU2eFgmN535+6plYCDOd5EwoPWRqL3rGNu9bgwwlxjjwpHOm++\n7z4hClJpjzUCAwEAAaOBlDCBkTAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIB\nBjAdBgNVHQ4EFgQUWNNuNTaXBJFqtt9FgJpTs+Q2yD0wMQYJYIZIAYb4QgENBCQW\nIlB1cHBldCBTZXJ2ZXIgSW50ZXJuYWwgQ2VydGlmaWNhdGUwHwYDVR0jBBgwFoAU\nWNNuNTaXBJFqtt9FgJpTs+Q2yD0wDQYJKoZIhvcNAQELBQADggEBABhXUV3rY5ON\nJVtdjlxnsNLGcWOCcy7QFDIo88wnE7xMVWZrKHi4y174lSmaeFVZPuTe0a7waH68\n/zLSVIWy6wxNmWoopdijbopxhnD9PjYrrfwp+d7FH9dMZp1LvO5NlWCkOs51fF47\nQvUhGEJD9BViesl/O6HcOOXyEttitiKRtgaQ6CYqxj4ugJ3C/h1ntnIrN6pKHRm7\ndIALyLzB0zxRNBwwC2WG6ZY4AbLXrY8361Qagyru++jj++vC3yffSTRku4rZ8zVY\n97aB2N8hML1tr26yiUoaP1Y85CSUS/MfK+YGePE0aM3FwV8j2sbJlYKl6z/S01gD\nS99jEwC+hgA=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/bad-int-basic-constraints.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 3 (0x3)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=Test CA Subauthority\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:a4:f1:74:f2:db:24:84:39:da:26:5c:72:c8:2e:\n                    a6:3e:57:88:90:ab:41:04:7a:7a:f3:af:f7:43:b7:\n                    9b:d8:57:91:7e:15:d5:aa:cb:8d:d4:a5:de:54:4a:\n                    22:bb:e3:6f:1a:03:7f:27:42:8a:db:45:ee:79:0e:\n                    dd:c0:08:fc:f3:48:9e:ea:99:f9:08:cd:38:86:2d:\n                    ca:2c:9b:d0:02:f2:81:29:72:a1:aa:a3:ea:fc:5f:\n                    4a:a1:15:68:55:a9:ed:7d:3a:23:84:0c:0a:d8:10:\n                    c7:0b:8c:b3:93:9a:61:55:10:73:34:14:21:de:28:\n                    65:bf:6a:d3:dd:e8:3d:9e:69:78:1e:72:89:fd:73:\n                    7e:75:e7:9b:4c:f2:65:3c:d5:ed:d4:d1:53:96:52:\n                    b0:29:c1:e4:9d:5f:d7:62:d0:84:7b:e8:db:85:93:\n                    b9:8f:4a:01:c4:6e:6d:e2:a9:6d:ec:51:d7:51:48:\n                    fe:f4:83:45:a1:c1:b7:00:68:9c:02:92:a6:20:d4:\n                    40:a0:c8:ee:ab:96:40:53:10:f1:b5:2d:a8:f9:50:\n                    fe:eb:96:9a:9c:c1:31:2c:1f:70:d5:a7:37:ff:35:\n                    1a:ac:d7:fc:9d:4f:e1:67:20:e6:9d:4a:66:c8:ac:\n                    e0:0a:f5:4f:d4:43:56:d6:e1:6d:04:f4:ea:02:d5:\n                    af:a9\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Subject Key Identifier: \n                EB:EA:7B:15:C9:4E:08:5B:87:27:09:E4:42:10:54:47:72:E9:38:29\n            Netscape Comment: \n                Puppet Server Internal Certificate\n            X509v3 Authority Key Identifier: \n                keyid:58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n\n    Signature Algorithm: sha256WithRSAEncryption\n         9d:ea:1a:42:b5:38:8e:65:0d:a5:83:21:8a:37:21:05:a8:30:\n         ea:e9:1b:51:31:21:72:44:8e:b0:2a:78:cc:1b:f0:95:01:c4:\n         6c:e1:74:93:cc:cc:2e:36:ba:11:31:a1:61:06:84:d4:03:d5:\n         3d:b3:6e:7d:42:da:71:92:01:fd:2c:22:21:ca:47:78:6c:c0:\n         ce:ac:bc:79:6d:fb:9c:28:77:6f:57:41:67:dc:0e:a6:8a:f3:\n         b1:1c:b9:60:19:36:7d:33:00:64:c0:b1:ea:8a:86:d5:3b:9f:\n         1f:39:32:33:77:cb:bf:95:50:79:18:84:2d:12:23:63:cf:78:\n         02:3e:1f:44:0c:a7:f5:2a:64:e4:64:7c:79:ba:99:84:f1:7d:\n         71:78:3d:70:a2:2b:a0:04:df:f2:9b:5d:ee:c7:08:49:4e:a1:\n         fa:39:b3:08:51:a1:22:64:a1:8e:61:44:aa:2f:db:7b:3f:e4:\n         77:9c:0a:35:ba:9b:e2:b4:87:92:52:60:fb:bb:01:bc:4a:c4:\n         ca:de:70:61:c4:bb:d3:9a:ad:25:47:73:d3:cf:cb:92:3e:ff:\n         09:b6:dc:22:32:fc:00:bf:c9:1c:9b:f7:49:6a:89:fb:91:09:\n         b4:f1:a2:89:2c:6d:9c:c0:80:1e:10:de:eb:45:93:a8:07:c9:\n         a3:2f:c5:5e\n-----BEGIN CERTIFICATE-----\nMIIDQTCCAimgAwIBAgIBAzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0\nIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowHzEdMBsGA1UEAwwU\nVGVzdCBDQSBTdWJhdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQCk8XTy2ySEOdomXHLILqY+V4iQq0EEenrzr/dDt5vYV5F+FdWqy43Upd5U\nSiK7428aA38nQorbRe55Dt3ACPzzSJ7qmfkIzTiGLcosm9AC8oEpcqGqo+r8X0qh\nFWhVqe19OiOEDArYEMcLjLOTmmFVEHM0FCHeKGW/atPd6D2eaXgecon9c35155tM\n8mU81e3U0VOWUrApweSdX9di0IR76NuFk7mPSgHEbm3iqW3sUddRSP70g0WhwbcA\naJwCkqYg1ECgyO6rlkBTEPG1Laj5UP7rlpqcwTEsH3DVpzf/NRqs1/ydT+FnIOad\nSmbIrOAK9U/UQ1bW4W0E9OoC1a+pAgMBAAGjgZQwgZEwDAYDVR0TAQH/BAIwADAO\nBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFOvqexXJTghbhycJ5EIQVEdy6TgpMDEG\nCWCGSAGG+EIBDQQkFiJQdXBwZXQgU2VydmVyIEludGVybmFsIENlcnRpZmljYXRl\nMB8GA1UdIwQYMBaAFFjTbjU2lwSRarbfRYCaU7PkNsg9MA0GCSqGSIb3DQEBCwUA\nA4IBAQCd6hpCtTiOZQ2lgyGKNyEFqDDq6RtRMSFyRI6wKnjMG/CVAcRs4XSTzMwu\nNroRMaFhBoTUA9U9s259QtpxkgH9LCIhykd4bMDOrLx5bfucKHdvV0Fn3A6mivOx\nHLlgGTZ9MwBkwLHqiobVO58fOTIzd8u/lVB5GIQtEiNjz3gCPh9EDKf1KmTkZHx5\nupmE8X1xeD1woiugBN/ym13uxwhJTqH6ObMIUaEiZKGOYUSqL9t7P+R3nAo1upvi\ntIeSUmD7uwG8SsTK3nBhxLvTmq0lR3PTz8uSPv8JttwiMvwAv8kcm/dJaon7kQm0\n8aKJLG2cwIAeEN7rRZOoB8mjL8Ve\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/ca.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 2 (0x2)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=Test CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:bf:58:f9:d6:80:6e:15:94:3c:42:eb:4c:9a:74:\n                    a6:70:16:0e:31:aa:3b:94:06:d9:28:5e:21:5b:da:\n                    fc:b7:cc:3e:06:fb:f9:fe:4c:84:dd:d1:b0:a3:1a:\n                    68:20:99:2e:28:02:59:dc:03:12:5c:21:8b:2a:0b:\n                    92:8f:87:61:d0:2c:bd:4b:84:84:c2:e8:4b:cf:09:\n                    28:86:65:89:9d:f5:85:70:a0:58:29:aa:bd:f5:3d:\n                    b0:fe:98:5d:f2:50:f3:f1:1f:94:d5:f8:ff:25:92:\n                    14:a4:81:19:bc:1a:03:5b:b3:1b:ea:7b:9c:99:9a:\n                    f4:3f:2a:a5:96:b0:07:96:48:79:b0:52:d4:fc:f2:\n                    51:62:94:fa:b0:69:b8:86:11:ab:b8:02:4d:93:e5:\n                    2b:01:50:ef:fa:06:f6:91:4d:56:58:a3:11:f3:0f:\n                    16:87:de:42:0a:53:b0:07:73:07:6d:ed:cf:91:52:\n                    2b:b4:e8:93:7e:57:aa:39:a4:47:1f:f6:ff:7d:8f:\n                    73:16:b3:2e:cb:4c:5f:40:c0:0c:02:2c:c8:6c:1b:\n                    8c:ec:54:d9:e1:60:98:de:77:e7:ee:a9:95:80:83:\n                    39:de:44:c2:83:d6:46:a2:f7:ac:63:6e:f5:b8:30:\n                    c2:5c:63:8f:0a:47:3a:6f:be:ef:3e:21:0a:52:69:\n                    8f:35\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Subject Key Identifier: \n                58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n            Netscape Comment: \n                Puppet Server Internal Certificate\n            X509v3 Authority Key Identifier: \n                keyid:58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n\n    Signature Algorithm: sha256WithRSAEncryption\n         8b:75:55:6d:7c:61:c3:55:54:a8:d8:ed:47:e0:0a:e6:fb:ae:\n         93:b3:d4:e5:69:ec:c3:db:0c:6a:91:05:84:3b:44:3c:d9:ce:\n         36:50:ce:b1:f5:88:39:e1:a7:87:77:81:b3:67:4d:06:1a:87:\n         fd:50:6c:07:e5:24:32:0d:0e:95:c5:0d:c0:8f:af:1f:e7:9c:\n         65:5c:e1:c7:75:80:d5:6d:04:6a:2d:15:bc:d5:09:dc:9b:38:\n         bc:12:df:94:32:25:36:53:bc:a4:1a:a2:c7:11:1d:aa:c6:93:\n         48:ec:25:52:88:6b:04:94:c3:ac:6b:bc:17:c0:75:2e:c9:53:\n         83:63:1c:77:69:42:4a:cd:7c:d1:3b:a5:a4:fc:1c:71:66:1b:\n         b1:44:32:53:4d:3c:26:a8:7e:ea:99:de:3a:5a:a9:70:7b:49:\n         f5:27:c4:d2:10:de:51:87:e2:5b:c9:7c:08:58:de:13:68:07:\n         f1:69:05:65:e9:b3:6f:ad:cd:06:10:f7:1d:f8:bd:c3:26:e1:\n         e1:0c:8a:0b:f2:36:d8:ab:4f:fe:b2:b2:7e:b4:3a:f5:31:46:\n         3f:ce:36:1c:e6:30:f7:11:66:21:1c:48:e9:49:a0:90:b4:3f:\n         04:ae:f0:f8:00:65:9e:9c:c5:28:d5:c8:3a:ff:a0:eb:03:bf:\n         c0:2f:41:cd\n-----BEGIN CERTIFICATE-----\nMIIDNzCCAh+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0\nIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowEjEQMA4GA1UEAwwH\nVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9Y+daAbhWU\nPELrTJp0pnAWDjGqO5QG2SheIVva/LfMPgb7+f5MhN3RsKMaaCCZLigCWdwDElwh\niyoLko+HYdAsvUuEhMLoS88JKIZliZ31hXCgWCmqvfU9sP6YXfJQ8/EflNX4/yWS\nFKSBGbwaA1uzG+p7nJma9D8qpZawB5ZIebBS1PzyUWKU+rBpuIYRq7gCTZPlKwFQ\n7/oG9pFNVlijEfMPFofeQgpTsAdzB23tz5FSK7Tok35XqjmkRx/2/32PcxazLstM\nX0DADAIsyGwbjOxU2eFgmN535+6plYCDOd5EwoPWRqL3rGNu9bgwwlxjjwpHOm++\n7z4hClJpjzUCAwEAAaOBlzCBlDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\nAwIBBjAdBgNVHQ4EFgQUWNNuNTaXBJFqtt9FgJpTs+Q2yD0wMQYJYIZIAYb4QgEN\nBCQWIlB1cHBldCBTZXJ2ZXIgSW50ZXJuYWwgQ2VydGlmaWNhdGUwHwYDVR0jBBgw\nFoAUWNNuNTaXBJFqtt9FgJpTs+Q2yD0wDQYJKoZIhvcNAQELBQADggEBAIt1VW18\nYcNVVKjY7UfgCub7rpOz1OVp7MPbDGqRBYQ7RDzZzjZQzrH1iDnhp4d3gbNnTQYa\nh/1QbAflJDINDpXFDcCPrx/nnGVc4cd1gNVtBGotFbzVCdybOLwS35QyJTZTvKQa\noscRHarGk0jsJVKIawSUw6xrvBfAdS7JU4NjHHdpQkrNfNE7paT8HHFmG7FEMlNN\nPCaofuqZ3jpaqXB7SfUnxNIQ3lGH4lvJfAhY3hNoB/FpBWXps2+tzQYQ9x34vcMm\n4eEMigvyNtirT/6ysn60OvUxRj/ONhzmMPcRZiEcSOlJoJC0PwSu8PgAZZ6cxSjV\nyDr/oOsDv8AvQc0=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/crl.pem",
    "content": "Certificate Revocation List (CRL):\n        Version 2 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Last Update: Jan  1 00:00:00 1970 GMT\n        Next Update: Jan 27 18:43:32 2034 GMT\n        CRL extensions:\n            X509v3 Authority Key Identifier: \n                keyid:58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n\n            X509v3 CRL Number: \n                0\nNo Revoked Certificates.\n    Signature Algorithm: sha256WithRSAEncryption\n         9c:a5:dd:b2:ab:44:59:89:72:dc:e3:70:5d:e0:cb:5c:4d:a2:\n         d3:35:61:15:2f:77:9c:d5:2c:82:82:b0:57:a6:f0:87:76:15:\n         4c:34:18:8b:6c:e9:df:36:e0:a2:60:68:0d:67:3c:e4:82:9a:\n         50:25:8d:fe:8c:be:57:f6:b1:ef:35:41:64:19:87:86:a5:39:\n         d4:1b:8e:3f:59:29:42:9b:74:dd:b8:b0:03:8d:62:cd:c6:f1:\n         84:ce:cb:8d:2f:dc:4c:5a:26:09:0d:e7:88:41:05:5f:27:80:\n         14:68:50:56:46:00:2c:15:63:8d:01:78:fe:48:b0:89:63:05:\n         38:5a:d5:b8:cf:14:0a:da:e0:ea:51:68:fe:5e:a6:d7:e9:30:\n         8f:99:6a:3b:bd:a3:79:e2:f7:b5:ae:11:79:b9:aa:a4:55:80:\n         74:95:ee:c9:f2:e6:fb:5b:71:ae:df:4f:ad:a1:8e:42:d2:c6:\n         2d:03:1a:46:d4:41:a8:6e:25:a4:1f:9d:b1:04:bc:8c:8e:c7:\n         b8:0d:77:ce:92:55:48:72:fb:5b:26:85:64:ff:ea:33:57:0c:\n         d0:38:d7:cb:3e:07:08:1e:be:e3:03:05:1e:e9:31:2f:86:0f:\n         8c:59:cd:20:30:5e:e7:e5:2b:a5:8e:11:0c:33:04:23:64:d0:\n         78:80:28:a7\n-----BEGIN X509 CRL-----\nMIIBizB1AgEBMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EXDTcw\nMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlqgLzAtMB8GA1UdIwQYMBaAFFjTbjU2\nlwSRarbfRYCaU7PkNsg9MAoGA1UdFAQDAgEAMA0GCSqGSIb3DQEBCwUAA4IBAQCc\npd2yq0RZiXLc43Bd4MtcTaLTNWEVL3ec1SyCgrBXpvCHdhVMNBiLbOnfNuCiYGgN\nZzzkgppQJY3+jL5X9rHvNUFkGYeGpTnUG44/WSlCm3TduLADjWLNxvGEzsuNL9xM\nWiYJDeeIQQVfJ4AUaFBWRgAsFWONAXj+SLCJYwU4WtW4zxQK2uDqUWj+XqbX6TCP\nmWo7vaN54ve1rhF5uaqkVYB0le7J8ub7W3Gu30+toY5C0sYtAxpG1EGobiWkH52x\nBLyMjse4DXfOklVIcvtbJoVk/+ozVwzQONfLPgcIHr7jAwUe6TEvhg+MWc0gMF7n\n5SuljhEMMwQjZNB4gCin\n-----END X509 CRL-----\n"
  },
  {
    "path": "spec/fixtures/ssl/ec-key-openssl.pem",
    "content": "-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIC1xD81Rha4nzXM4OrG/eLlh3KzmClXOUlOeE975dZ8loAoGCCqGSM49\nAwEHoUQDQgAEUFW3mhj/TNMWNrctybN7Dho5ro/437/9luPrrPqkP5ucfZ+ftClN\n8yHzPyf4l770aFB0jf4r1BkdBmQ16fzE3g==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/ec-key-pk8.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrSK9I6zVGCuBNUcc\nJ4KS6WOdfeE4SnfK02vhMzWXWl+hRANCAASh/nwXGgRbniDHt3fUKq6Odqokxqn3\n62kj/zH0oSYC3eJIaEaw9kwYb1cSD9mHDlrisWSRFX66i/LrXGTKAVtd\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/ec-key.pem",
    "content": "Private-Key: (256 bit)\npriv:\n    3c:a7:b7:fd:33:b3:ab:bf:09:82:0e:97:88:2f:1a:\n    7b:af:da:9d:c6:ad:e0:b1:06:ce:42:33:be:c9:97:\n    db:96\npub:\n    04:e7:c1:a2:97:cd:95:ad:fe:a5:05:43:41:2d:19:\n    55:21:11:f5:f6:46:9f:45:74:6a:8c:91:c2:2e:32:\n    aa:47:1e:9e:2e:49:99:7c:af:98:fa:d8:ae:b3:a1:\n    26:46:d3:22:d3:3c:b1:2d:8d:3b:e2:03:cf:25:93:\n    d5:b7:a8:e2:ab\nASN1 OID: prime256v1\nNIST CURVE: P-256\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDynt/0zs6u/CYIOl4gvGnuv2p3GreCxBs5CM77Jl9uWoAoGCCqGSM49\nAwEHoUQDQgAE58Gil82Vrf6lBUNBLRlVIRH19kafRXRqjJHCLjKqRx6eLkmZfK+Y\n+tius6EmRtMi0zyxLY074gPPJZPVt6jiqw==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/ec.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 9 (0x9)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=ec\n        Subject Public Key Info:\n            Public Key Algorithm: id-ecPublicKey\n                Public-Key: (256 bit)\n                pub:\n                    04:e7:c1:a2:97:cd:95:ad:fe:a5:05:43:41:2d:19:\n                    55:21:11:f5:f6:46:9f:45:74:6a:8c:91:c2:2e:32:\n                    aa:47:1e:9e:2e:49:99:7c:af:98:fa:d8:ae:b3:a1:\n                    26:46:d3:22:d3:3c:b1:2d:8d:3b:e2:03:cf:25:93:\n                    d5:b7:a8:e2:ab\n                ASN1 OID: prime256v1\n                NIST CURVE: P-256\n    Signature Algorithm: sha256WithRSAEncryption\n         7b:c4:a8:34:2e:98:20:62:b2:23:44:29:1b:b7:b4:00:01:ea:\n         94:59:ef:2a:76:73:4d:a4:5a:28:73:11:54:d5:00:19:89:90:\n         e9:00:1b:68:01:2e:ab:66:76:bb:0b:2f:08:48:86:7b:8f:02:\n         81:42:70:ec:fb:66:59:e7:40:f3:7b:13:c7:3a:29:79:60:5e:\n         b1:86:0e:ef:78:dd:1c:67:b6:60:02:e0:a1:6d:bc:66:99:6d:\n         46:ec:dd:a8:b6:36:38:1a:2e:29:5d:f7:aa:07:ef:d5:a7:48:\n         78:38:16:29:08:61:f7:ab:a3:03:05:e6:7c:60:46:50:f6:05:\n         03:a2:37:7a:67:0a:63:f5:bd:5d:12:82:06:0f:58:c9:58:33:\n         bf:a7:4c:c1:e5:3c:5e:d7:e4:91:f2:67:24:03:46:e8:94:84:\n         14:ae:be:c6:bc:2d:37:d8:5f:1d:ee:68:52:6c:9f:34:14:8d:\n         ba:e6:7d:b1:d3:47:76:13:ef:11:18:28:64:c3:84:6b:cf:5d:\n         68:4c:59:4f:09:55:e9:55:63:de:61:cf:98:2b:cf:f2:be:8d:\n         c8:7b:62:8d:8b:98:4d:16:81:ce:8a:83:5a:e8:3a:50:18:cb:\n         cc:92:54:07:d0:29:3c:96:70:86:b7:f3:a2:bb:8f:df:62:51:\n         3d:20:58:0d\n-----BEGIN CERTIFICATE-----\nMIIB2TCBwqADAgECAgEJMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNVBAMMFFRlc3Qg\nQ0EgU3ViYXV0aG9yaXR5MB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlow\nDTELMAkGA1UEAwwCZWMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATnwaKXzZWt\n/qUFQ0EtGVUhEfX2Rp9FdGqMkcIuMqpHHp4uSZl8r5j62K6zoSZG0yLTPLEtjTvi\nA88lk9W3qOKrMA0GCSqGSIb3DQEBCwUAA4IBAQB7xKg0LpggYrIjRCkbt7QAAeqU\nWe8qdnNNpFoocxFU1QAZiZDpABtoAS6rZna7Cy8ISIZ7jwKBQnDs+2ZZ50DzexPH\nOil5YF6xhg7veN0cZ7ZgAuChbbxmmW1G7N2otjY4Gi4pXfeqB+/Vp0h4OBYpCGH3\nq6MDBeZ8YEZQ9gUDojd6Zwpj9b1dEoIGD1jJWDO/p0zB5Txe1+SR8mckA0bolIQU\nrr7GvC032F8d7mhSbJ80FI265n2x00d2E+8RGChkw4Rrz11oTFlPCVXpVWPeYc+Y\nK8/yvo3Ie2KNi5hNFoHOioNa6DpQGMvMklQH0Ck8lnCGt/Oiu4/fYlE9IFgN\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/encrypted-ec-key.pem",
    "content": "Private-Key: (256 bit)\npriv:\n    3c:a7:b7:fd:33:b3:ab:bf:09:82:0e:97:88:2f:1a:\n    7b:af:da:9d:c6:ad:e0:b1:06:ce:42:33:be:c9:97:\n    db:96\npub:\n    04:e7:c1:a2:97:cd:95:ad:fe:a5:05:43:41:2d:19:\n    55:21:11:f5:f6:46:9f:45:74:6a:8c:91:c2:2e:32:\n    aa:47:1e:9e:2e:49:99:7c:af:98:fa:d8:ae:b3:a1:\n    26:46:d3:22:d3:3c:b1:2d:8d:3b:e2:03:cf:25:93:\n    d5:b7:a8:e2:ab\nASN1 OID: prime256v1\nNIST CURVE: P-256\n-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,1ADDF7360FB53F38DC3B58192DBBCC40\n\nacDyKuVWLOvDWANpKQrbR45nec2DS6RMmwi5f6n96+4Z+ejPVjKFswmabDyyOwnd\nUJgwDn+LZzLBNHuvF7OBc7eQ9cLsR2abGhLdW7HrmGspKL1ZItxnAuKPGGby1JT3\n4T/RxSVjlIROCpJ+oKW6dCMGWQNH2uoifGjHmsuLS8o=\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/encrypted-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:ac:0d:e2:e1:62:ee:7a:55:f6:46:0a:5f:ed:dc:\n    4a:a6:d4:d0:19:7f:a8:4d:cd:a0:c1:61:d4:e8:78:\n    50:d4:5d:40:1f:e0:c3:79:26:95:19:4c:cd:70:10:\n    53:38:b1:68:e4:7e:d4:44:65:68:94:8c:b6:ab:d2:\n    22:36:d8:b0:92:ba:ed:3f:00:ec:b0:b6:5f:26:ac:\n    f7:2b:71:e4:d7:8a:28:49:a8:fe:f9:a1:c8:b8:09:\n    73:8c:f7:d3:90:ab:ed:74:fb:25:29:bf:81:51:b3:\n    ac:02:81:ba:c1:7f:8a:ad:d7:18:6d:d0:73:1e:7f:\n    d9:54:ed:0b:a2:a6:d7:7e:22:e1:3f:a3:6d:77:4a:\n    9e:17:68:42:ef:99:63:e0:0f:5c:35:4e:3a:ef:5a:\n    84:3c:c6:c1:ac:78:c5:fe:60:56:f4:e5:86:7d:60:\n    1b:55:3c:bd:6a:c8:22:ea:b6:c3:f6:8a:67:e8:cb:\n    5d:23:61:44:4b:9e:24:f8:91:69:25:90:0b:d7:2e:\n    84:3f:c1:a2:8f:87:68:a8:36:b3:a7:b3:c8:0e:06:\n    bd:6b:f3:54:41:d2:47:49:a7:2f:9b:3c:2f:37:89:\n    46:12:90:95:fe:6a:6f:5e:29:c6:d9:ea:90:47:29:\n    1b:85:1e:d3:19:e9:80:40:f7:33:67:bb:e5:17:50:\n    5c:cd\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    15:08:01:82:c1:80:1b:2e:24:d3:7c:f3:2a:f5:31:\n    9e:e1:06:ab:07:42:c0:77:f1:3d:92:42:df:43:cd:\n    c5:97:bc:7d:e9:10:9b:df:8e:7c:30:3a:30:87:9e:\n    54:a0:c3:0c:a3:40:39:38:18:27:88:67:cf:ea:f8:\n    c9:b1:85:2b:fa:73:83:af:0e:3f:af:9b:43:f4:02:\n    a6:a9:de:6a:46:76:14:42:f7:1f:f6:99:bd:7d:52:\n    45:9a:09:9f:76:94:a9:27:05:ec:eb:7e:d8:48:d9:\n    2e:d9:42:c5:e2:5c:46:51:b3:a7:c0:c2:41:a9:29:\n    1c:cb:79:0f:a1:cb:57:9c:a2:6a:35:d6:da:b2:21:\n    55:70:e2:8b:2d:e8:16:d3:37:41:fc:df:d4:b1:e0:\n    db:90:b8:e4:28:74:ad:8a:1d:92:b3:02:c3:68:65:\n    75:38:99:32:38:28:1f:64:91:3b:42:86:45:19:54:\n    2d:b0:26:48:88:25:32:5c:ab:f2:de:02:ec:77:fc:\n    cc:ea:6f:ce:e0:40:44:7e:80:c8:bf:99:25:5d:d5:\n    88:57:55:4b:07:64:d2:8b:a3:c2:86:56:d9:6f:16:\n    6c:c5:52:84:9b:e6:b4:4c:2e:ff:59:7b:d9:0e:fb:\n    95:4c:5a:56:58:e9:d1:9c:0f:84:ad:36:d3:c8:5b:\n    b5\nprime1:\n    00:e3:17:e6:10:fa:80:d4:86:c4:59:bb:36:57:ad:\n    9a:12:8c:63:e0:84:b3:f3:ce:4b:d1:bf:a3:8c:0e:\n    a2:41:62:1a:46:74:eb:ce:01:55:33:8b:b1:1f:f2:\n    30:df:12:0f:69:0a:fb:e1:1e:32:80:5c:b0:85:5b:\n    ff:5a:93:9a:50:1b:7b:85:83:f0:8e:26:f9:e2:35:\n    5c:94:0c:ce:29:22:52:a0:a9:ac:64:d3:56:93:0d:\n    11:f7:16:07:10:0f:97:03:43:11:57:91:15:4d:04:\n    9c:6d:5e:26:b3:41:46:c8:7c:46:3c:2a:dd:40:e1:\n    9f:32:75:2b:82:49:dc:d8:73\nprime2:\n    00:c1:f4:79:5b:51:e7:58:84:a6:57:11:48:e6:25:\n    60:dd:7b:67:e1:e4:61:6c:f2:37:6e:71:68:53:47:\n    1d:f2:e8:31:9f:60:64:6c:97:cf:2c:b9:b0:a5:39:\n    e2:11:40:c7:00:8a:4a:48:60:f3:33:3f:17:9f:56:\n    f9:74:f0:ad:ce:92:6a:30:c7:aa:32:b1:58:cb:08:\n    d4:44:40:29:56:cc:09:26:90:55:84:d8:b7:15:4a:\n    05:89:5d:29:d4:4a:4d:15:a9:40:f3:c7:a8:b0:73:\n    ba:4d:63:14:62:e6:cb:d6:b7:6c:8e:cb:68:a8:bf:\n    2f:10:5b:d5:f8:5b:64:e5:bf\nexponent1:\n    4c:d9:6f:8f:db:55:f1:95:d1:a8:94:04:25:d3:a7:\n    ca:13:1c:51:84:56:e9:70:ac:93:c4:88:72:03:19:\n    c1:8a:93:5d:b8:7f:7b:ed:53:89:e8:01:fe:cf:94:\n    de:48:5c:52:ad:d6:e3:2d:b6:e5:5d:78:97:08:b4:\n    f7:4f:ef:ee:9f:fe:43:06:8d:47:6d:c5:2e:59:e4:\n    84:6d:78:ee:ce:a0:ab:a1:ff:a6:f7:25:db:09:97:\n    44:c8:7d:87:5f:df:38:c9:5f:7b:04:ab:f2:ae:56:\n    c9:64:0d:30:a8:2c:6d:f8:30:44:78:34:fb:99:de:\n    a7:d2:a2:f0:aa:52:44:25\nexponent2:\n    7f:ef:5f:c1:51:d4:34:fa:42:b2:79:cf:49:27:ec:\n    ae:0b:81:a9:6a:38:ad:61:54:19:00:ab:5d:0b:33:\n    01:10:11:f3:5b:e4:c2:10:9c:f2:96:85:a3:66:fb:\n    ec:7f:7b:04:ab:33:76:6c:a4:de:ef:c6:08:2f:99:\n    9a:7e:4b:57:50:12:c5:9c:5e:72:d3:b2:8b:32:86:\n    b9:82:4d:02:58:d1:cc:63:36:55:cb:91:70:74:84:\n    14:68:a4:77:c8:8e:f2:33:d3:89:39:f0:d6:7b:6f:\n    af:2e:24:bb:5c:1b:a6:c5:14:d1:57:f0:f0:26:33:\n    c8:29:9b:89:17:d8:05:07\ncoefficient:\n    00:bd:78:38:fa:43:8a:96:b9:4f:69:3f:2a:e0:82:\n    b6:be:76:43:91:b5:eb:ad:d6:db:a2:e3:51:01:39:\n    c8:54:18:87:06:03:e7:0e:2e:9c:82:5d:54:df:bb:\n    97:c8:d2:fa:be:12:8f:df:eb:03:b0:78:74:d0:cb:\n    cc:0c:ad:9a:f0:c7:16:3e:cf:84:d4:c7:b8:91:65:\n    7d:bd:29:cd:71:7a:f9:16:30:cb:4f:f0:f0:59:96:\n    24:19:d6:77:eb:36:1d:1c:3d:0d:89:08:9f:64:bb:\n    86:b9:be:3f:c1:67:e5:b7:fb:d4:65:cd:54:cc:3d:\n    3b:a1:5a:3f:61:91:20:c3:10\n-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,0CF460F33E71D0E4C8E65D782B9359BA\n\njR5jkDTTEatVYhvUFeDQD8GVek/IrzTLnAX3fQzhcLY25gEqdYO4wwuP6JrZmT18\nBmyNJNTmOgmRXmIv8dxpZrUe5Ah1F4dmrgu4VdnOpZ7HiMzFyPaIZ6IpWBFsJ/F+\n6NFpR9ii21Om1anhF/uJymny00h1tbzQ+QKsylzhxCL5PinHgJnz/V1KTvuefQaZ\nYTudmKzgPs4eu2urrtDGbhluVUlRoSyDINsjHuOPmd3RAa8PdU6YgcdusO5Zkv6r\nPFho9xdcocbeJQyYlARuZaQD+cmLFYwiZ/oIWfspQWEVa6CN+4MpxWYLfMp/JvKK\n3WwQ1A55m49IE97peUjs5nP6uCFyJQkVQklo7+07CdclNnMryoCBpelwvOCzv0Ya\nhnDRT4VWyRaRp8+bhU0kIh9NewBWB5e/YmlhyOqi4k3cKYDPxZJoVyFF6fx5Ze4T\nJ0TTzMTYJltkPqxiBGS/5nQ6TvDgixDXSOJQ/KhSMu3oaSRsALiYaDHcW2ZtcXdV\nAsEMLgiPqXIfbLVBSTE0pf7MbWbjhRVgK19epjbt7W9qOAvlls9I2ebBQOXJKzD2\n4EkqIlIWkoITXlALYYuT5ub0V4XKxTp0OU3nnPtq6kG7jA6kK9iuVTPGkhgm0ZOX\n8sUEHBOeIGt9VR9BEgS/JbyOnOtalKQCSIeI+6MoJpQe1AU8f9jzUhMh0VxUWLtO\na41zpy1NvuN921ytCT8Ew9EVXgE1IewOLoEIBOJ1iNXgxSYs7qgvGwPcfxD7EySY\nTNIw4byEpTAgjtMF9H+AM8Ty1AJUk71d2i/WDZI9uQ8JS7GSa528yyoE4jEj7FZ5\nfYdyKBJ7Eh1bBFk/Sbfox6nSptWJnC0vQwllq0XoBPHGZkxJnmRZIFipe2YVOHN4\ni1ixHNE4H9lcWBXd2YmQ8w1phVbZPaBIuH+YaY9TWdgo0qQHPr4C4sjQDx8oFriT\n0TFCxLaWRHMzKUGMoDu2hfjpuyP3IsQXgDrLaNvb6qA00mssdoQ6ZfbYJsTcHYb4\nFZnSm7e+CgMDyNZgM5xA2hU9B+Gx7/GUnpLBmV3YsHQzMaAHxzsvXV6IgVnS9pva\nDWJAZlecggMU4U7qC0ag/7nG493SBc0bviyrVbTPKqo+yD72KknsF+Bj7n2Pb201\ngeuaF38HO4aU8fjtbl2gAoSGq2kaLGHwtiPRY+Iqq9fEydO9yN2w8WV3Vhso0kHr\n37FwgCmnWnsr/6HrEJkNJhZluSwoxnYXYFu7B2A4efgvDXtkdi/5iE8WgjLoazdJ\nudXY9Pfd0N/TFXugkWXNJ1K6bLluIMfnOn+D2Mt+Ha1hkgeguGazaB6lINBtwYJZ\nJ4FIn8A1L+34A/s1sRYzNVdZrvx9U91tl6KLfuJGAjVJe81D593DEd288+zpn8V6\nRgTcsWW7YhQ8nwwdsq0mA0Is2GshraCyV6yofugh7rOPIuxydp53Rrev0/K5dmjq\n0ROjjWHMF7XJhUS72GfmIeT752EjGKUWOeB7Rwzbi+3wcJW36ZbbsxJFPxq7Ydj6\nXZQ9/Nb97N9yVoNfnw2dhLlTvLg0f9uWC0A25hW7aCL88w62dbRiF8CVBqdO52BQ\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/intermediate-agent-crl.pem",
    "content": "Certificate Revocation List (CRL):\n        Version 2 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Agent Subauthority\n        Last Update: Jan  1 00:00:00 1970 GMT\n        Next Update: Jan 27 18:43:32 2034 GMT\n        CRL extensions:\n            X509v3 Authority Key Identifier: \n                keyid:48:07:33:D5:80:0C:18:AD:FD:7A:04:33:84:8F:A8:B2:D9:88:18:9A\n\n            X509v3 CRL Number: \n                0\nNo Revoked Certificates.\n    Signature Algorithm: sha256WithRSAEncryption\n         72:c6:aa:64:c7:1e:70:3a:93:ee:69:61:55:51:fb:95:3e:b1:\n         c7:e9:ac:60:4d:36:fb:94:26:53:c8:56:42:27:03:b9:f8:e2:\n         0c:fd:b7:75:21:58:27:08:69:93:19:34:71:ba:45:15:f5:34:\n         70:06:be:31:3a:63:83:1f:c2:ba:9f:08:0e:7f:10:97:41:32:\n         17:ac:71:69:ea:2b:44:b2:dd:27:c0:5f:da:7a:5a:26:ec:f6:\n         ba:b1:da:0b:c9:4d:f8:22:f4:78:7e:fe:4f:ae:86:55:11:74:\n         28:ba:c3:2f:c1:6c:09:a3:8a:60:ba:b3:8c:0e:de:cb:d4:3c:\n         a1:66:3e:a4:cd:f3:5d:4e:59:81:c1:b3:d4:6a:6c:ec:43:a1:\n         35:91:85:3c:ba:79:e4:4a:cc:a4:bd:ae:a8:cb:8f:0c:d4:b8:\n         26:5b:8d:2a:14:26:34:2b:84:d1:7e:6a:ae:ec:cf:14:cd:ca:\n         ad:1c:7e:f4:80:e3:32:c2:e8:b7:87:fa:5f:22:cf:08:bc:f4:\n         f5:a5:98:1f:d7:43:d9:29:eb:f3:65:c4:f4:d9:8e:90:3a:87:\n         27:ee:e7:7b:93:38:6b:5a:49:2e:4a:fa:e8:24:20:08:55:56:\n         4e:80:40:27:2f:d2:bf:52:31:19:d7:f1:b5:14:73:4c:cc:a0:\n         62:aa:50:0a\n-----BEGIN X509 CRL-----\nMIIBnzCBiAIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBpUZXN0IENBIEFn\nZW50IFN1YmF1dGhvcml0eRcNNzAwMTAxMDAwMDAwWhcNMzQwMTI3MTg0MzMyWqAv\nMC0wHwYDVR0jBBgwFoAUSAcz1YAMGK39egQzhI+ostmIGJowCgYDVR0UBAMCAQAw\nDQYJKoZIhvcNAQELBQADggEBAHLGqmTHHnA6k+5pYVVR+5U+scfprGBNNvuUJlPI\nVkInA7n44gz9t3UhWCcIaZMZNHG6RRX1NHAGvjE6Y4MfwrqfCA5/EJdBMhescWnq\nK0Sy3SfAX9p6Wibs9rqx2gvJTfgi9Hh+/k+uhlURdCi6wy/BbAmjimC6s4wO3svU\nPKFmPqTN811OWYHBs9RqbOxDoTWRhTy6eeRKzKS9rqjLjwzUuCZbjSoUJjQrhNF+\naq7szxTNyq0cfvSA4zLC6LeH+l8izwi89PWlmB/XQ9kp6/NlxPTZjpA6hyfu53uT\nOGtaSS5K+ugkIAhVVk6AQCcv0r9SMRnX8bUUc0zMoGKqUAo=\n-----END X509 CRL-----\n"
  },
  {
    "path": "spec/fixtures/ssl/intermediate-agent.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 10 (0xa)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=Test CA Agent Subauthority\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:a5:a5:c5:33:81:1d:c3:9f:fc:10:26:db:e0:81:\n                    cd:2d:fe:0f:b0:74:16:51:2a:39:c1:3b:10:ca:27:\n                    ef:c6:ea:e7:95:a3:05:bd:24:dc:cb:2a:46:2e:87:\n                    cb:dc:b1:dc:d4:52:fd:86:a2:ec:f2:03:2b:97:aa:\n                    a5:38:35:45:02:5d:17:f1:0e:e8:45:57:d9:99:9e:\n                    d6:74:32:ce:f4:87:87:f7:d6:22:12:cd:a7:40:56:\n                    00:a5:d7:d2:8e:be:91:69:c2:6c:89:02:f7:b5:ef:\n                    56:66:49:32:fc:f8:a0:f1:f2:d0:56:1b:cc:bc:5c:\n                    33:4b:fd:c9:9e:0f:55:bd:4f:ef:46:57:26:72:61:\n                    94:ae:a7:85:5b:91:53:d2:1c:ba:b8:19:44:ee:b5:\n                    ca:34:dd:95:6b:8b:de:61:75:3d:6e:c9:79:f0:38:\n                    00:08:56:6b:b4:b0:4e:34:c2:45:ca:6a:25:33:65:\n                    0a:1f:95:2b:5b:71:a6:fe:af:ff:94:a5:14:6d:ca:\n                    1a:d3:6b:2d:44:e1:74:45:24:50:e1:cd:00:bb:0f:\n                    0d:fe:20:27:f8:14:c6:2b:ea:3a:b9:50:5c:a4:de:\n                    c3:90:ee:7b:b6:d3:82:d4:06:4f:af:d8:b0:d7:a7:\n                    d4:64:45:75:1f:ba:12:77:18:aa:de:6c:29:fe:e0:\n                    ea:bb\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Subject Key Identifier: \n                48:07:33:D5:80:0C:18:AD:FD:7A:04:33:84:8F:A8:B2:D9:88:18:9A\n            Netscape Comment: \n                Puppet Server Internal Certificate\n            X509v3 Authority Key Identifier: \n                keyid:58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n\n    Signature Algorithm: sha256WithRSAEncryption\n         b3:91:b4:7f:0b:b9:c3:7a:60:cf:e9:94:55:f1:12:7d:fb:f5:\n         1f:4b:0a:39:2d:70:9a:8d:ad:fa:98:b6:db:6a:80:a5:1b:23:\n         6f:7e:85:c1:62:11:44:8d:8a:bb:76:35:58:19:a2:e5:e2:43:\n         e7:17:75:b8:b7:74:fd:a2:b4:70:d8:76:b6:f0:e0:be:83:65:\n         85:ce:e9:92:5a:23:aa:f5:f5:ef:6b:d9:c3:ec:94:7d:ca:de:\n         18:75:91:23:90:14:c4:a7:0f:e6:e0:a7:b1:2c:af:9b:71:7f:\n         9e:f6:91:78:db:25:77:16:42:92:cc:d8:a8:b0:5e:d2:98:69:\n         56:0e:b8:6a:ef:c9:b8:e5:37:2c:12:47:a8:53:31:3e:0e:1a:\n         48:85:9e:46:39:e3:60:40:5e:db:28:21:fa:8a:af:83:d7:c6:\n         62:b6:16:36:2e:3d:58:0e:96:21:88:d1:1a:e7:d6:74:4c:ee:\n         c0:02:e0:51:c1:c6:1f:bd:c8:83:38:eb:86:e1:45:a4:f6:a0:\n         9e:bd:62:e5:75:c2:e4:9c:3a:b7:cb:28:03:5c:7c:ff:28:3f:\n         80:22:4b:ae:6c:8c:9c:9f:71:8b:0c:93:42:85:50:20:7d:ca:\n         a3:84:4f:e2:a0:ce:48:69:fd:8f:ef:3c:0c:55:a8:f0:5c:9f:\n         e8:37:48:65\n-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIBCjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0\nIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowJTEjMCEGA1UEAwwa\nVGVzdCBDQSBBZ2VudCBTdWJhdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQClpcUzgR3Dn/wQJtvggc0t/g+wdBZRKjnBOxDKJ+/G6ueVowW9\nJNzLKkYuh8vcsdzUUv2GouzyAyuXqqU4NUUCXRfxDuhFV9mZntZ0Ms70h4f31iIS\nzadAVgCl19KOvpFpwmyJAve171ZmSTL8+KDx8tBWG8y8XDNL/cmeD1W9T+9GVyZy\nYZSup4VbkVPSHLq4GUTutco03ZVri95hdT1uyXnwOAAIVmu0sE40wkXKaiUzZQof\nlStbcab+r/+UpRRtyhrTay1E4XRFJFDhzQC7Dw3+ICf4FMYr6jq5UFyk3sOQ7nu2\n04LUBk+v2LDXp9RkRXUfuhJ3GKrebCn+4Oq7AgMBAAGjgZcwgZQwDwYDVR0TAQH/\nBAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFEgHM9WADBit/XoEM4SP\nqLLZiBiaMDEGCWCGSAGG+EIBDQQkFiJQdXBwZXQgU2VydmVyIEludGVybmFsIENl\ncnRpZmljYXRlMB8GA1UdIwQYMBaAFFjTbjU2lwSRarbfRYCaU7PkNsg9MA0GCSqG\nSIb3DQEBCwUAA4IBAQCzkbR/C7nDemDP6ZRV8RJ9+/UfSwo5LXCaja36mLbbaoCl\nGyNvfoXBYhFEjYq7djVYGaLl4kPnF3W4t3T9orRw2Ha28OC+g2WFzumSWiOq9fXv\na9nD7JR9yt4YdZEjkBTEpw/m4KexLK+bcX+e9pF42yV3FkKSzNiosF7SmGlWDrhq\n78m45TcsEkeoUzE+DhpIhZ5GOeNgQF7bKCH6iq+D18ZithY2Lj1YDpYhiNEa59Z0\nTO7AAuBRwcYfvciDOOuG4UWk9qCevWLldcLknDq3yygDXHz/KD+AIkuubIycn3GL\nDJNChVAgfcqjhE/ioM5Iaf2P7zwMVajwXJ/oN0hl\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/intermediate-crl.pem",
    "content": "Certificate Revocation List (CRL):\n        Version 2 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Last Update: Jan  1 00:00:00 1970 GMT\n        Next Update: Jan 27 18:43:32 2034 GMT\n        CRL extensions:\n            X509v3 Authority Key Identifier: \n                keyid:EB:EA:7B:15:C9:4E:08:5B:87:27:09:E4:42:10:54:47:72:E9:38:29\n\n            X509v3 CRL Number: \n                0\nRevoked Certificates:\n    Serial Number: 08\n        Revocation Date: Jan 30 18:43:32 2024 GMT\n        CRL entry extensions:\n            X509v3 CRL Reason Code: \n                Key Compromise\n    Signature Algorithm: sha256WithRSAEncryption\n         45:02:fb:e5:98:d2:db:b9:1d:00:55:6f:ca:2a:16:8f:62:5a:\n         2b:1f:34:75:1a:7e:84:70:35:92:e9:b9:76:79:88:dc:1c:77:\n         c3:2e:3d:e1:05:db:5f:23:ba:09:14:c8:39:4e:58:6d:19:2c:\n         c6:16:04:77:ae:5d:a6:09:1f:b2:c5:f5:76:f6:39:4f:7a:b7:\n         58:df:4b:ea:6f:6e:bb:54:88:dc:eb:f9:b9:f3:35:50:ed:66:\n         1a:53:58:3d:71:16:32:ab:1e:5f:85:90:96:70:72:10:77:90:\n         70:b5:5f:15:3e:ef:93:d4:c3:bb:92:cc:45:42:9e:d6:23:c4:\n         d2:20:75:08:d8:f4:09:82:56:85:92:83:32:87:0e:5e:85:e8:\n         2d:fd:8d:b8:57:9c:b8:3e:e3:83:4e:f0:65:87:ed:db:df:f8:\n         7a:ab:92:c7:dd:c4:88:b0:84:4a:dd:1b:14:97:7a:73:51:2b:\n         39:61:8a:0d:6d:9b:b4:4e:4e:9e:05:f2:4b:a0:b0:be:29:60:\n         12:99:d3:3b:d7:b6:5a:f7:fb:3d:79:01:c0:09:ea:79:32:38:\n         fa:4c:12:74:c8:73:39:62:b5:81:83:0e:0a:7c:d1:65:1f:b4:\n         ed:14:44:1c:95:5b:42:41:8b:49:f4:d4:0c:5c:82:7c:6d:29:\n         4a:c9:1d:a0\n-----BEGIN X509 CRL-----\nMIIBvTCBpgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0IENBIFN1\nYmF1dGhvcml0eRcNNzAwMTAxMDAwMDAwWhcNMzQwMTI3MTg0MzMyWjAiMCACAQgX\nDTI0MDEzMDE4NDMzMlowDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0jBBgwFoAU6+p7\nFclOCFuHJwnkQhBUR3LpOCkwCgYDVR0UBAMCAQAwDQYJKoZIhvcNAQELBQADggEB\nAEUC++WY0tu5HQBVb8oqFo9iWisfNHUafoRwNZLpuXZ5iNwcd8MuPeEF218jugkU\nyDlOWG0ZLMYWBHeuXaYJH7LF9Xb2OU96t1jfS+pvbrtUiNzr+bnzNVDtZhpTWD1x\nFjKrHl+FkJZwchB3kHC1XxU+75PUw7uSzEVCntYjxNIgdQjY9AmCVoWSgzKHDl6F\n6C39jbhXnLg+44NO8GWH7dvf+HqrksfdxIiwhErdGxSXenNRKzlhig1tm7ROTp4F\n8kugsL4pYBKZ0zvXtlr3+z15AcAJ6nkyOPpMEnTIczlitYGDDgp80WUftO0URByV\nW0JBi0n01AxcgnxtKUrJHaA=\n-----END X509 CRL-----\n"
  },
  {
    "path": "spec/fixtures/ssl/intermediate-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:a4:f1:74:f2:db:24:84:39:da:26:5c:72:c8:2e:\n    a6:3e:57:88:90:ab:41:04:7a:7a:f3:af:f7:43:b7:\n    9b:d8:57:91:7e:15:d5:aa:cb:8d:d4:a5:de:54:4a:\n    22:bb:e3:6f:1a:03:7f:27:42:8a:db:45:ee:79:0e:\n    dd:c0:08:fc:f3:48:9e:ea:99:f9:08:cd:38:86:2d:\n    ca:2c:9b:d0:02:f2:81:29:72:a1:aa:a3:ea:fc:5f:\n    4a:a1:15:68:55:a9:ed:7d:3a:23:84:0c:0a:d8:10:\n    c7:0b:8c:b3:93:9a:61:55:10:73:34:14:21:de:28:\n    65:bf:6a:d3:dd:e8:3d:9e:69:78:1e:72:89:fd:73:\n    7e:75:e7:9b:4c:f2:65:3c:d5:ed:d4:d1:53:96:52:\n    b0:29:c1:e4:9d:5f:d7:62:d0:84:7b:e8:db:85:93:\n    b9:8f:4a:01:c4:6e:6d:e2:a9:6d:ec:51:d7:51:48:\n    fe:f4:83:45:a1:c1:b7:00:68:9c:02:92:a6:20:d4:\n    40:a0:c8:ee:ab:96:40:53:10:f1:b5:2d:a8:f9:50:\n    fe:eb:96:9a:9c:c1:31:2c:1f:70:d5:a7:37:ff:35:\n    1a:ac:d7:fc:9d:4f:e1:67:20:e6:9d:4a:66:c8:ac:\n    e0:0a:f5:4f:d4:43:56:d6:e1:6d:04:f4:ea:02:d5:\n    af:a9\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    26:98:fa:2c:fa:6b:09:26:20:8e:69:83:d2:a4:57:\n    f2:ae:b9:1e:61:74:cd:7f:7b:d6:1a:8f:5a:21:55:\n    2e:c3:0f:20:da:2b:2d:d4:84:54:06:9c:88:4c:f0:\n    e3:d6:cc:e0:e1:80:97:c7:52:87:0d:4e:48:b5:d9:\n    de:5e:3c:13:12:23:5d:f2:b9:fb:fd:4a:04:dc:22:\n    3b:5e:1a:c7:15:c8:73:d2:87:52:4f:19:66:05:46:\n    89:7b:60:f6:ea:d4:d0:41:12:9e:8b:d2:dd:cc:cf:\n    47:3c:9d:a9:24:af:32:20:af:54:b8:81:54:63:4f:\n    ac:03:6b:a0:b9:ff:f1:59:8c:b6:62:7a:1b:70:fb:\n    2b:16:22:d9:ef:d8:c0:57:1e:96:f4:fa:54:45:70:\n    b6:4b:61:31:b4:4e:02:62:ba:b8:f3:3e:ee:ba:e0:\n    49:5b:8a:13:a4:09:ed:5f:62:91:18:35:59:1c:6b:\n    36:c5:fe:b9:4a:27:85:34:f2:9d:33:2b:00:57:aa:\n    b6:fb:27:aa:34:85:50:18:dd:c9:36:ff:37:dd:08:\n    72:b8:2f:7d:29:9c:63:d4:1e:02:b6:e2:fd:51:c9:\n    7f:0b:6a:8e:29:4e:b6:01:54:16:c8:25:e1:3f:1e:\n    a8:34:3e:78:0b:0b:93:09:f1:53:b1:4e:39:db:18:\n    01\nprime1:\n    00:d2:bf:f7:f0:c3:89:a2:57:50:46:40:38:c2:bb:\n    13:2b:c6:da:de:f8:28:eb:ce:aa:46:24:65:71:a3:\n    e4:8e:54:53:e1:34:fb:50:e9:ec:31:49:fb:a5:3b:\n    60:c7:e8:57:25:28:69:0d:e2:04:72:38:95:9a:4c:\n    a4:af:b6:61:b9:3e:6e:2b:0b:ed:cd:b9:48:db:d4:\n    06:17:a1:7c:4f:c6:66:ce:e0:da:1e:f3:b9:67:c8:\n    0c:2a:3d:29:37:32:55:d1:98:95:58:00:fe:6e:6f:\n    fb:3c:35:29:2c:22:0a:35:3d:fd:22:7f:30:75:7a:\n    f2:ff:f2:a8:f9:e9:1e:5e:e1\nprime2:\n    00:c8:5b:b0:24:02:d7:b8:a9:b8:59:fd:52:a1:08:\n    e8:4d:1a:96:f3:98:6c:b8:cf:12:6b:5f:e9:f9:ff:\n    cf:90:bc:1d:53:7c:e6:ae:48:c2:0c:0d:3e:13:47:\n    a5:53:e1:14:18:4c:19:b1:73:f5:fc:af:ef:56:44:\n    fc:03:7f:82:1d:91:8f:b2:c8:70:51:55:2f:e3:48:\n    b8:a0:15:bf:e5:b4:3e:fb:4c:07:18:9d:cb:66:58:\n    72:dc:73:04:c9:0b:05:e6:1c:53:0a:be:70:c0:07:\n    d6:d7:46:68:0f:e4:ea:83:af:24:22:71:a0:5f:da:\n    f8:a2:cb:79:b5:f4:f8:51:c9\nexponent1:\n    4d:27:bc:a4:e7:61:67:0e:a2:33:e5:e5:21:dd:8d:\n    4a:34:96:02:95:45:9d:f6:de:3d:a9:ab:7d:ec:2b:\n    8b:b3:f1:b5:7c:49:19:b9:5a:20:3d:5e:a4:82:55:\n    8f:3a:4f:55:2a:33:33:c2:f0:2f:c4:e9:78:40:e0:\n    f5:f7:46:55:a4:36:7f:09:f1:f8:a4:01:b9:81:28:\n    ed:d3:d0:08:00:b1:b8:c1:76:f9:67:ef:13:c0:98:\n    3b:8a:82:a1:53:8b:39:9d:ab:e7:39:0d:ec:ee:90:\n    42:dd:8f:82:39:c0:14:f7:e9:c9:8c:58:f8:59:97:\n    c6:fe:56:c0:8f:88:ef:e1\nexponent2:\n    00:b8:09:f8:9e:eb:79:89:e7:64:2e:4d:12:24:57:\n    91:42:99:e5:04:b7:03:4f:32:ee:41:71:25:f3:fc:\n    f5:85:86:36:0d:e5:51:e3:cf:73:67:2c:96:d3:90:\n    e1:1d:4e:47:6e:16:21:17:ae:63:cb:0b:34:76:73:\n    01:66:99:2e:44:c8:db:4d:26:ee:7c:d7:1a:18:d5:\n    48:b9:cb:a0:ac:77:c7:ce:7f:44:99:69:00:57:ef:\n    70:fa:6c:30:7e:17:41:00:e1:0d:aa:75:ca:0d:aa:\n    65:be:f2:ae:4d:c3:41:63:5a:72:7f:ad:0d:da:5e:\n    c2:3a:8f:5b:2a:37:6e:0d:79\ncoefficient:\n    4d:10:99:0a:78:4e:61:57:ee:44:a2:2c:b2:25:d2:\n    7a:8c:3d:a8:06:e2:a2:4b:ce:d5:bf:0b:6c:1f:1e:\n    60:c0:49:5f:a8:c5:93:80:0a:0f:b3:fe:58:38:2b:\n    4e:a6:3e:47:42:8b:9b:e7:e1:f5:b9:69:37:df:07:\n    70:b1:ec:0a:57:d2:f5:5d:b0:8e:3b:84:db:25:6c:\n    1c:32:73:d4:84:df:d8:73:30:33:79:1f:61:b3:6d:\n    eb:8c:80:cb:37:48:a0:54:45:f9:54:64:04:04:73:\n    42:6e:e1:18:c7:c9:41:6a:d1:34:c2:0e:14:ae:a7:\n    36:9b:05:9e:20:0e:a0:3f\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApPF08tskhDnaJlxyyC6mPleIkKtBBHp686/3Q7eb2FeRfhXV\nqsuN1KXeVEoiu+NvGgN/J0KK20XueQ7dwAj880ie6pn5CM04hi3KLJvQAvKBKXKh\nqqPq/F9KoRVoVantfTojhAwK2BDHC4yzk5phVRBzNBQh3ihlv2rT3eg9nml4HnKJ\n/XN+deebTPJlPNXt1NFTllKwKcHknV/XYtCEe+jbhZO5j0oBxG5t4qlt7FHXUUj+\n9INFocG3AGicApKmINRAoMjuq5ZAUxDxtS2o+VD+65aanMExLB9w1ac3/zUarNf8\nnU/hZyDmnUpmyKzgCvVP1ENW1uFtBPTqAtWvqQIDAQABAoIBACaY+iz6awkmII5p\ng9KkV/KuuR5hdM1/e9Yaj1ohVS7DDyDaKy3UhFQGnIhM8OPWzODhgJfHUocNTki1\n2d5ePBMSI13yufv9SgTcIjteGscVyHPSh1JPGWYFRol7YPbq1NBBEp6L0t3Mz0c8\nnakkrzIgr1S4gVRjT6wDa6C5//FZjLZiehtw+ysWItnv2MBXHpb0+lRFcLZLYTG0\nTgJiurjzPu664ElbihOkCe1fYpEYNVkcazbF/rlKJ4U08p0zKwBXqrb7J6o0hVAY\n3ck2/zfdCHK4L30pnGPUHgK24v1RyX8Lao4pTrYBVBbIJeE/Hqg0PngLC5MJ8VOx\nTjnbGAECgYEA0r/38MOJoldQRkA4wrsTK8ba3vgo686qRiRlcaPkjlRT4TT7UOns\nMUn7pTtgx+hXJShpDeIEcjiVmkykr7ZhuT5uKwvtzblI29QGF6F8T8ZmzuDaHvO5\nZ8gMKj0pNzJV0ZiVWAD+bm/7PDUpLCIKNT39In8wdXry//Ko+ekeXuECgYEAyFuw\nJALXuKm4Wf1SoQjoTRqW85hsuM8Sa1/p+f/PkLwdU3zmrkjCDA0+E0elU+EUGEwZ\nsXP1/K/vVkT8A3+CHZGPsshwUVUv40i4oBW/5bQ++0wHGJ3LZlhy3HMEyQsF5hxT\nCr5wwAfW10ZoD+Tqg68kInGgX9r4ost5tfT4UckCgYBNJ7yk52FnDqIz5eUh3Y1K\nNJYClUWd9t49qat97CuLs/G1fEkZuVogPV6kglWPOk9VKjMzwvAvxOl4QOD190ZV\npDZ/CfH4pAG5gSjt09AIALG4wXb5Z+8TwJg7ioKhU4s5navnOQ3s7pBC3Y+COcAU\n9+nJjFj4WZfG/lbAj4jv4QKBgQC4Cfie63mJ52QuTRIkV5FCmeUEtwNPMu5BcSXz\n/PWFhjYN5VHjz3NnLJbTkOEdTkduFiEXrmPLCzR2cwFmmS5EyNtNJu581xoY1Ui5\ny6Csd8fOf0SZaQBX73D6bDB+F0EA4Q2qdcoNqmW+8q5Nw0FjWnJ/rQ3aXsI6j1sq\nN24NeQKBgE0QmQp4TmFX7kSiLLIl0nqMPagG4qJLztW/C2wfHmDASV+oxZOACg+z\n/lg4K06mPkdCi5vn4fW5aTffB3Cx7ApX0vVdsI47hNslbBwyc9SE39hzMDN5H2Gz\nbeuMgMs3SKBURflUZAQEc0Ju4RjHyUFq0TTCDhSupzabBZ4gDqA/\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/intermediate.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 3 (0x3)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=Test CA Subauthority\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:a4:f1:74:f2:db:24:84:39:da:26:5c:72:c8:2e:\n                    a6:3e:57:88:90:ab:41:04:7a:7a:f3:af:f7:43:b7:\n                    9b:d8:57:91:7e:15:d5:aa:cb:8d:d4:a5:de:54:4a:\n                    22:bb:e3:6f:1a:03:7f:27:42:8a:db:45:ee:79:0e:\n                    dd:c0:08:fc:f3:48:9e:ea:99:f9:08:cd:38:86:2d:\n                    ca:2c:9b:d0:02:f2:81:29:72:a1:aa:a3:ea:fc:5f:\n                    4a:a1:15:68:55:a9:ed:7d:3a:23:84:0c:0a:d8:10:\n                    c7:0b:8c:b3:93:9a:61:55:10:73:34:14:21:de:28:\n                    65:bf:6a:d3:dd:e8:3d:9e:69:78:1e:72:89:fd:73:\n                    7e:75:e7:9b:4c:f2:65:3c:d5:ed:d4:d1:53:96:52:\n                    b0:29:c1:e4:9d:5f:d7:62:d0:84:7b:e8:db:85:93:\n                    b9:8f:4a:01:c4:6e:6d:e2:a9:6d:ec:51:d7:51:48:\n                    fe:f4:83:45:a1:c1:b7:00:68:9c:02:92:a6:20:d4:\n                    40:a0:c8:ee:ab:96:40:53:10:f1:b5:2d:a8:f9:50:\n                    fe:eb:96:9a:9c:c1:31:2c:1f:70:d5:a7:37:ff:35:\n                    1a:ac:d7:fc:9d:4f:e1:67:20:e6:9d:4a:66:c8:ac:\n                    e0:0a:f5:4f:d4:43:56:d6:e1:6d:04:f4:ea:02:d5:\n                    af:a9\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Subject Key Identifier: \n                EB:EA:7B:15:C9:4E:08:5B:87:27:09:E4:42:10:54:47:72:E9:38:29\n            Netscape Comment: \n                Puppet Server Internal Certificate\n            X509v3 Authority Key Identifier: \n                keyid:58:D3:6E:35:36:97:04:91:6A:B6:DF:45:80:9A:53:B3:E4:36:C8:3D\n\n    Signature Algorithm: sha256WithRSAEncryption\n         b1:df:5a:e5:7f:c9:d7:55:9f:a5:45:88:46:22:50:89:d2:0d:\n         b4:fb:ee:5c:93:d5:f4:3e:c4:da:af:63:c5:0f:1a:43:b6:7c:\n         6d:17:5e:ba:99:6b:ce:fe:e8:67:60:55:d4:fd:55:05:6c:fc:\n         d3:1f:9c:9f:45:e3:f6:43:52:0b:11:b0:fa:d7:58:a7:79:15:\n         a0:79:7b:e7:2b:59:49:5a:7f:84:73:34:98:d6:88:5a:e0:f3:\n         99:38:69:9b:eb:ea:4f:9e:43:91:c9:d9:c6:87:7f:a9:3e:96:\n         04:dd:75:04:30:ed:09:7e:da:bf:e9:a9:98:bf:a5:d1:71:c3:\n         de:cd:03:4f:0d:7d:18:ee:49:0f:d7:7b:b6:4d:da:3c:9e:e1:\n         d3:7b:d5:26:f7:b3:dc:af:c6:2c:5a:9f:f3:9f:ca:ef:23:7a:\n         ab:82:c3:b4:1a:2b:75:6a:9c:9f:5c:85:f8:18:a4:e0:b7:7d:\n         fd:12:a8:48:e6:b4:a9:5e:e9:12:06:56:47:24:7b:8a:30:62:\n         96:6d:27:f6:f3:0b:6c:1e:da:85:49:a3:2c:5b:45:8b:fe:17:\n         3b:bc:eb:54:57:23:15:b7:14:40:a0:72:4a:7f:f2:4c:f4:46:\n         96:93:d5:42:c5:e4:66:d8:99:9e:84:5a:03:ff:cd:6a:88:4a:\n         c8:40:b2:35\n-----BEGIN CERTIFICATE-----\nMIIDRDCCAiygAwIBAgIBAzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0\nIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowHzEdMBsGA1UEAwwU\nVGVzdCBDQSBTdWJhdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQCk8XTy2ySEOdomXHLILqY+V4iQq0EEenrzr/dDt5vYV5F+FdWqy43Upd5U\nSiK7428aA38nQorbRe55Dt3ACPzzSJ7qmfkIzTiGLcosm9AC8oEpcqGqo+r8X0qh\nFWhVqe19OiOEDArYEMcLjLOTmmFVEHM0FCHeKGW/atPd6D2eaXgecon9c35155tM\n8mU81e3U0VOWUrApweSdX9di0IR76NuFk7mPSgHEbm3iqW3sUddRSP70g0WhwbcA\naJwCkqYg1ECgyO6rlkBTEPG1Laj5UP7rlpqcwTEsH3DVpzf/NRqs1/ydT+FnIOad\nSmbIrOAK9U/UQ1bW4W0E9OoC1a+pAgMBAAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB\n/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFOvqexXJTghbhycJ5EIQVEdy6Tgp\nMDEGCWCGSAGG+EIBDQQkFiJQdXBwZXQgU2VydmVyIEludGVybmFsIENlcnRpZmlj\nYXRlMB8GA1UdIwQYMBaAFFjTbjU2lwSRarbfRYCaU7PkNsg9MA0GCSqGSIb3DQEB\nCwUAA4IBAQCx31rlf8nXVZ+lRYhGIlCJ0g20++5ck9X0PsTar2PFDxpDtnxtF166\nmWvO/uhnYFXU/VUFbPzTH5yfReP2Q1ILEbD611ineRWgeXvnK1lJWn+EczSY1oha\n4POZOGmb6+pPnkORydnGh3+pPpYE3XUEMO0Jftq/6amYv6XRccPezQNPDX0Y7kkP\n13u2Tdo8nuHTe9Um97Pcr8YsWp/zn8rvI3qrgsO0Git1apyfXIX4GKTgt339EqhI\n5rSpXukSBlZHJHuKMGKWbSf28wtsHtqFSaMsW0WL/hc7vOtUVyMVtxRAoHJKf/JM\n9EaWk9VCxeRm2JmehFoD/81qiErIQLI1\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/netlock-arany-utf8.pem",
    "content": "NetLock Arany (Class Gold) Főtanúsítvány\n========================================\n-----BEGIN CERTIFICATE-----\nMIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G\nA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610\ndsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB\ncmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx\nMjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO\nZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv\nbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6\nc8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu\n0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw\n/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk\nH3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw\nfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1\nneWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB\nBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW\nqZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta\nYtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC\nbLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna\nNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu\ndZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/oid-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:d3:1e:8b:39:52:df:37:6e:2f:44:44:b9:55:2e:\n    1d:44:1f:dd:41:b3:ce:a4:fd:f3:5a:41:f6:9a:37:\n    05:a7:2c:34:ec:46:2f:89:9e:ff:9e:20:75:05:35:\n    68:68:45:d2:f5:15:0d:f7:9c:78:a7:69:a9:f6:3d:\n    4b:5d:ab:0d:a7:a7:75:a2:c8:d7:09:be:53:3c:8b:\n    75:95:0e:e3:f3:80:dc:26:6d:98:c5:48:fa:1b:68:\n    51:ec:47:44:72:5a:83:32:a9:65:8d:40:12:6d:46:\n    59:05:e7:cd:55:c1:67:7f:81:da:3d:43:04:d3:58:\n    09:c2:27:0a:52:84:01:a1:01:dd:ab:34:3b:a7:90:\n    a5:52:8d:d9:86:fc:d9:c1:9b:08:f0:67:c7:7e:a8:\n    61:de:fe:57:af:dc:58:8c:8e:eb:27:2a:b9:a2:34:\n    d4:31:33:87:c7:2e:69:e7:26:75:cd:bd:bb:61:00:\n    3b:d2:73:54:9f:b0:f6:78:5e:46:51:37:35:65:a1:\n    10:0e:97:7b:81:dc:9f:f2:1a:c7:9f:80:4d:e6:72:\n    e8:92:ee:3f:81:61:c5:a1:43:43:0b:1a:03:c4:0e:\n    bc:71:d0:30:8f:95:f7:9e:de:86:5e:4d:18:ab:5e:\n    8e:14:3e:f1:c5:98:78:11:65:4f:3c:7f:c3:d9:5e:\n    89:5b\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    40:3c:c9:f5:fe:3e:47:1a:72:ad:5e:0f:da:58:36:\n    b5:74:d0:98:de:42:21:37:d8:24:fd:6f:30:8b:4f:\n    60:07:76:6e:f2:08:22:e2:cb:3a:6c:fb:ce:42:ea:\n    ea:d7:5c:12:10:7e:f8:79:96:c8:b1:c0:f2:58:c2:\n    26:d5:16:49:8e:0d:a0:23:66:32:e3:c3:65:e7:b5:\n    36:4e:9a:93:8c:00:f0:7c:66:80:98:ec:e9:0b:fb:\n    e9:5d:e1:f8:bd:8b:b0:0d:95:05:be:9a:1c:bf:cb:\n    f4:9d:0c:80:6c:61:b6:8d:67:3f:3e:b6:28:2c:60:\n    83:bf:e7:47:26:3d:6c:a4:9b:ac:89:50:42:c5:95:\n    53:b0:6f:ef:79:87:28:6c:12:c0:ea:1b:db:0a:5b:\n    1a:3e:24:e9:62:b3:c3:7b:9a:bd:3f:85:48:39:53:\n    c5:d1:21:33:55:39:85:70:21:30:52:5f:0d:69:d5:\n    9f:cf:9f:ca:f5:46:11:d4:84:92:62:d6:1d:8f:68:\n    3f:9a:27:d0:3d:82:c3:ff:06:84:6e:75:49:46:da:\n    60:61:eb:13:17:df:01:db:9a:a1:01:2b:82:2b:3d:\n    a5:bb:e9:01:8c:6f:0d:cc:82:67:71:e1:fe:4a:13:\n    6a:0e:f3:85:87:5a:05:e6:9d:0e:97:2d:7e:6d:7b:\n    89\nprime1:\n    00:fd:e9:b3:2e:6a:ce:87:8f:75:01:a1:a7:45:36:\n    42:83:99:f3:27:e5:86:83:ac:8d:5c:fd:5b:14:f8:\n    cf:02:c9:db:8e:a7:8e:c3:70:c4:cd:48:55:0f:21:\n    17:0e:3b:69:34:e0:5c:e6:dd:63:ae:ab:2c:a1:8a:\n    6b:a3:63:25:17:35:a4:67:d3:09:3f:0c:90:da:61:\n    96:3d:33:b2:f2:97:c0:62:2f:3b:f5:eb:cf:a2:1b:\n    7e:54:da:ab:36:d3:35:63:e6:6a:2c:4a:4a:71:b8:\n    df:3e:59:fd:d7:80:dd:31:ee:c6:16:7f:8d:a0:6b:\n    24:4b:c6:a2:e9:dc:d6:e0:dd\nprime2:\n    00:d4:da:cb:7d:00:b4:67:75:f8:5b:02:99:44:27:\n    7a:1d:84:9c:06:9b:7a:99:fb:53:3e:aa:c1:7d:6c:\n    16:b6:ea:36:e2:79:5d:0e:76:f6:6f:fa:82:c3:d7:\n    d8:59:05:3f:63:b3:a8:3c:46:4b:c5:e9:f9:e6:8f:\n    9b:8a:06:95:fb:6a:da:ce:5f:b3:ad:cc:76:02:1d:\n    3f:c9:8d:48:9d:e0:0b:c3:f4:f4:02:ef:f3:3e:3d:\n    db:43:17:4f:ca:5e:6f:b1:4b:05:b2:8d:3e:e2:01:\n    d5:d6:8b:21:15:7d:48:74:16:58:29:f3:64:cd:98:\n    19:34:2d:70:f6:00:f5:93:97\nexponent1:\n    00:fc:86:9f:c0:eb:52:aa:39:bf:b9:b6:a7:20:4b:\n    9d:8d:3a:e0:3c:ee:a6:80:70:6d:4b:6d:62:57:92:\n    38:77:e2:80:2c:f9:72:1f:e8:18:a2:bd:6c:73:16:\n    8a:d2:89:bd:d1:6b:ce:99:80:d7:71:d1:26:1b:27:\n    0a:74:3d:d7:96:a4:af:f4:de:4b:14:dc:8f:77:d1:\n    94:55:1c:04:be:06:b4:bc:31:93:e5:b9:f8:0f:96:\n    5e:67:4f:ee:f6:20:ca:b9:a7:60:32:da:53:fd:94:\n    4a:da:bd:2f:9b:53:bb:29:bc:fa:5f:47:ce:78:1b:\n    f6:c6:32:30:b6:7b:ff:6d:35\nexponent2:\n    7b:41:8f:5f:fd:4e:7a:32:c7:f3:fb:97:40:ee:13:\n    3a:90:95:c1:05:bb:82:fb:14:03:4f:e1:e7:7c:f5:\n    d2:49:e2:2a:56:d8:da:0d:6d:3a:fe:b7:46:2c:f1:\n    2c:eb:6a:ff:93:03:32:94:0a:40:ba:f2:68:de:73:\n    d5:03:67:4a:45:60:1a:ed:34:20:ca:2a:f3:a2:78:\n    53:80:2a:b2:b1:10:5f:12:b3:52:18:1c:a4:c8:30:\n    f5:b2:c4:13:8d:87:e4:04:6b:72:2f:74:60:6f:6b:\n    a2:66:c2:6b:ad:36:8d:72:7a:ef:1f:f0:19:55:a0:\n    12:a4:d5:aa:be:77:76:55\ncoefficient:\n    0f:15:97:5d:19:46:2b:fc:22:c8:5c:83:f2:57:5f:\n    33:d4:7d:57:88:dc:75:73:54:53:83:c9:a9:b9:ed:\n    9d:d0:f9:8a:2e:68:3b:ac:a3:b0:05:44:8d:8c:00:\n    3c:20:35:6c:93:ab:ed:22:05:8c:65:66:83:c1:a3:\n    56:94:34:85:ce:d8:79:05:5e:45:51:7f:74:8e:04:\n    45:23:bc:35:38:bc:cd:80:f1:1d:09:4e:e5:20:59:\n    44:59:b6:f5:b7:00:8a:79:6d:1b:c8:9b:93:a2:22:\n    2d:f4:79:f9:42:77:cd:db:a9:4b:c1:ff:89:9c:64:\n    7a:bd:73:66:86:11:70:d7\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA0x6LOVLfN24vRES5VS4dRB/dQbPOpP3zWkH2mjcFpyw07EYv\niZ7/niB1BTVoaEXS9RUN95x4p2mp9j1LXasNp6d1osjXCb5TPIt1lQ7j84DcJm2Y\nxUj6G2hR7EdEclqDMqlljUASbUZZBefNVcFnf4HaPUME01gJwicKUoQBoQHdqzQ7\np5ClUo3ZhvzZwZsI8GfHfqhh3v5Xr9xYjI7rJyq5ojTUMTOHxy5p5yZ1zb27YQA7\n0nNUn7D2eF5GUTc1ZaEQDpd7gdyf8hrHn4BN5nLoku4/gWHFoUNDCxoDxA68cdAw\nj5X3nt6GXk0Yq16OFD7xxZh4EWVPPH/D2V6JWwIDAQABAoIBAEA8yfX+Pkcacq1e\nD9pYNrV00JjeQiE32CT9bzCLT2AHdm7yCCLiyzps+85C6urXXBIQfvh5lsixwPJY\nwibVFkmODaAjZjLjw2XntTZOmpOMAPB8ZoCY7OkL++ld4fi9i7ANlQW+mhy/y/Sd\nDIBsYbaNZz8+tigsYIO/50cmPWykm6yJUELFlVOwb+95hyhsEsDqG9sKWxo+JOli\ns8N7mr0/hUg5U8XRITNVOYVwITBSXw1p1Z/Pn8r1RhHUhJJi1h2PaD+aJ9A9gsP/\nBoRudUlG2mBh6xMX3wHbmqEBK4IrPaW76QGMbw3Mgmdx4f5KE2oO84WHWgXmnQ6X\nLX5te4kCgYEA/emzLmrOh491AaGnRTZCg5nzJ+WGg6yNXP1bFPjPAsnbjqeOw3DE\nzUhVDyEXDjtpNOBc5t1jrqssoYpro2MlFzWkZ9MJPwyQ2mGWPTOy8pfAYi879evP\noht+VNqrNtM1Y+ZqLEpKcbjfPln914DdMe7GFn+NoGskS8ai6dzW4N0CgYEA1NrL\nfQC0Z3X4WwKZRCd6HYScBpt6mftTPqrBfWwWtuo24nldDnb2b/qCw9fYWQU/Y7Oo\nPEZLxen55o+bigaV+2razl+zrcx2Ah0/yY1IneALw/T0Au/zPj3bQxdPyl5vsUsF\nso0+4gHV1oshFX1IdBZYKfNkzZgZNC1w9gD1k5cCgYEA/IafwOtSqjm/ubanIEud\njTrgPO6mgHBtS21iV5I4d+KALPlyH+gYor1scxaK0om90WvOmYDXcdEmGycKdD3X\nlqSv9N5LFNyPd9GUVRwEvga0vDGT5bn4D5ZeZ0/u9iDKuadgMtpT/ZRK2r0vm1O7\nKbz6X0fOeBv2xjIwtnv/bTUCgYB7QY9f/U56Msfz+5dA7hM6kJXBBbuC+xQDT+Hn\nfPXSSeIqVtjaDW06/rdGLPEs62r/kwMylApAuvJo3nPVA2dKRWAa7TQgyirzonhT\ngCqysRBfErNSGBykyDD1ssQTjYfkBGtyL3Rgb2uiZsJrrTaNcnrvH/AZVaASpNWq\nvnd2VQKBgA8Vl10ZRiv8Ishcg/JXXzPUfVeI3HVzVFODyam57Z3Q+YouaDuso7AF\nRI2MADwgNWyTq+0iBYxlZoPBo1aUNIXO2HkFXkVRf3SOBEUjvDU4vM2A8R0JTuUg\nWURZtvW3AIp5bRvIm5OiIi30eflCd83bqUvB/4mcZHq9c2aGEXDX\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/oid.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 7 (0x7)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=oid\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:d3:1e:8b:39:52:df:37:6e:2f:44:44:b9:55:2e:\n                    1d:44:1f:dd:41:b3:ce:a4:fd:f3:5a:41:f6:9a:37:\n                    05:a7:2c:34:ec:46:2f:89:9e:ff:9e:20:75:05:35:\n                    68:68:45:d2:f5:15:0d:f7:9c:78:a7:69:a9:f6:3d:\n                    4b:5d:ab:0d:a7:a7:75:a2:c8:d7:09:be:53:3c:8b:\n                    75:95:0e:e3:f3:80:dc:26:6d:98:c5:48:fa:1b:68:\n                    51:ec:47:44:72:5a:83:32:a9:65:8d:40:12:6d:46:\n                    59:05:e7:cd:55:c1:67:7f:81:da:3d:43:04:d3:58:\n                    09:c2:27:0a:52:84:01:a1:01:dd:ab:34:3b:a7:90:\n                    a5:52:8d:d9:86:fc:d9:c1:9b:08:f0:67:c7:7e:a8:\n                    61:de:fe:57:af:dc:58:8c:8e:eb:27:2a:b9:a2:34:\n                    d4:31:33:87:c7:2e:69:e7:26:75:cd:bd:bb:61:00:\n                    3b:d2:73:54:9f:b0:f6:78:5e:46:51:37:35:65:a1:\n                    10:0e:97:7b:81:dc:9f:f2:1a:c7:9f:80:4d:e6:72:\n                    e8:92:ee:3f:81:61:c5:a1:43:43:0b:1a:03:c4:0e:\n                    bc:71:d0:30:8f:95:f7:9e:de:86:5e:4d:18:ab:5e:\n                    8e:14:3e:f1:c5:98:78:11:65:4f:3c:7f:c3:d9:5e:\n                    89:5b\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            1.3.6.1.4.1.34380.1.2.1.1: \n                ..somevalue\n    Signature Algorithm: sha256WithRSAEncryption\n         68:29:84:a9:fc:d4:b9:72:ab:96:be:a9:e0:cc:4f:e1:f1:e6:\n         0e:46:69:e1:99:da:98:b7:ff:58:2f:45:5d:e7:de:79:39:42:\n         b0:ba:ab:6e:1f:9c:a8:a8:eb:3e:7b:78:c1:cc:2f:82:df:50:\n         73:01:85:5c:e6:e8:72:11:22:8a:8f:11:6c:23:e0:07:b2:e5:\n         a6:a3:99:e0:68:7e:01:3e:fc:4c:3e:cd:13:ec:56:00:74:29:\n         9f:23:ba:ba:98:b0:dd:4a:c6:26:6e:e0:58:8c:4b:b7:e0:a5:\n         bc:1c:b5:41:8b:c8:33:d4:1b:10:64:d9:2c:ba:57:d9:32:50:\n         ea:b7:1b:90:8a:b0:04:e4:2c:c5:f1:a1:2f:fc:22:d7:9f:b1:\n         5c:06:fb:b0:ee:34:53:03:a2:25:bc:57:86:1e:29:ef:4c:ca:\n         5b:08:3c:9c:6e:19:38:5e:4e:4c:fe:97:69:de:11:5a:28:37:\n         5d:1c:e9:b6:21:78:33:16:12:7e:72:f5:e4:22:da:6e:aa:bd:\n         4d:ca:e7:f7:13:f4:fc:e3:55:ae:e0:57:c8:ac:a7:2e:5e:dd:\n         81:70:68:1b:68:8e:47:a5:bf:84:59:ed:4a:85:8a:07:ce:cc:\n         a5:91:b7:59:64:d8:5c:26:19:bb:51:fe:87:d8:e6:36:9d:2c:\n         28:2a:df:c9\n-----BEGIN CERTIFICATE-----\nMIICxzCCAa+gAwIBAgIBBzANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\nIENBIFN1YmF1dGhvcml0eTAeFw03MDAxMDEwMDAwMDBaFw0zNDAxMjcxODQzMzJa\nMA4xDDAKBgNVBAMMA29pZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANMeizlS3zduL0REuVUuHUQf3UGzzqT981pB9po3BacsNOxGL4me/54gdQU1aGhF\n0vUVDfeceKdpqfY9S12rDaendaLI1wm+UzyLdZUO4/OA3CZtmMVI+htoUexHRHJa\ngzKpZY1AEm1GWQXnzVXBZ3+B2j1DBNNYCcInClKEAaEB3as0O6eQpVKN2Yb82cGb\nCPBnx36oYd7+V6/cWIyO6ycquaI01DEzh8cuaecmdc29u2EAO9JzVJ+w9nheRlE3\nNWWhEA6Xe4Hcn/Iax5+ATeZy6JLuP4FhxaFDQwsaA8QOvHHQMI+V957ehl5NGKte\njhQ+8cWYeBFlTzx/w9leiVsCAwEAAaMfMB0wGwYMKwYBBAGCjEwBAgEBBAsMCXNv\nbWV2YWx1ZTANBgkqhkiG9w0BAQsFAAOCAQEAaCmEqfzUuXKrlr6p4MxP4fHmDkZp\n4ZnamLf/WC9FXefeeTlCsLqrbh+cqKjrPnt4wcwvgt9QcwGFXObochEiio8RbCPg\nB7LlpqOZ4Gh+AT78TD7NE+xWAHQpnyO6upiw3UrGJm7gWIxLt+ClvBy1QYvIM9Qb\nEGTZLLpX2TJQ6rcbkIqwBOQsxfGhL/wi15+xXAb7sO40UwOiJbxXhh4p70zKWwg8\nnG4ZOF5OTP6Xad4RWig3XRzptiF4MxYSfnL15CLabqq9Tcrn9xP0/ONVruBXyKyn\nLl7dgXBoG2iOR6W/hFntSoWKB87MpZG3WWTYXCYZu1H+h9jmNp0sKCrfyQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/pluto-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:cb:df:c1:d7:96:85:2d:3b:83:ec:a0:78:6d:5e:\n    ed:d5:bc:c9:41:96:37:f4:c1:5d:da:c4:97:38:b9:\n    b8:c7:0f:69:2b:c7:8c:83:74:9a:44:1c:93:0d:2b:\n    7d:c6:09:f1:6d:87:ac:45:3a:88:bf:b1:f5:5a:07:\n    d1:e6:77:a2:d5:e1:4a:06:e3:2e:34:66:0d:ef:c5:\n    0a:2e:c9:a4:81:d5:2b:8a:07:d3:2b:4f:21:4c:16:\n    6d:bb:b7:ed:58:9f:0a:b5:5b:46:61:0d:e6:8e:f8:\n    91:be:6f:7f:c0:52:0c:48:a5:e7:93:5b:e1:be:8b:\n    83:c5:91:49:2d:b6:de:7c:dc:9d:08:b3:77:0c:84:\n    8e:d8:c4:20:c7:2c:f5:88:11:35:7a:1f:bd:f4:c0:\n    bb:37:e4:35:3a:aa:63:bf:b6:8d:20:6a:2c:c3:aa:\n    79:e5:02:76:d1:65:f7:43:0d:d4:34:74:2d:75:8e:\n    b4:d0:a3:51:e7:75:ee:a8:80:21:5f:7e:d8:fd:c8:\n    f6:a7:35:dd:cb:d5:9f:06:f1:3d:e2:60:8f:cb:04:\n    5b:73:a1:2f:a4:a0:b5:80:56:11:73:7d:ac:75:19:\n    50:e7:1f:08:bc:6f:30:37:d0:49:59:9f:a5:8d:9b:\n    59:85:f9:d8:99:fc:68:54:e5:7b:54:c9:b5:b1:88:\n    b3:b1\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    55:0d:93:63:ea:92:c2:cd:1a:7b:84:72:d9:46:0a:\n    38:ea:1a:98:37:20:3f:06:d0:ad:ec:e8:1d:ad:35:\n    8e:2e:3a:b5:1d:f9:db:f9:b0:46:0c:19:b9:1b:d4:\n    cb:e2:e8:0a:c6:d8:32:c5:79:b9:67:77:b1:48:a3:\n    e3:df:33:5a:c1:33:4d:ee:47:1f:6b:18:13:58:83:\n    a8:03:4d:93:30:a8:8a:5c:3b:57:cd:1a:5a:10:dd:\n    54:bc:25:d3:9b:fe:2e:ac:d6:8e:b4:ea:e3:e8:85:\n    7d:0a:2d:24:c1:d6:08:14:d6:a8:9e:63:6e:47:ec:\n    00:67:5d:3d:2a:16:65:c6:30:cb:66:4f:73:a3:15:\n    07:ec:bc:09:d0:0f:ad:b2:76:f7:b3:4b:7a:37:6e:\n    99:1e:09:de:a7:b8:54:b5:e4:90:17:67:5f:ad:b8:\n    07:98:7a:0f:b6:20:bb:09:28:67:b4:ad:16:df:b6:\n    b3:a9:6e:fc:8e:a4:7d:23:ae:cc:b9:3c:7e:18:f0:\n    eb:dd:55:51:85:0c:5f:51:bd:09:f2:9c:0e:47:d3:\n    48:53:46:42:90:d8:10:fe:76:81:5c:34:70:f6:b9:\n    ef:ac:83:6a:9c:b4:e2:4a:8a:3c:e1:4a:ee:5c:d8:\n    df:54:46:cd:bf:8d:de:48:1f:80:d9:5d:a3:40:39:\n    b1\nprime1:\n    00:f5:91:c5:e8:9c:ea:3f:3f:be:2a:9f:65:9d:93:\n    18:d8:53:a0:93:90:7a:79:92:78:fd:25:53:94:ed:\n    9e:bd:2f:07:1d:ee:77:8e:6b:3c:33:fa:47:9b:89:\n    97:43:38:7b:68:5e:ab:0a:e8:74:f1:e1:6c:4b:2b:\n    06:23:b4:c1:4f:66:5e:6c:4a:08:48:74:fc:5f:07:\n    3f:2f:02:3d:0c:e2:ce:96:ee:9c:38:42:a2:a7:ef:\n    f5:d3:9b:d5:98:73:26:cb:07:2c:e2:61:9e:bf:79:\n    a4:3f:46:81:53:4b:5d:80:11:d6:24:6b:36:ad:e7:\n    99:86:aa:bb:97:68:e7:36:f5\nprime2:\n    00:d4:88:9a:d0:a0:06:3e:74:20:00:43:36:46:45:\n    a4:74:29:e4:a4:80:8a:f9:92:7f:c7:e5:62:6d:83:\n    94:8b:f0:1d:25:d1:9a:41:50:97:1a:0a:ff:9a:f6:\n    50:d8:dd:f6:3b:ff:4b:bb:07:5e:69:67:d7:0c:76:\n    9c:96:ad:50:9f:54:6b:10:8c:da:6d:44:19:9a:be:\n    67:9b:94:67:21:ee:e3:1a:93:5b:d0:6c:84:04:20:\n    9a:26:01:5a:8d:70:0e:ad:da:1e:25:93:90:47:67:\n    7b:b9:8e:6d:5b:ac:09:ed:9a:07:f3:32:6e:17:8e:\n    1d:69:c2:d4:db:8b:d4:fc:4d\nexponent1:\n    74:69:b2:84:5f:3e:bc:d5:1e:f5:5d:b2:f2:4c:35:\n    4e:f7:f5:fb:7e:56:51:23:9a:af:86:ee:64:7f:70:\n    ed:06:4a:3d:6a:3a:cb:0d:12:f1:21:08:a9:37:44:\n    b9:dd:20:c3:8d:7d:50:22:7a:ad:df:cb:52:a7:06:\n    b6:0e:8e:45:71:a5:f9:77:ef:0d:4a:48:09:54:8a:\n    23:62:d0:46:37:8e:f0:06:15:90:a6:26:2d:ae:97:\n    be:c4:f5:30:dc:05:db:e1:7e:e9:a2:95:7a:f7:d3:\n    61:1b:af:5b:29:33:1d:ef:56:b0:d5:12:8a:c6:6c:\n    05:67:12:9e:e8:60:13:b1\nexponent2:\n    64:f9:d6:1c:22:bb:74:b6:b5:71:8a:7d:61:db:ba:\n    ab:9a:2f:d0:5c:66:2a:f7:08:90:cf:91:f9:18:17:\n    de:78:a1:50:93:8a:27:58:03:c2:52:d1:68:2d:78:\n    f1:e0:5b:19:dc:4c:7d:ba:9d:87:94:d7:5e:4b:88:\n    50:2a:5d:f0:a5:2a:0c:fd:ee:d5:15:12:d9:2b:77:\n    8c:3a:70:d9:75:67:1e:fc:3e:16:03:48:a1:b1:5f:\n    ab:88:df:1d:ed:cc:7b:9a:f2:d1:c5:8f:39:ed:97:\n    a1:ec:62:56:b8:a1:2a:eb:10:b5:e2:12:de:4c:1d:\n    fb:5e:9e:c5:04:65:97:cd\ncoefficient:\n    00:e3:fb:7e:20:c6:a0:93:15:c9:ec:d1:87:94:71:\n    ba:09:8d:11:60:49:65:0a:98:ac:a3:08:53:cc:6d:\n    b2:57:51:89:1c:96:7b:ba:d3:24:60:ee:d1:90:88:\n    53:b9:58:34:e4:3d:3c:0f:b7:0e:91:c8:6d:2e:61:\n    2c:c0:0b:35:cb:4b:a4:4a:d9:b2:b1:3d:5d:d4:86:\n    52:db:f7:1d:6a:cd:e9:65:81:bc:df:76:88:b2:96:\n    2c:24:0e:70:41:38:31:fc:46:66:9a:f6:75:0f:fc:\n    8d:c3:59:b3:76:6e:ee:ed:a6:d8:16:df:48:dd:5a:\n    26:8a:a7:6a:39:c6:dc:c8:5b\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAy9/B15aFLTuD7KB4bV7t1bzJQZY39MFd2sSXOLm4xw9pK8eM\ng3SaRByTDSt9xgnxbYesRTqIv7H1WgfR5nei1eFKBuMuNGYN78UKLsmkgdUrigfT\nK08hTBZtu7ftWJ8KtVtGYQ3mjviRvm9/wFIMSKXnk1vhvouDxZFJLbbefNydCLN3\nDISO2MQgxyz1iBE1eh+99MC7N+Q1Oqpjv7aNIGosw6p55QJ20WX3Qw3UNHQtdY60\n0KNR53XuqIAhX37Y/cj2pzXdy9WfBvE94mCPywRbc6EvpKC1gFYRc32sdRlQ5x8I\nvG8wN9BJWZ+ljZtZhfnYmfxoVOV7VMm1sYizsQIDAQABAoIBAFUNk2PqksLNGnuE\nctlGCjjqGpg3ID8G0K3s6B2tNY4uOrUd+dv5sEYMGbkb1Mvi6ArG2DLFeblnd7FI\no+PfM1rBM03uRx9rGBNYg6gDTZMwqIpcO1fNGloQ3VS8JdOb/i6s1o606uPohX0K\nLSTB1ggU1qieY25H7ABnXT0qFmXGMMtmT3OjFQfsvAnQD62ydvezS3o3bpkeCd6n\nuFS15JAXZ1+tuAeYeg+2ILsJKGe0rRbftrOpbvyOpH0jrsy5PH4Y8OvdVVGFDF9R\nvQnynA5H00hTRkKQ2BD+doFcNHD2ue+sg2qctOJKijzhSu5c2N9URs2/jd5IH4DZ\nXaNAObECgYEA9ZHF6JzqPz++Kp9lnZMY2FOgk5B6eZJ4/SVTlO2evS8HHe53jms8\nM/pHm4mXQzh7aF6rCuh08eFsSysGI7TBT2ZebEoISHT8Xwc/LwI9DOLOlu6cOEKi\np+/105vVmHMmywcs4mGev3mkP0aBU0tdgBHWJGs2reeZhqq7l2jnNvUCgYEA1Iia\n0KAGPnQgAEM2RkWkdCnkpICK+ZJ/x+VibYOUi/AdJdGaQVCXGgr/mvZQ2N32O/9L\nuwdeaWfXDHaclq1Qn1RrEIzabUQZmr5nm5RnIe7jGpNb0GyEBCCaJgFajXAOrdoe\nJZOQR2d7uY5tW6wJ7ZoH8zJuF44dacLU24vU/E0CgYB0abKEXz681R71XbLyTDVO\n9/X7flZRI5qvhu5kf3DtBko9ajrLDRLxIQipN0S53SDDjX1QInqt38tSpwa2Do5F\ncaX5d+8NSkgJVIojYtBGN47wBhWQpiYtrpe+xPUw3AXb4X7popV699NhG69bKTMd\n71aw1RKKxmwFZxKe6GATsQKBgGT51hwiu3S2tXGKfWHbuquaL9BcZir3CJDPkfkY\nF954oVCTiidYA8JS0WgtePHgWxncTH26nYeU115LiFAqXfClKgz97tUVEtkrd4w6\ncNl1Zx78PhYDSKGxX6uI3x3tzHua8tHFjzntl6HsYla4oSrrELXiEt5MHftensUE\nZZfNAoGBAOP7fiDGoJMVyezRh5RxugmNEWBJZQqYrKMIU8xtsldRiRyWe7rTJGDu\n0ZCIU7lYNOQ9PA+3DpHIbS5hLMALNctLpErZsrE9XdSGUtv3HWrN6WWBvN92iLKW\nLCQOcEE4MfxGZpr2dQ/8jcNZs3Zu7u2m2BbfSN1aJoqnajnG3Mhb\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/pluto.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 11 (0xb)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Agent Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=pluto\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:cb:df:c1:d7:96:85:2d:3b:83:ec:a0:78:6d:5e:\n                    ed:d5:bc:c9:41:96:37:f4:c1:5d:da:c4:97:38:b9:\n                    b8:c7:0f:69:2b:c7:8c:83:74:9a:44:1c:93:0d:2b:\n                    7d:c6:09:f1:6d:87:ac:45:3a:88:bf:b1:f5:5a:07:\n                    d1:e6:77:a2:d5:e1:4a:06:e3:2e:34:66:0d:ef:c5:\n                    0a:2e:c9:a4:81:d5:2b:8a:07:d3:2b:4f:21:4c:16:\n                    6d:bb:b7:ed:58:9f:0a:b5:5b:46:61:0d:e6:8e:f8:\n                    91:be:6f:7f:c0:52:0c:48:a5:e7:93:5b:e1:be:8b:\n                    83:c5:91:49:2d:b6:de:7c:dc:9d:08:b3:77:0c:84:\n                    8e:d8:c4:20:c7:2c:f5:88:11:35:7a:1f:bd:f4:c0:\n                    bb:37:e4:35:3a:aa:63:bf:b6:8d:20:6a:2c:c3:aa:\n                    79:e5:02:76:d1:65:f7:43:0d:d4:34:74:2d:75:8e:\n                    b4:d0:a3:51:e7:75:ee:a8:80:21:5f:7e:d8:fd:c8:\n                    f6:a7:35:dd:cb:d5:9f:06:f1:3d:e2:60:8f:cb:04:\n                    5b:73:a1:2f:a4:a0:b5:80:56:11:73:7d:ac:75:19:\n                    50:e7:1f:08:bc:6f:30:37:d0:49:59:9f:a5:8d:9b:\n                    59:85:f9:d8:99:fc:68:54:e5:7b:54:c9:b5:b1:88:\n                    b3:b1\n                Exponent: 65537 (0x10001)\n    Signature Algorithm: sha256WithRSAEncryption\n         4d:d7:88:a0:d0:db:a1:00:ed:46:b8:dd:1d:a3:3d:3b:d4:c5:\n         46:df:01:0e:30:e5:2c:63:95:ca:90:68:50:3f:53:5c:df:e6:\n         0a:74:6b:88:20:3b:1e:19:4c:bd:08:70:44:f5:31:b3:23:b5:\n         4b:14:62:e2:0a:99:13:75:d0:db:63:01:49:3a:d2:c5:28:a5:\n         eb:cf:b5:92:38:5d:c8:3e:fc:8b:d1:b1:24:ca:60:de:9d:23:\n         14:10:e8:68:80:3c:a2:d3:89:ce:4b:2d:9a:42:42:00:06:9b:\n         92:76:a1:79:9d:e6:e6:1a:9f:10:06:0a:74:6d:8d:5f:2a:f3:\n         fb:49:16:7c:08:93:31:53:83:41:cc:62:c2:7c:2d:1a:57:cb:\n         2b:c1:6a:5f:7f:1e:a5:1d:2d:ff:3e:80:88:eb:55:53:a5:3f:\n         73:fc:5a:e7:ad:d9:00:77:a3:e4:a7:cd:12:c1:88:f6:93:7f:\n         46:9d:ca:20:9e:22:8a:1a:23:1e:20:78:cb:46:15:7e:73:40:\n         66:f5:16:b5:6d:5f:e2:9c:4c:4e:e1:23:f9:7b:45:7d:ee:7e:\n         b6:c9:96:16:52:dc:d8:8a:f6:4d:b0:d2:08:84:fb:4f:1f:af:\n         41:87:a3:39:d6:19:37:eb:ad:98:b2:6f:46:28:8b:26:43:f1:\n         c8:35:3c:81\n-----BEGIN CERTIFICATE-----\nMIICrjCCAZagAwIBAgIBCzANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBpUZXN0\nIENBIEFnZW50IFN1YmF1dGhvcml0eTAeFw03MDAxMDEwMDAwMDBaFw0zNDAxMjcx\nODQzMzJaMBAxDjAMBgNVBAMMBXBsdXRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEAy9/B15aFLTuD7KB4bV7t1bzJQZY39MFd2sSXOLm4xw9pK8eMg3Sa\nRByTDSt9xgnxbYesRTqIv7H1WgfR5nei1eFKBuMuNGYN78UKLsmkgdUrigfTK08h\nTBZtu7ftWJ8KtVtGYQ3mjviRvm9/wFIMSKXnk1vhvouDxZFJLbbefNydCLN3DISO\n2MQgxyz1iBE1eh+99MC7N+Q1Oqpjv7aNIGosw6p55QJ20WX3Qw3UNHQtdY600KNR\n53XuqIAhX37Y/cj2pzXdy9WfBvE94mCPywRbc6EvpKC1gFYRc32sdRlQ5x8IvG8w\nN9BJWZ+ljZtZhfnYmfxoVOV7VMm1sYizsQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB\nAQBN14ig0NuhAO1GuN0doz071MVG3wEOMOUsY5XKkGhQP1Nc3+YKdGuIIDseGUy9\nCHBE9TGzI7VLFGLiCpkTddDbYwFJOtLFKKXrz7WSOF3IPvyL0bEkymDenSMUEOho\ngDyi04nOSy2aQkIABpuSdqF5nebmGp8QBgp0bY1fKvP7SRZ8CJMxU4NBzGLCfC0a\nV8srwWpffx6lHS3/PoCI61VTpT9z/FrnrdkAd6Pkp80SwYj2k39GncogniKKGiMe\nIHjLRhV+c0Bm9Ra1bV/inExO4SP5e0V97n62yZYWUtzYivZNsNIIhPtPH69Bh6M5\n1hk3662Ysm9GKIsmQ/HINTyB\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/renewed.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 5 (0x5)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=renewed\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:ac:0d:e2:e1:62:ee:7a:55:f6:46:0a:5f:ed:dc:\n                    4a:a6:d4:d0:19:7f:a8:4d:cd:a0:c1:61:d4:e8:78:\n                    50:d4:5d:40:1f:e0:c3:79:26:95:19:4c:cd:70:10:\n                    53:38:b1:68:e4:7e:d4:44:65:68:94:8c:b6:ab:d2:\n                    22:36:d8:b0:92:ba:ed:3f:00:ec:b0:b6:5f:26:ac:\n                    f7:2b:71:e4:d7:8a:28:49:a8:fe:f9:a1:c8:b8:09:\n                    73:8c:f7:d3:90:ab:ed:74:fb:25:29:bf:81:51:b3:\n                    ac:02:81:ba:c1:7f:8a:ad:d7:18:6d:d0:73:1e:7f:\n                    d9:54:ed:0b:a2:a6:d7:7e:22:e1:3f:a3:6d:77:4a:\n                    9e:17:68:42:ef:99:63:e0:0f:5c:35:4e:3a:ef:5a:\n                    84:3c:c6:c1:ac:78:c5:fe:60:56:f4:e5:86:7d:60:\n                    1b:55:3c:bd:6a:c8:22:ea:b6:c3:f6:8a:67:e8:cb:\n                    5d:23:61:44:4b:9e:24:f8:91:69:25:90:0b:d7:2e:\n                    84:3f:c1:a2:8f:87:68:a8:36:b3:a7:b3:c8:0e:06:\n                    bd:6b:f3:54:41:d2:47:49:a7:2f:9b:3c:2f:37:89:\n                    46:12:90:95:fe:6a:6f:5e:29:c6:d9:ea:90:47:29:\n                    1b:85:1e:d3:19:e9:80:40:f7:33:67:bb:e5:17:50:\n                    5c:cd\n                Exponent: 65537 (0x10001)\n    Signature Algorithm: sha256WithRSAEncryption\n         11:65:97:e8:a7:de:13:c9:de:80:c2:7b:06:71:f7:de:23:75:\n         6d:7a:fe:ad:cf:27:a6:29:c0:5c:c0:0e:4c:92:60:90:59:e0:\n         f6:ab:00:cd:9b:04:cb:36:f1:b3:02:b8:76:75:63:7f:fe:7c:\n         6d:7e:b9:c6:74:44:4d:73:da:02:90:05:de:8b:fa:83:b2:42:\n         a2:2d:50:7f:31:d3:c8:3c:90:8a:6d:f5:a1:d8:46:eb:5b:97:\n         89:bf:52:6f:7a:36:fd:dd:fc:fe:d0:2a:6b:3b:fb:5d:fc:e8:\n         ee:3f:03:75:d0:6a:f7:78:9f:f2:37:f9:24:a1:04:96:db:da:\n         0c:54:19:84:bb:a8:d8:88:61:aa:f9:08:a6:87:6b:cd:24:73:\n         e0:c3:03:1a:6b:66:a6:df:ab:f1:0f:fc:2d:77:fb:11:c2:2f:\n         a8:18:d9:26:a7:af:55:d1:1b:13:48:f1:6e:08:2b:83:90:8f:\n         37:ef:de:19:3c:34:97:d6:d8:b5:04:48:aa:83:8c:f9:73:a5:\n         02:93:2d:45:dd:05:77:80:4b:a3:13:88:e7:ce:24:58:33:b6:\n         42:5b:cf:91:9a:ab:6e:15:38:b1:a1:2c:01:b0:e0:9b:25:00:\n         7f:b0:30:6e:06:70:b3:ee:45:56:bb:04:b4:b6:7d:18:17:df:\n         bc:a3:89:87\n-----BEGIN CERTIFICATE-----\nMIICqjCCAZKgAwIBAgIBBTANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\nIENBIFN1YmF1dGhvcml0eTAeFw03MDAxMDEwMDAwMDBaFw0zNDAxMjcxODQzMzJa\nMBIxEDAOBgNVBAMMB3JlbmV3ZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQCsDeLhYu56VfZGCl/t3Eqm1NAZf6hNzaDBYdToeFDUXUAf4MN5JpUZTM1w\nEFM4sWjkftREZWiUjLar0iI22LCSuu0/AOywtl8mrPcrceTXiihJqP75oci4CXOM\n99OQq+10+yUpv4FRs6wCgbrBf4qt1xht0HMef9lU7Quiptd+IuE/o213Sp4XaELv\nmWPgD1w1TjrvWoQ8xsGseMX+YFb05YZ9YBtVPL1qyCLqtsP2imfoy10jYURLniT4\nkWklkAvXLoQ/waKPh2ioNrOns8gOBr1r81RB0kdJpy+bPC83iUYSkJX+am9eKcbZ\n6pBHKRuFHtMZ6YBA9zNnu+UXUFzNAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABFl\nl+in3hPJ3oDCewZx994jdW16/q3PJ6YpwFzADkySYJBZ4ParAM2bBMs28bMCuHZ1\nY3/+fG1+ucZ0RE1z2gKQBd6L+oOyQqItUH8x08g8kIpt9aHYRutbl4m/Um96Nv3d\n/P7QKms7+1386O4/A3XQavd4n/I3+SShBJbb2gxUGYS7qNiIYar5CKaHa80kc+DD\nAxprZqbfq/EP/C13+xHCL6gY2Sanr1XRGxNI8W4IK4OQjzfv3hk8NJfW2LUESKqD\njPlzpQKTLUXdBXeAS6MTiOfOJFgztkJbz5Gaq24VOLGhLAGw4JslAH+wMG4GcLPu\nRVa7BLS2fRgX37yjiYc=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/request-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:ca:4a:fc:49:f0:75:de:db:71:88:87:f7:48:ec:\n    77:67:7c:38:a4:91:24:47:a4:85:1c:38:e4:ec:6d:\n    8a:aa:24:8d:fb:1c:41:ca:96:d7:92:70:14:9d:cc:\n    a7:26:d5:91:04:87:c3:9b:bc:7c:c2:7d:5f:8d:50:\n    75:d6:02:51:a3:38:94:b8:b8:63:4b:38:cf:08:0b:\n    c0:31:d9:a6:db:61:b7:11:32:95:ca:87:dc:54:21:\n    d1:bb:56:b9:2c:f2:c6:84:1f:54:df:09:d1:50:9d:\n    ae:97:45:f9:c0:66:58:d9:63:3e:fb:bf:af:e6:1a:\n    c8:ee:84:6b:cd:50:fe:dc:7a:52:74:17:3c:35:89:\n    4b:a7:48:3b:b8:84:ae:d1:0c:3c:5c:e0:e3:4c:20:\n    f8:aa:a4:70:4e:10:15:5e:a5:6e:6f:99:87:fd:79:\n    45:40:2b:6b:ec:e3:36:7a:ed:73:02:28:c7:cb:2e:\n    a0:ca:34:05:31:f5:98:c9:27:40:e6:e3:f3:26:2a:\n    25:cc:89:52:87:a0:33:a5:61:a2:c6:73:bd:a7:fc:\n    a6:0b:93:46:fe:3b:a7:6f:a3:3b:a4:6d:1e:1b:d1:\n    c4:28:09:ea:1d:b4:aa:f3:9a:fb:10:a2:b2:21:f2:\n    86:70:a4:b8:a8:df:4c:09:2c:fb:d2:48:a9:1c:3a:\n    4d:17\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    47:98:90:80:2e:cd:d8:e9:3b:de:81:98:c0:c3:88:\n    63:24:f0:fb:35:af:6c:77:ca:f0:b9:26:34:93:74:\n    7f:51:5e:ba:e0:3e:5c:d0:54:4d:ac:c3:6f:32:0e:\n    7a:cd:1d:7d:47:fb:b0:33:de:c8:0b:0d:75:7b:63:\n    a9:31:ba:e9:14:ce:76:2e:34:5e:5c:25:d2:08:2e:\n    a6:b6:0c:bb:1a:73:6b:6f:c6:da:1e:89:99:71:3a:\n    c4:9e:25:fd:c5:93:03:11:ff:bc:cc:c1:bb:51:83:\n    88:60:a8:cb:32:ae:01:8a:81:b1:22:1f:c4:45:f8:\n    95:8a:62:89:11:40:92:91:4a:66:95:43:fc:f4:ef:\n    58:2f:6a:11:28:37:5d:8c:3a:f1:94:db:4e:36:79:\n    c4:0d:de:4f:5a:4e:f9:91:48:e9:a5:0c:2a:71:3f:\n    0f:18:e8:bc:f4:27:c7:3d:96:18:68:be:10:a5:5c:\n    44:30:f0:6f:6f:39:8a:8d:d8:a3:ee:55:c1:4a:c0:\n    d6:e2:b4:a2:a8:0d:3a:a5:f2:29:25:ff:b9:8c:aa:\n    fd:8f:80:7a:58:e6:29:fb:fc:40:10:f9:6a:90:e0:\n    0a:28:0d:78:08:18:da:44:ea:8d:13:5f:c8:d2:de:\n    ca:0e:81:af:d7:8f:37:ad:58:2a:bf:ca:9a:73:d5:\n    99\nprime1:\n    00:fe:54:28:81:30:a5:dc:18:13:ec:a6:30:5c:89:\n    a7:98:df:94:42:24:1a:28:ea:79:a7:f3:db:08:4d:\n    2b:27:29:f2:eb:10:7f:76:bb:d6:9d:de:09:af:6a:\n    0e:a2:fb:0b:6d:d1:a7:e5:cf:c5:7d:ba:5d:f6:a8:\n    06:51:7e:fb:17:70:3f:77:5a:16:f9:d3:af:44:d3:\n    d8:f7:75:47:ce:2b:83:f5:fe:c1:b8:40:dd:32:4f:\n    9b:66:42:be:da:42:03:17:ad:ec:d2:54:62:88:f2:\n    8f:0b:ca:fc:e6:4d:43:58:ae:36:ec:3e:90:55:f8:\n    42:7e:f3:83:8c:25:d7:68:1b\nprime2:\n    00:cb:9f:4a:62:a7:d0:bf:3e:84:39:58:41:7c:9b:\n    9b:dc:95:d0:7c:f7:39:59:7c:1e:2c:ac:cc:de:d8:\n    e6:5b:6a:45:86:18:96:c4:75:63:41:04:fb:e3:68:\n    2d:cf:8d:a2:3a:e3:ff:ed:58:2a:61:73:bb:b0:e6:\n    30:02:67:1d:7f:1a:bb:9b:96:cb:cb:64:da:bd:30:\n    64:cd:30:2b:6d:df:d2:28:56:9b:08:f9:81:32:27:\n    9c:7c:85:f2:03:d7:93:1c:b5:e3:65:76:81:48:98:\n    90:2e:29:01:ff:ec:2f:82:34:0c:d4:da:c8:49:eb:\n    ea:09:77:bd:c7:e0:68:36:b5\nexponent1:\n    00:88:ab:8d:00:af:b5:d1:aa:96:ba:6b:2c:3d:ee:\n    33:44:31:91:a8:61:62:35:6e:9b:65:a7:e3:a2:78:\n    65:a2:2c:26:c0:2f:23:70:18:cc:e2:14:f9:bb:6c:\n    10:e5:80:66:c9:e3:0a:88:b2:b8:7c:31:f4:60:a2:\n    36:93:00:db:c9:e2:a6:18:6b:2f:41:9d:81:e8:48:\n    b7:a5:73:3e:dd:0a:01:65:e6:3b:0f:da:bb:83:57:\n    c8:38:91:d1:a1:d4:2b:79:44:1b:a9:83:73:58:08:\n    90:da:53:80:c7:f9:e9:20:f4:ad:cb:12:6b:d9:e7:\n    09:44:d3:73:73:92:ba:48:d5\nexponent2:\n    05:6a:5c:21:19:93:7c:b2:9d:f3:a4:7c:41:63:26:\n    17:0c:c8:f9:b9:dd:85:9a:be:76:b3:b5:d3:2b:73:\n    9c:f4:cb:9a:90:66:da:1c:c1:22:06:8e:e8:72:69:\n    dd:7e:47:d5:47:21:14:5f:e6:e8:a6:9a:54:4f:1a:\n    f1:c5:6a:4c:6a:1a:99:3d:be:77:cf:5b:dd:f2:3e:\n    8c:12:7e:e5:32:31:b5:ae:ef:fa:a2:20:24:84:57:\n    2e:1c:a5:de:22:4a:a0:55:da:11:65:c0:74:7c:d2:\n    40:d8:08:5c:95:1c:82:5a:fe:08:5f:35:3a:7a:12:\n    c4:63:b1:e1:71:ff:73:8d\ncoefficient:\n    00:d4:26:27:a7:99:06:86:1c:e3:d0:79:d8:e0:7c:\n    9b:9b:b0:85:86:6f:67:c5:01:37:c0:b9:4a:92:32:\n    da:22:ec:e3:4a:98:a9:36:6d:01:a7:c1:11:3e:45:\n    2f:0f:01:ce:05:0c:de:27:af:c8:22:d9:de:64:83:\n    0a:fa:bb:0e:76:c1:a9:83:d0:55:62:36:93:bb:c0:\n    54:ed:03:bc:38:7a:94:68:0e:71:db:e0:0b:a2:fb:\n    4e:09:9a:bc:0f:6d:18:72:ab:76:aa:c9:4b:37:0e:\n    fa:0e:db:8b:0d:97:96:b5:59:ab:ac:7e:6f:d6:87:\n    da:45:25:78:a2:4c:ec:92:16\n-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAykr8SfB13ttxiIf3SOx3Z3w4pJEkR6SFHDjk7G2KqiSN+xxB\nypbXknAUncynJtWRBIfDm7x8wn1fjVB11gJRoziUuLhjSzjPCAvAMdmm22G3ETKV\nyofcVCHRu1a5LPLGhB9U3wnRUJ2ul0X5wGZY2WM++7+v5hrI7oRrzVD+3HpSdBc8\nNYlLp0g7uISu0Qw8XODjTCD4qqRwThAVXqVub5mH/XlFQCtr7OM2eu1zAijHyy6g\nyjQFMfWYySdA5uPzJiolzIlSh6AzpWGixnO9p/ymC5NG/junb6M7pG0eG9HEKAnq\nHbSq85r7EKKyIfKGcKS4qN9MCSz70kipHDpNFwIDAQABAoIBAEeYkIAuzdjpO96B\nmMDDiGMk8Ps1r2x3yvC5JjSTdH9RXrrgPlzQVE2sw28yDnrNHX1H+7Az3sgLDXV7\nY6kxuukUznYuNF5cJdIILqa2DLsac2tvxtoeiZlxOsSeJf3FkwMR/7zMwbtRg4hg\nqMsyrgGKgbEiH8RF+JWKYokRQJKRSmaVQ/z071gvahEoN12MOvGU2042ecQN3k9a\nTvmRSOmlDCpxPw8Y6Lz0J8c9lhhovhClXEQw8G9vOYqN2KPuVcFKwNbitKKoDTql\n8ikl/7mMqv2PgHpY5in7/EAQ+WqQ4AooDXgIGNpE6o0TX8jS3soOga/XjzetWCq/\nyppz1ZkCgYEA/lQogTCl3BgT7KYwXImnmN+UQiQaKOp5p/PbCE0rJyny6xB/drvW\nnd4Jr2oOovsLbdGn5c/Ffbpd9qgGUX77F3A/d1oW+dOvRNPY93VHziuD9f7BuEDd\nMk+bZkK+2kIDF63s0lRiiPKPC8r85k1DWK427D6QVfhCfvODjCXXaBsCgYEAy59K\nYqfQvz6EOVhBfJub3JXQfPc5WXweLKzM3tjmW2pFhhiWxHVjQQT742gtz42iOuP/\n7VgqYXO7sOYwAmcdfxq7m5bLy2TavTBkzTArbd/SKFabCPmBMiecfIXyA9eTHLXj\nZXaBSJiQLikB/+wvgjQM1NrISevqCXe9x+BoNrUCgYEAiKuNAK+10aqWumssPe4z\nRDGRqGFiNW6bZafjonhloiwmwC8jcBjM4hT5u2wQ5YBmyeMKiLK4fDH0YKI2kwDb\nyeKmGGsvQZ2B6Ei3pXM+3QoBZeY7D9q7g1fIOJHRodQreUQbqYNzWAiQ2lOAx/np\nIPStyxJr2ecJRNNzc5K6SNUCgYAFalwhGZN8sp3zpHxBYyYXDMj5ud2Fmr52s7XT\nK3Oc9MuakGbaHMEiBo7ocmndfkfVRyEUX+bopppUTxrxxWpMahqZPb53z1vd8j6M\nEn7lMjG1ru/6oiAkhFcuHKXeIkqgVdoRZcB0fNJA2AhclRyCWv4IXzU6ehLEY7Hh\ncf9zjQKBgQDUJienmQaGHOPQedjgfJubsIWGb2fFATfAuUqSMtoi7ONKmKk2bQGn\nwRE+RS8PAc4FDN4nr8gi2d5kgwr6uw52wamD0FViNpO7wFTtA7w4epRoDnHb4Aui\n+04JmrwPbRhyq3aqyUs3DvoO24sNl5a1Wausfm/Wh9pFJXiiTOySFg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/request.pem",
    "content": "Certificate Request:\n    Data:\n        Version: 3 (0x2)\n        Subject: CN=pending\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:ca:4a:fc:49:f0:75:de:db:71:88:87:f7:48:ec:\n                    77:67:7c:38:a4:91:24:47:a4:85:1c:38:e4:ec:6d:\n                    8a:aa:24:8d:fb:1c:41:ca:96:d7:92:70:14:9d:cc:\n                    a7:26:d5:91:04:87:c3:9b:bc:7c:c2:7d:5f:8d:50:\n                    75:d6:02:51:a3:38:94:b8:b8:63:4b:38:cf:08:0b:\n                    c0:31:d9:a6:db:61:b7:11:32:95:ca:87:dc:54:21:\n                    d1:bb:56:b9:2c:f2:c6:84:1f:54:df:09:d1:50:9d:\n                    ae:97:45:f9:c0:66:58:d9:63:3e:fb:bf:af:e6:1a:\n                    c8:ee:84:6b:cd:50:fe:dc:7a:52:74:17:3c:35:89:\n                    4b:a7:48:3b:b8:84:ae:d1:0c:3c:5c:e0:e3:4c:20:\n                    f8:aa:a4:70:4e:10:15:5e:a5:6e:6f:99:87:fd:79:\n                    45:40:2b:6b:ec:e3:36:7a:ed:73:02:28:c7:cb:2e:\n                    a0:ca:34:05:31:f5:98:c9:27:40:e6:e3:f3:26:2a:\n                    25:cc:89:52:87:a0:33:a5:61:a2:c6:73:bd:a7:fc:\n                    a6:0b:93:46:fe:3b:a7:6f:a3:3b:a4:6d:1e:1b:d1:\n                    c4:28:09:ea:1d:b4:aa:f3:9a:fb:10:a2:b2:21:f2:\n                    86:70:a4:b8:a8:df:4c:09:2c:fb:d2:48:a9:1c:3a:\n                    4d:17\n                Exponent: 65537 (0x10001)\n        Attributes:\n            a0:00\n    Signature Algorithm: sha256WithRSAEncryption\n         4b:33:bf:da:81:1a:39:41:11:c4:1c:d0:e5:3c:c6:93:8d:df:\n         e5:91:c4:9f:d0:6b:07:61:94:25:d8:dc:9e:99:0d:9d:96:91:\n         b3:92:ff:eb:2e:f4:93:cd:05:26:6d:42:70:7b:73:08:59:2f:\n         4f:c8:7f:5a:ea:de:84:a8:62:b9:6b:6c:24:0a:89:6c:83:66:\n         43:d2:f5:84:d2:09:63:9e:21:44:9f:70:4a:90:9e:9d:4a:e2:\n         e6:b1:62:79:0f:12:cf:f7:91:39:31:e6:24:ee:96:bc:82:5f:\n         4e:0e:a4:f3:81:75:6f:e3:59:bd:e2:8e:24:9e:3f:fd:c4:52:\n         81:f6:0d:95:31:36:48:0b:29:4e:94:22:10:a6:25:1f:f9:a7:\n         9d:e9:fc:8d:c9:33:87:1e:00:c9:f8:81:0e:d7:02:31:74:f7:\n         57:ef:31:06:b8:fd:10:d3:43:a5:e9:ee:47:83:05:ac:8a:69:\n         22:19:03:52:66:df:ee:0a:3c:82:33:23:9c:ef:c6:f2:e3:88:\n         9e:03:aa:c1:ab:92:e5:ca:b7:ab:e9:ab:40:ab:8f:73:53:69:\n         ca:19:89:8c:a5:e2:2f:9a:0b:31:59:17:08:03:4b:d5:6e:74:\n         5a:d0:c8:b8:df:d7:39:88:45:cb:6a:02:d4:41:1c:f2:1e:b9:\n         77:3f:09:80\n-----BEGIN CERTIFICATE REQUEST-----\nMIICVzCCAT8CAQIwEjEQMA4GA1UEAwwHcGVuZGluZzCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAMpK/Enwdd7bcYiH90jsd2d8OKSRJEekhRw45Oxtiqok\njfscQcqW15JwFJ3MpybVkQSHw5u8fMJ9X41QddYCUaM4lLi4Y0s4zwgLwDHZptth\ntxEylcqH3FQh0btWuSzyxoQfVN8J0VCdrpdF+cBmWNljPvu/r+YayO6Ea81Q/tx6\nUnQXPDWJS6dIO7iErtEMPFzg40wg+KqkcE4QFV6lbm+Zh/15RUAra+zjNnrtcwIo\nx8suoMo0BTH1mMknQObj8yYqJcyJUoegM6VhosZzvaf8pguTRv47p2+jO6RtHhvR\nxCgJ6h20qvOa+xCisiHyhnCkuKjfTAks+9JIqRw6TRcCAwEAAaAAMA0GCSqGSIb3\nDQEBCwUAA4IBAQBLM7/agRo5QRHEHNDlPMaTjd/lkcSf0GsHYZQl2NyemQ2dlpGz\nkv/rLvSTzQUmbUJwe3MIWS9PyH9a6t6EqGK5a2wkColsg2ZD0vWE0gljniFEn3BK\nkJ6dSuLmsWJ5DxLP95E5MeYk7pa8gl9ODqTzgXVv41m94o4knj/9xFKB9g2VMTZI\nCylOlCIQpiUf+aed6fyNyTOHHgDJ+IEO1wIxdPdX7zEGuP0Q00Ol6e5HgwWsimki\nGQNSZt/uCjyCMyOc78by44ieA6rBq5Llyrer6atAq49zU2nKGYmMpeIvmgsxWRcI\nA0vVbnRa0Mi439c5iEXLagLUQRzyHrl3PwmA\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "spec/fixtures/ssl/revoked-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:c1:5b:fe:c1:98:52:de:3b:2b:71:ea:52:c9:4b:\n    07:f6:4e:d1:72:1f:5e:cc:cd:41:0b:2b:c7:aa:68:\n    05:56:0b:c3:42:66:ac:ee:d6:5a:3e:35:bb:b8:85:\n    27:ba:79:1b:97:b8:8c:b7:c7:64:96:de:7b:b8:78:\n    c1:56:5e:84:f8:9d:4a:a3:81:b4:d9:7d:ba:35:ee:\n    47:74:40:a4:47:a7:83:6c:af:9c:63:41:fa:8e:0e:\n    01:1d:92:b1:94:a9:ff:57:44:61:e9:3d:74:74:dd:\n    be:0b:73:f6:24:b7:92:f1:d3:35:34:a7:75:88:58:\n    7d:8b:5c:fb:39:f2:25:c4:fb:f2:39:8f:b6:85:a1:\n    38:7a:20:b8:8d:0d:68:c7:7f:48:1b:19:00:7a:f2:\n    71:ae:ef:b8:c7:6f:d5:0b:ff:ea:3b:7f:be:35:45:\n    ff:ae:3c:78:13:57:bf:ec:3c:cf:cc:26:9b:31:70:\n    8c:a9:c3:34:a0:49:99:d6:4c:db:30:8b:a0:b9:44:\n    15:49:3a:e6:51:f2:cd:26:c5:f1:b8:df:3f:70:20:\n    f5:52:ec:e2:f3:67:fc:86:f5:dd:31:a3:e9:70:ef:\n    35:0c:24:91:7d:20:06:d1:64:28:9e:80:23:c4:95:\n    06:b3:5b:25:3d:51:52:95:ad:29:c2:cd:34:40:b2:\n    96:b5\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    00:93:a3:e3:e4:fc:a3:21:ce:fa:40:54:14:7c:60:\n    cf:26:92:4e:38:9e:9d:6b:31:ba:5d:86:43:41:e9:\n    85:51:8b:4f:bf:8e:d8:b2:d1:77:3c:93:18:d0:2e:\n    d4:03:fd:5f:45:3d:04:2d:7b:91:61:e5:65:80:98:\n    ab:bf:80:12:76:26:dc:0b:f7:09:19:1c:78:27:9f:\n    d6:6d:7c:c2:33:78:43:d0:a4:52:7c:33:af:d5:f1:\n    f9:86:14:31:85:fb:3b:dd:ed:77:f9:79:14:47:fb:\n    dc:95:b0:28:9d:26:92:8c:15:a2:45:9f:2c:0e:3c:\n    cb:37:b3:7a:9c:67:39:d1:aa:de:bb:2e:e9:17:56:\n    dd:0f:2f:3d:60:09:82:1d:74:cc:68:2a:b7:db:00:\n    28:32:1a:4c:d4:71:1d:b5:97:8c:fd:a5:d0:0a:41:\n    e1:b1:80:ae:1c:bd:01:b1:66:44:db:47:25:53:41:\n    d1:b2:c1:4e:74:b9:2c:8a:58:d9:22:37:5b:94:f0:\n    e0:32:58:1b:6a:6a:02:00:48:a8:97:e9:92:4b:2b:\n    8b:50:49:ce:a9:5d:e9:ea:9b:73:e1:28:0c:a6:09:\n    af:c6:da:c1:fc:8b:5a:c5:20:7e:6a:fd:da:1e:7d:\n    4a:12:fa:d7:75:a2:47:22:30:8e:0b:60:a2:33:f1:\n    37:6d\nprime1:\n    00:e9:ee:32:cf:2e:3e:0b:94:a7:e6:b7:21:e8:ad:\n    61:53:14:dc:3f:18:89:1c:f4:bd:4b:66:4a:c3:4c:\n    03:b4:d4:7d:76:34:37:4c:24:ce:cf:20:b8:5e:fd:\n    b7:f1:d0:69:9e:9a:30:d9:ef:cb:3d:f1:c1:48:b0:\n    42:64:9e:07:20:13:dd:bd:61:0f:84:27:0e:57:05:\n    74:d5:ea:b9:1c:11:7e:17:43:fc:81:c3:da:2d:8d:\n    80:9c:9f:72:fc:04:4e:ca:90:a6:c5:8a:32:78:60:\n    6f:a5:81:6d:14:52:be:b9:40:a5:22:b5:10:a6:04:\n    8f:11:76:6b:3c:4e:4d:21:67\nprime2:\n    00:d3:99:f0:3a:2b:8a:42:12:08:c1:4e:17:3d:60:\n    73:15:b6:eb:d6:ae:d6:1e:18:25:6d:a8:34:1f:d7:\n    22:04:3b:bd:8d:51:5c:51:8d:46:06:50:6c:e4:c7:\n    7e:ef:c4:a9:55:73:46:13:e9:df:7b:bf:b2:aa:0d:\n    b2:a8:7c:a6:6e:7b:c3:d8:20:d9:dd:45:88:8b:2c:\n    d1:a3:83:b1:55:42:a4:b2:47:35:04:c5:e1:01:60:\n    b7:f9:a5:c5:09:3e:5e:28:30:63:7d:f4:fa:47:1e:\n    8c:63:2c:51:73:be:4f:aa:c0:fe:53:1d:2c:d0:d3:\n    46:d8:a1:ec:fb:82:77:29:83\nexponent1:\n    07:63:84:ab:52:94:97:1d:0c:e8:96:a4:35:8b:34:\n    65:c1:64:eb:81:44:e3:6b:3f:87:60:25:c0:61:68:\n    44:8f:e9:9e:90:2b:a6:f6:0a:aa:34:28:5e:a7:b8:\n    e5:a4:65:91:a2:e1:24:21:14:d4:52:0c:7e:d5:42:\n    63:97:32:36:6b:2a:37:cf:17:04:5f:8f:6e:64:37:\n    ba:81:fd:72:cb:82:a6:0d:79:ae:47:97:5e:94:f7:\n    98:fa:d8:50:e9:79:eb:a0:02:04:7f:46:f4:d2:66:\n    f4:5d:50:2a:b2:2a:60:03:90:b3:b5:f0:5e:ae:0a:\n    2e:cd:04:ba:14:e7:0e:c5\nexponent2:\n    33:57:0d:47:3f:db:2a:ce:af:5b:1f:74:5a:0a:f5:\n    c1:56:01:80:b4:28:f5:62:4c:6a:7f:be:2d:df:87:\n    bc:59:36:53:7c:63:16:d2:5d:24:19:0d:62:b0:d8:\n    a6:9a:23:af:bc:e9:4a:d9:9a:c4:ae:ad:4d:bf:47:\n    12:c4:33:a9:68:d4:0a:b3:65:c8:df:1f:13:0d:8b:\n    cb:cd:9f:10:1d:bc:52:19:1e:cf:0a:a7:de:2a:b6:\n    58:97:14:e8:7a:3e:64:27:e1:6b:e5:2d:78:65:fc:\n    0f:ef:e7:cf:5a:64:7f:fc:95:78:5b:02:cd:a4:05:\n    73:78:4e:44:73:d2:2f:bb\ncoefficient:\n    60:ff:46:e7:ea:48:35:9e:b6:53:2c:0c:23:b4:f8:\n    a9:74:9a:d5:30:d5:65:b8:ad:44:d8:af:45:bc:37:\n    d3:15:bd:71:ab:58:4f:92:fe:d6:33:61:89:0d:db:\n    9d:1f:12:cf:64:b3:0d:51:71:9b:61:b4:a5:75:6e:\n    79:64:e0:ae:ca:29:f3:a3:02:b6:d6:99:d8:a9:9f:\n    d9:e6:cd:87:de:89:62:ab:f5:ef:be:1f:43:a3:d1:\n    97:62:1d:bf:ad:07:b9:b5:4e:a5:c3:2d:dd:55:d6:\n    15:9a:ff:57:b5:68:22:44:8f:33:8c:68:39:b0:bf:\n    14:18:52:91:24:8c:99:8e\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwVv+wZhS3jsrcepSyUsH9k7Rch9ezM1BCyvHqmgFVgvDQmas\n7tZaPjW7uIUnunkbl7iMt8dklt57uHjBVl6E+J1Ko4G02X26Ne5HdECkR6eDbK+c\nY0H6jg4BHZKxlKn/V0Rh6T10dN2+C3P2JLeS8dM1NKd1iFh9i1z7OfIlxPvyOY+2\nhaE4eiC4jQ1ox39IGxkAevJxru+4x2/VC//qO3++NUX/rjx4E1e/7DzPzCabMXCM\nqcM0oEmZ1kzbMIuguUQVSTrmUfLNJsXxuN8/cCD1Uuzi82f8hvXdMaPpcO81DCSR\nfSAG0WQonoAjxJUGs1slPVFSla0pws00QLKWtQIDAQABAoIBAQCTo+Pk/KMhzvpA\nVBR8YM8mkk44np1rMbpdhkNB6YVRi0+/jtiy0Xc8kxjQLtQD/V9FPQQte5Fh5WWA\nmKu/gBJ2JtwL9wkZHHgnn9ZtfMIzeEPQpFJ8M6/V8fmGFDGF+zvd7Xf5eRRH+9yV\nsCidJpKMFaJFnywOPMs3s3qcZznRqt67LukXVt0PLz1gCYIddMxoKrfbACgyGkzU\ncR21l4z9pdAKQeGxgK4cvQGxZkTbRyVTQdGywU50uSyKWNkiN1uU8OAyWBtqagIA\nSKiX6ZJLK4tQSc6pXenqm3PhKAymCa/G2sH8i1rFIH5q/doefUoS+td1okciMI4L\nYKIz8TdtAoGBAOnuMs8uPguUp+a3IeitYVMU3D8YiRz0vUtmSsNMA7TUfXY0N0wk\nzs8guF79t/HQaZ6aMNnvyz3xwUiwQmSeByAT3b1hD4QnDlcFdNXquRwRfhdD/IHD\n2i2NgJyfcvwETsqQpsWKMnhgb6WBbRRSvrlApSK1EKYEjxF2azxOTSFnAoGBANOZ\n8DorikISCMFOFz1gcxW269au1h4YJW2oNB/XIgQ7vY1RXFGNRgZQbOTHfu/EqVVz\nRhPp33u/sqoNsqh8pm57w9gg2d1FiIss0aODsVVCpLJHNQTF4QFgt/mlxQk+Xigw\nY330+kcejGMsUXO+T6rA/lMdLNDTRtih7PuCdymDAoGAB2OEq1KUlx0M6JakNYs0\nZcFk64FE42s/h2AlwGFoRI/pnpArpvYKqjQoXqe45aRlkaLhJCEU1FIMftVCY5cy\nNmsqN88XBF+PbmQ3uoH9csuCpg15rkeXXpT3mPrYUOl566ACBH9G9NJm9F1QKrIq\nYAOQs7XwXq4KLs0EuhTnDsUCgYAzVw1HP9sqzq9bH3RaCvXBVgGAtCj1Ykxqf74t\n34e8WTZTfGMW0l0kGQ1isNimmiOvvOlK2ZrErq1Nv0cSxDOpaNQKs2XI3x8TDYvL\nzZ8QHbxSGR7PCqfeKrZYlxToej5kJ+Fr5S14ZfwP7+fPWmR//JV4WwLNpAVzeE5E\nc9IvuwKBgGD/RufqSDWetlMsDCO0+Kl0mtUw1WW4rUTYr0W8N9MVvXGrWE+S/tYz\nYYkN250fEs9ksw1RcZthtKV1bnlk4K7KKfOjArbWmdipn9nmzYfeiWKr9e++H0Oj\n0ZdiHb+tB7m1TqXDLd1V1hWa/1e1aCJEjzOMaDmwvxQYUpEkjJmO\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/revoked.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 8 (0x8)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=revoked\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:c1:5b:fe:c1:98:52:de:3b:2b:71:ea:52:c9:4b:\n                    07:f6:4e:d1:72:1f:5e:cc:cd:41:0b:2b:c7:aa:68:\n                    05:56:0b:c3:42:66:ac:ee:d6:5a:3e:35:bb:b8:85:\n                    27:ba:79:1b:97:b8:8c:b7:c7:64:96:de:7b:b8:78:\n                    c1:56:5e:84:f8:9d:4a:a3:81:b4:d9:7d:ba:35:ee:\n                    47:74:40:a4:47:a7:83:6c:af:9c:63:41:fa:8e:0e:\n                    01:1d:92:b1:94:a9:ff:57:44:61:e9:3d:74:74:dd:\n                    be:0b:73:f6:24:b7:92:f1:d3:35:34:a7:75:88:58:\n                    7d:8b:5c:fb:39:f2:25:c4:fb:f2:39:8f:b6:85:a1:\n                    38:7a:20:b8:8d:0d:68:c7:7f:48:1b:19:00:7a:f2:\n                    71:ae:ef:b8:c7:6f:d5:0b:ff:ea:3b:7f:be:35:45:\n                    ff:ae:3c:78:13:57:bf:ec:3c:cf:cc:26:9b:31:70:\n                    8c:a9:c3:34:a0:49:99:d6:4c:db:30:8b:a0:b9:44:\n                    15:49:3a:e6:51:f2:cd:26:c5:f1:b8:df:3f:70:20:\n                    f5:52:ec:e2:f3:67:fc:86:f5:dd:31:a3:e9:70:ef:\n                    35:0c:24:91:7d:20:06:d1:64:28:9e:80:23:c4:95:\n                    06:b3:5b:25:3d:51:52:95:ad:29:c2:cd:34:40:b2:\n                    96:b5\n                Exponent: 65537 (0x10001)\n    Signature Algorithm: sha256WithRSAEncryption\n         6e:a8:0b:21:b3:f4:24:38:6e:9d:95:c5:cb:79:4e:7a:7a:43:\n         47:ab:d7:ff:b3:ff:2d:09:6f:b2:52:6c:c1:79:db:ab:78:06:\n         57:56:9b:57:a8:8d:1e:a4:18:6f:62:78:63:cd:f1:0a:40:2f:\n         46:bd:c1:2f:5e:cc:07:cc:52:70:f9:fa:45:d3:6a:7a:d3:59:\n         51:53:89:f1:cd:98:32:94:33:6b:e6:77:ac:f6:4f:e9:97:e0:\n         79:0e:7a:8e:45:86:8b:43:9f:04:3b:b1:ac:05:b1:da:2d:7b:\n         3f:f8:d6:44:a7:87:6b:17:36:d7:f4:f9:c6:ea:3b:cf:0c:30:\n         de:99:be:e4:8f:44:dd:06:29:28:bf:06:91:a4:94:8b:0a:97:\n         d4:70:1b:19:7e:c1:98:6d:b7:82:b5:55:da:01:7f:71:73:24:\n         05:5e:18:3b:48:38:44:53:99:06:c5:29:56:2c:ec:c5:62:ab:\n         2e:bc:16:a6:57:ee:e5:a2:04:74:2d:ee:64:75:8b:ce:8f:e2:\n         47:7b:78:4e:a6:fe:cd:5b:5c:ab:b3:c5:52:39:29:65:9b:49:\n         22:3f:4b:47:91:61:e9:34:c3:00:b8:bf:9c:b4:f4:66:d0:3c:\n         e5:91:3a:16:4f:e5:70:fe:d2:7a:f7:14:e4:f5:f1:34:32:e8:\n         b5:a4:e7:d9\n-----BEGIN CERTIFICATE-----\nMIICqjCCAZKgAwIBAgIBCDANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\nIENBIFN1YmF1dGhvcml0eTAeFw03MDAxMDEwMDAwMDBaFw0zNDAxMjcxODQzMzJa\nMBIxEDAOBgNVBAMMB3Jldm9rZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQDBW/7BmFLeOytx6lLJSwf2TtFyH17MzUELK8eqaAVWC8NCZqzu1lo+Nbu4\nhSe6eRuXuIy3x2SW3nu4eMFWXoT4nUqjgbTZfbo17kd0QKRHp4Nsr5xjQfqODgEd\nkrGUqf9XRGHpPXR03b4Lc/Ykt5Lx0zU0p3WIWH2LXPs58iXE+/I5j7aFoTh6ILiN\nDWjHf0gbGQB68nGu77jHb9UL/+o7f741Rf+uPHgTV7/sPM/MJpsxcIypwzSgSZnW\nTNswi6C5RBVJOuZR8s0mxfG43z9wIPVS7OLzZ/yG9d0xo+lw7zUMJJF9IAbRZCie\ngCPElQazWyU9UVKVrSnCzTRAspa1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAG6o\nCyGz9CQ4bp2Vxct5Tnp6Q0er1/+z/y0Jb7JSbMF526t4BldWm1eojR6kGG9ieGPN\n8QpAL0a9wS9ezAfMUnD5+kXTanrTWVFTifHNmDKUM2vmd6z2T+mX4HkOeo5FhotD\nnwQ7sawFsdotez/41kSnh2sXNtf0+cbqO88MMN6ZvuSPRN0GKSi/BpGklIsKl9Rw\nGxl+wZhtt4K1VdoBf3FzJAVeGDtIOERTmQbFKVYs7MViqy68FqZX7uWiBHQt7mR1\ni86P4kd7eE6m/s1bXKuzxVI5KWWbSSI/S0eRYek0wwC4v5y09GbQPOWROhZP5XD+\n0nr3FOT18TQy6LWk59k=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/signed-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:ac:0d:e2:e1:62:ee:7a:55:f6:46:0a:5f:ed:dc:\n    4a:a6:d4:d0:19:7f:a8:4d:cd:a0:c1:61:d4:e8:78:\n    50:d4:5d:40:1f:e0:c3:79:26:95:19:4c:cd:70:10:\n    53:38:b1:68:e4:7e:d4:44:65:68:94:8c:b6:ab:d2:\n    22:36:d8:b0:92:ba:ed:3f:00:ec:b0:b6:5f:26:ac:\n    f7:2b:71:e4:d7:8a:28:49:a8:fe:f9:a1:c8:b8:09:\n    73:8c:f7:d3:90:ab:ed:74:fb:25:29:bf:81:51:b3:\n    ac:02:81:ba:c1:7f:8a:ad:d7:18:6d:d0:73:1e:7f:\n    d9:54:ed:0b:a2:a6:d7:7e:22:e1:3f:a3:6d:77:4a:\n    9e:17:68:42:ef:99:63:e0:0f:5c:35:4e:3a:ef:5a:\n    84:3c:c6:c1:ac:78:c5:fe:60:56:f4:e5:86:7d:60:\n    1b:55:3c:bd:6a:c8:22:ea:b6:c3:f6:8a:67:e8:cb:\n    5d:23:61:44:4b:9e:24:f8:91:69:25:90:0b:d7:2e:\n    84:3f:c1:a2:8f:87:68:a8:36:b3:a7:b3:c8:0e:06:\n    bd:6b:f3:54:41:d2:47:49:a7:2f:9b:3c:2f:37:89:\n    46:12:90:95:fe:6a:6f:5e:29:c6:d9:ea:90:47:29:\n    1b:85:1e:d3:19:e9:80:40:f7:33:67:bb:e5:17:50:\n    5c:cd\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    15:08:01:82:c1:80:1b:2e:24:d3:7c:f3:2a:f5:31:\n    9e:e1:06:ab:07:42:c0:77:f1:3d:92:42:df:43:cd:\n    c5:97:bc:7d:e9:10:9b:df:8e:7c:30:3a:30:87:9e:\n    54:a0:c3:0c:a3:40:39:38:18:27:88:67:cf:ea:f8:\n    c9:b1:85:2b:fa:73:83:af:0e:3f:af:9b:43:f4:02:\n    a6:a9:de:6a:46:76:14:42:f7:1f:f6:99:bd:7d:52:\n    45:9a:09:9f:76:94:a9:27:05:ec:eb:7e:d8:48:d9:\n    2e:d9:42:c5:e2:5c:46:51:b3:a7:c0:c2:41:a9:29:\n    1c:cb:79:0f:a1:cb:57:9c:a2:6a:35:d6:da:b2:21:\n    55:70:e2:8b:2d:e8:16:d3:37:41:fc:df:d4:b1:e0:\n    db:90:b8:e4:28:74:ad:8a:1d:92:b3:02:c3:68:65:\n    75:38:99:32:38:28:1f:64:91:3b:42:86:45:19:54:\n    2d:b0:26:48:88:25:32:5c:ab:f2:de:02:ec:77:fc:\n    cc:ea:6f:ce:e0:40:44:7e:80:c8:bf:99:25:5d:d5:\n    88:57:55:4b:07:64:d2:8b:a3:c2:86:56:d9:6f:16:\n    6c:c5:52:84:9b:e6:b4:4c:2e:ff:59:7b:d9:0e:fb:\n    95:4c:5a:56:58:e9:d1:9c:0f:84:ad:36:d3:c8:5b:\n    b5\nprime1:\n    00:e3:17:e6:10:fa:80:d4:86:c4:59:bb:36:57:ad:\n    9a:12:8c:63:e0:84:b3:f3:ce:4b:d1:bf:a3:8c:0e:\n    a2:41:62:1a:46:74:eb:ce:01:55:33:8b:b1:1f:f2:\n    30:df:12:0f:69:0a:fb:e1:1e:32:80:5c:b0:85:5b:\n    ff:5a:93:9a:50:1b:7b:85:83:f0:8e:26:f9:e2:35:\n    5c:94:0c:ce:29:22:52:a0:a9:ac:64:d3:56:93:0d:\n    11:f7:16:07:10:0f:97:03:43:11:57:91:15:4d:04:\n    9c:6d:5e:26:b3:41:46:c8:7c:46:3c:2a:dd:40:e1:\n    9f:32:75:2b:82:49:dc:d8:73\nprime2:\n    00:c1:f4:79:5b:51:e7:58:84:a6:57:11:48:e6:25:\n    60:dd:7b:67:e1:e4:61:6c:f2:37:6e:71:68:53:47:\n    1d:f2:e8:31:9f:60:64:6c:97:cf:2c:b9:b0:a5:39:\n    e2:11:40:c7:00:8a:4a:48:60:f3:33:3f:17:9f:56:\n    f9:74:f0:ad:ce:92:6a:30:c7:aa:32:b1:58:cb:08:\n    d4:44:40:29:56:cc:09:26:90:55:84:d8:b7:15:4a:\n    05:89:5d:29:d4:4a:4d:15:a9:40:f3:c7:a8:b0:73:\n    ba:4d:63:14:62:e6:cb:d6:b7:6c:8e:cb:68:a8:bf:\n    2f:10:5b:d5:f8:5b:64:e5:bf\nexponent1:\n    4c:d9:6f:8f:db:55:f1:95:d1:a8:94:04:25:d3:a7:\n    ca:13:1c:51:84:56:e9:70:ac:93:c4:88:72:03:19:\n    c1:8a:93:5d:b8:7f:7b:ed:53:89:e8:01:fe:cf:94:\n    de:48:5c:52:ad:d6:e3:2d:b6:e5:5d:78:97:08:b4:\n    f7:4f:ef:ee:9f:fe:43:06:8d:47:6d:c5:2e:59:e4:\n    84:6d:78:ee:ce:a0:ab:a1:ff:a6:f7:25:db:09:97:\n    44:c8:7d:87:5f:df:38:c9:5f:7b:04:ab:f2:ae:56:\n    c9:64:0d:30:a8:2c:6d:f8:30:44:78:34:fb:99:de:\n    a7:d2:a2:f0:aa:52:44:25\nexponent2:\n    7f:ef:5f:c1:51:d4:34:fa:42:b2:79:cf:49:27:ec:\n    ae:0b:81:a9:6a:38:ad:61:54:19:00:ab:5d:0b:33:\n    01:10:11:f3:5b:e4:c2:10:9c:f2:96:85:a3:66:fb:\n    ec:7f:7b:04:ab:33:76:6c:a4:de:ef:c6:08:2f:99:\n    9a:7e:4b:57:50:12:c5:9c:5e:72:d3:b2:8b:32:86:\n    b9:82:4d:02:58:d1:cc:63:36:55:cb:91:70:74:84:\n    14:68:a4:77:c8:8e:f2:33:d3:89:39:f0:d6:7b:6f:\n    af:2e:24:bb:5c:1b:a6:c5:14:d1:57:f0:f0:26:33:\n    c8:29:9b:89:17:d8:05:07\ncoefficient:\n    00:bd:78:38:fa:43:8a:96:b9:4f:69:3f:2a:e0:82:\n    b6:be:76:43:91:b5:eb:ad:d6:db:a2:e3:51:01:39:\n    c8:54:18:87:06:03:e7:0e:2e:9c:82:5d:54:df:bb:\n    97:c8:d2:fa:be:12:8f:df:eb:03:b0:78:74:d0:cb:\n    cc:0c:ad:9a:f0:c7:16:3e:cf:84:d4:c7:b8:91:65:\n    7d:bd:29:cd:71:7a:f9:16:30:cb:4f:f0:f0:59:96:\n    24:19:d6:77:eb:36:1d:1c:3d:0d:89:08:9f:64:bb:\n    86:b9:be:3f:c1:67:e5:b7:fb:d4:65:cd:54:cc:3d:\n    3b:a1:5a:3f:61:91:20:c3:10\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArA3i4WLuelX2Rgpf7dxKptTQGX+oTc2gwWHU6HhQ1F1AH+DD\neSaVGUzNcBBTOLFo5H7URGVolIy2q9IiNtiwkrrtPwDssLZfJqz3K3Hk14ooSaj+\n+aHIuAlzjPfTkKvtdPslKb+BUbOsAoG6wX+KrdcYbdBzHn/ZVO0LoqbXfiLhP6Nt\nd0qeF2hC75lj4A9cNU4671qEPMbBrHjF/mBW9OWGfWAbVTy9asgi6rbD9opn6Mtd\nI2FES54k+JFpJZAL1y6EP8Gij4doqDazp7PIDga9a/NUQdJHSacvmzwvN4lGEpCV\n/mpvXinG2eqQRykbhR7TGemAQPczZ7vlF1BczQIDAQABAoIBABUIAYLBgBsuJNN8\n8yr1MZ7hBqsHQsB38T2SQt9DzcWXvH3pEJvfjnwwOjCHnlSgwwyjQDk4GCeIZ8/q\n+MmxhSv6c4OvDj+vm0P0Aqap3mpGdhRC9x/2mb19UkWaCZ92lKknBezrfthI2S7Z\nQsXiXEZRs6fAwkGpKRzLeQ+hy1ecomo11tqyIVVw4ost6BbTN0H839Sx4NuQuOQo\ndK2KHZKzAsNoZXU4mTI4KB9kkTtChkUZVC2wJkiIJTJcq/LeAux3/Mzqb87gQER+\ngMi/mSVd1YhXVUsHZNKLo8KGVtlvFmzFUoSb5rRMLv9Ze9kO+5VMWlZY6dGcD4St\nNtPIW7UCgYEA4xfmEPqA1IbEWbs2V62aEoxj4ISz885L0b+jjA6iQWIaRnTrzgFV\nM4uxH/Iw3xIPaQr74R4ygFywhVv/WpOaUBt7hYPwjib54jVclAzOKSJSoKmsZNNW\nkw0R9xYHEA+XA0MRV5EVTQScbV4ms0FGyHxGPCrdQOGfMnUrgknc2HMCgYEAwfR5\nW1HnWISmVxFI5iVg3Xtn4eRhbPI3bnFoU0cd8ugxn2BkbJfPLLmwpTniEUDHAIpK\nSGDzMz8Xn1b5dPCtzpJqMMeqMrFYywjUREApVswJJpBVhNi3FUoFiV0p1EpNFalA\n88eosHO6TWMUYubL1rdsjstoqL8vEFvV+Ftk5b8CgYBM2W+P21XxldGolAQl06fK\nExxRhFbpcKyTxIhyAxnBipNduH977VOJ6AH+z5TeSFxSrdbjLbblXXiXCLT3T+/u\nn/5DBo1HbcUuWeSEbXjuzqCrof+m9yXbCZdEyH2HX984yV97BKvyrlbJZA0wqCxt\n+DBEeDT7md6n0qLwqlJEJQKBgH/vX8FR1DT6QrJ5z0kn7K4LgalqOK1hVBkAq10L\nMwEQEfNb5MIQnPKWhaNm++x/ewSrM3ZspN7vxggvmZp+S1dQEsWcXnLTsosyhrmC\nTQJY0cxjNlXLkXB0hBRopHfIjvIz04k58NZ7b68uJLtcG6bFFNFX8PAmM8gpm4kX\n2AUHAoGBAL14OPpDipa5T2k/KuCCtr52Q5G1663W26LjUQE5yFQYhwYD5w4unIJd\nVN+7l8jS+r4Sj9/rA7B4dNDLzAytmvDHFj7PhNTHuJFlfb0pzXF6+RYwy0/w8FmW\nJBnWd+s2HRw9DYkIn2S7hrm+P8Fn5bf71GXNVMw9O6FaP2GRIMMQ\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/signed.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 4 (0x4)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=signed\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:ac:0d:e2:e1:62:ee:7a:55:f6:46:0a:5f:ed:dc:\n                    4a:a6:d4:d0:19:7f:a8:4d:cd:a0:c1:61:d4:e8:78:\n                    50:d4:5d:40:1f:e0:c3:79:26:95:19:4c:cd:70:10:\n                    53:38:b1:68:e4:7e:d4:44:65:68:94:8c:b6:ab:d2:\n                    22:36:d8:b0:92:ba:ed:3f:00:ec:b0:b6:5f:26:ac:\n                    f7:2b:71:e4:d7:8a:28:49:a8:fe:f9:a1:c8:b8:09:\n                    73:8c:f7:d3:90:ab:ed:74:fb:25:29:bf:81:51:b3:\n                    ac:02:81:ba:c1:7f:8a:ad:d7:18:6d:d0:73:1e:7f:\n                    d9:54:ed:0b:a2:a6:d7:7e:22:e1:3f:a3:6d:77:4a:\n                    9e:17:68:42:ef:99:63:e0:0f:5c:35:4e:3a:ef:5a:\n                    84:3c:c6:c1:ac:78:c5:fe:60:56:f4:e5:86:7d:60:\n                    1b:55:3c:bd:6a:c8:22:ea:b6:c3:f6:8a:67:e8:cb:\n                    5d:23:61:44:4b:9e:24:f8:91:69:25:90:0b:d7:2e:\n                    84:3f:c1:a2:8f:87:68:a8:36:b3:a7:b3:c8:0e:06:\n                    bd:6b:f3:54:41:d2:47:49:a7:2f:9b:3c:2f:37:89:\n                    46:12:90:95:fe:6a:6f:5e:29:c6:d9:ea:90:47:29:\n                    1b:85:1e:d3:19:e9:80:40:f7:33:67:bb:e5:17:50:\n                    5c:cd\n                Exponent: 65537 (0x10001)\n    Signature Algorithm: sha256WithRSAEncryption\n         8d:44:e0:60:b2:84:0b:75:3c:1b:b1:b9:a3:7b:1c:68:00:17:\n         c7:ff:7b:ea:49:cb:79:e8:33:7c:ce:bd:22:f7:91:6e:6f:ca:\n         1d:35:18:9c:fd:7a:3a:6d:8f:7a:f5:a0:ba:fa:db:08:be:52:\n         50:36:2b:6d:a0:a1:1e:8c:ab:07:34:28:0b:fd:4d:1a:64:5c:\n         bd:64:76:94:1d:1b:08:95:6d:3e:b7:15:3a:61:3d:2a:73:f9:\n         6b:38:c5:b8:17:a8:62:cc:43:6c:f7:0f:05:02:d5:fa:d5:81:\n         91:56:7c:9e:e2:1f:22:e8:e0:1d:68:02:10:08:fe:88:5f:03:\n         d2:38:0b:68:0e:b4:c3:2c:3e:09:f6:48:22:75:12:65:f5:78:\n         d0:6d:29:57:3c:75:f4:4b:cb:c0:c8:99:4f:82:df:2f:1f:97:\n         17:5a:3c:05:79:69:e3:3e:d6:71:6b:71:bd:55:92:49:e1:6f:\n         71:d5:73:96:89:e9:9c:63:b3:48:ee:f8:eb:1c:34:96:c1:98:\n         69:4d:4c:e9:09:0e:e1:4e:44:62:20:48:18:cf:6a:a2:0a:a8:\n         1d:29:3f:91:a7:e0:d6:14:6b:ba:fa:29:4f:df:eb:02:b2:59:\n         bf:c9:20:14:bb:e8:72:a8:26:a6:4c:fe:76:7d:66:28:44:64:\n         cf:65:91:38\n-----BEGIN CERTIFICATE-----\nMIICqTCCAZGgAwIBAgIBBDANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\nIENBIFN1YmF1dGhvcml0eTAeFw03MDAxMDEwMDAwMDBaFw0zNDAxMjcxODQzMzJa\nMBExDzANBgNVBAMMBnNpZ25lZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAKwN4uFi7npV9kYKX+3cSqbU0Bl/qE3NoMFh1Oh4UNRdQB/gw3kmlRlMzXAQ\nUzixaOR+1ERlaJSMtqvSIjbYsJK67T8A7LC2Xyas9ytx5NeKKEmo/vmhyLgJc4z3\n05Cr7XT7JSm/gVGzrAKBusF/iq3XGG3Qcx5/2VTtC6Km134i4T+jbXdKnhdoQu+Z\nY+APXDVOOu9ahDzGwax4xf5gVvTlhn1gG1U8vWrIIuq2w/aKZ+jLXSNhREueJPiR\naSWQC9cuhD/Boo+HaKg2s6ezyA4GvWvzVEHSR0mnL5s8LzeJRhKQlf5qb14pxtnq\nkEcpG4Ue0xnpgED3M2e75RdQXM0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAjUTg\nYLKEC3U8G7G5o3scaAAXx/976knLeegzfM69IveRbm/KHTUYnP16Om2PevWguvrb\nCL5SUDYrbaChHoyrBzQoC/1NGmRcvWR2lB0bCJVtPrcVOmE9KnP5azjFuBeoYsxD\nbPcPBQLV+tWBkVZ8nuIfIujgHWgCEAj+iF8D0jgLaA60wyw+CfZIInUSZfV40G0p\nVzx19EvLwMiZT4LfLx+XF1o8BXlp4z7WcWtxvVWSSeFvcdVzlonpnGOzSO746xw0\nlsGYaU1M6QkO4U5EYiBIGM9qogqoHSk/kafg1hRruvopT9/rArJZv8kgFLvocqgm\npkz+dn1mKERkz2WROA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/tampered-cert.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 13 (0xd)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Test CA Subauthority\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=signed\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:bd:72:c2:5b:1a:e2:a2:27:e6:20:8c:5b:dd:b6:\n                    f2:49:66:5e:e0:a2:06:c0:83:01:c7:d3:ea:a5:28:\n                    89:1f:0d:11:e4:91:94:9a:23:39:06:b3:24:c3:92:\n                    6e:76:9d:f7:cb:76:d7:d2:df:3f:ce:6b:14:02:b8:\n                    01:15:5e:86:54:ae:b9:42:d7:4e:e1:ac:2e:65:83:\n                    1e:45:dc:93:b5:f1:de:55:97:5d:82:3a:f0:27:dc:\n                    1e:fe:4f:c6:c9:a6:4d:f6:5a:9d:dc:ae:1b:09:4c:\n                    b7:79:58:c2:e7:6f:81:2d:1a:63:f6:ef:a4:91:83:\n                    e0:dc:cb:ac:01:e6:5e:a6:fe:1b:78:7e:51:ad:ec:\n                    fd:68:c9:11:de:71:bf:e3:06:0d:fe:36:4b:8f:26:\n                    b8:d8:ff:68:41:f6:01:fc:40:a1:0e:30:06:c5:7e:\n                    de:f2:e7:99:69:92:88:07:1a:d0:93:ed:85:38:a1:\n                    82:8f:6b:3b:8c:a7:62:78:42:3a:54:83:01:45:56:\n                    4a:88:fe:1d:68:34:7b:29:6f:39:6b:08:90:f0:6d:\n                    50:6e:a8:44:b1:27:2d:c7:51:a3:90:d7:54:26:29:\n                    72:a7:c8:33:b7:42:08:1d:48:5f:61:41:aa:f0:b5:\n                    86:7c:94:ec:e0:97:d7:11:e2:80:35:35:ba:b0:3b:\n                    54:a7\n                Exponent: 65537 (0x10001)\n    Signature Algorithm: sha256WithRSAEncryption\n         54:d4:70:cf:7c:14:90:0e:8c:ad:5b:3d:51:5e:2e:d3:88:f6:\n         ba:94:b8:ab:18:6b:7a:87:7c:d7:31:74:1e:0a:11:1b:72:88:\n         84:2b:3d:0b:52:38:cd:d8:44:30:e8:0a:e4:15:7a:80:20:9e:\n         fd:9c:5e:66:61:71:54:52:38:fd:64:7a:ae:10:93:21:90:94:\n         68:fa:13:33:1c:e4:d6:f5:79:81:c3:2c:ec:c2:d2:3f:d6:89:\n         40:50:6a:e8:7e:2d:c0:35:f2:77:b7:50:39:95:6c:72:02:d3:\n         78:46:de:72:87:29:ff:a7:4b:74:66:f2:15:ac:77:1a:09:46:\n         40:19:42:a9:4c:80:2e:d0:4d:d9:84:97:76:5e:dd:38:f1:e7:\n         d2:96:b4:89:bb:e4:f6:b2:0a:9f:7d:42:d2:04:3f:ce:8a:53:\n         f4:2f:a2:ca:a3:2f:b5:d7:08:d8:8d:32:1c:f9:e4:00:04:56:\n         a9:bb:ba:be:70:66:ef:ef:fc:bf:0a:13:b8:59:be:fc:a3:18:\n         97:e1:26:55:55:d2:6b:9e:17:4c:78:a1:73:db:bc:26:dd:a1:\n         29:94:a2:cc:56:8c:4e:00:2b:6c:66:2f:04:4d:44:37:35:86:\n         df:57:95:16:1b:97:bd:12:29:56:77:9f:d5:e1:f3:35:24:d2:\n         36:26:d0:3f\n-----BEGIN CERTIFICATE-----\nMIICqTCCAZGgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\nIENBIFN1YmF1dGhvcml0eTAeFw03MDAxMDEwMDAwMDBaFw0zNDAxMjcxODQzMzJa\nMBExDzANBgNVBAMMBnNpZ25lZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAL1ywlsa4qIn5iCMW9228klmXuCiBsCDAcfT6qUoiR8NEeSRlJojOQazJMOS\nbnad98t219LfP85rFAK4ARVehlSuuULXTuGsLmWDHkXck7Xx3lWXXYI68CfcHv5P\nxsmmTfZandyuGwlMt3lYwudvgS0aY/bvpJGD4NzLrAHmXqb+G3h+Ua3s/WjJEd5x\nv+MGDf42S48muNj/aEH2AfxAoQ4wBsV+3vLnmWmSiAca0JPthTihgo9rO4ynYnhC\nOlSDAUVWSoj+HWg0eylvOWsIkPBtUG6oRLEnLcdRo5DXVCYpcqfIM7dCCB1IX2FB\nqvC1hnyU7OCX1xHigDU1urA7VKcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVNRw\nz3wUkA6MrVs9UV4u04j2upS4qxhreod81zF0HgoRG3KIhCs9C1I4zdhEMOgK5BV6\ngCCe/ZxeZmFxVFI4/WR6rhCTIZCUaPoTMxzk1vV5gcMs7MLSP9aJQFBq6H4twDXy\nd7dQOZVscgLTeEbecocp/6dLdGbyFax3GglGQBlCqUyALtBN2YSXdl7dOPHn0pa0\nibvk9rIKn31C0gQ/zopT9C+iyqMvtdcI2I0yHPnkAARWqbu6vnBm7+/8vwoTuFm+\n/KMYl+EmVVXSa54XTHihc9u8Jt2hKZSizFaMTgArbGYvBE1ENzWG31eVFhuXvRIp\nVnef1eHzNSTSNibQPw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/tampered-csr.pem",
    "content": "Certificate Request:\n    Data:\n        Version: 3 (0x2)\n        Subject: CN=signed\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:ca:3c:49:ca:69:e4:42:bc:a6:01:37:4e:c6:6c:\n                    1e:a9:d3:b1:7d:20:b6:7f:a9:74:c2:ce:33:f8:32:\n                    85:50:8a:c4:da:d6:47:2c:8e:3e:ef:9e:14:42:b3:\n                    43:b9:9f:59:ca:18:2c:32:f8:f8:5d:c6:74:1b:29:\n                    27:f8:d0:90:05:2e:03:46:b3:a2:c2:9d:38:de:06:\n                    f6:30:52:ff:e5:26:6e:88:fc:c4:23:32:f3:d3:09:\n                    67:1d:a6:52:f1:cc:06:28:2b:4e:af:7b:b2:50:a9:\n                    4f:7f:9b:62:6b:5a:cb:8c:f6:7e:ae:ed:a2:f2:4f:\n                    0d:d2:f5:42:2e:d8:50:b8:0f:fc:38:cd:80:7d:2a:\n                    d8:fc:76:e6:f7:28:91:f9:59:a7:d7:81:88:78:5c:\n                    74:0c:82:9b:14:65:35:2d:04:69:53:24:e4:c2:37:\n                    96:36:61:0b:e0:0f:2c:ac:50:85:84:ca:68:df:a4:\n                    15:2a:b0:a3:03:50:1f:11:45:d4:82:6a:02:eb:76:\n                    8b:82:1c:36:2b:8c:7e:3b:a6:12:c4:5a:8a:20:ab:\n                    2d:08:f8:49:1b:d6:f9:45:dc:d8:34:6c:8c:7b:2d:\n                    2a:8a:c6:87:d0:6a:45:20:9d:f0:43:d0:0f:0a:a2:\n                    69:d6:9b:a1:69:9c:57:e8:b2:f8:56:8f:f6:e4:24:\n                    af:c1\n                Exponent: 65537 (0x10001)\n        Attributes:\n            a0:00\n    Signature Algorithm: sha256WithRSAEncryption\n         9f:1b:09:61:fd:59:7a:99:8d:c3:f3:44:44:10:af:ac:82:f6:\n         20:5e:5e:3d:e3:07:af:e7:f6:0e:31:1f:ae:7e:bc:fd:4c:db:\n         53:8c:6b:6b:ea:76:e7:96:2c:21:f7:e7:ac:ff:ce:47:ec:e3:\n         61:c2:40:3a:0a:58:ac:94:80:c2:24:25:c3:75:82:8a:60:aa:\n         c6:20:8a:b6:28:b6:97:56:7e:0c:92:d8:da:2f:e0:0e:59:6d:\n         e1:55:b0:01:a1:e7:a9:bc:57:a1:50:de:b3:47:8a:cc:2c:44:\n         cd:5a:9b:bf:64:d3:aa:f9:b1:b2:55:db:c6:6f:5a:6c:54:19:\n         8b:4d:b2:9c:54:e0:2b:6e:c7:8c:26:d4:8d:c7:6c:43:8d:3b:\n         d1:12:87:c2:ca:ba:49:1f:93:eb:e2:8a:a9:7c:7d:e6:32:f6:\n         78:83:ab:54:9b:47:d1:c1:c2:bb:b4:25:b0:9d:bb:29:40:db:\n         30:7f:9a:4d:7a:94:5b:a0:1d:33:99:0e:9a:02:f3:4f:a4:82:\n         dd:47:15:f6:76:03:14:9f:60:9e:89:1a:4f:04:fa:a8:23:49:\n         48:af:65:bd:8c:3a:0f:77:fa:c3:86:d4:87:1a:9c:94:61:28:\n         0b:72:2e:91:98:19:0b:fe:9a:93:45:2a:92:a7:93:83:89:d9:\n         93:6b:d5:ee\n-----BEGIN CERTIFICATE REQUEST-----\nMIICVjCCAT4CAQIwETEPMA0GA1UEAwwGc2lnbmVkMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAyjxJymnkQrymATdOxmweqdOxfSC2f6l0ws4z+DKFUIrE\n2tZHLI4+754UQrNDuZ9ZyhgsMvj4XcZ0Gykn+NCQBS4DRrOiwp043gb2MFL/5SZu\niPzEIzLz0wlnHaZS8cwGKCtOr3uyUKlPf5tia1rLjPZ+ru2i8k8N0vVCLthQuA/8\nOM2AfSrY/Hbm9yiR+Vmn14GIeFx0DIKbFGU1LQRpUyTkwjeWNmEL4A8srFCFhMpo\n36QVKrCjA1AfEUXUgmoC63aLghw2K4x+O6YSxFqKIKstCPhJG9b5RdzYNGyMey0q\nisaH0GpFIJ3wQ9APCqJp1puhaZxX6LL4Vo/25CSvwQIDAQABoAAwDQYJKoZIhvcN\nAQELBQADggEBAJ8bCWH9WXqZjcPzREQQr6yC9iBeXj3jB6/n9g4xH65+vP1M21OM\na2vqdueWLCH356z/zkfs42HCQDoKWKyUgMIkJcN1gopgqsYgirYotpdWfgyS2Nov\n4A5ZbeFVsAGh56m8V6FQ3rNHiswsRM1am79k06r5sbJV28ZvWmxUGYtNspxU4Ctu\nx4wm1I3HbEONO9ESh8LKukkfk+viiql8feYy9niDq1SbR9HBwru0JbCduylA2zB/\nmk16lFugHTOZDpoC80+kgt1HFfZ2AxSfYJ6JGk8E+qgjSUivZb2MOg93+sOG1Ica\nnJRhKAtyLpGYGQv+mpNFKpKnk4OJ2ZNr1e4=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "spec/fixtures/ssl/trusted_oid_mapping.yaml",
    "content": "---\noid_mapping:\n  '1.3.6.1.4.1.34380.1.2.1.1':\n    shortname : 'myshortname'\n    longname  : 'Long name'\n"
  },
  {
    "path": "spec/fixtures/ssl/unknown-127.0.0.1-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:ca:58:a4:83:cd:f9:ba:e2:42:9e:d4:ef:52:88:\n    2e:0a:65:74:54:0e:71:82:fe:62:e7:97:91:d1:51:\n    45:12:3f:a6:5c:ba:a3:37:0d:09:c5:d3:3f:7e:24:\n    13:36:ad:be:b6:83:3e:ca:53:ac:b5:e6:e5:0f:2b:\n    68:fb:a2:1e:55:09:79:51:ad:c1:a5:30:f2:fb:ad:\n    cf:59:c1:35:3e:08:f8:a8:20:49:3f:3e:37:e9:46:\n    1a:3f:af:a2:d7:c2:57:9c:6e:2d:ef:36:8b:fa:c8:\n    83:58:ed:bb:58:33:52:42:df:93:29:26:7a:92:70:\n    42:1e:f9:80:33:a2:08:f0:b3:2e:7b:7d:9a:07:e1:\n    41:9c:c1:3c:94:52:38:e2:3a:a3:72:f3:08:75:db:\n    38:ac:9f:f0:39:e7:e3:40:e5:22:34:0f:06:3a:d6:\n    33:40:7c:0a:41:27:ef:a9:06:95:c5:33:84:7b:96:\n    a9:9e:4b:3e:c4:a9:02:ce:a8:6b:61:e4:59:bb:f2:\n    2b:6f:2e:3d:41:75:74:5d:94:3a:8d:6b:28:3e:d9:\n    10:da:09:42:a0:35:84:66:63:57:8b:b6:3a:c1:fd:\n    1b:be:56:b1:16:a3:e4:18:60:e2:70:66:63:23:21:\n    13:25:87:5f:ab:6d:07:4c:8c:62:52:ac:c7:69:4d:\n    e7:bb\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    3e:3f:50:b4:df:6b:80:cb:54:15:4a:8a:29:08:8d:\n    a9:53:16:9b:39:2a:40:53:03:98:1d:2f:95:85:fb:\n    ca:a6:5c:06:50:c0:1f:12:5a:bc:49:c5:51:87:c5:\n    85:5f:a3:b8:bf:dd:54:1e:b3:95:ed:e6:ef:68:ba:\n    4e:16:cc:5f:fa:9f:20:ba:64:44:ee:2f:01:af:b1:\n    86:fa:01:e8:08:98:7e:18:18:90:65:12:8c:27:ad:\n    b1:83:d5:83:ac:5c:89:59:f7:b5:8c:41:39:af:ef:\n    80:2e:fa:20:23:01:9e:62:eb:01:90:bd:ca:48:d0:\n    7e:78:e0:b0:81:9b:60:78:22:af:8b:88:0b:6b:85:\n    d0:8c:c9:f8:27:0e:1d:e8:22:58:08:f4:0c:01:34:\n    17:e6:33:05:39:f2:c3:03:98:89:a5:f2:7b:f6:2e:\n    09:b2:53:d1:1d:bd:dd:db:0f:03:48:d1:cb:c7:5c:\n    ad:5b:0a:01:4a:ec:a3:9b:6e:00:a2:c8:1c:86:fb:\n    96:3b:10:b7:b4:50:0a:76:e2:1e:e4:a7:41:ab:3d:\n    a6:12:4d:37:29:bd:d9:80:49:a2:e7:73:8f:09:12:\n    fd:18:9b:27:89:a9:f4:cc:da:c8:c5:bc:59:7e:94:\n    2b:7b:65:67:b7:0a:de:70:e1:be:de:55:97:4c:a9:\n    89\nprime1:\n    00:ea:69:43:3a:cb:29:35:b5:d0:4f:e6:01:2f:f3:\n    ea:9a:74:cf:66:ae:30:dd:71:91:77:48:8d:05:2f:\n    9e:70:d9:23:7f:bd:8f:f5:c9:f5:70:26:ab:99:a3:\n    c2:2c:6c:05:82:c2:99:50:ba:05:1c:18:0a:98:b0:\n    85:77:d0:fa:5b:ed:21:d3:b6:26:c4:20:e9:1f:1c:\n    ef:cd:9e:ac:56:87:61:a0:92:83:45:7d:fa:31:5f:\n    71:a8:0e:67:94:3e:57:cf:17:4b:c6:ad:04:d6:3f:\n    a9:57:7e:e5:1f:cb:88:ac:96:59:67:0f:a4:84:c2:\n    70:59:81:0b:42:71:e3:fd:c7\nprime2:\n    00:dc:fb:61:c7:48:8f:0a:d8:e7:03:7f:e4:a4:a8:\n    31:3a:24:c9:97:47:87:69:2b:ea:c4:6c:fb:20:56:\n    72:f9:c4:7b:b4:fd:62:f6:65:23:4c:7d:2d:af:a8:\n    ca:4c:71:a0:04:e3:b3:f6:01:44:40:68:85:80:99:\n    47:5c:65:ec:0d:3b:53:f0:e7:f6:f0:f8:c4:00:da:\n    69:d4:9c:e2:ba:04:bd:2b:b8:58:04:e9:5b:e1:9b:\n    81:a3:76:55:c4:9d:50:05:53:af:d4:cf:d3:fc:20:\n    93:7c:7f:7f:65:1a:47:54:5c:d3:5f:b6:8e:a3:82:\n    6e:36:4e:35:36:d4:36:56:6d\nexponent1:\n    00:a5:4d:e5:e3:28:31:bf:76:9f:98:38:92:cd:c6:\n    2c:c3:0f:6c:d2:f4:33:f1:75:8d:27:99:3a:19:56:\n    23:5d:61:42:ef:6a:36:83:16:10:c7:2d:fe:05:32:\n    be:53:a5:39:9d:a4:ad:89:88:24:e2:52:f1:e6:0b:\n    55:93:d1:03:3c:a0:55:22:7f:69:87:15:ce:4f:ea:\n    90:11:68:bc:0f:a9:18:e7:ab:6a:77:2f:07:a8:99:\n    ae:04:dd:63:9d:de:f8:fd:49:68:5e:8a:d4:c6:61:\n    ce:81:ad:32:d6:e1:29:58:cc:0d:ef:e7:d0:14:ef:\n    29:5b:74:ed:03:c6:5d:09:31\nexponent2:\n    00:a3:cc:69:e3:cf:b2:c7:b5:93:37:12:db:a5:f9:\n    4d:d3:ed:64:c8:0f:ab:1d:98:02:02:eb:4d:11:e1:\n    42:84:44:d1:f5:8c:44:88:a2:db:11:5d:50:39:fe:\n    81:45:3c:8c:02:53:ce:17:31:45:28:00:d1:c4:4a:\n    d9:8c:71:b8:10:ee:c2:ff:b4:d1:64:38:e1:00:48:\n    e5:5d:45:95:01:91:75:af:2c:8b:81:c8:7f:e4:b0:\n    e1:dd:0a:5f:f4:c7:2e:83:64:4e:d7:0d:dd:f1:eb:\n    2b:a7:82:d2:29:5c:db:36:7b:3f:ea:98:65:0e:6a:\n    25:cb:77:19:16:b5:22:39:d9\ncoefficient:\n    0f:73:98:6f:8c:82:a4:80:95:b2:88:f5:52:34:ce:\n    da:16:a0:6b:6b:01:d5:d9:fb:d1:80:50:c2:7c:b2:\n    c7:f2:c2:51:2c:99:b2:18:cf:6b:29:b9:b5:14:9e:\n    ea:e0:4d:db:f1:c2:81:3e:88:3f:fc:3d:8f:c2:5e:\n    c7:eb:cc:68:f3:5e:48:ed:11:f8:65:01:b4:a7:6b:\n    75:54:6e:67:7a:4b:8c:ed:32:11:ec:d1:91:7e:e8:\n    7a:6d:24:b7:7e:51:ae:e1:1a:84:49:10:41:4d:9c:\n    63:25:5b:8d:50:f0:01:90:02:34:01:9d:3e:9b:ea:\n    8c:2b:5d:d6:51:06:39:71\n-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAylikg835uuJCntTvUoguCmV0VA5xgv5i55eR0VFFEj+mXLqj\nNw0JxdM/fiQTNq2+toM+ylOsteblDyto+6IeVQl5Ua3BpTDy+63PWcE1Pgj4qCBJ\nPz436UYaP6+i18JXnG4t7zaL+siDWO27WDNSQt+TKSZ6knBCHvmAM6II8LMue32a\nB+FBnME8lFI44jqjcvMIdds4rJ/wOefjQOUiNA8GOtYzQHwKQSfvqQaVxTOEe5ap\nnks+xKkCzqhrYeRZu/Irby49QXV0XZQ6jWsoPtkQ2glCoDWEZmNXi7Y6wf0bvlax\nFqPkGGDicGZjIyETJYdfq20HTIxiUqzHaU3nuwIDAQABAoIBAD4/ULTfa4DLVBVK\niikIjalTFps5KkBTA5gdL5WF+8qmXAZQwB8SWrxJxVGHxYVfo7i/3VQes5Xt5u9o\nuk4WzF/6nyC6ZETuLwGvsYb6AegImH4YGJBlEownrbGD1YOsXIlZ97WMQTmv74Au\n+iAjAZ5i6wGQvcpI0H544LCBm2B4Iq+LiAtrhdCMyfgnDh3oIlgI9AwBNBfmMwU5\n8sMDmIml8nv2LgmyU9Edvd3bDwNI0cvHXK1bCgFK7KObbgCiyByG+5Y7ELe0UAp2\n4h7kp0GrPaYSTTcpvdmASaLnc48JEv0YmyeJqfTM2sjFvFl+lCt7ZWe3Ct5w4b7e\nVZdMqYkCgYEA6mlDOsspNbXQT+YBL/PqmnTPZq4w3XGRd0iNBS+ecNkjf72P9cn1\ncCarmaPCLGwFgsKZULoFHBgKmLCFd9D6W+0h07YmxCDpHxzvzZ6sVodhoJKDRX36\nMV9xqA5nlD5XzxdLxq0E1j+pV37lH8uIrJZZZw+khMJwWYELQnHj/ccCgYEA3Pth\nx0iPCtjnA3/kpKgxOiTJl0eHaSvqxGz7IFZy+cR7tP1i9mUjTH0tr6jKTHGgBOOz\n9gFEQGiFgJlHXGXsDTtT8Of28PjEANpp1JziugS9K7hYBOlb4ZuBo3ZVxJ1QBVOv\n1M/T/CCTfH9/ZRpHVFzTX7aOo4JuNk41NtQ2Vm0CgYEApU3l4ygxv3afmDiSzcYs\nww9s0vQz8XWNJ5k6GVYjXWFC72o2gxYQxy3+BTK+U6U5naStiYgk4lLx5gtVk9ED\nPKBVIn9phxXOT+qQEWi8D6kY56tqdy8HqJmuBN1jnd74/UloXorUxmHOga0y1uEp\nWMwN7+fQFO8pW3TtA8ZdCTECgYEAo8xp48+yx7WTNxLbpflN0+1kyA+rHZgCAutN\nEeFChETR9YxEiKLbEV1QOf6BRTyMAlPOFzFFKADRxErZjHG4EO7C/7TRZDjhAEjl\nXUWVAZF1ryyLgch/5LDh3Qpf9Mcug2RO1w3d8esrp4LSKVzbNns/6phlDmoly3cZ\nFrUiOdkCgYAPc5hvjIKkgJWyiPVSNM7aFqBrawHV2fvRgFDCfLLH8sJRLJmyGM9r\nKbm1FJ7q4E3b8cKBPog//D2Pwl7H68xo815I7RH4ZQG0p2t1VG5nekuM7TIR7NGR\nfuh6bSS3flGu4RqESRBBTZxjJVuNUPABkAI0AZ0+m+qMK13WUQY5cQ==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/unknown-127.0.0.1.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 1 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Unknown CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=127.0.0.1\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:ca:58:a4:83:cd:f9:ba:e2:42:9e:d4:ef:52:88:\n                    2e:0a:65:74:54:0e:71:82:fe:62:e7:97:91:d1:51:\n                    45:12:3f:a6:5c:ba:a3:37:0d:09:c5:d3:3f:7e:24:\n                    13:36:ad:be:b6:83:3e:ca:53:ac:b5:e6:e5:0f:2b:\n                    68:fb:a2:1e:55:09:79:51:ad:c1:a5:30:f2:fb:ad:\n                    cf:59:c1:35:3e:08:f8:a8:20:49:3f:3e:37:e9:46:\n                    1a:3f:af:a2:d7:c2:57:9c:6e:2d:ef:36:8b:fa:c8:\n                    83:58:ed:bb:58:33:52:42:df:93:29:26:7a:92:70:\n                    42:1e:f9:80:33:a2:08:f0:b3:2e:7b:7d:9a:07:e1:\n                    41:9c:c1:3c:94:52:38:e2:3a:a3:72:f3:08:75:db:\n                    38:ac:9f:f0:39:e7:e3:40:e5:22:34:0f:06:3a:d6:\n                    33:40:7c:0a:41:27:ef:a9:06:95:c5:33:84:7b:96:\n                    a9:9e:4b:3e:c4:a9:02:ce:a8:6b:61:e4:59:bb:f2:\n                    2b:6f:2e:3d:41:75:74:5d:94:3a:8d:6b:28:3e:d9:\n                    10:da:09:42:a0:35:84:66:63:57:8b:b6:3a:c1:fd:\n                    1b:be:56:b1:16:a3:e4:18:60:e2:70:66:63:23:21:\n                    13:25:87:5f:ab:6d:07:4c:8c:62:52:ac:c7:69:4d:\n                    e7:bb\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Subject Alternative Name: \n                DNS:127.0.0.1, DNS:127.0.0.2\n    Signature Algorithm: sha256WithRSAEncryption\n         94:63:d8:4e:ec:a1:98:49:08:5d:b1:14:e4:2e:db:1d:0f:f2:\n         88:0a:be:f4:2a:23:0f:80:f7:3a:93:c2:b1:7a:0e:c7:93:23:\n         82:24:87:f5:65:9c:18:63:51:b5:9e:0d:b2:cd:9d:13:01:83:\n         f6:f2:d4:94:c5:00:97:9e:72:6e:3b:29:7f:4e:73:72:52:2c:\n         e5:8f:ce:f3:18:08:8b:fc:dc:64:c1:f5:2f:5b:56:29:89:b4:\n         74:d6:66:3e:f7:f0:18:67:a8:c8:6e:fc:bc:ff:35:28:6f:85:\n         52:10:e2:fd:c4:87:f3:18:20:40:ea:e3:e6:11:6d:4c:c0:d4:\n         e7:05:d5:02:09:02:43:15:4a:d5:b6:db:97:ba:d5:31:00:68:\n         9f:20:34:59:77:33:e5:5d:c1:f3:4b:78:d7:d2:83:aa:a6:cc:\n         60:cf:79:27:b1:18:9b:09:64:61:41:0b:23:7a:c4:38:53:05:\n         e7:55:d7:26:2f:7b:f9:36:8d:82:2c:f1:bf:89:c6:cf:b9:30:\n         d9:42:05:83:43:91:cc:36:0c:e1:74:af:6d:1c:fb:38:77:c6:\n         cb:46:9d:63:df:9e:f8:4e:a5:40:ba:6c:14:01:0e:72:7c:87:\n         62:06:ba:1f:25:2b:ce:22:be:8d:8f:d2:45:d6:45:6f:d3:7e:\n         cd:fa:34:40\n-----BEGIN CERTIFICATE-----\nMIICxzCCAa+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDApVbmtu\nb3duIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowFDESMBAGA1UE\nAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylik\ng835uuJCntTvUoguCmV0VA5xgv5i55eR0VFFEj+mXLqjNw0JxdM/fiQTNq2+toM+\nylOsteblDyto+6IeVQl5Ua3BpTDy+63PWcE1Pgj4qCBJPz436UYaP6+i18JXnG4t\n7zaL+siDWO27WDNSQt+TKSZ6knBCHvmAM6II8LMue32aB+FBnME8lFI44jqjcvMI\ndds4rJ/wOefjQOUiNA8GOtYzQHwKQSfvqQaVxTOEe5apnks+xKkCzqhrYeRZu/Ir\nby49QXV0XZQ6jWsoPtkQ2glCoDWEZmNXi7Y6wf0bvlaxFqPkGGDicGZjIyETJYdf\nq20HTIxiUqzHaU3nuwIDAQABoyMwITAfBgNVHREEGDAWggkxMjcuMC4wLjGCCTEy\nNy4wLjAuMjANBgkqhkiG9w0BAQsFAAOCAQEAlGPYTuyhmEkIXbEU5C7bHQ/yiAq+\n9CojD4D3OpPCsXoOx5MjgiSH9WWcGGNRtZ4Nss2dEwGD9vLUlMUAl55ybjspf05z\nclIs5Y/O8xgIi/zcZMH1L1tWKYm0dNZmPvfwGGeoyG78vP81KG+FUhDi/cSH8xgg\nQOrj5hFtTMDU5wXVAgkCQxVK1bbbl7rVMQBonyA0WXcz5V3B80t419KDqqbMYM95\nJ7EYmwlkYUELI3rEOFMF51XXJi97+TaNgizxv4nGz7kw2UIFg0ORzDYM4XSvbRz7\nOHfGy0adY9+e+E6lQLpsFAEOcnyHYga6HyUrziK+jY/SRdZFb9N+zfo0QA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/ssl/unknown-ca-key.pem",
    "content": "RSA Private-Key: (2048 bit, 2 primes)\nmodulus:\n    00:ae:4c:42:b8:da:d0:6f:11:a4:ba:9c:ba:6b:dd:\n    7a:dd:8d:5f:8b:51:e9:b2:85:4d:9a:26:41:1d:8e:\n    52:2f:19:ed:79:6a:b7:04:f7:8e:9c:82:37:04:26:\n    71:10:3f:98:e6:a3:0c:58:fb:00:84:4f:d0:8d:fa:\n    50:2c:0c:59:1d:48:7d:de:af:5e:b4:27:09:f2:b8:\n    33:18:56:cb:40:9f:09:a7:61:08:88:ed:be:2d:96:\n    9c:03:a3:1a:ad:61:f6:e3:98:23:90:90:5c:66:90:\n    25:06:87:62:4b:f9:87:e6:dd:08:c3:e0:7a:67:3c:\n    1f:f9:cb:99:ad:53:dc:1d:9b:8f:85:0b:84:78:13:\n    95:71:a8:6a:b2:13:20:19:b6:b6:63:5e:3f:68:14:\n    1b:67:c1:75:cf:16:d1:26:c0:94:24:89:ce:3c:b3:\n    6d:05:4d:10:52:40:73:c4:7b:ac:f1:dd:64:6a:f0:\n    62:36:c6:08:17:03:64:d9:fb:1c:e2:99:25:2b:df:\n    47:fc:f8:c9:07:f3:84:96:da:19:27:25:1b:b6:ab:\n    be:0c:93:54:df:0d:2e:38:ab:da:e7:1c:56:86:0c:\n    4b:0a:96:46:30:8f:7c:ab:be:b0:22:6b:92:76:c6:\n    56:32:43:54:ab:18:25:5a:6c:09:05:ba:ca:74:d8:\n    86:1f\npublicExponent: 65537 (0x10001)\nprivateExponent:\n    5e:f6:c7:e7:a5:b8:a8:bb:49:30:2f:92:56:90:be:\n    8f:95:a6:37:e7:32:58:04:cf:2a:2c:ab:8f:a9:ea:\n    57:25:5e:40:a8:06:fd:9a:cf:c5:b0:20:bf:8f:1e:\n    4d:07:09:8c:a7:cb:63:73:a6:6d:70:7b:25:cf:fa:\n    fc:74:e1:dc:d5:91:56:d1:df:dc:71:e4:b6:ac:eb:\n    91:d8:40:37:7e:2f:29:55:f1:eb:80:f4:fc:2b:b1:\n    e7:3d:67:9c:8d:e2:91:50:64:18:30:bf:57:56:34:\n    06:21:9f:49:db:b3:d2:f2:1d:03:73:fb:dc:e1:62:\n    5e:9c:32:a7:c0:28:0e:ac:bb:46:cf:cb:d2:27:2e:\n    af:6d:14:da:ab:41:49:f0:36:04:a6:4f:1a:a2:1f:\n    48:d1:1c:b5:fc:21:aa:85:9b:5b:42:9d:26:a4:89:\n    92:a5:dd:11:cf:06:61:16:56:3e:2f:1c:c3:d5:df:\n    43:41:2a:cf:ea:e1:bc:e3:47:b3:a1:68:61:d8:a5:\n    19:9a:68:dd:6c:89:2b:56:d0:d3:de:7c:dd:0f:e5:\n    53:da:eb:b1:3e:24:11:a4:60:c7:bb:f1:0e:46:b2:\n    32:62:65:ad:28:dd:0e:29:d9:14:e6:33:cb:8b:69:\n    64:89:c6:90:8f:e3:eb:b4:80:79:70:3f:fb:64:f2:\n    91\nprime1:\n    00:e2:28:0b:9f:48:71:4e:64:49:0e:17:fa:84:95:\n    e8:c1:1c:65:f9:7f:55:65:45:90:3d:27:d4:38:aa:\n    8d:5d:ab:83:79:b9:75:96:86:88:c2:0f:a9:d8:2a:\n    0c:de:8b:d3:66:48:54:89:ee:02:3b:06:1c:22:1c:\n    c8:9a:6f:9b:b8:47:a7:be:86:e6:de:e7:b8:93:45:\n    c5:20:3a:59:eb:49:12:63:8a:73:91:18:70:8a:21:\n    c3:76:62:1c:7b:c6:93:ee:9c:5b:41:fd:58:2b:d8:\n    bd:53:1e:6a:e6:77:45:72:8c:95:b1:37:5c:a5:d0:\n    80:eb:20:76:c5:cb:64:0f:27\nprime2:\n    00:c5:4c:58:2b:16:b4:a5:99:47:71:40:74:9e:e3:\n    ea:2d:6c:1a:f4:1b:23:33:01:28:e2:f8:6d:3f:d0:\n    d3:90:db:ef:52:04:0e:b9:cd:39:ee:10:07:51:4e:\n    28:b1:96:84:32:52:74:04:8e:04:ce:5b:1d:dd:a4:\n    e4:87:bf:80:6c:f9:c6:7b:6a:19:b9:d5:66:52:20:\n    7a:f1:43:59:2e:aa:eb:d2:32:74:8a:2b:03:a4:74:\n    e9:1b:48:5c:b3:55:f9:2a:0b:03:e7:80:fe:63:7e:\n    4a:a9:83:ff:3b:a5:b0:ac:96:08:86:67:fa:78:a6:\n    db:c7:ae:e6:d1:c1:0e:ac:49\nexponent1:\n    00:b8:4f:d0:a1:00:a6:2f:30:36:05:c1:6f:0e:cd:\n    29:c2:f0:44:ff:60:52:15:55:eb:26:9c:26:2e:04:\n    79:6f:a4:8f:63:0d:cd:92:5e:94:68:2b:e1:cc:e5:\n    00:56:02:f8:c7:bc:1d:01:c8:32:93:2d:f8:91:a8:\n    89:a8:ab:5a:ea:85:a3:64:f8:86:81:95:b1:ec:7c:\n    89:1a:29:d4:0c:98:21:df:73:ff:99:79:a4:86:3e:\n    dc:10:c8:06:d4:ad:52:f4:bd:02:f6:b5:e5:3c:de:\n    c7:f5:4c:ad:ec:a3:b9:ba:90:6e:92:de:3e:a8:78:\n    54:08:cc:0d:60:47:d1:85:33\nexponent2:\n    6d:a8:2e:04:dc:dc:d0:81:6d:d4:c3:37:4e:2c:6c:\n    b6:8c:34:3a:e2:6e:60:e5:cf:1c:bf:68:b1:24:56:\n    c2:57:8b:1f:31:84:21:be:af:e8:e1:dd:bf:51:ca:\n    8a:51:96:ee:05:27:d8:74:3a:b5:9a:ac:f1:c8:b8:\n    ff:bd:ef:1a:22:85:2f:88:db:fd:8e:5f:0d:5c:62:\n    18:80:0f:2c:41:f1:49:e5:a3:22:3c:20:0d:22:b1:\n    80:e9:6a:24:7e:af:3a:af:de:9e:a7:f2:3b:c8:30:\n    a5:20:e9:15:f4:d4:4a:48:25:3a:d6:c5:be:93:36:\n    ea:38:0a:b0:20:36:10:89\ncoefficient:\n    5f:bb:2d:4b:b2:dc:11:ea:bf:76:aa:61:f0:8c:ba:\n    59:71:21:af:d8:36:7a:4f:eb:9f:ba:a3:cd:75:1c:\n    80:8d:1c:f8:d6:14:18:7e:dc:22:a2:8a:f5:f3:bf:\n    09:d0:5c:b6:a8:21:8a:eb:ce:f0:d1:10:46:ca:62:\n    8b:5d:39:cf:58:cf:cf:52:14:6d:43:ac:a4:3c:97:\n    2e:11:f0:d9:ff:8b:61:f4:06:f5:fc:b7:e2:eb:5f:\n    f0:5b:67:71:16:53:e7:32:f8:88:20:dd:e9:12:2f:\n    5d:a3:7f:2d:26:83:13:ff:67:95:4d:5f:60:62:95:\n    a2:85:f5:f4:e5:7c:57:f8\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArkxCuNrQbxGkupy6a9163Y1fi1HpsoVNmiZBHY5SLxnteWq3\nBPeOnII3BCZxED+Y5qMMWPsAhE/QjfpQLAxZHUh93q9etCcJ8rgzGFbLQJ8Jp2EI\niO2+LZacA6MarWH245gjkJBcZpAlBodiS/mH5t0Iw+B6Zzwf+cuZrVPcHZuPhQuE\neBOVcahqshMgGba2Y14/aBQbZ8F1zxbRJsCUJInOPLNtBU0QUkBzxHus8d1kavBi\nNsYIFwNk2fsc4pklK99H/PjJB/OEltoZJyUbtqu+DJNU3w0uOKva5xxWhgxLCpZG\nMI98q76wImuSdsZWMkNUqxglWmwJBbrKdNiGHwIDAQABAoIBAF72x+eluKi7STAv\nklaQvo+VpjfnMlgEzyosq4+p6lclXkCoBv2az8WwIL+PHk0HCYyny2Nzpm1weyXP\n+vx04dzVkVbR39xx5Las65HYQDd+LylV8euA9Pwrsec9Z5yN4pFQZBgwv1dWNAYh\nn0nbs9LyHQNz+9zhYl6cMqfAKA6su0bPy9InLq9tFNqrQUnwNgSmTxqiH0jRHLX8\nIaqFm1tCnSakiZKl3RHPBmEWVj4vHMPV30NBKs/q4bzjR7OhaGHYpRmaaN1siStW\n0NPefN0P5VPa67E+JBGkYMe78Q5GsjJiZa0o3Q4p2RTmM8uLaWSJxpCP4+u0gHlw\nP/tk8pECgYEA4igLn0hxTmRJDhf6hJXowRxl+X9VZUWQPSfUOKqNXauDebl1loaI\nwg+p2CoM3ovTZkhUie4COwYcIhzImm+buEenvobm3ue4k0XFIDpZ60kSY4pzkRhw\niiHDdmIce8aT7pxbQf1YK9i9Ux5q5ndFcoyVsTdcpdCA6yB2xctkDycCgYEAxUxY\nKxa0pZlHcUB0nuPqLWwa9BsjMwEo4vhtP9DTkNvvUgQOuc057hAHUU4osZaEMlJ0\nBI4Ezlsd3aTkh7+AbPnGe2oZudVmUiB68UNZLqrr0jJ0iisDpHTpG0hcs1X5KgsD\n54D+Y35KqYP/O6WwrJYIhmf6eKbbx67m0cEOrEkCgYEAuE/QoQCmLzA2BcFvDs0p\nwvBE/2BSFVXrJpwmLgR5b6SPYw3Nkl6UaCvhzOUAVgL4x7wdAcgyky34kaiJqKta\n6oWjZPiGgZWx7HyJGinUDJgh33P/mXmkhj7cEMgG1K1S9L0C9rXlPN7H9Uyt7KO5\nupBukt4+qHhUCMwNYEfRhTMCgYBtqC4E3NzQgW3UwzdOLGy2jDQ64m5g5c8cv2ix\nJFbCV4sfMYQhvq/o4d2/UcqKUZbuBSfYdDq1mqzxyLj/ve8aIoUviNv9jl8NXGIY\ngA8sQfFJ5aMiPCANIrGA6Wokfq86r96ep/I7yDClIOkV9NRKSCU61sW+kzbqOAqw\nIDYQiQKBgF+7LUuy3BHqv3aqYfCMullxIa/YNnpP65+6o811HICNHPjWFBh+3CKi\nivXzvwnQXLaoIYrrzvDREEbKYotdOc9Yz89SFG1DrKQ8ly4R8Nn/i2H0BvX8t+Lr\nX/BbZ3EWU+cy+Igg3ekSL12jfy0mgxP/Z5VNX2BilaKF9fTlfFf4\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "spec/fixtures/ssl/unknown-ca.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 0 (0x0)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=Unknown CA\n        Validity\n            Not Before: Jan  1 00:00:00 1970 GMT\n            Not After : Jan 27 18:43:32 2034 GMT\n        Subject: CN=Unknown CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:ae:4c:42:b8:da:d0:6f:11:a4:ba:9c:ba:6b:dd:\n                    7a:dd:8d:5f:8b:51:e9:b2:85:4d:9a:26:41:1d:8e:\n                    52:2f:19:ed:79:6a:b7:04:f7:8e:9c:82:37:04:26:\n                    71:10:3f:98:e6:a3:0c:58:fb:00:84:4f:d0:8d:fa:\n                    50:2c:0c:59:1d:48:7d:de:af:5e:b4:27:09:f2:b8:\n                    33:18:56:cb:40:9f:09:a7:61:08:88:ed:be:2d:96:\n                    9c:03:a3:1a:ad:61:f6:e3:98:23:90:90:5c:66:90:\n                    25:06:87:62:4b:f9:87:e6:dd:08:c3:e0:7a:67:3c:\n                    1f:f9:cb:99:ad:53:dc:1d:9b:8f:85:0b:84:78:13:\n                    95:71:a8:6a:b2:13:20:19:b6:b6:63:5e:3f:68:14:\n                    1b:67:c1:75:cf:16:d1:26:c0:94:24:89:ce:3c:b3:\n                    6d:05:4d:10:52:40:73:c4:7b:ac:f1:dd:64:6a:f0:\n                    62:36:c6:08:17:03:64:d9:fb:1c:e2:99:25:2b:df:\n                    47:fc:f8:c9:07:f3:84:96:da:19:27:25:1b:b6:ab:\n                    be:0c:93:54:df:0d:2e:38:ab:da:e7:1c:56:86:0c:\n                    4b:0a:96:46:30:8f:7c:ab:be:b0:22:6b:92:76:c6:\n                    56:32:43:54:ab:18:25:5a:6c:09:05:ba:ca:74:d8:\n                    86:1f\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Subject Key Identifier: \n                62:4A:BC:91:9E:B7:40:6C:32:53:82:EC:19:CC:08:28:2D:6A:90:6F\n            Netscape Comment: \n                Puppet Server Internal Certificate\n            X509v3 Authority Key Identifier: \n                keyid:62:4A:BC:91:9E:B7:40:6C:32:53:82:EC:19:CC:08:28:2D:6A:90:6F\n\n    Signature Algorithm: sha256WithRSAEncryption\n         47:5d:e0:f5:db:72:f1:c7:94:1b:3e:1c:8f:75:aa:8c:f0:53:\n         f0:2f:82:d6:8c:6f:67:3f:39:6d:9a:0c:73:18:87:8d:53:91:\n         18:0e:94:86:22:26:1c:81:f4:71:79:c4:38:9e:55:5e:26:e1:\n         69:05:43:3e:00:f8:c3:22:81:f9:a3:d4:43:13:8b:f2:76:b5:\n         c8:a7:f7:bb:0e:14:78:e7:29:8a:19:35:95:3a:99:c8:5b:6c:\n         ed:57:df:78:ea:0a:05:5d:2a:f3:c5:56:32:fa:2f:ec:1e:69:\n         30:ab:87:79:a8:89:f3:ca:f1:17:b2:94:d8:a5:03:92:07:55:\n         be:26:c7:2f:96:f8:e5:67:81:8e:63:d8:2b:4e:54:5e:b4:34:\n         e8:af:6b:ab:34:78:bc:1c:0b:71:60:23:0f:2e:b6:c8:83:a3:\n         ed:77:8d:76:66:8b:fd:07:d8:7b:3d:76:a6:2c:2d:71:66:86:\n         d1:cc:89:ed:95:fa:e7:dc:57:3f:14:20:08:4b:44:96:2f:c3:\n         f5:42:19:a4:06:5e:11:85:13:62:b8:6b:53:c0:e9:aa:56:41:\n         a4:eb:6b:00:4e:c6:85:77:9c:07:83:96:cb:a4:e6:89:3f:5e:\n         78:c2:f8:f3:dc:d4:0b:ef:5e:79:23:22:07:c1:fb:c4:7f:98:\n         8a:fe:10:cc\n-----BEGIN CERTIFICATE-----\nMIIDPTCCAiWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDApVbmtu\nb3duIENBMB4XDTcwMDEwMTAwMDAwMFoXDTM0MDEyNzE4NDMzMlowFTETMBEGA1UE\nAwwKVW5rbm93biBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5M\nQrja0G8RpLqcumvdet2NX4tR6bKFTZomQR2OUi8Z7XlqtwT3jpyCNwQmcRA/mOaj\nDFj7AIRP0I36UCwMWR1Ifd6vXrQnCfK4MxhWy0CfCadhCIjtvi2WnAOjGq1h9uOY\nI5CQXGaQJQaHYkv5h+bdCMPgemc8H/nLma1T3B2bj4ULhHgTlXGoarITIBm2tmNe\nP2gUG2fBdc8W0SbAlCSJzjyzbQVNEFJAc8R7rPHdZGrwYjbGCBcDZNn7HOKZJSvf\nR/z4yQfzhJbaGSclG7arvgyTVN8NLjir2uccVoYMSwqWRjCPfKu+sCJrknbGVjJD\nVKsYJVpsCQW6ynTYhh8CAwEAAaOBlzCBlDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud\nDwEB/wQEAwIBBjAdBgNVHQ4EFgQUYkq8kZ63QGwyU4LsGcwIKC1qkG8wMQYJYIZI\nAYb4QgENBCQWIlB1cHBldCBTZXJ2ZXIgSW50ZXJuYWwgQ2VydGlmaWNhdGUwHwYD\nVR0jBBgwFoAUYkq8kZ63QGwyU4LsGcwIKC1qkG8wDQYJKoZIhvcNAQELBQADggEB\nAEdd4PXbcvHHlBs+HI91qozwU/AvgtaMb2c/OW2aDHMYh41TkRgOlIYiJhyB9HF5\nxDieVV4m4WkFQz4A+MMigfmj1EMTi/J2tcin97sOFHjnKYoZNZU6mchbbO1X33jq\nCgVdKvPFVjL6L+weaTCrh3moifPK8ReylNilA5IHVb4mxy+W+OVngY5j2CtOVF60\nNOiva6s0eLwcC3FgIw8utsiDo+13jXZmi/0H2Hs9dqYsLXFmhtHMie2V+ufcVz8U\nIAhLRJYvw/VCGaQGXhGFE2K4a1PA6apWQaTrawBOxoV3nAeDlsuk5ok/XnjC+PPc\n1AvvXnkjIgfB+8R/mIr+EMw=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/unit/application/environments/production/data/common.yaml",
    "content": "---\na: This is A\nb: This is B\nc: \"This is%{cx}\"\n\nd:\n  one:\n    two:\n      three: the value\n\ne.one.two.three: the value\n\nf.one:\n  two.three:\n    - first value\n    - second value\n    - third value\n\nab: \"%{hiera('a')} and %{hiera('b')}\"\n\ng: \"This is%{facts.cx} in facts hash\"\n\nh: \"server version is %{server_facts.serverversion}\"\n\nlookup_options:\n  a: first\n"
  },
  {
    "path": "spec/fixtures/unit/application/environments/production/environment.conf",
    "content": "environment_data_provider = 'hiera'"
  },
  {
    "path": "spec/fixtures/unit/application/environments/production/manifests/site.pp",
    "content": "$cx = ' C from site.pp'\n"
  },
  {
    "path": "spec/fixtures/unit/application/environments/puppet_func_provider/environment.conf",
    "content": "environment_data_provider = 'function'\n"
  },
  {
    "path": "spec/fixtures/unit/application/environments/puppet_func_provider/functions/environment/data.pp",
    "content": "function environment::data() {\n  {\n     a => 'This is A',\n     b => 'This is B',\n     c => \"This is ${if $cx == undef { 'C from data.pp' } else { $cx }}\",\n     lookup_options => {\n        a => 'first'\n     }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/application/environments/puppet_func_provider/manifests/site.pp",
    "content": "$cx = 'C from site.pp'\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_json/data/bad.json",
    "content": "{\n    \"test::param_a\": \"env data param_a is 10\",\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_json/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_json/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"bad\"\n    :backend: json\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_json/manifests/site.pp",
    "content": "class test($param_a) {\n  notify { \"$param_a\": }\n}\n\ninclude test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_yaml/data/bad.yaml",
    "content": "---\n {\none::test::param_c: env data param_c is 300\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_yaml/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_yaml/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"bad\"\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_bad_syntax_yaml/manifests/site.pp",
    "content": "class test($param_a) {\n  notify { \"$param_a\": }\n}\n\ninclude test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_defaults/data/common.yaml",
    "content": "---\none::test::param_c: env data param_c is 300\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_defaults/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_defaults/manifests/site.pp",
    "content": "include one::test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_defaults/modules/one/data/common.yaml",
    "content": "---\none::test::param_a: module data param_a is 100\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_defaults/modules/one/manifests/init.pp",
    "content": "class one {\n  class test($param_a = \"param default is 100\", $param_b = \"param default is 200\", $param_c = \"param default is 300\") {\n    notify { \"$param_a, $param_b, $param_c\": }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_defaults/modules/one/metadata.json",
    "content": "{\n    \"name\": \"example/one\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-one.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/first.json",
    "content": "{\n    \"test::param_a\": \"env data param_a is 10\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/name.yaml",
    "content": "---\ntest::param_b: env data param_b is 20\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/second.json",
    "content": "{\n    \"test::param_c\": \"env data param_c is 30\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/single.yaml",
    "content": "---\ntest::param_d: env data param_d is 40\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/third_utf8.json",
    "content": "{\n    \"test::param_json_utf8\": \"env data param_json_utf8 is ᚠᛇᚻ\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/utf8.yaml",
    "content": "---\ntest::param_yaml_utf8: env data param_yaml_utf8 is ᛫ᛒᛦ\n\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/data2/single.yaml",
    "content": "---\ntest::param_e: env data param_e is 50\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"name\"\n    :backend: yaml\n  - :name: \"one path\"\n    :backend: yaml\n    :path: \"single\"\n  - :name: \"utf8\"\n    :backend: yaml\n    :path: \"utf8\"\n  - :name: \"three paths\"\n    :backend: json\n    :paths:\n      - \"first\"\n      - \"second\"\n      - \"third_utf8\"\n  - :name:  'other datadir'\n    :backend: yaml\n    :datadir: \"data2\"\n    :path: \"single\"\n:datadir: \"data1\"\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_env_config/manifests/site.pp",
    "content": "class test($param_a = 1, $param_b = 2, $param_c = 3, $param_d = 4, $param_e = 5, $param_yaml_utf8 = 'hi', $param_json_utf8 = 'hi') {\n  notify { \"$param_a, $param_b, $param_c, $param_d, $param_e, $param_yaml_utf8, $param_json_utf8\": }\n}\n\ninclude test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_misc/data/common.yaml",
    "content": "---\nlookup_options:\n  one::lopts_test::hash:\n    merge: deep\n\none::test::param:\n  key1: env 1\n\none::array:\n  - second\n  - third\n  - fourth\n\none::lopts_test::hash:\n  a: A\n  b: B\n  m:\n    ma: MA\n    mb: MB\n\none::loptsm_test::hash:\n  a: A\n  b: B\n  m:\n    ma: MA\n    mb: MB\n\nukey1: Some value\n\ntarget_lookup: with lookup\ntarget_hiera: with hiera\ntarget_alias: Value from interpolation with alias\n\nkm_default: 'Value from interpolation %{target_default}'\nkm_alias: '%{alias(\"target_alias\")}'\nkm_lookup: 'Value from interpolation %{lookup(\"target_lookup\")}'\nkm_hiera: 'Value from interpolation %{hiera(\"target_hiera\")}'\nkm_scope: 'Value from interpolation %{scope(\"target_scope\")}'\nkm_literal: 'Value from interpolation %{literal(\"with literal\")}'\n\nkm_sqalias: \"%{alias('target_alias')}\"\nrecursive: '%{r1}'\n\nkm_qualified: \"Value from qualified interpolation OS = %{os.name}\"\n\nkm_multi: \"cluster/%{literal('%')}{::cluster}/%{literal('%')}{role}\"\n\ndomain: \"-- %{domain} --\"\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_misc/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_misc/manifests/site.pp",
    "content": "include one::test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_misc/modules/one/data/common.yaml",
    "content": "---\none::test::param:\n  key2: module 2\n\none::array:\n  - first\n  - second\n  - third\n\none::lopts_test::hash:\n  a: A\n  c: C\n  m:\n    ma: MA\n    mc: MC\n\none::loptsm_test::hash:\n  a: A\n  c: C\n  m:\n    ma: MA\n    mc: MC\n\nlookup_options:\n  one::loptsm_test::hash:\n    merge: deep\n\n\n# should not be found since it's not qualified with module name\nukey2: Some value\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_misc/modules/one/manifests/init.pp",
    "content": "class one {\n  class test($param = { key1 => 'default 1', key2 => 'default 2' }) {\n    notify { \"${param[key1]}, ${param[key2]}\": }\n  }\n\n  class lopts_test($hash = { a => 'default A', b => 'default B', c => 'default C', m => {} }) {\n    notify { \"${hash[a]}, ${hash[b]}, ${hash[c]}, ${hash[m]['ma']}, ${hash[m]['mb']}, ${hash[m]['mc']}\": }\n  }\n\n  class loptsm_test($hash = { a => 'default A', b => 'default B', c => 'default C', m => {} }) {\n    notify { \"${hash[a]}, ${hash[b]}, ${hash[c]}, ${hash[m]['ma']}, ${hash[m]['mb']}, ${hash[m]['mc']}\": }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_misc/modules/one/metadata.json",
    "content": "{\n    \"name\": \"example/one\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-one.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/data/common.yaml",
    "content": "---\nusers::local:\n  bob:\n    name: Bob\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/data/specific.yaml",
    "content": "---\nusers::local:\n  bob:\n    shell: /bin/zsh\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"common\"\n    :backend: yaml\n  - :name: \"specific\"\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/manifests/site.pp",
    "content": "include one::test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data1/first.json",
    "content": "{\n    \"one::test::param_a\": \"module data param_a is 100\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data1/name.yaml",
    "content": "one::test::param_b: module data param_b is 200\none::my_var: 'In name.yaml'"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data1/second.json",
    "content": "{\n    \"one::test::param_c\": \"module data param_c is 300\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data1/server1.yaml",
    "content": "one::my_var: 'server1'\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data1/server2.yaml",
    "content": "one::my_var: 'server2'\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data1/single.yaml",
    "content": "---\none::test::param_d: module data param_d is 400\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/data2/single.yaml",
    "content": "---\none::test::param_e: module data param_e is 500\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"%{my_fact}\"\n    :backend: yaml\n  - :name: \"name\"\n    :backend: yaml\n  - :name: \"one path\"\n    :backend: yaml\n    :path: \"single\"\n  - :name: \"two paths\"\n    :backend: json\n    :paths:\n      - \"first\"\n      - \"second\"\n  - :name:  'other datadir'\n    :backend: yaml\n    :datadir: \"data2\"\n    :path: \"single\"\n:datadir: \"data1\"\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/manifests/init.pp",
    "content": "class one {\n  class test($param_a = \"param default is 100\", $param_b = \"param default is 200\", $param_c = \"param default is 300\", $param_d = \"param default is 400\", $param_e = \"param default is 500\") {\n    notify { \"$param_a, $param_b, $param_c, $param_d, $param_e\": }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_module_config/modules/one/metadata.json",
    "content": "{\n    \"name\": \"example/one\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-one.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/data/common.yaml",
    "content": "---\none::local:\n  bob:\n    name: Bob\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/data/specific.yaml",
    "content": "---\none::local:\n  bob:\n    shell: /bin/zsh\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/environment.conf",
    "content": "# Use the 'sample' env data provider (in this fixture)\nenvironment_data_provider=hiera\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"common\"\n    :backend: yaml\n  - :name: \"specific\"\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/manifests/site.pp",
    "content": "include one::test\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/one/data/common.yaml",
    "content": "---\nlookup_options:\n  one::local:\n    merge:\n       strategy: \"deep\"\n       merge_hash_arrays: true\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/one/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"common\"\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/one/manifests/init.pp",
    "content": "class one($local={}) {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/one/metadata.json",
    "content": "{\n    \"name\": \"example/one\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-one.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/two/data/common.yaml",
    "content": "---\nlookup_options:\n  two::arg:\n    merge: unique"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/two/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"common\"\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/two/manifests/init.pp",
    "content": "class two($arg=[]) {\n  include one\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/hiera_modules/modules/two/metadata.json",
    "content": "{\n    \"name\": \"example/two\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-two.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/environment.conf",
    "content": "environment_timeout = 0\nenvironment_data_provider = 'function'"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/lib/puppet/functions/environment/data.rb",
    "content": "Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    { 'abc::def::test1' => 'env_test1',\n      'abc::def::test2' => 'env_test2',\n      'xyz::def::test1' => 'env_test1',\n      'xyz::def::test2' => 'env_test2'\n    }\n  end\nend"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/modules/abc/lib/puppet/functions/abc/data.rb",
    "content": "Puppet::Functions.create_function(:'abc::data') do\n  def data()\n    { 'abc::def::test1' => 'module_test1',\n      'abc::def::test2' => 'module_test2',\n      'abc::def::test3' => 'module_test3',\n      'abc::def::ipl' => '%{lookup(\"abc::def::test2\")}-ipl'\n    }\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/modules/abc/manifests/init.pp",
    "content": "class abc {\n  include 'abc::def'\n}\n\nclass abc::def ($test1, $test2, $test3, $ipl ) {\n  notify { $test1: }\n  notify { $test2: }\n  notify { $test3: }\n  notify { $ipl: }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/modules/abc/metadata.json",
    "content": "{\n    \"name\": \"example/abc\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-abc.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"function\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/modules/xyz/functions/data.pp",
    "content": "function xyz::data {\n    { 'xyz::def::test1' => 'module_test1',\n      'xyz::def::test2' => 'module_test2',\n      'xyz::def::test3' => 'module_test3'\n    }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/modules/xyz/manifests/init.pp",
    "content": "class xyz {\n  include 'xyz::def'\n}\n\nclass xyz::def ($test1, $test2, $test3) {\n  notify { $test1: }\n  notify { $test2: }\n  notify { $test3: }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/data_providers/environments/production/modules/xyz/metadata.json",
    "content": "{\n    \"name\": \"example/abc\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-abc.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"function\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/forge/bacula-releases.json",
    "content": "{\n  \"pagination\": {\n    \"limit\": 20,\n    \"offset\": 0,\n    \"first\": \"/v3/releases?module=puppetlabs-bacula&sort_by=version&exclude_fields=readme%2Cchangelog%2Clicense%2Curi%2Cmodule%2Ctags%2Csupported%2Cfile_size%2Cdownloads%2Ccreated_at%2Cupdated_at%2Cdeleted_at&limit=20&offset=0\",\n    \"previous\": null,\n    \"current\": \"/v3/releases?module=puppetlabs-bacula&sort_by=version&exclude_fields=readme%2Cchangelog%2Clicense%2Curi%2Cmodule%2Ctags%2Csupported%2Cfile_size%2Cdownloads%2Ccreated_at%2Cupdated_at%2Cdeleted_at&limit=20&offset=0\",\n    \"next\": null,\n    \"total\": 2\n  },\n  \"results\": [\n    {\n      \"slug\": \"puppetlabs-bacula-0.0.2\",\n      \"version\": \"0.0.2\",\n      \"metadata\": {\n        \"description\": \"This module manages Bacula, a complete backup solution\",\n        \"source\": \"http://github.com/puppetlabs/puppetlabs-bacula\",\n        \"checksums\": {\n          \"templates/client_config.erb\": \"5d4005eda5ace78fd90a8cb6dcb388bd\",\n          \"spec/spec_helper.rb\": \"ca19ec4f451ebc7fdb035b52eae6e909\",\n          \"templates/bacula-fd.conf.erb\": \"344fff616e138fbf8cd150f4bab8125d\",\n          \"CHANGELOG\": \"a20043858790de6086b9b30c36806cd2\",\n          \"manifests/config/validate.pp\": \"7d2f9f7cffb2bd1d221eb699ea55f246\",\n          \"LICENSE\": \"26ce13c80c7a8493533c65c32dc29f09\",\n          \"manifests/director.pp\": \"36f34f749314634173b6a47b92a58421\",\n          \"manifests/config/client.pp\": \"5d25cbfd94be2829d9d530673cd2307f\",\n          \"lib/puppet/parser/functions/generate_clients.rb\": \"e09d77f88d18ec3e59bbe7bae4a48036\",\n          \"manifests/common.pp\": \"67391e5560ee7fc97d873ab5c3e2f84c\",\n          \"manifests/client.pp\": \"42e9255711d0a6abed6ab8deafb1248c\",\n          \"manifests/console.pp\": \"4cf8ac2f96f9268e2a3d70ed777797b7\",\n          \"README.md\": \"13d0f1119d510e41c539cbc3825a4f82\",\n          \"templates/bacula-sd.conf.erb\": \"df7987695bf1dd18bb789a2673b2cd95\",\n          \"manifests/config.pp\": \"624a225f85f907e7db5cf717561953f3\",\n          \"tests/init.pp\": \"b95d199119d5e592df7e1580bf23fe06\",\n          \"templates/bconsole.conf.erb\": \"ba5e1ef7d320a48bde6e403dc956952a\",\n          \"manifests/bat.pp\": \"3743bddf0197b4110e54a8e297e927e8\",\n          \"manifests/storage.pp\": \"84f4ec1413df4fdec7288c18d6ba2897\",\n          \"templates/bacula-dir.conf.erb\": \"5ab5aa263cf318ebf65c6b52a0726647\",\n          \"metadata.json\": \"d34d0b70aba36510fbc2df4e667479ef\",\n          \"spec/spec.opts\": \"a600ded995d948e393fbe2320ba8e51c\",\n          \"Modulefile\": \"63f93af605871a69211cd3f79ac62582\",\n          \"manifests/init.pp\": \"fafac38161dd69f4b7e452cf910812f4\"\n        },\n        \"summary\": \"This module manages a bacula infrastructure\",\n        \"author\": \"Puppet Labs\",\n        \"dependencies\": [\n\n        ],\n        \"project_page\": \"http://github.com/puppetlabs/puppetlabs-bacula\",\n        \"types\": [\n\n        ],\n        \"license\": \"Apache\",\n        \"version\": \"0.0.2\",\n        \"name\": \"puppetlabs-bacula\"\n      },\n      \"pdk\": false,\n      \"validation_score\": 22,\n      \"file_uri\": \"/v3/files/puppetlabs-bacula-0.0.2.tar.gz\",\n      \"file_md5\": \"6cd7b8a3ec788e4ddbd06ae59be45d7a\",\n      \"file_sha256\": \"4cea5bae3c2b3dbe236bdc87ab9caa4f6d41943d1bf81651b2c0e08205910cdd\",\n      \"reference\": null,\n      \"tasks\": [\n\n      ],\n      \"plans\": [\n\n      ],\n      \"deleted_for\": null\n    },\n    {\n      \"slug\": \"puppetlabs-bacula-0.0.1\",\n      \"version\": \"0.0.1\",\n      \"metadata\": {\n        \"description\": \"This module manages Bacula, a complete backup solution\",\n        \"source\": \"http://github.com/puppetlabs/puppetlabs-bacula\",\n        \"checksums\": {\n          \"spec/spec_helper.rb\": \"ca19ec4f451ebc7fdb035b52eae6e909\",\n          \"templates/bacula-fd.conf.erb\": \"344fff616e138fbf8cd150f4bab8125d\",\n          \"CHANGELOG\": \"28c8a36d149971979b867491203b5fef\",\n          \"manifests/config/validate.pp\": \"e74d9dfbf4814d455a2c4a27ac516571\",\n          \"LICENSE\": \"26ce13c80c7a8493533c65c32dc29f09\",\n          \"manifests/director.pp\": \"bec6d5b6b8befca9640edb8bdb8b5b10\",\n          \"manifests/common.pp\": \"9c037f51fd8b70c4bed0ceeb55f93ffb\",\n          \"manifests/client.pp\": \"42e9255711d0a6abed6ab8deafb1248c\",\n          \"manifests/console.pp\": \"1ee546361e47c0bc43299b3b62baa925\",\n          \"README.md\": \"470c6e1e9b4d877bcfdc88d751de8d19\",\n          \"templates/bacula-sd.conf.erb\": \"df7987695bf1dd18bb789a2673b2cd95\",\n          \"manifests/config.pp\": \"4410fadc6d82543e0b19039e3365fd3a\",\n          \"tests/init.pp\": \"b95d199119d5e592df7e1580bf23fe06\",\n          \"templates/bconsole.conf.erb\": \"ba5e1ef7d320a48bde6e403dc956952a\",\n          \"manifests/bat.pp\": \"3743bddf0197b4110e54a8e297e927e8\",\n          \"manifests/storage.pp\": \"13eadb4376a07d095d1ee13e8ff60846\",\n          \"templates/bacula-dir.conf.erb\": \"c0b1b57660cf965bcff7e5fda547ee03\",\n          \"metadata.json\": \"d34d0b70aba36510fbc2df4e667479ef\",\n          \"spec/spec.opts\": \"a600ded995d948e393fbe2320ba8e51c\",\n          \"Modulefile\": \"082b764ac91831266b7cbda75f79c199\",\n          \"manifests/init.pp\": \"6b9d2e9ca0a06c1a318837307fadcc70\"\n        },\n        \"summary\": \"This module manages a bacula infrastructure\",\n        \"author\": \"Puppet Labs\",\n        \"dependencies\": [\n\n        ],\n        \"project_page\": \"http://github.com/puppetlabs/puppetlabs-bacula\",\n        \"types\": [\n\n        ],\n        \"license\": \"Apache\",\n        \"version\": \"0.0.1\",\n        \"name\": \"puppetlabs-bacula\"\n      },\n      \"pdk\": false,\n      \"validation_score\": 38,\n      \"file_uri\": \"/v3/files/puppetlabs-bacula-0.0.1.tar.gz\",\n      \"file_md5\": \"c29882cf29eff17f18dcb74cc544c200\",\n      \"file_sha256\": \"7a68af4ab6e413a1cb3ec1694ddc3956138aa4c34d97abbe61a50947d0b12069\",\n      \"reference\": null,\n      \"tasks\": [\n\n      ],\n      \"plans\": [\n\n      ],\n      \"deleted_for\": null\n    }\n  ]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/forge/bacula.json",
    "content": "{\n  \"pagination\": {\n    \"limit\": 1,\n    \"offset\": 0,\n    \"first\": \"/v3/modules?limit=1&offset=0\",\n    \"previous\": null,\n    \"current\": \"/v3/modules?limit=1&offset=0\",\n    \"next\": null,\n    \"total\": 1832\n  },\n  \"results\": [\n    {\n      \"uri\": \"/v3/modules/puppetlabs-bacula\",\n      \"name\": \"bacula\",\n      \"downloads\": 640274,\n      \"created_at\": \"2011-05-24 18:34:58 -0700\",\n      \"updated_at\": \"2013-12-03 15:24:20 -0800\",\n      \"owner\": {\n        \"uri\": \"/v3/users/puppetlabs\",\n        \"username\": \"puppetlabs\",\n        \"gravatar_id\": \"fdd009b7c1ec96e088b389f773e87aec\"\n      },\n      \"current_release\": {\n        \"uri\": \"/v3/releases/puppetlabs-bacula-0.0.2\",\n        \"module\": {\n          \"uri\": \"/v3/modules/puppetlabs-bacula\",\n          \"name\": \"bacula\",\n          \"owner\": {\n            \"uri\": \"/v3/users/puppetlabs\",\n            \"username\": \"puppetlabs\",\n            \"gravatar_id\": \"fdd009b7c1ec96e088b389f773e87aec\"\n          }\n        },\n        \"version\": \"0.0.2\",\n        \"metadata\": {\n          \"types\": [],\n          \"license\": \"Apache 2.0\",\n          \"checksums\": { },\n          \"version\": \"0.0.2\",\n          \"source\": \"https://github.com/puppetlabs/puppetlabs-bacula\",\n          \"project_page\": \"https://github.com/puppetlabs/puppetlabs-bacula\",\n          \"summary\": \"bacula\",\n          \"dependencies\": [ ],\n          \"author\": \"puppetlabs\",\n          \"name\": \"puppetlabs-bacula\"\n        },\n        \"tags\": [\n          \"backup\",\n          \"bacula\"\n        ],\n        \"file_uri\": \"/v3/files/puppetlabs-bacula-0.0.2.tar.gz\",\n        \"file_size\": 67586,\n        \"file_md5\": \"bbf919d7ee9d278d2facf39c25578bf8\",\n        \"downloads\": 565041,\n        \"readme\": \"\",\n        \"changelog\": \"\",\n        \"license\": \"\",\n        \"created_at\": \"2013-05-13 08:31:19 -0700\",\n        \"updated_at\": \"2013-05-13 08:31:19 -0700\",\n        \"deleted_at\": null\n      },\n      \"releases\": [\n        {\n          \"uri\": \"/v3/releases/puppetlabs-bacula-0.0.2\",\n          \"version\": \"0.0.2\"\n        },\n        {\n          \"uri\": \"/v3/releases/puppetlabs-bacula-0.0.1\",\n          \"version\": \"0.0.1\"\n        }\n      ],\n      \"homepage_url\": \"https://github.com/puppetlabs/puppetlabs-bacula\",\n      \"issues_url\": \"https://projects.puppetlabs.com/projects/bacula/issues\"\n    }\n  ]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/hiera/hiera/backend/hieraspec_backend.rb",
    "content": "class Hiera::Backend::Hieraspec_backend\n  def initialize(cache = nil)\n    Hiera.debug('Custom_backend starting')\n  end\n\n  def lookup(key, scope, order_override, resolution_type, context)\n    case key\n    when 'datasources'\n      Hiera::Backend.datasources(scope, order_override) { |source| source }\n    when 'resolution_type'\n      if resolution_type == :hash\n        { key => resolution_type.to_s }\n      elsif resolution_type == :array\n        [ key, resolution_type.to_s ]\n      else\n        \"resolution_type=#{resolution_type}\"\n      end\n    else\n      throw :no_such_key\n    end\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup/data/common.yaml",
    "content": "---\nabc::a: global_a\nabc::c:\n  - global_c\n\nabc::e:\n  k1: global_e1\n\nabc::f:\n  k1:\n    s1: global_f11\n  k2:\n    s3: global_f23\n\nbca::e:\n  k1: global_e1\n\nno_provider::e:\n  k1: global_e1\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup/hiera/backend/custom_backend.rb",
    "content": "class Hiera::Backend::Custom_backend\n  def lookup(key, scope, order_override, resolution_type, context)\n    case key\n    when 'hash_c'\n      { 'hash_ca' => { 'cad' => 'value hash_c.hash_ca.cad (from global custom)' }}\n    when 'hash'\n      { 'array' => [ 'x5,x6' ] }\n    when 'array'\n      [ 'x5,x6' ]\n    when 'datasources'\n      Hiera::Backend.datasources(scope, order_override) { |source| source }\n    when 'dotted.key'\n      'custom backend received request for dotted.key value'\n    else\n      throw :no_such_key\n    end\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup/hiera/backend/other_backend.rb",
    "content": "class Hiera::Backend::Other_backend\n  def lookup(key, scope, order_override, resolution_type, context)\n    value = Hiera::Config[:other][key.to_sym]\n    throw :no_such_key if value.nil?\n    value\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/data/common.yaml",
    "content": "---\nabc::a: global_a\nabc::c:\n  - global_c\n\nabc::e:\n  k1: global_e1\n\nabc::f:\n  k1:\n    s1: global_f11\n  k2:\n    s3: global_f23\n\nbca::e:\n  k1: global_e1\n\nno_provider::e:\n  k1: global_e1\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/environment.conf",
    "content": "environment_timeout = 0\nenvironment_data_provider = 'function'\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/lib/puppet/functions/environment/data.rb",
    "content": "Puppet::Functions.create_function(:'environment::data') do\n  def data()\n    { 'abc::a' => 'env_a',\n      'abc::c' => 'env_c',\n      'abc::d' => { 'k1' => 'env_d1', 'k2' => 'env_d2', 'k3' => 'env_d3' },\n      'abc::e' => { 'k1' => 'env_e1', 'k3' => 'env_e3' },\n      'bca::e' => { 'k1' => 'env_bca_e1', 'k3' => 'env_bca_e3' },\n      'no_provider::e' => { 'k1' => 'env_no_provider_e1', 'k3' => 'env_no_provider_e3' },\n      'abc::f' => { 'k1' => { 's1' => 'env_f11', 's2' => 'env_f12' },  'k2' => { 's1' => 'env_f21', 's3' => 'env_f23' }},\n      'abc::n' => nil\n    }\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/abc/lib/puppet/functions/abc/data.rb",
    "content": "Puppet::Functions.create_function(:'abc::data') do\n  def data()\n    { 'abc::b' => 'module_b',\n      'abc::c' => 'module_c',\n      'abc::e' => { 'k1' => 'module_e1', 'k2' => 'module_e2' },\n      'abc::f' => { 'k1' => { 's1' => 'module_f11', 's3' => 'module_f13' },  'k2' => { 's1' => 'module_f21', 's2' => 'module_f22' }},\n    }\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/abc/manifests/init.pp",
    "content": "class abc {\n  if $block != 'no_block_present' {\n    $result = lookup(*$args) |$names| { if $block == true { $names } else { $block } }\n  }\n  else {\n    $result = lookup(*$args)\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/abc/metadata.json",
    "content": "{\n    \"name\": \"example/abc\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-abc.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"function\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/bad_data/lib/puppet/functions/bad_data/data.rb",
    "content": "Puppet::Functions.create_function(:'bad_data::data') do\n  def data()\n    { 'b' => 'module_b', # Intentionally bad key (no module prefix)\n      'bad_data::c' => 'module_c' # Good key. Should be OK\n    }\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/bad_data/manifests/init.pp",
    "content": "class bad_data {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/bad_data/metadata.json",
    "content": "{\n    \"name\": \"example/bad_data\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-bad_data.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"function\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/bca/lib/puppet/functions/bca/data.rb",
    "content": "Puppet::Functions.create_function(:'bca::data') do\n  def data()\n    { 'bca::b' => 'module_bca_b',\n      'bca::c' => 'module_bca_c',\n      'bca::e' => { 'k1' => 'module_bca_e1', 'k2' => 'module_bca_e2' },\n      'bca::f' => { 'k1' => { 's1' => 'module_bca_f11', 's3' => 'module_bca_f13' },  'k2' => { 's1' => 'module_bca_f21', 's2' => 'module_bca_f22' }},\n    }\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/bca/manifests/init.pp",
    "content": "class bca {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/bca/metadata.json",
    "content": "{\n    \"name\": \"example/bca\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-bca.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"function\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_json/data/empty.json",
    "content": ""
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_json/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: empty\n    :backend: json\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_json/manifests/init.pp",
    "content": "class empty_json {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_json/metadata.json",
    "content": "{\n    \"name\": \"example/empty_json\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-empty_json.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_json/data/empty_key.json",
    "content": "{ \"empty_key_json::has_undef_value\": null }\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_json/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: empty_key\n    :backend: json\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_json/manifests/init.pp",
    "content": "class empty_key_json {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_json/metadata.json",
    "content": "{\n    \"name\": \"example/empty_key_json\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-empty_key_json.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_yaml/data/empty_key.yaml",
    "content": "empty_key_yaml::has_undef_value: ~\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_yaml/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: empty_key\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_yaml/manifests/init.pp",
    "content": "class empty_key_yaml {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_key_yaml/metadata.json",
    "content": "{\n    \"name\": \"example/empty_key_yaml\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-empty_key_yaml.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_yaml/data/empty.yaml",
    "content": "---\nempty_key: ~\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_yaml/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: empty\n    :backend: yaml\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_yaml/manifests/init.pp",
    "content": "class empty_yaml {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/empty_yaml/metadata.json",
    "content": "{\n    \"name\": \"example/empty_yaml\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-empty_yaml.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/hieraprovider/data/first.json",
    "content": "{\n    \"hieraprovider::test::param_a\": \"module data param_a is 100\",\n    \"test::param_b\": \"module data param_a is 100\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/hieraprovider/hiera.yaml",
    "content": "---\n:version: 4\n:hierarchy:\n  - :name: \"two paths\"\n    :backend: json\n    :paths:\n      - \"first\"\n      - \"second_not_present\"\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/hieraprovider/manifests/init.pp",
    "content": "class hieraprovider {\n  class test($param_a = \"param default is 100\") {\n    notify { \"$param_a\": }\n  }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/hieraprovider/metadata.json",
    "content": "{\n    \"name\": \"example/hieraprovider\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-hieraprovider.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"hiera\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/meta/lib/puppet/functions/meta/data.rb",
    "content": "Puppet::Functions.create_function(:'meta::data') do\n  def data()\n    { 'meta::b' => 'module_b',\n      'meta::c' => 'module_c',\n      'meta::e' => { 'k1' => 'module_e1', 'k2' => 'module_e2' },\n      'meta::f' => { 'k1' => { 's1' => 'module_f11', 's3' => 'module_f13' },  'k2' => { 's1' => 'module_f21', 's2' => 'module_f22' }},\n    }\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/meta/manifests/init.pp",
    "content": "class meta {\n  $result = lookup(*$args)\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/meta/metadata.json",
    "content": "{\n    \"name\": \"example/meta\",\n    \"version\": \"0.0.2\",\n    \"source\": \"git@github.com/example/example-meta.git\",\n    \"dependencies\": [],\n    \"author\": \"Bob the Builder\",\n    \"license\": \"Apache-2.0\",\n    \"data_provider\": \"function\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/functions/lookup_fixture/environments/production/modules/no_provider/manifests/init.pp",
    "content": "class no_provider {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/indirector/data_binding/hiera/global.yaml",
    "content": "---\ninteger: 3000\nstring: 'apache'\nhash:\n  user:  'Hightower'\n  group: 'admin'\n  mode:  '0644'\narray:\n - '0.ntp.puppetlabs.com'\n - '1.ntp.puppetlabs.com'\n"
  },
  {
    "path": "spec/fixtures/unit/indirector/data_binding/hiera/invalid.yaml",
    "content": "{ invalid:\n"
  },
  {
    "path": "spec/fixtures/unit/indirector/hiera/global.yaml",
    "content": "---\ninteger: 3000\nstring: 'apache'\nhash:\n  user:  'Hightower'\n  group: 'admin'\n  mode:  '0644'\narray:\n - '0.ntp.puppetlabs.com'\n - '1.ntp.puppetlabs.com'\n"
  },
  {
    "path": "spec/fixtures/unit/indirector/hiera/invalid.yaml",
    "content": "{ invalid:\n"
  },
  {
    "path": "spec/fixtures/unit/module/trailing-comma.json",
    "content": "{\n    \"name\": \"puppetlabs/ruby\",\n    \"version\": \"0.0.2\",\n    \"summary\": \"Manage Ruby\",\n    \"source\": \"git@github.com/puppetlabs/puppetlabs-ruby.git\",\n    \"project_page\": \"https://github.com/puppetlabs/puppetlabs-ruby\",\n    \"author\": \"Puppet Labs\",\n    \"license\": \"Apache-2.0\",\n    \"operatingsystem_support\": [\n        \"RedHat\",\n        \"OpenSUSE\",\n        \"SLES\",\n        \"SLED\",\n        \"Debian\",\n        \"Ubuntu\"\n    ],\n    \"puppet_version\": [\n        2.7,\n        3.0,\n        3.1,\n        3.2,\n        3.3,\n    ],\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/functions/create_resources/foo/manifests/init.pp",
    "content": "class foo {\n  create_resources('foo::wrongdefine', {'blah'=>{'one'=>'two'}})\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/functions/create_resources/foo/manifests/wrongdefine.pp",
    "content": "define foo::wrongdefine($one){\n  $foo = $one,\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/aliastest.pp",
    "content": "file { \"a file\":\n    path => \"/tmp/aliastest\",\n    ensure => file\n}\n\nfile { \"another\":\n    path => \"/tmp/aliastest2\",\n    ensure => file,\n    require => File[\"a file\"]\n}\n\nfile { \"a third\":\n    path => \"/tmp/aliastest3\",\n    ensure => file,\n    require => File[\"/tmp/aliastest\"]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/append.pp",
    "content": "$var=['/tmp/file1','/tmp/file2']\n\nclass arraytest {\n    $var += ['/tmp/file3', '/tmp/file4']\n    file {\n        $var:\n            content => \"test\"\n    }\n}\n\ninclude arraytest\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/argumentdefaults.pp",
    "content": "# $Id$\n\ndefine testargs($file, $mode = '0755') {\n    file { $file: ensure => file, mode => $mode }\n}\n\ntestargs { \"testingname\":\n    file => \"/tmp/argumenttest1\"\n}\n\ntestargs { \"testingother\":\n    file => \"/tmp/argumenttest2\",\n    mode => '0644'\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/arithmetic_expression.pp",
    "content": "\n$one = 1.30\n$two = 2.034e-2\n\n$result = ((( $two + 2) / $one) + 4 * 5.45) - (6 << 7) + (0x800 + -9)\n\n\nnotice(\"result is $result == 1295.87692307692\")\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/arraytrailingcomma.pp",
    "content": "file {\n    [\"/tmp/arraytrailingcomma1\",\"/tmp/arraytrailingcomma2\", ]: content => \"tmp\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/casestatement.pp",
    "content": "# $Id$\n\n$var = \"value\"\n\ncase $var {\n  \"nope\": {\n     file { \"/tmp/fakefile\": mode => '0644', ensure => file }\n  }\n  \"value\": {\n     file { \"/tmp/existsfile\": mode => '0755', ensure => file }\n  }\n}\n\n$ovar = \"yayness\"\n\ncase $ovar {\n    \"fooness\": {\n         file { \"/tmp/nostillexistsfile\": mode => '0644', ensure => file }\n    }\n    \"booness\", \"yayness\": {\n        case $var {\n            \"nep\": {\n                 file { \"/tmp/noexistsfile\": mode => '0644', ensure => file }\n            }\n            \"value\": {\n                 file { \"/tmp/existsfile2\": mode => '0755', ensure => file }\n            }\n        }\n    }\n}\n\ncase $ovar {\n    \"fooness\": {\n         file { \"/tmp/nostillexistsfile\": mode => '0644', ensure => file }\n    }\n    default: {\n        file { \"/tmp/existsfile3\": mode => '0755', ensure => file }\n    }\n}\n\n$bool = true\n\ncase $bool {\n    true: {\n        file { \"/tmp/existsfile4\": mode => '0755', ensure => file }\n    }\n}\n\n$yay = yay\n$a = yay\n$b = boo\n\ncase $yay {\n    $a: { file { \"/tmp/existsfile5\": mode => '0755', ensure => file } }\n    $b: { file { \"/tmp/existsfile5\": mode => '0644', ensure => file } }\n    default: { file { \"/tmp/existsfile5\": mode => '0711', ensure => file } }\n\n}\n\n$regexvar = \"exists regex\"\ncase $regexvar {\n    \"no match\": { file { \"/tmp/existsfile6\": mode => '0644', ensure => file } }\n    /(.*) regex$/: { file { \"/tmp/${1}file6\": mode => '0755', ensure => file } }\n    default: { file { \"/tmp/existsfile6\": mode => '0711', ensure => file } }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/classheirarchy.pp",
    "content": "# $Id$\n\nclass base {\n    file { \"/tmp/classheir1\": ensure => file, mode => '0755' }\n}\n\nclass sub1 inherits base {\n    file { \"/tmp/classheir2\": ensure => file, mode => '0755' }\n}\n\nclass sub2 inherits base {\n    file { \"/tmp/classheir3\": ensure => file, mode => '0755' }\n}\n\ninclude sub1, sub2\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/classincludes.pp",
    "content": "# $Id$\n\nclass base {\n    file { \"/tmp/classincludes1\": ensure => file, mode => '0755' }\n}\n\nclass sub1 inherits base {\n    file { \"/tmp/classincludes2\": ensure => file, mode => '0755' }\n}\n\nclass sub2 inherits base {\n    file { \"/tmp/classincludes3\": ensure => file, mode => '0755' }\n}\n\n$sub = \"sub2\"\n\ninclude sub1, $sub\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/classpathtest.pp",
    "content": "# $Id$\n\ndefine mytype {\n    file { \"/tmp/classtest\": ensure => file, mode => '0755' }\n}\n\nclass testing {\n    mytype { \"componentname\": }\n}\n\ninclude testing\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/collection.pp",
    "content": "class one {\n    @file { \"/tmp/colltest1\": content => \"one\" }\n    @file { \"/tmp/colltest2\": content => \"two\" }\n}\n\nclass two {\n    File <| content == \"one\" |>\n}\n\ninclude one, two\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/collection_override.pp",
    "content": "@file {\n    \"/tmp/collection\":\n        content => \"whatever\"\n}\n\nFile<| |> {\n    mode => '0600'\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/collection_within_virtual_definitions.pp",
    "content": "define test($name) {\n    file {\"/tmp/collection_within_virtual_definitions1_$name.txt\":\n        content => \"File name $name\\n\"\n    }\n    Test2 <||>\n}\n\ndefine test2() {\n    file {\"/tmp/collection_within_virtual_definitions2_$name.txt\":\n        content => \"This is a test\\n\"\n    }\n}\n\nnode default {\n    @test {\"foo\":\n        name => \"foo\"\n    }\n    @test2 {\"foo2\": }\n    Test <||>\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/componentmetaparams.pp",
    "content": "file { \"/tmp/component1\":\n    ensure => file\n}\n\ndefine thing {\n    file { $name: ensure => file }\n}\n\nthing { \"/tmp/component2\":\n    require => File[\"/tmp/component1\"]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/componentrequire.pp",
    "content": "define testfile($mode) {\n    file { $name: mode => $mode, ensure => present }\n}\n\ntestfile { \"/tmp/testing_component_requires2\": mode => '0755' }\n\nfile { \"/tmp/testing_component_requires1\": mode => '0755', ensure => present,\n    require => Testfile[\"/tmp/testing_component_requires2\"] }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/deepclassheirarchy.pp",
    "content": "# $Id$\n\nclass base {\n    file { \"/tmp/deepclassheir1\": ensure => file, mode => '0755' }\n}\n\nclass sub1 inherits base {\n    file { \"/tmp/deepclassheir2\": ensure => file, mode => '0755' }\n}\n\nclass sub2 inherits sub1 {\n    file { \"/tmp/deepclassheir3\": ensure => file, mode => '0755' }\n}\n\nclass sub3 inherits sub2 {\n    file { \"/tmp/deepclassheir4\": ensure => file, mode => '0755' }\n}\n\nclass sub4 inherits sub3 {\n    file { \"/tmp/deepclassheir5\": ensure => file, mode => '0755' }\n}\n\ninclude sub4\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/defineoverrides.pp",
    "content": "# $Id$\n\n$file = \"/tmp/defineoverrides1\"\n\ndefine myfile($mode) {\n    file { $name: ensure => file, mode => $mode }\n}\n\nclass base {\n    myfile { $file: mode => '0644' }\n}\n\nclass sub inherits base {\n    Myfile[$file] { mode => '0755', } # test the end-comma\n}\n\ninclude sub\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/emptyclass.pp",
    "content": "# $Id$\n\ndefine component {\n}\n\nclass testing {\n}\n\ninclude testing\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/emptyexec.pp",
    "content": "exec { \"touch /tmp/emptyexectest\":\n    path => \"/usr/bin:/bin\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/emptyifelse.pp",
    "content": "\nif false {\n} else {\n  # nothing here\n}\n\nif true {\n  # still nothing\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/falsevalues.pp",
    "content": "$value = false\n\nfile { \"/tmp/falsevalues$value\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/filecreate.pp",
    "content": "# $Id$\n\nfile {\n    \"/tmp/createatest\": ensure => file, mode => '0755';\n    \"/tmp/createbtest\": ensure => file, mode => '0755'\n}\n\nfile {\n    \"/tmp/createctest\": ensure => file;\n    \"/tmp/createdtest\": ensure => file;\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/fqdefinition.pp",
    "content": "define one::two($ensure) {\n    file { \"/tmp/fqdefinition\": ensure => $ensure }\n}\n\none::two { \"/tmp/fqdefinition\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/fqparents.pp",
    "content": "class base {\n    class one {\n        file { \"/tmp/fqparent1\": ensure => file }\n    }\n}\n\nclass two::three inherits base::one {\n    file { \"/tmp/fqparent2\": ensure => file }\n}\n\ninclude two::three\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/funccomma.pp",
    "content": "@file {\n    [\"/tmp/funccomma1\",\"/tmp/funccomma2\"]: content => \"1\"\n}\n\nrealize( File[\"/tmp/funccomma1\"], File[\"/tmp/funccomma2\"] , )\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/hash.pp",
    "content": "\n$hash = { \"file\" => \"/tmp/myhashfile1\" }\n\nfile {\n    $hash[\"file\"]:\n        ensure => file, content => \"content\";\n}\n\n$hash2 = { \"a\" => { key => \"/tmp/myhashfile2\" }}\n\nfile {\n    $hash2[\"a\"][key]:\n        ensure => file, content => \"content\";\n}\n\ndefine test($a = { \"b\" => \"c\" }) {\n    file {\n        $a[\"b\"]:\n            ensure => file, content => \"content\"\n    }\n}\n\ntest {\n    \"test\":\n        a => { \"b\" => \"/tmp/myhashfile3\" }\n}\n\n$hash3 = { mykey => \"/tmp/myhashfile4\" }\n$key = \"mykey\"\n\nfile {\n    $hash3[$key]: ensure => file, content => \"content\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/ifexpression.pp",
    "content": "$one = 1\n$two = 2\n\nif ($one < $two) and (($two < 3) or ($two == 2)) {\n    notice(\"True!\")\n}\n\nif \"test regex\" =~ /(.*) regex/ {\n    file {\n        \"/tmp/${1}iftest\": ensure => file, mode => '0755'\n    }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/implicititeration.pp",
    "content": "# $Id$\n\n$files = [\"/tmp/iterationatest\", \"/tmp/iterationbtest\"]\n\nfile { $files: ensure => file, mode => '0755' }\n\nfile { [\"/tmp/iterationctest\", \"/tmp/iterationdtest\"]:\n    ensure => file,\n    mode => '0755'\n}\n\nfile {\n    [\"/tmp/iterationetest\", \"/tmp/iterationftest\"]: ensure => file, mode => '0755';\n    [\"/tmp/iterationgtest\", \"/tmp/iterationhtest\"]: ensure => file, mode => '0755';\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/multilinecomments.pp",
    "content": "\n/*\nfile {\n    \"/tmp/multilinecomments\": content => \"pouet\"\n}\n*/\n\n/* and another one for #2333, the whitespace after the \nend comment is here on purpose */  \n\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/multipleclass.pp",
    "content": "class one {\n    file { \"/tmp/multipleclassone\": content => \"one\" }\n}\n\nclass one {\n    file { \"/tmp/multipleclasstwo\": content => \"two\" }\n}\n\ninclude one\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/multipleinstances.pp",
    "content": "# $Id$\n\nfile {\n    \"/tmp/multipleinstancesa\": ensure => file, mode => '0755';\n    \"/tmp/multipleinstancesb\": ensure => file, mode => '0755';\n    \"/tmp/multipleinstancesc\": ensure => file, mode => '0755';\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/multisubs.pp",
    "content": "class base {\n    file { \"/tmp/multisubtest\": content => \"base\", mode => '0644' }\n}\n\nclass sub1 inherits base {\n    File[\"/tmp/multisubtest\"] { mode => '0755' }\n}\n\nclass sub2 inherits base {\n    File[\"/tmp/multisubtest\"] { content => sub2 }\n}\n\ninclude sub1, sub2\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/namevartest.pp",
    "content": "define filetest($mode, $ensure = file) {\n    file { $name:\n        mode => $mode,\n        ensure => $ensure\n    }\n}\n\nfiletest { \"/tmp/testfiletest\": mode => '0644'}\nfiletest { \"/tmp/testdirtest\": mode => '0755', ensure => directory}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/scopetest.pp",
    "content": "\n$mode = 640\n\ndefine thing {\n    file { \"/tmp/$name\": ensure => file, mode => $mode }\n}\n\nclass testing {\n    $mode = 755\n    thing {scopetest: }\n}\n\ninclude testing\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/selectorvalues.pp",
    "content": "$value1 = \"\"\n$value2 = true\n$value3 = false\n$value4 = yay\n\n$test = \"yay\"\n\n$mode1 = $value1 ? {\n    \"\" => 755,\n    default => 644\n}\n\n$mode2 = $value2 ? {\n    true => 755,\n    default => 644\n}\n\n$mode3 = $value3 ? {\n    false => 755,\n    default => 644\n}\n\n$mode4 = $value4 ? {\n    $test => 755,\n    default => 644\n}\n\n$mode5 = yay ? {\n    $test => 755,\n    default => 644\n}\n\n$mode6 = $mode5 ? {\n    755 => 755\n}\n\n$mode7 = \"test regex\" ? {\n    /regex$/ => 755,\n    default => 644\n}\n\n\nfile { \"/tmp/selectorvalues1\": ensure => file, mode => $mode1 }\nfile { \"/tmp/selectorvalues2\": ensure => file, mode => $mode2 }\nfile { \"/tmp/selectorvalues3\": ensure => file, mode => $mode3 }\nfile { \"/tmp/selectorvalues4\": ensure => file, mode => $mode4 }\nfile { \"/tmp/selectorvalues5\": ensure => file, mode => $mode5 }\nfile { \"/tmp/selectorvalues6\": ensure => file, mode => $mode6 }\nfile { \"/tmp/selectorvalues7\": ensure => file, mode => $mode7 }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/simpledefaults.pp",
    "content": "# $Id$\n\nFile { mode => '0755' }\n\nfile { \"/tmp/defaulttest\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/simpleselector.pp",
    "content": "# $Id$\n\n$var = \"value\"\n\nfile { \"/tmp/snippetselectatest\":\n    ensure => file,\n    mode => $var ? {\n        nottrue => 641,\n        value => 755\n    }\n}\n\nfile { \"/tmp/snippetselectbtest\":\n    ensure => file,\n    mode => $var ? {\n        nottrue => 644,\n        default => 755\n    }\n}\n\n$othervar = \"complex value\"\n\nfile { \"/tmp/snippetselectctest\":\n    ensure => file,\n    mode => $othervar ? {\n        \"complex value\" => 755,\n        default => 644\n    }\n}\n$anothervar = Yayness\n\nfile { \"/tmp/snippetselectdtest\":\n    ensure => file,\n    mode => $anothervar ? {\n        Yayness => 755,\n        default => 644\n    }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/singleary.pp",
    "content": "# $Id$\n\nfile { \"/tmp/singleary1\":\n    ensure => file\n}\n\nfile { \"/tmp/singleary2\":\n    ensure => file\n}\n\nfile { \"/tmp/singleary3\":\n    ensure => file,\n    require => [File[\"/tmp/singleary1\"], File[\"/tmp/singleary2\"]]\n}\n\nfile { \"/tmp/singleary4\":\n    ensure => file,\n    require => [File[\"/tmp/singleary1\"]]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/singlequote.pp",
    "content": "# $Id$\n\nfile { \"/tmp/singlequote1\":\n    ensure => file,\n    content => 'a $quote'\n}\n\nfile { \"/tmp/singlequote2\":\n    ensure => file,\n    content => 'some \"\\yayness\\\"'\n}\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/singleselector.pp",
    "content": "$value1 = \"\"\n$value2 = true\n$value3 = false\n$value4 = yay\n\n$test = \"yay\"\n\n$mode1 = $value1 ? {\n    \"\" => 755\n}\n\n$mode2 = $value2 ? {\n    true => 755\n}\n\n$mode3 = $value3 ? {\n    default => 755\n}\n\nfile { \"/tmp/singleselector1\": ensure => file, mode => $mode1 }\nfile { \"/tmp/singleselector2\": ensure => file, mode => $mode2 }\nfile { \"/tmp/singleselector3\": ensure => file, mode => $mode3 }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/subclass_name_duplication.pp",
    "content": "#!/usr/bin/env puppet\n\nclass one::fake {\n    file { \"/tmp/subclass_name_duplication1\": ensure => present }\n}\n\nclass two::fake {\n    file { \"/tmp/subclass_name_duplication2\": ensure => present }\n}\n\ninclude one::fake, two::fake\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/tag.pp",
    "content": "# $Id$\n\n$variable = value\n\ntag yayness, rahness\n\ntag booness, $variable\n\nfile { \"/tmp/settestingness\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/tagged.pp",
    "content": "# $Id$\n\ntag testing\ntag(funtest)\n\nclass tagdefine {\n    $path = tagged(tagdefine) ? {\n        true => \"true\", false => \"false\"\n    }\n\n    file { \"/tmp/taggeddefine$path\": ensure => file }\n}\n\ninclude tagdefine\n\n$yayness = tagged(yayness) ? {\n    true => \"true\", false => \"false\"\n}\n\n$funtest = tagged(testing) ? {\n    true => \"true\", false => \"false\"\n}\n\n$both = tagged(testing, yayness) ? {\n    true => \"true\", false => \"false\"\n}\n\n$bothtrue = tagged(testing, testing) ? {\n    true => \"true\", false => \"false\"\n}\n\nfile { \"/tmp/taggedyayness$yayness\": ensure => file }\nfile { \"/tmp/taggedtesting$funtest\": ensure => file }\nfile { \"/tmp/taggedboth$both\": ensure => file }\nfile { \"/tmp/taggedbothtrue$bothtrue\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/parser/lexer/virtualresources.pp",
    "content": "class one {\n    @file { \"/tmp/virtualtest1\": content => \"one\" }\n    @file { \"/tmp/virtualtest2\": content => \"two\" }\n    @file { \"/tmp/virtualtest3\": content => \"three\" }\n    @file { \"/tmp/virtualtest4\": content => \"four\" }\n}\n\nclass two {\n    File <| content == \"one\" |>\n    realize File[\"/tmp/virtualtest2\"]\n    realize(File[\"/tmp/virtualtest3\"], File[\"/tmp/virtualtest4\"])\n}\n\ninclude one, two\n"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml",
    "content": "---\nversion: 1\nlayers:\n  [{name: site, include: 'confdir:/confdirtest'},\n   {name: test, include: 'echo:/quick/brown/fox'},\n   {name: modules, include: ['module:/*::default'], exclude: 'module:/bad::default/' }\n  ]\nextensions:\n  scheme_handlers:\n    echo: 'PuppetX::Awesome2::EchoSchemeHandler'\n"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb",
    "content": "Puppet::Bindings.newbindings('confdirtest') do |scope|\n  bind {\n    name 'has_funny_hat'\n    to 'the pope'\n  }\n  bind {\n    name 'the_meaning_of_life'\n    to 42\n  }\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb",
    "content": "Puppet::Bindings.newbindings('awesome2::default') do |scope|\n  bind.name('all your base').to('are belong to us')\n  bind.name('env_meaning_of_life').to(puppet_string(\"$environment thinks it is 42\", __FILE__))\n  bind {\n    name 'awesome_x'\n    to 'golden'\n  }\n  bind {\n    name 'the_meaning_of_life'\n    to 100\n  }\n  bind {\n    name 'has_funny_hat'\n    to 'kkk'\n  }\n  bind {\n    name 'good_x'\n    to 'golden'\n  }\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet_x/awesome2/echo_scheme_handler.rb",
    "content": "require 'puppet/plugins/binding_schemes'\n\nmodule PuppetX\n  module Awesome2\n    # A binding scheme that echos its path\n    # 'echo:/quick/brown/fox' becomes key '::quick::brown::fox' => 'echo: quick brown fox'.\n    # (silly class for testing loading of extension)\n    #\n    class EchoSchemeHandler < Puppet::Plugins::BindingSchemes::BindingsSchemeHandler\n      def contributed_bindings(uri, scope, composer)\n        factory = ::Puppet::Pops::Binder::BindingsFactory\n        bindings = factory.named_bindings(\"echo\")\n        bindings.bind.name(uri.path.gsub(/\\//, '::')).to(\"echo: #{uri.path.gsub(/\\//, ' ').strip!}\")\n        factory.contributed_bindings(\"echo\", bindings.model) ### , nil)\n      end\n    end\n  end\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/lib/puppet/bindings/bad/default.rb",
    "content": "nil + nil + nil # broken on purpose, this file should never be loaded\n\nPuppet::Bindings.newbindings('bad::default') do |scope|\n  nil + nil + nil # broken on purpose, this should never be evaluated\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb",
    "content": "Puppet::Bindings.newbindings('good::default') do |scope|\n  bind {\n    name 'the_meaning_of_life'\n    to 300\n  }\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/config/binder_config/nolayer/binder_config.yaml",
    "content": "---\nversion: 1\ncategories:\n  - ['node', '$fqn']\n  - ['environment', '$environment']\n  - ['common', 'true']\n"
  },
  {
    "path": "spec/fixtures/unit/pops/binder/config/binder_config/ok/binder_config.yaml",
    "content": "---\nversion: 1\nlayers:\n  - {name: site, include: 'confdir:/'}\n  - {name: modules, include: 'module:/*::test/', exclude: 'module:/bad::test/' }\ncategories:\n  - ['node', '$fqn']\n  - ['environment', '$environment']\n  - ['common', 'true']"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/functions/usee_puppet.pp",
    "content": "function usee::usee_puppet() {\n   \"I'm the function usee::usee_puppet()\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/lib/puppet/functions/usee/callee.rb",
    "content": "Puppet::Functions.create_function(:'usee::callee') do\n  def callee(value)\n    \"usee::callee() was told '#{value}'\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/lib/puppet/functions/usee/usee_ruby.rb",
    "content": "Puppet::Functions.create_function(:'usee::usee_ruby') do\n  def usee_ruby()\n    \"I'm the function usee::usee_ruby()\"\n  end\nend\n\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/lib/puppet/type/usee_type.rb",
    "content": "Puppet::Type.newtype(:usee_type) do\n  newparam(:name, :namevar => true) do\n    desc 'An arbitrary name used as the identity of the resource.'\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/manifests/init.pp",
    "content": "function usee_puppet_init() {\n  \"I'm the function usee::usee_puppet_init()\"\n}\n\ntype Usee::One = Integer[1,1]\n\nclass usee {\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/types/zero.pp",
    "content": "type Usee::Zero = Integer[0,0]\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee2/lib/puppet/functions/usee2/callee.rb",
    "content": "Puppet::Functions.create_function(:'usee2::callee') do\n  def callee(value)\n    \"usee2::callee() was told '#{value}'\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/functions/puppet_calling_puppet.pp",
    "content": "function user::puppet_calling_puppet () {\n   usee::usee_puppet()\n}\n\n\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/functions/puppet_calling_puppet_init.pp",
    "content": "function user::puppet_calling_puppet_init () {\n   usee_puppet_init()\n}\n\n\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/functions/puppet_calling_ruby.pp",
    "content": "function user::puppet_calling_ruby() {\n    usee::usee_ruby()\n}\n\n\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/lib/puppet/functions/user/caller.rb",
    "content": "Puppet::Functions.create_function(:'user::caller') do\n  def caller()\n    call_function('usee::callee', 'passed value') + \" + I am user::caller()\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/lib/puppet/functions/user/caller2.rb",
    "content": "Puppet::Functions.create_function(:'user::caller2') do\n  def caller2()\n    call_function('usee2::callee', 'passed value') + \" + I am user::caller2()\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/lib/puppet/functions/user/ruby_calling_puppet.rb",
    "content": "Puppet::Functions.create_function(:'user::ruby_calling_puppet') do\n  def ruby_calling_puppet()\n     call_function('usee::usee_puppet')\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/lib/puppet/functions/user/ruby_calling_puppet_init.rb",
    "content": "Puppet::Functions.create_function(:'user::ruby_calling_puppet_init') do\n  def ruby_calling_puppet_init()\n     call_function('usee_puppet_init')\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/lib/puppet/functions/user/ruby_calling_ruby.rb",
    "content": "Puppet::Functions.create_function(:'user::ruby_calling_ruby') do\n  def ruby_calling_ruby()\n    call_function('usee::usee_ruby')\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/manifests/init.pp",
    "content": "function user::puppet_init_calling_puppet() {\n  usee::usee_puppet()\n}\n\nfunction user::puppet_init_calling_puppet_init() {\n  usee_puppet_init()\n}\n\nfunction user::puppet_init_calling_ruby() {\n  usee::usee_ruby()\n}\n\nclass user {\n  # Dummy resource. Added just to assert that a ruby type in another module is loaded correctly\n  # by the auto loader\n  Usee_type {\n    name => 'pelle'\n  }\n\n  case $::case_number {\n    1: {\n      # Call a puppet function that resides in usee/functions directly from init.pp\n      #\n      notify { 'case_1': message => usee::usee_puppet() }\n    }\n    2: {\n      # Call a puppet function that resides in usee/manifests/init.pp directly from init.pp\n      #\n      include usee\n      notify { 'case_2': message => usee_puppet_init() }\n    }\n    3: {\n      # Call a ruby function that resides in usee directly from init.pp\n      #\n      notify { 'case_3': message => usee::usee_ruby() }\n    }\n    4: {\n      # Call a puppet function that resides in usee/functions from a puppet function under functions\n      #\n      notify { 'case_4': message => user::puppet_calling_puppet() }\n    }\n    5: {\n      # Call a puppet function that resides in usee/manifests/init.pp from a puppet function under functions\n      #\n      include usee\n      notify { 'case_5': message => user::puppet_calling_puppet_init() }\n    }\n    6: {\n      # Call a ruby function that resides in usee from a puppet function under functions\n      #\n      notify { 'case_6': message => user::puppet_calling_ruby() }\n    }\n    7: {\n      # Call a puppet function that resides in usee/functions from a puppet function in init.pp\n      #\n      notify { 'case_7': message => user::puppet_init_calling_puppet() }\n    }\n    8: {\n      # Call a puppet function that resides in usee/manifests/init.pp from a puppet function in init.pp\n      #\n      include usee\n      notify { 'case_8': message => user::puppet_init_calling_puppet_init() }\n    }\n    9: {\n      # Call a ruby function that resides in usee from a puppet function in init.pp\n      #\n      notify { 'case_9': message => user::puppet_init_calling_ruby() }\n    }\n    10: {\n      # Call a puppet function that resides in usee/functions from a ruby function in this module\n      #\n      notify { 'case_10': message => user::ruby_calling_puppet() }\n    }\n    11: {\n      # Call a puppet function that resides in usee/manifests/init.pp from a ruby function in this module\n      #\n      include usee\n      notify { 'case_11': message => user::ruby_calling_puppet_init() }\n    }\n    12: {\n      # Call a ruby function that resides in usee from a ruby function in this module\n      #\n      notify { 'case_12': message => user::ruby_calling_ruby() }\n    }\n  }\n}\n\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/metadata.json",
    "content": "{\n  \"name\": \"test-user\",\n  \"author\": \"test\",\n  \"description\": \"\",\n  \"license\": \"\",\n  \"source\": \"\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": [\n  ]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/types/withuseeone.pp",
    "content": "type User::WithUseeOne = Array[Usee::One]\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/types/withuseezero.pp",
    "content": "type User::WithUseeZero = Array[Usee::Zero]\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:bad_func_load, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    \"the returned value\"\n  end\n\n  def method_here_is_illegal()\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load2.rb",
    "content": "module Puppet::Parser::Functions\n  x = newfunction(:bad_func_load2, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    \"some return value\"\n  end\nend\ndef illegal_method_here\nend\nx\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load3.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:bad_func_load3, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    def bad_func_load3_illegal_method\n      \"some return value from illegal method\"\n    end\n    \"some return value\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load4.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:bad_func_load4, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    def self.bad_func_load4_illegal_method\n      \"some return value from illegal method\"\n    end\n    \"some return value\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load5.rb",
    "content": "x = module Puppet::Parser::Functions\n  newfunction(:bad_func_load5, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    \"some return value\"\n  end\nend\ndef self.bad_func_load5_illegal_method\nend\n# Attempt to get around problem of not returning what newfunction returns\nx\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/callee.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:callee, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    \"usee::callee() got '#{arguments[0]}'\"\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/callee_ws.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:callee_ws, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    \"usee::callee_ws() got '#{self['passed_in_scope']}'\"\n  end\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/func_with_syntax_error.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:func_with_syntax_error, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API having a syntax error\n  EOS\n  ) do |arguments|\n    # this syntax error is here on purpose!\n    1+ + + +\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/good_func_load.rb",
    "content": "module Puppet::Parser::Functions\n  newfunction(:good_func_load, :type => :rvalue, :doc => <<-EOS\n    A function using the 3x API\n  EOS\n  ) do |arguments|\n    # This is not illegal\n    Float(\"3.14\")\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/metadata.json",
    "content": "{\n  \"name\": \"test-usee\",\n  \"author\": \"test\",\n  \"description\": \"\",\n  \"license\": \"\",\n  \"source\": \"\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": []\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/functions/puppetcalled.pp",
    "content": "function user::puppetcalled($who) {\n  \"Did you call to say you love $who?\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/functions/puppetcaller.pp",
    "content": "function user::puppetcaller {\n  \"${callee(first)} - ${callee(second)}\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/functions/puppetcaller4.pp",
    "content": "function user::puppetcaller4 {\n  user::caller()\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/lib/puppet/functions/user/caller.rb",
    "content": "Puppet::Functions.create_function(:'user::caller') do\n  def caller()\n    call_function('callee', 'first') + ' - ' + call_function('callee', 'second')\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/lib/puppet/functions/user/caller_ws.rb",
    "content": "Puppet::Functions.create_function(:'user::caller_ws', Puppet::Functions::InternalFunction) do\n  dispatch :caller_ws do\n    scope_param\n    param 'String', :value\n  end\n\n  def caller_ws(scope, value)\n    scope = scope.compiler.newscope(scope)\n    scope['passed_in_scope'] = value\n    call_function_with_scope(scope, 'callee_ws')\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/lib/puppet/functions/user/callingpuppet.rb",
    "content": "Puppet::Functions.create_function(:'user::callingpuppet') do\n  def callingpuppet()\n    call_function('user::puppetcalled', 'me')\n  end\nend\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/metadata.json",
    "content": "{\n  \"name\": \"test-user\",\n  \"author\": \"test\",\n  \"description\": \"\",\n  \"license\": \"\",\n  \"source\": \"\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": [{ \"name\": \"test/usee\" }]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/module_no_lib/modules/modulea/functions/hello.pp",
    "content": "function modulea::hello() {\n  \"modulea::hello()\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/module_no_lib/modules/modulea/manifests/init.pp",
    "content": "class modulea {\n\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/module_no_lib/modules/modulea/metadata.json",
    "content": "{\n  \"name\": \"test-modulea\",\n  \"author\": \"test\",\n  \"license\": \"\",\n  \"project_page\": \"\",\n  \"source\": \"\",\n  \"summary\": \"\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": []\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/no_modules/manifests/site.pp",
    "content": "function bar() {\n  $value_from_scope\n}\n\nclass foo::bar {\n  with(1) |$x| { notice $x }\n  notify { bar(): }\n}\n\ninclude foo::bar\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/pp_resources/.resource_types/addlogic.pp",
    "content": "$rname = 'addlogic'\nPuppet::Resource::ResourceType3.new($rname)"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/pp_resources/.resource_types/badcall.pp",
    "content": "Integer.new(34)"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/pp_resources/.resource_types/empty.pp",
    "content": "# Just some\n# comments\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/pp_resources/.resource_types/myresource.pp",
    "content": "Puppet::Resource::ResourceType3.new('myresource',\n  [Puppet::Resource::Param.new(String, 'myprop')],\n  [Puppet::Resource::Param.new(String, 'myparam')]\n)"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/pp_resources/.resource_types/wrongname.pp",
    "content": "Puppet::Resource::ResourceType3.new('notwrongname')"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/hello.pp",
    "content": "function modulea::hello() {\n  \"modulea::hello()\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/subspace/hello.pp",
    "content": "function modulea::subspace::hello() {\n  \"modulea::subspace::hello()\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/lib/puppet/functions/modulea/rb_func_a.rb",
    "content": "Puppet::Functions.create_function(:'modulea::rb_func_a') do\n  def rb_func_a()\n    \"I am modulea::rb_func_a()\"\n  end\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/lib/puppet/functions/rb_func_a.rb",
    "content": "Puppet::Functions.create_function(:rb_func_a) do\n  def rb_func_a()\n    \"I am rb_func_a()\"\n  end\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/manifests/init.pp",
    "content": "class modulea {\n\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/metadata.json",
    "content": "{\n  \"name\": \"test-modulea\",\n  \"author\": \"test\",\n  \"license\": \"\",\n  \"project_page\": \"\",\n  \"source\": \"\",\n  \"summary\": \"\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": []\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/wo_metadata_module/modules/moduleb/lib/puppet/functions/moduleb/rb_func_b.rb",
    "content": "Puppet::Functions.create_function(:'moduleb::rb_func_b') do\n  def rb_func_b()\n    # Should be able to call modulea::rb_func_a()\n    call_function('modulea::rb_func_a') + \" + I am moduleb::rb_func_b()\"\n  end\nend"
  },
  {
    "path": "spec/fixtures/unit/pops/loaders/loaders/wo_metadata_module/modules/moduleb/manifests/init.pp",
    "content": "class moduleb {\n\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/aliastest.pp",
    "content": "file { \"a file\":\n    path => \"/tmp/aliastest\",\n    ensure => file\n}\n\nfile { \"another\":\n    path => \"/tmp/aliastest2\",\n    ensure => file,\n    require => File[\"a file\"]\n}\n\nfile { \"a third\":\n    path => \"/tmp/aliastest3\",\n    ensure => file,\n    require => File[\"/tmp/aliastest\"]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/append.pp",
    "content": "$var=['/tmp/file1','/tmp/file2']\n\nclass arraytest {\n    $var += ['/tmp/file3', '/tmp/file4']\n    file {\n        $var:\n            content => \"test\"\n    }\n}\n\ninclude arraytest\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp",
    "content": "# $Id$\n\ndefine testargs($file, $mode = 755) {\n    file { $file: ensure => file, mode => $mode }\n}\n\ntestargs { \"testingname\":\n    file => \"/tmp/argumenttest1\"\n}\n\ntestargs { \"testingother\":\n    file => \"/tmp/argumenttest2\",\n    mode => '0644'\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/arithmetic_expression.pp",
    "content": "\n$one = 1.30\n$two = 2.034e-2\n\n$result = ((( $two + 2) / $one) + 4 * 5.45) - (6 << 7) + (0x800 + -9)\n\n\nnotice(\"result is $result == 1295.87692307692\")\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/arraytrailingcomma.pp",
    "content": "file {\n    [\"/tmp/arraytrailingcomma1\",\"/tmp/arraytrailingcomma2\", ]: content => \"tmp\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/casestatement.pp",
    "content": "# $Id$\n\n$var = \"value\"\n\ncase $var {\n  \"nope\": {\n     file { \"/tmp/fakefile\": mode => '0644', ensure => file }\n  }\n  \"value\": {\n     file { \"/tmp/existsfile\": mode => '0755', ensure => file }\n  }\n}\n\n$ovar = \"yayness\"\n\ncase $ovar {\n    \"fooness\": {\n         file { \"/tmp/nostillexistsfile\": mode => '0644', ensure => file }\n    }\n    \"booness\", \"yayness\": {\n        case $var {\n            \"nep\": {\n                 file { \"/tmp/noexistsfile\": mode => '0644', ensure => file }\n            }\n            \"value\": {\n                 file { \"/tmp/existsfile2\": mode => '0755', ensure => file }\n            }\n        }\n    }\n}\n\ncase $ovar {\n    \"fooness\": {\n         file { \"/tmp/nostillexistsfile\": mode => '0644', ensure => file }\n    }\n    default: {\n        file { \"/tmp/existsfile3\": mode => '0755', ensure => file }\n    }\n}\n\n$bool = true\n\ncase $bool {\n    true: {\n        file { \"/tmp/existsfile4\": mode => '0755', ensure => file }\n    }\n}\n\n$yay = yay\n$a = yay\n$b = boo\n\ncase $yay {\n    $a: { file { \"/tmp/existsfile5\": mode => '0755', ensure => file } }\n    $b: { file { \"/tmp/existsfile5\": mode => '0644', ensure => file } }\n    default: { file { \"/tmp/existsfile5\": mode => '0711', ensure => file } }\n\n}\n\n$regexvar = \"exists regex\"\ncase $regexvar {\n    \"no match\": { file { \"/tmp/existsfile6\": mode => '0644', ensure => file } }\n    /(.*) regex$/: { file { \"/tmp/${1}file6\": mode => '0755', ensure => file } }\n    default: { file { \"/tmp/existsfile6\": mode => '0711', ensure => file } }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp",
    "content": "# $Id$\n\nclass base {\n    file { \"/tmp/classheir1\": ensure => file, mode => '0755' }\n}\n\nclass sub1 inherits base {\n    file { \"/tmp/classheir2\": ensure => file, mode => '0755' }\n}\n\nclass sub2 inherits base {\n    file { \"/tmp/classheir3\": ensure => file, mode => '0755' }\n}\n\ninclude sub1, sub2\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/classincludes.pp",
    "content": "# $Id$\n\nclass base {\n    file { \"/tmp/classincludes1\": ensure => file, mode => '0755' }\n}\n\nclass sub1 inherits base {\n    file { \"/tmp/classincludes2\": ensure => file, mode => '0755' }\n}\n\nclass sub2 inherits base {\n    file { \"/tmp/classincludes3\": ensure => file, mode => '0755' }\n}\n\n$sub = \"sub2\"\n\ninclude sub1, $sub\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/classpathtest.pp",
    "content": "# $Id$\n\ndefine mytype {\n    file { \"/tmp/classtest\": ensure => file, mode => '0755' }\n}\n\nclass testing {\n    mytype { \"componentname\": }\n}\n\ninclude testing\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/collection.pp",
    "content": "class one {\n    @file { \"/tmp/colltest1\": content => \"one\" }\n    @file { \"/tmp/colltest2\": content => \"two\" }\n}\n\nclass two {\n    File <| content == \"one\" |>\n}\n\ninclude one, two\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/collection_override.pp",
    "content": "@file {\n    \"/tmp/collection\":\n        content => \"whatever\"\n}\n\nFile<| |> {\n    mode => '0600'\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/collection_within_virtual_definitions.pp",
    "content": "define test($name) {\n    file {\"/tmp/collection_within_virtual_definitions1_$name.txt\":\n        content => \"File name $name\\n\"\n    }\n    Test2 <||>\n}\n\ndefine test2() {\n    file {\"/tmp/collection_within_virtual_definitions2_$name.txt\":\n        content => \"This is a test\\n\"\n    }\n}\n\nnode default {\n    @test {\"foo\":\n        name => \"foo\"\n    }\n    @test2 {\"foo2\": }\n    Test <||>\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/componentmetaparams.pp",
    "content": "file { \"/tmp/component1\":\n    ensure => file\n}\n\ndefine thing {\n    file { $name: ensure => file }\n}\n\nthing { \"/tmp/component2\":\n    require => File[\"/tmp/component1\"]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/componentrequire.pp",
    "content": "define testfile($mode) {\n    file { $name: mode => $mode, ensure => present }\n}\n\ntestfile { \"/tmp/testing_component_requires2\": mode => '0755' }\n\nfile { \"/tmp/testing_component_requires1\": mode => '0755', ensure => present,\n    require => Testfile[\"/tmp/testing_component_requires2\"] }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp",
    "content": "# $Id$\n\nclass base {\n    file { \"/tmp/deepclassheir1\": ensure => file, mode => '0755' }\n}\n\nclass sub1 inherits base {\n    file { \"/tmp/deepclassheir2\": ensure => file, mode => '0755' }\n}\n\nclass sub2 inherits sub1 {\n    file { \"/tmp/deepclassheir3\": ensure => file, mode => '0755' }\n}\n\nclass sub3 inherits sub2 {\n    file { \"/tmp/deepclassheir4\": ensure => file, mode => '0755' }\n}\n\nclass sub4 inherits sub3 {\n    file { \"/tmp/deepclassheir5\": ensure => file, mode => '0755' }\n}\n\ninclude sub4\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp",
    "content": "# $Id$\n\n$file = \"/tmp/defineoverrides1\"\n\ndefine myfile($mode) {\n    file { $name: ensure => file, mode => $mode }\n}\n\nclass base {\n    myfile { $file: mode => '0644' }\n}\n\nclass sub inherits base {\n    Myfile[$file] { mode => '0755', } # test the end-comma\n}\n\ninclude sub\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/emptyclass.pp",
    "content": "# $Id$\n\ndefine component {\n}\n\nclass testing {\n}\n\ninclude testing\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/emptyexec.pp",
    "content": "exec { \"touch /tmp/emptyexectest\":\n    path => \"/usr/bin:/bin\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/emptyifelse.pp",
    "content": "\nif false {\n} else {\n  # nothing here\n}\n\nif true {\n  # still nothing\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/falsevalues.pp",
    "content": "$value = false\n\nfile { \"/tmp/falsevalues$value\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/filecreate.pp",
    "content": "# $Id$\n\nfile {\n    \"/tmp/createatest\": ensure => file, mode => '0755';\n    \"/tmp/createbtest\": ensure => file, mode => '0755'\n}\n\nfile {\n    \"/tmp/createctest\": ensure => file;\n    \"/tmp/createdtest\": ensure => file;\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/fqdefinition.pp",
    "content": "define one::two($ensure) {\n    file { \"/tmp/fqdefinition\": ensure => $ensure }\n}\n\none::two { \"/tmp/fqdefinition\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/fqparents.pp",
    "content": "class base {\n    class one {\n        file { \"/tmp/fqparent1\": ensure => file }\n    }\n}\n\nclass two::three inherits base::one {\n    file { \"/tmp/fqparent2\": ensure => file }\n}\n\ninclude two::three\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/funccomma.pp",
    "content": "@file {\n    [\"/tmp/funccomma1\",\"/tmp/funccomma2\"]: content => \"1\"\n}\n\nrealize( File[\"/tmp/funccomma1\"], File[\"/tmp/funccomma2\"] , )\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/hash.pp",
    "content": "\n$hash = { \"file\" => \"/tmp/myhashfile1\" }\n\nfile {\n    $hash[\"file\"]:\n        ensure => file, content => \"content\";\n}\n\n$hash2 = { \"a\" => { key => \"/tmp/myhashfile2\" }}\n\nfile {\n    $hash2[\"a\"][key]:\n        ensure => file, content => \"content\";\n}\n\ndefine test($a = { \"b\" => \"c\" }) {\n    file {\n        $a[\"b\"]:\n            ensure => file, content => \"content\"\n    }\n}\n\ntest {\n    \"test\":\n        a => { \"b\" => \"/tmp/myhashfile3\" }\n}\n\n$hash3 = { mykey => \"/tmp/myhashfile4\" }\n$key = \"mykey\"\n\nfile {\n    $hash3[$key]: ensure => file, content => \"content\"\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/ifexpression.pp",
    "content": "$one = 1\n$two = 2\n\nif ($one < $two) and (($two < 3) or ($two == 2)) {\n    notice(\"True!\")\n}\n\nif \"test regex\" =~ /(.*) regex/ {\n    file {\n        \"/tmp/${1}iftest\": ensure => file, mode => '0755'\n    }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/implicititeration.pp",
    "content": "# $Id$\n\n$files = [\"/tmp/iterationatest\", \"/tmp/iterationbtest\"]\n\nfile { $files: ensure => file, mode => '0755' }\n\nfile { [\"/tmp/iterationctest\", \"/tmp/iterationdtest\"]:\n    ensure => file,\n    mode => '0755'\n}\n\nfile {\n    [\"/tmp/iterationetest\", \"/tmp/iterationftest\"]: ensure => file, mode => '0755';\n    [\"/tmp/iterationgtest\", \"/tmp/iterationhtest\"]: ensure => file, mode => '0755';\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/multilinecomments.pp",
    "content": "\n/*\nfile {\n    \"/tmp/multilinecomments\": content => \"pouet\"\n}\n*/\n\n/* and another one for #2333, the whitespace after the \nend comment is here on purpose */  \n\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/multipleclass.pp",
    "content": "class one {\n    file { \"/tmp/multipleclassone\": content => \"one\" }\n}\n\nclass one {\n    file { \"/tmp/multipleclasstwo\": content => \"two\" }\n}\n\ninclude one\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp",
    "content": "# $Id$\n\nfile {\n    \"/tmp/multipleinstancesa\": ensure => file, mode => '0755';\n    \"/tmp/multipleinstancesb\": ensure => file, mode => '0755';\n    \"/tmp/multipleinstancesc\": ensure => file, mode => '0755';\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/multisubs.pp",
    "content": "class base {\n    file { \"/tmp/multisubtest\": content => \"base\", mode => '0644' }\n}\n\nclass sub1 inherits base {\n    File[\"/tmp/multisubtest\"] { mode => '0755' }\n}\n\nclass sub2 inherits base {\n    File[\"/tmp/multisubtest\"] { content => sub2 }\n}\n\ninclude sub1, sub2\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/namevartest.pp",
    "content": "define filetest($mode, $ensure = file) {\n    file { $name:\n        mode => $mode,\n        ensure => $ensure\n    }\n}\n\nfiletest { \"/tmp/testfiletest\": mode => '0644'}\nfiletest { \"/tmp/testdirtest\": mode => '0755', ensure => directory}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/scopetest.pp",
    "content": "\n$mode = 640\n\ndefine thing {\n    file { \"/tmp/$name\": ensure => file, mode => $mode }\n}\n\nclass testing {\n    $mode = 755\n    thing {scopetest: }\n}\n\ninclude testing\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/selectorvalues.pp",
    "content": "$value1 = \"\"\n$value2 = true\n$value3 = false\n$value4 = yay\n\n$test = \"yay\"\n\n$mode1 = $value1 ? {\n    \"\" => 755,\n    default => 644\n}\n\n$mode2 = $value2 ? {\n    true => 755,\n    default => 644\n}\n\n$mode3 = $value3 ? {\n    false => 755,\n    default => 644\n}\n\n$mode4 = $value4 ? {\n    $test => 755,\n    default => 644\n}\n\n$mode5 = yay ? {\n    $test => 755,\n    default => 644\n}\n\n$mode6 = $mode5 ? {\n    755 => 755\n}\n\n$mode7 = \"test regex\" ? {\n    /regex$/ => 755,\n    default => 644\n}\n\n\nfile { \"/tmp/selectorvalues1\": ensure => file, mode => $mode1 }\nfile { \"/tmp/selectorvalues2\": ensure => file, mode => $mode2 }\nfile { \"/tmp/selectorvalues3\": ensure => file, mode => $mode3 }\nfile { \"/tmp/selectorvalues4\": ensure => file, mode => $mode4 }\nfile { \"/tmp/selectorvalues5\": ensure => file, mode => $mode5 }\nfile { \"/tmp/selectorvalues6\": ensure => file, mode => $mode6 }\nfile { \"/tmp/selectorvalues7\": ensure => file, mode => $mode7 }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp",
    "content": "# $Id$\n\nFile { mode => '0755' }\n\nfile { \"/tmp/defaulttest\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/simpleselector.pp",
    "content": "# $Id$\n\n$var = \"value\"\n\nfile { \"/tmp/snippetselectatest\":\n    ensure => file,\n    mode => $var ? {\n        nottrue => 641,\n        value => 755\n    }\n}\n\nfile { \"/tmp/snippetselectbtest\":\n    ensure => file,\n    mode => $var ? {\n        nottrue => 644,\n        default => 755\n    }\n}\n\n$othervar = \"complex value\"\n\nfile { \"/tmp/snippetselectctest\":\n    ensure => file,\n    mode => $othervar ? {\n        \"complex value\" => 755,\n        default => 644\n    }\n}\n$anothervar = Yayness\n\nfile { \"/tmp/snippetselectdtest\":\n    ensure => file,\n    mode => $anothervar ? {\n        Yayness => 755,\n        default => 644\n    }\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/singleary.pp",
    "content": "# $Id$\n\nfile { \"/tmp/singleary1\":\n    ensure => file\n}\n\nfile { \"/tmp/singleary2\":\n    ensure => file\n}\n\nfile { \"/tmp/singleary3\":\n    ensure => file,\n    require => [File[\"/tmp/singleary1\"], File[\"/tmp/singleary2\"]]\n}\n\nfile { \"/tmp/singleary4\":\n    ensure => file,\n    require => [File[\"/tmp/singleary1\"]]\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/singlequote.pp",
    "content": "# $Id$\n\nfile { \"/tmp/singlequote1\":\n    ensure => file,\n    content => 'a $quote'\n}\n\nfile { \"/tmp/singlequote2\":\n    ensure => file,\n    content => 'some \"\\yayness\\\"'\n}\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/singleselector.pp",
    "content": "$value1 = \"\"\n$value2 = true\n$value3 = false\n$value4 = yay\n\n$test = \"yay\"\n\n$mode1 = $value1 ? {\n    \"\" => 755\n}\n\n$mode2 = $value2 ? {\n    true => 755\n}\n\n$mode3 = $value3 ? {\n    default => 755\n}\n\nfile { \"/tmp/singleselector1\": ensure => file, mode => $mode1 }\nfile { \"/tmp/singleselector2\": ensure => file, mode => $mode2 }\nfile { \"/tmp/singleselector3\": ensure => file, mode => $mode3 }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/subclass_name_duplication.pp",
    "content": "#!/usr/bin/env puppet\n\nclass one::fake {\n    file { \"/tmp/subclass_name_duplication1\": ensure => present }\n}\n\nclass two::fake {\n    file { \"/tmp/subclass_name_duplication2\": ensure => present }\n}\n\ninclude one::fake, two::fake\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/tag.pp",
    "content": "# $Id$\n\n$variable = value\n\ntag yayness, rahness\n\ntag booness, $variable\n\nfile { \"/tmp/settestingness\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/tagged.pp",
    "content": "# $Id$\n\ntag testing\ntag(funtest)\n\nclass tagdefine {\n    $path = tagged(tagdefine) ? {\n        true => \"true\", false => \"false\"\n    }\n\n    file { \"/tmp/taggeddefine$path\": ensure => file }\n}\n\ninclude tagdefine\n\n$yayness = tagged(yayness) ? {\n    true => \"true\", false => \"false\"\n}\n\n$funtest = tagged(testing) ? {\n    true => \"true\", false => \"false\"\n}\n\n$both = tagged(testing, yayness) ? {\n    true => \"true\", false => \"false\"\n}\n\n$bothtrue = tagged(testing, testing) ? {\n    true => \"true\", false => \"false\"\n}\n\nfile { \"/tmp/taggedyayness$yayness\": ensure => file }\nfile { \"/tmp/taggedtesting$funtest\": ensure => file }\nfile { \"/tmp/taggedboth$both\": ensure => file }\nfile { \"/tmp/taggedbothtrue$bothtrue\": ensure => file }\n"
  },
  {
    "path": "spec/fixtures/unit/pops/parser/lexer/virtualresources.pp",
    "content": "class one {\n    @file { \"/tmp/virtualtest1\": content => \"one\" }\n    @file { \"/tmp/virtualtest2\": content => \"two\" }\n    @file { \"/tmp/virtualtest3\": content => \"three\" }\n    @file { \"/tmp/virtualtest4\": content => \"four\" }\n}\n\nclass two {\n    File <| content == \"one\" |>\n    realize File[\"/tmp/virtualtest2\"]\n    realize(File[\"/tmp/virtualtest3\"], File[\"/tmp/virtualtest4\"])\n}\n\ninclude one, two\n"
  },
  {
    "path": "spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_input.out",
    "content": "root:0:system:system,bin,sys,security,cron,audit,lp:/:/usr/bin/ksh:root:general:true:false:false:true:true:system:nosak:ALL:0:SYSTEM:NONE:22:files:compat:0:0:false:0:0:-1:0:0:0:0:0:0:0:8:0:0:0:-1:-1:-1:-1:-1:-1:-1:1527849270:1533085305:ssh:ssh:fd8c#!:215d#!:178e#!:12#!:290#!:fa72#!:fab2#!:882:10.10.28.247:147:This is some comment I added\n"
  },
  {
    "path": "spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_output.out",
    "content": "[\"root\", \"0\", \"system\", \"system,bin,sys,security,cron,audit,lp\", \"/\", \"/usr/bin/ksh\", \"root\", \"general\", \"true\", \"false\", \"false\", \"true\", \"true\", \"system\", \"nosak\", \"ALL\", \"0\", \"SYSTEM\", \"NONE\", \"22\", \"files\", \"compat\", \"0\", \"0\", \"false\", \"0\", \"0\", \"-1\", \"0\", \"0\", \"0\", \"0\", \"0\", \"0\", \"0\", \"8\", \"0\", \"0\", \"0\", \"-1\", \"-1\", \"-1\", \"-1\", \"-1\", \"-1\", \"-1\", \"1527849270\", \"1533085305\", \"ssh\", \"ssh\", \"fd8c:215d:178e:12:290:fa72:fab2:882\", \"10.10.28.247\", \"147\", \"This is some comment I added\"]\n"
  },
  {
    "path": "spec/fixtures/unit/provider/cron/crontab/single_line.yaml",
    "content": "---\n:longcommment:\n  :text: \"# This is a comment\"\n  :record:\n    :line: \"# This is a comment\"\n    :record_type: :comment\n:special:\n  :text: \"@hourly /bin/date\"\n  :record:\n    :special: hourly\n    :command: /bin/date\n    :record_type: :crontab\n:long_name:\n  :text: \"# Puppet Name: long_name\"\n  :record:\n    :line: \"# Puppet Name: long_name\"\n    :name: long_name\n    :record_type: :comment\n:multiple_minutes:\n  :text: 5,15 * * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    - \"15\"\n    :command: /bin/date\n    :record_type: :crontab\n:environment:\n  :text: ONE=TWO\n  :record:\n    :line: ONE=TWO\n    :record_type: :environment\n:empty:\n  :text: \"\"\n  :record:\n    :line: \"\"\n    :record_type: :blank\n:simple:\n  :text: \"* * * * * /bin/date\"\n  :record:\n    :command: /bin/date\n    :record_type: :crontab\n:whitespace:\n  :text: \"   \"\n  :record:\n    :line: \"   \"\n    :record_type: :blank\n:minute_and_hour:\n  :text: 5 15 * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    :hour:\n    - \"15\"\n    :command: /bin/date\n    :record_type: :crontab\n:lowercase_environment:\n  :text: a=b\n  :record:\n    :line: a=b\n    :record_type: :environment\n:special_with_spaces:\n  :text: \"@daily /bin/echo testing\"\n  :record:\n    :special: daily\n    :command: /bin/echo testing\n    :record_type: :crontab\n:tabs:\n  :text: !binary |\n    CQ==\n\n  :record:\n    :line: !binary |\n      CQ==\n\n    :record_type: :blank\n:multiple_minute_and_hour:\n  :text: 5,10 15,20 * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    - \"10\"\n    :hour:\n    - \"15\"\n    - \"20\"\n    :command: /bin/date\n    :record_type: :crontab\n:name:\n  :text: \"# Puppet Name: testing\"\n  :record:\n    :line: \"# Puppet Name: testing\"\n    :name: testing\n    :record_type: :comment\n:another_env:\n  :text: Testing=True\n  :record:\n    :line: Testing=True\n    :record_type: :environment\n:shortcomment:\n  :text: \"#\"\n  :record:\n    :line: \"#\"\n    :record_type: :comment\n:spaces_in_command:\n  :text: \"* * * * * /bin/echo testing\"\n  :record:\n    :command: /bin/echo testing\n    :record_type: :crontab\n:fourth_env:\n  :text: True=False\n  :record:\n    :line: True=False\n    :record_type: :environment\n:simple_with_minute:\n  :text: 5 * * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    :command: /bin/date\n    :record_type: :crontab\n:spaces_in_command_with_times:\n  :text: 5,10 15,20 * * * /bin/echo testing\n  :record:\n    :minute:\n    - \"5\"\n    - \"10\"\n    :hour:\n    - \"15\"\n    - \"20\"\n    :command: /bin/echo testing\n    :record_type: :crontab\n:name_with_spaces:\n  :text: \"# Puppet Name: another name\"\n  :record:\n    :line: \"# Puppet Name: another name\"\n    :name: another name\n    :record_type: :comment\n---\n:longcommment:\n  :text: \"# This is a comment\"\n  :record:\n    :line: \"# This is a comment\"\n    :record_type: :comment\n:special:\n  :text: \"@hourly /bin/date\"\n  :record:\n    :special: hourly\n    :command: /bin/date\n    :record_type: :crontab\n:long_name:\n  :text: \"# Puppet Name: long_name\"\n  :record:\n    :line: \"# Puppet Name: long_name\"\n    :name: long_name\n    :record_type: :comment\n:multiple_minutes:\n  :text: 5,15 * * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    - \"15\"\n    :command: /bin/date\n    :record_type: :crontab\n:environment:\n  :text: ONE=TWO\n  :record:\n    :line: ONE=TWO\n    :record_type: :environment\n:empty:\n  :text: \"\"\n  :record:\n    :line: \"\"\n    :record_type: :blank\n:simple:\n  :text: \"* * * * * /bin/date\"\n  :record:\n    :command: /bin/date\n    :record_type: :crontab\n:whitespace:\n  :text: \"   \"\n  :record:\n    :line: \"   \"\n    :record_type: :blank\n:minute_and_hour:\n  :text: 5 15 * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    :hour:\n    - \"15\"\n    :command: /bin/date\n    :record_type: :crontab\n:lowercase_environment:\n  :text: a=b\n  :record:\n    :line: a=b\n    :record_type: :environment\n:special_with_spaces:\n  :text: \"@daily /bin/echo testing\"\n  :record:\n    :special: daily\n    :command: /bin/echo testing\n    :record_type: :crontab\n:tabs:\n  :text: !binary |\n    CQ==\n\n  :record:\n    :line: !binary |\n      CQ==\n\n    :record_type: :blank\n:multiple_minute_and_hour:\n  :text: 5,10 15,20 * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    - \"10\"\n    :hour:\n    - \"15\"\n    - \"20\"\n    :command: /bin/date\n    :record_type: :crontab\n:name:\n  :text: \"# Puppet Name: testing\"\n  :record:\n    :line: \"# Puppet Name: testing\"\n    :name: testing\n    :record_type: :comment\n:another_env:\n  :text: Testing=True\n  :record:\n    :line: Testing=True\n    :record_type: :environment\n:shortcomment:\n  :text: \"#\"\n  :record:\n    :line: \"#\"\n    :record_type: :comment\n:spaces_in_command:\n  :text: \"* * * * * /bin/echo testing\"\n  :record:\n    :command: /bin/echo testing\n    :record_type: :crontab\n:fourth_env:\n  :text: True=False\n  :record:\n    :line: True=False\n    :record_type: :environment\n:simple_with_minute:\n  :text: 5 * * * * /bin/date\n  :record:\n    :minute:\n    - \"5\"\n    :command: /bin/date\n    :record_type: :crontab\n:spaces_in_command_with_times:\n  :text: 5,10 15,20 * * * /bin/echo testing\n  :record:\n    :minute:\n    - \"5\"\n    - \"10\"\n    :hour:\n    - \"15\"\n    - \"20\"\n    :command: /bin/echo testing\n    :record_type: :crontab\n:name_with_spaces:\n  :text: \"# Puppet Name: another name\"\n  :record:\n    :line: \"# Puppet Name: another name\"\n    :name: another name\n    :record_type: :comment\n"
  },
  {
    "path": "spec/fixtures/unit/provider/cron/crontab/vixie_header.txt",
    "content": "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n# (- installed on Thu Apr 12 12:16:01 2007)\n# (Cron version V5.0 -- $Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $)\n"
  },
  {
    "path": "spec/fixtures/unit/provider/cron/parsed/managed",
    "content": "# Puppet Name: real_job\n* * * * * /bin/true\n# Puppet Name: complex_job\nMAILTO=foo@example.com\nSHELL=/bin/sh\n@reboot /bin/true >> /dev/null 2>&1\n"
  },
  {
    "path": "spec/fixtures/unit/provider/cron/parsed/simple",
    "content": "# use /bin/sh to run commands, no matter what /etc/passwd says\nSHELL=/bin/sh\n# mail any output to `paul', no matter whose crontab this is\nMAILTO=paul\n#\n# run five minutes after midnight, every day\n5 0 * * *       $HOME/bin/daily.job >> $HOME/tmp/out 2>&1\n# run at 2:15pm on the first of every month -- output mailed to paul\n15 14 1 * *     $HOME/bin/monthly\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/dnfmodule/dnf-module-list.txt",
    "content": "localmirror-appstream\nName         Stream       Profiles                                  Summary                                                            \n389-ds       1.4 [e]                                                389 Directory Server (base)                                           \ngimp         2.8 [d][e]   common [d], devel [i]                     gimp module                                                        \nmariadb      10.3 [d][e]  client [i], server [d], galera            MariaDB Module                                                     \nnodejs       10 [d][e]    common [d], development, minimal [i], s2i Javascript runtime                                                 \nperl         5.26 [d][e]  common [d], minimal [i]                   Practical Extraction and Report Language                           \npostgresql   10 [d][e]    client, server [d] [i]                    PostgreSQL server and client module                                \nruby         2.5 [d][e]   common [d]                                An interpreter of object-oriented scripting language                  \nrust-toolset rhel8 [d][e] common [d] [i]                            Rust                                                               \nsubversion   1.10 [d][e]  common [d], server [i]                    Apache Subversion                                                  \nscala        2.10 [d]     common [d]                                A hybrid functional/object-oriented language for the JVM\nsquid        4 [d]        common [d]                                Squid - Optimising Web Delivery\nsubversion   1.10 [d]     common [d], server                        Apache Subversion\nswig         3.0 [d][x]   common [d], complete                      Connects C/C++/Objective C to some high-level programming languages\nvarnish      6 [d]        common [d]                                Varnish HTTP cache\nvirt         rhel [d][x]  common [d]                                Virtualization module\n\nHint: [d]efault, [e]nabled, [x]disabled, [i]nstalled\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/gem/gem-list-single-package",
    "content": "\n*** REMOTE GEMS ***\n\nbundler (1.6.2)\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/gem/line-with-1.8.5-warning",
    "content": "/home/jenkins/.rvm/gems/ruby-1.8.5-p231@global/gems/rubygems-bundler-0.9.0/lib/rubygems-bundler/regenerate_binstubs_command.rb:34: warning: parenthesize argument(s) for future version\n\n*** LOCAL GEMS ***\n\ncolumnize (0.3.2)\ndiff-lcs (1.1.3)\nmetaclass (0.0.1)\nmocha (0.10.5)\nrake (0.8.7)\nrspec-core (2.9.0)\nrspec-expectations (2.9.1)\nrspec-mocks (2.9.0)\nrubygems-bundler (0.9.0)\nrvm (1.11.3.3)\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/openbsd/pkginfo.detail",
    "content": "Information for bash-3.1.17\n\nComment:\nGNU Bourne Again Shell\n\nDescription:\nBash is the GNU Project's Bourne Again SHell, an sh-compatible\ncommand language interpreter that executes commands read from the\nstandard input or from a file.  Bash also incorporates useful\nfeatures from the Korn and C shells (ksh and csh).\n\nBash is intended to be a conformant implementation of the IEEE POSIX\nShell and Tools specification (IEEE Working Group 1003.2).\n\nMaintainer: Christian Weisgerber <naddy@openbsd.org>\n\nWWW: http://cnswww.cns.cwru.edu/~chet/bash/bashtop.html\n\n\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/openbsd/pkginfo.list",
    "content": "bash-3.1.17         GNU Bourne Again Shell\nbzip2-1.0.3         block-sorting file compressor, unencumbered\nexpat-2.0.0         XML 1.0 parser written in C\ngettext-0.14.5p1    GNU gettext\nlibiconv-1.9.2p3    character set conversion library\nlzo-1.08p1          portable speedy lossless data compression library\nopenvpn-2.0.6       easy-to-use, robust, and highly configurable VPN\npython-2.4.3p0      interpreted object-oriented programming language\nvim-7.0.42-no_x11   vi clone, many additional features\nwget-1.10.2p0       retrieve files from the web via HTTP, HTTPS and FTP\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/openbsd/pkginfo.query",
    "content": "bash-3.1.17         GNU Bourne Again Shell\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/openbsd/pkginfo_flavors.list",
    "content": "bash-3.1.17-static  GNU Bourne Again Shell\nvim-7.0.42-no_x11   vi clone, many additional features\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_implicit_version",
    "content": "pkg://solaris/dummy@1.0,5.11-0.151006:20140220T084443Z ---\npkg://solaris/dummy@1.0,5.11-0.151006:20140219T191632Z ---\npkg://solaris/dummy@1.0,5.11-0.151006:20140219T191204Z ---\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_solaris10",
    "content": "pkg://solaris/dummy@2.5.5,5.10-0.111:20131230T130000Z     installed  -----\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_solaris11.certificate_warning",
    "content": "Certificate '/var/pkg/ssl/871b4ed0ade09926e6adf95f86bf17535f987684' for publisher 'solarisstudio', needed to access 'https://pkg.oracle.com/solarisstudio/release/', will expire in '29' days.\npkg://solaris/dummy@1.0.6-0.175.0.0.0.2.537    i--\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_solaris11.ifo.installed",
    "content": "pkg://solaris/dummy@1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z    i--\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_solaris11.ifo.known",
    "content": "pkg://solaris/dummy@1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z    ---\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_solaris11.installed",
    "content": "pkg://solaris/dummy@1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z   installed  u----\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/dummy_solaris11.known",
    "content": "pkg://solaris/dummy@1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z   known      u----\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/incomplete",
    "content": "pkg://solaris/dummy@2.5.5,5.11-0.111:20131230T130000Z     installed  ---- RANDOM_TRASH  \n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/solaris11",
    "content": "pkg://solaris/dummy/dummy@3.0,5.11-0.175.0.0.0.2.537:20131230T130000Z      i--\npkg://solaris/dummy/dummy2@1.8.1.2-0.175.0.0.0.2.537:20131230T130000Z  i--\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkg/unknown_status",
    "content": "pkg://solaris/compress/zip@3.0,5.11-0.175.0.0.0.2.537:20131230T130000Z                   i--\npkg://solaris/archiver/gnu-tar@1.26,5.11-0.175.0.0.0.2.537:20131230T130000Z              i--\npkg://solaris/compress/bzip2@1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z               i--\npkg://solaris/compress/gzip@1.3.5,5.11-0.175.0.0.0.2.537:20131230T130000Z                i--\npkg://solaris/compress/p7zip@9.20.1,5.11-0.175.0.0.0.2.537:20131230T130000Z              i--\npkg://solaris/compress/unzip@6.0,5.11-0.175.0.0.0.2.537:20131230T130000Z                 x--\npkg://solaris/compress/zip@3.0,5.11-0.175.0.0.0.2.537:20131230T130000Z                   i--\npkg://solaris/x11/library/toolkit/libxaw7@1.0.9,5.11-0.175.0.0.0.0.1215:20131230T130000Z i--\npkg://solaris/x11/library/toolkit/libxt@1.0.9,5.11-0.175.0.0.0.0.1215:20131230T130000Z   i--\npkg://solaris/shell/bash@4.1.9,5.11-0.175.0.0.0.2.537:20131230T130000Z                   i--\npkg://solaris/shell/zsh@4.3.12,5.11-0.175.0.0.0.2.537:20131230T130000Z                   i--\npkg://solaris/security/sudo@1.8.1.2,5.11-0.175.0.0.0.2.537:20131230T130000Z              i--\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkgng/pkg.query",
    "content": "ca_root_nss 3.15.3.1 security/ca_root_nss\ncurl 7.33.0 ftp/curl\ngnupg 2.0.22 security/gnupg\nnmap 6.40 security/nmap\npkg 1.2.4_1 ports-mgmt/pkg\nzsh 5.0.2_1 shells/zsh\ntac_plus F4.0.4.27a net/tac_plus4\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkgng/pkg.query.zsh",
    "content": "zsh 5.0.2_1 shells/zsh\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/pkgng/pkg.version",
    "content": "shells/bash-completion             <   needs updating (index has 2.1_3)\nftp/curl                           <   needs updating (index has 7.33.0_2)\nshells/zsh                         <   needs updating (index has 5.0.4)\nsysutils/orphan                    ?   orphaned: sysutils/orphan\nsysutils/broken                    !   Comparison failed\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/puppetserver_gem/gem-list-local-packages",
    "content": "concurrent-ruby (1.1.5)\n\ndeep_merge (1.0.1)\n\nfast_gettext (1.1.2)\n\ngettext (3.2.2)\n\nhiera-eyaml (3.2.0)\n\nhighline (1.6.21)\n\nhocon (1.3.1, 1.2.5)\n\nlocale (2.1.3, 2.1.2)\n\nmulti_json (1.14.1)\n\noptimist (3.0.1)\n\npuppet-resource_api (1.8.13)\n\npuppetserver-ca (1.8.0)\n\nsemantic_puppet (1.0.2)\n\ntext (1.3.1)\n\nworld_airports (1.1.3)\n\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/sun/dummy.server",
    "content": "   PKGINST:  SUNWdummy\n      NAME:  Dummy server\n  CATEGORY:  system\n      ARCH:  i386\n   VERSION:  11.11.0,REV=2010.10.12.04.23\n   BASEDIR:  /\n    VENDOR:  Oracle Corporation\n      DESC:  Dummy server (9.6.1-P3)\n  INSTDATE:  Nov 05 2010 09:14\n   HOTLINE:  Please contact your local service provider\n    STATUS:  completely installed\n\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/sun/simple",
    "content": "   PKGINST:  SUNWdummy\n      NAME:  Dummy server\n  CATEGORY:  system\n      ARCH:  i386\n   VERSION:  11.11.0,REV=2010.10.12.04.23\n   BASEDIR:  /\n    VENDOR:  Oracle Corporation\n      DESC:  Dummy server (9.6.1-P3)\n  INSTDATE:  Nov 05 2010 09:14\n   HOTLINE:  Please contact your local service provider\n    STATUS:  completely installed\n\n   PKGINST:  SUNWdummyc\n      NAME:  Dummy client\n  CATEGORY:  system\n      ARCH:  i386\n   VERSION:  11.11.0,REV=2010.10.12.04.24\n   BASEDIR:  /\n    VENDOR:  Oracle Corporation\n      DESC:  Dummy client (9.6.1-P4)\n  INSTDATE:  Nov 05 2010 09:14\n   HOTLINE:  Please contact your local service provider\n    STATUS:  completely installed\n\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-broken-notices.txt",
    "content": "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n\nNetworkManager.x86_64              1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-glib.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-tui.x86_64          1:1.0.0-14.git20150121.b4ea599c.el7    base\nalsa-firmware.noarch               1.0.28-2.el7                           base\nalsa-lib.x86_64                    1.0.28-2.el7                           base\naudit.x86_64                       2.4.1-5.el7                            base\naudit-libs.x86_64                  2.4.1-5.el7                            base\nauthconfig.x86_64                  6.2.8-9.el7                            base\navahi.x86_64                       0.6.31-14.el7                          base\navahi-autoipd.x86_64               0.6.31-14.el7                          base\navahi-libs.x86_64                  0.6.31-14.el7                          base\nbash.x86_64                        4.2.46-12.el7                          base\nbind-libs-lite.x86_64              32:9.9.4-18.el7_1.1                    updates\nbind-license.noarch                32:9.9.4-18.el7_1.1                    updates\nbinutils.x86_64                    2.23.52.0.1-30.el7_1.2                 updates\nbiosdevname.x86_64                 0.6.1-2.el7                            base\nbtrfs-progs.x86_64                 3.16.2-1.el7                           base\nca-certificates.noarch             2015.2.4-70.0.el7_1                    updates\ncentos-logos.noarch                70.0.6-2.el7.centos                    updates\ncentos-release.x86_64              7-1.1503.el7.centos.2.8                base\ncpp.x86_64                         4.8.3-9.el7                            base\ncronie.x86_64                      1.4.11-13.el7                          base\ncronie-anacron.x86_64              1.4.11-13.el7                          base\ncryptsetup-libs.x86_64             1.6.6-3.el7                            base\ndbus.x86_64                        1:1.6.12-11.el7                        base\ndbus-libs.x86_64                   1:1.6.12-11.el7                        base\ndevice-mapper.x86_64               7:1.02.93-3.el7                        base\ndevice-mapper-event.x86_64         7:1.02.93-3.el7                        base\ndevice-mapper-event-libs.x86_64    7:1.02.93-3.el7                        base\ndevice-mapper-libs.x86_64          7:1.02.93-3.el7                        base\ndevice-mapper-persistent-data.x86_64\n                                   0.4.1-2.el7                            base\ndhclient.x86_64                    12:4.2.5-36.el7.centos                 base\ndhcp-common.x86_64                 12:4.2.5-36.el7.centos                 base\ndhcp-libs.x86_64                   12:4.2.5-36.el7.centos                 base\ndnsmasq.x86_64                     2.66-13.el7_1                          updates\ndracut.x86_64                      033-241.el7_1.1                        updates\ndracut-config-rescue.x86_64        033-241.el7_1.1                        updates\ndracut-network.x86_64              033-241.el7_1.1                        updates\ne2fsprogs.x86_64                   1.42.9-7.el7                           base\ne2fsprogs-libs.x86_64              1.42.9-7.el7                           base\nelfutils-libelf.x86_64             0.160-1.el7                            base\nelfutils-libs.x86_64               0.160-1.el7                            base\nethtool.x86_64                     2:3.15-2.el7                           base\nfirewalld.noarch                   0.3.9-11.el7                           base\nfreetype.x86_64                    2.4.11-10.el7_1.1                      updates\ngcc.x86_64                         4.8.3-9.el7                            base\nglib-networking.x86_64             2.40.0-1.el7                           base\nglib2.x86_64                       2.40.0-4.el7                           base\nglibc.x86_64                       2.17-78.el7                            base\nglibc-common.x86_64                2.17-78.el7                            base\nglibc-devel.x86_64                 2.17-78.el7                            base\nglibc-headers.x86_64               2.17-78.el7                            base\ngmp.x86_64                         1:6.0.0-11.el7                         base\ngnutls.x86_64                      3.3.8-12.el7                           base\ngrep.x86_64                        2.20-1.el7                             base\ngrub2.x86_64                       1:2.02-0.16.el7.centos                 base\ngrub2-tools.x86_64                 1:2.02-0.16.el7.centos                 base\ngrubby.x86_64                      8.28-11.el7                            base\nhwdata.noarch                      0.252-7.5.el7                          base\nhwdata.x86_64                      0.252-7.8.el7_1                        updates\ninitscripts.x86_64                 9.49.24-1.el7                          base\niproute.x86_64                     3.10.0-21.el7                          base\niprutils.x86_64                    2.4.3-3.el7                            base\nirqbalance.x86_64                  2:1.0.7-1.el7                          base\niwl100-firmware.noarch             39.31.5.1-36.el7                       base\niwl1000-firmware.noarch            1:39.31.5.1-36.el7                     base\niwl105-firmware.noarch             18.168.6.1-36.el7                      base\niwl135-firmware.noarch             18.168.6.1-36.el7                      base\niwl2000-firmware.noarch            18.168.6.1-36.el7                      base\niwl2030-firmware.noarch            18.168.6.1-36.el7                      base\niwl3160-firmware.noarch            22.0.7.0-36.el7                        base\niwl3945-firmware.noarch            15.32.2.9-36.el7                       base\niwl4965-firmware.noarch            228.61.2.24-36.el7                     base\niwl5000-firmware.noarch            8.83.5.1_1-36.el7                      base\niwl5150-firmware.noarch            8.24.2.2-36.el7                        base\niwl6000-firmware.noarch            9.221.4.1-36.el7                       base\niwl6000g2a-firmware.noarch         17.168.5.3-36.el7                      base\niwl6000g2b-firmware.noarch         17.168.5.2-36.el7                      base\niwl6050-firmware.noarch            41.28.5.1-36.el7                       base\niwl7260-firmware.noarch            22.0.7.0-36.el7                        base\nkbd.x86_64                         1.15.5-11.el7                          base\nkbd-misc.noarch                    1.15.5-11.el7                          base\nkernel.x86_64                      3.10.0-229.4.2.el7                     updates\nkernel-headers.x86_64              3.10.0-229.4.2.el7                     updates\nkernel-tools.x86_64                3.10.0-229.4.2.el7                     updates\nkernel-tools-libs.x86_64           3.10.0-229.4.2.el7                     updates\nkexec-tools.x86_64                 2.0.7-19.el7_1.2                       updates\nkmod.x86_64                        14-10.el7                              base\nkmod-libs.x86_64                   14-10.el7                              base\nkpartx.x86_64                      0.4.9-77.el7                           base\nkrb5-libs.x86_64                   1.12.2-14.el7                          base\nlibblkid.x86_64                    2.23.2-22.el7_1                        updates\nlibcom_err.x86_64                  1.42.9-7.el7                           base\nlibdb.x86_64                       5.3.21-17.el7_0.1                      base\nlibdb-utils.x86_64                 5.3.21-17.el7_0.1                      base\nlibdrm.x86_64                      2.4.56-2.el7                           base\nlibgcc.x86_64                      4.8.3-9.el7                            base\nlibgcrypt.x86_64                   1.5.3-12.el7_1.1                       updates\nlibgcrypt-devel.x86_64             1.5.3-12.el7_1.1                       updates\nlibgomp.x86_64                     4.8.3-9.el7                            base\nlibgudev1.x86_64                   208-20.el7_1.3                         updates\nlibmount.x86_64                    2.23.2-22.el7_1                        updates\nlibnl3.x86_64                      3.2.21-8.el7                           base\nlibnl3-cli.x86_64                  3.2.21-8.el7                           base\nlibpcap.x86_64                     14:1.5.3-4.el7_1.2                     updates\nlibsoup.x86_64                     2.46.0-3.el7                           base\nlibss.x86_64                       1.42.9-7.el7                           base\nlibstdc++.x86_64                   4.8.3-9.el7                            base\nlibtasn1.x86_64                    3.8-2.el7                              base\nlibteam.x86_64                     1.15-1.el7                             base\nlibuuid.x86_64                     2.23.2-22.el7_1                        updates\nlibxml2.x86_64                     2.9.1-5.el7_1.2                        updates\nlibxml2-devel.x86_64               2.9.1-5.el7_1.2                        updates\nlibyaml.x86_64                     0.1.4-11.el7_0                         base\nlinux-firmware.noarch              20140911-0.1.git365e80c.el7            base\nlvm2.x86_64                        7:2.02.115-3.el7                       base\nlvm2-libs.x86_64                   7:2.02.115-3.el7                       base\nmariadb-libs.x86_64                1:5.5.41-2.el7_0                       base\nmicrocode_ctl.x86_64               2:2.1-10.el7                           base\nnettle.x86_64                      2.7.1-4.el7                            base\nnspr.x86_64                        4.10.8-1.el7_1                         updates\nnss.x86_64                         3.18.0-2.2.el7_1                       updates\nnss-softokn.x86_64                 3.16.2.3-9.el7                         base\nnss-softokn-freebl.x86_64          3.16.2.3-9.el7                         base\nnss-sysinit.x86_64                 3.18.0-2.2.el7_1                       updates\nnss-tools.x86_64                   3.18.0-2.2.el7_1                       updates\nnss-util.x86_64                    3.18.0-1.el7_1                         updates\nntpdate.x86_64                     4.2.6p5-19.el7.centos                  base\nnumactl-libs.x86_64                2.0.9-4.el7                            base\nopen-vm-tools.x86_64               9.4.0-6.el7                            base\nopenldap.x86_64                    2.4.39-6.el7                           base\nopenssh.x86_64                     6.6.1p1-12.el7_1                       updates\nopenssh-clients.x86_64             6.6.1p1-12.el7_1                       updates\nopenssh-server.x86_64              6.6.1p1-12.el7_1                       updates\nopenssl.x86_64                     1:1.0.1e-42.el7.4                      updates\nopenssl-libs.x86_64                1:1.0.1e-42.el7.4                      updates\np11-kit.x86_64                     0.20.7-3.el7                           base\np11-kit-trust.x86_64               0.20.7-3.el7                           base\npam.x86_64                         1.1.8-12.el7                           base\nparted.x86_64                      3.1-20.el7                             base\npcre.x86_64                        8.32-14.el7                            base\nplymouth.x86_64                    0.8.9-0.13.20140113.el7.centos         base\nplymouth-core-libs.x86_64          0.8.9-0.13.20140113.el7.centos         base\nplymouth-scripts.x86_64            0.8.9-0.13.20140113.el7.centos         base\npolicycoreutils.x86_64             2.2.5-15.el7                           base\nprocps-ng.x86_64                   3.3.10-3.el7                           base\npygobject3-base.x86_64             3.8.2-6.el7                            base\npython-backports.x86_64            1.0-8.el7                              base\npython-urlgrabber.noarch           3.10-6.el7                             base\nrpm.x86_64                         4.11.1-25.el7                          base\nrpm-build-libs.x86_64              4.11.1-25.el7                          base\nrpm-libs.x86_64                    4.11.1-25.el7                          base\nrpm-python.x86_64                  4.11.1-25.el7                          base\nrsyslog.x86_64                     7.4.7-7.el7_0                          base\nrubygem-bigdecimal.x86_64          1.2.0-24.el7                           base\nrubygem-io-console.x86_64          0.4.2-24.el7                           base\nrubygem-json.x86_64                1.7.7-24.el7                           base\nrubygem-psych.x86_64               2.0.0-24.el7                           base\nrubygems.noarch                    2.0.14-24.el7                          base\nselinux-policy.noarch              3.13.1-23.el7_1.7                      updates\nselinux-policy-targeted.noarch     3.13.1-23.el7_1.7                      updates\nsetup.noarch                       2.8.71-5.el7                           base\nshadow-utils.x86_64                2:4.1.5.1-18.el7                       base\nsudo.x86_64                        1.8.6p7-13.el7                         base\nsystemd.x86_64                     208-20.el7_1.3                         updates\nsystemd-libs.x86_64                208-20.el7_1.3                         updates\nsystemd-sysv.x86_64                208-20.el7_1.3                         updates\nteamd.x86_64                       1.15-1.el7                             base\ntuned.noarch                       2.4.1-1.el7                            base\ntzdata.noarch                      2015d-1.el7                            updates\nutil-linux.x86_64                  2.23.2-22.el7_1                        updates\nwpa_supplicant.x86_64              1:2.0-13.el7_0                         base\nxfsprogs.x86_64                    3.2.1-6.el7                            base\nxz.x86_64                          5.1.2-9alpha.el7                       base\nxz-devel.x86_64                    5.1.2-9alpha.el7                       base\nxz-libs.x86_64                     5.1.2-9alpha.el7                       base\nyum.noarch                         3.4.3-125.el7.centos                   base\nyum-plugin-fastestmirror.noarch    1.1.31-29.el7                          base\nUpdate notice RHBA-2014:0722 (from rhel-7-server-rpms) is broken, or a bad duplicate, skipping.\nYou should report this problem to the owner of the rhel-7-server-rpms repository.\nUpdate notice RHSA-2014:1971 (from rhel-7-server-rpms) is broken, or a bad duplicate, skipping.\nUpdate notice RHSA-2015:1981 (from rhel-7-server-rpms) is broken, or a bad duplicate, skipping.\nUpdate notice RHSA-2015:0067 (from rhel-7-server-rpms) is broken, or a bad duplicate, skipping.\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-multiline.txt",
    "content": "Loaded plugins: fastestmirror, security\nLoading mirror speeds from cached hostfile\n * base: mirrors.usinternet.com\n * extras: mirror.itc.virginia.edu\n * updates: mirrors.usinternet.com\n\nabrt.x86_64                            2.0.8-21.el6.centos       base           \nabrt-addon-ccpp.x86_64                 2.0.8-21.el6.centos       base           \nabrt-addon-kerneloops.x86_64           2.0.8-21.el6.centos       base           \nabrt-addon-python.x86_64               2.0.8-21.el6.centos       base           \nabrt-cli.x86_64                        2.0.8-21.el6.centos       base           \nabrt-libs.x86_64                       2.0.8-21.el6.centos       base           \nabrt-tui.x86_64                        2.0.8-21.el6.centos       base           \natk.x86_64                             1.30.0-1.el6              base           \naudit.x86_64                           2.2-4.el6_5               updates        \naudit-libs.x86_64                      2.2-4.el6_5               updates        \naugeas-libs.x86_64                     1.0.0-5.el6_5.1           updates        \nbash.x86_64                            4.1.2-15.el6_4            base           \nbfa-firmware.noarch                    3.2.21.1-2.el6            base           \nbind-libs.x86_64                       32:9.8.2-0.23.rc1.el6_5.1 updates        \nbind-utils.x86_64                      32:9.8.2-0.23.rc1.el6_5.1 updates        \nbiosdevname.x86_64                     0.5.0-2.el6               base           \nbtparser.x86_64                        0.17-2.el6                base           \nbusybox.x86_64                         1:1.15.1-20.el6           base           \ncentos-release.x86_64                  6-5.el6.centos.11.2       updates        \nchkconfig.x86_64                       1.3.49.3-2.el6_4.1        base           \ncoreutils.x86_64                       8.4-31.el6_5.1            updates        \ncoreutils-libs.x86_64                  8.4-31.el6_5.1            updates        \ncpp.x86_64                             4.4.7-4.el6               base           \ncpuspeed.x86_64                        1:1.5-20.el6_4            base           \ncronie.x86_64                          1.4.4-12.el6              base           \ncronie-anacron.x86_64                  1.4.4-12.el6              base           \ncups-libs.x86_64                       1:1.4.2-50.el6_4.5        base           \ncurl.x86_64                            7.19.7-37.el6_5.3         updates        \ndb4.x86_64                             4.7.25-18.el6_4           base           \ndb4-utils.x86_64                       4.7.25-18.el6_4           base           \ndbus-glib.x86_64                       0.86-6.el6                base           \ndevice-mapper.x86_64                   1.02.79-8.el6             base           \ndevice-mapper-event.x86_64             1.02.79-8.el6             base           \ndevice-mapper-event-libs.x86_64        1.02.79-8.el6             base           \ndevice-mapper-libs.x86_64              1.02.79-8.el6             base           \ndevice-mapper-persistent-data.x86_64   0.2.8-4.el6_5             updates        \ndhclient.x86_64                        12:4.1.1-38.P1.el6.centos base           \ndhcp-common.x86_64                     12:4.1.1-38.P1.el6.centos base           \ndmidecode.x86_64                       1:2.12-5.el6_5            updates        \ndracut.noarch                          004-336.el6_5.2           updates        \ndracut-kernel.noarch                   004-336.el6_5.2           updates        \ne2fsprogs.x86_64                       1.41.12-18.el6            base           \ne2fsprogs-libs.x86_64                  1.41.12-18.el6            base           \nefibootmgr.x86_64                      0.5.4-11.el6              base           \nethtool.x86_64                         2:3.5-1.4.el6_5           updates        \nfacter.x86_64                          1:2.0.2-1.el6             puppetlabs-products\ngcc.x86_64                             4.4.7-4.el6               base           \ngcc-c++.x86_64                         4.4.7-4.el6               base           \nglib2.x86_64                           2.26.1-7.el6_5            updates        \nglibc.x86_64                           2.12-1.132.el6_5.2        updates        \nglibc-common.x86_64                    2.12-1.132.el6_5.2        updates        \nglibc-devel.x86_64                     2.12-1.132.el6_5.2        updates        \nglibc-headers.x86_64                   2.12-1.132.el6_5.2        updates        \ngnupg2.x86_64                          2.0.14-6.el6_4            base           \ngnutls.x86_64                          2.8.5-14.el6_5            updates        \ngrep.x86_64                            2.6.3-4.el6_5.1           updates        \ngrub.x86_64                            1:0.97-83.el6             base           \ngrubby.x86_64                          7.0.15-5.el6              base           \ngzip.x86_64                            1.3.12-19.el6_4           base           \nhdparm.x86_64                          9.43-4.el6                base           \nhiera.noarch                           1.3.4-1.el6               puppetlabs-products\nhwdata.noarch                          0.233-9.1.el6             base           \ninitscripts.x86_64                     9.03.40-2.el6.centos.1    updates        \niproute.x86_64                         2.6.32-32.el6_5           updates        \niptables.x86_64                        1.4.7-11.el6              base           \niptables-ipv6.x86_64                   1.4.7-11.el6              base           \niputils.x86_64                         20071127-17.el6_4.2       base           \nirqbalance.x86_64                      2:1.0.4-9.el6_5           updates        \niw.x86_64                              3.10-1.1.el6              base           \nkernel.x86_64                          2.6.32-431.17.1.el6       updates        \nkernel-devel.x86_64                    2.6.32-431.17.1.el6       updates        \nkernel-firmware.noarch                 2.6.32-431.17.1.el6       updates        \nkernel-headers.x86_64                  2.6.32-431.17.1.el6       updates        \nkexec-tools.x86_64                     2.0.0-273.el6             base           \nkpartx.x86_64                          0.4.9-72.el6_5.2          updates        \nkrb5-devel.x86_64                      1.10.3-15.el6_5.1         updates        \nkrb5-libs.x86_64                       1.10.3-15.el6_5.1         updates        \nledmon.x86_64                          0.78-1.el6                base           \nlibblkid.x86_64                        2.17.2-12.14.el6_5        updates        \nlibcom_err.x86_64                      1.41.12-18.el6            base           \nlibcom_err-devel.x86_64                1.41.12-18.el6            base           \nlibcurl.x86_64                         7.19.7-37.el6_5.3         updates        \nlibdrm.x86_64                          2.4.45-2.el6              base           \nlibgcc.x86_64                          4.4.7-4.el6               base           \nlibgcrypt.x86_64                       1.4.5-11.el6_4            base           \nlibgomp.x86_64                         4.4.7-4.el6               base           \nlibjpeg-turbo.x86_64                   1.2.1-3.el6_5             updates        \nlibnl.x86_64                           1.1.4-2.el6               base           \nlibpcap.x86_64                         14:1.4.0-1.20130826git2dbcaa1.el6\n                                                                 base           \nlibproxy.x86_64                        0.3.0-4.el6_3             base           \nlibproxy-bin.x86_64                    0.3.0-4.el6_3             base           \nlibproxy-python.x86_64                 0.3.0-4.el6_3             base           \nlibreport.x86_64                       2.0.9-19.el6.centos       base           \nlibreport-cli.x86_64                   2.0.9-19.el6.centos       base           \nlibreport-compat.x86_64                2.0.9-19.el6.centos       base           \nlibreport-plugin-kerneloops.x86_64     2.0.9-19.el6.centos       base           \nlibreport-plugin-logger.x86_64         2.0.9-19.el6.centos       base           \nlibreport-plugin-mailx.x86_64          2.0.9-19.el6.centos       base           \nlibreport-plugin-reportuploader.x86_64 2.0.9-19.el6.centos       base           \nlibreport-plugin-rhtsupport.x86_64     2.0.9-19.el6.centos       base           \nlibreport-python.x86_64                2.0.9-19.el6.centos       base           \nlibselinux.x86_64                      2.0.94-5.3.el6_4.1        base           \nlibselinux-devel.x86_64                2.0.94-5.3.el6_4.1        base           \nlibselinux-ruby.x86_64                 2.0.94-5.3.el6_4.1        base           \nlibselinux-utils.x86_64                2.0.94-5.3.el6_4.1        base           \nlibss.x86_64                           1.41.12-18.el6            base           \nlibstdc++.x86_64                       4.4.7-4.el6               base           \nlibstdc++-devel.x86_64                 4.4.7-4.el6               base           \nlibtar.x86_64                          1.2.11-17.el6_4.1         base           \nlibtasn1.x86_64                        2.3-6.el6_5               updates        \nlibtiff.x86_64                         3.9.4-10.el6_5            updates        \nlibudev.x86_64                         147-2.51.el6              base           \nlibuuid.x86_64                         2.17.2-12.14.el6_5        updates        \nlibxml2.x86_64                         2.7.6-14.el6_5.1          updates        \nlibxml2-python.x86_64                  2.7.6-14.el6_5.1          updates        \nlogrotate.x86_64                       3.7.8-17.el6              base           \nlvm2.x86_64                            2.02.100-8.el6            base           \nlvm2-libs.x86_64                       2.02.100-8.el6            base           \nmailx.x86_64                           12.4-7.el6                base           \nman-pages-overrides.noarch             6.5.3-1.el6_5             updates        \nmdadm.x86_64                           3.2.6-7.el6_5.2           updates        \nmicrocode_ctl.x86_64                   1:1.17-17.el6             base           \nmodule-init-tools.x86_64               3.9-21.el6_4              base           \nmysql-libs.x86_64                      5.1.73-3.el6_5            updates        \nntp.x86_64                             4.2.6p5-1.el6.centos      base           \nntpdate.x86_64                         4.2.6p5-1.el6.centos      base           \nntsysv.x86_64                          1.3.49.3-2.el6_4.1        base           \nnumactl.x86_64                         2.0.7-8.el6               base           \nopenldap.x86_64                        2.4.23-34.el6_5.1         updates        \nopenssh.x86_64                         5.3p1-94.el6              base           \nopenssh-clients.x86_64                 5.3p1-94.el6              base           \nopenssh-server.x86_64                  5.3p1-94.el6              base           \nopenssl.x86_64                         1.0.1e-16.el6_5.14        updates        \nopenssl-devel.x86_64                   1.0.1e-16.el6_5.14        updates        \npam.x86_64                             1.1.1-17.el6              base           \nparted.x86_64                          2.1-21.el6                base           \nperl.x86_64                            4:5.10.1-136.el6          base           \nperl-Module-Pluggable.x86_64           1:3.90-136.el6            base           \nperl-Pod-Escapes.x86_64                1:1.04-136.el6            base           \nperl-Pod-Simple.x86_64                 1:3.13-136.el6            base           \nperl-libs.x86_64                       4:5.10.1-136.el6          base           \nperl-version.x86_64                    3:0.77-136.el6            base           \npixman.x86_64                          0.26.2-5.1.el6_5          updates        \npm-utils.x86_64                        1.2.5-10.el6_5.1          updates        \npolicycoreutils.x86_64                 2.0.83-19.39.el6          base           \npolkit.x86_64                          0.96-5.el6_4              base           \npostfix.x86_64                         2:2.6.6-6.el6_5           updates        \nprelink.x86_64                         0.4.6-3.1.el6_4           base           \npsmisc.x86_64                          22.6-19.el6_5             updates        \npython.x86_64                          2.6.6-52.el6              updates        \npython-ethtool.x86_64                  0.6-5.el6                 base           \npython-libs.x86_64                     2.6.6-52.el6              updates        \npython-urlgrabber.noarch               3.9.1-9.el6               base           \nql2400-firmware.noarch                 7.00.01-1.el6             base           \nql2500-firmware.noarch                 7.00.01-1.el6             base           \nquota.x86_64                           1:3.17-21.el6_5           updates        \nreadahead.x86_64                       1:1.5.6-2.el6             base           \nrpm.x86_64                             4.8.0-37.el6              base           \nrpm-libs.x86_64                        4.8.0-37.el6              base           \nrpm-python.x86_64                      4.8.0-37.el6              base           \nrsync.x86_64                           3.0.6-9.el6_4.1           base           \nrsyslog.x86_64                         5.8.10-8.el6              base           \nruby.x86_64                            1.8.7.352-13.el6          updates        \nruby-augeas.x86_64                     0.4.1-3.el6               puppetlabs-deps\nruby-devel.x86_64                      1.8.7.352-13.el6          updates        \nruby-irb.x86_64                        1.8.7.352-13.el6          updates        \nruby-libs.x86_64                       1.8.7.352-13.el6          updates        \nruby-rdoc.x86_64                       1.8.7.352-13.el6          updates        \nruby-shadow.x86_64                     1:2.2.0-2.el6             puppetlabs-deps\nrubygem-json.x86_64                    1.5.5-1.el6               puppetlabs-deps\nrubygems.noarch                        1.3.7-5.el6               base           \nscl-utils.x86_64                       20120927-8.el6            base           \nselinux-policy.noarch                  3.7.19-231.el6_5.3        updates        \nselinux-policy-targeted.noarch         3.7.19-231.el6_5.3        updates        \nsetup.noarch                           2.8.14-20.el6_4.1         base           \nsetuptool.x86_64                       1.19.9-4.el6              base           \nsg3_utils-libs.x86_64                  1.28-5.el6                base           \nsos.noarch                             2.2-47.el6.centos.1       updates        \nsudo.x86_64                            1.8.6p3-12.el6            base           \nsysstat.x86_64                         9.0.4-22.el6              base           \nsystemtap-runtime.x86_64               2.3-4.el6_5               updates        \nsysvinit-tools.x86_64                  2.87-5.dsf.el6            base           \ntzdata.noarch                          2014d-1.el6               updates        \nudev.x86_64                            147-2.51.el6              base           \nupstart.x86_64                         0.6.5-13.el6_5.3          updates        \nutil-linux-ng.x86_64                   2.17.2-12.14.el6_5        updates        \nwget.x86_64                            1.12-1.11.el6_5           updates        \nxmlrpc-c.x86_64                        1.16.24-1210.1840.el6     base           \nxmlrpc-c-client.x86_64                 1.16.24-1210.1840.el6     base           \nxorg-x11-drv-ati-firmware.noarch       7.1.0-3.el6               base           \nyum.noarch                             3.2.29-43.el6.centos      updates        \nyum-plugin-fastestmirror.noarch        1.1.30-17.el6_5           updates        \nyum-plugin-security.noarch             1.1.30-17.el6_5           updates        \nyum-utils.noarch                       1.1.30-17.el6_5           updates        \n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-obsoletes.txt",
    "content": "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n\nNetworkManager.x86_64              1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-glib.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-tui.x86_64          1:1.0.0-14.git20150121.b4ea599c.el7    base\nalsa-firmware.noarch               1.0.28-2.el7                           base\nalsa-lib.x86_64                    1.0.28-2.el7                           base\naudit.x86_64                       2.4.1-5.el7                            base\naudit-libs.x86_64                  2.4.1-5.el7                            base\nauthconfig.x86_64                  6.2.8-9.el7                            base\navahi.x86_64                       0.6.31-14.el7                          base\navahi-autoipd.x86_64               0.6.31-14.el7                          base\navahi-libs.x86_64                  0.6.31-14.el7                          base\nbash.x86_64                        4.2.46-12.el7                          base\nbind-libs-lite.x86_64              32:9.9.4-18.el7_1.1                    updates\nbind-license.noarch                32:9.9.4-18.el7_1.1                    updates\nbinutils.x86_64                    2.23.52.0.1-30.el7_1.2                 updates\nbiosdevname.x86_64                 0.6.1-2.el7                            base\nbtrfs-progs.x86_64                 3.16.2-1.el7                           base\nca-certificates.noarch             2015.2.4-70.0.el7_1                    updates\ncentos-logos.noarch                70.0.6-2.el7.centos                    updates\ncentos-release.x86_64              7-1.1503.el7.centos.2.8                base\ncpp.x86_64                         4.8.3-9.el7                            base\ncronie.x86_64                      1.4.11-13.el7                          base\ncronie-anacron.x86_64              1.4.11-13.el7                          base\ncryptsetup-libs.x86_64             1.6.6-3.el7                            base\ndbus.x86_64                        1:1.6.12-11.el7                        base\ndbus-libs.x86_64                   1:1.6.12-11.el7                        base\ndevice-mapper.x86_64               7:1.02.93-3.el7                        base\ndevice-mapper-event.x86_64         7:1.02.93-3.el7                        base\ndevice-mapper-event-libs.x86_64    7:1.02.93-3.el7                        base\ndevice-mapper-libs.x86_64          7:1.02.93-3.el7                        base\ndevice-mapper-persistent-data.x86_64\n                                   0.4.1-2.el7                            base\ndhclient.x86_64                    12:4.2.5-36.el7.centos                 base\ndhcp-common.x86_64                 12:4.2.5-36.el7.centos                 base\ndhcp-libs.x86_64                   12:4.2.5-36.el7.centos                 base\ndnsmasq.x86_64                     2.66-13.el7_1                          updates\ndracut.x86_64                      033-241.el7_1.1                        updates\ndracut-config-rescue.x86_64        033-241.el7_1.1                        updates\ndracut-network.x86_64              033-241.el7_1.1                        updates\ne2fsprogs.x86_64                   1.42.9-7.el7                           base\ne2fsprogs-libs.x86_64              1.42.9-7.el7                           base\nelfutils-libelf.x86_64             0.160-1.el7                            base\nelfutils-libs.x86_64               0.160-1.el7                            base\nethtool.x86_64                     2:3.15-2.el7                           base\nfirewalld.noarch                   0.3.9-11.el7                           base\nfreetype.x86_64                    2.4.11-10.el7_1.1                      updates\ngcc.x86_64                         4.8.3-9.el7                            base\nglib-networking.x86_64             2.40.0-1.el7                           base\nglib2.x86_64                       2.40.0-4.el7                           base\nglibc.x86_64                       2.17-78.el7                            base\nglibc-common.x86_64                2.17-78.el7                            base\nglibc-devel.x86_64                 2.17-78.el7                            base\nglibc-headers.x86_64               2.17-78.el7                            base\ngmp.x86_64                         1:6.0.0-11.el7                         base\ngnutls.x86_64                      3.3.8-12.el7                           base\ngrep.x86_64                        2.20-1.el7                             base\ngrub2.x86_64                       1:2.02-0.16.el7.centos                 base\ngrub2-tools.x86_64                 1:2.02-0.16.el7.centos                 base\ngrubby.x86_64                      8.28-11.el7                            base\nhwdata.noarch                      0.252-7.5.el7                          base\nhwdata.x86_64                      0.252-7.8.el7_1                        updates\ninitscripts.x86_64                 9.49.24-1.el7                          base\niproute.x86_64                     3.10.0-21.el7                          base\niprutils.x86_64                    2.4.3-3.el7                            base\nirqbalance.x86_64                  2:1.0.7-1.el7                          base\niwl100-firmware.noarch             39.31.5.1-36.el7                       base\niwl1000-firmware.noarch            1:39.31.5.1-36.el7                     base\niwl105-firmware.noarch             18.168.6.1-36.el7                      base\niwl135-firmware.noarch             18.168.6.1-36.el7                      base\niwl2000-firmware.noarch            18.168.6.1-36.el7                      base\niwl2030-firmware.noarch            18.168.6.1-36.el7                      base\niwl3160-firmware.noarch            22.0.7.0-36.el7                        base\niwl3945-firmware.noarch            15.32.2.9-36.el7                       base\niwl4965-firmware.noarch            228.61.2.24-36.el7                     base\niwl5000-firmware.noarch            8.83.5.1_1-36.el7                      base\niwl5150-firmware.noarch            8.24.2.2-36.el7                        base\niwl6000-firmware.noarch            9.221.4.1-36.el7                       base\niwl6000g2a-firmware.noarch         17.168.5.3-36.el7                      base\niwl6000g2b-firmware.noarch         17.168.5.2-36.el7                      base\niwl6050-firmware.noarch            41.28.5.1-36.el7                       base\niwl7260-firmware.noarch            22.0.7.0-36.el7                        base\nkbd.x86_64                         1.15.5-11.el7                          base\nkbd-misc.noarch                    1.15.5-11.el7                          base\nkernel.x86_64                      3.10.0-229.4.2.el7                     updates\nkernel-headers.x86_64              3.10.0-229.4.2.el7                     updates\nkernel-tools.x86_64                3.10.0-229.4.2.el7                     updates\nkernel-tools-libs.x86_64           3.10.0-229.4.2.el7                     updates\nkexec-tools.x86_64                 2.0.7-19.el7_1.2                       updates\nkmod.x86_64                        14-10.el7                              base\nkmod-libs.x86_64                   14-10.el7                              base\nkpartx.x86_64                      0.4.9-77.el7                           base\nkrb5-libs.x86_64                   1.12.2-14.el7                          base\nlibblkid.x86_64                    2.23.2-22.el7_1                        updates\nlibcom_err.x86_64                  1.42.9-7.el7                           base\nlibdb.x86_64                       5.3.21-17.el7_0.1                      base\nlibdb-utils.x86_64                 5.3.21-17.el7_0.1                      base\nlibdrm.x86_64                      2.4.56-2.el7                           base\nlibgcc.x86_64                      4.8.3-9.el7                            base\nlibgcrypt.x86_64                   1.5.3-12.el7_1.1                       updates\nlibgcrypt-devel.x86_64             1.5.3-12.el7_1.1                       updates\nlibgomp.x86_64                     4.8.3-9.el7                            base\nlibgudev1.x86_64                   208-20.el7_1.3                         updates\nlibmount.x86_64                    2.23.2-22.el7_1                        updates\nlibnl3.x86_64                      3.2.21-8.el7                           base\nlibnl3-cli.x86_64                  3.2.21-8.el7                           base\nlibpcap.x86_64                     14:1.5.3-4.el7_1.2                     updates\nlibsoup.x86_64                     2.46.0-3.el7                           base\nlibss.x86_64                       1.42.9-7.el7                           base\nlibstdc++.x86_64                   4.8.3-9.el7                            base\nlibtasn1.x86_64                    3.8-2.el7                              base\nlibteam.x86_64                     1.15-1.el7                             base\nlibuuid.x86_64                     2.23.2-22.el7_1                        updates\nlibxml2.x86_64                     2.9.1-5.el7_1.2                        updates\nlibxml2-devel.x86_64               2.9.1-5.el7_1.2                        updates\nlibyaml.x86_64                     0.1.4-11.el7_0                         base\nlinux-firmware.noarch              20140911-0.1.git365e80c.el7            base\nlvm2.x86_64                        7:2.02.115-3.el7                       base\nlvm2-libs.x86_64                   7:2.02.115-3.el7                       base\nmariadb-libs.x86_64                1:5.5.41-2.el7_0                       base\nmicrocode_ctl.x86_64               2:2.1-10.el7                           base\nnettle.x86_64                      2.7.1-4.el7                            base\nnspr.x86_64                        4.10.8-1.el7_1                         updates\nnss.x86_64                         3.18.0-2.2.el7_1                       updates\nnss-softokn.x86_64                 3.16.2.3-9.el7                         base\nnss-softokn-freebl.x86_64          3.16.2.3-9.el7                         base\nnss-sysinit.x86_64                 3.18.0-2.2.el7_1                       updates\nnss-tools.x86_64                   3.18.0-2.2.el7_1                       updates\nnss-util.x86_64                    3.18.0-1.el7_1                         updates\nntpdate.x86_64                     4.2.6p5-19.el7.centos                  base\nnumactl-libs.x86_64                2.0.9-4.el7                            base\nopen-vm-tools.x86_64               9.4.0-6.el7                            base\nopenldap.x86_64                    2.4.39-6.el7                           base\nopenssh.x86_64                     6.6.1p1-12.el7_1                       updates\nopenssh-clients.x86_64             6.6.1p1-12.el7_1                       updates\nopenssh-server.x86_64              6.6.1p1-12.el7_1                       updates\nopenssl.x86_64                     1:1.0.1e-42.el7.4                      updates\nopenssl-libs.x86_64                1:1.0.1e-42.el7.4                      updates\np11-kit.x86_64                     0.20.7-3.el7                           base\np11-kit-trust.x86_64               0.20.7-3.el7                           base\npam.x86_64                         1.1.8-12.el7                           base\nparted.x86_64                      3.1-20.el7                             base\npcre.x86_64                        8.32-14.el7                            base\nplymouth.x86_64                    0.8.9-0.13.20140113.el7.centos         base\nplymouth-core-libs.x86_64          0.8.9-0.13.20140113.el7.centos         base\nplymouth-scripts.x86_64            0.8.9-0.13.20140113.el7.centos         base\npolicycoreutils.x86_64             2.2.5-15.el7                           base\nprocps-ng.x86_64                   3.3.10-3.el7                           base\npygobject3-base.x86_64             3.8.2-6.el7                            base\npython-backports.x86_64            1.0-8.el7                              base\npython-urlgrabber.noarch           3.10-6.el7                             base\nrpm.x86_64                         4.11.1-25.el7                          base\nrpm-build-libs.x86_64              4.11.1-25.el7                          base\nrpm-libs.x86_64                    4.11.1-25.el7                          base\nrpm-python.x86_64                  4.11.1-25.el7                          base\nrsyslog.x86_64                     7.4.7-7.el7_0                          base\nrubygem-bigdecimal.x86_64          1.2.0-24.el7                           base\nrubygem-io-console.x86_64          0.4.2-24.el7                           base\nrubygem-json.x86_64                1.7.7-24.el7                           base\nrubygem-psych.x86_64               2.0.0-24.el7                           base\nrubygems.noarch                    2.0.14-24.el7                          base\nselinux-policy.noarch              3.13.1-23.el7_1.7                      updates\nselinux-policy-targeted.noarch     3.13.1-23.el7_1.7                      updates\nsetup.noarch                       2.8.71-5.el7                           base\nshadow-utils.x86_64                2:4.1.5.1-18.el7                       base\nsudo.x86_64                        1.8.6p7-13.el7                         base\nsystemd.x86_64                     208-20.el7_1.3                         updates\nsystemd-libs.x86_64                208-20.el7_1.3                         updates\nsystemd-sysv.x86_64                208-20.el7_1.3                         updates\nteamd.x86_64                       1.15-1.el7                             base\ntuned.noarch                       2.4.1-1.el7                            base\ntzdata.noarch                      2015d-1.el7                            updates\nutil-linux.x86_64                  2.23.2-22.el7_1                        updates\nwpa_supplicant.x86_64              1:2.0-13.el7_0                         base\nxfsprogs.x86_64                    3.2.1-6.el7                            base\nxz.x86_64                          5.1.2-9alpha.el7                       base\nxz-devel.x86_64                    5.1.2-9alpha.el7                       base\nxz-libs.x86_64                     5.1.2-9alpha.el7                       base\nyum.noarch                         3.4.3-125.el7.centos                   base\nyum-plugin-fastestmirror.noarch    1.1.31-29.el7                          base\nObsoleting Packages\nNetworkManager.x86_64              1:1.0.0-14.git20150121.b4ea599c.el7    base\n    NetworkManager.x86_64          1:0.9.9.1-25.git20140326.4dba720.el7_0 @updates\nNetworkManager-adsl.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\n    NetworkManager.x86_64          1:0.9.9.1-25.git20140326.4dba720.el7_0 @updates\nNetworkManager-bluetooth.x86_64    1:1.0.0-14.git20150121.b4ea599c.el7    base\n    NetworkManager.x86_64          1:0.9.9.1-25.git20140326.4dba720.el7_0 @updates\nNetworkManager-team.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\n    NetworkManager.x86_64          1:0.9.9.1-25.git20140326.4dba720.el7_0 @updates\nNetworkManager-wifi.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\n    NetworkManager.x86_64          1:0.9.9.1-25.git20140326.4dba720.el7_0 @updates\nNetworkManager-wwan.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\n    NetworkManager.x86_64          1:0.9.9.1-25.git20140326.4dba720.el7_0 @updates\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-plugin-output.txt",
    "content": "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n\nNetworkManager.x86_64              1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-glib.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-tui.x86_64          1:1.0.0-14.git20150121.b4ea599c.el7    base\nalsa-firmware.noarch               1.0.28-2.el7                           base\nalsa-lib.x86_64                    1.0.28-2.el7                           base\naudit.x86_64                       2.4.1-5.el7                            base\naudit-libs.x86_64                  2.4.1-5.el7                            base\nauthconfig.x86_64                  6.2.8-9.el7                            base\navahi.x86_64                       0.6.31-14.el7                          base\navahi-autoipd.x86_64               0.6.31-14.el7                          base\navahi-libs.x86_64                  0.6.31-14.el7                          base\nbash.x86_64                        4.2.46-12.el7                          base\nbind-libs-lite.x86_64              32:9.9.4-18.el7_1.1                    updates\nbind-license.noarch                32:9.9.4-18.el7_1.1                    updates\nbinutils.x86_64                    2.23.52.0.1-30.el7_1.2                 updates\nbiosdevname.x86_64                 0.6.1-2.el7                            base\nbtrfs-progs.x86_64                 3.16.2-1.el7                           base\nca-certificates.noarch             2015.2.4-70.0.el7_1                    updates\ncentos-logos.noarch                70.0.6-2.el7.centos                    updates\ncentos-release.x86_64              7-1.1503.el7.centos.2.8                base\ncpp.x86_64                         4.8.3-9.el7                            base\ncronie.x86_64                      1.4.11-13.el7                          base\ncronie-anacron.x86_64              1.4.11-13.el7                          base\ncryptsetup-libs.x86_64             1.6.6-3.el7                            base\ndbus.x86_64                        1:1.6.12-11.el7                        base\ndbus-libs.x86_64                   1:1.6.12-11.el7                        base\ndevice-mapper.x86_64               7:1.02.93-3.el7                        base\ndevice-mapper-event.x86_64         7:1.02.93-3.el7                        base\ndevice-mapper-event-libs.x86_64    7:1.02.93-3.el7                        base\ndevice-mapper-libs.x86_64          7:1.02.93-3.el7                        base\nRandom plugin output\nLoaded plugins: fastestmirror, product-id, fake-plugin\nRandom plugin failed, is the mirror available?\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-security.txt",
    "content": "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n\nNetworkManager.x86_64              1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-glib.x86_64         1:1.0.0-14.git20150121.b4ea599c.el7    base\nNetworkManager-tui.x86_64          1:1.0.0-14.git20150121.b4ea599c.el7    base\nalsa-firmware.noarch               1.0.28-2.el7                           base\nalsa-lib.x86_64                    1.0.28-2.el7                           base\naudit.x86_64                       2.4.1-5.el7                            base\naudit-libs.x86_64                  2.4.1-5.el7                            base\nauthconfig.x86_64                  6.2.8-9.el7                            base\navahi.x86_64                       0.6.31-14.el7                          base\navahi-autoipd.x86_64               0.6.31-14.el7                          base\navahi-libs.x86_64                  0.6.31-14.el7                          base\nbash.x86_64                        4.2.46-12.el7                          base\nbind-libs-lite.x86_64              32:9.9.4-18.el7_1.1                    updates\nbind-license.noarch                32:9.9.4-18.el7_1.1                    updates\nbinutils.x86_64                    2.23.52.0.1-30.el7_1.2                 updates\nbiosdevname.x86_64                 0.6.1-2.el7                            base\nbtrfs-progs.x86_64                 3.16.2-1.el7                           base\nca-certificates.noarch             2015.2.4-70.0.el7_1                    updates\ncentos-logos.noarch                70.0.6-2.el7.centos                    updates\ncentos-release.x86_64              7-1.1503.el7.centos.2.8                base\ncpp.x86_64                         4.8.3-9.el7                            base\ncronie.x86_64                      1.4.11-13.el7                          base\ncronie-anacron.x86_64              1.4.11-13.el7                          base\ncryptsetup-libs.x86_64             1.6.6-3.el7                            base\ndbus.x86_64                        1:1.6.12-11.el7                        base\ndbus-libs.x86_64                   1:1.6.12-11.el7                        base\ndevice-mapper.x86_64               7:1.02.93-3.el7                        base\ndevice-mapper-event.x86_64         7:1.02.93-3.el7                        base\ndevice-mapper-event-libs.x86_64    7:1.02.93-3.el7                        base\ndevice-mapper-libs.x86_64          7:1.02.93-3.el7                        base\ndevice-mapper-persistent-data.x86_64\n                                   0.4.1-2.el7                            base\ndhclient.x86_64                    12:4.2.5-36.el7.centos                 base\ndhcp-common.x86_64                 12:4.2.5-36.el7.centos                 base\ndhcp-libs.x86_64                   12:4.2.5-36.el7.centos                 base\ndnsmasq.x86_64                     2.66-13.el7_1                          updates\ndracut.x86_64                      033-241.el7_1.1                        updates\ndracut-config-rescue.x86_64        033-241.el7_1.1                        updates\ndracut-network.x86_64              033-241.el7_1.1                        updates\ne2fsprogs.x86_64                   1.42.9-7.el7                           base\ne2fsprogs-libs.x86_64              1.42.9-7.el7                           base\nelfutils-libelf.x86_64             0.160-1.el7                            base\nelfutils-libs.x86_64               0.160-1.el7                            base\nethtool.x86_64                     2:3.15-2.el7                           base\nfirewalld.noarch                   0.3.9-11.el7                           base\nfreetype.x86_64                    2.4.11-10.el7_1.1                      updates\ngcc.x86_64                         4.8.3-9.el7                            base\nglib-networking.x86_64             2.40.0-1.el7                           base\nglib2.x86_64                       2.40.0-4.el7                           base\nglibc.x86_64                       2.17-78.el7                            base\nglibc-common.x86_64                2.17-78.el7                            base\nglibc-devel.x86_64                 2.17-78.el7                            base\nglibc-headers.x86_64               2.17-78.el7                            base\ngmp.x86_64                         1:6.0.0-11.el7                         base\ngnutls.x86_64                      3.3.8-12.el7                           base\ngrep.x86_64                        2.20-1.el7                             base\ngrub2.x86_64                       1:2.02-0.16.el7.centos                 base\ngrub2-tools.x86_64                 1:2.02-0.16.el7.centos                 base\ngrubby.x86_64                      8.28-11.el7                            base\nhwdata.noarch                      0.252-7.5.el7                          base\nhwdata.x86_64                      0.252-7.8.el7_1                        updates\ninitscripts.x86_64                 9.49.24-1.el7                          base\niproute.x86_64                     3.10.0-21.el7                          base\niprutils.x86_64                    2.4.3-3.el7                            base\nirqbalance.x86_64                  2:1.0.7-1.el7                          base\niwl100-firmware.noarch             39.31.5.1-36.el7                       base\niwl1000-firmware.noarch            1:39.31.5.1-36.el7                     base\niwl105-firmware.noarch             18.168.6.1-36.el7                      base\niwl135-firmware.noarch             18.168.6.1-36.el7                      base\niwl2000-firmware.noarch            18.168.6.1-36.el7                      base\niwl2030-firmware.noarch            18.168.6.1-36.el7                      base\niwl3160-firmware.noarch            22.0.7.0-36.el7                        base\niwl3945-firmware.noarch            15.32.2.9-36.el7                       base\niwl4965-firmware.noarch            228.61.2.24-36.el7                     base\niwl5000-firmware.noarch            8.83.5.1_1-36.el7                      base\niwl5150-firmware.noarch            8.24.2.2-36.el7                        base\niwl6000-firmware.noarch            9.221.4.1-36.el7                       base\niwl6000g2a-firmware.noarch         17.168.5.3-36.el7                      base\niwl6000g2b-firmware.noarch         17.168.5.2-36.el7                      base\niwl6050-firmware.noarch            41.28.5.1-36.el7                       base\niwl7260-firmware.noarch            22.0.7.0-36.el7                        base\nkbd.x86_64                         1.15.5-11.el7                          base\nkbd-misc.noarch                    1.15.5-11.el7                          base\nkernel.x86_64                      3.10.0-229.4.2.el7                     updates\nkernel-headers.x86_64              3.10.0-229.4.2.el7                     updates\nkernel-tools.x86_64                3.10.0-229.4.2.el7                     updates\nkernel-tools-libs.x86_64           3.10.0-229.4.2.el7                     updates\nkexec-tools.x86_64                 2.0.7-19.el7_1.2                       updates\nkmod.x86_64                        14-10.el7                              base\nkmod-libs.x86_64                   14-10.el7                              base\nkpartx.x86_64                      0.4.9-77.el7                           base\nkrb5-libs.x86_64                   1.12.2-14.el7                          base\nlibblkid.x86_64                    2.23.2-22.el7_1                        updates\nlibcom_err.x86_64                  1.42.9-7.el7                           base\nlibdb.x86_64                       5.3.21-17.el7_0.1                      base\nlibdb-utils.x86_64                 5.3.21-17.el7_0.1                      base\nlibdrm.x86_64                      2.4.56-2.el7                           base\nlibgcc.x86_64                      4.8.3-9.el7                            base\nlibgcrypt.x86_64                   1.5.3-12.el7_1.1                       updates\nlibgcrypt-devel.x86_64             1.5.3-12.el7_1.1                       updates\nlibgomp.x86_64                     4.8.3-9.el7                            base\nlibgudev1.x86_64                   208-20.el7_1.3                         updates\nlibmount.x86_64                    2.23.2-22.el7_1                        updates\nlibnl3.x86_64                      3.2.21-8.el7                           base\nlibnl3-cli.x86_64                  3.2.21-8.el7                           base\nlibpcap.x86_64                     14:1.5.3-4.el7_1.2                     updates\nlibsoup.x86_64                     2.46.0-3.el7                           base\nlibss.x86_64                       1.42.9-7.el7                           base\nlibstdc++.x86_64                   4.8.3-9.el7                            base\nlibtasn1.x86_64                    3.8-2.el7                              base\nlibteam.x86_64                     1.15-1.el7                             base\nlibuuid.x86_64                     2.23.2-22.el7_1                        updates\nlibxml2.x86_64                     2.9.1-5.el7_1.2                        updates\nlibxml2-devel.x86_64               2.9.1-5.el7_1.2                        updates\nlibyaml.x86_64                     0.1.4-11.el7_0                         base\nlinux-firmware.noarch              20140911-0.1.git365e80c.el7            base\nlvm2.x86_64                        7:2.02.115-3.el7                       base\nlvm2-libs.x86_64                   7:2.02.115-3.el7                       base\nmariadb-libs.x86_64                1:5.5.41-2.el7_0                       base\nmicrocode_ctl.x86_64               2:2.1-10.el7                           base\nnettle.x86_64                      2.7.1-4.el7                            base\nnspr.x86_64                        4.10.8-1.el7_1                         updates\nnss.x86_64                         3.18.0-2.2.el7_1                       updates\nnss-softokn.x86_64                 3.16.2.3-9.el7                         base\nnss-softokn-freebl.x86_64          3.16.2.3-9.el7                         base\nnss-sysinit.x86_64                 3.18.0-2.2.el7_1                       updates\nnss-tools.x86_64                   3.18.0-2.2.el7_1                       updates\nnss-util.x86_64                    3.18.0-1.el7_1                         updates\nntpdate.x86_64                     4.2.6p5-19.el7.centos                  base\nnumactl-libs.x86_64                2.0.9-4.el7                            base\nopen-vm-tools.x86_64               9.4.0-6.el7                            base\nopenldap.x86_64                    2.4.39-6.el7                           base\nopenssh.x86_64                     6.6.1p1-12.el7_1                       updates\nopenssh-clients.x86_64             6.6.1p1-12.el7_1                       updates\nopenssh-server.x86_64              6.6.1p1-12.el7_1                       updates\nopenssl.x86_64                     1:1.0.1e-42.el7.4                      updates\nopenssl-libs.x86_64                1:1.0.1e-42.el7.4                      updates\np11-kit.x86_64                     0.20.7-3.el7                           base\np11-kit-trust.x86_64               0.20.7-3.el7                           base\npam.x86_64                         1.1.8-12.el7                           base\nparted.x86_64                      3.1-20.el7                             base\npcre.x86_64                        8.32-14.el7                            base\nplymouth.x86_64                    0.8.9-0.13.20140113.el7.centos         base\nplymouth-core-libs.x86_64          0.8.9-0.13.20140113.el7.centos         base\nplymouth-scripts.x86_64            0.8.9-0.13.20140113.el7.centos         base\npolicycoreutils.x86_64             2.2.5-15.el7                           base\nprocps-ng.x86_64                   3.3.10-3.el7                           base\npygobject3-base.x86_64             3.8.2-6.el7                            base\npython-backports.x86_64            1.0-8.el7                              base\npython-urlgrabber.noarch           3.10-6.el7                             base\nrpm.x86_64                         4.11.1-25.el7                          base\nrpm-build-libs.x86_64              4.11.1-25.el7                          base\nrpm-libs.x86_64                    4.11.1-25.el7                          base\nrpm-python.x86_64                  4.11.1-25.el7                          base\nrsyslog.x86_64                     7.4.7-7.el7_0                          base\nrubygem-bigdecimal.x86_64          1.2.0-24.el7                           base\nrubygem-io-console.x86_64          0.4.2-24.el7                           base\nrubygem-json.x86_64                1.7.7-24.el7                           base\nrubygem-psych.x86_64               2.0.0-24.el7                           base\nrubygems.noarch                    2.0.14-24.el7                          base\nselinux-policy.noarch              3.13.1-23.el7_1.7                      updates\nselinux-policy-targeted.noarch     3.13.1-23.el7_1.7                      updates\nsetup.noarch                       2.8.71-5.el7                           base\nshadow-utils.x86_64                2:4.1.5.1-18.el7                       base\nsudo.x86_64                        1.8.6p7-13.el7                         base\nsystemd.x86_64                     208-20.el7_1.3                         updates\nsystemd-libs.x86_64                208-20.el7_1.3                         updates\nsystemd-sysv.x86_64                208-20.el7_1.3                         updates\nteamd.x86_64                       1.15-1.el7                             base\ntuned.noarch                       2.4.1-1.el7                            base\ntzdata.noarch                      2015d-1.el7                            updates\nutil-linux.x86_64                  2.23.2-22.el7_1                        updates\nwpa_supplicant.x86_64              1:2.0-13.el7_0                         base\nxfsprogs.x86_64                    3.2.1-6.el7                            base\nxz.x86_64                          5.1.2-9alpha.el7                       base\nxz-devel.x86_64                    5.1.2-9alpha.el7                       base\nxz-libs.x86_64                     5.1.2-9alpha.el7                       base\nyum.noarch                         3.4.3-125.el7.centos                   base\nyum-plugin-fastestmirror.noarch    1.1.31-29.el7                          base\nSecurity: kernel-3.10.0-229.14.1.el7.x86_64 is an installed security update\nSecurity: kernel-3.10.0-229.11.1.el7.x86_64 is the currently running version\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-simple.txt",
    "content": "Loaded plugins: fastestmirror\nDetermining fastest mirrors\n * base: centos.sonn.com\n * epel: ftp.osuosl.org\n * extras: mirror.web-ster.com\n * updates: centos.sonn.com\n\ncurl.i686                               7.32.0-10.fc20           updates        \ncurl.x86_64                             7.32.0-10.fc20           updates        \ngawk.i686                               4.1.0-3.fc20             updates        \ndhclient.i686                           12:4.1.1-38.P1.fc20      updates        \njava-1.8.0-openjdk.x86_64               1:1.8.0.131-2.b11.el7_3  updates\nselinux-policy.noarch                   3.12.1-163.fc20          updates-testing\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/yum/yum-check-update-subscription-manager.txt",
    "content": "Loaded plugins: product-id, search-disabled-repos, subscription-manager\n\nThis system is not registered with an entitlement server. You can use subscription-manager to register on.\n\ncurl.i686                               7.32.0-10.fc20           updates\ncurl.x86_64                             7.32.0-10.fc20           updates\ngawk.i686                               4.1.0-3.fc20             updates\ndhclient.i686                           12:4.1.1-38.P1.fc20      updates\njava-1.8.0-openjdk.x86_64               1:1.8.0.131-2.b11.el7_3  updates\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/zypper/zypper-list-updates-SLES11sp1.out",
    "content": "Loading repository data...\nReading installed packages...\nS | Repository         | Name                              | Current Version       | Available Version         | Arch  \n--+--------------------+-----------------------------------+-----------------------+---------------------------+-------\nv | SLES11-SP1-Updates | ConsoleKit                        | 0.2.10-64.13.6        | 0.2.10-64.65.1            | x86_64\nv | SLES11-SP1-Updates | ConsoleKit-32bit                  | 0.2.10-64.13.6        | 0.2.10-64.65.1            | x86_64\nv | SLES11-SP1-Updates | ConsoleKit-x11                    | 0.2.10-64.13.6        | 0.2.10-64.65.1            | x86_64\nv | SLES11-SP1-Updates | Mesa                              | 7.7-0.4.41            | 7.7-5.10.1                | x86_64\nv | SLES11-SP1-Updates | Mesa-32bit                        | 7.7-0.4.41            | 7.7-5.10.1                | x86_64\nv | SLES11-SP1-Updates | MozillaFirefox                    | 3.6.16-0.2.1          | 10.0.2-0.4.1              | x86_64\nv | SLES11-SP1-Updates | MozillaFirefox-branding-SLED      | 3.5-1.2.4             | 7-0.6.7.7                 | x86_64\nv | SLES11-SP1-Updates | MozillaFirefox-translations       | 3.6.16-0.2.1          | 10.0.2-0.4.1              | x86_64\nv | SLES11-SP1-Updates | NetworkManager-glib               | 0.7.0.r4359-15.25.1   | 0.7.1_git20090811-3.9.9.5 | x86_64\nv | SLES11-SP1-Updates | PackageKit                        | 0.3.14-2.12.105       | 0.3.14-2.14.5.1           | x86_64\nv | SLES11-SP1-Updates | PackageKit-lang                   | 0.3.14-2.12.105       | 0.3.14-2.14.5.1           | x86_64\nv | SLES11-SP1-Updates | SuSEfirewall2                     | 3.6_SVNr208-2.1       | 3.6_SVNr208-2.5.1         | noarch\nv | SLES11-SP1-Updates | a2ps                              | 4.13-1326.33          | 4.13-1326.35.1            | x86_64\nv | SLES11-SP1-Updates | acpid                             | 1.0.6-91.6            | 1.0.6-91.16.1             | x86_64\nv | SLES11-SP1-Updates | alsa                              | 1.0.18-16.9.29        | 1.0.18-16.24.1            | x86_64\nv | SLES11-SP1-Updates | alsa-plugins                      | 1.0.18-7.5            | 1.0.18-7.12.23            | x86_64\nv | SLES11-SP1-Updates | alsa-plugins-pulse                | 1.0.18-7.5            | 1.0.18-7.12.23            | x86_64\nv | SLES11-SP1-Updates | apparmor-parser                   | 2.3.1-8.14.9          | 2.3.1-8.18.7              | x86_64\nv | SLES11-SP1-Updates | apparmor-profiles                 | 2.3-48.3              | 2.3-48.7.1                | noarch\nv | SLES11-SP1-Updates | apparmor-utils                    | 2.3.1-9.6.3           | 2.3.1-9.8.5               | noarch\nv | SLES11-SP1-Updates | at                                | 3.1.8-1069.15.53      | 3.1.8-1069.18.2           | x86_64\nv | SLES11-SP1-Updates | audit                             | 1.7.7-5.16            | 1.7.7-5.18.4.1            | x86_64\nv | SLES11-SP1-Updates | audit-libs                        | 1.7.7-5.16            | 1.7.7-5.18.4.1            | x86_64\nv | SLES11-SP1-Updates | audit-libs-32bit                  | 1.7.7-5.16            | 1.7.7-5.18.4.1            | x86_64\nv | SLES11-SP1-Updates | bind-libs                         | 9.5.0P2-20.7.1        | 9.6ESVR5P1-0.2.4.1        | x86_64\nv | SLES11-SP1-Updates | bind-libs-32bit                   | 9.5.0P2-20.7.1        | 9.6ESVR5P1-0.2.4.1        | x86_64\nv | SLES11-SP1-Updates | bind-utils                        | 9.5.0P2-20.7.1        | 9.6ESVR5P1-0.2.4.1        | x86_64\nv | SLES11-SP1-Updates | cifs-mount                        | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | coreutils                         | 6.12-32.17            | 6.12-32.35.1              | x86_64\nv | SLES11-SP1-Updates | coreutils-lang                    | 6.12-32.17            | 6.12-32.35.1              | x86_64\nv | SLES11-SP1-Updates | cpio                              | 2.9-75.29.1           | 2.9-75.76.1               | x86_64\nv | SLES11-SP1-Updates | cpio-lang                         | 2.9-75.29.1           | 2.9-75.76.1               | x86_64\nv | SLES11-SP1-Updates | cron                              | 4.1-194.24.4          | 4.1-194.199.1             | x86_64\nv | SLES11-SP1-Updates | cups-client                       | 1.3.9-8.37.1          | 1.3.9-8.44.1              | x86_64\nv | SLES11-SP1-Updates | cups-libs                         | 1.3.9-8.37.1          | 1.3.9-8.44.1              | x86_64\nv | SLES11-SP1-Updates | cups-libs-32bit                   | 1.3.9-8.37.1          | 1.3.9-8.44.1              | x86_64\nv | SLES11-SP1-Updates | curl                              | 7.19.0-11.25.1        | 7.19.7-1.18.1             | x86_64\nv | SLES11-SP1-Updates | cvs                               | 1.12.12-144.21        | 1.12.12-144.23.5.1        | x86_64\nv | SLES11-SP1-Updates | dbus-1                            | 1.2.10-3.15.1         | 1.2.10-3.23.1             | x86_64\nv | SLES11-SP1-Updates | dbus-1-32bit                      | 1.2.10-3.15.1         | 1.2.10-3.23.1             | x86_64\nv | SLES11-SP1-Updates | dbus-1-glib                       | 0.76-34.18.5          | 0.76-34.22.1              | x86_64\nv | SLES11-SP1-Updates | dbus-1-glib-32bit                 | 0.76-34.18.5          | 0.76-34.22.1              | x86_64\nv | SLES11-SP1-Updates | dbus-1-x11                        | 1.2.10-3.11.33        | 1.2.10-3.23.1             | x86_64\nv | SLES11-SP1-Updates | dejavu                            | 2.26-1.17             | 2.32-4.2.1                | noarch\nv | SLES11-SP1-Updates | device-mapper                     | 1.02.27-8.18.1        | 1.02.27-8.22.2            | x86_64\nv | SLES11-SP1-Updates | device-mapper-32bit               | 1.02.27-8.18.1        | 1.02.27-8.22.2            | x86_64\nv | SLES11-SP1-Updates | dhcpcd                            | 3.2.3-44.20.1         | 3.2.3-44.28.1             | x86_64\nv | SLES11-SP1-Updates | dmraid                            | 1.0.0.rc16-0.6.18     | 1.0.0.rc16-0.12.1         | x86_64\nv | SLES11-SP1-Updates | eject                             | 2.1.0-115.17          | 2.1.0-115.19.2.1          | x86_64\nv | SLES11-SP1-Updates | emacs                             | 22.3-4.32.4           | 22.3-4.36.1               | x86_64\nv | SLES11-SP1-Updates | emacs-info                        | 22.3-4.32.4           | 22.3-4.36.1               | x86_64\nv | SLES11-SP1-Updates | emacs-x11                         | 22.3-4.32.4           | 22.3-4.36.1               | x86_64\nv | SLES11-SP1-Updates | evolution-data-server             | 2.28.2-0.16.1         | 2.28.2-0.22.1             | x86_64\nv | SLES11-SP1-Updates | evolution-data-server-32bit       | 2.28.2-0.16.1         | 2.28.2-0.22.1             | x86_64\nv | SLES11-SP1-Updates | evolution-data-server-lang        | 2.28.2-0.16.1         | 2.28.2-0.22.1             | x86_64\nv | SLES11-SP1-Updates | file-32bit                        | 4.24-43.17            | 4.24-43.19.1              | x86_64\nv | SLES11-SP1-Updates | findutils                         | 4.4.0-38.24.11        | 4.4.0-38.26.1             | x86_64\nv | SLES11-SP1-Updates | findutils-locate                  | 4.4.0-38.24.11        | 4.4.0-38.26.1             | x86_64\nv | SLES11-SP1-Updates | freetype2                         | 2.3.7-25.24.1         | 2.3.7-25.28.1             | x86_64\nv | SLES11-SP1-Updates | freetype2-32bit                   | 2.3.7-25.24.1         | 2.3.7-25.28.1             | x86_64\nv | SLES11-SP1-Updates | gawk                              | 3.1.6-26.1.35         | 3.1.8-2.6.1               | x86_64\nv | SLES11-SP1-Updates | gdb                               | 7.0-0.4.16            | 7.3-0.6.1                 | x86_64\nv | SLES11-SP1-Updates | gdm                               | 2.24.0-24.28.1        | 2.24.0-24.77.1            | x86_64\nv | SLES11-SP1-Updates | gdm-branding-upstream             | 2.24.0-24.28.1        | 2.24.0-24.77.1            | x86_64\nv | SLES11-SP1-Updates | gdm-lang                          | 2.24.0-24.28.1        | 2.24.0-24.77.1            | x86_64\nv | SLES11-SP1-Updates | glibc                             | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-32bit                       | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-devel                       | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-devel-32bit                 | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-i18ndata                    | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-info                        | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-locale                      | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | glibc-locale-32bit                | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | gnome-control-center              | 2.28.1-0.9.1          | 2.28.1-0.15.25            | x86_64\nv | SLES11-SP1-Updates | gnome-control-center-lang         | 2.28.1-0.9.1          | 2.28.1-0.15.25            | x86_64\nv | SLES11-SP1-Updates | gnome-desktop                     | 2.28.2-0.3.22         | 2.28.2-0.6.18             | x86_64\nv | SLES11-SP1-Updates | gnome-desktop-lang                | 2.28.2-0.3.22         | 2.28.2-0.6.18             | x86_64\nv | SLES11-SP1-Updates | gnome-media                       | 2.28.5-1.2.97         | 2.28.5-1.7.8              | x86_64\nv | SLES11-SP1-Updates | gnome-media-lang                  | 2.28.5-1.2.97         | 2.28.5-1.7.8              | x86_64\nv | SLES11-SP1-Updates | gnome-packagekit                  | 0.3.14-2.76.59        | 0.3.14-2.82.1             | x86_64\nv | SLES11-SP1-Updates | gnome-packagekit-lang             | 0.3.14-2.76.59        | 0.3.14-2.82.1             | x86_64\nv | SLES11-SP1-Updates | gnome-panel                       | 2.28.0-2.4.49         | 2.28.0-2.6.4              | x86_64\nv | SLES11-SP1-Updates | gnome-panel-32bit                 | 2.28.0-2.4.49         | 2.28.0-2.6.4              | x86_64\nv | SLES11-SP1-Updates | gnome-panel-lang                  | 2.28.0-2.4.49         | 2.28.0-2.6.4              | x86_64\nv | SLES11-SP1-Updates | gnome-power-manager               | 2.24.1-17.36.1        | 2.24.1-17.52.2            | x86_64\nv | SLES11-SP1-Updates | gnome-power-manager-lang          | 2.24.1-17.36.1        | 2.24.1-17.52.2            | x86_64\nv | SLES11-SP1-Updates | gnome-screensaver                 | 2.28.3-0.4.30         | 2.28.3-0.28.1             | x86_64\nv | SLES11-SP1-Updates | gnome-screensaver-lang            | 2.28.3-0.4.30         | 2.28.3-0.28.1             | x86_64\nv | SLES11-SP1-Updates | gnome-settings-daemon             | 2.28.2-0.25.2         | 2.28.2-0.33.2             | x86_64\nv | SLES11-SP1-Updates | gnome-settings-daemon-lang        | 2.28.2-0.25.2         | 2.28.2-0.33.2             | x86_64\nv | SLES11-SP1-Updates | gnome-system-monitor              | 2.28.0-1.3.26         | 2.28.0-1.7.1              | x86_64\nv | SLES11-SP1-Updates | gnome-system-monitor-lang         | 2.28.0-1.3.26         | 2.28.0-1.7.1              | x86_64\nv | SLES11-SP1-Updates | gok                               | 2.28.1-0.1.136        | 2.28.1-0.3.51             | x86_64\nv | SLES11-SP1-Updates | gok-lang                          | 2.28.1-0.1.136        | 2.28.1-0.3.51             | x86_64\nv | SLES11-SP1-Updates | gpg2                              | 2.0.9-25.26.1         | 2.0.9-25.33.27.1          | x86_64\nv | SLES11-SP1-Updates | gpg2-lang                         | 2.0.9-25.26.1         | 2.0.9-25.33.27.1          | x86_64\nv | SLES11-SP1-Updates | grub                              | 0.97-162.10.1         | 0.97-162.13.12.1          | x86_64\nv | SLES11-SP1-Updates | gtk2                              | 2.18.9-0.5.1          | 2.18.9-0.16.1             | x86_64\nv | SLES11-SP1-Updates | gtk2-32bit                        | 2.18.9-0.5.1          | 2.18.9-0.16.1             | x86_64\nv | SLES11-SP1-Updates | gtk2-lang                         | 2.18.9-0.5.1          | 2.18.9-0.16.1             | x86_64\nv | SLES11-SP1-Updates | gvfs                              | 1.4.3-0.3.13          | 1.4.3-0.13.1              | x86_64\nv | SLES11-SP1-Updates | gvfs-backends                     | 1.4.3-0.3.13          | 1.4.3-0.13.1              | x86_64\nv | SLES11-SP1-Updates | gvfs-fuse                         | 1.4.3-0.3.13          | 1.4.3-0.13.1              | x86_64\nv | SLES11-SP1-Updates | gvfs-lang                         | 1.4.3-0.3.13          | 1.4.3-0.13.1              | x86_64\nv | SLES11-SP1-Updates | hal                               | 0.5.12-23.40.5        | 0.5.12-23.47.4            | x86_64\nv | SLES11-SP1-Updates | hal-32bit                         | 0.5.12-23.40.5        | 0.5.12-23.47.4            | x86_64\nv | SLES11-SP1-Updates | hplip-hpijs                       | 3.9.8-3.5.1           | 3.11.10-0.6.7.1           | x86_64\nv | SLES11-SP1-Updates | hwinfo                            | 15.33-0.2.19          | 15.33-0.4.26              | x86_64\nv | SLES11-SP1-Updates | iproute2                          | 2.6.29.1-6.5.1        | 2.6.29.1-6.7.7.1          | x86_64\nv | SLES11-SP1-Updates | iptables                          | 1.4.6-2.2.24          | 1.4.6-2.8.3.1             | x86_64\nv | SLES11-SP1-Updates | iputils                           | ss021109-292.26.1     | ss021109-292.28.1         | x86_64\nv | SLES11-SP1-Updates | irqbalance                        | 0.55-120.20.9         | 0.55-120.32.1             | x86_64\nv | SLES11-SP1-Updates | kbd                               | 1.14.1-16.24.25       | 1.14.1-16.31.1            | x86_64\nv | SLES11-SP1-Updates | kdump                             | 0.7.8-1.15.7          | 0.7.8-1.33.32.1           | x86_64\nv | SLES11-SP1-Updates | kernel-default                    | 2.6.32.29-0.3.1       | 2.6.32.54-0.3.1           | x86_64\nv | SLES11-SP1-Updates | kernel-default-base               | 2.6.32.29-0.3.1       | 2.6.32.54-0.3.1           | x86_64\nv | SLES11-SP1-Updates | kernel-default-devel              | 2.6.32.29-0.3.1       | 2.6.32.54-0.3.1           | x86_64\nv | SLES11-SP1-Updates | kernel-source                     | 2.6.32.29-0.3.1       | 2.6.32.54-0.3.1           | x86_64\nv | SLES11-SP1-Updates | kexec-tools                       | 2.0.0-53.28.1         | 2.0.0-53.30.1             | x86_64\nv | SLES11-SP1-Updates | klogd                             | 1.4.1-708.25.34       | 1.4.1-708.37.1            | x86_64\nv | SLES11-SP1-Updates | kpartx                            | 0.4.8-40.25.1         | 0.4.8-40.44.1             | x86_64\nv | SLES11-SP1-Updates | krb5                              | 1.6.3-133.46.1        | 1.6.3-133.48.48.1         | x86_64\nv | SLES11-SP1-Updates | krb5-32bit                        | 1.6.3-133.46.1        | 1.6.3-133.48.48.1         | x86_64\nv | SLES11-SP1-Updates | krb5-client                       | 1.6.3-133.46.1        | 1.6.3-133.48.48.1         | x86_64\nv | SLES11-SP1-Updates | ksh                               | 93t-9.184.1           | 93u-0.6.1                 | x86_64\nv | SLES11-SP1-Updates | libMagickCore1                    | 6.4.3.6-7.20.1        | 6.4.3.6-7.22.1            | x86_64\nv | SLES11-SP1-Updates | libapparmor1                      | 2.3-51.14             | 2.3-51.16.1               | x86_64\nv | SLES11-SP1-Updates | libapr-util1                      | 1.3.4-12.20.2         | 1.3.4-12.22.21.2          | x86_64\nv | SLES11-SP1-Updates | libapr1                           | 1.3.3-11.16.1         | 1.3.3-11.18.19.1          | x86_64\nv | SLES11-SP1-Updates | libasound2                        | 1.0.18-16.9.29        | 1.0.18-16.24.1            | x86_64\nv | SLES11-SP1-Updates | libasound2-32bit                  | 1.0.18-16.9.29        | 1.0.18-16.24.1            | x86_64\nv | SLES11-SP1-Updates | libaugeas0                        | 0.5.0-1.1.61          | 0.8.1-7.8.2               | x86_64\nv | SLES11-SP1-Updates | libblkid1                         | 2.16-6.11.1           | 2.16-6.13.1               | x86_64\nv | SLES11-SP1-Updates | libcap2                           | 2.11-2.15             | 2.11-2.17.1               | x86_64\nv | SLES11-SP1-Updates | libcap2-32bit                     | 2.11-2.15             | 2.11-2.17.1               | x86_64\nv | SLES11-SP1-Updates | libcurl4                          | 7.19.0-11.25.1        | 7.19.7-1.18.1             | x86_64\nv | SLES11-SP1-Updates | libcurl4-32bit                    | 7.19.0-11.25.1        | 7.19.7-1.18.1             | x86_64\nv | SLES11-SP1-Updates | libdrm                            | 2.4.17-0.3.12         | 2.4.21-2.2.15             | x86_64\nv | SLES11-SP1-Updates | libdrm-32bit                      | 2.4.17-0.3.12         | 2.4.21-2.2.15             | x86_64\nv | SLES11-SP1-Updates | libfprint0                        | 0.0.6-9.16            | 0.0.6-18.15.4             | x86_64\nv | SLES11-SP1-Updates | libfreebl3                        | 3.12.8-1.2.1          | 3.13.1-0.2.1              | x86_64\nv | SLES11-SP1-Updates | libfreebl3-32bit                  | 3.12.8-1.2.1          | 3.13.1-0.2.1              | x86_64\nv | SLES11-SP1-Updates | libgnome-desktop-2-11             | 2.28.2-0.3.22         | 2.28.2-0.6.18             | x86_64\nv | SLES11-SP1-Updates | libgnome-desktop-2-11-32bit       | 2.28.2-0.3.22         | 2.28.2-0.6.18             | x86_64\nv | SLES11-SP1-Updates | libgnome-window-settings1         | 2.28.1-0.9.1          | 2.28.1-0.15.25            | x86_64\nv | SLES11-SP1-Updates | libgnomesu                        | 1.0.0-307.5.12        | 1.0.0-307.10.1            | x86_64\nv | SLES11-SP1-Updates | libgnomesu-lang                   | 1.0.0-307.5.12        | 1.0.0-307.10.1            | x86_64\nv | SLES11-SP1-Updates | libgnomesu0                       | 1.0.0-307.5.12        | 1.0.0-307.10.1            | x86_64\nv | SLES11-SP1-Updates | libgnutls26                       | 2.4.1-24.32.1         | 2.4.1-24.39.33.1          | x86_64\nv | SLES11-SP1-Updates | libgnutls26-32bit                 | 2.4.1-24.32.1         | 2.4.1-24.39.33.1          | x86_64\nv | SLES11-SP1-Updates | libgssglue1                       | 0.1-6.22              | 0.1-20.2.1                | x86_64\nv | SLES11-SP1-Updates | libgvfscommon0                    | 1.4.3-0.3.13          | 1.4.3-0.13.1              | x86_64\nv | SLES11-SP1-Updates | libicu                            | 4.0-7.24.11           | 4.0-7.26.1                | x86_64\nv | SLES11-SP1-Updates | libjasper                         | 1.900.1-134.9         | 1.900.1-134.11.1          | x86_64\nv | SLES11-SP1-Updates | libldap-2_4-2                     | 2.4.20-0.9.1          | 2.4.26-0.12.1             | x86_64\nv | SLES11-SP1-Updates | libldap-2_4-2-32bit               | 2.4.20-0.9.1          | 2.4.26-0.12.1             | x86_64\nv | SLES11-SP1-Updates | libmysqlclient15                  | 5.0.67-13.26.1        | 5.0.94-0.2.4.1            | x86_64\nv | SLES11-SP1-Updates | libmysqlclient_r15                | 5.0.67-13.26.1        | 5.0.94-0.2.4.1            | x86_64\nv | SLES11-SP1-Updates | libneon27                         | 0.28.3-2.12.1         | 0.29.6-6.7.1              | x86_64\nv | SLES11-SP1-Updates | libnet                            | 1.1.2.1-140.22        | 1.1.2.1-140.24.1          | x86_64\nv | SLES11-SP1-Updates | libnotify                         | 0.4.4-173.27.1        | 0.4.4-173.29.28.1         | x86_64\nv | SLES11-SP1-Updates | libnotify1                        | 0.4.4-173.27.1        | 0.4.4-173.29.28.1         | x86_64\nv | SLES11-SP1-Updates | libopenssl0_9_8                   | 0.9.8h-30.32.1        | 0.9.8j-0.28.1             | x86_64\nv | SLES11-SP1-Updates | libopenssl0_9_8-32bit             | 0.9.8h-30.32.1        | 0.9.8j-0.28.1             | x86_64\nv | SLES11-SP1-Updates | libpackagekit-glib10              | 0.3.14-2.12.105       | 0.3.14-2.14.5.1           | x86_64\nv | SLES11-SP1-Updates | libpcap0                          | 0.9.8-50.4.80         | 0.9.8-50.6.2              | x86_64\nv | SLES11-SP1-Updates | libpciaccess0                     | 7.4-8.24.2            | 7.4_0.11.0-0.4.6.1        | x86_64\nv | SLES11-SP1-Updates | libpng12-0                        | 1.2.31-5.18.1         | 1.2.31-5.25.1             | x86_64\nv | SLES11-SP1-Updates | libpng12-0-32bit                  | 1.2.31-5.18.1         | 1.2.31-5.25.1             | x86_64\nv | SLES11-SP1-Updates | libpulse-browse0                  | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | libpulse-mainloop-glib0           | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | libpulse0                         | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | libpulse0-32bit                   | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | libpython2_6-1_0                  | 2.6.0-8.10.1          | 2.6.0-8.14.1              | x86_64\nv | SLES11-SP1-Updates | libqt4                            | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-32bit                      | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-qt3support                 | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-qt3support-32bit           | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-sql                        | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-sql-32bit                  | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-sql-mysql                  | 4.6.2-1.6.11          | 4.6.3-5.10.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-x11                        | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | libqt4-x11-32bit                  | 4.6.2-1.6.9           | 4.6.3-5.12.1              | x86_64\nv | SLES11-SP1-Updates | librsvg                           | 2.26.0-2.1.227        | 2.26.0-2.3.1              | x86_64\nv | SLES11-SP1-Updates | librsvg-32bit                     | 2.26.0-2.1.227        | 2.26.0-2.3.1              | x86_64\nv | SLES11-SP1-Updates | libslab-lang                      | 2.27.91-6.10.1        | 2.27.91-6.15.2            | x86_64\nv | SLES11-SP1-Updates | libslab0                          | 2.27.91-6.10.1        | 2.27.91-6.15.2            | x86_64\nv | SLES11-SP1-Updates | libsmbclient0                     | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libsmbclient0-32bit               | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libsndfile                        | 1.0.20-2.1.46         | 1.0.20-2.4.1              | x86_64\nv | SLES11-SP1-Updates | libsndfile-32bit                  | 1.0.20-2.1.46         | 1.0.20-2.4.1              | x86_64\nv | SLES11-SP1-Updates | libsnmp15                         | 5.4.2.1-8.5.1         | 5.4.2.1-8.12.6.1          | x86_64\nv | SLES11-SP1-Updates | libsoup-2_4-1                     | 2.28.2-0.1.151        | 2.28.2-0.3.1              | x86_64\nv | SLES11-SP1-Updates | libsoup-2_4-1-32bit               | 2.28.2-0.1.151        | 2.28.2-0.3.1              | x86_64\nv | SLES11-SP1-Updates | libtalloc1                        | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libtalloc1-32bit                  | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libtdb1                           | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libtdb1-32bit                     | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libtiff3                          | 3.8.2-141.16.1        | 3.8.2-141.142.1           | x86_64\nv | SLES11-SP1-Updates | libtiff3-32bit                    | 3.8.2-141.16.1        | 3.8.2-141.142.1           | x86_64\nv | SLES11-SP1-Updates | libtirpc1                         | 0.2.1-1.3.1           | 0.2.1-1.5.1               | x86_64\nv | SLES11-SP1-Updates | libuuid1                          | 2.16-6.11.1           | 2.16-6.13.1               | x86_64\nv | SLES11-SP1-Updates | libuuid1-32bit                    | 2.16-6.11.1           | 2.16-6.13.1               | x86_64\nv | SLES11-SP1-Updates | libwbclient0                      | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libwbclient0-32bit                | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | libxcrypt                         | 3.0.3-0.1.50          | 3.0.3-0.6.1               | x86_64\nv | SLES11-SP1-Updates | libxcrypt-32bit                   | 3.0.3-0.1.50          | 3.0.3-0.6.1               | x86_64\nv | SLES11-SP1-Updates | libxml2                           | 2.7.6-0.7.1           | 2.7.6-0.13.1              | x86_64\nv | SLES11-SP1-Updates | libxml2-32bit                     | 2.7.6-0.7.1           | 2.7.6-0.13.1              | x86_64\nv | SLES11-SP1-Updates | libzypp                           | 6.35.3-0.3.1          | 6.37.5-0.5.6              | x86_64\nv | SLES11-SP1-Updates | limal-ca-mgm                      | 1.5.22-0.2.15         | 1.5.23-0.3.2              | x86_64\nv | SLES11-SP1-Updates | limal-ca-mgm-perl                 | 1.5.22-0.2.15         | 1.5.23-0.3.2              | x86_64\nv | SLES11-SP1-Updates | logrotate                         | 3.7.7-10.22           | 3.7.7-10.24.1             | x86_64\nv | SLES11-SP1-Updates | lvm2                              | 2.02.39-18.32.2       | 2.02.39-18.48.1           | x86_64\nv | SLES11-SP1-Updates | man-pages                         | 3.15-2.8.28           | 3.15-2.14.1               | noarch\nv | SLES11-SP1-Updates | mcelog                            | 1.0.2010.03.10-0.2.8  | 1.0.2010.03.10-0.4.1      | x86_64\nv | SLES11-SP1-Updates | mdadm                             | 3.0.3-0.22.4          | 3.0.3-0.30.1              | x86_64\nv | SLES11-SP1-Updates | microcode_ctl                     | 1.17-102.22.1         | 1.17-102.40.1             | x86_64\nv | SLES11-SP1-Updates | mkinitrd                          | 2.4.1-0.14.1          | 2.4.1-0.16.16.1           | x86_64\nv | SLES11-SP1-Updates | mozilla-nspr                      | 4.8.6-1.2.1           | 4.8.9-1.2.2.1             | x86_64\nv | SLES11-SP1-Updates | mozilla-nspr-32bit                | 4.8.6-1.2.1           | 4.8.9-1.2.2.1             | x86_64\nv | SLES11-SP1-Updates | mozilla-nss                       | 3.12.8-1.2.1          | 3.13.1-0.2.1              | x86_64\nv | SLES11-SP1-Updates | mozilla-nss-32bit                 | 3.12.8-1.2.1          | 3.13.1-0.2.1              | x86_64\nv | SLES11-SP1-Updates | mozilla-xulrunner191              | 1.9.1.18-0.2.1        | 1.9.1.19-0.2.1            | x86_64\nv | SLES11-SP1-Updates | mozilla-xulrunner191-32bit        | 1.9.1.18-0.2.1        | 1.9.1.19-0.2.1            | x86_64\nv | SLES11-SP1-Updates | mozilla-xulrunner191-gnomevfs     | 1.9.1.18-0.2.1        | 1.9.1.19-0.2.1            | x86_64\nv | SLES11-SP1-Updates | mozilla-xulrunner191-translations | 1.9.1.18-0.2.1        | 1.9.1.19-0.2.1            | x86_64\nv | SLES11-SP1-Updates | mozilla-xulrunner192              | 1.9.2.16-1.2.1        | 1.9.2.27-0.2.1            | x86_64\nv | SLES11-SP1-Updates | mozilla-xulrunner192-translations | 1.9.2.16-1.2.1        | 1.9.2.27-0.2.1            | x86_64\nv | SLES11-SP1-Updates | multipath-tools                   | 0.4.8-40.25.1         | 0.4.8-40.44.1             | x86_64\nv | SLES11-SP1-Updates | mysql                             | 5.0.67-13.26.1        | 5.0.94-0.2.4.1            | x86_64\nv | SLES11-SP1-Updates | mysql-client                      | 5.0.67-13.26.1        | 5.0.94-0.2.4.1            | x86_64\nv | SLES11-SP1-Updates | nautilus                          | 2.28.4-1.6.8          | 2.28.4-1.10.1             | x86_64\nv | SLES11-SP1-Updates | nautilus-32bit                    | 2.28.4-1.6.8          | 2.28.4-1.10.1             | x86_64\nv | SLES11-SP1-Updates | nautilus-lang                     | 2.28.4-1.6.8          | 2.28.4-1.10.1             | x86_64\nv | SLES11-SP1-Updates | net-tools                         | 1.60-725.23.24.39     | 1.60-725.30.1             | x86_64\nv | SLES11-SP1-Updates | nfs-client                        | 1.2.1-2.10.1          | 1.2.1-2.18.1              | x86_64\nv | SLES11-SP1-Updates | nfs-doc                           | 1.2.1-2.10.1          | 1.2.1-2.18.1              | x86_64\nv | SLES11-SP1-Updates | notification-daemon               | 0.3.7-185.30.1        | 0.3.7-185.34.2            | x86_64\nv | SLES11-SP1-Updates | notification-daemon-lang          | 0.3.7-185.30.1        | 0.3.7-185.34.2            | x86_64\nv | SLES11-SP1-Updates | nscd                              | 2.11.1-0.20.1         | 2.11.1-0.34.1             | x86_64\nv | SLES11-SP1-Updates | ntp                               | 4.2.4p8-1.6.1         | 4.2.4p8-1.18.1            | x86_64\nv | SLES11-SP1-Updates | openldap2-client                  | 2.4.20-0.9.1          | 2.4.26-0.12.1             | x86_64\nv | SLES11-SP1-Updates | openslp                           | 1.2.0-172.18.1        | 1.2.0-172.22.1            | x86_64\nv | SLES11-SP1-Updates | openslp-32bit                     | 1.2.0-172.18.1        | 1.2.0-172.22.1            | x86_64\nv | SLES11-SP1-Updates | openslp-server                    | 1.2.0-172.18.1        | 1.2.0-172.22.1            | x86_64\nv | SLES11-SP1-Updates | openssh-askpass                   | 5.1p1-41.31.36        | 5.1p1-41.51.1             | x86_64\nv | SLES11-SP1-Updates | openssl                           | 0.9.8h-30.32.1        | 0.9.8j-0.28.1             | x86_64\nv | SLES11-SP1-Updates | openssl-certs                     | 0.9.8h-27.1.30        | 0.9.8h-27.3.1             | noarch\nv | SLES11-SP1-Updates | opie                              | 2.4-662.16            | 2.4-662.18.1              | x86_64\nv | SLES11-SP1-Updates | opie-32bit                        | 2.4-662.16            | 2.4-662.18.1              | x86_64\nv | SLES11-SP1-Updates | pam                               | 1.0.4-0.5.12          | 1.0.4-0.7.1               | x86_64\nv | SLES11-SP1-Updates | pam-32bit                         | 1.0.4-0.5.12          | 1.0.4-0.7.1               | x86_64\nv | SLES11-SP1-Updates | pam-doc                           | 1.0.4-0.5.12          | 1.0.4-0.7.1               | x86_64\nv | SLES11-SP1-Updates | pam-modules                       | 11-1.6.15             | 11-1.22.1                 | x86_64\nv | SLES11-SP1-Updates | pam-modules-32bit                 | 11-1.6.15             | 11-1.22.1                 | x86_64\nv | SLES11-SP1-Updates | parted                            | 1.8.8-102.21.8        | 1.8.8-102.23.1            | x86_64\nv | SLES11-SP1-Updates | parted-32bit                      | 1.8.8-102.21.8        | 1.8.8-102.23.1            | x86_64\nv | SLES11-SP1-Updates | perl                              | 5.10.0-64.53.1        | 5.10.0-64.55.1            | x86_64\nv | SLES11-SP1-Updates | perl-32bit                        | 5.10.0-64.53.1        | 5.10.0-64.55.1            | x86_64\nv | SLES11-SP1-Updates | perl-Bootloader                   | 0.4.89.20-0.3.1       | 0.4.89.29-0.6.1           | x86_64\nv | SLES11-SP1-Updates | perl-base                         | 5.10.0-64.53.1        | 5.10.0-64.55.1            | x86_64\nv | SLES11-SP1-Updates | perl-doc                          | 5.10.0-64.53.1        | 5.10.0-64.55.1            | x86_64\nv | SLES11-SP1-Updates | perl-libapparmor                  | 2.3-51.14             | 2.3-51.16.1               | x86_64\nv | SLES11-SP1-Updates | perl-libwww-perl                  | 5.816-2.15            | 5.816-2.23.1              | x86_64\nv | SLES11-SP1-Updates | perl-satsolver                    | 0.14.18-0.2.1         | 0.14.19-0.3.8             | x86_64\nv | SLES11-SP1-Updates | permissions                       | 2011.2.15-0.3.1       | 2011.6.28-0.3.1           | x86_64\nv | SLES11-SP1-Updates | pm-utils                          | 0.99.4.20071229-12.10 | 0.99.4.20071229-12.14.1   | x86_64\nv | SLES11-SP1-Updates | pmtools                           | 20071116-44.18        | 20071116-44.20.2.1        | x86_64\nv | SLES11-SP1-Updates | popt                              | 1.7-37.25.1           | 1.7-37.29.29.1            | x86_64\nv | SLES11-SP1-Updates | popt-32bit                        | 1.7-37.25.1           | 1.7-37.29.29.1            | x86_64\nv | SLES11-SP1-Updates | postfix                           | 2.5.6-5.4.21          | 2.5.6-5.10.1              | x86_64\nv | SLES11-SP1-Updates | ppp                               | 2.4.5.git-2.23.10     | 2.4.5.git-2.27.1          | x86_64\nv | SLES11-SP1-Updates | pulseaudio                        | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | pulseaudio-esound-compat          | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | pulseaudio-lang                   | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | pulseaudio-module-x11             | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | pulseaudio-utils                  | 0.9.21-1.5.26         | 0.9.21-1.14.8             | x86_64\nv | SLES11-SP1-Updates | pwdutils                          | 3.2.8-0.2.35          | 3.2.8-0.4.1               | x86_64\nv | SLES11-SP1-Updates | python                            | 2.6.0-8.10.1          | 2.6.0-8.14.1              | x86_64\nv | SLES11-SP1-Updates | python-base                       | 2.6.0-8.10.1          | 2.6.0-8.14.1              | x86_64\nv | SLES11-SP1-Updates | python-satsolver                  | 0.14.18-0.2.1         | 0.14.19-0.3.8             | x86_64\nv | SLES11-SP1-Updates | python-tk                         | 2.6.0-8.10.1          | 2.6.0-8.14.1              | x86_64\nv | SLES11-SP1-Updates | python-xml                        | 2.6.0-8.10.1          | 2.6.0-8.14.1              | x86_64\nv | SLES11-SP1-Updates | rarian                            | 0.8.1-5.16            | 0.8.1-5.17.9              | x86_64\nv | SLES11-SP1-Updates | release-notes-sles                | 11.1.1.1-0.2.1        | 11.1.1.9-0.6.1            | x86_64\nv | SLES11-SP1-Updates | rpm                               | 4.4.2.3-37.25.1       | 4.4.2.3-37.29.29.1        | x86_64\nv | SLES11-SP1-Updates | rpm-32bit                         | 4.4.2.3-37.25.1       | 4.4.2.3-37.29.29.1        | x86_64\nv | SLES11-SP1-Updates | rsh                               | 0.17-706.16           | 0.17-706.18.1             | x86_64\nv | SLES11-SP1-Updates | rsync                             | 3.0.4-2.33.82         | 3.0.4-2.38.1              | x86_64\nv | SLES11-SP1-Updates | samba                             | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | samba-32bit                       | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | samba-client                      | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | samba-client-32bit                | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | samba-winbind                     | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | samba-winbind-32bit               | 3.4.3-1.19.1          | 3.4.3-1.34.1              | x86_64\nv | SLES11-SP1-Updates | satsolver-tools                   | 0.14.18-0.2.1         | 0.14.19-0.3.8             | x86_64\nv | SLES11-SP1-Updates | sax2                              | 8.1-561.29.2          | 8.1-561.548.7             | x86_64\nv | SLES11-SP1-Updates | sax2-gui                          | 8.1-561.29.2          | 8.1-561.548.7             | x86_64\nv | SLES11-SP1-Updates | sax2-ident                        | 8.1-561.29.2          | 8.1-561.548.7             | x86_64\nv | SLES11-SP1-Updates | sax2-libsax                       | 8.1-561.29.2          | 8.1-561.548.7             | x86_64\nv | SLES11-SP1-Updates | sax2-libsax-perl                  | 8.1-561.29.2          | 8.1-561.548.7             | x86_64\nv | SLES11-SP1-Updates | sax2-tools                        | 8.1-561.29.2          | 8.1-561.548.7             | x86_64\nv | SLES11-SP1-Updates | sblim-cmpi-base                   | 1.6.0-0.1.87          | 1.6.0-0.4.1               | x86_64\nv | SLES11-SP1-Updates | sblim-sfcb                        | 1.3.7-0.11.1          | 1.3.7-0.17.1              | x86_64\nv | SLES11-SP1-Updates | screen                            | 4.0.2-162.17          | 4.0.2-162.19.2.1          | x86_64\nv | SLES11-SP1-Updates | sg3_utils                         | 1.28-0.3.5            | 1.28-0.5.1                | x86_64\nv | SLES11-SP1-Updates | smartmontools                     | 5.38.0.20081027-2.14  | 5.38.0.20081027-2.16.2.1  | x86_64\nv | SLES11-SP1-Updates | snmp-mibs                         | 5.4.2.1-8.5.1         | 5.4.2.1-8.12.6.1          | x86_64\nv | SLES11-SP1-Updates | star                              | 1.5final-28.19        | 1.5final-28.21.1          | x86_64\nv | SLES11-SP1-Updates | supportutils                      | 1.20-0.10.1           | 1.20-0.28.27.1            | noarch\nv | SLES11-SP1-Updates | suseRegister                      | 1.4-1.11.1            | 1.4-1.13.1                | noarch\nv | SLES11-SP1-Updates | suspend                           | 0.80.20081103-1.39.63 | 0.80.20081103-1.45.2      | x86_64\nv | SLES11-SP1-Updates | sysconfig                         | 0.71.30-0.8.1         | 0.71.31-0.7.1             | x86_64\nv | SLES11-SP1-Updates | syslog-ng                         | 2.0.9-27.28.3         | 2.0.9-27.32.1             | x86_64\nv | SLES11-SP1-Updates | sysstat                           | 8.1.5-7.12.1          | 8.1.5-7.27.1              | x86_64\nv | SLES11-SP1-Updates | sysvinit                          | 2.86-200.1            | 2.86-204.1                | x86_64\nv | SLES11-SP1-Updates | tar                               | 1.20-23.23.1          | 1.26-1.2.4.1              | x86_64\nv | SLES11-SP1-Updates | tcsh                              | 6.15.00-93.25.18      | 6.15.00-93.33.1           | x86_64\nv | SLES11-SP1-Updates | tightvnc                          | 1.3.9-81.11.28        | 1.3.9-81.13.1             | x86_64\nv | SLES11-SP1-Updates | timezone                          | 2011d-0.3.1           | 2011m-0.3.1               | x86_64\nv | SLES11-SP1-Updates | tk                                | 8.5.5-3.12            | 8.5.5-3.14.1              | x86_64\nv | SLES11-SP1-Updates | tk-32bit                          | 8.5.5-3.12            | 8.5.5-3.14.1              | x86_64\nv | SLES11-SP1-Updates | util-linux                        | 2.16-6.11.1           | 2.16-6.13.1               | x86_64\nv | SLES11-SP1-Updates | util-linux-lang                   | 2.16-6.11.1           | 2.16-6.13.1               | x86_64\nv | SLES11-SP1-Updates | uuid-runtime                      | 2.16-6.11.1           | 2.16-6.13.1               | x86_64\nv | SLES11-SP1-Updates | vino                              | 2.28.1-2.1.143        | 2.28.1-2.3.1              | x86_64\nv | SLES11-SP1-Updates | vino-lang                         | 2.28.1-2.1.143        | 2.28.1-2.3.1              | x86_64\nv | SLES11-SP1-Updates | xinetd                            | 2.3.14-130.10.1       | 2.3.14-130.131.1          | x86_64\nv | SLES11-SP1-Updates | xkeyboard-config                  | 1.5-4.28.1            | 1.5-4.40.1                | noarch\nv | SLES11-SP1-Updates | xorg-x11                          | 7.4-9.39.1            | 7.4-9.47.1                | x86_64\nv | SLES11-SP1-Updates | xorg-x11-Xvnc                     | 7.4-27.30.1           | 7.4-27.40.56.1            | x86_64\nv | SLES11-SP1-Updates | xorg-x11-driver-input             | 7.4-13.31.1           | 7.4-13.33.6               | x86_64\nv | SLES11-SP1-Updates | xorg-x11-driver-video             | 7.4-40.26.20          | 7.4-40.32.4               | x86_64\nv | SLES11-SP1-Updates | xorg-x11-libX11                   | 7.4-5.5               | 7.4-5.9.1                 | x86_64\nv | SLES11-SP1-Updates | xorg-x11-libX11-32bit             | 7.4-5.5               | 7.4-5.9.1                 | x86_64\nv | SLES11-SP1-Updates | xorg-x11-libs                     | 7.4-8.24.2            | 7.4-8.26.32.1             | x86_64\nv | SLES11-SP1-Updates | xorg-x11-libs-32bit               | 7.4-8.24.2            | 7.4-8.26.32.1             | x86_64\nv | SLES11-SP1-Updates | xorg-x11-server                   | 7.4-27.30.1           | 7.4-27.40.56.1            | x86_64\nv | SLES11-SP1-Updates | xorg-x11-server-extra             | 7.4-27.30.1           | 7.4-27.40.56.1            | x86_64\nv | SLES11-SP1-Updates | xorg-x11-xauth                    | 7.4-9.39.1            | 7.4-9.47.1                | x86_64\nv | SLES11-SP1-Updates | yast2                             | 2.17.92-0.2.27        | 2.17.92.2-0.3.1           | x86_64\nv | SLES11-SP1-Updates | yast2-ca-management               | 2.17.17-0.2.34        | 2.17.22-0.6.1             | noarch\nv | SLES11-SP1-Updates | yast2-core                        | 2.17.35-0.2.17        | 2.17.35.3-0.3.1           | x86_64\nv | SLES11-SP1-Updates | yast2-country                     | 2.17.48-0.2.2         | 2.17.50-0.4.1             | x86_64\nv | SLES11-SP1-Updates | yast2-country-data                | 2.17.48-0.2.2         | 2.17.50-0.4.1             | x86_64\nv | SLES11-SP1-Updates | yast2-http-server                 | 2.17.5-1.53           | 2.17.14-0.2.2             | noarch\nv | SLES11-SP1-Updates | yast2-installation                | 2.17.75-0.2.13        | 2.17.76.1-0.3.1           | noarch\nv | SLES11-SP1-Updates | yast2-iscsi-client                | 2.17.20-0.2.23        | 2.17.22-0.5.1             | noarch\nv | SLES11-SP1-Updates | yast2-kerberos-server             | 2.17.7-0.1.164        | 2.17.8-0.6.2              | noarch\nv | SLES11-SP1-Updates | yast2-ldap-client                 | 2.17.22-0.2.1.18      | 2.17.23-0.2.1             | noarch\nv | SLES11-SP1-Updates | yast2-ncurses                     | 2.17.18-0.2.1         | 2.17.18.1-0.3.15          | x86_64\nv | SLES11-SP1-Updates | yast2-ncurses-pkg                 | 2.17.17-0.2.2         | 2.17.17.1-0.4.1           | x86_64\nv | SLES11-SP1-Updates | yast2-network                     | 2.17.141-0.3.10       | 2.17.155.2-0.3.8          | x86_64\nv | SLES11-SP1-Updates | yast2-packager                    | 2.17.78-0.2.6         | 2.17.78.3-0.3.11          | x86_64\nv | SLES11-SP1-Updates | yast2-pkg-bindings                | 2.17.45-0.2.10        | 2.17.45.5-0.3.1           | x86_64\nv | SLES11-SP1-Updates | yast2-qt                          | 2.18.13-0.3.1         | 2.18.14-0.3.1             | x86_64\nv | SLES11-SP1-Updates | yast2-registration                | 2.17.35-0.5.1         | 2.17.35.3-0.4.2.1         | noarch\nv | SLES11-SP1-Updates | yast2-registration-branding-SLE   | 2.17.35-0.5.1         | 2.17.35.3-0.4.2.1         | noarch\nv | SLES11-SP1-Updates | yast2-storage                     | 2.17.99-0.2.5         | 2.17.99.2-0.3.1           | x86_64\nv | SLES11-SP1-Updates | yast2-storage-lib                 | 2.17.99-0.2.5         | 2.17.99.2-0.3.1           | x86_64\nv | SLES11-SP1-Updates | yast2-users                       | 2.17.43-0.2.20        | 2.17.43.1-0.3.1           | x86_64\nv | SLES11-SP1-Updates | yast2-wagon                       | 2.17.17-0.2.56        | 2.17.17.8-0.3.1           | noarch\nv | SLES11-SP1-Updates | yast2-x11                         | 2.17.13-0.2.2         | 2.17.13.1-0.3.1           | noarch\nv | SLES11-SP1-Updates | zypper                            | 1.3.12-0.3.1          | 1.3.16-0.3.7              | x86_64\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/zypper/zypper-list-updates-empty.out",
    "content": "Loading repository data...\nReading installed packages...\nNo updates found.\n"
  },
  {
    "path": "spec/fixtures/unit/provider/package/zypper/zypper-search-uninstalled.out",
    "content": "Refreshing service 'spacewalk'.\nProblem retrieving the repository index file for service 'spacewalk':\n[|] This client is not registered to any spacewalk server.\n\nSkipping service 'spacewalk' because of the above error.\nLoading repository data...\nReading installed packages...\n\nS | Name    | Type    | Version           | Arch   | Repository                                      \n--+---------+---------+-------------------+--------+-------------------------------------------------\n  | vim     | package | 1.0.20040813-19.9 | noarch | localmirror-os                                  \n  | vim     | package | 1.0.20040813-19.9 | noarch | localmirror-os                                  \n  | vim     | package | 1.0.20040813-19.9 | noarch | SUSE-Linux-Enterprise-Server-11-SP4 11.4.4-1.109\n"
  },
  {
    "path": "spec/fixtures/unit/provider/parsedfile/aliases.txt",
    "content": "manager:  root\ndumper:   postmaster\n"
  },
  {
    "path": "spec/fixtures/unit/provider/parsedfile/simple.txt",
    "content": "# This is a sample fixture for the parsedfile provider.\n# HEADER As added by software from a third party.\n# Another inconspicuous comment.\nA generic content line with: a value.\nA_second_line_with_no_spaces\nQexample for bug PUP-4012\nAnother line\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/base/ps_ef.mixed_encoding",
    "content": "UID        PID  PPID  C STIME TTY          TIME CMD\nroot      4113 26549  0 11:07 pts/0    00:00:00 latin-1 args mni interesting furry animals\nroot      4056 26549  0 11:06 pts/0    00:00:00 utf-8 args including the majestik møøse\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/gentoo/rc_update_show",
    "content": "            alsasound |      default                 \n             bootmisc | boot                         \n                devfs |                       sysinit\n              dmcrypt | boot                         \n                dmesg |                       sysinit\n                 fsck | boot                         \n             hostname | boot                         \n              hwclock | boot                         \n              keymaps | boot                         \n            killprocs |              shutdown        \n                local |      default                 \n           localmount | boot                         \n                  lvm | boot                         \n              modules | boot                         \n             mount-ro |              shutdown        \n                 mtab | boot                         \n               net.lo | boot                         \n             netmount |      default                 \n               procfs | boot                         \n                 root | boot                         \n              rsyslog | boot                         \n            savecache |              shutdown        \n                 swap | boot                         \n            swapfiles | boot                         \n               sysctl | boot                         \n         termencoding | boot                         \n                 udev |                       sysinit\n       udev-postmount |      default                 \n              urandom | boot                         \n                  xdm |      default                 \n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/openbsd/rcctl_getall",
    "content": "accounting=NO\npf=YES\npostgresql_flags=-l /var/postgresql/logfile\ntftpd_flags=/tftpboot\nwsmoused_flags=NO\nxdm_flags=\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/openrc/rcservice_list",
    "content": "alsasound\nconsolefont\nlvm-monitoring\npydoc-2.7\npydoc-3.2\nwpa_supplicant\nxdm\nxdm-setup\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/openrc/rcstatus",
    "content": "Runlevel: boot\n hwclock                                                           [  started  ]\n modules                                                           [  started  ]\n dmcrypt                                                           [  started  ]\n lvm                                                               [  started  ]\n fsck                                                              [  started  ]\n root                                                              [  started  ]\n mtab                                                              [  started  ]\n swap                                                              [  started  ]\n localmount                                                        [  started  ]\n sysctl                                                            [  started  ]\n bootmisc                                                          [  started  ]\n hostname                                                          [  started  ]\n termencoding                                                      [  started  ]\n keymaps                                                           [  started  ]\n net.lo                                                            [  started  ]\n procfs                                                            [  started  ]\n rsyslog                                                           [  started  ]\n swapfiles                                                         [  started  ]\n urandom                                                           [  started  ]\nRunlevel: default\n netmount                                                          [  started  ]\n foo_with_very_very_long_servicename_no_still_not_the_end_wait_for_it_almost_there_almost_there_now_finally_the_end [  started  ]\n xdm                                                               [  started  ]\n alsasound                                                         [  started  ]\n udev-postmount                                                    [  started  ]\n local                                                             [  started  ]\nRunlevel: shutdown\n killprocs                                                         [  stopped  ]\n savecache                                                         [  stopped  ]\n mount-ro                                                          [  stopped  ]\nRunlevel: sysinit\n dmesg                                                             [  started  ]\n udev                                                              [  started  ]\n devfs                                                             [  started  ]\nDynamic Runlevel: hotplugged\n net.eth0                                                          [  started  ]\n pcscd                                                             [  started  ]\nDynamic Runlevel: needed\n sysfs                                                             [  started  ]\n udev-mount                                                        [  started  ]\nDynamic Runlevel: manual\n sshd                                                              [  started  ]\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/smf/svcs_fmri.out",
    "content": "fmri         svc:/application/tstapp:default\nname         Dummy\nenabled      false\nstate        disabled\nnext_state   none\nstate_time   July 26, 2018 11:57:49 AM PDT\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/smf/svcs_instances.out",
    "content": "legacy_run    lrc:/etc/rcS_d/S50sk98sol\nonline        svc:/system/svc/restarter:default\nmaintenance   svc:/network/cswrsyncd:default\ndegraded      svc:/network/dns/client:default\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/smf/svcs_multiple_fmris.out",
    "content": "fmri         svc:/application/tstapp:one\nname         Dummy\nenabled      false\nstate        disabled\nnext_state   none\nstate_time   July 26, 2018 11:57:49 AM PDT\n\nfmri         svc:/application/tstapp:two\nname         Dummy\nenabled      false\nstate        disabled\nnext_state   none\nstate_time   July 26, 2018 11:57:49 AM PDT\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/systemd/list_unit_files_services",
    "content": "UNIT FILE                                   STATE\narp-ethers.service                          disabled\nauditd.service                              enabled\nautovt@.service                             disabled\navahi-daemon.service                        enabled\nblk-availability.service                    disabled\nbrandbot.service                            static\napparmor.service                            bad\nudev.service                                enabled-runtime\nufw.service                                 linked\numountfs.service                            linked-runtime\numountnfs.service                           masked\numountroot.service                          masked-runtime\nurandom.service                             indirect\nuser@.service                               generated\nuuidd.service                               transient\n"
  },
  {
    "path": "spec/fixtures/unit/provider/service/systemd/list_unit_files_services_vendor_preset",
    "content": "UNIT FILE                                  STATE           VENDOR PRESET\narp-ethers.service                         disabled        disabled     \nauditd.service                             enabled         enabled      \ndbus.service                               enabled         disabled     \nudev.service                               enabled-runtime disabled\numountfs.service                           linked-runtime  disabled\numountnfs.service                          masked          disabled\numountroot.service                         masked-runtime  disabled\nurandom.service                            indirect        enabled\n"
  },
  {
    "path": "spec/fixtures/unit/provider/user/aix/aix_passwd_file.out",
    "content": "\ntest_aix_user:\n        password = some_password\n        lastupdate = last_update\n\nno_password_user:\n        lastupdate = another_last_update\n\ntab_password_user:\n        password  =  some_password\n        lastupdate = another_last_update\n\ndaemon:\n        password = *\n\nbin:\n        password = *\n\nsys:\n        password = *\n\nadm:\n        password = *\n\nuucp:\n        password = *\n\nguest:\n        password = *\n\nnobody:\n        password = *\n\nlpd:\n        password = *\n\n"
  },
  {
    "path": "spec/fixtures/unit/reports/tagmail/tagmail_email.conf",
    "content": "secure: user@domain.com\n\n"
  },
  {
    "path": "spec/fixtures/unit/reports/tagmail/tagmail_failers.conf",
    "content": "tag:\n: abuse@domain.com\ninvalid!tag: abuse@domain.com\n"
  },
  {
    "path": "spec/fixtures/unit/reports/tagmail/tagmail_passers.conf",
    "content": "# A comment\n# or maybe two\n# plus some blank lines\n    # with some blanks plus a comment\n\n# a simple tag report\none: abuse@domain.com\n\n# with weird spacing\n one : abuse@domain.com\n\n# with multiple tags\none, two: abuse@domain.com\n\n# again with the weird syntax\n one , two : abuse@domain.com\n\n# Some negations\none, !two: abuse@domain.com\n\n# some oddly-formatted tags\none, two-three, !four-five, !six: abuse@domain.com\n\n# multiple addresses\none, two: abuse@domain.com, testing@domain.com\n\n# and with weird spacing\none, two: abuse@domain.com , testing@domain.com\n\n# and a trailing comment\n"
  },
  {
    "path": "spec/fixtures/unit/ssl/certificate/old-style-cert-exts.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFyjCCA7KgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBw\nZXQgQ0E6IGxvY2FsaG9zdDAeFw0xNDExMTMwMDQ5MzJaFw0xOTExMTMwMDQ5MzJa\nMBkxFzAVBgNVBAMMDmZpcmVob3NlLWFnZW50MIICIjANBgkqhkiG9w0BAQEFAAOC\nAg8AMIICCgKCAgEA0/mNeudQrpfTFn/+U8/QlFQ5+IAfKVaRuT/9jGJKrTz+oQJ6\nwQJvAEJUts2RL8JcxGJNQROGteyo7jgIvmj4Lk+zbjOL9nWZqzV4IcM/Q0vUsZtv\n5ejeAxyzgIC7O3t8KWGiEopozIUU2ipN4vEkdhYu4zKc/vEHEzluMbIwmWjCs+58\n/f94ZrrCDDeVL+HXxJWeCyrVLa9dkPlLmEl24GHsb38cUdwCWR6Jp2NjdBRcj8DY\ndZs6Bl+eFyOXhYgz0rzHvHbtERaluP/lQU6f7i4jRRV6N7j2gZio2CjFNg7Lg/Od\n7TzAQ3fejWYDkCE9Z34tMEw3k+wFbUNrLNRq1ReKX6qVPQOCxq3Y3fDrwAFaHzFb\nW+kTMjh9Znh19EfI8pZjBqEM7Nt87oSNULQPrY3KxY/iA5oyrxW5GTSy1qLNKSt0\nSGIlUQmTSAoLYud04sbAuYk+7w9PayT2+JARsJD/LGhCGpCa64Uq89QUNKJZHy6F\n2fgvXyqrdfZsnSIAV/EAudLZdZNZ0t6zsTzMSSV7wTHNNVNLUu23IU72ptjbvEMh\nkurKXzK70RtIQZJU0U7EQ4nzihTh7V8tpkU41/ZfX6Aipv2pl3Ii9b68nywXBbbN\nzp55hx9+Of0i2moi1UMhzEjARJZ2GKb3IGkkiP9gOpjycB5SJfVPFBM9LQUCAwEA\nAaOCARUwggERMDUGCWCGSAGG+EIBDQQoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRl\ncm5hbCBDZXJ0aWZpY2F0ZTAaBgsrBgEEAYKMTAEBAQQLSS1BTS1BLVVVSUQwGQYL\nKwYBBAGCjEwBAQIECmlfYW1fYW5faWQwIQYLKwYBBAGCjEwBAQMEEmlfYW1fYW5f\naW1hZ2VfbmFtZTAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYIKwYBBQUH\nAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFG/V7waEORzLG/9J\nwQsOFF8+LDvBMB8GA1UdIwQYMBaAFCCZleE/LbK3Z5X34EVnNHL6giIeMA0GCSqG\nSIb3DQEBCwUAA4ICAQC9Nt5qdOh2kwX8gchqDLy4dyAey+aRdqAmzyOLCMYzEOl9\nXNTVqF+1MHfrxsVuqrC19fONg67dJEberZhyx3XYV8gA2WemraP8Pq2oWxm9AIp+\nMOT0EEbSbSu5GXF/VOXK4fqRA/A3YeTqocGpnyC2H8VRvPObWEg9RVXCSfDsjBzz\nulmrvW6N46u2eLAPNqWf0389Pp8XA/HPQeId4HTUUA46aMwIyYpIj45+Nq21zFD3\n25z2Nmd6t0bRM54MTVl9FWH2ZJck2pGKXh+1dqQtnOT1Oo6iRK9zqjCcnRiCyIb7\nr4kU9gEucN34MMm0CQFhNVhlI+xKA+wNTRsA+MSQkyp0vn1fAKSJv3tU7+/15Ix7\nkSQ3gLFlxEQOokBRZRW3SzxyvBl9iL3893kL0goYIShjg/N/T5oDFAVS3KCS3ppu\nnVTij2WJA4Gq7ORvmRwKFkXCyHFZh5Ws5z3OGCdm3PXcFtm0RZLptta62ZGRp6rV\nb5/BvcJryvj0ZnTrqmADhqTK1gpuwjl2O8kYwPfdL0zKIlEINT895uDxs5R51cLG\n/wy3JBHO9KOuu+P1pyJ+Dag3uMu0VgMOf3d81Ew4AXRvHehsNVJcu/7t+UMem6Xl\nw1E+VGqvTpEwziQvi9AifhFJXwAI24GsSVYgw09miyJeswSqMMvfTsEPRpb8uw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/unit/ssl/certificate_request/old-style-cert-request.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIEyTCCArECAQAwGTEXMBUGA1UEAwwOZmlyZWhvc2UtYWdlbnQwggIiMA0GCSqG\nSIb3DQEBAQUAA4ICDwAwggIKAoICAQDT+Y1651Cul9MWf/5Tz9CUVDn4gB8pVpG5\nP/2MYkqtPP6hAnrBAm8AQlS2zZEvwlzEYk1BE4a17KjuOAi+aPguT7NuM4v2dZmr\nNXghwz9DS9Sxm2/l6N4DHLOAgLs7e3wpYaISimjMhRTaKk3i8SR2Fi7jMpz+8QcT\nOW4xsjCZaMKz7nz9/3hmusIMN5Uv4dfElZ4LKtUtr12Q+UuYSXbgYexvfxxR3AJZ\nHomnY2N0FFyPwNh1mzoGX54XI5eFiDPSvMe8du0RFqW4/+VBTp/uLiNFFXo3uPaB\nmKjYKMU2DsuD853tPMBDd96NZgOQIT1nfi0wTDeT7AVtQ2ss1GrVF4pfqpU9A4LG\nrdjd8OvAAVofMVtb6RMyOH1meHX0R8jylmMGoQzs23zuhI1QtA+tjcrFj+IDmjKv\nFbkZNLLWos0pK3RIYiVRCZNICgti53TixsC5iT7vD09rJPb4kBGwkP8saEIakJrr\nhSrz1BQ0olkfLoXZ+C9fKqt19mydIgBX8QC50tl1k1nS3rOxPMxJJXvBMc01U0tS\n7bchTvam2Nu8QyGS6spfMrvRG0hBklTRTsRDifOKFOHtXy2mRTjX9l9foCKm/amX\nciL1vryfLBcFts3OnnmHH345/SLaaiLVQyHMSMBElnYYpvcgaSSI/2A6mPJwHlIl\n9U8UEz0tBQIDAQABoGswaQYJKoZIhvcNAQkOMVwwWjAaBgsrBgEEAYKMTAEBAQQL\nSS1BTS1BLVVVSUQwGQYLKwYBBAGCjEwBAQIECmlfYW1fYW5faWQwIQYLKwYBBAGC\njEwBAQMEEmlfYW1fYW5faW1hZ2VfbmFtZTANBgkqhkiG9w0BAQsFAAOCAgEAtA8H\nA/I+yp8CowMHVN1oqNSas2MyJWczpqDkMRW4DksI/6S3EbVwg9SDRXR9oPcwIhuV\n8teqHVkAbCYtthZG+NdNJgBUyGupIw+Jw1xn0ayXYm4ZdHsdUneNKtWRHpYwd1KA\ne13sjhQocCdFU9hLQGdE3vUvcyE+evdtjKhTJ059bJmuXuJxhoHS6AOLTGVEWy/V\nbOurda450+YnA+eLDmc+edkJqlb6odO0blPlhb72A/pT6z4fi/KbHizx5Mqs4s9t\nRRgriyPtIx4hR4qRNTGik87KnJOQ2C5Anak89vzr9vsi08sNun6xOuJ/zDroBtXI\nGif+4Uxg75XgnKFysg85XhzEjaYtsD3rjl9V5xPJOo5qwnksuHQs02M4Wh5qP2xZ\nFmR4gJ1uFsACIKQDDY9dCYrJc0QWPT3G70MS6c5fxaXhAkG3idJBWVxTrRyQw3/v\n4z3B+cBAwGSwa/vxyjZoXSp2gR+/D0gHFaiQL4DHfkZ0vZrp8cFPyiQcXVDct0M/\nq6zw0uTAoNpp0R0mUlcm3LPKW9muW1rZWTtmxfbPGwMWUk3BPwX8ro1XlkSePao/\nBv04PBh0ph9zBEPHz54skjPYMUia6B9ZsD1WhKR8DZlJvmGliHHCv9OgCkCNxnts\nBwfglgz2jKGf1BcZeZCzFKob40W9aRmwHSKRsWw=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "spec/fixtures/unit/type/user/authorized_keys",
    "content": "# fixture for testing ssh key purging\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTXvM7AslzjNUYrPLiNVBsF5VnqL2RmqrkzscdVdHzVxvieNwmLGeUkg8EfXPiz7j5F/Lr0J8oItTCWzyN2KmM+DhUMjvP4AbELO/VYbnVrZICRiUNYSO3EN9/uapKAuiev88d7ynbonCU0VZoTPg/ug4OondOrLCtcGri5ltF+mausGfAYiFAQVEWqXV+1tyejoawJ884etb3n4ilpsrH9JK6AtOkEWVD3TDrNi29O1mQQ/Cn88g472zAJ+DhsIn+iehtfX5nmOtDNN/1t1bGMIBzkSYEAYwUiRJbRXvbobT7qKZQPA3dh0m8AYQS5/hd4/c4pmlxL8kgr24SnBY5 key1 name\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTXvM7AslzjNUYrPLiNVBsF5VnqL2RmqrkzscdVdHzVxvieNwmLGeUkg8EfXPiz7j5F/Lr0J8oItTCWzyN2KmM+DhUMjvP4AbELO/VYbnVrZICRiUNYSO3EN9/uapKAuiev88d7ynbonCU0VZoTPg/ug4OondOrLCtcGri5ltF+mausGfAYiFAQVEWqXV+1tyejoawJ884etb3n4ilpsrH9JK6AtOkEWVD3TDrNi29O1mQQ/Cn88g472zAJ+DhsIn+iehtfX5nmOtDNN/1t1bGMIBzkSYEAYwUiRJbRXvbobT7qKZQPA3dh0m8AYQS5/hd4/c4pmlxL8kgr24SnBY5 keyname2\n#ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTXvM7AslzjNUYrPLiNVBsF5VnqL2RmqrkzscdVdHzVxvieNwmLGeUkg8EfXPiz7j5F/Lr0J8oItTCWzyN2KmM+DhUMjvP4AbELO/VYbnVrZICRiUNYSO3EN9/uapKAuiev88d7ynbonCU0VZoTPg/ug4OondOrLCtcGri5ltF+mausGfAYiFAQVEWqXV+1tyejoawJ884etb3n4ilpsrH9JK6AtOkEWVD3TDrNi29O1mQQ/Cn88g472zAJ+DhsIn+iehtfX5nmOtDNN/1t1bGMIBzkSYEAYwUiRJbRXvbobT7qKZQPA3dh0m8AYQS5/hd4/c4pmlxL8kgr24SnBY5 keyname3\nssh-rsa KEY-WITH-NO-NAME\n"
  },
  {
    "path": "spec/fixtures/unit/util/filetype/aixtab_output",
    "content": "# @(#)08        1.15.1.3  src/bos/usr/sbin/cron/root, cmdcntl, bos530 2/11/94 17:19:47\n# IBM_PROLOG_BEGIN_TAG \n# This is an automatically generated prolog. \n#  \n# bos530 src/bos/usr/sbin/cron/root 1.15.1.3 \n#  \n# Licensed Materials - Property of IBM \n#  \n# (C) COPYRIGHT International Business Machines Corp. 1989,1994 \n# All Rights Reserved \n#  \n# US Government Users Restricted Rights - Use, duplication or \n# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. \n#  \n# IBM_PROLOG_END_TAG \n#\n# COMPONENT_NAME: (CMDCNTL) commands needed for basic system needs\n#\n# FUNCTIONS: \n#\n# ORIGINS: 27\n#\n# (C) COPYRIGHT International Business Machines Corp. 1989,1994\n# All Rights Reserved\n# Licensed Materials - Property of IBM\n#\n# US Government Users Restricted Rights - Use, duplication or\n# disclosure restricted by GSA ADP Schedule Contract with IBM Corp.\n#\n#0 3 * * * /usr/sbin/skulker\n#45 2 * * 0 /usr/lib/spell/compress\n#45 23 * * * ulimit 5000; /usr/lib/smdemon.cleanu > /dev/null\n0 11 * * * /usr/bin/errclear -d S,O 30\n0 12 * * * /usr/bin/errclear -d H 90\n0 15 * * *  /usr/lib/ras/dumpcheck >/dev/null 2>&1\n# SSA warning : Deleting the next two lines may cause errors in redundant\n# SSA warning : hardware to go undetected.\n01 5 * * * /usr/lpp/diagnostics/bin/run_ssa_ela 1>/dev/null 2>/dev/null\n0 * * * * /usr/lpp/diagnostics/bin/run_ssa_healthcheck 1>/dev/null 2>/dev/null\n# SSA warning : Deleting the next line may allow enclosure hardware errors to go undetected\n30 * * * * /usr/lpp/diagnostics/bin/run_ssa_encl_healthcheck 1>/dev/null 2>/dev/null\n# SSA warning : Deleting the next line may allow link speed exceptions to go undetected\n30 4 * * * /usr/lpp/diagnostics/bin/run_ssa_link_speed 1>/dev/null 2>/dev/null\n55 23 * * * /var/perf/pm/bin/pmcfg >/dev/null 2>&1      #Enable PM Data Collection\n"
  },
  {
    "path": "spec/fixtures/unit/util/filetype/suntab_output",
    "content": "#ident  \"@(#)root       1.19    98/07/06 SMI\"   /* SVr4.0 1.1.3.1       */\n#\n# The root crontab should be used to perform accounting data collection.\n#\n#\n10 3 * * * /usr/sbin/logadm\n15 3 * * 0 /usr/lib/fs/nfs/nfsfind\n30 3 * * * [ -x /usr/lib/gss/gsscred_clean ] && /usr/lib/gss/gsscred_clean\n#10 3 * * * /usr/lib/krb5/kprop_script ___slave_kdcs___\n"
  },
  {
    "path": "spec/fixtures/unit/util/monkey_patches/x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFmTCCA4GgAwIBAgIQea0WoUqgpa1Mc1j0BxMuZTANBgkqhkiG9w0BAQUFADBf\nMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0\nMS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw\nHhcNMDEwNTA5MjMxOTIyWhcNMjEwNTA5MjMyODEzWjBfMRMwEQYKCZImiZPyLGQB\nGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNy\nb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQDzXfqAZ9Rap6kMLJAg0DUIPHWEzbcHiZyJ2t7Ow2D6\nkWhanpRxKRh2fMLgyCV2lA5Y+gQ0Nubfr/eAuulYCyuT5Z0F43cikfc0ZDwikR1e\n4QmQvBT+/HVYGeF5tweSo66IWQjYnwfKA1j8aCltMtfSqMtL/OELSDJP5uu4rU/k\nXG8TlJnbldV126gat5SRtHdb9UgMj2p5fRRwBH1tr5D12nDYR7e/my9s5wW34RFg\nrHmRFHzF1qbk4X7Vw37lktI8ALU2gt554W3ztW74nzPJy1J9c5g224uha6KVl5uj\n3sJNJv8GlmclBsjnrOTuEjOVMZnINQhONMp5U9W1vmMyWUA2wKVOBE0921sHM+RY\nv+8/U2TYQlk1V/0PRXwkBE2e1jh0EZcikM5oRHSSb9VLb7CG48c2QqDQ/MHAWvmj\nYbkwR3GWChawkcBCle8Qfyhq4yofseTNAz93cQTHIPxJDx1FiKTXy36IrY4t7EXb\nxFEEySr87IaemhGXW97OU4jm4rf9rJXCKEDb7wSQ34EzOdmyRaUjhwalVYkxuwYt\nYA5BGH0fLrWXyxHrFdUkpZTvFRSJ/Utz+jJb/NEzAPlZYnAHMuouq0Ate8rdIWcb\nMJmPFqojqEHRsG4RmzbE3kB0nOFYZcFgHnpbOMiPuwQmfNQWQOW2a2yqhv0Av87B\nNQIDAQABo1EwTzALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\nFgQUDqyCYEBWJ5flJRP8KuEKU5VZ5KQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI\nhvcNAQEFBQADggIBAMURTQM6YN1dUhF3j7K7NsiyBb+0t6jYIJ1cEwO2HCL6BhM1\ntshj1JpHbyZX0lXxBLEmX9apUGigvNK4bszD6azfGc14rFl0rGY0NsQbPmw4TDMO\nMBINoyb+UVMA/69aToQNDx/kbQUuToVLjWwzb1TSZKu/UK99ejmgN+1jAw/8EwbO\nFjbUVDuVG1FiOuVNF9QFOZKaJ6hbqr3su77jIIlgcWxWs6UT0G0OI36VA+1oPfLY\nY7hrTbboMLXhypRL96KqXZkwsj2nwlFsKCABJCcrSwC3nRFrcL6yEIK8DJto0I07\nJIeqmShynTNfWZC99d6TnjpiWjQ54ohVHbkGsMGJay3XacMZEjaE0Mmg2v8vaXiy\n5Xra69cMwPe9Yxe4ORM4ojZbe/KFVmodZGLBOOKqv1FmopT1EpxmIhBr8rcwki3y\nKfA9OxRDaKLxnCk3y844ICVtfGfzfiQSJAMIgUfspZ6X9RjXz7vV73aW7/3O21ad\nlaBC+ZdY4dcxItNfWeY+biIA6kOEtiXb2fMIVmjAZGsdfOy2k6JiV24u2OdYj8Qx\nSSbd3ik1h/UwcXBbFDxpvYkSfesuo/7Yf56CWlIKK8FDK9kwiJ/IEPuJjeahhXUz\nfmye23MTZGJppS99ypZtn/gETTCSPW4hFCHJPeDD/YprnUr90aGdmUN3P7Da\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/fixtures/unit/util/rdoc/basic.pp",
    "content": "# im a class\nclass foo {\n  file { '/tmp/foo' :\n    ensure => present,\n  }\n}\n\n# im a node\nnode gar {\n}\n\n# im a define\ndefine baz { }\n\n# im a resource\nhost { 'cow' : }\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_fetch_if_not_on_the_local_disk.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_not_update_if_content_on_disk_is_up-to-date.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Content-MD5:\n      - Es93YfogzPk5EimSmqb9XQ==\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Content-MD5:\n      - Es93YfogzPk5EimSmqb9XQ==\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_update_if_content_differs_on_disk.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Content-MD5:\n      - Es93YfogzPk5EimSmqb9XQ==\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Content-MD5:\n      - Es93YfogzPk5EimSmqb9XQ==\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_mtime_is_older_on_disk.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_no_header_specified.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_not_on_the_local_disk.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_not_update_if_mtime_is_newer_on_disk.yml",
    "content": "---\nhttp_interactions:\n- request:\n    method: head\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 301\n      message: ! 'Moved Permanently '\n    headers:\n      Location:\n      - http://my-server/file/\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Content-Length:\n      - '44'\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: head\n    uri: http://my-server/file/\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ''\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\n- request:\n    method: get\n    uri: http://my-server/file\n    body:\n      encoding: US-ASCII\n      string: ''\n    headers:\n      Accept:\n      - ! '*/*'\n      User-Agent:\n      - Ruby\n  response:\n    status:\n      code: 200\n      message: ! 'OK '\n    headers:\n      Etag:\n      - 62e0b-184a-550f415e\n      Content-Type:\n      - text/html\n      Content-Length:\n      - '6218'\n      Last-Modified:\n      - Sun, 22 Mar 2015 22:25:34 GMT\n      Server:\n      - WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)\n      Date:\n      - Sun, 22 Mar 2015 22:57:44 GMT\n      Connection:\n      - Keep-Alive\n    body:\n      encoding: US-ASCII\n      string: ! \"Content via HTTP\\n\"\n    http_version: \n  recorded_at: Sun, 22 Mar 2015 22:57:44 GMT\nrecorded_with: VCR 2.9.3\n"
  },
  {
    "path": "spec/fixtures/yaml/report2.6.x.yaml",
    "content": "--- !ruby/object:Puppet::Transaction::Report\n  external_times: \n    !ruby/sym config_retrieval: 0.170313835144043\n  host: ubuntu1004desktop.localdomain\n  logs: \n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym debug\n      message: Using cached certificate for ca\n      source: Puppet\n      tags: \n        - debug\n      time: 2010-09-23 15:44:06.244173 -07:00\n      version: &id001 2.6.1\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym debug\n      message: Using cached certificate for ubuntu1004desktop.localdomain\n      source: Puppet\n      tags: \n        - debug\n      time: 2010-09-23 15:44:06.244764 -07:00\n      version: *id001\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym debug\n      message: Using cached certificate_revocation_list for ca\n      source: Puppet\n      tags: \n        - debug\n      time: 2010-09-23 15:44:06.245677 -07:00\n      version: *id001\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym debug\n      message: \"catalog supports formats: b64_zlib_yaml dot marshal pson raw yaml; using pson\"\n      source: Puppet\n      tags: \n        - debug\n      time: 2010-09-23 15:44:06.247069 -07:00\n      version: *id001\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym info\n      message: Caching catalog for ubuntu1004desktop.localdomain\n      source: Puppet\n      tags: \n        - info\n      time: 2010-09-23 15:44:06.409109 -07:00\n      version: *id001\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym debug\n      message: Creating default schedules\n      source: Puppet\n      tags: \n        - debug\n      time: 2010-09-23 15:44:06.418755 -07:00\n      version: *id001\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym debug\n      message: Loaded state in 0.00 seconds\n      source: Puppet\n      tags: \n        - debug\n      time: 2010-09-23 15:44:06.427441 -07:00\n      version: *id001\n    - !ruby/object:Puppet::Util::Log\n      level: !ruby/sym info\n      message: Applying configuration version '1285281846'\n      source: Puppet\n      tags: \n        - info\n      time: 2010-09-23 15:44:06.429532 -07:00\n      version: *id001\n  metrics: \n    time: !ruby/object:Puppet::Util::Metric\n      label: Time\n      name: time\n      values: \n        - - config_retrieval\n          - Config retrieval\n          - 0.170313835144043\n        - - schedule\n          - Schedule\n          - 0.00077\n        - - filebucket\n          - Filebucket\n          - 0.000166\n    resources: !ruby/object:Puppet::Util::Metric\n      label: Resources\n      name: resources\n      values: \n        - - !ruby/sym total\n          - Total\n          - 7\n    events: !ruby/object:Puppet::Util::Metric\n      label: Events\n      name: events\n      values: \n        - - !ruby/sym total\n          - Total\n          - 0\n    changes: !ruby/object:Puppet::Util::Metric\n      label: Changes\n      name: changes\n      values: \n        - - !ruby/sym total\n          - Total\n          - 0\n  resource_statuses: \n    \"Schedule[monthly]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000121\n      events: []\n      file: \n      line: \n      resource: \"Schedule[monthly]\"\n      source_description: \"/Schedule[monthly]\"\n      tags: \n        - schedule\n        - monthly\n      time: 2010-09-23 15:44:06.430577 -07:00\n      version: 1285281846\n    \"Filebucket[puppet]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000166\n      events: []\n      file: \n      line: \n      resource: \"Filebucket[puppet]\"\n      source_description: \"/Filebucket[puppet]\"\n      tags: \n        - filebucket\n        - puppet\n      time: 2010-09-23 15:44:06.430998 -07:00\n      version: 1285281846\n    \"Schedule[never]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000119\n      events: []\n      file: \n      line: \n      resource: \"Schedule[never]\"\n      source_description: \"/Schedule[never]\"\n      tags: \n        - schedule\n        - never\n      time: 2010-09-23 15:44:06.433034 -07:00\n      version: 1285281846\n    \"Schedule[weekly]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000118\n      events: []\n      file: \n      line: \n      resource: \"Schedule[weekly]\"\n      source_description: \"/Schedule[weekly]\"\n      tags: \n        - schedule\n        - weekly\n      time: 2010-09-23 15:44:06.431443 -07:00\n      version: 1285281846\n    \"Schedule[puppet]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000129\n      events: []\n      file: \n      line: \n      resource: \"Schedule[puppet]\"\n      source_description: \"/Schedule[puppet]\"\n      tags: \n        - schedule\n        - puppet\n      time: 2010-09-23 15:44:06.432626 -07:00\n      version: 1285281846\n    \"Schedule[daily]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000154\n      events: []\n      file: \n      line: \n      resource: \"Schedule[daily]\"\n      source_description: \"/Schedule[daily]\"\n      tags: \n        - schedule\n        - daily\n      time: 2010-09-23 15:44:06.430130 -07:00\n      version: 1285281846\n    \"Schedule[hourly]\": !ruby/object:Puppet::Resource::Status\n      evaluation_time: 0.000129\n      events: []\n      file: \n      line: \n      resource: \"Schedule[hourly]\"\n      source_description: \"/Schedule[hourly]\"\n      tags: \n        - schedule\n        - hourly\n      time: 2010-09-23 15:44:06.432185 -07:00\n      version: 1285281846\n  time: 2010-09-23 15:44:05.894401 -07:00\n"
  },
  {
    "path": "spec/fixtures/yaml/test.local.yaml",
    "content": "--- !ruby/object:Puppet::Node::Facts\n  expiration: 2010-06-07 19:15:36.519351 -07:00\n  name: test.local\n  values: \n    sp_number_processors: \"2\"\n    kernelmajversion: \"10.3\"\n    kernelversion: 10.3.1\n    sp_secure_vm: secure_vm_enabled\n    ps: ps auxwww\n    macosx_productversion_major: \"10.6\"\n    hostname: test\n    !ruby/sym _timestamp: Mon Jun 07 18:45:36 -0700 2010\n    facterversion: 1.5.7\n    sp_packages: \"1\"\n    timezone: PDT\n    environment: production\n"
  },
  {
    "path": "spec/integration/agent/logging_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet'\nrequire 'puppet/daemon'\nrequire 'puppet/application/agent'\n\n# The command line flags affecting #20900 and #20919:\n#\n# --onetime\n# --daemonize\n# --no-daemonize\n# --logdest\n# --verbose\n# --debug\n# (no flags)     (-)\n#\n# d and nd are mutally exclusive\n#\n# Combinations without logdest, verbose or debug:\n#\n# --onetime --daemonize\n# --onetime --no-daemonize\n# --onetime\n# --daemonize\n# --no-daemonize\n# -\n#\n# 6 cases X [--logdest=console, --logdest=syslog, --logdest=/some/file, <nothing added>]\n# = 24 cases to test\n#\n# X [--verbose, --debug, <nothing added>]\n# = 72 cases to test\n#\n# Expectations of behavior are defined in the expected_loggers, expected_level methods,\n# so adapting to a change in logging behavior should hopefully be mostly a matter of\n# adjusting the logic in those methods to define new behavior.\n#\n# Note that this test does not have anything to say about what happens to logging after\n# daemonizing.\ndescribe 'agent logging' do\n  ONETIME  = '--onetime'\n  DAEMONIZE  = '--daemonize'\n  NO_DAEMONIZE  = '--no-daemonize'\n  LOGDEST_FILE = '--logdest=/dev/null/foo'\n  LOGDEST_SYSLOG = '--logdest=syslog'\n  LOGDEST_CONSOLE = '--logdest=console'\n  VERBOSE  = '--verbose'\n  DEBUG  = '--debug'\n\n  DEFAULT_LOG_LEVEL = :notice\n  INFO_LEVEL        = :info\n  DEBUG_LEVEL       = :debug\n  CONSOLE           = :console\n  SYSLOG            = :syslog\n  EVENTLOG          = :eventlog\n  FILE              = :file\n\n  ONETIME_DAEMONIZE_ARGS = [\n    [ONETIME],\n    [ONETIME, DAEMONIZE],\n    [ONETIME, NO_DAEMONIZE],\n    [DAEMONIZE],\n    [NO_DAEMONIZE],\n    [],\n  ]\n  LOG_DEST_ARGS = [LOGDEST_FILE, LOGDEST_SYSLOG, LOGDEST_CONSOLE, nil]\n  LOG_LEVEL_ARGS = [VERBOSE, DEBUG, nil]\n\n  shared_examples \"an agent\" do |argv, expected|\n    before(:each) do\n      # Don't actually run the agent, bypassing cert checks, forking and the puppet run itself\n      allow_any_instance_of(Puppet::Application::Agent).to receive(:run_command)\n      # Let exceptions be raised instead of exiting\n      allow_any_instance_of(Puppet::Application::Agent).to receive(:exit_on_fail).and_yield\n    end\n\n    def double_of_bin_puppet_agent_call(argv)\n      argv.unshift('agent')\n      command_line = Puppet::Util::CommandLine.new('puppet', argv)\n      command_line.execute\n    end\n\n    if Puppet::Util::Platform.windows? && argv.include?(DAEMONIZE)\n\n      it \"should exit on a platform which cannot daemonize if the --daemonize flag is set\" do\n        expect { double_of_bin_puppet_agent_call(argv) }.to raise_error(SystemExit)\n      end\n\n    else\n      if no_log_dest_set_in(argv)\n        it \"when evoked with #{argv}, logs to #{expected[:loggers].inspect} at level #{expected[:level]}\" do\n          # This logger is created by the Puppet::Settings object which creates and\n          # applies a catalog to ensure that configuration files and users are in\n          # place.\n          #\n          # It's not something we are specifically testing here since it occurs\n          # regardless of user flags.\n          expect(Puppet::Util::Log).to receive(:newdestination).with(instance_of(Puppet::Transaction::Report))\n          expected[:loggers].each do |logclass|\n            expect(Puppet::Util::Log).to receive(:newdestination).with(logclass)\n          end\n          double_of_bin_puppet_agent_call(argv)\n\n          expect(Puppet::Util::Log.level).to eq(expected[:level])\n        end\n      end\n    end\n  end\n\n  def self.no_log_dest_set_in(argv)\n    ([LOGDEST_SYSLOG, LOGDEST_CONSOLE, LOGDEST_FILE] & argv).empty?\n  end\n\n  def self.verbose_or_debug_set_in_argv(argv)\n    !([VERBOSE, DEBUG] & argv).empty?\n  end\n\n  def self.log_dest_is_set_to(argv, log_dest)\n    argv.include?(log_dest)\n  end\n\n  # @param argv Array of commandline flags\n  # @return Set<Symbol> of expected loggers\n  def self.expected_loggers(argv)\n    loggers = Set.new\n    loggers << CONSOLE if verbose_or_debug_set_in_argv(argv)\n    loggers << 'console' if log_dest_is_set_to(argv, LOGDEST_CONSOLE)\n    loggers << '/dev/null/foo' if log_dest_is_set_to(argv, LOGDEST_FILE)\n    if Puppet::Util::Platform.windows?\n      # an explicit call to --logdest syslog on windows is swallowed silently with no\n      # logger created (see #suitable() of the syslog Puppet::Util::Log::Destination subclass)\n      # however Puppet::Util::Log.newdestination('syslog') does get called...so we have\n      # to set an expectation\n      loggers << 'syslog' if log_dest_is_set_to(argv, LOGDEST_SYSLOG)\n\n      loggers << EVENTLOG if no_log_dest_set_in(argv)\n    else\n      # posix\n      loggers << 'syslog' if log_dest_is_set_to(argv, LOGDEST_SYSLOG)\n      loggers << SYSLOG if no_log_dest_set_in(argv)\n    end\n    return loggers\n  end\n\n  # @param argv Array of commandline flags\n  # @return Symbol of the expected log level\n  def self.expected_level(argv)\n    case\n      when argv.include?(VERBOSE) then INFO_LEVEL\n      when argv.include?(DEBUG) then DEBUG_LEVEL\n      else DEFAULT_LOG_LEVEL\n    end\n  end\n\n  # @param argv Array of commandline flags\n  # @return Hash of expected loggers and the expected log level\n  def self.with_expectations_based_on(argv)\n    {\n      :loggers => expected_loggers(argv),\n      :level => expected_level(argv),\n    }\n  end\n\n# For running a single spec (by line number): rspec -l150 spec/integration/agent/logging_spec.rb\n#  debug_argv = []\n#  it_should_behave_like( \"an agent\", [debug_argv], with_expectations_based_on([debug_argv]))\n\n  ONETIME_DAEMONIZE_ARGS.each do |onetime_daemonize_args|\n    LOG_LEVEL_ARGS.each do |log_level_args|\n      LOG_DEST_ARGS.each do |log_dest_args|\n        argv = (onetime_daemonize_args + [log_level_args, log_dest_args]).flatten.compact\n\n        describe \"for #{argv}\" do\n          it_should_behave_like(\"an agent\", argv, with_expectations_based_on(argv))\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/agent_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/puppetserver'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/https'\nrequire 'puppet/application/agent'\n\ndescribe \"puppet agent\", unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n  include_context \"https client\"\n\n  let(:server) { PuppetSpec::Puppetserver.new }\n  let(:agent) { Puppet::Application[:agent] }\n  let(:node) { Puppet::Node.new(Puppet[:certname], environment: 'production')}\n  let(:formatter) { Puppet::Network::FormatHandler.format(:rich_data_json) }\n\n  # Create temp fixtures since the agent will attempt to refresh the CA/CRL\n  before do\n    Puppet[:localcacert] = ca = tmpfile('ca')\n    Puppet[:hostcrl] = crl = tmpfile('crl')\n\n    copy_fixtures(%w[ca.pem intermediate.pem], ca)\n    copy_fixtures(%w[crl.pem intermediate-crl.pem], crl)\n  end\n\n  def copy_fixtures(sources, dest)\n    ssldir = File.join(PuppetSpec::FIXTURE_DIR, 'ssl')\n    File.open(dest, 'w') do |f|\n      sources.each do |s|\n        f.write(File.read(File.join(ssldir, s)))\n      end\n    end\n  end\n\n  context 'server identification' do\n    it 'emits a notice if the server sends the X-Puppet-Compiler-Name header' do\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n               .and output(%r{Notice: Catalog compiled by test-compiler-hostname}).to_stdout\n      end\n    end\n  end\n\n  context 'server_list' do\n    it \"uses the first server in the list\" do\n      Puppet[:server_list] = '127.0.0.1'\n      Puppet[:log_level] = 'debug'\n\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(%r{HTTP GET https://127.0.0.1:#{port}/status/v1/simple/server returned 200 OK}).to_stdout\n      end\n    end\n\n    it \"falls back, recording the first viable server in the report\" do\n      Puppet[:server_list] = \"puppet.example.com,#{Puppet[:server]}\"\n\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(%r{Notice: Applied catalog}).to_stdout\n         .and output(%r{Unable to connect to server from server_list setting: Request to https://puppet.example.com:#{port}/status/v1/simple/server failed}).to_stderr\n\n        report = Puppet::Transaction::Report.convert_from(:yaml, File.read(Puppet[:lastrunreport]))\n        expect(report.server_used).to eq(\"127.0.0.1:#{port}\")\n      end\n    end\n\n    it \"doesn't write a report if no servers could be contacted\" do\n      Puppet[:server_list] = \"puppet.example.com\"\n\n      expect {\n        agent.command_line.args << '--test'\n        agent.run\n      }.to exit_with(1)\n       .and output(a_string_matching(%r{Unable to connect to server from server_list setting})\n       .and matching(/Error: Could not run Puppet configuration client: Could not select a functional puppet server from server_list: 'puppet.example.com'/)).to_stderr\n\n      # I'd expect puppet to update the last run report even if the server_list was\n      # exhausted, but it doesn't work that way currently, see PUP-6708\n      expect(File).to_not be_exist(Puppet[:lastrunreport])\n    end\n\n    it \"omits server_used when not using server_list\" do\n      Puppet[:log_level] = 'debug'\n\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(%r{Resolved service 'puppet' to https://127.0.0.1:#{port}/puppet/v3}).to_stdout\n      end\n\n      report = Puppet::Transaction::Report.convert_from(:yaml, File.read(Puppet[:lastrunreport]))\n      expect(report.server_used).to be_nil\n    end\n\n    it \"server_list takes precedence over server\" do\n      Puppet[:server] = 'notvalid.example.com'\n      Puppet[:log_level] = 'debug'\n\n      server.start_server do |port|\n        Puppet[:server_list] = \"127.0.0.1:#{port}\"\n\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(%r{Debug: Resolved service 'puppet' to https://127.0.0.1:#{port}/puppet/v3}).to_stdout\n\n        report = Puppet::Transaction::Report.convert_from(:yaml, File.read(Puppet[:lastrunreport]))\n        expect(report.server_used).to eq(\"127.0.0.1:#{port}\")\n      end\n    end\n  end\n\n  context 'rich data' do\n    let(:deferred_file) { tmpfile('deferred') }\n    let(:deferred_manifest) do <<~END\n      file { '#{deferred_file}':\n        ensure => file,\n        content => '123',\n      } ->\n      notify { 'deferred':\n        message => Deferred('binary_file', ['#{deferred_file}'])\n      }\n      END\n    end\n\n    it \"calls a deferred 4x function\" do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'deferred4x':\n            message => Deferred('join', [[1,2,3], ':'])\n          }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(%r{Notice: /Stage\\[main\\]/Main/Notify\\[deferred4x\\]/message: defined 'message' as '1:2:3'}).to_stdout\n      end\n    end\n\n    it \"calls a deferred 3x function\" do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'deferred3x':\n            message => Deferred('sprintf', ['%s', 'I am deferred'])\n          }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(%r{Notice: /Stage\\[main\\]/Main/Notify\\[deferred3x\\]/message: defined 'message' as 'I am deferred'}).to_stdout\n      end\n    end\n\n    it \"fails to apply a deferred function with an unsatisfied prerequisite\" do\n      Puppet[:preprocess_deferred] = true\n\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(deferred_manifest, node)\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(1)\n         .and output(%r{Using environment}).to_stdout\n         .and output(%r{The given file '#{deferred_file}' does not exist}).to_stderr\n      end\n    end\n\n    it \"applies a deferred function and its prerequisite in the same run\" do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(deferred_manifest, node)\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(%r{defined 'message' as Binary\\(\"MTIz\"\\)}).to_stdout\n      end\n    end\n\n    it \"re-evaluates a deferred function in a cached catalog\" do\n      Puppet[:report] = false\n      Puppet[:use_cached_catalog] = true\n      Puppet[:usecacheonfailure] = false\n\n      catalog_dir = File.join(Puppet[:client_datadir], 'catalog')\n      Puppet::FileSystem.mkpath(catalog_dir)\n      cached_catalog_path = \"#{File.join(catalog_dir, Puppet[:certname])}.json\"\n\n      # our catalog contains a deferred function that calls `binary_file`\n      # to read `source`. The function returns a Binary object, whose\n      # base64 value is printed to stdout\n      source = tmpfile('deferred_source')\n      catalog = File.read(my_fixture('cached_deferred_catalog.json'))\n      catalog.gsub!('__SOURCE_PATH__', source)\n      File.write(cached_catalog_path, catalog)\n\n      # verify we get a different result each time the deferred function\n      # is evaluated, and reads `source`.\n      {\n        '1234' => 'MTIzNA==',\n        '5678' => 'NTY3OA=='\n      }.each_pair do |content, base64|\n        File.write(source, content)\n\n        expect {\n          agent.command_line.args << '-t'\n          agent.run\n\n        }.to exit_with(2)\n         .and output(/Notice: #{base64}/).to_stdout\n\n        # reset state so we can run again\n        Puppet::Application.clear!\n      end\n    end\n\n    it \"redacts sensitive values\" do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'sensitive':\n            message => Sensitive('supersecret')\n          }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(a_string_matching(\n          /Notice: Sensitive \\[value redacted\\]/\n        ).and matching(\n          /Notify\\[sensitive\\]\\/message: changed \\[redacted\\] to \\[redacted\\]/\n        )).to_stdout\n      end\n    end\n\n    it \"applies binary data in a cached catalog\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'some title':\n            message => Binary.new('aGk=')\n          }\n        MANIFEST\n\n      catalog_dir = File.join(Puppet[:client_datadir], 'catalog')\n      Puppet::FileSystem.mkpath(catalog_dir)\n      cached_catalog = \"#{File.join(catalog_dir, Puppet[:certname])}.json\"\n      File.write(cached_catalog, catalog.render(:rich_data_json))\n\n      expect {\n        Puppet[:report] = false\n        Puppet[:use_cached_catalog] = true\n        Puppet[:usecacheonfailure] = false\n        agent.command_line.args << '-t'\n        agent.run\n      }.to exit_with(2)\n       .and output(%r{defined 'message' as 'hi'}).to_stdout\n    end\n  end\n\n  context 'static catalogs' do\n    let(:path) { tmpfile('file') }\n    let(:metadata) { Puppet::FileServing::Metadata.new(path) }\n    let(:source) { \"puppet:///modules/foo/foo.txt\" }\n\n    before :each do\n      Puppet::FileSystem.touch(path)\n\n      metadata.collect\n      metadata.source = source\n      metadata.content_uri = \"puppet:///modules/foo/files/foo.txt\"\n    end\n\n    it 'uses inline file metadata to determine the file is insync' do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          file { \"#{path}\":\n            ensure => file,\n            source => \"#{source}\"\n          }\n        MANIFEST\n        catalog.metadata = { path => metadata }\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          expect {\n            agent.command_line.args << '--test'\n            agent.run\n          }.to exit_with(0)\n        }.to_not output(/content changed/).to_stdout\n      end\n    end\n\n    it 'retrieves file content using the content_uri from the inlined file metadata' do\n      # create file with binary content\n      binary_content = \"\\xC0\\xFF\".force_encoding('binary')\n      File.binwrite(path, binary_content)\n\n      # recollect metadata\n      metadata.collect\n\n      # overwrite local file so it is no longer in sync\n      File.binwrite(path, \"\")\n\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          file { \"#{path}\":\n            ensure => file,\n            source => \"#{source}\",\n          }\n        MANIFEST\n        catalog.metadata = { path => metadata }\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      static_file_content_handler = -> (req, res) {\n        res.body = binary_content\n        res['Content-Type'] = 'application/octet-stream'\n      }\n\n      mounts = {\n        catalog: catalog_handler,\n        static_file_content: static_file_content_handler\n      }\n\n      server.start_server(mounts: mounts) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(/content changed '{sha256}e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' to '{sha256}3bef83ad320b471d8e3a03c9b9f150749eea610fe266560395d3195cfbd8e6b8'/).to_stdout\n\n        # verify puppet restored binary content\n        expect(File.binread(path)).to eq(binary_content)\n      end\n    end\n  end\n\n  context 'https file sources' do\n    let(:path) { tmpfile('https_file_source') }\n    let(:response_body) { \"from https server\" }\n    let(:digest) { Digest::SHA1.hexdigest(response_body) }\n\n    it 'rejects HTTPS servers whose root cert is not in the system CA store' do\n      unknown_ca_cert = cert_fixture('unknown-ca.pem')\n      https = PuppetSpec::HTTPSServer.new(\n        ca_cert: unknown_ca_cert,\n        server_cert: cert_fixture('unknown-127.0.0.1.pem'),\n        server_key: key_fixture('unknown-127.0.0.1-key.pem')\n      )\n\n      # create a temp cacert bundle\n      ssl_file = tmpfile('systemstore')\n      # add CA cert that is neither the puppet CA nor unknown CA\n      File.write(ssl_file, cert_fixture('netlock-arany-utf8.pem').to_pem)\n\n      https.start_server do |https_port|\n        catalog_handler = -> (req, res) {\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { \"#{path}\":\n              ensure => file,\n              backup => false,\n              checksum => sha1,\n              checksum_value => '#{digest}',\n              source => \"https://127.0.0.1:#{https_port}/path/to/file\"\n            }\n          MANIFEST\n\n          res.body = formatter.render(catalog)\n          res['Content-Type'] = formatter.mime\n        }\n\n        server.start_server(mounts: {catalog: catalog_handler}) do |puppetserver_port|\n          Puppet[:serverport] = puppetserver_port\n\n          # override path to system cacert bundle, this must be done before\n          # the SSLContext is created and the call to X509::Store.set_default_paths\n          Puppet::Util.withenv(\"SSL_CERT_FILE\" => ssl_file) do\n            expect {\n              agent.command_line.args << '--test'\n              agent.run\n            }.to exit_with(4)\n             .and output(/Notice: Applied catalog/).to_stdout\n             .and output(%r{Error: Could not retrieve file metadata for https://127.0.0.1:#{https_port}/path/to/file: certificate verify failed}).to_stderr\n          end\n\n          expect(File).to_not be_exist(path)\n        end\n      end\n    end\n\n    it 'accepts HTTPS servers whose cert is in the system CA store' do\n      unknown_ca_cert = cert_fixture('unknown-ca.pem')\n      https = PuppetSpec::HTTPSServer.new(\n        ca_cert: unknown_ca_cert,\n        server_cert: cert_fixture('unknown-127.0.0.1.pem'),\n        server_key: key_fixture('unknown-127.0.0.1-key.pem')\n      )\n\n      # create a temp cacert bundle\n      ssl_file = tmpfile('systemstore')\n      File.write(ssl_file, unknown_ca_cert.to_pem)\n\n      response_proc = -> (req, res) {\n        res.status = 200\n        res.body = response_body\n      }\n\n      https.start_server(response_proc: response_proc) do |https_port|\n        catalog_handler = -> (req, res) {\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { \"#{path}\":\n              ensure => file,\n              backup => false,\n              checksum => sha1,\n              checksum_value => '#{digest}',\n              source => \"https://127.0.0.1:#{https_port}/path/to/file\"\n            }\n          MANIFEST\n\n          res.body = formatter.render(catalog)\n          res['Content-Type'] = formatter.mime\n        }\n\n        server.start_server(mounts: {catalog: catalog_handler}) do |puppetserver_port|\n          Puppet[:serverport] = puppetserver_port\n\n          # override path to system cacert bundle, this must be done before\n          # the SSLContext is created and the call to X509::Store.set_default_paths\n          Puppet::Util.withenv(\"SSL_CERT_FILE\" => ssl_file) do\n            expect {\n                agent.command_line.args << '--test'\n                agent.run\n            }.to exit_with(2)\n             .and output(%r{https_file_source.*/ensure: created}).to_stdout\n          end\n\n          expect(File.binread(path)).to eq(\"from https server\")\n        end\n      end\n    end\n\n    it 'accepts HTTPS servers whose cert is in the external CA store' do\n      unknown_ca_cert = cert_fixture('unknown-ca.pem')\n      https = PuppetSpec::HTTPSServer.new(\n        ca_cert: unknown_ca_cert,\n        server_cert: cert_fixture('unknown-127.0.0.1.pem'),\n        server_key: key_fixture('unknown-127.0.0.1-key.pem')\n      )\n\n      # create a temp cacert bundle\n      ssl_file = tmpfile('systemstore')\n      File.write(ssl_file, unknown_ca_cert.to_pem)\n\n      response_proc = -> (req, res) {\n        res.status = 200\n        res.body = response_body\n      }\n\n      https.start_server(response_proc: response_proc) do |https_port|\n        catalog_handler = -> (req, res) {\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { \"#{path}\":\n              ensure => file,\n              backup => false,\n              checksum => sha1,\n              checksum_value => '#{digest}',\n              source => \"https://127.0.0.1:#{https_port}/path/to/file\"\n            }\n          MANIFEST\n\n          res.body = formatter.render(catalog)\n          res['Content-Type'] = formatter.mime\n        }\n\n        server.start_server(mounts: {catalog: catalog_handler}) do |puppetserver_port|\n          Puppet[:serverport] = puppetserver_port\n\n          # set path to external cacert bundle, this must be done before\n          # the SSLContext is created\n          Puppet[:ssl_trust_store] = ssl_file\n          expect {\n            agent.command_line.args << '--test'\n            agent.run\n          }.to exit_with(2)\n           .and output(%r{https_file_source.*/ensure: created}).to_stdout\n        end\n\n        expect(File.binread(path)).to eq(\"from https server\")\n      end\n    end\n  end\n\n  context 'multiple agents running' do\n    def with_another_agent_running(&block)\n      path = Puppet[:agent_catalog_run_lockfile]\n\n      th = Thread.new {\n        %x{ruby -e \"$0 = 'puppet'; File.write('#{path}', Process.pid); sleep(5)\"}\n      }\n\n      # ensure file is written before yielding\n      until File.exist?(path) && File.size(path) > 0 do\n        sleep 0.1\n      end\n\n      begin\n        yield\n      ensure\n        th.kill # kill thread so we don't wait too much\n      end\n    end\n\n    it \"exits if an agent is already running\" do\n      with_another_agent_running do\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(1).and output(/Run of Puppet configuration client already in progress; skipping/).to_stdout\n      end\n    end\n\n    it \"waits for other agent run to finish before starting\" do\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        Puppet[:waitforlock] = 1\n\n        with_another_agent_running do\n          expect {\n            agent.command_line.args << '--test'\n            agent.run\n          }.to exit_with(0)\n           .and output(a_string_matching(\n             /Info: Will try again in #{Puppet[:waitforlock]} seconds/\n           ).and matching(\n             /Applied catalog/\n           )).to_stdout\n\n        end\n      end\n    end\n\n    it \"exits if maxwaitforlock is exceeded\" do\n      Puppet[:waitforlock] = 1\n      Puppet[:maxwaitforlock] = 0\n\n      with_another_agent_running do\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(1).and output(/Exiting now because the maxwaitforlock timeout has been exceeded./).to_stdout\n      end\n    end\n  end\n\n  context 'cached catalogs' do\n    it 'falls back to a cached catalog' do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'a message': }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(%r{Caching catalog for #{Puppet[:certname]}}).to_stdout\n      end\n\n      # reset state so we can run again\n      Puppet::Application.clear!\n\n      # --test above turns off `usecacheonfailure` so re-enable here\n      Puppet[:usecacheonfailure] = true\n\n      # run agent without server\n      expect {\n        agent.command_line.args << '--no-daemonize' << '--onetime' << '--server' << '127.0.0.1'\n        agent.run\n      }.to exit_with(2)\n       .and output(a_string_matching(\n         /Using cached catalog from environment 'production'/\n       ).and matching(\n         /Notify\\[a message\\]\\/message:/\n       )).to_stdout\n       .and output(/No more routes to fileserver/).to_stderr\n    end\n\n    it 'preserves the old cached catalog if validation fails with the old one' do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n          exec { 'unqualified_command': }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(1)\n         .and output(%r{Retrieving plugin}).to_stdout\n         .and output(%r{Validation of Exec\\[unqualified_command\\] failed: 'unqualified_command' is not qualified and no path was specified}).to_stderr\n      end\n\n      # cached catalog should not be updated\n      cached_catalog = \"#{File.join(Puppet[:client_datadir], 'catalog', Puppet[:certname])}.json\"\n      expect(File).to_not be_exist(cached_catalog)\n    end\n  end\n\n  context \"reporting\" do\n    it \"stores a finalized report\" do\n      catalog_handler = -> (req, res) {\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n        notify { 'foo':\n          require => Notify['bar']\n        }\n\n        notify { 'bar':\n          require => Notify['foo']\n        }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: {catalog: catalog_handler}) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(1)\n         .and output(%r{Applying configuration}).to_stdout\n         .and output(%r{Found 1 dependency cycle}).to_stderr\n\n        report = Puppet::Transaction::Report.convert_from(:yaml, File.read(Puppet[:lastrunreport]))\n        expect(report.status).to eq(\"failed\")\n        expect(report.metrics).to_not be_empty\n      end\n    end\n\n    it \"caches a report even if the REST request fails\" do\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        Puppet[:report_port] = \"-1\"\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(%r{Applied catalog}).to_stdout\n         .and output(%r{Could not send report}).to_stderr\n\n        report = Puppet::Transaction::Report.convert_from(:yaml, File.read(Puppet[:lastrunreport]))\n        expect(report).to be\n      end\n    end\n  end\n\n  context \"environment convergence\" do\n    it \"falls back to making a node request if the last server-specified environment cannot be loaded\" do\n      mounts = {}\n      mounts[:node] = -> (req, res) {\n        node = Puppet::Node.new('test', environment: Puppet::Node::Environment.remote('doesnotexistonagent'))\n        res.body = formatter.render(node)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: mounts) do |port|\n        Puppet[:serverport] = port\n        Puppet[:log_level] = 'debug'\n\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(a_string_matching(%r{Debug: Requesting environment from the server})).to_stdout\n\n        Puppet::Application.clear!\n\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(a_string_matching(%r{Debug: Successfully loaded last environment from the lastrunfile})).to_stdout\n      end\n    end\n\n    it \"switches to 'newenv' environment and retries the run\" do\n      first_run = true\n      libdir = File.join(my_fixture_dir, 'lib')\n\n      # we have to use the :facter terminus to reliably test that pluginsynced\n      # facts are included in the catalog request\n      Puppet::Node::Facts.indirection.terminus_class = :facter\n\n      mounts = {}\n\n      # During the first run, only return metadata for the top-level directory.\n      # During the second run, include metadata for all of the 'lib' fixtures\n      # due to the `recurse` option.\n      mounts[:file_metadatas] = -> (req, res) {\n        request = Puppet::FileServing::Metadata.indirection.request(\n          :search, libdir, nil, recurse: !first_run\n        )\n        data = Puppet::FileServing::Metadata.indirection.terminus(:file).search(request)\n        res.body = formatter.render(data)\n        res['Content-Type'] = formatter.mime\n      }\n\n      mounts[:file_content] = -> (req, res) {\n        request = Puppet::FileServing::Content.indirection.request(\n          :find, File.join(libdir, 'facter', 'agent_spec_role.rb'), nil\n        )\n        content = Puppet::FileServing::Content.indirection.terminus(:file).find(request)\n        res.body = content.content\n        res['Content-Length'] = content.content.length\n        res['Content-Type'] = 'application/octet-stream'\n      }\n\n      # During the first run, return an empty catalog referring to the newenv.\n      # During the second run, compile a catalog that depends on a fact that\n      # only exists in the second environment. If the fact is missing/empty,\n      # then compilation will fail since resources can't have an empty title.\n      mounts[:catalog] = -> (req, res) {\n        node = Puppet::Node.new('test')\n\n        code = if first_run\n                 first_run = false\n                 ''\n               else\n                 data = CGI.unescape(req.query['facts'])\n                 facts = Puppet::Node::Facts.convert_from('json', data)\n                 node.fact_merge(facts)\n                 'notify { $facts[\"agent_spec_role\"]: }'\n               end\n\n        catalog = compile_to_catalog(code, node)\n        catalog.environment = 'newenv'\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: mounts) do |port|\n        Puppet[:serverport] = port\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(2)\n         .and output(a_string_matching(%r{Notice: Local environment: 'production' doesn't match server specified environment 'newenv', restarting agent run with environment 'newenv'})\n         .and matching(%r{defined 'message' as 'web'})).to_stdout\n      end\n    end\n  end\n\n  context \"ssl\" do\n    context \"bootstrapping\" do\n      before :each do\n        # reconfigure ssl to non-existent dir and files to force bootstrapping\n        dir = tmpdir('ssl')\n        Puppet[:ssldir] = dir\n        Puppet[:localcacert] = File.join(dir, 'ca.pem')\n        Puppet[:hostcrl] = File.join(dir, 'crl.pem')\n        Puppet[:hostprivkey] = File.join(dir, 'cert.pem')\n        Puppet[:hostcert] = File.join(dir, 'key.pem')\n\n        Puppet[:daemonize] = false\n        Puppet[:logdest] = 'console'\n        Puppet[:log_level] = 'info'\n      end\n\n      it \"exits if the agent is not allowed to wait\" do\n        Puppet[:waitforcert] = 0\n\n        server.start_server do |port|\n          Puppet[:serverport] = port\n          expect {\n            agent.run\n          }.to exit_with(1)\n           .and output(%r{Exiting now because the waitforcert setting is set to 0}).to_stdout\n           .and output(%r{Failed to submit the CSR, HTTP response was 404}).to_stderr\n        end\n      end\n\n      it \"exits if the maxwaitforcert time is exceeded\" do\n        Puppet[:waitforcert] = 1\n        Puppet[:maxwaitforcert] = 1\n\n        server.start_server do |port|\n          Puppet[:serverport] = port\n          expect {\n            agent.run\n          }.to exit_with(1)\n            .and output(%r{Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate \\(127.0.0.1\\). Exiting now because the maxwaitforcert timeout has been exceeded.}).to_stdout\n            .and output(%r{Failed to submit the CSR, HTTP response was 404}).to_stderr\n        end\n      end\n    end\n\n    it \"reloads the CRL between runs\" do\n      Puppet[:hostcert] = cert = tmpfile('cert')\n      Puppet[:hostprivkey] = key = tmpfile('key')\n\n      copy_fixtures(%w[127.0.0.1.pem], cert)\n      copy_fixtures(%w[127.0.0.1-key.pem], key)\n\n      revoked = cert_fixture('revoked.pem')\n      revoked_key = key_fixture('revoked-key.pem')\n\n      mounts = {}\n      mounts[:catalog] = -> (req, res) {\n        catalog = compile_to_catalog(<<~MANIFEST, node)\n          file { '#{cert}':\n            ensure => file,\n            content => '#{revoked}'\n          }\n          file { '#{key}':\n            ensure => file,\n            content => '#{revoked_key}'\n          }\n        MANIFEST\n\n        res.body = formatter.render(catalog)\n        res['Content-Type'] = formatter.mime\n      }\n\n      server.start_server(mounts: mounts) do |port|\n        Puppet[:serverport] = port\n        Puppet[:daemonize] = false\n        Puppet[:runinterval] = 1\n        Puppet[:waitforcert] = 1\n        Puppet[:maxwaitforcert] = 1\n\n        # simulate two runs of the agent, then return so we don't infinite loop\n        allow_any_instance_of(Puppet::Daemon).to receive(:run_event_loop) do |instance|\n          instance.agent.run(splay: false)\n          instance.agent.run(splay: false)\n        end\n\n        agent.command_line.args << '--verbose'\n        expect {\n          agent.run\n        }.to exit_with(1)\n         .and output(%r{Exiting now because the maxwaitforcert timeout has been exceeded}).to_stdout\n         .and output(%r{Certificate 'CN=revoked' is revoked}).to_stderr\n      end\n    end\n\n    it \"refreshes the CA and CRL\" do\n      now = Time.now\n      yesterday = now - (60 * 60 * 24)\n      Puppet::FileSystem.touch(Puppet[:localcacert], mtime: yesterday)\n      Puppet::FileSystem.touch(Puppet[:hostcrl], mtime: yesterday)\n\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        Puppet[:ca_refresh_interval] = 1\n\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(/Info: Refreshed CA certificate: /).to_stdout\n      end\n\n      # If the CA is updated, then the CRL must be updated too\n      expect(Puppet::FileSystem.stat(Puppet[:localcacert]).mtime).to be >= now\n      expect(Puppet::FileSystem.stat(Puppet[:hostcrl]).mtime).to be >= now\n    end\n\n    it \"refreshes only the CRL\" do\n      now = Time.now\n      tomorrow = now + (60 * 60 * 24)\n      Puppet::FileSystem.touch(Puppet[:localcacert], mtime: tomorrow)\n\n      yesterday = now - (60 * 60 * 24)\n      Puppet::FileSystem.touch(Puppet[:hostcrl], mtime: yesterday)\n\n      server.start_server do |port|\n        Puppet[:serverport] = port\n        Puppet[:crl_refresh_interval] = 1\n\n        expect {\n          agent.command_line.args << '--test'\n          agent.run\n        }.to exit_with(0)\n         .and output(/Info: Refreshed CRL: /).to_stdout\n      end\n\n      expect(Puppet::FileSystem.stat(Puppet[:hostcrl]).mtime).to be >= now\n    end\n  end\n\n  context \"legacy facts\" do\n    let(:mod_dir) { tmpdir('module_dir') }\n    let(:custom_dir) { File.join(mod_dir, 'lib') }\n    let(:external_dir) { File.join(mod_dir, 'facts.d') }\n\n    before(:each) do\n      # don't stub facter behavior, since we're relying on\n      # `Facter.resolve` to omit legacy facts\n      Puppet::Node::Facts.indirection.terminus_class = :facter\n\n      # need to use `Facter::OptionStore.reset` in order to reset\n      # Facter::OptionStore since Facter.clear does not reset it.\n      # Specifically, Options[:show_legacy] needs to be reset to\n      # true for legacy facts test below\n      Facter.clear\n      Facter::OptionStore.reset\n\n      facter_dir = File.join(custom_dir, 'facter')\n      FileUtils.mkdir_p(facter_dir)\n      File.write(File.join(facter_dir, 'custom.rb'), <<~END)\n        Facter.add(:custom) { setcode { 'a custom value' } }\n      END\n\n      FileUtils.mkdir_p(external_dir)\n      File.write(File.join(external_dir, 'external.json'), <<~END)\n        {\"external\":\"an external value\"}\n      END\n\n      # avoid pluginsync'ing contents\n      FileUtils.mkdir_p(Puppet[:vardir])\n      FileUtils.cp_r(custom_dir, Puppet[:vardir])\n      FileUtils.cp_r(external_dir, Puppet[:vardir])\n    end\n\n    def mounts\n      {\n        # the server needs to provide metadata that matches what the agent has\n        # so that the agent doesn't delete them during pluginsync\n        file_metadatas: -> (req, res) {\n          path = case req.path\n                 when /pluginfacts/\n                   external_dir\n                 when /plugins/\n                   custom_dir\n                 else\n                   raise \"Unknown mount #{req.path}\"\n                 end\n          request = Puppet::FileServing::Metadata.indirection.request(\n            :search, path, nil, recurse: true\n          )\n          data = Puppet::FileServing::Metadata.indirection.terminus(:file).search(request)\n          res.body = formatter.render(data)\n          res['Content-Type'] = formatter.mime\n        },\n        catalog: -> (req, res) {\n          data = CGI.unescape(req.query['facts'])\n          facts = Puppet::Node::Facts.convert_from('json', data)\n          node.fact_merge(facts)\n\n          catalog = compile_to_catalog(<<~MANIFEST, node)\n            notify { \"legacy $osfamily\": }\n            notify { \"custom ${facts['custom']}\": }\n            notify { \"external ${facts['external']}\": }\n          MANIFEST\n\n          res.body = formatter.render(catalog)\n          res['Content-Type'] = formatter.mime\n        }\n      }\n    end\n\n    it \"can include legacy facts\" do\n      server.start_server(mounts: mounts) do |port|\n        Puppet[:serverport] = port\n        Puppet[:include_legacy_facts] = true\n\n        agent.command_line.args << '--test'\n        expect {\n          agent.run\n        }.to exit_with(2)\n          .and output(\n            match(/defined 'message' as 'legacy [A-Za-z]+'/)\n              .and match(/defined 'message' as 'custom a custom value'/)\n              .and match(/defined 'message' as 'external an external value'/)\n          ).to_stdout\n      end\n    end\n\n    it \"excludes legacy facts by default\" do\n      server.start_server(mounts: mounts) do |port|\n        Puppet[:serverport] = port\n\n        agent.command_line.args << '--test'\n        expect {\n          agent.run\n        }.to exit_with(1)\n          .and output(/Info: Loading facts/).to_stdout\n          .and output(\n            match(/Error: Evaluation Error: Unknown variable: 'osfamily'/)\n              .and match(/Error: Could not retrieve catalog from remote server: Error 500 on SERVER:/)\n          ).to_stderr\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/apply_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/https'\n\ndescribe \"apply\", unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  let(:apply) { Puppet::Application[:apply] }\n\n  before :each do\n    Puppet[:reports] = \"none\"\n    # Let exceptions be raised instead of exiting\n    allow_any_instance_of(Puppet::Application).to receive(:exit_on_fail).and_yield\n  end\n\n  describe \"when applying provided catalogs\" do\n    it \"can apply catalogs provided in a file in json\" do\n      file_to_create = tmpfile(\"json_catalog\")\n      catalog = Puppet::Resource::Catalog.new('mine', Puppet.lookup(:environments).get(Puppet[:environment]))\n      resource = Puppet::Resource.new(:file, file_to_create, :parameters => {:content => \"my stuff\"})\n      catalog.add_resource resource\n\n      apply.command_line.args = ['--catalog', file_containing(\"manifest\", catalog.to_json)]\n      expect {\n        apply.run\n      }.to output(/ensure: defined content as/).to_stdout\n\n      expect(Puppet::FileSystem.exist?(file_to_create)).to be_truthy\n      expect(File.read(file_to_create)).to eq(\"my stuff\")\n    end\n\n    context 'and pcore types are available' do\n      let(:envdir) { my_fixture('environments') }\n      let(:env_name) { 'spec' }\n\n      before(:each) do\n        Puppet[:environmentpath] = envdir\n        Puppet[:environment] = env_name\n      end\n\n      it 'does not load the pcore type' do\n        apply = Puppet::Application[:apply]\n        apply.command_line.args = [ '-e', \"Applytest { message => 'the default'} applytest { 'applytest was here': }\" ]\n\n        expect {\n          apply.run\n        }.to exit_with(0)\n         .and output(a_string_matching(\n           /the Puppet::Type says hello/\n         ).and matching(\n           /applytest was here/\n         )).to_stdout\n      end\n    end\n\n    context 'from environment with a pcore defined resource type' do\n      include PuppetSpec::Compiler\n\n      let(:envdir) { my_fixture('environments') }\n      let(:env_name) { 'spec' }\n      let(:environments) { Puppet::Environments::Directories.new(envdir, []) }\n      let(:env) { Puppet::Node::Environment.create(:'spec', [File.join(envdir, 'spec', 'modules')]) }\n      let(:node) { Puppet::Node.new('test', :environment => env) }\n\n      around(:each) do |example|\n        Puppet::Type.rmtype(:applytest)\n        Puppet[:environment] = env_name\n        Puppet.override(:environments => environments, :current_environment => env) do\n          example.run\n        end\n      end\n\n      it 'does not load the pcore type' do\n        catalog = compile_to_catalog('applytest { \"applytest was here\":}', node)\n        apply.command_line.args = ['--catalog', file_containing('manifest', catalog.to_json)]\n\n        Puppet[:environmentpath] = envdir\n        expect_any_instance_of(Puppet::Pops::Loader::Runtime3TypeLoader).not_to receive(:find)\n        expect {\n          apply.run\n        }.to output(/the Puppet::Type says hello.*applytest was here/m).to_stdout\n      end\n\n      # Test just to verify that the Pcore Resource Type and not the Ruby one is produced when the catalog is produced\n      it 'loads pcore resource type instead of ruby resource type during compile' do\n        Puppet[:code] = 'applytest { \"applytest was here\": }'\n        compiler = Puppet::Parser::Compiler.new(node)\n        tn = Puppet::Pops::Loader::TypedName.new(:resource_type_pp, 'applytest')\n        rt = Puppet::Pops::Resource::ResourceTypeImpl.new('applytest', [Puppet::Pops::Resource::Param.new(String, 'message')], [Puppet::Pops::Resource::Param.new(String, 'name', true)])\n\n        expect(compiler.loaders.runtime3_type_loader.instance_variable_get(:@resource_3x_loader)).to receive(:set_entry).once.with(tn, rt, instance_of(String))\n          .and_return(Puppet::Pops::Loader::Loader::NamedEntry.new(tn, rt, nil))\n        expect {\n          compiler.compile\n        }.not_to output(/the Puppet::Type says hello/).to_stdout\n      end\n\n      it \"does not fail when pcore type is loaded twice\" do\n        Puppet[:code] = 'applytest { xyz: alias => aptest }; Resource[applytest]'\n        compiler = Puppet::Parser::Compiler.new(node)\n        expect { compiler.compile }.not_to raise_error\n      end\n\n      it \"does not load the ruby type when using function 'defined()' on a loaded resource that is missing from the catalog\" do\n        # Ensure that the Resource[applytest,foo] is loaded'\n        eval_and_collect_notices('applytest { xyz: }', node)\n\n        # Ensure that:\n        # a) The catalog contains aliases (using a name for the abc resource ensures this)\n        # b) That Resource[applytest,xyz] is not defined in the catalog (although it's loaded)\n        # c) That this doesn't trigger a load of the Puppet::Type\n        notices = eval_and_collect_notices('applytest { abc: name => some_alias }; notice(defined(Resource[applytest,xyz]))', node)\n        expect(notices).to include('false')\n        expect(notices).not_to include('the Puppet::Type says hello')\n      end\n\n      it 'does not load the ruby type when when referenced from collector during compile' do\n        notices = eval_and_collect_notices(\"@applytest { 'applytest was here': }\\nApplytest<| title == 'applytest was here' |>\", node)\n        expect(notices).not_to include('the Puppet::Type says hello')\n      end\n\n      it 'does not load the ruby type when when referenced from exported collector during compile' do\n        notices = eval_and_collect_notices(\"@@applytest { 'applytest was here': }\\nApplytest<<| |>>\", node)\n        expect(notices).not_to include('the Puppet::Type says hello')\n      end\n    end\n  end\n\n  context 'from environment with pcore object types' do\n    include PuppetSpec::Compiler\n\n    let!(:envdir) { Puppet[:environmentpath] }\n    let(:env_name) { 'spec' }\n    let(:dir_structure) {\n      {\n        'environment.conf' => <<-CONF,\n          rich_data = true\n        CONF\n        'modules' => {\n          'mod' => {\n            'types' => {\n              'streetaddress.pp' => <<-PUPPET,\n                type Mod::StreetAddress = Object[{\n                  attributes => {\n                    'street' => String,\n                    'zipcode' => String,\n                    'city' => String,\n                  }\n                }]\n              PUPPET\n              'address.pp' => <<-PUPPET,\n                type Mod::Address = Object[{\n                  parent => Mod::StreetAddress,\n                  attributes => {\n                    'state' => String\n                  }\n                }]\n              PUPPET\n              'contact.pp' => <<-PUPPET,\n                type Mod::Contact = Object[{\n                  attributes => {\n                    'address' => Mod::Address,\n                    'email' => String\n                  }\n                }]\n              PUPPET\n            },\n            'manifests' => {\n              'init.pp' => <<-PUPPET,\n                define mod::person(Mod::Contact $contact) {\n                  notify { $title: }\n                  notify { $contact.address.street: }\n                  notify { $contact.address.zipcode: }\n                  notify { $contact.address.city: }\n                  notify { $contact.address.state: }\n                }\n\n                class mod {\n                  mod::person { 'Test Person':\n                    contact => Mod::Contact(\n                      Mod::Address('The Street 23', '12345', 'Some City', 'A State'),\n                      'test@example.com')\n                  }\n                }\n              PUPPET\n            }\n          }\n        }\n      }\n    }\n\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(envdir, env_name, 'modules')]) }\n    let(:node) { Puppet::Node.new('test', :environment => env) }\n\n    before(:each) do\n      dir_contained_in(envdir, env_name => dir_structure)\n      PuppetSpec::Files.record_tmp(File.join(envdir, env_name))\n    end\n\n    it 'can compile the catalog' do\n      compile_to_catalog('include mod', node)\n    end\n\n    it 'can apply the catalog with no warning' do\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        catalog = compile_to_catalog('include mod', node)\n        Puppet[:environment] = env_name\n        handler = Puppet::Network::FormatHandler.format(:rich_data_json)\n        apply.command_line.args = ['--catalog', file_containing('manifest', handler.render(catalog))]\n        expect {\n          apply.run\n        }.to output(%r{Notify\\[The Street 23\\]/message: defined 'message' as 'The Street 23'}).to_stdout\n      end\n      # expected to have no warnings\n      expect(logs.select { |log| log.level == :warning }.map { |log| log.message }).to be_empty\n    end\n  end\n\n  it \"raises if the environment directory does not exist\" do\n    manifest = file_containing(\"manifest.pp\", \"notice('it was applied')\")\n    apply.command_line.args = [manifest]\n\n    special = Puppet::Node::Environment.create(:special, [])\n    Puppet.override(:current_environment => special) do\n      Puppet[:environment] = 'special'\n      expect {\n        apply.run\n      }.to raise_error(Puppet::Environments::EnvironmentNotFound,\n                       /Could not find a directory environment named 'special' anywhere in the path/)\n    end\n  end\n\n  it \"adds environment to the $server_facts variable\" do\n    manifest = file_containing(\"manifest.pp\", \"notice(\\\"$server_facts\\\")\")\n    apply.command_line.args = [manifest]\n\n    expect {\n      apply.run\n    }.to exit_with(0)\n     .and output(/{environment => production}/).to_stdout\n  end\n\n  it \"applies a given file even when an ENC is configured\", :unless => Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n    manifest = file_containing(\"manifest.pp\", \"notice('specific manifest applied')\")\n    enc = script_containing('enc_script',\n      :windows => '@echo classes: []' + \"\\n\" + '@echo environment: special',\n      :posix   => '#!/bin/sh' + \"\\n\" + 'echo \"classes: []\"' + \"\\n\" + 'echo \"environment: special\"')\n\n    Dir.mkdir(File.join(Puppet[:environmentpath], \"special\"), 0755)\n\n    special = Puppet::Node::Environment.create(:special, [])\n    Puppet.override(:current_environment => special) do\n      Puppet[:environment] = 'special'\n      Puppet[:node_terminus] = 'exec'\n      Puppet[:external_nodes] = enc\n      apply.command_line.args = [manifest]\n      expect {\n        apply.run\n      }.to exit_with(0)\n       .and output(/Notice: Scope\\(Class\\[main\\]\\): specific manifest applied/).to_stdout\n    end\n  end\n\n  context \"handles errors\" do\n    it \"logs compile errors once\" do\n      apply.command_line.args = ['-e', '08']\n      expect {\n        apply.run\n      }.to exit_with(1)\n       .and output(/Not a valid octal number/).to_stderr\n    end\n\n    it \"logs compile post processing errors once\" do\n      path = File.expand_path('/tmp/content_file_test.Q634Dlmtime')\n      apply.command_line.args = ['-e', \"file { '#{path}':\n        content => 'This is the test file content',\n        ensure => present,\n        checksum => mtime\n      }\"]\n\n      expect {\n        apply.run\n      }.to exit_with(1)\n       .and output(/Compiled catalog/).to_stdout\n       .and output(/You cannot specify content when using checksum/).to_stderr\n    end\n  end\n\n  context \"with a module in an environment\" do\n    let(:envdir) { tmpdir('environments') }\n    let(:modulepath) { File.join(envdir, 'spec', 'modules') }\n    let(:execute) { 'include amod' }\n\n    before(:each) do\n      dir_contained_in(envdir, {\n        \"spec\" => {\n          \"modules\" => {\n            \"amod\" => {\n              \"manifests\" => {\n                \"init.pp\" => \"class amod{ notice('amod class included') }\"\n              }\n            }\n          }\n        }\n      })\n\n      Puppet[:environmentpath] = envdir\n    end\n\n    context \"given a modulepath\" do\n      let(:args) { ['-e', execute] }\n\n      before :each do\n        Puppet[:modulepath] = modulepath\n\n        apply.command_line.args = args\n      end\n\n      it \"looks in modulepath even when the default directory environment exists\" do\n        expect {\n          apply.run\n        }.to exit_with(0)\n         .and output(/amod class included/).to_stdout\n      end\n\n      it \"looks in modulepath even when given a specific directory --environment\" do\n        apply.command_line.args = args << '--environment' << 'production'\n\n        expect {\n          apply.run\n        }.to exit_with(0)\n         .and output(/amod class included/).to_stdout\n      end\n\n      it \"looks in modulepath when given multiple paths in modulepath\" do\n        Puppet[:modulepath] = [tmpdir('notmodulepath'), modulepath].join(File::PATH_SEPARATOR)\n\n        expect {\n          apply.run\n        }.to exit_with(0)\n         .and output(/amod class included/).to_stdout\n      end\n    end\n\n    context \"with an ENC\" do\n      let(:enc) do\n        script_containing('enc_script',\n          :windows => '@echo environment: spec',\n          :posix   => '#!/bin/sh' + \"\\n\" + 'echo \"environment: spec\"')\n      end\n\n      before :each do\n        Puppet[:node_terminus] = 'exec'\n        Puppet[:external_nodes] = enc\n      end\n\n      it \"should use the environment that the ENC mandates\" do\n        apply.command_line.args = ['-e', execute]\n\n        expect {\n          apply.run\n       }.to exit_with(0)\n        .and output(a_string_matching(/amod class included/)\n        .and matching(/Compiled catalog for .* in environment spec/)).to_stdout\n      end\n\n      it \"should prefer the ENC environment over the configured one and emit a warning\" do\n        apply.command_line.args = ['-e', execute, '--environment', 'production']\n\n        expect {\n          apply.run\n        }.to exit_with(0)\n         .and output(a_string_matching('amod class included')\n         .and matching(/doesn't match server specified environment/)).to_stdout\n      end\n    end\n  end\n\n  context 'when applying from file' do\n    include PuppetSpec::Compiler\n\n    let(:env_dir) { tmpdir('environments') }\n    let(:execute) { 'include amod' }\n    let(:rich_data) { false }\n    let(:env_name) { 'spec' }\n    let(:populated_env_dir) do\n      dir_contained_in(env_dir, {\n        env_name => {\n          'modules' => {\n            'amod' => {\n              'manifests' => {\n                'init.pp' => <<-EOF\nclass amod {\n  notify { rx: message => /[Rr]eg[Ee]xp/ }\n  notify { bin: message => Binary('w5ZzdGVuIG1lZCByw7ZzdGVuCg==') }\n  notify { ver: message  => SemVer('2.3.1') }\n  notify { vrange: message => SemVerRange('>=2.3.0') }\n  notify { tspan: message => Timespan(3600) }\n  notify { tstamp: message => Timestamp('2012-03-04T18:15:11.001') }\n}\n\nclass amod::bad_type {\n  notify { bogus: message => amod::bogus() }\n}\n              EOF\n              },\n              'lib' => {\n                'puppet' => {\n                  'functions' => {\n                    'amod' => {\n                      'bogus.rb' => <<-RUBY\n                        # Function that leaks an object that is not recognized in the catalog\n                        Puppet::Functions.create_function(:'amod::bogus') do\n                          def bogus()\n                            Time.new(2016, 10, 6, 23, 51, 14, '+02:00')\n                          end\n                        end\n                      RUBY\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      })\n      env_dir\n    end\n\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'spec', 'modules')]) }\n    let(:node) { Puppet::Node.new('test', :environment => env) }\n\n    before(:each) do\n      Puppet[:rich_data] = rich_data\n      Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))\n    end\n\n    after(:each) do\n      Puppet.pop_context()\n    end\n\n    context 'and the file is not serialized with rich_data' do\n      # do not want to stub out behavior in tests\n      before :each do\n        Puppet[:strict] = :warning\n      end\n\n      around :each do |test|\n        Puppet.override(rich_data: false) do\n          test.run\n        end\n      end\n\n      it 'will notify a string that is the result of Regexp#inspect (from Runtime3xConverter)' do\n        catalog = compile_to_catalog(execute, node)\n        apply.command_line.args = ['--catalog', file_containing('manifest', catalog.to_json)]\n        expect(apply).to receive(:apply_catalog) do |cat|\n          expect(cat.resource(:notify, 'rx')['message']).to be_a(String)\n          expect(cat.resource(:notify, 'bin')['message']).to be_a(String)\n          expect(cat.resource(:notify, 'ver')['message']).to be_a(String)\n          expect(cat.resource(:notify, 'vrange')['message']).to be_a(String)\n          expect(cat.resource(:notify, 'tspan')['message']).to be_a(String)\n          expect(cat.resource(:notify, 'tstamp')['message']).to be_a(String)\n        end\n\n        apply.run\n      end\n\n      it 'will notify a string that is the result of to_s on uknown data types' do\n        json = compile_to_catalog('include amod::bad_type', node).to_json\n        apply.command_line.args = ['--catalog', file_containing('manifest', json)]\n        expect(apply).to receive(:apply_catalog) do |catalog|\n          expect(catalog.resource(:notify, 'bogus')['message']).to be_a(String)\n        end\n\n        apply.run\n      end\n\n      it 'will log a warning that a value of unknown type is converted into a string' do\n        logs = []\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          compile_to_catalog('include amod::bad_type', node).to_json\n        end\n        logs = logs.select { |log| log.level == :warning }.map { |log| log.message }\n        expect(logs.empty?).to be_falsey\n        expect(logs[0]).to eql(\"Notify[bogus]['message'] contains a Time value. It will be converted to the String '2016-10-06 23:51:14 +0200'\")\n      end\n    end\n\n    context 'and the file is serialized with rich_data' do\n      it 'will notify a regexp using Regexp#to_s' do\n        catalog = compile_to_catalog(execute, node)\n        serialized_catalog = Puppet.override(rich_data: true) do\n          catalog.to_json\n        end\n        apply.command_line.args = ['--catalog', file_containing('manifest', serialized_catalog)]\n        expect(apply).to receive(:apply_catalog) do |cat|\n          expect(cat.resource(:notify, 'rx')['message']).to be_a(Regexp)\n          # The resource return in this expect is a String, but since it was a Binary type that\n          # was converted with `resolve_and_replace`, we want to make sure that the encoding\n          # of that string is the expected ASCII-8BIT.\n          expect(cat.resource(:notify, 'bin')['message'].encoding.inspect).to include('ASCII-8BIT')\n          expect(cat.resource(:notify, 'ver')['message']).to be_a(SemanticPuppet::Version)\n          expect(cat.resource(:notify, 'vrange')['message']).to be_a(SemanticPuppet::VersionRange)\n          expect(cat.resource(:notify, 'tspan')['message']).to be_a(Puppet::Pops::Time::Timespan)\n          expect(cat.resource(:notify, 'tstamp')['message']).to be_a(Puppet::Pops::Time::Timestamp)\n        end\n\n        apply.run\n      end\n    end\n  end\n\n  context 'puppet file sources' do\n    let(:env_name) { 'dev' }\n    let(:env_dir) { File.join(Puppet[:environmentpath], env_name) }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(env_dir, 'modules')]) }\n    let(:node) { Puppet::Node.new(Puppet[:certname], environment: environment) }\n\n    before :each do\n      Puppet[:environment] = env_name\n      Puppet::FileSystem.mkpath(env_dir)\n    end\n\n    it \"recursively copies a directory from a module\" do\n      dir = File.join(env.full_modulepath, 'amod', 'files', 'dir1', 'dir2')\n      Puppet::FileSystem.mkpath(dir)\n      File.write(File.join(dir, 'file'), 'content from the module')\n\n      base_dir = tmpdir('apply_spec_base')\n      manifest = file_containing(\"manifest.pp\", <<-MANIFEST)\n        file { \"#{base_dir}/dir1\":\n          ensure  => file,\n          source  => \"puppet:///modules/amod/dir1\",\n          recurse => true,\n        }\n      MANIFEST\n\n      expect {\n        apply.command_line.args << manifest\n        apply.run\n      }.to exit_with(0)\n       .and output(a_string_matching(\n         /dir1\\]\\/ensure: created/\n      ).and matching(\n         /dir1\\/dir2\\]\\/ensure: created/\n      ).and matching(\n         /dir1\\/dir2\\/file\\]\\/ensure: defined content as '{sha256}b37c1d77e09471b3139b2cdfee449fd8ba72ebf7634d52023aff0c0cd088cf1b'/\n      )).to_stdout\n\n      dest_file = File.join(base_dir, 'dir1', 'dir2', 'file')\n      expect(File.read(dest_file)).to eq(\"content from the module\")\n    end\n  end\n\n  context 'http file sources' do\n    include_context 'https client'\n\n    it \"requires the caller to URL encode special characters in the request path and query\" do\n      Puppet[:server] = '127.0.0.1'\n      request = nil\n\n      response_proc = -> (req, res) {\n        request = req\n\n        res['Content-Type'] = 'text/plain'\n        res.body = \"from the server\"\n      }\n\n      https = PuppetSpec::HTTPSServer.new\n      https.start_server(response_proc: response_proc) do |https_port|\n        dest = tmpfile('http_file_source')\n\n        # spaces in path are encoded as %20 and '[' in query is encoded as %5B,\n        # but ':', '=', '-' are not encoded\n        manifest = file_containing(\"manifest.pp\", <<~MANIFEST)\n          file { \"#{dest}\":\n            ensure  => file,\n            source  => \"https://#{Puppet[:server]}:#{https_port}/path%20to%20file?x=b%5Bc&sv=2019-02-02&st=2020-07-28T20:18:53Z&se=2020-07-28T21:03:00Z&sr=b&sp=r&sig=JaZhcqxT4akJcOwUdUGrQB2m1geUoh89iL8WMag8a8c=\",\n          }\n        MANIFEST\n\n        expect {\n          apply.command_line.args << manifest\n          apply.run\n        }.to exit_with(0)\n         .and output(%r{Main/File\\[#{dest}\\]/ensure: defined content as}).to_stdout\n\n        expect(request.path).to eq('/path to file')\n        expect(request.query).to include('x' => 'b[c')\n        expect(request.query).to include('sig' => 'JaZhcqxT4akJcOwUdUGrQB2m1geUoh89iL8WMag8a8c=')\n      end\n    end\n  end\n\n  context 'http report processor' do\n    include_context 'https client'\n\n    before :each do\n      Puppet[:reports] = 'http'\n    end\n\n    let(:unknown_server) do\n      unknown_ca_cert = cert_fixture('unknown-ca.pem')\n      PuppetSpec::HTTPSServer.new(\n        ca_cert: unknown_ca_cert,\n        server_cert: cert_fixture('unknown-127.0.0.1.pem'),\n        server_key: key_fixture('unknown-127.0.0.1-key.pem')\n      )\n    end\n\n    it 'submits a report via reporturl' do\n      report = nil\n\n      response_proc = -> (req, res) {\n        report = Puppet::Transaction::Report.convert_from(:yaml, req.body)\n      }\n\n      https = PuppetSpec::HTTPSServer.new\n      https.start_server(response_proc: response_proc) do |https_port|\n        Puppet[:reporturl] = \"https://127.0.0.1:#{https_port}/reports/upload\"\n\n        expect {\n          apply.command_line.args = ['-e', 'notify { \"hi\": }']\n          apply.run\n        }.to exit_with(0)\n         .and output(/Applied catalog/).to_stdout\n\n        expect(report).to be_a(Puppet::Transaction::Report)\n        expect(report.resource_statuses['Notify[hi]']).to be_a(Puppet::Resource::Status)\n      end\n    end\n\n    it 'rejects an HTTPS report server whose root cert is not the puppet CA' do\n      unknown_server.start_server do |https_port|\n        Puppet[:reporturl] = \"https://127.0.0.1:#{https_port}/reports/upload\"\n\n        # processing the report happens after the transaction is finished,\n        # so we expect exit code 0, with a later failure on stderr\n        expect {\n          apply.command_line.args = ['-e', 'notify { \"hi\": }']\n          apply.run\n        }.to exit_with(0)\n         .and output(/Applied catalog/).to_stdout\n         .and output(/Report processor failed: certificate verify failed \\[self.signed certificate in certificate chain for CN=Unknown CA\\]/).to_stderr\n      end\n    end\n\n    it 'accepts an HTTPS report servers whose cert is in the system CA store' do\n      Puppet[:report_include_system_store] = true\n      report = nil\n\n      response_proc = -> (req, res) {\n        report = Puppet::Transaction::Report.convert_from(:yaml, req.body)\n      }\n\n      # create a temp cacert bundle\n      ssl_file = tmpfile('systemstore')\n      File.write(ssl_file, unknown_server.ca_cert.to_pem)\n\n      unknown_server.start_server(response_proc: response_proc) do |https_port|\n        Puppet[:reporturl] = \"https://127.0.0.1:#{https_port}/reports/upload\"\n\n        # override path to system cacert bundle, this must be done before\n        # the SSLContext is created and the call to X509::Store.set_default_paths\n        Puppet::Util.withenv(\"SSL_CERT_FILE\" => ssl_file) do\n          expect {\n            apply.command_line.args = ['-e', 'notify { \"hi\": }']\n            apply.run\n          }.to exit_with(0)\n           .and output(/Applied catalog/).to_stdout\n        end\n\n        expect(report).to be_a(Puppet::Transaction::Report)\n        expect(report.resource_statuses['Notify[hi]']).to be_a(Puppet::Resource::Status)\n      end\n    end\n  end\n\n  context 'rich data' do\n    let(:deferred_file) { tmpfile('deferred') }\n    let(:deferred_manifest) do <<~END\n      file { '#{deferred_file}':\n        ensure => file,\n        content => '123',\n      } ->\n      notify { 'deferred':\n        message => Deferred('binary_file', ['#{deferred_file}'])\n      }\n      END\n    end\n\n    it \"calls a deferred 4x function\" do\n      apply.command_line.args = ['-e', 'notify { \"deferred3x\": message => Deferred(\"join\", [[1,2,3], \":\"]) }']\n\n      expect {\n        apply.run\n      }.to exit_with(0) # for some reason apply returns 0 instead of 2\n       .and output(%r{Notice: /Stage\\[main\\]/Main/Notify\\[deferred3x\\]/message: defined 'message' as '1:2:3'}).to_stdout\n    end\n\n    it \"calls a deferred 3x function\" do\n      apply.command_line.args = ['-e', 'notify { \"deferred4x\": message => Deferred(\"sprintf\", [\"%s\", \"I am deferred\"]) }']\n      expect {\n        apply.run\n      }.to exit_with(0) # for some reason apply returns 0 instead of 2\n       .and output(%r{Notice: /Stage\\[main\\]/Main/Notify\\[deferred4x\\]/message: defined 'message' as 'I am deferred'}).to_stdout\n    end\n\n    it \"fails to apply a deferred function with an unsatisfied prerequisite\" do\n      Puppet[:preprocess_deferred] = true\n\n      apply.command_line.args = ['-e', deferred_manifest]\n      expect {\n        apply.run\n      }.to exit_with(1) # for some reason apply returns 0 instead of 2\n       .and output(/Compiled catalog/).to_stdout\n       .and output(%r{The given file '#{deferred_file}' does not exist}).to_stderr\n    end\n\n    it \"applies a deferred function and its prerequisite in the same run\" do\n      apply.command_line.args = ['-e', deferred_manifest]\n      expect {\n        apply.run\n      }.to exit_with(0) # for some reason apply returns 0 instead of 2\n        .and output(%r{defined 'message' as Binary\\(\"MTIz\"\\)}).to_stdout\n    end\n\n    it \"validates the deferred resource before applying any resources\" do\n      Puppet[:preprocess_deferred] = true\n      undeferred_file = tmpfile('undeferred')\n\n      manifest = <<~END\n      file { '#{undeferred_file}':\n        ensure => file,\n      }\n      file { '#{deferred_file}':\n          ensure => file,\n          content => Deferred('inline_epp', ['<%= 42 %>']),\n          source => 'http://example.com/content',\n      }\n      END\n      apply.command_line.args = ['-e', manifest]\n      expect {\n        apply.run\n      }.to exit_with(1)\n        .and output(/Compiled catalog/).to_stdout\n        .and output(/Validation of File.* failed: You cannot specify more than one of content, source, target/).to_stderr\n\n      # validation happens before all resources are applied, so this shouldn't exist\n      expect(File).to_not be_exist(undeferred_file)\n    end\n\n    it \"evaluates resources before validating the deferred resource\" do\n      manifest = <<~END\n        notify { 'runs before file': } ->\n        file { '#{deferred_file}':\n          ensure => file,\n          content => Deferred('inline_epp', ['<%= 42 %>']),\n          source => 'http://example.com/content',\n      }\n      END\n      apply.command_line.args = ['-e', manifest]\n      expect {\n        apply.run\n      }.to exit_with(1)\n        .and output(/Notify\\[runs before file\\]/).to_stdout\n        .and output(/Validation of File.* failed: You cannot specify more than one of content, source, target/).to_stderr\n    end\n\n    it \"applies deferred sensitive file content\" do\n      manifest = <<~END\n      file { '#{deferred_file}':\n        ensure => file,\n        content => Deferred('new', [Sensitive, \"hello\\n\"])\n      }\n      END\n      apply.command_line.args = ['-e', manifest]\n      expect {\n        apply.run\n      }.to exit_with(0)\n        .and output(/ensure: changed \\[redacted\\] to \\[redacted\\]/).to_stdout\n    end\n\n    it \"applies nested deferred sensitive file content\" do\n      manifest = <<~END\n      $vars = {'token' => Deferred('new', [Sensitive, \"hello\"])}\n      file { '#{deferred_file}':\n        ensure => file,\n        content => Deferred('inline_epp', ['<%= $token %>', $vars])\n      }\n      END\n      apply.command_line.args = ['-e', manifest]\n      expect {\n        apply.run\n      }.to exit_with(0)\n        .and output(/ensure: changed \\[redacted\\] to \\[redacted\\]/).to_stdout\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/doc_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/application/doc'\n\ndescribe Puppet::Application::Doc do\n  include PuppetSpec::Files\n\n  let(:app) { Puppet::Application[:doc] }\n\n  it 'lists references' do\n    app.command_line.args = ['-l']\n    expect {\n      app.run\n    }.to exit_with(0)\n     .and output(/configuration - A reference for all settings/).to_stdout\n  end\n\n  {\n    'configuration' => /# Configuration Reference/,\n    'function'      => /# Function Reference/,\n    'indirection'   => /# Indirection Reference/,\n    'metaparameter' => /# Metaparameter Reference/,\n    'providers'     => /# Provider Suitability Report/,\n    'report'        => /# Report Reference/,\n    'type'          => /# Type Reference/\n  }.each_pair do |type, expected|\n    it \"generates #{type} reference\" do\n      app.command_line.args = ['-r', type]\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(expected).to_stdout\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/filebucket_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet_spec/puppetserver'\nrequire 'puppet_spec/files'\n\ndescribe \"puppet filebucket\", unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n  include_context \"https client\"\n\n  let(:server) { PuppetSpec::Puppetserver.new }\n  let(:filebucket) { Puppet::Application[:filebucket] }\n  let(:backup_file) { tmpfile('backup_file') }\n  let(:text) { 'some random text' }\n  let(:sha256) { Digest::SHA256.file(backup_file).to_s }\n\n  before :each do\n    Puppet[:log_level] = 'debug'\n    File.binwrite(backup_file, text)\n  end\n\n  after :each do\n    # mute debug messages generated during `after :each` blocks\n    Puppet::Util::Log.close_all\n  end\n\n  it \"backs up to and restores from the local filebucket\" do\n    filebucket.command_line.args = ['backup', backup_file, '--local']\n    expect {\n      filebucket.run\n    }.to output(/: #{sha256}/).to_stdout\n\n    dest = tmpfile('file_bucket_restore')\n    filebucket.command_line.args = ['restore', dest, sha256, '--local']\n    expect {\n      filebucket.run\n    }.to output(/FileBucket read #{sha256}/).to_stdout\n\n    expect(FileUtils.compare_file(backup_file, dest)).to eq(true)\n  end\n\n  it \"backs up text files to the filebucket server\" do\n    server.start_server do |port|\n      Puppet[:serverport] = port\n      expect {\n        filebucket.command_line.args = ['backup', backup_file]\n        filebucket.run\n      }.to output(a_string_matching(\n        %r{Debug: HTTP HEAD https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file\\/sha256\\/#{sha256}\\/#{File.realpath(backup_file)}\\?environment\\=production returned 404 Not Found}\n      ).and matching(\n        %r{Debug: HTTP PUT https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file\\/sha256\\/#{sha256}\\/#{File.realpath(backup_file)}\\?environment\\=production returned 200 OK}\n      ).and matching(\n        %r{#{backup_file}: #{sha256}}\n      )).to_stdout\n\n      expect(File.binread(File.join(server.upload_directory, 'filebucket'))).to eq(text)\n    end\n  end\n\n  it \"backs up binary files to the filebucket server\" do\n    binary = \"\\xD1\\xF2\\r\\n\\x81NuSc\\x00\".force_encoding(Encoding::ASCII_8BIT)\n    File.binwrite(backup_file, binary)\n\n    server.start_server do |port|\n      Puppet[:serverport] = port\n      expect {\n        filebucket.command_line.args = ['backup', backup_file]\n        filebucket.run\n      }.to output(a_string_matching(\n        %r{Debug: HTTP HEAD https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file/sha256/f3aee54d781e413862eb068d89661f930385cc81bbafffc68477ff82eb9bea43/}\n      ).and matching(\n        %r{Debug: HTTP PUT https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file/sha256/f3aee54d781e413862eb068d89661f930385cc81bbafffc68477ff82eb9bea43/}\n      )).to_stdout\n\n      expect(File.binread(File.join(server.upload_directory, 'filebucket'))).to eq(binary)\n    end\n  end\n\n  it \"backs up utf-8 encoded files to the filebucket server\" do\n    utf8 = \"\\u2603\".force_encoding(Encoding::UTF_8)\n    File.binwrite(backup_file, utf8)\n\n    server.start_server do |port|\n      Puppet[:serverport] = port\n      expect {\n        filebucket.command_line.args = ['backup', backup_file]\n        filebucket.run\n      }.to output(a_string_matching(\n        %r{Debug: HTTP HEAD https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file/sha256/51643361c79ecaef25a8de802de24f570ba25d9c2df1d22d94fade11b4f466cc/}\n      ).and matching(\n        %r{Debug: HTTP PUT https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file/sha256/51643361c79ecaef25a8de802de24f570ba25d9c2df1d22d94fade11b4f466cc/}\n      )).to_stdout\n\n      expect(File.read(File.join(server.upload_directory, 'filebucket'), encoding: 'utf-8')).to eq(utf8)\n    end\n  end\n\n  it \"doesn't attempt to back up file that already exists on the filebucket server\" do\n    file_exists_handler = -> (req, res) {\n      res.status = 200\n    }\n\n    server.start_server(mounts: {filebucket: file_exists_handler}) do |port|\n      Puppet[:serverport] = port\n      expect {\n        filebucket.command_line.args = ['backup', backup_file]\n        filebucket.run\n      }.to output(a_string_matching(\n        %r{Debug: HTTP HEAD https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file\\/sha256\\/#{sha256}\\/#{File.realpath(backup_file)}\\?environment\\=production returned 200 OK}\n      ).and matching(\n        %r{#{backup_file}: #{sha256}}\n      )).to_stdout\n    end\n  end\n\n  it \"downloads files from the filebucket server\" do\n    get_handler = -> (req, res) {\n      res['Content-Type'] = 'application/octet-stream'\n      res.body = 'something to store'\n    }\n\n    server.start_server(mounts: {filebucket: get_handler}) do |port|\n      Puppet[:serverport] = port\n      expect {\n        filebucket.command_line.args = ['get', 'fac251367c9e083c6b1f0f3181']\n        filebucket.run\n      }.to output(a_string_matching(\n        %r{Debug: HTTP GET https:\\/\\/127.0.0.1:#{port}\\/puppet\\/v3\\/file_bucket_file\\/sha256\\/fac251367c9e083c6b1f0f3181\\?environment\\=production returned 200 OK}\n      ).and matching(\n        %r{something to store}\n       )).to_stdout\n    end\n  end\n\n  it \"lists the local filebucket even if the environment doesn't exist locally\" do\n    Puppet[:environment] = 'doesnotexist'\n    Puppet::FileSystem.mkpath(Puppet[:clientbucketdir])\n\n    filebucket.command_line.args = ['backup', '--local', backup_file]\n    expect {\n      result = filebucket.run\n      expect(result).to eq([backup_file])\n    }.to output(/Computing checksum on file/).to_stdout\n  end\n\n  context 'diff', unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n    context 'using a remote bucket' do\n      it 'outputs a diff between a local and remote file' do\n        File.binwrite(backup_file, \"bar\\nbaz\")\n\n        get_handler = -> (req, res) {\n          res['Content-Type'] = 'application/octet-stream'\n          res.body = 'foo'\n        }\n\n        server.start_server(mounts: {filebucket: get_handler}) do |port|\n          Puppet[:serverport] = port\n          expect {\n            filebucket.command_line.args = ['diff', 'fac251367c9e083c6b1f0f3181', backup_file, '--remote']\n            filebucket.run\n          }.to output(a_string_matching(\n            /[-<] ?foo/\n          ).and matching(\n            /[+>] ?bar/\n          ).and matching(\n            /[+>] ?baz/\n          )).to_stdout\n        end\n      end\n\n      it 'outputs a diff between two remote files' do\n        get_handler = -> (req, res) {\n          res['Content-Type'] = 'application/octet-stream'\n          res.body = <<~END\n          --- /opt/puppetlabs/server/data/puppetserver/bucket/d/3/b/0/7/3/8/4/d3b07384d113edec49eaa6238ad5ff00/contents\\t2020-04-06 21:25:24.892367570 +0000\n          +++ /opt/puppetlabs/server/data/puppetserver/bucket/9/9/b/9/9/9/2/0/99b999207e287afffc86c053e5693247/contents\\t2020-04-06 21:26:13.603398063 +0000\n          @@ -1 +1,2 @@\n          -foo\n          +bar\n          +baz\n          END\n        }\n\n        server.start_server(mounts: {filebucket: get_handler}) do |port|\n          Puppet[:serverport] = port\n          expect {\n            filebucket.command_line.args = ['diff', 'd3b07384d113edec49eaa6238ad5ff00', \"99b999207e287afffc86c053e5693247\", '--remote']\n            filebucket.run\n          }.to output(a_string_matching(\n            /[-<] ?foo/\n          ).and matching(\n            /[+>] ?bar/\n          ).and matching(\n            /[+>] ?baz/\n          )).to_stdout\n        end\n      end\n    end\n\n    context 'using a local bucket' do\n      let(:filea) {\n        f = tmpfile('filea')\n        File.binwrite(f, 'foo')\n        f\n      }\n      let(:fileb) {\n        f = tmpfile('fileb')\n        File.binwrite(f, \"bar\\nbaz\")\n        f\n      }\n      let(:checksuma) { Digest::SHA256.file(filea).to_s }\n      let(:checksumb) { Digest::SHA256.file(fileb).to_s }\n\n      it 'compares to files stored in a local bucket' do\n        expect {\n          filebucket.command_line.args = ['backup', filea, '--local']\n          filebucket.run\n        }.to output(/#{filea}: #{checksuma}/).to_stdout\n\n        expect{\n          filebucket.command_line.args = ['backup', fileb, '--local']\n          filebucket.run\n        }.to output(/#{fileb}: #{checksumb}\\n/).to_stdout\n\n        expect {\n          filebucket.command_line.args = ['diff', checksuma, checksumb, '--local']\n          filebucket.run\n        }.to output(a_string_matching(\n          /[-<] ?foo/\n        ).and matching(\n          /[+>] ?bar/\n        ).and matching(\n          /[+>] ?baz/\n        )).to_stdout\n      end\n\n      it 'compares a file on the filesystem and a file stored in a local bucket' do\n        expect {\n          filebucket.command_line.args = ['backup', filea, '--local']\n          filebucket.run\n        }.to output(/#{filea}: #{checksuma}/).to_stdout\n\n        expect {\n          filebucket.command_line.args = ['diff', checksuma, fileb, '--local']\n          filebucket.run\n        }.to output(a_string_matching(\n          /[-<] ?foo/\n        ).and matching(\n          /[+>] ?bar/\n        ).and matching(\n          /[+>] ?baz/\n        )).to_stdout\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/help_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/application/help'\n\ndescribe \"puppet help\" do\n  let(:app) { Puppet::Application[:help] }\n\n  it \"generates global help\" do\n    expect {\n      app.run\n    }.to exit_with(0)\n     .and output(Regexp.new(Regexp.escape(<<~END), Regexp::MULTILINE)).to_stdout\n\n       Usage: puppet <subcommand> [options] <action> [options]\n\n       Available subcommands:\n     END\n  end\n\n  Puppet::Face.faces.sort.each do |face_name|\n    next if face_name == :key\n\n    context \"for #{face_name}\" do\n      it \"generates help\" do\n        app.command_line.args = ['help', face_name]\n\n        expect {\n          app.run\n        }.to exit_with(0)\n         .and output(/USAGE: puppet #{face_name} <action>/).to_stdout\n      end\n\n      Puppet::Face[face_name, :current].actions.sort.each do |action_name|\n        it \"for action #{action_name}\" do\n          app.command_line.args = ['help', face_name, action_name]\n\n          expect {\n            app.run\n          }.to exit_with(0)\n           .and output(/USAGE: puppet #{face_name}/).to_stdout\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/lookup_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\nrequire 'deep_merge/core'\n\ndescribe 'lookup' do\n  include PuppetSpec::Files\n\n  context 'with an environment' do\n    let(:fqdn) { Puppet[:certname] }\n    let(:env_name) { 'spec' }\n    let(:env_dir) { tmpdir('environments') }\n    let(:environment_files) do\n      {\n        env_name => {\n          'modules' => {},\n          'hiera.yaml' => <<-YAML.unindent,\n            ---\n            version: 5\n            hierarchy:\n              - name: \"Common\"\n                data_hash: yaml_data\n                path: \"common.yaml\"\n            YAML\n          'data' => {\n            'common.yaml' => <<-YAML.unindent\n              ---\n              a: value a\n              mod_a::a: value mod_a::a (from environment)\n              mod_a::hash_a:\n                a: value mod_a::hash_a.a (from environment)\n              mod_a::hash_b:\n                a: value mod_a::hash_b.a (from environment)\n              lookup_options:\n                mod_a::hash_b:\n                  merge: hash\n              YAML\n          }\n        },\n        'someother' => {\n        }\n      }\n    end\n\n    let(:app) { Puppet::Application[:lookup] }\n    let(:facts) { Puppet::Node::Facts.new(\"facts\", {'my_fact' => 'my_fact_value'}) }\n    let(:cert) { pem_content('oid.pem') }\n\n    let(:node) { Puppet::Node.new('testnode', :facts => facts) }\n    let(:populated_env_dir) do\n      dir_contained_in(env_dir, environment_files)\n      env_dir\n    end\n\n    before do\n      stub_request(:get, \"https://puppet:8140/puppet-ca/v1/certificate/#{fqdn}\").to_return(body: cert)\n      allow(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)\n\n      Puppet[:environment] = env_name\n      Puppet[:environmentpath] = populated_env_dir\n\n      http = Puppet::HTTP::Client.new(ssl_context: Puppet::SSL::SSLProvider.new.create_insecure_context)\n      Puppet.runtime[:http] = http\n    end\n\n    def expect_lookup_with_output(exitcode, out)\n      expect { app.run }.to exit_with(exitcode).and output(out).to_stdout\n    end\n\n    it 'finds data in the environment' do\n      app.command_line.args << 'a'\n      expect_lookup_with_output(0, /value a/)\n    end\n\n    it \"resolves hiera data using a top-level node parameter\" do\n      File.write(File.join(env_dir, env_name, 'hiera.yaml'), <<~YAML)\n        ---\n        version: 5\n        hierarchy:\n          - name: \"Per Node\"\n            data_hash: yaml_data\n            path: \"%{my_fact}.yaml\"\n      YAML\n\n      File.write(File.join(env_dir, env_name, 'data', \"my_fact_value.yaml\"), <<~YAML)\n        ---\n        a: value from per node data\n      YAML\n\n      app.command_line.args << 'a'\n      expect_lookup_with_output(0, /--- value from per node data/)\n    end\n\n    it \"resolves hiera data using a top-level node parameter from enc\" do\n      Puppet.settings[:node_terminus] = 'exec'\n      enc = tmpfile('enc.sh')\n      Puppet.settings[:external_nodes] = enc\n      File.write(File.join(env_dir, env_name, 'hiera.yaml'), <<~YAML)\n      ---\n      version: 5\n      hierarchy:\n        - name: \"Node parameters\"\n          data_hash: yaml_data\n          path: \"%{site}.yaml\"\n      YAML\n\n      File.write(File.join(env_dir, env_name, 'data', \"pdx.yaml\"), <<~YAML)\n      ---\n      key: value\n      YAML\n      allow(Puppet::Util::Execution).to receive(:execute).with([enc, fqdn], anything).and_return(<<~YAML)\n      parameters:\n        site: pdx\n      YAML\n      app.command_line.args << 'key' << '--compile'\n      Puppet.initialize_settings(['-E', env_name])\n      expect_lookup_with_output(0, /--- value/)\n    end\n\n    it \"prefers the environment specified on the commandline over the enc environment\" do\n      Puppet.settings[:node_terminus] = 'exec'\n      enc = tmpfile('enc.sh')\n      Puppet.settings[:external_nodes] = enc\n      File.write(File.join(env_dir, env_name, 'hiera.yaml'), <<~YAML)\n      ---\n      version: 5\n      hierarchy:\n        - name: \"Node parameters\"\n          data_hash: yaml_data\n          path: \"%{site}.yaml\"\n      YAML\n\n      File.write(File.join(env_dir, env_name, 'data', \"pdx.yaml\"), <<~YAML)\n      ---\n      key: value\n      YAML\n      allow(Puppet::Util::Execution).to receive(:execute).with([enc, fqdn], anything).and_return(<<~YAML)\n      ---\n      # return 'someother' environment because it doesn't have any hiera data\n      environment: someother\n      parameters:\n        site: pdx\n      YAML\n      app.command_line.args << 'key' << '--compile'\n      Puppet.initialize_settings(['-E', env_name])\n      expect_lookup_with_output(0, /--- value/)\n    end\n\n    it 'loads trusted information from the node certificate' do\n      Puppet.settings[:node_terminus] = 'exec'\n      expect_any_instance_of(Puppet::Node::Exec).to receive(:find) do |args|\n        info = Puppet.lookup(:trusted_information)\n        expect(info.certname).to eq(fqdn)\n        expect(info.extensions).to eq({ \"1.3.6.1.4.1.34380.1.2.1.1\" => \"somevalue\" })\n      end.and_return(node)\n\n      app.command_line.args << 'a' << '--compile'\n      expect_lookup_with_output(0, /--- value a/)\n    end\n\n    it 'loads external facts when running without --node' do\n      expect(Puppet::Util).not_to receive(:skip_external_facts)\n      expect(Facter).not_to receive(:load_external)\n\n      app.command_line.args << 'a'\n      expect_lookup_with_output(0, /--- value a/)\n    end\n\n    describe 'when using --node' do\n      let(:fqdn) { 'random_node' }\n\n      it 'skips loading of external facts' do\n        app.command_line.args << 'a' << '--node' << fqdn\n\n        expect(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)\n        expect(Facter).to receive(:load_external).twice.with(false)\n        expect(Facter).to receive(:load_external).twice.with(true)\n        expect_lookup_with_output(0, /--- value a/)\n      end\n    end\n\n    context 'uses node_terminus' do\n      require 'puppet/indirector/node/exec'\n      require 'puppet/indirector/node/plain'\n\n      let(:node) { Puppet::Node.new('testnode', :facts => facts) }\n\n      it ':plain without --compile' do\n        Puppet.settings[:node_terminus] = 'exec'\n        expect_any_instance_of(Puppet::Node::Plain).to receive(:find).and_return(node)\n        expect_any_instance_of(Puppet::Node::Exec).not_to receive(:find)\n\n        app.command_line.args << 'a'\n        expect_lookup_with_output(0, /--- value a/)\n      end\n\n      it 'configured in Puppet settings with --compile' do\n        Puppet.settings[:node_terminus] = 'exec'\n        expect_any_instance_of(Puppet::Node::Plain).not_to receive(:find)\n        expect_any_instance_of(Puppet::Node::Exec).to receive(:find).and_return(node)\n\n        app.command_line.args << 'a' << '--compile'\n        expect_lookup_with_output(0, /--- value a/)\n      end\n    end\n\n    context 'configured with the wrong environment' do\n      it 'does not find data in non-existing environment' do\n        Puppet[:environment] = 'doesntexist'\n        app.command_line.args << 'a'\n        expect { app.run }.to raise_error(Puppet::Environments::EnvironmentNotFound, /Could not find a directory environment named 'doesntexist'/)\n      end\n    end\n\n    context 'and a module' do\n      let(:mod_a_files) do\n        {\n          'mod_a' => {\n            'data' => {\n              'common.yaml' => <<-YAML.unindent\n                ---\n                mod_a::a: value mod_a::a (from mod_a)\n                mod_a::b: value mod_a::b (from mod_a)\n                mod_a::hash_a:\n                  a: value mod_a::hash_a.a (from mod_a)\n                  b: value mod_a::hash_a.b (from mod_a)\n                mod_a::hash_b:\n                  a: value mod_a::hash_b.a (from mod_a)\n                  b: value mod_a::hash_b.b (from mod_a)\n                mod_a::interpolated: \"-- %{lookup('mod_a::a')} --\"\n                mod_a::a_a: \"-- %{lookup('mod_a::hash_a.a')} --\"\n                mod_a::a_b: \"-- %{lookup('mod_a::hash_a.b')} --\"\n                mod_a::b_a: \"-- %{lookup('mod_a::hash_b.a')} --\"\n                mod_a::b_b: \"-- %{lookup('mod_a::hash_b.b')} --\"\n                'mod_a::a.quoted.key': 'value mod_a::a.quoted.key (from mod_a)'\n                YAML\n            },\n            'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              hierarchy:\n                - name: \"Common\"\n                  data_hash: yaml_data\n                  path: \"common.yaml\"\n              YAML\n          }\n        }\n      end\n\n      let(:populated_env_dir) do\n        dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files }))\n        env_dir\n      end\n\n      it 'finds data in the module' do\n        app.command_line.args << 'mod_a::b'\n        expect_lookup_with_output(0, /value mod_a::b \\(from mod_a\\)/)\n      end\n\n      it 'finds quoted keys in the module' do\n        app.command_line.args << \"'mod_a::a.quoted.key'\"\n        expect_lookup_with_output(0, /value mod_a::a.quoted.key \\(from mod_a\\)/)\n      end\n\n      it 'merges hashes from environment and module when merge strategy hash is used' do\n        app.command_line.args << 'mod_a::hash_a' << '--merge' << 'hash'\n        expect_lookup_with_output(0, <<~END)\n          ---\n          a: value mod_a::hash_a.a (from environment)\n          b: value mod_a::hash_a.b (from mod_a)\n        END\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/module_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/forge'\nrequire 'puppet_spec/https'\n\ndescribe 'puppet module', unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n  include_context \"https client\"\n\n  let(:app) { Puppet::Application[:module] }\n  let(:wrong_hostname) { 'localhost' }\n  let(:server) { PuppetSpec::HTTPSServer.new }\n  let(:ssl_provider) { Puppet::SSL::SSLProvider.new }\n\n  let(:release_response) { File.read(fixtures('unit/forge/bacula-releases.json')) }\n  let(:release_tarball) { File.binread(fixtures('unit/forge/bacula.tar.gz')) }\n  let(:target_dir) { tmpdir('bacula') }\n\n  before :each do\n    SemanticPuppet::Dependency.clear_sources\n  end\n\n  it 'installs a module' do\n    # create a temp cacert bundle\n    ssl_file = tmpfile('systemstore')\n    File.write(ssl_file, server.ca_cert)\n\n    response_proc = -> (req, res) {\n      if req.path == '/v3/releases'\n        res['Content-Type'] = 'application/json'\n        res.body = release_response\n      else\n        res['Content-Type'] = 'application/octet-stream'\n        res.body = release_tarball\n      end\n      res.status = 200\n    }\n\n    # override path to system cacert bundle, this must be done before\n    # the SSLContext is created and the call to X509::Store.set_default_paths\n    Puppet::Util.withenv(\"SSL_CERT_FILE\" => ssl_file) do\n      server.start_server(response_proc: response_proc) do |port|\n        Puppet[:module_repository] = \"https://127.0.0.1:#{port}\"\n\n        # On Windows, CP437 encoded output can't be matched against UTF-8 regexp,\n        # so encode the regexp to the external encoding and match against that.\n        app.command_line.args = ['install', 'puppetlabs-bacula', '--target-dir', target_dir]\n        expect {\n          app.run\n        }.to exit_with(0)\n         .and output(Regexp.new(\"└── puppetlabs-bacula\".encode(Encoding.default_external))).to_stdout\n      end\n    end\n  end\n\n  it 'returns a valid exception when there is an SSL verification problem' do\n    server.start_server do |port|\n      Puppet[:module_repository] = \"https://#{wrong_hostname}:#{port}\"\n\n      expect {\n        app.command_line.args = ['install', 'puppetlabs-bacula', '--target-dir', target_dir]\n        app.run\n      }.to exit_with(1)\n       .and output(%r{Notice: Downloading from https://#{wrong_hostname}:#{port}}).to_stdout\n       .and output(%r{Unable to verify the SSL certificate}).to_stderr\n    end\n  end\n\n  it 'prints the complete URL it tried to connect to' do\n    response_proc = -> (req, res) { res.status = 404 }\n\n    # create a temp cacert bundle\n    ssl_file = tmpfile('systemstore')\n    File.write(ssl_file, server.ca_cert)\n\n    Puppet::Util.withenv(\"SSL_CERT_FILE\" => ssl_file) do\n      server.start_server(response_proc: response_proc) do |port|\n        Puppet[:module_repository] = \"https://127.0.0.1:#{port}/bogus_test/puppet\"\n\n        expect {\n          app.command_line.args = ['install', 'puppetlabs-bacula']\n          app.run\n        }.to exit_with(1)\n         .and output(%r{Notice: Downloading from https://127.0.0.1:#{port}}).to_stdout\n         .and output(%r{https://127.0.0.1:#{port}/bogus_test/puppet/v3/releases}).to_stderr\n      end\n    end\n  end\n\n  context 'install' do\n    it 'lists a module in a non-default directory environment' do\n      Puppet.initialize_settings(['-E', 'direnv'])\n      Puppet[:color] = false\n      Puppet[:environmentpath] = File.join(my_fixture_dir, 'environments')\n\n      expect {\n        app.command_line.args = ['list']\n        app.run\n      }.to exit_with(0)\n       .and output(Regexp.new(\"└── pmtacceptance-nginx\".encode(Encoding.default_external), Regexp::MULTILINE)).to_stdout\n    end\n  end\n\n  context 'changes' do\n    let(:tmp) { tmpdir('module_changes') }\n\n    before :each do\n      Puppet.initialize_settings(['-E', 'direnv'])\n      Puppet[:color] = false\n    end\n\n    def use_local_fixture\n      Puppet[:environmentpath] = File.join(my_fixture_dir, 'environments')\n    end\n\n    def create_working_copy\n      Puppet[:environmentpath] = File.join(tmp, 'environments')\n      FileUtils.cp_r(File.join(my_fixture_dir, 'environments'), tmp)\n    end\n\n    it 'reports an error when the install path is invalid' do\n      use_local_fixture\n\n      pattern = Regexp.new([\n        %Q{.*Error: Could not find a valid module at \"#{tmp}/nginx\".*},\n        %Q{.*Error: Try 'puppet help module changes' for usage.*},\n      ].join(\"\\n\"), Regexp::MULTILINE)\n\n      expect {\n        app.command_line.args = ['changes', File.join(tmp, 'nginx')]\n        app.run\n      }.to exit_with(1)\n       .and output(pattern).to_stderr\n    end\n\n    it 'reports when checksums are missing from metadata.json' do\n      create_working_copy\n\n      # overwrite checksums in metadata.json\n      nginx_dir = File.join(tmp, 'environments', 'direnv', 'modules', 'nginx')\n      File.write(File.join(nginx_dir, 'metadata.json'), <<~END)\n        {\n          \"name\": \"pmtacceptance/nginx\",\n          \"version\": \"0.0.1\"\n        }\n      END\n\n      pattern = Regexp.new([\n        %Q{.*Error: No file containing checksums found.*},\n        %Q{.*Error: Try 'puppet help module changes' for usage.*},\n      ].join(\"\\n\"), Regexp::MULTILINE)\n\n      expect {\n        app.command_line.args = ['changes', nginx_dir]\n        app.run\n      }.to exit_with(1)\n        .and output(pattern).to_stderr\n    end\n\n    it 'reports module not found when metadata.json is missing' do\n      create_working_copy\n\n      # overwrite checksums in metadata.json\n      nginx_dir = File.join(tmp, 'environments', 'direnv', 'modules', 'nginx')\n      File.unlink(File.join(nginx_dir, 'metadata.json'))\n\n      pattern = Regexp.new([\n        %Q{.*Error: Could not find a valid module at.*},\n        %Q{.*Error: Try 'puppet help module changes' for usage.*},\n      ].join(\"\\n\"), Regexp::MULTILINE)\n\n      expect {\n        app.command_line.args = ['changes', nginx_dir]\n        app.run\n      }.to exit_with(1)\n        .and output(pattern).to_stderr\n    end\n\n    it 'reports when a file is modified' do\n      create_working_copy\n\n      # overwrite README so checksum doesn't match\n      nginx_dir = File.join(tmp, 'environments', 'direnv', 'modules', 'nginx')\n      File.write(File.join(nginx_dir, 'README'), '')\n\n      pattern = Regexp.new([\n        %Q{.*Warning: 1 files modified.*},\n      ].join(\"\\n\"), Regexp::MULTILINE)\n\n      expect {\n        app.command_line.args = ['changes', nginx_dir]\n        app.run\n      }.to exit_with(0)\n        .and output(%r{README}).to_stdout\n        .and output(pattern).to_stderr\n    end\n\n    it 'reports when a file is missing' do\n      create_working_copy\n\n      # delete README so checksum doesn't match\n      nginx_dir = File.join(tmp, 'environments', 'direnv', 'modules', 'nginx')\n      File.unlink(File.join(nginx_dir, 'README'))\n\n      # odd that it says modified\n      pattern = Regexp.new([\n        %Q{.*Warning: 1 files modified.*},\n      ].join(\"\\n\"), Regexp::MULTILINE)\n\n      expect {\n        app.command_line.args = ['changes', nginx_dir]\n        app.run\n      }.to exit_with(0)\n        .and output(%r{README}).to_stdout\n        .and output(pattern).to_stderr\n    end\n\n    it 'reports when there are no changes' do\n      use_local_fixture\n\n      nginx_dir = File.join(Puppet[:environmentpath], 'direnv', 'modules', 'nginx')\n\n      expect {\n        app.command_line.args = ['changes', nginx_dir]\n        app.run\n      }.to exit_with(0)\n        .and output(/No modified files/).to_stdout\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/plugin_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet_spec/puppetserver'\n\ndescribe \"puppet plugin\", unless: Puppet::Util::Platform.jruby? do\n  include_context \"https client\"\n\n  let(:server) { PuppetSpec::Puppetserver.new }\n  let(:plugin) { Puppet::Application[:plugin] }\n  let(:response_body) { \"[{\\\"path\\\":\\\"/etc/puppetlabs/code/environments/production/modules\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":0,\\\"group\\\":0,\\\"mode\\\":493,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-03-06 20:14:25 UTC\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}]\" }\n\n  it \"downloads from plugins, pluginsfacts and locales mounts when i18n is enabled\" do\n    Puppet[:disable_i18n] = false\n    current_version_handler = -> (req, res) {\n      res['X-Puppet-Version'] = Puppet.version\n      res['Content-Type'] = 'application/json'\n      res.body = response_body\n    }\n\n    server.start_server(mounts: {file_metadatas: current_version_handler}) do |port|\n      Puppet[:serverport] = port\n      expect {\n        plugin.command_line.args << 'download'\n        plugin.run\n      }.to exit_with(0)\n       .and output(matching(\n         \"Downloaded these plugins: #{Regexp.escape(Puppet[:pluginfactdest])}, #{Regexp.escape(Puppet[:plugindest])}, #{Regexp.escape(Puppet[:localedest])}\"\n       )).to_stdout\n    end\n  end\n\n  it \"downloads from plugins, pluginsfacts but no locales mounts when i18n is disabled\" do\n    Puppet[:disable_i18n] = true\n\n    current_version_handler = -> (req, res) {\n      res['X-Puppet-Version'] = Puppet.version\n      res['Content-Type'] = 'application/json'\n      res.body = response_body\n    }\n\n    server.start_server(mounts: {file_metadatas: current_version_handler}) do |port|\n      Puppet[:serverport] = port\n      expect {\n        plugin.command_line.args << 'download'\n        plugin.run\n      }.to exit_with(0)\n       .and output(matching(\n         \"Downloaded these plugins: #{Regexp.escape(Puppet[:pluginfactdest])}, #{Regexp.escape(Puppet[:plugindest])}\"\n       )).to_stdout\n    end\n  end\n\n  it \"downloads from plugins and pluginsfacts from older puppetservers\" do\n    no_locales_handler = -> (req, res) {\n      res['X-Puppet-Version'] = '5.3.3' # locales mount was added in 5.3.4\n      res['Content-Type'] = 'application/json'\n      res.body = response_body\n    }\n\n    server.start_server(mounts: {file_metadatas: no_locales_handler}) do |port|\n      Puppet[:serverport] = port\n      expect {\n        plugin.command_line.args << 'download'\n        plugin.run\n      }.to exit_with(0)\n       .and output(matching(\n         \"Downloaded these plugins: #{Regexp.escape(Puppet[:pluginfactdest])}, #{Regexp.escape(Puppet[:plugindest])}\"\n       )).to_stdout\n    end\n  end\n\n  it \"downloads from an environment that doesn't exist locally\" do\n    requested_environment = nil\n\n    current_version_handler = -> (req, res) {\n      res['X-Puppet-Version'] = Puppet.version\n      res['Content-Type'] = 'application/json'\n      res.body = response_body\n      requested_environment = req.query['environment']\n    }\n\n    server.start_server(mounts: {file_metadatas: current_version_handler}) do |port|\n      Puppet[:environment] = 'doesnotexistontheagent'\n      Puppet[:serverport] = port\n      expect {\n        plugin.command_line.args << 'download'\n        plugin.run\n      }.to exit_with(0)\n       .and output(matching(\"Downloaded these plugins\")).to_stdout\n\n      expect(requested_environment).to eq('doesnotexistontheagent')\n    end\n  end\n\n  context \"pluginsync for external facts uses source permissions to preserve fact executable-ness\" do\n    before :all do\n      WebMock.enable!\n    end\n\n    after :all do\n      WebMock.disable!\n    end\n\n    before :each do\n      metadata = \"[{\\\"path\\\":\\\"/etc/puppetlabs/code\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":0,\\\"group\\\":0,\\\"mode\\\":420,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-07-10 14:00:00 -0700\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}]\"\n      stub_request(:get, %r{/puppet/v3/file_metadatas/(plugins|locales)}).to_return(status: 200, body: metadata, headers: {'Content-Type' => 'application/json'})\n\n      # response retains owner/group/mode due to source_permissions => use\n      facts_metadata = \"[{\\\"path\\\":\\\"/etc/puppetlabs/code\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":500,\\\"group\\\":500,\\\"mode\\\":493,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-07-10 14:00:00 -0700\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}]\"\n      stub_request(:get, %r{/puppet/v3/file_metadatas/pluginfacts}).to_return(status: 200, body: facts_metadata, headers: {'Content-Type' => 'application/json'})\n    end\n\n    it \"processes a download request resulting in no changes\" do\n      # Create these so there are no changes\n      Puppet::FileSystem.mkpath(Puppet[:plugindest])\n      Puppet::FileSystem.mkpath(Puppet[:localedest])\n\n      # /opt/puppetlabs/puppet/cache/facts.d will be created based on our umask.\n      # If the mode on disk is not 0755, then the mode from the metadata response\n      # (493 => 0755) will be applied, resulting in \"plugins were downloaded\"\n      # message. Enforce a umask so the results are consistent.\n      Puppet::FileSystem.mkpath(Puppet[:pluginfactdest])\n      Puppet::FileSystem.chmod(0755, Puppet[:pluginfactdest])\n\n      app = Puppet::Application[:plugin]\n      app.command_line.args << 'download'\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/No plugins downloaded/).to_stdout\n    end\n\n    it \"updates the facts.d mode\", unless: Puppet::Util::Platform.windows? do\n      Puppet::FileSystem.mkpath(Puppet[:pluginfactdest])\n      Puppet::FileSystem.chmod(0775, Puppet[:pluginfactdest])\n\n      app = Puppet::Application[:plugin]\n      app.command_line.args << 'download'\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/Downloaded these plugins: .*facts\\.d/).to_stdout\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/resource_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\ndescribe \"puppet resource\", unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  let(:resource) { Puppet::Application[:resource] }\n\n  context 'when given an invalid environment' do\n    before { Puppet[:environment] = 'badenv' }\n\n    it 'falls back to the default environment' do\n      Puppet[:log_level] = 'debug'\n\n      expect {\n        resource.run\n      }.to exit_with(1)\n       .and output(/Debug: Specified environment 'badenv' does not exist on the filesystem, defaulting to 'production'/).to_stdout\n       .and output(/Error: Could not run: You must specify the type to display/).to_stderr\n    end\n\n    it 'lists resources' do\n      resource.command_line.args = ['file', Puppet[:confdir]]\n\n      expect {\n        resource.run\n      }.to output(/file { '#{Puppet[:confdir]}':/).to_stdout\n    end\n\n    it 'lists types from the default environment' do\n      begin\n      modulepath = File.join(Puppet[:codedir], 'modules', 'test', 'lib', 'puppet', 'type')\n      FileUtils.mkdir_p(modulepath)\n      File.write(File.join(modulepath, 'test_resource_spec.rb'), 'Puppet::Type.newtype(:test_resource_spec)')\n      resource.command_line.args = ['--types']\n\n      expect {\n        resource.run\n      }.to exit_with(0).and output(/test_resource_spec/).to_stdout\n      ensure\n        Puppet::Type.rmtype(:test_resource_spec)\n      end\n    end\n  end\n\n\n  context 'when handling file and tidy types' do\n    let!(:dir) { dir_containing('testdir', 'testfile' => 'contents') }\n\n    it 'does not raise when generating file resources' do\n      resource.command_line.args = ['file', dir, 'ensure=directory', 'recurse=true']\n\n      expect {\n        resource.run\n      }.to output(/ensure.+=> 'directory'/).to_stdout\n    end\n\n    it 'correctly cleans up a given path' do\n      resource.command_line.args = ['tidy', dir, 'rmdirs=true', 'recurse=true']\n\n      expect {\n        resource.run\n      }.to output(/Notice: \\/File\\[#{dir}\\]\\/ensure: removed/).to_stdout\n\n      expect(Puppet::FileSystem.exist?(dir)).to be false\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/application/ssl_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"puppet ssl\", unless: Puppet::Util::Platform.jruby? do\n  context \"print\" do\n    it 'translates custom oids to their long name' do\n      basedir = File.expand_path(\"#{__FILE__}/../../../fixtures/ssl\")\n      # registering custom oids changes global state, so shell out\n      output =\n        %x{puppet ssl show \\\n           --certname oid \\\n           --localcacert #{basedir}/ca.pem \\\n           --hostcrl #{basedir}/crl.pem \\\n           --hostprivkey #{basedir}/oid-key.pem \\\n           --hostcert #{basedir}/oid.pem \\\n           --trusted_oid_mapping_file #{basedir}/trusted_oid_mapping.yaml 2>&1\n        }\n      expect(output).to match(/Long name:/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/configurer_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/network'\nrequire 'puppet/configurer'\n\ndescribe Puppet::Configurer do\n  include PuppetSpec::Files\n  include PuppetSpec::Network\n\n  describe \"when running\" do\n    before(:each) do\n      @catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet.lookup(:environments).get(Puppet[:environment]))\n      @catalog.add_resource(Puppet::Type.type(:notify).new(:title => \"testing\"))\n\n      # Make sure we don't try to persist the local state after the transaction ran,\n      # because it will fail during test (the state file is in a not-existing directory)\n      # and we need the transaction to be successful to be able to produce a summary report\n      @catalog.host_config = false\n\n      @configurer = Puppet::Configurer.new\n    end\n\n    it \"should send a transaction report with valid data\" do\n      allow(@configurer).to receive(:save_last_run_summary)\n      expect(Puppet::Transaction::Report.indirection).to receive(:save) do |report, x|\n        expect(report.time).to be_a(Time)\n        expect(report.logs.length).to be > 0\n      end.twice\n\n      Puppet[:report] = true\n\n      @configurer.run :catalog => @catalog\n    end\n\n    it \"should save a correct last run summary\" do\n      report = Puppet::Transaction::Report.new\n      allow(Puppet::Transaction::Report.indirection).to receive(:save)\n\n      Puppet[:lastrunfile] = tmpfile(\"lastrunfile\")\n      Puppet.settings.setting(:lastrunfile).mode = 0666\n      Puppet[:report] = true\n\n      # We only record integer seconds in the timestamp, and truncate\n      # backwards, so don't use a more accurate timestamp in the test.\n      # --daniel 2011-03-07\n      t1 = Time.now.tv_sec\n      @configurer.run :catalog => @catalog, :report => report\n      t2 = Time.now.tv_sec\n\n      # sticky bit only applies to directories in windows\n      file_mode = Puppet::Util::Platform.windows? ? '666' : '100666'\n\n      expect(Puppet::FileSystem.stat(Puppet[:lastrunfile]).mode.to_s(8)).to eq(file_mode)\n\n      summary = Puppet::Util::Yaml.safe_load_file(Puppet[:lastrunfile])\n\n      expect(summary).to be_a(Hash)\n      %w{time changes events resources}.each do |key|\n        expect(summary).to be_key(key)\n      end\n      expect(summary[\"time\"]).to be_key(\"notify\")\n      expect(summary[\"time\"][\"last_run\"]).to be_between(t1, t2)\n    end\n\n    it \"applies a cached catalog if pluginsync fails when usecacheonfailure is true and environment is valid\" do\n      expect(@configurer).to receive(:valid_server_environment?).and_return(true)\n      Puppet[:ignore_plugin_errors] = false\n\n      Puppet[:use_cached_catalog] = false\n      Puppet[:usecacheonfailure] = true\n\n      report = Puppet::Transaction::Report.new\n      expect_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate).and_raise(Puppet::Error, 'Failed to retrieve: some file')\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(@catalog)\n\n      @configurer.run(pluginsync: true, report: report)\n      expect(report.cached_catalog_status).to eq('on_failure')\n    end\n\n    it \"applies a cached catalog if pluginsync fails when usecacheonfailure is true and environment is invalid\" do\n      expect(@configurer).to receive(:valid_server_environment?).and_return(false)\n      Puppet[:ignore_plugin_errors] = false\n\n      Puppet[:use_cached_catalog] = false\n      Puppet[:usecacheonfailure] = true\n\n      report = Puppet::Transaction::Report.new\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_raise(Puppet::Error, 'Cannot compile remote catalog')\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(@catalog)\n\n      @configurer.run(pluginsync: true, report: report)\n      expect(report.cached_catalog_status).to eq('on_failure')\n    end\n\n    describe 'resubmitting facts' do\n      context 'when resubmit_facts is set to false' do\n        it 'should not send data' do\n          expect(@configurer).to receive(:resubmit_facts).never\n\n          @configurer.run(catalog: @catalog)\n        end\n      end\n\n      context 'when resubmit_facts is set to true' do\n        let(:test_facts) { Puppet::Node::Facts.new('configurer.test', {test_fact: 'test value'}) }\n\n        before(:each) do\n          Puppet[:resubmit_facts] = true\n\n          allow(@configurer).to receive(:find_facts).and_return(test_facts)\n        end\n\n        it 'uploads facts as application/json' do\n          stub_request(:put, \"https://puppet:8140/puppet/v3/facts/configurer.test?environment=production\").\n            with(\n              body: hash_including(\n                {\n                  \"name\" => \"configurer.test\",\n                  \"values\" => {\"test_fact\" => 'test value',},\n                }),\n              headers: {\n                'Accept'=>acceptable_content_types_string,\n                'Content-Type'=>'application/json',\n              })\n\n          @configurer.run(catalog: @catalog)\n        end\n\n        it 'logs errors that occur during fact generation' do\n          allow(@configurer).to receive(:find_facts).and_raise('error generating facts')\n          expect(Puppet).to receive(:log_exception).with(instance_of(RuntimeError),\n                                                         /^Failed to submit facts/)\n\n          @configurer.run(catalog: @catalog)\n        end\n\n        it 'logs errors that occur during fact submission' do\n          stub_request(:put, \"https://puppet:8140/puppet/v3/facts/configurer.test?environment=production\").to_return(status: 502)\n          expect(Puppet).to receive(:log_exception).with(Puppet::HTTP::ResponseError,\n                                                         /^Failed to submit facts/)\n\n          @configurer.run(catalog: @catalog)\n        end\n\n        it 'records time spent resubmitting facts' do\n          report = Puppet::Transaction::Report.new\n\n          stub_request(:put, \"https://puppet:8140/puppet/v3/facts/configurer.test?environment=production\").\n            with(\n              body: hash_including({\n                \"name\" => \"configurer.test\",\n                \"values\" => {\"test_fact\": \"test value\"},\n              }),\n              headers: {\n                'Accept'=>acceptable_content_types_string,\n                'Content-Type'=>'application/json',\n              }).to_return(status: 200)\n\n          @configurer.run(catalog: @catalog, report: report)\n\n          expect(report.metrics['time'].values).to include([\"resubmit_facts\", anything, Numeric])\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/data_binding_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/hiera'\n\nrequire 'puppet_spec/compiler'\nrequire 'puppet/indirector/data_binding/hiera'\n\ndescribe \"Data binding\" do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  let(:dir) { tmpdir(\"puppetdir\") }\n  let(:data) {{\n    'global' => {\n      'testing::binding::value' => 'the value',\n      'testing::binding::calling_class' => '%{calling_class}',\n      'testing::binding::calling_class_path' => '%{calling_class_path}'\n    }\n  }}\n  let(:hash_data) {{\n    'global' => {\n      'testing::hash::options' => {\n        'key'  => 'value',\n        'port' => '80',\n        'bind' => 'localhost'\n      }\n    },\n    'agent.example.com' => {\n      'testing::hash::options' => {\n        'key'  => 'new value',\n        'port' => '443'\n      }\n    }\n  }}\n  let(:hash_data_with_lopts) {{\n    'global' => {\n      'testing::hash::options' => {\n        'key'  => 'value',\n        'port' => '80',\n        'bind' => 'localhost'\n      }\n    },\n    'agent.example.com' => {\n      'lookup_options' => {\n        'testing::hash::options' => {\n          'merge' => 'deep'\n        }\n      },\n      'testing::hash::options' => {\n        'key'  => 'new value',\n        'port' => '443'\n      }\n    }\n  }}\n\n  before do\n    # Drop all occurances of cached hiera instances. This will reset @hiera in Puppet::Indirector::Hiera, Testing::DataBinding::Hiera,\n    # and Puppet::DataBinding::Hiera. Different classes are active as indirection depending on configuration\n    ObjectSpace.each_object(Class).select {|klass| klass <= Puppet::Indirector::Hiera }.each { |klass| klass.instance_variable_set(:@hiera, nil) }\n    Puppet[:data_binding_terminus] = 'hiera'\n    Puppet[:modulepath] = dir\n  end\n\n  context \"with testing::binding and global data only\" do\n    it \"looks up global data from hiera\" do\n      configure_hiera_for_one_tier(data)\n\n      create_manifest_in_module(\"testing\", \"binding.pp\",\n                                <<-MANIFEST)\n      class testing::binding($value,\n                             $calling_class,\n                             $calling_class_path,\n                             $calling_module = $module_name) {}\n      MANIFEST\n\n      catalog = compile_to_catalog(\"include testing::binding\")\n      resource = catalog.resource('Class[testing::binding]')\n\n      expect(resource[:value]).to eq(\"the value\")\n      expect(resource[:calling_class]).to eq(\"testing::binding\")\n      expect(resource[:calling_class_path]).to eq(\"testing/binding\")\n      expect(resource[:calling_module]).to eq(\"testing\")\n    end\n  end\n\n  context \"with testing::hash and global data only\" do\n    it \"looks up global data from hiera\" do\n      configure_hiera_for_one_tier(hash_data)\n\n      create_manifest_in_module(\"testing\", \"hash.pp\",\n                                <<-MANIFEST)\n      class testing::hash($options) {}\n      MANIFEST\n\n      catalog = compile_to_catalog(\"include testing::hash\")\n      resource = catalog.resource('Class[testing::hash]')\n\n      expect(resource[:options]).to eq({\n\t'key'  => 'value',\n\t'port' => '80',\n\t'bind' => 'localhost'\n      })\n    end\n  end\n\n  context \"with custom clientcert\" do\n    it \"merges global data with agent.example.com data from hiera\" do\n      configure_hiera_for_two_tier(hash_data)\n\n      create_manifest_in_module(\"testing\", \"hash.pp\",\n                                <<-MANIFEST)\n      class testing::hash($options) {}\n      MANIFEST\n\n      catalog = compile_to_catalog(\"include testing::hash\")\n      resource = catalog.resource('Class[testing::hash]')\n\n      expect(resource[:options]).to eq({\n            'key'  => 'new value',\n            'port' => '443',\n      })\n    end\n  end\n\n  context \"with custom clientcert and with lookup_options\" do\n    it \"merges global data with agent.example.com data from hiera\" do\n      configure_hiera_for_two_tier(hash_data_with_lopts)\n\n      create_manifest_in_module(\"testing\", \"hash.pp\",\n                                <<-MANIFEST)\n      class testing::hash($options) {}\n      MANIFEST\n\n      catalog = compile_to_catalog(\"include testing::hash\")\n      resource = catalog.resource('Class[testing::hash]')\n\n      expect(resource[:options]).to eq({\n        'key'  => 'new value',\n        'port' => '443',\n\t      'bind' => 'localhost',\n      })\n    end\n  end\n\n  context \"with plan_hierarchy key\" do\n    context \"using Hiera 5\" do\n      let(:hiera_config) { <<~CONF  }\n      ---\n      version: 5\n      plan_hierarchy:\n        - path: global\n          name: Common\n      CONF\n\n      it \"ignores plan_hierarchy outside of a Bolt plan\" do\n        configure_hiera_for_plan_hierarchy(data, hiera_config)\n\n        create_manifest_in_module(\"testing\", \"binding.pp\",\n                                  <<-MANIFEST)\n      class testing::binding($value) {}\n        MANIFEST\n\n        expect { compile_to_catalog(\"include testing::binding\") }\n          .to raise_error(/Class\\[Testing::Binding\\]: expects a value for parameter 'value'/)\n      end\n    end\n\n    context \"with invalid data\" do\n      let(:hiera_config) { <<~CONF  }\n      ---\n      version: 5\n      plan_hierarchy:\n        - pop: the question\n      CONF\n\n      it \"raises a validation error\" do\n        configure_hiera_for_plan_hierarchy(data, hiera_config)\n\n        create_manifest_in_module(\"testing\", \"binding.pp\",\n                                  <<-MANIFEST)\n      class testing::binding($value) {}\n        MANIFEST\n\n        expect { compile_to_catalog(\"include testing::binding\") }\n          .to raise_error(/entry 'plan_hierarchy' index 0 unrecognized key 'pop'/)\n      end\n    end\n\n    context \"with Hiera 3\" do\n      let(:hiera_config) { <<~CONF  }\n      ---\n      plan_hierarchy: ['global']\n      CONF\n\n      it \"errors with plan_hierarchy key\" do\n        configure_hiera_for_plan_hierarchy(data, hiera_config)\n\n        create_manifest_in_module(\"testing\", \"binding.pp\",\n                                  <<-MANIFEST)\n      class testing::binding($value) {}\n        MANIFEST\n\n        expect { compile_to_catalog(\"include testing::binding\") }\n          .to raise_error(/unrecognized key 'plan_hierarchy'/)\n\n      end\n    end\n  end\n\n\n  def configure_hiera_for_one_tier(data)\n    hiera_config_file = tmpfile(\"hiera.yaml\")\n\n    File.open(hiera_config_file, 'w:UTF-8') do |f|\n      f.write(\"---\n        :yaml:\n          :datadir: #{dir}\n        :hierarchy: ['global']\n        :logger: 'noop'\n        :backends: ['yaml']\n      \")\n    end\n\n    data.each do | file, contents |\n      File.open(File.join(dir, \"#{file}.yaml\"), 'w:UTF-8') do |f|\n        f.write(YAML.dump(contents))\n      end\n    end\n\n    Puppet[:hiera_config] = hiera_config_file\n  end\n\n  def configure_hiera_for_plan_hierarchy(data, config)\n    hiera_config_file = tmpfile(\"hiera.yaml\")\n\n    File.open(hiera_config_file, 'w:UTF-8') do |f|\n      f.write(config)\n    end\n\n    data.each do | file, contents |\n      File.open(File.join(dir, \"#{file}.yaml\"), 'w:UTF-8') do |f|\n        f.write(YAML.dump(contents))\n      end\n    end\n\n    Puppet[:hiera_config] = hiera_config_file\n  end\n\n  def configure_hiera_for_two_tier(data)\n    hiera_config_file = tmpfile(\"hiera.yaml\")\n\n    File.open(hiera_config_file, 'w:UTF-8') do |f|\n      f.write(\"---\n        :yaml:\n          :datadir: #{dir}\n        :hierarchy: ['agent.example.com', 'global']\n        :logger: 'noop'\n        :backends: ['yaml']\n      \")\n    end\n\n    data.each do | file, contents |\n      File.open(File.join(dir, \"#{file}.yaml\"), 'w:UTF-8') do |f|\n        f.write(YAML.dump(contents))\n      end\n    end\n\n    Puppet[:hiera_config] = hiera_config_file\n  end\n\n  def create_manifest_in_module(module_name, name, manifest)\n    module_dir = File.join(dir, module_name, 'manifests')\n    FileUtils.mkdir_p(module_dir)\n\n    File.open(File.join(module_dir, name), 'w:UTF-8') do |f|\n      f.write(manifest)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/defaults_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/defaults'\n\ndescribe \"Puppet defaults\" do\n\n  describe \"when default_manifest is set\" do\n    it \"returns ./manifests by default\" do\n      expect(Puppet[:default_manifest]).to eq('./manifests')\n    end\n  end\n\n  describe \"when disable_per_environment_manifest is set\" do\n    it \"returns false by default\" do\n      expect(Puppet[:disable_per_environment_manifest]).to eq(false)\n    end\n\n    it \"errors when set to true and default_manifest is not an absolute path\" do\n      expect {\n        Puppet[:default_manifest] = './some/relative/manifest.pp'\n        Puppet[:disable_per_environment_manifest] = true\n      }.to raise_error Puppet::Settings::ValidationError, /'default_manifest' setting must be.*absolute/\n    end\n  end\n\n  describe \"when setting the :masterport\" do\n    it \"should also set :serverport to the same value\" do\n      Puppet.settings[:masterport] = 3939\n      expect(Puppet.settings[:serverport]).to eq(3939)\n    end\n\n    it \"should not overwrite :serverport if explicitly set\" do\n      Puppet.settings[:serverport] = 9000\n      Puppet.settings[:masterport] = 9001\n      expect(Puppet.settings[:serverport]).to eq(9000)\n    end\n  end\n\n  describe \"when setting the :factpath\" do\n    it \"should add the :factpath to Facter's search paths\" do\n      expect(Facter).to receive(:search).with(\"/my/fact/path\")\n\n      Puppet.settings[:factpath] = \"/my/fact/path\"\n    end\n  end\n\n  describe \"when setting the :certname\" do\n    it \"should fail if the certname is not downcased\" do\n      expect { Puppet.settings[:certname] = \"Host.Domain.Com\" }.to raise_error(ArgumentError)\n    end\n\n    it 'can set it to a value containing all digits' do\n      Puppet.settings[:certname] = \"000000000180\"\n      expect(Puppet.settings[:certname]).to eq(\"000000000180\")\n    end\n  end\n\n  describe \"when setting :node_name_value\" do\n    it \"should default to the value of :certname\" do\n      Puppet.settings[:certname] = 'blargle'\n      expect(Puppet.settings[:node_name_value]).to eq('blargle')\n    end\n  end\n\n  describe \"when setting the :node_name_fact\" do\n    it \"should fail when also setting :node_name_value\" do\n      expect do\n        Puppet.settings[:node_name_value] = \"some value\"\n        Puppet.settings[:node_name_fact] = \"some_fact\"\n      end.to raise_error(\"Cannot specify both the node_name_value and node_name_fact settings\")\n    end\n\n    it \"should not fail when using the default for :node_name_value\" do\n      expect do\n        Puppet.settings[:node_name_fact] = \"some_fact\"\n      end.not_to raise_error\n    end\n  end\n\n  it \"should have a clientyamldir setting\" do\n    expect(Puppet.settings[:clientyamldir]).not_to be_nil\n  end\n\n  it \"should have different values for the yamldir and clientyamldir\" do\n    expect(Puppet.settings[:yamldir]).not_to eq(Puppet.settings[:clientyamldir])\n  end\n\n  it \"should have a client_datadir setting\" do\n    expect(Puppet.settings[:client_datadir]).not_to be_nil\n  end\n\n  it \"should have different values for the server_datadir and client_datadir\" do\n    expect(Puppet.settings[:server_datadir]).not_to eq(Puppet.settings[:client_datadir])\n  end\n\n  # See #1232\n  it \"should not specify a user or group for the clientyamldir\" do\n    expect(Puppet.settings.setting(:clientyamldir).owner).to be_nil\n    expect(Puppet.settings.setting(:clientyamldir).group).to be_nil\n  end\n\n  it \"should use the service user and group for the yamldir\" do\n    allow(Puppet.settings).to receive(:service_user_available?).and_return(true)\n    allow(Puppet.settings).to receive(:service_group_available?).and_return(true)\n    expect(Puppet.settings.setting(:yamldir).owner).to eq(Puppet.settings[:user])\n    expect(Puppet.settings.setting(:yamldir).group).to eq(Puppet.settings[:group])\n  end\n\n  it \"should specify that the host private key should be owned by the service user\" do\n    allow(Puppet.settings).to receive(:service_user_available?).and_return(true)\n    expect(Puppet.settings.setting(:hostprivkey).owner).to eq(Puppet.settings[:user])\n  end\n\n  it \"should specify that the host certificate should be owned by the service user\" do\n    allow(Puppet.settings).to receive(:service_user_available?).and_return(true)\n    expect(Puppet.settings.setting(:hostcert).owner).to eq(Puppet.settings[:user])\n  end\n\n  [:modulepath, :factpath].each do |setting|\n    it \"should configure '#{setting}' not to be a file setting, so multi-directory settings are acceptable\" do\n      expect(Puppet.settings.setting(setting)).to be_instance_of(Puppet::Settings::PathSetting)\n    end\n  end\n\n  describe \"on a Unix-like platform it\", :if => Puppet.features.posix? do\n    it \"should add /usr/sbin and /sbin to the path if they're not there\" do\n      Puppet::Util.withenv(\"PATH\" => \"/usr/bin#{File::PATH_SEPARATOR}/usr/local/bin\") do\n        Puppet.settings[:path] = \"none\" # this causes it to ignore the setting\n        expect(ENV[\"PATH\"].split(File::PATH_SEPARATOR)).to be_include(\"/usr/sbin\")\n        expect(ENV[\"PATH\"].split(File::PATH_SEPARATOR)).to be_include(\"/sbin\")\n      end\n    end\n  end\n\n  describe \"on a Windows-like platform it\", :if => Puppet::Util::Platform.windows? do\n    let (:rune_utf8) { \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\" }\n\n    it \"path should not add anything\" do\n      path = \"c:\\\\windows\\\\system32#{File::PATH_SEPARATOR}c:\\\\windows\"\n      Puppet::Util.withenv(\"PATH\" => path) do\n        Puppet.settings[:path] = \"none\" # this causes it to ignore the setting\n        expect(ENV[\"PATH\"]).to eq(path)\n      end\n    end\n\n    it \"path should support UTF8 characters\" do\n      path = \"c:\\\\windows\\\\system32#{File::PATH_SEPARATOR}c:\\\\windows#{File::PATH_SEPARATOR}C:\\\\\" + rune_utf8\n      Puppet::Util.withenv(\"PATH\" => path) do\n        Puppet.settings[:path] = \"none\" # this causes it to ignore the setting\n\n        expect(ENV['Path']).to eq(path)\n      end\n    end\n  end\n\n  it \"should default to json for the preferred serialization format\" do\n    expect(Puppet.settings.value(:preferred_serialization_format)).to eq(\"json\")\n  end\n\n  it \"should default to true the disable_i18n setting\" do\n    expect(Puppet.settings.value(:disable_i18n)).to eq(true)\n  end\n\n  it \"should have a setting for determining the configuration version and should default to an empty string\" do\n    expect(Puppet.settings[:config_version]).to eq(\"\")\n  end\n\n  describe \"when enabling reports\" do\n    it \"should use the default server value when report server is unspecified\" do\n      Puppet.settings[:server] = \"server\"\n      expect(Puppet.settings[:report_server]).to eq(\"server\")\n    end\n\n    it \"should use the default serverport value when report port is unspecified\" do\n      Puppet.settings[:serverport] = \"1234\"\n      expect(Puppet.settings[:report_port]).to eq(1234)\n    end\n\n    it \"should use the default masterport value when report port is unspecified\" do\n      Puppet.settings[:masterport] = \"1234\"\n      expect(Puppet.settings[:report_port]).to eq(1234)\n    end\n\n    it \"should use report_port when set\" do\n      Puppet.settings[:serverport] = \"1234\"\n      Puppet.settings[:report_port] = \"5678\"\n      expect(Puppet.settings[:report_port]).to eq(5678)\n    end\n  end\n\n  it \"should have a 'prerun_command' that defaults to the empty string\" do\n    expect(Puppet.settings[:prerun_command]).to eq(\"\")\n  end\n\n  it \"should have a 'postrun_command' that defaults to the empty string\" do\n    expect(Puppet.settings[:postrun_command]).to eq(\"\")\n  end\n\n  it \"should have a 'certificate_revocation' setting that defaults to true\" do\n    expect(Puppet.settings[:certificate_revocation]).to be_truthy\n  end\n\n  describe \"reportdir\" do\n    subject { Puppet.settings[:reportdir] }\n    it { is_expected.to eq(\"#{Puppet[:vardir]}/reports\") }\n  end\n\n  describe \"reporturl\" do\n    subject { Puppet.settings[:reporturl] }\n    it { is_expected.to eq(\"http://localhost:3000/reports/upload\") }\n  end\n\n  describe \"when configuring color\" do\n    subject { Puppet.settings[:color] }\n    it { is_expected.to eq(\"ansi\") }\n  end\n\n  describe \"daemonize\" do\n    it \"should default to true\", :unless => Puppet::Util::Platform.windows? do\n      expect(Puppet.settings[:daemonize]).to eq(true)\n    end\n\n    describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should default to false\" do\n        expect(Puppet.settings[:daemonize]).to eq(false)\n      end\n\n      it \"should raise an error if set to true\" do\n        expect { Puppet.settings[:daemonize] = true }.to raise_error(/Cannot daemonize on Windows/)\n      end\n    end\n  end\n\n  describe \"diff\" do\n    it \"should default to 'diff' on POSIX\", :unless => Puppet::Util::Platform.windows? do\n      expect(Puppet.settings[:diff]).to eq('diff')\n    end\n\n    it \"should default to '' on Windows\", :if => Puppet::Util::Platform.windows? do\n      expect(Puppet.settings[:diff]).to eq('')\n    end\n  end\n\n  describe \"when configuring hiera\" do\n    it \"should have a hiera_config setting\" do\n      expect(Puppet.settings[:hiera_config]).not_to be_nil\n    end\n  end\n\n  describe \"when configuring the data_binding terminus\" do\n    it \"should have a data_binding_terminus setting\" do\n      expect(Puppet.settings[:data_binding_terminus]).not_to be_nil\n    end\n\n    it \"should be set to hiera by default\" do\n      expect(Puppet.settings[:data_binding_terminus]).to eq(:hiera)\n    end\n\n    it \"to be neither 'hiera' nor 'none', a deprecation warning is logged\" do\n      expect(@logs).to eql([])\n      Puppet[:data_binding_terminus] = 'magic'\n      expect(@logs[0].to_s).to match(/Setting 'data_binding_terminus' is deprecated/)\n    end\n\n    it \"to not log a warning if set to 'none' or 'hiera'\" do\n      expect(@logs).to eql([])\n      Puppet[:data_binding_terminus] = 'none'\n      Puppet[:data_binding_terminus] = 'hiera'\n      expect(@logs).to eql([])\n    end\n  end\n\n  describe \"agent_catalog_run_lockfile\" do\n    it \"(#2888) is not a file setting so it is absent from the Settings catalog\" do\n      expect(Puppet.settings.setting(:agent_catalog_run_lockfile)).not_to be_a_kind_of Puppet::Settings::FileSetting\n      expect(Puppet.settings.setting(:agent_catalog_run_lockfile)).to be_a Puppet::Settings::StringSetting\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/directory_environments_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"directory environments\" do\n  let(:args) { ['--configprint', 'modulepath', '--environment', 'direnv'] }\n  let(:puppet) { Puppet::Application[:apply] }\n\n  context \"with a single directory environmentpath\" do\n    before(:each) do\n      environmentdir = PuppetSpec::Files.tmpdir('envpath')\n      Puppet[:environmentpath] = environmentdir\n      FileUtils.mkdir_p(environmentdir + \"/direnv/modules\")\n    end\n\n    it \"config prints the environments modulepath\" do\n      Puppet.settings.initialize_global_settings(args)\n      expect {\n        puppet.run\n      }.to exit_with(0)\n       .and output(%r{/direnv/modules}).to_stdout\n    end\n\n    it \"config prints the cli --modulepath despite environment\" do\n      args << '--modulepath' << '/completely/different'\n      Puppet.settings.initialize_global_settings(args)\n      expect {\n        puppet.run\n      }.to exit_with(0)\n       .and output(%r{/completely/different}).to_stdout\n    end\n\n    it 'given an 8.3 style path on Windows, will config print an expanded path',\n      :if => Puppet::Util::Platform.windows? do\n      pending(\"GH runners seem to have disabled 8.3 support\")\n\n      # ensure an 8.3 style path is set for environmentpath\n      shortened = Puppet::Util::Windows::File.get_short_pathname(Puppet[:environmentpath])\n      expanded = Puppet::FileSystem.expand_path(shortened)\n\n      Puppet[:environmentpath] = shortened\n      expect(Puppet[:environmentpath]).to match(/~/)\n\n      Puppet.settings.initialize_global_settings(args)\n      expect {\n        puppet.run\n      }.to exit_with(0)\n       .and output(a_string_matching(expanded)).to_stdout\n    end\n  end\n\n  context \"with an environmentpath having multiple directories\" do\n    let(:args) { ['--configprint', 'modulepath', '--environment', 'otherdirenv'] }\n\n    before(:each) do\n      envdir1 = File.join(Puppet[:confdir], 'env1')\n      envdir2 = File.join(Puppet[:confdir], 'env2')\n      Puppet[:environmentpath] = [envdir1, envdir2].join(File::PATH_SEPARATOR)\n      FileUtils.mkdir_p(envdir2 + \"/otherdirenv/modules\")\n    end\n\n    it \"config prints a directory environment modulepath\" do\n      Puppet.settings.initialize_global_settings(args)\n      expect {\n        puppet.run\n      }.to exit_with(0)\n       .and output(%r{otherdirenv/modules}).to_stdout\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/environments/default_manifest_spec.rb",
    "content": "require 'spec_helper'\n\nmodule EnvironmentsDefaultManifestsSpec\ndescribe \"default manifests\" do\n\n  context \"puppet with default_manifest settings\" do\n    let(:confdir) { Puppet[:confdir] }\n    let(:environmentpath) { File.expand_path(\"envdir\", confdir) }\n\n    context \"relative default\" do\n      let(:testingdir) { File.join(environmentpath, \"testing\") }\n\n      before(:each) do\n        FileUtils.mkdir_p(testingdir)\n      end\n\n      it \"reads manifest from ./manifest of a basic directory environment\" do\n        manifestsdir = File.join(testingdir, \"manifests\")\n        FileUtils.mkdir_p(manifestsdir)\n\n        File.open(File.join(manifestsdir, \"site.pp\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"notify { 'ManifestFromRelativeDefault': }\")\n        end\n\n        File.open(File.join(confdir, \"puppet.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"environmentpath=#{environmentpath}\")\n        end\n\n        expect(a_catalog_compiled_for_environment('testing')).to(\n          include_resource('Notify[ManifestFromRelativeDefault]')\n        )\n      end\n    end\n\n    context \"set absolute\" do\n      let(:testingdir) { File.join(environmentpath, \"testing\") }\n\n      before(:each) do\n        FileUtils.mkdir_p(testingdir)\n      end\n\n      it \"reads manifest from an absolute default_manifest\" do\n        manifestsdir = File.expand_path(\"manifests\", confdir)\n        FileUtils.mkdir_p(manifestsdir)\n\n        File.open(File.join(confdir, \"puppet.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(<<-EOF)\n  environmentpath=#{environmentpath}\n  default_manifest=#{manifestsdir}\n          EOF\n        end\n\n        File.open(File.join(manifestsdir, \"site.pp\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"notify { 'ManifestFromAbsoluteDefaultManifest': }\")\n        end\n\n        expect(a_catalog_compiled_for_environment('testing')).to(\n          include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')\n        )\n      end\n\n      it \"reads manifest from directory environment manifest when environment.conf manifest set\" do\n        default_manifestsdir = File.expand_path(\"manifests\", confdir)\n        File.open(File.join(confdir, \"puppet.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(<<-EOF)\n  environmentpath=#{environmentpath}\n  default_manifest=#{default_manifestsdir}\n          EOF\n        end\n\n        manifestsdir = File.join(testingdir, \"special_manifests\")\n        FileUtils.mkdir_p(manifestsdir)\n\n        File.open(File.join(manifestsdir, \"site.pp\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"notify { 'ManifestFromEnvironmentConfManifest': }\")\n        end\n\n        File.open(File.join(testingdir, \"environment.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"manifest=./special_manifests\")\n        end\n\n        expect(a_catalog_compiled_for_environment('testing')).to(\n          include_resource('Notify[ManifestFromEnvironmentConfManifest]')\n        )\n        expect(Puppet[:default_manifest]).to eq(default_manifestsdir)\n      end\n\n      it \"ignores manifests in the local ./manifests if default_manifest specifies another directory\" do\n        default_manifestsdir = File.expand_path(\"manifests\", confdir)\n        FileUtils.mkdir_p(default_manifestsdir)\n\n        File.open(File.join(confdir, \"puppet.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(<<-EOF)\n  environmentpath=#{environmentpath}\n  default_manifest=#{default_manifestsdir}\n          EOF\n        end\n\n        File.open(File.join(default_manifestsdir, \"site.pp\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"notify { 'ManifestFromAbsoluteDefaultManifest': }\")\n        end\n\n        implicit_manifestsdir = File.join(testingdir, \"manifests\")\n        FileUtils.mkdir_p(implicit_manifestsdir)\n\n        File.open(File.join(implicit_manifestsdir, \"site.pp\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"notify { 'ManifestFromImplicitRelativeEnvironmentManifestDirectory': }\")\n        end\n\n        expect(a_catalog_compiled_for_environment('testing')).to(\n          include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')\n        )\n      end\n\n    end\n\n    context \"with disable_per_environment_manifest true\" do\n      let(:manifestsdir) { File.expand_path(\"manifests\", confdir) }\n      let(:testingdir) { File.join(environmentpath, \"testing\") }\n\n      before(:each) do\n        FileUtils.mkdir_p(testingdir)\n      end\n\n      before(:each) do\n        FileUtils.mkdir_p(manifestsdir)\n\n        File.open(File.join(confdir, \"puppet.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(<<-EOF)\n  environmentpath=#{environmentpath}\n  default_manifest=#{manifestsdir}\n  disable_per_environment_manifest=true\n          EOF\n        end\n\n        File.open(File.join(manifestsdir, \"site.pp\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"notify { 'ManifestFromAbsoluteDefaultManifest': }\")\n        end\n      end\n\n      it \"reads manifest from the default manifest setting\" do\n        expect(a_catalog_compiled_for_environment('testing')).to(\n          include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')\n        )\n      end\n\n      it \"refuses to compile if environment.conf specifies a different manifest\" do\n        File.open(File.join(testingdir, \"environment.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"manifest=./special_manifests\")\n        end\n\n        expect { a_catalog_compiled_for_environment('testing') }.to(\n          raise_error(Puppet::Error, /disable_per_environment_manifest.*environment.conf.*manifest.*conflict/)\n        )\n      end\n\n      it \"reads manifest from default_manifest setting when environment.conf has manifest set if setting equals default_manifest setting\" do\n        File.open(File.join(testingdir, \"environment.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"manifest=#{manifestsdir}\")\n        end\n\n        expect(a_catalog_compiled_for_environment('testing')).to(\n          include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')\n        )\n      end\n\n      it \"logs errors if environment.conf specifies a different manifest\" do\n        File.open(File.join(testingdir, \"environment.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(\"manifest=./special_manifests\")\n        end\n\n        Puppet.initialize_settings\n        expect(Puppet[:environmentpath]).to eq(environmentpath)\n        environment = Puppet.lookup(:environments).get('testing')\n        expect(environment.manifest).to eq(manifestsdir)\n        expect(@logs.first.to_s).to match(%r{disable_per_environment_manifest.*is true, but.*environment.*at #{testingdir}.*has.*environment.conf.*manifest.*#{testingdir}/special_manifests})\n      end\n\n      it \"raises an error if default_manifest is not absolute\" do\n        File.open(File.join(confdir, \"puppet.conf\"), \"w\", :encoding => Encoding::UTF_8) do |f|\n          f.puts(<<-EOF)\n  environmentpath=#{environmentpath}\n  default_manifest=./relative\n  disable_per_environment_manifest=true\n          EOF\n        end\n\n        expect { Puppet.initialize_settings }.to raise_error(Puppet::Settings::ValidationError, /default_manifest.*must be.*absolute.*when.*disable_per_environment_manifest.*true/)\n      end\n    end\n  end\n\n  RSpec::Matchers.define :include_resource do |expected|\n    match do |actual|\n      actual.resources.map(&:ref).include?(expected)\n    end\n\n    def failure_message\n      \"expected #{@actual.resources.map(&:ref)} to include #{expected}\"\n    end\n\n    def failure_message_when_negated\n      \"expected #{@actual.resources.map(&:ref)} not to include #{expected}\"\n    end\n  end\n\n  def a_catalog_compiled_for_environment(envname)\n    Puppet.initialize_settings\n    expect(Puppet[:environmentpath]).to eq(environmentpath)\n    node = Puppet::Node.new('testnode', :environment => 'testing')\n    expect(node.environment).to eq(Puppet.lookup(:environments).get('testing'))\n    Puppet::Parser::Compiler.compile(node)\n  end\nend\nend\n"
  },
  {
    "path": "spec/integration/environments/setting_hooks_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"setting hooks\" do\n  let(:confdir) { Puppet[:confdir] }\n  let(:environmentpath) { File.expand_path(\"envdir\", confdir) }\n\n  describe \"reproducing PUP-3500\" do\n    let(:productiondir) { File.join(environmentpath, \"production\") }\n\n    before(:each) do\n      FileUtils.mkdir_p(productiondir)\n    end\n\n    it \"accesses correct directory environment settings after initializing a setting with an on_write hook\" do\n      expect(Puppet.settings.setting(:strict).call_hook).to eq(:on_write_only)\n\n      File.open(File.join(confdir, \"puppet.conf\"), \"w:UTF-8\") do |f|\n        f.puts(\"environmentpath=#{environmentpath}\")\n        f.puts(\"certname=something\")\n      end\n\n      Puppet.initialize_settings\n      production_env = Puppet.lookup(:environments).get(:production)\n      expect(Puppet.settings.value(:manifest, production_env)).to eq(\"#{productiondir}/manifests\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/environments/settings_interpolation_spec.rb",
    "content": "require 'pp'\nrequire 'spec_helper'\nrequire 'puppet_spec/settings'\n\nmodule SettingsInterpolationSpec\ndescribe \"interpolating $environment\" do\n  include PuppetSpec::Settings\n\n  let(:confdir) { Puppet[:confdir] }\n  let(:cmdline_args) { ['--confdir', confdir, '--vardir', Puppet[:vardir], '--hiera_config', Puppet[:hiera_config]] }\n\n  shared_examples_for \"a setting that does not interpolate $environment\" do\n\n    before(:each) do\n      set_puppet_conf(confdir, <<-EOF)\n        environmentpath=$confdir/environments\n        #{setting}=#{value}\n      EOF\n    end\n\n    it \"does not interpolate $environment\" do\n      Puppet.initialize_settings(cmdline_args)\n      expect(Puppet[:environmentpath]).to eq(\"#{confdir}/environments\")\n      expect(Puppet[setting.intern]).to eq(expected)\n    end\n\n    it \"displays the interpolated value in the warning\" do\n      Puppet.initialize_settings(cmdline_args)\n      Puppet[setting.intern]\n      expect(@logs).to have_matching_log(/cannot interpolate \\$environment within '#{setting}'.*Its value will remain #{Regexp.escape(expected)}/)\n    end\n  end\n\n  describe \"config_version\" do\n    it \"interpolates $environment\" do\n      envname = 'testing'\n      setting = 'config_version'\n      value = '/some/script $environment'\n      expected = \"#{File.expand_path('/some/script')} testing\"\n\n      set_puppet_conf(confdir, <<-EOF)\n        environmentpath=$confdir/environments\n        environment=#{envname}\n      EOF\n\n      set_environment_conf(\"#{confdir}/environments\", envname, <<-EOF)\n        #{setting}=#{value}\n      EOF\n\n      Puppet.initialize_settings(cmdline_args)\n      expect(Puppet[:environmentpath]).to eq(\"#{confdir}/environments\")\n      environment = Puppet.lookup(:environments).get(envname)\n      expect(environment.config_version).to eq(expected)\n      expect(@logs).to be_empty\n    end\n  end\n\n  describe \"basemodulepath\" do\n    let(:setting) { \"basemodulepath\" }\n    let(:value) { \"$confdir/environments/$environment/modules:$confdir/environments/$environment/other_modules\" }\n    let(:expected) { \"#{confdir}/environments/$environment/modules:#{confdir}/environments/$environment/other_modules\" }\n\n    it_behaves_like \"a setting that does not interpolate $environment\"\n\n    it \"logs a single warning for multiple instaces of $environment in the setting\" do\n      set_puppet_conf(confdir, <<-EOF)\n        environmentpath=$confdir/environments\n        #{setting}=#{value}\n      EOF\n\n      Puppet.initialize_settings(cmdline_args)\n      expect(@logs.map(&:to_s).grep(/cannot interpolate \\$environment within '#{setting}'/).count).to eq(1)\n    end\n  end\n\n  describe \"environment\" do\n    let(:setting) { \"environment\" }\n    let(:value) { \"whatareyouthinking$environment\" }\n    let(:expected) { value }\n\n    it_behaves_like \"a setting that does not interpolate $environment\"\n  end\n\n  describe \"the default_manifest\" do\n    let(:setting) { \"default_manifest\" }\n    let(:value) { \"$confdir/manifests/$environment\" }\n    let(:expected) { \"#{confdir}/manifests/$environment\" }\n\n    it_behaves_like \"a setting that does not interpolate $environment\"\n  end\n\n  it \"does not interpolate $environment and logs a warning when interpolating environmentpath\" do\n    setting = 'environmentpath'\n    value = \"$confdir/environments/$environment\"\n    expected = \"#{confdir}/environments/$environment\"\n\n    set_puppet_conf(confdir, <<-EOF)\n      #{setting}=#{value}\n    EOF\n\n    Puppet.initialize_settings(cmdline_args)\n    expect(Puppet[setting.intern]).to eq(expected)\n    expect(@logs).to have_matching_log(/cannot interpolate \\$environment within '#{setting}'/)\n  end\nend\nend\n"
  },
  {
    "path": "spec/integration/environments/settings_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/settings'\n\ndescribe \"environment settings\" do\n  include PuppetSpec::Settings\n\n  let(:confdir) { Puppet[:confdir] }\n  let(:cmdline_args) { ['--confdir', confdir, '--vardir', Puppet[:vardir], '--hiera_config', Puppet[:hiera_config]] }\n  let(:environmentpath) { File.expand_path(\"envdir\", confdir) }\n  let(:testingdir) { File.join(environmentpath, \"testing\") }\n\n  before(:each) do\n    FileUtils.mkdir_p(testingdir)\n  end\n\n  def init_puppet_conf(settings = {})\n    set_puppet_conf(confdir, <<-EOF)\n      environmentpath=#{environmentpath}\n      #{settings.map { |k,v| \"#{k}=#{v}\" }.join(\"\\n\")}\n    EOF\n    Puppet.initialize_settings\n  end\n\n  it \"raises an error if you set manifest in puppet.conf\" do\n    expect { init_puppet_conf(\"manifest\" => \"/something\") }.to raise_error(Puppet::Settings::SettingsError, /Cannot set manifest.*in puppet.conf/)\n  end\n\n  it \"raises an error if you set modulepath in puppet.conf\" do\n    expect { init_puppet_conf(\"modulepath\" => \"/something\") }.to raise_error(Puppet::Settings::SettingsError, /Cannot set modulepath.*in puppet.conf/)\n  end\n\n  it \"raises an error if you set config_version in puppet.conf\" do\n    expect { init_puppet_conf(\"config_version\" => \"/something\") }.to raise_error(Puppet::Settings::SettingsError, /Cannot set config_version.*in puppet.conf/)\n  end\n\n  context \"when given an environment\" do\n    before(:each) do\n      init_puppet_conf\n    end\n\n    context \"without an environment.conf\" do\n      it \"reads manifest from environment.conf defaults\" do\n        expect(Puppet.settings.value(:manifest, :testing)).to eq(File.join(testingdir, \"manifests\"))\n      end\n\n      it \"reads modulepath from environment.conf defaults\" do\n        expect(Puppet.settings.value(:modulepath, :testing)).to match(/#{File.join(testingdir, \"modules\")}/)\n      end\n\n      it \"reads config_version from environment.conf defaults\" do\n        expect(Puppet.settings.value(:config_version, :testing)).to eq('')\n      end\n    end\n\n    context \"with an environment.conf\" do\n      before(:each) do\n        set_environment_conf(environmentpath, 'testing', <<-EOF)\n          manifest=/special/manifest\n          modulepath=/special/modulepath\n          config_version=/special/config_version\n        EOF\n      end\n\n      it \"reads the configured manifest\" do\n        expect(Puppet.settings.value(:manifest, :testing)).to eq(Puppet::FileSystem.expand_path('/special/manifest'))\n      end\n\n      it \"reads the configured modulepath\" do\n        expect(Puppet.settings.value(:modulepath, :testing)).to eq(Puppet::FileSystem.expand_path('/special/modulepath'))\n      end\n\n      it \"reads the configured config_version\" do\n        expect(Puppet.settings.value(:config_version, :testing)).to eq(Puppet::FileSystem.expand_path('/special/config_version'))\n      end\n    end\n\n    context \"with an environment.conf containing 8.3 style Windows paths\",\n      :if => Puppet::Util::Platform.windows? do\n\n      before(:each) do\n        # set 8.3 style Windows paths\n        @modulepath = Puppet::Util::Windows::File.get_short_pathname(PuppetSpec::Files.tmpdir('fakemodulepath'))\n\n        # for expansion to work, the file must actually exist\n        @manifest = PuppetSpec::Files.tmpfile('foo.pp', @modulepath)\n        # but tmpfile won't create an empty file\n        Puppet::FileSystem.touch(@manifest)\n        @manifest = Puppet::Util::Windows::File.get_short_pathname(@manifest)\n\n        set_environment_conf(environmentpath, 'testing', <<-EOF)\n          manifest=#{@manifest}\n          modulepath=#{@modulepath}\n        EOF\n      end\n\n      it \"reads the configured manifest as a fully expanded path\" do\n        expect(Puppet.settings.value(:manifest, :testing)).to eq(Puppet::FileSystem.expand_path(@manifest))\n      end\n\n      it \"reads the configured modulepath as a fully expanded path\" do\n        expect(Puppet.settings.value(:modulepath, :testing)).to eq(Puppet::FileSystem.expand_path(@modulepath))\n      end\n    end\n\n    context \"when environment name collides with a puppet.conf section\" do\n      let(:testingdir) { File.join(environmentpath, \"main\") }\n\n      it \"reads manifest from environment.conf defaults\" do\n        expect(Puppet.settings.value(:environmentpath)).to eq(environmentpath)\n        expect(Puppet.settings.value(:manifest, :main)).to eq(File.join(testingdir, \"manifests\"))\n      end\n\n      context \"and an environment.conf\" do\n        before(:each) do\n          set_environment_conf(environmentpath, 'main', <<-EOF)\n            manifest=/special/manifest\n          EOF\n        end\n\n        it \"reads manifest from environment.conf settings\" do\n          expect(Puppet.settings.value(:environmentpath)).to eq(environmentpath)\n          expect(Puppet.settings.value(:manifest, :main)).to eq(Puppet::FileSystem.expand_path(\"/special/manifest\"))\n        end\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/integration/http/client_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/https'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n  include_context \"https client\"\n\n  let(:wrong_hostname) { 'localhost' }\n  let(:client) { Puppet::HTTP::Client.new }\n  let(:ssl_provider) { Puppet::SSL::SSLProvider.new }\n  let(:root_context) { ssl_provider.create_root_context(cacerts: [https_server.ca_cert], crls: [https_server.ca_crl]) }\n\n  context \"when verifying an HTTPS server\" do\n    it \"connects over SSL\" do\n      https_server.start_server do |port|\n        res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: root_context})\n        expect(res).to be_success\n      end\n    end\n\n    it \"raises connection error if we can't connect\" do\n      Puppet[:http_connect_timeout] = '0s'\n\n      # get available port, but don't bind to it\n      tcps = TCPServer.new(\"127.0.0.1\", 0)\n      port = tcps.connect_address.ip_port\n\n      expect {\n        client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: root_context})\n      }.to raise_error(Puppet::HTTP::ConnectionError, %r{^Request to https://127.0.0.1:#{port} timed out connect operation after .* seconds})\n    end\n\n    it \"raises if the server's cert doesn't match the hostname we connected to\" do\n      https_server.start_server do |port|\n        expect {\n          client.get(URI(\"https://#{wrong_hostname}:#{port}\"), options: {ssl_context: root_context})\n        }.to raise_error { |err|\n          expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)\n          expect(err.message).to match(/Server hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)\n\n          md = err.message.match(/expected one of (.+)/)\n          expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')\n        }\n      end\n    end\n\n    it \"raises if the server's CA is unknown\" do\n      wrong_ca = cert_fixture('netlock-arany-utf8.pem')\n      alt_context = ssl_provider.create_root_context(cacerts: [wrong_ca], revocation: false)\n\n      https_server.start_server do |port|\n        expect {\n          client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: alt_context})\n        }.to raise_error(Puppet::SSL::CertVerifyError,\n                         %r{certificate verify failed.* .self.signed certificate in certificate chain for CN=Test CA.})\n      end\n    end\n\n    it \"prints TLS protocol and ciphersuite in debug\" do\n      Puppet[:log_level] = 'debug'\n      https_server.start_server do |port|\n        client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: root_context})\n        # TLS version string can be TLSv1 or TLSv1.[1-3], but not TLSv1.0\n        expect(@logs).to include(\n          an_object_having_attributes(level: :debug, message: /Using TLSv1(\\.[1-3])? with cipher .*/),\n        )\n      end\n    end\n  end\n\n  context \"with client certs\" do\n    let(:ctx_proc) {\n      -> ctx {\n        # configures the server to require the client to present a client cert\n        ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT\n      }\n    }\n\n    let(:cert_file) do\n      res = tmpfile('cert_file')\n      File.write(res, https_server.ca_cert)\n      res\n    end\n\n    it \"mutually authenticates the connection using an explicit context\" do\n      client_context = ssl_provider.create_context(\n        cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],\n        client_cert: https_server.server_cert, private_key: https_server.server_key\n      )\n\n      https_server.start_server(ctx_proc: ctx_proc) do |port|\n        res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: client_context})\n        expect(res).to be_success\n      end\n    end\n\n    it \"mutually authenticates the connection when the client and server certs are issued from different CAs\" do\n      # this is the client cert's CA, key and cert\n      Puppet[:localcacert] = fixtures('ssl/unknown-ca.pem')\n      Puppet[:hostprivkey] = fixtures('ssl/unknown-127.0.0.1-key.pem')\n      Puppet[:hostcert] = fixtures('ssl/unknown-127.0.0.1.pem')\n\n      # this is the server cert's CA that the client needs in order to authenticate the server\n      Puppet[:ssl_trust_store] = fixtures('ssl/ca.pem')\n\n      # need to pass both the client and server CAs. The former is needed so the server can authenticate our client cert\n      https_server = PuppetSpec::HTTPSServer.new(ca_cert: [cert_fixture('ca.pem'), cert_fixture('unknown-ca.pem')])\n      https_server.start_server(ctx_proc: ctx_proc) do |port|\n        res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {include_system_store: true})\n        expect(res).to be_success\n      end\n    end\n\n    it \"connects when the server's CA is in the system store and the connection is mutually authenticated using create_context\" do\n      Puppet::Util.withenv(\"SSL_CERT_FILE\" => cert_file) do\n        client_context = ssl_provider.create_context(\n          cacerts: [], crls: [],\n          client_cert: https_server.server_cert, private_key: https_server.server_key,\n          revocation: false, include_system_store: true\n        )\n        https_server.start_server(ctx_proc: ctx_proc) do |port|\n          res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: client_context})\n          expect(res).to be_success\n        end\n      end\n    end\n\n    it \"connects when the server's CA is in the system store and the connection is mutually authenticated using load_context\" do\n      Puppet::Util.withenv(\"SSL_CERT_FILE\" => cert_file) do\n        client_context = ssl_provider.load_context(revocation: false, include_system_store: true)\n        https_server.start_server(ctx_proc: ctx_proc) do |port|\n          res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: client_context})\n          expect(res).to be_success\n        end\n      end\n    end\n  end\n\n  context \"with a system trust store\" do\n    it \"connects when the client trusts the server's CA\" do\n      system_context = ssl_provider.create_system_context(cacerts: [https_server.ca_cert])\n\n      https_server.start_server do |port|\n        res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: system_context})\n        expect(res).to be_success\n      end\n    end\n\n    it \"connects when the server's CA is in the system store\" do\n      # create a temp cacert bundle\n      cert_file = tmpfile('cert_file')\n      File.write(cert_file, https_server.ca_cert)\n\n      # override path to system cacert bundle, this must be done before\n      # the SSLContext is created and the call to X509::Store.set_default_paths\n      Puppet::Util.withenv(\"SSL_CERT_FILE\" => cert_file) do\n        system_context = ssl_provider.create_system_context(cacerts: [])\n        https_server.start_server do |port|\n          res = client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: system_context})\n          expect(res).to be_success\n        end\n      end\n    end\n\n    it \"raises if the server's CA is not in the context or system store\" do\n      system_context = ssl_provider.create_system_context(cacerts: [cert_fixture('netlock-arany-utf8.pem')])\n\n      https_server.start_server do |port|\n        expect {\n          client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: system_context})\n        }.to raise_error(Puppet::SSL::CertVerifyError,\n                         %r{certificate verify failed.* .self.signed certificate in certificate chain for CN=Test CA.})\n      end\n    end\n  end\n\n  context 'ensure that retrying does not attempt to read the body after closing the connection' do\n    let(:client) { Puppet::HTTP::Client.new(retry_limit: 1) }\n    it 'raises a retry error instead' do\n      response_proc = -> (req, res) {\n        res['Retry-After'] = 1\n        res.status = 503\n      }\n\n      https_server.start_server(response_proc: response_proc) do |port|\n        uri = URI(\"https://127.0.0.1:#{port}\")\n        kwargs = {headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: root_context}}\n        expect{client.post(uri, '', **kwargs)}.to raise_error(Puppet::HTTP::TooManyRetryAfters)\n      end\n    end\n  end\n\n  context 'persistent connections' do\n    it \"detects when the server has closed the connection and reconnects\" do\n      Puppet[:http_debug] = true\n\n      # advertise that we support keep-alive, but we don't really\n      response_proc = -> (req, res) {\n        res['Connection'] = 'Keep-Alive'\n      }\n\n      https_server.start_server(response_proc: response_proc) do |port|\n        uri = URI(\"https://127.0.0.1:#{port}\")\n        kwargs = {headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: root_context}}\n\n        expect {\n          expect(client.post(uri, '', **kwargs)).to be_success\n          # the server closes its connection after each request, so posting\n          # again will force ruby to detect that the remote side closed the\n          # connection, and reconnect\n          expect(client.post(uri, '', **kwargs)).to be_success\n        }.to output(/Conn close because of EOF/).to_stderr\n      end\n    end\n  end\n\n  context 'ciphersuites' do\n    it \"does not connect when using an SSLv3 ciphersuite\", :if => Puppet::Util::Package.versioncmp(OpenSSL::OPENSSL_LIBRARY_VERSION.split[1], '1.1.1e') > 0 do\n      Puppet[:ciphers] = \"DES-CBC3-SHA\"\n\n      https_server.start_server do |port|\n        expect {\n          client.get(URI(\"https://127.0.0.1:#{port}\"), options: {ssl_context: root_context})\n        }.to raise_error(Puppet::HTTP::ConnectionError, /no cipher match|sslv3 alert handshake failure/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/indirector/catalog/compiler_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/resource/catalog'\n\nPuppet::Resource::Catalog.indirection.terminus(:compiler)\n\ndescribe Puppet::Resource::Catalog::Compiler do\n  before do\n    allow(Facter).to receive(:value).and_return(\"something\")\n    @catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n    @catalog.add_resource(@one = Puppet::Resource.new(:file, \"/one\"))\n    @catalog.add_resource(@two = Puppet::Resource.new(:file, \"/two\"))\n  end\n\n  it \"should remove virtual resources when filtering\" do\n    @one.virtual = true\n    expect(Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs).to eq([ @two.ref ])\n  end\n\n  it \"should not remove exported resources when filtering\" do\n    @one.exported = true\n    expect(Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.sort).to eq([ @one.ref, @two.ref ])\n  end\n\n  it \"should remove virtual exported resources when filtering\" do\n    @one.exported = true\n    @one.virtual = true\n    expect(Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs).to eq([ @two.ref ])\n  end\n\n  it \"should filter out virtual resources when finding a catalog\" do\n    Puppet[:node_terminus] = :memory\n    Puppet::Node.indirection.save(Puppet::Node.new(\"mynode\"))\n    allow(Puppet::Resource::Catalog.indirection.terminus).to receive(:extract_facts_from_request)\n    allow(Puppet::Resource::Catalog.indirection.terminus).to receive(:compile).and_return(@catalog)\n\n    @one.virtual = true\n\n    expect(Puppet::Resource::Catalog.indirection.find(\"mynode\").resource_refs).to eq([ @two.ref ])\n  end\n\n  it \"should not filter out exported resources when finding a catalog\" do\n    Puppet[:node_terminus] = :memory\n    Puppet::Node.indirection.save(Puppet::Node.new(\"mynode\"))\n    allow(Puppet::Resource::Catalog.indirection.terminus).to receive(:extract_facts_from_request)\n    allow(Puppet::Resource::Catalog.indirection.terminus).to receive(:compile).and_return(@catalog)\n\n    @one.exported = true\n\n    expect(Puppet::Resource::Catalog.indirection.find(\"mynode\").resource_refs.sort).to eq([ @one.ref, @two.ref ])\n  end\n\n  it \"should filter out virtual exported resources when finding a catalog\" do\n    Puppet[:node_terminus] = :memory\n    Puppet::Node.indirection.save(Puppet::Node.new(\"mynode\"))\n    allow(Puppet::Resource::Catalog.indirection.terminus).to receive(:extract_facts_from_request)\n    allow(Puppet::Resource::Catalog.indirection.terminus).to receive(:compile).and_return(@catalog)\n\n    @one.exported = true\n    @one.virtual = true\n\n    expect(Puppet::Resource::Catalog.indirection.find(\"mynode\").resource_refs).to eq([ @two.ref ])\n  end\n\n  it \"filters out virtual exported resources using the agent's production environment\" do\n    Puppet[:node_terminus] = :memory\n    Puppet::Node.indirection.save(Puppet::Node.new(\"mynode\"))\n\n    catalog_environment = nil\n    expect_any_instance_of(Puppet::Parser::Resource::Catalog).to receive(:to_resource) {catalog_environment = Puppet.lookup(:current_environment).name}\n\n    Puppet::Resource::Catalog.indirection.find(\"mynode\")\n    expect(catalog_environment).to eq(:production)\n  end\nend\n"
  },
  {
    "path": "spec/integration/indirector/direct_file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_content/file'\nrequire 'puppet/indirector/file_metadata/file'\n\ndescribe Puppet::Indirector::DirectFileServer, \" when interacting with the filesystem and the model\" do\n  include PuppetSpec::Files\n\n  before do\n    # We just test a subclass, since it's close enough.\n    @terminus = Puppet::Indirector::FileContent::File.new\n  end\n\n  it \"should return an instance of the model\" do\n    filepath = make_absolute(\"/path/to/my/file\")\n    expect(Puppet::FileSystem).to receive(:exist?).with(filepath).and_return(true)\n\n    expect(@terminus.find(@terminus.indirection.request(:find, Puppet::Util.path_to_uri(filepath).to_s, nil))).to be_instance_of(Puppet::FileServing::Content)\n  end\n\n  it \"should return an instance capable of returning its content\" do\n    filename = file_containing(\"testfile\", \"my content\")\n\n    instance = @terminus.find(@terminus.indirection.request(:find, Puppet::Util.path_to_uri(filename).to_s, nil))\n\n    expect(instance.content).to eq(\"my content\")\n  end\nend\n\ndescribe Puppet::Indirector::DirectFileServer, \" when interacting with FileServing::Fileset and the model\" do\n  include PuppetSpec::Files\n\n  matcher :file_with_content do |name, content|\n    match do |actual|\n      actual.full_path == name && actual.content == content\n    end\n  end\n\n  matcher :directory_named do |name|\n    match do |actual|\n      actual.full_path == name\n    end\n  end\n\n  it \"should return an instance for every file in the fileset\" do\n    path = tmpdir('direct_file_server_testing')\n    File.open(File.join(path, \"one\"), \"w\") { |f| f.print \"one content\" }\n    File.open(File.join(path, \"two\"), \"w\") { |f| f.print \"two content\" }\n\n    terminus = Puppet::Indirector::FileContent::File.new\n    request = terminus.indirection.request(:search, Puppet::Util.path_to_uri(path).to_s, nil, :recurse => true)\n\n    expect(terminus.search(request)).to contain_exactly(\n      file_with_content(File.join(path, \"one\"), \"one content\"),\n      file_with_content(File.join(path, \"two\"), \"two content\"),\n      directory_named(path))\n  end\nend\n\ndescribe Puppet::Indirector::DirectFileServer, \" when interacting with filesystem metadata\" do\n  include PuppetSpec::Files\n  include_context 'with supported checksum types'\n\n  before do\n    @terminus = Puppet::Indirector::FileMetadata::File.new\n  end\n\n  with_checksum_types(\"file_metadata\", \"testfile\") do\n    it \"should return the correct metadata\" do\n      request = @terminus.indirection.request(:find, Puppet::Util.path_to_uri(checksum_file).to_s, nil, :checksum_type => checksum_type)\n      result = @terminus.find(request)\n      expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata)\n    end\n  end\n\n  with_checksum_types(\"direct_file_server_testing\", \"testfile\") do\n    it \"search of FileServing::Fileset should return the correct metadata\" do\n      request = @terminus.indirection.request(:search, Puppet::Util.path_to_uri(env_path).to_s, nil, :recurse => true, :checksum_type => checksum_type)\n      result = @terminus.search(request)\n\n      expect(result).to_not be_nil\n      expect(result.length).to eq(2)\n      file, dir = result.partition {|x| x.relative_path == 'testfile'}\n      expect(file.length).to eq(1)\n      expect(dir.length).to eq(1)\n      expect_correct_checksum(dir[0], 'ctime', \"#{CHECKSUM_STAT_TIME}\", Puppet::FileServing::Metadata)\n      expect_correct_checksum(file[0], checksum_type, checksum, Puppet::FileServing::Metadata)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/indirector/facts/facter_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/indirector/facts/facter'\n\ndescribe Puppet::Node::Facts::Facter do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n  include PuppetSpec::Settings\n\n  before :each do\n    Puppet::Node::Facts.indirection.terminus_class = :facter\n  end\n\n  it \"preserves case in fact values\" do\n    Puppet.runtime[:facter].add(:downcase_test) do\n      setcode do\n        \"AaBbCc\"\n      end\n    end\n\n    allow(Facter).to receive(:reset)\n\n    cat = compile_to_catalog('notify { $downcase_test: }',\n                             Puppet::Node.indirection.find('foo'))\n    expect(cat.resource(\"Notify[AaBbCc]\")).to be\n  end\n\n  context \"resolving file based facts\" do\n    let(:factdir) { tmpdir('factdir') }\n\n    it \"should resolve custom facts\" do\n      test_module = File.join(factdir, 'module', 'lib', 'facter')\n      FileUtils.mkdir_p(test_module)\n\n      File.open(File.join(test_module, 'custom.rb'), 'wb') { |file| file.write(<<-EOF)}\n      Puppet.runtime[:facter].add(:custom) do\n        setcode do\n          Puppet.runtime[:facter].value('puppetversion')\n        end\n      end\n      EOF\n\n      Puppet.initialize_settings(['--modulepath', factdir])\n      apply = Puppet::Application.find(:apply).new(double('command_line', :subcommand_name => :apply, :args => ['--modulepath', factdir, '-e', 'notify { $custom: }']))\n\n      expect {\n        apply.run\n      }.to exit_with(0)\n       .and output(/defined 'message' as '#{Puppet.version}'/).to_stdout\n    end\n\n    it \"should resolve external facts\" do\n      external_fact = File.join(factdir, 'external.json')\n\n      File.open(external_fact, 'wb') { |file| file.write(<<-EOF)}\n        {\"foo\": \"bar\"}\n        EOF\n\n      Puppet.initialize_settings(['--pluginfactdest', factdir])\n      apply = Puppet::Application.find(:apply).new(double('command_line', :subcommand_name => :apply, :args => ['--pluginfactdest', factdir, '-e', 'notify { $foo: }']))\n\n      expect {\n        apply.run\n      }.to exit_with(0)\n       .and output(/defined 'message' as 'bar'/).to_stdout\n    end\n  end\n\n  context \"adding facts\" do\n    it \"adds the puppetversion fact\" do\n      allow(Facter).to receive(:reset)\n\n      cat = compile_to_catalog('notify { $::puppetversion: }',\n                               Puppet::Node.indirection.find('foo'))\n      expect(cat.resource(\"Notify[#{Puppet.version.to_s}]\")).to be\n    end\n\n    context \"when adding the agent_specified_environment fact\" do\n      it \"does not add the fact if the agent environment is not set\" do\n        expect do\n          compile_to_catalog('notify { $::agent_specified_environment: }',\n                             Puppet::Node.indirection.find('foo'))\n        end.to raise_error(Puppet::PreformattedError)\n      end\n\n      it \"does not add the fact if the agent environment is set in sections other than agent or main\" do\n        set_puppet_conf(Puppet[:confdir], <<~CONF)\n        [user]\n        environment=bar\n        CONF\n\n        Puppet.initialize_settings\n        expect do\n          compile_to_catalog('notify { $::agent_specified_environment: }',\n                             Puppet::Node.indirection.find('foo'))\n        end.to raise_error(Puppet::PreformattedError)\n      end\n\n      it \"adds the agent_specified_environment fact when set in the agent section in puppet.conf\" do\n        set_puppet_conf(Puppet[:confdir], <<~CONF)\n        [agent]\n        environment=bar\n        CONF\n\n        Puppet.initialize_settings\n        cat = compile_to_catalog('notify { $::agent_specified_environment: }',\n                                 Puppet::Node.indirection.find('foo'))\n        expect(cat.resource(\"Notify[bar]\")).to be\n      end\n\n      it \"prefers agent_specified_environment from main if set in section other than agent\" do\n        set_puppet_conf(Puppet[:confdir], <<~CONF)\n        [main]\n        environment=baz\n\n        [user]\n        environment=bar\n        CONF\n\n        Puppet.initialize_settings\n        cat = compile_to_catalog('notify { $::agent_specified_environment: }',\n                                 Puppet::Node.indirection.find('foo'))\n        expect(cat.resource(\"Notify[baz]\")).to be\n      end\n\n      it \"prefers agent_specified_environment from agent if set in multiple sections\" do\n        set_puppet_conf(Puppet[:confdir], <<~CONF)\n        [main]\n        environment=baz\n\n        [agent]\n        environment=bar\n        CONF\n\n        Puppet.initialize_settings\n        cat = compile_to_catalog('notify { $::agent_specified_environment: }',\n                                 Puppet::Node.indirection.find('foo'))\n        expect(cat.resource(\"Notify[bar]\")).to be\n      end\n\n      it \"adds the agent_specified_environment fact when set in puppet.conf\" do\n        set_puppet_conf(Puppet[:confdir], 'environment=bar')\n\n        Puppet.initialize_settings\n        cat = compile_to_catalog('notify { $::agent_specified_environment: }',\n                                 Puppet::Node.indirection.find('foo'))\n        expect(cat.resource(\"Notify[bar]\")).to be\n      end\n\n      it \"adds the agent_specified_environment fact when set via command-line\" do\n        Puppet.initialize_settings(['--environment', 'bar'])\n        cat = compile_to_catalog('notify { $::agent_specified_environment: }',\n                                 Puppet::Node.indirection.find('foo'))\n        expect(cat.resource(\"Notify[bar]\")).to be\n      end\n\n      it \"adds the agent_specified_environment fact, preferring cli, when set in puppet.conf and via command-line\" do\n        set_puppet_conf(Puppet[:confdir], 'environment=bar')\n\n        Puppet.initialize_settings(['--environment', 'baz'])\n        cat = compile_to_catalog('notify { $::agent_specified_environment: }',\n                                 Puppet::Node.indirection.find('foo'))\n        expect(cat.resource(\"Notify[baz]\")).to be\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/indirector/file_content/file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_content/file_server'\nrequire 'shared_behaviours/file_server_terminus'\n\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Indirector::FileContent::FileServer, \" when finding files\" do\n  it_should_behave_like \"Puppet::Indirector::FileServerTerminus\"\n  include PuppetSpec::Files\n\n  before do\n    @terminus = Puppet::Indirector::FileContent::FileServer.new\n    @test_class = Puppet::FileServing::Content\n    Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil)\n  end\n\n  it \"should find plugin file content in the environment specified in the request\" do\n    path = tmpfile(\"file_content_with_env\")\n\n    Dir.mkdir(path)\n\n    modpath = File.join(path, \"mod\")\n    FileUtils.mkdir_p(File.join(modpath, \"lib\"))\n    file = File.join(modpath, \"lib\", \"file.rb\")\n    File.open(file, \"wb\") { |f| f.write \"1\\r\\n\" }\n\n    Puppet.settings[:modulepath] = \"/no/such/file\"\n\n    env = Puppet::Node::Environment.create(:foo, [path])\n\n    result = Puppet::FileServing::Content.indirection.search(\"plugins\", :environment => env, :recurse => true)\n\n    expect(result).not_to be_nil\n    expect(result.length).to eq(2)\n    result.map {|x| expect(x).to be_instance_of(Puppet::FileServing::Content) }\n    expect(result.find {|x| x.relative_path == 'file.rb' }.content).to eq(\"1\\r\\n\")\n  end\n\n  it \"should find file content in modules\" do\n    path = tmpfile(\"file_content\")\n\n    Dir.mkdir(path)\n\n    modpath = File.join(path, \"mymod\")\n    FileUtils.mkdir_p(File.join(modpath, \"files\"))\n    file = File.join(modpath, \"files\", \"myfile\")\n    File.open(file, \"wb\") { |f| f.write \"1\\r\\n\" }\n\n    env = Puppet::Node::Environment.create(:foo, [path])\n\n    result = Puppet::FileServing::Content.indirection.find(\"modules/mymod/myfile\", :environment => env)\n\n    expect(result).not_to be_nil\n    expect(result).to be_instance_of(Puppet::FileServing::Content)\n    expect(result.content).to eq(\"1\\r\\n\")\n  end\n\n  it \"should find file content of tasks in modules\" do\n    path = tmpfile(\"task_file_content\")\n    Dir.mkdir(path)\n\n    modpath = File.join(path, \"myothermod\")\n    FileUtils.mkdir_p(File.join(modpath, \"tasks\"))\n    file = File.join(modpath, \"tasks\", \"mytask\")\n    File.open(file, \"wb\") { |f| f.write \"I'm a task\" }\n\n    env = Puppet::Node::Environment.create(:foo, [path])\n    result = Puppet::FileServing::Content.indirection.find(\"tasks/myothermod/mytask\", :environment => env)\n\n    expect(result).not_to be_nil\n    expect(result).to be_instance_of(Puppet::FileServing::Content)\n    expect(result.content).to eq(\"I'm a task\")\n  end\n\n  it \"should find file content in files when node name expansions are used\" do\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    allow(Puppet::FileSystem).to receive(:exist?).with(Puppet[:fileserverconfig]).and_return(true)\n\n    path = tmpfile(\"file_server_testing\")\n\n    Dir.mkdir(path)\n    subdir = File.join(path, \"mynode\")\n    Dir.mkdir(subdir)\n    File.open(File.join(subdir, \"myfile\"), \"wb\") { |f| f.write \"1\\r\\n\" }\n\n    # Use a real mount, so the integration is a bit deeper.\n    mount1 = Puppet::FileServing::Configuration::Mount::File.new(\"one\")\n    mount1.path = File.join(path, \"%h\")\n\n    parser = double('parser', :changed? => false)\n    allow(parser).to receive(:parse).and_return(\"one\" => mount1)\n\n    allow(Puppet::FileServing::Configuration::Parser).to receive(:new).and_return(parser)\n\n    path = File.join(path, \"myfile\")\n\n    env = Puppet::Node::Environment.create(:foo, [])\n\n    result = Puppet::FileServing::Content.indirection.find(\"one/myfile\", :environment => env, :node => \"mynode\")\n\n    expect(result).not_to be_nil\n    expect(result).to be_instance_of(Puppet::FileServing::Content)\n    expect(result.content).to eq(\"1\\r\\n\")\n  end\nend\n"
  },
  {
    "path": "spec/integration/indirector/file_metadata/file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_metadata/file_server'\nrequire 'shared_behaviours/file_server_terminus'\n\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Indirector::FileMetadata::FileServer, \" when finding files\" do\n  it_should_behave_like \"Puppet::Indirector::FileServerTerminus\"\n  include PuppetSpec::Files\n  include_context 'with supported checksum types'\n\n  before do\n    @terminus = Puppet::Indirector::FileMetadata::FileServer.new\n    @test_class = Puppet::FileServing::Metadata\n    Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil)\n  end\n\n  describe \"with a plugin environment specified in the request\" do\n    with_checksum_types(\"file_content_with_env\", \"mod/lib/file.rb\") do\n      it \"should return the correct metadata\" do\n        Puppet.settings[:modulepath] = \"/no/such/file\"\n        env = Puppet::Node::Environment.create(:foo, [env_path])\n        result = Puppet::FileServing::Metadata.indirection.search(\"plugins\", :environment => env, :checksum_type => checksum_type, :recurse => true)\n\n        expect(result).to_not be_nil\n        expect(result.length).to eq(2)\n        result.map {|x| expect(x).to be_instance_of(Puppet::FileServing::Metadata)}\n        expect_correct_checksum(result.find {|x| x.relative_path == 'file.rb'}, checksum_type, checksum, Puppet::FileServing::Metadata)\n      end\n    end\n  end\n\n  describe \"in modules\" do\n    with_checksum_types(\"file_content\", \"mymod/files/myfile\") do\n      it \"should return the correct metadata\" do\n        env = Puppet::Node::Environment.create(:foo, [env_path])\n        result = Puppet::FileServing::Metadata.indirection.find(\"modules/mymod/myfile\", :environment => env, :checksum_type => checksum_type)\n        expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata)\n      end\n    end\n  end\n\n  describe \"that are tasks in modules\" do\n    with_checksum_types(\"task_file_content\", \"mymod/tasks/mytask\") do\n      it \"should return the correct metadata\" do\n        env = Puppet::Node::Environment.create(:foo, [env_path])\n        result = Puppet::FileServing::Metadata.indirection.find(\"tasks/mymod/mytask\", :environment => env, :checksum_type => checksum_type)\n        expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata)\n      end\n    end\n  end\n\n  describe \"when node name expansions are used\" do\n    with_checksum_types(\"file_server_testing\", \"mynode/myfile\") do\n      it \"should return the correct metadata\" do\n        allow(Puppet::FileSystem).to receive(:exist?).with(checksum_file).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(Puppet[:fileserverconfig]).and_return(true)\n\n        # Use a real mount, so the integration is a bit deeper.\n        mount1 = Puppet::FileServing::Configuration::Mount::File.new(\"one\")\n        mount1.path = File.join(env_path, \"%h\")\n\n        parser = double('parser', :changed? => false)\n        allow(parser).to receive(:parse).and_return(\"one\" => mount1)\n\n        allow(Puppet::FileServing::Configuration::Parser).to receive(:new).and_return(parser)\n        env = Puppet::Node::Environment.create(:foo, [])\n\n        result = Puppet::FileServing::Metadata.indirection.find(\"one/myfile\", :environment => env, :node => \"mynode\", :checksum_type => checksum_type)\n        expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/l10n/compiler_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'compiler localization' do\n  include_context 'l10n', 'ja'\n\n  let(:envdir) { File.join(my_fixture_dir, '..', 'envs') }\n  let(:environments) do\n    Puppet::Environments::Cached.new(\n      Puppet::Environments::Directories.new(envdir, [])\n    )\n  end\n  let(:env) { Puppet::Node::Environment.create(:prod, [File.join(envdir, 'prod', 'modules')]) }\n  let(:node) { Puppet::Node.new('test', :environment => env) }\n\n  around(:each) do |example|\n    Puppet.override(current_environment: env,\n                    loaders: Puppet::Pops::Loaders.new(env),\n                    environments: environments) do\n      example.run\n    end\n  end\n\n  it 'localizes strings in functions' do\n    Puppet[:code] = <<~END\n      notify { 'demo':\n        message => l10n()\n      }\n    END\n\n    Puppet::Resource::Catalog.indirection.terminus_class = :compiler\n    catalog = Puppet::Resource::Catalog.indirection.find(node.name)\n    resource = catalog.resource(:notify, 'demo')\n\n    expect(resource).to be\n    expect(resource[:message]).to eq(\"それは楽しい時間です\")\n  end\nend\n"
  },
  {
    "path": "spec/integration/network/formats_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/formats'\n\nclass PsonIntTest\n  attr_accessor :string\n  def ==(other)\n    other.class == self.class and string == other.string\n  end\n\n  def self.from_data_hash(data)\n    new(data[0])\n  end\n\n  def initialize(string)\n    @string = string\n  end\n\n  def to_pson(*args)\n    {\n      'type' => self.class.name,\n      'data' => [@string]\n    }.to_pson(*args)\n  end\n\n  def self.canonical_order(s)\n    s.gsub(/\\{\"data\":\\[(.*?)\\],\"type\":\"PsonIntTest\"\\}/,'{\"type\":\"PsonIntTest\",\"data\":[\\1]}')\n  end\n\nend\n\ndescribe Puppet::Network::FormatHandler.format(:s) do\n  before do\n    @format = Puppet::Network::FormatHandler.format(:s)\n  end\n\n  it \"should support certificates\" do\n    expect(@format).to be_supported(Puppet::SSL::Certificate)\n  end\n\n  it \"should not support catalogs\" do\n    expect(@format).not_to be_supported(Puppet::Resource::Catalog)\n  end\nend\n\ndescribe Puppet::Network::FormatHandler.format(:pson), if: Puppet.features.pson? do\n  before do\n    @pson = Puppet::Network::FormatHandler.format(:pson)\n  end\n\n  it \"should be able to render an instance to pson\" do\n    instance = PsonIntTest.new(\"foo\")\n    expect(PsonIntTest.canonical_order(@pson.render(instance))).to eq(PsonIntTest.canonical_order('{\"type\":\"PsonIntTest\",\"data\":[\"foo\"]}' ))\n  end\n\n  it \"should be able to render arrays to pson\" do\n    expect(@pson.render([1,2])).to eq('[1,2]')\n  end\n\n  it \"should be able to render arrays containing hashes to pson\" do\n    expect(@pson.render([{\"one\"=>1},{\"two\"=>2}])).to eq('[{\"one\":1},{\"two\":2}]')\n  end\n\n  it \"should be able to render multiple instances to pson\" do\n    one = PsonIntTest.new(\"one\")\n    two = PsonIntTest.new(\"two\")\n\n    expect(PsonIntTest.canonical_order(@pson.render([one,two]))).to eq(PsonIntTest.canonical_order('[{\"type\":\"PsonIntTest\",\"data\":[\"one\"]},{\"type\":\"PsonIntTest\",\"data\":[\"two\"]}]'))\n  end\n\n  it \"should be able to intern pson into an instance\" do\n    expect(@pson.intern(PsonIntTest, '{\"type\":\"PsonIntTest\",\"data\":[\"foo\"]}')).to eq(PsonIntTest.new(\"foo\"))\n  end\n\n  it \"should be able to intern pson with no class information into an instance\" do\n    expect(@pson.intern(PsonIntTest, '[\"foo\"]')).to eq(PsonIntTest.new(\"foo\"))\n  end\n\n  it \"should be able to intern multiple instances from pson\" do\n    expect(@pson.intern_multiple(PsonIntTest, '[{\"type\": \"PsonIntTest\", \"data\": [\"one\"]},{\"type\": \"PsonIntTest\", \"data\": [\"two\"]}]')).to eq([\n      PsonIntTest.new(\"one\"), PsonIntTest.new(\"two\")\n    ])\n  end\n\n  it \"should be able to intern multiple instances from pson with no class information\" do\n    expect(@pson.intern_multiple(PsonIntTest, '[[\"one\"],[\"two\"]]')).to eq([\n      PsonIntTest.new(\"one\"), PsonIntTest.new(\"two\")\n    ])\n  end\nend\n"
  },
  {
    "path": "spec/integration/network/http/api/indirected_routes_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/network/http'\nrequire 'puppet/network/http/api/indirected_routes'\nrequire 'puppet/indirector_proxy'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/network'\nrequire 'puppet/util/json'\nrequire 'puppet/file_serving/content'\nrequire 'puppet/file_serving/metadata'\n\ndescribe Puppet::Network::HTTP::API::IndirectedRoutes do\n  include PuppetSpec::Files\n  include PuppetSpec::Network\n  include_context 'with supported checksum types'\n\n  describe \"when running the master application\" do\n    before :each do\n      Puppet::FileServing::Content.indirection.terminus_class = :file_server\n      Puppet::FileServing::Metadata.indirection.terminus_class = :file_server\n\n      Puppet::FileBucket::File.indirection.terminus_class = :file\n    end\n\n    describe \"using Puppet API to request file metadata\" do\n      let(:handler) { Puppet::Network::HTTP::API::IndirectedRoutes.new }\n      let(:response) { Puppet::Network::HTTP::MemoryResponse.new }\n\n      with_checksum_types 'file_content', 'lib/files/file.rb' do\n        before :each do\n          Puppet.settings[:modulepath] = env_path\n        end\n\n        it \"should find the file metadata with expected checksum\" do\n          request = a_request_that_finds(Puppet::IndirectorProxy.new(\"modules/lib/file.rb\", \"file_metadata\"),\n                                         {:accept_header => 'unknown, application/json'},\n                                         {:environment => 'production', :checksum_type => checksum_type})\n          handler.call(request, response)\n          resp = Puppet::Util::Json.load(response.body)\n\n          expect(resp['checksum']['type']).to eq(checksum_type)\n          expect(checksum_valid(checksum_type, checksum, resp['checksum']['value'])).to be_truthy\n        end\n\n        it \"should search for the file metadata with expected checksum\" do\n          request = a_request_that_searches(Puppet::IndirectorProxy.new(\"modules/lib\", \"file_metadata\"),\n                                            {:accept_header => 'unknown, application/json'},\n                                            {:environment => 'production', :checksum_type => checksum_type, :recurse => 'yes'})\n          handler.call(request, response)\n          resp = Puppet::Util::Json.load(response.body)\n\n          expect(resp.length).to eq(2)\n          file = resp.find {|x| x['relative_path'] == 'file.rb'}\n\n          expect(file['checksum']['type']).to eq(checksum_type)\n          expect(checksum_valid(checksum_type, checksum, file['checksum']['value'])).to be_truthy\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/network/http_pool_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/https'\nrequire 'puppet_spec/files'\nrequire 'puppet/network/http_pool'\n\ndescribe Puppet::Network::HttpPool, unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  before :all do\n    WebMock.disable!\n  end\n\n  after :all do\n    WebMock.enable!\n  end\n\n  before :each do\n    # make sure we don't take too long\n    Puppet[:http_connect_timeout] = '5s'\n  end\n\n  let(:hostname) { '127.0.0.1' }\n  let(:wrong_hostname) { 'localhost' }\n  let(:server) { PuppetSpec::HTTPSServer.new }\n\n  context \"when calling deprecated HttpPool methods\" do\n    before(:each) do\n      ssldir = tmpdir('http_pool')\n      Puppet[:ssldir] = ssldir\n      Puppet.settings.use(:main, :ssl)\n\n      File.write(Puppet[:localcacert], server.ca_cert.to_pem)\n      File.write(Puppet[:hostcrl], server.ca_crl.to_pem)\n      File.write(Puppet[:hostcert], server.server_cert.to_pem)\n      File.write(Puppet[:hostprivkey], server.server_key.to_pem)\n    end\n\n    def connection(host, port)\n      Puppet::Network::HttpPool.http_instance(host, port, use_ssl: true)\n    end\n\n    shared_examples_for 'HTTPS client' do\n      it \"connects over SSL\" do\n        server.start_server do |port|\n          http = connection(hostname, port)\n          res = http.get('/')\n          expect(res.code).to eq('200')\n        end\n      end\n\n      it \"raises if the server's cert doesn't match the hostname we connected to\" do\n        server.start_server do |port|\n          http = connection(wrong_hostname, port)\n          expect {\n            http.get('/')\n          }.to raise_error { |err|\n            expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)\n            expect(err.message).to match(/\\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)\n\n            md = err.message.match(/expected one of (.+)/)\n            expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')\n          }\n        end\n      end\n\n      it \"raises if the server's CA is unknown\" do\n        # File must exist and by not empty so DefaultValidator doesn't\n        # downgrade to VERIFY_NONE, so use a different CA that didn't\n        # issue the server's cert\n        capath = tmpfile('empty')\n        File.write(capath, cert_fixture('netlock-arany-utf8.pem'))\n        Puppet[:localcacert] = capath\n        Puppet[:certificate_revocation] = false\n\n        server.start_server do |port|\n          http = connection(hostname, port)\n          expect {\n            http.get('/')\n          }.to raise_error(Puppet::Error,\n                           %r{certificate verify failed.* .self.signed certificate in certificate chain for CN=Test CA.})\n        end\n      end\n\n      it \"detects when the server has closed the connection and reconnects\" do\n        server.start_server do |port|\n          http = connection(hostname, port)\n\n          expect(http.request_get('/')).to be_a(Net::HTTPSuccess)\n          expect(http.request_get('/')).to be_a(Net::HTTPSuccess)\n        end\n      end\n    end\n\n    context \"when using persistent HTTPS connections\" do\n      around :each do |example|\n        begin\n          example.run\n        ensure\n          Puppet.runtime[:http].close\n        end\n      end\n\n      include_examples 'HTTPS client'\n    end\n\n    shared_examples_for \"an HttpPool connection\" do |klass, legacy_api|\n      before :each do\n        Puppet::Network::HttpPool.http_client_class = klass\n      end\n\n      it \"connects using the scheme, host and port from the http instance preserving the URL path and query\" do\n        request_line = nil\n\n        response_proc = -> (req, res) {\n          request_line = req.request_line\n        }\n\n        server.start_server(response_proc: response_proc) do |port|\n          http = Puppet::Network::HttpPool.http_instance(hostname, port, true)\n          path  = \"http://bogus.example.com:443/foo?q=a\"\n          http.get(path)\n\n          if legacy_api\n            # The old API uses 'absolute-form' and passes the bogus hostname\n            # which isn't the host we connected to.\n            expect(request_line).to eq(\"GET http://bogus.example.com:443/foo?q=a HTTP/1.1\\r\\n\")\n          else\n            expect(request_line).to eq(\"GET /foo?q=a HTTP/1.1\\r\\n\")\n          end\n        end\n      end\n\n      it \"requires the caller to URL encode the path and query when using absolute form\" do\n        request_line = nil\n\n        response_proc = -> (req, res) {\n          request_line = req.request_line\n        }\n\n        server.start_server(response_proc: response_proc) do |port|\n          http = Puppet::Network::HttpPool.http_instance(hostname, port, true)\n          params = { 'key' => 'a value' }\n          encoded_url = \"https://#{hostname}:#{port}/foo%20bar?q=#{Puppet::Util.uri_query_encode(params.to_json)}\"\n          http.get(encoded_url)\n\n          if legacy_api\n            expect(request_line).to eq(\"GET #{encoded_url} HTTP/1.1\\r\\n\")\n          else\n            expect(request_line).to eq(\"GET /foo%20bar?q=%7B%22key%22%3A%22a%20value%22%7D HTTP/1.1\\r\\n\")\n          end\n        end\n      end\n\n      it \"requires the caller to URL encode the path and query when using a path\" do\n        request_line = nil\n\n        response_proc = -> (req, res) {\n          request_line = req.request_line\n        }\n\n        server.start_server(response_proc: response_proc) do |port|\n          http = Puppet::Network::HttpPool.http_instance(hostname, port, true)\n          params = { 'key' => 'a value' }\n          http.get(\"/foo%20bar?q=#{Puppet::Util.uri_query_encode(params.to_json)}\")\n\n          expect(request_line).to eq(\"GET /foo%20bar?q=%7B%22key%22%3A%22a%20value%22%7D HTTP/1.1\\r\\n\")\n        end\n      end\n    end\n\n    describe Puppet::Network::HTTP::Connection do\n      it_behaves_like \"an HttpPool connection\", described_class, false\n    end\n  end\n\n  context \"when calling HttpPool.connection method\" do\n    let(:ssl) { Puppet::SSL::SSLProvider.new }\n    let(:ssl_context) { ssl.create_root_context(cacerts: [server.ca_cert], crls: [server.ca_crl]) }\n\n    def connection(host, port, ssl_context:)\n      Puppet::Network::HttpPool.connection(host, port, ssl_context: ssl_context)\n    end\n\n    # Configure the server's SSLContext to require a client certificate. The `client_ca`\n    # setting allows the server to advertise which client CAs it will accept.\n    def require_client_certs(ctx)\n      ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT\n      ctx.client_ca = [cert_fixture('ca.pem')]\n    end\n\n    it \"connects over SSL\" do\n      server.start_server do |port|\n        http = connection(hostname, port, ssl_context: ssl_context)\n        res = http.get('/')\n        expect(res.code).to eq('200')\n      end\n    end\n\n    it \"raises if the server's cert doesn't match the hostname we connected to\" do\n      server.start_server do |port|\n        http = connection(wrong_hostname, port, ssl_context: ssl_context)\n        expect {\n          http.get('/')\n        }.to raise_error { |err|\n          expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)\n          expect(err.message).to match(/\\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)\n\n          md = err.message.match(/expected one of (.+)/)\n          expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')\n        }\n      end\n    end\n\n    it \"raises if the server's CA is unknown\" do\n      server.start_server do |port|\n        ssl_context = ssl.create_root_context(cacerts: [cert_fixture('netlock-arany-utf8.pem')],\n                                              crls: [server.ca_crl])\n        http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)\n        expect {\n          http.get('/')\n        }.to raise_error(Puppet::Error,\n                         %r{certificate verify failed.* .self.signed certificate in certificate chain for CN=Test CA.})\n      end\n    end\n\n    it \"warns when client has an incomplete client cert chain\" do\n      expect(Puppet).to receive(:warning).with(\"The issuer 'CN=Test CA Agent Subauthority' of certificate 'CN=pluto' cannot be found locally\")\n\n      pluto = cert_fixture('pluto.pem')\n\n      ssl_context = ssl.create_context(\n        cacerts: [server.ca_cert], crls: [server.ca_crl],\n        client_cert: pluto, private_key: key_fixture('pluto-key.pem')\n      )\n\n      # verify client has incomplete chain\n      expect(ssl_context.client_chain.map(&:to_der)).to eq([pluto.to_der])\n\n      # force server to require (not request) client certs\n      ctx_proc = -> (ctx) {\n        require_client_certs(ctx)\n\n        # server needs to trust the client's intermediate CA to complete the client's chain\n        ctx.cert_store.add_cert(cert_fixture('intermediate-agent.pem'))\n      }\n\n      server.start_server(ctx_proc: ctx_proc) do |port|\n        http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)\n        res = http.get('/')\n        expect(res.code).to eq('200')\n      end\n    end\n\n    it \"sends a complete client cert chain\" do\n      pluto = cert_fixture('pluto.pem')\n      client_ca = cert_fixture('intermediate-agent.pem')\n\n      ssl_context = ssl.create_context(\n        cacerts: [server.ca_cert, client_ca],\n        crls: [server.ca_crl, crl_fixture('intermediate-agent-crl.pem')],\n        client_cert: pluto,\n        private_key: key_fixture('pluto-key.pem')\n      )\n\n      # verify client has complete chain from leaf to root\n      expect(ssl_context.client_chain.map(&:to_der)).to eq([pluto, client_ca, server.ca_cert].map(&:to_der))\n\n      server.start_server(ctx_proc: method(:require_client_certs)) do |port|\n        http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)\n        res = http.get('/')\n        expect(res.code).to eq('200')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/node/environment_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/scope'\nrequire 'matchers/resource'\n\ndescribe Puppet::Node::Environment do\n  include PuppetSpec::Files\n  include Matchers::Resource\n\n  def a_module_in(name, dir)\n    Dir.mkdir(dir)\n    moddir = File.join(dir, name)\n    Dir.mkdir(moddir)\n    moddir\n  end\n\n  it \"should be able to return each module from its environment with the environment, name, and path set correctly\" do\n    base = tmpfile(\"env_modules\")\n    Dir.mkdir(base)\n\n    dirs = []\n    mods = {}\n    %w{1 2}.each do |num|\n      dir = File.join(base, \"dir#{num}\")\n      dirs << dir\n\n      mods[\"mod#{num}\"] = a_module_in(\"mod#{num}\", dir)\n    end\n\n    environment = Puppet::Node::Environment.create(:foo, dirs)\n\n    environment.modules.each do |mod|\n      expect(mod.environment).to eq(environment)\n      expect(mod.path).to eq(mods[mod.name])\n    end\n  end\n\n  it \"should expand 8.3 paths on Windows when creating an environment\",\n    :if => Puppet::Util::Platform.windows? do\n    pending(\"GH runners seem to have disabled 8.3 support\")\n\n    # asking for short names only works on paths that exist\n    base = Puppet::Util::Windows::File.get_short_pathname(tmpdir(\"env_modules\"))\n    parent_modules_dir = File.join(base, 'testmoduledir')\n\n    # make sure the paths have ~ in them, indicating unexpanded 8.3 paths\n    expect(parent_modules_dir).to match(/~/)\n\n    module_dir = a_module_in('testmodule', parent_modules_dir)\n\n    # create the environment with unexpanded 8.3 paths\n    environment = Puppet::Node::Environment.create(:foo, [parent_modules_dir])\n\n    # and expect fully expanded paths inside the environment\n    # necessary for comparing module paths internally by the parser\n    expect(environment.modulepath).to eq([Puppet::FileSystem.expand_path(parent_modules_dir)])\n    expect(environment.modules.first.path).to eq(Puppet::FileSystem.expand_path(module_dir))\n  end\n\n  it \"should not yield the same module from different module paths\" do\n    base = tmpfile(\"env_modules\")\n    Dir.mkdir(base)\n\n    dirs = []\n    %w{1 2}.each do |num|\n      dir = File.join(base, \"dir#{num}\")\n      dirs << dir\n\n      a_module_in(\"mod\", dir)\n    end\n\n    environment = Puppet::Node::Environment.create(:foo, dirs)\n\n    mods = environment.modules\n    expect(mods.length).to eq(1)\n    expect(mods[0].path).to eq(File.join(base, \"dir1\", \"mod\"))\n  end\n\n  it \"should not yield a module with the same name as a defined Bolt project\" do\n    project_path = File.join(tmpfile('project'), 'bolt_project')\n    FileUtils.mkdir_p(project_path)\n    project = Struct.new(\"Project\", :name, :path, :load_as_module?).new('project', project_path, true)\n\n    Puppet.override(bolt_project: project) do\n      base = tmpfile(\"base\")\n      FileUtils.mkdir_p([File.join(base, 'project'), File.join(base, 'other')])\n      environment = Puppet::Node::Environment.create(:env, [base])\n      mods = environment.modules\n      expect(mods.length).to eq(2)\n      expect(mods.map(&:path)).to eq([project_path, File.join(base, 'other')])\n    end\n  end\n\n  shared_examples_for \"the environment's initial import\" do |settings|\n    it \"a manifest referring to a directory invokes parsing of all its files in sorted order\" do\n      settings.each do |name, value|\n        Puppet[name] = value\n      end\n\n      # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file\n      # depends on 'a' being evaluated first. The 'c' file is empty (to ensure\n      # empty things do not break the directory import).\n      #\n      dirname = my_fixture('sitedir')\n\n      # Set the manifest to the directory to make it parse and combine them when compiling\n      node = Puppet::Node.new('testnode',\n                              :environment => Puppet::Node::Environment.create(:testing, [], dirname))\n\n      catalog = Puppet::Parser::Compiler.compile(node)\n\n      expect(catalog).to have_resource('Class[A]')\n      expect(catalog).to have_resource('Class[B]')\n      expect(catalog).to have_resource('Notify[variables]').with_parameter(:message, \"a: 10, b: 10\")\n    end\n  end\n\n  shared_examples_for \"the environment's initial import in 4x\" do |settings|\n    it \"a manifest referring to a directory invokes recursive parsing of all its files in sorted order\" do\n      settings.each do |name, value|\n        Puppet[name] = value\n      end\n\n      # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file\n      # depends on 'a' being evaluated first. The 'c' file is empty (to ensure\n      # empty things do not break the directory import).\n      #\n      dirname = my_fixture('sitedir2')\n\n      # Set the manifest to the directory to make it parse and combine them when compiling\n      node = Puppet::Node.new('testnode',\n                              :environment => Puppet::Node::Environment.create(:testing, [], dirname))\n\n      catalog = Puppet::Parser::Compiler.compile(node)\n\n      expect(catalog).to have_resource('Class[A]')\n      expect(catalog).to have_resource('Class[B]')\n      expect(catalog).to have_resource('Notify[variables]').with_parameter(:message, \"a: 10, b: 10 c: 20\")\n    end\n  end\n\n  describe 'using 4x parser' do\n    it_behaves_like \"the environment's initial import\",\n      # fixture uses variables that are set in a particular order (this ensures\n      # that files are parsed and combined in the right order or an error will\n      # be raised if 'b' is evaluated before 'a').\n      :strict_variables => true\n  end\n\n  describe 'using 4x parser' do\n    it_behaves_like \"the environment's initial import in 4x\",\n      # fixture uses variables that are set in a particular order (this ensures\n      # that files are parsed and combined in the right order or an error will\n      # be raised if 'b' is evaluated before 'a').\n      :strict_variables => true\n  end\n\n  describe \"#extralibs on Windows\", :if => Puppet::Util::Platform.windows? do\n\n    describe \"with UTF8 characters in PUPPETLIB\" do\n      let(:rune_utf8) { \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\" }\n\n      before { Puppet::Util::Windows::Process.set_environment_variable('PUPPETLIB', rune_utf8) }\n\n      it \"should use UTF8 characters in PUPPETLIB environment variable\" do\n        expect(Puppet::Node::Environment.extralibs()).to eq([rune_utf8])\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/node/facts_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Node::Facts do\n  describe \"when using the indirector\" do\n    it \"should expire any cached node instances when it is saved\" do\n      allow(Puppet::Node::Facts.indirection).to receive(:terminus_class).and_return(:yaml)\n\n      expect(Puppet::Node::Facts.indirection.terminus(:yaml)).to equal(Puppet::Node::Facts.indirection.terminus(:yaml))\n      terminus = Puppet::Node::Facts.indirection.terminus(:yaml)\n      allow(terminus).to receive(:save)\n\n      expect(Puppet::Node.indirection).to receive(:expire).with(\"me\", be_a(Hash).or(be_nil))\n\n      facts = Puppet::Node::Facts.new(\"me\")\n      Puppet::Node::Facts.indirection.save(facts)\n    end\n\n    it \"should be able to delegate to the :yaml terminus\" do\n      allow(Puppet::Node::Facts.indirection).to receive(:terminus_class).and_return(:yaml)\n\n      # Load now, before we stub the exists? method.\n      terminus = Puppet::Node::Facts.indirection.terminus(:yaml)\n\n      expect(terminus).to receive(:path).with(\"me\").and_return(\"/my/yaml/file\")\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/yaml/file\").and_return(false)\n\n      expect(Puppet::Node::Facts.indirection.find(\"me\")).to be_nil\n    end\n\n    it \"should be able to delegate to the :facter terminus\" do\n      allow(Puppet::Node::Facts.indirection).to receive(:terminus_class).and_return(:facter)\n\n      expect(Facter).to receive(:resolve).and_return({1 => 2})\n      facts = Puppet::Node::Facts.new(\"me\")\n      expect(Puppet::Node::Facts).to receive(:new).with(\"me\", {1 => 2}).and_return(facts)\n\n      expect(Puppet::Node::Facts.indirection.find(\"me\")).to equal(facts)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/node_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/node'\n\ndescribe Puppet::Node do\n  describe \"when delegating indirection calls\" do\n    before do\n      Puppet::Node.indirection.reset_terminus_class\n      Puppet::Node.indirection.cache_class = nil\n\n      @name = \"me\"\n      @node = Puppet::Node.new(@name)\n    end\n\n    it \"should be able to use the yaml terminus\" do\n      allow(Puppet::Node.indirection).to receive(:terminus_class).and_return(:yaml)\n\n      # Load now, before we stub the exists? method.\n      terminus = Puppet::Node.indirection.terminus(:yaml)\n\n      expect(terminus).to receive(:path).with(@name).and_return(\"/my/yaml/file\")\n\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/yaml/file\").and_return(false)\n      expect(Puppet::Node.indirection.find(@name)).to be_nil\n    end\n\n    it \"should be able to use the plain terminus\" do\n      allow(Puppet::Node.indirection).to receive(:terminus_class).and_return(:plain)\n\n      # Load now, before we stub the exists? method.\n      Puppet::Node.indirection.terminus(:plain)\n\n      expect(Puppet::Node).to receive(:new).with(@name).and_return(@node)\n\n      expect(Puppet::Node.indirection.find(@name)).to equal(@node)\n    end\n\n    describe \"and using the memory terminus\" do\n      before do\n        @name = \"me\"\n        @terminus = Puppet::Node.indirection.terminus(:memory)\n        allow(Puppet::Node.indirection).to receive(:terminus).and_return(@terminus)\n        @node = Puppet::Node.new(@name)\n      end\n\n      after do\n        @terminus.instance_variable_set(:@instances, {})\n      end\n\n      it \"should find no nodes by default\" do\n        expect(Puppet::Node.indirection.find(@name)).to be_nil\n      end\n\n      it \"should be able to find nodes that were previously saved\" do\n        Puppet::Node.indirection.save(@node)\n        expect(Puppet::Node.indirection.find(@name)).to equal(@node)\n      end\n\n      it \"should replace existing saved nodes when a new node with the same name is saved\" do\n        Puppet::Node.indirection.save(@node)\n        two = Puppet::Node.new(@name)\n        Puppet::Node.indirection.save(two)\n        expect(Puppet::Node.indirection.find(@name)).to equal(two)\n      end\n\n      it \"should be able to remove previously saved nodes\" do\n        Puppet::Node.indirection.save(@node)\n        Puppet::Node.indirection.destroy(@node.name)\n        expect(Puppet::Node.indirection.find(@name)).to be_nil\n      end\n\n      it \"should fail when asked to destroy a node that does not exist\" do\n        expect { Puppet::Node.indirection.destroy(@node) }.to raise_error(ArgumentError)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/catalog_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/include_in_order'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/indirector/catalog/compiler'\n\ndescribe \"A catalog\" do\n  include PuppetSpec::Compiler\n\n  context \"when compiled\" do\n    let(:env) { Puppet::Node::Environment.create(:testing, []) }\n    let(:node) { Puppet::Node.new('test', :environment => env) }\n    let(:loaders) { Puppet::Pops::Loaders.new(env) }\n\n    before(:each) do\n      Puppet.push_context({:loaders => loaders, :current_environment => env})\n      allow_any_instance_of(Puppet::Parser::Compiler).to receive(:loaders).and_return(loaders)\n    end\n\n    after(:each) do\n      Puppet.pop_context()\n    end\n\n    context \"when transmitted to the agent\" do\n      it \"preserves the order in which the resources are added to the catalog\" do\n        resources_in_declaration_order = [\"Class[First]\",\n                                          \"Second[position]\",\n                                          \"Class[Third]\",\n                                          \"Fourth[position]\"]\n\n        master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM)\n          define fourth() { }\n          class third { }\n\n          define second() {\n            fourth { \"position\": }\n          }\n\n          class first {\n            second { \"position\": }\n            class { \"third\": }\n          }\n\n          include first\n        EOM\n\n        expect(resources_in(master_catalog)).\n          to include_in_order(*resources_in_declaration_order)\n        expect(resources_in(agent_catalog)).\n          to include_in_order(*resources_in_declaration_order)\n      end\n    end\n  end\n\n  def master_catalog_for(manifest)\n    Puppet::Resource::Catalog::Compiler.new.filter(compile_to_catalog(manifest, node))\n  end\n\n  def master_and_agent_catalogs_for(manifest)\n    compiler = Puppet::Resource::Catalog::Compiler.new\n    master_catalog = compiler.filter(compile_to_catalog(manifest, node))\n    agent_catalog = Puppet::Resource::Catalog.convert_from(:json, master_catalog.render(:json))\n    [master_catalog, agent_catalog]\n  end\n\n  def resources_in(catalog)\n    catalog.resources.map(&:ref)\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/class_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/language'\n\ndescribe \"Class expressions\" do\n  extend PuppetSpec::Language\n\n  produces(\n    \"class hi { }\"                                       => '!defined(Class[hi])',\n\n    \"class hi { } include hi\"                            => 'defined(Class[hi])',\n    \"include(hi) class hi { }\"                           => 'defined(Class[hi])',\n\n    \"class hi { } class { hi: }\"                         => 'defined(Class[hi])',\n    \"class { hi: } class hi { }\"                         => 'defined(Class[hi])',\n\n    \"class bye { } class hi inherits bye { } include hi\" => 'defined(Class[hi]) and defined(Class[bye])')\n\n  produces(<<-EXAMPLE => 'defined(Notify[foo]) and defined(Notify[bar]) and !defined(Notify[foo::bar])')\n    class bar { notify { 'bar': } }\n    class foo::bar { notify { 'foo::bar': } }\n    class foo inherits bar { notify { 'foo': } }\n\n    include foo\n  EXAMPLE\n\n  produces(<<-EXAMPLE => 'defined(Notify[foo]) and defined(Notify[bar]) and !defined(Notify[foo::bar])')\n    class bar { notify { 'bar': } }\n    class foo::bar { notify { 'foo::bar': } }\n    class foo inherits ::bar { notify { 'foo': } }\n\n    include foo\n  EXAMPLE\nend\n"
  },
  {
    "path": "spec/integration/parser/collection_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe 'collectors' do\n  include PuppetSpec::Compiler\n\n  def expect_the_message_to_be(expected_messages, code, node = Puppet::Node.new('the node'))\n    catalog = compile_to_catalog(code, node)\n    messages = catalog.resources.find_all { |resource| resource.type == 'Notify' }.\n                                 collect { |notify| notify[:message] }\n    expected_messages.each { |message| expect(messages).to include(message) }\n  end\n\n  def warnings\n    @logs.select { |log| log.level == :warning }.map { |log| log.message }\n  end\n\n  context \"virtual resource collection\" do\n    it \"matches everything when no query given\" do\n      expect_the_message_to_be([\"the other message\", \"the message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"the message\" }\n        @notify { \"other\": message => \"the other message\" }\n\n        Notify <| |>\n      MANIFEST\n    end\n\n    it \"matches regular resources \" do\n      expect_the_message_to_be([\"changed\", \"changed\"], <<-MANIFEST)\n        notify { \"testing\": message => \"the message\" }\n        notify { \"other\": message => \"the other message\" }\n\n        Notify <| |> { message => \"changed\" }\n      MANIFEST\n    end\n\n    it \"matches on tags\" do\n      expect_the_message_to_be([\"wanted\"], <<-MANIFEST)\n        @notify { \"testing\": tag => [\"one\"], message => \"wanted\" }\n        @notify { \"other\": tag => [\"two\"], message => \"unwanted\" }\n\n        Notify <| tag == one |>\n      MANIFEST\n    end\n\n    it \"matches on title\" do\n      expect_the_message_to_be([\"the message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"the message\" }\n\n        Notify <| title == \"testing\" |>\n      MANIFEST\n    end\n\n    it \"matches on other parameters\" do\n      expect_the_message_to_be([\"the message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"the message\" }\n        @notify { \"other testing\": message => \"the wrong message\" }\n\n        Notify <| message == \"the message\" |>\n      MANIFEST\n    end\n\n    it \"matches against elements of an array valued parameter\" do\n      expect_the_message_to_be([[\"the\", \"message\"]], <<-MANIFEST)\n        @notify { \"testing\": message => [\"the\", \"message\"] }\n        @notify { \"other testing\": message => [\"not\", \"here\"] }\n\n        Notify <| message == \"message\" |>\n      MANIFEST\n    end\n\n    it \"matches with bare word\" do\n      expect_the_message_to_be([\"wanted\"], <<-MANIFEST)\n        @notify { \"testing\": tag => [\"one\"], message => \"wanted\" }\n        Notify <| tag == one |>\n      MANIFEST\n    end\n\n    it \"matches with single quoted string\" do\n      expect_the_message_to_be([\"wanted\"], <<-MANIFEST)\n        @notify { \"testing\": tag => [\"one\"], message => \"wanted\" }\n        Notify <| tag == 'one' |>\n      MANIFEST\n    end\n\n    it \"matches with double quoted string\" do\n      expect_the_message_to_be([\"wanted\"], <<-MANIFEST)\n        @notify { \"testing\": tag => [\"one\"], message => \"wanted\" }\n        Notify <| tag == \"one\" |>\n      MANIFEST\n    end\n\n    it \"matches with double quoted string with interpolated expression\" do\n      expect_the_message_to_be([\"wanted\"], <<-MANIFEST)\n        @notify { \"testing\": tag => [\"one\"], message => \"wanted\" }\n        $x = 'one'\n        Notify <| tag == \"$x\" |>\n      MANIFEST\n    end\n\n    it \"matches with resource references\" do\n      expect_the_message_to_be([\"wanted\"], <<-MANIFEST)\n        @notify { \"foobar\": }\n        @notify { \"testing\": require => Notify[\"foobar\"], message => \"wanted\" }\n        Notify <| require == Notify[\"foobar\"] |>\n      MANIFEST\n    end\n\n    it \"allows criteria to be combined with 'and'\" do\n      expect_the_message_to_be([\"the message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"the message\" }\n        @notify { \"other\": message => \"the message\" }\n\n        Notify <| title == \"testing\" and message == \"the message\" |>\n      MANIFEST\n    end\n\n    it \"allows criteria to be combined with 'or'\" do\n      expect_the_message_to_be([\"the message\", \"other message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"the message\" }\n        @notify { \"other\": message => \"other message\" }\n        @notify { \"yet another\": message => \"different message\" }\n\n        Notify <| title == \"testing\" or message == \"other message\" |>\n      MANIFEST\n    end\n\n    it \"allows criteria to be combined with 'or'\" do\n      expect_the_message_to_be([\"the message\", \"other message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"the message\" }\n        @notify { \"other\": message => \"other message\" }\n        @notify { \"yet another\": message => \"different message\" }\n\n        Notify <| title == \"testing\" or message == \"other message\" |>\n      MANIFEST\n    end\n\n    it \"allows criteria to be grouped with parens\" do\n      expect_the_message_to_be([\"the message\", \"different message\"], <<-MANIFEST)\n        @notify { \"testing\":     message => \"different message\", withpath => true }\n        @notify { \"other\":       message => \"the message\" }\n        @notify { \"yet another\": message => \"the message\",       withpath => true }\n\n        Notify <| (title == \"testing\" or message == \"the message\") and withpath == true |>\n      MANIFEST\n    end\n\n    it \"does not do anything if nothing matches\" do\n      expect_the_message_to_be([], <<-MANIFEST)\n        @notify { \"testing\": message => \"different message\" }\n\n        Notify <| title == \"does not exist\" |>\n      MANIFEST\n    end\n\n    it \"excludes items with inequalities\" do\n      expect_the_message_to_be([\"good message\"], <<-MANIFEST)\n        @notify { \"testing\": message => \"good message\" }\n        @notify { \"the wrong one\": message => \"bad message\" }\n\n        Notify <| title != \"the wrong one\" |>\n      MANIFEST\n    end\n\n    it \"does not exclude resources with unequal arrays\" do\n      expect_the_message_to_be([\"message\", [\"not this message\", \"or this one\"]], <<-MANIFEST)\n        @notify { \"testing\": message => \"message\" }\n        @notify { \"the wrong one\": message => [\"not this message\", \"or this one\"] }\n\n        Notify <| message != \"not this message\" |>\n      MANIFEST\n    end\n\n    it \"does not exclude tags with inequalities\" do\n      expect_the_message_to_be([\"wanted message\", \"the way it works\"], <<-MANIFEST)\n        @notify { \"testing\": tag => [\"wanted\"], message => \"wanted message\" }\n        @notify { \"other\": tag => [\"why\"], message => \"the way it works\" }\n\n        Notify <| tag != \"why\" |>\n      MANIFEST\n    end\n\n    it \"does not collect classes\" do\n      node = Puppet::Node.new('the node')\n      expect do\n        compile_to_catalog(<<-MANIFEST, node)\n          class theclass {\n            @notify { \"testing\": message => \"good message\" }\n          }\n          Class <|  |>\n        MANIFEST\n      end.to raise_error(/Classes cannot be collected/)\n    end\n\n    it \"does not collect resources that don't exist\" do\n      node = Puppet::Node.new('the node')\n      expect do\n        compile_to_catalog(<<-MANIFEST, node)\n          class theclass {\n            @notify { \"testing\": message => \"good message\" }\n          }\n          SomeResource <|  |>\n        MANIFEST\n      end.to raise_error(/Resource type someresource doesn't exist/)\n    end\n\n    it 'allows query for literal undef' do\n      expect_the_message_to_be([\"foo::baz::quux\"], <<-MANIFEST)\n        define foo ($x = undef, $y = undef) {\n          notify { 'testing': message => \"foo::${x}::${y}\" }\n        }\n        foo { 'bar': y => 'quux' }\n        Foo <| x == undef |> { x => 'baz' }\n      MANIFEST\n    end\n\n    context \"overrides\" do\n      it \"modifies an existing array\" do\n        expect_the_message_to_be([[\"original message\", \"extra message\"]], <<-MANIFEST)\n          @notify { \"testing\": message => [\"original message\"] }\n\n          Notify <| |> {\n            message +> \"extra message\"\n          }\n        MANIFEST\n      end\n\n      it \"converts a scalar to an array\" do\n        expect_the_message_to_be([[\"original message\", \"extra message\"]], <<-MANIFEST)\n          @notify { \"testing\": message => \"original message\" }\n\n          Notify <| |> {\n            message +> \"extra message\"\n          }\n        MANIFEST\n      end\n\n      it \"splats attributes from a hash\" do\n        expect_the_message_to_be([\"overridden message\"], <<-MANIFEST)\n          @notify { \"testing\": message => \"original message\" }\n\n          Notify <| |> {\n            * => { message => \"overridden message\" }\n          }\n        MANIFEST\n      end\n\n      it \"collects with override when inside a class (#10963)\" do\n        expect_the_message_to_be([\"overridden message\"], <<-MANIFEST)\n          @notify { \"testing\": message => \"original message\" }\n\n          include collector_test\n          class collector_test {\n            Notify <| |> {\n              message => \"overridden message\"\n            }\n          }\n        MANIFEST\n      end\n\n      it \"collects with override when inside a define (#10963)\" do\n        expect_the_message_to_be([\"overridden message\"], <<-MANIFEST)\n          @notify { \"testing\": message => \"original message\" }\n\n          collector_test { testing: }\n          define collector_test() {\n            Notify <| |> {\n              message => \"overridden message\"\n            }\n          }\n        MANIFEST\n      end\n\n      # Catches regression in implemented behavior, this is not to be taken as this is the wanted behavior\n      # but it has been this way for a long time.\n      it \"collects and overrides user defined resources immediately (before queue is evaluated)\" do\n        expect_the_message_to_be([\"overridden\"], <<-MANIFEST)\n          define foo($message) {\n            notify { \"testing\": message => $message }\n          }\n          foo { test: message => 'given' }\n          Foo <|  |> { message => 'overridden' }\n        MANIFEST\n      end\n\n      # Catches regression in implemented behavior, this is not to be taken as this is the wanted behavior\n      # but it has been this way for a long time.\n      it \"collects and overrides user defined resources immediately (virtual resources not queued)\" do\n        expect_the_message_to_be([\"overridden\"], <<-MANIFEST)\n          define foo($message) {\n            @notify { \"testing\": message => $message }\n          }\n          foo { test: message => 'given' }\n          Notify <| |> # must be collected or the assertion does not find it\n          Foo <|  |> { message => 'overridden' }\n        MANIFEST\n      end\n\n      # Catches regression in implemented behavior, this is not to be taken as this is the wanted behavior\n      # but it has been this way for a long time.\n      # Note difference from none +> case where the override takes effect\n      it \"collects and overrides user defined resources with +>\" do\n        expect_the_message_to_be([[\"given\", \"overridden\"]], <<-MANIFEST)\n          define foo($message) {\n            notify { \"$name\": message => $message }\n          }\n          foo { test: message => ['given'] }\n          Notify <|  |> { message +> ['overridden'] }\n        MANIFEST\n      end\n\n      it \"collects and overrides virtual resources multiple times using multiple collects\" do\n        expect_the_message_to_be([\"overridden2\"], <<-MANIFEST)\n          @notify { \"testing\": message => \"original\" }\n          Notify <|  |> { message => 'overridden1' }\n          Notify <|  |> { message => 'overridden2' }\n        MANIFEST\n      end\n\n      it \"collects and overrides non virtual resources multiple times using multiple collects\" do\n        expect_the_message_to_be([\"overridden2\"], <<-MANIFEST)\n          notify { \"testing\": message => \"original\" }\n          Notify <|  |> { message => 'overridden1' }\n          Notify <|  |> { message => 'overridden2' }\n        MANIFEST\n      end\n\n      context 'when overriding an already evaluated resource' do\n        let(:manifest) { <<-MANIFEST }\n          define foo($message) {\n            notify { \"testing\": message => $message }\n          }\n          foo { test: message => 'given' }\n          define delayed {\n            Foo <|  |> { message => 'overridden' }\n          }\n          delayed {'do it now': }\n        MANIFEST\n\n        it 'and --strict=off, it silently skips the override' do\n          Puppet[:strict] = :off\n          expect_the_message_to_be(['given'], manifest)\n          expect(warnings).to be_empty\n        end\n\n        it 'and --strict=warning, it warns about the attempt to override and skips it' do\n          Puppet[:strict] = :warning\n          expect_the_message_to_be(['given'], manifest)\n          expect(warnings).to include(\n            /Attempt to override an already evaluated resource, defined at \\(line: 4\\), with new values \\(line: 6\\)/)\n        end\n\n        it 'and --strict=error, it fails compilation' do\n          Puppet[:strict] = :error\n          expect { compile_to_catalog(manifest) }.to raise_error(\n            /Attempt to override an already evaluated resource, defined at \\(line: 4\\), with new values \\(line: 6\\)/)\n          expect(warnings).to be_empty\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/compiler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\n# COPY OF UNIT TEST\nclass CompilerTestResource\n  attr_accessor :builtin, :virtual, :evaluated, :type, :title\n\n  def initialize(type, title)\n    @type = type\n    @title = title\n  end\n\n  def [](attr)\n    return nil if (attr == :stage || attr == :alias)\n    :main\n  end\n\n  def ref\n    \"#{type.to_s.capitalize}[#{title}]\"\n  end\n\n  def evaluated?\n    @evaluated\n  end\n\n  def builtin_type?\n    @builtin\n  end\n\n  def virtual?\n    @virtual\n  end\n\n  def class?\n    false\n  end\n\n  def stage?\n    false\n  end\n\n  def evaluate\n  end\n\n  def file\n    \"/fake/file/goes/here\"\n  end\n\n  def line\n    \"42\"\n  end\n\n  def resource_type\n    self.class\n  end\nend\n\ndescribe Puppet::Parser::Compiler do\n  include PuppetSpec::Files\n  include Matchers::Resource\n\n  def resource(type, title)\n    Puppet::Parser::Resource.new(type, title, :scope => @scope)\n  end\n\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n\n  before :each do\n    # Push me faster, I wanna go back in time!  (Specifically, freeze time\n    # across the test since we have a bunch of version == timestamp code\n    # hidden away in the implementation and we keep losing the race.)\n    # --daniel 2011-04-21\n    now = Time.now\n    allow(Time).to receive(:now).and_return(now)\n\n    @node = Puppet::Node.new(\"testnode\",\n                             :facts => Puppet::Node::Facts.new(\"facts\", {}),\n                             :environment => environment)\n    @known_resource_types = environment.known_resource_types\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @scope = Puppet::Parser::Scope.new(@compiler, :source => double('source'))\n    @scope_resource = Puppet::Parser::Resource.new(:file, \"/my/file\", :scope => @scope)\n    @scope.resource = @scope_resource\n  end\n\n  # NEW INTEGRATION TEST\n  describe \"when evaluating collections\" do\n    it 'matches on container inherited tags' do\n      Puppet[:code] = <<-MANIFEST\n      class xport_test {\n        tag('foo_bar')\n        @notify { 'nbr1':\n          message => 'explicitly tagged',\n          tag => 'foo_bar'\n        }\n\n        @notify { 'nbr2':\n          message => 'implicitly tagged'\n        }\n\n        Notify <| tag == 'foo_bar' |> {\n          message => 'overridden'\n        }\n      }\n      include xport_test\n      MANIFEST\n\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new(\"mynode\"))\n\n      expect(catalog).to have_resource(\"Notify[nbr1]\").with_parameter(:message, 'overridden')\n      expect(catalog).to have_resource(\"Notify[nbr2]\").with_parameter(:message, 'overridden')\n    end\n  end\n\n  describe \"when evaluating node classes\" do\n    include PuppetSpec::Compiler\n\n    describe \"when provided classes in hash format\" do\n      it 'looks up default parameter values from inherited class (PUP-2532)' do\n        catalog = compile_to_catalog(<<-CODE)\n          class a {\n            Notify { message => \"defaulted\" }\n            include c\n            notify { bye: }\n          }\n          class b { Notify { message => \"inherited\" } }\n          class c inherits b { notify { hi: } }\n\n          include a\n          notify {hi_test: message => Notify[hi][message] }\n          notify {bye_test: message => Notify[bye][message] }\n        CODE\n\n        expect(catalog).to have_resource(\"Notify[hi_test]\").with_parameter(:message, \"inherited\")\n        expect(catalog).to have_resource(\"Notify[bye_test]\").with_parameter(:message, \"defaulted\")\n      end\n    end\n  end\n\n  context \"when converting catalog to resource\" do\n    it \"the same environment is used for compilation as for transformation to resource form\" do\n        Puppet[:code] = <<-MANIFEST\n          notify { 'dummy':\n          }\n        MANIFEST\n\n      expect_any_instance_of(Puppet::Parser::Resource::Catalog).to receive(:to_resource) do |catalog|\n        expect(Puppet.lookup(:current_environment).name).to eq(:production)\n      end\n\n      Puppet::Parser::Compiler.compile(Puppet::Node.new(\"mynode\"))\n    end\n  end\n\n  context 'when working with $settings name space' do\n    include PuppetSpec::Compiler\n    it 'makes $settings::strict available as string' do\n      node = Puppet::Node.new(\"testing\")\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'test': message => $settings::strict == 'error' }\n      MANIFEST\n      expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, true)\n    end\n\n    it 'can return boolean settings as Boolean' do\n      node = Puppet::Node.new(\"testing\")\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'test': message => $settings::storeconfigs == false }\n      MANIFEST\n      expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, true)\n    end\n\n    it 'makes all server settings available as $settings::all_local hash' do\n      node = Puppet::Node.new(\"testing\")\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'test': message => $settings::all_local['strict'] == 'error' }\n      MANIFEST\n      expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, true)\n    end\n\n  end\n\n  context 'when working with $server_facts' do\n    include PuppetSpec::Compiler\n\n    it '$trusted is available' do\n      node = Puppet::Node.new(\"testing\")\n      node.add_server_facts({ \"server_fact\" => \"foo\" })\n\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n          notify { 'test': message => $server_facts[server_fact] }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, \"foo\")\n    end\n\n    it 'does not allow assignment to $server_facts' do\n      node = Puppet::Node.new(\"testing\")\n      node.add_server_facts({ \"server_fact\" => \"foo\" })\n\n      expect do\n        compile_to_catalog(<<-MANIFEST, node)\n            $server_facts = 'changed'\n            notify { 'test': message => $server_facts == 'changed' }\n        MANIFEST\n      end.to raise_error(Puppet::PreformattedError, /Attempt to assign to a reserved variable name: '\\$server_facts'.*/)\n    end\n  end\n\n  describe \"the compiler when using 4.x language constructs\" do\n    include PuppetSpec::Compiler\n\n    if Puppet::Util::Platform.windows?\n      it \"should be able to determine the configuration version from a local version control repository\" do\n        pending(\"Bug #14071 about semantics of Puppet::Util::Execute on Windows\")\n        # This should always work, because we should always be\n        # in the puppet repo when we run this.\n        version = %x{git rev-parse HEAD}.chomp\n\n        Puppet.settings[:config_version] = 'git rev-parse HEAD'\n\n        compiler = Puppet::Parser::Compiler.new(Puppet::Node.new(\"testnode\"))\n        compiler.catalog.version.should == version\n      end\n    end\n\n    it 'assigns multiple variables from a class' do\n      node = Puppet::Node.new(\"testnodex\")\n      catalog = compile_to_catalog(<<-PP, node)\n      class foo::bar::example($x = 100)  {\n        $a = 10\n        $c = undef\n      }\n      include foo::bar::example\n\n      [$a, $x, $c] = Class['foo::bar::example']\n      notify{'check_me': message => \"$a, $x, -${c}-\" }\n      PP\n      expect(catalog).to have_resource(\"Notify[check_me]\").with_parameter(:message, \"10, 100, --\")\n    end\n\n    it 'errors on attempt to assigns multiple variables from a class when variable does not exist' do\n      node = Puppet::Node.new(\"testnodex\")\n      expect do\n        compile_to_catalog(<<-PP, node)\n        class foo::bar::example($x = 100)  {\n          $ah = 10\n          $c = undef\n        }\n        include foo::bar::example\n\n        [$a, $x, $c] = Class['foo::bar::example']\n        notify{'check_me': message => \"$a, $x, -${c}-\" }\n        PP\n      end.to raise_error(/No value for required variable '\\$foo::bar::example::a'/)\n    end\n\n    it \"should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)\" do\n      node = Puppet::Node.new(\"testnodex\")\n      node.classes = ['foo', 'bar']\n      compile_to_catalog(<<-PP, node)\n        class foo\n        {\n          notify { foo_notify: }\n          include bar\n        }\n        class bar\n        {\n          notify { bar_notify: }\n        }\n      PP\n\n      catalog = Puppet::Parser::Compiler.compile(node)\n\n      expect(catalog).to have_resource(\"Notify[foo_notify]\")\n      expect(catalog).to have_resource(\"Notify[bar_notify]\")\n    end\n\n    it 'applies defaults for defines with qualified names (PUP-2302)' do\n      catalog = compile_to_catalog(<<-CODE)\n        define my::thing($msg = 'foo') { notify {'check_me': message => $msg } }\n        My::Thing { msg => 'evoe' }\n        my::thing { 'name': }\n      CODE\n\n      expect(catalog).to have_resource(\"Notify[check_me]\").with_parameter(:message, \"evoe\")\n    end\n\n    it 'Applies defaults from dynamic scopes (3x and future with reverted PUP-867)' do\n      catalog = compile_to_catalog(<<-CODE)\n      class a {\n        Notify { message => \"defaulted\" }\n        include b\n        notify { bye: }\n      }\n      class b { notify { hi: } }\n\n      include a\n      CODE\n      expect(catalog).to have_resource(\"Notify[hi]\").with_parameter(:message, \"defaulted\")\n      expect(catalog).to have_resource(\"Notify[bye]\").with_parameter(:message, \"defaulted\")\n    end\n\n    it 'gets default from inherited class (PUP-867)' do\n      catalog = compile_to_catalog(<<-CODE)\n      class a {\n        Notify { message => \"defaulted\" }\n        include c\n        notify { bye: }\n      }\n      class b { Notify { message => \"inherited\" } }\n      class c inherits b { notify { hi: } }\n\n      include a\n      CODE\n\n      expect(catalog).to have_resource(\"Notify[hi]\").with_parameter(:message, \"inherited\")\n      expect(catalog).to have_resource(\"Notify[bye]\").with_parameter(:message, \"defaulted\")\n    end\n\n    it 'looks up default parameter values from inherited class (PUP-2532)' do\n      catalog = compile_to_catalog(<<-CODE)\n      class a {\n        Notify { message => \"defaulted\" }\n        include c\n        notify { bye: }\n      }\n      class b { Notify { message => \"inherited\" } }\n      class c inherits b { notify { hi: } }\n\n      include a\n      notify {hi_test: message => Notify[hi][message] }\n      notify {bye_test: message => Notify[bye][message] }\n      CODE\n\n      expect(catalog).to have_resource(\"Notify[hi_test]\").with_parameter(:message, \"inherited\")\n      expect(catalog).to have_resource(\"Notify[bye_test]\").with_parameter(:message, \"defaulted\")\n    end\n\n    it 'does not allow override of class parameters using a resource override expression' do\n      expect do\n        compile_to_catalog(<<-CODE)\n          Class[a] { x => 2}\n        CODE\n      end.to raise_error(/Resource Override can only.*got: Class\\[a\\].*/)\n    end\n\n    describe 'when resolving class references' do\n      include Matchers::Resource\n\n      { 'string'             => 'myWay',\n        'class reference'    => 'Class[\"myWay\"]',\n        'resource reference' => 'Resource[\"class\", \"myWay\"]'\n      }.each do |label, code|\n        it \"allows camel cased class name reference in 'include' using a #{label}\" do\n          catalog = compile_to_catalog(<<-\"PP\")\n            class myWay {\n              notify { 'I did it': message => 'my way'}\n            }\n            include #{code}\n          PP\n          expect(catalog).to have_resource(\"Notify[I did it]\")\n        end\n      end\n\n      describe 'and classname is a Resource Reference' do\n        # tested with strict == off since this was once conditional on strict\n        # can be removed in a later version.\n        before(:each) do\n          Puppet[:strict] = :off\n        end\n\n        it 'is reported as an error' do\n          expect { \n            compile_to_catalog(<<-PP)\n              notice Class[ToothFairy]\n            PP\n          }.to raise_error(/Illegal Class name in class reference. A TypeReference\\['ToothFairy'\\]-Type cannot be used where a String is expected/)\n        end\n      end\n\n      it \"should not favor local scope (with class included in topscope)\" do\n        catalog = compile_to_catalog(<<-PP)\n          class experiment {\n            class baz {\n            }\n            notify {\"x\" : require => Class['baz'] }\n            notify {\"y\" : require => Class['experiment::baz'] }\n          }\n          class baz {\n          }\n          include baz\n          include experiment\n          include experiment::baz\n        PP\n\n        expect(catalog).to have_resource(\"Notify[x]\").with_parameter(:require, be_resource(\"Class[Baz]\"))\n        expect(catalog).to have_resource(\"Notify[y]\").with_parameter(:require, be_resource(\"Class[Experiment::Baz]\"))\n      end\n\n      it \"should not favor local name scope\" do\n        expect {\n          compile_to_catalog(<<-PP)\n            class experiment {\n              class baz {\n              }\n              notify {\"x\" : require => Class['baz'] }\n              notify {\"y\" : require => Class['experiment::baz'] }\n            }\n            class baz {\n            }\n            include experiment\n            include experiment::baz\n          PP\n        }.to raise_error(/Could not find resource 'Class\\[Baz\\]' in parameter 'require'/)\n      end\n    end\n\n    describe \"(ticket #13349) when explicitly specifying top scope\" do\n      [\"class {'::bar::baz':}\", \"include ::bar::baz\"].each do |include|\n        describe \"with #{include}\" do\n          it \"should find the top level class\" do\n            catalog = compile_to_catalog(<<-MANIFEST)\n              class { 'foo::test': }\n              class foo::test {\n              \t#{include}\n              }\n              class bar::baz {\n              \tnotify { 'good!': }\n              }\n              class foo::bar::baz {\n              \tnotify { 'bad!': }\n              }\n            MANIFEST\n\n            expect(catalog).to have_resource(\"Class[Bar::Baz]\")\n            expect(catalog).to have_resource(\"Notify[good!]\")\n            expect(catalog).not_to have_resource(\"Class[Foo::Bar::Baz]\")\n            expect(catalog).not_to have_resource(\"Notify[bad!]\")\n          end\n        end\n      end\n    end\n\n    it 'should recompute the version after input files are re-parsed' do\n      Puppet[:code] = 'class foo { }'\n      first_time = Time.at(1)\n      second_time = Time.at(200)\n      allow(Time).to receive(:now).and_return(first_time)\n      node = Puppet::Node.new('mynode')\n      expect(Puppet::Parser::Compiler.compile(node).version).to eq(first_time.to_i)\n      allow(Time).to receive(:now).and_return(second_time)\n      expect(Puppet::Parser::Compiler.compile(node).version).to eq(first_time.to_i) # no change because files didn't change\n      Puppet[:code] = nil\n      expect(Puppet::Parser::Compiler.compile(node).version).to eq(second_time.to_i)\n    end\n\n    ['define', 'class', 'node'].each do |thing|\n      it \"'#{thing}' is not allowed inside evaluated conditional constructs\" do\n        expect do\n          compile_to_catalog(<<-PP)\n            if true {\n              #{thing} foo {\n              }\n              notify { decoy: }\n            }\n          PP\n        end.to raise_error(Puppet::Error, /Classes, definitions, and nodes may only appear at toplevel/)\n      end\n\n      it \"'#{thing}' is not allowed inside un-evaluated conditional constructs\" do\n        expect do\n          compile_to_catalog(<<-PP)\n            if false {\n              #{thing} foo {\n              }\n              notify { decoy: }\n            }\n          PP\n        end.to raise_error(Puppet::Error, /Classes, definitions, and nodes may only appear at toplevel/)\n      end\n    end\n\n    describe \"relationships to non existing resources (even with strict==off)\" do\n\n      [ 'before',\n        'subscribe',\n        'notify',\n        'require'].each do |meta_param|\n        it \"are reported as an error when formed via meta parameter #{meta_param}\" do\n          expect { \n            compile_to_catalog(<<-PP)\n              notify{ x : #{meta_param} => Notify[tooth_fairy] }\n            PP\n          }.to raise_error(/Could not find resource 'Notify\\[tooth_fairy\\]' in parameter '#{meta_param}'/)\n        end\n      end\n\n      it 'is not reported for virtual resources' do\n        expect { \n          compile_to_catalog(<<-PP)\n            @notify{ x : require => Notify[tooth_fairy] }\n          PP\n        }.to_not raise_error\n      end\n\n      it 'is reported for a realized virtual resources' do\n        expect { \n          compile_to_catalog(<<-PP)\n            @notify{ x : require => Notify[tooth_fairy] }\n            realize(Notify['x'])\n          PP\n        }.to raise_error(/Could not find resource 'Notify\\[tooth_fairy\\]' in parameter 'require'/)\n      end\n\n      it 'faulty references are reported with source location' do\n        expect { \n          compile_to_catalog(<<-PP)\n            notify{ x : \n              require => tooth_fairy }\n          PP\n        }.to raise_error(/\"tooth_fairy\" is not a valid resource reference.*\\(line: 2\\)/)\n      end\n    end\n\n    describe \"relationships can be formed\" do\n      def extract_name(ref)\n        ref.sub(/File\\[(\\w+)\\]/, '\\1')\n      end\n\n      def assert_creates_relationships(relationship_code, expectations)\n        base_manifest = <<-MANIFEST\n          file { [a,b,c]:\n            mode => '0644',\n          }\n          file { [d,e]:\n            mode => '0755',\n          }\n        MANIFEST\n\n        catalog = compile_to_catalog(base_manifest + relationship_code)\n        resources = catalog.resources.select { |res| res.type == 'File' }\n\n        actual_relationships, actual_subscriptions = [:before, :notify].map do |relation|\n          resources.map do |res|\n            dependents = Array(res[relation])\n            dependents.map { |ref| [res.title, extract_name(ref)] }\n          end.inject(&:concat)\n        end\n\n        expect(actual_relationships).to match_array(expectations[:relationships] || [])\n        expect(actual_subscriptions).to match_array(expectations[:subscriptions] || [])\n      end\n\n      it \"of regular type\" do\n        assert_creates_relationships(\"File[a] -> File[b]\",\n          :relationships => [['a','b']])\n      end\n\n      it \"of subscription type\" do\n        assert_creates_relationships(\"File[a] ~> File[b]\",\n          :subscriptions => [['a', 'b']])\n      end\n\n      it \"between multiple resources expressed as resource with multiple titles\" do\n        assert_creates_relationships(\"File[a,b] -> File[c,d]\",\n          :relationships => [['a', 'c'],\n            ['b', 'c'],\n            ['a', 'd'],\n            ['b', 'd']])\n      end\n\n      it \"between collection expressions\" do\n        assert_creates_relationships(\"File <| mode == '0644' |> -> File <| mode == '0755' |>\",\n          :relationships => [['a', 'd'],\n            ['b', 'd'],\n            ['c', 'd'],\n            ['a', 'e'],\n            ['b', 'e'],\n            ['c', 'e']])\n      end\n\n      it \"between resources expressed as Strings\" do\n        assert_creates_relationships(\"'File[a]' -> 'File[b]'\",\n          :relationships => [['a', 'b']])\n      end\n\n      it \"between resources expressed as variables\" do\n        assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']])\n          $var = File[a]\n          $var -> File[b]\n        MANIFEST\n\n      end\n\n      it \"between resources expressed as case statements\" do\n        assert_creates_relationships(<<-MANIFEST, :relationships => [['s1', 't2']])\n          $var = 10\n          case $var {\n            10: {\n              file { s1: }\n            }\n            12: {\n              file { s2: }\n            }\n          }\n          ->\n          case $var + 2 {\n            10: {\n              file { t1: }\n            }\n            12: {\n              file { t2: }\n            }\n          }\n        MANIFEST\n      end\n\n      it \"using deep access in array\" do\n        assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']])\n          $var = [ [ [ File[a], File[b] ] ] ]\n          $var[0][0][0] -> $var[0][0][1]\n        MANIFEST\n\n      end\n\n      it \"using deep access in hash\" do\n        assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']])\n          $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}}\n          $var[foo][bar][source] -> $var[foo][bar][target]\n        MANIFEST\n\n      end\n\n      it \"using resource declarations\" do\n        assert_creates_relationships(\"file { l: } -> file { r: }\", :relationships => [['l', 'r']])\n      end\n\n      it \"between entries in a chain of relationships\" do\n        assert_creates_relationships(\"File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]\",\n          :relationships => [['a', 'b'], ['d', 'c']],\n          :subscriptions => [['b', 'c'], ['e', 'd']])\n      end\n\n      it 'should close the gap created by an intermediate empty set produced by collection' do\n        source = \"file { [aa, bb]: } [File[a], File[aa]] -> Notify<| tag == 'na' |> ~> [File[b], File[bb]]\"\n        assert_creates_relationships(source,\n          :relationships => [ ],\n          :subscriptions => [['a', 'b'],['aa', 'b'],['a', 'bb'], ['aa', 'bb']])\n      end\n\n      it 'should close the gap created by empty set followed by empty collection' do\n        source = \"file { [aa, bb]: } [File[a], File[aa]] -> [] -> Notify<| tag == 'na' |> ~> [File[b], File[bb]]\"\n        assert_creates_relationships(source,\n          :relationships => [ ],\n          :subscriptions => [['a', 'b'],['aa', 'b'],['a', 'bb'], ['aa', 'bb']])\n      end\n\n      it 'should close the gap created by empty collection surrounded by empty sets' do\n        source = \"file { [aa, bb]: } [File[a], File[aa]] -> [] -> Notify<| tag == 'na' |> -> [] ~> [File[b], File[bb]]\"\n        assert_creates_relationships(source,\n          :relationships => [ ],\n          :subscriptions => [['a', 'b'],['aa', 'b'],['a', 'bb'], ['aa', 'bb']])\n      end\n\n      it 'should close the gap created by several intermediate empty sets produced by collection' do\n        source = \"file { [aa, bb]: } [File[a], File[aa]] -> Notify<| tag == 'na' |> -> Notify<| tag == 'na' |> ~> [File[b], File[bb]]\"\n        assert_creates_relationships(source,\n          :relationships => [ ],\n          :subscriptions => [['a', 'b'],['aa', 'b'],['a', 'bb'], ['aa', 'bb']])\n      end\n    end\n\n    context \"when dealing with variable references\" do\n      it 'an initial underscore in a variable name is ok' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          class a { $_a = 10}\n          include a\n          notify { 'test': message => $a::_a }\n        MANIFEST\n\n        expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, 10)\n      end\n\n      it 'an initial underscore in not ok if elsewhere than last segment' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class a { $_a = 10}\n            include a\n            notify { 'test': message => $_a::_a }\n          MANIFEST\n        end.to raise_error(/Illegal variable name/)\n      end\n\n      it 'a missing variable as default causes an evaluation error' do\n        # strict mode on by default for 8.x\n        expect {\n          compile_to_catalog(<<-MANIFEST)\n          class a ($b=$x) { notify {test: message=>\"yes ${undef == $b}\" } }\n            include a\n          MANIFEST\n        }.to raise_error(/Evaluation Error: Unknown variable: 'x'/)\n      end\n\n      it 'a missing variable as default value becomes undef when strict mode is off' do\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :warning\n        catalog = compile_to_catalog(<<-MANIFEST)\n        class a ($b=$x) { notify {test: message=>\"yes ${undef == $b}\" } }\n          include a\n        MANIFEST\n\n        expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, \"yes true\")\n      end\n    end\n\n    context \"when dealing with resources (e.g. File) that modifies its name from title\" do\n      [['', ''],\n       ['', '/'],\n       ['/', ''],\n       ['/', '/']].each do |t, r|\n        it \"a circular reference can be compiled with endings: title='#{t}' and ref='#{r}'\" do\n          expect {\n            node = Puppet::Node.new(\"testing\")\n            compile_to_catalog(<<-\"MANIFEST\", node)\n            file { '/tmp/bazinga.txt#{t}':\n              content => 'henrik testing',\n              require => File['/tmp/bazinga.txt#{r}']\n            }\n          MANIFEST\n          }.not_to raise_error\n        end\n      end\n    end\n\n    context 'when working with the trusted data hash' do\n      context 'and have opted in to hashed_node_data' do\n        it 'should make $trusted available' do\n          node = Puppet::Node.new(\"testing\")\n          node.trusted_data = { \"data\" => \"value\" }\n\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            notify { 'test': message => $trusted[data] }\n          MANIFEST\n\n          expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, \"value\")\n        end\n\n        it 'should not allow assignment to $trusted' do\n          node = Puppet::Node.new(\"testing\")\n          node.trusted_data = { \"data\" => \"value\" }\n\n          expect do\n            compile_to_catalog(<<-MANIFEST, node)\n              $trusted = 'changed'\n              notify { 'test': message => $trusted == 'changed' }\n            MANIFEST\n          end.to raise_error(Puppet::PreformattedError, /Attempt to assign to a reserved variable name: '\\$trusted'/)\n        end\n      end\n    end\n\n    context 'when using typed parameters in definition' do\n      it 'accepts type compliant arguments' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define foo(String $x) { }\n          foo { 'test': x =>'say friend' }\n        MANIFEST\n        expect(catalog).to have_resource(\"Foo[test]\").with_parameter(:x, 'say friend')\n      end\n\n      it 'accepts undef as the default for an Optional argument' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define foo(Optional[String] $x = undef) {\n            notify { \"expected\": message => $x == undef }\n          }\n          foo { 'test': }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[expected]\").with_parameter(:message, true)\n      end\n\n      it 'accepts anything when parameters are untyped' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n          define foo($a, $b, $c) { }\n          foo { 'test': a => String, b=>10, c=>undef }\n          MANIFEST\n        end.to_not raise_error()\n      end\n\n      it 'denies non type compliant arguments' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            define foo(Integer $x) { }\n            foo { 'test': x =>'say friend' }\n          MANIFEST\n        end.to raise_error(/Foo\\[test\\]: parameter 'x' expects an Integer value, got String/)\n      end\n\n      it 'denies undef for a non-optional type' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            define foo(Integer $x) { }\n            foo { 'test': x => undef }\n          MANIFEST\n        end.to raise_error(/Foo\\[test\\]: parameter 'x' expects an Integer value, got Undef/)\n      end\n\n      it 'denies non type compliant default argument' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            define foo(Integer $x = 'pow') { }\n            foo { 'test':  }\n          MANIFEST\n        end.to raise_error(/Foo\\[test\\]: parameter 'x' expects an Integer value, got String/)\n      end\n\n      it 'denies undef as the default for a non-optional type' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            define foo(Integer $x = undef) { }\n            foo { 'test':  }\n          MANIFEST\n        end.to raise_error(/Foo\\[test\\]: parameter 'x' expects an Integer value, got Undef/)\n      end\n\n      it 'accepts a Resource as a Type' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define bar($text) { }\n          define foo(Type[Bar] $x) {\n            notify { 'test': message => $x[text] }\n          }\n          bar { 'joke': text => 'knock knock' }\n          foo { 'test': x => Bar[joke] }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, 'knock knock')\n      end\n\n      it 'uses infer_set when reporting type mismatch' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            define foo(Struct[{b => Integer, d=>String}] $a) { }\n            foo{ bar: a => {b => 5, c => 'stuff'}}\n          MANIFEST\n        end.to raise_error(/Foo\\[bar\\]:\\s+parameter 'a' expects a value for key 'd'\\s+parameter 'a' unrecognized key 'c'/m)\n      end\n\n      it 'handles Sensitive type in resource array' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define foo(Sensitive[String] $password) {\n            notify{ \"${title}\": message => \"${password}\" }\n          }\n          foo { ['testA', 'testB']: password =>Sensitive('some password') }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[testA]\").with_parameter(:message, 'Sensitive [value redacted]')\n        expect(catalog).to have_resource(\"Notify[testB]\").with_parameter(:message, 'Sensitive [value redacted]')\n      end\n    end\n\n    context 'when using typed parameters in class' do\n      it 'accepts type compliant arguments' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          class foo(String $x) { }\n          class { 'foo': x =>'say friend' }\n        MANIFEST\n        expect(catalog).to have_resource(\"Class[Foo]\").with_parameter(:x, 'say friend')\n      end\n\n      it 'accepts undef as the default for an Optional argument' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          class foo(Optional[String] $x = undef) {\n            notify { \"expected\": message => $x == undef }\n          }\n          class { 'foo': }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[expected]\").with_parameter(:message, true)\n      end\n\n      it 'accepts anything when parameters are untyped' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class foo($a, $b, $c) { }\n            class { 'foo': a => String, b=>10, c=>undef }\n          MANIFEST\n        end.to_not raise_error()\n      end\n\n      it 'denies non type compliant arguments' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class foo(Integer $x) { }\n            class { 'foo': x =>'say friend' }\n          MANIFEST\n        end.to raise_error(/Class\\[Foo\\]: parameter 'x' expects an Integer value, got String/)\n      end\n\n      it 'denies undef for a non-optional type' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class foo(Integer $x) { }\n            class { 'foo': x => undef }\n          MANIFEST\n        end.to raise_error(/Class\\[Foo\\]: parameter 'x' expects an Integer value, got Undef/)\n      end\n\n      it 'denies non type compliant default argument' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class foo(Integer $x = 'pow') { }\n            class { 'foo':  }\n          MANIFEST\n        end.to raise_error(/Class\\[Foo\\]: parameter 'x' expects an Integer value, got String/)\n      end\n\n      it 'denies undef as the default for a non-optional type' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class foo(Integer $x = undef) { }\n            class { 'foo':  }\n          MANIFEST\n        end.to raise_error(/Class\\[Foo\\]: parameter 'x' expects an Integer value, got Undef/)\n      end\n\n      it 'denies a regexp (rich data) argument given to class String parameter (even if later encoding of it is a string)' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            class foo(String $x) { }\n            class { 'foo':  x => /I am a regexp and I don't want to be a String/}\n          MANIFEST\n        end.to raise_error(/Class\\[Foo\\]: parameter 'x' expects a String value, got Regexp/)\n      end\n\n      it 'denies a regexp (rich data) argument given to define String parameter (even if later encoding of it is a string)' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            define foo(String $x) { }\n            foo { 'foo':  x => /I am a regexp and I don't want to be a String/}\n          MANIFEST\n        end.to raise_error(/Foo\\[foo\\]: parameter 'x' expects a String value, got Regexp/)\n      end\n\n      it 'accepts a Resource as a Type' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define bar($text) { }\n          class foo(Type[Bar] $x) {\n            notify { 'test': message => $x[text] }\n          }\n          bar { 'joke': text => 'knock knock' }\n          class { 'foo': x => Bar[joke] }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[test]\").with_parameter(:message, 'knock knock')\n      end\n    end\n\n    context 'when using typed parameters in lambdas' do\n      it 'accepts type compliant arguments' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with('value') |String $x| { notify { \"$x\": } }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[value]\")\n      end\n\n      it 'handles an array as a single argument' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with(['value', 'second']) |$x| { notify { \"${x[0]} ${x[1]}\": } }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[value second]\")\n      end\n\n      it 'denies when missing required arguments' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with(1) |$x, $y| { }\n          MANIFEST\n        end.to raise_error(/Parameter \\$y is required but no value was given/m)\n      end\n\n      it 'accepts anything when parameters are untyped' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          ['value', 1, true, undef].each |$x| { notify { \"value: $x\": } }\n        MANIFEST\n\n        expect(catalog).to have_resource(\"Notify[value: value]\")\n        expect(catalog).to have_resource(\"Notify[value: 1]\")\n        expect(catalog).to have_resource(\"Notify[value: true]\")\n        expect(catalog).to have_resource(\"Notify[value: ]\")\n      end\n\n      it 'accepts type-compliant, slurped arguments' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with(1, 2) |Integer *$x| { notify { \"${$x[0] + $x[1]}\": } }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[3]\")\n      end\n\n      it 'denies non-type-compliant arguments' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with(1) |String $x| { }\n          MANIFEST\n        end.to raise_error(/block parameter 'x' expects a String value, got Integer/m)\n      end\n\n      it 'denies non-type-compliant, slurped arguments' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with(1, \"hello\") |Integer *$x| { }\n          MANIFEST\n        end.to raise_error(/block parameter 'x' expects an Integer value, got String/m)\n      end\n\n      it 'denies non-type-compliant default argument' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with(1) |$x, String $defaulted = 1| { notify { \"${$x + $defaulted}\": }}\n          MANIFEST\n        end.to raise_error(/block parameter 'defaulted' expects a String value, got Integer/m)\n      end\n\n      it 'raises an error when a default argument value is an incorrect type and there are no arguments passed' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with() |String $defaulted = 1| {}\n          MANIFEST\n        end.to raise_error(/block parameter 'defaulted' expects a String value, got Integer/m)\n      end\n\n      it 'raises an error when the default argument for a slurped parameter is an incorrect type' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with() |String *$defaulted = 1| {}\n          MANIFEST\n        end.to raise_error(/block parameter 'defaulted' expects a String value, got Integer/m)\n      end\n\n      it 'allows using an array as the default slurped value' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with() |String *$defaulted = [hi]| { notify { $defaulted[0]: } }\n        MANIFEST\n\n        expect(catalog).to have_resource('Notify[hi]')\n      end\n\n      it 'allows using a value of the type as the default slurped value' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with() |String *$defaulted = hi| { notify { $defaulted[0]: } }\n        MANIFEST\n\n        expect(catalog).to have_resource('Notify[hi]')\n      end\n\n      it 'allows specifying the type of a slurped parameter as an array' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with() |Array[String] *$defaulted = hi| { notify { $defaulted[0]: } }\n        MANIFEST\n\n        expect(catalog).to have_resource('Notify[hi]')\n      end\n\n      it 'raises an error when the number of default values does not match the parameter\\'s size specification' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with() |Array[String, 2] *$defaulted = hi| { }\n          MANIFEST\n        end.to raise_error(/block expects at least 2 arguments, got 1/m)\n      end\n\n      it 'raises an error when the number of passed values does not match the parameter\\'s size specification' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with(hi) |Array[String, 2] *$passed| { }\n          MANIFEST\n        end.to raise_error(/block expects at least 2 arguments, got 1/m)\n      end\n\n      it 'matches when the number of arguments passed for a slurp parameter match the size specification' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with(hi, bye) |Array[String, 2] *$passed| {\n            $passed.each |$n| { notify { $n: } }\n          }\n        MANIFEST\n\n        expect(catalog).to have_resource('Notify[hi]')\n        expect(catalog).to have_resource('Notify[bye]')\n      end\n\n      it 'raises an error when the number of allowed slurp parameters exceeds the size constraint' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with(hi, bye) |Array[String, 1, 1] *$passed| { }\n          MANIFEST\n        end.to raise_error(/block expects 1 argument, got 2/m)\n      end\n\n      it 'allows passing slurped arrays by specifying an array of arrays' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with([hi], [bye]) |Array[Array[String, 1, 1]] *$passed| {\n            notify { $passed[0][0]: }\n            notify { $passed[1][0]: }\n          }\n        MANIFEST\n\n        expect(catalog).to have_resource('Notify[hi]')\n        expect(catalog).to have_resource('Notify[bye]')\n      end\n\n      it 'raises an error when a required argument follows an optional one' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with() |$y = first, $x, Array[String, 1] *$passed = bye| {}\n          MANIFEST\n        end.to raise_error(/Parameter \\$x is required/)\n      end\n\n      it 'raises an error when the minimum size of a slurped argument makes it required and it follows an optional argument' do\n        expect do\n          compile_to_catalog(<<-MANIFEST)\n            with() |$x = first, Array[String, 1] *$passed| {}\n          MANIFEST\n        end.to raise_error(/Parameter \\$passed is required/)\n      end\n\n      it 'allows slurped arguments with a minimum size of 0 after an optional argument' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          with() |$x = first, Array[String, 0] *$passed| {\n            notify { $x: }\n          }\n        MANIFEST\n\n        expect(catalog).to have_resource('Notify[first]')\n      end\n\n      it 'accepts a Resource as a Type' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define bar($text) { }\n          bar { 'joke': text => 'knock knock' }\n\n          with(Bar[joke]) |Type[Bar] $joke| { notify { \"${joke[text]}\": } }\n        MANIFEST\n        expect(catalog).to have_resource(\"Notify[knock knock]\")\n      end\n    end\n  end\n\n  describe \"the compiler when handling aliases\" do\n    include PuppetSpec::Compiler\n\n    def extract_name(ref)\n      ref.sub(/.*\\[(\\w+)\\]/, '\\1')\n    end\n\n    def assert_created_relationships(catalog, type_name, expectations)\n      resources = catalog.resources.select { |res| res.type == type_name }\n\n      actual_relationships, actual_subscriptions = [:before, :notify].map do |relation|\n        resources.map do |res|\n          dependents = Array(res[relation])\n          dependents.map { |ref| [res.title, extract_name(ref)] }\n        end.inject(&:concat)\n      end\n\n      expect(actual_relationships).to match_array(expectations[:relationships] || [])\n      expect(actual_subscriptions).to match_array(expectations[:subscriptions] || [])\n    end\n\n    it 'allows a relationship to be formed using metaparam relationship' do\n      node = Puppet::Node.new(\"testnodex\")\n      catalog = compile_to_catalog(<<-PP, node)\n        notify { 'actual_2':  before => 'Notify[alias_1]' }\n        notify { 'actual_1': alias => 'alias_1' }\n      PP\n      assert_created_relationships(catalog, 'Notify', { :relationships => [['actual_2', 'alias_1']] })\n    end\n\n    it 'allows a relationship to be formed using -> operator and alias' do\n      node = Puppet::Node.new(\"testnodex\")\n      catalog = compile_to_catalog(<<-PP, node)\n        notify { 'actual_2':  }\n        notify { 'actual_1': alias => 'alias_1' }\n        Notify[actual_2] -> Notify[alias_1]\n      PP\n      assert_created_relationships(catalog, 'Notify', { :relationships => [['actual_2', 'alias_1']] })\n    end\n\n    it 'errors when an alias cannot be found when relationship is formed with -> operator' do\n      node = Puppet::Node.new(\"testnodex\")\n      expect {\n        compile_to_catalog(<<-PP, node)\n          notify { 'actual_2':  }\n          notify { 'actual_1': alias => 'alias_1' }\n          Notify[actual_2] -> Notify[alias_2]\n        PP\n      }.to raise_error(/Could not find resource 'Notify\\[alias_2\\]'/)\n    end\n  end\n\n  describe 'the compiler when using collection and override' do\n    include PuppetSpec::Compiler\n\n    it 'allows an override when there is a default present' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        Package { require => Class['bar'] }\n        class bar { }\n        class foo {\n          package { 'python': }\n          package { 'pip': require => Package['python'] }\n\n          Package <| title == 'pip' |> {\n            name     => \"python-pip\",\n            category => undef,\n          }\n        }\n        include foo\n        include bar\n      MANIFEST\n      expect(catalog.resource('Package', 'pip')[:require].to_s).to eql('Package[python]')\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/conditionals_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe \"Evaluation of Conditionals\" do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context \"a catalog built with conditionals\" do\n    it \"evaluates an if block correctly\" do\n      catalog = compile_to_catalog(<<-CODE)\n      if( 1 == 1) {\n        notify { 'if': }\n      } elsif(2 == 2) {\n        notify { 'elsif': }\n      } else {\n        notify { 'else': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[if]\")\n    end\n\n    it \"evaluates elsif block\" do\n      catalog = compile_to_catalog(<<-CODE)\n      if( 1 == 3) {\n        notify { 'if': }\n      } elsif(2 == 2) {\n        notify { 'elsif': }\n      } else {\n        notify { 'else': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[elsif]\")\n    end\n\n    it \"reaches the else clause if no expressions match\" do\n      catalog = compile_to_catalog(<<-CODE)\n      if( 1 == 2) {\n        notify { 'if': }\n      } elsif(2 == 3) {\n        notify { 'elsif': }\n      } else {\n        notify { 'else': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[else]\")\n    end\n\n    it \"evalutes false to false\" do\n      catalog = compile_to_catalog(<<-CODE)\n      if false {\n      } else {\n        notify { 'false': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[false]\")\n    end\n\n    it \"evaluates the string 'false' as true\" do\n      catalog = compile_to_catalog(<<-CODE)\n      if 'false' {\n        notify { 'true': }\n      } else {\n        notify { 'false': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[true]\")\n    end\n\n    it \"evaluates undefined variables as false\" do\n      # strict mode is off so behavior this test is trying to check isn't stubbed out\n      Puppet[:strict_variables] = false\n      Puppet[:strict] = :warning\n      catalog = compile_to_catalog(<<-CODE)\n      if $undef_var {\n      } else {\n        notify { 'undef': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[undef]\")\n    end\n\n    it \"evaluates empty string as true\" do\n      catalog = compile_to_catalog(<<-CODE)\n      if '' {\n        notify { 'true': }\n      } else {\n        notify { 'empty': }\n      }\n      CODE\n      expect(catalog).to have_resource(\"Notify[true]\")\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/integration/parser/dynamic_scoping_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/parser/parser_factory'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\nrequire 'matchers/resource'\n\n# These tests are in a separate file since othr compiler related tests have\n# been dramatically changed between 3.x and 4.x and it is a pain to merge\n# them.\n#\ndescribe \"Puppet::Parser::Compiler when dealing with relative naming\" do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  describe \"the compiler when using 4.x parser and evaluator\" do\n    it \"should use absolute references even if references are not anchored\" do\n      node = Puppet::Node.new(\"testnodex\")\n      catalog = compile_to_catalog(<<-PP, node)\n      class foo::thing {\n        notify {\"from foo::thing\":}\n      }\n\n      class thing {\n        notify {\"from ::thing\":}\n      }\n\n      class foo {\n      #  include thing\n        class {'thing':}\n      }\n\n      include foo\n      PP\n\n      catalog = Puppet::Parser::Compiler.compile(node)\n\n      expect(catalog).to have_resource(\"Notify[from ::thing]\")\n    end\n\n    it \"should use absolute references when references are absolute\" do\n      node = Puppet::Node.new(\"testnodex\")\n      catalog = compile_to_catalog(<<-PP, node)\n      class foo::thing {\n        notify {\"from foo::thing\":}\n      }\n\n      class thing {\n        notify {\"from ::thing\":}\n      }\n\n      class foo {\n      #  include thing\n        class {'::thing':}\n      }\n\n      include foo\n      PP\n\n      catalog = Puppet::Parser::Compiler.compile(node)\n\n      expect(catalog).to have_resource(\"Notify[from ::thing]\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/environment_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"A parser environment setting\" do\n\n  let(:confdir) { Puppet[:confdir] }\n  let(:environmentpath) { File.expand_path(\"envdir\", confdir) }\n  let(:testingdir) { File.join(environmentpath, \"testing\") }\n\n  before(:each) do\n    FileUtils.mkdir_p(testingdir)\n  end\n\n  it \"selects the given parser when compiling\" do\n    manifestsdir = File.expand_path(\"manifests\", confdir)\n    FileUtils.mkdir_p(manifestsdir)\n\n    File.open(File.join(testingdir, \"environment.conf\"), \"w\") do |f|\n      f.puts(<<-ENVCONF)\n        parser='future'\n        manifest =#{manifestsdir}\n      ENVCONF\n    end\n\n    File.open(File.join(confdir, \"puppet.conf\"), \"w\") do |f|\n      f.puts(<<-EOF)\n          environmentpath=#{environmentpath}\n          parser='current'\n      EOF\n    end\n\n    File.open(File.join(manifestsdir, \"site.pp\"), \"w\") do |f|\n      f.puts(\"notice( [1,2,3].map |$x| { $x*10 })\")\n    end\n\n    expect { a_catalog_compiled_for_environment('testing') }.to_not raise_error\n  end\n\n  def a_catalog_compiled_for_environment(envname)\n    Puppet.initialize_settings\n    expect(Puppet[:environmentpath]).to eq(environmentpath)\n    node = Puppet::Node.new('testnode', :environment => 'testing')\n    expect(node.environment).to eq(Puppet.lookup(:environments).get('testing'))\n    Puppet.override(:current_environment => Puppet.lookup(:environments).get('testing')) do\n      Puppet::Parser::Compiler.compile(node)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/node_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'node statements' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'nodes' do\n    it 'selects a node where the name is just a number' do\n      # Future parser doesn't allow a number in this position\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"5\"))\n      node 5 { notify { 'matched': } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'selects the node with a matching name' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node noden {}\n      node nodename { notify { matched: } }\n      node name {}\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'prefers a node with a literal name over one with a regex' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node /noden.me/ { notify { ignored: } }\n      node nodename { notify { matched: } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'selects a node where one of the names matches' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node different, nodename, other { notify { matched: } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'arbitrarily selects one of the matching nodes' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node /not/ { notify { 'is not matched': } }\n      node /name.*/ { notify { 'could be matched': } }\n      node /na.e/ { notify { 'could also be matched': } }\n      MANIFEST\n\n      expect([catalog.resource('Notify[could be matched]'), catalog.resource('Notify[could also be matched]')].compact).to_not be_empty\n    end\n\n    it 'selects a node where one of the names matches with a mixture of literals and regex' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node different, /name/, other { notify { matched: } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'that have regex names should not collide with matching class names' do\n        catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"foo\"))\n        class foo {\n          $bar = 'one'\n        }\n\n        node /foo/ {\n          $bar = 'two'\n          include foo\n          notify{\"${::foo::bar}\":}\n        }\n        MANIFEST\n        expect(catalog).to have_resource('Notify[one]')\n    end\n\n    it 'does not raise an error with regex and non-regex node names are the same' do\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n        node /a.*(c)?/ { }\n        node 'a.c' { }\n        MANIFEST\n      end.not_to raise_error\n    end\n\n    it 'does not raise an error with 2 regex node names are the same due to lookarround pattern' do\n      expect do\n        compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"async\"))\n        node /(?<!a)sync/ { }\n        node /async/ { }\n        MANIFEST\n      end.not_to raise_error\n    end\n\n    it 'errors when two nodes with regexes collide after some regex syntax is removed' do\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n        node /a.*(c)?/ { }\n        node /a.*c/ { }\n        MANIFEST\n      end.to raise_error(Puppet::Error, /Node '__node_regexp__a.c' is already defined/)\n    end\n\n    it 'provides captures from the regex in the node body' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node /(.*)/ { notify { \"$1\": } }\n      MANIFEST\n      expect(catalog).to have_resource('Notify[nodename]')\n    end\n\n    it 'selects the node with the matching regex' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n      node /node.*/ { notify { matched: } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'selects a node that is a literal string' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"node.name\"))\n      node 'node.name' { notify { matched: } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'does not treat regex symbols as a regex inside a string literal' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodexname\"))\n      node 'node.name' { notify { 'not matched': } }\n      node 'nodexname' { notify { 'matched': } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'errors when two nodes have the same name' do\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n        node name { }\n        node 'name' { }\n        MANIFEST\n      end.to raise_error(Puppet::Error, /Node 'name' is already defined/)\n    end\n\n    it 'is unable to parse a name that is an invalid number' do\n      expect do\n        compile_to_catalog('node 5name {} ')\n      end.to raise_error(Puppet::Error, /Illegal number '5name'/)\n    end\n\n    it 'parses a node name that is dotted numbers' do\n      catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"1.2.3.4\"))\n        node 1.2.3.4 { notify { matched: } }\n      MANIFEST\n\n      expect(catalog).to have_resource('Notify[matched]')\n    end\n\n    it 'raises error for node inheritance' do\n      expect do\n        compile_to_catalog(<<-MANIFEST, Puppet::Node.new(\"nodename\"))\n        node default {}\n          node nodename inherits default {  }\n        MANIFEST\n      end.to raise_error(/Node inheritance is not supported in Puppet >= 4\\.0\\.0/)\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "spec/integration/parser/parameter_defaults_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/language'\n\n['Function', 'EPP'].each do |call_type|\n  describe \"#{call_type} parameter default expressions\" do\n    let! (:func_bodies) {\n      [\n        '{}',\n        '{ notice(\"\\$a == ${a}\") }',\n        '{ notice(\"\\$a == ${a}\") notice(\"\\$b == ${b}\") }',\n        '{ notice(\"\\$a == ${a}\") notice(\"\\$b == ${b}\")  notice(\"\\$c == ${c}\") }'\n      ]\n    }\n\n    let! (:epp_bodies) {\n      [\n        '',\n        '<% notice(\"\\$a == ${a}\") %>',\n        '<% notice(\"\\$a == ${a}\") notice(\"\\$b == ${b}\") %>',\n        '<% notice(\"\\$a == ${a}\") notice(\"\\$b == ${b}\")  notice(\"\\$c == ${c}\") %>'\n      ]\n    }\n    let! (:param_names) {  ('a'..'c').to_a }\n\n    let (:call_type) { call_type }\n\n    let (:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new('specification')) }\n\n    let (:topscope) { compiler.topscope }\n\n    def collect_notices(code)\n      logs = []\n      Puppet[:code] = code\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          compiler.compile do |catalog|\n            yield\n            catalog\n          end\n      end\n      logs.select { |log| log.level == :notice }.map { |log| log.message }\n    end\n\n    # @param arg_list [String] comma separated parameter declarations. Not enclosed in parenthesis\n    # @param body [String,Integer] verbatim body or index of an entry in func_bodies\n    # @param call_params [Array[#to_s]] array of call parameters\n    # @return [Array] array of notice output entries\n    #\n    def eval_collect_notices(arg_list, body, call_params)\n      call = call_params.is_a?(String) ? call_params : \"example(#{call_params.map {|p| p.nil? ? 'undef' : (p.is_a?(String) ? \"'#{p}'\" : p )}.join(',')})\"\n      body = func_bodies[body] if body.is_a?(Integer)\n      evaluator = Puppet::Pops::Parser::EvaluatingParser.new()\n      collect_notices(\"function example(#{arg_list}) #{body}\") do\n        evaluator.evaluate_string(compiler.topscope, call)\n      end\n    end\n\n    # @param arg_list [String] comma separated parameter declarations. Not enclosed in parenthesis\n    # @param body [String,Integer] verbatim body or index of an entry in bodies\n    # @param call_params [Array] array of call parameters\n    # @param code [String] code to evaluate\n    # @return [Array] array of notice output entries\n    #\n    def epp_eval_collect_notices(arg_list, body, call_params, code, inline_epp)\n      body = body.is_a?(Integer) ? epp_bodies[body] : body.strip\n      source = \"<%| #{arg_list} |%>#{body}\"\n      named_params = call_params.reduce({}) {|h, v| h[param_names[h.size]] = v; h }\n      collect_notices(code) do\n        if inline_epp\n          Puppet::Pops::Evaluator::EppEvaluator.inline_epp(compiler.topscope, source, named_params)\n        else\n          file = Tempfile.new(['epp-script', '.epp'])\n          begin\n            file.write(source)\n            file.close\n            Puppet::Pops::Evaluator::EppEvaluator.epp(compiler.topscope, file.path, 'test', named_params)\n          ensure\n            file.unlink\n          end\n        end\n      end\n    end\n\n\n    # evaluates a function or EPP call, collects notice output in a log and compares log to expected result\n    #\n    # @param decl [Array[]] two element array with argument list declaration and body. Body can a verbatim string\n    #   or an integer index of an entry in bodies\n    # @param call_params [Array[#to_s]] array of call parameters\n    # @param code [String] code to evaluate. Only applicable when call_type == 'EPP'\n    # @param inline_epp [Boolean] true for inline_epp, false for file based epp, Only applicable when call_type == 'EPP'\n    # @return [Array] array of notice output entries\n    #\n    def expect_log(decl, call_params, result, code = 'undef', inline_epp = true)\n      if call_type == 'Function'\n        expect(eval_collect_notices(decl[0], decl[1], call_params)).to include(*result)\n      else\n        expect(epp_eval_collect_notices(decl[0], decl[1], call_params, code, inline_epp)).to include(*result)\n      end\n    end\n\n    # evaluates a function or EPP call and expects a failure\n    #\n    # @param decl [Array[]] two element array with argument list declaration and body. Body can a verbatim string\n    #   or an integer index of an entry in bodies\n    # @param call_params [Array[#to_s]] array of call parameters\n    # @param text [String,Regexp] expected error message\n    def expect_fail(decl, call, text, inline_epp = true)\n      if call_type == 'Function'\n        expect{eval_collect_notices(decl[0], decl[1], call) }.to raise_error(StandardError, text)\n      else\n        expect{epp_eval_collect_notices(decl[0], decl[1], call, 'undef', inline_epp) }.to raise_error(StandardError, text)\n      end\n    end\n\n    context 'that references a parameter to the left that has no default' do\n      let!(:params) { [ <<-SOURCE, 2 ]\n      $a,\n      $b = $a\n      SOURCE\n      }\n\n      it 'fails when no value is provided for required first parameter', :if => call_type == 'Function' do\n        expect_fail(params, [], /expects between 1 and 2 arguments, got none/)\n      end\n\n      it 'fails when no value is provided for required first parameter', :if => call_type == 'EPP' do\n        expect_fail(params, [], /expects a value for parameter \\$a/)\n      end\n\n      it \"will use the referenced parameter's given value\" do\n        expect_log(params, [2], ['$a == 2', '$b == 2'])\n      end\n\n      it 'will not be evaluated when a value is given' do\n        expect_log(params, [2, 5], ['$a == 2', '$b == 5'])\n      end\n    end\n\n    context 'that references a parameter to the left that has a default' do\n      let!(:params) { [ <<-SOURCE, 2 ]\n      $a = 10,\n      $b = $a\n      SOURCE\n      }\n\n      it \"will use the referenced parameter's default value when no value is given for the referenced parameter\" do\n        expect_log(params, [], ['$a == 10', '$b == 10'])\n      end\n\n      it \"will use the referenced parameter's given value\" do\n        expect_log(params, [2], ['$a == 2', '$b == 2'])\n      end\n\n      it 'will not be evaluated when a value is given' do\n        expect_log(params, [2, 5], ['$a == 2', '$b == 5'])\n      end\n    end\n\n    context 'that references a variable to the right' do\n      let!(:params) { [ <<-SOURCE, 3 ]\n      $a = 10,\n      $b = $c,\n      $c = 20\n      SOURCE\n      }\n\n      it 'fails when the reference is evaluated' do\n        expect_fail(params, [1], /default expression for \\$b tries to illegally access not yet evaluated \\$c/)\n      end\n\n      it 'does not fail when a value is given for the culprit parameter' do\n        expect_log(params, [1,2], ['$a == 1', '$b == 2', '$c == 20'])\n      end\n\n      it 'does not fail when all values are given' do\n        expect_log(params, [1,2,3], ['$a == 1', '$b == 2', '$c == 3'])\n      end\n    end\n\n    context 'with regular expressions' do\n      it \"evaluates unset match scope parameter's to undef\" do\n        expect_log([<<-SOURCE, 2], [], ['$a == ', '$b == '])\n        $a = $0,\n        $b = $1\n        SOURCE\n      end\n\n      it 'does not leak match variables from one expression to the next' do\n        expect_log([<<-SOURCE, 2], [], ['$a == [true, h, ello]', '$b == '])\n        $a = ['hello' =~ /(h)(.*)/, $1, $2],\n        $b = $1\n        SOURCE\n      end\n\n      it 'can evaluate expressions in separate match scopes' do\n        expect_log([<<-SOURCE, 3], [], ['$a == [true, h, ell, o]', '$b == [true, h, i, ]', '$c == '])\n        $a = ['hello' =~ /(h)(.*)(o)/, $1, $2, $3],\n        $b = ['hi' =~ /(h)(.*)/, $1, $2, $3],\n        $c = $1\n        SOURCE\n      end\n\n      it 'can have nested match expressions' do\n        expect_log([<<-SOURCE, 2], [], ['$a == [true, h, oo, h, i]', '$b == '] )\n        $a = ['hi' =~ /(h)(.*)/, $1, if'foo' =~ /f(oo)/ { $1 }, $1, $2],\n        $b = $0\n        SOURCE\n      end\n\n      it 'can not see match scope from calling scope', :if => call_type == 'Function' do\n        expect_log([<<-SOURCE, <<-BODY], <<-CALL, ['$a == '])\n        $a = $0\n        SOURCE\n        {\n          notice(\"\\\\$a == ${a}\")\n        }\n        function caller() {\n          example()\n        }\n        BODY\n        $tmp = 'foo' =~ /(f)(o)(o)/\n        caller()\n        CALL\n      end\n\n      context 'matches in calling scope', :if => call_type == 'EPP' do\n        it 'are available when using inlined epp' do\n          # Note that CODE is evaluated before the EPP is evaluated\n          #\n          expect_log([<<-SOURCE, <<-BODY], [], ['$ax == true', '$bx == foo'], <<-CODE, true)\n          $a = $tmp,\n          $b = $0\n          SOURCE\n          <% called_from_template($a, $b) %>\n          BODY\n          function called_from_template($ax, $bx) {\n            notice(\"\\\\$ax == $ax\")\n            notice(\"\\\\$bx == $bx\")\n          }\n          $tmp = 'foo' =~ /(f)(o)(o)/\n          CODE\n        end\n\n        it 'are not available when using epp file' do\n          # Note that CODE is evaluated before the EPP is evaluated\n          #\n          expect_log([<<-SOURCE, <<-BODY], [], ['$ax == true', '$bx == '], <<-CODE, false)\n          $a = $tmp,\n          $b = $0\n            SOURCE\n          <% called_from_template($a, $b) %>\n            BODY\n          function called_from_template($ax, $bx) {\n            notice(\"\\\\$ax == $ax\")\n            notice(\"\\\\$bx == $bx\")\n          }\n          $tmp = 'foo' =~ /(f)(o)(o)/\n          CODE\n        end\n      end\n\n\n      it 'will allow nested lambdas to access enclosing match scope' do\n        expect_log([<<-SOURCE, 1], [], ['$a == [1-ello, 2-ello, 3-ello]'])\n        $a = case \"hello\" {\n          /(h)(.*)/ : {\n            [1,2,3].map |$x| { \"$x-$2\" }\n          }\n        }\n        SOURCE\n      end\n\n      it \"will not make match scope available to #{call_type} body\" do\n        expect_log([<<-SOURCE, call_type == 'Function' ? <<-BODY : <<-EPP_BODY], [], ['Yes'])\n        $a = \"hello\" =~ /.*/\n        SOURCE\n        {\n          notice(\"Y${0}es\")\n        }\n        BODY\n        <%\n          notice(\"Y${0}es\")\n        %>\n        EPP_BODY\n      end\n\n      it 'can access earlier match results when produced using the match function' do\n        expect_log([<<-SOURCE, 3], [], ['$a == [hello, h, ello]', '$b == hello', '$c == h'])\n        $a = 'hello'.match(/(h)(.*)/),\n        $b = $a[0],\n        $c = $a[1]\n        SOURCE\n      end\n    end\n\n\n    context 'will not permit assignments' do\n      it 'at top level' do\n        expect_fail([<<-SOURCE, 0], [], /Syntax error at '='/)\n        $a = $x = $0\n        SOURCE\n      end\n\n      it 'in arrays' do\n        expect_fail([<<-SOURCE, 0], [], /Assignment not allowed here/)\n        $a = [$x = 3]\n        SOURCE\n      end\n\n      it 'of variable with the same name as a subsequently declared parameter' do\n        expect_fail([<<-SOURCE, 0], [], /Assignment not allowed here/)\n        $a = ($b = 3),\n        $b = 5\n        SOURCE\n      end\n\n      it 'of variable with the same name as a previously declared parameter' do\n        expect_fail([<<-SOURCE, 0], [], /Assignment not allowed here/)\n        $a = 10,\n        $b = ($a = 10)\n        SOURCE\n      end\n    end\n\n    it 'will permit assignments in nested scope' do\n      expect_log([<<-SOURCE, 3], [], ['$a == [1, 2, 3]', '$b == 0', '$c == [6, 12, 18]'])\n      $a = [1,2,3],\n      $b = 0,\n      $c = $a.map |$x| { $b = $x; $b * $a.reduce |$x, $y| {$x + $y} }\n      SOURCE\n    end\n\n    it 'will not permit duplicate parameter names' do\n      expect_fail([<<-SOURCE, 0], [], /The parameter 'a' is declared more than once/ )\n      $a = 2,\n      $a = 5\n      SOURCE\n    end\n\n    it 'will permit undef for optional parameters' do\n      expect_log([<<-SOURCE, 1], [nil], ['$a == '])\n      Optional[Integer] $a\n      SOURCE\n    end\n\n    it 'undef will override parameter default', :if => call_type == 'Function' do\n      expect_log([<<-SOURCE, 1], [nil], ['$a == '])\n      Optional[Integer] $a = 4\n      SOURCE\n    end\n\n    it 'undef will not override parameter default', :unless => call_type == 'Function' do\n      expect_log([<<-SOURCE, 1], [nil], ['$a == 4'])\n      Optional[Integer] $a = 4\n      SOURCE\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/pcore_resource_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\n\nrequire 'puppet/face'\n\ndescribe 'when pcore described resources types are in use' do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  let(:genface) { Puppet::Face[:generate, :current] }\n\n  context \"in an environment with two modules\" do\n    let(:dir) do\n      dir_containing('environments', { 'production' => {\n        'environment.conf' => \"modulepath = modules\",\n        'manifests' => { 'site.pp' => \"\" },\n        'modules' => {\n          'm1' => {\n            'lib' => { 'puppet' => { 'type' => {\n              'test1.rb' => <<-EOF\n              module Puppet\n              Type.newtype(:test1) do\n                @doc = \"Docs for resource\"\n                newproperty(:message) do\n                  desc \"Docs for 'message' property\"\n                end\n                newparam(:name) do\n                  desc \"Docs for 'name' parameter\"\n                  isnamevar\n                end\n                newparam(:whatever) do\n                  desc \"Docs for 'whatever' parameter\"\n                end\n              end; end\n              EOF\n             } }\n          },\n        },\n        'm2' => {\n          'lib' => { 'puppet' => { 'type' => {\n            'test2.rb' => <<-EOF,\n            module Puppet\n            Type.newtype(:test2) do\n              @doc = \"Docs for resource\"\n              @isomorphic = false\n              newproperty(:message) do\n                desc \"Docs for 'message' property\"\n              end\n              newparam(:name) do\n                desc \"Docs for 'name' parameter\"\n                isnamevar\n              end\n              newparam(:color) do\n                desc \"Docs for 'color' parameter\"\n                newvalues(:red, :green, :blue, /#[0-9A-Z]{6}/)\n              end\n            end;end\n            EOF\n            'test3.rb' => <<-RUBY,\n              Puppet::Type.newtype(:test3) do\n                newproperty(:message)\n                newparam(:a) { isnamevar }\n                newparam(:b) { isnamevar }\n                newparam(:c) { isnamevar }\n                def self.title_patterns\n                  [ [ /^((.+)\\\\/(.*))$/,  [[:a], [:b], [:c]]] ]\n                end\n              end\n            RUBY\n           } } },\n        }\n      }}})\n    end\n\n    let(:modulepath) do\n      File.join(dir, 'production', 'modules')\n    end\n\n    let(:m1) do\n      File.join(modulepath, 'm1')\n    end\n\n    let(:m2) do\n      File.join(modulepath, 'm2')\n    end\n\n    let(:outputdir) do\n      File.join(dir, 'production', '.resource_types')\n    end\n\n    around(:each) do |example|\n      Puppet.settings.initialize_global_settings\n      Puppet[:manifest] = ''\n      loader = Puppet::Environments::Directories.new(dir, [])\n      Puppet.override(:environments => loader) do\n        Puppet.override(:current_environment => loader.get('production')) do\n          example.run\n        end\n      end\n    end\n\n    it 'can use generated types to compile a catalog' do\n      genface.types\n      catalog = compile_to_catalog(<<-MANIFEST)\n        test1 { 'a':\n          message => 'a works'\n        }\n        # Several instances of the type can be created - implicit test\n        test1 { 'another a':\n          message => 'another a works'\n        }\n        test2 { 'b':\n          message => 'b works'\n        }\n        test3 { 'x/y':\n          message => 'x/y works'\n        }\n      MANIFEST\n      expect(catalog.resource(:test1, \"a\")['message']).to eq('a works')\n      expect(catalog.resource(:test2, \"b\")['message']).to eq('b works')\n      expect(catalog.resource(:test3, \"x/y\")['message']).to eq('x/y works')\n    end\n\n    it 'considers Pcore types to be builtin ' do\n      genface.types\n      catalog = compile_to_catalog(<<-MANIFEST)\n        test1 { 'a':\n          message => 'a works'\n        }\n      MANIFEST\n      expect(catalog.resource(:test1, \"a\").kind).to eq('compilable_type')\n    end\n\n    it 'considers Pcore types to be builtin ' do\n      genface.types\n      catalog = compile_to_catalog(<<-MANIFEST)\n        test1 { 'a':\n          message => 'a works'\n        }\n      MANIFEST\n      expect(catalog.resource(:test1, \"a\").kind).to eq('compilable_type')\n    end\n\n    it 'the validity of attribute names are checked' do\n      genface.types\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n          test1 { 'a':\n            mezzage => 'a works'\n          }\n        MANIFEST\n      end.to raise_error(/no parameter named 'mezzage'/)\n    end\n\n    it 'meta-parameters such as noop can be used' do\n      genface.types\n      catalog = compile_to_catalog(<<-MANIFEST)\n        test1 { 'a':\n          message => 'noop works',\n          noop => true\n        }\n      MANIFEST\n      expect(catalog.resource(:test1, \"a\")['noop']).to eq(true)\n    end\n\n    it 'a generated type describes if it is isomorphic' do\n      generate_and_in_a_compilers_context do |compiler|\n        t1 = find_resource_type(compiler.topscope, 'test1')\n        expect(t1.isomorphic?).to be(true)\n        t2 = find_resource_type(compiler.topscope, 'test2')\n        expect(t2.isomorphic?).to be(false)\n      end\n    end\n\n    it 'a generated type returns parameters defined in pcore' do\n      generate_and_in_a_compilers_context do |compiler|\n        t1 = find_resource_type(compiler.topscope, 'test1')\n        expect(t1.parameters.size).to be(2)\n        expect(t1.parameters[0].name).to eql('name')\n        expect(t1.parameters[1].name).to eql('whatever')\n      end\n    end\n\n    it 'a generated type picks up and returns if a parameter is a namevar' do\n      generate_and_in_a_compilers_context do |compiler|\n        t1 = find_resource_type(compiler.topscope, 'test1')\n        expect(t1.parameters[0].name_var).to be(true)\n        expect(t1.parameters[1].name_var).to be(false)\n      end\n    end\n\n    it 'a generated type returns properties defined in pcore' do\n      generate_and_in_a_compilers_context do |compiler|\n        t1 = find_resource_type(compiler.topscope, 'test1')\n        expect(t1.properties.size).to be(1)\n        expect(t1.properties[0].name).to eql('message')\n      end\n    end\n\n    it 'a generated type returns [[/(.*)/m, <first attr>]] as default title_pattern when there is a namevar but no pattern specified' do\n      generate_and_in_a_compilers_context do |compiler|\n        t1 = find_resource_type(compiler.topscope, 'test1')\n        expect(t1.title_patterns.size).to be(1)\n        expect(t1.title_patterns[0][0]).to eql(/(?m-ix:(.*))/)\n      end\n    end\n\n    it \"the compiler asserts the type of parameters\" do\n      pending \"assertion of parameter types not yet implemented\"\n      genface.types\n      expect {\n      compile_to_catalog(<<-MANIFEST)\n        test2 { 'b':\n          color => 'white is not a color'\n        }\n      MANIFEST\n      }.to raise_error(/an error indicating that color cannot have that value/) # ERROR TBD.\n    end\n  end\n\n  def find_resource_type(scope, name)\n    Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type(scope, name)\n  end\n\n  def generate_and_in_a_compilers_context(&block)\n    genface.types\n    # Since an instance of a compiler is needed and it starts an initial import that evaluates\n    # code, and that code will be loaded from manifests with a glob (go figure)\n    # the only way to stop that is to set 'code' to something as that overrides \"importing\" files.\n    Puppet[:code] = \"undef\"\n    node = Puppet::Node.new('test')\n    # All loading must be done in a context configured as the compiler does it.\n    # (Therefore: use the context a compiler creates as this test logic must otherwise\n    #  know how to do this).\n    #\n    compiler = Puppet::Parser::Compiler.new(node)\n    Puppet::override(compiler.context_overrides) do\n      block.call(compiler)\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/integration/parser/resource_expressions_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/language'\n\ndescribe \"Puppet resource expressions\" do\n  extend PuppetSpec::Language\n\n  produces(\n  \"$a = notify\n       $b = example\n       $c = { message => hello }\n       @@Resource[$a] {\n         $b:\n           * => $c\n       }\n       realize(Resource[$a, $b])\n       \" => \"Notify[example][message] == 'hello'\")\n\n  context \"resource titles\" do\n    produces(\n    \"notify { thing: }\"                     => \"defined(Notify[thing])\",\n    \"$x = thing notify { $x: }\"             => \"defined(Notify[thing])\",\n\n    \"notify { [thing]: }\"                   => \"defined(Notify[thing])\",\n    \"$x = [thing] notify { $x: }\"           => \"defined(Notify[thing])\",\n\n    \"notify { [[nested, array]]: }\"         => \"defined(Notify[nested]) and defined(Notify[array])\",\n    \"$x = [[nested, array]] notify { $x: }\" => \"defined(Notify[nested]) and defined(Notify[array])\",\n\n    \"notify { []: }\"                        => [], # this asserts nothing added\n    \"$x = [] notify { $x: }\"                => [], # this asserts nothing added\n\n    \"notify { default: }\"                   => \"!defined(Notify['default'])\", # nothing created because this is just a local default\n    \"$x = default notify { $x: }\"           => \"!defined(Notify['default'])\")\n\n    fails(\n    \"notify { '': }\"                         => /Empty string title/,\n    \"$x = '' notify { $x: }\"                 => /Empty string title/,\n\n    \"notify { 1: }\"                          => /Illegal title type.*Expected String, got Integer/,\n    \"$x = 1 notify { $x: }\"                  => /Illegal title type.*Expected String, got Integer/,\n\n    \"notify { [1]: }\"                        => /Illegal title type.*Expected String, got Integer/,\n    \"$x = [1] notify { $x: }\"                => /Illegal title type.*Expected String, got Integer/,\n\n    \"notify { 3.0: }\"                        => /Illegal title type.*Expected String, got Float/,\n    \"$x = 3.0 notify { $x: }\"                => /Illegal title type.*Expected String, got Float/,\n\n    \"notify { [3.0]: }\"                      => /Illegal title type.*Expected String, got Float/,\n    \"$x = [3.0] notify { $x: }\"              => /Illegal title type.*Expected String, got Float/,\n\n    \"notify { true: }\"                       => /Illegal title type.*Expected String, got Boolean/,\n    \"$x = true notify { $x: }\"               => /Illegal title type.*Expected String, got Boolean/,\n\n    \"notify { [true]: }\"                     => /Illegal title type.*Expected String, got Boolean/,\n    \"$x = [true] notify { $x: }\"             => /Illegal title type.*Expected String, got Boolean/,\n\n    \"notify { [false]: }\"                    => /Illegal title type.*Expected String, got Boolean/,\n    \"$x = [false] notify { $x: }\"            => /Illegal title type.*Expected String, got Boolean/,\n\n    \"notify { undef: }\"                      => /Missing title.*undef/,\n    \"$x = undef notify { $x: }\"              => /Missing title.*undef/,\n\n    \"notify { [undef]: }\"                    => /Missing title.*undef/,\n    \"$x = [undef] notify { $x: }\"            => /Missing title.*undef/,\n\n    \"notify { {nested => hash}: }\"           => /Illegal title type.*Expected String, got Hash/,\n    \"$x = {nested => hash} notify { $x: }\"   => /Illegal title type.*Expected String, got Hash/,\n\n    \"notify { [{nested => hash}]: }\"         => /Illegal title type.*Expected String, got Hash/,\n    \"$x = [{nested => hash}] notify { $x: }\" => /Illegal title type.*Expected String, got Hash/,\n\n    \"notify { /regexp/: }\"                   => /Illegal title type.*Expected String, got Regexp/,\n    \"$x = /regexp/ notify { $x: }\"           => /Illegal title type.*Expected String, got Regexp/,\n\n    \"notify { [/regexp/]: }\"                 => /Illegal title type.*Expected String, got Regexp/,\n    \"$x = [/regexp/] notify { $x: }\"         => /Illegal title type.*Expected String, got Regexp/,\n\n    \"notify { [dupe, dupe]: }\"               => /The title 'dupe' has already been used/,\n    \"notify { dupe:; dupe: }\"                => /The title 'dupe' has already been used/,\n    \"notify { [dupe]:; dupe: }\"              => /The title 'dupe' has already been used/,\n    \"notify { [default, default]:}\"          => /The title 'default' has already been used/,\n    \"notify { default:; default:}\"           => /The title 'default' has already been used/,\n    \"notify { [default]:; default:}\"         => /The title 'default' has already been used/)\n  end\n\n  context \"type names\" do\n    produces( \"notify { testing: }\"                            => \"defined(Notify[testing])\")\n    produces( \"$a = notify; Resource[$a] { testing: }\"         => \"defined(Notify[testing])\")\n    produces( \"Resource['notify'] { testing: }\"                => \"defined(Notify[testing])\")\n    produces( \"Resource[sprintf('%s', 'notify')] { testing: }\" => \"defined(Notify[testing])\")\n    produces( \"$a = ify; Resource[\\\"not$a\\\"] { testing: }\"     => \"defined(Notify[testing])\")\n\n    produces( \"Notify { testing: }\"           => \"defined(Notify[testing])\")\n    produces( \"Resource[Notify] { testing: }\" => \"defined(Notify[testing])\")\n    produces( \"Resource['Notify'] { testing: }\"         => \"defined(Notify[testing])\")\n\n    produces( \"class a { notify { testing: } } class { a: }\"   => \"defined(Notify[testing])\")\n    produces( \"class a { notify { testing: } } Class { a: }\"   => \"defined(Notify[testing])\")\n    produces( \"class a { notify { testing: } } Resource['class'] { a: }\" => \"defined(Notify[testing])\")\n\n    produces( \"define a::b { notify { testing: } } a::b { title: }\" => \"defined(Notify[testing])\")\n    produces( \"define a::b { notify { testing: } } A::B { title: }\" => \"defined(Notify[testing])\")\n    produces( \"define a::b { notify { testing: } } Resource['a::b'] { title: }\" => \"defined(Notify[testing])\")\n\n    fails( \"'class' { a: }\"              => /Illegal Resource Type expression.*got String/)\n    fails( \"'' { testing: }\"             => /Illegal Resource Type expression.*got String/)\n    fails( \"1 { testing: }\"              => /Illegal Resource Type expression.*got Integer/)\n    fails( \"3.0 { testing: }\"            => /Illegal Resource Type expression.*got Float/)\n    fails( \"true { testing: }\"           => /Illegal Resource Type expression.*got Boolean/)\n    fails( \"'not correct' { testing: }\"  => /Illegal Resource Type expression.*got String/)\n\n    fails( \"Notify[hi] { testing: }\"     => /Illegal Resource Type expression.*got Notify\\['hi'\\]/)\n    fails( \"[Notify, File] { testing: }\" => /Illegal Resource Type expression.*got Array\\[Type\\[Resource\\]\\]/)\n\n    fails( \"define a::b { notify { testing: } } 'a::b' { title: }\" => /Illegal Resource Type expression.*got String/)\n\n    fails( \"Does::Not::Exist { title: }\" => /Resource type not found: Does::Not::Exist/)\n  end\n\n  context \"local defaults\" do\n    produces(\n    \"notify { example:;                     default: message => defaulted }\" => \"Notify[example][message] == 'defaulted'\",\n    \"notify { example: message => specific; default: message => defaulted }\" => \"Notify[example][message] == 'specific'\",\n    \"notify { example: message => undef;    default: message => defaulted }\" => \"Notify[example][message] == undef\",\n    \"notify { [example, other]: ;           default: message => defaulted }\" => \"Notify[example][message] == 'defaulted' and Notify[other][message] == 'defaulted'\",\n    \"notify { [example, default]: message => set; other: }\"                  => \"Notify[example][message] == 'set' and Notify[other][message] == 'set'\")\n  end\n\n  context \"order of evaluation\" do\n    fails(\"notify { hi: message => value; bye: message => Notify[hi][message] }\" => /Resource not found: Notify\\['hi'\\]/)\n\n    produces(\"notify { hi: message => (notify { param: message => set }); bye: message => Notify[param][message] }\" => \"defined(Notify[hi]) and Notify[bye][message] == 'set'\")\n    fails(\"notify { bye: message => Notify[param][message]; hi: message => (notify { param: message => set }) }\" => /Resource not found: Notify\\['param'\\]/)\n  end\n\n  context \"parameters\" do\n    produces(\n    \"notify { title: message => set }\"                   => \"Notify[title][message] == 'set'\",\n    \"$x = set notify { title: message => $x }\"           => \"Notify[title][message] == 'set'\",\n\n    \"notify { title: *=> { message => set } }\"           => \"Notify[title][message] == 'set'\",\n\n    \"$x = { message => set } notify { title: * => $x }\"  => \"Notify[title][message] == 'set'\",\n\n    # picks up defaults\n    \"$x = { owner => the_x }\n         $y = { mode =>  '0666' }\n         $t = '/tmp/x'\n         file {\n           default:\n             * => $x;\n           $t:\n             path => '/somewhere',\n             * => $y }\"  => \"File[$t][mode] == '0666' and File[$t][owner] == 'the_x' and File[$t][path] == '/somewhere'\",\n\n    # explicit wins over default - no error\n    \"$x = { owner => the_x, mode => '0777' }\n         $y = { mode =>  '0666' }\n         $t = '/tmp/x'\n         file {\n           default:\n             * => $x;\n           $t:\n             path => '/somewhere',\n             * => $y }\"  => \"File[$t][mode] == '0666' and File[$t][owner] == 'the_x' and File[$t][path] == '/somewhere'\")\n\n    produces(\"notify{title:}; Notify[title] { * => { message => set}}\"  => \"Notify[title][message] == 'set'\")\n    produces(\"Notify { * => { message => set}}; notify{title:}\"         => \"Notify[title][message] == 'set'\")\n    produces('define foo($x) { notify { \"title\": message =>\"aaa${x}bbb\"} } foo{ test: x => undef }' => \"Notify[title][message] == 'aaabbb'\")\n    produces('define foo($x=\"xx\") { notify { \"title\": message =>\"aaa${x}bbb\"} } foo{ test: x => undef }' => \"Notify[title][message] == 'aaaxxbbb'\")\n\n    fails(\"notify { title: unknown => value }\" => /no parameter named 'unknown'/)\n\n    # this really needs to be a better error message.\n    fails(\"notify { title: * => { hash => value }, message => oops }\" => /no parameter named 'hash'/)\n\n    # should this be a better error message?\n    fails(\"notify { title: message => oops, * => { hash => value } }\" => /no parameter named 'hash'/)\n\n    fails(\"notify { title: * => { unknown => value } }\" => /no parameter named 'unknown'/)\n    fails(\"\n         $x = { mode => '0666' }\n         $y = { owner => the_y }\n         $t = '/tmp/x'\n         file { $t:\n           * => $x,\n           * => $y }\"  => /Unfolding of attributes from Hash can only be used once per resource body/)\n  end\n\n  context \"virtual\" do\n    produces(\n    \"@notify { example: }\"                     => \"!defined(Notify[example])\",\n\n    \"@notify { example: }\n         realize(Notify[example])\"                 => \"defined(Notify[example])\",\n\n    \"@notify { virtual: message => set }\n         notify { real:\n           message => Notify[virtual][message] }\"  => \"Notify[real][message] == 'set'\")\n  end\n\n  context \"exported\" do\n    produces(\n    \"@@notify { example: }\" => \"!defined(Notify[example])\",\n    \"@@notify { example: } realize(Notify[example])\" => \"defined(Notify[example])\",\n    \"@@notify { exported: message => set } notify { real: message => Notify[exported][message] }\" => \"Notify[real][message] == 'set'\")\n  end\n\n  context \"explicit undefs\" do\n    # PUP-3505\n    produces(\"\n        $x = 10\n        define foo($x = undef) {\n          notify { example:\n            message => \\\"'$x'\\\"\n          }\n        }\n        foo {'blah': x => undef }\n      \" => \"Notify[example][message] == \\\"''\\\"\")\n  end\nend\n\n"
  },
  {
    "path": "spec/integration/parser/scope_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe \"Two step scoping for variables\" do\n  include PuppetSpec::Compiler\n  def expect_the_message_to_be(message, node = Puppet::Node.new('the node'))\n    catalog = compile_to_catalog(yield, node)\n    expect(catalog.resource('Notify', 'something')[:message]).to eq(message)\n  end\n\n  def expect_the_message_not_to_be(message, node = Puppet::Node.new('the node'))\n    catalog = compile_to_catalog(yield, node)\n    expect(catalog.resource('Notify', 'something')[:message]).to_not eq(message)\n  end\n\n  before :each do\n    expect(Puppet).not_to receive(:deprecation_warning)\n  end\n\n  describe \"using unsupported operators\" do\n    it \"issues an error for +=\" do\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n              $var = [\"top_msg\"]\n              node default {\n                $var += [\"override\"]\n              }\n        MANIFEST\n      end.to raise_error(/The operator '\\+=' is no longer supported/)\n    end\n\n    it \"issues an error for -=\" do\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n              $var = [\"top_msg\"]\n              node default {\n                $var -= [\"top_msg\"]\n              }\n        MANIFEST\n      end.to raise_error(/The operator '-=' is no longer supported/)\n    end\n\n    it \"issues error about built-in variable when reassigning to name\" do\n        enc_node = Puppet::Node.new(\"the_node\", { :parameters => {  } })\n\n        expect {\n          compile_to_catalog(\"$name = 'never in a 0xF4240 years'\", enc_node)\n        }.to raise_error(\n          Puppet::Error,\n          /Cannot reassign built in \\(or already assigned\\) variable '\\$name' \\(line: 1(, column: 7)?\\) on node the_node/\n        )\n    end\n\n    it \"issues error about built-in variable when reassigning to title\" do\n        enc_node = Puppet::Node.new(\"the_node\", { :parameters => {  } })\n\n        expect {\n          compile_to_catalog(\"$title = 'never in a 0xF4240 years'\", enc_node)\n        }.to raise_error(\n          Puppet::Error,\n          /Cannot reassign built in \\(or already assigned\\) variable '\\$title' \\(line: 1(, column: 8)?\\) on node the_node/\n        )\n    end\n\n    it \"when using a template ignores the dynamic value of the var when using the @varname syntax\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n          node default {\n            $var = \"node_msg\"\n            include foo\n          }\n          class foo {\n            $var = \"foo_msg\"\n            include bar\n          }\n          class bar {\n            notify { 'something': message => inline_template(\"<%= @var %>\"), }\n          }\n      MANIFEST\n    end\n  end\n\n  it \"when using a template gets the var from an inherited class when using the @varname syntax\" do\n    expect_the_message_to_be('Barbamama') do <<-MANIFEST\n          node default {\n            $var = \"node_msg\"\n            include bar_bamama\n            include foo\n          }\n          class bar_bamama {\n            $var = \"Barbamama\"\n          }\n          class foo {\n            $var = \"foo_msg\"\n            include bar\n          }\n          class bar inherits bar_bamama {\n            notify { 'something': message => inline_template(\"<%= @var %>\"), }\n          }\n      MANIFEST\n    end\n  end\n\n  it \"when using a template ignores the dynamic var when it is not present in an inherited class\" do\n    expect_the_message_to_be('node_msg') do <<-MANIFEST\n          node default {\n            $var = \"node_msg\"\n            include bar_bamama\n            include foo\n          }\n          class bar_bamama {\n          }\n          class foo {\n            $var = \"foo_msg\"\n            include bar\n          }\n          class bar inherits bar_bamama {\n            notify { 'something': message => inline_template(\"<%= @var %>\"), }\n          }\n        MANIFEST\n      end\n    end\n\n    describe 'handles 3.x/4.x functions' do\n      it 'can call a 3.x function via call_function' do\n        expect_the_message_to_be('yes') do <<-MANIFEST\n          $msg = inline_template('<%= scope().call_function(\"fqdn_rand\", [30]).to_i <= 30 ? \"yes\" : \"no\" %>')\n          notify { 'something': message => $msg }\n          MANIFEST\n        end\n      end\n\n      it 'it can call a 4.x function via call_function' do\n        expect_the_message_to_be('yes') do <<-MANIFEST\n          $msg = inline_template('<%= scope().call_function(\"with\", [\"yes\"]) { |x| x } %>')\n          notify { 'something': message => $msg }\n          MANIFEST\n        end\n      end\n    end\n  end\n\n  describe \"fully qualified variable names\" do\n    it \"keeps nodescope separate from topscope\" do\n      expect_the_message_to_be('topscope') do <<-MANIFEST\n            $c = \"topscope\"\n            node default {\n              $c = \"nodescope\"\n              notify { 'something': message => $::c }\n            }\n        MANIFEST\n      end\n    end\n  end\n\n  describe \"when colliding class and variable names\" do\n    it \"finds a topscope variable with the same name as a class\" do\n      expect_the_message_to_be('topscope') do <<-MANIFEST\n            $c = \"topscope\"\n            class c { }\n            node default {\n              include c\n              notify { 'something': message => $c }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds a node scope variable with the same name as a class\" do\n      expect_the_message_to_be('nodescope') do <<-MANIFEST\n            class c { }\n            node default {\n              $c = \"nodescope\"\n              include c\n              notify { 'something': message => $c }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds a class variable when the class collides with a nodescope variable\" do\n      expect_the_message_to_be('class') do <<-MANIFEST\n            class c { $b = \"class\" }\n            node default {\n              $c = \"nodescope\"\n              include c\n              notify { 'something': message => $c::b }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds a class variable when the class collides with a topscope variable\" do\n      expect_the_message_to_be('class') do <<-MANIFEST\n            $c = \"topscope\"\n            class c { $b = \"class\" }\n            node default {\n              include c\n              notify { 'something': message => $::c::b }\n            }\n        MANIFEST\n      end\n    end\n  end\n\n  describe \"when using shadowing and inheritance\" do\n    it \"finds values in its local scope\" do\n      expect_the_message_to_be('local_msg') do <<-MANIFEST\n            node default {\n              include baz\n            }\n            class foo {\n            }\n            class bar inherits foo {\n              $var = \"local_msg\"\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds values in its inherited scope\" do\n      expect_the_message_to_be('foo_msg') do <<-MANIFEST\n            node default {\n              include baz\n            }\n            class foo {\n              $var = \"foo_msg\"\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"prefers values in its local scope over values in the inherited scope\" do\n      expect_the_message_to_be('local_msg') do <<-MANIFEST\n            include bar\n\n            class foo {\n              $var = \"inherited\"\n            }\n\n            class bar inherits foo {\n              $var = \"local_msg\"\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds a qualified variable by following inherited scope of the specified scope\" do\n      expect_the_message_to_be(\"from parent\") do <<-MANIFEST\n            class c {\n              notify { 'something': message => \"$a::b\" }\n            }\n            class parent {\n              $b = 'from parent'\n            }\n            class a inherits parent { }\n\n            node default {\n              $b = \"from node\"\n              include a\n              include c\n            }\n        MANIFEST\n      end\n    end\n\n    ['a:.b', '::a::b'].each do |ref|\n      it \"does not resolve a qualified name on the form #{ref} against top scope\" do\n        # strict mode is off so behavior this test is trying to check isn't stubbed out\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :warning\n        expect_the_message_not_to_be(\"from topscope\") do <<-\"MANIFEST\"\n              class c {\n                notify { 'something': message => \"$#{ref}\" }\n              }\n              class parent {\n                $not_b = 'from parent'\n              }\n              class a inherits parent { }\n\n              $b = \"from topscope\"\n              node default {\n                include a\n                include c\n              }\n          MANIFEST\n        end\n      end\n    end\n\n    ['a:.b', '::a::b'].each do |ref|\n      it \"does not resolve a qualified name on the form #{ref} against node scope\" do\n        # strict mode is off so behavior this test is trying to check isn't stubbed out\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :warning\n        expect_the_message_not_to_be(\"from node\") do <<-MANIFEST\n              class c {\n                notify { 'something': message => \"$a::b\" }\n              }\n              class parent {\n                $not_b = 'from parent'\n              }\n              class a inherits parent { }\n\n              node default {\n                $b = \"from node\"\n                include a\n                include c\n              }\n          MANIFEST\n        end\n      end\n    end\n\n    it 'resolves a qualified name in class parameter scope' do\n      expect_the_message_to_be('Does it work? Yes!') do <<-PUPPET\n        class a ( \n          $var1 = 'Does it work?',\n          $var2 = \"${a::var1} Yes!\"\n        ) { \n          notify { 'something': message => $var2 }\n        }\n        include a\n        PUPPET\n      end\n    end\n\n    it \"finds values in its inherited scope when the inherited class is qualified to the top\" do\n      expect_the_message_to_be('foo_msg') do <<-MANIFEST\n            node default {\n              include baz\n            }\n            class foo {\n              $var = \"foo_msg\"\n            }\n            class bar inherits ::foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"prefers values in its local scope over values in the inherited scope when the inherited class is fully qualified\" do\n      expect_the_message_to_be('local_msg') do <<-MANIFEST\n            include bar\n\n            class foo {\n              $var = \"inherited\"\n            }\n\n            class bar inherits ::foo {\n              $var = \"local_msg\"\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds values in top scope when the inherited class is qualified to the top\" do\n      expect_the_message_to_be('top msg') do <<-MANIFEST\n            $var = \"top msg\"\n            class foo {\n            }\n\n            class bar inherits ::foo {\n              notify { 'something': message => $var, }\n            }\n\n            include bar\n        MANIFEST\n      end\n    end\n\n    it \"finds values in its inherited scope when the inherited class is a nested class that shadows another class at the top\" do\n      expect_the_message_to_be('inner baz') do <<-MANIFEST\n            node default {\n              include foo::bar\n            }\n            class baz {\n              $var = \"top baz\"\n            }\n            class foo {\n              class baz {\n                $var = \"inner baz\"\n              }\n\n              class bar inherits foo::baz {\n                notify { 'something': message => $var, }\n              }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds values in its inherited scope when the inherited class is qualified to a nested class and qualified to the top\" do\n      expect_the_message_to_be('top baz') do <<-MANIFEST\n            node default {\n              include foo::bar\n            }\n            class baz {\n              $var = \"top baz\"\n            }\n            class foo {\n              class baz {\n                $var = \"inner baz\"\n              }\n\n              class bar inherits ::baz {\n                notify { 'something': message => $var, }\n              }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds values in its inherited scope when the inherited class is qualified\" do\n      expect_the_message_to_be('foo_msg') do <<-MANIFEST\n            node default {\n              include bar\n            }\n            class foo {\n              class baz {\n                $var = \"foo_msg\"\n              }\n            }\n            class bar inherits foo::baz {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"prefers values in its inherited scope over those in the node (with intermediate inclusion)\" do\n      expect_the_message_to_be('foo_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include baz\n            }\n            class foo {\n              $var = \"foo_msg\"\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"prefers values in its inherited scope over those in the node (without intermediate inclusion)\" do\n      expect_the_message_to_be('foo_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include bar\n            }\n            class foo {\n              $var = \"foo_msg\"\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"prefers values in its inherited scope over those from where it is included\" do\n      expect_the_message_to_be('foo_msg') do <<-MANIFEST\n            node default {\n              include baz\n            }\n            class foo {\n              $var = \"foo_msg\"\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              $var = \"baz_msg\"\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"does not used variables from classes included in the inherited scope\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include bar\n            }\n            class quux {\n              $var = \"quux_msg\"\n            }\n            class foo inherits quux {\n            }\n            class baz {\n              include foo\n            }\n            class bar inherits baz {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"does not use a variable from a scope lexically enclosing it\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include other::bar\n            }\n            class other {\n              $var = \"other_msg\"\n              class bar {\n                notify { 'something': message => $var, }\n              }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds values in its node scope\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include baz\n            }\n            class foo {\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds values in its top scope\" do\n      expect_the_message_to_be('top_msg') do <<-MANIFEST\n            $var = \"top_msg\"\n            node default {\n              include baz\n            }\n            class foo {\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              include bar\n            }\n        MANIFEST\n      end\n    end\n\n    it \"prefers variables from the node over those in the top scope\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            $var = \"top_msg\"\n            node default {\n              $var = \"node_msg\"\n              include foo\n            }\n            class foo {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds top scope variables referenced inside a defined type\" do\n      expect_the_message_to_be('top_msg') do <<-MANIFEST\n            $var = \"top_msg\"\n            node default {\n              foo { \"testing\": }\n            }\n            define foo() {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"finds node scope variables referenced inside a defined type\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            $var = \"top_msg\"\n            node default {\n              $var = \"node_msg\"\n              foo { \"testing\": }\n            }\n            define foo() {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n  end\n\n  describe \"in situations that used to have dynamic lookup\" do\n    it \"ignores the dynamic value of the var\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include foo\n            }\n            class baz {\n              $var = \"baz_msg\"\n              include bar\n            }\n            class foo inherits baz {\n            }\n            class bar {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"raises an evaluation error when the only set variable is in the dynamic scope\" do\n      expect {\n        compile_to_catalog(<<-MANIFEST)\n            node default {\n              include baz\n            }\n            class foo {\n            }\n            class bar inherits foo {\n              notify { 'something': message => $var, }\n            }\n            class baz {\n              $var = \"baz_msg\"\n              include bar\n            }\n        MANIFEST\n      }.to raise_error(/Evaluation Error: Unknown variable: 'var'./)\n    end\n\n    it \"ignores the value in the dynamic scope for a defined type\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include foo\n            }\n            class foo {\n              $var = \"foo_msg\"\n              bar { \"testing\": }\n            }\n            define bar() {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"when using a template ignores the dynamic value of the var when using scope.lookupvar\" do\n      expect_the_message_to_be('node_msg') do <<-MANIFEST\n            node default {\n              $var = \"node_msg\"\n              include foo\n            }\n            class foo {\n              $var = \"foo_msg\"\n              include bar\n            }\n            class bar {\n              notify { 'something': message => inline_template(\"<%= scope.lookupvar('var') %>\"), }\n            }\n        MANIFEST\n      end\n    end\n  end\n\n  describe \"when using an enc\" do\n    it \"places enc parameters in top scope\" do\n      enc_node = Puppet::Node.new(\"the node\", { :parameters => { \"var\" => 'from_enc' } })\n\n      expect_the_message_to_be('from_enc', enc_node) do <<-MANIFEST\n            notify { 'something': message => $var, }\n        MANIFEST\n      end\n    end\n\n    it \"does not allow the enc to specify an existing top scope var\" do\n      enc_node = Puppet::Node.new(\"the_node\", { :parameters => { \"var\" => 'from_enc' } })\n      expect {\n        compile_to_catalog(\"$var = 'top scope'\", enc_node)\n      }.to raise_error(\n        Puppet::Error,\n        /Cannot reassign variable '\\$var' \\(line: 1(, column: 6)?\\) on node the_node/\n      )\n    end\n\n    it \"evaluates enc classes in top scope when there is no node\" do\n      enc_node = Puppet::Node.new(\"the node\", { :classes => ['foo'], :parameters => { \"var\" => 'from_enc' } })\n\n      expect_the_message_to_be('from_enc', enc_node) do <<-MANIFEST\n            class foo {\n              notify { 'something': message => $var, }\n            }\n        MANIFEST\n      end\n    end\n\n    it \"overrides enc variables from a node scope var\" do\n      enc_node = Puppet::Node.new(\"the_node\", { :classes => ['foo'], :parameters => { 'enc_var' => 'Set from ENC.' } })\n\n      expect_the_message_to_be('ENC overridden in node', enc_node) do <<-MANIFEST\n            node the_node {\n              $enc_var = \"ENC overridden in node\"\n            }\n\n            class foo {\n              notify { 'something': message => $enc_var, }\n            }\n        MANIFEST\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/script_compiler_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\nrequire 'puppet/parser/script_compiler'\n\ndescribe 'the script compiler' do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n  include Matchers::Resource\n  before(:each) do\n    Puppet[:tasks] = true\n  end\n\n  context \"when used\" do\n    let(:env_name) { 'testenv' }\n    let(:environments_dir) { Puppet[:environmentpath] }\n    let(:env_dir) { File.join(environments_dir, env_name) }\n    let(:manifest) { Puppet::Node::Environment::NO_MANIFEST }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')], manifest) }\n    let(:node) { Puppet::Node.new(\"test\", :environment => env) }\n\n    let(:env_dir_files) {\n      {\n        'manifests' => {\n          'good.pp' => \"'good'\\n\"\n        },\n        'modules' => {\n          'test' => {\n            'plans' => {\n               'run_me.pp' => 'plan test::run_me() { \"worked2\" }'\n            }\n          }\n        }\n      }\n    }\n\n    let(:populated_env_dir) do\n      dir_contained_in(environments_dir, env_name => env_dir_files)\n      PuppetSpec::Files.record_tmp(env_dir)\n      env_dir\n    end\n\n    let(:script_compiler) do\n      Puppet::Parser::ScriptCompiler.new(env, node.name)\n    end\n\n    context 'is configured such that' do\n      it 'returns what the script_compiler returns' do\n        Puppet[:code] = <<-CODE\n            42\n          CODE\n        expect(script_compiler.compile).to eql(42)\n      end\n\n      it 'referencing undefined variables raises an error' do\n        expect do\n          Puppet[:code] = <<-CODE\n              notice $rubyversion\n            CODE\n            Puppet::Parser::ScriptCompiler.new(env, 'test_node_name').compile\n\n        end.to raise_error(/Unknown variable: 'rubyversion'/)\n      end\n\n      it 'has strict=error behavior' do\n        expect do\n          Puppet[:code] = <<-CODE\n              notice({a => 10, a => 20})\n            CODE\n            Puppet::Parser::ScriptCompiler.new(env, 'test_node_name').compile\n\n        end.to raise_error(/The key 'a' is declared more than once/)\n      end\n\n      it 'performing a multi assign from a class reference raises an error' do\n        expect do\n          Puppet[:code] = <<-CODE\n              [$a] = Class[the_dalit]\n            CODE\n            Puppet::Parser::ScriptCompiler.new(env, 'test_node_name').compile\n\n        end.to raise_error(/The catalog operation 'multi var assignment from class' is only available when compiling a catalog/)\n      end\n    end\n\n    context 'when using environment manifest' do\n      context 'set to single file' do\n        let (:manifest) { \"#{env_dir}/manifests/good.pp\" }\n\n        it 'loads and evaluates' do\n          expect(script_compiler.compile).to eql('good')\n        end\n      end\n\n      context 'set to directory' do\n        let (:manifest) { \"#{env_dir}/manifests\" }\n\n        it 'fails with an error' do\n          expect{script_compiler.compile}.to raise_error(/manifest of environment 'testenv' appoints directory '.*\\/manifests'. It must be a file/)\n        end\n      end\n\n      context 'set to non existing path' do\n        let (:manifest) { \"#{env_dir}/manyfiests/good.pp\" }\n\n        it 'fails with an error' do\n          expect{script_compiler.compile}.to raise_error(/manifest of environment 'testenv' appoints '.*\\/good.pp'. It does not exist/)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parser/undef_param_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe \"Parameter passing\" do\n  include PuppetSpec::Compiler\n\n  before :each do\n    # DataBinding will be consulted before falling back to a default value,\n    # but we aren't testing that here\n    allow(Puppet::DataBinding.indirection).to receive(:find)\n  end\n\n  def expect_the_message_to_be(message, node = Puppet::Node.new('the node'))\n    catalog = compile_to_catalog(yield, node)\n    expect(catalog.resource('Notify', 'something')[:message]).to eq(message)\n  end\n\n  def expect_puppet_error(message, node = Puppet::Node.new('the node'))\n    expect { compile_to_catalog(yield, node) }.to raise_error(Puppet::Error, message)\n  end\n\n  it \"overrides the default when a value is given\" do\n    expect_the_message_to_be('2') do <<-MANIFEST\n      define a($x='1') { notify { 'something': message => $x }}\n      a {'a': x => '2'}\n      MANIFEST\n    end\n  end\n\n  it \"shadows an inherited variable with the default value when undef is passed\" do\n    expect_the_message_to_be('default') do <<-MANIFEST\n      class a { $x = 'inherited' }\n      class b($x='default') inherits a { notify { 'something': message => $x }}\n      class { 'b': x => undef}\n      MANIFEST\n    end\n  end\n\n  it \"uses a default value that comes from an inherited class when the parameter is undef\" do\n    expect_the_message_to_be('inherited') do <<-MANIFEST\n      class a { $x = 'inherited' }\n      class b($y=$x) inherits a { notify { 'something': message => $y }}\n      class { 'b': y => undef}\n      MANIFEST\n    end\n  end\n\n  it \"uses a default value that references another variable when the parameter is passed as undef\" do\n    expect_the_message_to_be('a') do <<-MANIFEST\n        define a($a = $title) { notify { 'something': message => $a }}\n        a {'a': a => undef}\n      MANIFEST\n    end\n  end\n\n  it \"uses the default when 'undef' is given'\" do\n    expect_the_message_to_be('1') do <<-MANIFEST\n        define a($x='1') { notify { 'something': message => $x }}\n        a {'a': x => undef}\n      MANIFEST\n    end\n  end\n\n  it \"uses the default when no parameter is provided\" do\n    expect_the_message_to_be('1') do <<-MANIFEST\n        define a($x='1') { notify { 'something': message => $x }}\n        a {'a': }\n      MANIFEST\n    end\n  end\n\n  it \"uses a value of undef when the default is undef and no parameter is provided\" do\n    expect_the_message_to_be(true) do <<-MANIFEST\n        define a($x=undef) { notify { 'something': message => $x == undef}}\n        a {'a': }\n    MANIFEST\n    end\n  end\n\n  it \"errors when no parameter is provided and there is no default\" do\n    expect_puppet_error(/A\\[a\\]: expects a value for parameter 'x'/) do <<-MANIFEST\n        define a($x) { notify { 'something': message => $x }}\n        a {'a': }\n    MANIFEST\n    end\n  end\n\n  it \"uses a given undef and do not require a default expression\" do\n    expect_the_message_to_be(true) do <<-MANIFEST\n        define a(Optional[Integer] $x) { notify { 'something': message => $x == undef}}\n        a {'a': x => undef }\n    MANIFEST\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/provider/file/windows_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\n\n# For some reason the provider test will not filter out on windows when using the\n# :if => Puppet.features.microsoft_windows? method of filtering the tests.\nif Puppet.features.microsoft_windows?\n  require 'puppet/util/windows'\n  describe Puppet::Type.type(:file).provider(:windows), '(integration)' do\n    include PuppetSpec::Compiler\n    include PuppetSpec::Files\n\n    def create_temp_file(owner_sid, group_sid, initial_mode)\n      tmp_file = tmpfile('filewindowsprovider')\n      File.delete(tmp_file) if File.exist?(tmp_file)\n      File.open(tmp_file, 'w') { |file| file.write(\"rspec test\") }\n\n      # There are other tests to ensure that these methods do indeed\n      # set the owner and group.  Therefore it's ok to depend on them\n      # here\n      Puppet::Util::Windows::Security.set_owner(owner_sid, tmp_file) unless owner_sid.nil?\n      Puppet::Util::Windows::Security.set_group(group_sid, tmp_file) unless group_sid.nil?\n      # Pretend we are managing the owner and group to FORCE this mode, even if it's \"bad\"\n      Puppet::Util::Windows::Security.set_mode(initial_mode.to_i(8), tmp_file, true, true, true) unless initial_mode.nil?\n\n      tmp_file\n    end\n\n    def strip_sticky(value)\n      # For the purposes of these tests we don't care about the extra-ace bit in modes\n      # This function removes it\n      value & ~Puppet::Util::Windows::Security::S_IEXTRA\n    end\n\n    sids = {\n      :system => Puppet::Util::Windows::SID::LocalSystem,\n      :administrators => Puppet::Util::Windows::SID::BuiltinAdministrators,\n      :users => Puppet::Util::Windows::SID::BuiltinUsers,\n      :power_users => Puppet::Util::Windows::SID::PowerUsers,\n      :none => Puppet::Util::Windows::SID::Nobody,\n      :everyone => Puppet::Util::Windows::SID::Everyone\n    }\n\n    # Testcase Hash options\n    # create_* : These options are used when creating the initial test file\n    # create_owner (Required!)\n    # create_group (Required!)\n    # create_mode\n    #\n    # manifest_* : These options are used to craft the manifest which is applied to the test file after createion\n    # manifest_owner,\n    # manifest_group,\n    # manifest_mode (Required!)\n    #\n    # actual_* : These options are used to check the _actual_ values as opposed to the munged values from puppet\n    # actual_mode (Uses manifest_mode for checks if not set)\n\n    RSpec.shared_examples \"a mungable file resource\" do |testcase|\n\n      before(:each) do\n        @tmp_file = create_temp_file(sids[testcase[:create_owner]], sids[testcase[:create_group]], testcase[:create_mode])\n        raise \"Could not create temporary file\" if @tmp_file.nil?\n      end\n\n      after(:each) do\n        File.delete(@tmp_file) if File.exist?(@tmp_file)\n      end\n\n      context_name = \"With initial owner '#{testcase[:create_owner]}' and initial group '#{testcase[:create_owner]}'\"\n      context_name += \" and initial mode of '#{testcase[:create_mode]}'\" unless testcase[:create_mode].nil?\n      context_name += \" and a mode of '#{testcase[:manifest_mode]}' in the manifest\"\n      context_name += \" and an owner of '#{testcase[:manifest_owner]}' in the manifest\" unless testcase[:manifest_owner].nil?\n      context_name += \" and a group of '#{testcase[:manifest_group]}' in the manifest\" unless testcase[:manifest_group].nil?\n\n      context context_name do\n        is_idempotent = testcase[:is_idempotent].nil? || testcase[:is_idempotent]\n\n        let(:manifest) do\n          value = <<-MANIFEST\n            file { 'rspec_example':\n              ensure => present,\n              path   => '#{@tmp_file}',\n              mode   => '#{testcase[:manifest_mode]}',\n          MANIFEST\n          value += \"  owner  => '#{testcase[:manifest_owner]}',\\n\" unless testcase[:manifest_owner].nil?\n          value += \"  group  => '#{testcase[:manifest_group]}',\\n\" unless testcase[:manifest_group].nil?\n          value + \"}\"\n        end\n\n        it \"should apply with no errors and have expected ACL\" do\n          apply_with_error_check(manifest)\n          new_mode = strip_sticky(Puppet::Util::Windows::Security.get_mode(@tmp_file))\n          expect(new_mode.to_s(8)).to eq (testcase[:actual_mode].nil? ? testcase[:manifest_mode] : testcase[:actual_mode])\n        end\n\n        it \"should be idempotent\", :if => is_idempotent do\n          result = apply_with_error_check(manifest)\n          result = apply_with_error_check(manifest)\n          # Idempotent. Should be no changed resources\n          expect(result.changed?.count).to eq 0\n        end\n\n        it \"should NOT be idempotent\", :unless => is_idempotent do\n          result = apply_with_error_check(manifest)\n          result = apply_with_error_check(manifest)\n          result = apply_with_error_check(manifest)\n          result = apply_with_error_check(manifest)\n          # Not idempotent. Expect changed resources\n          expect(result.changed?.count).to be > 0\n        end\n      end\n    end\n\n    # These scenarios round-trip permissions and are idempotent\n    [\n      { :create_owner => :system,         :create_group => :administrators, :manifest_mode => '760' },\n      { :create_owner => :administrators, :create_group => :administrators, :manifest_mode => '660' },\n      { :create_owner => :system,         :create_group => :system,         :manifest_mode => '770' },\n    ].each do |testcase|\n      # What happens if the owner and group are not managed\n      it_behaves_like \"a mungable file resource\", testcase\n      # What happens if the owner is managed\n      it_behaves_like \"a mungable file resource\", testcase.merge({ :manifest_owner => testcase[:create_owner]})\n      # What happens if the group is managed\n      it_behaves_like \"a mungable file resource\", testcase.merge({ :manifest_group => testcase[:create_group]})\n      # What happens if both the owner and group are managed\n      it_behaves_like \"a mungable file resource\", testcase.merge({\n        :manifest_owner => testcase[:create_owner],\n        :manifest_group => testcase[:create_group]\n      })\n    end\n\n    # SYSTEM is special in that when specifying less than mode 7, the owner and/or group MUST be managed\n    # otherwise it's munged to 7 behind the scenes and is not idempotent\n    both_system_testcase = { :create_owner => :system, :create_group => :system, :manifest_mode => '660', :actual_mode => '770', :is_idempotent => false }\n    # What happens if the owner and group are not managed\n    it_behaves_like \"a mungable file resource\", both_system_testcase.merge({ :is_idempotent => true })\n    # What happens if the owner is managed\n    it_behaves_like \"a mungable file resource\", both_system_testcase.merge({ :manifest_owner => both_system_testcase[:create_owner]})\n    # What happens if the group is managed\n    it_behaves_like \"a mungable file resource\", both_system_testcase.merge({ :manifest_group => both_system_testcase[:create_group]})\n\n    # However when we manage SYSTEM explicitly, then the modes lower than 7 stick and the file provider\n    # assumes it's insync (i.e. idempotent)\n    it_behaves_like \"a mungable file resource\", both_system_testcase.merge({\n      :manifest_owner => both_system_testcase[:create_owner],\n      :manifest_group => both_system_testcase[:create_group],\n      :actual_mode    => both_system_testcase[:manifest_mode],\n      :is_idempotent  => true\n    })\n\n    # What happens if we _create_ a file that SYSTEM is a part of, and is Full Control, but the manifest says it should not be Full Control\n    # Behind the scenes the mode should be changed to 7 and be idempotent\n    [\n      { :create_owner => :system,         :create_group => :system,         :manifest_mode => '660' },\n      { :create_owner => :administrators, :create_group => :system,         :manifest_mode => '760' },\n      { :create_owner => :system,         :create_group => :administrators, :manifest_mode => '670' },\n    ].each do |testcase|\n      it_behaves_like \"a mungable file resource\", testcase.merge({ :create_mode => '770', :actual_mode => '770'})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/resource/catalog_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Resource::Catalog do\n  describe \"when using the indirector\" do\n    before do\n      # This is so the tests work w/out networking.\n      allow(Facter).to receive(:to_hash).and_return({\"hostname\" => \"foo.domain.com\"})\n      allow(Facter).to receive(:value).and_return(\"eh\")\n    end\n\n    it \"should be able to delegate to the :yaml terminus\" do\n      allow(Puppet::Resource::Catalog.indirection).to receive(:terminus_class).and_return(:yaml)\n\n      # Load now, before we stub the exists? method.\n      terminus = Puppet::Resource::Catalog.indirection.terminus(:yaml)\n      expect(terminus).to receive(:path).with(\"me\").and_return(\"/my/yaml/file\")\n\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/yaml/file\").and_return(false)\n      expect(Puppet::Resource::Catalog.indirection.find(\"me\")).to be_nil\n    end\n\n    it \"should be able to delegate to the :compiler terminus\" do\n      allow(Puppet::Resource::Catalog.indirection).to receive(:terminus_class).and_return(:compiler)\n\n      # Load now, before we stub the exists? method.\n      compiler = Puppet::Resource::Catalog.indirection.terminus(:compiler)\n\n      node = double('node', :add_server_facts => nil, :trusted_data= => nil, :environment => nil)\n\n      expect(Puppet::Node.indirection).to receive(:find).and_return(node)\n      expect(compiler).to receive(:compile).with(node, anything).and_return(nil)\n\n      expect(Puppet::Resource::Catalog.indirection.find(\"me\")).to be_nil\n    end\n\n    it \"should pass provided node information directly to the terminus\" do\n      node = double('node')\n      terminus = double('terminus')\n      allow(terminus).to receive(:validate)\n      expect(terminus).to receive(:find) { |request| expect(request.options[:use_node]).to eq(node) }\n\n      allow(Puppet::Resource::Catalog.indirection).to receive(:terminus).and_return(terminus)\n\n      Puppet::Resource::Catalog.indirection.find(\"me\", :use_node => node)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/resource/type_collection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/files'\nrequire 'puppet/resource/type_collection'\n\ndescribe Puppet::Resource::TypeCollection do\n  describe \"when autoloading from modules\" do\n    include PuppetSpec::Files\n\n    before do\n      @dir = tmpfile(\"autoload_testing\")\n      FileUtils.mkdir_p @dir\n\n      loader = double('loader', load: nil, set_entry: nil)\n      loaders = double('loaders', runtime3_type_loader: loader)\n      expect(Puppet::Pops::Loaders).to receive(:loaders).at_most(:once).and_return(loaders)\n\n      environment = Puppet::Node::Environment.create(:env, [@dir])\n      @code = environment.known_resource_types\n    end\n\n    # Setup a module.\n    def mk_module(name, files = {})\n      mdir = File.join(@dir, name)\n      mandir = File.join(mdir, \"manifests\")\n      FileUtils.mkdir_p mandir\n\n      defs = files.delete(:define)\n\n      Dir.chdir(mandir) do\n        files.each do |file, classes|\n          File.open(\"#{file}.pp\", \"w\") do |f|\n            classes.each { |klass|\n              if defs\n                f.puts \"define #{klass} {}\"\n              else\n                f.puts \"class #{klass} {}\"\n              end\n            }\n          end\n        end\n      end\n    end\n\n    it \"should return nil when a class can't be found or loaded\" do\n      expect(@code.find_hostclass('nosuchclass')).to be_nil\n    end\n\n    it \"should load the module's init file first\" do\n      name = \"simple\"\n      mk_module(name, :init => [name])\n      expect(@code.find_hostclass(name).name).to eq(name)\n    end\n\n    it \"should be able to load definitions from the module base file\" do\n      name = \"simpdef\"\n      mk_module(name, :define => true, :init => [name])\n      expect(@code.find_definition(name).name).to eq(name)\n    end\n\n    it \"should be able to load qualified classes from the module base file\" do\n      mk_module('both', :init => %w{both both::sub})\n      expect(@code.find_hostclass(\"both::sub\").name).to eq(\"both::sub\")\n    end\n\n    it \"should be able load classes from a separate file\" do\n      mk_module('separate', :init => %w{separate}, :sub => %w{separate::sub})\n      expect(@code.find_hostclass(\"separate::sub\").name).to eq(\"separate::sub\")\n    end\n\n    it \"should not fail when loading from a separate file if there is no module file\" do\n      mk_module('alone', :sub => %w{alone::sub})\n      expect { @code.find_hostclass(\"alone::sub\") }.not_to raise_error\n    end\n\n    it \"should be able to load definitions from their own file\" do\n      name = \"mymod\"\n      mk_module(name, :define => true, :mydefine => [\"mymod::mydefine\"])\n      expect(@code.find_definition(\"mymod::mydefine\").name).to eq(\"mymod::mydefine\")\n    end\n\n    it 'should be able to load definitions from their own file using uppercased name' do\n      name = 'mymod'\n      mk_module(name, :define => true, :mydefine => ['mymod::mydefine'])\n      expect(@code.find_definition('Mymod::Mydefine')).not_to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/transaction/report_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Transaction::Report do\n  before :each do\n    # Enable persistence during tests\n    allow_any_instance_of(Puppet::Transaction::Persistence).to receive(:enabled?).and_return(true)\n  end\n\n  describe \"when using the indirector\" do\n    it \"should be able to delegate to the :processor terminus\" do\n      allow(Puppet::Transaction::Report.indirection).to receive(:terminus_class).and_return(:processor)\n\n      terminus = Puppet::Transaction::Report.indirection.terminus(:processor)\n\n      allow(Facter).to receive(:value).and_return(\"host.domain.com\")\n\n      report = Puppet::Transaction::Report.new\n\n      expect(terminus).to receive(:process).with(report)\n\n      Puppet::Transaction::Report.indirection.save(report)\n    end\n  end\n\n  describe \"when dumping to YAML\" do\n    it \"should not contain TagSet objects\" do\n      resource = Puppet::Resource.new(:notify, \"Hello\")\n      ral_resource = resource.to_ral\n      status = Puppet::Resource::Status.new(ral_resource)\n\n      log = Puppet::Util::Log.new(:level => :info, :message => \"foo\")\n\n      report = Puppet::Transaction::Report.new\n      report.add_resource_status(status)\n      report << log\n\n      expect(YAML.dump(report)).to_not match('Puppet::Util::TagSet')\n    end\n  end\n\n  describe \"inference checking\" do\n    include PuppetSpec::Files\n    require 'puppet/configurer'\n\n    def run_catalogs(resources1, resources2, noop1 = false, noop2 = false, &block)\n      last_run_report = nil\n      expect(Puppet::Transaction::Report.indirection).to receive(:save) do |report, x|\n        last_run_report = report\n        true\n      end.exactly(4)\n\n      Puppet[:report] = true\n      Puppet[:noop] = noop1\n\n      configurer = Puppet::Configurer.new\n      configurer.run :catalog => new_catalog(resources1)\n\n      yield block if block\n      last_report = last_run_report\n\n      Puppet[:noop] = noop2\n\n      configurer = Puppet::Configurer.new\n      configurer.run :catalog => new_catalog(resources2)\n\n      expect(last_report).not_to eq(last_run_report)\n\n      return last_run_report\n    end\n\n    def new_blank_catalog\n      Puppet::Resource::Catalog.new(\"testing\", Puppet.lookup(:environments).get(Puppet[:environment]))\n    end\n\n    def new_catalog(resources = [])\n      new_cat = new_blank_catalog\n      [resources].flatten.each do |resource|\n        new_cat.add_resource(resource)\n      end\n      new_cat\n    end\n\n    def get_cc_count(report)\n      report.metrics[\"resources\"].values.each do |v|\n        if v[0] == \"corrective_change\"\n          return v[2]\n        end\n      end\n      return nil\n    end\n\n    describe \"for agent runs that contain\" do\n      it \"notifies with catalog change\" do\n        report = run_catalogs(Puppet::Type.type(:notify).new(:title => \"testing\",\n                                                             :message => \"foo\"),\n                              Puppet::Type.type(:notify).new(:title => \"testing\",\n                                                             :message => \"foobar\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"Notify[testing]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"notifies with no catalog change\" do\n        report = run_catalogs(Puppet::Type.type(:notify).new(:title => \"testing\",\n                                                             :message => \"foo\"),\n                              Puppet::Type.type(:notify).new(:title => \"testing\",\n                                                             :message => \"foo\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"Notify[testing]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"new file resource\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs([],\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"removal of a file resource\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              [])\n\n        expect(report.status).to eq(\"unchanged\")\n        expect(report.resource_statuses[\"File[#{file}]\"]).to eq(nil)\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with a title change\" do\n        file1 = tmpfile(\"test_file\")\n        file2 = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file1,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file2,\n                                                           :content => \"mystuff\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        expect(report.resource_statuses[\"File[#{file1}]\"]).to eq(nil)\n\n        rs = report.resource_statuses[\"File[#{file2}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with no catalog change\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"))\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with a new parameter\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\",\n                                                           :loglevel => :debug))\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with a removed parameter\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\",\n                                                           :loglevel => :debug),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"))\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with a property no longer managed\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file))\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with no catalog change, but file changed between runs\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\")) do\n          File.open(file, 'w') do |f|\n            f.puts \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"file with catalog change, but file changed between runs that matched catalog change\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"some content\")) do\n          File.open(file, 'w') do |f|\n            f.write \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with catalog change, but file changed between runs that did not match catalog change\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff1\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff2\")) do\n          File.open(file, 'w') do |f|\n            f.write \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"file with catalog change\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff1\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff2\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with ensure property set to present\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :present),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :present))\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with ensure property change file => absent\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :file),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :absent))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with ensure property change present => absent\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :present),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :absent))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"link with ensure property change present => absent\", :unless => Puppet::Util::Platform.windows? do\n        file = tmpfile(\"test_file\")\n        FileUtils.symlink(file, tmpfile(\"test_link\"))\n\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :present),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :absent))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file with ensure property change absent => present\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :absent),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :ensure => :present))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"new resource in catalog\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs([],\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff asdf\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"exec with idempotence issue\", :unless => Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n        report = run_catalogs(Puppet::Type.type(:exec).new(:title => \"exec1\",\n                                                           :command => \"/bin/echo foo\"),\n                              Puppet::Type.type(:exec).new(:title => \"exec1\",\n                                                           :command => \"/bin/echo foo\"))\n\n        expect(report.status).to eq(\"changed\")\n\n        # Of note here, is that the main idempotence issues lives in 'returns'\n        rs = report.resource_statuses[\"Exec[exec1]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"exec with no idempotence issue\", :unless => Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n        report = run_catalogs(Puppet::Type.type(:exec).new(:title => \"exec1\",\n                                                           :command => \"echo foo\",\n                                                           :path => \"/bin\",\n                                                           :unless => \"ls\"),\n                              Puppet::Type.type(:exec).new(:title => \"exec1\",\n                                                           :command => \"echo foo\",\n                                                           :path => \"/bin\",\n                                                           :unless => \"ls\"))\n\n        expect(report.status).to eq(\"unchanged\")\n\n        # Of note here, is that the main idempotence issues lives in 'returns'\n        rs = report.resource_statuses[\"Exec[exec1]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"noop on second run, file with no catalog change, but file changed between runs\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              false, true) do\n          File.open(file, 'w') do |f|\n            f.puts \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.events.size).to eq(1)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"noop on all subsequent runs, file with no catalog change, but file changed between run 1 and 2\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              false, true) do\n          File.open(file, 'w') do |f|\n            f.puts \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.events.size).to eq(1)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n\n        # Simply run the catalog twice again, but this time both runs are noop to\n        # test if the corrective field is still set.\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              true, true)\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.events.size).to eq(1)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"noop on first run, file with no catalog change, but file changed between runs\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              true, false) do\n          File.open(file, 'w') do |f|\n            f.puts \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"changed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.events.size).to eq(1)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"noop on both runs, file with no catalog change, but file changed between runs\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              true, true) do\n          File.open(file, 'w') do |f|\n            f.puts \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"noop on 4 runs, file with no catalog change, but file changed between runs 1 and 2\" do\n        file = tmpfile(\"test_file\")\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              true, true) do\n          File.open(file, 'w') do |f|\n            f.puts \"some content\"\n          end\n        end\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"mystuff\"),\n                              true, true)\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(true)\n        expect(rs.corrective_change).to eq(true)\n\n        expect(report.corrective_change).to eq(true)\n        expect(get_cc_count(report)).to eq(1)\n      end\n\n      it \"noop on both runs, file already exists but with catalog change each time\" do\n        file = tmpfile(\"test_file\")\n\n        File.open(file, 'w') do |f|\n          f.puts \"some content\"\n        end\n\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"a\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"b\"),\n                              true, true)\n\n        expect(report.status).to eq(\"unchanged\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file failure should not return corrective_change\" do\n        # Making the path a child path (with no parent) forces a failure\n        file = tmpfile(\"test_file\") + \"/foo\"\n        report = run_catalogs(Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"a\"),\n                              Puppet::Type.type(:file).new(:title => file,\n                                                           :content => \"b\"),\n                              false, false)\n\n        expect(report.status).to eq(\"failed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n\n      it \"file skipped with file change between runs will not show corrective_change\" do\n        # Making the path a child path (with no parent) forces a failure\n        file = tmpfile(\"test_file\") + \"/foo\"\n\n        resources1 = [\n          Puppet::Type.type(:file).new(:title => file,\n                                       :content => \"a\",\n                                       :notify => \"Notify['foo']\"),\n          Puppet::Type.type(:notify).new(:title => \"foo\")\n        ]\n        resources2 = [\n          Puppet::Type.type(:file).new(:title => file,\n                                       :content => \"a\",\n                                       :notify => \"Notify[foo]\"),\n          Puppet::Type.type(:notify).new(:title => \"foo\",\n                                         :message => \"foo\")\n        ]\n\n        report = run_catalogs(resources1, resources2, false, false)\n\n        expect(report.status).to eq(\"failed\")\n\n        rs = report.resource_statuses[\"File[#{file}]\"]\n        expect(rs.events.size).to eq(1)\n        expect(rs.events[0].corrective_change).to eq(false)\n        expect(rs.corrective_change).to eq(false)\n\n        rs = report.resource_statuses[\"Notify[foo]\"]\n        expect(rs.events.size).to eq(0)\n        expect(rs.corrective_change).to eq(false)\n\n        expect(report.corrective_change).to eq(false)\n        expect(get_cc_count(report)).to eq(0)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/transaction_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'puppet/transaction'\n\nPuppet::Type.newtype(:devicetype) do\n  apply_to_device\n  newparam(:name)\nend\n\ndescribe Puppet::Transaction do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  before do\n    allow(Puppet::Util::Storage).to receive(:store)\n  end\n\n  def mk_catalog(*resources)\n    catalog = Puppet::Resource::Catalog.new(Puppet::Node.new(\"mynode\"))\n    resources.each { |res| catalog.add_resource res }\n    catalog\n  end\n\n  def touch_path\n    Puppet.features.microsoft_windows? ? \"#{ENV['windir']}\\\\system32\" : \"/usr/bin:/bin\"\n  end\n\n  def usr_bin_touch(path)\n    Puppet.features.microsoft_windows? ? \"#{ENV['windir']}\\\\system32\\\\cmd.exe /c \\\"type NUL >> \\\"#{path}\\\"\\\"\" : \"/usr/bin/touch #{path}\"\n  end\n\n  def touch(path)\n    Puppet.features.microsoft_windows? ? \"cmd.exe /c \\\"type NUL >> \\\"#{path}\\\"\\\"\" : \"touch #{path}\"\n  end\n\n  it \"should not apply generated resources if the parent resource fails\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:file).new :path => make_absolute(\"/foo/bar\"), :backup => false\n    catalog.add_resource resource\n\n    child_resource = Puppet::Type.type(:file).new :path => make_absolute(\"/foo/bar/baz\"), :backup => false\n\n    expect(resource).to receive(:eval_generate).and_return([child_resource])\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n\n    expect(resource).to receive(:retrieve).and_raise(\"this is a failure\")\n    allow(resource).to receive(:err)\n\n    expect(child_resource).not_to receive(:retrieve)\n\n    transaction.evaluate\n  end\n\n  it \"should not apply virtual resources\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:file).new :path => make_absolute(\"/foo/bar\"), :backup => false\n    resource.virtual = true\n    catalog.add_resource resource\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n\n    expect(resource).not_to receive(:retrieve)\n\n    transaction.evaluate\n  end\n\n  it \"should apply exported resources\" do\n    catalog = Puppet::Resource::Catalog.new\n    path = tmpfile(\"exported_files\")\n    resource = Puppet::Type.type(:file).new :path => path, :backup => false, :ensure => :file\n    resource.exported = true\n    catalog.add_resource resource\n\n    catalog.apply\n    expect(Puppet::FileSystem.exist?(path)).to be_truthy\n  end\n\n  it \"should not apply virtual exported resources\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:file).new :path => make_absolute(\"/foo/bar\"), :backup => false\n    resource.exported = true\n    resource.virtual = true\n    catalog.add_resource resource\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n\n    expect(resource).not_to receive(:retrieve)\n\n    transaction.evaluate\n  end\n\n  it \"should not apply device resources on normal host\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:devicetype).new :name => \"FastEthernet 0/1\"\n    catalog.add_resource resource\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    transaction.for_network_device = false\n\n    expect(transaction).not_to receive(:apply).with(resource, nil)\n\n    transaction.evaluate\n    expect(transaction.resource_status(resource)).to be_skipped\n  end\n\n  it \"should not apply host resources on device\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:file).new :path => make_absolute(\"/foo/bar\"), :backup => false\n    catalog.add_resource resource\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    transaction.for_network_device = true\n\n    expect(transaction).not_to receive(:apply).with(resource, nil)\n\n    transaction.evaluate\n    expect(transaction.resource_status(resource)).to be_skipped\n  end\n\n  it \"should apply device resources on device\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:devicetype).new :name => \"FastEthernet 0/1\"\n    catalog.add_resource resource\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    transaction.for_network_device = true\n\n    expect(transaction).to receive(:apply).with(resource, nil)\n\n    transaction.evaluate\n    expect(transaction.resource_status(resource)).not_to be_skipped\n  end\n\n  it \"should apply resources appliable on host and device on a device\" do\n    catalog = Puppet::Resource::Catalog.new\n    resource = Puppet::Type.type(:schedule).new :name => \"test\"\n    catalog.add_resource resource\n\n    transaction = Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    transaction.for_network_device = true\n\n    expect(transaction).to receive(:apply).with(resource, nil)\n\n    transaction.evaluate\n    expect(transaction.resource_status(resource)).not_to be_skipped\n  end\n\n  # Verify that one component requiring another causes the contained\n  # resources in the requiring component to get refreshed.\n  it \"should propagate events from a contained resource through its container to its dependent container's contained resources\" do\n    file = Puppet::Type.type(:file).new :path => tmpfile(\"event_propagation\"), :ensure => :present\n    execfile = File.join(tmpdir(\"exec_event\"), \"exectestingness2\")\n    exec = Puppet::Type.type(:exec).new :command => touch(execfile), :path => ENV['PATH']\n    catalog = mk_catalog(file)\n\n    fcomp = Puppet::Type.type(:component).new(:name => \"Foo[file]\")\n    catalog.add_resource fcomp\n    catalog.add_edge(fcomp, file)\n\n    ecomp = Puppet::Type.type(:component).new(:name => \"Foo[exec]\")\n    catalog.add_resource ecomp\n    catalog.add_resource exec\n    catalog.add_edge(ecomp, exec)\n\n    ecomp[:subscribe] = Puppet::Resource.new(:foo, \"file\")\n    exec[:refreshonly] = true\n\n    expect(exec).to receive(:refresh)\n    catalog.apply\n  end\n\n  # Make sure that multiple subscriptions get triggered.\n  it \"should propagate events to all dependent resources\", :unless => RUBY_PLATFORM == 'java' do\n    path = tmpfile(\"path\")\n    file1 = tmpfile(\"file1\")\n    file2 = tmpfile(\"file2\")\n\n    file = Puppet::Type.type(:file).new(\n      :path   => path,\n      :ensure => \"file\"\n    )\n\n    exec1 = Puppet::Type.type(:exec).new(\n      :path    => ENV[\"PATH\"],\n      :command => touch(file1),\n      :refreshonly => true,\n      :subscribe   => Puppet::Resource.new(:file, path)\n    )\n\n    exec2 = Puppet::Type.type(:exec).new(\n      :path        => ENV[\"PATH\"],\n      :command     => touch(file2),\n      :refreshonly => true,\n      :subscribe   => Puppet::Resource.new(:file, path)\n    )\n\n    catalog = mk_catalog(file, exec1, exec2)\n    catalog.apply\n    expect(Puppet::FileSystem.exist?(file1)).to be_truthy\n    expect(Puppet::FileSystem.exist?(file2)).to be_truthy\n  end\n\n  it \"does not refresh resources that have 'noop => true'\" do\n    path = tmpfile(\"path\")\n\n    notify = Puppet::Type.type(:notify).new(\n      :name    => \"trigger\",\n      :notify  => Puppet::Resource.new(:exec, \"noop exec\")\n    )\n\n    noop_exec = Puppet::Type.type(:exec).new(\n      :name    => \"noop exec\",\n      :path    => ENV[\"PATH\"],\n      :command => touch(path),\n      :noop    => true\n    )\n\n    catalog = mk_catalog(notify, noop_exec)\n    catalog.apply\n    expect(Puppet::FileSystem.exist?(path)).to be_falsey\n  end\n\n  it \"should apply no resources whatsoever if a pre_run_check fails\" do\n    path = tmpfile(\"path\")\n    file = Puppet::Type.type(:file).new(\n      :path => path,\n      :ensure => \"file\"\n    )\n    notify = Puppet::Type.type(:notify).new(\n      :title => \"foo\"\n    )\n    expect(notify).to receive(:pre_run_check).and_raise(Puppet::Error, \"fail for testing\")\n\n    catalog = mk_catalog(file, notify)\n    expect { catalog.apply }.to raise_error(Puppet::Error, /Some pre-run checks failed/)\n    expect(Puppet::FileSystem.exist?(path)).not_to be_truthy\n  end\n\n  it \"one failed refresh should propagate its failure to dependent refreshes\", :unless => RUBY_PLATFORM == 'java' do\n    path = tmpfile(\"path\")\n    newfile = tmpfile(\"file\")\n      file = Puppet::Type.type(:file).new(\n      :path => path,\n      :ensure => \"file\"\n    )\n\n    exec1 = Puppet::Type.type(:exec).new(\n      :path => ENV[\"PATH\"],\n      :command => touch(File.expand_path(\"/this/cannot/possibly/exist\")),\n      :logoutput => true,\n      :refreshonly => true,\n      :subscribe => file,\n      :title => \"one\"\n    )\n\n    exec2 = Puppet::Type.type(:exec).new(\n      :path => ENV[\"PATH\"],\n      :command => touch(newfile),\n      :logoutput => true,\n      :refreshonly => true,\n      :subscribe => [file, exec1],\n      :title => \"two\"\n    )\n\n    allow(exec1).to receive(:err)\n\n    catalog = mk_catalog(file, exec1, exec2)\n    catalog.apply\n    expect(Puppet::FileSystem.exist?(newfile)).to be_falsey\n  end\n\n  # Ensure when resources have been generated with eval_generate that event\n  # propagation still works when filtering with tags\n  context \"when filtering with tags\", :unless => RUBY_PLATFORM == 'java' do\n    context \"when resources are dependent on dynamically generated resources\" do\n      it \"should trigger (only) appropriately tagged dependent resources\" do\n        source = dir_containing('sourcedir', {'foo' => 'bar'})\n        target = tmpdir('targetdir')\n        file1 = tmpfile(\"file1\")\n        file2 = tmpfile(\"file2\")\n\n        file = Puppet::Type.type(:file).new(\n          :path    => target,\n          :source  => source,\n          :ensure  => :present,\n          :recurse => true,\n          :tag     => \"foo_tag\",\n        )\n\n        exec1 = Puppet::Type.type(:exec).new(\n          :path        => ENV[\"PATH\"],\n          :command     => touch(file1),\n          :refreshonly => true,\n          :subscribe   => file,\n          :tag         => \"foo_tag\",\n        )\n\n        exec2 = Puppet::Type.type(:exec).new(\n          :path        => ENV[\"PATH\"],\n          :command     => touch(file2),\n          :refreshonly => true,\n          :subscribe   => file,\n        )\n\n        Puppet[:tags] = \"foo_tag\"\n        catalog = mk_catalog(file, exec1, exec2)\n        catalog.apply\n        expect(Puppet::FileSystem.exist?(file1)).to be_truthy\n        expect(Puppet::FileSystem.exist?(file2)).to be_falsey\n      end\n\n      it \"should trigger implicitly tagged dependent resources, ie via type name\" do\n        file1 = tmpfile(\"file1\")\n        file2 = tmpfile(\"file2\")\n\n        expect(Puppet::FileSystem).to_not be_exist(file2)\n\n        exec1 = Puppet::Type.type(:exec).new(\n          :name        => \"exec1\",\n          :path        => ENV[\"PATH\"],\n          :command     => touch(file1),\n        )\n\n        exec2 = Puppet::Type.type(:exec).new(\n          :name        => \"exec2\",\n          :path        => ENV[\"PATH\"],\n          :command     => touch(file2),\n          :refreshonly => true,\n          :subscribe   => exec1,\n        )\n\n        Puppet[:tags] = \"exec\"\n        catalog = mk_catalog(exec1, exec2)\n        catalog.apply\n        expect(Puppet::FileSystem.exist?(file1)).to be_truthy\n        expect(Puppet::FileSystem.exist?(file2)).to be_truthy\n      end\n    end\n\n    it \"should propagate events correctly from a tagged container when running with tags\" do\n      file1 = tmpfile(\"original_tag\")\n      file2 = tmpfile(\"tag_propagation\")\n      command1 = usr_bin_touch(file1)\n      command2 = usr_bin_touch(file2)\n      manifest = <<-\"MANIFEST\"\n        class foo {\n          exec { 'notify test':\n            command     => '#{command1}',\n            refreshonly => true,\n          }\n        }\n\n        class test {\n          include foo\n\n          exec { 'test':\n            command => '#{command2}',\n            notify  => Class['foo'],\n          }\n        }\n\n        include test\n      MANIFEST\n\n      Puppet[:tags] = 'test'\n      apply_compiled_manifest(manifest)\n      expect(Puppet::FileSystem.exist?(file1)).to be_truthy\n      expect(Puppet::FileSystem.exist?(file2)).to be_truthy\n    end\n  end\n\n  describe \"skipping resources\" do\n    let(:fname) { tmpfile(\"exec\") }\n\n    let(:file) do\n      Puppet::Type.type(:file).new(\n        :name => tmpfile(\"file\"),\n        :ensure => \"file\",\n        :backup => false\n      )\n    end\n\n    let(:exec) do\n      Puppet::Type.type(:exec).new(\n        :name => touch(fname),\n        :path => touch_path,\n        :subscribe => Puppet::Resource.new(\"file\", file.name)\n      )\n    end\n\n    it \"does not trigger unscheduled resources\", :unless => RUBY_PLATFORM == 'java' do\n      catalog = mk_catalog\n      catalog.add_resource(*Puppet::Type.type(:schedule).mkdefaultschedules)\n\n      Puppet[:ignoreschedules] = false\n\n      exec[:schedule] = \"monthly\"\n\n      catalog.add_resource(file, exec)\n\n      # Run it once so further runs don't schedule the resource\n      catalog.apply\n      expect(Puppet::FileSystem.exist?(fname)).to be_truthy\n\n      # Now remove it, so it can get created again\n      Puppet::FileSystem.unlink(fname)\n\n      file[:content] = \"some content\"\n\n      catalog.apply\n      expect(Puppet::FileSystem.exist?(fname)).to be_falsey\n    end\n\n    it \"does not trigger untagged resources\" do\n      catalog = mk_catalog\n\n      Puppet[:tags] = \"runonly\"\n      file.tag(\"runonly\")\n\n      catalog.add_resource(file, exec)\n      catalog.apply\n      expect(Puppet::FileSystem.exist?(fname)).to be_falsey\n    end\n\n    it \"does not trigger skip-tagged resources\" do\n      catalog = mk_catalog\n\n      Puppet[:skip_tags] = \"skipme\"\n      exec.tag(\"skipme\")\n\n      catalog.add_resource(file, exec)\n      catalog.apply\n      expect(Puppet::FileSystem.exist?(fname)).to be_falsey\n    end\n\n    it \"does not trigger resources with failed dependencies\" do\n      catalog = mk_catalog\n      file[:path] = make_absolute(\"/foo/bar/baz\")\n\n      catalog.add_resource(file, exec)\n      catalog.apply\n\n      expect(Puppet::FileSystem.exist?(fname)).to be_falsey\n    end\n  end\n\n  it \"should not attempt to evaluate resources with failed dependencies\", :unless => RUBY_PLATFORM == 'java' do\n\n    exec = Puppet::Type.type(:exec).new(\n      :command => \"#{File.expand_path('/bin/mkdir')} /this/path/cannot/possibly/exist\",\n      :title => \"mkdir\"\n    )\n\n    file1 = Puppet::Type.type(:file).new(\n      :title => \"file1\",\n      :path => tmpfile(\"file1\"),\n      :require => exec,\n      :ensure => :file\n    )\n\n    file2 = Puppet::Type.type(:file).new(\n      :title => \"file2\",\n      :path => tmpfile(\"file2\"),\n      :require => file1,\n      :ensure => :file\n    )\n\n    catalog = mk_catalog(exec, file1, file2)\n    transaction = catalog.apply\n\n    expect(Puppet::FileSystem.exist?(file1[:path])).to be_falsey\n    expect(Puppet::FileSystem.exist?(file2[:path])).to be_falsey\n\n    expect(transaction.resource_status(file1).skipped).to be_truthy\n    expect(transaction.resource_status(file2).skipped).to be_truthy\n\n    expect(transaction.resource_status(file1).failed_dependencies).to eq([exec])\n    expect(transaction.resource_status(file2).failed_dependencies).to eq([exec])\n  end\n\n  it \"on failure, skips dynamically-generated dependents\", :unless => RUBY_PLATFORM == 'java' do\n    exec = Puppet::Type.type(:exec).new(\n      :command => \"#{File.expand_path('/bin/mkdir')} /this/path/cannot/possibly/exist\",\n      :title => \"mkdir\"\n    )\n\n    tmp = tmpfile(\"dir1\")\n    FileUtils.mkdir_p(tmp)\n    FileUtils.mkdir_p(File.join(tmp, \"foo\"))\n\n    purge_dir = Puppet::Type.type(:file).new(\n      :title => \"dir1\",\n      :path => tmp,\n      :require => exec,\n      :ensure => :directory,\n      :recurse => true,\n      :purge => true\n    )\n\n    catalog = mk_catalog(exec, purge_dir)\n    txn = catalog.apply\n\n    expect(txn.resource_status(purge_dir).skipped).to be_truthy\n\n    children = catalog.relationship_graph.direct_dependents_of(purge_dir)\n\n    children.each do |child|\n      expect(txn.resource_status(child).skipped).to be_truthy\n    end\n\n    expect(Puppet::FileSystem.exist?(File.join(tmp, \"foo\"))).to be_truthy\n  end\n\n  it \"should not trigger subscribing resources on failure\", :unless => RUBY_PLATFORM == 'java' do\n    file1 = tmpfile(\"file1\")\n    file2 = tmpfile(\"file2\")\n\n    create_file1 = Puppet::Type.type(:exec).new(\n      :command => usr_bin_touch(file1)\n    )\n\n    exec = Puppet::Type.type(:exec).new(\n      :command => \"#{File.expand_path('/bin/mkdir')} /this/path/cannot/possibly/exist\",\n      :title => \"mkdir\",\n      :notify => create_file1\n    )\n\n    create_file2 = Puppet::Type.type(:exec).new(\n      :command => usr_bin_touch(file2),\n      :subscribe => exec\n    )\n\n    catalog = mk_catalog(exec, create_file1, create_file2)\n    catalog.apply\n\n    expect(Puppet::FileSystem.exist?(file1)).to be_falsey\n    expect(Puppet::FileSystem.exist?(file2)).to be_falsey\n  end\n\n  # #801 -- resources only checked in noop should be rescheduled immediately.\n  it \"should immediately reschedule noop resources\" do\n    Puppet::Type.type(:schedule).mkdefaultschedules\n    resource = Puppet::Type.type(:notify).new(:name => \"mymessage\", :noop => true)\n    catalog = Puppet::Resource::Catalog.new\n    catalog.add_resource resource\n\n    trans = catalog.apply\n\n    expect(trans.resource_harness).to be_scheduled(resource)\n  end\nend\n"
  },
  {
    "path": "spec/integration/type/exec_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Type.type(:exec), unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  let(:catalog) { Puppet::Resource::Catalog.new }\n  let(:path) { tmpfile('exec_provider') }\n\n  before :each do\n    catalog.host_config = false\n  end\n\n  shared_examples_for 'a valid exec resource' do\n    it \"should execute the command\" do\n      exec = described_class.new :command => command, :path => ENV['PATH']\n\n      catalog.add_resource exec\n      catalog.apply\n\n      expect(File.read(path)).to eq('foo')\n    end\n\n    it \"should not execute the command if onlyif returns non-zero\" do\n      exec = described_class.new(\n        :command => command,\n        :onlyif => \"ruby -e 'exit 44'\",\n        :path => ENV['PATH']\n      )\n\n      catalog.add_resource exec\n      catalog.apply\n\n      expect(Puppet::FileSystem.exist?(path)).to be_falsey\n    end\n\n    it \"should execute the command if onlyif returns zero\" do\n      exec = described_class.new(\n        :command => command,\n        :onlyif => \"ruby -e 'exit 0'\",\n        :path => ENV['PATH']\n      )\n\n      catalog.add_resource exec\n      catalog.apply\n\n      expect(File.read(path)).to eq('foo')\n    end\n\n    it \"should execute the command if unless returns non-zero\" do\n      exec = described_class.new(\n        :command => command,\n        :unless => \"ruby -e 'exit 45'\",\n        :path => ENV['PATH']\n      )\n\n      catalog.add_resource exec\n      catalog.apply\n\n      expect(File.read(path)).to eq('foo')\n    end\n\n    it \"should not execute the command if unless returns zero\" do\n      exec = described_class.new(\n        :command => command,\n        :unless => \"ruby -e 'exit 0'\",\n        :path => ENV['PATH']\n      )\n\n      catalog.add_resource exec\n      catalog.apply\n\n      expect(Puppet::FileSystem.exist?(path)).to be_falsey\n    end\n  end\n\n  context 'when an exec sends an EOF' do\n    let(:command) { [\"/bin/bash\", \"-c\", \"exec /bin/sleep 1 >/dev/null 2>&1\"] }\n\n    it 'should not take significant user time' do\n      exec = described_class.new :command => command, :path => ENV['PATH']\n      catalog.add_resource exec\n      timed_apply = Benchmark.measure { catalog.apply }\n      # In testing I found the user time before the patch in 4f35fd262e to be above\n      # 0.3, after the patch it was consistently below 0.1 seconds.\n      expect(timed_apply.utime).to be < 0.3\n    end\n  end\n\n  context 'when command is a string' do\n    let(:command) { \"ruby -e 'File.open(\\\"#{path}\\\", \\\"w\\\") { |f| f.print \\\"foo\\\" }'\" }\n\n    it_behaves_like 'a valid exec resource'\n  end\n\n  context 'when command is an array' do\n    let(:command) { ['ruby', '-e', \"File.open(\\\"#{path}\\\", \\\"w\\\") { |f| f.print \\\"foo\\\" }\"] }\n\n    it_behaves_like 'a valid exec resource'\n\n    context 'when is invalid' do\n      let(:command) { [ \"ruby -e 'puts 1'\" ] }\n\n      it 'logs error' do\n        exec = described_class.new :command => command, :path => ENV['PATH']\n        catalog.add_resource exec\n        logs = catalog.apply.report.logs\n\n        expect(logs[0].message).to eql(\"Could not find command 'ruby -e 'puts 1''\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/type/file_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\n\nrequire 'puppet_spec/files'\n\nif Puppet::Util::Platform.windows?\n  require 'puppet/util/windows'\n  class WindowsSecurity\n    extend Puppet::Util::Windows::Security\n  end\nend\n\ndescribe Puppet::Type.type(:file), :uses_checksums => true do\n  include PuppetSpec::Files\n  include_context 'with supported checksum types'\n\n  let(:catalog) { Puppet::Resource::Catalog.new }\n  let(:path) do\n    # we create a directory first so backups of :path that are stored in\n    # the same directory will also be removed after the tests\n    parent = tmpdir('file_spec')\n    File.join(parent, 'file_testing')\n  end\n\n  let(:path_protected) do\n    # we create a file inside windows protected folders (C:\\Windows, C:\\Windows\\system32, etc)\n    # the file will also be removed after the tests\n    parent = 'C:\\Windows'\n    File.join(parent, 'file_testing')\n  end\n\n  let(:dir) do\n    # we create a directory first so backups of :path that are stored in\n    # the same directory will also be removed after the tests\n    parent = tmpdir('file_spec')\n    File.join(parent, 'dir_testing')\n  end\n\n  if Puppet.features.posix?\n    def set_mode(mode, file)\n      File.chmod(mode, file)\n    end\n\n    def get_mode(file)\n      Puppet::FileSystem.lstat(file).mode\n    end\n\n    def get_owner(file)\n      Puppet::FileSystem.lstat(file).uid\n    end\n\n    def get_group(file)\n      Puppet::FileSystem.lstat(file).gid\n    end\n  else\n    class SecurityHelper\n      extend Puppet::Util::Windows::Security\n    end\n\n    def set_mode(mode, file)\n      SecurityHelper.set_mode(mode, file)\n    end\n\n    def get_mode(file)\n      SecurityHelper.get_mode(file)\n    end\n\n    def get_owner(file)\n      SecurityHelper.get_owner(file)\n    end\n\n    def get_group(file)\n      SecurityHelper.get_group(file)\n    end\n\n    def get_aces_for_path_by_sid(path, sid)\n      SecurityHelper.get_aces_for_path_by_sid(path, sid)\n    end\n  end\n\n  around :each do |example|\n    Puppet.override(:environments => Puppet::Environments::Static.new) do\n      example.run\n    end\n  end\n\n  before do\n    # stub this to not try to create state.yaml\n    allow(Puppet::Util::Storage).to receive(:store)\n\n    allow_any_instance_of(Puppet::Type.type(:file)).to receive(:file).and_return('my/file.pp')\n    allow_any_instance_of(Puppet::Type.type(:file)).to receive(:line).and_return(5)\n  end\n\n  it \"should not attempt to manage files that do not exist if no means of creating the file is specified\" do\n    source = tmpfile('source')\n\n    catalog.add_resource described_class.new :path => source, :mode => '0755'\n\n    status = catalog.apply.report.resource_statuses[\"File[#{source}]\"]\n    expect(status).not_to be_failed\n    expect(status).not_to be_changed\n    expect(Puppet::FileSystem.exist?(source)).to be_falsey\n  end\n\n  describe \"when ensure is present using an empty file\" do\n    before(:each) do\n      catalog.add_resource(described_class.new(:path => path, :ensure => :present, :backup => :false))\n    end\n\n    context \"file is present\" do\n      before(:each) do\n        FileUtils.touch(path)\n      end\n\n      it \"should do nothing\" do\n        report = catalog.apply.report\n        expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n        expect(Puppet::FileSystem.exist?(path)).to be_truthy\n      end\n\n      it \"should log nothing\" do\n        logs = catalog.apply.report.logs\n        expect(logs).to be_empty\n      end\n    end\n\n    context \"file is not present\" do\n      it \"should create the file\" do\n        report = catalog.apply.report\n        expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n        expect(Puppet::FileSystem.exist?(path)).to be_truthy\n      end\n\n      it \"should log that the file was created\" do\n        logs = catalog.apply.report.logs\n        expect(logs.first.source).to eq(\"/File[#{path}]/ensure\")\n        expect(logs.first.message).to eq(\"created\")\n      end\n    end\n  end\n\n  describe \"when ensure is absent\" do\n    before(:each) do\n      catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false))\n    end\n\n    context \"file is present\" do\n      before(:each) do\n        FileUtils.touch(path)\n      end\n\n      it \"should remove the file\" do\n        report = catalog.apply.report\n        expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n        expect(Puppet::FileSystem.exist?(path)).to be_falsey\n      end\n\n      it \"should log that the file was removed\" do\n        logs = catalog.apply.report.logs\n        expect(logs.first.source).to eq(\"/File[#{path}]/ensure\")\n        expect(logs.first.message).to eq(\"removed\")\n      end\n    end\n\n    context \"file is not present\" do\n      it \"should do nothing\" do\n        report = catalog.apply.report\n        expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n        expect(Puppet::FileSystem.exist?(path)).to be_falsey\n      end\n\n      it \"should log nothing\" do\n        logs = catalog.apply.report.logs\n        expect(logs).to be_empty\n      end\n    end\n\n    # issue #14599\n    it \"should not fail if parts of path aren't directories\" do\n      FileUtils.touch(path)\n      catalog.add_resource(described_class.new(:path => File.join(path,'no_such_file'), :ensure => :absent, :backup => :false))\n      report = catalog.apply.report\n      expect(report.resource_statuses[\"File[#{File.join(path,'no_such_file')}]\"]).not_to be_failed\n    end\n  end\n\n  describe \"when setting permissions\" do\n    it \"should set the owner\" do\n      target = tmpfile_with_contents('target', '')\n      owner = get_owner(target)\n\n      catalog.add_resource described_class.new(\n        :name    => target,\n        :owner   => owner\n      )\n\n      catalog.apply\n\n      expect(get_owner(target)).to eq(owner)\n    end\n\n    it \"should set the group\" do\n      target = tmpfile_with_contents('target', '')\n      group = get_group(target)\n\n      catalog.add_resource described_class.new(\n        :name    => target,\n        :group   => group\n      )\n\n      catalog.apply\n\n      expect(get_group(target)).to eq(group)\n    end\n\n    describe \"when setting mode\" do\n      describe \"for directories\" do\n        let(:target) { tmpdir('dir_mode') }\n\n        it \"should set executable bits for newly created directories\" do\n          catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => '0600')\n\n          catalog.apply\n\n          expect(get_mode(target) & 07777).to eq(0700)\n        end\n\n        it \"should set executable bits for existing readable directories\" do\n          set_mode(0600, target)\n\n          catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => '0644')\n          catalog.apply\n\n          expect(get_mode(target) & 07777).to eq(0755)\n        end\n\n        it \"should not set executable bits for unreadable directories\" do\n          begin\n            catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => '0300')\n\n            catalog.apply\n\n            expect(get_mode(target) & 07777).to eq(0300)\n          ensure\n            # so we can cleanup\n            set_mode(0700, target)\n          end\n        end\n\n        it \"should set user, group, and other executable bits\" do\n          catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => '0664')\n\n          catalog.apply\n\n          expect(get_mode(target) & 07777).to eq(0775)\n        end\n\n        it \"should set executable bits when overwriting a non-executable file\" do\n          target_path = tmpfile_with_contents('executable', '')\n          set_mode(0444, target_path)\n\n          catalog.add_resource described_class.new(:path => target_path, :ensure => :directory, :mode => '0666', :backup => false)\n          catalog.apply\n\n          expect(get_mode(target_path) & 07777).to eq(0777)\n          expect(File).to be_directory(target_path)\n        end\n      end\n\n      describe \"for files\" do\n        it \"should not set executable bits\" do\n          catalog.add_resource described_class.new(:path => path, :ensure => :file, :mode => '0666')\n          catalog.apply\n\n          expect(get_mode(path) & 07777).to eq(0666)\n        end\n\n        context \"file is in protected windows directory\", :if => Puppet.features.microsoft_windows? do\n          after { FileUtils.rm(path_protected) }\n\n          it \"should set and get the correct mode for files inside protected windows folders\" do\n            catalog.add_resource described_class.new(:path => path_protected, :ensure => :file, :mode => '0640')\n            catalog.apply\n  \n            expect(get_mode(path_protected) & 07777).to eq(0640)\n          end\n\n          it \"should not change resource's status inside protected windows folders if mode is the same\" do\n            FileUtils.touch(path_protected)\n            set_mode(0644, path_protected)\n            catalog.add_resource described_class.new(:path => path_protected, :ensure => :file, :mode => '0644')\n            result = catalog.apply\n            status = result.report.resource_statuses[\"File[#{path_protected}]\"]\n            expect(status).not_to be_failed\n            expect(status).not_to be_changed\n          end\n        end\n        \n        it \"should not set executable bits when replacing an executable directory (#10365)\" do\n          pending(\"bug #10365\")\n\n          FileUtils.mkdir(path)\n          set_mode(0777, path)\n\n          catalog.add_resource described_class.new(:path => path, :ensure => :file, :mode => '0666', :backup => false, :force => true)\n          catalog.apply\n\n          expect(get_mode(path) & 07777).to eq(0666)\n        end\n      end\n\n      describe \"for links\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n        let(:link) { tmpfile('link_mode') }\n\n        describe \"when managing links\" do\n          let(:link_target) { tmpfile('target') }\n\n          before :each do\n            FileUtils.touch(link_target)\n            File.chmod(0444, link_target)\n\n            Puppet::FileSystem.symlink(link_target, link)\n          end\n\n          it \"should not set the executable bit on the link target\" do\n            catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => '0666', :target => link_target, :links => :manage)\n\n            catalog.apply\n\n            expected_target_permissions = Puppet::Util::Platform.windows? ? 0700 : 0444\n\n            expect(Puppet::FileSystem.stat(link_target).mode & 07777).to eq(expected_target_permissions)\n          end\n\n          it \"should ignore dangling symlinks (#6856)\" do\n            File.delete(link_target)\n\n            catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => '0666', :target => link_target, :links => :manage)\n            catalog.apply\n\n            expect(Puppet::FileSystem.exist?(link)).to be_falsey\n          end\n\n          it \"should create a link to the target if ensure is omitted\" do\n            FileUtils.touch(link_target)\n            catalog.add_resource described_class.new(:path => link, :target => link_target)\n            catalog.apply\n\n            expect(Puppet::FileSystem.exist?(link)).to be_truthy\n            expect(Puppet::FileSystem.lstat(link).ftype).to eq('link')\n            expect(Puppet::FileSystem.readlink(link)).to eq(link_target)\n          end\n        end\n\n        describe \"when following links\" do\n          it \"should ignore dangling symlinks (#6856)\" do\n            target = tmpfile('dangling')\n\n            FileUtils.touch(target)\n            Puppet::FileSystem.symlink(target, link)\n            File.delete(target)\n\n            catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0600', :links => :follow)\n            catalog.apply\n          end\n\n          describe \"to a directory\" do\n            let(:link_target) { tmpdir('dir_target') }\n\n            before :each do\n              File.chmod(0600, link_target)\n\n              Puppet::FileSystem.symlink(link_target, link)\n            end\n\n            after :each do\n              File.chmod(0750, link_target)\n            end\n\n            describe \"that is readable\" do\n              it \"should set the executable bits when creating the destination (#10315)\" do\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0666', :links => :follow)\n                catalog.apply\n\n                expect(File).to be_directory(path)\n                expect(get_mode(path) & 07777).to eq(0777)\n              end\n\n              it \"should set the executable bits when overwriting the destination (#10315)\" do\n                FileUtils.touch(path)\n\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0666', :links => :follow, :backup => false)\n                catalog.apply\n\n                expect(File).to be_directory(path)\n                expect(get_mode(path) & 07777).to eq(0777)\n              end\n            end\n\n            describe \"that is not readable\" do\n              before :each do\n                set_mode(0300, link_target)\n              end\n\n              # so we can cleanup\n              after :each do\n                set_mode(0700, link_target)\n              end\n\n              it \"should set executable bits when creating the destination (#10315)\" do\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0666', :links => :follow)\n                catalog.apply\n\n                expect(File).to be_directory(path)\n                expect(get_mode(path) & 07777).to eq(0777)\n              end\n\n              it \"should set executable bits when overwriting the destination\" do\n                FileUtils.touch(path)\n\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0666', :links => :follow, :backup => false)\n                catalog.apply\n\n                expect(File).to be_directory(path)\n                expect(get_mode(path) & 07777).to eq(0777)\n              end\n            end\n          end\n\n          describe \"to a file\" do\n            let(:link_target) { tmpfile('file_target') }\n\n            before :each do\n              FileUtils.touch(link_target)\n\n              Puppet::FileSystem.symlink(link_target, link)\n            end\n\n            it \"should create the file, not a symlink (#2817, #10315)\" do\n              catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0600', :links => :follow)\n              catalog.apply\n\n              expect(File).to be_file(path)\n              expect(get_mode(path) & 07777).to eq(0600)\n            end\n\n            it \"should not give a deprecation warning about using a checksum in content when using source to define content\" do\n              FileUtils.touch(path)\n              expect(Puppet).not_to receive(:puppet_deprecation_warning)\n              catalog.add_resource described_class.new(:path => path, :source => link, :links => :follow)\n              catalog.apply\n            end\n\n            context \"overwriting a file\" do\n              before :each do\n                FileUtils.touch(path)\n                set_mode(0644, path)\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0600', :links => :follow)\n              end\n\n              it \"should overwrite the file\" do\n                catalog.apply\n\n                expect(File).to be_file(path)\n                expect(get_mode(path) & 07777).to eq(0600)\n              end\n\n              it \"should log that the mode changed\" do\n                report = catalog.apply.report\n\n                expect(report.logs.first.message).to eq(\"mode changed '0644' to '0600'\")\n                expect(report.logs.first.source).to eq(\"/File[#{path}]/mode\")\n              end\n            end\n          end\n\n          describe \"to a link to a directory\" do\n            let(:real_target) { tmpdir('real_target') }\n            let(:target) { tmpfile('target') }\n\n            before :each do\n              File.chmod(0666, real_target)\n\n              # link -> target -> real_target\n              Puppet::FileSystem.symlink(real_target, target)\n              Puppet::FileSystem.symlink(target, link)\n            end\n\n            after :each do\n              File.chmod(0750, real_target)\n            end\n\n            describe \"when following all links\" do\n              it \"should create the destination and apply executable bits (#10315)\" do\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0600', :links => :follow)\n                catalog.apply\n\n                expect(File).to be_directory(path)\n                expect(get_mode(path) & 07777).to eq(0700)\n              end\n\n              it \"should overwrite the destination and apply executable bits\" do\n                FileUtils.mkdir(path)\n\n                catalog.add_resource described_class.new(:path => path, :source => link, :mode => '0600', :links => :follow)\n                catalog.apply\n\n                expect(File).to be_directory(path)\n                expect(get_mode(path) & 0111).to eq(0100)\n              end\n            end\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when writing files\" do\n    shared_examples \"files are backed up\" do |resource_options|\n      it \"should backup files to a filebucket when one is configured\" do |example|\n        if Puppet::Util::Platform.windows? && ['sha512', 'sha384'].include?(example.metadata[:digest_algorithm])\n          skip \"PUP-8257: Skip file bucket test on windows for #{example.metadata[:digest_algorithm]} due to long path names\"\n        end\n\n        filebucket = Puppet::Type.type(:filebucket).new :path => tmpfile(\"filebucket\"), :name => \"mybucket\"\n        file = described_class.new({:path => path, :backup => \"mybucket\", :content => \"foo\"}.merge(resource_options))\n        catalog.add_resource file\n        catalog.add_resource filebucket\n\n        File.open(file[:path], \"w\") { |f| f.write(\"bar\") }\n\n        d = filebucket_digest.call(IO.binread(file[:path]))\n\n        catalog.apply\n\n        expect(filebucket.bucket.getfile(d)).to eq(\"bar\")\n      end\n\n      it \"should backup files in the local directory when a backup string is provided\" do\n        file = described_class.new({:path => path, :backup => \".bak\", :content => \"foo\"}.merge(resource_options))\n        catalog.add_resource file\n\n        File.open(file[:path], \"w\") { |f| f.puts \"bar\" }\n\n        catalog.apply\n\n        backup = file[:path] + \".bak\"\n        expect(Puppet::FileSystem.exist?(backup)).to be_truthy\n        expect(File.read(backup)).to eq(\"bar\\n\")\n      end\n\n      it \"should fail if no backup can be performed\" do\n        dir = tmpdir(\"backups\")\n\n        file = described_class.new({:path => File.join(dir, \"testfile\"), :backup => \".bak\", :content => \"foo\"}.merge(resource_options))\n        catalog.add_resource file\n\n        File.open(file[:path], 'w') { |f| f.puts \"bar\" }\n\n        # Create a directory where the backup should be so that writing to it fails\n        Dir.mkdir(File.join(dir, \"testfile.bak\"))\n\n        allow(Puppet::Util::Log).to receive(:newmessage)\n\n        catalog.apply\n\n        expect(File.read(file[:path])).to eq(\"bar\\n\")\n      end\n\n      it \"should not backup symlinks\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n        link = tmpfile(\"link\")\n        dest1 = tmpfile(\"dest1\")\n        dest2 = tmpfile(\"dest2\")\n        bucket = Puppet::Type.type(:filebucket).new :path => tmpfile(\"filebucket\"), :name => \"mybucket\"\n        file = described_class.new({:path => link, :target => dest2, :ensure => :link, :backup => \"mybucket\"}.merge(resource_options))\n        catalog.add_resource file\n        catalog.add_resource bucket\n\n        File.open(dest1, \"w\") { |f| f.puts \"whatever\" }\n        Puppet::FileSystem.symlink(dest1, link)\n\n        catalog.apply\n\n        expect(Puppet::FileSystem.readlink(link)).to eq(dest2)\n        expect(Puppet::FileSystem.exist?(bucket[:path])).to be_falsey\n      end\n\n      it \"should backup directories to the local filesystem by copying the whole directory\" do\n        file = described_class.new({:path => path, :backup => \".bak\", :content => \"foo\", :force => true}.merge(resource_options))\n        catalog.add_resource file\n\n        Dir.mkdir(path)\n\n        otherfile = File.join(path, \"foo\")\n        File.open(otherfile, \"w\") { |f| f.print \"yay\" }\n\n        catalog.apply\n\n        backup = \"#{path}.bak\"\n        expect(FileTest).to be_directory(backup)\n\n        expect(File.read(File.join(backup, \"foo\"))).to eq(\"yay\")\n      end\n\n      it \"should backup directories to filebuckets by backing up each file separately\" do |example|\n        if Puppet::Util::Platform.windows? && ['sha512', 'sha384'].include?(example.metadata[:digest_algorithm])\n          skip \"PUP-8257: Skip file bucket test on windows for #{example.metadata[:digest_algorithm]} due to long path names\"\n        end\n\n        bucket = Puppet::Type.type(:filebucket).new :path => tmpfile(\"filebucket\"), :name => \"mybucket\"\n        file = described_class.new({:path => tmpfile(\"bucket_backs\"), :backup => \"mybucket\", :content => \"foo\", :force => true}.merge(resource_options))\n        catalog.add_resource file\n        catalog.add_resource bucket\n\n        Dir.mkdir(file[:path])\n        foofile = File.join(file[:path], \"foo\")\n        barfile = File.join(file[:path], \"bar\")\n        File.open(foofile, \"w\") { |f| f.print \"fooyay\" }\n        File.open(barfile, \"w\") { |f| f.print \"baryay\" }\n\n\n        food = filebucket_digest.call(File.read(foofile))\n        bard = filebucket_digest.call(File.read(barfile))\n\n        catalog.apply\n\n        expect(bucket.bucket.getfile(food)).to eq(\"fooyay\")\n        expect(bucket.bucket.getfile(bard)).to eq(\"baryay\")\n      end\n    end\n\n    it \"should not give a checksum deprecation warning when given actual content\" do\n      expect(Puppet).not_to receive(:puppet_deprecation_warning)\n      catalog.add_resource described_class.new(:path => path, :content => 'this is content')\n      catalog.apply\n    end\n\n    with_digest_algorithms do\n      it_should_behave_like \"files are backed up\", {} do\n        let(:filebucket_digest) { method(:digest) }\n      end\n\n      it \"should give a checksum deprecation warning\" do\n        expect(Puppet).to receive(:puppet_deprecation_warning).with('Using a checksum in a file\\'s \"content\" property is deprecated. The ability to use a checksum to retrieve content from the filebucket using the \"content\" property will be removed in a future release. The literal value of the \"content\" property will be written to the file. The checksum retrieval functionality is being replaced by the use of static catalogs. See https://puppet.com/docs/puppet/latest/static_catalogs.html for more information.', {:file => 'my/file.pp', :line => 5})\n        d = digest(\"this is some content\")\n        catalog.add_resource described_class.new(:path => path, :content => \"{#{digest_algorithm}}#{d}\")\n        catalog.apply\n      end\n\n      it \"should not give a checksum deprecation warning when no content is specified while checksum and checksum value are used\" do\n        expect(Puppet).not_to receive(:puppet_deprecation_warning)\n        d = digest(\"this is some content\")\n        catalog.add_resource described_class.new(:path => path, :checksum => digest_algorithm, :checksum_value => d)\n        catalog.apply\n      end\n    end\n\n    CHECKSUM_TYPES_TO_TRY.each do |checksum_type, checksum|\n      describe \"when checksum_type is #{checksum_type}\" do\n        # FileBucket uses the globally configured default for lookup by digest, which right now is SHA256.\n        it_should_behave_like \"files are backed up\", {:checksum => checksum_type} do\n          let(:filebucket_digest) { Proc.new {|x| Puppet::Util::Checksums.sha256(x)} }\n        end\n      end\n    end\n  end\n\n  describe \"when recursing\" do\n    def build_path(dir)\n      Dir.mkdir(dir)\n      File.chmod(0750, dir)\n\n      @dirs = [dir]\n      @files = []\n\n      %w{one two}.each do |subdir|\n        fdir = File.join(dir, subdir)\n        Dir.mkdir(fdir)\n        File.chmod(0750, fdir)\n        @dirs << fdir\n\n        %w{three}.each do |file|\n          ffile = File.join(fdir, file)\n          @files << ffile\n          File.open(ffile, \"w\") { |f| f.puts \"test #{file}\" }\n          File.chmod(0640, ffile)\n        end\n      end\n    end\n\n    it \"should be able to recurse over a nonexistent file\" do\n      @file = described_class.new(\n        :name    => path,\n        :mode    => '0644',\n        :recurse => true,\n        :backup  => false\n      )\n\n      catalog.add_resource @file\n\n      expect { @file.eval_generate }.not_to raise_error\n    end\n\n    it \"should be able to recursively set properties on existing files\" do\n      path = tmpfile(\"file_integration_tests\")\n\n      build_path(path)\n\n      file = described_class.new(\n        :name    => path,\n        :mode    => '0644',\n        :recurse => true,\n        :backup  => false\n      )\n\n      catalog.add_resource file\n\n      catalog.apply\n\n      expect(@dirs).not_to be_empty\n      @dirs.each do |dir|\n        expect(get_mode(dir) & 007777).to eq(0755)\n      end\n\n      expect(@files).not_to be_empty\n      @files.each do |dir|\n        expect(get_mode(dir) & 007777).to eq(0644)\n      end\n    end\n\n    it \"should be able to recursively make links to other files\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n      source = tmpfile(\"file_link_integration_source\")\n\n      build_path(source)\n\n      dest = tmpfile(\"file_link_integration_dest\")\n\n      @file = described_class.new(:name => dest, :target => source, :recurse => true, :ensure => :link, :backup => false)\n\n      catalog.add_resource @file\n\n      catalog.apply\n\n      @dirs.each do |path|\n        link_path = path.sub(source, dest)\n\n        expect(Puppet::FileSystem.lstat(link_path)).to be_directory\n      end\n\n      @files.each do |path|\n        link_path = path.sub(source, dest)\n\n        expect(Puppet::FileSystem.lstat(link_path).ftype).to eq(\"link\")\n      end\n    end\n\n    it \"should be able to recursively copy files\" do\n      source = tmpfile(\"file_source_integration_source\")\n\n      build_path(source)\n\n      dest = tmpfile(\"file_source_integration_dest\")\n\n      @file = described_class.new(:name => dest, :source => source, :recurse => true, :backup => false)\n\n      catalog.add_resource @file\n\n      catalog.apply\n\n      @dirs.each do |path|\n        newpath = path.sub(source, dest)\n\n        expect(Puppet::FileSystem.lstat(newpath)).to be_directory\n      end\n\n      @files.each do |path|\n        newpath = path.sub(source, dest)\n\n        expect(Puppet::FileSystem.lstat(newpath).ftype).to eq(\"file\")\n      end\n    end\n\n    it \"should not recursively manage files set to be ignored\" do\n      srcdir = tmpfile(\"ignore_vs_recurse_1\")\n      dstdir = tmpfile(\"ignore_vs_recurse_2\")\n\n      FileUtils.mkdir_p(srcdir)\n      FileUtils.mkdir_p(dstdir)\n\n      srcfile = File.join(srcdir, \"file.src\")\n      cpyfile = File.join(dstdir, \"file.src\")\n      ignfile = File.join(srcdir, \"file.ign\")\n\n      File.open(srcfile, \"w\") { |f| f.puts \"don't ignore me\" }\n      File.open(ignfile, \"w\") { |f| f.puts \"you better ignore me\" }\n\n\n      catalog.add_resource described_class.new(\n                             :name => srcdir,\n                             :ensure => 'directory',\n                             :mode => '0755',)\n\n      catalog.add_resource described_class.new(\n                             :name => dstdir,\n                             :ensure => 'directory',\n                             :mode => \"755\",\n                             :source => srcdir,\n                             :recurse => true,\n                             :ignore => '*.ign',)\n\n      catalog.apply\n      expect(Puppet::FileSystem.exist?(srcdir)).to be_truthy\n      expect(Puppet::FileSystem.exist?(dstdir)).to be_truthy\n      expect(File.read(srcfile).strip).to eq(\"don't ignore me\")\n      expect(File.read(cpyfile).strip).to eq(\"don't ignore me\")\n      expect(Puppet::FileSystem.exist?(\"#{dstdir}/file.ign\")).to be_falsey\n    end\n\n    it \"should not recursively manage files managed by a more specific explicit file\" do\n      dir = tmpfile(\"recursion_vs_explicit_1\")\n\n      subdir = File.join(dir, \"subdir\")\n      file = File.join(subdir, \"file\")\n\n      FileUtils.mkdir_p(subdir)\n      File.open(file, \"w\") { |f| f.puts \"\" }\n\n      base = described_class.new(:name => dir, :recurse => true, :backup => false, :mode => \"755\")\n      sub = described_class.new(:name => subdir, :recurse => true, :backup => false, :mode => \"644\")\n\n      catalog.add_resource base\n      catalog.add_resource sub\n\n      catalog.apply\n\n      expect(get_mode(file) & 007777).to eq(0644)\n    end\n\n    it \"should recursively manage files even if there is an explicit file whose name is a prefix of the managed file\" do\n      managed      = File.join(path, \"file\")\n      generated    = File.join(path, \"file_with_a_name_starting_with_the_word_file\")\n\n      FileUtils.mkdir_p(path)\n      FileUtils.touch(managed)\n      FileUtils.touch(generated)\n\n      catalog.add_resource described_class.new(:name => path,    :recurse => true, :backup => false, :mode => '0700')\n      catalog.add_resource described_class.new(:name => managed, :recurse => true, :backup => false, :mode => \"644\")\n\n      catalog.apply\n\n      expect(get_mode(generated) & 007777).to eq(0700)\n    end\n\n    describe \"when recursing remote directories\" do\n      describe \"for the 2nd time\" do\n        with_checksum_types \"one\", \"x\" do\n          let(:target_file) { File.join(path, 'x') }\n          let(:second_catalog) { Puppet::Resource::Catalog.new }\n          before(:each) do\n            @options = {\n              :path => path,\n              :ensure => :directory,\n              :backup => false,\n              :recurse => true,\n              :checksum => checksum_type,\n              :source => env_path\n            }\n          end\n\n          it \"should not update the target directory\" do\n            # Ensure the test believes the source file was written in the past.\n            FileUtils.touch checksum_file, :mtime => Time.now - 20\n            catalog.add_resource Puppet::Type.send(:newfile, @options)\n            catalog.apply\n            expect(File).to be_directory(path)\n            expect(Puppet::FileSystem.exist?(target_file)).to be_truthy\n\n            # The 2nd time the resource should not change.\n            second_catalog.add_resource Puppet::Type.send(:newfile, @options)\n            result = second_catalog.apply\n            status = result.report.resource_statuses[\"File[#{target_file}]\"]\n            expect(status).not_to be_failed\n            expect(status).not_to be_changed\n          end\n\n          it \"should update the target directory if contents change\" do\n            pending \"a way to appropriately mock ctime checks for a particular file\" if checksum_type == 'ctime'\n\n            catalog.add_resource Puppet::Type.send(:newfile, @options)\n            catalog.apply\n            expect(File).to be_directory(path)\n            expect(Puppet::FileSystem.exist?(target_file)).to be_truthy\n\n            # Change the source file.\n            File.open(checksum_file, \"wb\") { |f| f.write \"some content\" }\n            FileUtils.touch target_file, mtime: Time.now - 20\n\n            # The 2nd time should update the resource.\n            second_catalog.add_resource Puppet::Type.send(:newfile, @options)\n            result = second_catalog.apply\n            status = result.report.resource_statuses[\"File[#{target_file}]\"]\n            expect(status).not_to be_failed\n            expect(status).to be_changed\n          end\n        end\n      end\n\n      describe \"when sourceselect first\" do\n        describe \"for a directory\" do\n          it \"should recursively copy the first directory that exists\" do\n            one = File.expand_path('thisdoesnotexist')\n            two = tmpdir('two')\n\n            FileUtils.mkdir_p(File.join(two, 'three'))\n            FileUtils.touch(File.join(two, 'three', 'four'))\n\n            catalog.add_resource Puppet::Type.newfile(\n                               :path    => path,\n                               :ensure  => :directory,\n                               :backup  => false,\n                               :recurse => true,\n                               :sourceselect => :first,\n                               :source => [one, two]\n                               )\n\n            catalog.apply\n\n            expect(File).to be_directory(path)\n            expect(Puppet::FileSystem.exist?(File.join(path, 'one'))).to be_falsey\n            expect(Puppet::FileSystem.exist?(File.join(path, 'three', 'four'))).to be_truthy\n          end\n\n          it \"should recursively copy an empty directory\" do\n            one = File.expand_path('thisdoesnotexist')\n            two = tmpdir('two')\n            three = tmpdir('three')\n            file_in_dir_with_contents(three, 'a', '')\n\n            catalog.add_resource Puppet::Type.newfile(\n                               :path    => path,\n                               :ensure  => :directory,\n                               :backup  => false,\n                               :recurse => true,\n                               :sourceselect => :first,\n                               :source => [one, two, three]\n                               )\n\n            catalog.apply\n\n            expect(File).to be_directory(path)\n            expect(Puppet::FileSystem.exist?(File.join(path, 'a'))).to be_falsey\n          end\n\n          it \"should only recurse one level\" do\n            one = tmpdir('one')\n            FileUtils.mkdir_p(File.join(one, 'a', 'b'))\n            FileUtils.touch(File.join(one, 'a', 'b', 'c'))\n\n            two = tmpdir('two')\n            FileUtils.mkdir_p(File.join(two, 'z'))\n            FileUtils.touch(File.join(two, 'z', 'y'))\n\n            catalog.add_resource Puppet::Type.newfile(\n                               :path    => path,\n                               :ensure  => :directory,\n                               :backup  => false,\n                               :recurse => true,\n                               :recurselimit => 1,\n                               :sourceselect => :first,\n                               :source => [one, two]\n                               )\n\n            catalog.apply\n\n            expect(Puppet::FileSystem.exist?(File.join(path, 'a'))).to be_truthy\n            expect(Puppet::FileSystem.exist?(File.join(path, 'a', 'b'))).to be_falsey\n            expect(Puppet::FileSystem.exist?(File.join(path, 'z'))).to be_falsey\n          end\n        end\n\n        describe \"for a file\" do\n          it \"should copy the first file that exists\" do\n            one = File.expand_path('thisdoesnotexist')\n            two = tmpfile_with_contents('two', 'yay')\n            three = tmpfile_with_contents('three', 'no')\n\n            catalog.add_resource Puppet::Type.newfile(\n                               :path    => path,\n                               :ensure  => :file,\n                               :backup  => false,\n                               :sourceselect => :first,\n                               :source => [one, two, three]\n                               )\n\n            catalog.apply\n\n            expect(File.read(path)).to eq('yay')\n          end\n\n          it \"should copy an empty file\" do\n            one = File.expand_path('thisdoesnotexist')\n            two = tmpfile_with_contents('two', '')\n            three = tmpfile_with_contents('three', 'no')\n\n            catalog.add_resource Puppet::Type.newfile(\n                               :path    => path,\n                               :ensure  => :file,\n                               :backup  => false,\n                               :sourceselect => :first,\n                               :source => [one, two, three]\n                               )\n\n            catalog.apply\n\n            expect(File.read(path)).to eq('')\n          end\n        end\n      end\n\n      describe \"when sourceselect all\" do\n        describe \"for a directory\" do\n          it \"should recursively copy all sources from the first valid source\" do\n            dest = tmpdir('dest')\n            one = tmpdir('one')\n            two = tmpdir('two')\n            three = tmpdir('three')\n            four = tmpdir('four')\n\n            file_in_dir_with_contents(one, 'a', one)\n            file_in_dir_with_contents(two, 'a', two)\n            file_in_dir_with_contents(two, 'b', two)\n            file_in_dir_with_contents(three, 'a', three)\n            file_in_dir_with_contents(three, 'c', three)\n\n            obj = Puppet::Type.newfile(\n                               :path    => dest,\n                               :ensure  => :directory,\n                               :backup  => false,\n                               :recurse => true,\n                               :sourceselect => :all,\n                               :source => [one, two, three, four]\n                               )\n\n            catalog.add_resource obj\n            catalog.apply\n\n            expect(File.read(File.join(dest, 'a'))).to eq(one)\n            expect(File.read(File.join(dest, 'b'))).to eq(two)\n            expect(File.read(File.join(dest, 'c'))).to eq(three)\n          end\n\n          it \"should only recurse one level from each valid source\" do\n            one = tmpdir('one')\n            FileUtils.mkdir_p(File.join(one, 'a', 'b'))\n            FileUtils.touch(File.join(one, 'a', 'b', 'c'))\n\n            two = tmpdir('two')\n            FileUtils.mkdir_p(File.join(two, 'z'))\n            FileUtils.touch(File.join(two, 'z', 'y'))\n\n            obj = Puppet::Type.newfile(\n                               :path    => path,\n                               :ensure  => :directory,\n                               :backup  => false,\n                               :recurse => true,\n                               :recurselimit => 1,\n                               :sourceselect => :all,\n                               :source => [one, two]\n                               )\n\n            catalog.add_resource obj\n            catalog.apply\n\n            expect(Puppet::FileSystem.exist?(File.join(path, 'a'))).to be_truthy\n            expect(Puppet::FileSystem.exist?(File.join(path, 'a', 'b'))).to be_falsey\n            expect(Puppet::FileSystem.exist?(File.join(path, 'z'))).to be_truthy\n            expect(Puppet::FileSystem.exist?(File.join(path, 'z', 'y'))).to be_falsey\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when generating resources\" do\n    before do\n      source = tmpdir(\"generating_in_catalog_source\")\n\n      file_in_dir_with_contents(source, \"one\", \"uno\")\n      file_in_dir_with_contents(source, \"two\", \"dos\")\n\n      @file = described_class.new(\n        :name => path,\n        :source => source,\n        :recurse => true,\n        :backup => false\n      )\n\n      catalog.add_resource @file\n    end\n\n    it \"should add each generated resource to the catalog\" do\n      catalog.apply do |trans|\n        expect(catalog.resource(:file, File.join(path, \"one\"))).to be_a(described_class)\n        expect(catalog.resource(:file, File.join(path, \"two\"))).to be_a(described_class)\n      end\n    end\n\n    it \"should have an edge to each resource in the relationship graph\" do\n      catalog.apply do |trans|\n        one = catalog.resource(:file, File.join(path, \"one\"))\n        expect(catalog.relationship_graph).to be_edge(@file, one)\n\n        two = catalog.resource(:file, File.join(path, \"two\"))\n        expect(catalog.relationship_graph).to be_edge(@file, two)\n      end\n    end\n  end\n\n  describe \"when copying files\" do\n    it \"should be able to copy files with pound signs in their names (#285)\" do\n      source = tmpfile_with_contents(\"filewith#signs\", \"foo\")\n      dest = tmpfile(\"destwith#signs\")\n      catalog.add_resource described_class.new(:name => dest, :source => source)\n\n      catalog.apply\n\n      expect(File.read(dest)).to eq(\"foo\")\n    end\n\n    it \"should be able to copy files with spaces in their names\" do\n      dest = tmpfile(\"destwith spaces\")\n      source = tmpfile_with_contents(\"filewith spaces\", \"foo\")\n      catalog.add_resource described_class.new(:path => dest, :source => source)\n\n      catalog.apply\n\n      expect(File.read(dest)).to eq(\"foo\")\n    end\n\n    it \"should maintain source URIs as UTF-8 with Unicode characters in their names and be able to copy such files\" do\n      # different UTF-8 widths\n      # 1-byte A\n      # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n      # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n      # 4-byte <U+070E> - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n      mixed_utf8 = \"A\\u06FF\\u16A0\\u{2070E}\" # Aۿᚠ<U+070E>\n\n      dest = tmpfile(\"destwith #{mixed_utf8}\")\n      source = tmpfile_with_contents(\"filewith #{mixed_utf8}\", \"foo\")\n      catalog.add_resource described_class.new(:path => dest, :source => source)\n\n      catalog.apply\n\n      # find the resource and verify\n      resource = catalog.resources.first { |r| r.title == \"File[#{dest}]\" }\n      uri_path = resource.parameters[:source].uri.path\n\n      # note that Windows file:// style URIs get an extra / in front of c:/ like /c:/\n      source_prefix = Puppet::Util::Platform.windows? ? '/' : ''\n\n      # the URI can be round-tripped through unescape\n      expect(Puppet::Util.uri_unescape(uri_path)).to eq(source_prefix + source)\n      # and is properly UTF-8\n      expect(uri_path.encoding).to eq (Encoding::UTF_8)\n\n      expect(File.read(dest)).to eq('foo')\n    end\n\n    it \"should be able to copy individual files even if recurse has been specified\" do\n      source = tmpfile_with_contents(\"source\", \"foo\")\n      dest = tmpfile(\"dest\")\n      catalog.add_resource described_class.new(:name => dest, :source => source, :recurse => true)\n\n      catalog.apply\n\n      expect(File.read(dest)).to eq(\"foo\")\n    end\n  end\n\n  CHECKSUM_TYPES_TO_TRY.each do |checksum_type, checksum|\n    describe \"when checksum_type is #{checksum_type}\" do\n      before(:each) do\n        @options = {:path => path, :content => CHECKSUM_PLAINTEXT, :checksum => checksum_type}\n      end\n\n      context \"when changing the content\" do\n        before :each do\n          FileUtils.touch(path)\n          catalog.add_resource described_class.send(:new, @options)\n        end\n\n        it \"should overwrite contents\" do\n          catalog.apply\n          expect(Puppet::FileSystem.binread(path)).to eq(CHECKSUM_PLAINTEXT)\n        end\n\n        it \"should log that content changed\" do\n          report = catalog.apply.report\n          expect(report.logs.first.source).to eq(\"/File[#{path}]/content\")\n          expect(report.logs.first.message).to match(/content changed '{#{checksum_type}}[0-9a-f]*' to '{#{checksum_type}}#{checksum}'/)\n        end\n      end\n\n      context \"ensure is present\" do\n        before(:each) do\n          @options[:ensure] = \"present\"\n        end\n\n        it \"should create a file with content\" do\n          catalog.add_resource described_class.send(:new, @options)\n          catalog.apply\n          expect(Puppet::FileSystem.binread(path)).to eq(CHECKSUM_PLAINTEXT)\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          status = second_catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n          expect(status).not_to be_failed\n          expect(status).not_to be_changed\n        end\n\n        it \"should log the content checksum\" do\n          catalog.add_resource described_class.send(:new, @options)\n          report = catalog.apply.report\n          expect(report.logs.first.source).to eq(\"/File[#{path}]/ensure\")\n          expect(report.logs.first.message).to eq(\"defined content as '{#{checksum_type}}#{checksum}'\")\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          logs = second_catalog.apply.report.logs\n          expect(logs).to be_empty\n        end\n      end\n\n      context \"ensure is omitted\" do\n        it \"should create a file with content\" do\n          catalog.add_resource described_class.send(:new, @options)\n          catalog.apply\n          expect(Puppet::FileSystem.binread(path)).to eq(CHECKSUM_PLAINTEXT)\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          status = second_catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n          expect(status).not_to be_failed\n          expect(status).not_to be_changed\n        end\n\n        it \"should log the content checksum\" do\n          catalog.add_resource described_class.send(:new, @options)\n          report = catalog.apply.report\n          expect(report.logs.first.source).to eq(\"/File[#{path}]/ensure\")\n          expect(report.logs.first.message).to eq(\"defined content as '{#{checksum_type}}#{checksum}'\")\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          logs = second_catalog.apply.report.logs\n          expect(logs).to be_empty\n        end\n      end\n\n      context \"both content and ensure are set\" do\n        before(:each) do\n          @options[:ensure] = \"file\"\n        end\n\n        it \"should create files with content\" do\n          catalog.add_resource described_class.send(:new, @options)\n          catalog.apply\n          expect(Puppet::FileSystem.binread(path)).to eq(CHECKSUM_PLAINTEXT)\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          status = second_catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n          expect(status).not_to be_failed\n          expect(status).not_to be_changed\n        end\n\n        it \"should log the content checksum\" do\n          catalog.add_resource described_class.send(:new, @options)\n          report = catalog.apply.report\n          expect(report.logs.first.source).to eq(\"/File[#{path}]/ensure\")\n          expect(report.logs.first.message).to eq(\"defined content as '{#{checksum_type}}#{checksum}'\")\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          logs = second_catalog.apply.report.logs\n          expect(logs).to be_empty\n        end\n      end\n    end\n  end\n\n  it \"should delete files with sources but that are set for deletion\" do\n    source = tmpfile_with_contents(\"source_source_with_ensure\", \"yay\")\n    dest = tmpfile_with_contents(\"source_source_with_ensure\", \"boo\")\n\n    file = described_class.new(\n      :path   => dest,\n      :ensure => :absent,\n      :source => source,\n      :backup => false\n    )\n\n    catalog.add_resource file\n    catalog.apply\n\n    expect(Puppet::FileSystem.exist?(dest)).to be_falsey\n  end\n\n  describe \"when sourcing\" do\n    it \"should give a deprecation warning when the user sets source_permissions\" do\n      expect(Puppet).to receive(:puppet_deprecation_warning).with(\n        'The `source_permissions` parameter is deprecated. Explicitly set `owner`, `group`, and `mode`.',\n        {:file => 'my/file.pp', :line => 5})\n\n      catalog.add_resource described_class.new(:path => path, :content => 'this is content', :source_permissions => :use_when_creating)\n      catalog.apply\n    end\n\n    it \"should not give a deprecation warning when the user does not set source_permissions\" do\n      expect(Puppet).not_to receive(:puppet_deprecation_warning)\n      catalog.add_resource described_class.new(:path => path, :content => 'this is content')\n      catalog.apply\n    end\n\n    with_checksum_types \"source\", \"default_values\" do\n      before(:each) do\n        set_mode(0770, checksum_file)\n        @options = {\n          :path   => path,\n          :ensure => :file,\n          :source => checksum_file,\n          :checksum => checksum_type,\n          :backup => false\n        }\n      end\n\n      describe \"on POSIX systems\", :if => Puppet.features.posix? do\n        it \"should apply the source metadata values\" do\n          @options[:source_permissions] = :use\n\n          catalog.add_resource described_class.send(:new, @options)\n          catalog.apply\n          expect(get_owner(path)).to eq(get_owner(checksum_file))\n          expect(get_group(path)).to eq(get_group(checksum_file))\n          expect(get_mode(path) & 07777).to eq(0770)\n\n          second_catalog = Puppet::Resource::Catalog.new\n          second_catalog.add_resource described_class.send(:new, @options)\n          status = second_catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n          expect(status).not_to be_failed\n          expect(status).not_to be_changed\n        end\n      end\n\n      it \"should override the default metadata values\" do\n        @options[:mode] = '0440'\n\n        catalog.add_resource described_class.send(:new, @options)\n        catalog.apply\n        expect(get_mode(path) & 07777).to eq(0440)\n\n        second_catalog = Puppet::Resource::Catalog.new\n        second_catalog.add_resource described_class.send(:new, @options)\n        status = second_catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n        expect(status).not_to be_failed\n        expect(status).not_to be_changed\n      end\n    end\n\n    let(:source) { tmpfile_with_contents(\"source_default_values\", \"yay\") }\n\n    describe \"from http\" do\n      let(:http_source) { \"http://my-server/file\" }\n      let(:httppath) { \"#{path}http\" }\n\n      context \"using mtime\", :vcr => true do\n        let(:resource) do\n          described_class.new(\n            :path   => httppath,\n            :ensure => :file,\n            :source => http_source,\n            :backup => false,\n            :checksum => :mtime\n          )\n        end\n\n        it \"should fetch if not on the local disk\" do\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n        end\n\n        # The fixture has neither last-modified nor content-checksum headers.\n        # Such upstream ressources are treated as \"really fresh\" and get\n        # downloaded during every run.\n        it \"should fetch if no header specified\" do\n          File.open(httppath, \"wb\") { |f| f.puts \"Content originally on disk\\n\" }\n          # make sure the mtime is not \"right now\", lest we get a race\n          FileUtils.touch httppath, mtime: Time.parse(\"Sun, 22 Mar 2015 22:57:43 GMT\")\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n        end\n\n        it \"should fetch if mtime is older on disk\" do\n          File.open(httppath, \"wb\") { |f| f.puts \"Content originally on disk\\n\" }\n          # fixture has Last-Modified: Sun, 22 Mar 2015 22:25:34 GMT\n          FileUtils.touch httppath, mtime: Time.parse(\"Sun, 22 Mar 2015 22:22:34 GMT\")\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n        end\n\n        it \"should not update if mtime is newer on disk\" do\n          File.open(httppath, \"wb\") { |f| f.puts \"Content via HTTP\\n\" }\n          mtime = File.stat(httppath).mtime\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n          expect(File.stat(httppath).mtime).to eq mtime\n        end\n      end\n\n      context \"using md5\", :vcr => true do\n        let(:resource) do\n          described_class.new(\n            :path   => httppath,\n            :ensure => :file,\n            :source => http_source,\n            :backup => false,\n          )\n        end\n\n        it \"should fetch if not on the local disk\" do\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n        end\n\n        it \"should update if content differs on disk\" do\n          File.open(httppath, \"wb\") { |f| f.puts \"Content originally on disk\\n\" }\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n        end\n\n        it \"should not update if content on disk is up-to-date\" do\n          File.open(httppath, \"wb\") { |f| f.puts \"Content via HTTP\\n\" }\n          disk_mtime = Time.parse(\"Sun, 22 Mar 2015 22:22:34 GMT\")\n          FileUtils.touch httppath, mtime: disk_mtime\n          catalog.add_resource resource\n          catalog.apply\n          expect(Puppet::FileSystem.exist?(httppath)).to be_truthy\n          expect(File.read(httppath)).to eq \"Content via HTTP\\n\"\n          expect(File.stat(httppath).mtime).to eq disk_mtime\n        end\n\n      end\n    end\n\n    describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n      def expects_sid_granted_full_access_explicitly(path, sid)\n        inherited_ace = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE\n\n        aces = get_aces_for_path_by_sid(path, sid)\n        expect(aces).not_to be_empty\n\n        aces.each do |ace|\n          expect(ace.mask).to eq(Puppet::Util::Windows::File::FILE_ALL_ACCESS)\n          expect(ace.flags & inherited_ace).not_to eq(inherited_ace)\n        end\n      end\n\n      def expects_system_granted_full_access_explicitly(path)\n        expects_sid_granted_full_access_explicitly(path, @sids[:system])\n      end\n\n      def expects_at_least_one_inherited_ace_grants_full_access(path, sid)\n        inherited_ace = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE\n\n        aces = get_aces_for_path_by_sid(path, sid)\n        expect(aces).not_to be_empty\n\n        expect(aces.any? do |ace|\n          ace.mask == Puppet::Util::Windows::File::FILE_ALL_ACCESS &&\n            (ace.flags & inherited_ace) == inherited_ace\n        end).to be_truthy\n      end\n\n      def expects_at_least_one_inherited_system_ace_grants_full_access(path)\n        expects_at_least_one_inherited_ace_grants_full_access(path, @sids[:system])\n      end\n\n      describe \"when processing SYSTEM ACEs\" do\n        before do\n          @sids = {\n            :current_user => Puppet::Util::Windows::ADSI::User.current_user_sid.sid,\n            :system => Puppet::Util::Windows::SID::LocalSystem,\n            :users => Puppet::Util::Windows::SID::BuiltinUsers,\n            :power_users => Puppet::Util::Windows::SID::PowerUsers,\n            :none => Puppet::Util::Windows::SID::Nobody\n          }\n        end\n\n        describe \"on files\" do\n          before :each do\n            @file = described_class.new(\n              :path   => path,\n              :ensure => :file,\n              :source => source,\n              :backup => false\n            )\n            catalog.add_resource @file\n          end\n\n          describe \"when permissions are not insync?\" do\n            before :each do\n              @file[:owner] = @sids[:none]\n              @file[:group] = @sids[:none]\n            end\n\n            it \"preserves the inherited SYSTEM ACE for an existing file\" do\n              FileUtils.touch(path)\n\n              expects_at_least_one_inherited_system_ace_grants_full_access(path)\n\n              catalog.apply\n\n              expects_at_least_one_inherited_system_ace_grants_full_access(path)\n            end\n\n            it \"applies the inherited SYSTEM ACEs for a new file\" do\n              catalog.apply\n\n              expects_at_least_one_inherited_system_ace_grants_full_access(path)\n            end\n          end\n\n          describe \"created with SYSTEM as the group\" do\n            before :each do\n              @file[:owner] = @sids[:users]\n              @file[:group] = @sids[:system]\n              @file[:mode] = '0644'\n\n              catalog.apply\n            end\n\n            it \"prepends SYSTEM ace when changing group from system to power users\" do\n              @file[:group] = @sids[:power_users]\n              catalog.apply\n\n              system_aces = get_aces_for_path_by_sid(path, @sids[:system])\n              expect(system_aces.size).to eq(1)\n            end\n          end\n\n          describe \"with :links set to :follow\" do\n            it \"should not fail to apply\" do\n              # at minimal, we need an owner and/or group\n              @file[:owner] = @sids[:users]\n              @file[:links] = :follow\n\n              catalog.apply do |transaction|\n                if transaction.any_failed?\n                  pretty_transaction_error(transaction)\n                end\n              end\n            end\n          end\n        end\n\n        describe \"on directories\" do\n          before :each do\n            @directory = described_class.new(\n              :path   => dir,\n              :ensure => :directory\n            )\n            catalog.add_resource @directory\n          end\n\n          def grant_everyone_full_access(path)\n            sd = Puppet::Util::Windows::Security.get_security_descriptor(path)\n            sd.dacl.allow(\n              'S-1-1-0', #everyone\n              Puppet::Util::Windows::File::FILE_ALL_ACCESS,\n              Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |\n              Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE)\n            Puppet::Util::Windows::Security.set_security_descriptor(path, sd)\n          end\n\n          after :each do\n            grant_everyone_full_access(dir)\n          end\n\n          describe \"when permissions are not insync?\" do\n            before :each do\n              @directory[:owner] = @sids[:none]\n              @directory[:group] = @sids[:none]\n            end\n\n            it \"preserves the inherited SYSTEM ACEs for an existing directory\" do\n              FileUtils.mkdir(dir)\n\n              expects_at_least_one_inherited_system_ace_grants_full_access(dir)\n\n              catalog.apply\n\n              expects_at_least_one_inherited_system_ace_grants_full_access(dir)\n            end\n\n            it \"applies the inherited SYSTEM ACEs for a new directory\" do\n              catalog.apply\n\n              expects_at_least_one_inherited_system_ace_grants_full_access(dir)\n            end\n\n            describe \"created with SYSTEM as the group\" do\n              before :each do\n                @directory[:owner] = @sids[:users]\n                @directory[:group] = @sids[:system]\n                @directory[:mode] = '0644'\n\n                catalog.apply\n              end\n\n              it \"prepends SYSTEM ace when changing group from system to power users\" do\n                @directory[:group] = @sids[:power_users]\n                catalog.apply\n\n                system_aces = get_aces_for_path_by_sid(dir, @sids[:system])\n                expect(system_aces.size).to eq(1)\n              end\n            end\n\n            describe \"with :links set to :follow\" do\n              it \"should not fail to apply\" do\n                # at minimal, we need an owner and/or group\n                @directory[:owner] = @sids[:users]\n                @directory[:links] = :follow\n\n                catalog.apply do |transaction|\n                  if transaction.any_failed?\n                    pretty_transaction_error(transaction)\n                  end\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when purging files\" do\n    before do\n      sourcedir = tmpdir(\"purge_source\")\n      destdir = tmpdir(\"purge_dest\")\n      sourcefile = File.join(sourcedir, \"sourcefile\")\n\n      @copiedfile = File.join(destdir, \"sourcefile\")\n      @localfile  = File.join(destdir, \"localfile\")\n      @purgee     = File.join(destdir, \"to_be_purged\")\n\n      File.open(@localfile, \"w\") { |f| f.print \"oldtest\" }\n      File.open(sourcefile, \"w\") { |f| f.print \"funtest\" }\n      # this file should get removed\n      File.open(@purgee, \"w\") { |f| f.print \"footest\" }\n\n      lfobj = Puppet::Type.newfile(\n        :title   => \"localfile\",\n        :path    => @localfile,\n        :content => \"rahtest\",\n        :ensure  => :file,\n        :backup  => false\n      )\n\n      destobj = Puppet::Type.newfile(\n        :title   => \"destdir\",\n        :path    => destdir,\n        :source  => sourcedir,\n        :backup  => false,\n        :purge   => true,\n        :recurse => true\n      )\n\n      catalog.add_resource lfobj, destobj\n      catalog.apply\n    end\n\n    it \"should still copy remote files\" do\n      expect(File.read(@copiedfile)).to eq('funtest')\n    end\n\n    it \"should not purge managed, local files\" do\n      expect(File.read(@localfile)).to eq('rahtest')\n    end\n\n    it \"should purge files that are neither remote nor otherwise managed\" do\n      expect(Puppet::FileSystem.exist?(@purgee)).to be_falsey\n    end\n  end\n\n  describe \"when using validate_cmd\" do\n    test_cmd = '/bin/test'\n    if Puppet.runtime[:facter].value('os.family') == 'Debian'\n      test_cmd = '/usr/bin/test'\n    end\n\n    if Puppet.runtime[:facter].value('os.name') == 'Darwin'\n      stat_cmd = \"stat -f '%Lp'\"\n    else\n      stat_cmd = \"stat --format=%a\"\n    end\n\n    it \"sets the default mode of the temporary file to '0644'\", :unless => Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n      catalog.add_resource(described_class.new(:path => path, :content => \"foo\",\n                                               :validate_replacement => '^',\n                                               :validate_cmd => %Q{\n                                               echo \"The permissions of the file ($(#{stat_cmd} ^)) should equal 644\";\n                                               #{test_cmd} \"644\" == \"$(#{stat_cmd} ^)\"\n                                               }))\n      report = catalog.apply.report\n      expect(report.resource_statuses[\"File[#{path}]\"].events.first.message).to match(/defined content as '{sha256}/)\n      expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n      expect(Puppet::FileSystem.exist?(path)).to be_truthy\n    end\n\n    it \"should change the permissions of the temp file to match the final file permissions\", :unless => Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby?do\n      catalog.add_resource(described_class.new(:path => path, :content => \"foo\",\n                                               :mode => '0555',\n                                               :validate_replacement => '^',\n                                               :validate_cmd => %Q{\n                                               echo \"The permissions of the file ($(#{stat_cmd} ^)) should equal 555\";\n                                               #{test_cmd} \"555\" == \"$(#{stat_cmd} ^)\"\n                                               }))\n      report = catalog.apply.report\n      expect(report.resource_statuses[\"File[#{path}]\"].events.first.message).to match(/defined content as '{sha256}/)\n      expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n      expect(Puppet::FileSystem.exist?(path)).to be_truthy\n    end\n\n    it \"should fail the file resource if command fails\" do\n      catalog.add_resource(described_class.new(:path => path, :content => \"foo\", :validate_cmd => \"/usr/bin/env false\"))\n      expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/bin/env false\", {:combine => true, :failonfail => true}).and_raise(Puppet::ExecutionFailure, \"Failed\")\n      report = catalog.apply.report\n      expect(report.resource_statuses[\"File[#{path}]\"]).to be_failed\n      expect(Puppet::FileSystem.exist?(path)).to be_falsey\n    end\n\n    it \"should succeed the file resource if command succeeds\" do\n      catalog.add_resource(described_class.new(:path => path, :content => \"foo\", :validate_cmd => \"/usr/bin/env true\"))\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(\"/usr/bin/env true\", {:combine => true, :failonfail => true})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      report = catalog.apply.report\n      expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n      expect(Puppet::FileSystem.exist?(path)).to be_truthy\n    end\n  end\n\n  def tmpfile_with_contents(name, contents)\n    file = tmpfile(name)\n    File.open(file, \"w\") { |f| f.write contents }\n    file\n  end\n\n  def file_in_dir_with_contents(dir, name, contents)\n    full_name = File.join(dir, name)\n    File.open(full_name, \"w\") { |f| f.write contents }\n    full_name\n  end\n\n  def pretty_transaction_error(transaction)\n    report = transaction.report\n    status_failures = report.resource_statuses.values.select { |r| r.failed? }\n    status_fail_msg = status_failures.\n      collect(&:events).\n      flatten.\n      select { |event| event.status == 'failure' }.\n      collect { |event| \"#{event.resource}: #{event.message}\" }.join(\"; \")\n\n    raise \"Got #{status_failures.length} failure(s) while applying: #{status_fail_msg}\"\n  end\n\n  describe \"copying a file that is a link to a file\", :if => Puppet.features.manages_symlinks? do\n    let(:target) { tmpfile('target') }\n    let(:link) { tmpfile('link') }\n    let(:copy) { tmpfile('copy') }\n    it \"should copy the target of the link if :links => follow\" do\n      catalog.add_resource described_class.new(\n        :name => target,\n        :ensure => \"present\",\n        :content => \"Jenny I got your number / I need to make you mine\")\n      catalog.add_resource described_class.new(\n        :name => link,\n        :ensure => \"link\",\n        :target => target)\n      catalog.add_resource described_class.new(\n        :name => copy,\n        :ensure => \"present\",\n        :source => link,\n        :links => \"follow\")\n      catalog.apply\n      expect(Puppet::FileSystem).to be_file(copy)\n      expect(File.read(target)).to eq(File.read(copy))\n    end\n\n    it \"should copy the link itself if :links => manage\" do\n      catalog.add_resource described_class.new(\n        :name => target,\n        :ensure => \"present\",\n        :content => \"Jenny I got your number / I need to make you mine\")\n      catalog.add_resource described_class.new(\n        :name => link,\n        :ensure => \"link\",\n        :target => target)\n      catalog.add_resource described_class.new(\n        :name => copy,\n        :ensure => \"present\",\n        :source => link,\n        :links => \"manage\")\n      catalog.apply\n      expect(Puppet::FileSystem).to be_symlink(copy)\n      expect(File.read(link)).to eq(File.read(copy))\n    end\n  end\n\n  describe \"copying a file that is a link to a directory\", :if => Puppet.features.manages_symlinks? do\n    let(:target) { tmpdir('target') }\n    let(:link) { tmpfile('link') }\n    let(:copy) { tmpfile('copy') }\n    context \"when the recurse attribute is false\" do\n      it \"should copy the top-level directory if :links => follow\" do\n        catalog.add_resource described_class.new(\n          :name => target,\n          :ensure => \"directory\")\n        catalog.add_resource described_class.new(\n          :name => link,\n          :ensure => \"link\",\n          :target => target)\n        catalog.add_resource described_class.new(\n          :name => copy,\n          :ensure => \"present\",\n          :source => link,\n          :recurse => false,\n          :links => \"follow\")\n        catalog.apply\n        expect(Puppet::FileSystem).to be_directory(copy)\n      end\n\n      it \"should copy the link itself if :links => manage\" do\n        catalog.add_resource described_class.new(\n          :name => target,\n          :ensure => \"directory\")\n        catalog.add_resource described_class.new(\n          :name => link,\n          :ensure => \"link\",\n          :target => target)\n        catalog.add_resource described_class.new(\n          :name => copy,\n          :ensure => \"present\",\n          :source => link,\n          :recurse => false,\n          :links => \"manage\")\n        catalog.apply\n        expect(Puppet::FileSystem).to be_symlink(copy)\n        expect(Dir.entries(link)).to eq(Dir.entries(copy))\n      end\n    end\n\n    context \"and the recurse attribute is true\" do\n      it \"should recursively copy the directory if :links => follow\" do\n        catalog.add_resource described_class.new(\n          :name => target,\n          :ensure => \"directory\")\n        catalog.add_resource described_class.new(\n          :name => link,\n          :ensure => \"link\",\n          :target => target)\n        catalog.add_resource described_class.new(\n          :name => copy,\n          :ensure => \"present\",\n          :source => link,\n          :recurse => true,\n          :links => \"follow\")\n        catalog.apply\n        expect(Puppet::FileSystem).to be_directory(copy)\n        expect(Dir.entries(target)).to eq(Dir.entries(copy))\n      end\n\n      it \"should copy the link itself if :links => manage\" do\n        catalog.add_resource described_class.new(\n          :name => target,\n          :ensure => \"directory\")\n        catalog.add_resource described_class.new(\n          :name => link,\n          :ensure => \"link\",\n          :target => target)\n        catalog.add_resource described_class.new(\n          :name => copy,\n          :ensure => \"present\",\n          :source => link,\n          :recurse => true,\n          :links => \"manage\")\n        catalog.apply\n        expect(Puppet::FileSystem).to be_symlink(copy)\n        expect(Dir.entries(link)).to eq(Dir.entries(copy))\n      end\n    end\n  end\n\n  [:md5, :sha256, :md5lite, :sha256lite, :sha384, :sha512, :sha224].each do |checksum|\n    describe \"setting checksum_value explicitly with checksum #{checksum}\" do\n      let(:path) { tmpfile('target') }\n      let(:contents) { 'yay' }\n\n      before :each do\n        @options = {\n          :path           => path,\n          :ensure         => :file,\n          :checksum       => checksum,\n          :checksum_value => Puppet::Util::Checksums.send(checksum, contents)\n        }\n      end\n\n      def verify_file(transaction)\n        status = transaction.report.resource_statuses[\"File[#{path}]\"]\n        expect(status).not_to be_failed\n        expect(Puppet::FileSystem).to be_file(path)\n        expect(File.read(path)).to eq(contents)\n        status\n      end\n\n      [:source, :content].each do |prop|\n        context \"from #{prop}\" do\n          let(:source) { tmpfile_with_contents(\"source_default_values\", contents) }\n\n          before :each do\n            @options[prop] = {:source => source, :content => contents}[prop]\n          end\n\n          it \"should create a new file\" do\n            catalog.add_resource described_class.new(@options)\n            status = verify_file catalog.apply\n            expect(status).to be_changed\n          end\n\n          it \"should overwrite an existing file\" do\n            File.open(path, \"w\") { |f| f.write('bar') }\n            catalog.add_resource described_class.new(@options)\n            status = verify_file catalog.apply\n            expect(status).to be_changed\n          end\n\n          it \"should not overwrite the same file\" do\n            File.open(path, \"w\") { |f| f.write(contents) }\n            catalog.add_resource described_class.new(@options)\n            status = verify_file catalog.apply\n            expect(status).to_not be_changed\n          end\n\n          it \"should not create a file when ensuring absent\" do\n            @options[:ensure] = :absent\n            catalog.add_resource described_class.new(@options)\n            catalog.apply\n            expect(Puppet::FileSystem).to_not be_file(path)\n          end\n        end\n      end\n    end\n  end\n\n  describe \"setting checksum_value explicitly with checksum mtime\" do\n    let(:path) { tmpfile('target_dir') }\n    let(:time) { Time.now }\n\n    before :each do\n      @options = {\n        :path           => path,\n        :ensure         => :directory,\n        :checksum       => :mtime,\n        :checksum_value => time\n      }\n    end\n\n    it \"should create a new directory\" do\n      catalog.add_resource described_class.new(@options)\n      status = catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n      expect(status).not_to be_failed\n      expect(status).to be_changed\n      expect(Puppet::FileSystem).to be_directory(path)\n    end\n\n    it \"should not update mtime on an old directory\" do\n      disk_mtime = Time.parse(\"Sun, 22 Mar 2015 22:22:34 GMT\")\n      FileUtils.mkdir_p path\n      FileUtils.touch path, mtime: disk_mtime\n      status = catalog.apply.report.resource_statuses[\"File[#{path}]\"]\n      expect(status).to be_nil\n      expect(Puppet::FileSystem).to be_directory(path)\n      expect(File.stat(path).mtime).to eq(disk_mtime)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/type/notify_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe Puppet::Type.type(:notify) do\n  include PuppetSpec::Compiler\n\n  it \"logs the title at notice level\" do\n    apply_compiled_manifest(<<-MANIFEST)\n      notify { 'hi': }\n    MANIFEST\n\n    expect(@logs).to include(an_object_having_attributes(level: :notice, message: 'hi'))\n  end\n\n  it \"logs the message property\" do\n    apply_compiled_manifest(<<-MANIFEST)\n      notify { 'title':\n        message => 'hello'\n      }\n    MANIFEST\n\n    expect(@logs).to include(an_object_having_attributes(level: :notice, message: \"defined 'message' as 'hello'\"))\n  end\n\n  it \"redacts sensitive message properties\" do\n    apply_compiled_manifest(<<-MANIFEST)\n      $message = Sensitive('secret')\n      notify { 'notify1':\n        message => $message\n      }\n    MANIFEST\n\n    expect(@logs).to include(an_object_having_attributes(level: :notice, message: 'changed [redacted] to [redacted]'))\n  end\n\n  it \"redacts sensitive interpolated message properties\" do\n    apply_compiled_manifest(<<-MANIFEST)\n      $message = Sensitive('secret')\n      notify { 'notify2':\n        message => \"${message}\"\n      }\n    MANIFEST\n\n    expect(@logs).to include(an_object_having_attributes(level: :notice, message: \"defined 'message' as 'Sensitive [value redacted]'\"))\n  end\nend\n"
  },
  {
    "path": "spec/integration/type/package_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package), \"when choosing a default package provider\" do\n  before do\n    # the default provider is cached.\n    Puppet::Type.type(:package).defaultprovider = nil\n  end\n\n  def provider_name(os)\n    case os\n    when 'Solaris'\n      if Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value(:kernelrelease), '5.11') >= 0\n        :pkg\n      else\n        :sun\n      end\n    when 'Ubuntu'\n      :apt\n    when 'Debian'\n      :apt\n    when 'Darwin'\n      :pkgdmg\n    when 'RedHat'\n      if ['2.1', '3', '4'].include?(Puppet.runtime[:facter].value('os.distro.release.full'))\n        :up2date\n      else\n        :yum\n      end\n    when 'Fedora'\n      if Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value('os.release.major'), '22') >= 0\n        :dnf\n      else\n        :yum\n      end\n    when 'Suse'\n      if Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value('os.release.major'), '10') >= 0\n        :zypper\n      else\n        :rug\n      end\n    when 'FreeBSD'\n      :ports\n    when 'OpenBSD'\n      :openbsd\n    when 'DragonFly'\n      :pkgng\n    when 'OpenWrt'\n      :opkg\n    end\n  end\n\n  it \"should have a default provider\" do\n    expect(Puppet::Type.type(:package).defaultprovider).not_to be_nil\n  end\n\n  it \"should choose the correct provider each platform\" do\n    unless default_provider = provider_name(Puppet.runtime[:facter].value('os.name'))\n      pending(\"No default provider specified in this test for #{Puppet.runtime[:facter].value('os.name')}\")\n    end\n    expect(Puppet::Type.type(:package).defaultprovider.name).to eq(default_provider)\n  end\nend\n\ndescribe Puppet::Type.type(:package), \"when packages with the same name are sourced\" do\n  before :each do\n    allow(Process).to receive(:euid).and_return(0)\n    @provider = double(\n      'provider',\n      :class           => Puppet::Type.type(:package).defaultprovider,\n      :clear           => nil,\n      :satisfies?      => true,\n      :name            => :mock,\n      :validate_source => nil\n    )\n    allow(Puppet::Type.type(:package).defaultprovider).to receive(:new).and_return(@provider)\n    allow(Puppet::Type.type(:package).defaultprovider).to receive(:instances).and_return([])\n    @package = Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :present)\n\n    @catalog = Puppet::Resource::Catalog.new\n    @catalog.add_resource(@package)\n  end\n\n  describe \"with same title\" do\n    before {\n      @alt_package = Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :present)\n    }\n    it \"should give an error\" do\n      expect {\n        @catalog.add_resource(@alt_package)\n      }.to raise_error Puppet::Resource::Catalog::DuplicateResourceError, 'Duplicate declaration: Package[yay] is already declared; cannot redeclare'\n    end\n  end\n\n  describe \"with different title\" do\n    before :each do\n      @alt_package = Puppet::Type.type(:package).new(:name => \"yay\", :title => \"gem-yay\", :ensure => :present)\n    end\n\n    it \"should give an error\" do\n      provider_name = Puppet::Type.type(:package).defaultprovider.name\n      expect {\n        @catalog.add_resource(@alt_package)\n      }.to raise_error ArgumentError, \"Cannot alias Package[gem-yay] to [nil, \\\"yay\\\", :#{provider_name}]; resource [\\\"Package\\\", nil, \\\"yay\\\", :#{provider_name}] already declared\"\n    end\n  end\n\n  describe \"from multiple providers\", unless: Puppet::Util::Platform.jruby? do\n    provider_class = Puppet::Type.type(:package).provider(:gem)\n\n    before :each do\n      @alt_provider = provider_class.new\n      @alt_package = Puppet::Type.type(:package).new(:name => \"yay\", :title => \"gem-yay\", :provider => @alt_provider, :ensure => :present)\n      @catalog.add_resource(@alt_package)\n    end\n\n    describe \"when it should be present\" do\n      [:present, :latest, \"1.0\"].each do |state|\n        it \"should do nothing if it is #{state.to_s}\" do\n          expect(@provider).to receive(:properties).and_return(:ensure => state).at_least(:once)\n          expect(@alt_provider).to receive(:properties).and_return(:ensure => state).at_least(:once)\n          @catalog.apply\n        end\n      end\n\n      [:purged, :absent].each do |state|\n        it \"should install if it is #{state.to_s}\" do\n          allow(@provider).to receive(:properties).and_return(:ensure => state)\n          expect(@provider).to receive(:install)\n          allow(@alt_provider).to receive(:properties).and_return(:ensure => state)\n          expect(@alt_provider).to receive(:install)\n          @catalog.apply\n        end\n      end\n    end\n  end\nend\n\ndescribe Puppet::Type.type(:package), 'logging package state transitions' do\n  let(:catalog) { Puppet::Resource::Catalog.new }\n  let(:provider) { double('provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :validate_source => nil) }\n\n  before :each do\n    allow(Process).to receive(:euid).and_return(0)\n    allow(provider).to receive(:satisfies?).with([:purgeable]).and_return(true)\n    allow(provider.class).to receive(:instances).and_return([])\n    allow(provider).to receive(:install).and_return(nil)\n    allow(provider).to receive(:uninstall).and_return(nil)\n    allow(provider).to receive(:purge).and_return(nil)\n    allow(Puppet::Type.type(:package).defaultprovider).to receive(:new).and_return(provider)\n  end\n\n  after :each do\n    Puppet::Type.type(:package).defaultprovider = nil\n  end\n\n  # Map of old state -> {new state -> change}\n  states = {\n    # 'installed' transitions to 'removed' or 'purged'\n    :installed => {\n      :installed => nil,\n      :absent    => 'removed',\n      :purged    => 'purged'\n    },\n    # 'absent' transitions to 'created' or 'purged'\n    :absent => {\n      :installed => 'created',\n      :absent    => nil,\n      :purged    => 'purged'\n    },\n    # 'purged' transitions to 'created'\n    :purged => {\n      :installed => 'created',\n      :absent    => nil,\n      :purged    => nil\n    }\n  }\n\n  states.each do |old, new_states|\n    describe \"#{old} package\" do\n      before :each do\n        allow(provider).to receive(:properties).and_return(:ensure => old)\n      end\n\n      new_states.each do |new, status|\n        it \"ensure => #{new} should log #{status ? status : 'nothing'}\" do\n          catalog.add_resource(described_class.new(:name => 'yay', :ensure => new))\n          catalog.apply\n\n          logs = catalog.apply.report.logs\n          if status\n            expect(logs.first.source).to eq(\"/Package[yay]/ensure\")\n            expect(logs.first.message).to eq(status)\n          else\n            expect(logs.first).to be_nil\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/type/tidy_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\nrequire 'puppet/file_bucket/dipper'\n\ndescribe Puppet::Type.type(:tidy) do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  before do\n    allow(Puppet::Util::Storage).to receive(:store)\n  end\n\n  it \"should be able to recursively remove directories\" do\n    dir = tmpfile(\"tidy_testing\")\n    FileUtils.mkdir_p(File.join(dir, \"foo\", \"bar\"))\n\n    apply_compiled_manifest(<<-MANIFEST)\n      tidy { '#{dir}':\n        recurse => true,\n        rmdirs  => true,\n      }\n    MANIFEST\n\n    expect(Puppet::FileSystem.directory?(dir)).to be_falsey\n  end\n\n  # Testing #355.\n  it \"should be able to remove dead links\", :if => Puppet.features.manages_symlinks? do\n    dir = tmpfile(\"tidy_link_testing\")\n    link = File.join(dir, \"link\")\n    target = tmpfile(\"no_such_file_tidy_link_testing\")\n    Dir.mkdir(dir)\n    Puppet::FileSystem.symlink(target, link)\n\n    apply_compiled_manifest(<<-MANIFEST)\n      tidy { '#{dir}':\n        recurse => true,\n      }\n    MANIFEST\n\n    expect(Puppet::FileSystem.symlink?(link)).to be_falsey\n  end\nend\n"
  },
  {
    "path": "spec/integration/type_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/type'\n\ndescribe Puppet::Type do\n  it \"should not lose its provider list when it is reloaded\" do\n    type = Puppet::Type.newtype(:integration_test) do\n      newparam(:name) {}\n    end\n\n    provider = type.provide(:myprovider) {}\n\n    # reload it\n    type = Puppet::Type.newtype(:integration_test) do\n      newparam(:name) {}\n    end\n\n    expect(type.provider(:myprovider)).to equal(provider)\n  end\n\n  it \"should not lose its provider parameter when it is reloaded\" do\n    type = Puppet::Type.newtype(:reload_test_type)\n    type.provide(:test_provider)\n\n    # reload it\n    type = Puppet::Type.newtype(:reload_test_type)\n\n    expect(type.parameters).to include(:provider)\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/autoload_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/autoload'\nrequire 'fileutils'\n\nclass AutoloadIntegrator\n  @things = []\n  def self.newthing(name)\n    @things << name\n  end\n\n  def self.thing?(name)\n    @things.include? name\n  end\n\n  def self.clear\n    @things.clear\n  end\nend\n\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Util::Autoload do\n  include PuppetSpec::Files\n\n  let(:env) { Puppet::Node::Environment.create(:foo, []) }\n\n  def with_file(name, *path)\n    path = File.join(*path)\n    # Now create a file to load\n    File.open(path, \"w\") { |f|\n      f.puts \"\\nAutoloadIntegrator.newthing(:#{name.to_s})\\n\"\n      }\n    yield\n    File.delete(path)\n  end\n\n  def with_loader(name, path)\n    dir = tmpfile(name + path)\n    $LOAD_PATH << dir\n    Dir.mkdir(dir)\n    rbdir = File.join(dir, path.to_s)\n    Dir.mkdir(rbdir)\n    loader = Puppet::Util::Autoload.new(name, path)\n    yield rbdir, loader\n    Dir.rmdir(rbdir)\n    Dir.rmdir(dir)\n    $LOAD_PATH.pop\n    AutoloadIntegrator.clear\n  end\n\n  it \"should not fail when asked to load a missing file\" do\n    expect(Puppet::Util::Autoload.new(\"foo\", \"bar\").load(:eh, env)).to be_falsey\n  end\n\n  it \"should load and return true when it successfully loads a file\" do\n    with_loader(\"foo\", \"bar\") { |dir,loader|\n      with_file(:mything, dir, \"mything.rb\") {\n        expect(loader.load(:mything, env)).to be_truthy\n        expect(loader.class).to be_loaded(\"bar/mything\")\n        expect(AutoloadIntegrator).to be_thing(:mything)\n      }\n    }\n  end\n\n  it \"should consider a file loaded when asked for the name without an extension\" do\n    with_loader(\"foo\", \"bar\") { |dir,loader|\n      with_file(:noext, dir, \"noext.rb\") {\n        loader.load(:noext, env)\n        expect(loader.class).to be_loaded(\"bar/noext\")\n      }\n    }\n  end\n\n  it \"should consider a file loaded when asked for the name with an extension\" do\n    with_loader(\"foo\", \"bar\") { |dir,loader|\n      with_file(:noext, dir, \"withext.rb\") {\n        loader.load(:withext, env)\n        expect(loader.class).to be_loaded(\"bar/withext.rb\")\n      }\n    }\n  end\n\n  it \"should be able to load files directly from modules\" do\n    ## modulepath can't be used until after app settings are initialized, so we need to simulate that:\n    expect(Puppet.settings).to receive(:app_defaults_initialized?).and_return(true).at_least(:once)\n\n    modulepath = tmpfile(\"autoload_module_testing\")\n    libdir = File.join(modulepath, \"mymod\", \"lib\", \"foo\")\n    FileUtils.mkdir_p(libdir)\n\n    file = File.join(libdir, \"plugin.rb\")\n\n    env = Puppet::Node::Environment.create(:production, [modulepath])\n    Puppet.override(:environments => Puppet::Environments::Static.new(env)) do\n      with_loader(\"foo\", \"foo\") do |dir, loader|\n        with_file(:plugin, file.split(\"/\")) do\n          loader.load(:plugin, env)\n          expect(loader.class).to be_loaded(\"foo/plugin.rb\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/execution_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::Execution, unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  describe \"#execpipe\" do\n    it \"should set LANG to C avoid localized output\", :if => !Puppet::Util::Platform.windows? do\n      out = \"\"\n      Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp }\n      expect(out).to eq(\"C\")\n    end\n\n    it \"should set LC_ALL to C avoid localized output\", :if => !Puppet::Util::Platform.windows? do\n      out = \"\"\n      Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp }\n      expect(out).to eq(\"C\")\n    end\n\n    it \"should raise an ExecutionFailure with a missing command and :failonfail set to true\" do\n      expect {\n        failonfail = true\n        # NOTE: critical to return l in the block for `output` in method to be #<IO:(closed)>\n        Puppet::Util::Execution.execpipe('conan_the_librarion', failonfail) { |l| l }\n      }.to raise_error(Puppet::ExecutionFailure)\n    end\n  end\n\n  describe \"#execute\" do\n    if Puppet::Util::Platform.windows?\n      let(:argv) { [\"cmd\", \"/c\", \"echo\", 123] }\n    else\n      let(:argv) { [\"echo\", 123] }\n    end\n\n    it 'stringifies sensitive arguments when given an array containing integers' do\n      result = Puppet::Util::Execution.execute(argv, sensitive: true)\n      expect(result.to_s.strip).to eq(\"123\")\n      expect(result.exitstatus).to eq(0)\n    end\n\n    it 'redacts sensitive arguments when given an array' do\n      Puppet[:log_level] = :debug\n      Puppet::Util::Execution.execute(argv, sensitive: true)\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\"))\n    end\n\n    it 'redacts sensitive arguments when given a string' do\n      Puppet[:log_level] = :debug\n      str = argv.map(&:to_s).join(' ')\n      Puppet::Util::Execution.execute(str, sensitive: true)\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\"))\n    end\n\n    it \"allows stdout and stderr to share a file\" do\n      command = \"ruby -e '(1..10).each {|i| (i%2==0) ? $stdout.puts(i) : $stderr.puts(i)}'\"\n\n      expect(Puppet::Util::Execution.execute(command, :combine => true).split).to match_array([*'1'..'10'])\n    end\n\n    it \"returns output and set $CHILD_STATUS\" do\n      command = \"ruby -e 'puts \\\"foo\\\"; exit 42'\"\n\n      output = Puppet::Util::Execution.execute(command, {:failonfail => false})\n\n      expect(output).to eq(\"foo\\n\")\n      expect($CHILD_STATUS.exitstatus).to eq(42)\n    end\n\n    it \"raises an error if non-zero exit status is returned\" do\n      command = \"ruby -e 'exit 43'\"\n\n      expect { Puppet::Util::Execution.execute(command) }.to raise_error(Puppet::ExecutionFailure, /Execution of '#{command}' returned 43: /)\n      expect($CHILD_STATUS.exitstatus).to eq(43)\n    end\n  end\n\n  describe \"#execute (non-Windows)\", :if => !Puppet::Util::Platform.windows? do\n    it \"should execute basic shell command\" do\n      result = Puppet::Util::Execution.execute(\"ls /tmp\", :failonfail => true)\n      expect(result.exitstatus).to eq(0)\n      expect(result.to_s).to_not be_nil\n    end\n  end\n\n  describe \"#execute (Windows)\", :if => Puppet::Util::Platform.windows? do\n    let(:utf8text) do\n      # Japanese Lorem Ipsum snippet\n      \"utf8testfile\" + [227, 131, 171, 227, 131, 147, 227, 131, 179, 227, 131, 132, 227,\n                        130, 162, 227, 130, 166, 227, 130, 167, 227, 131, 150, 227, 130,\n                        162, 227, 129, 181, 227, 129, 185, 227, 129, 139, 227, 130, 137,\n                        227, 129, 154, 227, 130, 187, 227, 130, 183, 227, 131, 147, 227,\n                        131, 170, 227, 131, 134].pack('c*').force_encoding(Encoding::UTF_8)\n    end\n    let(:temputf8filename) do\n      script_containing(utf8text, :windows => \"@ECHO OFF\\r\\nECHO #{utf8text}\\r\\nEXIT 100\")\n    end\n\n    it \"should execute with non-english characters in command line\" do\n      result = Puppet::Util::Execution.execute(\"cmd /c \\\"#{temputf8filename}\\\"\", :failonfail => false)\n      expect(temputf8filename.encoding.name).to eq('UTF-8')\n      expect(result.exitstatus).to eq(100)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/rdoc/parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/rdoc'\n\ndescribe \"RDoc::Parser\", :unless => Puppet::Util::Platform.windows? do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  let(:document_all) { false }\n  let(:tmp_dir) { tmpdir('rdoc_parser_tmp') }\n  let(:doc_dir) { File.join(tmp_dir, 'doc') }\n  let(:manifests_dir) { File.join(tmp_dir, 'manifests') }\n  let(:modules_dir) { File.join(tmp_dir, 'modules') }\n\n  let(:modules_and_manifests) do\n    {\n      :site => [\n        File.join(manifests_dir, 'site.pp'),\n        <<-EOF\n# The test class comment\nclass test {\n  # The virtual resource comment\n  @notify { virtual: }\n  # The a_notify_resource comment\n  notify { a_notify_resource:\n    message => \"a_notify_resource message\"\n  }\n}\n\n# The includes_another class comment\nclass includes_another {\n  include another\n}\n\n# The requires_another class comment\nclass requires_another {\n  require another\n}\n\n# node comment\nnode foo {\n  include test\n  $a_var = \"var_value\"\n  realize Notify[virtual]\n  notify { bar: }\n}\n        EOF\n      ],\n      :module_readme => [\n        File.join(modules_dir, 'a_module', 'README'),\n        <<-EOF\nThe a_module README docs.\n        EOF\n      ],\n      :module_init => [\n        File.join(modules_dir, 'a_module', 'manifests', 'init.pp'),\n        <<-EOF\n# The a_module class comment\nclass a_module {}\n\nclass another {}\n        EOF\n      ],\n      :module_type => [\n        File.join(modules_dir, 'a_module', 'manifests', 'a_type.pp'),\n        <<-EOF\n# The a_type type comment\ndefine a_module::a_type() {}\n        EOF\n      ],\n      :module_plugin => [\n        File.join(modules_dir, 'a_module', 'lib', 'puppet', 'type', 'a_plugin.rb'),\n        <<-EOF\n# The a_plugin type comment\nPuppet::Type.newtype(:a_plugin) do\n  @doc = \"Not presented\"\nend\n        EOF\n      ],\n      :module_function => [\n        File.join(modules_dir, 'a_module', 'lib', 'puppet', 'parser', 'a_function.rb'),\n        <<-EOF\n# The a_function function comment\nmodule Puppet::Parser::Functions\n  newfunction(:a_function, :type => :rvalue) do\n    return\n  end\nend\n        EOF\n      ],\n      :module_fact => [\n        File.join(modules_dir, 'a_module', 'lib', 'facter', 'a_fact.rb'),\n        <<-EOF\n# The a_fact fact comment\nPuppet.runtime[:facter].add(\"a_fact\") do\nend\n        EOF\n      ],\n    }\n  end\n\n  def write_file(file, content)\n    FileUtils.mkdir_p(File.dirname(file))\n    File.open(file, 'w') do |f|\n      f.puts(content)\n    end\n  end\n\n  def prepare_manifests_and_modules\n    modules_and_manifests.each do |key,array|\n      write_file(*array)\n    end\n  end\n\n  def file_exists_and_matches_content(file, *content_patterns)\n    expect(Puppet::FileSystem.exist?(file)).to(be_truthy, \"Cannot find #{file}\")\n    content_patterns.each do |pattern|\n      content = File.read(file)\n      expect(content).to match(pattern)\n    end\n  end\n\n  def some_file_exists_with_matching_content(glob, *content_patterns)\n    expect(Dir.glob(glob).select do |f|\n      contents = File.read(f)\n      content_patterns.all? { |p| p.match(contents) }\n    end).not_to(be_empty, \"Could not match #{content_patterns} in any of the files found in #{glob}\")\n  end\n\n  around(:each) do |example|\n    env = Puppet::Node::Environment.create(:doc_test_env, [modules_dir], manifests_dir)\n    Puppet.override({:environments => Puppet::Environments::Static.new(env), :current_environment => env}) do\n      example.run\n    end\n  end\n\n  before :each do\n    prepare_manifests_and_modules\n    Puppet.settings[:document_all] = document_all\n    Puppet.settings[:modulepath] = modules_dir\n    Puppet::Util::RDoc.rdoc(doc_dir, [modules_dir, manifests_dir])\n  end\n\n  describe \"rdoc2 support\" do\n    def module_path(module_name)\n      \"#{doc_dir}/#{module_name}.html\"\n    end\n\n    def plugin_path(module_name, type, name)\n      \"#{doc_dir}/#{module_name}/__#{type}s__.html\"\n    end\n\n    def has_plugin_rdoc(module_name, type, name)\n      file_exists_and_matches_content(\n        plugin_path(module_name, type, name),\n        /The .*?#{name}.*?\\s*#{type} comment/m, /Type.*?#{type}/m\n      )\n    end\n\n    it \"documents the a_module::a_plugin type\" do\n      has_plugin_rdoc(\"a_module\", :type, 'a_plugin')\n    end\n\n    it \"documents the a_module::a_function function\" do\n      has_plugin_rdoc(\"a_module\", :function, 'a_function')\n    end\n\n    it \"documents the a_module::a_fact fact\" do\n      has_plugin_rdoc(\"a_module\", :fact, 'a_fact')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/settings_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Settings do\n  include PuppetSpec::Files\n\n  def minimal_default_settings\n    { :noop => {:default => false, :desc => \"noop\"} }\n  end\n\n  def define_settings(section, settings_hash)\n    settings.define_settings(section, minimal_default_settings.update(settings_hash))\n  end\n\n  let(:settings) { Puppet::Settings.new }\n\n  it \"should be able to make needed directories\" do\n    define_settings(:main,\n      :maindir => {\n          :default => tmpfile(\"main\"),\n          :type => :directory,\n          :desc => \"a\",\n      }\n    )\n    settings.use(:main)\n\n    expect(File.directory?(settings[:maindir])).to be_truthy\n  end\n\n  it \"should make its directories with the correct modes\" do\n    Puppet[:manage_internal_file_permissions] = true\n    define_settings(:main,\n        :maindir => {\n            :default => tmpfile(\"main\"),\n            :type => :directory,\n            :desc => \"a\",\n            :mode => 0750\n        }\n    )\n\n    settings.use(:main)\n\n    expect(Puppet::FileSystem.stat(settings[:maindir]).mode & 007777).to eq(0750)\n  end\n\n  it \"will properly parse a UTF-8 configuration file\" do\n    rune_utf8 = \"\\u16A0\\u16C7\\u16BB\" # ᚠᛇᚻ\n    config = tmpfile(\"config\")\n    define_settings(:main,\n      :config => {\n        :type => :file,\n        :default => config,\n        :desc => \"a\"\n      },\n      :environment => {\n        :default => 'dingos',\n        :desc => 'test',\n      }\n    )\n\n    File.open(config, 'w') do |file|\n      file.puts <<-EOF\n[main]\nenvironment=#{rune_utf8}\n      EOF\n    end\n\n    settings.initialize_global_settings\n    expect(settings[:environment]).to eq(rune_utf8)\n  end\n\n  it \"reparses configuration if configuration file is touched\", :if => !Puppet::Util::Platform.windows? do\n    config = tmpfile(\"config\")\n    define_settings(:main,\n      :config => {\n        :type => :file,\n        :default => config,\n        :desc => \"a\"\n      },\n      :environment => {\n        :default => 'dingos',\n        :desc => 'test',\n      }\n    )\n\n    Puppet[:filetimeout] = '1s'\n\n    File.open(config, 'w') do |file|\n      file.puts <<-EOF\n[main]\nenvironment=toast\n      EOF\n    end\n\n    settings.initialize_global_settings\n    expect(settings[:environment]).to eq('toast')\n\n    # First reparse establishes WatchedFiles\n    settings.reparse_config_files\n\n    sleep 1\n\n    File.open(config, 'w') do |file|\n      file.puts <<-EOF\n[main]\nenvironment=bacon\n      EOF\n    end\n\n    # Second reparse if later than filetimeout, reparses if changed\n    settings.reparse_config_files\n    expect(settings[:environment]).to eq('bacon')\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/windows/adsi_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe Puppet::Util::Windows::ADSI::User,\n  :if => Puppet::Util::Platform.windows? do\n\n  describe \".initialize\" do\n    it \"cannot reference BUILTIN accounts like SYSTEM due to WinNT moniker limitations\" do\n      system = Puppet::Util::Windows::ADSI::User.new('SYSTEM')\n      # trying to retrieve COM object should fail to load with a localized version of:\n      # ADSI connection error: failed to parse display name of moniker `WinNT://./SYSTEM,user'\n      #     HRESULT error code:0x800708ad\n      #           The user name could not be found.\n      # Matching on error code alone is sufficient\n      expect { system.native_object }.to raise_error(/0x800708ad/)\n    end\n  end\n\n  describe '.each' do\n    it 'should return a list of users with UTF-8 names' do\n      begin\n        original_codepage = Encoding.default_external\n        Encoding.default_external = Encoding::CP850 # Western Europe\n\n        Puppet::Util::Windows::ADSI::User.each do |user|\n          expect(user.name.encoding).to be(Encoding::UTF_8)\n        end\n      ensure\n        Encoding.default_external = original_codepage\n      end\n    end\n  end\n\n  describe '.[]' do\n    it 'should return string attributes as UTF-8' do\n      user = Puppet::Util::Windows::ADSI::User.new('Guest')\n      expect(user['Description'].encoding).to eq(Encoding::UTF_8)\n    end\n  end\n\n  describe '.groups' do\n    it 'should return a list of groups with UTF-8 names' do\n      begin\n        original_codepage = Encoding.default_external\n        Encoding.default_external = Encoding::CP850 # Western Europe\n\n\n        # lookup by English name Administrator is OK on localized Windows\n        administrator = Puppet::Util::Windows::ADSI::User.new('Administrator')\n        administrator.groups.each do |name|\n          expect(name.encoding).to be(Encoding::UTF_8)\n        end\n      ensure\n        Encoding.default_external = original_codepage\n      end\n    end\n  end\n\n  describe '.current_user_name_with_format' do\n    context 'when desired format is NameSamCompatible' do\n      it 'should get the same user name as the current_user_name method but fully qualified' do\n        user_name = Puppet::Util::Windows::ADSI::User.current_user_name\n        fully_qualified_user_name = Puppet::Util::Windows::ADSI::User.current_sam_compatible_user_name\n\n        expect(fully_qualified_user_name).to match(/^.+\\\\#{user_name}$/)\n      end\n\n      it 'should have the same SID as with the current_user_name method' do\n        user_name = Puppet::Util::Windows::ADSI::User.current_user_name\n        fully_qualified_user_name = Puppet::Util::Windows::ADSI::User.current_sam_compatible_user_name\n\n        expect(Puppet::Util::Windows::SID.name_to_sid(user_name)).to eq(Puppet::Util::Windows::SID.name_to_sid(fully_qualified_user_name))\n      end\n    end\n  end\nend\n\ndescribe Puppet::Util::Windows::ADSI::Group,\n  :if => Puppet::Util::Platform.windows? do\n\n  let (:administrator_bytes) { [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0] }\n  let (:administrators_principal) { Puppet::Util::Windows::SID::Principal.lookup_account_sid(administrator_bytes) }\n\n  describe '.each' do\n    it 'should return a list of groups with UTF-8 names' do\n      begin\n        original_codepage = Encoding.default_external\n        Encoding.default_external = Encoding::CP850 # Western Europe\n\n        Puppet::Util::Windows::ADSI::Group.each do |group|\n          expect(group.name.encoding).to be(Encoding::UTF_8)\n        end\n      ensure\n        Encoding.default_external = original_codepage\n      end\n    end\n  end\n\n  describe '.members' do\n    it 'should return a list of members resolvable with Puppet::Util::Windows::ADSI::Group.name_sid_hash' do\n      temp_groupname = \"g#{SecureRandom.uuid}\"\n      temp_username  = \"u#{SecureRandom.uuid}\"[0..12]\n      # From https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/password-must-meet-complexity-requirements\n      specials = \"~!@#$%^&*_-+=`|\\(){}[]:;\\\"'<>,.?/\"\n      temp_password = \"p#{SecureRandom.uuid[0..7]}-#{SecureRandom.uuid.upcase[0..7]}-#{specials[rand(specials.length)]}\"\n\n      # select a virtual account that requires an authority to be able to resolve to SID\n      # the Dhcp service is chosen for no particular reason aside from it's a service available on all Windows versions\n      dhcp_virtualaccount = Puppet::Util::Windows::SID.name_to_principal('NT SERVICE\\Dhcp')\n\n      # adding :SidTypeGroup as a group member will cause error in IAdsUser::Add\n      # adding :SidTypeDomain (such as S-1-5-80 / NT SERVICE or computer name) won't error\n      #   but also won't be returned as a group member\n      # uncertain how to obtain :SidTypeComputer (perhaps AD? the local machine is :SidTypeDomain)\n      users = [\n        # Use sid_to_name to get localized names of SIDs - BUILTIN, SYSTEM, NT AUTHORITY, Everyone are all localized\n        # :SidTypeWellKnownGroup\n        # SYSTEM is prefixed with the NT Authority authority, resolveable with or without authority\n        { :sid => 'S-1-5-18', :name => Puppet::Util::Windows::SID.sid_to_name('S-1-5-18') },\n        # Everyone is not prefixed with an authority, resolveable with or without NT AUTHORITY authority\n        { :sid => 'S-1-1-0', :name => Puppet::Util::Windows::SID.sid_to_name('S-1-1-0') },\n        # Dhcp service account is prefixed with NT SERVICE authority, requires authority to resolve SID\n        # behavior is similar to IIS APPPOOL\\DefaultAppPool\n        { :sid => dhcp_virtualaccount.sid, :name => dhcp_virtualaccount.domain_account },\n\n        # :SidTypeAlias with authority component\n        # Administrators group is prefixed with BUILTIN authority, can be resolved with or without authority\n        { :sid => 'S-1-5-32-544', :name => Puppet::Util::Windows::SID.sid_to_name('S-1-5-32-544') },\n      ]\n\n      begin\n        # :SidTypeUser as user on localhost, can be resolved with or without authority prefix\n        user = Puppet::Util::Windows::ADSI::User.create(temp_username)\n        # appveyor sometimes requires a password\n        user.password = temp_password\n        user.commit()\n        users.push({ :sid => user.sid.sid, :name => Puppet::Util::Windows::ADSI.computer_name + '\\\\' + temp_username })\n\n        # create a test group and add above 5 members by SID\n        group = described_class.create(temp_groupname)\n        group.commit()\n        group.set_members(users.map { |u| u[:sid]} )\n\n        # most importantly make sure that all name are convertible to SIDs\n        expect { described_class.name_sid_hash(group.members) }.to_not raise_error\n\n        # also verify the names returned are as expected\n        expected_usernames = users.map { |u| u[:name] }\n        expect(group.members.map(&:domain_account)).to eq(expected_usernames)\n      ensure\n        described_class.delete(temp_groupname) if described_class.exists?(temp_groupname)\n        Puppet::Util::Windows::ADSI::User.delete(temp_username) if Puppet::Util::Windows::ADSI::User.exists?(temp_username)\n      end\n    end\n\n    it 'should return a list of Principal objects even with unresolvable SIDs' do\n      members = [\n        # NULL SID is not localized\n        double('WIN32OLE', {\n          :objectSID => [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n          :Name => 'NULL SID',\n          :ole_respond_to? => true,\n        }),\n        # unresolvable SID is a different story altogether\n        double('WIN32OLE', {\n          # completely valid SID, but Name is just a stringified version\n          :objectSID => [1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 5, 113, 65, 218, 15, 127, 9, 57, 219, 4, 84, 126, 88, 4, 0, 0],\n          :Name => 'S-1-5-21-3661721861-956923663-2119435483-1112',\n          :ole_respond_to? => true,\n        })\n      ]\n\n      admins_name = Puppet::Util::Windows::SID.sid_to_name('S-1-5-32-544')\n      admins = Puppet::Util::Windows::ADSI::Group.new(admins_name)\n\n      # touch the native_object member to have it lazily loaded, so COM objects can be stubbed\n      admins.native_object\n      without_partial_double_verification do\n        allow(admins.native_object).to receive(:Members).and_return(members)\n      end\n\n      # well-known NULL SID\n      expect(admins.members[0].sid).to eq('S-1-0-0')\n      expect(admins.members[0].account_type).to eq(:SidTypeWellKnownGroup)\n\n      # unresolvable SID\n      expect(admins.members[1].sid).to eq('S-1-5-21-3661721861-956923663-2119435483-1112')\n      expect(admins.members[1].account).to eq('S-1-5-21-3661721861-956923663-2119435483-1112')\n      expect(admins.members[1].account_type).to eq(:SidTypeUnknown)\n    end\n\n    it 'should return a list of members with UTF-8 names' do\n      begin\n        original_codepage = Encoding.default_external\n        Encoding.default_external = Encoding::CP850 # Western Europe\n\n        # lookup by English name Administrators is not OK on localized Windows\n        admins = Puppet::Util::Windows::ADSI::Group.new(administrators_principal.account)\n        admins.members.map(&:domain_account).each do |name|\n          expect(name.encoding).to be(Encoding::UTF_8)\n        end\n      ensure\n        Encoding.default_external = original_codepage\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/windows/monkey_patches/process_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'Process', if: Puppet::Util::Platform.windows? do\n  describe '.create' do\n    context 'with common flags' do\n      it do\n        Process.create(\n          app_name: 'cmd.exe /c echo 123',\n          creation_flags: 0x00000008,\n          process_inherit: false,\n          thread_inherit: false,\n          cwd: 'C:\\\\'\n        )\n      end\n\n      context 'when FFI call fails' do\n        before do\n          allow(Process).to receive(:CreateProcessW).and_return(false)\n        end\n\n        it 'raises SystemCallError' do\n          expect do\n            Process.create(\n              app_name: 'cmd.exe /c echo 123',\n              creation_flags: 0x00000008\n            )\n          end.to raise_error(SystemCallError)\n        end\n      end\n    end\n\n    context 'with logon' do\n      context 'without password' do\n        it 'raises error' do\n          expect do\n            Process.create(\n              app_name: 'cmd.exe /c echo 123',\n              creation_flags: 0x00000008,\n              with_logon: 'test'\n            )\n          end.to raise_error(ArgumentError, 'password must be specified if with_logon is used')\n        end\n      end\n\n      context 'with common flags' do\n        before do\n          allow(Process).to receive(:CreateProcessWithLogonW).and_return(true)\n        end\n\n        it do\n          Process.create(\n            app_name: 'cmd.exe /c echo 123',\n            creation_flags: 0x00000008,\n            process_inherit: false,\n            thread_inherit: false,\n            with_logon: 'test',\n            password: 'password',\n            cwd: 'C:\\\\'\n          )\n        end\n\n        context 'when FFI call fails' do\n          before do\n            allow(Process).to receive(:CreateProcessWithLogonW).and_return(false)\n          end\n\n          it 'raises SystemCallError' do\n            expect do\n              Process.create(\n                app_name: 'cmd.exe /c echo 123',\n                creation_flags: 0x00000008,\n                with_logon: 'test',\n                password: 'password'\n              )\n            end.to raise_error(SystemCallError)\n          end\n        end\n      end\n    end\n\n    describe 'validations' do\n      context 'when args is not a hash' do\n        it 'raises TypeError' do\n          expect do\n            Process.create('test')\n          end.to raise_error(TypeError, 'hash keyword arguments expected')\n        end\n      end\n\n      context 'when args key is invalid' do\n        it 'raises ArgumentError' do\n          expect do\n            Process.create(invalid_key: 'test')\n          end.to raise_error(ArgumentError, \"invalid key 'invalid_key'\")\n        end\n      end\n\n      context 'when startup_info is invalid' do\n        it 'raises ArgumentError' do\n          expect do\n            Process.create(startup_info: { invalid_key: 'test' })\n          end.to raise_error(ArgumentError, \"invalid startup_info key 'invalid_key'\")\n        end\n      end\n\n      context 'when app_name and command_line are missing' do\n        it 'raises ArgumentError' do\n          expect do\n            Process.create(creation_flags: 0)\n          end.to raise_error(ArgumentError, 'command_line or app_name must be specified')\n        end\n      end\n\n      context 'when executable is not found' do\n        it 'raises Errno::ENOENT' do\n          expect do\n            Process.create(app_name: 'non_existent')\n          end.to raise_error(Errno::ENOENT)\n        end\n      end\n    end\n\n    context 'when environment is not specified' do\n      it 'passes local environment' do\n        stdout_read, stdout_write = IO.pipe\n        ENV['TEST_ENV'] = 'B'\n\n        Process.create(\n          app_name: 'cmd.exe /c echo %TEST_ENV%',\n          creation_flags: 0x00000008,\n          startup_info: { stdout: stdout_write }\n        )\n\n        stdout_write.close\n        expect(stdout_read.read.chomp).to eql('B')\n      end\n    end\n\n    context 'when environment is specified' do\n      it 'does not pass local environment' do\n        stdout_read, stdout_write = IO.pipe\n        ENV['TEST_ENV'] = 'B'\n\n        Process.create(\n          app_name: 'cmd.exe /c echo %TEST_ENV%',\n          creation_flags: 0x00000008,\n          environment: '',\n          startup_info: { stdout: stdout_write }\n        )\n\n        stdout_write.close\n        expect(stdout_read.read.chomp).to eql('%TEST_ENV%')\n      end\n\n      it 'supports :environment as a string' do\n        stdout_read, stdout_write = IO.pipe\n\n        Process.create(\n          app_name: 'cmd.exe /c echo %A% %B%',\n          creation_flags: 0x00000008,\n          environment: 'A=C;B=D',\n          startup_info: { stdout: stdout_write }\n        )\n\n        stdout_write.close\n        expect(stdout_read.read.chomp).to eql('C D')\n      end\n\n      it 'supports :environment as a string' do\n        stdout_read, stdout_write = IO.pipe\n\n        Process.create(\n          app_name: 'cmd.exe /c echo %A% %C%',\n          creation_flags: 0x00000008,\n          environment: ['A=B;X;', 'C=;D;Y'],\n          startup_info: { stdout: stdout_write }\n        )\n\n        stdout_write.close\n        expect(stdout_read.read.chomp).to eql('B;X; ;D;Y')\n      end\n    end\n  end\n\n  describe '.setpriority' do\n    let(:priority) { Process::BELOW_NORMAL_PRIORITY_CLASS }\n\n    context 'when success' do\n      it 'returns 0' do\n        expect(Process.setpriority(0, Process.pid, priority)).to eql(0)\n      end\n\n      it 'treats an int argument of zero as the current process' do\n        expect(Process.setpriority(0, 0, priority)).to eql(0)\n      end\n    end\n\n    context 'when invalid arguments are sent' do\n      it 'raises TypeError' do\n        expect {\n          Process.setpriority('test', 0, priority)\n        }.to raise_error(TypeError)\n      end\n    end\n\n    context 'when process is not found' do\n      before do\n        allow(Process).to receive(:OpenProcess).and_return(0)\n      end\n      it 'raises SystemCallError' do\n        expect {\n          Process.setpriority(0, 0, priority)\n        }.to raise_error(SystemCallError)\n      end\n    end\n\n    context 'when priority is not set' do\n      before do\n        allow(Process).to receive(:SetPriorityClass).and_return(false)\n      end\n\n      it 'raises SystemCallError' do\n        expect {\n          Process.setpriority(0, 0, priority)\n        }.to raise_error(SystemCallError)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/windows/principal_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe Puppet::Util::Windows::SID::Principal, :if => Puppet::Util::Platform.windows? do\n\n  let (:current_user_sid) { Puppet::Util::Windows::ADSI::User.current_user_sid }\n  let (:system_bytes) { [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0] }\n  let (:null_sid_bytes) { [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }\n  let (:administrator_bytes) { [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0] }\n  let (:all_application_packages_bytes) { [1, 2, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0, 1, 0, 0, 0] }\n  let (:computer_sid) { Puppet::Util::Windows::SID.name_to_principal(Puppet::Util::Windows::ADSI.computer_name) }\n  # BUILTIN is localized on German Windows, but not French\n  # looking this up like this dilutes the values of the tests as we're comparing two mechanisms\n  # for returning the same values, rather than to a known good\n  let (:builtin_localized) { Puppet::Util::Windows::SID.sid_to_name('S-1-5-32') }\n\n  describe \".lookup_account_name\" do\n    it \"should create an instance from a well-known account name\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name('NULL SID')\n      expect(principal.account).to eq('NULL SID')\n      expect(principal.sid_bytes).to eq(null_sid_bytes)\n      expect(principal.sid).to eq('S-1-0-0')\n      expect(principal.domain).to eq('')\n      expect(principal.domain_account).to eq('NULL SID')\n      expect(principal.account_type).to eq(:SidTypeWellKnownGroup)\n      expect(principal.to_s).to eq('NULL SID')\n    end\n\n    it \"should create an instance from a well-known account prefixed with NT AUTHORITY\" do\n      # a special case that can be used to lookup an account on a localized Windows\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name('NT AUTHORITY\\\\SYSTEM')\n      expect(principal.sid_bytes).to eq(system_bytes)\n      expect(principal.sid).to eq('S-1-5-18')\n\n      # guard these 3 checks on a US Windows with 1033 - primary language id of 9\n      primary_language_id = 9\n      # even though lookup in English, returned values may be localized\n      # French Windows returns AUTORITE NT\\\\Syst\\u00E8me, German Windows returns NT-AUTORIT\\u00C4T\\\\SYSTEM\n      if (Puppet::Util::Windows::Process.get_system_default_ui_language & primary_language_id == primary_language_id)\n        expect(principal.account).to eq('SYSTEM')\n        expect(principal.domain).to eq('NT AUTHORITY')\n        expect(principal.domain_account).to eq('NT AUTHORITY\\\\SYSTEM')\n        expect(principal.to_s).to eq('NT AUTHORITY\\\\SYSTEM')\n      end\n\n      # Windows API LookupAccountSid behaves differently if current user is SYSTEM\n      if current_user_sid.sid_bytes != system_bytes\n        account_type = :SidTypeWellKnownGroup\n      else\n        account_type = :SidTypeUser\n      end\n\n      expect(principal.account_type).to eq(account_type)\n    end\n\n    it \"should create an instance from a local account prefixed with hostname\" do\n      running_as_system = (current_user_sid.sid_bytes == system_bytes)\n      username = running_as_system ?\n        # need to return localized name of Administrator account\n        Puppet::Util::Windows::SID.sid_to_name(computer_sid.sid + '-500').split('\\\\').last :\n        current_user_sid.account\n\n      user_exists = Puppet::Util::Windows::ADSI::User.exists?(\".\\\\#{username}\")\n\n      # when running as SYSTEM (in Jenkins CI), then Administrator should be used\n      # otherwise running in AppVeyor there is no Administrator and a the current local user can be used\n      skip if (running_as_system && !user_exists)\n\n      hostname = Puppet::Util::Windows::ADSI.computer_name\n\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name(\"#{hostname}\\\\#{username}\")\n      expect(principal.account).to match(/^#{Regexp.quote(username)}$/i)\n      # skip SID and bytes in this case since the most interesting thing here is domain_account\n      expect(principal.domain).to match(/^#{Regexp.quote(hostname)}$/i)\n      expect(principal.domain_account).to match(/^#{Regexp.quote(hostname)}\\\\#{Regexp.quote(username)}$/i)\n      expect(principal.account_type).to eq(:SidTypeUser)\n    end\n\n    it \"should create an instance from a well-known BUILTIN alias\" do\n      # by looking up the localized name of the account, the test value is diluted\n      # this localizes Administrators AND BUILTIN\n      qualified_name = Puppet::Util::Windows::SID.sid_to_name('S-1-5-32-544')\n      domain, name = qualified_name.split('\\\\')\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name(name)\n\n      expect(principal.account).to eq(name)\n      expect(principal.sid_bytes).to eq(administrator_bytes)\n      expect(principal.sid).to eq('S-1-5-32-544')\n      expect(principal.domain).to eq(domain)\n      expect(principal.domain_account).to eq(qualified_name)\n      expect(principal.account_type).to eq(:SidTypeAlias)\n      expect(principal.to_s).to eq(qualified_name)\n    end\n\n    it \"should raise an error when trying to lookup an account that doesn't exist\" do\n      principal = Puppet::Util::Windows::SID::Principal\n      expect {\n        principal.lookup_account_name('ConanTheBarbarian')\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(1332) # ERROR_NONE_MAPPED\n      end\n    end\n\n    it \"should return a BUILTIN domain principal for empty account names\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name('')\n      expect(principal.account_type).to eq(:SidTypeDomain)\n      expect(principal.sid).to eq('S-1-5-32')\n      expect(principal.account).to eq(builtin_localized)\n      expect(principal.domain).to eq(builtin_localized)\n      expect(principal.domain_account).to eq(builtin_localized)\n      expect(principal.to_s).to eq(builtin_localized)\n    end\n\n    it \"should return a BUILTIN domain principal for BUILTIN account names\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name(builtin_localized)\n      expect(principal.account_type).to eq(:SidTypeDomain)\n      expect(principal.sid).to eq('S-1-5-32')\n      expect(principal.account).to eq(builtin_localized)\n      expect(principal.domain).to eq(builtin_localized)\n      expect(principal.domain_account).to eq(builtin_localized)\n      expect(principal.to_s).to eq(builtin_localized)\n    end\n\n    it \"should always sanitize the account name first\" do\n      expect(Puppet::Util::Windows::SID::Principal).to receive(:sanitize_account_name).with('NT AUTHORITY\\\\SYSTEM').and_call_original\n      Puppet::Util::Windows::SID::Principal.lookup_account_name('NT AUTHORITY\\\\SYSTEM')\n    end\n\n    it \"should be able to create an instance from an account name prefixed by APPLICATION PACKAGE AUTHORITY\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_name('APPLICATION PACKAGE AUTHORITY\\\\ALL APPLICATION PACKAGES')\n      expect(principal.account).to eq('ALL APPLICATION PACKAGES')\n      expect(principal.sid_bytes).to eq(all_application_packages_bytes)\n      expect(principal.sid).to eq('S-1-15-2-1')\n      expect(principal.domain).to eq('APPLICATION PACKAGE AUTHORITY')\n      expect(principal.domain_account).to eq('APPLICATION PACKAGE AUTHORITY\\\\ALL APPLICATION PACKAGES')\n      expect(principal.account_type).to eq(:SidTypeWellKnownGroup)\n      expect(principal.to_s).to eq('APPLICATION PACKAGE AUTHORITY\\\\ALL APPLICATION PACKAGES')\n    end\n\n    it \"should fail without proper account name sanitization when it is prefixed by APPLICATION PACKAGE AUTHORITY\" do\n      given_account_name = 'APPLICATION PACKAGE AUTHORITY\\\\ALL APPLICATION PACKAGES'\n      expect { Puppet::Util::Windows::SID::Principal.lookup_account_name(nil, false, given_account_name) }.to raise_error(Puppet::Util::Windows::Error, /No mapping between account names and security IDs was done./)\n    end\n  end\n\n  describe \".lookup_account_sid\" do\n    it \"should create an instance from a user SID\" do\n      # take the computer account bytes and append the equivalent of -501 for Guest\n      bytes = (computer_sid.sid_bytes + [245, 1, 0, 0])\n      # computer SID bytes start with [1, 4, ...] but need to be [1, 5, ...]\n      bytes[1] = 5\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_sid(bytes)\n      # use the returned SID to lookup localized Guest account name in Windows\n      guest_name = Puppet::Util::Windows::SID.sid_to_name(principal.sid)\n\n      expect(principal.sid_bytes).to eq(bytes)\n      expect(principal.sid).to eq(computer_sid.sid + '-501')\n      expect(principal.account).to eq(guest_name.split('\\\\')[1])\n      expect(principal.domain).to eq(computer_sid.domain)\n      expect(principal.domain_account).to eq(guest_name)\n      expect(principal.account_type).to eq(:SidTypeUser)\n      expect(principal.to_s).to eq(guest_name)\n    end\n\n    it \"should create an instance from a well-known group SID\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_sid(null_sid_bytes)\n      expect(principal.sid_bytes).to eq(null_sid_bytes)\n      expect(principal.sid).to eq('S-1-0-0')\n      expect(principal.account).to eq('NULL SID')\n      expect(principal.domain).to eq('')\n      expect(principal.domain_account).to eq('NULL SID')\n      expect(principal.account_type).to eq(:SidTypeWellKnownGroup)\n      expect(principal.to_s).to eq('NULL SID')\n    end\n\n    it \"should create an instance from a well-known BUILTIN Alias SID\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_sid(administrator_bytes)\n      # by looking up the localized name of the account, the test value is diluted\n      # this localizes Administrators AND BUILTIN\n      qualified_name = Puppet::Util::Windows::SID.sid_to_name('S-1-5-32-544')\n      domain, name = qualified_name.split('\\\\')\n\n      expect(principal.account).to eq(name)\n      expect(principal.sid_bytes).to eq(administrator_bytes)\n      expect(principal.sid).to eq('S-1-5-32-544')\n      expect(principal.domain).to eq(domain)\n      expect(principal.domain_account).to eq(qualified_name)\n      expect(principal.account_type).to eq(:SidTypeAlias)\n      expect(principal.to_s).to eq(qualified_name)\n    end\n\n    it \"should raise an error when trying to lookup nil\" do\n      principal = Puppet::Util::Windows::SID::Principal\n      expect {\n        principal.lookup_account_sid(nil)\n      }.to raise_error(Puppet::Util::Windows::Error, /must not be nil/)\n    end\n\n    it \"should raise an error when trying to lookup non-byte array\" do\n      principal = Puppet::Util::Windows::SID::Principal\n      expect {\n        principal.lookup_account_sid('ConanTheBarbarian')\n      }.to raise_error(Puppet::Util::Windows::Error, /array/)\n    end\n\n    it \"should raise an error when trying to lookup an empty array\" do\n      principal = Puppet::Util::Windows::SID::Principal\n      expect {\n        principal.lookup_account_sid([])\n      }.to raise_error(Puppet::Util::Windows::Error, /at least 1 byte long/)\n    end\n\n    # https://technet.microsoft.com/en-us/library/cc962011.aspx\n    # \"... The structure used in all SIDs created by Windows NT and Windows 2000 is revision level 1. ...\"\n    # Therefore a value of zero for the revision, is not a valid SID\n    it \"should raise an error when trying to lookup completely invalid SID bytes\" do\n      principal = Puppet::Util::Windows::SID::Principal\n      expect {\n        principal.lookup_account_sid([0])\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(87) # ERROR_INVALID_PARAMETER\n      end\n    end\n\n    it \"should raise an error when trying to lookup a valid SID that doesn't have a matching account\" do\n      principal = Puppet::Util::Windows::SID::Principal\n      expect {\n        # S-1-1-1 which is not a valid account\n        principal.lookup_account_sid([1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0])\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(1332) # ERROR_NONE_MAPPED\n      end\n    end\n\n    it \"should return a domain principal for BUILTIN SID S-1-5-32\" do\n      principal = Puppet::Util::Windows::SID::Principal.lookup_account_sid([1, 1, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0])\n      expect(principal.account_type).to eq(:SidTypeDomain)\n      expect(principal.sid).to eq('S-1-5-32')\n      expect(principal.account).to eq(builtin_localized)\n      expect(principal.domain).to eq(builtin_localized)\n      expect(principal.domain_account).to eq(builtin_localized)\n      expect(principal.to_s).to eq(builtin_localized)\n    end\n  end\n\n  describe \"it should create matching Principal objects\" do\n    let(:builtin_sid) { [1, 1, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0] }\n    let(:sid_principal) { Puppet::Util::Windows::SID::Principal.lookup_account_sid(builtin_sid) }\n\n    ['.', ''].each do |name|\n      it \"when comparing the one looked up via SID S-1-5-32 to one looked up via non-canonical name #{name} for the BUILTIN domain\" do\n        name_principal = Puppet::Util::Windows::SID::Principal.lookup_account_name(name)\n\n        # compares canonical sid\n        expect(sid_principal).to eq(name_principal)\n\n        # compare all properties that have public accessors\n        sid_principal.public_methods(false).reject { |m| m == :== }.each do |method|\n          expect(sid_principal.send(method)).to eq(name_principal.send(method))\n        end\n      end\n    end\n\n    it \"when comparing the one looked up via SID S-1-5-32 to one looked up via non-canonical localized name for the BUILTIN domain\" do\n      name_principal = Puppet::Util::Windows::SID::Principal.lookup_account_name(builtin_localized)\n\n      # compares canonical sid\n      expect(sid_principal).to eq(name_principal)\n\n      # compare all properties that have public accessors\n      sid_principal.public_methods(false).reject { |m| m == :== }.each do |method|\n        expect(sid_principal.send(method)).to eq(name_principal.send(method))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/windows/process_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"Puppet::Util::Windows::Process\", :if => Puppet::Util::Platform.windows?  do\n  describe \"as an admin\" do\n    it \"should have the SeCreateSymbolicLinkPrivilege necessary to create symlinks\" do\n      # this is a bit of a lame duck test since it requires running user to be admin\n      # a better integration test would create a new user with the privilege and verify\n      expect(Puppet::Util::Windows::User).to be_admin\n      expect(Puppet::Util::Windows::Process.process_privilege_symlink?).to be_truthy\n    end\n\n    it \"should be able to lookup a standard Windows process privilege\" do\n      Puppet::Util::Windows::Process.lookup_privilege_value('SeShutdownPrivilege') do |luid|\n        expect(luid).not_to be_nil\n        expect(luid).to be_instance_of(Puppet::Util::Windows::Process::LUID)\n      end\n    end\n\n    it \"should raise an error for an unknown privilege name\" do\n      expect { Puppet::Util::Windows::Process.lookup_privilege_value('foo') }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(1313) # ERROR_NO_SUCH_PRIVILEGE\n      end\n    end\n  end\n\n  describe \"when reading environment variables\" do\n    it \"will ignore only keys or values with corrupt byte sequences\" do\n      env_vars = {}\n\n      # Create a UTF-16LE version of the below null separated environment string\n      # \"a=b\\x00c=d\\x00e=\\xDD\\xDD\\x00f=g\\x00\\x00\"\n      env_var_block =\n        \"a=b\\x00\".encode(Encoding::UTF_16LE) +\n        \"c=d\\x00\".encode(Encoding::UTF_16LE) +\n        'e='.encode(Encoding::UTF_16LE) + \"\\xDD\\xDD\".force_encoding(Encoding::UTF_16LE) + \"\\x00\".encode(Encoding::UTF_16LE) +\n        \"f=g\\x00\\x00\".encode(Encoding::UTF_16LE)\n\n      env_var_block_bytes = env_var_block.bytes.to_a\n\n      FFI::MemoryPointer.new(:byte, env_var_block_bytes.count) do |ptr|\n        # uchar here is synonymous with byte\n        ptr.put_array_of_uchar(0, env_var_block_bytes)\n\n        # stub the block of memory that the Win32 API would typically return via pointer\n        allow(Puppet::Util::Windows::Process).to receive(:GetEnvironmentStringsW).and_return(ptr)\n        # stub out the real API call to free memory, else process crashes\n        allow(Puppet::Util::Windows::Process).to receive(:FreeEnvironmentStringsW)\n\n        env_vars = Puppet::Util::Windows::Process.get_environment_strings\n      end\n\n      # based on corrupted memory, the e=\\xDD\\xDD should have been removed from the set\n      expect(env_vars).to eq({'a' => 'b', 'c' => 'd', 'f' => 'g'})\n\n      # and Puppet should emit a warning about it\n      expect(@logs.last.level).to eq(:warning)\n      expect(@logs.last.message).to eq(\"Discarding environment variable e=\\uFFFD which contains invalid bytes\")\n    end\n  end\n\n  describe \"when setting environment variables\" do\n    let(:name) { SecureRandom.uuid }\n\n    around :each do |example|\n      begin\n        example.run\n      ensure\n        Puppet::Util::Windows::Process.set_environment_variable(name, nil)\n      end\n    end\n\n    it \"sets environment variables containing '='\" do\n      value = 'foo=bar'\n      Puppet::Util::Windows::Process.set_environment_variable(name, value)\n      env = Puppet::Util::Windows::Process.get_environment_strings\n\n      expect(env[name]).to eq(value)\n    end\n\n    it \"sets environment variables contains spaces\" do\n      Puppet::Util::Windows::Process.set_environment_variable(name, '')\n      env = Puppet::Util::Windows::Process.get_environment_strings\n\n      expect(env[name]).to eq('')\n    end\n\n    it \"sets environment variables containing UTF-8\" do\n      rune_utf8 = \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\"\n      Puppet::Util::Windows::Process.set_environment_variable(name, rune_utf8)\n      env = Puppet::Util::Windows::Process.get_environment_strings\n\n      expect(env[name]).to eq(rune_utf8)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/windows/registry_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\nif Puppet::Util::Platform.windows?\ndescribe Puppet::Util::Windows::Registry do\n  subject do\n    class TestRegistry\n      include Puppet::Util::Windows::Registry\n      extend FFI::Library\n\n      ffi_lib :advapi32\n      attach_function :RegSetValueExW,\n        [:handle, :pointer, :dword, :dword, :pointer, :dword], :win32_long\n\n      def write_corrupt_dword(reg, valuename)\n        # Normally DWORDs contain 4 bytes.  This bad data only has 2\n        bad_data = [0, 0]\n        FFI::Pointer.from_string_to_wide_string(valuename) do |name_ptr|\n          FFI::MemoryPointer.new(:uchar, bad_data.length) do |data_ptr|\n            data_ptr.write_array_of_uchar(bad_data)\n            if RegSetValueExW(reg.hkey, name_ptr, 0,\n              Win32::Registry::REG_DWORD, data_ptr, data_ptr.size) != 0\n                raise Puppet::Util::Windows::Error.new(\"Failed to write registry value\")\n            end\n          end\n        end\n      end\n    end\n\n    TestRegistry.new\n  end\n\n  let(:name) { 'HKEY_LOCAL_MACHINE' }\n  let(:path) { 'Software\\Microsoft' }\n\n  context \"#root\" do\n    it \"should lookup the root hkey\" do\n      expect(subject.root(name)).to be_instance_of(Win32::Registry::PredefinedKey)\n    end\n\n    it \"should raise for unknown root keys\" do\n      expect { subject.root('HKEY_BOGUS') }.to raise_error(Puppet::Error, /Invalid registry key/)\n    end\n  end\n\n  context \"#open\" do\n    let(:hkey)   { double('hklm') }\n    let(:subkey) { double('subkey') }\n\n    before :each do\n      allow(subject).to receive(:root).and_return(hkey)\n    end\n\n    it \"should yield the opened the subkey\" do\n      expect(hkey).to receive(:open).with(path, anything).and_yield(subkey)\n\n      yielded = nil\n      subject.open(name, path) {|reg| yielded = reg}\n      expect(yielded).to eq(subkey)\n    end\n\n    if Puppet::Util::Platform.windows?\n      [described_class::KEY64, described_class::KEY32].each do |access|\n        it \"should open the key for read access 0x#{access.to_s(16)}\" do\n          mode = described_class::KEY_READ | access\n          expect(hkey).to receive(:open).with(path, mode)\n\n          subject.open(name, path, mode) {|reg| }\n        end\n      end\n    end\n\n    it \"should default to KEY64\" do\n      expect(hkey).to receive(:open).with(path, described_class::KEY_READ | described_class::KEY64)\n\n      subject.open(hkey, path) {|hkey| }\n    end\n\n    it \"should raise for a path that doesn't exist\" do\n      expect(hkey).to receive(:keyname).and_return('HKEY_LOCAL_MACHINE')\n      expect(hkey).to receive(:open).and_raise(Win32::Registry::Error.new(2)) # file not found\n      expect do\n        subject.open(hkey, 'doesnotexist') {|hkey| }\n      end.to raise_error(Puppet::Error, /Failed to open registry key 'HKEY_LOCAL_MACHINE\\\\doesnotexist'/)\n    end\n  end\n\n  context \"#values\" do\n    let(:key) { double('uninstall') }\n\n    it \"should return each value's name and data\" do\n      expect(key).not_to receive(:each_value)\n      expect(subject).to receive(:each_value).with(key).and_yield('string', 1, 'foo').and_yield('dword', 4, 0)\n\n      expect(subject.values(key)).to eq({ 'string' => 'foo', 'dword' => 0 })\n    end\n\n    it \"should return an empty hash if there are no values\" do\n      expect(key).not_to receive(:each_value)\n      expect(subject).to receive(:each_value).with(key)\n\n      expect(subject.values(key)).to eq({})\n    end\n\n    it \"passes REG_DWORD through\" do\n      expect(key).not_to receive(:each_value)\n      expect(subject).to receive(:each_value).with(key).and_yield('dword', Win32::Registry::REG_DWORD, '1')\n\n      value = subject.values(key).first[1]\n\n      expect(Integer(value)).to eq(1)\n    end\n\n    context \"when reading non-ASCII values\" do\n      ENDASH_UTF_8 = [0xE2, 0x80, 0x93]\n      ENDASH_UTF_16 = [0x2013]\n      TM_UTF_8 = [0xE2, 0x84, 0xA2]\n      TM_UTF_16 = [0x2122]\n\n      let(:hklm) { Win32::Registry::HKEY_LOCAL_MACHINE }\n      let(:puppet_key) { \"SOFTWARE\\\\Puppet Labs\"}\n      let(:subkey_name) { \"PuppetRegistryTest#{SecureRandom.uuid}\" }\n      let(:guid) { SecureRandom.uuid }\n      let(:regsam) { Puppet::Util::Windows::Registry::KEY32 }\n\n      after(:each) do\n        # Ruby 2.1.5 has bugs with deleting registry keys due to using ANSI\n        # character APIs, but passing wide strings to them (facepalm)\n        # https://github.com/ruby/ruby/blob/v2_1_5/ext/win32/lib/win32/registry.rb#L323-L329\n        # therefore, use our own built-in registry helper code\n\n        hklm.open(puppet_key, Win32::Registry::KEY_ALL_ACCESS | regsam) do |reg|\n          subject.delete_key(reg, subkey_name, regsam)\n        end\n      end\n\n      # proof that local encodings (such as IBM437 are no longer relevant)\n      it \"will return a UTF-8 string from a REG_SZ registry value (written as UTF-16LE)\",\n        :if => Puppet::Util::Platform.windows? do\n\n        # create a UTF-16LE byte array representing \"–™\"\n        utf_16_bytes = ENDASH_UTF_16 + TM_UTF_16\n        utf_16_str = utf_16_bytes.pack('s*').force_encoding(Encoding::UTF_16LE)\n\n        # and it's UTF-8 equivalent bytes\n        utf_8_bytes = ENDASH_UTF_8 + TM_UTF_8\n        utf_8_str = utf_8_bytes.pack('c*').force_encoding(Encoding::UTF_8)\n\n        hklm.create(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS | regsam) do |reg|\n          reg.write(\"#{guid}\", Win32::Registry::REG_SZ, utf_16_str)\n\n          # trigger Puppet::Util::Windows::Registry FFI calls\n          keys = subject.keys(reg)\n          vals = subject.values(reg)\n\n          expect(keys).to be_empty\n          expect(vals).to have_key(guid)\n\n          # The UTF-16LE string written should come back as the equivalent UTF-8\n          written = vals[guid]\n          expect(written).to eq(utf_8_str)\n          expect(written.encoding).to eq(Encoding::UTF_8)\n        end\n      end\n    end\n\n    context \"when reading values\" do\n      let(:hklm) { Win32::Registry::HKEY_LOCAL_MACHINE }\n      let(:puppet_key) { \"SOFTWARE\\\\Puppet Labs\"}\n      let(:subkey_name) { \"PuppetRegistryTest#{SecureRandom.uuid}\" }\n      let(:value_name) { SecureRandom.uuid }\n\n      after(:each) do\n        hklm.open(puppet_key, Win32::Registry::KEY_ALL_ACCESS) do |reg|\n          subject.delete_key(reg, subkey_name)\n        end\n      end\n\n      [\n        {:name => 'REG_SZ', :type => Win32::Registry::REG_SZ, :value => 'reg sz string'},\n        {:name => 'REG_EXPAND_SZ', :type => Win32::Registry::REG_EXPAND_SZ, :value => 'reg expand string'},\n        {:name => 'REG_MULTI_SZ', :type => Win32::Registry::REG_MULTI_SZ, :value => ['string1', 'string2']},\n        {:name => 'REG_BINARY', :type => Win32::Registry::REG_BINARY, :value => 'abinarystring'},\n        {:name => 'REG_DWORD', :type => Win32::Registry::REG_DWORD, :value => 0xFFFFFFFF},\n        {:name => 'REG_DWORD_BIG_ENDIAN', :type => Win32::Registry::REG_DWORD_BIG_ENDIAN, :value => 0xFFFF},\n        {:name => 'REG_QWORD', :type => Win32::Registry::REG_QWORD, :value => 0xFFFFFFFFFFFFFFFF},\n      ].each do |pair|\n        it \"should return #{pair[:name]} values\" do\n          hklm.create(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS) do |reg|\n            reg.write(value_name, pair[:type], pair[:value])\n          end\n\n          hklm.open(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_READ) do |reg|\n            vals = subject.values(reg)\n\n            expect(vals).to have_key(value_name)\n            subject.each_value(reg) do |subkey, type, data|\n              expect(type).to eq(pair[:type])\n            end\n\n            written = vals[value_name]\n            expect(written).to eq(pair[:value])\n          end\n        end\n      end\n    end\n\n    context \"when reading corrupt values\" do\n      let(:hklm) { Win32::Registry::HKEY_LOCAL_MACHINE }\n      let(:puppet_key) { \"SOFTWARE\\\\Puppet Labs\"}\n      let(:subkey_name) { \"PuppetRegistryTest#{SecureRandom.uuid}\" }\n      let(:value_name) { SecureRandom.uuid }\n\n      before(:each) do\n        hklm.create(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS) do |reg_key|\n          subject.write_corrupt_dword(reg_key, value_name)\n        end\n      end\n\n      after(:each) do\n        hklm.open(puppet_key, Win32::Registry::KEY_ALL_ACCESS) do |reg_key|\n          subject.delete_key(reg_key, subkey_name)\n        end\n      end\n\n      it \"should return nil for a corrupt DWORD\" do\n        hklm.open(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS) do |reg_key|\n          vals = subject.values(reg_key)\n\n          expect(vals).to have_key(value_name)\n          expect(vals[value_name]).to be_nil\n        end\n      end\n    end\n\n    context 'whean reading null byte' do\n      let(:hklm) { Win32::Registry::HKEY_LOCAL_MACHINE }\n      let(:puppet_key) { 'SOFTWARE\\\\Puppet Labs' }\n      let(:subkey_name) { \"PuppetRegistryTest#{SecureRandom.uuid}\" }\n      let(:value_name) { SecureRandom.uuid }\n\n      after(:each) do\n        hklm.open(puppet_key, Win32::Registry::KEY_ALL_ACCESS) do |reg|\n          subject.delete_key(reg, subkey_name)\n        end\n      end\n\n      [\n        {\n          name: 'REG_SZ',\n          type: Win32::Registry::REG_SZ,\n          value: \"reg sz\\u0000\\u0000 string\",\n          expected_value: \"reg sz\"\n        },\n        {\n          name: 'REG_SZ_2',\n          type: Win32::Registry::REG_SZ,\n          value: \"reg sz 2\\x00\\x00 string\",\n          expected_value: \"reg sz 2\"\n        },\n        {\n          name: 'REG_EXPAND_SZ',\n          type: Win32::Registry::REG_EXPAND_SZ,\n          value: \"\\0\\0\\0reg expand string\",\n          expected_value: \"\"\n        },\n        {\n          name: 'REG_EXPAND_SZ_2',\n          type: Win32::Registry::REG_EXPAND_SZ,\n          value: \"1\\x002\\x003\\x004\\x00\\x00\\x00\\x90\\xD8UoY\".force_encoding(\"UTF-16LE\"),\n          expected_value: \"1234\"\n        }\n      ].each do |pair|\n        it 'reads up to the first wide null' do\n          hklm.create(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS) do |reg|\n            reg.write(value_name, pair[:type], pair[:value])\n          end\n\n          hklm.open(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_READ) do |reg|\n            vals = subject.values(reg)\n\n            expect(vals).to have_key(value_name)\n            subject.each_value(reg) do |_subkey, type, _data|\n              expect(type).to eq(pair[:type])\n            end\n\n            written = vals[value_name]\n            expect(written).to eq(pair[:expected_value])\n          end\n        end\n      end\n    end\n  end\n\n  context \"#values_by_name\" do\n    let(:hkey)   { double('hklm') }\n    let(:subkey) { double('subkey') }\n\n    before :each do\n      allow(subject).to receive(:root).and_return(hkey)\n    end\n\n    context \"when reading values\" do\n      let(:hklm) { Win32::Registry::HKEY_LOCAL_MACHINE }\n      let(:puppet_key) { \"SOFTWARE\\\\Puppet Labs\"}\n      let(:subkey_name) { \"PuppetRegistryTest#{SecureRandom.uuid}\" }\n\n      before(:each) do\n        hklm.create(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS) do |reg|\n          reg.write('valuename1', Win32::Registry::REG_SZ, 'value1')\n          reg.write('valuename2', Win32::Registry::REG_SZ, 'value2')\n        end\n      end\n\n      after(:each) do\n        hklm.open(puppet_key, Win32::Registry::KEY_ALL_ACCESS) do |reg|\n          subject.delete_key(reg, subkey_name)\n        end\n      end\n\n      it \"should return only the values for the names specified\" do\n        hklm.open(\"#{puppet_key}\\\\#{subkey_name}\", Win32::Registry::KEY_ALL_ACCESS) do |reg_key|\n          vals = subject.values_by_name(reg_key, ['valuename1', 'missingname'])\n\n          expect(vals).to have_key('valuename1')\n          expect(vals).to_not have_key('valuename2')\n          expect(vals['valuename1']).to eq('value1')\n          expect(vals['missingname']).to be_nil\n        end\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/integration/util/windows/security_spec.rb",
    "content": "require 'spec_helper'\n\nif Puppet::Util::Platform.windows?\n  class WindowsSecurityTester\n    require 'puppet/util/windows/security'\n    include Puppet::Util::Windows::Security\n  end\nend\n\ndescribe \"Puppet::Util::Windows::Security\", :if => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n\n  before :all do\n    # necessary for localized name of guests\n    guests_name = Puppet::Util::Windows::SID.sid_to_name('S-1-5-32-546')\n    guests = Puppet::Util::Windows::ADSI::Group.new(guests_name)\n\n    @sids = {\n      :current_user => Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name),\n      :system => Puppet::Util::Windows::SID::LocalSystem,\n      :administrators => Puppet::Util::Windows::SID::BuiltinAdministrators,\n      :guest => Puppet::Util::Windows::SID.name_to_sid(guests.members[0]),\n      :users => Puppet::Util::Windows::SID::BuiltinUsers,\n      :power_users => Puppet::Util::Windows::SID::PowerUsers,\n      :none => Puppet::Util::Windows::SID::Nobody,\n      :everyone => Puppet::Util::Windows::SID::Everyone\n    }\n    # The TCP/IP NetBIOS Helper service (aka 'lmhosts') has ended up\n    # disabled on some VMs for reasons we couldn't track down. This\n    # condition causes tests which rely on resolving UNC style paths\n    # (like \\\\localhost) to fail with unhelpful error messages.\n    # Put a check for this upfront to aid debug should this strike again.\n    service = Puppet::Type.type(:service).new(:name => 'lmhosts')\n    expect(service.provider.status).to eq(:running), 'lmhosts service is not running'\n  end\n\n  let (:sids) { @sids }\n  let (:winsec) { WindowsSecurityTester.new }\n  let (:klass) { Puppet::Util::Windows::File }\n\n  def set_group_depending_on_current_user(path)\n    if sids[:current_user] == sids[:system]\n      # if the current user is SYSTEM, by setting the group to\n      # guest, SYSTEM is automagically given full control, so instead\n      # override that behavior with SYSTEM as group and a specific mode\n      winsec.set_group(sids[:system], path)\n      mode = winsec.get_mode(path)\n      winsec.set_mode(mode & ~WindowsSecurityTester::S_IRWXG, path)\n    else\n      winsec.set_group(sids[:guest], path)\n    end\n  end\n\n  def grant_everyone_full_access(path)\n    sd = winsec.get_security_descriptor(path)\n    everyone = 'S-1-1-0'\n    inherit = Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE\n    sd.dacl.allow(everyone, klass::FILE_ALL_ACCESS, inherit)\n    winsec.set_security_descriptor(path, sd)\n  end\n\n  shared_examples_for \"only child owner\" do\n    it \"should allow child owner\" do\n      winsec.set_owner(sids[:guest], parent)\n      winsec.set_group(sids[:current_user], parent)\n      winsec.set_mode(0700, parent)\n\n      check_delete(path)\n    end\n\n    it \"should deny parent owner\" do\n      winsec.set_owner(sids[:guest], path)\n      winsec.set_group(sids[:current_user], path)\n      winsec.set_mode(0700, path)\n\n      expect { check_delete(path) }.to raise_error(Errno::EACCES)\n    end\n\n    it \"should deny group\" do\n      winsec.set_owner(sids[:guest], path)\n      winsec.set_group(sids[:current_user], path)\n      winsec.set_mode(0700, path)\n\n      expect { check_delete(path) }.to raise_error(Errno::EACCES)\n    end\n\n    it \"should deny other\" do\n      winsec.set_owner(sids[:guest], path)\n      winsec.set_group(sids[:current_user], path)\n      winsec.set_mode(0700, path)\n\n      expect { check_delete(path) }.to raise_error(Errno::EACCES)\n    end\n  end\n\n  shared_examples_for \"a securable object\" do\n    describe \"on a volume that doesn't support ACLs\" do\n      [:owner, :group, :mode].each do |p|\n        it \"should return nil #{p}\" do\n          allow(winsec).to receive(:supports_acl?).and_return(false)\n\n          expect(winsec.send(\"get_#{p}\", path)).to be_nil\n        end\n      end\n    end\n\n    describe \"on a volume that supports ACLs\" do\n      describe \"for a normal user\" do\n        before :each do\n          allow(Puppet.features).to receive(:root?).and_return(false)\n        end\n\n        after :each do\n          winsec.set_mode(WindowsSecurityTester::S_IRWXU, parent)\n          begin winsec.set_mode(WindowsSecurityTester::S_IRWXU, path) rescue nil end\n        end\n\n        describe \"#supports_acl?\" do\n          %w[c:/ c:\\\\ c:/windows/system32 \\\\\\\\localhost\\\\C$ \\\\\\\\127.0.0.1\\\\C$\\\\foo].each do |path|\n            it \"should accept #{path}\" do\n              expect(winsec).to be_supports_acl(path)\n            end\n          end\n\n          it \"should raise an exception if it cannot get volume information\" do\n            expect {\n              winsec.supports_acl?('foobar')\n            }.to raise_error(Puppet::Error, /Failed to get volume information/)\n          end\n        end\n\n        describe \"#owner=\" do\n          it \"should allow setting to the current user\" do\n            winsec.set_owner(sids[:current_user], path)\n          end\n\n          it \"should raise an exception when setting to a different user\" do\n            expect { winsec.set_owner(sids[:guest], path) }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(1307) # ERROR_INVALID_OWNER\n            end\n          end\n        end\n\n        describe \"#owner\" do\n          it \"it should not be empty\" do\n            expect(winsec.get_owner(path)).not_to be_empty\n          end\n\n          it \"should raise an exception if an invalid path is provided\" do\n            expect { winsec.get_owner(\"c:\\\\doesnotexist.txt\") }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(2) # ERROR_FILE_NOT_FOUND\n            end\n          end\n        end\n\n        describe \"#group=\" do\n          it \"should allow setting to a group the current owner is a member of\" do\n            winsec.set_group(sids[:users], path)\n          end\n\n          # Unlike unix, if the user has permission to WRITE_OWNER, which the file owner has by default,\n          # then they can set the primary group to a group that the user does not belong to.\n          it \"should allow setting to a group the current owner is not a member of\" do\n            winsec.set_group(sids[:power_users], path)\n          end\n\n          it \"should consider a mode of 7 for group to be FullControl (F)\" do\n            winsec.set_group(sids[:power_users], path)\n            winsec.set_mode(0070, path)\n\n            group_ace = winsec.get_aces_for_path_by_sid(path, sids[:power_users])\n            # there should only be a single ace for the given group\n            expect(group_ace.count).to eq(1)\n            expect(group_ace[0].mask).to eq(klass::FILE_ALL_ACCESS)\n\n            # ensure that mode is still read as 070 (written as 70)\n            expect((winsec.get_mode(path) & 0777).to_s(8).rjust(3, '0')).to eq(\"070\")\n          end\n        end\n\n        describe \"#group\" do\n          it \"should not be empty\" do\n            expect(winsec.get_group(path)).not_to be_empty\n          end\n\n          it \"should raise an exception if an invalid path is provided\" do\n            expect { winsec.get_group(\"c:\\\\doesnotexist.txt\") }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(2) # ERROR_FILE_NOT_FOUND\n            end\n          end\n        end\n\n        it \"should preserve inherited full control for SYSTEM when setting owner and group\" do\n          # new file has SYSTEM\n          system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])\n          expect(system_aces).not_to be_empty\n\n          # when running under SYSTEM account, multiple ACEs come back\n          # so we only care that we have at least one of these\n          expect(system_aces.any? do |ace|\n            ace.mask == klass::FILE_ALL_ACCESS\n          end).to be_truthy\n\n          # changing the owner/group will no longer make the SD protected\n          winsec.set_group(sids[:power_users], path)\n          winsec.set_owner(sids[:administrators], path)\n\n          expect(system_aces.find do |ace|\n            ace.mask == klass::FILE_ALL_ACCESS && ace.inherited?\n          end).not_to be_nil\n        end\n\n        describe \"#mode=\" do\n          (0000..0700).step(0100) do |mode|\n            it \"should enforce mode #{mode.to_s(8)}\" do\n              winsec.set_mode(mode, path)\n\n              check_access(mode, path)\n            end\n          end\n\n          it \"should round-trip all 128 modes that do not require deny ACEs, where owner and group are different\" do\n            # windows defaults set Administrators, None when Administrator\n            # or Administrators, SYSTEM when System\n            # but we can guarantee group is different by explicitly setting to Users\n            winsec.set_group(sids[:users], path)\n\n            0.upto(1).each do |s|\n              0.upto(7).each do |u|\n                0.upto(u).each do |g|\n                  0.upto(g).each do |o|\n                    # if user is superset of group, and group superset of other, then\n                    # no deny ace is required, and mode can be converted to win32\n                    # access mask, and back to mode without loss of information\n                    # (provided the owner and group are not the same)\n                    next if ((u & g) != g) or ((g & o) != o)\n\n                    mode = (s << 9 | u << 6 | g << 3 | o << 0)\n                    winsec.set_mode(mode, path)\n                    expect(winsec.get_mode(path).to_s(8)).to eq(mode.to_s(8))\n                  end\n                end\n              end\n            end\n          end\n\n          it \"should round-trip all 54 modes that do not require deny ACEs, where owner and group are same\" do\n            winsec.set_group(winsec.get_owner(path), path)\n\n            0.upto(1).each do |s|\n              0.upto(7).each do |ug|\n                0.upto(ug).each do |o|\n                  # if user and group superset of other, then\n                  # no deny ace is required, and mode can be converted to win32\n                  # access mask, and back to mode without loss of information\n                  # (provided the owner and group are the same)\n                  next if ((ug & o) != o)\n                  mode = (s << 9 | ug << 6 | ug << 3 | o << 0)\n                  winsec.set_mode(mode, path)\n                  expect(winsec.get_mode(path).to_s(8)).to eq(mode.to_s(8))\n                end\n              end\n            end\n          end\n\n          # The SYSTEM user is a special case therefore we need to test that we round trip correctly when set\n          it \"should round-trip all 128 modes that do not require deny ACEs, when simulating a SYSTEM service\" do\n            # The owner and group for files/dirs created, when running as a service under Local System are\n            # Owner = Administrators\n            # Group = SYSTEM\n            winsec.set_owner(sids[:administrators], path)\n            winsec.set_group(sids[:system], path)\n\n            0.upto(1).each do |s|\n              0.upto(7).each do |u|\n                0.upto(u).each do |g|\n                  0.upto(g).each do |o|\n                    # if user is superset of group, and group superset of other, then\n                    # no deny ace is required, and mode can be converted to win32\n                    # access mask, and back to mode without loss of information\n                    # (provided the owner and group are not the same)\n                    next if ((u & g) != g) or ((g & o) != o)\n                    applied_mode  = (s << 9 | u << 6 | g << 3 | o << 0)\n                    # SYSTEM must always be Full Control (7)\n                    expected_mode = (s << 9 | u << 6 | 7 << 3 | o << 0)\n                    winsec.set_mode(applied_mode, path)\n                    expect(winsec.get_mode(path).to_s(8)).to eq(expected_mode.to_s(8))\n                  end\n                end\n              end\n            end\n          end\n\n          it \"should preserve full control for SYSTEM when setting mode\" do\n            # new file has SYSTEM\n            system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])\n            expect(system_aces).not_to be_empty\n\n            # when running under SYSTEM account, multiple ACEs come back\n            # so we only care that we have at least one of these\n            expect(system_aces.any? do |ace|\n              ace.mask == klass::FILE_ALL_ACCESS\n            end).to be_truthy\n\n            # changing the mode will make the SD protected\n            winsec.set_group(sids[:none], path)\n            winsec.set_mode(0600, path)\n\n            # and should have a non-inherited SYSTEM ACE(s)\n            system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])\n            system_aces.each do |ace|\n              expect(ace.mask).to eq(klass::FILE_ALL_ACCESS)\n              expect(ace).not_to be_inherited\n            end\n\n            if Puppet::FileSystem.directory?(path)\n              system_aces.each do |ace|\n                expect(ace).to be_object_inherit\n                expect(ace).to be_container_inherit\n              end\n\n              # it's critically important that this file be default created\n              # and that this file not have it's owner / group / mode set by winsec\n              nested_file = File.join(path, 'nested_file')\n              File.new(nested_file, 'w').close\n\n              system_aces = winsec.get_aces_for_path_by_sid(nested_file, sids[:system])\n              # even when SYSTEM is the owner (in CI), there should be an inherited SYSTEM\n              expect(system_aces.any? do |ace|\n                ace.mask == klass::FILE_ALL_ACCESS && ace.inherited?\n              end).to be_truthy\n            end\n          end\n\n          describe \"for modes that require deny aces\" do\n            it \"should map everyone to group and owner\" do\n              winsec.set_mode(0426, path)\n              expect(winsec.get_mode(path).to_s(8)).to eq(\"666\")\n            end\n\n            it \"should combine user and group modes when owner and group sids are equal\" do\n              winsec.set_group(winsec.get_owner(path), path)\n\n              winsec.set_mode(0410, path)\n              expect(winsec.get_mode(path).to_s(8)).to eq(\"550\")\n            end\n          end\n\n          describe \"for read-only objects\" do\n            before :each do\n              winsec.set_group(sids[:none], path)\n              winsec.set_mode(0600, path)\n              Puppet::Util::Windows::File.add_attributes(path, klass::FILE_ATTRIBUTE_READONLY)\n              expect(Puppet::Util::Windows::File.get_attributes(path) & klass::FILE_ATTRIBUTE_READONLY).to be_nonzero\n            end\n\n            it \"should make them writable if any sid has write permission\" do\n              winsec.set_mode(WindowsSecurityTester::S_IWUSR, path)\n              expect(Puppet::Util::Windows::File.get_attributes(path) & klass::FILE_ATTRIBUTE_READONLY).to eq(0)\n            end\n\n            it \"should leave them read-only if no sid has write permission and should allow full access for SYSTEM\" do\n              winsec.set_mode(WindowsSecurityTester::S_IRUSR | WindowsSecurityTester::S_IXGRP, path)\n              expect(Puppet::Util::Windows::File.get_attributes(path) & klass::FILE_ATTRIBUTE_READONLY).to be_nonzero\n\n              system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])\n\n              # when running under SYSTEM account, and set_group / set_owner hasn't been called\n              # SYSTEM full access will be restored\n              expect(system_aces.any? do |ace|\n                ace.mask == klass::FILE_ALL_ACCESS\n              end).to be_truthy\n            end\n          end\n\n          it \"should raise an exception if an invalid path is provided\" do\n            expect { winsec.set_mode(sids[:guest], \"c:\\\\doesnotexist.txt\") }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(2) # ERROR_FILE_NOT_FOUND\n            end\n          end\n        end\n\n        describe \"#mode\" do\n          it \"should report when extra aces are encounted\" do\n            sd = winsec.get_security_descriptor(path)\n            (544..547).each do |rid|\n              sd.dacl.allow(\"S-1-5-32-#{rid}\", klass::STANDARD_RIGHTS_ALL)\n            end\n            winsec.set_security_descriptor(path, sd)\n\n            mode = winsec.get_mode(path)\n            expect(mode & WindowsSecurityTester::S_IEXTRA).to eq(WindowsSecurityTester::S_IEXTRA)\n          end\n\n          it \"should return deny aces\" do\n            sd = winsec.get_security_descriptor(path)\n            sd.dacl.deny(sids[:guest], klass::FILE_GENERIC_WRITE)\n            winsec.set_security_descriptor(path, sd)\n\n            guest_aces = winsec.get_aces_for_path_by_sid(path, sids[:guest])\n            expect(guest_aces.find do |ace|\n              ace.type == Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE\n            end).not_to be_nil\n          end\n\n          it \"should skip inherit-only ace\" do\n            sd = winsec.get_security_descriptor(path)\n            dacl = Puppet::Util::Windows::AccessControlList.new\n            dacl.allow(\n              sids[:current_user], klass::STANDARD_RIGHTS_ALL | klass::SPECIFIC_RIGHTS_ALL\n            )\n            dacl.allow(\n              sids[:everyone],\n              klass::FILE_GENERIC_READ,\n              Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE\n            )\n            winsec.set_security_descriptor(path, sd)\n\n            expect(winsec.get_mode(path) & WindowsSecurityTester::S_IRWXO).to eq(0)\n          end\n\n          it \"should raise an exception if an invalid path is provided\" do\n            expect { winsec.get_mode(\"c:\\\\doesnotexist.txt\") }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(2) # ERROR_FILE_NOT_FOUND\n            end\n          end\n        end\n\n        describe \"inherited access control entries\" do\n          it \"should be absent when the access control list is protected, and should not remove SYSTEM\" do\n            winsec.set_mode(WindowsSecurityTester::S_IRWXU, path)\n\n            mode = winsec.get_mode(path)\n            [ WindowsSecurityTester::S_IEXTRA,\n              WindowsSecurityTester::S_ISYSTEM_MISSING ].each do |flag|\n              expect(mode & flag).not_to eq(flag)\n            end\n          end\n\n          it \"should be present when the access control list is unprotected\" do\n            # add a bunch of aces to the parent with permission to add children\n            allow = klass::STANDARD_RIGHTS_ALL | klass::SPECIFIC_RIGHTS_ALL\n            inherit = Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE\n\n            sd = winsec.get_security_descriptor(parent)\n            sd.dacl.allow(\n              \"S-1-1-0\", #everyone\n              allow,\n              inherit\n            )\n            (544..547).each do |rid|\n              sd.dacl.allow(\n                \"S-1-5-32-#{rid}\",\n                klass::STANDARD_RIGHTS_ALL,\n                inherit\n              )\n            end\n            winsec.set_security_descriptor(parent, sd)\n\n            # unprotect child, it should inherit from parent\n            winsec.set_mode(WindowsSecurityTester::S_IRWXU, path, false)\n            expect(winsec.get_mode(path) & WindowsSecurityTester::S_IEXTRA).to eq(WindowsSecurityTester::S_IEXTRA)\n          end\n        end\n      end\n\n      describe \"for an administrator\", :if => (Puppet.features.root? && Puppet::Util::Platform.windows?) do\n        before :each do\n          is_dir = Puppet::FileSystem.directory?(path)\n          winsec.set_mode(WindowsSecurityTester::S_IRWXU | WindowsSecurityTester::S_IRWXG, path)\n          set_group_depending_on_current_user(path)\n          winsec.set_owner(sids[:guest], path)\n          expected_error = is_dir ? Errno::EISDIR : Errno::EACCES\n          expect { File.open(path, 'r') }.to raise_error(expected_error)\n        end\n\n        after :each do\n          if Puppet::FileSystem.exist?(path)\n            winsec.set_owner(sids[:current_user], path)\n            winsec.set_mode(WindowsSecurityTester::S_IRWXU, path)\n          end\n        end\n\n        describe \"#owner=\" do\n          it \"should accept the guest sid\" do\n            winsec.set_owner(sids[:guest], path)\n            expect(winsec.get_owner(path)).to eq(sids[:guest])\n          end\n\n          it \"should accept a user sid\" do\n            winsec.set_owner(sids[:current_user], path)\n            expect(winsec.get_owner(path)).to eq(sids[:current_user])\n          end\n\n          it \"should accept a group sid\" do\n            winsec.set_owner(sids[:power_users], path)\n            expect(winsec.get_owner(path)).to eq(sids[:power_users])\n          end\n\n          it \"should raise an exception if an invalid sid is provided\" do\n            expect { winsec.set_owner(\"foobar\", path) }.to raise_error(Puppet::Error, /Failed to convert string SID/)\n          end\n\n          it \"should raise an exception if an invalid path is provided\" do\n            expect { winsec.set_owner(sids[:guest], \"c:\\\\doesnotexist.txt\") }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(2) # ERROR_FILE_NOT_FOUND\n            end\n          end\n        end\n\n        describe \"#group=\" do\n          it \"should accept the test group\" do\n            winsec.set_group(sids[:guest], path)\n            expect(winsec.get_group(path)).to eq(sids[:guest])\n          end\n\n          it \"should accept a group sid\" do\n            winsec.set_group(sids[:power_users], path)\n            expect(winsec.get_group(path)).to eq(sids[:power_users])\n          end\n\n          it \"should accept a user sid\" do\n            winsec.set_group(sids[:current_user], path)\n            expect(winsec.get_group(path)).to eq(sids[:current_user])\n          end\n\n          it \"should combine owner and group rights when they are the same sid\" do\n            winsec.set_owner(sids[:power_users], path)\n            winsec.set_group(sids[:power_users], path)\n            winsec.set_mode(0610, path)\n\n            expect(winsec.get_owner(path)).to eq(sids[:power_users])\n            expect(winsec.get_group(path)).to eq(sids[:power_users])\n            # note group execute permission added to user ace, and then group rwx value\n            # reflected to match\n\n            # Exclude missing system ace, since that's not relevant\n            expect((winsec.get_mode(path) & 0777).to_s(8)).to eq(\"770\")\n          end\n\n          it \"should raise an exception if an invalid sid is provided\" do\n            expect { winsec.set_group(\"foobar\", path) }.to raise_error(Puppet::Error, /Failed to convert string SID/)\n          end\n\n          it \"should raise an exception if an invalid path is provided\" do\n            expect { winsec.set_group(sids[:guest], \"c:\\\\doesnotexist.txt\") }.to raise_error do |error|\n              expect(error).to be_a(Puppet::Util::Windows::Error)\n              expect(error.code).to eq(2) # ERROR_FILE_NOT_FOUND\n            end\n          end\n        end\n\n        describe \"when the sid is NULL\" do\n          it \"should retrieve an empty owner sid\"\n          it \"should retrieve an empty group sid\"\n        end\n\n        describe \"when the sid refers to a deleted trustee\" do\n          it \"should retrieve the user sid\" do\n            sid = nil\n            user = Puppet::Util::Windows::ADSI::User.create(\"puppet#{rand(10000)}\")\n            user.password = 'PUPPET_RULeZ_123!'\n            user.commit\n            begin\n              sid = Puppet::Util::Windows::ADSI::User.new(user.name).sid.sid\n              winsec.set_owner(sid, path)\n              winsec.set_mode(WindowsSecurityTester::S_IRWXU, path)\n            ensure\n              Puppet::Util::Windows::ADSI::User.delete(user.name)\n            end\n\n            expect(winsec.get_owner(path)).to eq(sid)\n            expect(winsec.get_mode(path)).to eq(WindowsSecurityTester::S_IRWXU)\n          end\n\n          it \"should retrieve the group sid\" do\n            sid = nil\n            group = Puppet::Util::Windows::ADSI::Group.create(\"puppet#{rand(10000)}\")\n            group.commit\n            begin\n              sid = Puppet::Util::Windows::ADSI::Group.new(group.name).sid.sid\n              winsec.set_group(sid, path)\n              winsec.set_mode(WindowsSecurityTester::S_IRWXG, path)\n            ensure\n              Puppet::Util::Windows::ADSI::Group.delete(group.name)\n            end\n            expect(winsec.get_group(path)).to eq(sid)\n            expect(winsec.get_mode(path)).to eq(WindowsSecurityTester::S_IRWXG)\n          end\n        end\n\n        describe \"#mode\" do\n          it \"should deny all access when the DACL is empty, including SYSTEM\" do\n            sd = winsec.get_security_descriptor(path)\n            # don't allow inherited aces to affect the test\n            protect = true\n            new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, [], protect)\n            winsec.set_security_descriptor(path, new_sd)\n\n            expect(winsec.get_mode(path)).to eq(WindowsSecurityTester::S_ISYSTEM_MISSING)\n          end\n\n          # REMIND: ruby crashes when trying to set a NULL DACL\n          # it \"should allow all when it is nil\" do\n          #   winsec.set_owner(sids[:current_user], path)\n          #   winsec.open_file(path, WindowsSecurityTester::READ_CONTROL | WindowsSecurityTester::WRITE_DAC) do |handle|\n          #     winsec.set_security_info(handle, WindowsSecurityTester::DACL_SECURITY_INFORMATION | WindowsSecurityTester::PROTECTED_DACL_SECURITY_INFORMATION, nil)\n          #   end\n          #   winsec.get_mode(path).to_s(8).should == \"777\"\n          # end\n        end\n\n        describe \"#mode=\" do\n          # setting owner to SYSTEM requires root\n          it \"should round-trip all 54 modes that do not require deny ACEs, when simulating a SYSTEM scheduled task\" do\n            # The owner and group for files/dirs created, when running as a Scheduled Task as Local System are\n            # Owner = SYSTEM\n            # Group = SYSTEM\n            winsec.set_group(sids[:system], path)\n            winsec.set_owner(sids[:system], path)\n\n            0.upto(1).each do |s|\n              0.upto(7).each do |ug|\n                0.upto(ug).each do |o|\n                  # if user and group superset of other, then\n                  # no deny ace is required, and mode can be converted to win32\n                  # access mask, and back to mode without loss of information\n                  # (provided the owner and group are the same)\n                  next if ((ug & o) != o)\n                  applied_mode  = (s << 9 | ug << 6 | ug << 3 | o << 0)\n                  # SYSTEM must always be Full Control (7)\n                  expected_mode = (s << 9 | 7 << 6 | 7 << 3 | o << 0)\n                  winsec.set_mode(applied_mode, path)\n                  expect(winsec.get_mode(path).to_s(8)).to eq(expected_mode.to_s(8))\n                end\n              end\n            end\n          end\n        end\n\n        describe \"when the parent directory\" do\n          before :each do\n            winsec.set_owner(sids[:current_user], parent)\n            winsec.set_owner(sids[:current_user], path)\n            winsec.set_mode(0777, path, false)\n          end\n\n          describe \"is writable and executable\" do\n            describe \"and sticky bit is set\" do\n              it \"should allow child owner\" do\n                winsec.set_owner(sids[:guest], parent)\n                winsec.set_group(sids[:current_user], parent)\n                winsec.set_mode(01700, parent)\n\n                check_delete(path)\n              end\n\n              it \"should allow parent owner\" do\n                winsec.set_owner(sids[:current_user], parent)\n                winsec.set_group(sids[:guest], parent)\n                winsec.set_mode(01700, parent)\n\n                winsec.set_owner(sids[:current_user], path)\n                winsec.set_group(sids[:guest], path)\n                winsec.set_mode(0700, path)\n\n                check_delete(path)\n              end\n\n              it \"should deny group\" do\n                winsec.set_owner(sids[:guest], parent)\n                winsec.set_group(sids[:current_user], parent)\n                winsec.set_mode(01770, parent)\n\n                winsec.set_owner(sids[:guest], path)\n                winsec.set_group(sids[:current_user], path)\n                winsec.set_mode(0700, path)\n\n                expect { check_delete(path) }.to raise_error(Errno::EACCES)\n              end\n\n              it \"should deny other\" do\n                winsec.set_owner(sids[:guest], parent)\n                winsec.set_group(sids[:current_user], parent)\n                winsec.set_mode(01777, parent)\n\n                winsec.set_owner(sids[:guest], path)\n                winsec.set_group(sids[:current_user], path)\n                winsec.set_mode(0700, path)\n\n                expect { check_delete(path) }.to raise_error(Errno::EACCES)\n              end\n            end\n\n            describe \"and sticky bit is not set\" do\n              it \"should allow child owner\" do\n                winsec.set_owner(sids[:guest], parent)\n                winsec.set_group(sids[:current_user], parent)\n                winsec.set_mode(0700, parent)\n\n                check_delete(path)\n              end\n\n              it \"should allow parent owner\" do\n                winsec.set_owner(sids[:current_user], parent)\n                winsec.set_group(sids[:guest], parent)\n                winsec.set_mode(0700, parent)\n\n                winsec.set_owner(sids[:current_user], path)\n                winsec.set_group(sids[:guest], path)\n                winsec.set_mode(0700, path)\n\n                check_delete(path)\n              end\n\n              it \"should allow group\" do\n                winsec.set_owner(sids[:guest], parent)\n                winsec.set_group(sids[:current_user], parent)\n                winsec.set_mode(0770, parent)\n\n                winsec.set_owner(sids[:guest], path)\n                winsec.set_group(sids[:current_user], path)\n                winsec.set_mode(0700, path)\n\n                check_delete(path)\n              end\n\n              it \"should allow other\" do\n                winsec.set_owner(sids[:guest], parent)\n                winsec.set_group(sids[:current_user], parent)\n                winsec.set_mode(0777, parent)\n\n                winsec.set_owner(sids[:guest], path)\n                winsec.set_group(sids[:current_user], path)\n                winsec.set_mode(0700, path)\n\n                check_delete(path)\n              end\n            end\n          end\n\n          describe \"is not writable\" do\n            before :each do\n              winsec.set_group(sids[:current_user], parent)\n              winsec.set_mode(0555, parent)\n            end\n\n            it_behaves_like \"only child owner\"\n          end\n\n          describe \"is not executable\" do\n            before :each do\n              winsec.set_group(sids[:current_user], parent)\n              winsec.set_mode(0666, parent)\n            end\n\n            it_behaves_like \"only child owner\"\n          end\n        end\n      end\n    end\n  end\n\n  describe \"file\" do\n    let (:parent) do\n      tmpdir('win_sec_test_file')\n    end\n\n    let (:path) do\n      path = File.join(parent, 'childfile')\n      File.new(path, 'w').close\n      path\n    end\n\n    after :each do\n      # allow temp files to be cleaned up\n      grant_everyone_full_access(parent)\n    end\n\n    it_behaves_like \"a securable object\" do\n      def check_access(mode, path)\n        if (mode & WindowsSecurityTester::S_IRUSR).nonzero?\n          check_read(path)\n        else\n          expect { check_read(path) }.to raise_error(Errno::EACCES)\n        end\n\n        if (mode & WindowsSecurityTester::S_IWUSR).nonzero?\n          check_write(path)\n        else\n          expect { check_write(path) }.to raise_error(Errno::EACCES)\n        end\n\n        if (mode & WindowsSecurityTester::S_IXUSR).nonzero?\n          expect { check_execute(path) }.to raise_error(Errno::ENOEXEC)\n        else\n          expect { check_execute(path) }.to raise_error(Errno::EACCES)\n        end\n      end\n\n      def check_read(path)\n        File.open(path, 'r').close\n      end\n\n      def check_write(path)\n        File.open(path, 'w').close\n      end\n\n      def check_execute(path)\n        Kernel.exec(path)\n      end\n\n      def check_delete(path)\n        File.delete(path)\n      end\n    end\n\n    describe \"locked files\" do\n      let (:explorer) { File.join(ENV['SystemRoot'], \"explorer.exe\") }\n\n      it \"should get the owner\" do\n        expect(winsec.get_owner(explorer)).to match(/^S-1-5-/)\n      end\n\n      it \"should get the group\" do\n        expect(winsec.get_group(explorer)).to match(/^S-1-5-/)\n      end\n\n      it \"should get the mode\" do\n        expect(winsec.get_mode(explorer)).to eq(WindowsSecurityTester::S_IRWXU | WindowsSecurityTester::S_IRWXG | WindowsSecurityTester::S_IEXTRA)\n      end\n    end\n  end\n\n  describe \"directory\" do\n    let (:parent) do\n      tmpdir('win_sec_test_dir')\n    end\n\n    let (:path) do\n      path = File.join(parent, 'childdir')\n      Dir.mkdir(path)\n      path\n    end\n\n    after :each do\n      # allow temp files to be cleaned up\n      grant_everyone_full_access(parent)\n    end\n\n    it_behaves_like \"a securable object\" do\n      def check_access(mode, path)\n        if (mode & WindowsSecurityTester::S_IRUSR).nonzero?\n          check_read(path)\n        else\n          expect { check_read(path) }.to raise_error(Errno::EACCES)\n        end\n\n        if (mode & WindowsSecurityTester::S_IWUSR).nonzero?\n          check_write(path)\n        else\n          expect { check_write(path) }.to raise_error(Errno::EACCES)\n        end\n\n        if (mode & WindowsSecurityTester::S_IXUSR).nonzero?\n          check_execute(path)\n        else\n          expect { check_execute(path) }.to raise_error(Errno::EACCES)\n        end\n      end\n\n      def check_read(path)\n        Dir.entries(path)\n      end\n\n      def check_write(path)\n        Dir.mkdir(File.join(path, \"subdir\"))\n      end\n\n      def check_execute(path)\n        Dir.chdir(path) {}\n      end\n\n      def check_delete(path)\n        Dir.rmdir(path)\n      end\n    end\n\n    describe \"inheritable aces\" do\n      it \"should be applied to child objects\" do\n        mode640 = WindowsSecurityTester::S_IRUSR | WindowsSecurityTester::S_IWUSR | WindowsSecurityTester::S_IRGRP\n        winsec.set_mode(mode640, path)\n\n        newfile = File.join(path, \"newfile.txt\")\n        File.new(newfile, \"w\").close\n\n        newdir = File.join(path, \"newdir\")\n        Dir.mkdir(newdir)\n\n        [newfile, newdir].each do |p|\n          mode = winsec.get_mode(p)\n          expect((mode & 07777).to_s(8)).to eq(mode640.to_s(8))\n        end\n      end\n    end\n  end\n\n  context \"security descriptor\" do\n    let(:path) { tmpfile('sec_descriptor') }\n    let(:read_execute) { 0x201FF }\n    let(:synchronize)  { 0x100000 }\n\n    before :each do\n      FileUtils.touch(path)\n    end\n\n    it \"preserves aces for other users\" do\n      dacl = Puppet::Util::Windows::AccessControlList.new\n      sids_in_dacl = [sids[:current_user], sids[:users]]\n      sids_in_dacl.each do |sid|\n        dacl.allow(sid, read_execute)\n      end\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(sids[:guest], sids[:guest], dacl, true)\n      winsec.set_security_descriptor(path, sd)\n\n      aces = winsec.get_security_descriptor(path).dacl.to_a\n      expect(aces.map(&:sid)).to eq(sids_in_dacl)\n      expect(aces.map(&:mask).all? { |mask| mask == read_execute }).to be_truthy\n    end\n\n    it \"changes the sid for all aces that were assigned to the old owner\" do\n      sd = winsec.get_security_descriptor(path)\n      expect(sd.owner).not_to eq(sids[:guest])\n\n      sd.dacl.allow(sd.owner, read_execute)\n      sd.dacl.allow(sd.owner, synchronize)\n\n      sd.owner = sids[:guest]\n      winsec.set_security_descriptor(path, sd)\n\n      dacl = winsec.get_security_descriptor(path).dacl\n      aces = dacl.find_all { |ace| ace.sid == sids[:guest] }\n      # only non-inherited aces will be reassigned to guest, so\n      # make sure we find at least the two we added\n      expect(aces.size).to be >= 2\n    end\n\n    it \"preserves INHERIT_ONLY_ACEs\" do\n      # inherit only aces can only be set on directories\n      dir = tmpdir('inheritonlyace')\n\n      inherit_flags = Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE |\n        Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |\n        Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE\n\n      sd = winsec.get_security_descriptor(dir)\n      sd.dacl.allow(sd.owner, klass::FILE_ALL_ACCESS, inherit_flags)\n      winsec.set_security_descriptor(dir, sd)\n\n      sd = winsec.get_security_descriptor(dir)\n\n      winsec.set_owner(sids[:guest], dir)\n\n      sd = winsec.get_security_descriptor(dir)\n      expect(sd.dacl.find do |ace|\n        ace.sid == sids[:guest] && ace.inherit_only?\n      end).not_to be_nil\n    end\n\n    it \"allows deny ACEs with inheritance\" do\n      # inheritance can only be set on directories\n      dir = tmpdir('denyaces')\n\n      inherit_flags = Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |\n          Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE\n\n      sd = winsec.get_security_descriptor(dir)\n      sd.dacl.deny(sids[:guest], klass::FILE_ALL_ACCESS, inherit_flags)\n      winsec.set_security_descriptor(dir, sd)\n\n      sd = winsec.get_security_descriptor(dir)\n      expect(sd.dacl.find do |ace|\n        ace.sid == sids[:guest] && ace.flags != 0\n      end).not_to be_nil\n    end\n\n    context \"when managing mode\" do\n      it \"removes aces for sids that are neither the owner nor group\" do\n        # add a guest ace, it's never owner or group\n        sd = winsec.get_security_descriptor(path)\n        sd.dacl.allow(sids[:guest], read_execute)\n        winsec.set_security_descriptor(path, sd)\n\n        # setting the mode, it should remove extra aces\n        winsec.set_mode(0770, path)\n\n        # make sure it's gone\n        dacl = winsec.get_security_descriptor(path).dacl\n        aces = dacl.find_all { |ace| ace.sid == sids[:guest] }\n        expect(aces).to be_empty\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util/windows/user_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"Puppet::Util::Windows::User\", :if => Puppet::Util::Platform.windows? do\n  describe \"2003 without UAC\" do\n    before :each do\n      allow(Puppet::Util::Windows::Process).to receive(:windows_major_version).and_return(5)\n      allow(Puppet::Util::Windows::Process).to receive(:supports_elevated_security?).and_return(false)\n    end\n\n    it \"should be an admin if user's token contains the Administrators SID\" do\n      expect(Puppet::Util::Windows::User).to receive(:check_token_membership).and_return(true)\n\n      expect(Puppet::Util::Windows::User).to be_admin\n    end\n\n    it \"should not be an admin if user's token doesn't contain the Administrators SID\" do\n      expect(Puppet::Util::Windows::User).to receive(:check_token_membership).and_return(false)\n\n      expect(Puppet::Util::Windows::User).not_to be_admin\n    end\n\n    it \"should raise an exception if we can't check token membership\" do\n      expect(Puppet::Util::Windows::User).to receive(:check_token_membership).and_raise(Puppet::Util::Windows::Error, \"Access denied.\")\n\n      expect { Puppet::Util::Windows::User.admin? }.to raise_error(Puppet::Util::Windows::Error, /Access denied./)\n    end\n  end\n\n  context \"2008 with UAC\" do\n    before :each do\n      allow(Puppet::Util::Windows::Process).to receive(:windows_major_version).and_return(6)\n      allow(Puppet::Util::Windows::Process).to receive(:supports_elevated_security?).and_return(true)\n    end\n\n    describe \"in local administrators group\" do\n      before :each do\n        allow(Puppet::Util::Windows::User).to receive(:check_token_membership).and_return(true)\n      end\n\n      it \"should be an admin if user is running with elevated privileges\" do\n        allow(Puppet::Util::Windows::Process).to receive(:elevated_security?).and_return(true)\n\n        expect(Puppet::Util::Windows::User).to be_admin\n      end\n\n      it \"should not be an admin if user is not running with elevated privileges\" do\n        allow(Puppet::Util::Windows::Process).to receive(:elevated_security?).and_return(false)\n\n        expect(Puppet::Util::Windows::User).not_to be_admin\n      end\n\n      it \"should raise an exception if the process fails to open the process token\" do\n        allow(Puppet::Util::Windows::Process).to receive(:elevated_security?).and_raise(Puppet::Util::Windows::Error, \"Access denied.\")\n\n        expect { Puppet::Util::Windows::User.admin? }.to raise_error(Puppet::Util::Windows::Error, /Access denied./)\n      end\n    end\n\n    describe \"not in local administrators group\" do\n      before :each do\n        allow(Puppet::Util::Windows::User).to receive(:check_token_membership).and_return(false)\n      end\n\n      it \"should not be an admin if user is running with elevated privileges\" do\n        allow(Puppet::Util::Windows::Process).to receive(:elevated_security?).and_return(true)\n\n        expect(Puppet::Util::Windows::User).not_to be_admin\n      end\n\n      it \"should not be an admin if user is not running with elevated privileges\" do\n        allow(Puppet::Util::Windows::Process).to receive(:elevated_security?).and_return(false)\n\n        expect(Puppet::Util::Windows::User).not_to be_admin\n      end\n    end\n  end\n\n  describe \"module function\" do\n    let(:username) { 'fabio' }\n    let(:bad_password) { 'goldilocks' }\n    let(:logon_fail_msg) { /Failed to logon user \"fabio\":  Logon failure: unknown user name or bad password./ }\n\n    def expect_logon_failure_error(&block)\n      expect {\n        yield\n      }.to raise_error { |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        # https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx\n        # ERROR_LOGON_FAILURE 1326\n        expect(error.code).to eq(1326)\n      }\n    end\n\n    describe \"load_profile\" do\n      it \"should raise an error when provided with an incorrect username and password\" do\n        expect_logon_failure_error {\n          Puppet::Util::Windows::User.load_profile(username, bad_password)\n        }\n      end\n\n      it \"should raise an error when provided with an incorrect username and nil password\" do\n        expect_logon_failure_error {\n          Puppet::Util::Windows::User.load_profile(username, nil)\n        }\n      end\n    end\n\n    describe \"logon_user\" do\n      let(:fLOGON32_PROVIDER_DEFAULT) {0}\n      let(:fLOGON32_LOGON_INTERACTIVE) {2}\n      let(:fLOGON32_LOGON_NETWORK) {3}\n      let(:token) {'test'}\n      let(:user) {'test'}\n      let(:passwd) {'test'}\n      it \"should raise an error when provided with an incorrect username and password\" do\n        expect_logon_failure_error {\n          Puppet::Util::Windows::User.logon_user(username, bad_password)\n        }\n      end\n\n      it \"should raise an error when provided with an incorrect username and nil password\" do\n        expect_logon_failure_error {\n          Puppet::Util::Windows::User.logon_user(username, nil)\n        }\n      end\n\n      it 'should raise error given that logon returns false' do\n        allow(Puppet::Util::Windows::User).to receive(:logon_user_by_logon_type).with(\n            user, '.', passwd, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, anything).and_return (0)\n        allow(Puppet::Util::Windows::User).to receive(:logon_user_by_logon_type).with(\n            user, '.', passwd, fLOGON32_LOGON_INTERACTIVE, fLOGON32_PROVIDER_DEFAULT, anything).and_return(0)\n\n        expect {Puppet::Util::Windows::User.logon_user(user, passwd) {}}\n            .to raise_error(Puppet::Util::Windows::Error, /Failed to logon user/)\n      end\n    end\n\n    describe \"password_is?\" do\n      it \"should return false given an incorrect username and password\" do\n        expect(Puppet::Util::Windows::User.password_is?(username, bad_password)).to be_falsey\n      end\n\n      it \"should return false given a nil username and an incorrect password\" do\n        expect(Puppet::Util::Windows::User.password_is?(nil, bad_password)).to be_falsey\n      end\n\n      context \"with a correct password\" do\n        it \"should return true even if account restrictions are in place \" do\n          error = Puppet::Util::Windows::Error.new('', Puppet::Util::Windows::User::ERROR_ACCOUNT_RESTRICTION)\n          allow(Puppet::Util::Windows::User).to receive(:logon_user).and_raise(error)\n          expect(Puppet::Util::Windows::User.password_is?(username, 'p@ssword')).to be(true)\n        end\n\n        it \"should return true even for an account outside of logon hours\" do\n          error = Puppet::Util::Windows::Error.new('', Puppet::Util::Windows::User::ERROR_INVALID_LOGON_HOURS)\n          allow(Puppet::Util::Windows::User).to receive(:logon_user).and_raise(error)\n          expect(Puppet::Util::Windows::User.password_is?(username, 'p@ssword')).to be(true)\n        end\n\n        it \"should return true even for an account not allowed to log into this workstation\" do\n          error = Puppet::Util::Windows::Error.new('', Puppet::Util::Windows::User::ERROR_INVALID_WORKSTATION)\n          allow(Puppet::Util::Windows::User).to receive(:logon_user).and_raise(error)\n          expect(Puppet::Util::Windows::User.password_is?(username, 'p@ssword')).to be(true)\n        end\n\n        it \"should return true even for a disabled account\" do\n          error = Puppet::Util::Windows::Error.new('', Puppet::Util::Windows::User::ERROR_ACCOUNT_DISABLED)\n          allow(Puppet::Util::Windows::User).to receive(:logon_user).and_raise(error)\n          expect(Puppet::Util::Windows::User.password_is?(username, 'p@ssword')).to be(true)\n        end\n      end\n    end\n\n    describe \"check_token_membership\" do\n      it \"should not raise an error\" do\n        # added just to call an FFI code path on all platforms\n        expect { Puppet::Util::Windows::User.check_token_membership }.not_to raise_error\n      end\n    end\n\n    describe \"default_system_account?\" do\n      it \"should succesfully identify 'SYSTEM' user as a default system account\" do\n        allow(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('SYSTEM').and_return(Puppet::Util::Windows::SID::LocalSystem)\n        expect(Puppet::Util::Windows::User.default_system_account?('SYSTEM')).to eq(true)\n      end\n\n      it \"should succesfully identify 'NETWORK SERVICE' user as a default system account\" do\n        allow(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('NETWORK SERVICE').and_return(Puppet::Util::Windows::SID::NtNetwork)\n        expect(Puppet::Util::Windows::User.default_system_account?('NETWORK SERVICE')).to eq(true)\n      end\n\n      it \"should succesfully identify 'LOCAL SERVICE' user as a default system account\" do\n        allow(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('LOCAL SERVICE').and_return(Puppet::Util::Windows::SID::NtLocal)\n        expect(Puppet::Util::Windows::User.default_system_account?('LOCAL SERVICE')).to eq(true)\n      end\n\n      it \"should not identify user with unknown sid as a default system account\" do\n        allow(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('UnknownUser').and_return(Puppet::Util::Windows::SID::Null)\n        expect(Puppet::Util::Windows::User.default_system_account?('UnknownUser')).to eq(false)\n      end\n    end\n\n    describe \"localsystem?\" do\n      before do\n        allow(Puppet::Util::Windows::ADSI).to receive(:computer_name).and_return(\"myPC\")\n      end\n\n      ['LocalSystem', '.\\LocalSystem', 'myPC\\LocalSystem', 'lOcALsysTem'].each do |input|\n        it \"should succesfully identify #{input} as the 'LocalSystem' account\" do\n          expect(Puppet::Util::Windows::User.localsystem?(input)).to eq(true)\n        end\n      end\n\n      it \"should not identify any other user as the 'LocalSystem' account\" do\n        expect(Puppet::Util::Windows::User.localsystem?('OtherUser')).to eq(false)\n      end\n    end\n\n    describe \"get_rights\" do\n      it \"should be empty when given user does not exist\" do\n        allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('NonExistingUser').and_return(nil)\n        expect(Puppet::Util::Windows::User.get_rights('NonExistingUser')).to eq(\"\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/util_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\n\ndescribe Puppet::Util do\n  include PuppetSpec::Files\n\n  describe \"#replace_file on Windows\", :if => Puppet::Util::Platform.windows? do\n    it \"replace_file should preserve original ACEs from existing replaced file on Windows\" do\n\n      file = tmpfile(\"somefile\")\n      FileUtils.touch(file)\n\n      admins = 'S-1-5-32-544'\n      dacl = Puppet::Util::Windows::AccessControlList.new\n      dacl.allow(admins, Puppet::Util::Windows::File::FILE_ALL_ACCESS)\n      protect = true\n      expected_sd = Puppet::Util::Windows::SecurityDescriptor.new(admins, admins, dacl, protect)\n      Puppet::Util::Windows::Security.set_security_descriptor(file, expected_sd)\n\n      ignored_mode = 0644\n      Puppet::Util.replace_file(file, ignored_mode) do |temp_file|\n        ignored_sd = Puppet::Util::Windows::Security.get_security_descriptor(temp_file.path)\n        users = 'S-1-5-11'\n        ignored_sd.dacl.allow(users, Puppet::Util::Windows::File::FILE_GENERIC_READ)\n        Puppet::Util::Windows::Security.set_security_descriptor(temp_file.path, ignored_sd)\n      end\n\n      replaced_sd = Puppet::Util::Windows::Security.get_security_descriptor(file)\n\n      expect(replaced_sd.dacl).to eq(expected_sd.dacl)\n    end\n\n    it \"replace_file should use reasonable default ACEs on a new file on Windows\" do\n\n      dir = tmpdir('DACL_playground')\n      protected_sd = Puppet::Util::Windows::Security.get_security_descriptor(dir)\n      protected_sd.protect = true\n      Puppet::Util::Windows::Security.set_security_descriptor(dir, protected_sd)\n\n      sibling_path = File.join(dir, 'sibling_file')\n      FileUtils.touch(sibling_path)\n\n      expected_sd = Puppet::Util::Windows::Security.get_security_descriptor(sibling_path)\n\n      new_file_path = File.join(dir, 'new_file')\n\n      ignored_mode = nil\n      Puppet::Util.replace_file(new_file_path, ignored_mode) { |tmp_file| }\n\n      new_sd = Puppet::Util::Windows::Security.get_security_descriptor(new_file_path)\n\n      expect(new_sd.dacl).to eq(expected_sd.dacl)\n    end\n\n    it \"replace_file should work with filenames that include - and . (PUP-1389)\" do\n      expected_content = 'some content'\n      dir = tmpdir('ReplaceFile_playground')\n      destination_file = File.join(dir, 'some-file.xml')\n\n      Puppet::Util.replace_file(destination_file, nil) do |temp_file|\n          temp_file.open\n          temp_file.write(expected_content)\n      end\n\n      actual_content = File.read(destination_file)\n      expect(actual_content).to eq(expected_content)\n    end\n\n    it \"replace_file should work with filenames that include special characters (PUP-1389)\" do\n      expected_content = 'some content'\n      dir = tmpdir('ReplaceFile_playground')\n      # http://www.fileformat.info/info/unicode/char/00e8/index.htm\n      # dest_name = \"somèfile.xml\"\n      dest_name = \"som\\u00E8file.xml\"\n      destination_file = File.join(dir, dest_name)\n\n      Puppet::Util.replace_file(destination_file, nil) do |temp_file|\n          temp_file.open\n          temp_file.write(expected_content)\n      end\n\n      actual_content = File.read(destination_file)\n      expect(actual_content).to eq(expected_content)\n    end\n  end\n\n  describe \"#which on Windows\", :if => Puppet::Util::Platform.windows? do\n    let (:rune_utf8) { \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\" }\n    let (:filename) { 'foo.exe' }\n\n    it \"should be able to use UTF8 characters in the path\" do\n      utf8 = tmpdir(rune_utf8)\n      Puppet::FileSystem.mkpath(utf8)\n\n      filepath = File.join(utf8, filename)\n      Puppet::FileSystem.touch(filepath)\n\n      path = [utf8, \"c:\\\\windows\\\\system32\", \"c:\\\\windows\"].join(File::PATH_SEPARATOR)\n      Puppet::Util.withenv(\"PATH\" => path) do\n        expect(Puppet::Util.which(filename)).to eq(filepath)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/containment_matchers.rb",
    "content": "module ContainmentMatchers\n  class ContainClass\n    def initialize(containee)\n      @containee = containee\n    end\n\n    def in(container)\n      @container = container\n      self\n    end\n\n    def matches?(catalog)\n      @catalog = catalog\n\n      raise ArgumentError, \"You must set the container using #in\" unless @container\n\n      @container_resource = catalog.resource(\"Class\", @container)\n      @containee_resource = catalog.resource(\"Class\", @containee)\n\n      if @containee_resource && @container_resource\n        catalog.edge?(@container_resource, @containee_resource)\n      else\n        false\n      end\n    end\n\n    def failure_message\n      message = \"Expected #{@catalog.to_dot} to contain Class #{@containee.inspect} inside of Class #{@container.inspect} but \"\n\n      missing = []\n      if @container_resource.nil?\n        missing << @container\n      end\n      if @containee_resource.nil?\n        missing << @containee\n      end\n\n      if ! missing.empty?\n        message << \"the catalog does not contain #{missing.map(&:inspect).join(' or ')}\"\n      else\n        message << \"no containment relationship exists\"\n      end\n\n      message\n    end\n  end\n\n  # expect(catalog).to contain_class(containee).in(container)\n  def contain_class(containee)\n    ContainClass.new(containee)\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/include_in_order.rb",
    "content": "RSpec::Matchers.define :include_in_order do |*expected|\n\n  match do |actual|\n    elements = expected.dup\n    actual.each do |elt|\n      if elt == elements.first\n        elements.shift\n      end\n    end\n    elements.empty?\n  end\n\n  def failure_message\n    \"expected #{@actual.inspect} to include#{expected} in order\"\n  end\n\n  def failure_message_when_negated\n    \"expected #{@actual.inspect} not to include#{expected} in order\"\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/include_in_order_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/include_in_order'\n\ndescribe \"Matching whether elements are included in order\" do\n  context \"an empty array\" do\n    it \"is included in an empty array\" do\n      expect([]).to include_in_order()\n    end\n\n    it \"is included in a non-empty array\" do\n      expect([1]).to include_in_order()\n    end\n  end\n\n  it \"[1,2,3] is included in [0,1,2,3,4]\" do\n    expect([0,1,2,3,4]).to include_in_order(1,2,3)\n  end\n\n  it \"[2,1] is not included in order in [1,2]\" do\n    expect([1,2]).not_to include_in_order(2,1)\n  end\n\n  it \"[2,4,6] is included in order in [1,2,3,4,5,6]\" do\n    expect([1,2,3,4,5,6]).to include_in_order(2,4,6)\n  end\n\n  it \"overlapping ordered array is not included\" do\n    expect([1,2,3]).not_to include_in_order(2,3,4)\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/json.rb",
    "content": "module JSONMatchers\n  class SetJsonAttribute\n    def initialize(attributes)\n      @attributes = attributes\n    end\n\n    def format\n      @format ||= Puppet::Network::FormatHandler.format('json')\n    end\n\n    def json(instance)\n      Puppet::Util::Json.load(instance.to_json)\n    end\n\n    def attr_value(attrs, instance)\n      attrs = attrs.dup\n      hash = json(instance)\n      while attrs.length > 0\n        name = attrs.shift\n        hash = hash[name]\n      end\n      hash\n    end\n\n    def to(value)\n      @value = value\n      self\n    end\n\n    def matches?(instance)\n      @instance = instance\n      result = attr_value(@attributes, instance)\n      if @value\n        result == @value\n      else\n        ! result.nil?\n      end\n    end\n\n    def failure_message\n      if @value\n        \"expected #{@instance.inspect} to set #{@attributes.inspect} to #{@value.inspect}; got #{attr_value(@attributes, @instance).inspect}\"\n      else\n        \"expected #{@instance.inspect} to set #{@attributes.inspect} but was nil\"\n      end\n    end\n\n    def failure_message_when_negated\n      if @value\n        \"expected #{@instance.inspect} not to set #{@attributes.inspect} to #{@value.inspect}\"\n      else\n        \"expected #{@instance.inspect} not to set #{@attributes.inspect} to nil\"\n      end\n    end\n  end\n\n  class ReadJsonAttribute\n    def initialize(attribute)\n      @attribute = attribute\n    end\n\n    def format\n      @format ||= Puppet::Network::FormatHandler.format('json')\n    end\n\n    def from(value)\n      @json = value\n      self\n    end\n\n    def as(as)\n      @value = as\n      self\n    end\n\n    def matches?(klass)\n      raise \"Must specify json with 'from'\" unless @json\n      @klass = klass\n      @instance = format.intern(klass, @json)\n      if @value\n        @instance.send(@attribute) == @value\n      else\n        ! @instance.send(@attribute).nil?\n      end\n    end\n\n    def failure_message\n      if @value\n        \"expected #{@klass} to read #{@attribute} from #{@json} as #{@value.inspect}; got #{@instance.send(@attribute).inspect}\"\n      else\n        \"expected #{@klass} to read #{@attribute} from #{@json} but was nil\"\n      end\n    end\n\n    def failure_message_when_negated\n      if @value\n        \"expected #{@klass} not to set #{@attribute} to #{@value}\"\n      else\n        \"expected #{@klass} not to set #{@attribute} to nil\"\n      end\n    end\n  end\n\n  require 'puppet/util/json'\n  require 'json-schema'\n\n  class SchemaMatcher\n    JSON_META_SCHEMA = Puppet::Util::Json.load(File.read('api/schemas/json-meta-schema.json'))\n\n    def initialize(schema)\n      @schema = schema\n    end\n\n    def matches?(json)\n      JSON::Validator.validate!(JSON_META_SCHEMA, @schema)\n      JSON::Validator.validate!(@schema, json)\n    end\n  end\n\n  def validate_against(schema_file)\n    schema = Puppet::Util::Json.load(File.read(schema_file))\n    SchemaMatcher.new(schema)\n  end\n\n  def set_json_attribute(*attributes)\n    SetJsonAttribute.new(attributes)\n  end\n\n  def read_json_attribute(attribute)\n    ReadJsonAttribute.new(attribute)\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/match_tokens2.rb",
    "content": "# Matches tokens produced by lexer\n# The given exepected is one or more entries where an entry is one of\n# - a token symbol\n# - an Array with a token symbol and the text value\n# - an Array with a token symbol and a Hash specifying all attributes of the token\n# - nil (ignore)\n#\nRSpec::Matchers.define :match_tokens2 do | *expected |\n  match do | actual |\n    expected.zip(actual).all? do |e, a|\n      compare(e, a)\n    end\n  end\n\n  def failure_message\n    msg = [\"Expected (#{expected.size}):\"]\n    expected.each {|e| msg << e.to_s }\n\n    zipped = expected.zip(actual)\n    msg << \"\\nGot (#{actual.size}):\"\n    actual.each_with_index do |e, idx|\n      if zipped[idx]\n        zipped_expected = zipped[idx][0]\n        zipped_actual = zipped[idx][1]\n\n        prefix = compare(zipped_expected, zipped_actual) ? ' ' : '*'\n        msg2 = [\"#{prefix}[:\"]\n        msg2 << e[0].to_s\n        msg2 << ', '\n        if e[1] == false\n          msg2 << 'false'\n        else\n          msg2 << e[1][:value].to_s.dump\n        end\n        # If expectation has options, output them\n        if zipped_expected.is_a?(Array) && zipped_expected[2] && zipped_expected[2].is_a?(Hash)\n          msg2 << \", {\"\n          msg3 = []\n          zipped_expected[2].each do |k,v|\n            prefix = e[1][k] != v ? \"*\" : ''\n            msg3 << \"#{prefix}:#{k}=>#{e[1][k]}\"\n          end\n          msg2 << msg3.join(\", \")\n          msg2 << \"}\"\n        end\n        msg2 << ']'\n        msg << msg2.join('')\n      end\n    end\n    msg.join(\"\\n\")\n  end\n\n  def compare(e, a)\n    # if expected ends before actual\n    return true if !e\n\n    # If actual ends before expected\n    return false if !a\n\n    # Simple - only expect token to match\n    return true if a[0] == e\n\n    # Expect value and optional attributes to match\n    if e.is_a? Array\n      # tokens must match\n      return false unless a[0] == e[0]\n      if e[2].is_a?(Hash)\n        e[2].each {|k,v| return false unless a[1][k] == v }\n      end\n      return (a[1] == e[1] || (a[1][:value] == e[1]))\n    end\n    false\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/relationship_graph_matchers.rb",
    "content": "module RelationshipGraphMatchers\n  class EnforceOrderWithEdge\n    def initialize(before, after)\n      @before = before\n      @after = after\n    end\n\n    def matches?(actual_graph)\n      @actual_graph = actual_graph\n\n      @reverse_edge = actual_graph.edge?(\n          vertex_called(actual_graph, @after),\n          vertex_called(actual_graph, @before))\n\n      @forward_edge = actual_graph.edge?(\n          vertex_called(actual_graph, @before),\n          vertex_called(actual_graph, @after))\n\n      @forward_edge && !@reverse_edge\n    end\n\n    def failure_message\n      \"expect #{@actual_graph.to_dot_graph} to only contain an edge from #{@before} to #{@after} but #{[forward_failure_message, reverse_failure_message].compact.join(' and ')}\"\n    end\n\n    def forward_failure_message\n      if !@forward_edge\n        \"did not contain an edge from #{@before} to #{@after}\"\n      end\n    end\n\n    def reverse_failure_message\n      if @reverse_edge\n        \"contained an edge from #{@after} to #{@before}\"\n      end\n    end\n\n    private\n\n    def vertex_called(graph, name)\n      graph.vertices.find { |v| v.ref =~ /#{Regexp.escape(name)}/ }\n    end\n  end\n\n  def enforce_order_with_edge(before, after)\n    EnforceOrderWithEdge.new(before, after)\n  end\nend\n"
  },
  {
    "path": "spec/lib/matchers/resource.rb",
    "content": "module Matchers; module Resource\n  extend RSpec::Matchers::DSL\n\n  matcher :have_resource do |expected_resource|\n    def resource_match(expected_resource, actual_resource)\n      matched = true\n      failures = []\n\n      if actual_resource.ref != expected_resource\n        matched = false\n        failures << \"expected #{expected_resource} but was #{actual_resource.ref}\"\n      end\n\n      @params ||= {}\n      @params.each do |name, value|\n        case value\n        when RSpec::Matchers::DSL::Matcher\n          if !value.matches?(actual_resource[name])\n            matched = false\n            failures << \"expected #{name} to match '#{value.description}' but was '#{actual_resource[name]}'\"\n          end\n        else\n          if actual_resource[name] != value\n            matched = false\n            failures << \"expected #{name} to be '#{value}' but was '#{actual_resource[name]}'\"\n          end\n        end\n      end\n      @mismatch = failures.join(\"\\n\")\n\n      matched\n    end\n\n    match do |actual_catalog|\n      @mismatch = \"\"\n      if resource = actual_catalog.resource(expected_resource)\n        resource_match(expected_resource, resource)\n      else\n        @mismatch = \"expected #{@actual.to_dot} to include #{expected_resource}\"\n        false\n      end\n    end\n\n    chain :with_parameter do |name, value|\n      @params ||= {}\n      @params[name] = value\n    end\n\n    def failure_message\n      @mismatch\n    end\n  end\n\n\n  matcher :be_resource do |expected_resource|\n    def resource_match(expected_resource, actual_resource)\n      if actual_resource.ref == expected_resource\n        true\n      else\n        @mismatch = \"expected #{expected_resource} but was #{actual_resource.ref}\"\n        false\n      end\n    end\n\n    match do |actual_resource|\n      resource_match(expected_resource, actual_resource)\n    end\n\n    def failure_message\n      @mismatch\n    end\n  end\n\nend; end\n"
  },
  {
    "path": "spec/lib/puppet/certificate_factory.rb",
    "content": "require 'puppet/ssl'\n\n# This class encapsulates the logic of creating and adding extensions to X509\n# certificates.\n#\n# @api private\nmodule Puppet::CertificateFactory\n\n  # Create a new X509 certificate and add any needed extensions to the cert.\n  #\n  # @param cert_type [Symbol] The certificate type to create, which specifies\n  #   what extensions are added to the certificate.\n  #   One of (:ca, :terminalsubca, :server, :ocsp, :client)\n  # @param csr [Puppet::SSL::CertificateRequest] The signing request associated with\n  #   the certificate being created.\n  # @param issuer [OpenSSL::X509::Certificate, OpenSSL::X509::Request] An X509 CSR\n  #   if this is a self signed certificate, or the X509 certificate of the CA if\n  #   this is a CA signed certificate.\n  # @param serial [Integer] The serial number for the given certificate, which\n  #   MUST be unique for the given CA.\n  # @param ttl [String] The duration of the validity for the given certificate.\n  #\n  # @api public\n  #\n  # @return [OpenSSL::X509::Certificate]\n  def self.build(cert_type, csr, issuer, serial, ttl = 3600)\n    # Work out if we can even build the requested type of certificate.\n    build_extensions = \"build_#{cert_type}_extensions\"\n    respond_to?(build_extensions) or\n      raise ArgumentError, _(\"%{cert_type} is an invalid certificate type!\") % { cert_type: cert_type }\n\n    raise ArgumentError, _(\"Certificate TTL must be an integer\") unless ttl.nil? || ttl.is_a?(Integer)\n\n    # set up the certificate, and start building the content.\n    cert = OpenSSL::X509::Certificate.new\n\n    cert.version    = 2 # X509v3\n    cert.subject    = csr.content.subject\n    cert.issuer     = issuer.subject\n    cert.public_key = csr.content.public_key\n    cert.serial     = serial\n\n    # Make the certificate valid as of yesterday, because so many people's\n    # clocks are out of sync.  This gives one more day of validity than people\n    # might expect, but is better than making every person who has a messed up\n    # clock fail, and better than having every cert we generate expire a day\n    # before the user expected it to when they asked for \"one year\".\n    cert.not_before = Time.now - (60*60*24)\n    cert.not_after  = Time.now + ttl\n\n    add_extensions_to(cert, csr, issuer, send(build_extensions))\n\n    return cert\n  end\n\n  # Add X509v3 extensions to the given certificate.\n  #\n  # @param cert [OpenSSL::X509::Certificate] The certificate to add the\n  #   extensions to.\n  # @param csr [OpenSSL::X509::Request] The CSR associated with the given\n  #   certificate, which may specify requested extensions for the given cert.\n  #   See https://tools.ietf.org/html/rfc2985 Section 5.4.2 Extension request\n  # @param issuer [OpenSSL::X509::Certificate, OpenSSL::X509::Request] An X509 CSR\n  #   if this is a self signed certificate, or the X509 certificate of the CA if\n  #   this is a CA signed certificate.\n  # @param extensions [Hash<String, Array<String> | String>] The extensions to\n  #   add to the certificate, based on the certificate type being created (CA,\n  #   server, client, etc)\n  #\n  # @api private\n  #\n  # @return [void]\n  def self.add_extensions_to(cert, csr, issuer, extensions)\n    ef = OpenSSL::X509::ExtensionFactory.new\n    ef.subject_certificate = cert\n    ef.issuer_certificate  = issuer.is_a?(OpenSSL::X509::Request) ? cert : issuer\n\n    # Extract the requested extensions from the CSR.\n    requested_exts = csr.request_extensions.inject({}) do |hash, re|\n      hash[re[\"oid\"]] = [re[\"value\"], re[\"critical\"]]\n      hash\n    end\n\n    # Produce our final set of extensions.  We deliberately order these to\n    # build the way we want:\n    # 1. \"safe\" default values, like the comment, that no one cares about.\n    # 2. request extensions, from the CSR\n    # 3. extensions based on the type we are generating\n    # 4. overrides, which we always want to have in their form\n    #\n    # This ordering *is* security-critical, but we want to allow the user\n    # enough rope to shoot themselves in the foot, if they want to ignore our\n    # advice and externally approve a CSR that sets the basicConstraints.\n    #\n    # Swapping the order of 2 and 3 would ensure that you couldn't slip a\n    # certificate through where the CA constraint was true, though, if\n    # something went wrong up there. --daniel 2011-10-11\n    defaults = { \"nsComment\" => \"Puppet Ruby/OpenSSL Internal Certificate\" }\n\n    # See https://www.openssl.org/docs/apps/x509v3_config.html\n    # for information about the special meanings of 'hash', 'keyid', 'issuer'\n    override = {\n      \"subjectKeyIdentifier\"   => \"hash\",\n      \"authorityKeyIdentifier\" => \"keyid,issuer\"\n    }\n\n    exts = [defaults, requested_exts, extensions, override].\n      inject({}) {|ret, val| ret.merge(val) }\n\n    cert.extensions = exts.map do |oid, val|\n      generate_extension(ef, oid, *val)\n    end\n  end\n  private_class_method :add_extensions_to\n\n  # Woot! We're a CA.\n  def self.build_ca_extensions\n    {\n      # This was accidentally omitted in the previous version of this code: an\n      # effort was made to add it last, but that actually managed to avoid\n      # adding it to the certificate at all.\n      #\n      # We have some sort of bug, which means that when we add it we get a\n      # complaint that the issuer keyid can't be fetched, which breaks all\n      # sorts of things in our test suite and, e.g., bootstrapping the CA.\n      #\n      # https://tools.ietf.org/html/rfc5280#section-4.2.1.1 says that, to be a\n      # conforming CA we MAY omit the field if we are self-signed, which I\n      # think gives us a pass in the specific case.\n      #\n      # It also notes that we MAY derive the ID from the subject and serial\n      # number of the issuer, or from the key ID, and we definitely have the\n      # former data, should we want to restore this...\n      #\n      # Anyway, preserving this bug means we don't risk breaking anything in\n      # the field, even though it would be nice to have. --daniel 2011-10-11\n      #\n      # \"authorityKeyIdentifier\" => \"keyid:always,issuer:always\",\n      \"keyUsage\"               => [%w{cRLSign keyCertSign}, true],\n      \"basicConstraints\"       => [\"CA:TRUE\", true],\n    }\n  end\n\n  # We're a terminal CA, probably not self-signed.\n  def self.build_terminalsubca_extensions\n    {\n      \"keyUsage\"         => [%w{cRLSign keyCertSign}, true],\n      \"basicConstraints\" => [\"CA:TRUE,pathlen:0\", true],\n    }\n  end\n\n  # We're a normal server.\n  def self.build_server_extensions\n    {\n      \"keyUsage\"         => [%w{digitalSignature keyEncipherment}, true],\n      \"extendedKeyUsage\" => [%w{serverAuth clientAuth}, true],\n      \"basicConstraints\" => [\"CA:FALSE\", true],\n    }\n  end\n\n  # Um, no idea.\n  def self.build_ocsp_extensions\n    {\n      \"keyUsage\"         => [%w{nonRepudiation digitalSignature}, true],\n      \"extendedKeyUsage\" => [%w{serverAuth OCSPSigning}, true],\n      \"basicConstraints\" => [\"CA:FALSE\", true],\n    }\n  end\n\n  # Normal client.\n  def self.build_client_extensions\n    {\n      \"keyUsage\"         => [%w{nonRepudiation digitalSignature keyEncipherment}, true],\n      # We don't seem to use this, but that seems much more reasonable here...\n      \"extendedKeyUsage\" => [%w{clientAuth emailProtection}, true],\n      \"basicConstraints\" => [\"CA:FALSE\", true],\n      \"nsCertType\"       => \"client,email\",\n    }\n  end\n\n  # Generate an extension with the given OID, value, and critical state\n  #\n  # @param oid [String] The numeric value or short name of a given OID. X509v3\n  #   extensions must be passed by short name or long name, while custom\n  #   extensions may be passed by short name, long name, oid numeric OID.\n  # @param ef [OpenSSL::X509::ExtensionFactory] The extension factory to use\n  #   when generating the extension.\n  # @param val [String, Array<String>] The extension value.\n  # @param crit [true, false] Whether the given extension is critical, defaults\n  #   to false.\n  #\n  # @return [OpenSSL::X509::Extension]\n  #\n  # @api private\n  def self.generate_extension(ef, oid, val, crit = false)\n\n    val = val.join(', ') unless val.is_a? String\n\n    # Enforce the X509v3 rules about subjectAltName being critical:\n    # specifically, it SHOULD NOT be critical if we have a subject, which we\n    # always do. --daniel 2011-10-18\n    crit = false if oid == \"subjectAltName\"\n\n    if Puppet::SSL::Oids.subtree_of?('id-ce', oid) or Puppet::SSL::Oids.subtree_of?('id-pkix', oid)\n      # Attempt to create a X509v3 certificate extension. Standard certificate\n      # extensions may need access to the associated subject certificate and\n      # issuing certificate, so must be created by the OpenSSL::X509::ExtensionFactory\n      # which provides that context.\n      ef.create_ext(oid, val, crit)\n    else\n      # This is not an X509v3 extension which means that the extension\n      # factory cannot generate it. We need to generate the extension\n      # manually.\n      OpenSSL::X509::Extension.new(oid, OpenSSL::ASN1::UTF8String.new(val).to_der, crit)\n    end\n  end\n  private_class_method :generate_extension\nend\n"
  },
  {
    "path": "spec/lib/puppet/face/1.0.0/huzzah.rb",
    "content": "require 'puppet/face'\nPuppet::Face.define(:huzzah, '1.0.0') do\n  copyright \"Puppet Inc.\", 2011\n  license   \"Apache 2 license; see COPYING\"\n  summary \"life is a thing for celebration\"\n  action(:obsolete_in_core) { when_invoked { |_| \"you are in obsolete core now!\" } }\n  action(:call_newer) { when_invoked { |_| method_on_newer } }\nend\n"
  },
  {
    "path": "spec/lib/puppet/face/basetest.rb",
    "content": "require 'puppet/face'\n\nPuppet::Face.define(:basetest, '0.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   \"Apache 2 license; see COPYING\"\n  summary \"This is just so tests don't fail\"\n\n  option \"--[no-]boolean\"\n  option \"--mandatory ARGUMENT\"\n\n  action :foo do\n    option(\"--action\")\n    when_invoked do |*args| args.length end\n  end\n\n  action :return_true do\n    summary \"just returns true\"\n    when_invoked do |options| true end\n  end\n\n  action :return_false do\n    summary \"just returns false\"\n    when_invoked do |options| false end\n  end\n\n  action :return_nil do\n    summary \"just returns nil\"\n    when_invoked do |options| nil end\n  end\n\n  action :raise do\n    summary \"just raises an exception\"\n    when_invoked do |options| raise ArgumentError, \"your failure\" end\n  end\n\n  action :with_s_rendering_hook do\n    summary \"has a rendering hook for 's'\"\n    when_invoked do |options| \"this is not the hook you are looking for\" end\n    when_rendering :s do |value| \"you invoked the 's' rendering hook\" end\n  end\n\n  action :count_args do\n    summary \"return the count of arguments given\"\n    when_invoked do |*args| args.length - 1 end\n  end\n\n  action :with_specific_exit_code do\n    summary \"just call exit with the desired exit code\"\n    when_invoked do |options| exit(5) end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet/face/huzzah/obsolete.rb",
    "content": "Puppet::Face.define(:huzzah, '1.0.0') do\n  action :obsolete do\n    summary \"This is an action on version 1.0.0 of the face\"\n    when_invoked do |options| options end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet/face/huzzah.rb",
    "content": "require 'puppet/face'\nPuppet::Face.define(:huzzah, '2.0.1') do\n  copyright \"Puppet Inc.\", 2011\n  license   \"Apache 2 license; see COPYING\"\n  summary \"life is a thing for celebration\"\n  action(:bar) { when_invoked { |options| \"is where beer comes from\" } }\n  action(:call_older) { when_invoked { |_| method_on_older } }\nend\n"
  },
  {
    "path": "spec/lib/puppet/face/version_matching.rb",
    "content": "require 'puppet/face'\n\n# The set of versions here are used explicitly in the interface_spec; if you\n# change this you need to ensure that is still correct. --daniel 2011-04-21\n['1.0.0', '1.0.1', '1.1.0', '1.1.1', '2.0.0'].each do |version|\n  Puppet::Face.define(:version_matching, version) do\n    copyright \"Puppet Inc.\", 2011\n    license   \"Apache 2 license; see COPYING\"\n    summary \"version matching face #{version}\"\n    action(:version) { when_invoked { |options| version } }\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet/indirector/indirector_testing/json.rb",
    "content": "require 'puppet/indirector_testing'\nrequire 'puppet/indirector/json'\n\nclass Puppet::IndirectorTesting::JSON < Puppet::Indirector::JSON\n  desc \"Testing the JSON indirector\"\nend\n"
  },
  {
    "path": "spec/lib/puppet/indirector/indirector_testing/memory.rb",
    "content": "require 'puppet/indirector/memory'\n\nclass Puppet::IndirectorTesting::Memory < Puppet::Indirector::Memory\n  def supports_remote_requests?\n    true\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet/indirector/indirector_testing/msgpack.rb",
    "content": "require 'puppet/indirector_testing'\nrequire 'puppet/indirector/msgpack'\n\nclass Puppet::IndirectorTesting::Msgpack < Puppet::Indirector::Msgpack\n  desc \"Testing the MessagePack indirector\"\nend\n"
  },
  {
    "path": "spec/lib/puppet/indirector_proxy.rb",
    "content": "class Puppet::IndirectorProxy\n  class ProxyId\n    attr_accessor :name\n    def initialize(name)\n      self.name = name\n    end\n  end\n\n  # We should have some way to identify if we got a valid object back with the\n  # current values, no?\n  attr_accessor :value, :proxyname\n  alias_method :name, :value\n  alias_method :name=, :value=\n  def initialize(value, proxyname)\n    self.value = value\n    self.proxyname = proxyname\n  end\n\n  def self.indirection\n    ProxyId.new(\"file_metadata\")\n  end\n\n  def self.from_binary(raw)\n    new(raw)\n  end\n\n  def self.from_data_hash(data)\n    new(data['value'])\n  end\n\n  def to_data_hash\n    { 'value' => value }\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet/indirector_testing.rb",
    "content": "require 'puppet/indirector'\n\nclass Puppet::IndirectorTesting\n  extend Puppet::Indirector\n  indirects :indirector_testing\n\n  # We should have some way to identify if we got a valid object back with the\n  # current values, no?\n  attr_accessor :value\n  alias_method :name, :value\n  alias_method :name=, :value=\n  def initialize(value)\n    self.value = value\n  end\n\n  def self.from_binary(raw)\n    new(raw)\n  end\n\n  def self.from_data_hash(data)\n    new(data['value'])\n  end\n\n  def to_binary\n    value\n  end\n\n  def to_data_hash\n    { 'value' => value }\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet/test_ca.rb",
    "content": "module Puppet\n  class TestCa\n\n    CERT_VALID_FROM = Time.at(0).freeze # 1969-12-31 16:00:00 -0800\n    CERT_VALID_UNTIL = (Time.now + (10 * 365 * 24 * 60 * 60)).freeze # 10 years from now\n\n    CA_EXTENSIONS = [\n      [\"basicConstraints\", \"CA:TRUE\", true],\n      [\"keyUsage\", \"keyCertSign, cRLSign\", true],\n      [\"subjectKeyIdentifier\", \"hash\", false],\n      [\"nsComment\", \"Puppet Server Internal Certificate\", false],\n      [\"authorityKeyIdentifier\", \"keyid:always\", false]\n    ].freeze\n\n    attr_reader :ca_cert, :ca_crl, :key\n\n    @serial = 0\n    def self.next_serial\n      id = @serial\n      @serial += 1\n      id\n    end\n\n    def initialize(name = 'Test CA')\n      @digest = OpenSSL::Digest::SHA256.new\n      info = create_cacert(name)\n      @key = info[:private_key]\n      @ca_cert = info[:cert]\n      @ca_crl = create_crl(@ca_cert, @key)\n    end\n\n    def create_request(name)\n      key = OpenSSL::PKey::RSA.new(2048)\n      csr = OpenSSL::X509::Request.new\n      csr.public_key = key.public_key\n      csr.subject = OpenSSL::X509::Name.new([[\"CN\", name]])\n      csr.version = 2\n      csr.sign(key, @digest)\n      { private_key: key, csr: csr }\n    end\n\n    def create_cert(name, issuer_cert, issuer_key, opts = {})\n      key, cert = build_cert(name, issuer_cert.subject, opts)\n      ef = extension_factory_for(issuer_cert, cert)\n      if opts[:subject_alt_names]\n        ext = ef.create_extension([\"subjectAltName\", opts[:subject_alt_names], false])\n        cert.add_extension(ext)\n      end\n      if exts = opts[:extensions]\n        exts.each do |e|\n          cert.add_extension(OpenSSL::X509::Extension.new(*e))\n        end\n      end\n      cert.sign(issuer_key, @digest)\n      { private_key: key, cert: cert }\n    end\n\n    def create_intermediate_cert(name, issuer_cert, issuer_key)\n      key, cert = build_cert(name, issuer_cert.subject)\n      ef = extension_factory_for(issuer_cert, cert)\n      CA_EXTENSIONS.each do |ext|\n        cert.add_extension(ef.create_extension(*ext))\n      end\n      cert.sign(issuer_key, @digest)\n      { private_key: key, cert: cert }\n    end\n\n    def create_cacert(name)\n      issuer = OpenSSL::X509::Name.new([[\"CN\", name]])\n      key, cert = build_cert(name, issuer)\n      ef = extension_factory_for(cert, cert)\n      CA_EXTENSIONS.each do |ext|\n        cert.add_extension(ef.create_extension(*ext))\n      end\n      cert.sign(key, @digest)\n      { private_key: key, cert: cert }\n    end\n\n    def create_crl(issuer_cert, issuer_key)\n      crl = OpenSSL::X509::CRL.new\n      crl.version = 1\n      crl.issuer = issuer_cert.subject\n      ef = extension_factory_for(issuer_cert)\n      crl.add_extension(\n        ef.create_extension([\"authorityKeyIdentifier\", \"keyid:always\", false]))\n      crl.add_extension(\n        OpenSSL::X509::Extension.new(\"crlNumber\", OpenSSL::ASN1::Integer(0)))\n      crl.last_update = CERT_VALID_FROM\n      crl.next_update = CERT_VALID_UNTIL\n      crl.sign(issuer_key, @digest)\n      crl\n    end\n\n    def sign(csr, opts = {})\n      cert = OpenSSL::X509::Certificate.new\n      cert.public_key = csr.public_key\n      cert.subject = csr.subject\n      cert.issuer = @ca_cert.subject\n      cert.version = 2\n      cert.serial = self.class.next_serial\n      cert.not_before = CERT_VALID_FROM\n      cert.not_after =  CERT_VALID_UNTIL\n      ef = extension_factory_for(@ca_cert, cert)\n      if opts[:subject_alt_names]\n        ext = ef.create_extension([\"subjectAltName\", opts[:subject_alt_names], false])\n        cert.add_extension(ext)\n      end\n      cert.sign(@key, @digest)\n      Puppet::SSL::Certificate.from_instance(cert)\n    end\n\n    def revoke(cert, crl = @crl, issuer_key = @key)\n      revoked = OpenSSL::X509::Revoked.new\n      revoked.serial = cert.serial\n      revoked.time = Time.now\n      enum = OpenSSL::ASN1::Enumerated(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE)\n      ext = OpenSSL::X509::Extension.new(\"CRLReason\", enum)\n      revoked.add_extension(ext)\n      crl.add_revoked(revoked)\n      crl.sign(issuer_key, @digest)\n    end\n\n    def generate(name, opts)\n      info = create_request(name)\n      cert = sign(info[:csr], opts).content\n      info.merge(cert: cert)\n    end\n\n    private\n\n    def build_cert(name, issuer, opts = {})\n      key = if opts[:key_type] == :ec\n              key = OpenSSL::PKey::EC.generate('prime256v1')\n            elsif opts[:reuse_key]\n              key = opts[:reuse_key]\n            else\n              key = OpenSSL::PKey::RSA.new(2048)\n            end\n      cert = OpenSSL::X509::Certificate.new\n      cert.public_key = key\n      cert.subject = OpenSSL::X509::Name.new([[\"CN\", name]])\n      cert.issuer = issuer\n      cert.version = 2\n      cert.serial = self.class.next_serial\n      cert.not_before = CERT_VALID_FROM\n      cert.not_after = CERT_VALID_UNTIL\n      [key, cert]\n    end\n\n    def extension_factory_for(ca, cert = nil)\n      ef = OpenSSL::X509::ExtensionFactory.new\n      ef.issuer_certificate  = ca\n      ef.subject_certificate = cert if cert\n      ef\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/character_encoding.rb",
    "content": "# A support module for testing character encoding\nmodule PuppetSpec::CharacterEncoding\n  def self.with_external_encoding(encoding, &blk)\n    original_encoding = Encoding.default_external\n    begin\n      Encoding.default_external = encoding\n      yield\n    ensure\n      Encoding.default_external = original_encoding\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/compiler.rb",
    "content": "module PuppetSpec::Compiler\n  module_function\n\n  def compile_to_catalog(string, node = Puppet::Node.new('test'))\n    Puppet[:code] = string\n    # see lib/puppet/indirector/catalog/compiler.rb#filter\n    Puppet::Parser::Compiler.compile(node).filter { |r| r.virtual? }\n  end\n\n  # Does not removed virtual resources in compiled catalog (i.e. keeps unrealized)\n  def compile_to_catalog_unfiltered(string, node = Puppet::Node.new('test'))\n    Puppet[:code] = string\n    # see lib/puppet/indirector/catalog/compiler.rb#filter\n    Puppet::Parser::Compiler.compile(node)\n  end\n\n  def compile_to_ral(manifest, node = Puppet::Node.new('test'))\n    catalog = compile_to_catalog(manifest, node)\n    ral = catalog.to_ral\n    ral.finalize\n    ral\n  end\n\n  def compile_to_relationship_graph(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new)\n    ral = compile_to_ral(manifest)\n    graph = Puppet::Graph::RelationshipGraph.new(prioritizer)\n    graph.populate_from(ral)\n    graph\n  end\n\n  def apply_compiled_manifest(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new)\n    catalog = compile_to_ral(manifest)\n    if block_given?\n      catalog.resources.each { |res| yield res }\n    end\n    transaction = Puppet::Transaction.new(catalog,\n                                         Puppet::Transaction::Report.new,\n                                         prioritizer)\n    transaction.evaluate\n    transaction.report.finalize_report\n\n    transaction\n  end\n\n  def apply_with_error_check(manifest)\n    apply_compiled_manifest(manifest) do |res|\n      expect(res).not_to receive(:err)\n    end\n  end\n\n  def order_resources_traversed_in(relationships)\n    order_seen = []\n    relationships.traverse { |resource| order_seen << resource.ref }\n    order_seen\n  end\n\n  def collect_notices(code, node = Puppet::Node.new('foonode'))\n    Puppet[:code] = code\n    compiler = Puppet::Parser::Compiler.new(node)\n    node.environment.check_for_reparse\n    logs = []\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      yield(compiler)\n    end\n    logs = logs.select { |log| log.level == :notice }.map { |log| log.message }\n    logs\n  end\n\n  def eval_and_collect_notices(code, node = Puppet::Node.new('foonode'), topscope_vars = {})\n    collect_notices(code, node) do |compiler|\n      unless topscope_vars.empty?\n        scope = compiler.topscope\n        topscope_vars.each {|k,v| scope.setvar(k, v) }\n      end\n      if block_given?\n        compiler.compile do |catalog|\n          yield(compiler.topscope, catalog)\n          catalog\n        end\n      else\n        compiler.compile\n      end\n    end\n  end\n\n  # Compiles a catalog, and if source is given evaluates it and returns its result.\n  # The catalog is returned if no source is given.\n  # Topscope variables are set before compilation\n  # Uses a created node 'testnode' if none is given.\n  # (Parameters given by name)\n  #\n  def evaluate(code: 'undef', source: nil, node: Puppet::Node.new('testnode'), variables: {})\n    source_location = caller[0]\n    Puppet[:code] = code\n    compiler = Puppet::Parser::Compiler.new(node)\n    unless variables.empty?\n      scope = compiler.topscope\n      variables.each {|k,v| scope.setvar(k, v) }\n    end\n\n    if source.nil?\n      compiler.compile\n      # see lib/puppet/indirector/catalog/compiler.rb#filter\n      return compiler.filter { |r| r.virtual? }\n    end\n\n    # evaluate given source is the context of the compiled state and return its result\n    compiler.compile do |catalog |\n      Puppet::Pops::Parser::EvaluatingParser.singleton.evaluate_string(compiler.topscope, source, source_location)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/files.rb",
    "content": "require 'fileutils'\nrequire 'tempfile'\nrequire 'tmpdir'\nrequire 'pathname'\n\n# A support module for testing files.\nmodule PuppetSpec::Files\n  def self.cleanup\n    $global_tempfiles ||= []\n    while path = $global_tempfiles.pop do\n      begin\n        FileUtils.rm_rf path, secure: true\n      rescue Errno::ENOENT\n        # nothing to do\n      end\n    end\n  end\n\n  module_function\n\n  def make_absolute(path)\n    path = File.expand_path(path)\n    path[0] = 'c' if Puppet::Util::Platform.windows?\n    path\n  end\n\n  def tmpfile(name, dir = nil)\n    dir ||= Dir.tmpdir\n    path = Puppet::FileSystem.expand_path(make_tmpname(name, nil).encode(Encoding::UTF_8), dir)\n    record_tmp(File.expand_path(path))\n\n    path\n  end\n\n  def file_containing(name, contents)\n    file = tmpfile(name)\n    File.open(file, 'wb') { |f| f.write(contents) }\n    file\n  end\n\n  def script_containing(name, contents)\n    file = tmpfile(name)\n    if Puppet::Util::Platform.windows?\n      file += '.bat'\n      text = contents[:windows]\n    else\n      text = contents[:posix]\n    end\n    File.open(file, 'wb') { |f| f.write(text) }\n    Puppet::FileSystem.chmod(0755, file)\n    file\n  end\n\n  def tmpdir(name)\n    dir = Puppet::FileSystem.expand_path(Dir.mktmpdir(name).encode!(Encoding::UTF_8))\n\n    record_tmp(dir)\n\n    dir\n  end\n\n  # Copied from ruby 2.4 source\n  def make_tmpname((prefix, suffix), n)\n    prefix = (String.try_convert(prefix) or\n              raise ArgumentError, \"unexpected prefix: #{prefix.inspect}\")\n    suffix &&= (String.try_convert(suffix) or\n                raise ArgumentError, \"unexpected suffix: #{suffix.inspect}\")\n    t = Time.now.strftime(\"%Y%m%d\")\n    path = \"#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}\".dup\n    path << \"-#{n}\" if n\n    path << suffix if suffix\n    path\n  end\n\n  def dir_containing(name, contents_hash)\n    dir_contained_in(tmpdir(name), contents_hash)\n  end\n\n  def dir_contained_in(dir, contents_hash)\n    contents_hash.each do |k,v|\n      if v.is_a?(Hash)\n        Dir.mkdir(tmp = File.join(dir,k))\n        dir_contained_in(tmp, v)\n      else\n        file = File.join(dir, k)\n        File.open(file, 'wb') {|f| f.write(v) }\n      end\n    end\n    dir\n  end\n\n  def record_tmp(tmp)\n    # ...record it for cleanup,\n    $global_tempfiles ||= []\n    $global_tempfiles << tmp\n  end\n\n  def expect_file_mode(file, mode)\n    actual_mode = \"%o\" % Puppet::FileSystem.stat(file).mode\n    target_mode = if Puppet::Util::Platform.windows?\n      mode\n    else\n      \"10\" + \"%04i\" % mode.to_i\n    end\n    expect(actual_mode).to eq(target_mode)\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/fixtures.rb",
    "content": "module PuppetSpec::Fixtures\n  def fixtures(*rest)\n    File.join(PuppetSpec::FIXTURE_DIR, *rest)\n  end\n  def my_fixture_dir\n    callers = caller\n    while line = callers.shift do\n      next unless found = line.match(%r{/spec/(.*)_spec\\.rb:})\n      return fixtures(found[1])\n    end\n    fail \"sorry, I couldn't work out your path from the caller stack!\"\n  end\n  def my_fixture(name)\n    file = File.join(my_fixture_dir, name)\n    unless File.readable? file then\n      fail Puppet::DevError, \"fixture '#{name}' for #{my_fixture_dir} is not readable\"\n    end\n    return file\n  end\n  def my_fixtures(glob = '*', flags = 0)\n    files = Dir.glob(File.join(my_fixture_dir, glob), flags)\n    unless files.length > 0 then\n      fail Puppet::DevError, \"fixture '#{glob}' for #{my_fixture_dir} had no files!\"\n    end\n    block_given? and files.each do |file| yield file end\n    files\n  end\n\n  def pem_content(name)\n    File.read(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', name), encoding: 'UTF-8')\n  end\n\n  def cert_fixture(name)\n    OpenSSL::X509::Certificate.new(pem_content(name))\n  end\n\n  def crl_fixture(name)\n    OpenSSL::X509::CRL.new(pem_content(name))\n  end\n\n  def key_fixture(name)\n    OpenSSL::PKey::RSA.new(pem_content(name))\n  end\n\n  def ec_key_fixture(name)\n    OpenSSL::PKey::EC.new(pem_content(name))\n  end\n\n  def request_fixture(name)\n    OpenSSL::X509::Request.new(pem_content(name))\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/handler.rb",
    "content": "require 'puppet/network/http/handler'\n\nclass PuppetSpec::Handler\n  include Puppet::Network::HTTP::Handler\n\n  def initialize(* routes)\n    register(routes)\n  end\n\n  def set_content_type(response, format)\n    response[:content_type_header] = format\n  end\n\n  def set_response(response, body, status = 200)\n    response[:body] = body\n    response[:status] = status\n  end\n\n  def http_method(request)\n    request[:method]\n  end\n\n  def path(request)\n    request[:path]\n  end\n\n  def params(request)\n    request[:params]\n  end\n\n  def client_cert(request)\n    request[:client_cert]\n  end\n\n  def body(request)\n    request[:body]\n  end\n\n  def headers(request)\n    request[:headers] || {}\n  end\nend\n\nclass PuppetSpec::HandlerProfiler\n  def start(metric, description)\n  end\n\n  def finish(context, metric, description)\n  end\n\n  def shutdown()\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/https.rb",
    "content": "require 'spec_helper'\nrequire 'webrick'\n\nclass PuppetSpec::HTTPSServer\n  include PuppetSpec::Fixtures\n\n  attr_reader :ca_cert, :ca_crl, :server_cert, :server_key\n\n  def initialize(ca_cert: nil, ca_crl: nil, server_key: nil, server_cert: nil)\n    @ca_cert = ca_cert || cert_fixture('ca.pem')\n    @ca_crl = ca_crl || crl_fixture('crl.pem')\n    @server_key = server_key || key_fixture('127.0.0.1-key.pem')\n    @server_cert = server_cert || cert_fixture('127.0.0.1.pem')\n    @config = WEBrick::Config::HTTP.dup\n  end\n\n  def handle_request(ctx, ssl, response_proc)\n    req = WEBrick::HTTPRequest.new(@config)\n    req.parse(ssl)\n\n    # always drain request body\n    req.body\n\n    res = WEBrick::HTTPResponse.new(@config)\n    res.status = 200\n    res.body = 'OK'\n    # The server explicitly closes the connection after handling it,\n    # so explicitly tell the client we're not going to keep it open.\n    # Without this, ruby will add `Connection: Keep-Alive`, which\n    # confuses the client when it tries to reuse the half-closed\n    # connection.\n    res['Connection'] = 'close'\n    response_proc.call(req, res) if response_proc\n\n    res.send_response(ssl)\n  end\n\n  def start_server(ctx_proc: nil, response_proc: nil, &block)\n    errors = []\n\n    IO.pipe {|stop_pipe_r, stop_pipe_w|\n      store = OpenSSL::X509::Store.new\n      Array(@ca_cert).each { |c| store.add_cert(c) }\n      store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT\n      ctx = OpenSSL::SSL::SSLContext.new\n      ctx.cert_store = store\n      ctx.cert = @server_cert\n      ctx.key = @server_key\n      ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE\n      ctx_proc.call(ctx) if ctx_proc\n\n      Socket.do_not_reverse_lookup = true\n      tcps = TCPServer.new(\"127.0.0.1\", 0)\n      begin\n        port = tcps.connect_address.ip_port\n        begin\n          server_thread = Thread.new do\n            begin\n              ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)\n              ssls.start_immediately = true\n\n              loop do\n                readable, = IO.select([ssls, stop_pipe_r])\n                break if readable.include?(stop_pipe_r)\n\n                ssl = ssls.accept\n                begin\n                  handle_request(ctx, ssl, response_proc)\n                ensure\n                  ssl.close\n                end\n              end\n            rescue => e\n              # uncomment this line if something goes wrong\n              # puts \"SERVER #{e.message}\"\n              errors << e\n            end\n          end\n\n          begin\n            yield port\n          ensure\n            stop_pipe_w.close\n          end\n        ensure\n          server_thread.join\n        end\n      ensure\n        tcps.close\n      end\n    }\n\n    errors\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/language.rb",
    "content": "require 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\nmodule PuppetSpec::Language\n  extend RSpec::Matchers::DSL\n\n  def produces(expectations)\n    calledFrom = caller\n    expectations.each do |manifest, resources|\n      it \"evaluates #{manifest} to produce #{resources}\" do\n        begin\n          case resources\n          when String\n            node = Puppet::Node.new('specification')\n            Puppet[:code] = manifest\n            compiler = Puppet::Parser::Compiler.new(node)\n            evaluator = Puppet::Pops::Parser::EvaluatingParser.new()\n\n            # see lib/puppet/indirector/catalog/compiler.rb#filter\n            catalog = compiler.compile.filter { |r| r.virtual? }\n\n            compiler.send(:instance_variable_set, :@catalog, catalog)\n\n            Puppet.override(:loaders => compiler.loaders) do\n              expect(evaluator.evaluate_string(compiler.topscope, resources)).to eq(true)\n            end\n          when Array\n            catalog = PuppetSpec::Compiler.compile_to_catalog(manifest)\n\n            if resources.empty?\n              base_resources = [\"Class[Settings]\", \"Class[main]\", \"Stage[main]\"]\n              expect(catalog.resources.collect(&:ref) - base_resources).to eq([])\n            else\n              resources.each do |reference|\n                if reference.is_a?(Array)\n                  matcher = Matchers::Resource.have_resource(reference[0])\n                  reference[1].each do |name, value|\n                    matcher = matcher.with_parameter(name, value)\n                  end\n                else\n                  matcher = Matchers::Resource.have_resource(reference)\n                end\n\n                expect(catalog).to matcher\n              end\n            end\n          else\n            raise \"Unsupported creates specification: #{resources.inspect}\"\n          end\n        rescue  Puppet::Error, RSpec::Expectations::ExpectationNotMetError => e\n          # provide the backtrace from the caller, or it is close to impossible to find some originators\n          e.set_backtrace(calledFrom)\n          raise\n        end\n      end\n    end\n  end\n\n  def fails(expectations)\n    calledFrom = caller\n    expectations.each do |manifest, pattern|\n      it \"fails to evaluate #{manifest} with message #{pattern}\" do\n        begin\n        expect do\n          PuppetSpec::Compiler.compile_to_catalog(manifest)\n        end.to raise_error(Puppet::Error, pattern)\n        rescue  RSpec::Expectations::ExpectationNotMetError => e\n          # provide the backgrace from the caller, or it is close to impossible to find some originators\n          e.set_backtrace(calledFrom)\n          raise\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/matchers.rb",
    "content": "require 'stringio'\n\n########################################################################\n# Custom matchers...\nRSpec::Matchers.define :have_matching_element do |expected|\n  match do |actual|\n    actual.any? { |item| item =~ expected }\n  end\nend\n\nRSpec::Matchers.define :have_matching_log do |expected|\n  match do |actual|\n    actual.map(&:to_s).any? { |item| item =~ expected }\n  end\nend\n\nRSpec::Matchers.define :have_matching_log_with_source do |expected, file, line, pos|\n  match do |actual|\n    actual.any? { |item| item.message =~ expected && item.file == file && item.line == line && item.pos == pos }\n  end\nend\n\nRSpec::Matchers.define :exit_with do |expected|\n  actual = nil\n  match do |block|\n    begin\n      block.call\n    rescue SystemExit => e\n      actual = e.status\n    end\n    actual and actual == expected\n  end\n\n  supports_block_expectations\n\n  failure_message do |block|\n    \"expected exit with code #{expected} but \" +\n      (actual.nil? ? \" exit was not called\" : \"we exited with #{actual} instead\")\n  end\n\n  failure_message_when_negated do |block|\n    \"expected that exit would not be called with #{expected}\"\n  end\n\n  description do\n    \"expect exit with #{expected}\"\n  end\nend\n\nRSpec::Matchers.define :equal_attributes_of do |expected|\n  match do |actual|\n    actual.instance_variables.all? do |attr|\n      actual.instance_variable_get(attr) == expected.instance_variable_get(attr)\n    end\n  end\nend\n\nRSpec::Matchers.define :equal_resource_attributes_of do |expected|\n  match do |actual|\n    actual.keys do |attr|\n      actual[attr] == expected[attr]\n    end\n  end\nend\n\nRSpec::Matchers.define :be_one_of do |*expected|\n  match do |actual|\n    expected.include? actual\n  end\n\n  failure_message do |actual|\n    \"expected #{actual.inspect} to be one of #{expected.map(&:inspect).join(' or ')}\"\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/module_tool/shared_functions.rb",
    "content": "require 'puppet/util/json'\n\nmodule PuppetSpec\n  module ModuleTool\n    module SharedFunctions\n      def remote_release(name, version)\n        remote_source.available_releases[name][version]\n      end\n\n      def preinstall(name, version, options = { :into => primary_dir })\n        release = remote_release(name, version)\n        raise \"Could not preinstall #{name} v#{version}\" if release.nil?\n\n        name = release.name[/-(.*)/, 1]\n        moddir = File.join(options[:into], name)\n        FileUtils.mkdir_p(moddir)\n        File.open(File.join(moddir, 'metadata.json'), 'w') do |file|\n          file.puts(Puppet::Util::Json.dump(release.metadata))\n        end\n      end\n\n      def mark_changed(path)\n        app = Puppet::ModuleTool::Applications::Checksummer\n        allow(app).to receive(:run).with(path).and_return(['README'])\n      end\n\n      def graph_should_include(name, options)\n        releases = flatten_graph(subject[:graph] || [])\n        release = releases.find { |x| x[:name] == name }\n\n        if options.nil?\n          expect(release).to be_nil\n        else\n          from = options.keys.find { |k| k.nil? || k.is_a?(SemanticPuppet::Version) }\n          to   = options.delete(from)\n\n          if to or from\n            options[:previous_version] ||= from\n            options[:version] ||= to\n          end\n\n          expect(release).not_to be_nil\n          expect(release).to include options\n        end\n      end\n\n      def flatten_graph(graph)\n        graph + graph.map { |sub| flatten_graph(sub[:dependencies]) }.flatten\n      end\n\n      def v(str)\n        SemanticPuppet::Version.parse(str)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/module_tool/stub_source.rb",
    "content": "module PuppetSpec\n  module ModuleTool\n    class StubSource < SemanticPuppet::Dependency::Source\n      def inspect; \"Stub Source\"; end\n      def host\n        \"http://nowhe.re\"\n      end\n\n      def fetch(name)\n        available_releases[name.tr('/', '-')].values\n      end\n\n      def available_releases\n        return @available_releases if defined? @available_releases\n\n        @available_releases = {\n          'puppetlabs-java' => {\n            '10.0.0' => { 'puppetlabs/stdlib' => '4.1.0' },\n          },\n          'puppetlabs-stdlib' => {\n            '4.1.0' => {},\n          },\n          'pmtacceptance-stdlib' => {\n            \"4.1.0\" => {},\n            \"3.2.0\" => {},\n            \"3.1.0\" => {},\n            \"3.0.0\" => {},\n            \"2.6.0\" => {},\n            \"2.5.1\" => {},\n            \"2.5.0\" => {},\n            \"2.4.0\" => {},\n            \"2.3.2\" => {},\n            \"2.3.1\" => {},\n            \"2.3.0\" => {},\n            \"2.2.1\" => {},\n            \"2.2.0\" => {},\n            \"2.1.3\" => {},\n            \"2.0.0\" => {},\n            \"1.1.0\" => {},\n            \"1.0.0\" => {},\n          },\n          'pmtacceptance-keystone' => {\n            '3.0.0-rc2' => { \"pmtacceptance/mysql\" => \">=0.6.1 <1.0.0\", \"pmtacceptance/stdlib\" => \">= 2.5.0\" },\n            '3.0.0-rc1' => { \"pmtacceptance/mysql\" => \">=0.6.1 <1.0.0\", \"pmtacceptance/stdlib\" => \">= 2.5.0\" },\n            '2.2.0'     => { \"pmtacceptance/mysql\" => \">=0.6.1 <1.0.0\", \"pmtacceptance/stdlib\" => \">= 2.5.0\" },\n            '2.2.0-rc1' => { \"pmtacceptance/mysql\" => \">=0.6.1 <1.0.0\", \"pmtacceptance/stdlib\" => \">= 2.5.0\" },\n            '2.1.0'     => { \"pmtacceptance/mysql\" => \">=0.6.1 <1.0.0\", \"pmtacceptance/stdlib\" => \">= 2.5.0\" },\n            '2.0.0'     => { \"pmtacceptance/mysql\" => \">= 0.6.1\" },\n            '1.2.0'     => { \"pmtacceptance/mysql\" => \">= 0.5.0\" },\n            '1.1.1'     => { \"pmtacceptance/mysql\" => \">= 0.5.0\" },\n            '1.1.0'     => { \"pmtacceptance/mysql\" => \">= 0.5.0\" },\n            '1.0.1'     => { \"pmtacceptance/mysql\" => \">= 0.5.0\" },\n            '1.0.0'     => { \"pmtacceptance/mysql\" => \">= 0.5.0\" },\n            '0.2.0'     => { \"pmtacceptance/mysql\" => \">= 0.5.0\" },\n            '0.1.0'     => { \"pmtacceptance/mysql\" => \">= 0.3.0\" },\n          },\n          'pmtacceptance-mysql' => {\n            \"2.1.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.0-rc5\" => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.0-rc4\" => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.0-rc3\" => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.0-rc2\" => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"2.0.0-rc1\" => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"1.0.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.9.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.8.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.8.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.7.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.7.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.6.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.6.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.5.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.4.0\"     => {},\n            \"0.3.0\"     => {},\n            \"0.2.0\"     => {},\n          },\n          'pmtacceptance-apache' => {\n            \"0.10.0\"    => { \"pmtacceptance/stdlib\" => \">= 2.4.0\" },\n            \"0.9.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.4.0\" },\n            \"0.8.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.8.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.7.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.6.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.5.0-rc1\" => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.4.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.3.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.2.2\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.2.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.2.0\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.1.1\"     => { \"pmtacceptance/stdlib\" => \">= 2.2.1\" },\n            \"0.0.4\"     => {},\n            \"0.0.3\"     => {},\n            \"0.0.2\"     => {},\n            \"0.0.1\"     => {},\n          },\n          'pmtacceptance-bacula' => {\n            \"0.0.3\" => { \"pmtacceptance/stdlib\" => \">= 2.2.0\", \"pmtacceptance/mysql\" => \">= 1.0.0\" },\n            \"0.0.2\" => { \"pmtacceptance/stdlib\" => \">= 2.2.0\", \"pmtacceptance/mysql\" => \">= 0.0.1\" },\n            \"0.0.1\" => { \"pmtacceptance/stdlib\" => \">= 2.2.0\" },\n          },\n          'puppetlabs-oneversion' => {\n            \"0.0.1\" => {}\n          }\n        }\n\n        @available_releases.each do |name, versions|\n          versions.each do |version, deps|\n            deps, metadata = deps.partition { |k,v| k.is_a? String }\n            dependencies = Hash[deps.map { |k, v| [ k.tr('/', '-'), v ] }]\n\n            versions[version] = create_release(name, version, dependencies).tap do |release|\n              release.meta_def(:prepare)     { }\n              release.meta_def(:install)     { |x| @install_dir = x.to_s }\n              release.meta_def(:install_dir) { @install_dir }\n              release.meta_def(:metadata) do\n                metadata = Hash[metadata].merge(\n                  :name         => name,\n                  :version      => version,\n                  :source       => '',   # GRR, Puppet!\n                  :author       => '',   # GRR, Puppet!\n                  :license      => '',   # GRR, Puppet!\n                  :dependencies => dependencies.map do |dep, range|\n                    { :name => dep, :version_requirement => range }\n                  end\n                )\n                Hash[metadata.map { |k,v| [ k.to_s, v ] }]\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/modules.rb",
    "content": "module PuppetSpec::Modules\n  class << self\n    def create(name, dir, options = {})\n      module_dir = File.join(dir, name)\n      FileUtils.mkdir_p(module_dir)\n\n      environment = options[:environment]\n\n      metadata = options[:metadata]\n      if metadata\n        metadata[:source]  ||= 'github'\n        metadata[:author]  ||= 'puppetlabs'\n        metadata[:version] ||= '9.9.9'\n        metadata[:license] ||= 'to kill'\n        metadata[:dependencies] ||= []\n\n        metadata[:name] = \"#{metadata[:author]}/#{name}\"\n\n        File.open(File.join(module_dir, 'metadata.json'), 'w') do |f|\n          f.write(metadata.to_json)\n        end\n      end\n\n      tasks = options[:tasks]\n      if tasks\n        tasks_dir = File.join(module_dir, 'tasks')\n        FileUtils.mkdir_p(tasks_dir)\n        tasks.each do |task_files|\n          task_files.each do |task_file|\n            if task_file.is_a?(String)\n              # default content to acceptable metadata\n              task_file = { :name => task_file, :content => \"{}\" }\n            end\n            File.write(File.join(tasks_dir, task_file[:name]), task_file[:content])\n          end\n        end\n      end\n\n      if (plans = options[:plans])\n        plans_dir = File.join(module_dir, 'plans')\n        FileUtils.mkdir_p(plans_dir)\n        plans.each do |plan_file|\n          if plan_file.is_a?(String)\n            # default content to acceptable metadata\n            plan_file = { :name => plan_file, :content => \"{}\" }\n          end\n          File.write(File.join(plans_dir, plan_file[:name]), plan_file[:content])\n        end\n      end\n\n      if (scripts = options[:scripts])\n        scripts_dir = File.join(module_dir, 'scripts')\n        FileUtils.mkdir_p(scripts_dir)\n        scripts.each do |script_file|\n          if script_file.is_a?(String)\n            script_file = { :name => script_file, :content => \"\" }\n          end\n          File.write(File.join(scripts_dir, script_file[:name]), script_file[:content])\n        end\n      end\n\n      (options[:files] || {}).each do |fname, content|\n        path = File.join(module_dir, fname)\n        FileUtils.mkdir_p(File.dirname(path))\n        File.write(path, content)\n      end\n\n      Puppet::Module.new(name, module_dir, environment)\n    end\n\n    def generate_files(name, dir, options = {})\n      module_dir = File.join(dir, name)\n      FileUtils.mkdir_p(module_dir)\n\n      if (metadata = options[:metadata])\n        File.open(File.join(module_dir, 'metadata.json'), 'w') do |f|\n          f.write(metadata.to_json)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/network.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/http'\nrequire 'puppet/network/http/api/indirected_routes'\nrequire 'puppet/indirector_testing'\n\nmodule PuppetSpec::Network\n  def not_found_error\n    Puppet::Network::HTTP::Error::HTTPNotFoundError\n  end\n\n  def not_acceptable_error\n    Puppet::Network::HTTP::Error::HTTPNotAcceptableError\n  end\n\n  def bad_request_error\n    Puppet::Network::HTTP::Error::HTTPBadRequestError\n  end\n\n  def not_authorized_error\n    Puppet::Network::HTTP::Error::HTTPNotAuthorizedError\n  end\n\n  def method_not_allowed_error\n    Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError\n  end\n\n  def unsupported_media_type_error\n    Puppet::Network::HTTP::Error::HTTPUnsupportedMediaTypeError\n  end\n\n  def params\n    { :environment => \"production\" }\n  end\n\n  def acceptable_content_types\n    types = ['application/json']\n    types << 'application/x-msgpack' if Puppet.features.msgpack?\n    types << 'text/pson' if Puppet.features.pson?\n    types\n  end\n\n  def acceptable_content_types_string\n    acceptable_content_types.join(', ')\n  end\n\n  def acceptable_catalog_content_types\n    types = %w[application/vnd.puppet.rich+json application/json]\n    types.concat(%w[application/vnd.puppet.rich+msgpack application/x-msgpack]) if Puppet.features.msgpack?\n    types << 'text/pson' if Puppet.features.pson?\n    types\n  end\n\n  def master_url_prefix\n    \"#{Puppet::Network::HTTP::MASTER_URL_PREFIX}/v3\"\n  end\n\n  def ca_url_prefix\n    \"#{Puppet::Network::HTTP::CA_URL_PREFIX}/v1\"\n  end\n\n  def a_request_that_heads(data, request = {}, params = params())\n    Puppet::Network::HTTP::Request.from_hash({\n      :headers => {\n        'accept' => request[:accept_header],\n        'content-type' => \"application/json\"\n      },\n      :method => \"HEAD\",\n      :path => \"#{master_url_prefix}/#{data.class.indirection.name}/#{data.value}\",\n      :params => params,\n    })\n  end\n\n  def a_request_that_submits(data, request = {}, params = params())\n    Puppet::Network::HTTP::Request.from_hash({\n      :headers => {\n        'accept' => request[:accept_header],\n        'content-type' => request[:content_type_header] || \"application/json\"\n      },\n      :method => \"PUT\",\n      :path => \"#{master_url_prefix}/#{data.class.indirection.name}/#{data.value}\",\n      :params => params,\n      :body => request[:body].nil? ? data.render(\"json\") : request[:body]\n    })\n  end\n\n  def a_request_that_destroys(data, request = {}, params = params())\n    Puppet::Network::HTTP::Request.from_hash({\n      :headers => {\n        'accept' => request[:accept_header],\n        'content-type' => \"application/json\"\n      },\n      :method => \"DELETE\",\n      :path => \"#{master_url_prefix}/#{data.class.indirection.name}/#{data.value}\",\n      :params => params,\n      :body => ''\n    })\n  end\n\n  def a_request_that_finds(data, request = {}, params = params())\n    Puppet::Network::HTTP::Request.from_hash({\n      :headers => {\n        'accept' => request[:accept_header],\n        'content-type' => \"application/json\"\n      },\n      :method => \"GET\",\n      :path => \"#{master_url_prefix}/#{data.class.indirection.name}/#{data.value}\",\n      :params => params,\n      :body => ''\n    })\n  end\n\n  def a_request_that_searches(data, request = {}, params = params())\n    Puppet::Network::HTTP::Request.from_hash({\n      :headers => {\n        'accept' => request[:accept_header],\n        'content-type' => \"application/json\"\n      },\n      :method => \"GET\",\n      :path => \"#{master_url_prefix}/#{data.class.indirection.name}s/#{data.name}\",\n      :params => params,\n      :body => ''\n    })\n  end\n\nend\n\n"
  },
  {
    "path": "spec/lib/puppet_spec/pops.rb",
    "content": "module PuppetSpec::Pops\n  extend RSpec::Matchers::DSL\n\n  # Checks if an Acceptor has a specific issue in its list of diagnostics\n  matcher :have_issue do |expected|\n    match do |actual|\n      actual.diagnostics.index { |i| i.issue == expected } != nil\n    end\n    failure_message do |actual|\n      \"expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to contain issue #{expected.issue_code}\"\n    end\n    failure_message_when_negated do |actual|\n      \"expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to not contain issue #{expected.issue_code}\"\n    end\n  end\n\n  # Checks if an Acceptor has any issues\n  matcher :have_any_issues do\n    match do |actual|\n      !actual.diagnostics.empty?\n    end\n    failure_message do |actual|\n      'expected Acceptor[] to contain at least one issue'\n    end\n    failure_message_when_negated do |actual|\n      \"expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to not contain any issues\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/puppetserver.rb",
    "content": "require 'spec_helper'\nrequire 'webrick'\nrequire \"webrick/ssl\"\n\nclass PuppetSpec::Puppetserver\n  include PuppetSpec::Fixtures\n  include PuppetSpec::Files\n\n  attr_reader :ca_cert, :ca_crl, :server_cert, :server_key\n\n  class NodeServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_GET request, response\n      node = Puppet::Node.new(Puppet[:certname])\n      response.body = node.render(:json)\n      response['Content-Type'] = 'application/json'\n    end\n  end\n\n  class CatalogServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_POST request, response\n      response['Content-Type'] = 'application/json'\n      response['X-Puppet-Compiler-Name'] = 'test-compiler-hostname'\n      catalog = Puppet::Resource::Catalog.new(Puppet[:certname], 'production')\n      response.body = catalog.render(:json)\n    end\n  end\n\n  class FileMetadatasServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_GET request, response\n      response['Content-Type'] = 'application/json'\n      response.body = \"[{\\\"path\\\":\\\"/etc/puppetlabs/code/environments/production/modules\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":0,\\\"group\\\":0,\\\"mode\\\":493,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-03-06 20:14:25 UTC\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}]\"\n    end\n  end\n\n  class FileMetadataServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_GET request, response\n      response['Content-Type'] = 'application/json'\n      response.body = \"{\\\"path\\\":\\\"/etc/puppetlabs/code/environments/production/modules\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":0,\\\"group\\\":0,\\\"mode\\\":493,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-03-06 20:14:25 UTC\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}\"\n    end\n  end\n\n  class FileContentServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_GET request, response\n      response.status = 404\n    end\n  end\n\n  class ReportServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_PUT request, response\n      response['Content-Type'] = 'application/json'\n      response.body = \"[]\"\n    end\n  end\n\n  class StaticFileContentServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_GET request, response\n      response.status = 404\n    end\n  end\n\n  class FilebucketServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_GET request, response\n    end\n\n    def do_PUT request, response\n      upload = File.join(@config.config[:TempDir], 'filebucket')\n      File.open(upload, 'wb') { |f| f.write(request.body) }\n      response['Content-Type'] = 'application/octet-stream'\n    end\n\n    def do_HEAD request, response\n      response.status = 404\n    end\n  end\n\n  class CertificateServlet < WEBrick::HTTPServlet::AbstractServlet\n    def initialize(server, ca_cert)\n      super(server)\n      @ca_cert = ca_cert\n    end\n\n    def do_GET request, response\n      if request.path =~ %r{/puppet-ca/v1/certificate/ca$}\n        response['Content-Type'] = 'text/plain'\n        response.body = @ca_cert.to_pem\n      else\n        response.status = 404\n      end\n    end\n  end\n\n  class CertificateRevocationListServlet < WEBrick::HTTPServlet::AbstractServlet\n    def initialize(server, crl)\n      super(server)\n      @crl = crl\n    end\n\n    def do_GET request, response\n      response['Content-Type'] = 'text/plain'\n      response.body = @crl.to_pem\n    end\n  end\n\n  class CertificateRequestServlet < WEBrick::HTTPServlet::AbstractServlet\n    def do_PUT request, response\n      response.status = 404\n    end\n  end\n\n  def initialize\n    @ca_cert = cert_fixture('ca.pem')\n    @ca_crl = crl_fixture('crl.pem')\n    @server_key = key_fixture('127.0.0.1-key.pem')\n    @server_cert = cert_fixture('127.0.0.1.pem')\n    @path = tmpfile('webrick')\n\n    @https = WEBrick::HTTPServer.new(\n      BindAddress: \"127.0.0.1\",\n      Port: 0, # webrick will choose the first available port, and set it in the config\n      SSLEnable: true,\n      SSLStartImmediately: true,\n      SSLCACertificateFile: File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'ca.pem'),\n      SSLCertificate: @server_cert,\n      SSLPrivateKey: @server_key,\n      Logger: WEBrick::Log.new(@path),\n      AccessLog: [\n        [@path, WEBrick::AccessLog::COMBINED_LOG_FORMAT],\n      ]\n    )\n\n    trap('INT') do\n      @https.shutdown\n    end\n\n    # Enable this line for more detailed webrick logging\n    # @https.logger.level = 5 # DEBUG\n  end\n\n  def start_server(mounts: {}, &block)\n    register_mounts(mounts: mounts)\n\n    Thread.new do\n      @https.start\n    end\n\n    begin\n      yield @https.config[:Port]\n    ensure\n      @https.shutdown\n    end\n  end\n\n  def register_mounts(mounts: {})\n    register_mount('/status/v1/simple/server', proc { |req, res|  }, nil)\n    register_mount('/puppet/v3/node', mounts[:node], NodeServlet)\n    register_mount('/puppet/v3/catalog', mounts[:catalog], CatalogServlet)\n    register_mount('/puppet/v3/file_metadata', mounts[:file_metadata], FileMetadataServlet)\n    register_mount('/puppet/v3/file_metadatas', mounts[:file_metadatas], FileMetadatasServlet)\n    register_mount('/puppet/v3/file_content', mounts[:file_content], FileContentServlet)\n    register_mount('/puppet/v3/static_file_content', mounts[:static_file_content], StaticFileContentServlet)\n    register_mount('/puppet/v3/report', mounts[:report], ReportServlet)\n    register_mount('/puppet/v3/file_bucket_file', mounts[:filebucket], FilebucketServlet)\n    register_mount('/puppet-ca/v1/certificate', mounts[:certificate], CertificateServlet, @ca_cert)\n    register_mount('/puppet-ca/v1/certificate_revocation_list', mounts[:certificate_revocation_list], CertificateRevocationListServlet, @ca_crl)\n    register_mount('/puppet-ca/v1/certificate_request', mounts[:certificate_request], CertificateRequestServlet)\n  end\n\n  def register_mount(path, user_proc, default_servlet, *args)\n    handler = if user_proc\n                WEBrick::HTTPServlet::ProcHandler.new(user_proc)\n              else\n                default_servlet\n              end\n    @https.mount(path, handler, *args)\n  end\n\n  def upload_directory\n    @https.config[:TempDir]\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/scope.rb",
    "content": "module PuppetSpec::Scope\n  # Initialize a new scope suitable for testing.\n  #\n  def create_test_scope_for_node(node_name)\n    node = Puppet::Node.new(node_name)\n    compiler = Puppet::Parser::Compiler.new(node)\n    scope = Puppet::Parser::Scope.new(compiler)\n    scope.source = Puppet::Resource::Type.new(:node, node_name)\n    scope.parent = compiler.topscope\n    scope\n  end\n\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/settings.rb",
    "content": "module PuppetSpec::Settings\n\n  # It would probably be preferable to refactor defaults.rb such that the real definitions of\n  #  these settings were available as a variable, which was then accessible for use during tests.\n  #  However, I'm not doing that yet because I don't want to introduce any additional moving parts\n  #  to this already very large changeset.\n  #  Would be nice to clean this up later.  --cprice 2012-03-20\n  TEST_APP_DEFAULT_DEFINITIONS = {\n    :name         => { :default => \"test\", :desc => \"name\" },\n    :logdir       => { :type => :directory, :default => \"test\", :desc => \"logdir\" },\n    :confdir      => { :type => :directory, :default => \"test\", :desc => \"confdir\" },\n    :codedir      => { :type => :directory, :default => \"test\", :desc => \"codedir\" },\n    :vardir       => { :type => :directory, :default => \"test\", :desc => \"vardir\" },\n    :publicdir    => { :type => :directory, :default => \"test\", :desc => \"publicdir\" },\n    :rundir       => { :type => :directory, :default => \"test\", :desc => \"rundir\" },\n  }.freeze\n\n  TEST_APP_DEFAULT_VALUES = TEST_APP_DEFAULT_DEFINITIONS.inject({}) do |memo, (key, value)|\n    memo[key] = value[:default]\n    memo\n  end.freeze\n\n  def set_puppet_conf(confdir, settings)\n    FileUtils.mkdir_p(confdir)\n    write_file(File.join(confdir, \"puppet.conf\"), settings)\n  end\n\n  def set_environment_conf(environmentpath, environment, settings)\n    envdir = File.join(environmentpath, environment)\n    FileUtils.mkdir_p(envdir)\n    write_file(File.join(envdir, 'environment.conf'), settings)\n  end\n\n  def write_file(file, contents)\n    File.open(file, \"w\") do |f|\n      f.puts(contents)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/ssl.rb",
    "content": "require 'openssl'\n\nmodule PuppetSpec\n  module SSL\n\n    PRIVATE_KEY_LENGTH = 2048\n    FIVE_YEARS = 5 * 365 * 24 * 60 * 60\n    CA_EXTENSIONS = [\n      [\"basicConstraints\", \"CA:TRUE\", true],\n      [\"keyUsage\", \"keyCertSign, cRLSign\", true],\n      [\"subjectKeyIdentifier\", \"hash\", false],\n      [\"authorityKeyIdentifier\", \"keyid:always\", false]\n    ]\n    NODE_EXTENSIONS = [\n      [\"keyUsage\", \"digitalSignature\", true],\n      [\"subjectKeyIdentifier\", \"hash\", false]\n    ]\n    DEFAULT_SIGNING_DIGEST = OpenSSL::Digest::SHA256.new\n    DEFAULT_REVOCATION_REASON = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE\n    ROOT_CA_NAME = \"/CN=root-ca-\\u{2070E}\"\n    REVOKED_INT_CA_NAME = \"/CN=revoked-int-ca-\\u16A0\"\n    INT_CA_NAME = \"/CN=unrevoked-int-ca\\u06FF\\u16A0\\u{2070E}\"\n    LEAF_CA_NAME = \"/CN=leaf-ca-\\u06FF\"\n    EXPLANATORY_TEXT = <<-EOT\n# Root Issuer: #{ROOT_CA_NAME}\n# Intermediate Issuer: #{INT_CA_NAME}\n# Leaf Issuer: #{LEAF_CA_NAME}\nEOT\n\n\n    def self.create_private_key(length = PRIVATE_KEY_LENGTH)\n      OpenSSL::PKey::RSA.new(length)\n    end\n\n    def self.self_signed_ca(key, name)\n      cert = OpenSSL::X509::Certificate.new\n\n      cert.public_key = key.public_key\n      cert.subject = OpenSSL::X509::Name.parse(name)\n      cert.issuer = cert.subject\n      cert.version = 2\n      cert.serial = rand(2**128)\n\n      not_before = just_now\n      cert.not_before = not_before\n      cert.not_after = not_before + FIVE_YEARS\n\n      ext_factory = extension_factory_for(cert, cert)\n      CA_EXTENSIONS.each do |ext|\n        extension = ext_factory.create_extension(*ext)\n        cert.add_extension(extension)\n      end\n\n      cert.sign(key, DEFAULT_SIGNING_DIGEST)\n\n      cert\n    end\n\n    def self.create_csr(key, name)\n      csr = OpenSSL::X509::Request.new\n\n      csr.public_key = key.public_key\n      csr.subject = OpenSSL::X509::Name.parse(name)\n      csr.version = 2\n      csr.sign(key, DEFAULT_SIGNING_DIGEST)\n\n      csr\n    end\n\n    def self.sign(ca_key, ca_cert, csr, extensions = NODE_EXTENSIONS)\n      cert = OpenSSL::X509::Certificate.new\n\n      cert.public_key = csr.public_key\n      cert.subject = csr.subject\n      cert.issuer = ca_cert.subject\n      cert.version = 2\n      cert.serial = rand(2**128)\n\n      not_before = just_now\n      cert.not_before = not_before\n      cert.not_after = not_before + FIVE_YEARS\n\n      ext_factory = extension_factory_for(ca_cert, cert)\n      extensions.each do |ext|\n        extension = ext_factory.create_extension(*ext)\n        cert.add_extension(extension)\n      end\n\n      cert.sign(ca_key, DEFAULT_SIGNING_DIGEST)\n\n      cert\n    end\n\n    def self.create_crl_for(ca_cert, ca_key)\n      crl = OpenSSL::X509::CRL.new\n      crl.version = 1\n      crl.issuer = ca_cert.subject\n\n      ef = extension_factory_for(ca_cert)\n      crl.add_extension(\n        ef.create_extension([\"authorityKeyIdentifier\", \"keyid:always\", false]))\n      crl.add_extension(\n        OpenSSL::X509::Extension.new(\"crlNumber\", OpenSSL::ASN1::Integer(0)))\n\n      not_before = just_now\n      crl.last_update = not_before\n      crl.next_update = not_before + FIVE_YEARS\n      crl.sign(ca_key, DEFAULT_SIGNING_DIGEST)\n\n      crl\n    end\n\n    def self.revoke(serial, crl, ca_key)\n      revoked = OpenSSL::X509::Revoked.new\n      revoked.serial = serial\n      revoked.time = Time.now\n      revoked.add_extension(\n        OpenSSL::X509::Extension.new(\"CRLReason\",\n                                     OpenSSL::ASN1::Enumerated(DEFAULT_REVOCATION_REASON)))\n\n      crl.add_revoked(revoked)\n      extensions = crl.extensions.group_by{|e| e.oid == 'crlNumber' }\n      crl_number = extensions[true].first\n      unchanged_exts = extensions[false]\n\n      next_crl_number = crl_number.value.to_i + 1\n      new_crl_number_ext = OpenSSL::X509::Extension.new(\"crlNumber\",\n                                                        OpenSSL::ASN1::Integer(next_crl_number))\n\n      crl.extensions = unchanged_exts + [new_crl_number_ext]\n      crl.sign(ca_key, DEFAULT_SIGNING_DIGEST)\n\n      crl\n    end\n\n    # Creates a self-signed root ca, then signs two node certs, revoking one of them.\n    # Creates an intermediate CA and one node cert off of it.\n    # Creates a second intermediate CA and one node cert off if it.\n    # Creates a leaf CA off of the intermediate CA, then signs two node certs revoking one of them.\n    # Revokes an intermediate CA.\n    # Returns the ca bundle, crl chain, and all the node certs\n    #\n    #            -----\n    #           /     \\\n    #          /       \\\n    #          | root  +-------------------o------------------o\n    #          \\  CA   /                   |                  |\n    #           \\     /                    |                  |\n    #            --+--                     |                  |\n    #              |                       |                  |\n    #              |                       |                  |\n    #              |                       |                  |\n    #              |                     --+--              --+--\n    # +---------+  |   +---------+      /     \\            /     \\\n    # | revoked |  |   |         |     /revoked\\          /       \\\n    # |   node  +--o---+   node  |     |  int  |          | int   |\n    # |         |      |         |     \\  CA   /          \\  CA   /\n    # +---------+      +---------+      \\     /            \\     /\n    #                                    --+--              --+--\n    #                                      |                  |\n    #                                      |                  |\n    #                                      |                  |\n    #                                    --+--                |\n    #                                   /     \\           +---+-----+\n    #                                  /       \\          |         |\n    #                                  | leaf  |          |  node   |\n    #                                  \\  CA   /          |         |\n    #                                   \\     /           +---------+\n    #                                    --+--\n    #                                      |\n    #                                      |\n    #                         +---------+  |  +----------+\n    #                         | revoked |  |  |          |\n    #                         |   node  +--o--+  node    |\n    #                         |         |     |          |\n    #                         +---------+     +----------+\n    def self.create_chained_pki\n      root_key = create_private_key\n      root_cert = self_signed_ca(root_key, ROOT_CA_NAME)\n      root_crl = create_crl_for(root_cert, root_key)\n\n      unrevoked_root_node_key = create_private_key\n      unrevoked_root_node_csr = create_csr(unrevoked_root_node_key, \"/CN=unrevoked-root-node\")\n      unrevoked_root_node_cert = sign(root_key, root_cert, unrevoked_root_node_csr)\n\n      revoked_root_node_key = create_private_key\n      revoked_root_node_csr = create_csr(revoked_root_node_key, \"/CN=revoked-root-node\")\n      revoked_root_node_cert = sign(root_key, root_cert, revoked_root_node_csr)\n\n      revoke(revoked_root_node_cert.serial, root_crl, root_key)\n\n      revoked_int_key = create_private_key\n      revoked_int_csr = create_csr(revoked_int_key, REVOKED_INT_CA_NAME)\n      revoked_int_cert = sign(root_key, root_cert, revoked_int_csr, CA_EXTENSIONS)\n      revoked_int_crl = create_crl_for(revoked_int_cert, revoked_int_key)\n\n      int_key = create_private_key\n      int_csr = create_csr(int_key, INT_CA_NAME)\n      int_cert = sign(root_key, root_cert, int_csr, CA_EXTENSIONS)\n\n      int_node_key = create_private_key\n      int_node_csr = create_csr(int_node_key, \"/CN=unrevoked-int-node\")\n      int_node_cert = sign(int_key, int_cert, int_node_csr)\n\n      unrevoked_int_node_key = create_private_key\n      unrevoked_int_node_csr = create_csr(unrevoked_int_node_key, \"/CN=unrevoked-int-node\")\n      unrevoked_int_node_cert = sign(revoked_int_key, revoked_int_cert, unrevoked_int_node_csr)\n\n      leaf_key = create_private_key\n      leaf_csr = create_csr(leaf_key, LEAF_CA_NAME)\n      leaf_cert = sign(revoked_int_key, revoked_int_cert, leaf_csr, CA_EXTENSIONS)\n      leaf_crl = create_crl_for(leaf_cert, leaf_key)\n\n      revoke(revoked_int_cert.serial, root_crl, root_key)\n\n      unrevoked_leaf_node_key = create_private_key\n      unrevoked_leaf_node_csr = create_csr(unrevoked_leaf_node_key, \"/CN=unrevoked-leaf-node\")\n      unrevoked_leaf_node_cert = sign(leaf_key, leaf_cert, unrevoked_leaf_node_csr)\n\n      revoked_leaf_node_key = create_private_key\n      revoked_leaf_node_csr = create_csr(revoked_leaf_node_key, \"/CN=revoked-leaf-node\")\n      revoked_leaf_node_cert = sign(leaf_key, leaf_cert, revoked_leaf_node_csr)\n\n      revoke(revoked_leaf_node_cert.serial, leaf_crl, leaf_key)\n\n\n      ca_bundle = bundle(root_cert, revoked_int_cert, leaf_cert)\n      crl_chain = bundle(root_crl, revoked_int_crl, leaf_crl)\n\n      {\n        :root_cert => root_cert,\n        :int_cert => int_cert,\n        :int_node_cert => int_node_cert,\n        :leaf_cert => leaf_cert,\n        :leaf_key => leaf_key,\n        :revoked_root_node_cert => revoked_root_node_cert,\n        :revoked_int_cert => revoked_int_cert,\n        :revoked_leaf_node_cert => revoked_leaf_node_cert,\n        :unrevoked_root_node_cert => unrevoked_root_node_cert,\n        :unrevoked_int_node_cert  => unrevoked_int_node_cert,\n        :unrevoked_leaf_node_cert => unrevoked_leaf_node_cert,\n        :ca_bundle => ca_bundle,\n        :crl_chain => crl_chain,\n      }\n    end\n\n   private\n\n    def self.just_now\n      Time.now - 1\n    end\n\n    def self.extension_factory_for(ca, cert = nil)\n      ef = OpenSSL::X509::ExtensionFactory.new\n      ef.issuer_certificate  = ca\n      ef.subject_certificate = cert if cert\n\n      ef\n    end\n\n    def self.bundle(*items)\n      items.map {|i| EXPLANATORY_TEXT + i.to_pem }.join(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/unindent.rb",
    "content": "class String\n  def unindent(left_padding = '')\n    gsub(/^#{scan(/^\\s*/).min_by{ |l| l.length }}/, left_padding)\n  end\nend\n"
  },
  {
    "path": "spec/lib/puppet_spec/verbose.rb",
    "content": "# Support code for running stuff with warnings disabled or enabled\nmodule Kernel\n  def with_verbose_disabled\n    verbose, $VERBOSE = $VERBOSE, nil\n    begin\n      yield\n    ensure\n      $VERBOSE = verbose\n    end\n  end\n\n  def with_verbose_enabled\n    verbose, $VERBOSE = $VERBOSE, true\n    begin\n      yield\n    ensure\n      $VERBOSE = verbose\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/all_parsedfile_providers.rb",
    "content": "shared_examples_for \"all parsedfile providers\" do |provider, *files|\n  if files.empty? then\n    files = my_fixtures\n  end\n\n  files.flatten.each do |file|\n    it \"should rewrite #{file} reasonably unchanged\" do\n      allow(provider).to receive(:default_target).and_return(file)\n      provider.prefetch\n\n      text = provider.to_file(provider.target_records(file))\n      text.gsub!(/^# HEADER.+\\n/, '')\n\n      oldlines = File.readlines(file)\n      newlines = text.chomp.split \"\\n\"\n      oldlines.zip(newlines).each do |old, new|\n        expect(new.gsub(/\\s+/, '')).to eq(old.chomp.gsub(/\\s+/, ''))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/an_indirector_face.rb",
    "content": "shared_examples_for \"an indirector face\" do\n  [:find, :search, :save, :destroy, :info].each do |action|\n    it { is_expected.to be_action action }\n    it { is_expected.to respond_to action }\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/documentation_on_faces.rb",
    "content": "# encoding: UTF-8\nshared_examples_for \"documentation on faces\" do\n  defined?(Attrs) or\n    Attrs = [:summary, :description, :examples, :short_description, :notes, :author]\n\n  defined?(SingleLineAttrs) or\n    SingleLineAttrs = [:summary, :author]\n\n  # Simple, procedural tests that apply to a bunch of methods.\n  Attrs.each do |attr|\n    it \"should accept a #{attr}\" do\n      expect { subject.send(\"#{attr}=\", \"hello\") }.not_to raise_error\n      expect(subject.send(attr)).to eq(\"hello\")\n    end\n\n    it \"should accept a long (single line) value for #{attr}\" do\n      text = \"I never know when to stop with the word banana\" + (\"na\" * 1000)\n      expect { subject.send(\"#{attr}=\", text) }.to_not raise_error\n      expect(subject.send(attr)).to eq(text)\n    end\n  end\n\n  Attrs.each do |getter|\n    setter = \"#{getter}=\".to_sym\n    context \"#{getter}\" do\n      it \"should strip leading whitespace on a single line\" do\n        subject.send(setter, \"  death to whitespace\")\n        expect(subject.send(getter)).to eq(\"death to whitespace\")\n      end\n\n      it \"should strip trailing whitespace on a single line\" do\n        subject.send(setter, \"death to whitespace  \")\n        expect(subject.send(getter)).to eq(\"death to whitespace\")\n      end\n\n      it \"should strip whitespace at both ends at once\" do\n        subject.send(setter, \"  death to whitespace  \")\n        expect(subject.send(getter)).to eq(\"death to whitespace\")\n      end\n\n      multiline_text = \"with\\nnewlines\"\n      if SingleLineAttrs.include? getter then\n        it \"should not accept multiline values\" do\n          expect { subject.send(setter, multiline_text) }.\n            to raise_error ArgumentError, /#{getter} should be a single line/\n          expect(subject.send(getter)).to be_nil\n        end\n      else\n        it \"should accept multiline values\" do\n          expect { subject.send(setter, multiline_text) }.not_to raise_error\n          expect(subject.send(getter)).to eq(multiline_text)\n        end\n\n        [1, 2, 4, 7, 25].each do |length|\n          context \"#{length} chars indent\" do\n            indent = ' ' * length\n\n            it \"should strip leading whitespace on multiple lines\" do\n              text = \"this\\nis\\the\\final\\outcome\"\n              subject.send(setter, text.gsub(/^/, indent))\n              expect(subject.send(getter)).to eq(text)\n            end\n\n            it \"should not remove formatting whitespace, only global indent\" do\n              text = \"this\\n  is\\n    the\\n  ultimate\\ntest\"\n              subject.send(setter, text.gsub(/^/, indent))\n              expect(subject.send(getter)).to eq(text)\n            end\n          end\n        end\n\n        it \"should strip whitespace with a blank line\" do\n          subject.send(setter, \"  this\\n\\n  should outdent\")\n          expect(subject.send(getter)).to eq(\"this\\n\\nshould outdent\")\n        end\n      end\n    end\n  end\n\n  describe \"#short_description\" do\n    it \"should return the set value if set after description\" do\n      subject.description = \"hello\\ngoodbye\"\n      subject.short_description = \"whatever\"\n      expect(subject.short_description).to eq(\"whatever\")\n    end\n\n    it \"should return the set value if set before description\" do\n      subject.short_description = \"whatever\"\n      subject.description = \"hello\\ngoodbye\"\n      expect(subject.short_description).to eq(\"whatever\")\n    end\n\n    it \"should return nothing if not set and no description\" do\n      expect(subject.short_description).to be_nil\n    end\n\n    it \"should return the first paragraph of description if not set (where it is one line long)\" do\n      subject.description = \"hello\"\n      expect(subject.short_description).to eq(subject.description)\n    end\n\n    it \"should return the first paragraph of description if not set (where there is no paragraph break)\" do\n      subject.description = \"hello\\ngoodbye\"\n      expect(subject.short_description).to eq(subject.description)\n    end\n\n    it \"should return the first paragraph of description if not set (where there is a paragraph break)\" do\n      subject.description = \"hello\\ngoodbye\\n\\nmore\\ntext\\nhere\\n\\nfinal\\nparagraph\"\n      expect(subject.short_description).to eq(\"hello\\ngoodbye\")\n    end\n\n    it \"should trim a very, very long first paragraph and add ellipsis\" do\n      line = \"this is a very, very, very long long line full of text\\n\"\n      subject.description = line * 20 + \"\\n\\nwhatever, dude.\"\n\n      expect(subject.short_description).to eq((line * 5).chomp + ' [...]')\n    end\n\n    it \"should trim a very very long only paragraph even if it is followed by a new paragraph\" do\n      line = \"this is a very, very, very long long line full of text\\n\"\n      subject.description = line * 20\n\n      expect(subject.short_description).to eq((line * 5).chomp + ' [...]')\n    end\n  end\n\n  describe \"multiple authors\" do\n    authors = %w{John Paul George Ringo}\n\n    context \"in the DSL\" do\n      it \"should support multiple authors\" do\n\n        authors.each {|name| subject.author name }\n        expect(subject.authors).to match_array(authors)\n\n        expect(subject.author).to eq(authors.join(\"\\n\"))\n      end\n\n      it \"should reject author as an array\" do\n        expect { subject.author [\"Foo\", \"Bar\"] }.\n          to raise_error ArgumentError, /author must be a string/\n      end\n    end\n\n    context \"#author=\" do\n      it \"should accept a single name\" do\n        subject.author = \"Fred\"\n        expect(subject.author).to eq(\"Fred\")\n      end\n\n      it \"should accept an array of names\" do\n        subject.author = authors\n        expect(subject.authors).to match_array(authors)\n        expect(subject.author).to eq(authors.join(\"\\n\"))\n      end\n\n      it \"should not append when set multiple times\" do\n        subject.author = \"Fred\"\n        subject.author = \"John\"\n        expect(subject.author).to eq(\"John\")\n      end\n\n      it \"should reject arrays with embedded newlines\" do\n        expect { subject.author = [\"Fred\\nJohn\"] }.\n          to raise_error ArgumentError, /author should be a single line/\n      end\n    end\n  end\n\n  describe \"#license\" do\n    it \"should default to reserving rights\" do\n      expect(subject.license).to match(/All Rights Reserved/)\n    end\n\n    it \"should accept an arbitrary license string on the object\" do\n      subject.license = \"foo\"\n      expect(subject.license).to eq(\"foo\")\n    end\n  end\n\n  describe \"#copyright\" do\n    it \"should fail with just a name\" do\n      expect { subject.copyright(\"invalid\") }.\n        to raise_error ArgumentError, /copyright takes the owners names, then the years covered/\n    end\n\n    [1997, \"1997\"].each do |year|\n      it \"should accept an entity name and a #{year.class.name} year\" do\n        subject.copyright(\"me\", year)\n        expect(subject.copyright).to match(/\\bme\\b/)\n        expect(subject.copyright).to match(/#{year}/)\n      end\n\n      it \"should accept multiple entity names and a #{year.class.name} year\" do\n        subject.copyright [\"me\", \"you\"], year\n        expect(subject.copyright).to match(/\\bme\\b/)\n        expect(subject.copyright).to match(/\\byou\\b/)\n        expect(subject.copyright).to match(/#{year}/)\n      end\n    end\n\n    [\"1997-2003\", \"1997 - 2003\", 1997..2003].each do |range|\n      it \"should accept a #{range.class.name} range of years\" do\n        subject.copyright(\"me\", range)\n        expect(subject.copyright).to match(/\\bme\\b/)\n        expect(subject.copyright).to match(/1997-2003/)\n      end\n\n      it \"should accept a #{range.class.name} range of years\" do\n        subject.copyright [\"me\", \"you\"], range\n        expect(subject.copyright).to match(/\\bme\\b/)\n        expect(subject.copyright).to match(/\\byou\\b/)\n        expect(subject.copyright).to match(/1997-2003/)\n      end\n    end\n\n    [[1997, 2003], [\"1997\", 2003], [\"1997\", \"2003\"]].each do |input|\n      it \"should accept the set of years #{input.inspect} in an array\" do\n        subject.copyright \"me\", input\n        expect(subject.copyright).to match(/\\bme\\b/)\n        expect(subject.copyright).to match(/1997, 2003/)\n      end\n\n      it \"should accept the set of years #{input.inspect} in an array\" do\n        subject.copyright [\"me\", \"you\"], input\n        expect(subject.copyright).to match(/\\bme\\b/)\n        expect(subject.copyright).to match(/\\byou\\b/)\n        expect(subject.copyright).to match(/1997, 2003/)\n      end\n    end\n\n    it \"should warn if someone does math accidentally on the range of years\" do\n      expect { subject.copyright \"me\", 1997-2003 }.\n        to raise_error ArgumentError, /copyright with a year before 1970 is very strange; did you accidentally add or subtract two years\\?/\n    end\n\n    it \"should accept complex copyright years\" do\n      years = [1997, 1999, 2000..2002, 2005].reverse\n      subject.copyright \"me\", years\n      expect(subject.copyright).to match(/\\bme\\b/)\n      expect(subject.copyright).to match(/1997, 1999, 2000-2002, 2005/)\n    end\n  end\n\n  # Things that are automatically generated.\n  [:name, :options, :synopsis].each do |attr|\n    describe \"##{attr}\" do\n      it \"should not allow you to set #{attr}\" do\n        expect(subject).not_to respond_to :\"#{attr}=\"\n      end\n\n      it \"should have a #{attr}\" do\n        expect(subject.send(attr)).not_to be_nil\n      end\n\n      it \"'s #{attr} should not be empty...\" do\n        expect(subject.send(attr)).not_to eq('')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/file_server_terminus.rb",
    "content": "shared_examples_for \"Puppet::Indirector::FileServerTerminus\" do\n  # This only works if the shared behaviour is included before\n  # the 'before' block in the including context.\n  before do\n    Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil)\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    allow(Puppet::FileSystem).to receive(:exist?).with(Puppet[:fileserverconfig]).and_return(true)\n\n    @path = Tempfile.new(\"file_server_testing\")\n    path = @path.path\n    @path.close!\n    @path = path\n\n    Dir.mkdir(@path)\n    File.open(File.join(@path, \"myfile\"), \"w\") { |f| f.print \"my content\" }\n\n    # Use a real mount, so the integration is a bit deeper.\n    @mount1 = Puppet::FileServing::Configuration::Mount::File.new(\"one\")\n    @mount1.path = @path\n\n    @parser = double('parser', :changed? => false)\n    allow(@parser).to receive(:parse).and_return(\"one\" => @mount1)\n\n    allow(Puppet::FileServing::Configuration::Parser).to receive(:new).and_return(@parser)\n\n    # Stub out the modules terminus\n    @modules = double('modules terminus')\n\n    @request = Puppet::Indirector::Request.new(:indirection, :method, \"puppet://myhost/one/myfile\", nil)\n  end\n\n  it \"should use the file server configuration to find files\" do\n    allow(@modules).to receive(:find).and_return(nil)\n    allow(@terminus.indirection).to receive(:terminus).with(:modules).and_return(@modules)\n\n    expect(@terminus.find(@request)).to be_instance_of(@test_class)\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/file_serving.rb",
    "content": "shared_examples_for \"Puppet::FileServing::Files\" do |indirection|\n  %w[find search].each do |method|\n    let(:request) { Puppet::Indirector::Request.new(indirection, method, 'foo', nil) }\n\n    describe \"##{method}\" do\n      it \"should proxy to file terminus if the path is absolute\" do\n        request.key = make_absolute('/tmp/foo')\n\n        expect_any_instance_of(described_class.indirection.terminus(:file).class).to receive(method).with(request)\n\n        subject.send(method, request)\n      end\n\n      it \"should proxy to file terminus if the protocol is file\" do\n        request.protocol = 'file'\n\n        expect_any_instance_of(described_class.indirection.terminus(:file).class).to receive(method).with(request)\n\n        subject.send(method, request)\n      end\n\n      describe \"when the protocol is puppet\" do\n        before :each do\n          request.protocol = 'puppet'\n        end\n\n        describe \"and a server is specified\" do\n          before :each do\n            request.server = 'puppet_server'\n          end\n\n          it \"should proxy to rest terminus if default_file_terminus is rest\" do\n            Puppet[:default_file_terminus] = \"rest\"\n\n            expect_any_instance_of(described_class.indirection.terminus(:rest).class).to receive(method).with(request)\n\n            subject.send(method, request)\n          end\n\n          it \"should proxy to rest terminus if default_file_terminus is not rest\" do\n            Puppet[:default_file_terminus] = 'file_server'\n\n            expect_any_instance_of(described_class.indirection.terminus(:rest).class).to receive(method).with(request)\n\n            subject.send(method, request)\n          end\n        end\n\n        describe \"and no server is specified\" do\n          before :each do\n            request.server = nil\n          end\n\n          it \"should proxy to file_server if default_file_terminus is 'file_server'\" do\n            Puppet[:default_file_terminus] = 'file_server'\n\n            expect_any_instance_of(described_class.indirection.terminus(:file_server).class).to receive(method).with(request)\n\n            subject.send(method, request)\n          end\n\n          it \"should proxy to rest if default_file_terminus is 'rest'\" do\n            Puppet[:default_file_terminus] = \"rest\"\n\n            expect_any_instance_of(described_class.indirection.terminus(:rest).class).to receive(method).with(request)\n\n            subject.send(method, request)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/hiera_indirections.rb",
    "content": "shared_examples_for \"Hiera indirection\" do |test_klass, fixture_dir|\n  include PuppetSpec::Files\n\n  def write_hiera_config(config_file, datadir)\n    File.open(config_file, 'w') do |f|\n      f.write(\"---\n        :yaml:\n          :datadir: #{datadir}\n        :hierarchy: ['global', 'invalid']\n        :logger: 'noop'\n        :backends: ['yaml']\n      \")\n    end\n  end\n\n  def request(key)\n    Puppet::Indirector::Request.new(:hiera, :find, key, nil)\n  end\n\n  before do\n    hiera_config_file = tmpfile(\"hiera.yaml\")\n    Puppet.settings[:hiera_config] = hiera_config_file\n    write_hiera_config(hiera_config_file, fixture_dir)\n  end\n\n  after do\n    test_klass.instance_variable_set(:@hiera, nil)\n  end\n\n  it \"should be the default data_binding terminus\" do\n    expect(Puppet.settings[:data_binding_terminus]).to eq(:hiera)\n  end\n\n  it \"should raise an error if we don't have the hiera feature\" do\n    expect(Puppet.features).to receive(:hiera?).and_return(false)\n    expect { test_klass.new }.to raise_error RuntimeError,\n      \"Hiera terminus not supported without hiera library\"\n  end\n\n  describe \"the behavior of the hiera_config method\", :if => Puppet.features.hiera? do\n    it \"should override the logger and set it to puppet\" do\n      expect(test_klass.hiera_config[:logger]).to eq(\"puppet\")\n    end\n\n    context \"when the Hiera configuration file does not exist\" do\n      let(:path) { File.expand_path('/doesnotexist') }\n\n      before do\n        Puppet.settings[:hiera_config] = path\n      end\n\n      it \"should log a warning\" do\n        expect(Puppet).to receive(:warning).with(\n         \"Config file #{path} not found, using Hiera defaults\")\n        test_klass.hiera_config\n      end\n\n      it \"should only configure the logger and set it to puppet\" do\n        expect(Puppet).to receive(:warning).with(\n         \"Config file #{path} not found, using Hiera defaults\")\n        expect(test_klass.hiera_config).to eq({ :logger => 'puppet' })\n      end\n    end\n  end\n\n  describe \"the behavior of the find method\", :if => Puppet.features.hiera? do\n    let(:data_binder) { test_klass.new }\n\n    it \"should support looking up an integer\" do\n      expect(data_binder.find(request(\"integer\"))).to eq(3000)\n    end\n\n    it \"should support looking up a string\" do\n      expect(data_binder.find(request(\"string\"))).to eq('apache')\n    end\n\n    it \"should support looking up an array\" do\n      expect(data_binder.find(request(\"array\"))).to eq([\n        '0.ntp.puppetlabs.com',\n        '1.ntp.puppetlabs.com',\n      ])\n    end\n\n    it \"should support looking up a hash\" do\n      expect(data_binder.find(request(\"hash\"))).to eq({\n        'user'  => 'Hightower',\n        'group' => 'admin',\n        'mode'  => '0644'\n      })\n    end\n\n    it \"raises a data binding error if hiera cannot parse the yaml data\" do\n      expect do\n        data_binder.find(request('invalid'))\n      end.to raise_error(Puppet::DataBinding::LookupError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/iterative_functions.rb",
    "content": "shared_examples_for 'all iterative functions hash handling' do |func|\n  it 'passes a hash entry as an array of the key and value' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      {a=>1}.#{func} |$v| { notify { \"${v[0]} ${v[1]}\": } }\n    MANIFEST\n\n    expect(catalog.resource(:notify, \"a 1\")).not_to be_nil\n  end\nend\n\nshared_examples_for 'all iterative functions argument checks' do |func|\n\n  it 'raises an error when used against an unsupported type' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        3.14.#{func} |$k, $v| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects an Iterable value, got Float/)\n  end\n\n  it 'raises an error when called with any parameters besides a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].#{func}(1,2) |$v,$y| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects (?:between 1 and 2 arguments|1 argument), got 3/)\n  end\n\n  it 'raises an error when called without a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].#{func}\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects a block/)\n  end\n\n  it 'raises an error when called with something that is not a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].#{func}(1,2)\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects (?:between 1 and 2 arguments|1 argument), got 3/)\n  end\n\n  it 'raises an error when called with a block with too many required parameters' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].#{func}() |$v1, $v2, $v3| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /block expects(?: between 1 and)? 2 arguments, got 3/)\n  end\n\n  it 'raises an error when called with a block with too few parameters' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].#{func}() | | {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /block expects(?: between 1 and)? 2 arguments, got none/)\n  end\n\n  it 'does not raise an error when called with a block with too many but optional arguments' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].#{func}() |$v1, $v2, $v3=extra| {  }\n      MANIFEST\n    end.to_not raise_error\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/memory_terminus.rb",
    "content": "shared_examples_for \"A Memory Terminus\" do\n  it \"should find no instances by default\" do\n    expect(@searcher.find(@request)).to be_nil\n  end\n\n  it \"should be able to find instances that were previously saved\" do\n    @searcher.save(@request)\n    expect(@searcher.find(@request)).to equal(@instance)\n  end\n\n  it \"should replace existing saved instances when a new instance with the same name is saved\" do\n    @searcher.save(@request)\n    two = double('second', :name => @name)\n    trequest = double('request', :key => @name, :instance => two)\n    @searcher.save(trequest)\n    expect(@searcher.find(@request)).to equal(two)\n  end\n\n  it \"should be able to remove previously saved instances\" do\n    @searcher.save(@request)\n    @searcher.destroy(@request)\n    expect(@searcher.find(@request)).to be_nil\n  end\n\n  it \"should fail when asked to destroy an instance that does not exist\" do\n    expect { @searcher.destroy(@request) }.to raise_error(ArgumentError)\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/path_parameters.rb",
    "content": "# In order to use this correctly you must define a method to get an instance\n# of the type being tested, so that this code can remain generic:\n#\n#    it_should_behave_like \"all path parameters\", :path do\n#      def instance(path)\n#        Puppet::Type.type(:example).new(\n#          :name => 'foo', :require => 'bar', :path_param => path\n#        )\n#      end\n#\n# That method will be invoked for each test to create the instance that we\n# subsequently test through the system; you should ensure that the minimum of\n# possible attributes are set to keep the tests clean.\n#\n# You must also pass the symbolic name of the parameter being tested to the\n# block, and optionally can pass a hash of additional options to the block.\n#\n# The known options are:\n#  :array :: boolean, does this support arrays of paths, default true.\n\nshared_examples_for \"all pathname parameters with arrays\" do |win32|\n  path_types = {\n    \"unix absolute\"            => %q{/foo/bar},\n    \"unix relative\"            => %q{foo/bar},\n    \"win32 non-drive absolute\" => %q{\\foo\\bar},\n    \"win32 non-drive relative\" => %q{foo\\bar},\n    \"win32 drive absolute\"     => %q{c:\\foo\\bar},\n    \"win32 drive relative\"     => %q{c:foo\\bar}\n  }\n\n  describe \"when given an array of paths\" do\n    (1..path_types.length).each do |n|\n      path_types.keys.combination(n) do |set|\n        data = path_types.collect { |k, v| set.member?(k) ? v : nil } .compact\n\n        has_relative = set.find { |k| k =~ /relative/ or k =~ /non-drive/ }\n        has_windows = set.find { |k| k =~ /win32/ }\n        has_unix = set.find { |k| k =~ /unix/ }\n\n        if has_relative or (has_windows and !win32) or (has_unix and win32)\n          reject = true\n        else\n          reject = false\n        end\n\n        it \"should #{reject ? 'reject' : 'accept'} #{set.join(\", \")}\" do\n          if reject then\n            expect { instance(data) }.\n              to raise_error Puppet::Error, /fully qualified/\n          else\n            instance = instance(data)\n            expect(instance[@param]).to eq(data)\n          end\n        end\n\n        it \"should #{reject ? 'reject' : 'accept'} #{set.join(\", \")} doubled\" do\n          if reject then\n            expect { instance(data + data) }.\n              to raise_error Puppet::Error, /fully qualified/\n          else\n            instance = instance(data + data)\n            expect(instance[@param]).to eq(data + data)\n          end\n        end\n      end\n    end\n  end\nend\n\n\nshared_examples_for \"all path parameters\" do |param, options|\n  # Extract and process options to the block.\n  options ||= {}\n  array = options[:array].nil? ? true : options.delete(:array)\n  if options.keys.length > 0 then\n    fail \"unknown options for 'all path parameters': \" +\n      options.keys.sort.join(', ')\n  end\n\n  def instance(path)\n    fail \"we didn't implement the 'instance(path)' method in the it_should_behave_like block\"\n  end\n\n  ########################################################################\n  # The actual testing code...\n  before :all do\n    @param = param\n  end\n\n  describe \"on a Unix-like platform it\", :if => Puppet.features.posix? do\n    if array then\n      it_should_behave_like \"all pathname parameters with arrays\", false\n    end\n\n    it \"should accept a fully qualified path\" do\n      path = File.join('', 'foo')\n      instance = instance(path)\n      expect(instance[@param]).to eq(path)\n    end\n\n    it \"should give a useful error when the path is not absolute\" do\n      path = 'foo'\n      expect { instance(path) }.\n        to raise_error Puppet::Error, /fully qualified/\n    end\n\n    { \"Unix\" => '/', \"Win32\" => '\\\\' }.each do |style, slash|\n      %w{q Q a A z Z c C}.sort.each do |drive|\n        it \"should reject drive letter '#{drive}' with #{style} path separators\" do\n          path = \"#{drive}:#{slash}Program Files\"\n          expect { instance(path) }.\n            to raise_error Puppet::Error, /fully qualified/\n        end\n      end\n    end\n  end\n\n  describe \"on a Windows-like platform it\", :if => Puppet::Util::Platform.windows? do\n    if array then\n      it_should_behave_like \"all pathname parameters with arrays\", true\n    end\n\n    it \"should reject a fully qualified unix path\" do\n      path = '/foo'\n      expect { instance(path) }.to raise_error(Puppet::Error, /fully qualified/)\n    end\n\n    it \"should give a useful error when the path is not absolute\" do\n      path = 'foo'\n      expect { instance(path) }.\n        to raise_error Puppet::Error, /fully qualified/\n    end\n\n    it \"also accepts Unix style path separators\" do\n      path = 'C:/Program Files'\n      instance = instance(path)\n      expect(instance[@param]).to eq(path)\n    end\n\n    { \"Unix\" => '/', \"Win32\" => '\\\\' }.each do |style, slash|\n      %w{q Q a A z Z c C}.sort.each do |drive|\n        it \"should accept drive letter '#{drive}' with #{style} path separators \" do\n          path = \"#{drive}:#{slash}Program Files\"\n          instance = instance(path)\n          expect(instance[@param]).to eq(path)\n        end\n      end\n    end\n\n    { \"UNC paths\"            => %q{\\\\\\\\foo\\bar},\n      \"unparsed local paths\" => %q{\\\\\\\\?\\c:\\foo},\n      \"unparsed UNC paths\"   => %q{\\\\\\\\?\\foo\\bar}\n    }.each do |name, path|\n      it \"should accept #{name} as absolute\" do\n        instance = instance(path)\n        expect(instance[@param]).to eq(path)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/store_configs_terminus.rb",
    "content": "shared_examples_for \"a StoreConfigs terminus\" do\n  before :each do\n    Puppet[:storeconfigs] = true\n    Puppet[:storeconfigs_backend] = \"store_configs_testing\"\n  end\n\n  api = [:find, :search, :save, :destroy, :head]\n\n  api.each do |name|\n    it { is_expected.to respond_to(name) }\n  end\n\n  it \"should fail if an invalid backend is configured\" do\n    Puppet[:storeconfigs_backend] = \"synergy\"\n    expect { subject }.to raise_error(ArgumentError, /could not find terminus synergy/i)\n  end\n\n  it \"should wrap the declared backend\" do\n    expect(subject.target.class.name).to eq(:store_configs_testing)\n  end\nend\n"
  },
  {
    "path": "spec/shared_behaviours/things_that_declare_options.rb",
    "content": "# encoding: UTF-8\nshared_examples_for \"things that declare options\" do\n  it \"should support options without arguments\" do\n    thing = add_options_to { option \"--bar\" }\n    expect(thing).to be_option :bar\n  end\n\n  it \"should support options with an empty block\" do\n    thing = add_options_to do\n      option \"--foo\" do\n        # this section deliberately left blank\n      end\n    end\n    expect(thing).to be\n    expect(thing).to be_option :foo\n  end\n\n  { \"--foo=\" => :foo }.each do |input, option|\n    it \"should accept #{name.inspect}\" do\n      thing = add_options_to { option input }\n      expect(thing).to be_option option\n    end\n  end\n\n  it \"should support option documentation\" do\n    text = \"Sturm und Drang (German pronunciation: [ˈʃtʊʁm ʊnt ˈdʁaŋ]) …\"\n\n    thing = add_options_to do\n      option \"--foo\" do\n        description text\n        summary text\n      end\n    end\n\n    expect(thing.get_option(:foo).description).to eq(text)\n  end\n\n  it \"should list all the options\" do\n    thing = add_options_to do\n      option \"--foo\"\n      option \"--bar\", '-b'\n      option \"-q\", \"--quux\"\n      option \"-f\"\n      option \"--baz\"\n    end\n    expect(thing.options).to eq([:foo, :bar, :quux, :f, :baz])\n  end\n\n  it \"should detect conflicts in long options\" do\n    expect {\n      add_options_to do\n        option \"--foo\"\n        option \"--foo\"\n      end\n    }.to raise_error ArgumentError, /Option foo conflicts with existing option foo/i\n  end\n\n  it \"should detect conflicts in short options\" do\n    expect {\n      add_options_to do\n        option \"-f\"\n        option \"-f\"\n      end\n    }.to raise_error ArgumentError, /Option f conflicts with existing option f/\n  end\n\n  [\"-f\", \"--foo\"].each do |option|\n    [\"\", \" FOO\", \"=FOO\", \" [FOO]\", \"=[FOO]\"].each do |argument|\n      input = option + argument\n      it \"should detect conflicts within a single option like #{input.inspect}\" do\n        expect {\n          add_options_to do\n            option input, input\n          end\n        }.to raise_error ArgumentError, /duplicates existing alias/\n      end\n    end\n  end\n\n\n  # Verify the range of interesting conflicts to check for ordering causing\n  # the behaviour to change, or anything exciting like that.\n  [ %w{--foo}, %w{-f}, %w{-f --foo}, %w{--baz -f},\n    %w{-f --baz}, %w{-b --foo}, %w{--foo -b}\n  ].each do |conflict|\n    base = %w{--foo -f}\n    it \"should detect conflicts between #{base.inspect} and #{conflict.inspect}\" do\n      expect {\n        add_options_to do\n          option(*base)\n          option(*conflict)\n        end\n      }.to raise_error ArgumentError, /conflicts with existing option/\n    end\n  end\n\n  it \"should fail if we are not consistent about taking an argument\" do\n    expect { add_options_to do option \"--foo=bar\", \"--bar\" end }.\n      to raise_error ArgumentError, /inconsistent about taking an argument/\n  end\n\n  it \"should not accept optional arguments\" do\n    expect do\n      thing = add_options_to do option \"--foo=[baz]\", \"--bar=[baz]\" end\n      [:foo, :bar].each do |name|\n        expect(thing).to be_option name\n      end\n    end.to raise_error(ArgumentError, /optional arguments are not supported/)\n  end\n\n  describe \"#takes_argument?\" do\n    it \"should detect an argument being absent\" do\n      thing = add_options_to do option \"--foo\" end\n      expect(thing.get_option(:foo)).not_to be_takes_argument\n    end\n    [\"=FOO\", \" FOO\"].each do |input|\n      it \"should detect an argument given #{input.inspect}\" do\n        thing = add_options_to do option \"--foo#{input}\" end\n        expect(thing.get_option(:foo)).to be_takes_argument\n      end\n    end\n  end\n\n  describe \"#optional_argument?\" do\n    it \"should be false if no argument is present\" do\n      option = add_options_to do option \"--foo\" end.get_option(:foo)\n      expect(option).not_to be_takes_argument\n      expect(option).not_to be_optional_argument\n    end\n\n    [\"=FOO\", \" FOO\"].each do |input|\n      it \"should be false if the argument is mandatory (like #{input.inspect})\" do\n        option = add_options_to do option \"--foo#{input}\" end.get_option(:foo)\n      expect(option).to be_takes_argument\n      expect(option).not_to be_optional_argument\n      end\n    end\n\n    [\"=[FOO]\", \" [FOO]\"].each do |input|\n      it \"should fail if the argument is optional (like #{input.inspect})\" do\n        expect do\n          option = add_options_to do option \"--foo#{input}\" end.get_option(:foo)\n          expect(option).to be_takes_argument\n          expect(option).to be_optional_argument\n        end.to raise_error(ArgumentError, /optional arguments are not supported/)\n      end\n    end\n  end\n\n  describe \"#default_to\" do\n    it \"should not have a default value by default\" do\n      option = add_options_to do option \"--foo\" end.get_option(:foo)\n      expect(option).not_to be_has_default\n    end\n\n    it \"should accept a block for the default value\" do\n      option = add_options_to do\n        option \"--foo\" do\n          default_to do\n            12\n          end\n        end\n      end.get_option(:foo)\n\n      expect(option).to be_has_default\n    end\n\n    it \"should invoke the block when asked for the default value\" do\n      invoked = false\n      option = add_options_to do\n        option \"--foo\" do\n          default_to do\n            invoked = true\n          end\n        end\n      end.get_option(:foo)\n\n      expect(option).to be_has_default\n      expect(option.default).to be_truthy\n      expect(invoked).to be_truthy\n    end\n\n    it \"should return the value of the block when asked for the default\" do\n      option = add_options_to do\n        option \"--foo\" do\n          default_to do\n            12\n          end\n        end\n      end.get_option(:foo)\n\n      expect(option).to be_has_default\n      expect(option.default).to eq(12)\n    end\n\n    it \"should invoke the block every time the default is requested\" do\n      option = add_options_to do\n        option \"--foo\" do\n          default_to do\n            {}\n          end\n        end\n      end.get_option(:foo)\n\n      first  = option.default.object_id\n      second = option.default.object_id\n      third  = option.default.object_id\n\n      expect(first).not_to eq(second)\n      expect(first).not_to eq(third)\n      expect(second).not_to eq(third)\n    end\n\n    it \"should fail if the option has a default and is required\" do\n      expect {\n        add_options_to do\n          option \"--foo\" do\n            required\n            default_to do 12 end\n          end\n        end\n      }.to raise_error ArgumentError, /can't be optional and have a default value/\n\n      expect {\n        add_options_to do\n          option \"--foo\" do\n            default_to do 12 end\n            required\n          end\n        end\n      }.to raise_error ArgumentError, /can't be optional and have a default value/\n    end\n\n    it \"should fail if default_to has no block\" do\n      expect { add_options_to do option \"--foo\" do default_to end end }.\n        to raise_error ArgumentError, /default_to requires a block/\n    end\n\n    it \"should fail if default_to is invoked twice\" do\n      expect {\n        add_options_to do\n          option \"--foo\" do\n            default_to do 12 end\n            default_to do \"fun\" end\n          end\n        end\n      }.to raise_error ArgumentError, /already has a default value/\n    end\n\n    [ \"one\", \"one, two\", \"one, *two\" ].each do |input|\n      it \"should fail if the block has the wrong arity (#{input})\" do\n        expect {\n          add_options_to do\n            option \"--foo\" do\n              eval \"default_to do |#{input}| 12 end\"\n            end\n          end\n        }.to raise_error ArgumentError, /should not take any arguments/\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_contexts/checksum.rb",
    "content": "# Shared contexts for testing against all supported checksum types.\n#\n# These helpers define nested rspec example groups to test code against all our\n# supported checksum types. Example groups that need to be run against all\n# types should use the `with_checksum_types` helper which will\n# create a new example group for each types and will run the given block\n# in each example group.\n\nCHECKSUM_PLAINTEXT = \"1\\r\\n\"*4000\nCHECKSUM_TYPES_TO_TRY = [\n  ['md5', 'a7a169ac84bb863b30484d0aa03139c1'],\n  ['md5lite', '22b4182363e81b326e98231fde616782'],\n  ['sha256', '47fcae62967db2fb5cba2fc0d9cf3e6767035d763d825ecda535a7b1928b9746'],\n  ['sha256lite', 'fd50217a2b0286ba25121bf2297bbe6c197933992de67e4e568f19861444ecf8'],\n  ['sha224', '6894cd976b60b2caa825bc699b54f715853659f0243f67cda4dd7ac4'],\n  ['sha384', 'afc3d952fe1a4d3aa083d438ea464f6e7456c048d34ff554340721b463b38547e5ee7c964513dfba0d65dd91ac97deb5'],\n  ['sha512', 'a953dcd95824cfa2a555651585d3980b1091a740a785d52ee5e72a55c9038242433e55026758636b0a29d0e5f9e77f24bc888ea5d5e01ab36d2bbcb3d3163859']\n]\n\nCHECKSUM_STAT_TIME = Time.now\nTIME_TYPES_TO_TRY = [\n  ['ctime', \"#{CHECKSUM_STAT_TIME}\"],\n  ['mtime', \"#{CHECKSUM_STAT_TIME}\"]\n]\n\nshared_context('with supported checksum types') do\n  def self.with_checksum_types(path, file, &block)\n    def checksum_valid(checksum_type, expected_checksum, actual_checksum_signature)\n      case checksum_type\n      when 'mtime', 'ctime'\n        expect(DateTime.parse(actual_checksum_signature)).to be >= DateTime.parse(expected_checksum)\n      else\n        expect(actual_checksum_signature).to eq(\"{#{checksum_type}}#{expected_checksum}\")\n      end\n    end\n\n    def expect_correct_checksum(meta, checksum_type, checksum, type)\n      expect(meta).to_not be_nil\n      expect(meta).to be_instance_of(type)\n      expect(meta.checksum_type).to eq(checksum_type)\n      expect(checksum_valid(checksum_type, checksum, meta.checksum)).to be_truthy\n    end\n\n    (CHECKSUM_TYPES_TO_TRY + TIME_TYPES_TO_TRY).each do |checksum_type, checksum|\n      describe(\"when checksum_type is #{checksum_type}\") do\n        let(:checksum_type) { checksum_type }\n        let(:plaintext) { CHECKSUM_PLAINTEXT }\n        let(:checksum) { checksum }\n        let(:env_path) { tmpfile(path) }\n        let(:checksum_file) { File.join(env_path, file) }\n\n        def digest(content)\n          Puppet::Util::Checksums.send(checksum_type, content)\n        end\n\n        before(:each) do\n          FileUtils.mkdir_p(File.dirname(checksum_file))\n          File.open(checksum_file, \"wb\") { |f| f.write plaintext }\n        end\n\n        instance_eval(&block)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_contexts/digests.rb",
    "content": "# Shared contexts for testing against all supported digest algorithms.\n#\n# These helpers define nested rspec example groups to test code against all our\n# supported digest algorithms. Example groups that need to be run against all\n# algorithms should use the `with_digest_algorithms` helper which will\n# create a new example group for each algorithm and will run the given block\n# in each example group.\n#\n# For each algorithm a shared context is defined for the given algorithm that\n# has precomputed checksum values and paths. These contexts are included\n# automatically based on the rspec metadata selected with\n# `with_digest_algorithms`.\n\nDIGEST_ALGORITHMS_TO_TRY = ['md5', 'sha256', 'sha384', 'sha512', 'sha224']\n\nshared_context('with supported digest algorithms', :uses_checksums => true) do\n\n  def self.with_digest_algorithms(&block)\n    DIGEST_ALGORITHMS_TO_TRY.each do |digest_algorithm|\n      describe(\"when digest_algorithm is #{digest_algorithm}\", :digest_algorithm => digest_algorithm) do\n        instance_eval(&block)\n      end\n    end\n  end\nend\n\nshared_context(\"when digest_algorithm is set to sha256\", :digest_algorithm => 'sha256') do\n  before { Puppet[:digest_algorithm] = 'sha256' }\n  after { Puppet[:digest_algorithm] = nil }\n\n  let(:digest_algorithm) { 'sha256' }\n\n  let(:plaintext) { \"my\\r\\ncontents\" }\n  let(:checksum) { '409a11465ed0938227128b1756c677a8480a8b84814f1963853775e15a74d4b4' }\n  let(:bucket_dir) { '4/0/9/a/1/1/4/6/409a11465ed0938227128b1756c677a8480a8b84814f1963853775e15a74d4b4' }\n\n  def digest(content)\n    Puppet::Util::Checksums.sha256(content)\n  end\nend\n\nshared_context(\"when digest_algorithm is set to md5\", :digest_algorithm => 'md5') do\n  before { Puppet[:digest_algorithm] = 'md5' }\n  after { Puppet[:digest_algorithm] = nil }\n\n  let(:digest_algorithm) { 'md5' }\n\n  let(:plaintext) { \"my\\r\\ncontents\" }\n  let(:checksum) { 'f0d7d4e480ad698ed56aeec8b6bd6dea' }\n  let(:bucket_dir) { 'f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea' }\n\n  def digest(content)\n    Puppet::Util::Checksums.md5(content)\n  end\nend\n\nshared_context(\"when digest_algorithm is set to sha512\", :digest_algorithm => 'sha512') do\n  before { Puppet[:digest_algorithm] = 'sha512' }\n  after { Puppet[:digest_algorithm] = nil }\n\n  let(:digest_algorithm) { 'sha512' }\n\n  let(:plaintext) { \"my\\r\\ncontents\" }\n  let(:checksum) { 'ed9b62ae313c8e4e3e6a96f937101e85f8f8af8d51dea7772177244087e5d6152778605ad6bdb42886ff1436abaec4fa44acbfe171fda755959b52b0e4e015d4' }\n  let(:bucket_dir) { 'e/d/9/b/6/2/a/e/ed9b62ae313c8e4e3e6a96f937101e85f8f8af8d51dea7772177244087e5d6152778605ad6bdb42886ff1436abaec4fa44acbfe171fda755959b52b0e4e015d4' }\n\n  def digest(content)\n    Puppet::Util::Checksums.sha512(content)\n  end\nend\n\nshared_context(\"when digest_algorithm is set to sha384\", :digest_algorithm => 'sha384') do\n  before { Puppet[:digest_algorithm] = 'sha384' }\n  after { Puppet[:digest_algorithm] = nil }\n\n  let(:digest_algorithm) { 'sha384' }\n\n  let(:plaintext) { \"my\\r\\ncontents\" }\n  let(:checksum) { 'f40debfec135e4f2b9fb92110c53aadb8e9bda28bb05f09901480fd70126fe3b70f9f074ce6182ec8184eb1bcabe4440' }\n  let(:bucket_dir) { 'f/4/0/d/e/b/f/e/f40debfec135e4f2b9fb92110c53aadb8e9bda28bb05f09901480fd70126fe3b70f9f074ce6182ec8184eb1bcabe4440' }\n\n  def digest(content)\n    Puppet::Util::Checksums.sha384(content)\n  end\nend\n\nshared_context(\"when digest_algorithm is set to sha224\", :digest_algorithm => 'sha224') do\n  before { Puppet[:digest_algorithm] = 'sha224' }\n  after { Puppet[:digest_algorithm] = nil }\n\n  let(:digest_algorithm) { 'sha224' }\n\n  let(:plaintext) { \"my\\r\\ncontents\" }\n  let(:checksum) { 'b8c05079b24c37a0e03f03e611167a3ea24455db3ad638a3a0c7e9cb' }\n  let(:bucket_dir) { 'b/8/c/0/5/0/7/9/b8c05079b24c37a0e03f03e611167a3ea24455db3ad638a3a0c7e9cb' }\n\n  def digest(content)\n    Puppet::Util::Checksums.sha224(content)\n  end\nend\n"
  },
  {
    "path": "spec/shared_contexts/https.rb",
    "content": "require 'spec_helper'\n\nRSpec.shared_context('https client') do\n  before :all do\n    WebMock.disable!\n  end\n\n  after :all do\n    WebMock.enable!\n  end\n\n  before :each do\n    # make sure we don't take too long\n    Puppet[:http_connect_timeout] = '5s'\n    Puppet[:server] = '127.0.0.1'\n    Puppet[:certname] = '127.0.0.1'\n\n    Puppet[:localcacert] = File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'ca.pem')\n    Puppet[:hostcrl] = File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'crl.pem')\n    Puppet[:hostprivkey] = File.join(PuppetSpec::FIXTURE_DIR, 'ssl', '127.0.0.1-key.pem')\n    Puppet[:hostcert] = File.join(PuppetSpec::FIXTURE_DIR, 'ssl', '127.0.0.1.pem')\n\n    # set in memory facts since certname is changed above\n    facts = Puppet::Node::Facts.new(Puppet[:certname])\n    Puppet::Node::Facts.indirection.save(facts)\n  end\n\n  let(:https_server) { PuppetSpec::HTTPSServer.new }\nend\n"
  },
  {
    "path": "spec/shared_contexts/l10n.rb",
    "content": "require 'spec_helper'\n\nRSpec.shared_context('l10n') do |locale|\n  before :all do\n    @old_locale = Locale.current\n    Locale.current = locale\n\n    @old_gettext_disabled = Puppet::GettextConfig.instance_variable_get(:@gettext_disabled)\n    Puppet::GettextConfig.instance_variable_set(:@gettext_disabled, false)\n    Puppet::GettextConfig.setup_locale\n    Puppet::GettextConfig.create_default_text_domain\n\n    # overwrite stubs with real implementation\n    ::Object.send(:remove_method, :_)\n    ::Object.send(:remove_method, :n_)\n    class ::Object\n      include FastGettext::Translation\n    end\n  end\n\n  after :all do\n    Locale.current = @old_locale\n\n    Puppet::GettextConfig.instance_variable_set(:@gettext_disabled, @old_gettext_disabled)\n    # restore stubs\n    load File.expand_path(File.join(__dir__, '../../lib/puppet/gettext/stubs.rb'))\n  end\n\n  before :each do\n    Puppet[:disable_i18n] = false\n  end\nend\n"
  },
  {
    "path": "spec/shared_contexts/provider.rb",
    "content": "require 'spec_helper'\n\nRSpec.shared_context('provider specificity') do\n  around do |example|\n    old_defaults = described_class.instance_variable_get(:@defaults)\n    old_notdefaults = described_class.instance_variable_get(:@notdefaults)\n    begin\n      described_class.instance_variable_set(:@defaults, [])\n      described_class.instance_variable_set(:@notdefaults, [])\n      example.run\n    ensure\n      described_class.instance_variable_set(:@defaults, old_defaults)\n      described_class.instance_variable_set(:@notdefaults, old_notdefaults)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/shared_contexts/types_setup.rb",
    "content": "shared_context 'types_setup' do\n\n  # Do not include the special type Unit in this list\n  # Do not include the type Variant in this list as it needs to be parameterized to be meaningful\n  def self.all_types\n    [ Puppet::Pops::Types::PAnyType,\n      Puppet::Pops::Types::PUndefType,\n      Puppet::Pops::Types::PNotUndefType,\n      Puppet::Pops::Types::PScalarType,\n      Puppet::Pops::Types::PScalarDataType,\n      Puppet::Pops::Types::PStringType,\n      Puppet::Pops::Types::PNumericType,\n      Puppet::Pops::Types::PIntegerType,\n      Puppet::Pops::Types::PFloatType,\n      Puppet::Pops::Types::PRegexpType,\n      Puppet::Pops::Types::PBooleanType,\n      Puppet::Pops::Types::PCollectionType,\n      Puppet::Pops::Types::PArrayType,\n      Puppet::Pops::Types::PHashType,\n      Puppet::Pops::Types::PIterableType,\n      Puppet::Pops::Types::PIteratorType,\n      Puppet::Pops::Types::PRuntimeType,\n      Puppet::Pops::Types::PClassType,\n      Puppet::Pops::Types::PResourceType,\n      Puppet::Pops::Types::PPatternType,\n      Puppet::Pops::Types::PEnumType,\n      Puppet::Pops::Types::PStructType,\n      Puppet::Pops::Types::PTupleType,\n      Puppet::Pops::Types::PCallableType,\n      Puppet::Pops::Types::PTypeType,\n      Puppet::Pops::Types::POptionalType,\n      Puppet::Pops::Types::PDefaultType,\n      Puppet::Pops::Types::PTypeReferenceType,\n      Puppet::Pops::Types::PTypeAliasType,\n      Puppet::Pops::Types::PSemVerType,\n      Puppet::Pops::Types::PSemVerRangeType,\n      Puppet::Pops::Types::PTimespanType,\n      Puppet::Pops::Types::PTimestampType,\n      Puppet::Pops::Types::PSensitiveType,\n      Puppet::Pops::Types::PBinaryType,\n      Puppet::Pops::Types::PInitType,\n      Puppet::Pops::Types::PURIType,\n    ]\n  end\n  def all_types\n    self.class.all_types\n  end\n\n  # Do not include the Variant type in this list - while it is abstract it is also special in that\n  # it must be parameterized to be meaningful.\n  #\n  def self.abstract_types\n    [ Puppet::Pops::Types::PAnyType,\n      Puppet::Pops::Types::PCallableType,\n      Puppet::Pops::Types::PEnumType,\n      Puppet::Pops::Types::PClassType,\n      Puppet::Pops::Types::PDefaultType,\n      Puppet::Pops::Types::PCollectionType,\n      Puppet::Pops::Types::PInitType,\n      Puppet::Pops::Types::PIterableType,\n      Puppet::Pops::Types::PIteratorType,\n      Puppet::Pops::Types::PNotUndefType,\n      Puppet::Pops::Types::PResourceType,\n      Puppet::Pops::Types::PRuntimeType,\n      Puppet::Pops::Types::POptionalType,\n      Puppet::Pops::Types::PPatternType,\n      Puppet::Pops::Types::PScalarType,\n      Puppet::Pops::Types::PScalarDataType,\n      Puppet::Pops::Types::PUndefType,\n      Puppet::Pops::Types::PTypeReferenceType,\n      Puppet::Pops::Types::PTypeAliasType,\n    ]\n  end\n  def abstract_types\n    self.class.abstract_types\n  end\n\n  # Internal types. Not meaningful in pp\n  def self.internal_types\n    [ Puppet::Pops::Types::PTypeReferenceType,\n      Puppet::Pops::Types::PTypeAliasType,\n    ]\n  end\n  def internal_types\n    self.class.internal_types\n  end\n\n\n  def self.scalar_data_types\n    # PVariantType is also scalar data, if its types are all ScalarData\n    [\n      Puppet::Pops::Types::PScalarDataType,\n      Puppet::Pops::Types::PStringType,\n      Puppet::Pops::Types::PNumericType,\n      Puppet::Pops::Types::PIntegerType,\n      Puppet::Pops::Types::PFloatType,\n      Puppet::Pops::Types::PBooleanType,\n      Puppet::Pops::Types::PEnumType,\n      Puppet::Pops::Types::PPatternType,\n    ]\n  end\n  def scalar_data_types\n    self.class.scalar_data_types\n  end\n\n\n  def self.scalar_types\n    # PVariantType is also scalar, if its types are all Scalar\n    [\n      Puppet::Pops::Types::PScalarType,\n      Puppet::Pops::Types::PScalarDataType,\n      Puppet::Pops::Types::PStringType,\n      Puppet::Pops::Types::PNumericType,\n      Puppet::Pops::Types::PIntegerType,\n      Puppet::Pops::Types::PFloatType,\n      Puppet::Pops::Types::PRegexpType,\n      Puppet::Pops::Types::PBooleanType,\n      Puppet::Pops::Types::PPatternType,\n      Puppet::Pops::Types::PEnumType,\n      Puppet::Pops::Types::PSemVerType,\n      Puppet::Pops::Types::PTimespanType,\n      Puppet::Pops::Types::PTimestampType,\n    ]\n  end\n  def scalar_types\n    self.class.scalar_types\n  end\n\n  def self.numeric_types\n    # PVariantType is also numeric, if its types are all numeric\n    [\n      Puppet::Pops::Types::PNumericType,\n      Puppet::Pops::Types::PIntegerType,\n      Puppet::Pops::Types::PFloatType,\n    ]\n  end\n  def numeric_types\n    self.class.numeric_types\n  end\n\n  def self.string_types\n    # PVariantType is also string type, if its types are all compatible\n    [\n      Puppet::Pops::Types::PStringType,\n      Puppet::Pops::Types::PPatternType,\n      Puppet::Pops::Types::PEnumType,\n    ]\n  end\n  def string_types\n    self.class.string_types\n  end\n\n  def self.collection_types\n    # PVariantType is also string type, if its types are all compatible\n    [\n      Puppet::Pops::Types::PCollectionType,\n      Puppet::Pops::Types::PHashType,\n      Puppet::Pops::Types::PArrayType,\n      Puppet::Pops::Types::PStructType,\n      Puppet::Pops::Types::PTupleType,\n    ]\n  end\n  def collection_types\n    self.class.collection_types\n  end\n\n  def self.data_compatible_types\n    tf = Puppet::Pops::Types::TypeFactory\n    result = scalar_data_types\n    result << Puppet::Pops::Types::PArrayType.new(tf.data)\n    result << Puppet::Pops::Types::PHashType.new(Puppet::Pops::Types::PStringType::DEFAULT, tf.data)\n    result << Puppet::Pops::Types::PUndefType\n    result << Puppet::Pops::Types::PTupleType.new([tf.data])\n    result\n  end\n  def data_compatible_types\n    self.class.data_compatible_types\n  end\n\n  def self.rich_data_compatible_types\n    tf = Puppet::Pops::Types::TypeFactory\n    result = scalar_types\n    result << Puppet::Pops::Types::PArrayType.new(tf.rich_data)\n    result << Puppet::Pops::Types::PHashType.new(tf.rich_data_key, tf.rich_data)\n    result << Puppet::Pops::Types::PUndefType\n    result << Puppet::Pops::Types::PDefaultType\n    result << Puppet::Pops::Types::PURIType\n    result << Puppet::Pops::Types::PTupleType.new([tf.rich_data])\n    result << Puppet::Pops::Types::PObjectType\n    result << Puppet::Pops::Types::PTypeType\n    result << Puppet::Pops::Types::PTypeSetType\n    result\n  end\n  def rich_data_compatible_types\n    self.class.rich_data_compatible_types\n  end\n\n  def self.type_from_class(c)\n    c.is_a?(Class) ? c::DEFAULT : c\n  end\n  def type_from_class(c)\n    self.class.type_from_class(c)\n  end\nend\n"
  },
  {
    "path": "spec/shared_examples/rhel_package_provider.rb",
    "content": "shared_examples \"RHEL package provider\" do |provider_class, provider_name|\n  describe provider_name do\n    let(:name) { 'mypackage' }\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name     => name,\n        :ensure   => :installed,\n        :provider => provider_name\n      )\n    end\n    let(:provider) do\n      provider = provider_class.new\n      provider.resource = resource\n      provider\n    end\n    let(:arch) { 'x86_64' }\n    let(:arch_resource) do\n      Puppet::Type.type(:package).new(\n        :name     => \"#{name}.#{arch}\",\n        :ensure   => :installed,\n        :provider => provider_name\n      )\n    end\n    let(:arch_provider) do\n      provider = provider_class.new\n      provider.resource = arch_resource\n      provider\n    end\n\n    case provider_name\n    when 'yum'\n      let(:error_level) { '0' }\n    when 'dnf'\n      let(:error_level) { '1' }\n    when 'tdnf'\n      let(:error_level) { '1' }\n    end\n\n    case provider_name\n    when 'yum'\n      let(:upgrade_command) { 'update' }\n    when 'dnf'\n      let(:upgrade_command) { 'upgrade' }\n    when 'tdnf'\n      let(:upgrade_command) { 'upgrade' }\n    end\n\n    before do\n      allow(provider_class).to receive(:command).with(:cmd).and_return(\"/usr/bin/#{provider_name}\")\n      allow(provider).to receive(:rpm).and_return('rpm')\n      allow(provider).to receive(:get).with(:version).and_return('1')\n      allow(provider).to receive(:get).with(:release).and_return('1')\n      allow(provider).to receive(:get).with(:arch).and_return('i386')\n    end\n\n    describe 'provider features' do\n      it { is_expected.to be_versionable }\n      it { is_expected.to be_install_options }\n      it { is_expected.to be_virtual_packages }\n    end\n    # provider should repond to the following methods\n     [:install, :latest, :update, :purge, :install_options].each do |method|\n       it \"should have a(n) #{method}\" do\n         expect(provider).to respond_to(method)\n      end\n    end\n    describe 'when installing' do\n      before(:each) do\n        allow(Puppet::Util).to receive(:which).with(\"rpm\").and_return(\"/bin/rpm\")\n        allow(provider).to receive(:which).with(\"rpm\").and_return(\"/bin/rpm\")\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/bin/rpm\", \"--version\"], {:combine => true, :custom_environment => {}, :failonfail => true}).and_return(Puppet::Util::Execution::ProcessOutput.new(\"4.10.1\\n\", 0)).at_most(:once)\n        allow(Facter).to receive(:value).with('os.release.major').and_return('6')\n      end\n\n      it \"should call #{provider_name} install for :installed\" do\n        allow(resource).to receive(:should).with(:ensure).and_return(:installed)\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, 'mypackage'])\n        provider.install\n      end\n\n      if provider_name == 'yum'\n        context 'on el-5' do\n          before(:each) do\n            allow(Facter).to receive(:value).with('os.release.major').and_return('5')\n          end\n\n          it \"should catch #{provider_name} install failures when status code is wrong\" do\n            allow(resource).to receive(:should).with(:ensure).and_return(:installed)\n            expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-e', error_level, '-y', :install, name]).and_return(Puppet::Util::Execution::ProcessOutput.new(\"No package #{name} available.\", 0))\n            expect {\n              provider.install\n            }.to raise_error(Puppet::Error, \"Could not find package #{name}\")\n          end\n        end\n      end\n\n      it 'should use :install to update' do\n        expect(provider).to receive(:install)\n        provider.update\n      end\n\n      it 'should be able to set version' do\n        version = '1.2'\n        resource[:ensure] = version\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, \"#{name}-#{version}\"])\n        allow(provider).to receive(:query).and_return(:ensure => version)\n        provider.install\n      end\n      it 'should handle partial versions specified' do\n        version = '1.3.4'\n        resource[:ensure] = version\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, 'mypackage-1.3.4'])\n        allow(provider).to receive(:query).and_return(:ensure => '1.3.4-1.el6')\n        provider.install\n      end\n\n      it 'should be able to downgrade' do\n        current_version = '1.2'\n        version = '1.0'\n        resource[:ensure] = '1.0'\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :downgrade, \"#{name}-#{version}\"])\n        allow(provider).to receive(:query).and_return({:ensure => current_version}, {:ensure => version})\n        provider.install\n      end\n\n      it 'should be able to upgrade' do\n        current_version = '1.0'\n        version = '1.2'\n        resource[:ensure] = '1.2'\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', upgrade_command, \"#{name}-#{version}\"])\n        allow(provider).to receive(:query).and_return({:ensure => current_version}, {:ensure => version})\n        provider.install\n      end\n\n      it 'should not run upgrade command if absent and ensure latest' do\n        current_version = ''\n        version = '1.2'\n        resource[:ensure] = :latest\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, name])\n        allow(provider).to receive(:query).and_return({:ensure => current_version}, {:ensure => version})\n        provider.install\n      end\n\n      it 'should run upgrade command if present and ensure latest' do\n        current_version = '1.0'\n        version = '1.2'\n        resource[:ensure] = :latest\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', upgrade_command, name])\n        allow(provider).to receive(:query).and_return({:ensure => current_version}, {:ensure => version})\n        provider.install\n      end\n\n      it 'should accept install options' do\n        resource[:ensure] = :installed\n        resource[:install_options] = ['-t', {'-x' => 'expackage'}]\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', ['-t', '-x=expackage'], :install, name])\n        provider.install\n      end\n\n      it 'allow virtual packages' do\n        resource[:ensure] = :installed\n        resource[:allow_virtual] = true\n        expect(Puppet::Util::Execution).not_to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :list, name])\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, name])\n        provider.install\n      end\n\n      it 'moves architecture to end of version' do\n        version = '1.2.3'\n        arch_resource[:ensure] = version\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, \"#{name}-#{version}.#{arch}\"])\n        allow(arch_provider).to receive(:query).and_return(:ensure => version)\n        arch_provider.install\n      end\n\n      it \"does not move '-noarch' to the end of version\" do\n        version = '1.2.3'\n        resource = Puppet::Type.type(:package).new(\n          :name => \"#{name}-noarch\",\n          :ensure => version,\n          :provider =>provider_name\n        )\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-d', '0', '-e', error_level, '-y', :install, \"#{name}-noarch-#{version}\"])\n        provider = provider_class.new\n        provider.resource = resource\n        allow(provider).to receive(:query).and_return(:ensure => version)\n        provider.install\n      end\n    end\n\n    describe 'when uninstalling' do\n      it 'should use erase to purge' do\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"/usr/bin/#{provider_name}\", '-y', :erase, name])\n        provider.purge\n      end\n    end\n\n    it 'should be versionable' do\n      expect(provider).to be_versionable\n    end\n\n    describe 'determining the latest version available for a package' do\n      it \"passes the value of enablerepo install_options when querying\" do\n        resource[:install_options] = [\n          {'--enablerepo' => 'contrib'},\n          {'--enablerepo' => 'centosplus'},\n        ]\n        allow(provider).to receive(:properties).and_return({:ensure => '3.4.5'})\n        expect(described_class).to receive(:latest_package_version).with(name, [], ['contrib', 'centosplus'], [])\n        provider.latest\n      end\n\n      it \"passes the value of disablerepo install_options when querying\" do\n        resource[:install_options] = [\n          {'--disablerepo' => 'updates'},\n          {'--disablerepo' => 'centosplus'},\n        ]\n        allow(provider).to receive(:properties).and_return({:ensure => '3.4.5'})\n        expect(described_class).to receive(:latest_package_version).with(name, ['updates', 'centosplus'], [], [])\n        provider.latest\n      end\n\n      it \"passes the value of disableexcludes install_options when querying\" do\n        resource[:install_options] = [\n          {'--disableexcludes' => 'main'},\n          {'--disableexcludes' => 'centosplus'},\n        ]\n        allow(provider).to receive(:properties).and_return({:ensure => '3.4.5'})\n        expect(described_class).to receive(:latest_package_version).with(name, [], [], ['main', 'centosplus'])\n        provider.latest\n      end\n\n      describe 'and a newer version is not available' do\n        before :each do\n          allow(described_class).to receive(:latest_package_version).with(name, [], [], []).and_return(nil)\n        end\n\n        it 'raises an error the package is not installed' do\n          allow(provider).to receive(:properties).and_return({:ensure => :absent})\n          expect {\n            provider.latest\n          }.to raise_error(Puppet::DevError, 'Tried to get latest on a missing package')\n        end\n\n        it 'returns version of the currently installed package' do\n          allow(provider).to receive(:properties).and_return({:ensure => '3.4.5'})\n          expect(provider.latest).to eq('3.4.5')\n        end\n      end\n\n      describe 'and a newer version is available' do\n        let(:latest_version) do\n          {\n            :name     => name,\n            :epoch    => '1',\n            :version  => '2.3.4',\n            :release  => '5',\n            :arch     => 'i686',\n          }\n        end\n\n        it 'includes the epoch in the version string' do\n          allow(described_class).to receive(:latest_package_version).with(name, [], [], []).and_return(latest_version)\n          expect(provider.latest).to eq('1:2.3.4-5')\n        end\n      end\n    end\n\n    describe \"lazy loading of latest package versions\" do\n      before { described_class.clear }\n      after { described_class.clear }\n      let(:mypackage_version) do\n        {\n          :name     => name,\n          :epoch    => '1',\n          :version  => '2.3.4',\n          :release  => '5',\n          :arch     => 'i686',\n        }\n      end\n      let(:mypackage_newerversion) do\n        {\n          :name     => name,\n          :epoch    => '1',\n          :version  => '4.5.6',\n          :release  => '7',\n          :arch     => 'i686',\n        }\n      end\n      let(:latest_versions) { {name => [mypackage_version]} }\n      let(:enabled_versions) { {name => [mypackage_newerversion]} }\n\n      it \"returns the version hash if the package was found\" do\n        expect(described_class).to receive(:check_updates).with([], [], []).once.and_return(latest_versions)\n        version = described_class.latest_package_version(name, [], [], [])\n        expect(version).to eq(mypackage_version)\n      end\n\n      it \"is nil if the package was not found in the query\" do\n        expect(described_class).to receive(:check_updates).with([], [], []).once.and_return(latest_versions)\n        version = described_class.latest_package_version('nopackage', [], [], [])\n        expect(version).to be_nil\n      end\n\n      it \"caches the package list and reuses that for subsequent queries\" do\n        expect(described_class).to receive(:check_updates).with([], [], []).once.and_return(latest_versions)\n        2.times {\n          version = described_class.latest_package_version(name, [], [], [])\n          expect(version).to eq mypackage_version\n        }\n      end\n\n      it \"caches separate lists for each combination of 'disablerepo' and 'enablerepo' and 'disableexcludes'\" do\n        expect(described_class).to receive(:check_updates).with([], [], []).once.and_return(latest_versions)\n        expect(described_class).to receive(:check_updates).with(['disabled'], ['enabled'], ['disableexcludes']).once.and_return(enabled_versions)\n        2.times {\n          version = described_class.latest_package_version(name, [], [], [])\n          expect(version).to eq mypackage_version\n        }\n        2.times {\n          version = described_class.latest_package_version(name, ['disabled'], ['enabled'], ['disableexcludes'])\n          expect(version).to eq(mypackage_newerversion)\n        }\n      end\n    end\n\n    describe \"executing #{provider_name} check-update\" do\n      it \"passes repos to enable to '#{provider_name} check-update'\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          %W[/usr/bin/#{provider_name} check-update --enablerepo=updates --enablerepo=centosplus],\n          any_args\n        ).and_return(double(:exitstatus => 0))\n        described_class.check_updates([], %W[updates centosplus], [])\n      end\n\n      it \"passes repos to disable to '#{provider_name} check-update'\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          %W[/usr/bin/#{provider_name} check-update --disablerepo=updates --disablerepo=centosplus],\n          any_args\n        ).and_return(double(:exitstatus => 0))\n        described_class.check_updates(%W[updates centosplus], [], [])\n      end\n\n      it \"passes a combination of repos to enable and disable to '#{provider_name} check-update'\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          %W[/usr/bin/#{provider_name} check-update --disablerepo=updates --disablerepo=centosplus --enablerepo=os --enablerepo=contrib ],\n          any_args\n        ).and_return(double(:exitstatus => 0))\n        described_class.check_updates(%W[updates centosplus], %W[os contrib], [])\n      end\n\n      it \"passes disableexcludes to '#{provider_name} check-update'\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          %W[/usr/bin/#{provider_name} check-update --disableexcludes=main --disableexcludes=centosplus],\n          any_args\n        ).and_return(double(:exitstatus => 0))\n        described_class.check_updates([], [], %W[main centosplus])\n      end\n\n      it \"passes all options to '#{provider_name} check-update'\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          %W[/usr/bin/#{provider_name} check-update --disablerepo=a --disablerepo=b --enablerepo=c --enablerepo=d --disableexcludes=e --disableexcludes=f],\n          any_args\n        ).and_return(double(:exitstatus => 0))\n        described_class.check_updates(%W[a b], %W[c d], %W[e f])\n      end\n\n      it \"returns an empty hash if '#{provider_name} check-update' returned 0\" do\n        expect(Puppet::Util::Execution).to receive(:execute).and_return(double(:exitstatus => 0))\n        expect(described_class.check_updates([], [], [])).to be_empty\n      end\n\n      it \"returns a populated hash if '#{provider_name} check-update returned 100'\" do\n        output = double(:exitstatus => 100)\n        expect(Puppet::Util::Execution).to receive(:execute).and_return(output)\n        expect(described_class).to receive(:parse_updates).with(output).and_return({:has => :updates})\n        expect(described_class.check_updates([], [], [])).to eq({:has => :updates})\n      end\n\n      it \"returns an empty hash if '#{provider_name} check-update' returned an exit code that was not 0 or 100\" do\n        expect(Puppet::Util::Execution).to receive(:execute).and_return(double(:exitstatus => 1))\n        expect(described_class).to receive(:warning).with(\"Could not check for updates, \\'/usr/bin/#{provider_name} check-update\\' exited with 1\")\n        expect(described_class.check_updates([], [], [])).to eq({})\n      end\n    end\n\n    describe \"parsing a line from #{provider_name} check-update\" do\n      it \"splits up the package name and architecture fields\" do\n        checkupdate = %W[curl.i686 7.32.0-10.fc20]\n        parsed = described_class.update_to_hash(*checkupdate)\n        expect(parsed[:name]).to eq 'curl'\n        expect(parsed[:arch]).to eq 'i686'\n      end\n\n      it \"splits up the epoch, version, and release fields\" do\n        checkupdate = %W[dhclient.i686 12:4.1.1-38.P1.el6.centos]\n        parsed = described_class.update_to_hash(*checkupdate)\n        expect(parsed[:epoch]).to eq '12'\n        expect(parsed[:version]).to eq '4.1.1'\n        expect(parsed[:release]).to eq '38.P1.el6.centos'\n      end\n\n      it \"sets the epoch to 0 when an epoch is not specified\" do\n        checkupdate = %W[curl.i686 7.32.0-10.fc20]\n        parsed = described_class.update_to_hash(*checkupdate)\n        expect(parsed[:epoch]).to eq '0'\n        expect(parsed[:version]).to eq '7.32.0'\n        expect(parsed[:release]).to eq '10.fc20'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# NOTE: a lot of the stuff in this file is duplicated in the \"puppet_spec_helper\" in the project\n#  puppetlabs_spec_helper.  We should probably eat our own dog food and get rid of most of this from here,\n#  and have the puppet core itself use puppetlabs_spec_helper\n\ndir = File.expand_path(File.dirname(__FILE__))\n$LOAD_PATH.unshift File.join(dir, 'lib')\n\n# Don't want puppet getting the command line arguments for rake\nARGV.clear\n\nbegin\n  require 'rubygems'\nrescue LoadError\nend\n\nrequire 'puppet'\n\n# Stub out gettext's `_` and `n_()` methods, which attempt to load translations.\n# Several of our mocks (mostly around file system interaction) are broken by\n# FastGettext's implementation of these methods.\nrequire 'puppet/gettext/stubs'\n\nrequire 'rspec'\nrequire 'rspec/its'\n\n# So everyone else doesn't have to include this base constant.\nmodule PuppetSpec\n  FIXTURE_DIR = File.join(File.expand_path(File.dirname(__FILE__)), \"fixtures\") unless defined?(FIXTURE_DIR)\nend\n\nrequire 'pathname'\nrequire 'tmpdir'\nrequire 'fileutils'\n\nrequire 'puppet_spec/verbose'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/settings'\nrequire 'puppet_spec/fixtures'\nrequire 'puppet_spec/matchers'\nrequire 'puppet_spec/unindent'\nrequire 'puppet/test/test_helper'\n\nPathname.glob(\"#{dir}/shared_contexts/*.rb\") do |file|\n  require file.relative_path_from(Pathname.new(dir))\nend\n\nPathname.glob(\"#{dir}/shared_behaviours/**/*.rb\") do |behaviour|\n  require behaviour.relative_path_from(Pathname.new(dir))\nend\n\nPathname.glob(\"#{dir}/shared_examples/**/*.rb\") do |behaviour|\n  require behaviour.relative_path_from(Pathname.new(dir))\nend\n\nrequire 'webmock/rspec'\nrequire 'vcr'\nVCR.configure do |vcr|\n  vcr.cassette_library_dir = File.expand_path('vcr/cassettes', PuppetSpec::FIXTURE_DIR)\n  vcr.hook_into :webmock\n  vcr.configure_rspec_metadata!\n  # Uncomment next line to debug vcr\n  # vcr.debug_logger = $stderr\nend\n\n# Disable VCR by default\nVCR.turn_off!\n\nRSpec.configure do |config|\n  include PuppetSpec::Fixtures\n\n  exclude_filters = {}\n  exclude_filters[:benchmark] = true unless ENV['BENCHMARK']\n  config.filter_run_excluding exclude_filters\n\n  config.filter_run_when_matching :focus\n\n  config.mock_with :rspec do |mocks|\n    mocks.verify_partial_doubles = true\n  end\n\n  tmpdir = Puppet::FileSystem.expand_path(Dir.mktmpdir(\"rspecrun\"))\n  oldtmpdir = Puppet::FileSystem.expand_path(Dir.tmpdir())\n  ENV['TMPDIR'] = tmpdir\n\n  Puppet::Test::TestHelper.initialize\n\n  config.before :all do\n    Puppet::Test::TestHelper.before_all_tests()\n    if ENV['PROFILE'] == 'all'\n      require 'ruby-prof'\n      RubyProf.start\n    end\n  end\n\n  config.after :all do\n    if ENV['PROFILE'] == 'all'\n      require 'ruby-prof'\n      result = RubyProf.stop\n      printer = RubyProf::CallTreePrinter.new(result)\n      open(File.join(ENV['PROFILEOUT'],\"callgrind.all.#{Time.now.to_i}.trace\"), \"w\") do |f|\n        printer.print(f)\n      end\n    end\n\n    Puppet::Test::TestHelper.after_all_tests()\n  end\n\n  config.before :each do |test|\n    # Disabling garbage collection inside each test, and only running it at\n    # the end of each block, gives us an ~ 15 percent speedup, and more on\n    # some platforms *cough* windows *cough* that are a little slower.\n    GC.disable\n\n    # TODO: in a more sane world, we'd move this logging redirection into our TestHelper class.\n    #  Without doing so, external projects will all have to roll their own solution for\n    #  redirecting logging, and for validating expected log messages.  However, because the\n    #  current implementation of this involves creating an instance variable \"@logs\" on\n    #  EVERY SINGLE TEST CLASS, and because there are over 1300 tests that are written to expect\n    #  this instance variable to be available--we can't easily solve this problem right now.\n    #\n    # redirecting logging away from console, because otherwise the test output will be\n    #  obscured by all of the log output\n    @logs = []\n    Puppet::Util::Log.close_all\n    if ENV[\"PUPPET_TEST_LOG_LEVEL\"]\n      Puppet::Util::Log.level = ENV[\"PUPPET_TEST_LOG_LEVEL\"].intern\n    end\n    if ENV[\"PUPPET_TEST_LOG\"]\n      Puppet::Util::Log.newdestination(ENV[\"PUPPET_TEST_LOG\"])\n      m = test.metadata\n      Puppet.notice(\"*** BEGIN TEST #{m[:file_path]}:#{m[:line_number]}\")\n    end\n    Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))\n\n    @log_level = Puppet::Util::Log.level\n\n    base = PuppetSpec::Files.tmpdir('tmp_settings')\n    Puppet[:vardir] = File.join(base, 'var')\n    Puppet[:publicdir] = File.join(base, 'public')\n    Puppet[:confdir] = File.join(base, 'etc')\n    Puppet[:codedir] = File.join(base, 'code')\n    Puppet[:logdir] = \"$vardir/log\"\n    Puppet[:rundir] = \"$vardir/run\"\n    Puppet[:hiera_config] = File.join(base, 'hiera')\n\n    FileUtils.mkdir_p Puppet[:statedir]\n    FileUtils.mkdir_p Puppet[:publicdir]\n\n    Puppet::Test::TestHelper.before_each_test()\n  end\n\n  # Facter 2 uses two versions of the GCE API, so match using regex\n  PUPPET_FACTER_2_GCE_URL = %r{^http://metadata/computeMetadata/v1(beta1)?}.freeze\n  PUPPET_FACTER_3_GCE_URL = \"http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=json\".freeze\n\n  # Facter azure metadata endpoint\n  PUPPET_FACTER_AZ_URL = \"http://169.254.169.254/metadata/instance?api-version=2020-09-01\"\n\n  # Facter EC2 endpoint\n  PUPPET_FACTER_EC2_METADATA = 'http://169.254.169.254/latest/meta-data/'\n  PUPPET_FACTER_EC2_USERDATA = 'http://169.254.169.254/latest/user-data/'\n\n  config.around :each do |example|\n    # Ignore requests from Facter to external services\n    stub_request(:get, PUPPET_FACTER_2_GCE_URL)\n    stub_request(:get, PUPPET_FACTER_3_GCE_URL)\n    stub_request(:get, PUPPET_FACTER_AZ_URL)\n    stub_request(:get, PUPPET_FACTER_EC2_METADATA)\n    stub_request(:get, PUPPET_FACTER_EC2_USERDATA)\n\n    # Enable VCR if the example is tagged with `:vcr` metadata.\n    if example.metadata[:vcr]\n      VCR.turn_on!\n      begin\n        example.run\n      ensure\n        VCR.turn_off!\n      end\n    else\n      example.run\n    end\n  end\n\n  config.after :each do\n    Puppet::Test::TestHelper.after_each_test()\n\n    # TODO: would like to move this into puppetlabs_spec_helper, but there are namespace issues at the moment.\n    allow(Dir).to receive(:entries).and_call_original\n    PuppetSpec::Files.cleanup\n\n    # TODO: this should be abstracted in the future--see comments above the '@logs' block in the\n    #  \"before\" code above.\n    #\n    # clean up after the logging changes that we made before each test.\n    @logs.clear\n    Puppet::Util::Log.close_all\n    Puppet::Util::Log.level = @log_level\n\n    # This will perform a GC between tests, but only if actually required.  We\n    # experimented with forcing a GC run, and that was less efficient than\n    # just letting it run all the time.\n    GC.enable\n  end\n\n  config.after :suite do\n    # Log the spec order to a file, but only if the LOG_SPEC_ORDER environment variable is\n    #  set.  This should be enabled on Jenkins runs, as it can be used with Nick L.'s bisect\n    #  script to help identify and debug order-dependent spec failures.\n    if ENV['LOG_SPEC_ORDER']\n      File.open(\"./spec_order.txt\", \"w\") do |logfile|\n        config.instance_variable_get(:@files_to_run).each { |f| logfile.puts f }\n      end\n    end\n\n    # return to original tmpdir\n    ENV['TMPDIR'] = oldtmpdir\n    FileUtils.rm_rf(tmpdir)\n  end\n\n  if ENV['PROFILE']\n    require 'ruby-prof'\n\n    def profile\n      result = RubyProf.profile { yield }\n      name = RSpec.current_example.metadata[:full_description].downcase.gsub(/[^a-z0-9_-]/, \"-\").gsub(/-+/, \"-\")\n      printer = RubyProf::CallTreePrinter.new(result)\n      open(File.join(ENV['PROFILEOUT'],\"callgrind.#{name}.#{Time.now.to_i}.trace\"), \"w\") do |f|\n        printer.print(f)\n      end\n    end\n\n    config.around(:each) do |example|\n      if ENV['PROFILE'] == 'each' or (example.metadata[:profile] and ENV['PROFILE'])\n        profile { example.run }\n      else\n        example.run\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/agent/disabler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/agent'\nrequire 'puppet/agent/locker'\n\nclass DisablerTester\n  include Puppet::Agent::Disabler\nend\n\ndescribe Puppet::Agent::Disabler do\n  before do\n    @disabler = DisablerTester.new\n  end\n\n  ## These tests are currently very implementation-specific, and they rely heavily on\n  ##  having access to the \"disable_lockfile\" method.  However, I've made this method private\n  ##  because it really shouldn't be exposed outside of our implementation... therefore\n  ##  these tests have to use a lot of \".send\" calls.  They should probably be cleaned up\n  ##  but for the moment I wanted to make sure not to lose any of the functionality of\n  ##  the tests. --cprice 2012-04-16\n\n  it \"should use an JsonLockfile instance as its disable_lockfile\" do\n    expect(@disabler.send(:disable_lockfile)).to be_instance_of(Puppet::Util::JsonLockfile)\n  end\n\n  it \"should use puppet's :agent_disabled_lockfile' setting to determine its lockfile path\" do\n    lockfile = File.expand_path(\"/my/lock.disabled\")\n    Puppet[:agent_disabled_lockfile] = lockfile\n    lock = Puppet::Util::JsonLockfile.new(lockfile)\n    expect(Puppet::Util::JsonLockfile).to receive(:new).with(lockfile).and_return(lock)\n\n    @disabler.send(:disable_lockfile)\n  end\n\n  it \"should reuse the same lock file each time\" do\n    expect(@disabler.send(:disable_lockfile)).to equal(@disabler.send(:disable_lockfile))\n  end\n\n  it \"should lock the file when disabled\" do\n    expect(@disabler.send(:disable_lockfile)).to receive(:lock)\n\n    @disabler.disable\n  end\n\n  it \"should unlock the file when enabled\" do\n    expect(@disabler.send(:disable_lockfile)).to receive(:unlock)\n\n    @disabler.enable\n  end\n\n  it \"should check the lock if it is disabled\" do\n    expect(@disabler.send(:disable_lockfile)).to receive(:locked?)\n\n    @disabler.disabled?\n  end\n\n  it \"should report the disable message when disabled\" do\n    Puppet[:agent_disabled_lockfile] = PuppetSpec::Files.tmpfile(\"lock\")\n\n    msg = \"I'm busy, go away\"\n    @disabler.disable(msg)\n    expect(@disabler.disable_message).to eq(msg)\n  end\nend\n"
  },
  {
    "path": "spec/unit/agent/locker_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/agent'\nrequire 'puppet/agent/locker'\n\nclass LockerTester\n  include Puppet::Agent::Locker\nend\n\ndescribe Puppet::Agent::Locker do\n  before do\n    @locker = LockerTester.new\n  end\n\n  ## These tests are currently very implementation-specific, and they rely heavily on\n  ##  having access to the lockfile object.  However, I've made this method private\n  ##  because it really shouldn't be exposed outside of our implementation... therefore\n  ##  these tests have to use a lot of \".send\" calls.  They should probably be cleaned up\n  ##  but for the moment I wanted to make sure not to lose any of the functionality of\n  ##  the tests.   --cprice 2012-04-16\n\n  it \"should use a Pidlock instance as its lockfile\" do\n    expect(@locker.send(:lockfile)).to be_instance_of(Puppet::Util::Pidlock)\n  end\n\n  it \"should use puppet's agent_catalog_run_lockfile' setting to determine its lockfile path\" do\n    lockfile = File.expand_path(\"/my/lock\")\n    Puppet[:agent_catalog_run_lockfile] = lockfile\n    lock = Puppet::Util::Pidlock.new(lockfile)\n    expect(Puppet::Util::Pidlock).to receive(:new).with(lockfile).and_return(lock)\n\n    @locker.send(:lockfile)\n  end\n\n  it \"#lockfile_path provides the path to the lockfile\" do\n    lockfile = File.expand_path(\"/my/lock\")\n    Puppet[:agent_catalog_run_lockfile] = lockfile\n    expect(@locker.lockfile_path).to eq(File.expand_path(\"/my/lock\"))\n  end\n\n  it \"should reuse the same lock file each time\" do\n    expect(@locker.send(:lockfile)).to equal(@locker.send(:lockfile))\n  end\n\n  it \"should have a method that yields when a lock is attained\" do\n    expect(@locker.send(:lockfile)).to receive(:lock).and_return(true)\n\n    yielded = false\n    @locker.lock do\n      yielded = true\n    end\n    expect(yielded).to be_truthy\n  end\n\n  it \"should return the block result when the lock method successfully locked\" do\n    expect(@locker.send(:lockfile)).to receive(:lock).and_return(true)\n\n    expect(@locker.lock { :result }).to eq(:result)\n  end\n\n  it \"should raise LockError when the lock method does not receive the lock\" do\n    expect(@locker.send(:lockfile)).to receive(:lock).and_return(false)\n\n    expect { @locker.lock {} }.to raise_error(Puppet::LockError)\n  end\n\n  it \"should not yield when the lock method does not receive the lock\" do\n    expect(@locker.send(:lockfile)).to receive(:lock).and_return(false)\n\n    yielded = false\n    expect { @locker.lock { yielded = true } }.to raise_error(Puppet::LockError)\n    expect(yielded).to be_falsey\n  end\n\n  it \"should not unlock when a lock was not received\" do\n    expect(@locker.send(:lockfile)).to receive(:lock).and_return(false)\n    expect(@locker.send(:lockfile)).not_to receive(:unlock)\n\n    expect { @locker.lock {} }.to raise_error(Puppet::LockError)\n  end\n\n  it \"should unlock after yielding upon obtaining a lock\" do\n    allow(@locker.send(:lockfile)).to receive(:lock).and_return(true)\n    expect(@locker.send(:lockfile)).to receive(:unlock)\n\n    @locker.lock {}\n  end\n\n  it \"should unlock after yielding upon obtaining a lock, even if the block throws an exception\" do\n    allow(@locker.send(:lockfile)).to receive(:lock).and_return(true)\n    expect(@locker.send(:lockfile)).to receive(:unlock)\n\n    expect { @locker.lock { raise \"foo\" } }.to raise_error(RuntimeError)\n  end\n\n  it \"should be considered running if the lockfile is locked\" do\n    expect(@locker.send(:lockfile)).to receive(:locked?).and_return(true)\n    expect(@locker).to be_running\n  end\nend\n"
  },
  {
    "path": "spec/unit/agent_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/agent'\nrequire 'puppet/configurer'\n\nclass AgentTestClient\n  def initialize(transaction_uuid = nil, job_id = nil)\n  end\n\n  def run(client_args)\n    # no-op\n  end\n\n  def stop\n    # no-op\n  end\nend\n\ndescribe Puppet::Agent do\n  before do\n    @agent = Puppet::Agent.new(AgentTestClient, false)\n\n    # make Puppet::Application safe for stubbing\n    stub_const('Puppet::Application', Class.new(Puppet::Application))\n    allow(Puppet::Application).to receive(:clear?).and_return(true)\n    Puppet::Application.class_eval do\n      class << self\n        def controlled_run(&block)\n          block.call\n        end\n      end\n    end\n\n    ssl_context = Puppet::SSL::SSLContext.new\n    machine = instance_double(\"Puppet::SSL::StateMachine\", ensure_client_certificate: ssl_context)\n    allow(Puppet::SSL::StateMachine).to receive(:new).and_return(machine)\n  end\n\n  it \"should set its client class at initialization\" do\n    expect(Puppet::Agent.new(\"foo\", false).client_class).to eq(\"foo\")\n  end\n\n  it \"should include the Locker module\" do\n    expect(Puppet::Agent.ancestors).to be_include(Puppet::Agent::Locker)\n  end\n\n  it \"should create an instance of its client class and run it when asked to run\" do\n    client = double('client')\n    allow(AgentTestClient).to receive(:new).with(nil, nil).and_return(client)\n\n    allow(@agent).to receive(:disabled?).and_return(false)\n    expect(client).to receive(:run)\n    @agent.run\n  end\n\n  it \"should initialize the client's transaction_uuid if passed as a client_option\" do\n    client = double('client')\n    transaction_uuid = 'foo'\n    expect(AgentTestClient).to receive(:new).with(transaction_uuid, nil).and_return(client)\n\n    expect(client).to receive(:run)\n\n    allow(@agent).to receive(:disabled?).and_return(false)\n    @agent.run(:transaction_uuid => transaction_uuid)\n  end\n\n  it \"should initialize the client's job_id if passed as a client_option\" do\n    client = double('client')\n    job_id = '289'\n    expect(AgentTestClient).to receive(:new).with(anything, job_id).and_return(client)\n\n    expect(client).to receive(:run)\n\n    allow(@agent).to receive(:disabled?).and_return(false)\n    @agent.run(:job_id => job_id)\n  end\n\n  it \"should be considered running if the lock file is locked\" do\n    lockfile = double('lockfile')\n\n    expect(@agent).to receive(:lockfile).and_return(lockfile)\n    expect(lockfile).to receive(:locked?).and_return(true)\n\n    expect(@agent).to be_running\n  end\n\n  describe \"when being run\" do\n    before do\n      allow(@agent).to receive(:disabled?).and_return(false)\n    end\n\n    it \"should splay\" do\n      Puppet[:splay] = true\n\n      expect(@agent).to receive(:splay)\n\n      @agent.run\n    end\n\n    it \"should do nothing if disabled\" do\n      expect(@agent).to receive(:disabled?).and_return(true)\n      expect(AgentTestClient).not_to receive(:new)\n      @agent.run\n    end\n\n    it \"(#11057) should notify the user about why a run is skipped\" do\n      allow(Puppet::Application).to receive(:controlled_run).and_return(false)\n      allow(Puppet::Application).to receive(:run_status).and_return('MOCK_RUN_STATUS')\n      # This is the actual test that we inform the user why the run is skipped.\n      # We assume this information is contained in\n      # Puppet::Application.run_status\n      expect(Puppet).to receive(:notice).with(/MOCK_RUN_STATUS/)\n      @agent.run\n    end\n\n    it \"should display an informative message if the agent is administratively disabled\" do\n      expect(@agent).to receive(:disabled?).and_return(true)\n      expect(@agent).to receive(:disable_message).and_return(\"foo\")\n      expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*\\(Reason: 'foo'\\)/)\n      @agent.run\n    end\n\n    it \"should use Puppet::Application.controlled_run to manage process state behavior\" do\n      expect(Puppet::Application).to receive(:controlled_run).ordered.and_yield\n      expect(AgentTestClient).to receive(:new).ordered.once\n      @agent.run\n    end\n\n    it \"should not fail if a client class instance cannot be created\" do\n      expect(AgentTestClient).to receive(:new).and_raise(\"eh\")\n      expect(Puppet).to receive(:log_exception)\n      @agent.run\n    end\n\n    it \"should not fail if there is an exception while running its client\" do\n      client = AgentTestClient.new\n      expect(AgentTestClient).to receive(:new).and_return(client)\n      expect(client).to receive(:run).and_raise(\"eh\")\n      expect(Puppet).to receive(:log_exception)\n      @agent.run\n    end\n\n    it \"should use a filesystem lock to restrict multiple processes running the agent\" do\n      client = AgentTestClient.new\n      expect(AgentTestClient).to receive(:new).and_return(client)\n\n      expect(@agent).to receive(:lock)\n\n      expect(client).not_to receive(:run) # if it doesn't run, then we know our yield is what triggers it\n      @agent.run\n    end\n\n    it \"should make its client instance available while running\" do\n      client = AgentTestClient.new\n      expect(AgentTestClient).to receive(:new).and_return(client)\n\n      expect(client).to receive(:run) { expect(@agent.client).to equal(client); nil }\n      @agent.run\n    end\n\n    it \"should run the client instance with any arguments passed to it\" do\n      client = AgentTestClient.new\n      expect(AgentTestClient).to receive(:new).and_return(client)\n\n      expect(client).to receive(:run).with({:pluginsync => true, :other => :options})\n      @agent.run(:other => :options)\n    end\n\n    it \"should return the agent result\" do\n      client = AgentTestClient.new\n      expect(AgentTestClient).to receive(:new).and_return(client)\n\n      expect(@agent).to receive(:lock).and_return(:result)\n      expect(@agent.run).to eq(:result)\n    end\n\n    it \"should check if it's disabled after splaying and log a message\" do\n      Puppet[:splay] = true\n      Puppet[:splaylimit] = '5s'\n      Puppet[:onetime] = true\n\n      expect(@agent).to receive(:disabled?).and_return(false, true)\n\n      allow(Puppet).to receive(:notice).and_call_original\n      expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)\n      @agent.run\n    end\n\n    it \"should check if it's disabled after acquiring the lock and log a message\" do\n      expect(@agent).to receive(:disabled?).and_return(false, true)\n\n      allow(Puppet).to receive(:notice).and_call_original\n      expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)\n      @agent.run\n    end\n\n    describe \"and a puppet agent is already running\" do\n      before(:each) do\n        allow_any_instance_of(Object).to receive(:sleep)\n        lockfile = double('lockfile')\n        expect(@agent).to receive(:lockfile).and_return(lockfile).at_least(:once)\n        # so the lock method raises Puppet::LockError\n        allow(lockfile).to receive(:lock).and_return(false)\n      end\n\n      it \"should notify that a run is already in progress\" do\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n        expect(Puppet).to receive(:notice).with(/Run of .* already in progress; skipping .* exists/)\n        @agent.run\n      end\n\n      it \"should inform that a run is already in progress and try to run every X seconds if waitforlock is used\" do\n        # so the locked file exists\n        allow(File).to receive(:file?).and_return(true)\n        # so we don't have to wait again for the run to exit (default maxwaitforcert is 60)\n        # first 0 is to get the time, second 0 is to inform user, then 1000 so the time expires\n        allow(Time).to receive(:now).and_return(0, 0, 1000)\n        allow(Puppet).to receive(:info)\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        Puppet[:waitforlock] = 1\n        Puppet[:maxwaitforlock] = 2\n        expect(Puppet).to receive(:info).with(/Another puppet instance is already running; --waitforlock flag used, waiting for running instance to finish./)\n        expect(Puppet).to receive(:info).with(/Will try again in #{Puppet[:waitforlock]} seconds./)\n        @agent.run\n      end\n\n      it \"should notify that the run is exiting if waitforlock is used and maxwaitforlock is exceeded\" do\n        # so we don't have to wait again for the run to exit (default maxwaitforcert is 60)\n        # first 0 is to get the time, then 1000 so that the time expires\n        allow(Time).to receive(:now).and_return(0, 1000)\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        Puppet[:waitforlock] = 1\n        expect(Puppet).to receive(:notice).with(/Exiting now because the maxwaitforlock timeout has been exceeded./)\n        @agent.run\n      end\n    end\n\n    describe \"when should_fork is true\", :if => Puppet.features.posix? && RUBY_PLATFORM != 'java' do\n      before do\n        @agent = Puppet::Agent.new(AgentTestClient, true)\n\n        # So we don't actually try to hit the filesystem.\n        allow(@agent).to receive(:lock).and_yield\n      end\n\n      it \"should run the agent in a forked process\" do\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        expect(client).to receive(:run).and_return(0)\n\n        expect(Kernel).to receive(:fork).and_yield\n        expect { @agent.run }.to exit_with(0)\n      end\n\n      it \"should exit child process if child exit\" do\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        expect(client).to receive(:run).and_raise(SystemExit.new(-1))\n\n        expect(Kernel).to receive(:fork).and_yield\n        expect { @agent.run }.to exit_with(-1)\n      end\n\n      it 'should exit with 1 if an exception is raised' do\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        expect(client).to receive(:run).and_raise(StandardError)\n\n        expect(Kernel).to receive(:fork).and_yield\n        expect { @agent.run }.to exit_with(1)\n      end\n\n      it 'should exit with 254 if NoMemoryError exception is raised' do\n        client = AgentTestClient.new\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        expect(client).to receive(:run).and_raise(NoMemoryError)\n\n        expect(Kernel).to receive(:fork).and_yield\n        expect { @agent.run }.to exit_with(254)\n      end\n\n      it \"should return the block exit code as the child exit code\" do\n        expect(Kernel).to receive(:fork).and_yield\n        expect {\n          @agent.run_in_fork {\n            777\n          }\n        }.to exit_with(777)\n      end\n\n      it \"should return `1` exit code if the block returns `nil`\" do\n        expect(Kernel).to receive(:fork).and_yield\n        expect {\n          @agent.run_in_fork {\n            nil\n          }\n        }.to exit_with(1)\n      end\n\n      it \"should return `1` exit code if the block returns `false`\" do\n        expect(Kernel).to receive(:fork).and_yield\n        expect {\n          @agent.run_in_fork {\n            false\n          }\n        }.to exit_with(1)\n      end\n    end\n\n    describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should never fork\" do\n        agent = Puppet::Agent.new(AgentTestClient, true)\n        expect(agent.should_fork).to be_falsey\n      end\n    end\n\n    describe 'when runtimeout is set' do\n      before(:each) do\n        Puppet[:runtimeout] = 1\n      end\n\n      it 'times out when a run exceeds the set limit' do\n        client = AgentTestClient.new\n        client.instance_eval do\n          # Stub methods used to set test expectations.\n          def processing; end\n          def handling; end\n\n          def run(client_options = {})\n            # Simulate a hanging agent operation that also traps errors.\n            begin\n              ::Kernel.sleep(5)\n              processing()\n            rescue\n              handling()\n            end\n          end\n        end\n\n        expect(AgentTestClient).to receive(:new).and_return(client)\n\n        expect(client).not_to receive(:processing)\n        expect(client).not_to receive(:handling)\n        expect(Puppet).to receive(:log_exception).with(be_an_instance_of(Puppet::Agent::RunTimeoutError), anything)\n\n        expect(@agent.run).to eq(nil)\n      end\n    end\n  end\n\n  describe \"when checking execution state\" do\n    describe 'with regular run status' do\n      before :each do\n        allow(Puppet::Application).to receive(:restart_requested?).and_return(false)\n        allow(Puppet::Application).to receive(:stop_requested?).and_return(false)\n        allow(Puppet::Application).to receive(:interrupted?).and_return(false)\n        allow(Puppet::Application).to receive(:clear?).and_return(true)\n      end\n\n      it 'should be false for :stopping?' do\n        expect(@agent.stopping?).to be_falsey\n      end\n\n      it 'should be false for :needing_restart?' do\n        expect(@agent.needing_restart?).to be_falsey\n      end\n    end\n\n    describe 'with a stop requested' do\n      before :each do\n        allow(Puppet::Application).to receive(:clear?).and_return(false)\n        allow(Puppet::Application).to receive(:restart_requested?).and_return(false)\n        allow(Puppet::Application).to receive(:stop_requested?).and_return(true)\n        allow(Puppet::Application).to receive(:interrupted?).and_return(true)\n      end\n\n      it 'should be true for :stopping?' do\n        expect(@agent.stopping?).to be_truthy\n      end\n\n      it 'should be false for :needing_restart?' do\n        expect(@agent.needing_restart?).to be_falsey\n      end\n    end\n\n    describe 'with a restart requested' do\n      before :each do\n        allow(Puppet::Application).to receive(:clear?).and_return(false)\n        allow(Puppet::Application).to receive(:restart_requested?).and_return(true)\n        allow(Puppet::Application).to receive(:stop_requested?).and_return(false)\n        allow(Puppet::Application).to receive(:interrupted?).and_return(true)\n      end\n\n      it 'should be false for :stopping?' do\n        expect(@agent.stopping?).to be_falsey\n      end\n\n      it 'should be true for :needing_restart?' do\n        expect(@agent.needing_restart?).to be_truthy\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/agent_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/agent'\nrequire 'puppet/application/agent'\nrequire 'puppet/daemon'\n\nclass TestAgentClientClass\n  def initialize(transaction_uuid = nil, job_id = nil); end\n  def run(options = {}); end\nend\n\ndescribe Puppet::Application::Agent do\n  include PuppetSpec::Files\n\n  let(:machine) { double(ensure_client_certificate: nil) }\n\n  before :each do\n    @puppetd = Puppet::Application[:agent]\n\n    @client = TestAgentClientClass.new\n    allow(TestAgentClientClass).to receive(:new).and_return(@client)\n\n    @agent = Puppet::Agent.new(TestAgentClientClass, false)\n    allow(Puppet::Agent).to receive(:new).and_return(@agent)\n\n    Puppet[:pidfile] = tmpfile('pidfile')\n    @daemon = Puppet::Daemon.new(@agent, Puppet::Util::Pidlock.new(Puppet[:pidfile]))\n    allow(@daemon).to receive(:daemonize)\n    allow(@daemon).to receive(:stop)\n    # simulate one run so we don't infinite looptwo runs of the agent, then return so we don't infinite loop\n    allow(@daemon).to receive(:run_event_loop) do\n      @agent.run(splay: false)\n    end\n    allow(Puppet::Daemon).to receive(:new).and_return(@daemon)\n    Puppet[:daemonize] = false\n\n    @puppetd.preinit\n    allow(Puppet::Util::Log).to receive(:newdestination)\n\n    allow(Puppet::Node.indirection).to receive(:terminus_class=)\n    allow(Puppet::Node.indirection).to receive(:cache_class=)\n    allow(Puppet::Node::Facts.indirection).to receive(:terminus_class=)\n\n    allow(Puppet.settings).to receive(:use)\n    allow(Puppet::SSL::StateMachine).to receive(:new).and_return(machine)\n  end\n\n  it \"should operate in agent run_mode\" do\n    expect(@puppetd.class.run_mode.name).to eq(:agent)\n  end\n\n  it \"should declare a main command\" do\n    expect(@puppetd).to respond_to(:main)\n  end\n\n  it \"should declare a onetime command\" do\n    expect(@puppetd).to respond_to(:onetime)\n  end\n\n  it \"should declare a fingerprint command\" do\n    expect(@puppetd).to respond_to(:fingerprint)\n  end\n\n  it \"should declare a preinit block\" do\n    expect(@puppetd).to respond_to(:preinit)\n  end\n\n  describe \"in preinit\" do\n    it \"should catch INT\" do\n      expect(Signal).to receive(:trap).with(:INT)\n\n      @puppetd.preinit\n    end\n\n    it \"should init fqdn to nil\" do\n      @puppetd.preinit\n\n      expect(@puppetd.options[:fqdn]).to be_nil\n    end\n\n    it \"should init serve to []\" do\n      @puppetd.preinit\n\n      expect(@puppetd.options[:serve]).to eq([])\n    end\n\n    it \"should use SHA256 as default digest algorithm\" do\n      @puppetd.preinit\n\n      expect(@puppetd.options[:digest]).to eq('SHA256')\n    end\n\n    it \"should not fingerprint by default\" do\n      @puppetd.preinit\n\n      expect(@puppetd.options[:fingerprint]).to be_falsey\n    end\n\n    it \"should init waitforcert to nil\" do\n      @puppetd.preinit\n\n      expect(@puppetd.options[:waitforcert]).to be_nil\n    end\n  end\n\n  describe \"when handling options\" do\n    [:enable, :debug, :fqdn, :test, :verbose, :digest].each do |option|\n      it \"should declare handle_#{option} method\" do\n        expect(@puppetd).to respond_to(\"handle_#{option}\".to_sym)\n      end\n\n      it \"should store argument value when calling handle_#{option}\" do\n        @puppetd.send(\"handle_#{option}\".to_sym, 'arg')\n\n        expect(@puppetd.options[option]).to eq('arg')\n      end\n    end\n\n    describe \"when handling --disable\" do\n      it \"should set disable to true\" do\n        @puppetd.handle_disable('')\n\n        expect(@puppetd.options[:disable]).to eq(true)\n      end\n\n      it \"should store disable message\" do\n        @puppetd.handle_disable('message')\n\n        expect(@puppetd.options[:disable_message]).to eq('message')\n      end\n    end\n\n    it \"should log the agent start time\" do\n      expect(@puppetd.options[:start_time]).to be_a(Time)\n    end\n\n    it \"should set waitforcert to 0 with --onetime and if --waitforcert wasn't given\" do\n      allow(@client).to receive(:run).and_return(2)\n      Puppet[:onetime] = true\n\n      expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 0)).and_return(machine)\n\n      expect { execute_agent }.to exit_with 0\n    end\n\n    it \"should use supplied waitforcert when --onetime is specified\" do\n      allow(@client).to receive(:run).and_return(2)\n      Puppet[:onetime] = true\n      @puppetd.handle_waitforcert(60)\n\n      expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 60)).and_return(machine)\n\n      expect { execute_agent }.to exit_with 0\n    end\n\n    it \"should use a default value for waitforcert when --onetime and --waitforcert are not specified\" do\n      allow(@client).to receive(:run).and_return(2)\n\n      expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 120)).and_return(machine)\n\n      execute_agent\n    end\n\n    it \"should register ssl OIDs\" do\n      expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 120)).and_return(machine)\n      expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)\n\n      execute_agent\n    end\n\n    it \"should use the waitforcert setting when checking for a signed certificate\" do\n      Puppet[:waitforcert] = 10\n\n      expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 10)).and_return(machine)\n\n      execute_agent\n    end\n\n    it \"should set the log destination with --logdest\" do\n      expect(Puppet::Log).to receive(:newdestination).with(\"console\")\n\n      @puppetd.handle_logdest(\"console\")\n    end\n\n    it \"should put the setdest options to true\" do\n      @puppetd.handle_logdest(\"console\")\n\n      expect(@puppetd.options[:setdest]).to eq(true)\n    end\n\n    it \"should parse the log destination from the command line\" do\n      allow(@puppetd.command_line).to receive(:args).and_return(%w{--logdest /my/file})\n\n      expect(Puppet::Util::Log).to receive(:newdestination).with(\"/my/file\")\n\n      @puppetd.parse_options\n    end\n\n    it \"should store the waitforcert options with --waitforcert\" do\n      @puppetd.handle_waitforcert(\"42\")\n\n      expect(@puppetd.options[:waitforcert]).to eq(42)\n    end\n  end\n\n  describe \"during setup\" do\n    before :each do\n      allow(Puppet).to receive(:info)\n      Puppet[:libdir] = \"/dev/null/lib\"\n      allow(Puppet::Transaction::Report.indirection).to receive(:terminus_class=)\n      allow(Puppet::Transaction::Report.indirection).to receive(:cache_class=)\n      allow(Puppet::Resource::Catalog.indirection).to receive(:terminus_class=)\n      allow(Puppet::Resource::Catalog.indirection).to receive(:cache_class=)\n      allow(Puppet::Node::Facts.indirection).to receive(:terminus_class=)\n    end\n\n    it \"should not run with extra arguments\" do\n      allow(@puppetd.command_line).to receive(:args).and_return(%w{disable})\n      expect{@puppetd.setup}.to raise_error ArgumentError, /does not take parameters/\n    end\n\n    describe \"with --test\" do\n      it \"should call setup_test\" do\n        @puppetd.options[:test] = true\n        expect(@puppetd).to receive(:setup_test)\n\n        @puppetd.setup\n      end\n\n      it \"should set options[:verbose] to true\" do\n        @puppetd.setup_test\n\n        expect(@puppetd.options[:verbose]).to eq(true)\n      end\n      it \"should set options[:onetime] to true\" do\n        Puppet[:onetime] = false\n        @puppetd.setup_test\n        expect(Puppet[:onetime]).to eq(true)\n      end\n      it \"should set options[:detailed_exitcodes] to true\" do\n        @puppetd.setup_test\n\n        expect(@puppetd.options[:detailed_exitcodes]).to eq(true)\n      end\n    end\n\n    it \"should call setup_logs\" do\n      expect(@puppetd).to receive(:setup_logs)\n      @puppetd.setup\n    end\n\n    describe \"when setting up logs\" do\n      before :each do\n        allow(Puppet::Util::Log).to receive(:newdestination)\n      end\n\n      it \"should set log level to debug if --debug was passed\" do\n        @puppetd.options[:debug] = true\n        @puppetd.setup_logs\n        expect(Puppet::Util::Log.level).to eq(:debug)\n      end\n\n      it \"should set log level to info if --verbose was passed\" do\n        @puppetd.options[:verbose] = true\n        @puppetd.setup_logs\n        expect(Puppet::Util::Log.level).to eq(:info)\n      end\n\n      [:verbose, :debug].each do |level|\n        it \"should set console as the log destination with level #{level}\" do\n          @puppetd.options[level] = true\n\n          allow(Puppet::Util::Log).to receive(:newdestination)\n          expect(Puppet::Util::Log).to receive(:newdestination).with(:console).exactly(:once)\n\n          @puppetd.setup_logs\n        end\n      end\n\n      it \"should set a default log destination if no --logdest\" do\n        @puppetd.options[:setdest] = false\n\n        expect(Puppet::Util::Log).to receive(:setup_default)\n\n        @puppetd.setup_logs\n      end\n    end\n\n    it \"should print puppet config if asked to in Puppet config\" do\n      Puppet[:configprint] = \"plugindest\"\n      expect(Puppet.settings).to receive(:print_configs).and_return(true)\n      expect { execute_agent }.to exit_with 0\n    end\n\n    it \"should exit after printing puppet config if asked to in Puppet config\" do\n      path = make_absolute('/my/path')\n      Puppet[:modulepath] = path\n      Puppet[:configprint] = \"modulepath\"\n      expect_any_instance_of(Puppet::Settings).to receive(:puts).with(path)\n      expect { execute_agent }.to exit_with 0\n    end\n\n    it \"should use :main, :puppetd, and :ssl\" do\n      expect(Puppet.settings).to receive(:use).with(:main, :agent, :ssl)\n\n      @puppetd.setup\n    end\n\n    it \"should setup an agent in fingerprint mode\" do\n      @puppetd.options[:fingerprint] = true\n      expect(@puppetd).not_to receive(:setup_agent)\n\n      @puppetd.setup\n    end\n\n    it \"should tell the report handler to use REST\" do\n      expect(Puppet::Transaction::Report.indirection).to receive(:terminus_class=).with(:rest)\n\n      @puppetd.setup\n    end\n\n    it \"should tell the report handler to cache locally as yaml\" do\n      expect(Puppet::Transaction::Report.indirection).to receive(:cache_class=).with(:yaml)\n\n      @puppetd.setup\n    end\n\n    it \"should default catalog_terminus setting to 'rest'\" do\n      @puppetd.initialize_app_defaults\n      expect(Puppet[:catalog_terminus]).to eq(:rest)\n    end\n\n    it \"should default node_terminus setting to 'rest'\" do\n      @puppetd.initialize_app_defaults\n      expect(Puppet[:node_terminus]).to eq(:rest)\n    end\n\n    it \"has an application default :catalog_cache_terminus setting of 'json'\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:cache_class=).with(:json)\n\n      @puppetd.initialize_app_defaults\n      @puppetd.setup\n    end\n\n    it \"should tell the catalog cache class based on the :catalog_cache_terminus setting\" do\n      Puppet[:catalog_cache_terminus] = \"yaml\"\n      expect(Puppet::Resource::Catalog.indirection).to receive(:cache_class=).with(:yaml)\n\n      @puppetd.initialize_app_defaults\n      @puppetd.setup\n    end\n\n    it \"should not set catalog cache class if :catalog_cache_terminus is explicitly nil\" do\n      Puppet[:catalog_cache_terminus] = nil\n      expect(Puppet::Resource::Catalog.indirection).not_to receive(:cache_class=)\n\n      @puppetd.initialize_app_defaults\n      @puppetd.setup\n    end\n\n    it \"should default facts_terminus setting to 'facter'\" do\n      @puppetd.initialize_app_defaults\n      expect(Puppet[:facts_terminus]).to eq(:facter)\n    end\n\n    it \"should create an agent\" do\n      allow(Puppet::Agent).to receive(:new).with(Puppet::Configurer)\n\n      @puppetd.setup\n    end\n\n    [:enable, :disable].each do |action|\n      it \"should delegate to enable_disable_client if we #{action} the agent\" do\n        @puppetd.options[action] = true\n        expect(@puppetd).to receive(:enable_disable_client).with(@agent)\n\n        @puppetd.setup\n      end\n    end\n\n    describe \"when enabling or disabling agent\" do\n      [:enable, :disable].each do |action|\n        it \"should call client.#{action}\" do\n          @puppetd.options[action] = true\n          expect(@agent).to receive(action)\n          expect { execute_agent }.to exit_with 0\n        end\n      end\n\n      it \"should pass the disable message when disabling\" do\n        @puppetd.options[:disable] = true\n        @puppetd.options[:disable_message] = \"message\"\n        expect(@agent).to receive(:disable).with(\"message\")\n\n        expect { execute_agent }.to exit_with 0\n      end\n\n      it \"should pass the default disable message when disabling without a message\" do\n        @puppetd.options[:disable] = true\n        @puppetd.options[:disable_message] = nil\n        expect(@agent).to receive(:disable).with(\"reason not specified\")\n\n        expect { execute_agent }.to exit_with 0\n      end\n    end\n\n    it \"should inform the daemon about our agent if :client is set to 'true'\" do\n      @puppetd.options[:client] = true\n\n      execute_agent\n\n      expect(@daemon.agent).to eq(@agent)\n    end\n\n    it \"should daemonize if needed\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      Puppet[:daemonize] = true\n      allow(Signal).to receive(:trap)\n\n      expect(@daemon).to receive(:daemonize)\n\n      execute_agent\n    end\n\n    it \"should wait for a certificate\" do\n      Puppet[:waitforcert] = 123\n\n      expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 123)).and_return(machine)\n\n      execute_agent\n    end\n\n    describe \"when setting up for fingerprint\" do\n      before(:each) do\n        @puppetd.options[:fingerprint] = true\n      end\n\n      it \"should not setup as an agent\" do\n        expect(@puppetd).not_to receive(:setup_agent)\n        @puppetd.setup\n      end\n\n      it \"should not create an agent\" do\n        expect(Puppet::Agent).not_to receive(:new).with(Puppet::Configurer)\n        @puppetd.setup\n      end\n\n      it \"should not daemonize\" do\n        expect(@daemon).not_to receive(:daemonize)\n        @puppetd.setup\n      end\n    end\n\n    describe \"when configuring agent for catalog run\" do\n      it \"should set should_fork as true when running normally\" do\n        expect(Puppet::Agent).to receive(:new).with(anything, true)\n        @puppetd.setup\n      end\n\n      it \"should not set should_fork as false for --onetime\" do\n        Puppet[:onetime] = true\n        expect(Puppet::Agent).to receive(:new).with(anything, false)\n        @puppetd.setup\n      end\n    end\n  end\n\n  describe \"when running\" do\n    before :each do\n      @puppetd.options[:fingerprint] = false\n    end\n\n    it \"should dispatch to fingerprint if --fingerprint is used\" do\n      @puppetd.options[:fingerprint] = true\n\n      expect(@puppetd).to receive(:fingerprint)\n\n      execute_agent\n    end\n\n    it \"should dispatch to onetime if --onetime is used\" do\n      Puppet[:onetime] = true\n\n      expect(@puppetd).to receive(:onetime)\n\n      execute_agent\n    end\n\n    it \"should dispatch to main if --onetime and --fingerprint are not used\" do\n      Puppet[:onetime] = false\n\n      expect(@puppetd).to receive(:main)\n\n      execute_agent\n    end\n\n    describe \"with --onetime\" do\n      before :each do\n        allow(@agent).to receive(:run).and_return(:report)\n        Puppet[:onetime] = true\n        @puppetd.options[:client] = :client\n        @puppetd.options[:detailed_exitcodes] = false\n\n\n      end\n\n      it \"should setup traps\" do\n        expect(@daemon).to receive(:set_signal_traps)\n\n        expect { execute_agent }.to exit_with 0\n      end\n\n      it \"should let the agent run\" do\n        expect(@agent).to receive(:run).and_return(:report)\n\n        expect { execute_agent }.to exit_with 0\n      end\n\n      it \"should run the agent with the supplied job_id\" do\n        @puppetd.options[:job_id] = 'special id'\n        expect(@agent).to receive(:run).with(hash_including(:job_id => 'special id')).and_return(:report)\n\n        expect { execute_agent }.to exit_with 0\n      end\n\n      it \"should stop the daemon\" do\n        expect(@daemon).to receive(:stop).with({:exit => false})\n\n        expect { execute_agent }.to exit_with 0\n      end\n\n      describe \"and --detailed-exitcodes\" do\n        before :each do\n          @puppetd.options[:detailed_exitcodes] = true\n        end\n\n        it \"should exit with agent computed exit status\" do\n          Puppet[:noop] = false\n          allow(@agent).to receive(:run).and_return(666)\n\n          expect { execute_agent }.to exit_with 666\n        end\n\n        it \"should exit with the agent's exit status, even if --noop is set.\" do\n          Puppet[:noop] = true\n          allow(@agent).to receive(:run).and_return(666)\n\n          expect { execute_agent }.to exit_with 666\n        end\n      end\n    end\n\n    describe \"with --fingerprint\" do\n      before :each do\n        @puppetd.options[:fingerprint] = true\n        @puppetd.options[:digest] = :MD5\n      end\n\n      def expected_fingerprint(name, x509)\n        digest = OpenSSL::Digest.new(name).hexdigest(x509.to_der)\n        digest.scan(/../).join(':').upcase\n      end\n\n      it \"should fingerprint the certificate if it exists\" do\n        cert = cert_fixture('signed.pem')\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_client_cert).and_return(cert)\n\n        expect(@puppetd).to receive(:puts).with(\"(MD5) #{expected_fingerprint('md5', cert)}\")\n\n        @puppetd.fingerprint\n      end\n\n      it \"should fingerprint the request if it exists\" do\n        request = request_fixture('request.pem')\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_client_cert).and_return(nil)\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_request).and_return(request)\n\n        expect(@puppetd).to receive(:puts).with(\"(MD5) #{expected_fingerprint('md5', request)}\")\n\n        @puppetd.fingerprint\n      end\n\n      it \"should print an error to stderr if neither exist\" do\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_client_cert).and_return(nil)\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_request).and_return(nil)\n\n        expect {\n          @puppetd.fingerprint\n        }.to exit_with(1)\n         .and output(/Fingerprint asked but neither the certificate, nor the certificate request have been issued/).to_stderr\n      end\n\n      it \"should log an error if an exception occurs\" do\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_client_cert).and_raise(Puppet::Error, \"Invalid PEM\")\n\n        expect {\n          @puppetd.fingerprint\n        }.to exit_with(1)\n\n        expect(@logs).to include(an_object_having_attributes(message: /Failed to generate fingerprint: Invalid PEM/))\n      end\n    end\n\n    describe \"without --onetime and --fingerprint\" do\n      before :each do\n        allow(Puppet).to receive(:notice)\n      end\n\n      it \"should start our daemon\" do\n        expect(@daemon).to receive(:start)\n\n        execute_agent\n      end\n    end\n  end\n\n  describe \"when starting in daemon mode on non-windows\", :unless => Puppet.features.microsoft_windows? do\n    before :each do\n      allow(Puppet).to receive(:notice)\n      Puppet[:daemonize] = true\n      allow(Puppet::SSL::StateMachine).to receive(:new).and_return(machine)\n    end\n\n    it \"should not print config in default mode\" do\n      execute_agent\n      expect(@logs).to be_empty\n    end\n\n    it \"should print config in debug mode\" do\n      @puppetd.options[:debug] = true\n      execute_agent\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: /agent_catalog_run_lockfile=/))\n    end\n  end\n\n  def execute_agent\n    @puppetd.setup\n    @puppetd.run_command\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/apply_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/application/apply'\nrequire 'puppet/file_bucket/dipper'\nrequire 'puppet/configurer'\nrequire 'fileutils'\n\ndescribe Puppet::Application::Apply do\n  include PuppetSpec::Files\n\n  before :each do\n    @apply = Puppet::Application[:apply]\n    Puppet[:reports] = \"none\"\n  end\n\n  [:debug,:loadclasses,:test,:verbose,:use_nodes,:detailed_exitcodes,:catalog].each do |option|\n    it \"should store argument value when calling handle_#{option}\" do\n      expect(@apply.options).to receive(:[]=).with(option, 'arg')\n      @apply.send(\"handle_#{option}\".to_sym, 'arg')\n    end\n  end\n\n  it \"should handle write_catalog_summary\" do\n    @apply.send(:handle_write_catalog_summary, true)\n\n    expect(Puppet[:write_catalog_summary]).to eq(true)\n  end\n\n  it \"should set the code to the provided code when :execute is used\" do\n    expect(@apply.options).to receive(:[]=).with(:code, 'arg')\n    @apply.send(\"handle_execute\".to_sym, 'arg')\n  end\n\n  describe \"when applying options\" do\n    it \"should set the log destination with --logdest\" do\n      expect(Puppet::Log).to receive(:newdestination).with(\"console\")\n\n      @apply.handle_logdest(\"console\")\n    end\n\n    it \"should set the setdest options to true\" do\n      expect(@apply.options).to receive(:[]=).with(:setdest,true)\n\n      @apply.handle_logdest(\"console\")\n    end\n  end\n\n  describe \"during setup\" do\n    before :each do\n      allow(Puppet::Log).to receive(:newdestination)\n      allow(Puppet::FileBucket::Dipper).to receive(:new)\n      allow(STDIN).to receive(:read)\n      allow(Puppet::Transaction::Report.indirection).to receive(:cache_class=)\n    end\n\n    describe \"with --test\" do\n      it \"should set options[:verbose] to true\" do\n        @apply.setup_test\n\n        expect(@apply.options[:verbose]).to eq(true)\n      end\n\n      it \"should set options[:show_diff] to true\" do\n        Puppet.settings.override_default(:show_diff, false)\n        @apply.setup_test\n        expect(Puppet[:show_diff]).to eq(true)\n      end\n\n      it \"should set options[:detailed_exitcodes] to true\" do\n        @apply.setup_test\n\n        expect(@apply.options[:detailed_exitcodes]).to eq(true)\n      end\n    end\n\n    it \"should set console as the log destination if logdest option wasn't provided\" do\n      expect(Puppet::Log).to receive(:newdestination).with(:console)\n\n      @apply.setup\n    end\n\n    it \"sets the log destination if logdest is provided via settings\" do\n      expect(Puppet::Log).to receive(:newdestination).with(\"set_via_config\")\n      Puppet[:logdest] = \"set_via_config\"\n\n      @apply.setup\n    end\n\n    it \"should set INT trap\" do\n      expect(Signal).to receive(:trap).with(:INT)\n\n      @apply.setup\n    end\n\n    it \"should set log level to debug if --debug was passed\" do\n      @apply.options[:debug] = true\n      @apply.setup\n      expect(Puppet::Log.level).to eq(:debug)\n    end\n\n    it \"should set log level to info if --verbose was passed\" do\n      @apply.options[:verbose] = true\n      @apply.setup\n      expect(Puppet::Log.level).to eq(:info)\n    end\n\n    it \"should print puppet config if asked to in Puppet config\" do\n      allow(Puppet.settings).to receive(:print_configs?).and_return(true)\n      expect(Puppet.settings).to receive(:print_configs).and_return(true)\n      expect { @apply.setup }.to exit_with 0\n    end\n\n    it \"should exit after printing puppet config if asked to in Puppet config\" do\n      allow(Puppet.settings).to receive(:print_configs?).and_return(true)\n      expect { @apply.setup }.to exit_with 1\n    end\n\n    it \"should use :main, :puppetd, and :ssl\" do\n      expect(Puppet.settings).to receive(:use).with(:main, :agent, :ssl)\n\n      @apply.setup\n    end\n\n    it \"should tell the report handler to cache locally as yaml\" do\n      expect(Puppet::Transaction::Report.indirection).to receive(:cache_class=).with(:yaml)\n\n      @apply.setup\n    end\n\n    it \"configures a profiler when profiling is enabled\" do\n      Puppet[:profile] = true\n\n      @apply.setup\n\n      expect(Puppet::Util::Profiler.current).to satisfy do |ps|\n        ps.any? {|p| p.is_a? Puppet::Util::Profiler::WallClock }\n      end\n    end\n\n    it \"does not have a profiler if profiling is disabled\" do\n      Puppet[:profile] = false\n\n      @apply.setup\n\n      expect(Puppet::Util::Profiler.current.length).to be 0\n    end\n\n    it \"should set default_file_terminus to `file_server` to be local\" do\n      expect(@apply.app_defaults[:default_file_terminus]).to eq(:file_server)\n    end\n  end\n\n  describe \"when executing\" do\n    it \"should dispatch to 'apply' if it was called with a catalog\" do\n      @apply.options[:catalog] = \"foo\"\n\n      expect(@apply).to receive(:apply)\n      @apply.run_command\n    end\n\n    it \"should dispatch to main otherwise\" do\n      allow(@apply).to receive(:options).and_return({})\n\n      expect(@apply).to receive(:main)\n      @apply.run_command\n    end\n\n    describe \"the main command\" do\n      before :each do\n        Puppet[:prerun_command] = ''\n        Puppet[:postrun_command] = ''\n\n        Puppet::Node.indirection.terminus_class = :memory\n        Puppet::Node.indirection.cache_class = :memory\n\n        facts = Puppet::Node::Facts.new(Puppet[:node_name_value])\n        Puppet::Node::Facts.indirection.save(facts)\n\n        @node = Puppet::Node.new(Puppet[:node_name_value])\n        Puppet::Node.indirection.save(@node)\n\n        @catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet.lookup(:environments).get(Puppet[:environment]))\n        allow(@catalog).to receive(:to_ral).and_return(@catalog)\n\n        allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(@catalog)\n\n        allow(STDIN).to receive(:read)\n\n        @transaction = double('transaction')\n        allow(@catalog).to receive(:apply).and_return(@transaction)\n\n        allow(Puppet::Util::Storage).to receive(:load)\n        allow_any_instance_of(Puppet::Configurer).to receive(:save_last_run_summary) # to prevent it from trying to write files\n      end\n\n      after :each do\n        Puppet::Node::Facts.indirection.reset_terminus_class\n        Puppet::Node::Facts.indirection.cache_class = nil\n      end\n\n      around :each do |example|\n        Puppet.override(:current_environment =>\n                        Puppet::Node::Environment.create(:production, [])) do\n          example.run\n        end\n      end\n\n      it \"should set the code to run from --code\" do\n        @apply.options[:code] = \"code to run\"\n        expect(Puppet).to receive(:[]=).with(:code,\"code to run\")\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should set the code to run from STDIN if no arguments\" do\n        @apply.command_line.args = []\n        allow(STDIN).to receive(:read).and_return(\"code to run\")\n\n        expect(Puppet).to receive(:[]=).with(:code,\"code to run\")\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should raise an error if a file is passed on command line and the file does not exist\" do\n        noexist = tmpfile('noexist.pp')\n        @apply.command_line.args << noexist\n        expect {\n          @apply.run\n        }.to exit_with(1)\n         .and output(anything).to_stdout\n         .and output(/Could not find file #{noexist}/).to_stderr\n      end\n\n      it \"should set the manifest to the first file and warn other files will be skipped\" do\n        manifest = tmpfile('starwarsIV')\n        FileUtils.touch(manifest)\n\n        @apply.command_line.args << manifest << 'starwarsI' << 'starwarsII'\n        expect {\n          @apply.run\n        }.to exit_with(0)\n         .and output(anything).to_stdout\n         .and output(/Warning: Only one file can be applied per run.  Skipping starwarsI, starwarsII/).to_stderr\n      end\n\n      it \"should splay\" do\n        expect(@apply).to receive(:splay)\n\n        expect {\n          @apply.run\n        }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should exit with 1 if we can't find the node\" do\n        expect(Puppet::Node.indirection).to receive(:find).and_return(nil)\n\n        expect { @apply.run }.to exit_with(1).and output(/Could not find node/).to_stderr\n      end\n\n      it \"should load custom classes if loadclasses\" do\n        @apply.options[:loadclasses] = true\n        classfile = tmpfile('classfile')\n        File.open(classfile, 'w') { |c| c.puts 'class' }\n        Puppet[:classfile] = classfile\n\n        expect(@node).to receive(:classes=).with(['class'])\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should compile the catalog\" do\n        expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(@catalog)\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it 'should called the DeferredResolver to resolve any Deferred values' do\n        expect(Puppet::Pops::Evaluator::DeferredResolver).to receive(:resolve_and_replace).with(any_args)\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it 'should make the Puppet::Pops::Loaders available when applying the compiled catalog' do\n        expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(@catalog)\n        expect(@apply).to receive(:apply_catalog) do |catalog|\n          expect(@catalog).to eq(@catalog)\n          fail('Loaders not found') unless Puppet.lookup(:loaders) { nil }.is_a?(Puppet::Pops::Loaders)\n          true\n        end.and_return(0)\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should transform the catalog to ral\" do\n        expect(@catalog).to receive(:to_ral).and_return(@catalog)\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should finalize the catalog\" do\n        expect(@catalog).to receive(:finalize)\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should not save the classes or resource file by default\" do\n        expect(@catalog).not_to receive(:write_class_file)\n        expect(@catalog).not_to receive(:write_resource_file)\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should save the classes and resources files when requested on the command line using dashes\" do\n        expect(@catalog).to receive(:write_class_file).once\n        expect(@catalog).to receive(:write_resource_file).once\n\n        # dashes are parsed by the application's OptionParser\n        @apply.command_line.args = ['--write-catalog-summary']\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should save the classes and resources files when requested on the command line using underscores\" do\n        expect(@catalog).to receive(:write_class_file).once\n        expect(@catalog).to receive(:write_resource_file).once\n\n        # underscores are parsed by the settings PuppetOptionParser\n        @apply.command_line.args = ['--write_catalog_summary']\n        Puppet.initialize_settings(['--write_catalog_summary'])\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should save the classes and resources files when specified as a setting\" do\n        Puppet[:write_catalog_summary] = true\n\n        expect(@catalog).to receive(:write_class_file).once\n        expect(@catalog).to receive(:write_resource_file).once\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should call the prerun and postrun commands on a Configurer instance\" do\n        expect_any_instance_of(Puppet::Configurer).to receive(:execute_prerun_command).and_return(true)\n        expect_any_instance_of(Puppet::Configurer).to receive(:execute_postrun_command).and_return(true)\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should apply the catalog\" do\n        expect(@catalog).to receive(:apply).and_return(double('transaction'))\n\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      it \"should save the last run summary\" do\n        Puppet[:noop] = false\n        report = Puppet::Transaction::Report.new\n        allow(Puppet::Transaction::Report).to receive(:new).and_return(report)\n\n        expect_any_instance_of(Puppet::Configurer).to receive(:save_last_run_summary).with(report)\n        expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n      end\n\n      describe \"when using node_name_fact\" do\n        before :each do\n          @facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')\n          Puppet::Node::Facts.indirection.save(@facts)\n          @node = Puppet::Node.new('other_node_name')\n          Puppet::Node.indirection.save(@node)\n          Puppet[:node_name_fact] = 'my_name_fact'\n        end\n\n        it \"should set the facts name based on the node_name_fact\" do\n          expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n          expect(@facts.name).to eq('other_node_name')\n        end\n\n        it \"should set the node_name_value based on the node_name_fact\" do\n          expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n          expect(Puppet[:node_name_value]).to eq('other_node_name')\n        end\n\n        it \"should merge in our node the loaded facts\" do\n          @facts.values.merge!('key' => 'value')\n\n          expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n\n          expect(@node.parameters['key']).to eq('value')\n        end\n\n        it \"should exit if we can't find the facts\" do\n          expect(Puppet::Node::Facts.indirection).to receive(:find).and_return(nil)\n\n          expect { @apply.run }.to exit_with(1).and output(/Could not find facts/).to_stderr\n        end\n      end\n\n      describe \"with detailed_exitcodes\" do\n        before :each do\n          @apply.options[:detailed_exitcodes] = true\n        end\n\n        it \"should exit with report's computed exit status\" do\n          Puppet[:noop] = false\n          allow_any_instance_of(Puppet::Transaction::Report).to receive(:exit_status).and_return(666)\n\n          expect { @apply.run }.to exit_with(666).and output(anything).to_stdout\n        end\n\n        it \"should exit with report's computed exit status, even if --noop is set\" do\n          Puppet[:noop] = true\n          allow_any_instance_of(Puppet::Transaction::Report).to receive(:exit_status).and_return(666)\n\n          expect { @apply.run }.to exit_with(666).and output(anything).to_stdout\n        end\n\n        it \"should always exit with 0 if option is disabled\" do\n          Puppet[:noop] = false\n          report = double('report', :exit_status => 666)\n          allow(@transaction).to receive(:report).and_return(report)\n\n          expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n        end\n\n        it \"should always exit with 0 if --noop\" do\n          Puppet[:noop] = true\n          report = double('report', :exit_status => 666)\n          allow(@transaction).to receive(:report).and_return(report)\n\n          expect { @apply.run }.to exit_with(0).and output(anything).to_stdout\n        end\n      end\n    end\n\n    describe \"the 'apply' command\" do\n      # We want this memoized, and to be able to adjust the content, so we\n      # have to do it ourselves.\n      def temporary_catalog(content = '\"something\"')\n        @tempfile = Tempfile.new('catalog.json')\n        @tempfile.write(content)\n        @tempfile.close\n        @tempfile.path\n      end\n\n      let(:default_format) { Puppet::Resource::Catalog.default_format }\n      it \"should read the catalog in from disk if a file name is provided\" do\n        @apply.options[:catalog] = temporary_catalog\n        catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n        allow(Puppet::Resource::Catalog).to receive(:convert_from).with(default_format, '\"something\"').and_return(catalog)\n        @apply.apply\n      end\n\n      it \"should read the catalog in from stdin if '-' is provided\" do\n        @apply.options[:catalog] = \"-\"\n        expect($stdin).to receive(:read).and_return('\"something\"')\n        catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n        allow(Puppet::Resource::Catalog).to receive(:convert_from).with(default_format, '\"something\"').and_return(catalog)\n        @apply.apply\n      end\n\n      it \"should deserialize the catalog from the default format\" do\n        @apply.options[:catalog] = temporary_catalog\n        allow(Puppet::Resource::Catalog).to receive(:default_format).and_return(:rot13_piglatin)\n        catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n        allow(Puppet::Resource::Catalog).to receive(:convert_from).with(:rot13_piglatin,'\"something\"').and_return(catalog)\n        @apply.apply\n      end\n\n      it \"should fail helpfully if deserializing fails\" do\n        @apply.options[:catalog] = temporary_catalog('something syntactically invalid')\n        expect { @apply.apply }.to raise_error(Puppet::Error)\n      end\n\n      it \"should convert the catalog to a RAL catalog and use a Configurer instance to apply it\" do\n        @apply.options[:catalog] = temporary_catalog\n        catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n        allow(Puppet::Resource::Catalog).to receive(:convert_from).with(default_format, '\"something\"').and_return(catalog)\n        expect(catalog).to receive(:to_ral).and_return(\"mycatalog\")\n\n        configurer = double('configurer')\n        expect(Puppet::Configurer).to receive(:new).and_return(configurer)\n        expect(configurer).to receive(:run).\n          with(:catalog => \"mycatalog\", :pluginsync => false)\n\n        @apply.apply\n      end\n\n      it 'should make the Puppet::Pops::Loaders available when applying a catalog' do\n        @apply.options[:catalog] = temporary_catalog\n        catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n        expect(@apply).to receive(:read_catalog) do |arg|\n          expect(arg).to eq('\"something\"')\n          fail('Loaders not found') unless Puppet.lookup(:loaders) { nil }.is_a?(Puppet::Pops::Loaders)\n          true\n        end.and_return(catalog)\n        expect(@apply).to receive(:apply_catalog) do |cat|\n          expect(cat).to eq(catalog)\n          fail('Loaders not found') unless Puppet.lookup(:loaders) { nil }.is_a?(Puppet::Pops::Loaders)\n          true\n        end\n        expect { @apply.apply }.not_to raise_error\n      end\n\n      it \"should call the DeferredResolver to resolve Deferred values\" do\n        @apply.options[:catalog] = temporary_catalog\n        allow(Puppet::Resource::Catalog).to receive(:default_format).and_return(:rot13_piglatin)\n        catalog = Puppet::Resource::Catalog.new(\"testing\", Puppet::Node::Environment::NONE)\n        allow(Puppet::Resource::Catalog).to receive(:convert_from).with(:rot13_piglatin, '\"something\"').and_return(catalog)\n        expect(Puppet::Pops::Evaluator::DeferredResolver).to receive(:resolve_and_replace).with(any_args)\n        @apply.apply\n      end\n    end\n  end\n\n  describe \"when really executing\" do\n    let(:testfile) { tmpfile('secret_file_name') }\n    let(:resourcefile) { tmpfile('resourcefile') }\n    let(:classfile) { tmpfile('classfile') }\n\n    it \"should not expose sensitive data in the relationship file\" do\n      @apply.options[:code] = <<-CODE\n        $secret = Sensitive('cat #{testfile}')\n\n        exec { 'do it':\n          command => $secret,\n          path    => '/bin/'\n        }\n      CODE\n\n      Puppet.settings[:write_catalog_summary] = true\n      Puppet.settings[:resourcefile] = resourcefile\n      Puppet.settings[:classfile] = classfile\n\n      #We don't actually need the resource to do anything, we are using it's properties in other parts of the workflow.\n      allow_any_instance_of(Puppet::Type.type(:exec).defaultprovider).to receive(:which).and_return('cat')\n      allow(Puppet::Util::Execution).to receive(:execute).and_return(double(exitstatus: 0, output: ''))\n\n      expect { @apply.run }.to exit_with(0).and output(%r{Exec\\[do it\\]/returns: executed successfully}).to_stdout\n      result = File.read(resourcefile)\n\n      expect(result).not_to match(/secret_file_name/)\n      expect(result).to match(/do it/)\n    end\n  end\n\n  describe \"apply_catalog\" do\n    it \"should call the configurer with the catalog\" do\n      catalog = \"I am a catalog\"\n      expect_any_instance_of(Puppet::Configurer).to receive(:run).\n        with(:catalog => catalog, :pluginsync => false)\n      @apply.send(:apply_catalog, catalog)\n    end\n  end\n\n  it \"should honor the catalog_cache_terminus setting\" do\n    Puppet.settings[:catalog_cache_terminus] = \"json\"\n    expect(Puppet::Resource::Catalog.indirection).to receive(:cache_class=).with(:json)\n\n    @apply.initialize_app_defaults\n    @apply.setup\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/config_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/application/config'\n\ndescribe Puppet::Application::Config do\n  include PuppetSpec::Files\n\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  MIXED_UTF8 = \"A\\u06FF\\u16A0\\u{2070E}\" # Aۿᚠ𠜎\n\n  let(:app) { Puppet::Application[:config] }\n\n  before :each do\n    Puppet[:config] = tmpfile('config')\n  end\n\n  def initialize_app(args)\n    app.command_line.args = args\n    # ensure global defaults are initialized prior to app defaults\n    Puppet.initialize_settings(args)\n  end\n\n  def read_utf8(path)\n    File.read(path, :encoding => 'UTF-8')\n  end\n\n  def write_utf8(path, content)\n    File.write(path, content, 0, :encoding => 'UTF-8')\n  end\n\n  context \"when printing\" do\n    it \"prints a value\" do\n      initialize_app(%w[print certname])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(a_string_matching(Puppet[:certname])).to_stdout\n    end\n\n    it \"prints a value from a section\" do\n      File.write(Puppet[:config], <<~END)\n        [main]\n        external_nodes=none\n        [server]\n        external_nodes=exec\n      END\n\n      initialize_app(%w[print external_nodes --section server])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(a_string_matching('exec')).to_stdout\n    end\n\n    it \"doesn't require the environment to exist\" do\n      initialize_app(%w[print certname --environment doesntexist])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(a_string_matching(Puppet[:certname])).to_stdout\n    end\n  end\n\n  context \"when setting\" do\n    it \"sets a value in its config file\" do\n      initialize_app(%w[set certname www.example.com])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n\n      expect(File.read(Puppet[:config])).to eq(\"[main]\\ncertname = www.example.com\\n\")\n    end\n\n    it \"sets a value in the server section\" do\n      initialize_app(%w[set external_nodes exec --section server])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n\n      expect(File.read(Puppet[:config])).to eq(\"[server]\\nexternal_nodes = exec\\n\")\n    end\n\n    {\n      %w[certname WWW.EXAMPLE.COM] => /Certificate names must be lower case/,\n      %w[log_level all] => /Invalid loglevel all/,\n      %w[disable_warnings true] => /Cannot disable unrecognized warning types 'true'/,\n      %w[strict on] => /Invalid value 'on' for parameter strict/,\n      %w[digest_algorithm rot13] => /Invalid value 'rot13' for parameter digest_algorithm/,\n      %w[http_proxy_password a#b] => /Passwords set in the http_proxy_password setting must be valid as part of a URL/,\n    }.each_pair do |args, message|\n      it \"rejects #{args.join(' ')}\" do\n        initialize_app(['set', *args])\n\n        expect {\n          app.run\n        }.to exit_with(1)\n         .and output(message).to_stderr\n      end\n    end\n\n    it 'sets unknown settings' do\n      initialize_app(['set', 'notarealsetting', 'true'])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n\n      expect(File.read(Puppet[:config])).to eq(\"[main]\\nnotarealsetting = true\\n\")\n    end\n  end\n\n  context \"when deleting\" do\n    it \"deletes a value\" do\n      initialize_app(%w[delete external_nodes])\n\n      File.write(Puppet[:config], <<~END)\n        [main]\n        external_nodes=none\n      END\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/Deleted setting from 'main': 'external_nodes=none'/).to_stdout\n\n      expect(File.read(Puppet[:config])).to eq(\"[main]\\n\")\n    end\n\n    it \"warns when deleting a value that isn't set\" do\n      initialize_app(%w[delete external_nodes])\n\n      File.write(Puppet[:config], \"\")\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(a_string_matching(\"Warning: No setting found in configuration file for section 'main' setting name 'external_nodes'\")).to_stderr\n\n      expect(File.read(Puppet[:config])).to eq(\"\")\n    end\n\n    it \"deletes a value from main\" do\n      initialize_app(%w[delete external_nodes])\n\n      File.write(Puppet[:config], <<~END)\n        [main]\n        external_nodes=none\n      END\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/Deleted setting from 'main': 'external_nodes=none'/).to_stdout\n\n      expect(File.read(Puppet[:config])).to eq(\"[main]\\n\")\n    end\n\n    it \"deletes a value from main a section\" do\n      initialize_app(%w[delete external_nodes --section server])\n\n      File.write(Puppet[:config], <<~END)\n        [main]\n        external_nodes=none\n        [server]\n        external_nodes=exec\n      END\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/Deleted setting from 'server': 'external_nodes'/).to_stdout\n\n      expect(File.read(Puppet[:config])).to eq(\"[main]\\nexternal_nodes=none\\n[server]\\n\")\n    end\n  end\n\n  context \"when managing UTF-8 values\" do\n    it \"reads a UTF-8 value\" do\n      write_utf8(Puppet[:config], <<~EOF)\n        [main]\n        tags=#{MIXED_UTF8}\n      EOF\n\n      initialize_app(%w[print tags])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(\"#{MIXED_UTF8}\\n\").to_stdout\n    end\n\n    it \"sets a UTF-8 value\" do\n      initialize_app(['set', 'tags', MIXED_UTF8])\n\n      expect {\n        app.run\n      }.to exit_with(0)\n\n      expect(read_utf8(Puppet[:config])).to eq(<<~EOF)\n        [main]\n        tags = #{MIXED_UTF8}\n      EOF\n    end\n\n    it \"deletes a UTF-8 value\" do\n      initialize_app(%w[delete tags])\n\n      write_utf8(Puppet[:config], <<~EOF)\n        [main]\n        tags=#{MIXED_UTF8}\n      EOF\n\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/Deleted setting from 'main': 'tags=#{MIXED_UTF8}'/).to_stdout\n\n      expect(read_utf8(Puppet[:config])).to eq(<<~EOF)\n        [main]\n      EOF\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/describe_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/application/describe'\n\ndescribe Puppet::Application::Describe do\n  let(:describe) { Puppet::Application[:describe] }\n\n  it \"lists all types\" do\n    describe.command_line.args << '--list'\n\n    expect {\n      describe.run\n    }.to output(/These are the types known to puppet:/).to_stdout\n  end\n\n  it \"describes a single type\" do\n    describe.command_line.args << 'exec'\n\n    expect {\n      describe.run\n    }.to output(/exec.*====.*Executes external commands/m).to_stdout\n  end\n\n  it \"describes multiple types\" do\n    describe.command_line.args.concat(['exec', 'file'])\n\n    expect {\n      describe.run\n    }.to output(/Executes external commands.*Manages files, including their content, ownership, and permissions./m).to_stdout\n  end\n\n  it \"describes parameters for the type by default\" do\n    describe.command_line.args << 'exec'\n\n    expect {\n      describe.run\n    }.to output(/Parameters\\n----------/m).to_stdout\n  end\n\n  it \"lists parameter names, but excludes description in short mode\" do\n    describe.command_line.args.concat(['exec', '-s'])\n\n    expect {\n      describe.run\n    }.to output(/Parameters.*command, creates, cwd/m).to_stdout\n  end\n\n  it \"outputs providers for the type\" do\n    describe.command_line.args.concat(['exec', '--providers'])\n\n    expect {\n      describe.run\n    }.to output(/Providers.*#{Regexp.escape('**posix**')}.*#{Regexp.escape('**windows**')}/m).to_stdout\n  end\n\n  it \"lists metaparameters for a type\" do\n    describe.command_line.args.concat(['exec', '--meta'])\n\n    expect {\n      describe.run\n    }.to output(/Meta Parameters.*#{Regexp.escape('**notify**')}/m).to_stdout\n  end\n\n  it \"outputs no documentation if the summary is missing\" do\n    Puppet::Type.newtype(:describe_test) {}\n\n    describe.command_line.args << '--list'\n    expect {\n      describe.run\n    }.to output(/#{Regexp.escape(\"describe_test   - .. no documentation ..\")}/).to_stdout\n  end\n\n  it \"outputs the first short sentence ending in a dot\" do\n    Puppet::Type.newtype(:describe_test) do\n      @doc = \"ends in a dot.\"\n    end\n\n    describe.command_line.args << '--list'\n    expect {\n      describe.run\n    }.to output(/#{Regexp.escape(\"describe_test   - ends in a dot\\n\")}/).to_stdout\n  end\n\n  it \"outputs the first short sentence missing a dot\" do\n    Puppet::Type.newtype(:describe_test) do\n      @doc = \"missing a dot\"\n    end\n\n    describe.command_line.args << '--list'\n    expect {\n      describe.run\n    }.to output(/describe_test   - missing a dot\\n/).to_stdout\n  end\n\n  it \"truncates long summaries ending in a dot\" do\n    Puppet::Type.newtype(:describe_test) do\n      @doc = \"This sentence is more than 45 characters and ends in a dot.\"\n    end\n\n    describe.command_line.args << '--list'\n    expect {\n      describe.run\n    }.to output(/#{Regexp.escape(\"describe_test   - This sentence is more than 45 characters and  ...\")}/).to_stdout\n  end\n\n  it \"truncates long summaries missing a dot\" do\n    Puppet::Type.newtype(:describe_test) do\n      @doc = \"This sentence is more than 45 characters and is missing a dot\"\n    end\n\n    describe.command_line.args << '--list'\n    expect {\n      describe.run\n    }.to output(/#{Regexp.escape(\"describe_test   - This sentence is more than 45 characters and  ...\")}/).to_stdout\n  end\n\n  it \"formats text with long non-space runs without garbling\" do\n    f = Formatter.new(76)\n\n    teststring = <<TESTSTRING\n. 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 nick@magpie.puppetlabs.lan\n**this part should not repeat!**\nTESTSTRING\n\n    expected_result = <<EXPECTED\n.\n1234567890123456789012345678901234567890123456789012345678901234567890123456\n7890123456789012345678901234567890 nick@magpie.puppetlabs.lan\n**this part should not repeat!**\nEXPECTED\n\n    result = f.wrap(teststring, {:indent => 0, :scrub => true})\n    expect(result).to eql(expected_result)\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/device_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'ostruct'\nrequire 'puppet/application/apply'\nrequire 'puppet/application/device'\nrequire 'puppet/configurer'\nrequire 'puppet/util/network_device/config'\n\ndescribe Puppet::Application::Device do\n  include PuppetSpec::Files\n\n  let(:device) do\n    dev = Puppet::Application[:device]\n    allow(dev).to receive(:trap)\n    allow(Signal).to receive(:trap)\n    dev.preinit\n    dev\n  end\n  let(:ssl_context) { instance_double(Puppet::SSL::SSLContext, 'ssl_context') }\n  let(:state_machine) { instance_double(Puppet::SSL::StateMachine, 'state machine') }\n\n  before do\n    allow(Puppet::Node::Facts.indirection).to receive(:terminus_class=)\n    allow(Puppet::Node.indirection).to receive(:cache_class=)\n    allow(Puppet::Node.indirection).to receive(:terminus_class=)\n    allow(Puppet::Resource::Catalog.indirection).to receive(:cache_class=)\n    allow(Puppet::Resource::Catalog.indirection).to receive(:terminus_class=)\n    allow(Puppet::Transaction::Report.indirection).to receive(:terminus_class=)\n\n    allow(Puppet::Util::Log).to receive(:newdestination)\n\n    allow(state_machine).to receive(:ensure_client_certificate).and_return(ssl_context)\n    allow(Puppet::SSL::StateMachine).to receive(:new).and_return(state_machine)\n  end\n\n  it \"operates in agent run_mode\" do\n    expect(device.class.run_mode.name).to eq(:agent)\n  end\n\n  it \"declares a main command\" do\n    expect(device).to respond_to(:main)\n  end\n\n  it \"declares a preinit block\" do\n    expect(device).to respond_to(:preinit)\n  end\n\n  describe \"in preinit\" do\n    before do\n    end\n\n    it \"catches INT\" do\n      device\n\n      expect(Signal).to have_received(:trap).with(:INT)\n    end\n\n    it \"inits waitforcert to nil\" do\n      expect(device.options[:waitforcert]).to be_nil\n    end\n\n    it \"inits target to nil\" do\n      expect(device.options[:target]).to be_nil\n    end\n  end\n\n  describe \"when handling options\" do\n    before do\n      Puppet[:certname] = 'device.example.com'\n      allow(device.command_line).to receive(:args).and_return([])\n    end\n\n    [:centrallogging, :debug, :verbose,].each do |option|\n      it \"should declare handle_#{option} method\" do\n        expect(device).to respond_to(\"handle_#{option}\".to_sym)\n      end\n\n      it \"should store argument value when calling handle_#{option}\" do\n        allow(device.options).to receive(:[]=).with(option, 'arg')\n        device.send(\"handle_#{option}\".to_sym, 'arg')\n      end\n    end\n\n    context 'when setting --onetime' do\n      before do\n        Puppet[:onetime] = true\n      end\n\n      context 'without --waitforcert' do\n        it \"defaults waitforcert to 0\" do\n          device.setup_context\n\n          expect(Puppet::SSL::StateMachine).to have_received(:new).with(hash_including(waitforcert: 0))\n        end\n      end\n\n      context 'with --waitforcert=60' do\n        it \"uses supplied waitforcert\" do\n          device.handle_waitforcert(60)\n          device.setup_context\n\n          expect(Puppet::SSL::StateMachine).to have_received(:new).with(hash_including(waitforcert: 60))\n        end\n      end\n    end\n\n    context 'without setting --onetime' do\n      before do\n        Puppet[:onetime] = false\n      end\n\n      it \"uses a default value for waitforcert when --onetime and --waitforcert are not specified\" do\n        device.setup_context\n        expect(Puppet::SSL::StateMachine).to have_received(:new).with(hash_including(waitforcert: 120))\n      end\n\n      it \"uses the waitforcert setting when checking for a signed certificate\" do\n        Puppet[:waitforcert] = 10\n        device.setup_context\n        expect(Puppet::SSL::StateMachine).to have_received(:new).with(hash_including(waitforcert: 10))\n      end\n    end\n\n    it \"sets the log destination with --logdest\" do\n      allow(device.options).to receive(:[]=).with(:setdest, anything)\n      expect(Puppet::Log).to receive(:newdestination).with(\"console\")\n\n      device.handle_logdest(\"console\")\n    end\n\n    it \"puts the setdest options to true\" do\n      expect(device.options).to receive(:[]=).with(:setdest, true)\n\n      device.handle_logdest(\"console\")\n    end\n\n    it \"parses the log destination from the command line\" do\n      allow(device.command_line).to receive(:args).and_return(%w{--logdest /my/file})\n\n      expect(Puppet::Util::Log).to receive(:newdestination).with(\"/my/file\")\n\n      device.parse_options\n    end\n\n    it \"stores the waitforcert options with --waitforcert\" do\n      expect(device.options).to receive(:[]=).with(:waitforcert,42)\n\n      device.handle_waitforcert(\"42\")\n    end\n\n    it \"sets args[:Port] with --port\" do\n      device.handle_port(\"42\")\n      expect(device.args[:Port]).to eq(\"42\")\n    end\n\n    it \"stores the target options with --target\" do\n      expect(device.options).to receive(:[]=).with(:target,'test123')\n\n      device.handle_target('test123')\n    end\n\n    it \"stores the resource options with --resource\" do\n      expect(device.options).to receive(:[]=).with(:resource,true)\n\n      device.handle_resource(true)\n    end\n\n    it \"stores the facts options with --facts\" do\n      expect(device.options).to receive(:[]=).with(:facts,true)\n\n      device.handle_facts(true)\n    end\n\n    it \"should register ssl OIDs\" do\n      expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)\n\n      device.setup\n    end\n  end\n\n  describe \"during setup\" do\n    before do\n      allow(device.options).to receive(:[])\n      Puppet[:libdir] = \"/dev/null/lib\"\n    end\n\n    it \"calls setup_logs\" do\n      expect(device).to receive(:setup_logs)\n      device.setup\n    end\n\n    describe \"when setting up logs\" do\n      before do\n        allow(Puppet::Util::Log).to receive(:newdestination)\n      end\n\n      it \"sets log level to debug if --debug was passed\" do\n        allow(device.options).to receive(:[]).with(:debug).and_return(true)\n        device.setup_logs\n        expect(Puppet::Util::Log.level).to eq(:debug)\n      end\n\n      it \"sets log level to info if --verbose was passed\" do\n        allow(device.options).to receive(:[]).with(:verbose).and_return(true)\n        device.setup_logs\n        expect(Puppet::Util::Log.level).to eq(:info)\n      end\n\n      [:verbose, :debug].each do |level|\n        it \"should set console as the log destination with level #{level}\" do\n          allow(device.options).to receive(:[]).with(level).and_return(true)\n\n          expect(Puppet::Util::Log).to receive(:newdestination).with(:console)\n\n          device.setup_logs\n        end\n      end\n\n      it \"sets a default log destination if no --logdest\" do\n        allow(device.options).to receive(:[]).with(:setdest).and_return(false)\n\n        expect(Puppet::Util::Log).to receive(:setup_default)\n\n        device.setup_logs\n      end\n    end\n\n    it \"sets a central log destination with --centrallogs\" do\n      allow(device.options).to receive(:[]).with(:centrallogs).and_return(true)\n      Puppet[:server] = \"puppet.example.com\"\n      allow(Puppet::Util::Log).to receive(:newdestination).with(:syslog)\n\n      expect(Puppet::Util::Log).to receive(:newdestination).with(\"puppet.example.com\")\n\n      device.setup\n    end\n\n    it \"uses :main, :agent, :device and :ssl config\" do\n      expect(Puppet.settings).to receive(:use).with(:main, :agent, :device, :ssl)\n\n      device.setup\n    end\n\n    it \"tells the report handler to use REST\" do\n      device.setup\n      expect(Puppet::Transaction::Report.indirection).to have_received(:terminus_class=).with(:rest)\n    end\n\n    it \"defaults the catalog_terminus setting to 'rest'\" do\n      device.initialize_app_defaults\n      expect(Puppet[:catalog_terminus]).to eq(:rest)\n    end\n\n    it \"defaults the node_terminus setting to 'rest'\" do\n      device.initialize_app_defaults\n      expect(Puppet[:node_terminus]).to eq(:rest)\n    end\n\n    it \"has an application default :catalog_cache_terminus setting of 'json'\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:cache_class=).with(:json)\n\n      device.initialize_app_defaults\n      device.setup\n    end\n\n    it \"tells the catalog cache class based on the :catalog_cache_terminus setting\" do\n      Puppet[:catalog_cache_terminus] = \"yaml\"\n      expect(Puppet::Resource::Catalog.indirection).to receive(:cache_class=).with(:yaml)\n\n      device.initialize_app_defaults\n      device.setup\n    end\n\n    it \"does not set catalog cache class if :catalog_cache_terminus is explicitly nil\" do\n      Puppet[:catalog_cache_terminus] = nil\n      expect(Puppet::Resource::Catalog.indirection).not_to receive(:cache_class=)\n\n      device.initialize_app_defaults\n      device.setup\n    end\n\n    it \"defaults the facts_terminus setting to 'network_device'\" do\n      device.initialize_app_defaults\n      expect(Puppet[:facts_terminus]).to eq(:network_device)\n    end\n  end\n\n  describe \"when initializing SSL\" do\n    it \"creates a new ssl host\" do\n      allow(device.options).to receive(:[]).with(:waitforcert).and_return(123)\n\n      device.setup_context\n\n      expect(Puppet::SSL::StateMachine).to have_received(:new).with(hash_including(waitforcert: 123))\n    end\n  end\n\n  describe \"when running\" do\n    let(:device_hash) { {} }\n    let(:plugin_handler) { instance_double(Puppet::Configurer::PluginHandler, 'plugin_handler') }\n\n    before do\n      allow(device.options).to receive(:[]).with(:fingerprint).and_return(false)\n      allow(Puppet).to receive(:notice)\n      allow(device.options).to receive(:[]).with(:detailed_exitcodes).and_return(false)\n      allow(device.options).to receive(:[]).with(:target).and_return(nil)\n      allow(device.options).to receive(:[]).with(:apply).and_return(nil)\n      allow(device.options).to receive(:[]).with(:facts).and_return(false)\n      allow(device.options).to receive(:[]).with(:resource).and_return(false)\n      allow(device.options).to receive(:[]).with(:to_yaml).and_return(false)\n      allow(device.options).to receive(:[]).with(:libdir).and_return(nil)\n      allow(device.options).to receive(:[]).with(:client)\n      allow(device.command_line).to receive(:args).and_return([])\n      allow(Puppet::Util::NetworkDevice::Config).to receive(:devices).and_return(device_hash)\n      allow(Puppet::Configurer::PluginHandler).to receive(:new).and_return(plugin_handler)\n    end\n\n    it \"dispatches to main\" do\n      allow(device).to receive(:main)\n      device.run_command\n    end\n\n    it \"errors if resource is requested without target\" do\n      allow(device.options).to receive(:[]).with(:resource).and_return(true)\n      expect { device.main }.to raise_error(RuntimeError, \"resource command requires target\")\n    end\n\n    it \"errors if facts is requested without target\" do\n      allow(device.options).to receive(:[]).with(:facts).and_return(true)\n      expect { device.main }.to raise_error(RuntimeError, \"facts command requires target\")\n    end\n\n    it \"gets the device list\" do\n      expect(Puppet::Util::NetworkDevice::Config).to receive(:devices).and_return(device_hash)\n      expect { device.main }.to exit_with 1\n    end\n\n    it \"errors when an invalid target parameter is passed\" do\n      allow(device.options).to receive(:[]).with(:target).and_return('bla')\n      expect(Puppet).not_to receive(:info).with(/starting applying configuration to/)\n      expect { device.main }.to raise_error(RuntimeError, /Target device \\/ certificate 'bla' not found in .*\\.conf/)\n    end\n\n    it \"errors if target is passed and the apply path is incorrect\" do\n      allow(device.options).to receive(:[]).with(:apply).and_return('file.pp')\n      allow(device.options).to receive(:[]).with(:target).and_return('device1')\n\n      expect(File).to receive(:file?).and_return(false)\n      expect { device.main }.to raise_error(RuntimeError, /does not exist, cannot apply/)\n    end\n\n    it \"exits if the device list is empty\" do\n      expect { device.main }.to exit_with 1\n    end\n\n    context 'with some devices configured' do\n      let(:configurer) { instance_double(Puppet::Configurer, 'configurer') }\n      let(:device_hash) {\n        {\n          \"device1\" => OpenStruct.new(:name => \"device1\", :url => \"ssh://user:pass@testhost\", :provider => \"cisco\"),\n          \"device2\" => OpenStruct.new(:name => \"device2\", :url => \"https://user:pass@testhost/some/path\", :provider => \"rest\"),\n        }\n      }\n\n      before do\n        Puppet[:vardir] = make_absolute(\"/dummy\")\n        Puppet[:confdir] = make_absolute(\"/dummy\")\n        Puppet[:certname] = \"certname\"\n\n        allow(Puppet).to receive(:[]=)\n        allow(Puppet.settings).to receive(:use)\n\n        allow(device).to receive(:setup_context)\n        allow(Puppet::Util::NetworkDevice).to receive(:init)\n\n        allow(configurer).to receive(:run)\n        allow(Puppet::Configurer).to receive(:new).and_return(configurer)\n\n        allow(Puppet::FileSystem).to receive(:exist?)\n        allow(Puppet::FileSystem).to receive(:symlink)\n        allow(Puppet::FileSystem).to receive(:dir_mkpath).and_return(true)\n        allow(Puppet::FileSystem).to receive(:dir_exist?).and_return(true)\n\n        allow(plugin_handler).to receive(:download_plugins)\n      end\n\n      it \"sets ssldir relative to the global confdir\" do\n        expect(Puppet).to receive(:[]=).with(:ssldir, make_absolute(\"/dummy/devices/device1/ssl\"))\n        expect { device.main }.to exit_with 1\n      end\n\n      it \"sets vardir to the device vardir\" do\n        expect(Puppet).to receive(:[]=).with(:vardir, make_absolute(\"/dummy/devices/device1\"))\n        expect { device.main }.to exit_with 1\n      end\n\n      it \"sets confdir to the device confdir\" do\n        expect(Puppet).to receive(:[]=).with(:confdir, make_absolute(\"/dummy/devices/device1\"))\n        expect { device.main }.to exit_with 1\n      end\n\n      it \"sets certname to the device certname\" do\n        expect(Puppet).to receive(:[]=).with(:certname, \"device1\")\n        expect(Puppet).to receive(:[]=).with(:certname, \"device2\")\n        expect { device.main }.to exit_with 1\n      end\n\n      context 'with --target=device1' do\n        it \"symlinks the ssl directory if it doesn't exist\" do\n          allow(device.options).to receive(:[]).with(:target).and_return('device1')\n          allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n\n          expect(Puppet::FileSystem).to receive(:symlink).with(Puppet[:ssldir], File.join(Puppet[:confdir], 'ssl')).and_return(true)\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"creates the device confdir under the global confdir\" do\n          allow(device.options).to receive(:[]).with(:target).and_return('device1')\n          allow(Puppet::FileSystem).to receive(:dir_exist?).and_return(false)\n\n          expect(Puppet::FileSystem).to receive(:dir_mkpath).with(Puppet[:ssldir]).and_return(true)\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"manages the specified target\" do\n          allow(device.options).to receive(:[]).with(:target).and_return('device1')\n\n          expect(URI).to receive(:parse).with(\"ssh://user:pass@testhost\")\n          expect(URI).not_to receive(:parse).with(\"https://user:pass@testhost/some/path\")\n          expect { device.main }.to exit_with 1\n        end\n      end\n\n      context 'when running --resource' do\n        before do\n          allow(device.options).to receive(:[]).with(:resource).and_return(true)\n          allow(device.options).to receive(:[]).with(:target).and_return('device1')\n        end\n\n        it \"raises an error if no type is given\" do\n          allow(device.command_line).to receive(:args).and_return([])\n          expect(Puppet).to receive(:log_exception) { |e| expect(e.message).to eq(\"You must specify the type to display\") }\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"raises an error if the type is not found\" do\n          allow(device.command_line).to receive(:args).and_return(['nope'])\n          expect(Puppet).to receive(:log_exception) { |e| expect(e.message).to eq(\"Could not find type nope\") }\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"retrieves all resources of a type\" do\n          allow(device.command_line).to receive(:args).and_return(['user'])\n          expect(Puppet::Resource.indirection).to receive(:search).with('user/', {}).and_return([])\n          expect { device.main }.to exit_with 0\n        end\n\n        it \"retrieves named resources of a type\" do\n          resource = Puppet::Type.type(:user).new(:name => \"jim\").to_resource\n          allow(device.command_line).to receive(:args).and_return(['user', 'jim'])\n          expect(Puppet::Resource.indirection).to receive(:find).with('user/jim').and_return(resource)\n          expect(device).to receive(:puts).with(\"user { 'jim':\\n  ensure => 'absent',\\n}\")\n          expect { device.main }.to exit_with 0\n        end\n\n        it \"outputs resources as YAML\" do\n          resources = [\n            Puppet::Type.type(:user).new(:name => \"title\").to_resource,\n          ]\n          allow(device.options).to receive(:[]).with(:to_yaml).and_return(true)\n          allow(device.command_line).to receive(:args).and_return(['user'])\n          expect(Puppet::Resource.indirection).to receive(:search).with('user/', {}).and_return(resources)\n          expect(device).to receive(:puts).with(\"---\\nuser:\\n  title:\\n    ensure: absent\\n\")\n          expect { device.main }.to exit_with 0\n        end\n      end\n\n      context 'when running --facts' do\n        before do\n          allow(device.options).to receive(:[]).with(:facts).and_return(true)\n          allow(device.options).to receive(:[]).with(:target).and_return('device1')\n        end\n\n        it \"retrieves facts\" do\n          indirection_fact_values = {\"os\"=> {\"name\" => \"cisco_ios\"},\"clientcert\"=>\"3750\"}\n          indirection_facts = Puppet::Node::Facts.new(\"nil\", indirection_fact_values)\n          expect(Puppet::Node::Facts.indirection).to receive(:find).with(nil, anything()).and_return(indirection_facts)\n          expect(device).to receive(:puts).with(/name.*3750.*\\n.*values.*\\n.*os.*\\n.*name.*cisco_ios/)\n          expect { device.main }.to exit_with 0\n        end\n      end\n\n      context 'when running in agent mode' do\n        it \"makes sure all the required folders and files are created\" do\n          expect(Puppet.settings).to receive(:use).with(:main, :agent, :ssl).twice\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"initializes the device singleton\" do\n          expect(Puppet::Util::NetworkDevice).to receive(:init).with(device_hash[\"device1\"]).ordered\n          expect(Puppet::Util::NetworkDevice).to receive(:init).with(device_hash[\"device2\"]).ordered\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"retrieves plugins and print the device url scheme, host, and port\" do\n          allow(Puppet).to receive(:info)\n          expect(plugin_handler).to receive(:download_plugins).twice\n          expect(Puppet).to receive(:info).with(\"starting applying configuration to device1 at ssh://testhost\")\n          expect(Puppet).to receive(:info).with(\"starting applying configuration to device2 at https://testhost:443/some/path\")\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"setups the SSL context before pluginsync\" do\n          expect(device).to receive(:setup_context).ordered\n          expect(plugin_handler).to receive(:download_plugins).ordered\n          expect(device).to receive(:setup_context).ordered\n          expect(plugin_handler).to receive(:download_plugins).ordered\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"launches a configurer for this device\" do\n          expect(configurer).to receive(:run).twice\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"exits 1 when configurer raises error\" do\n          expect(configurer).to receive(:run).and_raise(Puppet::Error).ordered\n          expect(configurer).to receive(:run).and_return(0).ordered\n          expect { device.main }.to exit_with 1\n        end\n\n        it \"exits 0 when run happens without puppet errors but with failed run\" do\n          allow(configurer).to receive(:run).and_return(6, 2)\n          expect { device.main }.to exit_with 0\n        end\n\n        it \"makes the Puppet::Pops::Loaders available\" do\n          expect(configurer).to receive(:run).with({:network_device => true, :pluginsync => false}) do\n            fail('Loaders not available') unless Puppet.lookup(:loaders) { nil }.is_a?(Puppet::Pops::Loaders)\n            true\n          end.and_return(6, 2)\n          expect { device.main }.to exit_with 0\n        end\n\n        it \"exits 2 when --detailed-exitcodes and successful runs\" do\n          allow(device.options).to receive(:[]).with(:detailed_exitcodes).and_return(true)\n          allow(configurer).to receive(:run).and_return(0, 2)\n          expect { device.main }.to exit_with 2\n        end\n\n        it \"exits 1 when --detailed-exitcodes and failed parse\" do\n          allow(Puppet::Configurer).to receive(:new).and_return(configurer)\n          allow(device.options).to receive(:[]).with(:detailed_exitcodes).and_return(true)\n          allow(configurer).to receive(:run).and_return(6, 1)\n          expect { device.main }.to exit_with 7\n        end\n\n        it \"exits 6 when --detailed-exitcodes and failed run\" do\n          allow(Puppet::Configurer).to receive(:new).and_return(configurer)\n          allow(device.options).to receive(:[]).with(:detailed_exitcodes).and_return(true)\n          allow(configurer).to receive(:run).and_return(6, 2)\n          expect { device.main }.to exit_with 6\n        end\n\n        [:vardir, :confdir].each do |setting|\n          it \"resets the #{setting} setting after the run\" do\n            all_devices = Set.new(device_hash.keys.map do |device_name| make_absolute(\"/dummy/devices/#{device_name}\") end)\n            found_devices = Set.new()\n\n            # a block to use in a few places later to validate the updated settings\n            p = Proc.new do |my_setting, my_value|\n              expect(all_devices).to include(my_value)\n              found_devices.add(my_value)\n            end\n\n            all_devices.size.times do\n              ## one occurrence of set / run / set(\"/dummy\") for each device\n              expect(Puppet).to receive(:[]=, &p).with(setting, anything).ordered\n              expect(configurer).to receive(:run).ordered\n              expect(Puppet).to receive(:[]=).with(setting, make_absolute(\"/dummy\")).ordered\n            end\n\n            expect { device.main }.to exit_with 1\n\n            expect(found_devices).to eq(all_devices)\n          end\n        end\n\n        it \"resets the certname setting after the run\" do\n          all_devices = Set.new(device_hash.keys)\n          found_devices = Set.new()\n\n          # a block to use in a few places later to validate the updated settings\n          p = Proc.new do |my_setting, my_value|\n            expect(all_devices).to include(my_value)\n            found_devices.add(my_value)\n          end\n\n          allow(Puppet).to receive(:[]=)\n          all_devices.size.times do\n            ## one occurrence of set / run / set(\"certname\") for each device\n            expect(Puppet).to receive(:[]=, &p).with(:certname, anything).ordered\n            expect(configurer).to receive(:run).ordered\n            expect(Puppet).to receive(:[]=).with(:certname, \"certname\").ordered\n          end\n\n\n          expect { device.main }.to exit_with 1\n\n          # make sure that we were called with each of the defined devices\n          expect(found_devices).to eq(all_devices)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/doc_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/application/doc'\nrequire 'puppet/util/reference'\nrequire 'puppet/util/rdoc'\n\ndescribe Puppet::Application::Doc do\n  before :each do\n    @doc = Puppet::Application[:doc]\n    allow(@doc).to receive(:puts)\n    @doc.preinit\n    allow(Puppet::Util::Log).to receive(:newdestination)\n  end\n\n  it \"should declare an other command\" do\n    expect(@doc).to respond_to(:other)\n  end\n\n  it \"should declare a rdoc command\" do\n    expect(@doc).to respond_to(:rdoc)\n  end\n\n  it \"should declare a fallback for unknown options\" do\n    expect(@doc).to respond_to(:handle_unknown)\n  end\n\n  it \"should declare a preinit block\" do\n    expect(@doc).to respond_to(:preinit)\n  end\n\n  describe \"in preinit\" do\n    it \"should set references to []\" do\n      @doc.preinit\n\n      expect(@doc.options[:references]).to eq([])\n    end\n\n    it \"should init mode to text\" do\n      @doc.preinit\n\n      expect(@doc.options[:mode]).to eq(:text)\n    end\n\n    it \"should init format to to_markdown\" do\n      @doc.preinit\n\n      expect(@doc.options[:format]).to eq(:to_markdown)\n    end\n  end\n\n  describe \"when handling options\" do\n    [:all, :outputdir, :verbose, :debug, :charset].each do |option|\n      it \"should declare handle_#{option} method\" do\n        expect(@doc).to respond_to(\"handle_#{option}\".to_sym)\n      end\n\n      it \"should store argument value when calling handle_#{option}\" do\n        expect(@doc.options).to receive(:[]=).with(option, 'arg')\n        @doc.send(\"handle_#{option}\".to_sym, 'arg')\n      end\n    end\n\n    it \"should store the format if valid\" do\n      allow(Puppet::Util::Reference).to receive(:method_defined?).with('to_format').and_return(true)\n\n      @doc.handle_format('format')\n      expect(@doc.options[:format]).to eq('to_format')\n    end\n\n    it \"should raise an error if the format is not valid\" do\n      allow(Puppet::Util::Reference).to receive(:method_defined?).with('to_format').and_return(false)\n      expect { @doc.handle_format('format') }.to raise_error(RuntimeError, /Invalid output format/)\n    end\n\n    it \"should store the mode if valid\" do\n      allow(Puppet::Util::Reference).to receive(:modes).and_return(double('mode', :include? => true))\n\n      @doc.handle_mode('mode')\n      expect(@doc.options[:mode]).to eq(:mode)\n    end\n\n    it \"should store the mode if :rdoc\" do\n      allow(Puppet::Util::Reference.modes).to receive(:include?).with('rdoc').and_return(false)\n\n      @doc.handle_mode('rdoc')\n      expect(@doc.options[:mode]).to eq(:rdoc)\n    end\n\n    it \"should raise an error if the mode is not valid\" do\n      allow(Puppet::Util::Reference.modes).to receive(:include?).with('unknown').and_return(false)\n      expect { @doc.handle_mode('unknown') }.to raise_error(RuntimeError, /Invalid output mode/)\n    end\n\n    it \"should list all references on list and exit\" do\n      reference = double('reference')\n      ref = double('ref')\n      allow(Puppet::Util::Reference).to receive(:references).and_return([reference])\n\n      expect(Puppet::Util::Reference).to receive(:reference).with(reference).and_return(ref)\n      expect(ref).to receive(:doc)\n\n      expect { @doc.handle_list(nil) }.to exit_with 0\n    end\n\n    it \"should add reference to references list with --reference\" do\n      @doc.options[:references] = [:ref1]\n\n      @doc.handle_reference('ref2')\n\n      expect(@doc.options[:references]).to eq([:ref1,:ref2])\n    end\n  end\n\n  describe \"during setup\" do\n    before :each do\n      allow(Puppet::Log).to receive(:newdestination)\n      allow(@doc.command_line).to receive(:args).and_return([])\n    end\n\n    it \"should default to rdoc mode if there are command line arguments\" do\n      allow(@doc.command_line).to receive(:args).and_return([\"1\"])\n      allow(@doc).to receive(:setup_rdoc)\n\n      @doc.setup\n      expect(@doc.options[:mode]).to eq(:rdoc)\n    end\n\n    it \"should call setup_rdoc in rdoc mode\" do\n      @doc.options[:mode] = :rdoc\n\n      expect(@doc).to receive(:setup_rdoc)\n\n      @doc.setup\n    end\n\n    it \"should call setup_reference if not rdoc\" do\n      @doc.options[:mode] = :test\n\n      expect(@doc).to receive(:setup_reference)\n\n      @doc.setup\n    end\n\n    describe \"configuring logging\" do\n      before :each do\n        allow(Puppet::Util::Log).to receive(:newdestination)\n      end\n\n      describe \"with --debug\" do\n        before do\n          @doc.options[:debug] = true\n        end\n\n        it \"should set log level to debug\" do\n          @doc.setup\n          expect(Puppet::Util::Log.level).to eq(:debug)\n        end\n\n        it \"should set log destination to console\" do\n          expect(Puppet::Util::Log).to receive(:newdestination).with(:console)\n          @doc.setup\n        end\n      end\n\n      describe \"with --verbose\" do\n        before do\n          @doc.options[:verbose] = true\n        end\n\n        it \"should set log level to info\" do\n          @doc.setup\n          expect(Puppet::Util::Log.level).to eq(:info)\n        end\n\n        it \"should set log destination to console\" do\n          expect(Puppet::Util::Log).to receive(:newdestination).with(:console)\n          @doc.setup\n        end\n      end\n\n      describe \"without --debug or --verbose\" do\n        before do\n          @doc.options[:debug] = false\n          @doc.options[:verbose] = false\n        end\n\n        it \"should set log level to warning\" do\n          @doc.setup\n          expect(Puppet::Util::Log.level).to eq(:warning)\n        end\n\n        it \"should set log destination to console\" do\n          expect(Puppet::Util::Log).to receive(:newdestination).with(:console)\n          @doc.setup\n        end\n      end\n    end\n\n    describe \"in non-rdoc mode\" do\n      it \"should get all non-dynamic reference if --all\" do\n        @doc.options[:all] = true\n        static = double('static', :dynamic? => false)\n        dynamic = double('dynamic', :dynamic? => true)\n        allow(Puppet::Util::Reference).to receive(:reference).with(:static).and_return(static)\n        allow(Puppet::Util::Reference).to receive(:reference).with(:dynamic).and_return(dynamic)\n        allow(Puppet::Util::Reference).to receive(:references).and_return([:static,:dynamic])\n\n        @doc.setup_reference\n        expect(@doc.options[:references]).to eq([:static])\n      end\n\n      it \"should default to :type if no references\" do\n        @doc.setup_reference\n        expect(@doc.options[:references]).to eq([:type])\n      end\n    end\n\n    describe \"in rdoc mode\" do\n      describe \"when there are unknown args\" do\n        it \"should expand --modulepath if any\" do\n          @doc.unknown_args = [ { :opt => \"--modulepath\", :arg => \"path\" } ]\n          allow(Puppet.settings).to receive(:handlearg)\n\n          @doc.setup_rdoc\n\n          expect(@doc.unknown_args[0][:arg]).to eq(File.expand_path('path'))\n        end\n\n        it \"should give them to Puppet.settings\" do\n          @doc.unknown_args = [ { :opt => :option, :arg => :argument } ]\n          expect(Puppet.settings).to receive(:handlearg).with(:option,:argument)\n\n          @doc.setup_rdoc\n        end\n      end\n\n      it \"should operate in server run_mode\" do\n        expect(@doc.class.run_mode.name).to eq(:server)\n\n        @doc.setup_rdoc\n      end\n    end\n  end\n\n  describe \"when running\" do\n    describe \"in rdoc mode\" do\n      include PuppetSpec::Files\n\n      let(:envdir) { tmpdir('env') }\n      let(:modules) { File.join(envdir, \"modules\") }\n      let(:modules2) { File.join(envdir, \"modules2\") }\n      let(:manifests) { File.join(envdir, \"manifests\") }\n\n      before :each do\n        @doc.manifest = false\n        allow(Puppet).to receive(:info)\n        Puppet[:trace] = false\n        Puppet[:modulepath] = modules\n        Puppet[:manifest] = manifests\n        @doc.options[:all] = false\n        @doc.options[:outputdir] = 'doc'\n        @doc.options[:charset] = nil\n        allow(Puppet.settings).to receive(:define_settings)\n        allow(Puppet::Util::RDoc).to receive(:rdoc)\n        allow(@doc.command_line).to receive(:args).and_return([])\n      end\n\n      around(:each) do |example|\n        FileUtils.mkdir_p(modules)\n        env = Puppet::Node::Environment.create(Puppet[:environment].to_sym, [modules], \"#{manifests}/site.pp\")\n        Puppet.override({:environments => Puppet::Environments::Static.new(env), :current_environment => env}) do\n          example.run\n        end\n      end\n\n      it \"should set document_all on --all\" do\n        @doc.options[:all] = true\n        expect(Puppet.settings).to receive(:[]=).with(:document_all, true)\n\n        expect { @doc.rdoc }.to exit_with(0)\n      end\n\n      it \"should call Puppet::Util::RDoc.rdoc in full mode\" do\n        expect(Puppet::Util::RDoc).to receive(:rdoc).with('doc', [modules, manifests], nil)\n        expect { @doc.rdoc }.to exit_with(0)\n      end\n\n      it \"should call Puppet::Util::RDoc.rdoc with a charset if --charset has been provided\" do\n        @doc.options[:charset] = 'utf-8'\n        expect(Puppet::Util::RDoc).to receive(:rdoc).with('doc', [modules, manifests], \"utf-8\")\n        expect { @doc.rdoc }.to exit_with(0)\n      end\n\n      it \"should call Puppet::Util::RDoc.rdoc in full mode with outputdir set to doc if no --outputdir\" do\n        @doc.options[:outputdir] = false\n        expect(Puppet::Util::RDoc).to receive(:rdoc).with('doc', [modules, manifests], nil)\n        expect { @doc.rdoc }.to exit_with(0)\n      end\n\n      it \"should call Puppet::Util::RDoc.manifestdoc in manifest mode\" do\n        @doc.manifest = true\n        expect(Puppet::Util::RDoc).to receive(:manifestdoc)\n        expect { @doc.rdoc }.to exit_with(0)\n      end\n\n      it \"should get modulepath and manifest values from the environment\" do\n        FileUtils.mkdir_p(modules)\n        FileUtils.mkdir_p(modules2)\n        env = Puppet::Node::Environment.create(Puppet[:environment].to_sym,\n          [modules, modules2],\n          \"envmanifests/site.pp\")\n        Puppet.override({:environments => Puppet::Environments::Static.new(env), :current_environment => env}) do\n           allow(Puppet::Util::RDoc).to receive(:rdoc).with('doc', [modules.to_s, modules2.to_s, env.manifest.to_s], nil)\n          expect { @doc.rdoc }.to exit_with(0)\n        end\n      end\n    end\n\n    describe \"in the other modes\" do\n      it \"should get reference in given format\" do\n        reference = double('reference')\n        @doc.options[:mode] = :none\n        @doc.options[:references] = [:ref]\n        expect(Puppet::Util::Reference).to receive(:reference).with(:ref).and_return(reference)\n        @doc.options[:format] = :format\n        allow(@doc).to receive(:exit)\n\n        expect(reference).to receive(:send).with(:format, anything).and_return('doc')\n        @doc.other\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/face_base_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/application/face_base'\nrequire 'tmpdir'\n\nclass Puppet::Application::FaceBase::Basetest < Puppet::Application::FaceBase\nend\n\ndescribe Puppet::Application::FaceBase do\n  let :app do\n    app = Puppet::Application::FaceBase::Basetest.new\n    allow(app.command_line).to receive(:subcommand_name).and_return('subcommand')\n    allow(Puppet::Util::Log).to receive(:newdestination)\n    app\n  end\n\n  after :each do\n    app.class.clear_everything_for_tests\n  end\n\n  describe \"#find_global_settings_argument\" do\n    it \"should not match --ca to --ca-location\" do\n      option = double('ca option', :optparse_args => [\"--ca\"])\n      expect(Puppet.settings).to receive(:each).and_yield(:ca, option)\n\n      expect(app.find_global_settings_argument(\"--ca-location\")).to be_nil\n    end\n  end\n\n  describe \"#parse_options\" do\n    before :each do\n      allow(app.command_line).to receive(:args).and_return(%w{})\n    end\n\n    describe \"with just an action\" do\n      before(:each) do\n        # We have to stub Signal.trap to avoid a crazy mess where we take\n        # over signal handling and make it impossible to cancel the test\n        # suite run.\n        #\n        # It would be nice to fix this elsewhere, but it is actually hard to\n        # capture this in rspec 2.5 and all. :(  --daniel 2011-04-08\n        allow(Signal).to receive(:trap)\n        allow(app.command_line).to receive(:args).and_return(%w{foo})\n        app.preinit\n        app.parse_options\n      end\n\n      it \"should set the face based on the type\" do\n        expect(app.face.name).to eq(:basetest)\n      end\n\n      it \"should find the action\" do\n        expect(app.action).to be\n        expect(app.action.name).to eq(:foo)\n      end\n    end\n\n    it \"should stop if the first thing found is not an action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{banana count_args})\n\n      expect { app.run }.to exit_with(1)\n\n      expect(@logs.map(&:message)).to eq([\"'basetest' has no 'banana' action.  See `puppet help basetest`.\"])\n    end\n\n    it \"should use the default action if not given any arguments\" do\n      allow(app.command_line).to receive(:args).and_return([])\n      action = double(:options => [], :render_as => nil)\n      expect(Puppet::Face[:basetest, '0.0.1']).to receive(:get_default_action).and_return(action)\n      allow(app).to receive(:main)\n      app.run\n      expect(app.action).to eq(action)\n      expect(app.arguments).to eq([ { } ])\n    end\n\n    it \"should use the default action if not given a valid one\" do\n      allow(app.command_line).to receive(:args).and_return(%w{bar})\n      action = double(:options => [], :render_as => nil)\n      expect(Puppet::Face[:basetest, '0.0.1']).to receive(:get_default_action).and_return(action)\n      allow(app).to receive(:main)\n      app.run\n      expect(app.action).to eq(action)\n      expect(app.arguments).to eq([ 'bar', { } ])\n    end\n\n    it \"should have no action if not given a valid one and there is no default action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{bar})\n      expect(Puppet::Face[:basetest, '0.0.1']).to receive(:get_default_action).and_return(nil)\n      allow(app).to receive(:main)\n      expect { app.run }.to exit_with(1)\n      expect(@logs.first.message).to match(/has no 'bar' action./)\n    end\n\n    [%w{something_I_cannot_do},\n     %w{something_I_cannot_do argument}].each do |input|\n      it \"should report unknown actions nicely\" do\n        allow(app.command_line).to receive(:args).and_return(input)\n        expect(Puppet::Face[:basetest, '0.0.1']).to receive(:get_default_action).and_return(nil)\n        allow(app).to receive(:main)\n        expect { app.run }.to exit_with(1)\n        expect(@logs.first.message).to match(/has no 'something_I_cannot_do' action/)\n      end\n    end\n\n    [%w{something_I_cannot_do --unknown-option},\n     %w{something_I_cannot_do argument --unknown-option}].each do |input|\n      it \"should report unknown actions even if there are unknown options\" do\n        allow(app.command_line).to receive(:args).and_return(input)\n        expect(Puppet::Face[:basetest, '0.0.1']).to receive(:get_default_action).and_return(nil)\n        allow(app).to receive(:main)\n        expect { app.run }.to exit_with(1)\n        expect(@logs.first.message).to match(/has no 'something_I_cannot_do' action/)\n      end\n    end\n\n    it \"should report a sensible error when options with = fail\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--action=bar foo})\n      expect { app.preinit; app.parse_options }.\n        to raise_error(OptionParser::InvalidOption, /invalid option: --action/)\n    end\n\n    it \"should fail if an action option is before the action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--action foo})\n      expect { app.preinit; app.parse_options }.\n        to raise_error(OptionParser::InvalidOption, /invalid option: --action/)\n    end\n\n    it \"should fail if an unknown option is before the action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--bar foo})\n      expect { app.preinit; app.parse_options }.\n        to raise_error(OptionParser::InvalidOption, /invalid option: --bar/)\n    end\n\n    it \"should fail if an unknown option is after the action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{foo --bar})\n      expect { app.preinit; app.parse_options }.\n        to raise_error(OptionParser::InvalidOption, /invalid option: --bar/)\n    end\n\n    it \"should accept --bar as an argument to a mandatory option after action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{foo --mandatory --bar})\n      app.preinit\n      app.parse_options\n      expect(app.action.name).to eq(:foo)\n      expect(app.options).to eq({ :mandatory => \"--bar\" })\n    end\n\n    it \"should accept --bar as an argument to a mandatory option before action\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--mandatory --bar foo})\n      app.preinit\n      app.parse_options\n      expect(app.action.name).to eq(:foo)\n      expect(app.options).to eq({ :mandatory => \"--bar\" })\n    end\n\n    it \"should not skip when --foo=bar is given\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--mandatory=bar --bar foo})\n      expect { app.preinit; app.parse_options }.\n        to raise_error(OptionParser::InvalidOption, /invalid option: --bar/)\n    end\n\n    it \"does not skip when a puppet global setting is given as one item\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--confdir=/tmp/puppet foo})\n      app.preinit\n      app.parse_options\n      expect(app.action.name).to eq(:foo)\n      expect(app.options).to eq({})\n    end\n\n    it \"does not skip when a puppet global setting is given as two items\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--confdir /tmp/puppet foo})\n      app.preinit\n      app.parse_options\n      expect(app.action.name).to eq(:foo)\n      expect(app.options).to eq({})\n    end\n\n    it \"should not add :debug to the application-level options\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--confdir /tmp/puppet foo --debug})\n      app.preinit\n      app.parse_options\n      expect(app.action.name).to eq(:foo)\n      expect(app.options).to eq({})\n    end\n\n    it \"should not add :verbose to the application-level options\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--confdir /tmp/puppet foo --verbose})\n      app.preinit\n      app.parse_options\n      expect(app.action.name).to eq(:foo)\n      expect(app.options).to eq({})\n    end\n\n    { \"boolean options before\" => %w{--trace foo},\n      \"boolean options after\"  => %w{foo --trace}\n    }.each do |name, args|\n      it \"should accept global boolean settings #{name} the action\" do\n        allow(app.command_line).to receive(:args).and_return(args)\n        Puppet.settings.initialize_global_settings(args)\n        app.preinit\n        app.parse_options\n        expect(Puppet[:trace]).to be_truthy\n      end\n    end\n\n    { \"before\" => %w{--syslogfacility user1 foo},\n      \" after\" => %w{foo --syslogfacility user1}\n    }.each do |name, args|\n      it \"should accept global settings with arguments #{name} the action\" do\n        allow(app.command_line).to receive(:args).and_return(args)\n        Puppet.settings.initialize_global_settings(args)\n        app.preinit\n        app.parse_options\n        expect(Puppet[:syslogfacility]).to eq(\"user1\")\n      end\n    end\n\n    it \"should handle application-level options\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--verbose return_true})\n      app.preinit\n      app.parse_options\n      expect(app.face.name).to eq(:basetest)\n    end\n  end\n\n  describe \"#setup\" do\n    it \"should remove the action name from the arguments\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--mandatory --bar foo})\n      app.preinit\n      app.parse_options\n      app.setup\n      expect(app.arguments).to eq([{ :mandatory => \"--bar\" }])\n    end\n\n    it \"should pass positional arguments\" do\n      myargs = %w{--mandatory --bar foo bar baz quux}\n      allow(app.command_line).to receive(:args).and_return(myargs)\n      app.preinit\n      app.parse_options\n      app.setup\n      expect(app.arguments).to eq(['bar', 'baz', 'quux', { :mandatory => \"--bar\" }])\n    end\n  end\n\n  describe \"#main\" do\n    before :each do\n      allow(app).to receive(:puts)          # don't dump text to screen.\n\n      app.face      = Puppet::Face[:basetest, '0.0.1']\n      app.action    = app.face.get_action(:foo)\n      app.arguments = [\"myname\", \"myarg\"]\n    end\n\n    it \"should send the specified verb and name to the face\" do\n      expect(app.face).to receive(:foo).with(*app.arguments)\n      expect { app.main }.to exit_with(0)\n    end\n\n    it \"should lookup help when it cannot do anything else\" do\n      app.action = nil\n      expect(Puppet::Face[:help, :current]).to receive(:help).with(:basetest)\n      expect { app.main }.to exit_with(1)\n    end\n\n    it \"should use its render method to render any result\" do\n      expect(app).to receive(:render).with(app.arguments.length + 1, [\"myname\", \"myarg\"])\n      expect { app.main }.to exit_with(0)\n    end\n\n    it \"should issue a deprecation warning if the face is deprecated\" do\n      # since app is shared across examples, stub to avoid affecting shared context\n      allow(app.face).to receive(:deprecated?).and_return(true)\n      expect(app.face).to receive(:foo).with(*app.arguments)\n      expect(Puppet).to receive(:deprecation_warning).with(/'puppet basetest' is deprecated/)\n      expect { app.main }.to exit_with(0)\n    end\n\n    it \"should not issue a deprecation warning if the face is not deprecated\" do\n      expect(Puppet).not_to receive(:deprecation_warning)\n      # since app is shared across examples, stub to avoid affecting shared context\n      allow(app.face).to receive(:deprecated?).and_return(false)\n      expect(app.face).to receive(:foo).with(*app.arguments)\n      expect { app.main }.to exit_with(0)\n    end\n  end\n\n  describe \"error reporting\" do\n    before :each do\n      allow(app).to receive(:puts)          # don't dump text to screen.\n\n      app.render_as = :json\n      app.face      = Puppet::Face[:basetest, '0.0.1']\n      app.arguments = [{}]      # we always have options in there...\n    end\n\n    it \"should exit 0 when the action returns true\" do\n      app.action    = app.face.get_action :return_true\n      expect { app.main }.to exit_with(0)\n    end\n\n    it \"should exit 0 when the action returns false\" do\n      app.action = app.face.get_action :return_false\n      expect { app.main }.to exit_with(0)\n    end\n\n    it \"should exit 0 when the action returns nil\" do\n      app.action = app.face.get_action :return_nil\n      expect { app.main }.to exit_with(0)\n    end\n\n    it \"should exit non-0 when the action raises\" do\n      app.action = app.face.get_action :return_raise\n      expect { app.main }.not_to exit_with(0)\n    end\n\n    it \"should use the exit code set by the action\" do\n      app.action = app.face.get_action :with_specific_exit_code\n      expect { app.main }.to exit_with(5)\n    end\n  end\n\n  describe \"#render\" do\n    before :each do\n      app.face      = Puppet::Interface.new('basetest', '0.0.1')\n      app.action    = Puppet::Interface::Action.new(app.face, :foo)\n    end\n\n    context \"default rendering\" do\n      before :each do app.setup end\n\n      [\"hello\", 1, 1.0].each do |input|\n        it \"should just return a #{input.class.name}\" do\n          expect(app.render(input, {})).to eq(input)\n        end\n      end\n\n      [[1, 2], [\"one\"], [{ 1 => 1 }]].each do |input|\n        it \"should render Array as one item per line\" do\n          expect(app.render(input, {})).to eq(input.collect { |item| item.to_s + \"\\n\" }.join(''))\n        end\n      end\n\n      it \"should render a non-trivially-keyed Hash with using pretty printed JSON\" do\n        hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 }\n        expect(app.render(hash, {})).to eq(Puppet::Util::Json.dump(hash, :pretty => true).chomp)\n      end\n\n      it \"should render a {String,Numeric}-keyed Hash into a table\" do\n        object = Object.new\n        hash = { \"one\" => 1, \"two\" => [], \"three\" => {}, \"four\" => object,\n          5 => 5, 6.0 => 6 }\n\n        # Gotta love ASCII-betical sort order.  Hope your objects are better\n        # structured for display than my test one is. --daniel 2011-04-18\n        expect(app.render(hash, {})).to eq <<EOT\n5      5\n6.0    6\nfour   #{Puppet::Util::Json.dump(object).chomp}\none    1\nthree  {}\ntwo    []\nEOT\n      end\n\n      it \"should render a hash nicely with a multi-line value\" do\n        pending \"Moving to PSON rather than PP makes this unsupportable.\"\n        hash = {\n          \"number\" => { \"1\" => '1' * 40, \"2\" => '2' * 40, '3' => '3' * 40 },\n          \"text\"   => { \"a\" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 }\n        }\n        expect(app.render(hash, {})).to eq <<EOT\nnumber  {\"1\"=>\"1111111111111111111111111111111111111111\",\n         \"2\"=>\"2222222222222222222222222222222222222222\",\n         \"3\"=>\"3333333333333333333333333333333333333333\"}\ntext    {\"a\"=>\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n         \"b\"=>\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\n         \"c\"=>\"cccccccccccccccccccccccccccccccccccccccc\"}\nEOT\n      end\n\n      describe \"when setting the rendering method\" do\n        after do\n          # need to reset the when_rendering block so that other tests can set it later\n          app.action.instance_variable_set(\"@when_rendering\", {})\n        end\n\n        it \"should invoke the action rendering hook while rendering\" do\n          app.action.set_rendering_method_for(:console, proc { |value| \"bi-winning!\" })\n          expect(app.render(\"bi-polar?\", {})).to eq(\"bi-winning!\")\n        end\n\n        it \"should invoke the action rendering hook with args and options while rendering\" do\n          app.action.instance_variable_set(\"@when_rendering\", {})\n          app.action.when_invoked = proc { |name, options| 'just need to match arity for rendering' }\n          app.action.set_rendering_method_for(\n            :console,\n            proc { |value, name, options| \"I'm #{name}, no wait, I'm #{options[:altername]}\" }\n          )\n          expect(app.render(\"bi-polar?\", ['bob', {:altername => 'sue'}])).to eq(\"I'm bob, no wait, I'm sue\")\n        end\n      end\n\n      it \"should render JSON when asked for json\" do\n        app.render_as = :json\n        json = app.render({ :one => 1, :two => 2 }, {})\n        expect(json).to match(/\"one\":\\s*1\\b/)\n        expect(json).to match(/\"two\":\\s*2\\b/)\n        expect(JSON.parse(json)).to eq({ \"one\" => 1, \"two\" => 2 })\n      end\n    end\n\n    it \"should fail early if asked to render an invalid format\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--render-as interpretive-dance return_true})\n      # We shouldn't get here, thanks to the exception, and our expectation on\n      # it, but this helps us fail if that slips up and all. --daniel 2011-04-27\n      expect(Puppet::Face[:help, :current]).not_to receive(:help)\n\n      expect(Puppet).to receive(:send_log).with(:err, \"Could not parse application options: I don't know how to render 'interpretive-dance'\")\n\n      expect { app.run }.to exit_with(1)\n    end\n\n    it \"should work if asked to render json\" do\n      allow(app.command_line).to receive(:args).and_return(%w{count_args a b c --render-as json})\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/3/).to_stdout\n    end\n\n    it \"should invoke when_rendering hook 's' when asked to render-as 's'\" do\n      allow(app.command_line).to receive(:args).and_return(%w{with_s_rendering_hook --render-as s})\n      app.action = app.face.get_action(:with_s_rendering_hook)\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(/you invoked the 's' rendering hook/).to_stdout\n    end\n  end\n\n  describe \"#help\" do\n    it \"should generate help for --help\" do\n      allow(app.command_line).to receive(:args).and_return(%w{--help})\n      expect(Puppet::Face[:help, :current]).to receive(:help)\n      expect { app.run }.to exit_with(0)\n    end\n\n    it \"should generate help for -h\" do\n      allow(app.command_line).to receive(:args).and_return(%w{-h})\n      expect(Puppet::Face[:help, :current]).to receive(:help)\n      expect { app.run }.to exit_with(0)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/facts_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/application/facts'\n\ndescribe Puppet::Application::Facts do\n  let(:app) { Puppet::Application[:facts] }\n  let(:values) { {\"filesystems\" => \"apfs,autofs,devfs\", \"macaddress\" => \"64:52:11:22:03:2e\"} }\n\n  before :each do\n    Puppet::Node::Facts.indirection.terminus_class = :memory\n  end\n\n  it \"returns facts for a given node\" do\n    facts = Puppet::Node::Facts.new('whatever', values)\n    Puppet::Node::Facts.indirection.save(facts)\n\n    app.command_line.args = %w{find whatever --render-as yaml}\n\n    # due to PUP-10105 we emit the class tag when we shouldn't\n    expected = Regexp.new(<<~END)\n      --- !ruby/object:Puppet::Node::Facts\n      name: whatever\n      values:\n        filesystems: apfs,autofs,devfs\n        macaddress: \"64:52:11:22:03:2e\"\n    END\n\n    expect {\n      app.run\n    }.to exit_with(0)\n     .and output(expected).to_stdout\n  end\n\n  it \"returns facts for the current node when the name is omitted\" do\n    facts = Puppet::Node::Facts.new(Puppet[:certname], values)\n    Puppet::Node::Facts.indirection.save(facts)\n\n    app.command_line.args = %w{find --render-as yaml}\n\n    # due to PUP-10105 we emit the class tag when we shouldn't\n    expected = Regexp.new(<<~END)\n      --- !ruby/object:Puppet::Node::Facts\n      name: #{Puppet[:certname]}\n      values:\n        filesystems: apfs,autofs,devfs\n        macaddress: \"64:52:11:22:03:2e\"\n    END\n\n    expect {\n      app.run\n    }.to exit_with(0)\n     .and output(expected).to_stdout\n  end\n\n  context 'when show action is called' do\n    let(:expected) { <<~END }\n      {\n        \"filesystems\": \"apfs,autofs,devfs\",\n        \"macaddress\": \"64:52:11:22:03:2e\"\n      }\n    END\n\n    before :each do\n      Puppet::Node::Facts.indirection.terminus_class = :facter\n      allow(Facter).to receive(:resolve).and_return(values)\n      app.command_line.args = %w{show}\n    end\n\n    it 'correctly displays facts with default formatting' do\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(expected).to_stdout\n    end\n\n    it 'displays a single fact value' do\n      app.command_line.args << 'filesystems' << '--value-only'\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(\"apfs,autofs,devfs\\n\").to_stdout\n    end\n\n    it \"warns and ignores value-only when multiple fact names are specified\" do\n      app.command_line.args << 'filesystems' << 'macaddress' << '--value-only'\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(expected).to_stdout\n       .and output(/it can only be used when querying for a single fact/).to_stderr\n    end\n\n    {\n      \"type_hash\" => [{'a' => 2}, \"{\\n  \\\"a\\\": 2\\n}\"],\n      \"type_empty_hash\" => [{}, \"{\\n}\"],\n      \"type_array\" => [[], \"[\\n\\n]\"],\n      \"type_string\" => [\"str\", \"str\"],\n      \"type_int\" => [1, \"1\"],\n      \"type_float\" => [1.0, \"1.0\"],\n      \"type_true\" => [true, \"true\"],\n      \"type_false\" => [false, \"false\"],\n      \"type_nil\" => [nil, \"\"],\n      \"type_sym\" => [:sym, \"sym\"]\n    }.each_pair do |name, values|\n      it \"renders '#{name}' as '#{values.last}'\" do\n        fact_value = values.first\n        fact_output = values.last\n\n        allow(Facter).to receive(:resolve).and_return({name => fact_value})\n\n        app.command_line.args << name << '--value-only'\n        expect {\n          app.run\n        }.to exit_with(0)\n         .and output(\"#{fact_output}\\n\").to_stdout\n      end\n    end\n  end\n\n  context 'when default action is called' do\n    let(:expected) { <<~END }\n      ---\n      filesystems: apfs,autofs,devfs\n      macaddress: 64:52:11:22:03:2e\n    END\n\n    before :each do\n      Puppet::Node::Facts.indirection.terminus_class = :facter\n      allow(Facter).to receive(:resolve).and_return(values)\n      app.command_line.args = %w{--render-as yaml}\n    end\n\n    it 'calls show action' do\n      expect {\n        app.run\n      }.to exit_with(0)\n       .and output(expected).to_stdout\n      expect(app.action.name).to eq(:show)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/filebucket_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/application/filebucket'\nrequire 'puppet/file_bucket/dipper'\n\ndescribe Puppet::Application::Filebucket do\n  before :each do\n    @filebucket = Puppet::Application[:filebucket]\n  end\n\n  it \"should declare a get command\" do\n    expect(@filebucket).to respond_to(:get)\n  end\n\n  it \"should declare a backup command\" do\n    expect(@filebucket).to respond_to(:backup)\n  end\n\n  it \"should declare a restore command\" do\n    expect(@filebucket).to respond_to(:restore)\n  end\n\n  it \"should declare a diff command\" do\n    expect(@filebucket).to respond_to(:diff)\n  end\n\n  it \"should declare a list command\" do\n    expect(@filebucket).to respond_to(:list)\n  end\n\n  [:bucket, :debug, :local, :remote, :verbose, :fromdate, :todate].each do |option|\n    it \"should declare handle_#{option} method\" do\n      expect(@filebucket).to respond_to(\"handle_#{option}\".to_sym)\n    end\n\n    it \"should store argument value when calling handle_#{option}\" do\n      expect(@filebucket.options).to receive(:[]=).with(\"#{option}\".to_sym, 'arg')\n      @filebucket.send(\"handle_#{option}\".to_sym, 'arg')\n    end\n  end\n\n  describe \"during setup\" do\n    before :each do\n      allow(Puppet::Log).to receive(:newdestination)\n      allow(Puppet::FileBucket::Dipper).to receive(:new)\n      allow(@filebucket.options).to receive(:[])\n    end\n\n    it \"should set console as the log destination\" do\n      expect(Puppet::Log).to receive(:newdestination).with(:console)\n\n      @filebucket.setup\n    end\n\n    it \"should trap INT\" do\n      expect(Signal).to receive(:trap).with(:INT)\n\n      @filebucket.setup\n    end\n\n    it \"should set log level to debug if --debug was passed\" do\n      allow(@filebucket.options).to receive(:[]).with(:debug).and_return(true)\n      @filebucket.setup\n      expect(Puppet::Log.level).to eq(:debug)\n    end\n\n    it \"should set log level to info if --verbose was passed\" do\n      allow(@filebucket.options).to receive(:[]).with(:verbose).and_return(true)\n      @filebucket.setup\n      expect(Puppet::Log.level).to eq(:info)\n    end\n\n    it \"should print puppet config if asked to in Puppet config\" do\n      allow(Puppet.settings).to receive(:print_configs?).and_return(true)\n      expect(Puppet.settings).to receive(:print_configs).and_return(true)\n      expect { @filebucket.setup }.to exit_with 0\n    end\n\n    it \"should exit after printing puppet config if asked to in Puppet config\" do\n      allow(Puppet.settings).to receive(:print_configs?).and_return(true)\n      expect { @filebucket.setup }.to exit_with 1\n    end\n\n    describe \"with local bucket\" do\n      let(:path) { File.expand_path(\"path\") }\n\n      before :each do\n        allow(@filebucket.options).to receive(:[]).with(:local).and_return(true)\n      end\n\n      it \"should create a client with the default bucket if none passed\" do\n        Puppet[:clientbucketdir] = path\n        Puppet[:bucketdir] = path + \"2\"\n\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with(hash_including(Path: path))\n\n        @filebucket.setup\n      end\n\n      it \"should create a local Dipper with the given bucket\" do\n        allow(@filebucket.options).to receive(:[]).with(:bucket).and_return(path)\n\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with(hash_including(Path: path))\n\n        @filebucket.setup\n      end\n    end\n\n    describe \"with remote bucket\" do\n      it \"should create a remote Client to the configured server\" do\n        Puppet[:server] = \"puppet.reductivelabs.com\"\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with(hash_including(Server: \"puppet.reductivelabs.com\"))\n        @filebucket.setup\n      end\n\n      it \"should default to the first good server_list entry if server_list is set\" do\n        stub_request(:get, \"https://foo:8140/status/v1/simple/server\").to_return(status: 200)\n        Puppet[:server_list] = \"foo,bar,baz\"\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with(hash_including(Server: \"foo\"))\n        @filebucket.setup\n      end\n\n      it \"should walk server_list until it finds a good entry\" do\n        stub_request(:get, \"https://foo:8140/status/v1/simple/server\").to_return(status: 502)\n        stub_request(:get, \"https://bar:8140/status/v1/simple/server\").to_return(status: 200)\n        Puppet[:server_list] = \"foo,bar,baz\"\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with(hash_including(Server: \"bar\"))\n        @filebucket.setup\n      end\n\n      # FileBucket catches any exceptions raised, logs them, then just exits\n      it \"raises an error if there are no functional servers in server_list\" do\n        stub_request(:get, \"https://foo:8140/status/v1/simple/server\").to_return(status: 404)\n        stub_request(:get, \"https://bar:8140/status/v1/simple/server\").to_return(status: 404)\n        stub_request(:get, \"https://foo:8140/status/v1/simple/master\").to_return(status: 404)\n        stub_request(:get, \"https://bar:8140/status/v1/simple/master\").to_return(status: 404)\n        Puppet[:server] = 'horacio'\n        Puppet[:server_list] = \"foo,bar\"\n\n        expect{@filebucket.setup}.to exit_with(1)\n      end\n\n      it \"should fall back to server if server_list is empty\" do\n        Puppet[:server_list] = \"\"\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with(hash_including(Server: \"puppet\"))\n        @filebucket.setup\n      end\n\n      it \"should take both the server and port specified in server_list\" do\n        stub_request(:get, \"https://foo:632/status/v1/simple/server\").to_return(status: 200)\n        Puppet[:server_list] = \"foo:632,bar:6215,baz:351\"\n        expect(Puppet::FileBucket::Dipper).to receive(:new).with({ :Server => \"foo\", :Port => 632 })\n        @filebucket.setup\n      end\n    end\n  end\n\n  describe \"when running\" do\n    before :each do\n      allow(Puppet::Log).to receive(:newdestination)\n      allow(Puppet::FileBucket::Dipper).to receive(:new)\n      allow(@filebucket.options).to receive(:[])\n\n      @client = double('client')\n      allow(Puppet::FileBucket::Dipper).to receive(:new).and_return(@client)\n\n      @filebucket.setup\n    end\n\n    it \"should use the first non-option parameter as the dispatch\" do\n      allow(@filebucket.command_line).to receive(:args).and_return(['get'])\n\n      expect(@filebucket).to receive(:get)\n\n      @filebucket.run_command\n    end\n\n    describe \"the command get\" do\n      before :each do\n        allow(@filebucket).to receive(:print)\n        allow(@filebucket).to receive(:args).and_return([])\n      end\n\n      it \"should call the client getfile method\" do\n        expect(@client).to receive(:getfile)\n\n        @filebucket.get\n      end\n\n      it \"should call the client getfile method with the given digest\" do\n        digest = 'DEADBEEF'\n        allow(@filebucket).to receive(:args).and_return([digest])\n\n        expect(@client).to receive(:getfile).with(digest)\n\n        @filebucket.get\n      end\n\n      it \"should print the file content\" do\n        allow(@client).to receive(:getfile).and_return(\"content\")\n\n        expect(@filebucket).to receive(:print).and_return(\"content\")\n\n        @filebucket.get\n      end\n    end\n\n    describe \"the command backup\" do\n      it \"should fail if no arguments are specified\" do\n        allow(@filebucket).to receive(:args).and_return([])\n        expect { @filebucket.backup }.to raise_error(RuntimeError, /You must specify a file to back up/)\n      end\n\n      it \"should call the client backup method for each given parameter\" do\n        allow(@filebucket).to receive(:puts)\n        allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n        allow(FileTest).to receive(:readable?).and_return(true)\n        allow(@filebucket).to receive(:args).and_return([\"file1\", \"file2\"])\n\n        expect(@client).to receive(:backup).with(\"file1\")\n        expect(@client).to receive(:backup).with(\"file2\")\n\n        @filebucket.backup\n      end\n    end\n\n    describe \"the command restore\" do\n      it \"should call the client getfile method with the given digest\" do\n        digest = 'DEADBEEF'\n        file = 'testfile'\n        allow(@filebucket).to receive(:args).and_return([file, digest])\n\n        expect(@client).to receive(:restore).with(file, digest)\n\n        @filebucket.restore\n      end\n    end\n\n    describe \"the command diff\" do\n      it \"should call the client diff method with 2 given checksums\" do\n        digest_a = 'DEADBEEF'\n        digest_b = 'BEEF'\n        allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n        allow(@filebucket).to receive(:args).and_return([digest_a, digest_b])\n\n        expect(@client).to receive(:diff).with(digest_a, digest_b, nil, nil)\n\n        @filebucket.diff\n      end\n\n      it \"should call the client diff with a path if the second argument is a file\" do\n        digest_a = 'DEADBEEF'\n        digest_b = 'BEEF'\n        allow(Puppet::FileSystem).to receive(:exist?).with(digest_a).and_return(false)\n        allow(Puppet::FileSystem).to receive(:exist?).with(digest_b).and_return(true)\n        allow(@filebucket).to receive(:args).and_return([digest_a, digest_b])\n\n        expect(@client).to receive(:diff).with(digest_a, nil, nil, digest_b)\n\n        @filebucket.diff\n      end\n\n      it \"should call the client diff with a path if the first argument is a file\" do\n        digest_a = 'DEADBEEF'\n        digest_b = 'BEEF'\n        allow(Puppet::FileSystem).to receive(:exist?).with(digest_a).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(digest_b).and_return(false)\n        allow(@filebucket).to receive(:args).and_return([digest_a, digest_b])\n\n        expect(@client).to receive(:diff).with(nil, digest_b, digest_a, nil)\n\n        @filebucket.diff\n      end\n\n      it \"should call the clien diff with paths if the both arguments are files\" do\n        digest_a = 'DEADBEEF'\n        digest_b = 'BEEF'\n        allow(Puppet::FileSystem).to receive(:exist?).with(digest_a).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(digest_b).and_return(true)\n        allow(@filebucket).to receive(:args).and_return([digest_a, digest_b])\n\n        expect(@client).to receive(:diff).with(nil, nil, digest_a, digest_b)\n\n        @filebucket.diff\n      end\n\n      it \"should fail if only one checksum is given\" do\n        digest_a = 'DEADBEEF'\n        allow(@filebucket).to receive(:args).and_return([digest_a])\n\n        expect { @filebucket.diff }.to raise_error Puppet::Error\n      end\n    end\n\n    describe \"the command list\" do\n      it \"should call the client list method with nil dates\" do\n        expect(@client).to receive(:list).with(nil, nil)\n\n        @filebucket.list\n      end\n\n      it \"should call the client list method with the given dates\" do\n        # 3 Hours ago\n        threehours = 60*60*3\n        fromdate = (Time.now - threehours).strftime(\"%F %T\")\n        # 1 Hour ago\n        onehour = 60*60\n        todate = (Time.now - onehour).strftime(\"%F %T\")\n\n        allow(@filebucket.options).to receive(:[]).with(:fromdate).and_return(fromdate)\n        allow(@filebucket.options).to receive(:[]).with(:todate).and_return(todate)\n\n        expect(@client).to receive(:list).with(fromdate, todate)\n\n        @filebucket.list\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/indirection_base_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/command_line'\nrequire 'puppet/application/indirection_base'\nrequire 'puppet/indirector/face'\n\n########################################################################\n# Stub for testing; the names are critical, sadly. --daniel 2011-03-30\nclass Puppet::Application::TestIndirection < Puppet::Application::IndirectionBase\nend\n########################################################################\n\ndescribe Puppet::Application::IndirectionBase do\n  before :all do\n    @face = Puppet::Indirector::Face.define(:test_indirection, '0.0.1') do\n      summary \"fake summary\"\n      copyright \"Puppet Labs\", 2011\n      license   \"Apache 2 license; see COPYING\"\n    end\n    # REVISIT: This horror is required because we don't allow anything to be\n    # :current except for if it lives on, and is loaded from, disk. --daniel 2011-03-29\n    @face.instance_variable_set('@version', :current)\n\n    Puppet::Face.register(@face)\n  end\n\n  after :all do\n    # Delete the face so that it doesn't interfere with other specs\n    Puppet::Interface::FaceCollection.instance_variable_get(:@faces).delete Puppet::Interface::FaceCollection.underscorize(@face.name)\n  end\n\n  it \"should accept a terminus command line option\" do\n    # It would be nice not to have to stub this, but whatever... writing an\n    # entire indirection stack would cause us more grief. --daniel 2011-03-31\n    terminus = double(\"test indirection terminus\")\n    allow(terminus).to receive(:name).and_return(:test_indirection)\n    allow(terminus).to receive(:terminus_class=)\n    allow(terminus).to receive(:save)\n    allow(terminus).to receive(:reset_terminus_class)\n\n    expect(Puppet::Indirector::Indirection).to receive(:instance).\n      with(:test_indirection).and_return(terminus)\n\n    command_line = Puppet::Util::CommandLine.new(\"puppet\", %w{test_indirection --terminus foo save bar})\n    application = Puppet::Application::TestIndirection.new(command_line)\n\n    expect {\n      application.run\n    }.to exit_with 0\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/lookup_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/application/lookup'\nrequire 'puppet/pops/lookup'\n\ndescribe Puppet::Application::Lookup do\n\n  def run_lookup(lookup)\n    capture = StringIO.new\n    saved_stdout = $stdout\n    begin\n      $stdout = capture\n      expect { lookup.run_command }.to exit_with(0)\n    ensure\n      $stdout = saved_stdout\n    end\n    # Drop end of line and an optional yaml end of document\n    capture.string.gsub(/\\n(\\.\\.\\.\\n)?\\Z/m, '')\n  end\n\n  context \"when running with incorrect command line options\" do\n    let (:lookup) { Puppet::Application[:lookup] }\n\n    it \"errors if no keys are given via the command line\" do\n      lookup.options[:node] = 'dantooine.local'\n      expected_error = \"No keys were given to lookup.\"\n\n      expect { lookup.run_command }.to raise_error(RuntimeError, expected_error)\n    end\n\n    it \"does not allow invalid arguments for '--merge'\" do\n      lookup.options[:node] = 'dantooine.local'\n      lookup.options[:merge] = 'something_bad'\n      allow(lookup.command_line).to receive(:args).and_return(['atton', 'kreia'])\n\n      expected_error = \"The --merge option only accepts 'first', 'hash', 'unique', or 'deep'\\nRun 'puppet lookup --help' for more details\"\n\n      expect { lookup.run_command }.to raise_error(RuntimeError, expected_error)\n    end\n\n    it \"does not allow deep merge options if '--merge' was not set to deep\" do\n      lookup.options[:node] = 'dantooine.local'\n      lookup.options[:merge_hash_arrays] = true\n      lookup.options[:merge] = 'hash'\n      allow(lookup.command_line).to receive(:args).and_return(['atton', 'kreia'])\n\n      expected_error = \"The options --knock-out-prefix, --sort-merged-arrays, and --merge-hash-arrays are only available with '--merge deep'\\nRun 'puppet lookup --help' for more details\"\n\n      expect { lookup.run_command }.to raise_error(RuntimeError, expected_error)\n    end\n  end\n\n  context \"when running with correct command line options\" do\n    let (:lookup) { Puppet::Application[:lookup] }\n\n    it \"calls the lookup method with the correct arguments\" do\n      lookup.options[:node] = 'dantooine.local'\n      lookup.options[:render_as] = :s;\n      lookup.options[:merge_hash_arrays] = true\n      lookup.options[:merge] = 'deep'\n      allow(lookup.command_line).to receive(:args).and_return(['atton', 'kreia'])\n      allow(lookup).to receive(:generate_scope).and_yield('scope')\n\n      expected_merge = { \"strategy\" => \"deep\", \"sort_merged_arrays\" => false, \"merge_hash_arrays\" => true }\n\n      expect(Puppet::Pops::Lookup).to receive(:lookup).with(['atton', 'kreia'], nil, nil, false, expected_merge, anything).and_return('rand')\n\n      expect(run_lookup(lookup)).to eql(\"rand\")\n    end\n\n    %w(first unique hash deep).each do |opt|\n\n      it \"accepts --merge #{opt}\" do\n        lookup.options[:node] = 'dantooine.local'\n        lookup.options[:merge] = opt\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['atton', 'kreia'])\n        allow(lookup).to receive(:generate_scope).and_yield('scope')\n        allow(Puppet::Pops::Lookup).to receive(:lookup).and_return('rand')\n        expect(run_lookup(lookup)).to eql(\"rand\")\n      end\n    end\n\n    it \"prints the value found by lookup\" do\n      lookup.options[:node] = 'dantooine.local'\n      lookup.options[:render_as] = :s\n      allow(lookup.command_line).to receive(:args).and_return(['atton', 'kreia'])\n      allow(lookup).to receive(:generate_scope).and_yield('scope')\n\n      allow(Puppet::Pops::Lookup).to receive(:lookup).and_return('rand')\n\n      expect(run_lookup(lookup)).to eql(\"rand\")\n    end\n  end\n\n  context 'when given a valid configuration' do\n    let (:lookup) { Puppet::Application[:lookup] }\n\n    # There is a fully configured 'sample' environment in fixtures at this location\n    let(:environmentpath) { File.absolute_path(File.join(my_fixture_dir(), '../environments')) }\n\n    let(:facts) { Puppet::Node::Facts.new(\"facts\", {}) }\n\n    let(:node) { Puppet::Node.new(\"testnode\", :facts => facts, :environment => 'production') }\n\n    let(:expected_json_hash) {\n      {\n        'branches' =>\n          [\n            {\n              'branches'=>\n                [\n                  {\n                    'key'=>'lookup_options',\n                    'event'=>'not_found',\n                    'type'=>'data_provider',\n                    'name'=>'Global Data Provider (hiera configuration version 5)'\n                  },\n                  {\n                    'branches'=>\n                      [\n                        {\n                          'branches'=>\n                            [\n                              {\n                                'key' => 'lookup_options',\n                                'value' => {'a'=>'first'},\n                                'event'=>'found',\n                                'type'=>'path',\n                                'original_path'=>'common.yaml',\n                                'path'=>\"#{environmentpath}/production/data/common.yaml\"\n                              }\n                            ],\n                          'type'=>'data_provider',\n                          'name'=>'Hierarchy entry \"Common\"'\n                        }\n                      ],\n                    'type'=>'data_provider',\n                    'name'=>'Environment Data Provider (hiera configuration version 5)'\n                  }\n                ],\n              'key'=>'lookup_options',\n              'type'=>'root'\n            },\n            {\n              'branches'=>\n                [\n                  {\n                    'key'=>'a',\n                    'event'=>'not_found',\n                    'type'=>'data_provider',\n                    'name'=>'Global Data Provider (hiera configuration version 5)'\n                  },\n                  {\n                    'branches'=>\n                      [\n                        {\n                          'branches'=>\n                            [\n                              {\n                                'key'=>'a',\n                                'value'=>'This is A',\n                                'event'=>'found',\n                                'type'=>'path',\n                                'original_path'=>'common.yaml',\n                                'path'=>\"#{environmentpath}/production/data/common.yaml\"\n                              }\n                            ],\n                          'type'=>'data_provider',\n                          'name'=>'Hierarchy entry \"Common\"'\n                        }\n                      ],\n                    'type'=>'data_provider',\n                    'name'=>'Environment Data Provider (hiera configuration version 5)'\n                  }\n                ],\n              'key'=>'a',\n              'type'=>'root'\n            }\n          ]\n      }\n    }\n\n    let(:expected_yaml_hash) {\n      {\n      :branches =>\n        [\n          {\n            :branches=>\n              [\n                {\n                  :key=>'lookup_options',\n                  :event=>:not_found,\n                  :type=>:data_provider,\n                  :name=>'Global Data Provider (hiera configuration version 5)'\n                },\n                {\n                  :branches=>\n                    [\n                      {\n                        :branches=>\n                          [\n                            {\n                              :key => 'lookup_options',\n                              :value => {'a'=>'first'},\n                              :event=>:found,\n                              :type=>:path,\n                              :original_path=>'common.yaml',\n                              :path=>\"#{environmentpath}/production/data/common.yaml\"\n                            }\n                          ],\n                        :type=>:data_provider,\n                        :name=>'Hierarchy entry \"Common\"'\n                      }\n                  ],\n                  :type=>:data_provider,\n                  :name=>'Environment Data Provider (hiera configuration version 5)'\n                }\n              ],\n            :key=>'lookup_options',\n            :type=>:root\n          },\n          {\n            :branches=>\n              [\n                {\n                  :key=>'a',\n                  :event=>:not_found,\n                  :type=>:data_provider,\n                  :name=>'Global Data Provider (hiera configuration version 5)'\n                },\n                {\n                  :branches=>\n                    [\n                      {\n                        :branches=>\n                        [\n                          {\n                            :key=>'a',\n                            :value=>'This is A',\n                            :event=>:found,\n                            :type=>:path,\n                            :original_path=>'common.yaml',\n                            :path=>\"#{environmentpath}/production/data/common.yaml\"\n                          }\n                        ],\n                        :type=>:data_provider,\n                        :name=>'Hierarchy entry \"Common\"'\n                      }\n                    ],\n                  :type=>:data_provider,\n                  :name=>'Environment Data Provider (hiera configuration version 5)'\n                }\n              ],\n            :key=>'a',\n            :type=>:root\n          }\n        ]\n      }\n    }\n\n    around(:each) do |example|\n      # Initialize settings to get a full compile as close as possible to a real\n      # environment load\n      Puppet.settings.initialize_global_settings\n      loader = Puppet::Environments::Directories.new(environmentpath, [])\n      Puppet.override(:environments => loader) do\n        example.run\n      end\n    end\n\n    it '--explain produces human readable text by default and does not produce output to debug logger' do\n      lookup.options[:node] = node\n      lookup.options[:explain] = true\n      allow(lookup.command_line).to receive(:args).and_return(['a'])\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(run_lookup(lookup)).to eql(<<-EXPLANATION.chomp)\nSearching for \"lookup_options\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"a\" => \"first\"\n        }\nSearching for \"a\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"a\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"a\" value: \"This is A\"\n        EXPLANATION\n      end\n      expect(logs.any? { |log| log.level == :debug }).to be_falsey\n    end\n\n    it '--debug using multiple interpolation functions produces output to the logger' do\n      lookup.options[:node] = node\n      allow(lookup.command_line).to receive(:args).and_return(['ab'])\n      Puppet.debug = true\n      logs = []\n      begin\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect { lookup.run_command }.to output(<<-VALUE.unindent).to_stdout\n            --- This is A and This is B\n            ...\n          VALUE\n        end\n      rescue SystemExit => e\n        expect(e.status).to eq(0)\n      end\n      logs = logs.select { |log| log.level == :debug }.map { |log| log.message }\n      expect(logs).to include(/Found key: \"ab\" value: \"This is A and This is B\"/)\n    end\n\n    it '--explain produces human readable text by default and --debug produces the same output to debug logger' do\n      lookup.options[:node] = node\n      lookup.options[:explain] = true\n      allow(lookup.command_line).to receive(:args).and_return(['a'])\n      Puppet.debug = true\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(run_lookup(lookup)).to eql(<<-EXPLANATION.chomp)\nSearching for \"lookup_options\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"a\" => \"first\"\n        }\nSearching for \"a\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"a\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"a\" value: \"This is A\"\n        EXPLANATION\n      end\n      logs = logs.select { |log| log.level == :debug }.map { |log| log.message }\n      expect(logs).to include(<<-EXPLANATION.chomp)\nLookup of 'a'\n  Searching for \"lookup_options\"\n    Global Data Provider (hiera configuration version 5)\n      No such key: \"lookup_options\"\n    Environment Data Provider (hiera configuration version 5)\n      Hierarchy entry \"Common\"\n        Path \"#{environmentpath}/production/data/common.yaml\"\n          Original path: \"common.yaml\"\n          Found key: \"lookup_options\" value: {\n            \"a\" => \"first\"\n          }\n  Searching for \"a\"\n    Global Data Provider (hiera configuration version 5)\n      No such key: \"a\"\n    Environment Data Provider (hiera configuration version 5)\n      Hierarchy entry \"Common\"\n        Path \"#{environmentpath}/production/data/common.yaml\"\n          Original path: \"common.yaml\"\n          Found key: \"a\" value: \"This is A\"\n      EXPLANATION\n    end\n\n    it '--explain-options produces human readable text of a hash merge' do\n      lookup.options[:node] = node\n      lookup.options[:explain_options] = true\n      expect(run_lookup(lookup)).to eql(<<-EXPLANATION.chomp)\nMerge strategy hash\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"a\" => \"first\"\n        }\n  Merged result: {\n    \"a\" => \"first\"\n  }\n      EXPLANATION\n    end\n\n    it '--explain-options produces human readable text of a hash merge and --debug produces the same output to debug logger' do\n      lookup.options[:node] = node\n      lookup.options[:explain_options] = true\n      Puppet.debug = true\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(run_lookup(lookup)).to eql(<<-EXPLANATION.chomp)\nMerge strategy hash\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"a\" => \"first\"\n        }\n  Merged result: {\n    \"a\" => \"first\"\n  }\n          EXPLANATION\n      logs = logs.select { |log| log.level == :debug }.map { |log| log.message }\n      expect(logs).to include(<<-EXPLANATION.chomp)\nLookup of '__global__'\n  Merge strategy hash\n    Global Data Provider (hiera configuration version 5)\n      No such key: \"lookup_options\"\n    Environment Data Provider (hiera configuration version 5)\n      Hierarchy entry \"Common\"\n        Path \"#{environmentpath}/production/data/common.yaml\"\n          Original path: \"common.yaml\"\n          Found key: \"lookup_options\" value: {\n            \"a\" => \"first\"\n          }\n    Merged result: {\n      \"a\" => \"first\"\n    }\n        EXPLANATION\n      end\n    end\n\n    it '--explain produces human readable text of a hash merge when using both --explain and --explain-options' do\n      lookup.options[:node] = node\n      lookup.options[:explain] = true\n      lookup.options[:explain_options] = true\n      allow(lookup.command_line).to receive(:args).and_return(['a'])\n      expect(run_lookup(lookup)).to eql(<<-EXPLANATION.chomp)\nSearching for \"lookup_options\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"a\" => \"first\"\n        }\nSearching for \"a\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"a\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/production/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"a\" value: \"This is A\"\n      EXPLANATION\n    end\n\n    it 'can produce a yaml explanation' do\n      lookup.options[:node] = node\n      lookup.options[:explain] = true\n      lookup.options[:render_as] = :yaml\n      allow(lookup.command_line).to receive(:args).and_return(['a'])\n      output = run_lookup(lookup)\n      expect(Puppet::Util::Yaml.safe_load(output, [Symbol])).to eq(expected_yaml_hash)\n    end\n\n    it 'can produce a json explanation' do\n      lookup.options[:node] = node\n      lookup.options[:explain] = true\n      lookup.options[:render_as] = :json\n      allow(lookup.command_line).to receive(:args).and_return(['a'])\n      output = run_lookup(lookup)\n      expect(JSON.parse(output)).to eq(expected_json_hash)\n    end\n\n    it 'can access values using dotted keys' do\n      lookup.options[:node] = node\n      lookup.options[:render_as] = :json\n      allow(lookup.command_line).to receive(:args).and_return(['d.one.two.three'])\n      output = run_lookup(lookup)\n      expect(JSON.parse(\"[#{output}]\")).to eq(['the value'])\n    end\n\n    it 'can access values using quoted dotted keys' do\n      lookup.options[:node] = node\n      lookup.options[:render_as] = :json\n      allow(lookup.command_line).to receive(:args).and_return(['\"e.one.two.three\"'])\n      output = run_lookup(lookup)\n      expect(JSON.parse(\"[#{output}]\")).to eq(['the value'])\n    end\n\n    it 'can access values using mix of dotted keys and quoted dotted keys' do\n      lookup.options[:node] = node\n      lookup.options[:render_as] = :json\n      allow(lookup.command_line).to receive(:args).and_return(['\"f.one\".\"two.three\".1'])\n      output = run_lookup(lookup)\n      expect(JSON.parse(\"[#{output}]\")).to eq(['second value'])\n    end\n\n    context 'the global scope' do\n      include PuppetSpec::Files\n\n      it \"is unaffected by global variables unless '--compile' is used\" do\n        # strict mode is off so behavior this test is trying to check isn't stubbed out\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :warning\n        lookup.options[:node] = node\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['c'])\n        expect(run_lookup(lookup)).to eql(\"This is\")\n      end\n\n      it \"is affected by global variables when '--compile' is used\" do\n        lookup.options[:node] = node\n        lookup.options[:compile] = true\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['c'])\n        expect(run_lookup(lookup)).to eql(\"This is C from site.pp\")\n      end\n\n      it 'receives extra facts in top scope' do\n        file_path = file_containing('facts.yaml', <<~CONTENT)\n          ---\n          cx: ' C from facts'\n        CONTENT\n\n        lookup.options[:node] = node\n        lookup.options[:fact_file] = file_path\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['c'])\n        expect(run_lookup(lookup)).to eql(\"This is C from facts\")\n      end\n\n      it 'receives extra facts in the facts hash' do\n        file_path = file_containing('facts.yaml', <<~CONTENT)\n          ---\n          cx: ' G from facts'\n        CONTENT\n\n        lookup.options[:node] = node\n        lookup.options[:fact_file] = file_path\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['g'])\n        expect(run_lookup(lookup)).to eql(\"This is G from facts in facts hash\")\n      end\n\n      it 'looks up server facts' do\n        lookup.options[:node] = node\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['h'])\n        expect(run_lookup(lookup)).to eql(\"server version is #{Puppet.version}\")\n      end\n\n      describe 'when retrieving given facts' do\n        before do\n          lookup.options[:node] = node\n          allow(lookup.command_line).to receive(:args).and_return(['g'])\n        end\n\n        it 'loads files with yaml extension as yaml on first try' do\n          file_path = file_containing('facts.yaml', <<~CONTENT)\n            ---\n            cx: ' G from facts'\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect(Puppet::Util::Yaml).to receive(:safe_load_file).with(file_path, []).and_call_original\n          run_lookup(lookup)\n        end\n\n        it 'loads files with yml extension as yaml on first try' do\n          file_path = file_containing('facts.yml', <<~CONTENT)\n            ---\n            cx: ' G from facts'\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect(Puppet::Util::Yaml).to receive(:safe_load_file).with(file_path, []).and_call_original\n          run_lookup(lookup)\n        end\n\n        it 'loads files with json extension as json on first try' do\n          file_path = file_containing('facts.json', <<~CONTENT)\n            {\n              \"cx\": \" G from facts\"\n            }\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect(Puppet::Util::Json).to receive(:load_file).with(file_path, {}).and_call_original\n          run_lookup(lookup)\n        end\n\n        it 'detects file format accordingly even with random file extension' do\n          file_path = file_containing('facts.txt', <<~CONTENT)\n            {\n              \"cx\": \" G from facts\"\n            }\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect(Puppet::Util::Json).to receive(:load_file_if_valid).with(file_path).and_call_original\n          expect(Puppet::Util::Yaml).not_to receive(:safe_load_file_if_valid).with(file_path)\n          run_lookup(lookup)\n        end\n\n        it 'detects file without extension as json due to valid contents' do\n          file_path = file_containing('facts', <<~CONTENT)\n            {\n              \"cx\": \" G from facts\"\n            }\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect(Puppet::Util::Json).to receive(:load_file_if_valid).with(file_path).and_call_original\n          expect(Puppet::Util::Yaml).not_to receive(:safe_load_file_if_valid).with(file_path)\n          run_lookup(lookup)\n        end\n\n        it 'detects file without extension as yaml due to valid contents' do\n          file_path = file_containing('facts', <<~CONTENT)\n            ---\n            cx: ' G from facts'\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect(Puppet::Util::Json.load_file_if_valid(file_path)).to be_nil\n          expect(Puppet::Util::Yaml).to receive(:safe_load_file_if_valid).with(file_path).and_call_original\n          run_lookup(lookup)\n        end\n\n        it 'raises error due to invalid contents' do\n          file_path = file_containing('facts.yml', <<~CONTENT)\n            INVALID CONTENT\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect {\n            lookup.run_command\n          }.to raise_error(/Incorrectly formatted data in .+ given via the --facts flag \\(only accepts yaml and json files\\)/)\n        end\n\n        it \"fails when a node doesn't have facts\" do\n          lookup.options[:node] = \"bad.node\"\n          allow(lookup.command_line).to receive(:args).and_return(['c'])\n\n          expected_error = \"No facts available for target node: #{lookup.options[:node]}\"\n          expect { lookup.run_command }.to raise_error(RuntimeError, expected_error)\n        end\n\n        it 'raises error due to missing trusted information facts in --facts file' do\n          file_path = file_containing('facts.yaml', <<~CONTENT)\n            ---\n            fqdn: some.fqdn.com\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect {\n            lookup.run_command\n          }.to raise_error(/When overriding any of the hostname,domain,fqdn,clientcert facts with #{file_path} given via the --facts flag, they must all be overridden./)\n        end\n\n        it 'does not fail when all trusted information facts are provided via --facts file' do\n          file_path = file_containing('facts.yaml', <<~CONTENT)\n            ---\n            fqdn: some.fqdn.com\n            hostname: some.hostname\n            domain: some.domain\n            clientcert: some.clientcert\n          CONTENT\n          lookup.options[:fact_file] = file_path\n\n          expect {\n            lookup.run_command\n          }.to exit_with(0)\n           .and output(/This is in facts hash/).to_stdout\n        end\n      end\n    end\n\n    context 'using a puppet function as data provider' do\n      let(:node) { Puppet::Node.new(\"testnode\", :facts => facts, :environment => 'puppet_func_provider') }\n\n      it \"works OK in the absense of '--compile'\" do\n        # strict mode is off so behavior this test is trying to check isn't stubbed out\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :warning\n        lookup.options[:node] = node\n        allow(lookup.command_line).to receive(:args).and_return(['c'])\n        lookup.options[:render_as] = :s\n        expect(run_lookup(lookup)).to eql(\"This is C from data.pp\")\n      end\n\n      it \"global scope is affected by global variables when '--compile' is used\" do\n        lookup.options[:node] = node\n        lookup.options[:compile] = true\n        lookup.options[:render_as] = :s\n        allow(lookup.command_line).to receive(:args).and_return(['c'])\n        expect(run_lookup(lookup)).to eql(\"This is C from site.pp\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/resource_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/application/resource'\nrequire 'puppet_spec/character_encoding'\n\ndescribe Puppet::Application::Resource do\n  include PuppetSpec::Files\n\n  before :each do\n    @resource_app = Puppet::Application[:resource]\n    allow(Puppet::Util::Log).to receive(:newdestination)\n  end\n\n  describe \"in preinit\" do\n    it \"should include provider parameter by default\" do\n      @resource_app.preinit\n      expect(@resource_app.extra_params).to eq([:provider])\n    end\n  end\n\n  describe \"when handling options\" do\n    [:debug, :verbose, :edit].each do |option|\n      it \"should store argument value when calling handle_#{option}\" do\n        expect(@resource_app.options).to receive(:[]=).with(option, 'arg')\n        @resource_app.send(\"handle_#{option}\".to_sym, 'arg')\n      end\n    end\n\n    it \"should load a display all types with types option\" do\n      type1 = double('type1', :name => :type1)\n      type2 = double('type2', :name => :type2)\n      allow(Puppet::Type).to receive(:loadall)\n      allow(Puppet::Type).to receive(:eachtype).and_yield(type1).and_yield(type2)\n      expect(@resource_app).to receive(:puts).with(['type1','type2'])\n      expect { @resource_app.handle_types(nil) }.to exit_with 0\n    end\n\n    it \"should add param to extra_params list\" do\n      @resource_app.extra_params = [ :param1 ]\n      @resource_app.handle_param(\"whatever\")\n\n      expect(@resource_app.extra_params).to eq([ :param1, :whatever ])\n    end\n\n    it \"should get a parameter in the printed data if extra_params are passed\" do\n      tty  = double(\"tty\",  :tty? => true )\n      path = tmpfile('testfile')\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", [ 'resource', 'file', path ], tty )\n      allow(@resource_app).to receive(:command_line).and_return(command_line)\n\n      # provider is a parameter that should always be available\n      @resource_app.extra_params = [ :provider ]\n\n      expect {\n        @resource_app.main\n      }.to output(/provider\\s+=>/).to_stdout\n    end\n  end\n\n  describe \"during setup\" do\n    before :each do\n      allow(Puppet::Log).to receive(:newdestination)\n    end\n\n    it \"should set console as the log destination\" do\n      expect(Puppet::Log).to receive(:newdestination).with(:console)\n\n      @resource_app.setup\n    end\n\n    it \"should set log level to debug if --debug was passed\" do\n      allow(@resource_app.options).to receive(:[]).with(:debug).and_return(true)\n      @resource_app.setup\n      expect(Puppet::Log.level).to eq(:debug)\n    end\n\n    it \"should set log level to info if --verbose was passed\" do\n      allow(@resource_app.options).to receive(:[]).with(:debug).and_return(false)\n      allow(@resource_app.options).to receive(:[]).with(:verbose).and_return(true)\n      @resource_app.setup\n      expect(Puppet::Log.level).to eq(:info)\n    end\n  end\n\n  describe \"when running\" do\n    before :each do\n      @type = double('type', :properties => [])\n      allow(@resource_app.command_line).to receive(:args).and_return(['mytype'])\n      allow(Puppet::Type).to receive(:type).and_return(@type)\n\n      @res = double(\"resource\")\n      allow(@res).to receive(:prune_parameters).and_return(@res)\n      allow(@res).to receive(:to_manifest).and_return(\"resource\")\n      @report = double(\"report\")\n\n      allow(@resource_app).to receive(:puts)\n    end\n\n    it \"should raise an error if no type is given\" do\n      allow(@resource_app.command_line).to receive(:args).and_return([])\n      expect { @resource_app.main }.to raise_error(RuntimeError, \"You must specify the type to display\")\n    end\n\n    it \"should raise an error if the type is not found\" do\n      allow(Puppet::Type).to receive(:type).and_return(nil)\n\n      expect { @resource_app.main }.to raise_error(RuntimeError, 'Could not find type mytype')\n    end\n\n    it \"should search for resources\" do\n      expect(Puppet::Resource.indirection).to receive(:search).with('mytype/', {}).and_return([])\n      @resource_app.main\n    end\n\n    it \"should describe the given resource\" do\n      allow(@resource_app.command_line).to receive(:args).and_return(['type','name'])\n      expect(Puppet::Resource.indirection).to receive(:find).with('type/name').and_return(@res)\n      @resource_app.main\n    end\n\n    before :each do\n      allow(@res).to receive(:ref).and_return(\"type/name\")\n    end\n\n    it \"should add given parameters to the object\" do\n      allow(@resource_app.command_line).to receive(:args).and_return(['type','name','param=temp'])\n\n      expect(Puppet::Resource.indirection).to receive(:save).with(@res, 'type/name').and_return([@res, @report])\n      expect(Puppet::Resource).to receive(:new).with('type', 'name', {:parameters => {'param' => 'temp'}}).and_return(@res)\n\n      resource_status = instance_double('Puppet::Resource::Status')\n      allow(@report).to receive(:resource_statuses).and_return({'type/name' => resource_status})\n      allow(resource_status).to receive(:failed?).and_return(false)\n      @resource_app.main\n    end\n  end\n\n  describe \"when printing output\" do\n    Puppet::Type.newtype(:stringify) do\n      ensurable\n      newparam(:name, isnamevar: true)\n      newproperty(:string)\n    end\n\n    Puppet::Type.type(:stringify).provide(:stringify) do\n      def exists?\n        true\n      end\n\n      def string=(value)\n      end\n\n      def string\n        Puppet::Util::Execution::ProcessOutput.new('test', 0)\n      end\n    end\n    \n    it \"should not emit puppet class tags when printing yaml when strict mode is off\" do\n      Puppet[:strict] = :warning\n\n      @resource_app.options[:to_yaml] = true\n      allow(@resource_app.command_line).to receive(:args).and_return(['stringify', 'hello', 'ensure=present', 'string=asd'])\n      expect(@resource_app).to receive(:puts).with(<<~YAML)\n      ---\n      stringify:\n        hello:\n          ensure: present\n          string: test\n      YAML\n      expect { @resource_app.main }.not_to raise_error\n    end\n\n    it \"should ensure all values to be printed are in the external encoding\" do\n      resources = [\n        Puppet::Type.type(:user).new(:name => \"\\u2603\".force_encoding(Encoding::UTF_8)).to_resource,\n        Puppet::Type.type(:user).new(:name => \"Jos\\xE9\".force_encoding(Encoding::ISO_8859_1)).to_resource\n      ]\n      expect(Puppet::Resource.indirection).to receive(:search).with('user/', {}).and_return(resources)\n      allow(@resource_app.command_line).to receive(:args).and_return(['user'])\n\n      # All of our output should be in external encoding\n      expect(@resource_app).to receive(:puts) { |args| expect(args.encoding).to eq(Encoding::ISO_8859_1) }\n\n      # This would raise an error if we weren't handling it\n      PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ISO_8859_1) do\n        expect { @resource_app.main }.not_to raise_error\n      end\n    end\n  end\n\n  describe \"when handling file type\" do\n    before :each do\n      allow(Facter).to receive(:loadfacts)\n      @resource_app.preinit\n    end\n\n    it \"should raise an exception if no file specified\" do\n      allow(@resource_app.command_line).to receive(:args).and_return(['file'])\n\n      expect { @resource_app.main }.to raise_error(RuntimeError, /Listing all file instances is not supported/)\n    end\n\n    it \"should output a file resource when given a file path\" do\n      path = File.expand_path('/etc')\n      res = Puppet::Type.type(:file).new(:path => path).to_resource\n      expect(Puppet::Resource.indirection).to receive(:find).and_return(res)\n\n      allow(@resource_app.command_line).to receive(:args).and_return(['file', path])\n      expect(@resource_app).to receive(:puts).with(/file \\{ '#{Regexp.escape(path)}'/m)\n\n      @resource_app.main\n    end\n  end\n\n  describe 'when handling a custom type' do\n    it 'the Puppet::Pops::Loaders instance is available' do\n      Puppet::Type.newtype(:testing) do\n        newparam(:name) do\n          isnamevar\n        end\n        def self.instances\n          fail('Loader not found') unless Puppet::Pops::Loaders.find_loader(nil).is_a?(Puppet::Pops::Loader::Loader)\n          @instances ||= [new(:name => name)]\n        end\n      end\n\n      allow(@resource_app.command_line).to receive(:args).and_return(['testing', 'hello'])\n      expect(@resource_app).to receive(:puts).with(\"testing { 'hello':\\n}\")\n      expect { @resource_app.main }.not_to raise_error\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application/ssl_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/application/ssl'\nrequire 'openssl'\nrequire 'puppet/test_ca'\n\ndescribe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  let(:ssl) do\n    app = Puppet::Application[:ssl]\n    app.options[:verbose] = true\n    app.setup_logs\n    app\n  end\n  let(:name) { 'ssl-client' }\n\n  before :all do\n    @ca = Puppet::TestCa.new\n    @ca_cert = @ca.ca_cert\n    @crl = @ca.ca_crl\n    @host = @ca.generate('ssl-client', {})\n  end\n\n  before do\n    Puppet.settings.use(:main)\n    Puppet[:certname] = name\n    Puppet[:vardir] = tmpdir(\"ssl_testing\")\n\n    # Host assumes ca cert and crl are present\n    File.write(Puppet[:localcacert], @ca_cert.to_pem)\n    File.write(Puppet[:hostcrl], @crl.to_pem)\n\n    # Setup our ssl client\n    File.write(Puppet[:hostprivkey], @host[:private_key].to_pem)\n    File.write(Puppet[:hostpubkey], @host[:private_key].public_key.to_pem)\n  end\n\n  def expects_command_to_pass(expected_output = nil)\n    expect {\n      ssl.run_command\n    }.to output(expected_output).to_stdout\n  end\n\n  def expects_command_to_fail(message)\n    expect {\n      expect {\n        ssl.run_command\n      }.to raise_error(Puppet::Error, message)\n    }.to output(/.*/).to_stdout\n  end\n\n  shared_examples_for 'an ssl action' do\n    it 'downloads the CA bundle first when missing' do\n      File.delete(Puppet[:localcacert])\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: @ca.ca_cert.to_pem)\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)\n\n      expects_command_to_pass\n\n      expect(File.read(Puppet[:localcacert])).to eq(@ca.ca_cert.to_pem)\n    end\n\n    it 'downloads the CRL bundle first when missing' do\n      File.delete(Puppet[:hostcrl])\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: @crl.to_pem)\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)\n\n      expects_command_to_pass\n\n      expect(File.read(Puppet[:hostcrl])).to eq(@crl.to_pem)\n    end\n  end\n\n  it 'uses the agent run mode' do\n    # make sure the ssl app resolves certname, server, etc\n    # the same way the agent application does\n    expect(ssl.class.run_mode.name).to eq(:agent)\n  end\n\n  context 'when generating help' do\n    it 'prints a message when an unknown action is specified' do\n      ssl.command_line.args << 'whoops'\n\n      expects_command_to_fail(/Unknown action 'whoops'/)\n    end\n\n    it 'prints a message requiring an action to be specified' do\n      expects_command_to_fail(/An action must be specified/)\n    end\n  end\n\n  context 'when submitting a CSR' do\n    let(:csr_path) { Puppet[:hostcsr] }\n\n    before do\n      ssl.command_line.args << 'submit_request'\n    end\n\n    it_behaves_like 'an ssl action'\n\n    it 'generates an RSA private key' do\n      File.unlink(Puppet[:hostprivkey])\n\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})\n    end\n\n    it 'generates an EC private key' do\n      Puppet[:key_type] = 'ec'\n      File.unlink(Puppet[:hostprivkey])\n\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})\n    end\n\n    it 'registers OIDs' do\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)\n      expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})\n    end\n\n    it 'submits the CSR and saves it locally' do\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})\n\n      expect(Puppet::FileSystem).to be_exist(csr_path)\n    end\n\n    it 'detects when a CSR with the same public key has already been submitted' do\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})\n\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_pass\n    end\n\n    it 'downloads the certificate when autosigning is enabled' do\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)\n\n      expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})\n\n      expect(Puppet::FileSystem).to be_exist(Puppet[:hostcert])\n      expect(Puppet::FileSystem).to_not be_exist(csr_path)\n    end\n\n    it 'accepts dns alt names' do\n      Puppet[:dns_alt_names] = 'majortom'\n\n      stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_pass\n\n      csr = Puppet::SSL::CertificateRequest.new(name)\n      csr.read(csr_path)\n      expect(csr.subject_alt_names).to include('DNS:majortom')\n    end\n  end\n\n  context 'when generating a CSR' do\n    let(:csr_path) { Puppet[:hostcsr] }\n    let(:requestdir) { Puppet[:requestdir] }\n\n    before do\n      ssl.command_line.args << 'generate_request'\n    end\n\n    it 'generates an RSA private key' do\n      File.unlink(Puppet[:hostprivkey])\n\n      expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})\n    end\n\n    it 'generates an EC private key' do\n      Puppet[:key_type] = 'ec'\n      File.unlink(Puppet[:hostprivkey])\n\n      expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})\n    end\n\n    it 'registers OIDs' do\n      expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)\n\n      expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})\n    end\n\n    it 'saves the CSR locally' do\n      expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})\n\n      expect(Puppet::FileSystem).to be_exist(csr_path)\n    end\n\n    it 'accepts dns alt names' do\n      Puppet[:dns_alt_names] = 'majortom'\n\n      expects_command_to_pass\n\n      csr = Puppet::SSL::CertificateRequest.new(name)\n      csr.read(csr_path)\n      expect(csr.subject_alt_names).to include('DNS:majortom')\n    end\n  end\n\n  context 'when downloading a certificate' do\n    before do\n      ssl.command_line.args << 'download_cert'\n    end\n\n    it_behaves_like 'an ssl action'\n\n    it 'downloads a new cert' do\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)\n\n      expects_command_to_pass(%r{Downloaded certificate '#{name}' with fingerprint .*})\n\n      expect(File.read(Puppet[:hostcert])).to eq(@host[:cert].to_pem)\n    end\n\n    it 'overwrites an existing cert' do\n      File.write(Puppet[:hostcert], \"existing certificate\")\n\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)\n\n      expects_command_to_pass(%r{Downloaded certificate '#{name}' with fingerprint .*})\n\n      expect(File.read(Puppet[:hostcert])).to eq(@host[:cert].to_pem)\n    end\n\n    it \"reports an error if the downloaded cert's public key doesn't match our private key\" do\n      File.write(Puppet[:hostcert], \"existing cert\")\n\n      # generate a new host key, whose public key doesn't match the cert\n      private_key = OpenSSL::PKey::RSA.new(512)\n      File.write(Puppet[:hostprivkey], private_key.to_pem)\n      File.write(Puppet[:hostpubkey], private_key.public_key.to_pem)\n\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)\n\n      expects_command_to_fail(\n        %r{^Failed to download certificate: The certificate for 'CN=ssl-client' does not match its private key}\n      )\n    end\n\n    it \"prints a message if there isn't a cert to download\" do\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)\n\n      expects_command_to_fail(/The certificate for '#{name}' has not yet been signed/)\n    end\n  end\n\n  context 'when verifying' do\n    before do\n      ssl.command_line.args << 'verify'\n\n      File.write(Puppet[:hostcert], @host[:cert].to_pem)\n    end\n\n    it 'reports if the key is missing' do\n      File.delete(Puppet[:hostprivkey])\n\n      expects_command_to_fail(/The private key is missing from/)\n    end\n\n    it 'reports if the cert is missing' do\n      File.delete(Puppet[:hostcert])\n\n      expects_command_to_fail(/The client certificate is missing from/)\n    end\n\n    it 'reports if the key and cert are mismatched' do\n      # generate new keys\n      private_key = OpenSSL::PKey::RSA.new(512)\n      public_key = private_key.public_key\n      File.write(Puppet[:hostprivkey], private_key.to_pem)\n      File.write(Puppet[:hostpubkey], public_key.to_pem)\n\n      expects_command_to_fail(%r{The certificate for 'CN=ssl-client' does not match its private key})\n    end\n\n    it 'reports if the cert verification fails' do\n      # generate a new CA to force an error\n      new_ca = Puppet::TestCa.new\n      File.write(Puppet[:localcacert], new_ca.ca_cert.to_pem)\n\n      # and CRL for that CA\n      File.write(Puppet[:hostcrl], new_ca.ca_crl.to_pem)\n\n      expects_command_to_fail(%r{Invalid signature for certificate 'CN=ssl-client'})\n    end\n\n    it 'reports when verification succeeds' do\n      expects_command_to_pass(%r{Verified client certificate 'CN=ssl-client' fingerprint})\n    end\n\n    it 'reports when verification succeeds with a password protected private key' do\n      FileUtils.cp(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'encrypted-key.pem'), Puppet[:hostprivkey])\n      FileUtils.cp(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'signed.pem'), Puppet[:hostcert])\n\n      # To verify the client cert we need the root and intermediate certs and crls.\n      # We don't need to do this with `ssl-client` cert above, because it is issued\n      # directly from the generated TestCa above.\n      File.open(Puppet[:localcacert], 'w') do |f|\n        f.write(File.read(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'ca.pem')))\n        f.write(File.read(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'intermediate.pem')))\n      end\n\n      File.open(Puppet[:hostcrl], 'w') do |f|\n        f.write(File.read(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'crl.pem')))\n        f.write(File.read(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'intermediate-crl.pem')))\n      end\n\n      Puppet[:passfile] = file_containing('passfile', '74695716c8b6')\n\n      expects_command_to_pass(%r{Verified client certificate 'CN=signed' fingerprint})\n    end\n\n    it 'reports if the private key password is incorrect' do\n      FileUtils.cp(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'encrypted-key.pem'), Puppet[:hostprivkey])\n      FileUtils.cp(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'signed.pem'), Puppet[:hostcert])\n\n      Puppet[:passfile] = file_containing('passfile', 'wrongpassword')\n\n      expects_command_to_fail(/Failed to load private key for host 'ssl-client'/)\n    end\n  end\n\n  context 'when cleaning' do\n    before do\n      ssl.command_line.args << 'clean'\n    end\n\n    it 'deletes the hostcert' do\n      File.write(Puppet[:hostcert], @host[:cert].to_pem)\n\n      expects_command_to_pass(%r{Removed certificate #{Puppet[:cert]}})\n    end\n\n    it 'deletes the private key' do\n      File.write(Puppet[:hostprivkey], @host[:private_key].to_pem)\n\n      expects_command_to_pass(%r{Removed private key #{Puppet[:hostprivkey]}})\n    end\n\n    it 'deletes the public key' do\n      File.write(Puppet[:hostpubkey], @host[:private_key].public_key.to_pem)\n\n      expects_command_to_pass(%r{Removed public key #{Puppet[:hostpubkey]}})\n    end\n\n    it 'deletes the request' do\n      path = Puppet[:hostcsr]\n      File.write(path, @host[:csr].to_pem)\n\n      expects_command_to_pass(%r{Removed certificate request #{path}})\n    end\n\n    it 'deletes the passfile' do\n      FileUtils.touch(Puppet[:passfile])\n\n      expects_command_to_pass(%r{Removed private key password file #{Puppet[:passfile]}})\n    end\n\n    it 'skips files that do not exist' do\n      File.delete(Puppet[:hostprivkey])\n\n      expect {\n        ssl.run_command\n      }.to_not output(%r{Removed private key #{Puppet[:hostprivkey]}}).to_stdout\n    end\n\n    it \"raises if we fail to retrieve server's cert that we're about to clean\" do\n      Puppet[:certname] = name\n      Puppet[:server] = name\n\n      stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_raise(Errno::ECONNREFUSED)\n\n      expects_command_to_fail(%r{Failed to connect to the CA to determine if certificate #{name} has been cleaned})\n    end\n\n    it 'raises if we have extra args' do\n      ssl.command_line.args << 'hostname.example.biz'\n      expects_command_to_fail(/Extra arguments detected: hostname.example.biz/)\n    end\n\n    context 'when deleting local CA' do\n      before do\n        ssl.command_line.args << '--localca'\n        ssl.parse_options\n      end\n\n      it 'deletes the local CA cert' do\n        File.write(Puppet[:localcacert], @ca_cert.to_pem)\n\n        expects_command_to_pass(%r{Removed local CA certificate #{Puppet[:localcacert]}})\n      end\n\n      it 'deletes the local CRL' do\n        File.write(Puppet[:hostcrl], @crl.to_pem)\n\n        expects_command_to_pass(%r{Removed local CRL #{Puppet[:hostcrl]}})\n      end\n    end\n\n    context 'on the puppetserver host' do\n      before :each do\n        Puppet[:certname] = 'puppetserver'\n        Puppet[:server] = 'puppetserver'\n      end\n\n      it \"prints an error when the CA is local and the CA has not cleaned its cert\" do\n        stub_request(:get, %r{puppet-ca/v1/certificate/puppetserver}).to_return(status: 200, body: @host[:cert].to_pem)\n\n        expects_command_to_fail(%r{The certificate puppetserver must be cleaned from the CA first})\n      end\n\n      it \"cleans the cert when the CA is local and the CA has already cleaned its cert\" do\n        File.write(Puppet[:hostcert], @host[:cert].to_pem)\n\n        stub_request(:get, %r{puppet-ca/v1/certificate/puppetserver}).to_return(status: 404)\n\n        expects_command_to_pass(%r{Removed certificate .*puppetserver.pem})\n      end\n\n      it \"cleans the cert when run on a puppetserver that isn't the CA\" do\n        File.write(Puppet[:hostcert], @host[:cert].to_pem)\n\n        Puppet[:ca_server] = 'caserver'\n\n        expects_command_to_pass(%r{Removed certificate .*puppetserver.pem})\n      end\n    end\n\n    context 'when cleaning a device' do\n      before do\n        ssl.command_line.args = ['clean', '--target', 'device.example.com']\n        ssl.parse_options\n      end\n\n      it 'deletes the device certificate' do\n        device_cert_path = File.join(Puppet[:devicedir], 'device.example.com', 'ssl', 'certs')\n        device_cert_file = File.join(device_cert_path, 'device.example.com.pem')\n        FileUtils.mkdir_p(device_cert_path)\n        File.write(device_cert_file, 'device.example.com')\n        expects_command_to_pass(%r{Removed certificate #{device_cert_file}})\n     end\n    end\n  end\n\n  context 'when bootstrapping' do\n    before do\n      ssl.command_line.args << 'bootstrap'\n    end\n\n    it 'registers the OIDs' do\n      expect_any_instance_of(Puppet::SSL::StateMachine).to receive(:ensure_client_certificate).and_return(\n        double('ssl_context')\n      )\n      expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)\n      expects_command_to_pass\n    end\n\n    it 'returns an SSLContext with the loaded CA certs, CRLs, private key and client cert' do\n      expect_any_instance_of(Puppet::SSL::StateMachine).to receive(:ensure_client_certificate).and_return(\n        double('ssl_context')\n      )\n\n      expects_command_to_pass\n    end\n  end\n\n  context 'when showing' do\n    before do\n      ssl.command_line.args << 'show'\n      File.write(Puppet[:hostcert], @host[:cert].to_pem)\n    end\n\n    it 'reports if the key is missing' do\n      File.delete(Puppet[:hostprivkey])\n\n      expects_command_to_fail(/The private key is missing from/)\n    end\n\n    it 'reports if the cert is missing' do\n      File.delete(Puppet[:hostcert])\n\n      expects_command_to_fail(/The client certificate is missing from/)\n    end\n\n    it 'prints certificate information' do\n      expects_command_to_pass(@host[:cert].to_text)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/application_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/application'\nrequire 'puppet'\nrequire 'getoptlong'\nrequire 'timeout'\n\ndescribe Puppet::Application do\n  before(:each) do\n    @appclass = Class.new(Puppet::Application) do\n      def handle_unknown(opt, arg); end\n    end\n    @app = @appclass.new\n\n    allow(@app).to receive(:name).and_return(\"test_app\")\n  end\n\n  describe \"application commandline\" do\n    it \"should not pick up changes to the array of arguments\" do\n      args = %w{subcommand --arg}\n      command_line = Puppet::Util::CommandLine.new('puppet', args)\n      app = Puppet::Application.new(command_line)\n\n      args[0] = 'different_subcommand'\n      args[1] = '--other-arg'\n\n      expect(app.command_line.subcommand_name).to eq('subcommand')\n      expect(app.command_line.args).to eq(['--arg'])\n    end\n  end\n\n  describe \"application defaults\" do\n    it \"should fail if required app default values are missing\" do\n      allow(@app).to receive(:app_defaults).and_return({ :foo => 'bar' })\n      expect(Puppet).to receive(:send_log).with(:err, /missing required app default setting/)\n      expect {\n        @app.run\n      }.to exit_with(1)\n    end\n  end\n\n  describe \"finding\" do\n    before do\n      @klass = Puppet::Application\n      allow(@klass).to receive(:puts)\n    end\n\n    it \"should find classes in the namespace\" do\n      expect(@klass.find(\"Agent\")).to eq(@klass::Agent)\n    end\n\n    it \"should not find classes outside the namespace\" do\n      expect { @klass.find(\"String\") }.to raise_error(LoadError)\n    end\n\n    it \"should error if it can't find a class\" do\n      expect(Puppet).to receive(:send_log) do |_level, message|\n        expect(message).to match(/Unable to find application 'ThisShallNeverEverEverExist'/)\n        expect(message).to match(/puppet\\/application\\/thisshallneverevereverexist/)\n        expect(message).to match(/no such file to load|cannot load such file/)\n      end\n\n      expect {\n        @klass.find(\"ThisShallNeverEverEverExist\")\n      }.to raise_error(LoadError)\n    end\n  end\n\n  describe \"#available_application_names\" do\n    it 'should be able to find available application names' do\n      apps =  %w{describe filebucket kick queue resource agent cert apply doc master}\n      expect(Puppet::Util::Autoload).to receive(:files_to_load).and_return(apps)\n\n      expect(Puppet::Application.available_application_names).to match_array(apps)\n    end\n\n    it 'should find applications from multiple paths' do\n      expect(Puppet::Util::Autoload).to receive(:files_to_load).with(\n        'puppet/application',\n        be_a(Puppet::Node::Environment)\n      ).and_return(%w{ /a/foo.rb /b/bar.rb })\n\n      expect(Puppet::Application.available_application_names).to match_array(%w{ foo bar })\n    end\n\n    it 'should return unique application names' do\n      expect(Puppet::Util::Autoload).to receive(:files_to_load).with(\n        'puppet/application',\n        be_a(Puppet::Node::Environment)\n      ).and_return(%w{ /a/foo.rb /b/foo.rb })\n\n      expect(Puppet::Application.available_application_names).to eq(%w{ foo })\n    end\n\n    it 'finds the application using the configured environment' do\n      Puppet[:environment] = 'production'\n      expect(Puppet::Util::Autoload).to receive(:files_to_load) do |_, env|\n        expect(env.name).to eq(:production)\n      end.and_return(%w{ /a/foo.rb })\n\n      expect(Puppet::Application.available_application_names).to eq(%w{ foo })\n    end\n\n    it \"falls back to the current environment if the configured environment doesn't exist\" do\n      Puppet[:environment] = 'doesnotexist'\n      expect(Puppet::Util::Autoload).to receive(:files_to_load) do |_, env|\n        expect(env.name).to eq(:'*root*')\n      end.and_return(%w[a/foo.rb])\n\n      expect(Puppet::Application.available_application_names).to eq(%w[foo])\n    end\n  end\n\n  describe \".run_mode\" do\n    it \"should default to user\" do\n      expect(@appclass.run_mode.name).to eq(:user)\n    end\n\n    it \"should set and get a value\" do\n      @appclass.run_mode :agent\n      expect(@appclass.run_mode.name).to eq(:agent)\n    end\n\n    it \"considers :server to be master\" do\n      @appclass.run_mode :server\n      expect(@appclass.run_mode).to be_master\n    end\n  end\n\n  describe \".environment_mode\" do\n    it \"should default to :local\" do\n      expect(@appclass.get_environment_mode).to eq(:local)\n    end\n\n    it \"should set and get a value\" do\n      @appclass.environment_mode :remote\n      expect(@appclass.get_environment_mode).to eq(:remote)\n    end\n\n    it \"should error if given a random symbol\" do\n      expect{@appclass.environment_mode :foo}.to raise_error(/Invalid environment mode/)\n    end\n\n    it \"should error if given a string\" do\n      expect{@appclass.environment_mode 'local'}.to raise_error(/Invalid environment mode/)\n    end\n  end\n\n\n  # These tests may look a little weird and repetative in its current state;\n  #  it used to illustrate several ways that the run_mode could be changed\n  #  at run time; there are fewer ways now, but it would still be nice to\n  #  get to a point where it was entirely impossible.\n  describe \"when dealing with run_mode\" do\n\n    class TestApp < Puppet::Application\n      run_mode :server\n      def run_command\n        # no-op\n      end\n    end\n\n    it \"should sadly and frighteningly allow run_mode to change at runtime via #initialize_app_defaults\" do\n      allow(Puppet.features).to receive(:syslog?).and_return(true)\n\n      app = TestApp.new\n      app.initialize_app_defaults\n\n      expect(Puppet.run_mode).to be_server\n    end\n\n    it \"should sadly and frighteningly allow run_mode to change at runtime via #run\" do\n      app = TestApp.new\n      app.run\n\n      expect(app.class.run_mode.name).to eq(:server)\n\n      expect(Puppet.run_mode).to be_server\n    end\n  end\n\n  it \"should explode when an invalid run mode is set at runtime, for great victory\" do\n    expect {\n      class InvalidRunModeTestApp < Puppet::Application\n        run_mode :abracadabra\n        def run_command\n          # no-op\n        end\n      end\n    }.to raise_error(Puppet::Settings::ValidationError, /Invalid run mode/)\n  end\n\n  it \"should have a run entry-point\" do\n    expect(@app).to respond_to(:run)\n  end\n\n  it \"should have a read accessor to options\" do\n    expect(@app).to respond_to(:options)\n  end\n\n  it \"should include a default setup method\" do\n    expect(@app).to respond_to(:setup)\n  end\n\n  it \"should include a default preinit method\" do\n    expect(@app).to respond_to(:preinit)\n  end\n\n  it \"should include a default run_command method\" do\n    expect(@app).to respond_to(:run_command)\n  end\n\n  it \"should invoke main as the default\" do\n    expect(@app).to receive(:main)\n    @app.run_command\n  end\n\n  describe 'when invoking clear!' do\n    before :each do\n      Puppet::Application.run_status = :stop_requested\n      Puppet::Application.clear!\n    end\n\n    it 'should have nil run_status' do\n      expect(Puppet::Application.run_status).to be_nil\n    end\n\n    it 'should return false for restart_requested?' do\n      expect(Puppet::Application.restart_requested?).to be_falsey\n    end\n\n    it 'should return false for stop_requested?' do\n      expect(Puppet::Application.stop_requested?).to be_falsey\n    end\n\n    it 'should return false for interrupted?' do\n      expect(Puppet::Application.interrupted?).to be_falsey\n    end\n\n    it 'should return true for clear?' do\n      expect(Puppet::Application.clear?).to be_truthy\n    end\n  end\n\n  describe 'after invoking stop!' do\n    before :each do\n      Puppet::Application.run_status = nil\n      Puppet::Application.stop!\n    end\n\n    after :each do\n      Puppet::Application.run_status = nil\n    end\n\n    it 'should have run_status of :stop_requested' do\n      expect(Puppet::Application.run_status).to eq(:stop_requested)\n    end\n\n    it 'should return true for stop_requested?' do\n      expect(Puppet::Application.stop_requested?).to be_truthy\n    end\n\n    it 'should return false for restart_requested?' do\n      expect(Puppet::Application.restart_requested?).to be_falsey\n    end\n\n    it 'should return true for interrupted?' do\n      expect(Puppet::Application.interrupted?).to be_truthy\n    end\n\n    it 'should return false for clear?' do\n      expect(Puppet::Application.clear?).to be_falsey\n    end\n  end\n\n  describe 'when invoking restart!' do\n    before :each do\n      Puppet::Application.run_status = nil\n      Puppet::Application.restart!\n    end\n\n    after :each do\n      Puppet::Application.run_status = nil\n    end\n\n    it 'should have run_status of :restart_requested' do\n      expect(Puppet::Application.run_status).to eq(:restart_requested)\n    end\n\n    it 'should return true for restart_requested?' do\n      expect(Puppet::Application.restart_requested?).to be_truthy\n    end\n\n    it 'should return false for stop_requested?' do\n      expect(Puppet::Application.stop_requested?).to be_falsey\n    end\n\n    it 'should return true for interrupted?' do\n      expect(Puppet::Application.interrupted?).to be_truthy\n    end\n\n    it 'should return false for clear?' do\n      expect(Puppet::Application.clear?).to be_falsey\n    end\n  end\n\n  describe 'when performing a controlled_run' do\n    it 'should not execute block if not :clear?' do\n      Puppet::Application.run_status = :stop_requested\n      target = double('target')\n      expect(target).not_to receive(:some_method)\n      Puppet::Application.controlled_run do\n        target.some_method\n      end\n    end\n\n    it 'should execute block if :clear?' do\n      Puppet::Application.run_status = nil\n      target = double('target')\n      expect(target).to receive(:some_method).once\n      Puppet::Application.controlled_run do\n        target.some_method\n      end\n    end\n\n    describe 'on POSIX systems', :if => (Puppet.features.posix? && RUBY_PLATFORM != 'java') do\n      it 'should signal process with HUP after block if restart requested during block execution' do\n        Timeout::timeout(3) do  # if the signal doesn't fire, this causes failure.\n\n          has_run = false\n          old_handler = trap('HUP') { has_run = true }\n\n          begin\n            Puppet::Application.controlled_run do\n              Puppet::Application.run_status = :restart_requested\n            end\n\n            # Ruby 1.9 uses a separate OS level thread to run the signal\n            # handler, so we have to poll - ideally, in a way that will kick\n            # the OS into running other threads - for a while.\n            #\n            # You can't just use the Ruby Thread yield thing either, because\n            # that is just an OS hint, and Linux ... doesn't take that\n            # seriously. --daniel 2012-03-22\n            sleep 0.001 while not has_run\n          ensure\n            trap('HUP', old_handler)\n          end\n        end\n      end\n    end\n\n    after :each do\n      Puppet::Application.run_status = nil\n    end\n  end\n\n  describe \"when parsing command-line options\" do\n    before :each do\n      allow(@app.command_line).to receive(:args).and_return([])\n\n      allow(Puppet.settings).to receive(:optparse_addargs).and_return([])\n    end\n\n    it \"should pass the banner to the option parser\" do\n      option_parser = double(\"option parser\")\n      allow(option_parser).to receive(:on)\n      allow(option_parser).to receive(:parse!)\n      @app.class.instance_eval do\n        banner \"banner\"\n      end\n\n      expect(OptionParser).to receive(:new).with(\"banner\").and_return(option_parser)\n\n      @app.parse_options\n    end\n\n    it \"should ask OptionParser to parse the command-line argument\" do\n      allow(@app.command_line).to receive(:args).and_return(%w{ fake args })\n      expect_any_instance_of(OptionParser).to receive(:parse!).with(%w{ fake args })\n\n      @app.parse_options\n    end\n\n    describe \"when using --help\" do\n      it \"should call exit\" do\n        allow(@app).to receive(:puts)\n        expect { @app.handle_help(nil) }.to exit_with 0\n      end\n    end\n\n    describe \"when using --version\" do\n      it \"should declare a version option\" do\n        expect(@app).to respond_to(:handle_version)\n      end\n\n      it \"should exit after printing the version\" do\n        allow(@app).to receive(:puts)\n        expect { @app.handle_version(nil) }.to exit_with 0\n      end\n    end\n\n    describe \"when dealing with an argument not declared directly by the application\" do\n      it \"should pass it to handle_unknown if this method exists\" do\n        allow(Puppet.settings).to receive(:optparse_addargs).and_return([[\"--not-handled\", :REQUIRED]])\n\n        expect(@app).to receive(:handle_unknown).with(\"--not-handled\", \"value\").and_return(true)\n        allow(@app.command_line).to receive(:args).and_return([\"--not-handled\", \"value\"])\n        @app.parse_options\n      end\n\n      it \"should transform boolean option to normal form for Puppet.settings\" do\n        expect(@app).to receive(:handle_unknown).with(\"--option\", true)\n        @app.send(:handlearg, \"--[no-]option\", true)\n      end\n\n      it \"should transform boolean option to no- form for Puppet.settings\" do\n        expect(@app).to receive(:handle_unknown).with(\"--no-option\", false)\n        @app.send(:handlearg, \"--[no-]option\", false)\n      end\n    end\n  end\n\n  describe \"when calling default setup\" do\n    before :each do\n      allow(@app.options).to receive(:[])\n    end\n\n    [ :debug, :verbose ].each do |level|\n      it \"should honor option #{level}\" do\n        allow(@app.options).to receive(:[]).with(level).and_return(true)\n        allow(Puppet::Util::Log).to receive(:newdestination)\n        @app.setup\n        expect(Puppet::Util::Log.level).to eq(level == :verbose ? :info : :debug)\n      end\n    end\n\n    it \"should honor setdest option\" do\n      allow(@app.options).to receive(:[]).with(:setdest).and_return(false)\n\n      expect(Puppet::Util::Log).to receive(:setup_default)\n\n      @app.setup\n    end\n\n    it \"sets the log destination if provided via settings\" do\n      allow(@app.options).to receive(:[]).and_call_original\n      Puppet[:logdest] = \"set_via_config\"\n      expect(Puppet::Util::Log).to receive(:newdestination).with(\"set_via_config\")\n\n      @app.setup\n    end\n\n    it \"does not downgrade the loglevel when --verbose is specified\" do\n      Puppet[:log_level] = :debug\n      allow(@app.options).to receive(:[]).with(:verbose).and_return(true)\n      @app.setup_logs\n\n      expect(Puppet::Util::Log.level).to eq(:debug)\n    end\n\n    it \"allows the loglevel to be specified as an argument\" do\n      @app.set_log_level(:debug => true)\n\n      expect(Puppet::Util::Log.level).to eq(:debug)\n    end\n  end\n\n  describe \"when configuring routes\" do\n    include PuppetSpec::Files\n\n    before :each do\n      Puppet::Node.indirection.reset_terminus_class\n    end\n\n    after :each do\n      Puppet::Node.indirection.reset_terminus_class\n    end\n\n    it \"should use the routes specified for only the active application\" do\n      Puppet[:route_file] = tmpfile('routes')\n      File.open(Puppet[:route_file], 'w') do |f|\n        f.print <<-ROUTES\n          test_app:\n            node:\n              terminus: exec\n          other_app:\n            node:\n              terminus: plain\n            catalog:\n              terminus: invalid\n        ROUTES\n      end\n\n      @app.configure_indirector_routes\n\n      expect(Puppet::Node.indirection.terminus_class).to eq('exec')\n    end\n\n    it \"should not fail if the route file doesn't exist\" do\n      Puppet[:route_file] = \"/dev/null/non-existent\"\n\n      expect { @app.configure_indirector_routes }.to_not raise_error\n    end\n\n    it \"should raise an error if the routes file is invalid\" do\n      Puppet[:route_file] = tmpfile('routes')\n      File.open(Puppet[:route_file], 'w') do |f|\n        f.print <<-ROUTES\n         invalid : : yaml\n        ROUTES\n      end\n\n      expect { @app.configure_indirector_routes }.to raise_error(Puppet::Error, /mapping values are not allowed/)\n    end\n\n    it \"should treat master routes on server application\" do\n      allow(@app).to receive(:name).and_return(\"server\")\n\n      Puppet[:route_file] = tmpfile('routes')\n      File.open(Puppet[:route_file], 'w') do |f|\n        f.print <<-ROUTES\n          master:\n            node:\n              terminus: exec\n        ROUTES\n      end\n\n      @app.configure_indirector_routes\n\n      expect(Puppet::Node.indirection.terminus_class).to eq('exec')\n    end\n\n    it \"should treat server routes on master application\" do\n      allow(@app).to receive(:name).and_return(\"master\")\n\n      Puppet[:route_file] = tmpfile('routes')\n      File.open(Puppet[:route_file], 'w') do |f|\n        f.print <<-ROUTES\n          server:\n            node:\n              terminus: exec\n        ROUTES\n      end\n\n      @app.configure_indirector_routes\n\n      expect(Puppet::Node.indirection.terminus_class).to eq('exec')\n    end\n  end\n\n  describe \"when running\" do\n    before :each do\n      allow(@app).to receive(:preinit)\n      allow(@app).to receive(:setup)\n      allow(@app).to receive(:parse_options)\n    end\n\n    it \"should call preinit\" do\n      allow(@app).to receive(:run_command)\n\n      expect(@app).to receive(:preinit)\n\n      @app.run\n    end\n\n    it \"should call parse_options\" do\n      allow(@app).to receive(:run_command)\n\n      expect(@app).to receive(:parse_options)\n\n      @app.run\n    end\n\n    it \"should call run_command\" do\n      expect(@app).to receive(:run_command)\n\n      @app.run\n    end\n\n    it \"should call run_command\" do\n      expect(@app).to receive(:run_command)\n\n      @app.run\n    end\n\n    it \"should call main as the default command\" do\n      expect(@app).to receive(:main)\n\n      @app.run\n    end\n\n    it \"should warn and exit if no command can be called\" do\n      expect(Puppet).to receive(:send_log).with(:err, \"Could not run: No valid command or main\")\n      expect { @app.run }.to exit_with 1\n    end\n\n    it \"should raise an error if dispatch returns no command\" do\n      expect(Puppet).to receive(:send_log).with(:err, \"Could not run: No valid command or main\")\n      expect { @app.run }.to exit_with 1\n    end\n  end\n\n  describe \"when metaprogramming\" do\n    describe \"when calling option\" do\n      it \"should create a new method named after the option\" do\n        @app.class.option(\"--test1\",\"-t\") do\n        end\n\n        expect(@app).to respond_to(:handle_test1)\n      end\n\n      it \"should transpose in option name any '-' into '_'\" do\n        @app.class.option(\"--test-dashes-again\",\"-t\") do\n        end\n\n        expect(@app).to respond_to(:handle_test_dashes_again)\n      end\n\n      it \"should create a new method called handle_test2 with option(\\\"--[no-]test2\\\")\" do\n        @app.class.option(\"--[no-]test2\",\"-t\") do\n        end\n\n        expect(@app).to respond_to(:handle_test2)\n      end\n\n      describe \"when a block is passed\" do\n        it \"should create a new method with it\" do\n          @app.class.option(\"--[no-]test2\",\"-t\") do\n            raise \"I can't believe it, it works!\"\n          end\n\n          expect { @app.handle_test2 }.to raise_error(RuntimeError, /I can't believe it, it works!/)\n        end\n\n        it \"should declare the option to OptionParser\" do\n          allow_any_instance_of(OptionParser).to receive(:on)\n          expect_any_instance_of(OptionParser).to receive(:on).with(\"--[no-]test3\", anything)\n\n          @app.class.option(\"--[no-]test3\",\"-t\") do\n          end\n\n          @app.parse_options\n        end\n\n        it \"should pass a block that calls our defined method\" do\n          allow_any_instance_of(OptionParser).to receive(:on)\n          allow_any_instance_of(OptionParser).to receive(:on).with('--test4', '-t').and_yield(nil)\n\n          expect(@app).to receive(:send).with(:handle_test4, nil)\n\n          @app.class.option(\"--test4\",\"-t\") do\n          end\n\n          @app.parse_options\n        end\n      end\n\n      describe \"when no block is given\" do\n        it \"should declare the option to OptionParser\" do\n          allow_any_instance_of(OptionParser).to receive(:on)\n          expect_any_instance_of(OptionParser).to receive(:on).with(\"--test4\", \"-t\")\n\n          @app.class.option(\"--test4\",\"-t\")\n\n          @app.parse_options\n        end\n\n        it \"should give to OptionParser a block that adds the value to the options array\" do\n          allow_any_instance_of(OptionParser).to receive(:on)\n          allow_any_instance_of(OptionParser).to receive(:on).with(\"--test4\", \"-t\").and_yield(nil)\n\n          expect(@app.options).to receive(:[]=).with(:test4, nil)\n\n          @app.class.option(\"--test4\",\"-t\")\n\n          @app.parse_options\n        end\n      end\n    end\n  end\n\n  describe \"#handle_logdest_arg\" do\n    let(:test_arg) { \"arg_test_logdest\" }\n\n    it \"should log an exception that is raised\" do\n      our_exception = Puppet::DevError.new(\"test exception\")\n      expect(Puppet::Util::Log).to receive(:newdestination).with(test_arg).and_raise(our_exception)\n      expect(Puppet).to receive(:log_and_raise).with(our_exception, anything)\n      @app.handle_logdest_arg(test_arg)\n    end\n\n    it \"should exit when an exception is raised\" do\n      our_exception = Puppet::DevError.new(\"test exception\")\n      expect(Puppet::Util::Log).to receive(:newdestination).with(test_arg).and_raise(our_exception)\n      expect(Puppet).to receive(:log_and_raise).with(our_exception, anything).and_raise(our_exception)\n      expect { @app.handle_logdest_arg(test_arg) }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should set the new log destination\" do\n      expect(Puppet::Util::Log).to receive(:newdestination).with(test_arg)\n      @app.handle_logdest_arg(test_arg)\n    end\n\n    it \"should set the flag that a destination is set in the options hash\" do\n      allow(Puppet::Util::Log).to receive(:newdestination).with(test_arg)\n      @app.handle_logdest_arg(test_arg)\n      expect(@app.options[:setdest]).to be_truthy\n    end\n\n    it \"does not set the log destination if arg is nil\" do\n      expect(Puppet::Util::Log).not_to receive(:newdestination)\n\n      @app.handle_logdest_arg(nil)\n    end\n\n    it \"accepts multiple destinations as a comma sepparated list\" do\n      dest1 = '/tmp/path1'\n      dest2 = 'console'\n      dest3 = '/tmp/path2'\n      dest_args = [dest1, dest2, dest3].join(' , ')\n\n      [dest1, dest2, dest3].each do |dest|\n        expect(Puppet::Util::Log).to receive(:newdestination).with(dest)\n      end\n\n      @app.handle_logdest_arg(dest_args)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/certificate_factory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/test_ca'\n\nrequire 'puppet/certificate_factory'\n\ndescribe Puppet::CertificateFactory, :unless => RUBY_PLATFORM == 'java' do\n  let :serial    do OpenSSL::BN.new('12') end\n  let :name      do \"example.local\" end\n  let :x509_name do OpenSSL::X509::Name.new([['CN', name]]) end\n  let :key       do OpenSSL::PKey::RSA.new(Puppet[:keylength]) end\n  let :csr       do\n    csr = Puppet::SSL::CertificateRequest.new(name)\n    csr.generate(key)\n    csr\n  end\n  let(:issuer) { Puppet::TestCa.new.ca_cert }\n\n  describe \"when generating the certificate\" do\n    it \"should return a new X509 certificate\" do\n      a = subject.build(:server, csr, issuer, serial)\n      b = subject.build(:server, csr, issuer, serial)\n      # The two instances are equal in every aspect except that they are\n      # different instances - they are `==`, but not hash `eql?`\n      expect(a).not_to eql(b)\n    end\n\n    it \"should set the certificate's version to 2\" do\n      expect(subject.build(:server, csr, issuer, serial).version).to eq(2)\n    end\n\n    it \"should set the certificate's subject to the CSR's subject\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      expect(cert.subject).to eq x509_name\n    end\n\n    it \"should set the certificate's issuer to the Issuer's subject\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      expect(cert.issuer).to eq issuer.subject\n    end\n\n    it \"should set the certificate's public key to the CSR's public key\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      expect(cert.public_key).to be_public\n      expect(cert.public_key.to_s).to eq(csr.content.public_key.to_s)\n    end\n\n    it \"should set the certificate's serial number to the provided serial number\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      expect(cert.serial).to eq(serial)\n    end\n\n    it \"should have 24 hours grace on the start of the cert\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      expect(cert.not_before).to be_within(30).of(Time.now - 24*60*60)\n    end\n\n    it \"should not allow a non-integer TTL\" do\n      [ 'foo', 1.2, Time.now, true ].each do |ttl|\n        expect { subject.build(:server, csr, issuer, serial, ttl) }.to raise_error(ArgumentError)\n      end\n    end\n\n    it \"should respect a custom TTL for the CA\" do\n      now = Time.now.utc\n      expect(Time).to receive(:now).at_least(:once).and_return(now)\n      cert = subject.build(:server, csr, issuer, serial, 12)\n      expect(cert.not_after.to_i).to eq(now.to_i + 12)\n    end\n\n    it \"should adds an extension for the nsComment\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      expect(cert.extensions.map {|x| x.to_h }.find {|x| x[\"oid\"] == \"nsComment\" }).to eq(\n        { \"oid\"      => \"nsComment\",\n          # Note that this output is due to a bug in OpenSSL::X509::Extensions\n          # where the values of some extensions are not properly decoded\n          \"value\"    => \".(Puppet Ruby/OpenSSL Internal Certificate\",\n          \"critical\" => false }\n      )\n    end\n\n    it \"should add an extension for the subjectKeyIdentifer\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      ef = OpenSSL::X509::ExtensionFactory.new(issuer, cert)\n      expect(cert.extensions.map { |x| x.to_h }.find {|x| x[\"oid\"] == \"subjectKeyIdentifier\" }).to eq(\n        ef.create_extension(\"subjectKeyIdentifier\", \"hash\", false).to_h\n      )\n    end\n\n    it \"should add an extension for the authorityKeyIdentifer\" do\n      cert = subject.build(:server, csr, issuer, serial)\n      ef = OpenSSL::X509::ExtensionFactory.new(issuer, cert)\n      expect(cert.extensions.map { |x| x.to_h }.find {|x| x[\"oid\"] == \"authorityKeyIdentifier\" }).to eq(\n        ef.create_extension(\"authorityKeyIdentifier\", \"keyid:always\", false).to_h\n      )\n    end\n\n    # See #2848 for why we are doing this: we need to make sure that\n    # subjectAltName is set if the CSR has it, but *not* if it is set when the\n    # certificate is built!\n    it \"should not add subjectAltNames from dns_alt_names\" do\n      Puppet[:dns_alt_names] = 'one, two'\n      # Verify the CSR still has no extReq, just in case...\n      expect(csr.request_extensions).to eq([])\n      cert = subject.build(:server, csr, issuer, serial)\n\n      expect(cert.extensions.find {|x| x.oid == 'subjectAltName' }).to be_nil\n    end\n\n    it \"should add subjectAltName when the CSR requests them\" do\n      Puppet[:dns_alt_names] = ''\n\n      expect = %w{one two} + [name]\n\n      csr = Puppet::SSL::CertificateRequest.new(name)\n      csr.generate(key, :dns_alt_names => expect.join(', '))\n\n      expect(csr.request_extensions).not_to be_nil\n      expect(csr.subject_alt_names).to match_array(expect.map{|x| \"DNS:#{x}\"})\n\n      cert = subject.build(:server, csr, issuer, serial)\n      san = cert.extensions.find {|x| x.oid == 'subjectAltName' }\n      expect(san).not_to be_nil\n      expect.each do |name|\n        expect(san.value).to match(/DNS:#{name}\\b/i)\n      end\n    end\n\n    it \"can add custom extension requests\" do\n      csr = Puppet::SSL::CertificateRequest.new(name)\n      csr.generate(key)\n\n      allow(csr).to receive(:request_extensions).and_return([\n        {'oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'some-value'},\n        {'oid' => 'pp_uuid', 'value' => 'some-uuid'},\n      ])\n\n      cert = subject.build(:client, csr, issuer, serial)\n\n      # The cert must be signed before being later DER-decoding\n      signer = Puppet::SSL::CertificateSigner.new\n      signer.sign(cert, key)\n      wrapped_cert = Puppet::SSL::Certificate.from_instance cert\n\n      priv_ext = wrapped_cert.custom_extensions.find {|ext| ext['oid'] == '1.3.6.1.4.1.34380.1.2.1'}\n      uuid_ext = wrapped_cert.custom_extensions.find {|ext| ext['oid'] == 'pp_uuid'}\n\n      # The expected results should be DER encoded, the Puppet cert wrapper will turn\n      # these into normal strings.\n      expect(priv_ext['value']).to eq 'some-value'\n      expect(uuid_ext['value']).to eq 'some-uuid'\n    end\n\n    # Can't check the CA here, since that requires way more infrastructure\n    # that I want to build up at this time.  We can verify the critical\n    # values, though, which are non-CA certs. --daniel 2011-10-11\n    { :ca            => 'CA:TRUE',\n      :terminalsubca => ['CA:TRUE', 'pathlen:0'],\n      :server        => 'CA:FALSE',\n      :ocsp          => 'CA:FALSE',\n      :client        => 'CA:FALSE',\n    }.each do |name, value|\n      it \"should set basicConstraints for #{name} #{value.inspect}\" do\n        cert = subject.build(name, csr, issuer, serial)\n        bc = cert.extensions.find {|x| x.oid == 'basicConstraints' }\n        expect(bc).to be\n        expect(bc.value.split(/\\s*,\\s*/)).to match_array(Array(value))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/concurrent/lock_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/concurrent/lock'\n\ndescribe Puppet::Concurrent::Lock do\n  if Puppet::Util::Platform.jruby?\n    context 'on jruby' do\n      it 'synchronizes a block on itself' do\n        iterations = 100\n        value = 0\n\n        lock = Puppet::Concurrent::Lock.new\n        threads = iterations.times.collect do\n          Thread.new do\n            lock.synchronize do\n              tmp = (value += 1)\n              sleep(0.001)\n              # This update using tmp is designed to lose increments if threads overlap\n              value = tmp + 1\n            end\n          end\n        end\n        threads.each(&:join)\n\n        # In my testing this always fails by quite a lot when not synchronized (ie on mri)\n        expect(value).to eq(iterations * 2)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/concurrent/thread_local_singleton_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/concurrent/thread_local_singleton'\n\nclass PuppetSpec::Singleton\n  extend Puppet::Concurrent::ThreadLocalSingleton\nend\n\n# Use the `equal?` matcher to ensure we get the same object\ndescribe Puppet::Concurrent::ThreadLocalSingleton do\n  it 'returns the same object for a single thread' do\n    expect(PuppetSpec::Singleton.singleton).to equal(PuppetSpec::Singleton.singleton)\n  end\n\n  it 'is not inherited for a newly created thread' do\n    main_thread_local = PuppetSpec::Singleton.singleton\n    Thread.new do\n      expect(main_thread_local).to_not equal(PuppetSpec::Singleton.singleton)\n    end.join\n  end\n\n  it 'does not leak outside a thread' do\n    thread_local = nil\n    Thread.new do\n      thread_local = PuppetSpec::Singleton.singleton\n    end.join\n    expect(thread_local).to_not equal(PuppetSpec::Singleton.singleton)\n  end\n\n  it 'is different for each thread' do\n    locals = []\n    Thread.new do\n      locals << PuppetSpec::Singleton.singleton\n    end.join\n    Thread.new do\n      locals << PuppetSpec::Singleton.singleton\n    end.join\n    expect(locals.first).to_not equal(locals.last)\n  end\nend\n"
  },
  {
    "path": "spec/unit/configurer/downloader_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/configurer/downloader'\n\ndescribe Puppet::Configurer::Downloader do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  let(:path)   { Puppet[:plugindest] }\n  let(:source) { 'puppet://puppet/plugins' }\n\n  it \"should require a name\" do\n    expect { Puppet::Configurer::Downloader.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should require a path and a source at initialization\" do\n    expect { Puppet::Configurer::Downloader.new(\"name\") }.to raise_error(ArgumentError)\n  end\n\n  it \"should set the name, path and source appropriately\" do\n    dler = Puppet::Configurer::Downloader.new(\"facts\", \"path\", \"source\")\n    expect(dler.name).to eq(\"facts\")\n    expect(dler.path).to eq(\"path\")\n    expect(dler.source).to eq(\"source\")\n  end\n\n  def downloader(options = {})\n    options[:name] ||= \"facts\"\n    options[:path] ||= path\n    options[:source_permissions] ||= :ignore\n    Puppet::Configurer::Downloader.new(options[:name], options[:path], source, options[:ignore], options[:environment], options[:source_permissions])\n  end\n\n  def generate_file_resource(options = {})\n    dler = downloader(options)\n    dler.file\n  end\n\n  describe \"when creating the file that does the downloading\" do\n    it \"should create a file instance with the right path and source\" do\n      file = generate_file_resource(:path => path, :source => source)\n\n      expect(file[:path]).to eq(path)\n      expect(file[:source]).to eq([source])\n    end\n\n    it \"should tag the file with the downloader name\" do\n      name = \"mydownloader\"\n      file = generate_file_resource(:name => name)\n\n      expect(file[:tag]).to eq([name])\n    end\n\n    it \"should always recurse\" do\n      file = generate_file_resource\n\n      expect(file[:recurse]).to be_truthy\n    end\n\n    it \"should follow links by default\" do\n      file = generate_file_resource\n\n      expect(file[:links]).to eq(:follow)\n    end\n\n    it \"should always purge\" do\n      file = generate_file_resource\n\n      expect(file[:purge]).to be_truthy\n    end\n\n    it \"should never be in noop\" do\n      file = generate_file_resource\n\n      expect(file[:noop]).to be_falsey\n    end\n\n    it \"should set source_permissions to ignore by default\" do\n      file = generate_file_resource\n\n      expect(file[:source_permissions]).to eq(:ignore)\n    end\n\n    it \"should ignore the max file limit\" do\n      file = generate_file_resource\n\n      expect(file[:max_files]).to eq(-1)\n    end\n\n    describe \"on POSIX\", :if => Puppet.features.posix? do\n      it \"should allow source_permissions to be overridden\" do\n        file = generate_file_resource(:source_permissions => :use)\n\n        expect(file[:source_permissions]).to eq(:use)\n      end\n\n      it \"should always set the owner to the current UID\" do\n        expect(Process).to receive(:uid).and_return(51)\n\n        file = generate_file_resource(:path => '/path')\n        expect(file[:owner]).to eq(51)\n      end\n\n      it \"should always set the group to the current GID\" do\n        expect(Process).to receive(:gid).and_return(61)\n\n        file = generate_file_resource(:path => '/path')\n        expect(file[:group]).to eq(61)\n      end\n    end\n\n    describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should omit the owner\" do\n        file = generate_file_resource(:path => 'C:/path')\n\n        expect(file[:owner]).to be_nil\n      end\n\n      it \"should omit the group\" do\n        file = generate_file_resource(:path => 'C:/path')\n\n        expect(file[:group]).to be_nil\n      end\n    end\n\n    it \"should always force the download\" do\n      file = generate_file_resource\n\n      expect(file[:force]).to be_truthy\n    end\n\n    it \"should never back up when downloading\" do\n      file = generate_file_resource\n\n      expect(file[:backup]).to be_falsey\n    end\n\n    it \"should support providing an 'ignore' parameter\" do\n      file = generate_file_resource(:ignore => '.svn')\n\n      expect(file[:ignore]).to eq(['.svn'])\n    end\n\n    it \"should split the 'ignore' parameter on whitespace\" do\n      file = generate_file_resource(:ignore => '.svn CVS')\n\n      expect(file[:ignore]).to eq(['.svn', 'CVS'])\n    end\n  end\n\n  describe \"when creating the catalog to do the downloading\" do\n    before do\n      @path = make_absolute(\"/download/path\")\n      @dler = Puppet::Configurer::Downloader.new(\"foo\", @path, make_absolute(\"source\"))\n    end\n\n    it \"should create a catalog and add the file to it\" do\n      catalog = @dler.catalog\n      expect(catalog.resources.size).to eq(1)\n      expect(catalog.resources.first.class).to eq(Puppet::Type::File)\n      expect(catalog.resources.first.name).to eq(@path)\n    end\n\n    it \"should specify that it is not managing a host catalog\" do\n      expect(@dler.catalog.host_config).to eq(false)\n    end\n\n    it \"should not issue a deprecation warning for source_permissions\" do\n      expect(Puppet).not_to receive(:puppet_deprecation_warning)\n      catalog = @dler.catalog\n      expect(catalog.resources.size).to eq(1) # Must consume catalog to fix warnings\n    end\n  end\n\n  describe \"when downloading\" do\n    before do\n      @dl_name = tmpfile(\"downloadpath\")\n      source_name = tmpfile(\"source\")\n      File.open(source_name, 'w') {|f| f.write('hola mundo') }\n      env = Puppet::Node::Environment.remote('foo')\n      @dler = Puppet::Configurer::Downloader.new(\"foo\", @dl_name, source_name, Puppet[:pluginsignore], env)\n    end\n\n    it \"should not skip downloaded resources when filtering on tags\" do\n      Puppet[:tags] = 'maytag'\n      @dler.evaluate\n\n      expect(Puppet::FileSystem.exist?(@dl_name)).to be_truthy\n    end\n\n    it \"should log that it is downloading\" do\n      expect(Puppet).to receive(:info)\n\n      @dler.evaluate\n    end\n\n    it \"should return all changed file paths\" do\n      Puppet[:ignore_plugin_errors] = true\n\n      trans = double('transaction')\n\n      catalog = double('catalog')\n      expect(@dler).to receive(:catalog).and_return(catalog)\n      expect(catalog).to receive(:apply).and_yield(trans)\n\n      resource = double('resource')\n      expect(resource).to receive(:[]).with(:path).and_return(\"/changed/file\")\n\n      expect(trans).to receive(:changed?).and_return([resource])\n\n      expect(@dler.evaluate).to eq(%w{/changed/file})\n    end\n\n    it \"should yield the resources if a block is given\" do\n      Puppet[:ignore_plugin_errors] = true\n\n      trans = double('transaction')\n\n      catalog = double('catalog')\n      expect(@dler).to receive(:catalog).and_return(catalog)\n      expect(catalog).to receive(:apply).and_yield(trans)\n\n      resource = double('resource')\n      expect(resource).to receive(:[]).with(:path).and_return(\"/changed/file\")\n\n      expect(trans).to receive(:changed?).and_return([resource])\n\n      yielded = nil\n      @dler.evaluate { |r| yielded = r }\n      expect(yielded).to eq(resource)\n    end\n\n    it \"should catch and log exceptions\" do\n      Puppet[:ignore_plugin_errors] = true\n\n      expect(Puppet).to receive(:log_exception)\n      # The downloader creates a new catalog for each apply, and really the only object\n      # that it is possible to stub for the purpose of generating a puppet error\n      allow_any_instance_of(Puppet::Resource::Catalog).to receive(:apply).and_raise(Puppet::Error, \"testing\")\n\n      expect { @dler.evaluate }.not_to raise_error\n    end\n\n    it \"raises an exception if catalog application fails\" do\n      expect(@dler.file).to receive(:retrieve).and_raise(Puppet::Error, \"testing\")\n\n      expect {\n        @dler.evaluate\n      }.to raise_error(Puppet::Error, /testing/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/configurer/fact_handler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/configurer'\nrequire 'puppet/configurer/fact_handler'\nrequire 'matchers/json'\n\nclass FactHandlerTester\n  include Puppet::Configurer::FactHandler\n\n  attr_accessor :environment\n\n  def initialize(environment)\n    self.environment = environment\n  end\n\n  def reload_facter\n    # don't want to do this in tests\n  end\nend\n\ndescribe Puppet::Configurer::FactHandler do\n  include JSONMatchers\n\n  let(:facthandler) { FactHandlerTester.new('production') }\n\n  describe \"when finding facts\" do\n    it \"should use the node name value to retrieve the facts\" do\n      foo_facts = Puppet::Node::Facts.new('foo')\n      bar_facts = Puppet::Node::Facts.new('bar')\n      Puppet::Node::Facts.indirection.save(foo_facts)\n      Puppet::Node::Facts.indirection.save(bar_facts)\n      Puppet[:certname] = 'foo'\n      Puppet[:node_name_value] = 'bar'\n\n      expect(facthandler.find_facts).to eq(bar_facts)\n    end\n\n    it \"should set the facts name based on the node_name_fact\" do\n      facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')\n      Puppet::Node::Facts.indirection.save(facts)\n      Puppet[:node_name_fact] = 'my_name_fact'\n\n      expect(facthandler.find_facts.name).to eq('other_node_name')\n    end\n\n    it \"should set the node_name_value based on the node_name_fact\" do\n      facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')\n      Puppet::Node::Facts.indirection.save(facts)\n      Puppet[:node_name_fact] = 'my_name_fact'\n\n      facthandler.find_facts\n\n      expect(Puppet[:node_name_value]).to eq('other_node_name')\n    end\n\n    it \"should fail if finding facts fails\" do\n      expect(Puppet::Node::Facts.indirection).to receive(:find).and_raise(RuntimeError)\n\n      expect { facthandler.find_facts }.to raise_error(Puppet::Error, /Could not retrieve local facts/)\n    end\n\n    it \"should only load fact plugins once\" do\n      expect(Puppet::Node::Facts.indirection).to receive(:find).once\n      facthandler.find_facts\n    end\n  end\n\n  context \"when serializing\" do\n    facts_with_special_characters = [\n      { :hash => { 'afact' => 'a+b' }, :encoded => '%22values%22%3A%7B%22afact%22%3A%22' + 'a%2Bb' + '%22%7D' },\n      { :hash => { 'afact' => 'a b' }, :encoded => '%22values%22%3A%7B%22afact%22%3A%22' + 'a%20b' + '%22%7D' },\n      { :hash => { 'afact' => 'a&b' }, :encoded => '%22values%22%3A%7B%22afact%22%3A%22' + 'a%26b' + '%22%7D' },\n      { :hash => { 'afact' => 'a*b' }, :encoded => '%22values%22%3A%7B%22afact%22%3A%22' + 'a%2Ab' + '%22%7D' },\n      { :hash => { 'afact' => 'a=b' }, :encoded => '%22values%22%3A%7B%22afact%22%3A%22' + 'a%3Db' + '%22%7D' },\n      # different UTF-8 widths\n      # 1-byte A\n      # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n      # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n      # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n      { :hash => { 'afact' => \"A\\u06FF\\u16A0\\u{2070E}\" }, :encoded => '%22values%22%3A%7B%22afact%22%3A%22' + 'A%DB%BF%E1%9A%A0%F0%A0%9C%8E' + '%22%7D' },\n    ]\n\n    context \"as pson\", if: Puppet.features.pson? do\n      before :each do\n        Puppet[:preferred_serialization_format] = 'pson'\n      end\n\n      it \"should serialize and CGI escape the fact values for uploading\" do\n        facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')\n        Puppet::Node::Facts.indirection.save(facts)\n        text = Puppet::Util.uri_query_encode(facthandler.find_facts.render(:pson))\n\n        expect(text).to include('%22values%22%3A%7B%22my_name_fact%22%3A%22other_node_name%22%7D')\n        expect(facthandler.facts_for_uploading).to eq({:facts_format => :pson, :facts => text})\n      end\n\n      facts_with_special_characters.each do |test_fact|\n        it \"should properly accept the fact #{test_fact[:hash]}\" do\n          facts = Puppet::Node::Facts.new(Puppet[:node_name_value], test_fact[:hash])\n          Puppet::Node::Facts.indirection.save(facts)\n          text = Puppet::Util.uri_query_encode(facthandler.find_facts.render(:pson))\n\n          to_upload = facthandler.facts_for_uploading\n          expect(to_upload).to eq({:facts_format => :pson, :facts => text})\n          expect(text).to include(test_fact[:encoded])\n\n          # this is not sufficient to test whether these values are sent via HTTP GET or HTTP POST in actual catalog request\n          expect(JSON.parse(Puppet::Util.uri_unescape(to_upload[:facts]))['values']).to eq(test_fact[:hash])\n        end\n      end\n    end\n\n    context \"as json\" do\n      it \"should serialize and CGI escape the fact values for uploading\" do\n        facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')\n        Puppet::Node::Facts.indirection.save(facts)\n        text = Puppet::Util.uri_query_encode(facthandler.find_facts.render(:json))\n\n        expect(text).to include('%22values%22%3A%7B%22my_name_fact%22%3A%22other_node_name%22%7D')\n        expect(facthandler.facts_for_uploading).to eq({:facts_format => 'application/json', :facts => text})\n      end\n\n      facts_with_special_characters.each do |test_fact|\n        it \"should properly accept the fact #{test_fact[:hash]}\" do\n          facts = Puppet::Node::Facts.new(Puppet[:node_name_value], test_fact[:hash])\n          Puppet::Node::Facts.indirection.save(facts)\n          text = Puppet::Util.uri_query_encode(facthandler.find_facts.render(:json))\n\n          to_upload = facthandler.facts_for_uploading\n          expect(to_upload).to eq({:facts_format => 'application/json', :facts => text})\n          expect(text).to include(test_fact[:encoded])\n\n          expect(JSON.parse(Puppet::Util.uri_unescape(to_upload[:facts]))['values']).to eq(test_fact[:hash])\n        end\n      end\n    end\n\n    it \"should generate valid facts data against the facts schema\" do\n      facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')\n      Puppet::Node::Facts.indirection.save(facts)\n\n      # prefer Puppet::Util.uri_unescape but validate CGI also works\n      encoded_facts = facthandler.facts_for_uploading[:facts]\n      expect(Puppet::Util.uri_unescape(encoded_facts)).to validate_against('api/schemas/facts.json')\n      expect(CGI.unescape(encoded_facts)).to validate_against('api/schemas/facts.json')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/configurer/plugin_handler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/configurer'\nrequire 'puppet/configurer/plugin_handler'\n\ndescribe Puppet::Configurer::PluginHandler do\n  let(:pluginhandler) { Puppet::Configurer::PluginHandler.new() }\n  let(:environment)   { Puppet::Node::Environment.create(:myenv, []) }\n\n  before :each do\n    # PluginHandler#load_plugin has an extra-strong rescue clause\n    # this mock is to make sure that we don't silently ignore errors\n    expect(Puppet).not_to receive(:err)\n  end\n\n  context \"server agent version is 5.3.4\" do\n    around do |example|\n      Puppet.override(server_agent_version: \"5.3.4\") do\n        example.run\n      end\n    end\n\n    context \"when i18n is enabled\" do\n      before :each do\n        Puppet[:disable_i18n] = false\n      end\n\n      it \"downloads plugins, facts, and locales\" do\n        times_called = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { times_called += 1 }.and_return([])\n\n        pluginhandler.download_plugins(environment)\n        expect(times_called).to eq(3)\n      end\n\n      it \"returns downloaded plugin, fact, and locale filenames\" do\n        times_called = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n          times_called += 1\n\n          if times_called == 1\n            %w[/a]\n          elsif times_called == 2\n            %w[/b]\n          else\n            %w[/c]\n          end\n        end\n\n        expect(pluginhandler.download_plugins(environment)).to match_array(%w[/a /b /c])\n        expect(times_called).to eq(3)\n      end\n    end\n\n    context \"when i18n is disabled\" do\n      before :each do\n        Puppet[:disable_i18n] = true\n      end\n\n      it \"downloads plugins, facts, but no locales\" do\n        times_called = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { times_called += 1 }.and_return([])\n\n        pluginhandler.download_plugins(environment)\n        expect(times_called).to eq(2)\n      end\n\n      it \"returns downloaded plugin, fact, and locale filenames\" do\n        times_called = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n          times_called += 1\n\n          if times_called == 1\n            %w[/a]\n          elsif times_called == 2\n            %w[/b]\n          else\n            %w[/c]\n          end\n        end\n\n        expect(pluginhandler.download_plugins(environment)).to match_array(%w[/a /b])\n        expect(times_called).to eq(2)\n      end\n    end\n  end\n\n  context \"server agent version is 5.3.3\" do\n    around do |example|\n      Puppet.override(server_agent_version: \"5.3.3\") do\n        example.run\n      end\n    end\n\n    it \"returns downloaded plugin, fact, but not locale filenames\" do\n      times_called = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n        times_called += 1\n\n        if times_called == 1\n          %w[/a]\n        else\n          %w[/b]\n        end\n      end\n\n      expect(pluginhandler.download_plugins(environment)).to match_array(%w[/a /b])\n      expect(times_called).to eq(2)\n    end\n  end\n\n  context \"blank server agent version\" do\n    around do |example|\n      Puppet.override(server_agent_version: \"\") do\n        example.run\n      end\n    end\n\n    it \"returns downloaded plugin, fact, but not locale filenames\" do\n      times_called = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n        times_called += 1\n\n        if times_called == 1\n          %w[/a]\n        else\n          %w[/b]\n        end\n      end\n\n      expect(pluginhandler.download_plugins(environment)).to match_array(%w[/a /b])\n      expect(times_called).to eq(2)\n    end\n  end\n\n  context \"nil server agent version\" do\n    it \"returns downloaded plugin, fact, but not locale filenames\" do\n      times_called = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n        times_called += 1\n\n        if times_called == 1\n          %w[/a]\n        else\n          %w[/b]\n        end\n      end\n\n      expect(pluginhandler.download_plugins(environment)).to match_array(%w[/a /b])\n      expect(times_called).to eq(2)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/configurer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/configurer'\n\ndescribe Puppet::Configurer do\n  include PuppetSpec::Files\n\n  before do\n    Puppet[:server] = \"puppetmaster\"\n    Puppet[:report] = true\n\n    catalog.add_resource(resource)\n\n    Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)\n    ---\n    version:\n      config: 1624882680\n      puppet: #{Puppet.version}\n    application:\n      initial_environment: #{Puppet[:environment]}\n      converged_environment: #{Puppet[:environment]}\n      run_mode: agent\n    SUMMARY\n  end\n\n  let(:node_name) { Puppet[:node_name_value] }\n  let(:configurer) { Puppet::Configurer.new }\n  let(:report) { Puppet::Transaction::Report.new }\n  let(:catalog) { Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote(Puppet[:environment].to_sym)) }\n  let(:resource) { Puppet::Resource.new(:notice, 'a') }\n  let(:facts) { Puppet::Node::Facts.new(node_name) }\n\n  describe \"when executing a pre-run hook\" do\n    it \"should do nothing if the hook is set to an empty string\" do\n      Puppet.settings[:prerun_command] = \"\"\n      expect(Puppet::Util::Execution).not_to receive(:execute)\n\n      configurer.execute_prerun_command\n    end\n\n    it \"should execute any pre-run command provided via the 'prerun_command' setting\" do\n      Puppet.settings[:prerun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      configurer.execute_prerun_command\n    end\n\n    it \"should fail if the command fails\" do\n      Puppet.settings[:prerun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      expect(configurer.execute_prerun_command).to be_falsey\n    end\n  end\n\n  describe \"when executing a post-run hook\" do\n    it \"should do nothing if the hook is set to an empty string\" do\n      Puppet.settings[:postrun_command] = \"\"\n      expect(Puppet::Util::Execution).not_to receive(:execute)\n\n      configurer.execute_postrun_command\n    end\n\n    it \"should execute any post-run command provided via the 'postrun_command' setting\" do\n      Puppet.settings[:postrun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      configurer.execute_postrun_command\n    end\n\n    it \"should fail if the command fails\" do\n      Puppet.settings[:postrun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      expect(configurer.execute_postrun_command).to be_falsey\n    end\n  end\n\n  describe \"when executing a catalog run without stubbing valid_server_environment?\" do\n    before do\n      Puppet::Resource::Catalog.indirection.terminus_class = :rest\n      allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(catalog)\n    end\n\n    it 'skips initial plugin sync if environment is not found and no strict_environment_mode' do\n      body = \"{\\\"message\\\":\\\"Not Found: Could not find environment 'fasdfad'\\\",\\\"issue_kind\\\":\\\"RUNTIME_ERROR\\\"}\"\n      stub_request(:get, %r{/puppet/v3/file_metadatas/plugins?}).to_return(\n        status: 404, body: body, headers: {'Content-Type' => 'application/json'}\n      )\n\n      configurer.run(:pluginsync => true)\n\n      expect(@logs).to include(an_object_having_attributes(level: :notice, message: %r{Environment 'production' not found on server, skipping initial pluginsync.}))\n      expect(@logs).to include(an_object_having_attributes(level: :notice, message: /Applied catalog in .* seconds/))\n    end\n\n    it 'if strict_environment_mode is set and environment is not found, aborts the puppet run' do\n      Puppet[:strict_environment_mode] = true\n      body = \"{\\\"message\\\":\\\"Not Found: Could not find environment 'fasdfad'\\\",\\\"issue_kind\\\":\\\"RUNTIME_ERROR\\\"}\"\n      stub_request(:get, %r{/puppet/v3/file_metadatas/plugins?}).to_return(\n        status: 404, body: body, headers: {'Content-Type' => 'application/json'}\n      )\n\n      configurer.run(:pluginsync => true)\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: %r{Failed to apply catalog: Environment 'production' not found on server, aborting run.}))\n    end\n  end\n\n  describe \"when executing a catalog run\" do\n    before do\n      Puppet::Resource::Catalog.indirection.terminus_class = :rest\n      allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(catalog)\n      allow_any_instance_of(described_class).to(\n        receive(:valid_server_environment?).and_return(true)\n      )\n    end\n\n    it \"downloads plugins when told\" do\n      expect(configurer).to receive(:download_plugins)\n      configurer.run(:pluginsync => true)\n    end\n\n    it \"does not download plugins when told\" do\n      expect(configurer).not_to receive(:download_plugins)\n      configurer.run(:pluginsync => false)\n    end\n\n    it \"does not download plugins when specified environment is not vaild on server\" do\n      expect(configurer).to receive(:valid_server_environment?).and_return(false)\n      expect(configurer).not_to receive(:download_plugins)\n      configurer.run(:pluginsync => true)\n    end\n\n    it \"fails the run if pluginsync fails when usecacheonfailure is false\" do\n      Puppet[:ignore_plugin_errors] = false\n\n      # --test implies these, set them so we don't fall back to a cached catalog\n      Puppet[:use_cached_catalog] = false\n      Puppet[:usecacheonfailure] = false\n\n      body = \"{\\\"message\\\":\\\"Not Found: Could not find environment 'fasdfad'\\\",\\\"issue_kind\\\":\\\"RUNTIME_ERROR\\\"}\"\n      stub_request(:get, %r{/puppet/v3/file_metadatas/pluginfacts}).to_return(\n        status: 404, body: body, headers: {'Content-Type' => 'application/json'}\n      )\n      stub_request(:get, %r{/puppet/v3/file_metadata/pluginfacts}).to_return(\n        status: 404, body: body, headers: {'Content-Type' => 'application/json'}\n      )\n\n      configurer.run(pluginsync: true)\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: %r{Failed to apply catalog: Failed to retrieve pluginfacts: Could not retrieve information from environment production source\\(s\\) puppet:///pluginfacts}))\n    end\n\n    it \"applies a cached catalog if pluginsync fails when usecacheonfailure is true\" do\n      Puppet[:ignore_plugin_errors] = false\n\n      Puppet[:use_cached_catalog] = false\n      Puppet[:usecacheonfailure] = true\n\n      body = \"{\\\"message\\\":\\\"Not Found: Could not find environment 'fasdfad'\\\",\\\"issue_kind\\\":\\\"RUNTIME_ERROR\\\"}\"\n      stub_request(:get, %r{/puppet/v3/file_metadatas/pluginfacts}).to_return(\n        status: 404, body: body, headers: {'Content-Type' => 'application/json'}\n      )\n      stub_request(:get, %r{/puppet/v3/file_metadata/pluginfacts}).to_return(\n        status: 404, body: body, headers: {'Content-Type' => 'application/json'}\n      )\n\n      expect(configurer.run(pluginsync: true, :report => report)).to eq(0)\n      expect(report.cached_catalog_status).to eq('on_failure')\n    end\n\n    it \"applies a cached catalog when it can't connect to the master\" do\n      error = Errno::ECONNREFUSED.new('Connection refused - connect(2)')\n\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(:ignore_cache => true)).and_raise(error)\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(:ignore_terminus => true)).and_return(catalog)\n\n      expect(configurer.run).to eq(0)\n    end\n\n    it \"should initialize a transaction report if one is not provided\" do\n      # host and settings catalogs each create a report...\n      expect(Puppet::Transaction::Report).to receive(:new).and_return(report).twice\n\n      configurer.run\n    end\n\n    it \"should respect node_name_fact when setting the host on a report\" do\n      Puppet[:node_name_value] = nil\n      Puppet[:node_name_fact] = 'my_name_fact'\n      facts.values = {'my_name_fact' => 'node_name_from_fact'}\n      Puppet::Node::Facts.indirection.save(facts)\n\n      configurer.run(:report => report)\n      expect(report.host).to eq('node_name_from_fact')\n    end\n\n    it \"should warn the user when the fact value length limits are exceeded\" do\n      Puppet[:fact_name_length_soft_limit] = 0\n      Puppet[:fact_value_length_soft_limit] = 1\n      Puppet[:top_level_facts_soft_limit] = 0\n      Puppet[:number_of_facts_soft_limit] = 0\n      Puppet[:payload_soft_limit] = 0\n\n      facts.values = { 'processors' => {\n        'isa' => \"i386\" }\n      }\n      Puppet::Node::Facts.indirection.save(facts)\n\n      expect(Puppet).to receive(:warning).with(/Fact processors.isa with value i386 with the value length: 4 exceeds the value length limit: 1/)\n      configurer.run\n    end\n\n    it \"should warn the user when the payload limits are exceeded\" do\n      Puppet[:fact_name_length_soft_limit] = 0\n      Puppet[:fact_value_length_soft_limit] = 0\n      Puppet[:top_level_facts_soft_limit] = 0\n      Puppet[:number_of_facts_soft_limit] = 0\n      Puppet[:payload_soft_limit] = 1\n\n      facts.values = { 'processors' => {\n        'cores' => 1,\n        'count' => 2,\n        'isa' => \"i386\",\n        'models' => [\n          \"CPU1 @ 2.80GHz\"\n        ],\n        'physicalcount' => 4 }\n      }\n      Puppet::Node::Facts.indirection.save(facts)\n\n      expect(Puppet).to receive(:warning).with(/Payload with the current size of: \\d* exceeds the payload size limit: \\d*/)\n      configurer.run\n    end\n\n    it \"should warn the user when the total number of facts limit is exceeded\" do\n      Puppet[:fact_name_length_soft_limit] = 0\n      Puppet[:fact_value_length_soft_limit] = 0\n      Puppet[:top_level_facts_soft_limit] = 0\n      Puppet[:number_of_facts_soft_limit] = 1\n      Puppet[:payload_soft_limit] = 0\n\n      facts.values = { \n        'processors' => {\n          'cores' => 1,\n          'count' => 2,\n          'isa' => \"i386\",\n          'models' => [\n            \"CPU1 @ 2.80GHz\",\n            \"CPU1 @ 2.80GHz\",\n            \"CPU1 @ 2.80GHz\",\n            \"CPU1 @ 2.80GHz\",\n            \"CPU1 @ 2.80GHz\",\n            { \n              'processors' => {\n                'cores' => [1,2]\n              }\n            }\n          ],\n          'physicalcount' => 4 \n        }\n      }\n      Puppet::Node::Facts.indirection.save(facts)\n\n      expect(Puppet).to receive(:warning).with(/The current total number of fact values: [1-9]* exceeds the fact values limit: [1-9]*/)\n      configurer.run\n    end\n\n    it \"should warn the user when the top level facts size limits are exceeded\" do\n      Puppet[:fact_name_length_soft_limit] = 0\n      Puppet[:fact_value_length_soft_limit] = 0\n      Puppet[:top_level_facts_soft_limit] = 1\n      Puppet[:number_of_facts_soft_limit] = 0\n      Puppet[:payload_soft_limit] = 0\n\n      facts.values = {'my_new_fact_name' => 'my_new_fact_value',\n                      'my_new_fact_name2' => 'my_new_fact_value2'}\n      Puppet::Node::Facts.indirection.save(facts)\n\n      expect(Puppet).to receive(:warning).with(/The current number of top level facts: [1-9]* exceeds the top facts limit: [1-9]*/)\n      configurer.run\n    end\n\n    it \"should warn the user when the fact name length limits are exceeded\" do\n      Puppet[:fact_name_length_soft_limit] = 1\n      Puppet[:fact_value_length_soft_limit] = 0\n      Puppet[:top_level_facts_soft_limit] = 0\n      Puppet[:number_of_facts_soft_limit] = 0\n      Puppet[:payload_soft_limit] = 0\n\n      facts.values = { 'processors' => {\n        'isa' => \"i386\" }\n      }\n      Puppet::Node::Facts.indirection.save(facts)\n\n      expect(Puppet).to receive(:warning).with(/Fact processors.isa with length: 25 exceeds the fact name length limit: 1/)\n      configurer.run\n    end\n\n    it \"shouldn't warn the user when the fact limit settings are set to 0\" do\n      Puppet[:fact_name_length_soft_limit] = 0\n      Puppet[:fact_value_length_soft_limit] = 0\n      Puppet[:top_level_facts_soft_limit] = 0\n      Puppet[:number_of_facts_soft_limit] = 0\n      Puppet[:payload_soft_limit] = 0\n\n      facts.values = {'my_new_fact_name' => 'my_new_fact_value'}\n      Puppet::Node::Facts.indirection.save(facts)\n\n      expect(Puppet).not_to receive(:warning)\n      configurer.run\n    end\n\n    it \"creates a new report when applying the catalog\" do\n      options = {}\n      configurer.run(options)\n\n      expect(options[:report].metrics['time']['catalog_application']).to be_an_instance_of(Float)\n    end\n\n    it \"uses the provided report when applying the catalog\" do\n      configurer.run(:report => report)\n\n      expect(report.metrics['time']['catalog_application']).to be_an_instance_of(Float)\n    end\n\n    it \"should log a failure and do nothing if no catalog can be retrieved\" do\n      expect(configurer).to receive(:retrieve_catalog).and_return(nil)\n\n      expect(Puppet).to receive(:err).with(\"Could not retrieve catalog; skipping run\")\n\n      configurer.run\n    end\n\n    it \"passes arbitrary options when applying the catalog\" do\n      expect(catalog).to receive(:apply).with(hash_including(one: true))\n\n      configurer.run(catalog: catalog, one: true)\n    end\n\n    it \"should benchmark how long it takes to apply the catalog\" do\n      configurer.run(report: report)\n\n      expect(report.logs).to include(an_object_having_attributes(level: :notice, message: /Applied catalog in .* seconds/))\n    end\n\n    it \"should create report with passed transaction_uuid and job_id\" do\n      configurer = Puppet::Configurer.new(\"test_tuuid\", \"test_jid\")\n\n      report = Puppet::Transaction::Report.new(nil, \"test\", \"aaaa\")\n      expect(Puppet::Transaction::Report).to receive(:new).with(anything, anything, 'test_tuuid', 'test_jid', anything).and_return(report)\n      expect(configurer).to receive(:send_report).with(report)\n\n      configurer.run\n    end\n\n    it \"should send the report\" do\n      report = Puppet::Transaction::Report.new(nil, \"test\", \"aaaa\")\n      expect(Puppet::Transaction::Report).to receive(:new).and_return(report)\n      expect(configurer).to receive(:send_report).with(report)\n\n      expect(report.environment).to eq(\"test\")\n      expect(report.transaction_uuid).to eq(\"aaaa\")\n\n      configurer.run\n    end\n\n    it \"should send the transaction report even if the catalog could not be retrieved\" do\n      expect(configurer).to receive(:retrieve_catalog).and_return(nil)\n\n      report = Puppet::Transaction::Report.new(nil, \"test\", \"aaaa\")\n      expect(Puppet::Transaction::Report).to receive(:new).and_return(report)\n      expect(configurer).to receive(:send_report).with(report)\n\n      expect(report.environment).to eq(\"test\")\n      expect(report.transaction_uuid).to eq(\"aaaa\")\n\n      configurer.run\n    end\n\n    it \"should send the transaction report even if there is a failure\" do\n      expect(configurer).to receive(:retrieve_catalog).and_raise(\"whatever\")\n\n      report = Puppet::Transaction::Report.new(nil, \"test\", \"aaaa\")\n      expect(Puppet::Transaction::Report).to receive(:new).and_return(report)\n      expect(configurer).to receive(:send_report).with(report)\n\n      expect(report.environment).to eq(\"test\")\n      expect(report.transaction_uuid).to eq(\"aaaa\")\n\n      expect(configurer.run).to be_nil\n    end\n\n    it \"should remove the report as a log destination when the run is finished\" do\n      configurer.run(report: report)\n\n      expect(Puppet::Util::Log.destinations).not_to include(report)\n    end\n\n    it \"should return an exit status of 2 due to the notify resource 'changing'\" do\n      cat = Puppet::Resource::Catalog.new(\"tester\", Puppet::Node::Environment.remote(Puppet[:environment].to_sym))\n      cat.add_resource(Puppet::Type.type(:notify).new(:name => 'something changed'))\n\n      expect(configurer.run(catalog: cat, report: report)).to eq(2)\n    end\n\n    it \"should return nil if catalog application fails\" do\n      expect(catalog).to receive(:apply).and_raise(Puppet::Error, 'One or more resource dependency cycles detected in graph')\n\n      expect(configurer.run(catalog: catalog, report: report)).to be_nil\n    end\n\n    it \"should send the transaction report even if the pre-run command fails\" do\n      expect(Puppet::Transaction::Report).to receive(:new).and_return(report)\n\n      Puppet.settings[:prerun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n      expect(configurer).to receive(:send_report).with(report)\n\n      expect(configurer.run).to be_nil\n    end\n\n    it \"should include the pre-run command failure in the report\" do\n      Puppet.settings[:prerun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      expect(configurer.run(report: report)).to be_nil\n      expect(report.logs.find { |x| x.message =~ /Could not run command from prerun_command/ }).to be\n    end\n\n    it \"should send the transaction report even if the post-run command fails\" do\n      Puppet.settings[:postrun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n      expect(configurer).to receive(:send_report).with(report)\n\n      expect(configurer.run(report: report)).to be_nil\n    end\n\n    it \"should include the post-run command failure in the report\" do\n      Puppet.settings[:postrun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      expect(report).to receive(:<<) { |log, _| expect(log.message).to match(/Could not run command from postrun_command/) }.at_least(:once)\n\n      expect(configurer.run(report: report)).to be_nil\n    end\n\n    it \"should execute post-run command even if the pre-run command fails\" do\n      Puppet.settings[:prerun_command] = \"/my/precommand\"\n      Puppet.settings[:postrun_command] = \"/my/postcommand\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/precommand\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/postcommand\"])\n\n      expect(configurer.run).to be_nil\n    end\n\n    it \"should finalize the report\" do\n      expect(report).to receive(:finalize_report)\n      configurer.run(report: report)\n    end\n\n    it \"should not apply the catalog if the pre-run command fails\" do\n      Puppet.settings[:prerun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      expect_any_instance_of(Puppet::Resource::Catalog).not_to receive(:apply)\n      expect(configurer).to receive(:send_report)\n\n      expect(configurer.run(report: report)).to be_nil\n    end\n\n    it \"should apply the catalog, send the report, and return nil if the post-run command fails\" do\n      Puppet.settings[:postrun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      expect_any_instance_of(Puppet::Resource::Catalog).to receive(:apply)\n      expect(configurer).to receive(:send_report)\n\n      expect(configurer.run(report: report)).to be_nil\n    end\n\n    it 'includes total time metrics in the report after successfully applying the catalog' do\n      configurer.run(report: report)\n\n      expect(report.metrics['time']).to be\n      expect(report.metrics['time']['total']).to be_a_kind_of(Numeric)\n    end\n\n    it 'includes total time metrics in the report even if prerun fails' do\n      Puppet.settings[:prerun_command] = \"/my/command\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/my/command\"]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      configurer.run(report: report)\n\n      expect(report.metrics['time']).to be\n      expect(report.metrics['time']['total']).to be_a_kind_of(Numeric)\n    end\n\n    it 'includes total time metrics in the report even if catalog retrieval fails' do\n      allow(configurer).to receive(:prepare_and_retrieve_catalog_from_cache).and_raise\n      configurer.run(:report => report)\n\n      expect(report.metrics['time']).to be\n      expect(report.metrics['time']['total']).to be_a_kind_of(Numeric)\n    end\n\n    it \"should refetch the catalog if the server specifies a new environment in the catalog\" do\n      catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote('second_env'))\n      expect(configurer).to receive(:retrieve_catalog).and_return(catalog).twice\n\n      configurer.run\n    end\n\n    it \"changes the configurer's environment if the server specifies a new environment in the catalog\" do\n      allow_any_instance_of(Puppet::Resource::Catalog).to receive(:environment).and_return(\"second_env\")\n\n      configurer.run\n\n      expect(configurer.environment).to eq(\"second_env\")\n    end\n\n    it \"changes the report's environment if the server specifies a new environment in the catalog\" do\n      allow_any_instance_of(Puppet::Resource::Catalog).to receive(:environment).and_return(\"second_env\")\n\n      configurer.run(report: report)\n\n      expect(report.environment).to eq(\"second_env\")\n    end\n\n    it \"sends the transaction uuid in a catalog request\" do\n      configurer = Puppet::Configurer.new('aaa')\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(transaction_uuid: 'aaa'))\n      configurer.run\n    end\n\n    it \"sends the transaction uuid in a catalog request\" do\n      configurer = Puppet::Configurer.new('b', 'aaa')\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(job_id: 'aaa'))\n      configurer.run\n    end\n\n    it \"sets the static_catalog query param to true in a catalog request\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(static_catalog: true))\n      configurer.run\n    end\n\n    it \"sets the checksum_type query param to the default supported_checksum_types in a catalog request\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything,\n        hash_including(checksum_type: 'sha256.sha384.sha512.sha224.md5'))\n      configurer.run\n    end\n\n    it \"sets the checksum_type query param to the supported_checksum_types setting in a catalog request\" do\n      Puppet[:supported_checksum_types] = ['sha256']\n      # Regenerate the agent to pick up the new setting\n      configurer = Puppet::Configurer.new\n\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(checksum_type: 'sha256'))\n      configurer.run\n    end\n\n    describe \"when not using a REST terminus for catalogs\" do\n      it \"should not pass any facts when retrieving the catalog\" do\n        # This is weird, we collect facts when constructing the node,\n        # but we don't send them in the indirector request. Then the compiler\n        # looks up the node, and collects its facts, which we could have sent\n        # in the first place. This seems like a bug.\n        Puppet::Resource::Catalog.indirection.terminus_class = :compiler\n\n        expect(Puppet::Resource::Catalog.indirection).to receive(:find) do |name, options|\n          expect(options[:facts]).to be_nil\n        end.and_return(catalog)\n\n        configurer.run\n      end\n    end\n\n    describe \"when using a REST terminus for catalogs\" do\n      it \"should pass the url encoded facts and facts format as arguments when retrieving the catalog\" do\n        Puppet::Resource::Catalog.indirection.terminus_class = :rest\n\n        facts.values = { 'foo' => 'bar' }\n        Puppet::Node::Facts.indirection.save(facts)\n\n        expect(\n          Puppet::Resource::Catalog.indirection\n        ).to receive(:find) do |_, options|\n          expect(options[:facts_format]).to eq(\"application/json\")\n\n          unescaped = JSON.parse(CGI.unescape(options[:facts]))\n          expect(unescaped).to include(\"values\" => {\"foo\" => \"bar\"})\n        end.and_return(catalog)\n\n        configurer.run\n      end\n    end\n  end\n\n  describe \"when sending a report\" do\n    include PuppetSpec::Files\n\n    before do\n      Puppet[:lastrunfile] = tmpfile('last_run_file')\n      Puppet[:reports] = \"none\"\n    end\n\n    it \"should print a report summary if configured to do so\" do\n      Puppet.settings[:summarize] = true\n\n      expect(report).to receive(:summary).and_return(\"stuff\")\n\n      expect(configurer).to receive(:puts).with(\"stuff\")\n      configurer.send_report(report)\n    end\n\n    it \"should not print a report summary if not configured to do so\" do\n      Puppet.settings[:summarize] = false\n\n      expect(configurer).not_to receive(:puts)\n      configurer.send_report(report)\n    end\n\n    it \"should save the report if reporting is enabled\" do\n      Puppet.settings[:report] = true\n\n      expect(Puppet::Transaction::Report.indirection).to receive(:save).with(report, nil, instance_of(Hash)).twice\n      configurer.send_report(report)\n    end\n\n    it \"should not save the report if reporting is disabled\" do\n      Puppet.settings[:report] = false\n\n      expect(Puppet::Transaction::Report.indirection).not_to receive(:save).with(report, nil, instance_of(Hash))\n      configurer.send_report(report)\n    end\n\n    it \"should save the last run summary if reporting is enabled\" do\n      Puppet.settings[:report] = true\n\n      expect(configurer).to receive(:save_last_run_summary).with(report)\n      configurer.send_report(report)\n    end\n\n    it \"should save the last run summary if reporting is disabled\" do\n      Puppet.settings[:report] = false\n\n      expect(configurer).to receive(:save_last_run_summary).with(report)\n      configurer.send_report(report)\n    end\n\n    it \"should log but not fail if saving the report fails\" do\n      Puppet.settings[:report] = true\n\n      expect(Puppet::Transaction::Report.indirection).to receive(:save).with(report, nil, hash_including(ignore_cache: true)).and_raise(\"whatever\")\n      expect(Puppet::Transaction::Report.indirection).to receive(:save).with(report, nil, hash_including(ignore_terminus: true))\n\n      configurer.send_report(report)\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: 'Could not send report: whatever'))\n    end\n\n    it \"should save the cached report if fails to send the report\" do\n      allow(Puppet::Transaction::Report.indirection).to receive(:save).with(report, nil, hash_including(ignore_terminus: true)).and_call_original\n      allow(Puppet::Transaction::Report.indirection).to receive(:save).with(report, nil, hash_including(ignore_cache: true)).and_raise(\"whatever\")\n\n      expect(File).to_not be_exist(Puppet[:lastrunfile])\n      configurer.send_report(report)\n      expect(File.read(Puppet[:lastrunfile])).to match(/puppet: #{Puppet::PUPPETVERSION}/)\n    end\n  end\n\n  describe \"when saving the summary report file\" do\n    include PuppetSpec::Files\n\n    before do\n      Puppet[:lastrunfile] = tmpfile('last_run_file')\n    end\n\n    it \"should write the last run file\" do\n      configurer.save_last_run_summary(report)\n      expect(Puppet::FileSystem.exist?(Puppet[:lastrunfile])).to be_truthy\n    end\n\n    it \"should write the raw summary as yaml\" do\n      expect(report).to receive(:raw_summary).and_return(\"summary\")\n      configurer.save_last_run_summary(report)\n      expect(File.read(Puppet[:lastrunfile])).to eq(YAML.dump(\"summary\"))\n    end\n\n    it \"should log but not fail if saving the last run summary fails\" do\n      # The mock will raise an exception on any method used.  This should\n      # simulate a nice hard failure from the underlying OS for us.\n      fh = Class.new(Object) do\n        def method_missing(*args)\n          raise \"failed to do #{args[0]}\"\n        end\n      end.new\n\n      expect(Puppet::Util).to receive(:replace_file).and_yield(fh)\n\n      configurer.save_last_run_summary(report)\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: 'Could not save last run local report: failed to do print'))\n    end\n\n    it \"should create the last run file with the correct mode\" do\n      expect(Puppet.settings.setting(:lastrunfile)).to receive(:mode).and_return('664')\n      configurer.save_last_run_summary(report)\n\n      if Puppet::Util::Platform.windows?\n        require 'puppet/util/windows/security'\n        mode = Puppet::Util::Windows::Security.get_mode(Puppet[:lastrunfile])\n      else\n        mode = Puppet::FileSystem.stat(Puppet[:lastrunfile]).mode\n      end\n      expect(mode & 0777).to eq(0664)\n    end\n\n    it \"should report invalid last run file permissions\" do\n      expect(Puppet.settings.setting(:lastrunfile)).to receive(:mode).and_return('892')\n\n      configurer.save_last_run_summary(report)\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: /Could not save last run local report.*892 is invalid/))\n    end\n  end\n\n  def expects_pluginsync\n    metadata = \"[{\\\"path\\\":\\\"/etc/puppetlabs/code\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":0,\\\"group\\\":0,\\\"mode\\\":420,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-07-10 14:00:00 -0700\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}]\"\n    stub_request(:get, %r{/puppet/v3/file_metadatas/(plugins|locales)}).to_return(status: 200, body: metadata, headers: {'Content-Type' => 'application/json'})\n\n    # response retains owner/group/mode due to source_permissions => use\n    facts_metadata = \"[{\\\"path\\\":\\\"/etc/puppetlabs/code\\\",\\\"relative_path\\\":\\\".\\\",\\\"links\\\":\\\"follow\\\",\\\"owner\\\":500,\\\"group\\\":500,\\\"mode\\\":493,\\\"checksum\\\":{\\\"type\\\":\\\"ctime\\\",\\\"value\\\":\\\"{ctime}2020-07-10 14:00:00 -0700\\\"},\\\"type\\\":\\\"directory\\\",\\\"destination\\\":null}]\"\n    stub_request(:get, %r{/puppet/v3/file_metadatas/pluginfacts}).to_return(status: 200, body: facts_metadata, headers: {'Content-Type' => 'application/json'})\n  end\n\n  def expects_new_catalog_only(catalog)\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true, check_environment: true)).and_return(catalog)\n    expect(Puppet::Resource::Catalog.indirection).not_to receive(:find).with(anything, hash_including(ignore_terminus: true))\n  end\n\n  def expects_cached_catalog_only(catalog)\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_terminus: true)).and_return(catalog)\n    expect(Puppet::Resource::Catalog.indirection).not_to receive(:find).with(anything, hash_including(ignore_cache: true))\n  end\n\n  def expects_fallback_to_cached_catalog(catalog)\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_return(nil)\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_terminus: true)).and_return(catalog)\n  end\n\n  def expects_fallback_to_new_catalog(catalog)\n    expects_pluginsync\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_terminus: true)).and_return(nil)\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true, check_environment: true)).and_return(catalog)\n  end\n\n  def expects_neither_new_or_cached_catalog\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_return(nil)\n    expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_terminus: true)).and_return(nil)\n  end\n\n  describe \"when retrieving a catalog\" do\n    before do\n      allow(Puppet::Resource::Catalog.indirection).to receive(:terminus_class).and_return(:rest)\n    end\n\n    describe \"and configured to only retrieve a catalog from the cache\" do\n      before do\n        Puppet.settings[:use_cached_catalog] = true\n      end\n\n      it \"should first look in the cache for a catalog\" do\n        expects_cached_catalog_only(catalog)\n\n        configurer.run\n      end\n\n      it \"should not pluginsync when a cached catalog is successfully retrieved\" do\n        expects_cached_catalog_only(catalog)\n        expect(configurer).not_to receive(:download_plugins)\n\n        configurer.run\n      end\n\n      it \"should set its cached_catalog_status to 'explicitly_requested'\" do\n        expects_cached_catalog_only(catalog)\n\n        options = {}\n        configurer.run(options)\n\n        expect(options[:report].cached_catalog_status).to eq('explicitly_requested')\n      end\n\n      it \"should set its cached_catalog_status to 'explicitly requested' if the cached catalog is from a different environment\" do\n        cached_catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote('second_env'))\n        expects_cached_catalog_only(cached_catalog)\n\n        options = {}\n        configurer.run(options)\n\n        expect(options[:report].cached_catalog_status).to eq('explicitly_requested')\n      end\n\n      it \"should pluginsync and compile a new catalog if none is found in the cache\" do\n        Puppet[:ignore_plugin_errors] = true\n\n        expects_fallback_to_new_catalog(catalog)\n        stub_request(:get, %r{/puppet/v3/file_metadatas?/plugins}).to_return(:status => 404)\n        stub_request(:get, %r{/puppet/v3/file_metadatas?/pluginfacts}).to_return(:status => 404)\n\n        options = {}\n        configurer.run(options)\n\n        expect(options[:report].cached_catalog_status).to eq('not_used')\n      end\n\n      it \"should not attempt to retrieve a cached catalog again if the first attempt failed\" do\n        Puppet[:ignore_plugin_errors] = true\n\n        expects_neither_new_or_cached_catalog\n        expects_pluginsync\n\n        # after failing to use a cached catalog, we'll need to pluginsync before getting\n        # a new catalog, which also fails.\n        stub_request(:get, %r{/puppet/v3/file_metadatas?/plugins}).to_return(:status => 404)\n        stub_request(:get, %r{/puppet/v3/file_metadatas?/pluginfacts}).to_return(:status => 404)\n\n        configurer.run\n      end\n\n      it \"should return the cached catalog when the environment doesn't match\" do\n        cached_catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote('second_env'))\n        expects_cached_catalog_only(cached_catalog)\n\n        allow(Puppet).to receive(:info)\n        expect(Puppet).to receive(:info).with(\"Using cached catalog from environment 'second_env'\")\n\n        configurer.run\n      end\n\n      it \"applies the catalog passed as options when the catalog cache terminus is not set\" do\n        expects_pluginsync\n\n        catalog.add_resource(Puppet::Resource.new('notify', 'from apply'))\n        configurer.run(catalog: catalog.to_ral)\n\n        # make sure cache class is not set to avoid surprises later\n        expect(Puppet::Resource::Catalog.indirection).to_not be_cache\n        expect(@logs).to include(an_object_having_attributes(level: :notice, message: /defined 'message' as 'from apply'/))\n      end\n\n      it \"applies the cached catalog when the catalog cache terminus is set, ignoring the catalog passed as options\" do\n        Puppet::Resource::Catalog.indirection.cache_class = :json\n\n        cached_catalog = Puppet::Resource::Catalog.new(Puppet[:node_name_value], Puppet[:environment])\n        cached_catalog.add_resource(Puppet::Resource.new('notify', 'from cache'))\n\n        # update cached catalog\n        Puppet.settings.use(:main, :agent)\n        path = Puppet::Resource::Catalog.indirection.cache.path(cached_catalog.name)\n        FileUtils.mkdir(File.dirname(path))\n        File.write(path, cached_catalog.render(:json))\n\n        configurer.run(catalog: catalog.to_ral)\n\n        expect(@logs).to include(an_object_having_attributes(level: :notice, message: /defined 'message' as 'from cache'/))\n      end\n    end\n\n    describe \"and strict environment mode is set\" do\n      before do\n        Puppet.settings[:strict_environment_mode] = true\n      end\n\n      it \"should return nil when the catalog's environment doesn't match the agent specified environment\" do\n        Puppet[:environment] = 'second_env'\n        configurer = Puppet::Configurer.new\n\n        catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote(\"production\"))\n        expects_new_catalog_only(catalog)\n\n        expect(Puppet).to receive(:err).with(\"Not using catalog because its environment 'production' does not match agent specified environment 'second_env' and strict_environment_mode is set\")\n        expect(configurer.run).to be_nil\n      end\n\n      it \"should return 0 when the catalog's environment matches the agent specified environment\" do\n        expects_new_catalog_only(catalog)\n\n        expect(configurer.run).to eq(0)\n      end\n\n      describe \"and a cached catalog is explicitly requested\" do\n        before do\n          Puppet.settings[:use_cached_catalog] = true\n        end\n\n        it \"should return nil when the cached catalog's environment doesn't match the agent specified environment\" do\n          Puppet[:environment] = 'second_env'\n          configurer = Puppet::Configurer.new\n\n          catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote(\"production\"))\n          expects_cached_catalog_only(catalog)\n\n          expect(Puppet).to receive(:err).with(\"Not using catalog because its environment 'production' does not match agent specified environment 'second_env' and strict_environment_mode is set\")\n          expect(configurer.run).to be_nil\n        end\n\n        it \"should proceed with the cached catalog if its environment matches the local environment\" do\n          expects_cached_catalog_only(catalog)\n\n          expect(configurer.run).to eq(0)\n        end\n      end\n    end\n\n    it \"should set its cached_catalog_status to 'not_used' when downloading a new catalog\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_return(catalog)\n\n      options = {}\n      configurer.run(options)\n\n      expect(options[:report].cached_catalog_status).to eq('not_used')\n    end\n\n    it \"should use its node_name_value to retrieve the catalog\" do\n      myhost_facts = Puppet::Node::Facts.new(\"myhost.domain.com\")\n      Puppet::Node::Facts.indirection.save(myhost_facts)\n\n      Puppet.settings[:node_name_value] = \"myhost.domain.com\"\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(\"myhost.domain.com\", anything).and_return(catalog)\n\n      configurer.run\n    end\n\n    it \"should log when no catalog can be retrieved from the server\" do\n      expects_fallback_to_cached_catalog(catalog)\n\n      allow(Puppet).to receive(:info)\n      expect(Puppet).to receive(:info).with(\"Using cached catalog from environment 'production'\")\n      configurer.run\n    end\n\n    it \"should set its cached_catalog_status to 'on_failure' when no catalog can be retrieved from the server\" do\n      expects_fallback_to_cached_catalog(catalog)\n\n      options = {}\n      configurer.run(options)\n\n      expect(options[:report].cached_catalog_status).to eq('on_failure')\n    end\n\n    it \"should not look in the cache for a catalog if one is returned from the server\" do\n      expects_new_catalog_only(catalog)\n\n      configurer.run\n    end\n\n    it \"should return the cached catalog when retrieving the remote catalog throws an exception\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_raise(\"eh\")\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_terminus: true)).and_return(catalog)\n\n      configurer.run\n    end\n\n    it \"should set its cached_catalog_status to 'on_failure' when retrieving the remote catalog throws an exception\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_raise(\"eh\")\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_terminus: true)).and_return(catalog)\n\n      options = {}\n      configurer.run(options)\n\n      expect(options[:report].cached_catalog_status).to eq('on_failure')\n    end\n\n    it \"should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled\" do\n      Puppet[:usecacheonfailure] = false\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_return(nil)\n\n      expect(Puppet).to receive(:warning).with('Not using cache on failed catalog')\n\n      expect(configurer.run).to be_nil\n    end\n\n    it \"should set its cached_catalog_status to 'not_used' if no catalog can be retrieved from the server and :usecacheonfailure is disabled or fails to retrieve a catalog\" do\n      Puppet[:usecacheonfailure] = false\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).with(anything, hash_including(ignore_cache: true)).and_return(nil)\n\n      options = {}\n      configurer.run(options)\n\n      expect(options[:report].cached_catalog_status).to eq('not_used')\n    end\n\n    it \"should return nil if no cached catalog is available and no catalog can be retrieved from the server\" do\n      expects_neither_new_or_cached_catalog\n\n      expect(configurer.run).to be_nil\n    end\n\n    it \"should return nil if its cached catalog environment doesn't match server-specified environment\" do\n      cached_catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote('second_env'))\n\n      expects_fallback_to_cached_catalog(cached_catalog)\n\n      allow(Puppet).to receive(:err)\n      expect(Puppet).to receive(:err).with(\"Not using cached catalog because its environment 'second_env' does not match 'production'\")\n      expect(configurer.run).to be_nil\n    end\n\n    it \"should set its cached_catalog_status to 'not_used' if the cached catalog environment doesn't match server-specified environment\" do\n      cached_catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote('second_env'))\n\n      expects_fallback_to_cached_catalog(cached_catalog)\n\n      options = {}\n      configurer.run(options)\n      expect(options[:report].cached_catalog_status).to eq('not_used')\n    end\n\n    it \"should set its cached_catalog_status to 'on_failure' if the cached catalog environment matches server-specified environment\" do\n      expects_fallback_to_cached_catalog(catalog)\n\n      options = {}\n      configurer.run(options)\n      expect(options[:report].cached_catalog_status).to eq('on_failure')\n    end\n\n    it \"should not update the cached catalog in noop mode\" do\n      Puppet[:noop] = true\n\n      stub_request(:post, %r{/puppet/v3/catalog}).to_return(:status => 200, :body => catalog.render(:json), :headers => {'Content-Type' => 'application/json'})\n\n      Puppet::Resource::Catalog.indirection.cache_class = :json\n      path = Puppet::Resource::Catalog.indirection.cache.path(catalog.name)\n\n      expect(File).to_not be_exist(path)\n      configurer.run\n      expect(File).to_not be_exist(path)\n    end\n\n    it \"should update the cached catalog when not in noop mode\" do\n      Puppet[:noop] = false\n      Puppet[:log_level] = 'info'\n\n      stub_request(:post, %r{/puppet/v3/catalog}).to_return(:status => 200, :body => catalog.render(:json), :headers => {'Content-Type' => 'application/json'})\n\n      Puppet::Resource::Catalog.indirection.cache_class = :json\n      cache_path = Puppet::Resource::Catalog.indirection.cache.path(Puppet[:node_name_value])\n\n      expect(File).to_not be_exist(cache_path)\n      configurer.run\n      expect(File).to be_exist(cache_path)\n\n      expect(@logs).to include(an_object_having_attributes(level: :info, message: \"Caching catalog for #{Puppet[:node_name_value]}\"))\n    end\n\n    it \"successfully applies the catalog without a cache\" do\n      stub_request(:post, %r{/puppet/v3/catalog}).to_return(:status => 200, :body => catalog.render(:json), :headers => {'Content-Type' => 'application/json'})\n\n      Puppet::Resource::Catalog.indirection.cache_class = nil\n\n      expect(configurer.run).to eq(0)\n    end\n\n    it \"should not update the cached catalog when running puppet apply\" do\n      Puppet::Resource::Catalog.indirection.cache_class = :json\n      path = Puppet::Resource::Catalog.indirection.cache.path(catalog.name)\n\n      expect(File).to_not be_exist(path)\n      configurer.run(catalog: catalog)\n      expect(File).to_not be_exist(path)\n    end\n  end\n\n  describe \"when converging the environment\" do\n    let(:apple) { Puppet::Resource::Catalog.new(Puppet[:node_name_value], Puppet::Node::Environment.remote('apple')) }\n    let(:banana) { Puppet::Resource::Catalog.new(Puppet[:node_name_value], Puppet::Node::Environment.remote('banana')) }\n\n    before :each do\n      apple.add_resource(resource)\n      banana.add_resource(resource)\n    end\n\n    it \"converges after multiple attempts\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(apple, banana, banana)\n\n      allow(Puppet).to receive(:notice)\n      allow(Puppet).to receive(:push_context)\n      expect(Puppet).to receive(:notice).with(\"Local environment: 'production' doesn't match server specified environment 'apple', restarting agent run with environment 'apple'\")\n      expect(Puppet).to receive(:notice).with(\"Local environment: 'apple' doesn't match server specified environment 'banana', restarting agent run with environment 'banana'\")\n\n      expect(Puppet).to receive(:push_context).with(\n        hash_including(current_environment: an_object_having_attributes(name: :production)),\n        'Local node environment production for configurer transaction'\n      )\n      expect(Puppet).to receive(:push_context).with(\n        hash_including(current_environment: an_object_having_attributes(name: :apple)),\n        'Local node environment apple for configurer transaction'\n      )\n      expect(Puppet).to receive(:push_context).with(\n        hash_including(current_environment: an_object_having_attributes(name: :banana)),\n        'Local node environment banana for configurer transaction'\n      )\n\n      configurer.run\n    end\n\n    it \"raises if it can't converge after 4 tries after the initial catalog request\" do\n      expect(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(apple, banana, apple, banana, apple)\n\n      configurer.run\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: \"Failed to apply catalog: Catalog environment didn't stabilize after 4 fetches, aborting run\"))\n    end\n  end\n\n  describe \"when converting the catalog\" do\n    it \"converts Puppet::Resource into Puppet::Type::Notify\" do\n      expect(configurer).to receive(:apply_catalog) do |ral, _|\n        expect(ral.resources).to contain(an_instance_of(Puppet::Type::Notify))\n      end\n\n      configurer.run(catalog: catalog)\n    end\n\n    it \"adds default schedules\" do\n      expect(configurer).to receive(:apply_catalog) do |ral, _|\n        expect(ral.resources.map(&:to_ref)).to contain(%w{Schedule[puppet] Schedule[hourly] Schedule[daily] Schedule[weekly] Schedule[monthly] Schedule[never]})\n      end\n\n      configurer.run\n    end\n\n    it \"records the retrieval duration to the catalog\" do\n      expect(configurer).to receive(:apply_catalog) do |ral, _|\n        expect(ral.retrieval_duration).to be_an_instance_of(Float)\n      end\n\n      configurer.run\n    end\n\n    it \"writes the class file containing applied settings classes\" do\n      expect(File).to_not be_exist(Puppet[:classfile])\n\n      configurer.run\n\n      expect(File.read(Puppet[:classfile]).chomp).to eq('settings')\n    end\n\n    it \"writes an empty resource file since no resources are 'managed'\" do\n      expect(File).to_not be_exist(Puppet[:resourcefile])\n\n      configurer.run\n\n      expect(File.read(Puppet[:resourcefile]).chomp).to eq(\"\")\n    end\n\n    it \"adds the conversion time to the report\" do\n      configurer.run(report: report)\n\n      expect(report.metrics['time']['convert_catalog']).to be_an_instance_of(Float)\n    end\n  end\n\n  describe \"when determining whether to pluginsync\" do\n    it \"should be true if use_cached_catalog is false\" do\n      Puppet.settings[:use_cached_catalog] = false\n\n      expect(described_class).to be_should_pluginsync\n    end\n\n    it \"should be false if use_cached_catalog is true\" do\n      Puppet.settings[:use_cached_catalog] = true\n\n      expect(described_class).not_to be_should_pluginsync\n    end\n  end\n\n  describe \"when attempting failover\" do\n    it \"should not failover if server_list is not set\" do\n      Puppet.settings[:server_list] = []\n      configurer.run\n    end\n\n    it \"should not failover during an apply run\" do\n      Puppet.settings[:server_list] = [\"myserver:123\"]\n      catalog = Puppet::Resource::Catalog.new(node_name, Puppet::Node::Environment.remote(Puppet[:environment].to_sym))\n      configurer.run(catalog: catalog)\n    end\n\n    it \"should select a server when it receives 200 OK response\" do\n      Puppet.settings[:server_list] = [\"myserver:123\"]\n\n      stub_request(:get, 'https://myserver:123/status/v1/simple/server').to_return(status: 200)\n\n      options = {}\n      configurer.run(options)\n      expect(options[:report].server_used).to eq('myserver:123')\n    end\n\n    it \"should report when usecacheonfailure is false and server is unavailable\" do\n      Puppet.settings[:server_list] = [\"myserver:123\"]\n      Puppet[:usecacheonfailure] = false\n\n      stub_request(:get, 'https://myserver:123/status/v1/simple/server').to_return(status: [500, \"Internal Server Error\"])\n\n      expect {\n        configurer.run\n      }.to raise_error(Puppet::Error, /Could not select a functional puppet server from server_list:/)\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: /Puppet server myserver:123 is unavailable: 500 Internal Server Error/))\n    end\n\n    it \"should error when no servers in 'server_list' are reachable\" do\n      Puppet.settings[:server_list] = \"myserver:123,someotherservername\"\n      Puppet[:usecacheonfailure] = false\n\n      stub_request(:get, 'https://myserver:123/status/v1/simple/server').to_return(status: 400)\n      stub_request(:get, 'https://someotherservername:8140/status/v1/simple/server').to_return(status: 400)\n\n      expect{\n        configurer.run\n      }.to raise_error(Puppet::Error, /Could not select a functional puppet server from server_list: 'myserver:123,someotherservername'/)\n    end\n\n    it \"should warn when servers in 'server_list' are unreachable\" do\n      Puppet.settings[:server_list] = \"mybadserver1:123,mybadserver2:123,mygoodserver\"\n      Puppet[:usecacheonfailure] = false\n\n      stub_request(:get, 'https://mybadserver1:123/status/v1/simple/server').and_raise(Puppet::HTTP::HTTPError)\n      stub_request(:get, 'https://mybadserver2:123/status/v1/simple/server').and_raise(Puppet::HTTP::HTTPError)\n      stub_request(:get, 'https://mygoodserver:8140/status/v1/simple/server').to_return(status: 200)\n\n      expect(Puppet).to receive(:warning).with(/^Unable to connect to server from server_list setting:.*Trying with next server from server_list.$/).twice\n      configurer.run\n    end\n\n    it \"should warn when servers in 'server_list' respond with error\" do\n      Puppet.settings[:server_list] = \"mybadserver:123,someotherservername\"\n      Puppet[:usecacheonfailure] = false\n\n      stub_request(:get, 'https://mybadserver:123/status/v1/simple/server').to_return(status: 400)\n      stub_request(:get, 'https://someotherservername:8140/status/v1/simple/server').to_return(status: 200)\n\n      expect(Puppet).to receive(:warning).with(/^Puppet server mybadserver:123 is unavailable: 400  Trying with next server from server_list.$/)\n      configurer.run\n    end\n\n    it \"should not error when usecacheonfailure is true and no servers in 'server_list' are reachable\" do\n      Puppet.settings[:server_list] = \"myserver:123,someotherservername\"\n      Puppet[:usecacheonfailure] = true\n\n      stub_request(:get, 'https://myserver:123/status/v1/simple/server').to_return(status: 400)\n      stub_request(:get, 'https://someotherservername:8140/status/v1/simple/server').to_return(status: 400)\n\n      options = {}\n\n      expect(configurer.run(options)).to eq(0)\n      expect(options[:report].server_used).to be_nil\n    end\n  end\n\n  describe \"when selecting an environment\" do\n    include PuppetSpec::Settings\n\n    describe \"when the last used environment is available\" do\n      let(:last_server_specified_environment) { 'development' }\n\n      before do\n        Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)\n        ---\n        version:\n          config: 1624882680\n          puppet: 6.24.0\n        application:\n          initial_environment: #{Puppet[:environment]}\n          converged_environment: #{last_server_specified_environment}\n          run_mode: agent\n        SUMMARY\n      end\n\n      describe \"when the use_last_environment is set to true\" do\n        before do\n          expect(Puppet::Node.indirection).not_to receive(:find)\n          .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))\n        end\n\n        it \"prefers the environment set via cli\" do\n          Puppet.settings.handlearg('--environment', 'usethis')\n          configurer.run\n\n          expect(configurer.environment).to eq('usethis')\n        end\n\n        it \"prefers the environment set via lastrunfile over config\" do\n          FileUtils.mkdir_p(Puppet[:confdir])\n          set_puppet_conf(Puppet[:confdir], <<~CONF)\n          [main]\n          environment = usethis\n          lastrunfile = #{Puppet[:lastrunfile]}\n          CONF\n\n          Puppet.initialize_settings\n          configurer.run\n\n          expect(configurer.environment).to eq(last_server_specified_environment)\n        end\n\n        it \"uses the environment from Puppet[:environment] if given a catalog\" do\n          configurer.run(catalog: catalog)\n\n          expect(configurer.environment).to eq(Puppet[:environment])\n        end\n\n        it \"uses the environment from Puppet[:environment] if use_cached_catalog = true\" do\n          Puppet[:use_cached_catalog] = true\n          expects_cached_catalog_only(catalog)\n          configurer.run\n\n          expect(configurer.environment).to eq(Puppet[:environment])\n        end\n\n        describe \"when the environment is not set via CLI\" do\n          it \"uses the environment found in lastrunfile if the key exists\" do\n            configurer.run\n\n            expect(configurer.environment).to eq(last_server_specified_environment)\n          end\n\n          it \"pushes the converged environment found in lastrunfile over the existing context\" do\n            initial_env = Puppet::Node::Environment.remote('production')\n            Puppet.push_context(\n              current_environment: initial_env,\n              loaders: Puppet::Pops::Loaders.new(initial_env, true))\n\n            expect(Puppet).to receive(:push_context).with(\n              hash_including(:current_environment, :loaders),\n              \"Local node environment #{last_server_specified_environment} for configurer transaction\"\n            ).once.and_call_original\n\n            configurer.run\n          end\n\n          it \"uses the environment from Puppet[:environment] if strict_environment_mode is set\" do\n            Puppet[:strict_environment_mode] = true\n            configurer.run\n\n            expect(configurer.environment).to eq(Puppet[:environment])\n          end\n\n          it \"uses the environment from Puppet[:environment] if initial_environment is the same as converged_environment\" do\n            Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)\n            ---\n            version:\n              config: 1624882680\n              puppet: 6.24.0\n            application:\n              initial_environment: development\n              converged_environment: development\n              run_mode: agent\n            SUMMARY\n            configurer.run\n\n            expect(configurer.environment).to eq(Puppet[:environment])\n          end\n        end\n      end\n\n      describe \"when the use_last_environment setting is set to false\" do\n        let(:node_environment) { Puppet::Node::Environment.remote(:salam) }\n        let(:node) { Puppet::Node.new(Puppet[:node_name_value]) }\n\n        before do\n          Puppet[:use_last_environment] = false\n          node.environment = node_environment\n\n          allow(Puppet::Node.indirection).to receive(:find)\n          allow(Puppet::Node.indirection).to receive(:find)\n            .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))\n            .and_return(node)\n        end\n\n        it \"does a node request\" do\n          expect(Puppet::Node.indirection).to receive(:find)\n          .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))\n\n          configurer.run\n        end\n\n        it \"uses the node environment from the node request\" do\n          configurer.run\n\n          expect(configurer.environment).to eq(node_environment.name.to_s)\n        end\n      end\n    end\n\n    describe \"when the last used environment is not available\" do\n      describe \"when the node request succeeds\" do\n        let(:node_environment) { Puppet::Node::Environment.remote(:salam) }\n        let(:node) { Puppet::Node.new(Puppet[:node_name_value]) }\n        let(:last_server_specified_environment) { 'development' }\n\n        before do\n          node.environment = node_environment\n\n          allow(Puppet::Node.indirection).to receive(:find)\n          allow(Puppet::Node.indirection).to receive(:find)\n            .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))\n            .and_return(node)\n        end\n\n        it \"uses the environment from the node request if the run mode doesn't match\" do\n          Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)\n            ---\n            version:\n              config: 1624882680\n              puppet: 6.24.0\n            application:\n              initial_environment: #{Puppet[:environment]}\n              converged_environment: #{last_server_specified_environment}\n              run_mode: user\n          SUMMARY\n          configurer.run\n\n          expect(configurer.environment).to eq(node_environment.name.to_s)\n        end\n\n        it \"uses the environment from the node request if lastrunfile does not contain the expected keys\" do\n          Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)\n            ---\n            version:\n              config: 1624882680\n              puppet: 6.24.0\n          SUMMARY\n          configurer.run\n\n          expect(configurer.environment).to eq(node_environment.name.to_s)\n        end\n\n        it \"uses the environment from the node request if lastrunfile is invalid YAML\" do\n          Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)\n            Key: 'this is my very very very ' +\n                 'long string'\n          SUMMARY\n          configurer.run\n\n          expect(configurer.environment).to eq(node_environment.name.to_s)\n        end\n\n        it \"uses the environment from the node request if lastrunfile exists but is empty\" do\n          Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', '')\n          configurer.run\n\n          expect(configurer.environment).to eq(node_environment.name.to_s)\n        end\n\n        it \"uses the environment from the node request if the last used one cannot be found\" do\n          Puppet[:lastrunfile] = tmpfile('last_run_summary.yaml')\n          configurer.run\n\n          expect(configurer.environment).to eq(node_environment.name.to_s)\n        end\n      end\n\n      describe \"when the node request fails\" do\n        before do\n          allow(Puppet::Node.indirection).to receive(:find).and_call_original\n          allow(Puppet::Node.indirection).to receive(:find)\n            .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))\n            .and_raise(Puppet::Error)\n        end\n\n        it \"uses the environment from Puppet[:environment] if the last used one cannot be found\" do\n          Puppet[:lastrunfile] = tmpfile('last_run_summary.yaml')\n          configurer.run\n\n          expect(configurer.environment).to eq(Puppet[:environment])\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine/exists_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine/exists'\n\ndescribe Puppet::Confine::Exists do\n  before do\n    @confine = Puppet::Confine::Exists.new(\"/my/file\")\n    @confine.label = \"eh\"\n  end\n\n  it \"should be named :exists\" do\n    expect(Puppet::Confine::Exists.name).to eq(:exists)\n  end\n  \n  it \"should not pass if exists is nil\" do\n    confine = Puppet::Confine::Exists.new(nil)\n    confine.label = \":exists => nil\"\n    expect(confine).to receive(:pass?).with(nil)\n    expect(confine).not_to be_valid\n  end\n\n  it \"should use the 'pass?' method to test validity\" do\n    expect(@confine).to receive(:pass?).with(\"/my/file\")\n    @confine.valid?\n  end\n\n  it \"should return false if the value is false\" do\n    expect(@confine.pass?(false)).to be_falsey\n  end\n\n  it \"should return false if the value does not point to a file\" do\n    expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/file\").and_return(false)\n    expect(@confine.pass?(\"/my/file\")).to be_falsey\n  end\n\n  it \"should return true if the value points to a file\" do\n    expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/file\").and_return(true)\n    expect(@confine.pass?(\"/my/file\")).to be_truthy\n  end\n\n  it \"should produce a message saying that a file is missing\" do\n    expect(@confine.message(\"/my/file\")).to be_include(\"does not exist\")\n  end\n\n  describe \"and the confine is for binaries\" do\n    before do\n      allow(@confine).to receive(:for_binary).and_return(true)\n    end\n\n    it \"should use its 'which' method to look up the full path of the file\" do\n      expect(@confine).to receive(:which).and_return(nil)\n      @confine.pass?(\"/my/file\")\n    end\n\n    it \"should return false if no executable can be found\" do\n      expect(@confine).to receive(:which).with(\"/my/file\").and_return(nil)\n      expect(@confine.pass?(\"/my/file\")).to be_falsey\n    end\n\n    it \"should return true if the executable can be found\" do\n      expect(@confine).to receive(:which).with(\"/my/file\").and_return(\"/my/file\")\n      expect(@confine.pass?(\"/my/file\")).to be_truthy\n    end\n  end\n\n  it \"should produce a summary containing all missing files\" do\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    expect(Puppet::FileSystem).to receive(:exist?).with(\"/two\").and_return(false)\n    expect(Puppet::FileSystem).to receive(:exist?).with(\"/four\").and_return(false)\n\n    confine = Puppet::Confine::Exists.new %w{/one /two /three /four}\n    expect(confine.summary).to eq(%w{/two /four})\n  end\n\n  it \"should summarize multiple instances by returning a flattened array of their summaries\" do\n    c1 = double('1', :summary => %w{one})\n    c2 = double('2', :summary => %w{two})\n    c3 = double('3', :summary => %w{three})\n\n    expect(Puppet::Confine::Exists.summarize([c1, c2, c3])).to eq(%w{one two three})\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine/false_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine/false'\n\ndescribe Puppet::Confine::False do\n  it \"should be named :false\" do\n    expect(Puppet::Confine::False.name).to eq(:false)\n  end\n\n  it \"should require a value\" do\n    expect { Puppet::Confine.new }.to raise_error(ArgumentError)\n  end\n\n  describe \"when passing in a lambda as a value for lazy evaluation\" do\n    it \"should accept it\" do\n      confine = Puppet::Confine::False.new(lambda { false })\n      expect(confine.values).to eql([false])\n    end\n\n    describe \"when enforcing cache-positive behavior\" do\n      def cached_value_of(confine)\n        confine.instance_variable_get(:@cached_value)\n      end\n\n      it \"should cache a false value\" do\n        confine = Puppet::Confine::False.new(lambda { false })\n        confine.values\n\n        expect(cached_value_of(confine)).to eql([false])\n      end\n\n      it \"should not cache a true value\" do\n        confine = Puppet::Confine::False.new(lambda { true })\n        confine.values\n\n        expect(cached_value_of(confine)).to be_nil\n      end\n    end\n  end\n\n  describe \"when testing values\" do\n    before { @confine = Puppet::Confine::False.new(\"foo\") }\n\n    it \"should use the 'pass?' method to test validity\" do\n      @confine = Puppet::Confine::False.new(\"foo\")\n      @confine.label = \"eh\"\n      expect(@confine).to receive(:pass?).with(\"foo\")\n      @confine.valid?\n    end\n\n    it \"should return true if the value is false\" do\n      expect(@confine.pass?(false)).to be_truthy\n    end\n\n    it \"should return false if the value is not false\" do\n      expect(@confine.pass?(\"else\")).to be_falsey\n    end\n\n    it \"should produce a message that a value is true\" do\n      @confine = Puppet::Confine::False.new(\"foo\")\n      expect(@confine.message(\"eh\")).to be_include(\"true\")\n    end\n  end\n\n  it \"should be able to produce a summary with the number of incorrectly true values\" do\n    confine = Puppet::Confine::False.new %w{one two three four}\n    expect(confine).to receive(:pass?).exactly(4).times.and_return(true, false, true, false)\n    expect(confine.summary).to eq(2)\n  end\n\n  it \"should summarize multiple instances by summing their summaries\" do\n    c1 = double('1', :summary => 1)\n    c2 = double('2', :summary => 2)\n    c3 = double('3', :summary => 3)\n\n    expect(Puppet::Confine::False.summarize([c1, c2, c3])).to eq(6)\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine/feature_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine/feature'\n\ndescribe Puppet::Confine::Feature do\n  it \"should be named :feature\" do\n    expect(Puppet::Confine::Feature.name).to eq(:feature)\n  end\n\n  it \"should require a value\" do\n    expect { Puppet::Confine::Feature.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should always convert values to an array\" do\n    expect(Puppet::Confine::Feature.new(\"/some/file\").values).to be_instance_of(Array)\n  end\n\n  describe \"when testing values\" do\n    before do\n      @confine = Puppet::Confine::Feature.new(\"myfeature\")\n      @confine.label = \"eh\"\n    end\n\n    it \"should use the Puppet features instance to test validity\" do\n      Puppet.features.add(:myfeature) do true end\n      @confine.valid?\n    end\n\n    it \"should return true if the feature is present\" do\n      Puppet.features.add(:myfeature) do true end\n      expect(@confine.pass?(\"myfeature\")).to be_truthy\n    end\n\n    it \"should return false if the value is false\" do\n      Puppet.features.add(:myfeature) do false end\n      expect(@confine.pass?(\"myfeature\")).to be_falsey\n    end\n\n    it \"should log that a feature is missing\" do\n      expect(@confine.message(\"myfeat\")).to be_include(\"missing\")\n    end\n  end\n\n  it \"should summarize multiple instances by returning a flattened array of all missing features\" do\n    confines = []\n    confines << Puppet::Confine::Feature.new(%w{one two})\n    confines << Puppet::Confine::Feature.new(%w{two})\n    confines << Puppet::Confine::Feature.new(%w{three four})\n\n    features = double('feature')\n    allow(features).to receive(:one?)\n    allow(features).to receive(:two?)\n    allow(features).to receive(:three?)\n    allow(features).to receive(:four?)\n    allow(Puppet).to receive(:features).and_return(features)\n\n    expect(Puppet::Confine::Feature.summarize(confines).sort).to eq(%w{one two three four}.sort)\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine/true_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine/true'\n\ndescribe Puppet::Confine::True do\n  it \"should be named :true\" do\n    expect(Puppet::Confine::True.name).to eq(:true)\n  end\n\n  it \"should require a value\" do\n    expect { Puppet::Confine::True.new }.to raise_error(ArgumentError)\n  end\n\n  describe \"when passing in a lambda as a value for lazy evaluation\" do\n    it \"should accept it\" do\n      confine = Puppet::Confine::True.new(lambda { true })\n      expect(confine.values).to eql([true])\n    end\n\n    describe \"when enforcing cache-positive behavior\" do\n      def cached_value_of(confine)\n        confine.instance_variable_get(:@cached_value)\n      end\n\n      it \"should cache a true value\" do\n        confine = Puppet::Confine::True.new(lambda { true })\n        confine.values\n\n        expect(cached_value_of(confine)).to eql([true])\n      end\n\n      it \"should not cache a false value\" do\n        confine = Puppet::Confine::True.new(lambda { false })\n        confine.values\n\n        expect(cached_value_of(confine)).to be_nil\n      end\n    end\n  end\n\n  describe \"when testing values\" do\n    before do\n      @confine = Puppet::Confine::True.new(\"foo\")\n      @confine.label = \"eh\"\n    end\n\n    it \"should use the 'pass?' method to test validity\" do\n      expect(@confine).to receive(:pass?).with(\"foo\")\n      @confine.valid?\n    end\n\n    it \"should return true if the value is not false\" do\n      expect(@confine.pass?(\"else\")).to be_truthy\n    end\n\n    it \"should return false if the value is false\" do\n      expect(@confine.pass?(nil)).to be_falsey\n    end\n\n    it \"should produce the message that a value is false\" do\n      expect(@confine.message(\"eh\")).to be_include(\"false\")\n    end\n  end\n\n  it \"should produce the number of false values when asked for a summary\" do\n    @confine = Puppet::Confine::True.new %w{one two three four}\n    expect(@confine).to receive(:pass?).exactly(4).times.and_return(true, false, true, false)\n    expect(@confine.summary).to eq(2)\n  end\n\n  it \"should summarize multiple instances by summing their summaries\" do\n    c1 = double('1', :summary => 1)\n    c2 = double('2', :summary => 2)\n    c3 = double('3', :summary => 3)\n\n    expect(Puppet::Confine::True.summarize([c1, c2, c3])).to eq(6)\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine/variable_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine/variable'\n\ndescribe Puppet::Confine::Variable do\n  it \"should be named :variable\" do\n    expect(Puppet::Confine::Variable.name).to eq(:variable)\n  end\n\n  it \"should require a value\" do\n    expect { Puppet::Confine::Variable.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should always convert values to an array\" do\n    expect(Puppet::Confine::Variable.new(\"/some/file\").values).to be_instance_of(Array)\n  end\n\n  it \"should have an accessor for its name\" do\n    expect(Puppet::Confine::Variable.new(:bar)).to respond_to(:name)\n  end\n\n  describe \"when testing values\" do\n    before do\n      @confine = Puppet::Confine::Variable.new(\"foo\")\n      @confine.name = :myvar\n    end\n\n    it \"should use settings if the variable name is a valid setting\" do\n      expect(Puppet.settings).to receive(:valid?).with(:myvar).and_return(true)\n      expect(Puppet.settings).to receive(:value).with(:myvar).and_return(\"foo\")\n      @confine.valid?\n    end\n\n    it \"should use Facter if the variable name is not a valid setting\" do\n      expect(Puppet.settings).to receive(:valid?).with(:myvar).and_return(false)\n      expect(Facter).to receive(:value).with(:myvar).and_return(\"foo\")\n      @confine.valid?\n    end\n\n    it \"should be valid if the value matches the facter value\" do\n      expect(@confine).to receive(:test_value).and_return(\"foo\")\n\n      expect(@confine).to be_valid\n    end\n\n    it \"should return false if the value does not match the facter value\" do\n      expect(@confine).to receive(:test_value).and_return(\"fee\")\n\n      expect(@confine).not_to be_valid\n    end\n\n    it \"should be case insensitive\" do\n      expect(@confine).to receive(:test_value).and_return(\"FOO\")\n\n      expect(@confine).to be_valid\n    end\n\n    it \"should not care whether the value is a string or symbol\" do\n      expect(@confine).to receive(:test_value).and_return(\"FOO\")\n\n      expect(@confine).to be_valid\n    end\n\n    it \"should produce a message that the fact value is not correct\" do\n      @confine = Puppet::Confine::Variable.new(%w{bar bee})\n      @confine.name = \"eh\"\n      message = @confine.message(\"value\")\n      expect(message).to be_include(\"facter\")\n      expect(message).to be_include(\"bar,bee\")\n    end\n\n    it \"should be valid if the test value matches any of the provided values\" do\n      @confine = Puppet::Confine::Variable.new(%w{bar bee})\n      expect(@confine).to receive(:test_value).and_return(\"bee\")\n      expect(@confine).to be_valid\n    end\n  end\n\n  describe \"when summarizing multiple instances\" do\n    it \"should return a hash of failing variables and their values\" do\n      c1 = Puppet::Confine::Variable.new(\"one\")\n      c1.name = \"uno\"\n      expect(c1).to receive(:valid?).and_return(false)\n      c2 = Puppet::Confine::Variable.new(\"two\")\n      c2.name = \"dos\"\n      expect(c2).to receive(:valid?).and_return(true)\n      c3 = Puppet::Confine::Variable.new(\"three\")\n      c3.name = \"tres\"\n      expect(c3).to receive(:valid?).and_return(false)\n\n      expect(Puppet::Confine::Variable.summarize([c1, c2, c3])).to eq({\"uno\" => %w{one}, \"tres\" => %w{three}})\n    end\n\n    it \"should combine the values of multiple confines with the same fact\" do\n      c1 = Puppet::Confine::Variable.new(\"one\")\n      c1.name = \"uno\"\n      expect(c1).to receive(:valid?).and_return(false)\n      c2 = Puppet::Confine::Variable.new(\"two\")\n      c2.name = \"uno\"\n      expect(c2).to receive(:valid?).and_return(false)\n\n      expect(Puppet::Confine::Variable.summarize([c1, c2])).to eq({\"uno\" => %w{one two}})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine_collection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine_collection'\n\ndescribe Puppet::ConfineCollection do\n  it \"should be able to add confines\" do\n    expect(Puppet::ConfineCollection.new(\"label\")).to respond_to(:confine)\n  end\n\n  it \"should require a label at initialization\" do\n    expect { Puppet::ConfineCollection.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should make its label available\" do\n    expect(Puppet::ConfineCollection.new(\"mylabel\").label).to eq(\"mylabel\")\n  end\n\n  describe \"when creating confine instances\" do\n    it \"should create an instance of the named test with the provided values\" do\n      test_class = double('test_class')\n      expect(test_class).to receive(:new).with(%w{my values}).and_return(double('confine', :label= => nil))\n      expect(Puppet::Confine).to receive(:test).with(:foo).and_return(test_class)\n\n      Puppet::ConfineCollection.new(\"label\").confine :foo => %w{my values}\n    end\n\n    it \"should copy its label to the confine instance\" do\n      confine = double('confine')\n      test_class = double('test_class')\n      expect(test_class).to receive(:new).and_return(confine)\n      expect(Puppet::Confine).to receive(:test).and_return(test_class)\n\n      expect(confine).to receive(:label=).with(\"label\")\n\n      Puppet::ConfineCollection.new(\"label\").confine :foo => %w{my values}\n    end\n\n    describe \"and the test cannot be found\" do\n      it \"should create a Facter test with the provided values and set the name to the test name\" do\n        confine = Puppet::Confine.test(:variable).new(%w{my values})\n        expect(confine).to receive(:name=).with(:foo)\n        expect(confine.class).to receive(:new).with(%w{my values}).and_return(confine)\n        Puppet::ConfineCollection.new(\"label\").confine(:foo => %w{my values})\n      end\n    end\n\n    describe \"and the 'for_binary' option was provided\" do\n      it \"should mark the test as a binary confine\" do\n        confine = Puppet::Confine.test(:exists).new(:bar)\n        expect(confine).to receive(:for_binary=).with(true)\n        expect(Puppet::Confine.test(:exists)).to receive(:new).with(:bar).and_return(confine)\n        Puppet::ConfineCollection.new(\"label\").confine :exists => :bar, :for_binary => true\n      end\n    end\n  end\n\n  it \"should be valid if no confines are present\" do\n    expect(Puppet::ConfineCollection.new(\"label\")).to be_valid\n  end\n\n  it \"should be valid if all confines pass\" do\n    c1 = double('c1', :valid? => true, :label= => nil)\n    c2 = double('c2', :valid? => true, :label= => nil)\n\n    expect(Puppet::Confine.test(:true)).to receive(:new).and_return(c1)\n    expect(Puppet::Confine.test(:false)).to receive(:new).and_return(c2)\n\n    confiner = Puppet::ConfineCollection.new(\"label\")\n    confiner.confine :true => :bar, :false => :bee\n\n    expect(confiner).to be_valid\n  end\n\n  it \"should not be valid if any confines fail\" do\n    c1 = double('c1', :valid? => true, :label= => nil)\n    c2 = double('c2', :valid? => false, :label= => nil)\n\n    expect(Puppet::Confine.test(:true)).to receive(:new).and_return(c1)\n    expect(Puppet::Confine.test(:false)).to receive(:new).and_return(c2)\n\n    confiner = Puppet::ConfineCollection.new(\"label\")\n    confiner.confine :true => :bar, :false => :bee\n\n    expect(confiner).not_to be_valid\n  end\n\n  describe \"when providing a summary\" do\n    before do\n      @confiner = Puppet::ConfineCollection.new(\"label\")\n    end\n\n    it \"should return a hash\" do\n      expect(@confiner.summary).to be_instance_of(Hash)\n    end\n\n    it \"should return an empty hash if the confiner is valid\" do\n      expect(@confiner.summary).to eq({})\n    end\n\n    it \"should add each test type's summary to the hash\" do\n      @confiner.confine :true => :bar, :false => :bee\n      expect(Puppet::Confine.test(:true)).to receive(:summarize).and_return(:tsumm)\n      expect(Puppet::Confine.test(:false)).to receive(:summarize).and_return(:fsumm)\n\n      expect(@confiner.summary).to eq({:true => :tsumm, :false => :fsumm})\n    end\n\n    it \"should not include tests that return 0\" do\n      @confiner.confine :true => :bar, :false => :bee\n      expect(Puppet::Confine.test(:true)).to receive(:summarize).and_return(0)\n      expect(Puppet::Confine.test(:false)).to receive(:summarize).and_return(:fsumm)\n\n      expect(@confiner.summary).to eq({:false => :fsumm})\n    end\n\n    it \"should not include tests that return empty arrays\" do\n      @confiner.confine :true => :bar, :false => :bee\n      expect(Puppet::Confine.test(:true)).to receive(:summarize).and_return([])\n      expect(Puppet::Confine.test(:false)).to receive(:summarize).and_return(:fsumm)\n\n      expect(@confiner.summary).to eq({:false => :fsumm})\n    end\n\n    it \"should not include tests that return empty hashes\" do\n      @confiner.confine :true => :bar, :false => :bee\n      expect(Puppet::Confine.test(:true)).to receive(:summarize).and_return({})\n      expect(Puppet::Confine.test(:false)).to receive(:summarize).and_return(:fsumm)\n\n      expect(@confiner.summary).to eq({:false => :fsumm})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/confine_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confine'\n\nclass Puppet::TestConfine < Puppet::Confine\n  def pass?(value)\n    false\n  end\nend\n\ndescribe Puppet::Confine do\n  it \"should require a value\" do\n    expect { Puppet::Confine.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should always convert values to an array\" do\n    expect(Puppet::Confine.new(\"/some/file\").values).to be_instance_of(Array)\n  end\n\n  it \"should have a 'true' test\" do\n    expect(Puppet::Confine.test(:true)).to be_instance_of(Class)\n  end\n\n  it \"should have a 'false' test\" do\n    expect(Puppet::Confine.test(:false)).to be_instance_of(Class)\n  end\n\n  it \"should have a 'feature' test\" do\n    expect(Puppet::Confine.test(:feature)).to be_instance_of(Class)\n  end\n\n  it \"should have an 'exists' test\" do\n    expect(Puppet::Confine.test(:exists)).to be_instance_of(Class)\n  end\n\n  it \"should have a 'variable' test\" do\n    expect(Puppet::Confine.test(:variable)).to be_instance_of(Class)\n  end\n\n  describe \"when testing all values\" do\n    before do\n      @confine = Puppet::TestConfine.new(%w{a b c})\n      @confine.label = \"foo\"\n    end\n\n    it \"should be invalid if any values fail\" do\n      allow(@confine).to receive(:pass?).and_return(true)\n      expect(@confine).to receive(:pass?).with(\"b\").and_return(false)\n      expect(@confine).not_to be_valid\n    end\n\n    it \"should be valid if all values pass\" do\n      allow(@confine).to receive(:pass?).and_return(true)\n      expect(@confine).to be_valid\n    end\n\n    it \"should short-cut at the first failing value\" do\n      expect(@confine).to receive(:pass?).once.and_return(false)\n      @confine.valid?\n    end\n\n    it \"should log failing confines with the label and message\" do\n      Puppet[:log_level] = 'debug'\n      allow(@confine).to receive(:pass?).and_return(false)\n      expect(@confine).to receive(:message).and_return(\"My message\")\n      expect(@confine).to receive(:label).and_return(\"Mylabel\")\n      expect(Puppet).to receive(:debug) { |&b| expect(b.call).to eq(\"Mylabel: My message\") }\n      @confine.valid?\n    end\n  end\n\n  describe \"when testing the result of the values\" do\n    before { @confine = Puppet::TestConfine.new(%w{a b c d}) }\n\n    it \"should return an array with the result of the test for each value\" do\n      allow(@confine).to receive(:pass?).and_return(true)\n      expect(@confine).to receive(:pass?).with(\"b\").and_return(false)\n      expect(@confine).to receive(:pass?).with(\"d\").and_return(false)\n\n      expect(@confine.result).to eq([true, false, true, false])\n    end\n  end\n\n  describe \"when requiring\" do\n    it \"does not cache failed requires when always_retry_plugins is true\" do\n      Puppet[:always_retry_plugins] = true\n      expect(Puppet::Confine).to receive(:require).with('puppet/confine/os.family').twice.and_raise(LoadError)\n      Puppet::Confine.test('os.family')\n      Puppet::Confine.test('os.family')\n    end\n\n    it \"caches failed requires when always_retry_plugins is false\" do\n      Puppet[:always_retry_plugins] = false\n      expect(Puppet::Confine).to receive(:require).with('puppet/confine/os.family').once.and_raise(LoadError)\n      Puppet::Confine.test('os.family')\n      Puppet::Confine.test('os.family')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/confiner_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/confiner'\n\ndescribe Puppet::Confiner do\n  let(:coll) { Puppet::ConfineCollection.new('') }\n\n  before do\n    @object = Object.new\n    @object.extend(Puppet::Confiner)\n  end\n\n  it \"should have a method for defining confines\" do\n    expect(@object).to respond_to(:confine)\n  end\n\n  it \"should have a method for returning its confine collection\" do\n    expect(@object).to respond_to(:confine_collection)\n  end\n\n  it \"should have a method for testing suitability\" do\n    expect(@object).to respond_to(:suitable?)\n  end\n\n  it \"should delegate its confine method to its confine collection\" do\n    allow(@object).to receive(:confine_collection).and_return(coll)\n    expect(coll).to receive(:confine).with({:foo => :bar, :bee => :baz})\n    @object.confine(:foo => :bar, :bee => :baz)\n  end\n\n  it \"should create a new confine collection if one does not exist\" do\n    expect(Puppet::ConfineCollection).to receive(:new).with(\"mylabel\").and_return(\"mycoll\")\n    expect(@object).to receive(:to_s).and_return(\"mylabel\")\n    expect(@object.confine_collection).to eq(\"mycoll\")\n  end\n\n  it \"should reuse the confine collection\" do\n    expect(@object.confine_collection).to equal(@object.confine_collection)\n  end\n\n  describe \"when testing suitability\" do\n    before do\n      allow(@object).to receive(:confine_collection).and_return(coll)\n    end\n\n    it \"should return true if the confine collection is valid\" do\n      expect(coll).to receive(:valid?).and_return(true)\n      expect(@object).to be_suitable\n    end\n\n    it \"should return false if the confine collection is invalid\" do\n      expect(coll).to receive(:valid?).and_return(false)\n      expect(@object).not_to be_suitable\n    end\n\n    it \"should return the summary of the confine collection if a long result is asked for\" do\n      expect(coll).to receive(:summary).and_return(\"myresult\")\n      expect(@object.suitable?(false)).to eq(\"myresult\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/context/trusted_information_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/certificate_factory'\n\nrequire 'puppet/context/trusted_information'\n\ndescribe Puppet::Context::TrustedInformation, :unless => RUBY_PLATFORM == 'java' do\n  let(:key) { OpenSSL::PKey::RSA.new(Puppet[:keylength]) }\n\n  let(:csr) do\n    csr = Puppet::SSL::CertificateRequest.new(\"csr\")\n    csr.generate(key, :extension_requests => {\n      '1.3.6.1.4.1.15.1.2.1' => 'Ignored CSR extension',\n\n      '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',\n      '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',\n    })\n    csr\n  end\n\n  let(:cert) do\n    cert = Puppet::SSL::Certificate.from_instance(Puppet::CertificateFactory.build('ca', csr, csr.content, 1))\n\n    # The cert must be signed so that it can be successfully be DER-decoded later\n    signer = Puppet::SSL::CertificateSigner.new\n    signer.sign(cert.content, key)\n    cert\n  end\n\n  let(:external_data) {\n    {\n      'string' => 'a',\n      'integer' => 1,\n      'boolean' => true,\n      'hash' => { 'one' => 'two' },\n      'array' => ['b', 2, {}]\n    }\n  }\n\n  def allow_external_trusted_data(certname, data)\n    command = 'generate_data.sh'\n    Puppet[:trusted_external_command] = command\n    # The expand_path bit is necessary b/c :trusted_external_command is a\n    # file_or_directory setting, and file_or_directory settings automatically\n    # expand the given path.\n    allow(Puppet::Util::Execution).to receive(:execute).with([File.expand_path(command), certname], anything).and_return(JSON.dump(data))\n  end\n\n  it \"defaults external to an empty hash\" do\n    trusted = Puppet::Context::TrustedInformation.new(false, 'ignored', nil)\n\n    expect(trusted.external).to eq({})\n  end\n\n  context \"when remote\" do\n    it \"has no cert information when it isn't authenticated\" do\n      trusted = Puppet::Context::TrustedInformation.remote(false, 'ignored', nil)\n\n      expect(trusted.authenticated).to eq(false)\n      expect(trusted.certname).to be_nil\n      expect(trusted.extensions).to eq({})\n    end\n\n    it \"is remote and has certificate information when it is authenticated\" do\n      trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert)\n\n      expect(trusted.authenticated).to eq('remote')\n      expect(trusted.certname).to eq('cert name')\n      expect(trusted.extensions).to eq({\n        '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',\n        '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',\n      })\n      expect(trusted.hostname).to eq('cert name')\n      expect(trusted.domain).to be_nil\n    end\n\n    it \"is remote but lacks certificate information when it is authenticated\" do\n      expect(Puppet).to receive(:info).once.with(\"TrustedInformation expected a certificate, but none was given.\")\n\n      trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', nil)\n\n      expect(trusted.authenticated).to eq('remote')\n      expect(trusted.certname).to eq('cert name')\n      expect(trusted.extensions).to eq({})\n    end\n\n    it 'contains external trusted data' do\n      allow_external_trusted_data('cert name', external_data)\n\n      trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', nil)\n\n      expect(trusted.external).to eq(external_data)\n    end\n\n    it 'does not run the trusted external command when creating a trusted context' do\n      Puppet[:trusted_external_command] = '/usr/bin/generate_data.sh'\n\n      expect(Puppet::Util::Execution).to receive(:execute).never\n      Puppet::Context::TrustedInformation.remote(true, 'cert name', cert)\n    end\n\n    it 'only runs the trusted external command the first time it is invoked' do\n      command = 'generate_data.sh'\n      Puppet[:trusted_external_command] = command\n\n      # See allow_external_trusted_data to understand why expand_path is necessary\n      expect(Puppet::Util::Execution).to receive(:execute).with([File.expand_path(command), 'cert name'], anything).and_return(JSON.dump(external_data)).once\n\n      trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert)\n      trusted.external\n      trusted.external\n    end\n  end\n\n  context \"when local\" do\n    it \"is authenticated local with the nodes clientcert\" do\n      node = Puppet::Node.new('testing', :parameters => { 'clientcert' => 'cert name' })\n\n      trusted = Puppet::Context::TrustedInformation.local(node)\n\n      expect(trusted.authenticated).to eq('local')\n      expect(trusted.certname).to eq('cert name')\n      expect(trusted.extensions).to eq({})\n      expect(trusted.hostname).to eq('cert name')\n      expect(trusted.domain).to be_nil\n    end\n\n    it \"is authenticated local with no clientcert when there is no node\" do\n      trusted = Puppet::Context::TrustedInformation.local(nil)\n\n      expect(trusted.authenticated).to eq('local')\n      expect(trusted.certname).to be_nil\n      expect(trusted.extensions).to eq({})\n      expect(trusted.hostname).to be_nil\n      expect(trusted.domain).to be_nil\n    end\n\n    it 'contains external trusted data' do\n      allow_external_trusted_data('cert name', external_data)\n\n      trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', nil)\n\n      expect(trusted.external).to eq(external_data)\n    end\n  end\n\n  it \"converts itself to a hash\" do\n    trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert)\n\n    expect(trusted.to_h).to eq({\n      'authenticated' => 'remote',\n      'certname' => 'cert name',\n      'extensions' => {\n        '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',\n        '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',\n      },\n      'hostname' => 'cert name',\n      'domain' => nil,\n      'external' => {},\n    })\n  end\n\n  it \"extracts domain and hostname from certname\" do\n    trusted = Puppet::Context::TrustedInformation.remote(true, 'hostname.domain.long', cert)\n\n    expect(trusted.to_h).to eq({\n      'authenticated' => 'remote',\n      'certname' => 'hostname.domain.long',\n      'extensions' => {\n        '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',\n        '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',\n      },\n      'hostname' => 'hostname',\n      'domain' => 'domain.long',\n      'external' => {},\n    })\n  end\n\n  it \"freezes the hash\" do\n    trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert)\n\n    expect(trusted.to_h).to be_deeply_frozen\n  end\n\n  matcher :be_deeply_frozen do\n    match do |actual|\n      unfrozen_items(actual).empty?\n    end\n\n    failure_message do |actual|\n      \"expected all items to be frozen but <#{unfrozen_items(actual).join(', ')}> was not\"\n    end\n\n    define_method :unfrozen_items do |actual|\n      unfrozen = []\n      stack = [actual]\n      while item = stack.pop\n        if !item.frozen?\n          unfrozen.push(item)\n        end\n\n        case item\n        when Hash\n          stack.concat(item.keys)\n          stack.concat(item.values)\n        when Array\n          stack.concat(item)\n        end\n      end\n\n      unfrozen\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/context_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Context do\n  let(:context) { Puppet::Context.new({ :testing => \"value\" }) }\n\n  describe \"with additional context\" do\n    before :each do\n      context.push(\"a\" => 1)\n    end\n\n    it \"allows rebinding values in a nested context\" do\n      inner = nil\n      context.override(\"a\" => 2) do\n        inner = context.lookup(\"a\")\n      end\n\n      expect(inner).to eq(2)\n    end\n\n    it \"outer bindings are available in an overridden context\" do\n      inner_a = nil\n      inner_b = nil\n      context.override(\"b\" => 2) do\n        inner_a = context.lookup(\"a\")\n        inner_b = context.lookup(\"b\")\n      end\n\n      expect(inner_a).to eq(1)\n      expect(inner_b).to eq(2)\n    end\n\n    it \"overridden bindings do not exist outside of the override\" do\n      context.override(\"a\" => 2) do\n      end\n\n      expect(context.lookup(\"a\")).to eq(1)\n    end\n\n    it \"overridden bindings do not exist outside of the override even when leaving via an error\" do\n      begin\n        context.override(\"a\" => 2) do\n          raise \"this should still cause the bindings to leave\"\n        end\n      rescue\n      end\n\n      expect(context.lookup(\"a\")).to eq(1)\n    end\n  end\n\n  context \"a rollback\" do\n    it \"returns to the mark\" do\n      context.push(\"a\" => 1)\n      context.mark(\"start\")\n      context.push(\"a\" => 2)\n      context.push(\"a\" => 3)\n      context.pop\n\n      context.rollback(\"start\")\n\n      expect(context.lookup(\"a\")).to eq(1)\n    end\n\n    it \"rolls back to the mark across a scoped override\" do\n      context.push(\"a\" => 1)\n      context.mark(\"start\")\n      context.override(\"a\" => 3) do\n\n        context.rollback(\"start\")\n\n        expect(context.lookup(\"a\")).to eq(1)\n      end\n      expect(context.lookup(\"a\")).to eq(1)\n    end\n\n    it \"fails to rollback to an unknown mark\" do\n      expect do\n        context.rollback(\"unknown\")\n      end.to raise_error(Puppet::Context::UnknownRollbackMarkError)\n    end\n\n    it \"does not allow the same mark to be set twice\" do\n      context.mark(\"duplicate\")\n      expect do\n        context.mark(\"duplicate\")\n      end.to raise_error(Puppet::Context::DuplicateRollbackMarkError)\n    end\n  end\n\n  context \"with multiple threads\" do\n    it \"a value pushed in another thread is not seen in the original thread\" do\n      context.push(a: 1)\n      t = Thread.new do\n        context.push(a: 2, b: 5)\n      end\n      t.join\n\n      expect(context.lookup(:a)).to eq(1)\n      expect{ context.lookup(:b) }.to raise_error(Puppet::Context::UndefinedBindingError)\n    end\n\n    it \"pops on a different thread do not interfere\" do\n      context.push(a: 1)\n      t = Thread.new do\n        context.pop\n      end\n      t.join\n\n      # Raises exception if the binding we pushed has already been popped\n      context.pop\n    end\n\n    it \"a mark in one thread is not seen in another thread\" do\n      t = Thread.new do\n        context.push(b: 2)\n        context.mark('point b')\n      end\n      t.join\n\n      expect { context.rollback('point b') }.to raise_error(Puppet::Context::UnknownRollbackMarkError)\n    end\n  end\nend\n\n\ndescribe Puppet::Context::EmptyStack do\n  let(:empty_stack) { Puppet::Context::EmptyStack.new }\n\n  it \"raises undefined binding on lookup\" do\n    expect { empty_stack.lookup(\"a\") }.to raise_error(Puppet::Context::UndefinedBindingError)\n  end\n\n  it \"calls a provided block for a default value when none is found\" do\n    expect(empty_stack.lookup(\"a\") { \"default\" }).to eq(\"default\")\n  end\n\n  it \"raises an error when trying to pop\" do\n    expect { empty_stack.pop }.to raise_error(Puppet::Context::StackUnderflow)\n  end\n\n  it \"returns a stack when something is pushed\" do\n    stack = empty_stack.push(a: 1)\n    expect(stack).to be_a(Puppet::Context::Stack)\n  end\n\n  it \"returns a new stack with no bindings when pushed nil\" do\n    stack = empty_stack.push(nil)\n    expect(stack).not_to be(empty_stack)\n    expect(stack.pop).to be(empty_stack)\n  end\nend\n\ndescribe Puppet::Context::Stack do\n  let(:empty_stack) { Puppet::Context::EmptyStack.new }\n\n  context \"a stack with depth of 1\" do\n    let(:stack) { empty_stack.push(a: 1) }\n\n    it \"returns the empty stack when popped\" do\n      expect(stack.pop).to be(empty_stack)\n    end\n\n    it \"calls a provided block for a default value when none is found\" do\n      expect(stack.lookup(\"a\") { \"default\" }).to eq(\"default\")\n    end\n\n    it \"returns a new but equivalent stack when pushed nil\" do\n      stackier = stack.push(nil)\n      expect(stackier).not_to be(stack)\n      expect(stackier.pop).to be(stack)\n      expect(stackier.bindings).to eq(stack.bindings)\n    end\n  end\n\n  context \"a stack with more than 1 element\" do\n    let(:level_one) { empty_stack.push(a: 1, c: 4) }\n    let(:level_two) { level_one.push(b: 2, c: 3) }\n\n    it \"falls back to lower levels on lookup\" do\n      expect(level_two.lookup(:c)).to eq(3)\n      expect(level_two.lookup(:a)).to eq(1)\n      expect{ level_two.lookup(:d) }.to raise_error(Puppet::Context::UndefinedBindingError)\n    end\n\n    it \"the parent is immutable\" do\n      expect(level_one.lookup(:c)).to eq(4)\n      expect{ level_one.lookup(:b) }.to raise_error(Puppet::Context::UndefinedBindingError)\n    end\n  end\n\n  context 'supports lazy entries' do\n    it 'by evaluating a bound proc' do\n      stack = empty_stack.push(a: lambda { || 'yay' })\n      expect(stack.lookup(:a)).to eq('yay')\n    end\n\n    it 'by memoizing the bound value' do\n      original = 'yay'\n      stack = empty_stack.push(:a => lambda {|| tmp = original; original = 'no'; tmp})\n      expect(stack.lookup(:a)).to eq('yay')\n      expect(original).to eq('no')\n      expect(stack.lookup(:a)).to eq('yay')\n    end\n\n    it 'the bound value is memoized only at the top level of the stack' do\n      # I'm just characterizing the current behavior here\n\n      original = 'yay'\n      stack = empty_stack.push(:a => lambda {|| tmp = original; original = 'no'; tmp})\n      stack_two = stack.push({})\n      expect(stack.lookup(:a)).to eq('yay')\n      expect(original).to eq('no')\n      expect(stack.lookup(:a)).to eq('yay')\n      expect(stack_two.lookup(:a)).to eq('no')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/daemon_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/daemon'\nrequire 'puppet/agent'\nrequire 'puppet/configurer'\n\ndescribe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n\n  class RecordingScheduler\n    attr_reader :jobs\n\n    def run_loop(jobs)\n      @jobs = jobs\n    end\n  end\n\n  let(:agent) { Puppet::Agent.new(Puppet::Configurer, false) }\n  let(:server) { double(\"Server\", :start => nil, :wait_for_shutdown => nil) }\n\n  let(:pidfile) { double(\"PidFile\", :lock => true, :unlock => true, :file_path => 'fake.pid') }\n  let(:scheduler) { RecordingScheduler.new }\n\n  let(:daemon) { Puppet::Daemon.new(agent, pidfile, scheduler) }\n\n  before(:each) do\n    allow(Signal).to receive(:trap)\n    allow(daemon).to receive(:close_streams).and_return(nil)\n  end\n\n  it \"should fail when no agent is provided\" do\n    expect { Puppet::Daemon.new(nil, pidfile, scheduler) }.to raise_error(Puppet::DevError)\n  end\n\n  it \"should reopen the Log logs when told to reopen logs\" do\n    expect(Puppet::Util::Log).to receive(:reopen)\n    daemon.reopen_logs\n  end\n\n  describe \"when setting signal traps\" do\n    [:INT, :TERM].each do |signal|\n      it \"logs a notice and exits when sent #{signal}\" do\n        allow(Signal).to receive(:trap).with(signal).and_yield\n        expect(Puppet).to receive(:notice).with(\"Caught #{signal}; exiting\")\n        expect(daemon).to receive(:stop)\n\n        daemon.set_signal_traps\n      end\n    end\n\n    {:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method|\n      it \"logs a notice and remembers to call #{method} when it receives #{signal}\" do\n        allow(Signal).to receive(:trap).with(signal).and_yield\n        expect(Puppet).to receive(:notice).with(\"Caught #{signal}; storing #{method}\")\n\n        daemon.set_signal_traps\n\n        expect(daemon.signals).to eq([method])\n      end\n    end\n  end\n\n  describe \"when starting\" do\n    let(:reparse_run) { scheduler.jobs[0] }\n    let(:agent_run) { scheduler.jobs[1] }\n\n    before do\n      allow(daemon).to receive(:set_signal_traps)\n    end\n\n    it \"should create its pidfile\" do\n      expect(pidfile).to receive(:lock).and_return(true)\n      daemon.start\n    end\n\n    it \"should fail if it cannot lock\" do\n      expect(pidfile).to receive(:lock).and_return(false)\n      expect { daemon.start }.to raise_error(RuntimeError, \"Could not create PID file: #{pidfile.file_path}\")\n    end\n\n    it \"disables the reparse of configs if the filetimeout is 0\" do\n      Puppet[:filetimeout] = 0\n      daemon.start\n      expect(reparse_run).not_to be_enabled\n    end\n\n    it \"does not splay the agent run by default\" do\n      daemon.start\n      expect(agent_run.splay).to eq(0)\n    end\n\n    describe \"and calculating splay\" do\n      before do\n        # Set file timeout so the daemon reparses\n        Puppet[:filetimeout] = 1\n        Puppet[:splay] = true\n      end\n\n      it \"recalculates when splaylimit changes\" do\n        daemon.start\n\n        Puppet[:splaylimit] = 60\n        init_splay = agent_run.splay\n        next_splay = init_splay + 1\n        allow(agent_run).to receive(:rand).and_return(next_splay)\n        reparse_run.run(Time.now)\n\n        expect(agent_run.splay).to eq(next_splay)\n      end\n\n      it \"does not change splay if splaylimit is unmodified\" do\n        daemon.start\n\n        init_splay = agent_run.splay\n        reparse_run.run(Time.now)\n\n        expect(agent_run.splay).to eq(init_splay)\n      end\n\n      it \"recalculates when splay is enabled later\" do\n        Puppet[:splay] = false\n        daemon.start\n\n        Puppet[:splay] = true\n        allow(agent_run).to receive(:rand).and_return(999)\n        reparse_run.run(Time.now)\n\n        expect(agent_run.splay).to eq(999)\n      end\n\n      it \"sets splay to 0 when splay is disabled\" do\n        daemon.start\n\n        Puppet[:splay] = false\n        reparse_run.run(Time.now)\n\n        expect(agent_run.splay).to eq(0)\n      end\n\n      it \"recalculates splay when runinterval is decreased\" do\n        Puppet[:runinterval] = 60\n        daemon.start\n\n        Puppet[:runinterval] = Puppet[:runinterval] - 30\n        new_splay = agent_run.splay + 1\n        allow(agent_run).to receive(:rand).and_return(new_splay)\n        reparse_run.run(Time.now)\n\n        expect(agent_run.splay).to eq(new_splay)\n      end\n\n      it \"recalculates splay when runinterval is increased\" do\n        Puppet[:runinterval] = 60\n        daemon.start\n\n        Puppet[:runinterval] = Puppet[:runinterval] + 30\n        new_splay = agent_run.splay - 1\n        allow(agent_run).to receive(:rand).and_return(new_splay)\n        reparse_run.run(Time.now)\n\n        expect(agent_run.splay).to eq(new_splay)\n      end\n    end\n  end\n\n  describe \"when stopping\" do\n    before do\n      allow(Puppet::Util::Log).to receive(:close_all)\n      # to make the global safe to mock, set it to a subclass of itself\n      stub_const('Puppet::Application', Class.new(Puppet::Application))\n    end\n\n    it 'should request a stop from Puppet::Application' do\n      expect(Puppet::Application).to receive(:stop!)\n      expect { daemon.stop }.to exit_with 0\n    end\n\n    it \"should remove its pidfile\" do\n      expect(pidfile).to receive(:unlock)\n      expect { daemon.stop }.to exit_with 0\n    end\n\n    it \"should close all logs\" do\n      expect(Puppet::Util::Log).to receive(:close_all)\n      expect { daemon.stop }.to exit_with 0\n    end\n\n    it \"should exit unless called with ':exit => false'\" do\n      expect { daemon.stop }.to exit_with 0\n    end\n\n    it \"should not exit if called with ':exit => false'\" do\n      daemon.stop :exit => false\n    end\n  end\n\n  describe \"when reloading\" do\n    it \"should do nothing if the agent is running\" do\n      expect(agent).to receive(:run).with({:splay => false}).and_raise(Puppet::LockError, 'Failed to aquire lock')\n      expect(Puppet).to receive(:notice).with('Not triggering already-running agent')\n\n      daemon.reload\n    end\n\n    it \"should run the agent if one is available and it is not running\" do\n      expect(agent).to receive(:run).with({:splay => false})\n      expect(Puppet).not_to receive(:notice).with('Not triggering already-running agent')\n\n      daemon.reload\n    end\n  end\n\n  describe \"when restarting\" do\n    before do\n      stub_const('Puppet::Application', Class.new(Puppet::Application))\n    end\n\n    it 'should set Puppet::Application.restart!' do\n      expect(Puppet::Application).to receive(:restart!)\n      allow(daemon).to receive(:reexec)\n      daemon.restart\n    end\n\n    it \"should reexec itself if no agent is available\" do\n      expect(daemon).to receive(:reexec)\n      daemon.restart\n    end\n\n    it \"should reexec itself if the agent is not running\" do\n      expect(daemon).to receive(:reexec)\n      daemon.restart\n    end\n  end\n\n  describe \"when reexecing it self\" do\n    before do\n      allow(daemon).to receive(:exec)\n      allow(daemon).to receive(:stop)\n    end\n\n    it \"should fail if no argv values are available\" do\n      expect(daemon).to receive(:argv).and_return(nil)\n      expect { daemon.reexec }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should shut down without exiting\" do\n      daemon.argv = %w{foo}\n      expect(daemon).to receive(:stop).with({:exit => false})\n      daemon.reexec\n    end\n\n    it \"should call 'exec' with the original executable and arguments\" do\n      daemon.argv = %w{foo}\n      expect(daemon).to receive(:exec).with($0 + \" foo\")\n      daemon.reexec\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/data_binding_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/data_binding'\n\ndescribe Puppet::DataBinding do\n  describe \"when indirecting\" do\n    it \"should default to the 'hiera' data_binding terminus\" do\n      Puppet::DataBinding.indirection.reset_terminus_class\n      expect(Puppet::DataBinding.indirection.terminus_class).to eq(:hiera)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/data_providers/function_data_provider_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe \"when using function data provider\" do\n  include PuppetSpec::Compiler\n\n  # There is a fully configured environment in fixtures in this location\n  let(:environmentpath) { parent_fixture('environments') }\n\n  around(:each) do |example|\n    # Initialize settings to get a full compile as close as possible to a real\n    # environment load\n    Puppet.settings.initialize_global_settings\n    # Initialize loaders based on the environmentpath. It does not work to\n    # just set the setting environmentpath for some reason - this achieves the same:\n    # - first a loader is created, loading directory environments from the fixture (there is\n    # one such environment, 'production', which will be loaded since the node references this\n    # environment by name).\n    # - secondly, the created env loader is set as 'environments' in the puppet context.\n    #\n    loader = Puppet::Environments::Directories.new(environmentpath, [])\n    Puppet.override(:environments => loader) do\n      example.run\n    end\n  end\n\n  # The environment configured in the fixture has one module called 'abc'. Its class abc, includes\n  # a class called 'def'. This class has three parameters test1, test2, and test3 and it creates\n  # three notify with name set to the value of the three parameters.\n  #\n  # Since the abc class does not provide any parameter values to its def class, it attempts to\n  # get them from data lookup. The fixture has an environment that is configured to load data from\n  # a function called environment::data, this data sets test1, and test2.\n  # The module 'abc' is configured to get data by calling the function abc::data(), this function\n  # returns data for all three parameters test1-test3, now with the prefix 'module'.\n  #\n  # The result should be that the data set in the environment wins over those set in the\n  # module.\n  #\n  it 'gets data from module and environment functions and combines them with env having higher precedence' do\n    Puppet[:code] = 'include abc'\n    node = Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}), :environment => 'production')\n    compiler = Puppet::Parser::Compiler.new(node)\n    catalog = compiler.compile()\n    resources_created_in_fixture = [\"Notify[env_test1]\", \"Notify[env_test2]\", \"Notify[module_test3]\", \"Notify[env_test2-ipl]\"]\n    expect(resources_in(catalog)).to include(*resources_created_in_fixture)\n  end\n\n  it 'gets data from module having a puppet function delivering module data' do\n    Puppet[:code] = 'include xyz'\n    node = Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}), :environment => 'production')\n    compiler = Puppet::Parser::Compiler.new(node)\n    catalog = compiler.compile()\n    resources_created_in_fixture = [\"Notify[env_test1]\", \"Notify[env_test2]\", \"Notify[module_test3]\"]\n    expect(resources_in(catalog)).to include(*resources_created_in_fixture)\n  end\n\n  it 'gets data from puppet function delivering environment data' do\n    Puppet[:code] = <<-CODE\n      function environment::data() {\n        { 'cls::test1' => 'env_puppet1',\n          'cls::test2' => 'env_puppet2'\n        }\n      }\n      class cls ($test1, $test2) {\n        notify { $test1: }\n        notify { $test2: }\n      }\n      include cls\n    CODE\n    node = Puppet::Node.new('testnode', :facts => Puppet::Node::Facts.new('facts', {}), :environment => 'production')\n    catalog = Puppet::Parser::Compiler.new(node).compile\n    expect(resources_in(catalog)).to include('Notify[env_puppet1]', 'Notify[env_puppet2]')\n  end\n\n  it 'raises an error if the environment data function does not return a hash' do\n    Puppet[:code] = 'include abc'\n    # find the loaders to patch with faulty function\n    node = Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}), :environment => 'production')\n\n    compiler = Puppet::Parser::Compiler.new(node)\n    loaders = compiler.loaders()\n    env_loader = loaders.private_environment_loader()\n    f = Puppet::Functions.create_function('environment::data') do\n      def data()\n        'this is not a hash'\n      end\n    end\n    env_loader.add_entry(:function, 'environment::data', f.new(compiler.topscope, env_loader), nil)\n    expect do\n      compiler.compile()\n    end.to raise_error(/Value returned from deprecated API function 'environment::data' has wrong type/)\n  end\n\n  it 'raises an error if the module data function does not return a hash' do\n    Puppet[:code] = 'include abc'\n    # find the loaders to patch with faulty function\n    node = Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}), :environment => 'production')\n\n    compiler = Puppet::Parser::Compiler.new(node)\n    loaders = compiler.loaders()\n    module_loader = loaders.public_loader_for_module('abc')\n    f = Puppet::Functions.create_function('abc::data') do\n      def data()\n        'this is not a hash'\n      end\n    end\n    module_loader.add_entry(:function, 'abc::data', f.new(compiler.topscope, module_loader), nil)\n    expect do\n      compiler.compile()\n    end.to raise_error(/Value returned from deprecated API function 'abc::data' has wrong type/)\n  end\n\n  def parent_fixture(dir_name)\n    File.absolute_path(File.join(my_fixture_dir(), \"../#{dir_name}\"))\n  end\n\n  def resources_in(catalog)\n    catalog.resources.map(&:ref)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/data_providers/hiera_data_provider_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe \"when using a hiera data provider\" do\n  include PuppetSpec::Compiler\n\n  # There is a fully configured 'sample' environment in fixtures at this location\n  let(:environmentpath) { parent_fixture('environments') }\n\n  let(:facts) { Puppet::Node::Facts.new(\"facts\", {}) }\n\n  around(:each) do |example|\n    # Initialize settings to get a full compile as close as possible to a real\n    # environment load\n    Puppet.settings.initialize_global_settings\n    # Initialize loaders based on the environmentpath. It does not work to\n    # just set the setting environmentpath for some reason - this achieves the same:\n    # - first a loader is created, loading directory environments from the fixture (there is\n    # one environment, 'sample', which will be loaded since the node references this\n    # environment by name).\n    # - secondly, the created env loader is set as 'environments' in the puppet context.\n    #\n    loader = Puppet::Environments::Directories.new(environmentpath, [])\n    Puppet.override(:environments => loader) do\n      example.run\n    end\n  end\n\n  def compile_and_get_notifications(environment, code = nil)\n    extract_notifications(compile(environment, code))\n  end\n\n  def compile(environment, code = nil)\n    Puppet[:code] = code if code\n    node = Puppet::Node.new(\"testnode\", :facts => facts, :environment => environment)\n    compiler = Puppet::Parser::Compiler.new(node)\n    compiler.topscope['domain'] = 'example.com'\n    compiler.topscope['my_fact'] = 'server3'\n    block_given? ? compiler.compile { |catalog| yield(compiler); catalog } : compiler.compile\n  end\n\n  def extract_notifications(catalog)\n    catalog.resources.map(&:ref).select { |r| r.start_with?('Notify[') }.map { |r| r[7..-2] }\n  end\n\n  it 'uses default configuration for environment and module data' do\n    resources = compile_and_get_notifications('hiera_defaults')\n    expect(resources).to include('module data param_a is 100, param default is 200, env data param_c is 300')\n  end\n\n  it 'reads hiera.yaml in environment root and configures multiple json and yaml providers' do\n    resources = compile_and_get_notifications('hiera_env_config')\n    expect(resources).to include(\"env data param_a is 10, env data param_b is 20, env data param_c is 30, env data param_d is 40, env data param_e is 50, env data param_yaml_utf8 is \\u16EB\\u16D2\\u16E6, env data param_json_utf8 is \\u16A0\\u16C7\\u16BB\")\n  end\n\n  it 'reads hiera.yaml in module root and configures multiple json and yaml providers' do\n    resources = compile_and_get_notifications('hiera_module_config')\n    expect(resources).to include('module data param_a is 100, module data param_b is 200, module data param_c is 300, module data param_d is 400, module data param_e is 500')\n  end\n\n  it 'keeps lookup_options in one module separate from lookup_options in another' do\n    resources1 = compile('hiera_modules', 'include one').resources.select {|r| r.ref.start_with?('Class[One]')}\n    resources2 = compile('hiera_modules', 'include two').resources.select {|r| r.ref.start_with?('Class[One]')}\n    expect(resources1).to eq(resources2)\n  end\n\n  it 'does not perform merge of values declared in environment and module when resolving parameters' do\n    resources = compile_and_get_notifications('hiera_misc')\n    expect(resources).to include('env 1, ')\n  end\n\n  it 'performs hash merge of values declared in environment and module' do\n    resources = compile_and_get_notifications('hiera_misc', '$r = lookup(one::test::param, Hash[String,String], hash) notify{\"${r[key1]}, ${r[key2]}\":}')\n    expect(resources).to include('env 1, module 2')\n  end\n\n  it 'performs unique merge of values declared in environment and module' do\n    resources = compile_and_get_notifications('hiera_misc', '$r = lookup(one::array, Array[String], unique) notify{\"${r}\":}')\n    expect(resources.size).to eq(1)\n    expect(resources[0][1..-2].split(', ')).to contain_exactly('first', 'second', 'third', 'fourth')\n  end\n\n  it 'performs merge found in lookup_options in environment of values declared in environment and module' do\n    resources = compile_and_get_notifications('hiera_misc', 'include one::lopts_test')\n    expect(resources.size).to eq(1)\n    expect(resources[0]).to eq('A, B, C, MA, MB, MC')\n  end\n\n  it 'performs merge found in lookup_options in module of values declared in environment and module' do\n    resources = compile_and_get_notifications('hiera_misc', 'include one::loptsm_test')\n    expect(resources.size).to eq(1)\n    expect(resources[0]).to eq('A, B, C, MA, MB, MC')\n  end\n\n  it \"will not find 'lookup_options' as a regular value\" do\n    expect { compile_and_get_notifications('hiera_misc', '$r = lookup(\"lookup_options\")') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value/)\n  end\n\n  it 'does find unqualified keys in the environment' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(ukey1):}')\n    expect(resources).to include('Some value')\n  end\n\n  it 'does not find unqualified keys in the module' do\n    expect do\n      compile_and_get_notifications('hiera_misc', 'notify{lookup(ukey2):}')\n    end.to raise_error(Puppet::ParseError, /did not find a value for the name 'ukey2'/)\n  end\n\n  it 'can use interpolation lookup method \"alias\"' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_alias):}')\n    expect(resources).to include('Value from interpolation with alias')\n  end\n\n  it 'can use interpolation lookup method \"lookup\"' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_lookup):}')\n    expect(resources).to include('Value from interpolation with lookup')\n  end\n\n  it 'can use interpolation lookup method \"hiera\"' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_hiera):}')\n    expect(resources).to include('Value from interpolation with hiera')\n  end\n\n  it 'can use interpolation lookup method \"literal\"' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_literal):}')\n    expect(resources).to include('Value from interpolation with literal')\n  end\n\n  it 'can use interpolation lookup method \"scope\"' do\n    resources = compile_and_get_notifications('hiera_misc', '$target_scope = \"with scope\" notify{lookup(km_scope):}')\n    expect(resources).to include('Value from interpolation with scope')\n  end\n\n  it 'can use interpolation using default lookup method (scope)' do\n    resources = compile_and_get_notifications('hiera_misc', '$target_default = \"with default\" notify{lookup(km_default):}')\n    expect(resources).to include('Value from interpolation with default')\n  end\n\n  it 'performs lookup using qualified expressions in interpolation' do\n    resources = compile_and_get_notifications('hiera_misc', \"$os = { name => 'Fedora' } notify{lookup(km_qualified):}\")\n    expect(resources).to include('Value from qualified interpolation OS = Fedora')\n  end\n\n  it 'can have multiple interpolate expressions in one value' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_multi):}')\n    expect(resources).to include('cluster/%{::cluster}/%{role}')\n  end\n\n  it 'performs single quoted interpolation' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_sqalias):}')\n    expect(resources).to include('Value from interpolation with alias')\n  end\n\n  it 'uses compiler lifecycle for caching' do\n    Puppet[:code] = 'notify{lookup(one::my_var):}'\n    node = Puppet::Node.new('testnode', :facts => facts, :environment => 'hiera_module_config')\n\n    compiler = Puppet::Parser::Compiler.new(node)\n    compiler.topscope['my_fact'] = 'server1'\n    expect(extract_notifications(compiler.compile)).to include('server1')\n\n    compiler = Puppet::Parser::Compiler.new(node)\n    compiler.topscope['my_fact'] = 'server2'\n    expect(extract_notifications(compiler.compile)).to include('server2')\n\n    compiler = Puppet::Parser::Compiler.new(node)\n    compiler.topscope['my_fact'] = 'server3'\n    expect(extract_notifications(compiler.compile)).to include('In name.yaml')\n  end\n\n  it 'traps endless interpolate recursion' do\n    expect do\n      compile_and_get_notifications('hiera_misc', '$r1 = \"%{r2}\" $r2 = \"%{r1}\" notify{lookup(recursive):}')\n    end.to raise_error(Puppet::DataBinding::RecursiveLookupError, /detected in \\[recursive, scope:r1, scope:r2\\]/)\n  end\n\n  it 'does not consider use of same key in the lookup and scope namespaces as recursion' do\n    resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(domain):}')\n    expect(resources).to include('-- example.com --')\n  end\n\n  it 'traps bad alias declarations' do\n    expect do\n      compile_and_get_notifications('hiera_misc', \"$r1 = 'Alias within string %{alias(\\\"r2\\\")}' $r2 = '%{r1}' notify{lookup(recursive):}\")\n    end.to raise_error(Puppet::DataBinding::LookupError, /'alias' interpolation is only permitted if the expression is equal to the entire string/)\n  end\n\n  it 'reports syntax errors for JSON files' do\n    expect do\n      compile_and_get_notifications('hiera_bad_syntax_json')\n    end.to raise_error(Puppet::DataBinding::LookupError, /Unable to parse \\(#{environmentpath}[^)]+\\):/)\n  end\n\n  it 'reports syntax errors for YAML files' do\n    expect do\n      compile_and_get_notifications('hiera_bad_syntax_yaml')\n    end.to raise_error(Puppet::DataBinding::LookupError, /Unable to parse \\(#{environmentpath}[^)]+\\):/)\n  end\n\n  describe 'when using explain' do\n    it 'will report config path (original and resolved), data path (original and resolved), and interpolation (before and after)' do\n      compile('hiera_misc', '$target_scope = \"with scope\"') do |compiler|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)\n        Puppet::Pops::Lookup.lookup('km_scope', nil, nil, nil, nil, lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to include(<<-EOS)\n      Path \"#{environmentpath}/hiera_misc/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Interpolation on \"Value from interpolation %{scope(\"target_scope\")}\"\n          Global Scope\n            Found key: \"target_scope\" value: \"with scope\"\n        Found key: \"km_scope\" value: \"Value from interpolation with scope\"\n          EOS\n      end\n    end\n\n    it 'will report that merge options was found in the lookup_options hash' do\n      compile('hiera_misc', '$target_scope = \"with scope\"') do |compiler|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)\n        Puppet::Pops::Lookup.lookup('one::loptsm_test::hash', nil, nil, nil, nil, lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to include(\"Using merge options from \\\"lookup_options\\\" hash\")\n      end\n    end\n\n    it 'will report lookup_options details in combination with details of found value' do\n      compile('hiera_misc', '$target_scope = \"with scope\"') do |compiler|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, Puppet::Pops::Lookup::Explainer.new(true))\n        Puppet::Pops::Lookup.lookup('one::loptsm_test::hash', nil, nil, nil, nil, lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to eq(<<EOS)\nSearching for \"lookup_options\"\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/hiera_misc/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"one::lopts_test::hash\" => {\n            \"merge\" => \"deep\"\n          }\n        }\n  Module \"one\" Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/hiera_misc/modules/one/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"one::loptsm_test::hash\" => {\n            \"merge\" => \"deep\"\n          }\n        }\n  Merge strategy hash\n    Global and Environment\n      Found key: \"lookup_options\" value: {\n        \"one::lopts_test::hash\" => {\n          \"merge\" => \"deep\"\n        }\n      }\n    Module one\n      Found key: \"lookup_options\" value: {\n        \"one::loptsm_test::hash\" => {\n          \"merge\" => \"deep\"\n        }\n      }\n    Merged result: {\n      \"one::loptsm_test::hash\" => {\n        \"merge\" => \"deep\"\n      },\n      \"one::lopts_test::hash\" => {\n        \"merge\" => \"deep\"\n      }\n    }\nUsing merge options from \"lookup_options\" hash\nSearching for \"one::loptsm_test::hash\"\n  Merge strategy deep\n    Global Data Provider (hiera configuration version 5)\n      No such key: \"one::loptsm_test::hash\"\n    Environment Data Provider (hiera configuration version 5)\n      Hierarchy entry \"Common\"\n        Path \"#{environmentpath}/hiera_misc/data/common.yaml\"\n          Original path: \"common.yaml\"\n          Found key: \"one::loptsm_test::hash\" value: {\n            \"a\" => \"A\",\n            \"b\" => \"B\",\n            \"m\" => {\n              \"ma\" => \"MA\",\n              \"mb\" => \"MB\"\n            }\n          }\n    Module \"one\" Data Provider (hiera configuration version 5)\n      Hierarchy entry \"Common\"\n        Path \"#{environmentpath}/hiera_misc/modules/one/data/common.yaml\"\n          Original path: \"common.yaml\"\n          Found key: \"one::loptsm_test::hash\" value: {\n            \"a\" => \"A\",\n            \"c\" => \"C\",\n            \"m\" => {\n              \"ma\" => \"MA\",\n              \"mc\" => \"MC\"\n            }\n          }\n    Merged result: {\n      \"a\" => \"A\",\n      \"c\" => \"C\",\n      \"m\" => {\n        \"ma\" => \"MA\",\n        \"mc\" => \"MC\",\n        \"mb\" => \"MB\"\n      },\n      \"b\" => \"B\"\n    }\nEOS\n      end\n    end\n\n    it 'will report config path (original and resolved), data path (original and resolved), and interpolation (before and after)' do\n      compile('hiera_misc', '$target_scope = \"with scope\"') do |compiler|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, Puppet::Pops::Lookup::Explainer.new(true, true))\n        Puppet::Pops::Lookup.lookup('one::loptsm_test::hash', nil, nil, nil, nil, lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to eq(<<EOS)\nMerge strategy hash\n  Global Data Provider (hiera configuration version 5)\n    No such key: \"lookup_options\"\n  Environment Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/hiera_misc/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"one::lopts_test::hash\" => {\n            \"merge\" => \"deep\"\n          }\n        }\n  Module \"one\" Data Provider (hiera configuration version 5)\n    Hierarchy entry \"Common\"\n      Path \"#{environmentpath}/hiera_misc/modules/one/data/common.yaml\"\n        Original path: \"common.yaml\"\n        Found key: \"lookup_options\" value: {\n          \"one::loptsm_test::hash\" => {\n            \"merge\" => \"deep\"\n          }\n        }\n  Merged result: {\n    \"one::loptsm_test::hash\" => {\n      \"merge\" => \"deep\"\n    },\n    \"one::lopts_test::hash\" => {\n      \"merge\" => \"deep\"\n    }\n  }\nEOS\n      end\n    end\n  end\n\n  def parent_fixture(dir_name)\n    File.absolute_path(File.join(my_fixture_dir(), \"../#{dir_name}\"))\n  end\n\n  def resources_in(catalog)\n    catalog.resources.map(&:ref)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/datatypes_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\n\nmodule PuppetSpec::DataTypes\ndescribe \"Puppet::DataTypes\" do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  let(:modules) { { 'mytest' => mytest } }\n  let(:datatypes) { {} }\n  let(:environments_dir) { Puppet[:environmentpath] }\n\n  let(:mytest) {{\n    'lib' => {\n      'puppet' => {\n        'datatypes' => mytest_datatypes,\n        'functions' => mytest_functions },\n      'puppetx' => { 'mytest' => mytest_classes },\n    }\n  }}\n\n  let(:mytest_datatypes) { {} }\n  let(:mytest_classes) { {} }\n  let(:mytest_functions) { {\n    'mytest' => {\n      'to_data.rb' => <<-RUBY.unindent,\n        Puppet::Functions.create_function('mytest::to_data') do\n          def to_data(data)\n            Puppet::Pops::Serialization::ToDataConverter.convert(data, {\n              :rich_data => true,\n              :symbol_as_string => true,\n              :type_by_reference => true,\n              :message_prefix => 'test'\n            })\n          end\n        end\n        RUBY\n\n      'from_data.rb' => <<-RUBY.unindent,\n        Puppet::Functions.create_function('mytest::from_data') do\n          def from_data(data)\n            Puppet::Pops::Serialization::FromDataConverter.convert(data)\n          end\n        end\n        RUBY\n\n      'serialize.rb' => <<-RUBY.unindent,\n        Puppet::Functions.create_function('mytest::serialize') do\n          def serialize(data)\n            buffer = ''\n            serializer = Puppet::Pops::Serialization::Serializer.new(\n              Puppet::Pops::Serialization::JSON::Writer.new(buffer))\n            serializer.write(data)\n            serializer.finish\n            buffer\n          end\n        end\n        RUBY\n\n      'deserialize.rb' => <<-RUBY.unindent,\n        Puppet::Functions.create_function('mytest::deserialize') do\n          def deserialize(data)\n            deserializer = Puppet::Pops::Serialization::Deserializer.new(\n              Puppet::Pops::Serialization::JSON::Reader.new(data), Puppet::Pops::Loaders.find_loader(nil))\n            deserializer.read\n          end\n        end\n        RUBY\n      }\n  } }\n\n  let(:testing_env_dir) do\n    dir_contained_in(environments_dir, testing_env)\n    env_dir = File.join(environments_dir, 'testing')\n    PuppetSpec::Files.record_tmp(env_dir)\n    env_dir\n  end\n\n  let(:modules_dir) { File.join(testing_env_dir, 'modules') }\n  let(:env) { Puppet::Node::Environment.create(:testing, [modules_dir]) }\n  let(:node) { Puppet::Node.new('test', :environment => env) }\n\n  let(:testing_env) do\n    {\n      'testing' => {\n        'lib' => { 'puppet' => { 'datatypes' => datatypes } },\n        'modules' => modules,\n      }\n    }\n  end\n\n  before(:each) do\n    Puppet[:environment] = 'testing'\n  end\n\n  context 'when creating type with derived attributes using implementation' do\n    let(:datatypes) {\n      {\n        'mytype.rb' => <<-RUBY.unindent,\n          Puppet::DataTypes.create_type('Mytype') do\n            interface <<-PUPPET\n              attributes => {\n                name => { type => String },\n                year_of_birth => { type => Integer },\n                age => { type => Integer, kind => derived },\n              }\n              PUPPET\n\n            implementation do\n              def age\n                DateTime.now.year - @year_of_birth\n              end\n            end\n          end\n          RUBY\n      }\n    }\n\n    it 'loads and returns value of attribute' do\n      expect(eval_and_collect_notices('notice(Mytype(\"Bob\", 1984).age)', node)).to eql([\"#{DateTime.now.year - 1984}\"])\n    end\n\n    it 'can convert value to and from data' do\n      expect(eval_and_collect_notices(<<-PUPPET.unindent, node)).to eql(['false', 'true', 'true', \"#{DateTime.now.year - 1984}\"])\n        $m = Mytype(\"Bob\", 1984)\n        $d = $m.mytest::to_data\n        notice($m == $d)\n        notice($d =~ Data)\n        $m2 = $d.mytest::from_data\n        notice($m == $m2)\n        notice($m2.age)\n      PUPPET\n    end\n  end\n\n  context 'when creating type for an already implemented class' do\n    let(:datatypes) {\n      {\n        'mytest.rb' => <<-RUBY.unindent,\n          Puppet::DataTypes.create_type('Mytest') do\n            interface <<-PUPPET\n              attributes => {\n                name => { type => String },\n                year_of_birth => { type => Integer },\n                age => { type => Integer, kind => derived },\n              },\n              functions => {\n                '[]' => Callable[[String[1]], Variant[String, Integer]]\n              }\n              PUPPET\n\n            implementation_class PuppetSpec::DataTypes::MyTest\n          end\n      RUBY\n      }\n    }\n\n    before(:each) do\n      class ::PuppetSpec::DataTypes::MyTest\n        attr_reader :name, :year_of_birth\n\n        def initialize(name, year_of_birth)\n          @name = name\n          @year_of_birth = year_of_birth\n        end\n\n        def age\n          DateTime.now.year - @year_of_birth\n        end\n\n        def [](key)\n          case key\n          when 'name'\n            @name\n          when 'year_of_birth'\n            @year_of_birth\n          when 'age'\n            age\n          else\n            nil\n          end\n        end\n\n        def ==(o)\n          self.class == o.class && @name == o.name && @year_of_birth == o.year_of_birth\n        end\n      end\n    end\n\n    after(:each) do\n      ::PuppetSpec::DataTypes.send(:remove_const, :MyTest)\n    end\n\n    it 'loads and returns value of attribute' do\n      expect(eval_and_collect_notices('notice(Mytest(\"Bob\", 1984).age)', node)).to eql([\"#{DateTime.now.year - 1984}\"])\n    end\n\n    it 'can convert value to and from data' do\n      expect(eval_and_collect_notices(<<-PUPPET.unindent, node)).to eql(['true', 'true', \"#{DateTime.now.year - 1984}\"])\n        $m = Mytest(\"Bob\", 1984)\n        $d = $m.mytest::to_data\n        notice($d =~ Data)\n        $m2 = $d.mytest::from_data\n        notice($m == $m2)\n        notice($m2.age)\n        PUPPET\n    end\n\n    it 'can access using implemented [] method' do\n      expect(eval_and_collect_notices(<<-PUPPET.unindent, node)).to eql(['Bob', \"#{DateTime.now.year - 1984}\"])\n        $m = Mytest(\"Bob\", 1984)\n        notice($m['name'])\n        notice($m['age'])\n      PUPPET\n    end\n\n    it 'can serialize and deserialize data' do\n      expect(eval_and_collect_notices(<<-PUPPET.unindent, node)).to eql(['true', 'true', \"#{DateTime.now.year - 1984}\"])\n        $m = Mytest(\"Bob\", 1984)\n        $d = $m.mytest::serialize\n        notice($d =~ String)\n        $m2 = $d.mytest::deserialize\n        notice($m == $m2)\n        notice($m2.age)\n        PUPPET\n    end\n  end\n\n  context 'when creating type with custom new_function' do\n    let(:datatypes) {\n      {\n        'mytest.rb' => <<-RUBY.unindent,\n          Puppet::DataTypes.create_type('Mytest') do\n            interface <<-PUPPET\n              attributes => {\n                strings => { type => Array[String] },\n                ints => { type => Array[Integer] },\n              }\n              PUPPET\n\n          implementation_class PuppetSpec::DataTypes::MyTest\n        end\n      RUBY\n      }\n    }\n\n    before(:each) do\n      class ::PuppetSpec::DataTypes::MyTest\n        def self.create_new_function(t)\n          Puppet::Functions.create_function('new_%s' % t.name) do\n            dispatch :create do\n              repeated_param 'Variant[String,Integer]', :args\n            end\n\n            def create(*args)\n              ::PuppetSpec::DataTypes::MyTest.new(*args.partition { |arg| arg.is_a?(String) })\n            end\n          end\n        end\n        attr_reader :strings, :ints\n\n        def initialize(strings, ints)\n          @strings = strings\n          @ints = ints\n        end\n      end\n    end\n\n    after(:each) do\n      ::PuppetSpec::DataTypes.send(:remove_const, :MyTest)\n    end\n\n    it 'loads and calls custom new function' do\n      expect(eval_and_collect_notices('notice(Mytest(\"A\", 32, \"B\", 20).ints)', node)).to eql(['[32, 20]'])\n    end\n  end\n\n  context 'with data type and class defined in a module' do\n    let(:mytest_classes) {\n      {\n        'position.rb' => <<-RUBY\n            module PuppetX; module Mytest; class Position\n              attr_reader :x, :y\n\n              def initialize(x, y)\n                @x = x\n                @y = y\n              end\n            end; end; end\n      RUBY\n      }\n    }\n\n    after(:each) do\n      ::PuppetX.send(:remove_const, :Mytest)\n    end\n\n    context 'in module namespace' do\n      let(:mytest_datatypes) {\n        {\n          'mytest' => { 'position.rb' => <<-RUBY\n            Puppet::DataTypes.create_type('Mytest::Position') do\n              interface <<-PUPPET\n                attributes => {\n                  x => Integer,\n                  y => Integer\n                }\n                PUPPET\n\n              load_file('puppetx/mytest/position')\n\n              implementation_class PuppetX::Mytest::Position\n            end\n            RUBY\n          }\n        }\n      }\n\n      it 'loads and returns value of attribute' do\n        expect(eval_and_collect_notices('notice(Mytest::Position(23, 12).x)', node)).to eql(['23'])\n      end\n    end\n\n    context 'in top namespace' do\n      let(:mytest_datatypes) {\n        {\n          'position.rb' => <<-RUBY\n            Puppet::DataTypes.create_type('Position') do\n              interface <<-PUPPET\n                attributes => {\n                  x => Integer,\n                  y => Integer\n                }\n                PUPPET\n\n              load_file('puppetx/mytest/position')\n\n              implementation_class PuppetX::Mytest::Position\n            end\n        RUBY\n        }\n      }\n\n      it 'loads and returns value of attribute' do\n        expect(eval_and_collect_notices('notice(Position(23, 12).x)', node)).to eql(['23'])\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/defaults_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/settings'\n\ndescribe \"Defaults\" do\n  describe \".default_diffargs\" do\n    it \"should be '-u'\" do\n      expect(Puppet.default_diffargs).to eq(\"-u\")\n    end\n  end\n\n  describe 'strict' do\n    it 'should accept the valid value :off' do\n      expect {Puppet.settings[:strict] = 'off'}.to_not raise_exception\n    end\n\n    it 'should accept the valid value :warning' do\n      expect {Puppet.settings[:strict] = 'warning'}.to_not raise_exception\n    end\n\n    it 'should accept the valid value :error' do\n      expect {Puppet.settings[:strict] = 'error'}.to_not raise_exception\n    end\n\n    it 'should fail if given an invalid value' do\n      expect {Puppet.settings[:strict] = 'ignore'}.to raise_exception(/Invalid value 'ignore' for parameter strict\\./)\n    end\n  end\n\n  describe '.default_digest_algorithm' do\n    it 'defaults to sha256 when FIPS is not enabled' do\n      allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(false)\n      expect(Puppet.default_digest_algorithm).to eq('sha256')\n    end\n\n    it 'defaults to sha256 when FIPS is enabled' do\n      allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n      expect(Puppet.default_digest_algorithm).to eq('sha256')\n    end\n  end\n\n  describe '.supported_checksum_types' do\n    it 'defaults to sha256, sha384, sha512, sha224, md5 when FIPS is not enabled' do\n      allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(false)\n      expect(Puppet.default_file_checksum_types).to eq(%w[sha256 sha384 sha512 sha224 md5])\n    end\n\n    it 'defaults to sha256, sha384, sha512, sha224 when FIPS is enabled' do\n      allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n      expect(Puppet.default_file_checksum_types).to eq(%w[sha256 sha384 sha512 sha224])\n    end\n  end\n\n  describe 'Puppet[:supported_checksum_types]' do\n    it 'defaults to sha256, sha512, sha384, sha224, md5' do\n      expect(Puppet.settings[:supported_checksum_types]).to eq(%w[sha256 sha384 sha512 sha224 md5])\n    end\n\n    it 'should raise an error on an unsupported checksum type' do\n      expect {\n        Puppet.settings[:supported_checksum_types] = %w[md5 foo]\n      }.to raise_exception ArgumentError,\n                           /Invalid value 'foo' for parameter supported_checksum_types. Allowed values are/\n    end\n\n    it 'should not raise an error on setting a valid list of checksum types' do\n      Puppet.settings[:supported_checksum_types] = %w[sha256 md5lite mtime]\n      expect(Puppet.settings[:supported_checksum_types]).to eq(%w[sha256 md5lite mtime])\n    end\n\n    it 'raises when setting md5 in FIPS mode' do\n      allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n      expect {\n        Puppet.settings[:supported_checksum_types] = %w[md5]\n      }.to raise_error(ArgumentError,\n                       /Invalid value 'md5' for parameter supported_checksum_types. Allowed values are 'sha256'/)\n    end\n  end\n\n  describe 'manage_internal_file_permissions' do\n    describe 'on windows', :if => Puppet::Util::Platform.windows? do\n      it 'should default to false' do\n        expect(Puppet.settings[:manage_internal_file_permissions]).to be false\n      end\n    end\n\n    describe 'on non-windows', :if => ! Puppet::Util::Platform.windows? do\n      it 'should default to true' do\n        expect(Puppet.settings[:manage_internal_file_permissions]).to be true\n      end\n    end\n  end\n\n  describe 'basemodulepath' do\n    it 'includes the user and system modules', :unless => Puppet::Util::Platform.windows? do\n      expect(\n        Puppet[:basemodulepath]\n      ).to match(%r{.*/code/modules:/opt/puppetlabs/puppet/modules$})\n    end\n\n    describe 'on windows', :if => Puppet::Util::Platform.windows? do\n      let(:installdir) { 'C:\\Program Files\\Puppet Labs\\Puppet' }\n\n      it 'includes user and system modules' do\n        allow(ENV).to receive(:fetch).with(\"FACTER_env_windows_installdir\", anything).and_return(installdir)\n\n        expect(\n          Puppet.default_basemodulepath\n        ).to eq('$codedir/modules;C:\\Program Files\\Puppet Labs\\Puppet/puppet/modules')\n      end\n\n      it 'includes user modules if installdir fact is missing' do\n        allow(ENV).to receive(:[]).with(\"FACTER_env_windows_installdir\").and_return(nil)\n\n        expect(\n          Puppet.default_basemodulepath\n        ).to eq('$codedir/modules')\n      end\n    end\n  end\n\n  describe 'vendormoduledir' do\n    it 'includes the default vendormoduledir', :unless => Puppet::Util::Platform.windows? do\n      expect(\n        Puppet[:vendormoduledir]\n      ).to eq('/opt/puppetlabs/puppet/vendor_modules')\n    end\n\n    describe 'on windows', :if => Puppet::Util::Platform.windows? do\n      let(:installdir) { 'C:\\Program Files\\Puppet Labs\\Puppet' }\n\n      it 'includes the default vendormoduledir' do\n        allow(ENV).to receive(:fetch).with(\"FACTER_env_windows_installdir\", anything).and_return(installdir)\n\n        expect(\n          Puppet.default_vendormoduledir\n        ).to eq('C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\vendor_modules')\n      end\n\n      it 'is nil if installdir fact is missing' do\n        allow(ENV).to receive(:[]).with(\"FACTER_env_windows_installdir\").and_return(nil)\n\n        expect(Puppet.default_vendormoduledir).to be_nil\n      end\n    end\n  end\n\n  describe \"deprecated settings\" do\n    it 'does not issue a deprecation warning by default' do\n      expect(Puppet).to receive(:deprecation_warning).never\n\n      Puppet.initialize_settings\n    end\n  end\n\n  describe \"the default cadir\", :unless => Puppet::Util::Platform.windows?  do\n    it 'defaults to the puppetserver confdir when no cadir is found' do\n      Puppet.initialize_settings\n      expect(Puppet[:cadir]).to eq('/etc/puppetlabs/puppetserver/ca')\n    end\n\n    it 'returns an empty string for Windows platforms', :if => Puppet::Util::Platform.windows? do\n      Puppet.initialize_settings\n      expect(Puppet[:cadir]).to eq(\"\")\n    end\n  end\n\n  describe '#default_cadir', :unless => Puppet::Util::Platform.windows?  do\n    it 'warns when a CA dir exists in the current ssldir' do\n      cadir = File.join(Puppet[:ssldir], 'ca')\n      FileUtils.mkdir_p(cadir)\n      expect(Puppet.default_cadir).to eq(cadir)\n    end\n  end\n\n  describe \"#preferred_serialization_format\" do\n    it 'raises if PSON is not available', unless: Puppet.features.pson? do\n      expect {\n        Puppet.settings[:preferred_serialization_format] = \"pson\"\n      }.to raise_error(Puppet::Settings::ValidationError, \"The 'puppet-pson' gem must be installed to use the PSON serialization format.\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/environments_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/environments'\nrequire 'puppet/file_system'\n\ndescribe Puppet::Environments do\n  FS = Puppet::FileSystem\n\n  module FsRemove\n    def remove\n      @properties[:directory?] = false\n      @properties[:exist?] = false\n      @properties[:executable?] = false\n    end\n  end\n\n  before(:each) do\n    Puppet.settings.initialize_global_settings\n    Puppet[:environment_timeout] = \"unlimited\"\n    Puppet[:versioned_environment_dirs] = true\n  end\n\n  let(:directory_tree) do\n    FS::MemoryFile.a_directory(File.expand_path(\"top_level_dir\"), [\n      FS::MemoryFile.a_directory(\"envdir\", [\n        FS::MemoryFile.a_regular_file_containing(\"ignored_file\", ''),\n        FS::MemoryFile.a_directory(\"an_environment\", [\n          FS::MemoryFile.a_missing_file(\"environment.conf\"),\n          FS::MemoryFile.a_directory(\"modules\"),\n          FS::MemoryFile.a_directory(\"manifests\"),\n        ]),\n        FS::MemoryFile.a_directory(\"another_environment\", [\n          FS::MemoryFile.a_missing_file(\"environment.conf\"),\n        ]),\n        FS::MemoryFile.a_missing_file(\"doesnotexist\"),\n        FS::MemoryFile.a_symlink(\"symlinked_environment\", File.expand_path(File.join(\"top_level_dir\", \"versioned_env\")))]),\n      FS::MemoryFile.a_directory(\"versioned_env\", [\n        FS::MemoryFile.a_regular_file_containing(\"environment.conf\", ''),\n        FS::MemoryFile.a_directory(\"modules\"),\n        FS::MemoryFile.a_directory(\"manifests\"),\n      ]),\n      FS::MemoryFile.a_missing_file(\"missing\")\n    ])\n  end\n\n  describe \"directories loader\" do\n    it \"lists environments\" do\n      global_path_1_location = File.expand_path(\"global_path_1\")\n      global_path_2_location = File.expand_path(\"global_path_2\")\n      global_path_1 = FS::MemoryFile.a_directory(global_path_1_location)\n      global_path_2 = FS::MemoryFile.a_directory(global_path_2_location)\n\n      loader_from(:filesystem => [directory_tree, global_path_1, global_path_2],\n                  :directory => directory_tree.children.first,\n                  :modulepath => [global_path_1_location, global_path_2_location]) do |loader|\n        expect(loader.list).to contain_exactly(\n          environment(:an_environment).\n            with_manifest(\"#{FS.path_string(directory_tree)}/envdir/an_environment/manifests\").\n            with_modulepath([\"#{FS.path_string(directory_tree)}/envdir/an_environment/modules\",\n                             global_path_1_location,\n                             global_path_2_location]),\n          environment(:another_environment),\n          environment(:symlinked_environment).\n            with_manifest(\"#{FS.path_string(directory_tree)}/versioned_env/manifests\").\n            with_modulepath([\"#{FS.path_string(directory_tree)}/versioned_env/modules\",\n                             global_path_1_location,\n                             global_path_2_location]))\n      end\n    end\n\n    it \"has search_paths\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect(loader.search_paths).to eq([\"file://#{directory_tree.children.first}\"])\n      end\n    end\n\n    it \"ignores directories that are not valid env names (alphanumeric and _)\" do\n      envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n        FS::MemoryFile.a_directory(\".foo\"),\n        FS::MemoryFile.a_directory(\"bar-thing\"),\n        FS::MemoryFile.a_directory(\"with spaces\"),\n        FS::MemoryFile.a_directory(\"some.thing\"),\n        FS::MemoryFile.a_directory(\"env1\", [\n          FS::MemoryFile.a_missing_file(\"environment.conf\"),\n        ]),\n        FS::MemoryFile.a_directory(\"env2\", [\n          FS::MemoryFile.a_missing_file(\"environment.conf\"),\n        ]),\n      ])\n\n      loader_from(:filesystem => [envdir],\n                  :directory => envdir) do |loader|\n        expect(loader.list).to contain_exactly(environment(:env1), environment(:env2))\n      end\n    end\n\n    it \"proceeds with non-existant env dir\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.last) do |loader|\n        expect(loader.list).to eq([])\n      end\n    end\n\n    it \"gets a particular environment\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect(loader.get(\"an_environment\")).to environment(:an_environment)\n      end\n    end\n\n    it \"gets a symlinked environment\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect(loader.get(\"symlinked_environment\")).to environment(:symlinked_environment)\n      end\n    end\n\n    it \"sets the environment's configured and resolved paths set when symlinked\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        env = loader.get(\"symlinked_environment\")\n        expect(env.resolved_path).to eq(\"#{FS.path_string(directory_tree)}/versioned_env\")\n        expect(env.configured_path).to eq(\"#{FS.path_string(directory_tree)}/envdir/symlinked_environment\")\n      end\n    end\n\n    it \"ignores symlinked environments when `:versioned_environment_dirs` is false\" do\n      Puppet[:versioned_environment_dirs] = false\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect(loader.get(\"symlinked_environment\")).to be_nil\n      end\n    end\n\n    it \"raises error when environment not found\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect do\n          loader.get!(\"doesnotexist\")\n        end.to raise_error(Puppet::Environments::EnvironmentNotFound)\n      end\n    end\n\n    it \"returns nil if an environment can't be found\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect(loader.get(\"doesnotexist\")).to be_nil\n      end\n    end\n\n    it \"implements guard and unguard\" do\n      loader_from(:filesystem => [directory_tree],\n                  :directory => directory_tree.children.first) do |loader|\n        expect(loader.guard('env1')).to be_nil\n        expect(loader.unguard('env1')).to be_nil\n      end\n    end\n\n    context \"with an environment.conf\" do\n      let(:envdir) do\n        FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", content),\n          ]),\n        ])\n      end\n      let(:manifestdir) { FS::MemoryFile.a_directory(File.expand_path(\"/some/manifest/path\")) }\n      let(:modulepath) do\n        [\n          FS::MemoryFile.a_directory(File.expand_path(\"/some/module/path\")),\n          FS::MemoryFile.a_directory(File.expand_path(\"/some/other/path\")),\n        ]\n      end\n\n      let(:content) do\n        <<-EOF\nmanifest=#{manifestdir}\nmodulepath=#{modulepath.join(File::PATH_SEPARATOR)}\nconfig_version=/some/script\nstatic_catalogs=false\n        EOF\n      end\n\n      it \"reads environment.conf settings\" do\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path))\n        end\n      end\n\n      it \"does not append global_module_path to environment.conf modulepath setting\" do\n        global_path_location = File.expand_path(\"global_path\")\n        global_path = FS::MemoryFile.a_directory(global_path_location)\n\n        loader_from(:filesystem => [envdir, manifestdir, modulepath, global_path].flatten,\n                    :directory => envdir,\n                    :modulepath => [global_path]) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path))\n        end\n      end\n\n      it \"reads config_version setting\" do\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path)).\n            with_config_version(File.expand_path('/some/script'))\n        end\n      end\n\n      it \"reads static_catalogs setting\" do\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path)).\n            with_config_version(File.expand_path('/some/script')).\n            with_static_catalogs(false)\n        end\n      end\n\n      it \"accepts an empty environment.conf without warning\" do\n        content = nil\n\n        envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", content),\n          ]),\n        ])\n\n        manifestdir = FS::MemoryFile.a_directory(File.join(envdir, \"env1\", \"manifests\"))\n        modulesdir = FS::MemoryFile.a_directory(File.join(envdir, \"env1\", \"modules\"))\n        global_path_location = File.expand_path(\"global_path\")\n        global_path = FS::MemoryFile.a_directory(global_path_location)\n\n        loader_from(:filesystem => [envdir, manifestdir, modulesdir, global_path].flatten,\n                    :directory => envdir,\n                    :modulepath => [global_path]) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(\"#{FS.path_string(envdir)}/env1/manifests\").\n            with_modulepath([\"#{FS.path_string(envdir)}/env1/modules\", global_path_location]).\n            with_config_version(nil).\n            with_static_catalogs(true)\n        end\n\n        expect(@logs).to be_empty\n      end\n\n      it \"logs a warning, but processes the main settings if there are extraneous sections\" do\n        content << \"[foo]\"\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path)).\n            with_config_version(File.expand_path('/some/script'))\n        end\n\n        expect(@logs.map(&:to_s).join).to match(/Invalid.*at.*\\/env1.*may not have sections.*ignored: 'foo'/)\n      end\n\n      it \"logs a warning, but processes the main settings if there are any extraneous settings\" do\n        content << \"dog=arf\\n\"\n        content << \"cat=mew\\n\"\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path)).\n            with_config_version(File.expand_path('/some/script'))\n        end\n\n        expect(@logs.map(&:to_s).join).to match(/Invalid.*at.*\\/env1.*unknown setting.*dog, cat/)\n      end\n\n      it \"logs a warning, but processes the main settings if there are any ignored sections\" do\n        content << \"dog=arf\\n\"\n        content << \"cat=mew\\n\"\n        content << \"[ignored]\\n\"\n        content << \"cow=moo\\n\"\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path)).\n            with_config_version(File.expand_path('/some/script'))\n        end\n\n        expect(@logs.map(&:to_s).join).to match(/Invalid.*at.*\\/env1.*The following sections are being ignored: 'ignored'/)\n        expect(@logs.map(&:to_s).join).to match(/Invalid.*at.*\\/env1.*unknown setting.*dog, cat/)\n      end\n\n      it \"interpretes relative paths from the environment's directory\" do\n        content = <<-EOF\nmanifest=relative/manifest\nmodulepath=relative/modules\nconfig_version=relative/script\n        EOF\n\n        envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", content),\n            FS::MemoryFile.a_missing_file(\"modules\"),\n            FS::MemoryFile.a_directory('relative', [\n              FS::MemoryFile.a_directory('modules'),\n            ]),\n          ]),\n        ])\n\n        loader_from(:filesystem => [envdir],\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(File.join(envdir, 'env1', 'relative', 'manifest')).\n            with_modulepath([File.join(envdir, 'env1', 'relative', 'modules')]).\n            with_config_version(File.join(envdir, 'env1', 'relative', 'script'))\n        end\n      end\n\n      it \"interprets glob modulepaths from the environment's directory\" do\n        allow(Dir).to receive(:glob).with(File.join(envdir, 'env1', 'other', '*', 'modules')).and_return([\n          File.join(envdir, 'env1', 'other', 'foo', 'modules'),\n          File.join(envdir, 'env1', 'other', 'bar', 'modules')\n        ])\n        content = <<-EOF\nmanifest=relative/manifest\nmodulepath=relative/modules#{File::PATH_SEPARATOR}other/*/modules\nconfig_version=relative/script\n        EOF\n\n        envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", content),\n            FS::MemoryFile.a_missing_file(\"modules\"),\n            FS::MemoryFile.a_directory('relative', [\n              FS::MemoryFile.a_directory('modules'),\n            ]),\n            FS::MemoryFile.a_directory('other', [\n              FS::MemoryFile.a_directory('foo', [\n                FS::MemoryFile.a_directory('modules'),\n              ]),\n              FS::MemoryFile.a_directory('bar', [\n                FS::MemoryFile.a_directory('modules'),\n              ]),\n            ]),\n          ]),\n        ])\n\n        loader_from(:filesystem => [envdir],\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(File.join(envdir, 'env1', 'relative', 'manifest')).\n            with_modulepath([File.join(envdir, 'env1', 'relative', 'modules'),\n                             File.join(envdir, 'env1', 'other', 'foo', 'modules'),\n                             File.join(envdir, 'env1', 'other', 'bar', 'modules')]).\n            with_config_version(File.join(envdir, 'env1', 'relative', 'script'))\n        end\n      end\n\n      it \"interpolates other setting values correctly\" do\n        modulepath = [\n          File.expand_path('/some/absolute'),\n          '$basemodulepath',\n          'modules'\n        ].join(File::PATH_SEPARATOR)\n\n        content = <<-EOF\nmanifest=$confdir/whackymanifests\nmodulepath=#{modulepath}\nconfig_version=$vardir/random/scripts\n        EOF\n\n        some_absolute_dir = FS::MemoryFile.a_directory(File.expand_path('/some/absolute'))\n        base_module_dirs = Puppet[:basemodulepath].split(File::PATH_SEPARATOR).map do |path|\n          FS::MemoryFile.a_directory(path)\n        end\n        envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", content),\n            FS::MemoryFile.a_directory(\"modules\"),\n          ]),\n        ])\n\n        loader_from(:filesystem => [envdir, some_absolute_dir, base_module_dirs].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(File.join(Puppet[:confdir], 'whackymanifests')).\n            with_modulepath([some_absolute_dir.path,\n                            base_module_dirs.map { |d| d.path },\n                            File.join(envdir, 'env1', 'modules')].flatten).\n            with_config_version(File.join(Puppet[:vardir], 'random', 'scripts'))\n        end\n      end\n\n      it \"uses environment.conf settings regardless of existence of modules and manifests subdirectories\" do\n        envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", content),\n            FS::MemoryFile.a_directory(\"modules\"),\n            FS::MemoryFile.a_directory(\"manifests\"),\n          ]),\n        ])\n\n        loader_from(:filesystem => [envdir, manifestdir, modulepath].flatten,\n                    :directory => envdir) do |loader|\n          expect(loader.get(\"env1\")).to environment(:env1).\n            with_manifest(manifestdir.path).\n            with_modulepath(modulepath.map(&:path)).\n            with_config_version(File.expand_path('/some/script'))\n        end\n      end\n\n      it \"should update environment settings if environment.conf has changed and timeout has expired\" do\n        base_dir = File.expand_path(\"envdir\")\n        original_envdir = FS::MemoryFile.a_directory(base_dir, [\n          FS::MemoryFile.a_directory(\"env3\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", <<-EOF)\n              manifest=/manifest_orig\n              modulepath=/modules_orig\n              environment_timeout=0\n            EOF\n          ]),\n        ])\n\n        cached_loader_from(:filesystem => [original_envdir], :directory => original_envdir) do |loader|\n          original_env = loader.get(\"env3\") # force the environment.conf to be read\n\n          changed_envdir = FS::MemoryFile.a_directory(base_dir, [\n            FS::MemoryFile.a_directory(\"env3\", [\n              FS::MemoryFile.a_regular_file_containing(\"environment.conf\", <<-EOF)\n                manifest=/manifest_changed\n                modulepath=/modules_changed\n                environment_timeout=0\n              EOF\n            ]),\n          ])\n\n          FS.overlay(changed_envdir) do\n            changed_env = loader.get(\"env3\")\n\n            expect(original_env).to environment(:env3).\n              with_manifest(File.expand_path(\"/manifest_orig\")).\n              with_full_modulepath([File.expand_path(\"/modules_orig\")])\n\n            expect(changed_env).to environment(:env3).\n              with_manifest(File.expand_path(\"/manifest_changed\")).\n              with_full_modulepath([File.expand_path(\"/modules_changed\")])\n          end\n        end\n      end\n    end\n  end\n\n  describe \"static loaders\" do\n    let(:static1) { Puppet::Node::Environment.create(:static1, []) }\n    let(:static2) { Puppet::Node::Environment.create(:static2, []) }\n    let(:loader) { Puppet::Environments::Static.new(static1, static2) }\n\n    it \"lists environments\" do\n      expect(loader.list).to eq([static1, static2])\n    end\n\n    it \"has search_paths\" do\n      expect(loader.search_paths).to eq([\"data:text/plain,internal\"])\n    end\n\n    it \"gets an environment\" do\n      expect(loader.get(:static2)).to eq(static2)\n    end\n\n    it \"returns nil if env not found\" do\n      expect(loader.get(:doesnotexist)).to be_nil\n    end\n\n    it \"raises error if environment is not found\" do\n      expect do\n        loader.get!(:doesnotexist)\n      end.to raise_error(Puppet::Environments::EnvironmentNotFound)\n    end\n\n    it \"gets a basic conf\" do\n      conf = loader.get_conf(:static1)\n      expect(conf.modulepath).to eq('')\n      expect(conf.manifest).to eq(:no_manifest)\n      expect(conf.config_version).to be_nil\n      expect(conf.static_catalogs).to eq(true)\n    end\n\n    it \"returns nil if you request a configuration from an env that doesn't exist\" do\n      expect(loader.get_conf(:doesnotexist)).to be_nil\n    end\n\n    it \"gets the conf environment_timeout if one is specified\" do\n      Puppet[:environment_timeout] = 8675\n      conf = loader.get_conf(:static1)\n\n      expect(conf.environment_timeout).to eq(8675)\n    end\n\n    context \"that are private\" do\n      let(:private_env) { Puppet::Node::Environment.create(:private, []) }\n      let(:loader) { Puppet::Environments::StaticPrivate.new(private_env) }\n\n      it \"lists nothing\" do\n        expect(loader.list).to eq([])\n      end\n    end\n  end\n\n  describe \"combined loaders\" do\n    let(:static1) { Puppet::Node::Environment.create(:static1, []) }\n    let(:static2) { Puppet::Node::Environment.create(:static2, []) }\n    let(:static_loader) { Puppet::Environments::Static.new(static1, static2) }\n    let(:directory_tree) do\n      FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n        FS::MemoryFile.a_directory(\"an_environment\", [\n          FS::MemoryFile.a_missing_file(\"environment.conf\"),\n          FS::MemoryFile.a_directory(\"modules\"),\n          FS::MemoryFile.a_directory(\"manifests\"),\n        ]),\n        FS::MemoryFile.a_missing_file(\"env_does_not_exist\"),\n        FS::MemoryFile.a_missing_file(\"static2\"),\n      ])\n    end\n\n    it \"lists environments\" do\n      loader_from(:filesystem => [directory_tree], :directory => directory_tree) do |loader|\n        envs = Puppet::Environments::Combined.new(loader, static_loader).list\n        expect(envs[0]).to environment(:an_environment)\n        expect(envs[1]).to environment(:static1)\n        expect(envs[2]).to environment(:static2)\n      end\n    end\n\n    it \"has search_paths\" do\n      loader_from(:filesystem => [directory_tree], :directory => directory_tree) do |loader|\n        expect(Puppet::Environments::Combined.new(loader, static_loader).search_paths).to eq([\"file://#{directory_tree}\",\"data:text/plain,internal\"])\n      end\n    end\n\n    it \"gets an environment\" do\n      loader_from(:filesystem => [directory_tree], :directory => directory_tree) do |loader|\n        expect(Puppet::Environments::Combined.new(loader, static_loader).get(:an_environment)).to environment(:an_environment)\n        expect(Puppet::Environments::Combined.new(loader, static_loader).get(:static2)).to environment(:static2)\n      end\n    end\n\n    it \"returns nil if env not found\" do\n      loader_from(:filesystem => [directory_tree], :directory => directory_tree) do |loader|\n        expect(Puppet::Environments::Combined.new(loader, static_loader).get(:env_does_not_exist)).to be_nil\n      end\n    end\n\n    it \"raises an error if environment is not found\" do\n      loader_from(:filesystem => [directory_tree], :directory => directory_tree) do |loader|\n        expect do\n          Puppet::Environments::Combined.new(loader, static_loader).get!(:env_does_not_exist)\n        end.to raise_error(Puppet::Environments::EnvironmentNotFound)\n      end\n    end\n\n    it \"gets an environment.conf\" do\n      loader_from(:filesystem => [directory_tree], :directory => directory_tree) do |loader|\n        expect(Puppet::Environments::Combined.new(loader, static_loader).get_conf(:an_environment)).to match_environment_conf(:an_environment).\n          with_env_path(directory_tree).\n          with_global_module_path([])\n      end\n    end\n  end\n\n  describe \"cached loaders\" do\n    it \"lists environments\" do\n      cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n        expect(loader.list).to contain_exactly(\n          environment(:an_environment),\n          environment(:another_environment),\n          environment(:symlinked_environment))\n      end\n    end\n\n    it \"returns the same cached environment object for list and get methods\" do\n      cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n        env = loader.list.find { |e| e.name == :an_environment }\n\n        expect(env).to equal(loader.get(:an_environment)) # same object\n      end\n    end\n\n    it \"returns the same cached environment object for multiple list calls\" do\n      cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n        expect(loader.list.first).to equal(loader.list.first) # same object\n      end\n    end\n\n    it \"expires environments and returns a new environment object with the same value\" do\n      Puppet[:environment_timeout] = \"0\"\n\n      cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n        a = loader.list.first\n        b = loader.list.first\n        expect(a).to eq(b)        # same value\n        expect(a).to_not equal(b) # not same object\n      end\n    end\n\n    it \"has search_paths\" do\n      cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n        expect(loader.search_paths).to eq([\"file://#{directory_tree.children.first}\"])\n      end\n    end\n\n    context \"#get\" do\n      it \"gets an environment\" do\n        cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n          expect(loader.get(:an_environment)).to environment(:an_environment)\n        end\n      end\n\n      it \"does not reload the environment if it isn't expired\" do\n        env = Puppet::Node::Environment.create(:cached, [])\n        mocked_loader = double('loader')\n        expect(mocked_loader).to receive(:get).with(:cached).and_return(env).once\n        expect(mocked_loader).to receive(:get_conf).with(:cached).and_return(Puppet::Settings::EnvironmentConf.static_for(env, 20)).once\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n\n        cached.get(:cached)\n        cached.get(:cached)\n      end\n\n      it \"does not list deleted environments\" do\n        env3 = FS::MemoryFile.a_directory(\"env3\", [\n          FS::MemoryFile.a_regular_file_containing(\"environment.conf\", '')\n        ])\n\n        envdir = FS::MemoryFile.a_directory(File.expand_path(\"envdir\"), [\n          FS::MemoryFile.a_directory(\"env1\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", '')\n          ]),\n          FS::MemoryFile.a_directory(\"env2\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", '')\n          ]),\n          env3\n        ])\n\n        loader_from(:filesystem => [envdir], :directory => envdir) do |loader|\n          cached = Puppet::Environments::Cached.new(loader)\n          cached.get(:env1)\n          cached.get(:env2)\n          cached.get(:env3)\n          env3.extend(FsRemove).remove\n\n          expect(cached.list).to contain_exactly(environment(:env1),environment(:env2))\n          expect(cached.get(:env3)).to be_nil\n        end\n      end\n\n      it \"normalizes environment name to symbol\" do\n        env = Puppet::Node::Environment.create(:cached, [])\n        mocked_loader = double('loader')\n\n        expect(mocked_loader).not_to receive(:get).with('cached')\n        expect(mocked_loader).to receive(:get).with(:cached).and_return(env).once\n        expect(mocked_loader).to receive(:get_conf).with(:cached).and_return(Puppet::Settings::EnvironmentConf.static_for(env, 20)).once\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n        cached.get('cached')\n        cached.get(:cached)\n      end\n\n      it \"caches environment name as symbol and only once\" do\n        mocked_loader = double('loader')\n\n        env = Puppet::Node::Environment.create(:cached, [])\n        allow(mocked_loader).to receive(:get).with(:cached).and_return(env)\n        allow(mocked_loader).to receive(:get_conf).with(:cached).and_return(Puppet::Settings::EnvironmentConf.static_for(env, 20))\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n        cached.get(:cached)\n        cached.get('cached')\n\n        expect(cached.instance_variable_get(:@cache).keys).to eq([:cached])\n      end\n\n      it \"is able to cache multiple environments\" do\n        mocked_loader = double('loader')\n\n        env1 = Puppet::Node::Environment.create(:env1, [])\n        allow(mocked_loader).to receive(:get).with(:env1).and_return(env1)\n        allow(mocked_loader).to receive(:get_conf).with(:env1).and_return(Puppet::Settings::EnvironmentConf.static_for(env1, 20))\n\n        env2 = Puppet::Node::Environment.create(:env2, [])\n        allow(mocked_loader).to receive(:get).with(:env2).and_return(env2)\n        allow(mocked_loader).to receive(:get_conf).with(:env2).and_return(Puppet::Settings::EnvironmentConf.static_for(env2, 20))\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n        cached.get('env1')\n        cached.get('env2')\n\n        expect(cached.instance_variable_get(:@cache).keys).to eq([:env1, :env2])\n      end\n\n      it \"returns nil if env not found\" do\n        cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n          expect(loader.get(:doesnotexist)).to be_nil\n        end\n      end\n    end\n\n    context \"#get!\" do\n      it \"gets an environment\" do\n        cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n          expect(loader.get!(:an_environment)).to environment(:an_environment)\n        end\n      end\n\n      it \"does not reload the environment if it isn't expired\" do\n        env = Puppet::Node::Environment.create(:cached, [])\n        mocked_loader = double('loader')\n        expect(mocked_loader).to receive(:get).with(:cached).and_return(env).once\n        expect(mocked_loader).to receive(:get_conf).with(:cached).and_return(Puppet::Settings::EnvironmentConf.static_for(env, 20)).once\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n\n        cached.get!(:cached)\n        cached.get!(:cached)\n      end\n\n      it \"raises error if environment is not found\" do\n        cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n          expect do\n            loader.get!(:doesnotexist)\n          end.to raise_error(Puppet::Environments::EnvironmentNotFound)\n        end\n      end\n    end\n\n    context \"#get_conf\" do\n      it \"loads environment.conf\" do\n        cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n          expect(loader.get_conf(:an_environment)).to match_environment_conf(:an_environment).\n            with_env_path(directory_tree.children.first).\n            with_global_module_path([])\n        end\n      end\n\n      it \"always reloads environment.conf\" do\n        env = Puppet::Node::Environment.create(:cached, [])\n        mocked_loader = double('loader')\n        expect(mocked_loader).to receive(:get_conf).with(:cached).and_return(Puppet::Settings::EnvironmentConf.static_for(env, 20)).twice\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n\n        cached.get_conf(:cached)\n        cached.get_conf(:cached)\n      end\n\n      it \"normalizes environment name to symbol\" do\n        env = Puppet::Node::Environment.create(:cached, [])\n        mocked_loader = double('loader')\n        expect(mocked_loader).to receive(:get_conf).with(:cached).and_return(Puppet::Settings::EnvironmentConf.static_for(env, 20)).twice\n\n        cached = Puppet::Environments::Cached.new(mocked_loader)\n\n        cached.get_conf('cached')\n        cached.get_conf(:cached)\n      end\n\n      it \"returns nil if environment is not found\" do\n        cached_loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n          expect(loader.get_conf(:doesnotexist)).to be_nil\n        end\n      end\n    end\n\n    context \"expiration policies\" do\n      let(:service) { ReplayExpirationService.new }\n\n      it \"notifies when the environment is first created\" do\n        with_environment_loaded(service)\n\n        expect(service.created_envs).to eq([:an_environment])\n      end\n\n      it \"does not evict an unexpired environment\" do\n        Puppet[:environment_timeout] = 'unlimited'\n\n        with_environment_loaded(service) do |cached|\n          cached.get!(:an_environment)\n        end\n\n        expect(service.created_envs).to eq([:an_environment])\n        expect(service.evicted_envs).to eq([])\n      end\n\n      it \"evicts an expired environment\" do\n        expect(service).to receive(:expired?).and_return(true)\n\n        with_environment_loaded(service) do |cached|\n          cached.get!(:an_environment)\n        end\n\n        expect(service.created_envs).to eq([:an_environment, :an_environment])\n        expect(service.evicted_envs).to eq([:an_environment])\n      end\n\n      it \"evicts an environment that hasn't been recently touched\" do\n        Puppet[:environment_timeout] = 1\n\n        with_environment_loaded(service) do |cached|\n          future = Time.now + 60\n          expect(Time).to receive(:now).and_return(future).at_least(:once)\n\n          # this should cause the cached environment to be evicted and a new one created\n          cached.get!(:an_environment)\n        end\n\n        expect(service.created_envs).to eq([:an_environment, :an_environment])\n        expect(service.evicted_envs).to eq([:an_environment])\n\n      end\n\n      it \"reuses an environment that was recently touched\" do\n        Puppet[:environment_timeout] = 60\n\n        with_environment_loaded(service) do |cached|\n          # reuse the already cached environment\n          cached.get!(:an_environment)\n        end\n\n        expect(service.created_envs).to eq([:an_environment])\n        expect(service.evicted_envs).to eq([])\n      end\n\n      it \"evicts a recently touched environment\" do\n        Puppet[:environment_timeout] = 60\n\n        expect(service).to receive(:expired?).and_return(true)\n\n        with_environment_loaded(service) do |cached|\n          # even though the environment was recently touched, it's been expired\n          cached.get!(:an_environment)\n        end\n\n        expect(service.created_envs).to eq([:an_environment, :an_environment])\n        expect(service.evicted_envs).to eq([:an_environment])\n      end\n\n      it \"evicts expired environments when listing\" do\n        expect(service).to receive(:expired?).with(:an_environment).and_return(true)\n\n        with_environment_loaded(service) do |cached|\n          cached.list\n        end\n\n        expect(service.evicted_envs).to eq([:an_environment])\n      end\n\n      context \"when guarding an environment\" do\n        before :each do\n          Puppet[:environment_timeout] = 0\n        end\n\n        let(:name) { :an_environment }\n\n        def with_guard(cached, name, &block)\n          cached.guard(name)\n          begin\n            yield\n          ensure\n            cached.unguard(name)\n          end\n        end\n\n        it \"evicts an expired and unguarded environment\" do\n          with_environment_loaded(service) do |cached|\n            cached.get!(name)\n          end\n\n          expect(service.created_envs).to eq([name, name])\n          expect(service.evicted_envs).to eq([name])\n        end\n\n        it \"does not evict an expired, but guarded environment\" do\n          with_environment_loaded(service) do |cached|\n            with_guard(cached, name) do\n              cached.get!(name) # these shouldn't reload\n              cached.get!(name)\n            end\n          end\n\n          expect(service.created_envs).to eq([name])\n          expect(service.evicted_envs).to eq([])\n        end\n\n        it \"does not evict an environment marked for expiration, but is guarded\" do\n          Puppet[:environment_timeout] = 'unlimited'\n\n          expect(service).to receive(:expired?).never\n\n          with_environment_loaded(service) do |cached|\n            with_guard(cached, name) do\n              cached.get!(name)\n            end\n          end\n\n          expect(service.created_envs).to eq([name])\n          expect(service.evicted_envs).to eq([])\n        end\n\n        it \"evicts an environment that is no longer guarded\" do\n          with_environment_loaded(service) do |cached|\n            with_guard(cached, name) {}\n\n            cached.get!(name) # this reloads\n          end\n\n          expect(service.created_envs).to eq([name, name])\n          expect(service.evicted_envs).to eq([name])\n        end\n\n        it \"can nest guards\" do\n          with_environment_loaded(service) do |cached|\n            with_guard(cached, name) do\n              with_guard(cached, name) do\n                cached.get!(name) # doesn't reload\n              end\n            end\n          end\n\n          expect(service.created_envs).to eq([name])\n          expect(service.evicted_envs).to eq([])\n        end\n      end\n    end\n\n    context '#clear' do\n      let(:service) { ReplayExpirationService.new }\n\n      it \"evicts an environment\" do\n        with_environment_loaded(service) do |cached|\n          cached.clear(:an_environment)\n        end\n\n        expect(service.evicted_envs).to eq([:an_environment])\n      end\n\n      it \"normalizes environment name to symbol\" do\n        with_environment_loaded(service) do |cached|\n          cached.clear('an_environment')\n        end\n\n        expect(service.evicted_envs).to eq([:an_environment])\n      end\n    end\n\n    context '#clear_all' do\n      let(:service) { ReplayExpirationService.new }\n      let(:envdir) { File.expand_path(\"envdir\") }\n      let(:default_dir) { File.join(envdir, \"cached_env\", \"modules\") }\n      let(:expected_dir) { File.join(envdir, \"cached_env\", \"site\") }\n\n      let(:base_dir) do\n        FS::MemoryFile.a_directory(envdir, [\n          FS::MemoryFile.a_directory(\"cached_env\", [\n            FS::MemoryFile.a_missing_file(\"environment.conf\")\n          ])\n        ])\n      end\n\n      let(:updated_dir) do\n        FS::MemoryFile.a_directory(envdir, [\n          FS::MemoryFile.a_directory(\"cached_env\", [\n            FS::MemoryFile.a_directory(\"site\"),\n            FS::MemoryFile.a_missing_directory(\"modules\"),\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", <<-EOF)\n              modulepath=site\n              environment_timeout=unlimited\n            EOF\n          ])\n        ])\n      end\n\n      it 'evicts all environments' do\n        with_environment_loaded(service) do |cached|\n          cached.get(:an_environment)\n          cached.get(:another_environment)\n          cached.clear_all\n\n          expect(service.evicted_envs).to match([:an_environment, :another_environment])\n        end\n      end\n\n      it \"recomputes modulepath if 'get' is called before 'clear_all'\" do\n        cached_loader_from(:filesystem => [base_dir], :directory => base_dir) do |loader|\n          loader.get(:cached_env)\n\n          expect(Puppet.settings.value(:modulepath, :cached_env)).to eq(default_dir)\n\n          FS.overlay(updated_dir) do\n            loader.clear_all\n\n            expect(loader.get(:cached_env).modulepath).to contain_exactly(expected_dir)\n          end\n        end\n      end\n\n      it \"recomputes modulepath if 'list' is called before 'clear_all'\"  do\n        cached_loader_from(:filesystem => [base_dir], :directory => base_dir) do |loader|\n          loader.list\n\n          expect(Puppet.settings.value(:modulepath, :cached_env)).to eq(default_dir)\n\n          FS.overlay(updated_dir) do\n            loader.clear_all\n\n            expect(loader.get(:cached_env).modulepath).to contain_exactly(expected_dir)\n          end\n        end\n      end\n\n      it \"recomputes modulepath if 'get_conf' is called before 'clear_all'\" do\n        cached_loader_from(:filesystem => [base_dir], :directory => base_dir) do |loader|\n          loader.get_conf(:cached_env)\n\n          expect(Puppet.settings.value(:modulepath, :cached_env)).to eq(default_dir)\n\n          FS.overlay(updated_dir) do\n            loader.clear_all\n\n            expect(loader.get(:cached_env).modulepath).to contain_exactly(expected_dir)\n          end\n        end\n      end\n\n      it 'deletes environment text domains' do\n        with_environment_loaded(service) do |cached|\n          cached.get(:an_environment)\n          cached.clear_all\n\n          expect(FastGettext.text_domain).to eq(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN)\n        end\n      end\n    end\n  end\n\n  RSpec::Matchers.define :environment do |name|\n    match do |env|\n      env.name == name &&\n        (!@manifest || @manifest == env.manifest) &&\n        (!@modulepath || @modulepath == env.modulepath) &&\n        (!@full_modulepath || @full_modulepath == env.full_modulepath) &&\n        (!@config_version || @config_version == env.config_version) &&\n        (!@static_catalogs || @static_catalogs == env.static_catalogs?)\n    end\n\n    chain :with_manifest do |manifest|\n      @manifest = manifest\n    end\n\n    chain :with_modulepath do |modulepath|\n      @modulepath = modulepath\n    end\n\n    chain :with_full_modulepath do |full_modulepath|\n      @full_modulepath = full_modulepath\n    end\n\n    chain :with_config_version do |config_version|\n      @config_version = config_version\n    end\n\n    chain :with_static_catalogs do |static_catalogs|\n      @static_catalogs = static_catalogs\n    end\n\n    description do\n      \"environment #{expected}\" +\n        (@manifest ? \" with manifest #{@manifest}\" : \"\") +\n        (@modulepath ? \" with modulepath [#{@modulepath.join(', ')}]\" : \"\") +\n        (@full_modulepath ? \" with full_modulepath [#{@full_modulepath.join(', ')}]\" : \"\") +\n        (@config_version ? \" with config_version #{@config_version}\" : \"\") +\n        (@static_catalogs ? \" with static_catalogs #{@static_catalogs}\" : \"\")\n    end\n\n    failure_message do |env|\n      \"expected <#{env.name}: modulepath = [#{env.full_modulepath.join(', ')}], manifest = #{env.manifest}, config_version = #{env.config_version}>, static_catalogs = #{env.static_catalogs?} to be #{description}\"\n    end\n  end\n\n  RSpec::Matchers.define :match_environment_conf do |env_name|\n    match do |env_conf|\n      env_conf.path_to_env =~ /#{env_name}$/ &&\n        (!@env_path || File.join(@env_path,env_name.to_s) == env_conf.path_to_env) &&\n        (!@global_modulepath || @global_module_path == env_conf.global_module_path)\n    end\n\n    chain :with_env_path do |env_path|\n      @env_path = env_path.to_s\n    end\n\n    chain :with_global_module_path do |global_module_path|\n      @global_module_path = global_module_path\n    end\n\n    description do\n      \"EnvironmentConf #{expected}\" +\n        \" with path_to_env: #{@env_path ? @env_path : \"*\"}/#{env_name}\" +\n        (@global_module_path ? \" with global_module_path [#{@global_module_path.join(', ')}]\" : \"\")\n    end\n\n    failure_message do |env_conf|\n      \"expected #{env_conf.inspect} to be #{description}\"\n    end\n  end\n\n  def cached_loader_from(options, &block)\n    FS.overlay(*options[:filesystem]) do\n      environments = Puppet::Environments::Cached.new(\n        Puppet::Environments::Directories.new(\n          options[:directory],\n          options[:modulepath] || []\n        )\n      )\n      Puppet.override(:environments => environments) do\n        yield environments\n      end\n    end\n  end\n\n  def loader_from(options, &block)\n    FS.overlay(*options[:filesystem]) do\n      environments = Puppet::Environments::Directories.new(\n        options[:directory],\n        options[:modulepath] || []\n      )\n      Puppet.override(:environments => environments) do\n        yield environments\n      end\n    end\n  end\n\n  def using_expiration_service(service)\n    begin\n      orig_svc = Puppet::Environments::Cached.cache_expiration_service\n      Puppet::Environments::Cached.cache_expiration_service = service\n      yield\n    ensure\n      Puppet::Environments::Cached.cache_expiration_service = orig_svc\n    end\n  end\n\n  # The environment named `:an_environment` will already be loaded when the\n  # block is yielded to\n  def with_environment_loaded(service, &block)\n    loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|\n      using_expiration_service(service) do\n        cached = Puppet::Environments::Cached.new(loader)\n        cached.get!(:an_environment)\n\n        yield cached if block_given?\n      end\n    end\n  end\n\n  class ReplayExpirationService < Puppet::Environments::Cached::DefaultCacheExpirationService\n    attr_reader :created_envs, :evicted_envs\n\n    def initialize\n      @created_envs = []\n      @evicted_envs = []\n    end\n\n    def created(env)\n      @created_envs << env.name\n    end\n\n    def evicted(env_name)\n      @evicted_envs << env_name\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/etc_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/character_encoding'\n\n# The Ruby::Etc module is largely non-functional on Windows - many methods\n# simply return nil regardless of input, the Etc::Group struct is not defined,\n# and Etc::Passwd is missing fields\n# We want to test that:\n# - We correctly set external encoding values IF they're valid UTF-8 bytes\n# - We do not modify non-UTF-8 values if they're NOT valid UTF-8 bytes\n\ndescribe Puppet::Etc, :if => !Puppet::Util::Platform.windows? do\n  # http://www.fileformat.info/info/unicode/char/5e0c/index.htm\n  # 希 Han Character 'rare; hope, expect, strive for'\n  # In EUC_KR: \\xfd \\xf1 - 253 241\n  # In UTF-8: \\u5e0c - \\xe5 \\xb8 \\x8c - 229 184 140\n  let(:euc_kr) { [253, 241].pack('C*').force_encoding(Encoding::EUC_KR) } # valid_encoding? == true\n  let(:euc_kr_as_binary) { [253, 241].pack('C*') } # valid_encoding? == true\n  let(:euc_kr_as_utf_8) { [253, 241].pack('C*').force_encoding(Encoding::UTF_8) } # valid_encoding? == false\n\n  # characters representing different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  let(:mixed_utf_8) { \"A\\u06FF\\u16A0\\u{2070E}\".force_encoding(Encoding::UTF_8) } # Aۿᚠ𠜎\n  let(:mixed_utf_8_as_binary) { \"A\\u06FF\\u16A0\\u{2070E}\".force_encoding(Encoding::BINARY) }\n  let(:mixed_utf_8_as_euc_kr) { \"A\\u06FF\\u16A0\\u{2070E}\".force_encoding(Encoding::EUC_KR) }\n\n  # An uninteresting value that ruby might return in an Etc struct.\n  let(:root) { 'root' }\n\n  # Set up example Etc Group structs with values representative of what we would\n  # get back in these encodings\n\n  let(:utf_8_group_struct) do\n    group = Etc::Group.new\n    # In a UTF-8 environment, these values will come back as UTF-8, even if\n    # they're not valid UTF-8. We do not modify anything about either the\n    # valid or invalid UTF-8 strings.\n\n    # Group member contains a mix of valid and invalid UTF-8-labeled strings\n    group.mem = [mixed_utf_8, root.dup.force_encoding(Encoding::UTF_8), euc_kr_as_utf_8]\n    # group name contains same EUC_KR bytes labeled as UTF-8\n    group.name = euc_kr_as_utf_8\n    # group passwd field is valid UTF-8\n    group.passwd = mixed_utf_8\n    group.gid = 12345\n    group\n  end\n\n  let(:euc_kr_group_struct) do\n    # In an EUC_KR environment, values will come back as EUC_KR, even if they're\n    # not valid in that encoding. For values that are valid in UTF-8 we expect\n    # their external encoding to be set to UTF-8 by Puppet::Etc. For values that\n    # are invalid in UTF-8, we expect the string to be kept intact, unmodified,\n    # as we can't transcode it.\n    group = Etc::Group.new\n    group.mem = [euc_kr, root.dup.force_encoding(Encoding::EUC_KR), mixed_utf_8_as_euc_kr]\n    group.name = euc_kr\n    group.passwd = mixed_utf_8_as_euc_kr\n    group.gid = 12345\n    group\n  end\n\n  let(:ascii_group_struct) do\n    # In a POSIX environment, any strings containing only values under\n    # code-point 128 will be returned as ASCII, whereas anything above that\n    # point will be returned as BINARY. In either case we override the encoding\n    # to UTF-8 if that would be valid.\n    group = Etc::Group.new\n    group.mem = [euc_kr_as_binary, root.dup.force_encoding(Encoding::ASCII), mixed_utf_8_as_binary]\n    group.name = euc_kr_as_binary\n    group.passwd = mixed_utf_8_as_binary\n    group.gid = 12345\n    group\n  end\n\n  let(:utf_8_user_struct) do\n    user = Etc::Passwd.new\n    # user name contains same EUC_KR bytes labeled as UTF-8\n    user.name = euc_kr_as_utf_8\n    # group passwd field is valid UTF-8\n    user.passwd = mixed_utf_8\n    user.uid = 12345\n    user\n  end\n\n  let(:euc_kr_user_struct) do\n    user = Etc::Passwd.new\n    user.name = euc_kr\n    user.passwd = mixed_utf_8_as_euc_kr\n    user.uid = 12345\n    user\n  end\n\n  let(:ascii_user_struct) do\n    user = Etc::Passwd.new\n    user.name = euc_kr_as_binary\n    user.passwd = mixed_utf_8_as_binary\n    user.uid = 12345\n    user\n  end\n\n  shared_examples \"methods that return an overridden group struct from Etc\" do |param|\n    it \"should return a new Struct object with corresponding canonical_ members\" do\n      group = Etc::Group.new\n      expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(group)\n      puppet_group = Puppet::Etc.send(subject, *param)\n\n      expect(puppet_group.members).to include(*group.members)\n      expect(puppet_group.members).to include(*group.members.map { |mem| \"canonical_#{mem}\".to_sym })\n\n      # Confirm we haven't just added the new members to the original struct object, ie this is really a new struct\n      expect(group.members.any? { |elem| elem.match(/^canonical_/) }).to be_falsey\n    end\n\n    context \"when Encoding.default_external is UTF-8\" do\n      before do\n        expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(utf_8_group_struct)\n      end\n\n      let(:overridden) {\n        PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do\n          Puppet::Etc.send(subject, *param)\n        end\n      }\n\n      it \"should leave the valid UTF-8 values in arrays unmodified\" do\n        expect(overridden.mem[0]).to eq(mixed_utf_8)\n        expect(overridden.mem[1]).to eq(root)\n      end\n\n      it \"should replace invalid characters with replacement characters in invalid UTF-8 values in arrays\" do\n        expect(overridden.mem[2]).to eq(\"\\uFFFD\\uFFFD\")\n      end\n\n      it \"should keep an unmodified version of the invalid UTF-8 values in arrays in the corresponding canonical_ member\" do\n        expect(overridden.canonical_mem[2]).to eq(euc_kr_as_utf_8)\n      end\n\n      it \"should leave the valid UTF-8 values unmodified\" do\n        expect(overridden.passwd).to eq(mixed_utf_8)\n      end\n\n      it \"should replace invalid characters with '?' characters in invalid UTF-8 values\" do\n        expect(overridden.name).to eq(\"\\uFFFD\\uFFFD\")\n      end\n\n      it \"should keep an unmodified version of the invalid UTF-8 values in the corresponding canonical_ member\" do\n        expect(overridden.canonical_name).to eq(euc_kr_as_utf_8)\n      end\n\n      it \"should copy all values to the new struct object\" do\n        # Confirm we've actually copied all the values to the canonical_members\n        utf_8_group_struct.each_pair do |member, value|\n          expect(overridden[\"canonical_#{member}\"]).to eq(value)\n\n          # Confirm we've reassigned all non-string and array values\n          if !value.is_a?(String) && !value.is_a?(Array)\n            expect(overridden[member]).to eq(value)\n            expect(overridden[member].object_id).to eq(value.object_id)\n          end\n        end\n      end\n    end\n\n    context \"when Encoding.default_external is EUC_KR (i.e., neither UTF-8 nor POSIX)\" do\n      before do\n        expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(euc_kr_group_struct)\n      end\n\n      let(:overridden) {\n        PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::EUC_KR) do\n          Puppet::Etc.send(subject, *param)\n        end\n      }\n\n      it \"should override EUC_KR-labeled values in arrays to UTF-8 if that would result in valid UTF-8\" do\n        expect(overridden.mem[2]).to eq(mixed_utf_8)\n        expect(overridden.mem[1]).to eq(root)\n      end\n\n      it \"should leave valid EUC_KR-labeled values that would not be valid UTF-8 in arrays unmodified\" do\n        expect(overridden.mem[0]).to eq(euc_kr)\n      end\n\n      it \"should override EUC_KR-labeled values to UTF-8 if that would result in valid UTF-8\" do\n        expect(overridden.passwd).to eq(mixed_utf_8)\n      end\n\n      it \"should leave valid EUC_KR-labeled values that would not be valid UTF-8 unmodified\" do\n        expect(overridden.name).to eq(euc_kr)\n      end\n\n      it \"should copy all values to the new struct object\" do\n        # Confirm we've actually copied all the values to the canonical_members\n        euc_kr_group_struct.each_pair do |member, value|\n          expect(overridden[\"canonical_#{member}\"]).to eq(value)\n\n          # Confirm we've reassigned all non-string and array values\n          if !value.is_a?(String) && !value.is_a?(Array)\n            expect(overridden[member]).to eq(value)\n            expect(overridden[member].object_id).to eq(value.object_id)\n          end\n        end\n      end\n    end\n\n    context \"when Encoding.default_external is POSIX (ASCII-7bit)\" do\n      before do\n        expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(ascii_group_struct)\n      end\n\n      let(:overridden) {\n        PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ASCII) do\n          Puppet::Etc.send(subject, *param)\n        end\n      }\n\n      it \"should not modify binary values in arrays that would be invalid UTF-8\" do\n        expect(overridden.mem[0]).to eq(euc_kr_as_binary)\n      end\n\n      it \"should set the encoding to UTF-8 on binary values in arrays that would be valid UTF-8\" do\n        expect(overridden.mem[1]).to eq(root.dup.force_encoding(Encoding::UTF_8))\n        expect(overridden.mem[2]).to eq(mixed_utf_8)\n      end\n\n      it \"should not modify binary values that would be invalid UTF-8\" do\n        expect(overridden.name).to eq(euc_kr_as_binary)\n      end\n\n      it \"should set the encoding to UTF-8 on binary values that would be valid UTF-8\" do\n        expect(overridden.passwd).to eq(mixed_utf_8)\n      end\n\n      it \"should copy all values to the new struct object\" do\n        # Confirm we've actually copied all the values to the canonical_members\n        ascii_group_struct.each_pair do |member, value|\n          expect(overridden[\"canonical_#{member}\"]).to eq(value)\n\n          # Confirm we've reassigned all non-string and array values\n          if !value.is_a?(String) && !value.is_a?(Array)\n            expect(overridden[member]).to eq(value)\n            expect(overridden[member].object_id).to eq(value.object_id)\n          end\n        end\n      end\n    end\n  end\n\n  shared_examples \"methods that return an overridden user struct from Etc\" do |param|\n    it \"should return a new Struct object with corresponding canonical_ members\" do\n      user = Etc::Passwd.new\n      expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(user)\n      puppet_user = Puppet::Etc.send(subject, *param)\n\n      expect(puppet_user.members).to include(*user.members)\n      expect(puppet_user.members).to include(*user.members.map { |mem| \"canonical_#{mem}\".to_sym })\n      # Confirm we haven't just added the new members to the original struct object, ie this is really a new struct\n      expect(user.members.any? { |elem| elem.match(/^canonical_/)}).to be_falsey\n    end\n\n    context \"when Encoding.default_external is UTF-8\" do\n      before do\n        expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(utf_8_user_struct)\n      end\n\n      let(:overridden) {\n        PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do\n          Puppet::Etc.send(subject, *param)\n        end\n      }\n\n      it \"should leave the valid UTF-8 values unmodified\" do\n        expect(overridden.passwd).to eq(mixed_utf_8)\n      end\n\n      it \"should replace invalid characters with unicode replacement characters in invalid UTF-8 values\" do\n        expect(overridden.name).to eq(\"\\uFFFD\\uFFFD\")\n      end\n\n      it \"should keep an unmodified version of the invalid UTF-8 values in the corresponding canonical_ member\" do\n        expect(overridden.canonical_name).to eq(euc_kr_as_utf_8)\n      end\n\n      it \"should copy all values to the new struct object\" do\n        # Confirm we've actually copied all the values to the canonical_members\n        utf_8_user_struct.each_pair do |member, value|\n          expect(overridden[\"canonical_#{member}\"]).to eq(value)\n\n          # Confirm we've reassigned all non-string and array values\n          if !value.is_a?(String) && !value.is_a?(Array)\n            expect(overridden[member]).to eq(value)\n            expect(overridden[member].object_id).to eq(value.object_id)\n          end\n        end\n      end\n    end\n\n    context \"when Encoding.default_external is EUC_KR (i.e., neither UTF-8 nor POSIX)\" do\n      before do\n        expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(euc_kr_user_struct)\n      end\n\n      let(:overridden) {\n        PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::EUC_KR) do\n          Puppet::Etc.send(subject, *param)\n        end\n      }\n\n      it \"should override valid UTF-8 EUC_KR-labeled values to UTF-8\" do\n        expect(overridden.passwd).to eq(mixed_utf_8)\n      end\n\n      it \"should leave invalid EUC_KR-labeled values unmodified\" do\n        expect(overridden.name).to eq(euc_kr)\n      end\n\n      it \"should copy all values to the new struct object\" do\n        # Confirm we've actually copied all the values to the canonical_members\n        euc_kr_user_struct.each_pair do |member, value|\n          expect(overridden[\"canonical_#{member}\"]).to eq(value)\n\n          # Confirm we've reassigned all non-string and array values\n          if !value.is_a?(String) && !value.is_a?(Array)\n            expect(overridden[member]).to eq(value)\n            expect(overridden[member].object_id).to eq(value.object_id)\n          end\n        end\n      end\n    end\n\n    context \"when Encoding.default_external is POSIX (ASCII-7bit)\" do\n      before do\n        expect(Etc).to receive(subject).with(param.nil? ? no_args : param).and_return(ascii_user_struct)\n      end\n\n      let(:overridden) {\n        PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ASCII) do\n          Puppet::Etc.send(subject, *param)\n        end\n      }\n\n      it \"should not modify binary values that would be invalid UTF-8\" do\n        expect(overridden.name).to eq(euc_kr_as_binary)\n      end\n\n      it \"should set the encoding to UTF-8 on binary values that would be valid UTF-8\" do\n        expect(overridden.passwd).to eq(mixed_utf_8)\n      end\n\n      it \"should copy all values to the new struct object\" do\n        # Confirm we've actually copied all the values to the canonical_members\n        ascii_user_struct.each_pair do |member, value|\n          expect(overridden[\"canonical_#{member}\"]).to eq(value)\n\n          # Confirm we've reassigned all non-string and array values\n          if !value.is_a?(String) && !value.is_a?(Array)\n            expect(overridden[member]).to eq(value)\n            expect(overridden[member].object_id).to eq(value.object_id)\n          end\n        end\n      end\n    end\n  end\n\n  describe :getgrent do\n    it_should_behave_like \"methods that return an overridden group struct from Etc\"\n  end\n\n  describe :getgrnam do\n    it_should_behave_like \"methods that return an overridden group struct from Etc\", 'foo'\n\n    it \"should call Etc.getgrnam with the supplied group name\" do\n      expect(Etc).to receive(:getgrnam).with('foo')\n      Puppet::Etc.getgrnam('foo')\n    end\n  end\n\n  describe :getgrgid do\n    it_should_behave_like \"methods that return an overridden group struct from Etc\", 0\n\n    it \"should call Etc.getgrgid with supplied group id\" do\n      expect(Etc).to receive(:getgrgid).with(0)\n      Puppet::Etc.getgrgid(0)\n    end\n  end\n\n  describe :getpwent do\n    it_should_behave_like \"methods that return an overridden user struct from Etc\"\n  end\n\n  describe :getpwnam do\n    it_should_behave_like \"methods that return an overridden user struct from Etc\", 'foo'\n\n    it \"should call Etc.getpwnam with that username\" do\n      expect(Etc).to receive(:getpwnam).with('foo')\n      Puppet::Etc.getpwnam('foo')\n    end\n  end\n\n  describe :getpwuid do\n    it_should_behave_like \"methods that return an overridden user struct from Etc\", 2\n\n    it \"should call Etc.getpwuid with the id\" do\n      expect(Etc).to receive(:getpwuid).with(2)\n      Puppet::Etc.getpwuid(2)\n    end\n  end\n\n  describe :group do\n    it 'should return the next group struct if a block is not provided' do\n      expect(Puppet::Etc).to receive(:getgrent).and_return(ascii_group_struct)\n\n      expect(Puppet::Etc.group).to eql(ascii_group_struct)\n    end\n\n    it 'should iterate over the available groups if a block is provided' do\n      expected_groups = [\n        utf_8_group_struct,\n        euc_kr_group_struct,\n        ascii_group_struct\n      ]\n      allow(Puppet::Etc).to receive(:getgrent).and_return(*(expected_groups + [nil]))\n\n      expect(Puppet::Etc).to receive(:setgrent)\n      expect(Puppet::Etc).to receive(:endgrent)\n\n      actual_groups = []\n      Puppet::Etc.group { |group| actual_groups << group }\n\n      expect(actual_groups).to eql(expected_groups)\n    end\n  end\n\n  describe \"endgrent\" do\n    it \"should call Etc.getgrent\" do\n      expect(Etc).to receive(:getgrent)\n      Puppet::Etc.getgrent\n    end\n  end\n\n  describe \"setgrent\" do\n    it \"should call Etc.setgrent\" do\n      expect(Etc).to receive(:setgrent)\n      Puppet::Etc.setgrent\n    end\n  end\n\n  describe \"endpwent\" do\n    it \"should call Etc.endpwent\" do\n      expect(Etc).to receive(:endpwent)\n      Puppet::Etc.endpwent\n    end\n  end\n\n  describe \"setpwent\" do\n    it \"should call Etc.setpwent\" do\n      expect(Etc).to receive(:setpwent)\n      Puppet::Etc.setpwent\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/external/pson_spec.rb",
    "content": "# Encoding: UTF-8\nrequire 'spec_helper'\n\ndescribe 'PSON', if: Puppet.features.pson? do\n  {\n    'foo' => '\"foo\"',\n    1 => '1',\n    \"\\x80\" => \"\\\"\\x80\\\"\",\n    [] => '[]'\n  }.each do |str, expect|\n    it \"should be able to encode #{str.inspect}\" do\n      got = str.to_pson\n      if got.respond_to? :force_encoding\n        expect(got.force_encoding('binary')).to eq(expect.force_encoding('binary'))\n      else\n        expect(got).to eq(expect)\n      end\n    end\n  end\n\n  it \"should be able to handle arbitrary binary data\" do\n    bin_string = (1..20000).collect { |i| ((17*i+13*i*i) % 255).chr }.join\n    parsed = PSON.parse(%Q{{ \"type\": \"foo\", \"data\": #{bin_string.to_pson} }})[\"data\"]\n\n    if parsed.respond_to? :force_encoding\n      parsed.force_encoding('binary')\n      bin_string.force_encoding('binary')\n    end\n\n    expect(parsed).to eq(bin_string)\n  end\n\n  it \"should be able to handle UTF8 that isn't a real unicode character\" do\n    s = [\"\\355\\274\\267\"]\n    expect(PSON.parse( [s].to_pson )).to eq([s])\n  end\n\n  it \"should be able to handle UTF8 for \\\\xFF\" do\n    s = [\"\\xc3\\xbf\"]\n    expect(PSON.parse( [s].to_pson )).to eq([s])\n  end\n\n  it \"should be able to handle invalid UTF8 bytes\" do\n    s = [\"\\xc3\\xc3\"]\n    expect(PSON.parse( [s].to_pson )).to eq([s])\n  end\n\n  it \"should be able to parse JSON containing UTF-8 characters in strings\" do\n    s = '{ \"foö\": \"bár\" }'\n    expect { PSON.parse s }.not_to raise_error\n  end\n\n  it 'ignores \"document_type\" during parsing' do\n    text = '{\"data\":{},\"document_type\":\"Node\"}'\n\n    expect(PSON.parse(text)).to eq({\"data\" => {}, \"document_type\" => \"Node\"})\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/catalog_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/indirector/facts/facter'\nrequire 'puppet/indirector/facts/rest'\n\ndescribe Puppet::Face[:catalog, '0.0.1'] do\n\n  describe '#download' do\n    let(:model) { Puppet::Node::Facts }\n    let(:test_data) { model.new('puppet.node.test', {test_fact: 'catalog_face_request_test_value'}) }\n    let(:catalog) { Puppet::Resource::Catalog.new('puppet.node.test', Puppet::Node::Environment.remote(Puppet[:environment].to_sym)) }\n\n    before(:each) do\n      Puppet[:facts_terminus] = :memory\n      Puppet::Node::Facts.indirection.save(test_data)\n      allow(Puppet::Face[:catalog, \"0.0.1\"]).to receive(:save).once\n\n      Puppet.settings.parse_config(<<-CONF)\n[main]\nserver=puppet.server.test\ncertname=puppet.node.test\nCONF\n\n      # Faces start in :user run mode\n      Puppet.settings.preferred_run_mode = :user\n    end\n\n    it \"adds facts to the catalog request\" do\n      stub_request(:post, 'https://puppet.server.test:8140/puppet/v3/catalog/puppet.node.test?environment=*root*')\n        .with(\n          headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },\n          body: hash_including(facts: URI.encode_www_form_component(Puppet::Node::Facts.indirection.find('puppet.node.test').to_json))\n        ).to_return(:status => 200, :body => catalog.render(:json), :headers => {'Content-Type' => 'application/json'})\n      subject.download\n    end\n  end\nend\n\n\n"
  },
  {
    "path": "spec/unit/face/config_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\n\ndescribe Puppet::Face[:config, '0.0.1'] do\n\n  let(:config) { described_class }\n\n  def render(action, result)\n    config.get_action(action).when_rendering(:console).call(result)\n  end\n\n  FS = Puppet::FileSystem\n\n  it \"prints a single setting without the name\" do\n    Puppet[:trace] = true\n\n    result = subject.print(\"trace\")\n    expect(render(:print, result)).to eq(\"true\\n\")\n  end\n\n  it \"prints multiple settings with the names\" do\n    Puppet[:trace] = true\n    Puppet[:syslogfacility] = \"file\"\n\n    result = subject.print(\"trace\", \"syslogfacility\")\n    expect(render(:print, result)).to eq(<<-OUTPUT)\nsyslogfacility = file\ntrace = true\n    OUTPUT\n  end\n\n  it \"prints environment_timeout=unlimited correctly\" do\n    Puppet[:environment_timeout] = \"unlimited\"\n\n    result = subject.print(\"environment_timeout\")\n    expect(render(:print, result)).to eq(\"unlimited\\n\")\n  end\n\n  it \"prints arrays correctly\" do\n    pending \"Still doesn't print arrays like they would appear in config\"\n    Puppet[:server_list] = %w{server1 server2}\n\n    result = subject.print(\"server_list\")\n    expect(render(:print, result)).to eq(\"server1, server2\\n\")\n  end\n\n  it \"prints the setting from the selected section\" do\n    Puppet.settings.parse_config(<<-CONF)\n    [user]\n    syslogfacility = file\n    CONF\n\n    result = subject.print(\"syslogfacility\", :section => \"user\")\n    expect(render(:print, result)).to eq(\"file\\n\")\n  end\n\n  it \"prints the section and environment, and not a warning, when a section is given and verbose is set\" do\n    Puppet.settings.parse_config(<<-CONF)\n    [user]\n    syslogfacility = file\n    CONF\n\n    #This has to be after the settings above, which resets the value\n    Puppet[:log_level] = 'info'\n\n    expect(Puppet).not_to receive(:warning)\n    expect {\n      result = subject.print(\"syslogfacility\", :section => \"user\")\n      expect(render(:print, result)).to eq(\"file\\n\")\n    }.to output(\"\\e[1;33mResolving settings from section 'user' in environment 'production'\\e[0m\\n\").to_stderr\n  end\n\n  it \"prints a warning and the section and environment when no section is given and verbose is set\" do\n    Puppet[:log_level] = 'info'\n    Puppet[:trace] = true\n\n    expect(Puppet).to receive(:warning).with(\"No section specified; defaulting to 'main'.\\nSet the config section \" +\n      \"by using the `--section` flag.\\nFor example, `puppet config --section user print foo`.\\nFor more \" +\n      \"information, see https://puppet.com/docs/puppet/latest/configuration.html\")\n    expect {\n      result = subject.print(\"trace\")\n      expect(render(:print, result)).to eq(\"true\\n\")\n    }.to output(\"\\e[1;33mResolving settings from section 'main' in environment 'production'\\e[0m\\n\").to_stderr\n  end\n\n  it \"does not print a warning or the section and environment when no section is given and verbose is not set\" do\n    Puppet[:log_level] = 'notice'\n    Puppet[:trace] = true\n\n    expect(Puppet).not_to receive(:warning)\n    expect {\n      result = subject.print(\"trace\")\n      expect(render(:print, result)).to eq(\"true\\n\")\n    }.to_not output.to_stderr\n  end\n\n  it \"defaults to all when no arguments are given\" do\n    result = subject.print\n    expect(render(:print, result).lines.to_a.length).to eq(Puppet.settings.to_a.length)\n  end\n\n  it \"prints out all of the settings when asked for 'all'\" do\n    result = subject.print('all')\n    expect(render(:print, result).lines.to_a.length).to eq(Puppet.settings.to_a.length)\n  end\n\n  it \"stringifies all keys for network format handlers to consume\" do\n    Puppet[:syslogfacility] = \"file\"\n\n    result = subject.print\n    expect(result[\"syslogfacility\"]).to eq(\"file\")\n    expect(result.keys).to all(be_a(String))\n  end\n\n  it \"stringifies multiple keys for network format handlers to consume\" do\n    Puppet[:trace] = true\n    Puppet[:syslogfacility] = \"file\"\n\n    expect(subject.print(\"trace\", \"syslogfacility\")).to eq({\"syslogfacility\" => \"file\", \"trace\" => true})\n  end\n\n  it \"stringifies single key for network format handlers to consume\" do\n    Puppet[:trace] = true\n\n    expect(subject.print(\"trace\")).to eq({\"trace\" => true})\n  end\n\n  context \"when setting config values\" do\n    let(:config_file) { '/foo/puppet.conf' }\n    let(:path) { Pathname.new(config_file).expand_path }\n    before(:each) do\n      Puppet[:config] = config_file\n      allow(Puppet::FileSystem).to receive(:pathname).with(path.to_s).and_return(path)\n      allow(Puppet::FileSystem).to receive(:touch)\n    end\n\n    it \"prints the section and environment when no section is given and verbose is set\" do\n      Puppet[:log_level] = 'info'\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      expect {\n        subject.set('certname', 'bar')\n      }.to output(\"\\e[1;33mResolving settings from section 'main' in environment 'production'\\e[0m\\n\").to_stderr\n    end\n\n    it \"prints the section and environment when a section is given and verbose is set\" do\n      Puppet[:log_level] = 'info'\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      expect {\n        subject.set('certname', 'bar', {:section => \"baz\"})\n      }.to output(\"\\e[1;33mResolving settings from section 'baz' in environment 'production'\\e[0m\\n\").to_stderr\n    end\n\n    it \"writes to the correct puppet config file\" do\n      expect(Puppet::FileSystem).to receive(:open).with(path, anything, anything)\n      subject.set('certname', 'bar')\n    end\n\n    it \"creates a config file if one does not exist\" do\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      expect(Puppet::FileSystem).to receive(:touch).with(path)\n      subject.set('certname', 'bar')\n    end\n\n    it \"sets the supplied config/value in the default section (main)\" do\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      config = Puppet::Settings::IniFile.new([Puppet::Settings::IniFile::DefaultSection.new])\n      manipulator = Puppet::Settings::IniFile::Manipulator.new(config)\n      allow(Puppet::Settings::IniFile::Manipulator).to receive(:new).and_return(manipulator)\n\n      expect(manipulator).to receive(:set).with(\"main\", \"certname\", \"bar\")\n      subject.set('certname', 'bar')\n    end\n\n    it \"sets the value in the supplied section\" do\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      config = Puppet::Settings::IniFile.new([Puppet::Settings::IniFile::DefaultSection.new])\n      manipulator = Puppet::Settings::IniFile::Manipulator.new(config)\n      allow(Puppet::Settings::IniFile::Manipulator).to receive(:new).and_return(manipulator)\n\n      expect(manipulator).to receive(:set).with(\"baz\", \"certname\", \"bar\")\n      subject.set('certname', 'bar', {:section => \"baz\"})\n    end\n\n    it \"does not duplicate an existing default section when a section is not specified\" do\n      contents = <<-CONF\n      [main]\n      myport = 4444\n      CONF\n\n      myfile = StringIO.new(contents)\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(myfile)\n\n      subject.set('certname', 'bar')\n\n      expect(myfile.string).to match(/certname = bar/)\n      expect(myfile.string).not_to match(/main.*main/)\n    end\n\n    it \"opens the file with UTF-8 encoding\" do\n      expect(Puppet::FileSystem).to receive(:open).with(path, nil, 'r+:UTF-8')\n      subject.set('certname', 'bar')\n    end\n\n    it \"sets settings into the [server] section when setting [master] section settings\" do\n      initial_contents = <<~CONFIG\n        [master]\n        node_terminus = none\n        reports = log\n      CONFIG\n\n      myinitialfile = StringIO.new(initial_contents)\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(myinitialfile)\n\n      expect {\n        subject.set('node_terminus', 'exec', {:section => 'master'})\n      }.to output(\"Deleted setting from 'master': 'node_terminus = none', and adding it to 'server' section\\n\").to_stdout\n\n      expect(myinitialfile.string).to match(<<~CONFIG)\n        [master]\n        reports = log\n        [server]\n        node_terminus = exec\n      CONFIG\n    end\n\n    it \"setting [master] section settings, sets settings into [server] section instead\" do\n      myinitialfile = StringIO.new(\"\")\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(myinitialfile)\n      subject.set('node_terminus', 'exec', {:section => 'master'})\n\n      expect(myinitialfile.string).to match(<<~CONFIG)\n        [server]\n        node_terminus = exec\n      CONFIG\n    end\n  end\n\n  context 'when the puppet.conf file does not exist' do\n    let(:config_file) { '/foo/puppet.conf' }\n    let(:path) { Pathname.new(config_file).expand_path }\n\n    before(:each) do\n      Puppet[:config] = config_file\n      allow(Puppet::FileSystem).to receive(:pathname).with(path.to_s).and_return(path)\n    end\n\n    it 'prints a message when the puppet.conf file does not exist' do\n      allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(false)\n      expect(Puppet).to receive(:warning).with(\"The puppet.conf file does not exist #{path.to_s}\")\n      subject.delete('setting', {:section => 'main'})\n    end\n  end\n\n  context 'when deleting config values' do\n    let(:config_file) { '/foo/puppet.conf' }\n    let(:path) { Pathname.new(config_file).expand_path }\n    before(:each) do\n      Puppet[:config] = config_file\n      allow(Puppet::FileSystem).to receive(:pathname).with(path.to_s).and_return(path)\n      allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n    end\n\n    it 'prints a message about what was deleted' do\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      config = Puppet::Settings::IniFile.new([Puppet::Settings::IniFile::DefaultSection.new])\n      manipulator = Puppet::Settings::IniFile::Manipulator.new(config)\n      allow(Puppet::Settings::IniFile::Manipulator).to receive(:new).and_return(manipulator)\n\n      expect(manipulator).to receive(:delete).with('main', 'setting').and_return('    setting=value')\n      expect {\n        subject.delete('setting', {:section => 'main'})\n      }.to output(\"Deleted setting from 'main': 'setting=value'\\n\").to_stdout\n    end\n\n    it 'prints a warning when a setting is not found to delete' do\n      allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n      config = Puppet::Settings::IniFile.new([Puppet::Settings::IniFile::DefaultSection.new])\n      manipulator = Puppet::Settings::IniFile::Manipulator.new(config)\n      allow(Puppet::Settings::IniFile::Manipulator).to receive(:new).and_return(manipulator)\n\n      expect(manipulator).to receive(:delete).with('main', 'setting').and_return(nil)\n      expect(Puppet).to receive(:warning).with(\"No setting found in configuration file for section 'main' setting name 'setting'\")\n      subject.delete('setting', {:section => 'main'})\n    end\n\n    ['master', 'server'].each do |section|\n      describe \"when deleting from [#{section}] section\" do\n        it \"deletes section values from both [server] and [master] sections\" do\n          allow(Puppet::FileSystem).to receive(:open).with(path, anything, anything).and_yield(StringIO.new)\n          config = Puppet::Settings::IniFile.new([Puppet::Settings::IniFile::DefaultSection.new])\n          manipulator = Puppet::Settings::IniFile::Manipulator.new(config)\n          allow(Puppet::Settings::IniFile::Manipulator).to receive(:new).and_return(manipulator)\n\n          expect(manipulator).to receive(:delete).with('master', 'setting').and_return('setting=value')\n          expect(manipulator).to receive(:delete).with('server', 'setting').and_return('setting=value')\n          expect {\n            subject.delete('setting', {:section => section})\n          }.to output(/Deleted setting from 'master': 'setting'\\nDeleted setting from 'server': 'setting'\\n/).to_stdout\n        end\n      end\n    end\n\n  end\n\n  shared_examples_for :config_printing_a_section do |section|\n    def add_section_option(args, section)\n      args << { :section => section } if section\n      args\n    end\n\n    it \"prints directory env settings for an env that exists\" do\n      FS.overlay(\n        FS::MemoryFile.a_directory(File.expand_path(\"/dev/null/environments\"), [\n          FS::MemoryFile.a_directory(\"production\", [\n            FS::MemoryFile.a_missing_file(\"environment.conf\"),\n          ]),\n        ])\n      ) do\n        args = \"environmentpath\",\"manifest\",\"modulepath\",\"environment\",\"basemodulepath\"\n\n        result = subject.print(*add_section_option(args, section))\n        expect(render(:print, result)).to eq(<<-OUTPUT)\nbasemodulepath = #{File.expand_path(\"/some/base\")}\nenvironment = production\nenvironmentpath = #{File.expand_path(\"/dev/null/environments\")}\nmanifest = #{File.expand_path(\"/dev/null/environments/production/manifests\")}\nmodulepath = #{File.expand_path(\"/dev/null/environments/production/modules\")}#{File::PATH_SEPARATOR}#{File.expand_path(\"/some/base\")}\n        OUTPUT\n      end\n    end\n\n    it \"interpolates settings in environment.conf\" do\n      FS.overlay(\n        FS::MemoryFile.a_directory(File.expand_path(\"/dev/null/environments\"), [\n          FS::MemoryFile.a_directory(\"production\", [\n            FS::MemoryFile.a_regular_file_containing(\"environment.conf\", <<-CONTENT),\n            modulepath=/custom/modules#{File::PATH_SEPARATOR}$basemodulepath\n            CONTENT\n          ]),\n        ])\n      ) do\n        args = \"environmentpath\",\"manifest\",\"modulepath\",\"environment\",\"basemodulepath\"\n\n        result = subject.print(*add_section_option(args, section))\n        expect(render(:print, result)).to eq(<<-OUTPUT)\nbasemodulepath = #{File.expand_path(\"/some/base\")}\nenvironment = production\nenvironmentpath = #{File.expand_path(\"/dev/null/environments\")}\nmanifest = #{File.expand_path(\"/dev/null/environments/production/manifests\")}\nmodulepath = #{File.expand_path(\"/custom/modules\")}#{File::PATH_SEPARATOR}#{File.expand_path(\"/some/base\")}\n        OUTPUT\n      end\n    end\n\n    it \"prints the default configured env settings for an env that does not exist\" do\n      Puppet[:environment] = 'doesnotexist'\n\n      FS.overlay(\n        FS::MemoryFile.a_directory(File.expand_path(\"/dev/null/environments\"), [\n          FS::MemoryFile.a_missing_file(\"doesnotexist\")\n        ])\n      ) do\n        args = \"environmentpath\",\"manifest\",\"modulepath\",\"environment\",\"basemodulepath\"\n\n        result = subject.print(*add_section_option(args, section))\n        expect(render(:print, result)).to eq(<<-OUTPUT)\nbasemodulepath = #{File.expand_path(\"/some/base\")}\nenvironment = doesnotexist\nenvironmentpath = #{File.expand_path(\"/dev/null/environments\")}\nmanifest = \nmodulepath = \n        OUTPUT\n      end\n    end\n  end\n\n  context \"when printing environment settings\" do\n    context \"from main section\" do\n      before(:each) do\n        Puppet.settings.parse_config(<<-CONF)\n        [main]\n        environmentpath=$confdir/environments\n        basemodulepath=/some/base\n        CONF\n      end\n\n      it_behaves_like :config_printing_a_section, nil\n    end\n\n    context \"from master section\" do\n      before(:each) do\n        Puppet.settings.parse_config(<<-CONF)\n        [master]\n        environmentpath=$confdir/environments\n        basemodulepath=/some/base\n        CONF\n      end\n\n      it_behaves_like :config_printing_a_section, :master\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/epp_face_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\nrequire 'puppet/face'\n\ndescribe Puppet::Face[:epp, :current] do\n  include PuppetSpec::Files\n\n  let(:eppface) { Puppet::Face[:epp, :current] }\n\n  context \"validate\" do\n    context \"from an interactive terminal\" do\n      before :each do\n        from_an_interactive_terminal\n      end\n\n      it \"validates the template referenced as an absolute file\" do\n        template_name = 'template1.epp'\n        dir = dir_containing('templates', { template_name => \"<%= |$a $b |%>\" })\n        template = File.join(dir, template_name)\n        expect { eppface.validate(template) }.to raise_exception(Puppet::Error, /Errors while validating epp/)\n      end\n\n      it \"runs error free when there are no validation errors from an absolute file\" do\n        template_name = 'template1.epp'\n        dir = dir_containing('templates', { template_name => \"just text\" })\n        template = File.join(dir, template_name)\n        expect { eppface.validate(template) }.to_not raise_exception()\n      end\n\n      it \"reports missing files\" do\n        expect do\n          eppface.validate(\"missing.epp\")\n        end.to raise_error(Puppet::Error, /One or more file\\(s\\) specified did not exist.*missing\\.epp/m)\n      end\n\n      context \"in an environment with templates\" do\n        let(:dir) do\n          dir_containing('environments', { 'production' => { 'modules' => {\n            'm1' => { 'templates' => {\n              'greetings.epp' => \"<% |$subject = world| %>hello <%= $subject -%>\",\n              'broken.epp'    => \"<% | $a $b | %> I am broken\",\n              'broken2.epp'   => \"<% | $a $b | %> I am broken too\"\n            }},\n            'm2' => { 'templates' => {\n              'goodbye.epp'   => \"<% | $subject = world |%>goodbye <%= $subject -%>\",\n              'broken3.epp'   => \"<% | $a $b | %> I am broken too\"\n            }}\n          }}})\n\n        end\n\n        around(:each) do |example|\n          Puppet.settings.initialize_global_settings\n          loader = Puppet::Environments::Directories.new(dir, [])\n          Puppet.override(:environments => loader) do\n            example.run\n          end\n        end\n\n        it \"parses supplied template files in different modules of a directory environment\" do\n          expect(eppface.validate('m1/greetings.epp')).to be_nil\n          expect(eppface.validate('m2/goodbye.epp')).to be_nil\n        end\n\n        it \"finds errors in supplied template file in the context of a directory environment\" do\n          expect { eppface.validate('m1/broken.epp') }.to raise_exception(Puppet::Error, /Errors while validating epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'/)\n        end\n\n        it \"stops on first error by default\" do\n          expect { eppface.validate('m1/broken.epp', 'm1/broken2.epp') }.to raise_exception(Puppet::Error, /Errors while validating epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'.*broken\\.epp/)\n          expect(@logs.join).to_not match(/Syntax error at 'b'.*broken2\\.epp/)\n        end\n\n        it \"continues after error when --continue_on_error is given\" do\n          expect { eppface.validate('m1/broken.epp', 'm1/broken2.epp', :continue_on_error => true) }.to raise_exception(Puppet::Error, /Errors while validating epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'.*broken\\.epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'.*broken2\\.epp/)\n        end\n\n        it \"validates all templates in the environment\" do\n          pending \"NOT IMPLEMENTED YET\"\n          expect { eppface.validate(:continue_on_error => true) }.to raise_exception(Puppet::Error, /Errors while validating epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'.*broken\\.epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'.*broken2\\.epp/)\n          expect(@logs.join).to match(/Syntax error at 'b'.*broken3\\.epp/)\n        end\n      end\n    end\n\n    it \"validates the contents of STDIN when no files given and STDIN is not a tty\" do\n      from_a_piped_input_of(\"<% | $a $oh_no | %> I am broken\")\n      expect { eppface.validate() }.to raise_exception(Puppet::Error, /Errors while validating epp/)\n      expect(@logs.join).to match(/Syntax error at 'oh_no'/)\n    end\n\n    it \"validates error free contents of STDIN when no files given and STDIN is not a tty\" do\n      from_a_piped_input_of(\"look, just text\")\n      expect(eppface.validate()).to be_nil\n    end\n  end\n\n  context \"dump\" do\n    it \"prints the AST of a template given with the -e option\" do\n      expect(eppface.dump({ :e => 'hello world' })).to eq(\"(lambda (epp (block\\n  (render-s 'hello world')\\n)))\\n\")\n    end\n\n    it \"prints the AST of a template given as an absolute file\" do\n      template_name = 'template1.epp'\n      dir = dir_containing('templates', { template_name => \"hello world\" })\n      template = File.join(dir, template_name)\n      expect(eppface.dump(template)).to eq(\"(lambda (epp (block\\n  (render-s 'hello world')\\n)))\\n\")\n    end\n\n    it \"adds a header between dumps by default\" do\n      template_name1 = 'template1.epp'\n      template_name2 = 'template2.epp'\n      dir = dir_containing('templates', { template_name1 => \"hello world\", template_name2 => \"hello again\"} )\n      template1 = File.join(dir, template_name1)\n      template2 = File.join(dir, template_name2)\n\n      # Do not move the text block, the left margin and indentation matters\n      expect(eppface.dump(template1, template2)).to eq( <<-\"EOT\" )\n--- #{template1}\n(lambda (epp (block\n  (render-s 'hello world')\n)))\n--- #{template2}\n(lambda (epp (block\n  (render-s 'hello again')\n)))\n      EOT\n    end\n\n    it \"dumps non validated content when given --no-validate\" do\n      template_name = 'template1.epp'\n      dir = dir_containing('templates', { template_name => \"<% 1 2 3 %>\" })\n      template = File.join(dir, template_name)\n      expect(eppface.dump(template, :validate => false)).to eq(\"(lambda (epp (block\\n  1\\n  2\\n  3\\n)))\\n\")\n    end\n\n    it \"validated content when given --validate\" do\n      template_name = 'template1.epp'\n      dir = dir_containing('templates', { template_name => \"<% 1 2 3 %>\" })\n      template = File.join(dir, template_name)\n      expect(eppface.dump(template, :validate => true)).to eq(\"\")\n      expect(@logs.join).to match(/This Literal Integer has no effect.*\\(file: .*\\/template1\\.epp, line: 1, column: 4\\)/)\n    end\n\n    it \"validated content by default\" do\n      template_name = 'template1.epp'\n      dir = dir_containing('templates', { template_name => \"<% 1 2 3 %>\" })\n      template = File.join(dir, template_name)\n      expect(eppface.dump(template)).to eq(\"\")\n      expect(@logs.join).to match(/This Literal Integer has no effect.*\\(file: .*\\/template1\\.epp, line: 1, column: 4\\)/)\n    end\n\n    it \"informs the user of files that don't exist\" do\n      expected_message = /One or more file\\(s\\) specified did not exist:\\n\\s*does_not_exist_here\\.epp/m\n      expect { eppface.dump('does_not_exist_here.epp') }.to raise_exception(Puppet::Error, expected_message)\n    end\n\n    it \"dumps the AST of STDIN when no files given and STDIN is not a tty\" do\n      from_a_piped_input_of(\"hello world\")\n      expect(eppface.dump()).to eq(\"(lambda (epp (block\\n  (render-s 'hello world')\\n)))\\n\")\n    end\n\n    it \"logs an error if the input cannot be parsed even if validation is off\" do\n      from_a_piped_input_of(\"<% |$a  $b| %> oh no\")\n      expect(eppface.dump(:validate => false)).to eq(\"\")\n      expect(@logs[0].message).to match(/Syntax error at 'b'/)\n      expect(@logs[0].level).to eq(:err)\n    end\n\n    context \"using 'pn' format\" do\n      it \"prints the AST of the given expression in PN format\" do\n        expect(eppface.dump({ :format => 'pn', :e => 'hello world' })).to eq(\n          '(lambda {:body [(epp (render-s \"hello world\"))]})')\n      end\n\n      it \"pretty prints the AST of the given expression in PN format when --pretty is given\" do\n        expect(eppface.dump({ :pretty => true, :format => 'pn', :e => 'hello world' })).to eq(<<-RESULT.unindent[0..-2])\n        (lambda\n          {\n            :body [\n              (epp\n                (render-s\n                  \"hello world\"))]})\n        RESULT\n      end\n    end\n\n    context \"using 'json' format\" do\n      it \"prints the AST of the given expression in JSON based on the PN format\" do\n        expect(eppface.dump({ :format => 'json', :e => 'hello world' })).to eq(\n          '{\"^\":[\"lambda\",{\"#\":[\"body\",[{\"^\":[\"epp\",{\"^\":[\"render-s\",\"hello world\"]}]}]]}]}')\n      end\n\n      it \"pretty prints the AST of the given expression in JSON based on the PN format when --pretty is given\" do\n        expect(eppface.dump({ :pretty => true, :format => 'json', :e => 'hello world' })).to eq(<<-RESULT.unindent[0..-2])\n        {\n          \"^\": [\n            \"lambda\",\n            {\n              \"#\": [\n                \"body\",\n                [\n                  {\n                    \"^\": [\n                      \"epp\",\n                      {\n                        \"^\": [\n                          \"render-s\",\n                          \"hello world\"\n                        ]\n                      }\n                    ]\n                  }\n                ]\n              ]\n            }\n          ]\n        }\n        RESULT\n      end\n    end\n  end\n\n  context \"render\" do\n    it \"renders input from stdin\" do\n      from_a_piped_input_of(\"hello world\")\n      expect(eppface.render()).to eq(\"hello world\")\n    end\n\n    it \"renders input from command line\" do\n      expect(eppface.render(:e => 'hello world')).to eq(\"hello world\")\n    end\n\n    it \"renders input from an absolute file\" do\n      template_name = 'template1.epp'\n      dir = dir_containing('templates', { template_name => \"absolute world\" })\n      template = File.join(dir, template_name)\n      expect(eppface.render(template)).to eq(\"absolute world\")\n    end\n\n    it \"renders expressions\" do\n      expect(eppface.render(:e => '<% $x = \"mr X\"%>hello <%= $x %>')).to eq(\"hello mr X\")\n    end\n\n    it \"adds values given in a puppet hash given on command line with --values\" do\n      expect(eppface.render(:e => 'hello <%= $x %>', :values => '{x => \"mr X\"}')).to eq(\"hello mr X\")\n    end\n\n    it \"adds fully qualified values given in a puppet hash given on command line with --values\" do\n      expect(eppface.render(:e => 'hello <%= $mr::x %>', :values => '{mr::x => \"mr X\"}')).to eq(\"hello mr X\")\n    end\n\n    it \"adds fully qualified values with leading :: given in a puppet hash given on command line with --values\" do\n      expect(eppface.render(:e => 'hello <%= $::mr %>', :values => '{\"::mr\" => \"mr X\"}')).to eq(\"hello mr X\")\n    end\n\n    it \"adds values given in a puppet hash produced by a .pp file given with --values_file\" do\n      file_name = 'values.pp'\n      dir = dir_containing('values', { file_name => '{x => \"mr X\"}' })\n      values_file = File.join(dir, file_name)\n      expect(eppface.render(:e => 'hello <%= $x %>', :values_file => values_file)).to eq(\"hello mr X\")\n    end\n\n    it \"adds values given in a yaml hash given with --values_file\" do\n      file_name = 'values.yaml'\n      dir = dir_containing('values', { file_name => \"---\\n x: 'mr X'\" })\n      values_file = File.join(dir, file_name)\n      expect(eppface.render(:e => 'hello <%= $x %>', :values_file => values_file)).to eq(\"hello mr X\")\n    end\n\n    it \"merges values from values file and command line with command line having higher precedence\" do\n      file_name = 'values.yaml'\n      dir = dir_containing('values', { file_name => \"---\\n x: 'mr X'\\n word: 'goodbye'\" })\n      values_file = File.join(dir, file_name)\n      expect(eppface.render(:e => '<%= $word %> <%= $x %>',\n        :values_file => values_file,\n        :values => '{x => \"mr Y\"}')\n        ).to eq(\"goodbye mr Y\")\n    end\n\n    it \"sets $facts\" do\n      expect(eppface.render({ :e => 'facts is hash: <%= $facts =~ Hash %>' })).to eql(\"facts is hash: true\")\n    end\n\n    it \"sets $trusted\" do\n      expect(eppface.render({ :e => 'trusted is hash: <%= $trusted =~ Hash %>' })).to eql(\"trusted is hash: true\")\n    end\n\n    it 'initializes the 4x loader' do\n      expect(eppface.render({ :e => <<-EPP.unindent })).to eql(\"\\nString\\n\\nInteger\\n\\nBoolean\\n\")\n        <% $data = [type('a',generalized), type(2,generalized), type(true,generalized)] -%>\n        <% $data.each |$value| { %>\n        <%= $value %>\n        <% } -%>\n      EPP\n    end\n\n    it \"facts can be added to\" do\n      expect(eppface.render({\n        :facts => {'the_crux' => 'biscuit'},\n        :e     => '<%= $facts[the_crux] %>', \n      })).to eql(\"biscuit\")\n    end\n\n    it \"facts can be overridden\" do\n      expect(eppface.render({\n        :facts => {'os' => {'name' => 'Merwin'} },\n        :e     => '<%= $facts[os][name] %>',\n      })).to eql(\"Merwin\")\n    end\n\n    context \"in an environment with templates\" do\n      let(:dir) do\n        dir_containing('environments', { 'production' => { 'modules' => {\n          'm1' => { 'templates' => {\n            'greetings.epp' => \"<% |$subject = world| %>hello <%= $subject -%>\",\n            'factshash.epp' => \"fact = <%= $facts[the_fact] -%>\",\n            'fact.epp'      => \"fact = <%= $the_fact -%>\",\n          }},\n          'm2' => { 'templates' => {\n            'goodbye.epp'   => \"<% | $subject = world |%>goodbye <%= $subject -%>\",\n          }}\n        },\n          'extra' => {\n            'facts.yaml' => \"---\\n the_fact: 42\"\n          }\n        }})\n\n      end\n\n      around(:each) do |example|\n        Puppet.settings.initialize_global_settings\n        loader = Puppet::Environments::Directories.new(dir, [])\n        Puppet.override(:environments => loader) do\n          example.run\n        end\n      end\n\n      it \"renders supplied template files in different modules of a directory environment\" do\n        expect(eppface.render('m1/greetings.epp')).to eq(\"hello world\")\n        expect(eppface.render('m2/goodbye.epp')).to eq(\"goodbye world\")\n      end\n\n      it \"makes facts available in $facts\" do\n        facts_file = File.join(dir, 'production', 'extra', 'facts.yaml')\n        expect(eppface.render('m1/factshash.epp', :facts => facts_file)).to eq(\"fact = 42\")\n      end\n\n      it \"makes facts available individually\" do\n        facts_file = File.join(dir, 'production', 'extra', 'facts.yaml')\n        expect(eppface.render('m1/fact.epp', :facts => facts_file)).to eq(\"fact = 42\")\n      end\n\n      it \"renders multiple files separated by headers by default\" do\n        # chomp the last newline, it is put there by heredoc\n        expect(eppface.render('m1/greetings.epp', 'm2/goodbye.epp')).to eq(<<-EOT.chomp)\n--- m1/greetings.epp\nhello world\n--- m2/goodbye.epp\ngoodbye world\n        EOT\n      end\n\n      it \"outputs multiple files verbatim when --no-headers is given\" do\n        expect(eppface.render('m1/greetings.epp', 'm2/goodbye.epp', :header => false)).to eq(\"hello worldgoodbye world\")\n      end\n    end\n  end\n\n  def from_an_interactive_terminal\n    allow(STDIN).to receive(:tty?).and_return(true)\n  end\n\n  def from_a_piped_input_of(contents)\n    allow(STDIN).to receive(:tty?).and_return(false)\n    allow(STDIN).to receive(:read).and_return(contents)\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/facts_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/indirector/facts/facter'\nrequire 'puppet/indirector/facts/rest'\n\ndescribe Puppet::Face[:facts, '0.0.1'] do\n  describe \"#find\" do\n    it { is_expected.to be_action :find }\n  end\n\n  describe '#upload' do\n    let(:model) { Puppet::Node::Facts }\n    let(:test_data) { model.new('puppet.node.test', {test_fact: 'test value'}) }\n    let(:facter_terminus) { model.indirection.terminus(:facter) }\n\n    before(:each) do\n      Puppet[:facts_terminus] = :memory\n      Puppet::Node::Facts.indirection.save(test_data)\n      allow(Puppet::Node::Facts.indirection).to receive(:terminus_class=).with(:facter)\n\n      Puppet.settings.parse_config(<<-CONF)\n[main]\nserver=puppet.server.invalid\ncertname=puppet.node.invalid\n[agent]\nserver=puppet.server.test\nnode_name_value=puppet.node.test\nCONF\n\n      # Faces start in :user run mode\n      Puppet.settings.preferred_run_mode = :user\n    end\n\n    it \"uploads facts as application/json\" do\n      stub_request(:put, 'https://puppet.server.test:8140/puppet/v3/facts/puppet.node.test?environment=*root*')\n        .with(\n          headers: { 'Content-Type' => 'application/json' },\n          body: hash_including(\n            {\n              \"name\" => \"puppet.node.test\",\n              \"values\" => {\n                \"test_fact\" => \"test value\"\n              }\n            }\n          )\n        )\n\n      subject.upload\n    end\n\n    it \"passes the current environment\" do\n      stub_request(:put, 'https://puppet.server.test:8140/puppet/v3/facts/puppet.node.test?environment=qa')\n\n      Puppet.override(:current_environment => Puppet::Node::Environment.remote('qa')) do\n        subject.upload\n      end\n    end\n\n    it \"uses settings from the agent section of puppet.conf to resolve the node name\" do\n      stub_request(:put, /puppet.node.test/)\n\n      subject.upload\n    end\n\n    it \"logs the name of the server that received the upload\" do\n      stub_request(:put, 'https://puppet.server.test:8140/puppet/v3/facts/puppet.node.test?environment=*root*')\n\n      subject.upload\n\n      expect(@logs).to be_any {|log| log.level == :notice &&\n                               log.message =~ /Uploading facts for '.*' to 'puppet\\.server\\.test'/}\n    end\n  end\n\n  describe \"#show\" do\n    it { is_expected.to be_action :show }\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/generate_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\nrequire 'puppet/face'\n\ndescribe Puppet::Face[:generate, :current] do\n  include PuppetSpec::Files\n\n  let(:genface) { Puppet::Face[:generate, :current] }\n\n  # * Format is 'pcore' by default\n  # * Format is accepted as 'pcore'\n  # * Any format expect 'pcore' is an error\n  # * Produces output to '<envroot>/.resource_types'\n  # * Produces all types found on the module path (that are not in puppet core)\n  # * Output files match input\n  # * Removes files for which there is no input\n  # * Updates a pcore file if it is out of date\n  # * The --force flag overwrite the output even if it is up to date\n  # * Environment is set with --environment (setting) (not tested explicitly)\n  # Writes output for:\n  #   - isomorphic\n  #   - parameters\n  #   - properties\n  #   - title patterns\n  #   - type information is written when the type is X, Y, or Z\n  #\n  # Additional features\n  #   - blacklist? whitelist? types to exclude/include\n  #   - generate one resource type (somewhere on modulepath)\n  #   - output to directory of choice\n  #   - clean, clean the output directory (similar to force)\n  #\n\n  [:types].each do |action|\n    it { is_expected.to be_action(action) }\n    it { is_expected.to respond_to(action) }\n  end\n\n  context \"when used from an interactive terminal\" do\n    before :each do\n      from_an_interactive_terminal\n    end\n\n    context \"in an environment with two modules containing resource types\" do\n      let(:dir) do\n        dir_containing('environments', { 'testing_generate' => {\n          'environment.conf' => \"modulepath = modules\",\n          'manifests' => { 'site.pp' => \"\" },\n          'modules' => {\n            'm1' => {\n              'lib' => { 'puppet' => { 'type' => {\n                'test1.rb' => <<-EOF\n                module Puppet\n                Type.newtype(:test1) do\n                  @doc = \"Docs for resource\"\n                  newproperty(:message) do\n                    desc \"Docs for 'message' property\"\n                  end\n                  newparam(:name) do\n                    desc \"Docs for 'name' parameter\"\n                    isnamevar\n                  end\n                end; end\n                EOF\n               } }\n            },\n          },\n          'm2' => { \n            'lib' => { 'puppet' => { 'type' => {\n              'test2.rb' => <<-EOF\n              module Puppet\n              Type.newtype(:test2) do\n                @doc = \"Docs for resource\"\n                newproperty(:message) do\n                  desc \"Docs for 'message' property\"\n                end\n                newparam(:name) do\n                  desc \"Docs for 'name' parameter\"\n                  isnamevar\n                end\n              end;end\n              EOF\n             } } },\n          }\n        }}})\n      end\n\n      let(:modulepath) do\n        File.join(dir, 'testing_generate', 'modules')\n      end\n\n      let(:m1) do\n        File.join(modulepath, 'm1')\n      end\n\n      let(:m2) do\n        File.join(modulepath, 'm2')\n      end\n\n      let(:outputdir) do\n        File.join(dir, 'testing_generate', '.resource_types')\n      end\n\n      around(:each) do |example|\n        Puppet.settings.initialize_global_settings\n        Puppet[:manifest] = ''\n        loader = Puppet::Environments::Directories.new(dir, [])\n        Puppet.override(:environments => loader) do\n          Puppet.override(:current_environment => loader.get('testing_generate')) do\n            example.run\n          end\n        end\n      end\n\n      it 'error if format is given as something other than pcore' do\n        expect {\n          genface.types(:format => 'json')\n        }.to raise_exception(ArgumentError, /'json' is not a supported format for type generation/)\n      end\n\n      it 'accepts --format pcore as a format' do\n        expect {\n          genface.types(:format => 'pcore')\n        }.not_to raise_error\n      end\n\n      it 'sets pcore as the default format' do\n        expect(Puppet::Generate::Type).to receive(:find_inputs).with(:pcore).and_return([])\n        genface.types()\n      end\n\n      it 'finds all files to generate types for' do\n        # using expects and returning what the side effect should have been\n        # (There is no way to call the original when mocking expected parameters).\n        input1 = Puppet::Generate::Type::Input.new(m1, File.join(m1, 'lib', 'puppet', 'type', 'test1.rb'), :pcore)\n        input2 = Puppet::Generate::Type::Input.new(m1, File.join(m2, 'lib', 'puppet', 'type', 'test2.rb'), :pcore)\n        expect(Puppet::Generate::Type::Input).to receive(:new).with(m1, File.join(m1, 'lib', 'puppet', 'type', 'test1.rb'), :pcore).and_return(input1)\n        expect(Puppet::Generate::Type::Input).to receive(:new).with(m2, File.join(m2, 'lib', 'puppet', 'type', 'test2.rb'), :pcore).and_return(input2)\n        genface.types\n      end\n\n      it 'creates output directory <env>/.resource_types/ if it does not exist' do\n        expect(Puppet::FileSystem.exist?(outputdir)).to be(false)\n        genface.types\n        expect(Puppet::FileSystem.dir_exist?(outputdir)).to be(true)\n      end\n\n      it 'creates output with matching names for each input' do\n        expect(Puppet::FileSystem.exist?(outputdir)).to be(false)\n        genface.types\n        children = Puppet::FileSystem.children(outputdir).map {|p| Puppet::FileSystem.basename_string(p) }\n        expect(children.sort).to eql(['test1.pp', 'test2.pp'])\n      end\n\n      it 'tolerates that <env>/.resource_types/ directory exists' do\n        Puppet::FileSystem.mkpath(outputdir)\n        expect(Puppet::FileSystem.exist?(outputdir)).to be(true)\n        genface.types\n        expect(Puppet::FileSystem.dir_exist?(outputdir)).to be(true)\n      end\n\n      it 'errors if <env>/.resource_types exists and is not a directory' do\n        expect(Puppet::FileSystem.exist?(outputdir)).to be(false) # assert it is not already there\n        Puppet::FileSystem.touch(outputdir)\n        expect(Puppet::FileSystem.exist?(outputdir)).to be(true)\n        expect(Puppet::FileSystem.directory?(outputdir)).to be(false)\n        expect {\n          genface.types\n        }.to raise_error(ArgumentError, /The output directory '#{outputdir}' exists and is not a directory/)\n      end\n\n      it 'does not overwrite if files exists and are up to date' do\n        # create them (first run)\n        genface.types\n        stats_before = [Puppet::FileSystem.stat(File.join(outputdir, 'test1.pp')), Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))]\n        # generate again\n        genface.types\n        stats_after = [Puppet::FileSystem.stat(File.join(outputdir, 'test1.pp')), Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))]\n        expect(stats_before <=> stats_after).to be(0)\n      end\n\n      it 'overwrites if files exists that are not up to date while keeping up to date files' do\n        # create them (first run)\n        genface.types\n        stats_before = [Puppet::FileSystem.stat(File.join(outputdir, 'test1.pp')), Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))]\n        # fake change in input test1 - sorry about the sleep (which there was a better way to change the modtime\n        sleep(1)\n        Puppet::FileSystem.touch(File.join(m1, 'lib', 'puppet', 'type', 'test1.rb'))\n        # generate again\n        genface.types\n        # assert that test1 was overwritten (later) but not test2 (same time)\n        stats_after = [Puppet::FileSystem.stat(File.join(outputdir, 'test1.pp')), Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))]\n        expect(stats_before[1] <=> stats_after[1]).to be(0)\n        expect(stats_before[0] <=> stats_after[0]).to be(-1)\n      end\n\n      it 'overwrites all files when called with --force' do\n        # create them (first run)\n        genface.types\n        stats_before = [Puppet::FileSystem.stat(File.join(outputdir, 'test1.pp')), Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))]\n        # generate again\n        sleep(1) # sorry, if there is no delay the stats will be the same\n        genface.types(:force => true)\n        stats_after = [Puppet::FileSystem.stat(File.join(outputdir, 'test1.pp')), Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))]\n        expect(stats_before <=> stats_after).to be(-1)\n      end\n\n      it 'removes previously generated files from output when there is no input for it' do\n        # create them (first run)\n        genface.types\n        stat_before = Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))\n        # remove input\n        Puppet::FileSystem.unlink(File.join(m1, 'lib', 'puppet', 'type', 'test1.rb'))\n        # generate again\n        genface.types\n        # assert that test1 was deleted but not test2 (same time)\n        expect(Puppet::FileSystem.exist?(File.join(outputdir, 'test1.pp'))).to be(false)\n        stats_after = Puppet::FileSystem.stat(File.join(outputdir, 'test2.pp'))\n        expect(stat_before <=> stats_after).to be(0)\n      end\n\n    end\n\n    context \"in an environment with a faulty type\" do\n      let(:dir) do\n        dir_containing('environments', { 'testing_generate2' => {\n          'environment.conf' => \"modulepath = modules\",\n          'manifests' => { 'site.pp' => \"\" },\n          'modules' => {\n            'm3' => {\n              'lib' => { 'puppet' => { 'type' => {\n                'test3.rb' => <<-EOF\n                module Puppet\n                Type.newtype(:test3) do\n                  @doc = \"Docs for resource\"\n                  def self.title_patterns\n                    identity = lambda {|x| x}\n                    [\n                      [\n                      /^(.*)_(.*)$/,\n                        [\n                          [:name, identity ]\n                        ]\n                      ]\n                    ]\n                  end\n                  newproperty(:message) do\n                    desc \"Docs for 'message' property\"\n                  end\n                  newparam(:name) do\n                    desc \"Docs for 'name' parameter\"\n                    isnamevar\n                  end\n                end; end\n                EOF\n               } }\n            }\n          }\n        }}})\n      end\n\n      let(:modulepath) do\n        File.join(dir, 'testing_generate2', 'modules')\n      end\n\n      let(:m3) do\n        File.join(modulepath, 'm3')\n      end\n\n      around(:each) do |example|\n        Puppet.settings.initialize_global_settings\n        Puppet[:manifest] = ''\n        loader = Puppet::Environments::Directories.new(dir, [])\n        Puppet.override(:environments => loader) do\n          Puppet.override(:current_environment => loader.get('testing_generate2')) do\n            example.run\n          end\n        end\n      end\n\n      it 'fails when using procs for title patterns' do\n        expect {\n          genface.types(:format => 'pcore')\n        }.to exit_with(1)\n      end\n    end\n  end\n\n  def from_an_interactive_terminal\n    allow(STDIN).to receive(:tty?).and_return(true)\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/help_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\n\ndescribe Puppet::Face[:help, '0.0.1'] do\n  it 'has a help action' do\n    expect(subject).to be_action :help\n  end\n\n  it 'has a default action of help' do\n    expect(subject.get_action('help')).to be_default\n  end\n\n  it 'accepts a call with no arguments' do\n    expect {\n      subject.help()\n    }.to_not raise_error\n  end\n\n  it 'accepts a face name' do\n    expect { subject.help(:help) }.to_not raise_error\n  end\n\n  it 'accepts a face and action name' do\n    expect { subject.help(:help, :help) }.to_not raise_error\n  end\n\n  it 'fails if more than a face and action are given' do\n    expect { subject.help(:help, :help, :for_the_love_of_god) }.to raise_error ArgumentError\n  end\n\n  it \"treats :current and 'current' identically\" do\n    expect(subject.help(:help, :version => :current)).to eq(\n      subject.help(:help, :version => 'current')\n    )\n  end\n\n  it 'raises an error when the face is unavailable' do\n    expect {\n      subject.help(:huzzah, :bar, :version => '17.0.0')\n    }.to raise_error(ArgumentError, /Could not find version 17\\.0\\.0/)\n  end\n\n  it 'finds a face by version' do\n    face = Puppet::Face[:huzzah, :current]\n    expect(subject.help(:huzzah, :version => face.version)).\n      to eq(subject.help(:huzzah, :version => :current))\n  end\n\n  context 'rendering has an error' do\n    it 'raises an ArgumentError if the face raises a StandardError' do\n      face = Puppet::Face[:module, :current]\n      allow(face).to receive(:short_description).and_raise(StandardError, 'whoops')\n\n      expect {\n        subject.help(:module)\n      }.to raise_error(ArgumentError, /Detail: \"whoops\"/)\n    end\n\n    it 'raises an ArgumentError if the face raises a LoadError' do\n      face = Puppet::Face[:module, :current]\n      allow(face).to receive(:short_description).and_raise(LoadError, 'cannot load such file -- yard')\n\n      expect {\n        subject.help(:module)\n      }.to raise_error(ArgumentError, /Detail: \"cannot load such file -- yard\"/)\n    end\n\n    context 'with face actions' do\n      it 'returns an error if we can not get an action for the module' do\n        face = Puppet::Face[:module, :current]\n        allow(face).to receive(:get_action).and_return(nil)\n\n        expect {subject.help('module', 'list')}.to raise_error(ArgumentError, /Unable to load action list from Puppet::Face/)\n      end\n    end\n  end\n\n  context 'when listing subcommands' do\n    subject { Puppet::Face[:help, :current].help }\n\n    RSpec::Matchers.define :have_a_summary do\n      match do |instance|\n        instance.summary.is_a?(String)\n      end\n    end\n\n    # Check a precondition for the next block; if this fails you have\n    # something odd in your set of face, and we skip testing things that\n    # matter. --daniel 2011-04-10\n    it 'has at least one face with a summary' do\n      expect(Puppet::Face.faces).to be_any do |name|\n        Puppet::Face[name, :current].summary\n      end\n    end\n\n    it 'lists all faces which are runnable from the command line' do\n      help_face = Puppet::Face[:help, :current]\n      # The main purpose of the help face is to provide documentation for\n      #  command line users.  It shouldn't show documentation for faces\n      #  that can't be run from the command line, so, rather than iterating\n      #  over all available faces, we need to iterate over the subcommands\n      #  that are available from the command line.\n      Puppet::Application.available_application_names.each do |name|\n        next unless help_face.is_face_app?(name)\n        next if help_face.exclude_from_docs?(name)\n        face = Puppet::Face[name, :current]\n        summary = face.summary\n\n        expect(subject).to match(%r{ #{name} })\n        summary and expect(subject).to match(%r{ #{name} +#{summary}})\n      end\n    end\n\n    context 'face summaries' do\n      it 'can generate face summaries' do\n        faces = Puppet::Face.faces\n        expect(faces.length).to be > 0\n        faces.each do |name|\n          expect(Puppet::Face[name, :current]).to have_a_summary\n        end\n      end\n    end\n\n    it 'lists all legacy applications' do\n      Puppet::Face[:help, :current].legacy_applications.each do |appname|\n        expect(subject).to match(%r{ #{appname} })\n\n        summary = Puppet::Face[:help, :current].horribly_extract_summary_from(appname)\n        summary_regex = Regexp.escape(summary)\n        summary and expect(subject).to match(%r{ #{summary_regex}$})\n      end\n    end\n  end\n\n  context 'deprecated faces' do\n    it 'prints a deprecation warning for deprecated faces' do\n      allow(Puppet::Face[:module, :current]).to receive(:deprecated?).and_return(true)\n      expect(Puppet::Face[:help, :current].help(:module)).to match(/Warning: 'puppet module' is deprecated/)\n    end\n  end\n\n  context '#all_application_summaries' do\n    it 'appends a deprecation warning for deprecated faces' do\n      # Stub the module face as deprecated\n      expect(Puppet::Face[:module, :current]).to receive(:deprecated?).and_return(true)\n      Puppet::Face[:help, :current].all_application_summaries.each do |appname,summary|\n        expect(summary).to match(/Deprecated/) if appname == 'module'\n      end\n    end\n  end\n\n  context '#legacy_applications' do\n    subject { Puppet::Face[:help, :current].legacy_applications }\n\n    # If we don't, these tests are ... less than useful, because they assume\n    # it.  When this breaks you should consider ditching the entire feature\n    # and tests, but if not work out how to fake one. --daniel 2011-04-11\n    it { expect(subject.count).to be > 1 }\n\n    # Meh.  This is nasty, but we can't control the other list; the specific\n    # bug that caused these to be listed is annoyingly subtle and has a nasty\n    # fix, so better to have a \"fail if you do something daft\" trigger in\n    # place here, I think. --daniel 2011-04-11\n    %w{face_base indirection_base}.each do |name|\n      it { is_expected.not_to include name }\n    end\n  end\n\n  context 'help for legacy applications' do\n    subject { Puppet::Face[:help, :current] }\n    let :appname do subject.legacy_applications.first end\n\n    # This test is purposely generic, so that as we eliminate legacy commands\n    # we don't get into a loop where we either test a face-based replacement\n    # and fail to notice breakage, or where we have to constantly rewrite this\n    # test and all. --daniel 2011-04-11\n    it 'returns the legacy help when given the subcommand' do\n      help = subject.help(appname)\n      expect(help).to match(/puppet-#{appname}/)\n      %w{SYNOPSIS USAGE DESCRIPTION OPTIONS COPYRIGHT}.each do |heading|\n        expect(help).to match(/^#{heading}$/)\n      end\n    end\n\n    it 'fails when asked for an action on a legacy command' do\n      expect { subject.help(appname, :whatever) }.\n        to raise_error(ArgumentError, /The legacy subcommand '#{appname}' does not support supplying an action/)\n    end\n\n    context 'rendering has an error' do\n      it 'raises an ArgumentError if a legacy application raises a StandardError' do\n        allow_any_instance_of(Puppet::Application[appname].class).to receive(:help).and_raise(StandardError, 'whoops')\n\n        expect {\n          subject.help(appname)\n        }.to raise_error(ArgumentError, /Detail: \"whoops\"/)\n      end\n\n      it 'raises an ArgumentError if a legacy application raises a LoadError' do\n        allow_any_instance_of(Puppet::Application[appname].class).to receive(:help).and_raise(LoadError, 'cannot load such file -- yard')\n\n        expect {\n          subject.help(appname)\n        }.to raise_error(ArgumentError, /Detail: \"cannot load such file -- yard\"/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/module/install_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/module_tool'\n\ndescribe \"puppet module install\" do\n  include PuppetSpec::Files\n\n  describe \"action\" do\n    let(:name)        { double(:name) }\n    let(:target_dir)  { tmpdir('module install face action') }\n    let(:options)     { { :target_dir => target_dir } }\n\n    it 'should invoke the Installer app' do\n      expect(Puppet::ModuleTool).to receive(:set_option_defaults).with(options)\n      expect(Puppet::ModuleTool::Applications::Installer).to receive(:run) do |mod, target, opts|\n        expect(mod).to eql(name)\n        expect(opts).to eql(options)\n        expect(target).to be_a(Puppet::ModuleTool::InstallDirectory)\n        expect(target.target).to eql(Pathname.new(target_dir))\n      end\n\n      Puppet::Face[:module, :current].install(name, options)\n    end\n  end\n\n  describe \"inline documentation\" do\n    subject { Puppet::Face.find_action(:module, :install) }\n\n    its(:summary)     { should =~ /install.*module/im }\n    its(:description) { should =~ /install.*module/im }\n    its(:returns)     { should =~ /pathname/i }\n    its(:examples)    { should_not be_empty }\n\n    %w{ license copyright summary description returns examples }.each do |doc|\n      context \"of the\" do\n        its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/module/list_spec.rb",
    "content": "# encoding: UTF-8\n\nrequire 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/module_tool'\nrequire 'puppet_spec/modules'\n\ndescribe \"puppet module list\" do\n  include PuppetSpec::Files\n\n  around do |example|\n    dir = tmpdir(\"deep_path\")\n\n    FileUtils.mkdir_p(@modpath1 = File.join(dir, \"modpath1\"))\n    FileUtils.mkdir_p(@modpath2 = File.join(dir, \"modpath2\"))\n    FileUtils.mkdir_p(@modpath3 = File.join(dir, \"modpath3\"))\n\n    env = Puppet::Node::Environment.create(:env, [@modpath1, @modpath2])\n    Puppet.override(:current_environment => env) do\n      example.run\n    end\n  end\n\n  it \"should return an empty list per dir in path if there are no modules\" do\n    expect(Puppet::Face[:module, :current].list[:modules_by_path]).to eq({\n      @modpath1 => [],\n      @modpath2 => []\n    })\n  end\n\n  it \"should include modules separated by the environment's modulepath\" do\n    foomod1 = PuppetSpec::Modules.create('foo', @modpath1)\n    barmod1 = PuppetSpec::Modules.create('bar', @modpath1)\n    foomod2 = PuppetSpec::Modules.create('foo', @modpath2)\n\n    usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2, @modpath3])\n\n    Puppet.override(:environments => Puppet::Environments::Static.new(usedenv)) do\n      expect(Puppet::Face[:module, :current].list(:environment => 'useme')[:modules_by_path]).to eq({\n        @modpath1 => [\n          Puppet::Module.new('bar', barmod1.path, usedenv),\n          Puppet::Module.new('foo', foomod1.path, usedenv)\n        ],\n        @modpath2 => [Puppet::Module.new('foo', foomod2.path, usedenv)],\n        @modpath3 => [],\n      })\n    end\n  end\n\n  it \"should use the specified environment\" do\n    foomod = PuppetSpec::Modules.create('foo', @modpath1)\n    barmod = PuppetSpec::Modules.create('bar', @modpath1)\n\n    usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2, @modpath3])\n\n    Puppet.override(:environments => Puppet::Environments::Static.new(usedenv)) do\n      expect(Puppet::Face[:module, :current].list(:environment => 'useme')[:modules_by_path]).to eq({\n        @modpath1 => [\n          Puppet::Module.new('bar', barmod.path, usedenv),\n          Puppet::Module.new('foo', foomod.path, usedenv)\n        ],\n        @modpath2 => [],\n        @modpath3 => [],\n      })\n    end\n  end\n\n  it \"should use the specified modulepath\" do\n    foomod = PuppetSpec::Modules.create('foo', @modpath1)\n    barmod = PuppetSpec::Modules.create('bar', @modpath2)\n\n    modules = Puppet::Face[:module, :current].list(:modulepath => \"#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}\")[:modules_by_path]\n\n    expect(modules[@modpath1].first.name).to eq('foo')\n    expect(modules[@modpath1].first.path).to eq(foomod.path)\n    expect(modules[@modpath1].first.environment.modulepath).to eq([@modpath1, @modpath2])\n\n    expect(modules[@modpath2].first.name).to eq('bar')\n    expect(modules[@modpath2].first.path).to eq(barmod.path)\n    expect(modules[@modpath2].first.environment.modulepath).to eq([@modpath1, @modpath2])\n  end\n\n  it \"prefers a given modulepath over the modulepath from the given environment\" do\n    foomod = PuppetSpec::Modules.create('foo', @modpath1)\n    barmod = PuppetSpec::Modules.create('bar', @modpath2)\n\n    modules = Puppet::Face[:module, :current].list(:environment => 'myenv', :modulepath => \"#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}\")[:modules_by_path]\n\n    expect(modules[@modpath1].first.name).to eq('foo')\n    expect(modules[@modpath1].first.path).to eq(foomod.path)\n    expect(modules[@modpath1].first.environment.modulepath).to eq([@modpath1, @modpath2])\n    expect(modules[@modpath1].first.environment.name).to_not eq(:myenv)\n\n    expect(modules[@modpath2].first.name).to eq('bar')\n    expect(modules[@modpath2].first.path).to eq(barmod.path)\n    expect(modules[@modpath2].first.environment.modulepath).to eq([@modpath1, @modpath2])\n    expect(modules[@modpath2].first.environment.name).to_not eq(:myenv)\n  end\n\n  describe \"inline documentation\" do\n    subject { Puppet::Face[:module, :current].get_action(:list) }\n\n    its(:summary)     { should =~ /list.*module/im }\n    its(:description) { should =~ /list.*module/im }\n    its(:returns)     { should =~ /hash of paths to module objects/i }\n    its(:examples)    { should_not be_empty }\n  end\n\n  describe \"when rendering to console\" do\n    let(:face) { Puppet::Face[:module, :current] }\n    let(:action) { face.get_action(:list) }\n\n    def console_output(options={})\n      result = face.list(options)\n      action.when_rendering(:console).call(result, options)\n    end\n\n    it \"should explicitly state when a modulepath is empty\" do\n      empty_modpath = tmpdir('empty')\n\n      expected = <<-HEREDOC.gsub('        ', '')\n        #{empty_modpath} (no modules installed)\n      HEREDOC\n\n      expect(console_output(:modulepath => empty_modpath)).to eq(expected)\n    end\n\n    it \"should print both modules with and without metadata\" do\n      modpath = tmpdir('modpath')\n\n      PuppetSpec::Modules.create('nometadata', modpath)\n      PuppetSpec::Modules.create('metadata', modpath, :metadata => {:author => 'metaman'})\n\n      env = Puppet::Node::Environment.create(:environ, [modpath])\n      Puppet.override(:current_environment => env) do\n        expected = <<-HEREDOC.gsub('          ', '')\n          #{modpath}\n          ├── metaman-metadata (\\e[0;36mv9.9.9\\e[0m)\n          └── nometadata (\\e[0;36m???\\e[0m)\n        HEREDOC\n\n        expect(console_output).to eq(expected)\n      end\n    end\n\n    it \"should print the modulepaths in the order they are in the modulepath setting\" do\n      path1 = tmpdir('b')\n      path2 = tmpdir('c')\n      path3 = tmpdir('a')\n\n      env = Puppet::Node::Environment.create(:environ, [path1, path2, path3])\n      Puppet.override(:current_environment => env) do\n        expected = <<-HEREDOC.gsub('          ', '')\n          #{path1} (no modules installed)\n          #{path2} (no modules installed)\n          #{path3} (no modules installed)\n        HEREDOC\n\n        expect(console_output).to eq(expected)\n      end\n    end\n\n    it \"should print dependencies as a tree\" do\n      PuppetSpec::Modules.create('dependable', @modpath1, :metadata => { :version => '0.0.5'})\n      PuppetSpec::Modules.create(\n        'other_mod',\n        @modpath1,\n        :metadata => {\n          :version => '1.0.0',\n          :dependencies => [{\n            \"version_requirement\" => \">= 0.0.5\",\n            \"name\"                => \"puppetlabs/dependable\"\n          }]\n        }\n      )\n\n      expected = <<-HEREDOC.gsub('        ', '')\n        #{@modpath1}\n        └─┬ puppetlabs-other_mod (\\e[0;36mv1.0.0\\e[0m)\n          └── puppetlabs-dependable (\\e[0;36mv0.0.5\\e[0m)\n        #{@modpath2} (no modules installed)\n      HEREDOC\n\n      expect(console_output(:tree => true)).to eq(expected)\n    end\n\n    it \"should print both modules with and without metadata as a tree\" do\n      PuppetSpec::Modules.create('nometadata', @modpath1)\n      PuppetSpec::Modules.create('metadata', @modpath1, :metadata => {:author => 'metaman'})\n\n      expected = <<-HEREDOC.gsub('        ', '')\n        #{@modpath1}\n        ├── metaman-metadata (\\e[0;36mv9.9.9\\e[0m)\n        └── nometadata (\\e[0;36m???\\e[0m)\n        #{@modpath2} (no modules installed)\n      HEREDOC\n\n      expect(console_output).to eq(expected)\n    end\n\n    it \"should warn about missing dependencies\" do\n      PuppetSpec::Modules.create('depender', @modpath1, :metadata => {\n        :version => '1.0.0',\n        :dependencies => [{\n          \"version_requirement\" => \">= 0.0.5\",\n          \"name\"                => \"puppetlabs/dependable\"\n        }]\n      })\n\n      expect(Puppet).to receive(:warning).with(match(/Missing dependency 'puppetlabs-dependable'/).and match(/'puppetlabs-depender' \\(v1\\.0\\.0\\) requires 'puppetlabs-dependable' \\(>= 0\\.0\\.5\\)/))\n\n      console_output(:tree => true)\n    end\n\n    it \"should warn about out of range dependencies\" do\n      PuppetSpec::Modules.create('dependable', @modpath1, :metadata => { :version => '0.0.1'})\n      PuppetSpec::Modules.create('depender', @modpath1, :metadata => {\n        :version => '1.0.0',\n        :dependencies => [{\n          \"version_requirement\" => \">= 0.0.5\",\n          \"name\"                => \"puppetlabs/dependable\"\n        }]\n      })\n\n      expect(Puppet).to receive(:warning).with(match(/Module 'puppetlabs-dependable' \\(v0\\.0\\.1\\) fails to meet some dependencies/).and match(/'puppetlabs-depender' \\(v1\\.0\\.0\\) requires 'puppetlabs-dependable' \\(>= 0\\.0\\.5\\)/))\n\n      console_output(:tree => true)\n    end\n  end\n\n  describe \"when rendering as json\" do\n    let(:face) { Puppet::Face[:module, :current] }\n    let(:action) { face.get_action(:list) }\n\n    it \"should warn about missing dependencies\" do\n      PuppetSpec::Modules.create('depender', @modpath1, :metadata => {\n        :version => '1.0.0',\n        :dependencies => [{\n          \"version_requirement\" => \">= 0.0.5\",\n          \"name\"                => \"puppetlabs/dependable\"\n        }]\n      })\n\n      result = face.list\n      expect(result.dig(:unmet_dependencies, :missing)).to include(\n        \"puppetlabs/dependable\" => {\n          errors: [\"'puppetlabs-depender' (v1.0.0) requires 'puppetlabs-dependable' (>= 0.0.5)\"],\n          parent: {\n            name: \"puppetlabs/depender\", :version=>\"v1.0.0\"\n          },\n          version: nil\n        }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/module/uninstall_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/module_tool'\n\ndescribe \"puppet module uninstall\" do\n  include PuppetSpec::Files\n\n  describe \"action\" do\n    let(:name)    { 'module-name' }\n    let(:options) { Hash.new }\n\n    it 'should invoke the Uninstaller app' do\n      expect(Puppet::ModuleTool).to receive(:set_option_defaults).with(options)\n      expect(Puppet::ModuleTool::Applications::Uninstaller).to receive(:run).with(name, options)\n\n      Puppet::Face[:module, :current].uninstall(name, options)\n    end\n\n    context 'slash-separated module name' do\n      let(:name) { 'module/name' }\n\n      it 'should invoke the Uninstaller app' do\n        expect(Puppet::ModuleTool).to receive(:set_option_defaults).with(options)\n        expect(Puppet::ModuleTool::Applications::Uninstaller).to receive(:run).with('module-name', options)\n\n        Puppet::Face[:module, :current].uninstall(name, options)\n      end\n    end\n  end\n\n  describe \"inline documentation\" do\n    subject { Puppet::Face.find_action(:module, :uninstall) }\n\n    its(:summary)     { should =~ /uninstall.*module/im }\n    its(:description) { should =~ /uninstall.*module/im }\n    its(:returns)     { should =~ /uninstalled modules/i }\n    its(:examples)    { should_not be_empty }\n\n    %w{ license copyright summary description returns examples }.each do |doc|\n      context \"of the\" do\n        its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/module/upgrade_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/module_tool'\n\ndescribe \"puppet module upgrade\" do\n  subject { Puppet::Face[:module, :current] }\n\n  let(:options) do\n    {}\n  end\n\n  describe \"inline documentation\" do\n    subject { Puppet::Face[:module, :current].get_action :upgrade }\n\n    its(:summary)     { should =~ /upgrade.*module/im }\n    its(:description) { should =~ /upgrade.*module/im }\n    its(:returns)     { should =~ /hash/i }\n    its(:examples)    { should_not be_empty }\n\n    %w{ license copyright summary description returns examples }.each do |doc|\n      context \"of the\" do\n        its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/node_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\n\ndescribe Puppet::Face[:node, '0.0.1'] do\n  describe '#cleanup' do\n    it \"should clean everything\" do\n      {\n        \"cert\"         => ['hostname'],\n        \"cached_facts\" => ['hostname'],\n        \"cached_node\"  => ['hostname'],\n        \"reports\"      => ['hostname'],\n      }.each { |k, v| expect(subject).to receive(\"clean_#{k}\".to_sym).with(*v) }\n      subject.cleanup('hostname')\n    end\n  end\n\n  describe 'when running #clean' do\n    it 'should invoke #cleanup' do\n      expect(subject).to receive(:cleanup).with('hostname')\n      subject.clean('hostname')\n    end\n  end\n\n  describe \"clean action\" do\n    before :each do\n      allow(subject).to receive(:cleanup)\n    end\n\n    it \"should have a clean action\" do\n      expect(subject).to be_action :clean\n    end\n\n    it \"should not accept a call with no arguments\" do\n      expect { subject.clean() }.to raise_error(RuntimeError, /At least one node should be passed/)\n    end\n\n    it \"should accept a node name\" do\n      expect { subject.clean('hostname') }.to_not raise_error\n    end\n\n    it \"should accept more than one node name\" do\n      expect do\n        subject.clean('hostname', 'hostname2', {})\n      end.to_not raise_error\n\n      expect do\n        subject.clean('hostname', 'hostname2', 'hostname3')\n      end.to_not raise_error\n    end\n\n    context \"clean action\" do\n      subject { Puppet::Face[:node, :current] }\n      before :each do\n        allow(Puppet::Util::Log).to receive(:newdestination)\n        allow(Puppet::Util::Log).to receive(:level=)\n      end\n\n      describe \"during setup\" do\n        it \"should set facts terminus and cache class to yaml\" do\n          expect(Puppet::Node::Facts.indirection).to receive(:terminus_class=).with(:yaml)\n          expect(Puppet::Node::Facts.indirection).to receive(:cache_class=).with(:yaml)\n\n          subject.clean('hostname')\n        end\n\n        it \"should run in server mode\" do\n          subject.clean('hostname')\n          expect(Puppet.run_mode).to be_server\n        end\n\n        it \"should set node cache as yaml\" do\n          expect(Puppet::Node.indirection).to receive(:terminus_class=).with(:yaml)\n          expect(Puppet::Node.indirection).to receive(:cache_class=).with(:yaml)\n\n          subject.clean('hostname')\n        end\n      end\n\n      describe \"when cleaning certificate\", :if => Puppet.features.puppetserver_ca? do\n        it \"should call the CA CLI gem's clean action\" do\n          expect_any_instance_of(Puppetserver::Ca::Action::Clean).\n            to receive(:clean_certs).\n            with(['hostname'], anything).\n            and_return(:success)\n\n          if Puppet[:cadir].start_with?(Puppet[:ssldir])\n            expect_any_instance_of(LoggerIO).\n              to receive(:warn).\n              with(/cadir is currently configured to be inside/)\n          end\n\n          expect(Puppet).not_to receive(:warning)\n          result = subject.clean_cert('hostname')\n          expect(result).to eq(0)\n        end\n\n        it \"should not call the CA CLI gem's clean action if the gem is missing\" do\n          expect(Puppet.features).to receive(:puppetserver_ca?).and_return(false)\n          expect_any_instance_of(Puppetserver::Ca::Action::Clean).not_to receive(:run)\n          subject.clean_cert(\"hostname\")\n        end\n      end\n\n      describe \"when cleaning cached facts\" do\n        it \"should destroy facts\" do\n          @host = 'node'\n          expect(Puppet::Node::Facts.indirection).to receive(:destroy).with(@host)\n\n          subject.clean_cached_facts(@host)\n        end\n      end\n\n      describe \"when cleaning cached node\" do\n        it \"should destroy the cached node\" do\n          expect(Puppet::Node.indirection).to receive(:destroy).with(@host)\n          subject.clean_cached_node(@host)\n        end\n      end\n\n      describe \"when cleaning archived reports\" do\n        it \"should tell the reports to remove themselves\" do\n          allow(Puppet::Transaction::Report.indirection).to receive(:destroy).with(@host)\n\n          subject.clean_reports(@host)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\nrequire 'puppet/face'\nrequire 'puppet/application/parser'\n\ndescribe Puppet::Face[:parser, :current] do\n  include PuppetSpec::Files\n\n  let(:parser) { Puppet::Face[:parser, :current] }\n\n  context \"validate\" do\n    let(:validate_app) do\n      Puppet::Application::Parser.new.tap do |app|\n        allow(app).to receive(:action).and_return(parser.get_action(:validate))\n      end\n    end\n\n    context \"from an interactive terminal\" do\n      before :each do\n        from_an_interactive_terminal\n      end\n\n      after(:each) do\n        # Reset cache of loaders (many examples run in the *root* environment\n        # which exists in \"eternity\")\n        Puppet.lookup(:current_environment).loaders = nil\n      end\n\n      it \"validates the configured site manifest when no files are given\" do\n        manifest = file_containing('site.pp', \"{ invalid =>\")\n\n        configured_environment = Puppet::Node::Environment.create(:default, [], manifest)\n        Puppet.override(:current_environment => configured_environment) do\n          parse_errors = parser.validate()\n\n          expect(parse_errors[manifest]).to be_a_kind_of(Puppet::ParseErrorWithIssue)\n        end\n      end\n\n      it \"validates the given file\" do\n        manifest = file_containing('site.pp', \"{ invalid =>\")\n        parse_errors = parser.validate(manifest)\n\n        expect(parse_errors[manifest]).to be_a_kind_of(Puppet::ParseErrorWithIssue)\n      end\n\n      it \"validates static heredoc with specified syntax\" do\n        manifest = file_containing('site.pp', \"@(EOT:pp)\n          { invalid =>\n          EOT\n        \")\n        parse_errors = parser.validate(manifest)\n\n        expect(parse_errors[manifest]).to be_a_kind_of(Puppet::ParseErrorWithIssue)\n      end\n\n      it \"does not validates dynamic heredoc with specified syntax\" do\n        manifest = file_containing('site.pp', \"@(\\\"EOT\\\":pp)\n        {invalid => ${1+1}\n        EOT\")\n        parse_errors = parser.validate(manifest)\n\n        expect(parse_errors).to be_empty\n      end\n\n      it \"runs error free when there are no validation errors\" do\n        manifest = file_containing('site.pp', \"notify { valid: }\")\n        parse_errors = parser.validate(manifest)\n\n        expect(parse_errors).to be_empty\n      end\n\n      it \"runs error free when there is a puppet function in manifest being validated\" do\n        manifest = file_containing('site.pp', \"function valid() { 'valid' } notify{ valid(): }\")\n        parse_errors = parser.validate(manifest)\n\n        expect(parse_errors).to be_empty\n      end\n\n      it \"runs error free when there is a type alias in a manifest that requires type resolution\" do\n        manifest = file_containing('site.pp',\n                                   \"type A = String; type B = Array[A]; function valid(B $x) { $x } notify{ valid([valid]): }\")\n        parse_errors = parser.validate(manifest)\n\n        expect(parse_errors).to be_empty\n      end\n\n      it \"reports missing files\" do\n        expect do\n          parser.validate(\"missing.pp\")\n        end.to raise_error(Puppet::Error, /One or more file\\(s\\) specified did not exist.*missing\\.pp/m)\n      end\n\n      it \"parses supplied manifest files in the context of a directory environment\" do\n        manifest = file_containing('test.pp', \"{ invalid =>\")\n\n        env = Puppet::Node::Environment.create(:special, [])\n        env_loader = Puppet::Environments::Static.new(env)\n        Puppet.override({:environments => env_loader, :current_environment => env}) do\n          parse_errors = parser.validate(manifest)\n\n          expect(parse_errors[manifest]).to be_a_kind_of(Puppet::ParseErrorWithIssue)\n        end\n      end\n    end\n\n    context \"when no files given and STDIN is not a tty\" do\n      it \"validates the contents of STDIN\" do\n        from_a_piped_input_of(\"{ invalid =>\")\n\n        Puppet.override(:current_environment => Puppet::Node::Environment.create(:special, [])) do\n          parse_errors = parser.validate()\n\n          expect(parse_errors['STDIN']).to be_a_kind_of(Puppet::ParseErrorWithIssue)\n        end\n      end\n\n      it \"runs error free when contents of STDIN is valid\" do\n        from_a_piped_input_of(\"notify { valid: }\")\n\n        Puppet.override(:current_environment => Puppet::Node::Environment.create(:special, [])) do\n          parse_errors = parser.validate()\n\n          expect(parse_errors).to be_empty\n        end\n      end\n    end\n\n    context \"when invoked with console output renderer\" do\n      before(:each) do\n        validate_app.render_as = :console\n      end\n\n      it \"logs errors using Puppet.log_exception\" do\n        manifest = file_containing('test.pp', \"{ invalid =>\")\n        results = parser.validate(manifest)\n\n        results.each do |_, error|\n          expect(Puppet).to receive(:log_exception).with(error)\n        end\n\n        expect { validate_app.render(results, nil) }.to raise_error(SystemExit)\n      end\n    end\n\n    context \"when invoked with --render-as=json\" do\n      before(:each) do\n        validate_app.render_as = :json\n      end\n\n      it \"outputs errors in a JSON document to stdout\" do\n        manifest = file_containing('test.pp', \"{ invalid =>\")\n        results = parser.validate(manifest)\n\n        expected_json = /\\A{.*#{Regexp.escape('\"message\":')}\\s*#{Regexp.escape('\"Syntax error at end of input\"')}.*}\\Z/m\n\n        expect { validate_app.render(results, nil) }.to output(expected_json).to_stdout.and raise_error(SystemExit)\n      end\n    end\n  end\n\n  context \"dump\" do\n    it \"prints the AST of the passed expression\" do\n      expect(parser.dump({ :e => 'notice hi' })).to eq(\"(invoke notice hi)\\n\")\n    end\n\n    it \"prints the AST of the code read from the passed files\" do\n      first_manifest = file_containing('site.pp', \"notice hi\")\n      second_manifest = file_containing('site2.pp', \"notice bye\")\n\n      output = parser.dump(first_manifest, second_manifest)\n\n      expect(output).to match(/site\\.pp.*\\(invoke notice hi\\)/)\n      expect(output).to match(/site2\\.pp.*\\(invoke notice bye\\)/)\n    end\n\n    it \"informs the user of files that don't exist\" do\n      expect(parser.dump('does_not_exist_here.pp')).to match(/did not exist:\\s*does_not_exist_here\\.pp/m)\n    end\n\n    it \"prints the AST of STDIN when no files given and STDIN is not a tty\" do\n      from_a_piped_input_of(\"notice hi\")\n\n      Puppet.override(:current_environment => Puppet::Node::Environment.create(:special, [])) do\n        expect(parser.dump()).to eq(\"(invoke notice hi)\\n\")\n      end\n    end\n\n    it \"logs an error if the input cannot be parsed\" do\n      output = parser.dump({ :e => '{ invalid =>' })\n\n      expect(output).to eq(\"\")\n      expect(@logs[0].message).to eq(\"Syntax error at end of input\")\n      expect(@logs[0].level).to eq(:err)\n    end\n\n    it \"logs an error if the input begins with a UTF-8 BOM (Byte Order Mark)\" do\n      utf8_bom_manifest = file_containing('utf8_bom.pp', \"\\uFEFFnotice hi\")\n\n      output = parser.dump(utf8_bom_manifest)\n\n      expect(output).to eq(\"\")\n      expect(@logs[1].message).to eq(\"Illegal UTF-8 Byte Order mark at beginning of input: [EF BB BF] - remove these from the puppet source\")\n      expect(@logs[1].level).to eq(:err)\n    end\n\n    it \"runs error free when there is a puppet function in manifest being dumped\" do\n      expect {\n        manifest = file_containing('site.pp', \"function valid() { 'valid' } notify{ valid(): }\")\n        parser.dump(manifest)\n      }.to_not raise_error\n    end\n\n    it \"runs error free when there is a type alias in a manifest that requires type resolution\" do\n      expect {\n        manifest = file_containing('site.pp',\n          \"type A = String; type B = Array[A]; function valid(B $x) { $x } notify{ valid([valid]): }\")\n        parser.dump(manifest)\n      }.to_not raise_error\n    end\n\n    context \"using 'pn' format\" do\n      it \"prints the AST of the given expression in PN format\" do\n        expect(parser.dump({ :format => 'pn', :e => 'if $x { \"hi ${x[2]}\" }' })).to eq(\n          '(if {:test (var \"x\") :then [(concat \"hi \" (str (access (var \"x\") 2)))]})')\n      end\n\n      it \"pretty prints the AST of the given expression in PN format when --pretty is given\" do\n        expect(parser.dump({ :pretty => true, :format => 'pn', :e => 'if $x { \"hi ${x[2]}\" }' })).to eq(<<-RESULT.unindent[0..-2])\n        (if\n          {\n            :test (var\n              \"x\")\n            :then [\n              (concat\n                \"hi \"\n                (str\n                  (access\n                    (var\n                      \"x\")\n                    2)))]})\n        RESULT\n      end\n    end\n\n    context \"using 'json' format\" do\n      it \"prints the AST of the given expression in JSON based on the PN format\" do\n        expect(parser.dump({ :format => 'json', :e => 'if $x { \"hi ${x[2]}\" }' })).to eq(\n          '{\"^\":[\"if\",{\"#\":[\"test\",{\"^\":[\"var\",\"x\"]},\"then\",[{\"^\":[\"concat\",\"hi \",{\"^\":[\"str\",{\"^\":[\"access\",{\"^\":[\"var\",\"x\"]},2]}]}]}]]}]}')\n      end\n\n      it \"pretty prints the AST of the given expression in JSON based on the PN format when --pretty is given\" do\n        expect(parser.dump({ :pretty => true, :format => 'json', :e => 'if $x { \"hi ${x[2]}\" }' })).to eq(<<-RESULT.unindent[0..-2])\n        {\n          \"^\": [\n            \"if\",\n            {\n              \"#\": [\n                \"test\",\n                {\n                  \"^\": [\n                    \"var\",\n                    \"x\"\n                  ]\n                },\n                \"then\",\n                [\n                  {\n                    \"^\": [\n                      \"concat\",\n                      \"hi \",\n                      {\n                        \"^\": [\n                          \"str\",\n                          {\n                            \"^\": [\n                              \"access\",\n                              {\n                                \"^\": [\n                                  \"var\",\n                                  \"x\"\n                                ]\n                              },\n                              2\n                            ]\n                          }\n                        ]\n                      }\n                    ]\n                  }\n                ]\n              ]\n            }\n          ]\n        }\n        RESULT\n      end\n    end\n  end\n\n  def from_an_interactive_terminal\n    allow(STDIN).to receive(:tty?).and_return(true)\n  end\n\n  def from_a_piped_input_of(contents)\n    allow(STDIN).to receive(:tty?).and_return(false)\n    allow(STDIN).to receive(:read).and_return(contents)\n  end\nend\n"
  },
  {
    "path": "spec/unit/face/plugin_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\n\ndescribe Puppet::Face[:plugin, :current] do\n  let(:pluginface) { described_class }\n  let(:action) { pluginface.get_action(:download) }\n\n  def render(result)\n    action.when_rendering(:console).call(result)\n  end\n\n  context \"download\" do\n    around do |example|\n      Puppet.override(server_agent_version: \"5.3.4\") do\n        example.run\n      end\n    end\n\n    context \"when i18n is enabled\" do\n      before(:each) do\n        Puppet[:disable_i18n] = false\n      end\n\n      it \"downloads plugins, external facts, and locales\" do\n        receive_count = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { receive_count += 1 }.and_return([])\n\n        pluginface.download\n        expect(receive_count).to eq(3)\n      end\n\n      it \"renders 'No plugins downloaded' if nothing was downloaded\" do\n        receive_count = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { receive_count += 1 }.and_return([])\n\n        result = pluginface.download\n        expect(receive_count).to eq(3)\n        expect(render(result)).to eq('No plugins downloaded.')\n      end\n\n      it \"renders comma separate list of downloaded file names\" do\n        receive_count = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n          receive_count += 1\n          case receive_count\n          when 1\n            %w[/a]\n          when 2\n            %w[/b]\n          when 3\n            %w[/c]\n          end\n        end\n\n        result = pluginface.download\n        expect(receive_count).to eq(3)\n        expect(render(result)).to eq('Downloaded these plugins: /a, /b, /c')\n      end\n    end\n\n    context \"when i18n is enabled\" do\n      before(:each) do\n        Puppet[:disable_i18n] = true\n      end\n\n      it \"downloads only plugins and external facts, no locales\" do\n        receive_count = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { receive_count += 1 }.and_return([])\n\n        pluginface.download\n        expect(receive_count).to eq(2)\n      end\n\n      it \"renders 'No plugins downloaded' if nothing was downloaded, without checking for locales\" do\n        receive_count = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { receive_count += 1 }.and_return([])\n\n        result = pluginface.download\n        expect(receive_count).to eq(2)\n        expect(render(result)).to eq('No plugins downloaded.')\n      end\n\n      it \"renders comma separate list of downloaded file names\" do\n        receive_count = 0\n        allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n          receive_count += 1\n          case receive_count\n          when 1\n            %w[/a]\n          when 2\n            %w[/b]\n          when 3\n            %w[/c]\n          end\n        end\n\n        result = pluginface.download\n        expect(receive_count).to eq(2)\n        expect(render(result)).to eq('Downloaded these plugins: /a, /b')\n      end\n    end\n  end\n\n  context \"download when server_agent_version is 5.3.3\" do\n    around do |example|\n      Puppet.override(server_agent_version: \"5.3.3\") do\n        example.run\n      end\n    end\n\n    it \"downloads plugins, and external facts, but not locales\" do\n      receive_count = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { receive_count += 1}.and_return([])\n\n      pluginface.download\n    end\n\n    it \"renders comma separate list of downloaded file names that does not include locales\" do\n      receive_count = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n        receive_count += 1\n        receive_count == 1 ? %w[/a] : %w[/b]\n      end\n\n      result = pluginface.download\n      expect(receive_count).to eq(2)\n      expect(render(result)).to eq('Downloaded these plugins: /a, /b')\n    end\n  end\n\n  context \"download when server_agent_version is blank\" do\n    around do |example|\n      Puppet.override(server_agent_version: \"\") do\n        example.run\n      end\n    end\n\n    it \"downloads plugins, and external facts, but not locales\" do\n      receive_count = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) { receive_count += 1 }.and_return([])\n\n      pluginface.download\n      expect(receive_count).to eq(2)\n    end\n\n    it \"renders comma separate list of downloaded file names that does not include locales\" do\n      receive_count = 0\n      allow_any_instance_of(Puppet::Configurer::Downloader).to receive(:evaluate) do\n        receive_count += 1\n        receive_count == 1 ? %w[/a] : %w[/b]\n      end\n\n      result = pluginface.download\n      expect(receive_count).to eq(2)\n      expect(render(result)).to eq('Downloaded these plugins: /a, /b')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/face_spec.rb",
    "content": "# You should look at interface_spec.rb\n"
  },
  {
    "path": "spec/unit/facter_impl_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::FacterImpl' do\n  subject(:facter_impl) { Puppet::FacterImpl.new }\n\n  it { is_expected.to respond_to(:value) }\n  it { is_expected.to respond_to(:add) }\n\n  describe '.value' do\n    let(:method_name) { :value }\n\n    before { allow(Facter).to receive(method_name) }\n\n    it 'delegates to Facter API' do\n      facter_impl.value('test_fact')\n      expect(Facter).to have_received(method_name).with('test_fact')\n    end\n  end\n\n  describe '.add' do\n    let(:block) { Proc.new { setcode 'test' } }\n    let(:method_name) { :add }\n\n    before { allow(Facter).to receive(method_name) }\n\n    it 'delegates to Facter API' do\n      facter_impl.add('test_fact', &block)\n      expect(Facter).to have_received(method_name).with('test_fact', &block)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_bucket/dipper_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'pathname'\n\nrequire 'puppet/file_bucket/dipper'\nrequire 'puppet/indirector/file_bucket_file/rest'\nrequire 'puppet/indirector/file_bucket_file/file'\nrequire 'puppet/util/checksums'\n\nshared_examples_for \"a restorable file\" do\n  let(:dest) { tmpfile('file_bucket_dest') }\n\n  describe \"restoring the file\" do\n    with_digest_algorithms do\n      it \"should restore the file\" do\n        request = nil\n\n        # With the *_any_instance_of form of using a block on receive, the\n        # first argument to the block is which instance is currently being\n        # dealt with, and the remaining arguments are the arguments to the\n        # method, instead of the block only getting the arguments to the\n        # method.\n        expect_any_instance_of(klass).to receive(:find) { |_,r| request = r }.and_return(Puppet::FileBucket::File.new(plaintext))\n\n        expect(dipper.restore(dest, checksum)).to eq(checksum)\n        expect(digest(Puppet::FileSystem.binread(dest))).to eq(checksum)\n\n        expect(request.key).to eq(\"#{digest_algorithm}/#{checksum}\")\n        expect(request.server).to eq(server)\n        expect(request.port).to eq(port)\n      end\n\n      it \"should skip restoring if existing file has the same checksum\" do\n        File.open(dest, 'wb') {|f| f.print(plaintext) }\n\n        expect(dipper).not_to receive(:getfile)\n        expect(dipper.restore(dest, checksum)).to be_nil\n      end\n\n      it \"should overwrite existing file if it has different checksum\" do\n        expect_any_instance_of(klass).to receive(:find).and_return(Puppet::FileBucket::File.new(plaintext))\n\n        File.open(dest, 'wb') {|f| f.print('other contents') }\n\n        expect(dipper.restore(dest, checksum)).to eq(checksum)\n      end\n    end\n  end\nend\n\ndescribe Puppet::FileBucket::Dipper, :uses_checksums => true do\n  include PuppetSpec::Files\n\n  def make_tmp_file(contents)\n    file = tmpfile(\"file_bucket_file\")\n    File.open(file, 'wb') { |f| f.write(contents) }\n    file\n  end\n\n  it \"should fail in an informative way when there are failures checking for the file on the server\" do\n    @dipper = Puppet::FileBucket::Dipper.new(:Path => make_absolute(\"/my/bucket\"))\n\n    file = make_tmp_file('contents')\n    expect(Puppet::FileBucket::File.indirection).to receive(:head).and_raise(ArgumentError)\n\n    expect { @dipper.backup(file) }.to raise_error(Puppet::Error)\n  end\n\n  it \"should fail in an informative way when there are failures backing up to the server\" do\n    @dipper = Puppet::FileBucket::Dipper.new(:Path => make_absolute(\"/my/bucket\"))\n\n    file = make_tmp_file('contents')\n    expect(Puppet::FileBucket::File.indirection).to receive(:head).and_return(false)\n    expect(Puppet::FileBucket::File.indirection).to receive(:save).and_raise(ArgumentError)\n\n    expect { @dipper.backup(file) }.to raise_error(Puppet::Error)\n  end\n\n  describe \"when diffing on a local filebucket\" do\n    describe \"in non-windows environments or JRuby\", :unless => Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n      with_digest_algorithms do\n\n        it \"should fail in an informative way when one or more checksum doesn't exists\" do\n          @dipper = Puppet::FileBucket::Dipper.new(:Path => tmpdir(\"bucket\"))\n          wrong_checksum = \"DEADBEEF\"\n\n          # First checksum fails\n          expect { @dipper.diff(wrong_checksum, \"WEIRDCKSM\", nil, nil) }.to raise_error(RuntimeError, \"Invalid checksum #{wrong_checksum.inspect}\")\n\n          file = make_tmp_file(plaintext)\n          @dipper.backup(file)\n\n          #Diff_with checksum fails\n          expect { @dipper.diff(checksum, wrong_checksum, nil, nil) }.to raise_error(RuntimeError, \"could not find diff_with #{wrong_checksum}\")\n        end\n\n        it \"should properly diff files on the filebucket\" do\n          file1 = make_tmp_file(\"OriginalContent\\n\")\n          file2 = make_tmp_file(\"ModifiedContent\\n\")\n          @dipper = Puppet::FileBucket::Dipper.new(:Path => tmpdir(\"bucket\"))\n          checksum1 = @dipper.backup(file1)\n          checksum2 = @dipper.backup(file2)\n\n          # Diff without the context\n          # Lines we need to see match 'Content' instead of trimming diff output filter out\n          # surrounding noise...or hard code the check values\n          if Puppet.runtime[:facter].value('os.family') == 'Solaris' &&\n            Puppet::Util::Package.versioncmp(Puppet.runtime[:facter].value('os.release.full'), '11.0') >= 0\n            # Use gdiff on Solaris\n            diff12 = Puppet::Util::Execution.execute(\"gdiff -uN #{file1} #{file2}| grep Content\")\n            diff21 = Puppet::Util::Execution.execute(\"gdiff -uN #{file2} #{file1}| grep Content\")\n          else\n            diff12 = Puppet::Util::Execution.execute(\"diff -uN #{file1} #{file2}| grep Content\")\n            diff21 = Puppet::Util::Execution.execute(\"diff -uN #{file2} #{file1}| grep Content\")\n          end\n\n          expect(@dipper.diff(checksum1, checksum2, nil, nil)).to include(diff12)\n          expect(@dipper.diff(checksum1, nil, nil, file2)).to include(diff12)\n          expect(@dipper.diff(nil, checksum2, file1, nil)).to include(diff12)\n          expect(@dipper.diff(nil, nil, file1, file2)).to include(diff12)\n          expect(@dipper.diff(checksum2, checksum1, nil, nil)).to include(diff21)\n          expect(@dipper.diff(checksum2, nil, nil, file1)).to include(diff21)\n          expect(@dipper.diff(nil, checksum1, file2, nil)).to include(diff21)\n          expect(@dipper.diff(nil, nil, file2, file1)).to include(diff21)\n        end\n      end\n\n      describe \"in windows environment\", :if => Puppet::Util::Platform.windows? do\n        it \"should fail in an informative way when trying to diff\" do\n          @dipper = Puppet::FileBucket::Dipper.new(:Path => tmpdir(\"bucket\"))\n          wrong_checksum = \"DEADBEEF\"\n\n          # First checksum fails\n          expect { @dipper.diff(wrong_checksum, \"WEIRDCKSM\", nil, nil) }.to raise_error(RuntimeError, \"Diff is not supported on this platform\")\n\n          # Diff_with checksum fails\n          expect { @dipper.diff(checksum, wrong_checksum, nil, nil) }.to raise_error(RuntimeError, \"Diff is not supported on this platform\")\n        end\n      end\n    end\n  end\n\n  it \"should fail in an informative way when there are failures listing files on the server\" do\n    @dipper = Puppet::FileBucket::Dipper.new(:Path => \"/unexistent/bucket\")\n    expect(Puppet::FileBucket::File.indirection).to receive(:find).and_return(nil)\n\n    expect { @dipper.list(nil, nil) }.to raise_error(Puppet::Error)\n  end\n\n  describe \"listing files in local filebucket\" do\n    with_digest_algorithms do\n      it \"should list all files present\" do\n        if Puppet::Util::Platform.windows? && digest_algorithm == \"sha512\"\n          skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n        end\n        Puppet[:bucketdir] =  \"/my/bucket\"\n        file_bucket = tmpdir(\"bucket\")\n\n        @dipper = Puppet::FileBucket::Dipper.new(:Path => file_bucket)\n\n        #First File\n        file1 = make_tmp_file(plaintext)\n        real_path = Pathname.new(file1).realpath\n        expect(digest(plaintext)).to eq(checksum)\n        expect(@dipper.backup(file1)).to eq(checksum)\n        expected_list1_1 = /#{checksum} \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} #{real_path}\\n/\n\n        File.open(file1, 'w') {|f| f.write(\"Blahhhh\")}\n        new_checksum = digest(\"Blahhhh\")\n        expect(@dipper.backup(file1)).to eq(new_checksum)\n        expected_list1_2 = /#{new_checksum} \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} #{real_path}\\n/\n\n        #Second File\n        content = \"DummyFileWithNonSenseTextInIt\"\n        file2 = make_tmp_file(content)\n        real_path = Pathname.new(file2).realpath\n        checksum = digest(content)\n        expect(@dipper.backup(file2)).to eq(checksum)\n        expected_list2 = /#{checksum} \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} #{real_path}\\n/\n\n        #Third file : Same as the first one with a different path\n        file3 = make_tmp_file(plaintext)\n        real_path = Pathname.new(file3).realpath\n        checksum = digest(plaintext)\n        expect(digest(plaintext)).to eq(checksum)\n        expect(@dipper.backup(file3)).to eq(checksum)\n        expected_list3 = /#{checksum} \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} #{real_path}\\n/\n\n        result = @dipper.list(nil, nil)\n        expect(result).to match(expected_list1_1)\n        expect(result).to match(expected_list1_2)\n        expect(result).to match(expected_list2)\n        expect(result).to match(expected_list3)\n      end\n\n      it \"should filter with the provided dates\" do\n        if Puppet::Util::Platform.windows? && digest_algorithm == \"sha512\"\n          skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n        end\n        Puppet[:bucketdir] =  \"/my/bucket\"\n        file_bucket = tmpdir(\"bucket\")\n\n        twentyminutes=60*20\n        thirtyminutes=60*30\n        onehour=60*60\n        twohours=onehour*2\n        threehours=onehour*3\n\n        # First File created now\n        @dipper = Puppet::FileBucket::Dipper.new(:Path => file_bucket)\n        file1 = make_tmp_file(plaintext)\n        real_path = Pathname.new(file1).realpath\n\n        expect(digest(plaintext)).to eq(checksum)\n        expect(@dipper.backup(file1)).to eq(checksum)\n        expected_list1 = /#{checksum} \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} #{real_path}\\n/\n\n        # Second File created an hour ago\n        content = \"DummyFileWithNonSenseTextInIt\"\n        file2 = make_tmp_file(content)\n        real_path = Pathname.new(file2).realpath\n        checksum = digest(content)\n        expect(@dipper.backup(file2)).to eq(checksum)\n\n        # Modify mtime of the second file to be an hour ago\n        onehourago = Time.now - onehour\n        bucketed_paths_file = Dir.glob(\"#{file_bucket}/**/#{checksum}/paths\")\n        FileUtils.touch(bucketed_paths_file, mtime: onehourago)\n        expected_list2 = /#{checksum} \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} #{real_path}\\n/\n\n        now = Time.now\n\n        #Future\n        expect(@dipper.list((now + threehours).strftime(\"%F %T\"), nil )).to eq(\"\")\n\n        #Epoch -> Future = Everything (Sorted (desc) by date)\n        expect(@dipper.list(nil, (now + twohours).strftime(\"%F %T\"))).to match(expected_list1)\n        expect(@dipper.list(nil, (now + twohours).strftime(\"%F %T\"))).to match(expected_list2)\n\n        #Now+1sec -> Future = Nothing\n        expect(@dipper.list((now + 1).strftime(\"%F %T\"), (now + twohours).strftime(\"%F %T\"))).to eq(\"\")\n\n        #Now-30mins -> Now-20mins = Nothing\n        expect(@dipper.list((now - thirtyminutes).strftime(\"%F %T\"), (now - twentyminutes).strftime(\"%F %T\"))).to eq(\"\")\n\n        #Now-2hours -> Now-30mins = Second file only\n        expect(@dipper.list((now - twohours).strftime(\"%F %T\"), (now - thirtyminutes).strftime(\"%F %T\"))).to match(expected_list2)\n        expect(@dipper.list((now - twohours).strftime(\"%F %T\"), (now - thirtyminutes).strftime(\"%F %T\"))).not_to match(expected_list1)\n\n        #Now-30minutes -> Now = First file only\n        expect(@dipper.list((now - thirtyminutes).strftime(\"%F %T\"), now.strftime(\"%F %T\"))).to match(expected_list1)\n        expect(@dipper.list((now - thirtyminutes).strftime(\"%F %T\"), now.strftime(\"%F %T\"))).not_to match(expected_list2)\n      end\n    end\n  end\n\n  describe \"when diffing on a remote filebucket\" do\n    describe \"in non-windows environments\", :unless => Puppet::Util::Platform.windows? do\n      with_digest_algorithms do\n        it \"should fail in an informative way when one or more checksum doesn't exists\" do\n          @dipper = Puppet::FileBucket::Dipper.new(:Server => \"puppetmaster\", :Port => \"31337\")\n          wrong_checksum = \"DEADBEEF\"\n\n          expect_any_instance_of(Puppet::FileBucketFile::Rest).to receive(:find).and_return(nil)\n          expect { @dipper.diff(wrong_checksum, \"WEIRDCKSM\", nil, nil) }.to raise_error(Puppet::Error, \"Failed to diff files\")\n\n        end\n\n        it \"should properly diff files on the filebucket\" do\n          @dipper = Puppet::FileBucket::Dipper.new(:Server => \"puppetmaster\", :Port => \"31337\")\n\n          expect_any_instance_of(Puppet::FileBucketFile::Rest).to receive(:find).and_return(\"Probably valid diff\")\n\n          expect(@dipper.diff(\"checksum1\", \"checksum2\", nil, nil)).to eq(\"Probably valid diff\")\n        end\n      end\n    end\n\n    describe \"in windows environment\", :if => Puppet::Util::Platform.windows? do\n      it \"should fail in an informative way when trying to diff\" do\n        @dipper = Puppet::FileBucket::Dipper.new(:Server => \"puppetmaster\", :Port => \"31337\")\n        wrong_checksum = \"DEADBEEF\"\n\n        expect { @dipper.diff(wrong_checksum, \"WEIRDCKSM\", nil, nil) }.to raise_error(RuntimeError, \"Diff is not supported on this platform\")\n\n        expect { @dipper.diff(wrong_checksum, nil, nil, nil) }.to raise_error(RuntimeError, \"Diff is not supported on this platform\")\n      end\n    end\n  end\n\n  describe \"listing files in remote filebucket\" do\n    it \"is not allowed\" do\n      @dipper = Puppet::FileBucket::Dipper.new(:Server => \"puppetmaster\", :Port=> \"31337\")\n\n      expect {\n        @dipper.list(nil, nil)\n      }.to raise_error(Puppet::Error, \"Listing remote file buckets is not allowed\")\n    end\n  end\n\n  describe \"backing up and retrieving local files\" do\n    with_digest_algorithms do\n      it \"should backup files to a local bucket\" do\n        if Puppet::Util::Platform.windows? && digest_algorithm == \"sha512\"\n          skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n        end\n        Puppet[:bucketdir] = \"/non/existent/directory\"\n        file_bucket = tmpdir(\"bucket\")\n\n        @dipper = Puppet::FileBucket::Dipper.new(:Path => file_bucket)\n\n        file = make_tmp_file(plaintext)\n        expect(digest(plaintext)).to eq(checksum)\n\n        expect(@dipper.backup(file)).to eq(checksum)\n        expect(Puppet::FileSystem.exist?(\"#{file_bucket}/#{bucket_dir}/contents\")).to eq(true)\n      end\n\n      it \"should not backup a file that is already in the bucket\" do\n        @dipper = Puppet::FileBucket::Dipper.new(:Path => \"/my/bucket\")\n\n        file = make_tmp_file(plaintext)\n\n        expect(Puppet::FileBucket::File.indirection).to receive(:head).with(\n          %r{#{digest_algorithm}/#{checksum}}, {:bucket_path => \"/my/bucket\"}\n        ).and_return(true)\n        expect(Puppet::FileBucket::File.indirection).not_to receive(:save)\n        expect(@dipper.backup(file)).to eq(checksum)\n      end\n\n      it \"should retrieve files from a local bucket\" do\n        @dipper = Puppet::FileBucket::Dipper.new(:Path => \"/my/bucket\")\n\n        request = nil\n\n        expect_any_instance_of(Puppet::FileBucketFile::File).to receive(:find) { |_,r| request = r }.once.and_return(Puppet::FileBucket::File.new(plaintext))\n\n        expect(@dipper.getfile(checksum)).to eq(plaintext)\n\n        expect(request.key).to eq(\"#{digest_algorithm}/#{checksum}\")\n      end\n    end\n  end\n\n  describe \"backing up and retrieving remote files\" do\n    with_digest_algorithms do\n      it \"should backup files to a remote server\" do\n        @dipper = Puppet::FileBucket::Dipper.new(:Server => \"puppetmaster\", :Port => \"31337\")\n\n        file = make_tmp_file(plaintext)\n\n        real_path = Pathname.new(file).realpath\n\n        request1 = nil\n        request2 = nil\n\n        expect_any_instance_of(Puppet::FileBucketFile::Rest).to receive(:head) { |_,r| request1 = r }.once.and_return(nil)\n        expect_any_instance_of(Puppet::FileBucketFile::Rest).to receive(:save) { |_,r| request2 = r }.once\n\n        expect(@dipper.backup(file)).to eq(checksum)\n        [request1, request2].each do |r|\n          expect(r.server).to eq('puppetmaster')\n          expect(r.port).to eq(31337)\n          expect(r.key).to eq(\"#{digest_algorithm}/#{checksum}/#{real_path}\")\n        end\n      end\n\n      it \"should retrieve files from a remote server\" do\n        @dipper = Puppet::FileBucket::Dipper.new(:Server => \"puppetmaster\", :Port => \"31337\")\n\n        request = nil\n\n        expect_any_instance_of(Puppet::FileBucketFile::Rest).to receive(:find) { |_,r| request = r }.and_return(Puppet::FileBucket::File.new(plaintext))\n\n        expect(@dipper.getfile(checksum)).to eq(plaintext)\n\n        expect(request.server).to eq('puppetmaster')\n        expect(request.port).to eq(31337)\n        expect(request.key).to eq(\"#{digest_algorithm}/#{checksum}\")\n      end\n    end\n  end\n\n  describe \"#restore\" do\n    describe \"when restoring from a remote server\" do\n      let(:klass) { Puppet::FileBucketFile::Rest }\n      let(:server) { \"puppetmaster\" }\n      let(:port) { 31337 }\n\n      it_behaves_like \"a restorable file\" do\n        let (:dipper) { Puppet::FileBucket::Dipper.new(:Server => server, :Port => port.to_s) }\n      end\n    end\n\n    describe \"when restoring from a local server\" do\n      let(:klass) { Puppet::FileBucketFile::File }\n      let(:server) { nil }\n      let(:port) { nil }\n\n      it_behaves_like \"a restorable file\" do\n        let (:dipper) { Puppet::FileBucket::Dipper.new(:Path => \"/my/bucket\") }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_bucket/file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_bucket/file'\n\ndescribe Puppet::FileBucket::File, :uses_checksums => true do\n  include PuppetSpec::Files\n\n  # this is the default from spec_helper, but it keeps getting reset at odd times\n  let(:bucketdir) { Puppet[:bucketdir] = tmpdir('bucket') }\n\n  it \"defaults to serializing to `:binary`\" do\n    expect(Puppet::FileBucket::File.default_format).to eq(:binary)\n  end\n\n  it \"only accepts binary\" do\n    expect(Puppet::FileBucket::File.supported_formats).to eq([:binary])\n  end\n\n  describe \"making round trips through network formats\" do\n    with_digest_algorithms do\n      it \"can make a round trip through `binary`\" do\n        file = Puppet::FileBucket::File.new(plaintext)\n        tripped = Puppet::FileBucket::File.convert_from(:binary, file.render)\n        expect(tripped.contents).to eq(plaintext)\n      end\n    end\n  end\n\n  it \"should require contents to be a string\" do\n    expect { Puppet::FileBucket::File.new(5) }.to raise_error(ArgumentError, /contents must be a String or Pathname, got a Integer$/)\n  end\n\n  it \"should complain about options other than :bucket_path\" do\n    expect {\n      Puppet::FileBucket::File.new('5', :crazy_option => 'should not be passed')\n    }.to raise_error(ArgumentError, /Unknown option\\(s\\): crazy_option/)\n  end\n\n  with_digest_algorithms do\n    it \"it uses #{metadata[:digest_algorithm]} as the configured digest algorithm\" do\n      file = Puppet::FileBucket::File.new(plaintext)\n\n      expect(file.contents).to eq(plaintext)\n      expect(file.checksum_type).to eq(digest_algorithm)\n      expect(file.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n      expect(file.name).to eq(\"#{digest_algorithm}/#{checksum}\")\n    end\n  end\n\n  describe \"when using back-ends\" do\n    it \"should redirect using Puppet::Indirector\" do\n      expect(Puppet::Indirector::Indirection.instance(:file_bucket_file).model).to equal(Puppet::FileBucket::File)\n    end\n\n    it \"should have a :save instance method\" do\n      expect(Puppet::FileBucket::File.indirection).to respond_to(:save)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/base_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/base'\n\ndescribe Puppet::FileServing::Base do\n  let(:path) { File.expand_path('/module/dir/file') }\n  let(:file) { File.expand_path('/my/file') }\n\n  it \"should accept a path\" do\n    expect(Puppet::FileServing::Base.new(path).path).to eq(path)\n  end\n\n  it \"should require that paths be fully qualified\" do\n    expect { Puppet::FileServing::Base.new(\"module/dir/file\") }.to raise_error(ArgumentError)\n  end\n\n  it \"should allow specification of whether links should be managed\" do\n    expect(Puppet::FileServing::Base.new(path, :links => :manage).links).to eq(:manage)\n  end\n\n  it \"should have a :source attribute\" do\n    file = Puppet::FileServing::Base.new(path)\n    expect(file).to respond_to(:source)\n    expect(file).to respond_to(:source=)\n  end\n\n  it \"should consider :ignore links equivalent to :manage links\" do\n    expect(Puppet::FileServing::Base.new(path, :links => :ignore).links).to eq(:manage)\n  end\n\n  it \"should fail if :links is set to anything other than :manage, :follow, or :ignore\" do\n    expect { Puppet::FileServing::Base.new(path, :links => :else) }.to raise_error(ArgumentError)\n  end\n\n  it \"should allow links values to be set as strings\" do\n    expect(Puppet::FileServing::Base.new(path, :links => \"follow\").links).to eq(:follow)\n  end\n\n  it \"should default to :manage for :links\" do\n    expect(Puppet::FileServing::Base.new(path).links).to eq(:manage)\n  end\n\n  it \"should allow specification of a relative path\" do\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    expect(Puppet::FileServing::Base.new(path, :relative_path => \"my/file\").relative_path).to eq(\"my/file\")\n  end\n\n  it \"should have a means of determining if the file exists\" do\n    expect(Puppet::FileServing::Base.new(file)).to respond_to(:exist?)\n  end\n\n  it \"should correctly indicate if the file is present\" do\n    expect(Puppet::FileSystem).to receive(:lstat).with(file).and_return(double('stat'))\n    expect(Puppet::FileServing::Base.new(file).exist?).to be_truthy\n  end\n\n  it \"should correctly indicate if the file is absent\" do\n    expect(Puppet::FileSystem).to receive(:lstat).with(file).and_raise(RuntimeError)\n    expect(Puppet::FileServing::Base.new(file).exist?).to be_falsey\n  end\n\n  describe \"when setting the relative path\" do\n    it \"should require that the relative path be unqualified\" do\n      @file = Puppet::FileServing::Base.new(path)\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      expect { @file.relative_path = File.expand_path(\"/qualified/file\") }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"when determining the full file path\" do\n    let(:path) { File.expand_path('/this/file') }\n    let(:file) { Puppet::FileServing::Base.new(path) }\n\n    it \"should return the path if there is no relative path\" do\n      expect(file.full_path).to eq(path)\n    end\n\n    it \"should return the path if the relative_path is set to ''\" do\n      file.relative_path = \"\"\n      expect(file.full_path).to eq(path)\n    end\n\n    it \"should return the path if the relative_path is set to '.'\" do\n      file.relative_path = \".\"\n      expect(file.full_path).to eq(path)\n    end\n\n    it \"should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''\" do\n      file.relative_path = \"not/qualified\"\n      expect(file.full_path).to eq(File.join(path, \"not/qualified\"))\n    end\n\n    it \"should strip extra slashes\" do\n      file = Puppet::FileServing::Base.new(File.join(File.expand_path('/'), \"//this//file\"))\n      expect(file.full_path).to eq(path)\n    end\n  end\n\n  describe \"when handling a UNC file path on Windows\" do\n    let(:path) { '//server/share/filename' }\n    let(:file) { Puppet::FileServing::Base.new(path) }\n\n    before :each do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n    end\n\n    it \"should preserve double slashes at the beginning of the path\" do\n      expect(file.full_path).to eq(path)\n    end\n\n    it \"should strip double slashes not at the beginning of the path\" do\n      file = Puppet::FileServing::Base.new('//server//share//filename')\n      expect(file.full_path).to eq(path)\n    end\n  end\n\n\n  describe \"when stat'ing files\" do\n    let(:path) { File.expand_path('/this/file') }\n    let(:file) { Puppet::FileServing::Base.new(path) }\n    let(:stat) { double('stat', :ftype => 'file' ) }\n    let(:stubbed_file) { double(path, :stat => stat, :lstat => stat)}\n\n    it \"should stat the file's full path\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(stat)\n      file.stat\n    end\n\n    it \"should fail if the file does not exist\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(path).and_raise(Errno::ENOENT)\n      expect { file.stat }.to raise_error(Errno::ENOENT)\n    end\n\n    it \"should use :lstat if :links is set to :manage\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(stubbed_file)\n      file.stat\n    end\n\n    it \"should use :stat if :links is set to :follow\" do\n      expect(Puppet::FileSystem).to receive(:stat).with(path).and_return(stubbed_file)\n      file.links = :follow\n      file.stat\n    end\n  end\n\n  describe \"#absolute?\" do\n    it \"should be accept POSIX paths\" do\n      expect(Puppet::FileServing::Base).to be_absolute('/')\n    end\n\n    it \"should accept Windows paths on Windows\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n      allow(Puppet.features).to receive(:posix?).and_return(false)\n\n      expect(Puppet::FileServing::Base).to be_absolute('c:/foo')\n    end\n\n    it \"should reject Windows paths on POSIX\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n\n      expect(Puppet::FileServing::Base).not_to be_absolute('c:/foo')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/configuration/parser_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/configuration/parser'\n\n\nmodule FSConfigurationParserTesting\n  def write_config_file(content)\n    # We want an array, but we actually want our carriage returns on all of it.\n    File.open(@path, 'w') {|f| f.puts content}\n  end\nend\n\ndescribe Puppet::FileServing::Configuration::Parser do\n  include PuppetSpec::Files\n\n  before :each do\n    @path = tmpfile('fileserving_config')\n    FileUtils.touch(@path)\n    @parser = Puppet::FileServing::Configuration::Parser.new(@path)\n  end\n\n  describe Puppet::FileServing::Configuration::Parser, \" when parsing\" do\n    include FSConfigurationParserTesting\n\n    it \"should allow comments\" do\n      write_config_file(\"# this is a comment\\n\")\n      expect { @parser.parse }.not_to raise_error\n    end\n\n    it \"should allow blank lines\" do\n      write_config_file(\"\\n\")\n      expect { @parser.parse }.not_to raise_error\n    end\n\n    it \"should return a hash of the created mounts\" do\n      mount1 = double('one', :validate => true)\n      mount2 = double('two', :validate => true)\n      expect(Puppet::FileServing::Mount::File).to receive(:new).with(\"one\").and_return(mount1)\n      expect(Puppet::FileServing::Mount::File).to receive(:new).with(\"two\").and_return(mount2)\n      write_config_file \"[one]\\n[two]\\n\"\n\n      result = @parser.parse\n      expect(result[\"one\"]).to equal(mount1)\n      expect(result[\"two\"]).to equal(mount2)\n    end\n\n    it \"should only allow mount names that are alphanumeric plus dashes\" do\n      write_config_file \"[a*b]\\n\"\n      expect { @parser.parse }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if the value for path starts with an equals sign\" do\n      write_config_file \"[one]\\npath = /testing\"\n      expect { @parser.parse }.to raise_error(ArgumentError)\n    end\n\n    it \"should validate each created mount\" do\n      mount1 = double('one')\n      expect(Puppet::FileServing::Mount::File).to receive(:new).with(\"one\").and_return(mount1)\n      write_config_file \"[one]\\n\"\n\n      expect(mount1).to receive(:validate)\n\n      @parser.parse\n    end\n\n    it \"should fail if any mount does not pass validation\" do\n      mount1 = double('one')\n      expect(Puppet::FileServing::Mount::File).to receive(:new).with(\"one\").and_return(mount1)\n      write_config_file \"[one]\\n\"\n\n      expect(mount1).to receive(:validate).and_raise(RuntimeError)\n\n      expect { @parser.parse }.to raise_error(RuntimeError)\n    end\n\n    it \"should return comprehensible error message, if invalid line detected\" do\n      write_config_file \"[one]\\n\\n\\x01path /etc/puppetlabs/puppet/files\\n\\x01allow *\\n\"\n\n      expect { @parser.parse }.to raise_error(ArgumentError, /Invalid entry at \\(file: .*, line: 3\\): .*/)\n    end\n  end\n\n  describe Puppet::FileServing::Configuration::Parser, \" when parsing mount attributes\" do\n    include FSConfigurationParserTesting\n\n    before do\n      @mount = double('testmount', :name => \"one\", :validate => true)\n      expect(Puppet::FileServing::Mount::File).to receive(:new).with(\"one\").and_return(@mount)\n    end\n\n    it \"should set the mount path to the path attribute from that section\" do\n      write_config_file \"[one]\\npath /some/path\\n\"\n\n      expect(@mount).to receive(:path=).with(\"/some/path\")\n      @parser.parse\n    end\n\n    [:allow,:deny].each { |acl_type|\n      it \"should ignore inline comments in #{acl_type}\" do\n        write_config_file \"[one]\\n#{acl_type} something \\# will it work?\\n\"\n\n        expect(@parser.parse).to eq('one' => @mount)\n      end\n\n      it \"should ignore #{acl_type} from ACLs with varying spacing around commas\" do\n        write_config_file \"[one]\\n#{acl_type} someone,sometwo, somethree , somefour ,somefive\\n\"\n\n        expect(@parser.parse).to eq('one' => @mount)\n      end\n\n      it \"should log an error and print the location of the #{acl_type} rule\" do\n        write_config_file(<<~CONFIG)\n        [one]\n        #{acl_type} one\n        CONFIG\n\n        expect(Puppet).to receive(:err).with(/Entry '#{acl_type} one' is unsupported and will be ignored at \\(file: .*, line: 2\\)/)\n\n        @parser.parse\n      end\n    }\n\n    it \"should not generate an error when parsing 'allow *'\" do\n      write_config_file \"[one]\\nallow *\\n\"\n\n      expect(Puppet).to receive(:err).never\n\n      @parser.parse\n    end\n\n    it \"should return comprehensible error message, if failed on invalid attribute\" do\n      write_config_file \"[one]\\ndo something\\n\"\n\n      expect { @parser.parse }.to raise_error(ArgumentError, /Invalid argument 'do' at \\(file: .*, line: 2\\)/)\n    end\n  end\n\n  describe Puppet::FileServing::Configuration::Parser, \" when parsing the modules mount\" do\n    include FSConfigurationParserTesting\n\n    before do\n      @mount = double('modulesmount', :name => \"modules\", :validate => true)\n    end\n\n    it \"should create an instance of the Modules Mount class\" do\n      write_config_file \"[modules]\\n\"\n\n      expect(Puppet::FileServing::Mount::Modules).to receive(:new).with(\"modules\").and_return(@mount)\n      @parser.parse\n    end\n\n    it \"should warn if a path is set\" do\n      write_config_file \"[modules]\\npath /some/path\\n\"\n      expect(Puppet::FileServing::Mount::Modules).to receive(:new).with(\"modules\").and_return(@mount)\n\n      expect(Puppet).to receive(:warning)\n      @parser.parse\n    end\n  end\n\n  describe Puppet::FileServing::Configuration::Parser, \" when parsing the scripts mount\" do\n    include FSConfigurationParserTesting\n\n    before do\n      @mount = double('scriptsmount', :name => \"scripts\", :validate => true)\n    end\n\n    it \"should create an instance of the Scripts Mount class\" do\n      write_config_file \"[scripts]\\n\"\n\n      expect(Puppet::FileServing::Mount::Scripts).to receive(:new).with(\"scripts\").and_return(@mount)\n      @parser.parse\n    end\n\n    it \"should warn if a path is set\" do\n      write_config_file \"[scripts]\\npath /some/path\\n\"\n      expect(Puppet::FileServing::Mount::Scripts).to receive(:new).with(\"scripts\").and_return(@mount)\n\n      expect(Puppet).to receive(:warning)\n      @parser.parse\n    end\n  end\n\n  describe Puppet::FileServing::Configuration::Parser, \" when parsing the plugins mount\" do\n    include FSConfigurationParserTesting\n\n    before do\n      @mount = double('pluginsmount', :name => \"plugins\", :validate => true)\n    end\n\n    it \"should create an instance of the Plugins Mount class\" do\n      write_config_file \"[plugins]\\n\"\n\n      expect(Puppet::FileServing::Mount::Plugins).to receive(:new).with(\"plugins\").and_return(@mount)\n      @parser.parse\n    end\n\n    it \"should warn if a path is set\" do\n      write_config_file \"[plugins]\\npath /some/path\\n\"\n\n      expect(Puppet).to receive(:warning)\n      @parser.parse\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/configuration_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/configuration'\n\ndescribe Puppet::FileServing::Configuration do\n  include PuppetSpec::Files\n\n  before :each do\n    @path = make_absolute(\"/path/to/configuration/file.conf\")\n    Puppet[:trace] = false\n    Puppet[:fileserverconfig] = @path\n  end\n\n  after :each do\n    Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil)\n  end\n\n  it \"should make :new a private method\" do\n    expect { Puppet::FileServing::Configuration.new }.to raise_error(NoMethodError, /private method `new' called/)\n  end\n\n  it \"should return the same configuration each time 'configuration' is called\" do\n    expect(Puppet::FileServing::Configuration.configuration).to equal(Puppet::FileServing::Configuration.configuration)\n  end\n\n  describe \"when initializing\" do\n    it \"should work without a configuration file\" do\n      allow(Puppet::FileSystem).to receive(:exist?).with(@path).and_return(false)\n      expect { Puppet::FileServing::Configuration.configuration }.to_not raise_error\n    end\n\n    it \"should parse the configuration file if present\" do\n      allow(Puppet::FileSystem).to receive(:exist?).with(@path).and_return(true)\n      @parser = double('parser')\n      expect(@parser).to receive(:parse).and_return({})\n      allow(Puppet::FileServing::Configuration::Parser).to receive(:new).and_return(@parser)\n      Puppet::FileServing::Configuration.configuration\n    end\n\n    it \"should determine the path to the configuration file from the Puppet settings\" do\n      Puppet::FileServing::Configuration.configuration\n    end\n  end\n\n  describe \"when parsing the configuration file\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).with(@path).and_return(true)\n      @parser = double('parser')\n      allow(Puppet::FileServing::Configuration::Parser).to receive(:new).and_return(@parser)\n    end\n\n    it \"should set the mount list to the results of parsing\" do\n      expect(@parser).to receive(:parse).and_return(\"one\" => double(\"mount\"))\n      config = Puppet::FileServing::Configuration.configuration\n      expect(config.mounted?(\"one\")).to be_truthy\n    end\n\n    it \"should not raise exceptions\" do\n      expect(@parser).to receive(:parse).and_raise(ArgumentError)\n      expect { Puppet::FileServing::Configuration.configuration }.to_not raise_error\n    end\n\n    it \"should replace the existing mount list with the results of reparsing\" do\n      expect(@parser).to receive(:parse).and_return(\"one\" => double(\"mount\"))\n      config = Puppet::FileServing::Configuration.configuration\n      expect(config.mounted?(\"one\")).to be_truthy\n      # Now parse again\n      expect(@parser).to receive(:parse).and_return(\"two\" => double('other'))\n      config.send(:readconfig, false)\n      expect(config.mounted?(\"one\")).to be_falsey\n      expect(config.mounted?(\"two\")).to be_truthy\n    end\n\n    it \"should not replace the mount list until the file is entirely parsed successfully\" do\n      expect(@parser).to receive(:parse).and_return(\"one\" => double(\"mount\"))\n      expect(@parser).to receive(:parse).and_raise(ArgumentError)\n      config = Puppet::FileServing::Configuration.configuration\n      # Now parse again, so the exception gets thrown\n      config.send(:readconfig, false)\n      expect(config.mounted?(\"one\")).to be_truthy\n    end\n\n    it \"should add modules, plugins, scripts, and tasks mounts even if the file does not exist\" do\n      expect(Puppet::FileSystem).to receive(:exist?).and_return(false) # the file doesn't exist\n      config = Puppet::FileServing::Configuration.configuration\n      expect(config.mounted?(\"modules\")).to be_truthy\n      expect(config.mounted?(\"plugins\")).to be_truthy\n      expect(config.mounted?(\"scripts\")).to be_truthy\n      expect(config.mounted?(\"tasks\")).to be_truthy\n    end\n\n    it \"should allow all access to modules, plugins, scripts, and tasks if no fileserver.conf exists\" do\n      expect(Puppet::FileSystem).to receive(:exist?).and_return(false) # the file doesn't exist\n      modules = double('modules')\n      allow(Puppet::FileServing::Mount::Modules).to receive(:new).and_return(modules)\n\n      plugins = double('plugins')\n      allow(Puppet::FileServing::Mount::Plugins).to receive(:new).and_return(plugins)\n\n      tasks = double('tasks')\n      allow(Puppet::FileServing::Mount::Tasks).to receive(:new).and_return(tasks)\n\n      scripts = double('scripts')\n      allow(Puppet::FileServing::Mount::Scripts).to receive(:new).and_return(scripts)\n\n      Puppet::FileServing::Configuration.configuration\n    end\n\n    it \"should not allow access from all to modules, plugins, scripts, and tasks if the fileserver.conf provided some rules\" do\n      expect(Puppet::FileSystem).to receive(:exist?).and_return(false) # the file doesn't exist\n\n      modules = double('modules')\n      allow(Puppet::FileServing::Mount::Modules).to receive(:new).and_return(modules)\n\n      plugins = double('plugins')\n      allow(Puppet::FileServing::Mount::Plugins).to receive(:new).and_return(plugins)\n\n      tasks = double('tasks')\n      allow(Puppet::FileServing::Mount::Tasks).to receive(:new).and_return(tasks)\n\n      scripts = double('scripts', :empty? => false)\n      allow(Puppet::FileServing::Mount::Scripts).to receive(:new).and_return(scripts)\n\n      Puppet::FileServing::Configuration.configuration\n    end\n\n    it \"should add modules, plugins, scripts, and tasks mounts even if they are not returned by the parser\" do\n      expect(@parser).to receive(:parse).and_return(\"one\" => double(\"mount\"))\n      expect(Puppet::FileSystem).to receive(:exist?).and_return(true) # the file doesn't exist\n      config = Puppet::FileServing::Configuration.configuration\n      expect(config.mounted?(\"modules\")).to be_truthy\n      expect(config.mounted?(\"plugins\")).to be_truthy\n      expect(config.mounted?(\"scripts\")).to be_truthy\n      expect(config.mounted?(\"tasks\")).to be_truthy\n    end\n  end\n\n  describe \"when finding the specified mount\" do\n    it \"should choose the named mount if one exists\" do\n      config = Puppet::FileServing::Configuration.configuration\n      expect(config).to receive(:mounts).and_return(\"one\" => \"foo\")\n      expect(config.find_mount(\"one\", double('env'))).to eq(\"foo\")\n    end\n\n    it \"should return nil if there is no such named mount\" do\n      config = Puppet::FileServing::Configuration.configuration\n\n      env = double('environment')\n      mount = double('mount')\n      allow(config).to receive(:mounts).and_return(\"modules\" => mount)\n\n      expect(config.find_mount(\"foo\", env)).to be_nil\n    end\n  end\n\n  describe \"#split_path\" do\n    let(:config) { Puppet::FileServing::Configuration.configuration }\n    let(:request) { double('request', :key => \"foo/bar/baz\", :options => {}, :node => nil, :environment => double(\"env\")) }\n\n    before do\n      allow(config).to receive(:find_mount)\n    end\n\n    it \"should reread the configuration\" do\n      expect(config).to receive(:readconfig)\n\n      config.split_path(request)\n    end\n\n    it \"should treat the first field of the URI path as the mount name\" do\n      expect(config).to receive(:find_mount).with(\"foo\", anything)\n\n      config.split_path(request)\n    end\n\n    it \"should fail if the mount name is not alpha-numeric\" do\n      expect(request).to receive(:key).and_return(\"foo&bar/asdf\")\n\n      expect { config.split_path(request) }.to raise_error(ArgumentError)\n    end\n\n    it \"should support dashes in the mount name\" do\n      expect(request).to receive(:key).and_return(\"foo-bar/asdf\")\n\n      expect { config.split_path(request) }.to_not raise_error\n    end\n\n    it \"should use the mount name and environment to find the mount\" do\n      expect(config).to receive(:find_mount).with(\"foo\", request.environment)\n      allow(request).to receive(:node).and_return(\"mynode\")\n\n      config.split_path(request)\n    end\n\n    it \"should return nil if the mount cannot be found\" do\n      expect(config).to receive(:find_mount).and_return(nil)\n\n      expect(config.split_path(request)).to be_nil\n    end\n\n    it \"should return the mount and the relative path if the mount is found\" do\n      mount = double('mount', :name => \"foo\")\n      expect(config).to receive(:find_mount).and_return(mount)\n\n      expect(config.split_path(request)).to eq([mount, \"bar/baz\"])\n    end\n\n    it \"should remove any double slashes\" do\n      allow(request).to receive(:key).and_return(\"foo/bar//baz\")\n      mount = double('mount', :name => \"foo\")\n      expect(config).to receive(:find_mount).and_return(mount)\n\n      expect(config.split_path(request)).to eq([mount, \"bar/baz\"])\n    end\n\n    it \"should fail if the path contains ..\" do\n      allow(request).to receive(:key).and_return('module/foo/../../bar')\n\n      expect do\n        config.split_path(request)\n      end.to raise_error(ArgumentError, /Invalid relative path/)\n    end\n\n    it \"should return the relative path as nil if it is an empty string\" do\n      expect(request).to receive(:key).and_return(\"foo\")\n      mount = double('mount', :name => \"foo\")\n      expect(config).to receive(:find_mount).and_return(mount)\n\n      expect(config.split_path(request)).to eq([mount, nil])\n    end\n\n    it \"should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility\" do\n      expect(request).to receive(:key).and_return(\"foo/bar\")\n      mount = double('mount', :name => \"modules\")\n      expect(config).to receive(:find_mount).and_return(mount)\n\n      expect(config.split_path(request)).to eq([mount, \"foo/bar\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/content_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/content'\n\ndescribe Puppet::FileServing::Content do\n  let(:path) { File.expand_path('/path') }\n\n  it \"should be a subclass of Base\" do\n    expect(Puppet::FileServing::Content.superclass).to equal(Puppet::FileServing::Base)\n  end\n\n  it \"should indirect file_content\" do\n    expect(Puppet::FileServing::Content.indirection.name).to eq(:file_content)\n  end\n\n  it \"should only support the binary format\" do\n    expect(Puppet::FileServing::Content.supported_formats).to eq([:binary])\n  end\n\n  it \"should have a method for collecting its attributes\" do\n    expect(Puppet::FileServing::Content.new(path)).to respond_to(:collect)\n  end\n\n  it \"should not retrieve and store its contents when its attributes are collected\" do\n    content = Puppet::FileServing::Content.new(path)\n\n    expect(File).not_to receive(:read).with(path)\n    content.collect\n\n    expect(content.instance_variable_get(\"@content\")).to be_nil\n  end\n\n  it \"should have a method for setting its content\" do\n    content = Puppet::FileServing::Content.new(path)\n    expect(content).to respond_to(:content=)\n  end\n\n  it \"should make content available when set externally\" do\n    content = Puppet::FileServing::Content.new(path)\n    content.content = \"foo/bar\"\n    expect(content.content).to eq(\"foo/bar\")\n  end\n\n  it \"should be able to create a content instance from binary file contents\" do\n    expect(Puppet::FileServing::Content).to respond_to(:from_binary)\n  end\n\n  it \"should create an instance with a fake file name and correct content when converting from binary\" do\n    instance = double('instance')\n    expect(Puppet::FileServing::Content).to receive(:new).with(\"/this/is/a/fake/path\").and_return(instance)\n\n    expect(instance).to receive(:content=).with(\"foo/bar\")\n\n    expect(Puppet::FileServing::Content.from_binary(\"foo/bar\")).to equal(instance)\n  end\n\n  it \"should return an opened File when converted to binary\" do\n    content = Puppet::FileServing::Content.new(path)\n\n    expect(File).to receive(:new).with(path, \"rb\").and_return(:file)\n\n    expect(content.to_binary).to eq(:file)\n  end\nend\n\ndescribe Puppet::FileServing::Content, \"when returning the contents\" do\n  let(:path) { File.expand_path('/my/path') }\n  let(:content) { Puppet::FileServing::Content.new(path, :links => :follow) }\n\n  it \"should fail if the file is a symlink and links are set to :manage\" do\n    content.links = :manage\n    expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(double(\"stat\", :ftype => \"symlink\"))\n    expect { content.content }.to raise_error(ArgumentError)\n  end\n\n  it \"should fail if a path is not set\" do\n    expect { content.content }.to raise_error(Errno::ENOENT)\n  end\n\n  it \"should raise Errno::ENOENT if the file is absent\" do\n    content.path = File.expand_path(\"/there/is/absolutely/no/chance/that/this/path/exists\")\n    expect { content.content }.to raise_error(Errno::ENOENT)\n  end\n\n  it \"should return the contents of the path if the file exists\" do\n    expect(Puppet::FileSystem).to receive(:stat).with(path).and_return(double('stat', :ftype => 'file'))\n    expect(Puppet::FileSystem).to receive(:binread).with(path).and_return(:mycontent)\n    expect(content.content).to eq(:mycontent)\n  end\n\n  it \"should cache the returned contents\" do\n    expect(Puppet::FileSystem).to receive(:stat).with(path).and_return(double('stat', :ftype => 'file'))\n    expect(Puppet::FileSystem).to receive(:binread).with(path).and_return(:mycontent)\n    content.content\n    # The second run would throw a failure if the content weren't being cached.\n    content.content\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/fileset_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/fileset'\n\ndescribe Puppet::FileServing::Fileset do\n  include PuppetSpec::Files\n  let(:somefile) { make_absolute(\"/some/file\") }\n\n  context \"when initializing\" do\n    it \"requires a path\" do\n      expect { Puppet::FileServing::Fileset.new }.to raise_error(ArgumentError)\n    end\n\n    it \"fails if its path is not fully qualified\" do\n      expect { Puppet::FileServing::Fileset.new(\"some/file\") }.to raise_error(ArgumentError, \"Fileset paths must be fully qualified: some/file\")\n    end\n\n    it \"removes a trailing file path separator\" do\n      path_with_separator = \"#{somefile}#{File::SEPARATOR}\"\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      fileset = Puppet::FileServing::Fileset.new(path_with_separator)\n      expect(fileset.path).to eq(somefile)\n    end\n\n    it \"can be created from the root directory\" do\n      path = File.expand_path(File::SEPARATOR)\n      expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(double('stat'))\n      fileset = Puppet::FileServing::Fileset.new(path)\n      expect(fileset.path).to eq(path)\n    end\n\n    it \"fails if its path does not exist\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_raise(Errno::ENOENT)\n      expect { Puppet::FileServing::Fileset.new(somefile) }.to raise_error(ArgumentError, \"Fileset paths must exist\")\n    end\n\n    it \"accepts a 'recurse' option\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      set = Puppet::FileServing::Fileset.new(somefile, :recurse => true)\n      expect(set.recurse).to be_truthy\n    end\n\n    it \"accepts a 'recurselimit' option\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      set = Puppet::FileServing::Fileset.new(somefile, :recurselimit => 3)\n      expect(set.recurselimit).to eq(3)\n    end\n\n    it \"accepts a 'max_files' option\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      set = Puppet::FileServing::Fileset.new(somefile, :recurselimit => 3, :max_files => 100)\n      expect(set.recurselimit).to eq(3)\n      expect(set.max_files).to eq(100)\n    end\n\n    it \"accepts an 'ignore' option\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      set = Puppet::FileServing::Fileset.new(somefile, :ignore => \".svn\")\n      expect(set.ignore).to eq([\".svn\"])\n    end\n\n    it \"accepts a 'links' option\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      set = Puppet::FileServing::Fileset.new(somefile, :links => :manage)\n      expect(set.links).to eq(:manage)\n    end\n\n    it \"accepts a 'checksum_type' option\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      set = Puppet::FileServing::Fileset.new(somefile, :checksum_type => :test)\n      expect(set.checksum_type).to eq(:test)\n    end\n\n    it \"fails if 'links' is set to anything other than :manage or :follow\" do\n      expect { Puppet::FileServing::Fileset.new(somefile, :links => :whatever) }.to raise_error(ArgumentError, \"Invalid :links value 'whatever'\")\n    end\n\n    it \"defaults to 'false' for recurse\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      expect(Puppet::FileServing::Fileset.new(somefile).recurse).to eq(false)\n    end\n\n    it \"defaults to :infinite for recurselimit\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      expect(Puppet::FileServing::Fileset.new(somefile).recurselimit).to eq(:infinite)\n    end\n\n    it \"defaults to an empty ignore list\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      expect(Puppet::FileServing::Fileset.new(somefile).ignore).to eq([])\n    end\n\n    it \"defaults to :manage for links\" do\n      expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      expect(Puppet::FileServing::Fileset.new(somefile).links).to eq(:manage)\n    end\n\n    describe \"using an indirector request\" do\n      let(:values) { { :links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234 } }\n\n      before :each do\n        expect(Puppet::FileSystem).to receive(:lstat).with(somefile).and_return(double('stat'))\n      end\n\n      [:recurse, :recurselimit, :ignore, :links].each do |option|\n        it \"passes the #{option} option on to the fileset if present\" do\n          request = Puppet::Indirector::Request.new(:file_serving, :find, \"foo\", nil, {option => values[option]})\n\n          expect(Puppet::FileServing::Fileset.new(somefile, request).send(option)).to eq(values[option])\n        end\n      end\n\n      it \"converts the integer as a string to their integer counterpart when setting options\" do\n        request = Puppet::Indirector::Request.new(:file_serving, :find, \"foo\", nil,\n                                                  {:recurselimit => \"1234\"})\n\n        expect(Puppet::FileServing::Fileset.new(somefile, request).recurselimit).to eq(1234)\n      end\n\n      it \"converts the string 'true' to the boolean true when setting options\" do\n        request = Puppet::Indirector::Request.new(:file_serving, :find, \"foo\", nil,\n                                                  {:recurse => \"true\"})\n\n        expect(Puppet::FileServing::Fileset.new(somefile, request).recurse).to eq(true)\n      end\n\n      it \"converts the string 'false' to the boolean false when setting options\" do\n        request = Puppet::Indirector::Request.new(:file_serving, :find, \"foo\", nil,\n                                                  {:recurse => \"false\"})\n\n        expect(Puppet::FileServing::Fileset.new(somefile, request).recurse).to eq(false)\n      end\n    end\n  end\n\n  context \"when recursing\" do\n    before do\n      @path = make_absolute(\"/my/path\")\n      allow(Puppet::FileSystem).to receive(:lstat).with(@path).and_return(double('stat', :directory? => true))\n\n      @fileset = Puppet::FileServing::Fileset.new(@path)\n\n      @dirstat = double('dirstat', :directory? => true)\n      @filestat = double('filestat', :directory? => false)\n    end\n\n    def mock_dir_structure(path, stat_method = :lstat)\n      allow(Puppet::FileSystem).to receive(stat_method).with(path).and_return(@dirstat)\n\n      # Keep track of the files we're stubbing.\n      @files = %w{.}\n\n      top_names = %w{one two .svn CVS}\n      sub_names = %w{file1 file2 .svn CVS 0 false}\n\n      allow(Dir).to receive(:entries).with(path, {encoding: Encoding::UTF_8}).and_return(top_names)\n      top_names.each do |subdir|\n        @files << subdir # relative path\n        subpath = File.join(path, subdir)\n        allow(Puppet::FileSystem).to receive(stat_method).with(subpath).and_return(@dirstat)\n        allow(Dir).to receive(:entries).with(subpath, {encoding: Encoding::UTF_8}).and_return(sub_names)\n        sub_names.each do |file|\n          @files << File.join(subdir, file) # relative path\n          subfile_path = File.join(subpath, file)\n          allow(Puppet::FileSystem).to receive(stat_method).with(subfile_path).and_return(@filestat)\n        end\n      end\n    end\n\n    def mock_big_dir_structure(path, stat_method = :lstat)\n      allow(Puppet::FileSystem).to receive(stat_method).with(path).and_return(@dirstat)\n\n      # Keep track of the files we're stubbing.\n      @files = %w{.}\n\n      top_names = (1..10).map {|i| \"dir_#{i}\" }\n      sub_names = (1..100).map {|i| \"file__#{i}\" }\n\n      allow(Dir).to receive(:entries).with(path, {encoding: Encoding::UTF_8}).and_return(top_names)\n      top_names.each do |subdir|\n        @files << subdir # relative path\n        subpath = File.join(path, subdir)\n        allow(Puppet::FileSystem).to receive(stat_method).with(subpath).and_return(@dirstat)\n        allow(Dir).to receive(:entries).with(subpath, {encoding: Encoding::UTF_8}).and_return(sub_names)\n        sub_names.each do |file|\n          @files << File.join(subdir, file) # relative path\n          subfile_path = File.join(subpath, file)\n          allow(Puppet::FileSystem).to receive(stat_method).with(subfile_path).and_return(@filestat)\n        end\n      end\n    end\n\n    def setup_mocks_for_dir(mock_dir, base_path)\n      path = File.join(base_path, mock_dir.name)\n      allow(Puppet::FileSystem).to receive(:lstat).with(path).and_return(MockStat.new(path, true))\n      allow(Dir).to receive(:entries).with(path, {encoding: Encoding::UTF_8}).and_return(['.', '..'] + mock_dir.entries.map(&:name))\n      mock_dir.entries.each do |entry|\n        setup_mocks_for_entry(entry, path)\n      end\n    end\n\n    def setup_mocks_for_file(mock_file, base_path)\n      path = File.join(base_path, mock_file.name)\n      allow(Puppet::FileSystem).to receive(:lstat).with(path).and_return(MockStat.new(path, false))\n    end\n\n    def setup_mocks_for_entry(entry, base_path)\n      case entry\n      when MockDirectory\n        setup_mocks_for_dir(entry, base_path)\n      when MockFile\n        setup_mocks_for_file(entry, base_path)\n      end\n    end\n\n    MockStat = Struct.new(:path, :directory) do\n      # struct doesn't support thing ending in ?\n      def directory?\n        directory\n      end\n    end\n\n    MockDirectory = Struct.new(:name, :entries)\n    MockFile = Struct.new(:name)\n\n    it \"doesn't ignore pending directories when the last entry at the top level is a file\" do\n      structure = MockDirectory.new('path',\n                    [MockDirectory.new('dir1',\n                                   [MockDirectory.new('a', [MockFile.new('f')])]),\n                     MockFile.new('file')])\n      setup_mocks_for_dir(structure, make_absolute('/your'))\n      fileset = Puppet::FileServing::Fileset.new(make_absolute('/your/path'))\n      fileset.recurse = true\n      fileset.links = :manage\n      expect(fileset.files).to eq([\".\", \"dir1\", \"file\", \"dir1/a\", \"dir1/a/f\"])\n    end\n\n    it \"recurses through the whole file tree if :recurse is set to 'true'\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      expect(@fileset.files.sort).to eq(@files.sort)\n    end\n\n    it \"does not recurse if :recurse is set to 'false'\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = false\n      expect(@fileset.files).to eq(%w{.})\n    end\n\n    it \"recurses to the level set by :recurselimit\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.recurselimit = 1\n      expect(@fileset.files).to eq(%w{. one two .svn CVS})\n    end\n\n    it \"ignores the '.' and '..' directories in subdirectories\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      expect(@fileset.files.sort).to eq(@files.sort)\n    end\n\n    it \"does not fail if the :ignore value provided is nil\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.ignore = nil\n      expect { @fileset.files }.to_not raise_error\n    end\n\n    it \"ignores files that match a single pattern in the ignore list\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.ignore = \".svn\"\n      expect(@fileset.files.find { |file| file.include?(\".svn\") }).to be_nil\n    end\n\n    it \"ignores files that match any of multiple patterns in the ignore list\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.ignore = %w{.svn CVS}\n      expect(@fileset.files.find { |file| file.include?(\".svn\") or file.include?(\"CVS\") }).to be_nil\n    end\n\n    it \"ignores files that match a pattern given as a number\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.ignore = [0]\n      expect(@fileset.files.find { |file| file.include?(\"0\") }).to be_nil\n    end\n\n    it \"raises exception if number of files is greater than :max_files\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.max_files = 22\n      expect { @fileset.files }.to raise_error(Puppet::Error, \"The directory '#{@path}' contains 28 entries, which exceeds the limit of 22 specified by the max_files parameter for this resource. The limit may be increased, but be aware that large number of file resources can result in excessive resource consumption and degraded performance. Consider using an alternate method to manage large directory trees\")\n    end\n\n    it \"logs a warning if number of files is greater than soft max_files limit of 1000\" do\n      mock_big_dir_structure(@path)\n      @fileset.recurse = true\n      expect(Puppet).to receive(:warning).with(\"The directory '#{@path}' contains 1010 entries, which exceeds the default soft limit 1000 and may cause excessive resource consumption and degraded performance. To remove this warning set a value for `max_files` parameter or consider using an alternate method to manage large directory trees\")\n      expect { @fileset.files }.to_not raise_error\n    end\n\n    it \"does not emit a warning if max_files is -1\" do\n      mock_big_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.max_files = -1\n      expect(Puppet).to receive(:warning).never\n      @fileset.files\n    end\n\n    it \"does not emit a warning if max_files is `-1`(string)\" do\n      mock_big_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.max_files = '-1'\n      expect(Puppet).to receive(:warning).never\n      @fileset.files\n    end\n\n    it \"ignores files that match a pattern given as a boolean\" do\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      @fileset.ignore = [false]\n      expect(@fileset.files.find { |file| file.include?(\"false\") }).to be_nil\n    end\n\n    it \"uses Puppet::FileSystem#stat if :links is set to :follow\" do\n      mock_dir_structure(@path, :stat)\n      @fileset.recurse = true\n      @fileset.links = :follow\n      expect(@fileset.files.sort).to eq(@files.sort)\n    end\n\n    it \"uses Puppet::FileSystem#lstat if :links is set to :manage\" do\n      mock_dir_structure(@path, :lstat)\n      @fileset.recurse = true\n      @fileset.links = :manage\n      expect(@fileset.files.sort).to eq(@files.sort)\n    end\n\n    it \"works when paths have regexp significant characters\" do\n      @path = make_absolute(\"/my/path/rV1x2DafFr0R6tGG+1bbk++++TM\")\n      stat = double('dir_stat', :directory? => true)\n      expect(Puppet::FileSystem).to receive(:lstat).with(@path).and_return(double(@path, :stat => stat, :lstat => stat))\n      @fileset = Puppet::FileServing::Fileset.new(@path)\n      mock_dir_structure(@path)\n      @fileset.recurse = true\n      expect(@fileset.files.sort).to eq(@files.sort)\n    end\n  end\n\n  it \"manages the links to missing files\" do\n    path = make_absolute(\"/my/path\")\n    stat = double('stat', :directory? => true)\n\n    expect(Puppet::FileSystem).to receive(:stat).with(path).and_return(stat)\n    expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(stat)\n\n    link_path = File.join(path, \"mylink\")\n    expect(Puppet::FileSystem).to receive(:stat).with(link_path).and_raise(Errno::ENOENT)\n\n    allow(Dir).to receive(:entries).with(path, {encoding: Encoding::UTF_8}).and_return([\"mylink\"])\n\n    fileset = Puppet::FileServing::Fileset.new(path)\n\n    fileset.links = :follow\n    fileset.recurse = true\n\n    expect(fileset.files.sort).to eq(%w{. mylink}.sort)\n  end\n\n  context \"when merging other filesets\" do\n    before do\n      @paths = [make_absolute(\"/first/path\"), make_absolute(\"/second/path\"), make_absolute(\"/third/path\")]\n      allow(Puppet::FileSystem).to receive(:lstat).and_return(double('stat', :directory? => false))\n\n      @filesets = @paths.collect do |path|\n        allow(Puppet::FileSystem).to receive(:lstat).with(path).and_return(double('stat', :directory? => true))\n        Puppet::FileServing::Fileset.new(path, {:recurse => true})\n      end\n\n      allow(Dir).to receive(:entries).and_return([])\n    end\n\n    it \"returns a hash of all files in each fileset with the value being the base path\" do\n      expect(Dir).to receive(:entries).with(make_absolute(\"/first/path\"), {encoding: Encoding::UTF_8}).and_return(%w{one uno})\n      expect(Dir).to receive(:entries).with(make_absolute(\"/second/path\"), {encoding: Encoding::UTF_8}).and_return(%w{two dos})\n      expect(Dir).to receive(:entries).with(make_absolute(\"/third/path\"), {encoding: Encoding::UTF_8}).and_return(%w{three tres})\n\n      expect(Puppet::FileServing::Fileset.merge(*@filesets)).to eq({\n        \".\" => make_absolute(\"/first/path\"),\n        \"one\" => make_absolute(\"/first/path\"),\n        \"uno\" => make_absolute(\"/first/path\"),\n        \"two\" => make_absolute(\"/second/path\"),\n        \"dos\" => make_absolute(\"/second/path\"),\n        \"three\" => make_absolute(\"/third/path\"),\n        \"tres\" => make_absolute(\"/third/path\"),\n      })\n    end\n\n    it \"includes the base directory from the first fileset\" do\n      expect(Dir).to receive(:entries).with(make_absolute(\"/first/path\"), {encoding: Encoding::UTF_8}).and_return(%w{one})\n      expect(Dir).to receive(:entries).with(make_absolute(\"/second/path\"), {encoding: Encoding::UTF_8}).and_return(%w{two})\n\n      expect(Puppet::FileServing::Fileset.merge(*@filesets)[\".\"]).to eq(make_absolute(\"/first/path\"))\n    end\n\n    it \"uses the base path of the first found file when relative file paths conflict\" do\n      expect(Dir).to receive(:entries).with(make_absolute(\"/first/path\"), {encoding: Encoding::UTF_8}).and_return(%w{one})\n      expect(Dir).to receive(:entries).with(make_absolute(\"/second/path\"), {encoding: Encoding::UTF_8}).and_return(%w{one})\n\n      expect(Puppet::FileServing::Fileset.merge(*@filesets)[\"one\"]).to eq(make_absolute(\"/first/path\"))\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/file_serving/http_metadata_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/http_metadata'\nrequire 'matchers/json'\nrequire 'net/http'\nrequire 'digest'\n\ndescribe Puppet::FileServing::HttpMetadata do\n  let(:foobar) { File.expand_path('/foo/bar') }\n\n  it \"should be a subclass of Metadata\" do\n    expect( described_class.superclass ).to be Puppet::FileServing::Metadata\n  end\n\n  describe \"when initializing\" do\n    let(:http_response) { Net::HTTPOK.new(1.0, '200', 'OK') }\n\n    it \"can be instantiated from a HTTP response object\" do\n      expect( described_class.new(http_response) ).to_not be_nil\n    end\n\n    it \"represents a plain file\" do\n      expect( described_class.new(http_response).ftype ).to eq 'file'\n    end\n\n    it \"carries no information on owner, group and mode\" do\n      metadata = described_class.new(http_response)\n      expect( metadata.owner ).to be_nil\n      expect( metadata.group ).to be_nil\n      expect( metadata.mode ).to be_nil\n    end\n\n    it \"skips md5 checksum type in collect on FIPS enabled platforms\" do\n      allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n      http_response['X-Checksum-Md5'] = 'c58989e9740a748de4f5054286faf99b'\n      metadata = described_class.new(http_response)\n      metadata.collect\n      expect( metadata.checksum_type ).to eq :mtime\n    end\n\n    context \"with no Last-Modified or Content-MD5 header from the server\" do\n      it \"should use :mtime as the checksum type, based on current time\" do\n        # Stringifying Time.now does some rounding; do so here so we don't end up with a time\n        # that's greater than the stringified version returned by collect.\n        time = Time.parse(Time.now.to_s)\n        metadata = described_class.new(http_response)\n        metadata.collect\n        expect( metadata.checksum_type ).to eq :mtime\n        checksum = metadata.checksum\n        expect( checksum[0...7] ).to eq '{mtime}'\n        expect( Time.parse(checksum[7..-1]) ).to be >= time\n      end\n    end\n\n    context \"with a Last-Modified header from the server\" do\n      let(:time) { Time.now.utc }\n\n      it \"should use :mtime as the checksum type, based on Last-Modified\" do\n        # HTTP uses \"GMT\" not \"UTC\"\n        http_response.add_field('last-modified', time.strftime(\"%a, %d %b %Y %T GMT\"))\n        metadata = described_class.new(http_response)\n        metadata.collect\n        expect( metadata.checksum_type ).to eq :mtime\n        expect( metadata.checksum ).to eq \"{mtime}#{time.to_time.utc}\"\n      end\n    end\n\n    context \"with a Content-MD5 header being received\" do\n      let(:input) { Time.now.to_s }\n      let(:base64) { Digest::MD5.new.base64digest input }\n      let(:hex) { Digest::MD5.new.hexdigest input }\n\n      it \"should use the md5 checksum\" do\n        http_response.add_field('content-md5', base64)\n        metadata = described_class.new(http_response)\n        metadata.collect\n        expect( metadata.checksum_type ).to eq :md5\n        expect( metadata.checksum ).to eq \"{md5}#{hex}\"\n      end\n    end\n\n    context \"with X-Checksum-Md5\" do\n      let(:md5) { \"c58989e9740a748de4f5054286faf99b\" }\n\n      it \"should use the md5 checksum\" do\n        http_response.add_field('X-Checksum-Md5', md5)\n        metadata = described_class.new(http_response)\n        metadata.collect\n        expect( metadata.checksum_type ).to eq :md5\n        expect( metadata.checksum ).to eq \"{md5}#{md5}\"\n      end\n    end\n\n    context \"with X-Checksum-Sha1\" do\n      let(:sha1) { \"01e4d15746f4274b84d740a93e04b9fd2882e3ea\" }\n\n      it \"should use the SHA1 checksum\" do\n        http_response.add_field('X-Checksum-Sha1', sha1)\n        metadata = described_class.new(http_response)\n        metadata.collect\n        expect( metadata.checksum_type ).to eq :sha1\n        expect( metadata.checksum ).to eq \"{sha1}#{sha1}\"\n      end\n    end\n\n    context \"with X-Checksum-Sha256\" do\n      let(:sha256) { \"a3eda98259c30e1e75039c2123670c18105e1c46efb672e42ca0e4cbe77b002a\" }\n\n      it \"should use the SHA256 checksum\" do\n        http_response.add_field('X-Checksum-Sha256', sha256)\n        metadata = described_class.new(http_response)\n        metadata.collect\n        expect( metadata.checksum_type ).to eq :sha256\n        expect( metadata.checksum ).to eq \"{sha256}#{sha256}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/metadata_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/metadata'\nrequire 'matchers/json'\n\ndescribe Puppet::FileServing::Metadata do\n  let(:foobar) { File.expand_path('/foo/bar') }\n\n  it \"should be a subclass of Base\" do\n    expect(Puppet::FileServing::Metadata.superclass).to equal(Puppet::FileServing::Base)\n  end\n\n  it \"should indirect file_metadata\" do\n    expect(Puppet::FileServing::Metadata.indirection.name).to eq(:file_metadata)\n  end\n\n  it \"should have a method that triggers attribute collection\" do\n    expect(Puppet::FileServing::Metadata.new(foobar)).to respond_to(:collect)\n  end\n\n  it \"should default to json\" do\n    expect(Puppet::FileServing::Metadata.default_format).to eq(:json)\n  end\n\n  it \"should support json and yaml\" do\n    # msgpack and pson are optional, so using include instead of eq\n    expect(Puppet::FileServing::Metadata.supported_formats).to include(:json, :yaml)\n  end\n\n  it \"should support deserialization\" do\n    expect(Puppet::FileServing::Metadata).to respond_to(:from_data_hash)\n  end\n\n  describe \"when serializing\" do\n    let(:metadata) { Puppet::FileServing::Metadata.new(foobar) }\n\n    it \"the data should include the path, relative_path, links, owner, group, mode, checksum, type, and destination\" do\n      expect(metadata.to_data_hash.keys.sort).to eq(%w{ path relative_path links owner group mode checksum type destination }.sort)\n    end\n\n    it \"should pass the path in the hash verbatim\" do\n      expect(metadata.to_data_hash['path']).to eq(metadata.path)\n    end\n\n    it \"should pass the relative_path in the hash verbatim\" do\n      expect(metadata.to_data_hash['relative_path']).to eq(metadata.relative_path)\n    end\n\n    it \"should pass the links in the hash as a string\" do\n      expect(metadata.to_data_hash['links']).to eq(metadata.links.to_s)\n    end\n\n    it \"should pass the path owner in the hash verbatim\" do\n      expect(metadata.to_data_hash['owner']).to eq(metadata.owner)\n    end\n\n    it \"should pass the group in the hash verbatim\" do\n      expect(metadata.to_data_hash['group']).to eq(metadata.group)\n    end\n\n    it \"should pass the mode in the hash verbatim\" do\n      expect(metadata.to_data_hash['mode']).to eq(metadata.mode)\n    end\n\n    it \"should pass the ftype in the hash verbatim as the 'type'\" do\n      expect(metadata.to_data_hash['type']).to eq(metadata.ftype)\n    end\n\n    it \"should pass the destination verbatim\" do\n      expect(metadata.to_data_hash['destination']).to eq(metadata.destination)\n    end\n\n    it \"should pass the checksum in the hash as a nested hash\" do\n      expect(metadata.to_data_hash['checksum']).to be_is_a(Hash)\n    end\n\n    it \"should pass the checksum_type in the hash verbatim as the checksum's type\" do\n      expect(metadata.to_data_hash['checksum']['type']).to eq(metadata.checksum_type)\n    end\n\n    it \"should pass the checksum in the hash verbatim as the checksum's value\" do\n      expect(metadata.to_data_hash['checksum']['value']).to eq(metadata.checksum)\n    end\n\n    describe \"when a source and content_uri are set\" do\n      before do\n        metadata.source = '/foo'\n        metadata.content_uri = 'puppet:///foo'\n      end\n\n      it \"the data should include the path, relative_path, links, owner, group, mode, checksum, type, destination, source, and content_uri\" do\n        expect(metadata.to_data_hash.keys.sort).to eq(%w{ path relative_path links owner group mode checksum type destination source content_uri }.sort)\n      end\n\n      it \"should pass the source in the hash verbatim\" do\n        expect(metadata.to_data_hash['source']).to eq(metadata.source)\n      end\n\n      it \"should pass the content_uri in the hash verbatim\" do\n        expect(metadata.to_data_hash['content_uri']).to eq(metadata.content_uri)\n      end\n    end\n\n    describe \"when assigning a content_uri\" do\n      it \"should fail if uri is invalid\" do\n        expect { metadata.content_uri = '://' }.to raise_error ArgumentError, /Could not understand URI :\\/\\//\n      end\n\n      it \"should accept characters that require percent-encoding\" do\n        uri = 'puppet:///modules/foo/files/ %:?#[]@!$&\\'()*+,;='\n        metadata.content_uri = uri\n        expect(metadata.content_uri).to eq(uri)\n      end\n\n      it \"should accept UTF-8 characters\" do\n        # different UTF-8 widths\n        # 1-byte A\n        # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n        # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n        # 4-byte <U+070E> - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n        mixed_utf8 = \"A\\u06FF\\u16A0\\u{2070E}\" # Aۿᚠ<U+070E>\n\n        uri = \"puppet:///modules/foo/files/ #{mixed_utf8}\"\n        metadata.content_uri = uri\n        expect(metadata.content_uri).to eq(uri)\n        expect(metadata.content_uri.encoding).to eq(Encoding::UTF_8)\n      end\n\n      it \"should always set it as UTF-8\" do\n        uri = \"puppet:///modules/foo/files/\".encode(Encoding::ASCII)\n        metadata.content_uri = uri\n        expect(metadata.content_uri).to eq(uri)\n        expect(metadata.content_uri.encoding).to eq(Encoding::UTF_8)\n      end\n\n      it \"should fail if uri is opaque\" do\n        expect { metadata.content_uri = 'scheme:www.example.com' }.to raise_error ArgumentError, \"Cannot use opaque URLs 'scheme:www.example.com'\"\n      end\n\n      it \"should fail if uri is not a puppet scheme\" do\n        expect { metadata.content_uri = 'http://www.example.com' }.to raise_error ArgumentError, \"Must use URLs of type puppet as content URI\"\n      end\n    end\n  end\nend\n\ndescribe Puppet::FileServing::Metadata, :uses_checksums => true do\n  include JSONMatchers\n  include PuppetSpec::Files\n\n  shared_examples_for \"metadata collector\" do\n    let(:metadata) do\n      data = described_class.new(path)\n      data.collect\n      data\n    end\n\n    describe \"when collecting attributes\" do\n      describe \"when managing files\" do\n        let(:path) { tmpfile('file_serving_metadata') }\n        let(:time) { Time.now }\n\n        before :each do\n          FileUtils.touch(path)\n        end\n\n        describe \"checksumming\" do\n          with_digest_algorithms do\n            before :each do\n              File.open(path, \"wb\") {|f| f.print(plaintext)}\n            end\n\n            it \"should default to a checksum of the proper type with the file's current checksum\" do\n              expect(metadata.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n            end\n\n            it \"should give a #{Puppet[:digest_algorithm]} when checksum_type is set\" do\n              Puppet[:digest_algorithm] = nil\n              metadata.checksum_type = digest_algorithm\n              metadata.collect\n              expect(metadata.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n            end\n          end\n\n          it \"should give a mtime checksum when checksum_type is set\" do\n            metadata.checksum_type = \"mtime\"\n            expect(metadata).to receive(:mtime_file).and_return(time)\n            metadata.collect\n            expect(metadata.checksum).to eq(\"{mtime}#{time}\")\n          end\n\n          it \"should give a ctime checksum when checksum_type is set\" do\n            metadata.checksum_type = \"ctime\"\n            expect(metadata).to receive(:ctime_file).and_return(time)\n            metadata.collect\n            expect(metadata.checksum).to eq(\"{ctime}#{time}\")\n          end\n        end\n\n        it \"should validate against the schema\" do\n          expect(metadata.to_json).to validate_against('api/schemas/file_metadata.json')\n        end\n\n        describe \"when a source and content_uri are set\" do\n          before do\n            metadata.source = '/foo'\n            metadata.content_uri = 'puppet:///foo'\n          end\n\n          it \"should validate against the schema\" do\n            expect(metadata.to_json).to validate_against('api/schemas/file_metadata.json')\n          end\n        end\n      end\n\n      describe \"when managing directories\" do\n        let(:path) { tmpdir('file_serving_metadata_dir') }\n        let(:time) { Time.now }\n\n        before :each do\n          expect(metadata).to receive(:ctime_file).and_return(time)\n        end\n\n        it \"should only use checksums of type 'ctime' for directories\" do\n          metadata.collect\n          expect(metadata.checksum).to eq(\"{ctime}#{time}\")\n        end\n\n        it \"should only use checksums of type 'ctime' for directories even if checksum_type set\" do\n          metadata.checksum_type = \"mtime\"\n          expect(metadata).not_to receive(:mtime_file)\n          metadata.collect\n          expect(metadata.checksum).to eq(\"{ctime}#{time}\")\n        end\n\n        it \"should validate against the schema\" do\n          metadata.collect\n          expect(metadata.to_json).to validate_against('api/schemas/file_metadata.json')\n        end\n      end\n    end\n  end\n\n  describe \"WindowsStat\", :if => Puppet::Util::Platform.windows? do\n    include PuppetSpec::Files\n\n    it \"should return default owner, group and mode when the given path has an invalid DACL (such as a non-NTFS volume)\" do\n      invalid_error = Puppet::Util::Windows::Error.new('Invalid DACL', 1336)\n      path = tmpfile('foo')\n      FileUtils.touch(path)\n\n      allow(Puppet::Util::Windows::Security).to receive(:get_owner).with(path).and_raise(invalid_error)\n      allow(Puppet::Util::Windows::Security).to receive(:get_group).with(path).and_raise(invalid_error)\n      allow(Puppet::Util::Windows::Security).to receive(:get_mode).with(path).and_raise(invalid_error)\n\n      stat = Puppet::FileSystem.stat(path)\n\n      win_stat = Puppet::FileServing::Metadata::WindowsStat.new(stat, path, :ignore)\n\n      expect(win_stat.owner).to eq('S-1-5-32-544')\n      expect(win_stat.group).to eq('S-1-0-0')\n      expect(win_stat.mode).to eq(0644)\n    end\n\n    it \"should still raise errors that are not the result of an 'Invalid DACL'\" do\n      invalid_error = ArgumentError.new('bar')\n      path = tmpfile('bar')\n      FileUtils.touch(path)\n\n      allow(Puppet::Util::Windows::Security).to receive(:get_owner).with(path).and_raise(invalid_error)\n      allow(Puppet::Util::Windows::Security).to receive(:get_group).with(path).and_raise(invalid_error)\n      allow(Puppet::Util::Windows::Security).to receive(:get_mode).with(path).and_raise(invalid_error)\n\n      stat = Puppet::FileSystem.stat(path)\n\n      expect { Puppet::FileServing::Metadata::WindowsStat.new(stat, path, :use) }.to raise_error(\"Unsupported Windows source permissions option use\")\n    end\n  end\n\n  shared_examples_for \"metadata collector symlinks\" do\n\n    let(:metadata) do\n      data = described_class.new(path)\n      data.collect\n      data\n    end\n\n    describe \"when collecting attributes\" do\n      describe \"when managing links\" do\n        # 'path' is a link that points to 'target'\n        let(:path) { tmpfile('file_serving_metadata_link') }\n        let(:target) { tmpfile('file_serving_metadata_target') }\n        let(:fmode) { Puppet::FileSystem.lstat(path).mode & 0777 }\n\n        before :each do\n          File.open(target, \"wb\") {|f| f.print('some content')}\n          set_mode(0644, target)\n\n          Puppet::FileSystem.symlink(target, path)\n        end\n\n        it \"should read links instead of returning their checksums\" do\n          expect(metadata.destination).to eq(target)\n        end\n\n        it \"should validate against the schema\" do\n          expect(metadata.to_json).to validate_against('api/schemas/file_metadata.json')\n        end\n      end\n    end\n\n    describe Puppet::FileServing::Metadata, \" when finding the file to use for setting attributes\" do\n      let(:path) { tmpfile('file_serving_metadata_find_file') }\n\n      before :each do\n        File.open(path, \"wb\") {|f| f.print('some content')}\n        set_mode(0755, path)\n      end\n\n      it \"should accept a base path to which the file should be relative\" do\n        dir = tmpdir('metadata_dir')\n        metadata = described_class.new(dir)\n        metadata.relative_path = 'relative_path'\n\n        FileUtils.touch(metadata.full_path)\n\n        metadata.collect\n      end\n\n      it \"should use the set base path if one is not provided\" do\n        metadata.collect\n      end\n\n      it \"should raise an exception if the file does not exist\" do\n        File.delete(path)\n\n        expect { metadata.collect}.to raise_error(Errno::ENOENT)\n      end\n\n      it \"should validate against the schema\" do\n        expect(metadata.to_json).to validate_against('api/schemas/file_metadata.json')\n      end\n    end\n  end\n\n  describe \"on POSIX systems\", :if => Puppet.features.posix? do\n    let(:owner) {10}\n    let(:group) {20}\n\n    before :each do\n      allow_any_instance_of(File::Stat).to receive(:uid).and_return(owner)\n      allow_any_instance_of(File::Stat).to receive(:gid).and_return(group)\n    end\n\n    describe \"when collecting attributes when managing files\" do\n      let(:metadata) do\n        data = described_class.new(path)\n        data.collect\n        data\n      end\n\n      let(:path) { tmpfile('file_serving_metadata') }\n\n      before :each do\n        FileUtils.touch(path)\n      end\n\n      it \"should set the owner to the Process's current owner\" do\n        expect(metadata.owner).to eq(Process.euid)\n      end\n\n      it \"should set the group to the Process's current group\" do\n        expect(metadata.group).to eq(Process.egid)\n      end\n\n      it \"should set the mode to the default mode\" do\n        set_mode(33261, path)\n\n        expect(metadata.mode).to eq(0644)\n      end\n    end\n\n    it_should_behave_like \"metadata collector\"\n    it_should_behave_like \"metadata collector symlinks\"\n\n    def set_mode(mode, path)\n      File.chmod(mode, path)\n    end\n  end\n\n  describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n    let(:owner) {'S-1-1-50'}\n    let(:group) {'S-1-1-51'}\n\n    before :each do\n      require 'puppet/util/windows/security'\n      allow(Puppet::Util::Windows::Security).to receive(:get_owner).and_return(owner)\n      allow(Puppet::Util::Windows::Security).to receive(:get_group).and_return(group)\n    end\n\n    describe \"when collecting attributes when managing files\" do\n      let(:metadata) do\n        data = described_class.new(path)\n        data.collect\n        data\n      end\n\n      let(:path) { tmpfile('file_serving_metadata') }\n\n      before :each do\n        FileUtils.touch(path)\n      end\n\n      it \"should set the owner to the Process's current owner\" do\n        expect(metadata.owner).to eq(\"S-1-5-32-544\")\n      end\n\n      it \"should set the group to the Process's current group\" do\n        expect(metadata.group).to eq(\"S-1-0-0\")\n      end\n\n      it \"should set the mode to the default mode\" do\n        set_mode(33261, path)\n\n        expect(metadata.mode).to eq(0644)\n      end\n    end\n\n    it_should_behave_like \"metadata collector\"\n    it_should_behave_like \"metadata collector symlinks\" if Puppet.features.manages_symlinks?\n\n    describe \"if ACL metadata cannot be collected\" do\n      let(:path) { tmpdir('file_serving_metadata_acl') }\n      let(:metadata) do\n        data = described_class.new(path)\n        data.collect\n        data\n      end\n      let (:invalid_dacl_error) do\n        Puppet::Util::Windows::Error.new('Invalid DACL', 1336)\n      end\n\n      it \"should default owner\" do\n        allow(Puppet::Util::Windows::Security).to receive(:get_owner).and_return(nil)\n\n        expect(metadata.owner).to eq('S-1-5-32-544')\n      end\n\n      it \"should default group\" do\n        allow(Puppet::Util::Windows::Security).to receive(:get_group).and_return(nil)\n\n        expect(metadata.group).to eq('S-1-0-0')\n      end\n\n      it \"should default mode\" do\n        allow(Puppet::Util::Windows::Security).to receive(:get_mode).and_return(nil)\n\n        expect(metadata.mode).to eq(0644)\n      end\n\n      describe \"when the path raises an Invalid ACL error\" do\n        # these simulate the behavior of a symlink file whose target does not support ACLs\n        it \"should default owner\" do\n          allow(Puppet::Util::Windows::Security).to receive(:get_owner).and_raise(invalid_dacl_error)\n\n          expect(metadata.owner).to eq('S-1-5-32-544')\n        end\n\n        it \"should default group\" do\n          allow(Puppet::Util::Windows::Security).to receive(:get_group).and_raise(invalid_dacl_error)\n\n          expect(metadata.group).to eq('S-1-0-0')\n        end\n\n        it \"should default mode\" do\n          allow(Puppet::Util::Windows::Security).to receive(:get_mode).and_raise(invalid_dacl_error)\n\n          expect(metadata.mode).to eq(0644)\n        end\n      end\n    end\n\n    def set_mode(mode, path)\n      Puppet::Util::Windows::Security.set_mode(mode, path)\n    end\n  end\nend\n\ndescribe Puppet::FileServing::Metadata, \" when pointing to a link\", :if => Puppet.features.manages_symlinks?, :uses_checksums => true do\n  with_digest_algorithms do\n    describe \"when links are managed\" do\n      before do\n        path = \"/base/path/my/file\"\n        @file = Puppet::FileServing::Metadata.new(path, :links => :manage)\n        stat = double(\"stat\", :uid => 1, :gid => 2, :ftype => \"link\", :mode => 0755)\n        expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(stat)\n        expect(Puppet::FileSystem).to receive(:readlink).with(path).and_return(\"/some/other/path\")\n        allow(@file).to receive(\"#{digest_algorithm}_file\".intern).and_return(checksum) # Remove these when :managed links are no longer checksumed.\n\n        if Puppet::Util::Platform.windows?\n          win_stat = double('win_stat', :owner => 'snarf', :group => 'thundercats',\n            :ftype => 'link', :mode => 0755)\n          allow(Puppet::FileServing::Metadata::WindowsStat).to receive(:new).and_return(win_stat)\n        end\n      end\n\n      it \"should store the destination of the link in :destination if links are :manage\" do\n        @file.collect\n        expect(@file.destination).to eq(\"/some/other/path\")\n      end\n\n      pending \"should not collect the checksum if links are :manage\" do\n        # We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow.\n        @file.collect\n        expect(@file.checksum).to be_nil\n      end\n\n      it \"should collect the checksum if links are :manage\" do # see pending note above\n        @file.collect\n        expect(@file.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n      end\n    end\n\n    describe \"when links are followed\" do\n      before do\n        path = \"/base/path/my/file\"\n        @file = Puppet::FileServing::Metadata.new(path, :links => :follow)\n        stat = double(\"stat\", :uid => 1, :gid => 2, :ftype => \"file\", :mode => 0755)\n        expect(Puppet::FileSystem).to receive(:stat).with(path).and_return(stat)\n        expect(Puppet::FileSystem).not_to receive(:readlink)\n\n        if Puppet::Util::Platform.windows?\n          win_stat = double('win_stat', :owner => 'snarf', :group => 'thundercats',\n            :ftype => 'file', :mode => 0755)\n          allow(Puppet::FileServing::Metadata::WindowsStat).to receive(:new).and_return(win_stat)\n        end\n\n        allow(@file).to receive(\"#{digest_algorithm}_file\".intern).and_return(checksum)\n      end\n\n      it \"should not store the destination of the link in :destination if links are :follow\" do\n        @file.collect\n        expect(@file.destination).to be_nil\n      end\n\n      it \"should collect the checksum if links are :follow\" do\n        @file.collect\n        expect(@file.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/file'\n\nmodule FileServingMountTesting\n  def stub_facter(hostname)\n    allow(Facter).to receive(:value).with(\"networking.hostname\").and_return(hostname.sub(/\\..+/, ''))\n    allow(Facter).to receive(:value).with(\"networking.domain\").and_return(hostname.sub(/^[^.]+\\./, ''))\n  end\nend\n\ndescribe Puppet::FileServing::Mount::File do\n  it \"should be invalid if it does not have a path\" do\n    expect { Puppet::FileServing::Mount::File.new(\"foo\").validate }.to raise_error(ArgumentError)\n  end\n\n  it \"should be valid if it has a path\" do\n    allow(FileTest).to receive(:directory?).and_return(true)\n    allow(FileTest).to receive(:readable?).and_return(true)\n    mount = Puppet::FileServing::Mount::File.new(\"foo\")\n    mount.path = \"/foo\"\n    expect { mount.validate }.not_to raise_error\n  end\n\n  describe \"when setting the path\" do\n    before do\n      @mount = Puppet::FileServing::Mount::File.new(\"test\")\n      @dir = \"/this/path/does/not/exist\"\n    end\n\n    it \"should fail if the path is not a directory\" do\n      expect(FileTest).to receive(:directory?).and_return(false)\n      expect { @mount.path = @dir }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if the path is not readable\" do\n      expect(FileTest).to receive(:directory?).and_return(true)\n      expect(FileTest).to receive(:readable?).and_return(false)\n      expect { @mount.path = @dir }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"when substituting hostnames and ip addresses into file paths\" do\n    include FileServingMountTesting\n\n    before do\n      allow(FileTest).to receive(:directory?).and_return(true)\n      allow(FileTest).to receive(:readable?).and_return(true)\n      @mount = Puppet::FileServing::Mount::File.new(\"test\")\n      @host = \"host.domain.com\"\n    end\n\n    after :each do\n      Puppet::FileServing::Mount::File.instance_variable_set(:@localmap, nil)\n    end\n\n    it \"should replace incidences of %h in the path with the client's short name\" do\n      @mount.path = \"/dir/%h/yay\"\n      expect(@mount.path(@host)).to eq(\"/dir/host/yay\")\n    end\n\n    it \"should replace incidences of %H in the path with the client's fully qualified name\" do\n      @mount.path = \"/dir/%H/yay\"\n      expect(@mount.path(@host)).to eq(\"/dir/host.domain.com/yay\")\n    end\n\n    it \"should replace incidences of %d in the path with the client's domain name\" do\n      @mount.path = \"/dir/%d/yay\"\n      expect(@mount.path(@host)).to eq(\"/dir/domain.com/yay\")\n    end\n\n    it \"should perform all necessary replacements\" do\n      @mount.path = \"/%h/%d/%H\"\n      expect(@mount.path(@host)).to eq(\"/host/domain.com/host.domain.com\")\n    end\n\n    it \"should use local host information if no client data is provided\" do\n      stub_facter(\"myhost.mydomain.com\")\n      @mount.path = \"/%h/%d/%H\"\n      expect(@mount.path).to eq(\"/myhost/mydomain.com/myhost.mydomain.com\")\n    end\n  end\n\n  describe \"when determining the complete file path\" do\n    include FileServingMountTesting\n\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(FileTest).to receive(:directory?).and_return(true)\n      allow(FileTest).to receive(:readable?).and_return(true)\n      @mount = Puppet::FileServing::Mount::File.new(\"test\")\n      @mount.path = \"/mount\"\n      stub_facter(\"myhost.mydomain.com\")\n      @host = \"host.domain.com\"\n    end\n\n    it \"should return nil if the file is absent\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n      expect(@mount.complete_path(\"/my/path\", nil)).to be_nil\n    end\n\n    it \"should write a log message if the file is absent\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n\n      expect(Puppet).to receive(:info).with(\"File does not exist or is not accessible: /mount/my/path\")\n\n      @mount.complete_path(\"/my/path\", nil)\n    end\n\n    it \"should return the file path if the file is present\" do\n      allow(Puppet::FileSystem).to receive(:exist?).with(\"/my/path\").and_return(true)\n      expect(@mount.complete_path(\"/my/path\", nil)).to eq(\"/mount/my/path\")\n    end\n\n    it \"should treat a nil file name as the path to the mount itself\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      expect(@mount.complete_path(nil, nil)).to eq(\"/mount\")\n    end\n\n    it \"should use the client host name if provided in the options\" do\n      @mount.path = \"/mount/%h\"\n      expect(@mount.complete_path(\"/my/path\", @host)).to eq(\"/mount/host/my/path\")\n    end\n\n    it \"should perform replacements on the base path\" do\n      @mount.path = \"/blah/%h\"\n      expect(@mount.complete_path(\"/my/stuff\", @host)).to eq(\"/blah/host/my/stuff\")\n    end\n\n    it \"should not perform replacements on the per-file path\" do\n      @mount.path = \"/blah\"\n      expect(@mount.complete_path(\"/%h/stuff\", @host)).to eq(\"/blah/%h/stuff\")\n    end\n\n    it \"should look for files relative to its base directory\" do\n      expect(@mount.complete_path(\"/my/stuff\", @host)).to eq(\"/mount/my/stuff\")\n    end\n  end\n\n  describe \"when finding files\" do\n    include FileServingMountTesting\n\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(FileTest).to receive(:directory?).and_return(true)\n      allow(FileTest).to receive(:readable?).and_return(true)\n      @mount = Puppet::FileServing::Mount::File.new(\"test\")\n      @mount.path = \"/mount\"\n      stub_facter(\"myhost.mydomain.com\")\n      @host = \"host.domain.com\"\n\n      @request = double('request', :node => \"foo\")\n    end\n\n    it \"should return the results of the complete file path\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n      expect(@mount).to receive(:complete_path).with(\"/my/path\", \"foo\").and_return(\"eh\")\n      expect(@mount.find(\"/my/path\", @request)).to eq(\"eh\")\n    end\n  end\n\n  describe \"when searching for files\" do\n    include FileServingMountTesting\n\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(FileTest).to receive(:directory?).and_return(true)\n      allow(FileTest).to receive(:readable?).and_return(true)\n      @mount = Puppet::FileServing::Mount::File.new(\"test\")\n      @mount.path = \"/mount\"\n      stub_facter(\"myhost.mydomain.com\")\n      @host = \"host.domain.com\"\n\n      @request = double('request', :node => \"foo\")\n    end\n\n    it \"should return the results of the complete file path as an array\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n      expect(@mount).to receive(:complete_path).with(\"/my/path\", \"foo\").and_return(\"eh\")\n      expect(@mount.search(\"/my/path\", @request)).to eq([\"eh\"])\n    end\n\n    it \"should return nil if the complete path is nil\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n      expect(@mount).to receive(:complete_path).with(\"/my/path\", \"foo\").and_return(nil)\n      expect(@mount.search(\"/my/path\", @request)).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/locales_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/locales'\n\ndescribe Puppet::FileServing::Mount::Locales do\n  before do\n    @mount = Puppet::FileServing::Mount::Locales.new(\"locales\")\n\n    @environment = double('environment', :module => nil)\n    @options = { :recurse => true }\n    @request = double('request', :environment => @environment, :options => @options)\n  end\n\n  describe  \"when finding files\" do\n    it \"should use the provided environment to find the modules\" do\n      expect(@environment).to receive(:modules).and_return([])\n\n      @mount.find(\"foo\", @request)\n    end\n\n    it \"should return nil if no module can be found with a matching locale\" do\n      mod = double('module')\n      allow(mod).to receive(:locale).with(\"foo/bar\").and_return(nil)\n\n      allow(@environment).to receive(:modules).and_return([mod])\n      expect(@mount.find(\"foo/bar\", @request)).to be_nil\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      allow(mod).to receive(:locale).with(\"foo/bar\").and_return(\"eh\")\n\n      allow(@environment).to receive(:modules).and_return([mod])\n      expect(@mount.find(\"foo/bar\", @request)).to eq(\"eh\")\n    end\n  end\n\n  describe \"when searching for files\" do\n    it \"should use the node's environment to find the modules\" do\n      expect(@environment).to receive(:modules).at_least(:once).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([\"/tmp/modules\"])\n\n      @mount.search(\"foo\", @request)\n    end\n\n    it \"should return modulepath if no modules can be found that have locales\" do\n      mod = double('module')\n      allow(mod).to receive(:locales?).and_return(false)\n\n      allow(@environment).to receive(:modules).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([\"/\"])\n      expect(@options).to receive(:[]=).with(:recurse, false)\n      expect(@mount.search(\"foo/bar\", @request)).to eq([\"/\"])\n    end\n\n    it \"should return the default search module path if no modules can be found that have locales and modulepath is invalid\" do\n      mod = double('module')\n      allow(mod).to receive(:locales?).and_return(false)\n\n      allow(@environment).to receive(:modules).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([])\n      expect(@mount.search(\"foo/bar\", @request)).to eq([Puppet[:codedir]])\n    end\n\n    it \"should return the locale paths for each module that has locales\" do\n      one = double('module', :locales? => true, :locale_directory => \"/one\")\n      two = double('module', :locales? => true, :locale_directory => \"/two\")\n\n      allow(@environment).to receive(:modules).and_return([one, two])\n      expect(@mount.search(\"foo/bar\", @request)).to eq(%w{/one /two})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/modules_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/modules'\n\ndescribe Puppet::FileServing::Mount::Modules do\n  before do\n    @mount = Puppet::FileServing::Mount::Modules.new(\"modules\")\n\n    @environment = double('environment', :module => nil)\n    @request = double('request', :environment => @environment)\n  end\n\n  describe \"when finding files\" do\n    it \"should fail if no module is specified\" do\n      expect { @mount.find(\"\", @request) }.to raise_error(/No module specified/)\n    end\n\n    it \"should use the provided environment to find the module\" do\n      expect(@environment).to receive(:module)\n\n      @mount.find(\"foo\", @request)\n    end\n\n    it \"should treat the first field of the relative path as the module name\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.find(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return nil if the specified module does not exist\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.find(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      expect(mod).to receive(:file).with(\"bar/baz\").and_return(\"eh\")\n      expect(@environment).to receive(:module).with(\"foo\").and_return(mod)\n      expect(@mount.find(\"foo/bar/baz\", @request)).to eq(\"eh\")\n    end\n  end\n\n  describe \"when searching for files\" do\n    it \"should fail if no module is specified\" do\n      expect { @mount.search(\"\", @request) }.to raise_error(/No module specified/)\n    end\n\n    it \"should use the node's environment to search the module\" do\n      expect(@environment).to receive(:module)\n\n      @mount.search(\"foo\", @request)\n    end\n\n    it \"should treat the first field of the relative path as the module name\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.search(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return nil if the specified module does not exist\" do\n      expect(@environment).to receive(:module).with(\"foo\").and_return(nil)\n      @mount.search(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return the file path as an array from the module\" do\n      mod = double('module')\n      expect(mod).to receive(:file).with(\"bar/baz\").and_return(\"eh\")\n      expect(@environment).to receive(:module).with(\"foo\").and_return(mod)\n      expect(@mount.search(\"foo/bar/baz\", @request)).to eq([\"eh\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/pluginfacts_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/pluginfacts'\n\ndescribe Puppet::FileServing::Mount::PluginFacts do\n  before do\n    @mount = Puppet::FileServing::Mount::PluginFacts.new(\"pluginfacts\")\n\n    @environment = double('environment', :module => nil)\n    @options = { :recurse => true }\n    @request = double('request', :environment => @environment, :options => @options)\n  end\n\n  describe  \"when finding files\" do\n    it \"should use the provided environment to find the modules\" do\n      expect(@environment).to receive(:modules).and_return([])\n\n      @mount.find(\"foo\", @request)\n    end\n\n    it \"should return nil if no module can be found with a matching plugin\" do\n      mod = double('module')\n      allow(mod).to receive(:pluginfact).with(\"foo/bar\").and_return(nil)\n\n      allow(@environment).to receive(:modules).and_return([mod])\n      expect(@mount.find(\"foo/bar\", @request)).to be_nil\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      allow(mod).to receive(:pluginfact).with(\"foo/bar\").and_return(\"eh\")\n\n      allow(@environment).to receive(:modules).and_return([mod])\n      expect(@mount.find(\"foo/bar\", @request)).to eq(\"eh\")\n    end\n  end\n\n  describe \"when searching for files\" do\n    it \"should use the node's environment to find the modules\" do\n      expect(@environment).to receive(:modules).at_least(:once).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([\"/tmp/modules\"])\n\n      @mount.search(\"foo\", @request)\n    end\n\n    it \"should return modulepath if no modules can be found that have plugins\" do\n      mod = double('module')\n      allow(mod).to receive(:pluginfacts?).and_return(false)\n\n      allow(@environment).to receive(:modules).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([\"/\"])\n      expect(@options).to receive(:[]=).with(:recurse, false)\n      expect(@mount.search(\"foo/bar\", @request)).to eq([\"/\"])\n    end\n\n    it \"should return the default search module path if no modules can be found that have plugins and modulepath is invalid\" do\n      mod = double('module')\n      allow(mod).to receive(:pluginfacts?).and_return(false)\n\n      allow(@environment).to receive(:modules).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([])\n      expect(@mount.search(\"foo/bar\", @request)).to eq([Puppet[:codedir]])\n    end\n\n    it \"should return the plugin paths for each module that has plugins\" do\n      one = double('module', :pluginfacts? => true, :plugin_fact_directory => \"/one\")\n      two = double('module', :pluginfacts? => true, :plugin_fact_directory => \"/two\")\n\n      allow(@environment).to receive(:modules).and_return([one, two])\n      expect(@mount.search(\"foo/bar\", @request)).to eq(%w{/one /two})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/plugins_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/plugins'\n\ndescribe Puppet::FileServing::Mount::Plugins do\n  before do\n    @mount = Puppet::FileServing::Mount::Plugins.new(\"plugins\")\n\n    @environment = double('environment', :module => nil)\n    @options = { :recurse => true }\n    @request = double('request', :environment => @environment, :options => @options)\n  end\n\n  describe  \"when finding files\" do\n    it \"should use the provided environment to find the modules\" do\n      expect(@environment).to receive(:modules).and_return([])\n\n      @mount.find(\"foo\", @request)\n    end\n\n    it \"should return nil if no module can be found with a matching plugin\" do\n      mod = double('module')\n      allow(mod).to receive(:plugin).with(\"foo/bar\").and_return(nil)\n\n      allow(@environment).to receive(:modules).and_return([mod])\n      expect(@mount.find(\"foo/bar\", @request)).to be_nil\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      allow(mod).to receive(:plugin).with(\"foo/bar\").and_return(\"eh\")\n\n      allow(@environment).to receive(:modules).and_return([mod])\n      expect(@mount.find(\"foo/bar\", @request)).to eq(\"eh\")\n    end\n  end\n\n  describe \"when searching for files\" do\n    it \"should use the node's environment to find the modules\" do\n      expect(@environment).to receive(:modules).at_least(:once).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([\"/tmp/modules\"])\n\n      @mount.search(\"foo\", @request)\n    end\n\n    it \"should return modulepath if no modules can be found that have plugins\" do\n      mod = double('module')\n      allow(mod).to receive(:plugins?).and_return(false)\n\n      allow(@environment).to receive(:modules).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([\"/\"])\n      expect(@options).to receive(:[]=).with(:recurse, false)\n      expect(@mount.search(\"foo/bar\", @request)).to eq([\"/\"])\n    end\n\n    it \"should return the default search module path if no modules can be found that have plugins and modulepath is invalid\" do\n      mod = double('module')\n      allow(mod).to receive(:plugins?).and_return(false)\n\n      allow(@environment).to receive(:modules).and_return([])\n      allow(@environment).to receive(:modulepath).and_return([])\n      expect(@mount.search(\"foo/bar\", @request)).to eq([Puppet[:codedir]])\n    end\n\n    it \"should return the plugin paths for each module that has plugins\" do\n      one = double('module', :plugins? => true, :plugin_directory => \"/one\")\n      two = double('module', :plugins? => true, :plugin_directory => \"/two\")\n\n      allow(@environment).to receive(:modules).and_return([one, two])\n      expect(@mount.search(\"foo/bar\", @request)).to eq(%w{/one /two})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/scripts_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/scripts'\n\ndescribe Puppet::FileServing::Mount::Scripts do\n  before do\n    @mount = Puppet::FileServing::Mount::Scripts.new(\"scripts\")\n\n    @environment = double('environment', :module => nil)\n    @request = double('request', :environment => @environment)\n  end\n\n  describe \"when finding files\" do\n    it \"should fail if no module is specified\" do\n      expect { @mount.find(\"\", @request) }.to raise_error(/No module specified/)\n    end\n\n    it \"should use the provided environment to find the module\" do\n      expect(@environment).to receive(:module)\n\n      @mount.find(\"foo\", @request)\n    end\n\n    it \"should treat the first field of the relative path as the module name\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.find(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return nil if the specified module does not exist\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.find(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      expect(mod).to receive(:script).with(\"bar/baz\").and_return(\"eh\")\n      expect(@environment).to receive(:module).with(\"foo\").and_return(mod)\n      expect(@mount.find(\"foo/bar/baz\", @request)).to eq(\"eh\")\n    end\n  end\n\n  describe \"when searching for files\" do\n    it \"should fail if no module is specified\" do\n      expect { @mount.search(\"\", @request) }.to raise_error(/No module specified/)\n    end\n\n    it \"should use the node's environment to search the module\" do\n      expect(@environment).to receive(:module)\n\n      @mount.search(\"foo\", @request)\n    end\n\n    it \"should treat the first field of the relative path as the module name\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.search(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return nil if the specified module does not exist\" do\n      expect(@environment).to receive(:module).with(\"foo\").and_return(nil)\n      @mount.search(\"foo/bar/baz\", @request)\n    end\n\n    it \"should return the script path as an array from the module\" do\n      mod = double('module')\n      expect(mod).to receive(:script).with(\"bar/baz\").and_return(\"eh\")\n      expect(@environment).to receive(:module).with(\"foo\").and_return(mod)\n      expect(@mount.search(\"foo/bar/baz\", @request)).to eq([\"eh\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount/tasks_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount/tasks'\n\ndescribe Puppet::FileServing::Mount::Tasks do\n  before do\n    @mount = Puppet::FileServing::Mount::Tasks.new(\"tasks\")\n\n    @environment = double('environment', :module => nil)\n    @request = double('request', :environment => @environment)\n  end\n\n  describe \"when finding task files\" do\n    it \"should fail if no task is specified\" do\n      expect { @mount.find(\"\", @request) }.to raise_error(/No task specified/)\n    end\n\n    it \"should use the request's environment to find the module\" do\n      mod_name = 'foo'\n      expect(@environment).to receive(:module).with(mod_name)\n\n      @mount.find(mod_name, @request)\n    end\n\n    it \"should use the first segment of the request's path as the module name\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.find(\"foo/bartask\", @request)\n    end\n\n    it \"should return nil if the module in the path doesn't exist\" do\n      expect(@environment).to receive(:module).with(\"foo\").and_return(nil)\n      expect(@mount.find(\"foo/bartask\", @request)).to be_nil\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      expect(mod).to receive(:task_file).with(\"bartask\").and_return(\"mocked\")\n      expect(@environment).to receive(:module).with(\"foo\").and_return(mod)\n      expect(@mount.find(\"foo/bartask\", @request)).to eq(\"mocked\")\n    end\n  end\n\n  describe \"when searching for task files\" do\n    it \"should fail if no module is specified\" do\n      expect { @mount.search(\"\", @request) }.to raise_error(/No task specified/)\n    end\n\n    it \"should use the request's environment to find the module\" do\n      mod_name = 'foo'\n      expect(@environment).to receive(:module).with(mod_name)\n\n      @mount.search(mod_name, @request)\n    end\n\n    it \"should use the first segment of the request's path as the module name\" do\n      expect(@environment).to receive(:module).with(\"foo\")\n      @mount.search(\"foo/bartask\", @request)\n    end\n\n    it \"should return nil if the module in the path doesn't exist\" do\n      expect(@environment).to receive(:module).with(\"foo\").and_return(nil)\n      expect(@mount.search(\"foo/bartask\", @request)).to be_nil\n    end\n\n    it \"should return the file path from the module\" do\n      mod = double('module')\n      expect(mod).to receive(:task_file).with(\"bartask\").and_return(\"mocked\")\n      expect(@environment).to receive(:module).with(\"foo\").and_return(mod)\n      expect(@mount.search(\"foo/bartask\", @request)).to eq([\"mocked\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/mount_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_serving/mount'\n\ndescribe Puppet::FileServing::Mount do\n  it \"should use 'mount[$name]' as its string form\" do\n    expect(Puppet::FileServing::Mount.new(\"foo\").to_s).to eq(\"mount[foo]\")\n  end\nend\n\ndescribe Puppet::FileServing::Mount, \" when initializing\" do\n  it \"should fail on non-alphanumeric name\" do\n    expect { Puppet::FileServing::Mount.new(\"non alpha\") }.to raise_error(ArgumentError)\n  end\n\n  it \"should allow dashes in its name\" do\n    expect(Puppet::FileServing::Mount.new(\"non-alpha\").name).to eq(\"non-alpha\")\n  end\nend\n\ndescribe Puppet::FileServing::Mount, \" when finding files\" do\n  it \"should fail\" do\n    expect { Puppet::FileServing::Mount.new(\"test\").find(\"foo\", :one => \"two\") }.to raise_error(NotImplementedError)\n  end\nend\n\ndescribe Puppet::FileServing::Mount, \" when searching for files\" do\n  it \"should fail\" do\n    expect { Puppet::FileServing::Mount.new(\"test\").search(\"foo\", :one => \"two\") }.to raise_error(NotImplementedError)\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/terminus_helper_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/terminus_helper'\n\nclass Puppet::FileServing::TestHelper\n  include Puppet::FileServing::TerminusHelper\n\n  attr_reader :model\n\n  def initialize(model)\n    @model = model\n  end\nend\n\ndescribe Puppet::FileServing::TerminusHelper do\n  before do\n    @model = double('model')\n    @helper = Puppet::FileServing::TestHelper.new(@model)\n\n    @request = double('request', :key => \"url\", :options => {})\n\n    @fileset = double('fileset', :files => [], :path => \"/my/file\")\n    allow(Puppet::FileServing::Fileset).to receive(:new).with(\"/my/file\", {}).and_return(@fileset)\n  end\n\n  it \"should find a file with absolute path\" do\n    file = double('file', :collect => nil)\n    expect(file).to receive(:collect).with(no_args)\n    expect(@model).to receive(:new).with(\"/my/file\", {:relative_path => nil}).and_return(file)\n    @helper.path2instance(@request, \"/my/file\")\n  end\n\n  it \"should pass through links, checksum_type, and source_permissions\" do\n    file = double('file', :checksum_type= => nil, :links= => nil, :collect => nil)\n    [[:checksum_type, :sha256], [:links, true], [:source_permissions, :use]].each {|k, v|\n      expect(file).to receive(k.to_s+'=').with(v)\n      @request.options[k] = v\n    }\n    expect(file).to receive(:collect)\n    expect(@model).to receive(:new).with(\"/my/file\", {:relative_path => :file}).and_return(file)\n    @helper.path2instance(@request, \"/my/file\", {:relative_path => :file})\n  end\n\n  it \"should use a fileset to find paths\" do\n    @fileset = double('fileset', :files => [], :path => \"/my/files\")\n    expect(Puppet::FileServing::Fileset).to receive(:new).with(\"/my/file\", anything).and_return(@fileset)\n    @helper.path2instances(@request, \"/my/file\")\n  end\n\n  it \"should support finding across multiple paths by merging the filesets\" do\n    first = double('fileset', :files => [], :path => \"/first/file\")\n    expect(Puppet::FileServing::Fileset).to receive(:new).with(\"/first/file\", anything).and_return(first)\n    second = double('fileset', :files => [], :path => \"/second/file\")\n    expect(Puppet::FileServing::Fileset).to receive(:new).with(\"/second/file\", anything).and_return(second)\n\n    expect(Puppet::FileServing::Fileset).to receive(:merge).with(first, second).and_return({})\n\n    @helper.path2instances(@request, \"/first/file\", \"/second/file\")\n  end\n\n  it \"should pass the indirection request to the Fileset at initialization\" do\n    expect(Puppet::FileServing::Fileset).to receive(:new).with(anything, @request).and_return(@fileset)\n    @helper.path2instances(@request, \"/my/file\")\n  end\n\n  describe \"when creating instances\" do\n    before do\n      allow(@request).to receive(:key).and_return(\"puppet://host/mount/dir\")\n\n      @one = double('one', :links= => nil, :collect => nil)\n      @two = double('two', :links= => nil, :collect => nil)\n\n      @fileset = double('fileset', :files => %w{one two}, :path => \"/my/file\")\n      allow(Puppet::FileServing::Fileset).to receive(:new).and_return(@fileset)\n    end\n\n    it \"should set each returned instance's path to the original path\" do\n      expect(@model).to receive(:new).with(\"/my/file\", anything).and_return(@one, @two)\n      @helper.path2instances(@request, \"/my/file\")\n    end\n\n    it \"should set each returned instance's relative path to the file-specific path\" do\n      expect(@model).to receive(:new).with(anything, hash_including(relative_path: \"one\")).and_return(@one)\n      expect(@model).to receive(:new).with(anything, hash_including(relative_path: \"two\")).and_return(@two)\n      @helper.path2instances(@request, \"/my/file\")\n    end\n\n    it \"should set the links value on each instance if one is provided\" do\n      expect(@one).to receive(:links=).with(:manage)\n      expect(@two).to receive(:links=).with(:manage)\n      expect(@model).to receive(:new).and_return(@one, @two)\n\n      @request.options[:links] = :manage\n      @helper.path2instances(@request, \"/my/file\")\n    end\n\n    it \"should set the request checksum_type if one is provided\" do\n      expect(@one).to receive(:checksum_type=).with(:test)\n      expect(@two).to receive(:checksum_type=).with(:test)\n      expect(@model).to receive(:new).and_return(@one, @two)\n\n      @request.options[:checksum_type] = :test\n      @helper.path2instances(@request, \"/my/file\")\n    end\n\n    it \"should collect the instance's attributes\" do\n      expect(@one).to receive(:collect)\n      expect(@two).to receive(:collect)\n      expect(@model).to receive(:new).and_return(@one, @two)\n\n      @helper.path2instances(@request, \"/my/file\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_serving/terminus_selector_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/file_serving/terminus_selector'\n\ndescribe Puppet::FileServing::TerminusSelector do\n  class TestSelector\n    include Puppet::FileServing::TerminusSelector\n  end\n\n  def create_request(key)\n    Puppet::Indirector::Request.new(:indirection_name, :find, key, nil, {node: 'whatever'})\n  end\n\n  subject { TestSelector.new }\n\n  describe \"when being used to select termini\" do\n    it \"should return :file if the request key is fully qualified\" do\n      request = create_request(File.expand_path('/foo'))\n\n      expect(subject.select(request)).to eq(:file)\n    end\n\n    it \"should return :file_server if the request key is relative\" do\n      request = create_request('modules/my_module/path/to_file')\n\n      expect(subject.select(request)).to eq(:file_server)\n    end\n\n    it \"should return :file if the URI protocol is set to 'file'\" do\n      request = create_request(Puppet::Util.path_to_uri(File.expand_path(\"/foo\")).to_s)\n\n      expect(subject.select(request)).to eq(:file)\n    end\n\n    it \"should return :http if the URI protocol is set to 'http'\" do\n      request = create_request(\"http://www.example.com\")\n\n      expect(subject.select(request)).to eq(:http)\n    end\n\n    it \"should return :http if the URI protocol is set to 'https'\" do\n      request = create_request(\"https://www.example.com\")\n\n      expect(subject.select(request)).to eq(:http)\n    end\n\n    it \"should return :http if the path starts with a double slash\" do\n      request = create_request(\"https://www.example.com//index.html\")\n\n      expect(subject.select(request)).to eq(:http)\n    end\n\n    it \"should fail when a protocol other than :puppet, :http(s) or :file is used\" do\n      request = create_request(\"ftp://ftp.example.com\")\n\n      expect {\n        subject.select(request)\n      }.to raise_error(ArgumentError, /URI protocol 'ftp' is not currently supported for file serving/)\n    end\n\n    describe \"and the protocol is 'puppet'\" do\n      it \"should choose :rest when a server is specified\" do\n        request = create_request(\"puppet://puppetserver.example.com\")\n\n        expect(subject.select(request)).to eq(:rest)\n      end\n\n      # This is so a given file location works when bootstrapping with no server.\n      it \"should choose :rest when default_file_terminus is rest\" do\n        Puppet[:server] = 'localhost'\n        request = create_request(\"puppet:///plugins\")\n\n        expect(subject.select(request)).to eq(:rest)\n      end\n\n      it \"should choose :file_server when default_file_terminus is file_server and no server is specified on the request\" do\n        Puppet[:default_file_terminus] = 'file_server'\n        request = create_request(\"puppet:///plugins\")\n\n        expect(subject.select(request)).to eq(:file_server)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_system/path_pattern_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/file_system'\nrequire 'puppet/util'\n\ndescribe Puppet::FileSystem::PathPattern do\n  include PuppetSpec::Files\n  InvalidPattern = Puppet::FileSystem::PathPattern::InvalidPattern\n\n  describe 'relative' do\n    it \"can not be created with a traversal up the directory tree\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\"my/../other\")\n      end.to raise_error(InvalidPattern, \"PathPatterns cannot be created with directory traversals.\")\n    end\n\n    it \"can be created with a '..' prefixing a filename\" do\n      expect(Puppet::FileSystem::PathPattern.relative(\"my/..other\").to_s).to eq(\"my/..other\")\n    end\n\n    it \"can be created with a '..' suffixing a filename\" do\n      expect(Puppet::FileSystem::PathPattern.relative(\"my/other..\").to_s).to eq(\"my/other..\")\n    end\n\n    it \"can be created with a '..' embedded in a filename\" do\n      expect(Puppet::FileSystem::PathPattern.relative(\"my/ot..her\").to_s).to eq(\"my/ot..her\")\n    end\n\n    it \"can not be created with a \\\\0 byte embedded\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\"my/\\0/other\")\n      end.to raise_error(InvalidPattern, \"PathPatterns cannot be created with a zero byte.\")\n    end\n\n    it \"can not be created with a windows drive\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\"c:\\\\relative\\\\path\")\n      end.to raise_error(InvalidPattern, \"A relative PathPattern cannot be prefixed with a drive.\")\n    end\n\n    it \"can not be created with a windows drive (with space)\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\" c:\\\\relative\\\\path\")\n      end.to raise_error(InvalidPattern, \"A relative PathPattern cannot be prefixed with a drive.\")\n    end\n\n    it \"can not create an absolute relative path\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\"/no/absolutes\")\n      end.to raise_error(InvalidPattern, \"A relative PathPattern cannot be an absolute path.\")\n    end\n\n    it \"can not create an absolute relative path (with space)\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\"\\t/no/absolutes\")\n      end.to raise_error(InvalidPattern, \"A relative PathPattern cannot be an absolute path.\")\n    end\n\n    it \"can not create a relative path that is a windows path relative to the current drive\" do\n      expect do\n        Puppet::FileSystem::PathPattern.relative(\"\\\\no\\relatives\")\n      end.to raise_error(InvalidPattern, \"A PathPattern cannot be a Windows current drive relative path.\")\n    end\n\n    it \"creates a relative PathPattern from a valid relative path\" do\n      expect(Puppet::FileSystem::PathPattern.relative(\"a/relative/path\").to_s).to eq(\"a/relative/path\")\n    end\n\n    it \"is not absolute\" do\n      expect(Puppet::FileSystem::PathPattern.relative(\"a/relative/path\")).to_not be_absolute\n    end\n  end\n\n  describe 'absolute' do\n    it \"can not create a relative absolute path\" do\n      expect do\n        Puppet::FileSystem::PathPattern.absolute(\"no/relatives\")\n      end.to raise_error(InvalidPattern, \"An absolute PathPattern cannot be a relative path.\")\n    end\n\n    it \"can not create an absolute path that is a windows path relative to the current drive\" do\n      expect do\n        Puppet::FileSystem::PathPattern.absolute(\"\\\\no\\\\relatives\")\n      end.to raise_error(InvalidPattern, \"A PathPattern cannot be a Windows current drive relative path.\")\n    end\n\n    it \"creates an absolute PathPattern from a valid absolute path\" do\n      expect(Puppet::FileSystem::PathPattern.absolute(\"/an/absolute/path\").to_s).to eq(\"/an/absolute/path\")\n    end\n\n    it \"creates an absolute PathPattern from a valid Windows absolute path\" do\n      expect(Puppet::FileSystem::PathPattern.absolute(\"c:/absolute/windows/path\").to_s).to eq(\"c:/absolute/windows/path\")\n    end\n\n    it \"can be created with a '..' embedded in a filename on windows\", :if => Puppet::Util::Platform.windows? do\n      expect(Puppet::FileSystem::PathPattern.absolute(%q{c:\\..my\\ot..her\\one..}).to_s).to eq(%q{c:\\..my\\ot..her\\one..})\n    end\n\n    it \"is absolute\" do\n      expect(Puppet::FileSystem::PathPattern.absolute(\"c:/absolute/windows/path\")).to be_absolute\n    end\n  end\n\n  it \"prefixes the relative path pattern with another path\" do\n    pattern = Puppet::FileSystem::PathPattern.relative(\"docs/*_thoughts.txt\")\n    prefix = Puppet::FileSystem::PathPattern.absolute(\"/prefix\")\n\n    absolute_pattern = pattern.prefix_with(prefix)\n\n    expect(absolute_pattern).to be_absolute\n    expect(absolute_pattern.to_s).to eq(File.join(\"/prefix\", \"docs/*_thoughts.txt\"))\n  end\n\n  it \"refuses to prefix with a relative pattern\" do\n    pattern = Puppet::FileSystem::PathPattern.relative(\"docs/*_thoughts.txt\")\n    prefix = Puppet::FileSystem::PathPattern.relative(\"prefix\")\n\n    expect do\n      pattern.prefix_with(prefix)\n    end.to raise_error(InvalidPattern, \"An absolute PathPattern cannot be a relative path.\")\n  end\n\n  it \"applies the pattern to the filesystem as a glob\" do\n    dir = tmpdir('globtest')\n    create_file_in(dir, \"found_one\")\n    create_file_in(dir, \"found_two\")\n    create_file_in(dir, \"third_not_found\")\n\n    pattern = Puppet::FileSystem::PathPattern.relative(\"found_*\").prefix_with(\n      Puppet::FileSystem::PathPattern.absolute(dir))\n\n    expect(pattern.glob).to match_array([File.join(dir, \"found_one\"),\n                                         File.join(dir, \"found_two\")])\n  end\n\n  it 'globs wildcard patterns properly' do\n    # See PUP-11788 and https://github.com/jruby/jruby/issues/7836.\n    pending 'JRuby does not properly handle Dir.glob' if Puppet::Util::Platform.jruby?\n\n    dir = tmpdir('globtest')\n    create_file_in(dir, 'foo.pp')\n    create_file_in(dir, 'foo.pp.pp')\n\n    pattern = Puppet::FileSystem::PathPattern.absolute(File.join(dir, '**/*.pp'))\n\n    expect(pattern.glob).to match_array([File.join(dir, 'foo.pp'),\n                                         File.join(dir, 'foo.pp.pp')])\n  end\n\n  def create_file_in(dir, name)\n    File.open(File.join(dir, name), \"w\") { |f| f.puts \"data\" }\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_system/uniquefile_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::FileSystem::Uniquefile do\n  include PuppetSpec::Files\n\n  it \"makes the name of the file available\" do\n    Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n      expect(file.path).to match(/foo/)\n    end\n  end\n\n  it \"ensures the file has permissions 0600\", unless: Puppet::Util::Platform.windows? do\n    Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n      expect(Puppet::FileSystem.stat(file.path).mode & 07777).to eq(0600)\n    end\n  end\n\n  it \"provides a writeable file\" do\n    Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n      file.write(\"stuff\")\n      file.flush\n\n      expect(Puppet::FileSystem.read(file.path)).to eq(\"stuff\")\n    end\n  end\n\n  it \"returns the value of the block\" do\n    the_value = Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n      \"my value\"\n    end\n\n    expect(the_value).to eq(\"my value\")\n  end\n\n  it \"unlinks the temporary file\" do\n    filename = Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n      file.path\n    end\n\n    expect(Puppet::FileSystem.exist?(filename)).to be_falsey\n  end\n\n  it \"unlinks the temporary file even if the block raises an error\" do\n    filename = nil\n\n    begin\n      Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n        filename = file.path\n        raise \"error!\"\n      end\n    rescue\n    end\n\n    expect(Puppet::FileSystem.exist?(filename)).to be_falsey\n  end\n\n  it \"propagates lock creation failures\" do\n    # use an arbitrary exception so as not accidentally collide\n    # with the ENOENT that occurs when trying to call rmdir\n    allow(Puppet::FileSystem::Uniquefile).to receive(:mkdir).and_raise('arbitrary failure')\n    expect(Puppet::FileSystem::Uniquefile).not_to receive(:rmdir)\n\n    expect {\n      Puppet::FileSystem::Uniquefile.open_tmp('foo') { |tmp| }\n    }.to raise_error('arbitrary failure')\n  end\n\n  it \"only removes lock files that exist\" do\n    # prevent the .lock directory from being created\n    allow(Puppet::FileSystem::Uniquefile).to receive(:mkdir)\n\n    # and expect cleanup to be skipped\n    expect(Puppet::FileSystem::Uniquefile).not_to receive(:rmdir)\n\n    Puppet::FileSystem::Uniquefile.open_tmp('foo') { |tmp| }\n  end\n\n  it \"reports when a parent directory does not exist\" do\n    dir = tmpdir('uniquefile')\n    lock = File.join(dir, 'path', 'to', 'lock')\n\n    expect {\n      Puppet::FileSystem::Uniquefile.new('foo', lock) { |tmp| }\n    }.to raise_error(Errno::ENOENT, %r{No such file or directory - A directory component in .* does not exist or is a dangling symbolic link})\n  end\n\n  it \"should use UTF8 characters in TMP,TEMP,TMPDIR environment variable\", :if => Puppet::Util::Platform.windows? do\n    rune_utf8 = \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\"\n    temp_rune_utf8 = File.join(Dir.tmpdir, rune_utf8)\n    Puppet::FileSystem.mkpath(temp_rune_utf8)\n\n    # Set the temporary environment variables to the UTF8 temp path\n    Puppet::Util::Windows::Process.set_environment_variable('TMPDIR', temp_rune_utf8)\n    Puppet::Util::Windows::Process.set_environment_variable('TMP', temp_rune_utf8)\n    Puppet::Util::Windows::Process.set_environment_variable('TEMP', temp_rune_utf8)\n\n    # Create a unique file\n    filename = Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|\n      File.dirname(file.path)\n    end\n\n    expect(filename).to eq(temp_rune_utf8)\n  end\n\n  it \"preserves tilde characters\" do\n    Puppet::FileSystem::Uniquefile.open_tmp('~foo') do |file|\n      expect(File.basename(file.path)).to start_with('~foo')\n    end\n  end\n\n  context \"Ruby 1.9.3 Tempfile tests\" do\n    # the remaining tests in this file are ported directly from the ruby 1.9.3 source,\n    # since most of this file was ported from there\n    # see: https://github.com/ruby/ruby/blob/v1_9_3_547/test/test_tempfile.rb\n\n    def tempfile(*args, &block)\n      t = Puppet::FileSystem::Uniquefile.new(*args, &block)\n      @tempfile = (t unless block)\n    end\n\n    after(:each) do\n      if @tempfile\n        @tempfile.close!\n      end\n    end\n\n    it \"creates tempfiles\" do\n      t = tempfile(\"foo\")\n      path = t.path\n      t.write(\"hello world\")\n      t.close\n      expect(File.read(path)).to eq(\"hello world\")\n    end\n\n    it \"saves in tmpdir by default\" do\n      t = tempfile(\"foo\")\n      expect(Dir.tmpdir).to eq(File.dirname(t.path))\n    end\n\n    it \"saves in given directory\" do\n      subdir = File.join(Dir.tmpdir, \"tempfile-test-#{rand}\")\n      Dir.mkdir(subdir)\n      begin\n        tempfile = Tempfile.new(\"foo\", subdir)\n        tempfile.close\n        begin\n          expect(subdir).to eq(File.dirname(tempfile.path))\n        ensure\n          tempfile.unlink\n        end\n      ensure\n        Dir.rmdir(subdir)\n      end\n    end\n\n    it \"supports basename\" do\n      t = tempfile(\"foo\")\n      expect(File.basename(t.path)).to match(/^foo/)\n    end\n\n    it \"supports basename with suffix\" do\n      t = tempfile([\"foo\", \".txt\"])\n      expect(File.basename(t.path)).to match(/^foo/)\n      expect(File.basename(t.path)).to match(/\\.txt$/)\n    end\n\n    it \"supports unlink\" do\n      t = tempfile(\"foo\")\n      path = t.path\n      t.close\n      expect(File.exist?(path)).to eq(true)\n      t.unlink\n      expect(File.exist?(path)).to eq(false)\n      expect(t.path).to eq(nil)\n    end\n\n    it \"supports closing\" do\n      t = tempfile(\"foo\")\n      expect(t.closed?).to eq(false)\n      t.close\n      expect(t.closed?).to eq(true)\n    end\n\n    it \"supports closing and unlinking via boolean argument\" do\n      t = tempfile(\"foo\")\n      path = t.path\n      t.close(true)\n      expect(t.closed?).to eq(true)\n      expect(t.path).to eq(nil)\n      expect(File.exist?(path)).to eq(false)\n    end\n\n    context \"on unix platforms\", :unless => Puppet::Util::Platform.windows? do\n      it \"close doesn't unlink if already unlinked\" do\n        t = tempfile(\"foo\")\n        path = t.path\n        t.unlink\n        File.open(path, \"w\").close\n        begin\n          t.close(true)\n          expect(File.exist?(path)).to eq(true)\n        ensure\n          File.unlink(path) rescue nil\n        end\n      end\n    end\n\n    it \"supports close!\" do\n      t = tempfile(\"foo\")\n      path = t.path\n      t.close!\n      expect(t.closed?).to eq(true)\n      expect(t.path).to eq(nil)\n      expect(File.exist?(path)).to eq(false)\n    end\n\n    context \"on unix platforms\", :unless => Puppet::Util::Platform.windows? do\n      it \"close! doesn't unlink if already unlinked\" do\n        t = tempfile(\"foo\")\n        path = t.path\n        t.unlink\n        File.open(path, \"w\").close\n        begin\n          t.close!\n          expect(File.exist?(path)).to eq(true)\n        ensure\n          File.unlink(path) rescue nil\n        end\n      end\n    end\n\n    it \"close does not make path nil\" do\n      t = tempfile(\"foo\")\n      t.close\n      expect(t.path.nil?).to eq(false)\n    end\n\n    it \"close flushes buffer\" do\n      t = tempfile(\"foo\")\n      t.write(\"hello\")\n      t.close\n      expect(File.size(t.path)).to eq(5)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/file_system_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_system'\nrequire 'puppet/util/platform'\n\ndescribe \"Puppet::FileSystem\" do\n  include PuppetSpec::Files\n\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ𠜎\n\n  def with_file_content(content)\n    path = tmpfile('file-system')\n    file = File.new(path, 'wb')\n    file.sync = true\n    file.print content\n\n    yield path\n\n  ensure\n    file.close\n  end\n\n  SYSTEM_SID_BYTES = [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0]\n\n  def is_current_user_system?\n    SYSTEM_SID_BYTES == Puppet::Util::Windows::ADSI::User.current_user_sid.sid_bytes\n  end\n\n  def expects_public_file(path)\n    if Puppet::Util::Platform.windows?\n      current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n      sd = Puppet::Util::Windows::Security.get_security_descriptor(path)\n      expect(sd.dacl).to contain_exactly(\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),\n        an_object_having_attributes(sid: current_sid, mask: 0x1f01ff),\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinUsers, mask: 0x120089)\n      )\n    else\n      expect(File.stat(path).mode & 07777).to eq(0644)\n    end\n  end\n\n  def expects_private_file(path)\n    if Puppet::Util::Platform.windows?\n      current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n      sd = Puppet::Util::Windows::Security.get_security_descriptor(path)\n      expect(sd.dacl).to contain_exactly(\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),\n        an_object_having_attributes(sid: current_sid, mask: 0x1f01ff)\n      )\n    else\n      expect(File.stat(path).mode & 07777).to eq(0640)\n    end\n  end\n\n  context \"#open\" do\n    it \"uses the same default mode as File.open, when specifying a nil mode (umask used on non-Windows)\" do\n      file = tmpfile('file_to_update')\n      expect(Puppet::FileSystem.exist?(file)).to be_falsey\n\n      Puppet::FileSystem.open(file, nil, 'a') { |fh| fh.write('') }\n\n      expected_perms = Puppet::Util::Platform.windows? ?\n        # default Windows mode based on temp file storage for SYSTEM user or regular user\n        # for Jenkins or other services running as SYSTEM writing to c:\\windows\\temp\n        # the permissions will typically be SYSTEM(F) / Administrators(F) which is 770\n        # but sometimes there are extra users like IIS_IUSRS granted rights which adds the \"extra ace\" 2\n        # for local Administrators writing to their own temp folders under c:\\users\\USER\n        # they will have (F) for themselves, and Users will not have a permission, hence 700\n        (is_current_user_system? ? ['770', '2000770'] : '2000700') :\n        # or for *nix determine expected mode via bitwise AND complement of umask\n        (0100000 | 0666 & ~File.umask).to_s(8)\n      expect([expected_perms].flatten).to include(Puppet::FileSystem.stat(file).mode.to_s(8))\n\n      default_file = tmpfile('file_to_update2')\n      expect(Puppet::FileSystem.exist?(default_file)).to be_falsey\n\n      File.open(default_file, 'a') { |fh| fh.write('') }\n\n      # which matches the behavior of File.open\n      expect(Puppet::FileSystem.stat(file).mode).to eq(Puppet::FileSystem.stat(default_file).mode)\n    end\n\n    it \"can accept an octal mode integer\" do\n      file = tmpfile('file_to_update')\n      # NOTE: 777 here returns 755, but due to Ruby?\n      Puppet::FileSystem.open(file, 0444, 'a') { |fh| fh.write('') }\n\n      # Behavior may change in the future on Windows, to *actually* change perms\n      # but for now, setting a mode doesn't touch them\n      expected_perms = Puppet::Util::Platform.windows? ?\n        (is_current_user_system? ? ['770', '2000770'] : '2000700') :\n        '100444'\n      expect([expected_perms].flatten).to include(Puppet::FileSystem.stat(file).mode.to_s(8))\n\n      expected_ruby_mode = Puppet::Util::Platform.windows? ?\n        # The Windows behavior has been changed to ignore the mode specified by open\n        # given it's unlikely a caller expects Windows file attributes to be set\n        # therefore mode is explicitly not managed (until PUP-6959 is fixed)\n        #\n        # In default Ruby on Windows a mode controls file attribute setting\n        # (like archive, read-only, etc)\n        # The GetFileInformationByHandle API returns an attributes value that is\n        # a bitmask of Windows File Attribute Constants at\n        # https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx\n        '100644' :\n        # On other platforms, the mode should be what was set by octal 0444\n        '100444'\n\n      expect(File.stat(file).mode.to_s(8)).to eq(expected_ruby_mode)\n    end\n\n    it \"cannot accept a mode string\" do\n      file = tmpfile('file_to_update')\n      expect {\n        Puppet::FileSystem.open(file, \"444\", 'a') { |fh| fh.write('') }\n      }.to raise_error(TypeError)\n    end\n\n    it \"opens, creates ands allows updating of a new file, using by default, the external system encoding\" do\n      begin\n        original_encoding = Encoding.default_external\n\n        # this must be set through Ruby API and cannot be mocked - it sets internal state used by File.open\n        # pick a bizarre encoding unlikely to be used in any real tests\n        Encoding.default_external = Encoding::CP737\n\n        file = tmpfile('file_to_update')\n\n        # test writing a UTF-8 string when Default external encoding is something different\n        Puppet::FileSystem.open(file, 0660, 'w') do |fh|\n          # note Ruby behavior which has no external_encoding, but implicitly uses Encoding.default_external\n          expect(fh.external_encoding).to be_nil\n          # write a UTF-8 string to this file\n          fh.write(mixed_utf8)\n        end\n\n        # prove that Ruby implicitly converts read strings back to Encoding.default_external\n        # and that it did that in the previous write\n        written = Puppet::FileSystem.read(file)\n        expect(written.encoding).to eq(Encoding.default_external)\n        expect(written).to eq(mixed_utf8.force_encoding(Encoding.default_external))\n      ensure\n        # carefully roll back to the previous\n        Encoding.default_external = original_encoding\n      end\n    end\n  end\n\n  context \"#exclusive_open\" do\n    it \"opens ands allows updating of an existing file\" do\n      file = file_containing(\"file_to_update\", \"the contents\")\n\n      Puppet::FileSystem.exclusive_open(file, 0660, 'r+') do |fh|\n        old = fh.read\n        fh.truncate(0)\n        fh.rewind\n        fh.write(\"updated #{old}\")\n      end\n\n      expect(Puppet::FileSystem.read(file)).to eq(\"updated the contents\")\n    end\n\n    it \"opens, creates ands allows updating of a new file, using by default, the external system encoding\" do\n      begin\n        original_encoding = Encoding.default_external\n\n        # this must be set through Ruby API and cannot be mocked - it sets internal state used by File.open\n        # pick a bizarre encoding unlikely to be used in any real tests\n        Encoding.default_external = Encoding::CP737\n\n        file = tmpfile('file_to_update')\n\n        # test writing a UTF-8 string when Default external encoding is something different\n        Puppet::FileSystem.exclusive_open(file, 0660, 'w') do |fh|\n          # note Ruby behavior which has no external_encoding, but implicitly uses Encoding.default_external\n          expect(fh.external_encoding).to be_nil\n          # write a UTF-8 string to this file\n          fh.write(mixed_utf8)\n        end\n\n        # prove that Ruby implicitly converts read strings back to Encoding.default_external\n        # and that it did that in the previous write\n        written = Puppet::FileSystem.read(file)\n        expect(written.encoding).to eq(Encoding.default_external)\n        expect(written).to eq(mixed_utf8.force_encoding(Encoding.default_external))\n      ensure\n        # carefully roll back to the previous\n        Encoding.default_external = original_encoding\n      end\n    end\n\n    it \"excludes other processes from updating at the same time\",\n       unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n      file = file_containing(\"file_to_update\", \"0\")\n\n      increment_counter_in_multiple_processes(file, 5, 'r+')\n\n      expect(Puppet::FileSystem.read(file)).to eq(\"5\")\n    end\n\n    it \"excludes other processes from updating at the same time even when creating the file\",\n       unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n      file = tmpfile(\"file_to_update\")\n\n      increment_counter_in_multiple_processes(file, 5, 'a+')\n\n      expect(Puppet::FileSystem.read(file)).to eq(\"5\")\n    end\n\n    it \"times out if the lock cannot be acquired in a specified amount of time\",\n       unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n      file = tmpfile(\"file_to_update\")\n\n      child = spawn_process_that_locks(file)\n\n      expect do\n        Puppet::FileSystem.exclusive_open(file, 0666, 'a', 0.1) do |f|\n        end\n      end.to raise_error(Timeout::Error, /Timeout waiting for exclusive lock on #{file}/)\n\n      Process.kill(9, child)\n    end\n\n    def spawn_process_that_locks(file)\n      read, write = IO.pipe\n\n      child = Kernel.fork do\n        read.close\n        Puppet::FileSystem.exclusive_open(file, 0666, 'a') do |fh|\n          write.write(true)\n          write.close\n          sleep 10\n        end\n      end\n\n      write.close\n      read.read\n      read.close\n\n      child\n    end\n\n    def increment_counter_in_multiple_processes(file, num_procs, options)\n      children = []\n      num_procs.times do\n        children << Kernel.fork do\n          Puppet::FileSystem.exclusive_open(file, 0660, options) do |fh|\n            fh.rewind\n            contents = (fh.read || 0).to_i\n            fh.truncate(0)\n            fh.rewind\n            fh.write((contents + 1).to_s)\n          end\n          exit(0)\n        end\n      end\n\n      children.each { |pid| Process.wait(pid) }\n    end\n  end\n\n  context \"read_preserve_line_endings\" do\n    it \"should read a file with line feed\" do\n      with_file_content(\"file content \\n\") do |file|\n        expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq(\"file content \\n\")\n      end\n    end\n\n    it \"should read a file with carriage return line feed\" do\n      with_file_content(\"file content \\r\\n\") do |file|\n        expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq(\"file content \\r\\n\")\n      end\n    end\n\n    it \"should read a mixed file using only the first line newline when lf\" do\n      with_file_content(\"file content \\nsecond line \\r\\n\") do |file|\n        expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq(\"file content \\nsecond line \\r\\n\")\n      end\n    end\n\n    it \"should read a mixed file using only the first line newline when crlf\" do\n      with_file_content(\"file content \\r\\nsecond line \\n\") do |file|\n        expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq(\"file content \\r\\nsecond line \\n\")\n      end\n    end\n\n    it \"should ignore leading BOM\" do\n      with_file_content(\"\\uFEFFfile content \\n\") do |file|\n        expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq(\"file content \\n\")\n      end\n    end\n\n    it \"should not warn about misusage of BOM with non-UTF encoding\" do\n      allow(Encoding).to receive(:default_external).and_return(Encoding::US_ASCII)\n      with_file_content(\"file content \\n\") do |file|\n        expect{ Puppet::FileSystem.read_preserve_line_endings(file) }.not_to output(/BOM with non-UTF encoding US-ASCII is nonsense/).to_stderr\n      end\n    end\n  end\n\n  context \"read without an encoding specified\" do\n    it \"returns strings as Encoding.default_external\" do\n      temp_file = file_containing('test.txt', 'hello world')\n\n      contents = Puppet::FileSystem.read(temp_file)\n\n      expect(contents.encoding).to eq(Encoding.default_external)\n      expect(contents).to eq('hello world')\n    end\n  end\n\n  context \"read should allow an encoding to be specified\" do\n    # First line of Rune version of Rune poem at http://www.columbia.edu/~fdc/utf8/\n    # characters chosen since they will not parse on Windows with codepage 437 or 1252\n    # Section 3.2.1.3 of Ruby spec guarantees that \\u strings are encoded as UTF-8\n    let (:rune_utf8) { \"\\u16A0\\u16C7\\u16BB\" } # 'ᚠᛇᚻ'\n\n    it \"and should read a UTF8 file properly\" do\n      temp_file = file_containing('utf8.txt', rune_utf8)\n\n      contents = Puppet::FileSystem.read(temp_file, :encoding => 'utf-8')\n\n      expect(contents.encoding).to eq(Encoding::UTF_8)\n      expect(contents).to eq(rune_utf8)\n    end\n\n    it \"does not strip the UTF8 BOM (Byte Order Mark) if present in a file\" do\n      bom = \"\\uFEFF\"\n\n      temp_file = file_containing('utf8bom.txt', \"#{bom}#{rune_utf8}\")\n      contents = Puppet::FileSystem.read(temp_file, :encoding => 'utf-8')\n\n      expect(contents.encoding).to eq(Encoding::UTF_8)\n      expect(contents).to eq(\"#{bom}#{rune_utf8}\")\n    end\n  end\n\n  describe \"symlink\",\n    :if => ! Puppet.features.manages_symlinks? &&\n    Puppet::Util::Platform.windows? do\n\n    let(:file)         { tmpfile(\"somefile\") }\n    let(:missing_file) { tmpfile(\"missingfile\") }\n    let(:expected_msg) { \"This version of Windows does not support symlinks.  Windows Vista / 2008 or higher is required.\" }\n\n    before :each do\n      FileUtils.touch(file)\n    end\n\n    it \"should raise an error when trying to create a symlink\" do\n      expect { Puppet::FileSystem.symlink(file, 'foo') }.to raise_error(Puppet::Util::Windows::Error)\n    end\n\n    it \"should return false when trying to check if a path is a symlink\" do\n      expect(Puppet::FileSystem.symlink?(file)).to be_falsey\n    end\n\n    it \"should raise an error when trying to read a symlink\" do\n      expect { Puppet::FileSystem.readlink(file) }.to raise_error(Puppet::Util::Windows::Error)\n    end\n\n    it \"should return a File::Stat instance when calling stat on an existing file\" do\n      expect(Puppet::FileSystem.stat(file)).to be_instance_of(File::Stat)\n    end\n\n    it \"should raise Errno::ENOENT when calling stat on a missing file\" do\n      expect { Puppet::FileSystem.stat(missing_file) }.to raise_error(Errno::ENOENT)\n    end\n\n    it \"should fall back to stat when trying to lstat a file\" do\n      expect(Puppet::Util::Windows::File).to receive(:stat).with(Puppet::FileSystem.assert_path(file))\n\n      Puppet::FileSystem.lstat(file)\n    end\n  end\n\n  describe \"symlink\", :if => Puppet.features.manages_symlinks? do\n\n    let(:file)          { tmpfile(\"somefile\") }\n    let(:missing_file)  { tmpfile(\"missingfile\") }\n    let(:dir)           { tmpdir(\"somedir\") }\n\n    before :each do\n      FileUtils.touch(file)\n    end\n\n    it \"should return true for exist? on a present file\" do\n      expect(Puppet::FileSystem.exist?(file)).to be_truthy\n    end\n\n    it \"should return true for file? on a present file\" do\n      expect(Puppet::FileSystem.file?(file)).to be_truthy\n    end\n\n    it \"should return false for exist? on a non-existent file\" do\n      expect(Puppet::FileSystem.exist?(missing_file)).to be_falsey\n    end\n\n    it \"should return true for exist? on a present directory\" do\n      expect(Puppet::FileSystem.exist?(dir)).to be_truthy\n    end\n\n    it \"should return false for exist? on a dangling symlink\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(missing_file, symlink)\n\n      expect(Puppet::FileSystem.exist?(missing_file)).to be_falsey\n      expect(Puppet::FileSystem.exist?(symlink)).to be_falsey\n    end\n\n    it \"should return true for exist? on valid symlinks\" do\n      [file, dir].each do |target|\n        symlink = tmpfile(\"#{Puppet::FileSystem.basename(target).to_s}_link\")\n        Puppet::FileSystem.symlink(target, symlink)\n\n        expect(Puppet::FileSystem.exist?(target)).to be_truthy\n        expect(Puppet::FileSystem.exist?(symlink)).to be_truthy\n      end\n    end\n\n    it \"should return false for exist? when resolving a cyclic symlink chain\" do\n      # point symlink -> file\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      # point symlink2 -> symlink\n      symlink2 = tmpfile(\"somefile_link2\")\n      Puppet::FileSystem.symlink(symlink, symlink2)\n\n      # point symlink3 -> symlink2\n      symlink3 = tmpfile(\"somefile_link3\")\n      Puppet::FileSystem.symlink(symlink2, symlink3)\n\n      # yank file, temporarily dangle\n      ::File.delete(file)\n\n      # and trash it so that we can recreate it OK on windows\n      Puppet::FileSystem.unlink(symlink)\n\n      # point symlink -> symlink3 to create a cycle\n      Puppet::FileSystem.symlink(symlink3, symlink)\n\n      expect(Puppet::FileSystem.exist?(symlink3)).to be_falsey\n    end\n\n    it \"should return true for exist? when resolving a symlink chain pointing to a file\" do\n      # point symlink -> file\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      # point symlink2 -> symlink\n      symlink2 = tmpfile(\"somefile_link2\")\n      Puppet::FileSystem.symlink(symlink, symlink2)\n\n      # point symlink3 -> symlink2\n      symlink3 = tmpfile(\"somefile_link3\")\n      Puppet::FileSystem.symlink(symlink2, symlink3)\n\n      expect(Puppet::FileSystem.exist?(symlink3)).to be_truthy\n    end\n\n    it \"should return false for exist? when resolving a symlink chain that dangles\" do\n      # point symlink -> file\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      # point symlink2 -> symlink\n      symlink2 = tmpfile(\"somefile_link2\")\n      Puppet::FileSystem.symlink(symlink, symlink2)\n\n      # point symlink3 -> symlink2\n      symlink3 = tmpfile(\"somefile_link3\")\n      Puppet::FileSystem.symlink(symlink2, symlink3)\n\n      # yank file, and make symlink dangle\n      ::File.delete(file)\n\n      # symlink3 is now indirectly dangled\n      expect(Puppet::FileSystem.exist?(symlink3)).to be_falsey\n    end\n\n    it \"should not create a symlink when the :noop option is specified\" do\n      [file, dir].each do |target|\n        symlink = tmpfile(\"#{Puppet::FileSystem.basename(target)}_link\")\n        Puppet::FileSystem.symlink(target, symlink, { :noop => true })\n\n        expect(Puppet::FileSystem.exist?(target)).to be_truthy\n        expect(Puppet::FileSystem.exist?(symlink)).to be_falsey\n      end\n    end\n\n    it \"should raise Errno::EEXIST if trying to create a file / directory symlink when the symlink path already exists as a file\" do\n      existing_file = tmpfile(\"#{Puppet::FileSystem.basename(file)}_link\")\n      FileUtils.touch(existing_file)\n\n      [file, dir].each do |target|\n        expect { Puppet::FileSystem.symlink(target, existing_file) }.to raise_error(Errno::EEXIST)\n\n        expect(Puppet::FileSystem.exist?(existing_file)).to be_truthy\n        expect(Puppet::FileSystem.symlink?(existing_file)).to be_falsey\n      end\n    end\n\n    it \"should silently fail if trying to create a file / directory symlink when the symlink path already exists as a directory\" do\n      existing_dir = tmpdir(\"#{Puppet::FileSystem.basename(file)}_dir\")\n\n      [file, dir].each do |target|\n        expect(Puppet::FileSystem.symlink(target, existing_dir)).to eq(0)\n\n        expect(Puppet::FileSystem.exist?(existing_dir)).to be_truthy\n        expect(File.directory?(existing_dir)).to be_truthy\n        expect(Puppet::FileSystem.symlink?(existing_dir)).to be_falsey\n      end\n    end\n\n    it \"should silently fail to modify an existing directory symlink to reference a new file or directory\" do\n      [file, dir].each do |target|\n        existing_dir = tmpdir(\"#{Puppet::FileSystem.basename(target)}_dir\")\n        symlink = tmpfile(\"#{Puppet::FileSystem.basename(existing_dir)}_link\")\n        Puppet::FileSystem.symlink(existing_dir, symlink)\n\n        expect(Puppet::FileSystem.readlink(symlink)).to eq(Puppet::FileSystem.path_string(existing_dir))\n\n        # now try to point it at the new target, no error raised, but file system unchanged\n        expect(Puppet::FileSystem.symlink(target, symlink)).to eq(0)\n        expect(Puppet::FileSystem.readlink(symlink)).to eq(existing_dir.to_s)\n      end\n    end\n\n    it \"should raise Errno::EEXIST if trying to modify a file symlink to reference a new file or directory\" do\n      symlink = tmpfile(\"#{Puppet::FileSystem.basename(file)}_link\")\n      file_2 = tmpfile(\"#{Puppet::FileSystem.basename(file)}_2\")\n      FileUtils.touch(file_2)\n      # symlink -> file_2\n      Puppet::FileSystem.symlink(file_2, symlink)\n\n      [file, dir].each do |target|\n        expect { Puppet::FileSystem.symlink(target, symlink) }.to raise_error(Errno::EEXIST)\n        expect(Puppet::FileSystem.readlink(symlink)).to eq(file_2.to_s)\n      end\n    end\n\n    it \"should delete the existing file when creating a file / directory symlink with :force when the symlink path exists as a file\" do\n      [file, dir].each do |target|\n        existing_file = tmpfile(\"#{Puppet::FileSystem.basename(target)}_existing\")\n        FileUtils.touch(existing_file)\n        expect(Puppet::FileSystem.symlink?(existing_file)).to be_falsey\n\n        Puppet::FileSystem.symlink(target, existing_file, { :force => true })\n\n        expect(Puppet::FileSystem.symlink?(existing_file)).to be_truthy\n        expect(Puppet::FileSystem.readlink(existing_file)).to eq(target.to_s)\n      end\n    end\n\n    it \"should modify an existing file symlink when using :force to reference a new file or directory\" do\n      [file, dir].each do |target|\n        existing_file = tmpfile(\"#{Puppet::FileSystem.basename(target)}_existing\")\n        FileUtils.touch(existing_file)\n        existing_symlink = tmpfile(\"#{Puppet::FileSystem.basename(existing_file)}_link\")\n        Puppet::FileSystem.symlink(existing_file, existing_symlink)\n\n        expect(Puppet::FileSystem.readlink(existing_symlink)).to eq(existing_file.to_s)\n\n        Puppet::FileSystem.symlink(target, existing_symlink, { :force => true })\n\n        expect(Puppet::FileSystem.readlink(existing_symlink)).to eq(target.to_s)\n      end\n    end\n\n    it \"should silently fail if trying to overwrite an existing directory with a new symlink when using :force to reference a file or directory\" do\n      [file, dir].each do |target|\n        existing_dir = tmpdir(\"#{Puppet::FileSystem.basename(target)}_existing\")\n\n        expect(Puppet::FileSystem.symlink(target, existing_dir, { :force => true })).to eq(0)\n\n        expect(Puppet::FileSystem.symlink?(existing_dir)).to be_falsey\n      end\n    end\n\n    it \"should silently fail if trying to modify an existing directory symlink when using :force to reference a new file or directory\" do\n      [file, dir].each do |target|\n        existing_dir = tmpdir(\"#{Puppet::FileSystem.basename(target)}_existing\")\n        existing_symlink = tmpfile(\"#{Puppet::FileSystem.basename(existing_dir)}_link\")\n        Puppet::FileSystem.symlink(existing_dir, existing_symlink)\n\n        expect(Puppet::FileSystem.readlink(existing_symlink)).to eq(existing_dir.to_s)\n\n        expect(Puppet::FileSystem.symlink(target, existing_symlink, { :force => true })).to eq(0)\n\n        expect(Puppet::FileSystem.readlink(existing_symlink)).to eq(existing_dir.to_s)\n      end\n    end\n\n    it \"should accept a string, Pathname or object with to_str (Puppet::Util::WatchedFile) for exist?\" do\n      [ tmpfile('bogus1'),\n        Pathname.new(tmpfile('bogus2')),\n        Puppet::Util::WatchedFile.new(tmpfile('bogus3'))\n        ].each { |f| expect(Puppet::FileSystem.exist?(f)).to be_falsey  }\n    end\n\n    it \"should return a File::Stat instance when calling stat on an existing file\" do\n      expect(Puppet::FileSystem.stat(file)).to be_instance_of(File::Stat)\n    end\n\n    it \"should raise Errno::ENOENT when calling stat on a missing file\" do\n      expect { Puppet::FileSystem.stat(missing_file) }.to raise_error(Errno::ENOENT)\n    end\n\n    it \"should be able to create a symlink, and verify it with symlink?\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      expect(Puppet::FileSystem.symlink?(symlink)).to be_truthy\n    end\n\n    it \"should report symlink? as false on file, directory and missing files\" do\n      [file, dir, missing_file].each do |f|\n      expect(Puppet::FileSystem.symlink?(f)).to be_falsey\n      end\n    end\n\n    it \"should return a File::Stat with ftype 'link' when calling lstat on a symlink pointing to existing file\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      stat = Puppet::FileSystem.lstat(symlink)\n      expect(stat).to be_instance_of(File::Stat)\n      expect(stat.ftype).to eq('link')\n    end\n\n    it \"should return a File::Stat of ftype 'link' when calling lstat on a symlink pointing to missing file\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(missing_file, symlink)\n\n      stat = Puppet::FileSystem.lstat(symlink)\n      expect(stat).to be_instance_of(File::Stat)\n      expect(stat.ftype).to eq('link')\n    end\n\n    it \"should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to existing file\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      stat = Puppet::FileSystem.stat(symlink)\n      expect(stat).to be_instance_of(File::Stat)\n      expect(stat.ftype).to eq('file')\n    end\n\n    it \"should return a File::Stat of ftype 'directory' when calling stat on a symlink pointing to existing directory\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(dir, symlink)\n\n      stat = Puppet::FileSystem.stat(symlink)\n      expect(stat).to be_instance_of(File::Stat)\n      expect(stat.ftype).to eq('directory')\n\n      # on Windows, this won't get cleaned up if still linked\n      Puppet::FileSystem.unlink(symlink)\n    end\n\n    it \"should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to another symlink\" do\n      # point symlink -> file\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      # point symlink2 -> symlink\n      symlink2 = tmpfile(\"somefile_link2\")\n      Puppet::FileSystem.symlink(symlink, symlink2)\n\n      expect(Puppet::FileSystem.stat(symlink2).ftype).to eq('file')\n    end\n\n\n    it \"should raise Errno::ENOENT when calling stat on a dangling symlink\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(missing_file, symlink)\n\n      expect { Puppet::FileSystem.stat(symlink) }.to raise_error(Errno::ENOENT)\n    end\n\n    it \"should be able to readlink to resolve the physical path to a symlink\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      expect(Puppet::FileSystem.exist?(file)).to be_truthy\n      expect(Puppet::FileSystem.readlink(symlink)).to eq(file.to_s)\n    end\n\n    it \"should not resolve entire symlink chain with readlink on a symlink'd symlink\" do\n      # point symlink -> file\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n\n      # point symlink2 -> symlink\n      symlink2 = tmpfile(\"somefile_link2\")\n      Puppet::FileSystem.symlink(symlink, symlink2)\n\n      expect(Puppet::FileSystem.exist?(file)).to be_truthy\n      expect(Puppet::FileSystem.readlink(symlink2)).to eq(symlink.to_s)\n    end\n\n    it \"should be able to readlink to resolve the physical path to a dangling symlink\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(missing_file, symlink)\n\n      expect(Puppet::FileSystem.exist?(missing_file)).to be_falsey\n      expect(Puppet::FileSystem.readlink(symlink)).to eq(missing_file.to_s)\n    end\n\n    it \"should be able to unlink a dangling symlink pointed at a file\" do\n      symlink = tmpfile(\"somefile_link\")\n      Puppet::FileSystem.symlink(file, symlink)\n      ::File.delete(file)\n      Puppet::FileSystem.unlink(symlink)\n\n      expect(Puppet::FileSystem).to_not be_exist(file)\n      expect(Puppet::FileSystem).to_not be_exist(symlink)\n    end\n\n    it \"should be able to unlink a dangling symlink pointed at a directory\" do\n      symlink = tmpfile(\"somedir_link\")\n      Puppet::FileSystem.symlink(dir, symlink)\n      Dir.rmdir(dir)\n      Puppet::FileSystem.unlink(symlink)\n\n      expect(Puppet::FileSystem).to_not be_exist(dir)\n      expect(Puppet::FileSystem).to_not be_exist(symlink)\n    end\n\n    it \"should delete only the symlink and not the target when calling unlink instance method\" do\n      [file, dir].each do |target|\n        symlink = tmpfile(\"#{Puppet::FileSystem.basename(target)}_link\")\n        Puppet::FileSystem.symlink(target, symlink)\n\n        expect(Puppet::FileSystem.exist?(target)).to be_truthy\n        expect(Puppet::FileSystem.readlink(symlink)).to eq(target.to_s)\n\n        expect(Puppet::FileSystem.unlink(symlink)).to eq(1) # count of files\n\n        expect(Puppet::FileSystem.exist?(target)).to be_truthy\n        expect(Puppet::FileSystem.exist?(symlink)).to be_falsey\n      end\n    end\n\n    it \"should delete only the symlink and not the target when calling unlink class method\" do\n      [file, dir].each do |target|\n        symlink = tmpfile(\"#{Puppet::FileSystem.basename(target)}_link\")\n        Puppet::FileSystem.symlink(target, symlink)\n\n        expect(Puppet::FileSystem.exist?(target)).to be_truthy\n        expect(Puppet::FileSystem.readlink(symlink)).to eq(target.to_s)\n\n        expect(Puppet::FileSystem.unlink(symlink)).to eq(1)  # count of files\n\n        expect(Puppet::FileSystem.exist?(target)).to be_truthy\n        expect(Puppet::FileSystem.exist?(symlink)).to be_falsey\n      end\n    end\n\n    describe \"unlink\" do\n      it \"should delete files with unlink\" do\n        expect(Puppet::FileSystem.exist?(file)).to be_truthy\n\n        expect(Puppet::FileSystem.unlink(file)).to eq(1)  # count of files\n\n        expect(Puppet::FileSystem.exist?(file)).to be_falsey\n      end\n\n      it \"should delete files with unlink class method\" do\n        expect(Puppet::FileSystem.exist?(file)).to be_truthy\n\n        expect(Puppet::FileSystem.unlink(file)).to eq(1)  # count of files\n\n        expect(Puppet::FileSystem.exist?(file)).to be_falsey\n      end\n\n      it \"should delete multiple files with unlink class method\" do\n        paths = (1..3).collect do |i|\n          f = tmpfile(\"somefile_#{i}\")\n          FileUtils.touch(f)\n          expect(Puppet::FileSystem.exist?(f)).to be_truthy\n          f.to_s\n        end\n\n        expect(Puppet::FileSystem.unlink(*paths)).to eq(3)  # count of files\n\n        paths.each { |p| expect(Puppet::FileSystem.exist?(p)).to be_falsey  }\n      end\n\n      it \"should raise Errno::EPERM or Errno::EISDIR when trying to delete a directory with the unlink class method\" do\n        expect(Puppet::FileSystem.exist?(dir)).to be_truthy\n\n        ex = nil\n        begin\n          Puppet::FileSystem.unlink(dir)\n        rescue Exception => e\n          ex = e\n        end\n\n        expect([\n          Errno::EPERM, # Windows and OSX\n          Errno::EISDIR # Linux\n        ]).to include(ex.class)\n\n        expect(Puppet::FileSystem.exist?(dir)).to be_truthy\n      end\n\n      it \"should raise Errno::EACCESS when trying to delete a file whose parent directory does not allow execute/traverse\", unless: Puppet::Util::Platform.windows? do\n        dir = tmpdir('file_system_unlink')\n        path = File.join(dir, 'deleteme')\n        mode = Puppet::FileSystem.stat(dir).mode\n        Puppet::FileSystem.chmod(0, dir)\n        begin\n          # JRuby 9.2.21.0 drops the path from the message..\n          message = Puppet::Util::Platform.jruby? ? /^Permission denied/ : /^Permission denied .* #{path}/\n          expect {\n            Puppet::FileSystem.unlink(path)\n          }.to raise_error(Errno::EACCES, message)\n        ensure\n          Puppet::FileSystem.chmod(mode, dir)\n        end\n      end\n    end\n\n    describe \"exclusive_create\" do\n      it \"should create a file that doesn't exist\" do\n        expect(Puppet::FileSystem.exist?(missing_file)).to be_falsey\n\n        Puppet::FileSystem.exclusive_create(missing_file, nil) {}\n\n        expect(Puppet::FileSystem.exist?(missing_file)).to be_truthy\n      end\n\n      it \"should raise Errno::EEXIST creating a file that does exist\" do\n        expect(Puppet::FileSystem.exist?(file)).to be_truthy\n\n        expect do\n          Puppet::FileSystem.exclusive_create(file, nil) {}\n        end.to raise_error(Errno::EEXIST)\n      end\n    end\n\n    describe 'expand_path' do\n      it 'should raise an error when given nil, like Ruby File.expand_path' do\n        expect { File.expand_path(nil) }.to raise_error(TypeError)\n\n        # match Ruby behavior\n        expect { Puppet::FileSystem.expand_path(nil) }.to raise_error(TypeError)\n      end\n\n      it 'with an expanded path passed to Dir.glob, the same expanded path will be returned' do\n        # this exists specifically for Puppet::Pops::Loader::ModuleLoaders::FileBased#add_to_index\n        # which should receive an expanded path value from it's parent Environment\n        # and will later compare values generated by Dir.glob\n        tmp_long_file = tmpfile('foo.bar', tmpdir('super-long-thing-that-Windows-shortens'))\n        Puppet::FileSystem.touch(tmp_long_file)\n        expanded_path = Puppet::FileSystem.expand_path(tmp_long_file)\n\n        expect(expanded_path).to eq(Dir.glob(expanded_path).first)\n      end\n\n      describe 'on non-Windows', :unless => Puppet::Util::Platform.windows? do\n        it 'should produce the same results as the Ruby File.expand_path' do\n          # on Windows this may be 8.3 style, but not so on other platforms\n          # only done since expect(::File).to receive(:expand_path).with(path).at_least(:once)\n          # cannot be used since it will cause a stack overflow\n          path = tmpdir('foobar')\n\n          expect(Puppet::FileSystem.expand_path(path)).to eq(File.expand_path(path))\n        end\n      end\n\n      describe 'on Windows', :if => Puppet::Util::Platform.windows? do\n        let(:nonexist_file) { 'C:\\\\file~1.ext' }\n        let(:nonexist_path) { 'C:\\\\progra~1\\\\missing\\\\path\\\\file.ext' }\n\n        ['/', '\\\\'].each do |slash|\n          it \"should return the absolute path including system drive letter when given #{slash}, like Ruby File.expand_path\" do\n\n            # regardless of slash direction, return value is drive letter\n            expanded = Puppet::FileSystem.expand_path(slash)\n            expect(expanded).to match(/^[a-z]:/i)\n          end\n        end\n\n        it 'should behave like Rubys File.expand_path for a file that doesnt exist' do\n          expect(Puppet::FileSystem.exist?(nonexist_file)).to be_falsey\n          # this will change c:\\\\file~1.ext to c:/file~1.ext (existing Ruby behavior), but not expand any ~\n          ruby_expanded = File.expand_path(nonexist_file)\n          expect(ruby_expanded).to match(/~/)\n          expect(Puppet::FileSystem.expand_path(nonexist_file)).to eq(ruby_expanded)\n        end\n\n        it 'should behave like Rubys File.expand_path for a file with a parent path that doesnt exist' do\n          expect(Puppet::FileSystem.exist?(nonexist_path)).to be_falsey\n          # this will change c:\\\\progra~1 to c:/progra~1 (existing Ruby behavior), but not expand any ~\n          ruby_expanded = File.expand_path(nonexist_path)\n          expect(ruby_expanded).to match(/~/)\n          expect(Puppet::FileSystem.expand_path(nonexist_path)).to eq(ruby_expanded)\n        end\n\n        it 'should expand a shortened path completely, unlike Ruby File.expand_path' do\n          pending(\"GH runners seem to have disabled 8.3 support\")\n\n          tmp_long_dir = tmpdir('super-long-thing-that-Windows-shortens')\n          short_path = Puppet::Util::Windows::File.get_short_pathname(tmp_long_dir)\n\n          # a shortened path to the temp dir will have a least 2 ~\n          # for instance, C:\\\\Users\\\\Administrator\\\\AppData\\\\Local\\\\Temp\\\\rspecrun2016####-####-#######\\\\super-long-thing-that-Windows-shortens\\\n          # or C:\\\\Windows\\\\Temp\\\\rspecrun2016####-####-#######\\\\super-long-thing-that-Windows-shortens\\\n          # will shorten to Temp\\\\rspecr~#\\\\super-~1\n          expect(short_path).to match(/~.*~/)\n\n          # expand with Ruby, noting not all ~ have been expanded\n          # which is the primary reason that a Puppet helper exists\n          ruby_expanded = File.expand_path(short_path)\n          expect(ruby_expanded).to match(/~/)\n\n          # Puppet expansion uses the Windows API and has no ~ remaining\n          puppet_expanded = Puppet::FileSystem.expand_path(short_path)\n          expect(puppet_expanded).to_not match(/~/)\n\n          # and the directories are one and the same\n          expect(File.identical?(short_path, puppet_expanded)).to be_truthy\n        end\n      end\n    end\n  end\n\n  describe '#replace_file' do\n    let(:dest) { tmpfile('replace_file') }\n    let(:content) { \"some data\" }\n\n    context 'when creating' do\n      it 'writes the data' do\n        Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n        expect(Puppet::FileSystem.binread(dest)).to eq(content)\n      end\n\n      it 'writes in binary mode' do\n        Puppet::FileSystem.replace_file(dest) { |f| f.write(\"\\x00\\x01\\x02\") }\n\n        expect(Puppet::FileSystem.binread(dest)).to eq(\"\\x00\\x01\\x02\")\n      end\n\n      context 'on posix', unless: Puppet::Util::Platform.windows? do\n        it 'applies the default mode 0640' do\n          Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n          mode = Puppet::FileSystem.stat(dest).mode\n          expect(mode & 07777).to eq(0640)\n        end\n\n        it 'applies the specified mode' do\n          Puppet::FileSystem.replace_file(dest, 0777) { |f| f.write(content) }\n\n          mode = Puppet::FileSystem.stat(dest).mode\n          expect(mode & 07777).to eq(0777)\n        end\n\n        it 'raises EACCES if we do not have permission' do\n          dir = tmpdir('file_system')\n          dest = File.join(dir, 'unwritable')\n\n          Puppet::FileSystem.chmod(0600, dir)\n\n          expect {\n            Puppet::FileSystem.replace_file(dest) { |_| }\n          }.to raise_error(Errno::EACCES, /Permission denied/)\n        end\n\n        it 'creates a read-only file' do\n          Puppet::FileSystem.replace_file(dest, 0400) { |f| f.write(content) }\n\n          expect(Puppet::FileSystem.binread(dest)).to eq(content)\n\n          mode = Puppet::FileSystem.stat(dest).mode\n          expect(mode & 07777).to eq(0400)\n        end\n\n        it 'preserves file ownership' do\n          FileUtils.touch(dest)\n          allow(File).to receive(:lstat).and_call_original\n          allow(File).to receive(:lstat).with(Pathname.new(dest)).and_return(double(uid: 1, gid: 2, 'directory?': false))\n\n          allow(File).to receive(:chown).and_call_original\n          expect(FileUtils).to receive(:chown).with(1, 2, any_args)\n\n          Puppet::FileSystem.replace_file(dest, 0644) { |f| f.write(content) }\n        end\n      end\n\n      context 'on windows', if: Puppet::Util::Platform.windows? do\n        it 'does not grant users access by default' do\n          Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n          expects_private_file(dest)\n        end\n\n        it 'applies the specified mode' do\n          Puppet::FileSystem.replace_file(dest, 0644) { |f| f.write(content) }\n\n          expects_public_file(dest)\n        end\n\n        it 'rejects unsupported modes' do\n          expect {\n            Puppet::FileSystem.replace_file(dest, 0755) { |_| }\n          }.to raise_error(ArgumentError, /Only modes 0644, 0640, 0660, and 0440 are allowed/)\n        end\n\n        it 'falls back to fully qualified user name when sid retrieval fails' do\n          current_user_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n          allow(Puppet::Util::Windows::SID).to receive(:name_to_sid).with(Puppet::Util::Windows::ADSI::User.current_user_name).and_return(nil, current_user_sid)\n          allow(Puppet::Util::Windows::SID).to receive(:name_to_sid).with(Puppet::Util::Windows::ADSI::User.current_sam_compatible_user_name).and_call_original\n\n          Puppet::FileSystem.replace_file(dest, 0644) { |f| f.write(content) }\n          expects_public_file(dest)\n        end\n      end\n    end\n\n    context \"when overwriting\" do\n      before :each do\n        FileUtils.touch(dest)\n      end\n\n      it 'overwrites the content' do\n        Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n        expect(Puppet::FileSystem.binread(dest)).to eq(content)\n      end\n\n      it 'raises ISDIR if the destination is a directory' do\n        dir = tmpdir('file_system')\n\n        expect {\n          Puppet::FileSystem.replace_file(dir) { |f| f.write(content) }\n        }.to raise_error(Errno::EISDIR, /Is a directory/)\n      end\n\n      it 'preserves the existing content if an error is raised' do\n        File.write(dest, 'existing content')\n\n        Puppet::FileSystem.replace_file(dest) { |f| raise 'whoops' } rescue nil\n\n        expect(Puppet::FileSystem.binread(dest)).to eq('existing content')\n      end\n\n      context 'on posix', unless: Puppet::Util::Platform.windows? do\n        it 'preserves the existing mode' do\n          Puppet::FileSystem.chmod(0600, dest)\n\n          Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n          mode = Puppet::FileSystem.stat(dest).mode\n          expect(mode & 07777).to eq(0600)\n        end\n\n        it 'applies the specified mode' do\n          Puppet::FileSystem.chmod(0600, dest)\n\n          Puppet::FileSystem.replace_file(dest, 0777) { |f| f.write(content) }\n\n          mode = Puppet::FileSystem.stat(dest).mode\n          expect(mode & 07777).to eq(0777)\n        end\n\n        it 'updates a read-only file' do\n          Puppet::FileSystem.chmod(0400, dest)\n\n          Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n          expect(Puppet::FileSystem.binread(dest)).to eq(content)\n\n          mode = Puppet::FileSystem.stat(dest).mode\n          expect(mode & 07777).to eq(0400)\n        end\n      end\n\n      context 'on windows', if: Puppet::Util::Platform.windows? do\n        it 'preserves the existing mode' do\n          old_sd = Puppet::Util::Windows::Security.get_security_descriptor(dest)\n\n          Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n\n          new_sd = Puppet::Util::Windows::Security.get_security_descriptor(dest)\n          expect(old_sd.owner).to eq(new_sd.owner)\n          expect(old_sd.group).to eq(new_sd.group)\n          old_sd.dacl.each do |ace|\n            expect(new_sd.dacl).to include(an_object_having_attributes(sid: ace.sid, mask: ace.mask))\n          end\n        end\n\n        it 'applies 0644 mode' do\n          Puppet::FileSystem.replace_file(dest, 0644) { |f| f.write(content) }\n\n          expects_public_file(dest)\n        end\n\n        [0660, 0640, 0600, 0440].each do |mode|\n          it \"applies #{mode} mode\" do\n            Puppet::FileSystem.replace_file(dest, mode) { |f| f.write(content) }\n            current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n            sd = Puppet::Util::Windows::Security.get_security_descriptor(dest)\n\n            expect(sd.dacl).to contain_exactly(\n              an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),\n              an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),\n              an_object_having_attributes(sid: current_sid, mask: 0x1f01ff),\n            )\n          end\n        end\n\n        it 'raises Errno::EACCES if access is denied' do\n          allow(Puppet::Util::Windows::Security).to receive(:get_security_descriptor).and_raise(Puppet::Util::Windows::Error.new('access denied', 5))\n\n          expect {\n            Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n          }.to raise_error(Errno::EACCES, /Access is denied/)\n        end\n\n        it 'raises SystemCallError otherwise' do\n          allow(Puppet::Util::Windows::Security).to receive(:get_security_descriptor).and_raise(Puppet::Util::Windows::Error.new('arena is trashed', 7))\n\n          expect {\n            Puppet::FileSystem.replace_file(dest) { |f| f.write(content) }\n          }.to raise_error(SystemCallError, /The storage control blocks were destroyed/)\n        end\n      end\n    end\n  end\n\n  context '#touch' do\n    let(:dest) { tmpfile('touch_file') }\n\n    it 'creates a file' do\n      Puppet::FileSystem.touch(dest)\n\n      expect(File).to be_file(dest)\n    end\n\n    it 'updates the mtime for an existing file' do\n      Puppet::FileSystem.touch(dest)\n\n      now = Time.now\n      allow(Time).to receive(:now).and_return(now)\n\n      Puppet::FileSystem.touch(dest)\n\n      expect(File.mtime(dest)).to be_within(1).of(now)\n    end\n\n    it 'allows the mtime to be passed in' do\n      tomorrow = Time.now + (24 * 60 * 60)\n\n      Puppet::FileSystem.touch(dest, mtime: tomorrow)\n\n      expect(File.mtime(dest)).to be_within(1).of(tomorrow)\n    end\n  end\n\n  context '#chmod' do\n    let(:dest) { tmpfile('abs_file') }\n\n    it \"changes the mode given an absolute string\" do\n      Puppet::FileSystem.touch(dest)\n      Puppet::FileSystem.chmod(0644, dest)\n      expect(File.stat(dest).mode & 0777).to eq(0644)\n    end\n\n    it \"returns true if given an absolute pathname\" do\n      Puppet::FileSystem.touch(dest)\n      Puppet::FileSystem.chmod(0644, Pathname.new(dest))\n      expect(File.stat(dest).mode & 0777).to eq(0644)\n    end\n\n    it \"raises if the file doesn't exist\" do\n      klass = Puppet::Util::Platform.windows? ? Puppet::Error : Errno::ENOENT\n      expect {\n        Puppet::FileSystem.chmod(0644, dest)\n      }.to raise_error(klass)\n    end\n\n    it \"raises ArgumentError if dest is invalid\" do\n      expect {\n        Puppet::FileSystem.chmod(0644, nil)\n      }.to raise_error(ArgumentError, /expected Pathname, got: 'NilClass'/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/forge/errors_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/forge'\n\ndescribe Puppet::Forge::Errors do\n  describe 'SSLVerifyError' do\n    subject { Puppet::Forge::Errors::SSLVerifyError }\n    let(:exception) { subject.new(:uri => 'https://fake.com:1111') }\n\n    it 'should return a valid single line error' do\n      expect(exception.message).to eq('Unable to verify the SSL certificate at https://fake.com:1111')\n    end\n\n    it 'should return a valid multiline error' do\n      expect(exception.multiline).to eq <<-EOS.chomp\nCould not connect via HTTPS to https://fake.com:1111\n  Unable to verify the SSL certificate\n    The certificate may not be signed by a valid CA\n    The CA bundle included with OpenSSL may not be valid or up to date\n      EOS\n    end\n  end\n\n  describe 'CommunicationError' do\n    subject { Puppet::Forge::Errors::CommunicationError }\n    let(:socket_exception) { SocketError.new('There was a problem') }\n    let(:exception) { subject.new(:uri => 'http://fake.com:1111', :original => socket_exception) }\n\n    it 'should return a valid single line error' do\n      expect(exception.message).to eq('Unable to connect to the server at http://fake.com:1111. Detail: There was a problem.')\n    end\n\n    it 'should return a valid multiline error' do\n      expect(exception.multiline).to eq <<-EOS.chomp\nCould not connect to http://fake.com:1111\n  There was a network communications problem\n    The error we caught said 'There was a problem'\n    Check your network connection and try again\n      EOS\n    end\n  end\n\n  describe 'ResponseError' do\n    subject { Puppet::Forge::Errors::ResponseError }\n    let(:response) { double(:body => '{}', :code => '404', :reason => \"not found\") }\n\n    context 'without message' do\n      let(:exception) { subject.new(:uri => 'http://fake.com:1111', :response => response, :input => 'user/module') }\n\n      it 'should return a valid single line error' do\n        expect(exception.message).to eq('Request to Puppet Forge failed. Detail: 404 not found.')\n      end\n\n      it 'should return a valid multiline error' do\n        expect(exception.multiline).to eq <<-eos.chomp\nRequest to Puppet Forge failed.\n  The server being queried was http://fake.com:1111\n  The HTTP response we received was '404 not found'\n        eos\n      end\n    end\n\n    context 'with message' do\n      let(:exception) { subject.new(:uri => 'http://fake.com:1111', :response => response, :input => 'user/module', :message => 'no such module') }\n\n      it 'should return a valid single line error' do\n        expect(exception.message).to eq('Request to Puppet Forge failed. Detail: no such module / 404 not found.')\n      end\n\n      it 'should return a valid multiline error' do\n        expect(exception.multiline).to eq <<-eos.chomp\nRequest to Puppet Forge failed.\n  The server being queried was http://fake.com:1111\n  The HTTP response we received was '404 not found'\n  The message we received said 'no such module'\n        eos\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/forge/forge_spec.rb",
    "content": "# encoding: utf-8\nrequire 'spec_helper'\nrequire 'net/http'\nrequire 'puppet/forge/repository'\n\ndescribe Puppet::Forge do\n  before(:all) do\n    # any local http proxy will break these tests\n    ENV['http_proxy'] = nil\n    ENV['HTTP_PROXY'] = nil\n  end\n\n  let(:host) { 'http://fake.com' }\n  let(:forge) { Puppet::Forge.new(host) }\n\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  let (:mixed_utf8_query_param) { \"foo + A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ\n  let (:mixed_utf8_query_param_encoded) { \"foo%20%2B%20A%DB%BF%E1%9A%A0%F0%A0%9C%8E\"}\n  let (:empty_json) { '{ \"results\": [], \"pagination\" : { \"next\" : null } }' }\n\n  describe \"making a\" do\n    before :each do\n      Puppet[:http_proxy_host] = \"proxy\"\n      Puppet[:http_proxy_port] = 1234\n    end\n\n    context \"search request\" do\n      it \"includes any defined module_groups, ensuring to only encode them once in the URI\" do\n        Puppet[:module_groups] = 'base+pe'\n        encoded_uri = \"#{host}/v3/modules?query=#{mixed_utf8_query_param_encoded}&module_groups=base%20pe\"\n        stub_request(:get, encoded_uri).to_return(status: 200, body: empty_json)\n\n        forge.search(mixed_utf8_query_param)\n      end\n\n      it \"single encodes the search term in the URI\" do\n        encoded_uri = \"#{host}/v3/modules?query=#{mixed_utf8_query_param_encoded}\"\n        stub_request(:get, encoded_uri).to_return(status: 200, body: empty_json)\n\n        forge.search(mixed_utf8_query_param)\n      end\n    end\n\n    context \"fetch request\" do\n      it \"includes any defined module_groups, ensuring to only encode them once in the URI\" do\n        Puppet[:module_groups] = 'base+pe'\n        module_name = 'puppetlabs-acl'\n        exclusions = \"readme%2Cchangelog%2Clicense%2Curi%2Cmodule%2Ctags%2Csupported%2Cfile_size%2Cdownloads%2Ccreated_at%2Cupdated_at%2Cdeleted_at\"\n        encoded_uri = \"#{host}/v3/releases?module=#{module_name}&sort_by=version&exclude_fields=#{exclusions}&module_groups=base%20pe\"\n        stub_request(:get, encoded_uri).to_return(status: 200, body: empty_json)\n\n        forge.fetch(module_name)\n      end\n\n      it \"single encodes the module name term in the URI\" do\n        module_name = \"puppetlabs-#{mixed_utf8_query_param}\"\n        exclusions = \"readme%2Cchangelog%2Clicense%2Curi%2Cmodule%2Ctags%2Csupported%2Cfile_size%2Cdownloads%2Ccreated_at%2Cupdated_at%2Cdeleted_at\"\n        encoded_uri = \"#{host}/v3/releases?module=puppetlabs-#{mixed_utf8_query_param_encoded}&sort_by=version&exclude_fields=#{exclusions}\"\n        stub_request(:get, encoded_uri).to_return(status: 200, body: empty_json)\n\n        forge.fetch(module_name)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/forge/module_release_spec.rb",
    "content": "# encoding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/forge'\nrequire 'net/http'\nrequire 'puppet/module_tool'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Forge::ModuleRelease do\n  include PuppetSpec::Files\n\n  let(:agent) { \"Test/1.0\" }\n  let(:repository) { Puppet::Forge::Repository.new('http://fake.com', agent) }\n  let(:ssl_repository) { Puppet::Forge::Repository.new('https://fake.com', agent) }\n  let(:api_version) { \"v3\" }\n  let(:module_author) { \"puppetlabs\" }\n  let(:module_name) { \"stdlib\" }\n  let(:module_version) { \"4.1.0\" }\n  let(:module_full_name) { \"#{module_author}-#{module_name}\" }\n  let(:module_full_name_versioned) { \"#{module_full_name}-#{module_version}\" }\n  let(:module_md5) { \"bbf919d7ee9d278d2facf39c25578bf8\" }\n  let(:module_sha256) { \"b4c6f15cec64a9fe16ef0d291e2598fc84f381bc59f0e67198d61706fafedae4\" }\n  let(:uri) { \" \"}\n  let(:release) { Puppet::Forge::ModuleRelease.new(ssl_repository, JSON.parse(release_json)) }\n\n  let(:mock_file) { double('file', path: '/dev/null') }\n  let(:mock_dir) { tmpdir('dir') }\n\n  let(:destination) { tmpfile('forge_module_release') }\n\n  shared_examples 'a module release' do\n    def mock_digest_file_with_md5(md5)\n      allow(Digest::MD5).to receive(:file).and_return(double(:hexdigest => md5))\n    end\n\n    describe '#tmpfile' do\n      it 'should be opened in binary mode' do\n        allow(Puppet::Forge::Cache).to receive(:base_path).and_return(Dir.tmpdir)\n        expect(release.send(:tmpfile).binmode?).to be_truthy\n      end\n    end\n\n    describe '#download' do\n      it 'should download a file' do\n        stub_request(:get, \"https://fake.com/#{api_version}/files/#{module_full_name_versioned}.tar.gz\").to_return(status: 200, body: '{}')\n\n        File.open(destination, 'wb') do |fh|\n          release.send(:download, \"/#{api_version}/files/#{module_full_name_versioned}.tar.gz\", fh)\n        end\n\n        expect(File.read(destination)).to eq(\"{}\")\n      end\n\n      it 'should raise a response error when it receives an error from forge' do\n        stub_request(:get, \"https://fake.com/some/path\").to_return(\n          status: [500, 'server error'],\n          body: '{\"error\":\"invalid module\"}'\n        )\n        expect {\n          release.send(:download, \"/some/path\", StringIO.new)\n        }.to raise_error Puppet::Forge::Errors::ResponseError\n      end\n    end\n\n    describe '#unpack' do\n      it 'should call unpacker with correct params' do\n        expect(Puppet::ModuleTool::Applications::Unpacker).to receive(:unpack).with(mock_file.path, mock_dir).and_return(true)\n\n        release.send(:unpack, mock_file, mock_dir)\n      end\n    end\n  end\n\n  context 'standard forge module' do\n    let(:release_json) do %Q{\n    {\n      \"uri\": \"/#{api_version}/releases/#{module_full_name_versioned}\",\n      \"module\": {\n        \"uri\": \"/#{api_version}/modules/#{module_full_name}\",\n        \"name\": \"#{module_name}\",\n        \"owner\": {\n          \"uri\": \"/#{api_version}/users/#{module_author}\",\n          \"username\": \"#{module_author}\",\n          \"gravatar_id\": \"fdd009b7c1ec96e088b389f773e87aec\"\n        }\n      },\n      \"version\": \"#{module_version}\",\n      \"metadata\": {\n        \"types\": [ ],\n        \"license\": \"Apache 2.0\",\n        \"checksums\": { },\n        \"version\": \"#{module_version}\",\n        \"description\": \"Standard Library for Puppet Modules\",\n        \"source\": \"https://github.com/puppetlabs/puppetlabs-stdlib\",\n        \"project_page\": \"https://github.com/puppetlabs/puppetlabs-stdlib\",\n        \"summary\": \"Puppet Module Standard Library\",\n        \"dependencies\": [\n\n        ],\n        \"author\": \"#{module_author}\",\n        \"name\": \"#{module_full_name}\"\n      },\n      \"tags\": [\n        \"puppetlabs\",\n        \"library\",\n        \"stdlib\",\n        \"standard\",\n        \"stages\"\n      ],\n      \"file_uri\": \"/#{api_version}/files/#{module_full_name_versioned}.tar.gz\",\n      \"file_size\": 67586,\n      \"file_md5\": \"#{module_md5}\",\n      \"file_sha256\": \"#{module_sha256}\",\n      \"downloads\": 610751,\n      \"readme\": \"\",\n      \"changelog\": \"\",\n      \"license\": \"\",\n      \"created_at\": \"2013-05-13 08:31:19 -0700\",\n      \"updated_at\": \"2013-05-13 08:31:19 -0700\",\n      \"deleted_at\": null\n    }\n    }\n    end\n\n    it_behaves_like 'a module release'\n\n    context 'when verifying checksums' do\n      let(:json) { JSON.parse(release_json) }\n\n      def mock_release(json)\n        release = Puppet::Forge::ModuleRelease.new(ssl_repository, json)\n        allow(release).to receive(:tmpfile).and_return(mock_file)\n        allow(release).to receive(:tmpdir).and_return(mock_dir)\n        allow(release).to receive(:download).with(\"/#{api_version}/files/#{module_full_name_versioned}.tar.gz\", mock_file)\n        allow(release).to receive(:unpack)\n        release\n      end\n\n      it 'verifies using SHA256' do\n        expect(Digest::SHA256).to receive(:file).and_return(double(:hexdigest => module_sha256))\n\n        release = mock_release(json)\n        release.prepare\n      end\n\n      it 'rejects an invalid release with SHA256' do\n        expect(Digest::SHA256).to receive(:file).and_return(double(:hexdigest => 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'))\n\n        release = mock_release(json)\n        expect {\n          release.prepare\n        }.to raise_error(RuntimeError, /did not match expected checksum/)\n      end\n\n      context 'when `file_sha256` is missing' do\n        before(:each) do\n          json.delete('file_sha256')\n        end\n\n        it 'verifies using MD5 if `file_sha256` is missing' do\n          expect(Digest::MD5).to receive(:file).and_return(double(:hexdigest => module_md5))\n\n          release = mock_release(json)\n          release.prepare\n        end\n\n        it 'rejects an invalid release with MD5' do\n          expect(Digest::MD5).to receive(:file).and_return(double(:hexdigest => 'ffffffffffffffffffffffffffffffff'))\n\n          release = mock_release(json)\n          expect {\n            release.prepare\n          }.to raise_error(RuntimeError, /did not match expected checksum/)\n        end\n\n        it 'raises if FIPS is enabled' do\n          allow(Facter).to receive(:value).with(:fips_enabled).and_return(true)\n\n          release = mock_release(json)\n          expect {\n            release.prepare\n          }.to raise_error(/Module install using MD5 is prohibited in FIPS mode./)\n        end\n      end\n    end\n  end\n\n  context 'forge module with no dependencies field' do\n    let(:release_json) do %Q{\n    {\n      \"uri\": \"/#{api_version}/releases/#{module_full_name_versioned}\",\n      \"module\": {\n        \"uri\": \"/#{api_version}/modules/#{module_full_name}\",\n        \"name\": \"#{module_name}\",\n        \"owner\": {\n          \"uri\": \"/#{api_version}/users/#{module_author}\",\n          \"username\": \"#{module_author}\",\n          \"gravatar_id\": \"fdd009b7c1ec96e088b389f773e87aec\"\n        }\n      },\n      \"version\": \"#{module_version}\",\n      \"metadata\": {\n        \"types\": [ ],\n        \"license\": \"Apache 2.0\",\n        \"checksums\": { },\n        \"version\": \"#{module_version}\",\n        \"description\": \"Standard Library for Puppet Modules\",\n        \"source\": \"https://github.com/puppetlabs/puppetlabs-stdlib\",\n        \"project_page\": \"https://github.com/puppetlabs/puppetlabs-stdlib\",\n        \"summary\": \"Puppet Module Standard Library\",\n        \"author\": \"#{module_author}\",\n        \"name\": \"#{module_full_name}\"\n      },\n      \"tags\": [\n        \"puppetlabs\",\n        \"library\",\n        \"stdlib\",\n        \"standard\",\n        \"stages\"\n      ],\n      \"file_uri\": \"/#{api_version}/files/#{module_full_name_versioned}.tar.gz\",\n      \"file_size\": 67586,\n      \"file_md5\": \"#{module_md5}\",\n      \"file_sha256\": \"#{module_sha256}\",\n      \"downloads\": 610751,\n      \"readme\": \"\",\n      \"changelog\": \"\",\n      \"license\": \"\",\n      \"created_at\": \"2013-05-13 08:31:19 -0700\",\n      \"updated_at\": \"2013-05-13 08:31:19 -0700\",\n      \"deleted_at\": null\n    }\n    }\n    end\n\n    it_behaves_like 'a module release'\n  end\n\n  context 'forge module with the minimal set of fields' do\n    let(:release_json) do %Q{\n    {\n      \"uri\": \"/#{api_version}/releases/#{module_full_name_versioned}\",\n      \"module\": {\n        \"uri\": \"/#{api_version}/modules/#{module_full_name}\",\n        \"name\": \"#{module_name}\"\n      },\n      \"metadata\": {\n        \"version\": \"#{module_version}\",\n        \"name\": \"#{module_full_name}\"\n      },\n      \"file_uri\": \"/#{api_version}/files/#{module_full_name_versioned}.tar.gz\",\n      \"file_size\": 67586,\n      \"file_md5\": \"#{module_md5}\",\n      \"file_sha256\": \"#{module_sha256}\"\n    }\n    }\n    end\n\n    it_behaves_like 'a module release'\n  end\n\n  context 'deprecated forge module' do\n    let(:release_json) do %Q{\n    {\n      \"uri\": \"/#{api_version}/releases/#{module_full_name_versioned}\",\n      \"module\": {\n        \"uri\": \"/#{api_version}/modules/#{module_full_name}\",\n        \"name\": \"#{module_name}\",\n        \"deprecated_at\": \"2017-10-10 10:21:32 -0700\",\n        \"owner\": {\n          \"uri\": \"/#{api_version}/users/#{module_author}\",\n          \"username\": \"#{module_author}\",\n          \"gravatar_id\": \"fdd009b7c1ec96e088b389f773e87aec\"\n        }\n      },\n      \"version\": \"#{module_version}\",\n      \"metadata\": {\n        \"types\": [ ],\n        \"license\": \"Apache 2.0\",\n        \"checksums\": { },\n        \"version\": \"#{module_version}\",\n        \"description\": \"Standard Library for Puppet Modules\",\n        \"source\": \"https://github.com/puppetlabs/puppetlabs-stdlib\",\n        \"project_page\": \"https://github.com/puppetlabs/puppetlabs-stdlib\",\n        \"summary\": \"Puppet Module Standard Library\",\n        \"dependencies\": [\n\n        ],\n        \"author\": \"#{module_author}\",\n        \"name\": \"#{module_full_name}\"\n      },\n      \"tags\": [\n        \"puppetlabs\",\n        \"library\",\n        \"stdlib\",\n        \"standard\",\n        \"stages\"\n      ],\n      \"file_uri\": \"/#{api_version}/files/#{module_full_name_versioned}.tar.gz\",\n      \"file_size\": 67586,\n      \"file_md5\": \"#{module_md5}\",\n      \"file_sha256\": \"#{module_sha256}\",\n      \"downloads\": 610751,\n      \"readme\": \"\",\n      \"changelog\": \"\",\n      \"license\": \"\",\n      \"created_at\": \"2013-05-13 08:31:19 -0700\",\n      \"updated_at\": \"2013-05-13 08:31:19 -0700\",\n      \"deleted_at\": null\n    }\n    }\n    end\n\n    it_behaves_like 'a module release'\n\n    describe '#prepare' do\n      before :each do\n        allow(release).to receive(:tmpfile).and_return(mock_file)\n        allow(release).to receive(:tmpdir).and_return(mock_dir)\n        allow(release).to receive(:download)\n        allow(release).to receive(:validate_checksum)\n        allow(release).to receive(:unpack)\n      end\n\n      it 'should emit warning about module deprecation' do\n        expect(Puppet).to receive(:warning).with(/#{Regexp.escape(module_full_name)}.*deprecated/i)\n\n        release.prepare\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/forge/repository_spec.rb",
    "content": "# encoding: utf-8\nrequire 'spec_helper'\nrequire 'net/http'\nrequire 'puppet/forge/repository'\nrequire 'puppet/forge/cache'\nrequire 'puppet/forge/errors'\n\ndescribe Puppet::Forge::Repository do\n  before(:all) do\n    # any local http proxy will break these tests\n    ENV['http_proxy'] = nil\n    ENV['HTTP_PROXY'] = nil\n  end\n  let(:agent) { \"Test/1.0\" }\n  let(:repository) { Puppet::Forge::Repository.new('http://fake.com', agent) }\n\n  it \"retrieve accesses the cache\" do\n    path = '/module/foo.tar.gz'\n    expect(repository.cache).to receive(:retrieve)\n\n    repository.retrieve(path)\n  end\n\n  it \"retrieve merges forge URI and path specified\" do\n    host = 'http://fake.com/test'\n    path = '/module/foo.tar.gz'\n    uri  = [ host, path ].join('')\n\n    repository = Puppet::Forge::Repository.new(host, agent)\n    expect(repository.cache).to receive(:retrieve).with(uri)\n\n    repository.retrieve(path)\n  end\n\n  describe \"making a request\" do\n    let(:uri) { \"http://fake.com/the_path\" }\n\n    it \"returns the response object from the request\" do\n      stub_request(:get, uri)\n\n      expect(repository.make_http_request(\"/the_path\")).to be_a_kind_of(Puppet::HTTP::Response)\n    end\n\n    it \"requires path to have a leading slash\" do\n      expect {\n        repository.make_http_request(\"the_path\")\n      }.to raise_error(ArgumentError, 'Path must start with forward slash')\n    end\n\n    it \"merges forge URI and path specified\" do\n      stub_request(:get, \"http://fake.com/test/the_path/\")\n\n      repository = Puppet::Forge::Repository.new('http://fake.com/test', agent)\n      repository.make_http_request(\"/the_path/\")\n    end\n\n    it \"handles trailing slashes when merging URI and path\" do\n      stub_request(:get, \"http://fake.com/test/the_path\")\n\n      repository = Puppet::Forge::Repository.new('http://fake.com/test/', agent)\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it 'return a valid exception when there is a communication problem' do\n      stub_request(:get, uri).to_raise(SocketError.new('getaddrinfo: Name or service not known'))\n\n      expect {\n        repository.make_http_request(\"/the_path\")\n      }.to raise_error(Puppet::Forge::Errors::CommunicationError,\n                       %r{Unable to connect to the server at http://fake.com. Detail: Request to http://fake.com/the_path failed after .* seconds: getaddrinfo: Name or service not known.})\n    end\n\n    it \"sets the user agent for the request\" do\n      stub_request(:get, uri).with do |request|\n        expect(request.headers['User-Agent']).to match(/#{agent} #{Regexp.escape(Puppet[:http_user_agent])}/)\n      end\n\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"does not set Authorization header by default\" do\n      allow(Puppet.features).to receive(:pe_license?).and_return(false)\n      Puppet[:forge_authorization] = nil\n\n      stub_request(:get, uri).with do |request|\n        expect(request.headers).to_not include('Authorization')\n      end\n\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"sets Authorization header from config\" do\n      token = 'bearer some token'\n      Puppet[:forge_authorization] = token\n\n      stub_request(:get, uri).with(headers: {'Authorization' => token})\n\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"sets Authorization header from PE license\" do\n      allow(Puppet.features).to receive(:pe_license?).and_return(true)\n      stub_const(\"PELicense\", double(load_license_key: double(authorization_token: \"opensesame\")))\n\n      stub_request(:get, uri).with(headers: {'Authorization' => \"opensesame\"})\n\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"sets basic authentication if there isn't forge authorization or PE license\" do\n      stub_request(:get, uri).with(basic_auth: ['user1', 'password'])\n\n      repository = Puppet::Forge::Repository.new('http://user1:password@fake.com', agent)\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"omits basic authentication if there is a forge authorization\" do\n      token = 'bearer some token'\n      Puppet[:forge_authorization] = token\n      stub_request(:get, uri).with(headers: {'Authorization' => token})\n\n      repository = Puppet::Forge::Repository.new('http://user1:password@fake.com', agent)\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"encodes the URI path\" do\n      stub_request(:get, \"http://fake.com/h%C3%A9llo%20world%20!!%20%C3%A7%20%C3%A0\")\n\n      repository.make_http_request(\"/héllo world !! ç à\")\n    end\n\n    it \"connects via proxy\" do\n      Puppet[:http_proxy_host] = 'proxy'\n      Puppet[:http_proxy_port] = 1234\n\n      stub_request(:get, uri)\n      expect(Net::HTTP).to receive(:new).with(anything, anything, 'proxy', 1234, nil, nil).and_call_original\n\n      repository.make_http_request(\"/the_path\")\n    end\n\n    it \"connects via authenticating proxy\" do\n      Puppet[:http_proxy_host] = 'proxy'\n      Puppet[:http_proxy_port] = 1234\n      Puppet[:http_proxy_user] = 'user1'\n      Puppet[:http_proxy_password] = 'password'\n\n      stub_request(:get, uri)\n      expect(Net::HTTP).to receive(:new).with(anything, anything, 'proxy', 1234, \"user1\", \"password\").and_call_original\n\n      repository.make_http_request(\"/the_path\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/forge_spec.rb",
    "content": "require 'spec_helper'\nrequire 'spec_helper'\nrequire 'net/http'\nrequire 'puppet/forge'\nrequire 'puppet/module_tool'\n\ndescribe Puppet::Forge do\n\n  let(:http_response) do\n    File.read(my_fixture('bacula.json'))\n  end\n\n  let(:search_results) do\n    JSON.parse(http_response)['results'].map do |hash|\n      hash.merge(\n        \"author\" => \"puppetlabs\",\n        \"name\" => \"bacula\",\n        \"tag_list\" => [\"backup\", \"bacula\"],\n        \"full_name\" => \"puppetlabs/bacula\",\n        \"version\" => \"0.0.2\",\n        \"project_url\" => \"https://github.com/puppetlabs/puppetlabs-bacula\",\n        \"desc\" => \"bacula\"\n      )\n    end\n  end\n\n  let(:release_response) do\n    releases = JSON.parse(http_response)\n    releases['results'] = []\n    JSON.dump(releases)\n  end\n\n  let(:forge) { Puppet::Forge.new }\n\n  it \"returns a list of matches from the forge when there are matches for the search term\" do\n    stub_request(:get, \"https://forgeapi.puppet.com/v3/modules?query=bacula\").to_return(status: 200, body: http_response)\n\n    expect(forge.search('bacula')).to eq(search_results)\n  end\n\n  context \"when module_groups are defined\" do\n    before :each do\n      Puppet[:module_groups] = \"foo\"\n    end\n\n    it \"passes module_groups with search\" do\n      stub_request(:get, \"https://forgeapi.puppet.com/v3/modules\")\n        .with(query: hash_including(\"module_groups\" => \"foo\"))\n        .to_return(status: 200, body: release_response)\n\n      forge.search('bacula')\n    end\n\n    it \"passes module_groups with fetch\" do\n      stub_request(:get, \"https://forgeapi.puppet.com/v3/releases\")\n        .with(query: hash_including(\"module_groups\" => \"foo\"))\n        .to_return(status: 200, body: release_response)\n\n      forge.fetch('puppetlabs-bacula')\n    end\n  end\n\n  # See PUP-8008\n  context \"when multiple module_groups are defined\" do\n    context \"with space seperator\" do\n      before :each do\n        Puppet[:module_groups] = \"foo bar\"\n      end\n\n      it \"passes module_groups with search\" do\n        stub_request(:get, %r{forgeapi.puppet.com/v3/modules}).with do |req|\n          expect(req.uri.query).to match(/module_groups=foo%20bar/)\n        end.to_return(status: 200, body: release_response)\n\n        forge.search('bacula')\n      end\n\n      it \"passes module_groups with fetch\" do\n        stub_request(:get, %r{forgeapi.puppet.com/v3/releases}).with do |req|\n          expect(req.uri.query).to match(/module_groups=foo%20bar/)\n        end.to_return(status: 200, body: release_response)\n\n        forge.fetch('puppetlabs-bacula')\n      end\n    end\n\n    context \"with plus seperator\" do\n      before :each do\n        Puppet[:module_groups] = \"foo+bar\"\n      end\n\n      it \"passes module_groups with search\" do\n        stub_request(:get, %r{forgeapi.puppet.com/v3/modules}).with do |req|\n          expect(req.uri.query).to match(/module_groups=foo%20bar/)\n        end.to_return(status: 200, body: release_response)\n\n        forge.search('bacula')\n      end\n\n      it \"passes module_groups with fetch\" do\n        stub_request(:get, %r{forgeapi.puppet.com/v3/releases}).with do |req|\n          expect(req.uri.query).to match(/module_groups=foo%20bar/)\n        end.to_return(status: 200, body: release_response)\n\n        forge.fetch('puppetlabs-bacula')\n      end\n    end\n\n    # See PUP-8008\n    context \"when there are multiple pages of results\" do\n      before(:each) do\n        stub_request(:get, %r{forgeapi.puppet.com}).with do |req|\n          expect(req.uri.query).to match(/module_groups=foo%20bar/)\n        end.to_return(status: 200, body: first_page)\n          .to_return(status: 200, body: last_page)\n      end\n\n      context \"with space seperator\" do\n        before(:each) do\n          Puppet[:module_groups] = \"foo bar\"\n        end\n\n        let(:first_page) do\n          resp = JSON.parse(http_response)\n          resp['results'] = []\n          resp['pagination']['next'] = \"/v3/modules?limit=1&offset=1&module_groups=foo%20bar\"\n          JSON.dump(resp)\n        end\n\n        let(:last_page) do\n          resp = JSON.parse(http_response)\n          resp['results'] = []\n          resp['pagination']['current'] = \"/v3/modules?limit=1&offset=1&module_groups=foo%20bar\"\n          JSON.dump(resp)\n        end\n\n        it \"traverses pages during search\" do\n          forge.search('bacula')\n        end\n\n        it \"traverses pages during fetch\" do\n          forge.fetch('puppetlabs-bacula')\n        end\n      end\n\n      context \"with plus seperator\" do\n        before(:each) do\n          Puppet[:module_groups] = \"foo+bar\"\n        end\n\n        let(:first_page) do\n          resp = JSON.parse(http_response)\n          resp['results'] = []\n          resp['pagination']['next'] = \"/v3/modules?limit=1&offset=1&module_groups=foo+bar\"\n          JSON.dump(resp)\n        end\n\n        let(:last_page) do\n          resp = JSON.parse(http_response)\n          resp['results'] = []\n          resp['pagination']['current'] = \"/v3/modules?limit=1&offset=1&module_groups=foo+bar\"\n          JSON.dump(resp)\n        end\n\n        it \"traverses pages during search\" do\n          forge.search('bacula')\n        end\n\n        it \"traverses pages during fetch\" do\n          forge.fetch('puppetlabs-bacula')\n        end\n      end\n    end\n  end\n\n  context \"when the connection to the forge fails\" do\n    before :each do\n      stub_request(:get, /forgeapi.puppet.com/).to_return(status: [404, 'not found'])\n    end\n\n    it \"raises an error for search\" do\n      expect { forge.search('bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, \"Request to Puppet Forge failed. Detail: 404 not found.\"\n    end\n\n    it \"raises an error for fetch\" do\n      expect { forge.fetch('puppetlabs/bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, \"Request to Puppet Forge failed. Detail: 404 not found.\"\n    end\n  end\n\n  context \"when the API responds with an error\" do\n    it \"raises an error for fetch\" do\n      stub_request(:get, /forgeapi.puppet.com/).to_return(status: [410, 'Gone'], body: '{\"error\":\"invalid module\"}')\n\n      expect { forge.fetch('puppetlabs/bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, \"Request to Puppet Forge failed. Detail: 410 Gone.\"\n    end\n  end\n\n  context \"when the forge returns a module with unparseable dependencies\" do\n    it \"ignores modules with unparseable dependencies\" do\n      response = JSON.parse(http_response)\n      release = response['results'][0]['current_release']\n      release['metadata']['dependencies'] = [{'name' => 'broken-garbage >= 1.0.0', 'version_requirement' => 'banana'}]\n      response['results'] = [release]\n\n      stub_request(:get, /forgeapi.puppet.com/).to_return(status: 200, body: JSON.dump(response))\n\n      expect(forge.fetch('puppetlabs/bacula')).to be_empty\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/abs_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the abs function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an integer' do\n    { 0 => 0,\n      1 => 1,\n      -1 => 1 }.each_pair do |x, expected|\n      it \"called as abs(#{x}) results in #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( abs(#{x}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for an float' do\n    { 0.0 => 0.0,\n      1.0 => 1.0,\n      -1.1 => 1.1 }.each_pair do |x, expected|\n      it \"called as abs(#{x}) results in #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( abs(#{x}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for a string' do\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n    { \"0\" => 0,\n      \"1\" => 1,\n      \"-1\" => 1,\n      \"0.0\" => 0.0,\n      \"1.1\" => 1.1,\n      \"-1.1\" => 1.1,\n      \"0777\" => 777,\n      \"-0777\" => 777,\n    }.each_pair do |x, expected|\n      it \"called as abs('#{x}') results in #{expected} and deprecation warning\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( abs('#{x}') == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n    ['blue', '0xFF'].each do |x|\n      it \"errors when the string is not a decimal integer '#{x}' (indirectly deprecated)\" do\n        expect{ compile_to_catalog(\"abs('#{x}')\") }.to raise_error(/was given non decimal string/)\n      end\n    end\n\n    ['0.2.3', '1E+10'].each do |x|\n      it \"errors when the string is not a supported decimal float '#{x}' (indirectly deprecated)\" do\n        expect{ compile_to_catalog(\"abs('#{x}')\") }.to raise_error(/was given non decimal string/)\n      end\n    end\n  end\n\n  [[1,2,3], {'a' => 10}].each do |x|\n    it \"errors for a value of class #{x.class}\" do\n      expect{ compile_to_catalog(\"abs(#{x})\") }.to raise_error(/expects a value of type Numeric or String/)\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/all_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the all method' do\n  include PuppetSpec::Compiler\n\n  context \"should be callable as\" do\n    it 'all on an array' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.all |$v| { $v > 0 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'all on an array with index' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [0,2,4]\n        $n = $a.all |$i, $v| { $v == $i * 2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'all on a hash selecting entries' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {0=>0,1=>2,2=>4}\n        $n = $a.all |$e| { $e[1] == $e[0]*2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'all on a hash selecting key and value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {0=>0,1=>2,2=>4}\n        $n = $a.all |$k,$v| { $v == $k*2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n  end\n\n  context \"produces a boolean\" do\n    it 'true when boolean true is found' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.all |$v| { true }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'true when truthy is found' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.all |$v| { 42 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'false when truthy is not found (all undef)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.all |$v| { undef }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"false\")['ensure']).to eq('present')\n    end\n\n    it 'false when truthy is not found (all false)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.all |$v| { false }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"false\")['ensure']).to eq('present')\n    end\n\n  end\n  it_should_behave_like 'all iterative functions argument checks', 'any'\n  it_should_behave_like 'all iterative functions hash handling', 'any'\n\nend\n"
  },
  {
    "path": "spec/unit/functions/annotate_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\ndescribe 'the annotate function' do\n  include PuppetSpec::Compiler\n\n  let(:annotation) { <<-PUPPET }\n    type MyAdapter = Object[{\n      parent => Annotation,\n      attributes => {\n        id => Integer,\n        value => String[1]\n      }\n    }]\n  PUPPET\n\n  let(:annotation2) { <<-PUPPET }\n    type MyAdapter2 = Object[{\n      parent => Annotation,\n      attributes => {\n        id => Integer,\n        value => String[1]\n      }\n    }]\n  PUPPET\n\n  context 'with object and hash arguments' do\n    it 'creates new annotation on object' do\n      code = <<-PUPPET\n        #{annotation}\n        type MyObject = Object[{\n        }]\n        $my_object = MyObject({})\n        MyAdapter.annotate($my_object, { 'id' => 2, 'value' => 'annotation value' })\n        notice(MyAdapter.annotate($my_object).value)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['annotation value'])\n    end\n\n    it 'forces creation of new annotation' do\n      code = <<-PUPPET\n      #{annotation}\n      type MyObject = Object[{\n      }]\n      $my_object = MyObject({})\n      MyAdapter.annotate($my_object, { 'id' => 2, 'value' => 'annotation value' })\n      notice(MyAdapter.annotate($my_object).value)\n      MyAdapter.annotate($my_object, { 'id' => 2, 'value' => 'annotation value 2' })\n      notice(MyAdapter.annotate($my_object).value)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['annotation value', 'annotation value 2'])\n    end\n  end\n\n  context 'with object and block arguments' do\n    it 'creates new annotation on object' do\n      code = <<-PUPPET\n        #{annotation}\n        type MyObject = Object[{\n        }]\n        $my_object = MyObject({})\n        MyAdapter.annotate($my_object) || { { 'id' => 2, 'value' => 'annotation value' } }\n        notice(MyAdapter.annotate($my_object).value)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['annotation value'])\n    end\n\n    it 'does not recreate annotation' do\n      code = <<-PUPPET\n      #{annotation}\n      type MyObject = Object[{\n      }]\n      $my_object = MyObject({})\n      MyAdapter.annotate($my_object) || {\n        notice('should call this');\n        { 'id' => 2, 'value' => 'annotation value' }\n      }\n      MyAdapter.annotate($my_object) || {\n        notice('should not call this');\n        { 'id' => 2, 'value' => 'annotation value 2' }\n      }\n      notice(MyAdapter.annotate($my_object).value)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['should call this', 'annotation value'])\n    end\n  end\n\n  it \"with object and 'clear' arguments, clears and returns annotation\" do\n    code = <<-PUPPET\n      #{annotation}\n      type MyObject = Object[{\n      }]\n      $my_object = MyObject({})\n      MyAdapter.annotate($my_object, { 'id' => 2, 'value' => 'annotation value' })\n      notice(MyAdapter.annotate($my_object, clear).value)\n      notice(MyAdapter.annotate($my_object) == undef)\n    PUPPET\n    expect(eval_and_collect_notices(code)).to eql(['annotation value', 'true'])\n  end\n\n  context 'when object is an annotated Type' do\n    it 'finds annotation declared in the type' do\n      code = <<-PUPPET\n        #{annotation}\n        type MyObject = Object[{\n          annotations => {\n            MyAdapter => { 'id' => 2, 'value' => 'annotation value' }\n          }\n        }]\n        notice(MyAdapter.annotate(MyObject).value)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['annotation value'])\n    end\n\n    it 'fails attempts to clear a declared annotation' do\n      code = <<-PUPPET\n        #{annotation}\n        type MyObject = Object[{\n          annotations => {\n            MyAdapter => { 'id' => 2, 'value' => 'annotation value' }\n          }\n        }]\n        notice(MyAdapter.annotate(MyObject).value)\n        notice(MyAdapter.annotate(MyObject, clear).value)\n      PUPPET\n      expect { eval_and_collect_notices(code) }.to raise_error(/attempt to clear MyAdapter annotation declared on MyObject/)\n    end\n\n    it 'fails attempts to redefine a declared annotation' do\n      code = <<-PUPPET\n        #{annotation}\n        type MyObject = Object[{\n          annotations => {\n            MyAdapter => { 'id' => 2, 'value' => 'annotation value' }\n          }\n        }]\n        notice(MyAdapter.annotate(MyObject).value)\n        notice(MyAdapter.annotate(MyObject, { 'id' => 3, 'value' => 'some other value' }).value)\n      PUPPET\n      expect { eval_and_collect_notices(code) }.to raise_error(/attempt to redefine MyAdapter annotation declared on MyObject/)\n    end\n\n    it 'allows annotation that are not declared in the type' do\n      code = <<-PUPPET\n        #{annotation}\n        #{annotation2}\n        type MyObject = Object[{\n          annotations => {\n            MyAdapter => { 'id' => 2, 'value' => 'annotation value' }\n          }\n        }]\n        notice(MyAdapter.annotate(MyObject).value)\n        notice(MyAdapter2.annotate(MyObject, { 'id' => 3, 'value' => 'some other value' }).value)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['annotation value', 'some other value'])\n    end\n  end\n\n  it 'used on Pcore, can add multiple annotations an object' do\n    code = <<-PUPPET\n      #{annotation}\n      #{annotation2}\n      type MyObject = Object[{\n      }]\n      $my_object = Pcore.annotate(MyObject({}), {\n        MyAdapter => { 'id' => 2, 'value' => 'annotation value' },\n        MyAdapter2 => { 'id' => 3, 'value' => 'second annotation value' }\n      })\n      notice(MyAdapter.annotate($my_object).value)\n      notice(MyAdapter2.annotate($my_object).value)\n    PUPPET\n    expect(eval_and_collect_notices(code)).to eql(['annotation value', 'second annotation value'])\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/any_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the any method' do\n  include PuppetSpec::Compiler\n\n  context \"should be callable as\" do\n    it 'any on an array' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.any |$v| { $v == 2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'any on an array with index' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.any |$i, $v| { $i == 2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'any on a hash selecting entries' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>'ah','b'=>'be','c'=>'ce'}\n        $n = $a.any |$e| { $e[1] == 'be' }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'any on a hash selecting key and value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>'ah','b'=>'be','c'=>'ce'}\n        $n = $a.any |$k, $v| { $v == 'be' }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n  end\n\n  context 'stops iteration when result is known' do\n    it 'true when boolean true is found' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.any |$v| { if $v == 1 { true } else { fail(\"unwanted\") } }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n  end\n\n  context \"produces a boolean\" do\n    it 'true when boolean true is found' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.any |$v| { true }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'true when truthy is found' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.any |$v| { 42 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"true\")['ensure']).to eq('present')\n    end\n\n    it 'false when truthy is not found (all undef)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.any |$v| { undef }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"false\")['ensure']).to eq('present')\n    end\n\n    it 'false when truthy is not found (all false)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.any |$v| { false }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"false\")['ensure']).to eq('present')\n    end\n\n  end\n  it_should_behave_like 'all iterative functions argument checks', 'any'\n  it_should_behave_like 'all iterative functions hash handling', 'any'\n\nend\n"
  },
  {
    "path": "spec/unit/functions/assert_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\nrequire 'puppet_spec/compiler'\n\ndescribe 'the assert_type function' do\n  include PuppetSpec::Compiler\n\n  after(:all) { Puppet::Pops::Loaders.clear }\n\n  let(:loaders) { Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, [])) }\n  let(:func) { loaders.puppet_system_loader.load(:function, 'assert_type') }\n\n  it 'asserts compliant type by returning the value' do\n    expect(func.call({}, type(String), 'hello world')).to eql('hello world')\n  end\n\n  it 'accepts type given as a String' do\n    expect(func.call({}, 'String', 'hello world')).to eql('hello world')\n  end\n\n  it 'asserts non compliant type by raising an error' do\n    expect do\n      func.call({}, type(Integer), 'hello world')\n    end.to raise_error(Puppet::Pops::Types::TypeAssertionError, /expects an Integer value, got String/)\n  end\n\n  it 'checks that first argument is a type' do\n    expect do\n      func.call({}, 10, 10)\n    end.to raise_error(ArgumentError, \"The function 'assert_type' was called with arguments it does not accept. It expects one of:\n  (Type type, Any value, Callable[Type, Type] block?)\n    rejected: parameter 'type' expects a Type value, got Integer\n  (String type_string, Any value, Callable[Type, Type] block?)\n    rejected: parameter 'type_string' expects a String value, got Integer\")\n  end\n\n  it 'allows the second arg to be undef/nil)' do\n    expect do\n      func.call({}, optional(String), nil)\n    end.to_not raise_error\n  end\n\n  it 'can be called with a callable that receives a specific type' do\n    expected, actual, actual2 = func.call({}, 'Optional[String]', 1) { |expctd, actul| [expctd, actul, actul] }\n    expect(expected.to_s).to eql('Optional[String]')\n    expect(actual.to_s).to eql('Integer[1, 1]')\n    expect(actual2.to_s).to eql('Integer[1, 1]')\n  end\n\n  def optional(type_ref)\n    Puppet::Pops::Types::TypeFactory.optional(type(type_ref))\n  end\n\n  def type(type_ref)\n    Puppet::Pops::Types::TypeFactory.type_of(type_ref)\n  end\n\n  it 'can validate a resource type' do\n    expect(eval_and_collect_notices(\"assert_type(Type[Resource], File['/tmp/test']) notice('ok')\")).to eq(['ok'])\n  end\n\n  it 'can validate a type alias' do\n    code = <<-CODE\n      type UnprivilegedPort = Integer[1024,65537]\n      assert_type(UnprivilegedPort, 5432)\n      notice('ok')\n    CODE\n    expect(eval_and_collect_notices(code)).to eq(['ok'])\n  end\n\n  it 'can validate a type alias passed as a String' do\n    code = <<-CODE\n      type UnprivilegedPort = Integer[1024,65537]\n      assert_type('UnprivilegedPort', 5432)\n      notice('ok')\n    CODE\n    expect(eval_and_collect_notices(code)).to eq(['ok'])\n  end\n\n  it 'can validate and fail using a type alias' do\n    code = <<-CODE\n      type UnprivilegedPort = Integer[1024,65537]\n      assert_type(UnprivilegedPort, 345)\n      notice('ok')\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects an UnprivilegedPort = Integer\\[1024, 65537\\] value, got Integer\\[345, 345\\]/)\n  end\n\n  it 'will use infer_set to report detailed information about complex mismatches' do\n    code = <<-CODE\n      assert_type(Struct[{a=>Integer,b=>Boolean}], {a=>hej,x=>s})\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error,\n      /entry 'a' expects an Integer value, got String.*expects a value for key 'b'.*unrecognized key 'x'/m)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/binary_file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\nrequire 'puppet_spec/files'\n\ndescribe 'the binary_file function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n  include PuppetSpec::Files\n\n  def with_file_content(content)\n    path = tmpfile('find-file-function')\n    file = File.new(path, 'wb')\n    file.sync = true\n    file.print content\n    yield path\n  end\n\n  it 'reads an existing absolute file' do\n    with_file_content('one') do |one|\n      # Note that Binary to String produced Base64 encoded version of 'one' which is 'b23l'\n      expect(compile_to_catalog(\"notify { String(binary_file('#{one}')):}\")).to have_resource(\"Notify[b25l]\")\n    end\n  end\n\n  it 'errors on non existing files' do\n    expect do\n      with_file_content('one') do |one|\n        compile_to_catalog(\"notify { binary_file('#{one}/nope'):}\")\n      end\n    end.to raise_error(/The given file '.+\\/nope' does not exist/)\n  end\n\n  it 'reads an existing file in a module' do\n    with_file_content('binary_data') do |name|\n      mod = double('module')\n      allow(mod).to receive(:file).with('myfile').and_return(name)\n      Puppet[:code] = \"notify { String(binary_file('mymod/myfile')):}\"\n      node = Puppet::Node.new('localhost')\n      compiler = Puppet::Parser::Compiler.new(node)\n      allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n      # Note that the Binary to string produces Base64 encoded version of 'binary_data' which is 'YmluYXJ5X2RhdGE='\n      expect(compiler.compile().filter { |r| r.virtual? }).to have_resource(\"Notify[YmluYXJ5X2RhdGE=]\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/break_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the break function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context  do\n    it 'breaks iteration as if at end of input in a map for an array' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[1, 2]]')\n          function please_break() {\n            [1,2,3].map |$x| { if $x == 3 { break() } $x }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a map for a hash' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[1, 2]]')\n          function please_break() {\n            {'a' => 1, 'b' => 2, 'c' => 3}.map |$x, $y| { if $y == 3 { break() } $y }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a reduce for an array' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[6]')\n          function please_break() {\n            [1,2,3,4].reduce |$memo, $x| { if $x == 4 { break() } $memo + $x }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a reduce for a hash' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource(\"Notify[['abc', 6]]\")\n          function please_break() {\n            {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}.reduce |$memo, $x| {\n\t      if $x[1] == 4 { break() }\n              $string = \"${memo[0]}${x[0]}\"\n              $number = $memo[1] + $x[1]\n\t      [$string, $number]\n\t    }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in an each for an array' do\n      expect(compile_to_catalog(<<-CODE)).to_not have_resource('Notify[3]')\n          function please_break() {\n            [1,2,3].each |$x| { if $x == 3 { break() } notify { \"$x\": } }\n          }\n          please_break()\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in an each for a hash' do\n      expect(compile_to_catalog(<<-CODE)).to_not have_resource('Notify[3]')\n          function please_break() {\n            {'a' => 1, 'b' => 2, 'c' => 3}.each |$x, $y| { if $y == 3 { break() } notify { \"$y\": } }\n          }\n          please_break()\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a reverse_each' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[2]')\n          function please_break() {\n            [1,2,3].reverse_each |$x| { if $x == 1 { break() } notify { \"$x\": } }\n          }\n          please_break()\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a map for a hash' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[1, 2]]')\n          function please_break() {\n            {'a' => 1, 'b' => 2, 'c' => 3}.map |$x, $y| { if $y == 3 { break() } $y }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a reduce for an array' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[6]')\n          function please_break() {\n            [1,2,3,4].reduce |$memo, $x| { if $x == 4 { break() } $memo + $x }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a reduce for a hash' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource(\"Notify[['abc', 6]]\")\n          function please_break() {\n            {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}.reduce |$memo, $x| {\n              if $x[1] == 4 { break() }\n              $string = \"${memo[0]}${x[0]}\"\n              $number = $memo[1] + $x[1]\n              [$string, $number]\n            }\n          }\n          notify { String(please_break()): }\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in an each for an array' do\n      expect(compile_to_catalog(<<-CODE)).to_not have_resource('Notify[3]')\n          function please_break() {\n            [1,2,3].each |$x| { if $x == 3 { break() } notify { \"$x\": } }\n          }\n          please_break()\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in an each for a hash' do\n      expect(compile_to_catalog(<<-CODE)).to_not have_resource('Notify[3]')\n          function please_break() {\n            {'a' => 1, 'b' => 2, 'c' => 3}.each |$x, $y| { if $y == 3 { break() } notify { \"$y\": } }\n          }\n          please_break()\n        CODE\n    end\n\n    it 'breaks iteration as if at end of input in a reverse_each' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[2]')\n          function please_break() {\n            [1,2,3].reverse_each |$x| { if $x == 1 { break() } notify { \"$x\": } }\n          }\n          please_break()\n        CODE\n    end\n\n    it 'does not provide early exit from a class' do\n      # A break would semantically mean that the class should not be included - as if the\n      # iteration over class names should stop. That is too magic and should\n      # be done differently by the user.\n      #\n      expect do\n        compile_to_catalog(<<-CODE)\n          class does_break {\n            notice 'a'\n            if 1 == 1 { break() } # avoid making next line statically unreachable\n            notice 'b'\n          }\n          include(does_break)\n        CODE\n      end.to raise_error(/break\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n    end\n\n    it 'does not provide early exit from a define' do\n      # A break would semantically mean that the resource should not be created - as if the\n      # iteration over resource titles should stop. That is too magic and should\n      # be done differently by the user.\n      #\n      expect do\n        compile_to_catalog(<<-CODE)\n          define does_break {\n            notice 'a'\n            if 1 == 1 { break() } # avoid making next line statically unreachable\n            notice 'b'\n          }\n            does_break { 'no_you_cannot': }\n        CODE\n      end.to raise_error(/break\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n    end\n\n    it 'can be called when nested in a function to make that function behave as a break' do\n      # This allows functions like break_when(...) to be implemented by calling break() conditionally\n      #\n      expect(eval_and_collect_notices(<<-CODE)).to eql(['[100]'])\n        function nested_break($x) {\n          if $x == 2 { break() } else { $x * 100 }\n        }\n        function example() {\n          [1,2,3].map |$x| { nested_break($x)  }\n        }\n        notice example()\n        CODE\n    end\n\n    it 'can not be called nested from top scope' do\n      expect do\n        compile_to_catalog(<<-CODE)\n          # line 1\n          # line 2\n          $result = with(1) |$x| { with($x) |$x| {break() }}\n          notice $result\n        CODE\n      end.to raise_error(/break\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n    end\n\n    it 'can not be called from top scope' do\n      expect do\n        compile_to_catalog(<<-CODE)\n          # line 1\n          # line 2\n          break()\n        CODE\n      end.to raise_error(/break\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/call_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'matchers/resource'\n\ndescribe 'the call method' do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n  include Matchers::Resource\n\n  context \"should be callable as\" do\n    let(:env_name) { 'testenv' }\n    let(:environments_dir) { Puppet[:environmentpath] }\n    let(:env_dir) { File.join(environments_dir, env_name) }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')]) }\n    let(:node) { Puppet::Node.new(\"test\", :environment => env) }\n    let(:env_dir_files) {\n      {\n        'modules' => {\n          'test' => {\n            'functions' => {\n              'call_me.pp' => 'function test::call_me() { \"called\" }',\n              'abc.pp'     => 'function test::abc() { \"a-b-c\" }',\n              'dash.pp'    => 'function test::dash() { \"-\" }'\n            }\n          }\n        }\n      }\n    }\n\n    let(:populated_env_dir) do\n      dir_contained_in(environments_dir, env_name => env_dir_files)\n      PuppetSpec::Files.record_tmp(env_dir)\n      env_dir\n    end\n\n    it 'call on a built-in 4x Ruby API function' do\n      expect(compile_to_catalog(<<-CODE, node)).to have_resource('Notify[a]')\n          $a = call('split', 'a-b-c', '-')\n          notify { $a[0]: }\n        CODE\n    end\n\n    it 'call on a Puppet language function with no arguments' do\n      expect(compile_to_catalog(<<-CODE, node)).to have_resource('Notify[called]')\n        notify { test::call_me(): }\n        CODE\n    end\n\n    it 'call a Ruby 4x API built-in with block' do\n      catalog = compile_to_catalog(<<-CODE, node)\n        $a = 'each'\n        $b = [1,2,3]\n        call($a, $b) |$index, $v| {\n          file { \"/file_$v\": ensure => present }\n        }\n      CODE\n\n      expect(catalog.resource(:file, \"/file_1\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_2\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_3\")['ensure']).to eq('present')\n    end\n\n    it 'call with the calling context' do\n      expect(eval_and_collect_notices(<<-CODE, node)).to eq(['a'])\n        class a { call('notice', $title) }\n        include a\n      CODE\n    end\n\n    it 'call on a non-existent function name' do\n      expect { compile_to_catalog(<<-CODE, node) }.to raise_error(Puppet::Error, /Unknown function/)\n        $a = call('not_a_function_name')\n        notify { $a: }\n      CODE\n    end\n\n    it 'call a deferred value' do\n      expect(compile_to_catalog(<<-CODE, node)).to have_resource('Notify[a]')\n          $d = Deferred('split', ['a-b-c', '-'])\n          $a = $d.call()\n          notify { $a[0]: }\n        CODE\n    end\n\n    it 'resolves deferred value arguments in an array when calling a deferred' do\n      expect(compile_to_catalog(<<-CODE,node)).to have_resource('Notify[a]')\n          $d = Deferred('split', [Deferred('test::abc'), '-'])\n          $a = $d.call()\n          notify { $a[0]: }\n        CODE\n    end\n\n    it 'resolves deferred value arguments in a Sensitive when calling a deferred' do\n      expect(compile_to_catalog(<<-CODE, node)).to have_resource('Notify[a]')\n          function my_split(Sensitive $sensy, $on) { $sensy.unwrap |$x| { split($x, $on) } }\n          $d = Deferred('my_split', [ Sensitive(Deferred('test::abc')), '-'])\n          $a = $d.call()\n          notify { $a[0]: }\n        CODE\n    end\n\n    it 'resolves deferred value arguments in a Hash when calling a deferred' do\n      expect(compile_to_catalog(<<-CODE, node)).to have_resource('Notify[a]')\n          function my_split(Hash $hashy, $on) { split($hashy['key'], $on)  }\n          $d = Deferred('my_split', [ {'key' => Deferred('test::abc')}, '-'])\n          $a = $d.call()\n          notify { $a[0]: }\n        CODE\n    end\n\n    it 'resolves deferred value arguments in a nested structure when calling a deferred' do\n      expect(compile_to_catalog(<<-CODE,node)).to have_resource('Notify[a]')\n          function my_split(Hash $hashy, Array[Sensitive] $sensy) { split($hashy['key'][0], $sensy[0].unwrap |$x| {$x})  }\n          $d = Deferred('my_split', [ {'key' => [Deferred('test::abc')]}, [Sensitive(Deferred('test::dash'))]])\n          $a = $d.call()\n          notify { $a[0]: }\n        CODE\n    end\n\n    it 'call dig into a variable' do\n      expect(compile_to_catalog(<<-CODE, node)).to have_resource('Notify[value 3]')\n          $x = { 'a' => [1,2,3] }\n          $d = Deferred('$x', ['a', 2])\n          $a = $d.call()\n          notify { \"value $a\": }\n        CODE\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/camelcase_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the camelcase function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'replaces initial <char> and each _<char> with upper case version of the char' do\n    expect(compile_to_catalog(\"notify { 'abc_def'.camelcase: }\")).to have_resource('Notify[AbcDef]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.camelcase == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'performs camelcase of international UTF-8 characters' do\n    expect(compile_to_catalog(\"notify { 'åäö_äö'.camelcase: }\")).to have_resource('Notify[ÅäöÄö]')\n  end\n\n  it 'returns capitalized version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String(['a_a', 'b_a', 'c_a'].camelcase == ['AA', 'BA', 'CA']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns capitalized version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String(['a_a', 'b_a', 'c_a'].reverse_each.camelcase == ['CA', 'BA', 'AA']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].camelcase\")}.to raise_error(/'camelcase' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/capitalize_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the capitalize function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'returns initial char upper case version of a string' do\n    expect(compile_to_catalog(\"notify { 'abc'.capitalize: }\")).to have_resource('Notify[Abc]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.capitalize == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'performs capitalize of international UTF-8 characters' do\n    expect(compile_to_catalog(\"notify { 'åäö'.capitalize: }\")).to have_resource('Notify[Åäö]')\n  end\n\n  it 'returns capitalized version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String(['aa', 'ba', 'ca'].capitalize == ['Aa', 'Ba', 'Ca']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns capitalized version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String(['aa', 'ba', 'ca'].reverse_each.capitalize == ['Ca', 'Ba', 'Aa']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].capitalize\")}.to raise_error(/'capitalize' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/ceiling_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the ceiling function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an integer' do\n    [ 0, 1, -1].each do |x|\n      it \"called as ceiling(#{x}) results in the same value\" do\n        expect(compile_to_catalog(\"notify { String( ceiling(#{x}) == #{x}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for a float' do\n    {\n      0.0 => 0,\n      1.1 => 2,\n      -1.1 => -1,\n    }.each_pair do |x, expected|\n      it \"called as ceiling(#{x}) results in #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( ceiling(#{x}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for a string' do\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n    { \"0\" => 0,\n      \"1\" => 1,\n      \"-1\" => -1,\n      \"0.0\" => 0,\n      \"1.1\" => 2,\n      \"-1.1\" => -1,\n      \"0777\" => 777,\n      \"-0777\" => -777,\n      \"0xFF\" => 0xFF,\n    }.each_pair do |x, expected|\n      it \"called as ceiling('#{x}') results in #{expected} and a deprecation warning\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( ceiling('#{x}') == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n    ['blue', '0.2.3'].each do |x|\n      it \"errors as the string '#{x}' cannot be converted to a float\" do\n        expect{ compile_to_catalog(\"ceiling('#{x}')\") }.to raise_error(/cannot convert given value to a floating point value/)\n      end\n    end\n  end\n\n  [[1,2,3], {'a' => 10}].each do |x|\n    it \"errors for a value of class #{x.class}\" do\n      expect{ compile_to_catalog(\"ceiling(#{x})\") }.to raise_error(/expects a value of type Numeric or String/)\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/chomp_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the chomp function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'removes line endings CR LF from a string' do\n    expect(compile_to_catalog(\"notify { String(\\\"abc\\r\\n\\\".chomp == 'abc'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'removes line ending CR from a string' do\n    expect(compile_to_catalog(\"notify { String(\\\"abc\\r\\\".chomp == 'abc'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'removes line ending LF from a string' do\n    expect(compile_to_catalog(\"notify { String(\\\"abc\\n\\\".chomp == 'abc'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'does not removes LF before CR line ending from a string' do\n    expect(compile_to_catalog(\"notify { String(\\\"abc\\n\\r\\\".chomp == \\\"abc\\n\\\"): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns empty string for an empty string' do\n    expect(compile_to_catalog(\"notify { String(''.chomp == ''): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.chomp == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns chomped version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String([\\\"a\\n\\\", \\\"b\\n\\\", \\\"c\\n\\\"].chomp == ['a', 'b', 'c']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns chopped version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String([\\\"a\\n\\\", \\\"b\\n\\\", \\\"c\\n\\\"].reverse_each.chomp == ['c', 'b', 'a']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].chomp\")}.to raise_error(/'chomp' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/chop_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the chop function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'removes last character in a string' do\n    expect(compile_to_catalog(\"notify { String('abc'.chop == 'ab'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns empty string for an empty string' do\n    expect(compile_to_catalog(\"notify { String(''.chop == ''): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'removes both CR LF if both are the last characters in a string' do\n    expect(compile_to_catalog(\"notify { String(\\\"abc\\r\\n\\\".chop == 'abc'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.chop == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns chopped version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String(['aa', 'ba', 'ca'].chop == ['a', 'b', 'c']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns chopped version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String(['aa', 'bb', 'cc'].reverse_each.chop == ['c', 'b', 'a']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].chop\")}.to raise_error(/'chop' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/compare_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the compare function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  {\n    [0, 1]     => -1,\n    [1, 0]     => 1,\n    [1, 1]     => 0,\n    [0.0, 1.0] => -1,\n    [1.0, 0.0] => 1,\n    [1.0, 1.0] => 0,\n    [0.0, 1]   => -1,\n    [1.0, 0]   => 1,\n    [1.0, 1]   => 0,\n    [0, 1.0]   => -1,\n    [1, 0.0]   => 1,\n    [1, 1.0]   => 0,\n  }.each_pair do |values, expected|\n    it \"compares numeric/numeric such that compare(#{values[0]}, #{values[1]}) returns #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( compare(#{values[0]},#{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  {\n    ['\"a\"', '\"b\"'] => -1,\n    ['\"b\"', '\"a\"'] => 1,\n    ['\"b\"', '\"b\"'] => 0,\n    ['\"A\"', '\"b\"'] => -1,\n    ['\"B\"', '\"a\"'] => 1,\n    ['\"B\"', '\"b\"'] => 0,\n  }.each_pair do |values, expected|\n    it \"compares String values by default such that compare(#{values[0]}, #{values[1]}) returns #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( compare(#{values[0]},#{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n\n    it \"compares String values with third true arg such that compare(#{values[0]}, #{values[1]}, true) returns #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( compare(#{values[0]},#{values[1]}, true) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  {\n    ['\"a\"', '\"b\"'] => -1,\n    ['\"b\"', '\"a\"'] => 1,\n    ['\"b\"', '\"b\"'] => 0,\n    ['\"A\"', '\"b\"'] => -1,\n    ['\"B\"', '\"a\"'] => -1,\n    ['\"B\"', '\"b\"'] => -1,\n  }.each_pair do |values, expected|\n    it \"compares String values with third arg false such that compare(#{values[0]}, #{values[1]}, false) returns #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( compare(#{values[0]},#{values[1]}, false) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  {\n    [\"Semver('1.0.0')\", \"Semver('2.0.0')\"] => -1,\n    [\"Semver('2.0.0')\", \"Semver('1.0.0')\"] => 1,\n    [\"Semver('2.0.0')\", \"Semver('2.0.0')\"] => 0,\n  }.each_pair do |values, expected|\n    it \"compares Semver values such that compare(#{values[0]}, #{values[1]}) returns #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( compare(#{values[0]},#{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  {\n    [\"Timespan(1)\", \"Timespan(2)\"] => -1,\n    [\"Timespan(2)\", \"Timespan(1)\"] => 1,\n    [\"Timespan(1)\", \"Timespan(1)\"] => 0,\n    [\"Timespan(1)\", 2] => -1,\n    [\"Timespan(2)\", 1] => 1,\n    [\"Timespan(1)\", 1] => 0,\n    [1, \"Timespan(2)\"] => -1,\n    [2, \"Timespan(1)\"] => 1,\n    [1, \"Timespan(1)\"] => 0,\n\n    [\"Timestamp(1)\", \"Timestamp(2)\"] => -1,\n    [\"Timestamp(2)\", \"Timestamp(1)\"] => 1,\n    [\"Timestamp(1)\", \"Timestamp(1)\"] => 0,\n    [\"Timestamp(1)\", 2] => -1,\n    [\"Timestamp(2)\", 1] => 1,\n    [\"Timestamp(1)\", 1] => 0,\n    [1, \"Timestamp(2)\"] => -1,\n    [2, \"Timestamp(1)\"] => 1,\n    [1, \"Timestamp(1)\"] => 0,\n\n  }.each_pair do |values, expected|\n    it \"compares time values such that compare(#{values[0]}, #{values[1]}) returns #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( compare(#{values[0]},#{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  context \"errors when\" do\n    [ [true, false],\n      ['/x/', '/x/'],\n      ['undef', 'undef'],\n      ['undef', 1],\n      [1, 'undef'],\n      [[1], [1]],\n      [{'a' => 1}, {'b' => 1}],\n    ].each do |a, b|\n      it \"given values of non comparable types #{a.class}, #{b.class}\" do\n        expect { compile_to_catalog(\"compare(#{a},#{b})\")}.to raise_error(/Non comparable type/)\n      end\n    end\n\n    [\n      [10, '\"hello\"'],\n      ['\"hello\"', 10],\n      [10.0, '\"hello\"'],\n      ['\"hello\"', 10.0],\n      ['Timespan(1)', '\"hello\"'],\n      ['Timestamp(1)', '\"hello\"'],\n      ['Timespan(1)', 'Semver(\"1.2.3\")'],\n      ['Timestamp(1)', 'Semver(\"1.2.3\")'],\n    ].each do |a, b|\n      it \"given values of comparable, but incompatible types #{a.class}, #{b.class}\" do\n        expect { compile_to_catalog(\"compare(#{a},#{b})\")}.to raise_error(/Can only compare values of the same type/)\n      end\n    end\n\n      [\n        [10, 10],\n        [10.0, 10],\n        ['Timespan(1)', 'Timespan(1)'],\n        ['Timestamp(1)', 'Timestamp(1)'],\n        ['Semver(\"1.2.3\")', 'Semver(\"1.2.3\")'],\n      ].each do |a, b|\n        it \"given ignore case true when values are comparable, but not both being strings\" do\n          expect { compile_to_catalog(\"compare(#{a},#{b}, true)\")}.to raise_error(/can only be used when comparing strings/)\n        end\n        it \"given ignore case false when values are comparable, but not both being strings\" do\n          expect { compile_to_catalog(\"compare(#{a},#{b}, false)\")}.to raise_error(/can only be used when comparing strings/)\n        end\n      end\n\n      it \"given more than three arguments\" do\n          expect { compile_to_catalog(\"compare('a','b', false, false)\")}.to raise_error(/Accepts at most 3 arguments, got 4/)\n      end\n  end\n\n  # Error if not the same except Timestamp and Timespan that accepts Numeric on either side\n  # Error for non supported - Boolean, Regexp, etc\nend\n"
  },
  {
    "path": "spec/unit/functions/contain_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/parser/functions'\nrequire 'matchers/containment_matchers'\nrequire 'matchers/resource'\nrequire 'matchers/include_in_order'\nrequire 'unit/functions/shared'\n\ndescribe 'The \"contain\" function' do\n  include PuppetSpec::Compiler\n  include ContainmentMatchers\n  include Matchers::Resource\n\n  before(:each) do\n    compiler  = Puppet::Parser::Compiler.new(Puppet::Node.new(\"foo\"))\n    @scope = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it \"includes the class\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class contained {\n        notify { \"contained\": }\n      }\n\n      class container {\n        contain contained\n      }\n\n      include container\n    MANIFEST\n\n    expect(catalog.classes).to include(\"contained\")\n  end\n\n  it \"includes the class when using a fully qualified anchored name\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class contained {\n        notify { \"contained\": }\n      }\n\n      class container {\n        contain ::contained\n      }\n\n      include container\n    MANIFEST\n\n    expect(catalog.classes).to include(\"contained\")\n  end\n\n  it \"ensures that the edge is with the correct class\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class outer {\n        class named { }\n        contain outer::named\n      }\n\n      class named { }\n\n      include named\n      include outer\n    MANIFEST\n\n    expect(catalog).to have_resource(\"Class[Named]\")\n    expect(catalog).to have_resource(\"Class[Outer]\")\n    expect(catalog).to have_resource(\"Class[Outer::Named]\")\n    expect(catalog).to contain_class(\"outer::named\").in(\"outer\")\n  end\n\n  it \"makes the class contained in the current class\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class contained {\n        notify { \"contained\": }\n      }\n\n      class container {\n        contain contained\n      }\n\n      include container\n    MANIFEST\n\n    expect(catalog).to contain_class(\"contained\").in(\"container\")\n  end\n\n  it \"can contain multiple classes\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      class b {\n        notify { \"b\": }\n      }\n\n      class container {\n        contain a, b\n      }\n\n      include container\n    MANIFEST\n\n    expect(catalog).to contain_class(\"a\").in(\"container\")\n    expect(catalog).to contain_class(\"b\").in(\"container\")\n  end\n\n  context \"when containing a class in multiple classes\" do\n    it \"creates a catalog with all containment edges\" do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        class contained {\n          notify { \"contained\": }\n        }\n\n        class container {\n          contain contained\n        }\n\n        class another {\n          contain contained\n        }\n\n        include container\n        include another\n      MANIFEST\n\n      expect(catalog).to contain_class(\"contained\").in(\"container\")\n      expect(catalog).to contain_class(\"contained\").in(\"another\")\n    end\n\n    it \"and there are no dependencies applies successfully\" do\n      manifest = <<-MANIFEST\n        class contained {\n          notify { \"contained\": }\n        }\n\n        class container {\n          contain contained\n        }\n\n        class another {\n          contain contained\n        }\n\n        include container\n        include another\n      MANIFEST\n\n      expect { apply_compiled_manifest(manifest) }.not_to raise_error\n    end\n\n    it \"and there are explicit dependencies on the containing class causes a dependency cycle\" do\n      manifest = <<-MANIFEST\n        class contained {\n          notify { \"contained\": }\n        }\n\n        class container {\n          contain contained\n        }\n\n        class another {\n          contain contained\n        }\n\n        include container\n        include another\n\n        Class[\"container\"] -> Class[\"another\"]\n      MANIFEST\n\n      expect { apply_compiled_manifest(manifest) }.to raise_error(\n        Puppet::Error,\n        /One or more resource dependency cycles detected in graph/\n      )\n    end\n  end\n\n  it \"does not create duplicate edges\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class contained {\n        notify { \"contained\": }\n      }\n\n      class container {\n        contain contained\n        contain contained\n      }\n\n      include container\n    MANIFEST\n\n    contained = catalog.resource(\"Class\", \"contained\")\n    container = catalog.resource(\"Class\", \"container\")\n\n    expect(catalog.edges_between(container, contained).count).to eq 1\n  end\n\n  context \"when a containing class has a dependency order\" do\n    it \"the contained class is applied in that order\" do\n      catalog = compile_to_relationship_graph(<<-MANIFEST)\n        class contained {\n          notify { \"contained\": }\n        }\n\n        class container {\n          contain contained\n        }\n\n        class first {\n          notify { \"first\": }\n        }\n\n        class last {\n          notify { \"last\": }\n        }\n\n        include container, first, last\n\n        Class[\"first\"] -> Class[\"container\"] -> Class[\"last\"]\n      MANIFEST\n\n      expect(order_resources_traversed_in(catalog)).to include_in_order(\n        \"Notify[first]\", \"Notify[contained]\", \"Notify[last]\"\n      )\n    end\n  end\n\n  it 'produces an array with a single class references given a single argument' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      class container {\n        $x = contain(a)\n        Array[Type[Class], 1, 1].assert_type($x)\n        notify { 'feedback': message => \"$x\" }\n      }\n\n      include container\n    MANIFEST\n\n    feedback = catalog.resource(\"Notify\", \"feedback\")\n    expect(feedback[:message]).to eql(\"[Class[a]]\")\n  end\n\n  it 'produces an array with class references given multiple arguments' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      class b {\n        notify { \"b\": }\n      }\n\n      class container {\n        $x = contain(a, b)\n        Array[Type[Class], 2, 2].assert_type($x)\n        notify { 'feedback': message => \"$x\" }\n      }\n\n      include container\n    MANIFEST\n\n    feedback = catalog.resource(\"Notify\", \"feedback\")\n    expect(feedback[:message]).to eql(\"[Class[a], Class[b]]\")\n  end\n\n  it 'allows the result to be used in a relationship operation' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      class b {\n        notify { \"b\": }\n      }\n\n      notify { 'c': }\n\n      class container {\n        contain(a, b) -> Notify[c]\n      }\n\n      include container\n    MANIFEST\n\n    # Assert relationships are formed\n    expect(catalog.resource(\"Class\", \"a\")[:before][0]).to eql('Notify[c]')\n    expect(catalog.resource(\"Class\", \"b\")[:before][0]).to eql('Notify[c]')\n  end\n\n  it_should_behave_like 'all functions transforming relative to absolute names', :contain\n  it_should_behave_like 'an inclusion function, regardless of the type of class reference,', :contain\n  it_should_behave_like 'an inclusion function, when --tasks is on,', :contain\nend\n"
  },
  {
    "path": "spec/unit/functions/convert_to_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the convert_to function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'converts and returns the converted when no lambda is given' do\n    expect(compile_to_catalog('notify{ \"testing-${[a,1].convert_to(Hash) =~ Hash}\": }')).to have_resource('Notify[testing-true]')\n  end\n\n  it 'converts given value to instance of type and calls a lambda with converted value' do\n    expect(compile_to_catalog('\"1\".convert_to(Integer) |$x| { notify { \"testing-${x.type(generalized)}\": } }')).to have_resource('Notify[testing-Integer]')\n  end\n\n  it 'returns the lambda return when lambda is given' do\n    expect(compile_to_catalog('notify{ \"testing-${[a,1].convert_to(Hash) |$x| { yay }}\": }')).to have_resource('Notify[testing-yay]')\n  end\n\n  it 'passes on additional arguments to the new function' do\n    expect(compile_to_catalog('\"111\".convert_to(Integer, 2) |$x| { notify { \"testing-${x}\": } }')).to have_resource('Notify[testing-7]')\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/defined_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe \"the 'defined' function\" do\n  after(:all) { Puppet::Pops::Loaders.clear }\n\n  # This loads the function once and makes it easy to call it\n  # It does not matter that it is not bound to the env used later since the function\n  # looks up everything via the scope that is given to it.\n  # The individual tests needs to have a fresh env/catalog set up\n  #\n  let(:loaders) { Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, [])) }\n  let(:func) { loaders.puppet_system_loader.load(:function, 'defined') }\n\n  before :each do\n    # A fresh environment is needed for each test since tests creates types and resources\n    environment = Puppet::Node::Environment.create(:testing, [])\n    @node = Puppet::Node.new('yaynode', :environment => environment)\n    @known_resource_types = environment.known_resource_types\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @scope = Puppet::Parser::Scope.new(@compiler)\n  end\n\n  def newclass(name)\n    @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name)\n  end\n\n  def newdefine(name)\n    @known_resource_types.add Puppet::Resource::Type.new(:definition, name)\n  end\n\n  def newresource(type, title)\n    resource = Puppet::Resource.new(type, title)\n    @compiler.add_resource(@scope, resource)\n    resource\n  end\n\n  #--- CLASS\n  #\n  context 'can determine if a class' do\n    context 'is defined' do\n\n      it 'by using the class name in string form' do\n        newclass 'yayness'\n        expect(func.call(@scope, 'yayness')).to be_truthy\n      end\n\n      it 'by using a Type[Class[name]] type reference' do\n        name = 'yayness'\n        newclass name\n        class_type = Puppet::Pops::Types::TypeFactory.host_class(name)\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(class_type)\n        expect(func.call(@scope, type_type)).to be_truthy\n      end\n    end\n\n    context 'is not defined' do\n      it 'by using the class name in string form' do\n        expect(func.call(@scope, 'yayness')).to be_falsey\n      end\n\n      it 'even if there is a define, by using a Type[Class[name]] type reference' do\n        name = 'yayness'\n        newdefine name\n        class_type = Puppet::Pops::Types::TypeFactory.host_class(name)\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(class_type)\n        expect(func.call(@scope, type_type)).to be_falsey\n      end\n    end\n\n    context 'is defined and realized' do\n      it 'by using a Class[name] reference' do\n        name = 'cowabunga'\n        newclass name\n        newresource(:class, name)\n        class_type = Puppet::Pops::Types::TypeFactory.host_class(name)\n        expect(func.call(@scope, class_type)).to be_truthy\n      end\n    end\n\n    context 'is not realized' do\n      it '(although defined) by using a Class[name] reference' do\n        name = 'cowabunga'\n        newclass name\n        class_type = Puppet::Pops::Types::TypeFactory.host_class(name)\n        expect(func.call(@scope, class_type)).to be_falsey\n      end\n\n      it '(and not defined) by using a Class[name] reference' do\n        name = 'cowabunga'\n        class_type = Puppet::Pops::Types::TypeFactory.host_class(name)\n        expect(func.call(@scope, class_type)).to be_falsey\n      end\n    end\n  end\n\n  #---RESOURCE TYPE\n  #\n  context 'can determine if a resource type' do\n    context 'is defined' do\n\n      it 'by using the type name (of a built in type) in string form' do\n        expect(func.call(@scope, 'file')).to be_truthy\n      end\n\n      it 'by using the type name (of a resource type) in string form' do\n        newdefine 'yayness'\n        expect(func.call(@scope, 'yayness')).to be_truthy\n      end\n\n      it 'by using a File type reference (built in type)' do\n        resource_type = Puppet::Pops::Types::TypeFactory.resource('file')\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(resource_type)\n        expect(func.call(@scope, type_type)).to be_truthy\n      end\n\n      it 'by using a Type[File] type reference' do\n        resource_type = Puppet::Pops::Types::TypeFactory.resource('file')\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(resource_type)\n        expect(func.call(@scope, type_type)).to be_truthy\n      end\n\n      it 'by using a Resource[T] type reference (defined type)' do\n        name = 'yayness'\n        newdefine name\n        resource_type = Puppet::Pops::Types::TypeFactory.resource(name)\n        expect(func.call(@scope, resource_type)).to be_truthy\n      end\n\n      it 'by using a Type[Resource[T]] type reference (defined type)' do\n        name = 'yayness'\n        newdefine name\n        resource_type = Puppet::Pops::Types::TypeFactory.resource(name)\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(resource_type)\n        expect(func.call(@scope, type_type)).to be_truthy\n      end\n    end\n\n    context 'is not defined' do\n      it 'by using the resource name in string form' do\n        expect(func.call(@scope, 'notatype')).to be_falsey\n      end\n\n      it 'even if there is a class with the same name, by using a Type[Resource[T]] type reference' do\n        name = 'yayness'\n        newclass name\n        resource_type = Puppet::Pops::Types::TypeFactory.resource(name)\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(resource_type)\n        expect(func.call(@scope, type_type)).to be_falsey\n      end\n    end\n\n    context 'is defined and instance realized' do\n      it 'by using a Resource[T, title] reference for a built in type' do\n        type_name = 'file'\n        title = '/tmp/myfile'\n        newdefine type_name\n        newresource(type_name, title)\n        class_type = Puppet::Pops::Types::TypeFactory.resource(type_name, title)\n        expect(func.call(@scope, class_type)).to be_truthy\n      end\n\n      it 'by using a Resource[T, title] reference for a defined type' do\n        type_name = 'meme'\n        title = 'cowabunga'\n        newdefine type_name\n        newresource(type_name, title)\n        class_type = Puppet::Pops::Types::TypeFactory.resource(type_name, title)\n        expect(func.call(@scope, class_type)).to be_truthy\n      end\n    end\n\n    context 'is not realized' do\n      it '(although defined) by using a Resource[T, title] reference or Type[Resource[T, title]] reference' do\n        type_name = 'meme'\n        title = 'cowabunga'\n        newdefine type_name\n        resource_type = Puppet::Pops::Types::TypeFactory.resource(type_name, title)\n        expect(func.call(@scope, resource_type)).to be_falsey\n\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(resource_type)\n        expect(func.call(@scope, type_type)).to be_falsey\n      end\n\n      it '(and not defined) by using a Resource[T, title] reference or Type[Resource[T, title]] reference' do\n        type_name = 'meme'\n        title = 'cowabunga'\n        resource_type = Puppet::Pops::Types::TypeFactory.resource(type_name, title)\n        expect(func.call(@scope, resource_type)).to be_falsey\n\n        type_type = Puppet::Pops::Types::TypeFactory.type_type(resource_type)\n        expect(func.call(@scope, type_type)).to be_falsey\n      end\n    end\n  end\n\n  #---VARIABLES\n  #\n  context 'can determine if a variable' do\n    context 'is defined' do\n      it 'by giving the variable in string form' do\n        @scope['x'] = 'something'\n        expect(func.call(@scope, '$x')).to be_truthy\n      end\n\n      it 'by giving a :: prefixed variable in string form' do\n        @compiler.topscope['x'] = 'something'\n        expect(func.call(@scope, '$::x')).to be_truthy\n      end\n\n      it 'by giving a numeric variable in string form (when there is a match scope)' do\n        # with no match scope, there are no numeric variables defined\n        expect(func.call(@scope, '$0')).to be_falsey\n        expect(func.call(@scope, '$42')).to be_falsey\n        pattern = Regexp.new('.*')\n        @scope.new_match_scope(pattern.match('anything'))\n\n        # with a match scope, all numeric variables are set (the match defines if they have a value or not, but they are defined)\n        # even if their value is undef.\n        expect(func.call(@scope, '$0')).to be_truthy\n        expect(func.call(@scope, '$42')).to be_truthy\n      end\n    end\n\n    context 'is undefined' do\n      it 'by giving a :: prefixed or regular variable in string form' do\n        expect(func.call(@scope, '$x')).to be_falsey\n        expect(func.call(@scope, '$::x')).to be_falsey\n      end\n    end\n  end\n\n  context 'has any? semantics when given multiple arguments' do\n    it 'and one of the names is a defined user defined type' do\n      newdefine 'yayness'\n      expect(func.call(@scope, 'meh', 'yayness', 'booness')).to be_truthy\n    end\n\n    it 'and one of the names is a built type' do\n      expect(func.call(@scope, 'meh', 'file', 'booness')).to be_truthy\n    end\n\n    it 'and one of the names is a defined class' do\n      newclass 'yayness'\n      expect(func.call(@scope, 'meh', 'yayness', 'booness')).to be_truthy\n    end\n\n    it 'is true when at least one variable exists in scope' do\n      @scope['x'] = 'something'\n      expect(func.call(@scope, '$y', '$x', '$z')).to be_truthy\n    end\n\n    it 'is false when none of the names are defined' do\n      expect(func.call(@scope, 'meh', 'yayness', 'booness')).to be_falsey\n    end\n  end\n\n  it 'raises an argument error when asking if Resource type is defined' do\n    resource_type = Puppet::Pops::Types::TypeFactory.resource\n    expect { func.call(@scope, resource_type)}.to raise_error(ArgumentError, /reference to all.*type/)\n  end\n\n  it 'raises an argument error if you ask if Class is defined' do\n    class_type = Puppet::Pops::Types::TypeFactory.host_class\n    expect { func.call(@scope, class_type) }.to raise_error(ArgumentError, /reference to all.*class/)\n  end\n\n  it 'raises error if referencing undef' do\n    expect{func.call(@scope, nil)}.to raise_error(ArgumentError, /'defined' parameter 'vals' expects a value of type String, Type\\[CatalogEntry\\], or Type\\[Type\\], got Undef/)\n  end\n\n  it 'raises error if referencing a number' do\n    expect{func.call(@scope, 42)}.to raise_error(ArgumentError, /'defined' parameter 'vals' expects a value of type String, Type\\[CatalogEntry\\], or Type\\[Type\\], got Integer/)\n  end\n\n  it 'is false if referencing empty string' do\n    expect(func.call(@scope, '')).to be_falsey\n  end\n\n  it \"is true if referencing 'main'\" do\n    # mimic what compiler does with \"main\" in intial import\n    newclass ''\n    newresource :class, ''\n    expect(func.call(@scope, 'main')).to be_truthy\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/dig_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the dig function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'returns a value from an array index via integer index' do\n    expect(compile_to_catalog(\"notify { [testing].dig(0): }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'returns undef if given an undef key' do\n  expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test-Undef-ing]')\n    notify { \"test-${type([testing].dig(undef))}-ing\": }\n    SOURCE\n  end\n\n  it 'returns undef if starting with undef' do\n  expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test-Undef-ing]')\n    notify { \"test-${type(undef.dig(undef))}-ing\": }\n    SOURCE\n  end\n\n  it 'returns a value from an hash key via given key' do\n    expect(compile_to_catalog(\"notify { {key => testing}.dig(key): }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'continues digging if result is an array' do\n    expect(compile_to_catalog(\"notify { [nope, [testing]].dig(1, 0): }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'continues digging if result is a hash' do\n    expect(compile_to_catalog(\"notify { [nope, {yes => testing}].dig(1, yes): }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'stops digging when step is undef' do\n    expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[testing]')\n    $result = [nope, {yes => testing}].dig(1, no, 2)\n    notify { \"test${result}ing\": }\n    SOURCE\n  end\n\n  it 'errors if step is neither Array nor Hash' do\n    expect { compile_to_catalog(<<-SOURCE)}.to raise_error(/The given data does not contain a Collection at \\[1, \"yes\"\\], got 'String'/)\n    $result = [nope, {yes => testing}].dig(1, yes, 2)\n    notify { \"test${result}ing\": }\n    SOURCE\n  end\n\n  it 'errors if not given a non Collection as the starting point' do\n    expect { compile_to_catalog(<<-SOURCE)}.to raise_error(/'dig' parameter 'data' expects a value of type Undef or Collection, got String/)\n    \"hello\".dig(1, yes, 2)\n    SOURCE\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/downcase_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the downcase function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'returns lower case version of a string' do\n    expect(compile_to_catalog(\"notify { 'ABC'.downcase: }\")).to have_resource('Notify[abc]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.downcase == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'performs downcase of international UTF-8 characters' do\n    expect(compile_to_catalog(\"notify { 'ÅÄÖ'.downcase: }\")).to have_resource('Notify[åäö]')\n  end\n\n  it 'returns lower case version of each entry in an array (recursively)' do\n    expect(compile_to_catalog(\"notify { String(['A', ['B', ['C']]].downcase == ['a', ['b', ['c']]]): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns lower case version of keys and values in a hash (recursively)' do\n    expect(compile_to_catalog(\"notify { String({'A'=>'B','C'=>{'D'=>'E'}}.downcase == {'a'=>'b', 'c'=>{'d'=>'e'}}): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns lower case version of keys and values in nested hash / array structure' do\n    expect(compile_to_catalog(\"notify { String({'A'=>['B'],'C'=>[{'D'=>'E'}]}.downcase == {'a'=>['b'],'c'=>[{'d'=>'e'}]}): }\")).to have_resource('Notify[true]')\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/each_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the each method' do\n  include PuppetSpec::Compiler\n\n  context \"should be callable as\" do\n    it 'each on an array selecting each value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $a.each |$v| {\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_1\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_2\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_3\")['ensure']).to eq('present')\n    end\n\n    it 'each on an array selecting each value - function call style' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        each ($a) |$index, $v| {\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_1\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_2\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_3\")['ensure']).to eq('present')\n    end\n\n    it 'each on an array with index' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [present, absent, present]\n        $a.each |$k,$v| {\n          file { \"/file_${$k+1}\": ensure => $v }\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_1\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_2\")['ensure']).to eq('absent')\n      expect(catalog.resource(:file, \"/file_3\")['ensure']).to eq('present')\n    end\n\n    it 'each on a hash selecting entries' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>'present','b'=>'absent','c'=>'present'}\n        $a.each |$e| {\n        file { \"/file_${e[0]}\": ensure => $e[1] }\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_a\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_b\")['ensure']).to eq('absent')\n      expect(catalog.resource(:file, \"/file_c\")['ensure']).to eq('present')\n    end\n\n    it 'each on a hash selecting key and value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>present,'b'=>absent,'c'=>present}\n        $a.each |$k, $v| {\n          file { \"/file_$k\": ensure => $v }\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_a\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_b\")['ensure']).to eq('absent')\n      expect(catalog.resource(:file, \"/file_c\")['ensure']).to eq('present')\n    end\n\n    it 'each on a hash selecting key and value (using captures-last parameter)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>present,'b'=>absent,'c'=>present}\n        $a.each |*$kv| {\n          file { \"/file_${kv[0]}\": ensure => $kv[1] }\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_a\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/file_b\")['ensure']).to eq('absent')\n      expect(catalog.resource(:file, \"/file_c\")['ensure']).to eq('present')\n    end\n  end\n\n  context \"should produce receiver\" do\n    it 'each checking produced value using single expression' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, 3, 2]\n        $b = $a.each |$x| { \"unwanted\" }\n        file { \"/file_${b[1]}\":\n          ensure => present\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_3\")['ensure']).to eq('present')\n    end\n\n  end\n  it_should_behave_like 'all iterative functions argument checks', 'each'\n  it_should_behave_like 'all iterative functions hash handling', 'each'\n\nend\n"
  },
  {
    "path": "spec/unit/functions/empty_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the empty function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  let(:logs) { [] }\n  let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n  context 'for an array it' do\n    it 'returns true when empty' do\n      expect(compile_to_catalog(\"notify { String(empty([])): }\")).to have_resource('Notify[true]')\n    end\n\n    it 'returns false when not empty' do\n      expect(compile_to_catalog(\"notify { String(empty([1])): }\")).to have_resource('Notify[false]')\n    end\n  end\n\n  context 'for a hash it' do\n    it 'returns true when empty' do\n      expect(compile_to_catalog(\"notify { String(empty({})): }\")).to have_resource('Notify[true]')\n    end\n\n    it 'returns false when not empty' do\n      expect(compile_to_catalog(\"notify { String(empty({1=>1})): }\")).to have_resource('Notify[false]')\n    end\n  end\n\n  context 'for numeric values it' do\n    it 'always returns false for integer values (including 0)' do\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(compile_to_catalog(\"notify { String(empty(0)): }\")).to have_resource('Notify[false]')\n      end\n      expect(warnings).to include(/Calling function empty\\(\\) with Numeric value is deprecated/)\n    end\n\n    it 'always returns false for float values (including 0.0)' do\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(compile_to_catalog(\"notify { String(empty(0.0)): }\")).to have_resource('Notify[false]')\n      end\n      expect(warnings).to include(/Calling function empty\\(\\) with Numeric value is deprecated/)\n    end\n  end\n\n  context 'for a string it' do\n    it 'returns true when empty' do\n      expect(compile_to_catalog(\"notify { String(empty('')): }\")).to have_resource('Notify[true]')\n    end\n\n    it 'returns false when not empty' do\n      expect(compile_to_catalog(\"notify { String(empty(' ')): }\")).to have_resource('Notify[false]')\n    end\n  end\n\n  context 'for a sensitive string it' do\n    it 'returns true when empty' do\n      expect(compile_to_catalog(\"notify { String(empty(Sensitive(''))): }\")).to have_resource('Notify[true]')\n    end\n\n    it 'returns false when not empty' do\n      expect(compile_to_catalog(\"notify { String(empty(Sensitive(' '))): }\")).to have_resource('Notify[false]')\n    end\n  end\n\n  context 'for a binary it' do\n    it 'returns true when empty' do\n      expect(compile_to_catalog(\"notify { String(empty(Binary(''))): }\")).to have_resource('Notify[true]')\n    end\n\n    it 'returns false when not empty' do\n      expect(compile_to_catalog(\"notify { String(empty(Binary('b25l'))): }\")).to have_resource('Notify[false]')\n    end\n  end\n\n  context 'for undef it' do\n    it 'returns true without deprecation warning' do\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(compile_to_catalog(\"notify { String(empty(undef)): }\")).to have_resource('Notify[true]')\n      end\n      expect(warnings).to_not include(/Calling function empty\\(\\) with Undef value is deprecated/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/epp_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the epp function\" do\n  include PuppetSpec::Files\n\n  let :node     do Puppet::Node.new('localhost') end\n  let :compiler do Puppet::Parser::Compiler.new(node) end\n  let :scope    do compiler.topscope end\n\n  context \"when accessing scope variables as $ variables\" do\n    it \"looks up the value from the scope\" do\n      scope[\"what\"] = \"are belong\"\n      expect(eval_template(\"all your base <%= $what %> to us\")).to eq(\"all your base are belong to us\")\n    end\n\n    it \"looks up a fully qualified value from the scope\" do\n      scope[\"what::is\"] = \"are belong\"\n      expect(eval_template(\"all your base <%= $what::is %> to us\")).to eq(\"all your base are belong to us\")\n    end\n\n    it \"gets error accessing a variable that does not exist\" do\n      expect { eval_template(\"<%= $kryptonite == undef %>\")}.to raise_error(/Evaluation Error: Unknown variable: 'kryptonite'./)\n    end\n\n    it \"get nil accessing a variable that does not exist when strict mode is off\" do\n      Puppet[:strict_variables] = false\n      Puppet[:strict] = :warning\n      expect(eval_template(\"<%= $kryptonite == undef %>\")).to eq(\"true\")\n    end\n\n    it \"gets error accessing a variable that is malformed\" do\n      expect { eval_template(\"<%= $kryptonite::bbbbbbbbbbbb::cccccccc::ddd::USER %>\")}.to raise_error(\n        /Illegal variable name, The given name 'kryptonite::bbbbbbbbbbbb::cccccccc::ddd::USER' does not conform to the naming rule/)\n    end\n\n    it \"gets error accessing a variable that is malformed as reported in PUP-7848\" do\n      expect { eval_template(\"USER='<%= $hg_oais::archivematica::requirements::automation_tools::USER %>'\")}.to raise_error(\n        /Illegal variable name, The given name 'hg_oais::archivematica::requirements::automation_tools::USER' does not conform to the naming rule/)\n    end\n\n    it \"get nil accessing a variable that is undef\" do\n      scope['undef_var'] = nil\n      expect(eval_template(\"<%= $undef_var == undef %>\")).to eq(\"true\")\n    end\n\n    it \"gets shadowed variable if args are given\" do\n      scope['phantom'] = 'of the opera'\n      expect(eval_template_with_args(\"<%= $phantom == dragos %>\", 'phantom' => 'dragos')).to eq(\"true\")\n    end\n\n    it \"can use values from the global scope for defaults\" do\n      scope['phantom'] = 'of the opera'\n      expect(eval_template(\"<%- |$phantom = $::phantom| -%><%= $phantom %>\")).to eq(\"of the opera\")\n    end\n\n    it \"will not use values from the enclosing scope for defaults\" do\n      scope['the_phantom'] = 'of the state opera'\n      scope.new_ephemeral(true)\n      scope['the_phantom'] = 'of the local opera'\n      expect(scope['the_phantom']).to eq('of the local opera')\n      expect(eval_template(\"<%- |$phantom = $the_phantom| -%><%= $phantom %>\")).to eq(\"of the state opera\")\n     end\n\n    it \"uses the default value if the given value is undef/nil\" do\n      expect(eval_template_with_args(\"<%- |$phantom = 'inside your mind'| -%><%= $phantom %>\", 'phantom' => nil)).to eq(\"inside your mind\")\n    end\n\n    it \"gets shadowed variable if args are given and parameters are specified\" do\n      scope['x'] = 'wrong one'\n      expect(eval_template_with_args(\"<%- |$x| -%><%= $x == correct %>\", 'x' => 'correct')).to eq(\"true\")\n    end\n\n    it \"raises an error if required variable is not given\" do\n      scope['x'] = 'wrong one'\n      expect do\n        eval_template(\"<%-| $x |-%><%= $x == correct %>\")\n      end.to raise_error(/expects a value for parameter 'x'/)\n    end\n\n    it 'raises an error if invalid arguments are given' do\n      scope['x'] = 'wrong one'\n      expect do\n        eval_template_with_args(\"<%-| $x |-%><%= $x == correct %>\", 'x' => 'correct', 'y' => 'surplus')\n      end.to raise_error(/has no parameter named 'y'/)\n    end\n  end\n\n  context \"when given an empty template\" do\n     it \"allows the template file to be empty\" do\n       expect(eval_template(\"\")).to eq(\"\")\n     end\n\n    it \"allows the template to have empty body after parameters\" do\n      expect(eval_template_with_args(\"<%-|$x|%>\", 'x'=>1)).to eq(\"\")\n    end\n  end\n\n  context \"when using typed parameters\" do\n    it \"allows a passed value that matches the parameter's type\" do\n      expect(eval_template_with_args(\"<%-|String $x|-%><%= $x == correct %>\", 'x' => 'correct')).to eq(\"true\")\n    end\n\n    it \"does not allow slurped parameters\" do\n      expect do\n        eval_template_with_args(\"<%-|*$x|-%><%= $x %>\", 'x' => 'incorrect')\n      end.to raise_error(/'captures rest' - not supported in an Epp Template/)\n    end\n\n    it \"raises an error when the passed value does not match the parameter's type\" do\n      expect do\n        eval_template_with_args(\"<%-|Integer $x|-%><%= $x %>\", 'x' => 'incorrect')\n      end.to raise_error(/parameter 'x' expects an Integer value, got String/)\n    end\n\n    it \"raises an error when the default value does not match the parameter's type\" do\n      expect do\n        eval_template(\"<%-|Integer $x = 'nope'|-%><%= $x %>\")\n      end.to raise_error(/parameter 'x' expects an Integer value, got String/)\n    end\n\n    it \"allows an parameter to default to undef\" do\n      expect(eval_template(\"<%-|Optional[Integer] $x = undef|-%><%= $x == undef %>\")).to eq(\"true\")\n    end\n  end\n\n  it \"preserves CRLF when reading the template\" do\n    expect(eval_template(\"some text that\\r\\nis static with CRLF\")).to eq(\"some text that\\r\\nis static with CRLF\")\n  end\n\n  # although never a problem with epp\n  it \"is not interfered with by having a variable named 'string' (#14093)\" do\n    scope['string'] = \"this output should not be seen\"\n    expect(eval_template(\"some text that is static\")).to eq(\"some text that is static\")\n  end\n\n  it \"has access to a variable named 'string' (#14093)\" do\n    scope['string'] = \"the string value\"\n    expect(eval_template(\"string was: <%= $string %>\")).to eq(\"string was: the string value\")\n  end\n\n  describe 'when loading from modules' do\n    include PuppetSpec::Files\n    it 'an epp template is found' do\n      modules_dir = dir_containing('modules', {\n        'testmodule'  => {\n            'templates' => {\n              'the_x.epp' => 'The x is <%= $x %>'\n            }\n        }})\n      Puppet.override({:current_environment => (env = Puppet::Node::Environment.create(:testload, [ modules_dir ]))}, \"test\") do\n        node.environment = env\n        expect(epp_function.call(scope, 'testmodule/the_x.epp', { 'x' => '3'} )).to eql(\"The x is 3\")\n      end\n    end\n  end\n\n  def eval_template_with_args(content, args_hash)\n    file_path = tmpdir('epp_spec_content')\n    filename = File.join(file_path, \"template.epp\")\n    File.open(filename, \"wb+\") { |f| f.write(content) }\n\n    allow(Puppet::Parser::Files).to receive(:find_template).and_return(filename)\n    epp_function.call(scope, 'template', args_hash)\n  end\n\n  def eval_template(content)\n    file_path = tmpdir('epp_spec_content')\n    filename = File.join(file_path, \"template.epp\")\n    File.open(filename, \"wb+\") { |f| f.write(content) }\n\n    allow(Puppet::Parser::Files).to receive(:find_template).and_return(filename)\n    epp_function.call(scope, 'template')\n  end\n\n  def epp_function()\n    scope.compiler.loaders.public_environment_loader.load(:function, 'epp')\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/filter_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the filter method' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'should filter on an array (all berries)' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = ['strawberry','blueberry','orange']\n      $a.filter |$x|{ $x  =~ /berry$/}.each |$v|{\n        file { \"/file_$v\": ensure => present }\n      }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_strawberry]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_blueberry]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'should filter on enumerable type (Integer)' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = Integer[1,10]\n      $a.filter |$x|{ $x  % 3 == 0}.each |$v|{\n        file { \"/file_$v\": ensure => present }\n      }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_9]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'should filter on enumerable type (Integer) using two args index/value' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = Integer[10,18]\n      $a.filter |$i, $x|{ $i  % 3 == 0}.each |$v|{\n        file { \"/file_$v\": ensure => present }\n      }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_10]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_13]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_16]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'should produce an array when acting on an array' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = ['strawberry','blueberry','orange']\n      $b = $a.filter |$x|{ $x  =~ /berry$/}\n      file { \"/file_${b[0]}\": ensure => present }\n      file { \"/file_${b[1]}\": ensure => present }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_strawberry]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_blueberry]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'can filter array using index and value' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = ['strawberry','blueberry','orange']\n      $b = $a.filter |$index, $x|{ $index  == 0 or $index ==2}\n      file { \"/file_${b[0]}\": ensure => present }\n      file { \"/file_${b[1]}\": ensure => present }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_strawberry]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_orange]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'can filter array using index and value (using captures-rest)' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = ['strawberry','blueberry','orange']\n      $b = $a.filter |*$ix|{ $ix[0]  == 0 or $ix[0] ==2}\n      file { \"/file_${b[0]}\": ensure => present }\n      file { \"/file_${b[1]}\": ensure => present }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_strawberry]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_orange]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'filters on a hash (all berries) by key' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}\n      $a.filter |$x|{ $x[0]  =~ /berry$/}.each |$v|{\n        file { \"/file_${v[0]}\": ensure => present }\n      }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_strawberry]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_blueberry]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'should produce a hash when acting on a hash' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}\n      $b = $a.filter |$x|{ $x[0]  =~ /berry$/}\n      file { \"/file_${b['strawberry']}\": ensure => present }\n      file { \"/file_${b['blueberry']}\": ensure => present }\n      file { \"/file_${b['orange']}\": ensure => present }\n\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_red]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_blue]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'filters on a hash (all berries) by value' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'strawb'=>'red berry','blueb'=>'blue berry','orange'=>'orange fruit'}\n      $a.filter |$x|{ $x[1]  =~ /berry$/}.each |$v|{\n        file { \"/file_${v[0]}\": ensure => present }\n      }\n    MANIFEST\n\n    expect(catalog).to have_resource(\"File[/file_strawb]\").with_parameter(:ensure, 'present')\n    expect(catalog).to have_resource(\"File[/file_blueb]\").with_parameter(:ensure, 'present')\n  end\n\n  it 'filters on an array will include elements for which the block returns truthy' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $r = [1, 2, false, undef].filter |$v| { $v } == [1, 2]\n      notify { \"eval_${$r}\": }\n    MANIFEST\n\n    expect(catalog).to have_resource('Notify[eval_true]')\n  end\n\n  it 'filters on a hash will not include elements for which the block returns truthy' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      $r = {a => 1, b => 2, c => false, d=> undef}.filter |$k, $v| { $v } == {a => 1, b => 2}\n      notify { \"eval_${$r}\": }\n    MANIFEST\n\n    expect(catalog).to have_resource('Notify[eval_true]')\n  end\n\n  it_should_behave_like 'all iterative functions argument checks', 'filter'\n  it_should_behave_like 'all iterative functions hash handling', 'filter'\nend\n"
  },
  {
    "path": "spec/unit/functions/find_file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\nrequire 'puppet_spec/files'\n\ndescribe 'the find_file function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n  include PuppetSpec::Files\n\n  def with_file_content(content)\n    path = tmpfile('find-file-function')\n    file = File.new(path, 'wb')\n    file.sync = true\n    file.print content\n    yield path\n  end\n\n  it 'finds an existing absolute file when given arguments individually' do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(compile_to_catalog(\"notify { find_file('#{one}', '#{two}'):}\")).to have_resource(\"Notify[#{one}]\")\n      end\n    end\n  end\n\n  it 'skips non existing files' do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(compile_to_catalog(\"notify { find_file('#{one}/nope', '#{two}'):}\")).to have_resource(\"Notify[#{two}]\")\n      end\n    end\n  end\n\n  it 'accepts arguments given as an array' do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(compile_to_catalog(\"notify { find_file(['#{one}', '#{two}']):}\")).to have_resource(\"Notify[#{one}]\")\n      end\n    end\n  end\n\n  it 'finds an existing file in a module' do\n    with_file_content('file content') do |name|\n      mod = double('module')\n      allow(mod).to receive(:file).with('myfile').and_return(name)\n      Puppet[:code] = \"notify { find_file('mymod/myfile'):}\"\n      node = Puppet::Node.new('localhost')\n      compiler = Puppet::Parser::Compiler.new(node)\n      allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n\n      expect(compiler.compile().filter { |r| r.virtual? }).to have_resource(\"Notify[#{name}]\")\n    end\n  end\n\n  it 'returns undef when none of the paths were found' do\n    mod = double('module')\n    allow(mod).to receive(:file).with('myfile').and_return(nil)\n    Puppet[:code] = \"notify { String(type(find_file('mymod/myfile', 'nomod/nofile'))):}\"\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    # For a module that does not have the file\n    allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n    # For a module that does not exist\n    allow(compiler.environment).to receive(:module).with('nomod').and_return(nil)\n\n    expect(compiler.compile().filter { |r| r.virtual? }).to have_resource(\"Notify[Undef]\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/find_template_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\nrequire 'puppet_spec/files'\n\ndescribe 'the find_template function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n  include PuppetSpec::Files\n\n  def with_file_content(content)\n    path = tmpfile('find-file-function')\n    file = File.new(path, 'wb')\n    file.sync = true\n    file.print content\n    yield path\n  end\n\n  it 'finds an existing absolute file when given arguments individually' do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(compile_to_catalog(\"notify { find_template('#{one}', '#{two}'):}\")).to have_resource(\"Notify[#{one}]\")\n      end\n    end\n  end\n\n  it 'skips non existing files' do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(compile_to_catalog(\"notify { find_template('#{one}/nope', '#{two}'):}\")).to have_resource(\"Notify[#{two}]\")\n      end\n    end\n  end\n\n  it 'accepts arguments given as an array' do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(compile_to_catalog(\"notify { find_template(['#{one}', '#{two}']):}\")).to have_resource(\"Notify[#{one}]\")\n      end\n    end\n  end\n\n  it 'finds an existing file in a module' do\n    with_file_content('file content') do |name|\n      mod = double('module')\n      allow(mod).to receive(:template).with('myfile').and_return(name)\n      Puppet[:code] = \"notify { find_template('mymod/myfile'):}\"\n      node = Puppet::Node.new('localhost')\n      compiler = Puppet::Parser::Compiler.new(node)\n      allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n\n      expect(compiler.compile().filter { |r| r.virtual? }).to have_resource(\"Notify[#{name}]\")\n    end\n  end\n\n  it 'returns undef when none of the paths were found' do\n    mod = double('module')\n    allow(mod).to receive(:template).with('myfile').and_return(nil)\n    Puppet[:code] = \"notify { String(type(find_template('mymod/myfile', 'nomod/nofile'))):}\"\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    # For a module that does not have the file\n    allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n    # For a module that does not exist\n    allow(compiler.environment).to receive(:module).with('nomod').and_return(nil)\n\n    expect(compiler.compile().filter { |r| r.virtual? }).to have_resource(\"Notify[Undef]\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/flatten_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the flatten function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  let(:array_fmt) { { 'format' => \"%(a\", 'separator'=>\"\"} }\n\n  it 'returns flattened array of all its given arguments' do\n    expect(compile_to_catalog(\"notify { String([1,[2,[3]]].flatten, Array => #{array_fmt}): }\")).to have_resource('Notify[(123)]')\n  end\n\n  it 'accepts a single non array value which results in it being wrapped in an array' do\n    expect(compile_to_catalog(\"notify { String(flatten(1), Array => #{array_fmt}): }\")).to have_resource('Notify[(1)]')\n  end\n\n  it 'accepts a single array value - (which is a noop)' do\n    expect(compile_to_catalog(\"notify { String(flatten([1]), Array => #{array_fmt}): }\")).to have_resource('Notify[(1)]')\n  end\n\n  it 'it does not flatten a hash - it is a value that gets wrapped' do\n    expect(compile_to_catalog(\"notify { String(flatten({a=>1}), Array => #{array_fmt}): }\")).to have_resource(\"Notify[({'a' => 1})]\")\n  end\n\n  it 'accepts mix of array and non array arguments and concatenates and flattens them' do\n    expect(compile_to_catalog(\"notify { String(flatten([1],2,[[3,4]]), Array => #{array_fmt}): }\")).to have_resource('Notify[(1234)]')\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/floor_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the floor function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an integer' do\n    [ 0, 1, -1].each do |x|\n      it \"called as floor(#{x}) results in the same value\" do\n        expect(compile_to_catalog(\"notify { String( floor(#{x}) == #{x}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for a float' do\n    {\n      0.0 => 0,\n      1.1 => 1,\n      -1.1 => -2,\n    }.each_pair do |x, expected|\n      it \"called as floor(#{x}) results in #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( floor(#{x}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for a string' do\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n    { \"0\" => 0,\n      \"1\" => 1,\n      \"-1\" => -1,\n      \"0.0\" => 0,\n      \"1.1\" => 1,\n      \"-1.1\" => -2,\n      \"0777\" => 777,\n      \"-0777\" => -777,\n      \"0xFF\" => 0xFF,\n    }.each_pair do |x, expected|\n      it \"called as floor('#{x}') results in #{expected} and a deprecation warning\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( floor('#{x}') == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n    ['blue', '0.2.3'].each do |x|\n      it \"errors as the string '#{x}' cannot be converted to a float (indirectly deprecated)\" do\n        expect{ compile_to_catalog(\"floor('#{x}')\") }.to raise_error(/cannot convert given value to a floating point value/)\n      end\n    end\n  end\n\n  [[1,2,3], {'a' => 10}].each do |x|\n    it \"errors for a value of class #{x.class} (indirectly deprecated)\" do\n      expect{ compile_to_catalog(\"floor(#{x})\") }.to raise_error(/expects a value of type Numeric or String/)\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/get_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the get function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'returns undef value' do\n    it 'when result is undef due to given undef' do\n      expect( evaluate(source: \"get(undef, '')\")).to be_nil\n    end\n\n    it 'when result is undef due to navigation into undef' do\n      expect( evaluate(source: \"get(undef, '0')\")).to be_nil\n    end\n\n    it 'when navigation results in explicit undef in an array' do\n      expect( evaluate(source: \"get([undef], '0')\")).to be_nil\n    end\n\n    it 'when navigation results in explicit undef in a hash' do\n      expect( evaluate(source: \"get(a => undef, 'a')\")).to be_nil\n    end\n\n    it 'when navigation references unavailable value in an array' do\n      expect( evaluate(source: \"get([1], '2')\")).to be_nil\n    end\n\n    it 'when navigation references unavailable value in a hash' do\n      expect( evaluate(source: \"get(a => 43, 'b')\")).to be_nil\n    end\n  end\n\n  context 'returns default value' do\n    it 'when result is undef due to given undef' do\n      expect( evaluate(source: \"get(undef, '', 'ok')\")).to eql('ok')\n    end\n\n    it 'when result is undef due to navigation into undef' do\n      expect( evaluate(source: \"get(undef, '0', 'ok')\")).to eql('ok')\n    end\n\n    it 'when navigation results in explicit undef in array' do\n      expect( evaluate(source: \"get([undef], '0', 'ok')\")).to eql('ok')\n    end\n\n    it 'when navigation results in explicit undef in hash' do\n      expect( evaluate(source: \"get(a => undef, 'a', 'ok')\")).to eql('ok')\n    end\n\n    it 'when navigation references unavailable value in an array' do\n      expect( evaluate(source: \"get([1], '2', 'ok')\")).to eql('ok')\n    end\n\n    it 'when navigation references unavailable value in a hash' do\n      expect( evaluate(source: \"get(a => 43, 'b', 'ok')\")).to eql('ok')\n    end\n  end\n\n  it 'returns value if given empty navigation' do\n    expect(evaluate(source: \"get('ok', '')\")).to eql('ok')\n  end\n\n  it 'navigates into array as given by navigation string' do\n    expect( evaluate( source: \"get(['nope', ['ok']], '1.0')\")).to eql('ok')\n  end\n\n  it 'navigates into hash as given by navigation string' do\n    expect( evaluate( source: \"get(a => 'nope', b=> ['ok'], 'b.0')\")).to eql('ok')\n  end\n\n  it 'navigates into hash with numeric key' do\n    expect( evaluate( source: \"get(a => 'nope', 0=> ['ok'], '0.0')\")).to eql('ok')\n  end\n\n  it 'navigates into hash with numeric string but requires quoting' do\n    expect( evaluate( source: \"get(a => 'nope', '0'=> ['ok'], '\\\"0\\\".0')\")).to eql('ok')\n  end\n\n  it 'can navigate a key with . when it is quoted' do\n    expect(\n      evaluate(\n        variables: {'x' => {'a.b' => ['nope', ['ok']]}},\n        source: \"get($x, '\\\"a.b\\\".1.0')\"\n      )\n    ).to eql('ok')\n  end\n\n  it 'an error is raised when navigating with string key into an array' do\n    expect {\n      evaluate(source: \"get(['nope', ['ok']], '1.blue')\")\n    }.to raise_error(/The given data requires an Integer index/)\n  end\n\n  it 'calls a given block with EXPECTED_INTEGER_INDEX if navigating into array with string' do\n    expect(evaluate(\n             source:\n               \"get(['nope', ['ok']], '1.blue') |$error| {\n                   if $error.issue_code =~ /^EXPECTED_INTEGER_INDEX$/ {'ok'} else { 'nope'}\n                }\"\n           )).to eql('ok')\n  end\n\n  it 'calls a given block with EXPECTED_COLLECTION if navigating into something not Undef or Collection' do\n    expect(evaluate(\n             source:\n               \"get(['nope', /nah/], '1.blue') |$error| {\n                  if $error.issue_code =~ /^EXPECTED_COLLECTION$/ {'ok'} else { 'nope'}\n               }\"\n           )).to eql('ok')\n  end\n\n  context 'it does not pick the default value when undef is returned by error handling block' do\n    it 'for \"expected integer\" case' do\n      expect(evaluate(\n               source: \"get(['nope', ['ok']], '1.blue', 'nope') |$msg| { undef }\"\n             )).to be_nil\n    end\n\n    it 'for \"expected collection\" case' do\n      expect(evaluate(\n               source: \"get(['nope', /nah/], '1.blue') |$msg| { undef }\"\n             )).to be_nil\n    end\n  end\n\n  it 'does not call a given block if navigation string has syntax error' do\n    expect {\n      evaluate(source: \"get(['nope', /nah/], '1....') |$msg| { fail('so sad') }\")\n    }.to raise_error(/Syntax error/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/getvar_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the getvar function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'returns undef value' do\n    it 'when result is undef due to missing variable' do\n      expect( evaluate(source: \"getvar('x')\")).to be_nil\n    end\n\n    it 'when result is undef due to resolved undef variable value' do\n      expect( evaluate(source: \"$x = undef; getvar('x')\")).to be_nil\n    end\n\n    it 'when result is undef due to navigation into undef' do\n      expect( evaluate(source: \"$x = undef; getvar('x.0')\")).to be_nil\n    end\n  end\n\n  context 'returns default value' do\n    it 'when result is undef due to missing variable' do\n      expect( evaluate(source: \"getvar('x', 'ok')\")).to eql('ok')\n    end\n\n    it 'when result is undef due to resolved undef variable value' do\n      expect( evaluate(source: \"$x = undef; getvar('x', 'ok')\")).to eql('ok')\n    end\n\n    it 'when result is undef due to navigation into undef' do\n      expect( evaluate(source: \"$x = undef; getvar('x.0', 'ok')\")).to eql('ok')\n    end\n  end\n\n  it 'returns value of $variable if dotted navigation is not present' do\n    expect(evaluate(source: \"$x = 'testing'; getvar('x')\")).to eql('testing')\n  end\n\n  it 'returns value of fully qualified $namespace::variable if dotted navigation is not present' do\n    expect(evaluate(\n             code:\n               \"class testing::nested { $x = ['ok'] } include 'testing::nested'\",\n             source:\n               \"getvar('testing::nested::x.0')\"\n           )).to eql('ok')\n  end\n\n  it 'navigates into $variable if given dot syntax after variable name' do\n    expect(\n      evaluate(\n        variables: {'x'=> ['nope', ['ok']]},\n        source: \"getvar('x.1.0')\"\n      )\n    ).to eql('ok')\n  end\n\n  it 'can navigate a key with . when it is quoted' do\n    expect(\n      evaluate(\n        variables: {'x' => {'a.b' => ['nope', ['ok']]}},\n        source: \"getvar('x.\\\"a.b\\\".1.0')\"\n      )\n    ).to eql('ok')\n  end\n\n  it 'an error is raised when navigating with string key into an array' do\n    expect {\n      evaluate(source: \"$x =['nope', ['ok']]; getvar('x.1.blue')\")\n    }.to raise_error(/The given data requires an Integer index/)\n  end\n\n  ['X', ':::x', 'x:::x', 'x-x', '_x::x', 'x::', '1'].each do |var_string|\n    it \"an error pointing out that varible is invalid is raised for variable '#{var_string}'\" do\n      expect {\n        evaluate(source: \"getvar(\\\"#{var_string}.1.blue\\\")\")\n      }.to raise_error(/The given string does not start with a valid variable name/)\n    end\n  end\n\n  it 'calls a given block with EXPECTED_INTEGER_INDEX if navigating into array with string' do\n    expect(evaluate(\n             source:\n               \"$x = ['nope', ['ok']]; getvar('x.1.blue') |$error| {\n                 if $error.issue_code =~ /^EXPECTED_INTEGER_INDEX$/ {'ok'} else { 'nope'}\n                }\"\n           )).to eql('ok')\n  end\n\n  it 'calls a given block with EXPECTED_COLLECTION if navigating into something not Undef or Collection' do\n    expect(evaluate(\n             source:\n               \"$x = ['nope', /nah/]; getvar('x.1.blue') |$error| {\n                  if $error.issue_code =~ /^EXPECTED_COLLECTION$/ {'ok'} else { 'nope'}\n                }\"\n           )).to eql('ok')\n  end\n\n  context 'it does not pick the default value when undef is returned by error handling block' do\n    it 'for \"expected integer\" case' do\n      expect(evaluate(\n               source: \"$x = ['nope', ['ok']]; getvar('x.1.blue', 'nope') |$msg| { undef }\"\n             )).to be_nil\n    end\n\n    it 'for \"expected collection\" case' do\n      expect(evaluate(\n               source: \"$x = ['nope', /nah/]; getvar('x.1.blue') |$msg| { undef }\"\n             )).to be_nil\n    end\n  end\n\n  it 'does not call a given block if navigation string has syntax error' do\n    expect {evaluate(\n      source: \"$x = ['nope', /nah/]; getvar('x.1....') |$msg| { fail('so sad') }\"\n    )}.to raise_error(/Syntax error/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/group_by_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the group_by function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an array' do\n    it 'groups by item' do\n      manifest = \"notify { String(group_by([a, b, ab]) |$s| { $s.length }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[{1 => ['a', 'b'], 2 => ['ab']}]\")\n    end\n\n    it 'groups by index, item' do\n      manifest = \"notify { String(group_by([a, b, ab]) |$i, $s| { $i%2 + $s.length }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[{1 => ['a'], 2 => ['b', 'ab']}]\")\n    end\n  end\n\n  context 'for a hash' do\n    it 'groups by key-value pair' do\n      manifest = \"notify { String(group_by(a => [1, 2], b => [1]) |$kv| { $kv[1].length }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[{2 => [['a', [1, 2]]], 1 => [['b', [1]]]}]\")\n    end\n\n    it 'groups by key, value' do\n      manifest = \"notify { String(group_by(a => [1, 2], b => [1]) |$k, $v| { $v.length }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[{2 => [['a', [1, 2]]], 1 => [['b', [1]]]}]\")\n    end\n  end\n\n  context 'for a string' do\n    it 'fails' do\n      manifest = \"notify { String(group_by('something') |$s| { $s.length }): }\"\n      expect { compile_to_catalog(manifest) }.to raise_error(Puppet::PreformattedError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/hiera_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/pops'\n\ndescribe 'when calling' do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  let(:global_dir) { tmpdir('global') }\n  let(:env_config) { {} }\n  let(:hiera_yaml) { <<-YAML.unindent }\n    ---\n    :backends:\n      - yaml\n      - hieraspec\n    :yaml:\n      :datadir: #{global_dir}/hieradata\n    :hierarchy:\n      - first\n      - second\n    YAML\n\n  let(:hieradata_files) do\n    {\n      'first.yaml' => <<-YAML.unindent,\n        ---\n        a: first a\n        class_name: \"-- %{calling_class} --\"\n        class_path: \"-- %{calling_class_path} --\"\n        module: \"-- %{calling_module} --\"\n        mod_name: \"-- %{module_name} --\"\n        database_user:\n          name: postgres\n          uid: 500\n          gid: 500\n          groups:\n            db: 520\n        b:\n          b1: first b1\n          b2: first b2\n        fbb:\n          - mod::foo\n          - mod::bar\n          - mod::baz\n        empty_array: []\n        nested_array:\n          first:\n            - 10\n            - 11\n          second:\n            - 21\n            - 22\n        dotted.key:\n          a: dotted.key a\n          b: dotted.key b\n        dotted.array:\n          - a\n          - b\n        YAML\n      'second.yaml' => <<-YAML.unindent,\n        ---\n        a: second a\n        b:\n          b1: second b1\n          b3: second b3\n        YAML\n      'the_override.yaml' => <<-YAML.unindent\n        ---\n        key: foo_result\n        YAML\n    }\n  end\n\n  let(:environment_files) do\n    {\n      'test' => {\n        'modules' => {\n          'mod' => {\n            'manifests' => {\n              'foo.pp' => <<-PUPPET.unindent,\n                class mod::foo {\n                  notice(hiera('class_name'))\n                  notice(hiera('class_path'))\n                  notice(hiera('module'))\n                  notice(hiera('mod_name'))\n                }\n                PUPPET\n              'bar.pp' => <<-PUPPET.unindent,\n                class mod::bar {}\n                PUPPET\n              'baz.pp' => <<-PUPPET.unindent\n                class mod::baz {}\n                PUPPET\n              },\n            'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              YAML\n            'data' => {\n              'common.yaml' => <<-YAML.unindent\n                mod::c: mod::c (from module)\n                YAML\n            }\n          }\n        }\n      }.merge(env_config)\n    }\n  end\n\n  let(:global_files) do\n    {\n      'hiera.yaml' => hiera_yaml,\n      'hieradata' => hieradata_files,\n      'environments' => environment_files\n    }\n  end\n\n  let(:logs) { [] }\n  let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n  let(:env_dir) { File.join(global_dir, 'environments') }\n  let(:env) { Puppet::Node::Environment.create(:test, [File.join(env_dir, 'test', 'modules')]) }\n  let(:environments) { Puppet::Environments::Directories.new(env_dir, []) }\n  let(:node) { Puppet::Node.new('test_hiera', :environment => env) }\n  let(:compiler) { Puppet::Parser::Compiler.new(node) }\n  let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera') }\n\n  before(:all) do\n    $LOAD_PATH.unshift(File.join(my_fixture_dir))\n  end\n\n  after(:all) do\n    Hiera::Backend.send(:remove_const, :Hieraspec_backend) if Hiera::Backend.const_defined?(:Hieraspec_backend)\n    $LOAD_PATH.shift\n  end\n\n  before(:each) do\n    Puppet.settings[:codedir] = global_dir\n    Puppet.settings[:hiera_config] = File.join(global_dir, 'hiera.yaml')\n  end\n\n  around(:each) do |example|\n    dir_contained_in(global_dir, global_files)\n    Puppet.override(:environments => environments, :current_environment => env) do\n      example.run\n    end\n  end\n\n  def with_scope(code = 'undef')\n    result = nil\n    Puppet[:code] = 'undef'\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      compiler.compile do |catalog|\n        result = yield(compiler.topscope)\n        catalog\n      end\n    end\n    result\n  end\n\n  def func(*args, &block)\n    with_scope { |scope| the_func.call(scope, *args, &block) }\n  end\n\n  context 'hiera', :if => Puppet.features.hiera? do\n    it 'should require a key argument' do\n      expect { func([]) }.to raise_error(ArgumentError)\n    end\n\n    it 'should raise a useful error when nil is returned' do\n      expect { func('badkey') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'badkey'/)\n    end\n\n    it 'should use the \"first\" merge strategy' do\n      expect(func('a')).to eql('first a')\n    end\n\n    it 'should allow lookup with quoted dotted key' do\n      expect(func(\"'dotted.key'\")).to eql({'a' => 'dotted.key a', 'b' => 'dotted.key b'})\n    end\n\n    it 'should allow lookup with dotted key' do\n      expect(func('database_user.groups.db')).to eql(520)\n    end\n\n    it 'should not find data in module' do\n      expect(func('mod::c', 'default mod::c')).to eql('default mod::c')\n    end\n\n    it 'should propagate optional override' do\n      ovr = 'the_override'\n      expect(func('key', nil, ovr)).to eql('foo_result')\n    end\n\n    it 'backend data sources, including optional overrides, are propagated to custom backend' do\n      expect(func('datasources', nil, 'the_override')).to eql(['the_override', 'first', 'second'])\n    end\n\n    it 'a hiera v3 scope is used', :if => Puppet.features.hiera? do\n      expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['-- testing --', '-- mod::foo --', '-- mod/foo --', '-- mod --', '-- mod --'])\n      class testing () {\n         notice(hiera('class_name'))\n      }\n      include testing\n      include mod::foo\n      PUPPET\n    end\n\n    it 'should return default value nil when key is not found' do\n       expect(func('foo', nil)).to be_nil\n    end\n\n    it \"should return default value '' when key is not found\" do\n      expect(func('foo', '')).to eq('')\n    end\n\n    it 'should use default block' do\n      expect(func('foo') { |k| \"default for key '#{k}'\" }).to eql(\"default for key 'foo'\")\n    end\n\n    it 'should propagate optional override when combined with default block' do\n      ovr = 'the_override'\n      with_scope do |scope|\n        expect(the_func.call(scope, 'key', ovr) { |k| \"default for key '#{k}'\" }).to eql('foo_result')\n        expect(the_func.call(scope, 'foo.bar', ovr) { |k| \"default for key '#{k}'\" }).to eql(\"default for key 'foo.bar'\")\n      end\n    end\n\n    it 'should log deprecation errors' do\n      func('a')\n      expect(warnings).to include(/The function 'hiera' is deprecated in favor of using 'lookup'. See https:/)\n    end\n\n    context 'with environment with configured data provider' do\n      let(:env_config) {\n        {\n          'hiera.yaml' => <<-YAML.unindent,\n             ---\n             version: 5\n             YAML\n          'data' => {\n            'common.yaml' => <<-YAML.unindent\n              ---\n              a: a (from environment)\n              e: e (from environment)\n              YAML\n          }\n        }\n      }\n\n      it 'should find data globally' do\n        expect(func('a')).to eql('first a')\n      end\n\n      it 'should find data in the environment' do\n        expect(func('e')).to eql('e (from environment)')\n      end\n\n      it 'should find data in module' do\n        expect(func('mod::c')).to eql('mod::c (from module)')\n      end\n    end\n\n    it 'should not be disabled by data_binding_terminus setting' do\n      Puppet[:data_binding_terminus] = 'none'\n      expect(func('a')).to eql('first a')\n    end\n  end\n\n  context 'hiera_array', :if => Puppet.features.hiera? do\n    let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera_array') }\n\n    it 'should require a key argument' do\n      expect { func([]) }.to raise_error(ArgumentError)\n    end\n\n    it 'should raise a useful error when nil is returned' do\n      expect { func('badkey') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'badkey'/)\n    end\n\n    it 'should log deprecation errors' do\n      func('fbb')\n      expect(warnings).to include(/The function 'hiera_array' is deprecated in favor of using 'lookup'/)\n    end\n\n    it 'should use the array resolution_type' do\n      expect(func('fbb', {'fbb' => 'foo_result'})).to eql(%w[mod::foo mod::bar mod::baz])\n    end\n\n    it 'should allow lookup with quoted dotted key' do\n      expect(func(\"'dotted.array'\")).to eql(['a', 'b'])\n    end\n\n    it 'should fail lookup with dotted key' do\n      expect{ func('nested_array.0.first') }.to raise_error(/Resolution type :array is illegal when accessing values using dotted keys. Offending key was 'nested_array.0.first'/)\n    end\n\n    it 'should use default block' do\n      expect(func('foo') { |k| ['key', k] }).to eql(%w[key foo])\n    end\n  end\n\n  context 'hiera_hash', :if => Puppet.features.hiera? do\n    let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera_hash') }\n\n    it 'should require a key argument' do\n      expect { func([]) }.to raise_error(ArgumentError)\n    end\n\n    it 'should raise a useful error when nil is returned' do\n      expect { func('badkey') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'badkey'/)\n    end\n\n    it 'should use the hash resolution_type' do\n      expect(func('b', {'b' => 'foo_result'})).to eql({ 'b1' => 'first b1', 'b2' => 'first b2', 'b3' => 'second b3'})\n    end\n\n    it 'should lookup and return a hash' do\n      expect(func('database_user')).to eql({ 'name' => 'postgres', 'uid' => 500, 'gid' => 500, 'groups' => { 'db' => 520 }})\n    end\n\n    it 'should allow lookup with quoted dotted key' do\n      expect(func(\"'dotted.key'\")).to eql({'a' => 'dotted.key a', 'b' => 'dotted.key b'})\n    end\n\n    it 'should fail lookup with dotted key' do\n      expect{ func('database_user.groups') }.to raise_error(/Resolution type :hash is illegal when accessing values using dotted keys. Offending key was 'database_user.groups'/)\n    end\n\n    it 'should log deprecation errors' do\n      func('b')\n      expect(warnings).to include(/The function 'hiera_hash' is deprecated in favor of using 'lookup'. See https:/)\n    end\n\n    it 'should use default block' do\n      expect(func('foo') { |k| {'key' => k} }).to eql({'key' => 'foo'})\n    end\n  end\n\n  context 'hiera_include', :if => Puppet.features.hiera? do\n    let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera_include') }\n\n    it 'should require a key argument' do\n      expect { func([]) }.to raise_error(ArgumentError)\n    end\n\n    it 'should raise a useful error when nil is returned' do\n      expect { func('badkey') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'badkey'/)\n    end\n\n    it 'should use the array resolution_type to include classes' do\n      expect(func('fbb').map { |c| c.class_name }).to eql(%w[mod::foo mod::bar mod::baz])\n    end\n\n    it 'should log deprecation errors' do\n      func('fbb')\n      expect(warnings).to include(/The function 'hiera_include' is deprecated in favor of using 'lookup'. See https:/)\n    end\n\n    it 'should not raise an error if the resulting hiera lookup returns an empty array' do\n      expect { func('empty_array') }.to_not raise_error\n    end\n\n    it 'should use default block array to include classes' do\n      expect(func('foo') { |k| ['mod::bar', \"mod::#{k}\"] }.map { |c| c.class_name }).to eql(%w[mod::bar mod::foo])\n    end\n  end\n\n  context 'with custom backend and merge_behavior declared in hiera.yaml', :if => Puppet.features.hiera? do\n    let(:merge_behavior) { 'deeper' }\n\n    let(:hiera_yaml) do\n      <<-YAML.unindent\n        ---\n        :backends:\n          - yaml\n          - hieraspec\n        :yaml:\n          :datadir: #{global_dir}/hieradata\n        :hierarchy:\n          - common\n          - other\n        :merge_behavior: #{merge_behavior}\n        :deep_merge_options:\n          :unpack_arrays: ','\n        YAML\n    end\n\n    let(:global_files) do\n      {\n        'hiera.yaml' => hiera_yaml,\n        'hieradata' => {\n          'common.yaml' => <<-YAML.unindent,\n            da:\n              - da 0\n              - da 1\n            dm:\n              dm1:\n                dm11: value of dm11 (from common)\n                dm12: value of dm12 (from common)\n              dm2:\n                dm21: value of dm21 (from common)\n            hash:\n              array:\n                - x1,x2\n            array:\n              - x1,x2\n            YAML\n          'other.yaml' => <<-YAML.unindent,\n            da:\n              - da 2,da 3\n            dm:\n              dm1:\n                dm11: value of dm11 (from other)\n                dm13: value of dm13 (from other)\n              dm3:\n                dm31: value of dm31 (from other)\n            hash:\n              array:\n                - x3\n                - x4\n            array:\n              - x3\n              - x4\n            YAML\n        }\n      }\n    end\n\n    context 'hiera_hash' do\n      let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera_hash') }\n\n      context \"using 'deeper'\" do\n        it 'declared merge_behavior is honored' do\n          expect(func('dm')).to eql({\n            'dm1' => {\n              'dm11' => 'value of dm11 (from common)',\n              'dm12' => 'value of dm12 (from common)',\n              'dm13' => 'value of dm13 (from other)'\n            },\n            'dm2' => {\n              'dm21' => 'value of dm21 (from common)'\n            },\n            'dm3' => {\n              'dm31' => 'value of dm31 (from other)'\n            }\n          })\n        end\n\n        it \"merge behavior is propagated to a custom backend as 'hash'\" do\n          expect(func('resolution_type')).to eql({ 'resolution_type' => 'hash' })\n        end\n\n        it 'fails on attempts to merge an array' do\n          expect {func('da')}.to raise_error(/expects a Hash value/)\n        end\n\n        it 'honors option :unpack_arrays: (unsupported by puppet)' do\n          expect(func('hash')).to eql({'array' => %w(x3 x4 x1 x2)})\n        end\n      end\n\n      context \"using 'deep'\" do\n        let(:merge_behavior) { 'deep' }\n\n        it 'honors option :unpack_arrays: (unsupported by puppet)' do\n          expect(func('hash')).to eql({'array' => %w(x1 x2 x3 x4)})\n        end\n      end\n    end\n\n    context 'hiera_array', :if => Puppet.features.hiera? do\n      let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera_array') }\n\n      it 'declared merge_behavior is ignored' do\n        expect(func('da')).to eql(['da 0', 'da 1', 'da 2,da 3'])\n      end\n\n      it \"merge behavior is propagated to a custom backend as 'array'\" do\n        expect(func('resolution_type')).to eql(['resolution_type', 'array'])\n      end\n    end\n\n    context 'hiera' do\n      let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera') }\n\n      it 'declared merge_behavior is ignored' do\n        expect(func('da')).to eql(['da 0', 'da 1'])\n      end\n\n      it \"no merge behavior is propagated to a custom backend\" do\n        expect(func('resolution_type')).to eql('resolution_type=')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/include_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/parser/functions'\nrequire 'matchers/containment_matchers'\nrequire 'matchers/resource'\nrequire 'matchers/include_in_order'\nrequire 'unit/functions/shared'\n\ndescribe 'The \"include\" function' do\n  include PuppetSpec::Compiler\n  include ContainmentMatchers\n  include Matchers::Resource\n\n  before(:each) do\n    compiler  = Puppet::Parser::Compiler.new(Puppet::Node.new(\"foo\"))\n    @scope = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it \"includes a class\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class included {\n        notify { \"included\": }\n      }\n\n      include included\n    MANIFEST\n\n    expect(catalog.classes).to include(\"included\")\n  end\n\n  it \"includes a class when using a fully qualified anchored name\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class included {\n        notify { \"included\": }\n      }\n\n      include ::included\n    MANIFEST\n\n    expect(catalog.classes).to include(\"included\")\n  end\n\n  it \"includes multiple classes\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class included {\n        notify { \"included\": }\n      }\n      class included_too {\n        notify { \"included_too\": }\n      }\n\n      include included, included_too\n    MANIFEST\n\n    expect(catalog.classes).to include(\"included\")\n    expect(catalog.classes).to include(\"included_too\")\n  end\n\n  it \"includes multiple classes given as an array\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class included {\n        notify { \"included\": }\n      }\n      class included_too {\n        notify { \"included_too\": }\n      }\n\n      include [included, included_too]\n    MANIFEST\n\n    expect(catalog.classes).to include(\"included\")\n    expect(catalog.classes).to include(\"included_too\")\n  end\n\n  it \"flattens nested arrays\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class included {\n        notify { \"included\": }\n      }\n      class included_too {\n        notify { \"included_too\": }\n      }\n\n      include [[[included], [[[included_too]]]]]\n    MANIFEST\n\n    expect(catalog.classes).to include(\"included\")\n    expect(catalog.classes).to include(\"included_too\")\n  end\n\n  it \"raises an error if class does not exist\" do\n    expect {\n      compile_to_catalog(<<-MANIFEST)\n        include the_god_in_your_religion\n      MANIFEST\n    }.to raise_error(Puppet::Error)\n  end\n\n    { \"''\"      => 'empty string', \n      'undef'   => 'undef',\n      \"['']\"    => 'empty string',\n      \"[undef]\" => 'undef'\n    }.each_pair do |value, name_kind|\n      it \"raises an error if class is #{name_kind}\" do\n        expect {\n          compile_to_catalog(<<-MANIFEST)\n            include #{value}\n          MANIFEST\n        }.to raise_error(/Cannot use #{name_kind}/)\n      end\n    end\n\n  it \"does not contained the included class in the current class\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class not_contained {\n        notify { \"not_contained\": }\n      }\n\n      class container {\n        include not_contained\n      }\n\n      include container\n    MANIFEST\n\n    expect(catalog).to_not contain_class(\"not_contained\").in(\"container\")\n  end\n\n  it 'produces an array with a single class references given a single argument' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      $x = include(a)\n      Array[Type[Class], 1, 1].assert_type($x)\n      notify { 'feedback': message => \"$x\" }\n    MANIFEST\n\n    feedback = catalog.resource(\"Notify\", \"feedback\")\n    expect(feedback[:message]).to eql(\"[Class[a]]\")\n  end\n\n  it 'produces an array with class references given multiple arguments' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      class b {\n        notify { \"b\": }\n      }\n\n      $x = include(a, b)\n      Array[Type[Class], 2, 2].assert_type($x)\n      notify { 'feedback': message => \"$x\" }\n    MANIFEST\n\n    feedback = catalog.resource(\"Notify\", \"feedback\")\n    expect(feedback[:message]).to eql(\"[Class[a], Class[b]]\")\n  end\n\n  it 'allows the result to be used in a relationship operation' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class a {\n        notify { \"a\": }\n      }\n\n      class b {\n        notify { \"b\": }\n      }\n\n      notify { 'c': }\n\n      include(a, b) -> Notify[c]\n    MANIFEST\n\n    # Assert relationships are formed\n    expect(catalog.resource(\"Class\", \"a\")[:before][0]).to eql('Notify[c]')\n    expect(catalog.resource(\"Class\", \"b\")[:before][0]).to eql('Notify[c]')\n  end\n\n  it_should_behave_like 'all functions transforming relative to absolute names', :include\n  it_should_behave_like 'an inclusion function, regardless of the type of class reference,', :include\n  it_should_behave_like 'an inclusion function, when --tasks is on,', :include\nend\n"
  },
  {
    "path": "spec/unit/functions/index_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the index function' do\n  include PuppetSpec::Compiler\n\n  context \"should be callable on Array with\" do\n    it 'a lambda to compute match' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.index |$v| { $v == 2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"1\")['ensure']).to eq('present')\n    end\n\n    it 'a lambda taking two arguments to compute match' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [6,6,6]\n        $n = $a.index |$i, $v| { $i == 2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"2\")['ensure']).to eq('present')\n    end\n\n    it 'a given value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.index(3)\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"2\")['ensure']).to eq('present')\n    end\n  end\n\n  context \"should be callable on Hash with\" do\n    it 'a lambda to compute match' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $h = {1 => 10, 2 => 20, 3 => 30 }\n        $n = $h.index |$v| { $v == 20 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"2\")['ensure']).to eq('present')\n    end\n\n    it 'a lambda taking two arguments to compute match' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $h = {1 => 10, 2 => 20, 3 => 30 }\n        $n = $h.index |$k, $v| { $k == 3 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"3\")['ensure']).to eq('present')\n    end\n\n    it 'a given value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $h = {1 => 10, 2 => 20, 3 => 30 }\n        $n = $h.index(20)\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"2\")['ensure']).to eq('present')\n    end\n  end\n\n  context \"should be callable on String with\" do\n    it 'a lambda to compute match' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $s = \"foobarfuu\"\n        $n = $s.index |$v| { $v == 'o' }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"1\")['ensure']).to eq('present')\n    end\n\n    it 'a lambda taking two arguments to compute match' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $s = \"foobarfuu\"\n        $n = $s.index |$i, $v| { $i == 2 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"2\")['ensure']).to eq('present')\n    end\n\n    it 'a given value returns index of first found substring given as a string' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $s = \"foobarfuu\"\n        $n = $s.index('fu')\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"6\")['ensure']).to eq('present')\n    end\n\n    it 'a given value returns index of first found substring given as a regexp' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $s = \"foobarfuub\"\n        $n = $s.index(/f(oo|uu)b/)\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"0\")['ensure']).to eq('present')\n    end\n  end\n\n  context \"should be callable on an iterable\" do\n    it 'for example a reverse_each' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.reverse_each.index |$v| { $v == 1 }\n        file { \"$n\": ensure => present }\n      MANIFEST\n      expect(catalog.resource(:file, \"2\")['ensure']).to eq('present')\n    end\n  end\n\n  context 'stops iteration when result is known' do\n    it 'true when boolean true is found' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.index |$i, $v| { if $i == 0 { true } else { fail(\"unwanted\") } }\n        file { \"$n\": ensure => present }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"0\")['ensure']).to eq('present')\n    end\n  end\n\n  context 'returns undef when value is not found' do\n    it 'when using array and a lambda' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.index |$v| { $v == 'blue' }\n        file { \"test\": ensure => if $n =~ Undef { present } else {absent} }\n      MANIFEST\n      expect(catalog.resource(:file, \"test\")['ensure']).to eq('present')\n    end\n\n    it 'when using array and a value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $n = $a.index('blue')\n        file { \"test\": ensure => if $n =~ Undef { present } else {absent} }\n      MANIFEST\n      expect(catalog.resource(:file, \"test\")['ensure']).to eq('present')\n    end\n\n    it 'when using a hash and a lambda' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $h = {1 => 10, 2 => 20, 3 => 30}\n        $n = $h.index |$v| { $v == 'blue' }\n        file { \"test\": ensure => if $n =~ Undef { present } else {absent} }\n      MANIFEST\n      expect(catalog.resource(:file, \"test\")['ensure']).to eq('present')\n    end\n\n    it 'when using a hash and a value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $h = {1 => 10, 2 => 20, 3 => 30}\n        $n = $h.index('blue')\n        file { \"test\": ensure => if $n =~ Undef { present } else {absent} }\n      MANIFEST\n      expect(catalog.resource(:file, \"test\")['ensure']).to eq('present')\n    end\n\n    it 'when using a String and a lambda' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $s = \"foobarfuub\"\n        $n = $s.index() |$v| { false }\n        file { \"test\": ensure => if $n =~ Undef { present } else {absent} }\n      MANIFEST\n      expect(catalog.resource(:file, \"test\")['ensure']).to eq('present')\n    end\n\n    it 'when using a String and a value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $s = \"foobarfuub\"\n        $n = $s.index('banana')\n        file { \"test\": ensure => if $n =~ Undef { present } else {absent} }\n      MANIFEST\n      expect(catalog.resource(:file, \"test\")['ensure']).to eq('present')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/inline_epp_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\n\ndescribe \"the inline_epp function\" do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  let :node     do Puppet::Node.new('localhost') end\n  let :compiler do Puppet::Parser::Compiler.new(node) end\n  let :scope    do Puppet::Parser::Scope.new(compiler) end\n\n  context \"when accessing scope variables as $ variables\" do\n    it \"looks up the value from the scope\" do\n      scope[\"what\"] = \"are belong\"\n      expect(eval_template(\"all your base <%= $what %> to us\")).to eq(\"all your base are belong to us\")\n    end\n\n    it \"gets error accessing a variable that does not exist\" do\n      expect { eval_template(\"<%= $kryptonite == undef %>\")}.to raise_error(/Evaluation Error: Unknown variable: 'kryptonite'./)\n    end\n\n    it \"get nil accessing a variable that does not exist when strict mode is off\" do\n      Puppet[:strict_variables] = false\n      Puppet[:strict] = :warning\n      expect(eval_template(\"<%= $kryptonite == undef %>\")).to eq(\"true\")\n    end\n\n    it \"get nil accessing a variable that is undef\" do\n      scope['undef_var'] = :undef\n      expect(eval_template(\"<%= $undef_var == undef %>\")).to eq(\"true\")\n    end\n\n    it \"gets shadowed variable if args are given\" do\n      scope['phantom'] = 'of the opera'\n      expect(eval_template_with_args(\"<%= $phantom == dragos %>\", 'phantom' => 'dragos')).to eq(\"true\")\n    end\n\n    it \"gets shadowed variable if args are given and parameters are specified\" do\n      scope['x'] = 'wrong one'\n      expect(eval_template_with_args(\"<%-| $x |-%><%= $x == correct %>\", 'x' => 'correct')).to eq(\"true\")\n    end\n\n    it \"raises an error if required variable is not given\" do\n      scope['x'] = 'wrong one'\n      expect {\n        eval_template_with_args(\"<%-| $x |-%><%= $x == correct %>\", {})\n      }.to raise_error(/expects a value for parameter 'x'/)\n    end\n\n    it 'raises an error if unexpected arguments are given' do\n      scope['x'] = 'wrong one'\n      expect {\n        eval_template_with_args(\"<%-| $x |-%><%= $x == correct %>\", 'x' => 'correct', 'y' => 'surplus')\n      }.to raise_error(/has no parameter named 'y'/)\n    end\n  end\n\n  context \"when given an empty template\" do\n     it \"allows the template file to be empty\" do\n       expect(eval_template(\"\")).to eq(\"\")\n     end\n\n    it \"allows the template to have empty body after parameters\" do\n      expect(eval_template_with_args(\"<%-|$x|%>\", 'x'=>1)).to eq(\"\")\n    end\n  end\n\n  it \"renders a block expression\" do\n    expect(eval_template_with_args(\"<%= { $y = $x $x + 1} %>\", 'x' => 2)).to eq(\"3\")\n  end\n\n  # although never a problem with epp\n  it \"is not interfered with by having a variable named 'string' (#14093)\" do\n    scope['string'] = \"this output should not be seen\"\n    expect(eval_template(\"some text that is static\")).to eq(\"some text that is static\")\n  end\n\n  it \"has access to a variable named 'string' (#14093)\" do\n    scope['string'] = \"the string value\"\n    expect(eval_template(\"string was: <%= $string %>\")).to eq(\"string was: the string value\")\n  end\n\n  context \"when using Sensitive\" do\n    it \"returns an unwrapped sensitive value as a String\" do\n      expect(eval_and_collect_notices(<<~END)).to eq([\"opensesame\"])\n        notice(inline_epp(\"<%= Sensitive('opensesame').unwrap %>\"))\n      END\n    end\n\n    it \"rewraps a sensitive value\" do\n      # note entire result is redacted, not just sensitive part\n      expect(eval_and_collect_notices(<<~END)).to eq([\"Sensitive [value redacted]\"])\n        notice(inline_epp(\"This is sensitive <%= Sensitive('opensesame') %>\"))\n      END\n    end\n\n    it \"can be double wrapped\" do\n      catalog = compile_to_catalog(<<~END)\n        notify { 'title':\n          message => Sensitive(inline_epp(\"<%= Sensitive('opensesame') %>\"))\n        }\n      END\n      expect(catalog.resource(:notify, 'title')['message']).to eq('opensesame')\n    end\n  end\n\n  def eval_template_with_args(content, args_hash)\n    epp_function.call(scope, content, args_hash)\n  end\n\n  def eval_template(content)\n    epp_function.call(scope, content)\n  end\n\n  def epp_function()\n    scope.compiler.loaders.public_environment_loader.load(:function, 'inline_epp')\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/join_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the join function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'joins an array with empty string delimiter if delimiter is not given' do\n    expect(compile_to_catalog(\"notify { join([1,2,3]): }\")).to have_resource('Notify[123]')\n  end\n\n  it 'joins an array with given string delimiter' do\n    expect(compile_to_catalog(\"notify { join([1,2,3],'x'): }\")).to have_resource('Notify[1x2x3]')\n  end\n\n  it 'results in empty string if array is empty' do\n    expect(compile_to_catalog('notify { \"x${join([])}y\": }')).to have_resource('Notify[xy]')\n  end\n\n  it 'flattens nested arrays' do\n    expect(compile_to_catalog(\"notify { join([1,2,[3,4]]): }\")).to have_resource('Notify[1234]')\n  end\n\n  it 'does not flatten arrays nested in hashes' do\n    expect(compile_to_catalog(\"notify { join([1,2,{a => [3,4]}]): }\")).to have_resource('Notify[12{\"a\"=>[3, 4]}]')\n  end\n\n  it 'formats nil/undef as empty string' do\n    expect(compile_to_catalog('notify { join([undef, undef], \"x\"): }')).to have_resource('Notify[x]')\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/keys_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the keys function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'returns the keys in the hash in the order they appear in a hash iteration' do\n    expect(compile_to_catalog(<<-'SRC'.unindent)).to have_resource('Notify[apples & oranges]')\n        $k = {'apples' => 1, 'oranges' => 2}.keys\n        notify { \"${k[0]} & ${k[1]}\": }\n      SRC\n  end\n\n  it 'returns an empty array for an empty hash' do\n    expect(compile_to_catalog(<<-'SRC'.unindent)).to have_resource('Notify[0]')\n        $v = {}.keys.reduce(0) |$m, $v| { $m+1 }\n        notify { \"${v}\": }\n      SRC\n  end\n\n  it 'includes an undef key if one is present in the hash' do\n    expect(compile_to_catalog(<<-'SRC'.unindent)).to have_resource('Notify[Undef]')\n        $types = {undef => 1}.keys.map |$v| { $v.type }\n        notify { \"${types[0]}\": }\n      SRC\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/length_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the length function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an array it' do\n    it 'returns 0 when empty' do\n      expect(compile_to_catalog(\"notify { String(length([])): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of elements when not empty' do\n      expect(compile_to_catalog(\"notify { String(length([1, 2, 3])): }\")).to have_resource('Notify[3]')\n    end\n  end\n\n  context 'for a hash it' do\n    it 'returns 0 empty' do\n      expect(compile_to_catalog(\"notify { String(length({})): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of elements when not empty' do\n      expect(compile_to_catalog(\"notify { String(length({1=>1,2=>2})): }\")).to have_resource('Notify[2]')\n    end\n  end\n\n  context 'for a string it' do\n    it 'returns 0 when empty' do\n      expect(compile_to_catalog(\"notify { String(length('')): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of characters when not empty' do\n      # note the multibyte characters - åäö each taking two bytes in UTF-8\n      expect(compile_to_catalog('notify { String(length(\"\\u00e5\\u00e4\\u00f6\")): }')).to have_resource('Notify[3]')\n    end\n  end\n\n  context 'for a binary it' do\n    it 'returns 0 when empty' do\n      expect(compile_to_catalog(\"notify { String(length(Binary(''))): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of bytes when not empty' do\n      expect(compile_to_catalog(\"notify { String(length(Binary('b25l'))): }\")).to have_resource('Notify[3]')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/lest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the lest function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'calls a lambda passing no argument' do\n    expect(compile_to_catalog(\"lest(undef) || { notify { testing: } }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'produces what lambda returns if value is undef' do\n    expect(compile_to_catalog(\"notify{ lest(undef) || { testing }: }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'does not call lambda if argument is not undef' do\n    expect(compile_to_catalog('lest(1) || { notify { \"failed\": } }')).to_not have_resource('Notify[failed]')\n  end\n\n  it 'produces given argument if given not undef' do\n    expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test_yay_ing]')\n    notify{ \"test${lest('_yay_') || { '_oh_no_' }}ing\": }\n    SOURCE\n  end\n\n  it 'errors when lambda wants too many args' do\n    expect do\n      compile_to_catalog('lest(1) |$x| { }')\n    end.to raise_error(/'lest' block expects no arguments, got 1/m)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/logging_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe 'the log function' do\n  include PuppetSpec::Compiler\n\n  def collect_logs(code)\n    Puppet[:code] = code\n    Puppet[:environment_timeout] = 10\n    node = Puppet::Node.new('logtest')\n    compiler = Puppet::Parser::Compiler.new(node)\n    node.environment.check_for_reparse\n    logs = []\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      compiler.compile\n    end\n    logs\n  end\n\n  def expect_log(code, log_level, message)\n    logs = collect_logs(code)\n\n    # There's a chance that additional messages could have been\n    # added during code compilation like debug messages from\n    # Facter. This happened on Appveyor when the Appveyor image\n    # failed to resolve the domain fact. Since these messages are\n    # not relevant to our test, and since they can (possibly) be\n    # injected at any location in our logs array, it is good enough\n    # to assert that our desired log _is_ included in the logs array\n    # instead of trying to figure out _where_ it is included.\n    expect(logs).to include(satisfy { |log| log.level == log_level && log.message == message })\n  end\n\n  before(:each) do\n    Puppet[:log_level] = 'debug'\n  end\n\n  Puppet::Util::Log.levels.each do |level|\n    context \"for log level '#{level}'\" do\n      it 'can be called' do\n        expect_log(\"#{level.to_s}('yay')\", level, 'yay')\n      end\n\n      it 'joins multiple arguments using space' do\n        # Not using the evaluator would result in yay {\"a\"=>\"b\", \"c\"=>\"d\"}\n        expect_log(\"#{level.to_s}('a', 'b', 3)\", level, 'a b 3')\n      end\n\n      it 'uses the evaluator to format output' do\n        # Not using the evaluator would result in yay {\"a\"=>\"b\", \"c\"=>\"d\"}\n        expect_log(\"#{level.to_s}('yay', {a => b, c => d})\", level, 'yay {a => b, c => d}')\n      end\n\n      it 'returns undef value' do\n        logs = collect_logs(\"notice(type(#{level.to_s}('yay')))\")\n        expect(logs.size).to eql(2)\n        expect(logs[1].level).to eql(:notice)\n        expect(logs[1].message).to eql('Undef')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/lookup_fixture_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'deep_merge/core'\n\n# Tests the lookup function using fixtures\ndescribe 'The lookup function' do\n  include PuppetSpec::Compiler\n\n  # Assembles code that includes the *abc* class and compiles it into a catalog. This class will use the global\n  # variable $args to perform a lookup and assign the result to $abc::result. Unless the $block is set to\n  # the string 'no_block_present', it will be passed as a lambda to the lookup. The assembled code will declare\n  # a notify resource with a name that is formed by interpolating the result into a format string.\n  #\n  # The method performs the folloging steps.\n  #\n  # - Build the code that:\n  #    - sets the $args variable from _lookup_args_\n  #    - sets the $block parameter to the given block or the string 'no_block_present'\n  #    - includes the abc class\n  #    - assigns the $abc::result to $r\n  #    - interpolates a string using _fmt_ (which is assumed to use $r)\n  #    - declares a notify resource from the interpolated string\n  # - Compile the code into a catalog\n  # - Return the name of all Notify resources in that catalog\n  #\n  # @param fmt [String] The puppet interpolated string used when creating the notify title\n  # @param *args [String] splat of args that will be concatenated to form the puppet args sent to lookup\n  # @return [Array<String>] List of names of Notify resources in the resulting catalog\n  #\n  def assemble_and_compile(fmt, *lookup_args, &block)\n    assemble_and_compile_with_block(fmt, \"'no_block_present'\", *lookup_args, &block)\n  end\n\n  def assemble_and_compile_with_block(fmt, block, *lookup_args, &cblock)\n    compile_and_get_notifications(<<-END.unindent, &cblock)\n      $args = [#{lookup_args.join(',')}]\n      $block = #{block}\n      include abc\n      $r = if $abc::result == undef { 'no_value' } else { $abc::result }\n      notify { \\\"#{fmt}\\\": }\n    END\n  end\n\n  def compile_and_get_notifications(code)\n    Puppet[:code] = code\n    node.environment.check_for_reparse\n    catalog = block_given? ? compiler.compile { |cat| yield(compiler.topscope); cat } : compiler.compile\n    catalog.resources.map(&:ref).filter_map { |r| r[7..-2] if r.start_with?('Notify[') }\n  end\n\n  # There is a fully configured 'production' environment in fixtures at this location\n  let(:environmentpath) { File.join(my_fixture_dir, 'environments') }\n  let(:node) { Puppet::Node.new(\"testnode\", :facts => Puppet::Node::Facts.new(\"facts\", {}), :environment => 'production') }\n  let(:compiler) { Puppet::Parser::Compiler.new(node) }\n\n  around(:each) do |example|\n    # Initialize settings to get a full compile as close as possible to a real\n    # environment load\n    Puppet.settings.initialize_global_settings\n\n    # Initialize loaders based on the environmentpath. It does not work to\n    # just set the setting environmentpath for some reason - this achieves the same:\n    # - first a loader is created, loading directory environments from the fixture (there is\n    # one environment, 'production', which will be loaded since the node references this\n    # environment by name).\n    # - secondly, the created env loader is set as 'environments' in the puppet context.\n    #\n    environments = Puppet::Environments::Directories.new(environmentpath, [])\n    Puppet.override(:environments => environments) do\n      example.run\n    end\n  end\n\n  context 'using valid parameters' do\n    it 'can lookup value provided by the environment' do\n      resources = assemble_and_compile('${r}', \"'abc::a'\")\n      expect(resources).to include('env_a')\n    end\n\n    it 'can lookup value provided by the module' do\n      resources = assemble_and_compile('${r}', \"'abc::b'\")\n      expect(resources).to include('module_b')\n    end\n\n    it \"can lookup value provided by the module that has 'function' data_provider entry in metadata.json\" do\n      resources = compile_and_get_notifications(\"$args = ['meta::b']\\ninclude meta\\nnotify { $meta::result: }\\n\")\n      expect(resources).to include('module_b')\n    end\n\n    it 'can lookup value provided in global scope' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r}', \"'abc::a'\")\n      expect(resources).to include('global_a')\n    end\n\n    it 'will stop at first found name when several names are provided' do\n      resources = assemble_and_compile('${r}', \"['abc::b', 'abc::a']\")\n      expect(resources).to include('module_b')\n    end\n\n    it 'can lookup value provided by the module that is overriden by environment' do\n      resources = assemble_and_compile('${r}', \"'abc::c'\")\n      expect(resources).to include('env_c')\n    end\n\n    it \"can 'unique' merge values provided by both the module and the environment\" do\n      resources = assemble_and_compile('${r[0]}_${r[1]}', \"'abc::c'\", 'Array[String]', \"'unique'\")\n      expect(resources).to include('env_c_module_c')\n    end\n\n    it \"can 'hash' merge values provided by the environment only\" do\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::d'\", 'Hash[String,String]', \"'hash'\")\n      expect(resources).to include('env_d1_env_d2_env_d3')\n    end\n\n    it \"can 'hash' merge values provided by both the environment and the module\" do\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"'hash'\")\n      expect(resources).to include('env_e1_module_e2_env_e3')\n    end\n\n    it \"can 'hash' merge values provided by global, environment, and module\" do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"'hash'\")\n      expect(resources).to include('global_e1_module_e2_env_e3')\n    end\n\n    it \"can pass merge parameter in the form of a hash with a 'strategy=>unique'\" do\n      resources = assemble_and_compile('${r[0]}_${r[1]}', \"'abc::c'\", 'Array[String]', \"{strategy => 'unique'}\")\n      expect(resources).to include('env_c_module_c')\n    end\n\n    it \"can pass merge parameter in the form of a hash with 'strategy=>hash'\" do\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"{strategy => 'hash'}\")\n      expect(resources).to include('env_e1_module_e2_env_e3')\n    end\n\n    it \"can pass merge parameter in the form of a hash with a 'strategy=>deep'\" do\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"{strategy => 'deep'}\")\n      expect(resources).to include('env_e1_module_e2_env_e3')\n    end\n\n    it \"will fail unless merge in the form of a hash contains a 'strategy'\" do\n      expect do\n        assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"{merge_key => 'hash'}\")\n      end.to raise_error(Puppet::ParseError, /hash given as 'merge' must contain the name of a strategy/)\n    end\n\n    it 'will raise an exception when value is not found for single key and no default is provided' do\n      expect do\n        assemble_and_compile('${r}', \"'abc::x'\")\n      end.to raise_error(Puppet::ParseError, /did not find a value for the name 'abc::x'/)\n    end\n\n    it 'can lookup an undef value' do\n      resources = assemble_and_compile('${r}', \"'abc::n'\")\n      expect(resources).to include('no_value')\n    end\n\n    it 'will not replace an undef value with a given default' do\n      resources = assemble_and_compile('${r}', \"'abc::n'\", 'undef', 'undef', '\"default_n\"')\n      expect(resources).to include('no_value')\n    end\n\n    it 'will not accept a succesful lookup of an undef value when the type rejects it' do\n      expect do\n        assemble_and_compile('${r}', \"'abc::n'\", 'String')\n      end.to raise_error(Puppet::ParseError, /Found value has wrong type, expects a String value, got Undef/)\n    end\n\n    it 'will raise an exception when value is not found for array key and no default is provided' do\n      expect do\n        assemble_and_compile('${r}', \"['abc::x', 'abc::y']\")\n      end.to raise_error(Puppet::ParseError, /did not find a value for any of the names \\['abc::x', 'abc::y'\\]/)\n    end\n\n    it 'can lookup and deep merge shallow values provided by the environment only' do\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::d'\", 'Hash[String,String]', \"'deep'\")\n      expect(resources).to include('env_d1_env_d2_env_d3')\n    end\n\n    it 'can lookup and deep merge shallow values provided by both the module and the environment' do\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"'deep'\")\n      expect(resources).to include('env_e1_module_e2_env_e3')\n    end\n\n    it 'can lookup and deep merge deep values provided by global, environment, and module' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r[k1][s1]}_${r[k1][s2]}_${r[k1][s3]}_${r[k2][s1]}_${r[k2][s2]}_${r[k2][s3]}', \"'abc::f'\", 'Hash[String,Hash[String,String]]', \"'deep'\")\n      expect(resources).to include('global_f11_env_f12_module_f13_env_f21_module_f22_global_f23')\n    end\n\n    it 'will propagate resolution_type :array to Hiera when merge == \\'unique\\'' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r[0]}_${r[1]}_${r[2]}', \"'abc::c'\", 'Array[String]', \"'unique'\")\n      expect(resources).to include('global_c_env_c_module_c')\n    end\n\n    it 'will propagate a Hash resolution_type with :behavior => :native to Hiera when merge == \\'hash\\'' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', \"'abc::e'\", 'Hash[String,String]', \"{strategy => 'hash'}\")\n      expect(resources).to include('global_e1_module_e2_env_e3')\n    end\n\n    it 'will propagate a Hash resolution_type with :behavior => :deeper to Hiera when merge == \\'deep\\'' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r[k1][s1]}_${r[k1][s2]}_${r[k1][s3]}_${r[k2][s1]}_${r[k2][s2]}_${r[k2][s3]}', \"'abc::f'\", 'Hash[String,Hash[String,String]]', \"'deep'\")\n      expect(resources).to include('global_f11_env_f12_module_f13_env_f21_module_f22_global_f23')\n    end\n\n    it 'will propagate a Hash resolution_type with symbolic deep merge options to Hiera' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = assemble_and_compile('${r[k1][s1]}_${r[k1][s2]}_${r[k1][s3]}_${r[k2][s1]}_${r[k2][s2]}_${r[k2][s3]}', \"'abc::f'\", 'Hash[String,Hash[String,String]]', \"{ 'strategy' => 'deep', 'knockout_prefix' => '--' }\")\n      expect(resources).to include('global_f11_env_f12_module_f13_env_f21_module_f22_global_f23')\n    end\n\n    context 'with provided default' do\n      it 'will return default when lookup fails' do\n        resources = assemble_and_compile('${r}', \"'abc::x'\", 'String', 'undef', \"'dflt_x'\")\n        expect(resources).to include('dflt_x')\n      end\n\n      it 'can precede default parameter with undef as the value_type and undef as the merge type' do\n        resources = assemble_and_compile('${r}', \"'abc::x'\", 'undef', 'undef', \"'dflt_x'\")\n        expect(resources).to include('dflt_x')\n      end\n\n      it 'can use array' do\n        resources = assemble_and_compile('${r[0]}_${r[1]}', \"'abc::x'\", 'Array[String]', 'undef', \"['dflt_x', 'dflt_y']\")\n        expect(resources).to include('dflt_x_dflt_y')\n      end\n\n      it 'can use hash' do\n        resources = assemble_and_compile('${r[a]}_${r[b]}', \"'abc::x'\", 'Hash[String,String]', 'undef', \"{'a' => 'dflt_x', 'b' => 'dflt_y'}\")\n        expect(resources).to include('dflt_x_dflt_y')\n      end\n\n      it 'fails unless default is an instance of value_type' do\n        expect do\n          assemble_and_compile('${r[a]}_${r[b]}', \"'abc::x'\", 'Hash[String,String]', 'undef', \"{'a' => 'dflt_x', 'b' => 32}\")\n        end.to raise_error(Puppet::ParseError,\n          /Default value has wrong type, entry 'b' expects a String value, got Integer/)\n      end\n    end\n\n    context 'with a default block' do\n      it 'will be called when lookup fails' do\n        resources = assemble_and_compile_with_block('${r}', \"'dflt_x'\", \"'abc::x'\")\n        expect(resources).to include('dflt_x')\n      end\n\n      it 'will not called when lookup succeeds but the found value is nil' do\n        resources = assemble_and_compile_with_block('${r}', \"'dflt_x'\", \"'abc::n'\")\n        expect(resources).to include('no_value')\n      end\n\n      it 'can use array' do\n        resources = assemble_and_compile_with_block('${r[0]}_${r[1]}', \"['dflt_x', 'dflt_y']\", \"'abc::x'\")\n        expect(resources).to include('dflt_x_dflt_y')\n      end\n\n      it 'can use hash' do\n        resources = assemble_and_compile_with_block('${r[a]}_${r[b]}', \"{'a' => 'dflt_x', 'b' => 'dflt_y'}\", \"'abc::x'\")\n        expect(resources).to include('dflt_x_dflt_y')\n      end\n\n      it 'can return undef from block' do\n        resources = assemble_and_compile_with_block('${r}', 'undef', \"'abc::x'\")\n        expect(resources).to include('no_value')\n      end\n\n      it 'fails unless block returns an instance of value_type' do\n        expect do\n          assemble_and_compile_with_block('${r[a]}_${r[b]}', \"{'a' => 'dflt_x', 'b' => 32}\", \"'abc::x'\", 'Hash[String,String]')\n        end.to raise_error(Puppet::ParseError,\n          /Value returned from default block has wrong type, entry 'b' expects a String value, got Integer/)\n      end\n\n      it 'receives a single name parameter' do\n        resources = assemble_and_compile_with_block('${r}', 'true', \"'name_x'\")\n        expect(resources).to include('name_x')\n      end\n\n      it 'receives an array name parameter' do\n        resources = assemble_and_compile_with_block('${r[0]}_${r[1]}', 'true', \"['name_x', 'name_y']\")\n        expect(resources).to include('name_x_name_y')\n      end\n    end\n  end\n\n  context 'and using dotted keys' do\n    it 'can access values in data using dot notation' do\n      source = <<-PUPPET\n        function environment::data() {\n          { a => { b => { c => 'the data' }}}\n        }\n        notice(lookup('a.b.c'))\n      PUPPET\n      expect(eval_and_collect_notices(source)).to include('the data')\n    end\n\n    it 'can find data using quoted dot notation' do\n      source = <<-PUPPET\n        function environment::data() {\n          { 'a.b.c' => 'the data' }\n        }\n        notice(lookup('\"a.b.c\"'))\n      PUPPET\n      expect(eval_and_collect_notices(source)).to include('the data')\n    end\n\n    it 'can access values in data using a mix of dot notation and quoted dot notation' do\n      source = <<-PUPPET\n        function environment::data() {\n          { 'a' => { 'b.c' => 'the data' }}\n        }\n        notice(lookup('a.\"b.c\"'))\n      PUPPET\n      expect(eval_and_collect_notices(source)).to include('the data')\n    end\n  end\n\n  context 'passing a hash as the only parameter' do\n    it 'can pass a single name correctly' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::a'}\")\n      expect(resources).to include('env_a')\n    end\n\n    it 'can pass a an array of names correctly' do\n      resources = assemble_and_compile('${r}', \"{name => ['abc::b', 'abc::a']}\")\n      expect(resources).to include('module_b')\n    end\n\n    it 'can pass an override map and find values there even though they would be found' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::a', override => { abc::a => 'override_a'}}\")\n      expect(resources).to include('override_a')\n    end\n\n    it 'can pass an default_values_hash and find values there correctly' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::x', default_values_hash => { abc::x => 'extra_x'}}\")\n      expect(resources).to include('extra_x')\n    end\n\n    it 'can pass an default_values_hash but not use it when value is found elsewhere' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::a', default_values_hash => { abc::a => 'extra_a'}}\")\n      expect(resources).to include('env_a')\n    end\n\n    it 'can pass an default_values_hash but not use it when value is found elsewhere even when found value is undef' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::n', default_values_hash => { abc::n => 'extra_n'}}\")\n      expect(resources).to include('no_value')\n    end\n\n    it 'can pass an override and an default_values_hash and find the override value' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::x', override => { abc::x => 'override_x'}, default_values_hash => { abc::x => 'extra_x'}}\")\n      expect(resources).to include('override_x')\n    end\n\n    it 'will raise an exception when value is not found for single key and no default is provided' do\n      expect do\n        assemble_and_compile('${r}', \"{name => 'abc::x'}\")\n      end.to raise_error(Puppet::ParseError, /did not find a value for the name 'abc::x'/)\n    end\n\n    it 'will not raise an exception when value is not found default value is nil' do\n      resources = assemble_and_compile('${r}', \"{name => 'abc::x', default_value => undef}\")\n      expect(resources).to include('no_value')\n    end\n  end\n\n  context 'accessing from outside a module' do\n    it 'will both log a warning and raise an exception when key in the function provided module data is not prefixed' do\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        Puppet[:code] = \"include bad_data\\nlookup('bad_data::b')\"\n        expect { compiler.compile }.to raise_error(Puppet::ParseError, /did not find a value for the name 'bad_data::b'/)\n      end\n      warnings = logs.filter_map { |log| log.message if log.level == :warning }\n      expect(warnings).to include(\"Module 'bad_data': Value returned from deprecated API function 'bad_data::data' must use keys qualified with the name of the module; got b\")\n    end\n\n    it 'will succeed finding prefixed keys even when a key in the function provided module data is not prefixed' do\n      logs = []\n      resources = nil\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        resources = compile_and_get_notifications(<<-PUPPET.unindent)\n          include bad_data\n          notify { lookup('bad_data::c'): }\n        PUPPET\n        expect(resources).to include('module_c')\n      end\n      warnings = logs.filter_map { |log| log.message if log.level == :warning }\n      expect(warnings).to include(\"Module 'bad_data': Value returned from deprecated API function 'bad_data::data' must use keys qualified with the name of the module; got b\")\n    end\n\n    it 'will resolve global, environment, and module correctly' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = compile_and_get_notifications(<<-PUPPET.unindent)\n        include bca\n        $r = lookup(bca::e, Hash[String,String], hash)\n        notify { \"${r[k1]}_${r[k2]}_${r[k3]}\": }\n      PUPPET\n      expect(resources).to include('global_e1_module_bca_e2_env_bca_e3')\n    end\n\n    it 'will resolve global and environment correctly when module has no provider' do\n      Puppet.settings[:hiera_config] = File.join(my_fixture_dir, 'hiera.yaml')\n      resources = compile_and_get_notifications(<<-PUPPET.unindent)\n        include no_provider\n        $r = lookup(no_provider::e, Hash[String,String], hash)\n        notify { \"${r[k1]}_${r[k2]}_${r[k3]}\": }\n      PUPPET\n      expect(resources).to include('global_e1__env_no_provider_e3') # k2 is missing\n    end\n  end\n\n  context 'accessing bad data' do\n    it 'a warning will be logged when key in the function provided module data is not prefixed' do\n      Puppet[:code] = \"include bad_data\\nlookup('bad_data::c')\"\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        compiler.compile\n      end\n      warnings = logs.filter_map { |log| log.message if log.level == :warning }\n      expect(warnings).to include(\"Module 'bad_data': Value returned from deprecated API function 'bad_data::data' must use keys qualified with the name of the module; got b\")\n    end\n\n    it 'a warning will be logged when key in the hiera provided module data is not prefixed' do\n      Puppet[:code] = \"include hieraprovider\\nlookup('hieraprovider::test::param_a')\"\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        compiler.compile\n      end\n      warnings = logs.filter_map { |log| log.message if log.level == :warning }\n      expect(warnings).to include(\"Module 'hieraprovider': Value returned from data_hash function 'json_data', when using location '#{environmentpath}/production/modules/hieraprovider/data/first.json', must use keys qualified with the name of the module; got test::param_b\")\n    end\n  end\n\n  context 'accessing empty files' do\n    # An empty YAML file is OK and should be treated as a file that contains no keys\n    it \"will fail normally with a 'did not find a value' error when a yaml file is empty\" do\n      Puppet[:code] = \"include empty_yaml\\nlookup('empty_yaml::a')\"\n      expect { compiler.compile }.to raise_error(Puppet::ParseError, /did not find a value for the name 'empty_yaml::a'/)\n    end\n\n    # An empty JSON file is not OK. Should yield a parse error\n    it \"will fail with a LookupError indicating a parser failure when a json file is empty\" do\n      Puppet[:code] = \"include empty_json\\nlookup('empty_json::a')\"\n      expect { compiler.compile }.to raise_error(Puppet::DataBinding::LookupError, /Unable to parse/)\n    end\n  end\n\n  context 'accessing nil values' do\n    it 'will find a key with undef value in a yaml file' do\n      Puppet[:code] = 'include empty_key_yaml'\n      compiler.compile do |catalog|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('empty_key_yaml::has_undef_value', nil, nil, false, nil, lookup_invocation)\n        rescue Puppet::Error\n        end\n        expect(lookup_invocation.explainer.explain).to include(<<-EOS.unindent('      '))\n          Path \"#{environmentpath}/production/modules/empty_key_yaml/data/empty_key.yaml\"\n            Original path: \"empty_key\"\n            Found key: \"empty_key_yaml::has_undef_value\" value: nil\n        EOS\n      end\n    end\n\n    it 'will find a key with undef value in a json file' do\n      Puppet[:code] = 'include empty_key_json'\n      compiler.compile do |catalog|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('empty_key_json::has_undef_value', nil, nil, false, nil, lookup_invocation)\n        rescue Puppet::Error\n        end\n        expect(lookup_invocation.explainer.explain).to include(<<-EOS.unindent('      '))\n          Path \"#{environmentpath}/production/modules/empty_key_json/data/empty_key.json\"\n            Original path: \"empty_key\"\n            Found key: \"empty_key_json::has_undef_value\" value: nil\n        EOS\n      end\n    end\n  end\n\n  context 'using explain' do\n    it 'will explain that module is not found' do\n      Puppet[:code] = 'undef'\n      compiler.compile do |catalog|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('ppx::e', nil, nil, false, nil, lookup_invocation)\n        rescue Puppet::Error\n        end\n        expect(lookup_invocation.explainer.explain).to include('Module \"ppx\" not found')\n      end\n    end\n\n    it 'will explain that module does not find a key' do\n      Puppet[:code] = 'undef'\n      compiler.compile do |catalog|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('abc::x', nil, nil, false, nil, lookup_invocation)\n        rescue Puppet::Error\n        end\n        expect(lookup_invocation.explainer.explain).to include(<<-EOS.unindent('  '))\n          Module \"abc\" Data Provider (hiera configuration version 5)\n            Deprecated API function \"abc::data\"\n              No such key: \"abc::x\"\n        EOS\n      end\n    end\n\n    it 'will explain deep merge results without options' do\n      assemble_and_compile('${r}', \"'abc::a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        Puppet::Pops::Lookup.lookup('abc::e', Puppet::Pops::Types::TypeParser.singleton.parse('Hash[String,String]'), nil, false, 'deep', lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to eq(<<-EOS.unindent)\n          Searching for \"abc::e\"\n            Merge strategy deep\n              Global Data Provider (hiera configuration version 5)\n                No such key: \"abc::e\"\n              Environment Data Provider (hiera configuration version 5)\n                Deprecated API function \"environment::data\"\n                  Found key: \"abc::e\" value: {\n                    \"k1\" => \"env_e1\",\n                    \"k3\" => \"env_e3\"\n                  }\n              Module \"abc\" Data Provider (hiera configuration version 5)\n                Deprecated API function \"abc::data\"\n                  Found key: \"abc::e\" value: {\n                    \"k1\" => \"module_e1\",\n                    \"k2\" => \"module_e2\"\n                  }\n              Merged result: {\n                \"k1\" => \"env_e1\",\n                \"k2\" => \"module_e2\",\n                \"k3\" => \"env_e3\"\n              }\n        EOS\n      end\n    end\n\n    it 'will explain deep merge results with options' do\n      assemble_and_compile('${r}', \"'abc::a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        Puppet::Pops::Lookup.lookup('abc::e', Puppet::Pops::Types::TypeParser.singleton.parse('Hash[String,String]'), nil, false, { 'strategy' => 'deep', 'merge_hash_arrays' => true }, lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to include(<<-EOS.unindent('  '))\n          Merge strategy deep\n            Options: {\n              \"merge_hash_arrays\" => true\n            }\n        EOS\n      end\n    end\n\n    it 'will handle merge when no entries are not found' do\n      assemble_and_compile('${r}', \"'hieraprovider::test::param_a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('hieraprovider::test::not_found', nil, nil, false, 'deep', lookup_invocation)\n        rescue Puppet::DataBinding::LookupError\n        end\n        expect(lookup_invocation.explainer.explain).to eq(<<-EOS.unindent)\n          Searching for \"hieraprovider::test::not_found\"\n            Merge strategy deep\n              Global Data Provider (hiera configuration version 5)\n                No such key: \"hieraprovider::test::not_found\"\n              Environment Data Provider (hiera configuration version 5)\n                Deprecated API function \"environment::data\"\n                  No such key: \"hieraprovider::test::not_found\"\n              Module \"hieraprovider\" Data Provider (hiera configuration version 4)\n                Using configuration \"#{environmentpath}/production/modules/hieraprovider/hiera.yaml\"\n                Hierarchy entry \"two paths\"\n                  Merge strategy deep\n                    Path \"#{environmentpath}/production/modules/hieraprovider/data/first.json\"\n                      Original path: \"first\"\n                      No such key: \"hieraprovider::test::not_found\"\n                    Path \"#{environmentpath}/production/modules/hieraprovider/data/second_not_present.json\"\n                      Original path: \"second_not_present\"\n                      Path not found\n        EOS\n      end\n    end\n\n    it 'will explain value access caused by dot notation in key' do\n      assemble_and_compile('${r}', \"'abc::a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        Puppet::Pops::Lookup.lookup('abc::f.k1.s1', Puppet::Pops::Types::TypeParser.singleton.parse('String'), nil, false, nil, lookup_invocation)\n        expect(lookup_invocation.explainer.explain).to include(<<-EOS.unindent('  '))\n          Sub key: \"k1.s1\"\n            Found key: \"k1\" value: {\n              \"s1\" => \"env_f11\",\n              \"s2\" => \"env_f12\"\n            }\n            Found key: \"s1\" value: \"env_f11\"\n        EOS\n      end\n    end\n\n\n    it 'will provide a hash containing all explanation elements' do\n      assemble_and_compile('${r}', \"'abc::a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        Puppet::Pops::Lookup.lookup('abc::e', Puppet::Pops::Types::TypeParser.singleton.parse('Hash[String,String]'), nil, false, { 'strategy' => 'deep', 'merge_hash_arrays' => true }, lookup_invocation)\n        expect(lookup_invocation.explainer.to_hash).to eq(\n          {\n            :type => :root,\n            :key => 'abc::e',\n            :branches => [\n              {\n                :value => { 'k1' => 'env_e1', 'k2' => 'module_e2', 'k3' => 'env_e3' },\n                :event => :result,\n                :merge => :deep,\n                :options => { 'merge_hash_arrays' => true },\n                :type => :merge,\n                :branches => [\n                  {\n                    :key => 'abc::e',\n                    :event => :not_found,\n                    :type => :data_provider,\n                    :name => 'Global Data Provider (hiera configuration version 5)'\n                  },\n                  {\n                    :type => :data_provider,\n                    :name => 'Environment Data Provider (hiera configuration version 5)',\n                    :branches => [\n                      {\n                        :type => :data_provider,\n                        :name => 'Deprecated API function \"environment::data\"',\n                        :key => 'abc::e',\n                        :value => { 'k1' => 'env_e1', 'k3' => 'env_e3' },\n                        :event => :found\n                      }\n                    ]\n                  },\n                  {\n                    :type => :data_provider,\n                    :name => 'Module \"abc\" Data Provider (hiera configuration version 5)',\n                    :module => 'abc',\n                    :branches => [\n                      {\n                        :type => :data_provider,\n                        :name => 'Deprecated API function \"abc::data\"',\n                        :key => 'abc::e',\n                        :event => :found,\n                        :value => {\n                          'k1' => 'module_e1',\n                          'k2' => 'module_e2'\n                        }\n                      }\n                    ]\n                  }\n                ]\n              }\n            ]\n          }\n        )\n      end\n    end\n\n    it 'will explain that \"lookup_options\" is an invalid key' do\n      assemble_and_compile('${r}', \"'abc::a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('lookup_options', nil, nil, false, nil, lookup_invocation)\n        rescue Puppet::Error\n        end\n        expect(lookup_invocation.explainer.explain.chomp).to eq('Invalid key \"lookup_options\"')\n      end\n    end\n\n    it 'will explain that \"lookup_options\" is an invalid key for any key starting with \"lookup_options.\"' do\n      assemble_and_compile('${r}', \"'abc::a'\") do |scope|\n        lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)\n        begin\n          Puppet::Pops::Lookup.lookup('lookup_options.subkey', nil, nil, false, nil, lookup_invocation)\n        rescue Puppet::Error\n        end\n        expect(lookup_invocation.explainer.explain.chomp).to eq('Invalid key \"lookup_options\"')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/lookup_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'deep_merge/core'\n\ndescribe \"The lookup function\" do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  let(:env_name) { 'spec' }\n  let(:code_dir_files) { {} }\n  let(:code_dir) { tmpdir('code') }\n  let(:ruby_dir) { tmpdir('ruby') }\n  let(:env_modules) { {} }\n  let(:env_hiera_yaml) do\n    <<-YAML.unindent\n      ---\n      version: 5\n      hierarchy:\n        - name: \"Common\"\n          data_hash: yaml_data\n          path: \"common.yaml\"\n      YAML\n  end\n\n  let(:env_data) { {} }\n\n  let(:environment_files) do\n    {\n      env_name => {\n        'modules' => env_modules,\n        'hiera.yaml' => env_hiera_yaml,\n        'data' => env_data\n      }\n    }\n  end\n\n  let(:logs) { [] }\n  let(:scope_additions ) { {} }\n  let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }\n  let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n  let(:debugs) { logs.select { |log| log.level == :debug }.map { |log| log.message } }\n  let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, env_name, 'modules')]) }\n  let(:environments) { Puppet::Environments::Directories.new(populated_env_dir, []) }\n  let(:node) { Puppet::Node.new('test_lookup', :environment => env) }\n  let(:compiler) { Puppet::Parser::Compiler.new(node) }\n  let(:lookup_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'lookup') }\n  let(:invocation_with_explain) { Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true) }\n  let(:explanation) { invocation_with_explain.explainer.explain }\n\n  let(:populated_code_dir) do\n    dir_contained_in(code_dir, code_dir_files)\n    code_dir\n  end\n\n  let(:env_dir) do\n    d = File.join(populated_code_dir, 'environments')\n    Dir.mkdir(d)\n    d\n  end\n\n  let(:populated_env_dir) do\n    dir_contained_in(env_dir, environment_files)\n    env_dir\n  end\n\n  before(:each) do\n    Puppet.settings[:codedir] = code_dir\n    Puppet.push_context(:environments => environments, :current_environment => env)\n  end\n\n  after(:each) do\n    Puppet.pop_context\n    if Object.const_defined?(:Hiera)\n      Hiera.send(:remove_instance_variable, :@config) if Hiera.instance_variable_defined?(:@config)\n      Hiera.send(:remove_instance_variable, :@logger) if Hiera.instance_variable_defined?(:@logger)\n      if Hiera.const_defined?(:Config)\n        Hiera::Config.send(:remove_instance_variable, :@config) if Hiera::Config.instance_variable_defined?(:@config)\n      end\n      if Hiera.const_defined?(:Backend) && Hiera::Backend.respond_to?(:clear!)\n        Hiera::Backend.clear!\n      end\n    end\n  end\n\n  def collect_notices(code, explain = false, &block)\n    Puppet[:code] = code\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      scope = compiler.topscope\n      scope['domain'] = 'example.com'\n      scope_additions.each_pair { |k, v| scope[k] = v }\n      if explain\n        begin\n          invocation_with_explain.lookup('dummy', nil) do\n            if block_given?\n              compiler.compile { |catalog| block.call(compiler.topscope); catalog }\n            else\n              compiler.compile\n            end\n          end\n        rescue RuntimeError => e\n          invocation_with_explain.report_text { e.message }\n        end\n      else\n        if block_given?\n          compiler.compile { |catalog| block.call(compiler.topscope); catalog }\n        else\n          compiler.compile\n        end\n      end\n    end\n    nil\n  end\n\n  def lookup(key, options = {}, explain = false, type = nil)\n    value_type = type ? \", #{type}\" : ''\n    nc_opts = options.empty? ? '' : \", #{Puppet::Pops::Types::TypeFormatter.string(options)}\"\n    keys = key.is_a?(Array) ? key : [key]\n    collect_notices(keys.map { |k| \"notice(String(lookup('#{k}'#{value_type}#{nc_opts}), '%p'))\" }.join(\"\\n\"), explain)\n    if explain\n      explanation\n    else\n      result = notices.map { |n| Puppet::Pops::Types::TypeParser.singleton.parse_literal(n) }\n      key.is_a?(Array) ? result : result[0]\n    end\n  end\n\n  def explain(key, options = {})\n    lookup(key, options, true)[1]\n    explanation\n  end\n\n  context 'with faulty hiera.yaml configuration' do\n    context 'in global layer' do\n      let(:global_data) do\n        {\n          'common.yaml' => <<-YAML.unindent\n            a: value a (from global)\n            YAML\n        }\n      end\n\n      let(:code_dir_files) do\n        {\n          'hiera.yaml' => hiera_yaml,\n          'data' => global_data\n        }\n      end\n\n      before(:each) do\n        # Need to set here since spec_helper defines these settings in its \"before each\"\n        Puppet.settings[:codedir] = populated_code_dir\n        Puppet.settings[:hiera_config] = File.join(code_dir, 'hiera.yaml')\n      end\n\n      context 'using a not yet supported hiera version' do\n        let(:hiera_yaml) { <<-YAML.unindent }\n          version: 6\n          YAML\n\n        it 'fails and reports error' do\n          expect { lookup('a') }.to raise_error(\"This runtime does not support hiera.yaml version 6 (file: #{code_dir}/hiera.yaml)\")\n        end\n      end\n\n      context 'with multiply defined backend using hiera version 3' do\n        let(:hiera_yaml) { <<-YAML.unindent }\n          :version: 3\n          :backends:\n            - yaml\n            - json\n            - yaml\n          YAML\n\n        it 'fails and reports error' do\n          expect { lookup('a') }.to raise_error(\n            \"Backend 'yaml' is defined more than once. First defined at (line: 3) (file: #{code_dir}/hiera.yaml, line: 5)\")\n        end\n      end\n\n      context 'using hiera version 4' do\n        let(:hiera_yaml) { <<-YAML.unindent }\n          version: 4\n          YAML\n\n        it 'fails and reports error' do\n          expect { lookup('a') }.to raise_error(\n            \"hiera.yaml version 4 cannot be used in the global layer (file: #{code_dir}/hiera.yaml)\")\n        end\n      end\n\n      context 'using hiera version 5' do\n        context 'with multiply defined hierarchy' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common\n                path: common.yaml\n              - name: Other\n                path: other.yaml\n              - name: Common\n                path: common.yaml\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"Hierarchy name 'Common' defined more than once. First defined at (line: 3) (file: #{code_dir}/hiera.yaml, line: 7)\")\n          end\n        end\n\n        context 'with hiera3_backend that is provided as data_hash function' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common\n                hiera3_backend: hocon\n                path: common.conf\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"Use \\\"data_hash: hocon_data\\\" instead of \\\"hiera3_backend: hocon\\\" (file: #{code_dir}/hiera.yaml, line: 4)\")\n          end\n        end\n\n        context 'with no data provider function defined' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            defaults:\n              datadir: data\n            hierarchy:\n              - name: Common\n                path: common.txt\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"One of data_hash, lookup_key, data_dig, or hiera3_backend must be defined in hierarchy 'Common' (file: #{code_dir}/hiera.yaml)\")\n          end\n        end\n\n        context 'with multiple data providers in defaults' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            defaults:\n              data_hash: yaml_data\n              lookup_key: eyaml_lookup_key\n              datadir: data\n            hierarchy:\n              - name: Common\n                path: common.txt\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"Only one of data_hash, lookup_key, data_dig, or hiera3_backend can be defined in defaults (file: #{code_dir}/hiera.yaml)\")\n          end\n        end\n\n        context 'with non existing data provider function' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common\n                data_hash: nonesuch_txt_data\n                path: common.yaml\n            YAML\n\n          it 'fails and reports error' do\n            Puppet[:strict] = :error\n            expect { lookup('a') }.to raise_error(\n              \"Unable to find 'data_hash' function named 'nonesuch_txt_data' (file: #{code_dir}/hiera.yaml)\")\n          end\n        end\n\n        context 'with a declared default_hierarchy' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common\n                path: common.yaml\n            default_hierarchy:\n              - name: Defaults\n                path: defaults.yaml\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"'default_hierarchy' is only allowed in the module layer (file: #{code_dir}/hiera.yaml, line: 5)\")\n          end\n        end\n\n        context 'with missing variables' do\n          let(:scope_additions) { { 'fqdn' => 'test.example.com' } }\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common # don't report this line %{::nonesuch}\n                path: \"%{::fqdn}/%{::nonesuch}/data.yaml\"\n            YAML\n\n          it 'fails and reports errors when strict == error' do\n            expect { lookup('a') }.to raise_error(\"Function lookup() did not find a value for the name 'a'\")\n          end\n        end\n\n        context 'using interpolation functions' do\n          let(:hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common # don't report this line %{::nonesuch}\n                path: \"%{lookup('fqdn')}/data.yaml\"\n            YAML\n\n          it 'fails and reports errors when strict == error' do\n            expect { lookup('a') }.to raise_error(\"Interpolation using method syntax is not allowed in this context (file: #{code_dir}/hiera.yaml)\")\n          end\n        end\n      end\n    end\n\n    context 'in environment layer' do\n      context 'using hiera version 4' do\n        context 'with an unknown backend' do\n          let(:env_hiera_yaml) { <<-YAML.unindent }\n            version: 4\n            hierarchy:\n              - name: Common\n                backend: nonesuch\n                path: common.yaml\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"No data provider is registered for backend 'nonesuch' (file: #{env_dir}/spec/hiera.yaml, line: 4)\")\n          end\n        end\n\n        context 'with multiply defined hierarchy' do\n          let(:env_hiera_yaml) { <<-YAML.unindent }\n            version: 4\n            hierarchy:\n              - name: Common\n                backend: yaml\n                path: common.yaml\n              - name: Other\n                backend: yaml\n                path: other.yaml\n              - name: Common\n                backend: yaml\n                path: common.yaml\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"Hierarchy name 'Common' defined more than once. First defined at (line: 3) (file: #{env_dir}/spec/hiera.yaml, line: 9)\")\n          end\n        end\n      end\n\n      context 'using hiera version 5' do\n        context 'with a hiera3_backend declaration' do\n          let(:env_hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common\n                hiera3_backend: something\n            YAML\n\n          it 'fails and reports error' do\n            expect { lookup('a') }.to raise_error(\n              \"'hiera3_backend' is only allowed in the global layer (file: #{env_dir}/spec/hiera.yaml, line: 4)\")\n          end\n        end\n\n        context 'with a declared default_hierarchy' do\n          let(:env_hiera_yaml) { <<-YAML.unindent }\n            version: 5\n            hierarchy:\n              - name: Common\n                path: common.yaml\n            default_hierarchy:\n              - name: Defaults\n                path: defaults.yaml\n            YAML\n\n          it 'fails and reports error' do\n            Puppet[:strict] = :error\n            expect { lookup('a') }.to raise_error(\n              \"'default_hierarchy' is only allowed in the module layer (file: #{env_dir}/spec/hiera.yaml, line: 5)\")\n          end\n        end\n      end\n    end\n  end\n\n  context 'with an environment' do\n    let(:env_data) do\n      {\n        'common.yaml' => <<-YAML.unindent\n        ---\n        a: value a (from environment)\n        c:\n          c_b: value c_b (from environment)\n        mod_a::a: value mod_a::a (from environment)\n        mod_a::hash_a:\n          a: value mod_a::hash_a.a (from environment)\n        mod_a::hash_b:\n          a: value mod_a::hash_b.a (from environment)\n        hash_b:\n          hash_ba:\n            bab: value hash_b.hash_ba.bab (from environment)\n        hash_c:\n          hash_ca:\n            caa: value hash_c.hash_ca.caa (from environment)\n        lookup_options:\n          mod_a::hash_b:\n            merge: hash\n          hash_c:\n            merge: hash\n      YAML\n      }\n    end\n\n    it 'finds data in the environment' do\n      expect(lookup('a')).to eql('value a (from environment)')\n    end\n\n    context 'with log-level debug' do\n      before(:each) { Puppet[:log_level] = 'debug' }\n\n      it 'does not report a regular lookup as APL' do\n        expect(lookup('a')).to eql('value a (from environment)')\n        expect(debugs.count { |dbg| dbg =~ /\\A\\s*Automatic Parameter Lookup of/ }).to eql(0)\n      end\n\n      it 'reports regular lookup as lookup' do\n        expect(lookup('a')).to eql('value a (from environment)')\n        expect(debugs.count { |dbg| dbg =~ /\\A\\s*Lookup of/ }).to eql(1)\n      end\n\n      it 'does not report APL as lookup' do\n        collect_notices(\"class mod_a($a) { notice($a) }; include mod_a\")\n        expect(debugs.count { |dbg| dbg =~ /\\A\\s*Lookup of/ }).to eql(0)\n      end\n\n      it 'reports APL as APL' do\n        collect_notices(\"class mod_a($a) { notice($a) }; include mod_a\")\n        expect(debugs.count { |dbg| dbg =~ /\\A\\s*Automatic Parameter Lookup of/ }).to eql(1)\n      end\n    end\n\n    context 'that has no lookup configured' do\n      let(:environment_files) do\n        {\n          env_name => {\n            'data' => env_data\n          }\n        }\n      end\n\n      it 'does not find data in the environment' do\n        expect { lookup('a') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'a'/)\n      end\n\n      context \"but an environment.conf with 'environment_data_provider=hiera'\" do\n        let(:environment_files) do\n          {\n            env_name => {\n              'environment.conf' => \"environment_data_provider=hiera\\n\",\n              'data' => env_data\n            }\n          }\n        end\n\n        it 'finds data in the environment and reports deprecation warning for environment.conf' do\n          expect(lookup('a')).to eql('value a (from environment)')\n          expect(warnings).to include(/Defining environment_data_provider='hiera' in environment.conf is deprecated. A 'hiera.yaml' file should be used instead/)\n        end\n\n        context 'and a hiera.yaml file' do\n          let(:env_hiera_yaml) do\n            <<-YAML.unindent\n              ---\n              version: 4\n              hierarchy:\n                - name: common\n                  backend: yaml\n              YAML\n          end\n\n          let(:environment_files) do\n            {\n              env_name => {\n                'hiera.yaml' => env_hiera_yaml,\n                'environment.conf' => \"environment_data_provider=hiera\\n\",\n                'data' => env_data\n              }\n            }\n          end\n\n          it 'finds data in the environment and reports deprecation warnings for both environment.conf and hiera.yaml' do\n            expect(lookup('a')).to eql('value a (from environment)')\n            expect(warnings).to include(/Defining environment_data_provider='hiera' in environment.conf is deprecated/)\n            expect(warnings).to include(/Use of 'hiera.yaml' version 4 is deprecated. It should be converted to version 5/)\n          end\n        end\n      end\n\n      context \"but an environment.conf with 'environment_data_provider=function'\" do\n        let(:environment_files) do\n          {\n            env_name => {\n              'environment.conf' => \"environment_data_provider=function\\n\",\n              'functions' => {\n                'environment' => { 'data.pp' => <<-PUPPET.unindent }\n                    function environment::data() {\n                      { 'a' => 'value a' }\n                    }\n                    PUPPET\n              }\n            }\n          }\n        end\n\n        it 'finds data in the environment and reports deprecation warning for environment.conf' do\n          expect(lookup('a')).to eql('value a')\n          expect(warnings).to include(/Defining environment_data_provider='function' in environment.conf is deprecated. A 'hiera.yaml' file should be used instead/)\n          expect(warnings).to include(/Using of legacy data provider function 'environment::data'. Please convert to a 'data_hash' function/)\n        end\n      end\n    end\n\n    context 'that has interpolated paths configured' do\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n          ---\n          version: 5\n          hierarchy:\n            - name: \"Varying\"\n              data_hash: yaml_data\n              path: \"#{data_path}\"\n          YAML\n      end\n\n      let(:environment_files) do\n        {\n          env_name => {\n            'hiera.yaml' => env_hiera_yaml,\n            'modules' => {},\n            'data' => {\n              'x.yaml' => <<-YAML.unindent,\n                y: value y from x\n                YAML\n              'x_d.yaml' => <<-YAML.unindent,\n                y: value y from x_d\n                YAML\n              'x_e.yaml' => <<-YAML.unindent,\n                y: value y from x_e\n                YAML\n            }\n          }\n        }\n      end\n\n      context 'using local variable reference' do\n        let(:data_path) { 'x%{var.sub}.yaml' }\n\n        it 'reloads the configuration if interpolated values change' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            scope['var'] = {}\n            expect(lookup_func.call(scope, 'y')).to eql('value y from x')\n            nested_scope = scope.compiler.newscope(scope)\n            nested_scope['var'] = { 'sub' => '_d' }\n            expect(lookup_func.call(nested_scope, 'y')).to eql('value y from x_d')\n            nested_scope = scope.compiler.newscope(scope)\n            nested_scope['var'] = { 'sub' => '_e' }\n            expect(lookup_func.call(nested_scope, 'y')).to eql('value y from x_e')\n          end\n          expect(notices).to eql(['success'])\n          expect(debugs.any? { |m| m =~ /Hiera configuration recreated due to change of scope variables used in interpolation expressions/ }).to be_truthy\n        end\n\n        it 'does not include the lookups performed during stability check in explain output' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            var = { 'sub' => '_d' }\n            scope['var'] = var\n            expect(lookup_func.call(scope, 'y')).to eql('value y from x_d')\n\n            # Second call triggers the check\n            expect(lookup_func.call(scope, 'y')).to eql('value y from x_d')\n          end\n          expect(notices).to eql(['success'])\n          expect(debugs.any? { |m| m =~ /Sub key: \"sub\"/ }).to be_falsey\n        end\n      end\n\n      context 'using global variable reference' do\n        let(:data_path) { 'x%{::var.sub}.yaml' }\n\n        it 'does not raise an error when reloads the configuration if interpolating undefined values' do\n          collect_notices(\"notice('success')\") do |scope|\n            expect { lookup_func.call(scope, 'y') }.to_not raise_error\n          end\n        end\n\n        it 'does not reload the configuration if value changes locally' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            scope['var'] = { 'sub' => '_d' }\n            expect(lookup_func.call(scope, 'y')).to eql('value y from x_d')\n            nested_scope = scope.compiler.newscope(scope)\n            nested_scope['var'] = { 'sub' => '_e' }\n            expect(lookup_func.call(nested_scope, 'y')).to eql('value y from x_d')\n          end\n          expect(notices).to eql(['success'])\n          expect(debugs.any? { |m| m =~ /Hiera configuration recreated due to change of scope variables used in interpolation expressions/ }).to be_falsey\n        end\n      end\n    end\n\n    context 'that uses reserved' do\n       let(:environment_files) do\n        { env_name => { 'hiera.yaml' => hiera_yaml } }\n      end\n\n      context 'option' do\n       let(:hiera_yaml) { <<-YAML.unindent }\n          version: 5\n          hierarchy:\n            - name: \"Illegal\"\n              options:\n                #{opt_spec}\n              data_hash: yaml_data\n          YAML\n\n        context 'path' do\n          let(:opt_spec) { 'path: data/foo.yaml' }\n\n          it 'fails and reports the reserved option key' do\n            expect { lookup('a') }.to raise_error do |e|\n              expect(e.message).to match(/Option key 'path' used in hierarchy 'Illegal' is reserved by Puppet/)\n            end\n          end\n        end\n\n        context 'uri' do\n          let(:opt_spec) { 'uri: file:///data/foo.yaml' }\n\n          it 'fails and reports the reserved option key' do\n            expect { lookup('a') }.to raise_error do |e|\n              expect(e.message).to match(/Option key 'uri' used in hierarchy 'Illegal' is reserved by Puppet/)\n            end\n          end\n        end\n      end\n\n      context 'default option' do\n        let(:hiera_yaml) { <<-YAML.unindent }\n          ---\n          version: 5\n          defaults:\n              options:\n                #{opt_spec}\n          hierarchy:\n            - name: \"Illegal\"\n              data_hash: yaml_data\n          YAML\n\n        context 'path' do\n          let(:opt_spec) { 'path: data/foo.yaml' }\n\n          it 'fails and reports the reserved option key' do\n            expect { lookup('a') }.to raise_error do |e|\n              expect(e.message).to match(/Option key 'path' used in defaults is reserved by Puppet/)\n            end\n          end\n        end\n\n        context 'uri' do\n          let(:opt_spec) { 'uri: file:///data/foo.yaml' }\n\n          it 'fails and reports the reserved option key' do\n            expect { lookup('a') }.to raise_error do |e|\n              expect(e.message).to match(/Option key 'uri' used in defaults is reserved by Puppet/)\n            end\n          end\n        end\n      end\n    end\n\n    context 'with yaml data file' do\n      let(:environment_files) do\n        {\n          env_name => {\n            'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              YAML\n            'data' => {\n              'common.yaml' => common_yaml\n            }\n          }\n        }\n      end\n\n      context 'that contains hash values with interpolated keys' do\n        let(:common_yaml) do\n          <<-YAML.unindent\n          ---\n          a:\n              \"%{key}\": \"the %{value}\"\n          b:  \"Detail in %{lookup('a.a_key')}\"\n          YAML\n        end\n\n        it 'interpolates both key and value\"' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            scope['key'] = ''\n            scope['value'] = ''\n            expect(lookup_func.call(scope, 'a')).to eql({'' => 'the '})\n            nested_scope = scope.compiler.newscope(scope)\n            nested_scope['key'] = 'a_key'\n            nested_scope['value'] = 'interpolated value'\n            expect(lookup_func.call(nested_scope, 'a')).to eql({'a_key' => 'the interpolated value'})\n          end\n          expect(notices).to eql(['success'])\n        end\n\n        it 'navigates to a value behind an interpolated key\"' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            scope['key'] = 'a_key'\n            scope['value'] = 'interpolated value'\n            expect(lookup_func.call(scope, 'a.a_key')).to eql('the interpolated value')\n          end\n          expect(notices).to eql(['success'])\n        end\n\n        it 'navigates to a value behind an interpolated key using an interpolated value\"' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            scope['key'] = 'a_key'\n            scope['value'] = 'interpolated value'\n            expect(lookup_func.call(scope, 'b')).to eql('Detail in the interpolated value')\n          end\n          expect(notices).to eql(['success'])\n        end\n      end\n\n      context 'that is empty' do\n        let(:common_yaml) { '' }\n\n        it 'fails with a \"did not find\"' do\n          expect { lookup('a') }.to raise_error do |e|\n            expect(e.message).to match(/did not find a value for the name 'a'/)\n          end\n        end\n\n        it 'logs a warning that the file does not contain a hash' do\n          expect { lookup('a') }.to raise_error(Puppet::DataBinding::LookupError)\n          expect(warnings).to include(/spec\\/data\\/common.yaml: file does not contain a valid yaml hash/)\n        end\n      end\n\n      context 'that contains illegal yaml' do\n        let(:common_yaml) { \"@!#%**&:\\n\" }\n\n        it 'fails lookup and that the key is not found' do\n          expect { lookup('a') }.to raise_error do |e|\n            expect(e.message).to match(/Unable to parse/)\n          end\n        end\n      end\n\n      context 'that contains a legal yaml that is not a hash' do\n        let(:common_yaml) { \"- A list\\n- of things\" }\n\n        it 'fails with a \"invalid yaml hash\"' do\n          expect { lookup('a') }.to raise_error do |e|\n            expect(e.message).to match(/spec\\/data\\/common.yaml: file does not contain a valid yaml hash/)\n          end\n        end\n\n        context 'when strict mode is off' do\n          before :each do\n            Puppet[:strict] = :warning\n          end\n          it 'fails with a \"did not find\"' do\n            expect { lookup('a') }.to raise_error do |e|\n              expect(e.message).to match(/did not find a value for the name 'a'/)\n            end\n          end\n\n          it 'logs a warning that the file does not contain a hash' do\n            expect { lookup('a') }.to raise_error(Puppet::DataBinding::LookupError)\n            expect(warnings).to include(/spec\\/data\\/common.yaml: file does not contain a valid yaml hash/)\n          end\n        end\n      end\n\n      context 'that contains a legal yaml hash with illegal types' do\n        let(:common_yaml) do\n          <<-YAML.unindent\n          ---\n          a: !ruby/object:Puppet::Graph::Key\n              value: x\n          YAML\n        end\n\n        it 'fails lookup and reports parsing failed' do\n          expect { lookup('a') }.to raise_error do |e|\n            expect(e.message).to match(/Unable to parse .*: Tried to load unspecified class: Puppet::Graph::Key/)\n          end\n        end\n      end\n\n      context 'that contains a legal yaml hash with unexpected types' do\n        let(:common_yaml) do\n          <<-YAML.unindent\n          ---\n          a: 123\n          YAML\n        end\n\n        it 'fails lookup and reports a type mismatch' do\n          expect { lookup('a', {}, false, String) }.to raise_error do |e|\n            expect(e.message).to match(/Found value has wrong type, expects a String value, got Integer \\(line: 1, column: \\d+\\)/)\n          end\n        end\n      end\n\n      context 'that contains illegal interpolations' do\n        context 'in the form of an alias that is not the entire string' do\n          let(:common_yaml) { <<-YAML.unindent }\n            a: \"%{alias('x')} and then some\"\n            x: value x\n            YAML\n\n          it 'fails lookup and reports a type mismatch' do\n            expect { lookup('a') }.to raise_error(\"'alias' interpolation is only permitted if the expression is equal to the entire string\")\n          end\n        end\n\n        context 'in the form of an unknown function name' do\n          let(:common_yaml) { <<-YAML.unindent }\n            a: \"%{what('x')}\"\n            x: value x\n            YAML\n\n          it 'fails lookup and reports a type mismatch' do\n            expect { lookup('a') }.to raise_error(\"Unknown interpolation method 'what'\")\n          end\n        end\n      end\n\n      context 'that contains an array with duplicates' do\n        let(:common_yaml) { <<-YAML.unindent }\n          a:\n           - alpha\n           - bravo\n           - charlie\n           - bravo\n          YAML\n\n        it 'retains the duplicates when using default merge strategy' do\n          expect(lookup('a')).to eql(%w(alpha bravo charlie bravo))\n        end\n\n        it 'does deduplification when using merge strategy \"unique\"' do\n          expect(lookup('a', :merge => 'unique')).to eql(%w(alpha bravo charlie))\n        end\n      end\n    end\n\n    context 'with lookup_options' do\n      let(:environment_files) do\n        {\n          env_name => {\n            'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              YAML\n            'data' => {\n              'common.yaml' => common_yaml\n            }\n          }\n        }\n      end\n\n      context 'that are empty' do\n        let(:common_yaml) { <<-YAML.unindent }\n          lookup_options:\n          a: b\n          YAML\n\n        it 'ignores empty options' do\n          expect(lookup('a')).to eq(\"b\")\n        end\n      end\n\n      context 'that contains a legal yaml hash with unexpected types' do\n        let(:common_yaml) { <<-YAML.unindent }\n          lookup_options:\n            :invalid_symbol\n          YAML\n\n        it 'fails lookup and reports a type mismatch' do\n          expect {\n            lookup('a')\n          }.to raise_error(Puppet::DataBinding::LookupError, /has wrong type, expects Puppet::LookupValue, got Runtime\\[ruby, 'Symbol'\\]/)\n        end\n      end\n    end\n\n    context 'with lookup_options configured using patterns' do\n      let(:mod_common) {\n        <<-YAML.unindent\n          mod::hash_a:\n            aa:\n              aaa: aaa (from module)\n            ab:\n              aba: aba (from module)\n          mod::hash_b:\n            ba:\n              baa: baa (from module)\n            bb:\n              bba: bba (from module)\n          lookup_options:\n            '^mod::ha.*_a':\n              merge: deep\n            '^mod::ha.*_b':\n              merge: deep\n        YAML\n      }\n\n      let(:mod_base) do\n        {\n          'hiera.yaml' => <<-YAML.unindent,\n            version: 5\n            YAML\n          'data' => {\n            'common.yaml' => mod_common\n          }\n        }\n      end\n\n      let(:env_modules) do\n        {\n          'mod' => mod_base\n        }\n      end\n\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n          ---\n          version: 5\n          hierarchy:\n            - name: X\n              paths:\n              - first.yaml\n              - second.yaml\n          YAML\n      end\n\n      let(:env_lookup_options) { <<-YAML.unindent }\n        lookup_options:\n          b:\n            merge: hash\n          '^[^b]$':\n             merge: deep\n          '^c':\n             merge: first\n          '^b':\n             merge: first\n          '^mod::ha.*_b':\n            merge: hash\n        YAML\n\n      let(:env_data) do\n        {\n          'first.yaml' => <<-YAML.unindent + env_lookup_options,\n            a:\n              aa:\n                aaa: a.aa.aaa\n            b:\n              ba:\n                baa: b.ba.baa\n              bb:\n                bba: b.bb.bba\n            c:\n              ca:\n                caa: c.ca.caa\n            mod::hash_a:\n              aa:\n                aab: aab (from environment)\n              ab:\n                aba: aba (from environment)\n                abb: abb (from environment)\n            mod::hash_b:\n              ba:\n                bab: bab (from environment)\n              bc:\n                bca: bca (from environment)\n            sa:\n              sa1: ['e', 'd', '--f']\n            YAML\n          'second.yaml' => <<-YAML.unindent,\n            a:\n              aa:\n                aab: a.aa.aab\n            b:\n              ba:\n                bab: b.ba.bab\n              bb:\n                bbb: b.bb.bbb\n            c:\n              ca:\n                cab: c.ca.cab\n            sa:\n              sa1: ['b', 'a', 'f', 'c']\n            YAML\n        }\n      end\n\n      it 'finds lookup_options that matches a pattern' do\n        expect(lookup('a')).to eql({'aa' => { 'aaa' => 'a.aa.aaa', 'aab' => 'a.aa.aab' }})\n      end\n\n      it 'gives a direct key match higher priority than a matching pattern' do\n        expect(lookup('b')).to eql({'ba' => { 'baa' => 'b.ba.baa' }, 'bb' => { 'bba'=>'b.bb.bba' }})\n      end\n\n      it 'uses the first matching pattern' do\n        expect(lookup('c')).to eql({'ca' => { 'caa' => 'c.ca.caa', 'cab' => 'c.ca.cab' }})\n      end\n\n      it 'uses lookup_option found by pattern from module' do\n        expect(lookup('mod::hash_a')).to eql({\n          'aa' => {\n            'aaa' => 'aaa (from module)',\n            'aab' => 'aab (from environment)'\n          },\n          'ab' => {\n            'aba' => 'aba (from environment)',\n            'abb' => 'abb (from environment)'\n          }\n        })\n      end\n\n      it 'merges lookup_options found by pattern in environment and module (environment wins)' do\n        expect(lookup('mod::hash_b')).to eql({\n          'ba' => {\n            'bab' => 'bab (from environment)'\n          },\n          'bb' => {\n            'bba' => 'bba (from module)'\n          },\n          'bc' => {\n            'bca' => 'bca (from environment)'\n          }\n        })\n      end\n\n      context 'and lookup_options is empty' do\n        let(:mod_common) { <<-YAML.unindent }\n          lookup_options:\n          mod::a: b\n          YAML\n\n        it 'ignores empty options' do\n          expect(lookup('mod::a')).to eq(\"b\")\n        end\n      end\n\n      context 'and lookup_options contains a legal hash with unexpected types' do\n        let(:mod_common) { <<-YAML.unindent }\n          lookup_options:\n            :invalid_symbol\n          YAML\n\n        it 'fails lookup and reports a type mismatch' do\n          expect {\n            lookup('mod::a')\n          }.to raise_error(Puppet::DataBinding::LookupError, /has wrong type, expects Puppet::LookupValue, got Runtime\\[ruby, 'Symbol'\\]/)\n        end\n      end\n\n      context 'and patterns in module are not limited to module keys' do\n        let(:mod_common) {\n          <<-YAML.unindent\n          mod::hash_a:\n            aa:\n              aaa: aaa (from module)\n            ab:\n              aba: aba (from module)\n          lookup_options:\n            '^.*_a':\n              merge: deep\n          YAML\n        }\n\n        it 'fails with error' do\n          expect { lookup('mod::a') }.to raise_error(Puppet::DataBinding::LookupError, /all lookup_options patterns must match a key starting with module name/)\n        end\n      end\n\n      context 'and there are no lookup options that do not use patterns' do\n\n        let(:env_lookup_options) { <<-YAML.unindent }\n          lookup_options:\n            '^[^b]$':\n              merge: deep\n            '^c':\n              merge: first\n            '^b':\n              merge: first\n            '^mod::ha.*_b':\n              merge: hash\n          YAML\n\n        it 'finds lookup_options that matches a pattern' do\n          expect(lookup('a')).to eql({'aa' => { 'aaa' => 'a.aa.aaa', 'aab' => 'a.aa.aab' }})\n        end\n      end\n\n      context 'and lookup options use a hash' do\n\n        let(:env_lookup_options) { <<-YAML.unindent }\n          lookup_options:\n            'sa':\n              merge:\n                strategy: deep\n                knockout_prefix: --\n                sort_merged_arrays: true\n        YAML\n\n        it 'applies knockout_prefix and sort_merged_arrays' do\n          expect(lookup('sa')).to eql({ 'sa1' => %w(a b c d e) })\n        end\n\n        it 'overrides knockout_prefix and sort_merged_arrays with explicitly given values' do\n          expect(\n            lookup('sa', 'merge' => { 'strategy' => 'deep', 'knockout_prefix' => '##', 'sort_merged_arrays' => false })).to(\n              eql({ 'sa1' => %w(b a f c e d --f) }))\n        end\n      end\n    end\n\n    context 'and an environment Hiera v5 configuration using globs' do\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n        ---\n        version: 5\n        hierarchy:\n          - name: Globs\n            globs:\n              - \"globs/*.yaml\"\n              - \"globs_%{domain}/*.yaml\"\n        YAML\n      end\n\n      let(:env_data) do\n        {\n          'globs' => {\n            'a.yaml' => <<-YAML.unindent,\n              glob_a: value glob_a\n              YAML\n            'b.yaml' => <<-YAML.unindent\n              glob_b:\n                a: value glob_b.a\n                b: value glob_b.b\n            YAML\n          },\n          'globs_example.com' => {\n            'a.yaml' => <<-YAML.unindent,\n              glob_c: value glob_a\n              YAML\n            'b.yaml' => <<-YAML.unindent\n              glob_d:\n                a: value glob_d.a\n                b: value glob_d.b\n            YAML\n\n          }\n        }\n      end\n\n      it 'finds environment data using globs' do\n        expect(lookup('glob_a')).to eql('value glob_a')\n        expect(warnings).to be_empty\n      end\n\n      it 'finds environment data using interpolated globs' do\n        expect(lookup('glob_d.a')).to eql('value glob_d.a')\n        expect(warnings).to be_empty\n      end\n    end\n\n    context 'and an environment Hiera v5 configuration using uris' do\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n        ---\n        version: 5\n        hierarchy:\n          - name: Uris\n            uris:\n              - \"http://test.example.com\"\n              - \"/some/arbitrary/path\"\n              - \"urn:with:opaque:path\"\n              - \"dothis%20-f%20bar\"\n            data_hash: mod::uri_test_func\n        YAML\n      end\n\n      let(:env_modules) do\n        {\n          'mod' => { 'lib' => { 'puppet' => { 'functions' => { 'mod' => { 'uri_test_func.rb' => <<-RUBY } } } } }\n            Puppet::Functions.create_function(:'mod::uri_test_func') do\n              dispatch :uri_test_func do\n                param 'Hash', :options\n                param 'Puppet::LookupContext', :context\n              end\n\n              def uri_test_func(options, context)\n                { 'uri' => [ options['uri'] ] }\n              end\n            end\n            RUBY\n        }\n      end\n\n      it 'The uris are propagated in the options hash' do\n        expect(lookup('uri', 'merge' => 'unique')).to eql(\n          %w(http://test.example.com /some/arbitrary/path urn:with:opaque:path dothis%20-f%20bar))\n        expect(warnings).to be_empty\n      end\n\n      context 'and a uri uses bad syntax' do\n        let(:env_hiera_yaml) do\n          <<-YAML.unindent\n        ---\n        version: 5\n        hierarchy:\n          - name: Uris\n            uri: \"dothis -f bar\"\n            data_hash: mod::uri_test_func\n          YAML\n        end\n\n        it 'an attempt to lookup raises InvalidURIError' do\n          expect{ lookup('uri', 'merge' => 'unique') }.to raise_error(/bad URI/)\n        end\n      end\n    end\n\n    context 'and an environment Hiera v5 configuration using mapped_paths' do\n      let(:scope_additions) do\n        {\n          'mapped' =>  {\n            'array_var' => ['a', 'b', 'c'],\n            'hash_var' => { 'x' => 'a', 'y' => 'b', 'z' => 'c' },\n            'string_var' => 's' },\n          'var' => 'global_var' # overridden by mapped path variable\n        }\n      end\n\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n        ---\n        version: 5\n        hierarchy:\n          - name: Mapped Paths\n            mapped_paths: #{mapped_paths}\n          - name: Global Path\n            path: \"%{var}.yaml\"\n        YAML\n      end\n\n      let(:environment_files) do\n        {\n          env_name => {\n            'hiera.yaml' => env_hiera_yaml,\n            'data' => env_data\n          }\n        }\n      end\n\n      context 'that originates from an array' do\n        let (:mapped_paths) { '[mapped.array_var, var, \"paths/%{var}.yaml\"]' }\n\n        let(:env_data) do\n          {\n            'paths' => {\n              'a.yaml' => <<-YAML.unindent,\n                path_a: value path_a\n                path_h:\n                  a: value path_h.a\n                  c: value path_h.c\n                YAML\n              'b.yaml' => <<-YAML.unindent,\n                path_h:\n                  b: value path_h.b\n                  d: value path_h.d\n                YAML\n              'd.yaml' => <<-YAML.unindent\n                path_h:\n                  b: value path_h.b (from d.yaml)\n                  d: value path_h.d (from d.yaml)\n                YAML\n            },\n            'global_var.yaml' => <<-YAML.unindent,\n              path_h:\n                e: value path_h.e\n              YAML\n            'other_var.yaml' => <<-YAML.unindent\n              path_h:\n                e: value path_h.e (from other_var.yaml)\n              YAML\n          }\n        end\n\n        it 'finds environment data using mapped_paths' do\n          expect(lookup('path_a')).to eql('value path_a')\n          expect(warnings).to be_empty\n        end\n\n        it 'includes mapped path in explain output' do\n          explanation = explain('path_h', 'merge' => 'deep')\n          ['a', 'b', 'c'].each do |var|\n            expect(explanation).to match(/^\\s+Path \"#{env_dir}\\/spec\\/data\\/paths\\/#{var}\\.yaml\"\\n\\s+Original path: \"paths\\/%\\{var\\}\\.yaml\"/)\n          end\n          expect(warnings).to be_empty\n        end\n\n        it 'performs merges between mapped paths and global path interpolated using same key' do\n          expect(lookup('path_h', 'merge' => 'hash')).to eql(\n            {\n              'a' => 'value path_h.a',\n              'b' => 'value path_h.b',\n              'c' => 'value path_h.c',\n              'd' => 'value path_h.d',\n              'e' => 'value path_h.e'\n            })\n          expect(warnings).to be_empty\n        end\n\n        it 'keeps track of changes in key overridden by interpolated key' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            expect(lookup_func.call(scope, 'path_h', 'merge' => 'hash')).to eql(\n              {\n                'a' => 'value path_h.a',\n                'b' => 'value path_h.b',\n                'c' => 'value path_h.c',\n                'd' => 'value path_h.d',\n                'e' => 'value path_h.e'\n              })\n            scope.with_local_scope('var' => 'other_var') do\n              expect(lookup_func.call(scope, 'path_h', 'merge' => 'hash')).to eql(\n                {\n                  'a' => 'value path_h.a',\n                  'b' => 'value path_h.b',\n                  'c' => 'value path_h.c',\n                  'd' => 'value path_h.d',\n                  'e' => 'value path_h.e (from other_var.yaml)'\n                })\n            end\n          end\n          expect(notices).to eql(['success'])\n          expect(debugs.any? { |m| m =~ /Hiera configuration recreated due to change of scope variables used in interpolation expressions/ }).to be_truthy\n        end\n\n        it 'keeps track of changes in elements of mapped key' do\n          Puppet[:log_level] = 'debug'\n          collect_notices(\"notice('success')\") do |scope|\n            expect(lookup_func.call(scope, 'path_h', 'merge' => 'hash')).to eql(\n              {\n                'a' => 'value path_h.a',\n                'b' => 'value path_h.b',\n                'c' => 'value path_h.c',\n                'd' => 'value path_h.d',\n                'e' => 'value path_h.e'\n              })\n            scope['mapped']['array_var'] = ['a', 'c', 'd']\n            expect(lookup_func.call(scope, 'path_h', 'merge' => 'hash')).to eql(\n              {\n                'a' => 'value path_h.a',\n                'b' => 'value path_h.b (from d.yaml)',\n                'c' => 'value path_h.c',\n                'd' => 'value path_h.d (from d.yaml)',\n                'e' => 'value path_h.e'\n              })\n          end\n          expect(notices).to eql(['success'])\n          expect(debugs.any? { |m| m =~ /Hiera configuration recreated due to change of scope variables used in interpolation expressions/ }).to be_truthy\n        end\n      end\n\n      context 'that originates from a hash' do\n        let (:mapped_paths) { '[mapped.hash_var, var, \"paths/%{var.0}.%{var.1}.yaml\"]' }\n\n        let(:env_data) do\n          {\n            'paths' => {\n              'x.a.yaml' => <<-YAML.unindent,\n                path_xa: value path_xa\n                path_m:\n                  a: value path_m.a\n                  c: value path_m.c\n                YAML\n              'y.b.yaml' => <<-YAML.unindent\n                path_m:\n                  b: value path_m.b\n                  d: value path_m.d\n                YAML\n            },\n            'global_var.yaml' => <<-YAML.unindent\n              path_m:\n                e: value path_m.e\n              YAML\n          }\n        end\n\n        it 'finds environment data using mapped_paths' do\n          expect(lookup('path_xa')).to eql('value path_xa')\n          expect(warnings).to be_empty\n        end\n\n        it 'includes mapped path in explain output' do\n          explanation = explain('path_h', 'merge' => 'deep')\n          ['x\\.a', 'y\\.b', 'z\\.c'].each do |var|\n            expect(explanation).to match(/^\\s+Path \"#{env_dir}\\/spec\\/data\\/paths\\/#{var}\\.yaml\"\\n\\s+Original path: \"paths\\/%\\{var\\.0\\}\\.%\\{var\\.1\\}\\.yaml\"/)\n          end\n          expect(warnings).to be_empty\n        end\n\n        it 'performs merges between mapped paths' do\n          expect(lookup('path_m', 'merge' => 'hash')).to eql(\n            {\n              'a' => 'value path_m.a',\n              'b' => 'value path_m.b',\n              'c' => 'value path_m.c',\n              'd' => 'value path_m.d',\n              'e' => 'value path_m.e'\n            })\n          expect(warnings).to be_empty\n        end\n      end\n\n      context 'that originates from a string' do\n        let (:mapped_paths) { '[mapped.string_var, var, \"paths/%{var}.yaml\"]' }\n\n        let(:env_data) do\n          {\n            'paths' => {\n              's.yaml' => <<-YAML.unindent,\n                path_s: value path_s\n                YAML\n            }\n          }\n        end\n\n        it 'includes mapped path in explain output' do\n          expect(explain('path_s')).to match(/^\\s+Path \"#{env_dir}\\/spec\\/data\\/paths\\/s\\.yaml\"\\n\\s+Original path: \"paths\\/%\\{var\\}\\.yaml\"/)\n          expect(warnings).to be_empty\n        end\n\n        it 'finds environment data using mapped_paths' do\n          expect(lookup('path_s')).to eql('value path_s')\n          expect(warnings).to be_empty\n        end\n      end\n\n      context 'where the enty does not exist' do\n        let (:mapped_paths) { '[mapped.nosuch_var, var, \"paths/%{var}.yaml\"]' }\n\n        it 'finds environment data using mapped_paths' do\n          expect(explain('hello')).to match(/No such key: \"hello\"/)\n          expect(warnings).to be_empty\n        end\n      end\n    end\n\n    context 'and an environment Hiera v3 configuration' do\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n          ---\n          :backends: yaml\n          :yaml:\n            :datadir:  #{env_dir}/#{env_name}/hieradata\n          YAML\n      end\n\n      let(:environment_files) do\n        {\n          env_name => {\n            'hiera.yaml' => env_hiera_yaml,\n            'hieradata' => {\n              'common.yaml' => <<-YAML.unindent,\n                g: Value g\n                YAML\n            }\n          }\n        }\n      end\n\n      it 'will raise an error if --strict is set to error' do\n        Puppet[:strict] = :error\n        expect { lookup('g') }.to raise_error(Puppet::Error, /hiera.yaml version 3 cannot be used in an environment/)\n      end\n\n      it 'will log a warning and ignore the file if --strict is set to warning' do\n        Puppet[:strict] = :warning\n        expect { lookup('g') }.to raise_error(Puppet::Error, /did not find a value for the name 'g'/)\n      end\n\n      it 'will not log a warning and ignore the file if --strict is set to off' do\n        Puppet[:strict] = :off\n        expect { lookup('g') }.to raise_error(Puppet::Error, /did not find a value for the name 'g'/)\n        expect(warnings).to include(/hiera.yaml version 3 found at the environment root was ignored/)\n      end\n\n      it 'will use the configuration if appointed by global setting but still warn when encountered by environment data provider' do\n        Puppet[:strict] = :warning\n        Puppet.settings[:hiera_config] = File.join(env_dir, env_name, 'hiera.yaml')\n        expect(lookup('g')).to eql('Value g')\n        expect(warnings).to include(/hiera.yaml version 3 found at the environment root was ignored/)\n      end\n    end\n\n    context 'and a global empty Hiera configuration' do\n      let(:hiera_yaml_path) { File.join(code_dir, 'hiera.yaml') }\n      let(:code_dir_files) do\n        {\n          'hiera.yaml' => '',\n        }\n      end\n\n      let(:environment_files) do\n        {\n          env_name => {\n            'hieradata' => {\n              'common.yaml' =>  <<-YAML.unindent,\n                x: value x (from environment)\n                YAML\n            }\n          }\n        }\n      end\n\n      before(:each) do\n        # Need to set here since spec_helper defines these settings in its \"before each\"\n        Puppet.settings[:hiera_config] = hiera_yaml_path\n      end\n\n      it 'uses a Hiera version 3 defaults' do\n        expect(lookup('x')).to eql('value x (from environment)')\n      end\n\n      context 'obtained using /dev/null', :unless => Puppet::Util::Platform.windows? do\n        let(:code_dir_files) { {} }\n\n        it 'uses a Hiera version 3 defaults' do\n          Puppet[:hiera_config] = '/dev/null'\n          expect(lookup('x')).to eql('value x (from environment)')\n        end\n      end\n    end\n\n    context 'and a global configuration' do\n      let(:hiera_yaml) do\n        <<-YAML.unindent\n        ---\n        :backends:\n          - yaml\n          - json\n          - custom\n          - hocon\n        :yaml:\n          :datadir: #{code_dir}/hieradata\n        :json:\n          :datadir: #{code_dir}/hieradata\n        :hocon:\n          :datadir: #{code_dir}/hieradata\n        :hierarchy:\n          - common\n          - \"%{domain}\"\n        :merge_behavior: deeper\n        YAML\n      end\n\n      let(:code_dir_files) do\n        {\n          'hiera.yaml' => hiera_yaml,\n          'hieradata' => {\n            'common.yaml' =>  <<-YAML.unindent,\n              a: value a (from global)\n              hash_b:\n                hash_ba:\n                  bab: value hash_b.hash_ba.bab (from global)\n              hash_c:\n                hash_ca:\n                  cab: value hash_c.hash_ca.cab (from global)\n              ipl_hiera_env: \"environment value '%{hiera('mod_a::hash_a.a')}'\"\n              ipl_hiera_mod: \"module value '%{hiera('mod_a::abc')}'\"\n              ipl_hiera_modc: \"module value '%{hiera('mod_a::caller')}'\"\n              YAML\n            'example.com.yaml' =>  <<-YAML.unindent,\n              x: value x (from global example.com.yaml)\n              YAML\n            'common.json' =>  <<-JSON.unindent,\n              {\n                \"hash_b\": {\n                  \"hash_ba\": {\n                    \"bac\": \"value hash_b.hash_ba.bac (from global json)\"\n                  }\n                },\n                \"hash_c\": {\n                  \"hash_ca\": {\n                    \"cac\": \"value hash_c.hash_ca.cac (from global json)\"\n                  }\n                }\n              }\n              JSON\n            'common.conf' =>  <<-HOCON.unindent,\n              // The 'xs' is a value used for testing\n              xs = { subkey = value xs.subkey (from global hocon) }\n              HOCON\n          }\n        }\n      end\n\n      before(:all) do\n        $LOAD_PATH.unshift(my_fixture_dir)\n      end\n\n      after(:all) do\n        if Kernel.const_defined?(:Hiera) && Hiera.const_defined?(:Backend)\n          Hiera::Backend.send(:remove_const, :Custom_backend) if Hiera::Backend.const_defined?(:Custom_backend)\n          Hiera::Backend.send(:remove_const, :Other_backend) if Hiera::Backend.const_defined?(:Other_backend)\n        end\n        $LOAD_PATH.shift\n      end\n\n      before(:each) do\n        # Need to set here since spec_helper defines these settings in its \"before each\"\n        Puppet.settings[:codedir] = populated_code_dir\n        Puppet.settings[:hiera_config] = File.join(code_dir, 'hiera.yaml')\n      end\n\n      around(:each) do |example|\n        Puppet.override(:environments => environments, :current_environment => env) do\n          example.run\n        end\n      end\n\n      context 'version 3', :if => Puppet.features.hiera? do\n        it 'finds data in in global layer and reports deprecation warnings for hiera.yaml' do\n          expect(lookup('a')).to eql('value a (from global)')\n          expect(warnings).to include(/Use of 'hiera.yaml' version 3 is deprecated. It should be converted to version 5/)\n        end\n\n        it 'explain contains output from global layer' do\n          explanation = explain('a')\n          expect(explanation).to include('Global Data Provider (hiera configuration version 3)')\n          expect(explanation).to include('Hierarchy entry \"yaml\"')\n          expect(explanation).to include('Hierarchy entry \"json\"')\n          expect(explanation).to include('Found key: \"a\" value: \"value a (from global)\"')\n        end\n\n        it 'ignores merge behavior specified in global hiera.yaml' do\n          expect(lookup('hash_b')).to eql(\n            { 'hash_ba' => { 'bab' => 'value hash_b.hash_ba.bab (from global)'} })\n        end\n\n        it 'uses the merge from lookup options to merge all layers' do\n          expect(lookup('hash_c')).to eql(\n            { 'hash_ca' => { 'cab' => 'value hash_c.hash_ca.cab (from global)' } })\n        end\n\n        it 'uses the explicitly given merge to override lookup options and to merge all layers' do\n          expect(lookup('hash_c', 'merge' => 'deep')).to eql(\n            {\n              'hash_ca' =>\n              {\n                'caa' => 'value hash_c.hash_ca.caa (from environment)',\n                'cab' => 'value hash_c.hash_ca.cab (from global)',\n                'cac' => 'value hash_c.hash_ca.cac (from global json)',\n                'cad' => 'value hash_c.hash_ca.cad (from global custom)'\n              }\n            })\n        end\n\n        it 'paths are interpolated' do\n          expect(lookup('x')).to eql('value x (from global example.com.yaml)')\n        end\n\n        it 'backend data sources are propagated to custom backend' do\n          expect(lookup('datasources')).to eql(['common', 'example.com'])\n        end\n\n        it 'backend specific options are propagated to custom backend' do\n          expect(lookup('other_option')).to eql('value of other_option')\n        end\n\n        it 'dotted keys are passed down to custom backend' do\n          expect(lookup('dotted.key')).to eql('custom backend received request for dotted.key value')\n        end\n        \n        it 'delegates configured hocon backend to hocon_data function' do\n          expect(explain('xs')).to match(/Hierarchy entry \"hocon\"\\n.*\\n.*\\n.*\"common\"\\n\\s*Found key: \"xs\"/m)\n        end\n\n        it 'can dig down into subkeys provided by hocon_data function' do\n          expect(lookup('xs.subkey')).to eql('value xs.subkey (from global hocon)')\n        end\n\n        it 'multiple hiera3_backend declarations can be used and are merged into the generated config' do\n          expect(lookup(['datasources', 'other_option'])).to eql([['common', 'example.com'], 'value of other_option'])\n          expect(Hiera::Config.instance_variable_get(:@config)).to eql(\n            {\n              :backends => ['custom', 'other'],\n              :hierarchy => ['common', '%{domain}'],\n              :custom => { :datadir => \"#{code_dir}/hieradata\" },\n              :other => { :other_option => 'value of other_option', :datadir=>\"#{code_dir}/hieradata\" },\n              :logger => 'puppet'\n            })\n        end\n\n        context 'with a module data provider' do\n          let(:module_files) do\n            {\n              'mod_a' => {\n                'hiera.yaml' => <<-YAML.unindent,\n                  version: 5\n                  hierarchy:\n                    - name: Common\n                      path: common.yaml\n                  YAML\n                'data' => {\n                  'common.yaml' =>  <<-YAML.unindent\n                    mod_a::abc: value mod_a::abc (from module)\n                    mod_a::caller: \"calling module is %{calling_module}\"\n                  YAML\n                }\n              }\n            }\n          end\n\n          let(:environment_files) do\n            {\n              env_name => {\n                'hiera.yaml' => env_hiera_yaml,\n                'data' => env_data,\n                'modules' => module_files\n              }\n            }\n          end\n\n          it \"interpolation function 'hiera' finds values in environment\" do\n            expect(lookup('ipl_hiera_env')).to eql(\"environment value 'value mod_a::hash_a.a (from environment)'\")\n          end\n\n          it \"interpolation function 'hiera' finds values in module\" do\n            expect(lookup('ipl_hiera_mod')).to eql(\"module value 'value mod_a::abc (from module)'\")\n          end\n\n          it \"interpolation function 'hiera' finds values in module and that module does not find %{calling_module}\" do\n            expect(lookup('ipl_hiera_modc')).to eql(\"module value 'calling module is '\")\n          end\n\n          context 'but no environment data provider' do\n            let(:environment_files) do\n              {\n                env_name => {\n                  'modules' => module_files\n                }\n              }\n            end\n\n            it \"interpolation function 'hiera' does not find values in a module\" do\n              expect(lookup('ipl_hiera_mod')).to eql(\"module value ''\")\n            end\n          end\n        end\n\n\n        context 'using an eyaml backend' do\n          let(:private_key_name) { 'private_key.pkcs7.pem' }\n          let(:public_key_name) { 'public_key.pkcs7.pem' }\n\n          let(:private_key) do\n            <<-PKCS7.unindent\n              -----BEGIN RSA PRIVATE KEY-----\n              MIIEogIBAAKCAQEAwHB3GvImq59em4LV9DMfP0Zjs21eW3Jd5I9fuY0jLJhIkH6f\n              CR7tyOpYV6xUj+TF8giq9WLxZI7sourMJMWjEWhVjgUr5lqp1RLv4lwfDv3Wk4XC\n              2LUuqj1IAErUXKeRz8i3lUSZW1Pf4CaMpnIiPdWbz6f0KkaJSFi9bqexONBx4fKQ\n              NlgZwm2/aYjjrYng788I0QhWDKUqsQOi5mZKlHNRsDlk7J3Afhsx/jTLrCX/G8+2\n              tPtLsHyRN39kluM5vYHbKXDsCG/a88Z2yUE2+r4Clp0FUKffiEDBPm0/H0sQ4Q1o\n              EfQFDQRKaIkhpsm0nOnLYTy3/xJc5uqDNkLiawIDAQABAoIBAE98pNXOe8ab93oI\n              mtNZYmjCbGAqprTjEoFb71A3SfYbmK2Gf65GxjUdBwx/tBYTiuekSOk+yzKcDoZk\n              sZnmwKpqDByzaiSmAkxunANFxdZtZvpcX9UfUX0j/t+QCROUa5gF8j6HrUiZ5nkx\n              sxr1PcuItekaGLJ1nDLz5JsWTQ+H4M+GXQw7/t96x8v8g9el4exTiAHGk6Fv16kD\n              017T02M9qTTmV3Ab/enDIBmKVD42Ta36K/wc4l1aoUQNiRbIGVh96Cgd1CFXLF3x\n              CsaNbYT4SmRXaYqoj6MKq+QFEGxadFmJy48NoSd4joirIn2lUjHxJebw3lLbNLDR\n              uvQnQ2ECgYEA/nD94wEMr6078uMv6nKxPpNGq7fihwSKf0G/PQDqrRmjUCewuW+k\n              /iXMe1Y/y0PjFeNlSbUsUvKQ5xF7F/1AnpuPHIrn3cjGVLb71W+zen1m8SnhsW/f\n              7dPgtcb4SCvfhmLgoov+P34YcNfGi6qgPUu6319IqoB3BIi7PvfEomkCgYEAwZ4+\n              V0bMjFdDn2hnYzjTNcF2aUQ1jPvtuETizGwyCbbMLl9522lrjC2DrH41vvqX35ct\n              CBJkhQFbtHM8Gnmozv0vxhI2jP+u14mzfePZsaXuYrEgWRj+BCsYUHodXryxnEWj\n              yVrTNskab1B5jFm2SCJDmKcycBOYpRBLCMx6W7MCgYBA99z7/6KboOIzzKrJdGup\n              jLV410UyMIikoccQ7pD9jhRTPS80yjsY4dHqlEVJw5XSWvPb9DTTITi6p44EvBep\n              6BKMuTMnQELUEr0O7KypVCfa4FTOl8BX28f+4kU3OGykxc6R8qkC0VGwTohV1UWB\n              ITsgGhZV4uOA9uDI3T8KMQKBgEnQY2HwmuDSD/TA39GDA3qV8+ez2lqSXRGIKZLX\n              mMf9SaBQQ+uzKA4799wWDbVuYeIbB07xfCL83pJP8FUDlqi6+7Celu9wNp7zX1ua\n              Nw8z/ErhzjxJe+Xo7A8aTwIkG+5A2m1UU/up9YsEeiJYvVaIwY58B42U2vfq20BS\n              fD9jAoGAX2MscBzIsmN+U9R0ptL4SXcPiVnOl8mqvQWr1B4OLgxX7ghht5Fs956W\n              bHipxOWMFCPJA/AhNB8q1DvYiD1viZbIALSCJVUkzs4AEFIjiPsCBKxerl7jF6Xp\n              1WYSaCmfvoCVEpFNt8cKp4Gq+zEBYAV4Q6TkcD2lDtEW49MuN8A=\n              -----END RSA PRIVATE KEY-----\n              PKCS7\n          end\n\n          let(:public_key) do\n            <<-PKCS7.unindent\n              -----BEGIN CERTIFICATE-----\n              MIIC2TCCAcGgAwIBAgIBATANBgkqhkiG9w0BAQUFADAAMCAXDTE3MDExMzA5MTY1\n              MloYDzIwNjcwMTAxMDkxNjUyWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n              CgKCAQEAwHB3GvImq59em4LV9DMfP0Zjs21eW3Jd5I9fuY0jLJhIkH6fCR7tyOpY\n              V6xUj+TF8giq9WLxZI7sourMJMWjEWhVjgUr5lqp1RLv4lwfDv3Wk4XC2LUuqj1I\n              AErUXKeRz8i3lUSZW1Pf4CaMpnIiPdWbz6f0KkaJSFi9bqexONBx4fKQNlgZwm2/\n              aYjjrYng788I0QhWDKUqsQOi5mZKlHNRsDlk7J3Afhsx/jTLrCX/G8+2tPtLsHyR\n              N39kluM5vYHbKXDsCG/a88Z2yUE2+r4Clp0FUKffiEDBPm0/H0sQ4Q1oEfQFDQRK\n              aIkhpsm0nOnLYTy3/xJc5uqDNkLiawIDAQABo1wwWjAPBgNVHRMBAf8EBTADAQH/\n              MB0GA1UdDgQWBBSejWrVnw7QaBjNFCHMNFi+doSOcTAoBgNVHSMEITAfgBSejWrV\n              nw7QaBjNFCHMNFi+doSOcaEEpAIwAIIBATANBgkqhkiG9w0BAQUFAAOCAQEAAe85\n              BQ1ydAHFqo0ib38VRPOwf5xPHGbYGhvQi4/sU6aTuR7pxaOJPYz05jLhS+utEmy1\n              sknBq60G67yhQE7IHcfwrl1arirG2WmKGvAbjeYL2K1UiU0pVD3D+Klkv/pK6jIQ\n              eOJRGb3qNUn0Sq9EoYIOXiGXQ641F0bZZ0+5H92kT1lmnF5oLfCb84ImD9T3snH6\n              pIr5RKRx/0YmJIcv3WdpoPT903rOJiRIEgIj/hDk9QZTBpm222Ul5yQQ5pBywpSp\n              xh0bmJKAQWhQm7QlybKfyaQmg5ot1jEzWAvD2I5FjHQxmAlchjb6RreaRhExj+JE\n              5O117dMBdzDBjcNMOA==\n              -----END CERTIFICATE-----\n              PKCS7\n          end\n\n          let(:keys_dir) do\n            keys = tmpdir('keys')\n            dir_contained_in(keys, {\n              private_key_name => private_key,\n              public_key_name => public_key\n            })\n            keys\n          end\n\n          let(:private_key_path) { File.join(keys_dir, private_key_name) }\n          let(:public_key_path) { File.join(keys_dir, public_key_name) }\n          let(:hiera_yaml) do\n            <<-YAML.unindent\n            :backends:\n              - eyaml\n              - yaml\n            :eyaml:\n              :datadir: #{code_dir}/hieradata\n              :pkcs7_private_key: #{private_key_path}\n              :pkcs7_public_key: #{public_key_path}\n            :yaml:\n              :datadir: #{code_dir}/hieradata\n            :hierarchy:\n              - common\n           YAML\n          end\n\n          let(:data_files) do\n            {\n              'common.yaml' => <<-YAML.unindent,\n                b: value 'b' (from global)\n                c:\n                  c_a: value c_a (from global)\n                YAML\n              'common.eyaml' => <<-YAML.unindent\n                a: >\n                  ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEw\n                  DQYJKoZIhvcNAQEBBQAEggEAH457bsfL8kYw9O50roE3dcE21nCnmPnQ2XSX\n                  LYRJ2C78LarbfFonKz0gvDW7tyhsLWASFCFaiU8T1QPBd2b3hoQK8E4B2Ual\n                  xga/K7r9y3OSgRomTm9tpTltC6re0Ubh3Dy71H61obwxEdNVTqjPe95+m2b8\n                  6zWZVnzZzXXsTG1S17yJn1zaB/LXHbWNy4KyLLKCGAml+Gfl6ZMjmaplTmUA\n                  QIC5rI8abzbPP3TDMmbLOGNkrmLqI+3uS8tSueTMoJmWaMF6c+H/cA7oRxmV\n                  QCeEUVXjyFvCHcmbA+keS/RK9XF+vc07/XS4XkYSPs/I5hLQji1y9bkkGAs0\n                  tehxQjBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDHpA6Fcl/R16aIYcow\n                  oiO4gDAvfFH6jLUwXkcYtagnwdmhkd9TQJtxNWcIwMpvmk036MqIoGwwhQdg\n                  gV4beiCFtLU=]\n                a_ref: \"A reference to %{hiera('a')}\"\n                b_ref: \"A reference to %{hiera('b')}\"\n                c_ref: \"%{alias('c')}\"\n                :symbol: \"A symbol\"\n                YAML\n            }\n          end\n\n          let(:code_dir_files) do\n            {\n              'hiera.yaml' => hiera_yaml,\n              'hieradata' => data_files\n            }\n          end\n\n          before(:each) do\n            Puppet.settings[:hiera_config] = File.join(code_dir, 'hiera.yaml')\n          end\n\n          it 'finds data in the global layer' do\n            expect(lookup('a')).to eql(\"Encrypted value 'a' (from global)\")\n          end\n\n          it 'can use a hiera interpolation' do\n            expect(lookup('a_ref')).to eql(\"A reference to Encrypted value 'a' (from global)\")\n          end\n\n          it 'can use a hiera interpolation that refers back to yaml' do\n            expect(lookup('b_ref')).to eql(\"A reference to value 'b' (from global)\")\n          end\n\n          it 'can use a hiera interpolation that refers back to yaml, but only in global layer' do\n            expect(lookup(['c', 'c_ref'], 'merge' => 'deep')).to eql([{'c_a' => 'value c_a (from global)', 'c_b' => 'value c_b (from environment)'}, { 'c_a' => 'value c_a (from global)' }])\n          end\n\n          it 'delegates configured eyaml backend to eyaml_lookup_key function' do\n            expect(explain('a')).to match(/Hierarchy entry \"eyaml\"\\n.*\\n.*\\n.*\"common\"\\n\\s*Found key: \"a\"/m)\n          end\n        end\n\n        context 'using deep_merge_options' do\n          let(:hiera_yaml) do\n            <<-YAML.unindent\n              ---\n              :backends:\n                - yaml\n              :yaml:\n                :datadir: #{code_dir}/hieradata\n              :hierarchy:\n                - common\n                - other\n              :merge_behavior: deeper\n              :deep_merge_options:\n                :unpack_arrays: ','\n              YAML\n          end\n\n          let(:code_dir_files) do\n            {\n              'hiera.yaml' => hiera_yaml,\n              'hieradata' => {\n                'common.yaml' => <<-YAML.unindent,\n                  hash:\n                    array:\n                      - x1,x2\n                  array:\n                    - x1,x2\n                  str: a string\n                  mixed:\n                    x: hx\n                    y: hy\n                  YAML\n                'other.yaml' => <<-YAML.unindent,\n                  hash:\n                    array:\n                      - x3\n                      - x4\n                  array:\n                    - x3\n                    - x4\n                  str: another string\n                  mixed:\n                    - h1\n                    - h2\n                  YAML\n              }\n            }\n          end\n\n          it 'ignores configured merge_behavior when looking up arrays' do\n            expect(lookup('array')).to eql(['x1,x2'])\n          end\n\n          it 'ignores configured merge_behavior when merging arrays' do\n            expect(lookup('array', 'merge' => 'unique')).to eql(['x1,x2', 'x3', 'x4'])\n          end\n\n          it 'ignores configured merge_behavior when looking up hashes' do\n            expect(lookup('hash')).to eql({'array' => ['x1,x2']})\n          end\n\n          it 'ignores configured merge_behavior when merging hashes' do\n            expect(lookup('hash', 'merge' => 'hash')).to eql({'array' => ['x1,x2']})\n          end\n        end\n\n        context 'using relative datadir paths' do\n          let(:hiera_yaml) do\n            <<-YAML.unindent\n          ---\n          :backends:\n            - yaml\n          :yaml:\n            :datadir: relative_data\n          :hierarchy:\n            - common\n            YAML\n          end\n\n          let(:populated_code_dir) do\n            dir_contained_in(code_dir, code_dir_files.merge({\n              'fake_cwd' => {\n                'relative_data' => {\n                  'common.yaml' => <<-YAML.unindent\n                    a: value a (from fake_cwd/relative_data/common.yaml)\n                  YAML\n                }\n              }\n            }))\n            code_dir\n          end\n\n          around(:each) do |example|\n            cwd = Dir.pwd\n            Dir.chdir(File.join(code_dir, 'fake_cwd'))\n            begin\n              example.run\n            ensure\n              Dir.chdir(cwd)\n            end\n          end\n\n          it 'finds data from data file beneath relative datadir' do\n            expect(lookup('a')).to eql('value a (from fake_cwd/relative_data/common.yaml)')\n          end\n        end\n      end\n\n      context 'version 5' do\n        let(:scope_additions) { { 'ipl_datadir' => 'hieradata' } }\n        let(:hiera_yaml) do\n          <<-YAML.unindent\n          ---\n          version: 5\n          defaults:\n            datadir: \"%{ipl_datadir}\"\n\n          hierarchy:\n            - name: Yaml\n              data_hash: yaml_data\n              paths:\n                - common.yaml\n                - \"%{domain}.yaml\"\n            - name: Json\n              data_hash: json_data\n              paths:\n                - common.json\n                - \"%{domain}.json\"\n            - name: Hocon\n              data_hash: hocon_data\n              paths:\n                - common.conf\n                - \"%{domain}.conf\"\n              YAML\n        end\n\n        it 'finds global data and reports no deprecation warnings' do\n          expect(lookup('a')).to eql('value a (from global)')\n          expect(warnings).to be_empty\n        end\n\n        it 'explain contains output from global layer' do\n          explanation = explain('a')\n          expect(explanation).to include('Global Data Provider (hiera configuration version 5)')\n          expect(explanation).to include('Hierarchy entry \"Yaml\"')\n          expect(explanation).to include('Hierarchy entry \"Json\"')\n          expect(explanation).to include('Hierarchy entry \"Hocon\"')\n          expect(explanation).to include('Found key: \"a\" value: \"value a (from global)\"')\n        end\n\n        it 'uses the explicitly given merge to override lookup options and to merge all layers' do\n          expect(lookup('hash_c', 'merge' => 'deep')).to eql(\n            {\n              'hash_ca' =>\n                {\n                  'caa' => 'value hash_c.hash_ca.caa (from environment)',\n                  'cab' => 'value hash_c.hash_ca.cab (from global)',\n                  'cac' => 'value hash_c.hash_ca.cac (from global json)',\n                }\n            })\n        end\n\n        it 'provides a sensible error message when the hocon library is not loaded' do\n          allow(Puppet.features).to receive(:hocon?).and_return(false)\n\n          expect { lookup('a') }.to raise_error do |e|\n            expect(e.message).to match(/Lookup using Hocon data_hash function is not supported without hocon library/)\n          end\n        end\n\n        context 'with missing path declaraion' do\n          context 'and yaml_data function' do\n            let(:hiera_yaml) { <<-YAML.unindent }\n              version: 5\n              hierarchy:\n                - name: Yaml\n                  data_hash: yaml_data\n              YAML\n\n            it 'fails and reports the missing path' do\n              expect { lookup('a') }.to raise_error(/one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function/)\n            end\n          end\n\n          context 'and json_data function' do\n            let(:hiera_yaml) { <<-YAML.unindent }\n              version: 5\n              hierarchy:\n                - name: Json\n                  data_hash: json_data\n              YAML\n\n            it 'fails and reports the missing path' do\n              expect { lookup('a') }.to raise_error(/one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function/)\n            end\n          end\n\n          context 'and hocon_data function' do\n            let(:hiera_yaml) { <<-YAML.unindent }\n              version: 5\n              hierarchy:\n                - name: Hocon\n                  data_hash: hocon_data\n              YAML\n\n            it 'fails and reports the missing path' do\n              expect { lookup('a') }.to raise_error(/one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function/)\n            end\n          end\n\n          context 'and eyaml_lookup_key function', :if => Puppet.features.hiera_eyaml? do\n            let(:hiera_yaml) { <<-YAML.unindent }\n              version: 5\n              hierarchy:\n                - name: Yaml\n                  lookup_key: eyaml_lookup_key\n              YAML\n\n            it 'fails and reports the missing path' do\n              expect { lookup('a') }.to raise_error(/one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this lookup_key function/)\n            end\n          end\n        end\n      end\n\n      context 'with a hiera3_backend that has no paths', :if => Puppet.features.hiera? do\n        let(:hiera_yaml) do\n          <<-YAML.unindent\n          ---\n          version: 5\n          hierarchy:\n            - name: Custom\n              hiera3_backend: custom\n          YAML\n        end\n\n        it 'calls the backend' do\n          expect(lookup('hash_c')).to eql(\n            { 'hash_ca' => { 'cad' => 'value hash_c.hash_ca.cad (from global custom)' }})\n        end\n      end\n    end\n\n    context 'and a module' do\n      let(:mod_a_files) { {} }\n\n      let(:populated_env_dir) do\n        dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files }))\n        env_dir\n      end\n\n      context 'that has no lookup configured' do\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'data' => {\n                'common.yaml' => <<-YAML.unindent\n                ---\n                mod_a::b: value mod_a::b (from mod_a)\n              YAML\n              }\n            }\n          }\n        end\n\n        it 'does not find data in the module' do\n          expect { lookup('mod_a::b') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'mod_a::b'/)\n        end\n\n        context 'with a Hiera v3 configuration', :if => Puppet.features.hiera?  do\n          let(:mod_a_files) do\n            {\n              'mod_a' => {\n                'hiera.yaml' => <<-YAML.unindent\n                  ---\n                  :backends: yaml\n                  YAML\n              }\n            }\n          end\n\n          it 'raises a warning' do\n            expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')\n            expect(warnings).to include(/hiera.yaml version 3 found at module root was ignored/)\n          end\n        end\n\n        context \"but a metadata.json with 'module_data_provider=hiera'\" do\n          let(:mod_a_files_1) { DeepMerge.deep_merge!(mod_a_files, 'mod_a' => { 'metadata.json' => <<-JSON.unindent }) }\n              {\n                  \"name\": \"example/mod_a\",\n                  \"version\": \"0.0.2\",\n                  \"source\": \"git@github.com/example/mod_a.git\",\n                  \"dependencies\": [],\n                  \"author\": \"Bob the Builder\",\n                  \"license\": \"Apache-2.0\",\n                  \"data_provider\": \"hiera\"\n              }\n              JSON\n\n          let(:populated_env_dir) do\n            dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files_1 }))\n            env_dir\n          end\n\n          it 'finds data in the module and reports deprecation warning for metadata.json' do\n            expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')\n            expect(warnings).to include(/Defining \"data_provider\": \"hiera\" in metadata.json is deprecated. A 'hiera.yaml' file should be used instead/)\n          end\n\n          context 'and a hiera.yaml file' do\n            let(:mod_a_files_2) { DeepMerge.deep_merge!(mod_a_files_1, 'mod_a' => { 'hiera.yaml' => <<-YAML.unindent }) }\n            ---\n            version: 4\n            hierarchy:\n              - name: common\n                backend: yaml\n            YAML\n\n            let(:populated_env_dir) do\n              dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files_2 }))\n              env_dir\n            end\n\n            it 'finds data in the module and reports deprecation warnings for both metadata.json and hiera.yaml' do\n              expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')\n              expect(warnings).to include(/Defining \"data_provider\": \"hiera\" in metadata.json is deprecated/)\n              expect(warnings).to include(/Use of 'hiera.yaml' version 4 is deprecated. It should be converted to version 5/)\n            end\n          end\n        end\n      end\n\n      context 'using deep merge and module values that aliases environment values' do\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'data' => {\n                'common.yaml' => <<-YAML.unindent,\n                  ---\n                  mod_a::hash:\n                    b: value b (from module)\n                  lookup_options:\n                    mod_a::hash:\n                      merge: deep\n                  YAML\n              },\n              'hiera.yaml' => <<-YAML.unindent,\n                ---\n                version: 5\n                hierarchy:\n                  - name: \"Common\"\n                    path: \"common.yaml\"\n                  - name: \"Other\"\n                    path: \"other.yaml\"\n                YAML\n            }\n          }\n        end\n        let(:env_data) do\n          {\n            'common.yaml' => <<-YAML.unindent\n              a: value a (from environment)\n              mod_a::hash:\n                a: value mod_a::hash.a (from environment)\n                c: '%{alias(\"a\")}'\n              YAML\n          }\n        end\n\n        it 'continues with module lookup after alias is resolved in environment' do\n          expect(lookup('mod_a::hash')).to eql(\n            {\n              'a' => 'value mod_a::hash.a (from environment)',\n              'b' => 'value b (from module)',\n              'c' => 'value a (from environment)'\n            })\n        end\n      end\n\n      context 'using a data_hash that reads a yaml file' do\n        let(:defaults) {\n          {\n            'mod_a::xd' => 'value mod_a::xd (from default)',\n            'mod_a::xd_found' => 'value mod_a::xd_found (from default)',\n            'scope_xd' => 'value scope_xd (from default)'\n          }}\n        let(:overrides) {\n          {\n            'mod_a::xo' => 'value mod_a::xo (from override)',\n            'scope_xo' => 'value scope_xo (from override)'\n          }}\n\n        let(:scope_additions) do\n          {\n            'scope_scalar' => 'scope scalar value',\n            'scope_hash' => { 'a' => 'scope hash a', 'b' => 'scope hash b' }\n          }\n        end\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'data' => {\n                'common.yaml' => <<-YAML.unindent\n                ---\n                mod_a::a: value mod_a::a (from mod_a)\n                mod_a::b: value mod_a::b (from mod_a)\n                mod_a::xo: value mod_a::xo (from mod_a)\n                mod_a::xd_found: value mod_a::xd_found (from mod_a)\n                mod_a::interpolate_xo: \"-- %{lookup('mod_a::xo')} --\"\n                mod_a::interpolate_xd: \"-- %{lookup('mod_a::xd')} --\"\n                mod_a::interpolate_scope_xo: \"-- %{scope_xo} --\"\n                mod_a::interpolate_scope_xd: \"-- %{scope_xd} --\"\n                mod_a::hash_a:\n                  a: value mod_a::hash_a.a (from mod_a)\n                  b: value mod_a::hash_a.b (from mod_a)\n                mod_a::hash_b:\n                  a: value mod_a::hash_b.a (from mod_a)\n                  b: value mod_a::hash_b.b (from mod_a)\n                mod_a::interpolated: \"-- %{lookup('mod_a::a')} --\"\n                mod_a::a_a: \"-- %{lookup('mod_a::hash_a.a')} --\"\n                mod_a::a_b: \"-- %{lookup('mod_a::hash_a.b')} --\"\n                mod_a::b_a: \"-- %{lookup('mod_a::hash_b.a')} --\"\n                mod_a::b_b: \"-- %{lookup('mod_a::hash_b.b')} --\"\n                mod_a::interpolate_array:\n                  - \"-- %{lookup('mod_a::a')} --\"\n                  - \"-- %{lookup('mod_a::b')} --\"\n                mod_a::interpolate_literal: \"-- %{literal('hello')} --\"\n                mod_a::interpolate_scope: \"-- %{scope_scalar} --\"\n                mod_a::interpolate_scope_not_found: \"-- %{scope_nope} --\"\n                mod_a::interpolate_scope_dig: \"-- %{scope_hash.a} --\"\n                mod_a::interpolate_scope_dig_not_found: \"-- %{scope_hash.nope} --\"\n                mod_a::quoted_interpolation: '-- %{lookup(''\"mod_a::a.quoted.key\"'')} --'\n                \"mod_a::a.quoted.key\": \"value mod_a::a.quoted.key (from mod_a)\"\n              YAML\n              },\n              'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              hierarchy:\n                - name: \"Common\"\n                  data_hash: yaml_data\n                  path: \"common.yaml\"\n            YAML\n            }\n          }\n        end\n\n        it 'finds data in the module' do\n          expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')\n        end\n\n        it 'environment data has higher priority than module data' do\n          expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')\n        end\n\n        it 'environment data has higher priority than module data in interpolated module data' do\n          expect(lookup('mod_a::interpolated')).to eql('-- value mod_a::a (from environment) --')\n        end\n\n        it 'overrides have higher priority than found data' do\n          expect(lookup('mod_a::xo', { 'override' => overrides })).to eql('value mod_a::xo (from override)')\n        end\n\n        it 'overrides have higher priority than found data in lookup interpolations' do\n          expect(lookup('mod_a::interpolate_xo', { 'override' => overrides })).to eql('-- value mod_a::xo (from override) --')\n        end\n\n        it 'overrides have higher priority than found data in scope interpolations' do\n          expect(lookup('mod_a::interpolate_scope_xo', { 'override' => overrides })).to eql('-- value scope_xo (from override) --')\n        end\n\n        it 'defaults have lower priority than found data' do\n          expect(lookup('mod_a::xd_found', { 'default_values_hash' => defaults })).to eql('value mod_a::xd_found (from mod_a)')\n        end\n\n        it 'defaults are used when data is not found' do\n          expect(lookup('mod_a::xd', { 'default_values_hash' => defaults })).to eql('value mod_a::xd (from default)')\n        end\n\n        it 'defaults are used when data is not found in lookup interpolations' do\n          expect(lookup('mod_a::interpolate_xd', { 'default_values_hash' => defaults })).to eql('-- value mod_a::xd (from default) --')\n        end\n\n        it 'defaults are used when data is not found in scope interpolations' do\n          expect(lookup('mod_a::interpolate_scope_xd', { 'default_values_hash' => defaults })).to eql('-- value scope_xd (from default) --')\n        end\n\n        it 'merges hashes from environment and module unless strategy hash is used' do\n          expect(lookup('mod_a::hash_a')).to eql({'a' => 'value mod_a::hash_a.a (from environment)'})\n        end\n\n        it 'merges hashes from environment and module when merge strategy hash is used' do\n          expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql(\n            {'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)'})\n        end\n\n        it 'will not merge hashes from environment and module in interpolated expressions' do\n          expect(lookup(['mod_a::a_a', 'mod_a::a_b'])).to eql(\n            ['-- value mod_a::hash_a.a (from environment) --', '--  --']) # root key found in environment, no hash merge is performed\n        end\n\n        it 'interpolates arrays' do\n          expect(lookup('mod_a::interpolate_array')).to eql(['-- value mod_a::a (from environment) --', '-- value mod_a::b (from mod_a) --'])\n        end\n\n        it 'can dig into arrays using subkeys' do\n          expect(lookup('mod_a::interpolate_array.1')).to eql('-- value mod_a::b (from mod_a) --')\n        end\n\n        it 'treats an out of range subkey as not found' do\n          expect(explain('mod_a::interpolate_array.2')).to match(/No such key: \"2\"/)\n        end\n\n        it 'interpolates a literal' do\n          expect(lookup('mod_a::interpolate_literal')).to eql('-- hello --')\n        end\n\n        it 'interpolates scalar from scope' do\n          expect(lookup('mod_a::interpolate_scope')).to eql('-- scope scalar value --')\n        end\n\n        it 'raises an error when trying to interpolate not found in scope' do\n          expect { lookup('mod_a::interpolate_scope_not_found') \n          }.to raise_error(/Evaluation Error: Error while evaluating a Function Call, Undefined variable 'scope_nope';/)\n        end\n\n        it 'interpolates dotted key from scope' do\n          expect(lookup('mod_a::interpolate_scope_dig')).to eql('-- scope hash a --')\n        end\n\n        it 'treats interpolated dotted key but not found in scope as empty string' do\n          expect(lookup('mod_a::interpolate_scope_dig_not_found')).to eql('--  --')\n        end\n\n        it 'can use quoted keys in interpolation' do\n          expect(lookup('mod_a::quoted_interpolation')).to eql('-- value mod_a::a.quoted.key (from mod_a) --') # root key found in environment, no hash merge is performed\n        end\n\n        it 'merges hashes from environment and module in interpolated expressions if hash merge is specified in lookup options' do\n          expect(lookup(['mod_a::b_a', 'mod_a::b_b'])).to eql(\n            ['-- value mod_a::hash_b.a (from environment) --', '-- value mod_a::hash_b.b (from mod_a) --'])\n        end\n      end\n\n      context 'using a lookup_key that uses a path' do\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'functions' => {\n                'pp_lookup_key.pp' => <<-PUPPET.unindent\n                  function mod_a::pp_lookup_key($key, $options, $context) {\n                    if !$context.cache_has_key(undef) {\n                      $context.cache_all(yaml_data($options, $context))\n                      $context.cache(undef, true)\n                    }\n                    if $context.cache_has_key($key) { $context.cached_value($key) } else { $context.not_found }\n                  }\n                  PUPPET\n              },\n              'hiera.yaml' => <<-YAML.unindent,\n                ---\n                version: 5\n                hierarchy:\n                  - name: \"Common\"\n                    lookup_key: mod_a::pp_lookup_key\n                    path: common.yaml\n                YAML\n              'data' => {\n                'common.yaml' => <<-YAML.unindent\n                  mod_a::b: value mod_a::b (from mod_a)\n                  YAML\n              }\n            }\n          }\n        end\n\n        it 'finds data in the module' do\n          expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')\n        end\n      end\n\n      context 'using a lookup_key that is a puppet function' do\n        let(:puppet_function) { <<-PUPPET.unindent }\n          function mod_a::pp_lookup_key(Puppet::LookupKey $key, Hash[String,String] $options, Puppet::LookupContext $context) >> Puppet::LookupValue {\n            case $key {\n              'mod_a::really_interpolated': { $context.interpolate(\"-- %{lookup('mod_a::a')} --\") }\n              'mod_a::recursive': { lookup($key) }\n              default: {\n                if $context.cache_has_key(mod_a::a) {\n                  $context.explain || { 'reusing cache' }\n                } else {\n                  $context.explain || { 'initializing cache' }\n                  $context.cache_all({\n                    mod_a::a => 'value mod_a::a (from mod_a)',\n                    mod_a::b => 'value mod_a::b (from mod_a)',\n                    mod_a::c => 'value mod_a::c (from mod_a)',\n                    mod_a::hash_a => {\n                      a => 'value mod_a::hash_a.a (from mod_a)',\n                      b => 'value mod_a::hash_a.b (from mod_a)'\n                    },\n                    mod_a::hash_b => {\n                      a => 'value mod_a::hash_b.a (from mod_a)',\n                      b => 'value mod_a::hash_b.b (from mod_a)'\n                    },\n                    mod_a::interpolated => \"-- %{lookup('mod_a::a')} --\",\n                    mod_a::a_a => \"-- %{lookup('mod_a::hash_a.a')} --\",\n                    mod_a::a_b => \"-- %{lookup('mod_a::hash_a.b')} --\",\n                    mod_a::b_a => \"-- %{lookup('mod_a::hash_b.a')} --\",\n                    mod_a::b_b => \"-- %{lookup('mod_a::hash_b.b')} --\",\n                    'mod_a::a.quoted.key' => 'value mod_a::a.quoted.key (from mod_a)',\n                    mod_a::sensitive => Sensitive('reduct me please'),\n                    mod_a::type => Object[{name => 'FindMe', 'attributes' => {'x' => String}}],\n                    mod_a::version => SemVer('3.4.1'),\n                    mod_a::version_range => SemVerRange('>=3.4.1'),\n                    mod_a::timestamp => Timestamp(\"1994-03-25T19:30:00\"),\n                    mod_a::timespan => Timespan(\"3-10:00:00\")\n                  })\n                }\n                if !$context.cache_has_key($key) {\n                  $context.not_found\n                }\n                $context.explain || { \"returning value for $key\" }\n                $context.cached_value($key)\n              }\n            }\n          }\n        PUPPET\n\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'functions' => {\n                'pp_lookup_key.pp' => puppet_function\n              },\n              'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              hierarchy:\n                - name: \"Common\"\n                  lookup_key: mod_a::pp_lookup_key\n            YAML\n            }\n          }\n        end\n\n        it 'finds data in the module' do\n          expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')\n        end\n\n        it 'environment data has higher priority than module data' do\n          expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')\n        end\n\n        it 'finds quoted keys in the module' do\n          expect(lookup('\"mod_a::a.quoted.key\"')).to eql('value mod_a::a.quoted.key (from mod_a)')\n        end\n\n        it 'will not resolve interpolated expressions' do\n          expect(lookup('mod_a::interpolated')).to eql(\"-- %{lookup('mod_a::a')} --\")\n        end\n\n        it 'resolves interpolated expressions using Context#interpolate' do\n          expect(lookup('mod_a::really_interpolated')).to eql(\"-- value mod_a::a (from environment) --\")\n        end\n\n        it 'will not merge hashes from environment and module unless strategy hash is used' do\n          expect(lookup('mod_a::hash_a')).to eql({ 'a' => 'value mod_a::hash_a.a (from environment)' })\n        end\n\n        it 'merges hashes from environment and module when merge strategy hash is used' do\n          expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql({ 'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)' })\n        end\n\n        it 'traps recursive lookup trapped' do\n          expect(explain('mod_a::recursive')).to include('Recursive lookup detected')\n        end\n\n        it 'private cache is persisted over multiple calls' do\n          collect_notices(\"notice(lookup('mod_a::b')) notice(lookup('mod_a::c'))\", true)\n          expect(notices).to eql(['value mod_a::b (from mod_a)', 'value mod_a::c (from mod_a)'])\n          expect(explanation).to match(/initializing cache.*reusing cache/m)\n          expect(explanation).not_to match(/initializing cache.*initializing cache/m)\n        end\n\n        it 'the same key is requested only once' do\n          collect_notices(\"notice(lookup('mod_a::b')) notice(lookup('mod_a::b'))\", true)\n          expect(notices).to eql(['value mod_a::b (from mod_a)', 'value mod_a::b (from mod_a)'])\n          expect(explanation).to match(/Found key: \"mod_a::b\".*Found key: \"mod_a::b\"/m)\n          expect(explanation).to match(/returning value for mod_a::b/m)\n          expect(explanation).not_to match(/returning value for mod_a::b.*returning value for mod_a::b/m)\n        end\n\n        context 'and calling function via API' do\n          it 'finds and delivers rich data' do\n            collect_notices(\"notice('success')\") do |scope|\n              expect(lookup_func.call(scope, 'mod_a::sensitive')).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n              expect(lookup_func.call(scope, 'mod_a::type')).to be_a(Puppet::Pops::Types::PObjectType)\n              expect(lookup_func.call(scope, 'mod_a::version')).to eql(SemanticPuppet::Version.parse('3.4.1'))\n              expect(lookup_func.call(scope, 'mod_a::version_range')).to eql(SemanticPuppet::VersionRange.parse('>=3.4.1'))\n              expect(lookup_func.call(scope, 'mod_a::timestamp')).to eql(Puppet::Pops::Time::Timestamp.parse('1994-03-25T19:30:00'))\n              expect(lookup_func.call(scope, 'mod_a::timespan')).to eql(Puppet::Pops::Time::Timespan.parse('3-10:00:00'))\n            end\n            expect(notices).to eql(['success'])\n          end\n        end\n\n        context 'with declared but incompatible return_type' do\n          let(:puppet_function) { <<-PUPPET.unindent }\n            function mod_a::pp_lookup_key(Puppet::LookupKey $key, Hash[String,String] $options, Puppet::LookupContext $context) >> Runtime['ruby','Symbol'] {\n              undef\n            }\n            PUPPET\n\n          it 'fails and reports error' do\n            expect{lookup('mod_a::a')}.to raise_error(\n              \"Return type of 'lookup_key' function named 'mod_a::pp_lookup_key' is incorrect, expects a RichData value, got Runtime\")\n          end\n        end\n      end\n\n      context 'using a data_dig that is a ruby function' do\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'lib' => {\n                'puppet' => {\n                  'functions' => {\n                    'mod_a' => {\n                      'ruby_dig.rb' => <<-RUBY.unindent\n                      Puppet::Functions.create_function(:'mod_a::ruby_dig') do\n                        dispatch :ruby_dig do\n                          param 'Array[String[1]]', :segments\n                          param 'Hash[String,Any]', :options\n                          param 'Puppet::LookupContext', :context\n                          return_type 'Puppet::LookupValue'\n                        end\n\n                        def ruby_dig(segments, options, context)\n                          sub_segments = segments.dup\n                          root_key = sub_segments.shift\n                          case root_key\n                          when 'mod_a::options'\n                            hash = { 'mod_a::options' => options }\n                          when 'mod_a::lookup'\n                            return call_function('lookup', segments.join('.'))\n                          else\n                            hash = {\n                              'mod_a::a' => 'value mod_a::a (from mod_a)',\n                              'mod_a::b' => 'value mod_a::b (from mod_a)',\n                              'mod_a::hash_a' => {\n                                'a' => 'value mod_a::hash_a.a (from mod_a)',\n                                'b' => 'value mod_a::hash_a.b (from mod_a)'\n                              },\n                              'mod_a::hash_b' => {\n                                'a' => 'value mod_a::hash_b.a (from mod_a)',\n                                'b' => 'value mod_a::hash_b.b (from mod_a)'\n                              },\n                              'mod_a::interpolated' => \"-- %{lookup('mod_a::a')} --\",\n                              'mod_a::really_interpolated' => \"-- %{lookup('mod_a::a')} --\",\n                              'mod_a::a_a' => \"-- %{lookup('mod_a::hash_a.a')} --\",\n                              'mod_a::a_b' => \"-- %{lookup('mod_a::hash_a.b')} --\",\n                              'mod_a::b_a' => \"-- %{lookup('mod_a::hash_b.a')} --\",\n                              'mod_a::b_b' => \"-- %{lookup('mod_a::hash_b.b')} --\",\n                              'mod_a::bad_type' => :oops,\n                              'mod_a::bad_type_in_hash' => { 'a' => :oops },\n                            }\n                            end\n                          context.not_found unless hash.include?(root_key)\n                          value = sub_segments.reduce(hash[root_key]) do |memo, segment|\n                            context.not_found unless memo.is_a?(Hash) && memo.include?(segment)\n                            memo[segment]\n                          end\n                          root_key == 'mod_a::really_interpolated' ? context.interpolate(value) : value\n                        end\n                      end\n                    RUBY\n                    }\n                  }\n                }\n              },\n              'hiera.yaml' => <<-YAML.unindent,\n              ---\n              version: 5\n              defaults:\n                options:\n                  option_b:\n                    z: Default option value b.z\n\n              hierarchy:\n                - name: \"Common\"\n                  data_dig: mod_a::ruby_dig\n                  uri: \"http://www.example.com/passed/as/option\"\n                  options:\n                    option_a: Option value a\n                    option_b:\n                      x: Option value b.x\n                      y: Option value b.y\n                - name: \"Extra\"\n                  data_dig: mod_a::ruby_dig\n           YAML\n            }\n          }\n        end\n\n        it 'finds data in the module' do\n          expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')\n        end\n\n        it 'environment data has higher priority than module data' do\n          expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')\n        end\n\n        it 'will not resolve interpolated expressions' do\n          expect(lookup('mod_a::interpolated')).to eql(\"-- %{lookup('mod_a::a')} --\")\n        end\n\n        it 'resolves interpolated expressions using Context#interpolate' do\n          expect(lookup('mod_a::really_interpolated')).to eql(\"-- value mod_a::a (from environment) --\")\n        end\n\n        it 'does not accept return of runtime type from function' do\n          # Message is produced by the called function, not by the lookup framework\n          expect(explain('mod_a::bad_type')).to include(\"value returned from function 'ruby_dig' has wrong type\")\n        end\n\n        it 'does not accept return of runtime type embedded in hash from function' do\n          # Message is produced by the called function, not by the lookup framework\n          expect(explain('mod_a::bad_type_in_hash')).to include(\"value returned from function 'ruby_dig' has wrong type\")\n        end\n\n        it 'will not merge hashes from environment and module unless strategy hash is used' do\n          expect(lookup('mod_a::hash_a')).to eql({'a' => 'value mod_a::hash_a.a (from environment)'})\n        end\n\n        it 'hierarchy entry options are passed to the function' do\n          expect(lookup('mod_a::options.option_b.x')).to eql('Option value b.x')\n        end\n\n        it 'default options are passed to the function' do\n          expect(lookup('mod_a::options.option_b.z')).to eql('Default option value b.z')\n        end\n\n        it 'default options are not merged with hierarchy options' do\n          expect(lookup('mod_a::options')).to eql(\n            {\n              'option_a' => 'Option value a',\n              'option_b' => {\n                'y' => 'Option value b.y',\n                'x' => 'Option value b.x'\n              },\n              'uri' => 'http://www.example.com/passed/as/option'\n            })\n        end\n\n        it 'hierarchy entry \"uri\" is passed as location option to the function' do\n          expect(lookup('mod_a::options.uri')).to eql('http://www.example.com/passed/as/option')\n        end\n\n        it 'recursive lookup is trapped' do\n          expect(explain('mod_a::lookup.mod_a::lookup')).to include('Recursive lookup detected')\n        end\n\n        context 'with merge strategy hash' do\n          it 'merges hashes from environment and module' do\n            expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql({'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)'})\n          end\n\n          it 'will \"undig\" value from data_dig function, merge root hashes, and then dig to get values by subkey' do\n            expect(lookup(['mod_a::hash_a.a', 'mod_a::hash_a.b'], :merge => 'hash')).to eql(\n              ['value mod_a::hash_a.a (from environment)', 'value mod_a::hash_a.b (from mod_a)'])\n          end\n        end\n      end\n\n      context 'that has a default_hierarchy' do\n        let(:mod_a_hiera_yaml) { <<-YAML.unindent }\n          version: 5\n          hierarchy:\n            - name: \"Common\"\n              path: common.yaml\n            - name: \"Common 2\"\n              path: common2.yaml\n\n          default_hierarchy:\n            - name: \"Default\"\n              path: defaults.yaml\n            - name: \"Default 2\"\n              path: defaults2.yaml\n          YAML\n\n        let(:mod_a_common) { <<-YAML.unindent }\n          mod_a::a: value mod_a::a (from module)\n          mod_a::d:\n            a: value mod_a::d.a (from module)\n          mod_a::f:\n            a:\n              a: value mod_a::f.a.a (from module)\n          mod_a::to_array1: 'hello'\n          mod_a::to_array2: 'hello'\n          mod_a::to_int: 'bananas'\n          mod_a::to_bad_type: 'pyjamas'\n          mod_a::undef_value: null\n          lookup_options:\n            mod_a::e:\n              merge: deep\n            mod_a::to_array1:\n              merge: deep\n              convert_to: \"Array\"\n            mod_a::to_array2:\n              convert_to:\n                - \"Array\"\n                - true\n            mod_a::to_int:\n              convert_to: \"Integer\"\n            mod_a::to_bad_type:\n              convert_to: \"ComicSans\"\n            mod_a::undef_value:\n              convert_to:\n                - \"Array\"\n                - true\n          YAML\n\n\n        let(:mod_a_common2) { <<-YAML.unindent }\n          mod_a::b: value mod_a::b (from module)\n          mod_a::d:\n            c: value mod_a::d.c (from module)\n          mod_a::f:\n            a:\n              b: value mod_a::f.a.b (from module)\n          YAML\n\n        let(:mod_a_defaults) { <<-YAML.unindent }\n          mod_a::a: value mod_a::a (from module defaults)\n          mod_a::b: value mod_a::b (from module defaults)\n          mod_a::c: value mod_a::c (from module defaults)\n          mod_a::d:\n            b: value mod_a::d.b (from module defaults)\n          mod_a::e:\n            a:\n              a: value mod_a::e.a.a (from module defaults)\n          mod_a::g:\n            a:\n              a: value mod_a::g.a.a (from module defaults)\n          lookup_options:\n            mod_a::d:\n              merge: hash\n            mod_a::g:\n              merge: deep\n          YAML\n\n        let(:mod_a_defaults2) { <<-YAML.unindent }\n          mod_a::e:\n            a:\n              b: value mod_a::e.a.b (from module defaults)\n          mod_a::g:\n            a:\n              b: value mod_a::g.a.b (from module defaults)\n          YAML\n\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'data' => {\n                'common.yaml' => mod_a_common,\n                'common2.yaml' => mod_a_common2,\n                'defaults.yaml' => mod_a_defaults,\n                'defaults2.yaml' => mod_a_defaults2\n              },\n              'hiera.yaml' => mod_a_hiera_yaml\n            }\n          }\n        end\n\n        it 'the default hierarchy does not interfere with environment hierarchy' do\n          expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')\n        end\n\n        it 'the default hierarchy does not interfere with regular hierarchy in module' do\n          expect(lookup('mod_a::b')).to eql('value mod_a::b (from module)')\n        end\n\n        it 'the default hierarchy is consulted when no value is found elsewhere' do\n          expect(lookup('mod_a::c')).to eql('value mod_a::c (from module defaults)')\n        end\n\n        it 'the default hierarchy does not participate in a merge' do\n          expect(lookup('mod_a::d', 'merge' => 'hash')).to eql('a' => 'value mod_a::d.a (from module)', 'c' => 'value mod_a::d.c (from module)')\n        end\n\n        it 'lookup_options from regular hierarchy does not effect values found in the default hierarchy' do\n          expect(lookup('mod_a::e')).to eql('a' => { 'a' => 'value mod_a::e.a.a (from module defaults)' })\n        end\n\n        it 'lookup_options from default hierarchy affects values found in the default hierarchy' do\n          expect(lookup('mod_a::g')).to eql('a' => { 'a' => 'value mod_a::g.a.a (from module defaults)', 'b' => 'value mod_a::g.a.b (from module defaults)'})\n        end\n\n        it 'merge parameter does not override lookup_options defined in the default hierarchy' do\n          expect(lookup('mod_a::g', 'merge' => 'hash')).to eql(\n            'a' => { 'a' => 'value mod_a::g.a.a (from module defaults)', 'b' => 'value mod_a::g.a.b (from module defaults)'})\n        end\n\n        it 'lookup_options from default hierarchy does not effect values found in the regular hierarchy' do\n          expect(lookup('mod_a::d')).to eql('a' => 'value mod_a::d.a (from module)')\n        end\n\n        context \"and conversion via convert_to\" do\n          it 'converts with a single data type value' do\n            expect(lookup('mod_a::to_array1')).to eql(['h', 'e', 'l', 'l', 'o'])\n          end\n\n          it 'converts with an array of arguments to the convert_to call' do\n            expect(lookup('mod_a::to_array2')).to eql(['hello'])\n          end\n\n          it 'converts an undef/nil value that has convert_to option' do\n            expect(lookup('mod_a::undef_value')).to eql([nil])\n          end\n\n          it 'errors if a convert_to lookup_option cannot be performed because value does not match type' do\n            expect{lookup('mod_a::to_int')}.to raise_error(/The convert_to lookup_option for key 'mod_a::to_int' raised error.*The string 'bananas' cannot be converted to Integer/)\n          end\n\n          it 'errors if a convert_to lookup_option cannot be performed because type does not exist' do\n            expect{lookup('mod_a::to_bad_type')}.to raise_error(/The convert_to lookup_option for key 'mod_a::to_bad_type' raised error.*Creation of new instance of type 'TypeReference\\['ComicSans'\\]' is not supported/)\n          end\n\n          it 'adds explanation that conversion took place with a type' do\n            explanation = explain('mod_a::to_array1')\n            expect(explanation).to include('Applying convert_to lookup_option with arguments [Array]')\n          end\n\n          it 'adds explanation that conversion took place with a type and arguments' do\n            explanation = explain('mod_a::to_array2')\n            expect(explanation).to include('Applying convert_to lookup_option with arguments [Array, true]')\n          end\n        end\n\n        it 'the default hierarchy lookup is included in the explain output' do\n          explanation = explain('mod_a::c')\n          expect(explanation).to match(/Searching default_hierarchy of module \"mod_a\".+Original path: \"defaults.yaml\"/m)\n        end\n      end\n    end\n\n    context 'and an eyaml lookup_key function', :if => Puppet.features.hiera_eyaml? do\n      let(:private_key_name) { 'private_key.pkcs7.pem' }\n      let(:public_key_name) { 'public_key.pkcs7.pem' }\n\n      let(:private_key) do\n        <<-PKCS7.unindent\n          -----BEGIN RSA PRIVATE KEY-----\n          MIIEogIBAAKCAQEAwHB3GvImq59em4LV9DMfP0Zjs21eW3Jd5I9fuY0jLJhIkH6f\n          CR7tyOpYV6xUj+TF8giq9WLxZI7sourMJMWjEWhVjgUr5lqp1RLv4lwfDv3Wk4XC\n          2LUuqj1IAErUXKeRz8i3lUSZW1Pf4CaMpnIiPdWbz6f0KkaJSFi9bqexONBx4fKQ\n          NlgZwm2/aYjjrYng788I0QhWDKUqsQOi5mZKlHNRsDlk7J3Afhsx/jTLrCX/G8+2\n          tPtLsHyRN39kluM5vYHbKXDsCG/a88Z2yUE2+r4Clp0FUKffiEDBPm0/H0sQ4Q1o\n          EfQFDQRKaIkhpsm0nOnLYTy3/xJc5uqDNkLiawIDAQABAoIBAE98pNXOe8ab93oI\n          mtNZYmjCbGAqprTjEoFb71A3SfYbmK2Gf65GxjUdBwx/tBYTiuekSOk+yzKcDoZk\n          sZnmwKpqDByzaiSmAkxunANFxdZtZvpcX9UfUX0j/t+QCROUa5gF8j6HrUiZ5nkx\n          sxr1PcuItekaGLJ1nDLz5JsWTQ+H4M+GXQw7/t96x8v8g9el4exTiAHGk6Fv16kD\n          017T02M9qTTmV3Ab/enDIBmKVD42Ta36K/wc4l1aoUQNiRbIGVh96Cgd1CFXLF3x\n          CsaNbYT4SmRXaYqoj6MKq+QFEGxadFmJy48NoSd4joirIn2lUjHxJebw3lLbNLDR\n          uvQnQ2ECgYEA/nD94wEMr6078uMv6nKxPpNGq7fihwSKf0G/PQDqrRmjUCewuW+k\n          /iXMe1Y/y0PjFeNlSbUsUvKQ5xF7F/1AnpuPHIrn3cjGVLb71W+zen1m8SnhsW/f\n          7dPgtcb4SCvfhmLgoov+P34YcNfGi6qgPUu6319IqoB3BIi7PvfEomkCgYEAwZ4+\n          V0bMjFdDn2hnYzjTNcF2aUQ1jPvtuETizGwyCbbMLl9522lrjC2DrH41vvqX35ct\n          CBJkhQFbtHM8Gnmozv0vxhI2jP+u14mzfePZsaXuYrEgWRj+BCsYUHodXryxnEWj\n          yVrTNskab1B5jFm2SCJDmKcycBOYpRBLCMx6W7MCgYBA99z7/6KboOIzzKrJdGup\n          jLV410UyMIikoccQ7pD9jhRTPS80yjsY4dHqlEVJw5XSWvPb9DTTITi6p44EvBep\n          6BKMuTMnQELUEr0O7KypVCfa4FTOl8BX28f+4kU3OGykxc6R8qkC0VGwTohV1UWB\n          ITsgGhZV4uOA9uDI3T8KMQKBgEnQY2HwmuDSD/TA39GDA3qV8+ez2lqSXRGIKZLX\n          mMf9SaBQQ+uzKA4799wWDbVuYeIbB07xfCL83pJP8FUDlqi6+7Celu9wNp7zX1ua\n          Nw8z/ErhzjxJe+Xo7A8aTwIkG+5A2m1UU/up9YsEeiJYvVaIwY58B42U2vfq20BS\n          fD9jAoGAX2MscBzIsmN+U9R0ptL4SXcPiVnOl8mqvQWr1B4OLgxX7ghht5Fs956W\n          bHipxOWMFCPJA/AhNB8q1DvYiD1viZbIALSCJVUkzs4AEFIjiPsCBKxerl7jF6Xp\n          1WYSaCmfvoCVEpFNt8cKp4Gq+zEBYAV4Q6TkcD2lDtEW49MuN8A=\n          -----END RSA PRIVATE KEY-----\n          PKCS7\n      end\n\n      let(:public_key) do\n        <<-PKCS7.unindent\n          -----BEGIN CERTIFICATE-----\n          MIIC2TCCAcGgAwIBAgIBATANBgkqhkiG9w0BAQUFADAAMCAXDTE3MDExMzA5MTY1\n          MloYDzIwNjcwMTAxMDkxNjUyWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n          CgKCAQEAwHB3GvImq59em4LV9DMfP0Zjs21eW3Jd5I9fuY0jLJhIkH6fCR7tyOpY\n          V6xUj+TF8giq9WLxZI7sourMJMWjEWhVjgUr5lqp1RLv4lwfDv3Wk4XC2LUuqj1I\n          AErUXKeRz8i3lUSZW1Pf4CaMpnIiPdWbz6f0KkaJSFi9bqexONBx4fKQNlgZwm2/\n          aYjjrYng788I0QhWDKUqsQOi5mZKlHNRsDlk7J3Afhsx/jTLrCX/G8+2tPtLsHyR\n          N39kluM5vYHbKXDsCG/a88Z2yUE2+r4Clp0FUKffiEDBPm0/H0sQ4Q1oEfQFDQRK\n          aIkhpsm0nOnLYTy3/xJc5uqDNkLiawIDAQABo1wwWjAPBgNVHRMBAf8EBTADAQH/\n          MB0GA1UdDgQWBBSejWrVnw7QaBjNFCHMNFi+doSOcTAoBgNVHSMEITAfgBSejWrV\n          nw7QaBjNFCHMNFi+doSOcaEEpAIwAIIBATANBgkqhkiG9w0BAQUFAAOCAQEAAe85\n          BQ1ydAHFqo0ib38VRPOwf5xPHGbYGhvQi4/sU6aTuR7pxaOJPYz05jLhS+utEmy1\n          sknBq60G67yhQE7IHcfwrl1arirG2WmKGvAbjeYL2K1UiU0pVD3D+Klkv/pK6jIQ\n          eOJRGb3qNUn0Sq9EoYIOXiGXQ641F0bZZ0+5H92kT1lmnF5oLfCb84ImD9T3snH6\n          pIr5RKRx/0YmJIcv3WdpoPT903rOJiRIEgIj/hDk9QZTBpm222Ul5yQQ5pBywpSp\n          xh0bmJKAQWhQm7QlybKfyaQmg5ot1jEzWAvD2I5FjHQxmAlchjb6RreaRhExj+JE\n          5O117dMBdzDBjcNMOA==\n          -----END CERTIFICATE-----\n          PKCS7\n      end\n\n      let(:keys_dir) do\n        keys = tmpdir('keys')\n        dir_contained_in(keys, {\n          private_key_name => private_key,\n          public_key_name => public_key\n        })\n        keys\n      end\n\n      let(:private_key_path) { File.join(keys_dir, private_key_name) }\n      let(:public_key_path) { File.join(keys_dir, public_key_name) }\n\n      let(:env_hiera_yaml) do\n        <<-YAML.unindent\n          version: 5\n          hierarchy:\n            - name: EYaml\n              path: common.eyaml\n              lookup_key: eyaml_lookup_key\n              options:\n                pkcs7_private_key: #{private_key_path}\n                pkcs7_public_key: #{public_key_path}\n          YAML\n      end\n\n      let(:scope_additions) { { 'ipl_suffix' => 'aa' } }\n      let(:data_files) do\n        {\n          'common.eyaml' => <<-YAML.unindent\n            # a: Encrypted value 'a' (from environment)\n            a: >\n              ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEw\n              DQYJKoZIhvcNAQEBBQAEggEAUwwNRA5ZKM87SLnjnJfzDFRQbeheSYMTOhcr\n              sgTPCGtzEAzvRBrkdIRAvDZVRfadV9OB+bJsYrhWIkU1bYiOn1m78ginh96M\n              44RuspnIZYnL9Dhs+JyC8VvB5nlvlEph2RGt+KYg9iU4JYhwZ2+8+yxB6/UK\n              H5HGKDCjBbEc8o9MbCckLsciIh11hKKgT6K0yhKB/nBxxM78nrX0BxmAHX2u\n              bejKDRa9S/0uS7Y91nvnbIkaQpZ4KteSQ+J4/lQBMlMAeE+2F9ncM8jFKnQC\n              rzzdbn1O/zwsEt5J5CRP1Sc+8hM644+IqkLs+17segxArHVGOsEqyDcHbXEK\n              9jspfzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCIq/L5HeJgA9XQm67j\n              JHUngDDS5s52FsuSIMin7Z/pV+XuaJGFkL80ia4bXnCWilmtM8oUa/DZuBje\n              dCILO7I8QqU=]\n            hash_a:\n              \"hash_%{ipl_suffix}\":\n                # aaa: Encrypted value hash_a.hash_aa.aaa (from environment)\n                aaa: >\n                  ENC[PKCS7,MIIBqQYJKoZIhvcNAQcDoIIBmjCCAZYCAQAxggEhMIIBHQIBADAFMAACAQEw\n                  DQYJKoZIhvcNAQEBBQAEggEAhvGXL5RxVUs9wdqJvpCyXtfCHrm2HbG/u30L\n                  n8EuRD9ravlsgIISAnd27JPtrxA+0rZq4EQRGz6OcovnH9vTg86/lVBhhPnz\n                  b83ArptGJhRvTYUJ19GZI3AYjJbhWj/Jo5NL56oQJaPBccqHxMApm/U0wlus\n                  QtASL94cLuh4toVIBQCQzD5/Bx51p2wQobm9p4WKSl1zJhDceurmoLZXqhuN\n                  JwwEBwXopJvgid3ZDPbdX8nI6vHhb/8wDq9yb5DOsrkgqDqQgwPU9sUUioQj\n                  Hr1pGyeOWnbEe99iEb2+m7TWsC0NN7OBo06mAgFNbBLjvn2k4PiCxrOOgJ8S\n                  LI5eXjBsBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCWNS6j3m/Xvrp5RFaN\n                  ovm/gEB4oPlYJswoXuWqcEBfwZzbpy96x3b2Le/yoa72ylbPAUc5GfLENvFQ\n                  zXpTtSmQE0fixY4JMaBTke65ZRvoiOQO]\n            array_a:\n              # - \"array_a[0]\"\n              - >\n                ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQEw\n                DQYJKoZIhvcNAQEBBQAEggEAmXZfyfU77vVCZqHpR10qhD0Jy9DpMGBgal97\n                vUO2VHX7KlCgagK0kz8/uLRIthkYzcpn8ISmw/+CIAny3jOjxOsylJiujqyu\n                hx/JEFl8bOKOg40Bd0UuBaw/qZ+CoAtOorhiIW7x6t7DpknItC6gkH/cSJ4/\n                p3MdhoARRuwj2fvuaChVsD39l2rXjgJj0OJOaDXdbuisG75VRZf5l8IH6+44\n                Q7m6W7BU69LX+ozn+W3filQoiJ5MPf8w/KXAObMSbKYIDsrZUyIWyyNUbpW0\n                MieIkHj93bX3gIEcenECLdWaEzcPa7MHgl6zevQKg4H0JVmcvKYyfHYqcrVE\n                PqizKDA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDf259KZEay1widVSFy\n                I9zGgBAICjm0x2GeqoCnHdiAA+jt]\n              # - \"array_a[1]\"\n              - >\n                ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQEw\n                DQYJKoZIhvcNAQEBBQAEggEATVy4hHG356INFKOswAhoravh66iJljp+Vn3o\n                UVD1kyRiqY5tz3UVSptzUmzD+YssX/f73AKCjUI3HrPNL7kAxsk6fWS7nDEj\n                AuxtCqGYeBha6oYJYziSGIHfAdY3MiJUI1C9g/OQB4TTvKdrlDArPiY8THJi\n                bzLLMbVQYJ6ixSldwkdKD75vtikyamx+1LSyVBSg8maVyPvLHtLZJuT71rln\n                WON3Ious9PIbd+izbcCzaoqh5UnTfDCjOuAYliXalBxamIIwNzSV1sdR8/qf\n                t22zpYK4J8lgCBV2gKfrOWSi9MAs6JhCeOb8wNLMmAUTbc0WrFJxoCwAPX0z\n                MAjsNjA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBC4v4bNE4gFlbLmVY+9\n                BtSLgBBm7U0wu6d6s9wF9Ek9IHPe]\n            # ref_a: \"A resolved = '%{hiera('a')}'\"\n            ref_a: >\n                ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEw\n                DQYJKoZIhvcNAQEBBQAEggEAFSuUp+yk+oaA7b5ekT0u360CQ9Q2sIQ/bTcM\n                jT3XLjm8HIGYPcysOEnuo8WcAxJFY5iya4yQ7Y/UhMWXaTi7Vzv/6BmyPDwz\n                +7Z2Mf0r0PvS5+ylue6aem/3bXPOmXTKTf68OCehTRXlDUs8/av9gnsDzojp\n                yiUTBZvKxhIP2n//GyoHgyATveHT0lxPVpdMycB347DtWS7IduCxx0+KiOOw\n                DXYFlYbIVxVInwgERxtsfYSr+Fu0/mkjtRsQm+dPzMQOATE9Val2gGKsV6bi\n                kdm1OM9HrwVsFj6Lma6FYmr89Bcm/1uEc8fiOMtNK3z2+nwunWBMNCGneMYD\n                C5IJejBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAeiZDGQyXHkZlV5ceT\n                iCxpgCDDatuVvbPEEi8rKOC7xhPHZ22zLEEV//l7C9jxq+DZcA==]\n            YAML\n        }\n      end\n\n      let(:env_data) { data_files }\n\n      context 'with unencryptable eyaml' do\n        let(:data_files) do\n          {\n            'common.eyaml' => <<-YAML.unindent\n              key_with_invalid_eyaml: ENC[PKCS7,INVALID]\n              YAML\n          }\n        end\n\n        it 'fails and reports error with ENC value, key being looked up and filename' do\n          expect { lookup('key_with_invalid_eyaml') }.to raise_error(Puppet::DataBinding::LookupError, /hiera-eyaml backend error decrypting ENC\\[PKCS7,INVALID\\] when looking up key_with_invalid_eyaml in .*common\\.eyaml\\. Error was/)\n        end\n      end\n      context 'and a module using eyaml with different options' do\n\n        let(:private_module_key) do\n          <<-PKCS7.unindent\n          -----BEGIN RSA PRIVATE KEY-----\n          MIIEogIBAAKCAQEAuqVpctipK4OMWM+RwKcd/mR4pg6qE3+ItPVC9TlvBrmDaN/y\n          YZRjQR+XovXSGuy/CneSQ9Qss0Ff3FKAmEeH0qN0V47a81hgLpjhLCX1n+Ov7r1Q\n          DC1ciTpVzHE4krN3rJ/RmDohitIqT1IYYhdcEdaMG9E26HIzn1QIwaDiYU3mfqWM\n          8CZExa0CeIsEzHRLSxuMi/xX0ENImCRUzY9GH88Cu2gUhpKlbVzJmVqGPgp94pJY\n          YM+SUb0XP1yRySpJMnVg98oCUrQO2OoE/Gax/djAi6hrJUzejPsEKdZ1yxM6OyJW\n          NjWZYs8izAxBqm7pv1hx5+X7AIPqwZTMVrB7TQIDAQABAoIBAHIex13QOYeAlGSM\n          7bpUtBMiTV6DItxvIyA5wen8ZvU+oqmSHDorp5BfB7E9Cm0qChkVSRot9fLYawtk\n          anoxakuRY4ZRs3AMvipfkXYT854CckTP/cykQ6soPuOU6plQIEEtKtMf3/hoTjRX\n          ps77J3FEtEAh6Kexg/zMPdpeS2xgULhk0P9ZQEg+JhLA5dq0p0nz3SBkuzcxei79\n          +Za/Tg1osD0AINOajdvPnKxvlmWJN0+LpGwVjFNhkoUXPeDyvq0z2V/Uqwz4HP2I\n          UGv4tz3SbzFc3Ie4lzgUZzCQgUK3u60pq1uyA6BRtxwdEmpn5v++jGXBGJZpWwcW\n          UNblESUCgYEA4aTH9+LHsNjLPs2FmSc7hNjwHG1rAHcDXTX2ccySjRcQvH4Z7xBL\n          di+SzZ2Tf8gSLycPRgRVCbrgCODpjoV2D5wWnyUHfWm4+GQxHURYa4UDx69tsSKE\n          OTRASJo7/Mz0M1a6YzgCzVRM/TO676ucmawzKUY5OUm1oehtODAiZOcCgYEA08GM\n          AMBOznys02xREJI5nLR6AveuTbIjF2efEidoxoW+1RrMOkcqaDTrJQ5PLM+oDDwD\n          iPzVjnroSbwJzFB71atIg7b7TwltgkXy7wNTedO2cm5u/I0q8tY2Jaa4Mz8JUnbe\n          yafvtS0/mY6A5k+8/2UIMFin2rqU9NC9EUPIo6sCgYBhOvAwELibq89osIbxB8bN\n          5+0PUtbYzG/WqnoXb193DIlZr7zdFththPJtR4lXdo7fYqViNluuZahEKyZ5E2lc\n          MJZO3VXs5LGf1wyS3/B55EdMtHs/6O+w9qL8pflTZb2UobqPJoOOltTWBoR24iwI\n          y/r/vhLKbMini9AEdjlb4QKBgGdYsax4Lr4GCQ8ScSnmQ6ngRyAFo5MV2pyEnRTu\n          GOuywKUe9AeJTgAXu5+VMT0Mh9aYv5zu0Ic+IvpBhIKr0RRCCR0Hg/VaA5Et9FeE\n          RwxRMFz+2rn1Z72moDyV9pZEMJeHnknK5WmGEOEvtGczCWmX9Hwr+Jf+sc4dxfiU\n          HWsLAoGAXWSX73p/6R4eRfF5zU2UFJPvDzhmwObAuvU4zKs9x7PMxZfvyt/eBCO1\n          fj2+hIR72RxVuHbLApF1BT6gPVLtNdvaNuCs8YlHcnx/Oi088F0ni7fL/xYBUvaB\n          7wTf188UJxP1ofVMZW00P4I9mR6BrOulv455gCwsmg2X7WtJU48=\n          -----END RSA PRIVATE KEY-----\n          PKCS7\n        end\n\n        let(:public_module_key) do\n          <<-PKCS7.unindent\n          -----BEGIN CERTIFICATE-----\n          MIIC2TCCAcGgAwIBAgIBATANBgkqhkiG9w0BAQUFADAAMCAXDTE3MDUzMTE2Mjc0\n          M1oYDzIwNjcwNTE5MTYyNzQzWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n          CgKCAQEAuqVpctipK4OMWM+RwKcd/mR4pg6qE3+ItPVC9TlvBrmDaN/yYZRjQR+X\n          ovXSGuy/CneSQ9Qss0Ff3FKAmEeH0qN0V47a81hgLpjhLCX1n+Ov7r1QDC1ciTpV\n          zHE4krN3rJ/RmDohitIqT1IYYhdcEdaMG9E26HIzn1QIwaDiYU3mfqWM8CZExa0C\n          eIsEzHRLSxuMi/xX0ENImCRUzY9GH88Cu2gUhpKlbVzJmVqGPgp94pJYYM+SUb0X\n          P1yRySpJMnVg98oCUrQO2OoE/Gax/djAi6hrJUzejPsEKdZ1yxM6OyJWNjWZYs8i\n          zAxBqm7pv1hx5+X7AIPqwZTMVrB7TQIDAQABo1wwWjAPBgNVHRMBAf8EBTADAQH/\n          MB0GA1UdDgQWBBQkhoMgOyPzEe7tOOimNH2//PYF2TAoBgNVHSMEITAfgBQkhoMg\n          OyPzEe7tOOimNH2//PYF2aEEpAIwAIIBATANBgkqhkiG9w0BAQUFAAOCAQEAhRWc\n          Nz3PcUJllao5G/v4AyvjLgwB2JgjJgh6D3ILoOe9TrDSXD7ZV3F30vFae+Eztk86\n          pmM8x57E0HsuuY+Owf6/hvELtwbzf9N/lc9ySZSogGFoQeJ8rnCJAQ0FaPjqb7AN\n          xTaY9HTzr4dZG1f+sw32RUu2fDe7Deqgf85uMSZ1mtRTt9zvo8lMQxVA2nVOfwz2\n          Nxf+qSNYSCtf0/6iwfzHy0qPjaJnywgBCi3Lg2IMSqGUatxzH+9HWrBgD+ZYxmDz\n          2gW+EIU1Y/We/tbjIWaR1PD+IzeRJi5fHq60RKHPSdp7TGtV48bQRvyZXC7sVCRa\n          yxfX1IGYhCDzbFRQNg==\n          -----END CERTIFICATE-----\n          PKCS7\n        end\n\n        let(:module_keys_dir) do\n          keys = tmpdir('keys')\n          dir_contained_in(keys, {\n            private_key_name => private_module_key,\n            public_key_name => public_module_key\n          })\n          keys\n        end\n\n        let(:private_module_key_path) { File.join(module_keys_dir, private_key_name) }\n        let(:public_module_key_path) { File.join(module_keys_dir, public_key_name) }\n\n        let(:mod_a_files) do\n          {\n            'mod_a' => {\n              'hiera.yaml' => <<-YAML.unindent,\n                version: 5\n                hierarchy:\n                  - name: EYaml\n                    path: common.eyaml\n                    lookup_key: eyaml_lookup_key\n                    options:\n                      pkcs7_private_key: #{private_module_key_path}\n                      pkcs7_public_key: #{public_module_key_path}\n                YAML\n              'data' => {\n                'common.eyaml' => <<-YAML.unindent\n                ---\n                # \"%{lookup('a')} (from module)\"\n                mod_a::a: >\n                  ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEw\n                  DQYJKoZIhvcNAQEBBQAEggEAC+lvda8mX6XkgCBstNw4IQUDyFcS6M0mS9gZ\n                  ev4VBDeUK4AUNVnzzdbW0Mnj9LbqlpzFx96VGqSxsRBpe7BVD0kVo5jQsEMn\n                  nbrWOD1lvXYrXZMXBeD9xJbMbH5EiiFhbaXcEKRAVGaLVQKjXDENDQ/On+it\n                  1+wmmVwJynDJR0lsCz6dcSKvw6wnxBcv32qFyePvJuIf04CHMhaS4ykedYHK\n                  vagUn5uVXOv/8G0JPlZnQLyxjE0v0heb0Zj0mvcP2+Y5BSW50AQVrMWJNtdW\n                  aFEg6H5hpjduQfQh3iWVuDLnWhbP0sY2Grn5dTOxQP8aTDSsiTUcSeIAmjr/\n                  K8YRCjBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAjL7InlBjRuohLLcBx\n                  686ogCDkhCan8bCE7aX2nr75QtLF3q89pFIR4/NGl5+oGEO+qQ==]\n                YAML\n              }\n            }\n          }\n        end\n\n        let(:populated_env_dir) do\n          dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files }))\n          env_dir\n        end\n\n        it 'repeatedly finds data in environment and module' do\n          expect(lookup(['array_a', 'mod_a::a', 'hash_a'])).to eql([\n            ['array_a[0]', 'array_a[1]'],\n            \"Encrypted value 'a' (from environment) (from module)\",\n            {'hash_aa'=>{'aaa'=>'Encrypted value hash_a.hash_aa.aaa (from environment)'}}])\n        end\n      end\n\n      it 'finds data in the environment' do\n        expect(lookup('a')).to eql(\"Encrypted value 'a' (from environment)\")\n      end\n\n      it 'evaluates interpolated keys' do\n        expect(lookup('hash_a')).to include('hash_aa')\n      end\n\n      it 'evaluates interpolations in encrypted values' do\n        expect(lookup('ref_a')).to eql(\"A resolved = 'Encrypted value 'a' (from environment)'\")\n      end\n\n      it 'can read encrypted values inside a hash' do\n        expect(lookup('hash_a.hash_aa.aaa')).to eql('Encrypted value hash_a.hash_aa.aaa (from environment)')\n      end\n\n      it 'can read encrypted values inside an array' do\n        expect(lookup('array_a')).to eql(['array_a[0]', 'array_a[1]'])\n      end\n\n      context 'declared in global scope as a Hiera v3 backend', :if => Puppet.features.hiera? do\n        let(:environment_files) { {} }\n        let(:data_file_content) { <<-YAML.unindent }\n          a: >\n            ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEw\n            DQYJKoZIhvcNAQEBBQAEggEAH457bsfL8kYw9O50roE3dcE21nCnmPnQ2XSX\n            LYRJ2C78LarbfFonKz0gvDW7tyhsLWASFCFaiU8T1QPBd2b3hoQK8E4B2Ual\n            xga/K7r9y3OSgRomTm9tpTltC6re0Ubh3Dy71H61obwxEdNVTqjPe95+m2b8\n            6zWZVnzZzXXsTG1S17yJn1zaB/LXHbWNy4KyLLKCGAml+Gfl6ZMjmaplTmUA\n            QIC5rI8abzbPP3TDMmbLOGNkrmLqI+3uS8tSueTMoJmWaMF6c+H/cA7oRxmV\n            QCeEUVXjyFvCHcmbA+keS/RK9XF+vc07/XS4XkYSPs/I5hLQji1y9bkkGAs0\n            tehxQjBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDHpA6Fcl/R16aIYcow\n            oiO4gDAvfFH6jLUwXkcYtagnwdmhkd9TQJtxNWcIwMpvmk036MqIoGwwhQdg\n            gV4beiCFtLU=]\n          YAML\n\n        let(:hiera_yaml) do\n          <<-YAML.unindent\n          :backends: eyaml\n          :eyaml:\n            :datadir: #{code_dir}/hieradata\n            :pkcs7_private_key: #{private_key_path}\n            :pkcs7_public_key: #{public_key_path}\n          :hierarchy:\n            - common\n          YAML\n        end\n\n        let(:data_files) do\n          {\n            'common.eyaml' => data_file_content\n          }\n        end\n\n        let(:code_dir_files) do\n          {\n            'hiera.yaml' => hiera_yaml,\n            'hieradata' => data_files\n          }\n        end\n\n        before(:each) do\n          Puppet.settings[:hiera_config] = File.join(code_dir, 'hiera.yaml')\n        end\n\n        it 'finds data in the global layer' do\n          expect(lookup('a')).to eql(\"Encrypted value 'a' (from global)\")\n        end\n\n        it 'delegates configured eyaml backend to eyaml_lookup_key function' do\n          expect(explain('a')).to match(/Hierarchy entry \"eyaml\"\\n.*\\n.*\\n.*\"common\"\\n\\s*Found key: \"a\"/m)\n        end\n\n        context 'using intepolated paths to the key pair' do\n          let(:scope_additions) { { 'priv_path' => private_key_path, 'pub_path' => public_key_path } }\n\n          let(:hiera_yaml) do\n            <<-YAML.unindent\n          :backends: eyaml\n          :eyaml:\n            :datadir: #{code_dir}/hieradata\n            :pkcs7_private_key: \"%{priv_path}\"\n            :pkcs7_public_key: \"%{pub_path}\"\n          :hierarchy:\n            - common\n            YAML\n          end\n\n          it 'finds data in the global layer' do\n            expect(lookup('a')).to eql(\"Encrypted value 'a' (from global)\")\n          end\n        end\n\n        context 'using options containing intepolated paths to the key pair' do\n          let(:scope_additions) { { 'priv_path' => private_key_path, 'pub_path' => public_key_path } }\n\n          let(:hiera_yaml) do\n            <<-YAML.unindent\n          version: 5\n          defaults:\n            datadir: #{code_dir}/hieradata\n          hierarchy:\n            - name: \"secret data\"\n              lookup_key: eyaml_lookup_key\n              path: common.eyaml\n              options:\n                pkcs7_private_key: \"%{priv_path}\"\n                pkcs7_public_key: \"%{pub_path}\"\n            YAML\n          end\n\n          it 'finds data in the global layer' do\n            expect(lookup('a')).to eql(\"Encrypted value 'a' (from global)\")\n          end\n        end\n\n        context 'with special extension declared in options' do\n          let(:environment_files) { {} }\n          let(:hiera_yaml) do\n            <<-YAML.unindent\n            :backends: eyaml\n            :eyaml:\n              :extension: xyaml\n              :datadir: #{code_dir}/hieradata\n              :pkcs7_private_key: #{private_key_path}\n              :pkcs7_public_key: #{public_key_path}\n            :hierarchy:\n              - common\n            YAML\n          end\n\n          let(:data_files) do\n            {\n              'common.xyaml' => data_file_content\n            }\n          end\n\n          it 'finds data in the global layer' do\n            expect(lookup('a')).to eql(\"Encrypted value 'a' (from global)\")\n          end\n\n          it 'delegates configured eyaml backend to eyaml_lookup_key function' do\n            expect(explain('a')).to match(/Hierarchy entry \"eyaml\"\\n.*\\n.*\\n.*\"common\"\\n\\s*Found key: \"a\"/m)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/lstrip_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the lstrip function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'removes leading whitepsace' do\n    expect(compile_to_catalog(\"notify { String(\\\"\\t\\n abc \\\".lstrip == 'abc '): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.lstrip == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns lstripped version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String([' a ', ' b ', ' c '].lstrip == ['a ', 'b ', 'c ']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns lstripped version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String([' a', ' b', ' c'].reverse_each.lstrip == ['c', 'b', 'a']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].lstrip\")}.to raise_error(/'lstrip' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/map_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the map method can' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n    it 'map on an array (multiplying each value by 2)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $a.map |$x|{ $x*2}.each |$v|{\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_4]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on an enumerable type (multiplying each value by 2)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = Integer[1,3]\n        $a.map |$x|{ $x*2}.each |$v|{\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_4]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on an integer (multiply each by 3)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        3.map |$x|{ $x*3}.each |$v|{\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_0]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on a string' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {a=>x, b=>y}\n        \"ab\".map |$x|{$a[$x]}.each |$v|{\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_x]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_y]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on an array (multiplying value by 10 in even index position)' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $a.map |$i, $x|{ if $i % 2 == 0 {$x} else {$x*10}}.each |$v|{\n          file { \"/file_$v\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_20]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on a hash selecting keys' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'a'=>1,'b'=>2,'c'=>3}\n      $a.map |$x|{ $x[0]}.each |$k|{\n          file { \"/file_$k\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_a]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_b]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_c]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on a hash selecting keys - using two block parameters' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'a'=>1,'b'=>2,'c'=>3}\n      $a.map |$k,$v|{ file { \"/file_$k\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_a]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_b]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_c]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'map on a hash using captures-last parameter' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'a'=>present,'b'=>absent,'c'=>present}\n      $a.map |*$kv|{ file { \"/file_${kv[0]}\": ensure => $kv[1] } }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_a]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_b]\").with_parameter(:ensure, 'absent')\n      expect(catalog).to have_resource(\"File[/file_c]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'each on a hash selecting value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'a'=>1,'b'=>2,'c'=>3}\n      $a.map |$x|{ $x[1]}.each |$k|{ file { \"/file_$k\": ensure => present } }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'each on a hash selecting value - using two block parameters' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n      $a = {'a'=>1,'b'=>2,'c'=>3}\n      $a.map |$k,$v|{ file { \"/file_$v\": ensure => present } }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n\n    context \"handles data type corner cases\" do\n      it \"map gets values that are false\" do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          $a = [false,false]\n          $a.map |$x| { $x }.each |$i, $v| {\n            file { \"/file_$i.$v\": ensure => present }\n          }\n        MANIFEST\n\n        expect(catalog).to have_resource(\"File[/file_0.false]\").with_parameter(:ensure, 'present')\n        expect(catalog).to have_resource(\"File[/file_1.false]\").with_parameter(:ensure, 'present')\n      end\n\n      it \"map gets values that are nil\" do\n        Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args|\n          [nil]\n        end\n        catalog = compile_to_catalog(<<-MANIFEST)\n          $a = nil_array()\n          $a.map |$x| { $x }.each |$i, $v| {\n            file { \"/file_$i.$v\": ensure => present }\n          }\n        MANIFEST\n\n        expect(catalog).to have_resource(\"File[/file_0.]\").with_parameter(:ensure, 'present')\n      end\n    end\n\n  it_should_behave_like 'all iterative functions argument checks', 'map'\n  it_should_behave_like 'all iterative functions hash handling', 'map'\nend\n"
  },
  {
    "path": "spec/unit/functions/match_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'the match function' do\n\n  before(:each) do\n    loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))\n    Puppet.push_context({:loaders => loaders}, \"test-examples\")\n  end\n\n  after(:each) do\n    Puppet.pop_context()\n  end\n\n  let(:func) do\n    Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'match')\n  end\n\n  let(:type_parser) { Puppet::Pops::Types::TypeParser.singleton }\n\n\n  it 'matches string and regular expression without captures' do\n    expect(func.call({}, 'abc123', /[a-z]+[1-9]+/)).to eql(['abc123'])\n  end\n\n  it 'matches string and regular expression with captures' do\n    expect(func.call({}, 'abc123', /([a-z]+)([1-9]+)/)).to eql(['abc123', 'abc', '123'])\n  end\n\n  it 'produces nil if match is not found' do\n    expect(func.call({}, 'abc123', /([x]+)([6]+)/)).to be_nil\n  end\n\n  [ 'Pattern[/([a-z]+)([1-9]+)/]',       # regexp\n    'Pattern[\"([a-z]+)([1-9]+)\"]',       # string\n    'Regexp[/([a-z]+)([1-9]+)/]',        # regexp type\n    'Pattern[/x9/, /([a-z]+)([1-9]+)/]', # regexp, first found matches\n  ].each do |pattern|\n    it \"matches string and type #{pattern} with captures\" do\n      expect(func.call({}, 'abc123', type(pattern))).to eql(['abc123', 'abc', '123'])\n    end\n\n    it \"matches string with an alias type for #{pattern} with captures\" do\n      expect(func.call({}, 'abc123', alias_type(\"MyAlias\", type(pattern)))).to eql(['abc123', 'abc', '123'])\n    end\n\n    it \"matches string with a  matching variant type for #{pattern} with captures\" do\n      expect(func.call({}, 'abc123', variant_type(type(pattern)))).to eql(['abc123', 'abc', '123'])\n    end\n\n  end\n\n  it 'matches an array of strings and yields a map of the result' do\n    expect(func.call({}, ['abc123', '2a', 'xyz2'], /([a-z]+)[1-9]+/)).to eql([['abc123', 'abc'], nil, ['xyz2', 'xyz']])\n  end\n\n  it 'raises error if Regexp type without regexp is used' do\n    expect{func.call({}, 'abc123', type('Regexp'))}.to raise_error(ArgumentError, /Given Regexp Type has no regular expression/)\n  end\n\n  def variant_type(*t)\n    Puppet::Pops::Types::PVariantType.new(t)\n  end\n\n  def alias_type(name, t)\n    # Create an alias using a nil AST (which is never used because it is given a type as resolution)\n    Puppet::Pops::Types::PTypeAliasType.new(name, nil, t)\n  end\n\n  def type(s)\n    Puppet::Pops::Types::TypeParser.singleton.parse(s)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/max_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the max function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  let(:logs) { [] }\n  let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n  it 'errors if not give at least one argument' do\n    expect{ compile_to_catalog(\"max()\") }.to raise_error(/Wrong number of arguments need at least one/)\n  end\n\n  context 'compares numbers' do\n    { [0, 1]    => 1,\n      [-1, 0]   => 0,\n      [-1.0, 0] => 0,\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares strings that are not numbers without deprecation warning' do\n    it \"string as number is deprecated\" do\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(compile_to_catalog(\"notify { String( max('a', 'b') == 'b'): }\")).to have_resource(\"Notify[true]\")\n      end\n      expect(warnings).to_not include(/auto conversion of .* is deprecated/)\n    end\n  end\n\n  context 'compares strings as numbers if possible and issues deprecation warning' do\n    {\n      [20, \"'100'\"]     => \"'100'\",\n      [\"'20'\", \"'100'\"] => \"'100'\",\n      [\"'20'\", 100]     => \"100\",\n      [20, \"'100x'\"]    => \"20\",\n      [\"20\", \"'100x'\"]  => \"20\",\n      [\"'20x'\", 100]    => \"'20x'\",\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n    {\n      [20, \"'1e2'\"] => \"'1e2'\",\n      [20, \"'1E2'\"] => \"'1E2'\",\n      [20, \"'10_0'\"] => \"'10_0'\",\n      [20, \"'100.0'\"] => \"'100.0'\",\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n  end\n\n  context 'compares semver' do\n    { [\"Semver('2.0.0')\", \"Semver('10.0.0')\"] => \"Semver('10.0.0')\",\n      [\"Semver('5.5.5')\", \"Semver('5.6.7')\"]  => \"Semver('5.6.7')\",\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares timespans' do\n    { [\"Timespan(2)\", \"Timespan(77.3)\"] => \"Timespan(77.3)\",\n      [\"Timespan('1-00:00:00')\", \"Timespan('2-00:00:00')\"]  => \"Timespan('2-00:00:00')\",\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares timestamps' do\n    { [\"Timestamp(0)\", \"Timestamp(298922400)\"] => \"Timestamp(298922400)\",\n      [\"Timestamp('1970-01-01T12:00:00.000')\", \"Timestamp('1979-06-22T18:00:00.000')\"]  => \"Timestamp('1979-06-22T18:00:00.000')\",\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares all except numeric and string by conversion to string and issues deprecation warning' do\n    {\n      [[20], \"'a'\"]                  => \"'a'\",            # after since '[' is before 'a'\n      [\"{'a' => 10}\", \"'a'\"]         => \"{'a' => 10}\",    # after since '{' is after 'a'\n      [false, 'fal']                 => \"false\",          # the boolean since text 'false' is longer\n      ['/b/', \"'(?-mix:c)'\"]         => \"'(?-mix:c)'\",    # because regexp to_s is a (?-mix:b) string\n      [\"Timestamp(1)\", \"'1980 a.d'\"] => \"'1980 a.d'\",     # because timestamp to_s is a date-time string here starting with 1970\n    }.each_pair do |values, expected|\n      it \"called as max(#{values[0]}, #{values[1]}) results in the value #{expected} and issues deprecation warning\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n  end\n\n  it \"accepts a lambda that takes over the comparison (here avoiding the string as number conversion)\" do\n    src = <<-SRC\n      $val = max(\"2\", \"10\") |$a, $b| { compare($a, $b) }\n      notify { String( $val == \"2\"): }\n    SRC\n    expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n  end\n\n  context 'compares entries in a single array argument as if they were splatted as individual args' do\n    {\n      [1,2,3] => 3,\n      [\"1\", \"2\",\"3\"] => \"'3'\",\n      [1, \"2\", 3] => 3,\n    }.each_pair do |value, expected|\n      it \"called as max(#{value}) results in the value #{expected}\" do\n        src = \"notify { String( max(#{value}) == #{expected}): }\"\n        expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n      end\n    end\n\n    {\n      [1,2,3] => 3,\n      [\"10\",\"2\",\"3\"] => \"'3'\",\n      [1,\"x\",3] => \"'x'\",\n    }.each_pair do |value, expected|\n      it \"called as max(#{value}) with a lambda using compare() results in the value #{expected}\" do\n        src = <<-\"SRC\"\n        function s_after_n($a,$b) {\n          case [$a, $b] {\n            [String, Numeric]: { 1 }\n            [Numeric, String]: { -1 }\n            default: { compare($a, $b) }\n          }\n        }\n        notify { String( max(#{value}) |$a,$b| {s_after_n($a,$b) } == #{expected}): }\n        SRC\n        expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/min_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the min function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  let(:logs) { [] }\n  let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n  it 'errors if not give at least one argument' do\n    expect{ compile_to_catalog(\"min()\") }.to raise_error(/Wrong number of arguments need at least one/)\n  end\n\n  context 'compares numbers' do\n    { [0, 1]    => 0,\n      [-1, 0]   => -1,\n      [-1.0, 0] => -1.0,\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares strings that are not numbers without deprecation warning' do\n    it \"string as number is deprecated\" do\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        expect(compile_to_catalog(\"notify { String( min('a', 'b') == 'a'): }\")).to have_resource(\"Notify[true]\")\n      end\n      expect(warnings).to_not include(/auto conversion of .* is deprecated/)\n    end\n  end\n\n  context 'compares strings as numbers if possible and outputs deprecation warning' do\n    {\n      [20, \"'100'\"] => 20,\n      [\"'20'\", \"'100'\"] => \"'20'\",\n      [\"'20'\", 100] => \"'20'\",\n      [20, \"'100x'\"] => \"'100x'\",\n      [\"20\", \"'100x'\"] => \"'100x'\",\n      [\"'20x'\", 100] => 100,\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n    {\n      [20, \"'1e2'\"] => 20,\n      [20, \"'1E2'\"] => 20,\n      [20, \"'10_0'\"] => 20,\n      [20, \"'100.0'\"] => 20,\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n\n  end\n\n  context 'compares semver' do\n    { [\"Semver('2.0.0')\", \"Semver('10.0.0')\"] => \"Semver('2.0.0')\",\n      [\"Semver('5.5.5')\", \"Semver('5.6.7')\"]  => \"Semver('5.5.5')\",\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares timespans' do\n    { [\"Timespan(2)\", \"Timespan(77.3)\"] => \"Timespan(2)\",\n      [\"Timespan('1-00:00:00')\", \"Timespan('2-00:00:00')\"]  => \"Timespan('1-00:00:00')\",\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares timestamps' do\n    { [\"Timestamp(0)\", \"Timestamp(298922400)\"] => \"Timestamp(0)\",\n      [\"Timestamp('1970-01-01T12:00:00.000')\", \"Timestamp('1979-06-22T18:00:00.000')\"]  => \"Timestamp('1970-01-01T12:00:00.000')\",\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'compares all except numeric and string by conversion to string (and issues deprecation warning)' do\n    {\n      [[20], \"'a'\"]                  => [20],             # before since '[' is before 'a'\n      [\"{'a' => 10}\", \"'|a'\"]         => \"{'a' => 10}\",   # before since '{' is before '|'\n      [false, 'fal']                 => \"'fal'\",          # 'fal' before since shorter than 'false'\n      ['/b/', \"'(?-mix:a)'\"]         => \"'(?-mix:a)'\",    # because regexp to_s is a (?-mix:b) string\n      [\"Timestamp(1)\", \"'1556 a.d'\"] => \"'1556 a.d'\",     # because timestamp to_s is a date-time string here starting with 1970\n    }.each_pair do |values, expected|\n      it \"called as min(#{values[0]}, #{values[1]}) results in the value #{expected} and issues deprecation warning\" do\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(compile_to_catalog(\"notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n        end\n        expect(warnings).to include(/auto conversion of .* is deprecated/)\n      end\n    end\n  end\n\n  it \"accepts a lambda that takes over the comparison (here avoiding the string as number conversion)\" do\n    src = <<-SRC\n      $val = min(\"2\", \"10\") |$a, $b| { compare($a, $b) }\n      notify { String( $val == \"10\"): }\n    SRC\n    expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n  end\n\n  context 'compares entries in a single array argument as if they were splatted as individual args' do\n    {\n      [1,2,3] => 1,\n      [\"1\", \"2\",\"3\"] => \"'1'\",\n      [1,\"2\",3] => 1,\n    }.each_pair do |value, expected|\n      it \"called as max(#{value}) results in the value #{expected}\" do\n        src = \"notify { String( min(#{value}) == #{expected}): }\"\n        expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n      end\n    end\n\n    {\n      [1,2,3] => 1,\n      [\"10\",\"2\",\"3\"] => \"'10'\",\n      [1,\"x\",3] => \"'x'\",\n    }.each_pair do |value, expected|\n      it \"called as max(#{value}) with a lambda using compare() results in the value #{expected}\" do\n        src = <<-\"SRC\"\n        function n_after_s($a,$b) {\n          case [$a, $b] {\n            [String, Numeric]: { -1 }\n            [Numeric, String]: { 1 }\n            default: { compare($a, $b) }\n          }\n        }\n        notify { String( min(#{value}) |$a,$b| {n_after_s($a,$b) } == #{expected}): }\n        SRC\n        expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/module_directory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\nrequire 'puppet_spec/files'\n\ndescribe 'the module_directory function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n  include PuppetSpec::Files\n\n  it 'returns first found module from one or more given names' do\n    mod = double('module')\n    allow(mod).to receive(:path).and_return('expected_path')\n    Puppet[:code] = \"notify { module_directory('one', 'two'):}\"\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    allow(compiler.environment).to receive(:module).with('one').and_return(nil)\n    allow(compiler.environment).to receive(:module).with('two').and_return(mod)\n    expect(compiler.compile()).to have_resource(\"Notify[expected_path]\")\n  end\n\n  it 'returns first found module from one or more given names in an array' do\n    mod = double('module')\n    allow(mod).to receive(:path).and_return('expected_path')\n    Puppet[:code] = \"notify { module_directory(['one', 'two']):}\"\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    allow(compiler.environment).to receive(:module).with('one').and_return(nil)\n    allow(compiler.environment).to receive(:module).with('two').and_return(mod)\n    expect(compiler.compile()).to have_resource(\"Notify[expected_path]\")\n  end\n\n  it 'returns undef when none of the modules were found' do\n    mod = double('module')\n    allow(mod).to receive(:path).and_return('expected_path')\n    Puppet[:code] = \"notify { String(type(module_directory('one', 'two'))):}\"\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    allow(compiler.environment).to receive(:module).with('one').and_return(nil)\n    allow(compiler.environment).to receive(:module).with('two').and_return(nil)\n    expect(compiler.compile()).to have_resource(\"Notify[Undef]\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/new_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the new function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'yields converted value if given a block' do\n    expect(compile_to_catalog(<<-MANIFEST\n      $x = Integer.new('42') |$x| { $x+2 }\n      notify { \"${type($x, generalized)}, $x\": }\n    MANIFEST\n    )).to have_resource('Notify[Integer, 44]')\n  end\n\n  it 'produces undef if given an undef value and type accepts it' do\n    expect(compile_to_catalog(<<-MANIFEST\n      $x = Optional[Integer].new(undef)\n      notify { \"one${x}word\": }\n    MANIFEST\n    )).to have_resource('Notify[oneword]')\n  end\n\n  it 'errors if given undef and type does not accept the value' do\n    expect{compile_to_catalog(<<-MANIFEST\n      $x = Integer.new(undef)\n      notify { \"one${x}word\": }\n    MANIFEST\n    )}.to raise_error(Puppet::Error, /of type Undef cannot be converted to Integer/)\n  end\n\n  it 'errors if converted value is not assignable to the type' do\n    expect{compile_to_catalog(<<-MANIFEST\n      $x = Integer[1,5].new('42')\n      notify { \"one${x}word\": }\n    MANIFEST\n    )}.to raise_error(Puppet::Error, /expects an Integer\\[1, 5\\] value, got Integer\\[42, 42\\]/)\n  end\n\n  it 'accepts and returns a second parameter that is an instance of the first, even when the type has no backing new_function' do\n    expect(eval_and_collect_notices(<<-MANIFEST)).to eql(%w(true true true true true true))\n      notice(undef == Undef(undef))\n\n      notice(default == Default(default))\n\n      notice(Any == Type(Any))\n\n      $b = Binary('YmluYXI=')\n      notice($b == Binary($b))\n\n      $t = Timestamp('2012-03-04T09:10:11.001')\n      notice($t == Timestamp($t))\n\n      type MyObject = Object[{attributes => {'type' => String}}]\n      $o = MyObject('Remote')\n      notice($o == MyObject($o))\n    MANIFEST\n  end\n\n  context 'when invoked on NotUndef' do\n    it 'produces an instance of the NotUndef nested type' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = NotUndef[Integer].new(42)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 42]')\n    end\n\n    it 'produces the given value when there is no type specified' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = NotUndef.new(42)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 42]')\n    end\n  end\n\n  context 'when invoked on an Integer' do\n    it 'produces 42 when given the integer 42' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Integer.new(42)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 42]')\n    end\n\n    it 'produces 3 when given the float 3.1415' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Integer.new(3.1415)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 3]')\n    end\n\n    it 'produces 0 from false' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Integer.new(false)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 0]')\n    end\n\n    it 'produces 1 from true' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Integer.new(true)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 1]')\n    end\n\n    it \"produces an absolute value when third argument is 'true'\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Integer.new(-42, 10, true))\n      MANIFEST\n      )).to eql(['42'])\n    end\n\n    it \"does not produce an absolute value when third argument is 'false'\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Integer.new(-42, 10, false))\n      MANIFEST\n      )).to eql(['-42'])\n    end\n\n    it \"produces an absolute value from hash {from => val, abs => true}\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Integer.new({from => -42, abs => true}))\n      MANIFEST\n      )).to eql(['42'])\n    end\n\n    it \"does not produce an absolute value from hash {from => val, abs => false}\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Integer.new({from => -42, abs => false}))\n      MANIFEST\n      )).to eql(['-42'])\n    end\n\n    context 'when prefixed by a sign' do\n      { '+1'     => 1,\n        '-1'     => -1,\n        '+ 1'    => 1,\n        '- 1'    => -1,\n        '+0x10'  => 16,\n        '+ 0x10' => 16,\n        '-0x10'  => -16,\n        '- 0x10' => -16\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\")\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n    end\n\n    context \"when radix is not set it uses default and\" do\n      { \"10\"     => 10,\n        \"010\"    => 8,\n        \"0x10\"   => 16,\n        \"0X10\"   => 16,\n        '0B111'  => 7,\n        '0b111'  => 7\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\")\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n\n      { '0x0G'  => :error,\n        '08'    => :error,\n        '10F'   => :error,\n        '0B2'   => :error,\n      }.each do |str, result|\n        it \"errors when given a non Integer compliant string '#{str}'\" do\n          expect{compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\")\n          MANIFEST\n        )}.to raise_error(Puppet::Error, /invalid value|cannot be converted to Integer/)\n        end\n      end\n    end\n\n    context \"when radix is explicitly set to 'default' it\" do\n      { \"10\"     => 10,\n        \"010\"    => 8,\n        \"0x10\"   => 16,\n        \"0X10\"   => 16,\n        '0B111'  => 7,\n        '0b111'  => 7\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", default)\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n    end\n\n    context \"when radix is explicitly set to '2' it\" do\n      { \"10\"     => 2,\n        \"010\"    => 2,\n        \"00010\"  => 2,\n        '0B111'  => 7,\n        '0b111'  => 7,\n        '+0B111' => 7,\n        '-0b111' => -7,\n        '+ 0B111'=> 7,\n        '- 0b111'=> -7\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 2)\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n\n      { '0x10'  => :error,\n        '0X10'  => :error,\n        '+0X10' => :error,\n        '-0X10' => :error,\n        '+ 0X10'=> :error,\n        '- 0X10'=> :error\n      }.each do |str, result|\n        it \"errors when given the non binary value compliant string '#{str}'\" do\n          expect{compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 2)\n          MANIFEST\n        )}.to raise_error(Puppet::Error, /invalid value/)\n        end\n      end\n    end\n\n    context \"when radix is explicitly set to '8' it\" do\n      { \"10\"     => 8,\n        \"010\"    => 8,\n        \"00010\"  => 8,\n        '+00010' => 8,\n        '-00010' => -8,\n        '+ 00010'=> 8,\n        '- 00010'=> -8,\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 8)\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n\n      { \"0x10\"  => :error,\n        '0X10'  => :error,\n        '0B10'  => :error,\n        '0b10'  => :error,\n        '+0b10' => :error,\n        '-0b10' => :error,\n        '+ 0b10'=> :error,\n        '- 0b10'=> :error,\n      }.each do |str, result|\n        it \"errors when given the non octal value compliant string '#{str}'\" do\n          expect{compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 8)\n          MANIFEST\n        )}.to raise_error(Puppet::Error, /invalid value/)\n        end\n      end\n    end\n\n    context \"when radix is explicitly set to '16' it\" do\n      { \"10\"     => 16,\n        \"010\"    => 16,\n        \"00010\"  => 16,\n        \"0x10\"   => 16,\n        \"0X10\"   => 16,\n        \"0b1\"    => 16*11+1,\n        \"0B1\"    => 16*11+1,\n        '+0B1'   => 16*11+1,\n        '-0B1'   => -16*11-1,\n        '+ 0B1'  => 16*11+1,\n        '- 0B1'  => -16*11-1,\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 16)\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n\n      { '0XGG'  => :error,\n        '+0XGG' => :error,\n        '-0XGG' => :error,\n        '+ 0XGG'=> :error,\n        '- 0XGG'=> :error,\n      }.each do |str, result|\n        it \"errors when given the non hexadecimal value compliant string '#{str}'\" do\n          expect{compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 8)\n          MANIFEST\n        )}.to raise_error(Puppet::Error, /The string '#{Regexp.escape(str)}' cannot be converted to Integer/)\n        end\n      end\n    end\n\n    context \"when radix is explicitly set to '10' it\" do\n      { \"10\"     => 10,\n        \"010\"    => 10,\n        \"00010\"  => 10,\n        \"08\"     => 8,\n        \"0008\"   => 8,\n      }.each do |str, result|\n        it \"produces #{result} from the string '#{str}'\" do\n          expect(compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 10)\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          )).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n\n      { '0X10'  => :error,\n        '0b10'  => :error,\n        '0B10'  => :error,\n      }.each do |str, result|\n        it \"errors when given the non binary value compliant string '#{str}'\" do\n          expect{compile_to_catalog(<<-\"MANIFEST\"\n            $x = Integer.new(\"#{str}\", 10)\n          MANIFEST\n        )}.to raise_error(Puppet::Error, /invalid value/)\n        end\n      end\n    end\n\n    context \"input can be given in long form \" do\n      { {'from' => \"10\", 'radix' => 2}     => 2,\n        {'from' => \"10\", 'radix' => 8}     => 8,\n        {'from' => \"10\", 'radix' => 10}    => 10,\n        {'from' => \"10\", 'radix' => 16}    => 16,\n        {'from' => \"10\", 'radix' => :default}    => 10,\n      }.each do |options, result|\n        it \"produces #{result} from the long form '#{options}'\" do\n          src = <<-\"MANIFEST\"\n            $x = Integer.new(#{options.to_s.gsub(/:/, '')})\n            notify { \"${type($x, generalized)}, $x\": }\n          MANIFEST\n          expect(compile_to_catalog(src)).to have_resource(\"Notify[Integer, #{result}]\")\n        end\n      end\n    end\n\n    context 'errors when' do\n      it 'radix is wrong and when given directly' do\n        expect{compile_to_catalog(<<-\"MANIFEST\"\n          $x = Integer.new('10', 3)\n        MANIFEST\n      )}.to raise_error(Puppet::Error, /Illegal radix/)\n      end\n\n      it 'radix is wrong and when given in long form' do\n        expect{compile_to_catalog(<<-\"MANIFEST\"\n          $x = Integer.new({from =>'10', radix=>3})\n        MANIFEST\n      )}.to raise_error(Puppet::Error, /Illegal radix/)\n      end\n\n      it 'value is not numeric and given directly' do\n        expect{compile_to_catalog(<<-\"MANIFEST\"\n          $x = Integer.new('eleven', 10)\n        MANIFEST\n      )}.to raise_error(Puppet::Error, /The string 'eleven' cannot be converted to Integer/)\n      end\n\n      it 'value is not numeric and given in long form' do\n        expect{compile_to_catalog(<<-\"MANIFEST\"\n      $x = Integer.new({from => 'eleven', radix => 10})\n        MANIFEST\n      )}.to raise_error(Puppet::Error, /The string 'eleven' cannot be converted to Integer/)\n      end\n    end\n  end\n\n  context 'when invoked on Numeric' do\n    { 42 => \"Notify[Integer, 42]\",\n      42.3 => \"Notify[Float, 42.3]\",\n      \"42.0\" => \"Notify[Float, 42.0]\",\n      \"+42.0\" => \"Notify[Float, 42.0]\",\n      \"-42.0\" => \"Notify[Float, -42.0]\",\n      \"+ 42.0\" => \"Notify[Float, 42.0]\",\n      \"- 42.0\" => \"Notify[Float, -42.0]\",\n      \"42.3\" => \"Notify[Float, 42.3]\",\n      \"0x10\" => \"Notify[Integer, 16]\",\n      \"010\" => \"Notify[Integer, 8]\",\n      \"0.10\" => \"Notify[Float, 0.1]\",\n      \"0b10\" => \"Notify[Integer, 2]\",\n      \"0\"    => \"Notify[Integer, 0]\",\n      false => \"Notify[Integer, 0]\",\n      true => \"Notify[Integer, 1]\",\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect}\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Numeric.new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    it \"produces a result when long from hash {from => val} is used\" do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Numeric.new({from=>'42'})\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Integer, 42]')\n    end\n\n    it \"produces an absolute value when second argument is 'true'\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Numeric.new(-42.3, true))\n      MANIFEST\n      )).to eql(['42.3'])\n    end\n\n    it \"does not produce an absolute value when second argument is 'false'\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Numeric.new(-42.3, false))\n      MANIFEST\n      )).to eql(['-42.3'])\n    end\n\n    it \"produces an absolute value from hash {from => val, abs => true}\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Numeric.new({from => -42.3, abs => true}))\n      MANIFEST\n      )).to eql(['42.3'])\n    end\n\n    it \"does not produce an absolute value from hash {from => val, abs => false}\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Numeric.new({from => -42.3, abs => false}))\n      MANIFEST\n      )).to eql(['-42.3'])\n    end\n  end\n\n  context 'when invoked on Float' do\n    { 42     => \"Notify[Float, 42.0]\",\n      42.3   => \"Notify[Float, 42.3]\",\n      \"42.0\" => \"Notify[Float, 42.0]\",\n      \"+42.0\" => \"Notify[Float, 42.0]\",\n      \"-42.0\" => \"Notify[Float, -42.0]\",\n      \"+ 42.0\" => \"Notify[Float, 42.0]\",\n      \"- 42.0\" => \"Notify[Float, -42.0]\",\n      \"42.3\" => \"Notify[Float, 42.3]\",\n      \"0x10\" => \"Notify[Float, 16.0]\",\n      \"010\"  => \"Notify[Float, 10.0]\",\n      \"0.10\" => \"Notify[Float, 0.1]\",\n      false  => \"Notify[Float, 0.0]\",\n      true   => \"Notify[Float, 1.0]\",\n      '0b10'  => \"Notify[Float, 2.0]\",\n      '0B10'  => \"Notify[Float, 2.0]\",\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect}\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Float.new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    it \"produces a result when long from hash {from => val} is used\" do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Float.new({from=>42})\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Float, 42.0]')\n    end\n\n    it \"produces an absolute value when second argument is 'true'\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Float.new(-42.3, true))\n      MANIFEST\n      )).to eql(['42.3'])\n    end\n\n    it \"does not produce an absolute value when second argument is 'false'\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Float.new(-42.3, false))\n      MANIFEST\n      )).to eql(['-42.3'])\n    end\n\n    it \"produces an absolute value from hash {from => val, abs => true}\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Float.new({from => -42.3, abs => true}))\n      MANIFEST\n      )).to eql(['42.3'])\n    end\n\n    it \"does not produce an absolute value from hash {from => val, abs => false}\" do\n      expect(eval_and_collect_notices(<<-MANIFEST\n        notice(Float.new({from => -42.3, abs => false}))\n      MANIFEST\n      )).to eql(['-42.3'])\n    end\n  end\n\n  context 'when invoked on Boolean' do\n    { true     => 'Notify[Boolean, true]',\n      false    => 'Notify[Boolean, false]',\n      0        => 'Notify[Boolean, false]',\n      1        => 'Notify[Boolean, true]',\n      0.0      => 'Notify[Boolean, false]',\n      1.0      => 'Notify[Boolean, true]',\n\n      'true'   => 'Notify[Boolean, true]',\n      'TrUe'   => 'Notify[Boolean, true]',\n      'yes'    => 'Notify[Boolean, true]',\n      'YeS'    => 'Notify[Boolean, true]',\n      'y'      => 'Notify[Boolean, true]',\n      'Y'      => 'Notify[Boolean, true]',\n\n      'false'  => 'Notify[Boolean, false]',\n      'no'     => 'Notify[Boolean, false]',\n      'n'      => 'Notify[Boolean, false]',\n      'FalSE'  => 'Notify[Boolean, false]',\n      'nO'     => 'Notify[Boolean, false]',\n      'N'      => 'Notify[Boolean, false]',\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect}\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Boolean.new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    it \"errors when given an non boolean representation like the string 'hello'\" do\n      expect{compile_to_catalog(<<-\"MANIFEST\"\n        $x = Boolean.new('hello')\n      MANIFEST\n      )}.to raise_error(Puppet::Error, /The string 'hello' cannot be converted to Boolean/)\n    end\n\n    it \"does not convert an undef (as may be expected, but is handled as every other undef)\" do\n      expect{compile_to_catalog(<<-\"MANIFEST\"\n        $x = Boolean.new(undef)\n      MANIFEST\n      )}.to raise_error(Puppet::Error, /of type Undef cannot be converted to Boolean/)\n    end\n  end\n\n  context 'when invoked on Array' do\n    { []            => 'Notify[Array[Unit], []]',\n      [true]        => 'Notify[Array[Boolean], [true]]',\n      {'a'=>true, 'b' => false}   => 'Notify[Array[Array[ScalarData]], [[a, true], [b, false]]]',\n      'abc'         => 'Notify[Array[String[1, 1]], [a, b, c]]',\n      3             => 'Notify[Array[Integer], [0, 1, 2]]',\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect} and wrap is not given\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Array.new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    {\n      true          => /of type Boolean cannot be converted to Array/,\n      42.3          => /of type Float cannot be converted to Array/,\n    }.each do |input, error_match|\n      it \"errors when given an non convertible #{input.inspect} when wrap is not given\" do\n        expect{compile_to_catalog(<<-\"MANIFEST\"\n          $x = Array.new(#{input.inspect})\n        MANIFEST\n        )}.to raise_error(Puppet::Error, error_match)\n      end\n    end\n\n    { []            => 'Notify[Array[Unit], []]',\n      [true]        => 'Notify[Array[Boolean], [true]]',\n      {'a'=>true}   => 'Notify[Array[Hash[String, Boolean]], [{a => true}]]',\n      'hello'       => 'Notify[Array[String], [hello]]',\n      true          => 'Notify[Array[Boolean], [true]]',\n      42            => 'Notify[Array[Integer], [42]]',\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect} and wrap is given\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Array.new(#{input.inspect}, true)\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    it 'produces an array of byte integer values when given a Binary' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Array.new(Binary('ABC', '%s'))\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Array[Integer], [65, 66, 67]]')\n    end\n\n    it 'wraps a binary when given extra argument true' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Array[Any].new(Binary('ABC', '%s'), true)\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Array[Binary], [QUJD]]')\n    end\n  end\n\n  context 'when invoked on Tuple' do\n    { 'abc'         => 'Notify[Array[String[1, 1]], [a, b, c]]',\n      3             => 'Notify[Array[Integer], [0, 1, 2]]',\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect} and wrap is not given\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Tuple[Any,3].new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    it \"errors when tuple requirements are not met\" do\n      expect{compile_to_catalog(<<-\"MANIFEST\"\n        $x = Tuple[Integer,6].new(3)\n      MANIFEST\n      )}.to raise_error(Puppet::Error, /expects size to be at least 6, got 3/)\n    end\n  end\n\n  context 'when invoked on Hash' do\n    { {}            => 'Notify[Hash[0, 0], {}]',\n      []            => 'Notify[Hash[0, 0], {}]',\n      {'a'=>true}   => 'Notify[Hash[String, Boolean], {a => true}]',\n      [1,2,3,4]     => 'Notify[Hash[Integer, Integer], {1 => 2, 3 => 4}]',\n      [[1,2],[3,4]] => 'Notify[Hash[Integer, Integer], {1 => 2, 3 => 4}]',\n      'abcd'        => 'Notify[Hash[String[1, 1], String[1, 1]], {a => b, c => d}]',\n      4             => 'Notify[Hash[Integer, Integer], {0 => 1, 2 => 3}]',\n\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect}\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Hash.new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    { true             => /Value of type Boolean cannot be converted to Hash/,\n      [1,2,3]          => /odd number of arguments for Hash/,\n    }.each do |input, error_match|\n      it \"errors when given an non convertible #{input.inspect}\" do\n        expect{compile_to_catalog(<<-\"MANIFEST\"\n          $x = Hash.new(#{input.inspect})\n        MANIFEST\n        )}.to raise_error(Puppet::Error, error_match)\n      end\n    end\n\n    context 'when using the optional \"tree\" format' do\n      it 'can convert a tree in flat form to a hash' do\n        expect(compile_to_catalog(<<-\"MANIFEST\"\n          $x = Hash.new([[[0], a],[[1,0], b],[[1,1], c],[[2,0], d]], tree)\n          notify { test: message => $x }\n        MANIFEST\n        )).to have_resource('Notify[test]').with_parameter(:message, { 0 => 'a', 1 => { 0 => 'b', 1=> 'c'}, 2 => {0 => 'd'} })\n      end\n\n      it 'preserves array in flattened tree but overwrites entries if they are present' do\n        expect(compile_to_catalog(<<-\"MANIFEST\"\n          $x = Hash.new([[[0], a],[[1,0], b],[[1,1], c],[[2], [overwritten, kept]], [[2,0], d]], tree)\n          notify { test: message => $x }\n        MANIFEST\n        )).to have_resource('Notify[test]').with_parameter(:message, { 0 => 'a', 1 => { 0 => 'b', 1=> 'c'}, 2 => ['d', 'kept'] })\n      end\n\n      it 'preserves hash in flattened tree but overwrites entries if they are present' do\n        expect(compile_to_catalog(<<-\"MANIFEST\"\n          $x = Hash.new([[[0], a],[[1,0], b],[[1,1], c],[[2], {0 => 0, kept => 1}], [[2,0], d]], tree)\n          notify { test: message => $x }\n        MANIFEST\n  )).to have_resource('Notify[test]').with_parameter(:message, { 0 => 'a', 1 => { 0 => 'b', 1=> 'c'}, 2 => {0=>'d', 'kept'=>1} })\n      end\n    end\n\n    context 'when using the optional \"tree_hash\" format' do\n      it 'turns array in flattened tree into hash' do\n        expect(compile_to_catalog(<<-\"MANIFEST\"\n          $x = Hash.new([[[0], a],[[1,0], b],[[1,1], c],[[2], [overwritten, kept]], [[2,0], d]], hash_tree)\n          notify { test: message => $x }\n        MANIFEST\n        )).to have_resource('Notify[test]').with_parameter(:message, { 0=>'a', 1=>{ 0=>'b', 1=>'c'}, 2=>{0=>'d', 1=>'kept'}})\n      end\n    end\n  end\n\n  context 'when invoked on Struct' do\n    { {'a' => 2}      => 'Notify[Struct[{\\'a\\' => Integer[2, 2]}], {a => 2}]',\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect}\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = Struct[{a => Integer[2]}].new(#{input.inspect})\n          notify { \"${type($x)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n\n    it \"errors when tuple requirements are not met\" do\n      expect{compile_to_catalog(<<-\"MANIFEST\"\n        $x = Struct[{a => Integer[2]}].new({a => 0})\n      MANIFEST\n      )}.to raise_error(Puppet::Error, /entry 'a' expects an Integer\\[2\\]/)\n    end\n  end\n\n  context 'when invoked on String' do\n    { {}            => 'Notify[String, {}]',\n      []            => 'Notify[String, []]',\n      {'a'=>true}   => \"Notify[String, {'a' => true}]\",\n      [1,2,3,4]     => 'Notify[String, [1, 2, 3, 4]]',\n      [[1,2],[3,4]] => 'Notify[String, [[1, 2], [3, 4]]]',\n      'abcd'        => 'Notify[String, abcd]',\n      4             => 'Notify[String, 4]',\n    }.each do |input, result|\n      it \"produces #{result} when given the value #{input.inspect}\" do\n        expect(compile_to_catalog(<<-MANIFEST\n          $x = String.new(#{input.inspect})\n          notify { \"${type($x, generalized)}, $x\": }\n        MANIFEST\n        )).to have_resource(result)\n      end\n    end\n  end\n\n  context 'when invoked on a type alias' do\n    it 'delegates the new to the aliased type' do\n      expect(compile_to_catalog(<<-MANIFEST\n        type X = Boolean\n        $x = X.new('yes')\n        notify { \"${type($x, generalized)}, $x\": }\n      MANIFEST\n      )).to have_resource('Notify[Boolean, true]')\n    end\n  end\n\n  context 'when invoked on a Type' do\n    it 'creates a Type from its string representation' do\n      expect(compile_to_catalog(<<-MANIFEST\n        $x = Type.new('Integer[3,10]')\n        notify { \"${type($x)}\": }\n      MANIFEST\n      )).to have_resource('Notify[Type[Integer[3, 10]]]')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/next_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the next function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'exits a block yielded to iteratively' do\n    it 'with a given value as result for this iteration' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[100, 4, 6]]')\n          $result = String([1,2,3].map |$x| { if $x == 1 { next(100) } $x*2 })\n          notify { $result: }\n        CODE\n    end\n\n    it 'with undef value as result for this iteration when next is not given an argument' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[undef, 4, 6]]')\n          $result = String([1,2,3].map |$x| { if $x == 1 { next() } $x*2 })\n          notify { $result: }\n        CODE\n    end\n  end\n\n  it 'can be called without parentheses around the argument' do\n    expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[100, 4, 6]]')\n        $result = String([1,2,3].map |$x| { if $x == 1 { next 100 } $x*2 })\n        notify { $result: }\n      CODE\n  end\n\n  it 'has the same effect as a return when called from within a block not used in an iteration' do\n    expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[100]')\n        $result = String(with(1) |$x| { if $x == 1 { next(100) } 200 })\n        notify { $result: }\n      CODE\n  end\n\n  it 'has the same effect as a return when called from within a function' do\n    expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[[102, 200, 300]]')\n        function do_next() {\n          next(100)\n        }\n        $result = String([1,2,3].map |$x| { if $x == 1 { next do_next()+2 } $x*do_next() })\n        notify { $result: }\n      CODE\n  end\n\n  it 'provides early exit from a class and keeps the class' do\n    expect(eval_and_collect_notices(<<-CODE)).to eql(['a', 'c', 'true', 'true'])\n        class notices_c { notice 'c' }\n        class does_next {\n          notice 'a'\n          if 1 == 1 { next() } # avoid making next line statically unreachable\n          notice 'b'\n        }\n        # include two classes to check that next does not do an early return from\n        # the include function.\n        include(does_next, notices_c)\n        notice defined(does_next)\n        notice defined(notices_c)\n      CODE\n  end\n\n  it 'provides early exit from a user defined resource and keeps the resource' do\n    expect(eval_and_collect_notices(<<-CODE)).to eql(['the_doer_of_next', 'copy_cat', 'true', 'true'])\n        define does_next {\n          notice $title\n          if 1 == 1 { next() } # avoid making next line statically unreachable\n          notice 'b'\n        }\n        define checker {\n          notice defined(Does_next['the_doer_of_next'])\n          notice defined(Does_next['copy_cat'])\n        }\n        # create two instances to ensure next does not break the entire\n        # resource expression\n        does_next { ['the_doer_of_next', 'copy_cat']: }\n        checker { 'needed_because_evaluation_order': }\n      CODE\n  end\n\n  it 'can not be called from top scope' do\n    expect do\n      compile_to_catalog(<<-CODE)\n        # line 1\n        # line 2\n        next()\n      CODE\n    end.to raise_error(/next\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/partition_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the partition function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an array' do\n    it 'partitions by item' do\n      manifest = \"notify { String(partition(['', b, ab]) |$s| { $s.empty }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[[[''], ['b', 'ab']]]\")\n    end\n\n    it 'partitions by index, item' do\n      manifest = \"notify { String(partition(['', b, ab]) |$i, $s| { $i == 2 or $s.empty }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[[['', 'ab'], ['b']]]\")\n    end\n  end\n\n  context 'for a hash' do\n    it 'partitions by key-value pair' do\n      manifest = \"notify { String(partition(a => [1, 2], b => []) |$kv| { $kv[1].empty }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[[[['b', []]], [['a', [1, 2]]]]]\")\n    end\n\n    it 'partitions by key, value' do\n      manifest = \"notify { String(partition(a => [1, 2], b => []) |$k, $v| { $v.empty }): }\"\n      expect(compile_to_catalog(manifest)).to have_resource(\"Notify[[[['b', []]], [['a', [1, 2]]]]]\")\n    end\n  end\n\n  context 'for a string' do\n    it 'fails' do\n      manifest = \"notify { String(partition('something') |$s| { $s.empty }): }\"\n      expect { compile_to_catalog(manifest) }.to raise_error(Puppet::PreformattedError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/reduce_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the reduce method' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  before :each do\n    node      = Puppet::Node.new(\"floppy\", :environment => 'production')\n    @compiler = Puppet::Parser::Compiler.new(node)\n    @scope    = Puppet::Parser::Scope.new(@compiler)\n    @topscope = @scope.compiler.topscope\n    @scope.parent = @topscope\n  end\n\n  context \"should be callable as\" do\n    it 'reduce on an array' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $b = $a.reduce |$memo, $x| { $memo + $x }\n        file { \"/file_$b\": ensure => present }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'reduce on an array with captures rest in lambda' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $b = $a.reduce |*$mx| { $mx[0] + $mx[1] }\n        file { \"/file_$b\": ensure => present }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'reduce on enumerable type' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = Integer[1,3]\n        $b = $a.reduce |$memo, $x| { $memo + $x }\n        file { \"/file_$b\": ensure => present }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'reduce on an array with start value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $b = $a.reduce(4) |$memo, $x| { $memo + $x }\n        file { \"/file_$b\": ensure => present }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_10]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'reduce on a hash' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {a=>1, b=>2, c=>3}\n        $start = [ignored, 4]\n        $b = $a.reduce |$memo, $x| {['sum', $memo[1] + $x[1]] }\n        file { \"/file_${$b[0]}_${$b[1]}\": ensure => present }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_sum_6]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'reduce on a hash with start value' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {a=>1, b=>2, c=>3}\n        $start = ['ignored', 4]\n        $b = $a.reduce($start) |$memo, $x| { ['sum', $memo[1] + $x[1]] }\n        file { \"/file_${$b[0]}_${$b[1]}\": ensure => present }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_sum_10]\").with_parameter(:ensure, 'present')\n    end\n  end\n\n  it_should_behave_like 'all iterative functions argument checks', 'reduce'\n\nend\n"
  },
  {
    "path": "spec/unit/functions/regsubst_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'the regsubst function' do\n\n  before(:each) do\n    loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))\n    Puppet.push_context({:loaders => loaders}, \"test-examples\")\n  end\n\n  after(:each) do\n    Puppet.pop_context()\n  end\n\n  def regsubst(*args)\n    Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'regsubst').call({}, *args)\n  end\n\n  let(:type_parser) { Puppet::Pops::Types::TypeParser.singleton }\n\n  context 'when using a string pattern' do\n    it 'should raise an Error if there is less than 3 arguments' do\n      expect { regsubst('foo', 'bar') }.to raise_error(/expects between 3 and 5 arguments, got 2/)\n    end\n\n    it 'should raise an Error if there is more than 5 arguments' do\n      expect { regsubst('foo', 'bar', 'gazonk', 'G', 'U', 'y') }.to raise_error(/expects between 3 and 5 arguments, got 6/)\n    end\n\n    it 'should raise an Error if given a bad flag' do\n      expect { regsubst('foo', 'bar', 'gazonk', 'X') }.to raise_error(/parameter 'flags' expects an undef value or a match for Pattern\\[\\/\\^\\[GEIM\\]\\*\\$\\/\\], got 'X'/)\n    end\n\n    it 'should raise an Error if given a bad encoding' do\n      expect { regsubst('foo', 'bar', 'gazonk', nil, 'X') }.to raise_error(/parameter 'encoding' expects a match for Enum\\['E', 'N', 'S', 'U'\\], got 'X'/)\n    end\n\n    it 'should raise an Error if given a bad regular expression' do\n      expect { regsubst('foo', '(', 'gazonk') }.to raise_error(/pattern with unmatched parenthesis/)\n    end\n\n    it 'should handle case insensitive flag' do\n      expect(regsubst('the monkey breaks baNAna trees', 'b[an]+a', 'coconut', 'I')).to eql('the monkey breaks coconut trees')\n    end\n\n    it 'should allow hash as replacement' do\n      expect(regsubst('tuto', '[uo]', { 'u' => 'o', 'o' => 'u' }, 'G')).to eql('totu')\n    end\n  end\n\n  context 'when using a regexp pattern' do\n    it 'should raise an Error if there is less than 3 arguments' do\n      expect { regsubst('foo', /bar/) }.to raise_error(/expects between 3 and 5 arguments, got 2/)\n    end\n\n    it 'should raise an Error if there is more than 5 arguments' do\n      expect { regsubst('foo', /bar/, 'gazonk', 'G', 'E', 'y') }.to raise_error(/expects between 3 and 5 arguments, got 6/)\n    end\n\n    it 'should raise an Error if given a flag other thant G' do\n      expect { regsubst('foo', /bar/, 'gazonk', 'I') }.to raise_error(/expects one of/)\n    end\n\n    it 'should handle global substitutions' do\n      expect(regsubst(\"the monkey breaks\\tbanana trees\", /[ \\t]/, '--', 'G')).to eql('the--monkey--breaks--banana--trees')\n    end\n\n    it 'should accept Type[Regexp]' do\n      expect(regsubst('abc', type_parser.parse(\"Regexp['b']\"), '_')).to eql('a_c')\n    end\n\n    it 'should treat Regexp as Regexp[//]' do\n      expect(regsubst('abc', type_parser.parse(\"Regexp\"), '_', 'G')).to eql('_a_b_c_')\n    end\n\n    it 'should allow hash as replacement' do\n      expect(regsubst('tuto', /[uo]/, { 'u' => 'o', 'o' => 'u' }, 'G')).to eql('totu')\n    end\n  end\n\n  context 'when using an array target' do\n\n    it 'should perform substitutions in all elements and return array when using regexp pattern' do\n      expect(regsubst(['a#a', 'b#b', 'c#c'], /#/, '_')).to eql(['a_a', 'b_b', 'c_c'])\n    end\n\n    it 'should perform substitutions in all elements when using string pattern' do\n      expect(regsubst(['a#a', 'b#b', 'c#c'], '#', '_')).to eql(['a_a', 'b_b', 'c_c'])\n    end\n\n    it 'should perform substitutions in all elements when using Type[Regexp] pattern' do\n      expect(regsubst(['a#a', 'b#b', 'c#c'], type_parser.parse('Regexp[/#/]'), '_')).to eql(['a_a', 'b_b', 'c_c'])\n    end\n\n    it 'should handle global substitutions with groups on all elements' do\n      expect(regsubst(\n                 ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'],\n                 /([^.]+)/,\n                 '<\\1>',\n                 'G')\n      ).to eql(['<130>.<236>.<254>.<10>', '<foo>.<example>.<com>','<coconut>', '<10>.<20>.<30>.<40>'])\n    end\n\n    it 'should return an empty array if given an empty array and string pattern' do\n      expect(regsubst([], '', '')).to eql([])\n    end\n\n    it 'should return an empty array if given an empty array and regexp pattern' do\n      expect(regsubst([], //, '')).to eql([])\n    end\n\n  end\n\n  context 'when using a Target of Type sensitive String' do\n    it 'should process it' do\n      result = regsubst(Puppet::Pops::Types::PSensitiveType::Sensitive.new('very secret'), 'very', 'top')\n      expect(result).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      expect(result.unwrap).to eq(\"top secret\")\n    end\n  end\n\n  context 'when using a Target of Type Array with mixed String and sensitive String' do\n    it 'should process it' do\n      my_array = ['very down', Puppet::Pops::Types::PSensitiveType::Sensitive.new('very secret')]\n      expect(regsubst(my_array, 'very', 'top')).to be_a(Array)\n      expect(regsubst(my_array, 'very', 'top')[0]).to eq('top down')\n      result = regsubst(my_array, 'very', 'top')[1]\n      expect(result).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      expect(result.unwrap).to eq('top secret')\n    end\n  end\n\n  context 'when using a Target of Type Sensitive Array with mixed String and sensitive String' do\n    it 'should process it' do\n      my_array = Puppet::Pops::Types::PSensitiveType::Sensitive.new(['very down', Puppet::Pops::Types::PSensitiveType::Sensitive.new('very secret')])\n      expect(regsubst(my_array, 'very', 'top')).to be_a(Array)\n      expect(regsubst(my_array, 'very', 'top')[0]).to eq('top down')\n      result = regsubst(my_array, 'very', 'top')[1]\n      expect(result).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n      expect(result.unwrap).to eq('top secret')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/require_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/parser/functions'\nrequire 'matchers/containment_matchers'\nrequire 'matchers/resource'\nrequire 'matchers/include_in_order'\nrequire 'unit/functions/shared'\n\ndescribe 'The \"require\" function' do\n  include PuppetSpec::Compiler\n  include ContainmentMatchers\n  include Matchers::Resource\n\n  before(:each) do\n    compiler  = Puppet::Parser::Compiler.new(Puppet::Node.new(\"foo\"))\n    @scope = compiler.topscope\n  end\n\n  it 'includes a class that is not already included' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class required {\n        notify { \"required\": }\n      }\n      require required\n    MANIFEST\n\n    expect(catalog.classes).to include(\"required\")\n  end\n\n  it 'sets the require attribute on the requiring resource' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class required {\n        notify { \"required\": }\n      }\n      class requiring {\n        require required\n      }\n      include requiring\n    MANIFEST\n\n    requiring = catalog.resource(\"Class\", \"requiring\")\n    expect(requiring[\"require\"]).to be_instance_of(Array)\n    expect(requiring[\"require\"][0]).to be_instance_of(Puppet::Resource)\n    expect(requiring[\"require\"][0].to_s).to eql(\"Class[Required]\")\n  end\n\n  it 'appends to the require attribute on the requiring resource if it already has requirements' do\n    catalog = compile_to_catalog(<<-MANIFEST)\n\n      class required { }\n      class also_required { }\n\n      class requiring {\n        require required\n        require also_required\n      }\n      include requiring\n    MANIFEST\n\n    requiring = catalog.resource(\"Class\", \"requiring\")\n    expect(requiring[\"require\"]).to be_instance_of(Array)\n    expect(requiring[\"require\"][0]).to be_instance_of(Puppet::Resource)\n    expect(requiring[\"require\"][0].to_s).to eql(\"Class[Required]\")\n    expect(requiring[\"require\"][1]).to be_instance_of(Puppet::Resource)\n    expect(requiring[\"require\"][1].to_s).to eql(\"Class[Also_required]\")\n  end\n\n  it \"includes the class when using a fully qualified anchored name\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class required {\n        notify { \"required\": }\n      }\n      require ::required\n    MANIFEST\n\n    expect(catalog.classes).to include(\"required\")\n  end\n\n  it_should_behave_like 'all functions transforming relative to absolute names', :require\n  it_should_behave_like 'an inclusion function, regardless of the type of class reference,', :require\n  it_should_behave_like 'an inclusion function, when --tasks is on,', :require\nend\n"
  },
  {
    "path": "spec/unit/functions/return_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the return function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'returns from outer function when called from nested block' do\n    it 'with a given value as function result' do\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[100]')\n          function please_return() {\n            [1,2,3].map |$x| { if $x == 1 { return(100) } 200 }\n            300\n          }\n          notify { String(please_return()): }\n        CODE\n    end\n\n    it 'with undef value as function result when not given an argument' do\n      # strict mode is off so behavior this test is trying to check isn't stubbed out\n      Puppet[:strict_variables] = false\n      Puppet[:strict] = :warning\n\n      expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[xy]')\n          function please_return() {\n            [1,2,3].map |$x| { if $x == 1 { return() } 200 }\n            300\n          }\n          notify { \"x${please_return}y\": }\n        CODE\n    end\n  end\n\n  it 'can be called without parentheses around the argument' do\n    expect(compile_to_catalog(<<-CODE)).to have_resource('Notify[100]')\n        function please_return() {\n          if 1 == 1 { return 100 }\n          200\n        }\n        notify { String(please_return()): }\n      CODE\n  end\n\n  it 'provides early exit from a class and keeps the class' do\n    expect(eval_and_collect_notices(<<-CODE)).to eql(['a', 'c', 'true', 'true'])\n        class notices_c { notice 'c' }\n        class does_next {\n          notice 'a'\n          if 1 == 1 { return() } # avoid making next line statically unreachable\n          notice 'b'\n        }\n        # include two classes to check that next does not do an early return from\n        # the include function.\n        include(does_next, notices_c)\n        notice defined(does_next)\n        notice defined(notices_c)\n      CODE\n  end\n\n  it 'provides early exit from a user defined resource and keeps the resource' do\n    expect(eval_and_collect_notices(<<-CODE)).to eql(['the_doer_of_next', 'copy_cat', 'true', 'true'])\n        define does_next {\n          notice $title\n          if 1 == 1 { return() } # avoid making next line statically unreachable\n          notice 'b'\n        }\n        define checker {\n          notice defined(Does_next['the_doer_of_next'])\n          notice defined(Does_next['copy_cat'])\n        }\n        # create two instances to ensure next does not break the entire\n        # resource expression\n        does_next { ['the_doer_of_next', 'copy_cat']: }\n        checker { 'needed_because_evaluation_order': }\n      CODE\n  end\n\n  it 'can be called when nested in a function to make that function return' do\n    expect(eval_and_collect_notices(<<-CODE)).to eql(['100'])\n      function nested_return() {\n        with(1) |$x| { with($x) |$x| {return(100) }}\n      }\n      notice nested_return()\n      CODE\n  end\n\n  it 'can not be called nested from top scope' do\n    expect do\n      compile_to_catalog(<<-CODE)\n        # line 1\n        # line 2\n        $result = with(1) |$x| { with($x) |$x| {return(100) }}\n        notice $result\n      CODE\n    end.to raise_error(/return\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n  end\n\n  it 'can not be called from top scope' do\n    expect do\n      compile_to_catalog(<<-CODE)\n        # line 1\n        # line 2\n        return()\n      CODE\n    end.to raise_error(/return\\(\\) from context where this is illegal \\(file: unknown, line: 3\\) on node.*/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/reverse_each_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the reverse_each function' do\n  include PuppetSpec::Compiler\n\n  it 'raises an error when given a type that cannot be iterated' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        3.14.reverse_each |$v| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects an Iterable value, got Float/)\n  end\n\n  it 'raises an error when called with more than one argument and without a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].reverse_each(1)\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects 1 argument, got 2/)\n  end\n\n  it 'raises an error when called with more than one argument and a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].reverse_each(1) |$v| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects 1 argument, got 2/)\n  end\n\n  it 'raises an error when called with a block with too many required parameters' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].reverse_each() |$v1, $v2| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /block expects 1 argument, got 2/)\n  end\n\n  it 'raises an error when called with a block with too few parameters' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].reverse_each() | | {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /block expects 1 argument, got none/)\n  end\n\n  it 'does not raise an error when called with a block with too many but optional arguments' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].reverse_each() |$v1, $v2=extra| {  }\n      MANIFEST\n    end.to_not raise_error\n  end\n\n  it 'returns an Undef when called with a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n            assert_type(Undef, [1].reverse_each |$x| { $x })\n      MANIFEST\n    end.not_to raise_error\n  end\n\n  it 'returns an Iterable when called without a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n            assert_type(Iterable, [1].reverse_each)\n      MANIFEST\n    end.not_to raise_error\n  end\n\n  it 'should produce \"times\" interval of integer in reverse' do\n    expect(eval_and_collect_notices('5.reverse_each |$x| { notice($x) }')).to eq(['4', '3', '2', '1', '0'])\n  end\n\n  it 'should produce range Integer[5,8] in reverse' do\n    expect(eval_and_collect_notices('Integer[5,8].reverse_each |$x| { notice($x) }')).to eq(['8', '7', '6', '5'])\n  end\n\n  it 'should produce the choices of [first,second,third] in reverse' do\n    expect(eval_and_collect_notices('[first,second,third].reverse_each |$x| { notice($x) }')).to eq(%w(third second first))\n  end\n\n  it 'should produce the choices of {first => 1,second => 2,third => 3} in reverse' do\n    expect(eval_and_collect_notices('{first => 1,second => 2,third => 3}.reverse_each |$t| { notice($t[0]) }')).to eq(%w(third second first))\n  end\n\n  it 'should produce the choices of Enum[first,second,third] in reverse' do\n    expect(eval_and_collect_notices('Enum[first,second,third].reverse_each |$x| { notice($x) }')).to eq(%w(third second first))\n  end\n\n  it 'should produce nth element in reverse of range Integer[5,20] when chained after a step' do\n    expect(eval_and_collect_notices('Integer[5,20].step(4).reverse_each |$x| { notice($x) }')\n    ).to eq(['17', '13', '9', '5'])\n  end\n\n  it 'should produce nth element in reverse of times 5 when chained after a step' do\n    expect(eval_and_collect_notices('5.step(2).reverse_each |$x| { notice($x) }')).to eq(['4', '2', '0'])\n  end\n\n  it 'should produce nth element in reverse of range Integer[5,20] when chained after a step' do\n    expect(eval_and_collect_notices(\n      '[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20].step(4).reverse_each |$x| { notice($x) }')\n    ).to eq(['17', '13', '9', '5'])\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/round_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the round function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an integer' do\n    [ 0, 1, -1].each do |x|\n      it \"called as round(#{x}) results in the same value\" do\n        expect(compile_to_catalog(\"notify { String( round(#{x}) == #{x}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  context 'for a float' do\n    {\n      0.0 => 0,\n      1.1 => 1,\n      -1.1 => -1,\n      2.9 => 3,\n      2.1 => 2,\n      2.49 => 2,\n      2.50 => 3,\n      -2.9 => -3,\n    }.each_pair do |x, expected|\n      it \"called as round(#{x}) results in #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( round(#{x}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n  end\n\n  [[1,2,3], {'a' => 10}, '\"42\"'].each do |x|\n    it \"errors for a value of class #{x.class}\" do\n      expect{ compile_to_catalog(\"round(#{x})\") }.to raise_error(/expects a Numeric value/)\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/rstrip_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the rstrip function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'removes trailing whitepsace' do\n    expect(compile_to_catalog(\"notify { String(\\\" abc\\t\\n \\\".rstrip == ' abc'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.rstrip == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns rstripped version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String([' a ', ' b ', ' c '].rstrip == [' a', ' b', ' c']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns rstripped version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String(['a ', 'b ', 'c '].reverse_each.lstrip == ['c ', 'b ', 'a ']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].lstrip\")}.to raise_error(/'lstrip' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/scanf_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the scanf function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'scans a value and returns an array' do\n    expect(compile_to_catalog(\"$x = '42'.scanf('%i')[0] + 1; notify { \\\"test$x\\\": }\")).to have_resource('Notify[test43]')\n  end\n\n  it 'scans a value and returns result of a code block' do\n    expect(compile_to_catalog(\"$x = '42'.scanf('%i')|$x|{$x[0]} + 1; notify { \\\"test$x\\\": }\")).to have_resource('Notify[test43]')\n  end\n\n  it 'returns empty array if nothing was scanned' do\n    expect(compile_to_catalog(\"$x = 'no'.scanf('%i')[0]; notify { \\\"test${x}test\\\": }\")).to have_resource('Notify[testtest]')\n  end\n\n  it 'produces result up to first unsuccessful scan' do\n    expect(compile_to_catalog(\"$x = '42 no'.scanf('%i'); notify { \\\"test${x[0]}${x[1]}test\\\": }\")).to have_resource('Notify[test42test]')\n  end\n\n\n  it 'errors when not given enough arguments' do\n    expect do\n      compile_to_catalog(\"'42'.scanf()\")\n    end.to raise_error(/'scanf' expects 2 arguments, got 1/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/shared.rb",
    "content": "shared_examples_for 'all functions transforming relative to absolute names' do |func_name|\n  before(:each) do\n    # mock that the class 'myclass' exists which are needed for the 'require' functions\n    # as it checks the existence of the required class\n    @klass = double('class', :name => \"myclass\")\n    allow(@scope.environment.known_resource_types).to receive(:find_hostclass).and_return(@klass)\n    @resource = Puppet::Parser::Resource.new(:file, \"/my/file\", :scope => @scope, :source => \"source\")\n    allow(@scope).to receive(:resource).and_return(@resource)\n  end\n\n  it 'accepts a Class[name] type' do\n    expect(@scope.compiler).to receive(:evaluate_classes).with([\"::myclass\"], @scope, false)\n    @scope.call_function(func_name, [Puppet::Pops::Types::TypeFactory.host_class('myclass')])\n  end\n\n  it 'accepts a Resource[class, name] type' do\n    expect(@scope.compiler).to receive(:evaluate_classes).with([\"::myclass\"], @scope, false)\n    @scope.call_function(func_name, [Puppet::Pops::Types::TypeFactory.resource('class', 'myclass')])\n  end\n\n  it 'raises and error for unspecific Class' do\n    expect {\n      @scope.call_function(func_name, [Puppet::Pops::Types::TypeFactory.host_class()])\n    }.to raise_error(ArgumentError, /Cannot use an unspecific Class\\[\\] Type/)\n  end\n\n  it 'raises and error for Resource that is not of class type' do\n    expect {\n      @scope.call_function(func_name, [Puppet::Pops::Types::TypeFactory.resource('file')])\n    }.to raise_error(ArgumentError, /Cannot use a Resource\\[File\\] where a Resource\\['class', name\\] is expected/)\n  end\n\n  it 'raises and error for Resource that is unspecific' do\n    expect {\n      @scope.call_function(func_name, [Puppet::Pops::Types::TypeFactory.resource()])\n    }.to raise_error(ArgumentError, /Cannot use an unspecific Resource\\[\\] where a Resource\\['class', name\\] is expected/)\n  end\n\n  it 'raises and error for Resource[class] that is unspecific' do\n    expect {\n      @scope.call_function(func_name, [Puppet::Pops::Types::TypeFactory.resource('class')])\n    }.to raise_error(ArgumentError, /Cannot use an unspecific Resource\\['class'\\] where a Resource\\['class', name\\] is expected/)\n  end\n\nend\n\nshared_examples_for 'an inclusion function, regardless of the type of class reference,' do |function|\n    it \"and #{function} a class absolutely, even when a relative namespaced class of the same name is present\" do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        class foo {\n          class bar { }\n          #{function} bar\n        }\n        class bar { }\n        include foo\n      MANIFEST\n      expect(catalog.classes).to include('foo','bar')\n    end\n\n    it \"and #{function} a class absolutely by Class['type'] reference\" do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        class foo {\n          class bar { }\n          #{function} Class['bar'] \n        }\n        class bar { }\n        include foo\n      MANIFEST\n      expect(catalog.classes).to include('foo','bar')\n    end\n\n    it \"and #{function} a class absolutely by Resource['type','title'] reference\" do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        class foo {\n          class bar { }\n          #{function} Resource['class','bar'] \n        }\n        class bar { }\n        include foo\n      MANIFEST\n      expect(catalog.classes).to include('foo','bar')\n    end\nend\n\nshared_examples_for 'an inclusion function, when --tasks is on,' do |function|\n  it \"is not available when --tasks is on\" do\n    Puppet[:tasks] = true\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        #{function}(bar)\n      MANIFEST\n    end.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/size_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the size function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'for an array it' do\n    it 'returns 0 when empty' do\n      expect(compile_to_catalog(\"notify { String(size([])): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of elements when not empty' do\n      expect(compile_to_catalog(\"notify { String(size([1, 2, 3])): }\")).to have_resource('Notify[3]')\n    end\n  end\n\n  context 'for a hash it' do\n    it 'returns 0 empty' do\n      expect(compile_to_catalog(\"notify { String(size({})): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of elements when not empty' do\n      expect(compile_to_catalog(\"notify { String(size({1=>1,2=>2})): }\")).to have_resource('Notify[2]')\n    end\n  end\n\n  context 'for a string it' do\n    it 'returns 0 when empty' do\n      expect(compile_to_catalog(\"notify { String(size('')): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of characters when not empty' do\n      # note the multibyte characters - åäö each taking two bytes in UTF-8\n      expect(compile_to_catalog('notify { String(size(\"\\u00e5\\u00e4\\u00f6\")): }')).to have_resource('Notify[3]')\n    end\n  end\n\n  context 'for a binary it' do\n    it 'returns 0 when empty' do\n      expect(compile_to_catalog(\"notify { String(size(Binary(''))): }\")).to have_resource('Notify[0]')\n    end\n\n    it 'returns number of bytes when not empty' do\n      expect(compile_to_catalog(\"notify { String(size(Binary('b25l'))): }\")).to have_resource('Notify[3]')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/slice_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'methods' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  before :each do\n    node      = Puppet::Node.new(\"floppy\", :environment => 'production')\n    @compiler = Puppet::Parser::Compiler.new(node)\n    @scope    = Puppet::Parser::Scope.new(@compiler)\n    @topscope = @scope.compiler.topscope\n    @scope.parent = @topscope\n  end\n\n  context \"should be callable on array as\" do\n\n    it 'slice with explicit parameters' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, present, 2, absent, 3, present]\n        $a.slice(2) |$k,$v| {\n          file { \"/file_${$k}\": ensure => $v }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'absent')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'slice with captures last' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, present, 2, absent, 3, present]\n        $a.slice(2) |*$kv| {\n          file { \"/file_${$kv[0]}\": ensure => $kv[1] }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'absent')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'slice with one parameter' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, present, 2, absent, 3, present]\n        $a.slice(2) |$k| {\n          file { \"/file_${$k[0]}\": ensure => $k[1] }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'absent')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'slice with shorter last slice' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, present, 2, present, 3, absent]\n        $a.slice(4) |$a, $b, $c, $d| {\n          file { \"/file_$a.$c\": ensure => $b }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1.2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3.]\").with_parameter(:ensure, 'absent')\n    end\n  end\n\n  context \"should be callable on hash as\" do\n    it 'slice with explicit parameters, missing are empty' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {1=>present, 2=>present, 3=>absent}\n        $a.slice(2) |$a,$b| {\n          file { \"/file_${a[0]}.${b[0]}\": ensure => $a[1] }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1.2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3.]\").with_parameter(:ensure, 'absent')\n    end\n  end\n\n  context \"should be callable on enumerable types as\" do\n    it 'slice with integer range' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = Integer[1,4]\n        $a.slice(2) |$a,$b| {\n          file { \"/file_${a}.${b}\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1.2]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_3.4]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'slice with integer' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        4.slice(2) |$a,$b| {\n          file { \"/file_${a}.${b}\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_0.1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2.3]\").with_parameter(:ensure, 'present')\n    end\n\n    it 'slice with string' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        'abcd'.slice(2) |$a,$b| {\n          file { \"/file_${a}.${b}\": ensure => present }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_a.b]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_c.d]\").with_parameter(:ensure, 'present')\n    end\n  end\n\n  context \"when called without a block\" do\n    it \"should produce an array with the result\" do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, present, 2, absent, 3, present]\n        $a.slice(2).each |$k| {\n          file { \"/file_${$k[0]}\": ensure => $k[1] }\n        }\n      MANIFEST\n\n      expect(catalog).to have_resource(\"File[/file_1]\").with_parameter(:ensure, 'present')\n      expect(catalog).to have_resource(\"File[/file_2]\").with_parameter(:ensure, 'absent')\n      expect(catalog).to have_resource(\"File[/file_3]\").with_parameter(:ensure, 'present')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/sort_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the sort function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n    {\n      \"'bac'\"     => \"'abc'\",\n      \"'BaC'\"     => \"'BCa'\",\n    }.each_pair do |value, expected|\n      it \"sorts characters in a string such that #{value}.sort results in #{expected}\" do\n        expect(compile_to_catalog(\"notify { String( sort(#{value}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n      end\n    end\n\n  {\n     \"'bac'\"     => \"'abc'\",\n     \"'BaC'\"     => \"'aBC'\",\n   }.each_pair do |value, expected|\n     it \"accepts a lambda when sorting characters in a string such that #{value} results in #{expected}\" do\n       expect(compile_to_catalog(\"notify { String( sort(#{value}) |$a,$b| { compare($a,$b) } == #{expected}): }\")).to have_resource(\"Notify[true]\")\n     end\n   end\n\n  {\n    ['b', 'a', 'c']     => ['a', 'b', 'c'],\n    ['B', 'a', 'C']     => ['B', 'C', 'a'],\n  }.each_pair do |value, expected|\n    it \"sorts strings in an array such that #{value}.sort results in #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( sort(#{value}) == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  {\n    ['b', 'a', 'c']     => ['a', 'b', 'c'],\n    ['B', 'a', 'C']     => ['a', 'B', 'C'],\n  }.each_pair do |value, expected|\n    it \"accepts a lambda when sorting an array such that #{value} results in #{expected}\" do\n      expect(compile_to_catalog(\"notify { String( sort(#{value}) |$a,$b| { compare($a,$b) } == #{expected}): }\")).to have_resource(\"Notify[true]\")\n    end\n  end\n\n  it 'errors if given a mix of data types' do\n    expect { compile_to_catalog(\"sort([1, 'a'])\")}.to raise_error(/comparison .* failed/)\n  end\n\n  it 'returns empty string for empty string input' do\n    expect(compile_to_catalog(\"notify { String(sort('') == ''): }\")).to have_resource(\"Notify[true]\")\n  end\n\n  it 'returns empty string for empty string input' do\n    expect(compile_to_catalog(\"notify { String(sort([]) == []): }\")).to have_resource(\"Notify[true]\")\n  end\n\n  it 'can sort mixed data types when using a lambda' do\n    # source sorts Numeric before string and uses compare() for same data type\n    src = <<-SRC\n    notify{ String(sort(['b', 3, 'a', 2]) |$a, $b| {\n        case [$a, $b] {\n          [String, Numeric] : { 1 }\n          [Numeric, String] : { -1 }\n          default:            { compare($a, $b) }\n        }\n      } == [2, 3,'a', 'b']):\n    }\n    SRC\n    expect(compile_to_catalog(src)).to have_resource(\"Notify[true]\")\n\n  end\n\n  it 'errors if lambda does not accept 2 arguments' do\n    expect { compile_to_catalog(\"sort([1, 'a']) || { }\")}.to raise_error(/block expects 2 arguments/)\n    expect { compile_to_catalog(\"sort([1, 'a']) |$x| { }\")}.to raise_error(/block expects 2 arguments/)\n    expect { compile_to_catalog(\"sort([1, 'a']) |$x,$y, $z| { }\")}.to raise_error(/block expects 2 arguments/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/split_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'the split function' do\n\n  before(:each) do\n    loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))\n    Puppet.push_context({:loaders => loaders}, \"test-examples\")\n  end\n\n  after(:each) do\n    Puppet.pop_context()\n  end\n\n  def split(*args)\n    Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'split').call({}, *args)\n  end\n\n  let(:type_parser) { Puppet::Pops::Types::TypeParser.singleton }\n\n  it 'should raise an Error if there is less than 2 arguments' do\n    expect { split('a,b') }.to raise_error(/'split' expects 2 arguments, got 1/)\n  end\n\n  it 'should raise an Error if there is more than 2 arguments' do\n    expect { split('a,b','foo', 'bar') }.to raise_error(/'split' expects 2 arguments, got 3/)\n  end\n\n  it 'should raise a RegexpError if the regexp is malformed' do\n    expect { split('a,b',')') }.to raise_error(/unmatched close parenthesis/)\n  end\n\n  it 'should handle pattern in string form' do\n    expect(split('a,b',',')).to eql(['a', 'b'])\n  end\n\n  it 'should handle pattern in Regexp form' do\n    expect(split('a,b',/,/)).to eql(['a', 'b'])\n  end\n\n  it 'should handle pattern in Regexp Type form' do\n    expect(split('a,b',type_parser.parse('Regexp[/,/]'))).to eql(['a', 'b'])\n  end\n\n  it 'should handle pattern in Regexp Type form with empty regular expression' do\n    expect(split('ab',type_parser.parse('Regexp[//]'))).to eql(['a', 'b'])\n  end\n\n  it 'should handle pattern in Regexp Type form with missing regular expression' do\n    expect(split('ab',type_parser.parse('Regexp'))).to eql(['a', 'b'])\n  end\n\n  it 'should handle sensitive String' do\n    expect(split(Puppet::Pops::Types::PSensitiveType::Sensitive.new('a,b'), ',')).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n    expect(split(Puppet::Pops::Types::PSensitiveType::Sensitive.new('a,b'), /,/)).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n    expect(split(Puppet::Pops::Types::PSensitiveType::Sensitive.new('a,b'), type_parser.parse('Regexp[/,/]'))).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/step_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the step method' do\n  include PuppetSpec::Compiler\n\n  it 'raises an error when given a type that cannot be iterated' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        3.14.step(1) |$v| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects an Iterable value, got Float/)\n  end\n\n  it 'raises an error when called with more than two arguments and a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step(1,2) |$v| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects 2 arguments, got 3/)\n  end\n\n  it 'raises an error when called with more than two arguments and without a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step(1,2)\n      MANIFEST\n    end.to raise_error(Puppet::Error, /expects 2 arguments, got 3/)\n  end\n\n  it 'raises an error when called with a block with too many required parameters' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step(1) |$v1, $v2| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /block expects 1 argument, got 2/)\n  end\n\n  it 'raises an error when called with a block with too few parameters' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step(1) | | {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /block expects 1 argument, got none/)\n  end\n\n  it 'raises an error when called with step == 0' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step(0) |$x| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /'step' expects an Integer\\[1\\] value, got Integer\\[0, 0\\]/)\n  end\n\n  it 'raises an error when step is not an integer' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step('three') |$x| {  }\n      MANIFEST\n    end.to raise_error(Puppet::Error, /'step' expects an Integer value, got String/)\n  end\n\n  it 'does not raise an error when called with a block with too many but optional arguments' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        [1].step(1) |$v1, $v2=extra| {  }\n      MANIFEST\n    end.to_not raise_error\n  end\n\n  it 'returns Undef when called with a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n          assert_type(Undef, [1].step(2) |$x| { $x })\n      MANIFEST\n    end.not_to raise_error\n  end\n\n  it 'returns an Iterable when called without a block' do\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n          assert_type(Iterable, [1].step(2))\n      MANIFEST\n    end.not_to raise_error\n  end\n\n  it 'should produce \"times\" interval of integer according to step' do\n    expect(eval_and_collect_notices('10.step(2) |$x| { notice($x) }')).to eq(['0', '2', '4', '6', '8'])\n  end\n\n  it 'should produce interval of Integer[5,20] according to step' do\n    expect(eval_and_collect_notices('Integer[5,20].step(4) |$x| { notice($x) }')).to eq(['5', '9', '13', '17'])\n  end\n\n  it 'should produce the elements of [a,b,c,d,e,f,g,h] according to step' do\n    expect(eval_and_collect_notices('[a,b,c,d,e,f,g,h].step(2) |$x| { notice($x) }')).to eq(%w(a c e g))\n  end\n\n  it 'should produce the elements {a=>1,b=>2,c=>3,d=>4,e=>5,f=>6,g=>7,h=>8} according to step' do\n    expect(eval_and_collect_notices('{a=>1,b=>2,c=>3,d=>4,e=>5,f=>6,g=>7,h=>8}.step(2) |$t| { notice($t[1]) }')).to eq(%w(1 3 5 7))\n  end\n\n  it 'should produce the choices of Enum[a,b,c,d,e,f,g,h] according to step' do\n    expect(eval_and_collect_notices('Enum[a,b,c,d,e,f,g,h].step(2) |$x| { notice($x) }')).to eq(%w(a c e g))\n  end\n\n  it 'should produce descending interval of Integer[5,20] when chained after a reverse_each' do\n    expect(eval_and_collect_notices('Integer[5,20].reverse_each.step(4) |$x| { notice($x) }')).to eq(['20', '16', '12', '8'])\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/strftime_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe 'the strftime function' do\n  include PuppetSpec::Compiler\n\n  def test_format(ctor_arg, format, expected)\n    expect(eval_and_collect_notices(\"notice(strftime(Timespan(#{ctor_arg}), '#{format}'))\")).to eql([\"#{expected}\"])\n  end\n\n  context 'when applied to a Timespan' do\n    [\n      ['hours', 'H', 2],\n      ['minutes', 'M', 2],\n      ['seconds', 'S', 2],\n    ].each do |field, fmt, dflt_width|\n      ctor_arg = \"{#{field}=>3}\"\n      it \"%#{fmt} width defaults to #{dflt_width}\" do\n        test_format(ctor_arg, \"%#{fmt}\", sprintf(\"%0#{dflt_width}d\", 3))\n      end\n\n      it \"%_#{fmt} pads with space\" do\n        test_format(ctor_arg, \"%_#{fmt}\", sprintf(\"% #{dflt_width}d\", 3))\n      end\n\n      it \"%-#{fmt} does not pad\" do\n        test_format(ctor_arg, \"%-#{fmt}\", '3')\n      end\n\n      it \"%10#{fmt} pads with zeroes to specified width\" do\n        test_format(ctor_arg, \"%10#{fmt}\", sprintf(\"%010d\", 3))\n      end\n\n      it \"%_10#{fmt} pads with space to specified width\" do\n        test_format(ctor_arg, \"%_10#{fmt}\", sprintf(\"% 10d\", 3))\n      end\n\n      it \"%-10#{fmt} does not pad even if width is specified\" do\n        test_format(ctor_arg, \"%-10#{fmt}\", '3')\n      end\n    end\n\n    [\n      ['milliseconds', 'L', 3],\n      ['nanoseconds', 'N', 9],\n      ['milliseconds', '3N', 3],\n      ['microseconds', '6N', 6],\n      ['nanoseconds', '9N', 9],\n    ].each do |field, fmt, dflt_width|\n      ctor_arg = \"{#{field}=>3000}\"\n      it \"%#{fmt} width defaults to #{dflt_width}\" do\n        test_format(ctor_arg, \"%#{fmt}\", sprintf(\"%-#{dflt_width}d\", 3000))\n      end\n\n      it \"%_#{fmt} pads with space\" do\n        test_format(ctor_arg, \"%_#{fmt}\", sprintf(\"%-#{dflt_width}d\", 3000))\n      end\n\n      it \"%-#{fmt} does not pad\" do\n        test_format(ctor_arg, \"%-#{fmt}\", '3000')\n      end\n    end\n\n    it 'can use a format containing all format characters, flags, and widths' do\n      test_format(\"{string => '100-14:02:24.123456000', format => '%D-%H:%M:%S.%9N'}\", '%_10D%%%03H:%-M:%S.%9N', '       100%014:2:24.123456000')\n    end\n\n    it 'can format and strip excess zeroes from fragment using no-padding flag' do\n      test_format(\"{string => '100-14:02:24.123456000', format => '%D-%H:%M:%S.%N'}\", '%D-%H:%M:%S.%-N', '100-14:02:24.123456')\n    end\n\n    it 'can format and replace excess zeroes with spaces from fragment using space-padding flag and default widht' do\n      test_format(\"{string => '100-14:02:24.123456000', format => '%D-%H:%M:%S.%N'}\", '%D-%H:%M:%S.%_N', '100-14:02:24.123456   ')\n    end\n\n    it 'can format and replace excess zeroes with spaces from fragment using space-padding flag and specified width' do\n      test_format(\"{string => '100-14:02:24.123400000', format => '%D-%H:%M:%S.%N'}\", '%D-%H:%M:%S.%_6N', '100-14:02:24.1234  ')\n    end\n\n    it 'can format and retain excess zeroes in fragment using default width' do\n      test_format(\"{string => '100-14:02:24.123400000', format => '%D-%H:%M:%S.%N'}\", '%D-%H:%M:%S.%N', '100-14:02:24.123400000')\n    end\n\n    it 'can format and retain excess zeroes in fragment using specified width' do\n      test_format(\"{string => '100-14:02:24.123400000', format => '%D-%H:%M:%S.%N'}\", '%D-%H:%M:%S.%6N', '100-14:02:24.123400')\n    end\n  end\n\n  def test_timestamp_format(ctor_arg, format, expected)\n    expect(eval_and_collect_notices(\"notice(strftime(Timestamp('#{ctor_arg}'), '#{format}'))\")).to eql([\"#{expected}\"])\n  end\n\n  def test_timestamp_format_tz(ctor_arg, format, tz, expected)\n    expect(eval_and_collect_notices(\"notice(strftime(Timestamp('#{ctor_arg}'), '#{format}', '#{tz}'))\")).to eql([\"#{expected}\"])\n  end\n\n  def collect_log(code, node = Puppet::Node.new('foonode'))\n    Puppet[:code] = code\n    compiler = Puppet::Parser::Compiler.new(node)\n    node.environment.check_for_reparse\n    logs = []\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      compiler.compile\n    end\n    logs\n  end\n\n  context 'when applied to a Timestamp' do\n    it 'can format a timestamp with a format pattern' do\n      test_timestamp_format('2016-09-23T13:14:15.123 UTC', '%Y-%m-%d %H:%M:%S.%L %z', '2016-09-23 13:14:15.123 +0000')\n    end\n\n    it 'can format a timestamp using a specific timezone' do\n      test_timestamp_format_tz('2016-09-23T13:14:15.123 UTC', '%Y-%m-%d %H:%M:%S.%L %z', 'EST', '2016-09-23 08:14:15.123 -0500')\n    end\n  end\n\n  context 'when used with dispatcher covering legacy stdlib API (String format, String timeszone = undef)' do\n    it 'produces the current time when used with one argument' do\n      before_eval = Time.now\n      notices = eval_and_collect_notices(\"notice(strftime('%F %T'))\")\n      expect(notices).not_to be_empty\n      expect(notices[0]).to match(/\\A\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\z/)\n      parsed_time = DateTime.strptime(notices[0], '%F %T').to_time\n      expect(Time.now.to_i >= parsed_time.to_i && parsed_time.to_i >= before_eval.to_i).to be_truthy\n    end\n\n    it 'emits a deprecation warning when used with one argument' do\n      log = collect_log(\"notice(strftime('%F %T'))\")\n      warnings = log.select { |log_entry| log_entry.level == :warning }.map { |log_entry| log_entry.message }\n      expect(warnings).not_to be_empty\n      expect(warnings[0]).to match(/The argument signature \\(String format, \\[String timezone\\]\\) is deprecated for #strftime/)\n    end\n\n    it 'produces the current time formatted with specific timezone when used with two arguments' do\n      before_eval = Time.now\n      notices = eval_and_collect_notices(\"notice(strftime('%F %T %:z', 'EST'))\")\n      expect(notices).not_to be_empty\n      expect(notices[0]).to match(/\\A\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} -05:00\\z/)\n      parsed_time = DateTime.strptime(notices[0], '%F %T %z').to_time\n      expect(Time.now.to_i >= parsed_time.to_i && parsed_time.to_i >= before_eval.to_i).to be_truthy\n    end\n\n    it 'emits a deprecation warning when using legacy format with two arguments' do\n      log = collect_log(\"notice(strftime('%F %T', 'EST'))\")\n      warnings = log.select { |log_entry| log_entry.level == :warning }.map { |log_entry| log_entry.message }\n      expect(warnings).not_to be_empty\n      expect(warnings[0]).to match(/The argument signature \\(String format, \\[String timezone\\]\\) is deprecated for #strftime/)\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/functions/strip_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the strip function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'removes leading and trailing whitepsace' do\n    expect(compile_to_catalog(\"notify { String(\\\" abc\\t\\n \\\".strip == 'abc'): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.strip == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns rstripped version of each entry in an array' do\n    expect(compile_to_catalog(\"notify { String([' a ', ' b ', ' c '].strip == ['a', 'b', 'c']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns rstripped version of each entry in an Iterator' do\n    expect(compile_to_catalog(\"notify { String([' a ', ' b ', ' c '].reverse_each.strip == ['c', 'b', 'a']): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'errors when given a a nested Array' do\n    expect { compile_to_catalog(\"['a', 'b', ['c']].strip\")}.to raise_error(/'strip' parameter 'arg' expects a value of type Numeric, String, or Iterable/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/then_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the then function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'calls a lambda passing one argument' do\n    expect(compile_to_catalog(\"then(testing) |$x| { notify { $x: } }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'produces what lambda returns if value is not undef' do\n    expect(compile_to_catalog(\"notify{ then(1) |$x| { testing }: }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'does not call lambda if argument is undef' do\n    expect(compile_to_catalog('then(undef) |$x| { notify { \"failed\": } }')).to_not have_resource('Notify[failed]')\n  end\n\n  it 'produces undef if given value is undef' do\n    expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test-Undef-ing]')\n    notify{ \"test-${type(then(undef) |$x| { testing })}-ing\": }\n    SOURCE\n  end\n\n  it 'errors when lambda wants too many args' do\n    expect do\n      compile_to_catalog('then(1) |$x, $y| { }')\n    end.to raise_error(/'then' block expects 1 argument, got 2/m)\n  end\n\n  it 'errors when lambda wants too few args' do\n    expect do\n      compile_to_catalog('then(1) || { }')\n    end.to raise_error(/'then' block expects 1 argument, got none/m)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/tree_each_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'shared_behaviours/iterative_functions'\n\ndescribe 'the tree_each function' do\n  include PuppetSpec::Compiler\n\n  context \"can be called on\" do\n    it 'an Array, yielding path and value when lambda has arity 2' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [] value: [1, 2, 3]',\n          'path: [0] value: 1',\n          'path: [1] value: 2',\n          'path: [2] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'an Array, yielding only value  when lambda has arity 1' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,2,3]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() | $v| { -%>\n          path: - value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: - value: [1, 2, 3]',\n          'path: - value: 1',\n          'path: - value: 2',\n          'path: - value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'a Hash, yielding path and value when lambda has arity 2' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>'apple','b'=>'banana'}\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [] value: {a => apple, b => banana}',\n          'path: [a] value: apple',\n          'path: [b] value: banana',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'a Hash, yielding only value when lambda has arity 1' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {'a'=>'apple','b'=>'banana'}\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() | $v| { -%>\n          path: - value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: - value: {a => apple, b => banana}',\n          'path: - value: apple',\n          'path: - value: banana',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'an Object, yielding path and value when lambda has arity 2' do\n      # this also tests that include_refs => true includes references\n      catalog = compile_to_catalog(<<-MANIFEST)\n        type Person = Object[{attributes => {\n          name => String,\n          father => Optional[Person],\n          mother => { kind => reference, type => Optional[Person] }\n        }}]\n        $adam  = Person({name => 'Adam'})\n        $eve   = Person({name => 'Eve'})\n        $cain  = Person({name => 'Cain',  mother => $eve,  father => $adam})\n        $awan  = Person({name => 'Awan',  mother => $eve,  father => $adam})\n        $enoch = Person({name => 'Enoch', mother => $awan, father => $cain})\n\n        $msg = inline_epp(@(TEMPLATE))\n          <% $enoch.tree_each({include_containers=>false, include_refs => true}) |$path, $v| { unless $v =~ Undef {-%>\n          path: <%= $path %> value: <%= $v %>\n          <% }} -%>\n          | TEMPLATE\n        notify {'with_refs': message => $msg}\n\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'with_refs')['message']).to eq(\n        [\n          'path: [name] value: Enoch',\n          'path: [father, name] value: Cain',\n          'path: [father, father, name] value: Adam',\n          'path: [father, mother, name] value: Eve',\n          'path: [mother, name] value: Awan',\n          'path: [mother, father, name] value: Adam',\n          'path: [mother, mother, name] value: Eve',\n          ''\n          ].join(\"\\n\"))\n    end\n  end\n\n  context 'a yielded path' do\n    it 'holds integer values for Array index at each level' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() |$path, $v| { -%>\n          path: <%= $path %> t: <%= $path.map |$x| { type($x, generalized) } %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [] t: [] value: [1, [2, [3]]]',\n          'path: [0] t: [Integer] value: 1',\n          'path: [1] t: [Integer] value: [2, [3]]',\n          'path: [1, 0] t: [Integer, Integer] value: 2',\n          'path: [1, 1] t: [Integer, Integer] value: [3]',\n          'path: [1, 1, 0] t: [Integer, Integer, Integer] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'holds Any values for Hash keys at each level' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = {a => 1, /fancy/=> {c => 2, d=>{[e] => 3}}}\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() |$path, $v| { -%>\n          path: <%= $path %> t: <%= $path.map |$x| { type($x, generalized) } %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [] t: [] value: {a => 1, /fancy/ => {c => 2, d => {[e] => 3}}}',\n          'path: [a] t: [String] value: 1',\n          'path: [/fancy/] t: [Regexp[/fancy/]] value: {c => 2, d => {[e] => 3}}',\n          'path: [/fancy/, c] t: [Regexp[/fancy/], String] value: 2',\n          'path: [/fancy/, d] t: [Regexp[/fancy/], String] value: {[e] => 3}',\n          'path: [/fancy/, d, [e]] t: [Regexp[/fancy/], String, Array[String]] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n  end\n\n  it 'errors when asked to operate on a String' do\n    expect {\n      compile_to_catalog(<<-MANIFEST)\n      \"hello\".tree_each() |$path, $v| {\n        notice \"$v\"\n      }\n    MANIFEST\n    }.to raise_error(/expects a value of type Iterator, Array, Hash, or Object/)\n  end\n\n  context 'produces' do\n    it 'the receiver when given a lambda' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, 3, 2]\n        $b = $a.tree_each |$path, $x| { \"unwanted\" }\n        file { \"/file_${b[1]}\":\n          ensure => present\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_3\")['ensure']).to eq('present')\n    end\n\n    it 'an Iterator when not given a lambda' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1, 3, 2]\n        $b = $a.tree_each\n        file { \"/file_${$b =~ Iterator}\":\n          ensure => present\n        }\n      MANIFEST\n\n      expect(catalog.resource(:file, \"/file_true\")['ensure']).to eq('present')\n    end\n  end\n\n  context 'a produced iterator' do\n    ['depth_first', 'breadth_first'].each do |order|\n      context \"for #{order} can be unrolled by creating an Array using\" do\n        it \"the () operator\" do\n          catalog = compile_to_catalog(<<-MANIFEST)\n            $a = [1, 3, 2]\n            $b = Array($a.tree_each({order => #{order}}))\n            $msg = inline_epp(@(TEMPLATE))\n              <% $b.each() |$v| { -%>\n              path: <%= $v[0] %> value: <%= $v[1] %>\n              <% } -%>\n              | TEMPLATE\n            notify {'test': message => $msg}\n          MANIFEST\n\n          expect(catalog.resource(:notify, \"test\")['message']).to eq([\n            'path: [] value: [1, 3, 2]',\n            'path: [0] value: 1',\n            'path: [1] value: 3',\n            'path: [2] value: 2',\n            ''\n            ].join(\"\\n\"))\n        end\n\n        it \"the splat operator\" do\n          catalog = compile_to_catalog(<<-MANIFEST)\n            $a = [1, 3, 2]\n            $b = *$a.tree_each({order => #{order}})\n            assert_type(Array[Array], $b)\n            $msg = inline_epp(@(TEMPLATE))\n              <% $b.each() |$v| { -%>\n              path: <%= $v[0] %> value: <%= $v[1] %>\n              <% } -%>\n              | TEMPLATE\n            notify {'test': message => $msg}\n          MANIFEST\n\n          expect(catalog.resource(:notify, \"test\")['message']).to eq([\n            'path: [] value: [1, 3, 2]',\n            'path: [0] value: 1',\n            'path: [1] value: 3',\n            'path: [2] value: 2',\n            ''\n            ].join(\"\\n\"))\n        end\n      end\n    end\n  end\n\n  context 'recursively yields under the control of options such that' do\n    it 'both containers and leafs are included by default' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each() |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [] value: [1, [2, [3]]]',\n          'path: [0] value: 1',\n          'path: [1] value: [2, [3]]',\n          'path: [1, 0] value: 2',\n          'path: [1, 1] value: [3]',\n          'path: [1, 1, 0] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'containers are skipped when option include_containers=false is used' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({include_containers => false}) |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ \n          'path: [0] value: 1',\n          'path: [1, 0] value: 2',\n          'path: [1, 1, 0] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'values are skipped when option include_values=false is used' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({include_values => false}) |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [] value: [1, [2, [3]]]',\n          'path: [1] value: [2, [3]]',\n          'path: [1, 1] value: [3]',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'the root container is skipped when option include_root=false is used' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({include_root => false, include_values => false}) |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ 'path: [1] value: [2, [3]]',\n          'path: [1, 1] value: [3]',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'containers must be included for root to be included' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({include_containers => false, include_root => true}) |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [ \n          'path: [0] value: 1',\n          'path: [1, 0] value: 2',\n          'path: [1, 1, 0] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'errors when asked to exclude both containers and values' do\n      expect {\n        compile_to_catalog(<<-MANIFEST)\n          [1,2,3].tree_each({include_containers => false, include_values => false}) |$path, $v| {\n          notice \"$v\"\n        }\n      MANIFEST\n      }.to raise_error(/Options 'include_containers' and 'include_values' cannot both be false/)\n    end\n\n    it 'tree nodes are yielded in depth first order if option order=depth_first' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3], 4], 5]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({order => depth_first, include_containers => false}) |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [\n          'path: [0] value: 1',\n          'path: [1, 0] value: 2',\n          'path: [1, 1, 0] value: 3',\n          'path: [1, 2] value: 4',\n          'path: [2] value: 5',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'tree nodes are yielded in breadth first order if option order=breadth_first' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3], 4], 5]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({order => breadth_first, include_containers => false}) |$path, $v| { -%>\n          path: <%= $path %> value: <%= $v %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [\n          'path: [0] value: 1',\n          'path: [2] value: 5',\n          'path: [1, 0] value: 2',\n          'path: [1, 2] value: 4',\n          'path: [1, 1, 0] value: 3',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'attributes of an Object of \"reference\" kind are not yielded by default' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        type Person = Object[{attributes => {\n          name => String,\n          father => Optional[Person],\n          mother => { kind => reference, type => Optional[Person] }\n        }}]\n        $adam  = Person({name => 'Adam'})\n        $eve   = Person({name => 'Eve'})\n        $cain  = Person({name => 'Cain',  mother => $eve,  father => $adam})\n        $awan  = Person({name => 'Awan',  mother => $eve,  father => $adam})\n        $enoch = Person({name => 'Enoch', mother => $awan, father => $cain})\n\n        $msg = inline_epp(@(TEMPLATE))\n          <% $enoch.tree_each({include_containers=>false }) |$path, $v| { unless $v =~ Undef {-%>\n          path: <%= $path %> value: <%= $v %>\n          <% }} -%>\n          | TEMPLATE\n        notify {'by_default': message => $msg}\n\n        $msg2 = inline_epp(@(TEMPLATE))\n          <% $enoch.tree_each({include_containers=>false, include_refs => false}) |$path, $v| { unless $v =~ Undef {-%>\n          path: <%= $path %> value: <%= $v %>\n          <% }} -%>\n          | TEMPLATE\n        notify {'when_false': message => $msg2}\n\n      MANIFEST\n\n      expected_refs_excluded_result = [\n        'path: [name] value: Enoch',\n        'path: [father, name] value: Cain',\n        'path: [father, father, name] value: Adam',\n        ''\n        ].join(\"\\n\")\n\n      expect(catalog.resource(:notify, 'by_default')['message']).to eq(expected_refs_excluded_result)\n      expect(catalog.resource(:notify, 'when_false')['message']).to eq(expected_refs_excluded_result)\n    end\n  end\n\n  context 'can be chained' do\n    it 'with reverse_each()' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({include_containers => false}).reverse_each |$v| { -%>\n          path: <%= $v[0] %> value: <%= $v[1] %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [\n          'path: [1, 1, 0] value: 3',\n          'path: [1, 0] value: 2',\n          'path: [0] value: 1',\n          ''\n          ].join(\"\\n\"))\n    end\n\n    it 'with step()' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        $a = [1,[2,[3,[4,[5]]]]]\n        $msg = inline_epp(@(TEMPLATE))\n          <% $a.tree_each({include_containers => false}).step(2) |$v| { -%>\n          path: <%= $v[0] %> value: <%= $v[1] %>\n          <% } -%>\n          | TEMPLATE\n        notify {'test': message => $msg}\n      MANIFEST\n\n      expect(catalog.resource(:notify, 'test')['message']).to eq(\n        [\n          'path: [0] value: 1',\n          'path: [1, 1, 0] value: 3',\n          'path: [1, 1, 1, 1, 0] value: 5',\n          ''\n          ].join(\"\\n\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/type_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the type function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'produces the type of a given value with default detailed quality' do\n  expect(compile_to_catalog('notify { \"${ type([2, 3.14]) }\": }')).to have_resource(\n      'Notify[Tuple[Integer[2, 2], Float[3.14, 3.14]]]')\n  end\n\n  it 'produces the type of a give value with detailed quality when quality is given' do\n    expect(compile_to_catalog('notify { \"${ type([2, 3.14], detailed) }\": }')).to have_resource(\n      'Notify[Tuple[Integer[2, 2], Float[3.14, 3.14]]]')\n  end\n\n  it 'produces the type of a given value with reduced quality when quality is given' do\n    expect(compile_to_catalog('notify { \"${ type([2, 3.14], reduced) }\": }')).to have_resource(\n      'Notify[Array[Numeric, 2, 2]]')\n  end\n\n  it 'produces the type of a given value with generalized quality when quality is given' do\n    expect(compile_to_catalog('notify { \"${ type([2, 3.14], generalized) }\": }')).to have_resource(\n      'Notify[Array[Numeric]]')\n  end\n\n  it 'errors when given a fault inference quality' do\n    expect do\n      compile_to_catalog(\"notify { type([2, 4.14], gobbledygooked): }\")\n    end.to raise_error(/expects a match for Enum\\['detailed', 'generalized', 'reduced'\\], got 'gobbledygooked'/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/unique_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the unique function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  context 'produces the unique set of chars from a String such that' do\n    it 'same case is considered unique' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, \"abc\")\n          notify{ 'test': message => 'abcbbcc'.unique }\n        SOURCE\n    end\n\n    it 'different case is not considered unique' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, \"abcABC\")\n          notify{ 'test': message => 'abcAbbBccC'.unique }\n        SOURCE\n    end\n\n    it 'case independent matching can be performed with a lambda' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, \"abc\")\n          notify{ 'test': message => 'abcAbbBccC'.unique |$x| { String($x, '%d') } }\n        SOURCE\n    end\n\n    it 'the first found value in the unique set is used' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, \"AbC\")\n          notify{ 'test': message => 'AbCAbbBccC'.unique |$x| { String($x, '%d') } }\n        SOURCE\n    end\n  end\n\n  context 'produces the unique set of values from an Array such that' do\n    it 'ruby equality is used to compute uniqueness by default' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, ['a', 'b', 'c', 'B', 'C'])\n          notify{ 'test': message => [a, b, c, a, 'B', 'C'].unique }\n        SOURCE\n    end\n\n    it 'accepts a lambda to perform the value to use for uniqueness' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, ['a', 'b', 'c'])\n          notify{ 'test': message => [a, b, c, a, 'B', 'C'].unique |$x| { String($x, '%d') }}\n        SOURCE\n    end\n\n    it 'the first found value in the unique set is used' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, ['A', 'b', 'C'])\n          notify{ 'test': message => ['A', b, 'C', a, 'B', 'c'].unique |$x| { String($x, '%d') }}\n        SOURCE\n    end\n  end\n\n  context 'produces the unique set of values from an Hash such that' do\n    it 'resulting keys and values in hash are arrays' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, {['a'] => [10], ['b']=>[20]})\n          notify{ 'test': message => {a => 10, b => 20}.unique }\n        SOURCE\n    end\n\n    it 'resulting keys contain all keys with same value' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, {['a', 'b'] => [10], ['c']=>[20]})\n          notify{ 'test': message => {a => 10, b => 10, c => 20}.unique }\n        SOURCE\n    end\n\n    it 'resulting values contain Ruby == unique set of values' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, {['a'] => [10], ['b', 'c']=>[11, 20]})\n          notify{ 'test': message => {a => 10, b => 11, c => 20}.unique |$x| { if $x > 10 {bigly} else { $x }}}\n        SOURCE\n    end\n  end\n\n  context 'produces the unique set of values from an Iterable' do\n    it 'such as reverse_each - in reverse order' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, ['B','b','a'])\n          notify{ 'test': message => ['a', 'b', 'B'].reverse_each.unique }\n        SOURCE\n    end\n\n    it 'such as Integer[1,5]' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, [1,2,3,4,5])\n          notify{ 'test': message => Integer[1,5].unique }\n        SOURCE\n    end\n\n    it 'such as the Integer 3' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, [0,1,2])\n          notify{ 'test': message => 3.unique }\n        SOURCE\n    end\n\n    it 'allows lambda to be used with Iterable' do\n      expect(compile_to_catalog(<<-SOURCE)).to have_resource('Notify[test]').with_parameter(:message, ['B','a'])\n          notify{ 'test': message => ['a', 'b', 'B'].reverse_each.unique |$x| { String($x, '%d') }}\n        SOURCE\n    end\n  end\n\n  it 'errors when given unsupported data type as input' do\n    expect do\n      compile_to_catalog(<<-SOURCE)\n        undef.unique\n      SOURCE\n    end.to raise_error(/expects an Iterable value, got Undef/)\n  end\n\n\nend\n"
  },
  {
    "path": "spec/unit/functions/unwrap_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the unwrap function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'unwraps a sensitive value' do\n    code = <<-CODE\n      $sensitive = Sensitive.new(\"12345\")\n      notice(\"unwrapped value is ${sensitive.unwrap}\")\n    CODE\n    expect(eval_and_collect_notices(code)).to eq(['unwrapped value is 12345'])\n  end\n\n  it 'just returns a non-sensitive value' do\n    code = <<-CODE\n      $non_sensitive = \"12345\"\n      notice(\"value is still ${non_sensitive.unwrap}\")\n    CODE\n    expect(eval_and_collect_notices(code)).to eq(['value is still 12345'])\n  end\n\n  it 'unwraps a sensitive value when given a code block' do\n    code = <<-CODE\n      $sensitive = Sensitive.new(\"12345\")\n      $split = $sensitive.unwrap |$unwrapped| {\n        notice(\"unwrapped value is $unwrapped\")\n        $unwrapped.split(/3/)\n      }\n      notice(\"split is $split\")\n    CODE\n    expect(eval_and_collect_notices(code)).to eq(['unwrapped value is 12345', 'split is [12, 45]'])\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/upcase_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the upcase function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'returns upper case version of a string' do\n    expect(compile_to_catalog(\"notify { 'abc'.upcase: }\")).to have_resource('Notify[ABC]')\n  end\n\n  it 'returns the value if Numeric' do\n    expect(compile_to_catalog(\"notify { String(42.upcase == 42): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'performs upcase of international UTF-8 characters' do\n    expect(compile_to_catalog(\"notify { 'åäö'.upcase: }\")).to have_resource('Notify[ÅÄÖ]')\n  end\n\n  it 'returns upper case version of each entry in an array (recursively)' do\n    expect(compile_to_catalog(\"notify { String(['a', ['b', ['c']]].upcase == ['A', ['B', ['C']]]): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns upper case version of keys and values in a hash (recursively)' do\n    expect(compile_to_catalog(\"notify { String({'a'=>'b','c'=>{'d'=>'e'}}.upcase == {'A'=>'B', 'C'=>{'D'=>'E'}}): }\")).to have_resource('Notify[true]')\n  end\n\n  it 'returns upper case version of keys and values in nested hash / array structure' do\n    expect(compile_to_catalog(\"notify { String({'a'=>['b'],'c'=>[{'d'=>'e'}]}.upcase == {'A'=>['B'],'C'=>[{'D'=>'E'}]}): }\")).to have_resource('Notify[true]')\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/functions/values_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the values function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'returns the values in the hash in the order they appear in a hash iteration' do\n    expect(compile_to_catalog(<<-'SRC'.unindent)).to have_resource('Notify[1 & 2]')\n        $k = {'apples' => 1, 'oranges' => 2}.values\n        notify { \"${k[0]} & ${k[1]}\": }\n      SRC\n  end\n\n  it 'returns an empty array for an empty hash' do\n    expect(compile_to_catalog(<<-'SRC'.unindent)).to have_resource('Notify[0]')\n        $v = {}.values.reduce(0) |$m, $v| { $m+1 }\n        notify { \"${v}\": }\n      SRC\n  end\n\n  it 'includes an undef value if one is present in the hash' do\n    expect(compile_to_catalog(<<-'SRC'.unindent)).to have_resource('Notify[Undef]')\n        $types = {a => undef}.values.map |$v| { $v.type }\n        notify { \"${types[0]}\": }\n    SRC\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/versioncmp_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe \"the versioncmp function\" do\n  before(:each) do\n    loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))\n    Puppet.push_context({:loaders => loaders}, \"test-examples\")\n  end\n\n  after(:each) do\n    Puppet::pop_context()\n  end\n\n  def versioncmp(*args)\n    Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'versioncmp').call({}, *args)\n  end\n\n  let(:type_parser) { Puppet::Pops::Types::TypeParser.singleton }\n\n  it 'should raise an Error if there is less than 2 arguments' do\n    expect { versioncmp('a,b') }.to raise_error(/expects between 2 and 3 arguments, got 1/)\n  end\n\n  it 'should raise an Error if there is more than 3 arguments' do\n    expect { versioncmp('a,b','foo', false, 'bar') }.to raise_error(/expects between 2 and 3 arguments, got 4/)\n  end\n\n  it \"should call Puppet::Util::Package.versioncmp (included in scope)\" do\n    expect(Puppet::Util::Package).to receive(:versioncmp).with('1.2', '1.3', false).and_return(-1)\n\n    expect(versioncmp('1.2', '1.3')).to eq(-1)\n  end\n\n  context \"when ignore_trailing_zeroes is true\" do\n    it \"should equate versions with 2 elements and dots but with unnecessary zero\" do\n      expect(versioncmp(\"10.1.0\", \"10.1\", true)).to eq(0)\n    end\n\n    it \"should equate versions with 1 element and dot but with unnecessary zero\" do\n      expect(versioncmp(\"11.0\", \"11\", true)).to eq(0)\n    end\n\n    it \"should equate versions with 1 element and dot but with unnecessary zeros\" do\n      expect(versioncmp(\"11.00\", \"11\", true)).to eq(0)\n    end\n\n    it \"should equate versions with dots and iregular zeroes\" do\n      expect(versioncmp(\"11.0.00\", \"11\", true)).to eq(0)\n    end\n\n    it \"should equate versions with dashes\" do\n      expect(versioncmp(\"10.1-0\", \"10.1.0-0\", true)).to eq(0)\n    end\n\n    it \"should compare versions with dashes after normalization\" do\n      expect(versioncmp(\"10.1-1\", \"10.1.0-0\", true)).to eq(1)\n    end\n\n    it \"should not normalize versions if zeros are not trailing\" do\n      expect(versioncmp(\"1.1\", \"1.0.1\", true)).to eq(1)\n    end\n  end\n\n  context \"when ignore_trailing_zeroes is false\" do\n    it \"should not equate versions if zeros are not trailing\" do\n      expect(versioncmp(\"1.1\", \"1.0.1\")).to eq(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions/with_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\ndescribe 'the with function' do\n  include PuppetSpec::Compiler\n  include Matchers::Resource\n\n  it 'calls a lambda passing no arguments' do\n    expect(compile_to_catalog(\"with() || { notify { testing: } }\")).to have_resource('Notify[testing]')\n  end\n\n  it 'calls a lambda passing a single argument' do\n    expect(compile_to_catalog('with(1) |$x| { notify { \"testing$x\": } }')).to have_resource('Notify[testing1]')\n  end\n\n  it 'calls a lambda passing more than one argument' do\n    expect(compile_to_catalog('with(1, 2) |*$x| { notify { \"testing${x[0]}, ${x[1]}\": } }')).to have_resource('Notify[testing1, 2]')\n  end\n\n  it 'passes a type reference to a lambda' do\n    expect(compile_to_catalog('notify { test: message => \"data\" } with(Notify[test]) |$x| { notify { \"${x[message]}\": } }')).to have_resource('Notify[data]')\n  end\n\n  it 'errors when not given enough arguments for the lambda' do\n    expect do\n      compile_to_catalog('with(1) |$x, $y| { }')\n    end.to raise_error(/Parameter \\$y is required but no value was given/m)\n  end\nend\n"
  },
  {
    "path": "spec/unit/functions4_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\n\nmodule FunctionAPISpecModule\n  class TestDuck\n  end\n\n  class TestFunctionLoader < Puppet::Pops::Loader::StaticLoader\n    def initialize\n      @constants = {}\n    end\n\n    def add_function(name, function)\n      set_entry(Puppet::Pops::Loader::TypedName.new(:function, name), function, __FILE__)\n    end\n\n    def add_type(name, type)\n      set_entry(Puppet::Pops::Loader::TypedName.new(:type, name), type, __FILE__)\n    end\n\n    def set_entry(typed_name, value, origin = nil)\n      @constants[typed_name] = Puppet::Pops::Loader::Loader::NamedEntry.new(typed_name, value, origin)\n    end\n\n    # override StaticLoader\n    def load_constant(typed_name)\n      @constants[typed_name]\n    end\n  end\nend\n\ndescribe 'the 4x function api' do\n  include FunctionAPISpecModule\n  include PuppetSpec::Pops\n  include PuppetSpec::Scope\n\n  let(:loader) { FunctionAPISpecModule::TestFunctionLoader.new }\n\n  def parse_eval(code, here)\n    if here.respond_to?(:source_location)\n      eval(code, here, here.source_location[0], here.source_location[1])\n    else\n      # this can be removed when ruby < 2.6 is dropped\n      eval(code, here)\n    end\n  end\n\n  it 'allows a simple function to be created without dispatch declaration' do\n    f = Puppet::Functions.create_function('min') do\n      def min(x,y)\n        x <= y ? x : y\n      end\n    end\n\n    # the produced result is a Class inheriting from Function\n    expect(f.class).to be(Class)\n    expect(f.superclass).to be(Puppet::Functions::Function)\n    # and this class had the given name (not a real Ruby class name)\n    expect(f.name).to eql('min')\n  end\n\n  it 'refuses to create functions that are not based on the Function class' do\n    expect do\n      Puppet::Functions.create_function('testing', Object) {}\n    end.to raise_error(ArgumentError, /function 'testing'.*Functions must be based on Puppet::Pops::Functions::Function. Got Object/)\n  end\n\n  it 'refuses to create functions with parameters that are not named with a symbol' do\n    expect do\n      Puppet::Functions.create_function('testing') do\n        dispatch :test do\n          param 'Integer', 'not_symbol'\n        end\n        def test(x)\n        end\n      end\n    end.to raise_error(ArgumentError, /Parameter name argument must be a Symbol/)\n  end\n\n  it 'a function without arguments can be defined and called without dispatch declaration' do\n    f = create_noargs_function_class()\n    func = f.new(:closure_scope, :loader)\n    expect(func.call({})).to eql(10)\n  end\n\n  it 'an error is raised when calling a no arguments function with arguments' do\n    f = create_noargs_function_class()\n    func = f.new(:closure_scope, :loader)\n    expect{func.call({}, 'surprise')}.to raise_error(ArgumentError, \"'test' expects no arguments, got 1\")\n  end\n\n  it 'a simple function can be called' do\n    f = create_min_function_class()\n    # TODO: Bogus parameters, not yet used\n    func = f.new(:closure_scope, :loader)\n    expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n    expect(func.call({}, 10,20)).to eql(10)\n  end\n\n  it 'an error is raised if called with too few arguments' do\n    f = create_min_function_class()\n    # TODO: Bogus parameters, not yet used\n    func = f.new(:closure_scope, :loader)\n    expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n    expect do\n      func.call({}, 10)\n    end.to raise_error(ArgumentError, \"'min' expects 2 arguments, got 1\")\n  end\n\n  it 'an error is raised if called with too many arguments' do\n    f = create_min_function_class()\n    # TODO: Bogus parameters, not yet used\n    func = f.new(:closure_scope, :loader)\n    expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n    expect do\n      func.call({}, 10, 10, 10)\n    end.to raise_error(ArgumentError, \"'min' expects 2 arguments, got 3\")\n  end\n\n  it 'correct dispatch is chosen when zero parameter dispatch exists' do\n    f = create_function_with_no_parameter_dispatch\n    func = f.new(:closure_scope, :loader)\n    expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n    expect(func.call({}, 1)).to eql(1)\n  end\n\n  it 'an error is raised if simple function-name and method are not matched' do\n    expect do\n      create_badly_named_method_function_class()\n    end.to raise_error(ArgumentError, /Function Creation Error, cannot create a default dispatcher for function 'mix', no method with this name found/)\n  end\n\n  it 'the implementation separates dispatchers for different functions' do\n    # this tests that meta programming / construction puts class attributes in the correct class\n    f1 = create_min_function_class()\n    f2 = create_max_function_class()\n\n    d1 = f1.dispatcher\n    d2 = f2.dispatcher\n\n    expect(d1).to_not eql(d2)\n    expect(d1.dispatchers[0]).to_not eql(d2.dispatchers[0])\n  end\n\n  context 'when using regular dispatch' do\n    it 'a function can be created using dispatch and called' do\n      f = create_min_function_class_using_dispatch()\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({}, 3,4)).to eql(3)\n    end\n\n    it 'an error is raised with reference to given parameter names when called with mis-matched arguments' do\n      f = create_min_function_class_using_dispatch()\n      # TODO: Bogus parameters, not yet used\n      func = f.new(:closure_scope, :loader)\n      expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n      expect do\n        func.call({}, 10, 'ten')\n      end.to raise_error(ArgumentError, \"'min' parameter 'b' expects a Numeric value, got String\")\n    end\n\n    it 'an error includes optional indicators for last element' do\n      f = create_function_with_optionals_and_repeated_via_multiple_dispatch()\n      # TODO: Bogus parameters, not yet used\n      func = f.new(:closure_scope, :loader)\n      expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n      expect do\n        func.call({}, 3, 10, 3, \"4\")\n      end.to raise_error(ArgumentError, \"The function 'min' was called with arguments it does not accept. It expects one of:\n  (Numeric x, Numeric y, Numeric a?, Numeric b?, Numeric c*)\n    rejected: parameter 'b' expects a Numeric value, got String\n  (String x, String y, String a+)\n    rejected: parameter 'x' expects a String value, got Integer\")\n    end\n\n    it 'can create optional repeated parameter' do\n      f = create_function_with_repeated\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({})).to eql(0)\n      expect(func.call({}, 1)).to eql(1)\n      expect(func.call({}, 1, 2)).to eql(2)\n\n      f = create_function_with_optional_repeated\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({})).to eql(0)\n      expect(func.call({}, 1)).to eql(1)\n      expect(func.call({}, 1, 2)).to eql(2)\n    end\n\n    it 'can create required repeated parameter' do\n      f = create_function_with_required_repeated\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({}, 1)).to eql(1)\n      expect(func.call({}, 1, 2)).to eql(2)\n      expect { func.call({}) }.to raise_error(ArgumentError, \"'count_args' expects at least 1 argument, got none\")\n    end\n\n    it 'can create scope_param followed by repeated  parameter' do\n      f = create_function_with_scope_param_required_repeat\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({}, 'yay', 1,2,3)).to eql([{}, 'yay',1,2,3])\n    end\n\n    it 'a function can use inexact argument mapping' do\n      f = create_function_with_inexact_dispatch\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({}, 3.0,4.0,5.0)).to eql([Float, Float, Float])\n      expect(func.call({}, 'Apple', 'Banana')).to eql([String, String])\n    end\n\n    it 'a function can be created using dispatch and called' do\n      f = create_min_function_class_disptaching_to_two_methods()\n      func = f.new(:closure_scope, :loader)\n      expect(func.call({}, 3,4)).to eql(3)\n      expect(func.call({}, 'Apple', 'Banana')).to eql('Apple')\n    end\n\n    it 'a function can not be created with parameters declared after a repeated parameter' do\n      expect { create_function_with_param_after_repeated }.to raise_error(ArgumentError,\n        /function 't1'.*Parameters cannot be added after a repeated parameter/)\n    end\n\n    it 'a function can not be created with required parameters declared after optional ones' do\n      expect { create_function_with_rq_after_opt }.to raise_error(ArgumentError,\n        /function 't1'.*A required parameter cannot be added after an optional parameter/)\n    end\n\n    it 'a function can not be created with required repeated parameters declared after optional ones' do\n      expect { create_function_with_rq_repeated_after_opt }.to raise_error(ArgumentError,\n        /function 't1'.*A required repeated parameter cannot be added after an optional parameter/)\n    end\n\n    it 'an error is raised with reference to multiple methods when called with mis-matched arguments' do\n      f = create_min_function_class_disptaching_to_two_methods()\n      # TODO: Bogus parameters, not yet used\n      func = f.new(:closure_scope, :loader)\n      expect(func.is_a?(Puppet::Functions::Function)).to be_truthy\n      expect do\n        func.call({}, 10, '20')\n      end.to raise_error(ArgumentError, \"The function 'min' was called with arguments it does not accept. It expects one of:\n  (Numeric a, Numeric b)\n    rejected: parameter 'b' expects a Numeric value, got String\n  (String s1, String s2)\n    rejected: parameter 's1' expects a String value, got Integer\")\n    end\n\n    context 'an argument_mismatch handler' do\n      let(:func) { create_function_with_mismatch_handler.new(:closure_scope, :loader) }\n\n      it 'is called on matching arguments' do\n        expect { func.call({}, '1') }.to raise_error(ArgumentError, \"'test' It's not OK to pass a string\")\n      end\n\n      it 'is not called unless arguments are matching' do\n        expect { func.call({}, '1', 3) }.to raise_error(ArgumentError, \"'test' expects 1 argument, got 2\")\n      end\n\n      it 'is not included in a signature mismatch description' do\n        expect { func.call({}, 2.3) }.to raise_error { |e| expect(e.message).not_to match(/String/) }\n      end\n    end\n\n    context 'when requesting a type' do\n      it 'responds with a Callable for a single signature' do\n        tf = Puppet::Pops::Types::TypeFactory\n        fc = create_min_function_class_using_dispatch()\n        t = fc.dispatcher.to_type\n        expect(t.class).to be(Puppet::Pops::Types::PCallableType)\n        expect(t.param_types.class).to be(Puppet::Pops::Types::PTupleType)\n        expect(t.param_types.types).to eql([tf.numeric(), tf.numeric()])\n        expect(t.block_type).to be_nil\n      end\n\n      it 'responds with a Variant[Callable...] for multiple signatures' do\n        tf = Puppet::Pops::Types::TypeFactory\n        fc = create_min_function_class_disptaching_to_two_methods()\n        t = fc.dispatcher.to_type\n        expect(t.class).to be(Puppet::Pops::Types::PVariantType)\n        expect(t.types.size).to eql(2)\n        t1 = t.types[0]\n        expect(t1.param_types.class).to be(Puppet::Pops::Types::PTupleType)\n        expect(t1.param_types.types).to eql([tf.numeric(), tf.numeric()])\n        expect(t1.block_type).to be_nil\n        t2 = t.types[1]\n        expect(t2.param_types.class).to be(Puppet::Pops::Types::PTupleType)\n        expect(t2.param_types.types).to eql([tf.string(), tf.string()])\n        expect(t2.block_type).to be_nil\n      end\n    end\n\n    context 'supports lambdas' do\n      it 'such that, a required block can be defined and given as an argument' do\n        the_function = create_function_with_required_block_all_defaults().new(:closure_scope, :loader)\n        result = the_function.call({}, 7) { |a,b| a < b ? a : b }\n        expect(result).to eq(7)\n      end\n\n      it 'such that, a missing required block when called raises an error' do\n        the_function = create_function_with_required_block_all_defaults().new(:closure_scope, :loader)\n        expect do\n          the_function.call({}, 10)\n        end.to raise_error(ArgumentError, \"'test' expects a block\")\n      end\n\n      it 'such that, an optional block can be defined and given as an argument' do\n        the_function = create_function_with_optional_block_all_defaults().new(:closure_scope, :loader)\n        result = the_function.call({}, 4) { |a,b| a < b ? a : b }\n        expect(result).to eql(4)\n      end\n\n      it 'such that, an optional block can be omitted when called and gets the value nil' do\n        the_function = create_function_with_optional_block_all_defaults().new(:closure_scope, :loader)\n        expect(the_function.call({}, 2)).to be_nil\n      end\n\n      it 'such that, a scope can be injected and a block can be used' do\n        the_function = create_function_with_scope_required_block_all_defaults().new(:closure_scope, :loader)\n        expect(the_function.call({}, 1) { |a,b| a < b ? a : b }).to eql(1)\n      end\n    end\n\n    context 'provides signature information' do\n      it 'about capture rest (varargs)' do\n        fc = create_function_with_optionals_and_repeated\n        signatures = fc.signatures\n        expect(signatures.size).to eql(1)\n        signature = signatures[0]\n        expect(signature.last_captures_rest?).to be_truthy\n      end\n\n      it 'about optional and required parameters' do\n        fc = create_function_with_optionals_and_repeated\n        signature = fc.signatures[0]\n        expect(signature.args_range).to eql( [2, Float::INFINITY ] )\n        expect(signature.infinity?(signature.args_range[1])).to be_truthy\n      end\n\n      it 'about block not being allowed' do\n        fc = create_function_with_optionals_and_repeated\n        signature = fc.signatures[0]\n        expect(signature.block_range).to eql( [ 0, 0 ] )\n        expect(signature.block_type).to be_nil\n      end\n\n      it 'about required block' do\n        fc = create_function_with_required_block_all_defaults\n        signature = fc.signatures[0]\n        expect(signature.block_range).to eql( [ 1, 1 ] )\n        expect(signature.block_type).to_not be_nil\n      end\n\n      it 'about optional block' do\n        fc = create_function_with_optional_block_all_defaults\n        signature = fc.signatures[0]\n        expect(signature.block_range).to eql( [ 0, 1 ] )\n        expect(signature.block_type).to_not be_nil\n      end\n\n      it 'about the type' do\n        fc = create_function_with_optional_block_all_defaults\n        signature = fc.signatures[0]\n        expect(signature.type.class).to be(Puppet::Pops::Types::PCallableType)\n      end\n\n      it 'about parameter names obtained from ruby introspection' do\n        fc = create_min_function_class\n        signature = fc.signatures[0]\n        expect(signature.parameter_names).to eql(['x', 'y'])\n      end\n\n      it 'about parameter names specified with dispatch' do\n        fc = create_min_function_class_using_dispatch\n        signature = fc.signatures[0]\n        expect(signature.parameter_names).to eql([:a, :b])\n      end\n\n      it 'about block_name when it is *not* given in the definition' do\n        # neither type, nor name\n        fc = create_function_with_required_block_all_defaults\n        signature = fc.signatures[0]\n        expect(signature.block_name).to eql(:block)\n        # no name given, only type\n        fc = create_function_with_required_block_given_type\n        signature = fc.signatures[0]\n        expect(signature.block_name).to eql(:block)\n      end\n\n      it 'about block_name when it *is* given in the definition' do\n        # neither type, nor name\n        fc = create_function_with_required_block_default_type\n        signature = fc.signatures[0]\n        expect(signature.block_name).to eql(:the_block)\n        # no name given, only type\n        fc = create_function_with_required_block_fully_specified\n        signature = fc.signatures[0]\n        expect(signature.block_name).to eql(:the_block)\n      end\n    end\n\n    context 'supports calling other functions' do\n      before(:each) do\n        Puppet.push_context( {:loaders => Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))})\n      end\n\n      after(:each) do\n        Puppet.pop_context()\n      end\n\n      it 'such that, other functions are callable by name' do\n        fc = Puppet::Functions.create_function('test') do\n          def test()\n            # Call a function available in the puppet system\n            call_function('assert_type', 'Integer', 10)\n          end\n        end\n        # initiate the function the same way the loader initiates it\n        f = fc.new(:closure_scope, Puppet.lookup(:loaders).puppet_system_loader)\n        expect(f.call({})).to eql(10)\n      end\n\n      it 'such that, calling a non existing function raises an error' do\n        fc = Puppet::Functions.create_function('test') do\n          def test()\n            # Call a function not available in the puppet system\n            call_function('no_such_function', 'Integer', 'hello')\n          end\n        end\n        # initiate the function the same way the loader initiates it\n        f = fc.new(:closure_scope, Puppet.lookup(:loaders).puppet_system_loader)\n        expect{f.call({})}.to raise_error(ArgumentError, \"Function test(): Unknown function: 'no_such_function'\")\n      end\n    end\n\n    context 'functions in a context with a compiler' do\n      before(:each) do\n        Puppet.push_context( {:loaders => Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))})\n      end\n\n      after(:each) do\n        Puppet.pop_context()\n      end\n\n      before(:each) do\n        Puppet[:strict_variables] = true\n      end\n\n      let(:parser) {  Puppet::Pops::Parser::EvaluatingParser.new }\n      let(:node) { 'node.example.com' }\n      let(:scope) { s = create_test_scope_for_node(node); s }\n      let(:loader) { Puppet::Pops::Loaders.find_loader(nil) }\n\n      context 'supports calling ruby functions with lambda from puppet' do\n        it 'function with required block can be called' do\n          # construct ruby function to call\n          fc = Puppet::Functions.create_function('testing::test') do\n            dispatch :test do\n              param 'Integer', :x\n              # block called 'the_block', and using \"all_callables\"\n              required_block_param #(all_callables(), 'the_block')\n            end\n            def test(x)\n              # call the block with x\n              yield(x)\n            end\n          end\n          # add the function to the loader (as if it had been loaded from somewhere)\n          the_loader = loader\n          f = fc.new({}, the_loader)\n          loader.set_entry(Puppet::Pops::Loader::TypedName.new(:function, 'testing::test'), f)\n          # evaluate a puppet call\n          source = \"testing::test(10) |$x| { $x+1 }\"\n          program = parser.parse_string(source, __FILE__)\n          expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(the_loader)\n          expect(parser.evaluate(scope, program)).to eql(11)\n        end\n      end\n\n      it 'supports injection of a cache' do\n        the_function = create_function_with_cache_param.new(:closure_scope, :loader)\n        expect(the_function.call(scope, 'key', 10)).to eql(nil)\n        expect(the_function.call(scope, 'key', 20)).to eql(10)\n        expect(the_function.call(scope, 'key', 30)).to eql(20)\n      end\n    end\n\n    context 'reports meaningful errors' do\n      let(:parser) {  Puppet::Pops::Parser::EvaluatingParser.new }\n\n      it 'syntax error in local type is reported with puppet source, puppet location, and ruby file containing function' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        expect do\n          parse_eval(<<-CODE, here)\n            Puppet::Functions.create_function('testing::test') do\n              local_types do\n                type 'MyType += Array[Integer]'\n              end\n              dispatch :test do\n                param 'MyType', :x\n              end\n              def test(x)\n                x\n              end\n            end\n          CODE\n        end.to raise_error(/MyType \\+\\= Array.*<Syntax error at '\\+\\=' \\(line: 1, column: [0-9]+\\)>.*functions4_spec\\.rb.*/m)\n        # Note that raised error reports this spec file as the function source since the function is defined here\n      end\n\n      it 'syntax error in param type is reported with puppet source, puppet location, and ruby file containing function' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        expect do\n          parse_eval(<<-CODE, here)\n            Puppet::Functions.create_function('testing::test') do\n              dispatch :test do\n                param 'Array[1+=1]', :x\n              end\n              def test(x)\n                x\n              end\n            end\n          CODE\n        end.to raise_error(/Parsing of type string '\"Array\\[1\\+=1\\]\"' failed with message: <Syntax error at '\\]' \\(line: 1, column: [0-9]+\\)>/m)\n      end\n    end\n\n    context 'can use a loader when parsing types in function dispatch, and' do\n      let(:parser) {  Puppet::Pops::Parser::EvaluatingParser.new }\n\n      it 'uses return_type to validate returned value' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        fc = parse_eval(<<-CODE, here)\n          Puppet::Functions.create_function('testing::test') do\n            dispatch :test do\n              param 'Integer', :x\n              return_type 'String'\n            end\n            def test(x)\n              x\n            end\n          end\n        CODE\n        the_loader.add_function('testing::test', fc.new({}, the_loader))\n        program = parser.parse_string('testing::test(10)', __FILE__)\n        expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(the_loader)\n        expect { parser.evaluate({}, program) }.to raise_error(Puppet::Error,\n          /value returned from function 'test' has wrong type, expects a String value, got Integer/)\n      end\n\n      it 'resolve a referenced Type alias' do\n        the_loader = loader()\n        the_loader.add_type('myalias', type_alias_t('MyAlias', 'Integer'))\n        here = get_binding(the_loader)\n        fc = parse_eval(<<-CODE, here)\n          Puppet::Functions.create_function('testing::test') do\n            dispatch :test do\n              param 'MyAlias', :x\n              return_type 'MyAlias'\n            end\n            def test(x)\n              x\n            end\n          end\n        CODE\n        the_loader.add_function('testing::test', fc.new({}, the_loader))\n        program = parser.parse_string('testing::test(10)', __FILE__)\n        expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(the_loader)\n        expect(parser.evaluate({}, program)).to eql(10)\n      end\n\n      it 'reports a reference to an unresolved type' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        fc = parse_eval(<<-CODE, here)\n          Puppet::Functions.create_function('testing::test') do\n            dispatch :test do\n              param 'MyAlias', :x\n            end\n            def test(x)\n              x\n            end\n          end\n        CODE\n        the_loader.add_function('testing::test', fc.new({}, the_loader))\n        program = parser.parse_string('testing::test(10)', __FILE__)\n        expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(the_loader)\n        expect { parser.evaluate({}, program) }.to raise_error(Puppet::Error, /parameter 'x' references an unresolved type 'MyAlias'/)\n      end\n\n      it 'create local Type aliases' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        fc = parse_eval(<<-CODE, here)\n          Puppet::Functions.create_function('testing::test') do\n            local_types do\n              type 'MyType = Array[Integer]'\n            end\n            dispatch :test do\n              param 'MyType', :x\n            end\n            def test(x)\n              x\n            end\n          end\n        CODE\n        the_loader.add_function('testing::test', fc.new({}, the_loader))\n        program = parser.parse_string('testing::test([10,20])', __FILE__)\n        expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(the_loader)\n        expect(parser.evaluate({}, program)).to eq([10,20])\n      end\n\n      it 'create nested local Type aliases' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        fc = parse_eval(<<-CODE, here)\n          Puppet::Functions.create_function('testing::test') do\n            local_types do\n              type 'InnerType = Array[Integer]'\n              type 'OuterType = Hash[String,InnerType]'\n            end\n            dispatch :test do\n              param 'OuterType', :x\n            end\n            def test(x)\n              x\n            end\n          end\n        CODE\n        the_loader.add_function('testing::test', fc.new({}, the_loader))\n        program = parser.parse_string(\"testing::test({'x' => [10,20]})\", __FILE__)\n        expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(the_loader)\n        expect(parser.evaluate({}, program)).to eq({'x' => [10,20]})\n      end\n\n      it 'create self referencing local Type aliases' do\n        the_loader = loader()\n        here = get_binding(the_loader)\n        fc = parse_eval(<<-CODE, here)\n          Puppet::Functions.create_function('testing::test') do\n            local_types do\n              type 'Tree = Hash[String,Variant[String,Tree]]'\n            end\n            dispatch :test do\n              param 'Tree', :x\n            end\n            def test(x)\n              x\n            end\n          end\n        CODE\n        the_loader.add_function('testing::test', fc.new({}, the_loader))\n        program = parser.parse_string(\"testing::test({'x' => {'y' => 'n'}})\", __FILE__)\n        expect(Puppet::Pops::Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(the_loader)\n        expect(parser.evaluate({}, program)).to eq({'x' => {'y' => 'n'}})\n      end\n    end\n  end\n\n  def create_noargs_function_class\n    Puppet::Functions.create_function('test') do\n      def test()\n        10\n      end\n    end\n  end\n\n  def create_min_function_class\n    Puppet::Functions.create_function('min') do\n      def min(x,y)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_max_function_class\n    Puppet::Functions.create_function('max') do\n      def max(x,y)\n        x >= y ? x : y\n      end\n    end\n  end\n\n  def create_badly_named_method_function_class\n    Puppet::Functions.create_function('mix') do\n      def mix_up(x,y)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_min_function_class_using_dispatch\n    Puppet::Functions.create_function('min') do\n        dispatch :min do\n          param 'Numeric', :a\n          param 'Numeric', :b\n        end\n      def min(x,y)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_min_function_class_disptaching_to_two_methods\n    Puppet::Functions.create_function('min') do\n      dispatch :min do\n        param 'Numeric', :a\n        param 'Numeric', :b\n      end\n\n      dispatch :min_s do\n        param 'String', :s1\n        param 'String', :s2\n      end\n\n      def min(x,y)\n        x <= y ? x : y\n      end\n\n      def min_s(x,y)\n        cmp = (x.downcase <=> y.downcase)\n        cmp <= 0 ? x : y\n      end\n    end\n  end\n\n  def create_function_with_optionals_and_repeated\n    Puppet::Functions.create_function('min') do\n      def min(x,y,a=1, b=1, *c)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_function_with_optionals_and_repeated_via_dispatch\n    Puppet::Functions.create_function('min') do\n      dispatch :min do\n        param 'Numeric', :x\n        param 'Numeric', :y\n        optional_param 'Numeric', :a\n        optional_param 'Numeric', :b\n        repeated_param 'Numeric', :c\n      end\n      def min(x,y,a=1, b=1, *c)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_function_with_optionals_and_repeated_via_multiple_dispatch\n    Puppet::Functions.create_function('min') do\n      dispatch :min do\n        param 'Numeric', :x\n        param 'Numeric', :y\n        optional_param 'Numeric', :a\n        optional_param 'Numeric', :b\n        repeated_param 'Numeric', :c\n      end\n      dispatch :min do\n        param 'String', :x\n        param 'String', :y\n        required_repeated_param 'String', :a\n      end\n      def min(x,y,a=1, b=1, *c)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_function_with_required_repeated_via_dispatch\n    Puppet::Functions.create_function('min') do\n      dispatch :min do\n        param 'Numeric', :x\n        param 'Numeric', :y\n        required_repeated_param 'Numeric', :z\n      end\n      def min(x,y, *z)\n        x <= y ? x : y\n      end\n    end\n  end\n\n  def create_function_with_repeated\n    Puppet::Functions.create_function('count_args') do\n      dispatch :count_args do\n        repeated_param 'Any', :c\n      end\n      def count_args(*c)\n        c.size\n      end\n    end\n  end\n\n  def create_function_with_optional_repeated\n    Puppet::Functions.create_function('count_args') do\n      dispatch :count_args do\n        optional_repeated_param 'Any', :c\n      end\n      def count_args(*c)\n        c.size\n      end\n    end\n  end\n\n  def create_function_with_required_repeated\n    Puppet::Functions.create_function('count_args') do\n      dispatch :count_args do\n        required_repeated_param 'Any', :c\n      end\n      def count_args(*c)\n        c.size\n      end\n    end\n  end\n\n  def create_function_with_inexact_dispatch\n    Puppet::Functions.create_function('t1') do\n      dispatch :t1 do\n        param 'Numeric', :x\n        param 'Numeric', :y\n        repeated_param 'Numeric', :z\n      end\n      dispatch :t1 do\n        param 'String', :x\n        param 'String', :y\n        repeated_param 'String', :z\n      end\n      def t1(first, *x)\n        [first.class, *x.map {|e|e.class}]\n      end\n    end\n  end\n\n  def create_function_with_rq_after_opt\n    Puppet::Functions.create_function('t1') do\n      dispatch :t1 do\n        optional_param 'Numeric', :x\n        param 'Numeric', :y\n      end\n      def t1(*x)\n        x\n      end\n    end\n  end\n\n  def create_function_with_rq_repeated_after_opt\n    Puppet::Functions.create_function('t1') do\n      dispatch :t1 do\n        optional_param 'Numeric', :x\n        required_repeated_param 'Numeric', :y\n      end\n      def t1(x, *y)\n        x\n      end\n    end\n  end\n\n  def create_function_with_param_after_repeated\n    Puppet::Functions.create_function('t1') do\n      dispatch :t1 do\n        repeated_param 'Numeric', :x\n        param 'Numeric', :y\n      end\n      def t1(*x)\n        x\n      end\n    end\n  end\n\n# DEAD TODO REMOVE\n#  def create_function_with_param_injection_regular\n#    Puppet::Functions.create_function('test', Puppet::Functions::InternalFunction) do\n#      attr_injected Puppet::Pops::Types::TypeFactory.type_of(FunctionAPISpecModule::TestDuck), :test_attr\n#      attr_injected Puppet::Pops::Types::TypeFactory.string(), :test_attr2, \"a_string\"\n#      attr_injected_producer Puppet::Pops::Types::TypeFactory.integer(), :serial, \"an_int\"\n#\n#      dispatch :test do\n#        injected_param Puppet::Pops::Types::TypeFactory.string, :x, 'a_string'\n#        injected_producer_param Puppet::Pops::Types::TypeFactory.integer, :y, 'an_int'\n#        param 'Scalar', :a\n#        param 'Scalar', :b\n#      end\n#\n#      def test(x,y,a,b)\n#        y_produced = y.produce(nil)\n#        \"#{x}! #{a}, and #{b} < #{y_produced} = #{ !!(a < y_produced && b < y_produced)}\"\n#      end\n#    end\n#  end\n\n  def create_function_with_required_block_all_defaults\n    Puppet::Functions.create_function('test') do\n      dispatch :test do\n        param 'Integer', :x\n        # use defaults, any callable, name is 'block'\n        block_param\n      end\n      def test(x)\n        yield(8,x)\n      end\n    end\n  end\n\n  def create_function_with_scope_required_block_all_defaults\n    Puppet::Functions.create_function('test', Puppet::Functions::InternalFunction) do\n      dispatch :test do\n        scope_param\n        param 'Integer', :x\n        # use defaults, any callable, name is 'block'\n        required_block_param\n      end\n      def test(scope, x)\n        yield(3,x)\n      end\n    end\n  end\n\n  def create_function_with_required_block_default_type\n    Puppet::Functions.create_function('test') do\n      dispatch :test do\n        param 'Integer', :x\n        # use defaults, any callable, name is 'block'\n        required_block_param :the_block\n      end\n      def test(x)\n        yield\n      end\n    end\n  end\n\n  def create_function_with_scope_param_required_repeat\n    Puppet::Functions.create_function('test', Puppet::Functions::InternalFunction) do\n      dispatch :test do\n        scope_param\n        param 'Any', :extra\n        repeated_param 'Any', :the_block\n      end\n      def test(scope, *args)\n        [scope, *args]\n      end\n    end\n  end\n\n  def create_function_with_required_block_given_type\n    Puppet::Functions.create_function('test') do\n      dispatch :test do\n        param 'Integer', :x\n        required_block_param\n      end\n      def test(x)\n        yield\n      end\n    end\n  end\n\n  def create_function_with_required_block_fully_specified\n    Puppet::Functions.create_function('test') do\n      dispatch :test do\n        param 'Integer', :x\n        # use defaults, any callable, name is 'block'\n        required_block_param('Callable', :the_block)\n      end\n      def test(x)\n        yield\n      end\n    end\n  end\n\n  def create_function_with_optional_block_all_defaults\n    Puppet::Functions.create_function('test') do\n      dispatch :test do\n        param 'Integer', :x\n        # use defaults, any callable, name is 'block'\n        optional_block_param\n      end\n      def test(x)\n        yield(5,x) if block_given?\n      end\n    end\n  end\n\n  def create_function_with_no_parameter_dispatch\n    Puppet::Functions.create_function('test') do\n      dispatch :test_no_args do\n      end\n      dispatch :test_one_arg do\n        param 'Integer', :x\n      end\n      def test_no_args\n        0\n      end\n      def test_one_arg(x)\n        x\n      end\n    end\n  end\n\n  def create_function_with_mismatch_handler\n    Puppet::Functions.create_function('test') do\n      dispatch :test do\n        param 'Integer', :x\n      end\n\n      argument_mismatch :on_error do\n        param 'String', :x\n      end\n\n      def test(x)\n        yield(5,x) if block_given?\n      end\n\n      def on_error(x)\n        \"It's not OK to pass a string\"\n      end\n    end\n  end\n\n  def create_function_with_cache_param\n    Puppet::Functions.create_function('test', Puppet::Functions::InternalFunction) do\n      dispatch :test do\n        cache_param\n        param 'String', :key\n        param 'Any', :value\n      end\n      def test(cache, k, v)\n        h = cache.retrieve(self)\n        previous = h[k]\n        h[k] = v\n        previous\n      end\n    end\n  end\n\n  def type_alias_t(name, type_string)\n    type_expr = Puppet::Pops::Parser::EvaluatingParser.new.parse_string(type_string)\n    Puppet::Pops::Types::TypeFactory.type_alias(name, type_expr)\n  end\n\n  def get_binding(loader_injected_arg)\n    binding\n  end\nend\n"
  },
  {
    "path": "spec/unit/gettext/config_spec.rb",
    "content": "require 'puppet/gettext/config'\nrequire 'spec_helper'\n\ndescribe Puppet::GettextConfig do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n  include Puppet::GettextConfig\n\n  let(:local_path) do\n    local_path ||= Puppet::GettextConfig::LOCAL_PATH\n  end\n\n  let(:windows_path) do\n    windows_path ||= Puppet::GettextConfig::WINDOWS_PATH\n  end\n\n  let(:posix_path) do\n    windows_path ||= Puppet::GettextConfig::POSIX_PATH\n  end\n\n  before(:each) do\n    allow(Puppet::GettextConfig).to receive(:gettext_loaded?).and_return(true)\n  end\n\n  after(:each) do\n    Puppet::GettextConfig.set_locale('en')\n    Puppet::GettextConfig.delete_all_text_domains\n  end\n\n  # These tests assume gettext is enabled, but it will be disabled when the\n  # first time the `Puppet[:disable_i18n]` setting is resolved\n  around(:each) do |example|\n    disabled = Puppet::GettextConfig.instance_variable_get(:@gettext_disabled)\n    Puppet::GettextConfig.instance_variable_set(:@gettext_disabled, false)\n    begin\n      example.run\n    ensure\n      Puppet::GettextConfig.instance_variable_set(:@gettext_disabled, disabled)\n    end\n  end\n\n  describe 'setting and getting the locale' do\n    it 'should return \"en\" when gettext is unavailable' do\n      allow(Puppet::GettextConfig).to receive(:gettext_loaded?).and_return(false)\n\n      expect(Puppet::GettextConfig.current_locale).to eq('en')\n    end\n\n    it 'should allow the locale to be set' do\n      Puppet::GettextConfig.set_locale('hu')\n      expect(Puppet::GettextConfig.current_locale).to eq('hu')\n    end\n  end\n\n  describe 'translation mode selection' do\n    it 'should select PO mode when given a local config path' do\n      expect(Puppet::GettextConfig.translation_mode(local_path)).to eq(:po)\n    end\n\n    it 'should select PO mode when given a non-package config path' do\n      expect(Puppet::GettextConfig.translation_mode('../fake/path')).to eq(:po)\n    end\n\n    it 'should select MO mode when given a Windows package config path' do\n      expect(Puppet::GettextConfig.translation_mode(windows_path)).to eq(:mo)\n    end\n\n    it 'should select MO mode when given a POSIX package config path' do\n      expect(Puppet::GettextConfig.translation_mode(posix_path)).to eq(:mo)\n    end\n  end\n\n  describe 'loading translations' do\n    context 'when given a nil locale path' do\n      it 'should return false' do\n        expect(Puppet::GettextConfig.load_translations('puppet', nil, :po)).to be false\n      end\n    end\n\n    context 'when given a valid locale file location' do\n      it 'should return true' do\n        expect(Puppet::GettextConfig).to receive(:add_repository_to_domain).with('puppet', local_path, :po, anything)\n\n        expect(Puppet::GettextConfig.load_translations('puppet', local_path, :po)).to be true\n      end\n    end\n\n    context 'when given a bad file format' do\n      it 'should raise an exception' do\n        expect { Puppet::GettextConfig.load_translations('puppet', local_path, :bad_format) }.to raise_error(Puppet::Error)\n      end\n    end\n  end\n\n  describe \"setting up text domains\" do\n    it 'can create the default text domain after another is set' do\n      Puppet::GettextConfig.delete_all_text_domains\n      FastGettext.text_domain = 'other'\n      Puppet::GettextConfig.create_default_text_domain\n    end\n\n    it 'should add puppet translations to the default text domain' do\n      expect(Puppet::GettextConfig).to receive(:load_translations).with('puppet', local_path, :po, Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN).and_return(true)\n\n      Puppet::GettextConfig.create_default_text_domain\n      expect(Puppet::GettextConfig.loaded_text_domains).to include(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN)\n    end\n\n    it 'should copy default translations when creating a non-default text domain' do\n      Puppet::GettextConfig.reset_text_domain(:test)\n      expect(Puppet::GettextConfig.loaded_text_domains).to include(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN, :test)\n    end\n\n    it 'should normalize domain name when creating a non-default text domain' do\n      Puppet::GettextConfig.reset_text_domain('test')\n      expect(Puppet::GettextConfig.loaded_text_domains).to include(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN, :test)\n    end\n  end\n\n  describe \"clearing the configured text domain\" do\n    it 'succeeds' do\n      Puppet::GettextConfig.clear_text_domain\n      expect(FastGettext.text_domain).to eq(FastGettext.default_text_domain)\n    end\n\n    it 'falls back to default' do\n      Puppet::GettextConfig.reset_text_domain(:test)\n      expect(FastGettext.text_domain).to eq(:test)\n      Puppet::GettextConfig.clear_text_domain\n      expect(FastGettext.text_domain).to eq(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN)\n    end\n  end\n\n  describe \"deleting text domains\" do\n    it 'can delete a text domain by name' do\n      Puppet::GettextConfig.reset_text_domain(:test)\n      expect(Puppet::GettextConfig.loaded_text_domains).to include(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN, :test)\n      Puppet::GettextConfig.delete_text_domain(:test)\n      expect(Puppet::GettextConfig.loaded_text_domains).to eq([Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN])\n    end\n\n    it 'can delete all non-default text domains' do\n      Puppet::GettextConfig.reset_text_domain(:test)\n      expect(Puppet::GettextConfig.loaded_text_domains).to include(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN, :test)\n      Puppet::GettextConfig.delete_environment_text_domains\n      expect(Puppet::GettextConfig.loaded_text_domains).to eq([Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN])\n    end\n\n    it 'can delete all text domains' do\n      Puppet::GettextConfig.reset_text_domain(:test)\n      expect(Puppet::GettextConfig.loaded_text_domains).to include(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN, :test)\n      Puppet::GettextConfig.delete_all_text_domains\n      expect(Puppet::GettextConfig.loaded_text_domains).to be_empty\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/gettext/module_loading_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/modules'\nrequire 'puppet_spec/files'\n\nrequire 'puppet/gettext/module_translations'\n\ndescribe Puppet::ModuleTranslations do\n  include PuppetSpec::Files\n\n  describe \"loading translations from the module path\" do\n    let(:modpath) { tmpdir('modpath') }\n\n    let(:module_a) { PuppetSpec::Modules.create(\n      \"mod_a\",\n      modpath,\n      :metadata => {\n        :author => 'foo'\n      },\n      :environment => double(\"environment\"))\n    }\n\n    let(:module_b) { PuppetSpec::Modules.create(\n      \"mod_b\",\n      modpath,\n      :metadata => {\n        :author => 'foo'\n      },\n      :environment => double(\"environment\"))\n    }\n\n    it \"should attempt to load translations only for modules that have them\" do\n      expect(module_a).to receive(:has_translations?).and_return(false)\n      expect(module_b).to receive(:has_translations?).and_return(true)\n      expect(Puppet::GettextConfig).to receive(:load_translations).with(\"foo-mod_b\", File.join(modpath, \"mod_b\", \"locales\"), :po).and_return(true)\n\n      Puppet::ModuleTranslations.load_from_modulepath([module_a, module_b])\n    end\n  end\n\n  describe \"loading translations from $vardir\" do\n    let(:vardir) {\n      dir_containing(\"vardir\",\n        { \"locales\" => { \"ja\" => { \"foo-mod_a.po\" => \"\" } } })\n    }\n\n    it \"should attempt to load translations for the current locale\" do\n      expect(Puppet::GettextConfig).to receive(:current_locale).and_return(\"ja\")\n      expect(Puppet::GettextConfig).to receive(:load_translations).with(\"foo-mod_a\", File.join(vardir, \"locales\"), :po).and_return(true)\n\n      Puppet::ModuleTranslations.load_from_vardir(vardir)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/graph/key_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/graph'\n\ndescribe Puppet::Graph::Key do\n  it \"produces the next in the sequence\" do\n    key = Puppet::Graph::Key.new\n\n    expect(key.next).to be > key\n  end\n\n  it \"produces a key after itself but before next\" do\n    key = Puppet::Graph::Key.new\n    expect(key.down).to be > key\n    expect(key.down).to be < key.next\n  end\n\n  it \"downward keys of the same group are in sequence\" do\n    key = Puppet::Graph::Key.new\n\n    first = key.down\n    middle = key.down.next\n    last = key.down.next.next\n\n    expect(first).to be < middle\n    expect(middle).to be < last\n    expect(last).to be < key.next\n  end\n\n  it \"downward keys in sequential groups are in sequence\" do\n    key = Puppet::Graph::Key.new\n\n    first = key.down\n    middle = key.next\n    last = key.next.down\n\n    expect(first).to be < middle\n    expect(middle).to be < last\n    expect(last).to be < key.next.next\n  end\nend\n"
  },
  {
    "path": "spec/unit/graph/rb_tree_map_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/graph'\n\ndescribe Puppet::Graph::RbTreeMap do\n  describe \"#push\" do\n    it \"should allow a new element to be added\" do\n      subject[5] = 'foo'\n      expect(subject.size).to eq(1)\n\n      expect(subject[5]).to eq('foo')\n    end\n\n    it \"should replace the old value if the key is in tree already\" do\n      subject[0] = 10\n      subject[0] = 20\n\n      expect(subject[0]).to eq(20)\n      expect(subject.size).to eq(1)\n    end\n\n    it \"should be able to add a large number of elements\" do\n      (1..1000).each {|i| subject[i] = i.to_s}\n\n      expect(subject.size).to eq(1000)\n    end\n\n    it \"should create a root node if the tree was empty\" do\n      expect(subject.instance_variable_get(:@root)).to be_nil\n\n      subject[5] = 'foo'\n\n      expect(subject.instance_variable_get(:@root)).to be_a(Puppet::Graph::RbTreeMap::Node)\n    end\n  end\n\n  describe \"#size\" do\n    it \"should be 0 for en empty tree\" do\n      expect(subject.size).to eq(0)\n    end\n\n    it \"should correctly report the size for a non-empty tree\" do\n      (1..10).each {|i| subject[i] = i.to_s}\n\n      expect(subject.size).to eq(10)\n    end\n  end\n\n  describe \"#has_key?\" do\n    it \"should be true if the tree contains the key\" do\n      subject[1] = 2\n\n      expect(subject).to be_has_key(1)\n    end\n\n    it \"should be true if the tree contains the key and its value is nil\" do\n      subject[0] = nil\n\n      expect(subject).to be_has_key(0)\n    end\n\n    it \"should be false if the tree does not contain the key\" do\n      subject[1] = 2\n\n      expect(subject).not_to be_has_key(2)\n    end\n\n    it \"should be false if the tree is empty\" do\n      expect(subject).not_to be_has_key(5)\n    end\n  end\n\n  describe \"#get\" do\n    it \"should return the value at the key\" do\n      subject[1] = 2\n      subject[3] = 4\n\n      expect(subject.get(1)).to eq(2)\n      expect(subject.get(3)).to eq(4)\n    end\n\n    it \"should return nil if the tree is empty\" do\n      expect(subject[1]).to be_nil\n    end\n\n    it \"should return nil if the key is not in the tree\" do\n      subject[1] = 2\n\n      expect(subject[3]).to be_nil\n    end\n\n    it \"should return nil if the value at the key is nil\" do\n      subject[1] = nil\n\n      expect(subject[1]).to be_nil\n    end\n  end\n\n  describe \"#min_key\" do\n    it \"should return the smallest key in the tree\" do\n      [4,8,12,3,6,2,-4,7].each do |i|\n        subject[i] = i.to_s\n      end\n\n      expect(subject.min_key).to eq(-4)\n    end\n\n    it \"should return nil if the tree is empty\" do\n      expect(subject.min_key).to be_nil\n    end\n  end\n\n  describe \"#max_key\" do\n    it \"should return the largest key in the tree\" do\n      [4,8,12,3,6,2,-4,7].each do |i|\n        subject[i] = i.to_s\n      end\n\n      expect(subject.max_key).to eq(12)\n    end\n\n    it \"should return nil if the tree is empty\" do\n      expect(subject.max_key).to be_nil\n    end\n  end\n\n  describe \"#delete\" do\n    before :each do\n      subject[1] = '1'\n      subject[0] = '0'\n      subject[2] = '2'\n    end\n\n    it \"should return the value at the key deleted\" do\n      expect(subject.delete(0)).to eq('0')\n      expect(subject.delete(1)).to eq('1')\n      expect(subject.delete(2)).to eq('2')\n      expect(subject.size).to eq(0)\n    end\n\n    it \"should be able to delete the last node\" do\n      tree = described_class.new\n      tree[1] = '1'\n\n      expect(tree.delete(1)).to eq('1')\n      expect(tree).to be_empty\n    end\n\n    it \"should be able to delete the root node\" do\n      expect(subject.delete(1)).to eq('1')\n\n      expect(subject.size).to eq(2)\n\n      expect(subject.to_hash).to eq({\n        :node => {\n          :key => 2,\n          :value => '2',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 0,\n            :value => '0',\n            :color => :red,\n          }\n        }\n      })\n    end\n\n    it \"should be able to delete the left child\" do\n      expect(subject.delete(0)).to eq('0')\n\n      expect(subject.size).to eq(2)\n\n      expect(subject.to_hash).to eq({\n        :node => {\n          :key => 2,\n          :value => '2',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 1,\n            :value => '1',\n            :color => :red,\n          }\n        }\n      })\n    end\n\n    it \"should be able to delete the right child\" do\n      expect(subject.delete(2)).to eq('2')\n\n      expect(subject.size).to eq(2)\n\n      expect(subject.to_hash).to eq({\n        :node => {\n          :key => 1,\n          :value => '1',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 0,\n            :value => '0',\n            :color => :red,\n          }\n        }\n      })\n    end\n\n    it \"should be able to delete the left child if it is a subtree\" do\n      (3..6).each {|i| subject[i] = i.to_s}\n\n      expect(subject.delete(1)).to eq('1')\n\n      expect(subject.to_hash).to eq({\n        :node => {\n          :key => 5,\n          :value => '5',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 3,\n            :value => '3',\n            :color => :red,\n          },\n          :left => {\n            :node => {\n              :key => 2,\n              :value => '2',\n              :color => :black,\n            },\n            :left => {\n              :node => {\n                :key => 0,\n                :value => '0',\n                :color => :red,\n              },\n            },\n          },\n          :right => {\n            :node => {\n              :key => 4,\n              :value => '4',\n              :color => :black,\n            },\n          },\n        },\n        :right => {\n          :node => {\n            :key => 6,\n            :value => '6',\n            :color => :black,\n          },\n        },\n      })\n    end\n\n    it \"should be able to delete the right child if it is a subtree\" do\n      (3..6).each {|i| subject[i] = i.to_s}\n\n      expect(subject.delete(5)).to eq('5')\n\n      expect(subject.to_hash).to eq({\n        :node => {\n          :key => 3,\n          :value => '3',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 1,\n            :value => '1',\n            :color => :red,\n          },\n          :left => {\n            :node => {\n              :key => 0,\n              :value => '0',\n              :color => :black,\n            },\n          },\n          :right => {\n            :node => {\n              :key => 2,\n              :value => '2',\n              :color => :black,\n            },\n          },\n        },\n        :right => {\n          :node => {\n            :key => 6,\n            :value => '6',\n            :color => :black,\n          },\n          :left => {\n            :node => {\n              :key => 4,\n              :value => '4',\n              :color => :red,\n            },\n          },\n        },\n      })\n    end\n\n    it \"should return nil if the tree is empty\" do\n      tree = described_class.new\n\n      expect(tree.delete(14)).to be_nil\n\n      expect(tree.size).to eq(0)\n    end\n\n    it \"should return nil if the key is not in the tree\" do\n      (0..4).each {|i| subject[i] = i.to_s}\n\n      expect(subject.delete(2.5)).to be_nil\n      expect(subject.size).to eq(5)\n    end\n\n    it \"should return nil if the key is larger than the maximum key\" do\n      expect(subject.delete(100)).to be_nil\n      expect(subject.size).to eq(3)\n    end\n\n    it \"should return nil if the key is smaller than the minimum key\" do\n      expect(subject.delete(-1)).to be_nil\n      expect(subject.size).to eq(3)\n    end\n  end\n\n  describe \"#empty?\" do\n    it \"should return true if the tree is empty\" do\n      expect(subject).to be_empty\n    end\n\n    it \"should return false if the tree is not empty\" do\n      subject[5] = 10\n\n      expect(subject).not_to be_empty\n    end\n  end\n\n  describe \"#delete_min\" do\n    it \"should delete the smallest element of the tree\" do\n      (1..15).each {|i| subject[i] = i.to_s}\n\n      expect(subject.delete_min).to eq('1')\n      expect(subject.size).to eq(14)\n    end\n\n    it \"should return nil if the tree is empty\" do\n      expect(subject.delete_min).to be_nil\n    end\n  end\n\n  describe \"#delete_max\" do\n    it \"should delete the largest element of the tree\" do\n      (1..15).each {|i| subject[i] = i.to_s}\n\n      expect(subject.delete_max).to eq('15')\n      expect(subject.size).to eq(14)\n    end\n\n    it \"should return nil if the tree is empty\" do\n      expect(subject.delete_max).to be_nil\n    end\n  end\n\n  describe \"#each\" do\n    it \"should yield each pair in the tree in order if a block is provided\" do\n      # Insert in reverse to demonstrate they aren't being yielded in insertion order\n      (1..5).to_a.reverse_each {|i| subject[i] = i.to_s}\n\n      nodes = []\n      subject.each do |key,value|\n        nodes << [key,value]\n      end\n\n      expect(nodes).to eq((1..5).map {|i| [i, i.to_s]})\n    end\n\n    it \"should do nothing if the tree is empty\" do\n      subject.each do |key,value|\n        raise \"each on an empty tree incorrectly yielded #{key}, #{value}\"\n      end\n    end\n  end\n\n  describe \"#isred\" do\n    it \"should return true if the node is red\" do\n      node = Puppet::Graph::RbTreeMap::Node.new(1,2)\n      node.color = :red\n\n      expect(subject.send(:isred, node)).to eq(true)\n    end\n\n    it \"should return false if the node is black\" do\n      node = Puppet::Graph::RbTreeMap::Node.new(1,2)\n      node.color = :black\n\n      expect(subject.send(:isred, node)).to eq(false)\n    end\n\n    it \"should return false if the node is nil\" do\n      expect(subject.send(:isred, nil)).to eq(false)\n    end\n  end\nend\n\ndescribe Puppet::Graph::RbTreeMap::Node do\n  let(:tree) { Puppet::Graph::RbTreeMap.new }\n  let(:subject) { tree.instance_variable_get(:@root) }\n\n  before :each do\n    (1..3).each {|i| tree[i] = i.to_s}\n  end\n\n  describe \"#red?\" do\n    it \"should return true if the node is red\" do\n      subject.color = :red\n\n      expect(subject).to be_red\n    end\n\n    it \"should return false if the node is black\" do\n      subject.color = :black\n\n      expect(subject).not_to be_red\n    end\n  end\n\n  describe \"#colorflip\" do\n    it \"should switch the color of the node and its children\" do\n      expect(subject.color).to eq(:black)\n      expect(subject.left.color).to eq(:black)\n      expect(subject.right.color).to eq(:black)\n\n      subject.colorflip\n\n      expect(subject.color).to eq(:red)\n      expect(subject.left.color).to eq(:red)\n      expect(subject.right.color).to eq(:red)\n    end\n  end\n\n  describe \"#rotate_left\" do\n    it \"should rotate the tree once to the left\" do\n      (4..7).each {|i| tree[i] = i.to_s}\n\n      root = tree.instance_variable_get(:@root)\n\n      root.rotate_left\n\n      expect(tree.to_hash).to eq({\n        :node => {\n          :key => 6,\n          :value => '6',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 4,\n            :value => '4',\n            :color => :red,\n          },\n          :left => {\n            :node => {\n              :key => 2,\n              :value => '2',\n              :color => :black,\n            },\n            :left => {\n              :node => {\n                :key => 1,\n                :value => '1',\n                :color => :black,\n              },\n            },\n            :right => {\n              :node => {\n                :key => 3,\n                :value => '3',\n                :color => :black,\n              },\n            },\n          },\n          :right => {\n            :node => {\n              :key => 5,\n              :value => '5',\n              :color => :black,\n            },\n          },\n        },\n        :right => {\n          :node => {\n            :key => 7,\n            :value => '7',\n            :color => :black,\n          },\n        },\n      })\n    end\n  end\n\n  describe \"#rotate_right\" do\n    it \"should rotate the tree once to the right\" do\n      (4..7).each {|i| tree[i] = i.to_s}\n\n      root = tree.instance_variable_get(:@root)\n\n      root.rotate_right\n\n      expect(tree.to_hash).to eq({\n        :node => {\n          :key => 2,\n          :value => '2',\n          :color => :black,\n        },\n        :left => {\n          :node => {\n            :key => 1,\n            :value => '1',\n            :color => :black,\n          },\n        },\n        :right => {\n          :node => {\n            :key => 4,\n            :value => '4',\n            :color => :red,\n          },\n          :left => {\n            :node => {\n              :key => 3,\n              :value => '3',\n              :color => :black,\n            },\n          },\n          :right => {\n            :node => {\n              :key => 6,\n              :value => '6',\n              :color => :black,\n            },\n            :left => {\n              :node => {\n                :key => 5,\n                :value => '5',\n                :color => :black,\n              },\n            },\n            :right => {\n              :node => {\n                :key => 7,\n                :value => '7',\n                :color => :black,\n              },\n            },\n          },\n        },\n      })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/graph/relationship_graph_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/graph'\n\nrequire 'puppet_spec/compiler'\nrequire 'matchers/include_in_order'\nrequire 'matchers/relationship_graph_matchers'\n\ndescribe Puppet::Graph::RelationshipGraph do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n  include RelationshipGraphMatchers\n\n  let(:graph) { Puppet::Graph::RelationshipGraph.new(Puppet::Graph::SequentialPrioritizer.new) }\n\n  it \"allows adding a new vertex with a specific priority\" do\n    vertex = stub_vertex('something')\n\n    graph.add_vertex(vertex, 2)\n\n    expect(graph.resource_priority(vertex)).to eq(2)\n  end\n\n  it \"returns resource priority based on the order added\" do\n    # strings chosen so the old hex digest method would put these in the\n    # wrong order\n    first = stub_vertex('aa')\n    second = stub_vertex('b')\n\n    graph.add_vertex(first)\n    graph.add_vertex(second)\n\n    expect(graph.resource_priority(first)).to be < graph.resource_priority(second)\n  end\n\n  it \"retains the first priority when a resource is added more than once\" do\n    first = stub_vertex(1)\n    second = stub_vertex(2)\n\n    graph.add_vertex(first)\n    graph.add_vertex(second)\n    graph.add_vertex(first)\n\n    expect(graph.resource_priority(first)).to be < graph.resource_priority(second)\n  end\n\n  it \"forgets the priority of a removed resource\" do\n    vertex = stub_vertex(1)\n\n    graph.add_vertex(vertex)\n    graph.remove_vertex!(vertex)\n\n    expect(graph.resource_priority(vertex)).to be_nil\n  end\n\n  it \"does not give two resources the same priority\" do\n    first = stub_vertex(1)\n    second = stub_vertex(2)\n    third = stub_vertex(3)\n\n    graph.add_vertex(first)\n    graph.add_vertex(second)\n    graph.remove_vertex!(first)\n    graph.add_vertex(third)\n\n    expect(graph.resource_priority(second)).to be < graph.resource_priority(third)\n  end\n\n  context \"order of traversal\" do\n    it \"traverses independent resources in the order they are added\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n        notify { \"first\": }\n        notify { \"second\": }\n        notify { \"third\": }\n        notify { \"fourth\": }\n        notify { \"fifth\": }\n      MANIFEST\n\n      expect(order_resources_traversed_in(relationships)).to(\n        include_in_order(\"Notify[first]\",\n                         \"Notify[second]\",\n                         \"Notify[third]\",\n                         \"Notify[fourth]\",\n                         \"Notify[fifth]\"))\n    end\n\n    it \"traverses resources generated during catalog creation in the order inserted\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n        create_resources(notify, { \"first\" => {} })\n        create_resources(notify, { \"second\" => {} })\n        notify{ \"third\": }\n        create_resources(notify, { \"fourth\" => {} })\n        create_resources(notify, { \"fifth\" => {} })\n      MANIFEST\n\n      expect(order_resources_traversed_in(relationships)).to(\n        include_in_order(\"Notify[first]\",\n                         \"Notify[second]\",\n                         \"Notify[third]\",\n                         \"Notify[fourth]\",\n                         \"Notify[fifth]\"))\n    end\n\n    it \"traverses all independent resources before traversing dependent ones\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n        notify { \"first\": require => Notify[third] }\n        notify { \"second\": }\n        notify { \"third\": }\n      MANIFEST\n\n      expect(order_resources_traversed_in(relationships)).to(\n        include_in_order(\"Notify[second]\", \"Notify[third]\", \"Notify[first]\"))\n    end\n\n    it \"traverses all independent resources before traversing dependent ones (with a backwards require)\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n        notify { \"first\": }\n        notify { \"second\": }\n        notify { \"third\": require => Notify[second] }\n        notify { \"fourth\": }\n      MANIFEST\n\n      expect(order_resources_traversed_in(relationships)).to(\n        include_in_order(\"Notify[first]\", \"Notify[second]\", \"Notify[third]\", \"Notify[fourth]\"))\n    end\n\n    it \"traverses resources in classes in the order they are added\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n        class c1 {\n            notify { \"a\": }\n            notify { \"b\": }\n        }\n        class c2 {\n            notify { \"c\": require => Notify[b] }\n        }\n        class c3 {\n            notify { \"d\": }\n        }\n        include c2\n        include c1\n        include c3\n      MANIFEST\n\n      expect(order_resources_traversed_in(relationships)).to(\n        include_in_order(\"Notify[a]\", \"Notify[b]\", \"Notify[c]\", \"Notify[d]\"))\n    end\n\n    it \"traverses resources in defines in the order they are added\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n        define d1() {\n          notify { \"a\": }\n          notify { \"b\": }\n        }\n        define d2() {\n          notify { \"c\": require => Notify[b]}\n        }\n        define d3() {\n            notify { \"d\": }\n        }\n        d2 { \"c\": }\n        d1 { \"d\": }\n        d3 { \"e\": }\n      MANIFEST\n\n      expect(order_resources_traversed_in(relationships)).to(\n        include_in_order(\"Notify[a]\", \"Notify[b]\", \"Notify[c]\", \"Notify[d]\"))\n    end\n  end\n\n  describe \"when interrupting traversal\" do\n    def collect_canceled_resources(relationships, trigger_on)\n      continue = true\n      continue_while = lambda { continue }\n\n      canceled_resources = []\n      canceled_resource_handler = lambda { |resource| canceled_resources << resource.ref }\n\n      relationships.traverse(:while => continue_while,\n                             :canceled_resource_handler => canceled_resource_handler) do |resource|\n        if resource.ref == trigger_on\n          continue = false\n        end\n      end\n\n      canceled_resources\n    end\n\n    it \"enumerates the remaining resources\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n      notify { \"a\": }\n      notify { \"b\": }\n      notify { \"c\": }\n    MANIFEST\n      resources = collect_canceled_resources(relationships, 'Notify[b]')\n\n      expect(resources).to include('Notify[c]')\n    end\n\n    it \"enumerates the remaining blocked resources\" do\n      relationships = compile_to_relationship_graph(<<-MANIFEST)\n      notify { \"a\": }\n      notify { \"b\": }\n      notify { \"c\": }\n      notify { \"d\": require => Notify[\"c\"] }\n    MANIFEST\n      resources = collect_canceled_resources(relationships, 'Notify[b]')\n\n      expect(resources).to include('Notify[d]')\n    end\n  end\n\n  describe \"when constructing dependencies\" do\n    let(:child) { make_absolute('/a/b') }\n    let(:parent) { make_absolute('/a') }\n\n    it \"does not create an automatic relationship that would interfere with a manual relationship\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        file { \"#{child}\": }\n\n        file { \"#{parent}\": require => File[\"#{child}\"] }\n      MANIFEST\n\n      expect(relationship_graph).to enforce_order_with_edge(\"File[#{child}]\", \"File[#{parent}]\")\n    end\n\n    it \"creates automatic relationships defined by the type\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        file { \"#{child}\": }\n\n        file { \"#{parent}\": }\n      MANIFEST\n\n      expect(relationship_graph).to enforce_order_with_edge(\"File[#{parent}]\", \"File[#{child}]\")\n    end\n  end\n\n  describe \"when reconstructing containment relationships\" do\n    def admissible_sentinel_of(ref)\n      \"Admissible_#{ref}\"\n    end\n\n    def completed_sentinel_of(ref)\n      \"Completed_#{ref}\"\n    end\n\n    it \"an empty container's completed sentinel should depend on its admissible sentinel\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a { }\n\n        include a\n      MANIFEST\n\n      expect(relationship_graph).to enforce_order_with_edge(\n        admissible_sentinel_of(\"class[A]\"),\n        completed_sentinel_of(\"class[A]\"))\n    end\n\n    it \"a container with children does not directly connect the completed sentinel to its admissible sentinel\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a { notify { \"a\": } }\n\n        include a\n      MANIFEST\n\n      expect(relationship_graph).not_to enforce_order_with_edge(\n        admissible_sentinel_of(\"class[A]\"),\n        completed_sentinel_of(\"class[A]\"))\n    end\n\n    it \"all contained objects should depend on their container's admissible sentinel\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n          notify { \"class a\": }\n        }\n\n        include a\n      MANIFEST\n\n      expect(relationship_graph).to enforce_order_with_edge(\n        admissible_sentinel_of(\"class[A]\"),\n        \"Notify[class a]\")\n    end\n\n    it \"completed sentinels should depend on their container's contents\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n          notify { \"class a\": }\n        }\n\n        include a\n      MANIFEST\n\n      expect(relationship_graph).to enforce_order_with_edge(\n          \"Notify[class a]\",\n          completed_sentinel_of(\"class[A]\"))\n    end\n\n    it \"admissible and completed sentinels should inherit the same tags\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n\t  tag \"test_tag\"\n        }\n\n        include a\n      MANIFEST\n\n      expect(vertex_called(relationship_graph, admissible_sentinel_of(\"class[A]\")).tagged?(\"test_tag\")).\n      to eq(true)\n      expect(vertex_called(relationship_graph, completed_sentinel_of(\"class[A]\")).tagged?(\"test_tag\")).\n      to eq(true)\n    end\n\n    it \"should remove all Component objects from the dependency graph\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n          notify { \"class a\": }\n        }\n        define b() {\n          notify { \"define b\": }\n        }\n\n        include a\n        b { \"testing\": }\n      MANIFEST\n\n      expect(relationship_graph.vertices.find_all { |v| v.is_a?(Puppet::Type.type(:component)) }).to be_empty\n    end\n\n    it \"should remove all Stage resources from the dependency graph\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        notify { \"class a\": }\n      MANIFEST\n\n      expect(relationship_graph.vertices.find_all { |v| v.is_a?(Puppet::Type.type(:stage)) }).to be_empty\n    end\n\n    it \"should retain labels on non-containment edges\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n          notify { \"class a\": }\n        }\n        define b() {\n          notify { \"define b\": }\n        }\n\n        include a\n        Class[a] ~> b { \"testing\": }\n      MANIFEST\n\n      expect(relationship_graph.edges_between(\n        vertex_called(relationship_graph, completed_sentinel_of(\"class[A]\")),\n        vertex_called(relationship_graph, admissible_sentinel_of(\"b[testing]\")))[0].label).\n        to eq({:callback => :refresh, :event => :ALL_EVENTS})\n    end\n\n    it \"should not add labels to edges that have none\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n          notify { \"class a\": }\n        }\n        define b() {\n          notify { \"define b\": }\n        }\n\n        include a\n        Class[a] -> b { \"testing\": }\n      MANIFEST\n\n      expect(relationship_graph.edges_between(\n        vertex_called(relationship_graph, completed_sentinel_of(\"class[A]\")),\n        vertex_called(relationship_graph, admissible_sentinel_of(\"b[testing]\")))[0].label).\n        to be_empty\n    end\n\n    it \"should copy notification labels to all created edges\" do\n      relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n        class a {\n          notify { \"class a\": }\n        }\n        define b() {\n          notify { \"define b\": }\n        }\n\n        include a\n        Class[a] ~> b { \"testing\": }\n      MANIFEST\n\n      expect(relationship_graph.edges_between(\n        vertex_called(relationship_graph, admissible_sentinel_of(\"b[testing]\")),\n        vertex_called(relationship_graph, \"Notify[define b]\"))[0].label).\n        to eq({:callback => :refresh, :event => :ALL_EVENTS})\n    end\n  end\n\n  def vertex_called(graph, name)\n    graph.vertices.find { |v| v.ref =~ /#{Regexp.escape(name)}/ }\n  end\n\n  def stub_vertex(name)\n    double(\"vertex #{name}\", :ref => name)\n  end\nend\n"
  },
  {
    "path": "spec/unit/graph/sequential_prioritizer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/graph'\n\ndescribe Puppet::Graph::SequentialPrioritizer do\n  let(:priorities) { Puppet::Graph::SequentialPrioritizer.new }\n\n  it \"generates priorities that maintain the sequence\" do\n    first = priorities.generate_priority_for(\"one\")\n    second = priorities.generate_priority_for(\"two\")\n    third = priorities.generate_priority_for(\"three\")\n\n    expect(first).to be < second\n    expect(second).to be < third\n  end\n\n  it \"prioritizes contained keys after the container\" do\n    parent = priorities.generate_priority_for(\"one\")\n    child = priorities.generate_priority_contained_in(\"one\", \"child 1\")\n    sibling = priorities.generate_priority_contained_in(\"one\", \"child 2\")\n    uncle = priorities.generate_priority_for(\"two\")\n\n    expect(parent).to be < child\n    expect(child).to be < sibling\n    expect(sibling).to be < uncle\n  end\n\n  it \"fails to prioritize a key contained in an unknown container\" do\n    expect do\n      priorities.generate_priority_contained_in(\"unknown\", \"child 1\")\n    end.to raise_error(NoMethodError, /`down' for nil/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/graph/simple_graph_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/graph'\n\ndescribe Puppet::Graph::SimpleGraph do\n  it \"should return the number of its vertices as its length\" do\n    @graph = Puppet::Graph::SimpleGraph.new\n    @graph.add_vertex(\"one\")\n    @graph.add_vertex(\"two\")\n    expect(@graph.size).to eq(2)\n  end\n\n  it \"should consider itself a directed graph\" do\n    expect(Puppet::Graph::SimpleGraph.new.directed?).to be_truthy\n  end\n\n  it \"should provide a method for reversing the graph\" do\n    @graph = Puppet::Graph::SimpleGraph.new\n    @graph.add_edge(:one, :two)\n    expect(@graph.reversal.edge?(:two, :one)).to be_truthy\n  end\n\n  it \"should be able to produce a dot graph\" do\n    @graph = Puppet::Graph::SimpleGraph.new\n    class FauxVertex\n      def ref\n        \"never mind\"\n      end\n    end\n    v1 = FauxVertex.new\n    v2 = FauxVertex.new\n    @graph.add_edge(v1, v2)\n\n    expect { @graph.to_dot_graph }.to_not raise_error\n  end\n\n  describe \"when managing vertices\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n    end\n\n    it \"should provide a method to add a vertex\" do\n      @graph.add_vertex(:test)\n      expect(@graph.vertex?(:test)).to be_truthy\n    end\n\n    it \"should reset its reversed graph when vertices are added\" do\n      rev = @graph.reversal\n      @graph.add_vertex(:test)\n      expect(@graph.reversal).not_to equal(rev)\n    end\n\n    it \"should ignore already-present vertices when asked to add a vertex\" do\n      @graph.add_vertex(:test)\n      expect { @graph.add_vertex(:test) }.to_not raise_error\n    end\n\n    it \"should return true when asked if a vertex is present\" do\n      @graph.add_vertex(:test)\n      expect(@graph.vertex?(:test)).to be_truthy\n    end\n\n    it \"should return false when asked if a non-vertex is present\" do\n      expect(@graph.vertex?(:test)).to be_falsey\n    end\n\n    it \"should return all set vertices when asked\" do\n      @graph.add_vertex(:one)\n      @graph.add_vertex(:two)\n      expect(@graph.vertices.length).to eq(2)\n      expect(@graph.vertices).to include(:one)\n      expect(@graph.vertices).to include(:two)\n    end\n\n    it \"should remove a given vertex when asked\" do\n      @graph.add_vertex(:one)\n      @graph.remove_vertex!(:one)\n      expect(@graph.vertex?(:one)).to be_falsey\n    end\n\n    it \"should do nothing when a non-vertex is asked to be removed\" do\n      expect { @graph.remove_vertex!(:one) }.to_not raise_error\n    end\n\n    describe \"when managing resources with quotes in their title\" do\n      subject do\n        v = Puppet::Type.type(:notify).new(:name => 'GRANT ALL ON DATABASE \"puppetdb\" TO \"puppetdb\"')\n        @graph.add_vertex(v)\n        @graph.to_dot\n      end\n\n      it \"should escape quotes in node name\" do\n        expect(subject).to match(/^[[:space:]]{4}\"Notify\\[GRANT ALL ON DATABASE \\\\\"puppetdb\\\\\" TO \\\\\"puppetdb\\\\\"\\]\" \\[$/)\n      end\n\n      it \"should escape quotes in node label\" do\n        expect(subject).to match(/^[[:space:]]{8}label = \"Notify\\[GRANT ALL ON DATABASE \\\\\"puppetdb\\\\\" TO \\\\\"puppetdb\\\\\"\\]\"$/)\n      end\n    end\n  end\n\n  describe \"when managing edges\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n    end\n\n    it \"should provide a method to test whether a given vertex pair is an edge\" do\n      expect(@graph).to respond_to(:edge?)\n    end\n\n    it \"should reset its reversed graph when edges are added\" do\n      rev = @graph.reversal\n      @graph.add_edge(:one, :two)\n      expect(@graph.reversal).not_to equal(rev)\n    end\n\n    it \"should provide a method to add an edge as an instance of the edge class\" do\n      edge = Puppet::Relationship.new(:one, :two)\n      @graph.add_edge(edge)\n      expect(@graph.edge?(:one, :two)).to be_truthy\n    end\n\n    it \"should provide a method to add an edge by specifying the two vertices\" do\n      @graph.add_edge(:one, :two)\n      expect(@graph.edge?(:one, :two)).to be_truthy\n    end\n\n    it \"should provide a method to add an edge by specifying the two vertices and a label\" do\n      @graph.add_edge(:one, :two, :callback => :awesome)\n      expect(@graph.edge?(:one, :two)).to be_truthy\n    end\n\n    describe \"when managing edges between nodes with quotes in their title\" do\n      let(:vertex) do\n        Hash.new do |hash, key|\n          hash[key] = Puppet::Type.type(:notify).new(:name => key.to_s)\n        end\n      end\n\n      subject do\n        @graph.add_edge(vertex['Postgresql_psql[CREATE DATABASE \"ejabberd\"]'], vertex['Postgresql_psql[REVOKE CONNECT ON DATABASE \"ejabberd\" FROM public]'])\n        @graph.to_dot\n      end\n\n      it \"should escape quotes in edge\" do\n        expect(subject).to match(/^[[:space:]]{4}\"Notify\\[Postgresql_psql\\[CREATE DATABASE \\\\\"ejabberd\\\\\"\\]\\]\" -> \"Notify\\[Postgresql_psql\\[REVOKE CONNECT ON DATABASE \\\\\"ejabberd\\\\\" FROM public\\]\\]\" \\[$/)\n      end\n    end\n\n    describe \"when retrieving edges between two nodes\" do\n      it \"should handle the case of nodes not in the graph\" do\n        expect(@graph.edges_between(:one, :two)).to eq([])\n      end\n\n      it \"should handle the case of nodes with no edges between them\" do\n        @graph.add_vertex(:one)\n        @graph.add_vertex(:two)\n        expect(@graph.edges_between(:one, :two)).to eq([])\n      end\n\n      it \"should handle the case of nodes connected by a single edge\" do\n        edge = Puppet::Relationship.new(:one, :two)\n        @graph.add_edge(edge)\n        expect(@graph.edges_between(:one, :two).length).to eq(1)\n        expect(@graph.edges_between(:one, :two)[0]).to equal(edge)\n      end\n\n      it \"should handle the case of nodes connected by multiple edges\" do\n        edge1 = Puppet::Relationship.new(:one, :two, :callback => :foo)\n        edge2 = Puppet::Relationship.new(:one, :two, :callback => :bar)\n        @graph.add_edge(edge1)\n        @graph.add_edge(edge2)\n        expect(Set.new(@graph.edges_between(:one, :two))).to eq(Set.new([edge1, edge2]))\n      end\n    end\n\n    it \"should add the edge source as a vertex if it is not already\" do\n      edge = Puppet::Relationship.new(:one, :two)\n      @graph.add_edge(edge)\n      expect(@graph.vertex?(:one)).to be_truthy\n    end\n\n    it \"should add the edge target as a vertex if it is not already\" do\n      edge = Puppet::Relationship.new(:one, :two)\n      @graph.add_edge(edge)\n      expect(@graph.vertex?(:two)).to be_truthy\n    end\n\n    it \"should return all edges as edge instances when asked\" do\n      one = Puppet::Relationship.new(:one, :two)\n      two = Puppet::Relationship.new(:two, :three)\n      @graph.add_edge(one)\n      @graph.add_edge(two)\n      edges = @graph.edges\n      expect(edges).to be_instance_of(Array)\n      expect(edges.length).to eq(2)\n      expect(edges).to include(one)\n      expect(edges).to include(two)\n    end\n\n    it \"should remove an edge when asked\" do\n      edge = Puppet::Relationship.new(:one, :two)\n      @graph.add_edge(edge)\n      @graph.remove_edge!(edge)\n      expect(@graph.edge?(edge.source, edge.target)).to be_falsey\n    end\n\n    it \"should remove all related edges when a vertex is removed\" do\n      one = Puppet::Relationship.new(:one, :two)\n      two = Puppet::Relationship.new(:two, :three)\n      @graph.add_edge(one)\n      @graph.add_edge(two)\n      @graph.remove_vertex!(:two)\n      expect(@graph.edge?(:one, :two)).to be_falsey\n      expect(@graph.edge?(:two, :three)).to be_falsey\n      expect(@graph.edges.length).to eq(0)\n    end\n  end\n\n  describe \"when finding adjacent vertices\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n      @one_two = Puppet::Relationship.new(:one, :two)\n      @two_three = Puppet::Relationship.new(:two, :three)\n      @one_three = Puppet::Relationship.new(:one, :three)\n      @graph.add_edge(@one_two)\n      @graph.add_edge(@one_three)\n      @graph.add_edge(@two_three)\n    end\n\n    it \"should return adjacent vertices\" do\n      adj = @graph.adjacent(:one)\n      expect(adj).to be_include(:three)\n      expect(adj).to be_include(:two)\n    end\n\n    it \"should default to finding :out vertices\" do\n      expect(@graph.adjacent(:two)).to eq([:three])\n    end\n\n    it \"should support selecting :in vertices\" do\n      expect(@graph.adjacent(:two, :direction => :in)).to eq([:one])\n    end\n\n    it \"should default to returning the matching vertices as an array of vertices\" do\n      expect(@graph.adjacent(:two)).to eq([:three])\n    end\n\n    it \"should support returning an array of matching edges\" do\n      expect(@graph.adjacent(:two, :type => :edges)).to eq([@two_three])\n    end\n\n    # Bug #2111\n    it \"should not consider a vertex adjacent just because it was asked about previously\" do\n      @graph = Puppet::Graph::SimpleGraph.new\n      @graph.add_vertex(\"a\")\n      @graph.add_vertex(\"b\")\n      @graph.edge?(\"a\", \"b\")\n      expect(@graph.adjacent(\"a\")).to eq([])\n    end\n  end\n\n  describe \"when clearing\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n      one = Puppet::Relationship.new(:one, :two)\n      two = Puppet::Relationship.new(:two, :three)\n      @graph.add_edge(one)\n      @graph.add_edge(two)\n\n      @graph.clear\n    end\n\n    it \"should remove all vertices\" do\n      expect(@graph.vertices).to be_empty\n    end\n\n    it \"should remove all edges\" do\n      expect(@graph.edges).to be_empty\n    end\n  end\n\n  describe \"when reversing graphs\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n    end\n\n    it \"should provide a method for reversing the graph\" do\n      @graph.add_edge(:one, :two)\n      expect(@graph.reversal.edge?(:two, :one)).to be_truthy\n    end\n\n    it \"should add all vertices to the reversed graph\" do\n      @graph.add_edge(:one, :two)\n      expect(@graph.vertex?(:one)).to be_truthy\n      expect(@graph.vertex?(:two)).to be_truthy\n    end\n\n    it \"should retain labels on edges\" do\n      @graph.add_edge(:one, :two, :callback => :awesome)\n      edge = @graph.reversal.edges_between(:two, :one)[0]\n      expect(edge.label).to eq({:callback => :awesome})\n    end\n  end\n\n  describe \"when reporting cycles in the graph\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n    end\n\n    # This works with `add_edges` to auto-vivify the resource instances.\n    let :vertex do\n      Hash.new do |hash, key|\n        hash[key] = Puppet::Type.type(:notify).new(:name => key.to_s)\n      end\n    end\n\n    def add_edges(hash)\n      hash.each do |a,b|\n        @graph.add_edge(vertex[a], vertex[b])\n      end\n    end\n\n    def simplify(cycles)\n      cycles.map do |cycle|\n        cycle.map do |resource|\n          resource.name\n        end\n      end\n    end\n\n    def expect_cycle_to_include(cycle, *resource_names)\n      resource_names.each_with_index do |resource, index|\n        expect(cycle[index].ref).to eq(\"Notify[#{resource}]\")\n      end\n    end\n\n    it \"should report one-vertex loops\" do\n      add_edges :a => :a\n      expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:\\n\\(Notify\\[a\\] => Notify\\[a\\]\\)/)\n      cycle = @graph.report_cycles_in_graph.first\n      expect_cycle_to_include(cycle, :a)\n    end\n\n    it \"should report two-vertex loops\" do\n      add_edges :a => :b, :b => :a\n      expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:\\n\\(Notify\\[a\\] => Notify\\[b\\] => Notify\\[a\\]\\)/)\n      cycle = @graph.report_cycles_in_graph.first\n      expect_cycle_to_include(cycle, :a, :b)\n    end\n\n    it \"should report multi-vertex loops\" do\n      add_edges :a => :b, :b => :c, :c => :a\n      expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:\\n\\(Notify\\[a\\] => Notify\\[b\\] => Notify\\[c\\] => Notify\\[a\\]\\)/)\n      cycle = @graph.report_cycles_in_graph.first\n      expect_cycle_to_include(cycle, :a, :b, :c)\n    end\n\n    it \"should report when a larger tree contains a small cycle\" do\n      add_edges :a => :b, :b => :a, :c => :a, :d => :c\n      expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:\\n\\(Notify\\[a\\] => Notify\\[b\\] => Notify\\[a\\]\\)/)\n      cycle = @graph.report_cycles_in_graph.first\n      expect_cycle_to_include(cycle, :a, :b)\n    end\n\n    it \"should succeed on trees with no cycles\" do\n      add_edges :a => :b, :b => :e, :c => :a, :d => :c\n      expect(Puppet).not_to receive(:err)\n      expect(@graph.report_cycles_in_graph).to be_nil\n    end\n\n    it \"cycle discovery should be the minimum cycle for a simple graph\" do\n      add_edges \"a\" => \"b\"\n      add_edges \"b\" => \"a\"\n      add_edges \"b\" => \"c\"\n\n      expect(simplify(@graph.find_cycles_in_graph)).to eq([[\"a\", \"b\"]])\n    end\n\n    it \"cycle discovery handles a self-loop cycle\" do\n      add_edges :a => :a\n\n      expect(simplify(@graph.find_cycles_in_graph)).to eq([[\"a\"]])\n    end\n\n    it \"cycle discovery should handle two distinct cycles\" do\n      add_edges \"a\" => \"a1\", \"a1\" => \"a\"\n      add_edges \"b\" => \"b1\", \"b1\" => \"b\"\n\n      expect(simplify(@graph.find_cycles_in_graph)).to eq([[\"a1\", \"a\"], [\"b1\", \"b\"]])\n    end\n\n    it \"cycle discovery should handle two cycles in a connected graph\" do\n      add_edges \"a\" => \"b\", \"b\" => \"c\", \"c\" => \"d\"\n      add_edges \"a\" => \"a1\", \"a1\" => \"a\"\n      add_edges \"c\" => \"c1\", \"c1\" => \"c2\", \"c2\" => \"c3\", \"c3\" => \"c\"\n\n      expect(simplify(@graph.find_cycles_in_graph)).to eq([%w{a1 a}, %w{c1 c2 c3 c}])\n    end\n\n    it \"cycle discovery should handle a complicated cycle\" do\n      add_edges \"a\" => \"b\", \"b\" => \"c\"\n      add_edges \"a\" => \"c\"\n      add_edges \"c\" => \"c1\", \"c1\" => \"a\"\n      add_edges \"c\" => \"c2\", \"c2\" => \"b\"\n\n      expect(simplify(@graph.find_cycles_in_graph)).to eq([%w{a b c1 c2 c}])\n    end\n\n    it \"cycle discovery should not fail with large data sets\" do\n      limit = 3000\n      (1..(limit - 1)).each do |n| add_edges n.to_s => (n+1).to_s end\n\n      expect(simplify(@graph.find_cycles_in_graph)).to eq([])\n    end\n\n    it \"path finding should work with a simple cycle\" do\n      add_edges \"a\" => \"b\", \"b\" => \"c\", \"c\" => \"a\"\n\n      cycles = @graph.find_cycles_in_graph\n      paths = @graph.paths_in_cycle(cycles.first, 100)\n      expect(simplify(paths)).to eq([%w{a b c a}])\n    end\n\n    it \"path finding should work with two independent cycles\" do\n      add_edges \"a\" => \"b1\"\n      add_edges \"a\" => \"b2\"\n      add_edges \"b1\" => \"a\", \"b2\" => \"a\"\n\n      cycles = @graph.find_cycles_in_graph\n      expect(cycles.length).to eq(1)\n\n      paths = @graph.paths_in_cycle(cycles.first, 100)\n      expect(simplify(paths)).to eq([%w{a b1 a}, %w{a b2 a}])\n    end\n\n    it \"path finding should prefer shorter paths in cycles\" do\n      add_edges \"a\" => \"b\", \"b\" => \"c\", \"c\" => \"a\"\n      add_edges \"b\" => \"a\"\n\n      cycles = @graph.find_cycles_in_graph\n      expect(cycles.length).to eq(1)\n\n      paths = @graph.paths_in_cycle(cycles.first, 100)\n      expect(simplify(paths)).to eq([%w{a b a}, %w{a b c a}])\n    end\n\n    it \"path finding should respect the max_path value\" do\n      (1..20).each do |n| add_edges \"a\" => \"b#{n}\", \"b#{n}\" => \"a\" end\n\n      cycles = @graph.find_cycles_in_graph\n      expect(cycles.length).to eq(1)\n\n      (1..20).each do |n|\n        paths = @graph.paths_in_cycle(cycles.first, n)\n        expect(paths.length).to eq(n)\n      end\n\n      paths = @graph.paths_in_cycle(cycles.first, 21)\n      expect(paths.length).to eq(20)\n    end\n  end\n\n  describe \"when writing dot files\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n      @name = :test\n      @file = File.join(Puppet[:graphdir], @name.to_s + \".dot\")\n    end\n\n    it \"should only write when graphing is enabled\" do\n      expect(File).not_to receive(:open).with(@file)\n      Puppet[:graph] = false\n      @graph.write_graph(@name)\n    end\n\n    it \"should write a dot file based on the passed name\" do\n      expect(File).to receive(:open).with(@file, \"w:UTF-8\").and_yield(double(\"file\", :puts => nil))\n      expect(@graph).to receive(:to_dot).with({\"name\" => @name.to_s.capitalize})\n      Puppet[:graph] = true\n      @graph.write_graph(@name)\n    end\n  end\n\n  describe Puppet::Graph::SimpleGraph do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n    end\n\n    it \"should correctly clear vertices and edges when asked\" do\n      @graph.add_edge(\"a\", \"b\")\n      @graph.add_vertex \"c\"\n      @graph.clear\n      expect(@graph.vertices).to be_empty\n      expect(@graph.edges).to be_empty\n    end\n  end\n\n  describe \"when matching edges\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n\n      # Resource is a String here although not for realz. Stub [] to always return nil\n      # because indexing a String with a non-Integer throws an exception (and none of\n      # these tests need anything meaningful from []).\n      resource = \"a\"\n      allow(resource).to receive(:[])\n      @event = Puppet::Transaction::Event.new(:name => :yay, :resource => resource)\n      @none = Puppet::Transaction::Event.new(:name => :NONE, :resource => resource)\n\n      @edges = {}\n      @edges[\"a/b\"] = Puppet::Relationship.new(\"a\", \"b\", {:event => :yay, :callback => :refresh})\n      @edges[\"a/c\"] = Puppet::Relationship.new(\"a\", \"c\", {:event => :yay, :callback => :refresh})\n      @graph.add_edge(@edges[\"a/b\"])\n    end\n\n    it \"should match edges whose source matches the source of the event\" do\n      expect(@graph.matching_edges(@event)).to eq([@edges[\"a/b\"]])\n    end\n\n    it \"should match always match nothing when the event is :NONE\" do\n      expect(@graph.matching_edges(@none)).to be_empty\n    end\n\n    it \"should match multiple edges\" do\n      @graph.add_edge(@edges[\"a/c\"])\n      edges = @graph.matching_edges(@event)\n      expect(edges).to be_include(@edges[\"a/b\"])\n      expect(edges).to be_include(@edges[\"a/c\"])\n    end\n  end\n\n  describe \"when determining dependencies\" do\n    before do\n      @graph = Puppet::Graph::SimpleGraph.new\n\n      @graph.add_edge(\"a\", \"b\")\n      @graph.add_edge(\"a\", \"c\")\n      @graph.add_edge(\"b\", \"d\")\n    end\n\n    it \"should find all dependents when they are on multiple levels\" do\n      expect(@graph.dependents(\"a\").sort).to eq(%w{b c d}.sort)\n    end\n\n    it \"should find single dependents\" do\n      expect(@graph.dependents(\"b\").sort).to eq(%w{d}.sort)\n    end\n\n    it \"should return an empty array when there are no dependents\" do\n      expect(@graph.dependents(\"c\").sort).to eq([].sort)\n    end\n\n    it \"should find all dependencies when they are on multiple levels\" do\n      expect(@graph.dependencies(\"d\").sort).to eq(%w{a b})\n    end\n\n    it \"should find single dependencies\" do\n      expect(@graph.dependencies(\"c\").sort).to eq(%w{a})\n    end\n\n    it \"should return an empty array when there are no dependencies\" do\n      expect(@graph.dependencies(\"a\").sort).to eq([])\n    end\n  end\n\n  it \"should serialize to YAML using the old format by default\" do\n    expect(Puppet::Graph::SimpleGraph.use_new_yaml_format).to eq(false)\n  end\n\n  describe \"(yaml tests)\" do\n    def empty_graph(graph)\n    end\n\n    def one_vertex_graph(graph)\n      graph.add_vertex('a')\n    end\n\n    def graph_without_edges(graph)\n      ['a', 'b', 'c'].each { |x| graph.add_vertex(x) }\n    end\n\n    def one_edge_graph(graph)\n      graph.add_edge('a', 'b')\n    end\n\n    def many_edge_graph(graph)\n      graph.add_edge('a', 'b')\n      graph.add_edge('a', 'c')\n      graph.add_edge('b', 'd')\n      graph.add_edge('c', 'd')\n    end\n\n    def labeled_edge_graph(graph)\n      graph.add_edge('a', 'b', :callback => :foo, :event => :bar)\n    end\n\n    def overlapping_edge_graph(graph)\n      graph.add_edge('a', 'b', :callback => :foo, :event => :bar)\n      graph.add_edge('a', 'b', :callback => :biz, :event => :baz)\n    end\n\n    def self.all_test_graphs\n      [:empty_graph, :one_vertex_graph, :graph_without_edges, :one_edge_graph, :many_edge_graph, :labeled_edge_graph,\n       :overlapping_edge_graph]\n    end\n\n    def object_ids(enumerable)\n      # Return a sorted list of the object id's of the elements of an\n      # enumerable.\n      enumerable.collect { |x| x.object_id }.sort\n    end\n\n    def graph_to_yaml(graph, which_format)\n      previous_use_new_yaml_format = Puppet::Graph::SimpleGraph.use_new_yaml_format\n      Puppet::Graph::SimpleGraph.use_new_yaml_format = (which_format == :new)\n      if block_given?\n        yield\n      else\n        YAML.dump(graph)\n      end\n    ensure\n      Puppet::Graph::SimpleGraph.use_new_yaml_format = previous_use_new_yaml_format\n    end\n\n    # Test serialization of graph to YAML.\n    [:old, :new].each do |which_format|\n      all_test_graphs.each do |graph_to_test|\n        it \"should be able to serialize #{graph_to_test} to YAML (#{which_format} format)\" do\n          graph = Puppet::Graph::SimpleGraph.new\n          send(graph_to_test, graph)\n          yaml_form = graph_to_yaml(graph, which_format)\n\n          # Hack the YAML so that objects in the Puppet namespace get\n          # changed to YAML::DomainType objects.  This lets us inspect\n          # the serialized objects easily without invoking any\n          # yaml_initialize hooks.\n          yaml_form.gsub!('!ruby/object:Puppet::', '!hack/object:Puppet::')\n          serialized_object = Puppet::Util::Yaml.safe_load(yaml_form, [Symbol,Puppet::Graph::SimpleGraph])\n\n          # Check that the object contains instance variables @edges and\n          # @vertices only.  @reversal is also permitted, but we don't\n          # check it, because it is going to be phased out.\n          expect(serialized_object.keys.reject { |x| x == 'reversal' }.sort).to eq(['edges', 'vertices'])\n\n          # Check edges by forming a set of tuples (source, target,\n          # callback, event) based on the graph and the YAML and make sure\n          # they match.\n          edges = serialized_object['edges']\n          expect(edges).to be_a(Array)\n          expected_edge_tuples = graph.edges.collect { |edge| [edge.source, edge.target, edge.callback, edge.event] }\n          actual_edge_tuples = edges.collect do |edge|\n            %w{source target}.each { |x| expect(edge.keys).to include(x) }\n            edge.keys.each { |x| expect(['source', 'target', 'callback', 'event']).to include(x) }\n            %w{source target callback event}.collect { |x| edge[x] }\n          end\n          expect(Set.new(actual_edge_tuples)).to eq(Set.new(expected_edge_tuples.map { |tuple| tuple.map {|e| e.nil? ? nil : e.to_s }}))\n          expect(actual_edge_tuples.length).to eq(expected_edge_tuples.length)\n\n          # Check vertices one by one.\n          vertices = serialized_object['vertices']\n          if which_format == :old\n            expect(vertices).to be_a(Hash)\n            expect(Set.new(vertices.keys)).to eq(Set.new(graph.vertices))\n            vertices.each do |key, value|\n              expect(value.keys.sort).to eq(%w{adjacencies vertex})\n              expect(value['vertex']).to eq(key)\n              adjacencies = value['adjacencies']\n              expect(adjacencies).to be_a(Hash)\n              expect(Set.new(adjacencies.keys)).to eq(Set.new(['in', 'out']))\n              [:in, :out].each do |direction|\n                direction_hash = adjacencies[direction.to_s]\n                expect(direction_hash).to be_a(Hash)\n                expected_adjacent_vertices = Set.new(graph.adjacent(key, :direction => direction, :type => :vertices))\n                expect(Set.new(direction_hash.keys)).to eq(expected_adjacent_vertices)\n                direction_hash.each do |adj_key, adj_value|\n                  # Since we already checked edges, just check consistency\n                  # with edges.\n                  desired_source = direction == :in ? adj_key : key\n                  desired_target = direction == :in ? key : adj_key\n                  expected_edges = edges.select do |edge|\n                    edge['source'] == desired_source && edge['target'] == desired_target\n                  end\n                  expect(adj_value).to be_a(Array)\n                  if adj_value != expected_edges\n                    raise \"For vertex #{key.inspect}, direction #{direction.inspect}: expected adjacencies #{expected_edges.inspect} but got #{adj_value.inspect}\"\n                  end\n                end\n              end\n            end\n          else\n            expect(vertices).to be_a(Array)\n            expect(Set.new(vertices)).to eq(Set.new(graph.vertices))\n            expect(vertices.length).to eq(graph.vertices.length)\n          end\n        end\n      end\n\n      # Test deserialization of graph from YAML.  This presumes the\n      # correctness of serialization to YAML, which has already been\n      # tested.\n      all_test_graphs.each do |graph_to_test|\n        it \"should be able to deserialize #{graph_to_test} from YAML (#{which_format} format)\" do\n          reference_graph = Puppet::Graph::SimpleGraph.new\n          send(graph_to_test, reference_graph)\n          yaml_form = graph_to_yaml(reference_graph, which_format)\n          recovered_graph = Puppet::Util::Yaml.safe_load(yaml_form, [Symbol,Puppet::Graph::SimpleGraph])\n\n          # Test that the recovered vertices match the vertices in the\n          # reference graph.\n          expected_vertices = reference_graph.vertices.to_a\n          recovered_vertices = recovered_graph.vertices.to_a\n          expect(Set.new(recovered_vertices)).to eq(Set.new(expected_vertices))\n          expect(recovered_vertices.length).to eq(expected_vertices.length)\n\n          # Test that the recovered edges match the edges in the\n          # reference graph.\n          expected_edge_tuples = reference_graph.edges.collect do |edge|\n            [edge.source, edge.target, edge.callback, edge.event]\n          end\n          recovered_edge_tuples = recovered_graph.edges.collect do |edge|\n            [edge.source, edge.target, edge.callback, edge.event]\n          end\n          expect(Set.new(recovered_edge_tuples)).to eq(Set.new(expected_edge_tuples))\n          expect(recovered_edge_tuples.length).to eq(expected_edge_tuples.length)\n\n          # We ought to test that the recovered graph is self-consistent\n          # too.  But we're not going to bother with that yet because\n          # the internal representation of the graph is about to change.\n        end\n      end\n    end\n\n    it \"should serialize properly when used as a base class\" do\n      class Puppet::TestDerivedClass < Puppet::Graph::SimpleGraph\n        attr_accessor :foo\n\n        def initialize_from_hash(hash)\n          super(hash)\n          @foo = hash['foo']\n        end\n\n        def to_data_hash\n          super.merge('foo' => @foo)\n        end\n      end\n      derived = Puppet::TestDerivedClass.new\n      derived.add_edge('a', 'b')\n      derived.foo = 1234\n      yaml = YAML.dump(derived)\n      recovered_derived = Puppet::Util::Yaml.safe_load(yaml, [Puppet::TestDerivedClass])\n      expect(recovered_derived.class).to equal(Puppet::TestDerivedClass)\n      expect(recovered_derived.edges.length).to eq(1)\n      expect(recovered_derived.edges[0].source).to eq('a')\n      expect(recovered_derived.edges[0].target).to eq('b')\n      expect(recovered_derived.vertices.length).to eq(2)\n      expect(recovered_derived.foo).to eq(1234)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hiera/scope_spec.rb",
    "content": "require 'spec_helper'\nrequire 'hiera/scope'\n\nrequire 'puppet_spec/scope'\n\ndescribe Hiera::Scope do\n  include PuppetSpec::Scope\n\n  let(:real) { create_test_scope_for_node(\"test_node\") }\n  let(:scope) { Hiera::Scope.new(real) }\n\n  describe \"#initialize\" do\n    it \"should store the supplied puppet scope\" do\n      expect(scope.real).to eq(real)\n    end\n  end\n\n  describe \"#[]\" do\n    it \"should return nil when no value is found and strict mode is off\" do\n      Puppet[:strict] = :warning\n      expect(scope[\"foo\"]).to eq(nil)\n    end\n\n    it \"should raise error by default when no value is found\" do\n      expect { scope[\"foo\"] }.to raise_error(/Undefined variable 'foo'/)\n    end\n\n    it \"should treat '' as nil\" do\n      real[\"foo\"] = \"\"\n\n      expect(scope[\"foo\"]).to eq(nil)\n    end\n\n    it \"should return found data\" do\n      real[\"foo\"] = \"bar\"\n\n      expect(scope[\"foo\"]).to eq(\"bar\")\n    end\n\n    it \"preserves the case of a string that is found\" do\n      real[\"foo\"] = \"CAPITAL!\"\n\n      expect(scope[\"foo\"]).to eq(\"CAPITAL!\")\n    end\n\n    it \"aliases $module_name as calling_module\" do\n      real[\"module_name\"] = \"the_module\"\n\n      expect(scope[\"calling_module\"]).to eq(\"the_module\")\n    end\n\n    it \"uses the name of the of the scope's class as the calling_class\" do\n      real.source = Puppet::Resource::Type.new(:hostclass,\n                                               \"testing\",\n                                               :module_name => \"the_module\")\n\n      expect(scope[\"calling_class\"]).to eq(\"testing\")\n    end\n\n    it \"downcases the calling_class\" do\n      real.source = Puppet::Resource::Type.new(:hostclass,\n                                               \"UPPER CASE\",\n                                               :module_name => \"the_module\")\n\n      expect(scope[\"calling_class\"]).to eq(\"upper case\")\n    end\n\n    it \"looks for the class which includes the defined type as the calling_class\" do\n      parent = create_test_scope_for_node(\"parent\")\n      real.parent = parent\n      parent.source = Puppet::Resource::Type.new(:hostclass,\n                                                 \"name_of_the_class_including_the_definition\",\n                                                 :module_name => \"class_module\")\n      real.source = Puppet::Resource::Type.new(:definition,\n                                               \"definition_name\",\n                                               :module_name => \"definition_module\")\n\n      expect(scope[\"calling_class\"]).to eq(\"name_of_the_class_including_the_definition\")\n    end\n  end\n\n  describe \"#exist?\" do\n    it \"should correctly report missing data\" do\n      real[\"nil_value\"] = nil\n      real[\"blank_value\"] = \"\"\n\n      expect(scope.exist?(\"nil_value\")).to eq(true)\n      expect(scope.exist?(\"blank_value\")).to eq(true)\n      expect(scope.exist?(\"missing_value\")).to eq(false)\n    end\n\n    it \"should always return true for calling_class and calling_module\" do\n      expect(scope.include?(\"calling_class\")).to eq(true)\n      expect(scope.include?(\"calling_class_path\")).to eq(true)\n      expect(scope.include?(\"calling_module\")).to eq(true)\n    end\n  end\n\n  describe \"#call_function\" do\n    it \"should delegate a call to call_function to the real scope\" do\n      expect(real).to receive(:call_function).once\n      scope.call_function('some_function', [1,2,3])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hiera_puppet_spec.rb",
    "content": "require 'spec_helper'\nrequire 'hiera_puppet'\nrequire 'puppet_spec/scope'\n\ndescribe 'HieraPuppet', :if => Puppet.features.hiera? do\n  include PuppetSpec::Scope\n\n  after(:all) do\n    HieraPuppet.instance_variable_set(:@hiera, nil)\n  end\n\n  describe 'HieraPuppet#hiera_config' do\n    let(:hiera_config_data) do\n      { :backend => 'yaml' }\n    end\n\n    context \"when the hiera_config_file exists\" do\n      before do\n        expect(Hiera::Config).to receive(:load).and_return(hiera_config_data)\n        expect(HieraPuppet).to receive(:hiera_config_file).and_return(true)\n      end\n\n      it \"should return a configuration hash\" do\n        expected_results = {\n          :backend => 'yaml',\n          :logger  => 'puppet'\n        }\n        expect(HieraPuppet.send(:hiera_config)).to eq(expected_results)\n      end\n    end\n\n    context \"when the hiera_config_file does not exist\" do\n      before do\n        expect(Hiera::Config).not_to receive(:load)\n        expect(HieraPuppet).to receive(:hiera_config_file).and_return(nil)\n      end\n\n      it \"should return a configuration hash\" do\n        expect(HieraPuppet.send(:hiera_config)).to eq({ :logger => 'puppet' })\n      end\n    end\n  end\n\n  describe 'HieraPuppet#hiera_config_file' do\n    it \"should return nil when we cannot derive the hiera config file from Puppet.settings\" do\n      begin\n        Puppet.settings[:hiera_config] = nil\n      rescue ArgumentError => detail\n        raise unless detail.message =~ /unknown setting/\n      end\n      expect(HieraPuppet.send(:hiera_config_file)).to be_nil\n    end\n\n    it \"should use Puppet.settings[:hiera_config] as the hiera config file\" do\n      begin\n        Puppet.settings[:hiera_config] = \"/dev/null/my_hiera.yaml\"\n      rescue ArgumentError => detail\n        raise unless detail.message =~ /unknown setting/\n        pending(\"This example does not apply to Puppet #{Puppet.version} because it does not have this setting\")\n      end\n\n      allow(Puppet::FileSystem).to receive(:exist?).with(Puppet[:hiera_config]).and_return(true)\n      expect(HieraPuppet.send(:hiera_config_file)).to eq(Puppet[:hiera_config])\n    end\n\n    context 'when hiera_config is not set' do\n      let(:code_hiera_config) { File.join(Puppet[:codedir], 'hiera.yaml') }\n      let(:conf_hiera_config) { File.join(Puppet[:confdir], 'hiera.yaml') }\n\n      before(:each) do\n        Puppet.settings.setting(:hiera_config).send(:remove_instance_variable, :@evaluated_default)\n        Puppet.settings[:hiera_config] = nil\n        Puppet.settings[:codedir] = '/dev/null/puppetlabs/code'\n        Puppet.settings[:confdir] = '/dev/null/puppetlabs/puppet'\n      end\n\n      it \"should use Puppet.settings[:codedir]/hiera.yaml when '$codedir/hiera.yaml' exists and '$confdir/hiera.yaml' does not exist\" do\n        allow(Puppet::FileSystem).to receive(:exist?).with(code_hiera_config).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(conf_hiera_config).and_return(false)\n\n        expect(HieraPuppet.send(:hiera_config_file)).to eq(code_hiera_config)\n      end\n\n      it \"should use Puppet.settings[:confdir]/hiera.yaml when '$codedir/hiera.yaml' does not exist and '$confdir/hiera.yaml' exists\" do\n        allow(Puppet::FileSystem).to receive(:exist?).with(code_hiera_config).and_return(false)\n        allow(Puppet::FileSystem).to receive(:exist?).with(conf_hiera_config).and_return(true)\n\n        expect(HieraPuppet.send(:hiera_config_file)).to eq(conf_hiera_config)\n      end\n\n      it \"should use Puppet.settings[:codedir]/hiera.yaml when '$codedir/hiera.yaml' exists and '$confdir/hiera.yaml' exists\" do\n        allow(Puppet::FileSystem).to receive(:exist?).with(code_hiera_config).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(conf_hiera_config).and_return(true)\n\n        expect(HieraPuppet.send(:hiera_config_file)).to eq(code_hiera_config)\n      end\n\n      it \"should return nil when neither '$codedir/hiera.yaml' nor '$confdir/hiera.yaml' exists\" do\n        allow(Puppet::FileSystem).to receive(:exist?).with(code_hiera_config).and_return(false)\n        allow(Puppet::FileSystem).to receive(:exist?).with(conf_hiera_config).and_return(false)\n\n        expect(HieraPuppet.send(:hiera_config_file)).to eq(nil)\n      end\n\n      it \"should return explicitly set option even if both '$codedir/hiera.yaml' and '$confdir/hiera.yaml' exists\" do\n        if Puppet::Util::Platform.windows?\n          explicit_hiera_config = 'C:/an/explicit/hiera.yaml'\n        else\n          explicit_hiera_config = '/an/explicit/hiera.yaml'\n        end\n        Puppet.settings[:hiera_config] = explicit_hiera_config\n        allow(Puppet::FileSystem).to receive(:exist?).with(explicit_hiera_config).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(code_hiera_config).and_return(true)\n        allow(Puppet::FileSystem).to receive(:exist?).with(conf_hiera_config).and_return(true)\n\n        expect(HieraPuppet.send(:hiera_config_file)).to eq(explicit_hiera_config)\n      end\n    end\n  end\n\n  describe 'HieraPuppet#lookup' do\n    let :scope do create_test_scope_for_node('foo') end\n\n    before :each do\n      Puppet[:hiera_config] = PuppetSpec::Files.tmpfile('hiera_config')\n    end\n\n    it \"should return the value from Hiera\" do\n      allow_any_instance_of(Hiera).to receive(:lookup).and_return('8080')\n      expect(HieraPuppet.lookup('port', nil, scope, nil, :priority)).to eq('8080')\n\n      allow_any_instance_of(Hiera).to receive(:lookup).and_return(['foo', 'bar'])\n      expect(HieraPuppet.lookup('ntpservers', nil, scope, nil, :array)).to eq(['foo', 'bar'])\n\n      allow_any_instance_of(Hiera).to receive(:lookup).and_return({'uid' => '1000'})\n      expect(HieraPuppet.lookup('user', nil, scope, nil, :hash)).to eq({'uid' => '1000'})\n    end\n\n    it \"should raise a useful error when the answer is nil\" do\n      allow_any_instance_of(Hiera).to receive(:lookup).and_return(nil)\n      expect do\n        HieraPuppet.lookup('port', nil, scope, nil, :priority)\n      end.to raise_error(Puppet::ParseError,\n        /Could not find data item port in any Hiera data file and no default supplied/)\n    end\n  end\n\n  describe 'HieraPuppet#parse_args' do\n    it 'should return a 3 item array' do\n      args = ['foo', '8080', nil, nil]\n      expect(HieraPuppet.parse_args(args)).to eq(['foo', '8080', nil])\n    end\n\n    it 'should raise a useful error when no key is supplied' do\n      expect { HieraPuppet.parse_args([]) }.to raise_error(Puppet::ParseError,\n        /Please supply a parameter to perform a Hiera lookup/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/client_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Client do\n  let(:uri) { URI.parse('https://www.example.com') }\n  let(:puppet_context) { Puppet::SSL::SSLContext.new }\n  let(:system_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { described_class.new(ssl_context: puppet_context, system_ssl_context: system_context) }\n  let(:credentials) { ['user', 'pass'] }\n\n  it 'creates unique sessions' do\n    expect(client.create_session).to_not eq(client.create_session)\n  end\n\n  context \"when connecting\" do\n    it 'connects to HTTP URLs' do\n      uri = URI.parse('http://www.example.com')\n\n      client.connect(uri) do |http|\n        expect(http.address).to eq('www.example.com')\n        expect(http.port).to eq(80)\n        expect(http).to_not be_use_ssl\n      end\n    end\n\n    it 'connects to HTTPS URLs' do\n      client.connect(uri) do |http|\n        expect(http.address).to eq('www.example.com')\n        expect(http.port).to eq(443)\n        expect(http).to be_use_ssl\n      end\n    end\n\n    it 'raises ConnectionError if the connection is refused' do\n      allow_any_instance_of(Net::HTTP).to receive(:start).and_raise(Errno::ECONNREFUSED)\n\n      expect {\n        client.connect(uri)\n      }.to raise_error(Puppet::HTTP::ConnectionError, %r{^Request to https://www.example.com failed after .* seconds: (Connection refused|No connection could be made because the target machine actively refused it)})\n    end\n\n    it 'raises ConnectionError if the connect times out' do\n      allow_any_instance_of(Net::HTTP).to receive(:start).and_raise(Net::OpenTimeout)\n\n      expect {\n        client.connect(uri)\n      }.to raise_error(Puppet::HTTP::ConnectionError, %r{^Request to https://www.example.com timed out connect operation after .* seconds})\n    end\n\n    it 'connects using the default ssl context' do\n      expect(client.pool).to receive(:with_connection) do |_, verifier|\n        expect(verifier.ssl_context).to equal(puppet_context)\n      end\n\n      client.connect(uri)\n    end\n\n    it 'connects using a specified ssl context' do\n      other_context = Puppet::SSL::SSLContext.new\n\n      expect(client.pool).to receive(:with_connection) do |_, verifier|\n        expect(verifier.ssl_context).to equal(other_context)\n      end\n\n      client.connect(uri, options: {ssl_context: other_context})\n    end\n\n    it 'connects using the system store' do\n      expect(client.pool).to receive(:with_connection) do |_, verifier|\n        expect(verifier.ssl_context).to equal(system_context)\n      end\n\n      client.connect(uri, options: {include_system_store: true})\n    end\n\n    it 'does not create a verifier for HTTP connections' do\n      expect(client.pool).to receive(:with_connection) do |_, verifier|\n        expect(verifier).to be_nil\n      end\n\n      client.connect(URI.parse('http://www.example.com'))\n    end\n\n    it 'raises an HTTPError if both are specified' do\n      expect {\n        client.connect(uri, options: {ssl_context: puppet_context, include_system_store: true})\n      }.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)\n    end\n  end\n\n  context 'after connecting' do\n    def expect_http_error(cause, expected_message)\n      expect {\n        client.connect(uri) do |_|\n          raise cause, 'whoops'\n        end\n      }.to raise_error(Puppet::HTTP::HTTPError, expected_message)\n    end\n\n    it 're-raises HTTPError' do\n      expect_http_error(Puppet::HTTP::HTTPError, 'whoops')\n    end\n\n    it 'raises HTTPError if connection is interrupted while reading' do\n      expect_http_error(EOFError, %r{^Request to https://www.example.com interrupted after .* seconds})\n    end\n\n    it 'raises HTTPError if connection times out' do\n      expect_http_error(Net::ReadTimeout, %r{^Request to https://www.example.com timed out read operation after .* seconds})\n    end\n\n    it 'raises HTTPError if connection fails' do\n      expect_http_error(ArgumentError, %r{^Request to https://www.example.com failed after .* seconds})\n    end\n  end\n\n  context \"when closing\" do\n    it \"closes all connections in the pool\" do\n      expect(client.pool).to receive(:close)\n\n      client.close\n    end\n\n    it 'reloads the default ssl context' do\n      expect(client.pool).to receive(:with_connection) do |_, verifier|\n        expect(verifier.ssl_context).to_not equal(puppet_context)\n      end\n\n      client.close\n      client.connect(uri)\n    end\n\n    it 'reloads the default system ssl context' do\n      expect(client.pool).to receive(:with_connection) do |_, verifier|\n        expect(verifier.ssl_context).to_not equal(system_context)\n      end\n\n      client.close\n      client.connect(uri, options: {include_system_store: true})\n    end\n  end\n\n  context \"for GET requests\" do\n    it \"includes default HTTP headers\" do\n      stub_request(:get, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end\n\n      client.get(uri)\n    end\n\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:get, uri).with(query: \"foo=bar%3Dbaz\")\n\n      client.get(uri, params: {:foo => \"bar=baz\"})\n    end\n\n    it \"fails if a user passes in an invalid param type\" do\n      environment = Puppet::Node::Environment.create(:testing, [])\n\n      expect{client.get(uri, params: {environment: environment})}.to raise_error(Puppet::HTTP::SerializationError, /HTTP REST queries cannot handle values of type/)\n    end\n\n    it \"merges custom headers with default ones\" do\n      stub_request(:get, uri).with(headers: { 'X-Foo' => 'Bar', 'X-Puppet-Version' => /./, 'User-Agent' => /./ })\n\n      client.get(uri, headers: {'X-Foo' => 'Bar'})\n    end\n\n    it \"returns the response\" do\n      stub_request(:get, uri)\n\n      response = client.get(uri)\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"returns the entire response body\" do\n      stub_request(:get, uri).to_return(body: \"abc\")\n\n      expect(client.get(uri).body).to eq(\"abc\")\n    end\n\n    it \"streams the response body when a block is given\" do\n      stub_request(:get, uri).to_return(body: \"abc\")\n\n      io = StringIO.new\n      client.get(uri) do |response|\n        response.read_body do |data|\n          io.write(data)\n        end\n      end\n\n      expect(io.string).to eq(\"abc\")\n    end\n\n    context 'when connecting' do\n      it 'uses a specified ssl context' do\n        stub_request(:get, uri).to_return(body: \"abc\")\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.get(uri, options: {ssl_context: other_context})\n      end\n\n      it 'uses the system store' do\n        stub_request(:get, uri).to_return(body: \"abc\")\n\n        client.get(uri, options: {include_system_store: true})\n      end\n\n      it 'raises an HTTPError if both are specified' do\n        expect {\n          client.get(uri, options: {ssl_context: puppet_context, include_system_store: true})\n        }.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)\n      end\n    end\n  end\n\n  context \"for HEAD requests\" do\n    it \"includes default HTTP headers\" do\n      stub_request(:head, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./})\n\n      client.head(uri)\n    end\n\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:head, uri).with(query: \"foo=bar%3Dbaz\")\n\n      client.head(uri, params: {:foo => \"bar=baz\"})\n    end\n\n    it \"merges custom headers with default ones\" do\n      stub_request(:head, uri).with(headers: { 'X-Foo' => 'Bar', 'X-Puppet-Version' => /./, 'User-Agent' => /./ })\n\n      client.head(uri, headers: {'X-Foo' => 'Bar'})\n    end\n\n    it \"returns the response\" do\n      stub_request(:head, uri)\n\n      response = client.head(uri)\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"returns the entire response body\" do\n      stub_request(:head, uri).to_return(body: \"abc\")\n\n      expect(client.head(uri).body).to eq(\"abc\")\n    end\n\n    context 'when connecting' do\n      it 'uses a specified ssl context' do\n        stub_request(:head, uri)\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.head(uri, options: {ssl_context: other_context})\n      end\n\n      it 'uses the system store' do\n        stub_request(:head, uri)\n\n        client.head(uri, options: {include_system_store: true})\n      end\n\n      it 'raises an HTTPError if both are specified' do\n        expect {\n          client.head(uri, options: {ssl_context: puppet_context, include_system_store: true})\n        }.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)\n      end\n    end\n  end\n\n  context \"for PUT requests\" do\n    it \"includes default HTTP headers\" do\n      stub_request(:put, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end\n\n      client.put(uri, \"\", headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:put, \"https://www.example.com\").with(query: \"foo=bar%3Dbaz\")\n\n      client.put(uri, \"\", params: {:foo => \"bar=baz\"}, headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"includes custom headers\" do\n      stub_request(:put, \"https://www.example.com\").with(headers: { 'X-Foo' => 'Bar' })\n\n      client.put(uri, \"\", headers: {'X-Foo' => 'Bar', 'Content-Type' => 'text/plain'})\n    end\n\n    it \"returns the response\" do\n      stub_request(:put, uri)\n\n      response = client.put(uri, \"\", headers: {'Content-Type' => 'text/plain'})\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"sets content-length and content-type for the body\" do\n      stub_request(:put, uri).with(headers: {\"Content-Length\" => \"5\", \"Content-Type\" => \"text/plain\"})\n\n      client.put(uri, \"hello\", headers: {'Content-Type' => 'text/plain'})\n    end\n\n     it 'raises an ArgumentError if `body` is missing' do\n       expect {\n         client.put(uri, nil, headers: {'Content-Type' => 'text/plain'})\n       }.to raise_error(ArgumentError, /'put' requires a string 'body' argument/)\n     end\n\n     it 'raises an ArgumentError if `content_type` is missing from the headers hash' do\n       expect {\n         client.put(uri, '')\n       }.to raise_error(ArgumentError, /'put' requires a 'content-type' header/)\n     end\n\n    context 'when connecting' do\n      it 'uses a specified ssl context' do\n        stub_request(:put, uri)\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.put(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: other_context})\n      end\n\n      it 'uses the system store' do\n        stub_request(:put, uri)\n\n        client.put(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {include_system_store: true})\n      end\n\n      it 'raises an HTTPError if both are specified' do\n        expect {\n          client.put(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: puppet_context, include_system_store: true})\n        }.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)\n      end\n    end\n  end\n\n  context \"for POST requests\" do\n    it \"includes default HTTP headers\" do\n      stub_request(:post, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./})\n\n      client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:post, \"https://www.example.com\").with(query: \"foo=bar%3Dbaz\")\n\n      client.post(uri, \"\", params: {:foo => \"bar=baz\"}, headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"includes custom headers\" do\n      stub_request(:post, \"https://www.example.com\").with(headers: { 'X-Foo' => 'Bar' })\n\n      client.post(uri, \"\", headers: {'X-Foo' => 'Bar', 'Content-Type' => 'text/plain'})\n    end\n\n    it \"returns the response\" do\n      stub_request(:post, uri)\n\n      response = client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'})\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"sets content-length and content-type for the body\" do\n      stub_request(:post, uri).with(headers: {\"Content-Length\" => \"5\", \"Content-Type\" => \"text/plain\"})\n\n      client.post(uri, \"hello\", headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"streams the response body when a block is given\" do\n      stub_request(:post, uri).to_return(body: \"abc\")\n\n      io = StringIO.new\n      client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}) do |response|\n        response.read_body do |data|\n          io.write(data)\n        end\n      end\n\n      expect(io.string).to eq(\"abc\")\n    end\n\n    it 'raises an ArgumentError if `body` is missing' do\n      expect {\n        client.post(uri, nil, headers: {'Content-Type' => 'text/plain'})\n      }.to raise_error(ArgumentError, /'post' requires a string 'body' argument/)\n    end\n\n    it 'raises an ArgumentError if `content_type` is missing from the headers hash' do\n      expect {\n        client.post(uri, \"\")\n      }.to raise_error(ArgumentError, /'post' requires a 'content-type' header/)\n    end\n\n    context 'when connecting' do\n      it 'uses a specified ssl context' do\n        stub_request(:post, uri)\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {body: \"\", ssl_context: other_context})\n      end\n\n      it 'uses the system store' do\n        stub_request(:post, uri)\n\n        client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {include_system_store: true})\n      end\n\n      it 'raises an HTTPError if both are specified' do\n        expect {\n          client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: puppet_context, include_system_store: true})\n        }.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)\n      end\n    end\n  end\n\n  context \"for DELETE requests\" do\n    it \"includes default HTTP headers\" do\n      stub_request(:delete, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./})\n\n      client.delete(uri)\n    end\n\n    it \"merges custom headers with default ones\" do\n      stub_request(:delete, uri).with(headers: { 'X-Foo' => 'Bar', 'X-Puppet-Version' => /./, 'User-Agent' => /./ })\n\n      client.delete(uri, headers: {'X-Foo' => 'Bar'})\n    end\n\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:delete, \"https://www.example.com\").with(query: \"foo=bar%3Dbaz\")\n\n      client.delete(uri, params: {:foo => \"bar=baz\"})\n    end\n\n    it \"returns the response\" do\n      stub_request(:delete, uri)\n\n      response = client.delete(uri)\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"returns the entire response body\" do\n      stub_request(:delete, uri).to_return(body: \"abc\")\n\n      expect(client.delete(uri).body).to eq(\"abc\")\n    end\n\n    context 'when connecting' do\n      it 'uses a specified ssl context' do\n        stub_request(:delete, uri)\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.delete(uri, options: {ssl_context: other_context})\n      end\n\n      it 'uses the system store' do\n        stub_request(:delete, uri)\n\n        client.delete(uri, options: {include_system_store: true})\n      end\n\n      it 'raises an HTTPError if both are specified' do\n        expect {\n          client.delete(uri, options: {ssl_context: puppet_context, include_system_store: true})\n        }.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)\n      end\n    end\n  end\n\n  context \"Basic Auth\" do\n    it \"submits credentials for GET requests\" do\n      stub_request(:get, uri).with(basic_auth: credentials)\n\n      client.get(uri, options: {basic_auth: {user: 'user', password: 'pass'}})\n    end\n\n    it \"submits credentials for PUT requests\" do\n      stub_request(:put, uri).with(basic_auth: credentials)\n\n      client.put(uri, \"hello\", headers: {'Content-Type' => 'text/plain'}, options: {basic_auth: {user: 'user', password: 'pass'}})\n    end\n\n    it \"returns response containing access denied\" do\n      stub_request(:get, uri).with(basic_auth: credentials).to_return(status: [403, \"Ye Shall Not Pass\"])\n\n      response = client.get(uri, options: {basic_auth: {user: 'user', password: 'pass'}})\n      expect(response.code).to eq(403)\n      expect(response.reason).to eq(\"Ye Shall Not Pass\")\n      expect(response).to_not be_success\n    end\n\n    it 'includes basic auth if user is nil' do\n      stub_request(:get, uri).with do |req|\n        expect(req.headers).to include('Authorization')\n      end\n\n      client.get(uri, options: {basic_auth: {user: nil, password: 'pass'}})\n    end\n\n    it 'includes basic auth if password is nil' do\n      stub_request(:get, uri).with do |req|\n        expect(req.headers).to include('Authorization')\n      end\n\n      client.get(uri, options: {basic_auth: {user: 'user', password: nil}})\n    end\n\n    it 'observes userinfo in the URL' do\n      stub_request(:get, uri).with(basic_auth: credentials)\n\n      client.get(URI(\"https://user:pass@www.example.com\"))\n    end\n\n    it 'prefers explicit basic_auth credentials' do\n      uri = URI(\"https://ignored_user:ignored_pass@www.example.com\")\n\n      stub_request(:get, \"https://www.example.com\").with(basic_auth: credentials)\n\n      client.get(uri, options: {basic_auth: {user: 'user', password: 'pass'}})\n    end\n  end\n\n  context \"when redirecting\" do\n    let(:start_url)  { URI(\"https://www.example.com:8140/foo\") }\n    let(:bar_url)  { \"https://www.example.com:8140/bar\" }\n    let(:baz_url) { \"https://www.example.com:8140/baz\" }\n    let(:other_host)  { \"https://other.example.com:8140/qux\" }\n\n    def redirect_to(status: 302, url:)\n      { status: status, headers: { 'Location' => url }, body: \"Redirected to #{url}\" }\n    end\n\n    it \"preserves GET method\" do\n      stub_request(:get, start_url).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).to_return(status: 200)\n\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"preserves PUT method\" do\n      stub_request(:put, start_url).to_return(redirect_to(url: bar_url))\n      stub_request(:put, bar_url).to_return(status: 200)\n\n      response = client.put(start_url, \"\", headers: {'Content-Type' => 'text/plain'})\n      expect(response).to be_success\n    end\n\n    it \"updates the Host header from the Location host and port\" do\n      stub_request(:get, start_url).with(headers: { 'Host' => 'www.example.com:8140' })\n        .to_return(redirect_to(url: other_host))\n      stub_request(:get, other_host).with(headers: { 'Host' => 'other.example.com:8140' })\n        .to_return(status: 200)\n\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"omits the default HTTPS port from the Host header\" do\n      stub_request(:get, start_url).with(headers: { 'Host' => 'www.example.com:8140' })\n        .to_return(redirect_to(url: \"https://other.example.com/qux\"))\n      stub_request(:get, \"https://other.example.com/qux\").with(headers: { 'Host' => 'other.example.com' })\n        .to_return(status: 200)\n\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"omits the default HTTP port from the Host header\" do\n      stub_request(:get, start_url).with(headers: { 'Host' => 'www.example.com:8140' })\n        .to_return(redirect_to(url: \"http://other.example.com/qux\"))\n      stub_request(:get, \"http://other.example.com/qux\").with(headers: { 'Host' => 'other.example.com' })\n        .to_return(status: 200)\n\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"applies query parameters from the location header\" do\n      query = { 'redirected' => false }\n\n      stub_request(:get, start_url).with(query: query).to_return(redirect_to(url: \"#{bar_url}?redirected=true\"))\n      stub_request(:get, bar_url).with(query: {'redirected' => 'true'}).to_return(status: 200)\n\n      response = client.get(start_url, params: query)\n      expect(response).to be_success\n    end\n\n    it \"preserves custom and default headers when redirecting\" do\n      headers = { 'X-Foo' => 'Bar', 'X-Puppet-Version' => Puppet.version }\n      stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).with(headers: headers).to_return(status: 200)\n\n      response = client.get(start_url, headers: headers)\n      expect(response).to be_success\n    end\n\n    it \"does not preserve basic authorization when redirecting to different hosts\" do\n      stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: other_host))\n      stub_request(:get, other_host).to_return(status: 200)\n\n      client.get(start_url, options: {basic_auth: {user: 'user', password: 'pass'}})\n      expect(a_request(:get, other_host).\n      with{ |req| !req.headers.key?('Authorization')}).to have_been_made\n    end\n\n    it \"does preserve basic authorization when redirecting to the same hosts\" do\n      stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).with(basic_auth: credentials).to_return(status: 200)\n\n      client.get(start_url, options: {basic_auth: {user: 'user', password: 'pass'}})\n      expect(a_request(:get, bar_url).\n      with{ |req| req.headers.key?('Authorization')}).to have_been_made\n    end\n\n    it \"does not preserve cookie header when redirecting to different hosts\" do\n      headers = { 'Cookie' => 'TEST_COOKIE'}\n\n      stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: other_host))\n      stub_request(:get, other_host).to_return(status: 200)\n\n      client.get(start_url, headers: headers)\n      expect(a_request(:get, other_host).\n      with{ |req| !req.headers.key?('Cookie')}).to have_been_made\n    end\n\n    it \"does preserve cookie header when redirecting to the same hosts\" do\n      headers = { 'Cookie' => 'TEST_COOKIE'}\n\n      stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).with(headers: headers).to_return(status: 200)\n\n      client.get(start_url, headers: headers)\n      expect(a_request(:get, bar_url).\n      with{ |req| req.headers.key?('Cookie')}).to have_been_made\n    end\n\n    it \"does preserves cookie header and basic authentication when Puppet[:location_trusted] is true redirecting to different hosts\" do\n      headers = { 'cookie' => 'TEST_COOKIE'}\n      Puppet[:location_trusted] = true\n\n      stub_request(:get, start_url).with(headers: headers, basic_auth: credentials).to_return(redirect_to(url: other_host))\n      stub_request(:get, other_host).with(headers: headers, basic_auth: credentials).to_return(status: 200)\n\n      client.get(start_url, headers: headers, options: {basic_auth: {user: 'user', password: 'pass'}})\n      expect(a_request(:get, other_host).\n      with{ |req| req.headers.key?('Authorization') && req.headers.key?('Cookie')}).to have_been_made\n    end\n\n    it \"treats hosts as case-insensitive\" do\n      start_url = URI(\"https://www.EXAmple.com:8140/Start\")\n      bar_url = \"https://www.example.com:8140/bar\"\n\n      stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).with(basic_auth: credentials).to_return(status: 200)\n\n      client.get(start_url, options: {basic_auth: {user: 'user', password: 'pass'}})\n      expect(a_request(:get, bar_url).\n      with{ |req| req.headers.key?('Authorization')}).to have_been_made\n    end\n\n    it \"redirects given a relative location\" do\n      relative_url = \"/people.html\"\n      stub_request(:get, start_url).to_return(redirect_to(url: relative_url))\n      stub_request(:get, \"https://www.example.com:8140/people.html\").to_return(status: 200)\n\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"applies query parameters from the location header\" do\n      relative_url = \"/people.html\"\n      query = { 'redirected' => false }\n      stub_request(:get, start_url).with(query: query).to_return(redirect_to(url: \"#{relative_url}?redirected=true\"))\n      stub_request(:get, \"https://www.example.com:8140/people.html\").with(query: {'redirected' => 'true'}).to_return(status: 200)\n\n      response = client.get(start_url, params: query)\n      expect(response).to be_success\n    end\n\n    it \"removes dot segments from a relative location\" do\n      # from https://tools.ietf.org/html/rfc3986#section-5.4.2\n      base_url = URI(\"http://a/b/c/d;p?q\")\n      relative_url = \"../../../../g\"\n      stub_request(:get, base_url).to_return(redirect_to(url: relative_url))\n      stub_request(:get, \"http://a/g\").to_return(status: 200)\n\n      response = client.get(base_url)\n      expect(response).to be_success\n    end\n\n    it \"preserves request body for each request\" do\n      data = 'some data'\n      stub_request(:put, start_url).with(body: data).to_return(redirect_to(url: bar_url))\n      stub_request(:put, bar_url).with(body: data).to_return(status: 200)\n\n      response = client.put(start_url, data, headers: {'Content-Type' => 'text/plain'})\n      expect(response).to be_success\n    end\n\n    it \"returns the body from the final response\" do\n      stub_request(:get, start_url).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).to_return(status: 200, body: 'followed')\n\n      response = client.get(start_url)\n      expect(response.body).to eq('followed')\n    end\n\n    [301, 307].each do |code|\n      it \"also redirects on #{code}\" do\n        stub_request(:get, start_url).to_return(redirect_to(status: code, url: bar_url))\n        stub_request(:get, bar_url).to_return(status: 200)\n\n        response = client.get(start_url)\n        expect(response).to be_success\n      end\n    end\n\n    [303, 308].each do |code|\n      it \"returns an error on #{code}\" do\n        stub_request(:get, start_url).to_return(redirect_to(status: code, url: bar_url))\n\n        response = client.get(start_url)\n        expect(response.code).to eq(code)\n        expect(response).to_not be_success\n      end\n    end\n\n    it \"raises an error if the Location header is missing\" do\n      stub_request(:get, start_url).to_return(status: 302)\n\n      expect {\n        client.get(start_url)\n      }.to raise_error(Puppet::HTTP::ProtocolError, \"Location response header is missing\")\n    end\n\n    it \"raises an error if the Location header is invalid\" do\n      stub_request(:get, start_url).to_return(redirect_to(status: 302, url: 'http://foo\"bar'))\n\n      expect {\n        client.get(start_url)\n      }.to raise_error(Puppet::HTTP::ProtocolError, /Location URI is invalid/)\n    end\n\n    it \"raises an error if limit is 0 and we're asked to follow\" do\n      stub_request(:get, start_url).to_return(redirect_to(url: bar_url))\n\n      client = described_class.new(redirect_limit: 0)\n      expect {\n        client.get(start_url)\n      }.to raise_error(Puppet::HTTP::TooManyRedirects, %r{Too many HTTP redirections for https://www.example.com:8140})\n    end\n\n    it \"raises an error if asked to follow redirects more times than the limit\" do\n      stub_request(:get, start_url).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).to_return(redirect_to(url: baz_url))\n\n      client = described_class.new(redirect_limit: 1)\n      expect {\n        client.get(start_url)\n      }.to raise_error(Puppet::HTTP::TooManyRedirects, %r{Too many HTTP redirections for https://www.example.com:8140})\n    end\n\n    it \"follows multiple redirects if equal to or less than the redirect limit\" do\n      stub_request(:get, start_url).to_return(redirect_to(url: bar_url))\n      stub_request(:get, bar_url).to_return(redirect_to(url: baz_url))\n      stub_request(:get, baz_url).to_return(status: 200)\n\n      client = described_class.new(redirect_limit: 2)\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"redirects to a different host\" do\n      stub_request(:get, start_url).to_return(redirect_to(url: other_host))\n      stub_request(:get, other_host).to_return(status: 200)\n\n      response = client.get(start_url)\n      expect(response).to be_success\n    end\n\n    it \"redirects from http to https\" do\n      http = URI(\"http://example.com/foo\")\n      https = URI(\"https://example.com/bar\")\n\n      stub_request(:get, http).to_return(redirect_to(url: https))\n      stub_request(:get, https).to_return(status: 200)\n\n      response = client.get(http)\n      expect(response).to be_success\n    end\n\n    it \"redirects from https to http\" do\n      http = URI(\"http://example.com/foo\")\n      https = URI(\"https://example.com/bar\")\n\n      stub_request(:get, https).to_return(redirect_to(url: http))\n      stub_request(:get, http).to_return(status: 200)\n\n      response = client.get(https)\n      expect(response).to be_success\n    end\n\n    it \"does not preserve accept-encoding header when redirecting\" do\n      headers = { 'Accept-Encoding' => 'unwanted-encoding'}\n\n      stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: other_host))\n      stub_request(:get, other_host).to_return(status: 200)\n\n      client.get(start_url, headers: headers)\n      expect(a_request(:get, other_host).\n      with{ |req| req.headers['Accept-Encoding'] != 'unwanted-encoding' }).to have_been_made\n    end\n  end\n\n  context \"when response indicates an overloaded server\" do\n    def retry_after(datetime)\n      stub_request(:get, uri)\n        .to_return(status: [503, 'Service Unavailable'], headers: {'Retry-After' => datetime}).then\n        .to_return(status: 200)\n    end\n\n    it \"returns a 503 response if Retry-After is not set\" do\n      stub_request(:get, uri).to_return(status: [503, 'Service Unavailable'])\n\n      expect(client.get(uri).code).to eq(503)\n    end\n\n    it \"raises if Retry-After is not convertible to an Integer or RFC 2822 Date\" do\n      stub_request(:get, uri).to_return(status: [503, 'Service Unavailable'], headers: {'Retry-After' => 'foo'})\n\n      expect {\n        client.get(uri)\n      }.to raise_error(Puppet::HTTP::ProtocolError, /Failed to parse Retry-After header 'foo' as an integer or RFC 2822 date/)\n    end\n\n    it \"should close the connection before sleeping\" do\n      retry_after('42')\n\n      site = Puppet::HTTP::Site.from_uri(uri)\n\n      http1 = Net::HTTP.new(site.host, site.port)\n      http1.use_ssl = true\n      allow(http1).to receive(:started?).and_return(true)\n\n      http2 = Net::HTTP.new(site.host, site.port)\n      http2.use_ssl = true\n      allow(http2).to receive(:started?).and_return(true)\n\n      pool = Puppet::HTTP::Pool.new(15)\n      client = Puppet::HTTP::Client.new(pool: pool)\n\n      # The \"with_connection\" method is required to yield started connections\n      allow(pool).to receive(:with_connection).and_yield(http1).and_yield(http2)\n\n      expect(http1).to receive(:finish).ordered\n      expect(::Kernel).to receive(:sleep).with(42).ordered\n\n      client.get(uri)\n    end\n\n    it \"should sleep and retry if Retry-After is an Integer\" do\n      retry_after('42')\n\n      expect(::Kernel).to receive(:sleep).with(42)\n\n      client.get(uri)\n    end\n\n    it \"should sleep and retry if Retry-After is an RFC 2822 Date\" do\n      retry_after('Wed, 13 Apr 2005 15:18:05 GMT')\n\n      now = DateTime.new(2005, 4, 13, 8, 17, 5, '-07:00')\n      allow(DateTime).to receive(:now).and_return(now)\n\n      expect(::Kernel).to receive(:sleep).with(60)\n\n      client.get(uri)\n    end\n\n    it \"should sleep for no more than the Puppet runinterval\" do\n      retry_after('60')\n      Puppet[:runinterval] = 30\n\n      expect(::Kernel).to receive(:sleep).with(30)\n\n      client.get(uri)\n    end\n\n    it \"should sleep for 0 seconds if the RFC 2822 date has past\" do\n      retry_after('Wed, 13 Apr 2005 15:18:05 GMT')\n\n      expect(::Kernel).to receive(:sleep).with(0)\n\n      client.get(uri)\n    end\n  end\n\n  context \"persistent connections\" do\n    before :each do\n      stub_request(:get, uri)\n    end\n\n    it 'defaults keepalive to http_keepalive_timeout' do\n      expect(client.pool.keepalive_timeout).to eq(Puppet[:http_keepalive_timeout])\n    end\n\n    it 'reuses a cached connection' do\n      allow(Puppet).to receive(:debug)\n      expect(Puppet).to receive(:debug).with(/^Creating new connection/)\n      expect(Puppet).to receive(:debug).with(/^Using cached connection/)\n\n      client.get(uri)\n      client.get(uri)\n    end\n\n    it 'can be disabled' do\n      Puppet[:http_keepalive_timeout] = 0\n\n      allow(Puppet).to receive(:debug)\n      expect(Puppet).to receive(:debug).with(/^Creating new connection/).twice\n      expect(Puppet).to receive(:debug).with(/^Using cached connection/).never\n\n      client.get(uri)\n      client.get(uri)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/dns_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::DNS do\n  before do\n    @dns_mock_object = double('dns')\n    allow(Resolv::DNS).to receive(:new).and_return(@dns_mock_object)\n\n    @rr_type         = Resolv::DNS::Resource::IN::SRV\n    @test_srv_domain = \"domain.com\"\n    @test_a_hostname = \"puppet.domain.com\"\n    @test_port       = 1000\n\n    # The records we should use.\n    @test_records = [\n      #                                  priority,  weight, port, target\n      Resolv::DNS::Resource::IN::SRV.new(0,         20,     8140, \"puppet1.domain.com\"),\n      Resolv::DNS::Resource::IN::SRV.new(0,         80,     8140, \"puppet2.domain.com\"),\n      Resolv::DNS::Resource::IN::SRV.new(1,         1,      8140, \"puppet3.domain.com\"),\n      Resolv::DNS::Resource::IN::SRV.new(4,         1,      8140, \"puppet4.domain.com\")\n    ]\n\n    @test_records.each do |rec|\n      # Resources do not expose public API for setting the TTL\n      rec.instance_variable_set(:@ttl, 3600)\n    end\n  end\n\n  let(:resolver) { described_class.new }\n\n  describe 'when the domain is not known' do\n    before :each do\n      allow(@dns_mock_object).to receive(:getresources).and_return(@test_records)\n    end\n\n    describe 'because domain is nil' do\n      it 'does not yield' do\n        resolver.each_srv_record(nil) do |_,_,_|\n          raise Exception.new(\"nil domain caused SRV lookup\")\n        end\n      end\n    end\n\n    describe 'because domain is an empty string' do\n      it 'does not yield' do\n        resolver.each_srv_record('') do |_,_,_|\n          raise Exception.new(\"nil domain caused SRV lookup\")\n        end\n      end\n    end\n  end\n\n  describe \"when resolving a host without SRV records\" do\n\n    it \"should not yield anything\" do\n      # No records returned for a DNS entry without any SRV records\n      expect(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet._tcp.#{@test_a_hostname}\",\n        @rr_type\n      ).and_return([])\n\n      resolver.each_srv_record(@test_a_hostname) do |hostname, port, remaining|\n        raise Exception.new(\"host with no records passed block\")\n      end\n    end\n  end\n\n  describe \"when resolving a host with SRV records\" do\n    it \"should iterate through records in priority order\" do\n      # The order of the records that should be returned,\n      # an array means unordered (for weight)\n      order = {\n        0 => [\"puppet1.domain.com\", \"puppet2.domain.com\"],\n        1 => [\"puppet3.domain.com\"],\n        2 => [\"puppet4.domain.com\"]\n      }\n\n      expect(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet._tcp.#{@test_srv_domain}\",\n        @rr_type\n      ).and_return(@test_records)\n\n      resolver.each_srv_record(@test_srv_domain) do |hostname, port|\n        expected_priority = order.keys.min\n\n        expect(order[expected_priority]).to include(hostname)\n        expect(port).not_to be(@test_port)\n\n        # Remove the host from our expected hosts\n        order[expected_priority].delete hostname\n\n        # Remove this priority level if we're done with it\n        order.delete expected_priority if order[expected_priority] == []\n      end\n    end\n\n    it \"should fall back to the :puppet service if no records are found for a more specific service\" do\n      # The order of the records that should be returned,\n      # an array means unordered (for weight)\n      order = {\n        0 => [\"puppet1.domain.com\", \"puppet2.domain.com\"],\n        1 => [\"puppet3.domain.com\"],\n        2 => [\"puppet4.domain.com\"]\n      }\n\n      expect(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet-report._tcp.#{@test_srv_domain}\",\n        @rr_type\n      ).and_return([])\n\n      expect(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet._tcp.#{@test_srv_domain}\",\n        @rr_type\n      ).and_return(@test_records)\n\n      resolver.each_srv_record(@test_srv_domain, :report) do |hostname, port|\n        expected_priority = order.keys.min\n\n        expect(order[expected_priority]).to include(hostname)\n        expect(port).not_to be(@test_port)\n\n        # Remove the host from our expected hosts\n        order[expected_priority].delete hostname\n\n        # Remove this priority level if we're done with it\n        order.delete expected_priority if order[expected_priority] == []\n      end\n    end\n\n    it \"should use SRV records from the specific service if they exist\" do\n      # The order of the records that should be returned,\n      # an array means unordered (for weight)\n      order = {\n        0 => [\"puppet1.domain.com\", \"puppet2.domain.com\"],\n        1 => [\"puppet3.domain.com\"],\n        2 => [\"puppet4.domain.com\"]\n      }\n\n      bad_records = [\n        #                                  priority,  weight, port, hostname\n        Resolv::DNS::Resource::IN::SRV.new(0,         20,     8140, \"puppet1.bad.domain.com\"),\n        Resolv::DNS::Resource::IN::SRV.new(0,         80,     8140, \"puppet2.bad.domain.com\"),\n        Resolv::DNS::Resource::IN::SRV.new(1,         1,      8140, \"puppet3.bad.domain.com\"),\n        Resolv::DNS::Resource::IN::SRV.new(4,         1,      8140, \"puppet4.bad.domain.com\")\n      ]\n\n      expect(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet-report._tcp.#{@test_srv_domain}\",\n        @rr_type\n      ).and_return(@test_records)\n\n      allow(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet._tcp.#{@test_srv_domain}\",\n        @rr_type\n      ).and_return(bad_records)\n\n      resolver.each_srv_record(@test_srv_domain, :report) do |hostname, port|\n        expected_priority = order.keys.min\n\n        expect(order[expected_priority]).to include(hostname)\n        expect(port).not_to be(@test_port)\n\n        # Remove the host from our expected hosts\n        order[expected_priority].delete hostname\n\n        # Remove this priority level if we're done with it\n        order.delete expected_priority if order[expected_priority] == []\n      end\n    end\n  end\n\n  describe \"when finding weighted servers\" do\n    it \"should return nil when no records were found\" do\n      expect(resolver.find_weighted_server([])).to eq(nil)\n    end\n\n    it \"should return the first record when one record is passed\" do\n      result = resolver.find_weighted_server([@test_records.first])\n      expect(result).to eq(@test_records.first)\n    end\n\n    {\n      \"all have weights\"  => [1, 3, 2, 4],\n      \"some have weights\" => [2, 0, 1, 0],\n      \"none have weights\" => [0, 0, 0, 0],\n    }.each do |name, weights|\n      it \"should return correct results when #{name}\" do\n        records = []\n        count   = 0\n        weights.each do |w|\n          count += 1\n          #                                             priority, weight, port, server\n          records << Resolv::DNS::Resource::IN::SRV.new(0,        w,      1,    count.to_s)\n        end\n\n        seen  = Hash.new(0)\n        total_weight = records.inject(0) do |sum, record|\n          sum + resolver.weight(record)\n        end\n\n        total_weight.times do |n|\n          expect(Kernel).to receive(:rand).once.with(total_weight).and_return(n)\n          server = resolver.find_weighted_server(records)\n          seen[server] += 1\n        end\n\n        expect(seen.length).to eq(records.length)\n        records.each do |record|\n          expect(seen[record]).to eq(resolver.weight(record))\n        end\n      end\n    end\n  end\n\n  describe \"caching records\" do\n    it \"should query DNS when no cache entry exists, then retrieve the cached value\" do\n      expect(@dns_mock_object).to receive(:getresources).with(\n        \"_x-puppet._tcp.#{@test_srv_domain}\",\n        @rr_type\n      ).and_return(@test_records).once\n\n      fetched_servers = []\n      resolver.each_srv_record(@test_srv_domain) do |server, port|\n        fetched_servers << server\n      end\n\n      cached_servers = []\n      expect(resolver).to receive(:expired?).and_return(false)\n      resolver.each_srv_record(@test_srv_domain) do |server, port|\n        cached_servers << server\n      end\n      expect(fetched_servers).to match_array(cached_servers)\n    end\n\n    context \"TTLs\" do\n      before(:each) do\n        # The TTL of an SRV record cannot be set via any public API\n        ttl_record1 = Resolv::DNS::Resource::IN::SRV.new(0, 20, 8140, \"puppet1.domain.com\")\n        ttl_record1.instance_variable_set(:@ttl, 10)\n        ttl_record2 = Resolv::DNS::Resource::IN::SRV.new(0, 20, 8140, \"puppet2.domain.com\")\n        ttl_record2.instance_variable_set(:@ttl, 20)\n        records = [ttl_record1, ttl_record2]\n\n        expect(@dns_mock_object).to receive(:getresources).with(\n          \"_x-puppet._tcp.#{@test_srv_domain}\",\n          @rr_type\n        ).and_return(records)\n      end\n\n      it \"should save the shortest TTL among records for a service\" do\n        resolver.each_srv_record(@test_srv_domain) { |server, port| }\n        expect(resolver.ttl(:puppet)).to eq(10)\n      end\n\n      it \"should fetch records again if the TTL has expired\" do\n        # Fetch from DNS\n        resolver.each_srv_record(@test_srv_domain) do |server, port|\n          expect(server).to match(/puppet.*domain\\.com/)\n        end\n\n        expect(resolver).to receive(:expired?).with(:puppet).and_return(false)\n        # Load from cache\n        resolver.each_srv_record(@test_srv_domain) do |server, port|\n          expect(server).to match(/puppet.*domain\\.com/)\n        end\n\n        new_record = Resolv::DNS::Resource::IN::SRV.new(0, 20, 8140, \"new.domain.com\")\n        new_record.instance_variable_set(:@ttl, 10)\n        expect(@dns_mock_object).to receive(:getresources).with(\n          \"_x-puppet._tcp.#{@test_srv_domain}\",\n          @rr_type\n        ).and_return([new_record])\n        expect(resolver).to receive(:expired?).with(:puppet).and_return(true)\n        # Refresh from DNS\n        resolver.each_srv_record(@test_srv_domain) do |server, port|\n          expect(server).to eq(\"new.domain.com\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/external_client_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\n# Simple \"external\" client to make get & post requests. This is used\n# to test the old HTTP API, such as requiring use_ssl and basic_auth\n# to be passed as options.\nclass Puppet::HTTP::TestExternal\n  def initialize(host, port, options = {})\n    @host = host\n    @port = port\n    @options = options\n    @factory = Puppet::HTTP::Factory.new\n  end\n\n  def get(path, headers = {}, options = {})\n    request = Net::HTTP::Get.new(path, headers)\n    do_request(request, options)\n  end\n\n  def post(path, data, headers = nil, options = {})\n    request = Net::HTTP::Post.new(path, headers)\n    do_request(request, options)\n  end\n\n  def do_request(request, options)\n    if options[:basic_auth]\n      request.basic_auth(options[:basic_auth][:user], options[:basic_auth][:password])\n    end\n\n    site = Puppet::HTTP::Site.new(@options[:use_ssl] ? 'https' : 'http', @host, @port)\n    http = @factory.create_connection(site)\n    http.start\n    begin\n      http.request(request)\n    ensure\n      http.finish\n    end\n  end\nend\n\ndescribe Puppet::HTTP::ExternalClient do\n  let(:uri) { URI.parse('https://www.example.com') }\n  let(:http_client_class) { Puppet::HTTP::TestExternal }\n  let(:client) { described_class.new(http_client_class) }\n  let(:credentials) { ['user', 'pass'] }\n\n  context \"for GET requests\" do\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:get, uri).with(query: \"foo=bar%3Dbaz\")\n\n      client.get(uri, params: {:foo => \"bar=baz\"})\n    end\n\n    it \"fails if a user passes in an invalid param type\" do\n      environment = Puppet::Node::Environment.create(:testing, [])\n\n      expect{client.get(uri, params: {environment: environment})}.to raise_error(Puppet::HTTP::SerializationError, /HTTP REST queries cannot handle values of type/)\n    end\n\n    it \"returns the response\" do\n      stub_request(:get, uri)\n\n      response = client.get(uri)\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"returns the entire response body\" do\n      stub_request(:get, uri).to_return(body: \"abc\")\n\n      expect(client.get(uri).body).to eq(\"abc\")\n    end\n\n    it \"streams the response body when a block is given\" do\n      stub_request(:get, uri).to_return(body: \"abc\")\n\n      io = StringIO.new\n      client.get(uri) do |response|\n        response.read_body do |data|\n          io.write(data)\n        end\n      end\n\n      expect(io.string).to eq(\"abc\")\n    end\n\n    context 'when connecting' do\n      it 'accepts an ssl context' do\n        stub_request(:get, uri).to_return(body: \"abc\")\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.get(uri, options: {ssl_context: other_context})\n      end\n\n      it 'accepts include_system_store' do\n        stub_request(:get, uri).to_return(body: \"abc\")\n\n        client.get(uri, options: {include_system_store: true})\n      end\n    end\n  end\n\n  context \"for POST requests\" do\n    it \"stringifies keys and encodes values in the query\" do\n      stub_request(:post, \"https://www.example.com\").with(query: \"foo=bar%3Dbaz\")\n\n      client.post(uri, \"\", params: {:foo => \"bar=baz\"}, headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"returns the response\" do\n      stub_request(:post, uri)\n\n      response = client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'})\n      expect(response).to be_a(Puppet::HTTP::Response)\n      expect(response).to be_success\n      expect(response.code).to eq(200)\n    end\n\n    it \"sets content-type for the body\" do\n      stub_request(:post, uri).with(headers: {\"Content-Type\" => \"text/plain\"})\n\n      client.post(uri, \"hello\", headers: {'Content-Type' => 'text/plain'})\n    end\n\n    it \"streams the response body when a block is given\" do\n      stub_request(:post, uri).to_return(body: \"abc\")\n\n      io = StringIO.new\n      client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}) do |response|\n        response.read_body do |data|\n          io.write(data)\n        end\n      end\n\n      expect(io.string).to eq(\"abc\")\n    end\n\n    it 'raises an ArgumentError if `body` is missing' do\n      expect {\n        client.post(uri, nil, headers: {'Content-Type' => 'text/plain'})\n      }.to raise_error(ArgumentError, /'post' requires a string 'body' argument/)\n    end\n\n    context 'when connecting' do\n      it 'accepts an ssl context' do\n        stub_request(:post, uri)\n\n        other_context = Puppet::SSL::SSLContext.new\n\n        client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: other_context})\n      end\n\n      it 'accepts include_system_store' do\n        stub_request(:post, uri)\n\n        client.post(uri, \"\", headers: {'Content-Type' => 'text/plain'}, options: {include_system_store: true})\n      end\n    end\n  end\n\n  context \"Basic Auth\" do\n    it \"submits credentials for GET requests\" do\n      stub_request(:get, uri).with(basic_auth: credentials)\n\n      client.get(uri, options: {basic_auth: {user: 'user', password: 'pass'}})\n    end\n\n    it \"submits credentials for POST requests\" do\n      stub_request(:post, uri).with(basic_auth: credentials)\n\n      client.post(uri, \"\", options: {content_type: 'text/plain', basic_auth: {user: 'user', password: 'pass'}})\n    end\n\n    it \"returns response containing access denied\" do\n      stub_request(:get, uri).with(basic_auth: credentials).to_return(status: [403, \"Ye Shall Not Pass\"])\n\n      response = client.get(uri, options: { basic_auth: {user: 'user', password: 'pass'}})\n      expect(response.code).to eq(403)\n      expect(response.reason).to eq(\"Ye Shall Not Pass\")\n      expect(response).to_not be_success\n    end\n\n    it 'includes basic auth if user is nil' do\n      stub_request(:get, uri).with do |req|\n        expect(req.headers).to include('Authorization')\n      end\n\n      client.get(uri, options: {basic_auth: {user: nil, password: 'pass'}})\n    end\n\n    it 'includes basic auth if password is nil' do\n      stub_request(:get, uri).with do |req|\n        expect(req.headers).to include('Authorization')\n      end\n\n      client.get(uri, options: {basic_auth: {user: 'user', password: nil}})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/factory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Factory do\n  before(:all) do\n    ENV['http_proxy'] = nil\n    ENV['HTTP_PROXY'] = nil\n  end\n\n  let(:site) { Puppet::HTTP::Site.new('https', 'www.example.com', 443) }\n\n  def create_connection(site)\n    factory = described_class.new\n    factory.create_connection(site)\n  end\n\n  it 'creates a connection for the site' do\n    conn = create_connection(site)\n\n    expect(conn.use_ssl?).to be_truthy\n    expect(conn.address).to eq(site.host)\n    expect(conn.port).to eq(site.port)\n  end\n\n  it 'creates a connection that has not yet been started' do\n    conn = create_connection(site)\n\n    expect(conn).to_not be_started\n  end\n\n  it 'creates a connection supporting at least HTTP 1.1' do\n    conn = create_connection(site)\n\n    expect(conn.class.version_1_1? || conn.class.version_1_2?).to be_truthy\n  end\n\n  context \"proxy settings\" do\n    let(:proxy_host) { 'myhost' }\n    let(:proxy_port) { 432 }\n    let(:proxy_user) { 'mo' }\n    let(:proxy_pass) { 'password' }\n\n    it \"should not set a proxy if the http_proxy_host setting is 'none'\" do\n      Puppet[:http_proxy_host] = 'none'\n      conn = create_connection(site)\n\n      expect(conn.proxy_address).to be_nil\n    end\n\n    it 'should not set a proxy if a no_proxy env var matches the destination' do\n      Puppet[:http_proxy_host] = proxy_host\n      Puppet[:http_proxy_port] = proxy_port\n      Puppet::Util.withenv('NO_PROXY' => site.host) do\n        conn = create_connection(site)\n\n        expect(conn.proxy_address).to be_nil\n        expect(conn.proxy_port).to be_nil\n      end\n    end\n\n    it 'should not set a proxy if the no_proxy setting matches the destination' do\n      Puppet[:http_proxy_host] = proxy_host\n      Puppet[:http_proxy_port] = proxy_port\n      Puppet[:no_proxy] = site.host\n      conn = create_connection(site)\n\n      expect(conn.proxy_address).to be_nil\n      expect(conn.proxy_port).to be_nil\n    end\n\n    it 'sets proxy_address' do\n      Puppet[:http_proxy_host] = proxy_host\n      conn = create_connection(site)\n\n      expect(conn.proxy_address).to eq(proxy_host)\n    end\n\n    it 'sets proxy address and port' do\n      Puppet[:http_proxy_host] = proxy_host\n      Puppet[:http_proxy_port] = proxy_port\n      conn = create_connection(site)\n\n      expect(conn.proxy_port).to eq(proxy_port)\n    end\n\n    it 'sets proxy user and password' do\n      Puppet[:http_proxy_host] = proxy_host\n      Puppet[:http_proxy_port] = proxy_port\n      Puppet[:http_proxy_user] = proxy_user\n      Puppet[:http_proxy_password] = proxy_pass\n\n      conn = create_connection(site)\n\n      expect(conn.proxy_user).to eq(proxy_user)\n      expect(conn.proxy_pass).to eq(proxy_pass)\n    end\n  end\n\n  context 'socket timeouts' do\n    it 'sets open timeout' do\n      Puppet[:http_connect_timeout] = \"10s\"\n      conn = create_connection(site)\n\n      expect(conn.open_timeout).to eq(10)\n    end\n\n    it 'sets read timeout' do\n      Puppet[:http_read_timeout] = \"2m\"\n      conn = create_connection(site)\n\n      expect(conn.read_timeout).to eq(120)\n    end\n  end\n\n  it \"disables ruby's http_keepalive_timeout\" do\n    conn = create_connection(site)\n\n    expect(conn.keep_alive_timeout).to eq(2147483647)\n  end\n\n  it \"disables ruby's max retry\" do\n    conn = create_connection(site)\n\n    expect(conn.max_retries).to eq(0)\n  end\n\n  context 'source address' do\n    it 'defaults to system-defined' do\n      conn = create_connection(site)\n\n      expect(conn.local_host).to be(nil)\n    end\n\n    it 'sets the local_host address' do\n      Puppet[:sourceaddress] = \"127.0.0.1\"\n      conn = create_connection(site)\n\n      expect(conn.local_host).to eq('127.0.0.1')\n    end\n  end\n\n  context 'tls' do\n    it \"sets the minimum version to TLS 1.0\" do\n      conn = create_connection(site)\n      expect(conn.min_version).to eq(OpenSSL::SSL::TLS1_VERSION)\n    end\n\n    it \"defaults to ciphersuites providing 128 bits of security or greater\" do\n      conn = create_connection(site)\n      expect(conn.ciphers).to eq(\"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256\")\n    end\n\n    it \"can be restricted to TLSv1.3 ciphers\" do\n      tls13_ciphers = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\"\n      Puppet[:ciphers] = tls13_ciphers\n      conn = create_connection(site)\n      expect(conn.ciphers).to eq(tls13_ciphers)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/pool_entry_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::PoolEntry do\n  let(:connection) { double('connection') }\n  let(:verifier) { double('verifier') }\n\n  def create_session(connection, expiration_time = nil)\n    expiration_time ||= Time.now + 60 * 60\n\n    described_class.new(connection, verifier, expiration_time)\n  end\n\n  it 'provides access to its connection' do\n    session = create_session(connection)\n\n    expect(session.connection).to eq(connection)\n  end\n\n  it 'provides access to its verifier' do\n    session = create_session(connection)\n\n    expect(session.verifier).to eq(verifier)\n  end\n\n  it 'expires a connection whose expiration time is in the past' do\n    now = Time.now\n    past = now - 1\n\n    session = create_session(connection, past)\n    expect(session.expired?(now)).to be_truthy\n  end\n\n  it 'expires a connection whose expiration time is now' do\n    now = Time.now\n\n    session = create_session(connection, now)\n    expect(session.expired?(now)).to be_truthy\n  end\n\n  it 'does not expire a connection whose expiration time is in the future' do\n    now = Time.now\n    future = now + 1\n\n    session = create_session(connection, future)\n    expect(session.expired?(now)).to be_falsey\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/pool_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'openssl'\nrequire 'puppet/network/http'\nrequire 'puppet/network/http_pool'\n\ndescribe Puppet::HTTP::Pool do\n  let(:site) do\n    Puppet::HTTP::Site.new('https', 'rubygems.org', 443)\n  end\n\n  let(:different_site) do\n    Puppet::HTTP::Site.new('https', 'github.com', 443)\n  end\n\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:verifier) do\n    v = Puppet::SSL::Verifier.new(site.host, ssl_context)\n    allow(v).to receive(:setup_connection)\n    v\n  end\n\n  def create_pool\n    Puppet::HTTP::Pool.new(15)\n  end\n\n  def create_pool_with_connections(site, *connections)\n    pool = Puppet::HTTP::Pool.new(15)\n    connections.each do |conn|\n      pool.release(site, verifier, conn)\n    end\n    pool\n  end\n\n  def create_pool_with_http_connections(site, *connections)\n    pool = Puppet::HTTP::Pool.new(15)\n    connections.each do |conn|\n      pool.release(site, nil, conn)\n    end\n    pool\n  end\n\n  def create_pool_with_expired_connections(site, *connections)\n    # setting keepalive timeout to -1 ensures any newly added\n    # connections have already expired\n    pool = Puppet::HTTP::Pool.new(-1)\n    connections.each do |conn|\n      pool.release(site, verifier, conn)\n    end\n    pool\n  end\n\n  def create_connection(site)\n    double(site.addr, :started? => false, :start => nil, :finish => nil, :use_ssl? => true, :verify_mode => OpenSSL::SSL::VERIFY_PEER)\n  end\n\n  def create_http_connection(site)\n    double(site.addr, :started? => false, :start => nil, :finish => nil, :use_ssl? => false)\n  end\n\n  context 'when yielding a connection' do\n    it 'yields a connection' do\n      conn = create_connection(site)\n      pool = create_pool_with_connections(site, conn)\n\n      expect { |b|\n        pool.with_connection(site, verifier, &b)\n      }.to yield_with_args(conn)\n    end\n\n    it 'returns the connection to the pool' do\n      conn = create_connection(site)\n      expect(conn).to receive(:started?).and_return(true)\n\n      pool = create_pool\n      pool.release(site, verifier, conn)\n\n      pool.with_connection(site, verifier) { |c| }\n\n      expect(pool.pool[site].first.connection).to eq(conn)\n    end\n\n    it 'can yield multiple connections to the same site' do\n      lru_conn = create_connection(site)\n      mru_conn = create_connection(site)\n      pool = create_pool_with_connections(site, lru_conn, mru_conn)\n\n      pool.with_connection(site, verifier) do |a|\n        expect(a).to eq(mru_conn)\n\n        pool.with_connection(site, verifier) do |b|\n          expect(b).to eq(lru_conn)\n        end\n      end\n    end\n\n    it 'propagates exceptions' do\n      conn = create_connection(site)\n      pool = create_pool\n      pool.release(site, verifier, conn)\n\n      expect {\n        pool.with_connection(site, verifier) do |c|\n          raise IOError, 'connection reset'\n        end\n      }.to raise_error(IOError, 'connection reset')\n    end\n\n    it 'does not re-cache connections when an error occurs' do\n      # we're not distinguishing between network errors that would\n      # suggest we close the socket, and other errors\n      conn = create_connection(site)\n      pool = create_pool\n      pool.release(site, verifier, conn)\n\n      expect(pool).not_to receive(:release).with(site, verifier, conn)\n\n      pool.with_connection(site, verifier) do |c|\n        raise IOError, 'connection reset'\n      end rescue nil\n    end\n\n    it 'sets keepalive bit on network socket' do\n      pool = create_pool\n      s = Socket.new(Socket::PF_INET, Socket::SOCK_STREAM)\n      pool.setsockopts(Net::BufferedIO.new(s))\n\n      # On windows, Socket.getsockopt() doesn't return exactly the same data\n      # as an equivalent Socket::Option.new() statement, so we strip off the\n      # unrelevant bits only on this platform.\n      #\n      # To make sure we're not voiding the test case by doing this, we check\n      # both with and without the keepalive bit set.\n      #\n      # This workaround can be removed once all the ruby versions we care about\n      # have the patch from https://bugs.ruby-lang.org/issues/11958 applied.\n      #\n      if Puppet::Util::Platform.windows?\n        keepalive   = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true).data[0]\n        nokeepalive = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false).data[0]\n        expect(s.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).data).to eq(keepalive)\n        expect(s.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).data).to_not eq(nokeepalive)\n      else\n        expect(s.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool).to eq(true)\n      end\n    end\n\n    context 'when releasing connections' do\n      it 'releases HTTP connections' do\n        conn = create_connection(site)\n        expect(conn).to receive(:use_ssl?).and_return(false)\n        expect(conn).to receive(:started?).and_return(true)\n\n        pool = create_pool_with_connections(site, conn)\n        expect(pool).to receive(:release).with(site, verifier, conn)\n\n        pool.with_connection(site, verifier) {|c| }\n      end\n\n      it 'releases secure HTTPS connections' do\n        conn = create_connection(site)\n        expect(conn).to receive(:use_ssl?).and_return(true)\n        expect(conn).to receive(:verify_mode).and_return(OpenSSL::SSL::VERIFY_PEER)\n        expect(conn).to receive(:started?).and_return(true)\n\n        pool = create_pool_with_connections(site, conn)\n        expect(pool).to receive(:release).with(site, verifier, conn)\n\n        pool.with_connection(site, verifier) {|c| }\n      end\n\n      it 'closes insecure HTTPS connections' do\n        conn = create_connection(site)\n        expect(conn).to receive(:use_ssl?).and_return(true)\n        expect(conn).to receive(:verify_mode).and_return(OpenSSL::SSL::VERIFY_NONE)\n\n        pool = create_pool_with_connections(site, conn)\n\n        expect(pool).not_to receive(:release).with(site, verifier, conn)\n\n        pool.with_connection(site, verifier) {|c| }\n      end\n\n      it \"doesn't add a closed connection back to the pool\" do\n        http = Net::HTTP.new(site.addr)\n        http.use_ssl = true\n        http.verify_mode = OpenSSL::SSL::VERIFY_PEER\n        http.start\n\n        pool = create_pool_with_connections(site, http)\n\n        pool.with_connection(site, verifier) {|c| c.finish}\n\n        expect(pool.pool[site]).to be_nil\n      end\n    end\n  end\n\n  context 'when borrowing' do\n    it 'returns a new connection if the pool is empty' do\n      conn = create_connection(site)\n      pool = create_pool\n      expect(pool.factory).to receive(:create_connection).with(site).and_return(conn)\n\n      expect(pool.borrow(site, verifier)).to eq(conn)\n    end\n\n    it 'returns a matching connection' do\n      conn = create_connection(site)\n      pool = create_pool_with_connections(site, conn)\n\n      expect(pool.factory).not_to receive(:create_connection)\n\n      expect(pool.borrow(site, verifier)).to eq(conn)\n    end\n\n    it 'returns a new connection if there are no matching sites' do\n      different_conn = create_connection(different_site)\n      pool = create_pool_with_connections(different_site, different_conn)\n\n      conn = create_connection(site)\n      expect(pool.factory).to receive(:create_connection).with(site).and_return(conn)\n\n      expect(pool.borrow(site, verifier)).to eq(conn)\n    end\n\n    it 'returns a new HTTP connection if the cached connection is HTTPS' do\n      https_site = Puppet::HTTP::Site.new('https', 'www.example.com', 443)\n      old_conn = create_connection(https_site)\n      pool = create_pool_with_connections(https_site, old_conn)\n\n      http_site = Puppet::HTTP::Site.new('http', 'www.example.com', 443)\n      new_conn = create_http_connection(http_site)\n      allow(pool.factory).to receive(:create_connection).with(http_site).and_return(new_conn)\n\n      expect(pool.borrow(http_site, nil)).to eq(new_conn)\n    end\n\n    it 'returns a new HTTPS connection if the cached connection is HTTP' do\n      http_site = Puppet::HTTP::Site.new('http', 'www.example.com', 443)\n      old_conn = create_http_connection(http_site)\n      pool = create_pool_with_http_connections(http_site, old_conn)\n\n      https_site = Puppet::HTTP::Site.new('https', 'www.example.com', 443)\n      new_conn = create_connection(https_site)\n      allow(pool.factory).to receive(:create_connection).with(https_site).and_return(new_conn)\n\n      expect(pool.borrow(https_site, verifier)).to eq(new_conn)\n    end\n\n    it 'returns a new connection if the ssl contexts are different' do\n      old_conn = create_connection(site)\n      pool = create_pool_with_connections(site, old_conn)\n\n      new_conn = create_connection(site)\n      allow(pool.factory).to receive(:create_connection).with(site).and_return(new_conn)\n\n      new_verifier = Puppet::SSL::Verifier.new(site.host, Puppet::SSL::SSLContext.new)\n      allow(new_verifier).to receive(:setup_connection)\n\n      # 'equal' tests that it's the same object\n      expect(pool.borrow(site, new_verifier)).to eq(new_conn)\n    end\n\n    it 'returns a cached connection if the ssl contexts are the same' do\n      old_conn = create_connection(site)\n      pool = create_pool_with_connections(site, old_conn)\n\n      expect(pool.factory).not_to receive(:create_connection)\n\n      # 'equal' tests that it's the same object\n      new_verifier = Puppet::SSL::Verifier.new(site.host, ssl_context)\n      expect(pool.borrow(site, new_verifier)).to equal(old_conn)\n    end\n\n    it 'returns a cached connection if both connections are http' do\n      http_site = Puppet::HTTP::Site.new('http', 'www.example.com', 80)\n      old_conn = create_http_connection(http_site)\n      pool = create_pool_with_http_connections(http_site, old_conn)\n\n      # 'equal' tests that it's the same object\n      expect(pool.borrow(http_site, nil)).to equal(old_conn)\n    end\n\n    it 'returns started connections' do\n      conn = create_connection(site)\n      expect(conn).to receive(:start)\n\n      pool = create_pool\n      expect(pool.factory).to receive(:create_connection).with(site).and_return(conn)\n\n      expect(pool.borrow(site, verifier)).to eq(conn)\n    end\n\n    it \"doesn't start a cached connection\" do\n      conn = create_connection(site)\n      expect(conn).not_to receive(:start)\n\n      pool = create_pool_with_connections(site, conn)\n      pool.borrow(site, verifier)\n    end\n\n    it 'returns the most recently used connection from the pool' do\n      least_recently_used = create_connection(site)\n      most_recently_used = create_connection(site)\n\n      pool = create_pool_with_connections(site, least_recently_used, most_recently_used)\n      expect(pool.borrow(site, verifier)).to eq(most_recently_used)\n    end\n\n    it 'finishes expired connections' do\n      conn = create_connection(site)\n      allow(conn).to receive(:started?).and_return(true)\n      expect(conn).to receive(:finish)\n\n      pool = create_pool_with_expired_connections(site, conn)\n      expect(pool.factory).to receive(:create_connection).and_return(double('conn', :start => nil, :use_ssl? => true))\n\n      pool.borrow(site, verifier)\n    end\n\n    it 'logs an exception if it fails to close an expired connection' do\n      expect(Puppet).to receive(:log_exception).with(be_a(IOError), \"Failed to close connection for #{site}: read timeout\")\n\n      conn = create_connection(site)\n      expect(conn).to receive(:started?).and_return(true)\n      expect(conn).to receive(:finish).and_raise(IOError, 'read timeout')\n\n      pool = create_pool_with_expired_connections(site, conn)\n      expect(pool.factory).to receive(:create_connection).and_return(double('open_conn', :start => nil, :use_ssl? => true))\n\n      pool.borrow(site, verifier)\n    end\n\n    it 'deletes the session when the last connection is borrowed' do\n      conn = create_connection(site)\n      pool = create_pool_with_connections(site, conn)\n      pool.borrow(site, verifier)\n\n      expect(pool.pool[site]).to be_nil\n    end\n  end\n\n  context 'when releasing a connection' do\n    it 'adds the connection to an empty pool' do\n      conn = create_connection(site)\n\n      pool = create_pool\n      pool.release(site, verifier, conn)\n\n      expect(pool.pool[site].first.connection).to eq(conn)\n    end\n\n    it 'adds the connection to a pool with a connection for the same site' do\n      pool = create_pool\n      pool.release(site, verifier, create_connection(site))\n      pool.release(site, verifier, create_connection(site))\n\n      expect(pool.pool[site].count).to eq(2)\n    end\n\n    it 'adds the connection to a pool with a connection for a different site' do\n      pool = create_pool\n      pool.release(site, verifier, create_connection(site))\n      pool.release(different_site, verifier, create_connection(different_site))\n\n      expect(pool.pool[site].count).to eq(1)\n      expect(pool.pool[different_site].count).to eq(1)\n    end\n  end\n\n  context 'when closing' do\n    it 'clears the pool' do\n      pool = create_pool\n      pool.close\n\n      expect(pool.pool).to be_empty\n    end\n\n    it 'closes all cached connections' do\n      conn = create_connection(site)\n      allow(conn).to receive(:started?).and_return(true)\n      expect(conn).to receive(:finish)\n\n      pool = create_pool_with_connections(site, conn)\n      pool.close\n    end\n\n    it 'allows a connection to be closed multiple times safely' do\n      http = Net::HTTP.new(site.addr)\n      http.use_ssl = true\n      http.verify_mode = OpenSSL::SSL::VERIFY_PEER\n      http.start\n\n      pool = create_pool\n\n      expect(pool.close_connection(site, http)).to eq(true)\n      expect(pool.close_connection(site, http)).to eq(false)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/proxy_spec.rb",
    "content": "require 'uri'\nrequire 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Proxy do\n  before(:all) do\n    ENV['http_proxy'] = nil\n    ENV['HTTP_PROXY'] = nil\n  end\n\n  host, port, user, password = 'some.host', 1234, 'user1', 'pAssw0rd'\n\n  def expects_direct_connection_to(http, www)\n    expect(http.address).to eq(www.host)\n    expect(http.port).to eq(www.port)\n\n    expect(http.proxy_address).to be_nil\n    expect(http.proxy_port).to be_nil\n    expect(http.proxy_user).to be_nil\n    expect(http.proxy_pass).to be_nil\n  end\n\n  def expects_proxy_connection_via(http, www, host, port, user, password)\n    expect(http.address).to eq(www.host)\n    expect(http.port).to eq(www.port)\n\n    expect(http.proxy_address).to eq(host)\n    expect(http.proxy_port).to eq(port)\n    expect(http.proxy_user).to eq(user)\n    expect(http.proxy_pass).to eq(password)\n  end\n\n  describe '.proxy' do\n    let(:www) { URI::HTTP.build(host: 'www.example.com', port: 80) }\n\n    it 'uses a proxy' do\n      Puppet[:http_proxy_host] = host\n      Puppet[:http_proxy_port] = port\n      Puppet[:http_proxy_user] = user\n      Puppet[:http_proxy_password] = password\n\n      http = subject.proxy(www)\n      expects_proxy_connection_via(http, www, host, port, user, password)\n    end\n\n    it 'connects directly to the server' do\n      http = subject.proxy(www)\n      expects_direct_connection_to(http, www)\n    end\n\n    it 'connects directly to the server when HTTP_PROXY environment variable is set, but server matches no_proxy setting' do\n      Puppet[:http_proxy_host] = host\n      Puppet[:http_proxy_port] = port\n      Puppet[:no_proxy] = www.host\n\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{host}:#{port}\") do\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n    end\n\n    context 'when setting no_proxy' do\n      before :each do\n        Puppet[:http_proxy_host] = host\n        Puppet[:http_proxy_port] = port\n      end\n\n      it 'connects directly to the server when HTTP_PROXY environment variable is set, but server matches no_proxy setting' do\n        Puppet[:no_proxy] = www.host\n\n        Puppet::Util.withenv('HTTP_PROXY' => \"http://#{host}:#{port}\") do\n          http = subject.proxy(www)\n          expects_direct_connection_to(http, www)\n        end\n      end\n\n      it 'connects directly to the server when no_proxy matches wildcard domain' do\n        Puppet[:no_proxy] = '*.example.com'\n\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n\n      it 'connects directly to the server when no_proxy matches dotted domain' do\n        Puppet[:no_proxy] = '.example.com'\n\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n\n      it 'connects directly to the server when no_proxy matches a domain suffix like ruby does' do\n        Puppet[:no_proxy] = 'example.com'\n\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n\n      it 'connects directly to the server when no_proxy matches a partial suffix like ruby does' do\n        Puppet[:no_proxy] = 'ample.com'\n\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n\n      it 'connects directly to the server when it is a subdomain of no_proxy' do\n        Puppet[:no_proxy] = '*.com'\n\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n\n      it 'connects directly to the server when no_proxy is *' do\n        Puppet[:no_proxy] = '*'\n\n        http = subject.proxy(www)\n        expects_direct_connection_to(http, www)\n      end\n    end\n  end\n\n  describe \".http_proxy_env\" do\n    it \"should return nil if no environment variables\" do\n      expect(subject.http_proxy_env).to eq(nil)\n    end\n\n    it \"should return a URI::HTTP object if http_proxy env variable is set\" do\n      Puppet::Util.withenv('HTTP_PROXY' => host) do\n        expect(subject.http_proxy_env).to eq(URI.parse(host))\n      end\n    end\n\n    it \"should return a URI::HTTP object if HTTP_PROXY env variable is set\" do\n      Puppet::Util.withenv('HTTP_PROXY' => host) do\n        expect(subject.http_proxy_env).to eq(URI.parse(host))\n      end\n    end\n\n    it \"should return a URI::HTTP object with .host and .port if URI is given\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{host}:#{port}\") do\n        expect(subject.http_proxy_env).to eq(URI.parse(\"http://#{host}:#{port}\"))\n      end\n    end\n\n    it \"should return nil if proxy variable is malformed\" do\n      Puppet::Util.withenv('HTTP_PROXY' => 'this is not a valid URI') do\n        expect(subject.http_proxy_env).to eq(nil)\n      end\n    end\n  end\n\n  describe \".http_proxy_host\" do\n    it \"should return nil if no proxy host in config or env\" do\n      expect(subject.http_proxy_host).to eq(nil)\n    end\n\n    it \"should return a proxy host if set in config\" do\n      Puppet.settings[:http_proxy_host] = host\n      expect(subject.http_proxy_host).to eq(host)\n    end\n\n    it \"should return nil if set to `none` in config\" do\n      Puppet.settings[:http_proxy_host] = 'none'\n      expect(subject.http_proxy_host).to eq(nil)\n    end\n\n    it \"uses environment variable before puppet settings\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{host}:#{port}\") do\n        Puppet.settings[:http_proxy_host] = 'not.correct'\n        expect(subject.http_proxy_host).to eq(host)\n      end\n    end\n  end\n\n  describe \".http_proxy_port\" do\n    it \"should return a proxy port if set in environment\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{host}:#{port}\") do\n        expect(subject.http_proxy_port).to eq(port)\n      end\n    end\n\n    it \"should return a proxy port if set in config\" do\n      Puppet.settings[:http_proxy_port] = port\n      expect(subject.http_proxy_port).to eq(port)\n    end\n\n    it \"uses environment variable before puppet settings\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{host}:#{port}\") do\n        Puppet.settings[:http_proxy_port] = 7456\n        expect(subject.http_proxy_port).to eq(port)\n      end\n    end\n\n  end\n\n  describe \".http_proxy_user\" do\n    it \"should return a proxy user if set in environment\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{user}:#{password}@#{host}:#{port}\") do\n        expect(subject.http_proxy_user).to eq(user)\n      end\n    end\n\n    it \"should return a proxy user if set in config\" do\n      Puppet.settings[:http_proxy_user] = user\n      expect(subject.http_proxy_user).to eq(user)\n    end\n\n    it \"should use environment variable before puppet settings\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{user}:#{password}@#{host}:#{port}\") do\n        Puppet.settings[:http_proxy_user] = 'clownpants'\n        expect(subject.http_proxy_user).to eq(user)\n      end\n    end\n\n  end\n\n  describe \".http_proxy_password\" do\n    it \"should return a proxy password if set in environment\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{user}:#{password}@#{host}:#{port}\") do\n        expect(subject.http_proxy_password).to eq(password)\n      end\n    end\n\n    it \"should return a proxy password if set in config\" do\n      Puppet.settings[:http_proxy_user] = user\n      Puppet.settings[:http_proxy_password] = password\n      expect(subject.http_proxy_password).to eq(password)\n    end\n\n    it \"should use environment variable before puppet settings\" do\n      Puppet::Util.withenv('HTTP_PROXY' => \"http://#{user}:#{password}@#{host}:#{port}\") do\n        Puppet.settings[:http_proxy_password] = 'clownpants'\n        expect(subject.http_proxy_password).to eq(password)\n      end\n    end\n\n  end\n\n  describe \".no_proxy\" do\n    no_proxy = '127.0.0.1, localhost'\n    it \"should use a no_proxy list if set in environment\" do\n      Puppet::Util.withenv('NO_PROXY' => no_proxy) do\n        expect(subject.no_proxy).to eq(no_proxy)\n      end\n    end\n\n    it \"should use a no_proxy list if set in config\" do\n      Puppet.settings[:no_proxy] = no_proxy\n      expect(subject.no_proxy).to eq(no_proxy)\n    end\n\n    it \"should use environment variable before puppet settings\" do\n      no_proxy_puppet_setting = '10.0.0.1, localhost'\n      Puppet::Util.withenv('NO_PROXY' => no_proxy) do\n        Puppet.settings[:no_proxy] = no_proxy_puppet_setting\n        expect(subject.no_proxy).to eq(no_proxy)\n      end\n    end\n  end\n\n  describe \".no_proxy?\" do\n    no_proxy = '127.0.0.1, localhost, mydomain.com, *.otherdomain.com, oddport.com:8080, *.otheroddport.com:8080, .anotherdomain.com, .anotheroddport.com:8080'\n\n    it \"should return false if no_proxy does not exist in environment or puppet settings\" do\n      Puppet::Util.withenv('no_proxy' => nil) do\n        dest = 'https://puppetlabs.com'\n        expect(subject.no_proxy?(dest)).to be false\n      end\n    end\n\n    it \"should return false if the dest does not match any element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'https://puppetlabs.com'\n        expect(subject.no_proxy?(dest)).to be false\n      end\n    end\n\n    it \"should return true if the dest as an IP does match any element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://127.0.0.1'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return true if the dest as single word does match any element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://localhost'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return true if the dest as standard domain word does match any element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://mydomain.com'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return true if the dest as standard domain with port does match any element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://oddport.com:8080'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return false if the dest is standard domain not matching port\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://oddport.com'\n        expect(subject.no_proxy?(dest)).to be false\n      end\n    end\n\n    it \"should return true if the dest does match any wildcarded element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://sub.otherdomain.com'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return true if the dest does match any wildcarded element with port in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://sub.otheroddport.com:8080'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return true if the dest does match any domain level (no wildcard) element in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://sub.anotherdomain.com'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should return true if the dest does match any domain level (no wildcard) element with port in the no_proxy list\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = 'http://sub.anotheroddport.com:8080'\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n\n    it \"should work if passed a URI object\" do\n      Puppet::Util.withenv('no_proxy' => no_proxy) do\n        dest = URI.parse('http://sub.otheroddport.com:8080')\n        expect(subject.no_proxy?(dest)).to be true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/resolver_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Resolver do\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:session) { client.create_session }\n  let(:uri) { URI.parse('https://www.example.com') }\n\n  context 'when resolving using settings' do\n    let(:subject) { Puppet::HTTP::Resolver::Settings.new(client) }\n\n    it 'returns a service based on the current ca_server and ca_port settings' do\n      Puppet[:ca_server] = 'ca.example.com'\n      Puppet[:ca_port] = 8141\n\n      service = subject.resolve(session, :ca)\n      expect(service).to be_an_instance_of(Puppet::HTTP::Service::Ca)\n      expect(service.url.to_s).to eq(\"https://ca.example.com:8141/puppet-ca/v1\")\n    end\n  end\n\n  context 'when resolving using server_list' do\n    let(:subject) { Puppet::HTTP::Resolver::ServerList.new(client, server_list_setting: Puppet.settings.setting(:server_list), default_port: 8142, services: Puppet::HTTP::Service::SERVICE_NAMES) }\n\n    before :each do\n      Puppet[:server_list] = 'ca.example.com:8141,apple.example.com'\n    end\n\n    it 'returns a service based on the current server_list setting' do\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: 200)\n\n      service = subject.resolve(session, :ca)\n      expect(service).to be_an_instance_of(Puppet::HTTP::Service::Ca)\n      expect(service.url.to_s).to eq(\"https://ca.example.com:8141/puppet-ca/v1\")\n    end\n\n    it 'returns a service based on the current server_list setting if the server returns any success codes' do\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: 202)\n\n      service = subject.resolve(session, :ca)\n      expect(service).to be_an_instance_of(Puppet::HTTP::Service::Ca)\n      expect(service.url.to_s).to eq(\"https://ca.example.com:8141/puppet-ca/v1\")\n    end\n\n    it 'includes extra http headers' do\n      Puppet[:http_extra_headers] = 'region:us-west'\n\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\")\n        .with(headers: {'Region' => 'us-west'})\n\n      subject.resolve(session, :ca)\n    end\n\n    it 'uses the provided ssl context during resolution' do\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: 200)\n\n      other_ctx = Puppet::SSL::SSLContext.new\n      expect(client).to receive(:connect).with(URI(\"https://ca.example.com:8141/status/v1/simple/server\"), options: {ssl_context: other_ctx}).and_call_original\n\n      subject.resolve(session, :ca, ssl_context: other_ctx)\n    end\n\n    it 'logs unsuccessful HTTP 500 responses' do\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: [500, 'Internal Server Error'])\n      stub_request(:get, \"https://apple.example.com:8142/status/v1/simple/server\").to_return(status: 200)\n\n      subject.resolve(session, :ca)\n\n      expect(@logs.map(&:message)).to include(/Puppet server ca.example.com:8141 is unavailable: 500 Internal Server Error/)\n    end\n\n    it 'cancels resolution if no servers in server_list are accessible' do\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: 503)\n      stub_request(:get, \"https://apple.example.com:8142/status/v1/simple/server\").to_return(status: 503)\n\n      canceled = false\n      canceled_handler = lambda { |cancel| canceled = cancel }\n\n      expect(subject.resolve(session, :ca, canceled_handler: canceled_handler)).to eq(nil)\n      expect(canceled).to eq(true)\n    end\n\n    it 'cycles through server_list until a valid server is found' do\n      stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: 503)\n      stub_request(:get, \"https://apple.example.com:8142/status/v1/simple/server\").to_return(status: 200)\n\n      service = subject.resolve(session, :ca)\n      expect(service).to be_an_instance_of(Puppet::HTTP::Service::Ca)\n      expect(service.url.to_s).to eq(\"https://apple.example.com:8142/puppet-ca/v1\")\n    end\n\n    it 'resolves once per session' do\n      failed = stub_request(:get, \"https://ca.example.com:8141/status/v1/simple/server\").to_return(status: 503)\n      passed = stub_request(:get, \"https://apple.example.com:8142/status/v1/simple/server\").to_return(status: 200)\n\n      service = subject.resolve(session, :puppet)\n      expect(service).to be_a(Puppet::HTTP::Service::Compiler)\n      expect(service.url.to_s).to eq(\"https://apple.example.com:8142/puppet/v3\")\n\n      service = subject.resolve(session, :fileserver)\n      expect(service).to be_a(Puppet::HTTP::Service::FileServer)\n      expect(service.url.to_s).to eq(\"https://apple.example.com:8142/puppet/v3\")\n\n      service = subject.resolve(session, :report)\n      expect(service).to be_a(Puppet::HTTP::Service::Report)\n      expect(service.url.to_s).to eq(\"https://apple.example.com:8142/puppet/v3\")\n\n      expect(failed).to have_been_requested\n      expect(passed).to have_been_requested\n    end\n  end\n\n  context 'when resolving using SRV' do\n    let(:dns) { double('dns') }\n    let(:subject) { Puppet::HTTP::Resolver::SRV.new(client, domain: 'example.com', dns: dns) }\n\n    def stub_srv(host, port)\n      srv = Resolv::DNS::Resource::IN::SRV.new(0, 0, port, host)\n      srv.instance_variable_set :@ttl, 3600\n\n      allow(dns).to receive(:getresources).with(\"_x-puppet-ca._tcp.example.com\", Resolv::DNS::Resource::IN::SRV).and_return([srv])\n    end\n\n    it 'returns a service based on an SRV record' do\n      stub_srv('ca1.example.com', 8142)\n\n      service = subject.resolve(session, :ca)\n      expect(service).to be_an_instance_of(Puppet::HTTP::Service::Ca)\n      expect(service.url.to_s).to eq(\"https://ca1.example.com:8142/puppet-ca/v1\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/response_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Response do\n  let(:uri) { URI.parse('https://www.example.com') }\n  let(:client) { Puppet::HTTP::Client.new }\n\n  it \"returns the request URL\" do\n    stub_request(:get, uri)\n\n    response = client.get(uri)\n    expect(response.url).to eq(uri)\n  end\n\n  it \"returns the HTTP code\" do\n    stub_request(:get, uri)\n\n    response = client.get(uri)\n    expect(response.code).to eq(200)\n  end\n\n  it \"returns the HTTP reason string\" do\n    stub_request(:get, uri).to_return(status: [418, \"I'm a teapot\"])\n\n    response = client.get(uri)\n    expect(response.reason).to eq(\"I'm a teapot\")\n  end\n\n  it \"returns the response body\" do\n    stub_request(:get, uri).to_return(status: 200, body: \"I'm the body\")\n\n    response = client.get(uri)\n    expect(response.body).to eq(\"I'm the body\")\n  end\n\n  it \"streams the response body\" do\n    stub_request(:get, uri).to_return(status: 200, body: \"I'm the streaming body\")\n\n    content = StringIO.new\n    client.get(uri) do |response|\n      response.read_body do |data|\n        content << data\n      end\n    end\n    expect(content.string).to eq(\"I'm the streaming body\")\n  end\n\n  it \"raises if a block isn't given when streaming\" do\n    stub_request(:get, uri).to_return(status: 200, body: \"\")\n\n    expect {\n      client.get(uri) do |response|\n        response.read_body\n      end\n    }.to raise_error(Puppet::HTTP::HTTPError, %r{Request to https://www.example.com failed after .* seconds: A block is required})\n  end\n\n  it \"returns success for all 2xx codes\" do\n    stub_request(:get, uri).to_return(status: 202)\n\n    expect(client.get(uri)).to be_success\n  end\n\n  it \"returns a header value\" do\n    stub_request(:get, uri).to_return(status: 200, headers: { 'Content-Encoding' => 'gzip' })\n\n    expect(client.get(uri)['Content-Encoding']).to eq('gzip')\n  end\n\n  it \"enumerates headers\" do\n    stub_request(:get, uri).to_return(status: 200, headers: { 'Content-Encoding' => 'gzip' })\n\n    expect(client.get(uri).each_header.to_a).to eq([['content-encoding', 'gzip']])\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/service/ca_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Service::Ca do\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:session) { Puppet::HTTP::Session.new(client, []) }\n  let(:subject) { client.create_session.route_to(:ca) }\n\n  before :each do\n    Puppet[:ca_server] = 'www.example.com'\n    Puppet[:ca_port] = 443\n  end\n\n  context 'when making requests' do\n    let(:uri) {\"https://www.example.com:443/puppet-ca/v1/certificate/ca\"}\n\n    it 'includes default HTTP headers' do\n      stub_request(:get, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end\n\n      subject.get_certificate('ca')\n    end\n  end\n\n  context 'when routing to the CA service' do\n    let(:cert) { cert_fixture('ca.pem') }\n    let(:pem) { cert.to_pem }\n\n    it 'defaults the server and port based on settings' do\n      Puppet[:ca_server] = 'ca.example.com'\n      Puppet[:ca_port] = 8141\n\n      stub_request(:get, \"https://ca.example.com:8141/puppet-ca/v1/certificate/ca\").to_return(body: pem)\n\n      subject.get_certificate('ca')\n    end\n\n    it 'fallbacks to server and serverport' do\n      Puppet[:ca_server] = nil\n      Puppet[:ca_port] = nil\n      Puppet[:server] = 'ca2.example.com'\n      Puppet[:serverport] = 8142\n\n      stub_request(:get, \"https://ca2.example.com:8142/puppet-ca/v1/certificate/ca\").to_return(body: pem)\n\n      subject.get_certificate('ca')\n    end\n  end\n\n  context 'when getting certificates' do\n    let(:cert) { cert_fixture('ca.pem') }\n    let(:pem) { cert.to_pem }\n    let(:url) { \"https://www.example.com/puppet-ca/v1/certificate/ca\" }\n\n    it 'includes headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:get, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.get_certificate('ca')\n    end\n\n    it 'gets a certificate from the \"certificate\" endpoint' do\n      stub_request(:get, url).to_return(body: pem)\n\n      _, body = subject.get_certificate('ca')\n      expect(body).to eq(pem)\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, url).to_return(body: pem)\n\n      resp, _ = subject.get_certificate('ca')\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'accepts text/plain responses' do\n      stub_request(:get, url).with(headers: {'Accept' => 'text/plain'})\n\n      subject.get_certificate('ca')\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:get, url).to_return(status: [404, 'Not Found'])\n\n      expect {\n        subject.get_certificate('ca')\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Not Found\")\n        expect(err.response.code).to eq(404)\n      end\n    end\n\n    it 'raises a 304 response error if it is unmodified' do\n      stub_request(:get, url).to_return(status: [304, 'Not Modified'])\n\n      expect {\n        subject.get_certificate('ca', if_modified_since: Time.now)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Not Modified\")\n        expect(err.response.code).to eq(304)\n      end\n    end\n  end\n\n  context 'when getting CRLs' do\n    let(:crl) { crl_fixture('crl.pem') }\n    let(:pem) { crl.to_pem }\n    let(:url) { \"https://www.example.com/puppet-ca/v1/certificate_revocation_list/ca\" }\n\n    it 'includes headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:get, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.get_certificate_revocation_list\n    end\n\n    it 'gets a CRL from \"certificate_revocation_list\" endpoint' do\n      stub_request(:get, url).to_return(body: pem)\n\n      _, body = subject.get_certificate_revocation_list\n      expect(body).to eq(pem)\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, url).to_return(body: pem)\n\n      resp, _ = subject.get_certificate_revocation_list\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'accepts text/plain responses' do\n      stub_request(:get, url).with(headers: {'Accept' => 'text/plain'})\n\n      subject.get_certificate_revocation_list\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:get, url).to_return(status: [404, 'Not Found'])\n\n      expect {\n        subject.get_certificate_revocation_list\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Not Found\")\n        expect(err.response.code).to eq(404)\n      end\n    end\n\n    it 'raises a 304 response error if it is unmodified' do\n      stub_request(:get, url).to_return(status: [304, 'Not Modified'])\n\n      expect {\n        subject.get_certificate_revocation_list(if_modified_since: Time.now)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Not Modified\")\n        expect(err.response.code).to eq(304)\n      end\n    end\n  end\n\n  context 'when submitting a CSR' do\n    let(:request) { request_fixture('request.pem') }\n    let(:pem) { request.to_pem }\n    let(:url) { \"https://www.example.com/puppet-ca/v1/certificate_request/infinity\" }\n\n    it 'includes headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:put, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.put_certificate_request('infinity', request)\n    end\n\n    it 'submits a CSR to the \"certificate_request\" endpoint' do\n      stub_request(:put, url).with(body: pem, headers: { 'Content-Type' => 'text/plain' })\n\n      subject.put_certificate_request('infinity', request)\n    end\n\n    it 'returns the request response' do\n      stub_request(:put, url).with(body: pem, headers: { 'Content-Type' => 'text/plain' })\n\n      resp = subject.put_certificate_request('infinity', request)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:put, url).to_return(status: [400, 'Bad Request'])\n\n      expect {\n        subject.put_certificate_request('infinity', request)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n  end\n\n  context 'when getting certificates' do\n    let(:cert) { cert_fixture('signed.pem') }\n    let(:pem) { cert.to_pem }\n    let(:url) { \"https://www.example.com/puppet-ca/v1/certificate_renewal\" }\n    let(:cert_context) { Puppet::SSL::SSLContext.new(client_cert: pem) }\n    let(:client) { Puppet::HTTP::Client.new(ssl_context: cert_context) }\n    let(:session) { Puppet::HTTP::Session.new(client, []) }\n    let(:subject) { client.create_session.route_to(:ca) }\n\n    it \"gets a certificate from the 'certificate_renewal' endpoint\" do\n      stub_request(:post, url).to_return(body: pem)\n\n      _, body = subject.post_certificate_renewal(cert_context)\n      expect(body).to eq(pem)\n    end\n\n    it 'returns the request response' do\n      stub_request(:post, url).to_return(body: 'pem')\n\n      resp, _ = subject.post_certificate_renewal(cert_context)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'accepts text/plain responses' do\n      stub_request(:post, url).with(headers: {'Accept' => 'text/plain'})\n\n      subject.post_certificate_renewal(cert_context)\n    end\n\n    it 'raises an ArgumentError if the SSL context does not contain a client cert' do\n      stub_request(:post, url)\n      expect { subject.post_certificate_renewal(ssl_context) }.to raise_error(ArgumentError, 'SSL context must contain a client certificate.')\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:post, url).to_return(status: [400, 'Bad Request'])\n\n      expect {\n        subject.post_certificate_renewal(cert_context)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:post, url).to_return(status: [404, 'Not Found'])\n\n      expect {\n        subject.post_certificate_renewal(cert_context)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Not Found\")\n        expect(err.response.code).to eq(404)\n      end\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:post, url).to_return(status: [404, 'Forbidden'])\n\n      expect {\n        subject.post_certificate_renewal(cert_context)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Forbidden\")\n        expect(err.response.code).to eq(404)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/service/compiler_spec.rb",
    "content": "\n# coding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Service::Compiler do\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:subject) { client.create_session.route_to(:puppet) }\n  let(:environment) { 'testing' }\n  let(:certname) { 'ziggy' }\n  let(:node) { Puppet::Node.new(certname) }\n  let(:facts) { Puppet::Node::Facts.new(certname) }\n  let(:catalog) { Puppet::Resource::Catalog.new(certname) }\n  let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n\n  before :each do\n    Puppet[:server] = 'compiler.example.com'\n    Puppet[:serverport] = 8140\n\n    Puppet::Node::Facts.indirection.terminus_class = :memory\n  end\n\n  context 'when making requests' do\n    let(:uri) {\"https://compiler.example.com:8140/puppet/v3/catalog/ziggy?environment=testing\"}\n\n    it 'includes default HTTP headers' do\n      stub_request(:post, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end.to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n  end\n\n  context 'when routing to the compiler service' do\n    it 'defaults the server and port based on settings' do\n      Puppet[:server] = 'compiler2.example.com'\n      Puppet[:serverport] = 8141\n\n      stub_request(:post, \"https://compiler2.example.com:8141/puppet/v3/catalog/ziggy?environment=testing\")\n        .to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n  end\n\n  context 'when posting for a catalog' do\n    let(:uri) { %r{/puppet/v3/catalog/ziggy} }\n    let(:catalog_response) { { body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime } } }\n\n    it 'includes puppet headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:post, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).\n        to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n\n    it 'submits facts as application/json by default' do\n      stub_request(:post, uri)\n        .with(body: hash_including(\"facts_format\" => /application\\/json/))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n\n    it 'submits facts as pson if set as the preferred format', if: Puppet.features.pson? do\n      Puppet[:preferred_serialization_format] = \"pson\"\n\n      stub_request(:post, uri)\n        .with(body: hash_including(\"facts_format\" => /pson/))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n\n    it 'includes environment as a query parameter AND in the POST body' do\n      stub_request(:post, uri)\n        .with(query: {\"environment\" => \"outerspace\"},\n              body: hash_including(\"environment\" => 'outerspace'))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'outerspace', facts: facts)\n    end\n\n    it 'includes configured_environment' do\n      stub_request(:post, uri)\n        .with(body: hash_including(\"configured_environment\" => 'agent_specified'))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'production', facts: facts, configured_environment: 'agent_specified')\n    end\n\n    it 'includes check_environment' do\n      stub_request(:post, uri)\n        .with(body: hash_including('check_environment' => 'false'))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'production', facts: facts)\n    end\n\n    it 'includes transaction_uuid' do\n      uuid = \"ec3d2844-b236-4287-b0ad-632fbb4d1ff0\"\n\n      stub_request(:post, uri)\n        .with(body: hash_including(\"transaction_uuid\" => uuid))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'production', facts: facts, transaction_uuid: uuid)\n    end\n\n    it 'includes job_uuid' do\n      uuid = \"3dd13eec-1b6b-4b5d-867b-148193e0593e\"\n\n      stub_request(:post, uri)\n        .with(body: hash_including(\"job_uuid\" => uuid))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'production', facts: facts, job_uuid: uuid)\n    end\n\n    it 'includes static_catalog' do\n      stub_request(:post, uri)\n        .with(body: hash_including(\"static_catalog\" => \"false\"))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'production', facts: facts, static_catalog: false)\n    end\n\n    it 'includes dot-separated list of checksum_types' do\n      stub_request(:post, uri)\n        .with(body: hash_including(\"checksum_type\" => \"sha256.sha384\"))\n        .to_return(**catalog_response)\n\n      subject.post_catalog(certname, environment: 'production', facts: facts, checksum_type: %w[sha256 sha384])\n    end\n\n    it 'does not accept msgpack by default' do\n      stub_request(:post, uri)\n        .with(headers: {'Accept' => 'application/vnd.puppet.rich+json, application/json'})\n        .to_return(**catalog_response)\n\n      allow(Puppet.features).to receive(:msgpack?).and_return(false)\n      allow(Puppet.features).to receive(:pson?).and_return(false)\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n\n    it 'accepts msgpack & rich_json_msgpack if the gem is present' do\n      stub_request(:post, uri)\n        .with(headers: {'Accept' => 'application/vnd.puppet.rich+json, application/json, application/vnd.puppet.rich+msgpack, application/x-msgpack'})\n        .to_return(**catalog_response)\n\n      allow(Puppet.features).to receive(:msgpack?).and_return(true)\n      allow(Puppet.features).to receive(:pson?).and_return(false)\n\n      subject.post_catalog(certname, environment: environment, facts: facts)\n    end\n\n    it 'returns a deserialized catalog' do\n      stub_request(:post, uri)\n        .to_return(**catalog_response)\n\n      _, cat = subject.post_catalog(certname, environment: 'production', facts: facts)\n      expect(cat).to be_a(Puppet::Resource::Catalog)\n      expect(cat.name).to eq(certname)\n    end\n\n    it 'deserializes the catalog from msgpack', if: Puppet.features.msgpack? do\n      body = catalog.to_msgpack\n      formatter = Puppet::Network::FormatHandler.format(:msgpack)\n      catalog_response = { body: body, headers: {'Content-Type' => formatter.mime }}\n\n      stub_request(:post, uri)\n        .to_return(**catalog_response)\n\n      _, cat = subject.post_catalog(certname, environment: 'production', facts: facts)\n      expect(cat).to be_a(Puppet::Resource::Catalog)\n      expect(cat.name).to eq(certname)\n    end\n\n    it 'deserializes the catalog from rich msgpack', if: Puppet.features.msgpack? do\n      body = Puppet.override(rich_data: true) do\n        catalog.to_msgpack\n      end\n\n      formatter = Puppet::Network::FormatHandler.format(:rich_data_msgpack)\n      catalog_response = { body: body, headers: {'Content-Type' => formatter.mime }}\n\n      stub_request(:post, uri)\n        .to_return(**catalog_response)\n\n      _, cat = subject.post_catalog(certname, environment: 'production', facts: facts)\n      expect(cat).to be_a(Puppet::Resource::Catalog)\n      expect(cat.name).to eq(certname)\n    end\n\n    it 'returns the request response' do\n      stub_request(:post, uri)\n        .to_return(**catalog_response)\n\n      resp, _ = subject.post_catalog(certname, environment: 'production', facts: facts)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:post, uri)\n        .to_return(status: [500, \"Server Error\"])\n\n      expect {\n        subject.post_catalog(certname, environment: 'production', facts: facts)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Server Error')\n        expect(err.response.code).to eq(500)\n      end\n    end\n\n    it 'raises a protocol error if the content-type header is missing' do\n      stub_request(:post, uri)\n        .to_return(body: \"content-type is missing\")\n\n      expect {\n        subject.post_catalog(certname, environment: 'production', facts: facts)\n      }.to raise_error(Puppet::HTTP::ProtocolError, /No content type in http response; cannot parse/)\n    end\n\n    it 'raises a serialization error if the content is invalid' do\n      stub_request(:post, uri)\n        .to_return(body: \"this isn't valid JSON\", headers: {'Content-Type' => 'application/json'})\n\n      expect {\n        subject.post_catalog(certname, environment: 'production', facts: facts)\n      }.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::Resource::Catalog from json/)\n    end\n\n    context 'serializing facts' do\n      facts_with_special_characters = [\n        { :hash => { 'afact' => 'a+b' }, :encoded => 'a%2Bb' },\n        { :hash => { 'afact' => 'a b' }, :encoded => 'a%20b' },\n        { :hash => { 'afact' => 'a&b' }, :encoded => 'a%26b' },\n        { :hash => { 'afact' => 'a*b' }, :encoded => 'a%2Ab' },\n        { :hash => { 'afact' => 'a=b' }, :encoded => 'a%3Db' },\n        # different UTF-8 widths\n        # 1-byte A\n        # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n        # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n        # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n        { :hash => { 'afact' => \"A\\u06FF\\u16A0\\u{2070E}\" }, :encoded => 'A%DB%BF%E1%9A%A0%F0%A0%9C%8E' },\n      ]\n\n      facts_with_special_characters.each do |test_fact|\n        it \"escapes special characters #{test_fact[:hash]}\" do\n          facts = Puppet::Node::Facts.new(certname, test_fact[:hash])\n          Puppet::Node::Facts.indirection.save(facts)\n\n          stub_request(:post, uri)\n            .with(body: hash_including(\"facts\" => /#{test_fact[:encoded]}/))\n            .to_return(**catalog_response)\n\n          subject.post_catalog(certname, environment: environment, facts: facts)\n        end\n      end\n    end\n  end\n\n  context 'when posting for a v4 catalog' do\n    let(:uri) {\"https://compiler.example.com:8140/puppet/v4/catalog\"}\n    let(:persistence) {{ facts: true, catalog: true }}\n    let(:facts) {{ 'foo' => 'bar' }}\n    let(:trusted_facts) {{}}\n    let(:uuid) { \"ec3d2844-b236-4287-b0ad-632fbb4d1ff0\" }\n    let(:job_id) { \"1\" }\n    let(:payload) {{\n      environment: environment,\n      persistence: persistence,\n      facts: facts,\n      trusted_facts: trusted_facts,\n      transaction_uuid: uuid,\n      job_id: job_id,\n      options: {\n        prefer_requested_environment: false,\n        capture_logs: false\n      }\n    }}\n    let(:serialized_catalog) {{ 'catalog' => catalog.to_data_hash }.to_json}\n    let(:catalog_response) {{ body: serialized_catalog, headers: {'Content-Type' => formatter.mime }}}\n\n    it 'includes default HTTP headers' do\n      stub_request(:post, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end.to_return(**catalog_response)\n\n      subject.post_catalog4(certname, **payload)\n    end\n\n    it 'defaults the server and port based on settings' do\n      Puppet[:server] = 'compiler2.example.com'\n      Puppet[:serverport] = 8141\n\n      stub_request(:post, \"https://compiler2.example.com:8141/puppet/v4/catalog\")\n        .to_return(**catalog_response)\n\n      subject.post_catalog4(certname, **payload)\n    end\n\n    it 'includes puppet headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:post, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).\n        to_return(**catalog_response)\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.post_catalog4(certname, **payload)\n    end\n\n    it 'returns a deserialized catalog' do\n      stub_request(:post, uri)\n        .to_return(**catalog_response)\n\n      _, cat, _ = subject.post_catalog4(certname, **payload)\n      expect(cat).to be_a(Puppet::Resource::Catalog)\n      expect(cat.name).to eq(certname)\n    end\n\n    it 'returns the request response' do\n      stub_request(:post, uri)\n        .to_return(**catalog_response)\n\n      resp, _, _ = subject.post_catalog4(certname, **payload)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:post, uri)\n        .to_return(status: [500, \"Server Error\"])\n\n      expect {\n        subject.post_catalog4(certname, **payload)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Server Error')\n        expect(err.response.code).to eq(500)\n      end\n    end\n\n    it 'raises a response error when server response is not JSON' do\n      stub_request(:post, uri)\n        .to_return(body: \"this isn't valid JSON\", headers: {'Content-Type' => 'application/json'})\n\n      expect {\n        subject.post_catalog4(certname, **payload)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::SerializationError)\n        expect(err.message).to match(/Failed to deserialize catalog from puppetserver response/)\n      end\n    end\n\n    it 'raises a response error when server response a JSON serialized catalog' do\n      stub_request(:post, uri)\n        .to_return(body: {oops: 'bad response data'}.to_json, headers: {'Content-Type' => 'application/json'})\n\n      expect {\n        subject.post_catalog4(certname, **payload)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::SerializationError)\n        expect(err.message).to match(/Failed to deserialize catalog from puppetserver response/)\n      end\n    end\n\n    it 'raises ArgumentError when the `persistence` hash does not contain required keys' do\n      payload[:persistence].delete(:facts)\n      expect { subject.post_catalog4(certname, **payload) }.to raise_error do |err|\n        expect(err).to be_an_instance_of(ArgumentError)\n        expect(err.message).to match(/The 'persistence' hash is missing the keys: facts/)\n      end\n    end\n\n    it 'raises ArgumentError when `facts` are not a Hash' do\n      payload[:facts] = Puppet::Node::Facts.new(certname)\n      expect { subject.post_catalog4(certname, **payload) }.to raise_error do |err|\n        expect(err).to be_an_instance_of(ArgumentError)\n        expect(err.message).to match(/Facts must be a Hash not a Puppet::Node::Facts/)\n      end\n    end\n  end\n\n  context 'when getting a node' do\n    let(:uri) { %r{/puppet/v3/node/ziggy} }\n    let(:node_response) { { body: formatter.render(node), headers: {'Content-Type' => formatter.mime } } }\n\n    it 'includes custom headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:get, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).\n        to_return(**node_response)\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.get_node(certname, environment: 'production')\n    end\n\n    it 'includes environment' do\n      stub_request(:get, uri)\n          .with(query: hash_including(\"environment\" => \"outerspace\"))\n          .to_return(**node_response)\n\n      subject.get_node(certname, environment: 'outerspace')\n    end\n\n    it 'includes configured_environment' do\n      stub_request(:get, uri)\n        .with(query: hash_including(\"configured_environment\" => 'agent_specified'))\n        .to_return(**node_response)\n\n      subject.get_node(certname, environment: 'production', configured_environment: 'agent_specified')\n    end\n\n    it 'includes transaction_uuid' do\n      uuid = \"ec3d2844-b236-4287-b0ad-632fbb4d1ff0\"\n\n      stub_request(:get, uri)\n        .with(query: hash_including(\"transaction_uuid\" => uuid))\n        .to_return(**node_response)\n\n      subject.get_node(certname, environment: 'production', transaction_uuid: uuid)\n    end\n\n    it 'returns a deserialized node' do\n      stub_request(:get, uri)\n        .to_return(**node_response)\n\n      _, n = subject.get_node(certname, environment: 'production')\n      expect(n).to be_a(Puppet::Node)\n      expect(n.name).to eq(certname)\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, uri)\n        .to_return(**node_response)\n\n      resp, _ = subject.get_node(certname, environment: 'production')\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:get, uri)\n        .to_return(status: [500, \"Server Error\"])\n\n      expect {\n        subject.get_node(certname, environment: 'production')\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Server Error')\n        expect(err.response.code).to eq(500)\n      end\n    end\n\n    it 'raises a protocol error if the content-type header is missing' do\n      stub_request(:get, uri)\n        .to_return(body: \"content-type is missing\")\n\n      expect {\n        subject.get_node(certname, environment: 'production')\n      }.to raise_error(Puppet::HTTP::ProtocolError, /No content type in http response; cannot parse/)\n    end\n\n    it 'raises a serialization error if the content is invalid' do\n      stub_request(:get, uri)\n        .to_return(body: \"this isn't valid JSON\", headers: {'Content-Type' => 'application/json'})\n\n      expect {\n        subject.get_node(certname, environment: 'production')\n      }.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::Node from json/)\n    end\n  end\n\n  context 'when getting facts' do\n    let(:uri) { %r{/puppet/v3/facts/ziggy} }\n    let(:facts_response) { { body: formatter.render(facts), headers: {'Content-Type' => formatter.mime } } }\n\n    it 'includes environment' do\n      stub_request(:get, uri)\n          .with(query: hash_including(\"environment\" => \"outerspace\"))\n          .to_return(**facts_response)\n\n      subject.get_facts(certname, environment: 'outerspace')\n    end\n\n    it 'returns a deserialized facts object' do\n      stub_request(:get, uri)\n        .to_return(**facts_response)\n\n      _, n = subject.get_facts(certname, environment: 'production')\n      expect(n).to be_a(Puppet::Node::Facts)\n      expect(n.name).to eq(certname)\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, uri)\n        .to_return(**facts_response)\n\n      resp, _ = subject.get_facts(certname, environment: 'production')\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:get, uri)\n        .to_return(status: [500, \"Server Error\"])\n\n      expect {\n        subject.get_facts(certname, environment: 'production')\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Server Error')\n        expect(err.response.code).to eq(500)\n      end\n    end\n\n    it 'raises a protocol error if the content-type header is missing' do\n      stub_request(:get, uri)\n        .to_return(body: \"content-type is missing\")\n\n      expect {\n        subject.get_facts(certname, environment: 'production')\n      }.to raise_error(Puppet::HTTP::ProtocolError, /No content type in http response; cannot parse/)\n    end\n\n    it 'raises a serialization error if the content is invalid' do\n      stub_request(:get, uri)\n        .to_return(body: \"this isn't valid JSON\", headers: {'Content-Type' => 'application/json'})\n\n      expect {\n        subject.get_facts(certname, environment: 'production')\n      }.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::Node::Facts from json/)\n    end\n  end\n\n  context 'when putting facts' do\n    let(:uri) { %r{/puppet/v3/facts/ziggy} }\n\n    it 'includes custom headers set the :http_extra_headers and :profile settings' do\n      stub_request(:put, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.put_facts(certname, environment: environment, facts: facts)\n    end\n\n    it 'serializes facts in the body' do\n      facts = Puppet::Node::Facts.new(certname, { 'domain' => 'zork'})\n      Puppet::Node::Facts.indirection.save(facts)\n\n      stub_request(:put, uri)\n        .with(body: hash_including(\"name\" => \"ziggy\", \"values\" => {\"domain\" => \"zork\"}))\n\n      subject.put_facts(certname, environment: environment, facts: facts)\n    end\n\n    it 'includes environment' do\n      stub_request(:put, uri)\n        .with(query: {\"environment\" => \"outerspace\"})\n\n      subject.put_facts(certname, environment: 'outerspace', facts: facts)\n    end\n\n    it 'returns the request response' do\n      # the REST API returns the filename, good grief\n      stub_request(:put, uri)\n        .to_return(status: 200, body: \"/opt/puppetlabs/server/data/puppetserver/yaml/facts/#{certname}.yaml\")\n\n      expect(subject.put_facts(certname, environment: environment, facts: facts)).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:put, uri)\n        .to_return(status: [500, \"Server Error\"])\n\n      expect {\n        subject.put_facts(certname, environment: environment, facts: facts)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Server Error')\n        expect(err.response.code).to eq(500)\n      end\n    end\n\n    it 'raises a serialization error if the report cannot be serialized' do\n      invalid_facts = Puppet::Node::Facts.new(certname, {'invalid_utf8_sequence' => \"\\xE2\\x82\".force_encoding('binary')})\n      expect {\n        subject.put_facts(certname, environment: 'production', facts: invalid_facts)\n      }.to raise_error(Puppet::HTTP::SerializationError, /Failed to serialize Puppet::Node::Facts to json: (\"\\\\xE2\" from ASCII-8BIT to UTF-8|partial character in source, but hit end)/)\n    end\n  end\n\n  context 'filebucket' do\n    let(:filebucket_file) { Puppet::FileBucket::File.new('file to store') }\n    let(:formatter) { Puppet::Network::FormatHandler.format(:binary) }\n    let(:path) { \"md5/4aabe1257043bd03ce4c3319c155bc55\" }\n    let(:uri) { %r{/puppet/v3/file_bucket_file/#{path}} }\n\n    context 'when getting a file' do\n      let(:status_response) { { body: formatter.render(filebucket_file), headers: {'Content-Type' => 'application/octet-stream' }}}\n\n      it 'includes default HTTP headers' do\n        stub_request(:get, uri).with do |request|\n          expect(request.headers).to include({\n            'X-Puppet-Version' => /./,\n            'User-Agent' => /./,\n            'Accept' => 'application/octet-stream'\n            })\n          expect(request.headers).to_not include('X-Puppet-Profiling')\n        end.to_return(**status_response)\n\n        subject.get_filebucket_file(path, environment: 'production')\n      end\n\n      it 'always the environment as a parameter' do\n        stub_request(:get, uri).with(query: hash_including('environment' => 'production')).to_return(**status_response)\n\n        subject.get_filebucket_file(path, environment: 'production')\n      end\n\n      {bucket_path: 'path', diff_with: '4aabe1257043bd0', list_all: 'true', fromdate: '20200404', todate: '20200404'}.each do |param, val|\n        it \"includes #{param} as a parameter in the request if #{param} is set\" do\n          stub_request(:get, uri).with(query: hash_including(param => val)).to_return(**status_response)\n\n          options = { param => val }\n          subject.get_filebucket_file(path, environment: 'production', **options)\n        end\n      end\n\n      it \"doesn't include :diff_with as a query param if :bucket_path is nil\" do\n        stub_request(:get, uri).with do |request|\n          expect(request.uri.query).not_to match(/diff_with/)\n        end.to_return(**status_response)\n\n        subject.get_filebucket_file(path, environment: 'production', diff_with: nil)\n      end\n\n      it 'returns a deserialized response' do\n        stub_request(:get, uri)\n        .to_return(**status_response)\n\n        _, s = subject.get_filebucket_file(path, environment: 'production')\n        expect(s).to be_a(Puppet::FileBucket::File)\n        expect(s.contents).to eq('file to store')\n      end\n\n      it 'returns the request response' do\n        stub_request(:get, uri)\n        .to_return(**status_response)\n\n        resp, _ = subject.get_filebucket_file(path, environment: 'production')\n        expect(resp).to be_a(Puppet::HTTP::Response)\n      end\n    end\n\n    context 'when putting a file' do\n      let(:status_response) { { status: 200, body: '' } }\n\n      it 'includes default HTTP headers' do\n        stub_request(:put, uri).with do |request|\n          expect(request.headers).to include({\n            'X-Puppet-Version' => /./,\n            'User-Agent' => /./,\n            'Accept' => 'application/octet-stream',\n            'Content-Type' => 'application/octet-stream'\n            })\n          expect(request.headers).to_not include('X-Puppet-Profiling')\n        end.to_return(**status_response)\n\n        subject.put_filebucket_file(path, body: filebucket_file.contents, environment: 'production')\n      end\n\n      it 'always the environment as a parameter' do\n        stub_request(:put, uri).with(query: hash_including('environment' => 'production')).to_return(**status_response)\n\n        subject.put_filebucket_file(path, body: filebucket_file.contents, environment: 'production')\n      end\n\n      it 'sends the file contents as the request body' do\n        stub_request(:put, uri).with(body: filebucket_file.contents).to_return(**status_response)\n\n        subject.put_filebucket_file(path, body: filebucket_file.contents, environment: 'production')\n      end\n\n      it 'returns the request response' do\n        stub_request(:put, uri)\n        .to_return(**status_response)\n\n        s = subject.put_filebucket_file(path, body: filebucket_file.contents, environment: 'production')\n        expect(s).to be_a(Puppet::HTTP::Response)\n      end\n    end\n\n    context 'when heading a file' do\n      let(:status_response) {{ status: 200 }}\n\n      it 'includes default HTTP headers' do\n        stub_request(:head, uri).with do |request|\n          expect(request.headers).to include({\n            'X-Puppet-Version' => /./,\n            'User-Agent' => /./,\n            'Accept' => 'application/octet-stream',\n            })\n          expect(request.headers).to_not include('X-Puppet-Profiling')\n        end.to_return(**status_response)\n\n        subject.head_filebucket_file(path, environment: 'production')\n      end\n\n      it 'always the environment as a parameter' do\n        stub_request(:head, uri).with(query: hash_including('environment' => 'production')).to_return(**status_response)\n\n        subject.head_filebucket_file(path, environment: 'production')\n      end\n\n      it \"includes :bucket_path as a parameter in the request if :bucket_path is set\" do\n        stub_request(:head, uri).with(query: hash_including(:bucket_path => 'some/path')).to_return(**status_response)\n\n        subject.head_filebucket_file(path, environment: 'production', bucket_path: 'some/path')\n      end\n\n      it \"doesn't include :bucket_path as a query param if :bucket_path is nil\" do\n        stub_request(:head, uri).with do |request|\n          expect(request.uri.query).not_to match(/bucket_path/)\n        end.to_return(**status_response)\n\n        subject.head_filebucket_file(path, environment: 'production', bucket_path: nil)\n      end\n\n      it \"returns the request response\" do\n        stub_request(:head, uri).with(query: hash_including(:bucket_path => 'some/path')).to_return(**status_response)\n\n        resp = subject.head_filebucket_file(path, environment: 'production', bucket_path: 'some/path')\n        expect(resp).to be_a(Puppet::HTTP::Response)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/service/file_server_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/network'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Service::FileServer do\n  include PuppetSpec::Files\n  include PuppetSpec::Network\n\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:subject) { client.create_session.route_to(:fileserver) }\n  let(:environment) { 'testing' }\n  let(:report) { Puppet::Transaction::Report.new }\n\n  before :each do\n    Puppet[:server] = 'www.example.com'\n    Puppet[:serverport] = 443\n  end\n\n  context 'when making requests' do\n    let(:uri) {\"https://www.example.com:443/puppet/v3/file_content/:mount/:path?environment=testing\"}\n\n    it 'includes default HTTP headers' do\n      stub_request(:get, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end\n\n      subject.get_file_content(path: '/:mount/:path', environment: environment) { |data| }\n    end\n  end\n\n  context 'when routing to the file service' do\n    it 'defaults the server and port based on settings' do\n      Puppet[:server] = 'file.example.com'\n      Puppet[:serverport] = 8141\n\n      stub_request(:get, \"https://file.example.com:8141/puppet/v3/file_content/:mount/:path?environment=testing\")\n\n      subject.get_file_content(path: '/:mount/:path', environment: environment) { |data| }\n    end\n  end\n\n  context 'retrieving file metadata' do\n    let(:path) { tmpfile('get_file_metadata') }\n    let(:url) { \"https://www.example.com/puppet/v3/file_metadata/:mount/#{path}?checksum_type=sha256&environment=testing&links=manage&source_permissions=ignore\" }\n    let(:filemetadata) { Puppet::FileServing::Metadata.new(path) }\n    let(:request_path) { \"/:mount/#{path}\"}\n\n    it 'includes puppet headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:get, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).\n        to_return(status: 200, body: filemetadata.render, headers: { 'Content-Type' => 'application/json' })\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.get_file_metadata(path: request_path, environment: environment)\n    end\n\n    it 'submits a request for file metadata to the server' do\n      stub_request(:get, url).with(\n        headers: {'Accept'=>acceptable_content_types_string}\n      ).to_return(\n        status: 200, body: filemetadata.render, headers: { 'Content-Type' => 'application/json' }\n      )\n\n      _, metadata = subject.get_file_metadata(path: request_path, environment: environment)\n      expect(metadata.path).to eq(path)\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, url).to_return(\n        status: 200, body: filemetadata.render, headers: { 'Content-Type' => 'application/json' }\n      )\n\n      resp, _ = subject.get_file_metadata(path: request_path, environment: environment)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises a protocol error if the Content-Type header is missing from the response' do\n      stub_request(:get, url).to_return(status: 200, body: '', headers: {})\n\n      expect {\n        subject.get_file_metadata(path: request_path, environment: environment)\n      }.to raise_error(Puppet::HTTP::ProtocolError, \"No content type in http response; cannot parse\")\n    end\n\n    it 'raises an error if the Content-Type is unsupported' do\n      stub_request(:get, url).to_return(status: 200, body: '', headers: { 'Content-Type' => 'text/yaml' })\n\n      expect {\n        subject.get_file_metadata(path: request_path, environment: environment)\n      }.to raise_error(Puppet::HTTP::ProtocolError, \"Content-Type is unsupported\")\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:get, url).to_return(status: [400, 'Bad Request'])\n\n      expect {\n        subject.get_file_metadata(path: request_path, environment: environment)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n\n    it 'raises a serialization error if serialization fails' do\n      stub_request(:get, url).to_return(\n        status: 200, body: '', headers: { 'Content-Type' => 'application/json' }\n      )\n\n      expect {\n        subject.get_file_metadata(path: request_path, environment: environment)\n      }.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::FileServing::Metadata from json/)\n    end\n\n    it 'raises response error if path is relative' do\n      expect {\n        subject.get_file_metadata(path: 'relative_path', environment: environment)\n      }.to raise_error(ArgumentError, 'Path must start with a slash')\n    end\n  end\n\n  context 'retrieving multiple file metadatas' do\n    let(:path) { tmpfile('get_file_metadatas') }\n    let(:url) { \"https://www.example.com/puppet/v3/file_metadatas/:mount/#{path}?checksum_type=sha256&links=manage&recurse=false&source_permissions=ignore&environment=testing\" }\n    let(:filemetadatas) { [Puppet::FileServing::Metadata.new(path)] }\n    let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n    let(:request_path) { \"/:mount/#{path}\"}\n\n    it 'includes puppet headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:get, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).\n        to_return(status: 200, body: formatter.render_multiple(filemetadatas), headers: { 'Content-Type' => 'application/json' })\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.get_file_metadatas(path: request_path, environment: environment)\n    end\n\n    it 'submits a request for file metadata to the server' do\n      stub_request(:get, url).with(\n        headers: {'Accept' => acceptable_content_types_string}\n      ).to_return(\n        status: 200, body: formatter.render_multiple(filemetadatas), headers: { 'Content-Type' => 'application/json' }\n      )\n\n      _, metadatas = subject.get_file_metadatas(path: request_path, environment: environment)\n      expect(metadatas.first.path).to eq(path)\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, url).to_return(\n        status: 200, body: formatter.render_multiple(filemetadatas), headers: { 'Content-Type' => 'application/json' }\n      )\n\n      resp, _ = subject.get_file_metadatas(path: request_path, environment: environment)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'automatically converts an array of parameters to the stringified query' do\n      url = \"https://www.example.com/puppet/v3/file_metadatas/:mount/#{path}?checksum_type=sha256&environment=testing&ignore=CVS&ignore=.git&ignore=.hg&links=manage&recurse=false&source_permissions=ignore\"\n      stub_request(:get, url).with(\n        headers: {'Accept'=>acceptable_content_types_string}\n      ).to_return(\n        status: 200, body: formatter.render_multiple(filemetadatas), headers: { 'Content-Type' => 'application/json' }\n      )\n\n      _, metadatas = subject.get_file_metadatas(path: request_path, environment: environment, ignore: ['CVS', '.git', '.hg'])\n      expect(metadatas.first.path).to eq(path)\n    end\n\n    it 'raises a protocol error if the Content-Type header is missing from the response' do\n      stub_request(:get, url).to_return(status: 200, body: '', headers: {})\n\n      expect {\n        subject.get_file_metadatas(path: request_path, environment: environment)\n      }.to raise_error(Puppet::HTTP::ProtocolError, \"No content type in http response; cannot parse\")\n    end\n\n    it 'raises an error if the Content-Type is unsupported' do\n      stub_request(:get, url).to_return(status: 200, body: '', headers: { 'Content-Type' => 'text/yaml' })\n\n      expect {\n        subject.get_file_metadatas(path: request_path, environment: environment)\n      }.to raise_error(Puppet::HTTP::ProtocolError, \"Content-Type is unsupported\")\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:get, url).to_return(status: [400, 'Bad Request'])\n\n      expect {\n        subject.get_file_metadatas(path: request_path, environment: environment)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n\n    it 'raises a serialization error if serialization fails' do\n      stub_request(:get, url).to_return(\n        status: 200, body: '', headers: { 'Content-Type' => 'application/json' }\n      )\n\n      expect {\n        subject.get_file_metadatas(path: request_path, environment: environment)\n      }.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize multiple Puppet::FileServing::Metadata from json/)\n    end\n\n    it 'raises response error if path is relative' do\n      expect {\n        subject.get_file_metadatas(path: 'relative_path', environment: environment)\n      }.to raise_error(ArgumentError, 'Path must start with a slash')\n    end\n  end\n\n  context 'getting file content' do\n    let(:uri) {\"https://www.example.com:443/puppet/v3/file_content/:mount/:path?environment=testing\"}\n\n    it 'includes puppet headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:get, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).\n        to_return(status: 200, body: \"and beyond\")\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      expect { |b|\n        subject.get_file_content(path: '/:mount/:path', environment: environment, &b)\n      }.to yield_with_args(\"and beyond\")\n    end\n\n    it 'yields file content' do\n      stub_request(:get, uri).with do |request|\n        expect(request.headers).to include({'Accept' => 'application/octet-stream'})\n      end.to_return(status: 200, body: \"and beyond\")\n\n      expect { |b|\n        subject.get_file_content(path: '/:mount/:path', environment: environment, &b)\n      }.to yield_with_args(\"and beyond\")\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, uri)\n\n      resp = subject.get_file_content(path: '/:mount/:path', environment: environment) { |b| b }\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:get, uri).to_return(status: [400, 'Bad Request'])\n\n      expect {\n        subject.get_file_content(path: '/:mount/:path', environment: environment) { |data| }\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n\n    it 'raises response error if path is relative' do\n      expect {\n        subject.get_file_content(path: 'relative_path', environment: environment) { |data| }\n      }.to raise_error(ArgumentError, 'Path must start with a slash')\n    end\n  end\n\n  context 'getting static file content' do\n    let(:code_id) { \"0fc72115-adc6-4b1a-aa50-8f31b3ece440\" }\n    let(:uri) { \"https://www.example.com:443/puppet/v3/static_file_content/:mount/:path?environment=testing&code_id=#{code_id}\"}\n\n    it 'yields file content' do\n      stub_request(:get, uri).with do |request|\n        expect(request.headers).to include({'Accept' => 'application/octet-stream'})\n      end.to_return(status: 200, body: \"and beyond\")\n\n      expect { |b|\n        subject.get_static_file_content(path: '/:mount/:path', environment: environment, code_id: code_id, &b)\n      }.to yield_with_args(\"and beyond\")\n    end\n\n    it 'returns the request response' do\n      stub_request(:get, uri)\n\n      resp = subject.get_static_file_content(path: '/:mount/:path', environment: environment, code_id: code_id) { |b| b }\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:get, uri).to_return(status: [400, 'Bad Request'])\n\n      expect {\n        subject.get_static_file_content(path: '/:mount/:path', environment: environment, code_id: code_id) { |data| }\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n\n    it 'raises response error if path is relative' do\n      expect {\n        subject.get_static_file_content(path: 'relative_path', environment: environment, code_id: code_id) { |data| }\n      }.to raise_error(ArgumentError, 'Path must start with a slash')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/service/puppetserver_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Service::Puppetserver do\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:subject) { client.create_session.route_to(:puppetserver) }\n\n  before :each do\n    Puppet[:server] = 'puppetserver.example.com'\n  end\n\n  context 'when making requests' do\n    it 'includes default HTTP headers' do\n      stub_request(:get, \"https://puppetserver.example.com:8140/status/v1/simple/server\").with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end.to_return(body: \"running\", headers: {'Content-Type' => 'text/plain;charset=utf-8'})\n\n      subject.get_simple_status\n    end\n\n    it 'includes extra headers' do\n      Puppet[:http_extra_headers] = 'region:us-west'\n\n      stub_request(:get, \"https://puppetserver.example.com:8140/status/v1/simple/server\")\n        .with(headers: {'Region' => 'us-west'})\n        .to_return(body: \"running\", headers: {'Content-Type' => 'text/plain;charset=utf-8'})\n\n      subject.get_simple_status\n    end\n  end\n\n  context 'when routing to the puppetserver service' do\n    it 'defaults the server and port based on settings' do\n      Puppet[:server] = 'compiler2.example.com'\n      Puppet[:serverport] = 8141\n\n      stub_request(:get, \"https://compiler2.example.com:8141/status/v1/simple/server\")\n        .to_return(body: \"running\", headers: {'Content-Type' => 'text/plain;charset=utf-8'})\n\n      subject.get_simple_status\n    end\n  end\n\n  context 'when getting puppetserver status' do\n    let(:url) { \"https://puppetserver.example.com:8140/status/v1/simple/server\" }\n\n    it 'returns the request response and status' do\n      stub_request(:get, url)\n        .to_return(body: \"running\", headers: {'Content-Type' => 'text/plain;charset=utf-8'})\n\n      resp, status = subject.get_simple_status\n      expect(resp).to be_a(Puppet::HTTP::Response)\n      expect(status).to eq('running')\n    end\n\n    it 'raises a response error if unsuccessful' do\n      stub_request(:get, url).to_return(status: [500, 'Internal Server Error'])\n\n      expect {\n        subject.get_simple_status\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq(\"Internal Server Error\")\n        expect(err.response.code).to eq(500)\n      end\n    end\n\n    it 'accepts an ssl context' do\n      stub_request(:get, url)\n        .to_return(body: \"running\", headers: {'Content-Type' => 'text/plain;charset=utf-8'})\n\n      other_ctx = Puppet::SSL::SSLContext.new\n      expect(client).to receive(:connect).with(URI(url), options: {ssl_context: other_ctx}).and_call_original\n\n      session = client.create_session\n      service = Puppet::HTTP::Service.create_service(client, session, :puppetserver, 'puppetserver.example.com', 8140)\n      service.get_simple_status(ssl_context: other_ctx)\n    end\n  end\n\n  context 'when /status/v1/simple/server returns not found' do\n    it 'calls /status/v1/simple/master' do\n      stub_request(:get, \"https://puppetserver.example.com:8140/status/v1/simple/server\")\n        .to_return(status: [404, 'not found: server'])\n\n      stub_request(:get, \"https://puppetserver.example.com:8140/status/v1/simple/master\")\n        .to_return(body: \"running\", headers: {'Content-Type' => 'text/plain;charset=utf-8'})\n\n      resp, status = subject.get_simple_status\n      expect(resp).to be_a(Puppet::HTTP::Response)\n      expect(status).to eq('running')\n    end\n\n    it 'raises a response error if fallback is unsuccessful' do\n      stub_request(:get, \"https://puppetserver.example.com:8140/status/v1/simple/server\")\n        .to_return(status: [404, 'not found: server'])\n\n      stub_request(:get, \"https://puppetserver.example.com:8140/status/v1/simple/master\")\n        .to_return(status: [404, 'not found: master'])\n\n      expect {\n        subject.get_simple_status\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('not found: master')\n        expect(err.response.code).to eq(404)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/service/report_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/network'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Service::Report do\n  include PuppetSpec::Network\n\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:subject) { client.create_session.route_to(:report) }\n  let(:environment) { 'testing' }\n  let(:report) { Puppet::Transaction::Report.new }\n\n  before :each do\n    Puppet[:report_server] = 'www.example.com'\n    Puppet[:report_port] = 443\n  end\n\n  context 'when making requests' do\n    let(:uri) {\"https://www.example.com:443/puppet/v3/report/report?environment=testing\"}\n\n    it 'includes default HTTP headers' do\n      stub_request(:put, uri).with do |request|\n        expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})\n        expect(request.headers).to_not include('X-Puppet-Profiling')\n      end\n\n      subject.put_report('report', report, environment: environment)\n    end\n  end\n\n  context 'when routing to the report service' do\n    it 'defaults the server and port based on settings' do\n      Puppet[:report_server] = 'report.example.com'\n      Puppet[:report_port] = 8141\n\n      stub_request(:put, \"https://report.example.com:8141/puppet/v3/report/report?environment=testing\")\n\n      subject.put_report('report', report, environment: environment)\n    end\n\n    it 'fallbacks to server and serverport' do\n      Puppet[:report_server] = nil\n      Puppet[:report_port] = nil\n      Puppet[:server] = 'report2.example.com'\n      Puppet[:serverport] = 8142\n\n      stub_request(:put, \"https://report2.example.com:8142/puppet/v3/report/report?environment=testing\")\n\n      subject.put_report('report', report, environment: environment)\n    end\n  end\n\n  context 'when submitting a report' do\n    let(:url) { \"https://www.example.com/puppet/v3/report/infinity?environment=testing\" }\n\n    it 'includes puppet headers set via the :http_extra_headers and :profile settings' do\n      stub_request(:put, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})\n\n      Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'\n      Puppet[:profile] = true\n\n      subject.put_report('infinity', report, environment: environment)\n    end\n\n    it 'submits a report to the \"report\" endpoint' do\n      stub_request(:put, url)\n        .with(\n          headers: {\n            'Accept'=>acceptable_content_types_string,\n            'Content-Type'=>'application/json',\n           }).\n         to_return(status: 200, body: \"\", headers: {})\n\n      subject.put_report('infinity', report, environment: environment)\n    end\n\n    it 'percent encodes the uri before submitting the report' do\n      stub_request(:put, \"https://www.example.com/puppet/v3/report/node%20name?environment=testing\")\n        .to_return(status: 200, body: \"\", headers: {})\n\n      subject.put_report('node name', report, environment: environment)\n    end\n\n    it 'returns the response whose body contains the list of report processors' do\n      body = \"[\\\"store\\\":\\\"http\\\"]\"\n      stub_request(:put, url)\n        .to_return(status: 200, body: body, headers: {'Content-Type' => 'application/json'})\n\n      resp = subject.put_report('infinity', report, environment: environment)\n      expect(resp.body).to eq(body)\n      expect(resp).to be_a(Puppet::HTTP::Response)\n    end\n\n    it 'raises response error if unsuccessful' do\n      stub_request(:put, url).to_return(status: [400, 'Bad Request'], headers: {'X-Puppet-Version' => '6.1.8' })\n\n      expect {\n        subject.put_report('infinity', report, environment: environment)\n      }.to raise_error do |err|\n        expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)\n        expect(err.message).to eq('Bad Request')\n        expect(err.response.code).to eq(400)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/service_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/network'\nrequire 'puppet/http'\nrequire 'puppet/file_serving'\nrequire 'puppet/file_serving/content'\nrequire 'puppet/file_serving/metadata'\n\ndescribe Puppet::HTTP::Service do\n  include PuppetSpec::Network\n\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:session) { Puppet::HTTP::Session.new(client, []) }\n  let(:url) { URI.parse('https://www.example.com') }\n  let(:service) { described_class.new(client, session, url) }\n\n  class TestService < Puppet::HTTP::Service\n    def get_test(ssl_context)\n       @client.get(\n        url,\n        headers: add_puppet_headers({'Default-Header' => 'default-value'}),\n        options: {ssl_context: ssl_context}\n      )\n    end\n\n    def mime_types(model)\n      get_mime_types(model)\n    end\n  end\n\n  context 'when modifying headers for an http request' do\n    let(:service) { TestService.new(client, session, url) }\n\n    it 'adds custom user-specified headers' do\n      stub_request(:get, \"https://www.example.com/\").\n         with( headers: { 'Default-Header'=>'default-value', 'Header2'=>'newvalue' })\n\n      Puppet[:http_extra_headers] = 'header2:newvalue'\n\n      service.get_test(ssl_context)\n    end\n\n    it 'adds X-Puppet-Profiling header if set' do\n      stub_request(:get, \"https://www.example.com/\").\n         with( headers: { 'Default-Header'=>'default-value', 'X-Puppet-Profiling'=>'true' })\n\n      Puppet[:profile] = true\n\n      service.get_test(ssl_context)\n    end\n\n    it 'ignores a custom header does not have a value' do\n      stub_request(:get, \"https://www.example.com/\").with do |request|\n        expect(request.headers).to include({'Default-Header' => 'default-value'})\n        expect(request.headers).to_not include('header-with-no-value')\n      end\n\n      Puppet[:http_extra_headers] = 'header-with-no-value:'\n\n      service.get_test(ssl_context)\n    end\n\n    it 'ignores a custom header that already exists (case insensitive) in the header hash' do\n      stub_request(:get, \"https://www.example.com/\").\n         with( headers: { 'Default-Header'=>'default-value' })\n\n      Puppet[:http_extra_headers] = 'default-header:wrongvalue'\n\n      service.get_test(ssl_context)\n    end\n  end\n\n  it \"returns a URI containing the base URL and path\" do\n    expect(service.with_base_url('/puppet/v3')).to eq(URI.parse(\"https://www.example.com/puppet/v3\"))\n  end\n\n  it \"doesn't modify frozen the base URL\" do\n    service = described_class.new(client, session, url.freeze)\n    service.with_base_url('/puppet/v3')\n  end\n\n  it \"percent encodes paths before appending them to the path\" do\n    expect(service.with_base_url('/path/with/a space')).to eq(URI.parse(\"https://www.example.com/path/with/a%20space\"))\n  end\n\n  it \"connects to the base URL with a nil ssl context\" do\n    expect(client).to receive(:connect).with(url, options: {ssl_context: nil})\n\n    service.connect\n  end\n\n  it \"accepts an optional ssl_context\" do\n    other_ctx = Puppet::SSL::SSLContext.new\n    expect(client).to receive(:connect).with(url, options: {ssl_context: other_ctx})\n\n    service.connect(ssl_context: other_ctx)\n  end\n\n  it 'raises for unknown service names' do\n    expect {\n      described_class.create_service(client, session, :westbound)\n    }.to raise_error(ArgumentError, \"Unknown service westbound\")\n  end\n\n  [:ca].each do |name|\n    it \"returns true for #{name}\" do\n      expect(described_class.valid_name?(name)).to eq(true)\n    end\n  end\n\n  it \"returns false when the service name is a string\" do\n    expect(described_class.valid_name?(\"ca\")).to eq(false)\n  end\n\n  it \"returns false for unknown service names\" do\n    expect(described_class.valid_name?(:westbound)).to eq(false)\n  end\n\n  it 'returns different mime types for different models' do\n    mimes = acceptable_content_types\n\n    service = TestService.new(client, session, url)\n    [\n      Puppet::Node,\n      Puppet::Node::Facts,\n      Puppet::Transaction::Report,\n      Puppet::FileServing::Metadata,\n    ].each do |model|\n      expect(service.mime_types(model)).to eq(mimes)\n    end\n\n    # These are special\n    expect(service.mime_types(Puppet::FileServing::Content)).to eq(%w[application/octet-stream])\n\n    expect(service.mime_types(Puppet::Resource::Catalog)).to eq(acceptable_catalog_content_types)\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/session_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Session do\n  let(:ssl_context) { Puppet::SSL::SSLContext.new }\n  let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }\n  let(:uri) { URI.parse('https://www.example.com') }\n  let(:good_service) {\n    double('good', url: uri, connect: nil)\n  }\n  let(:bad_service) {\n    create_bad_service\n  }\n\n  def create_bad_service(failure_message = 'whoops')\n    service = double('bad', url: uri)\n    allow(service).to receive(:connect).and_raise(Puppet::HTTP::ConnectionError, failure_message)\n    service\n  end\n\n  class DummyResolver < Puppet::HTTP::Resolver\n    attr_reader :count\n\n    def initialize(service)\n      @service = service\n      @count = 0\n    end\n\n    def resolve(session, name, ssl_context: nil, canceled_handler: nil)\n      @count += 1\n      return @service if check_connection?(session, @service, ssl_context: ssl_context)\n    end\n  end\n\n  context 'when routing' do\n    it 'returns the first resolved service' do\n      resolvers = [DummyResolver.new(bad_service), DummyResolver.new(good_service)]\n      session = described_class.new(client, resolvers)\n      resolved = session.route_to(:ca)\n\n      expect(resolved).to eq(good_service)\n    end\n\n    it 'only resolves once per session' do\n      resolver = DummyResolver.new(good_service)\n      session = described_class.new(client, [resolver])\n      session.route_to(:ca)\n      session.route_to(:ca)\n\n      expect(resolver.count).to eq(1)\n    end\n\n    it 'raises if there are no more routes' do\n      resolvers = [DummyResolver.new(bad_service)]\n      session = described_class.new(client, resolvers)\n\n      expect {\n        session.route_to(:ca)\n      }.to raise_error(Puppet::HTTP::RouteError, 'No more routes to ca')\n    end\n\n    it 'logs all routing failures as errors when there are no more routes' do\n      resolvers = [DummyResolver.new(create_bad_service('whoops1')), DummyResolver.new(create_bad_service('whoops2'))]\n      session = described_class.new(client, resolvers)\n\n      expect {\n        session.route_to(:ca)\n      }.to raise_error(Puppet::HTTP::RouteError, 'No more routes to ca')\n\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: \"Connection to #{uri} failed, trying next route: whoops1\"),\n                               an_object_having_attributes(level: :err, message: \"Connection to #{uri} failed, trying next route: whoops2\"))\n    end\n\n    it 'accepts an ssl context to use when connecting' do\n      alt_context = Puppet::SSL::SSLContext.new\n      expect(good_service).to receive(:connect).with(ssl_context: alt_context)\n\n      resolvers = [DummyResolver.new(good_service)]\n      session = described_class.new(client, resolvers)\n      session.route_to(:ca, ssl_context: alt_context)\n    end\n\n    it 'raises for unknown service names' do\n      expect {\n        session = described_class.new(client, [])\n        session.route_to(:westbound)\n      }.to raise_error(ArgumentError, \"Unknown service westbound\")\n    end\n\n    it 'routes to the service when given a puppet URL with an explicit host' do\n      allow_any_instance_of(Net::HTTP).to receive(:start)\n\n      session = described_class.new(client, [])\n      url = URI(\"puppet://example.com:8140/:modules/:module/path/to/file\")\n      service = session.route_to(:fileserver, url: url)\n\n      expect(service.url.to_s).to eq(\"https://example.com:8140/puppet/v3\")\n    end\n\n    it 'raises a connection error if we cannot connect' do\n      allow_any_instance_of(Net::HTTP).to receive(:start).and_raise(Net::OpenTimeout)\n\n      session = described_class.new(client, [])\n      url = URI('puppet://example.com:8140/:modules/:module/path/to/file')\n\n      expect {\n        session.route_to(:fileserver, url: url)\n      }.to raise_error(Puppet::HTTP::ConnectionError,\n                       %r{Request to https://example.com:8140/puppet/v3 timed out connect operation after .* seconds})\n    end\n\n    it 'resolves the route when given a generic puppet:/// URL' do\n      resolvers = [DummyResolver.new(good_service)]\n      session = described_class.new(client, resolvers)\n      url = URI('puppet:///:modules/:module/path/to/file')\n      service = session.route_to(:fileserver, url: url)\n\n      expect(service.url).to eq(good_service.url)\n    end\n  end\n\n  context 'when resolving using multiple resolvers' do\n    let(:session) { client.create_session }\n\n    it \"prefers SRV records\" do\n      Puppet[:use_srv_records] = true\n      Puppet[:server_list] = 'foo.example.com,bar.example.com,baz.example.com'\n      Puppet[:ca_server] = 'caserver.example.com'\n\n      allow_any_instance_of(Puppet::HTTP::DNS).to receive(:each_srv_record).and_yield('mars.example.srv', 8140)\n      service = session.route_to(:ca)\n\n      expect(service.url).to eq(URI(\"https://mars.example.srv:8140/puppet-ca/v1\"))\n    end\n\n    it \"next prefers :ca_server when explicitly set\" do\n      Puppet[:use_srv_records] = true\n      Puppet[:server_list] = 'foo.example.com,bar.example.com,baz.example.com'\n      Puppet[:ca_server] = 'caserver.example.com'\n\n      service = session.route_to(:ca)\n\n      expect(service.url).to eq(URI(\"https://caserver.example.com:8140/puppet-ca/v1\"))\n    end\n\n    it \"next prefers the first successful connection from server_list\" do\n      Puppet[:use_srv_records] = true\n      Puppet[:server_list] = 'foo.example.com,bar.example.com,baz.example.com'\n\n      allow_any_instance_of(Puppet::HTTP::DNS).to receive(:each_srv_record)\n      stub_request(:get, \"https://foo.example.com:8140/status/v1/simple/server\").to_return(status: 500)\n      stub_request(:get, \"https://bar.example.com:8140/status/v1/simple/server\").to_return(status: 200)\n\n      service = session.route_to(:ca)\n\n      expect(service.url).to eq(URI(\"https://bar.example.com:8140/puppet-ca/v1\"))\n    end\n\n    it \"does not fallback from server_list to the settings resolver when server_list is exhausted\" do\n      Puppet[:server_list] = 'foo.example.com'\n\n      expect_any_instance_of(Puppet::HTTP::Resolver::Settings).to receive(:resolve).never\n      stub_request(:get, \"https://foo.example.com:8140/status/v1/simple/server\").to_return(status: 500)\n\n      expect {\n        session.route_to(:ca)\n      }.to raise_error(Puppet::HTTP::RouteError, \"No more routes to ca\")\n    end\n\n    it \"raises when there are no more routes\" do\n      allow_any_instance_of(Net::HTTP).to receive(:start).and_raise(Errno::EHOSTUNREACH)\n      session = client.create_session\n\n      expect {\n        session.route_to(:ca)\n      }.to raise_error(Puppet::HTTP::RouteError, 'No more routes to ca')\n    end\n\n    Puppet::HTTP::Service::SERVICE_NAMES.each do |name|\n      it \"resolves #{name} using server_list\" do\n        Puppet[:server_list] = 'apple.example.com'\n        req = stub_request(:get, \"https://apple.example.com:8140/status/v1/simple/server\").to_return(status: 200)\n\n        session.route_to(name)\n\n        expect(req).to have_been_requested\n      end\n    end\n\n    it 'does not use server_list to resolve the ca service when ca_server is explicitly set' do\n      Puppet[:ca_server] = 'banana.example.com'\n\n      expect(session.route_to(:ca).url.to_s).to eq(\"https://banana.example.com:8140/puppet-ca/v1\")\n    end\n\n    it 'does not use server_list to resolve the report service when the report_server is explicitly set' do\n      Puppet[:report_server] = 'cherry.example.com'\n\n      expect(session.route_to(:report).url.to_s).to eq(\"https://cherry.example.com:8140/puppet/v3\")\n    end\n\n    it 'resolves once for all services in a session' do\n      Puppet[:server_list] = 'apple.example.com'\n      req = stub_request(:get, \"https://apple.example.com:8140/status/v1/simple/server\").to_return(status: 200)\n\n      Puppet::HTTP::Service::SERVICE_NAMES.each do |name|\n        session.route_to(name)\n      end\n\n      expect(req).to have_been_requested\n    end\n\n    it 'resolves server_list for each new session' do\n      Puppet[:server_list] = 'apple.example.com'\n      req = stub_request(:get, \"https://apple.example.com:8140/status/v1/simple/server\").to_return(status: 200)\n\n      client.create_session.route_to(:puppet)\n      client.create_session.route_to(:puppet)\n\n      expect(req).to have_been_requested.twice\n    end\n  end\n\n  context 'when retrieving capabilities' do\n    let(:response) { Puppet::HTTP::Response.new(uri, 200, 'OK') }\n\n    let(:session) do\n      resolver = DummyResolver.new(good_service)\n      described_class.new(client, [resolver])\n    end\n\n    it 'raises for unknown service names' do\n      expect {\n        session = described_class.new(client, [])\n        session.supports?(:westbound, 'a capability')\n      }.to raise_error(ArgumentError, \"Unknown service westbound\")\n    end\n\n    context 'locales' do\n      it 'does not support locales if the cached service has not been resolved' do\n        session = described_class.new(client, [])\n\n        expect(session).to_not be_supports(:puppet, 'locales')\n      end\n\n      it \"supports locales if the cached service's version is 5.3.4 or greater\" do\n        allow(response).to receive(:[]).with('X-Puppet-Version').and_return('5.3.4')\n\n        session.route_to(:puppet)\n        session.process_response(response)\n\n        expect(session).to be_supports(:puppet, 'locales')\n      end\n\n      it \"does not support locales if the cached service's version is 5.3.3\" do\n        allow(response).to receive(:[]).with('X-Puppet-Version').and_return('5.3.3')\n\n        session.route_to(:puppet)\n        session.process_response(response)\n\n        expect(session).to_not be_supports(:puppet, 'locales')\n      end\n\n      it \"does not support locales if the cached service's version is missing\" do\n        allow(response).to receive(:[]).with('X-Puppet-Version').and_return(nil)\n\n        session.route_to(:puppet)\n        session.process_response(response)\n\n        expect(session).to_not be_supports(:puppet, 'locales')\n      end\n    end\n\n    context 'json' do\n      it 'does not support json if the cached service has not been resolved' do\n        session = described_class.new(client, [])\n\n        expect(session).to_not be_supports(:puppet, 'json')\n      end\n\n      it \"supports json if the cached service's version is 5 or greater\" do\n        allow(response).to receive(:[]).with('X-Puppet-Version').and_return('5.5.12')\n\n        session.route_to(:puppet)\n        session.process_response(response)\n\n        expect(session).to be_supports(:puppet, 'json')\n      end\n\n      it \"does not support json if the cached service's version is less than 5.0\" do\n        allow(response).to receive(:[]).with('X-Puppet-Version').and_return('4.10.1')\n\n        session.route_to(:puppet)\n        session.process_response(response)\n\n        expect(session).to_not be_supports(:puppet, 'json')\n      end\n\n      it \"supports json if the cached service's version is missing\" do\n        allow(response).to receive(:[]).with('X-Puppet-Version').and_return(nil)\n\n        session.route_to(:puppet)\n        session.process_response(response)\n\n        expect(session).to be_supports(:puppet, 'json')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/http/site_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/http'\n\ndescribe Puppet::HTTP::Site do\n  let(:scheme)      { 'https' }\n  let(:host)        { 'rubygems.org' }\n  let(:port)        { 443 }\n\n  def create_site(scheme, host, port)\n    described_class.new(scheme, host, port)\n  end\n\n  it 'accepts scheme, host, and port' do\n    site = create_site(scheme, host, port)\n\n    expect(site.scheme).to eq(scheme)\n    expect(site.host).to eq(host)\n    expect(site.port).to eq(port)\n  end\n\n  it 'generates an external URI string' do\n    site = create_site(scheme, host, port)\n\n    expect(site.addr).to eq(\"https://rubygems.org:443\")\n  end\n\n  it 'considers sites to be different when the scheme is different' do\n    https_site = create_site('https', host, port)\n    http_site = create_site('http', host, port)\n\n    expect(https_site).to_not eq(http_site)\n  end\n\n  it 'considers sites to be different when the host is different' do\n    rubygems_site = create_site(scheme, 'rubygems.org', port)\n    github_site = create_site(scheme, 'github.com', port)\n\n    expect(rubygems_site).to_not eq(github_site)\n  end\n\n  it 'considers sites to be different when the port is different' do\n    site_443 = create_site(scheme, host, 443)\n    site_80 = create_site(scheme, host, 80)\n\n    expect(site_443).to_not eq(site_80)\n  end\n\n  it 'compares values when determining equality' do\n    site = create_site(scheme, host, port)\n\n    sites = {}\n    sites[site] = site\n\n    another_site = create_site(scheme, host, port)\n\n    expect(sites.include?(another_site)).to be_truthy\n  end\n\n  it 'computes the same hash code for equivalent objects' do\n    site = create_site(scheme, host, port)\n    same_site = create_site(scheme, host, port)\n\n    expect(site.hash).to eq(same_site.hash)\n  end\n\n  it 'uses ssl with https' do\n    site = create_site('https', host, port)\n\n    expect(site).to be_use_ssl\n  end\n\n  it 'does not use ssl with http' do\n    site = create_site('http', host, port)\n\n    expect(site).to_not be_use_ssl\n  end\n\n  it 'moves to a new URI location' do\n    site = create_site('http', 'host1', 80)\n\n    uri = URI.parse('https://host2:443/some/where/else')\n    new_site = site.move_to(uri)\n\n    expect(new_site.scheme).to eq('https')\n    expect(new_site.host).to eq('host2')\n    expect(new_site.port).to eq(443)\n  end\n\n  it 'creates a site from a URI' do\n    site = create_site('https', 'rubygems.org', 443)\n    uri = URI.parse('https://rubygems.org/gems/puppet/')\n\n    expect(described_class.from_uri(uri)).to eq(site)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/catalog/compiler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\nrequire 'puppet/indirector/catalog/compiler'\n\ndef set_facts(fact_hash)\n  fact_hash.each do |key, value|\n    allow(Facter).to receive(:value).with(key).and_return(value)\n  end\nend\n\ndescribe Puppet::Resource::Catalog::Compiler do\n  include Matchers::Resource\n  include PuppetSpec::Files\n\n  let(:compiler) { described_class.new }\n  let(:node_name) { \"foo\" }\n  let(:node) { Puppet::Node.new(node_name)}\n\n  describe \"when initializing\" do\n    before do\n      allow(Puppet).to receive(:version).and_return(1)\n    end\n\n    it \"should cache the server metadata and reuse it\" do\n      Puppet[:node_terminus] = :memory\n      Puppet::Node.indirection.save(Puppet::Node.new(\"node1\"))\n      Puppet::Node.indirection.save(Puppet::Node.new(\"node2\"))\n\n      allow(compiler).to receive(:compile)\n\n      compiler.find(Puppet::Indirector::Request.new(:catalog, :find, 'node1', nil, :node => 'node1'))\n      compiler.find(Puppet::Indirector::Request.new(:catalog, :find, 'node2', nil, :node => 'node2'))\n    end\n  end\n\n  describe \"when finding catalogs\" do\n    before do\n      allow(node).to receive(:merge)\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request = Puppet::Indirector::Request.new(:catalog, :find, node_name, nil, :node => node_name)\n    end\n\n    it \"should directly use provided nodes for a local request\" do\n      expect(Puppet::Node.indirection).not_to receive(:find)\n      expect(compiler).to receive(:compile).with(node, anything)\n      allow(@request).to receive(:options).and_return(:use_node => node)\n      allow(@request).to receive(:remote?).and_return(false)\n      compiler.find(@request)\n    end\n\n    it \"rejects a provided node if the request is remote\" do\n      allow(@request).to receive(:options).and_return(:use_node => node)\n      allow(@request).to receive(:remote?).and_return(true)\n      expect {\n        compiler.find(@request)\n      }.to raise_error Puppet::Error, /invalid option use_node/i\n    end\n\n    it \"should use the authenticated node name if no request key is provided\" do\n      allow(@request).to receive(:key).and_return(nil)\n      expect(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n      expect(compiler).to receive(:compile).with(node, anything)\n      compiler.find(@request)\n    end\n\n    it \"should use the provided node name by default\" do\n      expect(@request).to receive(:key).and_return(\"my_node\")\n\n      expect(Puppet::Node.indirection).to receive(:find).with(\"my_node\", anything).and_return(node)\n      expect(compiler).to receive(:compile).with(node, anything)\n      compiler.find(@request)\n    end\n\n    it \"should fail if no node is passed and none can be found\" do\n      allow(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(nil)\n      expect { compiler.find(@request) }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail intelligently when searching for a node raises an exception\" do\n      allow(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_raise(\"eh\")\n      expect { compiler.find(@request) }.to raise_error(Puppet::Error)\n    end\n\n    it \"should pass the found node to the compiler for compiling\" do\n      expect(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n      expect(Puppet::Parser::Compiler).to receive(:compile).with(node, anything)\n      compiler.find(@request)\n    end\n\n    it \"should pass node containing percent character to the compiler\" do\n      node_with_percent_character = Puppet::Node.new \"%6de\"\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node_with_percent_character)\n      expect(Puppet::Parser::Compiler).to receive(:compile).with(node_with_percent_character, anything)\n      compiler.find(@request)\n    end\n\n    it \"should extract any facts from the request\" do\n      expect(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n      expect(compiler).to receive(:extract_facts_from_request).with(@request)\n      allow(Puppet::Parser::Compiler).to receive(:compile)\n      compiler.find(@request)\n    end\n\n    it \"requires `facts_format` option if facts are passed in\" do\n      facts = Puppet::Node::Facts.new(\"mynode\", :afact => \"avalue\")\n      request = Puppet::Indirector::Request.new(:catalog, :find, \"mynode\", nil, :facts => facts)\n      expect {\n        compiler.find(request)\n      }.to raise_error ArgumentError, /no fact format provided for mynode/\n    end\n\n    it \"rejects facts in the request from a different node\" do\n      facts = Puppet::Node::Facts.new(\"differentnode\", :afact => \"avalue\")\n      request = Puppet::Indirector::Request.new(\n        :catalog, :find, \"mynode\", nil, :facts => facts, :facts_format => \"unused\"\n      )\n      expect {\n        compiler.find(request)\n      }.to raise_error Puppet::Error, /fact definition for the wrong node/i\n    end\n\n    it \"should return the results of compiling as the catalog\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      catalog = Puppet::Resource::Catalog.new(node.name)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler.find(@request)).to equal(catalog)\n    end\n\n    it \"passes the code_id from the request to the compiler\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      code_id = 'b59e5df0578ef411f773ee6c33d8073c50e7b8fe'\n      @request.options[:code_id] = code_id\n\n      expect(Puppet::Parser::Compiler).to receive(:compile).with(anything, code_id)\n\n      compiler.find(@request)\n    end\n\n    it \"returns a catalog with the code_id from the request\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      code_id = 'b59e5df0578ef411f773ee6c33d8073c50e7b8fe'\n      @request.options[:code_id] = code_id\n\n      catalog = Puppet::Resource::Catalog.new(node.name, node.environment, code_id)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler.find(@request).code_id).to eq(code_id)\n    end\n\n    it \"does not inline metadata when the static_catalog option is false\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = false\n      @request.options[:code_id] = 'some_code_id'\n      allow(node.environment).to receive(:static_catalogs?).and_return(true)\n\n      catalog = Puppet::Resource::Catalog.new(node.name, node.environment)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler).not_to receive(:inline_metadata)\n      compiler.find(@request)\n    end\n\n    it \"does not inline metadata when static_catalogs are disabled\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = true\n      @request.options[:checksum_type] = 'md5'\n      @request.options[:code_id] = 'some_code_id'\n      allow(node.environment).to receive(:static_catalogs?).and_return(false)\n\n      catalog = Puppet::Resource::Catalog.new(node.name, node.environment)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler).not_to receive(:inline_metadata)\n      compiler.find(@request)\n    end\n\n    it \"does not inline metadata when code_id is not specified\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = true\n      @request.options[:checksum_type] = 'md5'\n      allow(node.environment).to receive(:static_catalogs?).and_return(true)\n\n      catalog = Puppet::Resource::Catalog.new(node.name, node.environment)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler).not_to receive(:inline_metadata)\n      expect(compiler.find(@request)).to eq(catalog)\n    end\n\n    it \"inlines metadata when the static_catalog option is true, static_catalogs are enabled, and a code_id is provided\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = true\n      @request.options[:checksum_type] = 'sha256'\n      @request.options[:code_id] = 'some_code_id'\n      allow(node.environment).to receive(:static_catalogs?).and_return(true)\n\n      catalog = Puppet::Resource::Catalog.new(node.name, node.environment)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler).to receive(:inline_metadata).with(catalog, :sha256).and_return(catalog)\n      compiler.find(@request)\n    end\n\n    it \"inlines metadata with the first common checksum type\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = true\n      @request.options[:checksum_type] = 'atime.md5.sha256.mtime'\n      @request.options[:code_id] = 'some_code_id'\n      allow(node.environment).to receive(:static_catalogs?).and_return(true)\n\n      catalog = Puppet::Resource::Catalog.new(node.name, node.environment)\n      allow(Puppet::Parser::Compiler).to receive(:compile).and_return(catalog)\n\n      expect(compiler).to receive(:inline_metadata).with(catalog, :md5).and_return(catalog)\n      compiler.find(@request)\n    end\n\n    it \"errors if checksum_type contains no shared checksum types\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = true\n      @request.options[:checksum_type] = 'atime.md2'\n      @request.options[:code_id] = 'some_code_id'\n      allow(node.environment).to receive(:static_catalogs?).and_return(true)\n\n      expect { compiler.find(@request) }.to raise_error Puppet::Error,\n        \"Unable to find a common checksum type between agent 'atime.md2' and master '[:sha256, :sha256lite, :md5, :md5lite, :sha1, :sha1lite, :sha512, :sha384, :sha224, :mtime, :ctime, :none]'.\"\n    end\n\n    it \"errors if checksum_type contains no shared checksum types\" do\n      allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n      @request.options[:static_catalog] = true\n      @request.options[:checksum_type] = nil\n      @request.options[:code_id] = 'some_code_id'\n      allow(node.environment).to receive(:static_catalogs?).and_return(true)\n\n      expect { compiler.find(@request) }.to raise_error Puppet::Error,\n        \"Unable to find a common checksum type between agent '' and master '[:sha256, :sha256lite, :md5, :md5lite, :sha1, :sha1lite, :sha512, :sha384, :sha224, :mtime, :ctime, :none]'.\"\n    end\n\n    it \"prevents the environment from being evicted during compilation\" do\n      Puppet[:environment_timeout] = 0\n\n      envs = Puppet.lookup(:environments)\n\n      expect(compiler).to receive(:compile) do\n        # we should get the same object\n        expect(envs.get!(:production)).to equal(envs.get!(:production))\n      end\n\n      compiler.find(@request)\n    end\n\n    context 'when checking agent and server specified environments' do\n      before :each do\n        FileUtils.mkdir_p(File.join(Puppet[:environmentpath], 'env_server'))\n        FileUtils.mkdir_p(File.join(Puppet[:environmentpath], 'env_agent'))\n\n        node.environment = 'env_server'\n        allow(Puppet::Node.indirection).to receive(:find).and_return(node)\n\n        @request.environment = 'env_agent'\n      end\n\n      it 'ignores mismatched environments by default' do\n        catalog = compiler.find(@request)\n\n        expect(catalog.environment).to eq('env_server')\n        expect(catalog).to have_resource('Stage[main]')\n      end\n\n      # versioned environment directories can cause this\n      it 'allows environments with the same name but mismatched modulepaths' do\n        envs = Puppet.lookup(:environments)\n        env_server = envs.get!(:env_server)\n        v1_env = env_server.override_with({ modulepath: ['/code-v1/env-v1/'] })\n        v2_env = env_server.override_with({ modulepath: ['/code-v2/env-v2/'] })\n\n        @request.options[:check_environment] = \"true\"\n        @request.environment = v1_env\n        node.environment = v2_env\n\n        catalog = compiler.find(@request)\n\n        expect(catalog.environment).to eq('env_server')\n        expect(catalog).to have_resource('Stage[main]')\n      end\n\n      it 'returns an empty catalog if asked to check the environment and they are mismatched' do\n        @request.options[:check_environment] = \"true\"\n        catalog = compiler.find(@request)\n\n        expect(catalog.environment).to eq('env_server')\n        expect(catalog.resources).to be_empty\n      end\n    end\n  end\n\n  describe \"when handling a request with facts\" do\n    before do\n      allow(Facter).to receive(:value).and_return(\"something\")\n\n      facts = Puppet::Node::Facts.new('hostname', \"fact\" => \"value\", \"architecture\" => \"i386\")\n      Puppet::Node::Facts.indirection.save(facts)\n    end\n\n    def a_legacy_request_that_contains(facts, format = :pson)\n      request = Puppet::Indirector::Request.new(:catalog, :find, \"hostname\", nil)\n      request.options[:facts_format] = format.to_s\n      request.options[:facts] = Puppet::Util.uri_query_encode(facts.render(format))\n      request\n    end\n\n    def a_request_that_contains(facts)\n      request = Puppet::Indirector::Request.new(:catalog, :find, \"hostname\", nil)\n      request.options[:facts_format] = \"application/json\"\n      request.options[:facts] = Puppet::Util.uri_query_encode(facts.render('json'))\n      request\n    end\n\n    context \"when extracting facts from the request\" do\n      let(:facts) { Puppet::Node::Facts.new(\"hostname\") }\n\n      it \"should do nothing if no facts are provided\" do\n        request = Puppet::Indirector::Request.new(:catalog, :find, \"hostname\", nil)\n        request.options[:facts] = nil\n\n        expect(compiler.extract_facts_from_request(request)).to be_nil\n      end\n\n      it \"should deserialize the facts without changing the timestamp\" do\n        time = Time.now\n        facts.timestamp = time\n        request = a_request_that_contains(facts)\n        facts = compiler.extract_facts_from_request(request)\n        expect(facts.timestamp).to eq(time)\n      end\n\n      it \"accepts PSON facts from older agents\", :if => Puppet.features.pson? do\n        request = a_legacy_request_that_contains(facts)\n\n        facts = compiler.extract_facts_from_request(request)\n        expect(facts).to eq(facts)\n      end\n\n      it \"rejects YAML facts\" do\n        request = a_legacy_request_that_contains(facts, :yaml)\n\n        expect {\n          compiler.extract_facts_from_request(request)\n        }.to raise_error(ArgumentError, /Unsupported facts format/)\n      end\n\n      it \"rejects unknown fact formats\" do\n        request = a_request_that_contains(facts)\n        request.options[:facts_format] = 'unknown-format'\n\n        expect {\n          compiler.extract_facts_from_request(request)\n        }.to raise_error(ArgumentError, /Unsupported facts format/)\n      end\n    end\n\n    context \"when saving facts from the request\" do\n      let(:facts) { Puppet::Node::Facts.new(\"hostname\") }\n\n      it \"should save facts if they were issued by the request\" do\n        request = a_request_that_contains(facts)\n\n        options = {\n          :environment => request.environment,\n          :transaction_uuid => request.options[:transaction_uuid],\n        }\n\n        expect(Puppet::Node::Facts.indirection).to receive(:save).with(facts, nil, options)\n        compiler.find(request)\n      end\n\n      it \"should skip saving facts if none were supplied\" do\n        request = Puppet::Indirector::Request.new(:catalog, :find, \"hostname\", nil)\n\n        options = {\n          :environment => request.environment,\n          :transaction_uuid => request.options[:transaction_uuid],\n        }\n\n        expect(Puppet::Node::Facts.indirection).not_to receive(:save).with(facts, nil, options)\n        compiler.find(request)\n      end\n    end\n  end\n\n  describe \"when finding nodes\" do\n    it \"should look node information up via the Node class with the provided key\" do\n      request = Puppet::Indirector::Request.new(:catalog, :find, node_name, nil)\n      allow(compiler).to receive(:compile)\n\n      expect(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n\n      compiler.find(request)\n    end\n\n    it \"should pass the transaction_uuid to the node indirection\" do\n      uuid = '793ff10d-89f8-4527-a645-3302cbc749f3'\n      allow(compiler).to receive(:compile)\n      request = Puppet::Indirector::Request.new(:catalog, :find, node_name,\n                                                nil, :transaction_uuid => uuid)\n\n      expect(Puppet::Node.indirection).to receive(:find).with(\n        node_name,\n        hash_including(:transaction_uuid => uuid)\n      ).and_return(node)\n\n      compiler.find(request)\n    end\n\n    it \"should pass the configured_environment to the node indirection\" do\n      environment = 'foo'\n      allow(compiler).to receive(:compile)\n      request = Puppet::Indirector::Request.new(:catalog, :find, node_name,\n                                                nil, :configured_environment => environment)\n\n      expect(Puppet::Node.indirection).to receive(:find).with(\n        node_name,\n        hash_including(:configured_environment => environment)\n      ).and_return(node)\n\n      compiler.find(request)\n    end\n\n    it \"should pass a facts object from the original request facts to the node indirection\" do\n      facts = Puppet::Node::Facts.new(\"hostname\", :afact => \"avalue\")\n      expect(compiler).to receive(:extract_facts_from_request).and_return(facts)\n      expect(compiler).to receive(:save_facts_from_request)\n\n      request = Puppet::Indirector::Request.new(:catalog, :find, \"hostname\",\n                                                nil, :facts_format => \"application/json\",\n                                                :facts => facts.render('json'))\n\n      expect(Puppet::Node.indirection).to receive(:find).with(\"hostname\", hash_including(:facts => facts)).and_return(node)\n\n      compiler.find(request)\n    end\n  end\n\n  describe \"after finding nodes\" do\n    before do\n      allow(Puppet).to receive(:version).and_return(1)\n      set_facts({\n        'networking.fqdn' => \"my.server.com\",\n        'networking.ip'   => \"my.ip.address\",\n        'networking.ip6'  => nil\n        })\n      @request = Puppet::Indirector::Request.new(:catalog, :find, node_name, nil)\n      allow(compiler).to receive(:compile)\n      allow(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n    end\n\n    it \"should add the server's Puppet version to the node's parameters as 'serverversion'\" do\n      expect(node).to receive(:merge).with(hash_including(\"serverversion\" => \"1\"))\n      compiler.find(@request)\n    end\n\n    it \"should add the server's fqdn to the node's parameters as 'servername'\" do\n      expect(node).to receive(:merge).with(hash_including(\"servername\" => \"my.server.com\"))\n      compiler.find(@request)\n    end\n\n    it \"should add the server's IP address to the node's parameters as 'serverip'\" do\n      expect(node).to receive(:merge).with(hash_including(\"serverip\" => \"my.ip.address\"))\n      compiler.find(@request)\n    end\n\n    it \"shouldn't warn if there is at least one ip fact\" do\n      expect(node).to receive(:merge).with(hash_including(\"serverip\" => \"my.ip.address\"))\n      compiler.find(@request)\n      expect(@logs).not_to be_any {|log| log.level == :warning and log.message =~ /Could not retrieve either serverip or serverip6 fact/}\n    end\n  end\n\n  describe \"in an IPv6 only environment\" do\n    before do |example|\n      allow(Puppet).to receive(:version).and_return(1)\n      set_facts({\n        'networking.fqdn' => \"my.server.com\",\n        'networking.ip'   => nil,\n      })\n      if example.metadata[:nil_ipv6]\n        set_facts({\n          'networking.ip6' => nil\n        })\n      else\n        set_facts({\n          'networking.ip6' => \"my.ipv6.address\"\n        })\n      end\n      @request = Puppet::Indirector::Request.new(:catalog, :find, node_name, nil)\n      allow(compiler).to receive(:compile)\n      allow(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n    end\n\n    it \"should populate the :serverip6 fact\" do\n      expect(node).to receive(:merge).with(hash_including(\"serverip6\" => \"my.ipv6.address\"))\n      compiler.find(@request)\n    end\n\n    it \"shouldn't warn if there is at least one ip fact\" do\n      expect(node).to receive(:merge).with(hash_including(\"serverip6\" => \"my.ipv6.address\"))\n      compiler.find(@request)\n      expect(@logs).not_to be_any {|log| log.level == :warning and log.message =~ /Could not retrieve either serverip or serverip6 fact/}\n    end\n\n    it \"should warn if there are no ip facts\", :nil_ipv6 do\n      expect(node).to receive(:merge)\n      compiler.find(@request)\n      expect(@logs).to be_any {|log| log.level == :warning and log.message =~ /Could not retrieve either serverip or serverip6 fact/}\n    end\n  end\n\n  describe \"after finding nodes when checking for a PE version\" do\n    include PuppetSpec::Compiler\n\n    let(:pe_version_file) { '/opt/puppetlabs/server/pe_version' }\n    let(:request) { Puppet::Indirector::Request.new(:catalog, :find, node_name, nil) }\n\n    before :each do\n      Puppet[:code] = 'notify { \"PE Version: $pe_serverversion\": }'\n    end\n\n    it \"should not add 'pe_serverversion' when FOSS\" do\n      allow(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n      expect {\n        catalog = compiler.find(request)\n        catalog.resource('notify', 'PE Version: 2019.2.0') \n      }.to raise_error(/Evaluation Error: Unknown variable: 'pe_serverversion'./)\n    end\n\n    it \"should add 'pe_serverversion' when PE\" do\n      allow(File).to receive(:readable?).and_call_original\n      allow(File).to receive(:readable?).with(pe_version_file).and_return(true)\n      allow(File).to receive(:zero?).with(pe_version_file).and_return(false)\n      allow(File).to receive(:read).and_call_original\n      allow(File).to receive(:read).with(pe_version_file).and_return('2019.2.0')\n\n      allow(Puppet::Node.indirection).to receive(:find).with(node_name, anything).and_return(node)\n      catalog = compiler.find(request)\n\n      expect(catalog.resource('notify', 'PE Version: 2019.2.0')).to be\n    end\n  end\n\n  describe \"when filtering resources\" do\n    before :each do\n      @catalog = double('catalog')\n      allow(@catalog).to receive(:respond_to?).with(:filter).and_return(true)\n    end\n\n    it \"should delegate to the catalog instance filtering\" do\n      expect(@catalog).to receive(:filter)\n      compiler.filter(@catalog)\n    end\n\n    it \"should filter out virtual resources\" do\n      resource = double('resource', :virtual? => true)\n      allow(@catalog).to receive(:filter).and_yield(resource)\n\n      compiler.filter(@catalog)\n    end\n\n    it \"should return the same catalog if it doesn't support filtering\" do\n      allow(@catalog).to receive(:respond_to?).with(:filter).and_return(false)\n\n      expect(compiler.filter(@catalog)).to eq(@catalog)\n    end\n\n    it \"should return the filtered catalog\" do\n      catalog = double('filtered catalog')\n      allow(@catalog).to receive(:filter).and_return(catalog)\n\n      expect(compiler.filter(@catalog)).to eq(catalog)\n    end\n  end\n\n  describe \"when inlining metadata\" do\n    include PuppetSpec::Compiler\n\n    let(:node) { Puppet::Node.new 'me' }\n    let(:checksum_type) { 'md5' }\n    let(:checksum_value) { 'b1946ac92492d2347c6235b4d2611184' }\n    let(:path) { File.expand_path('/foo') }\n    let(:source) { 'puppet:///modules/mymodule/config_file.txt' }\n\n    def stubs_resource_metadata(ftype, relative_path, full_path = nil)\n      full_path ||=  File.join(Puppet[:environmentpath], 'production', relative_path)\n\n      metadata = double('metadata')\n      allow(metadata).to receive(:ftype).and_return(ftype)\n      allow(metadata).to receive(:full_path).and_return(full_path)\n      allow(metadata).to receive(:relative_path).and_return(relative_path)\n      allow(metadata).to receive(:source).and_return(\"puppet:///#{relative_path}\")\n      allow(metadata).to receive(:source=)\n      allow(metadata).to receive(:content_uri=)\n\n      metadata\n    end\n\n    def stubs_file_metadata(checksum_type, sha, relative_path, full_path = nil)\n      metadata = stubs_resource_metadata('file', relative_path, full_path)\n      allow(metadata).to receive(:checksum).and_return(\"{#{checksum_type}}#{sha}\")\n      allow(metadata).to receive(:checksum_type).and_return(checksum_type)\n      metadata\n    end\n\n    def stubs_link_metadata(relative_path, destination)\n      metadata = stubs_resource_metadata('link', relative_path)\n      allow(metadata).to receive(:destination).and_return(destination)\n      metadata\n    end\n\n    def stubs_directory_metadata(relative_path)\n      metadata = stubs_resource_metadata('directory', relative_path)\n      allow(metadata).to receive(:relative_path).and_return('.')\n      metadata\n    end\n\n    describe \"and the environment is a symlink and versioned_environment_dirs is true\" do\n\n      let(:tmpdir) { Dir.mktmpdir }\n\n      before(:each) do\n        Puppet[:versioned_environment_dirs] = true\n        prod_path = File.join(Puppet[:environmentpath], 'production')\n        FileUtils.rm_rf(prod_path)\n        FileUtils.symlink(tmpdir, prod_path)\n      end\n\n      it \"inlines metadata for a file\" do\n        catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n        module_relative_path = 'modules/mymodule/files/config_file.txt'\n        metadata = stubs_file_metadata(checksum_type,\n                                       checksum_value,\n                                       module_relative_path,\n                                       File.join(tmpdir, module_relative_path) )\n        expect(metadata).to receive(:source=).with(source)\n        expect(metadata).to receive(:content_uri=).with(\"puppet:///#{module_relative_path}\")\n\n        options = {\n          :environment => catalog.environment_instance,\n          :links => :manage,\n          :checksum_type => checksum_type.to_sym,\n          :source_permissions => :ignore\n        }\n        expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, options).and_return(metadata)\n\n        compiler.send(:inline_metadata, catalog, checksum_type)\n\n\n        expect(catalog.metadata[path]).to eq(metadata)\n        expect(catalog.recursive_metadata).to be_empty\n\n      end\n    end\n\n    it \"inlines metadata for a file\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/config_file.txt')\n      expect(metadata).to receive(:source=).with(source)\n      expect(metadata).to receive(:content_uri=).with('puppet:///modules/mymodule/files/config_file.txt')\n\n      options = {\n        :environment => catalog.environment_instance,\n        :links => :manage,\n        :checksum_type => checksum_type.to_sym,\n        :source_permissions => :ignore\n      }\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, options).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n\n      expect(catalog.metadata[path]).to eq(metadata)\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"uses resource parameters when inlining metadata\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => link,\n          source => '#{source}',\n          links  => follow,\n          source_permissions => use,\n        }\n      MANIFEST\n\n      metadata = stubs_link_metadata('modules/mymodule/files/config_file.txt', '/tmp/some/absolute/path')\n      expect(metadata).to receive(:source=).with(source)\n      expect(metadata).to receive(:content_uri=).with('puppet:///modules/mymodule/files/config_file.txt')\n\n      options = {\n        :environment        => catalog.environment_instance,\n        :links              => :follow,\n        :checksum_type      => checksum_type.to_sym,\n        :source_permissions => :use\n      }\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, options).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n\n      expect(catalog.metadata[path]).to eq(metadata)\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"uses file parameters which match the true file type defaults\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n      if Puppet::Util::Platform.windows?\n        default_file = Puppet::Type.type(:file).new(:name => 'C:\\defaults')\n      else\n        default_file = Puppet::Type.type(:file).new(:name => '/defaults')\n      end\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/config_file.txt')\n\n      options = {\n        :environment => catalog.environment_instance,\n        :links => default_file[:links],\n        :checksum_type => checksum_type.to_sym,\n        :source_permissions => default_file[:source_permissions]\n      }\n\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, options).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n    end\n\n    it \"inlines metadata for the first source found\" do\n      alt_source = 'puppet:///modules/files/other.txt'\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => ['#{alt_source}', '#{source}'],\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/config_file.txt')\n      expect(metadata).to receive(:source=).with(source)\n      expect(metadata).to receive(:content_uri=).with('puppet:///modules/mymodule/files/config_file.txt')\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(alt_source, anything).and_return(nil)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n\n      expect(catalog.metadata[path]).to eq(metadata)\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    [['md5', 'b1946ac92492d2347c6235b4d2611184'],\n     ['sha256', '5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03']].each do |checksum_type, sha|\n      describe \"with agent requesting checksum_type #{checksum_type}\" do\n        it \"sets checksum and checksum_value for resources with puppet:// source URIs\" do\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure => file,\n              source => '#{source}'\n            }\n          MANIFEST\n\n          metadata = stubs_file_metadata(checksum_type, sha, 'modules/mymodule/files/config_file.txt')\n\n          options = {\n            :environment        => catalog.environment_instance,\n            :links              => :manage,\n            :checksum_type      => checksum_type.to_sym,\n            :source_permissions => :ignore\n          }\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, options).and_return(metadata)\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata[path]).to eq(metadata)\n          expect(catalog.recursive_metadata).to be_empty\n        end\n      end\n    end\n\n    it \"preserves source host and port in the content_uri\" do\n      source = 'puppet://myhost:8888/modules/mymodule/config_file.txt'\n\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/config_file.txt')\n      allow(metadata).to receive(:source).and_return(source)\n\n      expect(metadata).to receive(:content_uri=).with('puppet://myhost:8888/modules/mymodule/files/config_file.txt')\n\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n    end\n\n    it \"skips absent resources\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => absent,\n        }\n      MANIFEST\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources without a source\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n        }\n      MANIFEST\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources with a local source\" do\n      local_source = File.expand_path('/tmp/source')\n\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{local_source}',\n        }\n      MANIFEST\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources with a http source\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => ['http://foo.source.io', 'https://foo.source.io']\n        }\n      MANIFEST\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources with a source outside the environment path\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n      full_path = File.join(Puppet[:codedir], \"modules/mymodule/files/config_file.txt\")\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/config_file.txt', full_path)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources whose mount point is not 'modules'\" do\n      source = 'puppet:///secure/data'\n\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}',\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'secure/files/data.txt')\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources with 'modules' mount point resolving to a path not in 'modules/*/files'\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}',\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/not_in_files/config_file.txt')\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"skips resources with 'modules' mount point resolving to a path with an empty module name\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}',\n        }\n      MANIFEST\n\n      # note empty module name \"modules//files\"\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules//files/config_file.txt')\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"inlines resources in 'modules' mount point resolving to a 'site' directory within the per-environment codedir\" do\n      # example taken from https://github.com/puppetlabs/control-repo/blob/508b9cc/site/profile/manifests/puppetmaster.pp#L45-L49\n      source = 'puppet:///modules/profile/puppetmaster/update-classes.sh'\n\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n      # See https://github.com/puppetlabs/control-repo/blob/508b9cc/site/profile/files/puppetmaster/update-classes.sh\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'site/profile/files/puppetmaster/update-classes.sh')\n      allow(metadata).to receive(:source).and_return(source)\n\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata[path]).to eq(metadata)\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    # It's bizarre to strip trailing slashes for a file, but it's how\n    # puppet currently behaves, so match that.\n    it \"inlines resources with a trailing slash\" do\n      source = 'puppet:///modules/mymodule/myfile'\n\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { '#{path}':\n          ensure => file,\n          source => '#{source}/'\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/myfile')\n      allow(metadata).to receive(:source).and_return(source)\n\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata[path]).to eq(metadata)\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    describe \"when inlining directories\" do\n      let(:source_dir) { 'puppet:///modules/mymodule/directory' }\n      let(:metadata) { stubs_directory_metadata('modules/mymodule/files/directory') }\n\n      describe \"when recurse is false\" do\n        it \"skips children\" do\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => directory,\n              source  => '#{source_dir}'\n            }\n          MANIFEST\n\n          expect(metadata).to receive(:content_uri=).with('puppet:///modules/mymodule/files/directory')\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source_dir, anything).and_return(metadata)\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata[path]).to eq(metadata)\n          expect(catalog.recursive_metadata).to be_empty\n        end\n      end\n\n      describe \"when recurse is true\" do\n        let(:child_metadata) { stubs_file_metadata(checksum_type, checksum_value, 'myfile.txt') }\n\n        it \"inlines child metadata\" do\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure    => directory,\n              recurse   => true,\n              source    => '#{source_dir}',\n              max_files => 1234,\n            }\n          MANIFEST\n\n          expect(metadata).to receive(:content_uri=).with('puppet:///modules/mymodule/files/directory')\n          expect(child_metadata).to receive(:content_uri=).with('puppet:///modules/mymodule/files/directory/myfile.txt')\n\n          options = {\n            :environment        => catalog.environment_instance,\n            :links              => :manage,\n            :checksum_type      => checksum_type.to_sym,\n            :source_permissions => :ignore,\n            :recurse            => true,\n            :recurselimit       => nil,\n            :max_files          => 1234,\n            :ignore             => nil,\n          }\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source_dir, options).and_return([metadata, child_metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata[path]).to be_nil\n          expect(catalog.recursive_metadata[path][source_dir]).to eq([metadata, child_metadata])\n        end\n\n        it \"uses resource parameters when inlining metadata\" do\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure             => directory,\n              recurse            => true,\n              source             => '#{source_dir}',\n              checksum           => sha256,\n              source_permissions => use_when_creating,\n              recurselimit       => 2,\n              max_files          => 4321,\n              ignore             => 'foo.+',\n              links              => follow,\n            }\n          MANIFEST\n\n          options = {\n            :environment        => catalog.environment_instance,\n            :links              => :follow,\n            :checksum_type      => :sha256,\n            :source_permissions => :use_when_creating,\n            :recurse            => true,\n            :recurselimit       => 2,\n            :max_files          => 4321,\n            :ignore             => 'foo.+',\n          }\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source_dir, options).and_return([metadata, child_metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata[path]).to be_nil\n          expect(catalog.recursive_metadata[path][source_dir]).to eq([metadata, child_metadata])\n        end\n\n        it \"inlines metadata for all sources if source_select is all\" do\n          alt_source_dir = 'puppet:///modules/mymodule/other_directory'\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => directory,\n              recurse => true,\n              source  => ['#{source_dir}', '#{alt_source_dir}'],\n              sourceselect => all,\n            }\n          MANIFEST\n\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source_dir, anything).and_return([metadata, child_metadata])\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(alt_source_dir, anything).and_return([metadata, child_metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata[path]).to be_nil\n          expect(catalog.recursive_metadata[path][source_dir]).to eq([metadata, child_metadata])\n          expect(catalog.recursive_metadata[path][alt_source_dir]).to eq([metadata, child_metadata])\n        end\n\n        it \"inlines metadata for the first valid source if source_select is first\" do\n          alt_source_dir = 'puppet:///modules/mymodule/other_directory'\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => directory,\n              recurse => true,\n              source  => ['#{source_dir}', '#{alt_source_dir}'],\n            }\n          MANIFEST\n\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source_dir, anything).and_return(nil)\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(alt_source_dir, anything).and_return([metadata, child_metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata[path]).to be_nil\n          expect(catalog.recursive_metadata[path][source_dir]).to be_nil\n          expect(catalog.recursive_metadata[path][alt_source_dir]).to eq([metadata, child_metadata])\n        end\n\n        it \"skips resources whose mount point is not 'modules'\" do\n          source = 'puppet:///secure/data'\n\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => directory,\n              recurse => true,\n              source  => '#{source}',\n            }\n          MANIFEST\n\n          metadata = stubs_directory_metadata('secure/files/data')\n          allow(metadata).to receive(:source).and_return(source)\n\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source, anything).and_return([metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n          expect(catalog.metadata).to be_empty\n          expect(catalog.recursive_metadata).to be_empty\n        end\n\n        it \"skips resources with 'modules' mount point resolving to a path not in 'modules/*/files'\" do\n          source = 'puppet:///modules/mymodule/directory'\n\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => directory,\n              recurse => true,\n              source  => '#{source}',\n            }\n          MANIFEST\n\n          metadata = stubs_directory_metadata('modules/mymodule/not_in_files/directory')\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source, anything).and_return([metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n          expect(catalog.metadata).to be_empty\n          expect(catalog.recursive_metadata).to be_empty\n        end\n\n        it \"inlines resources in 'modules' mount point resolving to a 'site' directory within the per-environment codedir\" do\n          # example adopted from https://github.com/puppetlabs/control-repo/blob/508b9cc/site/profile/manifests/puppetmaster.pp#L45-L49\n          source = 'puppet:///modules/profile/puppetmaster'\n\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => file,\n              recurse => true,\n              source  => '#{source}'\n            }\n          MANIFEST\n\n          # See https://github.com/puppetlabs/control-repo/blob/508b9cc/site/profile/files/puppetmaster/update-classes.sh\n          dir_metadata = stubs_directory_metadata('site/profile/files/puppetmaster')\n          allow(dir_metadata).to receive(:source).and_return(source)\n\n          child_metadata = stubs_file_metadata(checksum_type, checksum_value, './update-classes.sh')\n          allow(child_metadata).to receive(:source).and_return(\"#{source}/update-classes.sh\")\n\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source, anything).and_return([dir_metadata, child_metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n          expect(catalog.metadata).to be_empty\n          expect(catalog.recursive_metadata[path][source]).to eq([dir_metadata, child_metadata])\n        end\n\n        it \"inlines resources with a trailing slash\" do\n          source = 'puppet:///modules/mymodule/directory'\n\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            file { '#{path}':\n              ensure  => directory,\n              recurse => true,\n              source  => '#{source}/'\n            }\n          MANIFEST\n\n          dir_metadata = stubs_directory_metadata('modules/mymodule/files/directory')\n          allow(dir_metadata).to receive(:source).and_return(source)\n\n          child_metadata = stubs_file_metadata(checksum_type, checksum_value, './file')\n          allow(child_metadata).to receive(:source).and_return(\"#{source}/file\")\n\n          expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source, anything).and_return([dir_metadata, child_metadata])\n\n          compiler.send(:inline_metadata, catalog, checksum_type)\n\n          expect(catalog.metadata).to be_empty\n          expect(catalog.recursive_metadata[path][source]).to eq([dir_metadata, child_metadata])\n        end\n      end\n    end\n\n    it \"skips non-file resources\" do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        notify { 'hi': }\n      MANIFEST\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata).to be_empty\n      expect(catalog.recursive_metadata).to be_empty\n    end\n\n    it \"inlines windows file paths\", :if => Puppet.features.posix? do\n      catalog = compile_to_catalog(<<-MANIFEST, node)\n        file { 'c:/foo':\n          ensure => file,\n          source => '#{source}'\n        }\n      MANIFEST\n\n      metadata = stubs_file_metadata(checksum_type, checksum_value, 'modules/mymodule/files/config_file.txt')\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      compiler.send(:inline_metadata, catalog, checksum_type)\n      expect(catalog.metadata['c:/foo']).to eq(metadata)\n      expect(catalog.recursive_metadata).to be_empty\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/catalog/json_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/resource/catalog'\nrequire 'puppet/indirector/catalog/json'\n\ndescribe Puppet::Resource::Catalog::Json do\n  include PuppetSpec::Files\n\n  # This is it for local functionality\n  it \"should be registered with the catalog store indirection\" do\n    expect(Puppet::Resource::Catalog.indirection.terminus(:json)).\n      to be_an_instance_of described_class\n  end\n\n  describe \"when handling requests\" do\n    let(:binary) { \"\\xC0\\xFF\".force_encoding(Encoding::BINARY) }\n    let(:key)    { 'foo' }\n    let(:file)   { subject.path(key) }\n    let(:env)    { Puppet::Node::Environment.create(:testing, []) }\n    let(:catalog) do\n      catalog = Puppet::Resource::Catalog.new(key, env)\n      catalog.add_resource(Puppet::Resource.new(:file, '/tmp/a_file', :parameters => { :content => binary }))\n      catalog\n    end\n\n    before :each do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:server_datadir] = tmpdir('jsondir')\n      FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing'))\n      Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))\n    end\n\n    after :each do\n      Puppet.pop_context()\n    end\n\n    it 'saves a catalog containing binary content' do\n      request = subject.indirection.request(:save, key, catalog)\n\n      subject.save(request)\n    end\n\n    it 'finds a catalog containing binary content' do\n      request = subject.indirection.request(:save, key, catalog)\n      subject.save(request)\n\n      request = subject.indirection.request(:find, key, nil)\n      parsed_catalog = subject.find(request)\n\n      content = parsed_catalog.resource(:file, '/tmp/a_file')[:content]\n      expect(content.binary_buffer.bytes.to_a).to eq(binary.bytes.to_a)\n    end\n\n    it 'searches for catalogs contains binary content' do\n      request = subject.indirection.request(:save, key, catalog)\n      subject.save(request)\n\n      request = subject.indirection.request(:search, '*', nil)\n      parsed_catalogs = subject.search(request)\n\n      expect(parsed_catalogs.size).to eq(1)\n      content = parsed_catalogs.first.resource(:file, '/tmp/a_file')[:content]\n      expect(content.binary_buffer.bytes.to_a).to eq(binary.bytes.to_a)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/catalog/msgpack_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/resource/catalog'\nrequire 'puppet/indirector/catalog/msgpack'\n\ndescribe Puppet::Resource::Catalog::Msgpack, :if => Puppet.features.msgpack? do\n  # This is it for local functionality: we don't *do* anything else.\n  it \"should be registered with the catalog store indirection\" do\n    expect(Puppet::Resource::Catalog.indirection.terminus(:msgpack)).\n      to be_an_instance_of described_class\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/catalog/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/catalog/rest'\n\ndescribe Puppet::Resource::Catalog::Rest do\n  let(:certname) { 'ziggy' }\n  let(:uri) { %r{/puppet/v3/catalog/ziggy} }\n  let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n  let(:catalog) { Puppet::Resource::Catalog.new(certname) }\n\n  before :each do\n    Puppet[:server] = 'compiler.example.com'\n    Puppet[:serverport] = 8140\n\n    described_class.indirection.terminus_class = :rest\n  end\n\n  def catalog_response(catalog)\n    { body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime } }\n  end\n\n  it 'finds a catalog' do\n    stub_request(:post, uri).to_return(**catalog_response(catalog))\n\n    expect(described_class.indirection.find(certname)).to be_a(Puppet::Resource::Catalog)\n  end\n\n  it 'logs a notice when requesting a catalog' do\n    expect(Puppet).to receive(:notice).with(\"Requesting catalog from compiler.example.com:8140\")\n\n    stub_request(:post, uri).to_return(**catalog_response(catalog))\n\n    described_class.indirection.find(certname)\n  end\n\n  it 'logs a notice when the IP address is resolvable when requesting a catalog' do\n    allow(Resolv).to receive_message_chain(:getaddress).and_return('192.0.2.0')\n    expect(Puppet).to receive(:notice).with(\"Requesting catalog from compiler.example.com:8140 (192.0.2.0)\")\n\n    stub_request(:post, uri).to_return(**catalog_response(catalog))\n\n    described_class.indirection.find(certname)\n  end\n\n  it \"serializes the environment\" do\n    stub_request(:post, uri)\n      .with(query: hash_including('environment' => 'outerspace'))\n      .to_return(**catalog_response(catalog))\n\n    described_class.indirection.find(certname, environment: Puppet::Node::Environment.remote('outerspace'))\n  end\n\n  it \"passes 'check_environment'\" do\n    stub_request(:post, uri)\n      .with(body: hash_including('check_environment' => 'true'))\n      .to_return(**catalog_response(catalog))\n\n    described_class.indirection.find(certname, check_environment: true)\n  end\n\n  it 'constructs a catalog environment_instance' do\n    env = Puppet::Node::Environment.remote('outerspace')\n    catalog = Puppet::Resource::Catalog.new(certname, env)\n\n    stub_request(:post, uri).to_return(**catalog_response(catalog))\n\n    expect(described_class.indirection.find(certname).environment_instance).to eq(env)\n  end\n\n  it 'returns nil if the node does not exist' do\n    stub_request(:post, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n    expect(described_class.indirection.find(certname)).to be_nil\n  end\n\n  it 'raises if fail_on_404 is specified' do\n    stub_request(:post, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n    expect{\n      described_class.indirection.find(certname, fail_on_404: true)\n    }.to raise_error(Puppet::Error, %r{Find /puppet/v3/catalog/ziggy resulted in 404 with the message: {}})\n  end\n\n  it 'raises Net::HTTPError on 500' do\n    stub_request(:post, uri).to_return(status: 500)\n\n    expect{\n      described_class.indirection.find(certname)\n    }.to raise_error(Net::HTTPError, %r{Error 500 on SERVER: })\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/catalog/store_configs_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/node'\nrequire 'puppet/indirector/memory'\nrequire 'puppet/indirector/catalog/store_configs'\n\nclass Puppet::Resource::Catalog::StoreConfigsTesting < Puppet::Indirector::Memory\nend\n\ndescribe Puppet::Resource::Catalog::StoreConfigs do\n  after :each do\n    Puppet::Resource::Catalog.indirection.reset_terminus_class\n    Puppet::Resource::Catalog.indirection.cache_class = nil\n  end\n\n  it_should_behave_like \"a StoreConfigs terminus\"\nend\n"
  },
  {
    "path": "spec/unit/indirector/catalog/yaml_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/resource/catalog'\nrequire 'puppet/indirector/catalog/yaml'\n\ndescribe Puppet::Resource::Catalog::Yaml do\n  it \"should be a subclass of the Yaml terminus\" do\n    expect(Puppet::Resource::Catalog::Yaml.superclass).to equal(Puppet::Indirector::Yaml)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Resource::Catalog::Yaml.doc).not_to be_nil\n  end\n\n  it \"should be registered with the catalog store indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:catalog)\n    expect(Puppet::Resource::Catalog::Yaml.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :yaml\" do\n    expect(Puppet::Resource::Catalog::Yaml.name).to eq(:yaml)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/data_binding/hiera_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/data_binding/hiera'\n\ndescribe Puppet::DataBinding::Hiera do\n  it \"should have documentation\" do\n    expect(Puppet::DataBinding::Hiera.doc).not_to be_nil\n  end\n\n  it \"should be registered with the data_binding indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:data_binding)\n    expect(Puppet::DataBinding::Hiera.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :hiera\" do\n    expect(Puppet::DataBinding::Hiera.name).to eq(:hiera)\n  end\n\n  it_should_behave_like \"Hiera indirection\", Puppet::DataBinding::Hiera, my_fixture_dir\nend\n"
  },
  {
    "path": "spec/unit/indirector/data_binding/none_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/data_binding/none'\n\ndescribe Puppet::DataBinding::None do\n  it \"should be a subclass of the None terminus\" do\n    expect(Puppet::DataBinding::None.superclass).to equal(Puppet::Indirector::None)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::DataBinding::None.doc).not_to be_nil\n  end\n\n  it \"should be registered with the data_binding indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:data_binding)\n    expect(Puppet::DataBinding::None.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :none\" do\n    expect(Puppet::DataBinding::None.name).to eq(:none)\n  end\n\n  describe \"the behavior of the find method\" do\n    it \"should just throw :no_such_key\" do\n      data_binding = Puppet::DataBinding::None.new\n      expect { data_binding.find('fake_request') }.to throw_symbol(:no_such_key)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/direct_file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/direct_file_server'\n\ndescribe Puppet::Indirector::DirectFileServer do\n  before(:each) do\n    class Puppet::FileTestModel\n      extend Puppet::Indirector\n      indirects :file_test_model\n      attr_accessor :path\n      def initialize(path = '/', options = {})\n        @path = path\n        @options = options\n      end\n    end\n\n    class Puppet::FileTestModel::DirectFileServer < Puppet::Indirector::DirectFileServer\n    end\n\n    Puppet::FileTestModel.indirection.terminus_class = :direct_file_server\n  end\n\n  let(:path) { File.expand_path('/my/local') }\n  let(:terminus) { Puppet::FileTestModel.indirection.terminus(:direct_file_server) }\n  let(:indirection) { Puppet::FileTestModel.indirection }\n  let(:model) { Puppet::FileTestModel }\n\n  after(:each) do\n    Puppet::FileTestModel.indirection.delete\n    Puppet.send(:remove_const, :FileTestModel)\n  end\n\n  describe \"when finding a single file\" do\n    it \"should return nil if the file does not exist\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(false)\n      expect(indirection.find(path)).to be_nil\n    end\n\n    it \"should return a Content instance created with the full path to the file if the file exists\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n      mycontent = double('content', :collect => nil)\n      expect(mycontent).to receive(:collect)\n      expect(model).to receive(:new).and_return(mycontent)\n      expect(indirection.find(path)).to eq(mycontent)\n    end\n  end\n\n  describe \"when creating the instance for a single found file\" do\n    let(:data) { double('content', collect: nil) }\n\n    before(:each) do\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n    end\n\n    it \"should pass the full path to the instance\" do\n      expect(model).to receive(:new).with(path, anything).and_return(data)\n      indirection.find(path)\n    end\n\n    it \"should pass the :links setting on to the created Content instance if the file exists and there is a value for :links\" do\n      expect(model).to receive(:new).and_return(data)\n      expect(data).to receive(:links=).with(:manage)\n\n      indirection.find(path, links: :manage)\n    end\n\n    it \"should set 'checksum_type' on the instances if it is set in the request options\" do\n      expect(model).to receive(:new).and_return(data)\n      expect(data).to receive(:checksum_type=).with(:checksum)\n\n      indirection.find(path, checksum_type: :checksum)\n    end\n  end\n\n  describe \"when searching for multiple files\" do\n    it \"should return nil if the file does not exist\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(false)\n      expect(indirection.search(path)).to be_nil\n    end\n\n    it \"should pass the original request to :path2instances\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n      expect(terminus).to receive(:path2instances).with(anything, path)\n      indirection.search(path)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/envelope_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/envelope'\n\ndescribe Puppet::Indirector::Envelope do\n  before do\n    @instance = Object.new\n    @instance.extend(Puppet::Indirector::Envelope)\n  end\n\n  describe \"when testing if it is expired\" do\n    it \"should return false if there is no expiration set\" do\n      expect(@instance).not_to be_expired\n    end\n\n    it \"should return true if the current date is after the expiration date\" do\n      @instance.expiration = Time.now - 10\n      expect(@instance).to be_expired\n    end\n\n    it \"should return false if the current date is prior to the expiration date\" do\n      @instance.expiration = Time.now + 10\n      expect(@instance).not_to be_expired\n    end\n\n    it \"should return false if the current date is equal to the expiration date\" do\n      now = Time.now\n      allow(Time).to receive(:now).and_return(now)\n      @instance.expiration = now\n      expect(@instance).not_to be_expired\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/exec_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/exec'\n\ndescribe Puppet::Indirector::Exec do\n  before :all do\n    class Puppet::ExecTestModel\n      extend Puppet::Indirector\n      indirects :exec_test_model\n    end\n\n    class Puppet::ExecTestModel::Exec < Puppet::Indirector::Exec\n      attr_accessor :command\n    end\n\n    Puppet::ExecTestModel.indirection.terminus_class = :exec\n  end\n\n  after(:all) do\n    Puppet::ExecTestModel.indirection.delete\n    Puppet.send(:remove_const, :ExecTestModel)\n  end\n\n  let(:terminus) { Puppet::ExecTestModel.indirection.terminus(:exec) }\n  let(:indirection) { Puppet::ExecTestModel.indirection }\n  let(:model) { Puppet::ExecTestModel }\n  let(:path) { File.expand_path('/echo') }\n  let(:arguments) { {:failonfail => true, :combine => false } }\n\n  before(:each) { terminus.command = [path] }\n\n  it \"should throw an exception if the command is not an array\" do\n    terminus.command = path\n    expect { indirection.find('foo') }.to raise_error(Puppet::DevError)\n  end\n\n  it \"should throw an exception if the command is not fully qualified\" do\n    terminus.command = [\"mycommand\"]\n    expect { indirection.find('foo') }.to raise_error(ArgumentError)\n  end\n\n  it \"should execute the command with the object name as the only argument\" do\n    expect(terminus).to receive(:execute).with([path, 'foo'], arguments)\n    indirection.find('foo')\n  end\n\n  it \"should return the output of the script\" do\n    expect(terminus).to receive(:execute).with([path, 'foo'], arguments).and_return(\"whatever\")\n    expect(indirection.find('foo')).to eq(\"whatever\")\n  end\n\n  it \"should return nil when the command produces no output\" do\n    expect(terminus).to receive(:execute).with([path, 'foo'], arguments).and_return(nil)\n    expect(indirection.find('foo')).to be_nil\n  end\n\n  it \"should raise an exception if there's an execution failure\" do\n    expect(terminus).to receive(:execute).with([path, 'foo'], arguments).and_raise(Puppet::ExecutionFailure.new(\"message\"))\n    expect {\n      indirection.find('foo')\n    }.to raise_exception(Puppet::Error, 'Failed to find foo via exec: message')\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/face_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/face'\n\ndescribe Puppet::Indirector::Face do\n  subject do\n    instance = Puppet::Indirector::Face.new(:test, '0.0.1')\n    indirection = double('indirection',\n                       :name => :stub_indirection,\n                       :reset_terminus_class => nil)\n    allow(instance).to receive(:indirection).and_return(indirection)\n    instance\n  end\n\n  it \"should be able to return a list of indirections\" do\n    expect(Puppet::Indirector::Face.indirections).to be_include(\"catalog\")\n  end\n\n  it \"should return the sorted to_s list of terminus classes\" do\n    expect(Puppet::Indirector::Terminus).to receive(:terminus_classes).and_return([\n      :yaml,\n      :compiler,\n      :rest\n   ])\n    expect(Puppet::Indirector::Face.terminus_classes(:catalog)).to eq([\n      'compiler',\n      'rest',\n      'yaml'\n    ])\n  end\n\n  describe \"as an instance\" do\n    it \"should be able to determine its indirection\" do\n      # Loading actions here can get, um, complicated\n      expect(Puppet::Indirector::Face.new(:catalog, '0.0.1').indirection).to equal(Puppet::Resource::Catalog.indirection)\n    end\n  end\n\n  [:find, :search, :save, :destroy].each do |method|\n    def params(method, options)\n      if method == :save\n        [nil, options]\n      else\n        [options]\n      end\n    end\n\n    it \"should define a '#{method}' action\" do\n      expect(Puppet::Indirector::Face).to be_action(method)\n    end\n\n    it \"should call the indirection method with options when the '#{method}' action is invoked\" do\n      expect(subject.indirection).to receive(method).with(:test, *params(method, {}))\n      subject.send(method, :test)\n    end\n\n    it \"should forward passed options\" do\n      expect(subject.indirection).to receive(method).with(:test, *params(method, {}))\n      subject.send(method, :test, {})\n    end\n  end\n\n  it \"should default key to certname for find action\" do\n    expect(subject.indirection).to receive(:find).with(Puppet[:certname], {})\n    subject.send(:find, {})\n  end\n\n  it \"should be able to override its indirection name\" do\n    subject.set_indirection_name :foo\n    expect(subject.indirection_name).to eq(:foo)\n  end\n\n  it \"should be able to set its terminus class\" do\n    expect(subject.indirection).to receive(:terminus_class=).with(:myterm)\n    subject.set_terminus(:myterm)\n  end\n\n  it \"should define a class-level 'info' action\" do\n    expect(Puppet::Indirector::Face).to be_action(:info)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/facts/facter_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/facts/facter'\n\nclass Puppet::Node::Facts::Facter::MyCollection < Hash; end\n\ndescribe Puppet::Node::Facts::Facter do\n  FS = Puppet::FileSystem\n\n  it \"should be a subclass of the Code terminus\" do\n    expect(Puppet::Node::Facts::Facter.superclass).to equal(Puppet::Indirector::Code)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Node::Facts::Facter.doc).not_to be_nil\n  end\n\n  it \"should be registered with the configuration store indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:facts)\n    expect(Puppet::Node::Facts::Facter.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :facter\" do\n    expect(Puppet::Node::Facts::Facter.name).to eq(:facter)\n  end\n\n  before :each do\n    @facter = Puppet::Node::Facts::Facter.new\n    allow(Facter).to receive(:to_hash).and_return({})\n    @name = \"me\"\n    @request = double('request', :key => @name)\n    @environment = double('environment')\n    allow(@request).to receive(:environment).and_return(@environment)\n    allow(@request).to receive(:options).and_return({})\n    allow(@request.environment).to receive(:modules).and_return([])\n    allow(@request.environment).to receive(:modulepath).and_return([])\n  end\n\n  describe 'when finding facts' do\n    it 'should reset facts' do\n      expect(Facter).to receive(:reset).ordered\n      expect(Puppet::Node::Facts::Facter).to receive(:setup_search_paths).ordered\n      @facter.find(@request)\n    end\n\n    it 'should add the puppetversion and agent_specified_environment facts' do\n      expect(Facter).to receive(:reset).ordered\n      expect(Facter).to receive(:add).with(:puppetversion)\n      expect(Facter).to receive(:add).with(:agent_specified_environment)\n      @facter.find(@request)\n    end\n\n    it 'should include external facts' do\n      expect(Facter).to receive(:reset).ordered\n      expect(Puppet::Node::Facts::Facter).to receive(:setup_external_search_paths).ordered\n      expect(Puppet::Node::Facts::Facter).to receive(:setup_search_paths).ordered\n      @facter.find(@request)\n    end\n\n    it \"should return a Facts instance\" do\n      expect(@facter.find(@request)).to be_instance_of(Puppet::Node::Facts)\n    end\n\n    it \"should return a Facts instance with the provided key as the name\" do\n      expect(@facter.find(@request).name).to eq(@name)\n    end\n\n    it \"should return the Facter facts as the values in the Facts instance\" do\n      expect(Facter).to receive(:resolve).and_return(\"one\" => \"two\")\n      facts = @facter.find(@request)\n      expect(facts.values[\"one\"]).to eq(\"two\")\n    end\n\n    it \"should add local facts\" do\n      facts = Puppet::Node::Facts.new(\"foo\")\n      expect(Puppet::Node::Facts).to receive(:new).and_return(facts)\n      expect(facts).to receive(:add_local_facts)\n\n      @facter.find(@request)\n    end\n\n    it \"should sanitize facts\" do\n      facts = Puppet::Node::Facts.new(\"foo\")\n      expect(Puppet::Node::Facts).to receive(:new).and_return(facts)\n      expect(facts).to receive(:sanitize)\n\n      @facter.find(@request)\n    end\n\n    it \"can exclude legacy facts\" do\n      Puppet[:include_legacy_facts] = false\n\n      expect(Puppet.runtime[:facter]).to receive(:resolve).with('')\n        .and_return({'kernelversion' => '1.2.3'})\n\n      values = @facter.find(@request).values\n      expect(values).to be_an_instance_of(Hash)\n      expect(values).to include({'kernelversion' => '1.2.3'})\n    end\n\n    it \"can exclude legacy facts using buggy facter implementations that return a Hash subclass\" do\n      Puppet[:include_legacy_facts] = false\n\n      collection = Puppet::Node::Facts::Facter::MyCollection[\"kernelversion\" => '1.2.3']\n      expect(Puppet.runtime[:facter]).to receive(:resolve).with('')\n        .and_return(collection)\n\n      values = @facter.find(@request).values\n      expect(values).to be_an_instance_of(Hash)\n      expect(values).to include({'kernelversion' => '1.2.3'})\n    end\n  end\n\n  it 'should fail when saving facts' do\n    expect { @facter.save(@facts) }.to raise_error(Puppet::DevError)\n  end\n\n  it 'should fail when destroying facts' do\n    expect { @facter.destroy(@facts) }.to raise_error(Puppet::DevError)\n  end\n\n  describe 'when setting up search paths' do\n    let(:factpath1) { File.expand_path 'one' }\n    let(:factpath2) { File.expand_path 'two' }\n    let(:factpath) { [factpath1, factpath2].join(File::PATH_SEPARATOR) }\n    let(:modulepath) { File.expand_path 'module/foo' }\n    let(:modulelibfacter) { File.expand_path 'module/foo/lib/facter' }\n    let(:modulepluginsfacter) { File.expand_path 'module/foo/plugins/facter' }\n\n    before :each do\n      expect(FileTest).to receive(:directory?).with(factpath1).and_return(true)\n      expect(FileTest).to receive(:directory?).with(factpath2).and_return(true)\n      allow(@request.environment).to receive(:modulepath).and_return([modulepath])\n      allow(@request).to receive(:options).and_return({})\n      expect(Dir).to receive(:glob).with(\"#{modulepath}/*/lib/facter\").and_return([modulelibfacter])\n      expect(Dir).to receive(:glob).with(\"#{modulepath}/*/plugins/facter\").and_return([modulepluginsfacter])\n\n      Puppet[:factpath] = factpath\n    end\n\n    it 'should skip files' do\n      expect(FileTest).to receive(:directory?).with(modulelibfacter).and_return(false)\n      expect(FileTest).to receive(:directory?).with(modulepluginsfacter).and_return(false)\n      expect(Facter).to receive(:search).with(factpath1, factpath2)\n      Puppet::Node::Facts::Facter.setup_search_paths @request\n    end\n\n    it 'should add directories' do\n      expect(FileTest).to receive(:directory?).with(modulelibfacter).and_return(true)\n      expect(FileTest).to receive(:directory?).with(modulepluginsfacter).and_return(true)\n      expect(Facter).to receive(:search).with(modulelibfacter, modulepluginsfacter, factpath1, factpath2)\n      Puppet::Node::Facts::Facter.setup_search_paths @request\n    end\n  end\n\n  describe 'when setting up external search paths' do\n    let(:pluginfactdest) { File.expand_path 'plugin/dest' }\n    let(:modulepath) { File.expand_path 'module/foo' }\n    let(:modulefactsd) { File.expand_path 'module/foo/facts.d'  }\n\n    before :each do\n      expect(FileTest).to receive(:directory?).with(pluginfactdest).and_return(true)\n      mod = Puppet::Module.new('foo', modulepath, @request.environment)\n      allow(@request.environment).to receive(:modules).and_return([mod])\n      Puppet[:pluginfactdest] = pluginfactdest\n    end\n\n    it 'should skip files' do\n      expect(File).to receive(:directory?).with(modulefactsd).and_return(false)\n      expect(Facter).to receive(:search_external).with([pluginfactdest])\n      Puppet::Node::Facts::Facter.setup_external_search_paths @request\n    end\n\n    it 'should add directories' do\n      expect(File).to receive(:directory?).with(modulefactsd).and_return(true)\n      expect(Facter).to receive(:search_external).with([modulefactsd, pluginfactdest])\n      Puppet::Node::Facts::Facter.setup_external_search_paths @request\n    end\n  end\n\n  describe 'when :resolve_options is true' do\n    let(:options) { { resolve_options: true, user_query: [\"os\", \"timezone\"] } }\n    let(:facts) { Puppet::Node::Facts.new(\"foo\") }\n\n    before :each do\n      allow(@request).to receive(:options).and_return(options)\n      allow(Puppet::Node::Facts).to receive(:new).and_return(facts)\n      allow(facts).to receive(:add_local_facts)\n    end\n\n    it 'should call Facter.resolve method' do\n      expect(Facter).to receive(:resolve).with(\"os timezone\")\n      @facter.find(@request)\n    end\n\n    it 'should NOT add local facts' do\n      expect(facts).not_to receive(:add_local_facts)\n\n      @facter.find(@request)\n    end\n\n    context 'when --show-legacy flag is present' do\n      let(:options) { { resolve_options: true, user_query: [\"os\", \"timezone\"], show_legacy: true } }\n\n      it 'should call Facter.resolve method with show-legacy' do\n        expect(Facter).to receive(:resolve).with(\"os timezone --show-legacy\")\n        @facter.find(@request)\n      end\n    end\n\n    context 'when --timing flag is present' do\n      let(:options) { { resolve_options: true, user_query: [\"os\", \"timezone\"], timing: true } }\n\n      it 'calls Facter.resolve with --timing' do\n        expect(Facter).to receive(:resolve).with(\"os timezone --timing\")\n        @facter.find(@request)\n      end\n    end\n\n    describe 'when Facter version is lower than 4.0.40' do\n      before :each do\n        allow(Facter).to receive(:respond_to?).and_return(false)\n        allow(Facter).to receive(:respond_to?).with(:resolve).and_return(false)\n      end\n\n      it 'raises an error' do\n        expect { @facter.find(@request) }.to raise_error(Puppet::Error, \"puppet facts show requires version 4.0.40 or greater of Facter.\")\n      end\n    end\n\n    describe 'when setting up external search paths' do\n      let(:options) { { resolve_options: true, user_query: [\"os\", \"timezone\"], external_dir: 'some/dir' } }\n      let(:pluginfactdest) { File.expand_path 'plugin/dest' }\n      let(:modulepath) { File.expand_path 'module/foo' }\n      let(:modulefactsd) { File.expand_path 'module/foo/facts.d'  }\n\n      before :each do\n        expect(FileTest).to receive(:directory?).with(pluginfactdest).and_return(true)\n        mod = Puppet::Module.new('foo', modulepath, @request.environment)\n        allow(@request.environment).to receive(:modules).and_return([mod])\n        Puppet[:pluginfactdest] = pluginfactdest\n      end\n\n      it 'should skip files' do\n        expect(File).to receive(:directory?).with(modulefactsd).and_return(false)\n        expect(Facter).to receive(:search_external).with([pluginfactdest, options[:external_dir]])\n        Puppet::Node::Facts::Facter.setup_external_search_paths @request\n      end\n\n      it 'should add directories' do\n        expect(File).to receive(:directory?).with(modulefactsd).and_return(true)\n        expect(Facter).to receive(:search_external).with([modulefactsd, pluginfactdest, options[:external_dir]])\n        Puppet::Node::Facts::Facter.setup_external_search_paths @request\n      end\n    end\n\n    describe 'when setting up search paths' do\n      let(:factpath1) { File.expand_path 'one' }\n      let(:factpath2) { File.expand_path 'two' }\n      let(:factpath) { [factpath1, factpath2].join(File::PATH_SEPARATOR) }\n      let(:modulepath) { File.expand_path 'module/foo' }\n      let(:modulelibfacter) { File.expand_path 'module/foo/lib/facter' }\n      let(:modulepluginsfacter) { File.expand_path 'module/foo/plugins/facter' }\n      let(:options) { { resolve_options: true, custom_dir: 'some/dir' } }\n\n      before :each do\n        expect(FileTest).to receive(:directory?).with(factpath1).and_return(true)\n        expect(FileTest).to receive(:directory?).with(factpath2).and_return(true)\n        allow(@request.environment).to receive(:modulepath).and_return([modulepath])\n        expect(Dir).to receive(:glob).with(\"#{modulepath}/*/lib/facter\").and_return([modulelibfacter])\n        expect(Dir).to receive(:glob).with(\"#{modulepath}/*/plugins/facter\").and_return([modulepluginsfacter])\n\n        Puppet[:factpath] = factpath\n      end\n\n      it 'should skip files' do\n        expect(FileTest).to receive(:directory?).with(modulelibfacter).and_return(false)\n        expect(FileTest).to receive(:directory?).with(modulepluginsfacter).and_return(false)\n        expect(Facter).to receive(:search).with(factpath1, factpath2, options[:custom_dir])\n        Puppet::Node::Facts::Facter.setup_search_paths @request\n      end\n\n      it 'should add directories' do\n        expect(FileTest).to receive(:directory?).with(modulelibfacter).and_return(true)\n        expect(FileTest).to receive(:directory?).with(modulepluginsfacter).and_return(false)\n        expect(Facter).to receive(:search).with(modulelibfacter, factpath1, factpath2, options[:custom_dir])\n        Puppet::Node::Facts::Facter.setup_search_paths @request\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/facts/json_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/node/facts'\nrequire 'puppet/indirector/facts/json'\n\ndef dir_containing_json_facts(hash)\n  jsondir = tmpdir('json_facts')\n\n  Puppet[:client_datadir] = jsondir\n  dir = File.join(jsondir, 'facts')\n  Dir.mkdir(dir)\n  hash.each_pair do |file, facts|\n    File.open(File.join(dir, file), 'wb') do |f|\n      f.write(JSON.dump(facts))\n    end\n  end\nend\n\ndescribe Puppet::Node::Facts::Json do\n  include PuppetSpec::Files\n\n  it \"should be a subclass of the Json terminus\" do\n    expect(Puppet::Node::Facts::Json.superclass).to equal(Puppet::Indirector::JSON)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Node::Facts::Json.doc).not_to be_nil\n    expect(Puppet::Node::Facts::Json.doc).not_to be_empty\n  end\n\n  it \"should be registered with the facts indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:facts)\n    expect(Puppet::Node::Facts::Json.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :json\" do\n    expect(Puppet::Node::Facts::Json.name).to eq(:json)\n  end\n\n  it \"should allow network requests\" do\n    # Doesn't allow json as a network format, but allows `puppet facts upload`\n    # to update the JSON cache on a master.\n    expect(Puppet::Node::Facts::Json.new.allow_remote_requests?).to be(true)\n  end\n\n  describe \"#search\" do\n    def assert_search_matches(matching, nonmatching, query)\n      request = Puppet::Indirector::Request.new(:inventory, :search, nil, nil, query)\n\n      dir_containing_json_facts(matching.merge(nonmatching))\n\n      results = Puppet::Node::Facts::Json.new.search(request)\n      expect(results).to match_array(matching.values.map {|facts| facts.name})\n    end\n\n    it \"should return node names that match the search query options\" do\n      assert_search_matches({\n          'matching.json'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\", 'processor_count' => '4'),\n          'matching1.json' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"i386\", 'processor_count' => '4', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '4'),\n          \"nonmatching1.json\" => Puppet::Node::Facts.new(\"nonmatchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '5'),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '5'),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\",                              'processor_count' => '4'),\n        },\n        {'facts.architecture' => 'i386', 'facts.processor_count' => '4'}\n      )\n    end\n\n    it \"should return empty array when no nodes match the search query options\" do\n      assert_search_matches({}, {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '10'),\n          \"nonmatching1.json\" => Puppet::Node::Facts.new(\"nonmatchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '5'),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '5'),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\",                              'processor_count' => '4'),\n        },\n        {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the greater than operator\" do\n      assert_search_matches({\n          'matching.json'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '5'),\n          'matching1.json' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '10', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '4'),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '3'),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                       ),\n        },\n        {'facts.processor_count.gt' => '4'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the less than operator\" do\n      assert_search_matches({\n          'matching.json'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '5'),\n          'matching1.json' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '30', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '50' ),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '100'),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                         ),\n        },\n        {'facts.processor_count.lt' => '50'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the less than or equal to operator\" do\n      assert_search_matches({\n          'matching.json'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '5'),\n          'matching1.json' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '50', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '100' ),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '5000'),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                          ),\n        },\n        {'facts.processor_count.le' => '50'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the greater than or equal to operator\" do\n      assert_search_matches({\n          'matching.json'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '100'),\n          'matching1.json' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '50', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '40'),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '9' ),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                        ),\n        },\n        {'facts.processor_count.ge' => '50'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the not equal operator\" do\n      assert_search_matches({\n          'matching.json'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => 'arm'                           ),\n          'matching1.json' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => 'powerpc', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.json\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"i386\"                           ),\n          \"nonmatching2.json\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\", 'processor_count' => '9' ),\n          \"nonmatching3.json\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                     ),\n        },\n        {'facts.architecture.ne' => 'i386'}\n      )\n    end\n\n    def apply_timestamp(facts, timestamp)\n      facts.timestamp = timestamp\n      facts\n    end\n\n    it \"should be able to query based on meta.timestamp.gt\" do\n      assert_search_matches({\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n        },\n        {\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {'meta.timestamp.gt' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.le\" do\n      assert_search_matches({\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n        },\n        {'meta.timestamp.le' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.lt\" do\n      assert_search_matches({\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {'meta.timestamp.lt' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.ge\" do\n      assert_search_matches({\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {'meta.timestamp.ge' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.eq\" do\n      assert_search_matches({\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {'meta.timestamp.eq' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp\" do\n      assert_search_matches({\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {'meta.timestamp' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.ne\" do\n      assert_search_matches({\n          '2010-11-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-01.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {\n          '2010-10-15.json' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {'meta.timestamp.ne' => '2010-10-15'}\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/facts/network_device_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/network_device'\nrequire 'puppet/indirector/facts/network_device'\n\ndescribe Puppet::Node::Facts::NetworkDevice do\n  it \"should be a subclass of the Code terminus\" do\n    expect(Puppet::Node::Facts::NetworkDevice.superclass).to equal(Puppet::Indirector::Code)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Node::Facts::NetworkDevice.doc).not_to be_nil\n  end\n\n  it \"should be registered with the configuration store indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:facts)\n    expect(Puppet::Node::Facts::NetworkDevice.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :facter\" do\n    expect(Puppet::Node::Facts::NetworkDevice.name).to eq(:network_device)\n  end\nend\n\ndescribe Puppet::Node::Facts::NetworkDevice do\n  before :each do\n    @remote_device = double('remote_device', :facts => {})\n    allow(Puppet::Util::NetworkDevice).to receive(:current).and_return(@remote_device)\n    @device = Puppet::Node::Facts::NetworkDevice.new\n    @name = \"me\"\n    @request = double('request', :key => @name)\n  end\n\n  describe Puppet::Node::Facts::NetworkDevice, \" when finding facts\" do\n    it \"should return a Facts instance\" do\n      expect(@device.find(@request)).to be_instance_of(Puppet::Node::Facts)\n    end\n\n    it \"should return a Facts instance with the provided key as the name\" do\n      expect(@device.find(@request).name).to eq(@name)\n    end\n\n    it \"should return the device facts as the values in the Facts instance\" do\n      expect(@remote_device).to receive(:facts).and_return(\"one\" => \"two\")\n      facts = @device.find(@request)\n      expect(facts.values[\"one\"]).to eq(\"two\")\n    end\n\n    it \"should add local facts\" do\n      facts = Puppet::Node::Facts.new(\"foo\")\n      expect(Puppet::Node::Facts).to receive(:new).and_return(facts)\n      expect(facts).to receive(:add_local_facts)\n\n      @device.find(@request)\n    end\n\n    it \"should sanitize facts\" do\n      facts = Puppet::Node::Facts.new(\"foo\")\n      expect(Puppet::Node::Facts).to receive(:new).and_return(facts)\n      expect(facts).to receive(:sanitize)\n\n      @device.find(@request)\n    end\n  end\n\n  describe Puppet::Node::Facts::NetworkDevice, \" when saving facts\" do\n    it \"should fail\" do\n      expect { @device.save(@facts) }.to raise_error(Puppet::DevError)\n    end\n  end\n\n  describe Puppet::Node::Facts::NetworkDevice, \" when destroying facts\" do\n    it \"should fail\" do\n      expect { @device.destroy(@facts) }.to raise_error(Puppet::DevError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/facts/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/facts/rest'\n\ndescribe Puppet::Node::Facts::Rest do\n  let(:certname) { 'ziggy' }\n  let(:uri) { %r{/puppet/v3/facts/ziggy} }\n  let(:facts) { Puppet::Node::Facts.new(certname, test_fact: 'test value') }\n\n  before do\n    Puppet[:server] = 'compiler.example.com'\n    Puppet[:serverport] = 8140\n\n    described_class.indirection.terminus_class = :rest\n  end\n\n  describe '#find' do\n    let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n\n    def facts_response(facts)\n      { body: formatter.render(facts), headers: {'Content-Type' => formatter.mime } }\n    end\n\n    it 'finds facts' do\n      stub_request(:get, uri).to_return(**facts_response(facts))\n\n      expect(described_class.indirection.find(certname)).to be_a(Puppet::Node::Facts)\n    end\n\n    it \"serializes the environment\" do\n      stub_request(:get, uri)\n        .with(query: hash_including('environment' => 'outerspace'))\n        .to_return(**facts_response(facts))\n\n      described_class.indirection.find(certname, environment: Puppet::Node::Environment.remote('outerspace'))\n    end\n\n    it 'returns nil if the facts do not exist' do\n      stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n      expect(described_class.indirection.find(certname)).to be_nil\n    end\n\n    it 'raises if fail_on_404 is specified' do\n      stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n      expect{\n        described_class.indirection.find(certname, fail_on_404: true)\n      }.to raise_error(Puppet::Error, %r{Find /puppet/v3/facts/ziggy resulted in 404 with the message: {}})\n    end\n\n    it 'raises Net::HTTPError on 500' do\n      stub_request(:get, uri).to_return(status: 500)\n\n      expect{\n        described_class.indirection.find(certname)\n      }.to raise_error(Net::HTTPError, %r{Error 500 on SERVER: })\n    end\n  end\n\n  describe '#save' do\n    it 'returns nil on success' do\n      stub_request(:put, %r{/puppet/v3/facts})\n        .to_return(status: 200, headers: { 'Content-Type' => 'application/json'}, body: '')\n\n      expect(described_class.indirection.save(facts)).to be_nil\n    end\n\n    it \"serializes the environment\" do\n      stub_request(:put, uri)\n        .with(query: hash_including('environment' => 'outerspace'))\n        .to_return(status: 200, headers: { 'Content-Type' => 'application/json'}, body: '')\n\n      described_class.indirection.save(facts, nil, environment: Puppet::Node::Environment.remote('outerspace'))\n    end\n\n    it 'raises if options are specified' do\n      expect {\n        described_class.indirection.save(facts, nil, foo: :bar)\n      }.to raise_error(ArgumentError, /PUT does not accept options/)\n    end\n\n    it 'raises with HTTP 404' do\n      stub_request(:put, %r{/puppet/v3/facts}).to_return(status: 404)\n\n      expect {\n        described_class.indirection.save(facts)\n      }.to raise_error(Net::HTTPError, /Error 404 on SERVER/)\n    end\n\n    it 'raises with HTTP 500' do\n      stub_request(:put, %r{/puppet/v3/facts}).to_return(status: 500)\n\n      expect {\n        described_class.indirection.save(facts)\n      }.to raise_error(Net::HTTPError, /Error 500 on SERVER/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/facts/store_configs_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/node'\nrequire 'puppet/indirector/memory'\nrequire 'puppet/indirector/facts/store_configs'\n\nclass Puppet::Node::Facts::StoreConfigsTesting < Puppet::Indirector::Memory\nend\n\ndescribe Puppet::Node::Facts::StoreConfigs do\n  after :all do\n    Puppet::Node::Facts.indirection.reset_terminus_class\n    Puppet::Node::Facts.indirection.cache_class = nil\n  end\n\n  it_should_behave_like \"a StoreConfigs terminus\"\nend\n"
  },
  {
    "path": "spec/unit/indirector/facts/yaml_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/node/facts'\nrequire 'puppet/indirector/facts/yaml'\n\ndef dir_containing_facts(hash)\n  yamldir = tmpdir('yaml_facts')\n\n  Puppet[:clientyamldir] = yamldir\n  dir = File.join(yamldir, 'facts')\n  Dir.mkdir(dir)\n  hash.each_pair do |file, facts|\n    File.open(File.join(dir, file), 'wb') do |f|\n      f.write(YAML.dump(facts))\n    end\n  end\nend\n\ndescribe Puppet::Node::Facts::Yaml do\n  include PuppetSpec::Files\n\n  it \"should be a subclass of the Yaml terminus\" do\n    expect(Puppet::Node::Facts::Yaml.superclass).to equal(Puppet::Indirector::Yaml)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Node::Facts::Yaml.doc).not_to be_nil\n    expect(Puppet::Node::Facts::Yaml.doc).not_to be_empty\n  end\n\n  it \"should be registered with the facts indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:facts)\n    expect(Puppet::Node::Facts::Yaml.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :yaml\" do\n    expect(Puppet::Node::Facts::Yaml.name).to eq(:yaml)\n  end\n\n  it \"should allow network requests\" do\n    # Doesn't allow yaml as a network format, but allows `puppet facts upload`\n    # to update the YAML cache on a master.\n    expect(Puppet::Node::Facts::Yaml.new.allow_remote_requests?).to be(true)\n  end\n\n  describe \"#search\" do\n    def assert_search_matches(matching, nonmatching, query)\n      request = Puppet::Indirector::Request.new(:inventory, :search, nil, nil, query)\n\n      dir_containing_facts(matching.merge(nonmatching))\n\n      results = Puppet::Node::Facts::Yaml.new.search(request)\n      expect(results).to match_array(matching.values.map {|facts| facts.name})\n    end\n\n    it \"should return node names that match the search query options\" do\n      assert_search_matches({\n          'matching.yaml'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\", 'processor_count' => '4'),\n          'matching1.yaml' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"i386\", 'processor_count' => '4', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '4'),\n          \"nonmatching1.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '5'),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '5'),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\",                              'processor_count' => '4'),\n        },\n        {'facts.architecture' => 'i386', 'facts.processor_count' => '4'}\n      )\n    end\n\n    it \"should return empty array when no nodes match the search query options\" do\n      assert_search_matches({}, {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '10'),\n          \"nonmatching1.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '5'),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '5'),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\",                              'processor_count' => '4'),\n        },\n        {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the greater than operator\" do\n      assert_search_matches({\n          'matching.yaml'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '5'),\n          'matching1.yaml' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '10', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '4'),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '3'),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                       ),\n        },\n        {'facts.processor_count.gt' => '4'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the less than operator\" do\n      assert_search_matches({\n          'matching.yaml'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '5'),\n          'matching1.yaml' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '30', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '50' ),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '100'),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                         ),\n        },\n        {'facts.processor_count.lt' => '50'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the less than or equal to operator\" do\n      assert_search_matches({\n          'matching.yaml'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '5'),\n          'matching1.yaml' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '50', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '100' ),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '5000'),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                          ),\n        },\n        {'facts.processor_count.le' => '50'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the greater than or equal to operator\" do\n      assert_search_matches({\n          'matching.yaml'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => \"i386\",    'processor_count' => '100'),\n          'matching1.yaml' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => \"powerpc\", 'processor_count' => '50', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"powerpc\", 'processor_count' => '40'),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\",    'processor_count' => '9' ),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                        ),\n        },\n        {'facts.processor_count.ge' => '50'}\n      )\n    end\n\n    it \"should return node names that match the search query options with the not equal operator\" do\n      assert_search_matches({\n          'matching.yaml'  => Puppet::Node::Facts.new(\"matchingnode\",  \"architecture\" => 'arm'                           ),\n          'matching1.yaml' => Puppet::Node::Facts.new(\"matchingnode1\", \"architecture\" => 'powerpc', 'randomfact' => 'foo')\n        },\n        {\n          \"nonmatching.yaml\"  => Puppet::Node::Facts.new(\"nonmatchingnode\",  \"architecture\" => \"i386\"                           ),\n          \"nonmatching2.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode2\", \"architecture\" => \"i386\", 'processor_count' => '9' ),\n          \"nonmatching3.yaml\" => Puppet::Node::Facts.new(\"nonmatchingnode3\"                                                     ),\n        },\n        {'facts.architecture.ne' => 'i386'}\n      )\n    end\n\n    def apply_timestamp(facts, timestamp)\n      facts.timestamp = timestamp\n      facts\n    end\n\n    it \"should be able to query based on meta.timestamp.gt\" do\n      assert_search_matches({\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n        },\n        {\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {'meta.timestamp.gt' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.le\" do\n      assert_search_matches({\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n        },\n        {'meta.timestamp.le' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.lt\" do\n      assert_search_matches({\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {'meta.timestamp.lt' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.ge\" do\n      assert_search_matches({\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {'meta.timestamp.ge' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.eq\" do\n      assert_search_matches({\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {'meta.timestamp.eq' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp\" do\n      assert_search_matches({\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {'meta.timestamp' => '2010-10-15'}\n      )\n    end\n\n    it \"should be able to query based on meta.timestamp.ne\" do\n      assert_search_matches({\n          '2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-01\", {}), Time.parse(\"2010-11-01\")),\n          '2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-11-10\", {}), Time.parse(\"2010-11-10\")),\n          '2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-01\", {}), Time.parse(\"2010-10-01\")),\n          '2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-10\", {}), Time.parse(\"2010-10-10\")),\n        },\n        {\n          '2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new(\"2010-10-15\", {}), Time.parse(\"2010-10-15\")),\n        },\n        {'meta.timestamp.ne' => '2010-10-15'}\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_bucket_file/file_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_bucket_file/file'\nrequire 'puppet/util/platform'\n\ndescribe Puppet::FileBucketFile::File, :uses_checksums => true do\n  include PuppetSpec::Files\n\n  describe \"non-stubbing tests\" do\n    include PuppetSpec::Files\n\n    def save_bucket_file(contents, path = \"/who_cares\")\n      bucket_file = Puppet::FileBucket::File.new(contents)\n      Puppet::FileBucket::File.indirection.save(bucket_file, \"#{bucket_file.name}#{path}\")\n      bucket_file.checksum_data\n    end\n\n    describe \"when servicing a save request\" do\n      it \"should return a result whose content is empty\" do\n        bucket_file = Puppet::FileBucket::File.new('stuff')\n        result = Puppet::FileBucket::File.indirection.save(bucket_file, \"sha256/35bafb1ce99aef3ab068afbaabae8f21fd9b9f02d3a9442e364fa92c0b3eeef0\")\n        expect(result.contents).to be_empty\n      end\n\n      it \"deals with multiple processes saving at the same time\", :unless => Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n        bucket_file = Puppet::FileBucket::File.new(\"contents\")\n\n        children = []\n        5.times do |count|\n          children << Kernel.fork do\n            save_bucket_file(\"contents\", \"/testing\")\n            exit(0)\n          end\n        end\n        children.each { |child| Process.wait(child) }\n\n        paths = File.read(\"#{Puppet[:bucketdir]}/d/1/b/2/a/5/9/f/d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8/paths\").lines.to_a\n        expect(paths.length).to eq(1)\n        expect(Puppet::FileBucket::File.indirection.head(\"#{bucket_file.checksum_type}/#{bucket_file.checksum_data}/testing\")).to be_truthy\n      end\n\n      it \"fails if the contents collide with existing contents\" do\n        Puppet[:digest_algorithm] = 'md5'\n\n        # This is the shortest known MD5 collision (little endian). See https://eprint.iacr.org/2010/643.pdf\n        first_contents = [0x6165300e,0x87a79a55,0xf7c60bd0,0x34febd0b,\n                          0x6503cf04,0x854f709e,0xfb0fc034,0x874c9c65,\n                          0x2f94cc40,0x15a12deb,0x5c15f4a3,0x490786bb,\n                          0x6d658673,0xa4341f7d,0x8fd75920,0xefd18d5a].pack(\"V\" * 16)\n\n        collision_contents = [0x6165300e,0x87a79a55,0xf7c60bd0,0x34febd0b,\n                              0x6503cf04,0x854f749e,0xfb0fc034,0x874c9c65,\n                              0x2f94cc40,0x15a12deb,0xdc15f4a3,0x490786bb,\n                              0x6d658673,0xa4341f7d,0x8fd75920,0xefd18d5a].pack(\"V\" * 16)\n\n        checksum_value = save_bucket_file(first_contents, \"/foo/bar\")\n\n        # We expect Puppet to log an error with the path to the file\n        expect(Puppet).to receive(:err).with(/Unable to verify existing FileBucket backup at '#{Puppet[:bucketdir]}.*#{checksum_value}\\/contents'/)\n\n        # But the exception should not contain it\n        expect do\n          save_bucket_file(collision_contents, \"/foo/bar\")\n        end.to raise_error(Puppet::FileBucket::BucketError, /\\AExisting backup and new file have different content but same checksum, {md5}#{checksum_value}\\. Verify existing backup and remove if incorrect\\.\\Z/)\n      end\n\n      # See PUP-1334\n      context \"when the contents file exists but is corrupted and does not match the expected checksum\" do\n        let(:original_contents) { \"a file that will get corrupted\" }\n        let(:bucket_file) { Puppet::FileBucket::File.new(original_contents) }\n        let(:contents_file) { \"#{Puppet[:bucketdir]}/7/7/4/1/0/2/7/9/77410279bb789b799c2f38bf654b46a509dd27ddad6e47a6684805e9ba390bce/contents\" }\n\n        before(:each) do\n          # Ensure we're starting with a clean slate - no pre-existing backup\n          Puppet::FileSystem.unlink(contents_file) if Puppet::FileSystem.exist?(contents_file)\n          # Create initial \"correct\" backup\n          Puppet::FileBucket::File.indirection.save(bucket_file)\n          # Modify the contents file so that it no longer matches the SHA, simulating a corrupt backup\n          Puppet::FileSystem.unlink(contents_file) # bucket_files are read-only\n          Puppet::Util.replace_file(contents_file, 0600) { |fh| fh.puts \"now with corrupted content\" }\n        end\n\n        it \"issues a warning that the backup will be overwritten\" do\n          expect(Puppet).to receive(:warning).with(/Existing backup does not match its expected sum, #{bucket_file.checksum}/)\n          Puppet::FileBucket::File.indirection.save(bucket_file)\n        end\n\n        it \"overwrites the existing contents file (backup)\" do\n          Puppet::FileBucket::File.indirection.save(bucket_file)\n          expect(Puppet::FileSystem.read(contents_file)).to eq(original_contents)\n        end\n      end\n\n      describe \"when supplying a path\" do\n        with_digest_algorithms do\n            it \"should store the path if not already stored\" do\n              if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n                skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n              else\n                save_bucket_file(plaintext, \"/foo/bar\")\n  \n                dir_path = \"#{Puppet[:bucketdir]}/#{bucket_dir}\"\n                contents_file = \"#{dir_path}/contents\"\n                paths_file = \"#{dir_path}/paths\"\n                expect(Puppet::FileSystem.binread(contents_file)).to eq(plaintext)\n                expect(Puppet::FileSystem.read(paths_file)).to eq(\"foo/bar\\n\")\n              end\n            end\n  \n            it \"should leave the paths file alone if the path is already stored\" do\n              if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n                skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n              else\n                # save it twice\n                save_bucket_file(plaintext, \"/foo/bar\")\n                save_bucket_file(plaintext, \"/foo/bar\")\n                dir_path = \"#{Puppet[:bucketdir]}/#{bucket_dir}\"\n                expect(Puppet::FileSystem.binread(\"#{dir_path}/contents\")).to eq(plaintext)\n                expect(File.read(\"#{dir_path}/paths\")).to eq(\"foo/bar\\n\")\n              end\n            end\n  \n            it \"should store an additional path if the new path differs from those already stored\" do\n              if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n                skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n              else\n                save_bucket_file(plaintext, \"/foo/bar\")\n                save_bucket_file(plaintext, \"/foo/baz\")\n                dir_path = \"#{Puppet[:bucketdir]}/#{bucket_dir}\"\n                expect(Puppet::FileSystem.binread(\"#{dir_path}/contents\")).to eq(plaintext)\n                expect(File.read(\"#{dir_path}/paths\")).to eq(\"foo/bar\\nfoo/baz\\n\")\n              end\n            end\n          # end\n        end\n      end\n\n      describe \"when not supplying a path\" do\n        with_digest_algorithms do\n          it \"should save the file and create an empty paths file\" do\n            if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n              skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n            else\n              save_bucket_file(plaintext, \"\")\n  \n              dir_path = \"#{Puppet[:bucketdir]}/#{bucket_dir}\"\n              expect(Puppet::FileSystem.binread(\"#{dir_path}/contents\")).to eq(plaintext)\n              expect(File.read(\"#{dir_path}/paths\")).to eq(\"\")\n            end\n          end\n        end\n      end\n    end\n\n    describe \"when servicing a head/find request\" do\n      with_digest_algorithms do\n        let(:not_bucketed_plaintext) { \"other stuff\" }\n        let(:not_bucketed_checksum) { digest(not_bucketed_plaintext) }\n\n        describe \"when listing the filebucket\" do\n          it \"should return false/nil when the bucket is empty\" do\n            expect(Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{not_bucketed_checksum}/foo/bar\", :list_all => true)).to eq(nil)\n          end\n\n          it \"raises when the request is remote\" do\n            Puppet[:bucketdir] = tmpdir('bucket')\n\n            request = Puppet::Indirector::Request.new(:file_bucket_file, :find, \"#{digest_algorithm}/#{checksum}/foo/bar\", nil, :list_all => true)\n            request.node = 'client.example.com'\n\n            expect {\n              Puppet::FileBucketFile::File.new.find(request)\n            }.to raise_error(Puppet::Error, \"Listing remote file buckets is not allowed\")\n          end\n\n          it \"should return the list of bucketed files in a human readable way\" do\n            if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n              skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n            else\n              checksum1 = save_bucket_file(\"I'm the contents of a file\", '/foo/bar1')\n              checksum2 = save_bucket_file(\"I'm the contents of another file\", '/foo/bar2')\n              checksum3 = save_bucket_file(\"I'm the modified content of a existing file\", '/foo/bar1')\n  \n              # Use the first checksum as we know it's stored in the bucket\n              find_result = Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum1}/foo/bar1\", :list_all => true)\n  \n              # The list is sort order from date and file name, so first and third checksums come before the second\n              date_pattern = '\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}'\n              expect(find_result.to_s).to match(Regexp.new(\"^(#{checksum1}|#{checksum3}) #{date_pattern} foo/bar1\\\\n(#{checksum3}|#{checksum1}) #{date_pattern} foo/bar1\\\\n#{checksum2} #{date_pattern} foo/bar2\\\\n$\"))\n            end\n          end\n\n          it \"should fail in an informative way when provided dates are not in the right format\" do\n            if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n              skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n            else\n              contents = \"I'm the contents of a file\"\n              save_bucket_file(contents, '/foo/bar1')\n              expect {\n                Puppet::FileBucket::File.indirection.find(\n                  \"#{digest_algorithm}/#{not_bucketed_checksum}/foo/bar\",\n                  :list_all => true,\n                  :todate => \"0:0:0 1-1-1970\",\n                  :fromdate => \"WEIRD\"\n                )\n              }.to raise_error(Puppet::Error, /fromdate/)\n              expect {\n                Puppet::FileBucket::File.indirection.find(\n                  \"#{digest_algorithm}/#{not_bucketed_checksum}/foo/bar\",\n                  :list_all => true,\n                  :todate => \"WEIRD\",\n                  :fromdate => Time.now\n                )\n              }.to raise_error(Puppet::Error, /todate/)\n            end\n          end\n        end\n\n        describe \"when supplying a path\" do\n          it \"should return false/nil if the file isn't bucketed\" do\n            expect(Puppet::FileBucket::File.indirection.head(\"#{digest_algorithm}/#{not_bucketed_checksum}/foo/bar\")).to eq(false)\n            expect(Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{not_bucketed_checksum}/foo/bar\")).to eq(nil)\n          end\n\n          it \"should return false/nil if the file is bucketed but with a different path\" do\n\n            if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n              skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n            else\n              checksum = save_bucket_file(\"I'm the contents of a file\", '/foo/bar')\n  \n              expect(Puppet::FileBucket::File.indirection.head(\"#{digest_algorithm}/#{checksum}/foo/baz\")).to eq(false)\n              expect(Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum}/foo/baz\")).to eq(nil)\n            end\n          end\n\n          it \"should return true/file if the file is already bucketed with the given path\" do\n            if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n              skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n            else\n              contents = \"I'm the contents of a file\"\n  \n              checksum = save_bucket_file(contents, '/foo/bar')\n  \n              expect(Puppet::FileBucket::File.indirection.head(\"#{digest_algorithm}/#{checksum}/foo/bar\")).to eq(true)\n              find_result = Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum}/foo/bar\")\n              expect(find_result.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n              expect(find_result.to_s).to eq(contents)\n            end\n          end\n        end\n\n        describe \"when not supplying a path\" do\n          [false, true].each do |trailing_slash|\n            describe \"#{trailing_slash ? 'with' : 'without'} a trailing slash\" do\n              trailing_string = trailing_slash ? '/' : ''\n\n              it \"should return false/nil if the file isn't bucketed\" do\n                expect(Puppet::FileBucket::File.indirection.head(\"#{digest_algorithm}/#{not_bucketed_checksum}#{trailing_string}\")).to eq(false)\n                expect(Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{not_bucketed_checksum}#{trailing_string}\")).to eq(nil)\n              end\n\n              it \"should return true/file if the file is already bucketed\" do\n    \n                # this one replaces most of the lets in the \"when\n                # digest_digest_algorithm is set...\" shared context, but it still needs digest_algorithm\n                if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n                  skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n                else\n                  contents = \"I'm the contents of a file\"\n  \n                  checksum = save_bucket_file(contents, '/foo/bar')\n  \n                  expect(Puppet::FileBucket::File.indirection.head(\"#{digest_algorithm}/#{checksum}#{trailing_string}\")).to eq(true)\n                  find_result = Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum}#{trailing_string}\")\n                  expect(find_result.checksum).to eq(\"{#{digest_algorithm}}#{checksum}\")\n                  expect(find_result.to_s).to eq(contents)\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n\n    describe \"when diffing files\", :unless => Puppet::Util::Platform.windows? do\n      with_digest_algorithms do\n        let(:not_bucketed_plaintext) { \"other stuff\" }\n        let(:not_bucketed_checksum) { digest(not_bucketed_plaintext) }\n\n        it \"should generate an empty string if there is no diff\" do\n          skip(\"usage of fork(1) no supported on this platform\") if RUBY_PLATFORM == 'java'\n          if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n            skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n          else\n            checksum = save_bucket_file(\"I'm the contents of a file\")\n            expect(Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum}\", :diff_with => checksum)).to eq('')\n          end\n        end\n\n        it \"should generate a proper diff if there is a diff\" do\n          skip(\"usage of fork(1) no supported on this platform\") if RUBY_PLATFORM == 'java'\n          if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n            skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n          else\n            checksum1 = save_bucket_file(\"foo\\nbar\\nbaz\")\n            checksum2 = save_bucket_file(\"foo\\nbiz\\nbaz\")\n  \n            diff = Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum1}\", :diff_with => checksum2)\n            expect(diff).to include(\"-bar\\n+biz\\n\")\n          end\n        end\n\n        it \"should raise an exception if the hash to diff against isn't found\" do\n          if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n            skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n          else\n            checksum = save_bucket_file(\"whatever\")\n  \n            expect do\n              Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{checksum}\", :diff_with => not_bucketed_checksum)\n            end.to raise_error \"could not find diff_with #{not_bucketed_checksum}\"\n          end\n        end\n\n        it \"should return nil if the hash to diff from isn't found\" do\n          if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n            skip \"PUP-8257: Skip file bucket test on windows for #{digest_algorithm} due to long path names\"\n          else\n            checksum = save_bucket_file(\"whatever\")\n  \n            expect(Puppet::FileBucket::File.indirection.find(\"#{digest_algorithm}/#{not_bucketed_checksum}\", :diff_with => checksum)).to eq(nil)\n          end\n        end\n      end\n    end\n  end\n\n  [true, false].each do |override_bucket_path|\n    describe \"when bucket path #{override_bucket_path ? 'is' : 'is not'} overridden\" do\n      [true, false].each do |supply_path|\n        describe \"when #{supply_path ? 'supplying' : 'not supplying'} a path\" do\n          with_digest_algorithms do\n            before :each do\n              allow(Puppet.settings).to receive(:use)\n              @store = Puppet::FileBucketFile::File.new\n\n              @bucket_top_dir = tmpdir(\"bucket\")\n\n              if override_bucket_path\n                Puppet[:bucketdir] = \"/bogus/path\" # should not be used\n              else\n                Puppet[:bucketdir] = @bucket_top_dir\n              end\n\n              @dir = \"#{@bucket_top_dir}/#{bucket_dir}\"\n              @contents_path = \"#{@dir}/contents\"\n            end\n\n            describe \"when retrieving files\" do\n              before :each do\n\n                request_options = {}\n                if override_bucket_path\n                  request_options[:bucket_path] = @bucket_top_dir\n                end\n\n                key = \"#{digest_algorithm}/#{checksum}\"\n                if supply_path\n                  key += \"/path/to/file\"\n                end\n\n                @request = Puppet::Indirector::Request.new(:indirection_name, :find, key, nil, request_options)\n              end\n\n              def make_bucketed_file\n                FileUtils.mkdir_p(@dir)\n                File.open(@contents_path, 'wb') { |f| f.write plaintext }\n              end\n\n              it \"should return an instance of Puppet::FileBucket::File created with the content if the file exists\" do\n                make_bucketed_file\n\n                if supply_path\n                  expect(@store.find(@request)).to eq(nil)\n                  expect(@store.head(@request)).to eq(false) # because path didn't match\n                else\n                  bucketfile = @store.find(@request)\n                  expect(bucketfile).to be_a(Puppet::FileBucket::File)\n                  expect(bucketfile.contents).to eq(plaintext)\n                  expect(@store.head(@request)).to eq(true)\n                end\n              end\n\n              it \"should return nil if no file is found\" do\n                expect(@store.find(@request)).to be_nil\n                expect(@store.head(@request)).to eq(false)\n              end\n            end\n\n            describe \"when saving files\" do\n              it \"should save the contents to the calculated path\" do\n                skip(\"Windows Long File Name support is incomplete PUP-8257, this doesn't fail reliably so it should be skipped.\") if Puppet::Util::Platform.windows? && (['sha512', 'sha384'].include? digest_algorithm)\n                options = {}\n                if override_bucket_path\n                  options[:bucket_path] = @bucket_top_dir\n                end\n\n                key = \"#{digest_algorithm}/#{checksum}\"\n                if supply_path\n                  key += \"//path/to/file\"\n                end\n\n                file_instance = Puppet::FileBucket::File.new(plaintext, options)\n                request = Puppet::Indirector::Request.new(:indirection_name, :save, key, file_instance)\n\n                @store.save(request)\n                expect(Puppet::FileSystem.binread(\"#{@dir}/contents\")).to eq(plaintext)\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_bucket_file/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_bucket_file/rest'\n\ndescribe Puppet::FileBucketFile::Rest do\n  let(:rest_path) {\"filebucket://xanadu:8141/\"}\n  let(:file_bucket_file) {Puppet::FileBucket::File.new('file contents', :bucket_path => '/some/random/path')}\n  let(:files_original_path) {'/path/to/file'}\n  let(:dest_path) {\"#{rest_path}#{file_bucket_file.name}/#{files_original_path}\"}\n  let(:file_bucket_path) {\"#{rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}/#{files_original_path}\"}\n  let(:source_path) {\"#{rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}\"}\n\n  let(:uri) { %r{/puppet/v3/file_bucket_file} }\n\n  describe '#head' do\n    it 'includes the environment as a request parameter' do\n      stub_request(:head, uri).with(query: hash_including(environment: 'outerspace'))\n\n      described_class.indirection.head(file_bucket_path, :bucket_path => file_bucket_file.bucket_path, environment: Puppet::Node::Environment.remote('outerspace'))\n    end\n\n    it 'includes bucket path in the request if bucket path is set' do\n      stub_request(:head, uri).with(query: hash_including(bucket_path: '/some/random/path'))\n\n      described_class.indirection.head(file_bucket_path, :bucket_path => file_bucket_file.bucket_path)\n    end\n\n    it \"returns nil on 404\" do\n      stub_request(:head, uri).to_return(status: 404)\n\n      expect(described_class.indirection.head(file_bucket_path, :bucket_path => file_bucket_file.bucket_path)).to be_falsy\n    end\n\n    it \"raises for all other fail codes\" do\n      stub_request(:head, uri).to_return(status: [503, 'server unavailable'])\n\n      expect{described_class.indirection.head(file_bucket_path, :bucket_path => file_bucket_file.bucket_path)}.to raise_error(Net::HTTPError, \"Error 503 on SERVER: server unavailable\")\n    end\n  end\n\n  describe '#find' do\n    it 'includes the environment as a request parameter' do\n      stub_request(:get, uri).with(query: hash_including(environment: 'outerspace')).to_return(status: 200, headers: {'Content-Type' => 'application/octet-stream'})\n\n      described_class.indirection.find(source_path, :bucket_path => nil, environment: Puppet::Node::Environment.remote('outerspace'))\n    end\n\n    {bucket_path: 'path', diff_with: '4aabe1257043bd0', list_all: 'true', fromdate: '20200404', todate: '20200404'}.each do |param, val|\n      it \"includes #{param} as a parameter in the request if #{param} is set\" do\n        stub_request(:get, uri).with(query: hash_including(param => val)).to_return(status: 200, headers: {'Content-Type' => 'application/octet-stream'})\n\n        options = { param => val }\n        described_class.indirection.find(source_path, **options)\n      end\n    end\n\n    it 'raises if unsuccessful' do\n      stub_request(:get, uri).to_return(status: [503, 'server unavailable'])\n\n      expect{described_class.indirection.find(source_path, :bucket_path => nil)}.to raise_error(Net::HTTPError, \"Error 503 on SERVER: server unavailable\")\n    end\n\n    it 'raises if Content-Type is not included in the response' do\n      stub_request(:get, uri).to_return(status: 200, headers: {})\n\n      expect{described_class.indirection.find(source_path, :bucket_path => nil)}.to raise_error(RuntimeError, \"No content type in http response; cannot parse\")\n    end\n  end\n\n  describe '#save' do\n    it 'includes the environment as a request parameter' do\n      stub_request(:put, uri).with(query: hash_including(environment: 'outerspace'))\n\n      described_class.indirection.save(file_bucket_file, dest_path, environment: Puppet::Node::Environment.remote('outerspace'))\n    end\n\n    it 'sends the contents of the file as the request body' do\n      stub_request(:put, uri).with(body: file_bucket_file.contents)\n\n      described_class.indirection.save(file_bucket_file, dest_path)\n    end\n\n    it 'raises if unsuccessful' do\n      stub_request(:put, uri).to_return(status: [503, 'server unavailable'])\n\n      expect{described_class.indirection.save(file_bucket_file, dest_path)}.to raise_error(Net::HTTPError, \"Error 503 on SERVER: server unavailable\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_bucket_file/selector_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_bucket_file/selector'\nrequire 'puppet/indirector/file_bucket_file/file'\nrequire 'puppet/indirector/file_bucket_file/rest'\n\ndescribe Puppet::FileBucketFile::Selector do\n  let(:model) { Puppet::FileBucket::File.new('') }\n  let(:indirection) { Puppet::FileBucket::File.indirection }\n  let(:terminus) { indirection.terminus(:selector) }\n\n  %w[head find save search destroy].each do |method|\n    describe \"##{method}\" do\n      it \"should proxy to rest terminus for https requests\" do\n        key = \"https://example.com/path/to/file\"\n\n        expect(indirection.terminus(:rest)).to receive(method)\n\n        if method == 'save'\n          terminus.send(method, indirection.request(method, key, model))\n        else\n          terminus.send(method, indirection.request(method, key, nil))\n        end\n      end\n\n      it \"should proxy to file terminus for other requests\" do\n        key = \"file:///path/to/file\"\n\n        case method\n        when 'save'\n          expect(indirection.terminus(:file)).to receive(method)\n          terminus.send(method, indirection.request(method, key, model))\n        when 'find', 'head'\n          expect(indirection.terminus(:file)).to receive(method)\n          terminus.send(method, indirection.request(method, key, nil))\n        else\n          # file terminus doesn't implement search or destroy\n          expect {\n            terminus.send(method, indirection.request(method, key, nil))\n          }.to raise_error(NoMethodError)\n        end\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/indirector/file_content/file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_content/file_server'\n\ndescribe Puppet::Indirector::FileContent::FileServer do\n  it \"should be registered with the file_content indirection\" do\n    expect(Puppet::Indirector::Terminus.terminus_class(:file_content, :file_server)).to equal(Puppet::Indirector::FileContent::FileServer)\n  end\n\n  it \"should be a subclass of the FileServer terminus\" do\n    expect(Puppet::Indirector::FileContent::FileServer.superclass).to equal(Puppet::Indirector::FileServer)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_content/file_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_content/file'\n\ndescribe Puppet::Indirector::FileContent::File do\n  it \"should be registered with the file_content indirection\" do\n    expect(Puppet::Indirector::Terminus.terminus_class(:file_content, :file)).to equal(Puppet::Indirector::FileContent::File)\n  end\n\n  it \"should be a subclass of the DirectFileServer terminus\" do\n    expect(Puppet::Indirector::FileContent::File.superclass).to equal(Puppet::Indirector::DirectFileServer)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_content/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_content/rest'\n\ndescribe Puppet::Indirector::FileContent::Rest do\n  let(:certname) { 'ziggy' }\n  let(:uri) { %r{/puppet/v3/file_content/:mount/path/to/file} }\n  let(:key) { \"puppet:///:mount/path/to/file\" }\n\n  before :each do\n    described_class.indirection.terminus_class = :rest\n  end\n\n  def file_content_response\n    {body: \"some content\", headers: { 'Content-Type' => 'application/octet-stream' } }\n  end\n\n  it \"returns content as a binary string\" do\n    stub_request(:get, uri).to_return(status: 200, **file_content_response)\n\n    file_content = described_class.indirection.find(key)\n    expect(file_content.content.encoding).to eq(Encoding::BINARY)\n    expect(file_content.content).to eq('some content')\n  end\n\n  it \"URL encodes special characters\" do\n    stub_request(:get, %r{/puppet/v3/file_content/:mount/path%20to%20file}).to_return(status: 200, **file_content_response)\n\n    described_class.indirection.find('puppet:///:mount/path to file')\n  end\n\n  it \"returns nil if the content doesn't exist\" do\n    stub_request(:get, uri).to_return(status: 404)\n\n    expect(described_class.indirection.find(key)).to be_nil\n  end\n\n  it \"raises if fail_on_404 is true\" do\n    stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json'}, body: \"{}\")\n\n    expect {\n      described_class.indirection.find(key, fail_on_404: true)\n    }.to raise_error(Puppet::Error, %r{Find /puppet/v3/file_content/:mount/path/to/file resulted in 404 with the message: {}})\n  end\n\n  it \"raises an error on HTTP 500\" do\n    stub_request(:get, uri).to_return(status: 500, headers: { 'Content-Type' => 'application/json'}, body: \"{}\")\n\n    expect {\n      described_class.indirection.find(key)\n    }.to raise_error(Net::HTTPError, %r{Error 500 on SERVER: })\n  end\n\n  it \"connects to a specific host\" do\n    stub_request(:get, %r{https://example.com:8140/puppet/v3/file_content/:mount/path/to/file})\n      .to_return(status: 200, **file_content_response)\n\n    described_class.indirection.find(\"puppet://example.com:8140/:mount/path/to/file\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_content/selector_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_content/selector'\n\ndescribe Puppet::Indirector::FileContent::Selector do\n  include PuppetSpec::Files\n\n  it_should_behave_like \"Puppet::FileServing::Files\", :file_content\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_metadata/file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_metadata/file_server'\n\ndescribe Puppet::Indirector::FileMetadata::FileServer do\n  it \"should be registered with the file_metadata indirection\" do\n    expect(Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file_server)).to equal(Puppet::Indirector::FileMetadata::FileServer)\n  end\n\n  it \"should be a subclass of the FileServer terminus\" do\n    expect(Puppet::Indirector::FileMetadata::FileServer.superclass).to equal(Puppet::Indirector::FileServer)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_metadata/file_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_metadata/file'\n\ndescribe Puppet::Indirector::FileMetadata::File do\n  it \"should be registered with the file_metadata indirection\" do\n    expect(Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file)).to equal(Puppet::Indirector::FileMetadata::File)\n  end\n\n  it \"should be a subclass of the DirectFileServer terminus\" do\n    expect(Puppet::Indirector::FileMetadata::File.superclass).to equal(Puppet::Indirector::DirectFileServer)\n  end\n\n  describe \"when creating the instance for a single found file\" do\n    before do\n      @metadata = Puppet::Indirector::FileMetadata::File.new\n      @path = File.expand_path('/my/local')\n      @uri = Puppet::Util.path_to_uri(@path).to_s\n      @data = double('metadata')\n      allow(@data).to receive(:collect)\n      expect(Puppet::FileSystem).to receive(:exist?).with(@path).and_return(true)\n\n      @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil)\n    end\n\n    it \"should collect its attributes when a file is found\" do\n      expect(@data).to receive(:collect)\n\n      expect(Puppet::FileServing::Metadata).to receive(:new).and_return(@data)\n      expect(@metadata.find(@request)).to eq(@data)\n    end\n  end\n\n  describe \"when searching for multiple files\" do\n    before do\n      @metadata = Puppet::Indirector::FileMetadata::File.new\n      @path = File.expand_path('/my/local')\n      @uri = Puppet::Util.path_to_uri(@path).to_s\n\n      @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil)\n    end\n\n    it \"should collect the attributes of the instances returned\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(@path).and_return(true)\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(@path, @request).and_return(double(\"fileset\"))\n      expect(Puppet::FileServing::Fileset).to receive(:merge).and_return([[\"one\", @path], [\"two\", @path]])\n\n      one = double(\"one\", :collect => nil)\n      expect(Puppet::FileServing::Metadata).to receive(:new).with(@path, {:relative_path => \"one\"}).and_return(one)\n\n      two = double(\"two\", :collect => nil)\n      expect(Puppet::FileServing::Metadata).to receive(:new).with(@path, {:relative_path => \"two\"}).and_return(two)\n\n      expect(@metadata.search(@request)).to eq([one, two])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_metadata/http_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_metadata'\nrequire 'puppet/indirector/file_metadata/http'\n\ndescribe Puppet::Indirector::FileMetadata::Http do\n  DEFAULT_HEADERS = {\n    \"Cache-Control\" => \"private, max-age=0\",\n    \"Connection\" => \"close\",\n    \"Content-Encoding\" => \"gzip\",\n    \"Content-Type\" => \"text/html; charset=ISO-8859-1\",\n    \"Date\" => \"Fri, 01 May 2020 17:16:00 GMT\",\n    \"Expires\" => \"-1\",\n    \"Server\" => \"gws\"\n  }.freeze\n\n  let(:certname) { 'ziggy' }\n  # The model is Puppet:FileServing::Metadata\n  let(:model) { described_class.model }\n  # The http terminus creates instances of HttpMetadata which subclass Metadata\n  let(:metadata) { Puppet::FileServing::HttpMetadata.new(key) }\n  let(:key) { \"https://example.com/path/to/file\" }\n  # Digest::MD5.base64digest(\"\") => \"1B2M2Y8AsgTpgAmY7PhCfg==\"\n  let(:content_md5) { {\"Content-MD5\" => \"1B2M2Y8AsgTpgAmY7PhCfg==\"} }\n  let(:last_modified) { {\"Last-Modified\" => \"Wed, 01 Jan 2020 08:00:00 GMT\"} }\n\n  before :each do\n    described_class.indirection.terminus_class = :http\n  end\n\n  context \"when finding\" do\n    it \"returns http file metadata\" do\n      stub_request(:head, key)\n        .to_return(status: 200, headers: DEFAULT_HEADERS)\n\n      result = model.indirection.find(key)\n      expect(result.ftype).to eq('file')\n      expect(result.path).to eq('/dev/null')\n      expect(result.relative_path).to be_nil\n      expect(result.destination).to be_nil\n      expect(result.checksum).to match(%r{mtime})\n      expect(result.owner).to be_nil\n      expect(result.group).to be_nil\n      expect(result.mode).to be_nil\n    end\n\n    it \"reports an md5 checksum if present in the response\" do\n      stub_request(:head, key)\n        .to_return(status: 200, headers: DEFAULT_HEADERS.merge(content_md5))\n\n      result = model.indirection.find(key)\n      expect(result.checksum_type).to eq(:md5)\n      expect(result.checksum).to eq(\"{md5}d41d8cd98f00b204e9800998ecf8427e\")\n    end\n\n    it \"reports an mtime checksum if present in the response\" do\n      stub_request(:head, key)\n        .to_return(status: 200, headers: DEFAULT_HEADERS.merge(last_modified))\n\n      result = model.indirection.find(key)\n      expect(result.checksum_type).to eq(:mtime)\n      expect(result.checksum).to eq(\"{mtime}2020-01-01 08:00:00 UTC\")\n    end\n\n    it \"prefers md5\" do\n      stub_request(:head, key)\n        .to_return(status: 200, headers: DEFAULT_HEADERS.merge(content_md5).merge(last_modified))\n\n      result = model.indirection.find(key)\n      expect(result.checksum_type).to eq(:md5)\n      expect(result.checksum).to eq(\"{md5}d41d8cd98f00b204e9800998ecf8427e\")\n    end\n\n    it \"prefers mtime when explicitly requested\" do\n      stub_request(:head, key)\n        .to_return(status: 200, headers: DEFAULT_HEADERS.merge(content_md5).merge(last_modified))\n\n      result = model.indirection.find(key, checksum_type: :mtime)\n      expect(result.checksum_type).to eq(:mtime)\n      expect(result.checksum).to eq(\"{mtime}2020-01-01 08:00:00 UTC\")\n    end\n\n    it \"leniently parses base64\" do\n      # Content-MD5 header is missing '==' padding\n      stub_request(:head, key)\n        .to_return(status: 200, headers: DEFAULT_HEADERS.merge(\"Content-MD5\" => \"1B2M2Y8AsgTpgAmY7PhCfg\"))\n\n      result = model.indirection.find(key)\n      expect(result.checksum_type).to eq(:md5)\n      expect(result.checksum).to eq(\"{md5}d41d8cd98f00b204e9800998ecf8427e\")\n    end\n\n    it \"URL encodes special characters\" do\n      pending(\"HTTP terminus doesn't encode the URI before parsing\")\n\n      stub_request(:head, %r{/path%20to%20file})\n\n      model.indirection.find('https://example.com/path to file')\n    end\n\n    it \"sends query parameters\" do\n      stub_request(:head, key).with(query: {'a' => 'b'})\n\n      model.indirection.find(\"#{key}?a=b\")\n    end\n\n    it \"returns nil if the content doesn't exist\" do\n      stub_request(:head, key).to_return(status: 404)\n\n      expect(model.indirection.find(key)).to be_nil\n    end\n\n    it \"returns nil if fail_on_404\" do\n      stub_request(:head, key).to_return(status: 404)\n\n      expect(model.indirection.find(key, fail_on_404: true)).to be_nil\n    end\n\n    it \"returns nil on HTTP 500\" do\n      stub_request(:head, key).to_return(status: 500)\n\n      # this is kind of strange, but it does allow puppet to try\n      # multiple `source => [\"URL1\", \"URL2\"]` and use the first\n      # one based on sourceselect\n      expect(model.indirection.find(key)).to be_nil\n    end\n\n    it \"accepts all content types\" do\n      stub_request(:head, key).with(headers: {'Accept' => '*/*'})\n\n      model.indirection.find(key)\n    end\n\n    it \"sets puppet user-agent\" do\n      stub_request(:head, key).with(headers: {'User-Agent' => Puppet[:http_user_agent]})\n\n      model.indirection.find(key)\n    end\n\n    it \"tries to persist the connection\" do\n      # HTTP/1.1 defaults to persistent connections, so check for\n      # the header's absence\n      stub_request(:head, key).with do |request|\n        expect(request.headers).to_not include('Connection')\n      end\n\n      model.indirection.find(key)\n    end\n\n    it \"follows redirects\" do\n      new_url = \"https://example.com/different/path\"\n      redirect = { status: 200, headers: { 'Location' => new_url }, body: \"\"}\n      stub_request(:head, key).to_return(redirect)\n      stub_request(:head, new_url)\n\n      model.indirection.find(key)\n    end\n\n    it \"falls back to partial GET if HEAD is not allowed\" do\n      stub_request(:head, key)\n        .to_return(status: 405)\n      stub_request(:get, key)\n        .to_return(status: 200, headers: {'Range' => 'bytes=0-0'})\n\n      model.indirection.find(key)\n    end\n\n    it \"falls back to partial GET if HEAD is forbidden\" do\n      stub_request(:head, key)\n        .to_return(status: 403)\n      stub_request(:get, key)\n        .to_return(status: 200, headers: {'Range' => 'bytes=0-0'})\n\n      model.indirection.find(key)\n    end\n\n    it \"returns nil if the partial GET fails\" do\n      stub_request(:head, key)\n        .to_return(status: 403)\n      stub_request(:get, key)\n        .to_return(status: 403)\n\n      expect(model.indirection.find(key)).to be_nil\n    end\n  end\n\n  context \"when searching\" do\n    it \"raises an error\" do\n      expect {\n        model.indirection.search(key)\n      }.to raise_error(Puppet::Error, 'cannot lookup multiple files')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_metadata/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_metadata'\nrequire 'puppet/indirector/file_metadata/rest'\n\ndescribe Puppet::Indirector::FileMetadata::Rest do\n  let(:certname) { 'ziggy' }\n  let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n  let(:model) { described_class.model }\n\n  before :each do\n    described_class.indirection.terminus_class = :rest\n  end\n\n  def metadata_response(metadata)\n    { body: formatter.render(metadata), headers: {'Content-Type' => formatter.mime } }\n  end\n\n  context \"when finding\" do\n    let(:uri) { %r{/puppet/v3/file_metadata/:mount/path/to/file} }\n    let(:key) { \"puppet:///:mount/path/to/file\" }\n    let(:metadata) { model.new('/path/to/file') }\n\n    it \"returns file metadata\" do\n      stub_request(:get, uri)\n        .to_return(status: 200, **metadata_response(metadata))\n\n      result = model.indirection.find(key)\n      expect(result.path).to eq('/path/to/file')\n    end\n\n    it \"URL encodes special characters\" do\n      stub_request(:get, %r{/puppet/v3/file_metadata/:mount/path%20to%20file})\n        .to_return(status: 200, **metadata_response(metadata))\n\n      model.indirection.find('puppet:///:mount/path to file')\n    end\n\n    it \"returns nil if the content doesn't exist\" do\n      stub_request(:get, uri).to_return(status: 404)\n\n      expect(model.indirection.find(key)).to be_nil\n    end\n\n    it \"raises if fail_on_404 is true\" do\n      stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json'}, body: \"{}\")\n\n      expect {\n        model.indirection.find(key, fail_on_404: true)\n      }.to raise_error(Puppet::Error, %r{Find /puppet/v3/file_metadata/:mount/path/to/file resulted in 404 with the message: {}})\n    end\n\n    it \"raises an error on HTTP 500\" do\n      stub_request(:get, uri).to_return(status: 500, headers: { 'Content-Type' => 'application/json'}, body: \"{}\")\n\n      expect {\n        model.indirection.find(key)\n      }.to raise_error(Net::HTTPError, %r{Error 500 on SERVER: })\n    end\n\n    it \"connects to a specific host\" do\n      stub_request(:get, %r{https://example.com:8140/puppet/v3/file_metadata/:mount/path/to/file})\n        .to_return(status: 200, **metadata_response(metadata))\n\n      model.indirection.find(\"puppet://example.com:8140/:mount/path/to/file\")\n    end\n  end\n\n  context \"when searching\" do\n    let(:uri) { %r{/puppet/v3/file_metadatas/:mount/path/to/dir} }\n    let(:key) { \"puppet:///:mount/path/to/dir\" }\n    let(:metadatas) { [model.new('/path/to/dir')] }\n\n    it \"returns an array of file metadata\" do\n      stub_request(:get, uri)\n        .to_return(status: 200, **metadata_response(metadatas))\n\n      result = model.indirection.search(key)\n      expect(result.first.path).to eq('/path/to/dir')\n    end\n\n    it \"URL encodes special characters\" do\n      stub_request(:get, %r{/puppet/v3/file_metadatas/:mount/path%20to%20dir})\n        .to_return(status: 200, **metadata_response(metadatas))\n\n      model.indirection.search('puppet:///:mount/path to dir')\n    end\n\n    it \"returns an empty array if the metadata doesn't exist\" do\n      stub_request(:get, uri).to_return(status: 404)\n\n      expect(model.indirection.search(key)).to eq([])\n    end\n\n    it \"returns an empty array if the metadata doesn't exist and fail_on_404 is true\" do\n      stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json'}, body: \"{}\")\n\n      expect(model.indirection.search(key, fail_on_404: true)).to eq([])\n    end\n\n    it \"raises an error on HTTP 500\" do\n      stub_request(:get, uri).to_return(status: 500, headers: { 'Content-Type' => 'application/json'}, body: \"{}\")\n\n      expect {\n        model.indirection.search(key)\n      }.to raise_error(Net::HTTPError, %r{Error 500 on SERVER: })\n    end\n\n    it \"connects to a specific host\" do\n      stub_request(:get, %r{https://example.com:8140/puppet/v3/file_metadatas/:mount/path/to/dir})\n        .to_return(status: 200, **metadata_response(metadatas))\n\n      model.indirection.search(\"puppet://example.com:8140/:mount/path/to/dir\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/file_metadata/selector_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_metadata/selector'\n\ndescribe Puppet::Indirector::FileMetadata::Selector do\n  include PuppetSpec::Files\n\n  it_should_behave_like \"Puppet::FileServing::Files\", :file_metadata\nend\n\n"
  },
  {
    "path": "spec/unit/indirector/file_server_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/file_server'\nrequire 'puppet/file_serving/configuration'\n\ndescribe Puppet::Indirector::FileServer do\n  before :all do\n    class Puppet::FileTestModel\n      extend Puppet::Indirector\n      indirects :file_test_model\n      attr_accessor :path\n      def initialize(path = '/', options = {})\n        @path = path\n        @options = options\n      end\n    end\n\n    class Puppet::FileTestModel::FileServer < Puppet::Indirector::FileServer\n    end\n\n    Puppet::FileTestModel.indirection.terminus_class = :file_server\n  end\n\n  let(:path) { File.expand_path('/my/local') }\n  let(:terminus) { Puppet::FileTestModel.indirection.terminus(:file_server) }\n  let(:indirection) { Puppet::FileTestModel.indirection }\n  let(:model) { Puppet::FileTestModel }\n  let(:uri) { \"puppet://host/my/local/file\" }\n  let(:configuration) { double('configuration') }\n\n  after(:all) do\n    Puppet::FileTestModel.indirection.delete\n    Puppet.send(:remove_const, :FileTestModel)\n  end\n\n  before(:each)do\n    allow(Puppet::FileServing::Configuration).to receive(:configuration).and_return(configuration)\n  end\n\n  describe \"when finding files\" do\n    let(:mount) { double('mount', find: nil) }\n    let(:instance) { double('instance', :links= => nil, :collect => nil) }\n\n    it \"should use the configuration to find the mount and relative path\" do\n      expect(configuration).to receive(:split_path) do |args|\n        expect(args.uri).to eq(uri)\n        nil\n      end\n\n      indirection.find(uri)\n    end\n\n    it \"should return nil if it cannot find the mount\" do\n      expect(configuration).to receive(:split_path).and_return([nil, nil])\n\n      expect(indirection.find(uri)).to be_nil\n    end\n\n    it \"should use the mount to find the full path\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:find).with(\"rel/path\", anything)\n\n      indirection.find(uri)\n    end\n\n    it \"should pass the request when finding a file\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:find) { |_, request| expect(request.uri).to eq(uri) }.and_return(nil)\n\n      indirection.find(uri)\n    end\n\n    it \"should return nil if it cannot find a full path\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:find).with(\"rel/path\", anything)\n\n      expect(indirection.find(uri)).to be_nil\n    end\n\n    it \"should create an instance with the found path\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:find).with(\"rel/path\", anything).and_return(\"/my/file\")\n\n      expect(model).to receive(:new).with(\"/my/file\", {:relative_path => nil}).and_return(instance)\n\n      expect(indirection.find(uri)).to equal(instance)\n    end\n\n    it \"should set 'links' on the instance if it is set in the request options\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:find).with(\"rel/path\", anything).and_return(\"/my/file\")\n\n      expect(model).to receive(:new).with(\"/my/file\", {:relative_path => nil}).and_return(instance)\n\n      expect(instance).to receive(:links=).with(true)\n\n      expect(indirection.find(uri, links: true)).to equal(instance)\n    end\n\n    it \"should collect the instance\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:find).with(\"rel/path\", anything).and_return(\"/my/file\")\n\n      expect(model).to receive(:new).with(\"/my/file\", {:relative_path => nil}).and_return(instance)\n\n      expect(instance).to receive(:collect)\n\n      expect(indirection.find(uri, links: true)).to equal(instance)\n    end\n  end\n\n  describe \"when searching for instances\" do\n    let(:mount) { double('mount', find: nil) }\n\n    it \"should use the configuration to search the mount and relative path\" do\n      expect(configuration).to receive(:split_path) do |args|\n        expect(args.uri).to eq(uri)\n      end.and_return([nil, nil])\n\n      indirection.search(uri)\n    end\n\n    it \"should return nil if it cannot search the mount\" do\n      expect(configuration).to receive(:split_path).and_return([nil, nil])\n\n      expect(indirection.search(uri)).to be_nil\n    end\n\n    it \"should use the mount to search for the full paths\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything)\n\n      indirection.search(uri)\n    end\n\n    it \"should pass the request\" do\n      allow(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search) { |_, request| expect(request.uri).to eq(uri) }.and_return(nil)\n\n      indirection.search(uri)\n    end\n\n    it \"should return nil if searching does not find any full paths\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything).and_return(nil)\n\n      expect(indirection.search(uri)).to be_nil\n    end\n\n    it \"should create a fileset with each returned path and merge them\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything).and_return(%w{/one /two})\n\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n      one = double('fileset_one')\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(\"/one\", anything).and_return(one)\n      two = double('fileset_two')\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(\"/two\", anything).and_return(two)\n\n      expect(Puppet::FileServing::Fileset).to receive(:merge).with(one, two).and_return([])\n\n      indirection.search(uri)\n    end\n\n    it \"should create an instance with each path resulting from the merger of the filesets\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything).and_return([])\n\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n      expect(Puppet::FileServing::Fileset).to receive(:merge).and_return(\"one\" => \"/one\", \"two\" => \"/two\")\n\n      one = double('one', :collect => nil)\n      expect(model).to receive(:new).with(\"/one\", {:relative_path => \"one\"}).and_return(one)\n\n      two = double('two', :collect => nil)\n      expect(model).to receive(:new).with(\"/two\", {:relative_path => \"two\"}).and_return(two)\n\n      # order can't be guaranteed\n      result = indirection.search(uri)\n      expect(result).to be_include(one)\n      expect(result).to be_include(two)\n      expect(result.length).to eq(2)\n    end\n\n    it \"should set 'links' on the instances if it is set in the request options\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything).and_return([])\n\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n      expect(Puppet::FileServing::Fileset).to receive(:merge).and_return(\"one\" => \"/one\")\n\n      one = double('one', :collect => nil)\n      expect(model).to receive(:new).with(\"/one\", {:relative_path => \"one\"}).and_return(one)\n      expect(one).to receive(:links=).with(true)\n\n      indirection.search(uri, links: true)\n    end\n\n    it \"should set 'checksum_type' on the instances if it is set in the request options\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything).and_return([])\n\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n      expect(Puppet::FileServing::Fileset).to receive(:merge).and_return(\"one\" => \"/one\")\n\n      one = double('one', :collect => nil)\n      expect(model).to receive(:new).with(\"/one\", {:relative_path => \"one\"}).and_return(one)\n\n      expect(one).to receive(:checksum_type=).with(:checksum)\n\n      indirection.search(uri, checksum_type: :checksum)\n    end\n\n    it \"should collect the instances\" do\n      expect(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n\n      expect(mount).to receive(:search).with(\"rel/path\", anything).and_return([])\n\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n      expect(Puppet::FileServing::Fileset).to receive(:merge).and_return(\"one\" => \"/one\")\n\n      one = double('one')\n      expect(model).to receive(:new).with(\"/one\", {:relative_path => \"one\"}).and_return(one)\n      expect(one).to receive(:collect)\n\n      indirection.search(uri)\n    end\n  end\n\n  describe \"when checking authorization\" do\n    let(:mount) { double('mount') }\n    let(:request) { Puppet::Indirector::Request.new(:myind, :mymethod, uri, :environment => \"myenv\") }\n\n    before(:each) do\n      request.method = :find\n\n      allow(configuration).to receive(:split_path).and_return([mount, \"rel/path\"])\n      allow(request).to receive(:node).and_return(\"mynode\")\n      allow(request).to receive(:ip).and_return(\"myip\")\n      allow(mount).to receive(:name).and_return(\"myname\")\n      allow(mount).to receive(:allowed?).with(\"mynode\", \"myip\").and_return(\"something\")\n    end\n\n    it \"should return false when destroying\" do\n      request.method = :destroy\n      expect(terminus).not_to be_authorized(request)\n    end\n\n    it \"should return false when saving\" do\n      request.method = :save\n      expect(terminus).not_to be_authorized(request)\n    end\n\n    it \"should use the configuration to find the mount and relative path\" do\n      expect(configuration).to receive(:split_path).with(request)\n\n      terminus.authorized?(request)\n    end\n\n    it \"should return false if it cannot find the mount\" do\n      expect(configuration).to receive(:split_path).and_return([nil, nil])\n\n      expect(terminus).not_to be_authorized(request)\n    end\n\n    it \"should return true when no auth directives are defined for the mount point\" do\n      expect(terminus).to be_authorized(request)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/hiera_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/data_binding'\nrequire 'puppet/indirector/hiera'\n\nbegin\n  require 'hiera/backend'\nrescue LoadError => e\n  Puppet.warning(_(\"Unable to load Hiera 3 backend: %{message}\") % {message: e.message})\nend\n\ndescribe Puppet::Indirector::Hiera, :if => Puppet.features.hiera? do\n\n  module Testing\n    module DataBinding\n      class Hiera < Puppet::Indirector::Hiera\n      end\n    end\n  end\n\n  it_should_behave_like \"Hiera indirection\", Testing::DataBinding::Hiera, my_fixture_dir\nend\n\n"
  },
  {
    "path": "spec/unit/indirector/indirection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/indirection'\n\nshared_examples_for \"Indirection Delegator\" do\n  it \"should create a request object with the appropriate method name and all of the passed arguments\" do\n    request = Puppet::Indirector::Request.new(:indirection, :find, \"me\", nil)\n\n    expect(@indirection).to receive(:request).with(@method, \"mystuff\", nil, {:one => :two}).and_return(request)\n\n    allow(@terminus).to receive(@method)\n\n    @indirection.send(@method, \"mystuff\", :one => :two)\n  end\n\n  it \"should choose the terminus returned by the :terminus_class\" do\n    expect(@indirection).to receive(:terminus_class).and_return(:test_terminus)\n\n    expect(@terminus).to receive(@method)\n\n    @indirection.send(@method, \"me\")\n  end\n\n  it \"should let the appropriate terminus perform the lookup\" do\n    expect(@terminus).to receive(@method).with(be_a(Puppet::Indirector::Request))\n    @indirection.send(@method, \"me\")\n  end\nend\n\nshared_examples_for \"Delegation Authorizer\" do\n  before do\n    # So the :respond_to? turns out correctly.\n    class << @terminus\n      def authorized?\n      end\n    end\n  end\n\n  it \"should not check authorization if a node name is not provided\" do\n    expect(@terminus).not_to receive(:authorized?)\n    allow(@terminus).to receive(@method)\n\n    # The parenthesis are necessary here, else it looks like a block.\n    allow(@request).to receive(:options).and_return({})\n    @indirection.send(@method, \"/my/key\")\n  end\n\n  it \"should pass the request to the terminus's authorization method\" do\n    expect(@terminus).to receive(:authorized?).with(be_a(Puppet::Indirector::Request)).and_return(true)\n    allow(@terminus).to receive(@method)\n\n    @indirection.send(@method, \"/my/key\", :node => \"mynode\")\n  end\n\n  it \"should fail if authorization returns false\" do\n    expect(@terminus).to receive(:authorized?).and_return(false)\n    allow(@terminus).to receive(@method)\n    expect { @indirection.send(@method, \"/my/key\", :node => \"mynode\") }.to raise_error(ArgumentError)\n  end\n\n  it \"should continue if authorization returns true\" do\n    expect(@terminus).to receive(:authorized?).and_return(true)\n    allow(@terminus).to receive(@method)\n    @indirection.send(@method, \"/my/key\", :node => \"mynode\")\n  end\nend\n\nshared_examples_for \"Request validator\" do\n  it \"asks the terminus to validate the request\" do\n    expect(@terminus).to receive(:validate).and_raise(Puppet::Indirector::ValidationError, \"Invalid\")\n    expect(@terminus).not_to receive(@method)\n    expect {\n      @indirection.send(@method, \"key\")\n    }.to raise_error Puppet::Indirector::ValidationError\n  end\nend\n\ndescribe Puppet::Indirector::Indirection do\n  describe \"when initializing\" do\n    it \"should keep a reference to the indirecting model\" do\n      model = double('model')\n      @indirection = Puppet::Indirector::Indirection.new(model, :myind)\n      expect(@indirection.model).to equal(model)\n    end\n\n    it \"should set the name\" do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :myind)\n      expect(@indirection.name).to eq(:myind)\n    end\n\n    it \"should require indirections to have unique names\" do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      expect { Puppet::Indirector::Indirection.new(:test) }.to raise_error(ArgumentError)\n    end\n\n    it \"should extend itself with any specified module\" do\n      mod = Module.new\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test, :extend => mod)\n      expect(@indirection.singleton_class.included_modules).to include(mod)\n    end\n\n    after do\n      @indirection.delete if defined?(@indirection)\n    end\n  end\n\n  describe \"when an instance\" do\n    before :each do\n      @terminus_class = double('terminus_class')\n      @terminus = double('terminus')\n      allow(@terminus).to receive(:validate)\n      allow(@terminus_class).to receive(:new).and_return(@terminus)\n      @cache = double('cache', :name => \"mycache\")\n      @cache_class = double('cache_class')\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :cache_terminus).and_return(@cache_class)\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :test_terminus).and_return(@terminus_class)\n\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      @indirection.terminus_class = :test_terminus\n\n      @instance = double('instance', :expiration => nil, :expiration= => nil, :name => \"whatever\")\n      @name = :mything\n\n      @request = double('instance')\n    end\n\n    describe 'ensure that indirection settings are threadsafe' do\n      before :each do\n        @alt_term = double('alternate_terminus')\n        expect(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :alternate_terminus).and_return(@alt_term)\n        @alt_cache= double('alternate_cache')\n        expect(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :alternate_cache).and_return(@alt_cache)\n      end\n\n      it 'does not change the original value when modified in new thread' do\n        Thread.new do\n          @indirection.terminus_class = :alternate_terminus\n          @indirection.terminus_setting = :alternate_terminus_setting\n          @indirection.cache_class = :alternate_cache\n        end.join\n        expect(@indirection.terminus_class).to eq(:test_terminus)\n        expect(@indirection.terminus_setting).to eq(nil)\n        expect(@indirection.cache_class).to eq(nil)\n      end\n\n      it 'can modify indirection settings globally for all threads using the global setter' do\n        Thread.new do\n          @indirection.set_global_setting(:terminus_class, :alternate_terminus)\n          @indirection.set_global_setting(:terminus_setting, :alternate_terminus_setting)\n          @indirection.set_global_setting(:cache_class, :alternate_cache)\n        end.join\n        expect(@indirection.terminus_class).to eq(:alternate_terminus)\n        expect(@indirection.terminus_setting).to eq(:alternate_terminus_setting)\n        expect(@indirection.cache_class).to eq(:alternate_cache)\n      end\n    end\n\n    it \"should allow setting the ttl\" do\n      @indirection.ttl = 300\n      expect(@indirection.ttl).to eq(300)\n    end\n\n    it \"should default to the :runinterval setting, converted to an integer, for its ttl\" do\n      Puppet[:runinterval] = 1800\n      expect(@indirection.ttl).to eq(1800)\n    end\n\n    it \"should calculate the current expiration by adding the TTL to the current time\" do\n      allow(@indirection).to receive(:ttl).and_return(100)\n      now = Time.now\n      allow(Time).to receive(:now).and_return(now)\n      expect(@indirection.expiration).to eq(Time.now + 100)\n    end\n\n    it \"should have a method for creating an indirection request instance\" do\n      expect(@indirection).to respond_to(:request)\n    end\n\n    describe \"creates a request\" do\n      it \"should create it with its name as the request's indirection name\" do\n        expect(@indirection.request(:funtest, \"yayness\", nil).indirection_name).to eq(@indirection.name)\n      end\n\n      it \"should require a method and key\" do\n        request = @indirection.request(:funtest, \"yayness\", nil)\n        expect(request.method).to eq(:funtest)\n        expect(request.key).to eq(\"yayness\")\n      end\n\n      it \"should support optional arguments\" do\n        expect(@indirection.request(:funtest, \"yayness\", nil, :one => :two).options).to eq(:one => :two)\n      end\n\n      it \"should not pass options if none are supplied\" do\n        expect(@indirection.request(:funtest, \"yayness\", nil).options).to eq({})\n      end\n\n      it \"should return the request\" do\n        expect(@indirection.request(:funtest, \"yayness\", nil)).to be_a(Puppet::Indirector::Request)\n      end\n    end\n\n    describe \"and looking for a model instance\" do\n      before { @method = :find }\n\n      it_should_behave_like \"Indirection Delegator\"\n      it_should_behave_like \"Delegation Authorizer\"\n      it_should_behave_like \"Request validator\"\n\n      it \"should return the results of the delegation\" do\n        expect(@terminus).to receive(:find).and_return(@instance)\n        expect(@indirection.find(\"me\")).to equal(@instance)\n      end\n\n      it \"should return false if the instance is false\" do\n        expect(@terminus).to receive(:find).and_return(false)\n        expect(@indirection.find(\"me\")).to equal(false)\n      end\n\n      it \"should set the expiration date on any instances without one set\" do\n        allow(@terminus).to receive(:find).and_return(@instance)\n\n        expect(@indirection).to receive(:expiration).and_return(:yay)\n\n        expect(@instance).to receive(:expiration).and_return(nil)\n        expect(@instance).to receive(:expiration=).with(:yay)\n\n        @indirection.find(\"/my/key\")\n      end\n\n      it \"should not override an already-set expiration date on returned instances\" do\n        allow(@terminus).to receive(:find).and_return(@instance)\n\n        expect(@indirection).not_to receive(:expiration)\n\n        expect(@instance).to receive(:expiration).and_return(:yay)\n        expect(@instance).not_to receive(:expiration=)\n\n        @indirection.find(\"/my/key\")\n      end\n\n      it \"should filter the result instance if the terminus supports it\" do\n        allow(@terminus).to receive(:find).and_return(@instance)\n        allow(@terminus).to receive(:respond_to?).with(:filter).and_return(true)\n\n        expect(@terminus).to receive(:filter).with(@instance)\n\n        @indirection.find(\"/my/key\")\n      end\n\n      describe \"when caching is enabled\" do\n        before do\n          @indirection.cache_class = :cache_terminus\n          allow(@cache_class).to receive(:new).and_return(@cache)\n\n          allow(@instance).to receive(:expired?).and_return(false)\n        end\n\n        it \"should first look in the cache for an instance\" do\n          expect(@terminus).not_to receive(:find)\n          expect(@cache).to receive(:find).and_return(@instance)\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should not look in the cache if the request specifies not to use the cache\" do\n          expect(@terminus).to receive(:find).and_return(@instance)\n          expect(@cache).not_to receive(:find)\n          allow(@cache).to receive(:save)\n\n          @indirection.find(\"/my/key\", :ignore_cache => true)\n        end\n\n        it \"should still save to the cache even if the cache is being ignored during readin\" do\n          expect(@terminus).to receive(:find).and_return(@instance)\n          expect(@cache).to receive(:save)\n\n          @indirection.find(\"/my/key\", :ignore_cache => true)\n        end\n\n        it \"should not save to the cache if told to skip updating the cache\" do\n          expect(@terminus).to receive(:find).and_return(@instance)\n          expect(@cache).to receive(:find).and_return(nil)\n          expect(@cache).not_to receive(:save)\n\n          @indirection.find(\"/my/key\", :ignore_cache_save => true)\n        end\n\n        it \"should only look in the cache if the request specifies not to use the terminus\" do\n          expect(@terminus).not_to receive(:find)\n          expect(@cache).to receive(:find)\n\n          @indirection.find(\"/my/key\", :ignore_terminus => true)\n        end\n\n        it \"should use a request to look in the cache for cached objects\" do\n          expect(@cache).to receive(:find) do |r|\n            expect(r.method).to eq(:find)\n            expect(r.key).to eq(\"/my/key\")\n\n            @instance\n          end\n\n          allow(@cache).to receive(:save)\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should return the cached object if it is not expired\" do\n          allow(@instance).to receive(:expired?).and_return(false)\n\n          allow(@cache).to receive(:find).and_return(@instance)\n          expect(@indirection.find(\"/my/key\")).to equal(@instance)\n        end\n\n        it \"should not fail if the cache fails\" do\n          allow(@terminus).to receive(:find).and_return(@instance)\n\n          expect(@cache).to receive(:find).and_raise(ArgumentError)\n          allow(@cache).to receive(:save)\n          expect { @indirection.find(\"/my/key\") }.not_to raise_error\n        end\n\n        it \"should look in the main terminus if the cache fails\" do\n          expect(@terminus).to receive(:find).and_return(@instance)\n          expect(@cache).to receive(:find).and_raise(ArgumentError)\n          allow(@cache).to receive(:save)\n          expect(@indirection.find(\"/my/key\")).to equal(@instance)\n        end\n\n        it \"should send a debug log if it is using the cached object\" do\n          expect(Puppet).to receive(:debug)\n          allow(@cache).to receive(:find).and_return(@instance)\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should not return the cached object if it is expired\" do\n          allow(@instance).to receive(:expired?).and_return(true)\n\n          allow(@cache).to receive(:find).and_return(@instance)\n          allow(@terminus).to receive(:find).and_return(nil)\n          expect(@indirection.find(\"/my/key\")).to be_nil\n        end\n\n        it \"should send an info log if it is using the cached object\" do\n          expect(Puppet).to receive(:info)\n          allow(@instance).to receive(:expired?).and_return(true)\n\n          allow(@cache).to receive(:find).and_return(@instance)\n          allow(@terminus).to receive(:find).and_return(nil)\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should cache any objects not retrieved from the cache\" do\n          expect(@cache).to receive(:find).and_return(nil)\n\n          expect(@terminus).to receive(:find).and_return(@instance)\n          expect(@cache).to receive(:save)\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should use a request to look in the cache for cached objects\" do\n          expect(@cache).to receive(:find) do |r|\n            expect(r.method).to eq(:find)\n            expect(r.key).to eq(\"/my/key\")\n\n            nil\n          end\n\n          allow(@terminus).to receive(:find).and_return(@instance)\n          allow(@cache).to receive(:save)\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should cache the instance using a request with the instance set to the cached object\" do\n          allow(@cache).to receive(:find).and_return(nil)\n\n          allow(@terminus).to receive(:find).and_return(@instance)\n\n          expect(@cache).to receive(:save) do |r|\n            expect(r.method).to eq(:save)\n            expect(r.instance).to eq(@instance)\n          end\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should send an info log that the object is being cached\" do\n          allow(@cache).to receive(:find).and_return(nil)\n\n          allow(@terminus).to receive(:find).and_return(@instance)\n          allow(@cache).to receive(:save)\n\n          expect(Puppet).to receive(:info)\n\n          @indirection.find(\"/my/key\")\n        end\n\n        it \"should fail if saving to the cache fails but log the exception\" do\n          allow(@cache).to receive(:find).and_return(nil)\n\n          allow(@terminus).to receive(:find).and_return(@instance)\n          allow(@cache).to receive(:save).and_raise(RuntimeError)\n\n          expect(Puppet).to receive(:log_exception)\n\n          expect { @indirection.find(\"/my/key\") }.to raise_error RuntimeError\n        end\n      end\n    end\n\n    describe \"and doing a head operation\" do\n      before { @method = :head }\n\n      it_should_behave_like \"Indirection Delegator\"\n      it_should_behave_like \"Delegation Authorizer\"\n      it_should_behave_like \"Request validator\"\n\n      it \"should return true if the head method returned true\" do\n        expect(@terminus).to receive(:head).and_return(true)\n        expect(@indirection.head(\"me\")).to eq(true)\n      end\n\n      it \"should return false if the head method returned false\" do\n        expect(@terminus).to receive(:head).and_return(false)\n        expect(@indirection.head(\"me\")).to eq(false)\n      end\n\n      describe \"when caching is enabled\" do\n        before do\n          @indirection.cache_class = :cache_terminus\n          allow(@cache_class).to receive(:new).and_return(@cache)\n\n          allow(@instance).to receive(:expired?).and_return(false)\n        end\n\n        it \"should first look in the cache for an instance\" do\n          expect(@terminus).not_to receive(:find)\n          expect(@terminus).not_to receive(:head)\n          expect(@cache).to receive(:find).and_return(@instance)\n\n          expect(@indirection.head(\"/my/key\")).to eq(true)\n        end\n\n        it \"should not save to the cache\" do\n          expect(@cache).to receive(:find).and_return(nil)\n          expect(@cache).not_to receive(:save)\n          expect(@terminus).to receive(:head).and_return(true)\n          expect(@indirection.head(\"/my/key\")).to eq(true)\n        end\n\n        it \"should not fail if the cache fails\" do\n          allow(@terminus).to receive(:head).and_return(true)\n\n          expect(@cache).to receive(:find).and_raise(ArgumentError)\n          expect { @indirection.head(\"/my/key\") }.not_to raise_error\n        end\n\n        it \"should look in the main terminus if the cache fails\" do\n          expect(@terminus).to receive(:head).and_return(true)\n          expect(@cache).to receive(:find).and_raise(ArgumentError)\n          expect(@indirection.head(\"/my/key\")).to eq(true)\n        end\n\n        it \"should send a debug log if it is using the cached object\" do\n          expect(Puppet).to receive(:debug)\n          allow(@cache).to receive(:find).and_return(@instance)\n\n          @indirection.head(\"/my/key\")\n        end\n\n        it \"should not accept the cached object if it is expired\" do\n          allow(@instance).to receive(:expired?).and_return(true)\n\n          allow(@cache).to receive(:find).and_return(@instance)\n          allow(@terminus).to receive(:head).and_return(false)\n          expect(@indirection.head(\"/my/key\")).to eq(false)\n        end\n      end\n    end\n\n    describe \"and storing a model instance\" do\n      before { @method = :save }\n\n      it \"should return the result of the save\" do\n        allow(@terminus).to receive(:save).and_return(\"foo\")\n        expect(@indirection.save(@instance)).to eq(\"foo\")\n      end\n\n      describe \"when caching is enabled\" do\n        before do\n          @indirection.cache_class = :cache_terminus\n          allow(@cache_class).to receive(:new).and_return(@cache)\n\n          allow(@instance).to receive(:expired?).and_return(false)\n        end\n\n        it \"should return the result of saving to the terminus\" do\n          request = double('request', :instance => @instance, :node => nil, :ignore_cache_save? => false, :ignore_terminus? => false)\n\n          expect(@indirection).to receive(:request).and_return(request)\n\n          allow(@cache).to receive(:save)\n          allow(@terminus).to receive(:save).and_return(@instance)\n          expect(@indirection.save(@instance)).to equal(@instance)\n        end\n\n        it \"should use a request to save the object to the cache\" do\n          request = double('request', :instance => @instance, :node => nil, :ignore_cache_save? => false, :ignore_terminus? => false)\n\n          expect(@indirection).to receive(:request).and_return(request)\n\n          expect(@cache).to receive(:save).with(request)\n          allow(@terminus).to receive(:save)\n          @indirection.save(@instance)\n        end\n\n        it \"should not save to the cache if the normal save fails\" do\n          request = double('request', :instance => @instance, :node => nil, :ignore_terminus? => false)\n\n          expect(@indirection).to receive(:request).and_return(request)\n\n          expect(@cache).not_to receive(:save)\n          expect(@terminus).to receive(:save).and_raise(\"eh\")\n          expect { @indirection.save(@instance) }.to raise_error(RuntimeError, /eh/)\n        end\n\n        it \"should not save to the cache if told to ignore saving to the cache\" do\n          expect(@terminus).to receive(:save)\n          expect(@cache).not_to receive(:save)\n\n          @indirection.save(@instance, '/my/key', :ignore_cache_save => true)\n        end\n\n        it \"should only save to the cache if the request specifies not to use the terminus\" do\n          expect(@terminus).not_to receive(:save)\n          expect(@cache).to receive(:save)\n\n          @indirection.save(@instance, \"/my/key\", :ignore_terminus => true)\n        end\n      end\n    end\n\n    describe \"and removing a model instance\" do\n      before { @method = :destroy }\n\n      it_should_behave_like \"Indirection Delegator\"\n      it_should_behave_like \"Delegation Authorizer\"\n      it_should_behave_like \"Request validator\"\n\n      it \"should return the result of removing the instance\" do\n        allow(@terminus).to receive(:destroy).and_return(\"yayness\")\n        expect(@indirection.destroy(\"/my/key\")).to eq(\"yayness\")\n      end\n\n      describe \"when caching is enabled\" do\n        before do\n          @indirection.cache_class = :cache_terminus\n          expect(@cache_class).to receive(:new).and_return(@cache)\n\n          allow(@instance).to receive(:expired?).and_return(false)\n        end\n\n        it \"should use a request instance to search in and remove objects from the cache\" do\n          destroy = double('destroy_request', :key => \"/my/key\", :node => nil)\n          find = double('destroy_request', :key => \"/my/key\", :node => nil)\n\n          expect(@indirection).to receive(:request).with(:destroy, \"/my/key\", nil, be_a(Hash).or(be_nil)).and_return(destroy)\n          expect(@indirection).to receive(:request).with(:find, \"/my/key\", nil, be_a(Hash).or(be_nil)).and_return(find)\n\n          cached = double('cache')\n\n          expect(@cache).to receive(:find).with(find).and_return(cached)\n          expect(@cache).to receive(:destroy).with(destroy)\n\n          allow(@terminus).to receive(:destroy)\n\n          @indirection.destroy(\"/my/key\")\n        end\n      end\n    end\n\n    describe \"and searching for multiple model instances\" do\n      before { @method = :search }\n\n      it_should_behave_like \"Indirection Delegator\"\n      it_should_behave_like \"Delegation Authorizer\"\n      it_should_behave_like \"Request validator\"\n\n      it \"should set the expiration date on any instances without one set\" do\n        allow(@terminus).to receive(:search).and_return([@instance])\n\n        expect(@indirection).to receive(:expiration).and_return(:yay)\n\n        expect(@instance).to receive(:expiration).and_return(nil)\n        expect(@instance).to receive(:expiration=).with(:yay)\n\n        @indirection.search(\"/my/key\")\n      end\n\n      it \"should not override an already-set expiration date on returned instances\" do\n        allow(@terminus).to receive(:search).and_return([@instance])\n\n        expect(@indirection).not_to receive(:expiration)\n\n        expect(@instance).to receive(:expiration).and_return(:yay)\n        expect(@instance).not_to receive(:expiration=)\n\n        @indirection.search(\"/my/key\")\n      end\n\n      it \"should return the results of searching in the terminus\" do\n        expect(@terminus).to receive(:search).and_return([@instance])\n        expect(@indirection.search(\"/my/key\")).to eq([@instance])\n      end\n    end\n\n    describe \"and expiring a model instance\" do\n      describe \"when caching is not enabled\" do\n        it \"should do nothing\" do\n          expect(@cache_class).not_to receive(:new)\n\n          @indirection.expire(\"/my/key\")\n        end\n      end\n\n      describe \"when caching is enabled\" do\n        before do\n          @indirection.cache_class = :cache_terminus\n          expect(@cache_class).to receive(:new).and_return(@cache)\n\n          allow(@instance).to receive(:expired?).and_return(false)\n\n          @cached = double('cached', :expiration= => nil, :name => \"/my/key\")\n        end\n\n        it \"should use a request to find within the cache\" do\n          expect(@cache).to receive(:find) do |r|\n            expect(r).to be_a(Puppet::Indirector::Request)\n            expect(r.method).to eq(:find)\n            nil\n          end\n          @indirection.expire(\"/my/key\")\n        end\n\n        it \"should do nothing if no such instance is cached\" do\n          expect(@cache).to receive(:find).and_return(nil)\n\n          @indirection.expire(\"/my/key\")\n        end\n\n        it \"should log when expiring a found instance\" do\n          expect(@cache).to receive(:find).and_return(@cached)\n          allow(@cache).to receive(:save)\n\n          expect(Puppet).to receive(:info)\n\n          @indirection.expire(\"/my/key\")\n        end\n\n        it \"should set the cached instance's expiration to a time in the past\" do\n          expect(@cache).to receive(:find).and_return(@cached)\n          allow(@cache).to receive(:save)\n\n          expect(@cached).to receive(:expiration=).with(be < Time.now)\n\n          @indirection.expire(\"/my/key\")\n        end\n\n        it \"should save the now expired instance back into the cache\" do\n          expect(@cache).to receive(:find).and_return(@cached)\n\n          expect(@cached).to receive(:expiration=).with(be < Time.now)\n\n          expect(@cache).to receive(:save)\n\n          @indirection.expire(\"/my/key\")\n        end\n\n        it \"does not expire an instance if told to skip cache saving\" do\n          expect(@indirection.cache).not_to receive(:find)\n          expect(@indirection.cache).not_to receive(:save)\n\n          @indirection.expire(\"/my/key\", :ignore_cache_save => true)\n        end\n\n        it \"should use a request to save the expired resource to the cache\" do\n          expect(@cache).to receive(:find).and_return(@cached)\n\n          expect(@cached).to receive(:expiration=).with(be < Time.now)\n\n          expect(@cache).to receive(:save) do |r|\n            expect(r).to be_a(Puppet::Indirector::Request)\n            expect(r.instance).to eq(@cached)\n            expect(r.method).to eq(:save)\n\n            @cached\n          end\n\n          @indirection.expire(\"/my/key\")\n        end\n      end\n    end\n\n    after :each do\n      @indirection.delete\n    end\n  end\n\n\n  describe \"when managing indirection instances\" do\n    it \"should allow an indirection to be retrieved by name\" do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      expect(Puppet::Indirector::Indirection.instance(:test)).to equal(@indirection)\n    end\n\n    it \"should return nil when the named indirection has not been created\" do\n      expect(Puppet::Indirector::Indirection.instance(:test)).to be_nil\n    end\n\n    it \"should allow an indirection's model to be retrieved by name\" do\n      mock_model = double('model')\n      @indirection = Puppet::Indirector::Indirection.new(mock_model, :test)\n      expect(Puppet::Indirector::Indirection.model(:test)).to equal(mock_model)\n    end\n\n    it \"should return nil when no model matches the requested name\" do\n      expect(Puppet::Indirector::Indirection.model(:test)).to be_nil\n    end\n\n    after do\n      @indirection.delete if defined?(@indirection)\n    end\n  end\n\n  describe \"when routing to the correct the terminus class\" do\n    before do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      @terminus = double('terminus')\n      @terminus_class = double('terminus class', :new => @terminus)\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :default).and_return(@terminus_class)\n    end\n\n    it \"should fail if no terminus class can be picked\" do\n      expect { @indirection.terminus_class }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should choose the default terminus class if one is specified\" do\n      @indirection.terminus_class = :default\n      expect(@indirection.terminus_class).to equal(:default)\n    end\n\n    it \"should use the provided Puppet setting if told to do so\" do\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :my_terminus).and_return(double(\"terminus_class2\"))\n      Puppet[:node_terminus] = :my_terminus\n      @indirection.terminus_setting = :node_terminus\n      expect(@indirection.terminus_class).to equal(:my_terminus)\n    end\n\n    it \"should fail if the provided terminus class is not valid\" do\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :nosuchclass).and_return(nil)\n      expect { @indirection.terminus_class = :nosuchclass }.to raise_error(ArgumentError)\n    end\n\n    after do\n      @indirection.delete if defined?(@indirection)\n    end\n  end\n\n  describe \"when specifying the terminus class to use\" do\n    before do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      @terminus = double('terminus')\n      allow(@terminus).to receive(:validate)\n      @terminus_class = double('terminus class', :new => @terminus)\n    end\n\n    it \"should allow specification of a terminus type\" do\n      expect(@indirection).to respond_to(:terminus_class=)\n    end\n\n    it \"should fail to redirect if no terminus type has been specified\" do\n      expect { @indirection.find(\"blah\") }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should fail when the terminus class name is an empty string\" do\n      expect { @indirection.terminus_class = \"\" }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail when the terminus class name is nil\" do\n      expect { @indirection.terminus_class = nil }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail when the specified terminus class cannot be found\" do\n      expect(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :foo).and_return(nil)\n      expect { @indirection.terminus_class = :foo }.to raise_error(ArgumentError)\n    end\n\n    it \"should select the specified terminus class if a terminus class name is provided\" do\n      expect(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :foo).and_return(@terminus_class)\n      expect(@indirection.terminus(:foo)).to equal(@terminus)\n    end\n\n    it \"should use the configured terminus class if no terminus name is specified\" do\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :foo).and_return(@terminus_class)\n      @indirection.terminus_class = :foo\n      expect(@indirection.terminus).to equal(@terminus)\n    end\n\n    after do\n      @indirection.delete if defined?(@indirection)\n    end\n  end\n\n  describe \"when managing terminus instances\" do\n    before do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      @terminus = double('terminus')\n      @terminus_class = double('terminus class')\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :foo).and_return(@terminus_class)\n    end\n\n    it \"should create an instance of the chosen terminus class\" do\n      allow(@terminus_class).to receive(:new).and_return(@terminus)\n      expect(@indirection.terminus(:foo)).to equal(@terminus)\n    end\n\n    # Make sure it caches the terminus.\n    it \"should return the same terminus instance each time for a given name\" do\n      allow(@terminus_class).to receive(:new).and_return(@terminus)\n      expect(@indirection.terminus(:foo)).to equal(@terminus)\n      expect(@indirection.terminus(:foo)).to equal(@terminus)\n    end\n\n    it \"should not create a terminus instance until one is actually needed\" do\n      expect(@indirection).not_to receive(:terminus)\n      Puppet::Indirector::Indirection.new(double('model'), :lazytest)\n    end\n\n    after do\n      @indirection.delete\n    end\n  end\n\n  describe \"when deciding whether to cache\" do\n    before do\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      @terminus = double('terminus')\n      @terminus_class = double('terminus class')\n      allow(@terminus_class).to receive(:new).and_return(@terminus)\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :foo).and_return(@terminus_class)\n      @indirection.terminus_class = :foo\n    end\n\n    it \"should provide a method for setting the cache terminus class\" do\n      expect(@indirection).to respond_to(:cache_class=)\n    end\n\n    it \"should fail to cache if no cache type has been specified\" do\n      expect { @indirection.cache }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should fail to set the cache class when the cache class name is an empty string\" do\n      expect { @indirection.cache_class = \"\" }.to raise_error(ArgumentError)\n    end\n\n    it \"should allow resetting the cache_class to nil\" do\n      @indirection.cache_class = nil\n      expect(@indirection.cache_class).to be_nil\n    end\n\n    it \"should fail to set the cache class when the specified cache class cannot be found\" do\n      expect(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :foo).and_return(nil)\n      expect { @indirection.cache_class = :foo }.to raise_error(ArgumentError)\n    end\n\n    after do\n      @indirection.delete\n    end\n  end\n\n  describe \"when using a cache\" do\n    before :each do\n      @terminus_class = double('terminus_class')\n      @terminus = double('terminus')\n      allow(@terminus_class).to receive(:new).and_return(@terminus)\n      @cache = double('cache')\n      @cache_class = double('cache_class')\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :cache_terminus).and_return(@cache_class)\n      allow(Puppet::Indirector::Terminus).to receive(:terminus_class).with(:test, :test_terminus).and_return(@terminus_class)\n      @indirection = Puppet::Indirector::Indirection.new(double('model'), :test)\n      @indirection.terminus_class = :test_terminus\n    end\n\n    describe \"and managing the cache terminus\" do\n      it \"should not create a cache terminus at initialization\" do\n        # This is weird, because all of the code is in the setup.  If we got\n        # new called on the cache class, we'd get an exception here.\n      end\n\n      it \"should reuse the cache terminus\" do\n        expect(@cache_class).to receive(:new).and_return(@cache)\n        @indirection.cache_class = :cache_terminus\n        expect(@indirection.cache).to equal(@cache)\n        expect(@indirection.cache).to equal(@cache)\n      end\n    end\n\n    after :each do\n      @indirection.delete\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/json_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/indirector/indirector_testing/json'\n\ndescribe Puppet::Indirector::JSON do\n  include PuppetSpec::Files\n\n  subject { Puppet::IndirectorTesting::JSON.new }\n  let :model       do Puppet::IndirectorTesting end\n  let :indirection do model.indirection end\n\n  context \"#path\" do\n    before :each do\n      Puppet[:server_datadir] = '/sample/datadir/server'\n      Puppet[:client_datadir] = '/sample/datadir/client'\n    end\n\n    it \"uses the :server_datadir setting if this is the server\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      expected = File.join(Puppet[:server_datadir], 'indirector_testing', 'testing.json')\n      expect(subject.path('testing')).to eq(expected)\n    end\n\n    it \"uses the :client_datadir setting if this is not the server\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(false)\n      expected = File.join(Puppet[:client_datadir], 'indirector_testing', 'testing.json')\n      expect(subject.path('testing')).to eq(expected)\n    end\n\n    it \"overrides the default extension with a supplied value\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      expected = File.join(Puppet[:server_datadir], 'indirector_testing', 'testing.not-json')\n      expect(subject.path('testing', '.not-json')).to eq(expected)\n    end\n\n    ['../foo', '..\\\\foo', './../foo', '.\\\\..\\\\foo',\n     '/foo', '//foo', '\\\\foo', '\\\\\\\\goo',\n     \"test\\0/../bar\", \"test\\0\\\\..\\\\bar\",\n     \"..\\\\/bar\", \"/tmp/bar\", \"/tmp\\\\bar\", \"tmp\\\\bar\",\n     \" / bar\", \" /../ bar\", \" \\\\..\\\\ bar\",\n     \"c:\\\\foo\", \"c:/foo\", \"\\\\\\\\?\\\\UNC\\\\bar\", \"\\\\\\\\foo\\\\bar\",\n     \"\\\\\\\\?\\\\c:\\\\foo\", \"//?/UNC/bar\", \"//foo/bar\",\n     \"//?/c:/foo\",\n    ].each do |input|\n      it \"should resist directory traversal attacks (#{input.inspect})\" do\n        expect { subject.path(input) }.to raise_error ArgumentError, 'invalid key'\n      end\n    end\n  end\n\n  context \"handling requests\" do\n    before :each do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:server_datadir] = tmpdir('jsondir')\n      FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing'))\n    end\n\n    let :file do subject.path(request.key) end\n\n    def with_content(text)\n      FileUtils.mkdir_p(File.dirname(file))\n      File.binwrite(file, text)\n      yield if block_given?\n    end\n\n    it \"data saves and then loads again correctly\" do\n      subject.save(indirection.request(:save, 'example', model.new('banana')))\n      expect(subject.find(indirection.request(:find, 'example', nil)).value).to eq('banana')\n    end\n\n    context \"#find\" do\n      let :request do indirection.request(:find, 'example', nil) end\n\n      it \"returns nil if the file doesn't exist\" do\n        expect(subject.find(request)).to be_nil\n      end\n\n      it \"raises a descriptive error when the file can't be read\" do\n        with_content(model.new('foo').to_json) do\n          # I don't like this, but there isn't a credible alternative that\n          # also works on Windows, so a stub it is. At least the expectation\n          # will fail if the implementation changes. Sorry to the next dev.\n          expect(Puppet::FileSystem).to receive(:read).with(file, anything).and_raise(Errno::EPERM)\n          expect { subject.find(request) }.\n            to raise_error Puppet::Error, /Could not read JSON/\n        end\n      end\n\n      it \"raises a descriptive error when the file content is invalid\" do\n        with_content(\"this is totally invalid JSON\") do\n          expect { subject.find(request) }.\n            to raise_error Puppet::Error, /Could not parse JSON data/\n        end\n      end\n\n      it \"raises if the content contains binary\" do\n        binary = \"\\xC0\\xFF\".force_encoding(Encoding::BINARY)\n\n        with_content(binary) do\n          expect {\n            subject.find(request)\n          }.to raise_error Puppet::Error, /Could not parse JSON data/\n        end\n      end\n\n      it \"should return an instance of the indirected object when valid\" do\n        with_content(model.new(1).to_json) do\n          instance = subject.find(request)\n          expect(instance).to be_an_instance_of model\n          expect(instance.value).to eq(1)\n        end\n      end\n    end\n\n    context \"#save\" do\n      let :instance do model.new(4) end\n      let :request  do indirection.request(:find, 'example', instance) end\n\n      it \"should save the instance of the request as JSON to disk\" do\n        subject.save(request)\n        content = File.read(file)\n        expect(content).to match(/\"value\"\\s*:\\s*4/)\n      end\n\n      it \"should create the indirection directory if required\" do\n        target = File.join(Puppet[:server_datadir], 'indirector_testing')\n        Dir.rmdir(target)\n\n        subject.save(request)\n\n        expect(File).to be_directory(target)\n      end\n    end\n\n    context \"#destroy\" do\n      let :request do indirection.request(:find, 'example', nil) end\n\n      it \"removes an existing file\" do\n        with_content('hello') do\n          subject.destroy(request)\n        end\n        expect(Puppet::FileSystem.exist?(file)).to be_falsey\n      end\n\n      it \"silently succeeds when files don't exist\" do\n        Puppet::FileSystem.unlink(file) rescue nil\n        expect(subject.destroy(request)).to be_truthy\n      end\n\n      it \"raises an informative error for other failures\" do\n        allow(Puppet::FileSystem).to receive(:unlink).with(file).and_raise(Errno::EPERM, 'fake permission problem')\n        with_content('hello') do\n          expect { subject.destroy(request) }.to raise_error(Puppet::Error)\n        end\n      end\n    end\n  end\n\n  context \"#search\" do\n    before :each do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:server_datadir] = tmpdir('jsondir')\n      FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing'))\n    end\n\n    def request(glob)\n      indirection.request(:search, glob, nil)\n    end\n\n    def create_file(name, value = 12)\n      File.open(subject.path(name, ''), 'wb') do |f|\n        f.puts Puppet::IndirectorTesting.new(value).to_json\n      end\n    end\n\n    it \"returns an empty array when nothing matches the key as a glob\" do\n      expect(subject.search(request('*'))).to eq([])\n    end\n\n    it \"returns an array with one item if one item matches\" do\n      create_file('foo.json', 'foo')\n      create_file('bar.json', 'bar')\n      expect(subject.search(request('f*')).map(&:value)).to eq(['foo'])\n    end\n\n    it \"returns an array of items when more than one item matches\" do\n      create_file('foo.json', 'foo')\n      create_file('bar.json', 'bar')\n      create_file('baz.json', 'baz')\n      expect(subject.search(request('b*')).map(&:value)).to match_array(['bar', 'baz'])\n    end\n\n    it \"only items with the .json extension\" do\n      create_file('foo.json', 'foo-json')\n      create_file('foo.pson', 'foo-pson')\n      create_file('foo.json~', 'foo-backup')\n      expect(subject.search(request('f*')).map(&:value)).to eq(['foo-json'])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/memory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/memory'\n\nrequire 'shared_behaviours/memory_terminus'\n\ndescribe Puppet::Indirector::Memory do\n  it_should_behave_like \"A Memory Terminus\"\n\n  before do\n    allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)\n    @model = double('model')\n    @indirection = double('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model)\n    allow(Puppet::Indirector::Indirection).to receive(:instance).and_return(@indirection)\n\n    module Testing; end\n    @memory_class = class Testing::MyMemory < Puppet::Indirector::Memory\n      self\n    end\n\n    @searcher = @memory_class.new\n    @name = \"me\"\n    @instance = double('instance', :name => @name)\n\n    @request = double('request', :key => @name, :instance => @instance)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/msgpack_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/indirector/indirector_testing/msgpack'\n\ndescribe Puppet::Indirector::Msgpack, :if => Puppet.features.msgpack? do\n  include PuppetSpec::Files\n\n  subject { Puppet::IndirectorTesting::Msgpack.new }\n  let :model       do Puppet::IndirectorTesting end\n  let :indirection do model.indirection end\n\n  context \"#path\" do\n    before :each do\n      Puppet[:server_datadir] = '/sample/datadir/server'\n      Puppet[:client_datadir] = '/sample/datadir/client'\n    end\n\n    it \"uses the :server_datadir setting if this is the server\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      expected = File.join(Puppet[:server_datadir], 'indirector_testing', 'testing.msgpack')\n      expect(subject.path('testing')).to eq(expected)\n    end\n\n    it \"uses the :client_datadir setting if this is not the server\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(false)\n      expected = File.join(Puppet[:client_datadir], 'indirector_testing', 'testing.msgpack')\n      expect(subject.path('testing')).to eq(expected)\n    end\n\n    it \"overrides the default extension with a supplied value\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      expected = File.join(Puppet[:server_datadir], 'indirector_testing', 'testing.not-msgpack')\n      expect(subject.path('testing', '.not-msgpack')).to eq(expected)\n    end\n\n    ['../foo', '..\\\\foo', './../foo', '.\\\\..\\\\foo',\n     '/foo', '//foo', '\\\\foo', '\\\\\\\\goo',\n     \"test\\0/../bar\", \"test\\0\\\\..\\\\bar\",\n     \"..\\\\/bar\", \"/tmp/bar\", \"/tmp\\\\bar\", \"tmp\\\\bar\",\n     \" / bar\", \" /../ bar\", \" \\\\..\\\\ bar\",\n     \"c:\\\\foo\", \"c:/foo\", \"\\\\\\\\?\\\\UNC\\\\bar\", \"\\\\\\\\foo\\\\bar\",\n     \"\\\\\\\\?\\\\c:\\\\foo\", \"//?/UNC/bar\", \"//foo/bar\",\n     \"//?/c:/foo\",\n    ].each do |input|\n      it \"should resist directory traversal attacks (#{input.inspect})\" do\n        expect { subject.path(input) }.to raise_error ArgumentError, 'invalid key'\n      end\n    end\n  end\n\n  context \"handling requests\" do\n    before :each do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:server_datadir] = tmpdir('msgpackdir')\n      FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing'))\n    end\n\n    let :file do subject.path(request.key) end\n\n    def with_content(text)\n      FileUtils.mkdir_p(File.dirname(file))\n      File.open(file, 'w') {|f| f.write text }\n      yield if block_given?\n    end\n\n    it \"data saves and then loads again correctly\" do\n      subject.save(indirection.request(:save, 'example', model.new('banana')))\n      expect(subject.find(indirection.request(:find, 'example', nil)).value).to eq('banana')\n    end\n\n    context \"#find\" do\n      let :request do indirection.request(:find, 'example', nil) end\n\n      it \"returns nil if the file doesn't exist\" do\n        expect(subject.find(request)).to be_nil\n      end\n\n      it \"can load a UTF-8 file from disk\" do\n        rune_utf8 = \"\\u16A0\\u16C7\\u16BB\" # ᚠᛇᚻ\n\n        with_content(model.new(rune_utf8).to_msgpack) do\n          instance = subject.find(request)\n          expect(instance).to be_an_instance_of model\n          expect(instance.value).to eq(rune_utf8)\n        end\n      end\n\n      it \"raises a descriptive error when the file can't be read\" do\n        with_content(model.new('foo').to_msgpack) do\n          # I don't like this, but there isn't a credible alternative that\n          # also works on Windows, so a stub it is. At least the expectation\n          # will fail if the implementation changes. Sorry to the next dev.\n          expect(Puppet::FileSystem).to receive(:read).with(file, {:encoding => 'utf-8'}).and_raise(Errno::EPERM)\n          expect { subject.find(request) }.\n            to raise_error Puppet::Error, /Could not read MessagePack/\n        end\n      end\n\n      it \"raises a descriptive error when the file content is invalid\" do\n        with_content(\"this is totally invalid MessagePack\") do\n          expect { subject.find(request) }.\n            to raise_error Puppet::Error, /Could not parse MessagePack data/\n        end\n      end\n\n      it \"should return an instance of the indirected object when valid\" do\n        with_content(model.new(1).to_msgpack) do\n          instance = subject.find(request)\n          expect(instance).to be_an_instance_of model\n          expect(instance.value).to eq(1)\n        end\n      end\n    end\n\n    context \"#save\" do\n      let :instance do model.new(4) end\n      let :request  do indirection.request(:find, 'example', instance) end\n\n      it \"should save the instance of the request as MessagePack to disk\" do\n        subject.save(request)\n        content = File.read(file)\n        expect(MessagePack.unpack(content)['value']).to eq(4)\n      end\n\n      it \"should create the indirection directory if required\" do\n        target = File.join(Puppet[:server_datadir], 'indirector_testing')\n        Dir.rmdir(target)\n\n        subject.save(request)\n\n        expect(File).to be_directory(target)\n      end\n    end\n\n    context \"#destroy\" do\n      let :request do indirection.request(:find, 'example', nil) end\n\n      it \"removes an existing file\" do\n        with_content('hello') do\n          subject.destroy(request)\n        end\n        expect(Puppet::FileSystem.exist?(file)).to be_falsey\n      end\n\n      it \"silently succeeds when files don't exist\" do\n        Puppet::FileSystem.unlink(file) rescue nil\n        expect(subject.destroy(request)).to be_truthy\n      end\n\n      it \"raises an informative error for other failures\" do\n        allow(Puppet::FileSystem).to receive(:unlink).with(file).and_raise(Errno::EPERM, 'fake permission problem')\n        with_content('hello') do\n          expect { subject.destroy(request) }.to raise_error(Puppet::Error)\n        end\n      end\n    end\n  end\n\n  context \"#search\" do\n    before :each do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:server_datadir] = tmpdir('msgpackdir')\n      FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing'))\n    end\n\n    def request(glob)\n      indirection.request(:search, glob, nil)\n    end\n\n    def create_file(name, value = 12)\n      File.open(subject.path(name, ''), 'w') do |f|\n        f.write Puppet::IndirectorTesting.new(value).to_msgpack\n      end\n    end\n\n    it \"returns an empty array when nothing matches the key as a glob\" do\n      expect(subject.search(request('*'))).to eq([])\n    end\n\n    it \"returns an array with one item if one item matches\" do\n      create_file('foo.msgpack', 'foo')\n      create_file('bar.msgpack', 'bar')\n      expect(subject.search(request('f*')).map(&:value)).to eq(['foo'])\n    end\n\n    it \"returns an array of items when more than one item matches\" do\n      create_file('foo.msgpack', 'foo')\n      create_file('bar.msgpack', 'bar')\n      create_file('baz.msgpack', 'baz')\n      expect(subject.search(request('b*')).map(&:value)).to match_array(['bar', 'baz'])\n    end\n\n    it \"only items with the .msgpack extension\" do\n      create_file('foo.msgpack', 'foo-msgpack')\n      create_file('foo.msgpack~', 'foo-backup')\n      expect(subject.search(request('f*')).map(&:value)).to eq(['foo-msgpack'])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/exec_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/node/exec'\nrequire 'puppet/indirector/request'\n\ndescribe Puppet::Node::Exec do\n  let(:indirection) { mock 'indirection' }\n  let(:searcher) { Puppet::Node::Exec.new }\n\n  before do\n    Puppet.settings[:external_nodes] = File.expand_path(\"/echo\")\n  end\n\n  describe \"when constructing the command to run\" do\n    it \"should use the external_node script as the command\" do\n      Puppet[:external_nodes] = \"/bin/echo\"\n      expect(searcher.command).to eq(%w{/bin/echo})\n    end\n\n    it \"should throw an exception if no external node command is set\" do\n      Puppet[:external_nodes] = \"none\"\n      expect { searcher.find(double('request', :key => \"foo\")) }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"when handling the results of the command\" do\n    let(:testing_env) { Puppet::Node::Environment.create(:testing, []) }\n    let(:other_env) { Puppet::Node::Environment.create(:other, []) }\n    let(:request) { Puppet::Indirector::Request.new(:node, :find, name, environment: testing_env) }\n    let(:name) { 'yay' }\n    let(:facts) { Puppet::Node::Facts.new(name, {}) }\n\n    before do\n      allow(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)\n\n      @result = {}\n      # Use a local variable so the reference is usable in the execute definition.\n      result = @result\n      searcher.meta_def(:execute) do |command, arguments|\n        return YAML.dump(result)\n      end\n    end\n\n    around do |example|\n      envs = Puppet::Environments::Static.new(testing_env, other_env)\n\n      Puppet.override(:environments => envs) do\n        example.run\n      end\n    end\n\n    it \"should translate the YAML into a Node instance\" do\n      @result = {}\n      node = searcher.find(request)\n      expect(node.name).to eq(name)\n      expect(node.parameters).to include('environment')\n      expect(node.facts).to eq(facts)\n      expect(node.environment.name).to eq(:'*root*') # request env is ignored\n    end\n\n    it \"should set the resulting parameters as the node parameters\" do\n      @result[:parameters] = {\"a\" => \"b\", \"c\" => \"d\"}\n      node = searcher.find(request)\n      expect(node.parameters).to eq({\"a\" => \"b\", \"c\" => \"d\", \"environment\" => \"*root*\"})\n    end\n\n    it \"accepts symbolic parameter names\" do\n      @result[:parameters] = {:name => \"value\"}\n      node = searcher.find(request)\n      expect(node.parameters).to include({:name => \"value\"})\n    end\n\n    it \"raises when deserializing unacceptable objects\" do\n      @result[:parameters] = {'name' => Object.new }\n\n      expect {\n        searcher.find(request)\n      }.to raise_error(Puppet::Error,\n                       /Could not load external node results for yay: \\(<unknown>\\): Tried to load unspecified class: Object/)\n    end\n\n    it \"should set the resulting classes as the node classes\" do\n      @result[:classes] = %w{one two}\n      node = searcher.find(request)\n      expect(node.classes).to eq([ 'one', 'two' ])\n    end\n\n    it \"should merge facts from the request if supplied\" do\n      facts = Puppet::Node::Facts.new('test', 'foo' => 'bar')\n      request.options[:facts] = facts\n      node = searcher.find(request)\n      expect(node.facts).to eq(facts)\n    end\n\n    it \"should set the node's environment if one is provided\" do\n      @result[:environment] = \"testing\"\n      node = searcher.find(request)\n      expect(node.environment.name).to eq(:testing)\n    end\n\n    it \"should set the node's environment based on the request if not otherwise provided\" do\n      request.environment = \"other\"\n      node = searcher.find(request)\n      expect(node.environment.name).to eq(:other)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/json_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/node'\nrequire 'puppet/indirector/node/json'\n\ndescribe Puppet::Node::Json do\n  describe '#save' do\n    subject(:indirection) { described_class.indirection }\n\n    let(:env) { Puppet::Node::Environment.create(:testing, []) }\n    let(:node) { Puppet::Node.new('node_name', :environment => env) }\n    let(:file) { File.join(Puppet[:client_datadir], \"node\", \"node_name.json\") }\n\n    before do\n      indirection.terminus_class = :json\n    end\n\n    it 'saves the instance of the node as JSON to disk' do\n      indirection.save(node)\n      json = Puppet::FileSystem.read(file, :encoding => 'bom|utf-8')\n      content = Puppet::Util::Json.load(json)\n      expect(content[\"name\"]).to eq('node_name')\n    end\n\n    context 'when node cannot be saved' do\n      it 'raises Errno::EISDIR' do\n        FileUtils.mkdir_p(file)\n        expect {\n          indirection.save(node)\n         }.to raise_error(Errno::EISDIR, /node_name.json/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/memory_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/node/memory'\nrequire 'shared_behaviours/memory_terminus'\n\ndescribe Puppet::Node::Memory do\n  before do\n    @name = \"me\"\n    @searcher = Puppet::Node::Memory.new\n    @instance = double('instance', :name => @name)\n\n    @request = double('request', :key => @name, :instance => @instance)\n  end\n\n  it_should_behave_like \"A Memory Terminus\"\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/msgpack_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/node'\nrequire 'puppet/indirector/node/msgpack'\n\ndescribe Puppet::Node::Msgpack, :if => Puppet.features.msgpack? do\n  it \"should be a subclass of the Msgpack terminus\" do\n    expect(Puppet::Node::Msgpack.superclass).to equal(Puppet::Indirector::Msgpack)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Node::Msgpack.doc).not_to be_nil\n  end\n\n  it \"should be registered with the configuration store indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:node)\n    expect(Puppet::Node::Msgpack.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :msgpack\" do\n    expect(Puppet::Node::Msgpack.name).to eq(:msgpack)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/plain_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/node/plain'\n\ndescribe Puppet::Node::Plain do\n  let(:nodename) { \"mynode\" }\n  let(:indirection_fact_values) { {:afact => \"a value\"} }\n  let(:indirection_facts) { Puppet::Node::Facts.new(nodename, indirection_fact_values) }\n  let(:request_fact_values) { {:foo => \"bar\" } }\n  let(:request_facts) { Puppet::Node::Facts.new(nodename, request_fact_values)}\n  let(:environment) { Puppet::Node::Environment.create(:myenv, []) }\n  let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) }\n  let(:node_indirection) { Puppet::Node::Plain.new }\n\n  it \"should merge facts from the request if supplied\" do\n    expect(Puppet::Node::Facts.indirection).not_to receive(:find)\n    request.options[:facts] = request_facts\n    node = node_indirection.find(request)\n    expect(node.parameters).to include(request_fact_values)\n    expect(node.facts).to eq(request_facts)\n  end\n\n  it \"should find facts if none are supplied\" do\n    expect(Puppet::Node::Facts.indirection).to receive(:find).with(nodename, {:environment => environment}).and_return(indirection_facts)\n    request.options.delete(:facts)\n    node = node_indirection.find(request)\n    expect(node.parameters).to include(indirection_fact_values)\n    expect(node.facts).to eq(indirection_facts)\n  end\n\n  it \"should set the node environment from the request\" do\n    expect(node_indirection.find(request).environment).to eq(environment)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/node/rest'\n\ndescribe Puppet::Node::Rest do\n  let(:certname) { 'ziggy' }\n  let(:uri) { %r{/puppet/v3/node/ziggy} }\n  let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n  let(:node) { Puppet::Node.new(certname) }\n\n  before :each do\n    Puppet[:server] = 'compiler.example.com'\n    Puppet[:serverport] = 8140\n\n    described_class.indirection.terminus_class = :rest\n  end\n\n  def node_response(node)\n    { body: formatter.render(node), headers: {'Content-Type' => formatter.mime } }\n  end\n\n  it 'finds a node' do\n    stub_request(:get, uri).to_return(**node_response(node))\n\n    expect(described_class.indirection.find(certname)).to be_a(Puppet::Node)\n  end\n\n  it \"serializes the environment\" do\n    stub_request(:get, uri)\n      .with(query: hash_including('environment' => 'outerspace'))\n      .to_return(**node_response(node))\n\n    described_class.indirection.find(certname, environment: Puppet::Node::Environment.remote('outerspace'))\n  end\n\n  it 'preserves the node environment_name as a symbol' do\n    env = Puppet::Node::Environment.remote('outerspace')\n    node = Puppet::Node.new(certname, environment: env)\n\n    stub_request(:get, uri).to_return(**node_response(node))\n\n    expect(described_class.indirection.find(certname).environment_name).to eq(:outerspace)\n  end\n\n  it 'returns nil if the node does not exist' do\n    stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n    expect(described_class.indirection.find(certname)).to be_nil\n  end\n\n  it 'raises if fail_on_404 is specified' do\n    stub_request(:get, uri).to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n    expect{\n      described_class.indirection.find(certname, fail_on_404: true)\n    }.to raise_error(Puppet::Error, %r{Find /puppet/v3/node/ziggy resulted in 404 with the message: {}})\n  end\n\n  it 'raises Net::HTTPError on 500' do\n    stub_request(:get, uri).to_return(status: 500)\n\n    expect{\n      described_class.indirection.find(certname)\n    }.to raise_error(Net::HTTPError, %r{Error 500 on SERVER: })\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/store_configs_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/node'\nrequire 'puppet/indirector/memory'\nrequire 'puppet/indirector/node/store_configs'\n\nclass Puppet::Node::StoreConfigsTesting < Puppet::Indirector::Memory\nend\n\ndescribe Puppet::Node::StoreConfigs do\n  after :each do\n    Puppet::Node.indirection.reset_terminus_class\n    Puppet::Node.indirection.cache_class = nil\n  end\n\n  it_should_behave_like \"a StoreConfigs terminus\"\nend\n"
  },
  {
    "path": "spec/unit/indirector/node/yaml_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/node'\nrequire 'puppet/indirector/node/yaml'\n\ndescribe Puppet::Node::Yaml do\n  it \"should be a subclass of the Yaml terminus\" do\n    expect(Puppet::Node::Yaml.superclass).to equal(Puppet::Indirector::Yaml)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Node::Yaml.doc).not_to be_nil\n  end\n\n  it \"should be registered with the configuration store indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:node)\n    expect(Puppet::Node::Yaml.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :node\" do\n    expect(Puppet::Node::Yaml.name).to eq(:yaml)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/none_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/none'\n\ndescribe Puppet::Indirector::None do\n  before do\n    allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)\n    allow(Puppet::Indirector::Indirection).to receive(:instance).and_return(indirection)\n\n    module Testing; end\n    @none_class = class Testing::None < Puppet::Indirector::None\n      self\n    end\n\n    @data_binder = @none_class.new\n  end\n\n  let(:model)   { double('model') }\n  let(:request) { double('request', :key => \"port\") }\n  let(:indirection) do\n    double('indirection', :name => :none, :register_terminus_type => nil,\n      :model => model)\n  end\n\n  it \"should not be the default data_binding_terminus\" do\n    expect(Puppet.settings[:data_binding_terminus]).not_to eq('none')\n  end\n\n  describe \"the behavior of the find method\" do\n    it \"should just return nil\" do\n      expect(@data_binder.find(request)).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/plain_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/plain'\n\ndescribe Puppet::Indirector::Plain do\n  before do\n    allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)\n    @model = double('model')\n    @indirection = double('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model)\n    allow(Puppet::Indirector::Indirection).to receive(:instance).and_return(@indirection)\n\n    module Testing; end\n    @plain_class = class Testing::MyPlain < Puppet::Indirector::Plain\n      self\n    end\n\n    @searcher = @plain_class.new\n\n    @request = double('request', :key => \"yay\")\n  end\n\n  it \"should return return an instance of the indirected model\" do\n    object = double('object')\n    expect(@model).to receive(:new).with(@request.key).and_return(object)\n    expect(@searcher.find(@request)).to equal(object)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/report/json_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/transaction/report'\nrequire 'puppet/indirector/report/json'\n\ndescribe Puppet::Transaction::Report::Json do\n  include PuppetSpec::Files\n  describe '#save' do\n    subject(:indirection) { described_class.indirection }\n\n    let(:request) { described_class.new }\n    let(:certname) { 'ziggy' }\n    let(:report) do\n      report = Puppet::Transaction::Report.new\n      report.host = certname\n      report\n    end\n    let(:file) { request.path(:me) }\n\n    before do\n      Puppet[:lastrunreport] = File.join(Puppet[:statedir], \"last_run_report.json\")\n\n      indirection.terminus_class = :json\n    end\n\n    it 'saves the instance of the report as JSON to disk' do\n\n      indirection.save(report)\n      json = Puppet::FileSystem.read(Puppet[:lastrunreport], :encoding => 'bom|utf-8')\n      content = Puppet::Util::Json.load(json)\n      expect(content[\"host\"]).to eq(certname)\n    end\n\n    it 'allows mode overwrite' do\n      Puppet.settings.setting(:lastrunreport).mode = '0644'\n      indirection.save(report)\n\n      if Puppet::Util::Platform.windows?\n        mode = File.stat(file).mode\n      else\n        mode = Puppet::FileSystem.stat(file).mode\n      end\n\n      expect(mode & 07777).to eq(0644)\n    end\n\n    context 'when mode is invalid' do\n      before do\n        Puppet.settings.setting(:lastrunreport).mode = '9999'\n      end\n\n      after do\n        Puppet.settings.setting(:lastrunreport).mode = '0644'\n      end\n\n      it 'raises Puppet::DevError ' do\n        expect{\n          indirection.save(report)\n        }.to raise_error(Puppet::DevError, \"replace_file mode: 9999 is invalid\")\n      end\n    end\n\n    context 'when report cannot be saved' do\n      it 'raises Error' do\n        FileUtils.mkdir_p(file)\n        expect {\n          indirection.save(report)\n         }.to raise_error(Errno::EISDIR, /last_run_report.json/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/report/msgpack_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/transaction/report'\nrequire 'puppet/indirector/report/msgpack'\n\ndescribe Puppet::Transaction::Report::Msgpack, :if => Puppet.features.msgpack? do\n  it \"should be a subclass of the Msgpack terminus\" do\n    expect(Puppet::Transaction::Report::Msgpack.superclass).to equal(Puppet::Indirector::Msgpack)\n  end\n\n  it \"should have documentation\" do\n    expect(Puppet::Transaction::Report::Msgpack.doc).not_to be_nil\n  end\n\n  it \"should be registered with the report indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:report)\n    expect(Puppet::Transaction::Report::Msgpack.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :msgpack\" do\n    expect(Puppet::Transaction::Report::Msgpack.name).to eq(:msgpack)\n  end\n\n  it \"should unconditionally save/load from the --lastrunreport setting\" do\n    expect(subject.path(:me)).to eq(Puppet[:lastrunreport])\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/report/processor_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/report/processor'\n\ndescribe Puppet::Transaction::Report::Processor do\n  before do\n    allow(Puppet.settings).to receive(:use).and_return(true)\n  end\n\n  it \"should provide a method for saving reports\" do\n    expect(Puppet::Transaction::Report::Processor.new).to respond_to(:save)\n  end\n\n  it \"should provide a method for cleaning reports\" do\n    expect(Puppet::Transaction::Report::Processor.new).to respond_to(:destroy)\n  end\n\nend\n\ndescribe Puppet::Transaction::Report::Processor, \" when processing a report\" do\n  before do\n    allow(Puppet.settings).to receive(:use)\n    @reporter = Puppet::Transaction::Report::Processor.new\n    @request = double('request', :instance => double(\"report\", :host => 'hostname'), :key => 'node')\n  end\n\n  it \"should not save the report if reports are set to 'none'\" do\n    expect(Puppet::Reports).not_to receive(:report)\n    Puppet[:reports] = 'none'\n\n    request = Puppet::Indirector::Request.new(:indirection_name, :head, \"key\", nil)\n    report = Puppet::Transaction::Report.new\n    request.instance = report\n\n    @reporter.save(request)\n  end\n\n  it \"should save the report with each configured report type\" do\n    Puppet[:reports] = \"one,two\"\n    expect(@reporter.send(:reports)).to eq(%w{one two})\n\n    expect(Puppet::Reports).to receive(:report).with('one')\n    expect(Puppet::Reports).to receive(:report).with('two')\n\n    @reporter.save(@request)\n  end\n\n  it \"should destroy reports for each processor that responds to destroy\" do\n    Puppet[:reports] = \"http,store\"\n    http_report = double()\n    store_report = double()\n    expect(store_report).to receive(:destroy).with(@request.key)\n    expect(Puppet::Reports).to receive(:report).with('http').and_return(http_report)\n    expect(Puppet::Reports).to receive(:report).with('store').and_return(store_report)\n    @reporter.destroy(@request)\n  end\nend\n\ndescribe Puppet::Transaction::Report::Processor, \" when processing a report\" do\n  before do\n    Puppet[:reports] = \"one\"\n    allow(Puppet.settings).to receive(:use)\n    @reporter = Puppet::Transaction::Report::Processor.new\n\n    @report_type = double('one')\n    @dup_report = double('dupe report')\n    allow(@dup_report).to receive(:process)\n    @report = Puppet::Transaction::Report.new\n    expect(@report).to receive(:dup).and_return(@dup_report)\n\n    @request = double('request', :instance => @report)\n\n    expect(Puppet::Reports).to receive(:report).with(\"one\").and_return(@report_type)\n\n    expect(@dup_report).to receive(:extend).with(@report_type)\n  end\n\n  # LAK:NOTE This is stupid, because the code is so short it doesn't\n  # make sense to split it out, which means I just do the same test\n  # three times so the spec looks right.\n  it \"should process a duplicate of the report, not the original\" do\n    @reporter.save(@request)\n  end\n\n  it \"should extend the report with the report type's module\" do\n    @reporter.save(@request)\n  end\n\n  it \"should call the report type's :process method\" do\n    expect(@dup_report).to receive(:process)\n    @reporter.save(@request)\n  end\n\n  it \"should not raise exceptions\" do\n    Puppet[:trace] = false\n    expect(@dup_report).to receive(:process).and_raise(ArgumentError)\n    expect { @reporter.save(@request) }.not_to raise_error\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/report/rest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/report/rest'\n\ndescribe Puppet::Transaction::Report::Rest do\n  let(:certname) { 'ziggy' }\n  let(:uri) { %r{/puppet/v3/report/ziggy} }\n  let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n  let(:report) do\n    report = Puppet::Transaction::Report.new\n    report.host = certname\n    report\n  end\n\n  before(:each) do\n    described_class.indirection.terminus_class = :rest\n  end\n\n  def report_response\n    { body: formatter.render([\"store\", \"http\"]), headers: {'Content-Type' => formatter.mime } }\n  end\n\n  it \"saves a report \" do\n    stub_request(:put, uri)\n      .to_return(status: 200, **report_response)\n\n    described_class.indirection.save(report)\n  end\n\n  it \"serializes the environment\" do\n    stub_request(:put, uri)\n      .with(query: hash_including('environment' => 'outerspace'))\n      .to_return(**report_response)\n\n    described_class.indirection.save(report, nil, environment: Puppet::Node::Environment.remote('outerspace'))\n  end\n\n  it \"deserializes the response as an array of report processor names\" do\n    stub_request(:put, %r{/puppet/v3/report})\n      .to_return(status: 200, **report_response)\n\n    expect(described_class.indirection.save(report)).to eq([\"store\", \"http\"])\n  end\n\n  it \"returns nil if the node does not exist\" do\n    stub_request(:put, uri)\n      .to_return(status: 404, headers: { 'Content-Type' => 'application/json' }, body: \"{}\")\n\n    expect(described_class.indirection.save(report)).to be_nil\n  end\n\n  it \"parses charset from response content-type\" do\n    stub_request(:put, uri)\n      .to_return(status: 200, body: JSON.dump([\"store\"]), headers: { 'Content-Type' => 'application/json;charset=utf-8' })\n\n    expect(described_class.indirection.save(report)).to eq([\"store\"])\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/report/yaml_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/transaction/report'\nrequire 'puppet/indirector/report/yaml'\n\ndescribe Puppet::Transaction::Report::Yaml do\n  it \"should be registered with the report indirection\" do\n    indirection = Puppet::Indirector::Indirection.instance(:report)\n    expect(Puppet::Transaction::Report::Yaml.indirection).to equal(indirection)\n  end\n\n  it \"should have its name set to :yaml\" do\n    expect(Puppet::Transaction::Report::Yaml.name).to eq(:yaml)\n  end\n\n  it \"should unconditionally save/load from the --lastrunreport setting\" do\n    expect(subject.path(:me)).to eq(Puppet[:lastrunreport])\n  end\n\n  describe '#save' do\n    subject(:indirection) { described_class.indirection }\n\n    let(:request) { described_class.new }\n    let(:certname) { 'ziggy' }\n    let(:report) do\n      report = Puppet::Transaction::Report.new\n      report.host = certname\n      report\n    end\n    let(:file) { request.path(:me) }\n\n    before do\n      indirection.terminus_class = :yaml\n    end\n\n    it 'saves the instance of the report as YAML to disk' do\n      indirection.save(report)\n      content = Puppet::Util::Yaml.safe_load_file(\n        Puppet[:lastrunreport], [Puppet::Transaction::Report]\n      )\n      expect(content.host).to eq(certname)\n    end\n\n    it 'allows mode overwrite' do\n      Puppet.settings.setting(:lastrunreport).mode = '0644'\n      indirection.save(report)\n\n      if Puppet::Util::Platform.windows?\n        mode = File.stat(file).mode\n      else\n        mode = Puppet::FileSystem.stat(file).mode\n      end\n\n      expect(mode & 07777).to eq(0644)\n    end\n\n    context 'when mode is invalid' do\n      before do\n        Puppet.settings.setting(:lastrunreport).mode = '9999'\n      end\n\n      after do\n        Puppet.settings.setting(:lastrunreport).mode = '0644'\n      end\n\n      it 'raises Puppet::DevError ' do\n        expect{\n          indirection.save(report)\n        }.to raise_error(Puppet::DevError, \"replace_file mode: 9999 is invalid\")\n      end\n    end\n\n    context 'when repport is invalid' do\n      it 'logs error' do\n        expect(Puppet).to receive(:send_log).with(:err, /Could not save yaml ziggy: can't dump anonymous class/)\n\n        report.configuration_version = Class.new\n        indirection.save(report)\n      end\n    end\n\n    context 'when report cannot be saved' do\n      it 'raises Error' do\n        FileUtils.mkdir_p(file)\n        expect {\n          indirection.save(report)\n        }.to raise_error(Errno::EISDIR, /last_run_report.yaml/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/request_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/json'\nrequire 'puppet/indirector/request'\n\ndescribe Puppet::Indirector::Request do\n  include JSONMatchers\n\n  describe \"when initializing\" do\n    it \"should always convert the indirection name to a symbol\" do\n      expect(Puppet::Indirector::Request.new(\"ind\", :method, \"mykey\", nil).indirection_name).to eq(:ind)\n    end\n\n    it \"should use provided value as the key if it is a string\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, \"mykey\", nil).key).to eq(\"mykey\")\n    end\n\n    it \"should use provided value as the key if it is a symbol\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :mykey, nil).key).to eq(:mykey)\n    end\n\n    it \"should use the name of the provided instance as its key if an instance is provided as the key instead of a string\" do\n      instance = double('instance', :name => \"mykey\")\n      request = Puppet::Indirector::Request.new(:ind, :method, nil, instance)\n      expect(request.key).to eq(\"mykey\")\n      expect(request.instance).to equal(instance)\n    end\n\n    it \"should support options specified as a hash\" do\n      expect { Puppet::Indirector::Request.new(:ind, :method, :key, nil, :one => :two) }.to_not raise_error\n    end\n\n    it \"should support nil options\" do\n      expect { Puppet::Indirector::Request.new(:ind, :method, :key, nil, nil) }.to_not raise_error\n    end\n\n    it \"should support unspecified options\" do\n      expect { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.to_not raise_error\n    end\n\n    it \"should use an empty options hash if nil was provided\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, nil).options).to eq({})\n    end\n\n    it \"should default to a nil node\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil).node).to be_nil\n    end\n\n    it \"should set its node attribute if provided in the options\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :node => \"foo.com\").node).to eq(\"foo.com\")\n    end\n\n    it \"should default to a nil ip\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil).ip).to be_nil\n    end\n\n    it \"should set its ip attribute if provided in the options\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ip => \"192.168.0.1\").ip).to eq(\"192.168.0.1\")\n    end\n\n    it \"should default to being unauthenticated\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil)).not_to be_authenticated\n    end\n\n    it \"should set be marked authenticated if configured in the options\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :authenticated => \"eh\")).to be_authenticated\n    end\n\n    it \"should keep its options as a hash even if a node is specified\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :node => \"eh\").options).to be_instance_of(Hash)\n    end\n\n    it \"should keep its options as a hash even if another option is specified\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :foo => \"bar\").options).to be_instance_of(Hash)\n    end\n\n    it \"should treat options other than :ip, :node, and :authenticated as options rather than attributes\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :server => \"bar\").options[:server]).to eq(\"bar\")\n    end\n\n    it \"should normalize options to use symbols as keys\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, \"foo\" => \"bar\").options[:foo]).to eq(\"bar\")\n    end\n\n    describe \"and the request key is a URI\" do\n      let(:file) { File.expand_path(\"/my/file with spaces\") }\n      let(:an_environment) { Puppet::Node::Environment.create(:an_environment, []) }\n      let(:env_loaders) { Puppet::Environments::Static.new(an_environment) }\n\n      around(:each) do |example|\n        Puppet.override({ :environments => env_loaders }, \"Static environment loader for specs\") do\n          example.run\n        end\n      end\n\n      describe \"and the URI is a 'file' URI\" do\n        before do\n          @request = Puppet::Indirector::Request.new(:ind, :method, \"#{Puppet::Util.uri_unescape(Puppet::Util.path_to_uri(file).to_s)}\", nil)\n        end\n\n        it \"should set the request key to the unescaped full file path\" do\n          expect(@request.key).to eq(file)\n        end\n\n        it \"should not set the protocol\" do\n          expect(@request.protocol).to be_nil\n        end\n\n        it \"should not set the port\" do\n          expect(@request.port).to be_nil\n        end\n\n        it \"should not set the server\" do\n          expect(@request.server).to be_nil\n        end\n      end\n\n      it \"should set the protocol to the URI scheme\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"http://host/\", nil).protocol).to eq(\"http\")\n      end\n\n      it \"should set the server if a server is provided\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"http://host/\", nil).server).to eq(\"host\")\n      end\n\n      it \"should set the server and port if both are provided\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"http://host:543/\", nil).port).to eq(543)\n      end\n\n      it \"should default to the serverport if the URI scheme is 'puppet'\" do\n        Puppet[:serverport] = \"321\"\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"puppet://host/\", nil).port).to eq(321)\n      end\n\n      it \"should use the provided port if the URI scheme is not 'puppet'\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"http://host/\", nil).port).to eq(80)\n      end\n\n      it \"should set the request key to the unescaped path from the URI\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"http://host/stuff with spaces\", nil).key).to eq(\"stuff with spaces\")\n      end\n\n      it \"should set the request key to the unescaped path from the URI, in UTF-8 encoding\" do\n        path = \"\\u4e07\"\n        uri = \"http://host/#{path}\"\n        request = Puppet::Indirector::Request.new(:ind, :method, uri, nil)\n\n        expect(request.key).to eq(path)\n        expect(request.key.encoding).to eq(Encoding::UTF_8)\n      end\n\n      it \"should set the request key properly given a UTF-8 URI\" do\n        # different UTF-8 widths\n        # 1-byte A\n        # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n        # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n        # 4-byte <U+070E> - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n        mixed_utf8 = \"A\\u06FF\\u16A0\\u{2070E}\" # Aۿᚠ<U+070E>\n\n        key = \"a/path/stu ff/#{mixed_utf8}\"\n        req = Puppet::Indirector::Request.new(:ind, :method, \"http:///#{key}\", nil)\n        expect(req.key).to eq(key)\n        expect(req.key.encoding).to eq(Encoding::UTF_8)\n        expect(req.uri).to eq(\"http:///#{key}\")\n      end\n\n      it \"should set the :uri attribute to the full URI\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"http:///a/path/stu ff\", nil).uri).to eq('http:///a/path/stu ff')\n      end\n\n      it \"should not parse relative URI\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"foo/bar\", nil).uri).to be_nil\n      end\n\n      it \"should not parse opaque URI\" do\n        expect(Puppet::Indirector::Request.new(:ind, :method, \"mailto:joe\", nil).uri).to be_nil\n      end\n    end\n\n    it \"should allow indication that it should not read a cached instance\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ignore_cache => true)).to be_ignore_cache\n    end\n\n    it \"should default to not ignoring the cache\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil)).not_to be_ignore_cache\n    end\n\n    it \"should allow indication that it should not not read an instance from the terminus\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ignore_terminus => true)).to be_ignore_terminus\n    end\n\n    it \"should default to not ignoring the terminus\" do\n      expect(Puppet::Indirector::Request.new(:ind, :method, :key, nil)).not_to be_ignore_terminus\n    end\n  end\n\n  it \"should look use the Indirection class to return the appropriate indirection\" do\n    ind = double('indirection')\n    expect(Puppet::Indirector::Indirection).to receive(:instance).with(:myind).and_return(ind)\n    request = Puppet::Indirector::Request.new(:myind, :method, :key, nil)\n\n    expect(request.indirection).to equal(ind)\n  end\n\n  it \"should use its indirection to look up the appropriate model\" do\n    ind = double('indirection')\n    expect(Puppet::Indirector::Indirection).to receive(:instance).with(:myind).and_return(ind)\n    request = Puppet::Indirector::Request.new(:myind, :method, :key, nil)\n\n    expect(ind).to receive(:model).and_return(\"mymodel\")\n\n    expect(request.model).to eq(\"mymodel\")\n  end\n\n  it \"should fail intelligently when asked to find a model but the indirection cannot be found\" do\n    expect(Puppet::Indirector::Indirection).to receive(:instance).with(:myind).and_return(nil)\n    request = Puppet::Indirector::Request.new(:myind, :method, :key, nil)\n\n    expect { request.model }.to raise_error(ArgumentError)\n  end\n\n  it \"should have a method for determining if the request is plural or singular\" do\n    expect(Puppet::Indirector::Request.new(:myind, :method, :key, nil)).to respond_to(:plural?)\n  end\n\n  it \"should be considered plural if the method is 'search'\" do\n    expect(Puppet::Indirector::Request.new(:myind, :search, :key, nil)).to be_plural\n  end\n\n  it \"should not be considered plural if the method is not 'search'\" do\n    expect(Puppet::Indirector::Request.new(:myind, :find, :key, nil)).not_to be_plural\n  end\n\n  it \"should use its uri, if it has one, as its description\" do\n    Puppet.override(\n      { :environments => Puppet::Environments::Static.new(Puppet::Node::Environment.create(:baz, [])) },\n      \"Static loader for spec\"\n    ) do\n      expect(Puppet::Indirector::Request.new(:myind, :find, \"foo://bar/baz\", nil).description).to eq(\"foo://bar/baz\")\n    end\n  end\n\n  it \"should use its indirection name and key, if it has no uri, as its description\" do\n    expect(Puppet::Indirector::Request.new(:myind, :find, \"key\", nil).description).to eq(\"/myind/key\")\n  end\n\n  it \"should set its environment to an environment instance when a string is specified as its environment\" do\n    env = Puppet::Node::Environment.create(:foo, [])\n\n    Puppet.override(:environments => Puppet::Environments::Static.new(env)) do\n      expect(Puppet::Indirector::Request.new(:myind, :find, \"my key\", nil, :environment => \"foo\").environment).to eq(env)\n    end\n  end\n\n  it \"should use any passed in environment instances as its environment\" do\n    env = Puppet::Node::Environment.create(:foo, [])\n\n    expect(Puppet::Indirector::Request.new(:myind, :find, \"my key\", nil, :environment => env).environment).to equal(env)\n  end\n\n  it \"should use the current environment when none is provided\" do\n    Puppet[:environment] = \"foo\"\n\n    expect(Puppet::Indirector::Request.new(:myind, :find, \"my key\", nil).environment).to eq(Puppet.lookup(:current_environment))\n  end\n\n  it \"should support converting its options to a hash\" do\n    expect(Puppet::Indirector::Request.new(:myind, :find, \"my key\", nil )).to respond_to(:to_hash)\n  end\n\n  it \"should include all of its attributes when its options are converted to a hash\" do\n    expect(Puppet::Indirector::Request.new(:myind, :find, \"my key\", nil, :node => 'foo').to_hash[:node]).to eq('foo')\n  end\n\n  describe \"#remote?\" do\n    def request(options = {})\n      Puppet::Indirector::Request.new('node', 'find', 'localhost', nil, options)\n    end\n\n    it \"should not be unless node or ip is set\" do\n      expect(request).not_to be_remote\n    end\n\n    it \"should be remote if node is set\" do\n      expect(request(:node => 'example.com')).to be_remote\n    end\n\n    it \"should be remote if ip is set\" do\n      expect(request(:ip => '127.0.0.1')).to be_remote\n    end\n\n    it \"should be remote if node and ip are set\" do\n      expect(request(:node => 'example.com', :ip => '127.0.0.1')).to be_remote\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/resource/ral_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/resource/ral'\n\ndescribe Puppet::Resource::Ral do\n  let(:my_instance) { Puppet::Type.type(:user).new(:name => \"root\") }\n  let(:wrong_instance) { Puppet::Type.type(:user).new(:name => \"bob\")}\n\n  def stub_retrieve(*instances)\n    instances.each do |i|\n      allow(i).to receive(:retrieve).and_return(Puppet::Resource.new(i, nil))\n    end\n  end\n\n  before do\n    described_class.indirection.terminus_class = :ral\n\n    # make sure we don't try to retrieve current state\n    allow_any_instance_of(Puppet::Type.type(:user)).to receive(:retrieve).never\n    stub_retrieve(my_instance, wrong_instance)\n  end\n\n  it \"disallows remote requests\" do\n    expect(Puppet::Resource::Ral.new.allow_remote_requests?).to eq(false)\n  end\n\n  describe \"find\" do\n    it \"should find an existing instance\" do\n      allow(Puppet::Type.type(:user)).to receive(:instances).and_return([ wrong_instance, my_instance, wrong_instance ])\n\n      actual_resource = described_class.indirection.find('user/root')\n      expect(actual_resource.name).to eq('User/root')\n    end\n\n    it \"should produce Puppet::Error instead of ArgumentError\" do\n      expect{described_class.indirection.find('thiswill/causeanerror')}.to raise_error(Puppet::Error)\n    end\n\n    it \"if there is no instance, it should create one\" do\n      allow(Puppet::Type.type(:user)).to receive(:instances).and_return([wrong_instance])\n\n      expect(Puppet::Type.type(:user)).to receive(:new).with(hash_including(name: \"root\")).and_return(my_instance)\n      expect(described_class.indirection.find('user/root')).to be\n    end\n  end\n\n  describe \"search\" do\n    it \"should convert ral resources into regular resources\" do\n      allow(Puppet::Type.type(:user)).to receive(:instances).and_return([ my_instance ])\n\n      actual = described_class.indirection.search('user')\n      expect(actual).to contain_exactly(an_instance_of(Puppet::Resource))\n    end\n\n    it \"should filter results by name if there's a name in the key\" do\n      allow(Puppet::Type.type(:user)).to receive(:instances).and_return([ my_instance, wrong_instance ])\n\n      actual = described_class.indirection.search('user/root')\n      expect(actual).to contain_exactly(an_object_having_attributes(name: 'User/root'))\n    end\n\n    it \"should filter results by query parameters\" do\n      allow(Puppet::Type.type(:user)).to receive(:instances).and_return([ my_instance, wrong_instance ])\n\n      actual = described_class.indirection.search('user', name: 'bob')\n      expect(actual).to contain_exactly(an_object_having_attributes(name: 'User/bob'))\n    end\n\n    it \"should return sorted results\" do\n      a_instance = Puppet::Type.type(:user).new(:name => \"alice\")\n      b_instance = Puppet::Type.type(:user).new(:name => \"bob\")\n      stub_retrieve(a_instance, b_instance)\n      allow(Puppet::Type.type(:user)).to receive(:instances).and_return([ b_instance, a_instance ])\n\n      expect(described_class.indirection.search('user').map(&:title)).to eq(['alice', 'bob'])\n    end\n  end\n\n  describe \"save\" do\n    it \"returns a report covering the application of the given resource to the system\" do\n      resource = Puppet::Resource.new(:notify, \"the title\")\n\n      applied_resource, report = described_class.indirection.save(resource, nil, environment: Puppet::Node::Environment.remote(:testing))\n\n      expect(applied_resource.title).to eq(\"the title\")\n      expect(report.environment).to eq(\"testing\")\n      expect(report.resource_statuses[\"Notify[the title]\"].changed).to eq(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/resource/store_configs_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/resource'\nrequire 'puppet/indirector/memory'\nrequire 'puppet/indirector/resource/store_configs'\n\nclass Puppet::Resource::StoreConfigsTesting < Puppet::Indirector::Memory\nend\n\ndescribe Puppet::Resource::StoreConfigs do\n  it_should_behave_like \"a StoreConfigs terminus\"\n\n  before :each do\n    Puppet[:storeconfigs] = true\n    Puppet[:storeconfigs_backend] = \"store_configs_testing\"\n  end\n\n  it \"disallows remote requests\" do\n    expect(Puppet::Resource::StoreConfigs.new.allow_remote_requests?).to eq(false)\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/rest_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector/rest'\n\nclass Puppet::TestModel\n  extend Puppet::Indirector\n  indirects :test_model\nend\n\n# The subclass must not be all caps even though the superclass is\nclass Puppet::TestModel::Rest < Puppet::Indirector::REST\nend\n\nclass Puppet::FailingTestModel\n  extend Puppet::Indirector\n  indirects :failing_test_model\nend\n\n# The subclass must not be all caps even though the superclass is\nclass Puppet::FailingTestModel::Rest < Puppet::Indirector::REST\n  def find(request)\n    http = Puppet.runtime[:http]\n    response = http.get(URI('http://puppet.example.com:8140/puppet/v3/failing_test_model'))\n\n    if response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(response)\n    end\n  end\nend\n\ndescribe Puppet::Indirector::REST do\n  before :each do\n    Puppet::TestModel.indirection.terminus_class = :rest\n  end\n\n  it \"raises when find is called\" do\n    expect {\n      Puppet::TestModel.indirection.find('foo')\n    }.to raise_error(NotImplementedError)\n  end\n\n  it \"raises when head is called\" do\n    expect {\n      Puppet::TestModel.indirection.head('foo')\n    }.to raise_error(NotImplementedError)\n  end\n\n  it \"raises when search is called\" do\n    expect {\n      Puppet::TestModel.indirection.search('foo')\n    }.to raise_error(NotImplementedError)\n  end\n\n  it \"raises when save is called\" do\n    expect {\n      Puppet::TestModel.indirection.save(Puppet::TestModel.new, 'foo')\n    }.to raise_error(NotImplementedError)\n  end\n\n  it \"raises when destroy is called\" do\n    expect {\n      Puppet::TestModel.indirection.destroy('foo')\n    }.to raise_error(NotImplementedError)\n  end\n\n  context 'when parsing the response error' do\n    before :each do\n      Puppet::FailingTestModel.indirection.terminus_class = :rest\n    end\n\n    it 'returns nil if 404 is returned and fail_on_404 is omitted' do\n      stub_request(:get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model').to_return(status: 404)\n\n      expect(Puppet::FailingTestModel.indirection.find('foo')).to be_nil\n    end\n\n    it 'raises if 404 is returned and fail_on_404 is true' do\n      stub_request(\n        :get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model',\n      ).to_return(status: 404,\n                  headers: { 'Content-Type' => 'text/plain' },\n                  body: 'plaintext')\n\n      expect {\n        Puppet::FailingTestModel.indirection.find('foo', fail_on_404: true)\n      }.to raise_error(Puppet::Error, 'Find /puppet/v3/failing_test_model resulted in 404 with the message: plaintext')\n    end\n\n    it 'returns the HTTP reason if the response body is empty' do\n      stub_request(:get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model').to_return(status: [500, 'Internal Server Error'])\n\n      expect {\n        Puppet::FailingTestModel.indirection.find('foo')\n      }.to raise_error(Net::HTTPError, 'Error 500 on SERVER: Internal Server Error')\n    end\n\n    it 'parses the response body as text' do\n      stub_request(\n        :get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model'\n      ).to_return(status: [500, 'Internal Server Error'],\n                  headers: { 'Content-Type' => 'text/plain' },\n                  body: 'plaintext')\n\n      expect {\n        Puppet::FailingTestModel.indirection.find('foo')\n      }.to raise_error(Net::HTTPError, 'Error 500 on SERVER: plaintext')\n    end\n\n    it 'parses the response body as json and returns the \"message\"' do\n      stub_request(\n        :get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model'\n      ).to_return(status: [500, 'Internal Server Error'],\n                  headers: { 'Content-Type' => 'application/json' },\n                  body: JSON.dump({'status' => false, 'message' => 'json error'}))\n\n      expect {\n        Puppet::FailingTestModel.indirection.find('foo')\n      }.to raise_error(Net::HTTPError, 'Error 500 on SERVER: json error')\n    end\n\n    it 'parses the response body as pson and returns the \"message\"', if: Puppet.features.pson? do\n      stub_request(\n        :get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model'\n      ).to_return(status: [500, 'Internal Server Error'],\n                  headers: { 'Content-Type' => 'application/pson' },\n                  body: PSON.dump({'status' => false, 'message' => 'pson error'}))\n\n      expect {\n        Puppet::FailingTestModel.indirection.find('foo')\n      }.to raise_error(Net::HTTPError, 'Error 500 on SERVER: pson error')\n    end\n\n    it 'returns the response body if no content-type given' do\n      stub_request(\n        :get, 'http://puppet.example.com:8140/puppet/v3/failing_test_model'\n      ).to_return(status: [500, 'Internal Server Error'],\n                  body: 'unknown text')\n\n      expect {\n        Puppet::FailingTestModel.indirection.find('foo')\n      }.to raise_error(Net::HTTPError, 'Error 500 on SERVER: unknown text')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/terminus_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/defaults'\nrequire 'puppet/indirector'\nrequire 'puppet/indirector/memory'\n\ndescribe Puppet::Indirector::Terminus do\n  before :all do\n    class Puppet::AbstractConcept\n      extend Puppet::Indirector\n      indirects :abstract_concept\n      attr_accessor :name\n      def initialize(name = \"name\")\n        @name = name\n      end\n    end\n\n    class Puppet::AbstractConcept::Freedom < Puppet::Indirector::Code\n    end\n  end\n\n  after :all do\n    # Remove the class, unlinking it from the rest of the system.\n    Puppet.send(:remove_const, :AbstractConcept) if Puppet.const_defined?(:AbstractConcept)\n  end\n\n  let :terminus_class do Puppet::AbstractConcept::Freedom end\n  let :terminus       do terminus_class.new end\n  let :indirection    do Puppet::AbstractConcept.indirection end\n  let :model          do Puppet::AbstractConcept end\n\n  it \"should provide a method for setting terminus class documentation\" do\n    expect(terminus_class).to respond_to(:desc)\n  end\n\n  it \"should support a class-level name attribute\" do\n    expect(terminus_class).to respond_to(:name)\n  end\n\n  it \"should support a class-level indirection attribute\" do\n    expect(terminus_class).to respond_to(:indirection)\n  end\n\n  it \"should support a class-level terminus-type attribute\" do\n    expect(terminus_class).to respond_to(:terminus_type)\n  end\n\n  it \"should support a class-level model attribute\" do\n    expect(terminus_class).to respond_to(:model)\n  end\n\n  it \"should accept indirection instances as its indirection\" do\n    # The test is that this shouldn't raise, and should preserve the object\n    # instance exactly, hence \"equal\", not just \"==\".\n    terminus_class.indirection = indirection\n    expect(terminus_class.indirection).to equal indirection\n  end\n\n  it \"should look up indirection instances when only a name has been provided\" do\n    terminus_class.indirection = :abstract_concept\n    expect(terminus_class.indirection).to equal indirection\n  end\n\n  it \"should fail when provided a name that does not resolve to an indirection\" do\n    expect {\n      terminus_class.indirection = :exploding_whales\n    }.to raise_error(ArgumentError, /Could not find indirection instance/)\n\n    # We should still have the default indirection.\n    expect(terminus_class.indirection).to equal indirection\n  end\n\n  describe \"when a terminus instance\" do\n    it \"should return the class's name as its name\" do\n      expect(terminus.name).to eq(:freedom)\n    end\n\n    it \"should return the class's indirection as its indirection\" do\n      expect(terminus.indirection).to equal indirection\n    end\n\n    it \"should set the instances's type to the abstract terminus type's name\" do\n      expect(terminus.terminus_type).to eq(:code)\n    end\n\n    it \"should set the instances's model to the indirection's model\" do\n      expect(terminus.model).to equal indirection.model\n    end\n  end\n\n  describe \"when managing terminus classes\" do\n    it \"should provide a method for registering terminus classes\" do\n      expect(Puppet::Indirector::Terminus).to respond_to(:register_terminus_class)\n    end\n\n    it \"should provide a method for returning terminus classes by name and type\" do\n      terminus = double('terminus_type', :name => :abstract, :indirection_name => :whatever)\n      Puppet::Indirector::Terminus.register_terminus_class(terminus)\n      expect(Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract)).to equal(terminus)\n    end\n\n    it \"should set up autoloading for any terminus class types requested\" do\n      expect(Puppet::Indirector::Terminus).to receive(:instance_load).with(:test2, \"puppet/indirector/test2\")\n      Puppet::Indirector::Terminus.terminus_class(:test2, :whatever)\n    end\n\n    it \"should load terminus classes that are not found\" do\n      # Set up instance loading; it would normally happen automatically\n      Puppet::Indirector::Terminus.instance_load :test1, \"puppet/indirector/test1\"\n\n      expect(Puppet::Indirector::Terminus.instance_loader(:test1)).to receive(:load).with(:yay, anything)\n      Puppet::Indirector::Terminus.terminus_class(:test1, :yay)\n    end\n\n    it \"should fail when no indirection can be found\" do\n      expect(Puppet::Indirector::Indirection).to receive(:instance).with(:abstract_concept).and_return(nil)\n      expect {\n        class Puppet::AbstractConcept::Physics < Puppet::Indirector::Code\n        end\n      }.to raise_error(ArgumentError, /Could not find indirection instance/)\n    end\n\n    it \"should register the terminus class with the terminus base class\" do\n      expect(Puppet::Indirector::Terminus).to receive(:register_terminus_class) do |type|\n        expect(type.indirection_name).to eq(:abstract_concept)\n        expect(type.name).to eq(:intellect)\n      end\n\n      begin\n        class Puppet::AbstractConcept::Intellect < Puppet::Indirector::Code\n        end\n      ensure\n        Puppet::AbstractConcept.send(:remove_const, :Intellect) rescue nil\n      end\n    end\n  end\n\n  describe \"when parsing class constants for indirection and terminus names\" do\n    before :each do\n      allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)\n    end\n\n    let :subclass do\n      subclass = double('subclass')\n      allow(subclass).to receive(:to_s).and_return(\"TestInd::OneTwo\")\n      allow(subclass).to receive(:mark_as_abstract_terminus)\n      subclass\n    end\n\n    it \"should fail when anonymous classes are used\" do\n      expect {\n        Puppet::Indirector::Terminus.inherited(Class.new)\n      }.to raise_error(Puppet::DevError, /Terminus subclasses must have associated constants/)\n    end\n\n    it \"should use the last term in the constant for the terminus class name\" do\n      expect(subclass).to receive(:name=).with(:one_two)\n      allow(subclass).to receive(:indirection=)\n      Puppet::Indirector::Terminus.inherited(subclass)\n    end\n\n    it \"should convert the terminus name to a downcased symbol\" do\n      expect(subclass).to receive(:name=).with(:one_two)\n      allow(subclass).to receive(:indirection=)\n      Puppet::Indirector::Terminus.inherited(subclass)\n    end\n\n    it \"should use the second to last term in the constant for the indirection name\" do\n      expect(subclass).to receive(:indirection=).with(:test_ind)\n      allow(subclass).to receive(:name=)\n      allow(subclass).to receive(:terminus_type=)\n      Puppet::Indirector::Memory.inherited(subclass)\n    end\n\n    it \"should convert the indirection name to a downcased symbol\" do\n      expect(subclass).to receive(:indirection=).with(:test_ind)\n      allow(subclass).to receive(:name=)\n      allow(subclass).to receive(:terminus_type=)\n      Puppet::Indirector::Memory.inherited(subclass)\n    end\n\n    it \"should convert camel case to lower case with underscores as word separators\" do\n      expect(subclass).to receive(:name=).with(:one_two)\n      allow(subclass).to receive(:indirection=)\n\n      Puppet::Indirector::Terminus.inherited(subclass)\n    end\n  end\n\n  describe \"when creating terminus class types\" do\n    before :each do\n      allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)\n\n      class Puppet::Indirector::Terminus::TestTerminusType < Puppet::Indirector::Terminus\n      end\n    end\n\n    after :each do\n      Puppet::Indirector::Terminus.send(:remove_const, :TestTerminusType)\n    end\n\n    let :subclass do\n      Puppet::Indirector::Terminus::TestTerminusType\n    end\n\n    it \"should set the name of the abstract subclass to be its class constant\" do\n      expect(subclass.name).to eq(:test_terminus_type)\n    end\n\n    it \"should mark abstract terminus types as such\" do\n      expect(subclass).to be_abstract_terminus\n    end\n\n    it \"should not allow instances of abstract subclasses to be created\" do\n      expect { subclass.new }.to raise_error(Puppet::DevError)\n    end\n  end\n\n  describe \"when listing terminus classes\" do\n    it \"should list the terminus files available to load\" do\n      allow_any_instance_of(Puppet::Util::Autoload).to receive(:files_to_load).and_return([\"/foo/bar/baz\", \"/max/runs/marathon\"])\n      expect(Puppet::Indirector::Terminus.terminus_classes('my_stuff')).to eq([:baz, :marathon])\n    end\n  end\n\n  describe \"when validating a request\" do\n    let :request do\n      Puppet::Indirector::Request.new(indirection.name, :find, \"the_key\", instance)\n    end\n\n    describe \"`instance.name` does not match the key in the request\" do\n      let(:instance) { model.new(\"wrong_key\") }\n\n      it \"raises an error \" do\n        expect {\n          terminus.validate(request)\n        }.to raise_error(\n          Puppet::Indirector::ValidationError,\n          /Instance name .* does not match requested key/\n        )\n      end\n    end\n\n    describe \"`instance` is not an instance of the model class\" do\n      let(:instance) { double(\"instance\") }\n\n      it \"raises an error\" do\n        expect {\n          terminus.validate(request)\n        }.to raise_error(\n          Puppet::Indirector::ValidationError,\n          /Invalid instance type/\n        )\n      end\n    end\n\n    describe \"the instance key and class match the request key and model class\" do\n      let(:instance) { model.new(\"the_key\") }\n\n      it \"passes\" do\n        terminus.validate(request)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector/yaml_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/indirector/yaml'\n\ndescribe Puppet::Indirector::Yaml do\n  include PuppetSpec::Files\n\n  before(:all) do\n    class Puppet::YamlTestModel\n      extend Puppet::Indirector\n      indirects :yaml_test_model\n      attr_reader :name\n\n      def initialize(name)\n        @name = name\n      end\n    end\n\n    class Puppet::YamlTestModel::Yaml < Puppet::Indirector::Yaml; end\n\n    Puppet::YamlTestModel.indirection.terminus_class = :yaml\n  end\n\n  after(:all) do\n    Puppet::YamlTestModel.indirection.delete\n    Puppet.send(:remove_const, :YamlTestModel)\n  end\n\n  let(:model) { Puppet::YamlTestModel }\n  let(:subject) { model.new(:me) }\n  let(:indirection) { model.indirection }\n  let(:terminus) { indirection.terminus(:yaml) }\n\n  let(:dir) { tmpdir(\"yaml_indirector\") }\n  let(:indirection_dir) { File.join(dir, indirection.name.to_s) }\n  let(:serverdir) { File.expand_path(\"/server/yaml/dir\") }\n  let(:clientdir) { File.expand_path(\"/client/yaml/dir\") }\n\n  before :each do\n    Puppet[:clientyamldir] = dir\n    allow(Puppet.run_mode).to receive(:server?).and_return(false)\n  end\n\n  describe \"when choosing file location\" do\n    it \"should use the server_datadir if the run_mode is server\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:yamldir] = serverdir\n      expect(terminus.path(:me)).to match(/^#{serverdir}/)\n    end\n\n    it \"should use the client yamldir if the run_mode is not server\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(false)\n      Puppet[:clientyamldir] = clientdir\n      expect(terminus.path(:me)).to match(/^#{clientdir}/)\n    end\n\n    it \"should use the extension if one is specified\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:yamldir] = serverdir\n      expect(terminus.path(:me,'.farfignewton')).to match(%r{\\.farfignewton$})\n    end\n\n    it \"should assume an extension of .yaml if none is specified\" do\n      allow(Puppet.run_mode).to receive(:server?).and_return(true)\n      Puppet[:yamldir] = serverdir\n      expect(terminus.path(:me)).to match(%r{\\.yaml$})\n    end\n\n    it \"should store all files in a single file root set in the Puppet defaults\" do\n      expect(terminus.path(:me)).to match(%r{^#{dir}})\n    end\n\n    it \"should use the terminus name for choosing the subdirectory\" do\n      expect(terminus.path(:me)).to match(%r{^#{dir}/yaml_test_model})\n    end\n\n    it \"should use the object's name to determine the file name\" do\n      expect(terminus.path(:me)).to match(%r{me.yaml$})\n    end\n\n    ['../foo', '..\\\\foo', './../foo', '.\\\\..\\\\foo',\n     '/foo', '//foo', '\\\\foo', '\\\\\\\\goo',\n     \"test\\0/../bar\", \"test\\0\\\\..\\\\bar\",\n     \"..\\\\/bar\", \"/tmp/bar\", \"/tmp\\\\bar\", \"tmp\\\\bar\",\n     \" / bar\", \" /../ bar\", \" \\\\..\\\\ bar\",\n     \"c:\\\\foo\", \"c:/foo\", \"\\\\\\\\?\\\\UNC\\\\bar\", \"\\\\\\\\foo\\\\bar\",\n     \"\\\\\\\\?\\\\c:\\\\foo\", \"//?/UNC/bar\", \"//foo/bar\",\n     \"//?/c:/foo\",\n    ].each do |input|\n      it \"should resist directory traversal attacks (#{input.inspect})\" do\n        expect { terminus.path(input) }.to raise_error(ArgumentError)\n      end\n    end\n  end\n\n  describe \"when storing objects as YAML\" do\n    it \"should only store objects that respond to :name\" do\n      request = indirection.request(:save, \"testing\", Object.new)\n      expect { terminus.save(request) }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"when retrieving YAML\" do\n    it \"should read YAML in from disk and convert it to Ruby objects\" do\n      terminus.save(indirection.request(:save, \"testing\", subject))\n      yaml = terminus.find(indirection.request(:find, \"testing\", nil))\n      expect(yaml.name).to eq(:me)\n    end\n\n    it \"should fail coherently when the stored YAML is invalid\" do\n      terminus.save(indirection.request(:save, \"testing\", subject))\n      # overwrite file\n      File.open(terminus.path('testing'), \"w\") do |file|\n        file.puts \"{ invalid\"\n      end\n\n      expect {\n        terminus.find(indirection.request(:find, \"testing\", nil))\n      }.to raise_error(Puppet::Error, /Could not parse YAML data/)\n    end\n  end\n\n  describe \"when searching\" do\n    before :each do\n      Puppet[:clientyamldir] = dir\n    end\n\n    def dir_containing_instances(instances)\n      Dir.mkdir(indirection_dir)\n      instances.each do |hash|\n        File.open(File.join(indirection_dir, \"#{hash['name']}.yaml\"), 'wb') do |f|\n          f.write(YAML.dump(hash))\n        end\n      end\n    end\n\n    it \"should return an array of fact instances with one instance for each file when globbing *\" do\n      one = { 'name' => 'one', 'values' => { 'foo' => 'bar' } }\n      two = { 'name' => 'two', 'values' => { 'foo' => 'baz' } }\n      dir_containing_instances([one, two])\n\n      request = indirection.request(:search, \"*\", nil)\n      expect(terminus.search(request)).to contain_exactly(one, two)\n    end\n\n    it \"should return an array containing a single instance of fact when globbing 'one*'\" do\n      one = { 'name' => 'one', 'values' => { 'foo' => 'bar' } }\n      two = { 'name' => 'two', 'values' => { 'foo' => 'baz' } }\n      dir_containing_instances([one, two])\n\n      request = indirection.request(:search, \"one*\", nil)\n      expect(terminus.search(request)).to eq([one])\n    end\n\n    it \"should return an empty array when the glob doesn't match anything\" do\n      one = { 'name' => 'one', 'values' => { 'foo' => 'bar' } }\n      dir_containing_instances([one])\n\n      request = indirection.request(:search, \"f*ilglobcanfail*\", nil)\n      expect(terminus.search(request)).to eq([])\n    end\n\n    describe \"when destroying\" do\n      let(:path) { File.join(indirection_dir, \"one.yaml\") }\n\n      before :each do\n        Puppet[:clientyamldir] = dir\n\n        one = { 'name' => 'one', 'values' => { 'foo' => 'bar' } }\n        dir_containing_instances([one])\n      end\n\n      it \"should unlink the right yaml file if it exists\" do\n        request = indirection.request(:destroy, \"one\", nil)\n        terminus.destroy(request)\n\n        expect(Puppet::FileSystem).to_not exist(path)\n      end\n\n      it \"should not unlink the yaml file if it does not exists\" do\n        request = indirection.request(:destroy, \"doesntexist\", nil)\n        terminus.destroy(request)\n\n        expect(Puppet::FileSystem).to exist(path)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/indirector_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/defaults'\nrequire 'puppet/indirector'\n\ndescribe Puppet::Indirector, \"when configuring routes\" do\n  before :each do\n    Puppet::Node.indirection.reset_terminus_class\n    Puppet::Node.indirection.cache_class = nil\n  end\n\n  after :each do\n    Puppet::Node.indirection.reset_terminus_class\n    Puppet::Node.indirection.cache_class = nil\n  end\n\n  it \"should configure routes as requested\" do\n    routes = {\n      \"node\" => {\n        \"terminus\" => \"exec\",\n        \"cache\"    => \"plain\"\n      }\n    }\n\n    Puppet::Indirector.configure_routes(routes)\n\n    expect(Puppet::Node.indirection.terminus_class).to eq(\"exec\")\n    expect(Puppet::Node.indirection.cache_class).to    eq(\"plain\")\n  end\n\n  it \"should fail when given an invalid indirection\" do\n    routes = {\n      \"fake_indirection\" => {\n        \"terminus\" => \"exec\",\n        \"cache\"    => \"plain\"\n      }\n    }\n\n    expect { Puppet::Indirector.configure_routes(routes) }.to raise_error(/fake_indirection does not exist/)\n  end\n\n  it \"should fail when given an invalid terminus\" do\n    routes = {\n      \"node\" => {\n        \"terminus\" => \"fake_terminus\",\n        \"cache\"    => \"plain\"\n      }\n    }\n\n    expect { Puppet::Indirector.configure_routes(routes) }.to raise_error(/Could not find terminus fake_terminus/)\n  end\n\n  it \"should fail when given an invalid cache\" do\n    routes = {\n      \"node\" => {\n        \"terminus\" => \"exec\",\n        \"cache\"    => \"fake_cache\"\n      }\n    }\n\n    expect { Puppet::Indirector.configure_routes(routes) }.to raise_error(/Could not find terminus fake_cache/)\n  end\nend\n\ndescribe Puppet::Indirector, \" when available to a model\" do\n  before do\n    @thingie = Class.new do\n      extend Puppet::Indirector\n    end\n  end\n\n  it \"should provide a way for the model to register an indirection under a name\" do\n    expect(@thingie).to respond_to(:indirects)\n  end\nend\n\ndescribe Puppet::Indirector, \"when registering an indirection\" do\n  before do\n    @thingie = Class.new do\n      extend Puppet::Indirector\n\n      # override Class#name, since we're not naming this ephemeral class\n      def self.name\n        'Thingie'\n      end\n\n      attr_reader :name\n      def initialize(name)\n        @name = name\n      end\n    end\n  end\n\n  it \"should require a name when registering a model\" do\n    expect {@thingie.send(:indirects) }.to raise_error(ArgumentError)\n  end\n\n  it \"should create an indirection instance to manage each indirecting model\" do\n    @indirection = @thingie.indirects(:test)\n    expect(@indirection).to be_instance_of(Puppet::Indirector::Indirection)\n  end\n\n  it \"should not allow a model to register under multiple names\" do\n    # Keep track of the indirection instance so we can delete it on cleanup\n    @indirection = @thingie.indirects :first\n    expect { @thingie.indirects :second }.to raise_error(ArgumentError)\n  end\n\n  it \"should make the indirection available via an accessor\" do\n    @indirection = @thingie.indirects :first\n    expect(@thingie.indirection).to equal(@indirection)\n  end\n\n  it \"should pass any provided options to the indirection during initialization\" do\n    expect(Puppet::Indirector::Indirection).to receive(:new).with(@thingie, :first, {:doc => 'some docs', :indirected_class => 'Thingie'})\n    @indirection = @thingie.indirects :first, :doc => 'some docs'\n  end\n\n  it \"should extend the class to handle serialization\" do\n    @indirection = @thingie.indirects :first\n    expect(@thingie).to respond_to(:convert_from)\n  end\n\n  after do\n    @indirection.delete if @indirection\n  end\nend\n\ndescribe Puppet::Indirector, \"when redirecting a model\" do\n  before do\n    @thingie = Class.new do\n      extend Puppet::Indirector\n      attr_reader :name\n      def initialize(name)\n        @name = name\n      end\n    end\n    @indirection = @thingie.send(:indirects, :test)\n  end\n\n  it \"should include the Envelope module in the model\" do\n    expect(@thingie.ancestors).to be_include(Puppet::Indirector::Envelope)\n  end\n\n  after do\n    @indirection.delete\n  end\nend\n"
  },
  {
    "path": "spec/unit/info_service_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/modules'\n\nrequire 'puppet/pops'\nrequire 'puppet/info_service'\nrequire 'puppet/pops/evaluator/literal_evaluator'\n\ndescribe \"Puppet::InfoService\" do\n  include PuppetSpec::Files\n\n  context 'task information service' do\n    let(:mod_name) { 'test1' }\n    let(:metadata) {\n      { \"private\" => true,\n        \"description\" => \"a task that does a thing\" } }\n    let(:task_name) { \"#{mod_name}::thingtask\" }\n    let(:modpath) { tmpdir('modpath') }\n    let(:env_name) { 'testing' }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [modpath]) }\n    let(:env_loader) { Puppet::Environments::Static.new(env) }\n\n    context 'tasks_per_environment method' do\n      it \"returns task data for the tasks in an environment\" do\n        Puppet.override(:environments => env_loader) do\n          PuppetSpec::Modules.create(mod_name, modpath, {:environment => env,\n                                                         :tasks => [['thingtask',\n                                                                     {:name => 'thingtask.json',\n                                                                      :content => metadata.to_json}]]})\n          expect(Puppet::InfoService.tasks_per_environment(env_name)).to eq([{:name => task_name,\n                                                                              :module => {:name => mod_name},\n                                                                              :metadata => metadata}  ])\n        end\n      end\n\n      it \"returns task data for valid tasks in an environment even if invalid tasks exist\" do\n        Puppet.override(:environments => env_loader) do\n          @mod = PuppetSpec::Modules.create(mod_name, modpath, {:environment => env,\n                                                                :tasks => [['atask',\n                                                                            {:name => 'atask.json',\n                                                                             :content => metadata.to_json}],\n                                                                           ['btask',\n                                                                            {:name => 'btask.json',\n                                                                             :content => metadata.to_json}],\n                                                                           ['ctask',\n                                                                            {:name => 'ctask.json',\n                                                                             :content => metadata.to_json}]]})\n          File.write(\"#{modpath}/#{mod_name}/tasks/atask.json\", \"NOT JSON\")\n\n          expect(Puppet).to receive(:send_log).with(:err, /unexpected token at 'NOT JSON'/)\n\n          @tasks = Puppet::InfoService.tasks_per_environment(env_name)\n          expect(@tasks.map{|t| t[:name]}).to contain_exactly('test1::btask', 'test1::ctask')\n        end\n      end\n\n      it \"should throw EnvironmentNotFound if given a nonexistent environment\" do\n        expect{ Puppet::InfoService.tasks_per_environment('utopia') }.to raise_error(Puppet::Environments::EnvironmentNotFound)\n      end\n    end\n\n    context 'task_data method' do\n      context 'For a valid simple module' do\n        before do\n          Puppet.override(:environments => env_loader) do\n            @mod = PuppetSpec::Modules.create(mod_name, modpath,\n                                              {:environment => env,\n                                               :tasks => [['thingtask',\n                                                           {:name => 'thingtask.json',\n                                                            :content => '{}'}]]})\n            @result = Puppet::InfoService.task_data(env_name, mod_name, task_name)\n          end\n        end\n\n        it 'returns the right set of keys' do\n          expect(@result.keys.sort).to eq([:files, :metadata])\n        end\n\n        it 'specifies the metadata_file correctly' do\n          expect(@result[:metadata]).to eq({})\n        end\n\n        it 'specifies the other files correctly' do\n          task = @mod.tasks[0]\n          expect(@result[:files]).to eq(task.files)\n        end\n      end\n\n      context 'For a module with multiple implemenations and files' do\n        let(:other_mod_name) { \"shell_helpers\" }\n        let(:metadata) {\n          { \"implementations\" => [\n            {\"name\" => \"thingtask.rb\", \"requirements\" => [\"puppet_agent\"],\n             \"files\" => [\"#{mod_name}/lib/puppet/providers/\"]},\n            {\"name\" => \"thingtask.sh\", \"requirements\" => [\"shell\"] } ],\n            \"files\" => [\n             \"#{mod_name}/files/my_data.json\",\n             \"#{other_mod_name}/files/scripts/helper.sh\",\n             \"#{mod_name}/files/data/files/data.rb\"] } }\n        let(:expected_files) { [ {'name' => 'thingtask.rb',\n                                  'path' => \"#{modpath}/#{mod_name}/tasks/thingtask.rb\"},\n        { 'name' => 'thingtask.sh',\n          'path' => \"#{modpath}/#{mod_name}/tasks/thingtask.sh\"},\n        { 'name' => \"#{mod_name}/lib/puppet/providers/prov.rb\",\n          'path' => \"#{modpath}/#{mod_name}/lib/puppet/providers/prov.rb\"},\n        { 'name' => \"#{mod_name}/files/data/files/data.rb\",\n          'path' => \"#{modpath}/#{mod_name}/files/data/files/data.rb\"},\n        { 'name' => \"#{mod_name}/files/my_data.json\",\n          'path' => \"#{modpath}/#{mod_name}/files/my_data.json\"},\n        { 'name' => \"#{other_mod_name}/files/scripts/helper.sh\",\n          'path' => \"#{modpath}/#{other_mod_name}/files/scripts/helper.sh\" }\n        ].sort_by {|f| f['name']} }\n\n        before do\n          Puppet.override(:environments => env_loader) do\n            @mod = PuppetSpec::Modules.create(mod_name, modpath,\n                                              {:environment => env,\n                                               :tasks => [['thingtask.rb',\n                                                           'thingtask.sh',\n                                                           {:name => 'thingtask.json',\n                                                            :content => metadata.to_json}]],\n                                               :files => {\n                                                 \"files/data/files/data.rb\" => \"a file of data\",\n                                                 \"files/my_data.json\" => \"{}\",\n                                                 \"lib/puppet/providers/prov.rb\" => \"provider_content\"} })\n            @other_mod = PuppetSpec::Modules.create(other_mod_name, modpath, { :environment => env,\n                                                                               :files =>{\n              \"files/scripts/helper.sh\" => \"helper content\" } } )\n            @result = Puppet::InfoService.task_data(env_name, mod_name, task_name)\n          end\n        end\n\n        it 'returns the right set of keys' do\n          expect(@result.keys.sort).to eq([:files, :metadata])\n        end\n\n        it 'specifies the metadata_file correctly' do\n          expect(@result[:metadata]).to eq(metadata)\n        end\n\n        it 'specifies the other file names correctly' do\n          expect(@result[:files].sort_by{|f| f['name']}).to eq(expected_files)\n        end\n      end\n\n      context 'For a task with files that do not exist' do\n        let(:metadata) {\n          { \"files\" => [\n            \"#{mod_name}/files/random_data\",\n            \"shell_helpers/files/scripts/helper.sh\"] } }\n\n        before do\n          Puppet.override(:environments => env_loader) do\n            @mod = PuppetSpec::Modules.create(mod_name, modpath,\n                                              {:environment => env,\n                                               :tasks => [['thingtask.rb',\n                                                           {:name => 'thingtask.json',\n                                                            :content => metadata.to_json}]]})\n            @result = Puppet::InfoService.task_data(env_name, mod_name, task_name)\n          end\n        end\n\n        it 'errors when the file is not found' do\n          expect(@result[:error][:kind]).to eq('puppet.tasks/invalid-file')\n        end\n      end\n\n      context 'For a task with bad metadata' do\n        let(:metadata) {\n          { \"implementations\" => [\n            {\"name\" => \"thingtask.rb\", \"requirements\" => [\"puppet_agent\"] },\n            {\"name\" => \"thingtask.sh\", \"requirements\" => [\"shell\"] } ] } }\n\n        before do\n          Puppet.override(:environments => env_loader) do\n            @mod = PuppetSpec::Modules.create(mod_name, modpath,\n                                              {:environment => env,\n                                               :tasks => [['thingtask.sh',\n                                                           {:name => 'thingtask.json',\n                                                            :content => metadata.to_json}]]})\n            @result = Puppet::InfoService.task_data(env_name, mod_name, task_name)\n          end\n        end\n\n        it 'returns the right set of keys' do\n          expect(@result.keys.sort).to eq([:error, :files, :metadata])\n        end\n\n        it 'returns the expected error' do\n          expect(@result[:error][:kind]).to eq('puppet.tasks/missing-implementation')\n        end\n      end\n\n      context 'For a task with required directories with no trailing slash' do\n        let(:metadata) { { \"files\" => [ \"#{mod_name}/files\" ] } }\n\n        before do\n          Puppet.override(:environments => env_loader) do\n            @mod = PuppetSpec::Modules.create(mod_name, modpath,\n                                              {:environment => env,\n                                               :tasks => [['thingtask.sh',\n                                                           {:name => 'thingtask.json',\n                                                            :content => metadata.to_json}]],\n                                               :files => {\n                                                 \"files/helper.rb\" => \"help\"}})\n            @result = Puppet::InfoService.task_data(env_name, mod_name, task_name)\n          end\n        end\n\n        it 'returns the right set of keys' do\n          expect(@result.keys.sort).to eq([:error, :files, :metadata])\n        end\n\n        it 'returns the expected error' do\n          expect(@result[:error][:kind]).to eq('puppet.tasks/invalid-metadata')\n        end\n      end\n\n      it \"should raise EnvironmentNotFound if given a nonexistent environment\" do\n        expect{ Puppet::InfoService.task_data('utopia', mod_name, task_name) }.to raise_error(Puppet::Environments::EnvironmentNotFound)\n      end\n\n      it \"should raise MissingModule if the module does not exist\" do\n        Puppet.override(:environments => env_loader) do\n          expect { Puppet::InfoService.task_data(env_name, 'notamodule', 'notamodule::thingtask') }\n            .to raise_error(Puppet::Module::MissingModule)\n        end\n      end\n\n      it \"should raise TaskNotFound if the task does not exist\" do\n        Puppet.override(:environments => env_loader) do\n          PuppetSpec::Modules.create(mod_name, modpath)\n          expect { Puppet::InfoService.task_data(env_name, mod_name, 'testing1::notatask') }\n            .to raise_error(Puppet::Module::Task::TaskNotFound)\n        end\n      end\n    end\n  end\n\n  context 'plan information service' do\n    let(:mod_name) { 'test1' }\n    let(:plan_name) { \"#{mod_name}::thingplan\" }\n    let(:modpath) { tmpdir('modpath') }\n    let(:env_name) { 'testing' }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [modpath]) }\n    let(:env_loader) { Puppet::Environments::Static.new(env) }\n\n    context 'plans_per_environment method' do\n      it \"returns plan data for the plans in an environment\" do\n        Puppet.override(:environments => env_loader) do\n          PuppetSpec::Modules.create(mod_name, modpath, {:environment => env, :plans => ['thingplan.pp']})\n          expect(Puppet::InfoService.plans_per_environment(env_name)).to eq([{:name => plan_name, :module => {:name => mod_name}}])\n        end\n      end\n\n      it \"should throw EnvironmentNotFound if given a nonexistent environment\" do\n        expect{ Puppet::InfoService.plans_per_environment('utopia') }.to raise_error(Puppet::Environments::EnvironmentNotFound)\n      end\n    end\n\n    context 'plan_data method' do\n      context 'For a valid simple module' do\n        before do\n          Puppet.override(:environments => env_loader) do\n            @mod = PuppetSpec::Modules.create(mod_name, modpath,\n                                              {:environment => env,\n                                               :plans => ['thingplan.pp']})\n            @result = Puppet::InfoService.plan_data(env_name, mod_name, plan_name)\n          end\n        end\n\n        it 'returns the right set of keys' do\n          expect(@result.keys.sort).to eq([:files, :metadata])\n        end\n\n        it 'specifies the metadata_file correctly' do\n          expect(@result[:metadata]).to eq({})\n        end\n\n        it 'specifies the other files correctly' do\n          plan = @mod.plans[0]\n          expect(@result[:files]).to eq(plan.files)\n        end\n      end\n    end\n  end\n\n  context 'classes_per_environment service' do\n    let(:code_dir) do\n      dir_containing('manifests', {\n        'foo.pp' => <<-CODE,\n           class foo($foo_a, Integer $foo_b, String $foo_c = 'c default value') { }\n           class foo2($foo2_a, Integer $foo2_b, String $foo2_c = 'c default value') { }\n        CODE\n        'bar.pp' => <<-CODE,\n           class bar($bar_a, Integer $bar_b, String $bar_c = 'c default value') { }\n           class bar2($bar2_a, Integer $bar2_b, String $bar2_c = 'c default value') { }\n        CODE\n        'intp.pp' => <<-CODE,\n           class intp(String $intp_a = \"default with interpolated $::os_family\") { }\n        CODE\n        'fee.pp' => <<-CODE,\n           class fee(Integer $fee_a = 1+1) { }\n        CODE\n        'fum.pp' => <<-CODE,\n           class fum($fum_a) { }\n        CODE\n        'nothing.pp' => <<-CODE,\n           # not much to see here, move along\n        CODE\n        'borked.pp' => <<-CODE,\n           class Borked($Herp+$Derp) {}\n        CODE\n        'json_unsafe.pp' => <<-CODE,\n             class json_unsafe($arg1 = /.*/, $arg2 = default, $arg3 = {1 => 1}) {}\n        CODE\n        'non_literal.pp' => <<-CODE,\n            class oops(Integer[1-3] $bad_int) { }\n        CODE\n        'non_literal_2.pp' => <<-CODE,\n           class oops_2(Optional[[String]] $double_brackets) { }\n          CODE\n       })\n    end\n\n    it \"errors if not given a hash\" do\n      expect{ Puppet::InfoService.classes_per_environment(\"you wassup?\")}.to raise_error(ArgumentError, 'Given argument must be a Hash')\n    end\n\n    it \"returns empty hash if given nothing\" do\n      expect(Puppet::InfoService.classes_per_environment({})).to eq({})\n    end\n\n    it \"produces classes and parameters from a given file\" do\n      files = ['foo.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({'production' => files })\n      expect(result).to eq({\n        \"production\"=>{\n           \"#{code_dir}/foo.pp\"=> {:classes => [\n             {:name=>\"foo\",\n               :params=>[\n                 {:name=>\"foo_a\"},\n                 {:name=>\"foo_b\", :type=>\"Integer\"},\n                 {:name=>\"foo_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                   :default_source=>\"'c default value'\"}\n               ]},\n             {:name=>\"foo2\",\n               :params=>[\n                 {:name=>\"foo2_a\"},\n                 {:name=>\"foo2_b\", :type=>\"Integer\"},\n                 {:name=>\"foo2_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                   :default_source=>\"'c default value'\"}\n               ]\n             }\n           ]}} # end production env\n        })\n    end\n\n    it \"produces classes and parameters from multiple files in same environment\" do\n      files = ['foo.pp', 'bar.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({'production' => files })\n      expect(result).to eq({\n        \"production\"=>{\n           \"#{code_dir}/foo.pp\"=>{:classes => [\n             {:name=>\"foo\",\n               :params=>[\n                 {:name=>\"foo_a\"},\n                 {:name=>\"foo_b\", :type=>\"Integer\"},\n                 {:name=>\"foo_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                   :default_source=>\"'c default value'\"}\n               ]},\n             {:name=>\"foo2\",\n               :params=>[\n                 {:name=>\"foo2_a\"},\n                 {:name=>\"foo2_b\", :type=>\"Integer\"},\n                 {:name=>\"foo2_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                   :default_source=>\"'c default value'\"}\n               ]\n             }\n           ]},\n          \"#{code_dir}/bar.pp\"=> {:classes =>[\n            {:name=>\"bar\",\n              :params=>[\n                {:name=>\"bar_a\"},\n                {:name=>\"bar_b\", :type=>\"Integer\"},\n                {:name=>\"bar_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                  :default_source=>\"'c default value'\"}\n              ]},\n            {:name=>\"bar2\",\n              :params=>[\n                {:name=>\"bar2_a\"},\n                {:name=>\"bar2_b\", :type=>\"Integer\"},\n                {:name=>\"bar2_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                :default_source=>\"'c default value'\"}\n              ]\n            }\n          ]},\n\n          } # end production env\n        }\n      )\n    end\n\n    it \"produces classes and parameters from multiple files in multiple environments\" do\n      files_production = ['foo.pp', 'bar.pp'].map {|f| File.join(code_dir, f) }\n      files_test = ['fee.pp', 'fum.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({\n        'production' => files_production,\n        'test'       => files_test\n      })\n\n      expect(result).to eq({\n        \"production\"=>{\n           \"#{code_dir}/foo.pp\"=>{:classes => [\n             {:name=>\"foo\",\n               :params=>[\n                 {:name=>\"foo_a\"},\n                 {:name=>\"foo_b\", :type=>\"Integer\"},\n                 {:name=>\"foo_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                   :default_source=>\"'c default value'\"}\n               ]},\n             {:name=>\"foo2\",\n               :params=>[\n                 {:name=>\"foo2_a\"},\n                 {:name=>\"foo2_b\", :type=>\"Integer\"},\n                 {:name=>\"foo2_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                   :default_source=>\"'c default value'\"}\n               ]\n             }\n           ]},\n          \"#{code_dir}/bar.pp\"=>{:classes => [\n            {:name=>\"bar\",\n              :params=>[\n                {:name=>\"bar_a\"},\n                {:name=>\"bar_b\", :type=>\"Integer\"},\n                {:name=>\"bar_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                  :default_source=>\"'c default value'\"}\n              ]},\n            {:name=>\"bar2\",\n              :params=>[\n                {:name=>\"bar2_a\"},\n                {:name=>\"bar2_b\", :type=>\"Integer\"},\n                {:name=>\"bar2_c\", :type=>\"String\", :default_literal=>\"c default value\",\n                  :default_source=>\"'c default value'\"}\n                ]\n              }\n          ]},\n          }, # end production env\n        \"test\"=>{\n           \"#{code_dir}/fee.pp\"=>{:classes => [\n             {:name=>\"fee\",\n               :params=>[\n                 {:name=>\"fee_a\", :type=>\"Integer\", :default_source=>\"1+1\"}\n               ]},\n           ]},\n          \"#{code_dir}/fum.pp\"=>{:classes => [\n            {:name=>\"fum\",\n              :params=>[\n                {:name=>\"fum_a\"}\n              ]},\n          ]},\n         } # end test env\n        }\n      )\n    end\n\n    it \"avoids parsing file more than once when environments have same feature flag set\" do\n      # in this version of puppet, all environments are equal in this respect\n      result = Puppet::Pops::Parser::EvaluatingParser.new.parse_file(\"#{code_dir}/fum.pp\")\n      expect_any_instance_of(Puppet::Pops::Parser::EvaluatingParser).to receive(:parse_file).with(\"#{code_dir}/fum.pp\").once.and_return(result)\n      files_production = ['fum.pp'].map {|f| File.join(code_dir, f) }\n      files_test       = files_production\n\n      result = Puppet::InfoService.classes_per_environment({\n        'production' => files_production,\n        'test'       => files_test\n        })\n       expect(result).to eq({\n         \"production\"=>{ \"#{code_dir}/fum.pp\"=>{:classes => [ {:name=>\"fum\", :params=>[ {:name=>\"fum_a\"}]}]}},\n         \"test\"      =>{ \"#{code_dir}/fum.pp\"=>{:classes => [ {:name=>\"fum\", :params=>[ {:name=>\"fum_a\"}]}]}}\n       }\n      )\n    end\n\n    it \"produces expression string if a default value is not literal\" do\n      files = ['fee.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({'production' => files })\n      expect(result).to eq({\n        \"production\"=>{\n           \"#{code_dir}/fee.pp\"=>{:classes => [\n             {:name=>\"fee\",\n               :params=>[\n                 {:name=>\"fee_a\", :type=>\"Integer\", :default_source=>\"1+1\"}\n               ]},\n           ]}} # end production env\n        })\n     end\n\n     it \"produces source string for literals that are not pure json\" do\n       files = ['json_unsafe.pp'].map {|f| File.join(code_dir, f) }\n       result = Puppet::InfoService.classes_per_environment({'production' => files })\n       expect(result).to eq({\n         \"production\"=>{\n            \"#{code_dir}/json_unsafe.pp\" => {:classes => [\n              {:name=>\"json_unsafe\",\n                :params => [\n                  {:name => \"arg1\",\n                    :default_source => \"/.*/\" },\n                  {:name => \"arg2\",\n                    :default_source => \"default\" },\n                  {:name => \"arg3\",\n                    :default_source => \"{1 => 1}\" }\n                ]}\n            ]}} # end production env\n         })\n     end\n\n     it \"errors with a descriptive message if non-literal class parameter is given\" do\n       files = ['non_literal.pp', 'non_literal_2.pp'].map {|f| File.join(code_dir, f) }\n       result = Puppet::InfoService.classes_per_environment({'production' => files })\n       expect(@logs).to have_matching_log_with_source(/The parameter '\\$bad_int' must be a literal type, not a Puppet::Pops::Model::AccessExpression/, \"#{code_dir}/non_literal.pp\", 1, 37)\n       expect(@logs).to have_matching_log_with_source(/The parameter '\\$double_brackets' must be a literal type, not a Puppet::Pops::Model::AccessExpression/, \"#{code_dir}/non_literal_2.pp\", 1, 44)\n\n       expect(result).to eq({\n        \"production\"=>{\n           \"#{code_dir}/non_literal.pp\" =>\n           {:error=> \"The parameter '\\$bad_int' is invalid: The expression <1-3> is not a valid type specification.\"},\n           \"#{code_dir}/non_literal_2.pp\" =>\n           {:error=> \"The parameter '\\$double_brackets' is invalid: The expression <Optional[[String]]> is not a valid type specification.\"}\n          } # end production env\n        })\n     end\n\n    it \"produces no type entry if type is not given\" do\n      files = ['fum.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({'production' => files })\n      expect(result).to eq({\n        \"production\"=>{\n           \"#{code_dir}/fum.pp\"=>{:classes => [\n             {:name=>\"fum\",\n               :params=>[\n                 {:name=>\"fum_a\" }\n               ]},\n           ]}} # end production env\n        })\n    end\n\n    it 'does not evaluate default expressions' do\n      files = ['intp.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({'production' => files })\n      expect(result).to eq({\n        'production' =>{\n          \"#{code_dir}/intp.pp\"=>{:classes => [\n            {:name=> 'intp',\n              :params=>[\n                {:name=> 'intp_a',\n                  :type=> 'String',\n                  :default_source=>'\"default with interpolated $::os_family\"'}\n              ]},\n          ]}} # end production env\n      })\n    end\n\n    it \"produces error entry if file is broken\" do\n      files = ['borked.pp'].map {|f| File.join(code_dir, f) }\n       result = Puppet::InfoService.classes_per_environment({'production' => files })\n       expect(result).to eq({\n         \"production\"=>{\n            \"#{code_dir}/borked.pp\"=>\n              {:error=>\"Syntax error at '+' (file: #{code_dir}/borked.pp, line: 1, column: 30)\",\n              },\n            } # end production env\n         })\n    end\n\n    it \"produces empty {} if parsed result has no classes\" do\n      files = ['nothing.pp'].map {|f| File.join(code_dir, f) }\n       result = Puppet::InfoService.classes_per_environment({'production' => files })\n       expect(result).to eq({\n         \"production\"=>{\n           \"#{code_dir}/nothing.pp\"=> {:classes => [] }\n           },\n         })\n    end\n\n    it \"produces error when given a file that does not exist\" do\n      files = ['the_tooth_fairy_does_not_exist.pp'].map {|f| File.join(code_dir, f) }\n      result = Puppet::InfoService.classes_per_environment({'production' => files })\n      expect(result).to eq({\n        \"production\"=>{\n          \"#{code_dir}/the_tooth_fairy_does_not_exist.pp\" => {:error  => \"The file #{code_dir}/the_tooth_fairy_does_not_exist.pp does not exist\"}\n             },\n        })\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/action_builder_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/interface'\nrequire 'puppet/network/format_handler'\n\ndescribe Puppet::Interface::ActionBuilder do\n  let :face do Puppet::Interface.new(:puppet_interface_actionbuilder, '0.0.1') end\n\n  it \"should build an action\" do\n    action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n      when_invoked do |options| true end\n    end\n    expect(action).to be_a(Puppet::Interface::Action)\n    expect(action.name).to eq(:foo)\n  end\n\n  it \"should define a method on the face which invokes the action\" do\n    face = Puppet::Interface.new(:action_builder_test_interface, '0.0.1') do\n      action(:foo) { when_invoked { |options| \"invoked the method\" } }\n    end\n\n    expect(face.foo).to eq(\"invoked the method\")\n  end\n\n  it \"should require a block\" do\n    expect {\n      Puppet::Interface::ActionBuilder.build(nil, :foo)\n    }.to raise_error(\"Action :foo must specify a block\")\n  end\n\n  it \"should require an invocation block\" do\n    expect {\n      Puppet::Interface::ActionBuilder.build(face, :foo) {}\n    }.to raise_error(/actions need to know what to do when_invoked; please add the block/)\n  end\n\n  describe \"when handling options\" do\n    it \"should have a #option DSL function\" do\n      method = nil\n      Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        method = self.method(:option)\n      end\n      expect(method).to be_an_instance_of Method\n    end\n\n    it \"should define an option without a block\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        option \"--bar\"\n      end\n      expect(action).to be_option :bar\n    end\n\n    it \"should accept an empty block\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        option \"--bar\" do\n          # This space left deliberately blank.\n        end\n      end\n      expect(action).to be_option :bar\n    end\n  end\n\n  context \"inline documentation\" do\n    it \"should set the summary\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        summary \"this is some text\"\n      end\n      expect(action.summary).to eq(\"this is some text\")\n    end\n  end\n\n  context \"action defaulting\" do\n    it \"should set the default to true\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        default\n      end\n      expect(action.default).to be_truthy\n    end\n\n    it \"should not be default by, er, default. *cough*\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n      end\n      expect(action.default).to be_falsey\n    end\n  end\n\n  context \"#when_rendering\" do\n    it \"should fail if no rendering format is given\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          when_rendering do true end\n        end\n      }.to raise_error ArgumentError, /must give a rendering format to when_rendering/\n    end\n\n    it \"should fail if no block is given\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          when_rendering :json\n        end\n      }.to raise_error ArgumentError, /must give a block to when_rendering/\n    end\n\n    it \"should fail if the block takes no arguments\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          when_rendering :json do true end\n        end\n      }.to raise_error ArgumentError,\n        /the puppet_interface_actionbuilder face foo action takes .* not/\n    end\n\n    it \"should fail if the when_rendering block takes a different number of arguments than when_invoked\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          when_rendering :json do |a, b, c| true end\n        end\n      }.to raise_error ArgumentError,\n        /the puppet_interface_actionbuilder face foo action takes .* not 3/\n    end\n\n    it \"should fail if the block takes a variable number of arguments\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          when_rendering :json do |*args| true end\n        end\n      }.to raise_error ArgumentError,\n        /the puppet_interface_actionbuilder face foo action takes .* not/\n    end\n\n    it \"should stash a rendering block\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        when_rendering :json do |a| true end\n      end\n      expect(action.when_rendering(:json)).to be_an_instance_of Method\n    end\n\n    it \"should fail if you try to set the same rendering twice\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          when_rendering :json do |a| true end\n          when_rendering :json do |a| true end\n        end\n      }.to raise_error ArgumentError, /You can't define a rendering method for json twice/\n    end\n\n    it \"should work if you set two different renderings\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        when_rendering :json do |a| true end\n        when_rendering :yaml do |a| true end\n      end\n      expect(action.when_rendering(:json)).to be_an_instance_of Method\n      expect(action.when_rendering(:yaml)).to be_an_instance_of Method\n    end\n\n    it \"should be bound to the face when called\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n        when_rendering :json do |a| self end\n      end\n      expect(action.when_rendering(:json).call(true)).to eq(face)\n    end\n  end\n\n  context \"#render_as\" do\n    it \"should default to nil (eg: based on context)\" do\n      action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n        when_invoked do |options| true end\n      end\n      expect(action.render_as).to be_nil\n    end\n\n    it \"should fail if not rendering format is given\" do\n      expect {\n        Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          render_as\n        end\n      }.to raise_error ArgumentError, /must give a rendering format to render_as/\n    end\n\n    Puppet::Network::FormatHandler.formats.each do |name|\n      it \"should accept #{name.inspect} format\" do\n        action = Puppet::Interface::ActionBuilder.build(face, :foo) do\n          when_invoked do |options| true end\n          render_as name\n        end\n        expect(action.render_as).to eq(name)\n      end\n    end\n\n    [:if_you_define_this_format_you_frighten_me, \"json\", 12].each do |input|\n      it \"should fail if given #{input.inspect}\" do\n        expect {\n          Puppet::Interface::ActionBuilder.build(face, :foo) do\n            when_invoked do |options| true end\n            render_as input\n          end\n        }.to raise_error ArgumentError, /#{input.inspect} is not a valid rendering format/\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/action_manager_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/interface'\n\nclass ActionManagerTester\n  include Puppet::Interface::ActionManager\nend\n\ndescribe Puppet::Interface::ActionManager do\n  subject { ActionManagerTester.new }\n\n  describe \"when included in a class\" do\n    it \"should be able to define an action\" do\n      subject.action(:foo) do\n        when_invoked { |options| \"something \"}\n      end\n    end\n\n    it \"should be able to list defined actions\" do\n      subject.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      subject.action(:bar) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(subject.actions).to match_array([:foo, :bar])\n    end\n\n    it \"should be able to indicate when an action is defined\" do\n      subject.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(subject).to be_action(:foo)\n    end\n\n    it \"should correctly treat action names specified as strings\" do\n      subject.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(subject).to be_action(\"foo\")\n    end\n  end\n\n  describe \"when used to extend a class\" do\n    subject { Class.new.extend(Puppet::Interface::ActionManager) }\n\n    it \"should be able to define an action\" do\n      subject.action(:foo) do\n        when_invoked { |options| \"something \"}\n      end\n    end\n\n    it \"should be able to list defined actions\" do\n      subject.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      subject.action(:bar) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(subject.actions).to include(:bar)\n      expect(subject.actions).to include(:foo)\n    end\n\n    it \"should be able to indicate when an action is defined\" do\n      subject.action(:foo) { when_invoked do |options| true end }\n      expect(subject).to be_action(:foo)\n    end\n  end\n\n  describe \"when used both at the class and instance level\" do\n    before do\n      @klass = Class.new do\n        include Puppet::Interface::ActionManager\n        extend Puppet::Interface::ActionManager\n        def __invoke_decorations(*args) true end\n        def options() [] end\n      end\n      @instance = @klass.new\n    end\n\n    it \"should be able to define an action at the class level\" do\n      @klass.action(:foo) do\n        when_invoked { |options| \"something \"}\n      end\n    end\n\n    it \"should create an instance method when an action is defined at the class level\" do\n      @klass.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      expect(@instance.foo).to eq(\"something\")\n    end\n\n    it \"should be able to define an action at the instance level\" do\n      @instance.action(:foo) do\n        when_invoked { |options| \"something \"}\n      end\n    end\n\n    it \"should create an instance method when an action is defined at the instance level\" do\n      @instance.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      expect(@instance.foo).to eq(\"something\")\n    end\n\n    it \"should be able to list actions defined at the class level\" do\n      @klass.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      @klass.action(:bar) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(@klass.actions).to include(:bar)\n      expect(@klass.actions).to include(:foo)\n    end\n\n    it \"should be able to list actions defined at the instance level\" do\n      @instance.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      @instance.action(:bar) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(@instance.actions).to include(:bar)\n      expect(@instance.actions).to include(:foo)\n    end\n\n    it \"should be able to list actions defined at both instance and class level\" do\n      @klass.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      @instance.action(:bar) do\n        when_invoked { |options| \"something\" }\n      end\n\n      expect(@instance.actions).to include(:bar)\n      expect(@instance.actions).to include(:foo)\n    end\n\n    it \"should be able to indicate when an action is defined at the class level\" do\n      @klass.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      expect(@instance).to be_action(:foo)\n    end\n\n    it \"should be able to indicate when an action is defined at the instance level\" do\n      @klass.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      expect(@instance).to be_action(:foo)\n    end\n\n    context \"with actions defined in superclass\" do\n      before :each do\n        @subclass = Class.new(@klass)\n        @instance = @subclass.new\n\n        @klass.action(:parent) do\n          when_invoked { |options| \"a\" }\n        end\n        @subclass.action(:sub) do\n          when_invoked { |options| \"a\" }\n        end\n        @instance.action(:instance) do\n          when_invoked { |options| \"a\" }\n        end\n      end\n\n      it \"should list actions defined in superclasses\" do\n        expect(@instance).to be_action(:parent)\n        expect(@instance).to be_action(:sub)\n        expect(@instance).to be_action(:instance)\n      end\n\n      it \"should list inherited actions\" do\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n      end\n\n      it \"should not duplicate instance actions after fetching them (#7699)\" do\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n        @instance.get_action(:instance)\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n      end\n\n      it \"should not duplicate subclass actions after fetching them (#7699)\" do\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n        @instance.get_action(:sub)\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n      end\n\n      it \"should not duplicate superclass actions after fetching them (#7699)\" do\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n        @instance.get_action(:parent)\n        expect(@instance.actions).to match_array([:instance, :parent, :sub])\n      end\n    end\n\n    it \"should create an instance method when an action is defined in a superclass\" do\n      @subclass = Class.new(@klass)\n      @instance = @subclass.new\n\n      @klass.action(:foo) do\n        when_invoked { |options| \"something\" }\n      end\n      expect(@instance.foo).to eq(\"something\")\n    end\n  end\n\n  describe \"#action\" do\n    it 'should add an action' do\n      subject.action(:foo) { when_invoked do |options| true end }\n      expect(subject.get_action(:foo)).to be_a Puppet::Interface::Action\n    end\n\n    it 'should support default actions' do\n      subject.action(:foo) { when_invoked do |options| true end; default }\n      expect(subject.get_default_action).to eq(subject.get_action(:foo))\n    end\n\n    it 'should not support more than one default action' do\n      subject.action(:foo) { when_invoked do |options| true end; default }\n      expect { subject.action(:bar) {\n          when_invoked do |options| true end\n          default\n        }\n      }.to raise_error(/cannot both be default/)\n    end\n  end\n\n  describe \"#get_action\" do\n    let :parent_class do\n      parent_class = Class.new(Puppet::Interface)\n      parent_class.action(:foo) { when_invoked do |options| true end }\n      parent_class\n    end\n\n    it \"should check that we can find inherited actions when we are a class\" do\n      expect(Class.new(parent_class).get_action(:foo).name).to eq(:foo)\n    end\n\n    it \"should check that we can find inherited actions when we are an instance\" do\n      instance = parent_class.new(:foo, '0.0.0')\n      expect(instance.get_action(:foo).name).to eq(:foo)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/action_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/interface'\n\ndescribe Puppet::Interface::Action do\n  describe \"when validating the action name\" do\n    [nil, '', 'foo bar', '-foobar'].each do |input|\n      it \"should treat #{input.inspect} as an invalid name\" do\n        expect {\n          Puppet::Interface::Action.new(nil, input)\n        }.to raise_error(/is an invalid action name/)\n      end\n    end\n  end\n\n  describe \"#when_invoked=\" do\n    it \"should fail if the block has arity 0\" do\n      expect {\n        Puppet::Interface.new(:action_when_invoked, '1.0.0') do\n          action :foo do\n            when_invoked { }\n          end\n        end\n      }.to raise_error ArgumentError, /foo/\n    end\n\n    it \"should work with arity 1 blocks\" do\n      face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do\n        action :foo do\n          when_invoked {|one| }\n        end\n      end\n      # -1, because we use option defaulting. :(\n      expect(face.method(:foo).arity).to eq(-1)\n    end\n\n    it \"should work with arity 2 blocks\" do\n      face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do\n        action :foo do\n          when_invoked {|one, two| }\n        end\n      end\n      # -2, because we use option defaulting. :(\n      expect(face.method(:foo).arity).to eq(-2)\n    end\n\n    it \"should work with arity 1 blocks that collect arguments\" do\n      face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do\n        action :foo do\n          when_invoked {|*one| }\n        end\n      end\n      # -1, because we use only varargs\n      expect(face.method(:foo).arity).to eq(-1)\n    end\n\n    it \"should work with arity 2 blocks that collect arguments\" do\n      face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do\n        action :foo do\n          when_invoked {|one, *two| }\n        end\n      end\n      # -2, because we take one mandatory argument, and one varargs\n      expect(face.method(:foo).arity).to eq(-2)\n    end\n  end\n\n  describe \"when invoking\" do\n    it \"should be able to call other actions on the same object\" do\n      face = Puppet::Interface.new(:my_face, '0.0.1') do\n        action(:foo) do\n          when_invoked { |options| 25 }\n        end\n\n        action(:bar) do\n          when_invoked { |options| \"the value of foo is '#{foo}'\" }\n        end\n      end\n      expect(face.foo).to eq(25)\n      expect(face.bar).to eq(\"the value of foo is '25'\")\n    end\n\n    # bar is a class action calling a class action\n    # quux is a class action calling an instance action\n    # baz is an instance action calling a class action\n    # qux is an instance action calling an instance action\n    it \"should be able to call other actions on the same object when defined on a class\" do\n      class Puppet::Interface::MyInterfaceBaseClass < Puppet::Interface\n        action(:foo) do\n          when_invoked { |options| 25 }\n        end\n\n        action(:bar) do\n          when_invoked { |options| \"the value of foo is '#{foo}'\" }\n        end\n\n        action(:quux) do\n          when_invoked { |options| \"qux told me #{qux}\" }\n        end\n      end\n\n      face = Puppet::Interface::MyInterfaceBaseClass.new(:my_inherited_face, '0.0.1') do\n        action(:baz) do\n          when_invoked { |options| \"the value of foo in baz is '#{foo}'\" }\n        end\n\n        action(:qux) do\n          when_invoked { |options| baz }\n        end\n      end\n      expect(face.foo).to  eq(25)\n      expect(face.bar).to  eq(\"the value of foo is '25'\")\n      expect(face.quux).to eq(\"qux told me the value of foo in baz is '25'\")\n      expect(face.baz).to  eq(\"the value of foo in baz is '25'\")\n      expect(face.qux).to  eq(\"the value of foo in baz is '25'\")\n    end\n\n    context \"when calling the Ruby API\" do\n      let :face do\n        Puppet::Interface.new(:ruby_api, '1.0.0') do\n          action :bar do\n            option \"--bar\"\n            when_invoked do |*args|\n              args.last\n            end\n          end\n        end\n      end\n\n      it \"should work when no options are supplied\" do\n        options = face.bar\n        expect(options).to eq({})\n      end\n\n      it \"should work when options are supplied\" do\n        options = face.bar(:bar => \"beer\")\n        expect(options).to eq({ :bar => \"beer\" })\n      end\n\n      it \"should call #validate_and_clean on the action when invoked\" do\n        expect(face.get_action(:bar)).to receive(:validate_and_clean).with({}).and_return({})\n        face.bar 1, :two, 'three'\n      end\n    end\n  end\n\n  describe \"with action-level options\" do\n    it \"should support options with an empty block\" do\n      face = Puppet::Interface.new(:action_level_options, '0.0.1') do\n        action :foo do\n          when_invoked do |options| true end\n          option \"--bar\" do\n            # this line left deliberately blank\n          end\n        end\n      end\n\n      expect(face).not_to be_option :bar\n      expect(face.get_action(:foo)).to be_option :bar\n    end\n\n    it \"should return only action level options when there are no face options\" do\n      face = Puppet::Interface.new(:action_level_options, '0.0.1') do\n        action :foo do\n          when_invoked do |options| true end\n          option \"--bar\"\n        end\n      end\n\n      expect(face.get_action(:foo).options).to match_array([:bar])\n    end\n\n    describe \"option aliases\" do\n      let :option do action.get_option :bar end\n      let :action do face.get_action :foo end\n      let :face do\n        Puppet::Interface.new(:action_level_options, '0.0.1') do\n          action :foo do\n            when_invoked do |options| options end\n            option \"--bar\", \"--foo\", \"-b\"\n          end\n        end\n      end\n\n      it \"should only list options and not aliases\" do\n        expect(action.options).to match_array([:bar])\n      end\n\n      it \"should use the canonical option name when passed aliases\" do\n        name = option.name\n        option.aliases.each do |input|\n          expect(face.foo(input => 1)).to eq({ name => 1 })\n        end\n      end\n    end\n\n    describe \"with both face and action options\" do\n      let :face do\n        Puppet::Interface.new(:action_level_options, '0.0.1') do\n          action :foo do when_invoked do |options| true end ; option \"--bar\" end\n          action :baz do when_invoked do |options| true end ; option \"--bim\" end\n          option \"--quux\"\n        end\n      end\n\n      it \"should return combined face and action options\" do\n        expect(face.get_action(:foo).options).to match_array([:bar, :quux])\n      end\n\n      it \"should fetch options that the face inherited\" do\n        parent = Class.new(Puppet::Interface)\n        parent.option \"--foo\"\n        child = parent.new(:inherited_options, '0.0.1') do\n          option \"--bar\"\n          action :action do\n            when_invoked do |options| true end\n            option \"--baz\"\n          end\n        end\n\n        action = child.get_action(:action)\n        expect(action).to be\n\n        [:baz, :bar, :foo].each do |name|\n          expect(action.get_option(name)).to be_an_instance_of Puppet::Interface::Option\n        end\n      end\n\n      it \"should get an action option when asked\" do\n        expect(face.get_action(:foo).get_option(:bar)).\n          to be_an_instance_of Puppet::Interface::Option\n      end\n\n      it \"should get a face option when asked\" do\n        expect(face.get_action(:foo).get_option(:quux)).\n          to be_an_instance_of Puppet::Interface::Option\n      end\n\n      it \"should return options only for this action\" do\n        expect(face.get_action(:baz).options).to match_array([:bim, :quux])\n      end\n    end\n\n    it_should_behave_like \"things that declare options\" do\n      def add_options_to(&block)\n        face = Puppet::Interface.new(:with_options, '0.0.1') do\n          action(:foo) do\n            when_invoked do |options| true end\n            self.instance_eval(&block)\n          end\n        end\n        face.get_action(:foo)\n      end\n    end\n\n    it \"should fail when a face option duplicates an action option\" do\n      expect {\n        Puppet::Interface.new(:action_level_options, '0.0.1') do\n          option \"--foo\"\n          action :bar do option \"--foo\" end\n        end\n      }.to raise_error ArgumentError, /Option foo conflicts with existing option foo/i\n    end\n\n    it \"should fail when a required action option is not provided\" do\n      face = Puppet::Interface.new(:required_action_option, '0.0.1') do\n        action(:bar) do\n          option('--foo') { required }\n          when_invoked {|options| }\n        end\n      end\n      expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/\n    end\n\n    it \"should fail when a required face option is not provided\" do\n      face = Puppet::Interface.new(:required_face_option, '0.0.1') do\n        option('--foo') { required }\n        action(:bar) { when_invoked {|options| } }\n      end\n      expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/\n    end\n  end\n\n  context \"with decorators\" do\n    context \"declared locally\" do\n      let :face do\n        Puppet::Interface.new(:action_decorators, '0.0.1') do\n          action :bar do when_invoked do |options| true end end\n          def reported; @reported; end\n          def report(arg)\n            (@reported ||= []) << arg\n          end\n        end\n      end\n\n      it \"should execute before advice on action options in declaration order\" do\n        face.action(:boo) do\n          option(\"--foo\")        { before_action { |_,_,_| report :foo  } }\n          option(\"--bar\", '-b')  { before_action { |_,_,_| report :bar  } }\n          option(\"-q\", \"--quux\") { before_action { |_,_,_| report :quux } }\n          option(\"-f\")           { before_action { |_,_,_| report :f    } }\n          option(\"--baz\")        { before_action { |_,_,_| report :baz  } }\n          when_invoked {|options| }\n        end\n\n        face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1\n        expect(face.reported).to eq([ :foo, :bar, :quux, :f, :baz ])\n      end\n\n      it \"should execute after advice on action options in declaration order\" do\n        face.action(:boo) do\n          option(\"--foo\")        { after_action { |_,_,_| report :foo  } }\n          option(\"--bar\", '-b')  { after_action { |_,_,_| report :bar  } }\n          option(\"-q\", \"--quux\") { after_action { |_,_,_| report :quux } }\n          option(\"-f\")           { after_action { |_,_,_| report :f    } }\n          option(\"--baz\")        { after_action { |_,_,_| report :baz  } }\n          when_invoked {|options| }\n        end\n\n        face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1\n        expect(face.reported).to eq([ :foo, :bar, :quux, :f, :baz ].reverse)\n      end\n\n      it \"should execute before advice on face options in declaration order\" do\n        face.instance_eval do\n          option(\"--foo\")        { before_action { |_,_,_| report :foo  } }\n          option(\"--bar\", '-b')  { before_action { |_,_,_| report :bar  } }\n          option(\"-q\", \"--quux\") { before_action { |_,_,_| report :quux } }\n          option(\"-f\")           { before_action { |_,_,_| report :f    } }\n          option(\"--baz\")        { before_action { |_,_,_| report :baz  } }\n        end\n        face.action(:boo) { when_invoked { |options| } }\n\n        face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1\n        expect(face.reported).to eq([ :foo, :bar, :quux, :f, :baz ])\n      end\n\n      it \"should execute after advice on face options in declaration order\" do\n        face.instance_eval do\n          option(\"--foo\")        { after_action { |_,_,_| report :foo  } }\n          option(\"--bar\", '-b')  { after_action { |_,_,_| report :bar  } }\n          option(\"-q\", \"--quux\") { after_action { |_,_,_| report :quux } }\n          option(\"-f\")           { after_action { |_,_,_| report :f    } }\n          option(\"--baz\")        { after_action { |_,_,_| report :baz  } }\n        end\n        face.action(:boo) { when_invoked { |options| } }\n\n        face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1\n        expect(face.reported).to eq([ :foo, :bar, :quux, :f, :baz ].reverse)\n      end\n\n      it \"should execute before advice on face options before action options\" do\n        face.instance_eval do\n          option(\"--face-foo\")        { before_action { |_,_,_| report :face_foo  } }\n          option(\"--face-bar\", '-r')  { before_action { |_,_,_| report :face_bar  } }\n          action(:boo) do\n            option(\"--action-foo\")        { before_action { |_,_,_| report :action_foo  } }\n            option(\"--action-bar\", '-b')  { before_action { |_,_,_| report :action_bar  } }\n            option(\"-q\", \"--action-quux\") { before_action { |_,_,_| report :action_quux } }\n            option(\"-a\")                  { before_action { |_,_,_| report :a           } }\n            option(\"--action-baz\")        { before_action { |_,_,_| report :action_baz  } }\n            when_invoked {|options| }\n          end\n          option(\"-u\", \"--face-quux\") { before_action { |_,_,_| report :face_quux } }\n          option(\"-f\")                { before_action { |_,_,_| report :f         } }\n          option(\"--face-baz\")        { before_action { |_,_,_| report :face_baz  } }\n        end\n\n        expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz,\n                           :action_foo, :action_bar, :action_quux, :a, :action_baz ]\n        face.boo Hash[ *expected_calls.zip([]).flatten ]\n        expect(face.reported).to eq(expected_calls)\n      end\n\n      it \"should execute after advice on face options in declaration order\" do\n        face.instance_eval do\n          option(\"--face-foo\")        { after_action { |_,_,_| report :face_foo  } }\n          option(\"--face-bar\", '-r')  { after_action { |_,_,_| report :face_bar  } }\n          action(:boo) do\n            option(\"--action-foo\")        { after_action { |_,_,_| report :action_foo  } }\n            option(\"--action-bar\", '-b')  { after_action { |_,_,_| report :action_bar  } }\n            option(\"-q\", \"--action-quux\") { after_action { |_,_,_| report :action_quux } }\n            option(\"-a\")                  { after_action { |_,_,_| report :a           } }\n            option(\"--action-baz\")        { after_action { |_,_,_| report :action_baz  } }\n            when_invoked {|options| }\n          end\n          option(\"-u\", \"--face-quux\") { after_action { |_,_,_| report :face_quux } }\n          option(\"-f\")                { after_action { |_,_,_| report :f         } }\n          option(\"--face-baz\")        { after_action { |_,_,_| report :face_baz  } }\n        end\n\n        expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz,\n                           :action_foo, :action_bar, :action_quux, :a, :action_baz ]\n        face.boo Hash[ *expected_calls.zip([]).flatten ]\n        expect(face.reported).to eq(expected_calls.reverse)\n      end\n\n      it \"should not invoke a decorator if the options are empty\" do\n        face.option(\"--foo FOO\") { before_action { |_,_,_| report :before_action } }\n        expect(face).not_to receive(:report)\n        face.bar\n      end\n\n      context \"passing a subset of the options\" do\n        before :each do\n          face.option(\"--foo\") { before_action { |_,_,_| report :foo } }\n          face.option(\"--bar\") { before_action { |_,_,_| report :bar } }\n        end\n\n        it \"should invoke only foo's advice when passed only 'foo'\" do\n          face.bar(:foo => true)\n          expect(face.reported).to eq([ :foo ])\n        end\n\n        it \"should invoke only bar's advice when passed only 'bar'\" do\n          face.bar(:bar => true)\n          expect(face.reported).to eq([ :bar ])\n        end\n\n        it \"should invoke advice for all passed options\" do\n          face.bar(:foo => true, :bar => true)\n          expect(face.reported).to eq([ :foo, :bar ])\n        end\n      end\n    end\n\n    context \"and inheritance\" do\n      let :parent do\n        Class.new(Puppet::Interface) do\n          action(:on_parent) { when_invoked { |options| :on_parent } }\n\n          def reported; @reported; end\n          def report(arg)\n            (@reported ||= []) << arg\n          end\n        end\n      end\n\n      let :child do\n        parent.new(:inherited_decorators, '0.0.1') do\n          action(:on_child) { when_invoked { |options| :on_child } }\n        end\n      end\n\n      context \"locally declared face options\" do\n        subject do\n          child.option(\"--foo=\") { before_action { |_,_,_| report :child_before } }\n          child\n        end\n\n        it \"should be invoked when calling a child action\" do\n          expect(subject.on_child(:foo => true)).to eq(:on_child)\n          expect(subject.reported).to eq([ :child_before ])\n        end\n\n        it \"should be invoked when calling a parent action\" do\n          expect(subject.on_parent(:foo => true)).to eq(:on_parent)\n          expect(subject.reported).to eq([ :child_before ])\n        end\n      end\n\n      context \"inherited face option decorators\" do\n        subject do\n          parent.option(\"--foo=\") { before_action { |_,_,_| report :parent_before } }\n          child\n        end\n\n        it \"should be invoked when calling a child action\" do\n          expect(subject.on_child(:foo => true)).to eq(:on_child)\n          expect(subject.reported).to eq([ :parent_before ])\n        end\n\n        it \"should be invoked when calling a parent action\" do\n          expect(subject.on_parent(:foo => true)).to eq(:on_parent)\n          expect(subject.reported).to eq([ :parent_before ])\n        end\n      end\n\n      context \"with both inherited and local face options\" do\n        # Decorations should be invoked in declaration order, according to\n        # inheritance (e.g. parent class options should be handled before\n        # subclass options).\n        subject do\n          child.option \"-c\" do\n            before_action { |action, args, options| report :c_before }\n            after_action  { |action, args, options| report :c_after  }\n          end\n\n          parent.option \"-a\" do\n            before_action { |action, args, options| report :a_before }\n            after_action  { |action, args, options| report :a_after  }\n          end\n\n          child.option \"-d\" do\n            before_action { |action, args, options| report :d_before }\n            after_action  { |action, args, options| report :d_after  }\n          end\n\n          parent.option \"-b\" do\n            before_action { |action, args, options| report :b_before }\n            after_action  { |action, args, options| report :b_after  }\n          end\n\n          child.action(:decorations) { when_invoked {  |options| report :invoked } }\n\n          child\n        end\n\n        it \"should invoke all decorations when calling a child action\" do\n          subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1)\n          expect(subject.reported).to eq([\n            :a_before, :b_before, :c_before, :d_before,\n            :invoked,\n            :d_after, :c_after, :b_after, :a_after\n          ])\n        end\n\n        it \"should invoke all decorations when calling a parent action\" do\n          subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1)\n          expect(subject.reported).to eq([\n            :a_before, :b_before, :c_before, :d_before,\n            :invoked,\n            :d_after, :c_after, :b_after, :a_after\n          ])\n        end\n      end\n    end\n  end\n\n  it_should_behave_like \"documentation on faces\" do\n    subject do\n      face = Puppet::Interface.new(:action_documentation, '0.0.1') do\n        action :documentation do\n          when_invoked do |options| true end\n        end\n      end\n      face.get_action(:documentation)\n    end\n  end\n\n  context \"#validate_and_clean\" do\n    subject do\n      Puppet::Interface.new(:validate_args, '1.0.0') do\n        action(:test) { when_invoked { |options| options } }\n      end\n    end\n\n    it \"should fail if a required option is not passed\" do\n      subject.option \"--foo\" do required end\n      expect { subject.test }.to raise_error ArgumentError, /options are required/\n    end\n\n    it \"should fail if two aliases to one option are passed\" do\n      subject.option \"--foo\", \"-f\"\n      expect { subject.test :foo => true, :f => true }.\n        to raise_error ArgumentError, /Multiple aliases for the same option/\n    end\n\n    it \"should fail if an unknown option is passed\" do\n      expect { subject.test :unknown => true }.\n        to raise_error ArgumentError, /Unknown options passed: unknown/\n    end\n\n    it \"should report all the unknown options passed\" do\n      expect { subject.test :unknown => true, :unseen => false }.\n        to raise_error ArgumentError, /Unknown options passed: unknown, unseen/\n    end\n\n    it \"should accept 'global' options from settings\" do\n      expect {\n        expect(subject.test(:certname => \"true\")).to eq({ :certname => \"true\" })\n      }.not_to raise_error\n    end\n  end\n\n  context \"default option values\" do\n    subject do\n      Puppet::Interface.new(:default_option_values, '1.0.0') do\n        action :foo do\n          option \"--foo\" do end\n          option \"--bar\" do end\n          when_invoked do |options| options end\n        end\n      end\n    end\n\n    let :action do subject.get_action :foo end\n    let :option do action.get_option :foo end\n\n    it \"should not add options without defaults\" do\n      expect(subject.foo).to eq({})\n    end\n\n    it \"should not add options without defaults, if options are given\" do\n      expect(subject.foo(:bar => 1)).to eq({ :bar => 1 })\n    end\n\n    it \"should add the option default value when set\" do\n      option.default = proc { 12 }\n      expect(subject.foo).to eq({ :foo => 12 })\n    end\n\n    it \"should add the option default value when set, if other options are given\" do\n      option.default = proc { 12 }\n      expect(subject.foo(:bar => 1)).to eq({ :foo => 12, :bar => 1 })\n    end\n\n    it \"should invoke the same default proc every time called\" do\n      option.default = proc { @foo ||= {} }\n      expect(subject.foo[:foo].object_id).to eq(subject.foo[:foo].object_id)\n    end\n\n    [nil, 0, 1, true, false, {}, []].each do |input|\n      it \"should not override a passed option (#{input.inspect})\" do\n        option.default = proc { :fail }\n        expect(subject.foo(:foo => input)).to eq({ :foo => input })\n      end\n    end\n  end\n\n  context \"runtime manipulations\" do\n    subject do\n      Puppet::Interface.new(:runtime_manipulations, '1.0.0') do\n        action :foo do\n          when_invoked do |options| options end\n        end\n      end\n    end\n\n    let :action do subject.get_action :foo end\n\n    it \"should be the face default action if default is set true\" do\n      expect(subject.get_default_action).to be_nil\n      action.default = true\n      expect(subject.get_default_action).to eq(action)\n    end\n  end\n\n  context \"when deprecating a face action\" do\n    let :face do\n      Puppet::Interface.new(:foo, '1.0.0') do\n        action :bar do\n          option \"--bar\"\n          when_invoked do |options| options end\n        end\n      end\n    end\n\n    let :action do face.get_action :bar end\n\n    describe \"#deprecate\" do\n      it \"should set the deprecated value to true\" do\n        expect(action).not_to be_deprecated\n        action.deprecate\n        expect(action).to be_deprecated\n      end\n    end\n\n    describe \"#deprecated?\" do\n      it \"should return a nil (falsey) value by default\" do\n        expect(action.deprecated?).to be_falsey\n      end\n\n      it \"should return true if the action has been deprecated\" do\n        expect(action).not_to be_deprecated\n        action.deprecate\n        expect(action).to be_deprecated\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/documentation_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/interface'\n\nclass Puppet::Interface::TinyDocs::Test\n  include Puppet::Interface::TinyDocs\n  attr_accessor :name, :options, :display_global_options\n  def initialize\n    self.name    = \"tinydoc-test\"\n    self.options = []\n    self.display_global_options = []\n  end\n\n  def get_option(name)\n    Puppet::Interface::Option.new(nil, \"--#{name}\")\n  end\nend\n\ndescribe Puppet::Interface::TinyDocs do\n  subject { Puppet::Interface::TinyDocs::Test.new }\n\n  context \"#build_synopsis\" do\n    before :each do\n      subject.options = [:foo, :bar]\n    end\n\n    it { is_expected.to respond_to :build_synopsis }\n\n    it \"should put a space between options (#7828)\" do\n      expect(subject.build_synopsis('baz')).to match(/#{Regexp.quote('[--foo] [--bar]')}/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/face_collection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'tmpdir'\nrequire 'puppet/interface'\n\ndescribe Puppet::Interface::FaceCollection do\n\n  # To prevent conflicts with other specs that use faces, we must save and restore global state.\n  # Because there are specs that do 'describe Puppet::Face[...]', we must restore the same objects otherwise\n  # the 'subject' of the specs will differ.\n  before :all do\n    # Save FaceCollection's global state\n    faces = described_class.instance_variable_get(:@faces)\n    @faces = faces.dup\n    faces.each do |k, v|\n      @faces[k] = v.dup\n    end\n    @faces_loaded = described_class.instance_variable_get(:@loaded)\n\n    # Save the already required face files\n    @required = []\n    $\".each do |path|\n      @required << path if path =~ /face\\/.*\\.rb$/\n    end\n\n    # Save Autoload's global state\n    @loaded = Puppet::Util::Autoload.instance_variable_get(:@loaded).dup\n  end\n\n  after :all do\n    # Restore global state\n    described_class.instance_variable_set :@faces, @faces\n    described_class.instance_variable_set :@loaded, @faces_loaded\n    $\".delete_if { |path| path =~ /face\\/.*\\.rb$/ }\n    @required.each { |path| $\".push path unless $\".include? path }\n    Puppet::Util::Autoload.instance_variable_set(:@loaded, @loaded)\n  end\n\n  before :each do\n    # Before each test, clear the faces\n    subject.instance_variable_get(:@faces).clear\n    subject.instance_variable_set(:@loaded, false)\n    Puppet::Util::Autoload.instance_variable_get(:@loaded).clear\n    $\".delete_if { |path| path =~ /face\\/.*\\.rb$/ }\n  end\n\n  describe \"::[]\" do\n    before :each do\n      subject.instance_variable_get(\"@faces\")[:foo][SemanticPuppet::Version.parse('0.0.1')] = 10\n    end\n\n    it \"should return the face with the given name\" do\n      expect(subject[\"foo\", '0.0.1']).to eq(10)\n    end\n\n    it \"should attempt to load the face if it isn't found\" do\n      expect(subject).to receive(:require).once.with('puppet/face/bar')\n      expect(subject).to receive(:require).once.with('puppet/face/0.0.1/bar')\n      subject[\"bar\", '0.0.1']\n    end\n\n    it \"should attempt to load the default face for the specified version :current\" do\n      expect(subject).to receive(:require).with('puppet/face/fozzie')\n      subject['fozzie', :current]\n    end\n\n    it \"should return true if the face specified is registered\" do\n      subject.instance_variable_get(\"@faces\")[:foo][SemanticPuppet::Version.parse('0.0.1')] = 10\n      expect(subject[\"foo\", '0.0.1']).to eq(10)\n    end\n\n    it \"should attempt to require the face if it is not registered\" do\n      expect(subject).to receive(:require) do |file|\n        subject.instance_variable_get(\"@faces\")[:bar][SemanticPuppet::Version.parse('0.0.1')] = true\n        expect(file).to eq('puppet/face/bar')\n      end\n\n      expect(subject[\"bar\", '0.0.1']).to be_truthy\n    end\n\n    it \"should return false if the face is not registered\" do\n      allow(subject).to receive(:require).and_return(true)\n      expect(subject[\"bar\", '0.0.1']).to be_falsey\n    end\n\n    it \"should return false if the face file itself is missing\" do\n      num_calls = 0\n      allow(subject).to receive(:require) do\n        num_calls += 1\n        if num_calls == 1\n          raise LoadError.new('no such file to load -- puppet/face/bar')\n        else\n          raise LoadError.new('no such file to load -- puppet/face/0.0.1/bar')\n        end\n      end\n\n      expect(subject[\"bar\", '0.0.1']).to be_falsey\n    end\n\n    it \"should register the version loaded by `:current` as `:current`\" do\n      expect(subject).to receive(:require) do |file|\n        subject.instance_variable_get(\"@faces\")[:huzzah]['2.0.1'] = :huzzah_face\n        expect(file).to eq('puppet/face/huzzah')\n      end\n      subject[\"huzzah\", :current]\n      expect(subject.instance_variable_get(\"@faces\")[:huzzah][:current]).to eq(:huzzah_face)\n    end\n\n    context \"with something on disk\" do\n      it \"should register the version loaded from `puppet/face/{name}` as `:current`\" do\n        expect(subject[\"huzzah\", '2.0.1']).to be\n        expect(subject[\"huzzah\", :current]).to be\n        expect(Puppet::Face[:huzzah, '2.0.1']).to eq(Puppet::Face[:huzzah, :current])\n      end\n\n      it \"should index :current when the code was pre-required\" do\n        expect(subject.instance_variable_get(\"@faces\")[:huzzah]).not_to be_key :current\n        require 'puppet/face/huzzah'\n        expect(subject[:huzzah, :current]).to be_truthy\n      end\n    end\n\n    it \"should not cause an invalid face to be enumerated later\" do\n      expect(subject[:there_is_no_face, :current]).to be_falsey\n      expect(subject.faces).not_to include :there_is_no_face\n    end\n  end\n\n  describe \"::get_action_for_face\" do\n    it \"should return an action on the current face\" do\n      expect(Puppet::Face::FaceCollection.get_action_for_face(:huzzah, :bar, :current)).\n        to be_an_instance_of Puppet::Interface::Action\n    end\n\n    it \"should return an action on an older version of a face\" do\n      action = Puppet::Face::FaceCollection.\n        get_action_for_face(:huzzah, :obsolete, :current)\n\n      expect(action).to be_an_instance_of Puppet::Interface::Action\n      expect(action.face.version).to eq(SemanticPuppet::Version.parse('1.0.0'))\n    end\n\n    it \"should load the full older version of a face\" do\n      action = Puppet::Face::FaceCollection.\n        get_action_for_face(:huzzah, :obsolete, :current)\n\n      expect(action.face.version).to eq(SemanticPuppet::Version.parse('1.0.0'))\n      expect(action.face).to be_action :obsolete_in_core\n    end\n\n    it \"should not add obsolete actions to the current version\" do\n      action = Puppet::Face::FaceCollection.\n        get_action_for_face(:huzzah, :obsolete, :current)\n\n      expect(action.face.version).to eq(SemanticPuppet::Version.parse('1.0.0'))\n      expect(action.face).to be_action :obsolete_in_core\n\n      current = Puppet::Face[:huzzah, :current]\n      expect(current.version).to eq(SemanticPuppet::Version.parse('2.0.1'))\n      expect(current).not_to be_action :obsolete_in_core\n      expect(current).not_to be_action :obsolete\n    end\n  end\n\n  describe \"::register\" do\n    it \"should store the face by name\" do\n      face = Puppet::Face.new(:my_face, '0.0.1')\n      subject.register(face)\n      expect(subject.instance_variable_get(\"@faces\")).to eq({\n        :my_face => { face.version => face }\n      })\n    end\n  end\n\n  describe \"::underscorize\" do\n    faulty = [1, \"23foo\", \"#foo\", \"$bar\", \"sturm und drang\", :\"sturm und drang\"]\n    valid  = {\n      \"Foo\"       => :foo,\n      :Foo        => :foo,\n      \"foo_bar\"   => :foo_bar,\n      :foo_bar    => :foo_bar,\n      \"foo-bar\"   => :foo_bar,\n      :\"foo-bar\"  => :foo_bar,\n      \"foo_bar23\" => :foo_bar23,\n      :foo_bar23  => :foo_bar23,\n    }\n\n    valid.each do |input, expect|\n      it \"should map #{input.inspect} to #{expect.inspect}\" do\n        result = subject.underscorize(input)\n        expect(result).to eq(expect)\n      end\n    end\n\n    faulty.each do |input|\n      it \"should fail when presented with #{input.inspect} (#{input.class})\" do\n        expect { subject.underscorize(input) }.\n          to raise_error ArgumentError, /not a valid face name/\n      end\n    end\n  end\n\n  context \"faulty faces\" do\n    before :each do\n      $:.unshift \"#{PuppetSpec::FIXTURE_DIR}/faulty_face\"\n    end\n\n    after :each do\n      $:.delete_if {|x| x == \"#{PuppetSpec::FIXTURE_DIR}/faulty_face\"}\n    end\n\n    it \"should not die if a face has a syntax error\" do\n      expect(subject.faces).to be_include :help\n      expect(subject.faces).not_to be_include :syntax\n      expect(@logs).not_to be_empty\n      expect(@logs.first.message).to match(/syntax error/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/option_builder_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/interface'\n\ndescribe Puppet::Interface::OptionBuilder do\n  let :face do Puppet::Interface.new(:option_builder_testing, '0.0.1') end\n\n  it \"should be able to construct an option without a block\" do\n    expect(Puppet::Interface::OptionBuilder.build(face, \"--foo\")).\n      to be_an_instance_of Puppet::Interface::Option\n  end\n\n  Puppet.settings.each do |name, value|\n    it \"should fail when option #{name.inspect} already exists in puppet core\" do\n      expect do\n        Puppet::Interface::OptionBuilder.build(face, \"--#{name}\")\n      end.to raise_error ArgumentError, /already defined/\n    end\n  end\n\n  it \"should work with an empty block\" do\n    option = Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n      # This block deliberately left blank.\n    end\n\n    expect(option).to be_an_instance_of Puppet::Interface::Option\n  end\n\n  [:description, :summary].each do |doc|\n    it \"should support #{doc} declarations\" do\n      text = \"this is the #{doc}\"\n      option = Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n        self.send doc, text\n      end\n      expect(option).to be_an_instance_of Puppet::Interface::Option\n      expect(option.send(doc)).to eq(text)\n    end\n  end\n\n  context \"before_action hook\" do\n    it \"should support a before_action hook\" do\n      option = Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n        before_action do |a,b,c| :whatever end\n      end\n      expect(option.before_action).to be_an_instance_of UnboundMethod\n    end\n\n    it \"should fail if the hook block takes too few arguments\" do\n      expect do\n        Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n          before_action do |one, two| true end\n        end\n      end.to raise_error ArgumentError, /takes three arguments/\n    end\n\n    it \"should fail if the hook block takes too many arguments\" do\n      expect do\n        Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n          before_action do |one, two, three, four| true end\n        end\n      end.to raise_error ArgumentError, /takes three arguments/\n    end\n\n    it \"should fail if the hook block takes a variable number of arguments\" do\n      expect do\n        Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n          before_action do |*blah| true end\n        end\n      end.to raise_error ArgumentError, /takes three arguments/\n    end\n\n    it \"should support simple required declarations\" do\n      opt = Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n        required\n      end\n      expect(opt).to be_required\n    end\n\n    it \"should support arguments to the required property\" do\n      opt = Puppet::Interface::OptionBuilder.build(face, \"--foo\") do\n        required(false)\n      end\n      expect(opt).not_to be_required\n    end\n    \n  end\nend\n"
  },
  {
    "path": "spec/unit/interface/option_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/interface'\n\ndescribe Puppet::Interface::Option do\n  let :face do Puppet::Interface.new(:option_testing, '0.0.1') end\n\n  describe \"#optparse_to_name\" do\n    [\"\", \"=BAR\", \" BAR\", \"=bar\", \" bar\"].each do |postfix|\n      { \"--foo\" => :foo, \"-f\" => :f }.each do |base, expect|\n        input = base + postfix\n        it \"should map #{input.inspect} to #{expect.inspect}\" do\n          option = Puppet::Interface::Option.new(face, input)\n          expect(option.name).to eq(expect)\n        end\n      end\n    end\n\n    [:foo, 12, nil, {}, []].each do |input|\n      it \"should fail sensible when given #{input.inspect}\" do\n        expect {\n          Puppet::Interface::Option.new(face, input)\n        }.to raise_error ArgumentError, /is not valid for an option argument/\n      end\n    end\n\n    [\"-foo\", \"-foo=BAR\", \"-foo BAR\"].each do |input|\n      it \"should fail with a single dash for long option #{input.inspect}\" do\n        expect {\n          Puppet::Interface::Option.new(face, input)\n        }.to raise_error ArgumentError, /long options need two dashes \\(--\\)/\n      end\n    end\n  end\n\n  it \"requires a face when created\" do\n    expect {\n      Puppet::Interface::Option.new\n    }.to raise_error ArgumentError, /wrong number of arguments/\n  end\n\n  it \"also requires some declaration arguments when created\" do\n    expect {\n      Puppet::Interface::Option.new(face)\n    }.to raise_error ArgumentError, /No option declarations found/\n  end\n\n  it \"should infer the name from an optparse string\" do\n    option = Puppet::Interface::Option.new(face, \"--foo\")\n    expect(option.name).to eq(:foo)\n  end\n\n  it \"should infer the name when multiple optparse string are given\" do\n    option = Puppet::Interface::Option.new(face, \"--foo\", \"-f\")\n    expect(option.name).to eq(:foo)\n  end\n\n  it \"should prefer the first long option name over a short option name\" do\n    option = Puppet::Interface::Option.new(face, \"-f\", \"--foo\")\n    expect(option.name).to eq(:foo)\n  end\n\n  it \"should create an instance when given a face and name\" do\n    expect(Puppet::Interface::Option.new(face, \"--foo\")).\n      to be_instance_of Puppet::Interface::Option\n  end\n\n  Puppet.settings.each do |name, value|\n    it \"should fail when option #{name.inspect} already exists in puppet core\" do\n      expect do\n        Puppet::Interface::Option.new(face, \"--#{name}\")\n      end.to raise_error ArgumentError, /already defined/\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"should transform a symbol into a string\" do\n      option = Puppet::Interface::Option.new(face, \"--foo\")\n      expect(option.name).to eq(:foo)\n      expect(option.to_s).to eq(\"foo\")\n    end\n\n    it \"should use - rather than _ to separate words in strings but not symbols\" do\n      option = Puppet::Interface::Option.new(face, \"--foo-bar\")\n      expect(option.name).to eq(:foo_bar)\n      expect(option.to_s).to eq(\"foo-bar\")\n    end\n  end\n\n  %w{before after}.each do |side|\n    describe \"#{side} hooks\" do\n      subject { Puppet::Interface::Option.new(face, \"--foo\") }\n      let :proc do Proc.new do :from_proc end end\n\n      it { is_expected.to respond_to \"#{side}_action\" }\n      it { is_expected.to respond_to \"#{side}_action=\" }\n\n      it \"should set the #{side}_action hook\" do\n        expect(subject.send(\"#{side}_action\")).to be_nil\n        subject.send(\"#{side}_action=\", proc)\n        expect(subject.send(\"#{side}_action\")).to be_an_instance_of UnboundMethod\n      end\n\n      data = [1, \"foo\", :foo, Object.new, method(:hash), method(:hash).unbind]\n      data.each do |input|\n        it \"should fail if a #{input.class} is added to the #{side} hooks\" do\n          expect { subject.send(\"#{side}_action=\", input) }.\n            to raise_error ArgumentError, /not a proc/\n        end\n      end\n    end\n  end\n\n  context \"defaults\" do\n    subject { Puppet::Interface::Option.new(face, \"--foo\") }\n\n    it \"should work sanely if member variables are used for state\" do\n      subject.default = proc { @foo ||= 0; @foo += 1 }\n      expect(subject.default).to eq(1)\n      expect(subject.default).to eq(2)\n      expect(subject.default).to eq(3)\n    end\n\n    context \"with no default\" do\n      it { is_expected.not_to be_has_default }\n      its :default do should be_nil end\n\n      it \"should set a proc as default\" do\n        expect { subject.default = proc { 12 } }.to_not raise_error\n      end\n\n      [1, {}, [], Object.new, \"foo\"].each do |input|\n        it \"should reject anything but a proc (#{input.class})\" do\n          expect { subject.default = input }.to raise_error ArgumentError, /not a proc/\n        end\n      end\n    end\n\n    context \"with a default\" do\n      before :each do subject.default = proc { [:foo] } end\n\n      it { is_expected.to be_has_default }\n      its :default do should == [:foo] end\n\n      it \"should invoke the block every time\" do\n        expect(subject.default.object_id).not_to eq(subject.default.object_id)\n        expect(subject.default).to eq(subject.default)\n      end\n\n      it \"should allow replacing the default proc\" do\n        expect(subject.default).to eq([:foo])\n        subject.default = proc { :bar }\n        expect(subject.default).to eq(:bar)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/interface_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/face'\nrequire 'puppet/interface'\n\ndescribe Puppet::Interface do\n  subject { Puppet::Interface }\n\n  before :each do\n    @faces = Puppet::Interface::FaceCollection.\n      instance_variable_get(\"@faces\").dup\n    @dq = $\".dup\n    $\".delete_if do |path| path =~ %r{/face/.*\\.rb$} end\n    Puppet::Interface::FaceCollection.instance_variable_get(\"@faces\").clear\n  end\n\n  after :each do\n    Puppet::Interface::FaceCollection.instance_variable_set(\"@faces\", @faces)\n    $\".clear ; @dq.each do |item| $\" << item end\n  end\n\n  describe \"#[]\" do\n    it \"should fail when no version is requested\" do\n      expect { subject[:huzzah] }.to raise_error ArgumentError\n    end\n\n    it \"should raise an exception when the requested version is unavailable\" do\n      expect { subject[:huzzah, '17.0.0'] }.to raise_error(Puppet::Error, /Could not find version/)\n    end\n\n    it \"should raise an exception when the requested face doesn't exist\" do\n      expect { subject[:burrble_toot, :current] }.to raise_error(Puppet::Error, /Could not find Puppet Face/)\n    end\n\n    describe \"version matching\" do\n      { '1'     => '1.1.1',\n        '1.0'   => '1.0.1',\n        '1.0.1' => '1.0.1',\n        '1.1'   => '1.1.1',\n        '1.1.1' => '1.1.1'\n      }.each do |input, expect|\n        it \"should match #{input.inspect} to #{expect.inspect}\" do\n          face = subject[:version_matching, input]\n          expect(face).to be\n          expect(face.version).to eq(expect)\n        end\n      end\n\n      %w{1.0.2 1.2}.each do |input|\n        it \"should not match #{input.inspect} to any version\" do\n          expect { subject[:version_matching, input] }.\n            to raise_error Puppet::Error, /Could not find version/\n        end\n      end\n    end\n  end\n\n  describe \"#define\" do\n    it \"should register the face\" do\n      face  = subject.define(:face_test_register, '0.0.1')\n      expect(face).to eq(subject[:face_test_register, '0.0.1'])\n    end\n\n    it \"should load actions\" do\n      expect_any_instance_of(subject).to receive(:load_actions)\n      subject.define(:face_test_load_actions, '0.0.1')\n    end\n\n    it \"should require a version number\" do\n      expect { subject.define(:no_version) }.to raise_error ArgumentError\n    end\n\n    it \"should support summary builder and accessor methods\" do\n      expect(subject.new(:foo, '1.0.0')).to respond_to(:summary).with(0).arguments\n      expect(subject.new(:foo, '1.0.0')).to respond_to(:summary=).with(1).arguments\n    end\n\n    # Required documentation methods...\n    { :summary     => \"summary\",\n      :description => \"This is the description of the stuff\\n\\nWhee\",\n      :examples    => \"This is my example\",\n      :short_description => \"This is my custom short description\",\n      :notes       => \"These are my notes...\",\n      :author      => \"This is my authorship data\",\n    }.each do |attr, value|\n      it \"should support #{attr} in the builder\" do\n        face = subject.new(:builder, '1.0.0') do\n          self.send(attr, value)\n        end\n        expect(face.send(attr)).to eq(value)\n      end\n    end\n  end\n\n  describe \"#initialize\" do\n    it \"should require a version number\" do\n      expect { subject.new(:no_version) }.to raise_error ArgumentError\n    end\n\n    it \"should require a valid version number\" do\n      expect { subject.new(:bad_version, 'Rasins') }.\n        to raise_error ArgumentError\n    end\n\n    it \"should instance-eval any provided block\" do\n      face = subject.new(:face_test_block, '0.0.1') do\n        action(:something) do\n          when_invoked {|_| \"foo\" }\n        end\n      end\n\n      expect(face.something).to eq(\"foo\")\n    end\n  end\n\n  it \"should have a name\" do\n    expect(subject.new(:me, '0.0.1').name).to eq(:me)\n  end\n\n  it \"should stringify with its own name\" do\n    expect(subject.new(:me, '0.0.1').to_s).to match(/\\bme\\b/)\n  end\n\n  it \"should try to require faces that are not known\" do\n    expect(subject::FaceCollection).to receive(:load_face).with(:foo, :current)\n    expect(subject::FaceCollection).to receive(:load_face).with(:foo, '0.0.1')\n    expect { subject[:foo, '0.0.1'] }.to raise_error Puppet::Error\n  end\n\n  describe 'when raising NoMethodErrors' do\n    subject { described_class.new(:foo, '1.0.0') }\n\n    if RUBY_VERSION.to_f >= 3.3\n      it 'includes the face name in the error message' do\n        expect { subject.boombaz }.to raise_error(NoMethodError, /for an instance of Puppet::Interface/)\n      end\n\n      it 'includes the face version in the error message' do\n        expect { subject.boombaz }.to raise_error(NoMethodError, /for an instance of Puppet::Interface/)\n      end\n    else\n      it 'includes the face name in the error message' do\n        expect { subject.boombaz }.to raise_error(NoMethodError, /#{subject.name}/)\n      end\n\n      it 'includes the face version in the error message' do\n        expect { subject.boombaz }.to raise_error(NoMethodError, /#{subject.version}/)\n      end\n    end\n  end\n\n  it_should_behave_like \"things that declare options\" do\n    def add_options_to(&block)\n      subject.new(:with_options, '0.0.1', &block)\n    end\n  end\n\n  context \"when deprecating a face\" do\n    let(:face) { subject.new(:foo, '0.0.1') }\n    describe \"#deprecate\" do\n      it \"should respond to #deprecate\" do\n        expect(subject.new(:foo, '0.0.1')).to respond_to(:deprecate)\n      end\n\n      it \"should set the deprecated value to true\" do\n        expect(face.deprecated?).to be_falsey\n        face.deprecate\n        expect(face.deprecated?).to be_truthy\n      end\n    end\n\n    describe \"#deprecated?\" do\n      it \"should return a nil (falsey) value by default\" do\n        expect(face.deprecated?).to be_falsey\n      end\n\n      it \"should return true if the face has been deprecated\" do\n        expect(face.deprecated?).to be_falsey\n        face.deprecate\n        expect(face.deprecated?).to be_truthy\n      end\n    end\n  end\n\n  describe \"with face-level display_global_options\" do\n    it \"should not return any action level display_global_options\" do\n      face = subject.new(:with_display_global_options, '0.0.1') do\n        display_global_options \"environment\"\n        action :baz do\n          when_invoked {|_| true }\n          display_global_options \"modulepath\"\n        end\n      end\n      expect(face.display_global_options).to match([\"environment\"])\n    end\n\n    it \"should not fail when a face d_g_o duplicates an action d_g_o\" do\n      expect {\n        subject.new(:action_level_display_global_options, '0.0.1') do\n          action :bar do\n            when_invoked {|_| true }\n            display_global_options \"environment\"\n          end\n          display_global_options \"environment\"\n        end\n      }.to_not raise_error\n    end\n\n    it \"should work when two actions have the same d_g_o\" do\n      face = subject.new(:with_display_global_options, '0.0.1') do\n        action :foo do when_invoked {|_| true} ; display_global_options \"environment\" end\n        action :bar do when_invoked {|_| true} ; display_global_options \"environment\" end\n      end\n      expect(face.get_action(:foo).display_global_options).to match([\"environment\"])\n      expect(face.get_action(:bar).display_global_options).to match([\"environment\"])\n    end\n      \n  end\n  \n  describe \"with inherited display_global_options\" do\n  end\n\n  describe \"with face-level options\" do\n    it \"should not return any action-level options\" do\n      face = subject.new(:with_options, '0.0.1') do\n        option \"--foo\"\n        option \"--bar\"\n        action :baz do\n          when_invoked {|_| true }\n          option \"--quux\"\n        end\n      end\n      expect(face.options).to match_array([:foo, :bar])\n    end\n\n    it \"should fail when a face option duplicates an action option\" do\n      expect {\n        subject.new(:action_level_options, '0.0.1') do\n          action :bar do\n            when_invoked {|_| true }\n            option \"--foo\"\n          end\n          option \"--foo\"\n        end\n      }.to raise_error ArgumentError, /Option foo conflicts with existing option foo on/i\n    end\n\n    it \"should work when two actions have the same option\" do\n      face = subject.new(:with_options, '0.0.1') do\n        action :foo do when_invoked {|_| true } ; option \"--quux\" end\n        action :bar do when_invoked {|_| true } ; option \"--quux\" end\n      end\n\n      expect(face.get_action(:foo).options).to match_array([:quux])\n      expect(face.get_action(:bar).options).to match_array([:quux])\n    end\n\n    it \"should only list options and not aliases\" do\n      face = subject.new(:face_options, '0.0.1') do\n        option \"--bar\", \"-b\", \"--foo-bar\"\n      end\n      expect(face.options).to match_array([:bar])\n    end\n\n  end\n\n  describe \"with inherited options\" do\n    let :parent do\n      parent = Class.new(subject)\n      parent.option(\"--inherited\")\n      parent.action(:parent_action) do when_invoked {|_| true } end\n      parent\n    end\n\n    let :face do\n      face = parent.new(:example, '0.2.1')\n      face.option(\"--local\")\n      face.action(:face_action) do when_invoked {|_| true } end\n      face\n    end\n\n    describe \"#options\" do\n      it \"should list inherited options\" do\n        expect(face.options).to match_array([:inherited, :local])\n      end\n\n      it \"should see all options on face actions\" do\n        expect(face.get_action(:face_action).options).to match_array([:inherited, :local])\n      end\n\n      it \"should see all options on inherited actions accessed on the subclass\" do\n        expect(face.get_action(:parent_action).options).to match_array([:inherited, :local])\n      end\n\n      it \"should not see subclass actions on the parent class\" do\n        expect(parent.options).to match_array([:inherited])\n      end\n\n      it \"should not see subclass actions on actions accessed on the parent class\" do\n        expect(parent.get_action(:parent_action).options).to match_array([:inherited])\n      end\n    end\n\n    describe \"#get_option\" do\n      it \"should return an inherited option object\" do\n        expect(face.get_option(:inherited)).to be_an_instance_of subject::Option\n      end\n    end\n  end\n\n  it_should_behave_like \"documentation on faces\" do\n    subject do\n      Puppet::Interface.new(:face_documentation, '0.0.1')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/modules'\nrequire 'puppet/module_tool/checksums'\n\ndescribe Puppet::Module do\n  include PuppetSpec::Files\n\n  let(:env) { double(\"environment\") }\n  let(:path) { \"/path\" }\n  let(:name) { \"mymod\" }\n  let(:mod) { Puppet::Module.new(name, path, env) }\n\n  before do\n    # This is necessary because of the extra checks we have for the deprecated\n    # 'plugins' directory\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n  end\n\n  it \"should have a class method that returns a named module from a given environment\" do\n    env = Puppet::Node::Environment.create(:myenv, [])\n    expect(env).to receive(:module).with(name).and_return(\"yep\")\n    Puppet.override(:environments => Puppet::Environments::Static.new(env)) do\n      expect(Puppet::Module.find(name, \"myenv\")).to eq(\"yep\")\n    end\n  end\n\n  it \"should return nil if asked for a named module that doesn't exist\" do\n    env = Puppet::Node::Environment.create(:myenv, [])\n    expect(env).to receive(:module).with(name).and_return(nil)\n    Puppet.override(:environments => Puppet::Environments::Static.new(env)) do\n      expect(Puppet::Module.find(name, \"myenv\")).to be_nil\n    end\n  end\n\n  describe \"is_module_directory?\" do\n    let(:first_modulepath) { tmpdir('firstmodules') }\n    let(:not_a_module) { tmpfile('thereisnomodule', first_modulepath) }\n\n    it \"should return false for a non-directory\" do\n      expect(Puppet::Module.is_module_directory?('thereisnomodule', first_modulepath)).to be_falsey\n    end\n\n    it \"should return true for a well named directories\" do\n      PuppetSpec::Modules.generate_files('foo', first_modulepath)\n      PuppetSpec::Modules.generate_files('foo2', first_modulepath)\n      PuppetSpec::Modules.generate_files('foo_bar', first_modulepath)\n      expect(Puppet::Module.is_module_directory?('foo', first_modulepath)).to be_truthy\n      expect(Puppet::Module.is_module_directory?('foo2', first_modulepath)).to be_truthy\n      expect(Puppet::Module.is_module_directory?('foo_bar', first_modulepath)).to be_truthy\n    end\n\n    it \"should return false for badly named directories\" do\n      PuppetSpec::Modules.generate_files('foo=bar', first_modulepath)\n      PuppetSpec::Modules.generate_files('.foo', first_modulepath)\n      expect(Puppet::Module.is_module_directory?('foo=bar', first_modulepath)).to be_falsey\n      expect(Puppet::Module.is_module_directory?('.foo', first_modulepath)).to be_falsey\n    end\n  end\n\n  describe \"is_module_directory_name?\" do\n    it \"should return true for a valid directory module name\" do\n      expect(Puppet::Module.is_module_directory_name?('foo')).to be_truthy\n      expect(Puppet::Module.is_module_directory_name?('foo2')).to be_truthy\n      expect(Puppet::Module.is_module_directory_name?('foo_bar')).to be_truthy\n    end\n\n    it \"should return false for badly formed directory module names\" do\n      expect(Puppet::Module.is_module_directory_name?('foo-bar')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('foo=bar')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('foo bar')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('foo.bar')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('-foo')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('foo-')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('foo--bar')).to be_falsey\n      expect(Puppet::Module.is_module_directory_name?('.foo')).to be_falsey\n    end\n  end\n\n  describe \"is_module_namespaced_name?\" do\n    it \"should return true for a valid namespaced module name\" do\n      expect(Puppet::Module.is_module_namespaced_name?('foo-bar')).to be_truthy\n    end\n\n    it \"should return false for badly formed namespaced module names\" do\n      expect(Puppet::Module.is_module_namespaced_name?('foo')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('.foo-bar')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo2')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo_bar')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo=bar')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo bar')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo.bar')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('-foo')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo-')).to be_falsey\n      expect(Puppet::Module.is_module_namespaced_name?('foo--bar')).to be_falsey\n    end\n  end\n\n  describe \"attributes\" do\n    it \"should support a 'version' attribute\" do\n      mod.version = 1.09\n      expect(mod.version).to eq(1.09)\n    end\n\n    it \"should support a 'source' attribute\" do\n      mod.source = \"http://foo/bar\"\n      expect(mod.source).to eq(\"http://foo/bar\")\n    end\n\n    it \"should support a 'project_page' attribute\" do\n      mod.project_page = \"http://foo/bar\"\n      expect(mod.project_page).to eq(\"http://foo/bar\")\n    end\n\n    it \"should support an 'author' attribute\" do\n      mod.author = \"Luke Kanies <luke@madstop.com>\"\n      expect(mod.author).to eq(\"Luke Kanies <luke@madstop.com>\")\n    end\n\n    it \"should support a 'license' attribute\" do\n      mod.license = \"GPL2\"\n      expect(mod.license).to eq(\"GPL2\")\n    end\n\n    it \"should support a 'summary' attribute\" do\n      mod.summary = \"GPL2\"\n      expect(mod.summary).to eq(\"GPL2\")\n    end\n\n    it \"should support a 'description' attribute\" do\n      mod.description = \"GPL2\"\n      expect(mod.description).to eq(\"GPL2\")\n    end\n  end\n\n  describe \"when finding unmet dependencies\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_call_original\n      @modpath = tmpdir('modpath')\n      Puppet.settings[:modulepath] = @modpath\n    end\n\n    it \"should resolve module dependencies using forge names\" do\n      parent = PuppetSpec::Modules.create(\n        'parent',\n        @modpath,\n        :metadata => {\n          :author => 'foo',\n          :dependencies => [{\n            \"name\" => \"foo/child\"\n          }]\n        },\n        :environment => env\n      )\n      child = PuppetSpec::Modules.create(\n        'child',\n        @modpath,\n        :metadata => {\n          :author => 'foo',\n          :dependencies => []\n        },\n        :environment => env\n      )\n\n      expect(env).to receive(:module_by_forge_name).with('foo/child').and_return(child)\n\n      expect(parent.unmet_dependencies).to eq([])\n    end\n\n    it \"should list modules that are missing\" do\n      mod = PuppetSpec::Modules.create(\n        'needy',\n        @modpath,\n        :metadata => {\n          :dependencies => [{\n            \"version_requirement\" => \">= 2.2.0\",\n            \"name\" => \"baz/foobar\"\n          }]\n        },\n        :environment => env\n      )\n\n      expect(env).to receive(:module_by_forge_name).with('baz/foobar').and_return(nil)\n\n      expect(mod.unmet_dependencies).to eq([{\n        :reason => :missing,\n        :name   => \"baz/foobar\",\n        :version_constraint => \">= 2.2.0\",\n        :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' },\n        :mod_details => { :installed_version => nil }\n      }])\n    end\n\n    it \"should list modules that are missing and have invalid names\" do\n      mod = PuppetSpec::Modules.create(\n        'needy',\n        @modpath,\n        :metadata => {\n          :dependencies => [{\n            \"version_requirement\" => \">= 2.2.0\",\n            \"name\" => \"baz/foobar=bar\"\n          }]\n        },\n        :environment => env\n      )\n\n      expect(env).to receive(:module_by_forge_name).with('baz/foobar=bar').and_return(nil)\n\n      expect(mod.unmet_dependencies).to eq([{\n        :reason => :missing,\n        :name   => \"baz/foobar=bar\",\n        :version_constraint => \">= 2.2.0\",\n        :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' },\n        :mod_details => { :installed_version => nil }\n      }])\n    end\n\n    it \"should list modules with unmet version requirement\" do\n      env = Puppet::Node::Environment.create(:testing, [@modpath])\n\n      ['test_gte_req', 'test_specific_req', 'foobar'].each do |mod_name|\n        mod_dir = \"#{@modpath}/#{mod_name}\"\n        metadata_file = \"#{mod_dir}/metadata.json\"\n        allow(Puppet::FileSystem).to receive(:exist?).with(metadata_file).and_return(true)\n      end\n      mod = PuppetSpec::Modules.create(\n        'test_gte_req',\n        @modpath,\n        :metadata => {\n          :dependencies => [{\n            \"version_requirement\" => \">= 2.2.0\",\n            \"name\" => \"baz/foobar\"\n          }]\n        },\n        :environment => env\n      )\n      mod2 = PuppetSpec::Modules.create(\n        'test_specific_req',\n        @modpath,\n        :metadata => {\n          :dependencies => [{\n            \"version_requirement\" => \"1.0.0\",\n            \"name\" => \"baz/foobar\"\n          }]\n        },\n        :environment => env\n      )\n\n      PuppetSpec::Modules.create(\n        'foobar',\n        @modpath,\n        :metadata => { :version => '2.0.0', :author  => 'baz' },\n        :environment => env\n      )\n\n      expect(mod.unmet_dependencies).to eq([{\n        :reason => :version_mismatch,\n        :name   => \"baz/foobar\",\n        :version_constraint => \">= 2.2.0\",\n        :parent => { :version => \"v9.9.9\", :name => \"puppetlabs/test_gte_req\" },\n        :mod_details => { :installed_version => \"2.0.0\" }\n      }])\n\n      expect(mod2.unmet_dependencies).to eq([{\n        :reason => :version_mismatch,\n        :name   => \"baz/foobar\",\n        :version_constraint => \"v1.0.0\",\n        :parent => { :version => \"v9.9.9\", :name => \"puppetlabs/test_specific_req\" },\n        :mod_details => { :installed_version => \"2.0.0\" }\n      }])\n\n    end\n\n    it \"should consider a dependency without a version requirement to be satisfied\" do\n      env = Puppet::Node::Environment.create(:testing, [@modpath])\n\n      mod = PuppetSpec::Modules.create(\n        'foobar',\n        @modpath,\n        :metadata => {\n          :dependencies => [{\n            \"name\" => \"baz/foobar\"\n          }]\n        },\n        :environment => env\n      )\n      PuppetSpec::Modules.create(\n        'foobar',\n        @modpath,\n        :metadata => {\n          :version => '2.0.0',\n          :author  => 'baz'\n        },\n        :environment => env\n      )\n\n      expect(mod.unmet_dependencies).to be_empty\n    end\n\n    it \"should consider a dependency without a semantic version to be unmet\" do\n      env = Puppet::Node::Environment.create(:testing, [@modpath])\n\n      mod = PuppetSpec::Modules.create(\n        'foobar',\n        @modpath,\n        :metadata => {\n          :dependencies => [{\n            \"name\" => \"baz/foobar\"\n          }]\n        },\n        :environment => env\n      )\n      PuppetSpec::Modules.create(\n        'foobar',\n        @modpath,\n        :metadata => {\n          :version => '5.1',\n          :author  => 'baz'\n        },\n        :environment => env\n      )\n\n      expect(mod.unmet_dependencies).to eq([{\n        :reason => :non_semantic_version,\n        :parent => { :version => \"v9.9.9\", :name => \"puppetlabs/foobar\" },\n        :mod_details => { :installed_version => \"5.1\" },\n        :name => \"baz/foobar\",\n        :version_constraint => \">= 0.0.0\"\n      }])\n    end\n\n    it \"should have valid dependencies when no dependencies have been specified\" do\n      mod = PuppetSpec::Modules.create(\n        'foobar',\n        @modpath,\n        :metadata => {\n          :dependencies => []\n        }\n      )\n\n      expect(mod.unmet_dependencies).to eq([])\n    end\n\n    it \"should throw an error if invalid dependencies are specified\" do\n      expect {\n        PuppetSpec::Modules.create(\n          'foobar',\n          @modpath,\n          :metadata => {\n            :dependencies => \"\"\n          }\n        )\n      }.to raise_error(\n        Puppet::Module::MissingMetadata,\n        /dependencies in the file metadata.json of the module foobar must be an array, not: ''/)\n    end\n\n    it \"should only list unmet dependencies\" do\n      env = Puppet::Node::Environment.create(:testing, [@modpath])\n\n      mod = PuppetSpec::Modules.create(\n        name,\n        @modpath,\n        :metadata => {\n          :dependencies => [\n            {\n              \"version_requirement\" => \">= 2.2.0\",\n              \"name\" => \"baz/satisfied\"\n            },\n            {\n              \"version_requirement\" => \">= 2.2.0\",\n              \"name\" => \"baz/notsatisfied\"\n            }\n          ]\n        },\n        :environment => env\n      )\n      PuppetSpec::Modules.create(\n        'satisfied',\n        @modpath,\n        :metadata => {\n          :version => '3.3.0',\n          :author  => 'baz'\n        },\n        :environment => env\n      )\n\n      expect(mod.unmet_dependencies).to eq([{\n        :reason => :missing,\n        :mod_details => { :installed_version => nil },\n        :parent => { :version => \"v9.9.9\", :name => \"puppetlabs/#{name}\" },\n        :name => \"baz/notsatisfied\",\n        :version_constraint => \">= 2.2.0\"\n      }])\n    end\n\n    it \"should be empty when all dependencies are met\" do\n      env = Puppet::Node::Environment.create(:testing, [@modpath])\n\n      mod = PuppetSpec::Modules.create(\n        'mymod2',\n        @modpath,\n        :metadata => {\n          :dependencies => [\n            {\n              \"version_requirement\" => \">= 2.2.0\",\n              \"name\" => \"baz/satisfied\"\n            },\n            {\n              \"version_requirement\" => \"< 2.2.0\",\n              \"name\" => \"baz/alsosatisfied\"\n            }\n          ]\n        },\n        :environment => env\n      )\n      PuppetSpec::Modules.create(\n        'satisfied',\n        @modpath,\n        :metadata => {\n          :version => '3.3.0',\n          :author  => 'baz'\n        },\n        :environment => env\n      )\n      PuppetSpec::Modules.create(\n        'alsosatisfied',\n        @modpath,\n        :metadata => {\n          :version => '2.1.0',\n          :author  => 'baz'\n        },\n        :environment => env\n      )\n\n      expect(mod.unmet_dependencies).to be_empty\n    end\n  end\n\n  describe \"when managing supported platforms\" do\n    it \"should support specifying a supported platform\" do\n      mod.supports \"solaris\"\n    end\n\n    it \"should support specifying a supported platform and version\" do\n      mod.supports \"solaris\", 1.0\n    end\n  end\n\n  it \"should return nil if asked for a module whose name is 'nil'\" do\n    expect(Puppet::Module.find(nil, \"myenv\")).to be_nil\n  end\n\n  it \"should provide support for logging\" do\n    expect(Puppet::Module.ancestors).to be_include(Puppet::Util::Logging)\n  end\n\n  it \"should be able to be converted to a string\" do\n    expect(mod.to_s).to eq(\"Module #{name}(#{path})\")\n  end\n\n  it \"should fail if its name is not alphanumeric\" do\n    expect { Puppet::Module.new(\".something\", \"/path\", env) }.to raise_error(Puppet::Module::InvalidName)\n  end\n\n  it \"should require a name at initialization\" do\n    expect { Puppet::Module.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should accept an environment at initialization\" do\n    expect(Puppet::Module.new(\"foo\", \"/path\", env).environment).to eq(env)\n  end\n\n  describe '#modulepath' do\n    it \"should return the directory the module is installed in, if a path exists\" do\n      mod = Puppet::Module.new(\"foo\", \"/a/foo\", env)\n      expect(mod.modulepath).to eq('/a')\n    end\n  end\n\n  [:plugins, :pluginfacts, :templates, :files, :manifests, :scripts].each do |filetype|\n    case filetype\n      when :plugins\n        dirname = \"lib\"\n      when :pluginfacts\n        dirname = \"facts.d\"\n      else\n        dirname = filetype.to_s\n    end\n\n    it \"should be able to return individual #{filetype}\" do\n      module_file = File.join(path, dirname, \"my/file\")\n      expect(Puppet::FileSystem).to receive(:exist?).with(module_file).and_return(true)\n      expect(mod.send(filetype.to_s.sub(/s$/, ''), \"my/file\")).to eq(module_file)\n    end\n\n    it \"should consider #{filetype} to be present if their base directory exists\" do\n      module_file = File.join(path, dirname)\n      expect(Puppet::FileSystem).to receive(:exist?).with(module_file).and_return(true)\n      expect(mod.send(filetype.to_s + \"?\")).to be_truthy\n    end\n\n    it \"should consider #{filetype} to be absent if their base directory does not exist\" do\n      module_file = File.join(path, dirname)\n      expect(Puppet::FileSystem).to receive(:exist?).with(module_file).and_return(false)\n      expect(mod.send(filetype.to_s + \"?\")).to be_falsey\n    end\n\n    it \"should return nil if asked to return individual #{filetype} that don't exist\" do\n      module_file = File.join(path, dirname, \"my/file\")\n      expect(Puppet::FileSystem).to receive(:exist?).with(module_file).and_return(false)\n      expect(mod.send(filetype.to_s.sub(/s$/, ''), \"my/file\")).to be_nil\n    end\n\n    it \"should return the base directory if asked for a nil path\" do\n      base = File.join(path, dirname)\n      expect(Puppet::FileSystem).to receive(:exist?).with(base).and_return(true)\n      expect(mod.send(filetype.to_s.sub(/s$/, ''), nil)).to eq(base)\n    end\n  end\n\n  it \"should return the path to the plugin directory\" do\n    expect(mod.plugin_directory).to eq(File.join(path, \"lib\"))\n  end\n\n  it \"should return the path to the tasks directory\" do\n    expect(mod.tasks_directory).to eq(File.join(path, \"tasks\"))\n  end\n\n  it \"should return the path to the plans directory\" do\n    expect(mod.plans_directory).to eq(File.join(path, \"plans\"))\n  end\n\n  describe \"when finding tasks\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_call_original\n      @modpath = tmpdir('modpath')\n      Puppet.settings[:modulepath] = @modpath\n    end\n\n    it \"should have an empty array for the tasks when the tasks directory does not exist\" do\n      mod = PuppetSpec::Modules.create('tasks_test_nodir', @modpath, :environment => env)\n      expect(mod.tasks).to eq([])\n    end\n\n    it \"should have an empty array for the tasks when the tasks directory does exist and is empty\" do\n      mod = PuppetSpec::Modules.create('tasks_test_empty', @modpath, {:environment => env,\n                                                                      :tasks => []})\n      expect(mod.tasks).to eq([])\n    end\n\n    it \"should list the expected tasks when the required files exist\" do\n      fake_tasks = [['task1'], ['task2.sh', 'task2.json']]\n      mod = PuppetSpec::Modules.create('tasks_smoke', @modpath, {:environment => env,\n                                                                 :tasks => fake_tasks})\n\n      expect(mod.tasks.count).to eq(2)\n      expect(mod.tasks.map{|t| t.name}.sort).to eq(['tasks_smoke::task1', 'tasks_smoke::task2'])\n      expect(mod.tasks.map{|t| t.class}).to eq([Puppet::Module::Task] * 2)\n    end\n\n    it \"should be able to find individual task files when they exist\" do\n      task_exe = 'stateskatetask.stk'\n      mod = PuppetSpec::Modules.create('task_file_smoke', @modpath, {:environment => env,\n                                                                     :tasks => [[task_exe]]})\n\n      expect(mod.task_file(task_exe)).to eq(\"#{mod.path}/tasks/#{task_exe}\")\n    end\n\n    it \"should list files from the scripts directory if required by the task\" do\n      mod         = 'loads_scripts'\n      task_dep    = 'myscript.sh'\n      script_ref  = \"#{mod}/scripts/#{task_dep}\"\n      task_json   = JSON.generate({'files' => [script_ref]})\n      task        = [['task', { name: 'task.json', content: task_json }]]\n      mod         = PuppetSpec::Modules.create(mod, @modpath, {:environment => env,\n                                                       :scripts     => [task_dep],\n                                                       :tasks       => task})\n\n      expect(mod.tasks.first.files).to include({'name' => script_ref,\n                                                'path' => /#{script_ref}/})\n    end\n\n    it \"should return nil when asked for an individual task file if it does not exist\" do\n      mod = PuppetSpec::Modules.create('task_file_neg', @modpath, {:environment => env,\n                                                                   :tasks => []})\n      expect(mod.task_file('nosuchtask')).to be_nil\n    end\n\n    describe \"does the task finding\" do\n      let(:mod_name) { 'tasks_test_lazy' }\n      let(:mod_tasks_dir) { File.join(@modpath, mod_name, 'tasks') }\n\n      it \"after the module is initialized\" do\n        expect(Puppet::FileSystem).not_to receive(:exist?).with(mod_tasks_dir)\n        expect(Puppet::Module::Task).not_to receive(:tasks_in_module)\n        Puppet::Module.new(mod_name, @modpath, env)\n      end\n\n      it \"when the tasks method is called\" do\n        expect(Puppet::Module::Task).to receive(:tasks_in_module)\n        mod = PuppetSpec::Modules.create(mod_name, @modpath, {:environment => env,\n                                                              :tasks => [['itascanstaccatotask']]})\n        mod.tasks\n      end\n\n      it \"only once for the lifetime of the module object\" do\n        expect(Dir).to receive(:glob).with(\"#{mod_tasks_dir}/*\").once.and_return(['allalaskataskattacktactics'])\n        mod = PuppetSpec::Modules.create(mod_name, @modpath, {:environment => env,\n                                                              :tasks => []})\n        mod.tasks\n        mod.tasks\n      end\n    end\n  end\n\n  describe \"when finding plans\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_call_original\n      @modpath = tmpdir('modpath')\n      Puppet.settings[:modulepath] = @modpath\n    end\n\n    it \"should have an empty array for the plans when the plans directory does not exist\" do\n      mod = PuppetSpec::Modules.create('plans_test_nodir', @modpath, :environment => env)\n      expect(mod.plans).to eq([])\n    end\n\n    it \"should have an empty array for the plans when the plans directory does exist and is empty\" do\n      mod = PuppetSpec::Modules.create('plans_test_empty', @modpath, {:environment => env,\n                                                                      :plans => []})\n      expect(mod.plans).to eq([])\n    end\n\n    it \"should list the expected plans when the required files exist\" do\n      fake_plans = ['plan1.pp', 'plan2.yaml']\n      mod = PuppetSpec::Modules.create('plans_smoke', @modpath, {:environment => env,\n                                                                 :plans => fake_plans})\n\n      expect(mod.plans.count).to eq(2)\n      expect(mod.plans.map{|t| t.name}.sort).to eq(['plans_smoke::plan1', 'plans_smoke::plan2'])\n      expect(mod.plans.map{|t| t.class}).to eq([Puppet::Module::Plan] * 2)\n    end\n\n    it \"should be able to find individual plan files when they exist\" do\n      plan_exe = 'stateskateplan.pp'\n      mod = PuppetSpec::Modules.create('plan_file_smoke', @modpath, {:environment => env,\n                                                                     :plans => [plan_exe]})\n\n      expect(mod.plan_file(plan_exe)).to eq(\"#{mod.path}/plans/#{plan_exe}\")\n    end\n\n    it \"should return nil when asked for an individual plan file if it does not exist\" do\n      mod = PuppetSpec::Modules.create('plan_file_neg', @modpath, {:environment => env,\n                                                                   :plans => []})\n      expect(mod.plan_file('nosuchplan')).to be_nil\n    end\n\n    describe \"does the plan finding\" do\n      let(:mod_name) { 'plans_test_lazy' }\n      let(:mod_plans_dir) { File.join(@modpath, mod_name, 'plans') }\n\n      it \"after the module is initialized\" do\n        expect(Puppet::FileSystem).not_to receive(:exist?).with(mod_plans_dir)\n        expect(Puppet::Module::Plan).not_to receive(:plans_in_module)\n        Puppet::Module.new(mod_name, @modpath, env)\n      end\n\n      it \"when the plans method is called\" do\n        expect(Puppet::Module::Plan).to receive(:plans_in_module)\n        mod = PuppetSpec::Modules.create(mod_name, @modpath, {:environment => env,\n                                                              :plans => ['itascanstaccatoplan.yaml']})\n        mod.plans\n      end\n\n      it \"only once for the lifetime of the module object\" do\n        expect(Dir).to receive(:glob).with(\"#{mod_plans_dir}/*\").once.and_return(['allalaskaplanattacktactics'])\n        mod = PuppetSpec::Modules.create(mod_name, @modpath, {:environment => env,\n                                                              :plans => []})\n        mod.plans\n        mod.plans\n      end\n    end\n  end\nend\n\ndescribe Puppet::Module, \"when finding matching manifests\" do\n  before do\n    @mod = Puppet::Module.new(\"mymod\", \"/a\", double(\"environment\"))\n    @pq_glob_with_extension = \"yay/*.xx\"\n    @fq_glob_with_extension = \"/a/manifests/#{@pq_glob_with_extension}\"\n  end\n\n  it \"should return all manifests matching the glob pattern\" do\n    expect(Dir).to receive(:glob).with(@fq_glob_with_extension).and_return(%w{foo bar})\n    allow(FileTest).to receive(:directory?).and_return(false)\n\n    expect(@mod.match_manifests(@pq_glob_with_extension)).to eq(%w{foo bar})\n  end\n\n  it \"should not return directories\" do\n    expect(Dir).to receive(:glob).with(@fq_glob_with_extension).and_return(%w{foo bar})\n\n    expect(FileTest).to receive(:directory?).with(\"foo\").and_return(false)\n    expect(FileTest).to receive(:directory?).with(\"bar\").and_return(true)\n    expect(@mod.match_manifests(@pq_glob_with_extension)).to eq(%w{foo})\n  end\n\n  it \"should default to the 'init' file if no glob pattern is specified\" do\n    expect(Puppet::FileSystem).to receive(:exist?).with(\"/a/manifests/init.pp\").and_return(true)\n\n    expect(@mod.match_manifests(nil)).to eq(%w{/a/manifests/init.pp})\n  end\n\n  it \"should return all manifests matching the glob pattern in all existing paths\" do\n    expect(Dir).to receive(:glob).with(@fq_glob_with_extension).and_return(%w{a b})\n\n    expect(@mod.match_manifests(@pq_glob_with_extension)).to eq(%w{a b})\n  end\n\n  it \"should match the glob pattern plus '.pp' if no extension is specified\" do\n    expect(Dir).to receive(:glob).with(\"/a/manifests/yay/foo.pp\").and_return(%w{yay})\n\n    expect(@mod.match_manifests(\"yay/foo\")).to eq(%w{yay})\n  end\n\n  it \"should return an empty array if no manifests matched\" do\n    expect(Dir).to receive(:glob).with(@fq_glob_with_extension).and_return([])\n\n    expect(@mod.match_manifests(@pq_glob_with_extension)).to eq([])\n  end\n\n  it \"should raise an error if the pattern tries to leave the manifest directory\" do\n    expect do\n      @mod.match_manifests(\"something/../../*\")\n    end.to raise_error(Puppet::Module::InvalidFilePattern, 'The pattern \"something/../../*\" to find manifests in the module \"mymod\" is invalid and potentially unsafe.')\n  end\nend\n\ndescribe Puppet::Module do\n  include PuppetSpec::Files\n\n  let!(:modpath) do\n    path = tmpdir('modpath')\n    PuppetSpec::Modules.create('mymod', path)\n    path\n  end\n\n  let!(:mymodpath) { File.join(modpath, 'mymod') }\n\n  let!(:mymod_metadata) { File.join(mymodpath, 'metadata.json') }\n\n  let(:mymod) { Puppet::Module.new('mymod', mymodpath, nil) }\n\n  it \"should use 'License' in its current path as its metadata file\" do\n    expect(mymod.license_file).to eq(\"#{modpath}/mymod/License\")\n  end\n\n  it \"should cache the license file\" do\n    expect(mymod).to receive(:path).once.and_return(nil)\n    mymod.license_file\n    mymod.license_file\n  end\n\n  it \"should use 'metadata.json' in its current path as its metadata file\" do\n    expect(mymod_metadata).to eq(\"#{modpath}/mymod/metadata.json\")\n  end\n\n  it \"should not have metadata if it has a metadata file and its data is valid but empty json hash\" do\n    allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(\"{}\")\n\n    expect(mymod).not_to be_has_metadata\n  end\n\n  it \"should not have metadata if it has a metadata file and its data is empty\" do\n    Puppet[:strict] = :warning\n    allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(\"\")\n\n    expect(mymod).not_to be_has_metadata\n  end\n\n  it \"should not have metadata if has a metadata file and its data is invalid\" do\n    Puppet[:strict] = :warning\n    allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(\"This is some invalid json.\\n\")\n    expect(mymod).not_to be_has_metadata\n  end\n\n  it \"should know if it is missing a metadata file\" do\n    allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n\n    expect(mymod).not_to be_has_metadata\n  end\n\n  it \"should be able to parse its metadata file\" do\n    expect(mymod).to respond_to(:load_metadata)\n  end\n\n  it \"should parse its metadata file on initialization if it is present\" do\n    expect_any_instance_of(Puppet::Module).to receive(:load_metadata)\n\n    Puppet::Module.new(\"yay\", \"/path\", double(\"env\"))\n  end\n\n  describe 'when --strict is warning' do\n    before :each do\n      Puppet[:strict] = :warning\n    end\n\n    it \"should warn about a failure to parse\" do\n      allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(my_fixture('trailing-comma.json'))\n\n      expect(mymod.has_metadata?).to be_falsey\n      expect(@logs).to have_matching_log(/mymod has an invalid and unparsable metadata\\.json file/)\n    end\n  end\n\n    describe 'when --strict is off' do\n      before :each do\n        Puppet[:strict] = :off\n      end\n\n      it \"should not warn about a failure to parse\" do\n        allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(my_fixture('trailing-comma.json'))\n\n        expect(mymod.has_metadata?).to be_falsey\n        expect(@logs).to_not have_matching_log(/mymod has an invalid and unparsable metadata\\.json file.*/)\n      end\n\n      it \"should log debug output about a failure to parse when --debug is on\" do\n        Puppet[:log_level] = :debug\n        allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(my_fixture('trailing-comma.json'))\n\n        expect(mymod.has_metadata?).to be_falsey\n        expect(@logs).to have_matching_log(/mymod has an invalid and unparsable metadata\\.json file.*/)\n      end\n    end\n\n    describe 'when --strict is error' do\n      before :each do\n        Puppet[:strict] = :error\n      end\n\n      it \"should fail on a failure to parse\" do\n        allow(File).to receive(:read).with(mymod_metadata, {:encoding => 'utf-8'}).and_return(my_fixture('trailing-comma.json'))\n\n        expect do\n        expect(mymod.has_metadata?).to be_falsey\n        end.to raise_error(/mymod has an invalid and unparsable metadata\\.json file/)\n      end\n    end\n\n  def a_module_with_metadata(data)\n    allow(File).to receive(:read).with(\"/path/metadata.json\", {:encoding => 'utf-8'}).and_return(data.to_json)\n    Puppet::Module.new(\"foo\", \"/path\", double(\"env\"))\n  end\n\n  describe \"when loading the metadata file\" do\n    let(:data) do\n      {\n        :license       => \"GPL2\",\n        :author        => \"luke\",\n        :version       => \"1.0\",\n        :source        => \"http://foo/\",\n        :dependencies  => []\n      }\n    end\n\n    %w{source author version license}.each do |attr|\n      it \"should set #{attr} if present in the metadata file\" do\n        mod = a_module_with_metadata(data)\n        expect(mod.send(attr)).to eq(data[attr.to_sym])\n      end\n\n      it \"should fail if #{attr} is not present in the metadata file\" do\n        data.delete(attr.to_sym)\n        expect { a_module_with_metadata(data) }.to raise_error(\n          Puppet::Module::MissingMetadata,\n          \"No #{attr} module metadata provided for foo\"\n        )\n      end\n    end\n  end\n\n  describe \"when loading the metadata file from disk\" do\n    it \"should properly parse utf-8 contents\" do\n      rune_utf8 = \"\\u16A0\\u16C7\\u16BB\" # ᚠᛇᚻ\n      metadata_json = tmpfile('metadata.json')\n      File.open(metadata_json, 'w:UTF-8') do |file|\n        file.puts <<-EOF\n  {\n    \"license\" : \"GPL2\",\n    \"author\" : \"#{rune_utf8}\",\n    \"version\" : \"1.0\",\n    \"source\" : \"http://foo/\",\n    \"dependencies\" : []\n  }\n        EOF\n      end\n\n      allow_any_instance_of(Puppet::Module).to receive(:metadata_file).and_return(metadata_json)\n      mod = Puppet::Module.new('foo', '/path', double('env'))\n\n      mod.load_metadata\n      expect(mod.author).to eq(rune_utf8)\n    end\n  end\n\n  it \"should be able to tell if there are local changes\" do\n    modpath = tmpdir('modpath')\n    foo_checksum = 'acbd18db4cc2f85cedef654fccc4a4d8'\n    checksummed_module = PuppetSpec::Modules.create(\n      'changed',\n      modpath,\n      :metadata => {\n        :checksums => {\n          \"foo\" => foo_checksum,\n        }\n      }\n    )\n\n    foo_path = Pathname.new(File.join(checksummed_module.path, 'foo'))\n\n    IO.binwrite(foo_path, 'notfoo')\n    expect(Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path)).not_to eq(foo_checksum)\n\n    IO.binwrite(foo_path, 'foo')\n    expect(Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path)).to eq(foo_checksum)\n  end\n\n  it \"should know what other modules require it\" do\n    env = Puppet::Node::Environment.create(:testing, [modpath])\n\n    dependable = PuppetSpec::Modules.create(\n      'dependable',\n      modpath,\n      :metadata => {:author => 'puppetlabs'},\n      :environment => env\n    )\n    PuppetSpec::Modules.create(\n      'needy',\n      modpath,\n      :metadata => {\n        :author => 'beggar',\n        :dependencies => [{\n            \"version_requirement\" => \">= 2.2.0\",\n            \"name\" => \"puppetlabs/dependable\"\n        }]\n      },\n      :environment => env\n    )\n    PuppetSpec::Modules.create(\n      'wantit',\n      modpath,\n      :metadata => {\n        :author => 'spoiled',\n        :dependencies => [{\n            \"version_requirement\" => \"< 5.0.0\",\n            \"name\" => \"puppetlabs/dependable\"\n        }]\n      },\n      :environment => env\n    )\n    expect(dependable.required_by).to match_array([\n      {\n        \"name\"    => \"beggar/needy\",\n        \"version\" => \"9.9.9\",\n        \"version_requirement\" => \">= 2.2.0\"\n      },\n      {\n        \"name\"    => \"spoiled/wantit\",\n        \"version\" => \"9.9.9\",\n        \"version_requirement\" => \"< 5.0.0\"\n      }\n    ])\n  end\n\n  context 'when parsing VersionRange' do\n    let(:logs) { [] }\n    let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }\n\n    it 'can parse a strict range' do\n      expect(Puppet::Module.parse_range('>=1.0.0').include?(SemanticPuppet::Version.parse('1.0.1-rc1'))).to be_falsey\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/application_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool'\n\ndescribe Puppet::ModuleTool::Applications::Application do\n  describe 'app' do\n\n    good_versions = %w{ 1.2.4 0.0.1 0.0.0 0.0.2-git-8-g3d316d1 0.0.3-b1 10.100.10000\n                         0.1.2-rc1 0.1.2-dev-1 0.1.2-svn12345 0.1.2-3 }\n    bad_versions = %w{ 0.1 0 0.1.2.3 dev 0.1.2beta }\n\n    let :app do Class.new(described_class).new end\n\n    good_versions.each do |ver|\n      it \"should accept version string #{ver}\" do\n        app.parse_filename(\"puppetlabs-ntp-#{ver}\")\n      end\n    end\n\n    bad_versions.each do |ver|\n      it \"should not accept version string #{ver}\" do\n        expect { app.parse_filename(\"puppetlabs-ntp-#{ver}\") }.to raise_error(ArgumentError, /(Invalid version format|Could not parse filename)/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/applications/checksummer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool/applications'\nrequire 'puppet_spec/files'\nrequire 'pathname'\n\ndescribe Puppet::ModuleTool::Applications::Checksummer do\n  let(:tmpdir) do\n    Pathname.new(PuppetSpec::Files.tmpdir('checksummer'))\n  end\n\n  let(:checksums) { Puppet::ModuleTool::Checksums.new(tmpdir).data }\n\n  subject do\n    described_class.run(tmpdir)\n  end\n\n  before do\n    File.open(tmpdir + 'README', 'w') { |f| f.puts \"This is a README!\" }\n    File.open(tmpdir + 'CHANGES', 'w') { |f| f.puts \"This is a changelog!\" }\n    File.open(tmpdir + 'DELETEME', 'w') { |f| f.puts \"I've got a really good feeling about this!\" }\n    Dir.mkdir(tmpdir + 'pkg')\n    File.open(tmpdir + 'pkg' + 'build-artifact', 'w') { |f| f.puts \"I'm unimportant!\" }\n    File.open(tmpdir + 'metadata.json', 'w') { |f| f.puts '{\"name\": \"package-name\", \"version\": \"1.0.0\"}' }\n    File.open(tmpdir + 'checksums.json', 'w') { |f| f.puts '{}' }\n  end\n\n  context 'with checksums.json' do\n    before do\n      File.open(tmpdir + 'checksums.json', 'w') { |f| f.puts checksums.to_json }\n      File.open(tmpdir + 'CHANGES', 'w') { |f| f.puts \"This is a changed log!\" }\n      File.open(tmpdir + 'pkg' + 'build-artifact', 'w') { |f| f.puts \"I'm still unimportant!\" }\n      (tmpdir + 'DELETEME').unlink\n    end\n\n    it 'reports changed files' do\n      expect(subject).to include 'CHANGES'\n    end\n\n    it 'reports removed files' do\n      expect(subject).to include 'DELETEME'\n    end\n\n    it 'does not report unchanged files' do\n      expect(subject).to_not include 'README'\n    end\n\n    it 'does not report build artifacts' do\n      expect(subject).to_not include 'pkg/build-artifact'\n    end\n\n    it 'does not report checksums.json' do\n      expect(subject).to_not include 'checksums.json'\n    end\n  end\n\n  context 'without checksums.json' do\n    context 'but with metadata.json containing checksums' do\n      before do\n        (tmpdir + 'checksums.json').unlink\n        File.open(tmpdir + 'metadata.json', 'w') { |f| f.puts \"{\\\"checksums\\\":#{checksums.to_json}}\" }\n        File.open(tmpdir + 'CHANGES', 'w') { |f| f.puts \"This is a changed log!\" }\n        File.open(tmpdir + 'pkg' + 'build-artifact', 'w') { |f| f.puts \"I'm still unimportant!\" }\n        (tmpdir + 'DELETEME').unlink\n      end\n\n      it 'reports changed files' do\n        expect(subject).to include 'CHANGES'\n      end\n\n      it 'reports removed files' do\n        expect(subject).to include 'DELETEME'\n      end\n\n      it 'does not report unchanged files' do\n        expect(subject).to_not include 'README'\n      end\n\n      it 'does not report build artifacts' do\n        expect(subject).to_not include 'pkg/build-artifact'\n      end\n\n      it 'does not report checksums.json' do\n        expect(subject).to_not include 'checksums.json'\n      end\n    end\n\n    context 'and with metadata.json that does not contain checksums' do\n      before do\n        (tmpdir + 'checksums.json').unlink\n        File.open(tmpdir + 'CHANGES', 'w') { |f| f.puts \"This is a changed log!\" }\n        File.open(tmpdir + 'pkg' + 'build-artifact', 'w') { |f| f.puts \"I'm still unimportant!\" }\n        (tmpdir + 'DELETEME').unlink\n      end\n\n      it 'fails' do\n        expect { subject }.to raise_error(ArgumentError, 'No file containing checksums found.')\n      end\n    end\n\n    context 'and without metadata.json' do\n      before do\n        (tmpdir + 'checksums.json').unlink\n        (tmpdir + 'metadata.json').unlink\n\n        File.open(tmpdir + 'CHANGES', 'w') { |f| f.puts \"This is a changed log!\" }\n        File.open(tmpdir + 'pkg' + 'build-artifact', 'w') { |f| f.puts \"I'm still unimportant!\" }\n        (tmpdir + 'DELETEME').unlink\n      end\n\n      it 'fails' do\n        expect { subject }.to raise_error(ArgumentError, 'No file containing checksums found.')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/applications/installer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool/applications'\nrequire 'puppet_spec/module_tool/shared_functions'\nrequire 'puppet_spec/module_tool/stub_source'\n\nrequire 'tmpdir'\n\ndescribe Puppet::ModuleTool::Applications::Installer, :unless => RUBY_PLATFORM == 'java' do\n  include PuppetSpec::ModuleTool::SharedFunctions\n  include PuppetSpec::Files\n  include PuppetSpec::Fixtures\n\n  before do\n    FileUtils.mkdir_p(primary_dir)\n    FileUtils.mkdir_p(secondary_dir)\n  end\n\n  let(:vardir)        { tmpdir('installer') }\n  let(:primary_dir)   { File.join(vardir, \"primary\") }\n  let(:secondary_dir) { File.join(vardir, \"secondary\") }\n  let(:remote_source) { PuppetSpec::ModuleTool::StubSource.new }\n\n  let(:install_dir) do\n    dir = double(\"Puppet::ModuleTool::InstallDirectory\")\n    allow(dir).to receive(:prepare)\n    allow(dir).to receive(:target).and_return(primary_dir)\n    dir\n  end\n\n  before do\n    SemanticPuppet::Dependency.clear_sources\n    allow_any_instance_of(Puppet::ModuleTool::Applications::Installer).to receive(:module_repository).and_return(remote_source)\n  end\n\n  if Puppet::Util::Platform.windows?\n    before :each do\n      Puppet[:module_working_dir] = tmpdir('module_tool_install').gsub('/', '\\\\')\n    end\n  end\n\n  def installer(modname, target_dir, options)\n    Puppet::ModuleTool.set_option_defaults(options)\n    Puppet::ModuleTool::Applications::Installer.new(modname, target_dir, options)\n  end\n\n  let(:environment) do\n    Puppet.lookup(:current_environment).override_with(\n      :vardir     => vardir,\n      :modulepath => [ primary_dir, secondary_dir ]\n    )\n  end\n\n  context '#run' do\n    let(:module) { 'pmtacceptance-stdlib' }\n\n    def options\n      { :environment => environment }\n    end\n\n    let(:application) { installer(self.module, install_dir, options) }\n    subject { application.run }\n\n    it 'installs the specified module' do\n      expect(subject).to include :result => :success\n      graph_should_include 'pmtacceptance-stdlib', nil => v('4.1.0')\n    end\n\n    it 'reports a meaningful error if the name is invalid' do\n      app = installer('ntp', install_dir, options)\n      results = app.run\n      expect(results).to include :result => :failure\n      expect(results[:error][:oneline]).to eq(\"Could not install 'ntp', did you mean 'puppetlabs-ntp'?\")\n      expect(results[:error][:multiline]).to eq(<<~END.chomp)\n        Could not install module 'ntp'\n          The name 'ntp' is invalid\n            Did you mean `puppet module install puppetlabs-ntp`?\n      END\n    end\n\n    context 'with a tarball file' do\n      let(:module) { fixtures('stdlib.tgz') }\n\n      it 'installs the specified tarball' do\n        expect(subject).to include :result => :success\n        graph_should_include 'puppetlabs-stdlib', nil => v('3.2.0')\n      end\n\n      context 'with --ignore-dependencies' do\n        def options\n          super.merge(:ignore_dependencies => true)\n        end\n\n        it 'installs the specified tarball' do\n          expect(remote_source).not_to receive(:fetch)\n          expect(subject).to include :result => :success\n          graph_should_include 'puppetlabs-stdlib', nil => v('3.2.0')\n        end\n      end\n\n      context 'with dependencies' do\n        let(:module) { fixtures('java.tgz') }\n\n        it 'installs the specified tarball' do\n          expect(subject).to include :result => :success\n          graph_should_include 'puppetlabs-java', nil => v('1.0.0')\n          graph_should_include 'puppetlabs-stdlib', nil => v('4.1.0')\n        end\n\n        context 'with --ignore-dependencies' do\n          def options\n            super.merge(:ignore_dependencies => true)\n          end\n\n          it 'installs the specified tarball without dependencies' do\n            expect(remote_source).not_to receive(:fetch)\n            expect(subject).to include :result => :success\n            graph_should_include 'puppetlabs-java', nil => v('1.0.0')\n            graph_should_include 'puppetlabs-stdlib', nil\n          end\n        end\n      end\n    end\n\n    context 'with dependencies' do\n      let(:module) { 'pmtacceptance-apache' }\n\n      it 'installs the specified module and its dependencies' do\n        expect(subject).to include :result => :success\n        graph_should_include 'pmtacceptance-apache', nil => v('0.10.0')\n        graph_should_include 'pmtacceptance-stdlib', nil => v('4.1.0')\n      end\n\n      context 'and using --ignore_dependencies' do\n        def options\n          super.merge(:ignore_dependencies => true)\n        end\n\n        it 'installs only the specified module' do\n          expect(subject).to include :result => :success\n          graph_should_include 'pmtacceptance-apache', nil => v('0.10.0')\n          graph_should_include 'pmtacceptance-stdlib', nil\n        end\n      end\n\n      context 'that are already installed' do\n        context 'and satisfied' do\n          before { preinstall('pmtacceptance-stdlib', '4.1.0') }\n\n          it 'installs only the specified module' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', nil => v('0.10.0')\n            graph_should_include 'pmtacceptance-stdlib', :path => primary_dir\n          end\n\n          context '(outdated but suitable version)' do\n            before { preinstall('pmtacceptance-stdlib', '2.4.0') }\n\n            it 'installs only the specified module' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-apache', nil => v('0.10.0')\n              graph_should_include 'pmtacceptance-stdlib', v('2.4.0') => v('2.4.0'), :path => primary_dir\n            end\n          end\n\n          context '(outdated and unsuitable version)' do\n            before { preinstall('pmtacceptance-stdlib', '1.0.0') }\n\n            it 'installs a version that is compatible with the installed dependencies' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-apache', nil => v('0.0.4')\n              graph_should_include 'pmtacceptance-stdlib', nil\n            end\n          end\n        end\n\n        context 'but not satisfied' do\n          let(:module) { 'pmtacceptance-keystone' }\n\n          def options\n            super.merge(:version => '2.0.0')\n          end\n\n          before { preinstall('pmtacceptance-mysql', '2.1.0') }\n\n          it 'installs only the specified module' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-keystone', nil => v('2.0.0')\n            graph_should_include 'pmtacceptance-mysql', v('2.1.0') => v('2.1.0')\n            graph_should_include 'pmtacceptance-stdlib', nil\n          end\n        end\n      end\n\n      context 'that are already installed in other modulepath directories' do\n        before { preinstall('pmtacceptance-stdlib', '1.0.0', :into => secondary_dir) }\n        let(:module) { 'pmtacceptance-apache' }\n\n        context 'without dependency updates' do\n          it 'installs the module only' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', nil => v('0.0.4')\n            graph_should_include 'pmtacceptance-stdlib', nil\n          end\n        end\n\n        context 'with dependency updates' do\n          before { preinstall('pmtacceptance-stdlib', '2.0.0', :into => secondary_dir) }\n\n          it 'installs the module and upgrades dependencies in-place' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', nil => v('0.10.0')\n            graph_should_include 'pmtacceptance-stdlib', v('2.0.0') => v('2.6.0'), :path => secondary_dir\n          end\n        end\n      end\n    end\n\n    context 'with a specified' do\n\n      context 'version' do\n        def options\n          super.merge(:version => '3.0.0')\n        end\n\n        it 'installs the specified release (or a prerelease thereof)' do\n          expect(subject).to include :result => :success\n          graph_should_include 'pmtacceptance-stdlib', nil => v('3.0.0')\n        end\n      end\n\n      context 'version range' do\n        def options\n          super.merge(:version => '3.x')\n        end\n\n        it 'installs the greatest available version matching that range' do\n          expect(subject).to include :result => :success\n          graph_should_include 'pmtacceptance-stdlib', nil => v('3.2.0')\n        end\n      end\n    end\n\n    context 'when depended upon' do\n      before { preinstall('pmtacceptance-keystone', '2.1.0') }\n      let(:module)  { 'pmtacceptance-mysql' }\n\n      it 'installs the greatest available version meeting the dependency constraints' do\n        expect(subject).to include :result => :success\n        graph_should_include 'pmtacceptance-mysql', nil => v('0.9.0')\n      end\n\n      context 'with a --version that can satisfy' do\n        def options\n          super.merge(:version => '0.8.0')\n        end\n\n        it 'installs the greatest available version satisfying both constraints' do\n          expect(subject).to include :result => :success\n          graph_should_include 'pmtacceptance-mysql', nil => v('0.8.0')\n        end\n\n        context 'with an already installed dependency' do\n          before { preinstall('pmtacceptance-stdlib', '2.6.0') }\n\n          def options\n            super.merge(:version => '0.7.0')\n          end\n\n          it 'installs given version without errors and does not change version of dependency' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-mysql', nil => v('0.7.0')\n            expect(subject[:error]).to be_nil\n            graph_should_include 'pmtacceptance-stdlib', v('2.6.0') => v('2.6.0')\n          end\n        end\n      end\n\n      context 'with a --version that cannot satisfy' do\n        def options\n          super.merge(:version => '> 1.0.0')\n        end\n\n        it 'fails to install, since there is no version that can satisfy both constraints' do\n          expect(subject).to include :result => :failure\n        end\n\n        context 'with unsatisfiable dependencies' do\n          let(:graph) { double(SemanticPuppet::Dependency::Graph, :modules => ['pmtacceptance-mysql']) }\n          let(:exception) { SemanticPuppet::Dependency::UnsatisfiableGraph.new(graph, constraint) }\n\n          before do\n            allow(SemanticPuppet::Dependency).to receive(:resolve).and_raise(exception)\n          end\n\n          context 'with known constraint' do\n            let(:constraint) { 'pmtacceptance-mysql' }\n\n            it 'prints a detailed error containing the modules that would not be satisfied' do\n              expect(subject[:error]).to include(:multiline)\n              expect(subject[:error][:multiline]).to include(\"Could not install module 'pmtacceptance-mysql' (> 1.0.0)\")\n              expect(subject[:error][:multiline]).to include(\"The requested version cannot satisfy one or more of the following installed modules:\")\n              expect(subject[:error][:multiline]).to include(\"pmtacceptance-keystone, expects 'pmtacceptance-mysql': >=0.6.1 <1.0.0\")\n              expect(subject[:error][:multiline]).to include(\"Use `puppet module install 'pmtacceptance-mysql' --ignore-dependencies` to install only this module\")\n            end\n          end\n\n          context 'with missing constraint' do\n            let(:constraint) { nil }\n\n            it 'prints the generic error message' do\n              expect(subject[:error]).to include(:multiline)\n              expect(subject[:error][:multiline]).to include(\"Could not install module 'pmtacceptance-mysql' (> 1.0.0)\")\n              expect(subject[:error][:multiline]).to include(\"The requested version cannot satisfy all dependencies\")\n            end\n          end\n\n          context 'with unknown constraint' do\n            let(:constraint) { 'another' }\n\n            it 'prints the generic error message' do\n              expect(subject[:error]).to include(:multiline)\n              expect(subject[:error][:multiline]).to include(\"Could not install module 'pmtacceptance-mysql' (> 1.0.0)\")\n              expect(subject[:error][:multiline]).to include(\"The requested version cannot satisfy all dependencies\")\n            end\n          end\n        end\n\n        context 'with --ignore-dependencies' do\n          def options\n            super.merge(:ignore_dependencies => true)\n          end\n\n          it 'fails to install, since ignore_dependencies should still respect dependencies from installed modules' do\n            expect(subject).to include :result => :failure\n          end\n        end\n\n        context 'with --force' do\n          def options\n            super.merge(:force => true)\n          end\n\n          it 'installs the greatest available version, ignoring dependencies' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-mysql', nil => v('2.1.0')\n          end\n        end\n\n        context 'with an already installed dependency' do\n          let(:graph) {\n            double(SemanticPuppet::Dependency::Graph,\n              :dependencies => {\n                'pmtacceptance-mysql' => {\n                  :version => '2.1.0'\n                }\n              },\n              :modules => ['pmtacceptance-mysql'],\n              :unsatisfied => 'pmtacceptance-stdlib'\n            )\n          }\n\n          let(:unsatisfiable_graph_exception) { SemanticPuppet::Dependency::UnsatisfiableGraph.new(graph) }\n\n          before do\n            allow(SemanticPuppet::Dependency).to receive(:resolve).and_raise(unsatisfiable_graph_exception)\n            allow(unsatisfiable_graph_exception).to receive(:respond_to?).and_return(true)\n            allow(unsatisfiable_graph_exception).to receive(:unsatisfied).and_return(graph.unsatisfied)\n\n            preinstall('pmtacceptance-stdlib', '2.6.0')\n          end\n\n          def options\n            super.merge(:version => '2.1.0')\n          end\n\n          it 'fails to install and outputs a multiline error containing the versions, expectations and workaround' do\n            expect(subject).to include :result => :failure\n            expect(subject[:error]).to include(:multiline)\n            expect(subject[:error][:multiline]).to include(\"Could not install module 'pmtacceptance-mysql' (v2.1.0)\")\n            expect(subject[:error][:multiline]).to include(\"The requested version cannot satisfy one or more of the following installed modules:\")\n            expect(subject[:error][:multiline]).to include(\"pmtacceptance-stdlib, installed: 2.6.0, expected: >= 2.2.1\")\n            expect(subject[:error][:multiline]).to include(\"Use `puppet module install 'pmtacceptance-mysql' --ignore-dependencies` to install only this module\")\n          end\n        end\n      end\n    end\n\n    context 'when already installed' do\n      before { preinstall('pmtacceptance-stdlib', '1.0.0') }\n\n      context 'but matching the requested version' do\n        it 'does nothing, since the installed version satisfies' do\n          expect(subject).to include :result => :noop\n        end\n\n        context 'with --force' do\n          def options\n            super.merge(:force => true)\n          end\n\n          it 'does reinstall the module' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('4.1.0')\n          end\n        end\n\n        context 'with local changes' do\n          before do\n            release = application.send(:installed_modules)['pmtacceptance-stdlib']\n            mark_changed(release.mod.path)\n          end\n\n          it 'does nothing, since local changes do not affect that' do\n            expect(subject).to include :result => :noop\n          end\n\n          context 'with --force' do\n            def options\n              super.merge(:force => true)\n            end\n\n            it 'does reinstall the module, since --force ignores local changes' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('4.1.0')\n            end\n          end\n        end\n      end\n\n      context 'but not matching the requested version' do\n        def options\n          super.merge(:version => '2.x')\n        end\n\n        it 'fails to install the module, since it is already installed' do\n          expect(subject).to include :result => :failure\n          expect(subject[:error]).to include :oneline => \"'pmtacceptance-stdlib' (v2.x) requested; 'pmtacceptance-stdlib' (v1.0.0) already installed\"\n        end\n\n        context 'with --force' do\n          def options\n            super.merge(:force => true)\n          end\n\n          it 'installs the greatest version matching the new version range' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('2.6.0')\n          end\n        end\n      end\n    end\n\n    context 'when a module with the same name is already installed' do\n      let(:module) { 'pmtacceptance-stdlib' }\n      before { preinstall('puppetlabs-stdlib', '4.1.0') }\n\n      it 'fails to install, since two modules with the same name cannot be installed simultaneously' do\n        expect(subject).to include :result => :failure\n      end\n\n      context 'using --force' do\n        def options\n          super.merge(:force => true)\n        end\n\n        it 'overwrites the existing module with the greatest version of the requested module' do\n          expect(subject).to include :result => :success\n          graph_should_include 'pmtacceptance-stdlib', nil => v('4.1.0')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/applications/uninstaller_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool'\nrequire 'tmpdir'\nrequire 'puppet_spec/module_tool/shared_functions'\nrequire 'puppet_spec/module_tool/stub_source'\n\ndescribe Puppet::ModuleTool::Applications::Uninstaller do\n  include PuppetSpec::ModuleTool::SharedFunctions\n  include PuppetSpec::Files\n\n  before do\n    FileUtils.mkdir_p(primary_dir)\n    FileUtils.mkdir_p(secondary_dir)\n  end\n\n  let(:environment) do\n    Puppet.lookup(:current_environment).override_with(\n      :vardir     => vardir,\n      :modulepath => [ primary_dir, secondary_dir ]\n    )\n  end\n\n  let(:vardir)   { tmpdir('uninstaller') }\n  let(:primary_dir) { File.join(vardir, \"primary\") }\n  let(:secondary_dir) { File.join(vardir, \"secondary\") }\n  let(:remote_source) { PuppetSpec::ModuleTool::StubSource.new }\n\n  let(:module) { 'module-not_installed' }\n  let(:application) do\n    opts = options\n    Puppet::ModuleTool.set_option_defaults(opts)\n    Puppet::ModuleTool::Applications::Uninstaller.new(self.module, opts)\n  end\n\n  def options\n    { :environment => environment }\n  end\n\n  subject { application.run }\n\n  context \"when the module is not installed\" do\n    it \"should fail\" do\n      expect(subject).to include :result => :failure\n    end\n  end\n\n  context \"when the module is installed\" do\n    let(:module) { 'pmtacceptance-stdlib' }\n\n    before { preinstall('pmtacceptance-stdlib', '1.0.0') }\n    before { preinstall('pmtacceptance-apache', '0.0.4') }\n\n    it \"should uninstall the module\" do\n      expect(subject[:affected_modules].first.forge_name).to eq(\"pmtacceptance/stdlib\")\n    end\n\n    it \"should only uninstall the requested module\" do\n      subject[:affected_modules].length == 1\n    end\n\n    it 'should refuse to uninstall in FIPS mode' do\n      allow(Facter).to receive(:value).with(:fips_enabled).and_return(true)\n      err = subject[:error][:oneline]\n      expect(err).to eq(\"Either the `--ignore_changes` or `--force` argument must be specified to uninstall modules when running in FIPS mode.\")\n    end\n\n    context 'in two modulepaths' do\n      before { preinstall('pmtacceptance-stdlib', '2.0.0', :into => secondary_dir) }\n\n      it \"should fail if a module exists twice in the modpath\" do\n        expect(subject).to include :result => :failure\n      end\n    end\n\n    context \"when options[:version] is specified\" do\n      def options\n        super.merge(:version => '1.0.0')\n      end\n\n      it \"should uninstall the module if the version matches\" do\n        expect(subject[:affected_modules].length).to eq(1)\n        expect(subject[:affected_modules].first.version).to eq(\"1.0.0\")\n      end\n\n      context 'but not matched' do\n        def options\n          super.merge(:version => '2.0.0')\n        end\n\n        it \"should not uninstall the module if the version does not match\" do\n          expect(subject).to include :result => :failure\n        end\n      end\n    end\n\n    context \"when the module metadata is missing\" do\n      before { File.unlink(File.join(primary_dir, 'stdlib', 'metadata.json')) }\n\n      it \"should not uninstall the module\" do\n        expect(application.run[:result]).to eq(:failure)\n      end\n    end\n\n    context \"when the module has local changes\" do\n      before do\n        mark_changed(File.join(primary_dir, 'stdlib'))\n      end\n\n      it \"should not uninstall the module\" do\n        expect(subject).to include :result => :failure\n      end\n    end\n\n    context \"when uninstalling the module will cause broken dependencies\" do\n      before { preinstall('pmtacceptance-apache', '0.10.0') }\n\n      it \"should not uninstall the module\" do\n        expect(subject).to include :result => :failure\n      end\n    end\n\n    context 'with --ignore-changes' do\n      def options\n        super.merge(:ignore_changes => true)\n      end\n\n      context 'with local changes' do\n        before do\n          mark_changed(File.join(primary_dir, 'stdlib'))\n        end\n\n        it 'overwrites the installed module with the greatest version matching that range' do\n          expect(subject).to include :result => :success\n        end\n\n        it 'uninstalls in FIPS mode' do\n          allow(Facter).to receive(:value).with(:fips_enabled).and_return(true)\n          expect(subject).to include :result => :success\n        end\n      end\n\n      context 'without local changes' do\n        it 'overwrites the installed module with the greatest version matching that range' do\n          expect(subject).to include :result => :success\n        end\n      end\n    end\n\n    context \"when using the --force flag\" do\n\n      def options\n        super.merge(:force => true)\n      end\n\n      context \"with local changes\" do\n        before do\n          mark_changed(File.join(primary_dir, 'stdlib'))\n        end\n\n        it \"should ignore local changes\" do\n          expect(subject[:affected_modules].length).to eq(1)\n          expect(subject[:affected_modules].first.forge_name).to eq(\"pmtacceptance/stdlib\")\n        end\n\n        it 'uninstalls in FIPS mode' do\n          allow(Facter).to receive(:value).with(:fips_enabled).and_return(true)\n          expect(subject).to include :result => :success\n        end\n      end\n\n      context \"while depended upon\" do\n        before { preinstall('pmtacceptance-apache', '0.10.0') }\n\n        it \"should ignore broken dependencies\" do\n          expect(subject[:affected_modules].length).to eq(1)\n          expect(subject[:affected_modules].first.forge_name).to eq(\"pmtacceptance/stdlib\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/applications/unpacker_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/json'\n\nrequire 'puppet/module_tool/applications'\nrequire 'puppet/file_system'\nrequire 'puppet_spec/modules'\n\ndescribe Puppet::ModuleTool::Applications::Unpacker do\n  include PuppetSpec::Files\n\n  let(:target)      { tmpdir(\"unpacker\") }\n  let(:module_name) { 'myusername-mytarball' }\n  let(:filename)    { tmpdir(\"module\") + \"/module.tar.gz\" }\n  let(:working_dir) { tmpdir(\"working_dir\") }\n\n  before :each do\n    allow(Puppet.settings).to receive(:[])\n    allow(Puppet.settings).to receive(:[]).with(:module_working_dir).and_return(working_dir)\n  end\n\n  it \"should attempt to untar file to temporary location\" do\n    untar = double('Tar')\n    expect(untar).to receive(:unpack).with(filename, anything, anything) do |src, dest, _|\n      FileUtils.mkdir(File.join(dest, 'extractedmodule'))\n      File.open(File.join(dest, 'extractedmodule', 'metadata.json'), 'w+') do |file|\n        file.puts Puppet::Util::Json.dump('name' => module_name, 'version' => '1.0.0')\n      end\n      true\n    end\n\n    expect(Puppet::ModuleTool::Tar).to receive(:instance).and_return(untar)\n\n    Puppet::ModuleTool::Applications::Unpacker.run(filename, :target_dir => target)\n    expect(File).to be_directory(File.join(target, 'mytarball'))\n  end\n\n  it \"should warn about symlinks\", :if => Puppet.features.manages_symlinks? do\n    untar = double('Tar')\n    expect(untar).to receive(:unpack).with(filename, anything, anything) do |src, dest, _|\n      FileUtils.mkdir(File.join(dest, 'extractedmodule'))\n      File.open(File.join(dest, 'extractedmodule', 'metadata.json'), 'w+') do |file|\n        file.puts Puppet::Util::Json.dump('name' => module_name, 'version' => '1.0.0')\n      end\n      FileUtils.touch(File.join(dest, 'extractedmodule/tempfile'))\n      Puppet::FileSystem.symlink(File.join(dest, 'extractedmodule/tempfile'), File.join(dest, 'extractedmodule/tempfile2'))\n      true\n    end\n\n    expect(Puppet::ModuleTool::Tar).to receive(:instance).and_return(untar)\n    expect(Puppet).to receive(:warning).with(/symlinks/i)\n\n    Puppet::ModuleTool::Applications::Unpacker.run(filename, :target_dir => target)\n    expect(File).to be_directory(File.join(target, 'mytarball'))\n  end\n\n  it \"should warn about symlinks in subdirectories\", :if => Puppet.features.manages_symlinks? do\n    untar = double('Tar')\n    expect(untar).to receive(:unpack).with(filename, anything, anything) do |src, dest, _|\n      FileUtils.mkdir(File.join(dest, 'extractedmodule'))\n      File.open(File.join(dest, 'extractedmodule', 'metadata.json'), 'w+') do |file|\n        file.puts Puppet::Util::Json.dump('name' => module_name, 'version' => '1.0.0')\n      end\n      FileUtils.mkdir(File.join(dest, 'extractedmodule/manifests'))\n      FileUtils.touch(File.join(dest, 'extractedmodule/manifests/tempfile'))\n      Puppet::FileSystem.symlink(File.join(dest, 'extractedmodule/manifests/tempfile'), File.join(dest, 'extractedmodule/manifests/tempfile2'))\n      true\n    end\n\n    expect(Puppet::ModuleTool::Tar).to receive(:instance).and_return(untar)\n    expect(Puppet).to receive(:warning).with(/symlinks/i)\n\n    Puppet::ModuleTool::Applications::Unpacker.run(filename, :target_dir => target)\n    expect(File).to be_directory(File.join(target, 'mytarball'))\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/applications/upgrader_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool/applications'\nrequire 'puppet_spec/module_tool/shared_functions'\nrequire 'puppet_spec/module_tool/stub_source'\nrequire 'tmpdir'\n\ndescribe Puppet::ModuleTool::Applications::Upgrader do\n  include PuppetSpec::ModuleTool::SharedFunctions\n  include PuppetSpec::Files\n\n  before do\n    FileUtils.mkdir_p(primary_dir)\n    FileUtils.mkdir_p(secondary_dir)\n  end\n\n  let(:vardir)   { tmpdir('upgrader') }\n  let(:primary_dir) { File.join(vardir, \"primary\") }\n  let(:secondary_dir) { File.join(vardir, \"secondary\") }\n  let(:remote_source) { PuppetSpec::ModuleTool::StubSource.new }\n\n  let(:environment) do\n    Puppet.lookup(:current_environment).override_with(\n      :vardir     => vardir,\n      :modulepath => [ primary_dir, secondary_dir ]\n    )\n  end\n\n  before do\n    SemanticPuppet::Dependency.clear_sources\n    allow_any_instance_of(Puppet::ModuleTool::Applications::Upgrader).to receive(:module_repository).and_return(remote_source)\n  end\n\n  if Puppet::Util::Platform.windows?\n    before :each do\n      allow(Puppet.settings).to receive(:[])\n      allow(Puppet.settings).to receive(:[]).with(:module_working_dir).and_return(Dir.mktmpdir('upgradertmp'))\n    end\n  end\n\n  def upgrader(name, options = {})\n    Puppet::ModuleTool.set_option_defaults(options)\n    Puppet::ModuleTool::Applications::Upgrader.new(name, options)\n  end\n\n  describe '#run' do\n    let(:module) { 'pmtacceptance-stdlib' }\n\n    def options\n      { :environment => environment }\n    end\n\n    let(:application) { upgrader(self.module, options) }\n    subject { application.run }\n\n    it 'fails if the module is not already installed' do\n      expect(subject).to include :result => :failure\n      expect(subject[:error]).to include :oneline => \"Could not upgrade '#{self.module}'; module is not installed\"\n    end\n\n    context 'for an installed module' do\n      context 'with only one version' do\n        before { preinstall('puppetlabs-oneversion', '0.0.1') }\n        let(:module) { 'puppetlabs-oneversion' }\n\n        it 'declines to upgrade' do\n          expect(subject).to include :result => :noop\n          expect(subject[:error][:multiline]).to match(/already the latest version/)\n        end\n      end\n\n      context 'without dependencies' do\n        before { preinstall('pmtacceptance-stdlib', '1.0.0') }\n\n        context 'without options' do\n          it 'properly upgrades the module to the greatest version' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('4.1.0')\n          end\n        end\n\n        context 'with version range' do\n          def options\n            super.merge(:version => '3.x')\n          end\n\n          context 'not matching the installed version' do\n            it 'properly upgrades the module to the greatest version within that range' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('3.2.0')\n            end\n          end\n\n          context 'matching the installed version' do\n            context 'with more recent version' do\n              before { preinstall('pmtacceptance-stdlib', '3.0.0')}\n\n              it 'properly upgrades the module to the greatest version within that range' do\n                expect(subject).to include :result => :success\n                graph_should_include 'pmtacceptance-stdlib', v('3.0.0') => v('3.2.0')\n              end\n            end\n\n            context 'without more recent version' do\n              before { preinstall('pmtacceptance-stdlib', '3.2.0')}\n\n              context 'without options' do\n                it 'declines to upgrade' do\n                  expect(subject).to include :result => :noop\n                  expect(subject[:error][:multiline]).to match(/already the latest version/)\n                end\n              end\n\n              context 'with --force' do\n                def options\n                  super.merge(:force => true)\n                end\n\n                it 'overwrites the installed module with the greatest version matching that range' do\n                  expect(subject).to include :result => :success\n                  graph_should_include 'pmtacceptance-stdlib', v('3.2.0') => v('3.2.0')\n                end\n              end\n            end\n          end\n        end\n      end\n\n      context 'that is depended upon' do\n        # pmtacceptance-keystone depends on pmtacceptance-mysql  >=0.6.1 <1.0.0\n        before { preinstall('pmtacceptance-keystone', '2.1.0') }\n        before { preinstall('pmtacceptance-mysql', '0.9.0') }\n\n        let(:module) { 'pmtacceptance-mysql' }\n\n        context 'and out of date' do\n          before { preinstall('pmtacceptance-mysql', '0.8.0') }\n\n          it 'properly upgrades to the greatest version matching the dependency' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-mysql', v('0.8.0') => v('0.9.0')\n          end\n        end\n\n        context 'and up to date' do\n          it 'declines to upgrade' do\n            expect(subject).to include :result => :failure\n          end\n        end\n\n        context 'when specifying a violating version range' do\n          def options\n            super.merge(:version => '2.1.0')\n          end\n\n          it 'fails to upgrade the module' do\n            # TODO: More helpful error message?\n            expect(subject).to include :result => :failure\n            expect(subject[:error]).to include :oneline => \"Could not upgrade '#{self.module}' (v0.9.0 -> v2.1.0); no version satisfies all dependencies\"\n          end\n\n          context 'using --force' do\n            def options\n              super.merge(:force => true)\n            end\n\n            it 'overwrites the installed module with the specified version' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-mysql', v('0.9.0') => v('2.1.0')\n            end\n          end\n        end\n      end\n\n      context 'with local changes' do\n        before { preinstall('pmtacceptance-stdlib', '1.0.0') }\n        before do\n          release = application.send(:installed_modules)['pmtacceptance-stdlib']\n          mark_changed(release.mod.path)\n        end\n\n        it 'fails to upgrade' do\n          expect(subject).to include :result => :failure\n          expect(subject[:error]).to include :oneline => \"Could not upgrade '#{self.module}'; module has had changes made locally\"\n        end\n\n        context 'with --ignore-changes' do\n          def options\n            super.merge(:ignore_changes => true)\n          end\n\n          it 'overwrites the installed module with the greatest version matching that range' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('4.1.0')\n          end\n        end\n      end\n\n      context 'with dependencies' do\n        context 'that are unsatisfied' do\n          def options\n            super.merge(:version => '0.1.1')\n          end\n\n          before { preinstall('pmtacceptance-apache', '0.0.3') }\n          let(:module) { 'pmtacceptance-apache' }\n\n          it 'upgrades the module and installs the missing dependencies' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.1.1')\n            graph_should_include 'pmtacceptance-stdlib', nil => v('4.1.0'), :action => :install\n          end\n        end\n\n        context 'with older major versions' do\n          # pmtacceptance-apache 0.0.4 has no dependency on pmtacceptance-stdlib\n          # the next available version (0.1.1) and all subsequent versions depend on pmtacceptance-stdlib >= 2.2.1\n          before { preinstall('pmtacceptance-apache', '0.0.3') }\n          before { preinstall('pmtacceptance-stdlib', '1.0.0') }\n          let(:module) { 'pmtacceptance-apache' }\n\n          it 'refuses to upgrade the installed dependency to a new major version, but upgrades the module to the greatest compatible version' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.0.4')\n          end\n\n          context 'using --ignore_dependencies' do\n            def options\n              super.merge(:ignore_dependencies => true)\n            end\n\n            it 'upgrades the module to the greatest available version' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.10.0')\n            end\n          end\n        end\n\n        context 'with satisfying major versions' do\n          before { preinstall('pmtacceptance-apache', '0.0.3') }\n          before { preinstall('pmtacceptance-stdlib', '2.0.0') }\n          let(:module) { 'pmtacceptance-apache' }\n\n          it 'upgrades the module and its dependencies to their greatest compatible versions' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.10.0')\n            graph_should_include 'pmtacceptance-stdlib', v('2.0.0') => v('2.6.0')\n          end\n        end\n\n        context 'with satisfying versions' do\n          before { preinstall('pmtacceptance-apache', '0.0.3') }\n          before { preinstall('pmtacceptance-stdlib', '2.4.0') }\n          let(:module) { 'pmtacceptance-apache' }\n\n          it 'upgrades the module to the greatest available version' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.10.0')\n            graph_should_include 'pmtacceptance-stdlib', nil\n          end\n        end\n\n        context 'with current versions' do\n          before { preinstall('pmtacceptance-apache', '0.0.3') }\n          before { preinstall('pmtacceptance-stdlib', '2.6.0') }\n          let(:module) { 'pmtacceptance-apache' }\n\n          it 'upgrades the module to the greatest available version' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.10.0')\n            graph_should_include 'pmtacceptance-stdlib', nil\n          end\n        end\n\n        context 'with shared dependencies' do\n          # bacula 0.0.3 depends on stdlib >= 2.2.0 and pmtacceptance/mysql >= 1.0.0\n          # bacula 0.0.2 depends on stdlib >= 2.2.0 and pmtacceptance/mysql >= 0.0.1\n          # bacula 0.0.1 depends on stdlib >= 2.2.0\n\n          # keystone 2.1.0 depends on pmtacceptance/stdlib >= 2.5.0 and pmtacceptance/mysql >=0.6.1 <1.0.0\n\n          before { preinstall('pmtacceptance-bacula', '0.0.1') }\n          before { preinstall('pmtacceptance-mysql', '0.9.0') }\n          before { preinstall('pmtacceptance-keystone', '2.1.0') }\n\n          let(:module) { 'pmtacceptance-bacula' }\n\n          it 'upgrades the module to the greatest version compatible with all other installed modules' do\n            expect(subject).to include :result => :success\n            graph_should_include 'pmtacceptance-bacula', v('0.0.1') => v('0.0.2')\n          end\n\n          context 'using --force' do\n            def options\n              super.merge(:force => true)\n            end\n\n            it 'upgrades the module to the greatest version available' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-bacula', v('0.0.1') => v('0.0.3')\n            end\n          end\n        end\n\n        context 'in other modulepath directories' do\n          before { preinstall('pmtacceptance-apache', '0.0.3') }\n          before { preinstall('pmtacceptance-stdlib', '1.0.0', :into => secondary_dir) }\n          let(:module) { 'pmtacceptance-apache' }\n\n          context 'with older major versions' do\n            it 'upgrades the module to the greatest version compatible with the installed modules' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.0.4')\n              graph_should_include 'pmtacceptance-stdlib', nil\n            end\n          end\n\n          context 'with satisfying major versions' do\n            before { preinstall('pmtacceptance-stdlib', '2.0.0', :into => secondary_dir) }\n\n            it 'upgrades the module and its dependencies to their greatest compatible versions, in-place' do\n              expect(subject).to include :result => :success\n              graph_should_include 'pmtacceptance-apache', v('0.0.3') => v('0.10.0')\n              graph_should_include 'pmtacceptance-stdlib', v('2.0.0') => v('2.6.0'), :path => secondary_dir\n            end\n          end\n        end\n      end\n    end\n\n    context 'when in FIPS mode...' do\n      it 'module unpgrader refuses to run' do\n        allow(Facter).to receive(:value).with(:fips_enabled).and_return(true)\n        expect { application.run }.to raise_error(/Module upgrade is prohibited in FIPS mode/)\n      end \n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/install_directory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool/install_directory'\n\ndescribe Puppet::ModuleTool::InstallDirectory do\n  def expect_normal_results\n    results = installer.run\n    expect(results[:installed_modules].length).to eq 1\n    expect(results[:installed_modules][0][:module]).to eq(\"pmtacceptance-stdlib\")\n    expect(results[:installed_modules][0][:version][:vstring]).to eq(\"1.0.0\")\n    results\n  end\n\n  it \"(#15202) creates the install directory\" do\n    target_dir = the_directory('foo', :directory? => false, :exist? => false)\n    expect(target_dir).to receive(:mkpath)\n\n    install = Puppet::ModuleTool::InstallDirectory.new(target_dir)\n\n    install.prepare('pmtacceptance-stdlib', '1.0.0')\n  end\n\n  it \"(#15202) errors when the directory is not accessible\" do\n    target_dir = the_directory('foo', :directory? => false, :exist? => false)\n    expect(target_dir).to receive(:mkpath).and_raise(Errno::EACCES)\n\n    install = Puppet::ModuleTool::InstallDirectory.new(target_dir)\n\n    expect {\n      install.prepare('module', '1.0.1')\n    }.to raise_error(\n      Puppet::ModuleTool::Errors::PermissionDeniedCreateInstallDirectoryError\n    )\n  end\n\n  it \"(#15202) errors when an entry along the path is not a directory\" do\n    target_dir = the_directory(\"foo/bar\", :exist? => false, :directory? => false)\n    expect(target_dir).to receive(:mkpath).and_raise(Errno::EEXIST)\n\n    install = Puppet::ModuleTool::InstallDirectory.new(target_dir)\n\n    expect {\n      install.prepare('module', '1.0.1')\n    }.to raise_error(Puppet::ModuleTool::Errors::InstallPathExistsNotDirectoryError)\n  end\n\n  it \"(#15202) simply re-raises an unknown error\" do\n    target_dir = the_directory(\"foo/bar\", :exist? => false, :directory? => false)\n    expect(target_dir).to receive(:mkpath).and_raise(\"unknown error\")\n\n    install = Puppet::ModuleTool::InstallDirectory.new(target_dir)\n\n    expect { install.prepare('module', '1.0.1') }.to raise_error(\"unknown error\")\n  end\n\n  it \"(#15202) simply re-raises an unknown system call error\" do\n    target_dir = the_directory(\"foo/bar\", :exist? => false, :directory? => false)\n    expect(target_dir).to receive(:mkpath).and_raise(SystemCallError, \"unknown\")\n\n    install = Puppet::ModuleTool::InstallDirectory.new(target_dir)\n\n    expect { install.prepare('module', '1.0.1') }.to raise_error(SystemCallError)\n  end\n\n  def the_directory(name, options)\n    dir = double(\"Pathname<#{name}>\")\n    allow(dir).to receive(:exist?).and_return(options.fetch(:exist?, true))\n    allow(dir).to receive(:directory?).and_return(options.fetch(:directory?, true))\n    dir\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/installed_modules_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool/installed_modules'\nrequire 'puppet_spec/modules'\n\ndescribe Puppet::ModuleTool::InstalledModules do\n  include PuppetSpec::Files\n\n  around do |example|\n    dir = tmpdir(\"deep_path\")\n\n    FileUtils.mkdir_p(@modpath = File.join(dir, \"modpath\"))\n\n    @env = Puppet::Node::Environment.create(:env, [@modpath])\n    Puppet.override(:current_environment => @env) do\n      example.run\n    end\n  end\n\n  it 'works when given a semantic version' do\n    mod = PuppetSpec::Modules.create('goodsemver', @modpath, :metadata => {:version => '1.2.3'})\n    installed = described_class.new(@env)\n    expect(installed.modules[\"puppetlabs-#{mod.name}\"].version).to eq(SemanticPuppet::Version.parse('1.2.3'))\n  end\n\n  it 'defaults when not given a semantic version' do\n    mod = PuppetSpec::Modules.create('badsemver', @modpath, :metadata => {:version => 'banana'})\n    expect(Puppet).to receive(:warning).with(/Semantic Version/)\n    installed = described_class.new(@env)\n    expect(installed.modules[\"puppetlabs-#{mod.name}\"].version).to eq(SemanticPuppet::Version.parse('0.0.0'))\n  end\n\n  it 'defaults when not given a full semantic version' do\n    mod = PuppetSpec::Modules.create('badsemver', @modpath, :metadata => {:version => '1.2'})\n    expect(Puppet).to receive(:warning).with(/Semantic Version/)\n    installed = described_class.new(@env)\n    expect(installed.modules[\"puppetlabs-#{mod.name}\"].version).to eq(SemanticPuppet::Version.parse('0.0.0'))\n  end\n\n  it 'still works if there is an invalid version in one of the modules' do\n    mod1 = PuppetSpec::Modules.create('badsemver', @modpath, :metadata => {:version => 'banana'})\n    mod2 = PuppetSpec::Modules.create('goodsemver', @modpath, :metadata => {:version => '1.2.3'})\n    mod3 = PuppetSpec::Modules.create('notquitesemver', @modpath, :metadata => {:version => '1.2'})\n    expect(Puppet).to receive(:warning).with(/Semantic Version/).twice\n    installed = described_class.new(@env)\n    expect(installed.modules[\"puppetlabs-#{mod1.name}\"].version).to eq(SemanticPuppet::Version.parse('0.0.0'))\n    expect(installed.modules[\"puppetlabs-#{mod2.name}\"].version).to eq(SemanticPuppet::Version.parse('1.2.3'))\n    expect(installed.modules[\"puppetlabs-#{mod3.name}\"].version).to eq(SemanticPuppet::Version.parse('0.0.0'))\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/metadata_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool'\n\ndescribe Puppet::ModuleTool::Metadata do\n  let(:data) { {} }\n  let(:metadata) { Puppet::ModuleTool::Metadata.new }\n\n  describe 'property lookups' do\n    subject { metadata }\n\n    %w[ name version author summary license source project_page issues_url\n    dependencies dashed_name release_name description data_provider].each do |prop|\n      describe \"##{prop}\" do\n        it \"responds to the property\" do\n          subject.send(prop)\n        end\n      end\n    end\n  end\n\n  describe \"#update\" do\n    subject { metadata.update(data) }\n\n    context \"with a valid name\" do\n      let(:data) { { 'name' => 'billgates-mymodule' } }\n\n      it \"extracts the author name from the name field\" do\n        expect(subject.to_hash['author']).to eq('billgates')\n      end\n\n      it \"extracts a module name from the name field\" do\n        expect(subject.module_name).to eq('mymodule')\n      end\n\n      context \"and existing author\" do\n        before { metadata.update('author' => 'foo') }\n\n        it \"avoids overwriting the existing author\" do\n          expect(subject.to_hash['author']).to eq('foo')\n        end\n      end\n    end\n\n    context \"with a valid name and author\" do\n      let(:data) { { 'name' => 'billgates-mymodule', 'author' => 'foo' } }\n\n      it \"use the author name from the author field\" do\n        expect(subject.to_hash['author']).to eq('foo')\n      end\n\n      context \"and preexisting author\" do\n        before { metadata.update('author' => 'bar') }\n\n        it \"avoids overwriting the existing author\" do\n          expect(subject.to_hash['author']).to eq('foo')\n        end\n      end\n    end\n\n    context \"with an invalid name\" do\n      context \"(short module name)\" do\n        let(:data) { { 'name' => 'mymodule' } }\n\n        it \"raises an exception\" do\n          expect { subject }.to raise_error(ArgumentError, \"Invalid 'name' field in metadata.json: the field must be a namespaced module name\")\n        end\n      end\n\n      context \"(missing namespace)\" do\n        let(:data) { { 'name' => '/mymodule' } }\n\n        it \"raises an exception\" do\n          expect { subject }.to raise_error(ArgumentError, \"Invalid 'name' field in metadata.json: the field must be a namespaced module name\")\n        end\n      end\n\n      context \"(missing module name)\" do\n        let(:data) { { 'name' => 'namespace/' } }\n\n        it \"raises an exception\" do\n          expect { subject }.to raise_error(ArgumentError, \"Invalid 'name' field in metadata.json: the field must be a namespaced module name\")\n        end\n      end\n\n      context \"(invalid namespace)\" do\n        let(:data) { { 'name' => \"dolla'bill$-mymodule\" } }\n\n        it \"raises an exception\" do\n          expect { subject }.to raise_error(ArgumentError, \"Invalid 'name' field in metadata.json: the namespace contains non-alphanumeric characters\")\n        end\n      end\n\n      context \"(non-alphanumeric module name)\" do\n        let(:data) { { 'name' => \"dollabils-fivedolla'\" } }\n\n        it \"raises an exception\" do\n          expect { subject }.to raise_error(ArgumentError, \"Invalid 'name' field in metadata.json: the module name contains non-alphanumeric (or underscore) characters\")\n        end\n      end\n\n      context \"(module name starts with a number)\" do\n        let(:data) { { 'name' => \"dollabills-5dollars\" } }\n\n        it \"raises an exception\" do\n          expect { subject }.to raise_error(ArgumentError, \"Invalid 'name' field in metadata.json: the module name must begin with a letter\")\n        end\n      end\n    end\n\n\n    context \"with an invalid version\" do\n      let(:data) { { 'version' => '3.0' } }\n\n      it \"raises an exception\" do\n        expect { subject }.to raise_error(ArgumentError, \"Invalid 'version' field in metadata.json: version string cannot be parsed as a valid Semantic Version\")\n      end\n    end\n\n    context \"with a valid source\" do\n      context \"which is a GitHub URL\" do\n        context \"with a scheme\" do\n          before { metadata.update('source' => 'https://github.com/billgates/amazingness') }\n\n          it \"predicts a default project_page\" do\n            expect(subject.to_hash['project_page']).to eq('https://github.com/billgates/amazingness')\n          end\n\n          it \"predicts a default issues_url\" do\n            expect(subject.to_hash['issues_url']).to eq('https://github.com/billgates/amazingness/issues')\n          end\n        end\n\n        context \"without a scheme\" do\n          before { metadata.update('source' => 'github.com/billgates/amazingness') }\n\n          it \"predicts a default project_page\" do\n            expect(subject.to_hash['project_page']).to eq('https://github.com/billgates/amazingness')\n          end\n\n          it \"predicts a default issues_url\" do\n            expect(subject.to_hash['issues_url']).to eq('https://github.com/billgates/amazingness/issues')\n          end\n        end\n      end\n\n      context \"which is not a GitHub URL\" do\n        before { metadata.update('source' => 'https://notgithub.com/billgates/amazingness') }\n\n        it \"does not predict a default project_page\" do\n          expect(subject.to_hash['project_page']).to be nil\n        end\n\n        it \"does not predict a default issues_url\" do\n          expect(subject.to_hash['issues_url']).to be nil\n        end\n      end\n\n      context \"which is not a URL\" do\n        before { metadata.update('source' => 'my brain') }\n\n        it \"does not predict a default project_page\" do\n          expect(subject.to_hash['project_page']).to be nil\n        end\n\n        it \"does not predict a default issues_url\" do\n          expect(subject.to_hash['issues_url']).to be nil\n        end\n      end\n\n    end\n\n    context \"with a valid dependency\" do\n      let(:data) { {'dependencies' => [{'name' => 'puppetlabs-goodmodule'}] }}\n\n      it \"adds the dependency\" do\n        expect(subject.dependencies.size).to eq(1)\n      end\n    end\n\n    context \"with a invalid dependency name\" do\n      let(:data) { {'dependencies' => [{'name' => 'puppetlabsbadmodule'}] }}\n\n      it \"raises an exception\" do\n        expect { subject }.to raise_error(ArgumentError)\n      end\n    end\n\n    context \"with a valid dependency version range\" do\n      let(:data) { {'dependencies' => [{'name' => 'puppetlabs-badmodule', 'version_requirement' => '>= 2.0.0'}] }}\n\n      it \"adds the dependency\" do\n        expect(subject.dependencies.size).to eq(1)\n      end\n    end\n\n    context \"with a invalid version range\" do\n      let(:data) { {'dependencies' => [{'name' => 'puppetlabsbadmodule', 'version_requirement' => '>= banana'}] }}\n\n      it \"raises an exception\" do\n        expect { subject }.to raise_error(ArgumentError)\n      end\n    end\n\n    context \"with duplicate dependencies\" do\n      let(:data) { {'dependencies' => [{'name' => 'puppetlabs-dupmodule', 'version_requirement' => '1.0.0'},\n        {'name' => 'puppetlabs-dupmodule', 'version_requirement' => '0.0.1'}] }\n      }\n\n      it \"raises an exception\" do\n        expect { subject }.to raise_error(ArgumentError)\n      end\n    end\n\n    context \"adding a duplicate dependency\" do\n      let(:data) { {'dependencies' => [{'name' => 'puppetlabs-origmodule', 'version_requirement' => '1.0.0'}] }}\n\n      it \"with a different version raises an exception\" do\n        metadata.add_dependency('puppetlabs-origmodule', '>= 0.0.1')\n        expect { subject }.to raise_error(ArgumentError)\n      end\n\n      it \"with the same version does not add another dependency\" do\n        metadata.add_dependency('puppetlabs-origmodule', '1.0.0')\n        expect(subject.dependencies.size).to eq(1)\n      end\n    end\n\n    context 'with valid data_provider' do\n      let(:data) { {'data_provider' => 'the_name'} }\n\n      it 'validates the provider correctly' do\n        expect { subject }.not_to raise_error\n      end\n\n      it 'returns the provider' do\n        expect(subject.data_provider).to eq('the_name')\n      end\n    end\n\n    context 'with invalid data_provider' do\n      let(:data) { }\n\n      it \"raises exception unless argument starts with a letter\" do\n        expect { metadata.update('data_provider' => '_the_name') }.to raise_error(ArgumentError, /field 'data_provider' must begin with a letter/)\n        expect { metadata.update('data_provider' => '') }.to raise_error(ArgumentError, /field 'data_provider' must begin with a letter/)\n      end\n\n      it \"raises exception if argument contains non-alphanumeric characters\" do\n        expect { metadata.update('data_provider' => 'the::name') }.to raise_error(ArgumentError, /field 'data_provider' contains non-alphanumeric characters/)\n      end\n\n      it \"raises exception unless argument is a string\" do\n        expect { metadata.update('data_provider' => 23) }.to raise_error(ArgumentError, /field 'data_provider' must be a string/)\n      end\n    end\n  end\n\n  describe '#dashed_name' do\n    it 'returns nil in the absence of a module name' do\n      expect(metadata.update('version' => '1.0.0').release_name).to be_nil\n    end\n\n    it 'returns a hyphenated string containing namespace and module name' do\n      data = metadata.update('name' => 'foo-bar')\n      expect(data.dashed_name).to eq('foo-bar')\n    end\n\n    it 'properly handles slash-separated names' do\n      data = metadata.update('name' => 'foo/bar')\n      expect(data.dashed_name).to eq('foo-bar')\n    end\n\n    it 'is unaffected by author name' do\n      data = metadata.update('name' => 'foo/bar', 'author' => 'me')\n      expect(data.dashed_name).to eq('foo-bar')\n    end\n  end\n\n  describe '#release_name' do\n    it 'returns nil in the absence of a module name' do\n      expect(metadata.update('version' => '1.0.0').release_name).to be_nil\n    end\n\n    it 'returns nil in the absence of a version' do\n      expect(metadata.update('name' => 'foo/bar').release_name).to be_nil\n    end\n\n    it 'returns a hyphenated string containing module name and version' do\n      data = metadata.update('name' => 'foo/bar', 'version' => '1.0.0')\n      expect(data.release_name).to eq('foo-bar-1.0.0')\n    end\n\n    it 'is unaffected by author name' do\n      data = metadata.update('name' => 'foo/bar', 'version' => '1.0.0', 'author' => 'me')\n      expect(data.release_name).to eq('foo-bar-1.0.0')\n    end\n  end\n\n  describe \"#to_hash\" do\n    subject { metadata.to_hash }\n\n    it \"contains the default set of keys\" do\n      expect(subject.keys.sort).to eq(%w[ name version author summary license source issues_url project_page dependencies data_provider].sort)\n    end\n\n    describe \"['license']\" do\n      it \"defaults to Apache 2\" do\n        expect(subject['license']).to eq(\"Apache-2.0\")\n      end\n    end\n\n    describe \"['dependencies']\" do\n      it \"defaults to an empty set\" do\n        expect(subject['dependencies']).to eq(Set.new)\n      end\n    end\n\n    context \"when updated with non-default data\" do\n      subject { metadata.update('license' => 'MIT', 'non-standard' => 'yup').to_hash }\n\n      it \"overrides the defaults\" do\n        expect(subject['license']).to eq('MIT')\n      end\n\n      it 'contains unanticipated values' do\n        expect(subject['non-standard']).to eq('yup')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/tar/gnu_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool'\n\ndescribe Puppet::ModuleTool::Tar::Gnu, unless: Puppet::Util::Platform.windows? do\n  let(:sourcedir)  { '/space path/the/src/dir' }\n  let(:sourcefile) { '/space path/the/module.tar.gz' }\n  let(:destdir)    { '/space path/the/dest/dir' }\n  let(:destfile)   { '/space path/the/dest/fi le.tar.gz' }\n\n  let(:safe_sourcedir)  { '/space\\ path/the/src/dir' }\n  let(:safe_sourcefile) { '/space\\ path/the/module.tar.gz' }\n  let(:safe_destdir)    { '/space\\ path/the/dest/dir' }\n  let(:safe_destfile)   { 'fi\\ le.tar.gz' }\n\n  it \"unpacks a tar file\" do\n    expect(Puppet::Util::Execution).to receive(:execute).with(\"gzip -dc #{safe_sourcefile} | tar --extract --no-same-owner --directory #{safe_destdir} --file -\")\n    expect(Puppet::Util::Execution).to receive(:execute).with(['find', destdir, '-type', 'd', '-exec', 'chmod', '755', '{}', '+'])\n    expect(Puppet::Util::Execution).to receive(:execute).with(['find', destdir, '-type', 'f', '-exec', 'chmod', 'u+rw,g+r,a-st', '{}', '+'])\n    expect(Puppet::Util::Execution).to receive(:execute).with(['chown', '-R', '<owner:group>', destdir])\n    subject.unpack(sourcefile, destdir, '<owner:group>')\n  end\n\n  it \"packs a tar file\" do\n    expect(Puppet::Util::Execution).to receive(:execute).with(\"tar cf - #{safe_sourcedir} | gzip -c > #{safe_destfile}\")\n    subject.pack(sourcedir, destfile)\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/tar/mini_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool'\n\ndescribe Puppet::ModuleTool::Tar::Mini, :if => (Puppet.features.minitar? and Puppet.features.zlib?) do\n  let(:sourcefile) { '/the/module.tar.gz' }\n  let(:destdir)    { File.expand_path '/the/dest/dir' }\n  let(:sourcedir)  { '/the/src/dir' }\n  let(:destfile)   { '/the/dest/file.tar.gz' }\n  let(:minitar)    { described_class.new }\n\n  class MockFileStatEntry\n    def initialize(mode = 0100)\n      @mode = mode\n    end\n  end\n\n  it \"unpacks a tar file with correct permissions\" do\n    entry = unpacks_the_entry(:file_start, 'thefile')\n\n    minitar.unpack(sourcefile, destdir, 'uid')\n    expect(entry.instance_variable_get(:@mode)).to eq(0755)\n  end\n\n  it \"does not allow an absolute path\" do\n    unpacks_the_entry(:file_start, '/thefile')\n\n    expect {\n      minitar.unpack(sourcefile, destdir, 'uid')\n    }.to raise_error(Puppet::ModuleTool::Errors::InvalidPathInPackageError,\n                     \"Attempt to install file with an invalid path into \\\"/thefile\\\" under \\\"#{destdir}\\\"\")\n  end\n\n  it \"does not allow a file to be written outside the destination directory\" do\n    unpacks_the_entry(:file_start, '../../thefile')\n\n    expect {\n      minitar.unpack(sourcefile, destdir, 'uid')\n    }.to raise_error(Puppet::ModuleTool::Errors::InvalidPathInPackageError,\n                     \"Attempt to install file with an invalid path into \\\"#{File.expand_path('/the/thefile')}\\\" under \\\"#{destdir}\\\"\")\n  end\n\n  it \"does not allow a directory to be written outside the destination directory\" do\n    unpacks_the_entry(:dir, '../../thedir')\n\n    expect {\n      minitar.unpack(sourcefile, destdir, 'uid')\n    }.to raise_error(Puppet::ModuleTool::Errors::InvalidPathInPackageError,\n                     \"Attempt to install file with an invalid path into \\\"#{File.expand_path('/the/thedir')}\\\" under \\\"#{destdir}\\\"\")\n  end\n\n  it \"unpacks on Windows\" do\n    unpacks_the_entry(:file_start, 'thefile', nil)\n\n    entry = minitar.unpack(sourcefile, destdir, 'uid')\n    # Windows does not use these permissions.\n    expect(entry.instance_variable_get(:@mode)).to eq(nil)\n  end\n\n  it \"packs a tar file\" do\n    writer = double('GzipWriter')\n\n    expect(Zlib::GzipWriter).to receive(:open).with(destfile).and_yield(writer)\n    stats = {:mode => 0222}\n    expect(Archive::Tar::Minitar).to receive(:pack).with(sourcedir, writer).and_yield(:file_start, 'abc', stats)\n\n    minitar.pack(sourcedir, destfile)\n  end\n\n  it \"packs a tar file on Windows\" do\n    writer = double('GzipWriter')\n\n    expect(Zlib::GzipWriter).to receive(:open).with(destfile).and_yield(writer)\n    expect(Archive::Tar::Minitar).to receive(:pack).with(sourcedir, writer).\n        and_yield(:file_start, 'abc', {:entry => MockFileStatEntry.new(nil)})\n\n    minitar.pack(sourcedir, destfile)\n  end\n\n  def unpacks_the_entry(type, name, mode = 0100)\n    reader = double('GzipReader')\n\n    expect(Zlib::GzipReader).to receive(:open).with(sourcefile).and_yield(reader)\n    expect(minitar).to receive(:find_valid_files).with(reader).and_return([name])\n    entry = MockFileStatEntry.new(mode)\n    expect(Archive::Tar::Minitar).to receive(:unpack).with(reader, destdir, [name], {:fsync => false}).\n        and_yield(type, name, {:entry => entry})\n    entry\n  end\n\n  describe \"Extracts tars with long and short pathnames\" do\n    let (:sourcetar) { fixtures('module.tar.gz') }\n    let (:longfilepath)  { \"puppetlabs-dsc-1.0.0/lib/puppet_x/dsc_resources/xWebAdministration/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof\" }\n    let (:shortfilepath) { \"puppetlabs-dsc-1.0.0/README.md\" }\n\n    it \"unpacks a tar with a short path length\" do\n      extractdir = PuppetSpec::Files.tmpdir('minitar')\n\n      minitar.unpack(sourcetar,extractdir,'module')\n      expect(File).to exist(File.expand_path(\"#{extractdir}/#{shortfilepath}\"))\n    end\n\n    it \"unpacks a tar with a long path length\" do\n      extractdir = PuppetSpec::Files.tmpdir('minitar')\n\n      minitar.unpack(sourcetar,extractdir,'module')\n      expect(File).to exist(File.expand_path(\"#{extractdir}/#{longfilepath}\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool/tar_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/module_tool/tar'\n\ndescribe Puppet::ModuleTool::Tar do\n  [\n    { :name => 'ObscureLinuxDistro', :win => false }, \n    { :name => 'Windows', :win => true }\n  ].each do |os|\n    it \"always prefers minitar if it and zlib are present, even with tar available\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(os[:name])\n      allow(Puppet::Util).to receive(:which).with('tar').and_return('/usr/bin/tar')\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(os[:win])\n      allow(Puppet).to receive(:features).and_return(double(:minitar? => true, :zlib? => true))\n\n      expect(described_class.instance).to be_a_kind_of Puppet::ModuleTool::Tar::Mini\n    end\n  end\n\n  it \"falls back to tar when minitar not present and not on Windows\" do\n    allow(Facter).to receive(:value).with('os.family').and_return('ObscureLinuxDistro')\n    allow(Puppet::Util).to receive(:which).with('tar').and_return('/usr/bin/tar')\n    allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n    allow(Puppet).to receive(:features).and_return(double(:minitar? => false))\n\n    expect(described_class.instance).to be_a_kind_of Puppet::ModuleTool::Tar::Gnu\n  end\n\n  it \"fails when there is no possible implementation\" do\n    allow(Facter).to receive(:value).with('os.family').and_return('Windows')\n    allow(Puppet::Util).to receive(:which).with('tar')\n    allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n    allow(Puppet).to receive(:features).and_return(double(:minitar? => false, :zlib? => false))\n\n    expect { described_class.instance }.to raise_error RuntimeError, /No suitable tar/\n  end\nend\n"
  },
  {
    "path": "spec/unit/module_tool_spec.rb",
    "content": "# encoding: UTF-8\n\nrequire 'spec_helper'\nrequire 'puppet/module_tool'\n\ndescribe Puppet::ModuleTool do\n  describe '.is_module_root?' do\n    it 'should return true if directory has a metadata.json file' do\n      expect(FileTest).to receive(:file?).with(have_attributes(to_s: '/a/b/c/metadata.json')).and_return(true)\n\n      expect(subject.is_module_root?(Pathname.new('/a/b/c'))).to be_truthy\n    end\n\n    it 'should return false if directory does not have a metadata.json file' do\n      expect(FileTest).to receive(:file?).with(have_attributes(to_s: '/a/b/c/metadata.json')).and_return(false)\n\n      expect(subject.is_module_root?(Pathname.new('/a/b/c'))).to be_falsey\n    end\n  end\n\n  describe '.find_module_root' do\n    let(:sample_path) { Pathname.new('/a/b/c').expand_path }\n\n    it 'should return the first path as a pathname when it contains a module file' do\n      expect(Puppet::ModuleTool).to receive(:is_module_root?).with(sample_path).\n        and_return(true)\n\n      expect(subject.find_module_root(sample_path)).to eq(sample_path)\n    end\n\n    it 'should return a parent path as a pathname when it contains a module file' do\n      expect(Puppet::ModuleTool).to receive(:is_module_root?).with(have_attributes(to_s: File.expand_path('/a/b/c'))).and_return(false)\n      expect(Puppet::ModuleTool).to receive(:is_module_root?).with(have_attributes(to_s: File.expand_path('/a/b'))).and_return(true)\n\n      expect(subject.find_module_root(sample_path)).to eq(Pathname.new('/a/b').expand_path)\n    end\n\n    it 'should return nil when no module root can be found' do\n      expect(Puppet::ModuleTool).to receive(:is_module_root?).at_least(:once).and_return(false)\n      expect(subject.find_module_root(sample_path)).to be_nil\n    end\n  end\n\n  describe '.format_tree' do\n    it 'should return an empty tree when given an empty list' do\n      expect(subject.format_tree([])).to eq('')\n    end\n\n    it 'should return a shallow when given a list without dependencies' do\n      list = [ { :text => 'first' }, { :text => 'second' }, { :text => 'third' } ]\n      expect(subject.format_tree(list)).to eq <<-TREE\n├── first\n├── second\n└── third\nTREE\n    end\n\n    it 'should return a deeply nested tree when given a list with deep dependencies' do\n      list = [\n        {\n          :text => 'first',\n          :dependencies => [\n            {\n              :text => 'second',\n              :dependencies => [\n                { :text => 'third' }\n              ]\n            }\n          ]\n        },\n      ]\n      expect(subject.format_tree(list)).to eq <<-TREE\n└─┬ first\n  └─┬ second\n    └── third\nTREE\n    end\n\n    it 'should show connectors when deep dependencies are not on the last node of the top level' do\n      list = [\n        {\n          :text => 'first',\n          :dependencies => [\n            {\n              :text => 'second',\n              :dependencies => [\n                { :text => 'third' }\n              ]\n            }\n          ]\n        },\n        { :text => 'fourth' }\n      ]\n      expect(subject.format_tree(list)).to eq <<-TREE\n├─┬ first\n│ └─┬ second\n│   └── third\n└── fourth\nTREE\n    end\n\n    it 'should show connectors when deep dependencies are not on the last node of any level' do\n      list = [\n        {\n          :text => 'first',\n          :dependencies => [\n            {\n              :text => 'second',\n              :dependencies => [\n                { :text => 'third' }\n              ]\n            },\n            { :text => 'fourth' }\n          ]\n        }\n      ]\n      expect(subject.format_tree(list)).to eq <<-TREE\n└─┬ first\n  ├─┬ second\n  │ └── third\n  └── fourth\nTREE\n    end\n\n    it 'should show connectors in every case when deep dependencies are not on the last node' do\n      list = [\n        {\n          :text => 'first',\n          :dependencies => [\n            {\n              :text => 'second',\n              :dependencies => [\n                { :text => 'third' }\n              ]\n            },\n            { :text => 'fourth' }\n          ]\n        },\n        { :text => 'fifth' }\n      ]\n      expect(subject.format_tree(list)).to eq <<-TREE\n├─┬ first\n│ ├─┬ second\n│ │ └── third\n│ └── fourth\n└── fifth\nTREE\n    end\n  end\n\n  describe '.set_option_defaults' do\n    let(:options) { {} }\n    let(:modulepath) { ['/env/module/path', '/global/module/path'] }\n    let(:environment_name) { :current_environment }\n    let(:environment) { Puppet::Node::Environment.create(environment_name, modulepath) }\n\n    subject do\n      described_class.set_option_defaults(options)\n      options\n    end\n\n    around do |example|\n      envs = Puppet::Environments::Static.new(environment)\n\n      Puppet.override(:environments => envs) do\n        example.run\n      end\n    end\n\n    describe ':environment' do\n      context 'as String' do\n        let(:options) { { :environment => \"#{environment_name}\" } }\n\n        it 'assigns the environment with the given name to :environment_instance' do\n          expect(subject).to include :environment_instance => environment\n        end\n      end\n\n      context 'as Symbol' do\n        let(:options) { { :environment => :\"#{environment_name}\" } }\n\n        it 'assigns the environment with the given name to :environment_instance' do\n          expect(subject).to include :environment_instance => environment\n        end\n      end\n\n      context 'as Puppet::Node::Environment' do\n        let(:env) { Puppet::Node::Environment.create('anonymous', []) }\n        let(:options) { { :environment => env } }\n\n        it 'assigns the given environment to :environment_instance' do\n          expect(subject).to include :environment_instance => env\n        end\n      end\n    end\n\n    describe ':modulepath' do\n      let(:options) do\n        { :modulepath => %w[bar foo baz].join(File::PATH_SEPARATOR) }\n      end\n\n      let(:paths) { options[:modulepath].split(File::PATH_SEPARATOR).map { |dir| File.expand_path(dir) } }\n\n      it 'is expanded to an absolute path' do\n        expect(subject[:environment_instance].full_modulepath).to eql paths\n      end\n\n      it 'is used to compute :target_dir' do\n        expect(subject).to include :target_dir => paths.first\n      end\n\n      context 'conflicts with :environment' do\n        let(:options) do\n          { :modulepath => %w[bar foo baz].join(File::PATH_SEPARATOR), :environment => environment_name }\n        end\n\n        it 'replaces the modulepath of the :environment_instance' do\n          expect(subject[:environment_instance].full_modulepath).to eql paths\n        end\n\n        it 'is used to compute :target_dir' do\n          expect(subject).to include :target_dir => paths.first\n        end\n      end\n    end\n\n    describe ':target_dir' do\n      let(:options) do\n        { :target_dir => 'foo' }\n      end\n\n      let(:target) { File.expand_path(options[:target_dir]) }\n\n      it 'is expanded to an absolute path' do\n        expect(subject).to include :target_dir => target\n      end\n\n      it 'is prepended to the modulepath of the :environment_instance' do\n        expect(subject[:environment_instance].full_modulepath.first).to eql target\n      end\n\n      context 'conflicts with :modulepath' do\n        let(:options) do\n          { :target_dir => 'foo', :modulepath => %w[bar foo baz].join(File::PATH_SEPARATOR) }\n        end\n\n        it 'is prepended to the modulepath of the :environment_instance' do\n          expect(subject[:environment_instance].full_modulepath.first).to eql target\n        end\n\n        it 'shares the provided :modulepath via the :environment_instance' do\n          paths = %w[foo] + options[:modulepath].split(File::PATH_SEPARATOR)\n          paths.map! { |dir| File.expand_path(dir) }\n          expect(subject[:environment_instance].full_modulepath).to eql paths\n        end\n      end\n\n      context 'conflicts with :environment' do\n        let(:options) do\n          { :target_dir => 'foo', :environment => environment_name }\n        end\n\n        it 'is prepended to the modulepath of the :environment_instance' do\n          expect(subject[:environment_instance].full_modulepath.first).to eql target\n        end\n\n        it 'shares the provided :modulepath via the :environment_instance' do\n          paths = %w[foo] + environment.full_modulepath\n          paths.map! { |dir| File.expand_path(dir) }\n          expect(subject[:environment_instance].full_modulepath).to eql paths\n        end\n      end\n\n      context 'when not passed' do\n        it 'is populated with the first component of the modulepath' do\n          expect(subject).to include :target_dir => subject[:environment_instance].full_modulepath.first\n        end\n      end\n    end\n  end\n\n  describe '.parse_module_dependency' do\n    it 'parses a dependency without a version range expression' do\n      name, range, expr = subject.parse_module_dependency('source', 'name' => 'foo-bar')\n      expect(name).to eql('foo-bar')\n      expect(range).to eql(SemanticPuppet::VersionRange.parse('>= 0.0.0'))\n      expect(expr).to eql('>= 0.0.0')\n    end\n\n    it 'parses a dependency with a version range expression' do\n      name, range, expr = subject.parse_module_dependency('source', 'name' => 'foo-bar', 'version_requirement' => '1.2.x')\n      expect(name).to eql('foo-bar')\n      expect(range).to eql(SemanticPuppet::VersionRange.parse('1.2.x'))\n      expect(expr).to eql('1.2.x')\n    end\n\n    it 'does not raise an error on invalid version range expressions' do\n      name, range, expr = subject.parse_module_dependency('source', 'name' => 'foo-bar', 'version_requirement' => 'nope')\n      expect(name).to eql('foo-bar')\n      expect(range).to eql(SemanticPuppet::VersionRange::EMPTY_RANGE)\n      expect(expr).to eql('nope')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/authconfig_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/network/authconfig'\n\ndescribe Puppet::Network::AuthConfig do\n  it \"accepts an auth provider class\" do\n    Puppet::Network::AuthConfig.authprovider_class = Object\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/authorization_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/network/authorization'\n\ndescribe Puppet::Network::Authorization do\n  it \"accepts an auth config loader class\" do\n    Puppet::Network::Authorization.authconfigloader_class = Object\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/format_handler_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/format_handler'\n\ndescribe Puppet::Network::FormatHandler do\n  before(:each) do\n    @saved_formats = Puppet::Network::FormatHandler.instance_variable_get(:@formats).dup\n    Puppet::Network::FormatHandler.instance_variable_set(:@formats, {})\n  end\n\n  after(:each) do\n    Puppet::Network::FormatHandler.instance_variable_set(:@formats, @saved_formats)\n  end\n\n  describe \"when creating formats\" do\n    it \"should instance_eval any block provided when creating a format\" do\n      format = Puppet::Network::FormatHandler.create(:test_format) do\n        def asdfghjkl; end\n      end\n      expect(format).to respond_to(:asdfghjkl)\n    end\n  end\n\n  describe \"when retrieving formats\" do\n    let!(:format) { Puppet::Network::FormatHandler.create(:the_format, :extension => \"foo\", :mime => \"foo/bar\") }\n\n    it \"should be able to retrieve a format by name\" do\n      expect(Puppet::Network::FormatHandler.format(:the_format)).to equal(format)\n    end\n\n    it \"should be able to retrieve a format by extension\" do\n      expect(Puppet::Network::FormatHandler.format_by_extension(\"foo\")).to equal(format)\n    end\n\n    it \"should return nil if asked to return a format by an unknown extension\" do\n      expect(Puppet::Network::FormatHandler.format_by_extension(\"yayness\")).to be_nil\n    end\n\n    it \"should be able to retrieve formats by name irrespective of case\" do\n      expect(Puppet::Network::FormatHandler.format(:The_Format)).to equal(format)\n    end\n\n    it \"should be able to retrieve a format by mime type\" do\n      expect(Puppet::Network::FormatHandler.mime(\"foo/bar\")).to equal(format)\n    end\n\n    it \"should be able to retrieve a format by mime type irrespective of case\" do\n      expect(Puppet::Network::FormatHandler.mime(\"Foo/Bar\")).to equal(format)\n    end\n  end\n\n  describe \"#most_suitable_formats_for\" do\n    before :each do\n      Puppet::Network::FormatHandler.create(:one, :extension => \"foo\", :mime => \"text/one\")\n      Puppet::Network::FormatHandler.create(:two, :extension => \"bar\", :mime => \"application/two\")\n    end\n\n    let(:format_one) { Puppet::Network::FormatHandler.format(:one) }\n    let(:format_two) { Puppet::Network::FormatHandler.format(:two) }\n\n    def suitable_in_setup_formats(accepted)\n      Puppet::Network::FormatHandler.most_suitable_formats_for(accepted, [:one, :two])\n    end\n\n    it \"finds the most preferred format when anything is acceptable\" do\n      expect(Puppet::Network::FormatHandler.most_suitable_formats_for([\"*/*\"], [:two, :one])).to eq([format_two])\n    end\n\n    it \"finds no format when none are acceptable\" do\n      expect(suitable_in_setup_formats([\"three\"])).to eq([])\n    end\n\n    it \"returns only the accepted and supported format\" do\n      expect(suitable_in_setup_formats([\"three\", \"two\"])).to eq([format_two])\n    end\n\n    it \"returns only accepted and supported formats, in order of accepted\" do\n      expect(suitable_in_setup_formats([\"three\", \"two\", \"one\"])).to eq([format_two, format_one])\n    end\n\n    it \"allows specifying acceptable formats by mime type\" do\n      expect(suitable_in_setup_formats([\"text/one\"])).to eq([format_one])\n    end\n\n    it \"ignores quality specifiers\" do\n      expect(suitable_in_setup_formats([\"two;q=0.8\", \"text/one;q=0.9\"])).to eq([format_two, format_one])\n    end\n\n    it \"allows specifying acceptable formats by canonical name\" do\n      expect(suitable_in_setup_formats([:one])).to eq([format_one])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/format_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/format'\n\n# A class with all of the necessary\n# hooks.\nclass FormatRenderer\n  def self.to_multiple_my_format(list)\n  end\n\n  def self.from_multiple_my_format(text)\n  end\n\n  def self.from_my_format(text)\n  end\n\n  def to_my_format\n  end\nend\n\ndescribe Puppet::Network::Format do\n  describe \"when initializing\" do\n    it \"should require a name\" do\n      expect { Puppet::Network::Format.new }.to raise_error(ArgumentError)\n    end\n\n    it \"should be able to provide its name\" do\n      expect(Puppet::Network::Format.new(:my_format).name).to eq(:my_format)\n    end\n\n    it \"should always convert its name to a downcased symbol\" do\n      expect(Puppet::Network::Format.new(:My_Format).name).to eq(:my_format)\n    end\n\n    it \"should be able to set its downcased mime type at initialization\" do\n      format = Puppet::Network::Format.new(:my_format, :mime => \"Foo/Bar\")\n      expect(format.mime).to eq(\"foo/bar\")\n    end\n\n    it \"should default to text plus the name of the format as the mime type\" do\n      expect(Puppet::Network::Format.new(:my_format).mime).to eq(\"text/my_format\")\n    end\n\n    it \"should fail if unsupported options are provided\" do\n      expect { Puppet::Network::Format.new(:my_format, :foo => \"bar\") }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"instances\" do\n    before do\n      @format = Puppet::Network::Format.new(:my_format)\n    end\n\n    it \"should support being confined\" do\n      expect(@format).to respond_to(:confine)\n    end\n\n    it \"should not be considered suitable if confinement conditions are not met\" do\n      @format.confine :true => false\n      expect(@format).not_to be_suitable\n    end\n\n    it \"should be able to determine if a class is supported\" do\n      expect(@format).to respond_to(:supported?)\n    end\n\n    it \"should consider a class to be supported if it has the individual and multiple methods for rendering and interning\" do\n      expect(@format).to be_supported(FormatRenderer)\n    end\n\n    it \"should default to its required methods being the individual and multiple methods for rendering and interning\" do\n      expect(Puppet::Network::Format.new(:foo).required_methods.sort { |a,b| a.to_s <=> b.to_s }).to eq([:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].sort { |a,b| a.to_s <=> b.to_s })\n    end\n\n    it \"should consider a class supported if the provided class has all required methods present\" do\n      format = Puppet::Network::Format.new(:foo)\n      [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method|\n        expect(format).to receive(:required_method_present?).with(method, String, anything).and_return(true)\n      end\n\n      expect(format).to be_required_methods_present(String)\n    end\n\n    it \"should consider a class not supported if any required methods are missing from the provided class\" do\n      format = Puppet::Network::Format.new(:foo)\n      allow(format).to receive(:required_method_present?).and_return(true)\n      expect(format).to receive(:required_method_present?).with(:intern_method, anything, anything).and_return(false)\n      expect(format).not_to be_required_methods_present(String)\n    end\n\n    it \"should be able to specify the methods required for support\" do\n      expect(Puppet::Network::Format.new(:foo, :required_methods => [:render_method, :intern_method]).required_methods).to eq([:render_method, :intern_method])\n    end\n\n    it \"should only test for required methods if specific methods are specified as required\" do\n      format = Puppet::Network::Format.new(:foo, :required_methods => [:intern_method])\n      expect(format).to receive(:required_method_present?).with(:intern_method, anything, anything)\n\n      format.required_methods_present?(String)\n    end\n\n    it \"should not consider a class supported unless the format is suitable\" do\n      expect(@format).to receive(:suitable?).and_return(false)\n      expect(@format).not_to be_supported(FormatRenderer)\n    end\n\n    it \"should always downcase mimetypes\" do\n      @format.mime = \"Foo/Bar\"\n      expect(@format.mime).to eq(\"foo/bar\")\n    end\n\n    it \"should support having a weight\" do\n      expect(@format).to respond_to(:weight)\n    end\n\n    it \"should default to a weight of of 5\" do\n      expect(@format.weight).to eq(5)\n    end\n\n    it \"should be able to override its weight at initialization\" do\n      expect(Puppet::Network::Format.new(:foo, :weight => 1).weight).to eq(1)\n    end\n\n    it \"should default to its extension being equal to its name\" do\n      expect(Puppet::Network::Format.new(:foo).extension).to eq(\"foo\")\n    end\n\n    it \"should support overriding the extension\" do\n      expect(Puppet::Network::Format.new(:foo, :extension => \"bar\").extension).to eq(\"bar\")\n    end\n\n    it \"doesn't support charset by default\" do\n      expect(Puppet::Network::Format.new(:foo).charset).to be_nil\n    end\n\n    it \"allows charset to be set to 'utf-8'\" do\n      expect(Puppet::Network::Format.new(:foo, :charset => Encoding::UTF_8).charset).to eq(Encoding::UTF_8)\n    end\n\n    [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method|\n      it \"should allow assignment of the #{method}\" do\n        expect(Puppet::Network::Format.new(:foo, method => :foo).send(method)).to eq(:foo)\n      end\n    end\n  end\n\n  describe \"when converting between instances and formatted text\" do\n    before do\n      @format = Puppet::Network::Format.new(:my_format)\n      @instance = FormatRenderer.new\n    end\n\n    it \"should have a method for rendering a single instance\" do\n      expect(@format).to respond_to(:render)\n    end\n\n    it \"should have a method for rendering multiple instances\" do\n      expect(@format).to respond_to(:render_multiple)\n    end\n\n    it \"should have a method for interning text\" do\n      expect(@format).to respond_to(:intern)\n    end\n\n    it \"should have a method for interning text into multiple instances\" do\n      expect(@format).to respond_to(:intern_multiple)\n    end\n\n    it \"should return the results of calling the instance-specific render method if the method is present\" do\n      expect(@instance).to receive(:to_my_format).and_return(\"foo\")\n      expect(@format.render(@instance)).to eq(\"foo\")\n    end\n\n    it \"should return the results of calling the class-specific render_multiple method if the method is present\" do\n      expect(@instance.class).to receive(:to_multiple_my_format).and_return([\"foo\"])\n      expect(@format.render_multiple([@instance])).to eq([\"foo\"])\n    end\n\n    it \"should return the results of calling the class-specific intern method if the method is present\" do\n      expect(FormatRenderer).to receive(:from_my_format).with(\"foo\").and_return(@instance)\n      expect(@format.intern(FormatRenderer, \"foo\")).to equal(@instance)\n    end\n\n    it \"should return the results of calling the class-specific intern_multiple method if the method is present\" do\n      expect(FormatRenderer).to receive(:from_multiple_my_format).with(\"foo\").and_return([@instance])\n      expect(@format.intern_multiple(FormatRenderer, \"foo\")).to eq([@instance])\n    end\n\n    it \"should fail if asked to render and the instance does not respond to 'to_<format>'\" do\n      expect { @format.render(\"foo\") }.to raise_error(NotImplementedError)\n    end\n\n    it \"should fail if asked to intern and the class does not respond to 'from_<format>'\" do\n      expect { @format.intern(String, \"foo\") }.to raise_error(NotImplementedError)\n    end\n\n    it \"should fail if asked to intern multiple and the class does not respond to 'from_multiple_<format>'\" do\n      expect { @format.intern_multiple(String, \"foo\") }.to raise_error(NotImplementedError)\n    end\n\n    it \"should fail if asked to render multiple and the instance does not respond to 'to_multiple_<format>'\" do\n      expect { @format.render_multiple([\"foo\", \"bar\"]) }.to raise_error(NotImplementedError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/format_support_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/format_handler'\nrequire 'puppet/network/format_support'\n\nclass FormatTester\n  include Puppet::Network::FormatSupport\nend\n\ndescribe Puppet::Network::FormatHandler do\n  before(:each) do\n    @saved_formats = Puppet::Network::FormatHandler.instance_variable_get(:@formats).dup\n    Puppet::Network::FormatHandler.instance_variable_set(:@formats, {})\n  end\n\n  after(:each) do\n    Puppet::Network::FormatHandler.instance_variable_set(:@formats, @saved_formats)\n  end\n\n  describe \"when listing formats\" do\n    before(:each) do\n      one = Puppet::Network::FormatHandler.create(:one, :weight => 1)\n      allow(one).to receive(:supported?).and_return(true)\n      two = Puppet::Network::FormatHandler.create(:two, :weight => 6)\n      allow(two).to receive(:supported?).and_return(true)\n      three = Puppet::Network::FormatHandler.create(:three, :weight => 2)\n      allow(three).to receive(:supported?).and_return(true)\n      four = Puppet::Network::FormatHandler.create(:four, :weight => 8)\n      allow(four).to receive(:supported?).and_return(false)\n    end\n\n    it \"should return all supported formats in decreasing order of weight\" do\n      expect(FormatTester.supported_formats).to eq([:two, :three, :one])\n    end\n  end\n\n  it \"should return the first format as the default format\" do\n    expect(FormatTester).to receive(:supported_formats).and_return [:one, :two]\n    expect(FormatTester.default_format).to eq(:one)\n  end\n\n  describe \"with a preferred serialization format setting\" do\n    before do\n      one = Puppet::Network::FormatHandler.create(:one, :weight => 1)\n      allow(one).to receive(:supported?).and_return(true)\n      two = Puppet::Network::FormatHandler.create(:two, :weight => 6)\n      allow(two).to receive(:supported?).and_return(true)\n    end\n\n    describe \"that is supported\" do\n      before do\n        Puppet[:preferred_serialization_format] = :one\n      end\n\n      it \"should return the preferred serialization format first\" do\n        expect(FormatTester.supported_formats).to eq([:one, :two])\n      end\n    end\n\n    describe \"that is not supported\" do\n      before do\n        Puppet[:preferred_serialization_format] = :unsupported\n      end\n\n      it \"should return the default format first\" do\n        expect(FormatTester.supported_formats).to eq([:two, :one])\n      end\n\n      it \"should log a debug message\" do\n        Puppet[:log_level] = 'debug'\n        expect(Puppet).to receive(:debug) { |&b| expect(b.call).to eq(\"Value of 'preferred_serialization_format' (unsupported) is invalid for FormatTester, using default (two)\") }\n        expect(Puppet).to receive(:debug) { |&b| expect(b.call).to eq(\"FormatTester supports formats: two one\") }\n        FormatTester.supported_formats\n      end\n    end\n  end\n\n  describe \"when using formats\" do\n    let(:format) { Puppet::Network::FormatHandler.create(:my_format, :mime => \"text/myformat\") }\n\n    it \"should use the Format to determine whether a given format is supported\" do\n      expect(format).to receive(:supported?).with(FormatTester)\n      FormatTester.support_format?(:my_format)\n    end\n\n    it \"should call the format-specific converter when asked to convert from a given format\" do\n      expect(format).to receive(:intern).with(FormatTester, \"mydata\")\n      FormatTester.convert_from(:my_format, \"mydata\")\n    end\n\n    it \"should call the format-specific converter when asked to convert from a given format by mime-type\" do\n      expect(format).to receive(:intern).with(FormatTester, \"mydata\")\n      FormatTester.convert_from(\"text/myformat\", \"mydata\")\n    end\n\n    it \"should call the format-specific converter when asked to convert from a given format by format instance\" do\n      expect(format).to receive(:intern).with(FormatTester, \"mydata\")\n      FormatTester.convert_from(format, \"mydata\")\n    end\n\n    it \"should raise a FormatError when an exception is encountered when converting from a format\" do\n      expect(format).to receive(:intern).with(FormatTester, \"mydata\").and_raise(\"foo\")\n      expect do\n        FormatTester.convert_from(:my_format, \"mydata\")\n      end.to raise_error(\n        Puppet::Network::FormatHandler::FormatError,\n        'Could not intern from my_format: foo'\n      )\n    end\n\n    it \"should be able to use a specific hook for converting into multiple instances\" do\n      expect(format).to receive(:intern_multiple).with(FormatTester, \"mydata\")\n\n      FormatTester.convert_from_multiple(:my_format, \"mydata\")\n    end\n\n    it \"should raise a FormatError when an exception is encountered when converting multiple items from a format\" do\n      expect(format).to receive(:intern_multiple).with(FormatTester, \"mydata\").and_raise(\"foo\")\n      expect do\n        FormatTester.convert_from_multiple(:my_format, \"mydata\")\n      end.to raise_error(Puppet::Network::FormatHandler::FormatError, 'Could not intern_multiple from my_format: foo')\n    end\n\n    it \"should be able to use a specific hook for rendering multiple instances\" do\n      expect(format).to receive(:render_multiple).with(\"mydata\")\n\n      FormatTester.render_multiple(:my_format, \"mydata\")\n    end\n\n    it \"should raise a FormatError when an exception is encountered when rendering multiple items into a format\" do\n      expect(format).to receive(:render_multiple).with(\"mydata\").and_raise(\"foo\")\n      expect do\n        FormatTester.render_multiple(:my_format, \"mydata\")\n      end.to raise_error(Puppet::Network::FormatHandler::FormatError, 'Could not render_multiple to my_format: foo')\n    end\n  end\n\n  describe \"when an instance\" do\n    let(:format) { Puppet::Network::FormatHandler.create(:foo, :mime => \"text/foo\") }\n\n    it \"should list as supported a format that reports itself supported\" do\n      expect(format).to receive(:supported?).and_return(true)\n      expect(FormatTester.new.support_format?(:foo)).to be_truthy\n    end\n\n    it \"should raise a FormatError when a rendering error is encountered\" do\n      tester = FormatTester.new\n      expect(format).to receive(:render).with(tester).and_raise(\"eh\")\n\n      expect do\n        tester.render(:foo)\n      end.to raise_error(Puppet::Network::FormatHandler::FormatError, 'Could not render to foo: eh')\n    end\n\n    it \"should call the format-specific converter when asked to convert to a given format\" do\n      tester = FormatTester.new\n      expect(format).to receive(:render).with(tester).and_return(\"foo\")\n\n      expect(tester.render(:foo)).to eq(\"foo\")\n    end\n\n    it \"should call the format-specific converter when asked to convert to a given format by mime-type\" do\n      tester = FormatTester.new\n      expect(format).to receive(:render).with(tester).and_return(\"foo\")\n\n      expect(tester.render(\"text/foo\")).to eq(\"foo\")\n    end\n\n    it \"should call the format converter when asked to convert to a given format instance\" do\n      tester = FormatTester.new\n      expect(format).to receive(:render).with(tester).and_return(\"foo\")\n\n      expect(tester.render(format)).to eq(\"foo\")\n    end\n\n    it \"should render to the default format if no format is provided when rendering\" do\n      expect(FormatTester).to receive(:default_format).and_return(:foo)\n      tester = FormatTester.new\n\n      expect(format).to receive(:render).with(tester)\n      tester.render\n    end\n\n    it \"should call the format-specific converter when asked for the mime-type of a given format\" do\n      tester = FormatTester.new\n      expect(format).to receive(:mime).and_return(\"text/foo\")\n\n      expect(tester.mime(:foo)).to eq(\"text/foo\")\n    end\n\n    it \"should return the default format mime-type if no format is provided\" do\n      expect(FormatTester).to receive(:default_format).and_return(:foo)\n      tester = FormatTester.new\n\n      expect(format).to receive(:mime).and_return(\"text/foo\")\n      expect(tester.mime).to eq(\"text/foo\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/formats_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/formats'\nrequire 'puppet/network/format_support'\n\nclass FormatsTest\n  include Puppet::Network::FormatSupport\n\n  attr_accessor :string\n  def ==(other)\n    string == other.string\n  end\n\n  def self.from_data_hash(data)\n    new(data['string'])\n  end\n\n  def initialize(string)\n    @string = string\n  end\n\n  def to_data_hash(*args)\n    {\n      'string' => @string\n    }\n  end\n\n  def to_binary\n    string\n  end\n\n  def self.from_binary(data)\n    self.new(data)\n  end\nend\n\ndescribe \"Puppet Network Format\" do\n  it \"should include a msgpack format\", :if => Puppet.features.msgpack? do\n    expect(Puppet::Network::FormatHandler.format(:msgpack)).not_to be_nil\n  end\n\n  describe \"msgpack\", :if => Puppet.features.msgpack? do\n    let(:msgpack) { Puppet::Network::FormatHandler.format(:msgpack) }\n\n    it \"should have its mime type set to application/x-msgpack\" do\n      expect(msgpack.mime).to eq(\"application/x-msgpack\")\n    end\n\n    it \"should have a nil charset\" do\n      expect(msgpack.charset).to be_nil\n    end\n\n    it \"should have a weight of 20\" do\n      expect(msgpack.weight).to eq(20)\n    end\n\n    it \"should fail when one element does not have a from_data_hash\" do\n      expect do\n        msgpack.intern_multiple(Hash, MessagePack.pack([\"foo\"]))\n      end.to raise_error(NoMethodError)\n    end\n\n    it \"should be able to serialize a catalog\" do\n      cat = Puppet::Resource::Catalog.new('foo', Puppet::Node::Environment.create(:testing, []))\n      cat.add_resource(Puppet::Resource.new(:file, 'my_file'))\n      catunpack = MessagePack.unpack(cat.to_msgpack)\n      expect(catunpack).to include(\n        \"tags\"=>[],\n        \"name\"=>\"foo\",\n        \"version\"=>nil,\n        \"environment\"=>\"testing\",\n        \"edges\"=>[],\n        \"classes\"=>[]\n      )\n      expect(catunpack[\"resources\"][0]).to include(\n        \"type\"=>\"File\",\n        \"title\"=>\"my_file\",\n        \"exported\"=>false\n      )\n      expect(catunpack[\"resources\"][0][\"tags\"]).to include(\n        \"file\",\n        \"my_file\"\n      )\n    end\n  end\n\n  describe \"yaml\" do\n    let(:yaml) { Puppet::Network::FormatHandler.format(:yaml) }\n\n    it \"should have its mime type set to text/yaml\" do\n      expect(yaml.mime).to eq(\"text/yaml\")\n    end\n\n    # we shouldn't be using yaml on the network\n    it \"should have a nil charset\" do\n      expect(yaml.charset).to be_nil\n    end\n\n    it \"should be supported on Strings\" do\n      expect(yaml).to be_supported(String)\n    end\n\n    it \"should render by calling 'to_yaml' on the instance\" do\n      instance = double('instance')\n      expect(instance).to receive(:to_yaml).and_return(\"foo\")\n      expect(yaml.render(instance)).to eq(\"foo\")\n    end\n\n    it \"should render multiple instances by calling 'to_yaml' on the array\" do\n      instances = [double('instance')]\n      expect(instances).to receive(:to_yaml).and_return(\"foo\")\n      expect(yaml.render_multiple(instances)).to eq(\"foo\")\n    end\n\n    it \"should deserialize YAML\" do\n      expect(yaml.intern(String, YAML.dump(\"foo\"))).to eq(\"foo\")\n    end\n\n    it \"should deserialize symbols as strings\" do\n      expect { yaml.intern(String, YAML.dump(:foo))}.to raise_error(Puppet::Network::FormatHandler::FormatError)\n    end\n\n    it \"should skip data_to_hash if data is already an instance of the specified class\" do\n      # The rest terminus for the report indirected type relies on this behavior\n      data = YAML.dump([1, 2])\n      instance = yaml.intern(Array, data)\n      expect(instance).to eq([1, 2])\n    end\n\n    it \"should load from yaml when deserializing an array\" do\n      text = YAML.dump([\"foo\"])\n      expect(yaml.intern_multiple(String, text)).to eq([\"foo\"])\n    end\n\n    it \"fails intelligibly instead of calling to_json with something other than a hash\" do\n      expect do\n        yaml.intern(Puppet::Node, '')\n      end.to raise_error(Puppet::Network::FormatHandler::FormatError, /did not contain a valid instance/)\n    end\n\n    it \"fails intelligibly when intern_multiple is called and yaml doesn't decode to an array\" do\n      expect do\n        yaml.intern_multiple(Puppet::Node, '')\n      end.to raise_error(Puppet::Network::FormatHandler::FormatError, /did not contain a collection/)\n    end\n\n    it \"fails intelligibly instead of calling to_json with something other than a hash when interning multiple\" do\n      expect do\n        yaml.intern_multiple(Puppet::Node, YAML.dump([\"hello\"]))\n      end.to raise_error(Puppet::Network::FormatHandler::FormatError, /did not contain a valid instance/)\n    end\n\n    it 'accepts indirected classes' do\n      [\n        Puppet::Node::Facts.new('foo', {}),\n        Puppet::Node.new('foo'),\n        Puppet::Resource.new('File', '/foo'),\n        Puppet::Transaction::Report.new('foo'),\n        Puppet::Resource::Catalog.new\n      ].each { |obj| yaml.intern(obj.class, YAML.dump(obj.to_data_hash)) }\n    end\n\n    it 'raises when interning an instance of an unacceptable indirected type' do\n      obj = :something\n\n      expect {\n        yaml.intern(obj.class, YAML.dump(obj))\n      }.to raise_error(Puppet::Network::FormatHandler::FormatError, /Tried to load unspecified class: Symbol/)\n    end\n\n    it 'raises when interning multple instances of an unacceptable indirected type' do\n      obj = :something\n\n      expect {\n        yaml.intern_multiple(obj.class, YAML.dump([obj]))\n      }.to raise_error(Puppet::Network::FormatHandler::FormatError, /Tried to load unspecified class: Symbol/)\n    end\n  end\n\n  describe \"plaintext\" do\n    let(:text) { Puppet::Network::FormatHandler.format(:s) }\n\n    it \"should have its mimetype set to text/plain\" do\n      expect(text.mime).to eq(\"text/plain\")\n    end\n\n    it \"should use 'utf-8' charset\" do\n      expect(text.charset).to eq(Encoding::UTF_8)\n    end\n\n    it \"should use 'txt' as its extension\" do\n      expect(text.extension).to eq(\"txt\")\n    end\n  end\n\n  describe \"dot\" do\n    let(:dot) { Puppet::Network::FormatHandler.format(:dot) }\n\n    it \"should have its mimetype set to text/dot\" do\n      expect(dot.mime).to eq(\"text/dot\")\n    end\n  end\n\n  describe Puppet::Network::FormatHandler.format(:binary) do\n    let(:binary) { Puppet::Network::FormatHandler.format(:binary) }\n\n    it \"should exist\" do\n      expect(binary).not_to be_nil\n    end\n\n    it \"should have its mimetype set to application/octet-stream\" do\n      expect(binary.mime).to eq(\"application/octet-stream\")\n    end\n\n    it \"should have a nil charset\" do\n      expect(binary.charset).to be_nil\n    end\n\n    it \"should not be supported by default\" do\n      expect(binary).to_not be_supported(String)\n    end\n\n    it \"should render an instance as binary\" do\n      instance = FormatsTest.new(\"foo\")\n      expect(binary.render(instance)).to eq(\"foo\")\n    end\n\n    it \"should intern an instance from a JSON hash\" do\n      instance = binary.intern(FormatsTest, \"foo\")\n      expect(instance.string).to eq(\"foo\")\n    end\n\n    it \"should fail if its multiple_render method is used\" do\n      expect {\n        binary.render_multiple(\"foo\")\n      }.to raise_error(NotImplementedError, /can not render multiple instances to application\\/octet-stream/)\n    end\n\n    it \"should fail if its multiple_intern method is used\" do\n      expect {\n        binary.intern_multiple(String, \"foo\")\n      }.to raise_error(NotImplementedError, /can not intern multiple instances from application\\/octet-stream/)\n    end\n\n    it \"should have a weight of 1\" do\n      expect(binary.weight).to eq(1)\n    end\n  end\n\n  describe \"pson\", :if => Puppet.features.pson? do\n    let(:pson) { Puppet::Network::FormatHandler.format(:pson) }\n\n    it \"should include a pson format\" do\n      expect(pson).not_to be_nil\n    end\n\n    it \"should have its mime type set to text/pson\" do\n      expect(pson.mime).to eq(\"text/pson\")\n    end\n\n    it \"should have a nil charset\" do\n      expect(pson.charset).to be_nil\n    end\n\n    it \"should require the :render_method\" do\n      expect(pson.required_methods).to be_include(:render_method)\n    end\n\n    it \"should require the :intern_method\" do\n      expect(pson.required_methods).to be_include(:intern_method)\n    end\n\n    it \"should have a weight of 10\" do\n      expect(pson.weight).to eq(10)\n    end\n\n    it \"should render an instance as pson\" do\n      instance = FormatsTest.new(\"foo\")\n      expect(pson.render(instance)).to eq({\"string\" => \"foo\"}.to_pson)\n    end\n\n    it \"should render multiple instances as pson\" do\n      instances = [FormatsTest.new(\"foo\")]\n      expect(pson.render_multiple(instances)).to eq([{\"string\" => \"foo\"}].to_pson)\n    end\n\n    it \"should intern an instance from a pson hash\" do\n      text = PSON.dump({\"string\" => \"parsed_pson\"})\n      instance = pson.intern(FormatsTest, text)\n      expect(instance.string).to eq(\"parsed_pson\")\n    end\n\n    it \"should skip data_to_hash if data is already an instance of the specified class\" do\n      # The rest terminus for the report indirected type relies on this behavior\n      data = PSON.dump([1, 2])\n      instance = pson.intern(Array, data)\n      expect(instance).to eq([1, 2])\n    end\n\n    it \"should intern multiple instances from a pson array\" do\n      text = PSON.dump(\n        [\n          {\n            \"string\" => \"BAR\"\n          },\n          {\n            \"string\" => \"BAZ\"\n          }\n        ]\n      )\n      expect(pson.intern_multiple(FormatsTest, text)).to eq([FormatsTest.new('BAR'), FormatsTest.new('BAZ')])\n    end\n\n    it \"should unwrap the data from legacy clients\" do\n      text = PSON.dump(\n        {\n          \"type\" => \"FormatsTest\",\n          \"data\" => {\n            \"string\" => \"parsed_json\"\n          }\n        }\n      )\n      instance = pson.intern(FormatsTest, text)\n      expect(instance.string).to eq(\"parsed_json\")\n    end\n\n    it \"fails intelligibly when given invalid data\" do\n      expect do\n        pson.intern(Puppet::Node, '')\n      end.to raise_error(PSON::ParserError, /source did not contain any PSON/)\n    end\n  end\n\n  describe \"json\" do\n    let(:json) { Puppet::Network::FormatHandler.format(:json) }\n\n    it \"should include a json format\" do\n      expect(json).not_to be_nil\n    end\n\n    it \"should have its mime type set to application/json\" do\n      expect(json.mime).to eq(\"application/json\")\n    end\n\n    it \"should use 'utf-8' charset\" do\n      expect(json.charset).to eq(Encoding::UTF_8)\n    end\n\n    it \"should require the :render_method\" do\n      expect(json.required_methods).to be_include(:render_method)\n    end\n\n    it \"should require the :intern_method\" do\n      expect(json.required_methods).to be_include(:intern_method)\n    end\n\n    it \"should have a weight of 15\" do\n      expect(json.weight).to eq(15)\n    end\n\n    it \"should render an instance as JSON\" do\n      instance = FormatsTest.new(\"foo\")\n      expect(json.render(instance)).to eq({\"string\" => \"foo\"}.to_json)\n    end\n\n    it \"should render multiple instances as a JSON array of hashes\" do\n      instances = [FormatsTest.new(\"foo\")]\n      expect(json.render_multiple(instances)).to eq([{\"string\" => \"foo\"}].to_json)\n    end\n\n    it \"should render multiple instances as a JSON array of hashes when multi_json is not present\" do\n      hide_const(\"MultiJson\") if defined?(MultiJson)\n      instances = [FormatsTest.new(\"foo\")]\n      expect(json.render_multiple(instances)).to eq([{\"string\" => \"foo\"}].to_json)\n    end\n\n    it \"should intern an instance from a JSON hash\" do\n      text = Puppet::Util::Json.dump({\"string\" => \"parsed_json\"})\n      instance = json.intern(FormatsTest, text)\n      expect(instance.string).to eq(\"parsed_json\")\n    end\n\n    it \"should skip data_to_hash if data is already an instance of the specified class\" do\n      # The rest terminus for the report indirected type relies on this behavior\n      data = Puppet::Util::Json.dump([1, 2])\n      instance = json.intern(Array, data)\n      expect(instance).to eq([1, 2])\n    end\n\n    it \"should intern multiple instances from a JSON array of hashes\" do\n      text = Puppet::Util::Json.dump(\n        [\n          {\n            \"string\" => \"BAR\"\n          },\n          {\n            \"string\" => \"BAZ\"\n          }\n        ]\n      )\n      expect(json.intern_multiple(FormatsTest, text)).to eq([FormatsTest.new('BAR'), FormatsTest.new('BAZ')])\n    end\n\n    it \"should reject wrapped data from legacy clients as they've never supported JSON\" do\n      text = Puppet::Util::Json.dump(\n        {\n          \"type\" => \"FormatsTest\",\n          \"data\" => {\n            \"string\" => \"parsed_json\"\n          }\n        }\n      )\n      instance = json.intern(FormatsTest, text)\n      expect(instance.string).to be_nil\n    end\n\n    it \"fails intelligibly when given invalid data\" do\n      expect do\n        json.intern(Puppet::Node, '')\n      end.to raise_error(Puppet::Util::Json::ParseError)\n    end\n  end\n\n  describe \":console format\" do\n    let(:console) { Puppet::Network::FormatHandler.format(:console) }\n\n    it \"should include a console format\" do\n      expect(console).to be_an_instance_of Puppet::Network::Format\n    end\n\n    [:intern, :intern_multiple].each do |method|\n      it \"should not implement #{method}\" do\n        expect { console.send(method, String, 'blah') }.to raise_error NotImplementedError\n      end\n    end\n\n    context \"when rendering ruby types\" do\n      [\"hello\", 1, 1.0].each do |input|\n        it \"should just return a #{input.inspect}\" do\n          expect(console.render(input)).to eq(input)\n        end\n      end\n\n      { true  => \"true\",\n        false => \"false\",\n        nil   => \"null\",\n      }.each_pair do |input, output|\n        it \"renders #{input.class} as '#{output}'\" do\n          expect(console.render(input)).to eq(output)\n        end\n      end\n\n      it \"renders an Object as its quoted inspect value\" do\n        obj = Object.new\n        expect(console.render(obj)).to eq(\"\\\"#{obj.inspect}\\\"\")\n      end\n    end\n\n    context \"when rendering arrays\" do\n      {\n        []                => \"\",\n        [1, 2]            => \"1\\n2\\n\",\n        [\"one\"]           => \"one\\n\",\n        [{1 => 1}]        => \"{1=>1}\\n\",\n        [[1, 2], [3, 4]]  => \"[1, 2]\\n[3, 4]\\n\"\n      }.each_pair do |input, output|\n        it \"should render #{input.inspect} as one item per line\" do\n          expect(console.render(input)).to eq(output)\n        end\n      end\n    end\n\n    context \"when rendering hashes\" do\n      {\n        {}                                   => \"\",\n        {1 => 2}                             => \"1  2\\n\",\n        {\"one\" => \"two\"}                     => \"one  \\\"two\\\"\\n\", # odd that two is quoted but one isn't\n        {[1,2] => 3, [2,3] => 5, [3,4] => 7} => \"{\\n  \\\"[1, 2]\\\": 3,\\n  \\\"[2, 3]\\\": 5,\\n  \\\"[3, 4]\\\": 7\\n}\",\n        {{1 => 2} => {3 => 4}}               => \"{\\n  \\\"{1=>2}\\\": {\\n    \\\"3\\\": 4\\n  }\\n}\"\n      }.each_pair do |input, output|\n        it \"should render #{input.inspect}\" do\n          expect(console.render(input)).to eq(output)\n        end\n      end\n\n      it \"should render a {String,Numeric}-keyed Hash into a table\" do\n        json = Puppet::Network::FormatHandler.format(:json)\n        object = Object.new\n        hash = { \"one\" => 1, \"two\" => [], \"three\" => {}, \"four\" => object, 5 => 5,\n                 6.0 => 6 }\n\n        # Gotta love ASCII-betical sort order.  Hope your objects are better\n        # structured for display than my test one is. --daniel 2011-04-18\n        expect(console.render(hash)).to eq <<EOT\n5      5\n6.0    6\nfour   #{json.render(object).chomp}\none    1\nthree  {}\ntwo    []\nEOT\n      end\n    end\n\n    context \"when rendering face-related objects\" do\n      it \"pretty prints facts\" do\n        tm = Time.new(2016, 1, 27, 19, 30, 0)\n        values = {\n          \"architecture\" =>  \"x86_64\",\n          \"os\" => {\n            \"release\" => {\n              \"full\" => \"15.6.0\"\n            }\n          },\n          \"system_uptime\" => {\n            \"seconds\" => 505532\n          }\n        }\n        facts = Puppet::Node::Facts.new(\"foo\", values)\n        facts.timestamp = tm\n\n        # For some reason, render omits the last newline, seems like a bug\n        expect(console.render(facts)).to eq(<<EOT.chomp)\n{\n  \"name\": \"foo\",\n  \"values\": {\n    \"architecture\": \"x86_64\",\n    \"os\": {\n      \"release\": {\n        \"full\": \"15.6.0\"\n      }\n    },\n    \"system_uptime\": {\n      \"seconds\": 505532\n    }\n  },\n  \"timestamp\": \"#{tm.iso8601(9)}\"\n}\nEOT\n      end\n    end\n  end\n\n  describe \":flat format\" do\n    let(:flat) { Puppet::Network::FormatHandler.format(:flat) }\n\n    it \"should include a flat format\" do\n      expect(flat).to be_an_instance_of Puppet::Network::Format\n    end\n\n    [:intern, :intern_multiple].each do |method|\n      it \"should not implement #{method}\" do\n        expect { flat.send(method, String, 'blah') }.to raise_error NotImplementedError\n      end\n    end\n\n    context \"when rendering arrays\" do\n      {\n          []                           => \"\",\n          [1, 2]                       => \"0=1\\n1=2\\n\",\n          [\"one\"]                      => \"0=one\\n\",\n          [{\"one\" => 1}, {\"two\" => 2}] => \"0.one=1\\n1.two=2\\n\",\n          [['something', 'for'], ['the', 'test']]  => \"0=[\\\"something\\\", \\\"for\\\"]\\n1=[\\\"the\\\", \\\"test\\\"]\\n\"\n      }.each_pair do |input, output|\n        it \"should render #{input.inspect} as one item per line\" do\n          expect(flat.render(input)).to eq(output)\n        end\n      end\n    end\n\n    context \"when rendering hashes\" do\n      {\n          {}                                   => \"\",\n          {1 => 2}                             => \"1=2\\n\",\n          {\"one\" => \"two\"}                     => \"one=two\\n\",\n          {[1,2] => 3, [2,3] => 5, [3,4] => 7} => \"[1, 2]=3\\n[2, 3]=5\\n[3, 4]=7\\n\",\n      }.each_pair do |input, output|\n        it \"should render #{input.inspect}\" do\n          expect(flat.render(input)).to eq(output)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/api/indirected_routes_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/http'\nrequire 'puppet/network/http/api/indirected_routes'\nrequire 'puppet/indirector_testing'\nrequire 'puppet_spec/network'\n\nRSpec::Matchers.define_negated_matcher :excluding, :include\n\ndescribe Puppet::Network::HTTP::API::IndirectedRoutes do\n  include PuppetSpec::Network\n\n  let(:indirection) { Puppet::IndirectorTesting.indirection }\n  let(:handler) { Puppet::Network::HTTP::API::IndirectedRoutes.new }\n  let(:response) { Puppet::Network::HTTP::MemoryResponse.new }\n\n  before do\n    Puppet::IndirectorTesting.indirection.terminus_class = :memory\n    Puppet::IndirectorTesting.indirection.terminus.clear\n  end\n\n  describe \"when converting a URI into a request\" do\n    let(:environment) { Puppet::Node::Environment.create(:env, []) }\n    let(:env_loaders) { Puppet::Environments::Static.new(environment) }\n    let(:params) { { :environment => \"env\" } }\n\n    around do |example|\n      Puppet.override(:environments => env_loaders) do\n        example.run\n      end\n    end\n\n    it \"should get the environment from a query parameter\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", params)[3][:environment].to_s).to eq(\"env\")\n    end\n\n    it \"should fail if there is no environment specified\" do\n      expect {\n        handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", {})\n      }.to raise_error(bad_request_error)\n    end\n\n    it \"should fail if the environment is not alphanumeric\" do\n      expect {\n        handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", {:environment => \"env ness\"})\n      }.to raise_error(bad_request_error)\n    end\n\n    it \"should fail if the indirection does not match the prefix\" do\n      expect {\n        handler.uri2indirection(\"GET\", \"#{master_url_prefix}/certificate/foo\", params)\n      }.to raise_error(bad_request_error)\n    end\n\n    it \"should fail if the indirection does not have the correct version\" do\n      expect {\n        handler.uri2indirection(\"GET\", \"#{Puppet::Network::HTTP::MASTER_URL_PREFIX}/v1/node/bar\", params)\n      }.to raise_error(bad_request_error)\n    end\n\n    it \"should not pass a bucket_path parameter through (See Bugs #13553, #13518, #13511)\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\",\n                              { :environment => \"env\",\n                                :bucket_path => \"/malicious/path\" })[3]).not_to include({ :bucket_path => \"/malicious/path\" })\n    end\n\n    it \"should pass allowed parameters through\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\",\n                              { :environment => \"env\",\n                                :allowed_param => \"value\" })[3]).to include({ :allowed_param => \"value\" })\n    end\n\n    it \"should return the environment as a Puppet::Node::Environment\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", params)[3][:environment]).to be_a(Puppet::Node::Environment)\n    end\n\n    it \"should use the first field of the URI as the indirection name\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", params)[0].name).to eq(:node)\n    end\n\n    it \"should fail if the indirection name is not alphanumeric\" do\n      expect {\n        handler.uri2indirection(\"GET\", \"#{master_url_prefix}/foo ness/bar\", params)\n      }.to raise_error(bad_request_error)\n    end\n\n    it \"should use the remainder of the URI as the indirection key\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", params)[2]).to eq(\"bar\")\n    end\n\n    it \"should support the indirection key being a /-separated file path\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bee/baz/bomb\", params)[2]).to eq(\"bee/baz/bomb\")\n    end\n\n    it \"should fail if no indirection key is specified\" do\n      expect {\n        handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node\", params)\n      }.to raise_error(bad_request_error)\n    end\n\n    it \"should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/bar\", params)[1]).to eq(:find)\n    end\n\n    it \"should choose 'find' as the indirection method if the http method is a POST and the indirection name is singular\" do\n      expect(handler.uri2indirection(\"POST\", \"#{master_url_prefix}/node/bar\", params)[1]).to eq(:find)\n    end\n\n    it \"should choose 'head' as the indirection method if the http method is a HEAD and the indirection name is singular\" do\n      expect(handler.uri2indirection(\"HEAD\", \"#{master_url_prefix}/node/bar\", params)[1]).to eq(:head)\n    end\n\n    it \"should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/nodes/bar\", params)[1]).to eq(:search)\n    end\n\n    it \"should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts\" do\n      expect(handler.uri2indirection(\"PUT\", \"#{master_url_prefix}/facts/puppet.node.test\", params)[0].name).to eq(:facts)\n    end\n\n    it \"should change indirection name to 'node' if the http method is a GET and the indirection name is nodes\" do\n      expect(handler.uri2indirection(\"GET\", \"#{master_url_prefix}/nodes/bar\", params)[0].name).to eq(:node)\n    end\n\n    it \"should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular\" do\n      expect(handler.uri2indirection(\"DELETE\", \"#{master_url_prefix}/node/bar\", params)[1]).to eq(:destroy)\n    end\n\n    it \"should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular\" do\n      expect(handler.uri2indirection(\"PUT\", \"#{master_url_prefix}/node/bar\", params)[1]).to eq(:save)\n    end\n\n    it \"should fail if an indirection method cannot be picked\" do\n      expect {\n        handler.uri2indirection(\"UPDATE\", \"#{master_url_prefix}/node/bar\", params)\n      }.to raise_error(method_not_allowed_error)\n    end\n\n    it \"should not URI unescape the indirection key\" do\n      escaped = Puppet::Util.uri_encode(\"foo bar\")\n      _, _, key, _ = handler.uri2indirection(\"GET\", \"#{master_url_prefix}/node/#{escaped}\", params)\n      expect(key).to eq(escaped)\n    end\n  end\n\n  describe \"when processing a request\" do\n    it \"should raise not_found_error if the indirection does not support remote requests\" do\n      request = a_request_that_heads(Puppet::IndirectorTesting.new(\"my data\"))\n\n      expect(indirection).to receive(:allow_remote_requests?).and_return(false)\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(not_found_error)\n    end\n\n    it \"should raise not_found_error if the environment does not exist\" do\n      Puppet.override(:environments => Puppet::Environments::Static.new()) do\n        request = a_request_that_heads(Puppet::IndirectorTesting.new(\"my data\"))\n\n        expect {\n          handler.call(request, response)\n        }.to raise_error(not_found_error)\n      end\n    end\n  end\n\n  describe \"when finding a model instance\" do\n    it \"uses the first supported format for the response\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_finds(data, :accept_header => \"unknown, application/json\")\n\n      handler.call(request, response)\n\n      expect(response.body).to eq(data.render(:json))\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:json))\n    end\n\n    it \"falls back to the next supported format\", if: Puppet.features.pson? do\n      Puppet[:allow_pson_serialization] = true\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_finds(data, :accept_header => \"application/json, text/pson\")\n      allow(data).to receive(:to_json).and_raise(Puppet::Network::FormatHandler::FormatError, 'Could not render to Puppet::Network::Format[json]: source sequence is illegal/malformed utf-8')\n      expect(Puppet).to receive(:warning).with(/Failed to serialize Puppet::IndirectorTesting for 'my data': Could not render to Puppet::Network::Format\\[json\\]/)\n\n      handler.call(request, response)\n\n      expect(response.body).to eq(data.render(:pson))\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson))\n    end\n\n    it \"should pass the result through without rendering it if the result is a string\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      data_string = \"my data string\"\n      request = a_request_that_finds(data, :accept_header => \"application/json\")\n      expect(indirection).to receive(:find).and_return(data_string)\n\n      handler.call(request, response)\n\n      expect(response.body).to eq(data_string)\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:json))\n    end\n\n    it \"should raise not_found_error when no model instance can be found\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_finds(data, :accept_header => \"unknown, application/json\")\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(not_found_error)\n    end\n\n    it \"should raise FormatError if tries to fallback\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_finds(data, :accept_header => \"unknown, text/pson\")\n      allow(Puppet.features).to receive(:pson?).and_return(true)\n      allow(data).to receive(:to_pson).and_raise(Puppet::Network::FormatHandler::FormatError, 'Could not render to Puppet::Network::Format[pson]: source sequence is illegal/malformed utf-8')\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(Puppet::Network::FormatHandler::FormatError,\n                       %r{Failed to serialize Puppet::IndirectorTesting for 'my data': Could not render to Puppet::Network::Format\\[pson\\]})\n    end\n  end\n\n  describe \"when searching for model instances\" do\n    it \"uses the first supported format for the response\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_searches(Puppet::IndirectorTesting.new(\"my\"), :accept_header => \"unknown, application/json\")\n\n      handler.call(request, response)\n\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:json))\n      expect(response.body).to eq(Puppet::IndirectorTesting.render_multiple(:json, [data]))\n    end\n\n    it \"falls back to the next supported format\", if: Puppet.features.pson? do\n      Puppet[:allow_pson_serialization] = true\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_searches(Puppet::IndirectorTesting.new(\"my\"), :accept_header => \"application/json, text/pson\")\n      allow(data).to receive(:to_json).and_raise(Puppet::Network::FormatHandler::FormatError, 'Could not render to Puppet::Network::Format[json]: source sequence is illegal/malformed utf-8')\n      expect(Puppet).to receive(:warning).with(/Failed to serialize Puppet::IndirectorTesting for 'my': Could not render_multiple to Puppet::Network::Format\\[json\\]/)\n\n      handler.call(request, response)\n\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson))\n      expect(response.body).to eq(Puppet::IndirectorTesting.render_multiple(:pson, [data]))\n    end\n\n    it \"raises 406 not acceptable if no formats are accceptable\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_searches(Puppet::IndirectorTesting.new(\"my\"), :accept_header => \"unknown\")\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAcceptableError,\n                       %r{No supported formats are acceptable \\(Accept: unknown\\)})\n    end\n\n    it \"raises FormatError if tries to fallback\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_searches(Puppet::IndirectorTesting.new(\"my\"), :accept_header => \"unknown, text/pson\")\n      allow(Puppet.features).to receive(:pson?).and_return(true)\n      allow(data).to receive(:to_pson).and_raise(Puppet::Network::FormatHandler::FormatError, 'Could not render to Puppet::Network::Format[pson]: source sequence is illegal/malformed utf-8')\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(Puppet::Network::FormatHandler::FormatError,\n                       %r{Failed to serialize Puppet::IndirectorTesting for 'my': Could not render_multiple to Puppet::Network::Format\\[pson\\]})\n    end\n\n    it \"should return [] when searching returns an empty array\" do\n      request = a_request_that_searches(Puppet::IndirectorTesting.new(\"nothing\"), :accept_header => \"unknown, application/json\")\n\n      handler.call(request, response)\n\n      expect(response.body).to eq(\"[]\")\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:json))\n    end\n\n    it \"should raise not_found_error when searching returns nil\" do\n      request = a_request_that_searches(Puppet::IndirectorTesting.new(\"nothing\"), :accept_header => \"unknown, application/json\")\n      expect(indirection).to receive(:search).and_return(nil)\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(not_found_error)\n    end\n  end\n\n  describe \"when destroying a model instance\" do\n    it \"destroys the data indicated in the request\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_destroys(data)\n\n      handler.call(request, response)\n\n      expect(Puppet::IndirectorTesting.indirection.find(\"my data\")).to be_nil\n    end\n\n    it \"uses the first supported format for the response\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_destroys(data, :accept_header => \"unknown, application/json\")\n\n      handler.call(request, response)\n\n      expect(response.body).to eq(data.render(:json))\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:json))\n    end\n\n    it \"raises an error and does not destroy when no accepted formats are known\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_destroys(data, :accept_header => \"unknown, also/unknown\")\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(not_acceptable_error)\n\n      expect(Puppet::IndirectorTesting.indirection.find(\"my data\")).not_to be_nil\n    end\n  end\n\n  describe \"when saving a model instance\" do\n    it \"allows an empty body when the format supports it\" do\n      class Puppet::IndirectorTesting::Nonvalidatingmemory < Puppet::IndirectorTesting::Memory\n        def validate_key(_)\n          # nothing\n        end\n      end\n\n      indirection.terminus_class = :nonvalidatingmemory\n\n      data = Puppet::IndirectorTesting.new(\"test\")\n      request = a_request_that_submits(data,\n                                       :content_type_header => \"application/octet-stream\",\n                                       :body => '')\n\n      handler.call(request, response)\n\n      saved = Puppet::IndirectorTesting.indirection.find(\"test\")\n      expect(saved.name).to eq('')\n    end\n\n    it \"saves the data sent in the request\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_submits(data)\n\n      handler.call(request, response)\n\n      saved = Puppet::IndirectorTesting.indirection.find(\"my data\")\n      expect(saved.name).to eq(data.name)\n    end\n\n    it \"responds with bad request when failing to parse the body\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_submits(data, :content_type_header => 'application/json', :body => \"this is invalid json content\")\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(bad_request_error, /The request body is invalid: Could not intern from json/)\n    end\n\n    it \"responds with unsupported media type error when submitted content is known, but not supported by the model\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_submits(data, :content_type_header => 's')\n      expect(data).to_not be_support_format('s')\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(unsupported_media_type_error, /Client sent a mime-type \\(s\\) that doesn't correspond to a format we support/)\n    end\n\n    it \"uses the first supported format for the response\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_submits(data, :accept_header => \"unknown, application/json\")\n\n      handler.call(request, response)\n\n      expect(response.body).to eq(data.render(:json))\n      expect(response.type).to eq(Puppet::Network::FormatHandler.format(:json))\n    end\n\n    it \"raises an error and does not save when no accepted formats are known\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_submits(data, :accept_header => \"unknown, also/unknown\")\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(not_acceptable_error)\n\n      expect(Puppet::IndirectorTesting.indirection.find(\"my data\")).to be_nil\n    end\n  end\n\n  describe \"when performing head operation\" do\n    it \"should not generate a response when a model head call succeeds\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      indirection.save(data, \"my data\")\n      request = a_request_that_heads(data)\n\n      handler.call(request, response)\n\n      expect(response.code).to eq(nil)\n    end\n\n    it \"should raise not_found_error when the model head call returns false\" do\n      data = Puppet::IndirectorTesting.new(\"my data\")\n      request = a_request_that_heads(data)\n\n      expect {\n        handler.call(request, response)\n      }.to raise_error(not_found_error)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/api/master_spec.rb",
    "content": "# Tests the backwards compatibility of our master -> server changes\n# in the HTTP API\n# This may be removed in Puppet 8\nrequire 'spec_helper'\n\nrequire 'puppet/network/http'\nrequire 'puppet_spec/network'\n\ndescribe Puppet::Network::HTTP::API::Master::V3 do\n  include PuppetSpec::Network\n\n  let(:response) { Puppet::Network::HTTP::MemoryResponse.new }\n  let(:server_url_prefix) { \"#{Puppet::Network::HTTP::MASTER_URL_PREFIX}/v3\" }\n  let(:server_routes) {\n    Puppet::Network::HTTP::Route.\n        path(Regexp.new(\"#{Puppet::Network::HTTP::MASTER_URL_PREFIX}/\")).\n        any.\n        chain(Puppet::Network::HTTP::API::Master::V3.routes)\n  }\n\n  # simulate puppetserver registering its authconfigloader class\n  around :each do |example|\n    Puppet::Network::Authorization.authconfigloader_class = Object\n    begin\n      example.run\n    ensure\n      Puppet::Network::Authorization.authconfigloader_class = nil\n    end\n  end\n\n  it \"mounts the environments endpoint\" do\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/environments\")\n    server_routes.process(request, response)\n\n    expect(response.code).to eq(200)\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/network/http/api/server/v3/environments_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/node/environment'\nrequire 'puppet/network/http'\nrequire 'matchers/json'\n\ndescribe Puppet::Network::HTTP::API::Server::V3::Environments do\n  include JSONMatchers\n\n  let(:environment) { Puppet::Node::Environment.create(:production, [\"/first\", \"/second\"], '/manifests') }\n  let(:loader) { Puppet::Environments::Static.new(environment) }\n  let(:handler) { Puppet::Network::HTTP::API::Server::V3::Environments.new(loader) }\n  let(:request) { Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }) }\n  let(:response) { Puppet::Network::HTTP::MemoryResponse.new }\n\n  it \"responds with all of the available environments\" do\n    handler.call(request, response)\n\n    expect(response.code).to eq(200)\n    expect(response.type).to eq(\"application/json\")\n    expect(JSON.parse(response.body)).to eq({\n      \"search_paths\" => loader.search_paths,\n      \"environments\" => {\n        \"production\" => {\n          \"settings\" => {\n            \"modulepath\" => [File.expand_path(\"/first\"), File.expand_path(\"/second\")],\n            \"manifest\" => File.expand_path(\"/manifests\"),\n            \"environment_timeout\" => 0,\n            \"config_version\" => \"\"\n          }\n        }\n      }\n    })\n  end\n\n  it \"the response conforms to the environments schema for unlimited timeout\" do\n    Puppet[:environment_timeout] = 'unlimited'\n\n    handler.call(request, response)\n\n    expect(response.body).to validate_against('api/schemas/environments.json')\n  end\n\n  it \"the response conforms to the environments schema for integer timeout\" do\n    Puppet[:environment_timeout] = 1\n\n    handler.call(request, response)\n\n    expect(response.body).to validate_against('api/schemas/environments.json')\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/api/server/v3_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/network/http'\nrequire 'puppet_spec/network'\n\ndescribe Puppet::Network::HTTP::API::Server::V3 do\n  include PuppetSpec::Network\n\n  let(:response) { Puppet::Network::HTTP::MemoryResponse.new }\n  let(:server_url_prefix) { \"#{Puppet::Network::HTTP::SERVER_URL_PREFIX}/v3\" }\n  let(:server_routes) {\n    Puppet::Network::HTTP::Route.\n        path(Regexp.new(\"#{Puppet::Network::HTTP::SERVER_URL_PREFIX}/\")).\n        any.\n        chain(Puppet::Network::HTTP::API::Server::V3.routes)\n  }\n\n  # simulate puppetserver registering its authconfigloader class\n  around :each do |example|\n    Puppet::Network::Authorization.authconfigloader_class = Object\n    begin\n      example.run\n    ensure\n      Puppet::Network::Authorization.authconfigloader_class = nil\n    end\n  end\n\n  it \"mounts the environments endpoint\" do\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/environments\")\n    server_routes.process(request, response)\n\n    expect(response.code).to eq(200)\n  end\n\n  it \"matches only complete routes\" do\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/foo/environments\")\n    expect { server_routes.process(request, response) }.to raise_error(Puppet::Network::HTTP::Error::HTTPNotFoundError)\n\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/foo/environment/production\")\n    expect { server_routes.process(request, response) }.to raise_error(Puppet::Network::HTTP::Error::HTTPNotFoundError)\n  end\n\n  it \"mounts indirected routes\" do\n    request = Puppet::Network::HTTP::Request.\n        from_hash(:path => \"#{server_url_prefix}/node/foo\",\n                  :params => {:environment => \"production\"},\n                  :headers => {\"accept\" => \"application/json\"})\n    server_routes.process(request, response)\n\n    expect(response.code).to eq(200)\n  end\n\n  it \"responds to unknown paths by raising not_found_error\" do\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/unknown\")\n\n    expect {\n      server_routes.process(request, response)\n    }.to raise_error(not_found_error)\n  end\n\n  it \"checks authorization for indirected routes\" do\n    Puppet::Network::Authorization.authconfigloader_class = nil\n\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/catalog/foo\")\n    expect {\n      server_routes.process(request, response)\n    }.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, %r{Not Authorized: Forbidden request: /puppet/v3/catalog/foo \\(method GET\\)})\n  end\n\n  it \"checks authorization for environments\" do\n    Puppet::Network::Authorization.authconfigloader_class = nil\n\n    request = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_url_prefix}/environments\")\n    expect {\n      server_routes.process(request, response)\n    }.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, %r{Not Authorized: Forbidden request: /puppet/v3/environments \\(method GET\\)})\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/api_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/handler'\nrequire 'puppet/network/http'\nrequire 'puppet/version'\n\ndescribe Puppet::Network::HTTP::API do\n  def respond(text)\n    lambda { |req, res| res.respond_with(200, \"text/plain\", text) }\n  end\n\n  describe \"#not_found\" do\n    let(:response) { Puppet::Network::HTTP::MemoryResponse.new }\n\n    let(:routes) {\n      Puppet::Network::HTTP::Route.path(Regexp.new(\"foo\")).\n      any.\n      chain(Puppet::Network::HTTP::Route.path(%r{^/bar$}).get(respond(\"bar\")),\n            Puppet::Network::HTTP::API.not_found)\n    }\n\n    it \"mounts the chained routes\" do\n      request = Puppet::Network::HTTP::Request.from_hash(:path => \"foo/bar\")\n      routes.process(request, response)\n\n      expect(response.code).to eq(200)\n      expect(response.body).to eq(\"bar\")\n    end\n\n    it \"responds to unknown paths with a 404\" do\n      request = Puppet::Network::HTTP::Request.from_hash(:path => \"foo/unknown\")\n\n      expect do\n        routes.process(request, response)\n      end.to raise_error(Puppet::Network::HTTP::Error::HTTPNotFoundError)\n    end\n  end\n\n  describe \"Puppet API\" do\n    let(:handler) { PuppetSpec::Handler.new(Puppet::Network::HTTP::API.server_routes,\n                                            Puppet::Network::HTTP::API.not_found_upgrade) }\n\n    let(:server_prefix) { Puppet::Network::HTTP::SERVER_URL_PREFIX }\n\n    it \"raises a not-found error for non-CA or server routes and suggests an upgrade\" do\n      req = Puppet::Network::HTTP::Request.from_hash(:path => \"/unknown\")\n      res = {}\n      handler.process(req, res)\n      expect(res[:status]).to eq(404)\n      expect(res[:body]).to include(\"Puppet version: #{Puppet.version}\")\n    end\n\n    describe \"when processing Puppet 3 routes\" do\n      it \"gives an upgrade message for server routes\" do\n        req = Puppet::Network::HTTP::Request.from_hash(:path => \"/production/node/foo\")\n        res = {}\n        handler.process(req, res)\n        expect(res[:status]).to eq(404)\n        expect(res[:body]).to include(\"Puppet version: #{Puppet.version}\")\n        expect(res[:body]).to include(\"Supported /puppet API versions: #{Puppet::Network::HTTP::SERVER_URL_VERSIONS}\")\n      end\n\n      it \"gives an upgrade message for CA routes\" do\n        req = Puppet::Network::HTTP::Request.from_hash(:path => \"/production/certificate/foo\")\n        res = {}\n        handler.process(req, res)\n        expect(res[:status]).to eq(404)\n        expect(res[:body]).to include(\"Puppet version: #{Puppet.version}\")\n        expect(res[:body]).to include(\"Supported /puppet API versions: #{Puppet::Network::HTTP::SERVER_URL_VERSIONS}\")\n      end\n    end\n\n    describe \"when processing server routes\" do\n      # simulate puppetserver registering its authconfigloader class\n      around :each do |example|\n        Puppet::Network::Authorization.authconfigloader_class = Object\n        begin\n          example.run\n        ensure\n          Puppet::Network::Authorization.authconfigloader_class = nil\n        end\n      end\n\n      it \"responds to v3 indirector requests\" do\n        req = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_prefix}/v3/node/foo\",\n                                                       :params => {:environment => \"production\"},\n                                                       :headers => {'accept' => \"application/json\"})\n        res = {}\n        handler.process(req, res)\n        expect(res[:status]).to eq(200)\n      end\n\n      it \"responds to v3 environments requests\" do\n        req = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_prefix}/v3/environments\")\n        res = {}\n        handler.process(req, res)\n        expect(res[:status]).to eq(200)\n      end\n\n      it \"responds with a not found error to non-v3 requests and does not suggest an upgrade\" do\n        req = Puppet::Network::HTTP::Request.from_hash(:path => \"#{server_prefix}/unknown\")\n        res = {}\n        handler.process(req, res)\n        expect(res[:status]).to eq(404)\n        expect(res[:body]).to include(\"No route for GET #{server_prefix}/unknown\")\n        expect(res[:body]).not_to include(\"Puppet version: #{Puppet.version}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/connection_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/network/http/connection'\nrequire 'puppet/test_ca'\n\ndescribe Puppet::Network::HTTP::Connection do\n  let(:host) { \"me.example.com\" }\n  let(:port) { 8140 }\n  let(:path) { '/foo' }\n  let(:url) { \"https://#{host}:#{port}#{path}\" }\n  let(:params) { { 'key' => 'a value' } }\n  let(:encoded_url_with_params) { \"#{url}?%7B%22key%22:%22a%20value%22%7D\" }\n  let(:ssl_context) { Puppet::SSL::SSLProvider.new.create_system_context(cacerts: []) }\n  let(:verifier) { Puppet::SSL::Verifier.new(host, ssl_context) }\n\n  shared_examples_for \"an HTTP connection\" do |klass|\n    subject { klass.new(host, port, :verifier => verifier) }\n\n    context \"when providing HTTP connections\" do\n      context \"when initializing http instances\" do\n        it \"should return an http instance created with the passed host and port\" do\n          conn = klass.new(host, port, :verifier => verifier)\n\n          expect(conn.address).to eq(host)\n          expect(conn.port).to eq(port)\n        end\n\n        it \"should enable ssl on the http instance by default\" do\n          conn = klass.new(host, port, :verifier => verifier)\n\n          expect(conn).to be_use_ssl\n        end\n\n        it \"can disable ssl using an option and ignore the verify\" do\n          conn = klass.new(host, port, :use_ssl => false)\n\n          expect(conn).to_not be_use_ssl\n        end\n\n        it \"can enable ssl using an option\" do\n          conn = klass.new(host, port, :use_ssl => true, :verifier => verifier)\n\n          expect(conn).to be_use_ssl\n        end\n\n        it \"ignores the ':verify' option when ssl is disabled\" do\n          conn = klass.new(host, port, :use_ssl => false, :verifier => verifier)\n\n          expect(conn.verifier).to be_nil\n        end\n\n        it \"wraps the validator in an adapter\" do\n          conn = klass.new(host, port, :verifier => verifier)\n\n          expect(conn.verifier).to be_a(Puppet::SSL::Verifier)\n        end\n\n        it \"should raise Puppet::Error when invalid options are specified\" do\n          expect { klass.new(host, port, :invalid_option => nil) }.to raise_error(Puppet::Error, 'Unrecognized option(s): :invalid_option')\n        end\n\n        it \"accepts a verifier\" do\n          verifier = Puppet::SSL::Verifier.new(host, double('ssl_context'))\n          conn = klass.new(host, port, :use_ssl => true, :verifier => verifier)\n\n          expect(conn.verifier).to eq(verifier)\n        end\n\n        it \"raises if the wrong verifier class is specified\" do\n          expect {\n            klass.new(host, port, :verifier => Object.new)\n          }.to raise_error(ArgumentError,\n                           \"Expected an instance of Puppet::SSL::Verifier but was passed a Object\")\n        end\n      end\n    end\n\n    context \"for streaming GET requests\" do\n      it 'yields the response' do\n        stub_request(:get, url)\n\n        expect { |b|\n          subject.request_get('/foo', {}, &b)\n        }.to yield_with_args(Net::HTTPResponse)\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:get, encoded_url_with_params)\n\n        subject.request_get(\"#{path}?#{params.to_json}\") { |_| }\n      end\n\n      it \"merges custom headers with default ones\" do\n        stub_request(:get, url).with(headers: { 'X-Foo' => 'Bar', 'User-Agent' => /./ })\n\n        subject.request_get(path, {'X-Foo' => 'Bar'}) { |_| }\n      end\n\n      it \"returns the response\" do\n        stub_request(:get, url)\n\n        response = subject.request_get(path) { |_| }\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:get, url_with_query)\n\n        response = subject.request_get(url_with_query) { |_| }\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for streaming head requests\" do\n      it 'yields the response when request_head is called' do\n        stub_request(:head, url)\n\n        expect { |b|\n          subject.request_head('/foo', {}, &b)\n        }.to yield_with_args(Net::HTTPResponse)\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:head, encoded_url_with_params)\n\n        subject.request_head(\"#{path}?#{params.to_json}\") { |_| }\n      end\n\n      it \"merges custom headers with default ones\" do\n        stub_request(:head, url).with(headers: { 'X-Foo' => 'Bar', 'User-Agent' => /./ })\n\n        subject.request_head(path, {'X-Foo' => 'Bar'}) { |_| }\n      end\n\n      it \"returns the response\" do\n        stub_request(:head, url)\n\n        response = subject.request_head(path) { |_| }\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:head, url_with_query)\n\n        response = subject.request_head(url_with_query) { |_| }\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for streaming post requests\" do\n      it 'yields the response when request_post is called' do\n        stub_request(:post, url)\n\n        expect { |b|\n          subject.request_post('/foo', \"param: value\", &b)\n        }.to yield_with_args(Net::HTTPResponse)\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:post, encoded_url_with_params)\n\n        subject.request_post(\"#{path}?#{params.to_json}\", \"\") { |_| }\n      end\n\n      it \"merges custom headers with default ones\" do\n        stub_request(:post, url).with(headers: { 'X-Foo' => 'Bar', 'User-Agent' => /./ })\n\n        subject.request_post(path, \"\", {'X-Foo' => 'Bar'}) { |_| }\n      end\n\n      it \"returns the response\" do\n        stub_request(:post, url)\n\n        response = subject.request_post(path, \"\") { |_| }\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:post, url_with_query)\n\n        response = subject.request_post(url_with_query, \"\") { |_| }\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for GET requests\" do\n      it \"includes default HTTP headers\" do\n        stub_request(:get, url).with(headers: {'User-Agent' => /./})\n\n        subject.get(path)\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:get, encoded_url_with_params)\n\n        subject.get(\"#{path}?#{params.to_json}\")\n      end\n\n      it \"merges custom headers with default ones\" do\n        stub_request(:get, url).with(headers: { 'X-Foo' => 'Bar', 'User-Agent' => /./ })\n\n        subject.get(path, {'X-Foo' => 'Bar'})\n      end\n\n      it \"returns the response\" do\n        stub_request(:get, url)\n\n        response = subject.get(path)\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"returns the entire response body\" do\n        stub_request(:get, url).to_return(body: \"abc\")\n\n        response = subject.get(path)\n        expect(response.body).to eq(\"abc\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:get, url_with_query)\n\n        response = subject.get(url_with_query)\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for HEAD requests\" do\n      it \"includes default HTTP headers\" do\n        stub_request(:head, url).with(headers: {'User-Agent' => /./})\n\n        subject.head(path)\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:head, encoded_url_with_params)\n\n        subject.head(\"#{path}?#{params.to_json}\")\n      end\n\n      it \"merges custom headers with default ones\" do\n        stub_request(:head, url).with(headers: { 'X-Foo' => 'Bar', 'User-Agent' => /./ })\n\n        subject.head(path, {'X-Foo' => 'Bar'})\n      end\n\n      it \"returns the response\" do\n        stub_request(:head, url)\n\n        response = subject.head(path)\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:head, url_with_query)\n\n        response = subject.head(url_with_query)\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for PUT requests\" do\n      it \"includes default HTTP headers\" do\n        stub_request(:put, url).with(headers: {'User-Agent' => /./})\n\n        subject.put(path, \"\", {'Content-Type' => 'text/plain'})\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:put, encoded_url_with_params)\n\n        subject.put(\"#{path}?#{params.to_json}\", \"\")\n      end\n\n      it \"includes custom headers\" do\n        stub_request(:put, url).with(headers: { 'X-Foo' => 'Bar' })\n\n        subject.put(path, \"\", {'X-Foo' => 'Bar', 'Content-Type' => 'text/plain'})\n      end\n\n      it \"returns the response\" do\n        stub_request(:put, url)\n\n        response = subject.put(path, \"\", {'Content-Type' => 'text/plain'})\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"sets content-type for the body\" do\n        stub_request(:put, url).with(headers: {\"Content-Type\" => \"text/plain\"})\n\n        subject.put(path, \"hello\", {'Content-Type' => 'text/plain'})\n      end\n\n      it 'sends an empty body' do\n        stub_request(:put, url).with(body: '')\n\n        subject.put(path, nil)\n      end\n\n      it 'defaults content-type to application/x-www-form-urlencoded' do\n        stub_request(:put, url).with(headers: {'Content-Type' => 'application/x-www-form-urlencoded'})\n\n        subject.put(path, '')\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:put, url_with_query)\n\n        response = subject.put(url_with_query, '')\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for POST requests\" do\n      it \"includes default HTTP headers\" do\n        stub_request(:post, url).with(headers: {'User-Agent' => /./})\n\n        subject.post(path, \"\", {'Content-Type' => 'text/plain'})\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:post, encoded_url_with_params)\n\n        subject.post(\"#{path}?#{params.to_json}\", \"\", {'Content-Type' => 'text/plain'})\n      end\n\n      it \"includes custom headers\" do\n        stub_request(:post, url).with(headers: { 'X-Foo' => 'Bar' })\n\n        subject.post(path, \"\", {'X-Foo' => 'Bar', 'Content-Type' => 'text/plain'})\n      end\n\n      it \"returns the response\" do\n        stub_request(:post, url)\n\n        response = subject.post(path, \"\", {'Content-Type' => 'text/plain'})\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"sets content-type for the body\" do\n        stub_request(:post, url).with(headers: {\"Content-Type\" => \"text/plain\"})\n\n        subject.post(path, \"hello\", {'Content-Type' => 'text/plain'})\n      end\n\n      it 'sends an empty body' do\n        stub_request(:post, url).with(body: '')\n\n        subject.post(path, nil)\n      end\n\n      it 'defaults content-type to application/x-www-form-urlencoded' do\n        stub_request(:post, url).with(headers: {'Content-Type' => 'application/x-www-form-urlencoded'})\n\n        subject.post(path, \"\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:post, url_with_query)\n\n        response = subject.post(url_with_query, '')\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"for DELETE requests\" do\n      it \"includes default HTTP headers\" do\n        stub_request(:delete, url).with(headers: {'User-Agent' => /./})\n\n        subject.delete(path)\n      end\n\n      it \"merges custom headers with default ones\" do\n        stub_request(:delete, url).with(headers: { 'X-Foo' => 'Bar', 'User-Agent' => /./ })\n\n        subject.delete(path, {'X-Foo' => 'Bar'})\n      end\n\n      it \"stringifies keys and encodes values in the query\" do\n        stub_request(:delete, encoded_url_with_params)\n\n        subject.delete(\"#{path}?#{params.to_json}\")\n      end\n\n      it \"returns the response\" do\n        stub_request(:delete, url)\n\n        response = subject.delete(path)\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n        expect(response.code).to eq(\"200\")\n      end\n\n      it \"returns the entire response body\" do\n        stub_request(:delete, url).to_return(body: \"abc\")\n\n        expect(subject.delete(path).body).to eq(\"abc\")\n      end\n\n      it \"accepts a URL string as the path\" do\n        url_with_query = \"#{url}?foo=bar\"\n        stub_request(:delete, url_with_query)\n\n        response = subject.delete(url_with_query)\n        expect(response).to be_an_instance_of(Net::HTTPOK)\n      end\n    end\n\n    context \"when response is a redirect\" do\n      subject { klass }\n\n      def create_connection(options = {})\n        options[:use_ssl] = false\n        options[:verifier] = verifier\n        subject.new(host, port, options)\n      end\n\n      def redirect_to(url)\n        { status: 302, headers: { 'Location' => url } }\n      end\n\n      it \"should follow the redirect to the final resource location\" do\n        stub_request(:get, \"http://me.example.com:8140/foo\").to_return(redirect_to(\"http://me.example.com:8140/bar\"))\n        stub_request(:get, \"http://me.example.com:8140/bar\").to_return(status: 200)\n\n        create_connection.get('/foo')\n      end\n\n      def expects_limit_exceeded(conn)\n        expect {\n          conn.get('/')\n        }.to raise_error(Puppet::Network::HTTP::RedirectionLimitExceededException)\n      end\n\n      it \"should not follow any redirects when the limit is 0\" do\n        stub_request(:get, \"http://me.example.com:8140/\").to_return(redirect_to(\"http://me.example.com:8140/foo\"))\n\n        conn = create_connection(:redirect_limit => 0)\n        expects_limit_exceeded(conn)\n      end\n\n      it \"should follow the redirect once\" do\n        stub_request(:get, \"http://me.example.com:8140/\").to_return(redirect_to(\"http://me.example.com:8140/foo\"))\n        stub_request(:get, \"http://me.example.com:8140/foo\").to_return(redirect_to(\"http://me.example.com:8140/bar\"))\n\n        conn = create_connection(:redirect_limit => 1)\n        expects_limit_exceeded(conn)\n      end\n\n      it \"should raise an exception when the redirect limit is exceeded\" do\n        stub_request(:get, \"http://me.example.com:8140/\").to_return(redirect_to(\"http://me.example.com:8140/foo\"))\n        stub_request(:get, \"http://me.example.com:8140/foo\").to_return(redirect_to(\"http://me.example.com:8140/bar\"))\n        stub_request(:get, \"http://me.example.com:8140/bar\").to_return(redirect_to(\"http://me.example.com:8140/baz\"))\n        stub_request(:get, \"http://me.example.com:8140/baz\").to_return(redirect_to(\"http://me.example.com:8140/qux\"))\n\n        conn = create_connection(:redirect_limit => 3)\n        expects_limit_exceeded(conn)\n      end\n\n      it 'raises an exception when the location header is missing' do\n        stub_request(:get, \"http://me.example.com:8140/\").to_return(status: 302)\n\n        expect {\n            create_connection.get('/')\n          }.to raise_error(Puppet::HTTP::ProtocolError, /Location response header is missing/)\n      end\n    end\n\n    context \"when response indicates an overloaded server\" do\n      def retry_after(datetime)\n        stub_request(:get, url)\n          .to_return(status: [503, 'Service Unavailable'], headers: {'Retry-After' => datetime}).then\n          .to_return(status: 200)\n      end\n\n      it \"should return a 503 response if Retry-After is not set\" do\n        stub_request(:get, url).to_return(status: [503, 'Service Unavailable'])\n\n        result = subject.get('/foo')\n        expect(result.code).to eq(\"503\")\n      end\n\n      it \"should return a 503 response if Retry-After is not convertible to an Integer or RFC 2822 Date\" do\n        retry_after('foo')\n\n        expect {\n          subject.get('/foo')\n        }.to raise_error(Puppet::HTTP::ProtocolError, /Failed to parse Retry-After header 'foo'/)\n      end\n\n      it \"should close the connection before sleeping\" do\n        retry_after('42')\n\n        http1 = Net::HTTP.new(host, port)\n        http1.use_ssl = true\n        allow(http1).to receive(:started?).and_return(true)\n\n        http2 = Net::HTTP.new(host, port)\n        http2.use_ssl = true\n        allow(http1).to receive(:started?).and_return(true)\n\n        # The \"with_connection\" method is required to yield started connections\n        pool = Puppet.runtime[:http].pool\n\n        allow(pool).to receive(:with_connection).and_yield(http1).and_yield(http2)\n\n        expect(http1).to receive(:finish).ordered\n        expect(::Kernel).to receive(:sleep).with(42).ordered\n\n        subject.get('/foo')\n      end\n\n      it \"should sleep and retry if Retry-After is an Integer\" do\n        retry_after('42')\n\n        expect(::Kernel).to receive(:sleep).with(42)\n\n        result = subject.get('/foo')\n        expect(result.code).to eq(\"200\")\n      end\n\n      it \"should sleep and retry if Retry-After is an RFC 2822 Date\" do\n        retry_after('Wed, 13 Apr 2005 15:18:05 GMT')\n\n        now = DateTime.new(2005, 4, 13, 8, 17, 5, '-07:00')\n        allow(DateTime).to receive(:now).and_return(now)\n\n        expect(::Kernel).to receive(:sleep).with(60)\n\n        result = subject.get('/foo')\n        expect(result.code).to eq(\"200\")\n      end\n\n      it \"should sleep for no more than the Puppet runinterval\" do\n        retry_after('60')\n\n        Puppet[:runinterval] = 30\n\n        expect(::Kernel).to receive(:sleep).with(30)\n\n        subject.get('/foo')\n      end\n\n      it \"should sleep for 0 seconds if the RFC 2822 date has past\" do\n        retry_after('Wed, 13 Apr 2005 15:18:05 GMT')\n\n        expect(::Kernel).to receive(:sleep).with(0)\n\n        subject.get('/foo')\n      end\n    end\n\n    context \"basic auth\" do\n      let(:auth) { { :user => 'user', :password => 'password' } }\n      let(:creds) { [ 'user', 'password'] }\n\n      it \"is allowed in get requests\" do\n        stub_request(:get, url).with(basic_auth: creds)\n\n        subject.get('/foo', nil, :basic_auth => auth)\n      end\n\n      it \"is allowed in post requests\" do\n        stub_request(:post, url).with(basic_auth: creds)\n\n        subject.post('/foo', 'data', nil, :basic_auth => auth)\n      end\n\n      it \"is allowed in head requests\" do\n        stub_request(:head, url).with(basic_auth: creds)\n\n        subject.head('/foo', nil, :basic_auth => auth)\n      end\n\n      it \"is allowed in delete requests\" do\n        stub_request(:delete, url).with(basic_auth: creds)\n\n        subject.delete('/foo', nil, :basic_auth => auth)\n      end\n\n      it \"is allowed in put requests\" do\n        stub_request(:put, url).with(basic_auth: creds)\n\n        subject.put('/foo', 'data', nil, :basic_auth => auth)\n      end\n    end\n\n    it \"sets HTTP User-Agent header\" do\n      puppet_ua = \"Puppet/#{Puppet.version} Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})\"\n      stub_request(:get, url).with(headers: { 'User-Agent' => puppet_ua })\n\n      subject.get('/foo')\n    end\n\n    describe 'connection request errors' do\n      it \"logs and raises generic http errors\" do\n        generic_error = Net::HTTPError.new('generic error', double(\"response\"))\n        stub_request(:get, url).to_raise(generic_error)\n\n        expect(Puppet).to receive(:log_exception).with(anything, /^.*failed.*: generic error$/)\n        expect { subject.get('/foo') }.to raise_error(generic_error)\n      end\n\n      it \"logs and raises timeout errors\" do\n        timeout_error = Net::OpenTimeout.new\n        stub_request(:get, url).to_raise(timeout_error)\n\n        expect(Puppet).to receive(:log_exception).with(anything, /^.*timed out .*after .* seconds/)\n        expect { subject.get('/foo') }.to raise_error(timeout_error)\n      end\n\n      it \"logs and raises eof errors\" do\n        eof_error = EOFError\n        stub_request(:get, url).to_raise(eof_error)\n\n        expect(Puppet).to receive(:log_exception).with(anything, /^.*interrupted after .* seconds$/)\n        expect { subject.get('/foo') }.to raise_error(eof_error)\n      end\n    end\n  end\n\n  describe Puppet::Network::HTTP::Connection do\n    it_behaves_like \"an HTTP connection\", described_class\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/error_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/json'\n\nrequire 'puppet/network/http'\n\ndescribe Puppet::Network::HTTP::Error do\n  include JSONMatchers\n\n  describe Puppet::Network::HTTP::Error::HTTPError do\n    it \"should serialize to JSON that matches the error schema\" do\n      error = Puppet::Network::HTTP::Error::HTTPError.new(\"I don't like the looks of you\", 400, :SHIFTY_USER)\n\n      expect(error.to_json).to validate_against('api/schemas/error.json')\n    end\n  end\n\n  describe Puppet::Network::HTTP::Error::HTTPServerError do\n    it \"should serialize to JSON that matches the error schema\" do\n      begin\n        raise Exception, \"a wild Exception appeared!\"\n      rescue Exception => e\n        culpable = e\n      end\n      error = Puppet::Network::HTTP::Error::HTTPServerError.new(culpable)\n\n      expect(error.to_json).to validate_against('api/schemas/error.json')\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/network/http/handler_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet_spec/handler'\nrequire 'puppet/indirector_testing'\nrequire 'puppet/network/http'\n\ndescribe Puppet::Network::HTTP::Handler do\n  before :each do\n    Puppet::IndirectorTesting.indirection.terminus_class = :memory\n  end\n\n  let(:indirection) { Puppet::IndirectorTesting.indirection }\n\n  def a_request(method = \"HEAD\", path = \"/production/#{indirection.name}/unknown\")\n    {\n      :accept_header => \"application/json\",\n      :content_type_header => \"application/json\",\n      :method => method,\n      :path => path,\n      :params => {},\n      :client_cert => nil,\n      :headers => {},\n      :body => nil\n    }\n  end\n\n  let(:handler) { PuppetSpec::Handler.new() }\n\n  describe \"the HTTP Handler\" do\n    def respond(text)\n      lambda { |req, res| res.respond_with(200, \"text/plain\", text) }\n    end\n\n    it \"hands the request to the first route that matches the request path\" do\n      handler = PuppetSpec::Handler.new(\n        Puppet::Network::HTTP::Route.path(%r{^/foo}).get(respond(\"skipped\")),\n        Puppet::Network::HTTP::Route.path(%r{^/vtest}).get(respond(\"used\")),\n        Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(respond(\"ignored\")))\n\n      req = a_request(\"GET\", \"/vtest/foo\")\n      res = {}\n\n      handler.process(req, res)\n\n      expect(res[:body]).to eq(\"used\")\n    end\n\n    it \"raises an error if multiple routes with the same path regex are registered\" do\n      expect do\n        PuppetSpec::Handler.new(\n          Puppet::Network::HTTP::Route.path(%r{^/foo}).get(respond(\"ignored\")),\n          Puppet::Network::HTTP::Route.path(%r{^/foo}).post(respond(\"also ignored\"))\n        )\n      end.to raise_error(ArgumentError)\n    end\n\n    it \"raises an HTTP not found error if no routes match\" do\n      handler = PuppetSpec::Handler.new\n\n      req = a_request(\"GET\", \"/vtest/foo\")\n      res = {}\n\n      handler.process(req, res)\n\n      res_body = JSON(res[:body])\n\n      expect(res[:content_type_header]).to eq(\"application/json; charset=utf-8\")\n      expect(res_body[\"issue_kind\"]).to eq(\"HANDLER_NOT_FOUND\")\n      expect(res_body[\"message\"]).to eq(\"Not Found: No route for GET /vtest/foo\")\n      expect(res[:status]).to eq(404)\n    end\n\n    it \"returns a structured error response when the server encounters an internal error\" do\n      error = StandardError.new(\"the sky is falling!\")\n      original_stacktrace = ['a.rb', 'b.rb']\n      error.set_backtrace(original_stacktrace)\n\n      handler = PuppetSpec::Handler.new(\n        Puppet::Network::HTTP::Route.path(/.*/).get(lambda { |_, _| raise error}))\n\n      # Stacktraces should be included in logs\n      expect(Puppet).to receive(:err).with(\"Server Error: the sky is falling!\\na.rb\\nb.rb\")\n\n      req = a_request(\"GET\", \"/vtest/foo\")\n      res = {}\n\n      handler.process(req, res)\n\n      res_body = JSON(res[:body])\n\n      expect(res[:content_type_header]).to eq(\"application/json; charset=utf-8\")\n      expect(res_body[\"issue_kind\"]).to eq(Puppet::Network::HTTP::Issues::RUNTIME_ERROR.to_s)\n      expect(res_body[\"message\"]).to eq(\"Server Error: the sky is falling!\")\n      expect(res[:status]).to eq(500)\n    end\n\n  end\n\n  describe \"when processing a request\" do\n    let(:response) do\n      { :status => 200 }\n    end\n\n    it \"should setup a profiler when the puppet-profiling header exists\" do\n      request = a_request\n      request[:headers][Puppet::Network::HTTP::HEADER_ENABLE_PROFILING.downcase] = \"true\"\n\n      p = PuppetSpec::HandlerProfiler.new\n\n      expect(Puppet::Util::Profiler).to receive(:add_profiler).with(be_a(Puppet::Util::Profiler::WallClock)).and_return(p)\n      expect(Puppet::Util::Profiler).to receive(:remove_profiler).with(p)\n\n      handler.process(request, response)\n    end\n\n    it \"should not setup profiler when the profile parameter is missing\" do\n      request = a_request\n      request[:params] = { }\n\n      expect(Puppet::Util::Profiler).not_to receive(:add_profiler)\n\n      handler.process(request, response)\n    end\n\n    it \"should still find the correct format if content type contains charset information\" do\n      request = Puppet::Network::HTTP::Request.new({ 'content-type' => \"text/plain; charset=UTF-8\" },\n                                                   {}, 'GET', '/', nil)\n      expect(request.formatter.name).to eq(:s)\n    end\n\n    # PUP-3272\n    # This used to be for YAML, and doing a to_yaml on an array.\n    # The result with to_json is something different, the result is a string\n    # Which seems correct. Looks like this was some kind of nesting option \"yaml inside yaml\" ?\n    # Removing the test\n#    it \"should deserialize JSON parameters\" do\n#      params = {'my_param' => [1,2,3].to_json}\n#\n#      decoded_params = handler.send(:decode_params, params)\n#\n#      decoded_params.should == {:my_param => [1,2,3]}\n#    end\n  end\n\n  describe \"when resolving node\" do\n    it \"should use a look-up from the ip address\" do\n      expect(Resolv).to receive(:getname).with(\"1.2.3.4\").and_return(\"host.domain.com\")\n\n      handler.resolve_node(:ip => \"1.2.3.4\")\n    end\n\n    it \"should return the look-up result\" do\n      allow(Resolv).to receive(:getname).with(\"1.2.3.4\").and_return(\"host.domain.com\")\n\n      expect(handler.resolve_node(:ip => \"1.2.3.4\")).to eq(\"host.domain.com\")\n    end\n\n    it \"should return the ip address if resolving fails\" do\n      allow(Resolv).to receive(:getname).with(\"1.2.3.4\").and_raise(RuntimeError, \"no such host\")\n\n      expect(handler.resolve_node(:ip => \"1.2.3.4\")).to eq(\"1.2.3.4\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/request_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/network'\nrequire 'puppet/network/http'\n\ndescribe Puppet::Network::HTTP::Request do\n  include PuppetSpec::Network\n\n  let(:json_formatter) { Puppet::Network::FormatHandler.format(:json) }\n  let(:pson_formatter) { Puppet::Network::FormatHandler.format(:pson) }\n\n  def headers\n    {\n      'accept' => 'application/json',\n      'content-type' => 'application/json'\n    }\n  end\n\n  def a_request(headers, body = \"\")\n    described_class.from_hash(\n      :method => \"PUT\",\n      :path   => \"/path/to/endpoint\",\n      :body   => body,\n      :headers => headers\n    )\n  end\n\n  context \"when resolving the formatter for the request body\" do\n    it \"returns the formatter for that Content-Type\" do\n      request = a_request(headers.merge(\"content-type\" => \"application/json\"))\n      expect(request.formatter).to eq(json_formatter)\n    end\n\n    it \"raises HTTP 400 if Content-Type is missing\" do\n      request = a_request({})\n      expect {\n        request.formatter\n      }.to raise_error(bad_request_error, /No Content-Type header was received, it isn't possible to unserialize the request/)\n    end\n\n    it \"raises HTTP 415 if Content-Type is unsupported\" do\n      request = a_request(headers.merge('content-type' => 'application/ogg'))\n      expect {\n        request.formatter\n      }.to raise_error(unsupported_media_type_error, /Unsupported Media Type: Client sent a mime-type \\(application\\/ogg\\) that doesn't correspond to a format we support/)\n    end\n\n    it \"raises HTTP 415 if Content-Type is unsafe yaml\" do\n      request = a_request(headers.merge('content-type' => 'yaml'))\n      expect {\n        request.formatter\n      }.to raise_error(unsupported_media_type_error, /Unsupported Media Type: Client sent a mime-type \\(yaml\\) that doesn't correspond to a format we support/)\n    end\n\n    it \"raises HTTP 415 if Content-Type is unsafe b64_zlib_yaml\" do\n      request = a_request(headers.merge('content-type' => 'b64_zlib_yaml'))\n      expect {\n        request.formatter\n      }.to raise_error(unsupported_media_type_error, /Unsupported Media Type: Client sent a mime-type \\(b64_zlib_yaml\\) that doesn't correspond to a format we support/)\n    end\n  end\n\n  context \"when resolving the formatter for the response body\" do\n    context \"when the client doesn't specify an Accept header\" do\n      it \"raises HTTP 400 if the server doesn't specify a default\" do\n        request = a_request({})\n        expect {\n          request.response_formatters_for([:json])\n        }.to raise_error(bad_request_error, /Missing required Accept header/)\n      end\n\n      it \"uses the server default\" do\n        request = a_request({})\n        expect(request.response_formatters_for([:json], 'application/json')).to eq([json_formatter])\n      end\n    end\n\n    it \"returns accepted and supported formats, in the accepted order\" do\n      request = a_request(headers.merge('accept' => 'application/json, application/x-msgpack, text/pson'))\n      expect(request.response_formatters_for([:pson, :json])).to eq([json_formatter, pson_formatter])\n    end\n\n    it \"selects the second format if the first one isn't supported by the server\" do\n      request = a_request(headers.merge('accept' => 'application/json, text/pson'))\n      expect(request.response_formatters_for([:pson])).to eq([pson_formatter])\n    end\n\n    it \"raises HTTP 406 if Accept doesn't include any server-supported formats\" do\n      request = a_request(headers.merge('accept' => 'application/ogg'))\n      expect {\n        request.response_formatters_for([:json])\n      }.to raise_error(not_acceptable_error, /No supported formats are acceptable \\(Accept: application\\/ogg\\)/)\n    end\n\n    it \"raises HTTP 406 if Accept resolves to unsafe yaml\" do\n      request = a_request(headers.merge('accept' => 'yaml'))\n      expect {\n        request.response_formatters_for([:json])\n      }.to raise_error(not_acceptable_error, /No supported formats are acceptable \\(Accept: yaml\\)/)\n    end\n\n    it \"raises HTTP 406 if Accept resolves to unsafe b64_zlib_yaml\" do\n      request = a_request(headers.merge('accept' => 'b64_zlib_yaml'))\n      expect {\n        request.response_formatters_for([:json])\n      }.to raise_error(not_acceptable_error, /No supported formats are acceptable \\(Accept: b64_zlib_yaml\\)/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/response_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/handler'\nrequire 'puppet/network/http'\n\ndescribe Puppet::Network::HTTP::Response do\n  include PuppetSpec::Files\n\n  let(:handler) { PuppetSpec::Handler.new }\n  let(:response) { {} }\n  let(:subject) { described_class.new(handler, response) }\n  let(:body_utf8) { JSON.dump({ \"foo\" => \"bar\"}).encode('UTF-8') }\n  let(:body_shift_jis) { [130, 174].pack('C*').force_encoding(Encoding::Shift_JIS) }\n  let(:invalid_shift_jis) { \"\\xC0\\xFF\".force_encoding(Encoding::Shift_JIS) }\n\n  context \"when passed a response body\" do\n    it \"passes the status code and body to the handler\" do\n      expect(handler).to receive(:set_response).with(response, body_utf8, 200)\n\n      subject.respond_with(200, 'application/json', body_utf8)\n    end\n\n    it \"accepts a File body\" do\n      file = tmpfile('response_spec')\n      expect(handler).to receive(:set_response).with(response, file, 200)\n\n      subject.respond_with(200, 'application/octet-stream', file)\n    end\n  end\n\n  context \"when passed a content type\" do\n    it \"accepts a mime string\" do\n      expect(handler).to receive(:set_content_type).with(response, 'application/json; charset=utf-8')\n\n      subject.respond_with(200, 'application/json', body_utf8)\n    end\n\n    it \"accepts a format object\" do\n      formatter = Puppet::Network::FormatHandler.format(:json)\n      expect(handler).to receive(:set_content_type).with(response, 'application/json; charset=utf-8')\n\n      subject.respond_with(200, formatter, body_utf8)\n    end\n  end\n\n  context \"when resolving charset\" do\n    context \"with binary content\" do\n      it \"omits the charset\" do\n        body_binary = [0xDEADCAFE].pack('L')\n\n        formatter = Puppet::Network::FormatHandler.format(:binary)\n        expect(handler).to receive(:set_content_type).with(response, 'application/octet-stream')\n\n        subject.respond_with(200, formatter, body_binary)\n      end\n    end\n\n    context \"with text/plain content\" do\n      let(:formatter) { Puppet::Network::FormatHandler.format(:s) }\n\n      it \"sets the charset to UTF-8 for content already in that format\" do\n        body_pem = \"BEGIN CERTIFICATE\".encode('UTF-8')\n\n        expect(handler).to receive(:set_content_type).with(response, 'text/plain; charset=utf-8')\n\n        subject.respond_with(200, formatter, body_pem)\n      end\n\n      it \"encodes the content to UTF-8 for content not already in UTF-8\" do\n        expect(handler).to receive(:set_content_type).with(response, 'text/plain; charset=utf-8')\n        expect(handler).to receive(:set_response).with(response, body_shift_jis.encode('utf-8'), 200)\n\n        subject.respond_with(200, formatter, body_shift_jis)\n      end\n\n      it \"raises an exception if transcoding fails\" do\n        expect {\n          subject.respond_with(200, formatter, invalid_shift_jis)\n        }.to raise_error(EncodingError, /\"\\\\xFF\" on Shift_JIS/)\n      end\n    end\n\n    context \"with application/json content\" do\n      let(:formatter) { Puppet::Network::FormatHandler.format(:json) }\n\n      it \"sets the charset to UTF-8 for content already in that format\" do\n        expect(handler).to receive(:set_content_type).with(response, 'application/json; charset=utf-8')\n\n        subject.respond_with(200, formatter, body_utf8)\n      end\n\n      it \"encodes the content to UTF-8 for content not already in UTF-8\" do\n        expect(handler).to receive(:set_content_type).with(response, 'application/json; charset=utf-8')\n        expect(handler).to receive(:set_response).with(response, body_shift_jis.encode('utf-8'), 200)\n\n        subject.respond_with(200, formatter, body_shift_jis)\n      end\n\n      it \"raises an exception if transcoding fails\" do\n        expect {\n          subject.respond_with(200, formatter, invalid_shift_jis)\n        }.to raise_error(EncodingError, /\"\\\\xFF\" on Shift_JIS/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http/route_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/indirector_testing'\n\nrequire 'puppet/network/http'\n\ndescribe Puppet::Network::HTTP::Route do\n  def request(method, path)\n    Puppet::Network::HTTP::Request.from_hash({\n      :method => method,\n      :path => path,\n      :routing_path => path })\n  end\n\n  def respond(text)\n    lambda { |req, res| res.respond_with(200, \"text/plain\", text) }\n  end\n\n  let(:req) { request(\"GET\", \"/vtest/foo\") }\n  let(:res) { Puppet::Network::HTTP::MemoryResponse.new }\n\n  describe \"an HTTP Route\" do\n    it \"can match a request\" do\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest})\n      expect(route.matches?(req)).to be_truthy\n    end\n\n    it \"will raise a Method Not Allowed error when no handler for the request's method is given\" do\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).post(respond(\"ignored\"))\n      expect do\n        route.process(req, res)\n      end.to raise_error(Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError)\n    end\n\n    it \"can match any HTTP method\" do\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).any(respond(\"used\"))\n      expect(route.matches?(req)).to be_truthy\n\n      route.process(req, res)\n\n      expect(res.body).to eq(\"used\")\n    end\n\n    it \"processes DELETE requests\" do\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).delete(respond(\"used\"))\n\n      route.process(request(\"DELETE\", \"/vtest/foo\"), res)\n\n      expect(res.body).to eq(\"used\")\n    end\n\n    it \"does something when it doesn't know the verb\" do\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo})\n\n      expect do\n        route.process(request(\"UNKNOWN\", \"/vtest/foo\"), res)\n      end.to raise_error(Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError, /UNKNOWN/)\n    end\n\n    it \"calls the method handlers in turn\" do\n      call_count = 0\n      handler = lambda { |request, response| call_count += 1 }\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(handler, handler)\n\n      route.process(req, res)\n      expect(call_count).to eq(2)\n    end\n\n    it \"stops calling handlers if one of them raises an error\" do\n      ignored_called = false\n      ignored = lambda { |req, res| ignored_called = true }\n      raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, \"go away\" }\n      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(raise_error, ignored)\n\n      expect do\n        route.process(req, res)\n      end.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAuthorizedError)\n      expect(ignored_called).to be_falsey\n    end\n\n    it \"chains to other routes after calling its handlers\" do\n      inner_route = Puppet::Network::HTTP::Route.path(%r{^/inner}).any(respond(\"inner\"))\n      unused_inner_route = Puppet::Network::HTTP::Route.path(%r{^/unused_inner}).any(respond(\"unused\"))\n\n      top_route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).any(respond(\"top\")).chain(unused_inner_route, inner_route)\n      top_route.process(request(\"GET\", \"/vtest/inner\"), res)\n\n      expect(res.body).to eq(\"topinner\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/http_pool_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/network/http_pool'\n\nclass Puppet::Network::HttpPool::FooClient\n  def initialize(host, port, options = {})\n    @host = host\n    @port = port\n  end\n  attr_reader :host, :port\nend\n\ndescribe Puppet::Network::HttpPool do\n  include PuppetSpec::Files\n\n  describe \"when registering an http client class\" do\n    let(:http_impl) { Puppet::Network::HttpPool::FooClient }\n\n    around :each do |example|\n      orig_class = Puppet::Network::HttpPool.http_client_class\n      begin\n        example.run\n      ensure\n        Puppet::Network::HttpPool.http_client_class = orig_class\n      end\n    end\n\n    it \"returns instances of the http client class\" do\n      Puppet::Network::HttpPool.http_client_class = http_impl\n      http = Puppet::Network::HttpPool.http_instance(\"me\", 54321)\n      expect(http).to be_an_instance_of(http_impl)\n      expect(http.host).to eq('me')\n      expect(http.port).to eq(54321)\n    end\n\n    it \"uses the default http client\" do\n      expect(Puppet.runtime[:http]).to be_an_instance_of(Puppet::HTTP::Client)\n    end\n\n    it \"switches to the external client implementation\" do\n      Puppet::Network::HttpPool.http_client_class = http_impl\n\n      expect(Puppet.runtime[:http]).to be_an_instance_of(Puppet::HTTP::ExternalClient)\n    end\n\n    it \"always uses an explicitly registered http implementation\" do\n      Puppet::Network::HttpPool.http_client_class = http_impl\n\n      new_impl = double('new_http_impl')\n      Puppet.initialize_settings([], true, true, http: new_impl)\n\n      expect(Puppet.runtime[:http]).to eq(new_impl)\n    end\n  end\n\n  describe \"when managing http instances\" do\n    it \"should return an http instance created with the passed host and port\" do\n      http = Puppet::Network::HttpPool.http_instance(\"me\", 54321)\n      expect(http).to be_a_kind_of Puppet::Network::HTTP::Connection\n      expect(http.address).to eq('me')\n      expect(http.port).to    eq(54321)\n    end\n\n    it \"should enable ssl on the http instance by default\" do\n      expect(Puppet::Network::HttpPool.http_instance(\"me\", 54321)).to be_use_ssl\n    end\n\n    it \"can set ssl using an option\" do\n      expect(Puppet::Network::HttpPool.http_instance(\"me\", 54321, false)).not_to be_use_ssl\n      expect(Puppet::Network::HttpPool.http_instance(\"me\", 54321, true)).to be_use_ssl\n    end\n\n    context \"when calling 'connection'\" do\n      it 'requires an ssl_context' do\n        expect {\n          Puppet::Network::HttpPool.connection('me', 8140)\n        }.to raise_error(ArgumentError, \"An ssl_context is required when connecting to 'https://me:8140'\")\n      end\n\n      it 'creates a verifier from the context' do\n        ssl_context = Puppet::SSL::SSLContext.new\n        expect(\n          Puppet::Network::HttpPool.connection('me', 8140, ssl_context: ssl_context).verifier\n        ).to be_a_kind_of(Puppet::SSL::Verifier)\n      end\n\n      it 'does not use SSL when specified' do\n        expect(Puppet::Network::HttpPool.connection('me', 8140, use_ssl: false)).to_not be_use_ssl\n      end\n\n      it 'defaults to SSL' do\n        ssl_context = Puppet::SSL::SSLContext.new\n        conn = Puppet::Network::HttpPool.connection('me', 8140, ssl_context: ssl_context)\n        expect(conn).to be_use_ssl\n      end\n\n      it 'warns if an ssl_context is used for an http connection' do\n        expect(Puppet).to receive(:warning).with(\"An ssl_context is unnecessary when connecting to 'http://me:8140' and will be ignored\")\n\n        ssl_context = Puppet::SSL::SSLContext.new\n        Puppet::Network::HttpPool.connection('me', 8140, use_ssl: false, ssl_context: ssl_context)\n      end\n    end\n\n    describe 'peer verification' do\n\n      before(:each) do\n        Puppet[:ssldir] = tmpdir('ssl')\n        Puppet.settings.use(:main)\n\n        Puppet[:certname] = 'signed'\n        File.write(Puppet[:localcacert], cert_fixture('ca.pem'))\n        File.write(Puppet[:hostcrl], crl_fixture('crl.pem'))\n        File.write(Puppet[:hostcert], cert_fixture('signed.pem'))\n        File.write(Puppet[:hostprivkey], key_fixture('signed-key.pem'))\n      end\n\n      it 'enables peer verification by default' do\n        stub_request(:get, \"https://me:54321\")\n\n        conn = Puppet::Network::HttpPool.http_instance(\"me\", 54321, true)\n        expect_any_instance_of(Net::HTTP).to receive(:start) do |http|\n          expect(http.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)\n        end\n\n        conn.get('/')\n      end\n\n      it 'can disable peer verification' do\n        stub_request(:get, \"https://me:54321\")\n\n        conn = Puppet::Network::HttpPool.http_instance(\"me\", 54321, true, false)\n        expect_any_instance_of(Net::HTTP).to receive(:start) do |http|\n          expect(http.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)\n        end\n\n        conn.get('/')\n      end\n    end\n\n    it \"should not cache http instances\" do\n      expect(Puppet::Network::HttpPool.http_instance(\"me\", 54321)).\n        not_to equal(Puppet::Network::HttpPool.http_instance(\"me\", 54321))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/network/uri_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/network/uri'\n\ndescribe Puppet::Network::Uri do\n  include Puppet::Network::Uri\n  describe '.mask_credentials' do\n    let(:address_with_passwd) { 'https://admin:S3cr3T@puppetforge.acmecorp.com/' }\n    let(:masked) { 'https://admin:***@puppetforge.acmecorp.com/' }\n    let(:address) { 'https://puppetforge.acmecorp.com/' }\n\n    subject do\n      input = to_be_masked.dup\n      result = mask_credentials(input)\n      raise 'illegal unexpected modification' if input != to_be_masked\n      result\n    end\n\n    describe 'if password was given in URI' do\n      describe 'as a String' do\n        let(:to_be_masked) { address_with_passwd }\n        it 'should mask out password' do\n          is_expected.to eq(masked)\n        end\n      end\n      describe 'as an URI' do\n        let(:to_be_masked) { URI.parse(address_with_passwd) }\n        it 'should mask out password' do\n          is_expected.to eq(masked)\n        end\n      end\n    end\n    describe \"if password wasn't given in URI\" do\n      describe 'as a String' do\n        let(:to_be_masked) { address }\n        it \"shouldn't add mask to URI\" do\n          is_expected.to eq(address)\n        end\n      end\n      describe 'as an URI' do\n        let(:to_be_masked) { URI.parse(address) }\n        it \"shouldn't add mask to URI\" do\n          is_expected.to eq(address)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/node/environment_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'tmpdir'\n\nrequire 'puppet/node/environment'\nrequire 'puppet/util/execution'\nrequire 'puppet_spec/modules'\nrequire 'puppet/parser/parser_factory'\n\ndescribe Puppet::Node::Environment do\n  let(:env) { Puppet::Node::Environment.create(\"testing\", []) }\n\n  include PuppetSpec::Files\n\n  context 'the environment' do\n    it \"converts an environment to string when converting to YAML\" do\n      expect(env.to_yaml).to match(/--- testing/)\n    end\n\n    describe \".create\" do\n      it \"creates equivalent environments whether specifying name as a symbol or a string\" do\n        expect(Puppet::Node::Environment.create(:one, [])).to eq(Puppet::Node::Environment.create(\"one\", []))\n      end\n\n      it \"interns name\" do\n        expect(Puppet::Node::Environment.create(\"one\", []).name).to equal(:one)\n      end\n      it \"does not produce environment singletons\" do\n        expect(Puppet::Node::Environment.create(\"one\", [])).to_not equal(Puppet::Node::Environment.create(\"one\", []))\n      end\n    end\n\n    it \"returns its name when converted to a string\" do\n      expect(env.to_s).to eq(\"testing\")\n    end\n\n    it \"has an inspect method for debugging\" do\n      e = Puppet::Node::Environment.create(:test, ['/modules/path', '/other/modules'], '/manifests/path')\n      expect(\"a #{e} env\").to eq(\"a test env\")\n      expect(e.inspect).to match(%r{<Puppet::Node::Environment:\\w* @name=\"test\" @manifest=\"#{File.expand_path('/manifests/path')}\" @modulepath=\"#{File.expand_path('/modules/path')}:#{File.expand_path('/other/modules')}\" >})\n    end\n\n    describe \"externalizing filepaths\" do\n      before(:each) do\n        env.resolved_path = \"/opt/puppetlabs/envs/prod_123\"\n        env.configured_path = \"/etc/puppetlabs/envs/prod\"\n\n        @vendored_manifest = \"/opt/puppetlabs/vendored/modules/foo/manifests/init.pp\"\n        @production_manifest = \"/opt/puppetlabs/envs/prod_123/modules/foo/manifests/init.pp\"\n      end\n\n      it \"leaves paths alone if they do not match the resolved path\" do\n        expect(env.externalize_path(@vendored_manifest)).to eq(@vendored_manifest)\n      end\n\n      it \"leaves paths alone if resolved or configured paths are not set\" do\n        env.resolved_path = nil\n        env.configured_path = nil\n        expect(env.externalize_path(@production_manifest)).to eq(@production_manifest)\n      end\n\n      it \"replaces resolved paths with configured paths\" do\n        externalized_path = env.externalize_path(@production_manifest)\n        expect(externalized_path).to eq(\"/etc/puppetlabs/envs/prod/modules/foo/manifests/init.pp\")\n      end\n\n      it \"handles nil\" do\n        externalized_path = env.externalize_path(nil)\n        expect(externalized_path).to eq(nil)\n      end\n\n      it \"appropriately handles mismatched trailing slashes\" do\n        env.resolved_path = \"/opt/puppetlabs/envs/prod_123/\"\n        externalized_path = env.externalize_path(@production_manifest)\n        expect(externalized_path).to eq(\"/etc/puppetlabs/envs/prod/modules/foo/manifests/init.pp\")\n      end\n\n      it \"can be disabled with the `report_configured_environmentpath` setting\" do\n        Puppet[:report_configured_environmentpath] = false\n        expect(env.externalize_path(@production_manifest)).to eq(@production_manifest)\n      end\n    end\n\n    describe \"equality\" do\n      it \"works as a hash key\" do\n        base = Puppet::Node::Environment.create(:first, [\"modules\"], \"manifests\")\n        same = Puppet::Node::Environment.create(:first, [\"modules\"], \"manifests\")\n        different = Puppet::Node::Environment.create(:first, [\"different\"], \"manifests\")\n        hash = {}\n\n        hash[base] = \"base env\"\n        hash[same] = \"same env\"\n        hash[different] = \"different env\"\n\n        expect(hash[base]).to eq(\"same env\")\n        expect(hash[different]).to eq(\"different env\")\n        expect(hash.count).to eq 2\n      end\n\n      it \"is equal when name, modules, and manifests are the same\" do\n        base = Puppet::Node::Environment.create(:base, [\"modules\"], \"manifests\")\n        different_name = Puppet::Node::Environment.create(:different, base.full_modulepath, base.manifest)\n\n        expect(base).to_not eq(\"not an environment\")\n\n        expect(base).to eq(base)\n        expect(base.hash).to eq(base.hash)\n\n        expect(base.override_with(:modulepath => [\"different\"])).to_not eq(base)\n        expect(base.override_with(:modulepath => [\"different\"]).hash).to_not eq(base.hash)\n\n        expect(base.override_with(:manifest => \"different\")).to_not eq(base)\n        expect(base.override_with(:manifest => \"different\").hash).to_not eq(base.hash)\n\n        expect(different_name).to_not eq(base)\n        expect(different_name.hash).to_not eq(base.hash)\n      end\n    end\n\n    describe \"overriding an existing environment\" do\n      let(:original_path) { [tmpdir('original')] }\n      let(:new_path) { [tmpdir('new')] }\n      let(:environment) { Puppet::Node::Environment.create(:overridden, original_path, 'orig.pp', '/config/script') }\n\n      it \"overrides modulepath\" do\n        overridden = environment.override_with(:modulepath => new_path)\n        expect(overridden).to_not be_equal(environment)\n        expect(overridden.name).to eq(:overridden)\n        expect(overridden.manifest).to eq(File.expand_path('orig.pp'))\n        expect(overridden.modulepath).to eq(new_path)\n        expect(overridden.config_version).to eq('/config/script')\n      end\n\n      it \"overrides manifest\" do\n        overridden = environment.override_with(:manifest => 'new.pp')\n        expect(overridden).to_not be_equal(environment)\n        expect(overridden.name).to eq(:overridden)\n        expect(overridden.manifest).to eq(File.expand_path('new.pp'))\n        expect(overridden.modulepath).to eq(original_path)\n        expect(overridden.config_version).to eq('/config/script')\n      end\n\n      it \"overrides config_version\" do\n        overridden = environment.override_with(:config_version => '/new/script')\n        expect(overridden).to_not be_equal(environment)\n        expect(overridden.name).to eq(:overridden)\n        expect(overridden.manifest).to eq(File.expand_path('orig.pp'))\n        expect(overridden.modulepath).to eq(original_path)\n        expect(overridden.config_version).to eq('/new/script')\n      end\n    end\n\n    describe \"when managing known resource types\" do\n      before do\n        allow(env).to receive(:perform_initial_import).and_return(Puppet::Parser::AST::Hostclass.new(''))\n      end\n\n      it \"creates a resource type collection if none exists\" do\n        expect(env.known_resource_types).to be_kind_of(Puppet::Resource::TypeCollection)\n      end\n\n      it \"memoizes resource type collection\" do\n        expect(env.known_resource_types).to equal(env.known_resource_types)\n      end\n\n      it \"performs the initial import when creating a new collection\" do\n        expect(env).to receive(:perform_initial_import).and_return(Puppet::Parser::AST::Hostclass.new(''))\n        env.known_resource_types\n      end\n\n      it \"generates a new TypeCollection if the current one requires reparsing\" do\n        old_type_collection = env.known_resource_types\n        allow(old_type_collection).to receive(:parse_failed?).and_return(true)\n\n        env.check_for_reparse\n\n        new_type_collection = env.known_resource_types\n        expect(new_type_collection).to be_a Puppet::Resource::TypeCollection\n        expect(new_type_collection).to_not equal(old_type_collection)\n      end\n    end\n\n    it \"validates the modulepath directories\" do\n      real_file = tmpdir('moduledir')\n      path = ['/one', '/two', real_file]\n\n      env = Puppet::Node::Environment.create(:test, path)\n\n      expect(env.modulepath).to eq([real_file])\n    end\n\n    it \"prefixes the value of the 'PUPPETLIB' environment variable to the module path if present\" do\n      first_puppetlib = tmpdir('puppetlib1')\n      second_puppetlib = tmpdir('puppetlib2')\n      first_moduledir = tmpdir('moduledir1')\n      second_moduledir = tmpdir('moduledir2')\n      Puppet::Util.withenv(\"PUPPETLIB\" => [first_puppetlib, second_puppetlib].join(File::PATH_SEPARATOR)) do\n        env = Puppet::Node::Environment.create(:testing, [first_moduledir, second_moduledir])\n\n        expect(env.modulepath).to eq([first_puppetlib, second_puppetlib, first_moduledir, second_moduledir])\n      end\n    end\n\n    describe \"validating manifest settings\" do\n      before(:each) do\n        Puppet[:default_manifest] = \"/default/manifests/site.pp\"\n      end\n\n      it \"has no validation errors when disable_per_environment_manifest is false\" do\n        expect(Puppet::Node::Environment.create(:directory, [], '/some/non/default/manifest.pp').validation_errors).to be_empty\n      end\n\n      context \"when disable_per_environment_manifest is true\" do\n        let(:config) { double('config') }\n        let(:global_modulepath) { [\"/global/modulepath\"] }\n        let(:envconf) { Puppet::Settings::EnvironmentConf.new(\"/some/direnv\", config, global_modulepath) }\n\n        before(:each) do\n          Puppet[:disable_per_environment_manifest] = true\n        end\n\n        def assert_manifest_conflict(expectation, envconf_manifest_value)\n          expect(config).to receive(:setting).with(:manifest).and_return(\n            double('setting', :value => envconf_manifest_value)\n          )\n          environment = Puppet::Node::Environment.create(:directory, [], '/default/manifests/site.pp')\n          loader = Puppet::Environments::Static.new(environment)\n          allow(loader).to receive(:get_conf).and_return(envconf)\n\n          Puppet.override(:environments => loader) do\n            if expectation\n              expect(environment.validation_errors).to have_matching_element(/The 'disable_per_environment_manifest' setting is true.*and the.*environment.*conflicts/)\n            else\n              expect(environment.validation_errors).to be_empty\n            end\n          end\n        end\n\n        it \"has conflicting_manifest_settings when environment.conf manifest was set\" do\n          assert_manifest_conflict(true, '/some/envconf/manifest/site.pp')\n        end\n\n        it \"does not have conflicting_manifest_settings when environment.conf manifest is empty\" do\n          assert_manifest_conflict(false, '')\n        end\n\n        it \"does not have conflicting_manifest_settings when environment.conf manifest is nil\" do\n          assert_manifest_conflict(false, nil)\n        end\n\n        it \"does not have conflicting_manifest_settings when environment.conf manifest is an exact, uninterpolated match of default_manifest\" do\n          assert_manifest_conflict(false, '/default/manifests/site.pp')\n        end\n      end\n    end\n\n    describe \"when modeling a specific environment\" do\n      let(:first_modulepath) { tmpdir('firstmodules') }\n      let(:second_modulepath) { tmpdir('secondmodules') }\n      let(:env) { Puppet::Node::Environment.create(:modules_test, [first_modulepath, second_modulepath]) }\n      let(:module_options) {\n        {\n          :environment => env,\n          :metadata => {\n            :author       => 'puppetlabs',\n          },\n        }\n      }\n\n      describe \"module data\" do\n        describe \".module\" do\n\n          it \"returns an individual module that exists in its module path\" do\n            one = PuppetSpec::Modules.create('one', first_modulepath, module_options)\n            expect(env.module('one')).to eq(one)\n          end\n\n          it \"returns nil if asked for a module that does not exist in its path\" do\n            expect(env.module(\"doesnotexist\")).to be_nil\n          end\n        end\n\n        describe \"#modules_by_path\" do\n          it \"returns an empty list if there are no modules\" do\n            expect(env.modules_by_path).to eq({\n              first_modulepath => [],\n              second_modulepath => []\n            })\n          end\n\n          it \"includes modules even if they exist in multiple dirs in the modulepath\" do\n            one = PuppetSpec::Modules.create('one', first_modulepath, module_options)\n            two = PuppetSpec::Modules.create('two', second_modulepath, module_options)\n\n            expect(env.modules_by_path).to eq({\n              first_modulepath  => [one],\n              second_modulepath => [two],\n            })\n          end\n\n          it \"ignores modules with invalid names\" do\n            PuppetSpec::Modules.generate_files('foo', first_modulepath)\n            PuppetSpec::Modules.generate_files('.foo', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo2', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo-bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo_bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo=bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo.bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('-foo', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo-', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo--bar', first_modulepath)\n\n            expect(env.modules_by_path[first_modulepath].collect{|mod| mod.name}.sort).to eq(%w{foo foo2 foo_bar})\n          end\n\n        end\n\n        describe \"#module_requirements\" do\n          it \"returns a list of what modules depend on other modules\" do\n            PuppetSpec::Modules.create(\n              'foo',\n              first_modulepath,\n              :metadata => {\n                :author       => 'puppetlabs',\n                :dependencies => [{ 'name' => 'puppetlabs/bar', \"version_requirement\" => \">= 1.0.0\" }]\n              }\n            )\n            PuppetSpec::Modules.create(\n              'bar',\n              second_modulepath,\n              :metadata => {\n                :author       => 'puppetlabs',\n                :dependencies => [{ 'name' => 'puppetlabs/foo', \"version_requirement\" => \"<= 2.0.0\" }]\n              }\n            )\n            PuppetSpec::Modules.create(\n              'baz',\n              first_modulepath,\n              :metadata => {\n                :author       => 'puppetlabs',\n                :dependencies => [{ 'name' => 'puppetlabs-bar', \"version_requirement\" => \"3.0.0\" }]\n              }\n            )\n            PuppetSpec::Modules.create(\n              'alpha',\n              first_modulepath,\n              :metadata => {\n                :author       => 'puppetlabs',\n                :dependencies => [{ 'name' => 'puppetlabs/bar', \"version_requirement\" => \"~3.0.0\" }]\n              }\n            )\n\n            expect(env.module_requirements).to eq({\n              'puppetlabs/alpha' => [],\n              'puppetlabs/foo' => [\n                {\n                  \"name\"    => \"puppetlabs/bar\",\n                  \"version\" => \"9.9.9\",\n                  \"version_requirement\" => \"<= 2.0.0\"\n                }\n              ],\n              'puppetlabs/bar' => [\n                {\n                  \"name\"    => \"puppetlabs/alpha\",\n                  \"version\" => \"9.9.9\",\n                  \"version_requirement\" => \"~3.0.0\"\n                },\n                {\n                  \"name\"    => \"puppetlabs/baz\",\n                  \"version\" => \"9.9.9\",\n                  \"version_requirement\" => \"3.0.0\"\n                },\n                {\n                  \"name\"    => \"puppetlabs/foo\",\n                  \"version\" => \"9.9.9\",\n                  \"version_requirement\" => \">= 1.0.0\"\n                }\n              ],\n              'puppetlabs/baz' => []\n            })\n          end\n        end\n\n        describe \".module_by_forge_name\" do\n          it \"finds modules by forge_name\" do\n            mod = PuppetSpec::Modules.create(\n              'baz',\n              first_modulepath,\n              module_options\n            )\n            expect(env.module_by_forge_name('puppetlabs/baz')).to eq(mod)\n          end\n\n          it \"does not find modules with same name by the wrong author\" do\n            PuppetSpec::Modules.create(\n              'baz',\n              first_modulepath,\n              :metadata => {:author => 'sneakylabs'},\n              :environment => env\n            )\n            expect(env.module_by_forge_name('puppetlabs/baz')).to eq(nil)\n          end\n\n          it \"returns nil when the module can't be found\" do\n            expect(env.module_by_forge_name('ima/nothere')).to be_nil\n          end\n        end\n\n        describe \".modules\" do\n          it \"returns an empty list if there are no modules\" do\n            expect(env.modules).to eq([])\n          end\n\n          it \"returns a module named for every directory in each module path\" do\n            %w{foo bar}.each do |mod_name|\n              PuppetSpec::Modules.generate_files(mod_name, first_modulepath)\n            end\n            %w{bee baz}.each do |mod_name|\n              PuppetSpec::Modules.generate_files(mod_name, second_modulepath)\n            end\n            expect(env.modules.collect{|mod| mod.name}.sort).to eq(%w{foo bar bee baz}.sort)\n          end\n\n          it \"removes duplicates\" do\n            PuppetSpec::Modules.generate_files('foo', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo', second_modulepath)\n\n            expect(env.modules.collect{|mod| mod.name}.sort).to eq(%w{foo})\n          end\n\n          it \"ignores modules with invalid names\" do\n            PuppetSpec::Modules.generate_files('foo', first_modulepath)\n            PuppetSpec::Modules.generate_files('.foo', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo2', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo-bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo_bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo=bar', first_modulepath)\n            PuppetSpec::Modules.generate_files('foo bar', first_modulepath)\n\n            expect(env.modules.collect{|mod| mod.name}.sort).to eq(%w{foo foo2 foo_bar})\n          end\n\n          it \"creates modules with the correct environment\" do\n            PuppetSpec::Modules.generate_files('foo', first_modulepath)\n\n            env.modules.each do |mod|\n              expect(mod.environment).to eq(env)\n            end\n          end\n\n          it \"logs an exception if a module contains invalid metadata\" do\n            PuppetSpec::Modules.generate_files(\n              'foo',\n              first_modulepath,\n              :metadata => {\n                :author       => 'puppetlabs'\n                # missing source, version, etc\n              }\n            )\n\n            expect(Puppet).to receive(:log_exception).with(be_a(Puppet::Module::MissingMetadata))\n\n            env.modules\n          end\n\n          it \"includes the Bolt project in modules if it's defined\" do\n            path = tmpdir('project')\n            PuppetSpec::Modules.generate_files('bolt_project', path)\n            project = Struct.new(\"Project\", :name, :path, :load_as_module?).new('bolt_project', path, true)\n\n            Puppet.override(bolt_project: project) do\n              %w{foo bar}.each do |mod_name|\n                PuppetSpec::Modules.generate_files(mod_name, first_modulepath)\n              end\n              %w{bee baz}.each do |mod_name|\n                PuppetSpec::Modules.generate_files(mod_name, second_modulepath)\n              end\n              expect(env.modules.collect{|mod| mod.name}.sort).to eq(%w{bolt_project foo bar bee baz}.sort)\n            end\n          end\n\n          it \"does not include the Bolt project in modules if load_as_module? is false\" do\n            path = tmpdir('project')\n            PuppetSpec::Modules.generate_files('bolt_project', path)\n            project = Struct.new(\"Project\", :name, :path, :load_as_module?).new('bolt_project', path, false)\n\n            Puppet.override(bolt_project: project) do\n              %w{foo bar}.each do |mod_name|\n                PuppetSpec::Modules.generate_files(mod_name, first_modulepath)\n              end\n              %w{bee baz}.each do |mod_name|\n                PuppetSpec::Modules.generate_files(mod_name, second_modulepath)\n              end\n              expect(env.modules.collect{|mod| mod.name}.sort).to eq(%w{foo bar bee baz}.sort)\n            end\n\n          end\n        end\n      end\n    end\n\n    describe \"when performing initial import\" do\n      let(:loaders) { Puppet::Pops::Loaders.new(env) }\n\n      before(:each) do\n        allow_any_instance_of(Puppet::Parser::Compiler).to receive(:loaders).and_return(loaders)\n        Puppet.push_context({:loaders => loaders, :current_environment => env})\n      end\n\n      after(:each) do\n        Puppet.pop_context()\n      end\n\n      it \"loads from Puppet[:code]\" do\n        Puppet[:code] = \"define foo {}\"\n        krt = env.known_resource_types\n        expect(krt.find_definition('foo')).to be_kind_of(Puppet::Resource::Type)\n      end\n\n      it \"parses from the environment's manifests if Puppet[:code] is not set\" do\n        filename = tmpfile('a_manifest.pp')\n        File.open(filename, 'w') do |f|\n          f.puts(\"define from_manifest {}\")\n        end\n        env = Puppet::Node::Environment.create(:testing, [], filename)\n        krt = env.known_resource_types\n        expect(krt.find_definition('from_manifest')).to be_kind_of(Puppet::Resource::Type)\n      end\n\n      it \"prefers Puppet[:code] over manifest files\" do\n        Puppet[:code] = \"define from_code_setting {}\"\n        filename = tmpfile('a_manifest.pp')\n        File.open(filename, 'w') do |f|\n          f.puts(\"define from_manifest {}\")\n        end\n        env = Puppet::Node::Environment.create(:testing, [], filename)\n        krt = env.known_resource_types\n        expect(krt.find_definition('from_code_setting')).to be_kind_of(Puppet::Resource::Type)\n      end\n\n      it \"initial import proceeds even if manifest file does not exist on disk\" do\n        filename = tmpfile('a_manifest.pp')\n        env = Puppet::Node::Environment.create(:testing, [], filename)\n        expect(env.known_resource_types).to be_kind_of(Puppet::Resource::TypeCollection)\n      end\n\n      it \"returns an empty TypeCollection if neither code nor manifests is present\" do\n        expect(env.known_resource_types).to be_kind_of(Puppet::Resource::TypeCollection)\n      end\n\n      it \"fails helpfully if there is an error importing\" do\n        Puppet[:code] = \"oops {\"\n        expect do\n          env.known_resource_types\n        end.to raise_error(Puppet::Error, /Could not parse for environment #{env.name}/)\n      end\n\n      it \"should mark the type collection as needing a reparse when there is an error parsing\" do\n        Puppet[:code] = \"oops {\"\n        expect do\n          env.known_resource_types\n        end.to raise_error(Puppet::Error, /Syntax error at .../)\n        expect(env.known_resource_types.parse_failed?).to be_truthy\n      end\n    end\n  end\n\n  describe \"managing module translations\" do\n    context \"when i18n is enabled\" do\n      before(:each) do\n        Puppet[:disable_i18n] = false\n      end\n\n      it \"yields block results\" do\n        ran = false\n        expect(env.with_text_domain { ran = true; :result }).to eq(:result)\n        expect(ran).to eq(true)\n      end\n\n      it \"creates a new text domain the first time we try to use the text domain\" do\n        expect(Puppet::GettextConfig).to receive(:reset_text_domain).with(env.name)\n        expect(Puppet::ModuleTranslations).to receive(:load_from_modulepath)\n        expect(Puppet::GettextConfig).to receive(:clear_text_domain)\n\n        env.with_text_domain do; end\n      end\n\n      it \"uses the existing text domain once it has been created\" do\n        env.with_text_domain do; end\n\n        expect(Puppet::GettextConfig).to receive(:use_text_domain).with(env.name)\n        env.with_text_domain do; end\n      end\n    end\n\n    context \"when i18n is disabled\" do\n      it \"yields block results\" do\n        ran = false\n        expect(env.with_text_domain { ran = true; :result }).to eq(:result)\n        expect(ran).to eq(true)\n      end\n\n      it \"does not create a new text domain the first time we try to use the text domain\" do\n        expect(Puppet::GettextConfig).not_to receive(:reset_text_domain)\n        expect(Puppet::ModuleTranslations).not_to receive(:load_from_modulepath)\n\n        env.with_text_domain do; end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/node/facts_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/node/facts'\nrequire 'matchers/json'\n\ndescribe Puppet::Node::Facts, \"when indirecting\" do\n  include JSONMatchers\n\n  before do\n    @facts = Puppet::Node::Facts.new(\"me\")\n  end\n\n  describe \"adding local facts\" do\n    it \"should add the node's certificate name as the 'clientcert' fact\" do\n      @facts.add_local_facts\n      expect(@facts.values[\"clientcert\"]).to eq(Puppet.settings[:certname])\n    end\n\n    it \"adds the Puppet version as a 'clientversion' fact\" do\n      @facts.add_local_facts\n      expect(@facts.values[\"clientversion\"]).to eq(Puppet.version.to_s)\n    end\n\n    it \"adds the agent side noop setting as 'clientnoop'\" do\n      @facts.add_local_facts\n      expect(@facts.values[\"clientnoop\"]).to eq(Puppet.settings[:noop])\n    end\n\n    it \"doesn't add the current environment\" do\n      @facts.add_local_facts\n      expect(@facts.values).not_to include(\"environment\")\n    end\n\n    it \"doesn't replace any existing environment fact when adding local facts\" do\n      @facts.values[\"environment\"] = \"foo\"\n      @facts.add_local_facts\n      expect(@facts.values[\"environment\"]).to eq(\"foo\")\n    end\n  end\n\n  describe \"when sanitizing facts\" do\n    it \"should convert fact values if needed\" do\n      @facts.values[\"test\"] = /foo/\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq(\"(?-mix:foo)\")\n    end\n\n    it \"should convert hash keys if needed\" do\n      @facts.values[\"test\"] = {/foo/ => \"bar\"}\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq({\"(?-mix:foo)\" => \"bar\"})\n    end\n\n    it \"should convert hash values if needed\" do\n      @facts.values[\"test\"] = {\"foo\" => /bar/}\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq({\"foo\" => \"(?-mix:bar)\"})\n    end\n\n    it \"should convert array elements if needed\" do\n      @facts.values[\"test\"] = [1, \"foo\", /bar/]\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq([1, \"foo\", \"(?-mix:bar)\"])\n    end\n\n    it \"should handle nested arrays\" do\n      @facts.values[\"test\"] = [1, \"foo\", [/bar/]]\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq([1, \"foo\", [\"(?-mix:bar)\"]])\n    end\n\n    it \"should handle nested hashes\" do\n      @facts.values[\"test\"] = {/foo/ => {\"bar\" => /baz/}}\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq({\"(?-mix:foo)\" => {\"bar\" => \"(?-mix:baz)\"}})\n    end\n\n    it \"should handle nester arrays and hashes\" do\n      @facts.values[\"test\"] = {/foo/ => [\"bar\", /baz/]}\n      @facts.sanitize\n      expect(@facts.values[\"test\"]).to eq({\"(?-mix:foo)\" => [\"bar\", \"(?-mix:baz)\"]})\n    end\n\n    it \"should handle alien values having a to_s that returns ascii-8bit\" do\n      class Alien\n      end\n      an_alien = Alien.new\n      @facts.values[\"test\"] = an_alien\n      @facts.sanitize\n      fact_value = @facts.values['test']\n      expect(fact_value).to eq(an_alien.to_s)\n      # JRuby 9.2.8 reports US-ASCII which is a subset of UTF-8\n      expect(fact_value.encoding).to eq(Encoding::UTF_8).or eq(Encoding::US_ASCII)\n    end\n\n  end\n\n  describe \"when indirecting\" do\n    before do\n      @indirection = double('indirection', :request => double('request'), :name => :facts)\n\n      @facts = Puppet::Node::Facts.new(\"me\", \"one\" => \"two\")\n    end\n\n    it \"should redirect to the specified fact store for storage\" do\n      allow(Puppet::Node::Facts).to receive(:indirection).and_return(@indirection)\n      expect(@indirection).to receive(:save)\n      Puppet::Node::Facts.indirection.save(@facts)\n    end\n\n    describe \"when the Puppet application is 'master'\" do\n      it \"should default to the 'yaml' terminus\" do\n        pending \"Cannot test the behavior of defaults in defaults.rb\"\n        expect(Puppet::Node::Facts.indirection.terminus_class).to eq(:yaml)\n      end\n    end\n\n    describe \"when the Puppet application is not 'master'\" do\n      it \"should default to the 'facter' terminus\" do\n        pending \"Cannot test the behavior of defaults in defaults.rb\"\n        expect(Puppet::Node::Facts.indirection.terminus_class).to eq(:facter)\n      end\n    end\n\n  end\n\n  describe \"when storing and retrieving\" do\n    it \"doesn't manufacture a `_timestamp` fact value\" do\n      values = {\"one\" => \"two\", \"three\" => \"four\"}\n      facts = Puppet::Node::Facts.new(\"mynode\", values)\n\n      expect(facts.values).to eq(values)\n    end\n\n    describe \"when deserializing from yaml\" do\n      let(:timestamp)  { Time.parse(\"Thu Oct 28 11:16:31 -0700 2010\") }\n      let(:expiration) { Time.parse(\"Thu Oct 28 11:21:31 -0700 2010\") }\n\n      def create_facts(values = {})\n        Puppet::Node::Facts.new('mynode', values)\n      end\n\n      def deserialize_yaml_facts(facts)\n        facts.sanitize\n        format = Puppet::Network::FormatHandler.format('yaml')\n        format.intern(Puppet::Node::Facts, YAML.dump(facts.to_data_hash))\n      end\n\n      it 'preserves `_timestamp` value' do\n        facts = deserialize_yaml_facts(create_facts('_timestamp' => timestamp))\n\n        expect(facts.timestamp).to eq(timestamp)\n      end\n\n      it \"doesn't preserve the `_timestamp` fact\" do\n        facts = deserialize_yaml_facts(create_facts('_timestamp' => timestamp))\n\n        expect(facts.values['_timestamp']).to be_nil\n      end\n\n      it 'preserves expiration time if present' do\n        old_facts = create_facts\n        old_facts.expiration = expiration\n        facts = deserialize_yaml_facts(old_facts)\n\n        expect(facts.expiration).to eq(expiration)\n      end\n\n      it 'ignores expiration time if absent' do\n        facts = deserialize_yaml_facts(create_facts)\n\n        expect(facts.expiration).to be_nil\n      end\n    end\n\n    describe \"using json\" do\n      before :each do\n        @timestamp = Time.parse(\"Thu Oct 28 11:16:31 -0700 2010\")\n        @expiration = Time.parse(\"Thu Oct 28 11:21:31 -0700 2010\")\n      end\n\n      it \"should accept properly formatted json\" do\n        json = %Q({\"name\": \"foo\", \"expiration\": \"#{@expiration}\", \"timestamp\": \"#{@timestamp}\", \"values\": {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\"}})\n        format = Puppet::Network::FormatHandler.format('json')\n        facts = format.intern(Puppet::Node::Facts, json)\n        expect(facts.name).to eq('foo')\n        expect(facts.expiration).to eq(@expiration)\n        expect(facts.timestamp).to eq(@timestamp)\n        expect(facts.values).to eq({'a' => '1', 'b' => '2', 'c' => '3'})\n      end\n\n      it \"should generate properly formatted json\" do\n        allow(Time).to receive(:now).and_return(@timestamp)\n        facts = Puppet::Node::Facts.new(\"foo\", {'a' => 1, 'b' => 2, 'c' => 3})\n        facts.expiration = @expiration\n        result = JSON.parse(facts.to_json)\n        expect(result['name']).to eq(facts.name)\n        expect(result['values']).to eq(facts.values)\n        expect(result['timestamp']).to eq(facts.timestamp.iso8601(9))\n        expect(result['expiration']).to eq(facts.expiration.iso8601(9))\n      end\n\n      it \"should generate valid facts data against the facts schema\" do\n        allow(Time).to receive(:now).and_return(@timestamp)\n        facts = Puppet::Node::Facts.new(\"foo\", {'a' => 1, 'b' => 2, 'c' => 3})\n        facts.expiration = @expiration\n\n        expect(facts.to_json).to validate_against('api/schemas/facts.json')\n      end\n\n      it \"should not include nil values\" do\n        facts = Puppet::Node::Facts.new(\"foo\", {'a' => 1, 'b' => 2, 'c' => 3})\n        json= JSON.parse(facts.to_json)\n        expect(json).not_to be_include(\"expiration\")\n      end\n\n      it \"should be able to handle nil values\" do\n        json = %Q({\"name\": \"foo\", \"values\": {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\"}})\n        format = Puppet::Network::FormatHandler.format('json')\n        facts = format.intern(Puppet::Node::Facts, json)\n        expect(facts.name).to eq('foo')\n        expect(facts.expiration).to be_nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/node_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/json'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Node do\n  include JSONMatchers\n  include PuppetSpec::Files\n\n  let(:environment) { Puppet::Node::Environment.create(:bar, []) }\n  let(:env_loader) { Puppet::Environments::Static.new(environment) }\n\n  describe \"when managing its environment\" do\n    it \"provides an environment instance\" do\n      expect(Puppet::Node.new(\"foo\", :environment => environment).environment.name).to eq(:bar)\n    end\n\n    context \"present in a loader\" do\n      around do |example|\n        Puppet.override(:environments => env_loader) do\n          example.run\n        end\n      end\n\n      it \"uses any set environment\" do\n        expect(Puppet::Node.new(\"foo\", :environment => \"bar\").environment).to eq(environment)\n      end\n\n      it \"determines its environment from its parameters if no environment is set\" do\n        expect(Puppet::Node.new(\"foo\", :parameters => {\"environment\" => :bar}).environment).to eq(environment)\n      end\n\n      it \"uses the configured environment if no environment is provided\" do\n        Puppet[:environment] = environment.name.to_s\n        expect(Puppet::Node.new(\"foo\").environment).to eq(environment)\n      end\n\n      it \"allows the environment to be set after initialization\" do\n        node = Puppet::Node.new(\"foo\")\n        node.environment = :bar\n        expect(node.environment.name).to eq(:bar)\n      end\n\n      it \"sets environment_name with the correct environment name\" do\n        node = Puppet::Node.new(\"foo\")\n        node.environment = Puppet::Node::Environment.remote('www123')\n        expect(node.environment_name).to eq(:www123)\n      end\n\n      it \"allows its environment to be set by parameters after initialization\" do\n        node = Puppet::Node.new(\"foo\")\n        node.parameters[\"environment\"] = :bar\n        expect(node.environment.name).to eq(:bar)\n      end\n    end\n  end\n\n  describe \"serialization\" do\n    around do |example|\n      Puppet.override(:environments => env_loader) do\n        example.run\n      end\n    end\n\n    it \"can round-trip through json\" do\n      Puppet::Node::Facts.new(\"hello\", \"one\" => \"c\", \"two\" => \"b\")\n      node = Puppet::Node.new(\"hello\",\n                              :environment => 'bar',\n                              :classes => ['erth', 'aiu'],\n                              :parameters => {\"hostname\"=>\"food\"}\n                             )\n      new_node = Puppet::Node.convert_from('json', node.render('json'))\n      expect(new_node.environment).to eq(node.environment)\n      expect(new_node.parameters).to eq(node.parameters)\n      expect(new_node.classes).to eq(node.classes)\n      expect(new_node.name).to eq(node.name)\n    end\n\n    it \"validates against the node json schema\" do\n      Puppet::Node::Facts.new(\"hello\", \"one\" => \"c\", \"two\" => \"b\")\n      node = Puppet::Node.new(\"hello\",\n                              :environment => 'bar',\n                              :classes => ['erth', 'aiu'],\n                              :parameters => {\"hostname\"=>\"food\"}\n                             )\n      expect(node.to_json).to validate_against('api/schemas/node.json')\n    end\n\n    it \"when missing optional parameters validates against the node json schema\" do\n      Puppet::Node::Facts.new(\"hello\", \"one\" => \"c\", \"two\" => \"b\")\n      node = Puppet::Node.new(\"hello\",\n                              :environment => 'bar'\n                             )\n      expect(node.to_json).to validate_against('api/schemas/node.json')\n    end\n\n    it \"should allow its environment parameter to be set by attribute after initialization\" do\n      node = Puppet::Node.new(\"foo\", { :parameters => { 'environment' => :foo } })\n      node.environment_name = :foo\n      node.environment = :bar\n      expect(node.environment_name).to eq(:bar)\n      expect(node.parameters['environment']).to eq('bar')\n    end\n\n    it 'to_data_hash returns value that is instance of to Data' do\n      node = Puppet::Node.new(\"hello\",\n        :environment => 'bar',\n        :classes => ['erth', 'aiu'],\n        :parameters => {\"hostname\"=>\"food\"}\n      )\n      expect(Puppet::Pops::Types::TypeFactory.data.instance?(node.to_data_hash)).to be_truthy\n    end\n  end\n\n  describe \"when serializing using yaml\" do\n    before do\n      @node = Puppet::Node.new(\"mynode\")\n    end\n\n    it \"a node can roundtrip\" do\n      expect(Puppet::Util::Yaml.safe_load(@node.to_yaml, [Puppet::Node]).name).to eql(\"mynode\")\n    end\n\n    it \"limits the serialization of environment to be just the name\" do\n      yaml_file = file_containing(\"temp_yaml\", @node.to_yaml)\n      expect(File.read(yaml_file)).to eq(<<~NODE)\n        --- !ruby/object:Puppet::Node\n        name: mynode\n        environment: production\n      NODE\n    end\n  end\n\n  describe \"when serializing using yaml and values classes and parameters are missing in deserialized hash\" do\n    it \"a node can roundtrip\" do\n      @node = Puppet::Node.from_data_hash({'name' => \"mynode\"})\n      expect(Puppet::Util::Yaml.safe_load(@node.to_yaml, [Puppet::Node]).name).to eql(\"mynode\")\n    end\n\n    it \"errors if name is nil\" do\n      expect { Puppet::Node.from_data_hash({ })}.to raise_error(ArgumentError, /No name provided in serialized data/)\n    end\n  end\n\n  describe \"when converting to json\" do\n    before do\n      @node = Puppet::Node.new(\"mynode\")\n    end\n\n    it \"provide its name\" do\n      expect(@node).to set_json_attribute('name').to(\"mynode\")\n    end\n\n    it \"includes the classes if set\" do\n      @node.classes = %w{a b c}\n      expect(@node).to set_json_attribute(\"classes\").to(%w{a b c})\n    end\n\n    it \"does not include the classes if there are none\" do\n      expect(@node).to_not set_json_attribute('classes')\n    end\n\n    it \"includes parameters if set\" do\n      @node.parameters = {\"a\" => \"b\", \"c\" => \"d\"}\n      expect(@node).to set_json_attribute('parameters').to({\"a\" => \"b\", \"c\" => \"d\"})\n    end\n\n    it \"does not include the environment parameter in the json\" do\n      @node.parameters = {\"a\" => \"b\", \"c\" => \"d\"}\n      @node.environment = environment\n      expect(@node.parameters).to eq({\"a\"=>\"b\", \"c\"=>\"d\", \"environment\"=>\"bar\"})\n      expect(@node).to set_json_attribute('parameters').to({\"a\" => \"b\", \"c\" => \"d\"})\n    end\n\n    it \"does not include the parameters if there are none\" do\n      expect(@node).to_not set_json_attribute('parameters')\n    end\n\n    it \"includes the environment\" do\n      @node.environment = \"production\"\n      expect(@node).to set_json_attribute('environment').to('production')\n    end\n  end\n\n  describe \"when converting from json\" do\n    before do\n      @node = Puppet::Node.new(\"mynode\")\n      @format = Puppet::Network::FormatHandler.format('json')\n    end\n\n    def from_json(json)\n      @format.intern(Puppet::Node, json)\n    end\n\n    it \"sets its name\" do\n      expect(Puppet::Node).to read_json_attribute('name').from(@node.to_json).as(\"mynode\")\n    end\n\n    it \"includes the classes if set\" do\n      @node.classes = %w{a b c}\n      expect(Puppet::Node).to read_json_attribute('classes').from(@node.to_json).as(%w{a b c})\n    end\n\n    it \"includes parameters if set\" do\n      @node.parameters = {\"a\" => \"b\", \"c\" => \"d\"}\n      expect(Puppet::Node).to read_json_attribute('parameters').from(@node.to_json).as({\"a\" => \"b\", \"c\" => \"d\", \"environment\" => \"production\"})\n    end\n\n    it \"deserializes environment to environment_name as a symbol\" do\n      Puppet.override(:environments => env_loader) do\n        @node.environment = environment\n        expect(Puppet::Node).to read_json_attribute('environment_name').from(@node.to_json).as(:bar)\n      end\n    end\n\n    it \"does not immediately populate the environment instance\" do\n      node = described_class.from_data_hash(\"name\" => \"foo\", \"environment\" => \"production\")\n\n      expect(node.environment_name).to eq(:production)\n      expect(node).not_to be_has_environment_instance\n      node.environment\n      expect(node).to be_has_environment_instance\n    end\n  end\nend\n\ndescribe Puppet::Node, \"when initializing\" do\n  before do\n    @node = Puppet::Node.new(\"testnode\")\n  end\n\n  it \"sets the node name\" do\n    expect(@node.name).to eq(\"testnode\")\n  end\n\n  it \"does not allow nil node names\" do\n    expect { Puppet::Node.new(nil) }.to raise_error(ArgumentError)\n  end\n\n  it \"defaults to an empty parameter hash\" do\n    expect(@node.parameters).to eq({})\n  end\n\n  it \"defaults to an empty class array\" do\n    expect(@node.classes).to eq([])\n  end\n\n  it \"notes its creation time\" do\n    expect(@node.time).to be_instance_of(Time)\n  end\n\n  it \"accepts parameters passed in during initialization\" do\n    params = {\"a\" => \"b\"}\n    @node = Puppet::Node.new(\"testing\", :parameters => params)\n    expect(@node.parameters).to eq(params)\n  end\n\n  it \"accepts classes passed in during initialization\" do\n    classes = %w{one two}\n    @node = Puppet::Node.new(\"testing\", :classes => classes)\n    expect(@node.classes).to eq(classes)\n  end\n\n  it \"always returns classes as an array\" do\n    @node = Puppet::Node.new(\"testing\", :classes => \"myclass\")\n    expect(@node.classes).to eq([\"myclass\"])\n  end\nend\n\ndescribe Puppet::Node, \"when merging facts\" do\n  before do\n    @node = Puppet::Node.new(\"testnode\")\n    Puppet[:facts_terminus] = :memory\n    Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new(@node.name, \"one\" => \"c\", \"two\" => \"b\"))\n  end\n\n  context \"when supplied facts as a parameter\" do\n    let(:facts) { Puppet::Node::Facts.new(@node.name, \"foo\" => \"bar\") }\n\n    it \"accepts facts to merge with the node\" do\n      expect(@node).to receive(:merge).with({ 'foo' => 'bar' })\n      @node.fact_merge(facts)\n    end\n\n    it \"will not query the facts indirection if facts are supplied\" do\n      expect(Puppet::Node::Facts.indirection).not_to receive(:find)\n      @node.fact_merge(facts)\n    end\n  end\n\n  it \"recovers with a Puppet::Error if something is thrown from the facts indirection\" do\n    expect(Puppet::Node::Facts.indirection).to receive(:find).and_raise(\"something bad happened in the indirector\")\n    expect { @node.fact_merge }.to raise_error(Puppet::Error, /Could not retrieve facts for testnode: something bad happened in the indirector/)\n  end\n\n  it \"prefers parameters already set on the node over facts from the node\" do\n    @node = Puppet::Node.new(\"testnode\", :parameters => {\"one\" => \"a\"})\n    @node.fact_merge\n    expect(@node.parameters[\"one\"]).to eq(\"a\")\n  end\n\n  it \"adds passed parameters to the parameter list\" do\n    @node = Puppet::Node.new(\"testnode\", :parameters => {\"one\" => \"a\"})\n    @node.fact_merge\n    expect(@node.parameters[\"two\"]).to eq(\"b\")\n  end\n\n  it \"warns when a parameter value is not updated\" do\n    @node = Puppet::Node.new(\"testnode\", :parameters => {\"one\" => \"a\"})\n    expect(Puppet).to receive(:warning).with('The node parameter \\'one\\' for node \\'testnode\\' was already set to \\'a\\'. It could not be set to \\'b\\'')\n    @node.merge \"one\" => \"b\"\n  end\n\n  it \"accepts arbitrary parameters to merge into its parameters\" do\n    @node = Puppet::Node.new(\"testnode\", :parameters => {\"one\" => \"a\"})\n    @node.merge \"two\" => \"three\"\n    expect(@node.parameters[\"two\"]).to eq(\"three\")\n  end\n\n  context \"with an env loader\" do\n    let(:environment) { Puppet::Node::Environment.create(:one, []) }\n    let(:environment_two) { Puppet::Node::Environment.create(:two, []) }\n    let(:environment_three) { Puppet::Node::Environment.create(:three, []) }\n    let(:environment_prod) { Puppet::Node::Environment.create(:production, []) }\n    let(:env_loader) { Puppet::Environments::Static.new(environment, environment_two, environment_three, environment_prod) }\n\n    around do |example|\n      Puppet.override(:environments => env_loader) do\n        example.run\n      end\n    end\n\n    context \"when a node is initialized from a data hash\" do\n      context \"when a node is initialzed with an environment\" do\n        it \"uses 'environment' when provided\" do\n          my_node = Puppet::Node.from_data_hash(\"environment\" => \"one\", \"name\" => \"my_node\")\n          expect(my_node.environment.name).to eq(:one)\n        end\n\n        it \"uses the environment parameter when provided\" do\n          my_node = Puppet::Node.from_data_hash(\"parameters\" => {\"environment\" => \"one\"}, \"name\" => \"my_node\")\n          expect(my_node.environment.name).to eq(:one)\n        end\n\n        it \"uses the environment when also given an environment parameter\" do\n          my_node = Puppet::Node.from_data_hash(\"parameters\" => {\"environment\" => \"one\"}, \"name\" => \"my_node\", \"environment\" => \"two\")\n          expect(my_node.environment.name).to eq(:two)\n        end\n\n        it \"uses 'environment' when an environment fact has been merged\" do\n          my_node = Puppet::Node.from_data_hash(\"environment\" => \"one\", \"name\" => \"my_node\")\n          my_node.fact_merge Puppet::Node::Facts.new \"my_node\", \"environment\" => \"two\"\n          expect(my_node.environment.name).to eq(:one)\n        end\n\n        it \"uses an environment parameter when an environment fact has been merged\" do\n          my_node = Puppet::Node.from_data_hash(\"parameters\" => {\"environment\" => \"one\"}, \"name\" => \"my_node\")\n          my_node.fact_merge Puppet::Node::Facts.new \"my_node\", \"environment\" => \"two\"\n          expect(my_node.environment.name).to eq(:one)\n        end\n\n        it \"uses an environment when an environment parameter has been given and an environment fact has been merged\" do\n          my_node = Puppet::Node.from_data_hash(\"parameters\" => {\"environment\" => \"two\"}, \"name\" => \"my_node\", \"environment\" => \"one\")\n          my_node.fact_merge Puppet::Node::Facts.new \"my_node\", \"environment\" => \"three\"\n          expect(my_node.environment.name).to eq(:one)\n        end\n      end\n\n      context \"when a node is initialized without an environment\" do\n        it \"should use the server's default environment\" do\n          my_node = Puppet::Node.from_data_hash(\"name\" => \"my_node\")\n          expect(my_node.environment.name).to eq(:production)\n        end\n\n        it \"should use the server's default when an environment fact has been merged\" do\n          my_node = Puppet::Node.from_data_hash(\"name\" => \"my_node\")\n          my_node.fact_merge Puppet::Node::Facts.new \"my_node\", \"environment\" => \"two\"\n          expect(my_node.environment.name).to eq(:production)\n        end\n      end\n    end\n\n    context \"when a node is initialized from new\" do\n      context \"when a node is initialzed with an environment\" do\n        it \"adds the environment to the list of parameters\" do\n          Puppet[:environment] = \"one\"\n          @node = Puppet::Node.new(\"testnode\", :environment => \"one\")\n          @node.merge \"two\" => \"three\"\n          expect(@node.parameters[\"environment\"]).to eq(\"one\")\n        end\n\n        it \"when merging, syncs the environment parameter to a node's existing environment\" do\n          @node = Puppet::Node.new(\"testnode\", :environment => \"one\")\n          @node.merge \"environment\" => \"two\"\n          expect(@node.parameters[\"environment\"]).to eq(\"one\")\n        end\n\n        it \"merging facts does not override that environment\" do\n          @node = Puppet::Node.new(\"testnode\", :environment => \"one\")\n          @node.fact_merge Puppet::Node::Facts.new \"testnode\", \"environment\" => \"two\"\n          expect(@node.environment.name.to_s).to eq(\"one\")\n        end\n      end\n\n      context \"when a node is initialized without an environment\" do\n        it \"it perfers an environment name to an environment fact\" do\n          @node = Puppet::Node.new(\"testnode\")\n          @node.environment_name = \"one\"\n          @node.fact_merge Puppet::Node::Facts.new \"testnode\", \"environment\" => \"two\"\n          expect(@node.environment.name.to_s).to eq(\"one\")\n        end\n      end\n    end\n  end\nend\n\ndescribe Puppet::Node, \"when indirecting\" do\n  it \"defaults to the 'plain' node terminus\" do\n    Puppet::Node.indirection.reset_terminus_class\n\n    expect(Puppet::Node.indirection.terminus_class).to eq(:plain)\n  end\nend\n\ndescribe Puppet::Node, \"when generating the list of names to search through\" do\n  before do\n    @node = Puppet::Node.new(\"foo.domain.com\",\n                             :parameters => {\"hostname\" => \"yay\", \"domain\" => \"domain.com\"})\n  end\n\n  it \"returns an array of one name\" do\n    expect(@node.names).to be_instance_of(Array)\n    expect(@node.names).to eq [\"foo.domain.com\"]\n  end\nend\n"
  },
  {
    "path": "spec/unit/other/selinux_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file), \" when manipulating file contexts\" do\n  include PuppetSpec::Files\n\n  before :each do\n\n    @file = Puppet::Type::File.new(\n      :name => make_absolute(\"/tmp/foo\"),\n      :ensure => \"file\",\n      :seluser => \"user_u\",\n      :selrole => \"role_r\",\n      :seltype => \"type_t\")\n  end\n\n  it \"should use :seluser to get/set an SELinux user file context attribute\" do\n    expect(@file[:seluser]).to eq(\"user_u\")\n  end\n\n  it \"should use :selrole to get/set an SELinux role file context attribute\" do\n    expect(@file[:selrole]).to eq(\"role_r\")\n  end\n\n  it \"should use :seltype to get/set an SELinux user file context attribute\" do\n    expect(@file[:seltype]).to eq(\"type_t\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/parameter/boolean_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet'\nrequire 'puppet/parameter/boolean'\n\ndescribe Puppet::Parameter::Boolean do\n  let (:resource) { double('resource') }\n  describe \"after initvars\" do\n    before { described_class.initvars }\n    it \"should have the correct value_collection\" do\n      expect(described_class.value_collection.values.sort).to eq(\n          [:true, :false, :yes, :no].sort\n      )\n    end\n  end\n\n  describe \"instances\" do\n    subject { described_class.new(:resource => resource) }\n\n    [ true, :true, 'true', :yes, 'yes', 'TrUe', 'yEs' ].each do |arg|\n      it \"should munge #{arg.inspect} as true\" do\n        expect(subject.munge(arg)).to eq(true)\n      end\n    end\n    [ false, :false, 'false', :no, 'no', 'FaLSE', 'nO' ].each do |arg|\n      it \"should munge #{arg.inspect} as false\" do\n        expect(subject.munge(arg)).to eq(false)\n      end\n    end\n    [ nil, :undef, 'undef', '0', 0, '1', 1, 9284 ].each do |arg|\n      it \"should fail to munge #{arg.inspect}\" do\n        expect { subject.munge(arg) }.to raise_error Puppet::Error\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parameter/package_options_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parameter/package_options'\n\ndescribe Puppet::Parameter::PackageOptions do\n  let (:resource) { double('resource') }\n  let (:param)    { described_class.new(:resource => resource) }\n  let (:arg)      { '/S' }\n  let (:key)      { 'INSTALLDIR' }\n  let (:value)    { 'C:/mydir' }\n\n  context '#munge' do\n    # The parser automatically converts single element arrays to just\n    # a single element, why it does this is beyond me. See 46252b5bb8\n    it 'should accept a string' do\n      expect(param.munge(arg)).to eq([arg])\n    end\n\n    it 'should accept a hash' do\n      expect(param.munge({key => value})).to eq([{key => value}])\n    end\n\n    it 'should accept an array of strings and hashes' do\n      munged = param.munge([arg, {key => value}, '/NCRC', {'CONF' => 'C:\\datadir'}])\n      expect(munged).to eq([arg, {key => value}, '/NCRC', {'CONF' => 'C:\\datadir'}])\n    end\n\n    it 'should quote strings' do\n      expect(param.munge('arg one')).to eq([\"\\\"arg one\\\"\"])\n    end\n\n    it 'should quote hash pairs' do\n      munged = param.munge({'INSTALL DIR' => 'C:\\Program Files'})\n      expect(munged).to eq([{\"\\\"INSTALL DIR\\\"\" => \"\\\"C:\\\\Program Files\\\"\"}])\n    end\n\n    it 'should reject symbols' do\n      expect {\n        param.munge([:symbol])\n      }.to raise_error(Puppet::Error, /Expected either a string or hash of options/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parameter/path_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parameter/path'\n\n[false, true].each do |arrays|\n  describe \"Puppet::Parameter::Path with arrays #{arrays}\" do\n    it_should_behave_like \"all path parameters\", :path, :array => arrays do\n      # The new type allows us a test that is guaranteed to go direct to our\n      # validation code, without passing through any \"real type\" overrides or\n      # whatever on the way.\n      Puppet::Type.newtype(:test_puppet_parameter_path) do\n        newparam(:path, :parent => Puppet::Parameter::Path, :arrays => arrays) do\n          isnamevar\n          accept_arrays arrays\n        end\n      end\n\n      def instance(path)\n        Puppet::Type.type(:test_puppet_parameter_path).new(:path => path)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parameter/value_collection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parameter'\n\ndescribe Puppet::Parameter::ValueCollection do\n  before do\n    @collection = Puppet::Parameter::ValueCollection.new\n  end\n\n  it \"should have a method for defining new values\" do\n    expect(@collection).to respond_to(:newvalues)\n  end\n\n  it \"should have a method for adding individual values\" do\n    expect(@collection).to respond_to(:newvalue)\n  end\n\n  it \"should be able to retrieve individual values\" do\n    value = @collection.newvalue(:foo)\n    expect(@collection.value(:foo)).to equal(value)\n  end\n\n  it \"should be able to add an individual value with a block\" do\n    @collection.newvalue(:foo) { raise \"testing\" }\n    expect(@collection.value(:foo).block).to be_instance_of(Proc)\n  end\n\n  it \"should be able to add values that are empty strings\" do\n    expect { @collection.newvalue('') }.to_not raise_error\n  end\n\n  it \"should be able to add values that are empty strings\" do\n    value = @collection.newvalue('')\n    expect(@collection.match?('')).to equal(value)\n  end\n\n  describe \"when adding a value with a block\" do\n    it \"should set the method name to 'set_' plus the value name\" do\n      value = @collection.newvalue(:myval) { raise \"testing\" }\n      expect(value.method).to eq(\"set_myval\")\n    end\n  end\n\n  it \"should be able to add an individual value with options\" do\n    value = @collection.newvalue(:foo, :method => 'set_myval')\n    expect(value.method).to eq('set_myval')\n  end\n\n  it \"should have a method for validating a value\" do\n    expect(@collection).to respond_to(:validate)\n  end\n\n  it \"should have a method for munging a value\" do\n    expect(@collection).to respond_to(:munge)\n  end\n\n  it \"should be able to generate documentation when it has both values and regexes\" do\n    @collection.newvalues :foo, \"bar\", %r{test}\n    expect(@collection.doc).to be_instance_of(String)\n  end\n\n  it \"should correctly generate documentation for values\" do\n    @collection.newvalues :foo\n    expect(@collection.doc).to be_include(\"Valid values are `foo`\")\n  end\n\n  it \"should correctly generate documentation for regexes\" do\n    @collection.newvalues %r{\\w+}\n    expect(@collection.doc).to be_include(\"Values can match `/\\\\w+/`\")\n  end\n\n  it \"should be able to find the first matching value\" do\n    @collection.newvalues :foo, :bar\n    expect(@collection.match?(\"foo\")).to be_instance_of(Puppet::Parameter::Value)\n  end\n\n  it \"should be able to match symbols\" do\n    @collection.newvalues :foo, :bar\n    expect(@collection.match?(:foo)).to be_instance_of(Puppet::Parameter::Value)\n  end\n\n  it \"should be able to match symbols when a regex is provided\" do\n    @collection.newvalues %r{.}\n    expect(@collection.match?(:foo)).to be_instance_of(Puppet::Parameter::Value)\n  end\n\n  it \"should be able to match values using regexes\" do\n    @collection.newvalues %r{.}\n    expect(@collection.match?(\"foo\")).not_to be_nil\n  end\n\n  it \"should prefer value matches to regex matches\" do\n    @collection.newvalues %r{.}, :foo\n    expect(@collection.match?(\"foo\").name).to eq(:foo)\n  end\n\n  describe \"when validating values\" do\n    it \"should do nothing if no values or regexes have been defined\" do\n      @collection.validate(\"foo\")\n    end\n\n    it \"should fail if the value is not a defined value or alias and does not match a regex\" do\n      @collection.newvalues :foo\n      expect { @collection.validate(\"bar\") }.to raise_error(ArgumentError)\n    end\n\n    it \"should succeed if the value is one of the defined values\" do\n      @collection.newvalues :foo\n      expect { @collection.validate(:foo) }.to_not raise_error\n    end\n\n    it \"should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string\" do\n      @collection.newvalues :foo\n      expect { @collection.validate(\"foo\") }.to_not raise_error\n    end\n\n    it \"should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol\" do\n      @collection.newvalues \"foo\"\n      expect { @collection.validate(:foo) }.to_not raise_error\n    end\n\n    it \"should succeed if the value is one of the defined aliases\" do\n      @collection.newvalues :foo\n      @collection.aliasvalue :bar, :foo\n      expect { @collection.validate(\"bar\") }.to_not raise_error\n    end\n\n    it \"should succeed if the value matches one of the regexes\" do\n      @collection.newvalues %r{\\d}\n      expect { @collection.validate(\"10\") }.to_not raise_error\n    end\n  end\n\n  describe \"when munging values\" do\n    it \"should do nothing if no values or regexes have been defined\" do\n      expect(@collection.munge(\"foo\")).to eq(\"foo\")\n    end\n\n    it \"should return return any matching defined values\" do\n      @collection.newvalues :foo, :bar\n      expect(@collection.munge(\"foo\")).to eq(:foo)\n    end\n\n    it \"should return any matching aliases\" do\n      @collection.newvalues :foo\n      @collection.aliasvalue :bar, :foo\n      expect(@collection.munge(\"bar\")).to eq(:foo)\n    end\n\n    it \"should return the value if it matches a regex\" do\n      @collection.newvalues %r{\\w}\n      expect(@collection.munge(\"bar\")).to eq(\"bar\")\n    end\n\n    it \"should return the value if no other option is matched\" do\n      @collection.newvalues :foo\n      expect(@collection.munge(\"bar\")).to eq(\"bar\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parameter/value_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parameter'\n\ndescribe Puppet::Parameter::Value do\n  it \"should require a name\" do\n    expect { Puppet::Parameter::Value.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should set its name\" do\n    expect(Puppet::Parameter::Value.new(:foo).name).to eq(:foo)\n  end\n\n  it \"should support regexes as names\" do\n    expect { Puppet::Parameter::Value.new(%r{foo}) }.not_to raise_error\n  end\n\n  it \"should mark itself as a regex if its name is a regex\" do\n    expect(Puppet::Parameter::Value.new(%r{foo})).to be_regex\n  end\n\n  it \"should always convert its name to a symbol if it is not a regex\" do\n    expect(Puppet::Parameter::Value.new(\"foo\").name).to eq(:foo)\n    expect(Puppet::Parameter::Value.new(true).name).to eq(:true)\n  end\n\n  it \"should support adding aliases\" do\n    expect(Puppet::Parameter::Value.new(\"foo\")).to respond_to(:alias)\n  end\n\n  it \"should be able to return its aliases\" do\n    value = Puppet::Parameter::Value.new(\"foo\")\n    value.alias(\"bar\")\n    value.alias(\"baz\")\n    expect(value.aliases).to eq([:bar, :baz])\n  end\n\n  [:block, :method, :event, :required_features].each do |attr|\n    it \"should support a #{attr} attribute\" do\n      value = Puppet::Parameter::Value.new(\"foo\")\n      expect(value).to respond_to(attr.to_s + \"=\")\n      expect(value).to respond_to(attr)\n    end\n  end\n\n  it \"should always return events as symbols\" do\n    value = Puppet::Parameter::Value.new(\"foo\")\n    value.event = \"foo_test\"\n    expect(value.event).to eq(:foo_test)\n  end\n\n  describe \"when matching\" do\n    describe \"a regex\" do\n      it \"should return true if the regex matches the value\" do\n        expect(Puppet::Parameter::Value.new(/\\w/)).to be_match(\"foo\")\n      end\n\n      it \"should return false if the regex does not match the value\" do\n        expect(Puppet::Parameter::Value.new(/\\d/)).not_to be_match(\"foo\")\n      end\n    end\n\n    describe \"a non-regex\" do\n      it \"should return true if the value, converted to a symbol, matches the name\" do\n        expect(Puppet::Parameter::Value.new(\"foo\")).to be_match(\"foo\")\n        expect(Puppet::Parameter::Value.new(:foo)).to be_match(:foo)\n        expect(Puppet::Parameter::Value.new(:foo)).to be_match(\"foo\")\n        expect(Puppet::Parameter::Value.new(\"foo\")).to be_match(:foo)\n      end\n\n      it \"should return false if the value, converted to a symbol, does not match the name\" do\n        expect(Puppet::Parameter::Value.new(:foo)).not_to be_match(:bar)\n      end\n\n      it \"should return true if any of its aliases match\" do\n        value = Puppet::Parameter::Value.new(\"foo\")\n        value.alias(\"bar\")\n        expect(value).to be_match(\"bar\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parameter_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parameter'\n\ndescribe Puppet::Parameter do\n  before do\n    @class = Class.new(Puppet::Parameter) do\n      @name = :foo\n    end\n    @class.initvars\n    @resource = double('resource')\n    allow(@resource).to receive(:expects)\n    allow(@resource).to receive(:pathbuilder)\n    @parameter = @class.new :resource => @resource\n  end\n\n  it \"should create a value collection\" do\n    @class = Class.new(Puppet::Parameter)\n    expect(@class.value_collection).to be_nil\n    @class.initvars\n    expect(@class.value_collection).to be_instance_of(Puppet::Parameter::ValueCollection)\n  end\n\n  it \"should return its name as a string when converted to a string\" do\n    expect(@parameter.to_s).to eq(@parameter.name.to_s)\n  end\n\n  [:line, :file, :version].each do |data|\n    it \"should return its resource's #{data} as its #{data}\" do\n      expect(@resource).to receive(data).and_return(\"foo\")\n      expect(@parameter.send(data)).to eq(\"foo\")\n    end\n  end\n\n  it \"should return the resource's tags plus its name as its tags\" do\n    expect(@resource).to receive(:tags).and_return(%w{one two})\n    expect(@parameter.tags).to eq(%w{one two foo})\n  end\n\n  it \"should have a path\" do\n    expect(@parameter.path).to eq(\"//foo\")\n  end\n\n  describe \"when returning the value\" do\n    it \"should return nil if no value is set\" do\n      expect(@parameter.value).to be_nil\n    end\n\n    it \"should validate the value\" do\n      expect(@parameter).to receive(:validate).with(\"foo\")\n      @parameter.value = \"foo\"\n    end\n\n    it \"should munge the value and use any result as the actual value\" do\n      expect(@parameter).to receive(:munge).with(\"foo\").and_return(\"bar\")\n      @parameter.value = \"foo\"\n      expect(@parameter.value).to eq(\"bar\")\n    end\n\n    it \"should unmunge the value when accessing the actual value\" do\n      @parameter.class.unmunge do |value| value.to_sym end\n      @parameter.value = \"foo\"\n      expect(@parameter.value).to eq(:foo)\n    end\n\n    it \"should return the actual value by default when unmunging\" do\n      expect(@parameter.unmunge(\"bar\")).to eq(\"bar\")\n    end\n\n    it \"should return any set value\" do\n      @parameter.value = \"foo\"\n      expect(@parameter.value).to eq(\"foo\")\n    end\n  end\n\n  describe \"when validating values\" do\n    it \"should do nothing if no values or regexes have been defined\" do\n      @parameter.validate(\"foo\")\n    end\n\n    it \"should catch abnormal failures thrown during validation\" do\n      @class.validate { |v| raise \"This is broken\" }\n      expect { @parameter.validate(\"eh\") }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should fail if the value is not a defined value or alias and does not match a regex\" do\n      @class.newvalues :foo\n      expect { @parameter.validate(\"bar\") }.to raise_error(Puppet::Error)\n    end\n\n    it \"should succeed if the value is one of the defined values\" do\n      @class.newvalues :foo\n      expect { @parameter.validate(:foo) }.to_not raise_error\n    end\n\n    it \"should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string\" do\n      @class.newvalues :foo\n      expect { @parameter.validate(\"foo\") }.to_not raise_error\n    end\n\n    it \"should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol\" do\n      @class.newvalues \"foo\"\n      expect { @parameter.validate(:foo) }.to_not raise_error\n    end\n\n    it \"should succeed if the value is one of the defined aliases\" do\n      @class.newvalues :foo\n      @class.aliasvalue :bar, :foo\n      expect { @parameter.validate(\"bar\") }.to_not raise_error\n    end\n\n    it \"should succeed if the value matches one of the regexes\" do\n      @class.newvalues %r{\\d}\n      expect { @parameter.validate(\"10\") }.to_not raise_error\n    end\n  end\n\n  describe \"when munging values\" do\n    it \"should do nothing if no values or regexes have been defined\" do\n      expect(@parameter.munge(\"foo\")).to eq(\"foo\")\n    end\n\n    it \"should catch abnormal failures thrown during munging\" do\n      @class.munge { |v| raise \"This is broken\" }\n      expect { @parameter.munge(\"eh\") }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should return return any matching defined values\" do\n      @class.newvalues :foo, :bar\n      expect(@parameter.munge(\"foo\")).to eq(:foo)\n    end\n\n    it \"should return any matching aliases\" do\n      @class.newvalues :foo\n      @class.aliasvalue :bar, :foo\n      expect(@parameter.munge(\"bar\")).to eq(:foo)\n    end\n\n    it \"should return the value if it matches a regex\" do\n      @class.newvalues %r{\\w}\n      expect(@parameter.munge(\"bar\")).to eq(\"bar\")\n    end\n\n    it \"should return the value if no other option is matched\" do\n      @class.newvalues :foo\n      expect(@parameter.munge(\"bar\")).to eq(\"bar\")\n    end\n  end\n\n  describe \"when logging\" do\n    it \"should use its resource's log level and the provided message\" do\n      expect(@resource).to receive(:[]).with(:loglevel).and_return(:notice)\n      expect(@parameter).to receive(:send_log).with(:notice, \"mymessage\")\n      @parameter.log \"mymessage\"\n    end\n  end\n\n  describe \".format_value_for_display\" do\n    it 'should format strings appropriately' do\n      expect(described_class.format_value_for_display('foo')).to eq(\"'foo'\")\n    end\n\n    it 'should format numbers appropriately' do\n      expect(described_class.format_value_for_display(1)).to eq('1')\n    end\n\n    it 'should format symbols appropriately' do\n      expect(described_class.format_value_for_display(:bar)).to eq(\"'bar'\")\n    end\n\n    it 'should format arrays appropriately' do\n      expect(described_class.format_value_for_display([1, 'foo', :bar])).to eq(\"[1, 'foo', 'bar']\")\n    end\n\n    it 'should format hashes appropriately' do\n      expect(described_class.format_value_for_display(\n        {1 => 'foo', :bar => 2, 'baz' => :qux}\n      )).to eq(<<-RUBY.unindent.sub(/\\n$/, ''))\n        {\n          1 => 'foo',\n          'bar' => 2,\n          'baz' => 'qux'\n        }\n      RUBY\n    end\n\n    it 'should format arrays with nested data appropriately' do\n      expect(described_class.format_value_for_display(\n        [1, 'foo', :bar, [1, 2, 3], {1 => 2, 3 => 4}]\n      )).to eq(<<-RUBY.unindent.sub(/\\n$/, ''))\n        [1, 'foo', 'bar',\n          [1, 2, 3],\n          {\n            1 => 2,\n            3 => 4\n          }]\n      RUBY\n    end\n\n    it 'should format hashes with nested data appropriately' do\n      expect(described_class.format_value_for_display(\n        {1 => 'foo', :bar => [2, 3, 4], 'baz' => {:qux => 1, :quux => 'two'}}\n      )).to eq(<<-RUBY.unindent.sub(/\\n$/, ''))\n        {\n          1 => 'foo',\n          'bar' => [2, 3, 4],\n          'baz' => {\n            'qux' => 1,\n            'quux' => 'two'\n          }\n        }\n       RUBY\n    end\n\n    it 'should format hashes with nested Objects appropriately' do\n      tf = Puppet::Pops::Types::TypeFactory\n      type = tf.object({'name' => 'MyType', 'attributes' => { 'qux' => tf.integer, 'quux' => tf.string }})\n      expect(described_class.format_value_for_display(\n        {1 => 'foo', 'bar' => type.create(1, 'one'), 'baz' => type.create(2, 'two')}\n      )).to eq(<<-RUBY.unindent.sub(/\\n$/, ''))\n        {\n          1 => 'foo',\n          'bar' => MyType({\n            'qux' => 1,\n            'quux' => 'one'\n          }),\n          'baz' => MyType({\n            'qux' => 2,\n            'quux' => 'two'\n          })\n        }\n      RUBY\n    end\n\n    it 'should format Objects with nested Objects appropriately' do\n      tf = Puppet::Pops::Types::TypeFactory\n      inner_type = tf.object({'name' => 'MyInnerType', 'attributes' => { 'qux' => tf.integer, 'quux' => tf.string }})\n      outer_type = tf.object({'name' => 'MyOuterType', 'attributes' => { 'x' => tf.string, 'inner' => inner_type }})\n      expect(described_class.format_value_for_display(\n        {'bar' => outer_type.create('a', inner_type.create(1, 'one')), 'baz' => outer_type.create('b', inner_type.create(2, 'two'))}\n      )).to eq(<<-RUBY.unindent.sub(/\\n$/, ''))\n        {\n          'bar' => MyOuterType({\n            'x' => 'a',\n            'inner' => MyInnerType({\n              'qux' => 1,\n              'quux' => 'one'\n            })\n          }),\n          'baz' => MyOuterType({\n            'x' => 'b',\n            'inner' => MyInnerType({\n              'qux' => 2,\n              'quux' => 'two'\n            })\n          })\n        }\n      RUBY\n    end\n  end\n\n  describe 'formatting messages' do\n    it \"formats messages as-is when the parameter is not sensitive\" do\n      expect(@parameter.format(\"hello %s\", \"world\")).to eq(\"hello world\")\n    end\n\n    it \"formats messages with redacted values when the parameter is not sensitive\" do\n      @parameter.sensitive = true\n      expect(@parameter.format(\"hello %s\", \"world\")).to eq(\"hello [redacted]\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/ast/block_expression_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/parser/ast/block_expression'\n\ndescribe 'Puppet::Parser::AST::BlockExpression' do\n  class StackDepthAST < Puppet::Parser::AST\n    attr_reader :call_depth\n    def evaluate(*options)\n      @call_depth = caller.length\n    end\n  end\n\n  NO_SCOPE = nil\n\n  def depth_probe\n    StackDepthAST.new\n  end\n\n  def sequence_probe(name)\n    probe = double(\"Sequence Probe #{name}\")\n    expect(probe).to receive(:safeevaluate).ordered\n    probe\n  end\n\n  def block_of(children)\n    Puppet::Parser::AST::BlockExpression.new(:children => children)\n  end\n\n  def assert_all_at_same_depth(*probes)\n    depth0 = probes[0].call_depth\n    probes.drop(1).each do |p|\n      expect(p.call_depth).to eq(depth0)\n    end\n  end\n\n  it \"evaluates all its children at the same stack depth\" do\n    depth_probes = [depth_probe, depth_probe]\n    expr = block_of(depth_probes)\n\n    expr.evaluate(NO_SCOPE)\n\n    assert_all_at_same_depth(*depth_probes)\n  end\n\n  it \"evaluates sequenced children at the same stack depth\" do\n    depth1 = depth_probe\n    depth2 = depth_probe\n    depth3 = depth_probe\n\n    expr1 = block_of([depth1])\n    expr2 = block_of([depth2])\n    expr3 = block_of([depth3])\n\n    expr1.sequence_with(expr2).sequence_with(expr3).evaluate(NO_SCOPE)\n\n    assert_all_at_same_depth(depth1, depth2, depth3)\n  end\n\n  it \"evaluates sequenced children in order\" do\n    expr1 = block_of([sequence_probe(\"Step 1\")])\n    expr2 = block_of([sequence_probe(\"Step 2\")])\n    expr3 = block_of([sequence_probe(\"Step 3\")])\n\n    expr1.sequence_with(expr2).sequence_with(expr3).evaluate(NO_SCOPE)\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/parser/ast/leaf_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Parser::AST::Leaf do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n    @value = double('value')\n    @leaf = Puppet::Parser::AST::Leaf.new(:value => @value)\n  end\n\n  describe \"when converting to string\" do\n    it \"should transform its value to string\" do\n      value = double('value', :is_a? => true)\n      expect(value).to receive(:to_s)\n      Puppet::Parser::AST::Leaf.new( :value => value ).to_s\n    end\n  end\n\n  it \"should have a match method\" do\n    expect(@leaf).to respond_to(:match)\n  end\n\n  it \"should delegate match to ==\" do\n    expect(@value).to receive(:==).with(\"value\")\n\n    @leaf.match(\"value\")\n  end\nend\n\n\ndescribe Puppet::Parser::AST::Regex do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  describe \"when initializing\" do\n    it \"should create a Regexp with its content when value is not a Regexp\" do\n      expect(Regexp).to receive(:new).with(\"/ab/\")\n\n      Puppet::Parser::AST::Regex.new :value => \"/ab/\"\n    end\n\n    it \"should not create a Regexp with its content when value is a Regexp\" do\n      value = Regexp.new(\"/ab/\")\n      expect(Regexp).not_to receive(:new).with(\"/ab/\")\n\n      Puppet::Parser::AST::Regex.new :value => value\n    end\n  end\n\n  describe \"when evaluating\" do\n    it \"should return self\" do\n      val = Puppet::Parser::AST::Regex.new :value => \"/ab/\"\n\n      expect(val.evaluate(@scope)).to be === val\n    end\n  end\n\n  it 'should return the PRegexpType#regexp_to_s_with_delimiters with to_s' do\n    regex = double('regex')\n    allow(Regexp).to receive(:new).and_return(regex)\n\n    val = Puppet::Parser::AST::Regex.new :value => '/ab/'\n    expect(Puppet::Pops::Types::PRegexpType).to receive(:regexp_to_s_with_delimiters)\n\n    val.to_s\n  end\n\n  it \"should delegate match to the underlying regexp match method\" do\n    regex = Regexp.new(\"/ab/\")\n    val = Puppet::Parser::AST::Regex.new :value => regex\n\n    expect(regex).to receive(:match).with(\"value\")\n\n    val.match(\"value\")\n  end\nend\n\ndescribe Puppet::Parser::AST::HostName do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n    @value   = 'value'\n    allow(@value).to receive(:to_s).and_return(@value)\n    allow(@value).to receive(:downcase).and_return(@value)\n    @host = Puppet::Parser::AST::HostName.new(:value => @value)\n  end\n\n  it \"should raise an error if hostname is not valid\" do\n    expect { Puppet::Parser::AST::HostName.new( :value => \"not a hostname!\" ) }.to raise_error(Puppet::DevError, /'not a hostname!' is not a valid hostname/)\n  end\n\n  it \"should not raise an error if hostname is a regex\" do\n    expect { Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => \"/test/\") ) }.not_to raise_error\n  end\n\n  it \"should stringify the value\" do\n    value = double('value', :=~ => false)\n\n    expect(value).to receive(:to_s).and_return(\"test\")\n\n    Puppet::Parser::AST::HostName.new(:value => value)\n  end\n\n  it \"should downcase the value\" do\n    value = double('value', :=~ => false)\n    allow(value).to receive(:to_s).and_return(\"UPCASED\")\n    host = Puppet::Parser::AST::HostName.new(:value => value)\n\n    host.value == \"upcased\"\n  end\n\n  it \"should evaluate to its value\" do\n    expect(@host.evaluate(@scope)).to eq(@value)\n  end\n\n  it \"should delegate eql? to the underlying value if it is an HostName\" do\n    expect(@value).to receive(:eql?).with(\"value\")\n    @host.eql?(\"value\")\n  end\n\n  it \"should delegate eql? to the underlying value if it is not an HostName\" do\n    value = double('compared', :is_a? => true, :value => \"value\")\n    expect(@value).to receive(:eql?).with(\"value\")\n    @host.eql?(value)\n  end\n\n  it \"should delegate hash to the underlying value\" do\n    expect(@value).to receive(:hash)\n    @host.hash\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/compiler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/resource'\n\nclass CompilerTestResource\n  attr_accessor :builtin, :virtual, :evaluated, :type, :title\n\n  def initialize(type, title)\n    @type = type\n    @title = title\n  end\n\n  def [](attr)\n    return nil if (attr == :stage || attr == :alias)\n    :main\n  end\n\n  def ref\n    \"#{type.to_s.capitalize}[#{title}]\"\n  end\n\n  def evaluated?\n    @evaluated\n  end\n\n  def builtin_type?\n    @builtin\n  end\n\n  def virtual?\n    @virtual\n  end\n\n  def class?\n    false\n  end\n\n  def stage?\n    false\n  end\n\n  def evaluate\n  end\n\n  def file\n    \"/fake/file/goes/here\"\n  end\n\n  def line\n    \"42\"\n  end\n\n  def resource_type\n    self.class\n  end\nend\n\ndescribe Puppet::Parser::Compiler do\n  include PuppetSpec::Files\n  include Matchers::Resource\n\n  def resource(type, title)\n    Puppet::Parser::Resource.new(type, title, :scope => @scope)\n  end\n\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n\n  before :each do\n    # Push me faster, I wanna go back in time!  (Specifically, freeze time\n    # across the test since we have a bunch of version == timestamp code\n    # hidden away in the implementation and we keep losing the race.)\n    # --daniel 2011-04-21\n    now = Time.now\n    allow(Time).to receive(:now).and_return(now)\n\n    @node = Puppet::Node.new(\"testnode\",\n                             :facts => Puppet::Node::Facts.new(\"facts\", {}),\n                             :environment => environment)\n    @known_resource_types = environment.known_resource_types\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @scope = Puppet::Parser::Scope.new(@compiler, :source => double('source'))\n    @scope_resource = Puppet::Parser::Resource.new(:file, \"/my/file\", :scope => @scope)\n    @scope.resource = @scope_resource\n  end\n\n  it \"should fail intelligently when a class-level compile fails\" do\n    expect(Puppet::Parser::Compiler).to receive(:new).and_raise(ArgumentError)\n    expect { Puppet::Parser::Compiler.compile(@node) }.to raise_error(Puppet::Error)\n  end\n\n  it \"should use the node's environment as its environment\" do\n    expect(@compiler.environment).to equal(@node.environment)\n  end\n\n  it \"fails if the node's environment has validation errors\" do\n    conflicted_environment = Puppet::Node::Environment.create(:testing, [], '/some/environment.conf/manifest.pp')\n    allow(conflicted_environment).to receive(:validation_errors).and_return(['bad environment'])\n    @node.environment = conflicted_environment\n    expect { Puppet::Parser::Compiler.compile(@node) }.to raise_error(Puppet::Error, /Compilation has been halted because.*bad environment/)\n  end\n\n  it \"should be able to return a class list containing all added classes\" do\n    @compiler.add_class \"\"\n    @compiler.add_class \"one\"\n    @compiler.add_class \"two\"\n\n    expect(@compiler.classlist.sort).to eq(%w{one two}.sort)\n  end\n\n  describe \"when initializing\" do\n    it 'should not create the settings class more than once' do\n      logs = []\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        Puppet[:code] = 'undef'\n        @compiler.compile\n\n        @compiler = Puppet::Parser::Compiler.new(@node)\n        Puppet[:code] = 'undef'\n        @compiler.compile\n      end\n      warnings = logs.select { |log| log.level == :warning }.map { |log| log.message }\n      expect(warnings).not_to include(/Class 'settings' is already defined/)\n    end\n\n    it \"should set its node attribute\" do\n      expect(@compiler.node).to equal(@node)\n    end\n\n    it \"the set of ast_nodes should be empty\" do\n      expect(@compiler.environment.known_resource_types.nodes?).to be_falsey\n    end\n\n    it \"should copy the known_resource_types version to the catalog\" do\n      expect(@compiler.catalog.version).to eq(@known_resource_types.version)\n    end\n\n    it \"should copy any node classes into the class list\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.classes = %w{foo bar}\n      compiler = Puppet::Parser::Compiler.new(node)\n\n      expect(compiler.classlist).to match_array(['foo', 'bar'])\n    end\n\n    it \"should transform node class hashes into a class list\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.classes = {'foo'=>{'one'=>'p1'}, 'bar'=>{'two'=>'p2'}}\n      compiler = Puppet::Parser::Compiler.new(node)\n\n      expect(compiler.classlist).to match_array(['foo', 'bar'])\n    end\n\n    it \"should return a catalog with the specified code_id\" do\n      node = Puppet::Node.new(\"mynode\")\n      code_id = 'b59e5df0578ef411f773ee6c33d8073c50e7b8fe'\n      compiler = Puppet::Parser::Compiler.new(node, :code_id => code_id)\n\n      expect(compiler.catalog.code_id).to eq(code_id)\n    end\n\n    it \"should add a 'main' stage to the catalog\" do\n      expect(@compiler.catalog.resource(:stage, :main)).to be_instance_of(Puppet::Parser::Resource)\n    end\n  end\n\n  describe \"sanitize_node\" do\n    it \"should delete trusted from parameters\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.parameters['trusted'] =  { :a => 42 }\n      node.parameters['preserve_me'] = 'other stuff'\n      compiler = Puppet::Parser::Compiler.new(node)\n      sanitized = compiler.node\n      expect(sanitized.parameters['trusted']).to eq(nil)\n      expect(sanitized.parameters['preserve_me']).to eq('other stuff')\n    end\n\n    it \"should not report trusted_data if trusted is false\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.parameters['trusted'] = false\n      compiler = Puppet::Parser::Compiler.new(node)\n      sanitized = compiler.node\n      expect(sanitized.trusted_data).to_not eq(false)\n    end\n\n    it \"should not report trusted_data if trusted is not a hash\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.parameters['trusted'] = 'not a hash'\n      compiler = Puppet::Parser::Compiler.new(node)\n      sanitized = compiler.node\n      expect(sanitized.trusted_data).to_not eq('not a hash')\n    end\n\n    it \"should not report trusted_data if trusted hash doesn't include known keys\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.parameters['trusted'] = { :a => 42 }\n      compiler = Puppet::Parser::Compiler.new(node)\n      sanitized = compiler.node\n      expect(sanitized.trusted_data).to_not eq({ :a => 42 })\n    end\n\n    it \"should prefer trusted_data in the node above other plausible sources\" do\n      node = Puppet::Node.new(\"mynode\")\n      node.trusted_data = { 'authenticated' => true,\n                           'certname'      => 'the real deal',\n                           'extensions'    => 'things' }\n\n      node.parameters['trusted'] = { 'authenticated' => true,\n                                     'certname'      => 'not me',\n                                     'extensions'    => 'things' }\n\n      compiler = Puppet::Parser::Compiler.new(node)\n      sanitized = compiler.node\n      expect(sanitized.trusted_data).to eq({ 'authenticated' => true,\n                                             'certname'      => 'the real deal',\n                                             'extensions'    => 'things' })\n    end\n  end\n\n  describe \"when managing scopes\" do\n    it \"should create a top scope\" do\n      expect(@compiler.topscope).to be_instance_of(Puppet::Parser::Scope)\n    end\n\n    it \"should be able to create new scopes\" do\n      expect(@compiler.newscope(@compiler.topscope)).to be_instance_of(Puppet::Parser::Scope)\n    end\n\n    it \"should set the parent scope of the new scope to be the passed-in parent\" do\n      scope = double('scope')\n      newscope = @compiler.newscope(scope)\n\n      expect(newscope.parent).to equal(scope)\n    end\n\n    it \"should set the parent scope of the new scope to its topscope if the parent passed in is nil\" do\n      newscope = @compiler.newscope(nil)\n\n      expect(newscope.parent).to equal(@compiler.topscope)\n    end\n  end\n\n  describe \"when compiling\" do\n    it \"should set node parameters as variables in the top scope\" do\n      params = {\"a\" => \"b\", \"c\" => \"d\"}\n      allow(@node).to receive(:parameters).and_return(params)\n      @compiler.compile\n      expect(@compiler.topscope['a']).to eq(\"b\")\n      expect(@compiler.topscope['c']).to eq(\"d\")\n    end\n\n    it \"should set node parameters that are of Symbol type as String variables in the top scope\" do\n      params = {\"a\" => :b}\n      allow(@node).to receive(:parameters).and_return(params)\n      @compiler.compile\n      expect(@compiler.topscope['a']).to eq(\"b\")\n    end\n\n    it \"should set the node's environment as a string variable in top scope\" do\n      @node.merge({'wat' => 'this is how the sausage is made'})\n      @compiler.compile\n      expect(@compiler.topscope['environment']).to eq(\"testing\")\n      expect(@compiler.topscope['wat']).to eq('this is how the sausage is made')\n    end\n\n    it \"sets the environment based on node.environment instead of the parameters\" do\n      @node.parameters['environment'] = \"Not actually #{@node.environment.name}\"\n\n      @compiler.compile\n      expect(@compiler.topscope['environment']).to eq('testing')\n    end\n\n    it \"should set the client and server versions on the catalog\" do\n      params = {\"clientversion\" => \"2\", \"serverversion\" => \"3\"}\n      allow(@node).to receive(:parameters).and_return(params)\n      @compiler.compile\n      expect(@compiler.catalog.client_version).to eq(\"2\")\n      expect(@compiler.catalog.server_version).to eq(\"3\")\n    end\n\n    it \"should evaluate the main class if it exists\" do\n      main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, \"\")\n      @compiler.topscope.source = main_class\n\n      expect(main_class).to receive(:evaluate_code).with(be_a(Puppet::Parser::Resource))\n\n      @compiler.compile\n    end\n\n    it \"should create a new, empty 'main' if no main class exists\" do\n      @compiler.compile\n      expect(@known_resource_types.find_hostclass(\"\")).to be_instance_of(Puppet::Resource::Type)\n    end\n\n    it \"should add an edge between the main stage and main class\" do\n      @compiler.compile\n      expect(stage = @compiler.catalog.resource(:stage, \"main\")).to be_instance_of(Puppet::Parser::Resource)\n      expect(klass = @compiler.catalog.resource(:class, \"\")).to be_instance_of(Puppet::Parser::Resource)\n\n      expect(@compiler.catalog.edge?(stage, klass)).to be_truthy\n    end\n\n    it \"should evaluate all added collections\" do\n      colls = []\n      # And when the collections fail to evaluate.\n      colls << double(\"coll1-false\")\n      colls << double(\"coll2-false\")\n      colls.each { |c| expect(c).to receive(:evaluate).and_return(false) }\n\n      @compiler.add_collection(colls[0])\n      @compiler.add_collection(colls[1])\n\n      allow(@compiler).to receive(:fail_on_unevaluated)\n      @compiler.compile\n    end\n\n    it \"should ignore builtin resources\" do\n      resource = resource(:file, \"testing\")\n\n      @compiler.add_resource(@scope, resource)\n      expect(resource).not_to receive(:evaluate)\n\n      @compiler.compile\n    end\n\n    it \"should evaluate unevaluated resources\" do\n      resource = CompilerTestResource.new(:file, \"testing\")\n\n      @compiler.add_resource(@scope, resource)\n\n      # We have to now mark the resource as evaluated\n      expect(resource).to receive(:evaluate) { resource.evaluated = true }\n\n      @compiler.compile\n    end\n\n    it \"should not evaluate already-evaluated resources\" do\n      resource = resource(:file, \"testing\")\n      allow(resource).to receive(:evaluated?).and_return(true)\n\n      @compiler.add_resource(@scope, resource)\n      expect(resource).not_to receive(:evaluate)\n\n      @compiler.compile\n    end\n\n    it \"should evaluate unevaluated resources created by evaluating other resources\" do\n      resource = CompilerTestResource.new(:file, \"testing\")\n      @compiler.add_resource(@scope, resource)\n\n      resource2 = CompilerTestResource.new(:file, \"other\")\n\n      # We have to now mark the resource as evaluated\n      expect(resource).to receive(:evaluate) { resource.evaluated = true; @compiler.add_resource(@scope, resource2) }\n      expect(resource2).to receive(:evaluate) { resource2.evaluated = true }\n\n      @compiler.compile\n    end\n\n    describe \"when finishing\" do\n      before do\n        @compiler.send(:evaluate_main)\n        @catalog = @compiler.catalog\n      end\n\n      def add_resource(name, parent = nil)\n        resource = Puppet::Parser::Resource.new \"file\", name, :scope => @scope\n        @compiler.add_resource(@scope, resource)\n        @catalog.add_edge(parent, resource) if parent\n        resource\n      end\n\n      it \"should call finish() on all resources\" do\n        # Add a resource that does respond to :finish\n        resource = Puppet::Parser::Resource.new \"file\", \"finish\", :scope => @scope\n        expect(resource).to receive(:finish)\n\n        @compiler.add_resource(@scope, resource)\n\n        # And one that does not\n        dnf_resource = double(\"dnf\", :ref => \"File[dnf]\", :type => \"file\", :resource_type => nil, :[] => nil, :class? => nil, :stage? => nil)\n\n        @compiler.add_resource(@scope, dnf_resource)\n\n        @compiler.send(:finish)\n      end\n\n      it \"should call finish() in add_resource order\" do\n        resource1 = add_resource(\"finish1\")\n        expect(resource1).to receive(:finish).ordered\n\n        resource2 = add_resource(\"finish2\")\n        expect(resource2).to receive(:finish).ordered\n\n        @compiler.send(:finish)\n      end\n\n      it \"should add each container's metaparams to its contained resources\" do\n        main = @catalog.resource(:class, :main)\n        main[:noop] = true\n\n        resource1 = add_resource(\"meh\", main)\n\n        @compiler.send(:finish)\n        expect(resource1[:noop]).to be_truthy\n      end\n\n      it \"should add metaparams recursively\" do\n        main = @catalog.resource(:class, :main)\n        main[:noop] = true\n\n        resource1 = add_resource(\"meh\", main)\n        resource2 = add_resource(\"foo\", resource1)\n\n        @compiler.send(:finish)\n        expect(resource2[:noop]).to be_truthy\n      end\n\n      it \"should prefer metaparams from immediate parents\" do\n        main = @catalog.resource(:class, :main)\n        main[:noop] = true\n\n        resource1 = add_resource(\"meh\", main)\n        resource2 = add_resource(\"foo\", resource1)\n\n        resource1[:noop] = false\n\n        @compiler.send(:finish)\n        expect(resource2[:noop]).to be_falsey\n      end\n\n      it \"should merge tags downward\" do\n        main = @catalog.resource(:class, :main)\n        main.tag(\"one\")\n\n        resource1 = add_resource(\"meh\", main)\n        resource1.tag \"two\"\n        resource2 = add_resource(\"foo\", resource1)\n\n        @compiler.send(:finish)\n        expect(resource2.tags).to be_include(\"one\")\n        expect(resource2.tags).to be_include(\"two\")\n      end\n\n      it \"should work if only middle resources have metaparams set\" do\n        main = @catalog.resource(:class, :main)\n\n        resource1 = add_resource(\"meh\", main)\n        resource1[:noop] = true\n        resource2 = add_resource(\"foo\", resource1)\n\n        @compiler.send(:finish)\n        expect(resource2[:noop]).to be_truthy\n      end\n    end\n\n    it \"should return added resources in add order\" do\n      resource1 = resource(:file, \"yay\")\n      @compiler.add_resource(@scope, resource1)\n      resource2 = resource(:file, \"youpi\")\n      @compiler.add_resource(@scope, resource2)\n\n      expect(@compiler.resources).to eq([resource1, resource2])\n    end\n\n    it \"should add resources that do not conflict with existing resources\" do\n      resource = resource(:file, \"yay\")\n      @compiler.add_resource(@scope, resource)\n\n      expect(@compiler.catalog).to be_vertex(resource)\n    end\n\n    it \"should fail to add resources that conflict with existing resources\" do\n      path = make_absolute(\"/foo\")\n      file1 = resource(:file, path)\n      file2 = resource(:file, path)\n\n      @compiler.add_resource(@scope, file1)\n      expect { @compiler.add_resource(@scope, file2) }.to raise_error(Puppet::Resource::Catalog::DuplicateResourceError)\n    end\n\n    it \"should add an edge from the scope resource to the added resource\" do\n      resource = resource(:file, \"yay\")\n      @compiler.add_resource(@scope, resource)\n\n      expect(@compiler.catalog).to be_edge(@scope.resource, resource)\n    end\n\n    it \"should not add non-class resources that don't specify a stage to the 'main' stage\" do\n      main = @compiler.catalog.resource(:stage, :main)\n      resource = resource(:file, \"foo\")\n      @compiler.add_resource(@scope, resource)\n\n      expect(@compiler.catalog).not_to be_edge(main, resource)\n    end\n\n    it \"should not add any parent-edges to stages\" do\n      stage = resource(:stage, \"other\")\n      @compiler.add_resource(@scope, stage)\n\n      @scope.resource = resource(:class, \"foo\")\n\n      expect(@compiler.catalog.edge?(@scope.resource, stage)).to be_falsey\n    end\n\n    it \"should not attempt to add stages to other stages\" do\n      other_stage = resource(:stage, \"other\")\n      second_stage = resource(:stage, \"second\")\n      @compiler.add_resource(@scope, other_stage)\n      @compiler.add_resource(@scope, second_stage)\n\n      second_stage[:stage] = \"other\"\n\n      expect(@compiler.catalog.edge?(other_stage, second_stage)).to be_falsey\n    end\n\n    it \"should have a method for looking up resources\" do\n      resource = resource(:yay, \"foo\")\n      @compiler.add_resource(@scope, resource)\n      expect(@compiler.findresource(\"Yay[foo]\")).to equal(resource)\n    end\n\n    it \"should be able to look resources up by type and title\" do\n      resource = resource(:yay, \"foo\")\n      @compiler.add_resource(@scope, resource)\n      expect(@compiler.findresource(\"Yay\", \"foo\")).to equal(resource)\n    end\n\n    it \"should not evaluate virtual defined resources\" do\n      resource = resource(:file, \"testing\")\n      resource.virtual = true\n      @compiler.add_resource(@scope, resource)\n\n      expect(resource).not_to receive(:evaluate)\n\n      @compiler.compile\n    end\n  end\n\n  describe \"when evaluating collections\" do\n    it \"should evaluate each collection\" do\n      2.times { |i|\n        coll = double('coll%s' % i)\n        @compiler.add_collection(coll)\n\n        # This is the hard part -- we have to emulate the fact that\n        # collections delete themselves if they are done evaluating.\n        expect(coll).to receive(:evaluate) do\n          @compiler.delete_collection(coll)\n        end\n      }\n\n      @compiler.compile\n    end\n\n    it \"should not fail when there are unevaluated resource collections that do not refer to specific resources\" do\n      coll = double('coll', :evaluate => false)\n      expect(coll).to receive(:unresolved_resources).and_return(nil)\n\n      @compiler.add_collection(coll)\n\n      expect { @compiler.compile }.not_to raise_error\n    end\n\n    it \"should fail when there are unevaluated resource collections that refer to a specific resource\" do\n      coll = double('coll', :evaluate => false)\n      expect(coll).to receive(:unresolved_resources).and_return(:something)\n\n      @compiler.add_collection(coll)\n\n      expect { @compiler.compile }.to raise_error(Puppet::ParseError, 'Failed to realize virtual resources something')\n    end\n\n    it \"should fail when there are unevaluated resource collections that refer to multiple specific resources\" do\n      coll = double('coll', :evaluate => false)\n      expect(coll).to receive(:unresolved_resources).and_return([:one, :two])\n\n      @compiler.add_collection(coll)\n\n      expect { @compiler.compile }.to raise_error(Puppet::ParseError, 'Failed to realize virtual resources one, two')\n    end\n  end\n\n  describe \"when evaluating relationships\" do\n    it \"should evaluate each relationship with its catalog\" do\n      dep = double('dep')\n      expect(dep).to receive(:evaluate).with(@compiler.catalog)\n      @compiler.add_relationship dep\n      @compiler.evaluate_relationships\n    end\n  end\n\n  describe \"when told to evaluate missing classes\" do\n    it \"should fail if there's no source listed for the scope\" do\n      scope = double('scope', :source => nil)\n      expect { @compiler.evaluate_classes(%w{one two}, scope) }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should raise an error if a class is not found\" do\n      expect(@scope.environment.known_resource_types).to receive(:find_hostclass).with(\"notfound\").and_return(nil)\n      expect{ @compiler.evaluate_classes(%w{notfound}, @scope) }.to raise_error(Puppet::Error, /Could not find class/)\n    end\n\n    it \"should raise an error when it can't find class\" do\n      klasses = {'foo'=>nil}\n      @node.classes = klasses\n      expect{ @compiler.compile }.to raise_error(Puppet::Error, /Could not find class foo for testnode/)\n    end\n  end\n\n  describe \"when evaluating found classes\" do\n    before do\n      Puppet.settings[:data_binding_terminus] = \"none\"\n      @class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, \"myclass\")\n      @resource = double('resource', :ref => \"Class[myclass]\", :type => \"file\")\n    end\n\n    around do |example|\n      Puppet.override(\n        :environments => Puppet::Environments::Static.new(environment),\n        :description => \"Static loader for specs\"\n      ) do\n        example.run\n      end\n    end\n\n    it \"should evaluate each class\" do\n      allow(@compiler.catalog).to receive(:tag)\n\n      expect(@class).to receive(:ensure_in_catalog).with(@scope)\n      allow(@scope).to receive(:class_scope).with(@class)\n\n      @compiler.evaluate_classes(%w{myclass}, @scope)\n    end\n\n    describe \"and the classes are specified as a hash with parameters\" do\n      before do\n        @node.classes = {}\n        @ast_obj = Puppet::Parser::AST::Leaf.new(:value => 'foo')\n      end\n\n      # Define the given class with default parameters\n      def define_class(name, parameters)\n        @node.classes[name] = parameters\n        klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'p1' => @ast_obj, 'p2' => @ast_obj})\n        @compiler.environment.known_resource_types.add klass\n      end\n\n      def compile\n        @catalog = @compiler.compile\n      end\n\n      it \"should record which classes are evaluated\" do\n        classes = {'foo'=>{}, 'bar::foo'=>{}, 'bar'=>{}}\n        classes.each { |c, params| define_class(c, params) }\n        compile()\n        classes.each { |name, p| expect(@catalog.classes).to include(name) }\n      end\n\n      it \"should provide default values for parameters that have no values specified\" do\n        define_class('foo', {})\n        compile()\n        expect(@catalog.resource(:class, 'foo')['p1']).to eq(\"foo\")\n      end\n\n      it \"should use any provided values\" do\n        define_class('foo', {'p1' => 'real_value'})\n        compile()\n        expect(@catalog.resource(:class, 'foo')['p1']).to eq(\"real_value\")\n      end\n\n      it \"should support providing some but not all values\" do\n        define_class('foo', {'p1' => 'real_value'})\n        compile()\n        expect(@catalog.resource(:class, 'Foo')['p1']).to eq(\"real_value\")\n        expect(@catalog.resource(:class, 'Foo')['p2']).to eq(\"foo\")\n      end\n\n      it \"should ensure each node class is in catalog and has appropriate tags\" do\n        klasses = ['bar::foo']\n        @node.classes = klasses\n        ast_obj = Puppet::Parser::AST::Leaf.new(:value => 'foo')\n        klasses.each do |name|\n          klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'p1' => ast_obj, 'p2' => ast_obj})\n          @compiler.environment.known_resource_types.add klass\n        end\n        catalog = @compiler.compile\n\n        r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' }\n        expect(r2.tags).to eq(Puppet::Util::TagSet.new(['bar::foo', 'class', 'bar', 'foo']))\n      end\n    end\n\n    it \"should fail if required parameters are missing\" do\n      klass = {'foo'=>{'a'=>'one'}}\n      @node.classes = klass\n      klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'a' => nil, 'b' => nil})\n      @compiler.environment.known_resource_types.add klass\n      expect { @compiler.compile }.to raise_error(Puppet::PreformattedError, /Class\\[Foo\\]: expects a value for parameter 'b'/)\n    end\n\n    it \"should fail if invalid parameters are passed\" do\n      klass = {'foo'=>{'3'=>'one'}}\n      @node.classes = klass\n      klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {})\n      @compiler.environment.known_resource_types.add klass\n      expect { @compiler.compile }.to raise_error(Puppet::PreformattedError, /Class\\[Foo\\]: has no parameter named '3'/)\n    end\n\n    it \"should ensure class is in catalog without params\" do\n      @node.classes = {'foo'=>nil}\n      foo = Puppet::Resource::Type.new(:hostclass, 'foo')\n      @compiler.environment.known_resource_types.add foo\n      catalog = @compiler.compile\n      expect(catalog.classes).to include 'foo'\n    end\n\n    it \"should not evaluate the resources created for found classes unless asked\" do\n      allow(@compiler.catalog).to receive(:tag)\n\n      expect(@resource).not_to receive(:evaluate)\n\n      expect(@class).to receive(:ensure_in_catalog).and_return(@resource)\n      allow(@scope).to receive(:class_scope).with(@class)\n\n      @compiler.evaluate_classes(%w{myclass}, @scope)\n    end\n\n    it \"should immediately evaluate the resources created for found classes when asked\" do\n      allow(@compiler.catalog).to receive(:tag)\n\n      expect(@resource).to receive(:evaluate)\n      expect(@class).to receive(:ensure_in_catalog).and_return(@resource)\n      allow(@scope).to receive(:class_scope).with(@class)\n\n      @compiler.evaluate_classes(%w{myclass}, @scope, false)\n    end\n\n    it \"should skip classes that have already been evaluated\" do\n      allow(@compiler.catalog).to receive(:tag)\n\n      allow(@scope).to receive(:class_scope).with(@class).and_return(@scope)\n\n      expect(@compiler).not_to receive(:add_resource)\n\n      expect(@resource).not_to receive(:evaluate)\n\n      expect(Puppet::Parser::Resource).not_to receive(:new)\n      @compiler.evaluate_classes(%w{myclass}, @scope, false)\n    end\n\n    it \"should skip classes previously evaluated with different capitalization\" do\n      allow(@compiler.catalog).to receive(:tag)\n      allow(@scope.environment.known_resource_types).to receive(:find_hostclass).with(\"MyClass\").and_return(@class)\n      allow(@scope).to receive(:class_scope).with(@class).and_return(@scope)\n      expect(@compiler).not_to receive(:add_resource)\n      expect(@resource).not_to receive(:evaluate)\n      expect(Puppet::Parser::Resource).not_to receive(:new)\n      @compiler.evaluate_classes(%w{MyClass}, @scope, false)\n    end\n  end\n\n  describe \"when evaluating AST nodes with no AST nodes present\" do\n    it \"should do nothing\" do\n      allow(@compiler.environment.known_resource_types).to receive(:nodes).and_return(false)\n      expect(Puppet::Parser::Resource).not_to receive(:new)\n\n      @compiler.send(:evaluate_ast_node)\n    end\n  end\n\n  describe \"when evaluating AST nodes with AST nodes present\" do\n    before do\n      allow(@compiler.environment.known_resource_types).to receive(:nodes?).and_return(true)\n\n      # Set some names for our test\n      allow(@node).to receive(:names).and_return(%w{a b c})\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"a\").and_return(nil)\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"b\").and_return(nil)\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"c\").and_return(nil)\n\n      # It should check this last, of course.\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"default\").and_return(nil)\n    end\n\n    it \"should fail if the named node cannot be found\" do\n      expect { @compiler.send(:evaluate_ast_node) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should evaluate the first node class matching the node name\" do\n      node_class = double('node', :name => \"c\", :evaluate_code => nil)\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"c\").and_return(node_class)\n\n      node_resource = double('node resource', :ref => \"Node[c]\", :evaluate => nil, :type => \"node\")\n      expect(node_class).to receive(:ensure_in_catalog).and_return(node_resource)\n\n      @compiler.compile\n    end\n\n    it \"should match the default node if no matching node can be found\" do\n      node_class = double('node', :name => \"default\", :evaluate_code => nil)\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"default\").and_return(node_class)\n\n      node_resource = double('node resource', :ref => \"Node[default]\", :evaluate => nil, :type => \"node\")\n      expect(node_class).to receive(:ensure_in_catalog).and_return(node_resource)\n\n      @compiler.compile\n    end\n\n    it \"should evaluate the node resource immediately rather than using lazy evaluation\" do\n      node_class = double('node', :name => \"c\")\n      allow(@compiler.environment.known_resource_types).to receive(:node).with(\"c\").and_return(node_class)\n\n      node_resource = double('node resource', :ref => \"Node[c]\", :type => \"node\")\n      expect(node_class).to receive(:ensure_in_catalog).and_return(node_resource)\n\n      expect(node_resource).to receive(:evaluate)\n\n      @compiler.send(:evaluate_ast_node)\n    end\n  end\n\n  describe 'when using meta parameters to form relationships' do\n    include PuppetSpec::Compiler\n    [:before, :subscribe, :notify, :require].each do | meta_p |\n      it \"an entry consisting of nested empty arrays is flattened for parameter #{meta_p}\" do\n          expect {\n          node = Puppet::Node.new('someone')\n          manifest = <<-\"MANIFEST\"\n            notify{hello_kitty: message => meow, #{meta_p} => [[],[]]}\n            notify{hello_kitty2: message => meow, #{meta_p} => [[],[[]],[]]}\n          MANIFEST\n\n          catalog = compile_to_catalog(manifest, node)\n          catalog.to_ral\n        }.not_to raise_error\n      end\n    end\n  end\n\n  describe \"when evaluating node classes\" do\n    include PuppetSpec::Compiler\n\n    describe \"when provided classes in array format\" do\n      let(:node) { Puppet::Node.new('someone', :classes => ['something']) }\n\n      describe \"when the class exists\" do\n        it \"should succeed if the class is already included\" do\n          manifest = <<-MANIFEST\n          class something {}\n          include something\n          MANIFEST\n\n          catalog = compile_to_catalog(manifest, node)\n\n          expect(catalog.resource('Class', 'Something')).not_to be_nil\n        end\n\n        it \"should evaluate the class without parameters if it's not already included\" do\n          manifest = \"class something {}\"\n\n          catalog = compile_to_catalog(manifest, node)\n\n          expect(catalog.resource('Class', 'Something')).not_to be_nil\n        end\n\n        it \"raises if the class name is the same as the node definition\" do\n          name = node.name\n          node.classes = [name]\n\n          expect {\n            compile_to_catalog(<<-MANIFEST, node)\n            class #{name} {}\n            node #{name} {\n              include #{name}\n            }\n          MANIFEST\n          }.to raise_error(Puppet::Error, /Class '#{name}' is already defined \\(line: 1\\); cannot be redefined as a node \\(line: 2\\) on node #{name}/)\n        end\n\n        it \"evaluates the class if the node definition uses a regexp\" do\n          name = node.name\n          node.classes = [name]\n\n          catalog = compile_to_catalog(<<-MANIFEST, node)\n            class #{name} {}\n            node /#{name}/ {\n              include #{name}\n            }\n          MANIFEST\n\n          expect(@logs).to be_empty\n          expect(catalog.resource('Class', node.name.capitalize)).to_not be_nil\n        end\n      end\n\n      it \"should fail if the class doesn't exist\" do\n        expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/)\n      end\n    end\n\n    describe \"when provided classes in hash format\" do\n      describe \"for classes without parameters\" do\n        let(:node) { Puppet::Node.new('someone', :classes => {'something' => {}}) }\n\n        describe \"when the class exists\" do\n          it \"should succeed if the class is already included\" do\n            manifest = <<-MANIFEST\n            class something {}\n            include something\n            MANIFEST\n\n            catalog = compile_to_catalog(manifest, node)\n\n            expect(catalog.resource('Class', 'Something')).not_to be_nil\n          end\n\n          it \"should evaluate the class if it's not already included\" do\n            manifest = <<-MANIFEST\n            class something {}\n            MANIFEST\n\n            catalog = compile_to_catalog(manifest, node)\n\n            expect(catalog.resource('Class', 'Something')).not_to be_nil\n          end\n        end\n\n        it \"should fail if the class doesn't exist\" do\n          expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/)\n        end\n      end\n\n      describe \"for classes with parameters\" do\n        let(:node) { Puppet::Node.new('someone', :classes => {'something' => {'configuron' => 'defrabulated'}}) }\n\n        describe \"when the class exists\" do\n          it \"should fail if the class is already included\" do\n            manifest = <<-MANIFEST\n            class something($configuron=frabulated) {}\n            include something\n            MANIFEST\n\n            expect { compile_to_catalog(manifest, node) }.to raise_error(Puppet::Error, /Class\\[Something\\] is already declared/)\n          end\n\n          it \"should evaluate the class if it's not already included\" do\n            manifest = <<-MANIFEST\n            class something($configuron=frabulated) {}\n            MANIFEST\n\n            catalog = compile_to_catalog(manifest, node)\n\n            resource = catalog.resource('Class', 'Something')\n            expect(resource['configuron']).to eq('defrabulated')\n          end\n        end\n\n        it \"should fail if the class doesn't exist\" do\n          expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/)\n        end\n\n        it 'evaluates classes declared with parameters before unparameterized classes' do\n          node = Puppet::Node.new('someone', :classes => { 'app::web' => {}, 'app' => { 'port' => 8080 } })\n          manifest = <<-MANIFEST\n          class app($port = 80) { }\n\n          class app::web($port = $app::port) inherits app {\n            notify { expected: message => \"$port\" }\n          }\n          MANIFEST\n\n          catalog = compile_to_catalog(manifest, node)\n\n          expect(catalog).to have_resource(\"Class[App]\").with_parameter(:port, 8080)\n          expect(catalog).to have_resource(\"Class[App::Web]\")\n          expect(catalog).to have_resource(\"Notify[expected]\").with_parameter(:message, \"8080\")\n        end\n      end\n    end\n  end\n\n  describe \"when managing resource overrides\" do\n    before do\n      @override = double('override', :ref => \"File[/foo]\", :type => \"my\")\n      @resource = resource(:file, \"/foo\")\n    end\n\n    it \"should be able to store overrides\" do\n      expect { @compiler.add_override(@override) }.not_to raise_error\n    end\n\n    it \"should apply overrides to the appropriate resources\" do\n      @compiler.add_resource(@scope, @resource)\n      expect(@resource).to receive(:merge).with(@override)\n\n      @compiler.add_override(@override)\n\n      @compiler.compile\n    end\n\n    it \"should accept overrides before the related resource has been created\" do\n      expect(@resource).to receive(:merge).with(@override)\n\n      # First store the override\n      @compiler.add_override(@override)\n\n      # Then the resource\n      @compiler.add_resource(@scope, @resource)\n\n      # And compile, so they get resolved\n      @compiler.compile\n    end\n\n    it \"should fail if the compile is finished and resource overrides have not been applied\" do\n      @compiler.add_override(@override)\n\n      expect { @compiler.compile }.to raise_error Puppet::ParseError, 'Could not find resource(s) File[/foo] for overriding'\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/files_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parser/files'\n\ndescribe Puppet::Parser::Files do\n  include PuppetSpec::Files\n\n  let(:modulepath) { tmpdir(\"modulepath\") }\n  let(:environment) { Puppet::Node::Environment.create(:testing, [modulepath]) }\n  let(:mymod) { File.join(modulepath, \"mymod\") }\n  let(:mymod_files) { File.join(mymod, \"files\") }\n  let(:mymod_a_file) { File.join(mymod_files, \"some.txt\") }\n  let(:mymod_templates) { File.join(mymod, \"templates\") }\n  let(:mymod_a_template) { File.join(mymod_templates, \"some.erb\") }\n  let(:mymod_manifests) { File.join(mymod, \"manifests\") }\n  let(:mymod_init_manifest) { File.join(mymod_manifests, \"init.pp\") }\n  let(:mymod_another_manifest) { File.join(mymod_manifests, \"another.pp\") }\n  let(:an_absolute_file_path_outside_of_module) { make_absolute(\"afilenamesomewhere\") }\n\n  before do\n    FileUtils.mkdir_p(mymod_files)\n    File.open(mymod_a_file, 'w') do |f|\n      f.puts('something')\n    end\n    FileUtils.mkdir_p(mymod_templates)\n    File.open(mymod_a_template, 'w') do |f|\n      f.puts('<%= \"something\" %>')\n    end\n    FileUtils.mkdir_p(mymod_manifests)\n    File.open(mymod_init_manifest, 'w') do |f|\n      f.puts('class mymod { }')\n    end\n    File.open(mymod_another_manifest, 'w') do |f|\n      f.puts('class mymod::another { }')\n    end\n  end\n\n  describe \"when searching for files\" do\n    it \"returns fully-qualified file names directly\" do\n      expect(Puppet::Parser::Files.find_file(an_absolute_file_path_outside_of_module, environment)).to eq(an_absolute_file_path_outside_of_module)\n    end\n\n    it \"returns the full path to the file if given a modulename/relative_filepath selector \" do\n      expect(Puppet::Parser::Files.find_file(\"mymod/some.txt\", environment)).to eq(mymod_a_file)\n    end\n\n    it \"returns nil if the module is not found\" do\n      expect(Puppet::Parser::Files.find_file(\"mod_does_not_exist/myfile\", environment)).to be_nil\n    end\n\n    it \"also returns nil if the module is found, but the file is not\" do\n      expect(Puppet::Parser::Files.find_file(\"mymod/file_does_not_exist\", environment)).to be_nil\n    end\n  end\n\n  describe \"when searching for templates\" do\n    it \"returns fully-qualified templates directly\" do\n      expect(Puppet::Parser::Files.find_template(an_absolute_file_path_outside_of_module, environment)).to eq(an_absolute_file_path_outside_of_module)\n    end\n\n    it \"returns the full path to the template if given a modulename/relative_templatepath selector\" do\n      expect(Puppet::Parser::Files.find_template(\"mymod/some.erb\", environment)).to eq(mymod_a_template)\n    end\n\n    it \"returns nil if the module is not found\" do\n      expect(Puppet::Parser::Files.find_template(\"module_does_not_exist/mytemplate\", environment)).to be_nil\n    end\n\n    it \"returns nil if the module is found, but the template is not \" do\n      expect(Puppet::Parser::Files.find_template(\"mymod/template_does_not_exist\", environment)).to be_nil\n    end\n  end\n\n  describe \"when searching for manifests in a module\" do\n    let(:no_manifests_found) { [nil, []] }\n\n    it \"ignores invalid module names\" do\n      expect(Puppet::Parser::Files.find_manifests_in_modules(\"mod.has.invalid.name/init.pp\", environment)).to eq(no_manifests_found)\n    end\n\n    it \"returns no files when no module is found\" do\n      expect(Puppet::Parser::Files.find_manifests_in_modules(\"not_here_module/init.pp\", environment)).to eq(no_manifests_found)\n    end\n\n    it \"returns the name of the module and the manifests from the first found module\" do\n      expect(Puppet::Parser::Files.find_manifests_in_modules(\"mymod/init.pp\", environment)\n            ).to eq([\"mymod\", [mymod_init_manifest]])\n    end\n\n    it \"always includes init.pp if present\" do\n      expect(Puppet::Parser::Files.find_manifests_in_modules(\"mymod/another.pp\", environment)\n            ).to eq([\"mymod\", [mymod_init_manifest, mymod_another_manifest]])\n    end\n\n    it \"does not find the module when it is a different environment\" do\n      different_env = Puppet::Node::Environment.create(:different, [])\n\n      expect(Puppet::Parser::Files.find_manifests_in_modules(\"mymod/init.pp\", different_env)).to eq(no_manifests_found)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/create_resources_spec.rb",
    "content": "require 'puppet'\nrequire 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\n\ndescribe 'function for dynamically creating resources' do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  before :each do\n    node      = Puppet::Node.new(\"floppy\", :environment => 'production')\n    @compiler = Puppet::Parser::Compiler.new(node)\n    @scope    = Puppet::Parser::Scope.new(@compiler)\n    @topscope = @scope.compiler.topscope\n    @scope.parent = @topscope\n    Puppet::Parser::Functions.function(:create_resources)\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(:create_resources)).to eq(\"function_create_resources\")\n  end\n\n  it 'should require two or three arguments' do\n    expect { @scope.function_create_resources(['foo']) }.to raise_error(ArgumentError, 'create_resources(): Wrong number of arguments given (1 for minimum 2)')\n    expect { @scope.function_create_resources(['foo', 'bar', 'blah', 'baz']) }.to raise_error(ArgumentError, 'create_resources(): wrong number of arguments (4; must be 2 or 3)')\n  end\n\n  it 'should require second argument to be a hash' do\n    expect { @scope.function_create_resources(['foo','bar']) }.to raise_error(ArgumentError, 'create_resources(): second argument must be a hash')\n  end\n\n  it 'should require optional third argument to be a hash' do\n    expect { @scope.function_create_resources(['foo',{},'foo']) }.to raise_error(ArgumentError, 'create_resources(): third argument, if provided, must be a hash')\n  end\n\n  context 'when being called from a manifest in a file' do\n    let(:dir) do\n      dir_containing('manifests', {\n              'site.pp' => <<-EOF\n                # comment here to make the call be on a particular\n                # source line (3)\n                create_resources('notify', {\n                  'a'  => { 'message'=>'message a'},\n                  'b'  => { 'message'=>'message b'},\n                  }\n                )\n              EOF\n          }\n      )\n    end\n\n    it 'file and line information where call originates is written to all resources created in one call' do\n      node = Puppet::Node.new('test')\n      file = File.join(dir, 'site.pp')\n      Puppet[:manifest] = file\n      catalog = Puppet::Parser::Compiler.compile(node).filter { |r| r.virtual? }\n\n      expect(catalog.resource(:notify, 'a').file).to eq(file)\n      expect(catalog.resource(:notify, 'a').line).to eq(3)\n      expect(catalog.resource(:notify, 'b').file).to eq(file)\n      expect(catalog.resource(:notify, 'b').line).to eq(3)\n    end\n\n  end\n  describe 'when creating native types' do\n    it 'empty hash should not cause resources to be added' do\n      noop_catalog = compile_to_catalog(\"create_resources('file', {})\")\n      empty_catalog = compile_to_catalog(\"\")\n      expect(noop_catalog.resources.size).to eq(empty_catalog.resources.size)\n    end\n\n    it 'should be able to add' do\n      catalog = compile_to_catalog(\"create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}})\")\n      expect(catalog.resource(:file, \"/etc/foo\")['ensure']).to eq('present')\n    end\n\n    it 'should pick up and pass on file and line information' do\n      # mock location as the compile_to_catalog sets Puppet[:code} which does not\n      # have file/line support.\n      expect(Puppet::Pops::PuppetStack).to receive(:top_of_stack).once.and_return(['test.pp', 1234])\n      catalog = compile_to_catalog(\"create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}})\")\n      r = catalog.resource(:file, \"/etc/foo\")\n      expect(r.file).to eq('test.pp')\n      expect(r.line).to eq(1234)\n    end\n\n    it 'should be able to add virtual resources' do\n      catalog = compile_to_catalog(\"create_resources('@file', {'/etc/foo'=>{'ensure'=>'present'}})\\nrealize(File['/etc/foo'])\")\n      expect(catalog.resource(:file, \"/etc/foo\")['ensure']).to eq('present')\n    end\n\n    it 'unrealized exported resources should not be added' do\n      # a compiled catalog is normally filtered on virtual resources\n      # here the compilation is performed unfiltered to be able to find the exported resource\n      # it is then asserted that the exported resource is also virtual (and therefore filtered out by a real compilation).\n      catalog = compile_to_catalog_unfiltered(\"create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}})\")\n      expect(catalog.resource(:file, \"/etc/foo\").exported).to eq(true)\n      expect(catalog.resource(:file, \"/etc/foo\").virtual).to eq(true)\n    end\n\n    it 'should be able to add exported resources' do\n      catalog = compile_to_catalog(\"create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}}) realize(File['/etc/foo'])\")\n      expect(catalog.resource(:file, \"/etc/foo\")['ensure']).to eq('present')\n      expect(catalog.resource(:file, \"/etc/foo\").exported).to eq(true)\n    end\n\n    it 'should accept multiple resources' do\n      catalog = compile_to_catalog(\"create_resources('notify', {'foo'=>{'message'=>'one'}, 'bar'=>{'message'=>'two'}})\")\n      expect(catalog.resource(:notify, \"foo\")['message']).to eq('one')\n      expect(catalog.resource(:notify, \"bar\")['message']).to eq('two')\n    end\n\n    it 'should fail to add non-existing resource type' do\n      expect do\n        @scope.function_create_resources(['create-resource-foo', { 'foo' => {} }])\n      end.to raise_error(/Unknown resource type: 'create-resource-foo'/)\n    end\n\n    it 'should be able to add edges' do\n      rg = compile_to_relationship_graph(\"notify { test: }\\n create_resources('notify', {'foo'=>{'require'=>'Notify[test]'}})\")\n      test  = rg.vertices.find { |v| v.title == 'test' }\n      foo   = rg.vertices.find { |v| v.title == 'foo' }\n      expect(test).to be\n      expect(foo).to be\n      expect(rg.path_between(test,foo)).to be\n    end\n\n    it 'should filter out undefined edges as they cause errors' do\n      rg = compile_to_relationship_graph(\"notify { test: }\\n create_resources('notify', {'foo'=>{'require'=>undef}})\")\n      test  = rg.vertices.find { |v| v.title == 'test' }\n      foo   = rg.vertices.find { |v| v.title == 'foo' }\n      expect(test).to be\n      expect(foo).to be\n      expect(rg.path_between(foo,nil)).to_not be\n    end\n\n    it 'should filter out undefined edges in an array as they cause errors' do\n      rg = compile_to_relationship_graph(\"notify { test: }\\n create_resources('notify', {'foo'=>{'require'=>[undef]}})\")\n      test  = rg.vertices.find { |v| v.title == 'test' }\n      foo   = rg.vertices.find { |v| v.title == 'foo' }\n      expect(test).to be\n      expect(foo).to be\n      expect(rg.path_between(foo,nil)).to_not be\n    end\n\n    it 'should account for default values' do\n      catalog = compile_to_catalog(\"create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}, '/etc/baz'=>{'group'=>'food'}}, {'group' => 'bar'})\")\n      expect(catalog.resource(:file, \"/etc/foo\")['group']).to eq('bar')\n      expect(catalog.resource(:file, \"/etc/baz\")['group']).to eq('food')\n    end\n  end\n\n  describe 'when dynamically creating resource types' do\n    it 'should be able to create defined resource types' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        define foocreateresource($one) {\n          notify { $name: message => $one }\n        }\n\n        create_resources('foocreateresource', {'blah'=>{'one'=>'two'}})\n      MANIFEST\n      expect(catalog.resource(:notify, \"blah\")['message']).to eq('two')\n    end\n\n    it 'should fail if defines are missing params' do\n      expect {\n        compile_to_catalog(<<-MANIFEST)\n          define foocreateresource($one) {\n            notify { $name: message => $one }\n          }\n\n          create_resources('foocreateresource', {'blah'=>{}})\n        MANIFEST\n      }.to raise_error(Puppet::Error, /Foocreateresource\\[blah\\]: expects a value for parameter 'one'/)\n    end\n\n    it 'should accept undef as explicit value when parameter has no default value' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define foocreateresource($one) {\n            notify { $name: message => \"aaa${one}bbb\" }\n          }\n\n          create_resources('foocreateresource', {'blah'=>{ one => undef}})\n        MANIFEST\n      expect(catalog.resource(:notify, \"blah\")['message']).to eq('aaabbb')\n    end\n\n    it 'should use default value expression if given value is undef' do\n        catalog = compile_to_catalog(<<-MANIFEST)\n          define foocreateresource($one = 'xx') {\n            notify { $name: message => \"aaa${one}bbb\" }\n          }\n\n          create_resources('foocreateresource', {'blah'=>{ one => undef}})\n        MANIFEST\n      expect(catalog.resource(:notify, \"blah\")['message']).to eq('aaaxxbbb')\n    end\n\n    it 'should be able to add multiple defines' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        define foocreateresource($one) {\n          notify { $name: message => $one }\n        }\n\n        create_resources('foocreateresource', {'blah'=>{'one'=>'two'}, 'blaz'=>{'one'=>'three'}})\n      MANIFEST\n\n      expect(catalog.resource(:notify, \"blah\")['message']).to eq('two')\n      expect(catalog.resource(:notify, \"blaz\")['message']).to eq('three')\n    end\n\n    it 'should be able to add edges' do\n      rg = compile_to_relationship_graph(<<-MANIFEST)\n        define foocreateresource($one) {\n          notify { $name: message => $one }\n        }\n\n        notify { test: }\n\n        create_resources('foocreateresource', {'blah'=>{'one'=>'two', 'require' => 'Notify[test]'}})\n      MANIFEST\n\n      test = rg.vertices.find { |v| v.title == 'test' }\n      blah = rg.vertices.find { |v| v.title == 'blah' }\n      expect(test).to be\n      expect(blah).to be\n      expect(rg.path_between(test,blah)).to be\n    end\n\n    it 'should account for default values' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        define foocreateresource($one) {\n          notify { $name: message => $one }\n        }\n\n        create_resources('foocreateresource', {'blah'=>{}}, {'one' => 'two'})\n      MANIFEST\n\n      expect(catalog.resource(:notify, \"blah\")['message']).to eq('two')\n    end\n  end\n\n  describe 'when creating classes' do\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n    it 'should be able to create classes' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        class bar($one) {\n          notify { test: message => $one }\n        }\n\n        create_resources('class', {'bar'=>{'one'=>'two'}})\n      MANIFEST\n\n      expect(catalog.resource(:notify, \"test\")['message']).to eq('two')\n      expect(catalog.resource(:class, \"bar\")).not_to be_nil\n    end\n\n    it 'should error if class is exported' do\n      expect{\n        compile_to_catalog('class test{} create_resources(\"@@class\", {test => {}})')\n      }.to raise_error(/Classes are not virtualizable/)\n    end\n\n    it 'should error if class is virtual' do\n      expect{\n        compile_to_catalog('class test{} create_resources(\"@class\", {test => {}})')\n      }.to raise_error(/Classes are not virtualizable/)\n    end\n\n    it 'should be able to add edges' do\n      rg = compile_to_relationship_graph(<<-MANIFEST)\n        class bar($one) {\n          notify { test: message => $one }\n        }\n\n        notify { tester: }\n\n        create_resources('class', {'bar'=>{'one'=>'two', 'require' => 'Notify[tester]'}})\n      MANIFEST\n\n      test   = rg.vertices.find { |v| v.title == 'test' }\n      tester = rg.vertices.find { |v| v.title == 'tester' }\n      expect(test).to be\n      expect(tester).to be\n      expect(rg.path_between(tester,test)).to be\n    end\n\n    it 'should account for default values' do\n      catalog = compile_to_catalog(<<-MANIFEST)\n        class bar($one) {\n          notify { test: message => $one }\n        }\n\n        create_resources('class', {'bar'=>{}}, {'one' => 'two'})\n      MANIFEST\n\n      expect(catalog.resource(:notify, \"test\")['message']).to eq('two')\n      expect(catalog.resource(:class, \"bar\")).not_to be_nil\n    end\n\n    it 'should fail with a correct error message if the syntax of an imported file is incorrect' do\n      expect{\n        Puppet[:modulepath] = my_fixture_dir\n        compile_to_catalog('include foo')\n      }.to raise_error(Puppet::Error, /Syntax error at.*/)\n    end\n\n    it 'is not available when --tasks is on' do\n      Puppet[:tasks] = true\n      expect do\n        compile_to_catalog(<<-MANIFEST)\n          create_resources('class', {'bar'=>{}}, {'one' => 'two'})\n        MANIFEST\n      end.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n    end\n  end\n\n  def collect_notices(code)\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      compile_to_catalog(code)\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/digest_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the digest function\", :uses_checksums => true do\n  before :each do\n    n = Puppet::Node.new('unnamed')\n    c = Puppet::Parser::Compiler.new(n)\n    @scope = Puppet::Parser::Scope.new(c)\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(\"digest\")).to eq(\"function_digest\")\n  end\n\n  with_digest_algorithms do\n    it \"should use the proper digest function\" do\n      result = @scope.function_digest([plaintext])\n      expect(result).to(eql( checksum ))\n    end\n\n    it \"should only accept one parameter\" do\n      expect do\n        @scope.function_digest(['foo', 'bar'])\n      end.to raise_error(ArgumentError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/fail_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the 'fail' parser function\" do\n  let :scope do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    scope    = Puppet::Parser::Scope.new(compiler)\n    allow(scope).to receive(:environment).and_return(nil)\n    scope\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(:fail)).to eq(\"function_fail\")\n  end\n\n  it \"should raise a parse error if invoked\" do\n    expect { scope.function_fail([]) }.to raise_error Puppet::ParseError\n  end\n\n  it \"should join arguments into a string in the error\" do\n    expect { scope.function_fail([\"hello\", \"world\"]) }.to raise_error(/hello world/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\ndescribe \"the 'file' function\" do\n  include PuppetSpec::Files\n\n  let :node     do Puppet::Node.new('localhost') end\n  let :compiler do Puppet::Parser::Compiler.new(node) end\n  let :scope    do Puppet::Parser::Scope.new(compiler) end\n\n  def with_file_content(content)\n    path = tmpfile('file-function')\n    file = File.new(path, 'wb')\n    file.sync = true\n    file.print content\n    yield path\n  end\n\n  it \"should read a file\" do\n    with_file_content('file content') do |name|\n      expect(scope.function_file([name])).to eq(\"file content\")\n    end\n  end\n\n  it \"should read a file keeping line endings intact\" do\n    with_file_content(\"file content\\r\\n\") do |name|\n      expect(scope.function_file([name])).to eq(\"file content\\r\\n\")\n    end\n  end\n\n  it \"should read a file from a module path\" do\n    with_file_content('file content') do |name|\n      mod = double('module')\n      allow(mod).to receive(:file).with('myfile').and_return(name)\n      allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n\n      expect(scope.function_file(['mymod/myfile'])).to eq('file content')\n    end\n  end\n\n  it \"should return the first file if given two files with absolute paths\" do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        expect(scope.function_file([one, two])).to eq(\"one\")\n      end\n    end\n  end\n\n  it \"should return the first file if given two files with module paths\" do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        mod = double('module')\n        expect(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n        expect(mod).to receive(:file).with('one').and_return(one)\n        allow(mod).to receive(:file).with('two').and_return(two)\n\n        expect(scope.function_file(['mymod/one','mymod/two'])).to eq('one')\n      end\n    end\n  end\n\n  it \"should return the first file if given two files with mixed paths, absolute first\" do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        mod = double('module')\n        allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n        allow(mod).to receive(:file).with('two').and_return(two)\n\n        expect(scope.function_file([one,'mymod/two'])).to eq('one')\n      end\n    end\n  end\n\n  it \"should return the first file if given two files with mixed paths, module first\" do\n    with_file_content('one') do |one|\n      with_file_content('two') do |two|\n        mod = double('module')\n        expect(compiler.environment).to receive(:module).with('mymod').and_return(mod)\n        allow(mod).to receive(:file).with('two').and_return(two)\n\n        expect(scope.function_file(['mymod/two',one])).to eq('two')\n      end\n    end\n  end\n\n  it \"should not fail when some files are absent\" do\n    expect {\n      with_file_content('one') do |one|\n        expect(scope.function_file([make_absolute(\"/should-not-exist\"), one])).to eq('one')\n      end\n    }.to_not raise_error\n  end\n\n  it \"should fail when all files are absent\" do\n    expect {\n      scope.function_file([File.expand_path('one')])\n    }.to raise_error(Puppet::ParseError, /Could not find any files/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/fqdn_rand_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/scope'\n\ndescribe \"the fqdn_rand function\" do\n  include PuppetSpec::Scope\n\n  it \"returns an integer\" do\n    expect(fqdn_rand(3)).to be_an(Integer)\n  end\n\n  it \"provides a random number strictly less than the given max\" do\n    expect(fqdn_rand(3)).to satisfy {|n| n < 3 }\n  end\n\n  it \"provides the same 'random' value on subsequent calls for the same host\" do\n    expect(fqdn_rand(3)).to eql(fqdn_rand(3))\n  end\n\n  it \"considers the same host and same extra arguments to have the same random sequence\" do\n    first_random = fqdn_rand(3, :extra_identifier => [1, \"same\", \"host\"])\n    second_random = fqdn_rand(3, :extra_identifier => [1, \"same\", \"host\"])\n\n    expect(first_random).to eql(second_random)\n  end\n\n  it \"allows extra arguments to control the random value on a single host\" do\n    first_random = fqdn_rand(10000, :extra_identifier => [1, \"different\", \"host\"])\n    second_different_random = fqdn_rand(10000, :extra_identifier => [2, \"different\", \"host\"])\n\n    expect(first_random).not_to eql(second_different_random)\n  end\n\n  it \"should return different sequences of value for different hosts\" do\n    val1 = fqdn_rand(1000000000, :host => \"first.host.com\")\n    val2 = fqdn_rand(1000000000, :host => \"second.host.com\")\n\n    expect(val1).not_to eql(val2)\n  end\n\n  it \"should return a specific value with given set of inputs on non-fips enabled host\" do\n    allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(false)\n\n    expect(fqdn_rand(3000, :host => 'dummy.fqdn.net')).to eql(338)\n  end\n\n  it \"should return a specific value with given set of inputs on fips enabled host\" do\n    allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n\n    expect(fqdn_rand(3000, :host => 'dummy.fqdn.net')).to eql(278)\n  end\n\n  it \"should return a specific value with given seed on a non-fips enabled host\" do\n    allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(false)\n\n    expect(fqdn_rand(5000, :extra_identifier => ['expensive job 33'])).to eql(3374)\n  end\n\n  it \"should return a specific value with given seed on a fips enabled host\" do\n    allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n\n    expect(fqdn_rand(5000, :extra_identifier => ['expensive job 33'])).to eql(2389)\n  end\n\n  it \"returns the same value if only host differs by case\" do\n    val1 = fqdn_rand(1000000000, :host => \"host.example.com\", :extra_identifier => [nil, true])\n    val2 = fqdn_rand(1000000000, :host => \"HOST.example.com\", :extra_identifier => [nil, true])\n\n    expect(val1).to eql(val2)\n  end\n\n  it \"returns the same value if only host differs by case and an initial seed is given\" do\n    val1 = fqdn_rand(1000000000, :host => \"host.example.com\", :extra_identifier => ['a seed', true])\n    val2 = fqdn_rand(1000000000, :host => \"HOST.example.com\", :extra_identifier => ['a seed', true])\n\n    expect(val1).to eql(val2)\n  end\n\n  def fqdn_rand(max, args = {})\n    host = args[:host] || '127.0.0.1'\n    extra = args[:extra_identifier] || []\n\n    scope = create_test_scope_for_node('localhost')\n    scope.set_facts({ 'networking' => { 'fqdn' => host }})\n\n    scope.function_fqdn_rand([max] + extra)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/generate_spec.rb",
    "content": "require 'spec_helper'\n\ndef with_executor\n  return yield unless Puppet::Util::Platform.jruby?\n\n  begin\n    Puppet::Util::ExecutionStub.set do |command, options, stdin, stdout, stderr|\n      require 'open3'\n      # simulate what puppetserver does\n      Dir.chdir(options[:cwd]) do\n        out, err, _status = Open3.capture3(*command)\n        stdout.write(out)\n        stderr.write(err)\n        # execution api expects stdout to be returned\n        out\n      end\n    end\n    yield\n  ensure\n    Puppet::Util::ExecutionStub.reset\n  end\nend\n\ndescribe \"the generate function\" do\n  include PuppetSpec::Files\n\n  let :node     do Puppet::Node.new('localhost') end\n  let :compiler do Puppet::Parser::Compiler.new(node) end\n  let :scope    do Puppet::Parser::Scope.new(compiler) end\n  let :cwd      do tmpdir('generate') end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(\"generate\")).to eq(\"function_generate\")\n  end\n\n  it \"accept a fully-qualified path as a command\" do\n    command = File.expand_path('/command/foo')\n    expect(Puppet::Util::Execution).to receive(:execute).with([command], anything).and_return(\"yay\")\n    expect(scope.function_generate([command])).to eq(\"yay\")\n  end\n\n  it \"should not accept a relative path as a command\" do\n    expect { scope.function_generate([\"command\"]) }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should not accept a command containing illegal characters\" do\n    expect { scope.function_generate([File.expand_path('/##/command')]) }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should not accept a command containing spaces\" do\n    expect { scope.function_generate([File.expand_path('/com mand')]) }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should not accept a command containing '..'\", :unless => RUBY_PLATFORM == 'java' do\n    command = File.expand_path(\"/command/../\")\n    expect { scope.function_generate([command]) }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should execute the generate script with the correct working directory\" do\n    command = File.expand_path(\"/usr/local/command\")\n    expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: %r{/usr/local})).and_return(\"yay\")\n    scope.function_generate([command])\n  end\n\n  it \"should execute the generate script with failonfail\" do\n    command = File.expand_path(\"/usr/local/command\")\n    expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(failonfail: true)).and_return(\"yay\")\n    scope.function_generate([command])\n  end\n\n  it \"should execute the generate script with combine\" do\n    command = File.expand_path(\"/usr/local/command\")\n    expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(combine: true)).and_return(\"yay\")\n    scope.function_generate([command])\n  end\n\n  it \"executes a command in a working directory\" do\n    if Puppet::Util::Platform.windows?\n      command = File.join(cwd, 'echo.bat')\n      File.write(command, <<~END)\n        @echo off\n        echo %CD%\n      END\n      expect(scope.function_generate([command]).chomp).to match(cwd.gsub('/', '\\\\'))\n    else\n      with_executor do\n        command = File.join(cwd, 'echo.sh')\n        File.write(command, <<~END)\n          #!/bin/sh\n          echo $PWD\n        END\n        Puppet::FileSystem.chmod(0755, command)\n        expect(scope.function_generate([command]).chomp).to eq(cwd)\n      end\n    end\n  end\n\n  describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n    it \"should accept the tilde in the path\" do\n      command = \"C:/DOCUME~1/ADMINI~1/foo.bat\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: \"C:/DOCUME~1/ADMINI~1\")).and_return(\"yay\")\n      expect(scope.function_generate([command])).to eq('yay')\n    end\n\n    it \"should accept lower-case drive letters\" do\n      command = 'd:/command/foo'\n      expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: \"d:/command\")).and_return(\"yay\")\n      expect(scope.function_generate([command])).to eq('yay')\n    end\n\n    it \"should accept upper-case drive letters\" do\n      command = 'D:/command/foo'\n      expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: \"D:/command\")).and_return(\"yay\")\n      expect(scope.function_generate([command])).to eq('yay')\n    end\n\n    it \"should accept forward and backslashes in the path\" do\n      command = 'D:\\command/foo\\bar'\n      expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: 'D:\\command/foo')).and_return(\"yay\")\n      expect(scope.function_generate([command])).to eq('yay')\n    end\n\n    it \"should reject colons when not part of the drive letter\" do\n      expect { scope.function_generate(['C:/com:mand']) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should reject root drives\" do\n      expect { scope.function_generate(['C:/']) }.to raise_error(Puppet::ParseError)\n    end\n  end\n\n  describe \"on POSIX\", :if => Puppet.features.posix? do\n    it \"should reject backslashes\" do\n      expect { scope.function_generate(['/com\\\\mand']) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should accept plus and dash\" do\n      command = \"/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-/foo\"\n      expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: '/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-')).and_return(\"yay\")\n      expect(scope.function_generate([command])).to eq('yay')\n    end\n  end\n\n  describe \"function_generate\", :unless => RUBY_PLATFORM == 'java' do\n    let :command do\n      script_containing('function_generate',\n        :windows => '@echo off' + \"\\n\" + 'echo a-%1 b-%2',\n        :posix   => '#!/bin/sh' + \"\\n\" + 'echo a-$1 b-$2')\n    end\n\n    after :each do\n      File.delete(command) if Puppet::FileSystem.exist?(command)\n    end\n\n    it \"returns the output as a String\" do\n      expect(scope.function_generate([command]).class).to eq(String)\n    end\n\n    it \"should call generator with no arguments\" do\n      expect(scope.function_generate([command])).to eq(\"a- b-\\n\")\n    end\n\n    it \"should call generator with one argument\" do\n      expect(scope.function_generate([command, 'one'])).to eq(\"a-one b-\\n\")\n    end\n\n    it \"should call generator with wo arguments\" do\n      expect(scope.function_generate([command, 'one', 'two'])).to eq(\"a-one b-two\\n\")\n    end\n\n    it \"should fail if generator is not absolute\" do\n      expect { scope.function_generate(['boo']) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should fail if generator fails\" do\n      expect { scope.function_generate(['/boo']) }.to raise_error(Puppet::ParseError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/hiera_array_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/scope'\n\ndescribe 'Puppet::Parser::Functions#hiera_array' do\n  include PuppetSpec::Scope\n\n  let :scope do create_test_scope_for_node('foo') end\n\n  it 'should raise an error since this function is converted to 4x API)' do\n    expect { scope.function_hiera_array(['key']) }.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/hiera_hash_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/scope'\n\ndescribe 'Puppet::Parser::Functions#hiera_hash' do\n  include PuppetSpec::Scope\n\n  let :scope do create_test_scope_for_node('foo') end\n\n  it 'should raise an error since this function is converted to 4x API)' do\n    expect { scope.function_hiera_hash(['key']) }.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/hiera_include_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/scope'\n\ndescribe 'Puppet::Parser::Functions#hiera_include' do\n  include PuppetSpec::Scope\n\n  let :scope do create_test_scope_for_node('foo') end\n\n  it 'should raise an error since this function is converted to 4x API)' do\n    expect { scope.function_hiera_include(['key']) }.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/hiera_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/scope'\n\ndescribe 'Puppet::Parser::Functions#hiera' do\n  include PuppetSpec::Scope\n\n  let :scope do create_test_scope_for_node('foo') end\n\n  it 'should raise an error since this function is converted to 4x API)' do\n    expect { scope.function_hiera(['key']) }.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/inline_template_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the inline_template function\" do\n  let(:node) { Puppet::Node.new('localhost') }\n  let(:compiler) { Puppet::Parser::Compiler.new(node) }\n  let(:scope) { Puppet::Parser::Scope.new(compiler) }\n\n  it \"should concatenate template wrapper outputs for multiple templates\" do\n    expect(inline_template(\"template1\", \"template2\")).to eq(\"template1template2\")\n  end\n\n  it \"should raise an error if the template raises an error\" do\n    expect { inline_template(\"<% raise 'error' %>\") }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"is not interfered with by a variable called 'string' (#14093)\" do\n    scope['string'] = \"this is a variable\"\n    expect(inline_template(\"this is a template\")).to eq(\"this is a template\")\n  end\n\n  it \"has access to a variable called 'string' (#14093)\" do\n    scope['string'] = \"this is a variable\"\n    expect(inline_template(\"string was: <%= @string %>\")).to eq(\"string was: this is a variable\")\n  end\n\n  it 'is not available when --tasks is on' do\n    Puppet[:tasks] = true\n    expect {\n      inline_template(\"<%= lookupvar('myvar') %>\")\n    }.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n  end\n\n  def inline_template(*templates)\n    scope.function_inline_template(templates)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/lookup_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'stringio'\nrequire 'puppet_spec/scope'\n\ndescribe \"lookup function\" do\n  include PuppetSpec::Scope\n\n  let :scope do create_test_scope_for_node('foo') end\n\n  it 'should raise an error since this function is converted to 4x API)' do\n    expect { scope.function_lookup(['key']) }.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/realize_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/resource'\nrequire 'puppet_spec/compiler'\n\ndescribe \"the realize function\" do\n  include Matchers::Resource\n  include PuppetSpec::Compiler\n\n  it \"realizes a single, referenced resource\" do\n    catalog = compile_to_catalog(<<-EOM)\n      @notify { testing: }\n      realize(Notify[testing])\n    EOM\n\n    expect(catalog).to have_resource(\"Notify[testing]\")\n  end\n\n  it \"realizes multiple resources\" do\n    catalog = compile_to_catalog(<<-EOM)\n      @notify { testing: }\n      @notify { other: }\n      realize(Notify[testing], Notify[other])\n    EOM\n\n    expect(catalog).to have_resource(\"Notify[testing]\")\n    expect(catalog).to have_resource(\"Notify[other]\")\n  end\n\n  it \"realizes resources provided in arrays\" do\n    catalog = compile_to_catalog(<<-EOM)\n      @notify { testing: }\n      @notify { other: }\n      realize([Notify[testing], [Notify[other]]])\n    EOM\n\n    expect(catalog).to have_resource(\"Notify[testing]\")\n    expect(catalog).to have_resource(\"Notify[other]\")\n  end\n\n  it \"fails when the resource does not exist\" do\n    expect do\n      compile_to_catalog(<<-EOM)\n        realize(Notify[missing])\n      EOM\n    end.to raise_error(Puppet::Error, /Failed to realize/)\n  end\n\n  it \"fails when no parameters given\" do\n    expect do\n      compile_to_catalog(<<-EOM)\n        realize()\n      EOM\n    end.to raise_error(Puppet::Error, /Wrong number of arguments/)\n  end\n\n  it \"silently does nothing when an empty array of resources is given\" do\n    compile_to_catalog(<<-EOM)\n      realize([])\n    EOM\n  end\n\n  it 'is not available when --tasks is on' do\n    Puppet[:tasks] = true\n    expect do\n      compile_to_catalog(<<-MANIFEST)\n        realize([])\n      MANIFEST\n    end.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/regsubst_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the regsubst function\" do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it 'should raise an ParseError' do\n    expect do\n      @scope.function_regsubst(\n      [ 'the monkey breaks banana trees',\n        'b[an]*a',\n        'coconut'\n      ])\n    end.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/scanf_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the scanf function\" do\n  let(:node) { Puppet::Node.new('localhost') }\n  let(:compiler) { Puppet::Parser::Compiler.new(node) }\n  let(:scope) { Puppet::Parser::Scope.new(compiler) }\n\n  it 'scans a value and returns an array' do\n    expect(scope.function_scanf(['42', '%i'])[0] == 42)\n  end\n\n  it 'returns empty array if nothing was scanned' do\n    expect(scope.function_scanf(['no', '%i']) == [])\n  end\n\n  it 'produces result up to first unsuccessful scan' do\n    expect(scope.function_scanf(['42 no', '%i'])[0] == 42)\n  end\n\n  it 'errors when not given enough arguments' do\n    expect do\n      scope.function_scanf(['42'])\n    end.to raise_error(/.*scanf\\(\\): Wrong number of arguments given/m)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/shellquote_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the shellquote function\" do\n  let :node     do Puppet::Node.new('localhost') end\n  let :compiler do Puppet::Parser::Compiler.new(node) end\n  let :scope    do Puppet::Parser::Scope.new(compiler) end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(\"shellquote\")).to eq(\"function_shellquote\")\n  end\n\n  it \"should handle no arguments\" do\n    expect(scope.function_shellquote([])).to eq(\"\")\n  end\n\n  it \"should handle several simple arguments\" do\n    expect(scope.function_shellquote(\n      ['foo', 'bar@example.com', 'localhost:/dev/null', 'xyzzy+-4711,23']\n    )).to eq('foo bar@example.com localhost:/dev/null xyzzy+-4711,23')\n  end\n\n  it \"should handle array arguments\" do\n    expect(scope.function_shellquote(\n      ['foo', ['bar@example.com', 'localhost:/dev/null'],\n      'xyzzy+-4711,23']\n    )).to eq('foo bar@example.com localhost:/dev/null xyzzy+-4711,23')\n  end\n\n  it \"should quote unsafe characters\" do\n    expect(scope.function_shellquote(['/etc/passwd ', '(ls)', '*', '[?]', \"'&'\"])).\n      to eq('\"/etc/passwd \" \"(ls)\" \"*\" \"[?]\" \"\\'&\\'\"')\n  end\n\n  it \"should deal with double quotes\" do\n    expect(scope.function_shellquote(['\"foo\"bar\"'])).to eq('\\'\"foo\"bar\"\\'')\n  end\n\n  it \"should cope with dollar signs\" do\n    expect(scope.function_shellquote(['$PATH', 'foo$bar', '\"x$\"'])).\n      to eq(\"'$PATH' 'foo$bar' '\\\"x$\\\"'\")\n  end\n\n  it \"should deal with apostrophes (single quotes)\" do\n    expect(scope.function_shellquote([\"'foo'bar'\", \"`$'EDITOR'`\"])).\n      to eq('\"\\'foo\\'bar\\'\" \"\\\\`\\\\$\\'EDITOR\\'\\\\`\"')\n  end\n\n  it \"should cope with grave accents (backquotes)\" do\n    expect(scope.function_shellquote(['`echo *`', '`ls \"$MAILPATH\"`'])).\n      to eq(\"'`echo *`' '`ls \\\"$MAILPATH\\\"`'\")\n  end\n\n  it \"should deal with both single and double quotes\" do\n    expect(scope.function_shellquote(['\\'foo\"bar\"xyzzy\\'', '\"foo\\'bar\\'xyzzy\"'])).\n      to eq('\"\\'foo\\\\\"bar\\\\\"xyzzy\\'\" \"\\\\\"foo\\'bar\\'xyzzy\\\\\"\"')\n  end\n\n  it \"should handle multiple quotes *and* dollars and backquotes\" do\n    expect(scope.function_shellquote(['\\'foo\"$x`bar`\"xyzzy\\''])).\n      to eq('\"\\'foo\\\\\"\\\\$x\\\\`bar\\\\`\\\\\"xyzzy\\'\"')\n  end\n\n  it \"should handle linefeeds\" do\n    expect(scope.function_shellquote([\"foo \\n bar\"])).to eq(\"\\\"foo \\n bar\\\"\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/split_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the split function\" do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it 'should raise a ParseError' do\n    expect { @scope.function_split([ '130;236;254;10', ';']) }.to raise_error(Puppet::ParseError, /can only be called using the 4.x function API/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/sprintf_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the sprintf function\" do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(\"sprintf\")).to eq(\"function_sprintf\")\n  end\n\n  it \"should raise an ArgumentError if there is less than 1 argument\" do\n    expect { @scope.function_sprintf([]) }.to( raise_error(ArgumentError))\n  end\n\n  it \"should format integers\" do\n    result = @scope.function_sprintf([\"%+05d\", \"23\"])\n    expect(result).to(eql(\"+0023\"))\n  end\n\n  it \"should format floats\" do\n    result = @scope.function_sprintf([\"%+.2f\", \"2.7182818284590451\"])\n    expect(result).to(eql(\"+2.72\"))\n  end\n\n  it \"should format large floats\" do\n    result = @scope.function_sprintf([\"%+.2e\", \"27182818284590451\"])\n    str =\n      \"+2.72e+16\"\n    expect(result).to(eql(str))\n  end\n\n  it \"should perform more complex formatting\" do\n    result = @scope.function_sprintf(\n      [ \"<%.8s:%#5o %#8X (%-8s)>\",\n        \"overlongstring\", \"23\", \"48879\", \"foo\" ])\n    expect(result).to(eql(\"<overlong:  027   0XBEEF (foo     )>\"))\n  end\n\n  it 'does not attempt to mutate its arguments' do\n    args = ['%d', 1].freeze\n    expect { @scope.function_sprintf(args) }.to_not raise_error\n  end\n\n  it 'support named arguments in a hash with string keys' do\n    result = @scope.function_sprintf([\"%<foo>d : %<bar>f\", {'foo' => 1, 'bar' => 2}])\n    expect(result).to eq(\"1 : 2.000000\")\n  end\n\n  it 'raises a key error if a key is not present' do\n    expect do\n      @scope.function_sprintf([\"%<foo>d : %<zanzibar>f\", {'foo' => 1, 'bar' => 2}])\n    end.to raise_error(KeyError, /key<zanzibar> not found/)\n  end\n\n  it 'a hash with string keys that is output formats as strings' do\n    result = @scope.function_sprintf([\"%s\", {'foo' => 1, 'bar' => 2}])\n    expect(result).to eq(\"{\\\"foo\\\"=>1, \\\"bar\\\"=>2}\")\n  end\n\n  it 'named arguments hash with non string keys are tolerated' do\n    result = @scope.function_sprintf([\"%<foo>d : %<bar>f\", {'foo' => 1, 'bar' => 2, 1 => 2, [1] => 2, false => true, {} => {}}])\n    expect(result).to eq(\"1 : 2.000000\")\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/tag_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the 'tag' function\" do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(:tag)).to eq(\"function_tag\")\n  end\n\n  it \"should tag the resource with any provided tags\" do\n    resource = Puppet::Parser::Resource.new(:file, \"/file\", :scope => @scope)\n    expect(@scope).to receive(:resource).and_return(resource)\n\n    @scope.function_tag [\"one\", \"two\"]\n\n    expect(resource).to be_tagged(\"one\")\n    expect(resource).to be_tagged(\"two\")\n  end\n\n  it 'is not available when --tasks is on' do\n    Puppet[:tasks] = true\n    expect do\n      @scope.function_tag(['one', 'two'])\n    end.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/tagged_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the 'tagged' function\" do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(:tagged)).to eq(\"function_tagged\")\n  end\n\n  it 'is not available when --tasks is on' do\n    Puppet[:tasks] = true\n    expect do\n      @scope.function_tagged(['one', 'two'])\n    end.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n  end\n\n  it 'should be case-insensitive' do\n    resource = Puppet::Parser::Resource.new(:file, \"/file\", :scope => @scope)\n    allow(@scope).to receive(:resource).and_return(resource)\n    @scope.function_tag [\"one\"]\n\n    expect(@scope.function_tagged(['One'])).to eq(true)\n  end\n\n  it 'should check if all specified tags are included' do\n    resource = Puppet::Parser::Resource.new(:file, \"/file\", :scope => @scope)\n    allow(@scope).to receive(:resource).and_return(resource)\n    @scope.function_tag [\"one\"]\n\n    expect(@scope.function_tagged(['one', 'two'])).to eq(false)\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/template_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the template function\" do\n  let :node     do Puppet::Node.new('localhost') end\n  let :compiler do Puppet::Parser::Compiler.new(node) end\n  let :scope    do Puppet::Parser::Scope.new(compiler) end\n\n  it \"concatenates outputs for multiple templates\" do\n    tw1 = double(\"template_wrapper1\")\n    tw2 = double(\"template_wrapper2\")\n    allow(Puppet::Parser::TemplateWrapper).to receive(:new).and_return(tw1,tw2)\n    allow(tw1).to receive(:file=).with(\"1\")\n    allow(tw2).to receive(:file=).with(\"2\")\n    allow(tw1).to receive(:result).and_return(\"result1\")\n    allow(tw2).to receive(:result).and_return(\"result2\")\n\n    expect(scope.function_template([\"1\",\"2\"])).to eq(\"result1result2\")\n  end\n\n  it \"raises an error if the template raises an error\" do\n    tw = double('template_wrapper')\n    allow(tw).to receive(:file=).with(\"1\")\n    allow(Puppet::Parser::TemplateWrapper).to receive(:new).and_return(tw)\n    allow(tw).to receive(:result).and_raise\n\n    expect {\n      scope.function_template([\"1\"])\n    }.to raise_error(Puppet::ParseError, /Failed to parse template/)\n  end\n\n  context \"when accessing scope variables via method calls (deprecated)\" do\n    it \"raises an error when accessing an undefined variable\" do\n      expect {\n        eval_template(\"template <%= deprecated %>\")\n      }.to raise_error(Puppet::ParseError, /undefined local variable or method `deprecated'/)\n    end\n\n    it \"looks up the value from the scope\" do\n      scope[\"deprecated\"] = \"deprecated value\"\n      expect { eval_template(\"template <%= deprecated %>\")}.to raise_error(/undefined local variable or method `deprecated'/)\n    end\n\n    it \"still has access to Kernel methods\" do\n      expect { eval_template(\"<%= binding %>\") }.to_not raise_error\n    end\n  end\n\n  context \"when accessing scope variables as instance variables\" do\n    it \"has access to values\" do\n      scope['scope_var'] = \"value\"\n      expect(eval_template(\"<%= @scope_var %>\")).to eq(\"value\")\n    end\n\n    it \"get nil accessing a variable that does not exist\" do\n      expect(eval_template(\"<%= @not_defined.nil? %>\")).to eq(\"true\")\n    end\n\n    it \"get nil accessing a variable that is undef\" do\n      scope['undef_var'] = :undef\n      expect(eval_template(\"<%= @undef_var.nil? %>\")).to eq(\"true\")\n    end\n  end\n\n  it \"is not interfered with by having a variable named 'string' (#14093)\" do\n    scope['string'] = \"this output should not be seen\"\n    expect(eval_template(\"some text that is static\")).to eq(\"some text that is static\")\n  end\n\n  it \"has access to a variable named 'string' (#14093)\" do\n    scope['string'] = \"the string value\"\n    expect(eval_template(\"string was: <%= @string %>\")).to eq(\"string was: the string value\")\n  end\n\n  it \"does not have direct access to Scope#lookupvar\" do\n    expect {\n      eval_template(\"<%= lookupvar('myvar') %>\")\n    }.to raise_error(Puppet::ParseError, /undefined method `lookupvar'/)\n  end\n\n  it 'is not available when --tasks is on' do\n    Puppet[:tasks] = true\n    expect {\n      eval_template(\"<%= lookupvar('myvar') %>\")\n    }.to raise_error(Puppet::ParseError, /is only available when compiling a catalog/)\n\n  end\n\n  def eval_template(content)\n    allow(Puppet::FileSystem).to receive(:read_preserve_line_endings).with(\"template\").and_return(content)\n    allow(Puppet::Parser::Files).to receive(:find_template).and_return(\"template\")\n    scope.function_template(['template'])\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/functions/versioncmp_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"the versioncmp function\" do\n  before :each do\n    node     = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n    @scope   = Puppet::Parser::Scope.new(compiler)\n  end\n\n  it \"should exist\" do\n    expect(Puppet::Parser::Functions.function(\"versioncmp\")).to eq(\"function_versioncmp\")\n  end\n\n  it \"should raise an ArgumentError if there is less than 2 arguments\" do\n    expect { @scope.function_versioncmp([\"1.2\"]) }.to raise_error(ArgumentError)\n  end\n\n  it \"should raise an ArgumentError if there is more than 2 arguments\" do\n    expect { @scope.function_versioncmp([\"1.2\", \"2.4.5\", \"3.5.6\"]) }.to raise_error(ArgumentError)\n  end\n\n  it \"should call Puppet::Util::Package.versioncmp (included in scope)\" do\n    expect(Puppet::Util::Package).to receive(:versioncmp).with(\"1.2\", \"1.3\").and_return(-1)\n\n    @scope.function_versioncmp([\"1.2\", \"1.3\"])\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/parser/functions_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Parser::Functions do\n  def callable_functions_from(mod)\n    Class.new { include mod }.new\n  end\n\n  let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet.lookup(:current_environment)) }\n\n  let(:environment) { Puppet::Node::Environment.create(:myenv, []) }\n\n  before do\n    Puppet::Parser::Functions.reset\n  end\n\n  it \"should have a method for returning an environment-specific module\" do\n    expect(Puppet::Parser::Functions.environment_module(environment)).to be_instance_of(Module)\n  end\n\n  describe \"when calling newfunction\" do\n    it \"should create the function in the environment module\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :type => :rvalue) { |args| }\n\n      expect(function_module).to be_method_defined :function_name\n    end\n\n    it \"should warn if the function already exists\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :type => :rvalue) { |args| }\n      expect(Puppet).to receive(:warning)\n\n      Puppet::Parser::Functions.newfunction(\"name\", :type => :rvalue) { |args| }\n    end\n\n    it \"should raise an error if the function type is not correct\" do\n      expect { Puppet::Parser::Functions.newfunction(\"name\", :type => :unknown) { |args| } }.to raise_error Puppet::DevError, \"Invalid statement type :unknown\"\n    end\n\n    it \"instruments the function to profile the execution\" do\n      messages = []\n      Puppet::Util::Profiler.add_profiler(Puppet::Util::Profiler::WallClock.new(proc { |msg| messages << msg }, \"id\"))\n\n      Puppet::Parser::Functions.newfunction(\"name\", :type => :rvalue) { |args| }\n      callable_functions_from(function_module).function_name([])\n\n      expect(messages.first).to match(/Called name/)\n    end\n  end\n\n  describe \"when calling function to test function existence\" do\n    before :each do\n      Puppet[:strict] = :error\n    end\n\n    it \"emits a deprecation warning when loading all 3.x functions\" do\n      allow(Puppet::Parser::Functions.autoloader.delegatee).to receive(:loadall)\n      Puppet::Parser::Functions.autoloader.loadall\n\n      expect(@logs.map(&:to_s)).to include(/The method 'Puppet::Parser::Functions.autoloader#loadall' is deprecated in favor of using 'Scope#call_function/)\n    end\n\n    it \"emits a deprecation warning when loading a single 3.x function\" do\n      allow(Puppet::Parser::Functions.autoloader.delegatee).to receive(:load)\n      Puppet::Parser::Functions.autoloader.load('beatles')\n\n      expect(@logs.map(&:to_s)).to include(/The method 'Puppet::Parser::Functions.autoloader#load\\(\"beatles\"\\)' is deprecated in favor of using 'Scope#call_function'/)\n    end\n\n    it \"emits a deprecation warning when checking if a 3x function is loaded\" do\n      allow(Puppet::Parser::Functions.autoloader.delegatee).to receive(:loaded?).and_return(false)\n      Puppet::Parser::Functions.autoloader.loaded?('beatles')\n\n      expect(@logs.map(&:to_s)).to include(/The method 'Puppet::Parser::Functions.autoloader#loaded\\?\\(\\\"beatles\\\"\\)' is deprecated in favor of using 'Scope#call_function'/)\n    end\n\n    it \"should return false if the function doesn't exist\" do\n      allow(Puppet::Parser::Functions.autoloader.delegatee).to receive(:load)\n\n      expect(Puppet::Parser::Functions.function(\"name\")).to be_falsey\n    end\n\n    it \"should return its name if the function exists\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :type => :rvalue) { |args| }\n\n      expect(Puppet::Parser::Functions.function(\"name\")).to eq(\"function_name\")\n    end\n\n    it \"should try to autoload the function if it doesn't exist yet\" do\n      expect(Puppet::Parser::Functions.autoloader.delegatee).to receive(:load)\n\n      Puppet::Parser::Functions.function(\"name\")\n    end\n\n    it \"combines functions from the root with those from the current environment\" do\n      Puppet.override(:current_environment => Puppet.lookup(:root_environment)) do\n        Puppet::Parser::Functions.newfunction(\"onlyroot\", :type => :rvalue) do |args|\n        end\n      end\n\n      Puppet.override(:current_environment => Puppet::Node::Environment.create(:other, [])) do\n        Puppet::Parser::Functions.newfunction(\"other_env\", :type => :rvalue) do |args|\n        end\n\n        expect(Puppet::Parser::Functions.function(\"onlyroot\")).to eq(\"function_onlyroot\")\n        expect(Puppet::Parser::Functions.function(\"other_env\")).to eq(\"function_other_env\")\n      end\n\n      expect(Puppet::Parser::Functions.function(\"other_env\")).to be_falsey\n    end\n  end\n\n  describe \"when calling function to test arity\" do\n    let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet.lookup(:current_environment)) }\n\n    it \"should raise an error if the function is called with too many arguments\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => 2) { |args| }\n      expect { callable_functions_from(function_module).function_name([1,2,3]) }.to raise_error ArgumentError\n    end\n\n    it \"should raise an error if the function is called with too few arguments\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => 2) { |args| }\n      expect { callable_functions_from(function_module).function_name([1]) }.to raise_error ArgumentError\n    end\n\n    it \"should not raise an error if the function is called with correct number of arguments\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => 2) { |args| }\n      expect { callable_functions_from(function_module).function_name([1,2]) }.to_not raise_error\n    end\n\n    it \"should raise an error if the variable arg function is called with too few arguments\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => -3) { |args| }\n      expect { callable_functions_from(function_module).function_name([1]) }.to raise_error ArgumentError\n    end\n\n    it \"should not raise an error if the variable arg function is called with correct number of arguments\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => -3) { |args| }\n      expect { callable_functions_from(function_module).function_name([1,2]) }.to_not raise_error\n    end\n\n    it \"should not raise an error if the variable arg function is called with more number of arguments\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => -3) { |args| }\n      expect { callable_functions_from(function_module).function_name([1,2,3]) }.to_not raise_error\n    end\n  end\n\n  describe \"::arity\" do\n    it \"returns the given arity of a function\" do\n      Puppet::Parser::Functions.newfunction(\"name\", :arity => 4) { |args| }\n      expect(Puppet::Parser::Functions.arity(:name)).to eq(4)\n    end\n\n    it \"returns -1 if no arity is given\" do\n      Puppet::Parser::Functions.newfunction(\"name\") { |args| }\n      expect(Puppet::Parser::Functions.arity(:name)).to eq(-1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/relationship_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parser/relationship'\n\ndescribe Puppet::Parser::Relationship do\n  before do\n    @source = Puppet::Resource.new(:mytype, \"source\")\n    @target = Puppet::Resource.new(:mytype, \"target\")\n    @extra_resource  = Puppet::Resource.new(:mytype, \"extra\")\n    @extra_resource2 = Puppet::Resource.new(:mytype, \"extra2\")\n    @dep = Puppet::Parser::Relationship.new(@source, @target, :relationship)\n  end\n\n  describe \"when evaluating\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @catalog.add_resource(@source)\n      @catalog.add_resource(@target)\n      @catalog.add_resource(@extra_resource)\n      @catalog.add_resource(@extra_resource2)\n    end\n\n    it \"should fail if the source resource cannot be found\" do\n      @catalog = Puppet::Resource::Catalog.new\n      @catalog.add_resource @target\n      expect { @dep.evaluate(@catalog) }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if the target resource cannot be found\" do\n      @catalog = Puppet::Resource::Catalog.new\n      @catalog.add_resource @source\n      expect { @dep.evaluate(@catalog) }.to raise_error(ArgumentError)\n    end\n\n    it \"should add the target as a 'before' value if the type is 'relationship'\" do\n      @dep.type = :relationship\n      @dep.evaluate(@catalog)\n      expect(@source[:before]).to be_include(\"Mytype[target]\")\n    end\n\n    it \"should add the target as a 'notify' value if the type is 'subscription'\" do\n      @dep.type = :subscription\n      @dep.evaluate(@catalog)\n      expect(@source[:notify]).to be_include(\"Mytype[target]\")\n    end\n\n    it \"should supplement rather than clobber existing relationship values\" do\n      @source[:before] = \"File[/bar]\"\n      @dep.evaluate(@catalog)\n      # this test did not work before. It was appending the resources\n      # together as a string\n      expect(@source[:before].class == Array).to be_truthy\n      expect(@source[:before]).to be_include(\"Mytype[target]\")\n      expect(@source[:before]).to be_include(\"File[/bar]\")\n    end\n\n    it \"should supplement rather than clobber existing resource relationships\" do\n      @source[:before] = @extra_resource\n      @dep.evaluate(@catalog)\n      expect(@source[:before].class == Array).to be_truthy\n      expect(@source[:before]).to be_include(\"Mytype[target]\")\n      expect(@source[:before]).to be_include(@extra_resource)\n    end\n\n    it \"should supplement rather than clobber multiple existing resource relationships\" do\n      @source[:before] = [@extra_resource, @extra_resource2]\n      @dep.evaluate(@catalog)\n      expect(@source[:before].class == Array).to be_truthy\n      expect(@source[:before]).to be_include(\"Mytype[target]\")\n      expect(@source[:before]).to be_include(@extra_resource)\n      expect(@source[:before]).to be_include(@extra_resource2)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/resource/param_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Parser::Resource::Param do\n  it \"has readers for all of the attributes\" do\n    param = Puppet::Parser::Resource::Param.new(:name => 'myparam', :value => 'foo', :file => 'foo.pp', :line => 42)\n\n    expect(param.name).to eq(:myparam)\n    expect(param.value).to eq('foo')\n    expect(param.file).to eq('foo.pp')\n    expect(param.line).to eq(42)\n  end\n\n  context \"parameter validation\" do\n    it \"throws an error when instantiated without a name\" do\n      expect {\n        Puppet::Parser::Resource::Param.new(:value => 'foo')\n      }.to raise_error(Puppet::Error, /'name' is a required option/)\n    end\n\n    it \"does not require a value\" do\n      param = Puppet::Parser::Resource::Param.new(:name => 'myparam')\n\n      expect(param.value).to be_nil\n    end\n\n    it \"includes file/line context in errors\" do\n      expect {\n        Puppet::Parser::Resource::Param.new(:file => 'foo.pp', :line => 42)\n      }.to raise_error(Puppet::Error, /\\(file: foo.pp, line: 42\\)/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/resource_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Parser::Resource do\n  before do\n    environment = Puppet::Node::Environment.create(:testing, [])\n    @node = Puppet::Node.new(\"yaynode\", :environment => environment)\n    @known_resource_types = environment.known_resource_types\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @source = newclass \"\"\n    @scope = @compiler.topscope\n  end\n\n  def mkresource(args = {})\n    args[:source] ||= @source\n    args[:scope] ||= @scope\n\n    params = args[:parameters] || {:one => \"yay\", :three => \"rah\"}\n    if args[:parameters] == :none\n      args.delete(:parameters)\n    elsif not args[:parameters].is_a? Array\n      args[:parameters] = paramify(args[:source], params)\n    end\n\n    Puppet::Parser::Resource.new(\"resource\", \"testing\", args)\n  end\n\n  def param(name, value, source)\n    Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source)\n  end\n\n  def paramify(source, hash)\n    hash.collect do |name, value|\n      Puppet::Parser::Resource::Param.new(\n        :name => name, :value => value, :source => source\n      )\n    end\n  end\n\n  def newclass(name)\n    @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name)\n  end\n\n  def newdefine(name)\n    @known_resource_types.add Puppet::Resource::Type.new(:definition, name)\n  end\n\n  def newnode(name)\n    @known_resource_types.add Puppet::Resource::Type.new(:node, name)\n  end\n\n  it \"should get its environment from its scope\" do\n    scope = double('scope', :source => double(\"source\"))\n    expect(scope).to receive(:environment).and_return(\"foo\").at_least(:once)\n    expect(scope).to receive(:lookupdefaults).and_return({})\n    expect(Puppet::Parser::Resource.new(\"file\", \"whatever\", :scope => scope).environment).to eq(\"foo\")\n  end\n\n  it \"should use the scope's environment as its environment\" do\n    expect(@scope).to receive(:environment).and_return(\"myenv\").at_least(:once)\n    expect(Puppet::Parser::Resource.new(\"file\", \"whatever\", :scope => @scope).environment).to eq(\"myenv\")\n  end\n\n  it \"should be isomorphic if it is builtin and models an isomorphic type\" do\n    expect(Puppet::Type.type(:file)).to receive(:isomorphic?).and_return(true)\n    @resource = expect(Puppet::Parser::Resource.new(\"file\", \"whatever\", :scope => @scope, :source => @source).isomorphic?).to be_truthy\n  end\n\n  it \"should not be isomorphic if it is builtin and models a non-isomorphic type\" do\n    expect(Puppet::Type.type(:file)).to receive(:isomorphic?).and_return(false)\n    @resource = expect(Puppet::Parser::Resource.new(\"file\", \"whatever\", :scope => @scope, :source => @source).isomorphic?).to be_falsey\n  end\n\n  it \"should be isomorphic if it is not builtin\" do\n    newdefine \"whatever\"\n    @resource = expect(Puppet::Parser::Resource.new(\"whatever\", \"whatever\", :scope => @scope, :source => @source).isomorphic?).to be_truthy\n  end\n\n  it \"should have an array-indexing method for retrieving parameter values\" do\n    @resource = mkresource\n    expect(@resource[:one]).to eq(\"yay\")\n  end\n\n  it \"should use a Puppet::Resource for converting to a ral resource\" do\n    trans = double('resource', :to_ral => \"yay\")\n    @resource = mkresource\n    expect(@resource).to receive(:copy_as_resource).and_return(trans)\n    expect(@resource.to_ral).to eq(\"yay\")\n  end\n\n  it \"should be able to use the indexing operator to access parameters\" do\n    resource = Puppet::Parser::Resource.new(\"resource\", \"testing\", :source => \"source\", :scope => @scope)\n    resource[\"foo\"] = \"bar\"\n    expect(resource[\"foo\"]).to eq(\"bar\")\n  end\n\n  it \"should return the title when asked for a parameter named 'title'\" do\n    expect(Puppet::Parser::Resource.new(\"resource\", \"testing\", :source => @source, :scope => @scope)[:title]).to eq(\"testing\")\n  end\n\n  describe \"when initializing\" do\n    before do\n      @arguments = {:scope => @scope}\n    end\n\n    it \"should fail unless hash is specified\" do\n      expect {\n        Puppet::Parser::Resource.new('file', '/my/file', nil)\n      }.to raise_error(ArgumentError, /Resources require a hash as last argument/)\n    end\n\n    it \"should attempt to externalize filepaths via the environment\" do\n      environment = Puppet::Node::Environment.create(:testing, [])\n      expect(environment).to receive(:externalize_path).at_least(:once).and_return(\"foo\")\n      Puppet[:code] = \"notify { 'hello': }\"\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone', environment: environment)\n      notify = catalog.resource('Notify[hello]')\n      expect(notify.file).to eq(\"foo\")\n    end\n\n    it \"should set the reference correctly\" do\n      res = Puppet::Parser::Resource.new(\"resource\", \"testing\", @arguments)\n      expect(res.ref).to eq(\"Resource[testing]\")\n    end\n\n    it \"should be tagged with user tags\" do\n      tags = [ \"tag1\", \"tag2\" ]\n      @arguments[:parameters] = [ param(:tag, tags , :source) ]\n      res = Puppet::Parser::Resource.new(\"resource\", \"testing\", @arguments)\n      expect(res).to be_tagged(\"tag1\")\n      expect(res).to be_tagged(\"tag2\")\n    end\n  end\n\n  describe \"when evaluating\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      source = double('source')\n      allow(source).to receive(:module_name)\n      @scope = Puppet::Parser::Scope.new(@compiler, :source => source)\n      @catalog.add_resource(Puppet::Parser::Resource.new(\"stage\", :main, :scope => @scope))\n    end\n\n    it \"should evaluate the associated AST definition\" do\n      definition = newdefine \"mydefine\"\n      res = Puppet::Parser::Resource.new(\"mydefine\", \"whatever\", :scope => @scope, :source => @source, :catalog => @catalog)\n      expect(definition).to receive(:evaluate_code).with(res)\n\n      res.evaluate\n    end\n\n    it \"should evaluate the associated AST class\" do\n      @class = newclass \"myclass\"\n      res = Puppet::Parser::Resource.new(\"class\", \"myclass\", :scope => @scope, :source => @source, :catalog => @catalog)\n      expect(@class).to receive(:evaluate_code).with(res)\n      res.evaluate\n    end\n\n    it \"should evaluate the associated AST node\" do\n      nodedef = newnode(\"mynode\")\n      res = Puppet::Parser::Resource.new(\"node\", \"mynode\", :scope => @scope, :source => @source, :catalog => @catalog)\n      expect(nodedef).to receive(:evaluate_code).with(res)\n      res.evaluate\n    end\n\n    it \"should add an edge to any specified stage for class resources\" do\n      @compiler.environment.known_resource_types.add Puppet::Resource::Type.new(:hostclass, \"foo\", {})\n\n      other_stage = Puppet::Parser::Resource.new(:stage, \"other\", :scope => @scope, :catalog => @catalog)\n      @compiler.add_resource(@scope, other_stage)\n      resource = Puppet::Parser::Resource.new(:class, \"foo\", :scope => @scope, :catalog => @catalog)\n      resource[:stage] = 'other'\n      @compiler.add_resource(@scope, resource)\n\n      resource.evaluate\n\n      expect(@compiler.catalog.edge?(other_stage, resource)).to be_truthy\n    end\n\n    it \"should fail if an unknown stage is specified\" do\n      @compiler.environment.known_resource_types.add Puppet::Resource::Type.new(:hostclass, \"foo\", {})\n\n      resource = Puppet::Parser::Resource.new(:class, \"foo\", :scope => @scope, :catalog => @catalog)\n      resource[:stage] = 'other'\n\n      expect { resource.evaluate }.to raise_error(ArgumentError, /Could not find stage other specified by/)\n    end\n\n    it \"should add edges from the class resources to the parent's stage if no stage is specified\" do\n      foo_stage = Puppet::Parser::Resource.new(:stage, :foo_stage, :scope => @scope, :catalog => @catalog)\n      @compiler.add_resource(@scope, foo_stage)\n      @compiler.environment.known_resource_types.add Puppet::Resource::Type.new(:hostclass, \"foo\", {})\n      resource = Puppet::Parser::Resource.new(:class, \"foo\", :scope => @scope, :catalog => @catalog)\n      resource[:stage] = 'foo_stage'\n      @compiler.add_resource(@scope, resource)\n\n      resource.evaluate\n\n      expect(@compiler.catalog).to be_edge(foo_stage, resource)\n    end\n\n    it 'should allow a resource reference to be undef' do\n      Puppet[:code] = \"notify { 'hello': message=>'yo', notify => undef }\"\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone')\n      edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]}\n      expect(edges).to include(['Class[main]', 'Notify[hello]'])\n    end\n\n    it 'should evaluate class in the same file without include' do\n      Puppet[:code] = <<-MANIFEST\n        class a($myvar = 'hello') {}\n        class { 'a': myvar => 'goodbye' }\n        notify { $a::myvar: }\n      MANIFEST\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone')\n      expect(catalog.resource('Notify[goodbye]')).to be_a(Puppet::Resource)\n    end\n\n    it \"should allow edges to propagate multiple levels down the scope hierarchy\" do\n      Puppet[:code] = <<-MANIFEST\n        stage { before: before => Stage[main] }\n\n        class alpha {\n          include beta\n        }\n        class beta {\n          include gamma\n        }\n        class gamma { }\n        class { alpha: stage => before }\n      MANIFEST\n\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone')\n\n      # Stringify them to make for easier lookup\n      edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]}\n\n      expect(edges).to include([\"Stage[before]\", \"Class[Alpha]\"])\n      expect(edges).to include([\"Stage[before]\", \"Class[Beta]\"])\n      expect(edges).to include([\"Stage[before]\", \"Class[Gamma]\"])\n    end\n\n    it \"should use the specified stage even if the parent scope specifies one\" do\n      Puppet[:code] = <<-MANIFEST\n        stage { before: before => Stage[main], }\n        stage { after: require => Stage[main], }\n\n        class alpha {\n          class { beta: stage => after }\n        }\n        class beta { }\n        class { alpha: stage => before }\n      MANIFEST\n\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone')\n\n      edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]}\n\n      expect(edges).to include([\"Stage[before]\", \"Class[Alpha]\"])\n      expect(edges).to include([\"Stage[after]\", \"Class[Beta]\"])\n    end\n\n    it \"should add edges from top-level class resources to the main stage if no stage is specified\" do\n      main = @compiler.catalog.resource(:stage, :main)\n      @compiler.environment.known_resource_types.add Puppet::Resource::Type.new(:hostclass, \"foo\", {})\n      resource = Puppet::Parser::Resource.new(:class, \"foo\", :scope => @scope, :catalog => @catalog)\n      @compiler.add_resource(@scope, resource)\n\n      resource.evaluate\n\n      expect(@compiler.catalog).to be_edge(main, resource)\n    end\n\n    it 'should assign default value to generated resource' do\n      Puppet[:code] = <<-PUPPET\n        define one($var) {\n          notify { \"${var} says hello\": }\n        }\n        \n        define two($x = $title) {\n          One {\n            var => $x\n          }\n          one { a: }\n          one { b: var => 'bill'}\n        }\n        two { 'bob': }\n      PUPPET\n\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone')\n      edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]}\n\n      expect(edges).to include(['One[a]', 'Notify[bob says hello]'])\n      expect(edges).to include(['One[b]', 'Notify[bill says hello]'])\n    end\n\n    it 'should override default value with new value' do\n      Puppet[:code] = <<-PUPPET.unindent\n        class foo {\n          File {\n            ensure => file,\n            mode   => '644',\n            owner  => 'root',\n            group  => 'root',\n          }\n        \n          file { '/tmp/foo':\n            ensure  => directory\n          }\n        \n          File['/tmp/foo'] { mode => '0755' }\n        }\n        include foo\n        PUPPET\n\n      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone')\n      file = catalog.resource('File[/tmp/foo]')\n      expect(file).to be_a(Puppet::Resource)\n      expect(file['mode']).to eql('0755')\n    end\n  end\n\n  describe 'when evaluating resource defaults' do\n    let(:resource) { Puppet::Parser::Resource.new('file', 'whatever', :scope => @scope, :source => @source) }\n\n    it 'should add all defaults available from the scope' do\n      expect(@scope).to receive(:lookupdefaults).with('File').and_return(:owner => param(:owner, 'default', @source))\n\n      expect(resource[:owner]).to eq('default')\n    end\n\n    it 'should not replace existing parameters with defaults' do\n      expect(@scope).to receive(:lookupdefaults).with('File').and_return(:owner => param(:owner, 'replaced', @source))\n      r = Puppet::Parser::Resource.new('file', 'whatever', :scope => @scope, :source => @source, :parameters => [ param(:owner, 'oldvalue', @source) ])\n      expect(r[:owner]).to eq('oldvalue')\n    end\n\n    it 'should override defaults with new parameters' do\n      expect(@scope).to receive(:lookupdefaults).with('File').and_return(:owner => param(:owner, 'replaced', @source))\n\n      resource.set_parameter(:owner, 'newvalue')\n      expect(resource[:owner]).to eq('newvalue')\n    end\n\n    it 'should add a copy of each default, rather than the actual default parameter instance' do\n      newparam = param(:owner, 'default', @source)\n      other = newparam.dup\n      other.value = \"other\"\n      expect(newparam).to receive(:dup).and_return(other)\n      expect(@scope).to receive(:lookupdefaults).with('File').and_return(:owner => newparam)\n\n      expect(resource[:owner]).to eq('other')\n    end\n\n    it \"should tag with value of default parameter named 'tag'\" do\n      expect(@scope).to receive(:lookupdefaults).with('File').and_return(:tag => param(:tag, 'the_tag', @source))\n\n      expect(resource.tags).to include('the_tag')\n    end\n  end\n\n  describe \"when finishing\" do\n    before do\n      @resource = Puppet::Parser::Resource.new(\"file\", \"whatever\", :scope => @scope, :source => @source)\n    end\n\n    it \"should do nothing if it has already been finished\" do\n      @resource.finish\n      expect(@resource).not_to receive(:add_scope_tags)\n      @resource.finish\n    end\n\n    it \"converts parameters with Sensitive values to unwrapped values and metadata\" do\n      @resource[:content] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(\"hunter2\")\n      @resource.finish\n      expect(@resource[:content]).to eq \"hunter2\"\n      expect(@resource.sensitive_parameters).to eq [:content]\n    end\n  end\n\n  describe \"when being tagged\" do\n    before do\n      @scope_resource = double('scope_resource', :tags => %w{srone srtwo})\n      allow(@scope).to receive(:resource).and_return(@scope_resource)\n      @resource = Puppet::Parser::Resource.new(\"file\", \"yay\", :scope => @scope, :source => double('source'))\n    end\n\n    it \"should get tagged with the resource type\" do\n      expect(@resource.tags).to be_include(\"file\")\n    end\n\n    it \"should get tagged with the title\" do\n      expect(@resource.tags).to be_include(\"yay\")\n    end\n\n    it \"should get tagged with each name in the title if the title is a qualified class name\" do\n      resource = Puppet::Parser::Resource.new(\"file\", \"one::two\", :scope => @scope, :source => double('source'))\n      expect(resource.tags).to be_include(\"one\")\n      expect(resource.tags).to be_include(\"two\")\n    end\n\n    it \"should get tagged with each name in the type if the type is a qualified class name\" do\n      resource = Puppet::Parser::Resource.new(\"one::two\", \"whatever\", :scope => @scope, :source => double('source'))\n      expect(resource.tags).to be_include(\"one\")\n      expect(resource.tags).to be_include(\"two\")\n    end\n\n    it \"should not get tagged with non-alphanumeric titles\" do\n      resource = Puppet::Parser::Resource.new(\"file\", \"this is a test\", :scope => @scope, :source => double('source'))\n      expect(resource.tags).not_to be_include(\"this is a test\")\n    end\n\n    it \"should fail on tags containing '*' characters\" do\n      expect { @resource.tag(\"bad*tag\") }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should fail on tags starting with '-' characters\" do\n      expect { @resource.tag(\"-badtag\") }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should fail on tags containing ' ' characters\" do\n      expect { @resource.tag(\"bad tag\") }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should allow alpha tags\" do\n      expect { @resource.tag(\"good_tag\") }.to_not raise_error\n    end\n  end\n\n  describe \"when merging overrides\" do\n    def resource_type(name)\n      double(name, :child_of? => false)\n    end\n\n    before do\n      @source = resource_type(\"source1\")\n      @resource = mkresource :source => @source\n      @override = mkresource :source => @source\n    end\n\n    it \"should fail when the override was not created by a parent class\" do\n      @override.source = resource_type(\"source2\")\n      expect(@override.source).to receive(:child_of?).with(@source).and_return(false)\n      expect { @resource.merge(@override) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should succeed when the override was created in the current scope\" do\n      @source3 = resource_type(\"source3\")\n      @resource.source = @source3\n      @override.source = @resource.source\n      expect(@override.source).not_to receive(:child_of?).with(@source3)\n      params = {:a => :b, :c => :d}\n      expect(@override).to receive(:parameters).and_return(params)\n      expect(@resource).to receive(:override_parameter).with(:b)\n      expect(@resource).to receive(:override_parameter).with(:d)\n      @resource.merge(@override)\n    end\n\n    it \"should succeed when a parent class created the override\" do\n      @source3 = resource_type(\"source3\")\n      @resource.source = @source3\n      @override.source = resource_type(\"source4\")\n      expect(@override.source).to receive(:child_of?).with(@source3).and_return(true)\n      params = {:a => :b, :c => :d}\n      expect(@override).to receive(:parameters).and_return(params)\n      expect(@resource).to receive(:override_parameter).with(:b)\n      expect(@resource).to receive(:override_parameter).with(:d)\n      @resource.merge(@override)\n    end\n\n    it \"should add new parameters when the parameter is not set\" do\n      allow(@source).to receive(:child_of?).and_return(true)\n      @override.set_parameter(:testing, \"value\")\n      @resource.merge(@override)\n\n      expect(@resource[:testing]).to eq(\"value\")\n    end\n\n    it \"should replace existing parameter values\" do\n      allow(@source).to receive(:child_of?).and_return(true)\n      @resource.set_parameter(:testing, \"old\")\n      @override.set_parameter(:testing, \"value\")\n\n      @resource.merge(@override)\n\n      expect(@resource[:testing]).to eq(\"value\")\n    end\n\n    it \"should add values to the parameter when the override was created with the '+>' syntax\" do\n      allow(@source).to receive(:child_of?).and_return(true)\n      param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => \"testing\", :source => @resource.source)\n      param.add = true\n\n      @override.set_parameter(param)\n\n      @resource.set_parameter(:testing, \"other\")\n\n      @resource.merge(@override)\n\n      expect(@resource[:testing]).to eq(%w{other testing})\n    end\n\n    it \"should not merge parameter values when multiple resources are overriden with '+>' at once \" do\n      @resource_2 = mkresource :source => @source\n\n      @resource.  set_parameter(:testing, \"old_val_1\")\n      @resource_2.set_parameter(:testing, \"old_val_2\")\n\n      allow(@source).to receive(:child_of?).and_return(true)\n      param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => \"new_val\", :source => @resource.source)\n      param.add = true\n      @override.set_parameter(param)\n\n      @resource.  merge(@override)\n      @resource_2.merge(@override)\n\n      expect(@resource  [:testing]).to eq(%w{old_val_1 new_val})\n      expect(@resource_2[:testing]).to eq(%w{old_val_2 new_val})\n    end\n\n    it \"should promote tag overrides to real tags\" do\n      allow(@source).to receive(:child_of?).and_return(true)\n      param = Puppet::Parser::Resource::Param.new(:name => :tag, :value => \"testing\", :source => @resource.source)\n\n      @override.set_parameter(param)\n\n      @resource.merge(@override)\n\n      expect(@resource.tagged?(\"testing\")).to be_truthy\n    end\n\n  end\n\n  it \"should be able to be converted to a normal resource\" do\n    @source = double('scope', :name => \"myscope\")\n    @resource = mkresource :source => @source\n    expect(@resource).to respond_to(:copy_as_resource)\n  end\n\n  describe \"when being converted to a resource\" do\n    before do\n      @parser_resource = mkresource :scope => @scope, :parameters => {:foo => \"bar\", :fee => \"fum\"}\n    end\n\n    it \"should create an instance of Puppet::Resource\" do\n      expect(@parser_resource.copy_as_resource).to be_instance_of(Puppet::Resource)\n    end\n\n    it \"should set the type correctly on the Puppet::Resource\" do\n      expect(@parser_resource.copy_as_resource.type).to eq(@parser_resource.type)\n    end\n\n    it \"should set the title correctly on the Puppet::Resource\" do\n      expect(@parser_resource.copy_as_resource.title).to eq(@parser_resource.title)\n    end\n\n    it \"should copy over all of the parameters\" do\n      result = @parser_resource.copy_as_resource.to_hash\n\n      # The name will be in here, also.\n      expect(result[:foo]).to eq(\"bar\")\n      expect(result[:fee]).to eq(\"fum\")\n    end\n\n    it \"should copy over the tags\" do\n      @parser_resource.tag \"foo\"\n      @parser_resource.tag \"bar\"\n\n      expect(@parser_resource.copy_as_resource.tags).to eq(@parser_resource.tags)\n    end\n\n    it \"should copy over the line\" do\n      @parser_resource.line = 40\n      expect(@parser_resource.copy_as_resource.line).to eq(40)\n    end\n\n    it \"should copy over the file\" do\n      @parser_resource.file = \"/my/file\"\n      expect(@parser_resource.copy_as_resource.file).to eq(\"/my/file\")\n    end\n\n    it \"should copy over the 'exported' value\" do\n      @parser_resource.exported = true\n      expect(@parser_resource.copy_as_resource.exported).to be_truthy\n    end\n\n    it \"should copy over the 'virtual' value\" do\n      @parser_resource.virtual = true\n      expect(@parser_resource.copy_as_resource.virtual).to be_truthy\n    end\n\n    it \"should convert any parser resource references to Puppet::Resource instances\" do\n      ref = Puppet::Resource.new(\"file\", \"/my/file\")\n      @parser_resource = mkresource :source => @source, :parameters => {:foo => \"bar\", :fee => ref}\n      result = @parser_resource.copy_as_resource\n      expect(result[:fee]).to eq(Puppet::Resource.new(:file, \"/my/file\"))\n    end\n\n    it \"should convert any parser resource references to Puppet::Resource instances even if they are in an array\" do\n      ref = Puppet::Resource.new(\"file\", \"/my/file\")\n      @parser_resource = mkresource :source => @source, :parameters => {:foo => \"bar\", :fee => [\"a\", ref]}\n      result = @parser_resource.copy_as_resource\n      expect(result[:fee]).to eq([\"a\", Puppet::Resource.new(:file, \"/my/file\")])\n    end\n\n    it \"should convert any parser resource references to Puppet::Resource instances even if they are in an array of array, and even deeper\" do\n      ref1 = Puppet::Resource.new(\"file\", \"/my/file1\")\n      ref2 = Puppet::Resource.new(\"file\", \"/my/file2\")\n      @parser_resource = mkresource :source => @source, :parameters => {:foo => \"bar\", :fee => [\"a\", [ref1,ref2]]}\n      result = @parser_resource.copy_as_resource\n      expect(result[:fee]).to eq([\"a\", Puppet::Resource.new(:file, \"/my/file1\"), Puppet::Resource.new(:file, \"/my/file2\")])\n    end\n\n    it \"should fail if the same param is declared twice\" do\n      expect do\n        @parser_resource = mkresource :source => @source, :parameters => [\n          Puppet::Parser::Resource::Param.new(\n            :name => :foo, :value => \"bar\", :source => @source\n          ),\n          Puppet::Parser::Resource::Param.new(\n            :name => :foo, :value => \"baz\", :source => @source\n          )\n        ]\n      end.to raise_error(Puppet::ParseError)\n    end\n  end\n\n  describe \"when setting parameters\" do\n    before do\n      @source = newclass \"foobar\"\n      @resource = Puppet::Parser::Resource.new :foo, \"bar\", :scope => @scope, :source => @source\n    end\n\n    it \"should accept Param instances and add them to the parameter list\" do\n      param = Puppet::Parser::Resource::Param.new :name => \"foo\", :value => \"bar\", :source => @source\n      @resource.set_parameter(param)\n      expect(@resource[\"foo\"]).to eq(\"bar\")\n    end\n\n    it \"should allow parameters to be set to 'false'\" do\n      @resource.set_parameter(\"myparam\", false)\n      expect(@resource[\"myparam\"]).to be_falsey\n    end\n\n    it \"should use its source when provided a parameter name and value\" do\n      @resource.set_parameter(\"myparam\", \"myvalue\")\n      expect(@resource[\"myparam\"]).to eq(\"myvalue\")\n    end\n  end\n\n  # part of #629 -- the undef keyword.  Make sure 'undef' params get skipped.\n  it \"should not include 'undef' parameters when converting itself to a hash\" do\n    resource = Puppet::Parser::Resource.new \"file\", \"/tmp/testing\", :source => double(\"source\"), :scope => @scope\n    resource[:owner] = :undef\n    resource[:mode] = \"755\"\n    expect(resource.to_hash[:owner]).to be_nil\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/scope_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/scope'\n\ndescribe Puppet::Parser::Scope do\n  include PuppetSpec::Scope\n\n  before :each do\n    @scope = Puppet::Parser::Scope.new(\n      Puppet::Parser::Compiler.new(Puppet::Node.new(\"foo\"))\n    )\n    @scope.source = Puppet::Resource::Type.new(:node, :foo)\n    @topscope = @scope.compiler.topscope\n    @scope.parent = @topscope\n  end\n\n  describe \"create_test_scope_for_node\" do\n    let(:node_name) { \"node_name_foo\" }\n    let(:scope) { create_test_scope_for_node(node_name) }\n\n    it \"should be a kind of Scope\" do\n      expect(scope).to be_a_kind_of(Puppet::Parser::Scope)\n    end\n    it \"should set the source to a node resource\" do\n      expect(scope.source).to be_a_kind_of(Puppet::Resource::Type)\n    end\n    it \"should have a compiler\" do\n      expect(scope.compiler).to be_a_kind_of(Puppet::Parser::Compiler)\n    end\n    it \"should set the parent to the compiler topscope\" do\n      expect(scope.parent).to be(scope.compiler.topscope)\n    end\n  end\n\n  it \"should generate a simple string when inspecting a scope\" do\n    expect(@scope.inspect).to eq(\"Scope()\")\n  end\n\n  it \"should generate a simple string when inspecting a scope with a resource\" do\n    @scope.resource=\"foo::bar\"\n    expect(@scope.inspect).to eq(\"Scope(foo::bar)\")\n  end\n\n  it \"should generate a path if there is one on the puppet stack\" do\n    result = Puppet::Pops::PuppetStack.stack('/tmp/kansas.pp', 42, @scope, 'inspect', [])\n    expect(result).to eq(\"Scope(/tmp/kansas.pp, 42)\")\n  end\n\n  it \"should generate an <env> shortened path if path points into the environment\" do\n    env_path = @scope.environment.configuration.path_to_env\n    mocked_path = File.join(env_path, 'oz.pp')\n    result = Puppet::Pops::PuppetStack.stack(mocked_path, 42, @scope, 'inspect', [])\n\n    expect(result).to eq(\"Scope(<env>/oz.pp, 42)\")\n  end\n\n  it \"should generate a <module> shortened path if path points into a module\" do\n    mocked_path = File.join(@scope.environment.full_modulepath[0], 'mymodule', 'oz.pp')\n    result = Puppet::Pops::PuppetStack.stack(mocked_path, 42, @scope, 'inspect', [])\n    expect(result).to eq(\"Scope(<module>/mymodule/oz.pp, 42)\")\n  end\n\n  it \"should return a scope for use in a test harness\" do\n    expect(create_test_scope_for_node(\"node_name_foo\")).to be_a_kind_of(Puppet::Parser::Scope)\n  end\n\n  it \"should be able to retrieve class scopes by name\" do\n    @scope.class_set \"myname\", \"myscope\"\n    expect(@scope.class_scope(\"myname\")).to eq(\"myscope\")\n  end\n\n  it \"should be able to retrieve class scopes by object\" do\n    klass = double('ast_class')\n    expect(klass).to receive(:name).and_return(\"myname\")\n    @scope.class_set \"myname\", \"myscope\"\n    expect(@scope.class_scope(klass)).to eq(\"myscope\")\n  end\n\n  it \"should be able to retrieve its parent module name from the source of its parent type\" do\n    @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo, :module_name => \"foo\")\n\n    expect(@scope.parent_module_name).to eq(\"foo\")\n  end\n\n  it \"should return a nil parent module name if it has no parent\" do\n    expect(@topscope.parent_module_name).to be_nil\n  end\n\n  it \"should return a nil parent module name if its parent has no source\" do\n    expect(@scope.parent_module_name).to be_nil\n  end\n\n  it \"should get its environment from its compiler\" do\n    env = Puppet::Node::Environment.create(:testing, [])\n    compiler = double('compiler', :environment => env, :is_a? => true)\n    scope = Puppet::Parser::Scope.new(compiler)\n    expect(scope.environment).to equal(env)\n  end\n\n  it \"should fail if no compiler is supplied\" do\n    expect {\n      Puppet::Parser::Scope.new\n    }.to raise_error(ArgumentError, /wrong number of arguments/)\n  end\n\n  it \"should fail if something that isn't a compiler is supplied\" do\n    expect {\n      Puppet::Parser::Scope.new(nil)\n    }.to raise_error(Puppet::DevError, /you must pass a compiler instance/)\n  end\n\n  describe \"when custom functions are called\" do\n    let(:env) { Puppet::Node::Environment.create(:testing, []) }\n    let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new('foo', :environment => env)) }\n    let(:scope) { Puppet::Parser::Scope.new(compiler) }\n\n    it \"calls methods prefixed with function_ as custom functions\" do\n      expect(scope.function_sprintf([\"%b\", 123])).to eq(\"1111011\")\n    end\n\n    it \"raises an error when arguments are not passed in an Array\" do\n      expect do\n        scope.function_sprintf(\"%b\", 123)\n      end.to raise_error ArgumentError, /custom functions must be called with a single array that contains the arguments/\n    end\n\n    it \"raises an error on subsequent calls when arguments are not passed in an Array\" do\n      scope.function_sprintf([\"first call\"])\n\n      expect do\n        scope.function_sprintf(\"%b\", 123)\n      end.to raise_error ArgumentError, /custom functions must be called with a single array that contains the arguments/\n    end\n\n    it \"raises NoMethodError when the not prefixed\" do\n      expect { scope.sprintf([\"%b\", 123]) }.to raise_error(NoMethodError)\n    end\n\n    it \"raises NoMethodError when prefixed with function_ but it doesn't exist\" do\n      expect { scope.function_fake_bs(['cows']) }.to raise_error(NoMethodError)\n    end\n  end\n\n  describe \"when initializing\" do\n    it \"should extend itself with its environment's Functions module as well as the default\" do\n      env = Puppet::Node::Environment.create(:myenv, [])\n      root = Puppet.lookup(:root_environment)\n      compiler = double('compiler', :environment => env, :is_a? => true)\n\n      scope = Puppet::Parser::Scope.new(compiler)\n      expect(scope.singleton_class.ancestors).to be_include(Puppet::Parser::Functions.environment_module(env))\n      expect(scope.singleton_class.ancestors).to be_include(Puppet::Parser::Functions.environment_module(root))\n    end\n\n    it \"should extend itself with the default Functions module if its environment is the default\" do\n      root     = Puppet.lookup(:root_environment)\n      node     = Puppet::Node.new('localhost')\n      compiler = Puppet::Parser::Compiler.new(node)\n      scope    = Puppet::Parser::Scope.new(compiler)\n      expect(scope.singleton_class.ancestors).to be_include(Puppet::Parser::Functions.environment_module(root))\n    end\n  end\n\n  describe \"when looking up a variable\" do\n    before :each do\n      Puppet[:strict] = :warning\n    end\n\n    it \"should support :lookupvar and :setvar for backward compatibility\" do\n      @scope.setvar(\"var\", \"yep\")\n      expect(@scope.lookupvar(\"var\")).to eq(\"yep\")\n    end\n\n    it \"should fail if invoked with a non-string name\" do\n      expect { @scope[:foo] }.to raise_error(Puppet::ParseError, /Scope variable name .* not a string/)\n      expect { @scope[:foo] = 12 }.to raise_error(Puppet::ParseError, /Scope variable name .* not a string/)\n    end\n\n    it \"should return nil for unset variables when --strict variables is not in effect\" do\n      expect(@scope[\"var\"]).to be_nil\n    end\n\n    it \"answers exist? with boolean false for non existing variables\" do\n      expect(@scope.exist?(\"var\")).to be(false)\n    end\n\n    it \"answers exist? with boolean false for non existing variables\" do\n      @scope[\"var\"] = \"yep\"\n      expect(@scope.exist?(\"var\")).to be(true)\n    end\n\n    it \"should be able to look up values\" do\n      @scope[\"var\"] = \"yep\"\n      expect(@scope[\"var\"]).to eq(\"yep\")\n    end\n\n    it \"should be able to look up hashes\" do\n      @scope[\"var\"] = {\"a\" => \"b\"}\n      expect(@scope[\"var\"]).to eq({\"a\" => \"b\"})\n    end\n\n    it \"should be able to look up variables in parent scopes\" do\n      @topscope[\"var\"] = \"parentval\"\n      expect(@scope[\"var\"]).to eq(\"parentval\")\n    end\n\n    it \"should prefer its own values to parent values\" do\n      @topscope[\"var\"] = \"parentval\"\n      @scope[\"var\"] = \"childval\"\n      expect(@scope[\"var\"]).to eq(\"childval\")\n    end\n\n    it \"should be able to detect when variables are set\" do\n      @scope[\"var\"] = \"childval\"\n      expect(@scope).to be_include(\"var\")\n    end\n\n    it \"does not allow changing a set value\" do\n      @scope[\"var\"] = \"childval\"\n      expect {\n        @scope[\"var\"] = \"change\"\n      }.to raise_error(Puppet::Error, \"Cannot reassign variable '$var'\")\n    end\n\n    it \"should be able to detect when variables are not set\" do\n      expect(@scope).not_to be_include(\"var\")\n    end\n\n    it \"warns and return nil for non found unqualified variable\" do\n      expect(Puppet).to receive(:warn_once)\n      expect(@scope[\"santa_clause\"]).to be_nil\n    end\n\n    it \"warns once for a non found variable\" do\n      expect(Puppet).to receive(:send_log).with(:warning, be_a(String)).once\n      expect([@scope[\"santa_claus\"],@scope[\"santa_claus\"]]).to eq([nil, nil])\n    end\n\n    it \"warns and return nil for non found qualified variable\" do\n      expect(Puppet).to receive(:warn_once)\n      expect(@scope[\"north_pole::santa_clause\"]).to be_nil\n    end\n\n    it \"does not warn when a numeric variable is missing - they always exist\" do\n      expect(Puppet).not_to receive(:warn_once)\n      expect(@scope[\"1\"]).to be_nil\n    end\n\n    describe \"and the variable is qualified\" do\n      before :each do\n        @known_resource_types = @scope.environment.known_resource_types\n\n        node      = Puppet::Node.new('localhost')\n        @compiler = Puppet::Parser::Compiler.new(node)\n      end\n\n      def newclass(name)\n        @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name)\n      end\n\n      def create_class_scope(name)\n        klass = newclass(name)\n\n        catalog = Puppet::Resource::Catalog.new\n        catalog.add_resource(Puppet::Parser::Resource.new(\"stage\", :main, :scope => Puppet::Parser::Scope.new(@compiler)))\n\n        Puppet::Parser::Resource.new(\"class\", name, :scope => @scope, :source => double('source'), :catalog => catalog).evaluate\n\n        @scope.class_scope(klass)\n      end\n\n      it \"should be able to look up explicitly fully qualified variables from compiler's top scope\" do\n        expect(Puppet).not_to receive(:deprecation_warning)\n        other_scope = @scope.compiler.topscope\n\n        other_scope[\"othervar\"] = \"otherval\"\n\n        expect(@scope[\"::othervar\"]).to eq(\"otherval\")\n      end\n\n      it \"should be able to look up explicitly fully qualified variables from other scopes\" do\n        expect(Puppet).not_to receive(:deprecation_warning)\n        other_scope = create_class_scope(\"other\")\n\n        other_scope[\"var\"] = \"otherval\"\n\n        expect(@scope[\"::other::var\"]).to eq(\"otherval\")\n      end\n\n      it \"should be able to look up deeply qualified variables\" do\n        expect(Puppet).not_to receive(:deprecation_warning)\n        other_scope = create_class_scope(\"other::deep::klass\")\n\n        other_scope[\"var\"] = \"otherval\"\n\n        expect(@scope[\"other::deep::klass::var\"]).to eq(\"otherval\")\n      end\n\n      it \"should return nil for qualified variables that cannot be found in other classes\" do\n        create_class_scope(\"other::deep::klass\")\n\n        expect(@scope[\"other::deep::klass::var\"]).to be_nil\n      end\n\n      it \"should warn and return nil for qualified variables whose classes have not been evaluated\" do\n        newclass(\"other::deep::klass\")\n        expect(Puppet).to receive(:warn_once)\n        expect(@scope[\"other::deep::klass::var\"]).to be_nil\n      end\n\n      it \"should warn and return nil for qualified variables whose classes do not exist\" do\n        expect(Puppet).to receive(:warn_once)\n        expect(@scope[\"other::deep::klass::var\"]).to be_nil\n      end\n\n      it \"should return nil when asked for a non-string qualified variable from a class that does not exist\" do\n        expect(@scope[\"other::deep::klass::var\"]).to be_nil\n      end\n\n      it \"should return nil when asked for a non-string qualified variable from a class that has not been evaluated\" do\n        allow(@scope).to receive(:warning)\n        newclass(\"other::deep::klass\")\n        expect(@scope[\"other::deep::klass::var\"]).to be_nil\n      end\n    end\n\n    context \"and strict_variables is true\" do\n      before(:each) do\n        Puppet[:strict_variables] = true\n      end\n\n      it \"should throw a symbol when unknown variable is looked up\" do\n        expect { @scope['john_doe'] }.to throw_symbol(:undefined_variable)\n      end\n\n      it \"should throw a symbol when unknown qualified variable is looked up\" do\n        expect { @scope['nowhere::john_doe'] }.to throw_symbol(:undefined_variable)\n      end\n\n      it \"should not raise an error when built in variable is looked up\" do\n        expect { @scope['caller_module_name'] }.to_not raise_error\n        expect { @scope['module_name'] }.to_not raise_error\n      end\n    end\n\n    context \"and strict_variables is false and --strict=off\" do\n      before(:each) do\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :off\n      end\n\n      it \"should not error when unknown variable is looked up and produce nil\" do\n        expect(@scope['john_doe']).to be_nil\n      end\n\n      it \"should not error when unknown qualified variable is looked up and produce nil\" do\n        expect(@scope['nowhere::john_doe']).to be_nil\n      end\n    end\n\n    context \"and strict_variables is false and --strict=warning\" do\n      before(:each) do\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :warning\n      end\n\n      it \"should not error when unknown variable is looked up\" do\n        expect(@scope['john_doe']).to be_nil\n      end\n\n      it \"should not error when unknown qualified variable is looked up\" do\n        expect(@scope['nowhere::john_doe']).to be_nil\n      end\n    end\n\n    context \"and strict_variables is false and --strict=error\" do\n      before(:each) do\n        Puppet[:strict_variables] = false\n        Puppet[:strict] = :error\n      end\n\n      it \"should raise error when unknown variable is looked up\" do\n        expect { @scope['john_doe'] }.to raise_error(/Undefined variable/)\n      end\n\n      it \"should not throw a symbol when unknown qualified variable is looked up\" do\n        expect { @scope['nowhere::john_doe'] }.to raise_error(/Undefined variable/)\n      end\n    end\n  end\n\n  describe \"when calling number?\" do\n    it \"should return nil if called with anything not a number\" do\n      expect(Puppet::Parser::Scope.number?([2])).to be_nil\n    end\n\n    it \"should return a Integer for an Integer\" do\n      expect(Puppet::Parser::Scope.number?(2)).to be_a(Integer)\n    end\n\n    it \"should return a Float for a Float\" do\n      expect(Puppet::Parser::Scope.number?(2.34)).to be_an_instance_of(Float)\n    end\n\n    it \"should return 234 for '234'\" do\n      expect(Puppet::Parser::Scope.number?(\"234\")).to eq(234)\n    end\n\n    it \"should return nil for 'not a number'\" do\n      expect(Puppet::Parser::Scope.number?(\"not a number\")).to be_nil\n    end\n\n    it \"should return 23.4 for '23.4'\" do\n      expect(Puppet::Parser::Scope.number?(\"23.4\")).to eq(23.4)\n    end\n\n    it \"should return 23.4e13 for '23.4e13'\" do\n      expect(Puppet::Parser::Scope.number?(\"23.4e13\")).to eq(23.4e13)\n    end\n\n    it \"should understand negative numbers\" do\n      expect(Puppet::Parser::Scope.number?(\"-234\")).to eq(-234)\n    end\n\n    it \"should know how to convert exponential float numbers ala '23e13'\" do\n      expect(Puppet::Parser::Scope.number?(\"23e13\")).to eq(23e13)\n    end\n\n    it \"should understand hexadecimal numbers\" do\n      expect(Puppet::Parser::Scope.number?(\"0x234\")).to eq(0x234)\n    end\n\n    it \"should understand octal numbers\" do\n      expect(Puppet::Parser::Scope.number?(\"0755\")).to eq(0755)\n    end\n\n    it \"should return nil on malformed integers\" do\n      expect(Puppet::Parser::Scope.number?(\"0.24.5\")).to be_nil\n    end\n\n    it \"should convert strings with leading 0 to integer if they are not octal\" do\n      expect(Puppet::Parser::Scope.number?(\"0788\")).to eq(788)\n    end\n\n    it \"should convert strings of negative integers\" do\n      expect(Puppet::Parser::Scope.number?(\"-0788\")).to eq(-788)\n    end\n\n    it \"should return nil on malformed hexadecimal numbers\" do\n      expect(Puppet::Parser::Scope.number?(\"0x89g\")).to be_nil\n    end\n  end\n\n  describe \"when using ephemeral variables\" do\n    it \"should store the variable value\" do\n      @scope.set_match_data({1 => :value})\n      expect(@scope[\"1\"]).to eq(:value)\n    end\n\n    it \"should raise an error when setting numerical variable\" do\n      expect {\n        @scope.setvar(\"1\", :value3, :ephemeral => true)\n      }.to raise_error(Puppet::ParseError, /Cannot assign to a numeric match result variable/)\n    end\n\n    describe \"with more than one level\" do\n      it \"should prefer latest ephemeral scopes\" do\n        @scope.set_match_data({0 => :earliest})\n        @scope.new_ephemeral\n        @scope.set_match_data({0 => :latest})\n        expect(@scope[\"0\"]).to eq(:latest)\n      end\n\n      it \"should be able to report the current level\" do\n        expect(@scope.ephemeral_level).to eq(1)\n        @scope.new_ephemeral\n        expect(@scope.ephemeral_level).to eq(2)\n      end\n\n      it \"should not check presence of an ephemeral variable across multiple levels\" do\n        @scope.new_ephemeral\n        @scope.set_match_data({1 => :value1})\n        @scope.new_ephemeral\n        @scope.set_match_data({0 => :value2})\n        @scope.new_ephemeral\n        expect(@scope.include?(\"1\")).to be_falsey\n      end\n\n      it \"should return false when an ephemeral variable doesn't exist in any ephemeral scope\" do\n        @scope.new_ephemeral\n        @scope.set_match_data({1 => :value1})\n        @scope.new_ephemeral\n        @scope.set_match_data({0 => :value2})\n        @scope.new_ephemeral\n        expect(@scope.include?(\"2\")).to be_falsey\n      end\n\n      it \"should not get ephemeral values from earlier scope when not in later\" do\n        @scope.set_match_data({1 => :value1})\n        @scope.new_ephemeral\n        @scope.set_match_data({0 => :value2})\n        expect(@scope.include?(\"1\")).to be_falsey\n      end\n\n      describe \"when using a guarded scope\" do\n        it \"should remove ephemeral scopes up to this level\" do\n          @scope.set_match_data({1 => :value1})\n          @scope.new_ephemeral\n          @scope.set_match_data({1 => :value2})\n          @scope.with_guarded_scope do\n            @scope.new_ephemeral\n            @scope.set_match_data({1 => :value3})\n          end\n          expect(@scope[\"1\"]).to eq(:value2)\n        end\n      end\n    end\n  end\n\n  context \"when using ephemeral as local scope\" do\n    it \"should store all variables in local scope\" do\n      @scope.new_ephemeral true\n      @scope.setvar(\"apple\", :fruit)\n      expect(@scope[\"apple\"]).to eq(:fruit)\n    end\n\n    it 'should store an undef in local scope and let it override parent scope' do\n      @scope['cloaked'] = 'Cloak me please'\n      @scope.new_ephemeral(true)\n      @scope['cloaked'] = nil\n      expect(@scope['cloaked']).to eq(nil)\n    end\n\n    it \"should be created from a hash\" do\n      @scope.ephemeral_from({ \"apple\" => :fruit, \"strawberry\" => :berry})\n      expect(@scope[\"apple\"]).to eq(:fruit)\n      expect(@scope[\"strawberry\"]).to eq(:berry)\n    end\n  end\n\n  describe \"when setting ephemeral vars from matches\" do\n    before :each do\n      @match = double('match', :is_a? => true)\n      allow(@match).to receive(:[]).with(0).and_return(\"this is a string\")\n      allow(@match).to receive(:captures).and_return([])\n      allow(@scope).to receive(:setvar)\n    end\n\n    it \"should accept only MatchData\" do\n      expect {\n        @scope.ephemeral_from(\"match\")\n      }.to raise_error(ArgumentError, /Invalid regex match data/)\n    end\n\n    it \"should set $0 with the full match\" do\n      # This is an internal impl detail test\n      expect(@scope).to receive(:new_match_scope) do |arg|\n        expect(arg[0]).to eq(\"this is a string\")\n      end\n      @scope.ephemeral_from(@match)\n    end\n\n    it \"should set every capture as ephemeral var\" do\n      # This is an internal impl detail test\n      allow(@match).to receive(:[]).with(1).and_return(:capture1)\n      allow(@match).to receive(:[]).with(2).and_return(:capture2)\n      expect(@scope).to receive(:new_match_scope) do |arg|\n        expect(arg[1]).to eq(:capture1)\n        expect(arg[2]).to eq(:capture2)\n      end\n\n      @scope.ephemeral_from(@match)\n    end\n\n    it \"should shadow previous match variables\" do\n      # This is an internal impl detail test\n      allow(@match).to receive(:[]).with(1).and_return(:capture1)\n      allow(@match).to receive(:[]).with(2).and_return(:capture2)\n\n      @match2 = double('match', :is_a? => true)\n      allow(@match2).to receive(:[]).with(1).and_return(:capture2_1)\n      allow(@match2).to receive(:[]).with(2).and_return(nil)\n      @scope.ephemeral_from(@match)\n      @scope.ephemeral_from(@match2)\n      expect(@scope.lookupvar('2')).to eq(nil)\n    end\n\n    it \"should create a new ephemeral level\" do\n      level_before = @scope.ephemeral_level\n      @scope.ephemeral_from(@match)\n      expect(level_before < @scope.ephemeral_level)\n    end\n  end\n\n  describe \"when managing defaults\" do\n    it \"should be able to set and lookup defaults\" do\n      param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => \"myvalue\", :source => double(\"source\"))\n      @scope.define_settings(:mytype, param)\n      expect(@scope.lookupdefaults(:mytype)).to eq({:myparam => param})\n    end\n\n    it \"should fail if a default is already defined and a new default is being defined\" do\n      param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => \"myvalue\", :source => double(\"source\"))\n      @scope.define_settings(:mytype, param)\n      expect {\n        @scope.define_settings(:mytype, param)\n      }.to raise_error(Puppet::ParseError, /Default already defined .* cannot redefine/)\n    end\n\n    it \"should return multiple defaults at once\" do\n      param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => \"myvalue\", :source => double(\"source\"))\n      @scope.define_settings(:mytype, param1)\n      param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => \"myvalue\", :source => double(\"source\"))\n      @scope.define_settings(:mytype, param2)\n\n      expect(@scope.lookupdefaults(:mytype)).to eq({:myparam => param1, :other => param2})\n    end\n\n    it \"should look up defaults defined in parent scopes\" do\n      param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => \"myvalue\", :source => double(\"source\"))\n      @scope.define_settings(:mytype, param1)\n\n      child_scope = @scope.newscope\n      param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => \"myvalue\", :source => double(\"source\"))\n      child_scope.define_settings(:mytype, param2)\n\n      expect(child_scope.lookupdefaults(:mytype)).to eq({:myparam => param1, :other => param2})\n    end\n  end\n\n  context \"#true?\" do\n    { \"a string\" => true,\n      \"true\"     => true,\n      \"false\"    => true,\n      true       => true,\n      \"\"         => false,\n      :undef     => false,\n      nil        => false\n    }.each do |input, output|\n      it \"should treat #{input.inspect} as #{output}\" do\n        expect(Puppet::Parser::Scope.true?(input)).to eq(output)\n      end\n    end\n  end\n\n  context \"when producing a hash of all variables (as used in templates)\" do\n    it \"should contain all defined variables in the scope\" do\n      @scope.setvar(\"orange\", :tangerine)\n      @scope.setvar(\"pear\", :green)\n      expect(@scope.to_hash).to eq({'orange' => :tangerine, 'pear' => :green })\n    end\n\n    it \"should contain variables in all local scopes (#21508)\" do\n      @scope.new_ephemeral true\n      @scope.setvar(\"orange\", :tangerine)\n      @scope.setvar(\"pear\", :green)\n      @scope.new_ephemeral true\n      @scope.setvar(\"apple\", :red)\n      expect(@scope.to_hash).to eq({'orange' => :tangerine, 'pear' => :green, 'apple' => :red })\n    end\n\n    it \"should contain all defined variables in the scope and all local scopes\" do\n      @scope.setvar(\"orange\", :tangerine)\n      @scope.setvar(\"pear\", :green)\n      @scope.new_ephemeral true\n      @scope.setvar(\"apple\", :red)\n      expect(@scope.to_hash).to eq({'orange' => :tangerine, 'pear' => :green, 'apple' => :red })\n    end\n\n    it \"should not contain varaibles in match scopes (non local emphemeral)\" do\n      @scope.new_ephemeral true\n      @scope.setvar(\"orange\", :tangerine)\n      @scope.setvar(\"pear\", :green)\n      @scope.ephemeral_from(/(f)(o)(o)/.match('foo'))\n      expect(@scope.to_hash).to eq({'orange' => :tangerine, 'pear' => :green })\n    end\n\n    it \"should delete values that are :undef in inner scope\" do\n      @scope.new_ephemeral true\n      @scope.setvar(\"orange\", :tangerine)\n      @scope.setvar(\"pear\", :green)\n      @scope.new_ephemeral true\n      @scope.setvar(\"apple\", :red)\n      @scope.setvar(\"orange\", :undef)\n      expect(@scope.to_hash).to eq({'pear' => :green, 'apple' => :red })\n    end\n\n    it \"should not delete values that are :undef in inner scope when include_undef is true\" do\n      @scope.new_ephemeral true\n      @scope.setvar(\"orange\", :tangerine)\n      @scope.setvar(\"pear\", :green)\n      @scope.new_ephemeral true\n      @scope.setvar(\"apple\", :red)\n      @scope.setvar(\"orange\", :undef)\n      expect(@scope.to_hash(true, true)).to eq({'pear' => :green, 'apple' => :red, 'orange' => :undef })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/templatewrapper_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/parser/templatewrapper'\n\ndescribe Puppet::Parser::TemplateWrapper do\n  include PuppetSpec::Files\n\n  let(:known_resource_types) { Puppet::Resource::TypeCollection.new(\"env\") }\n  let(:scope) do\n    compiler = Puppet::Parser::Compiler.new(Puppet::Node.new(\"mynode\"))\n    allow(compiler.environment).to receive(:known_resource_types).and_return(known_resource_types)\n    Puppet::Parser::Scope.new compiler\n  end\n\n  let(:tw) { Puppet::Parser::TemplateWrapper.new(scope) }\n\n  it \"fails if a template cannot be found\" do\n    expect(Puppet::Parser::Files).to receive(:find_template).and_return(nil)\n\n    expect { tw.file = \"fake_template\" }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"stringifies as template[<filename>] for a file based template\" do\n    allow(Puppet::Parser::Files).to receive(:find_template).and_return(\"/tmp/fake_template\")\n    tw.file = \"fake_template\"\n    expect(tw.to_s).to eql(\"template[/tmp/fake_template]\")\n  end\n\n  it \"stringifies as template[inline] for a string-based template\" do\n    expect(tw.to_s).to eql(\"template[inline]\")\n  end\n\n  it \"reads and evaluates a file-based template\" do\n    given_a_template_file(\"fake_template\", \"template contents\")\n\n    tw.file = \"fake_template\"\n    expect(tw.result).to eql(\"template contents\")\n  end\n\n  it \"provides access to the name of the template via #file\" do\n    full_file_name = given_a_template_file(\"fake_template\", \"<%= file %>\")\n\n    tw.file = \"fake_template\"\n    expect(tw.result).to eq(full_file_name)\n  end\n\n  it \"ignores a leading BOM\" do\n    full_file_name = given_a_template_file(\"bom_template\", \"\\uFEFF<%= file %>\")\n\n    tw.file = \"bom_template\"\n    expect(tw.result).to eq(full_file_name)\n  end\n\n  it \"evaluates a given string as a template\" do\n    expect(tw.result(\"template contents\")).to eql(\"template contents\")\n  end\n\n  it \"provides the defined classes with #classes\" do\n    catalog = double('catalog', :classes => [\"class1\", \"class2\"])\n    expect(scope).to receive(:catalog).and_return(catalog)\n    expect(tw.classes).to eq([\"class1\", \"class2\"])\n  end\n\n  it \"provides all the tags with #all_tags\" do\n    catalog = double('catalog', :tags => [\"tag1\", \"tag2\"])\n    expect(scope).to receive(:catalog).and_return(catalog)\n    expect(tw.all_tags).to eq([\"tag1\",\"tag2\"])\n  end\n\n  it \"raises not implemented error\" do\n    expect {\n      tw.tags\n    }.to raise_error(NotImplementedError, /Call 'all_tags' instead/)\n  end\n\n  it \"raises error on access to removed in-scope variables via method calls\" do\n    scope[\"in_scope_variable\"] = \"is good\"\n    expect { tw.result(\"<%= in_scope_variable %>\") }.to raise_error(/undefined local variable or method `in_scope_variable'/ )\n  end\n\n  it \"reports that variable is available when it is in scope\" do\n    scope[\"in_scope_variable\"] = \"is good\"\n    expect(tw.result(\"<%= has_variable?('in_scope_variable') %>\")).to eq(\"true\")\n  end\n\n  it \"reports that a variable is not available when it is not in scope\" do\n    expect(tw.result(\"<%= has_variable?('not_in_scope_variable') %>\")).to eq(\"false\")\n  end\n\n  it \"provides access to in-scope variables via instance variables\" do\n    scope[\"one\"] = \"foo\"\n    expect(tw.result(\"<%= @one %>\")).to eq(\"foo\")\n  end\n\n  %w{! . ; :}.each do |badchar|\n    it \"translates #{badchar} to _ in instance variables\" do\n      scope[\"one#{badchar}\"] = \"foo\"\n      expect(tw.result(\"<%= @one_ %>\")).to eq(\"foo\")\n    end\n  end\n\n  def given_a_template_file(name, contents)\n    full_name = tmpfile(\"template_#{name}\")\n    File.binwrite(full_name, contents)\n\n    allow(Puppet::Parser::Files).to receive(:find_template).\n      with(name, anything()).\n      and_return(full_name)\n\n    full_name\n  end\nend\n"
  },
  {
    "path": "spec/unit/parser/type_loader_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/parser/type_loader'\nrequire 'puppet/parser/parser_factory'\nrequire 'puppet_spec/modules'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Parser::TypeLoader do\n  include PuppetSpec::Modules\n  include PuppetSpec::Files\n\n  let(:empty_hostclass) { Puppet::Parser::AST::Hostclass.new('') }\n  let(:loader) { Puppet::Parser::TypeLoader.new(:myenv) }\n  let(:my_env) { Puppet::Node::Environment.create(:myenv, []) }\n\n  around do |example|\n    envs = Puppet::Environments::Static.new(my_env)\n\n    Puppet.override(:environments => envs) do\n      example.run\n    end\n  end\n\n  it \"should support an environment\" do\n    loader = Puppet::Parser::TypeLoader.new(:myenv)\n    expect(loader.environment.name).to eq(:myenv)\n  end\n\n  it \"should delegate its known resource types to its environment\" do\n    expect(loader.known_resource_types).to be_instance_of(Puppet::Resource::TypeCollection)\n  end\n\n  describe \"when loading names from namespaces\" do\n    it \"should do nothing if the name to import is an empty string\" do\n      expect(loader.try_load_fqname(:hostclass, \"\")).to be_nil\n    end\n\n    it \"should attempt to import each generated name\" do\n      expect(loader).to receive(:import_from_modules).with(\"foo/bar\").and_return([])\n      expect(loader).to receive(:import_from_modules).with(\"foo\").and_return([])\n      loader.try_load_fqname(:hostclass, \"foo::bar\")\n    end\n\n    it \"should attempt to load each possible name going from most to least specific\" do\n      ['foo/bar/baz', 'foo/bar', 'foo'].each do |path|\n        expect(Puppet::Parser::Files).to receive(:find_manifests_in_modules).with(path, anything).and_return([nil, []]).ordered\n      end\n\n      loader.try_load_fqname(:hostclass, 'foo::bar::baz')\n    end\n  end\n\n  describe \"when importing\" do\n    let(:stub_parser) { double('Parser', :file= => nil, :parse => empty_hostclass) }\n\n    before(:each) do\n      allow(Puppet::Parser::ParserFactory).to receive(:parser).and_return(stub_parser)\n    end\n\n    it \"should find all manifests matching the file or pattern\" do\n      expect(Puppet::Parser::Files).to receive(:find_manifests_in_modules).with(\"myfile\", anything).and_return([\"modname\", %w{one}])\n      loader.import(\"myfile\", \"/path\")\n    end\n\n    it \"should pass the environment when looking for files\" do\n      expect(Puppet::Parser::Files).to receive(:find_manifests_in_modules).with(anything, loader.environment).and_return([\"modname\", %w{one}])\n      loader.import(\"myfile\", \"/path\")\n    end\n\n    it \"should fail if no files are found\" do\n      expect(Puppet::Parser::Files).to receive(:find_manifests_in_modules).and_return([nil, []])\n      expect { loader.import(\"myfile\", \"/path\") }.to raise_error(/No file\\(s\\) found for import/)\n    end\n\n    it \"should parse each found file\" do\n      expect(Puppet::Parser::Files).to receive(:find_manifests_in_modules).and_return([\"modname\", [make_absolute(\"/one\")]])\n      expect(loader).to receive(:parse_file).with(make_absolute(\"/one\")).and_return(Puppet::Parser::AST::Hostclass.new(''))\n      loader.import(\"myfile\", \"/path\")\n    end\n\n    it \"should not attempt to import files that have already been imported\" do\n      loader = Puppet::Parser::TypeLoader.new(:myenv)\n\n      expect(Puppet::Parser::Files).to receive(:find_manifests_in_modules).twice.and_return([\"modname\", %w{/one}])\n      expect(loader.import(\"myfile\", \"/path\")).not_to be_empty\n\n      expect(loader.import(\"myfile\", \"/path\")).to be_empty\n    end\n  end\n\n  describe \"when importing all\" do\n    let(:base) { tmpdir(\"base\") }\n    let(:modulebase1) { File.join(base, \"first\") }\n    let(:modulebase2) { File.join(base, \"second\") }\n    let(:my_env) { Puppet::Node::Environment.create(:myenv, [modulebase1, modulebase2]) }\n\n    before do\n      # Create two module path directories\n      FileUtils.mkdir_p(modulebase1)\n      FileUtils.mkdir_p(modulebase2)\n    end\n\n    def mk_module(basedir, name)\n      PuppetSpec::Modules.create(name, basedir)\n    end\n\n    # We have to pass the base path so that we can\n    # write to modules that are in the second search path\n    def mk_manifests(base, mod, files)\n      files.collect do |file|\n        name = mod.name + \"::\" + file.gsub(\"/\", \"::\")\n        path = File.join(base, mod.name, \"manifests\", file + \".pp\")\n        FileUtils.mkdir_p(File.split(path)[0])\n\n        # write out the class\n        File.open(path, \"w\") { |f| f.print \"class #{name} {}\" }\n        name\n      end\n    end\n\n    it \"should load all puppet manifests from all modules in the specified environment\" do\n      module1 = mk_module(modulebase1, \"one\")\n      module2 = mk_module(modulebase2, \"two\")\n\n      mk_manifests(modulebase1, module1, %w{a b})\n      mk_manifests(modulebase2, module2, %w{c d})\n\n      loader.import_all\n\n      expect(loader.environment.known_resource_types.hostclass(\"one::a\")).to be_instance_of(Puppet::Resource::Type)\n      expect(loader.environment.known_resource_types.hostclass(\"one::b\")).to be_instance_of(Puppet::Resource::Type)\n      expect(loader.environment.known_resource_types.hostclass(\"two::c\")).to be_instance_of(Puppet::Resource::Type)\n      expect(loader.environment.known_resource_types.hostclass(\"two::d\")).to be_instance_of(Puppet::Resource::Type)\n    end\n\n    it \"should not load manifests from duplicate modules later in the module path\" do\n      module1 = mk_module(modulebase1, \"one\")\n\n      # duplicate\n      module2 = mk_module(modulebase2, \"one\")\n\n      mk_manifests(modulebase1, module1, %w{a})\n      mk_manifests(modulebase2, module2, %w{c})\n\n      loader.import_all\n\n      expect(loader.environment.known_resource_types.hostclass(\"one::c\")).to be_nil\n    end\n\n    it \"should load manifests from subdirectories\" do\n      module1 = mk_module(modulebase1, \"one\")\n\n      mk_manifests(modulebase1, module1, %w{a a/b a/b/c})\n\n      loader.import_all\n\n      expect(loader.environment.known_resource_types.hostclass(\"one::a::b\")).to be_instance_of(Puppet::Resource::Type)\n      expect(loader.environment.known_resource_types.hostclass(\"one::a::b::c\")).to be_instance_of(Puppet::Resource::Type)\n    end\n\n    it \"should skip modules that don't have manifests\" do\n      mk_module(modulebase1, \"one\")\n      module2 = mk_module(modulebase2, \"two\")\n      mk_manifests(modulebase2, module2, %w{c d})\n\n      loader.import_all\n\n      expect(loader.environment.known_resource_types.hostclass(\"one::a\")).to be_nil\n      expect(loader.environment.known_resource_types.hostclass(\"two::c\")).to be_instance_of(Puppet::Resource::Type)\n      expect(loader.environment.known_resource_types.hostclass(\"two::d\")).to be_instance_of(Puppet::Resource::Type)\n    end\n  end\n\n  describe \"when parsing a file\" do\n    it \"requests a new parser instance for each file\" do\n      parser = double('Parser', :file= => nil, :parse => empty_hostclass)\n\n      expect(Puppet::Parser::ParserFactory).to receive(:parser).twice.and_return(parser)\n\n      loader.parse_file(\"/my/file\")\n      loader.parse_file(\"/my/other_file\")\n    end\n\n    it \"assigns the parser its file and then parses\" do\n      parser = double('parser')\n\n      expect(Puppet::Parser::ParserFactory).to receive(:parser).and_return(parser)\n      expect(parser).to receive(:file=).with(\"/my/file\")\n      expect(parser).to receive(:parse).and_return(empty_hostclass)\n\n      loader.parse_file(\"/my/file\")\n    end\n  end\n\n  it \"should be able to add classes to the current resource type collection\" do\n    file = tmpfile(\"simple_file.pp\")\n    File.open(file, \"w\") { |f| f.puts \"class foo {}\" }\n    loader.import(File.basename(file), File.dirname(file))\n\n    expect(loader.known_resource_types.hostclass(\"foo\")).to be_instance_of(Puppet::Resource::Type)\n  end\nend\n"
  },
  {
    "path": "spec/unit/plan_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/modules'\nrequire 'puppet/module/plan'\n\ndescribe Puppet::Module::Plan do\n  include PuppetSpec::Files\n\n  let(:modpath) { tmpdir('plan_modpath') }\n  let(:mymodpath) { File.join(modpath, 'mymod') }\n  let(:othermodpath) { File.join(modpath, 'othermod') }\n  let(:mymod) { Puppet::Module.new('mymod', mymodpath, nil) }\n  let(:othermod) { Puppet::Module.new('othermod', othermodpath, nil) }\n  let(:plans_path) { File.join(mymodpath, 'plans') }\n  let(:other_plans_path) { File.join(othermodpath, 'plans') }\n  let(:plans_glob) { File.join(mymodpath, 'plans', '*') }\n\n  describe :naming do\n    word = (Puppet::Module::Plan::RESERVED_WORDS - Puppet::Module::Plan::RESERVED_DATA_TYPES).sample\n    datatype = (Puppet::Module::Plan::RESERVED_DATA_TYPES - Puppet::Module::Plan::RESERVED_WORDS).sample\n    test_cases = { 'iLegal.pp'  => 'Plan names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores',\n                   'name.md'    => 'Plan name cannot have extension .md, must be .pp or .yaml',\n                   \"#{word}.pp\"     => \"Plan name cannot be a reserved word, but was '#{word}'\",\n                   \"#{datatype}.pp\" => \"Plan name cannot be a Puppet data type, but was '#{datatype}'\",\n                   'test_1.pp'    => nil,\n                   'test_2.yaml'  => nil }\n    test_cases.each do |filename, error|\n      it \"constructs plans when needed with #{filename}\" do\n        name = File.basename(filename, '.*')\n        if error\n          expect { Puppet::Module::Plan.new(mymod, name, [File.join(plans_path, filename)]) }\n            .to raise_error(Puppet::Module::Plan::InvalidName,\n                            error)\n        else\n          expect { Puppet::Module::Plan.new(mymod, name, [filename]) }\n            .not_to raise_error\n        end\n      end\n    end\n  end\n\n  it \"finds all plans in module\" do\n    og_files = %w{plan1.pp plan2.yaml not-a-plan.ok}.map { |bn| \"#{plans_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(plans_glob).and_return(og_files)\n\n    plans = Puppet::Module::Plan.plans_in_module(mymod)\n\n    expect(plans.count).to eq(2)\n  end\n\n  it \"selects .pp file before .yaml\" do\n    og_files = %w{plan1.pp plan1.yaml}.map { |bn| \"#{plans_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(plans_glob).and_return(og_files)\n\n    plans = Puppet::Module::Plan.plans_in_module(mymod)\n\n    expect(plans.count).to eq(1)\n    expect(plans.first.files.count).to eq(1)\n    expect(plans.first.files.first['name']).to eq('plan1.pp')\n  end\n\n  it \"gives the 'init' plan a name that is just the module's name\" do\n    expect(Puppet::Module::Plan.new(mymod, 'init', [\"#{plans_path}/init.pp\"]).name).to eq('mymod')\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/adaptable_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe Puppet::Pops::Adaptable::Adapter do\n  class ValueAdapter < Puppet::Pops::Adaptable::Adapter\n    attr_accessor :value\n  end\n\n  class OtherAdapter < Puppet::Pops::Adaptable::Adapter\n    attr_accessor :value\n    def OtherAdapter.create_adapter(o)\n      x = new\n      x.value=\"I am calling you Daffy.\"\n      x\n    end\n  end\n\n  module Farm\n    class FarmAdapter < Puppet::Pops::Adaptable::Adapter\n      attr_accessor :value\n    end\n  end\n\n  class Duck\n    include Puppet::Pops::Adaptable\n  end\n\n  it \"should create specialized adapter instance on call to adapt\" do\n    d = Duck.new\n    a = ValueAdapter.adapt(d)\n    expect(a.class).to eq(ValueAdapter)\n  end\n\n  it \"should produce the same instance on multiple adaptations\" do\n    d = Duck.new\n    a = ValueAdapter.adapt(d)\n    a.value = 10\n    b = ValueAdapter.adapt(d)\n    expect(b.value).to eq(10)\n  end\n\n  it \"should return the correct adapter if there are several\" do\n    d = Duck.new\n    a = ValueAdapter.adapt(d)\n    a.value = 10\n    b = ValueAdapter.adapt(d)\n    expect(b.value).to eq(10)\n  end\n\n  it \"should allow specialization to override creating\" do\n    d = Duck.new\n    a = OtherAdapter.adapt(d)\n    expect(a.value).to eq(\"I am calling you Daffy.\")\n  end\n\n  it \"should create a new adapter overriding existing\" do\n    d = Duck.new\n    a = OtherAdapter.adapt(d)\n    expect(a.value).to eq(\"I am calling you Daffy.\")\n    a.value = \"Something different\"\n    expect(a.value).to eq(\"Something different\")\n    b = OtherAdapter.adapt(d)\n    expect(b.value).to eq(\"Something different\")\n    b = OtherAdapter.adapt_new(d)\n    expect(b.value).to eq(\"I am calling you Daffy.\")\n  end\n\n  it \"should not create adapter on get\" do\n    d = Duck.new\n    a = OtherAdapter.get(d)\n    expect(a).to eq(nil)\n  end\n\n  it \"should return same adapter from get after adapt\" do\n    d = Duck.new\n    a = OtherAdapter.get(d)\n    expect(a).to eq(nil)\n    a = OtherAdapter.adapt(d)\n    expect(a.value).to eq(\"I am calling you Daffy.\")\n    b = OtherAdapter.get(d)\n    expect(b.value).to eq(\"I am calling you Daffy.\")\n    expect(a).to eq(b)\n  end\n\n  it \"should handle adapters in nested namespaces\" do\n    d = Duck.new\n    a = Farm::FarmAdapter.get(d)\n    expect(a).to eq(nil)\n    a = Farm::FarmAdapter.adapt(d)\n    a.value = 10\n    b = Farm::FarmAdapter.get(d)\n    expect(b.value).to eq(10)\n  end\n\n  it \"should be able to clear the adapter\" do\n    d = Duck.new\n    a = OtherAdapter.adapt(d)\n    expect(a.value).to eq(\"I am calling you Daffy.\")\n    # The adapter cleared should be returned\n    expect(OtherAdapter.clear(d).value).to eq(\"I am calling you Daffy.\")\n    expect(OtherAdapter.get(d)).to eq(nil)\n  end\n\n  context \"When adapting with #adapt it\" do\n    it \"should be possible to pass a block to configure the adapter\" do\n      d = Duck.new\n      a = OtherAdapter.adapt(d) do |x|\n        x.value = \"Donald\"\n      end\n      expect(a.value).to eq(\"Donald\")\n    end\n\n    it \"should be possible to pass a block to configure the adapter and get the adapted\" do\n      d = Duck.new\n      a = OtherAdapter.adapt(d) do |x, o|\n        x.value = \"Donald, the #{o.class.name}\"\n      end\n      expect(a.value).to eq(\"Donald, the Duck\")\n    end\n  end\n\n  context \"When adapting with #adapt_new it\" do\n    it \"should be possible to pass a block to configure the adapter\" do\n      d = Duck.new\n      a = OtherAdapter.adapt_new(d) do |x|\n        x.value = \"Donald\"\n      end\n      expect(a.value).to eq(\"Donald\")\n    end\n\n    it \"should be possible to pass a block to configure the adapter and get the adapted\" do\n      d = Duck.new\n      a = OtherAdapter.adapt_new(d) do |x, o|\n        x.value = \"Donald, the #{o.class.name}\"\n      end\n      expect(a.value).to eq(\"Donald, the Duck\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/benchmark_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\n\ndescribe \"Benchmark\", :benchmark => true do\n  include PuppetSpec::Pops\n  include PuppetSpec::Scope\n\n    def code\n      'if true\n{\n$a = 10 + 10\n}\nelse\n{\n$a = \"interpolate ${foo} and stuff\"\n}\n'    end\n\n  class StringWriter < String\n    alias write concat\n  end\n\n  def json_dump(model)\n    output = StringWriter.new\n    ser = Puppet::Pops::Serialization::Serializer.new(Puppet::Pops::Serialization::JSON.writer.new(output))\n    ser.write(model)\n    ser.finish\n    output\n  end\n\n  it \"transformer\", :profile => true do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(code).model\n    transformer = Puppet::Pops::Model::AstTransformer.new()\n    m = Benchmark.measure { 10000.times { transformer.transform(model) }}\n    puts \"Transformer: #{m}\"\n  end\n\n  it \"validator\", :profile => true do\n    parser = Puppet::Pops::Parser::EvaluatingParser.new()\n    model = parser.parse_string(code)\n    m = Benchmark.measure { 100000.times { parser.assert_and_report(model) }}\n    puts \"Validator: #{m}\"\n  end\n\n  it \"parse transform\", :profile => true do\n    parser = Puppet::Pops::Parser::Parser.new()\n    transformer = Puppet::Pops::Model::AstTransformer.new()\n    m = Benchmark.measure { 10000.times { transformer.transform(parser.parse_string(code).model) }}\n    puts \"Parse and transform: #{m}\"\n  end\n\n  it \"parser0\", :profile => true do\n    parser = Puppet::Parser::Parser.new('test')\n    m = Benchmark.measure { 10000.times { parser.parse(code) }}\n    puts \"Parser 0: #{m}\"\n  end\n\n  it \"parser1\", :profile => true do\n    parser = Puppet::Pops::Parser::EvaluatingParser.new()\n    m = Benchmark.measure { 10000.times { parser.parse_string(code) }}\n    puts \"Parser1: #{m}\"\n  end\n\n  it \"marshal1\", :profile => true do\n    parser = Puppet::Pops::Parser::EvaluatingParser.new()\n    model = parser.parse_string(code).model\n    dumped = Marshal.dump(model)\n    m = Benchmark.measure { 10000.times { Marshal.load(dumped) }}\n    puts \"Marshal1: #{m}\"\n  end\n\n  it \"rgenjson\", :profile => true do\n    parser = Puppet::Pops::Parser::EvaluatingParser.new()\n    model = parser.parse_string(code).model\n    dumped = json_dump(model)\n    m = Benchmark.measure { 10000.times { json_load(dumped) }}\n    puts \"Pcore Json: #{m}\"\n  end\n\n  it \"lexer2\", :profile => true do\n    lexer = Puppet::Pops::Parser::Lexer2.new\n     m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }}\n     puts \"Lexer2: #{m}\"\n  end\n\n  it \"lexer1\", :profile => true do\n    lexer = Puppet::Pops::Parser::Lexer.new\n    m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }}\n    puts \"Pops Lexer: #{m}\"\n  end\n\n  it \"lexer0\", :profile => true do\n    lexer = Puppet::Parser::Lexer.new\n    m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }}\n    puts \"Original Lexer: #{m}\"\n  end\n\n  context \"Measure Evaluator\" do\n    let(:parser) { Puppet::Pops::Parser::EvaluatingParser.new }\n    let(:node) { 'node.example.com' }\n    let(:scope) { s = create_test_scope_for_node(node); s }\n    it \"evaluator\", :profile => true do\n      # Do the loop in puppet code since it otherwise drowns in setup\n      puppet_loop =\n        'Integer[0, 1000].each |$i| { if true\n{\n$a = 10 + 10\n}\nelse\n{\n$a = \"interpolate ${foo} and stuff\"\n}}\n'\n      # parse once, only measure the evaluation\n      model = parser.parse_string(puppet_loop, __FILE__)\n      m = Benchmark.measure { parser.evaluate(create_test_scope_for_node(node), model) }\n      puts(\"Evaluator: #{m}\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/containment_spec.rb",
    "content": ""
  },
  {
    "path": "spec/unit/pops/evaluator/access_ops_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\nrequire 'puppet/pops/types/type_factory'\nrequire 'base64'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do\n  include EvaluatorRspecHelper\n\n  def range(from, to)\n    Puppet::Pops::Types::TypeFactory.range(from, to)\n  end\n\n  def float_range(from, to)\n    Puppet::Pops::Types::TypeFactory.float_range(from, to)\n  end\n\n  def binary(s)\n    # Note that the factory is not aware of Binary and cannot operate on a\n    # literal binary. Instead, it must create a call to Binary.new() with the base64 encoded\n    # string as an argument\n    CALL_NAMED(QREF(\"Binary\"), true, [infer(Base64.strict_encode64(s))])\n  end\n\n  context 'The evaluator when operating on a String' do\n    it 'can get a single character using a single key index to []' do\n      expect(evaluate(literal('abc').access_at(1))).to eql('b')\n    end\n\n    it 'can get the last character using the key -1 in []' do\n      expect(evaluate(literal('abc').access_at(-1))).to eql('c')\n    end\n\n    it 'can get a substring by giving two keys' do\n      expect(evaluate(literal('abcd').access_at(1,2))).to eql('bc')\n      # flattens keys\n      expect(evaluate(literal('abcd').access_at([1,2]))).to eql('bc')\n    end\n\n    it 'produces empty string for a substring out of range' do\n      expect(evaluate(literal('abc').access_at(100))).to eql('')\n    end\n\n    it 'raises an error if arity is wrong for []' do\n      expect{evaluate(literal('abc').access_at)}.to raise_error(/String supports \\[\\] with one or two arguments\\. Got 0/)\n      expect{evaluate(literal('abc').access_at(1,2,3))}.to raise_error(/String supports \\[\\] with one or two arguments\\. Got 3/)\n    end\n  end\n\n  context 'The evaluator when operating on a Binary' do\n    it 'can get a single character using a single key index to []' do\n      expect(evaluate(binary('abc').access_at(1)).binary_buffer).to eql('b')\n    end\n\n    it 'can get the last character using the key -1 in []' do\n      expect(evaluate(binary('abc').access_at(-1)).binary_buffer).to eql('c')\n    end\n\n    it 'can get a substring by giving two keys' do\n      expect(evaluate(binary('abcd').access_at(1,2)).binary_buffer).to eql('bc')\n      # flattens keys\n      expect(evaluate(binary('abcd').access_at([1,2])).binary_buffer).to eql('bc')\n    end\n\n    it 'produces empty string for a substring out of range' do\n      expect(evaluate(binary('abc').access_at(100)).binary_buffer).to eql('')\n    end\n\n    it 'raises an error if arity is wrong for []' do\n      expect{evaluate(binary('abc').access_at)}.to raise_error(/String supports \\[\\] with one or two arguments\\. Got 0/)\n      expect{evaluate(binary('abc').access_at(1,2,3))}.to raise_error(/String supports \\[\\] with one or two arguments\\. Got 3/)\n    end\n  end\n\n  context 'The evaluator when operating on an Array' do\n    it 'is tested with the correct assumptions' do\n      expect(literal([1,2,3]).access_at(1).model_class <= Puppet::Pops::Model::AccessExpression).to eql(true)\n    end\n\n    it 'can get an element using a single key index to []' do\n      expect(evaluate(literal([1,2,3]).access_at(1))).to eql(2)\n    end\n\n    it 'can get the last element using the key -1 in []' do\n      expect(evaluate(literal([1,2,3]).access_at(-1))).to eql(3)\n    end\n\n    it 'can get a slice of elements using two keys' do\n      expect(evaluate(literal([1,2,3,4]).access_at(1,2))).to eql([2,3])\n      # flattens keys\n      expect(evaluate(literal([1,2,3,4]).access_at([1,2]))).to eql([2,3])\n    end\n\n    it 'produces nil for a missing entry' do\n      expect(evaluate(literal([1,2,3]).access_at(100))).to eql(nil)\n    end\n\n    it 'raises an error if arity is wrong for []' do\n      expect{evaluate(literal([1,2,3,4]).access_at)}.to raise_error(/Array supports \\[\\] with one or two arguments\\. Got 0/)\n      expect{evaluate(literal([1,2,3,4]).access_at(1,2,3))}.to raise_error(/Array supports \\[\\] with one or two arguments\\. Got 3/)\n    end\n  end\n\n  context 'The evaluator when operating on a Hash' do\n    it 'can get a single element giving a single key to []' do\n      expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3}).access_at('b'))).to eql(2)\n    end\n\n    it 'can lookup an array' do\n      expect(evaluate(literal({[1]=>10,[2]=>20}).access_at([2]))).to eql(20)\n    end\n\n    it 'produces nil for a missing key' do\n      expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3}).access_at('x'))).to eql(nil)\n    end\n\n    it 'can get multiple elements by giving multiple keys to []' do\n      expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3, 'd'=>4}).access_at('b', 'd'))).to eql([2, 4])\n    end\n\n    it 'compacts the result when using multiple keys' do\n      expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3, 'd'=>4}).access_at('b', 'x'))).to eql([2])\n    end\n\n    it 'produces an empty array if none of multiple given keys were missing' do\n      expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3, 'd'=>4}).access_at('x', 'y'))).to eql([])\n    end\n\n    it 'raises an error if arity is wrong for []' do\n      expect{evaluate(literal({'a'=>1,'b'=>2,'c'=>3}).access_at)}.to raise_error(/Hash supports \\[\\] with one or more arguments\\. Got 0/)\n    end\n  end\n\n  context \"When applied to a type it\" do\n    let(:types) { Puppet::Pops::Types::TypeFactory }\n\n    # Integer\n    #\n    it 'produces an Integer[from, to]' do\n      expr = fqr('Integer').access_at(1, 3)\n      expect(evaluate(expr)).to eql(range(1,3))\n\n      # arguments are flattened\n      expr = fqr('Integer').access_at([1, 3])\n      expect(evaluate(expr)).to eql(range(1,3))\n    end\n\n    it 'produces an Integer[1]' do\n      expr = fqr('Integer').access_at(1)\n      expect(evaluate(expr)).to eql(range(1,:default))\n    end\n\n    it 'gives an error for Integer[from, <from]' do\n      expr = fqr('Integer').access_at(1,0)\n      expect{evaluate(expr)}.to raise_error(/'from' must be less or equal to 'to'/)\n    end\n\n    it 'produces an error for Integer[] if there are more than 2 keys' do\n      expr = fqr('Integer').access_at(1,2,3)\n      expect { evaluate(expr)}.to raise_error(/with one or two arguments/)\n    end\n\n    # Float\n    #\n    it 'produces a Float[from, to]' do\n      expr = fqr('Float').access_at(1, 3)\n      expect(evaluate(expr)).to eql(float_range(1.0,3.0))\n\n      # arguments are flattened\n      expr = fqr('Float').access_at([1, 3])\n      expect(evaluate(expr)).to eql(float_range(1.0,3.0))\n    end\n\n    it 'produces a Float[1.0]' do\n      expr = fqr('Float').access_at(1.0)\n      expect(evaluate(expr)).to eql(float_range(1.0,:default))\n    end\n\n    it 'produces a Float[1]' do\n      expr = fqr('Float').access_at(1)\n      expect(evaluate(expr)).to eql(float_range(1.0,:default))\n    end\n\n    it 'gives an error for Float[from, <from]' do\n      expr = fqr('Float').access_at(1.0,0.0)\n      expect{evaluate(expr)}.to raise_error(/'from' must be less or equal to 'to'/)\n    end\n\n    it 'produces an error for Float[] if there are more than 2 keys' do\n      expr = fqr('Float').access_at(1,2,3)\n      expect { evaluate(expr)}.to raise_error(/with one or two arguments/)\n    end\n\n    # Hash Type\n    #\n    it 'produces a Hash[0, 0] from the expression Hash[0, 0]' do\n      expr = fqr('Hash').access_at(0, 0)\n      expect(evaluate(expr)).to be_the_type(types.hash_of(types.default, types.default, types.range(0, 0)))\n    end\n\n    it 'produces a Hash[Scalar,String] from the expression Hash[Scalar, String]' do\n      expr = fqr('Hash').access_at(fqr('Scalar'), fqr('String'))\n      expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.scalar))\n\n      # arguments are flattened\n      expr = fqr('Hash').access_at([fqr('Scalar'), fqr('String')])\n      expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.scalar))\n    end\n\n    it 'gives an error if only one type is specified ' do\n      expr = fqr('Hash').access_at(fqr('String'))\n      expect {evaluate(expr)}.to raise_error(/accepts 2 to 4 arguments/)\n    end\n\n    it 'produces a Hash[Scalar,String] from the expression Hash[Integer, Array][Integer, String]' do\n      expr = fqr('Hash').access_at(fqr('Integer'), fqr('Array')).access_at(fqr('Integer'), fqr('String'))\n      expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.integer))\n    end\n\n    it \"gives an error if parameter is not a type\" do\n      expr = fqr('Hash').access_at('String')\n      expect { evaluate(expr)}.to raise_error(/Hash-Type\\[\\] arguments must be types/)\n    end\n\n    # Array Type\n    #\n    it 'produces an Array[0, 0] from the expression Array[0, 0]' do\n      expr = fqr('Array').access_at(0, 0)\n      expect(evaluate(expr)).to be_the_type(types.array_of(types.default, types.range(0, 0)))\n\n      # arguments are flattened\n      expr = fqr('Array').access_at([fqr('String')])\n      expect(evaluate(expr)).to be_the_type(types.array_of(types.string))\n    end\n\n    it 'produces an Array[String] from the expression Array[String]' do\n      expr = fqr('Array').access_at(fqr('String'))\n      expect(evaluate(expr)).to be_the_type(types.array_of(types.string))\n\n      # arguments are flattened\n      expr = fqr('Array').access_at([fqr('String')])\n      expect(evaluate(expr)).to be_the_type(types.array_of(types.string))\n    end\n\n    it 'produces an Array[String] from the expression Array[Integer][String]' do\n      expr = fqr('Array').access_at(fqr('Integer')).access_at(fqr('String'))\n      expect(evaluate(expr)).to be_the_type(types.array_of(types.string))\n    end\n\n    it 'produces a size constrained Array when the last two arguments specify this' do\n      expr = fqr('Array').access_at(fqr('String'), 1)\n      expected_t = types.array_of(String, types.range(1, :default))\n      expect(evaluate(expr)).to be_the_type(expected_t)\n\n      expr = fqr('Array').access_at(fqr('String'), 1, 2)\n      expected_t = types.array_of(String, types.range(1, 2))\n      expect(evaluate(expr)).to be_the_type(expected_t)\n    end\n\n    it \"Array parameterization gives an error if parameter is not a type\" do\n      expr = fqr('Array').access_at('String')\n      expect { evaluate(expr)}.to raise_error(/Array-Type\\[\\] arguments must be types/)\n    end\n\n    # Timespan Type\n    #\n    it 'produdes a Timespan type with a lower bound' do\n      expr = fqr('Timespan').access_at({fqn('hours') => literal(3)})\n      expect(evaluate(expr)).to be_the_type(types.timespan({'hours' => 3}))\n    end\n\n    it 'produdes a Timespan type with an upper bound' do\n      expr = fqr('Timespan').access_at(literal(:default), {fqn('hours') => literal(9)})\n      expect(evaluate(expr)).to be_the_type(types.timespan(nil, {'hours' => 9}))\n    end\n\n    it 'produdes a Timespan type with both lower and upper bounds' do\n      expr = fqr('Timespan').access_at({fqn('hours') => literal(3)}, {fqn('hours') => literal(9)})\n      expect(evaluate(expr)).to be_the_type(types.timespan({'hours' => 3}, {'hours' => 9}))\n    end\n\n    # Timestamp Type\n    #\n    it 'produdes a Timestamp type with a lower bound' do\n      expr = fqr('Timestamp').access_at(literal('2014-12-12T13:14:15 CET'))\n      expect(evaluate(expr)).to be_the_type(types.timestamp('2014-12-12T13:14:15 CET'))\n    end\n\n    it 'produdes a Timestamp type with an upper bound' do\n      expr = fqr('Timestamp').access_at(literal(:default), literal('2016-08-23T17:50:00 CET'))\n      expect(evaluate(expr)).to be_the_type(types.timestamp(nil, '2016-08-23T17:50:00 CET'))\n    end\n\n    it 'produdes a Timestamp type with both lower and upper bounds' do\n      expr = fqr('Timestamp').access_at(literal('2014-12-12T13:14:15 CET'), literal('2016-08-23T17:50:00 CET'))\n      expect(evaluate(expr)).to be_the_type(types.timestamp('2014-12-12T13:14:15 CET', '2016-08-23T17:50:00 CET'))\n    end\n\n    # Tuple Type\n    #\n    it 'produces a Tuple[String] from the expression Tuple[String]' do\n      expr = fqr('Tuple').access_at(fqr('String'))\n      expect(evaluate(expr)).to be_the_type(types.tuple([String]))\n\n      # arguments are flattened\n      expr = fqr('Tuple').access_at([fqr('String')])\n      expect(evaluate(expr)).to be_the_type(types.tuple([String]))\n    end\n\n    it \"Tuple parameterization gives an error if parameter is not a type\" do\n      expr = fqr('Tuple').access_at('String')\n      expect { evaluate(expr)}.to raise_error(/Tuple-Type, Cannot use String where Any-Type is expected/)\n    end\n\n    it 'produces a varargs Tuple when the last two arguments specify size constraint' do\n      expr = fqr('Tuple').access_at(fqr('String'), 1)\n      expected_t = types.tuple([String], types.range(1, :default))\n      expect(evaluate(expr)).to be_the_type(expected_t)\n\n      expr = fqr('Tuple').access_at(fqr('String'), 1, 2)\n      expected_t = types.tuple([String], types.range(1, 2))\n      expect(evaluate(expr)).to be_the_type(expected_t)\n    end\n\n    # Pattern Type\n    #\n    it 'creates a PPatternType instance when applied to a Pattern' do\n      regexp_expr = fqr('Pattern').access_at('foo')\n      expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.pattern('foo'))\n    end\n\n    # Regexp Type\n    #\n    it 'creates a Regexp instance when applied to a Pattern' do\n      regexp_expr = fqr('Regexp').access_at('foo')\n      expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.regexp('foo'))\n\n      # arguments are flattened\n      regexp_expr = fqr('Regexp').access_at(['foo'])\n      expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.regexp('foo'))\n    end\n\n    # Class\n    #\n    it 'produces a specific class from Class[classname]' do\n      expr = fqr('Class').access_at(fqn('apache'))\n      expect(evaluate(expr)).to be_the_type(types.host_class('apache'))\n      expr = fqr('Class').access_at(literal('apache'))\n      expect(evaluate(expr)).to be_the_type(types.host_class('apache'))\n    end\n\n    it 'produces an array of Class when args are in an array' do\n      # arguments are flattened\n      expr = fqr('Class').access_at([fqn('apache')])\n      expect(evaluate(expr)[0]).to be_the_type(types.host_class('apache'))\n    end\n\n    it 'produces undef for Class if arg is undef' do\n      # arguments are flattened\n      expr = fqr('Class').access_at(nil)\n      expect(evaluate(expr)).to be_nil\n    end\n\n    it 'produces empty array for Class if arg is [undef]' do\n      # arguments are flattened\n      expr = fqr('Class').access_at([])\n      expect(evaluate(expr)).to be_eql([])\n      expr = fqr('Class').access_at([nil])\n      expect(evaluate(expr)).to be_eql([])\n    end\n\n    it 'raises error if access is to no keys' do\n      expr = fqr('Class').access_at(fqn('apache')).access_at\n      expect { evaluate(expr) }.to raise_error(/Evaluation Error: Class\\[apache\\]\\[\\] accepts 1 or more arguments\\. Got 0/)\n    end\n\n    it 'produces a collection of classes when multiple class names are given' do\n      expr = fqr('Class').access_at(fqn('apache'), literal('nginx'))\n      result = evaluate(expr)\n      expect(result[0]).to be_the_type(types.host_class('apache'))\n      expect(result[1]).to be_the_type(types.host_class('nginx'))\n    end\n\n    it 'removes leading :: in class name' do\n      expr = fqr('Class').access_at('::evoe')\n      expect(evaluate(expr)).to be_the_type(types.host_class('evoe'))\n    end\n\n    it 'raises error if the name is not a valid name' do\n      expr = fqr('Class').access_at('fail-whale')\n      expect { evaluate(expr) }.to raise_error(/Illegal name/)\n    end\n\n    it 'downcases capitalized class names' do\n      expr = fqr('Class').access_at('My::Class')\n\n      expect(evaluate(expr)).to be_the_type(types.host_class('my::class'))\n    end\n\n    it 'gives an error if no keys are given as argument' do\n      expr = fqr('Class').access_at\n      expect {evaluate(expr)}.to raise_error(/Evaluation Error: Class\\[\\] accepts 1 or more arguments. Got 0/)\n    end\n\n    it 'produces an empty array if the keys reduce to empty array' do\n      expr = fqr('Class').access_at(literal([[],[]]))\n      expect(evaluate(expr)).to be_eql([])\n    end\n\n    # Resource\n    it 'produces a specific resource type from Resource[type]' do\n      expr = fqr('Resource').access_at(fqr('File'))\n      expect(evaluate(expr)).to be_the_type(types.resource('File'))\n      expr = fqr('Resource').access_at(literal('File'))\n      expect(evaluate(expr)).to be_the_type(types.resource('File'))\n    end\n\n    it 'does not allow the type to be specified in an array' do\n      # arguments are flattened\n      expr = fqr('Resource').access_at([fqr('File')])\n      expect{evaluate(expr)}.to raise_error(Puppet::ParseError, /must be a resource type or a String/)\n    end\n\n    it 'produces a specific resource reference type from File[title]' do\n      expr = fqr('File').access_at(literal('/tmp/x'))\n      expect(evaluate(expr)).to be_the_type(types.resource('File', '/tmp/x'))\n    end\n\n    it 'produces a collection of specific resource references when multiple titles are used' do\n      # Using a resource type\n      expr = fqr('File').access_at(literal('x'),literal('y'))\n      result = evaluate(expr)\n      expect(result[0]).to be_the_type(types.resource('File', 'x'))\n      expect(result[1]).to be_the_type(types.resource('File', 'y'))\n\n      # Using generic resource\n      expr = fqr('Resource').access_at(fqr('File'), literal('x'),literal('y'))\n      result = evaluate(expr)\n      expect(result[0]).to be_the_type(types.resource('File', 'x'))\n      expect(result[1]).to be_the_type(types.resource('File', 'y'))\n    end\n\n    it 'produces undef for Resource if arg is undef' do\n      # arguments are flattened\n      expr = fqr('File').access_at(nil)\n      expect(evaluate(expr)).to be_nil\n    end\n\n    it 'gives an error if no keys are given as argument to Resource' do\n      expr = fqr('Resource').access_at\n      expect {evaluate(expr)}.to raise_error(/Evaluation Error: Resource\\[\\] accepts 1 or more arguments. Got 0/)\n    end\n\n    it 'produces an empty array if the type is given, and keys reduce to empty array for Resource' do\n      expr = fqr('Resource').access_at(fqr('File'),literal([[],[]]))\n      expect(evaluate(expr)).to be_eql([])\n    end\n\n    it 'gives an error i no keys are given as argument to a specific Resource type' do\n      expr = fqr('File').access_at\n      expect {evaluate(expr)}.to raise_error(/Evaluation Error: File\\[\\] accepts 1 or more arguments. Got 0/)\n    end\n\n    it 'produces an empty array if the keys reduce to empty array for a specific Resource tyoe' do\n      expr = fqr('File').access_at(literal([[],[]]))\n      expect(evaluate(expr)).to be_eql([])\n    end\n\n    it 'gives an error if resource is not found' do\n      expr = fqr('File').access_at(fqn('x')).access_at(fqn('y'))\n      expect {evaluate(expr)}.to raise_error(/Resource not found: File\\['x'\\]/)\n    end\n\n    # NotUndef Type\n    #\n    it 'produces a NotUndef instance' do\n      type_expr = fqr('NotUndef')\n      expect(evaluate(type_expr)).to eql(Puppet::Pops::Types::TypeFactory.not_undef())\n    end\n\n    it 'produces a NotUndef instance with contained type' do\n      type_expr = fqr('NotUndef').access_at(fqr('Integer'))\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to eql(tf.not_undef(tf.integer))\n    end\n\n    it 'produces a NotUndef instance with String type when given a literal String' do\n      type_expr = fqr('NotUndef').access_at(literal('hey'))\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to be_the_type(tf.not_undef(tf.string('hey')))\n    end\n\n    it 'Produces Optional instance with String type when using a String argument' do\n      type_expr = fqr('Optional').access_at(literal('hey'))\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to be_the_type(tf.optional(tf.string('hey')))\n    end\n\n    # Type Type\n    #\n    it 'creates a Type instance when applied to a Type' do\n      type_expr = fqr('Type').access_at(fqr('Integer'))\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to eql(tf.type_type(tf.integer))\n\n      # arguments are flattened\n      type_expr = fqr('Type').access_at([fqr('Integer')])\n      expect(evaluate(type_expr)).to eql(tf.type_type(tf.integer))\n    end\n\n    # Ruby Type\n    #\n    it 'creates a Ruby Type instance when applied to a Ruby Type' do\n      type_expr = fqr('Runtime').access_at('ruby','String')\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to eql(tf.ruby_type('String'))\n\n      # arguments are flattened\n      type_expr = fqr('Runtime').access_at(['ruby', 'String'])\n      expect(evaluate(type_expr)).to eql(tf.ruby_type('String'))\n    end\n\n    # Callable Type\n    #\n    it 'produces Callable instance without return type' do\n      type_expr = fqr('Callable').access_at(fqr('String'))\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to eql(tf.callable(String))\n    end\n\n    it 'produces Callable instance with parameters and return type' do\n      type_expr = fqr('Callable').access_at([fqr('String')], fqr('Integer'))\n      tf = Puppet::Pops::Types::TypeFactory\n      expect(evaluate(type_expr)).to eql(tf.callable([String], Integer))\n    end\n\n    # Variant Type\n    it 'does not allow Variant declarations with non-type arguments' do\n      type_expr = fqr('Variant').access_at(fqr('Integer'), 'not a type')\n      expect { evaluate(type_expr) }.to raise_error(/Cannot use String where Any-Type is expected/)\n    end\n  end\n\n  matcher :be_the_type do |type|\n    calc = Puppet::Pops::Types::TypeCalculator.new\n\n    match do |actual|\n      calc.assignable?(actual, type) && calc.assignable?(type, actual)\n    end\n\n    failure_message do |actual|\n      \"expected #{type.to_s}, but was #{actual.to_s}\"\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/arithmetic_ops_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When the evaluator performs arithmetic\" do\n    context \"on Integers\" do\n      it \"2 + 2  ==  4\"   do; expect(evaluate(literal(2) + literal(2))).to eq(4) ; end\n      it \"7 - 3  ==  4\"   do; expect(evaluate(literal(7) - literal(3))).to eq(4) ; end\n      it \"6 * 3  ==  18\"  do; expect(evaluate(literal(6) * literal(3))).to eq(18); end\n      it \"6 / 3  ==  2\"   do; expect(evaluate(literal(6) / literal(3))).to eq(2) ; end\n      it \"6 % 3  ==  0\"   do; expect(evaluate(literal(6) % literal(3))).to eq(0) ; end\n      it \"10 % 3 ==  1\"   do; expect(evaluate(literal(10) % literal(3))).to eq(1); end\n      it \"-(6/3) == -2\"   do; expect(evaluate(minus(literal(6) / literal(3)))).to eq(-2) ; end\n      it \"-6/3   == -2\"   do; expect(evaluate(minus(literal(6)) / literal(3))).to eq(-2) ; end\n      it \"8 >> 1 == 4\"    do; expect(evaluate(literal(8) >> literal(1))).to eq(4) ; end\n      it \"8 << 1 == 16\"   do; expect(evaluate(literal(8) << literal(1))).to eq(16); end\n      it \"8 >> -1 == 16\"    do; expect(evaluate(literal(8) >> literal(-1))).to eq(16) ; end\n      it \"8 << -1 == 4\"   do; expect(evaluate(literal(8) << literal(-1))).to eq(4); end\n    end\n\n    # 64 bit signed integer max and min\n    MAX_INTEGER =  0x7fffffffffffffff\n    MIN_INTEGER = -0x8000000000000000\n\n    context \"on integer values that cause 64 bit overflow\" do\n      it \"MAX + 1 => error\" do\n        expect{\n          evaluate(literal(MAX_INTEGER) + literal(1))\n        }.to raise_error(/resulted in a value outside of Puppet Integer max range/)\n      end\n\n      it \"MAX - -1 => error\" do\n        expect{\n          evaluate(literal(MAX_INTEGER) - literal(-1))\n        }.to raise_error(/resulted in a value outside of Puppet Integer max range/)\n      end\n\n      it \"MAX * 2 => error\" do\n        expect{\n          evaluate(literal(MAX_INTEGER) * literal(2))\n        }.to raise_error(/resulted in a value outside of Puppet Integer max range/)\n      end\n\n      it \"(MAX+1)*2 / 2 => error\" do\n        expect{\n          evaluate(literal((MAX_INTEGER+1)*2) / literal(2))\n        }.to raise_error(/resulted in a value outside of Puppet Integer max range/)\n      end\n\n      it \"MAX << 1 => error\" do\n        expect{\n          evaluate(literal(MAX_INTEGER) << literal(1))\n        }.to raise_error(/resulted in a value outside of Puppet Integer max range/)\n      end\n\n      it \"((MAX+1)*2)  << 1 => error\" do\n        expect{\n          evaluate(literal((MAX_INTEGER+1)*2) >> literal(1))\n        }.to raise_error(/resulted in a value outside of Puppet Integer max range/)\n      end\n\n      it \"MIN - 1 => error\" do\n        expect{\n          evaluate(literal(MIN_INTEGER) - literal(1))\n        }.to raise_error(/resulted in a value outside of Puppet Integer min range/)\n      end\n\n      it \"does not error on the border values\" do\n          expect(evaluate(literal(MAX_INTEGER) + literal(MIN_INTEGER))).to eq(MAX_INTEGER+MIN_INTEGER)\n      end\n\n    end\n\n    context \"on Floats\" do\n      it \"2.2 + 2.2  ==  4.4\"   do; expect(evaluate(literal(2.2) + literal(2.2))).to eq(4.4)  ; end\n      it \"7.7 - 3.3  ==  4.4\"   do; expect(evaluate(literal(7.7) - literal(3.3))).to eq(4.4)  ; end\n      it \"6.1 * 3.1  ==  18.91\" do; expect(evaluate(literal(6.1) * literal(3.1))).to eq(18.91); end\n      it \"6.6 / 3.3  ==  2.0\"   do; expect(evaluate(literal(6.6) / literal(3.3))).to eq(2.0)  ; end\n      it \"-(6.0/3.0) == -2.0\"   do; expect(evaluate(minus(literal(6.0) / literal(3.0)))).to eq(-2.0); end\n      it \"-6.0/3.0   == -2.0\"   do; expect(evaluate(minus(literal(6.0)) / literal(3.0))).to eq(-2.0); end\n      it \"6.6 % 3.3  ==  0.0\"   do; expect { evaluate(literal(6.6) % literal(3.3))}.to raise_error(Puppet::ParseError); end\n        it \"10.0 % 3.0 ==  1.0\"   do; expect { evaluate(literal(10.0) % literal(3.0))}.to raise_error(Puppet::ParseError); end\n      it \"3.14 << 2  == error\"  do; expect { evaluate(literal(3.14) << literal(2))}.to raise_error(Puppet::ParseError); end\n      it \"3.14 >> 2  == error\"  do; expect { evaluate(literal(3.14) >> literal(2))}.to raise_error(Puppet::ParseError); end\n    end\n\n    context \"on strings requiring boxing to Numeric\" do\n      before :each do\n        Puppet[:strict] = :off\n      end\n      it \"'2' + '2'        ==  4\" do\n        expect(evaluate(literal('2') + literal('2'))).to eq(4)\n      end\n\n      it \"'2.2' + '2.2'    ==  4.4\" do\n        expect(evaluate(literal('2.2') + literal('2.2'))).to eq(4.4)\n      end\n\n      it \"'0xF7' + '0x8'   ==  0xFF\" do\n        expect(evaluate(literal('0xF7') + literal('0x8'))).to eq(0xFF)\n      end\n\n      it \"'0367' + '010'   ==  0xFF\" do\n        expect(evaluate(literal('0367') + literal('010'))).to eq(0xFF)\n      end\n\n      it \"'0888' + '010'   ==  error\" do\n        expect { evaluate(literal('0888') + literal('010'))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"'0xWTF' + '010'  ==  error\" do\n        expect { evaluate(literal('0xWTF') + literal('010'))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"'0x12.3' + '010' ==  error\" do\n        expect { evaluate(literal('0x12.3') + literal('010'))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"'012.3' + '010'  ==  20.3 (not error, floats can start with 0)\" do\n        expect(evaluate(literal('012.3') + literal('010'))).to eq(20.3)\n      end\n\n      it \"raises an error when coercing string to numerical by default\" do\n        Puppet[:strict] = :error\n        expect { evaluate(literal('2') + literal('2')) }.to raise_error(/Evaluation Error: The string '2' was automatically coerced to the numerical value 2/)\n      end\n    end\n\n    context 'on binary values' do\n      include PuppetSpec::Compiler\n      require 'base64'\n      encoded = Base64.encode64('Hello world').chomp\n      it 'Binary + Binary' do\n        code = 'notice(assert_type(Binary, Binary([72,101,108,108,111,32]) + Binary([119,111,114,108,100])))'\n        expect(eval_and_collect_notices(code)).to eql([encoded])\n      end\n    end\n\n    context 'on timespans' do\n      include PuppetSpec::Compiler\n\n      it 'Timespan + Timespan = Timespan' do\n        code = 'notice(assert_type(Timespan, Timespan({days => 3}) + Timespan({hours => 12})))'\n        expect(eval_and_collect_notices(code)).to eql(['3-12:00:00.0'])\n      end\n\n      it 'Timespan - Timespan = Timespan' do\n        code = 'notice(assert_type(Timespan, Timespan({days => 3}) - Timespan({hours => 12})))'\n        expect(eval_and_collect_notices(code)).to eql(['2-12:00:00.0'])\n      end\n\n      it 'Timespan + -Timespan = Timespan' do\n        code = 'notice(assert_type(Timespan, Timespan({days => 3}) + -Timespan({hours => 12})))'\n        expect(eval_and_collect_notices(code)).to eql(['2-12:00:00.0'])\n      end\n\n      it 'Timespan - -Timespan = Timespan' do\n        code = 'notice(assert_type(Timespan, Timespan({days => 3}) - -Timespan({hours => 12})))'\n        expect(eval_and_collect_notices(code)).to eql(['3-12:00:00.0'])\n      end\n\n      it 'Timespan / Timespan = Float' do\n        code = \"notice(assert_type(Float, Timespan({days => 3}) / Timespan('0-12:00:00')))\"\n        expect(eval_and_collect_notices(code)).to eql(['6.0'])\n      end\n\n      it 'Timespan * Timespan is an error' do\n        code = 'notice(Timespan({days => 3}) * Timespan({hours => 12}))'\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /A Timestamp cannot be multiplied by a Timespan/)\n      end\n\n      it 'Timespan + Numeric = Timespan (numeric treated as seconds)' do\n        code = 'notice(assert_type(Timespan, Timespan({days => 3}) + 7300.0))'\n        expect(eval_and_collect_notices(code)).to eql(['3-02:01:40.0'])\n      end\n\n      it 'Timespan - Numeric = Timespan (numeric treated as seconds)' do\n        code = \"notice(assert_type(Timespan, Timespan({days => 3}) - 7300.123))\"\n        expect(eval_and_collect_notices(code)).to eql(['2-21:58:19.877'])\n      end\n\n      it 'Timespan * Numeric = Timespan (numeric treated as seconds)' do\n        code = \"notice(strftime(assert_type(Timespan, Timespan({days => 3}) * 2), '%D'))\"\n        expect(eval_and_collect_notices(code)).to eql(['6'])\n      end\n\n      it 'Numeric + Timespan = Timespan (numeric treated as seconds)' do\n        code = 'notice(assert_type(Timespan, 7300.0 + Timespan({days => 3})))'\n        expect(eval_and_collect_notices(code)).to eql(['3-02:01:40.0'])\n      end\n\n      it 'Numeric - Timespan = Timespan (numeric treated as seconds)' do\n        code = \"notice(strftime(assert_type(Timespan, 300000 - Timespan({days => 3})), '%H:%M'))\"\n        expect(eval_and_collect_notices(code)).to eql(['11:20'])\n      end\n\n      it 'Numeric * Timespan = Timespan (numeric treated as seconds)' do\n        code = \"notice(strftime(assert_type(Timespan, 2 * Timespan({days => 3})), '%D'))\"\n        expect(eval_and_collect_notices(code)).to eql(['6'])\n      end\n\n      it 'Timespan + Timestamp = Timestamp' do\n        code = \"notice(assert_type(Timestamp, Timespan({days => 3}) + Timestamp('2016-08-27T16:44:49.999 UTC')))\"\n        expect(eval_and_collect_notices(code)).to eql(['2016-08-30T16:44:49.999000000 UTC'])\n      end\n\n      it 'Timespan - Timestamp is an error' do\n        code = 'notice(Timespan({days => 3}) - Timestamp())'\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /A Timestamp cannot be subtracted from a Timespan/)\n      end\n\n      it 'Timespan * Timestamp is an error' do\n        code = 'notice(Timespan({days => 3}) * Timestamp())'\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /A Timestamp cannot be multiplied by a Timestamp/)\n      end\n\n      it 'Timespan / Timestamp is an error' do\n        code = 'notice(Timespan({days => 3}) / Timestamp())'\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /A Timespan cannot be divided by a Timestamp/)\n      end\n    end\n\n\n    context 'on timestamps' do\n      include PuppetSpec::Compiler\n\n      it 'Timestamp + Timestamp is an error' do\n        code = 'notice(Timestamp() + Timestamp())'\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /A Timestamp cannot be added to a Timestamp/)\n      end\n\n      it 'Timestamp + Timespan = Timestamp' do\n        code = \"notice(assert_type(Timestamp, Timestamp('2016-10-10') + Timespan('0-12:00:00')))\"\n        expect(eval_and_collect_notices(code)).to eql(['2016-10-10T12:00:00.000000000 UTC'])\n      end\n\n      it 'Timestamp + Numeric = Timestamp' do\n        code = \"notice(assert_type(Timestamp, Timestamp('2016-10-10T12:00:00.000') + 3600.123))\"\n        expect(eval_and_collect_notices(code)).to eql(['2016-10-10T13:00:00.123000000 UTC'])\n      end\n\n      it 'Numeric + Timestamp = Timestamp' do\n        code = \"notice(assert_type(Timestamp, 3600.123 + Timestamp('2016-10-10T12:00:00.000')))\"\n        expect(eval_and_collect_notices(code)).to eql(['2016-10-10T13:00:00.123000000 UTC'])\n      end\n\n      it 'Timestamp - Timestamp = Timespan' do\n        code = \"notice(assert_type(Timespan, Timestamp('2016-10-10') - Timestamp('2015-10-10')))\"\n        expect(eval_and_collect_notices(code)).to eql(['366-00:00:00.0'])\n      end\n\n      it 'Timestamp - Timespan = Timestamp' do\n        code = \"notice(assert_type(Timestamp, Timestamp('2016-10-10') - Timespan('0-12:00:00')))\"\n        expect(eval_and_collect_notices(code)).to eql(['2016-10-09T12:00:00.000000000 UTC'])\n      end\n\n      it 'Timestamp - Numeric = Timestamp' do\n        code = \"notice(assert_type(Timestamp, Timestamp('2016-10-10') - 3600.123))\"\n        expect(eval_and_collect_notices(code)).to eql(['2016-10-09T22:59:59.877000000 UTC'])\n      end\n\n      it 'Numeric - Timestamp = Timestamp' do\n        code = \"notice(assert_type(Timestamp, 123 - Timestamp('2016-10-10')))\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '-' is not applicable.*when right side is a Timestamp/)\n      end\n\n      it 'Timestamp / Timestamp is an error' do\n        code = \"notice(Timestamp('2016-10-10') / Timestamp())\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\/' is not applicable to a Timestamp/)\n      end\n\n      it 'Timestamp / Timespan is an error' do\n        code = \"notice(Timestamp('2016-10-10') / Timespan('0-12:00:00'))\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\/' is not applicable to a Timestamp/)\n      end\n\n      it 'Timestamp / Numeric is an error' do\n        code = \"notice(Timestamp('2016-10-10') / 3600.123)\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\/' is not applicable to a Timestamp/)\n      end\n\n      it 'Numeric / Timestamp is an error' do\n        code = \"notice(3600.123 / Timestamp('2016-10-10'))\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\/' is not applicable.*when right side is a Timestamp/)\n      end\n\n      it 'Timestamp * Timestamp is an error' do\n        code = \"notice(Timestamp('2016-10-10') * Timestamp())\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\*' is not applicable to a Timestamp/)\n      end\n\n      it 'Timestamp * Timespan is an error' do\n        code = \"notice(Timestamp('2016-10-10') * Timespan('0-12:00:00'))\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\*' is not applicable to a Timestamp/)\n      end\n\n      it 'Timestamp * Numeric is an error' do\n        code = \"notice(Timestamp('2016-10-10') * 3600.123)\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\*' is not applicable to a Timestamp/)\n      end\n\n      it 'Numeric * Timestamp is an error' do\n        code = \"notice(3600.123 * Timestamp('2016-10-10'))\"\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Operator '\\*' is not applicable.*when right side is a Timestamp/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/basic_expressions_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When the evaluator evaluates literals\" do\n    it 'should evaluator numbers to numbers' do\n      expect(evaluate(literal(1))).to eq(1)\n      expect(evaluate(literal(3.14))).to eq(3.14)\n    end\n\n    it 'should evaluate strings to string' do\n      expect(evaluate(literal('banana'))).to eq('banana')\n    end\n\n    it 'should evaluate booleans to booleans' do\n      expect(evaluate(literal(false))).to eq(false)\n      expect(evaluate(literal(true))).to eq(true)\n    end\n\n    it 'should evaluate names to strings' do\n      expect(evaluate(fqn('banana'))).to eq('banana')\n    end\n\n    it 'should evaluator types to types' do\n      array_type = Puppet::Pops::Types::PArrayType::DEFAULT\n      expect(evaluate(fqr('Array'))).to eq(array_type)\n    end\n  end\n\n  context \"When the evaluator evaluates Lists\" do\n    it \"should create an Array when evaluating a LiteralList\" do\n      expect(evaluate(literal([1,2,3]))).to eq([1,2,3])\n    end\n\n    it \"[...[...[]]] should create nested arrays without trouble\" do\n      expect(evaluate(literal([1,[2.0, 2.1, [2.2]],[3.0, 3.1]]))).to eq([1,[2.0, 2.1, [2.2]],[3.0, 3.1]])\n    end\n\n    it \"[2 + 2] should evaluate expressions in entries\" do\n      x = literal([literal(2) + literal(2)]);\n      expect(Puppet::Pops::Model::ModelTreeDumper.new.dump(x)).to eq(\"([] (+ 2 2))\")\n      expect(evaluate(x)[0]).to eq(4)\n    end\n\n    it \"[1,2,3] == [1,2,3] == true\" do\n      expect(evaluate(literal([1,2,3]).eq(literal([1,2,3])))).to eq(true);\n    end\n\n    it \"[1,2,3] != [2,3,4] == true\" do\n      expect(evaluate(literal([1,2,3]).ne(literal([2,3,4])))).to eq(true);\n    end\n\n    it \"[1, 2, 3][2] == 3\" do\n      expect(evaluate(literal([1,2,3]))[2]).to eq(3)\n    end\n  end\n\n  context \"When the evaluator evaluates Hashes\" do\n    it \"should create a  Hash when evaluating a LiteralHash\" do\n      expect(evaluate(literal({'a'=>1,'b'=>2}))).to eq({'a'=>1,'b'=>2})\n    end\n\n    it \"{...{...{}}} should create nested hashes without trouble\" do\n      expect(evaluate(literal({'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}))).to eq({'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}})\n    end\n\n    it \"{'a'=> 2 + 2} should evaluate values in entries\" do\n      expect(evaluate(literal({'a'=> literal(2) + literal(2)}))['a']).to eq(4)\n    end\n\n    it \"{'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2} == true\" do\n      expect(evaluate(literal({'a'=> 1, 'b'=>2}).eq(literal({'a'=> 1, 'b'=>2})))).to eq(true);\n    end\n\n    it \"{'a'=> 1, 'b'=>2} != {'x'=> 1, 'y'=>3} == true\" do\n      expect(evaluate(literal({'a'=> 1, 'b'=>2}).ne(literal({'x'=> 1, 'y'=>3})))).to eq(true);\n    end\n\n    it \"{'a' => 1, 'b' => 2}['b'] == 2\" do\n      expect(evaluate(literal({:a => 1, :b => 2}).access_at(:b))).to eq(2)\n    end\n  end\n\n  context 'When the evaluator evaluates a Block' do\n    it 'an empty block evaluates to nil' do\n      expect(evaluate(block())).to eq(nil)\n    end\n\n    it 'a block evaluates to its last expression' do\n      expect(evaluate(block(literal(1), literal(2)))).to eq(2)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/collections_ops_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\nrequire 'puppet/pops/types/type_factory'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl/Concat/Delete' do\n  include EvaluatorRspecHelper\n\n  context 'The evaluator when operating on an Array' do\n    it 'concatenates another array using +' do\n      expect(evaluate(literal([1,2,3]) + literal([4,5]))).to eql([1,2,3,4,5])\n    end\n\n    it 'concatenates another nested array using +' do\n      expect(evaluate(literal([1,2,3]) + literal([[4,5]]))).to eql([1,2,3,[4,5]])\n    end\n\n    it 'concatenates a hash by converting it to array' do\n      expect(evaluate(literal([1,2,3]) + literal({'a' => 1, 'b'=>2}))).to eql([1,2,3,['a',1],['b',2]])\n    end\n\n    it 'concatenates a non array value with +' do\n      expect(evaluate(literal([1,2,3]) + literal(4))).to eql([1,2,3,4])\n    end\n\n    it 'appends another array using <<' do\n      expect(evaluate(literal([1,2,3]) << literal([4,5]))).to eql([1,2,3,[4,5]])\n    end\n\n    it 'appends a hash without conversion when << operator is used' do\n      expect(evaluate(literal([1,2,3]) << literal({'a' => 1, 'b'=>2}))).to eql([1,2,3,{'a' => 1, 'b'=>2}])\n    end\n\n    it 'appends another non array using <<' do\n      expect(evaluate(literal([1,2,3]) << literal(4))).to eql([1,2,3,4])\n    end\n\n    it 'computes the difference with another array using -' do\n      expect(evaluate(literal([1,2,3,4]) - literal([2,3]))).to eql([1,4])\n    end\n\n    it 'computes the difference with a non array using -' do\n      expect(evaluate(literal([1,2,3,4]) - literal(2))).to eql([1,3,4])\n    end\n\n    it 'does not recurse into nested arrays when computing diff' do\n      expect(evaluate(literal([1,2,3,[2],4]) - literal(2))).to eql([1,3,[2],4])\n    end\n\n    it 'can compute diff with sub arrays' do\n      expect(evaluate(literal([1,2,3,[2,3],4]) - literal([[2,3]]))).to eql([1,2,3,4])\n    end\n\n    it 'computes difference by removing all matching instances' do\n      expect(evaluate(literal([1,2,3,3,2,4,2,3]) - literal([2,3]))).to eql([1,4])\n    end\n\n    it 'computes difference with a hash by converting it to an array' do\n      expect(evaluate(literal([1,2,3,['a',1],['b',2]]) - literal({'a' => 1, 'b'=>2}))).to eql([1,2,3])\n    end\n\n    it 'diffs hashes when given in an array' do\n      expect(evaluate(literal([1,2,3,{'a'=>1,'b'=>2}]) - literal([{'a' => 1, 'b'=>2}]))).to eql([1,2,3])\n    end\n\n    it 'raises and error when LHS of << is a hash' do\n    expect {\n       evaluate(literal({'a' => 1, 'b'=>2}) << literal(1))\n    }.to raise_error(/Operator '<<' is not applicable to a Hash/)\n    end\n  end\n\n  context 'The evaluator when operating on a Hash' do\n    it 'merges with another Hash using +' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal({'c' => 3}))).to eql({'a' => 1, 'b'=>2, 'c' => 3})\n    end\n\n    it 'merges RHS on top of LHS ' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal({'c' => 3, 'b'=>3}))).to eql({'a' => 1, 'b'=>3, 'c' => 3})\n    end\n\n    it 'merges a flat array of pairs converted to a hash' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal(['c', 3, 'b', 3]))).to eql({'a' => 1, 'b'=>3, 'c' => 3})\n    end\n\n    it 'merges an array converted to a hash' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal([['c', 3], ['b', 3]]))).to eql({'a' => 1, 'b'=>3, 'c' => 3})\n    end\n\n    it 'computes difference with another hash using the - operator' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2}) - literal({'b' => 3}))).to eql({'a' => 1 })\n    end\n\n    it 'computes difference with an array by treating array as array of keys' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2,'c'=>3}) - literal(['b', 'c']))).to eql({'a' => 1 })\n    end\n\n    it 'computes difference with a non array/hash by treating it as a key' do\n      expect(evaluate(literal({'a' => 1, 'b'=>2,'c'=>3}) - literal('c'))).to eql({'a' => 1, 'b' => 2 })\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/comparison_ops_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When the evaluator performs comparisons\" do\n\n    context \"of string values\" do\n      it \"'a' == 'a' == true\"  do; expect(evaluate(literal('a').eq(literal('a')))).to eq(true)  ; end\n      it \"'a' == 'b' == false\" do; expect(evaluate(literal('a').eq(literal('b')))).to eq(false) ; end\n      it \"'a' != 'a' == false\" do; expect(evaluate(literal('a').ne(literal('a')))).to eq(false) ; end\n      it \"'a' != 'b' == true\"  do; expect(evaluate(literal('a').ne(literal('b')))).to eq(true)  ; end\n\n      it \"'a' < 'b'  == true\"  do; expect(evaluate(literal('a')  < literal('b'))).to eq(true)   ; end\n      it \"'a' < 'a'  == false\" do; expect(evaluate(literal('a')  < literal('a'))).to eq(false)  ; end\n      it \"'b' < 'a'  == false\" do; expect(evaluate(literal('b')  < literal('a'))).to eq(false)  ; end\n\n      it \"'a' <= 'b' == true\"  do; expect(evaluate(literal('a')  <= literal('b'))).to eq(true)  ; end\n      it \"'a' <= 'a' == true\"  do; expect(evaluate(literal('a')  <= literal('a'))).to eq(true)  ; end\n      it \"'b' <= 'a' == false\" do; expect(evaluate(literal('b')  <= literal('a'))).to eq(false) ; end\n\n      it \"'a' > 'b'  == false\" do; expect(evaluate(literal('a')  > literal('b'))).to eq(false)  ; end\n      it \"'a' > 'a'  == false\" do; expect(evaluate(literal('a')  > literal('a'))).to eq(false)  ; end\n      it \"'b' > 'a'  == true\"  do; expect(evaluate(literal('b')  > literal('a'))).to eq(true)   ; end\n\n      it \"'a' >= 'b' == false\" do; expect(evaluate(literal('a')  >= literal('b'))).to eq(false) ; end\n      it \"'a' >= 'a' == true\"  do; expect(evaluate(literal('a')  >= literal('a'))).to eq(true)  ; end\n      it \"'b' >= 'a' == true\"  do; expect(evaluate(literal('b')  >= literal('a'))).to eq(true)  ; end\n\n      context \"with mixed case\" do\n        it \"'a' == 'A' == true\"    do; expect(evaluate(literal('a').eq(literal('A')))).to eq(true)  ; end\n        it \"'a' != 'A' == false\"   do; expect(evaluate(literal('a').ne(literal('A')))).to eq(false) ; end\n        it \"'a' >  'A' == false\"   do; expect(evaluate(literal('a') > literal('A'))).to eq(false)   ; end\n        it \"'a' >= 'A' == true\"    do; expect(evaluate(literal('a') >= literal('A'))).to eq(true)   ; end\n        it \"'A' <  'a' == false\"   do; expect(evaluate(literal('A') < literal('a'))).to eq(false)   ; end\n        it \"'A' <= 'a' == true\"    do; expect(evaluate(literal('A') <= literal('a'))).to eq(true)   ; end\n      end\n    end\n\n    context \"of integer values\" do\n      it \"1 == 1 == true\"  do; expect(evaluate(literal(1).eq(literal(1)))).to eq(true)  ; end\n      it \"1 == 2 == false\" do; expect(evaluate(literal(1).eq(literal(2)))).to eq(false) ; end\n      it \"1 != 1 == false\" do; expect(evaluate(literal(1).ne(literal(1)))).to eq(false) ; end\n      it \"1 != 2 == true\"  do; expect(evaluate(literal(1).ne(literal(2)))).to eq(true)  ; end\n\n      it \"1 < 2  == true\"  do; expect(evaluate(literal(1)  < literal(2))).to eq(true)   ; end\n      it \"1 < 1  == false\" do; expect(evaluate(literal(1)  < literal(1))).to eq(false)  ; end\n      it \"2 < 1  == false\" do; expect(evaluate(literal(2)  < literal(1))).to eq(false)  ; end\n\n      it \"1 <= 2 == true\"  do; expect(evaluate(literal(1)  <= literal(2))).to eq(true)  ; end\n      it \"1 <= 1 == true\"  do; expect(evaluate(literal(1)  <= literal(1))).to eq(true)  ; end\n      it \"2 <= 1 == false\" do; expect(evaluate(literal(2)  <= literal(1))).to eq(false) ; end\n\n      it \"1 > 2  == false\" do; expect(evaluate(literal(1)  > literal(2))).to eq(false)  ; end\n      it \"1 > 1  == false\" do; expect(evaluate(literal(1)  > literal(1))).to eq(false)  ; end\n      it \"2 > 1  == true\"  do; expect(evaluate(literal(2)  > literal(1))).to eq(true)   ; end\n\n      it \"1 >= 2 == false\" do; expect(evaluate(literal(1)  >= literal(2))).to eq(false) ; end\n      it \"1 >= 1 == true\"  do; expect(evaluate(literal(1)  >= literal(1))).to eq(true)  ; end\n      it \"2 >= 1 == true\"  do; expect(evaluate(literal(2)  >= literal(1))).to eq(true)  ; end\n    end\n\n    context \"of mixed value types\" do\n      it \"1 == 1.0  == true\"   do; expect(evaluate(literal(1).eq(    literal(1.0)))).to eq(true)  ; end\n      it \"1 < 1.1   == true\"   do; expect(evaluate(literal(1)     <  literal(1.1))).to eq(true)   ; end\n      it \"1.0 == 1  == true\"   do; expect(evaluate(literal(1.0).eq(  literal(1)))).to eq(true)    ; end\n      it \"1.0 < 2   == true\"   do; expect(evaluate(literal(1.0)   <  literal(2))).to eq(true)     ; end\n      it \"'1.0' < 'a' == true\" do; expect(evaluate(literal('1.0') <  literal('a'))).to eq(true)   ; end\n      it \"'1.0' < ''  == true\" do; expect(evaluate(literal('1.0') <  literal(''))).to eq(false)   ; end\n      it \"'1.0' < ' ' == true\" do; expect(evaluate(literal('1.0') <  literal(' '))).to eq(false)  ; end\n      it \"'a' > '1.0' == true\" do; expect(evaluate(literal('a')   >  literal('1.0'))).to eq(true) ; end\n    end\n\n    context \"of unsupported mixed value types\" do\n      it \"'1' < 1.1 == true\"   do\n        expect{ evaluate(literal('1') <  literal(1.1))}.to raise_error(/String < Float/)\n      end\n      it \"'1.0' < 1.1 == true\" do\n        expect{evaluate(literal('1.0') <  literal(1.1))}.to raise_error(/String < Float/)\n      end\n      it \"undef < 1.1 == true\" do\n        expect{evaluate(literal(nil) <  literal(1.1))}.to raise_error(/Undef Value < Float/)\n      end\n    end\n\n    context \"of regular expressions\" do\n      it \"/.*/ == /.*/  == true\"   do; expect(evaluate(literal(/.*/).eq(literal(/.*/)))).to eq(true)   ; end\n      it \"/.*/ != /a.*/ == true\"   do; expect(evaluate(literal(/.*/).ne(literal(/a.*/)))).to eq(true) ; end\n    end\n\n    context \"of booleans\" do\n      it \"true  == true  == true\"    do; expect(evaluate(literal(true).eq(literal(true)))).to eq(true)  ; end;\n      it \"false == false == true\"    do; expect(evaluate(literal(false).eq(literal(false)))).to eq(true) ; end;\n      it \"true == false  != true\"    do; expect(evaluate(literal(true).eq(literal(false)))).to eq(false) ; end;\n      it \"false  == ''  == false\"    do; expect(evaluate(literal(false).eq(literal('')))).to eq(false)  ; end;\n      it \"undef  == ''  == false\"    do; expect(evaluate(literal(:undef).eq(literal('')))).to eq(false)  ; end;\n      it \"undef  == undef  == true\"  do; expect(evaluate(literal(:undef).eq(literal(:undef)))).to eq(true)  ; end;\n      it \"nil    == undef  == true\"  do; expect(evaluate(literal(nil).eq(literal(:undef)))).to eq(true)  ; end;\n    end\n\n    context \"of collections\" do\n      it \"[1,2,3] == [1,2,3] == true\" do\n        expect(evaluate(literal([1,2,3]).eq(literal([1,2,3])))).to eq(true)\n        expect(evaluate(literal([1,2,3]).ne(literal([1,2,3])))).to eq(false)\n        expect(evaluate(literal([1,2,4]).eq(literal([1,2,3])))).to eq(false)\n        expect(evaluate(literal([1,2,4]).ne(literal([1,2,3])))).to eq(true)\n      end\n\n      it \"{'a'=>1, 'b'=>2} == {'a'=>1, 'b'=>2} == true\" do\n        expect(evaluate(literal({'a'=>1, 'b'=>2}).eq(literal({'a'=>1, 'b'=>2})))).to eq(true)\n        expect(evaluate(literal({'a'=>1, 'b'=>2}).ne(literal({'a'=>1, 'b'=>2})))).to eq(false)\n        expect(evaluate(literal({'a'=>1, 'b'=>2}).eq(literal({'x'=>1, 'b'=>2})))).to eq(false)\n        expect(evaluate(literal({'a'=>1, 'b'=>2}).ne(literal({'x'=>1, 'b'=>2})))).to eq(true)\n      end\n    end\n\n    context \"of non comparable types\" do\n      # TODO: Change the exception type\n      it \"false < true  == error\" do; expect { evaluate(literal(true) <  literal(false))}.to raise_error(Puppet::ParseError); end\n      it \"false <= true == error\" do; expect { evaluate(literal(true) <= literal(false))}.to raise_error(Puppet::ParseError); end\n      it \"false > true  == error\" do; expect { evaluate(literal(true) >  literal(false))}.to raise_error(Puppet::ParseError); end\n      it \"false >= true == error\" do; expect { evaluate(literal(true) >= literal(false))}.to raise_error(Puppet::ParseError); end\n\n      it \"/a/ < /b/  == error\" do; expect { evaluate(literal(/a/) <  literal(/b/))}.to raise_error(Puppet::ParseError); end\n      it \"/a/ <= /b/ == error\" do; expect { evaluate(literal(/a/) <= literal(/b/))}.to raise_error(Puppet::ParseError); end\n      it \"/a/ > /b/  == error\" do; expect { evaluate(literal(/a/) >  literal(/b/))}.to raise_error(Puppet::ParseError); end\n      it \"/a/ >= /b/ == error\" do; expect { evaluate(literal(/a/) >= literal(/b/))}.to raise_error(Puppet::ParseError); end\n\n      it \"[1,2,3] < [1,2,3] == error\" do\n        expect{ evaluate(literal([1,2,3]) < literal([1,2,3]))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"[1,2,3] > [1,2,3] == error\" do\n        expect{ evaluate(literal([1,2,3]) > literal([1,2,3]))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"[1,2,3] >= [1,2,3] == error\" do\n        expect{ evaluate(literal([1,2,3]) >= literal([1,2,3]))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"[1,2,3] <= [1,2,3] == error\" do\n        expect{ evaluate(literal([1,2,3]) <= literal([1,2,3]))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"{'a'=>1, 'b'=>2} < {'a'=>1, 'b'=>2} == error\" do\n        expect{ evaluate(literal({'a'=>1, 'b'=>2}) < literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"{'a'=>1, 'b'=>2} > {'a'=>1, 'b'=>2} == error\" do\n        expect{ evaluate(literal({'a'=>1, 'b'=>2}) > literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"{'a'=>1, 'b'=>2} <= {'a'=>1, 'b'=>2} == error\" do\n        expect{ evaluate(literal({'a'=>1, 'b'=>2}) <= literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError)\n      end\n\n      it \"{'a'=>1, 'b'=>2} >= {'a'=>1, 'b'=>2} == error\" do\n        expect{ evaluate(literal({'a'=>1, 'b'=>2}) >= literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError)\n      end\n    end\n  end\n\n  context \"When the evaluator performs Regular Expression matching\" do\n    it \"'a' =~ /.*/   == true\"    do; expect(evaluate(literal('a') =~ literal(/.*/))).to eq(true)     ; end\n    it \"'a' =~ '.*'   == true\"    do; expect(evaluate(literal('a') =~ literal(\".*\"))).to eq(true)     ; end\n    it \"'a' !~ /b.*/  == true\"    do; expect(evaluate(literal('a').mne(literal(/b.*/)))).to eq(true)  ; end\n    it \"'a' !~ 'b.*'  == true\"    do; expect(evaluate(literal('a').mne(literal(\"b.*\")))).to eq(true)  ; end\n\n    it \"'a' =~ Pattern['.*'] == true\"    do\n      expect(evaluate(literal('a') =~ fqr('Pattern').access_at(literal(\".*\")))).to eq(true)\n    end\n\n    it \"$a = Pattern['.*']; 'a' =~ $a  == true\"    do\n      expr = block(var('a').set(fqr('Pattern').access_at('.*')), literal('foo') =~ var('a'))\n      expect(evaluate(expr)).to eq(true)\n    end\n\n    it 'should fail if LHS is not a string' do\n      expect { evaluate(literal(666) =~ literal(/6/))}.to raise_error(Puppet::ParseError)\n    end\n  end\n\n  context \"When evaluator evaluates the 'in' operator\" do\n    it \"should find elements in an array\" do\n      expect(evaluate(literal(1).in(literal([1,2,3])))).to eq(true)\n      expect(evaluate(literal(4).in(literal([1,2,3])))).to eq(false)\n    end\n\n    it \"should find keys in a hash\" do\n      expect(evaluate(literal('a').in(literal({'x'=>1, 'a'=>2, 'y'=> 3})))).to eq(true)\n      expect(evaluate(literal('z').in(literal({'x'=>1, 'a'=>2, 'y'=> 3})))).to eq(false)\n    end\n\n    it \"should find substrings in a string\" do\n      expect(evaluate(literal('ana').in(literal('bananas')))).to eq(true)\n      expect(evaluate(literal('xxx').in(literal('bananas')))).to eq(false)\n    end\n\n    it \"should find substrings in a string (regexp)\" do\n      expect(evaluate(literal(/ana/).in(literal('bananas')))).to eq(true)\n      expect(evaluate(literal(/xxx/).in(literal('bananas')))).to eq(false)\n    end\n\n    it \"should find substrings in a string (ignoring case)\" do\n      expect(evaluate(literal('ANA').in(literal('bananas')))).to eq(true)\n      expect(evaluate(literal('ana').in(literal('BANANAS')))).to eq(true)\n      expect(evaluate(literal('xxx').in(literal('BANANAS')))).to eq(false)\n    end\n\n    it \"should find sublists in a list\" do\n      expect(evaluate(literal([2,3]).in(literal([1,[2,3],4])))).to eq(true)\n      expect(evaluate(literal([2,4]).in(literal([1,[2,3],4])))).to eq(false)\n    end\n\n    it \"should find sublists in a list (case insensitive)\" do\n      expect(evaluate(literal(['a','b']).in(literal(['A',['A','B'],'C'])))).to eq(true)\n      expect(evaluate(literal(['x','y']).in(literal(['A',['A','B'],'C'])))).to eq(false)\n    end\n\n    it \"should find keys in a hash\" do\n      expect(evaluate(literal('a').in(literal({'a' => 10, 'b' => 20})))).to eq(true)\n      expect(evaluate(literal('x').in(literal({'a' => 10, 'b' => 20})))).to eq(false)\n    end\n\n    it \"should find keys in a hash (case insensitive)\" do\n      expect(evaluate(literal('A').in(literal({'a' => 10, 'b' => 20})))).to eq(true)\n      expect(evaluate(literal('X').in(literal({'a' => 10, 'b' => 20})))).to eq(false)\n    end\n\n    it \"should find keys in a hash (regexp)\" do\n      expect(evaluate(literal(/xxx/).in(literal({'abcxxxabc' => 10, 'xyz' => 20})))).to eq(true)\n      expect(evaluate(literal(/yyy/).in(literal({'abcxxxabc' => 10, 'xyz' => 20})))).to eq(false)\n    end\n\n    it \"should find numbers as numbers\" do\n      expect(evaluate(literal(15).in(literal([1,0xf,2])))).to eq(true)\n    end\n\n    it \"should not find numbers as strings\" do\n      expect(evaluate(literal(15).in(literal([1, '0xf',2])))).to eq(false)\n      expect(evaluate(literal('15').in(literal([1, 0xf,2])))).to eq(false)\n    end\n\n    it \"should not find numbers embedded in strings, nor digits in numbers\" do\n      expect(evaluate(literal(15).in(literal([1, '115', 2])))).to eq(false)\n      expect(evaluate(literal(1).in(literal([11, 111, 2])))).to eq(false)\n      expect(evaluate(literal('1').in(literal([11, 111, 2])))).to eq(false)\n    end\n\n    it 'should find an entry with compatible type in an Array' do\n      expect(evaluate(fqr('Array').access_at(fqr('Integer')).in(literal(['a', [1,2,3], 'b'])))).to eq(true)\n      expect(evaluate(fqr('Array').access_at(fqr('Integer')).in(literal(['a', [1,2,'not integer'], 'b'])))).to eq(false)\n    end\n\n    it 'should find an entry with compatible type in a Hash' do\n      expect(evaluate(fqr('Integer').in(literal({1 => 'a', 'a' => 'b'})))).to eq(true)\n      expect(evaluate(fqr('Integer').in(literal({'a' => 'a', 'b' => 'b'})))).to eq(false)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/conditionals_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\n# This file contains testing of Conditionals, if, case, unless, selector\n#\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When the evaluator evaluates\" do\n    context \"an if expression\" do\n      it 'should output the expected result when dumped' do\n        expect(dump(IF(literal(true), literal(2), literal(5)))).to eq unindent(<<-TEXT\n          (if true\n            (then 2)\n            (else 5))\n          TEXT\n          )\n      end\n\n      it 'if true {5} == 5' do\n        expect(evaluate(IF(literal(true), literal(5)))).to eq(5)\n      end\n\n      it 'if false {5} == nil' do\n        expect(evaluate(IF(literal(false), literal(5)))).to eq(nil)\n      end\n\n      it 'if false {2} else {5} == 5' do\n        expect(evaluate(IF(literal(false), literal(2), literal(5)))).to eq(5)\n      end\n\n      it 'if false {2} elsif true {5} == 5' do\n        expect(evaluate(IF(literal(false), literal(2), IF(literal(true), literal(5))))).to eq(5)\n      end\n\n      it 'if false {2} elsif false {5} == nil' do\n        expect(evaluate(IF(literal(false), literal(2), IF(literal(false), literal(5))))).to eq(nil)\n      end\n    end\n\n    context \"an unless expression\" do\n      it 'should output the expected result when dumped' do\n        expect(dump(UNLESS(literal(true), literal(2), literal(5)))).to eq unindent(<<-TEXT\n          (unless true\n            (then 2)\n            (else 5))\n          TEXT\n          )\n      end\n\n      it 'unless false {5} == 5' do\n        expect(evaluate(UNLESS(literal(false), literal(5)))).to eq(5)\n      end\n\n      it 'unless true {5} == nil' do\n        expect(evaluate(UNLESS(literal(true), literal(5)))).to eq(nil)\n      end\n\n      it 'unless true {2} else {5} == 5' do\n        expect(evaluate(UNLESS(literal(true), literal(2), literal(5)))).to eq(5)\n      end\n\n      it 'unless true {2} elsif true {5} == 5' do\n        # not supported by concrete syntax\n        expect(evaluate(UNLESS(literal(true), literal(2), IF(literal(true), literal(5))))).to eq(5)\n      end\n\n      it 'unless true {2} elsif false {5} == nil' do\n        # not supported by concrete syntax\n        expect(evaluate(UNLESS(literal(true), literal(2), IF(literal(false), literal(5))))).to eq(nil)\n      end\n    end\n\n    context \"a case expression\" do\n      it 'should output the expected result when dumped' do\n        expect(dump(CASE(literal(2),\n                  WHEN(literal(1), literal('wat')),\n                  WHEN([literal(2), literal(3)], literal('w00t'))\n                  ))).to eq unindent(<<-TEXT\n                  (case 2\n                    (when (1) (then 'wat'))\n                    (when (2 3) (then 'w00t')))\n                  TEXT\n                  )\n        expect(dump(CASE(literal(2),\n                  WHEN(literal(1), literal('wat')),\n                  WHEN([literal(2), literal(3)], literal('w00t'))\n                  ).default(literal(4)))).to eq unindent(<<-TEXT\n                  (case 2\n                    (when (1) (then 'wat'))\n                    (when (2 3) (then 'w00t'))\n                    (when (:default) (then 4)))\n                  TEXT\n                  )\n      end\n\n      it \"case 1 { 1 : { 'w00t'} } == 'w00t'\" do\n        expect(evaluate(CASE(literal(1), WHEN(literal(1), literal('w00t'))))).to eq('w00t')\n      end\n\n      it \"case 2 { 1,2,3 : { 'w00t'} } == 'w00t'\" do\n        expect(evaluate(CASE(literal(2), WHEN([literal(1), literal(2), literal(3)], literal('w00t'))))).to eq('w00t')\n      end\n\n      it \"case 2 { 1,3 : {'wat'} 2: { 'w00t'} } == 'w00t'\" do\n        expect(evaluate(CASE(literal(2),\n          WHEN([literal(1), literal(3)], literal('wat')),\n          WHEN(literal(2), literal('w00t'))))).to eq('w00t')\n      end\n\n      it \"case 2 { 1,3 : {'wat'} 5: { 'wat'} default: {'w00t'}} == 'w00t'\" do\n        expect(evaluate(CASE(literal(2),\n          WHEN([literal(1), literal(3)], literal('wat')),\n          WHEN(literal(5), literal('wat'))).default(literal('w00t'))\n          )).to eq('w00t')\n      end\n\n      it \"case 2 { 1,3 : {'wat'} 5: { 'wat'} } == nil\" do\n        expect(evaluate(CASE(literal(2),\n          WHEN([literal(1), literal(3)], literal('wat')),\n          WHEN(literal(5), literal('wat')))\n          )).to eq(nil)\n      end\n\n      it \"case 'banana' { 1,3 : {'wat'} /.*ana.*/: { 'w00t'} } == w00t\" do\n        expect(evaluate(CASE(literal('banana'),\n          WHEN([literal(1), literal(3)], literal('wat')),\n          WHEN(literal(/.*ana.*/), literal('w00t')))\n          )).to eq('w00t')\n      end\n\n      context \"with regular expressions\" do\n        it \"should set numeric variables from the match\" do\n          expect(evaluate(CASE(literal('banana'),\n            WHEN([literal(1), literal(3)], literal('wat')),\n            WHEN(literal(/.*(ana).*/), var(1)))\n            )).to eq('ana')\n        end\n      end\n    end\n\n    context \"select expressions\" do\n      it 'should output the expected result when dumped' do\n        expect(dump(literal(2).select(\n                  MAP(literal(1), literal('wat')),\n                  MAP(literal(2), literal('w00t'))\n                  ))).to eq(\"(? 2 (1 => 'wat') (2 => 'w00t'))\")\n      end\n\n      it \"1 ? {1 => 'w00t'} == 'w00t'\" do\n        expect(evaluate(literal(1).select(MAP(literal(1), literal('w00t'))))).to eq('w00t')\n      end\n\n      it \"2 ? {1 => 'wat', 2 => 'w00t'} == 'w00t'\" do\n        expect(evaluate(literal(2).select(\n          MAP(literal(1), literal('wat')),\n          MAP(literal(2), literal('w00t'))\n          ))).to eq('w00t')\n      end\n\n      it \"3 ? {1 => 'wat', 2 => 'wat', default => 'w00t'} == 'w00t'\" do\n        expect(evaluate(literal(3).select(\n          MAP(literal(1), literal('wat')),\n          MAP(literal(2), literal('wat')),\n          MAP(literal(:default), literal('w00t'))\n          ))).to eq('w00t')\n      end\n\n      it \"3 ? {1 => 'wat', default => 'w00t', 2 => 'wat'} == 'w00t'\" do\n        expect(evaluate(literal(3).select(\n          MAP(literal(1), literal('wat')),\n          MAP(literal(:default), literal('w00t')),\n          MAP(literal(2), literal('wat'))\n          ))).to eq('w00t')\n      end\n\n      it \"should set numerical variables from match\" do\n        expect(evaluate(literal('banana').select(\n          MAP(literal(1), literal('wat')),\n          MAP(literal(/.*(ana).*/), var(1))\n          ))).to eq('ana')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/deferred_resolver_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nPuppet::Type.newtype(:test_deferred) do\n  newparam(:name)\n  newproperty(:value)\nend\n\ndescribe Puppet::Pops::Evaluator::DeferredResolver do\n  include PuppetSpec::Compiler\n\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n  let(:facts) { Puppet::Node::Facts.new('node.example.com') }\n\n  def compile_and_resolve_catalog(code, preprocess = false)\n    catalog = compile_to_catalog(code)\n    described_class.resolve_and_replace(facts, catalog, environment, preprocess)\n    catalog\n  end\n\n  it 'resolves deferred values in a catalog' do\n    catalog = compile_and_resolve_catalog(<<~END, true)\n      notify { \"deferred\":\n        message => Deferred(\"join\", [[1,2,3], \":\"])\n      }\n    END\n\n    expect(catalog.resource(:notify, 'deferred')[:message]).to eq('1:2:3')\n  end\n\n  it 'lazily resolves deferred values in a catalog' do\n    catalog = compile_and_resolve_catalog(<<~END)\n      notify { \"deferred\":\n        message => Deferred(\"join\", [[1,2,3], \":\"])\n      }\n    END\n\n    deferred = catalog.resource(:notify, 'deferred')[:message]\n    expect(deferred.resolve).to eq('1:2:3')\n  end\n\n  it 'lazily resolves nested deferred values in a catalog' do\n    catalog = compile_and_resolve_catalog(<<~END)\n      $args = Deferred(\"inline_epp\", [\"<%= 'a,b,c' %>\"])\n      notify { \"deferred\":\n        message => Deferred(\"split\", [$args, \",\"])\n      }\n    END\n\n    deferred = catalog.resource(:notify, 'deferred')[:message]\n    expect(deferred.resolve).to eq([\"a\", \"b\", \"c\"])\n  end\n\n  it 'marks the parameter as sensitive when passed an array containing a Sensitive instance' do\n    catalog = compile_and_resolve_catalog(<<~END)\n      test_deferred { \"deferred\":\n        value => Deferred('join', [['a', Sensitive('b')], ':'])\n      }\n    END\n\n    resource = catalog.resource(:test_deferred, 'deferred')\n    expect(resource.sensitive_parameters).to eq([:value])\n  end\n\n  it 'marks the parameter as sensitive when passed a hash containing a Sensitive key' do\n    catalog = compile_and_resolve_catalog(<<~END)\n      test_deferred { \"deferred\":\n        value => Deferred('keys', [{Sensitive('key') => 'value'}])\n      }\n    END\n\n    resource = catalog.resource(:test_deferred, 'deferred')\n    expect(resource.sensitive_parameters).to eq([:value])\n  end\n\n  it 'marks the parameter as sensitive when passed a hash containing a Sensitive value' do\n    catalog = compile_and_resolve_catalog(<<~END)\n      test_deferred { \"deferred\":\n        value => Deferred('values', [{key => Sensitive('value')}])\n      }\n    END\n\n    resource = catalog.resource(:test_deferred, 'deferred')\n    expect(resource.sensitive_parameters).to eq([:value])\n  end\n\n  it 'marks the parameter as sensitive when passed a nested Deferred containing a Sensitive type' do\n    catalog = compile_and_resolve_catalog(<<~END)\n      $vars = {'token' => Deferred('new', [Sensitive, \"hello\"])}\n      test_deferred { \"deferred\":\n        value => Deferred('inline_epp', ['<%= $token %>', $vars])\n      }\n    END\n\n    resource = catalog.resource(:test_deferred, 'deferred')\n    expect(resource.sensitive_parameters).to eq([:value])\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/evaluating_parser_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\nrequire 'puppet/loaders'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\nrequire 'puppet/parser/e4_parser_adapter'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\n#require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include PuppetSpec::Pops\n  include PuppetSpec::Scope\n  before(:each) do\n    Puppet[:strict_variables] = true\n    Puppet[:data_binding_terminus] = 'none'\n\n    # Tests needs a known configuration of node/scope/compiler since it parses and evaluates\n    # snippets as the compiler will evaluate them, butwithout the overhead of compiling a complete\n    # catalog for each tested expression.\n    #\n    @parser  = Puppet::Pops::Parser::EvaluatingParser.new\n    @node = Puppet::Node.new('node.example.com')\n    @node.environment = environment\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @scope = Puppet::Parser::Scope.new(@compiler)\n    @scope.source = Puppet::Resource::Type.new(:node, 'node.example.com')\n    @scope.parent = @compiler.topscope\n    Puppet.push_context(:loaders => @compiler.loaders)\n  end\n\n  after(:each) do\n    Puppet.pop_context\n  end\n\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n  let(:parser) { @parser }\n  let(:scope) { @scope }\n  types = Puppet::Pops::Types::TypeFactory\n\n  context \"When evaluator evaluates literals\" do\n    {\n      \"1\"             => 1,\n      \"010\"           => 8,\n      \"0x10\"          => 16,\n      \"3.14\"          => 3.14,\n      \"0.314e1\"       => 3.14,\n      \"31.4e-1\"       => 3.14,\n      \"'1'\"           => '1',\n      \"'banana'\"      => 'banana',\n      '\"banana\"'      => 'banana',\n      \"banana\"        => 'banana',\n      \"banana::split\" => 'banana::split',\n      \"false\"         => false,\n      \"true\"          => true,\n      \"Array\"         => types.array_of_any,\n      \"/.*/\"          => /.*/\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n    end\n\n    it 'should error when it encounters an unknown resource' do\n      expect {parser.evaluate_string(scope, '$a = SantaClause', __FILE__)}.to raise_error(/Resource type not found: SantaClause/)\n    end\n\n    it 'should error when it encounters an unknown resource with a parameter' do\n      expect {parser.evaluate_string(scope, '$b = ToothFairy[emea]', __FILE__)}.to raise_error(/Resource type not found: ToothFairy/)\n    end\n  end\n\n  context \"When the evaluator evaluates Lists and Hashes\" do\n    {\n      \"[]\"                                              => [],\n      \"[1,2,3]\"                                         => [1,2,3],\n      \"[1,[2.0, 2.1, [2.2]],[3.0, 3.1]]\"                => [1,[2.0, 2.1, [2.2]],[3.0, 3.1]],\n      \"[2 + 2]\"                                         => [4],\n      \"[1,2,3] == [1,2,3]\"                              => true,\n      \"[1,2,3] != [2,3,4]\"                              => true,\n      \"[1,2,3] == [2,2,3]\"                              => false,\n      \"[1,2,3] != [1,2,3]\"                              => false,\n      \"[1,2,3][2]\"                                      => 3,\n      \"[1,2,3] + [4,5]\"                                 => [1,2,3,4,5],\n      \"[1,2,3, *[4,5]]\"                                 => [1,2,3,4,5],\n      \"[1,2,3, (*[4,5])]\"                               => [1,2,3,4,5],\n      \"[1,2,3, ((*[4,5]))]\"                             => [1,2,3,4,5],\n      \"[1,2,3] + [[4,5]]\"                               => [1,2,3,[4,5]],\n      \"[1,2,3] + 4\"                                     => [1,2,3,4],\n      \"[1,2,3] << [4,5]\"                                => [1,2,3,[4,5]],\n      \"[1,2,3] << {'a' => 1, 'b'=>2}\"                   => [1,2,3,{'a' => 1, 'b'=>2}],\n      \"[1,2,3] << 4\"                                    => [1,2,3,4],\n      \"[1,2,3,4] - [2,3]\"                               => [1,4],\n      \"[1,2,3,4] - [2,5]\"                               => [1,3,4],\n      \"[1,2,3,4] - 2\"                                   => [1,3,4],\n      \"[1,2,3,[2],4] - 2\"                               => [1,3,[2],4],\n      \"[1,2,3,[2,3],4] - [[2,3]]\"                       => [1,2,3,4],\n      \"[1,2,3,3,2,4,2,3] - [2,3]\"                       => [1,4],\n      \"[1,2,3,['a',1],['b',2]] - {'a' => 1, 'b'=>2}\"    => [1,2,3],\n      \"[1,2,3,{'a'=>1,'b'=>2}] - [{'a' => 1, 'b'=>2}]\"  => [1,2,3],\n      \"[1,2,3] + {'a' => 1, 'b'=>2}\"                    => [1,2,3,['a',1],['b',2]],\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      \"[1,2,3][a]\" => :error,\n    }.each do |source, result|\n        it \"should parse and raise error for '#{source}'\" do\n          expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n        end\n      end\n\n    {\n      \"{}\"                                       => {},\n      \"{'a'=>1,'b'=>2}\"                          => {'a'=>1,'b'=>2},\n      \"{'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}\"        => {'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}},\n      \"{'a'=> 2 + 2}\"                            => {'a'=> 4},\n      \"{'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2}\"   => true,\n      \"{'a'=> 1, 'b'=>2} != {'x'=> 1, 'b'=>2}\"   => true,\n      \"{'a'=> 1, 'b'=>2} == {'a'=> 2, 'b'=>3}\"   => false,\n      \"{'a'=> 1, 'b'=>2} != {'a'=> 1, 'b'=>2}\"   => false,\n      \"{a => 1, b => 2}[b]\"                      => 2,\n      \"{2+2 => sum, b => 2}[4]\"                  => 'sum',\n      \"{'a'=>1, 'b'=>2} + {'c'=>3}\"              => {'a'=>1,'b'=>2,'c'=>3},\n      \"{'a'=>1, 'b'=>2} + {'b'=>3}\"              => {'a'=>1,'b'=>3},\n      \"{'a'=>1, 'b'=>2} + ['c', 3, 'b', 3]\"      => {'a'=>1,'b'=>3, 'c'=>3},\n      \"{'a'=>1, 'b'=>2} + [['c', 3], ['b', 3]]\"  => {'a'=>1,'b'=>3, 'c'=>3},\n      \"{'a'=>1, 'b'=>2} - {'b' => 3}\"            => {'a'=>1},\n      \"{'a'=>1, 'b'=>2, 'c'=>3} - ['b', 'c']\"    => {'a'=>1},\n      \"{'a'=>1, 'b'=>2, 'c'=>3} - 'c'\"           => {'a'=>1, 'b'=>2},\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      \"{'a' => 1, 'b'=>2} << 1\" => :error,\n    }.each do |source, result|\n        it \"should parse and raise error for '#{source}'\" do\n          expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n        end\n      end\n  end\n\n  context \"When the evaluator perform comparisons\" do\n    {\n      \"'a' == 'a'\"     => true,\n      \"'a' == 'b'\"     => false,\n      \"'a' != 'a'\"     => false,\n      \"'a' != 'b'\"     => true,\n      \"'a' < 'b' \"     => true,\n      \"'a' < 'a' \"     => false,\n      \"'b' < 'a' \"     => false,\n      \"'a' <= 'b'\"     => true,\n      \"'a' <= 'a'\"     => true,\n      \"'b' <= 'a'\"     => false,\n      \"'a' > 'b' \"     => false,\n      \"'a' > 'a' \"     => false,\n      \"'b' > 'a' \"     => true,\n      \"'a' >= 'b'\"     => false,\n      \"'a' >= 'a'\"     => true,\n      \"'b' >= 'a'\"     => true,\n      \"'a' == 'A'\"     => true,\n      \"'a' != 'A'\"     => false,\n      \"'a' >  'A'\"     => false,\n      \"'a' >= 'A'\"     => true,\n      \"'A' <  'a'\"     => false,\n      \"'A' <= 'a'\"     => true,\n      \"1 == 1\"         => true,\n      \"1 == 2\"         => false,\n      \"1 != 1\"         => false,\n      \"1 != 2\"         => true,\n      \"1 < 2 \"         => true,\n      \"1 < 1 \"         => false,\n      \"2 < 1 \"         => false,\n      \"1 <= 2\"         => true,\n      \"1 <= 1\"         => true,\n      \"2 <= 1\"         => false,\n      \"1 > 2 \"         => false,\n      \"1 > 1 \"         => false,\n      \"2 > 1 \"         => true,\n      \"1 >= 2\"         => false,\n      \"1 >= 1\"         => true,\n      \"2 >= 1\"         => true,\n      \"1 == 1.0 \"      => true,\n      \"1 < 1.1  \"      => true,\n      \"1.0 == 1 \"      => true,\n      \"1.0 < 2  \"      => true,\n      \"'1.0' < 'a'\"    => true,\n      \"'1.0' < '' \"    => false,\n      \"'1.0' < ' '\"    => false,\n      \"'a' > '1.0'\"    => true,\n      \"/.*/ == /.*/ \"  => true,\n      \"/.*/ != /a.*/\"  => true,\n      \"true  == true \" => true,\n      \"false == false\" => true,\n      \"true == false\"  => false,\n\n      \"Timestamp(1) < Timestamp(2)\"  => true,\n      \"Timespan(1) < Timespan(2)\"    => true,\n      \"Timestamp(1) > Timestamp(2)\"  => false,\n      \"Timespan(1) > Timespan(2)\"    => false,\n      \"Timestamp(1) == Timestamp(1)\" => true,\n      \"Timespan(1) == Timespan(1)\"   => true,\n\n      \"1 < Timestamp(2)\"  => true,\n      \"1 < Timespan(2)\"   => true,\n      \"1 > Timestamp(2)\"  => false,\n      \"1 > Timespan(2)\"   => false,\n      \"1 == Timestamp(1)\" => true,\n      \"1 == Timespan(1)\"  => true,\n\n      \"1.0 < Timestamp(2)\"  => true,\n      \"1.0 < Timespan(2)\"   => true,\n      \"1.0 > Timestamp(2)\"  => false,\n      \"1.0 > Timespan(2)\"   => false,\n      \"1.0 == Timestamp(1)\" => true,\n      \"1.0 == Timespan(1)\"  => true,\n\n      \"Timestamp(1) < 2\"  => true,\n      \"Timespan(1) < 2\"   => true,\n      \"Timestamp(1) > 2\"  => false,\n      \"Timespan(1) > 2\"   => false,\n      \"Timestamp(1) == 1\" => true,\n      \"Timespan(1) == 1\"  => true,\n\n      \"Timestamp(1) < 2.0\"  => true,\n      \"Timespan(1) < 2.0\"   => true,\n      \"Timestamp(1) > 2.0\"  => false,\n      \"Timespan(1) > 2.0\"   => false,\n      \"Timestamp(1) == 1.0\" => true,\n      \"Timespan(1) == 1.0\"  => true,\n\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n   {\n     \"a > 1\" => /String > Integer/,\n     \"a >= 1\" => /String >= Integer/,\n     \"a < 1\" => /String < Integer/,\n     \"a <= 1\" => /String <= Integer/,\n     \"1 > a\" => /Integer > String/,\n     \"1 >= a\" => /Integer >= String/,\n     \"1 < a\" => /Integer < String/,\n     \"1 <= a\" => /Integer <= String/,\n   }.each do | source, error|\n     it \"should not allow comparison of String and Number '#{source}'\" do\n       expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error)\n     end\n   end\n\n   {\n      \"'a' =~ /.*/\"                     => true,\n      \"'a' =~ '.*'\"                     => true,\n      \"/.*/ != /a.*/\"                   => true,\n      \"'a' !~ /b.*/\"                    => true,\n      \"'a' !~ 'b.*'\"                    => true,\n      '$x = a; a =~ \"$x.*\"'             => true,\n      \"a =~ Pattern['a.*']\"             => true,\n      \"a =~ Regexp['a.*']\"              => false, # String is not subtype of Regexp. PUP-957\n      \"$x = /a.*/ a =~ $x\"              => true,\n      \"$x = Pattern['a.*'] a =~ $x\"     => true,\n      \"1 =~ Integer\"                    => true,\n      \"1 !~ Integer\"                    => false,\n      \"undef =~ NotUndef\"               => false,\n      \"undef !~ NotUndef\"               => true,\n      \"[1,2,3] =~ Array[Integer[1,10]]\" => true,\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      \"666 =~ /6/\"    => :error,\n      \"[a] =~ /a/\"    => :error,\n      \"{a=>1} =~ /a/\" => :error,\n       \"/a/ =~ /a/\"    => :error,\n      \"Array =~ /A/\"  => :error,\n    }.each do |source, result|\n        it \"should parse and raise error for '#{source}'\" do\n          expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n        end\n      end\n\n    {\n      \"1 in [1,2,3]\"                  => true,\n      \"4 in [1,2,3]\"                  => false,\n      \"a in {x=>1, a=>2}\"             => true,\n      \"z in {x=>1, a=>2}\"             => false,\n      \"ana in bananas\"                => true,\n      \"xxx in bananas\"                => false,\n      \"/ana/ in bananas\"              => true,\n      \"/xxx/ in bananas\"              => false,\n      \"FILE in profiler\"              => false, # FILE is a type, not a String\n      \"'FILE' in profiler\"            => true,\n      \"String[1] in bananas\"          => false, # Philosophically true though :-)\n      \"ana in 'BANANAS'\"              => true,\n      \"/ana/ in 'BANANAS'\"            => false,\n      \"/ANA/ in 'BANANAS'\"            => true,\n      \"xxx in 'BANANAS'\"              => false,\n      \"[2,3] in [1,[2,3],4]\"          => true,\n      \"[2,4] in [1,[2,3],4]\"          => false,\n      \"[a,b] in ['A',['A','B'],'C']\"  => true,\n      \"[x,y] in ['A',['A','B'],'C']\"  => false,\n      \"a in {a=>1}\"                   => true,\n      \"x in {a=>1}\"                   => false,\n      \"'A' in {a=>1}\"                 => true,\n      \"'X' in {a=>1}\"                 => false,\n      \"a in {'A'=>1}\"                 => true,\n      \"x in {'A'=>1}\"                 => false,\n      \"/xxx/ in {'aaaxxxbbb'=>1}\"     => true,\n      \"/yyy/ in {'aaaxxxbbb'=>1}\"     => false,\n      \"15 in [1, 0xf]\"                => true,\n      \"15 in [1, '0xf']\"              => false,\n      \"'15' in [1, 0xf]\"              => false,\n      \"15 in [1, 115]\"                => false,\n      \"1 in [11, '111']\"              => false,\n      \"'1' in [11, '111']\"            => false,\n      \"Array[Integer] in [2, 3]\"      => false,\n      \"Array[Integer] in [2, [3, 4]]\" => true,\n      \"Array[Integer] in [2, [a, 4]]\" => false,\n      \"Integer in { 2 =>'a'}\"         => true,\n      \"Integer[5,10] in [1,5,3]\"      => true,\n      \"Integer[5,10] in [1,2,3]\"      => false,\n      \"Integer in {'a'=>'a'}\"         => false,\n      \"Integer in {'a'=>1}\"           => false,\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    {\n      \"if /(ana)/ in bananas {$1}\" => 'ana',\n      \"if /(xyz)/ in bananas {$1} else {$1}\" => nil,\n      \"$a = bananas =~ /(ana)/; $b = /(xyz)/ in bananas; $1\" => 'ana',\n      \"$a = xyz =~ /(xyz)/; $b = /(ana)/ in bananas; $1\" => 'ana',\n      \"if /p/ in [pineapple, bananas] {$0}\" => 'p',\n      \"if /b/ in [pineapple, bananas] {$0}\" => 'b',\n    }.each do |source, result|\n      it \"sets match variables for a regexp search using in such that '#{source}' produces '#{result}'\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    {\n      'Any'  => ['NotUndef', 'Data', 'Scalar', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Collection',\n                    'Array', 'Hash', 'CatalogEntry', 'Resource', 'Class', 'Undef', 'File' ],\n\n      # Note, Data > Collection is false (so not included)\n      'Data'    => ['ScalarData', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Array[Data]', 'Hash[String,Data]',],\n      'Scalar' => ['Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern'],\n      'Numeric' => ['Integer', 'Float'],\n      'CatalogEntry' => ['Class', 'Resource', 'File'],\n      'Integer[1,10]' => ['Integer[2,3]'],\n    }.each do |general, specials|\n      specials.each do |special |\n        it \"should compute that #{general} > #{special}\" do\n          expect(parser.evaluate_string(scope, \"#{general} > #{special}\", __FILE__)).to eq(true)\n        end\n        it \"should compute that  #{special} < #{general}\" do\n          expect(parser.evaluate_string(scope, \"#{special} < #{general}\", __FILE__)).to eq(true)\n        end\n        it \"should compute that #{general} != #{special}\" do\n          expect(parser.evaluate_string(scope, \"#{special} != #{general}\", __FILE__)).to eq(true)\n        end\n      end\n    end\n\n    {\n      'Integer[1,10] > Integer[2,3]'   => true,\n      'Integer[1,10] == Integer[2,3]'  => false,\n      'Integer[1,10] > Integer[0,5]'   => false,\n      'Integer[1,10] > Integer[1,10]'  => false,\n      'Integer[1,10] >= Integer[1,10]' => true,\n      'Integer[1,10] == Integer[1,10]' => true,\n    }.each do |source, result|\n        it \"should parse and evaluate the integer range comparison expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n  end\n\n  context \"When the evaluator performs arithmetic\" do\n    context \"on Integers\" do\n      {  \"2+2\"    => 4,\n         \"2 + 2\"  => 4,\n         \"7 - 3\"  => 4,\n         \"6 * 3\"  => 18,\n         \"6 / 3\"  => 2,\n         \"6 % 3\"  => 0,\n         \"10 % 3\" =>  1,\n         \"-(6/3)\" => -2,\n         \"-6/3  \" => -2,\n         \"8 >> 1\" => 4,\n         \"8 << 1\" => 16,\n      }.each do |source, result|\n          it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n            expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n          end\n        end\n\n    context \"on Floats\" do\n      {\n        \"2.2 + 2.2\"  => 4.4,\n        \"7.7 - 3.3\"  => 4.4,\n        \"6.1 * 3.1\"  => 18.91,\n        \"6.6 / 3.3\"  => 2.0,\n        \"-(6.0/3.0)\" => -2.0,\n        \"-6.0/3.0 \"  => -2.0,\n      }.each do |source, result|\n          it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n            expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n          end\n        end\n\n      {\n        \"3.14 << 2\" => :error,\n        \"3.14 >> 2\" => :error,\n        \"6.6 % 3.3\"  => 0.0,\n        \"10.0 % 3.0\" =>  1.0,\n      }.each do |source, result|\n          it \"should parse and raise error for '#{source}'\" do\n            expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n          end\n        end\n    end\n\n    context \"on strings requiring boxing to Numeric\" do\n      # turn strict mode off so behavior is not stubbed out\n      before :each do\n        Puppet[:strict] = :off\n      end\n      let(:logs) { [] }\n      let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }\n      let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n      let(:debugs) { logs.select { |log| log.level == :debug }.map { |log| log.message } }\n\n      {\n        \"'2' + '2'\"       => 4,\n        \"'-2' + '2'\"      => 0,\n        \"'- 2' + '2'\"     => 0,\n        '\"-\\t 2\" + \"2\"'   => 0,\n        \"'+2' + '2'\"      => 4,\n        \"'+ 2' + '2'\"     => 4,\n        \"'2.2' + '2.2'\"   => 4.4,\n        \"'-2.2' + '2.2'\"  => 0.0,\n        \"'0xF7' + '010'\"  => 0xFF,\n        \"'0xF7' + '0x8'\"  => 0xFF,\n        \"'0367' + '010'\"  => 0xFF,\n        \"'012.3' + '010'\" => 20.3,\n        \"'-0x2' + '0x4'\"  => 2,\n        \"'+0x2' + '0x4'\"  => 6,\n        \"'-02' + '04'\"    => 2,\n        \"'+02' + '04'\"    => 6,\n      }.each do |source, result|\n          it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n            expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n          end\n        end\n\n      {\n        \"'2' + 2\"       => 2,\n        \"'4' - 2\"       => 4,\n        \"'2' * 2\"       => 2,\n        \"'2' / 1\"       => 2,\n        \"'8' >> 1\"      => 8,\n        \"'4' << 1\"      => 4,\n        \"'10' % 3\"      => 10,\n      }.each do |source, coerced_val|\n          it \"should warn about numeric coercion in '#{source}' when strict = warning\" do\n            Puppet[:strict] = :warning\n            expect(Puppet::Pops::Evaluator::Runtime3Support::EvaluationError).not_to receive(:new)\n            collect_notices(source)\n            expect(warnings).to include(/The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/)\n          end\n\n          it \"should not warn about numeric coercion in '#{source}' if strict = off\" do\n            Puppet[:strict] = :off\n            expect(Puppet::Pops::Evaluator::Runtime3Support::EvaluationError).not_to receive(:new)\n            collect_notices(source)\n            expect(warnings).to_not include(/The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/)\n          end\n\n        it \"should error when finding numeric coercion in '#{source}' if strict = error\" do\n          Puppet[:strict] = :error\n          expect {\n            parser.evaluate_string(scope, source, __FILE__)\n          }.to raise_error {|error|\n            expect(error.message).to match(/The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/)\n            expect(error.backtrace.first).to match(/runtime3_support\\.rb.+optionally_fail/)\n          }\n        end\n      end\n\n      {\n        \"'0888' + '010'\"   => :error,\n        \"'0xWTF' + '010'\"  => :error,\n        \"'0x12.3' + '010'\" => :error,\n        '\"-\\n 2\" + \"2\"'    => :error,\n        '\"-\\v 2\" + \"2\"'    => :error,\n        '\"-2\\n\" + \"2\"'     => :error,\n        '\"-2\\n \" + \"2\"'    => :error,\n      }.each do |source, result|\n          it \"should parse and raise error for '#{source}'\" do\n            expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n          end\n        end\n      end\n    end\n  end # arithmetic\n\n  context \"When the evaluator evaluates assignment\" do\n    {\n      \"$a = 5\"                     => 5,\n      \"$a = 5; $a\"                 => 5,\n      \"$a = 5; $b = 6; $a\"         => 5,\n      \"$a = $b = 5; $a == $b\"      => true,\n      \"[$a] = 1 $a\"                              => 1,\n      \"[$a] = [1] $a\"                            => 1,\n      \"[$a, $b] = [1,2] $a+$b\"                   => 3,\n      \"[$a, [$b, $c]] = [1,[2, 3]] $a+$b+$c\"     => 6,\n      \"[$a] = {a => 1} $a\"                       => 1,\n      \"[$a, $b] = {a=>1,b=>2} $a+$b\"             => 3,\n      \"[$a, [$b, $c]] = {a=>1,[b,c] =>{b=>2, c=>3}} $a+$b+$c\"     => 6,\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    [\n      \"[a,b,c] = [1,2,3]\",\n      \"[a,b,c] = {b=>2,c=>3,a=>1}\",\n      \"[$a, $b] = 1\",\n      \"[$a, $b] = [1,2,3]\",\n      \"[$a, [$b,$c]] = [1,[2]]\",\n      \"[$a, [$b,$c]] = [1,[2,3,4]]\",\n      \"[$a, $b] = {a=>1}\",\n      \"[$a, [$b, $c]] = {a=>1, b =>{b=>2, c=>3}}\",\n    ].each do |source|\n        it \"should parse and evaluate the expression '#{source}' to error\" do\n          expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Puppet::ParseError)\n        end\n      end\n  end\n\n  context \"When the evaluator evaluates conditionals\" do\n    {\n      \"if true {5}\"                     => 5,\n      \"if false {5}\"                    => nil,\n      \"if false {2} else {5}\"           => 5,\n      \"if false {2} elsif true {5}\"     => 5,\n      \"if false {2} elsif false {5}\"    => nil,\n      \"unless false {5}\"                => 5,\n      \"unless true {5}\"                 => nil,\n      \"unless true {2} else {5}\"        => 5,\n      \"unless true {} else {5}\"         => 5,\n      \"$a = if true {5} $a\"                     => 5,\n      \"$a = if false {5} $a\"                    => nil,\n      \"$a = if false {2} else {5} $a\"           => 5,\n      \"$a = if false {2} elsif true {5} $a\"     => 5,\n      \"$a = if false {2} elsif false {5} $a\"    => nil,\n      \"$a = unless false {5} $a\"                => 5,\n      \"$a = unless true {5} $a\"                 => nil,\n      \"$a = unless true {2} else {5} $a\"        => 5,\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      \"case 1 { 1 : { yes } }\"                               => 'yes',\n      \"case 2 { 1,2,3 : { yes} }\"                            => 'yes',\n      \"case 2 { 1,3 : { no } 2: { yes} }\"                    => 'yes',\n      \"case 2 { 1,3 : { no } 5: { no } default: { yes }}\"    => 'yes',\n      \"case 2 { 1,3 : { no } 5: { no } }\"                    => nil,\n      \"case 'banana' { 1,3 : { no } /.*ana.*/: { yes } }\"    => 'yes',\n      \"case 'banana' { /.*(ana).*/: { $1 } }\"                => 'ana',\n      \"case [1] { Array : { yes } }\"                         => 'yes',\n      \"case [1] {\n         Array[String] : { no }\n         Array[Integer]: { yes }\n      }\"                                                     => 'yes',\n      \"case 1 {\n         Integer : { yes }\n         Type[Integer] : { no } }\"                           => 'yes',\n      \"case Integer {\n         Integer : { no }\n         Type[Integer] : { yes } }\"                          => 'yes',\n      # supports unfold\n      \"case ringo {\n         *[paul, john, ringo, george] : { 'beatle' } }\"      => 'beatle',\n\n      \"case ringo {\n         (*[paul, john, ringo, george]) : { 'beatle' } }\"    => 'beatle',\n\n      \"case undef {\n         undef : { 'yes' } }\"                                => 'yes',\n\n      \"case undef {\n         *undef : { 'no' }\n         default :{ 'yes' }}\"                                => 'yes',\n\n      \"case [green, 2, whatever] {\n         [/ee/, Integer[0,10], default] : { 'yes' }\n         default :{ 'no' }}\"                                => 'yes',\n\n      \"case [green, 2, whatever] {\n         default :{ 'no' }\n         [/ee/, Integer[0,10], default] : { 'yes' }}\"        => 'yes',\n\n      \"case {a=>1, b=>2, whatever=>3, extra => ignored} {\n         { a => Integer[0,5],\n           b => Integer[0,5],\n           whatever => default\n         }       : { 'yes' }\n         default : { 'no' }}\"                               => 'yes',\n\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      \"2 ? { 1 => no, 2 => yes}\"                          => 'yes',\n      \"3 ? { 1 => no, 2 => no, default => yes }\"          => 'yes',\n      \"3 ? { 1 => no, default => yes, 3 => no }\"          => 'no',\n      \"3 ? { 1 => no, 3 => no, default => yes }\"          => 'no',\n      \"4 ? { 1 => no, default => yes, 3 => no }\"          => 'yes',\n      \"4 ? { 1 => no, 3 => no, default => yes }\"          => 'yes',\n      \"'banana' ? { /.*(ana).*/  => $1 }\"                 => 'ana',\n      \"[2] ? { Array[String] => yes, Array => yes}\"       => 'yes',\n      \"ringo ? *[paul, john, ringo, george] => 'beatle'\"  => 'beatle',\n      \"ringo ? (*[paul, john, ringo, george]) => 'beatle'\"=> 'beatle',\n      \"undef ? undef => 'yes'\"                            => 'yes',\n      \"undef ? {*undef => 'no', default => 'yes'}\"        => 'yes',\n\n      \"[green, 2, whatever] ? {\n         [/ee/, Integer[0,10], default\n         ]       => 'yes',\n         default => 'no'}\"                                => 'yes',\n\n      \"{a=>1, b=>2, whatever=>3, extra => ignored} ?\n         {{ a => Integer[0,5],\n           b => Integer[0,5],\n           whatever => default\n         }       => 'yes',\n         default => 'no' }\"                               => 'yes',\n\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    it 'fails if a selector does not match' do\n      expect{parser.evaluate_string(scope, \"2 ? 3 => 4\")}.to raise_error(/No matching entry for selector parameter with value '2'/)\n    end\n  end\n\n  context \"When evaluator evaluated unfold\" do\n    {\n      \"*[1,2,3]\"             => [1,2,3],\n      \"*1\"                   => [1],\n      \"*'a'\"                 => ['a']\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    it \"should parse and evaluate the expression '*{a=>10, b=>20} to [['a',10],['b',20]]\" do\n      result = parser.evaluate_string(scope, '*{a=>10, b=>20}', __FILE__)\n      expect(result).to include(['a', 10])\n      expect(result).to include(['b', 20])\n    end\n\n    it \"should create an array from an Iterator\" do\n      expect(parser.evaluate_string(scope, '[1,2,3].reverse_each', __FILE__).is_a?(Array)).to be(false)\n      result = parser.evaluate_string(scope, '*[1,2,3].reverse_each', __FILE__)\n      expect(result).to eql([3,2,1])\n    end\n  end\n\n  context \"When evaluator performs [] operations\" do\n    {\n      \"[1,2,3][0]\"      => 1,\n      \"[1,2,3][2]\"      => 3,\n      \"[1,2,3][3]\"      => nil,\n      \"[1,2,3][-1]\"     => 3,\n      \"[1,2,3][-2]\"     => 2,\n      \"[1,2,3][-4]\"     => nil,\n      \"[1,2,3,4][0,2]\"  => [1,2],\n      \"[1,2,3,4][1,3]\"  => [2,3,4],\n      \"[1,2,3,4][-2,2]\"  => [3,4],\n      \"[1,2,3,4][-3,2]\"  => [2,3],\n      \"[1,2,3,4][3,5]\"   => [4],\n      \"[1,2,3,4][5,2]\"   => [],\n      \"[1,2,3,4][0,-1]\"  => [1,2,3,4],\n      \"[1,2,3,4][0,-2]\"  => [1,2,3],\n      \"[1,2,3,4][0,-4]\"  => [1],\n      \"[1,2,3,4][0,-5]\"  => [],\n      \"[1,2,3,4][-5,2]\"  => [1],\n      \"[1,2,3,4][-5,-3]\" => [1,2],\n      \"[1,2,3,4][-6,-3]\" => [1,2],\n      \"[1,2,3,4][2,-3]\"  => [],\n      \"[1,*[2,3],4]\"     => [1,2,3,4],\n      \"[1,*[2,3],4][1]\"  => 2,\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    {\n      \"{a=>1, b=>2, c=>3}[a]\"                => 1,\n      \"{a=>1, b=>2, c=>3}[c]\"                => 3,\n      \"{a=>1, b=>2, c=>3}[x]\"                => nil,\n      \"{a=>1, b=>2, c=>3}[c,b]\"              => [3,2],\n      \"{a=>1, b=>2, c=>3}[a,b,c]\"            => [1,2,3],\n      \"{a=>{b=>{c=>'it works'}}}[a][b][c]\"   => 'it works',\n      \"$a = {undef => 10} $a[free_lunch]\"     => nil,\n      \"$a = {undef => 10} $a[undef]\"          => 10,\n      \"$a = {undef => 10} $a[$a[free_lunch]]\" => 10,\n      \"$a = {} $a[free_lunch] == undef\"       => true,\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    {\n      \"'abc'[0]\"      => 'a',\n      \"'abc'[2]\"      => 'c',\n      \"'abc'[-1]\"     => 'c',\n      \"'abc'[-2]\"     => 'b',\n      \"'abc'[-3]\"     => 'a',\n      \"'abc'[-4]\"     => '',\n      \"'abc'[3]\"      => '',\n      \"abc[0]\"        => 'a',\n      \"abc[2]\"        => 'c',\n      \"abc[-1]\"       => 'c',\n      \"abc[-2]\"       => 'b',\n      \"abc[-3]\"       => 'a',\n      \"abc[-4]\"       => '',\n      \"abc[3]\"        => '',\n      \"'abcd'[0,2]\"   => 'ab',\n      \"'abcd'[1,3]\"   => 'bcd',\n      \"'abcd'[-2,2]\"  => 'cd',\n      \"'abcd'[-3,2]\"  => 'bc',\n      \"'abcd'[3,5]\"   => 'd',\n      \"'abcd'[5,2]\"   => '',\n      \"'abcd'[0,-1]\"  => 'abcd',\n      \"'abcd'[0,-2]\"  => 'abc',\n      \"'abcd'[0,-4]\"  => 'a',\n      \"'abcd'[0,-5]\"  => '',\n      \"'abcd'[-5,2]\"  => 'a',\n      \"'abcd'[-5,-3]\" => 'ab',\n      \"'abcd'[-6,-3]\" => 'ab',\n      \"'abcd'[2,-3]\"  => '',\n   }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    # Type operations (full set tested by tests covering type calculator)\n    {\n      \"Array[Integer]\"                  => types.array_of(types.integer),\n      \"Array[Integer,1]\"                => types.array_of(types.integer, types.range(1, :default)),\n      \"Array[Integer,1,2]\"              => types.array_of(types.integer, types.range(1, 2)),\n      \"Array[Integer,Integer[1,2]]\"     => types.array_of(types.integer, types.range(1, 2)),\n      \"Array[Integer,Integer[1]]\"       => types.array_of(types.integer, types.range(1, :default)),\n      \"Hash[Integer,Integer]\"           => types.hash_of(types.integer, types.integer),\n      \"Hash[Integer,Integer,1]\"         => types.hash_of(types.integer, types.integer, types.range(1, :default)),\n      \"Hash[Integer,Integer,1,2]\"       => types.hash_of(types.integer, types.integer, types.range(1, 2)),\n      \"Hash[Integer,Integer,Integer[1,2]]\" => types.hash_of(types.integer, types.integer, types.range(1, 2)),\n      \"Hash[Integer,Integer,Integer[1]]\"   => types.hash_of(types.integer, types.integer, types.range(1, :default)),\n      \"Resource[File]\"                  => types.resource('File'),\n      \"Resource['File']\"                => types.resource(types.resource('File')),\n      \"File[foo]\"                       => types.resource('file', 'foo'),\n      \"File[foo, bar]\"                  => [types.resource('file', 'foo'), types.resource('file', 'bar')],\n      \"Pattern[a, /b/, Pattern[c], Regexp[d]]\"  => types.pattern('a', 'b', 'c', 'd'),\n      \"String[1,2]\"                     => types.string(types.range(1, 2)),\n      \"String[Integer[1,2]]\"            => types.string(types.range(1, 2)),\n      \"String[Integer[1]]\"              => types.string(types.range(1, :default)),\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n      end\n    end\n\n    # LHS where [] not supported, and missing key(s)\n    {\n      \"Array[]\"                    => :error,\n      \"'abc'[]\"                    => :error,\n      \"Resource[]\"                 => :error,\n      \"File[]\"                     => :error,\n      \"String[]\"                   => :error,\n      \"1[]\"                        => :error,\n      \"3.14[]\"                     => :error,\n      \"/.*/[]\"                     => :error,\n      \"$a=[1] $a[]\"                => :error,\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(/Syntax error/)\n      end\n    end\n\n    # Errors when wrong number/type of keys are used\n    {\n      \"Array[0]\"                    => \"Array-Type[] arguments must be types. Got Integer\",\n      \"Hash[0]\"                     => \"Hash-Type[] arguments must be types. Got Integer\",\n      \"Hash[Integer, 0]\"            => \"Hash-Type[] arguments must be types. Got Integer\",\n      \"Array[Integer,1,2,3]\"        => 'Array-Type[] accepts 1 to 3 arguments. Got 4',\n      \"Array[Integer,String]\"       => \"A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got a String-Type\",\n      \"Hash[Integer,String, 1,2,3]\" => 'Hash-Type[] accepts 2 to 4 arguments. Got 5',\n      \"'abc'[x]\"                    => \"A substring operation does not accept a String as a character index. Expected an Integer\",\n      \"'abc'[1.0]\"                  => \"A substring operation does not accept a Float as a character index. Expected an Integer\",\n      \"'abc'[1, x]\"                 => \"A substring operation does not accept a String as a character index. Expected an Integer\",\n      \"'abc'[1,2,3]\"                => \"String supports [] with one or two arguments. Got 3\",\n      \"NotUndef[0]\"                 => \"NotUndef-Type[] argument must be a Type or a String. Got Integer\",\n      \"NotUndef[a,b]\"               => 'NotUndef-Type[] accepts 0 to 1 arguments. Got 2',\n      \"Resource[0]\"                 => 'First argument to Resource[] must be a resource type or a String. Got Integer',\n      \"Resource[a, 0]\"              => 'Error creating type specialization of a Resource-Type, Cannot use Integer where a resource title String is expected',\n      \"File[0]\"                     => 'Error creating type specialization of a File-Type, Cannot use Integer where a resource title String is expected',\n      \"String[a]\"                   => \"A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got a String\",\n      \"Pattern[0]\"                  => 'Error creating type specialization of a Pattern-Type, Cannot use Integer where String or Regexp or Pattern-Type or Regexp-Type is expected',\n      \"Regexp[0]\"                   => 'Error creating type specialization of a Regexp-Type, Cannot use Integer where String or Regexp is expected',\n      \"Regexp[a,b]\"                 => 'A Regexp-Type[] accepts 1 argument. Got 2',\n      \"true[0]\"                     => \"Operator '[]' is not applicable to a Boolean\",\n      \"1[0]\"                        => \"Operator '[]' is not applicable to an Integer\",\n      \"3.14[0]\"                     => \"Operator '[]' is not applicable to a Float\",\n      \"/.*/[0]\"                     => \"Operator '[]' is not applicable to a Regexp\",\n      \"[1][a]\"                      => \"The value 'a' cannot be converted to Numeric\",\n      \"[1][0.0]\"                    => \"An Array[] cannot use Float where Integer is expected\",\n      \"[1]['0.0']\"                  => \"An Array[] cannot use Float where Integer is expected\",\n      \"[1,2][1, 0.0]\"               => \"An Array[] cannot use Float where Integer is expected\",\n      \"[1,2][1.0, -1]\"              => \"An Array[] cannot use Float where Integer is expected\",\n      \"[1,2][1, -1.0]\"              => \"An Array[] cannot use Float where Integer is expected\",\n    }.each do |source, result|\n      it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n        Puppet[:strict] = :off\n        expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Regexp.new(Regexp.quote(result)))\n      end\n    end\n\n    context \"on catalog types\" do\n      it \"[n] gets resource parameter [n]\" do\n        source = \"notify { 'hello': message=>'yo'} Notify[hello][message]\"\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq('yo')\n      end\n\n      it \"[n] gets class parameter [n]\" do\n        source = \"class wonka($produces='chocolate'){ }\n           include wonka\n           Class[wonka]['produces']\"\n\n        # This is more complicated since it needs to run like 3.x and do an import_ast\n        adapted_parser = Puppet::Parser::E4ParserAdapter.new\n        adapted_parser.file = __FILE__\n        ast = adapted_parser.parse(source)\n        Puppet.override({:global_scope => scope,\n                         :environments => Puppet::Environments::Static.new(@node.environment)\n        }, \"gets class parameter test\") do\n          scope.environment.known_resource_types.import_ast(ast, '')\n          expect(ast.code.safeevaluate(scope)).to eq('chocolate')\n        end\n      end\n\n      # Resource default and override expressions and resource parameter access with []\n      {\n        # Properties\n        \"notify { id: message=>explicit} Notify[id][message]\"                   => \"explicit\",\n        \"Notify { message=>by_default} notify {foo:} Notify[foo][message]\"      => \"by_default\",\n        \"notify {foo:} Notify[foo]{message =>by_override} Notify[foo][message]\" => \"by_override\",\n        # Parameters\n        \"notify { id: withpath=>explicit} Notify[id][withpath]\"                 => \"explicit\",\n        \"Notify { withpath=>by_default } notify { foo: } Notify[foo][withpath]\" => \"by_default\",\n        \"notify {foo:}\n         Notify[foo]{withpath=>by_override}\n         Notify[foo][withpath]\"                                                 => \"by_override\",\n        # Metaparameters\n        \"notify { foo: tag => evoe} Notify[foo][tag]\"                           => \"evoe\",\n        # Does not produce the defaults for tag parameter (title, type or names of scopes)\n        \"notify { foo: } Notify[foo][tag]\"                                      => nil,\n        # But a default may be specified on the type\n        \"Notify { tag=>by_default } notify { foo: } Notify[foo][tag]\"           => \"by_default\",\n        \"Notify { tag=>by_default }\n         notify { foo: }\n         Notify[foo]{ tag=>by_override }\n         Notify[foo][tag]\"                                                      => \"by_override\",\n      }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n      # Virtual and realized resource default and overridden resource parameter access with []\n      {\n        # Properties\n        \"@notify { id: message=>explicit } Notify[id][message]\"                 => \"explicit\",\n        \"@notify { id: message=>explicit }\n         realize Notify[id]\n         Notify[id][message]\"                                                   => \"explicit\",\n        \"Notify { message=>by_default } @notify { id: } Notify[id][message]\"    => \"by_default\",\n        \"Notify { message=>by_default }\n         @notify { id: tag=>thisone }\n         Notify <| tag == thisone |>;\n         Notify[id][message]\"                                                   => \"by_default\",\n        \"@notify { id: } Notify[id]{message=>by_override} Notify[id][message]\"  => \"by_override\",\n        # Parameters\n        \"@notify { id: withpath=>explicit } Notify[id][withpath]\"               => \"explicit\",\n        \"Notify { withpath=>by_default }\n         @notify { id: }\n         Notify[id][withpath]\"                                                  => \"by_default\",\n        \"@notify { id: }\n         realize Notify[id]\n         Notify[id]{withpath=>by_override}\n         Notify[id][withpath]\"                                                  => \"by_override\",\n        # Metaparameters\n        \"@notify { id: tag=>explicit } Notify[id][tag]\"                         => \"explicit\",\n      }.each do |source, result|\n        it \"parses and evaluates virtual and realized resources in the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n      # Exported resource attributes\n      {\n        \"@@notify { id: message=>explicit } Notify[id][message]\"                => \"explicit\",\n        \"@@notify { id: message=>explicit, tag=>thisone }\n         Notify <<| tag == thisone |>>\n         Notify[id][message]\"                                                   => \"explicit\",\n      }.each do |source, result|\n        it \"parses and evaluates exported resources in the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n      # Resource default and override expressions and resource parameter access error conditions\n      {\n        \"notify { xid: message=>explicit} Notify[id][message]\"  => /Resource not found/,\n        \"notify { id: message=>explicit} Notify[id][mustard]\"   => /does not have a parameter called 'mustard'/,\n        # NOTE: these meta-esque parameters are not recognized as such\n        \"notify { id: message=>explicit} Notify[id][title]\"   => /does not have a parameter called 'title'/,\n        \"notify { id: message=>explicit} Notify[id]['type']\"   => /does not have a parameter called 'type'/,\n        \"notify { id: message=>explicit } Notify[id]{message=>override}\" => /'message' is already set on Notify\\[id\\]/,\n        \"notify { id: message => 'once', message => 'twice' }\" => /'message' has already been set/\n      }.each do |source, result|\n        it \"should parse '#{source}' and raise error matching #{result}\" do\n          expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result)\n        end\n      end\n\n      context 'with errors' do\n        { \"Class['fail-whale']\" => /Illegal name/,\n          \"Class[0]\"            => /An Integer cannot be used where a String is expected/,\n          \"Class[/.*/]\"         => /A Regexp cannot be used where a String is expected/,\n          \"Class[4.1415]\"       => /A Float cannot be used where a String is expected/,\n          \"Class[Integer]\"      => /An Integer-Type cannot be used where a String is expected/,\n          \"Class[File['tmp']]\"   => /A File\\['tmp'\\] Resource-Reference cannot be used where a String is expected/,\n        }.each do | source, error_pattern|\n          it \"an error is flagged for '#{source}'\" do\n            expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error_pattern)\n          end\n        end\n      end\n    end\n  # end [] operations\n  end\n\n  context \"When the evaluator performs boolean operations\" do\n    {\n      \"true and true\"   => true,\n      \"false and true\"  => false,\n      \"true and false\"  => false,\n      \"false and false\" => false,\n      \"true or true\"    => true,\n      \"false or true\"   => true,\n      \"true or false\"   => true,\n      \"false or false\"  => false,\n      \"! true\"          => false,\n      \"!! true\"         => true,\n      \"!! false\"        => false,\n      \"! 'x'\"           => false,\n      \"! ''\"            => false,\n      \"! undef\"         => true,\n      \"! [a]\"           => false,\n      \"! []\"            => false,\n      \"! {a=>1}\"        => false,\n      \"! {}\"            => false,\n      \"true and false and '0xwtf' + 1\"  => false,\n      \"false or true  or '0xwtf' + 1\"  => true,\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      \"false || false || '0xwtf' + 1\"   => :error,\n    }.each do |source, result|\n        it \"should parse and raise error for '#{source}'\" do\n          expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n        end\n      end\n  end\n\n  context \"When evaluator performs operations on literal undef\" do\n    it \"computes non existing hash lookup as undef\" do\n      expect(parser.evaluate_string(scope, \"{a => 1}[b] == undef\", __FILE__)).to eq(true)\n      expect(parser.evaluate_string(scope, \"undef == {a => 1}[b]\", __FILE__)).to eq(true)\n    end\n  end\n\n  context \"When evaluator performs calls\" do\n\n    let(:populate) do\n      parser.evaluate_string(scope, \"$a = 10 $b = [1,2,3]\")\n    end\n\n    {\n      'sprintf( \"x%iy\", $a )'                 => \"x10y\",\n      # unfolds\n      'sprintf( *[\"x%iy\", $a] )'              => \"x10y\",\n      '( *[\"x%iy\", $a] ).sprintf'             => \"x10y\",\n      '((*[\"x%iy\", $a])).sprintf'             => \"x10y\",\n      '\"x%iy\".sprintf( $a )'                  => \"x10y\",\n      '$b.reduce |$memo,$x| { $memo + $x }'   => 6,\n      'reduce($b) |$memo,$x| { $memo + $x }'  => 6,\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          populate\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    {\n      '\"value is ${a*2} yo\"'  => :error,\n    }.each do |source, result|\n        it \"should parse and raise error for '#{source}'\" do\n          expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n        end\n      end\n\n    it \"provides location information on error in unparenthesized call logic\" do\n    expect{parser.evaluate_string(scope, \"include non_existing_class\", __FILE__)}.to raise_error(Puppet::ParseError, /line: 1, column: 1/)\n    end\n\n    it 'defaults can be given in a lambda and used only when arg is missing' do\n      env_loader = @compiler.loaders.public_environment_loader\n      fc = Puppet::Functions.create_function(:test) do\n        dispatch :test do\n          param 'Integer', :count\n          required_block_param\n        end\n        def test(count)\n          yield(*[].fill(10, 0, count))\n        end\n      end\n      the_func = fc.new({}, env_loader)\n      env_loader.add_entry(:function, 'test', the_func, __FILE__)\n      expect(parser.evaluate_string(scope, \"test(1) |$x, $y=20| { $x + $y}\")).to eql(30)\n      expect(parser.evaluate_string(scope, \"test(2) |$x, $y=20| { $x + $y}\")).to eql(20)\n    end\n\n    it 'a given undef does not select the default value' do\n      env_loader = @compiler.loaders.public_environment_loader\n      fc = Puppet::Functions.create_function(:test) do\n        dispatch :test do\n          param 'Any', :lambda_arg\n          required_block_param\n        end\n        def test(lambda_arg)\n          yield(lambda_arg)\n        end\n      end\n      the_func = fc.new({}, env_loader)\n      env_loader.add_entry(:function, 'test', the_func, __FILE__)\n\n      expect(parser.evaluate_string(scope, \"test(undef) |$x=20| { $x == undef}\")).to eql(true)\n    end\n\n    it 'a given undef is given as nil' do\n      env_loader = @compiler.loaders.public_environment_loader\n      fc = Puppet::Functions.create_function(:assert_no_undef) do\n        dispatch :assert_no_undef do\n          param 'Any', :x\n        end\n\n        def assert_no_undef(x)\n          case x\n          when Array\n            return unless x.include?(:undef)\n          when Hash\n            return unless x.keys.include?(:undef) || x.values.include?(:undef)\n          else\n            return unless x == :undef\n          end\n          raise \"contains :undef\"\n        end\n      end\n\n      the_func = fc.new({}, env_loader)\n      env_loader.add_entry(:function, 'assert_no_undef', the_func, __FILE__)\n\n      expect{parser.evaluate_string(scope, \"assert_no_undef(undef)\")}.to_not raise_error()\n      expect{parser.evaluate_string(scope, \"assert_no_undef([undef])\")}.to_not raise_error()\n      expect{parser.evaluate_string(scope, \"assert_no_undef({undef => 1})\")}.to_not raise_error()\n      expect{parser.evaluate_string(scope, \"assert_no_undef({1 => undef})\")}.to_not raise_error()\n    end\n\n    it 'a lambda return value is checked using the return type' do\n      expect(parser.evaluate_string(scope, \"[1,2].map |Integer $x| >> Integer { $x }\")).to eql([1,2])\n      expect { parser.evaluate_string(scope, \"[1,2].map |Integer $x| >> String { $x }\") }.to raise_error(\n        /value returned from lambda has wrong type, expects a String value, got Integer/)\n    end\n\n    context 'using the 3x function api' do\n      it 'can call a 3x function' do\n        Puppet::Parser::Functions.newfunction(\"bazinga\", :type => :rvalue) { |args| args[0] }\n        expect(parser.evaluate_string(scope, \"bazinga(42)\", __FILE__)).to eq(42)\n      end\n\n      it 'maps :undef to empty string' do\n        Puppet::Parser::Functions.newfunction(\"bazinga\", :type => :rvalue) { |args| args[0] }\n        expect(parser.evaluate_string(scope, \"$a = {} bazinga($a[nope])\", __FILE__)).to eq('')\n        expect(parser.evaluate_string(scope, \"bazinga(undef)\", __FILE__)).to eq('')\n      end\n\n      it 'does not map :undef to empty string in arrays' do\n        Puppet::Parser::Functions.newfunction(\"bazinga\", :type => :rvalue) { |args| args[0][0] }\n        expect(parser.evaluate_string(scope, \"$a = {} $b = [$a[nope]] bazinga($b)\", __FILE__)).to eq(nil)\n        expect(parser.evaluate_string(scope, \"bazinga([undef])\", __FILE__)).to eq(nil)\n      end\n\n      it 'does not map :undef to empty string in hashes' do\n        Puppet::Parser::Functions.newfunction(\"bazinga\", :type => :rvalue) { |args| args[0]['a'] }\n        expect(parser.evaluate_string(scope, \"$a = {} $b = {a => $a[nope]} bazinga($b)\", __FILE__)).to eq(nil)\n        expect(parser.evaluate_string(scope, \"bazinga({a => undef})\", __FILE__)).to eq(nil)\n      end\n    end\n  end\n\n  context \"When evaluator performs string interpolation\" do\n    let(:populate) do\n      parser.evaluate_string(scope, \"$a = 10 $b = [1,2,3]\")\n    end\n\n    {\n      '\"value is $a yo\"'                      => \"value is 10 yo\",\n      '\"value is \\$a yo\"'                     => \"value is $a yo\",\n      '\"value is ${a} yo\"'                    => \"value is 10 yo\",\n      '\"value is \\${a} yo\"'                   => \"value is ${a} yo\",\n      '\"value is ${$a} yo\"'                   => \"value is 10 yo\",\n      '\"value is ${$a*2} yo\"'                 => \"value is 20 yo\",\n      '\"value is ${sprintf(\"x%iy\",$a)} yo\"'   => \"value is x10y yo\",\n      '\"value is ${\"x%iy\".sprintf($a)} yo\"'   => \"value is x10y yo\",\n      '\"value is ${[1,2,3]} yo\"'              => \"value is [1, 2, 3] yo\",\n      '\"value is ${/.*/} yo\"'                 => \"value is /.*/ yo\",\n      '$x = undef \"value is $x yo\"'           => \"value is  yo\",\n      '$x = default \"value is $x yo\"'         => \"value is default yo\",\n      '$x = Array[Integer] \"value is $x yo\"'  => \"value is Array[Integer] yo\",\n      '\"value is ${Array[Integer]} yo\"'       => \"value is Array[Integer] yo\",\n    }.each do |source, result|\n        it \"should parse and evaluate the expression '#{source}' to #{result}\" do\n          populate\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)\n        end\n      end\n\n    it \"should parse and evaluate an interpolation of a hash\" do\n      source = '\"value is ${{a=>1,b=>2}} yo\"'\n      # This test requires testing against two options because a hash to string\n      # produces a result that is unordered\n      alt_results = [\"value is {a => 1, b => 2} yo\", \"value is {b => 2, a => 1} yo\" ]\n      populate\n      parse_result = parser.evaluate_string(scope, source, __FILE__)\n      expect(alt_results.include?(parse_result)).to eq(true)\n    end\n\n    it 'should accept a variable with leading underscore when used directly' do\n      source = '$_x = 10; \"$_x\"'\n      expect(parser.evaluate_string(scope, source, __FILE__)).to eql('10')\n    end\n\n    it 'should accept a variable with leading underscore when used as an expression' do\n      source = '$_x = 10; \"${_x}\"'\n      expect(parser.evaluate_string(scope, source, __FILE__)).to eql('10')\n    end\n\n    it 'should accept a numeric variable expressed as $n' do\n      source = '$x = \"abc123def\" =~ /(abc)(123)(def)/; \"${$2}\"'\n      expect(parser.evaluate_string(scope, source, __FILE__)).to eql('123')\n    end\n\n    it 'should accept a numeric variable expressed as just an integer' do\n      source = '$x = \"abc123def\" =~ /(abc)(123)(def)/; \"${2}\"'\n      expect(parser.evaluate_string(scope, source, __FILE__)).to eql('123')\n    end\n\n    it 'should accept a numeric variable expressed as $n in an access operation' do\n      source = '$x = \"abc123def\" =~ /(abc)(123)(def)/; \"${$0[4,3]}\"'\n      expect(parser.evaluate_string(scope, source, __FILE__)).to eql('23d')\n    end\n\n    it 'should accept a numeric variable expressed as just an integer in an access operation' do\n      source = '$x = \"abc123def\" =~ /(abc)(123)(def)/; \"${0[4,3]}\"'\n      expect(parser.evaluate_string(scope, source, __FILE__)).to eql('23d')\n    end\n\n    {\n      '\"value is ${a*2} yo\"'  => :error,\n    }.each do |source, result|\n        it \"should parse and raise error for '#{source}'\" do\n          expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)\n        end\n      end\n  end\n\n  context \"When evaluating variables\" do\n    context \"that are non existing an error is raised for\" do\n      it \"unqualified variable\" do\n        expect { parser.evaluate_string(scope, \"$quantum_gravity\", __FILE__) }.to raise_error(/Unknown variable/)\n      end\n\n      it \"qualified variable\" do\n        expect { parser.evaluate_string(scope, \"$quantum_gravity::graviton\", __FILE__) }.to raise_error(/Unknown variable/)\n      end\n    end\n\n    it \"a lex error should be raised for '$foo::::bar'\" do\n      expect { parser.evaluate_string(scope, \"$foo::::bar\") }.to raise_error(Puppet::ParseErrorWithIssue, /Illegal fully qualified name \\(line: 1, column: 7\\)/)\n    end\n\n    { '$a = $0'   => nil,\n      '$a = $1'   => nil,\n    }.each do |source, value|\n      it \"it is ok to reference numeric unassigned variables '#{source}'\" do\n        expect(parser.evaluate_string(scope, source, __FILE__)).to eq(value)\n      end\n    end\n\n    { '$00 = 0'   => /must be a decimal value/,\n      '$0xf = 0'  => /must be a decimal value/,\n      '$0777 = 0' => /must be a decimal value/,\n      '$123a = 0' => /must be a decimal value/,\n    }.each do |source, error_pattern|\n      it \"should raise an error for '#{source}'\" do\n        expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(error_pattern)\n      end\n    end\n\n    context \"an initial underscore in the last segment of a var name is allowed\" do\n      { '$_a  = 1'   => 1,\n        '$__a = 1'   => 1,\n      }.each do |source, value|\n        it \"as in this example '#{source}'\" do\n          expect(parser.evaluate_string(scope, source, __FILE__)).to eq(value)\n        end\n      end\n    end\n  end\n\n  context \"When evaluating relationships\" do\n    it 'should form a relation with File[a] -> File[b]' do\n      source = \"File[a] -> File[b]\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['File', 'a', '->', 'File', 'b'])\n    end\n\n    it 'should form a relation with resource -> resource' do\n      source = \"notify{a:} -> notify{b:}\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['Notify', 'a', '->', 'Notify', 'b'])\n    end\n\n    it 'should form a relation with [File[a], File[b]] -> [File[x], File[y]]' do\n      source = \"[File[a], File[b]] -> [File[x], File[y]]\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['File', 'a', '->', 'File', 'x'])\n      expect(scope.compiler).to have_relationship(['File', 'b', '->', 'File', 'x'])\n      expect(scope.compiler).to have_relationship(['File', 'a', '->', 'File', 'y'])\n      expect(scope.compiler).to have_relationship(['File', 'b', '->', 'File', 'y'])\n    end\n\n    it 'should form a relation with 3.x resource -> resource' do\n      # Create a 3.x resource since this is the value given as arguments to defined type\n      scope['a_3x_resource']= Puppet::Parser::Resource.new('notify', 'a', {:scope => scope, :file => __FILE__, :line => 1})\n      source = \"$a_3x_resource -> notify{b:}\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['Notify', 'a', '->', 'Notify', 'b'])\n    end\n\n    it 'should tolerate (eliminate) duplicates in operands' do\n      source = \"[File[a], File[a]] -> File[x]\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['File', 'a', '->', 'File', 'x'])\n      expect(scope.compiler.relationships.size).to eq(1)\n    end\n\n    it 'should form a relation with <-' do\n      source = \"File[a] <- File[b]\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['File', 'b', '->', 'File', 'a'])\n    end\n\n    it 'should form a relation with <-' do\n      source = \"File[a] <~ File[b]\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['File', 'b', '~>', 'File', 'a'])\n    end\n\n    it 'should close the gap created by an intermediate empty set' do\n      source = \"[File[a], File[aa]] -> [] ~> [File[b], File[bb]]\"\n      parser.evaluate_string(scope, source, __FILE__)\n      expect(scope.compiler).to have_relationship(['File', 'a',  '~>', 'File', 'b'])\n      expect(scope.compiler).to have_relationship(['File', 'aa', '~>', 'File', 'b'])\n      expect(scope.compiler).to have_relationship(['File', 'a',  '~>', 'File', 'bb'])\n      expect(scope.compiler).to have_relationship(['File', 'aa', '~>', 'File', 'bb'])\n    end\n  end\n\n  context \"When evaluating heredoc\" do\n    it \"evaluates plain heredoc\" do\n      src = \"@(END)\\nThis is\\nheredoc text\\nEND\\n\"\n      expect(parser.evaluate_string(scope, src)).to eq(\"This is\\nheredoc text\\n\")\n    end\n\n    it \"parses heredoc with margin\" do\n      src = [\n      \"@(END)\",\n      \"   This is\",\n      \"   heredoc text\",\n      \"   | END\",\n      \"\"\n      ].join(\"\\n\")\n      expect(parser.evaluate_string(scope, src)).to eq(\"This is\\nheredoc text\\n\")\n    end\n\n    it \"parses heredoc with margin and right newline trim\" do\n      src = [\n      \"@(END)\",\n      \"   This is\",\n      \"   heredoc text\",\n      \"   |- END\",\n      \"\"\n      ].join(\"\\n\")\n      expect(parser.evaluate_string(scope, src)).to eq(\"This is\\nheredoc text\")\n    end\n\n    it \"parses escape specification\" do\n      src = <<-CODE\n      @(END/t)\n      Tex\\\\tt\\\\n\n      |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq(\"Tex\\tt\\\\n\")\n    end\n\n    it \"parses json syntax checked specification\" do\n      src = <<-CODE\n      @(END:json)\n      [\"foo\", \"bar\"]\n      |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq('[\"foo\", \"bar\"]')\n    end\n\n    it \"parses base64 syntax checked specification\" do\n      src = <<-CODE\n      @(END:base64)\n        dGhlIHF1aWNrIHJlZCBmb3g=\n        |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq('dGhlIHF1aWNrIHJlZCBmb3g=')\n    end\n\n    it \"parses pp syntax checked specification\" do\n      src = <<-CODE\n      @(END:pp)\n        $x = 42\n        |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq('$x = 42')\n    end\n\n    it \"parses epp syntax checked specification\" do\n      src = <<-CODE\n      @(END:epp)\n        <% $x = 42 %><%= $x %>\n        |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq('<% $x = 42 %><%= $x %>')\n    end\n\n    it \"parses json syntax checked specification with error and reports it\" do\n      src = <<-CODE\n      @(END:json)\n      ['foo', \"bar\"]\n      |- END\n      CODE\n      expect { parser.evaluate_string(scope, src)}.to raise_error(/Cannot parse invalid JSON string/)\n    end\n\n    it \"parses base syntax checked specification with error and reports it\" do\n      src = <<-CODE\n      @(END:base64)\n        dGhlIHF1aWNrIHJlZCBmb3g\n        |- END\n      CODE\n      expect { parser.evaluate_string(scope, src)}.to raise_error(/Cannot parse invalid Base64 string/)\n    end\n\n    it \"parses pp syntax checked specification with error and reports it\" do\n      src = <<-CODE\n      @(END:pp)\n        $x ==== 42\n        |- END\n      CODE\n      expect{parser.evaluate_string(scope, src)}.to raise_error(/Invalid produced text having syntax: 'pp'/)\n    end\n\n    it \"parses epp syntax checked specification with error and reports it\" do\n      src = <<-CODE\n      @(END:epp)\n        <% $x ==== 42 %>\n        |- END\n      CODE\n      expect{parser.evaluate_string(scope, src)}.to raise_error(/Invalid produced text having syntax: 'epp'/)\n    end\n\n    it \"parses interpolated heredoc expression\" do\n      src = <<-CODE\n      $pname = 'Fjodor'\n      @(\"END\")\n      Hello $pname\n      |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq(\"Hello Fjodor\")\n    end\n\n    it \"parses interpolated heredoc expression with escapes\" do\n      src = <<-CODE\n      $name = 'Fjodor'\n      @(\"END\")\n      Hello\\\\ \\\\$name\n      |- END\n      CODE\n      expect(parser.evaluate_string(scope, src)).to eq(\"Hello\\\\ \\\\Fjodor\")\n    end\n\n  end\n  context \"Handles Deprecations and Discontinuations\" do\n    it 'of import statements' do\n      source = \"\\nimport foo\"\n      # Error references position 5 at the opening '{'\n      # Set file to nil to make it easier to match with line number (no file name in output)\n      expect { parser.evaluate_string(scope, source) }.to raise_error(/'import' has been discontinued.* \\(line: 2, column: 1\\)/)\n    end\n  end\n\n  context \"Detailed Error messages are reported\" do\n    it 'for illegal type references' do\n      source = '1+1 { \"title\": }'\n      # Error references position 5 at the opening '{'\n      # Set file to nil to make it easier to match with line number (no file name in output)\n      expect { parser.evaluate_string(scope, source) }.to raise_error(\n        /Illegal Resource Type expression, expected result to be a type name, or untitled Resource.* \\(line: 1, column: 2\\)/)\n    end\n\n    it 'for non r-value producing <| |>' do\n      expect { parser.parse_string(\"$a = File <| |>\", nil) }.to raise_error(/A Virtual Query does not produce a value \\(line: 1, column: 6\\)/)\n    end\n\n    it 'for non r-value producing <<| |>>' do\n      expect { parser.parse_string(\"$a = File <<| |>>\", nil) }.to raise_error(/An Exported Query does not produce a value \\(line: 1, column: 6\\)/)\n    end\n\n    it 'for non r-value producing define' do\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(:level => :err, :message => \"Invalid use of expression. A 'define' expression does not produce a value\", :line => 1, :pos => 6))\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(:level => :err, :message => 'Classes, definitions, and nodes may only appear at toplevel or inside other classes', :line => 1, :pos => 6))\n      expect { parser.parse_string(\"$a = define foo { }\", nil) }.to raise_error(/2 errors/)\n    end\n\n    it 'for non r-value producing class' do\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(:level => :err, :message => 'Invalid use of expression. A Host Class Definition does not produce a value', :line => 1, :pos => 6))\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(:level => :err, :message => 'Classes, definitions, and nodes may only appear at toplevel or inside other classes', :line => 1, :pos => 6))\n      expect { parser.parse_string(\"$a = class foo { }\", nil) }.to raise_error(/2 errors/)\n    end\n\n    it 'for unclosed quote with indication of start position of string' do\n      source = <<-SOURCE.gsub(/^ {6}/,'')\n      $a = \"xx\n      yyy\n      SOURCE\n      # first char after opening \" reported as being in error.\n      expect { parser.parse_string(source) }.to raise_error(/Unclosed quote after '\"' followed by 'xx\\\\nyy\\.\\.\\.' \\(line: 1, column: 7\\)/)\n    end\n\n    it 'for multiple errors with a summary exception' do\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(:level => :err, :message => 'Invalid use of expression. A Node Definition does not produce a value', :line => 1, :pos => 6))\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(:level => :err, :message => 'Classes, definitions, and nodes may only appear at toplevel or inside other classes', :line => 1, :pos => 6))\n      expect { parser.parse_string(\"$a = node x { }\",nil) }.to raise_error(/2 errors/)\n    end\n\n    it 'for a bad hostname' do\n      expect {\n        parser.parse_string(\"node 'macbook+owned+by+name' { }\", nil)\n      }.to raise_error(/The hostname 'macbook\\+owned\\+by\\+name' contains illegal characters.* \\(line: 1, column: 6\\)/)\n    end\n\n    it 'for a hostname with interpolation' do\n      source = <<-SOURCE.gsub(/^ {6}/,'')\n      $pname = 'fred'\n      node \"macbook-owned-by$pname\" { }\n      SOURCE\n      expect {\n        parser.parse_string(source, nil)\n      }.to raise_error(/An interpolated expression is not allowed in a hostname of a node \\(line: 2, column: 23\\)/)\n    end\n\n  end\n\n  context 'does not leak variables' do\n    it 'local variables are gone when lambda ends' do\n      source = <<-SOURCE\n      [1,2,3].each |$x| { $y = $x}\n      $a = $y\n      SOURCE\n      expect do\n        parser.evaluate_string(scope, source)\n      end.to raise_error(/Unknown variable: 'y'/)\n    end\n\n    it 'lambda parameters are gone when lambda ends' do\n      source = <<-SOURCE\n      [1,2,3].each |$x| { $y = $x}\n      $a = $x\n      SOURCE\n      expect do\n        parser.evaluate_string(scope, source)\n      end.to raise_error(/Unknown variable: 'x'/)\n    end\n\n    it 'does not leak match variables' do\n      source = <<-SOURCE\n      if 'xyz' =~ /(x)(y)(z)/ { notice $2 }\n      case 'abc' {\n        /(a)(b)(c)/ : { $x = $2 }\n      }\n      \"-$x-$2-\"\n      SOURCE\n      expect(parser.evaluate_string(scope, source)).to eq('-b--')\n    end\n  end\n\n  context 'with --tasks' do\n    before(:each) do\n      Puppet[:tasks] = true\n    end\n\n    context 'when evaluating apply' do\n      let(:applicator) { double('apply_executor') }\n\n      it 'invokes an apply_executor' do\n        expect(applicator).to receive(:apply).with(\n          ['arg1', 'arg2'],\n          instance_of(Puppet::Pops::Model::BlockExpression),\n          scope).and_return(:result)\n        src = \"apply('arg1', 'arg2') { }\"\n        Puppet.override(apply_executor: applicator) do\n          expect(parser.evaluate_string(scope, src)).to eq(:result)\n        end\n      end\n\n      it 'passes the declared ast' do\n        expect(applicator).to receive(:apply).with(\n          [['arg1']],\n          instance_of(Puppet::Pops::Model::ResourceExpression),\n          scope).and_return(:result)\n        src = \"apply(['arg1']) { notify { 'hello': } }\"\n        Puppet.override(apply_executor: applicator) do\n          expect(parser.evaluate_string(scope, src)).to eq(:result)\n        end\n      end\n\n      it 'returns a BlockExpression with an empty apply block' do\n        expect(applicator).to receive(:apply).with(\n          [['arg1']],\n          instance_of(Puppet::Pops::Model::BlockExpression),\n          scope).and_return(:result)\n        src = \"apply(['arg1']) { }\"\n        Puppet.override(apply_executor: applicator) do\n          expect(parser.evaluate_string(scope, src)).to eq(:result)\n        end\n      end\n    end\n  end\n\n  matcher :have_relationship do |expected|\n    match do |compiler|\n      op_name = {'->' => :relationship, '~>' => :subscription}\n      compiler.relationships.any? do | relation |\n        relation.source.type == expected[0] &&\n        relation.source.title == expected[1] &&\n        relation.type == op_name[expected[2]] &&\n        relation.target.type == expected[3] &&\n        relation.target.title == expected[4]\n      end\n    end\n\n    failure_message do |actual|\n      \"Relationship #{expected[0]}[#{expected[1]}] #{expected[2]} #{expected[3]}[#{expected[4]}] but was unknown to compiler\"\n    end\n  end\n\n  def collect_notices(code)\n    Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n      parser.evaluate_string(scope, code, __FILE__)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/evaluator_rspec_helper.rb",
    "content": "require 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\nrequire File.join(File.dirname(__FILE__), '/../factory_rspec_helper')\n\nmodule EvaluatorRspecHelper\n  include FactoryRspecHelper\n\n  # Evaluate a Factory wrapper round a model object in top scope + named scope\n  # Optionally pass two or three model objects (typically blocks) to be executed\n  # in top scope, named scope, and then top scope again. If a named_scope is used, it must\n  # be preceded by the name of the scope.\n  # The optional block is executed before the result of the last specified model object\n  # is evaluated. This block gets the top scope as an argument. The intent is to pass\n  # a block that asserts the state of the top scope after the operations.\n  #\n  def evaluate in_top_scope, scopename=\"x\", in_named_scope = nil, in_top_scope_again = nil, &block\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n\n    # compiler creates the top scope if one is not present\n    top_scope = compiler.topscope()\n    # top_scope = Puppet::Parser::Scope.new(compiler)\n\n    evaluator = Puppet::Pops::Evaluator::EvaluatorImpl.new\n    Puppet.override(:loaders => compiler.loaders) do\n      result = evaluator.evaluate(in_top_scope.model, top_scope)\n      if in_named_scope\n        other_scope = Puppet::Parser::Scope.new(compiler)\n        result = evaluator.evaluate(in_named_scope.model, other_scope)\n      end\n      if in_top_scope_again\n        result = evaluator.evaluate(in_top_scope_again.model, top_scope)\n      end\n      if block_given?\n        block.call(top_scope)\n      end\n      result\n    end\n  end\n\n  # Evaluate a Factory wrapper round a model object in top scope + local scope\n  # Optionally pass two or three model objects (typically blocks) to be executed\n  # in top scope, local scope, and then top scope again\n  # The optional block is executed before the result of the last specified model object\n  # is evaluated. This block gets the top scope as an argument. The intent is to pass\n  # a block that asserts the state of the top scope after the operations.\n  #\n  def evaluate_l in_top_scope, in_local_scope = nil, in_top_scope_again = nil, &block\n    node = Puppet::Node.new('localhost')\n    compiler = Puppet::Parser::Compiler.new(node)\n\n    # compiler creates the top scope if one is not present\n    top_scope = compiler.topscope()\n\n    evaluator = Puppet::Pops::Evaluator::EvaluatorImpl.new\n    Puppet.override(:loaders => compiler.loaders) do\n      result = evaluator.evaluate(in_top_scope.model, top_scope)\n      if in_local_scope\n        # This is really bad in 3.x scope\n        top_scope.with_guarded_scope do\n          top_scope.new_ephemeral(true)\n          result = evaluator.evaluate(in_local_scope.model, top_scope)\n        end\n      end\n      if in_top_scope_again\n        result = evaluator.evaluate(in_top_scope_again.model, top_scope)\n      end\n      if block_given?\n        block.call(top_scope)\n      end\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/json_strict_literal_evaluator_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/json_strict_literal_evaluator'\n\ndescribe \"Puppet::Pops::Evaluator::JsonStrictLiteralEvaluator\" do\n  let(:parser) {  Puppet::Pops::Parser::EvaluatingParser.new }\n  let(:leval)  {  Puppet::Pops::Evaluator::JsonStrictLiteralEvaluator.new }\n\n  { \"1\"       => 1,\n    \"3.14\"    => 3.14,\n    \"true\"    => true,\n    \"false\"   => false,\n    \"'1'\"     => '1',\n    \"'a'\"     => 'a',\n    '\"a\"'     => 'a',\n    'a'       => 'a',\n    'a::b'    => 'a::b',\n    'undef'   => nil,\n\n    # collections\n    '[1,2,3]'     => [1,2,3],\n    '{a=>1,b=>2}' => {'a' => 1, 'b' => 2},\n    '[undef]'     => [nil],\n    '{a=>undef}'  => { 'a' => nil }\n\n  }.each do |source, result|\n    it \"evaluates '#{source}' to #{result}\" do\n      expect(leval.literal(parser.parse_string(source))).to eq(result)\n    end\n  end\n\n  [ '1+1', \n    'File',\n    '[1,2, 1+2]',\n    '{a=>1+1}', \n    'Integer[1]', \n    '\"x$y\"', \n    '\"x${y}z\"'\n  ].each do |source|\n    it \"throws :not_literal for non literal expression '#{source}'\" do\n      expect{leval.literal(parser.parse_string('1+1'))}.to throw_symbol(:not_literal)\n    end\n  end\n\n  [ 'default', \n    '/.*/', \n    '{1=>100}', \n    '{undef => 10}',\n    '{default => 10}',\n    '{/.*/ => 10}',\n    '{\"ok\" => {1 => 100}}',\n    '[default]',\n    '[[default]]',\n    '[/.*/]',\n    '[[/.*/]]',\n    '[{1 => 100}]',\n  ].each do |source|\n    it \"throws :not_literal for values not representable as pure JSON '#{source}'\" do\n      expect{leval.literal(parser.parse_string('1+1'))}.to throw_symbol(:not_literal)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/literal_evaluator_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/literal_evaluator'\n\ndescribe \"Puppet::Pops::Evaluator::LiteralEvaluator\" do\n  let(:parser) {  Puppet::Pops::Parser::EvaluatingParser.new }\n  let(:leval)  {  Puppet::Pops::Evaluator::LiteralEvaluator.new }\n\n  { \"1\"       => 1,\n    \"3.14\"    => 3.14,\n    \"true\"    => true,\n    \"false\"   => false,\n    \"'1'\"     => '1',\n    \"'a'\"     => 'a',\n    '\"a\"'     => 'a',\n    'a'       => 'a',\n    'a::b'    => 'a::b',\n    'Boolean[true]' => [true],\n    'Integer[1]' => [1],\n    'Integer[-1]' => [-1],\n    'Integer[-5, -1]' => [-5, -1],\n    'Integer[-5, 5]'  => [-5, 5],\n    # we can't actually represent MIN_INTEGER below, because it's lexed as\n    # UnaryMinusExpression containing a positive LiteralInteger and the integer\n    # must be <= MAX_INTEGER. Therefore, the effective minimum is one greater.\n    \"Integer[#{Puppet::Pops::MIN_INTEGER + 1}]\" => [-0x7FFFFFFFFFFFFFFF],\n    \"Integer[0, #{Puppet::Pops::MAX_INTEGER}]\"  => [0, 0x7FFFFFFFFFFFFFFF],\n    'Integer[0, default]'         => [0, :default],\n    'Integer[Infinity]'           => ['infinity'],\n    'Float[Infinity]'             => ['infinity'],\n    'Array[Integer, 1]'           => ['integer', 1],\n    'Hash[Integer, String, 1, 3]' => ['integer', 'string', 1, 3],\n    'Regexp[/-1/]'                => [/-1/],\n    'Sensitive[-1]'               => [-1],\n    'Timespan[-5, 5]'             => [-5, 5],\n    'Timestamp[\"2012-10-10\", 1]'  => ['2012-10-10', 1],\n    'Undef' => 'undef',\n    'File' => \"file\",\n\n    # special values\n    'default' => :default,\n    '/.*/'    => /.*/,\n\n    # collections\n    '[1,2,3]'     => [1,2,3],\n    '{a=>1,b=>2}' => {'a' => 1, 'b' => 2},\n\n  }.each do |source, result|\n    it \"evaluates '#{source}' to #{result}\" do\n      expect(leval.literal(parser.parse_string(source))).to eq(result)\n    end\n  end\n\n  it \"evaluates undef to nil\" do\n    expect(leval.literal(parser.parse_string('undef'))).to be_nil\n  end\n\n  [ '',\n    '1+1',\n    '[1,2, 1+2]',\n    '{a=>1+1}',\n    '\"x$y\"',\n    '\"x${y}z\"',\n    'Integer[1-3]',\n    'Integer[-1-3]',\n    'Optional[[String]]'\n  ].each do |source|\n    it \"throws :not_literal for non literal expression '#{source}'\" do\n      expect{leval.literal(parser.parse_string(source))}.to throw_symbol(:not_literal)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/logical_ops_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When the evaluator performs boolean operations\" do\n    context \"using operator AND\" do\n      it \"true  && true  == true\" do\n        expect(evaluate(literal(true).and(literal(true)))).to eq(true)\n      end\n\n      it \"false && true  == false\" do\n        expect(evaluate(literal(false).and(literal(true)))).to eq(false)\n      end\n\n      it \"true  && false == false\" do\n        expect(evaluate(literal(true).and(literal(false)))).to eq(false)\n      end\n\n      it \"false && false == false\" do\n        expect(evaluate(literal(false).and(literal(false)))).to eq(false)\n      end\n    end\n\n    context \"using operator OR\" do\n      it \"true  || true  == true\" do\n        expect(evaluate(literal(true).or(literal(true)))).to eq(true)\n      end\n\n      it \"false || true  == true\" do\n        expect(evaluate(literal(false).or(literal(true)))).to eq(true)\n      end\n\n      it \"true  || false == true\" do\n        expect(evaluate(literal(true).or(literal(false)))).to eq(true)\n      end\n\n      it \"false || false == false\" do\n        expect(evaluate(literal(false).or(literal(false)))).to eq(false)\n      end\n    end\n\n    context \"using operator NOT\" do\n      it \"!false         == true\" do\n        expect(evaluate(literal(false).not())).to eq(true)\n      end\n\n      it \"!true          == false\" do\n        expect(evaluate(literal(true).not())).to eq(false)\n      end\n    end\n\n    context \"on values requiring boxing to Boolean\" do\n      it \"'x'            == true\" do\n        expect(evaluate(literal('x').not())).to eq(false)\n      end\n\n      it \"''             == true\" do\n        expect(evaluate(literal('').not())).to eq(false)\n      end\n\n      it \":undef         == false\" do\n        expect(evaluate(literal(:undef).not())).to eq(true)\n      end\n    end\n\n    context \"connectives should stop when truth is obtained\" do\n      it \"true && false && error  == false (and no failure)\" do\n        expect(evaluate(literal(false).and(literal('0xwtf') + literal(1)).and(literal(true)))).to eq(false)\n      end\n\n      it \"false || true || error  == true (and no failure)\" do\n        expect(evaluate(literal(true).or(literal('0xwtf') + literal(1)).or(literal(false)))).to eq(true)\n      end\n\n      it \"false || false || error == error (false positive test)\" do\n        # TODO: Change the exception type\n        expect {evaluate(literal(true).and(literal('0xwtf') + literal(1)).or(literal(false)))}.to raise_error(Puppet::ParseError)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/runtime3_converter_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/types/type_factory'\n\ndescribe 'when converting to 3.x' do\n  let(:converter) { Puppet::Pops::Evaluator::Runtime3Converter.instance }\n\n  it \"converts a resource type starting with Class without confusing it with exact match on 'class'\" do\n    t = Puppet::Pops::Types::TypeFactory.resource('classroom', 'kermit')\n    converted = converter.catalog_type_to_split_type_title(t)\n    expect(converted).to eql(['classroom', 'kermit'])\n  end\n\n  it \"converts a resource type of exactly 'Class'\" do\n    t = Puppet::Pops::Types::TypeFactory.resource('class', 'kermit')\n    converted = converter.catalog_type_to_split_type_title(t)\n    expect(converted).to eql(['class', 'kermit'])\n  end\n\n  it \"errors on attempts to convert an 'Iterator'\" do\n    expect {\n      converter.convert(Puppet::Pops::Types::Iterable.on((1..3)), {}, nil)\n    }.to raise_error(Puppet::Error, /Use of an Iterator is not supported here/)\n  end\n\n  it 'does not convert a SemVer instance to string' do\n    v = SemanticPuppet::Version.parse('1.0.0')\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'converts top level symbol :undef to the given undef value' do\n    expect(converter.convert(:undef, {}, 'undef value')).to eql('undef value')\n  end\n\n  it 'converts top level nil value to the given undef value' do\n    expect(converter.convert(nil, {}, 'undef value')).to eql('undef value')\n  end\n\n  it 'converts nested :undef in a hash to nil' do\n    expect(converter.convert({'foo' => :undef}, {}, 'undef value')).to eql({'foo' => nil})\n  end\n\n  it 'converts nested :undef in an array to nil' do\n    expect(converter.convert(['boo', :undef], {}, 'undef value')).to eql(['boo', nil])\n  end\n\n  it 'converts deeply nested :undef in to nil' do\n    expect(converter.convert({'foo' => [[:undef]], 'bar' => { 'x' => :undef }}, {}, 'undef value')).to eql({'foo' => [[nil]], 'bar' => { 'x' => nil}})\n  end\n\n  it 'does not converts nil to given undef value when nested in an array' do\n    expect(converter.convert({'foo' => nil}, {}, 'undef value')).to eql({'foo' => nil})\n  end\n\n  it 'does not convert top level symbols' do\n    expect(converter.convert(:default, {}, 'undef value')).to eql(:default)\n    expect(converter.convert(:whatever, {}, 'undef value')).to eql(:whatever)\n  end\n\n  it 'does not convert nested symbols' do\n    expect(converter.convert([:default], {}, 'undef value')).to eql([:default])\n    expect(converter.convert([:whatever], {}, 'undef value')).to eql([:whatever])\n  end\n\n  it 'does not convert a Regex instance to string' do\n    v = /^[A-Z]$/\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'does not convert a Version instance to string' do\n    v = SemanticPuppet::Version.parse('1.0.0')\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'does not convert a VersionRange instance to string' do\n    v = SemanticPuppet::VersionRange.parse('>=1.0.0')\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'does not convert a Timespan instance to string' do\n    v = Puppet::Pops::Time::Timespan.new(1234)\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'does not convert a Timestamp instance to string' do\n    v = Puppet::Pops::Time::Timestamp.now\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'does not convert a Sensitive instance to string' do\n    v = Puppet::Pops::Types::PSensitiveType::Sensitive.new(\"don't reveal this\")\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  it 'does not convert a Binary instance to string' do\n    v = Puppet::Pops::Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n    expect(converter.convert(v, {}, nil)).to equal(v)\n  end\n\n  context 'the Runtime3FunctionArgumentConverter' do\n    let(:converter) { Puppet::Pops::Evaluator::Runtime3FunctionArgumentConverter.instance }\n\n    it 'converts a Regex instance to string' do\n      c = converter.convert(/^[A-Z]$/, {}, nil)\n      expect(c).to be_a(String)\n      expect(c).to eql('/^[A-Z]$/')\n    end\n\n    it 'converts a Version instance to string' do\n      c = converter.convert(SemanticPuppet::Version.parse('1.0.0'), {}, nil)\n      expect(c).to be_a(String)\n      expect(c).to eql('1.0.0')\n    end\n\n    it 'converts a VersionRange instance to string' do\n      c = converter.convert(SemanticPuppet::VersionRange.parse('>=1.0.0'), {}, nil)\n      expect(c).to be_a(String)\n      expect(c).to eql('>=1.0.0')\n    end\n\n    it 'converts a Timespan instance to string' do\n      c = converter.convert(Puppet::Pops::Time::Timespan.new(1234), {}, nil)\n      expect(c).to be_a(String)\n      expect(c).to eql('0-00:00:00.1234')\n    end\n\n    it 'converts a Timestamp instance to string' do\n      c = converter.convert(Puppet::Pops::Time::Timestamp.parse('2016-09-15T12:24:47.193 UTC'), {}, nil)\n      expect(c).to be_a(String)\n      expect(c).to eql('2016-09-15T12:24:47.193000000 UTC')\n    end\n\n    it 'converts a Binary instance to string' do\n      b64 = 'w5ZzdGVuIG1lZCByw7ZzdGVuCg=='\n      c = converter.convert(Puppet::Pops::Types::PBinaryType::Binary.from_base64(b64), {}, nil)\n      expect(c).to be_a(String)\n      expect(c).to eql(b64)\n    end\n\n    it 'does not convert a Sensitive instance to string' do\n      v = Puppet::Pops::Types::PSensitiveType::Sensitive.new(\"don't reveal this\")\n      expect(converter.convert(v, {}, nil)).to equal(v)\n    end\n\n    it 'errors if an Integer is too big' do\n      too_big = 0x7fffffffffffffff + 1\n      expect do\n        converter.convert(too_big, {}, nil)\n        end.to raise_error(/Use of a Ruby Integer outside of Puppet Integer max range, got/)\n    end\n\n    it 'errors if an Integer is too small' do\n      too_small = -0x8000000000000000-1\n      expect do\n        converter.convert(too_small, {}, nil)\n      end.to raise_error(/Use of a Ruby Integer outside of Puppet Integer min range, got/)\n    end\n\n    it 'errors if a BigDecimal is out of range for Float' do\n      big_dec = BigDecimal(\"123456789123456789.1415\")\n      expect do\n        converter.convert(big_dec, {}, nil)\n      end.to raise_error(/Use of a Ruby BigDecimal value outside Puppet Float range, got/)\n    end\n\n    it 'BigDecimal values in Float range are converted' do\n      big_dec = BigDecimal(\"3.1415\")\n      f = converter.convert(big_dec, {}, nil)\n      expect(f.class).to be(Float)\n    end\n\n    it 'errors when Integer is out of range in a structure' do\n      structure = {'key' => [{ 'key' => [0x7fffffffffffffff + 1]}]}\n      expect do\n        converter.convert(structure, {}, nil)\n        end.to raise_error(/Use of a Ruby Integer outside of Puppet Integer max range, got/)\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/string_interpolation_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When evaluator performs string interpolation\" do\n    it \"should interpolate a bare word as a variable name, \\\"${var}\\\"\" do\n      a_block = block(var('a').set(literal(10)), string('value is ', text(fqn('a')), ' yo'))\n      expect(evaluate(a_block)).to eq('value is 10 yo')\n    end\n\n    it \"should interpolate a variable in a text expression, \\\"${$var}\\\"\" do\n      a_block = block(var('a').set(literal(10)), string('value is ', text(var(fqn('a'))), ' yo'))\n      expect(evaluate(a_block)).to eq('value is 10 yo')\n    end\n\n    it \"should interpolate a variable, \\\"$var\\\"\" do\n      a_block = block(var('a').set(literal(10)), string('value is ', var(fqn('a')), ' yo'))\n      expect(evaluate(a_block)).to eq('value is 10 yo')\n    end\n\n    it \"should interpolate any expression in a text expression, \\\"${$var*2}\\\"\" do\n      a_block = block(var('a').set(literal(5)), string('value is ', text(var(fqn('a')) * literal(2)) , ' yo'))\n      expect(evaluate(a_block)).to eq('value is 10 yo')\n    end\n\n    it \"should interpolate any expression without a text expression, \\\"${$var*2}\\\"\" do\n      # there is no concrete syntax for this, but the parser can generate this simpler\n      # equivalent form where the expression is not wrapped in a TextExpression\n      a_block = block(var('a').set(literal(5)), string('value is ', var(fqn('a')) * literal(2) , ' yo'))\n      expect(evaluate(a_block)).to eq('value is 10 yo')\n    end\n\n    # TODO: Add function call tests - Pending implementation of calls in the evaluator\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/evaluator/variables_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\n\n\n# This file contains basic testing of variable references and assignments\n# using a top scope and a local scope.\n# It does not test variables and named scopes.\n#\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Impl::EvaluatorImpl' do\n  include EvaluatorRspecHelper\n\n  context \"When the evaluator deals with variables\" do\n    context \"it should handle\" do\n      it \"simple assignment and dereference\" do\n        expect(evaluate_l(block( var('a').set(literal(2)+literal(2)), var('a')))).to eq(4)\n      end\n\n      it \"local scope shadows top scope\" do\n        top_scope_block   = block( var('a').set(literal(2)+literal(2)), var('a'))\n        local_scope_block = block( var('a').set(var('a') + literal(2)), var('a'))\n        expect(evaluate_l(top_scope_block, local_scope_block)).to eq(6)\n      end\n\n      it \"shadowed in local does not affect parent scope\" do\n        top_scope_block   = block( var('a').set(literal(2)+literal(2)), var('a'))\n        local_scope_block = block( var('a').set(var('a') + literal(2)), var('a'))\n        top_scope_again = var('a')\n        expect(evaluate_l(top_scope_block, local_scope_block, top_scope_again)).to eq(4)\n      end\n\n      it \"access to global names works in top scope\" do\n        top_scope_block   = block( var('a').set(literal(2)+literal(2)), var('::a'))\n        expect(evaluate_l(top_scope_block)).to eq(4)\n      end\n\n      it \"access to global names works in local scope\" do\n        top_scope_block     = block( var('a').set(literal(2)+literal(2)))\n        local_scope_block   = block( var('a').set(literal(100)), var('b').set(var('::a')+literal(2)), var('b'))\n        expect(evaluate_l(top_scope_block, local_scope_block)).to eq(6)\n      end\n\n      it \"can not change a variable value in same scope\" do\n        expect { evaluate_l(block(var('a').set(literal(10)), var('a').set(literal(20)))) }.to raise_error(/Cannot reassign variable '\\$a'/)\n      end\n\n      context \"access to numeric variables\" do\n        it \"without a match\" do\n          expect(evaluate_l(block(literal(2) + literal(2),\n            [var(0), var(1), var(2), var(3)]))).to eq([nil, nil, nil, nil])\n        end\n\n        it \"after a match\" do\n          expect(evaluate_l(block(literal('abc') =~ literal(/(a)(b)(c)/),\n            [var(0), var(1), var(2), var(3)]))).to eq(['abc', 'a', 'b', 'c'])\n        end\n\n        it \"after a failed match\" do\n          expect(evaluate_l(block(literal('abc') =~ literal(/(x)(y)(z)/),\n            [var(0), var(1), var(2), var(3)]))).to eq([nil, nil, nil, nil])\n        end\n\n        it \"a failed match does not alter previous match\" do\n          expect(evaluate_l(block(\n            literal('abc') =~ literal(/(a)(b)(c)/),\n            literal('abc') =~ literal(/(x)(y)(z)/),\n            [var(0), var(1), var(2), var(3)]))).to eq(['abc', 'a', 'b', 'c'])\n        end\n\n        it \"a new match completely shadows previous match\" do\n          expect(evaluate_l(block(\n            literal('abc') =~ literal(/(a)(b)(c)/),\n            literal('abc') =~ literal(/(a)bc/),\n            [var(0), var(1), var(2), var(3)]))).to eq(['abc', 'a', nil, nil])\n        end\n\n        it \"after a match with variable referencing a non existing group\" do\n          expect(evaluate_l(block(literal('abc') =~ literal(/(a)(b)(c)/),\n            [var(0), var(1), var(2), var(3), var(4)]))).to eq(['abc', 'a', 'b', 'c', nil])\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/factory_rspec_helper.rb",
    "content": "require 'puppet/pops'\n\nmodule FactoryRspecHelper\n  def literal(x)\n    Puppet::Pops::Model::Factory.literal(x)\n  end\n\n  def block(*args)\n    Puppet::Pops::Model::Factory.block(*args)\n  end\n\n  def var(x)\n    Puppet::Pops::Model::Factory.var(x)\n  end\n\n  def fqn(x)\n    Puppet::Pops::Model::Factory.fqn(x)\n  end\n\n  def string(*args)\n    Puppet::Pops::Model::Factory.string(*args)\n  end\n\n  def minus(x)\n    Puppet::Pops::Model::Factory.minus(x)\n  end\n\n  def IF(test, then_expr, else_expr=nil)\n    Puppet::Pops::Model::Factory.IF(test, then_expr, else_expr)\n  end\n\n  def UNLESS(test, then_expr, else_expr=nil)\n    Puppet::Pops::Model::Factory.UNLESS(test, then_expr, else_expr)\n  end\n\n  def CASE(test, *options)\n    Puppet::Pops::Model::Factory.CASE(test, *options)\n  end\n\n  def WHEN(values, block)\n    Puppet::Pops::Model::Factory.WHEN(values, block)\n  end\n\n  def method_missing(method, *args, &block)\n    if Puppet::Pops::Model::Factory.respond_to? method\n      Puppet::Pops::Model::Factory.send(method, *args, &block)\n    else\n      super\n    end\n  end\n\n  # i.e. Selector Entry 1 => 'hello'\n  def MAP(match, value)\n    Puppet::Pops::Model::Factory.MAP(match, value)\n  end\n\n  def dump(x)\n    Puppet::Pops::Model::ModelTreeDumper.new.dump(x)\n  end\n\n  def unindent x\n    x.gsub(/^#{x[/\\A\\s*/]}/, '').chomp\n  end\n  factory ||= Puppet::Pops::Model::Factory\nend\n"
  },
  {
    "path": "spec/unit/pops/factory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire File.join(File.dirname(__FILE__), '/factory_rspec_helper')\n\n# This file contains testing of the pops model factory\n#\n\ndescribe Puppet::Pops::Model::Factory do\n  include FactoryRspecHelper\n\n  context \"When factory methods are invoked they should produce expected results\" do\n    it \"tests #var should create a VariableExpression\" do\n      expect(var('a').model.class).to eq(Puppet::Pops::Model::VariableExpression)\n    end\n\n    it \"tests #fqn should create a QualifiedName\" do\n      expect(fqn('a').model.class).to eq(Puppet::Pops::Model::QualifiedName)\n    end\n\n    it \"tests #QNAME should create a QualifiedName\" do\n      expect(QNAME('a').model.class).to eq(Puppet::Pops::Model::QualifiedName)\n    end\n\n    it \"tests #QREF should create a QualifiedReference\" do\n      expect(QREF('a').model.class).to eq(Puppet::Pops::Model::QualifiedReference)\n    end\n\n    it \"tests #block should create a BlockExpression\" do\n      expect(block().model.is_a?(Puppet::Pops::Model::BlockExpression)).to eq(true)\n    end\n\n    it \"should create a literal undef on :undef\" do\n      expect(literal(:undef).model.class).to eq(Puppet::Pops::Model::LiteralUndef)\n    end\n\n    it \"should create a literal default on :default\" do\n      expect(literal(:default).model.class).to eq(Puppet::Pops::Model::LiteralDefault)\n    end\n  end\n\n  context \"When calling block_or_expression\" do\n    it \"A single expression should produce identical output\" do\n      expect(block_or_expression([literal(1) + literal(2)]).model.is_a?(Puppet::Pops::Model::ArithmeticExpression)).to eq(true)\n    end\n\n    it \"Multiple expressions should produce a block expression\" do\n      braces = double('braces')\n      allow(braces).to receive(:offset).and_return(0)\n      allow(braces).to receive(:length).and_return(0)\n\n      model = block_or_expression([literal(1) + literal(2), literal(2) + literal(3)], braces, braces).model\n      expect(model.is_a?(Puppet::Pops::Model::BlockExpression)).to eq(true)\n      expect(model.statements.size).to eq(2)\n    end\n  end\n\n  context \"When processing calls with CALL_NAMED\" do\n    it \"Should be possible to state that r-value is required\" do\n      built = call_named('foo', true).model\n      expect(built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression)).to eq(true)\n      expect(built.rval_required).to eq(true)\n    end\n\n    it \"Should produce a call expression without arguments\" do\n      built = call_named('foo', false).model\n      expect(built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression)).to eq(true)\n      expect(built.functor_expr.is_a?(Puppet::Pops::Model::QualifiedName)).to eq(true)\n      expect(built.functor_expr.value).to eq(\"foo\")\n      expect(built.rval_required).to eq(false)\n      expect(built.arguments.size).to eq(0)\n    end\n\n    it \"Should produce a call expression with one argument\" do\n      built = call_named('foo', false, literal(1) + literal(2)).model\n      expect(built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression)).to eq(true)\n      expect(built.functor_expr.is_a?(Puppet::Pops::Model::QualifiedName)).to eq(true)\n      expect(built.functor_expr.value).to eq(\"foo\")\n      expect(built.rval_required).to eq(false)\n      expect(built.arguments.size).to eq(1)\n      expect(built.arguments[0].is_a?(Puppet::Pops::Model::ArithmeticExpression)).to eq(true)\n    end\n\n    it \"Should produce a call expression with two arguments\" do\n      built = call_named('foo', false, literal(1) + literal(2), literal(1) + literal(2)).model\n      expect(built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression)).to eq(true)\n      expect(built.functor_expr.is_a?(Puppet::Pops::Model::QualifiedName)).to eq(true)\n      expect(built.functor_expr.value).to eq(\"foo\")\n      expect(built.rval_required).to eq(false)\n      expect(built.arguments.size).to eq(2)\n      expect(built.arguments[0].is_a?(Puppet::Pops::Model::ArithmeticExpression)).to eq(true)\n      expect(built.arguments[1].is_a?(Puppet::Pops::Model::ArithmeticExpression)).to eq(true)\n    end\n  end\n\n  context \"When creating attribute operations\" do\n    it \"Should produce an attribute operation for =>\" do\n      built = ATTRIBUTE_OP('aname', '=>', literal('x')).model\n      built.is_a?(Puppet::Pops::Model::AttributeOperation)\n      expect(built.operator).to eq('=>')\n      expect(built.attribute_name).to eq(\"aname\")\n      expect(built.value_expr.is_a?(Puppet::Pops::Model::LiteralString)).to eq(true)\n    end\n\n    it \"Should produce an attribute operation for +>\" do\n      built = ATTRIBUTE_OP('aname', '+>', literal('x')).model\n      built.is_a?(Puppet::Pops::Model::AttributeOperation)\n      expect(built.operator).to eq('+>')\n      expect(built.attribute_name).to eq(\"aname\")\n      expect(built.value_expr.is_a?(Puppet::Pops::Model::LiteralString)).to eq(true)\n    end\n  end\n\n  context \"When processing RESOURCE\" do\n    it \"Should create a Resource body\" do\n      built = RESOURCE_BODY(literal('title'), [ATTRIBUTE_OP('aname', '=>', literal('x'))]).model\n      expect(built.is_a?(Puppet::Pops::Model::ResourceBody)).to eq(true)\n      expect(built.title.is_a?(Puppet::Pops::Model::LiteralString)).to eq(true)\n      expect(built.operations.size).to eq(1)\n      expect(built.operations[0].class).to eq(Puppet::Pops::Model::AttributeOperation)\n      expect(built.operations[0].attribute_name).to eq('aname')\n    end\n\n    it \"Should create a RESOURCE without a resource body\" do\n      bodies = []\n      built = RESOURCE(literal('rtype'), bodies).model\n      expect(built.class).to eq(Puppet::Pops::Model::ResourceExpression)\n      expect(built.bodies.size).to eq(0)\n    end\n\n    it \"Should create a RESOURCE with 1 resource body\" do\n      bodies = [] << RESOURCE_BODY(literal('title'), [])\n      built = RESOURCE(literal('rtype'), bodies).model\n      expect(built.class).to eq(Puppet::Pops::Model::ResourceExpression)\n      expect(built.bodies.size).to eq(1)\n      expect(built.bodies[0].title.value).to eq('title')\n    end\n\n    it \"Should create a RESOURCE with 2 resource bodies\" do\n      bodies = [] << RESOURCE_BODY(literal('title'), []) << RESOURCE_BODY(literal('title2'), [])\n      built = RESOURCE(literal('rtype'), bodies).model\n      expect(built.class).to eq(Puppet::Pops::Model::ResourceExpression)\n      expect(built.bodies.size).to eq(2)\n      expect(built.bodies[0].title.value).to eq('title')\n      expect(built.bodies[1].title.value).to eq('title2')\n    end\n  end\n\n  context \"When processing simple literals\" do\n    it \"Should produce a literal boolean from a boolean\" do\n      model = literal(true).model\n      expect(model.class).to eq(Puppet::Pops::Model::LiteralBoolean)\n      expect(model.value).to eq(true)\n      model = literal(false).model\n      expect(model.class).to eq(Puppet::Pops::Model::LiteralBoolean)\n      expect(model.value).to eq(false)\n    end\n  end\n\n  context \"When processing COLLECT\" do\n    it \"should produce a virtual query\" do\n      model = VIRTUAL_QUERY(fqn('a').eq(literal(1))).model\n      expect(model.class).to eq(Puppet::Pops::Model::VirtualQuery)\n      expect(model.expr.class).to eq(Puppet::Pops::Model::ComparisonExpression)\n      expect(model.expr.operator).to eq('==')\n    end\n\n    it \"should produce an export query\" do\n      model = EXPORTED_QUERY(fqn('a').eq(literal(1))).model\n      expect(model.class).to eq(Puppet::Pops::Model::ExportedQuery)\n      expect(model.expr.class).to eq(Puppet::Pops::Model::ComparisonExpression)\n      expect(model.expr.operator).to eq('==')\n    end\n\n    it \"should produce a collect expression\" do\n      q = VIRTUAL_QUERY(fqn('a').eq(literal(1)))\n      built = COLLECT(literal('t'), q, [ATTRIBUTE_OP('name', '=>', literal(3))]).model\n      expect(built.class).to eq(Puppet::Pops::Model::CollectExpression)\n      expect(built.operations.size).to eq(1)\n    end\n\n    it \"should produce a collect expression without attribute operations\" do\n      q = VIRTUAL_QUERY(fqn('a').eq(literal(1)))\n      built = COLLECT(literal('t'), q, []).model\n      expect(built.class).to eq(Puppet::Pops::Model::CollectExpression)\n      expect(built.operations.size).to eq(0)\n    end\n  end\n\n  context \"When processing concatenated string(iterpolation)\" do\n    it \"should handle 'just a string'\" do\n      model = string('blah blah').model\n      expect(model.class).to eq(Puppet::Pops::Model::ConcatenatedString)\n      expect(model.segments.size).to eq(1)\n      expect(model.segments[0].class).to eq(Puppet::Pops::Model::LiteralString)\n      expect(model.segments[0].value).to eq(\"blah blah\")\n    end\n\n    it \"should handle one expression in the middle\" do\n      model = string('blah blah', TEXT(literal(1)+literal(2)), 'blah blah').model\n      expect(model.class).to eq(Puppet::Pops::Model::ConcatenatedString)\n      expect(model.segments.size).to eq(3)\n      expect(model.segments[0].class).to eq(Puppet::Pops::Model::LiteralString)\n      expect(model.segments[0].value).to eq(\"blah blah\")\n      expect(model.segments[1].class).to eq(Puppet::Pops::Model::TextExpression)\n      expect(model.segments[1].expr.class).to eq(Puppet::Pops::Model::ArithmeticExpression)\n      expect(model.segments[2].class).to eq(Puppet::Pops::Model::LiteralString)\n      expect(model.segments[2].value).to eq(\"blah blah\")\n    end\n\n    it \"should handle one expression at the end\" do\n      model = string('blah blah', TEXT(literal(1)+literal(2))).model\n      expect(model.class).to eq(Puppet::Pops::Model::ConcatenatedString)\n      expect(model.segments.size).to eq(2)\n      expect(model.segments[0].class).to eq(Puppet::Pops::Model::LiteralString)\n      expect(model.segments[0].value).to eq(\"blah blah\")\n      expect(model.segments[1].class).to eq(Puppet::Pops::Model::TextExpression)\n      expect(model.segments[1].expr.class).to eq(Puppet::Pops::Model::ArithmeticExpression)\n    end\n\n    it \"should handle only one expression\" do\n      model = string(TEXT(literal(1)+literal(2))).model\n      expect(model.class).to eq(Puppet::Pops::Model::ConcatenatedString)\n      expect(model.segments.size).to eq(1)\n      expect(model.segments[0].class).to eq(Puppet::Pops::Model::TextExpression)\n      expect(model.segments[0].expr.class).to eq(Puppet::Pops::Model::ArithmeticExpression)\n    end\n\n    it \"should handle several expressions\" do\n      model = string(TEXT(literal(1)+literal(2)), TEXT(literal(1)+literal(2))).model\n      expect(model.class).to eq(Puppet::Pops::Model::ConcatenatedString)\n      expect(model.segments.size).to eq(2)\n      expect(model.segments[0].class).to eq(Puppet::Pops::Model::TextExpression)\n      expect(model.segments[0].expr.class).to eq(Puppet::Pops::Model::ArithmeticExpression)\n      expect(model.segments[1].class).to eq(Puppet::Pops::Model::TextExpression)\n      expect(model.segments[1].expr.class).to eq(Puppet::Pops::Model::ArithmeticExpression)\n    end\n\n    it \"should handle no expression\" do\n      model = string().model\n      expect(model.class).to eq(Puppet::Pops::Model::ConcatenatedString)\n      model.segments.size == 0\n    end\n  end\n\n  context \"When processing UNLESS\" do\n    it \"should create an UNLESS expression with then part\" do\n      built = UNLESS(literal(true), literal(1), literal(nil)).model\n      expect(built.class).to eq(Puppet::Pops::Model::UnlessExpression)\n      expect(built.test.class).to eq(Puppet::Pops::Model::LiteralBoolean)\n      expect(built.then_expr.class).to eq(Puppet::Pops::Model::LiteralInteger)\n      expect(built.else_expr.class).to eq(Puppet::Pops::Model::Nop)\n    end\n\n    it \"should create an UNLESS expression with then and else parts\" do\n      built = UNLESS(literal(true), literal(1), literal(2)).model\n      expect(built.class).to eq(Puppet::Pops::Model::UnlessExpression)\n      expect(built.test.class).to eq(Puppet::Pops::Model::LiteralBoolean)\n      expect(built.then_expr.class).to eq(Puppet::Pops::Model::LiteralInteger)\n      expect(built.else_expr.class).to eq(Puppet::Pops::Model::LiteralInteger)\n    end\n  end\n\n  context \"When processing IF\" do\n    it \"should create an IF expression with then part\" do\n      built = IF(literal(true), literal(1), literal(nil)).model\n      expect(built.class).to eq(Puppet::Pops::Model::IfExpression)\n      expect(built.test.class).to eq(Puppet::Pops::Model::LiteralBoolean)\n      expect(built.then_expr.class).to eq(Puppet::Pops::Model::LiteralInteger)\n      expect(built.else_expr.class).to eq(Puppet::Pops::Model::Nop)\n    end\n\n    it \"should create an IF expression with then and else parts\" do\n      built = IF(literal(true), literal(1), literal(2)).model\n      expect(built.class).to eq(Puppet::Pops::Model::IfExpression)\n      expect(built.test.class).to eq(Puppet::Pops::Model::LiteralBoolean)\n      expect(built.then_expr.class).to eq(Puppet::Pops::Model::LiteralInteger)\n      expect(built.else_expr.class).to eq(Puppet::Pops::Model::LiteralInteger)\n    end\n  end\n\n  context \"When processing a Parameter\" do\n    it \"should create a Parameter\" do\n      # PARAM(name, expr)\n      # PARAM(name)\n      #\n    end\n  end\n\n  # LIST, HASH, KEY_ENTRY\n  context \"When processing Definition\" do\n    # DEFINITION(classname, arguments, statements)\n    # should accept empty arguments, and no statements\n  end\n\n  context \"When processing Hostclass\" do\n    # HOSTCLASS(classname, arguments, parent, statements)\n    # parent may be passed as a nop /nil - check this works, should accept empty statements (nil)\n    # should accept empty arguments\n\n  end\n\n  context \"When processing Node\" do\n  end\n\n  # Tested in the evaluator test already, but should be here to test factory assumptions\n  #\n  # TODO: CASE / WHEN\n  # TODO: MAP\nend\n"
  },
  {
    "path": "spec/unit/pops/issues_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe \"Puppet::Pops::Issues\" do\n  include Puppet::Pops::Issues\n\n  it \"should have an issue called NAME_WITH_HYPHEN\" do\n    x = Puppet::Pops::Issues::NAME_WITH_HYPHEN\n    expect(x.class).to eq(Puppet::Pops::Issues::Issue)\n    expect(x.issue_code).to eq(:NAME_WITH_HYPHEN)\n  end\n\n  it \"should should format a message that requires an argument\" do\n    x = Puppet::Pops::Issues::NAME_WITH_HYPHEN\n    expect(x.format(:name => 'Boo-Hoo',\n      :label => Puppet::Pops::Model::ModelLabelProvider.new,\n      :semantic => \"dummy\"\n      )).to eq(\"A String may not have a name containing a hyphen. The name 'Boo-Hoo' is not legal\")\n  end\n\n  it \"should should format a message that does not require an argument\" do\n    x = Puppet::Pops::Issues::NOT_TOP_LEVEL\n    expect(x.format()).to eq(\"Classes, definitions, and nodes may only appear at toplevel or inside other classes\")\n  end\n\nend\n\ndescribe \"Puppet::Pops::IssueReporter\" do\n\n  let(:acceptor) { Puppet::Pops::Validation::Acceptor.new }\n\n  def fake_positioned(number)\n    double(\"positioned_#{number}\", :line => number, :pos => number)\n  end\n\n  def diagnostic(severity,  number, args)\n    Puppet::Pops::Validation::Diagnostic.new(\n      severity,\n      Puppet::Pops::Issues::Issue.new(number) { \"#{severity}#{number}\" },\n      \"#{severity}file\",\n      fake_positioned(number),\n      args)\n  end\n\n  def warning(number, args = {})\n    diagnostic(:warning, number, args)\n  end\n\n  def deprecation(number, args = {})\n    diagnostic(:deprecation, number, args)\n  end\n\n  def error(number, args = {})\n    diagnostic(:error, number, args)\n  end\n\n  context \"given warnings\" do\n\n    before(:each) do\n      acceptor.accept( warning(1) )\n      acceptor.accept( deprecation(1) )\n    end\n\n    it \"emits warnings if told to emit them\" do\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :warning, :message => match(/warning1|deprecation1/)))\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n    end\n\n    it \"does not emit warnings if not told to emit them\" do\n      expect(Puppet::Log).not_to receive(:create)\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, {})\n    end\n\n    it \"emits no warnings if :max_warnings is 0\" do\n      acceptor.accept( warning(2) )\n      Puppet[:max_warnings] = 0\n      expect(Puppet::Log).to receive(:create).once.with(hash_including(:level => :warning, :message => match(/deprecation1/)))\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n    end\n\n    it \"emits no more than 1 warning if :max_warnings is 1\" do\n      acceptor.accept( warning(2) )\n      acceptor.accept( warning(3) )\n      Puppet[:max_warnings] = 1\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :warning, :message => match(/warning1|deprecation1/)))\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n    end\n\n    it \"does not emit more deprecations warnings than the max deprecation warnings\" do\n      acceptor.accept( deprecation(2) )\n      Puppet[:max_deprecations] = 0\n      expect(Puppet::Log).to receive(:create).once.with(hash_including(:level => :warning, :message => match(/warning1/)))\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n    end\n\n    it \"does not emit deprecation warnings, but does emit regular warnings if disable_warnings includes deprecations\" do\n      Puppet[:disable_warnings] = 'deprecations'\n      expect(Puppet::Log).to receive(:create).once.with(hash_including(:level => :warning, :message => match(/warning1/)))\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n    end\n\n    it \"includes diagnostic arguments in logged entry\" do\n      acceptor.accept( warning(2, :n => 'a') )\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :warning, :message => match(/warning1|deprecation1/)))\n      expect(Puppet::Log).to receive(:create).once.with(hash_including(:level => :warning, :message => match(/warning2/), :arguments => {:n => 'a'}))\n      Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n    end\n  end\n\n  context \"given errors\" do\n    it \"logs nothing, but raises the given :message if :emit_errors is repressing error logging\" do\n      acceptor.accept( error(1) )\n      expect(Puppet::Log).not_to receive(:create)\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_errors => false, :message => 'special'})\n      end.to raise_error(Puppet::ParseError, 'special')\n    end\n\n    it \"prefixes :message if a single error is raised\" do\n      acceptor.accept( error(1) )\n      expect(Puppet::Log).not_to receive(:create)\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :message => 'special'})\n      end.to raise_error(Puppet::ParseError, /special error1/)\n    end\n\n    it \"logs nothing and raises immediately if there is only one error\" do\n      acceptor.accept( error(1) )\n      expect(Puppet::Log).not_to receive(:create)\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })\n      end.to raise_error(Puppet::ParseError, /error1/)\n    end\n\n    it \"logs nothing and raises immediately if there are multiple errors but max_errors is 0\" do\n      acceptor.accept( error(1) )\n      acceptor.accept( error(2) )\n      Puppet[:max_errors] = 0\n      expect(Puppet::Log).not_to receive(:create)\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })\n      end.to raise_error(Puppet::ParseError, /error1/)\n    end\n\n    it \"logs the :message if there is more than one allowed error\" do\n      acceptor.accept( error(1) )\n      acceptor.accept( error(2) )\n      expect(Puppet::Log).to receive(:create).exactly(3).times.with(hash_including(:level => :err, :message => match(/error1|error2|special/)))\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :message => 'special'})\n      end.to raise_error(Puppet::ParseError, /Giving up/)\n    end\n\n    it \"emits accumulated errors before raising a 'giving up' message if there are more errors than allowed\" do\n      acceptor.accept( error(1) )\n      acceptor.accept( error(2) )\n      acceptor.accept( error(3) )\n      Puppet[:max_errors] = 2\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :err, :message => match(/error1|error2/)))\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })\n      end.to raise_error(Puppet::ParseError, /3 errors.*Giving up/)\n    end\n\n    it \"emits accumulated errors before raising a 'giving up' message if there are multiple errors but fewer than limits\" do\n      acceptor.accept( error(1) )\n      acceptor.accept( error(2) )\n      acceptor.accept( error(3) )\n      Puppet[:max_errors] = 4\n      expect(Puppet::Log).to receive(:create).exactly(3).times.with(hash_including(:level => :err, :message => match(/error[123]/)))\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })\n      end.to raise_error(Puppet::ParseError, /3 errors.*Giving up/)\n    end\n\n    it \"emits errors regardless of disable_warnings setting\" do\n      acceptor.accept( error(1) )\n      acceptor.accept( error(2) )\n      Puppet[:disable_warnings] = 'deprecations'\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :err, :message => match(/error1|error2/)))\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })\n      end.to raise_error(Puppet::ParseError, /Giving up/)\n    end\n\n    it \"includes diagnostic arguments in raised error\" do\n      acceptor.accept( error(1, :n => 'a') )\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })\n      end.to raise_error(Puppet::ParseErrorWithIssue, /error1/) { |ex| expect(ex.arguments).to eq(:n => 'a')}\n    end\n  end\n\n  context \"given both\" do\n\n    it \"logs warnings and errors\" do\n      acceptor.accept( warning(1) )\n      acceptor.accept( error(1) )\n      acceptor.accept( error(2) )\n      acceptor.accept( error(3) )\n      acceptor.accept( deprecation(1) )\n      Puppet[:max_errors] = 2\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :warning, :message => match(/warning1|deprecation1/)))\n      expect(Puppet::Log).to receive(:create).twice.with(hash_including(:level => :err, :message => match(/error[123]/)))\n      expect do\n        Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })\n      end.to raise_error(Puppet::ParseError, /3 errors.*2 warnings.*Giving up/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/label_provider_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe Puppet::Pops::LabelProvider do\n  class TestLabelProvider\n    include Puppet::Pops::LabelProvider\n  end\n\n  let(:labeler) { TestLabelProvider.new }\n\n  it \"prefixes words that start with a vowel with an 'an'\" do\n    expect(labeler.a_an('owl')).to eq('an owl')\n  end\n\n  it \"prefixes words that start with a consonant with an 'a'\" do\n    expect(labeler.a_an('bear')).to eq('a bear')\n  end\n\n  it \"prefixes non-word characters with an 'a'\" do\n    expect(labeler.a_an('[] expression')).to eq('a [] expression')\n  end\n\n  it \"ignores a single quote leading the word\" do\n    expect(labeler.a_an(\"'owl'\")).to eq(\"an 'owl'\")\n  end\n\n  it \"ignores a double quote leading the word\" do\n    expect(labeler.a_an('\"owl\"')).to eq('an \"owl\"')\n  end\n\n  it \"capitalizes the indefinite article for a word when requested\" do\n    expect(labeler.a_an_uc('owl')).to eq('An owl')\n  end\n\n  it \"raises an error when missing a character to work with\" do\n    expect {\n      labeler.a_an('\"')\n    }.to raise_error(Puppet::DevError, /<\"> does not appear to contain a word/)\n  end\n\n  it \"raises an error when given an empty string\" do\n    expect {\n      labeler.a_an('')\n    }.to raise_error(Puppet::DevError, /<> does not appear to contain a word/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/dependency_loader_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'dependency loader' do\n  include PuppetSpec::Files\n\n  let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() }\n  let(:loaders) { Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, [])) }\n\n  describe 'FileBased module loader' do\n    it 'prints a pretty name for itself when inspected' do\n      module_dir = dir_containing('testmodule', {\n      'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n        'foo.rb' => 'Puppet::Functions.create_function(\"foo\") { def foo; end; }'\n      }}}}})\n\n      loader = loader_for('testmodule', module_dir)\n\n      expect(loader.inspect).to eq(\"(DependencyLoader 'test-dep' [(ModuleLoader::FileBased 'testmodule' 'testmodule')])\")\n      expect(loader.to_s).to eq(\"(DependencyLoader 'test-dep' [(ModuleLoader::FileBased 'testmodule' 'testmodule')])\")\n    end\n\n    it 'load something in global name space raises an error' do\n      module_dir = dir_containing('testmodule', {\n      'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n        'foo.rb' => 'Puppet::Functions.create_function(\"foo\") { def foo; end; }'\n      }}}}})\n\n      loader = loader_for('testmodule', module_dir)\n\n      expect do\n        loader.load_typed(typed_name(:function, 'testmodule::foo')).value\n      end.to raise_error(ArgumentError, /produced mis-matched name, expected 'testmodule::foo', got foo/)\n    end\n\n    it 'can load something in a qualified name space' do\n      module_dir = dir_containing('testmodule', {\n      'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n        'foo.rb' => 'Puppet::Functions.create_function(\"testmodule::foo\") { def foo; end; }'\n      }}}}})\n\n      loader = loader_for('testmodule', module_dir)\n\n      function = loader.load_typed(typed_name(:function, 'testmodule::foo')).value\n\n      expect(function.class.name).to eq('testmodule::foo')\n      expect(function.is_a?(Puppet::Functions::Function)).to eq(true)\n    end\n\n    it 'can load something in a qualified name space more than once' do\n      module_dir = dir_containing('testmodule', {\n      'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n        'foo.rb' => 'Puppet::Functions.create_function(\"testmodule::foo\") { def foo; end; }'\n      }}}}})\n\n      loader = loader_for('testmodule', module_dir)\n\n      function = loader.load_typed(typed_name(:function, 'testmodule::foo')).value\n      expect(function.class.name).to eq('testmodule::foo')\n      expect(function.is_a?(Puppet::Functions::Function)).to eq(true)\n\n      function = loader.load_typed(typed_name(:function, 'testmodule::foo')).value\n      expect(function.class.name).to eq('testmodule::foo')\n      expect(function.is_a?(Puppet::Functions::Function)).to eq(true)\n    end\n\n    describe \"when parsing files from disk\" do\n\n      # First line of Rune version of Rune poem at http://www.columbia.edu/~fdc/utf8/\n      # characters chosen since they will not parse on Windows with codepage 437 or 1252\n      # Section 3.2.1.3 of Ruby spec guarantees that \\u strings are encoded as UTF-8\n      let (:node) { Puppet::Node.new('node') }\n      let (:rune_utf8) { \"\\u16A0\\u16C7\\u16BB\" } # ᚠᛇᚻ\n      let (:code_utf8) do <<-CODE\nPuppet::Functions.create_function('testmodule::foo') {\n  def foo\n    return \\\"#{rune_utf8}\\\"\n  end\n}\n      CODE\n      end\n\n      context 'when loading files from disk' do\n        it 'should always read files as UTF-8' do\n          module_dir = dir_containing('testmodule', {\n          'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n            'foo.rb' => code_utf8\n          }}}}})\n\n          loader = loader_for('testmodule', module_dir)\n\n          function = loader.load_typed(typed_name(:function, 'testmodule::foo')).value\n          expect(function.call({})).to eq(rune_utf8)\n        end\n\n        it 'currently ignores the UTF-8 BOM (Byte Order Mark) when loading module files' do\n          bom = \"\\uFEFF\"\n          module_dir = dir_containing('testmodule', {\n          'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n            'foo.rb' => \"#{bom}#{code_utf8}\"\n          }}}}})\n\n          loader = loader_for('testmodule', module_dir)\n\n          function = loader.load_typed(typed_name(:function, 'testmodule::foo')).value\n          expect(function.call({})).to eq(rune_utf8)\n        end\n      end\n    end\n  end\n\n  def loader_for(name, dir)\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, name, dir)\n      Puppet::Pops::Loader::DependencyLoader.new(static_loader, 'test-dep', [module_loader], loaders.environment)\n  end\n\n  def typed_name(type, name)\n    Puppet::Pops::Loader::TypedName.new(type, name)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/environment_loader_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'Environment loader' do\n  include PuppetSpec::Files\n\n  let(:env_name) { 'spec' }\n  let(:code_dir) { Puppet[:environmentpath] }\n  let(:env_dir) { File.join(code_dir, env_name) }\n  let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_code_dir, env_name, 'modules')]) }\n  let(:populated_code_dir) do\n    dir_contained_in(code_dir, env_name => env_content)\n    PuppetSpec::Files.record_tmp(env_dir)\n    code_dir\n  end\n\n  let(:env_content) {\n    {\n      'lib' => {\n        'puppet' => {\n          'functions' => {\n            'ruby_foo.rb' => <<-RUBY.unindent,\n              Puppet::Functions.create_function(:ruby_foo) do\n                def ruby_foo()\n                  'ruby_foo'\n                end\n              end\n              RUBY\n            'environment' => {\n              'ruby_foo.rb' => <<-RUBY.unindent\n                Puppet::Functions.create_function(:'environment::ruby_foo') do\n                  def ruby_foo()\n                    'environment::ruby_foo'\n                  end\n                end\n                RUBY\n            },\n            'someother' => {\n              'ruby_foo.rb' => <<-RUBY.unindent\n                Puppet::Functions.create_function(:'someother::ruby_foo') do\n                  def ruby_foo()\n                    'someother::ruby_foo'\n                  end\n                end\n                RUBY\n            },\n          }\n        }\n      },\n      'functions' => {\n        'puppet_foo.pp' => <<-PUPPET.unindent,\n          function puppet_foo() {\n            'puppet_foo'\n          }\n          PUPPET\n        'environment' => {\n          'puppet_foo.pp' => <<-PUPPET.unindent,\n            function environment::puppet_foo() {\n              'environment::puppet_foo'\n            }\n            PUPPET\n        },\n        'someother' => {\n          'puppet_foo.pp' => <<-PUPPET.unindent,\n            function somether::puppet_foo() {\n              'someother::puppet_foo'\n            }\n            PUPPET\n        }\n      },\n      'types' => {\n        'footype.pp' => <<-PUPPET.unindent,\n          type FooType = Enum['foo', 'bar', 'baz']\n          PUPPET\n        'environment' => {\n          'footype.pp' => <<-PUPPET.unindent,\n            type Environment::FooType = Integer[0,9]\n            PUPPET\n        },\n        'someother' => {\n          'footype.pp' => <<-PUPPET.unindent,\n            type SomeOther::FooType = Float[0.0,9.0]\n            PUPPET\n        }\n      }\n    }\n  }\n\n  before(:each) do\n    Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))\n  end\n\n  after(:each) do\n    Puppet.pop_context\n  end\n\n  def load_or_nil(type, name)\n    found = Puppet::Pops::Loaders.find_loader(nil).load_typed(Puppet::Pops::Loader::TypedName.new(type, name))\n    found.nil? ? nil : found.value\n  end\n\n  context 'loading a Ruby function' do\n    it 'loads from global name space' do\n      function = load_or_nil(:function, 'ruby_foo')\n      expect(function).not_to be_nil\n\n      expect(function.class.name).to eq('ruby_foo')\n      expect(function).to be_a(Puppet::Functions::Function)\n    end\n\n    it 'loads from environment name space' do\n      function = load_or_nil(:function, 'environment::ruby_foo')\n      expect(function).not_to be_nil\n\n      expect(function.class.name).to eq('environment::ruby_foo')\n      expect(function).to be_a(Puppet::Functions::Function)\n    end\n\n    it 'fails to load from namespaces other than global or environment' do\n      function = load_or_nil(:function, 'someother::ruby_foo')\n      expect(function).to be_nil\n    end\n  end\n\n  context 'loading a Puppet function' do\n    it 'loads from global name space' do\n      function = load_or_nil(:function, 'puppet_foo')\n      expect(function).not_to be_nil\n\n      expect(function.class.name).to eq('puppet_foo')\n      expect(function).to be_a(Puppet::Functions::PuppetFunction)\n    end\n\n    it 'loads from environment name space' do\n      function = load_or_nil(:function, 'environment::puppet_foo')\n      expect(function).not_to be_nil\n\n      expect(function.class.name).to eq('environment::puppet_foo')\n      expect(function).to be_a(Puppet::Functions::PuppetFunction)\n    end\n\n    it 'fails to load from namespaces other than global or environment' do\n      function = load_or_nil(:function, 'someother::puppet_foo')\n      expect(function).to be_nil\n    end\n  end\n\n  context 'loading a Puppet type' do\n    it 'loads from global name space' do\n      type = load_or_nil(:type, 'footype')\n      expect(type).not_to be_nil\n\n      expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n      expect(type.name).to eq('FooType')\n    end\n\n    it 'loads from environment name space' do\n      type = load_or_nil(:type, 'environment::footype')\n      expect(type).not_to be_nil\n\n      expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n      expect(type.name).to eq('Environment::FooType')\n    end\n\n    it 'fails to load from namespaces other than global or environment' do\n      type = load_or_nil(:type, 'someother::footype')\n      expect(type).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/loader_paths_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'loader paths' do\n  include PuppetSpec::Files\n\n  let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() }\n  let(:unused_loaders) { Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:'*test*', [])) }\n\n  it 'module loader has smart-paths that prunes unavailable paths' do\n    module_dir = dir_containing('testmodule', {'lib' => {'puppet' => {'functions' =>\n      {'foo.rb' =>\n        'Puppet::Functions.create_function(\"testmodule::foo\") {\n          def foo; end;\n        }'\n      }\n    }}})\n    module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, unused_loaders, 'testmodule', module_dir)\n\n    effective_paths = module_loader.smart_paths.effective_paths(:function)\n\n    expect(effective_paths.size).to be_eql(1)\n    expect(effective_paths[0].generic_path).to be_eql(File.join(module_dir, 'lib', 'puppet', 'functions'))\n  end\n\n  it 'all function smart-paths produces entries if they exist' do\n    module_dir = dir_containing('testmodule', {\n      'lib' => {\n        'puppet' => {\n          'functions' => {'foo4x.rb' => 'ignored in this test'},\n        }}})\n    module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, unused_loaders, 'testmodule', module_dir)\n\n    effective_paths = module_loader.smart_paths.effective_paths(:function)\n\n    expect(effective_paths.size).to eq(1)\n    expect(module_loader.path_index.size).to eq(1)\n    path_index = module_loader.path_index\n    expect(path_index).to include(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo4x.rb'))\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/loader_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\n\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\nmodule Puppet::Pops\nmodule Loader\ndescribe 'The Loader' do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  before(:each) do\n    Puppet[:tasks] = true\n  end\n\n  let(:testing_env) do\n    {\n      'testing' => {\n        'functions' => functions,\n        'lib' => { 'puppet' => lib_puppet },\n        'manifests' => manifests,\n        'modules' => modules,\n        'plans' => plans,\n        'tasks' => tasks,\n        'types' => types,\n      }\n    }\n  end\n\n  let(:functions) { {} }\n  let(:manifests) { {} }\n  let(:modules) { {} }\n  let(:plans) { {} }\n  let(:lib_puppet) { {} }\n  let(:tasks) { {} }\n  let(:types) { {} }\n\n  let(:environments_dir) { Puppet[:environmentpath] }\n\n  let(:testing_env_dir) do\n    dir_contained_in(environments_dir, testing_env)\n    env_dir = File.join(environments_dir, 'testing')\n    PuppetSpec::Files.record_tmp(env_dir)\n    env_dir\n  end\n\n  let(:modules_dir) { File.join(testing_env_dir, 'modules') }\n  let(:env) { Puppet::Node::Environment.create(:testing, [modules_dir]) }\n  let(:node) { Puppet::Node.new('test', :environment => env) }\n  let(:loader) { Loaders.find_loader(nil) }\n  let(:tasks_feature) { false }\n\n  before(:each) do\n    Puppet[:tasks] = tasks_feature\n    loaders = Loaders.new(env)\n    Puppet.push_context(:loaders => loaders)\n    loaders.pre_load\n  end\n\n  after(:each) { Puppet.pop_context }\n\n  context 'when doing discovery' do\n    context 'of things' do\n      it 'finds statically basic types' do\n        expect(loader.discover(:type)).to include(tn(:type, 'integer'))\n      end\n\n      it 'finds statically loaded types' do\n        expect(loader.discover(:type)).to include(tn(:type, 'file'))\n      end\n\n      it 'finds statically loaded Object types' do\n        expect(loader.discover(:type)).to include(tn(:type, 'puppet::ast::accessexpression'))\n      end\n\n      context 'in environment' do\n        let(:types) {\n          {\n            'global.pp' => <<-PUPPET.unindent,\n              type Global = Integer\n              PUPPET\n            'environment' => {\n              'env.pp' => <<-PUPPET.unindent,\n                type Environment::Env = String\n                PUPPET\n            }\n          }\n        }\n\n        let(:functions) {\n          {\n            'globfunc.pp' => 'function globfunc() {}',\n            'environment' => {\n              'envfunc.pp' => 'function environment::envfunc() {}'\n            }\n          }\n        }\n\n        let(:lib_puppet) {\n          {\n            'functions' => {\n              'globrubyfunc.rb' => 'Puppet::Functions.create_function(:globrubyfunc) { def globrubyfunc; end }',\n              'environment' => {\n                'envrubyfunc.rb' => \"Puppet::Functions.create_function(:'environment::envrubyfunc') { def envrubyfunc; end }\",\n              }\n            }\n          }\n        }\n\n        it 'finds global types in environment' do\n          expect(loader.discover(:type)).to include(tn(:type, 'global'))\n        end\n\n        it 'finds global functions in environment' do\n          expect(loader.discover(:function)).to include(tn(:function, 'lookup'))\n        end\n\n        it 'finds types prefixed with Environment in environment' do\n          expect(loader.discover(:type)).to include(tn(:type, 'environment::env'))\n        end\n\n        it 'finds global functions in environment' do\n          expect(loader.discover(:function)).to include(tn(:function, 'globfunc'))\n        end\n\n        it 'finds functions prefixed with Environment in environment' do\n          expect(loader.discover(:function)).to include(tn(:function, 'environment::envfunc'))\n        end\n\n        it 'finds global ruby functions in environment' do\n          expect(loader.discover(:function)).to include(tn(:function, 'globrubyfunc'))\n        end\n\n        it 'finds ruby functions prefixed with Environment in environment' do\n          expect(loader.discover(:function)).to include(tn(:function, 'environment::envrubyfunc'))\n        end\n\n        it 'can filter the list of discovered entries using a block' do\n          expect(loader.discover(:function) { |t| t.name =~ /rubyfunc\\z/ }).to contain_exactly(\n            tn(:function, 'environment::envrubyfunc'),\n            tn(:function, 'globrubyfunc')\n          )\n        end\n\n        context 'with multiple modules' do\n          let(:metadata_json_a) {\n              {\n                'name' => 'example/a',\n                'version' => '0.1.0',\n                'source' => 'git@github.com/example/example-a.git',\n                'dependencies' => [{'name' => 'c', 'version_range' => '>=0.1.0'}],\n                'author' => 'Bob the Builder',\n                'license' => 'Apache-2.0'\n              }\n          }\n\n          let(:metadata_json_b) {\n            {\n              'name' => 'example/b',\n              'version' => '0.1.0',\n              'source' => 'git@github.com/example/example-b.git',\n              'dependencies' => [{'name' => 'c', 'version_range' => '>=0.1.0'}],\n              'author' => 'Bob the Builder',\n              'license' => 'Apache-2.0'\n            }\n          }\n\n          let(:metadata_json_c) {\n            {\n              'name' => 'example/c',\n              'version' => '0.1.0',\n              'source' => 'git@github.com/example/example-c.git',\n              'dependencies' => [],\n              'author' => 'Bob the Builder',\n              'license' => 'Apache-2.0'\n            }\n          }\n\n          let(:modules) {\n            {\n              'a' => {\n                'functions' => a_functions,\n                'lib' => { 'puppet' => a_lib_puppet },\n                'plans' => a_plans,\n                'tasks' => a_tasks,\n                'types' => a_types,\n                'metadata.json' => metadata_json_a.to_json\n              },\n              'b' => {\n                'functions' => b_functions,\n                'lib' => { 'puppet' => b_lib_puppet },\n                'plans' => b_plans,\n                'tasks' => b_tasks,\n                'types' => b_types,\n                'metadata.json' => metadata_json_b.to_json\n              },\n              'c' => {\n                'types' => c_types,\n                'tasks' => c_tasks,\n                'metadata.json' => metadata_json_c.to_json\n              },\n            }\n          }\n\n          let(:a_plans) {\n            {\n              'aplan.pp' => <<-PUPPET.unindent,\n                plan a::aplan() {}\n                PUPPET\n            }\n          }\n\n          let(:a_types) {\n            {\n              'atype.pp' => <<-PUPPET.unindent,\n                type A::Atype = Integer\n                PUPPET\n            }\n          }\n\n          let(:a_tasks) {\n            {\n              'atask' => '',\n            }\n          }\n\n          let(:a_functions) {\n            {\n              'afunc.pp' => 'function a::afunc() {}',\n            }\n          }\n\n          let(:a_lib_puppet) {\n            {\n              'functions' => {\n                'a' => {\n                  'arubyfunc.rb' => \"Puppet::Functions.create_function(:'a::arubyfunc') { def arubyfunc; end }\",\n                }\n              }\n            }\n          }\n\n          let(:b_plans) {\n            {\n              'init.pp' => <<-PUPPET.unindent,\n                plan b() {}\n                PUPPET\n              'aplan.pp' => <<-PUPPET.unindent,\n                plan b::aplan() {}\n                PUPPET\n              'yamlplan.yaml' => '{}',\n              'conflict.yaml' => '{}',\n              'conflict.pp' => <<-PUPPET.unindent,\n                plan b::conflict() {}\n                PUPPET\n            }\n          }\n\n          let(:b_types) {\n            {\n              'atype.pp' => <<-PUPPET.unindent,\n                type B::Atype = Integer\n                PUPPET\n            }\n          }\n\n          let(:b_tasks) {\n            {\n              'init.json' => <<-JSON.unindent,\n                {\n                  \"description\": \"test task b\",\n                  \"parameters\": {}\n                }\n                JSON\n              'init.sh' => \"# doing exactly nothing\\n\",\n              'atask' => \"# doing exactly nothing\\n\",\n              'atask.json' => <<-JSON.unindent,\n                {\n                  \"description\": \"test task b::atask\",\n                  \"input_method\": \"stdin\",\n                  \"parameters\": {\n                    \"string_param\": {\n                      \"description\": \"A string parameter\",\n                      \"type\": \"String[1]\"\n                    },\n                    \"int_param\": {\n                      \"description\": \"An integer parameter\",\n                      \"type\": \"Integer\"\n                    }\n                  }\n                }\n                JSON\n            }\n          }\n\n          let(:b_functions) {\n            {\n              'afunc.pp' => 'function b::afunc() {}',\n            }\n          }\n\n          let(:b_lib_puppet) {\n            {\n              'functions' => {\n                'b' => {\n                  'arubyfunc.rb' => \"Puppet::Functions.create_function(:'b::arubyfunc') { def arubyfunc; end }\",\n                }\n              }\n            }\n          }\n\n          let(:c_types) {\n            {\n              'atype.pp' => <<-PUPPET.unindent,\n                type C::Atype = Integer\n            PUPPET\n            }\n          }\n\n          let(:c_tasks) {\n            {\n              'foo.sh' => <<-SH.unindent,\n                # This is a task that does nothing\n                SH\n              'fee.md' => <<-MD.unindent,\n                This is not a task because it has .md extension\n                MD\n              'fum.conf' => <<-CONF.unindent,\n                text=This is not a task because it has .conf extension\n                CONF\n              'bad_syntax.sh' => '',\n              'bad_syntax.json' => <<-TXT.unindent,\n                text => This is not a task because JSON is unparsable\n                TXT\n              'missing_adjacent.json' => <<-JSON.unindent,\n                {\n                  \"description\": \"This is not a task because there is no adjacent file with the same base name\",\n                  \"parameters\": { \"string_param\": { \"type\": \"String[1]\" } }\n                }\n                JSON\n            }\n          }\n\n          it 'private loader finds types in all modules' do\n            expect(loader.private_loader.discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(\n              contain_exactly(tn(:type, 'a::atype'), tn(:type, 'b::atype'), tn(:type, 'c::atype')))\n          end\n\n          it 'module loader finds types only in itself' do\n            expect(Loaders.find_loader('a').discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(\n              contain_exactly(tn(:type, 'a::atype')))\n          end\n\n          it 'private loader finds functions in all modules' do\n            expect(loader.private_loader.discover(:function) { |t| t.name =~ /^.::.*\\z/ }).to(\n              contain_exactly(tn(:function, 'a::afunc'), tn(:function, 'b::afunc'), tn(:function, 'a::arubyfunc'), tn(:function, 'b::arubyfunc')))\n          end\n\n          it 'module loader finds functions only in itself' do\n            expect(Loaders.find_loader('a').discover(:function) { |t| t.name =~ /^.::.*\\z/ }).to(\n              contain_exactly(tn(:function, 'a::afunc'), tn(:function, 'a::arubyfunc')))\n          end\n\n          it 'discover is only called once on dependent loader' do\n            times_called = 0\n            allow_any_instance_of(ModuleLoaders::FileBased).to receive(:discover).with(:type, nil, Pcore::RUNTIME_NAME_AUTHORITY) { times_called += 1 }.and_return([])\n            expect(loader.private_loader.discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(contain_exactly())\n            expect(times_called).to eq(4)\n          end\n\n          context 'with tasks enabled' do\n            let(:tasks_feature) { true }\n\n            it 'private loader finds plans in all modules' do\n              expect(loader.private_loader.discover(:plan) { |t| t.name =~ /^.(?:::.*)?\\z/ }).to(\n                contain_exactly(tn(:plan, 'b'), tn(:plan, 'a::aplan'), tn(:plan, 'b::aplan'), tn(:plan, 'b::conflict')))\n            end\n\n            it 'module loader finds plans only in itself' do\n              expect(Loaders.find_loader('a').discover(:plan)).to(\n                contain_exactly(tn(:plan, 'a::aplan')))\n            end\n\n            context 'with a yaml plan instantiator defined' do\n              before :each do\n                Puppet.push_context(:yaml_plan_instantiator => double(:create => double('plan')))\n              end\n\n              after :each do\n                Puppet.pop_context\n              end\n\n              it 'module loader finds yaml plans' do\n                expect(Loaders.find_loader('b').discover(:plan)).to(\n                  include(tn(:plan, 'b::yamlplan')))\n              end\n\n              it 'module loader excludes plans with both .pp and .yaml versions' do\n                expect(Loaders.find_loader('b').discover(:plan)).not_to(\n                  include(tn(:plan, 'b::conflict')))\n              end\n            end\n\n            it 'private loader finds types in all modules' do\n              expect(loader.private_loader.discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(\n                contain_exactly(tn(:type, 'a::atype'), tn(:type, 'b::atype'), tn(:type, 'c::atype')))\n            end\n\n            it 'private loader finds tasks in all modules' do\n              expect(loader.private_loader.discover(:task) { |t| t.name =~ /^.(?:::.*)?\\z/ }).to(\n                contain_exactly(tn(:task, 'a::atask'), tn(:task, 'b::atask'), tn(:task, 'b'), tn(:task, 'c::foo')))\n            end\n\n            it 'module loader finds types only in itself' do\n              expect(Loaders.find_loader('a').discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(\n                contain_exactly(tn(:type, 'a::atype')))\n            end\n\n            it 'module loader finds tasks only in itself' do\n              expect(Loaders.find_loader('a').discover(:task) { |t| t.name =~ /^.::.*\\z/ }).to(\n                contain_exactly(tn(:task, 'a::atask')))\n            end\n\n            it 'module loader does not consider files with .md and .conf extension to be tasks' do\n              expect(Loaders.find_loader('c').discover(:task) { |t| t.name =~ /(?:foo|fee|fum)\\z/ }).to(\n                contain_exactly(tn(:task, 'c::foo')))\n            end\n\n            it 'without error_collector, invalid task metadata results in warnings' do\n              logs = []\n              Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n                expect(Loaders.find_loader('c').discover(:task)).to(\n                  contain_exactly(tn(:task, 'c::foo')))\n              end\n              expect(logs.select { |log| log.level == :warning }.map { |log| log.message }).to(\n                contain_exactly(/unexpected token/, /No source besides task metadata was found/)\n              )\n            end\n\n            it 'with error_collector, errors are collected and no warnings are logged' do\n              logs = []\n              error_collector = []\n              Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n                expect(Loaders.find_loader('c').discover(:task, error_collector)).to(\n                  contain_exactly(tn(:task, 'c::foo')))\n              end\n              expect(logs.select { |log| log.level == :warning }.map { |log| log.message }).to be_empty\n              expect(error_collector.size).to eql(2)\n              expect(error_collector.all? { |e| e.is_a?(Puppet::DataTypes::Error) })\n              expect(error_collector.all? { |e| e.issue_code == Puppet::Pops::Issues::LOADER_FAILURE.issue_code })\n              expect(error_collector.map { |e| e.details['original_error'] }).to(\n                contain_exactly(/unexpected token/, /No source besides task metadata was found/)\n              )\n            end\n\n            context 'and an environment without directory' do\n              let(:environments_dir) { tmpdir('loader_spec') }\n              let(:env) { Puppet::Node::Environment.create(:none_such, [modules_dir]) }\n\n              it 'an EmptyLoader is used and module loader finds types' do\n                expect_any_instance_of(Puppet::Pops::Loader::ModuleLoaders::EmptyLoader).to receive(:find).and_return(nil)\n                expect(Loaders.find_loader('a').discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(\n                  contain_exactly(tn(:type, 'a::atype')))\n              end\n\n              it 'an EmptyLoader is used and module loader finds tasks' do\n                expect_any_instance_of(Puppet::Pops::Loader::ModuleLoaders::EmptyLoader).to receive(:find).and_return(nil)\n                expect(Loaders.find_loader('a').discover(:task) { |t| t.name =~ /^.::.*\\z/ }).to(\n                  contain_exactly(tn(:task, 'a::atask')))\n              end\n            end\n          end\n\n          context 'with no explicit dependencies' do\n            let(:modules) do\n              {\n                'a' => {\n                  'functions' => a_functions,\n                  'lib' => { 'puppet' => a_lib_puppet },\n                  'plans' => a_plans,\n                  'tasks' => a_tasks,\n                  'types' => a_types,\n                },\n                'b' => {\n                  'functions' => b_functions,\n                  'lib' => { 'puppet' => b_lib_puppet },\n                  'plans' => b_plans,\n                  'tasks' => b_tasks,\n                  'types' => b_types,\n                },\n                'c' => {\n                  'types' => c_types,\n                },\n              }\n            end\n\n            it 'discover is only called once on dependent loader' do\n              times_called = 0\n              allow_any_instance_of(ModuleLoaders::FileBased).to receive(:discover).with(:type, nil, Pcore::RUNTIME_NAME_AUTHORITY) { times_called += 1 }.and_return([])\n              expect(loader.private_loader.discover(:type) { |t| t.name =~ /^.::.*\\z/ }).to(contain_exactly())\n              expect(times_called).to eq(4)\n            end\n          end\n        end\n      end\n    end\n  end\n\n  def tn(type, name)\n    TypedName.new(type, name)\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/loaders_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\n\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'loader helper classes' do\n  it 'NamedEntry holds values and is frozen' do\n    ne = Puppet::Pops::Loader::Loader::NamedEntry.new('name', 'value', 'origin')\n    expect(ne.frozen?).to be_truthy\n    expect(ne.typed_name).to eql('name')\n    expect(ne.origin).to eq('origin')\n    expect(ne.value).to eq('value')\n  end\n\n  it 'TypedName holds values and is frozen' do\n    tn = Puppet::Pops::Loader::TypedName.new(:function, '::foo::bar')\n    expect(tn.frozen?).to be_truthy\n    expect(tn.type).to eq(:function)\n    expect(tn.name_parts).to eq(['foo', 'bar'])\n    expect(tn.name).to eq('foo::bar')\n    expect(tn.qualified?).to be_truthy\n  end\n\n  it 'TypedName converts name to lower case' do\n    tn = Puppet::Pops::Loader::TypedName.new(:type, '::Foo::Bar')\n    expect(tn.name_parts).to eq(['foo', 'bar'])\n    expect(tn.name).to eq('foo::bar')\n  end\n\n  it 'TypedName is case insensitive' do\n    expect(Puppet::Pops::Loader::TypedName.new(:type, '::Foo::Bar')).to eq(Puppet::Pops::Loader::TypedName.new(:type, '::foo::bar'))\n  end\nend\n\ndescribe 'loaders' do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  let(:module_without_metadata) { File.join(config_dir('wo_metadata_module'), 'modules') }\n  let(:module_without_lib) { File.join(config_dir('module_no_lib'), 'modules') }\n  let(:mix_4x_and_3x_functions) { config_dir('mix_4x_and_3x_functions') }\n  let(:module_with_metadata) { File.join(config_dir('single_module'), 'modules') }\n  let(:dependent_modules_with_metadata) { config_dir('dependent_modules_with_metadata') }\n  let(:no_modules) { config_dir('no_modules') }\n  let(:user_metadata_path) { File.join(dependent_modules_with_metadata, 'modules/user/metadata.json') }\n  let(:usee_metadata_path) { File.join(dependent_modules_with_metadata, 'modules/usee/metadata.json') }\n  let(:usee2_metadata_path) { File.join(dependent_modules_with_metadata, 'modules/usee2/metadata.json') }\n\n  let(:empty_test_env) { environment_for() }\n\n  # Loaders caches the puppet_system_loader, must reset between tests\n\n  before :each do\n    allow(File).to receive(:read).and_call_original\n  end\n\n  context 'when loading pp resource types using auto loading' do\n    let(:pp_resources) { config_dir('pp_resources') }\n    let(:environments) { Puppet::Environments::Directories.new(my_fixture_dir, []) }\n    let(:env) { Puppet::Node::Environment.create(:'pp_resources', [File.join(pp_resources, 'modules')]) }\n    let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new(\"test\", :environment => env)) }\n    let(:loader) { Puppet::Pops::Loaders.loaders.find_loader(nil) }\n\n    before(:each) do\n      Puppet.push_context({ :environments => environments })\n      Puppet.push_context({ :loaders => compiler.loaders })\n    end\n\n    after(:each) do\n      Puppet.pop_context()\n    end\n\n    it 'finds a resource type that resides under <environment root>/.resource_types' do\n      rt = loader.load(:resource_type_pp, 'myresource')\n      expect(rt).to be_a(Puppet::Pops::Resource::ResourceTypeImpl)\n    end\n\n    it 'does not allow additional logic in the file' do\n      expect{loader.load(:resource_type_pp, 'addlogic')}.to raise_error(ArgumentError, /it has additional logic/)\n    end\n\n    it 'does not allow creation of classes other than Puppet::Resource::ResourceType3' do\n      expect{loader.load(:resource_type_pp, 'badcall')}.to raise_error(ArgumentError, /no call to Puppet::Resource::ResourceType3.new found/)\n    end\n\n    it 'does not allow creation of other types' do\n      expect{loader.load(:resource_type_pp, 'wrongname')}.to raise_error(ArgumentError, /produced resource type with the wrong name, expected 'wrongname', actual 'notwrongname'/)\n    end\n\n    it 'errors with message about empty file for files that contain no logic' do\n      expect{loader.load(:resource_type_pp, 'empty')}.to raise_error(ArgumentError, /it is empty/)\n    end\n\n    it 'creates a pcore resource type loader' do\n      pcore_loader = env.loaders.runtime3_type_loader.resource_3x_loader\n      expect(pcore_loader.loader_name).to eq('pcore_resource_types')\n      expect(pcore_loader).to be_a(Puppet::Pops::Loader::ModuleLoaders::FileBased)\n    end\n\n    it 'does not create a pcore resource type loader if requested not to' do\n      env.loaders = nil # clear cached loaders\n      loaders = Puppet::Pops::Loaders.new(env, false, false)\n      expect(loaders.runtime3_type_loader.resource_3x_loader).to be_nil\n    end\n\n    it 'does not create a pcore resource type loader for an empty environment' do\n      loaders = Puppet::Pops::Loaders.new(empty_test_env)\n      expect(loaders.runtime3_type_loader.resource_3x_loader).to be_nil\n    end\n  end\n\n  def expect_loader_hierarchy(loaders, expected_loaders)\n    actual_loaders = []\n\n    loader = loaders.private_environment_loader\n    while loader\n      actual_loaders << [loader.loader_name, loader]\n      loader = loader.parent\n    end\n\n    expect(actual_loaders).to contain_exactly(*expected_loaders)\n  end\n\n  it 'creates a puppet_system loader' do\n    loaders = Puppet::Pops::Loaders.new(empty_test_env)\n    expect(loaders.puppet_system_loader()).to be_a(Puppet::Pops::Loader::ModuleLoaders::FileBased)\n  end\n\n  it 'creates a cached_puppet loader when for_agent is set to true' do\n    loaders = Puppet::Pops::Loaders.new(empty_test_env, true)\n    expect(loaders.puppet_cache_loader()).to be_a(Puppet::Pops::Loader::ModuleLoaders::LibRootedFileBased)\n  end\n\n  it 'creates a cached_puppet loader that can load version 4 functions, version 3 functions, and data types, in that order' do\n    loaders = Puppet::Pops::Loaders.new(empty_test_env, true)\n    expect(loaders.puppet_cache_loader.loadables).to eq([:func_4x, :func_3x, :datatype])\n  end\n\n  it 'does not create a cached_puppet loader when for_agent is the default false value' do\n    loaders = Puppet::Pops::Loaders.new(empty_test_env)\n    expect(loaders.puppet_cache_loader()).to be(nil)\n  end\n\n  it 'creates an environment loader' do\n    loaders = Puppet::Pops::Loaders.new(empty_test_env)\n\n    expect(loaders.public_environment_loader()).to be_a(Puppet::Pops::Loader::SimpleEnvironmentLoader)\n    expect(loaders.public_environment_loader().to_s).to eql(\"(SimpleEnvironmentLoader 'environment')\")\n    expect(loaders.private_environment_loader()).to be_a(Puppet::Pops::Loader::DependencyLoader)\n    expect(loaders.private_environment_loader().to_s).to eql(\"(DependencyLoader 'environment private' [])\")\n  end\n\n  it 'creates a hierarchy of loaders' do\n    expect_loader_hierarchy(\n      Puppet::Pops::Loaders.new(empty_test_env),\n      [\n        [nil,                   Puppet::Pops::Loader::StaticLoader],\n        ['puppet_system',       Puppet::Pops::Loader::ModuleLoaders::LibRootedFileBased],\n        [empty_test_env.name,   Puppet::Pops::Loader::Runtime3TypeLoader],\n        ['environment',         Puppet::Pops::Loader::SimpleEnvironmentLoader],\n        ['environment private', Puppet::Pops::Loader::DependencyLoader],\n      ]\n    )\n  end\n\n  it 'excludes the Runtime3TypeLoader when tasks are enabled' do\n    Puppet[:tasks] = true\n\n    expect_loader_hierarchy(\n      Puppet::Pops::Loaders.new(empty_test_env),\n      [\n        [nil,                   Puppet::Pops::Loader::StaticLoader],\n        ['puppet_system',       Puppet::Pops::Loader::ModuleLoaders::LibRootedFileBased],\n        ['environment',         Puppet::Pops::Loader::ModuleLoaders::EmptyLoader],\n        ['environment private', Puppet::Pops::Loader::DependencyLoader],\n      ]\n    )\n  end\n\n  it 'includes the agent cache loader when for_agent is true' do\n    expect_loader_hierarchy(\n      Puppet::Pops::Loaders.new(empty_test_env, true),\n      [\n        [nil,                   Puppet::Pops::Loader::StaticLoader],\n        ['puppet_system',       Puppet::Pops::Loader::ModuleLoaders::LibRootedFileBased],\n        ['cached_puppet_lib',   Puppet::Pops::Loader::ModuleLoaders::LibRootedFileBased],\n        [empty_test_env.name,   Puppet::Pops::Loader::Runtime3TypeLoader],\n        ['environment',         Puppet::Pops::Loader::SimpleEnvironmentLoader],\n        ['environment private', Puppet::Pops::Loader::DependencyLoader],\n      ],\n    )\n  end\n\n  context 'when loading from a module' do\n    it 'loads a ruby function using a qualified or unqualified name' do\n      loaders = Puppet::Pops::Loaders.new(environment_for(module_with_metadata))\n      modulea_loader = loaders.public_loader_for_module('modulea')\n\n      unqualified_function = modulea_loader.load_typed(typed_name(:function, 'rb_func_a')).value\n      qualified_function = modulea_loader.load_typed(typed_name(:function, 'modulea::rb_func_a')).value\n\n      expect(unqualified_function).to be_a(Puppet::Functions::Function)\n      expect(qualified_function).to be_a(Puppet::Functions::Function)\n      expect(unqualified_function.class.name).to eq('rb_func_a')\n      expect(qualified_function.class.name).to eq('modulea::rb_func_a')\n    end\n\n    it 'loads a puppet function using a qualified name in module' do\n      loaders = Puppet::Pops::Loaders.new(environment_for(module_with_metadata))\n      modulea_loader = loaders.public_loader_for_module('modulea')\n\n      qualified_function = modulea_loader.load_typed(typed_name(:function, 'modulea::hello')).value\n\n      expect(qualified_function).to be_a(Puppet::Functions::Function)\n      expect(qualified_function.class.name).to eq('modulea::hello')\n    end\n\n    it 'loads a puppet function from a module without a lib directory' do\n      loaders = Puppet::Pops::Loaders.new(environment_for(module_without_lib))\n      modulea_loader = loaders.public_loader_for_module('modulea')\n\n      qualified_function = modulea_loader.load_typed(typed_name(:function, 'modulea::hello')).value\n\n      expect(qualified_function).to be_a(Puppet::Functions::Function)\n      expect(qualified_function.class.name).to eq('modulea::hello')\n    end\n\n    it 'loads a puppet function in a sub namespace of module' do\n      loaders = Puppet::Pops::Loaders.new(environment_for(module_with_metadata))\n      modulea_loader = loaders.public_loader_for_module('modulea')\n\n      qualified_function = modulea_loader.load_typed(typed_name(:function, 'modulea::subspace::hello')).value\n\n      expect(qualified_function).to be_a(Puppet::Functions::Function)\n      expect(qualified_function.class.name).to eq('modulea::subspace::hello')\n    end\n\n    it 'loader does not add namespace if not given' do\n      loaders = Puppet::Pops::Loaders.new(environment_for(module_without_metadata))\n\n      moduleb_loader = loaders.public_loader_for_module('moduleb')\n\n      expect(moduleb_loader.load_typed(typed_name(:function, 'rb_func_b'))).to be_nil\n    end\n\n    it 'loader allows loading a function more than once' do\n      allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return('')\n      allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n\n      env = environment_for(File.join(dependent_modules_with_metadata, 'modules'))\n      loaders = Puppet::Pops::Loaders.new(env)\n\n      moduleb_loader = loaders.private_loader_for_module('user')\n      function = moduleb_loader.load_typed(typed_name(:function, 'user::caller')).value\n      expect(function.call({})).to eql(\"usee::callee() was told 'passed value' + I am user::caller()\")\n\n      function = moduleb_loader.load_typed(typed_name(:function, 'user::caller')).value\n      expect(function.call({})).to eql(\"usee::callee() was told 'passed value' + I am user::caller()\")\n    end\n  end\n\n  context 'when loading from a module with metadata' do\n    let(:env) { environment_for(File.join(dependent_modules_with_metadata, 'modules')) }\n    let(:scope) { Puppet::Parser::Compiler.new(Puppet::Node.new(\"test\", :environment => env)).newscope(nil) }\n\n    let(:environmentpath) { my_fixture_dir }\n    let(:node) { Puppet::Node.new('test', :facts => Puppet::Node::Facts.new('facts', {}), :environment => 'dependent_modules_with_metadata') }\n    let(:compiler) { Puppet::Parser::Compiler.new(node) }\n\n    let(:user_metadata) {\n      {\n        'name' => 'test-user',\n        'author' =>  'test',\n        'description' =>  '',\n        'license' =>  '',\n        'source' =>  '',\n        'version' =>  '1.0.0',\n        'dependencies' =>  []\n      }\n    }\n\n    def compile_and_get_notifications(code)\n      Puppet[:code] = code\n      catalog = block_given? ? compiler.compile { |c| yield(compiler.topscope); c } : compiler.compile\n      catalog.resources.map(&:ref).select { |r| r.start_with?('Notify[') }.map { |r| r[7..-2] }\n    end\n\n    around(:each) do |example|\n      # Initialize settings to get a full compile as close as possible to a real\n      # environment load\n      Puppet.settings.initialize_global_settings\n\n      # Initialize loaders based on the environmentpath. It does not work to\n      # just set the setting environmentpath for some reason - this achieves the same:\n      # - first a loader is created, loading directory environments from the fixture (there is\n      # one environment, 'sample', which will be loaded since the node references this\n      # environment by name).\n      # - secondly, the created env loader is set as 'environments' in the puppet context.\n      #\n      environments = Puppet::Environments::Directories.new(environmentpath, [])\n      Puppet.override(:environments => environments) do\n        example.run\n      end\n    end\n\n    it 'all dependent modules are visible' do\n      allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'}, { 'name' => 'test-usee2'} ]).to_json)\n      allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      loaders = Puppet::Pops::Loaders.new(env)\n\n      moduleb_loader = loaders.private_loader_for_module('user')\n      function = moduleb_loader.load_typed(typed_name(:function, 'user::caller')).value\n      expect(function.call({})).to eql(\"usee::callee() was told 'passed value' + I am user::caller()\")\n\n      function = moduleb_loader.load_typed(typed_name(:function, 'user::caller2')).value\n      expect(function.call({})).to eql(\"usee2::callee() was told 'passed value' + I am user::caller2()\")\n    end\n\n    it 'all other modules are visible when tasks are enabled' do\n      Puppet[:tasks] = true\n\n      env = environment_for(File.join(dependent_modules_with_metadata, 'modules'))\n      loaders = Puppet::Pops::Loaders.new(env)\n\n      moduleb_loader = loaders.private_loader_for_module('user')\n      function = moduleb_loader.load_typed(typed_name(:function, 'user::caller')).value\n      expect(function.call({})).to eql(\"usee::callee() was told 'passed value' + I am user::caller()\")\n    end\n\n    [ 'outside a function', 'a puppet function declared under functions', 'a puppet function declared in init.pp', 'a ruby function'].each_with_index do |from, from_idx|\n      [ {:from => from, :called => 'a puppet function declared under functions', :expects => \"I'm the function usee::usee_puppet()\"},\n        {:from => from, :called => 'a puppet function declared in init.pp', :expects => \"I'm the function usee::usee_puppet_init()\"},\n        {:from => from, :called => 'a ruby function', :expects => \"I'm the function usee::usee_ruby()\"} ].each_with_index do |desc, called_idx|\n        case_number = from_idx * 3 + called_idx + 1\n\n        it \"can call #{desc[:called]} from #{desc[:from]} when dependency is present in metadata.json\" do\n          allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_json)\n          allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n          allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n          Puppet[:code] = \"$case_number = #{case_number}\\ninclude ::user\"\n          catalog = compiler.compile\n          resource = catalog.resource('Notify', \"case_#{case_number}\")\n          expect(resource).not_to be_nil\n          expect(resource['message']).to eq(desc[:expects])\n        end\n\n        it \"can call #{desc[:called]} from #{desc[:from]} when no metadata is present\" do\n          times_has_metadata_called = 0\n          allow_any_instance_of(Puppet::Module).to receive('has_metadata?') { times_has_metadata_called += 1 }.and_return(false)\n          Puppet[:code] = \"$case_number = #{case_number}\\ninclude ::user\"\n          catalog = compiler.compile\n          resource = catalog.resource('Notify', \"case_#{case_number}\")\n          expect(resource).not_to be_nil\n          expect(resource['message']).to eq(desc[:expects])\n          expect(times_has_metadata_called).to be >= 1\n        end\n\n        it \"can not call #{desc[:called]} from #{desc[:from]} if dependency is missing in existing metadata.json\" do\n          allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => []).to_json)\n          allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n          allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n          Puppet[:code] = \"$case_number = #{case_number}\\ninclude ::user\"\n          catalog = compiler.compile\n          resource = catalog.resource('Notify', \"case_#{case_number}\")\n          expect(resource).not_to be_nil\n          expect(resource['message']).to eq(desc[:expects])\n        end\n      end\n    end\n\n    it \"a type can reference an autoloaded type alias from another module when dependency is present in metadata.json\" do\n      allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_json)\n      allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok'])\n        assert_type(Usee::Zero, 0)\n        notice(ok)\n      CODE\n    end\n\n    it \"a type can reference an autoloaded type alias from another module when no metadata is present\" do\n      expect_any_instance_of(Puppet::Module).to receive('has_metadata?').at_least(:once).and_return(false)\n      expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok'])\n        assert_type(Usee::Zero, 0)\n        notice(ok)\n      CODE\n    end\n\n    it \"a type can reference a type alias from another module when other module has it declared in init.pp\" do\n      allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_json)\n      allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok'])\n        include 'usee'\n        assert_type(Usee::One, 1)\n        notice(ok)\n      CODE\n    end\n\n    it \"an autoloaded type can reference an autoloaded type alias from another module when dependency is present in metadata.json\" do\n      allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_json)\n      allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok'])\n        assert_type(User::WithUseeZero, [0])\n        notice(ok)\n      CODE\n    end\n\n    it \"an autoloaded type can reference an autoloaded type alias from another module when other module has it declared in init.pp\" do\n      allow(File).to receive(:read).with(user_metadata_path, {:encoding => 'utf-8'}).and_return(user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_json)\n      allow(File).to receive(:read).with(usee_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      allow(File).to receive(:read).with(usee2_metadata_path, {:encoding => 'utf-8'}).and_raise(Errno::ENOENT)\n      expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok'])\n        include 'usee'\n        assert_type(User::WithUseeOne, [1])\n        notice(ok)\n      CODE\n    end\n  end\n\n  context 'when loading from a module without metadata' do\n    it 'loads a ruby function with a qualified name' do\n      loaders = Puppet::Pops::Loaders.new(environment_for(module_without_metadata))\n\n      moduleb_loader = loaders.public_loader_for_module('moduleb')\n      function = moduleb_loader.load_typed(typed_name(:function, 'moduleb::rb_func_b')).value\n\n      expect(function).to be_a(Puppet::Functions::Function)\n      expect(function.class.name).to eq('moduleb::rb_func_b')\n    end\n\n\n    it 'all other modules are visible' do\n      env = environment_for(module_with_metadata, module_without_metadata)\n      loaders = Puppet::Pops::Loaders.new(env)\n\n      moduleb_loader = loaders.private_loader_for_module('moduleb')\n      function = moduleb_loader.load_typed(typed_name(:function, 'moduleb::rb_func_b')).value\n\n      expect(function.call({})).to eql(\"I am modulea::rb_func_a() + I am moduleb::rb_func_b()\")\n    end\n  end\n\n  context 'when loading from an environment without modules' do\n    let(:node) { Puppet::Node.new('test', :facts => Puppet::Node::Facts.new('facts', {}), :environment => 'no_modules') }\n\n    it 'can load the same function twice with two different compilations and produce different values' do\n      Puppet.settings.initialize_global_settings\n      environments = Puppet::Environments::Directories.new(my_fixture_dir, [])\n      Puppet.override(:environments => environments) do\n        compiler = Puppet::Parser::Compiler.new(node)\n        compiler.topscope['value_from_scope'] = 'first'\n        catalog = compiler.compile\n        expect(catalog.resource('Notify[first]')).to be_a(Puppet::Resource)\n\n        expect(Puppet::Pops::Loader::RubyFunctionInstantiator).not_to receive(:create)\n        compiler = Puppet::Parser::Compiler.new(node)\n        compiler.topscope['value_from_scope'] = 'second'\n        catalog = compiler.compile\n        expect(catalog.resource('Notify[first]')).to be_nil\n        expect(catalog.resource('Notify[second]')).to be_a(Puppet::Resource)\n      end\n    end\n  end\n\n  context 'when calling' do\n    let(:env) { environment_for(mix_4x_and_3x_functions) }\n    let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new(\"test\", :environment => env)) }\n    let(:scope) { compiler.topscope }\n    let(:loader) { compiler.loaders.private_loader_for_module('user') }\n\n    before(:each) do\n      Puppet.push_context(:current_environment => scope.environment, :global_scope => scope, :loaders => compiler.loaders)\n    end\n\n    it 'a 3x function in dependent module can be called from a 4x function' do\n      function = loader.load_typed(typed_name(:function, 'user::caller')).value\n      expect(function.call(scope)).to eql(\"usee::callee() got 'first' - usee::callee() got 'second'\")\n    end\n\n    it 'a 3x function in dependent module can be called from a puppet function' do\n      function = loader.load_typed(typed_name(:function, 'user::puppetcaller')).value\n      expect(function.call(scope)).to eql(\"usee::callee() got 'first' - usee::callee() got 'second'\")\n    end\n\n    it 'a 4x function can be called from a puppet function' do\n      function = loader.load_typed(typed_name(:function, 'user::puppetcaller4')).value\n      expect(function.call(scope)).to eql(\"usee::callee() got 'first' - usee::callee() got 'second'\")\n    end\n\n    it 'a puppet function can be called from a 4x function' do\n      function = loader.load_typed(typed_name(:function, 'user::callingpuppet')).value\n      expect(function.call(scope)).to eql(\"Did you call to say you love me?\")\n    end\n\n    it 'a 3x function can be called with caller scope propagated from a 4x function' do\n      function = loader.load_typed(typed_name(:function, 'user::caller_ws')).value\n      expect(function.call(scope, 'passed in scope')).to eql(\"usee::callee_ws() got 'passed in scope'\")\n    end\n\n    it 'calls can be made to a non core function via the call() function' do\n      call_function = loader.load_typed(typed_name(:function, 'call')).value\n      expect(call_function.call(scope, 'user::puppetcaller4')).to eql(\"usee::callee() got 'first' - usee::callee() got 'second'\")\n    end\n\n  end\n\n  context 'when a 3x load takes place' do\n    let(:env) { environment_for(mix_4x_and_3x_functions) }\n    let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new(\"test\", :environment => env)) }\n    let(:scope) { compiler.topscope }\n    let(:loader) { compiler.loaders.private_loader_for_module('user') }\n\n    before(:each) do\n      Puppet.push_context(:current_environment => scope.environment, :global_scope => scope, :loaders => compiler.loaders)\n    end\n    after(:each) do\n      Puppet.pop_context\n    end\n\n    it \"a function with no illegal constructs can be loaded\" do\n      function = loader.load_typed(typed_name(:function, 'good_func_load')).value\n      expect(function.call(scope)).to eql(Float(\"3.14\"))\n    end\n\n    it \"a function with syntax error has helpful error message\" do\n      expect {\n        loader.load_typed(typed_name(:function, 'func_with_syntax_error'))\n      }.to raise_error(/syntax error, unexpected (keyword_|`)?end/)\n    end\n  end\n\n  context 'when a 3x load has illegal method added' do\n    let(:env) { environment_for(mix_4x_and_3x_functions) }\n    let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new(\"test\", :environment => env)) }\n    let(:scope) { compiler.topscope }\n    let(:loader) { compiler.loaders.private_loader_for_module('user') }\n\n    before(:each) do\n      Puppet.push_context(:current_environment => scope.environment, :global_scope => scope, :loaders => compiler.loaders)\n    end\n    after(:each) do\n      Puppet.pop_context\n    end\n\n    it \"outside function body is reported as an error\" do\n      expect { loader.load_typed(typed_name(:function, 'bad_func_load')) }.to raise_error(/Illegal method definition/)\n    end\n\n    it \"to self outside function body is reported as an error\" do\n      expect { loader.load_typed(typed_name(:function, 'bad_func_load5')) }.to raise_error(/Illegal method definition.*'bad_func_load5_illegal_method'/)\n    end\n\n    it \"outside body is reported as an error even if returning the right func_info\" do\n      expect { loader.load_typed(typed_name(:function, 'bad_func_load2'))}.to raise_error(/Illegal method definition/)\n    end\n\n    it \"inside function body is reported as an error\" do\n      expect {\n        f = loader.load_typed(typed_name(:function, 'bad_func_load3')).value\n        f.call(scope)\n      }.to raise_error(/Illegal method definition.*'bad_func_load3_illegal_method'/)\n    end\n\n    it \"to self inside function body is reported as an error\" do\n      expect {\n        f = loader.load_typed(typed_name(:function, 'bad_func_load4')).value\n        f.call(scope)\n      }.to raise_error(/Illegal method definition.*'bad_func_load4_illegal_method'/)\n    end\n  end\n\n  context 'when causing a 3x load followed by a 4x load' do\n    let(:env) { environment_for(mix_4x_and_3x_functions) }\n    let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new(\"test\", :environment => env)) }\n    let(:scope) { compiler.topscope }\n    let(:loader) { compiler.loaders.private_loader_for_module('user') }\n\n\n    before(:each) do\n      Puppet.push_context(:current_environment => scope.environment, :global_scope => scope, :loaders => compiler.loaders)\n    end\n    after(:each) do\n      Puppet.pop_context\n    end\n\n    it 'a 3x function is loaded once' do\n      # create a 3x function that when called will do a load of \"callee_ws\"\n      Puppet::Parser::Functions::newfunction(:callee, :type => :rvalue, :arity => 1) do |args|\n        function_callee_ws(['passed in scope'])\n      end\n      expect(Puppet).not_to receive(:warning)\n      scope['passed_in_scope'] = 'value'\n      function = loader.load_typed(typed_name(:function, 'callee')).value\n      expect(function.call(scope, 'passed in scope')).to eql(\"usee::callee_ws() got 'value'\")\n\n      function = loader.load_typed(typed_name(:function, 'callee_ws')).value\n      expect(function.call(scope, 'passed in scope')).to eql(\"usee::callee_ws() got 'value'\")\n    end\n\n    it \"an illegal function is loaded\" do\n      expect {\n        loader.load_typed(typed_name(:function, 'bad_func_load3')).value\n      }.to raise_error(SecurityError, /Illegal method definition of method 'bad_func_load3_illegal_method' in source .*bad_func_load3.rb on line 8 in legacy function/)\n    end\n  end\n\n  context 'loading' do\n    let(:env_name) { 'testenv' }\n    let(:environments_dir) { Puppet[:environmentpath] }\n    let(:env_dir) { File.join(environments_dir, env_name) }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')]) }\n    let(:node) { Puppet::Node.new(\"test\", :environment => env) }\n    let(:env_dir_files) {}\n\n    let(:populated_env_dir) do\n      dir_contained_in(environments_dir, env_name => env_dir_files)\n      PuppetSpec::Files.record_tmp(env_dir)\n      env_dir\n    end\n\n    context 'non autoloaded types and functions' do\n      let(:env_dir_files) {\n        {\n          'modules' => {\n            'tstf' => {\n              'manifests' => {\n                'init.pp' => <<-PUPPET.unindent\n                  class tstf {\n                    notice(testfunc())\n                  }\n                  PUPPET\n              }\n            },\n            'tstt' => {\n              'manifests' => {\n                'init.pp' => <<-PUPPET.unindent\n                  class tstt {\n                    notice(assert_type(GlobalType, 23))\n                  }\n                  PUPPET\n              }\n            }\n          }\n        }\n      }\n\n      it 'finds the function from a module' do\n        expect(eval_and_collect_notices(<<-PUPPET.unindent, node)).to eq(['hello from testfunc'])\n          function testfunc() {\n            'hello from testfunc'\n          }\n          include 'tstf'\n          PUPPET\n      end\n\n      it 'finds the type from a module' do\n        expect(eval_and_collect_notices(<<-PUPPET.unindent, node)).to eq(['23'])\n          type GlobalType = Integer\n          include 'tstt'\n          PUPPET\n      end\n    end\n\n    context 'types' do\n      let(:env_name) { 'testenv' }\n      let(:environments_dir) { Puppet[:environmentpath] }\n      let(:env_dir) { File.join(environments_dir, env_name) }\n      let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')]) }\n      let(:metadata_json) {\n        <<-JSON\n        {\n          \"name\": \"example/%1$s\",\n          \"version\": \"0.0.2\",\n          \"source\": \"git@github.com/example/example-%1$s.git\",\n          \"dependencies\": [],\n          \"author\": \"Bob the Builder\",\n          \"license\": \"Apache-2.0\"%2$s\n        }\n        JSON\n      }\n\n      let(:env_dir_files) do\n        {\n          'types' => {\n            'c.pp' => 'type C = Integer'\n          },\n          'modules' => {\n            'a' => {\n              'manifests' => {\n                'init.pp' => 'class a { notice(A::A) }'\n              },\n              'types' => {\n                'a.pp' => 'type A::A = Variant[B::B, String]',\n                'n.pp' => 'type A::N = C::C'\n              },\n              'metadata.json' => sprintf(metadata_json, 'a', ', \"dependencies\": [{ \"name\": \"example/b\" }]')\n            },\n            'b' => {\n              'types' => {\n                'b.pp' => 'type B::B = Variant[C::C, Float]',\n                'x.pp' => 'type B::X = A::A'\n              },\n              'metadata.json' => sprintf(metadata_json, 'b', ', \"dependencies\": [{ \"name\": \"example/c\" }]')\n            },\n            'c' => {\n              'types' => {\n                'init_typeset.pp' => <<-PUPPET.unindent,\n                  type C = TypeSet[{\n                    pcore_version => '1.0.0',\n                    types => {\n                      C => Integer,\n                      D => Float\n                    }\n                  }]\n                  PUPPET\n                'd.pp' => <<-PUPPET.unindent,\n                  type C::D = TypeSet[{\n                    pcore_version => '1.0.0',\n                    types => {\n                      X => String,\n                      Y => Float\n                    }\n                  }]\n                  PUPPET\n                'd' => {\n                  'y.pp' => 'type C::D::Y = Integer'\n                }\n              },\n              'metadata.json' => sprintf(metadata_json, 'c', '')\n            },\n            'd' => {\n              'types' => {\n                'init_typeset.pp' => <<-PUPPET.unindent,\n                  type D = TypeSet[{\n                    pcore_version => '1.0.0',\n                    types => {\n                      P => Object[{}],\n                      O => Object[{ parent => P }]\n                    }\n                  }]\n                  PUPPET\n              },\n              'metadata.json' => sprintf(metadata_json, 'd', '')\n            }\n          }\n        }\n      end\n\n      before(:each) do\n        Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))\n      end\n\n      after(:each) do\n        Puppet.pop_context\n      end\n\n      it 'resolves types using the loader that loaded the type a -> b -> c' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('A::A', Puppet::Pops::Loaders.find_loader('a'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.name).to eql('A::A')\n        type = type.resolved_type\n        expect(type).to be_a(Puppet::Pops::Types::PVariantType)\n        type = type.types[0]\n        expect(type.name).to eql('B::B')\n        type = type.resolved_type\n        expect(type).to be_a(Puppet::Pops::Types::PVariantType)\n        type = type.types[0]\n        expect(type.name).to eql('C::C')\n        type = type.resolved_type\n        expect(type).to be_a(Puppet::Pops::Types::PIntegerType)\n      end\n\n      it 'will resolve implicit transitive dependencies, a -> c' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('A::N', Puppet::Pops::Loaders.find_loader('a'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.name).to eql('A::N')\n        type = type.resolved_type\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.name).to eql('C::C')\n      end\n\n      it 'will resolve reverse dependencies, b -> a' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('B::X', Puppet::Pops::Loaders.find_loader('b'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.name).to eql('B::X')\n        type = type.resolved_type\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.name).to eql('A::A')\n      end\n\n      it 'does not resolve init_typeset when more qualified type is found in typeset' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('C::D::X', Puppet::Pops::Loaders.find_loader('c'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.resolved_type).to be_a(Puppet::Pops::Types::PStringType)\n      end\n\n      it 'defined TypeSet type shadows type defined inside of TypeSet' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('C::D', Puppet::Pops::Loaders.find_loader('c'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeSetType)\n      end\n\n      it 'parent name search does not traverse parent loaders' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('C::C', Puppet::Pops::Loaders.find_loader('c'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.resolved_type).to be_a(Puppet::Pops::Types::PIntegerType)\n      end\n\n      it 'global type defined in environment trumps modules init_typeset type' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('C', Puppet::Pops::Loaders.find_loader('c'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.resolved_type).to be_a(Puppet::Pops::Types::PIntegerType)\n      end\n\n      it 'hit on qualified name trumps hit on typeset using parent name + traversal' do\n        type = Puppet::Pops::Types::TypeParser.singleton.parse('C::D::Y', Puppet::Pops::Loaders.find_loader('c'))\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.resolved_type).to be_a(Puppet::Pops::Types::PIntegerType)\n      end\n\n      it 'hit on qualified name and subsequent hit in typeset when searching for other name causes collision' do\n        l = Puppet::Pops::Loaders.find_loader('c')\n        p = Puppet::Pops::Types::TypeParser.singleton\n        p.parse('C::D::Y', l)\n        expect { p.parse('C::D::X', l) }.to raise_error(/Attempt to redefine entity 'http:\\/\\/puppet.com\\/2016.1\\/runtime\\/type\\/c::d::y'/)\n      end\n\n      it 'hit in typeset using parent name and subsequent search that would cause hit on fqn does not cause collision (fqn already loaded from typeset)' do\n        l = Puppet::Pops::Loaders.find_loader('c')\n        p = Puppet::Pops::Types::TypeParser.singleton\n        p.parse('C::D::X', l)\n        type = p.parse('C::D::Y', l)\n        expect(type).to be_a(Puppet::Pops::Types::PTypeAliasType)\n        expect(type.resolved_type).to be_a(Puppet::Pops::Types::PFloatType)\n      end\n\n      it 'loads an object type from a typeset that references another type defined in the same typeset' do\n        l = Puppet::Pops::Loaders.find_loader('d').private_loader\n        p = Puppet::Pops::Types::TypeParser.singleton\n        type = p.parse('D::O', l)\n        expect(type).to be_a(Puppet::Pops::Types::PObjectType)\n        expect(type.resolved_parent).to be_a(Puppet::Pops::Types::PObjectType)\n      end\n    end\n  end\n\n  def environment_for(*module_paths)\n    Puppet::Node::Environment.create(:'*test*', module_paths)\n  end\n\n  def typed_name(type, name)\n    Puppet::Pops::Loader::TypedName.new(type, name)\n  end\n\n  def config_dir(config_name)\n    my_fixture(config_name)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/module_loaders_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\nrequire 'puppet_spec/compiler'\n\ndescribe 'FileBased module loader' do\n  include PuppetSpec::Files\n\n  let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() }\n  let(:loaders) { Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, [])) }\n\n  it 'can load a 4x function API ruby function in global name space' do\n    module_dir = dir_containing('testmodule', {\n      'lib' => {\n        'puppet' => {\n          'functions' => {\n            'foo4x.rb' => <<-CODE\n               Puppet::Functions.create_function(:foo4x) do\n                 def foo4x()\n                   'yay'\n                 end\n               end\n            CODE\n          }\n            }\n          }\n        })\n\n    module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n    function = module_loader.load_typed(typed_name(:function, 'foo4x')).value\n\n    expect(function.class.name).to eq('foo4x')\n    expect(function.is_a?(Puppet::Functions::Function)).to eq(true)\n  end\n\n  it 'can load a 4x function API ruby function in qualified name space' do\n    module_dir = dir_containing('testmodule', {\n      'lib' => {\n        'puppet' => {\n          'functions' => {\n            'testmodule' => {\n              'foo4x.rb' => <<-CODE\n                 Puppet::Functions.create_function('testmodule::foo4x') do\n                   def foo4x()\n                     'yay'\n                   end\n                 end\n              CODE\n              }\n            }\n          }\n      }})\n\n    module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n    function = module_loader.load_typed(typed_name(:function, 'testmodule::foo4x')).value\n    expect(function.class.name).to eq('testmodule::foo4x')\n    expect(function.is_a?(Puppet::Functions::Function)).to eq(true)\n  end\n\n  it 'system loader has itself as private loader' do\n    module_loader = loaders.puppet_system_loader\n    expect(module_loader.private_loader).to be(module_loader)\n  end\n\n  it 'makes parent loader win over entries in child' do\n    module_dir = dir_containing('testmodule', {\n      'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {\n        'foo.rb' => <<-CODE\n           Puppet::Functions.create_function('testmodule::foo') do\n             def foo()\n               'yay'\n             end\n           end\n        CODE\n      }}}}})\n    module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n\n    module_dir2 = dir_containing('testmodule2', {\n      'lib' => { 'puppet' => { 'functions' => { 'testmodule2' => {\n        'foo.rb' => <<-CODE\n           raise \"should not get here\"\n        CODE\n      }}}}})\n    module_loader2 = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(module_loader, loaders, 'testmodule2', module_dir2, 'test2')\n\n    function = module_loader2.load_typed(typed_name(:function, 'testmodule::foo')).value\n\n    expect(function.class.name).to eq('testmodule::foo')\n    expect(function.is_a?(Puppet::Functions::Function)).to eq(true)\n  end\n\n  context 'loading tasks' do\n    before(:each) do\n      Puppet[:tasks] = true\n      Puppet.push_context(:loaders => loaders)\n    end\n    after(:each) { Puppet.pop_context }\n\n    it 'can load tasks with multiple files' do\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => '{}'})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n\n      task = module_loader.load_typed(typed_name(:task, 'testmodule::foo')).value\n      expect(task.name).to eq('testmodule::foo')\n      expect(task.files.length).to eq(1)\n      expect(task.files[0]['name']).to eq('foo.py')\n    end\n\n    it 'can load tasks with multiple implementations' do\n      metadata = { 'implementations' => [{'name' => 'foo.py'}, {'name' => 'foo.ps1'}] }\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.ps1' => '', 'foo.json' => metadata.to_json})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n\n      task = module_loader.load_typed(typed_name(:task, 'testmodule::foo')).value\n      expect(task.name).to eq('testmodule::foo')\n      expect(task.files.map {|impl| impl['name']}).to eq(['foo.py', 'foo.ps1'])\n    end\n\n    it 'can load multiple tasks with multiple files' do\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => '{}', 'foobar.py' => '', 'foobar.json' => '{}'})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n\n      foo_task = module_loader.load_typed(typed_name(:task, 'testmodule::foo')).value\n      foobar_task = module_loader.load_typed(typed_name(:task, 'testmodule::foobar')).value\n\n      expect(foo_task.name).to eq('testmodule::foo')\n      expect(foo_task.files.length).to eq(1)\n      expect(foo_task.files[0]['name']).to eq('foo.py')\n      expect(foobar_task.name).to eq('testmodule::foobar')\n      expect(foobar_task.files.length).to eq(1)\n      expect(foobar_task.files[0]['name']).to eq('foobar.py')\n    end\n\n    it \"won't load tasks with invalid names\" do\n      module_dir = dir_containing('testmodule', 'tasks' => {'a-b.py' => '', 'foo.tar.gz' => ''})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n\n      tasks = module_loader.discover(:task)\n      expect(tasks).to be_empty\n\n      expect(module_loader.load_typed(typed_name(:task, 'testmodule::foo'))).to be_nil\n    end\n\n    it \"lists tasks without executables, if they specify implementations\" do\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '',\n                                                            'bar.rb' => '',\n                                                            'baz.json' => {'implementations' => ['name' => 'foo.py']}.to_json,\n                                                            'qux.json' => '{}'})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n\n      tasks = module_loader.discover(:task)\n      expect(tasks.length).to eq(3)\n      expect(tasks.map(&:name).sort).to eq(['testmodule::bar', 'testmodule::baz', 'testmodule::foo'])\n\n      expect(module_loader.load_typed(typed_name(:task, 'testmodule::foo'))).not_to be_nil\n      expect(module_loader.load_typed(typed_name(:task, 'testmodule::bar'))).not_to be_nil\n      expect(module_loader.load_typed(typed_name(:task, 'testmodule::baz'))).not_to be_nil\n      expect { module_loader.load_typed(typed_name(:task, 'testmodule::qux')) }.to raise_error(/No source besides task metadata was found/)\n    end\n\n    it 'raises and error when `parameters` is not a hash' do\n      metadata = { 'parameters' => 'foo' }\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n      expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}\n        .to raise_error(Puppet::ParseError, /Failed to load metadata for task testmodule::foo: 'parameters' must be a hash/)\n    end\n\n    it 'raises and error when `implementations` `requirements` key is not an array' do\n      metadata = { 'implementations' => { 'name' => 'foo.py', 'requirements' => 'foo'} }\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n      expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}\n        .to raise_error(Puppet::Module::Task::InvalidMetadata,\n        /Task metadata for task testmodule::foo does not specify implementations as an array/)\n    end\n\n    it 'raises and error when top-level `files` is not an array' do\n      metadata = { 'files' => 'foo' }\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n      expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}\n        .to raise_error(Puppet::Module::Task::InvalidMetadata, /The 'files' task metadata expects an array, got foo/)\n    end\n\n    it 'raises and error when `files` nested in `interpreters` is not an array' do\n      metadata = { 'implementations' => [{'name' => 'foo.py', 'files' => 'foo'}] }\n      module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})\n\n      module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)\n      expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}\n        .to raise_error(Puppet::Module::Task::InvalidMetadata, /The 'files' task metadata expects an array, got foo/)\n    end\n  end\n\n  def typed_name(type, name)\n    Puppet::Pops::Loader::TypedName.new(type, name)\n  end\n\n  context 'module function and class using a module type alias' do\n    include PuppetSpec::Compiler\n\n    let(:modules) do\n      {\n        'mod' => {\n          'functions' => {\n            'afunc.pp' => <<-PUPPET.unindent\n              function mod::afunc(Mod::Analias $v) {\n                notice($v)\n              }\n          PUPPET\n          },\n          'types' => {\n            'analias.pp' => <<-PUPPET.unindent\n               type Mod::Analias = Enum[a,b]\n               PUPPET\n          },\n          'manifests' => {\n            'init.pp' => <<-PUPPET.unindent\n              class mod(Mod::Analias $v) {\n                notify { $v: }\n              }\n              PUPPET\n          }\n        }\n      }\n    end\n\n    let(:testing_env) do\n      {\n        'testing' => {\n          'modules' => modules\n        }\n      }\n    end\n\n    let(:environments_dir) { Puppet[:environmentpath] }\n\n    let(:testing_env_dir) do\n      dir_contained_in(environments_dir, testing_env)\n      env_dir = File.join(environments_dir, 'testing')\n      PuppetSpec::Files.record_tmp(env_dir)\n      env_dir\n    end\n\n    let(:env) { Puppet::Node::Environment.create(:testing, [File.join(testing_env_dir, 'modules')]) }\n    let(:node) { Puppet::Node.new('test', :environment => env) }\n\n    # The call to mod:afunc will load the function, and as a consequence, make an attempt to load\n    # the parameter type Mod::Analias. That load in turn, will trigger the Runtime3TypeLoader which\n    # will load the manifests in Mod. The init.pp manifest also references the Mod::Analias parameter\n    # which results in a recursive call to the same loader. This test asserts that this recursive\n    # call is handled OK.\n    # See PUP-7391 for more info.\n    it 'should handle a recursive load' do\n      expect(eval_and_collect_notices(\"mod::afunc('b')\", node)).to eql(['b'])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/loaders/static_loader_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/loaders'\n\ndescribe 'the static loader' do\n  let(:loader) do\n    loader = Puppet::Pops::Loader::StaticLoader.new()\n    loader.runtime_3_init\n    loader\n  end\n\n  it 'has no parent' do\n    expect(loader.parent).to be(nil)\n  end\n\n  it 'identifies itself in string form' do\n    expect(loader.to_s).to be_eql('(StaticLoader)')\n  end\n\n  it 'support the Loader API' do\n    # it may produce things later, this is just to test that calls work as they should - now all lookups are nil.\n    a_typed_name = typed_name(:function, 'foo')\n    expect(loader[a_typed_name]).to be(nil)\n    expect(loader.load_typed(a_typed_name)).to be(nil)\n    expect(loader.find(a_typed_name)).to be(nil)\n  end\n\n  context 'provides access to resource types built into puppet' do\n    %w{\n      Component\n      Exec\n      File\n      Filebucket\n      Group\n      Notify\n      Package\n      Resources\n      Schedule\n      Service\n      Stage\n      Tidy\n      User\n      Whit\n    }.each do |name |\n      it \"such that #{name} is available\" do\n        expect(loader.load(:type, name.downcase)).to be_the_type(resource_type(name))\n      end\n    end\n  end\n\n  context 'provides access to app-management specific resource types built into puppet' do\n    it \"such that Node is available\" do\n      expect(loader.load(:type, 'node')).to be_the_type(resource_type('Node'))\n    end\n  end\n\n  context 'without init_runtime3 initialization' do\n    let(:loader) { Puppet::Pops::Loader::StaticLoader.new() }\n\n    it 'does not provide access to resource types built into puppet' do\n      expect(loader.load(:type, 'file')).to be_nil\n    end\n  end\n\n  def typed_name(type, name)\n    Puppet::Pops::Loader::TypedName.new(type, name)\n  end\n\n  def resource_type(name)\n    Puppet::Pops::Types::TypeFactory.resource(name)\n  end\n\n  matcher :be_the_type do |type|\n    calc = Puppet::Pops::Types::TypeCalculator.new\n\n    match do |actual|\n      calc.assignable?(actual, type) && calc.assignable?(type, actual)\n    end\n\n    failure_message do |actual|\n      \"expected #{type.to_s}, but was #{actual.to_s}\"\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/lookup/context_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Lookup\ndescribe 'Puppet::Pops::Lookup::Context' do\n\n  context 'an instance' do\n    include PuppetSpec::Compiler\n    it 'can be created' do\n      code = \"notice(type(Puppet::LookupContext.new('m')))\"\n      expect(eval_and_collect_notices(code)[0]).to match(/Object\\[\\{name => 'Puppet::LookupContext'/)\n    end\n\n    it 'returns its environment_name' do\n      code = \"notice(Puppet::LookupContext.new('m').environment_name)\"\n      expect(eval_and_collect_notices(code)[0]).to eql('production')\n    end\n\n    it 'returns its module_name' do\n      code = \"notice(Puppet::LookupContext.new('m').module_name)\"\n      expect(eval_and_collect_notices(code)[0]).to eql('m')\n    end\n\n    it 'can use an undef module_name' do\n      code = \"notice(type(Puppet::LookupContext.new(undef).module_name))\"\n      expect(eval_and_collect_notices(code)[0]).to eql('Undef')\n    end\n\n    it 'can store and retrieve a value using the cache' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache('ze_key', 'ze_value')\n        notice($ctx.cached_value('ze_key'))\n      PUPPET\n      expect(eval_and_collect_notices(code)[0]).to eql('ze_value')\n    end\n\n    it 'the cache method returns the value that is cached' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        notice($ctx.cache('ze_key', 'ze_value'))\n      PUPPET\n      expect(eval_and_collect_notices(code)[0]).to eql('ze_value')\n    end\n\n    it 'can store and retrieve a hash using the cache' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second' })\n        $ctx.cached_entries |$key, $value| { notice($key); notice($value) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(%w(v1 first v2 second))\n    end\n\n    it 'can use the cache to merge hashes' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second' })\n        $ctx.cache_all({ 'v3' => 'third', 'v4' => 'fourth' })\n        $ctx.cached_entries |$key, $value| { notice($key); notice($value) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(%w(v1 first v2 second v3 third v4 fourth))\n    end\n\n    it 'can use the cache to merge hashes and individual entries' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second' })\n        $ctx.cache('v3', 'third')\n        $ctx.cached_entries |$key, $value| { notice($key); notice($value) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(%w(v1 first v2 second v3 third))\n    end\n\n    it 'can iterate the cache using one argument block' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second' })\n        $ctx.cached_entries |$entry| { notice($entry[0]); notice($entry[1]) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(%w(v1 first v2 second))\n    end\n\n    it 'can replace individual cached entries' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second' })\n        $ctx.cache('v2', 'changed')\n        $ctx.cached_entries |$key, $value| { notice($key); notice($value) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(%w(v1 first v2 changed))\n    end\n\n    it 'can replace multiple cached entries' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second', 'v3' => 'third' })\n        $ctx.cache_all({ 'v1' => 'one', 'v3' => 'three' })\n        $ctx.cached_entries |$key, $value| { notice($key); notice($value) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(%w(v1 one v2 second v3 three))\n    end\n\n    it 'cached_entries returns an Iterable when called without a block' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.cache_all({ 'v1' => 'first', 'v2' => 'second' })\n        $iter = $ctx.cached_entries\n        notice(type($iter, generalized))\n        $iter.each |$entry| { notice($entry[0]); notice($entry[1]) }\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['Iterator[Tuple[String, String, 2, 2]]', 'v1', 'first', 'v2', 'second'])\n    end\n\n    it 'will throw :no_such_key when not_found is called' do\n      code = <<-PUPPET.unindent\n        $ctx = Puppet::LookupContext.new('m')\n        $ctx.not_found\n      PUPPET\n      expect { eval_and_collect_notices(code) }.to throw_symbol(:no_such_key)\n    end\n\n    context 'with cached_file_data' do\n      include PuppetSpec::Files\n\n      let(:code_dir) { Puppet[:environmentpath] }\n      let(:env_name) { 'testing' }\n      let(:env_dir) { File.join(code_dir, env_name) }\n      let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')]) }\n      let(:node) { Puppet::Node.new('test', :environment => env) }\n      let(:data_yaml) { 'data.yaml' }\n      let(:data_path) { File.join(populated_env_dir, 'data', data_yaml) }\n      let(:populated_env_dir) do\n        dir_contained_in(code_dir,\n          {\n            env_name => {\n              'data' => {\n                data_yaml => <<-YAML.unindent\n                  a: value a\n                  YAML\n              }\n            }\n          }\n        )\n        PuppetSpec::Files.record_tmp(File.join(env_dir))\n        env_dir\n      end\n\n      it 'can use cached_file_data without a block' do\n        code = <<-PUPPET.unindent\n          $ctx = Puppet::LookupContext.new(nil)\n          $yaml_data = $ctx.cached_file_data('#{data_path}')\n          notice($yaml_data)\n        PUPPET\n        expect(eval_and_collect_notices(code, node)).to eql([\"a: value a\\n\"])\n      end\n\n      it 'can use cached_file_data with a block' do\n        code = <<-PUPPET.unindent\n          $ctx = Puppet::LookupContext.new(nil)\n          $yaml_data = $ctx.cached_file_data('#{data_path}') |$content| {\n            { 'parsed' => $content }\n          }\n          notice($yaml_data)\n        PUPPET\n        expect(eval_and_collect_notices(code, node)).to eql([\"{parsed => a: value a\\n}\"])\n      end\n\n      context 'and multiple compilations' do\n\n        before(:each) { Puppet.settings[:environment_timeout] = 'unlimited' }\n\n        it 'will reuse cached_file_data and not call block again' do\n\n          code1 = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new(nil)\n            $yaml_data = $ctx.cached_file_data('#{data_path}') |$content| {\n              { 'parsed' => $content }\n            }\n            notice($yaml_data)\n            PUPPET\n\n          code2 = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new(nil)\n            $yaml_data = $ctx.cached_file_data('#{data_path}') |$content| {\n              { 'parsed' => 'should not be called' }\n            }\n            notice($yaml_data)\n            PUPPET\n\n          logs = []\n          Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n            Puppet[:code] = code1\n            Puppet::Parser::Compiler.compile(node)\n            Puppet[:code] = code2\n            Puppet::Parser::Compiler.compile(node)\n          end\n          logs = logs.select { |log| log.level == :notice }.map { |log| log.message }\n          expect(logs.uniq.size).to eql(1)\n        end\n\n        it 'will invalidate cache if file changes size' do\n          code = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new(nil)\n            $yaml_data = $ctx.cached_file_data('#{data_path}') |$content| {\n              { 'parsed' => $content }\n            }\n            notice($yaml_data)\n            PUPPET\n\n          logs = []\n          Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n            Puppet[:code] = code\n            Puppet::Parser::Compiler.compile(node)\n\n            # Change content size!\n            File.write(data_path, \"a: value is now A\\n\")\n            Puppet::Parser::Compiler.compile(node)\n          end\n          logs = logs.select { |log| log.level == :notice }.map { |log| log.message }\n          expect(logs).to eql([\"{parsed => a: value a\\n}\", \"{parsed => a: value is now A\\n}\"])\n        end\n\n        it 'will invalidate cache if file changes mtime' do\n          old_mtime = Puppet::FileSystem.stat(data_path).mtime\n\n          code = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new(nil)\n            $yaml_data = $ctx.cached_file_data('#{data_path}') |$content| {\n              { 'parsed' => $content }\n            }\n            notice($yaml_data)\n          PUPPET\n\n          logs = []\n          Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n            Puppet[:code] = code\n            Puppet::Parser::Compiler.compile(node)\n\n            # Write content with the same size\n            File.write(data_path, \"a: value b\\n\")\n\n            # Ensure mtime is at least 1 second ahead\n            FileUtils.touch(data_path, mtime: old_mtime + 1)\n\n            Puppet::Parser::Compiler.compile(node)\n          end\n          logs = logs.select { |log| log.level == :notice }.map { |log| log.message }\n          expect(logs).to eql([\"{parsed => a: value a\\n}\", \"{parsed => a: value b\\n}\"])\n        end\n\n        it 'will invalidate cache if file changes inode' do\n          code = <<-PUPPET.unindent\n              $ctx = Puppet::LookupContext.new(nil)\n              $yaml_data = $ctx.cached_file_data('#{data_path}') |$content| {\n                { 'parsed' => $content }\n              }\n              notice($yaml_data)\n          PUPPET\n\n          logs = []\n          Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n            Puppet[:code] = code\n            Puppet::Parser::Compiler.compile(node)\n\n            # Change inode!\n            File.delete(data_path);\n            # Write content with the same size\n            File.write(data_path, \"a: value b\\n\")\n            Puppet::Parser::Compiler.compile(node)\n          end\n          logs = logs.select { |log| log.level == :notice }.map { |log| log.message }\n          expect(logs).to eql([\"{parsed => a: value a\\n}\", \"{parsed => a: value b\\n}\"])\n        end\n      end\n    end\n\n    context 'when used in an Invocation' do\n      let(:node) { Puppet::Node.new('test') }\n      let(:compiler) { Puppet::Parser::Compiler.new(node) }\n      let(:invocation) { Invocation.new(compiler.topscope) }\n      let(:invocation_with_explain) { Invocation.new(compiler.topscope, {}, {}, true) }\n\n      def compile_and_get_notices(code, scope_vars = {})\n        Puppet[:code] = code\n        scope = compiler.topscope\n        scope_vars.each_pair { |k,v| scope.setvar(k, v) }\n        node.environment.check_for_reparse\n        logs = []\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          compiler.compile\n        end\n        logs = logs.select { |log| log.level == :notice }.map { |log| log.message }\n        logs\n      end\n\n      it 'will not call explain unless explanations are active' do\n        invocation.lookup('dummy', nil) do\n          code = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new('m')\n            $ctx.explain || { notice('stop calling'); 'bad' }\n          PUPPET\n          expect(compile_and_get_notices(code)).to be_empty\n        end\n      end\n\n      it 'will call explain when explanations are active' do\n        invocation_with_explain.lookup('dummy', nil) do\n          code = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new('m')\n            $ctx.explain || { notice('called'); 'good' }\n          PUPPET\n          expect(compile_and_get_notices(code)).to eql(['called'])\n        end\n        expect(invocation_with_explain.explainer.explain).to eql(\"good\\n\")\n      end\n\n      it 'will call interpolate to resolve interpolation' do\n        invocation.lookup('dummy', nil) do\n          code = <<-PUPPET.unindent\n            $ctx = Puppet::LookupContext.new('m')\n            notice($ctx.interpolate('-- %{testing} --'))\n          PUPPET\n          expect(compile_and_get_notices(code, { 'testing' => 'called' })).to eql(['-- called --'])\n        end\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/lookup/interpolation_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet'\n\nmodule Puppet::Pops\ndescribe 'Puppet::Pops::Lookup::Interpolation' do\n  include Lookup::SubLookup\n\n  class InterpolationTestAdapter < Lookup::LookupAdapter\n    include Lookup::SubLookup\n\n    def initialize(data, interpolator)\n      @data = data\n      @interpolator = interpolator\n    end\n\n    def track(name)\n    end\n\n    def lookup(name, lookup_invocation, merge)\n      track(name)\n      segments = split_key(name)\n      root_key = segments.shift\n      found = @data[root_key]\n      found = sub_lookup(name, lookup_invocation, segments, found) unless segments.empty?\n      @interpolator.interpolate(found, lookup_invocation, true)\n    end\n\n    # ignore requests for lookup options when testing interpolation\n    def lookup_lookup_options(_, _)\n      nil\n    end\n  end\n\n  let(:interpolator) { Class.new { include Lookup::Interpolation }.new }\n  let(:scope) { {} }\n  let(:data) { {} }\n  let(:adapter) { InterpolationTestAdapter.new(data, interpolator) }\n  let(:lookup_invocation) { Lookup::Invocation.new(scope, {}, {}, nil) }\n\n  before(:each) do\n    allow_any_instance_of(Lookup::Invocation).to receive(:lookup_adapter).and_return(adapter)\n  end\n\n  def expect_lookup(*keys)\n    keys.each { |key| expect(adapter).to receive(:track).with(key) }\n  end\n\n  context 'when the invocation has a default falsey values' do\n    let(:lookup_invocation) { Lookup::Invocation.new(scope, {}, {'key' => false, 'key2' => nil}, nil) }\n\n    it 'converts boolean false to a \"false\" string in interpolation' do\n      expect(interpolator.interpolate('%{key}', lookup_invocation, true)).to eq(\"false\")\n    end\n\n    it 'converts nil to an empty string in interpolation' do\n      expect(interpolator.interpolate('%{key2}', lookup_invocation, true)).to eq(\"\")\n    end\n  end\n\n  context 'when interpolating nested data' do\n    let(:nested_hash) { {'a' => {'aa' => \"%{alias('aaa')}\"}} }\n\n    let(:scope) {\n      {\n        'ds1' => 'a',\n        'ds2' => 'b'\n      }\n    }\n\n    let(:data) {\n      {\n        'aaa' => {'b' => {'bb' => \"%{alias('bbb')}\"}},\n        'bbb' => [\"%{alias('ccc')}\"],\n        'ccc' => 'text',\n        'ddd' => \"%{literal('%')}{ds1}_%{literal('%')}{ds2}\",\n      }\n    }\n\n    it 'produces a nested hash with arrays from nested aliases with hashes and arrays' do\n      expect_lookup('aaa', 'bbb', 'ccc')\n      expect(interpolator.interpolate(nested_hash, lookup_invocation, true)).to eq('a' => {'aa' => {'b' => {'bb' => ['text']}}})\n    end\n\n    it \"'%{lookup('key')} will interpolate the returned value'\" do\n      expect_lookup('ddd')\n      expect(interpolator.interpolate(\"%{lookup('ddd')}\", lookup_invocation, true)).to eq('a_b')\n    end\n\n    it \"'%{alias('key')} will not interpolate the returned value'\" do\n      expect_lookup('ddd')\n      expect(interpolator.interpolate(\"%{alias('ddd')}\", lookup_invocation, true)).to eq('%{ds1}_%{ds2}')\n    end\n  end\n\n  context 'when interpolating boolean scope values' do\n    let(:scope) { { 'yes' => true, 'no' => false } }\n\n    it 'produces the string true' do\n      expect(interpolator.interpolate('should yield %{yes}', lookup_invocation, true)).to eq('should yield true')\n    end\n\n    it 'produces the string false' do\n      expect(interpolator.interpolate('should yield %{no}', lookup_invocation, true)).to eq('should yield false')\n    end\n  end\n\n  context 'when there are empty interpolations %{} in data' do\n\n    let(:empty_interpolation) { 'clown%{}shoe' }\n    let(:empty_interpolation_as_escape) { 'clown%%{}{shoe}s' }\n    let(:only_empty_interpolation) { '%{}' }\n    let(:empty_namespace) { '%{::}' }\n    let(:whitespace1) { '%{ :: }' }\n    let(:whitespace2) { '%{   }' }\n\n    it 'should produce an empty string for the interpolation' do\n      expect(interpolator.interpolate(empty_interpolation, lookup_invocation, true)).to eq('clownshoe')\n    end\n\n    it 'the empty interpolation can be used as an escape mechanism' do\n      expect(interpolator.interpolate(empty_interpolation_as_escape, lookup_invocation, true)).to eq('clown%{shoe}s')\n    end\n\n    it 'the value can consist of only an empty escape' do\n      expect(interpolator.interpolate(only_empty_interpolation, lookup_invocation, true)).to eq('')\n    end\n\n    it 'the value can consist of an empty namespace %{::}' do\n      expect(interpolator.interpolate(empty_namespace, lookup_invocation, true)).to eq('')\n    end\n\n    it 'the value can consist of whitespace %{ :: }' do\n      expect(interpolator.interpolate(whitespace1, lookup_invocation, true)).to eq('')\n    end\n\n    it 'the value can consist of whitespace %{  }' do\n      expect(interpolator.interpolate(whitespace2, lookup_invocation, true)).to eq('')\n    end\n  end\n\n  context 'when there are quoted empty interpolations %{} in data' do\n\n    let(:empty_interpolation) { 'clown%{\"\"}shoe' }\n    let(:empty_interpolation_as_escape) { 'clown%%{\"\"}{shoe}s' }\n    let(:only_empty_interpolation) { '%{\"\"}' }\n    let(:empty_namespace) { '%{\"::\"}' }\n    let(:whitespace1) { '%{ \"::\" }' }\n    let(:whitespace2) { '%{ \"\"  }' }\n\n    it 'should produce an empty string for the interpolation' do\n      expect(interpolator.interpolate(empty_interpolation, lookup_invocation, true)).to eq('clownshoe')\n    end\n\n    it 'the empty interpolation can be used as an escape mechanism' do\n      expect(interpolator.interpolate(empty_interpolation_as_escape, lookup_invocation, true)).to eq('clown%{shoe}s')\n    end\n\n    it 'the value can consist of only an empty escape' do\n      expect(interpolator.interpolate(only_empty_interpolation, lookup_invocation, true)).to eq('')\n    end\n\n    it 'the value can consist of an empty namespace %{\"::\"}' do\n      expect(interpolator.interpolate(empty_namespace, lookup_invocation, true)).to eq('')\n    end\n\n    it 'the value can consist of whitespace %{ \"::\" }' do\n      expect(interpolator.interpolate(whitespace1, lookup_invocation, true)).to eq('')\n    end\n\n    it 'the value can consist of whitespace %{ \"\" }' do\n      expect(interpolator.interpolate(whitespace2, lookup_invocation, true)).to eq('')\n    end\n  end\n\n\n  context 'when using dotted keys' do\n    let(:data) {\n      {\n        'a.b' => '(lookup) a dot b',\n        'a' => {\n          'd' => '(lookup) a dot d is a hash entry',\n          'd.x' => '(lookup) a dot d.x is a hash entry',\n          'd.z' => {\n            'g' => '(lookup) a dot d.z dot g is a hash entry'}\n        },\n        'a.x' => {\n          'd' => '(lookup) a.x dot d is a hash entry',\n          'd.x' => '(lookup) a.x dot d.x is a hash entry',\n          'd.z' => {\n            'g' => '(lookup) a.x dot d.z dot g is a hash entry'\n          }\n        },\n        'x.1' => '(lookup) x dot 1',\n        'key' => 'subkey'\n      }\n    }\n\n    let(:scope) {\n      {\n        'a.b' => '(scope) a dot b',\n        'a' => {\n          'd' => '(scope) a dot d is a hash entry',\n          'd.x' => '(scope) a dot d.x is a hash entry',\n          'd.z' => {\n            'g' => '(scope) a dot d.z dot g is a hash entry'}\n        },\n        'a.x' => {\n          'd' => '(scope) a.x dot d is a hash entry',\n          'd.x' => '(scope) a.x dot d.x is a hash entry',\n          'd.z' => {\n            'g' => '(scope) a.x dot d.z dot g is a hash entry'\n          }\n        },\n        'x.1' => '(scope) x dot 1',\n      }\n    }\n\n    it 'should find an entry using a quoted interpolation' do\n      expect(interpolator.interpolate(\"a dot c: %{'a.b'}\", lookup_invocation, true)).to eq('a dot c: (scope) a dot b')\n    end\n\n    it 'should find an entry using a quoted interpolation with method lookup' do\n      expect_lookup(\"'a.b'\")\n      expect(interpolator.interpolate(\"a dot c: %{lookup(\\\"'a.b'\\\")}\", lookup_invocation, true)).to eq('a dot c: (lookup) a dot b')\n    end\n\n    it 'should find an entry using a quoted interpolation with method alias' do\n      expect_lookup(\"'a.b'\")\n      expect(interpolator.interpolate(\"%{alias(\\\"'a.b'\\\")}\", lookup_invocation, true)).to eq('(lookup) a dot b')\n    end\n\n    it 'should use a dotted key to navigate into a structure when it is not quoted' do\n      expect(interpolator.interpolate('a dot e: %{a.d}', lookup_invocation, true)).to eq('a dot e: (scope) a dot d is a hash entry')\n    end\n\n    it 'should report a key missing and replace with empty string when a dotted key is used to navigate into a structure and then not found' do\n      expect(interpolator.interpolate('a dot n: %{a.n}', lookup_invocation, true)).to eq('a dot n: ')\n    end\n\n    it 'should use a dotted key to navigate into a structure when it is not quoted with method lookup' do\n      expect_lookup('a.d')\n      expect(interpolator.interpolate(\"a dot e: %{lookup('a.d')}\", lookup_invocation, true)).to eq('a dot e: (lookup) a dot d is a hash entry')\n    end\n\n    it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is last' do\n      expect(interpolator.interpolate(\"a dot ex: %{a.'d.x'}\", lookup_invocation, true)).to eq('a dot ex: (scope) a dot d.x is a hash entry')\n    end\n\n    it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is last and method is lookup' do\n      expect_lookup(\"a.'d.x'\")\n      expect(interpolator.interpolate(\"a dot ex: %{lookup(\\\"a.'d.x'\\\")}\", lookup_invocation, true)).to eq('a dot ex: (lookup) a dot d.x is a hash entry')\n    end\n\n    it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is first' do\n      expect(interpolator.interpolate(\"a dot xe: %{'a.x'.d}\", lookup_invocation, true)).to eq('a dot xe: (scope) a.x dot d is a hash entry')\n    end\n\n    it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is first and method is lookup' do\n      expect_lookup(\"'a.x'.d\")\n      expect(interpolator.interpolate(\"a dot xe: %{lookup(\\\"'a.x'.d\\\")}\", lookup_invocation, true)).to eq('a dot xe: (lookup) a.x dot d is a hash entry')\n    end\n\n    it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle' do\n      expect(interpolator.interpolate(\"a dot xm: %{a.'d.z'.g}\", lookup_invocation, true)).to eq('a dot xm: (scope) a dot d.z dot g is a hash entry')\n    end\n\n    it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle and method is lookup' do\n      expect_lookup(\"a.'d.z'.g\")\n      expect(interpolator.interpolate(\"a dot xm: %{lookup(\\\"a.'d.z'.g\\\")}\", lookup_invocation, true)).to eq('a dot xm: (lookup) a dot d.z dot g is a hash entry')\n    end\n\n    it 'should use a mix of several quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle' do\n      expect(interpolator.interpolate(\"a dot xx: %{'a.x'.'d.z'.g}\", lookup_invocation, true)).to eq('a dot xx: (scope) a.x dot d.z dot g is a hash entry')\n    end\n\n    it 'should use a mix of several quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle and method is lookup' do\n      expect_lookup(\"'a.x'.'d.z'.g\")\n      expect(interpolator.interpolate(\"a dot xx: %{lookup(\\\"'a.x'.'d.z'.g\\\")}\", lookup_invocation, true)).to eq('a dot xx: (lookup) a.x dot d.z dot g is a hash entry')\n    end\n\n    it 'should find an entry using using a quoted interpolation on dotted key containing numbers' do\n      expect(interpolator.interpolate(\"x dot 2: %{'x.1'}\", lookup_invocation, true)).to eq('x dot 2: (scope) x dot 1')\n    end\n\n    it 'should find an entry using using a quoted interpolation on dotted key containing numbers using method lookup' do\n      expect_lookup(\"'x.1'\")\n      expect(interpolator.interpolate(\"x dot 2: %{lookup(\\\"'x.1'\\\")}\", lookup_invocation, true)).to eq('x dot 2: (lookup) x dot 1')\n    end\n\n    it 'should not find a subkey when the dotted key is quoted' do\n      expect(interpolator.interpolate(\"a dot f: %{'a.d'}\", lookup_invocation, true)).to eq('a dot f: ')\n    end\n\n    it 'should not find a subkey when the dotted key is quoted with method lookup' do\n      expect_lookup(\"'a.d'\")\n      expect(interpolator.interpolate(\"a dot f: %{lookup(\\\"'a.d'\\\")}\", lookup_invocation, true)).to eq('a dot f: ')\n    end\n\n    it 'should not find a subkey that is matched within a string' do\n      expect{ interpolator.interpolate(\"%{lookup('key.subkey')}\", lookup_invocation, true) }.to raise_error(\n        /Got String when a hash-like object was expected to access value using 'subkey' from key 'key.subkey'/)\n    end\n  end\n\n  context 'when dealing with non alphanumeric characters' do\n    let(:data) {\n      {\n        'a key with whitespace' => 'value for a ws key',\n        'ws_key' => '%{alias(\"a key with whitespace\")}',\n        '\\#@!&%|' => 'not happy',\n        'angry' => '%{alias(\"\\#@!&%|\")}',\n        '!$\\%!' => {\n          '\\#@!&%|' => 'not happy at all'\n        },\n        'very_angry' => '%{alias(\"!$\\%!.\\#@!&%|\")}',\n        'a key with' => {\n          'nested whitespace' => 'value for nested ws key',\n          ' untrimmed whitespace ' => 'value for untrimmed ws key'\n        }\n      }\n    }\n\n    it 'allows keys with white space' do\n      expect_lookup('ws_key', 'a key with whitespace')\n      expect(interpolator.interpolate(\"%{lookup('ws_key')}\", lookup_invocation, true)).to eq('value for a ws key')\n    end\n\n    it 'allows keys with non alphanumeric characters' do\n      expect_lookup('angry', '\\#@!&%|')\n      expect(interpolator.interpolate(\"%{lookup('angry')}\", lookup_invocation, true)).to eq('not happy')\n    end\n\n    it 'allows dotted keys with non alphanumeric characters' do\n      expect_lookup('very_angry', '!$\\%!.\\#@!&%|')\n      expect(interpolator.interpolate(\"%{lookup('very_angry')}\", lookup_invocation, true)).to eq('not happy at all')\n    end\n\n    it 'allows dotted keys with nested white space' do\n      expect_lookup('a key with.nested whitespace')\n      expect(interpolator.interpolate(\"%{lookup('a key with.nested whitespace')}\", lookup_invocation, true)).to eq('value for nested ws key')\n    end\n\n    it 'will trim each key element' do\n      expect_lookup(' a key with . nested whitespace ')\n      expect(interpolator.interpolate(\"%{lookup(' a key with . nested whitespace ')}\", lookup_invocation, true)).to eq('value for nested ws key')\n    end\n\n    it 'will not trim quoted key element' do\n      expect_lookup(' a key with .\" untrimmed whitespace \"')\n      expect(interpolator.interpolate(\"%{lookup(' a key with .\\\" untrimmed whitespace \\\"')}\", lookup_invocation, true)).to eq('value for untrimmed ws key')\n    end\n\n    it 'will not trim spaces outside of quoted key element' do\n      expect_lookup(' a key with .  \" untrimmed whitespace \"  ')\n      expect(interpolator.interpolate(\"%{lookup(' a key with .  \\\" untrimmed whitespace \\\"  ')}\", lookup_invocation, true)).to eq('value for untrimmed ws key')\n    end\n  end\n\n  context 'when dealing with bad keys' do\n    it 'should produce an error when different quotes are used on either side' do\n      expect { interpolator.interpolate(\"%{'the.key\\\"}\", lookup_invocation, true)}.to raise_error(\"Syntax error in string: %{'the.key\\\"}\")\n    end\n\n    it 'should produce an if there is only one quote' do\n      expect { interpolator.interpolate(\"%{the.'key}\", lookup_invocation, true)}.to raise_error(\"Syntax error in string: %{the.'key}\")\n    end\n\n    it 'should produce an error for an empty segment' do\n      expect { interpolator.interpolate('%{the..key}', lookup_invocation, true)}.to raise_error(\"Syntax error in string: %{the..key}\")\n    end\n\n    it 'should produce an error for an empty quoted segment' do\n      expect { interpolator.interpolate(\"%{the.''.key}\", lookup_invocation, true)}.to raise_error(\"Syntax error in string: %{the.''.key}\")\n    end\n\n    it 'should produce an error for an partly quoted segment' do\n      expect { interpolator.interpolate(\"%{the.'pa'key}\", lookup_invocation, true)}.to raise_error(\"Syntax error in string: %{the.'pa'key}\")\n    end\n\n    it 'should produce an error when different quotes are used on either side in a method argument' do\n      expect { interpolator.interpolate(\"%{lookup('the.key\\\")}\", lookup_invocation, true)}.to raise_error(\"Syntax error in string: %{lookup('the.key\\\")}\")\n    end\n\n    it 'should produce an error unless a known interpolation method is used' do\n      expect { interpolator.interpolate(\"%{flubber(\\\"hello\\\")}\", lookup_invocation, true)}.to raise_error(\"Unknown interpolation method 'flubber'\")\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/lookup/lookup_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet/pops'\nrequire 'deep_merge/core'\n\nmodule Puppet::Pops\nmodule Lookup\ndescribe 'The lookup API' do\n  include PuppetSpec::Files\n\n  let(:env_name) { 'spec' }\n  let(:code_dir) { Puppet[:environmentpath] }\n  let(:env_dir) { File.join(code_dir, env_name) }\n  let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')]) }\n  let(:node) { Puppet::Node.new('test', :environment => env) }\n  let(:compiler) { Puppet::Parser::Compiler.new(node) }\n  let(:pal_compiler) { Puppet::Pal::ScriptCompiler.new(compiler) }\n  let(:scope) { compiler.topscope }\n  let(:invocation) { Invocation.new(scope) }\n\n  let(:code_dir_content) do\n    {\n      'hiera.yaml' => <<-YAML.unindent,\n        version: 5\n        YAML\n      'data' => {\n        'common.yaml' => <<-YAML.unindent\n          a: a (from global)\n          d: d (from global)\n          mod::e: mod::e (from global)\n          YAML\n      }\n    }\n  end\n\n  let(:env_content) do\n    {\n      'hiera.yaml' => <<-YAML.unindent,\n        version: 5\n        YAML\n      'data' => {\n        'common.yaml' => <<-YAML.unindent\n          b: b (from environment)\n          d: d (from environment)\n          mod::f: mod::f (from environment)\n          YAML\n      }\n    }\n  end\n\n  let(:mod_content) do\n    {\n      'hiera.yaml' => <<-YAML.unindent,\n        version: 5\n        YAML\n      'data' => {\n        'common.yaml' => <<-YAML.unindent\n          mod::c: mod::c (from module)\n          mod::e: mod::e (from module)\n          mod::f: mod::f (from module)\n          mod::g:\n            :symbol: symbol key value\n            key: string key value\n            6: integer key value\n            -4: negative integer key value\n            2.7: float key value\n            '6': string integer key value\n          YAML\n      }\n    }\n  end\n\n  let(:populated_env_dir) do\n    all_content = code_dir_content.merge(env_name => env_content.merge('modules' => { 'mod' => mod_content }))\n    dir_contained_in(code_dir, all_content)\n    all_content.keys.each { |key| PuppetSpec::Files.record_tmp(File.join(code_dir, key)) }\n    env_dir\n  end\n\n  before(:each) do\n    Puppet[:hiera_config] = File.join(code_dir, 'hiera.yaml')\n    Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))\n  end\n\n  after(:each) do\n    Puppet.pop_context\n  end\n\n  context 'when doing automatic parameter lookup' do\n\n    let(:mod_content) do\n      {\n        'hiera.yaml' => <<-YAML.unindent,\n          version: 5\n          YAML\n        'data' => {\n          'common.yaml' => <<-YAML.unindent\n            mod::x: mod::x (from module)\n            YAML\n        },\n        'manifests' => {\n           'init.pp' => <<-PUPPET.unindent\n             class mod($x) {\n               notify { $x: }\n             }\n             PUPPET\n        }\n      }\n    end\n    let(:logs) { [] }\n    let(:debugs) { logs.select { |log| log.level == :debug }.map { |log| log.message } }\n\n    it 'includes APL in explain output when debug is enabled' do\n      Puppet[:log_level] = 'debug'\n      Puppet[:code] = 'include mod'\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        compiler.compile\n      end\n      expect(debugs).to include(/Found key: \"mod::x\" value: \"mod::x \\(from module\\)\"/)\n    end\n  end\n\n  context 'when hiera YAML data is corrupt' do\n    let(:mod_content) do\n      {\n        'hiera.yaml' => 'version: 5',\n        'data' => {\n          'common.yaml' => <<-YAML.unindent\n            ---\n            #mod::classes:\n              - cls1\n              - cls2\n              \n            mod::somevar: 1\n            YAML\n        },\n      }\n    end\n    let(:msg) { /file does not contain a valid yaml hash/ }\n\n    %w(off warning).each do |strict|\n      it \"logs a warning when --strict is '#{strict}'\" do\n        Puppet[:strict] = strict\n        logs = []\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(Lookup.lookup('mod::somevar', nil, nil, true, nil, invocation)).to be_nil\n        end\n        expect(logs.map(&:message)).to contain_exactly(msg)\n      end\n    end\n\n    it 'fails when --strict is \"error\"' do\n      Puppet[:strict] = 'error'\n      expect { Lookup.lookup('mod::somevar', nil, nil, true, nil, invocation) }.to raise_error(msg)\n    end\n  end\n\n  context 'when hiera YAML data is empty' do\n    let(:mod_content) do\n      {\n        'hiera.yaml' => 'version: 5',\n        'data' => { 'common.yaml' => '' },\n      }\n    end\n    let(:msg) { /file does not contain a valid yaml hash/ }\n\n    %w(off warning error).each do |strict|\n      it \"logs a warning when --strict is '#{strict}'\" do\n        Puppet[:strict] = strict\n        logs = []\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          expect(Lookup.lookup('mod::somevar', nil, nil, true, nil, invocation)).to be_nil\n        end\n        expect(logs.map(&:message)).to contain_exactly(msg)\n      end\n    end\n  end\n\n  context 'when doing lookup' do\n    it 'finds data in global layer' do\n      expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')\n    end\n\n    it 'finds data in environment layer' do\n      expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('b (from environment)')\n    end\n\n    it 'global layer wins over environment layer' do\n      expect(Lookup.lookup('d', nil, 'not found', true, nil, invocation)).to eql('d (from global)')\n    end\n\n    it 'finds data in module layer' do\n      expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('mod::c (from module)')\n    end\n\n    it 'global layer wins over module layer' do\n      expect(Lookup.lookup('mod::e', nil, 'not found', true, nil, invocation)).to eql('mod::e (from global)')\n    end\n\n    it 'environment layer wins over module layer' do\n      expect(Lookup.lookup('mod::f', nil, 'not found', true, nil, invocation)).to eql('mod::f (from environment)')\n    end\n\n    it 'returns the correct types for hash keys' do\n      expect(Lookup.lookup('mod::g', nil, 'not found', true, nil, invocation)).to eql(\n\t      {\n          'symbol' => 'symbol key value',\n\t\t      'key' => 'string key value',\n\t\t      6 => 'integer key value',\n          -4 => 'negative integer key value',\n\t\t      2.7 => 'float key value',\n          '6' => 'string integer key value'\n\t      }\n      )\n    end\n\n    it 'can navigate a hash with an integer key using a dotted key' do\n      expect(Lookup.lookup('mod::g.6', nil, 'not found', true, nil, invocation)).to eql('integer key value')\n    end\n\n    it 'can navigate a hash with a negative integer key using a dotted key' do\n      expect(Lookup.lookup('mod::g.-4', nil, 'not found', true, nil, invocation)).to eql('negative integer key value')\n    end\n\n    it 'can navigate a hash with an string integer key using a dotted key with quoted integer' do\n      expect(Lookup.lookup(\"mod::g.'6'\", nil, 'not found', true, nil, invocation)).to eql('string integer key value')\n    end\n\n    context \"with 'global_only' set to true in the invocation\" do\n      let(:invocation) { Invocation.new(scope).set_global_only }\n\n      it 'finds data in global layer' do\n        expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')\n      end\n\n      it 'does not find data in environment layer' do\n        expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('not found')\n      end\n\n      it 'does not find data in module layer' do\n        expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('not found')\n      end\n    end\n\n    context \"with 'global_only' set to true in the lookup adapter\" do\n      it 'finds data in global layer' do\n        invocation.lookup_adapter.set_global_only\n        expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')\n      end\n\n      it 'does not find data in environment layer' do\n        invocation.lookup_adapter.set_global_only\n        expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('not found')\n      end\n\n      it 'does not find data in module layer' do\n        invocation.lookup_adapter.set_global_only\n        expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('not found')\n      end\n    end\n\n    context 'with subclassed lookup adpater' do\n      let(:other_dir) { tmpdir('other') }\n      let(:other_dir_content) do\n        {\n          'hiera.yaml' => <<-YAML.unindent,\n            version: 5\n            hierarchy:\n              - name: Common\n                path: common.yaml\n              - name: More\n                path: more.yaml\n            YAML\n          'data' => {\n            'common.yaml' => <<-YAML.unindent,\n              a: a (from other global)\n              d: d (from other global)\n              mixed_adapter_hash:\n                a:\n                  ab: value a.ab (from other common global)\n                  ad: value a.ad (from other common global)\n              mod::e: mod::e (from other global)\n              lookup_options:\n                mixed_adapter_hash:\n                  merge: deep\n              YAML\n            'more.yaml' => <<-YAML.unindent\n              mixed_adapter_hash:\n                a:\n                  aa: value a.aa (from other more global)\n                  ac: value a.ac (from other more global)\n              YAML\n          }\n        }\n      end\n\n      let(:populated_other_dir) do\n        dir_contained_in(other_dir, other_dir_content)\n        other_dir\n      end\n\n      before(:each) do\n        eval(<<-RUBY.unindent)\n        class SpecialLookupAdapter < LookupAdapter\n           def initialize(compiler)\n             super\n             set_global_only\n             set_global_hiera_config_path(File.join('#{populated_other_dir}', 'hiera.yaml'))\n           end\n        end\n        RUBY\n      end\n\n      after(:each) do\n        Puppet::Pops::Lookup.send(:remove_const, :SpecialLookupAdapter)\n      end\n\n      let(:other_invocation) { Invocation.new(scope, EMPTY_HASH, EMPTY_HASH, nil, SpecialLookupAdapter) }\n\n      it 'finds different data in global layer' do\n        expect(Lookup.lookup('a', nil, nil, false, nil, other_invocation)).to eql('a (from other global)')\n        expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')\n      end\n\n      it 'does not find data in environment layer' do\n        expect(Lookup.lookup('b', nil, 'not found', true, nil, other_invocation)).to eql('not found')\n        expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('b (from environment)')\n      end\n\n      it 'does not find data in module layer' do\n        expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, other_invocation)).to eql('not found')\n        expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('mod::c (from module)')\n      end\n\n      it 'resolves lookup options using the custom adapter' do\n        expect(Lookup.lookup('mixed_adapter_hash', nil, 'not found', true, nil, other_invocation)).to eql(\n          {\n            'a' => {\n              'aa' => 'value a.aa (from other more global)',\n              'ab' => 'value a.ab (from other common global)',\n              'ac' => 'value a.ac (from other more global)',\n              'ad' => 'value a.ad (from other common global)'\n            }\n          }\n        )\n      end\n    end\n  end\n\n  context 'when using plan_hierarchy' do\n    let(:code_dir_content) do\n      {\n        'hiera.yaml' => <<-YAML.unindent,\n        version: 5\n        plan_hierarchy:\n          - path: foo.yaml\n            name: Common\n        YAML\n        'data' => {\n          'foo.yaml' => <<-YAML.unindent\n          pop: star\n          YAML\n        }\n      }\n    end\n\n    it 'uses plan_hierarchy when using ScriptCompiler' do\n      Puppet.override(pal_compiler: pal_compiler) do\n        expect(Lookup.lookup('pop', nil, nil, true, nil, invocation)).to eq('star')\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/merge_strategy_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\ndescribe 'MergeStrategy' do\n  context 'deep merge' do\n    it 'does not mutate the source of a merge' do\n      a = { 'a' => { 'b' => 'va' }, 'c' => 2 }\n      b = { 'a' => { 'b' => 'vb' }, 'b' => 3}\n      c = MergeStrategy.strategy(:deep).merge(a, b);\n      expect(a).to eql({ 'a' => { 'b' => 'va' }, 'c' => 2 })\n      expect(b).to eql({ 'a' => { 'b' => 'vb' }, 'b' => 3 })\n      expect(c).to eql({ 'a' => { 'b' => 'va' }, 'b' => 3, 'c' => 2 })\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/migration_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\nrequire 'puppet/loaders'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\nrequire 'puppet/parser/e4_parser_adapter'\n\ndescribe 'Puppet::Pops::MigrationMigrationChecker' do\n  include PuppetSpec::Pops\n  include PuppetSpec::Scope\n  before(:each) do\n    Puppet[:strict_variables] = true\n\n    # Puppetx cannot be loaded until the correct parser has been set (injector is turned off otherwise)\n    require 'puppet_x'\n\n    # Tests needs a known configuration of node/scope/compiler since it parses and evaluates\n    # snippets as the compiler will evaluate them, butwithout the overhead of compiling a complete\n    # catalog for each tested expression.\n    #\n    @parser  = Puppet::Pops::Parser::EvaluatingParser.new\n    @node = Puppet::Node.new('node.example.com')\n    @node.environment = Puppet::Node::Environment.create(:testing, [])\n    @compiler = Puppet::Parser::Compiler.new(@node)\n    @scope = Puppet::Parser::Scope.new(@compiler)\n    @scope.source = Puppet::Resource::Type.new(:node, 'node.example.com')\n    @scope.parent = @compiler.topscope\n  end\n\n  let(:scope) { @scope }\n\n  describe \"when there is no MigrationChecker in the PuppetContext\" do\n    it \"a null implementation of the MigrationChecker gets created (once per impl that needs one)\" do\n      migration_checker = Puppet::Pops::Migration::MigrationChecker.new()\n      expect(Puppet::Pops::Migration::MigrationChecker).to receive(:new).at_least(:once).and_return(migration_checker)\n      expect(Puppet::Pops::Parser::EvaluatingParser.new.evaluate_string(scope, \"1\", __FILE__)).to eq(1)\n    end\n  end\n\n  describe \"when there is a MigrationChecker in the Puppet Context\" do\n    it \"does not create any MigrationChecker instances when parsing and evaluating\" do\n      migration_checker = double()\n      expect(Puppet::Pops::Migration::MigrationChecker).not_to receive(:new)\n      Puppet.override({:migration_checker => migration_checker}, \"test-context\") do\n        Puppet::Pops::Parser::EvaluatingParser.new.evaluate_string(scope, \"true\", __FILE__)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/model/model_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe Puppet::Pops::Model do\n  it \"should be possible to create an instance of a model object\" do\n    nop = Puppet::Pops::Model::Nop.new(Puppet::Pops::Parser::Locator.locator('code', 'file'), 0, 0)\n    expect(nop.class).to eq(Puppet::Pops::Model::Nop)\n  end\nend\n\ndescribe Puppet::Pops::Model::Factory do\n  Factory = Puppet::Pops::Model::Factory\n  Model = Puppet::Pops::Model\n\n  it \"construct an arithmetic expression\" do\n    x = Factory.literal(10) + Factory.literal(20)\n    expect(x.is_a?(Factory)).to eq(true)\n    model = x.model\n    expect(model.is_a?(Model::ArithmeticExpression)).to eq(true)\n    expect(model.operator).to eq('+')\n    expect(model.left_expr.class).to eq(Model::LiteralInteger)\n    expect(model.right_expr.class).to eq(Model::LiteralInteger)\n    expect(model.left_expr.value).to eq(10)\n    expect(model.right_expr.value).to eq(20)\n  end\n\n  it \"should be easy to compare using a model tree dumper\" do\n    x = Factory.literal(10) + Factory.literal(20)\n    expect(Puppet::Pops::Model::ModelTreeDumper.new.dump(x.model)).to eq(\"(+ 10 20)\")\n  end\n\n  it \"builder should apply precedence\" do\n    x = Factory.literal(2) * Factory.literal(10) + Factory.literal(20)\n    expect(Puppet::Pops::Model::ModelTreeDumper.new.dump(x.model)).to eq(\"(+ (* 2 10) 20)\")\n  end\n\ndescribe \"should be describable with labels\"\n  it 'describes a PlanDefinition as \"Plan Definition\"' do\n    expect(Puppet::Pops::Model::ModelLabelProvider.new.label(Factory.PLAN('test', [], nil))).to eq(\"Plan Definition\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/model/pn_transformer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/pops/pn'\n\nmodule Puppet::Pops\nmodule Model\n\ndescribe 'Puppet::Pops::Model::PNTransformer' do\n  def call(name, *elements)\n    PN::Call.new(name, *elements.map { |e| lit(e) })\n  end\n\n  context 'transforms the expression' do\n    it '\"\\'hello\\'\" into the corresponding literal' do\n      x = Factory.literal('hello')\n      expect(Puppet::Pops::Model::PNTransformer.transform(x.model)).to eq(lit('hello'))\n    end\n\n    it '\"32\" into into the corresponding literal' do\n      x = Factory.literal(32)\n      expect(Puppet::Pops::Model::PNTransformer.transform(x.model)).to eq(lit(32))\n    end\n\n    it '\"true\" into into the corresponding literal' do\n      x = Factory.literal(true)\n      expect(Puppet::Pops::Model::PNTransformer.transform(x.model)).to eq(lit(true))\n    end\n\n    it '\"10 + 20\" into (+ 10 20)' do\n      x = Factory.literal(10) + Factory.literal(20)\n      expect(Puppet::Pops::Model::PNTransformer.transform(x.model)).to eq(call('+', 10, 20))\n    end\n\n    it '\"[10, 20]\" into into (array 10 20)' do\n      x = Factory.literal([10, 20])\n      expect(Puppet::Pops::Model::PNTransformer.transform(x.model)).to eq(call('array', 10, 20))\n    end\n\n    it '\"{a => 1, b => 2}\" into into (hash (=> (\"a\" 1)) (=> (\"b\" 2)))' do\n      x = Factory.HASH([Factory.KEY_ENTRY(Factory.literal('a'), Factory.literal(1)), Factory.KEY_ENTRY(Factory.literal('b'), Factory.literal(2))])\n      expect(Puppet::Pops::Model::PNTransformer.transform(x.model)).to eq(\n        call('hash', call('=>', 'a', 1), call('=>', 'b', 2)))\n    end\n\n    it 'replaces empty/nil body with a Nop' do\n      expect(Puppet::Pops::Model::PNTransformer.transform(nil)).to eq(call('nop'))\n    end\n  end\n\n  def lit(value)\n    value.is_a?(PN) ? value : PN::Literal.new(value)\n  end\nend\nend\nend\n\n"
  },
  {
    "path": "spec/unit/pops/parser/epp_parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nrequire File.join(File.dirname(__FILE__), '/../factory_rspec_helper')\n\nmodule EppParserRspecHelper\n  include FactoryRspecHelper\n  def parse(code)\n    parser = Puppet::Pops::Parser::EppParser.new()\n    parser.parse_string(code)\n  end\nend\n\ndescribe \"epp parser\" do\n  include EppParserRspecHelper\n\n  it \"should instantiate an epp parser\" do\n    parser = Puppet::Pops::Parser::EppParser.new()\n    expect(parser.class).to eq(Puppet::Pops::Parser::EppParser)\n  end\n\n  it \"should parse a code string and return a program with epp\" do\n    parser = Puppet::Pops::Parser::EppParser.new()\n    model = parser.parse_string(\"Nothing to see here, move along...\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.class).to eq(Puppet::Pops::Model::LambdaExpression)\n    expect(model.body.body.class).to eq(Puppet::Pops::Model::EppExpression)\n  end\n\n  context \"when facing bad input it reports\" do\n    it \"unbalanced tags\" do\n      expect { dump(parse(\"<% missing end tag\")) }.to raise_error(/Unbalanced/)\n    end\n\n    it \"abrupt end\" do\n      expect { dump(parse(\"dum di dum di dum <%\")) }.to raise_error(/Unbalanced/)\n    end\n\n    it \"nested epp tags\" do\n      expect { dump(parse(\"<% $a = 10 <% $b = 20 %>%>\")) }.to raise_error(/Syntax error/)\n    end\n\n    it \"nested epp expression tags\" do\n      expect { dump(parse(\"<%= 1+1 <%= 2+2 %>%>\")) }.to raise_error(/Syntax error/)\n    end\n\n    it \"rendering sequence of expressions\" do\n      expect { dump(parse(\"<%= 1 2 3 %>\")) }.to raise_error(/Syntax error/)\n    end\n  end\n\n  context \"handles parsing of\" do\n    it \"text (and nothing else)\" do\n      expect(dump(parse(\"Hello World\"))).to eq([\n        \"(lambda (epp (block\",\n        \"  (render-s 'Hello World')\",\n        \")))\"].join(\"\\n\"))\n    end\n\n    it \"template parameters\" do\n      expect(dump(parse(\"<%|$x|%>Hello World\"))).to eq([\n        \"(lambda (parameters x) (epp (block\",\n        \"  (render-s 'Hello World')\",\n        \")))\"].join(\"\\n\"))\n    end\n\n    it \"template parameters with default\" do\n      expect(dump(parse(\"<%|$x='cigar'|%>Hello World\"))).to eq([\n        \"(lambda (parameters (= x 'cigar')) (epp (block\",\n        \"  (render-s 'Hello World')\",\n        \")))\"].join(\"\\n\"))\n    end\n\n    it \"template parameters with and without default\" do\n      expect(dump(parse(\"<%|$x='cigar', $y|%>Hello World\"))).to eq([\n        \"(lambda (parameters (= x 'cigar') y) (epp (block\",\n        \"  (render-s 'Hello World')\",\n        \")))\"].join(\"\\n\"))\n    end\n\n    it \"template parameters + additional setup\" do\n      expect(dump(parse(\"<%|$x| $y = 10 %>Hello World\"))).to eq([ \n        \"(lambda (parameters x) (epp (block\",\n        \"  (= $y 10)\",\n        \"  (render-s 'Hello World')\",\n        \")))\"].join(\"\\n\"))\n    end\n\n    it \"comments\" do\n      expect(dump(parse(\"<%#($x='cigar', $y)%>Hello World\"))).to eq([\n        \"(lambda (epp (block\",\n        \"  (render-s 'Hello World')\",\n        \")))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"verbatim epp tags\" do\n      expect(dump(parse(\"<%% contemplating %%>Hello World\"))).to eq([\n        \"(lambda (epp (block\",\n        \"  (render-s '<% contemplating %>Hello World')\",\n        \")))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"expressions\" do\n      expect(dump(parse(\"We all live in <%= 3.14 - 2.14 %> world\"))).to eq([\n        \"(lambda (epp (block\",\n        \"  (render-s 'We all live in ')\",\n        \"  (render (- 3.14 2.14))\",\n        \"  (render-s ' world')\",\n        \")))\"\n      ].join(\"\\n\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/evaluating_parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\n\ndescribe 'The Evaluating Parser' do\n\n  include PuppetSpec::Pops\n  include PuppetSpec::Scope\n\n  let(:acceptor) {  Puppet::Pops::Validation::Acceptor.new() }\n  let(:scope) { s = create_test_scope_for_node(node); s }\n  let(:node) { 'node.example.com' }\n\n  def quote(x)\n    Puppet::Pops::Parser::EvaluatingParser.quote(x)\n  end\n\n  def evaluator()\n    Puppet::Pops::Parser::EvaluatingParser.new()\n  end\n\n  def evaluate(s)\n    evaluator.evaluate(scope, quote(s))\n  end\n\n  def test(x)\n    expect(evaluator.evaluate_string(scope, quote(x))).to eq(x)\n  end\n\n  def test_interpolate(x, y)\n    scope['a'] = 'expansion'\n    expect(evaluator.evaluate_string(scope, quote(x))).to eq(y)\n  end\n\n  context 'when evaluating' do\n    it 'should produce an empty string with no change' do\n      test('')\n    end\n\n    it 'should produce a normal string with no change' do\n      test('A normal string')\n    end\n\n    it 'should produce a string with newlines with no change' do\n      test(\"A\\nnormal\\nstring\")\n    end\n\n    it 'should produce a string with escaped newlines with no change' do\n      test(\"A\\\\nnormal\\\\nstring\")\n    end\n\n    it 'should produce a string containing quotes without change' do\n      test('This \" should remain untouched')\n    end\n\n    it 'should produce a string containing escaped quotes without change' do\n      test('This \\\" should remain untouched')\n    end\n\n    it 'should expand ${a} variables' do\n      test_interpolate('This ${a} was expanded', 'This expansion was expanded')\n    end\n\n    it 'should expand quoted ${a} variables' do\n      test_interpolate('This \"${a}\" was expanded', 'This \"expansion\" was expanded')\n    end\n\n    it 'should not expand escaped ${a}' do\n      test_interpolate('This \\${a} was not expanded', 'This ${a} was not expanded')\n    end\n\n    it 'should expand $a variables' do\n      test_interpolate('This $a was expanded', 'This expansion was expanded')\n    end\n\n    it 'should expand quoted $a variables' do\n      test_interpolate('This \"$a\" was expanded', 'This \"expansion\" was expanded')\n    end\n\n    it 'should not expand escaped $a' do\n      test_interpolate('This \\$a was not expanded', 'This $a was not expanded')\n    end\n\n    it 'should produce an single space from a \\s' do\n      test_interpolate(\"\\\\s\", ' ')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/lexer2_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/match_tokens2'\nrequire 'puppet/pops'\nrequire 'puppet/pops/parser/lexer2'\n\nmodule EgrammarLexer2Spec\n  def tokens_scanned_from(s)\n    lexer = Puppet::Pops::Parser::Lexer2.new\n    lexer.string = s\n    lexer.fullscan[0..-2]\n  end\n\n  def epp_tokens_scanned_from(s)\n    lexer = Puppet::Pops::Parser::Lexer2.new\n    lexer.string = s\n    lexer.fullscan_epp[0..-2]\n  end\nend\n\ndescribe 'Lexer2' do\n  include EgrammarLexer2Spec\n\n  {\n    :LISTSTART => '[',\n    :RBRACK => ']',\n    :LBRACE => '{',\n    :RBRACE => '}',\n    :WSLPAREN => '(', # since it is first on a line it is special (LPAREN handled separately)\n    :RPAREN => ')',\n    :EQUALS => '=',\n    :ISEQUAL => '==',\n    :GREATEREQUAL => '>=',\n    :GREATERTHAN => '>',\n    :LESSTHAN => '<',\n    :LESSEQUAL => '<=',\n    :NOTEQUAL => '!=',\n    :NOT => '!',\n    :COMMA => ',',\n    :DOT => '.',\n    :COLON => ':',\n    :AT => '@',\n    :LLCOLLECT => '<<|',\n    :RRCOLLECT => '|>>',\n    :LCOLLECT => '<|',\n    :RCOLLECT => '|>',\n    :SEMIC => ';',\n    :QMARK => '?',\n    :OTHER => '\\\\',\n    :FARROW => '=>',\n    :PARROW => '+>',\n    :APPENDS => '+=',\n    :DELETES => '-=',\n    :PLUS => '+',\n    :MINUS => '-',\n    :DIV => '/',\n    :TIMES => '*',\n    :LSHIFT => '<<',\n    :RSHIFT => '>>',\n    :MATCH => '=~',\n    :NOMATCH => '!~',\n    :IN_EDGE => '->',\n    :OUT_EDGE => '<-',\n    :IN_EDGE_SUB => '~>',\n    :OUT_EDGE_SUB => '<~',\n    :PIPE => '|',\n  }.each do |name, string|\n    it \"should lex a token named #{name.to_s}\" do\n      expect(tokens_scanned_from(string)).to match_tokens2(name)\n    end\n  end\n\n  it \"should lex [ in position after non whitespace as LBRACK\" do\n    expect(tokens_scanned_from(\"a[\")).to match_tokens2(:NAME, :LBRACK)\n  end\n\n  {\n    \"case\"     => :CASE,\n    \"class\"    => :CLASS,\n    \"default\"  => :DEFAULT,\n    \"define\"   => :DEFINE,\n#    \"import\" => :IMPORT, # done as a function in egrammar\n    \"if\"       => :IF,\n    \"elsif\"    => :ELSIF,\n    \"else\"     => :ELSE,\n    \"inherits\" => :INHERITS,\n    \"node\"     => :NODE,\n    \"and\"      => :AND,\n    \"or\"       => :OR,\n    \"undef\"    => :UNDEF,\n    \"false\"    => :BOOLEAN,\n    \"true\"     => :BOOLEAN,\n    \"in\"       => :IN,\n    \"unless\"   => :UNLESS,\n    \"private\"  => :PRIVATE,\n    \"type\"     => :TYPE,\n    \"attr\"     => :ATTR,\n  }.each do |string, name|\n    it \"should lex a keyword from '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2(name)\n    end\n  end\n\n  context 'when --no-tasks (the default)' do\n    it \"should lex a NAME from 'plan'\" do\n      expect(tokens_scanned_from('plan')).to match_tokens2(:NAME)\n    end\n  end\n\n  context 'when --tasks' do\n    before(:each) { Puppet[:tasks] = true }\n    after(:each) { Puppet[:tasks] = false }\n\n    it \"should lex a keyword from 'plan'\" do\n      expect(tokens_scanned_from('plan')).to match_tokens2(:PLAN)\n    end\n  end\n\n  # TODO: Complete with all edge cases\n  [ 'A', 'A::B', '::A', '::A::B',].each do |string|\n    it \"should lex a CLASSREF on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:CLASSREF, string])\n    end\n  end\n\n  # TODO: Complete with all edge cases\n  [ 'a', 'a::b', '::a', '::a::b',].each do |string|\n    it \"should lex a NAME on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:NAME, string])\n    end\n  end\n\n  [ 'a-b', 'a--b', 'a-b-c', '_x'].each do |string|\n    it \"should lex a BARE WORD STRING on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:WORD, string])\n    end\n  end\n\n  [ '_x::y', 'x::_y'].each do |string|\n    it \"should consider the bare word '#{string}' to be a WORD\" do\n      expect(tokens_scanned_from(string)).to match_tokens2(:WORD)\n    end\n  end\n\n  { '-a'   =>      [:MINUS, :NAME],\n    '--a'  =>      [:MINUS, :MINUS, :NAME],\n    'a-'   =>      [:NAME, :MINUS],\n    'a- b'   =>    [:NAME, :MINUS, :NAME],\n    'a--'  =>      [:NAME, :MINUS, :MINUS],\n    'a-$3' =>      [:NAME, :MINUS, :VARIABLE],\n  }.each do |source, expected|\n    it \"should lex leading and trailing hyphens from #{source}\" do\n      expect(tokens_scanned_from(source)).to match_tokens2(*expected)\n    end\n  end\n\n  { 'false'=>false, 'true'=>true}.each do |string, value|\n    it \"should lex a BOOLEAN on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:BOOLEAN, value])\n    end\n  end\n\n  [ '0', '1', '2982383139'].each do |string|\n    it \"should lex a decimal integer NUMBER on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:NUMBER, string])\n    end\n  end\n\n  { ' 1' => '1', '1 ' => '1', ' 1 ' => '1'}.each do |string, value|\n    it \"should lex a NUMBER with surrounding space '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:NUMBER, value])\n    end\n  end\n\n  [ '0.0', '0.1', '0.2982383139', '29823.235', '10e23', '10e-23', '1.234e23'].each do |string|\n    it \"should lex a decimal floating point NUMBER on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:NUMBER, string])\n    end\n  end\n\n  [ '00', '01', '0123', '0777'].each do |string|\n    it \"should lex an octal integer NUMBER on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:NUMBER, string])\n    end\n  end\n\n  [ '0x0', '0x1', '0xa', '0xA', '0xabcdef', '0xABCDEF'].each do |string|\n    it \"should lex an hex integer NUMBER on the form '#{string}'\" do\n      expect(tokens_scanned_from(string)).to match_tokens2([:NUMBER, string])\n    end\n  end\n\n  { \"''\"      => '',\n    \"'a'\"     => 'a',\n    \"'a\\\\'b'\" =>\"a'b\",\n    \"'a\\\\rb'\" =>\"a\\\\rb\",\n    \"'a\\\\nb'\" =>\"a\\\\nb\",\n    \"'a\\\\tb'\" =>\"a\\\\tb\",\n    \"'a\\\\sb'\" =>\"a\\\\sb\",\n    \"'a\\\\$b'\" =>\"a\\\\$b\",\n    \"'a\\\\\\\"b'\" =>\"a\\\\\\\"b\",\n    \"'a\\\\\\\\b'\" =>\"a\\\\b\",\n    \"'a\\\\\\\\'\" =>\"a\\\\\",\n  }.each do |source, expected|\n    it \"should lex a single quoted STRING on the form #{source}\" do\n      expect(tokens_scanned_from(source)).to match_tokens2([:STRING, expected])\n    end\n  end\n\n  { \"''\"      => [2, \"\"],\n    \"'a'\"     => [3, \"a\"],\n    \"'a\\\\'b'\" => [6, \"a'b\"],\n    }.each do |source, expected|\n      it \"should lex a single quoted STRING on the form #{source} as having length #{expected[0]}\" do\n       length, value = expected\n       expect(tokens_scanned_from(source)).to match_tokens2([:STRING, value, {:line => 1, :pos=>1, :length=> length}])\n      end\n    end\n\n  { '\"\"'      => '',\n    '\"a\"'     => 'a',\n    '\"a\\'b\"'  => \"a'b\",\n  }.each do |source, expected|\n    it \"should lex a double quoted STRING on the form #{source}\" do\n      expect(tokens_scanned_from(source)).to match_tokens2([:STRING, expected])\n    end\n  end\n\n  { '\"a$x b\"'     => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>2 }],\n                      [:VARIABLE, 'x',   {:line => 1, :pos=>3, :length=>2 }],\n                      [:DQPOST,   ' b',  {:line => 1, :pos=>5, :length=>3 }]],\n\n    '\"a$x.b\"'     => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>2 }],\n                      [:VARIABLE, 'x',   {:line => 1, :pos=>3, :length=>2 }],\n                      [:DQPOST,   '.b',  {:line => 1, :pos=>5, :length=>3 }]],\n\n    '\"$x.b\"'      => [[:DQPRE,    '',    {:line => 1, :pos=>1, :length=>1 }],\n                      [:VARIABLE, 'x',   {:line => 1, :pos=>2, :length=>2 }],\n                      [:DQPOST,   '.b',  {:line => 1, :pos=>4, :length=>3 }]],\n\n    '\"a$x\"'       => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>2 }],\n                      [:VARIABLE, 'x',   {:line => 1, :pos=>3, :length=>2 }],\n                      [:DQPOST,   '',    {:line => 1, :pos=>5, :length=>1 }]],\n\n    '\"a${x}\"'     => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>4 }],\n                      [:VARIABLE, 'x',   {:line => 1, :pos=>5, :length=>1 }],\n                      [:DQPOST,   '',    {:line => 1, :pos=>7, :length=>1 }]],\n\n    '\"a${_x}\"'    => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>4 }],\n                      [:VARIABLE, '_x',  {:line => 1, :pos=>5, :length=>2 }],\n                      [:DQPOST,   '',    {:line => 1, :pos=>8, :length=>1 }]],\n\n    '\"a${y::_x}\"' => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>4 }],\n                      [:VARIABLE, 'y::_x',  {:line => 1, :pos=>5, :length=>5 }],\n                      [:DQPOST,   '',    {:line => 1, :pos=>11, :length=>1 }]],\n\n    '\"a${_x[1]}\"' => [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>4 }],\n                      [:VARIABLE, '_x',  {:line => 1, :pos=>5, :length=>2 }],\n                      [:LBRACK,   '[',   {:line => 1, :pos=>7, :length=>1 }],\n                      [:NUMBER,   '1',   {:line => 1, :pos=>8, :length=>1 }],\n                      [:RBRACK,   ']',   {:line => 1, :pos=>9, :length=>1 }],\n                      [:DQPOST,   '',    {:line => 1, :pos=>11, :length=>1 }]],\n\n    '\"a${_x.foo}\"'=> [[:DQPRE,    'a',   {:line => 1, :pos=>1, :length=>4 }],\n                      [:VARIABLE, '_x',  {:line => 1, :pos=>5, :length=>2 }],\n                      [:DOT,      '.',   {:line => 1, :pos=>7, :length=>1 }],\n                      [:NAME,     'foo', {:line => 1, :pos=>8, :length=>3 }],\n                      [:DQPOST,   '',    {:line => 1, :pos=>12, :length=>1 }]],\n  }.each do |source, expected|\n    it \"should lex an interpolated variable 'x' from #{source}\" do\n      expect(tokens_scanned_from(source)).to match_tokens2(*expected)\n    end\n  end\n\n  { '\"$\"'      => '$',\n    '\"a$\"'     => 'a$',\n    '\"a$%b\"'  => \"a$%b\",\n    '\"a$$\"'  => \"a$$\",\n    '\"a$$%\"'  => \"a$$%\",\n  }.each do |source, expected|\n    it \"should lex interpolation including false starts #{source}\" do\n      expect(tokens_scanned_from(source)).to match_tokens2([:STRING, expected])\n    end\n  end\n\n  it \"differentiates between foo[x] and foo [x] (whitespace)\" do\n    expect(tokens_scanned_from(\"$a[1]\")).to match_tokens2(:VARIABLE, :LBRACK, :NUMBER, :RBRACK)\n    expect(tokens_scanned_from(\"$a [1]\")).to match_tokens2(:VARIABLE, :LISTSTART, :NUMBER, :RBRACK)\n    expect(tokens_scanned_from(\"a[1]\")).to match_tokens2(:NAME, :LBRACK, :NUMBER, :RBRACK)\n    expect(tokens_scanned_from(\"a [1]\")).to match_tokens2(:NAME, :LISTSTART, :NUMBER, :RBRACK)\n  end\n\n  it \"differentiates between '(' first on line, and not first on line\" do\n    expect(tokens_scanned_from(\"(\")).to match_tokens2(:WSLPAREN)\n    expect(tokens_scanned_from(\"\\n(\")).to match_tokens2(:WSLPAREN)\n    expect(tokens_scanned_from(\"\\n\\r(\")).to match_tokens2(:WSLPAREN)\n    expect(tokens_scanned_from(\"\\n\\t(\")).to match_tokens2(:WSLPAREN)\n    expect(tokens_scanned_from(\"\\n\\r\\t(\")).to match_tokens2(:WSLPAREN)\n    expect(tokens_scanned_from(\"\\n\\u00a0(\")).to match_tokens2(:WSLPAREN)\n\n    expect(tokens_scanned_from(\"x(\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"\\nx(\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"\\n\\rx(\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"\\n\\tx(\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"\\n\\r\\tx(\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"\\n\\u00a0x(\")).to match_tokens2(:NAME, :LPAREN)\n\n    expect(tokens_scanned_from(\"x (\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"x\\t(\")).to match_tokens2(:NAME, :LPAREN)\n    expect(tokens_scanned_from(\"x\\u00a0(\")).to match_tokens2(:NAME, :LPAREN)\n  end\n\n  it \"skips whitepsace\" do\n    expect(tokens_scanned_from(\" if if if \")).to match_tokens2(:IF, :IF, :IF)\n    expect(tokens_scanned_from(\" if \\n\\r\\t\\nif if \")).to match_tokens2(:IF, :IF, :IF)\n    expect(tokens_scanned_from(\" if \\n\\r\\t\\n\\u00a0if\\u00a0 if \")).to match_tokens2(:IF, :IF, :IF)\n  end\n\n  it \"skips single line comments\" do\n    expect(tokens_scanned_from(\"if # comment\\nif\")).to match_tokens2(:IF, :IF)\n  end\n\n  [\"if /* comment */\\nif\",\n    \"if /* comment\\n */\\nif\",\n    \"if /*\\n comment\\n */\\nif\",\n    ].each do |source|\n    it \"skips multi line comments\" do\n      expect(tokens_scanned_from(source)).to match_tokens2(:IF, :IF)\n    end\n  end\n\n  it 'detects unterminated multiline comment' do\n    expect { tokens_scanned_from(\"/* not terminated\\nmultiline\\ncomment\") }.to raise_error(Puppet::ParseErrorWithIssue) { |e|\n      expect(e.issue_code).to be(Puppet::Pops::Issues::UNCLOSED_MLCOMMENT.issue_code)\n    }\n  end\n\n  { \"=~\" => [:MATCH, \"=~ /./\"],\n    \"!~\" => [:NOMATCH, \"!~ /./\"],\n    \",\"  => [:COMMA, \", /./\"],\n\n    \"(\"       => [:WSLPAREN, \"( /./\"],\n    \"x (\"     => [[:NAME, :LPAREN], \"x ( /./\"],\n    \"x\\\\t (\"  => [[:NAME, :LPAREN], \"x\\t ( /./\"],\n\n    \"[ (liststart)\"             => [:LISTSTART, \"[ /./\"],\n    \"[ (LBRACK)\"                => [[:NAME, :LBRACK], \"a[ /./\"],\n    \"[ (liststart after name)\"  => [[:NAME, :LISTSTART], \"a [ /./\"],\n    \"{\"  => [:LBRACE, \"{ /./\"],\n    \"+\"  => [:PLUS, \"+ /./\"],\n    \"-\"  => [:MINUS, \"- /./\"],\n    \"*\"  => [:TIMES, \"* /./\"],\n    \";\"  => [:SEMIC, \"; /./\"],\n  }.each do |token, entry|\n    it \"should lex regexp after '#{token}'\" do\n      expected = [entry[0], :REGEX].flatten\n      expect(tokens_scanned_from(entry[1])).to match_tokens2(*expected)\n    end\n  end\n\n  it \"should lex a simple expression\" do\n    expect(tokens_scanned_from('1 + 1')).to match_tokens2([:NUMBER, '1'], :PLUS, [:NUMBER, '1'])\n  end\n\n  { \"1\"     => [\"1 /./\",       [:NUMBER, :DIV, :DOT, :DIV]],\n    \"'a'\"   => [\"'a' /./\",     [:STRING, :DIV, :DOT, :DIV]],\n    \"true\"  => [\"true /./\",    [:BOOLEAN, :DIV, :DOT, :DIV]],\n    \"false\" => [\"false /./\",   [:BOOLEAN, :DIV, :DOT, :DIV]],\n    \"/./\"   => [\"/./ /./\",     [:REGEX, :DIV, :DOT, :DIV]],\n    \"a\"     => [\"a /./\",       [:NAME, :DIV, :DOT, :DIV]],\n    \"A\"     => [\"A /./\",       [:CLASSREF, :DIV, :DOT, :DIV]],\n    \")\"     => [\") /./\",       [:RPAREN, :DIV, :DOT, :DIV]],\n    \"]\"     => [\"] /./\",       [:RBRACK, :DIV, :DOT, :DIV]],\n    \"|>\"     => [\"|> /./\",     [:RCOLLECT, :DIV, :DOT, :DIV]],\n    \"|>>\"    => [\"|>> /./\",    [:RRCOLLECT, :DIV, :DOT, :DIV]],\n    \"$x\"     => [\"$x /1/\",     [:VARIABLE, :DIV, :NUMBER, :DIV]],\n    \"a-b\"    => [\"a-b /1/\",    [:WORD, :DIV, :NUMBER, :DIV]],\n    '\"a$a\"'  => ['\"a$a\" /./',  [:DQPRE, :VARIABLE, :DQPOST, :DIV, :DOT, :DIV]],\n  }.each do |token, entry|\n    it \"should not lex regexp after '#{token}'\" do\n      expect(tokens_scanned_from(entry[ 0 ])).to match_tokens2(*entry[ 1 ])\n    end\n  end\n\n  it 'should lex assignment' do\n    expect(tokens_scanned_from(\"$a = 10\")).to match_tokens2([:VARIABLE, \"a\"], :EQUALS, [:NUMBER, '10'])\n  end\n\n# TODO: Tricky, and heredoc not supported yet\n#  it \"should not lex regexp after heredoc\" do\n#    tokens_scanned_from(\"1 / /./\").should match_tokens2(:NUMBER, :DIV, :REGEX)\n#  end\n\n  it \"should lex regexp at beginning of input\" do\n    expect(tokens_scanned_from(\" /./\")).to match_tokens2(:REGEX)\n  end\n\n  it \"should lex regexp right of div\" do\n    expect(tokens_scanned_from(\"1 / /./\")).to match_tokens2(:NUMBER, :DIV, :REGEX)\n  end\n\n  it 'should lex regexp with escaped slash' do\n    scanned = tokens_scanned_from('/\\//')\n    expect(scanned).to match_tokens2(:REGEX)\n    expect(scanned[0][1][:value]).to eql(Regexp.new('/'))\n  end\n\n  it 'should lex regexp with escaped backslash' do\n    scanned = tokens_scanned_from('/\\\\\\\\/')\n    expect(scanned).to match_tokens2(:REGEX)\n    expect(scanned[0][1][:value]).to eql(Regexp.new('\\\\\\\\'))\n  end\n\n  it 'should lex regexp with escaped backslash followed escaped slash ' do\n    scanned = tokens_scanned_from('/\\\\\\\\\\\\//')\n    expect(scanned).to match_tokens2(:REGEX)\n    expect(scanned[0][1][:value]).to eql(Regexp.new('\\\\\\\\/'))\n  end\n\n  it 'should lex regexp with escaped slash followed escaped backslash ' do\n    scanned = tokens_scanned_from('/\\\\/\\\\\\\\/')\n    expect(scanned).to match_tokens2(:REGEX)\n    expect(scanned[0][1][:value]).to eql(Regexp.new('/\\\\\\\\'))\n  end\n\n  it 'should not lex regexp with escaped ending slash' do\n    expect(tokens_scanned_from('/\\\\/')).to match_tokens2(:DIV, :OTHER, :DIV)\n  end\n\n  it \"should accept newline in a regular expression\" do\n    scanned = tokens_scanned_from(\"/\\n.\\n/\")\n    # Note that strange formatting here is important\n    expect(scanned[0][1][:value]).to eql(/\n.\n/)\n  end\n\n  context 'when lexer lexes heredoc' do\n    it 'lexes tag, syntax and escapes, margin and right trim' do\n      code = <<-CODE\n      @(END:syntax/t)\n      Tex\\\\tt\\\\n\n      |- END\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2([:HEREDOC, 'syntax'], :SUBLOCATE, [:STRING, \"Tex\\tt\\\\n\"])\n    end\n\n    it 'lexes \"tag\", syntax and escapes, margin, right trim and interpolation' do\n      code = <<-CODE\n      @(\"END\":syntax/t)\n      Tex\\\\tt\\\\n$var After\n      |- END\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2(\n        [:HEREDOC, 'syntax'],\n        :SUBLOCATE,\n        [:DQPRE, \"Tex\\tt\\\\n\"],\n        [:VARIABLE, \"var\"],\n        [:DQPOST, \" After\"]\n        )\n    end\n\n    it 'strips only last newline when using trim option' do\n      code = <<-CODE.unindent\n        @(END)\n        Line 1\n        \n        Line 2\n        -END\n        CODE\n      expect(tokens_scanned_from(code)).to match_tokens2(\n        [:HEREDOC, ''],\n        [:SUBLOCATE, [\"Line 1\\n\", \"\\n\", \"Line 2\\n\"]],\n        [:STRING, \"Line 1\\n\\nLine 2\"],\n      )\n    end\n\n    it 'strips only one newline at the end when using trim option' do\n      code = <<-CODE.unindent\n        @(END)\n        Line 1\n        Line 2\n        \n        -END\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2(\n        [:HEREDOC, ''],\n        [:SUBLOCATE, [\"Line 1\\n\", \"Line 2\\n\", \"\\n\"]],\n        [:STRING, \"Line 1\\nLine 2\\n\"],\n      )\n    end\n\n    context 'with bad syntax' do\n      def expect_issue(code, issue)\n        expect { tokens_scanned_from(code) }.to raise_error(Puppet::ParseErrorWithIssue) { |e|\n          expect(e.issue_code).to be(issue.issue_code)\n        }\n      end\n\n      it 'detects and reports HEREDOC_UNCLOSED_PARENTHESIS' do\n        code = <<-CODE\n        @(END:syntax/t\n        Text\n        |- END\n        CODE\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_UNCLOSED_PARENTHESIS)\n      end\n\n      it 'detects and reports HEREDOC_WITHOUT_END_TAGGED_LINE' do\n        code = <<-CODE\n        @(END:syntax/t)\n        Text\n        CODE\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_WITHOUT_END_TAGGED_LINE)\n      end\n\n      it 'detects and reports HEREDOC_INVALID_ESCAPE' do\n        code = <<-CODE\n        @(END:syntax/x)\n        Text\n        |- END\n        CODE\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_INVALID_ESCAPE)\n      end\n\n      it 'detects and reports HEREDOC_INVALID_SYNTAX' do\n        code = <<-CODE\n        @(END:syntax/t/p)\n        Text\n        |- END\n        CODE\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_INVALID_SYNTAX)\n      end\n\n      it 'detects and reports HEREDOC_WITHOUT_TEXT' do\n        code = '@(END:syntax/t)'\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_WITHOUT_TEXT)\n      end\n\n      it 'detects and reports HEREDOC_EMPTY_ENDTAG' do\n        code = <<-CODE\n        @(\"\")\n        Text\n        |-END\n        CODE\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_EMPTY_ENDTAG)\n      end\n\n      it 'detects and reports HEREDOC_MULTIPLE_AT_ESCAPES' do\n        code = <<-CODE\n        @(END:syntax/tst)\n        Tex\\\\tt\\\\n\n        |- END\n        CODE\n        expect_issue(code, Puppet::Pops::Issues::HEREDOC_MULTIPLE_AT_ESCAPES)\n      end\n    end\n  end\n  context 'when not given multi byte characters' do\n    it 'produces byte offsets for tokens' do\n      code = <<-\"CODE\"\n1 2\\n3\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2(\n        [:NUMBER, '1', {:line => 1, :offset => 0, :length=>1}],\n        [:NUMBER, '2', {:line => 1, :offset => 2, :length=>1}],\n        [:NUMBER, '3', {:line => 2, :offset => 4, :length=>1}]\n      )\n    end\n  end\n\n  context 'when dealing with multi byte characters' do\n    it 'should support unicode characters' do\n      code = <<-CODE\n      \"x\\\\u2713y\"\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2([:STRING, \"x\\u2713y\"])\n    end\n\n    it 'should support adjacent short form unicode characters' do\n      code = <<-CODE\n      \"x\\\\u2713\\\\u2713y\"\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2([:STRING, \"x\\u2713\\u2713y\"])\n    end\n\n    it 'should support unicode characters in long form' do\n      code = <<-CODE\n      \"x\\\\u{1f452}y\"\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2([:STRING, \"x\\u{1f452}y\"])\n    end\n\n    it 'can escape the unicode escape' do\n      code = <<-\"CODE\"\n      \"x\\\\\\\\u{1f452}y\"\n      CODE\n      expect(tokens_scanned_from(code)).to match_tokens2([:STRING, \"x\\\\u{1f452}y\"])\n    end\n\n    it 'produces byte offsets that counts each byte in a comment' do\n      code = <<-\"CODE\"\n      # \\u{0400}\\na\n      CODE\n      expect(tokens_scanned_from(code.strip)).to match_tokens2([:NAME, 'a', {:line => 2, :offset => 5, :length=>1}])\n    end\n\n    it 'produces byte offsets that counts each byte in value token' do\n      code = <<-\"CODE\"\n      '\\u{0400}'\\na\n      CODE\n      expect(tokens_scanned_from(code.strip)).to match_tokens2(\n        [:STRING, \"\\u{400}\", {:line => 1, :offset => 0, :length=>4}],\n        [:NAME, 'a', {:line => 2, :offset => 5, :length=>1}]\n      )\n    end\n\n    it 'should not select LISTSTART token when preceded by multibyte chars' do\n      # This test is sensitive to the number of multibyte characters and position of the expressions\n      # within the string - it is designed to fail if the position is calculated on the byte offset of the '['\n      # instead of the char offset.\n      #\n      code = \"$a = '\\u00f6\\u00fc\\u00fc\\u00fc\\u00fc\\u00e4\\u00e4\\u00f6\\u00e4'\\nnotify {'x': message => B['dkda'] }\\n\"\n      expect(tokens_scanned_from(code)).to match_tokens2(\n        :VARIABLE, :EQUALS, :STRING,\n        [:NAME, 'notify'], :LBRACE,\n        [:STRING, 'x'], :COLON,\n        :NAME, :FARROW, :CLASSREF, :LBRACK, :STRING, :RBRACK,\n        :RBRACE)\n    end\n  end\n\n  context 'when lexing epp' do\n    it 'epp can contain just text' do\n      code = <<-CODE\n      This is just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(:EPP_START, [:RENDER_STRING, \"      This is just text\\n\"])\n    end\n\n    it 'epp can contain text with interpolated rendered expressions' do\n      code = <<-CODE\n      This is <%= $x %> just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"      This is \"],\n      [:RENDER_EXPR, nil],\n      [:VARIABLE, \"x\"],\n      [:EPP_END, \"%>\"],\n      [:RENDER_STRING, \" just text\\n\"]\n      )\n    end\n\n    it 'epp can contain text with trimmed interpolated rendered expressions' do\n      code = <<-CODE\n      This is <%= $x -%> just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"      This is \"],\n      [:RENDER_EXPR, nil],\n      [:VARIABLE, \"x\"],\n      [:EPP_END_TRIM, \"-%>\"],\n      [:RENDER_STRING, \"just text\\n\"]\n      )\n    end\n\n    it 'epp can contain text with expressions that are not rendered' do\n      code = <<-CODE\n      This is <% $x=10 %> just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"      This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \" just text\\n\"]\n      )\n    end\n\n    it 'epp can skip trailing space and newline in tail text' do\n      # note that trailing whitespace is significant on one of the lines\n      code = <<-CODE.unindent\n      This is <% $x=10 -%>   \n      just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"just text\\n\"]\n      )\n    end\n\n    it 'epp can skip comments' do\n      code = <<-CODE.unindent\n      This is <% $x=10 -%>\n      <%# This is an epp comment -%>\n      just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"just text\\n\"]\n      )\n    end\n\n    it 'epp comments does not strip left whitespace when preceding is right trim' do\n      code = <<-CODE.unindent\n      This is <% $x=10 -%>\n         <%# This is an epp comment %>\n      just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"   \\njust text\\n\"]\n      )\n    end\n\n    it 'epp comments does not strip left whitespace when preceding is not right trim' do\n      code = <<-CODE.unindent\n      This is <% $x=10 %>\n          <%# This is an epp comment -%>\n      just text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"\\n    just text\\n\"]\n      )\n    end\n\n    it 'epp comments can trim left with <%#-' do\n      # test has 4 space before comment and 3 after it\n      # check that there is 3 spaces before the 'and'\n      #\n      code = <<-CODE.unindent\n      This is <% $x=10 -%>\n      no-space-after-me:    <%#- This is an epp comment %>   and\n      some text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"no-space-after-me:   and\\nsome text\\n\"]\n      )\n    end\n\n    it 'puppet comment in left trimming epp tag works when containing a new line' do\n      # test has 4 space before comment and 3 after it\n      # check that there is 3 spaces before the 'and'\n      #\n      code = <<-CODE.unindent\n      This is <% $x=10 -%>\n      no-space-after-me:    <%-# This is an puppet comment\n        %>   and\n      some text\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"no-space-after-me:\"],\n      [:RENDER_STRING, \"   and\\nsome text\\n\"]\n      )\n    end\n\n    it 'epp can escape epp tags' do\n      code = <<-CODE\n      This is <% $x=10 -%>\n      <%% this is escaped epp %%>\n      CODE\n      expect(epp_tokens_scanned_from(code)).to match_tokens2(\n      :EPP_START,\n      [:RENDER_STRING, \"      This is \"],\n      [:VARIABLE, \"x\"],\n      :EQUALS,\n      [:NUMBER, \"10\"],\n      [:RENDER_STRING, \"      <% this is escaped epp %>\\n\"]\n      )\n    end\n\n    context 'with bad epp syntax' do\n      def expect_issue(code, issue)\n        expect { epp_tokens_scanned_from(code) }.to raise_error(Puppet::ParseErrorWithIssue) { |e|\n          expect(e.issue_code).to be(issue.issue_code)\n        }\n      end\n\n      it 'detects and reports EPP_UNBALANCED_TAG' do\n        expect_issue('<% asf', Puppet::Pops::Issues::EPP_UNBALANCED_TAG)\n      end\n\n      it 'detects and reports EPP_UNBALANCED_COMMENT' do\n        expect_issue('<%# asf', Puppet::Pops::Issues::EPP_UNBALANCED_COMMENT)\n      end\n\n      it 'detects and reports EPP_UNBALANCED_EXPRESSION' do\n        expect_issue('asf <%', Puppet::Pops::Issues::EPP_UNBALANCED_EXPRESSION)\n      end\n    end\n  end\n\n  context 'when parsing bad code' do\n    def expect_issue(code, issue)\n      expect { tokens_scanned_from(code) }.to raise_error(Puppet::ParseErrorWithIssue) do |e|\n        expect(e.issue_code).to be(issue.issue_code)\n      end\n    end\n\n    it 'detects and reports issue ILLEGAL_CLASS_REFERENCE' do\n      expect_issue('A::3', Puppet::Pops::Issues::ILLEGAL_CLASS_REFERENCE)\n    end\n\n    it 'detects and reports issue ILLEGAL_FULLY_QUALIFIED_CLASS_REFERENCE' do\n      expect_issue('::A::3', Puppet::Pops::Issues::ILLEGAL_FULLY_QUALIFIED_CLASS_REFERENCE)\n    end\n\n    it 'detects and reports issue ILLEGAL_FULLY_QUALIFIED_NAME' do\n      expect_issue('::a::3', Puppet::Pops::Issues::ILLEGAL_FULLY_QUALIFIED_NAME)\n    end\n\n    it 'detects and reports issue ILLEGAL_NUMBER' do\n     expect_issue('3g', Puppet::Pops::Issues::ILLEGAL_NUMBER)\n    end\n\n    it 'detects and reports issue INVALID_HEX_NUMBER' do\n      expect_issue('0x3g', Puppet::Pops::Issues::INVALID_HEX_NUMBER)\n    end\n\n    it 'detects and reports issue INVALID_OCTAL_NUMBER' do\n      expect_issue('038', Puppet::Pops::Issues::INVALID_OCTAL_NUMBER)\n    end\n\n    it 'detects and reports issue INVALID_DECIMAL_NUMBER' do\n      expect_issue('4.3g', Puppet::Pops::Issues::INVALID_DECIMAL_NUMBER)\n    end\n\n    it 'detects and reports issue NO_INPUT_TO_LEXER' do\n      expect { Puppet::Pops::Parser::Lexer2.new.fullscan }.to raise_error(Puppet::ParseErrorWithIssue) { |e|\n        expect(e.issue_code).to be(Puppet::Pops::Issues::NO_INPUT_TO_LEXER.issue_code)\n      }\n    end\n\n    it 'detects and reports issue UNCLOSED_QUOTE' do\n      expect_issue('\"asd', Puppet::Pops::Issues::UNCLOSED_QUOTE)\n    end\n  end\n\n  context 'when dealing with non UTF-8 and Byte Order Marks (BOMs)' do\n      {\n      'UTF_8'      => [0xEF, 0xBB, 0xBF],\n      'UTF_16_1'   => [0xFE, 0xFF],\n      'UTF_16_2'   => [0xFF, 0xFE],\n      'UTF_32_1'   => [0x00, 0x00, 0xFE, 0xFF],\n      'UTF_32_2'   => [0xFF, 0xFE, 0x00, 0x00],\n      'UTF_1'      => [0xF7, 0x64, 0x4C],\n      'UTF_EBCDIC' => [0xDD, 0x73, 0x66, 0x73],\n      'SCSU'       => [0x0E, 0xFE, 0xFF],\n      'BOCU'       => [0xFB, 0xEE, 0x28],\n      'GB_18030'   => [0x84, 0x31, 0x95, 0x33]\n      }.each do |key, bytes|\n        it \"errors on the byte order mark for #{key} '[#{bytes.map() {|b| '%X' % b}.join(' ')}]'\" do\n          format_name = key.split('_')[0,2].join('-')\n          bytes_str = \"\\\\[#{bytes.map {|b| '%X' % b}.join(' ')}\\\\]\"\n          fix =  \" - remove these from the puppet source\"\n          expect {\n            tokens_scanned_from(bytes.pack('C*'))\n          }.to raise_error(Puppet::ParseErrorWithIssue,\n            /Illegal #{format_name} .* at beginning of input: #{bytes_str}#{fix}/)\n        end\n\n       it \"can use a possibly 'broken' UTF-16 string without problems for #{key}\" do\n         format_name = key.split('_')[0,2].join('-')\n         string = bytes.pack('C*').force_encoding('UTF-16')\n         bytes_str = \"\\\\[#{string.bytes.map {|b| '%X' % b}.join(' ')}\\\\]\"\n         fix =  \" - remove these from the puppet source\"\n         expect {\n           tokens_scanned_from(string)\n         }.to raise_error(Puppet::ParseErrorWithIssue,\n           /Illegal #{format_name} .* at beginning of input: #{bytes_str}#{fix}/)\n       end\n    end\n  end\nend\n\ndescribe Puppet::Pops::Parser::Lexer2 do\n\n  include PuppetSpec::Files\n\n  # First line of Rune version of Rune poem at http://www.columbia.edu/~fdc/utf8/\n  # characters chosen since they will not parse on Windows with codepage 437 or 1252\n  # Section 3.2.1.3 of Ruby spec guarantees that \\u strings are encoded as UTF-8\n  # Runes (may show up as garbage if font is not available): ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n  let (:rune_utf8) {\n    \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\" +\n    \"\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\" +\n    \"\\u16B3\\u16A2\\u16D7\"\n  }\n\n  context 'when lexing files from disk' do\n    it 'should always read files as UTF-8' do\n      manifest_code = \"notify { '#{rune_utf8}': }\"\n      manifest = file_containing('manifest.pp', manifest_code)\n      lexed_file = described_class.new.lex_file(manifest)\n\n      expect(lexed_file.string.encoding).to eq(Encoding::UTF_8)\n      expect(lexed_file.string).to eq(manifest_code)\n    end\n\n    it 'currently errors when the UTF-8 BOM (Byte Order Mark) is present when lexing files' do\n      bom = \"\\uFEFF\"\n\n        manifest_code = \"#{bom}notify { '#{rune_utf8}': }\"\n        manifest = file_containing('manifest.pp', manifest_code)\n\n        expect {\n          described_class.new.lex_file(manifest)\n        }.to raise_error(Puppet::ParseErrorWithIssue,\n          'Illegal UTF-8 Byte Order mark at beginning of input: [EF BB BF] - remove these from the puppet source')\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/locator_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe Puppet::Pops::Parser::Locator do\n  it \"multi byte characters in a comment does not interfere with AST node text extraction\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"# \\u{0400}comment\\nabcdef#XXXXXXXXXX\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.offset).to eq(12)\n    expect(model.body.length).to eq(6)\n    expect(model.body.locator.extract_text(model.body.offset, model.body.length)).to eq('abcdef')\n  end\n\n  it \"multi byte characters in a comment does not interfere with AST node text extraction\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"# \\u{0400}comment\\n1 + 2#XXXXXXXXXX\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.offset).to eq(14) # The '+'\n    expect(model.body.length).to eq(1)\n    expect(model.body.locator.extract_tree_text(model.body)).to eq('1 + 2')\n  end\n\n  it 'Locator caches last offset / line' do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"$a\\n = 1\\n + 1\\n\").model\n    expect(model.body.locator).to receive(:ary_bsearch_i).with(anything, 2).once.and_return(:special_value)\n    expect(model.body.locator.line_for_offset(2)).to eq(:special_value)\n    expect(model.body.locator.line_for_offset(2)).to eq(:special_value)\n  end\n\n  it 'Locator invalidates last offset / line cache if asked for different offset' do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"$a\\n = 1\\n + 1\\n\").model\n    expect(model.body.locator).to receive(:ary_bsearch_i).with(anything, 2).twice.and_return(:first_value, :third_value)\n    expect(model.body.locator).to receive(:ary_bsearch_i).with(anything, 3).once.and_return(:second_value)\n    expect(model.body.locator.line_for_offset(2)).to eq(:first_value)\n    expect(model.body.locator.line_for_offset(3)).to eq(:second_value) # invalidates cache as side effect\n    expect(model.body.locator.line_for_offset(2)).to eq(:third_value)\n  end\n\n  it 'A heredoc without margin and interpolated expression location has offset and length relative the source' do\n    parser = Puppet::Pops::Parser::Parser.new()\n    src = <<-CODE\n    # line one\n    # line two\n    @(\"END\"/L)\n        Line four\\\\\n        Line five ${1 +\n        1}\n    END\n    CODE\n\n    model = parser.parse_string(src).model\n    interpolated_expr = model.body.text_expr.segments[1].expr\n    expect(interpolated_expr.left_expr.offset).to eq(84)\n    expect(interpolated_expr.left_expr.length).to eq(1)\n    expect(interpolated_expr.right_expr.offset).to eq(96)\n    expect(interpolated_expr.right_expr.length).to eq(1)\n    expect(interpolated_expr.offset).to eq(86) # the + sign\n    expect(interpolated_expr.length).to eq(1) # the + sign\n    expect(interpolated_expr.locator.extract_tree_text(interpolated_expr)).to eq(\"1 +\\n        1\")\n  end\n\n  it 'A heredoc with margin and interpolated expression location has offset and length relative the source' do\n    parser = Puppet::Pops::Parser::Parser.new()\n    src = <<-CODE\n    # line one\n    # line two\n    @(\"END\"/L)\n        Line four\\\\\n        Line five ${1 +\n        1}\n    |- END\n    CODE\n\n    model = parser.parse_string(src).model\n    interpolated_expr = model.body.text_expr.segments[1].expr\n    expect(interpolated_expr.left_expr.offset).to eq(84)\n    expect(interpolated_expr.left_expr.length).to eq(1)\n    expect(interpolated_expr.right_expr.offset).to eq(96)\n    expect(interpolated_expr.right_expr.length).to eq(1)\n    expect(interpolated_expr.offset).to eq(86) # the + sign\n    expect(interpolated_expr.length).to eq(1) # the + sign\n    expect(interpolated_expr.locator.extract_tree_text(interpolated_expr)).to eq(\"1 +\\n        1\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_basic_expressions_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/parser_rspec_helper')\n\ndescribe \"egrammar parsing basic expressions\" do\n  include ParserRspecHelper\n\n  context \"When the parser parses arithmetic\" do\n    context \"with Integers\" do\n      it \"$a = 2 + 2\"   do; expect(dump(parse(\"$a = 2 + 2\"))).to eq(\"(= $a (+ 2 2))\")      ; end\n      it \"$a = 7 - 3\"   do; expect(dump(parse(\"$a = 7 - 3\"))).to eq(\"(= $a (- 7 3))\")      ; end\n      it \"$a = 6 * 3\"   do; expect(dump(parse(\"$a = 6 * 3\"))).to eq(\"(= $a (* 6 3))\")      ; end\n      it \"$a = 6 / 3\"   do; expect(dump(parse(\"$a = 6 / 3\"))).to eq(\"(= $a (/ 6 3))\")      ; end\n      it \"$a = 6 % 3\"   do; expect(dump(parse(\"$a = 6 % 3\"))).to eq(\"(= $a (% 6 3))\")      ; end\n      it \"$a = -(6/3)\"  do; expect(dump(parse(\"$a = -(6/3)\"))).to eq(\"(= $a (- (/ 6 3)))\") ; end\n      it \"$a = -6/3\"    do; expect(dump(parse(\"$a = -6/3\"))).to eq(\"(= $a (/ (- 6) 3))\")   ; end\n      it \"$a = 8 >> 1 \" do; expect(dump(parse(\"$a = 8 >> 1\"))).to eq(\"(= $a (>> 8 1))\")    ; end\n      it \"$a = 8 << 1 \" do; expect(dump(parse(\"$a = 8 << 1\"))).to eq(\"(= $a (<< 8 1))\")    ; end\n    end\n\n    context \"with Floats\" do\n      it \"$a = 2.2 + 2.2\"  do; expect(dump(parse(\"$a = 2.2 + 2.2\"))).to eq(\"(= $a (+ 2.2 2.2))\")      ; end\n      it \"$a = 7.7 - 3.3\"  do; expect(dump(parse(\"$a = 7.7 - 3.3\"))).to eq(\"(= $a (- 7.7 3.3))\")      ; end\n      it \"$a = 6.1 * 3.1\"  do; expect(dump(parse(\"$a = 6.1 - 3.1\"))).to eq(\"(= $a (- 6.1 3.1))\")      ; end\n      it \"$a = 6.6 / 3.3\"  do; expect(dump(parse(\"$a = 6.6 / 3.3\"))).to eq(\"(= $a (/ 6.6 3.3))\")      ; end\n      it \"$a = -(6.0/3.0)\" do; expect(dump(parse(\"$a = -(6.0/3.0)\"))).to eq(\"(= $a (- (/ 6.0 3.0)))\") ; end\n      it \"$a = -6.0/3.0\"   do; expect(dump(parse(\"$a = -6.0/3.0\"))).to eq(\"(= $a (/ (- 6.0) 3.0))\")   ; end\n      it \"$a = 3.14 << 2\"  do; expect(dump(parse(\"$a = 3.14 << 2\"))).to eq(\"(= $a (<< 3.14 2))\")      ; end\n      it \"$a = 3.14 >> 2\"  do; expect(dump(parse(\"$a = 3.14 >> 2\"))).to eq(\"(= $a (>> 3.14 2))\")      ; end\n    end\n\n    context \"with hex and octal Integer values\" do\n      it \"$a = 0xAB + 0xCD\" do; expect(dump(parse(\"$a = 0xAB + 0xCD\"))).to eq(\"(= $a (+ 0xAB 0xCD))\")  ; end\n      it \"$a = 0777 - 0333\" do; expect(dump(parse(\"$a = 0777 - 0333\"))).to eq(\"(= $a (- 0777 0333))\")  ; end\n    end\n\n    context \"with strings requiring boxing to Numeric\" do\n      # Test that numbers in string form does not turn into numbers\n      it \"$a = '2' + '2'\"       do; expect(dump(parse(\"$a = '2' + '2'\"))).to eq(\"(= $a (+ '2' '2'))\")             ; end\n      it \"$a = '2.2' + '0.2'\"   do; expect(dump(parse(\"$a = '2.2' + '0.2'\"))).to eq(\"(= $a (+ '2.2' '0.2'))\")     ; end\n      it \"$a = '0xab' + '0xcd'\" do; expect(dump(parse(\"$a = '0xab' + '0xcd'\"))).to eq(\"(= $a (+ '0xab' '0xcd'))\") ; end\n      it \"$a = '0777' + '0333'\" do; expect(dump(parse(\"$a = '0777' + '0333'\"))).to eq(\"(= $a (+ '0777' '0333'))\") ; end\n    end\n\n    context \"precedence should be correct\" do\n      it \"$a = 1 + 2 * 3\" do; expect(dump(parse(\"$a = 1 + 2 * 3\"))).to eq(\"(= $a (+ 1 (* 2 3)))\"); end\n      it \"$a = 1 + 2 % 3\" do; expect(dump(parse(\"$a = 1 + 2 % 3\"))).to eq(\"(= $a (+ 1 (% 2 3)))\"); end\n      it \"$a = 1 + 2 / 3\" do; expect(dump(parse(\"$a = 1 + 2 / 3\"))).to eq(\"(= $a (+ 1 (/ 2 3)))\"); end\n      it \"$a = 1 + 2 << 3\" do; expect(dump(parse(\"$a = 1 + 2 << 3\"))).to eq(\"(= $a (<< (+ 1 2) 3))\"); end\n      it \"$a = 1 + 2 >> 3\" do; expect(dump(parse(\"$a = 1 + 2 >> 3\"))).to eq(\"(= $a (>> (+ 1 2) 3))\"); end\n    end\n\n    context \"parentheses alter precedence\" do\n      it \"$a = (1 + 2) * 3\" do; expect(dump(parse(\"$a = (1 + 2) * 3\"))).to eq(\"(= $a (* (+ 1 2) 3))\"); end\n      it \"$a = (1 + 2) / 3\" do; expect(dump(parse(\"$a = (1 + 2) / 3\"))).to eq(\"(= $a (/ (+ 1 2) 3))\"); end\n    end\n  end\n\n  context \"When the evaluator performs boolean operations\" do\n    context \"using operators AND OR NOT\" do\n      it \"$a = true  and true\" do; expect(dump(parse(\"$a = true and true\"))).to eq(\"(= $a (&& true true))\"); end\n      it \"$a = true  or true\"  do; expect(dump(parse(\"$a = true or true\"))).to eq(\"(= $a (|| true true))\") ; end\n      it \"$a = !true\"          do; expect(dump(parse(\"$a = !true\"))).to eq(\"(= $a (! true))\")              ; end\n    end\n\n    context \"precedence should be correct\" do\n      it \"$a = false or true and true\" do\n        expect(dump(parse(\"$a = false or true and true\"))).to eq(\"(= $a (|| false (&& true true)))\")\n      end\n\n      it \"$a = (false or true) and true\" do\n        expect(dump(parse(\"$a = (false or true) and true\"))).to eq(\"(= $a (&& (|| false true) true))\")\n      end\n\n      it \"$a = !true or true and true\" do\n        expect(dump(parse(\"$a = !false or true and true\"))).to eq(\"(= $a (|| (! false) (&& true true)))\")\n      end\n    end\n\n    # Possibly change to check of literal expressions\n    context \"on values requiring boxing to Boolean\" do\n      it \"'x'            == true\" do\n        expect(dump(parse(\"! 'x'\"))).to eq(\"(! 'x')\")\n      end\n\n      it \"''             == false\" do\n        expect(dump(parse(\"! ''\"))).to eq(\"(! '')\")\n      end\n\n      it \":undef         == false\" do\n        expect(dump(parse(\"! undef\"))).to eq(\"(! :undef)\")\n      end\n    end\n  end\n\n  context \"When parsing comparisons\" do\n    context \"of string values\" do\n      it \"$a = 'a' == 'a'\"  do; expect(dump(parse(\"$a = 'a' == 'a'\"))).to eq(\"(= $a (== 'a' 'a'))\")   ; end\n      it \"$a = 'a' != 'a'\"  do; expect(dump(parse(\"$a = 'a' != 'a'\"))).to eq(\"(= $a (!= 'a' 'a'))\")   ; end\n      it \"$a = 'a' < 'b'\"   do; expect(dump(parse(\"$a = 'a' < 'b'\"))).to eq(\"(= $a (< 'a' 'b'))\")     ; end\n      it \"$a = 'a' > 'b'\"   do; expect(dump(parse(\"$a = 'a' > 'b'\"))).to eq(\"(= $a (> 'a' 'b'))\")     ; end\n      it \"$a = 'a' <= 'b'\"  do; expect(dump(parse(\"$a = 'a' <= 'b'\"))).to eq(\"(= $a (<= 'a' 'b'))\")   ; end\n      it \"$a = 'a' >= 'b'\"  do; expect(dump(parse(\"$a = 'a' >= 'b'\"))).to eq(\"(= $a (>= 'a' 'b'))\")   ; end\n    end\n\n    context \"of integer values\" do\n      it \"$a = 1 == 1\"  do; expect(dump(parse(\"$a = 1 == 1\"))).to eq(\"(= $a (== 1 1))\")   ; end\n      it \"$a = 1 != 1\"  do; expect(dump(parse(\"$a = 1 != 1\"))).to eq(\"(= $a (!= 1 1))\")   ; end\n      it \"$a = 1 < 2\"   do; expect(dump(parse(\"$a = 1 < 2\"))).to eq(\"(= $a (< 1 2))\")     ; end\n      it \"$a = 1 > 2\"   do; expect(dump(parse(\"$a = 1 > 2\"))).to eq(\"(= $a (> 1 2))\")     ; end\n      it \"$a = 1 <= 2\"  do; expect(dump(parse(\"$a = 1 <= 2\"))).to eq(\"(= $a (<= 1 2))\")   ; end\n      it \"$a = 1 >= 2\"  do; expect(dump(parse(\"$a = 1 >= 2\"))).to eq(\"(= $a (>= 1 2))\")   ; end\n    end\n\n    context \"of regular expressions (parse errors)\" do\n      # Not supported in concrete syntax\n      it \"$a = /.*/ == /.*/\" do\n        expect(dump(parse(\"$a = /.*/ == /.*/\"))).to eq(\"(= $a (== /.*/ /.*/))\")\n      end\n\n      it \"$a = /.*/ != /a.*/\" do\n        expect(dump(parse(\"$a = /.*/ != /.*/\"))).to eq(\"(= $a (!= /.*/ /.*/))\")\n      end\n    end\n  end\n\n  context \"When parsing Regular Expression matching\" do\n    it \"$a = 'a' =~ /.*/\"    do; expect(dump(parse(\"$a = 'a' =~ /.*/\"))).to eq(\"(= $a (=~ 'a' /.*/))\")      ; end\n    it \"$a = 'a' =~ '.*'\"    do; expect(dump(parse(\"$a = 'a' =~ '.*'\"))).to eq(\"(= $a (=~ 'a' '.*'))\")      ; end\n    it \"$a = 'a' !~ /b.*/\"   do; expect(dump(parse(\"$a = 'a' !~ /b.*/\"))).to eq(\"(= $a (!~ 'a' /b.*/))\")    ; end\n    it \"$a = 'a' !~ 'b.*'\"   do; expect(dump(parse(\"$a = 'a' !~ 'b.*'\"))).to eq(\"(= $a (!~ 'a' 'b.*'))\")    ; end\n  end\n\n  context \"When parsing unfold\" do\n    it \"$a = *[1,2]\" do; expect(dump(parse(\"$a = *[1,2]\"))).to eq(\"(= $a (unfold ([] 1 2)))\") ; end\n    it \"$a = *1\"     do; expect(dump(parse(\"$a = *1\"))).to eq(\"(= $a (unfold 1))\") ; end\n    it \"$a = *[1,a => 2]\" do; expect(dump(parse(\"$a = *[1,a => 2]\"))).to eq(\"(= $a (unfold ([] 1 ({} (a 2)))))\") ; end\n  end\n\n  context \"When parsing Lists\" do\n    it \"$a = []\" do\n      expect(dump(parse(\"$a = []\"))).to eq(\"(= $a ([]))\")\n    end\n\n    it \"$a = [1]\" do\n      expect(dump(parse(\"$a = [1]\"))).to eq(\"(= $a ([] 1))\")\n    end\n\n    it \"$a = [1,2,3]\" do\n      expect(dump(parse(\"$a = [1,2,3]\"))).to eq(\"(= $a ([] 1 2 3))\")\n    end\n\n    it \"$a = [1,a => 2]\" do\n      expect(dump(parse(\"$a = [1,a => 2]\"))).to eq('(= $a ([] 1 ({} (a 2))))')\n    end\n\n    it \"$a = [1,a => 2, 3]\" do\n      expect(dump(parse(\"$a = [1,a => 2, 3]\"))).to eq('(= $a ([] 1 ({} (a 2)) 3))')\n    end\n\n    it \"$a = [1,a => 2, b => 3]\" do\n      expect(dump(parse(\"$a = [1,a => 2, b => 3]\"))).to eq('(= $a ([] 1 ({} (a 2) (b 3))))')\n    end\n\n    it \"$a = [1,a => 2, b => 3, 4]\" do\n      expect(dump(parse(\"$a = [1,a => 2, b => 3, 4]\"))).to eq('(= $a ([] 1 ({} (a 2) (b 3)) 4))')\n    end\n\n    it \"$a = [{ x => y }, a => 2, b => 3, { z => p }]\" do\n      expect(dump(parse(\"$a = [{ x => y }, a => 2, b => 3, { z => p }]\"))).to eq('(= $a ([] ({} (x y)) ({} (a 2) (b 3)) ({} (z p))))')\n    end\n\n    it \"[...[...[]]] should create nested arrays without trouble\" do\n      expect(dump(parse(\"$a = [1,[2.0, 2.1, [2.2]],[3.0, 3.1]]\"))).to eq(\"(= $a ([] 1 ([] 2.0 2.1 ([] 2.2)) ([] 3.0 3.1)))\")\n    end\n\n    it \"$a = [2 + 2]\" do\n      expect(dump(parse(\"$a = [2+2]\"))).to eq(\"(= $a ([] (+ 2 2)))\")\n    end\n\n    it \"$a [1,2,3] == [1,2,3]\" do\n      expect(dump(parse(\"$a = [1,2,3] == [1,2,3]\"))).to eq(\"(= $a (== ([] 1 2 3) ([] 1 2 3)))\")\n    end\n\n    it \"calculates the text length of an empty array\" do\n      expect(parse(\"[]\").model.body.length).to eq(2)\n      expect(parse(\"[ ]\").model.body.length).to eq(3)\n    end\n\n    {\n      'keyword' => %w(type function),\n    }.each_pair do |word_type, words|\n      words.each do |word|\n        it \"allows the #{word_type} '#{word}' in a list\" do\n          expect(dump(parse(\"$a = [#{word}]\"))).to(eq(\"(= $a ([] '#{word}'))\"))\n        end\n\n        it \"allows the #{word_type} '#{word}' as a key in a hash\" do\n          expect(dump(parse(\"$a = {#{word}=>'x'}\"))).to(eq(\"(= $a ({} ('#{word}' 'x')))\"))\n        end\n\n        it \"allows the #{word_type} '#{word}' as a value in a hash\" do\n          expect(dump(parse(\"$a = {'x'=>#{word}}\"))).to(eq(\"(= $a ({} ('x' '#{word}')))\"))\n        end\n      end\n    end\n  end\n\n  context \"When parsing indexed access\" do\n    it \"$a = $b[2]\" do\n      expect(dump(parse(\"$a = $b[2]\"))).to eq(\"(= $a (slice $b 2))\")\n    end\n\n    it \"$a = $b[2,]\" do\n      expect(dump(parse(\"$a = $b[2,]\"))).to eq(\"(= $a (slice $b 2))\")\n    end\n\n    it \"$a = [1, 2, 3][2]\" do\n      expect(dump(parse(\"$a = [1,2,3][2]\"))).to eq(\"(= $a (slice ([] 1 2 3) 2))\")\n    end\n\n    it '$a = [1, 2, 3][a => 2]' do\n      expect(dump(parse('$a = [1,2,3][a => 2]'))).to eq('(= $a (slice ([] 1 2 3) ({} (a 2))))')\n    end\n\n    it \"$a = {'a' => 1, 'b' => 2}['b']\" do\n      expect(dump(parse(\"$a = {'a'=>1,'b' =>2}[b]\"))).to eq(\"(= $a (slice ({} ('a' 1) ('b' 2)) b))\")\n    end\n  end\n\n  context 'When parsing type aliases' do\n    it 'type A = B' do\n      expect(dump(parse('type A = B'))).to eq('(type-alias A B)')\n    end\n\n    it 'type A = B[]' do\n      expect{parse('type A = B[]')}.to raise_error(/Syntax error at '\\]'/)\n    end\n\n    it 'type A = B[,]' do\n      expect{parse('type A = B[,]')}.to raise_error(/Syntax error at ','/)\n    end\n\n    it 'type A = B[C]' do\n      expect(dump(parse('type A = B[C]'))).to eq('(type-alias A (slice B C))')\n    end\n\n    it 'type A = B[C,]' do\n      expect(dump(parse('type A = B[C,]'))).to eq('(type-alias A (slice B C))')\n    end\n\n    it 'type A = B[C,D]' do\n      expect(dump(parse('type A = B[C,D]'))).to eq('(type-alias A (slice B (C D)))')\n    end\n\n    it 'type A = B[C,D,]' do\n      expect(dump(parse('type A = B[C,D,]'))).to eq('(type-alias A (slice B (C D)))')\n    end\n  end\n\n  context \"When parsing assignments\" do\n    it \"Should allow simple assignment\" do\n      expect(dump(parse(\"$a = 10\"))).to eq(\"(= $a 10)\")\n    end\n\n    it \"Should allow append assignment\" do\n      expect(dump(parse(\"$a += 10\"))).to eq(\"(+= $a 10)\")\n    end\n\n    it \"Should allow without assignment\" do\n      expect(dump(parse(\"$a -= 10\"))).to eq(\"(-= $a 10)\")\n    end\n\n    it \"Should allow chained assignment\" do\n      expect(dump(parse(\"$a = $b = 10\"))).to eq(\"(= $a (= $b 10))\")\n    end\n\n    it \"Should allow chained assignment with expressions\" do\n      expect(dump(parse(\"$a = 1 + ($b = 10)\"))).to eq(\"(= $a (+ 1 (= $b 10)))\")\n    end\n  end\n\n  context \"When parsing Hashes\" do\n    it \"should create a  Hash when evaluating a LiteralHash\" do\n      expect(dump(parse(\"$a = {'a'=>1,'b'=>2}\"))).to eq(\"(= $a ({} ('a' 1) ('b' 2)))\")\n    end\n\n    it \"$a = {...{...{}}} should create nested hashes without trouble\" do\n      expect(dump(parse(\"$a = {'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}\"))).to eq(\"(= $a ({} ('a' 1) ('b' ({} ('x' 2.1) ('y' 2.2)))))\")\n    end\n\n    it \"$a = {'a'=> 2 + 2} should evaluate values in entries\" do\n      expect(dump(parse(\"$a = {'a'=>2+2}\"))).to eq(\"(= $a ({} ('a' (+ 2 2))))\")\n    end\n\n    it \"$a = {'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2}\" do\n      expect(dump(parse(\"$a = {'a'=>1,'b'=>2} == {'a'=>1,'b'=>2}\"))).to eq(\"(= $a (== ({} ('a' 1) ('b' 2)) ({} ('a' 1) ('b' 2))))\")\n    end\n\n    it \"$a = {'a'=> 1, 'b'=>2} != {'x'=> 1, 'y'=>3}\" do\n      expect(dump(parse(\"$a = {'a'=>1,'b'=>2} != {'a'=>1,'b'=>2}\"))).to eq(\"(= $a (!= ({} ('a' 1) ('b' 2)) ({} ('a' 1) ('b' 2))))\")\n    end\n\n    it \"$a = 'a' => 1\" do\n      expect{parse(\"$a = 'a' => 1\")}.to raise_error(/Syntax error at '=>'/)\n    end\n\n    it \"$a = { 'a' => 'b' => 1 }\" do\n      expect{parse(\"$a = { 'a' => 'b' => 1 }\")}.to raise_error(/Syntax error at '=>'/)\n    end\n\n    it \"calculates the text length of an empty hash\" do\n      expect(parse(\"{}\").model.body.length).to eq(2)\n      expect(parse(\"{ }\").model.body.length).to eq(3)\n    end\n  end\n\n  context \"When parsing the 'in' operator\" do\n    it \"with integer in a list\" do\n      expect(dump(parse(\"$a = 1 in [1,2,3]\"))).to eq(\"(= $a (in 1 ([] 1 2 3)))\")\n    end\n\n    it \"with string key in a hash\" do\n      expect(dump(parse(\"$a = 'a' in {'x'=>1, 'a'=>2, 'y'=> 3}\"))).to eq(\"(= $a (in 'a' ({} ('x' 1) ('a' 2) ('y' 3))))\")\n    end\n\n    it \"with substrings of a string\" do\n      expect(dump(parse(\"$a = 'ana' in 'bananas'\"))).to eq(\"(= $a (in 'ana' 'bananas'))\")\n    end\n\n    it \"with sublist in a list\" do\n      expect(dump(parse(\"$a = [2,3] in [1,2,3]\"))).to eq(\"(= $a (in ([] 2 3) ([] 1 2 3)))\")\n    end\n  end\n\n  context \"When parsing string interpolation\" do\n    it \"should interpolate a bare word as a variable name, \\\"${var}\\\"\" do\n      expect(dump(parse(\"$a = \\\"$var\\\"\"))).to eq(\"(= $a (cat (str $var)))\")\n    end\n\n    it \"should interpolate a variable in a text expression, \\\"${$var}\\\"\" do\n      expect(dump(parse(\"$a = \\\"${$var}\\\"\"))).to eq(\"(= $a (cat (str $var)))\")\n    end\n\n    it \"should interpolate a variable, \\\"yo${var}yo\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${var}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str $var) 'yo'))\")\n    end\n\n    it \"should interpolate any expression in a text expression, \\\"${$var*2}\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${$var+2}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (+ $var 2)) 'yo'))\")\n    end\n\n    it \"should not interpolate names as variable in expression, \\\"${notvar*2}\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${notvar+2}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (+ notvar 2)) 'yo'))\")\n    end\n\n    it \"should interpolate name as variable in access expression, \\\"${var[0]}\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${var[0]}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (slice $var 0)) 'yo'))\")\n    end\n\n    it \"should interpolate name as variable in method call, \\\"${var.foo}\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${$var.foo}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (call-method (. $var foo))) 'yo'))\")\n    end\n\n    it \"should interpolate name as variable in method call, \\\"${var.foo}\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${var.foo}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (call-method (. $var foo))) 'yo'))\")\n      expect(dump(parse(\"$a = \\\"yo${var.foo.bar}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (call-method (. (call-method (. $var foo)) bar))) 'yo'))\")\n    end\n\n    it \"should interpolate interpolated expressions with a variable, \\\"yo${\\\"$var\\\"}yo\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${\\\"$var\\\"}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (cat (str $var))) 'yo'))\")\n    end\n\n    it \"should interpolate interpolated expressions with an expression, \\\"yo${\\\"${$var+2}\\\"}yo\\\"\" do\n      expect(dump(parse(\"$a = \\\"yo${\\\"${$var+2}\\\"}yo\\\"\"))).to eq(\"(= $a (cat 'yo' (str (cat (str (+ $var 2)))) 'yo'))\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_calls_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/parser_rspec_helper')\n\ndescribe \"egrammar parsing function calls\" do\n  include ParserRspecHelper\n\n  context \"When parsing calls as statements\" do\n    context \"in top level scope\" do\n      it \"foo()\" do\n        expect(dump(parse(\"foo()\"))).to eq(\"(invoke foo)\")\n      end\n\n      it \"notice bar\" do\n        expect(dump(parse(\"notice bar\"))).to eq(\"(invoke notice bar)\")\n      end\n\n      it \"notice(bar)\" do\n        expect(dump(parse(\"notice bar\"))).to eq(\"(invoke notice bar)\")\n      end\n\n      it \"foo(bar)\" do\n        expect(dump(parse(\"foo(bar)\"))).to eq(\"(invoke foo bar)\")\n      end\n\n      it \"notice {a=>2}\" do\n        expect(dump(parse(\"notice {a => 2}\"))).to eq(\"(invoke notice ({} ('a' 2)))\")\n      end\n\n      it 'notice a=>2' do\n        expect{parse('notice a => 2')}.to raise_error(/Syntax error at '=>'/)\n      end\n\n      it \"foo(bar,)\" do\n        expect(dump(parse(\"foo(bar,)\"))).to eq(\"(invoke foo bar)\")\n      end\n\n      it \"foo(bar, fum,)\" do\n        expect(dump(parse(\"foo(bar,fum,)\"))).to eq(\"(invoke foo bar fum)\")\n      end\n\n      it 'foo(a=>1)' do\n        expect(dump(parse('foo(a=>1)'))).to eq('(invoke foo ({} (a 1)))')\n      end\n\n      it 'foo(a=>1, b=>2)' do\n        expect(dump(parse('foo(a=>1, b=>2)'))).to eq('(invoke foo ({} (a 1) (b 2)))')\n      end\n\n      it 'foo({a=>1, b=>2})' do\n        expect(dump(parse('foo({a=>1, b=>2})'))).to eq('(invoke foo ({} (a 1) (b 2)))')\n      end\n\n      it 'foo({a=>1}, b=>2)' do\n        expect(dump(parse('foo({a=>1}, b=>2)'))).to eq('(invoke foo ({} (a 1)) ({} (b 2)))')\n      end\n\n      it 'foo(a=>1, {b=>2})' do\n        expect(dump(parse('foo(a=>1, {b=>2})'))).to eq('(invoke foo ({} (a 1)) ({} (b 2)))')\n      end\n\n      it \"notice fqdn_rand(30)\" do\n        expect(dump(parse(\"notice fqdn_rand(30)\"))).to eq('(invoke notice (call fqdn_rand 30))')\n      end\n\n      it \"notice type(42)\" do\n        expect(dump(parse(\"notice type(42)\"))).to eq('(invoke notice (call type 42))')\n      end\n\n      it \"is required to place the '(' on the same line as the name of the function to recognize it as arguments in call\" do\n        expect(dump(parse(\"notice foo\\n(42)\"))).to eq(\"(block\\n  (invoke notice foo)\\n  42\\n)\")\n      end\n\n    end\n\n    context \"in nested scopes\" do\n      it \"if true { foo() }\" do\n        expect(dump(parse(\"if true {foo()}\"))).to eq(\"(if true\\n  (then (invoke foo)))\")\n      end\n\n      it \"if true { notice bar}\" do\n        expect(dump(parse(\"if true {notice bar}\"))).to eq(\"(if true\\n  (then (invoke notice bar)))\")\n      end\n    end\n  end\n\n  context \"When parsing calls as expressions\" do\n    it \"$a = foo()\" do\n      expect(dump(parse(\"$a = foo()\"))).to eq(\"(= $a (call foo))\")\n    end\n\n    it \"$a = foo(bar)\" do\n      expect(dump(parse(\"$a = foo()\"))).to eq(\"(= $a (call foo))\")\n    end\n\n    # For egrammar where a bare word can be a \"statement\"\n    it \"$a = foo bar # illegal, must have parentheses\" do\n      expect(dump(parse(\"$a = foo bar\"))).to eq(\"(block\\n  (= $a foo)\\n  bar\\n)\")\n    end\n\n    context \"in nested scopes\" do\n      it \"if true { $a = foo() }\" do\n        expect(dump(parse(\"if true { $a = foo()}\"))).to eq(\"(if true\\n  (then (= $a (call foo))))\")\n      end\n\n      it \"if true { $a= foo(bar)}\" do\n        expect(dump(parse(\"if true {$a = foo(bar)}\"))).to eq(\"(if true\\n  (then (= $a (call foo bar))))\")\n      end\n    end\n  end\n\n  context \"When parsing method calls\" do\n    it \"$a.foo\" do\n      expect(dump(parse(\"$a.foo\"))).to eq(\"(call-method (. $a foo))\")\n    end\n\n    it \"$a.foo || { }\" do\n      expect(dump(parse(\"$a.foo || { }\"))).to eq(\"(call-method (. $a foo) (lambda ()))\")\n    end\n\n    it \"$a.foo |$x| { }\" do\n      expect(dump(parse(\"$a.foo |$x|{ }\"))).to eq(\"(call-method (. $a foo) (lambda (parameters x) ()))\")\n    end\n\n    it \"$a.foo |$x|{ }\" do\n      expect(dump(parse(\"$a.foo |$x|{ $b = $x}\"))).to eq([\n        \"(call-method (. $a foo) (lambda (parameters x) (block\",\n        \"  (= $b $x)\",\n        \")))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"notice 42.type()\" do\n      expect(dump(parse(\"notice 42.type()\"))).to eq('(invoke notice (call-method (. 42 type)))')\n    end\n\n    it 'notice Hash.assert_type(a => 42)' do\n      expect(dump(parse('notice Hash.assert_type(a => 42)'))).to eq('(invoke notice (call-method (. Hash assert_type) ({} (a 42))))')\n    end\n\n    it \"notice 42.type(detailed)\" do\n      expect(dump(parse(\"notice 42.type(detailed)\"))).to eq('(invoke notice (call-method (. 42 type) detailed))')\n    end\n\n    it \"is required to place the '(' on the same line as the name of the method to recognize it as arguments in call\" do\n      expect(dump(parse(\"notice 42.type\\n(detailed)\"))).to eq(\"(block\\n  (invoke notice (call-method (. 42 type)))\\n  detailed\\n)\")\n    end\n  end\n\n  context \"When parsing an illegal argument list\" do\n    it \"raises an error if argument list is not for a call\" do\n      expect do\n        parse(\"$a  = 10, 3\")\n      end.to raise_error(/illegal comma/)\n    end\n\n    it \"raises an error if argument list is for a potential call not allowed without parentheses\" do\n      expect do\n        parse(\"foo 10, 3\")\n      end.to raise_error(/attempt to pass argument list to the function 'foo' which cannot be called without parentheses/)\n    end\n\n    it \"does no raise an error for an argument list to an allowed call\" do\n      expect do\n        parse(\"notice 10, 3\")\n      end.to_not raise_error()\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_conditionals_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/parser_rspec_helper')\n\ndescribe \"egrammar parsing conditionals\" do\n  include ParserRspecHelper\n\n  context \"When parsing if statements\" do\n    it \"if true { $a = 10 }\" do\n      expect(dump(parse(\"if true { $a = 10 }\"))).to eq(\"(if true\\n  (then (= $a 10)))\")\n    end\n\n    it \"if true { $a = 10 } else {$a = 20}\" do\n      expect(dump(parse(\"if true { $a = 10 } else {$a = 20}\"))).to eq(\n      [\"(if true\",\n        \"  (then (= $a 10))\",\n        \"  (else (= $a 20)))\"].join(\"\\n\")\n      )\n    end\n\n    it \"if true { $a = 10 } elsif false { $a = 15} else {$a = 20}\" do\n      expect(dump(parse(\"if true { $a = 10 } elsif false { $a = 15} else {$a = 20}\"))).to eq(\n      [\"(if true\",\n        \"  (then (= $a 10))\",\n        \"  (else (if false\",\n        \"      (then (= $a 15))\",\n        \"      (else (= $a 20)))))\"].join(\"\\n\")\n      )\n    end\n\n    it \"if true { $a = 10 $b = 10 } else {$a = 20}\" do\n      expect(dump(parse(\"if true { $a = 10 $b = 20} else {$a = 20}\"))).to eq([\n       \"(if true\",\n       \"  (then (block\",\n       \"      (= $a 10)\",\n       \"      (= $b 20)\",\n       \"    ))\",\n       \"  (else (= $a 20)))\"\n       ].join(\"\\n\"))\n    end\n\n    it \"allows a parenthesized conditional expression\" do\n      expect(dump(parse(\"if (true) { 10 }\"))).to eq(\"(if true\\n  (then 10))\")\n    end\n\n    it \"allows a parenthesized elsif conditional expression\" do\n      expect(dump(parse(\"if true { 10 } elsif (false) { 20 }\"))).to eq(\n        [\"(if true\",\n         \"  (then 10)\",\n         \"  (else (if false\",\n         \"      (then 20))))\"].join(\"\\n\")\n      )\n    end\n  end\n\n  context \"When parsing unless statements\" do\n    it \"unless true { $a = 10 }\" do\n      expect(dump(parse(\"unless true { $a = 10 }\"))).to eq(\"(unless true\\n  (then (= $a 10)))\")\n    end\n\n    it \"unless true { $a = 10 } else {$a = 20}\" do\n      expect(dump(parse(\"unless true { $a = 10 } else {$a = 20}\"))).to eq(\n      [\"(unless true\",\n        \"  (then (= $a 10))\",\n        \"  (else (= $a 20)))\"].join(\"\\n\")\n      )\n    end\n\n    it \"allows a parenthesized conditional expression\" do\n      expect(dump(parse(\"unless (true) { 10 }\"))).to eq(\"(unless true\\n  (then 10))\")\n    end\n\n    it \"unless true { $a = 10 } elsif false { $a = 15} else {$a = 20} # is illegal\" do\n      expect { parse(\"unless true { $a = 10 } elsif false { $a = 15} else {$a = 20}\")}.to raise_error(Puppet::ParseError)\n    end\n  end\n\n  context \"When parsing selector expressions\" do\n    it \"$a = $b ? banana => fruit \" do\n      expect(dump(parse(\"$a = $b ? banana => fruit\"))).to eq(\n      \"(= $a (? $b (banana => fruit)))\"\n      )\n    end\n\n    it \"$a = $b ? { banana => fruit}\" do\n      expect(dump(parse(\"$a = $b ? { banana => fruit }\"))).to eq(\n      \"(= $a (? $b (banana => fruit)))\"\n      )\n    end\n\n    it \"does not fail on a trailing blank line\" do\n      expect(dump(parse(\"$a = $b ? { banana => fruit }\\n\\n\"))).to eq(\n      \"(= $a (? $b (banana => fruit)))\"\n      )\n    end\n\n    it \"$a = $b ? { banana => fruit, grape => berry }\" do\n      expect(dump(parse(\"$a = $b ? {banana => fruit, grape => berry}\"))).to eq(\n      \"(= $a (? $b (banana => fruit) (grape => berry)))\"\n      )\n    end\n\n    it \"$a = $b ? { banana => fruit, grape => berry, default => wat }\" do\n      expect(dump(parse(\"$a = $b ? {banana => fruit, grape => berry, default => wat}\"))).to eq(\n      \"(= $a (? $b (banana => fruit) (grape => berry) (:default => wat)))\"\n      )\n    end\n\n    it \"$a = $b ? { default => wat, banana => fruit, grape => berry,  }\" do\n      expect(dump(parse(\"$a = $b ? {default => wat, banana => fruit, grape => berry}\"))).to eq(\n      \"(= $a (? $b (:default => wat) (banana => fruit) (grape => berry)))\"\n      )\n    end\n\n    it '1+2 ? 3 => yes' do\n      expect(dump(parse(\"1+2 ? 3 => yes\"))).to eq(\n      \"(? (+ 1 2) (3 => yes))\"\n      )\n    end\n\n    it 'true and 1+2 ? 3 => yes' do\n      expect(dump(parse(\"true and 1+2 ? 3 => yes\"))).to eq(\n      \"(&& true (? (+ 1 2) (3 => yes)))\"\n      )\n    end\n  end\n\n  context \"When parsing case statements\" do\n    it \"case $a { a : {}}\" do\n      expect(dump(parse(\"case $a { a : {}}\"))).to eq(\n      [\"(case $a\",\n        \"  (when (a) (then ())))\"\n      ].join(\"\\n\")\n      )\n    end\n\n    it \"allows a parenthesized value expression\" do\n      expect(dump(parse(\"case ($a) { a : {}}\"))).to eq(\n      [\"(case $a\",\n        \"  (when (a) (then ())))\"\n      ].join(\"\\n\")\n      )\n    end\n\n    it \"case $a { /.*/ : {}}\" do\n      expect(dump(parse(\"case $a { /.*/ : {}}\"))).to eq(\n      [\"(case $a\",\n        \"  (when (/.*/) (then ())))\"\n      ].join(\"\\n\")\n      )\n    end\n\n    it \"case $a { a, b : {}}\" do\n      expect(dump(parse(\"case $a { a, b : {}}\"))).to eq(\n      [\"(case $a\",\n        \"  (when (a b) (then ())))\"\n      ].join(\"\\n\")\n      )\n    end\n\n    it \"case $a { a, b : {} default : {}}\" do\n      expect(dump(parse(\"case $a { a, b : {} default : {}}\"))).to eq(\n      [\"(case $a\",\n        \"  (when (a b) (then ()))\",\n        \"  (when (:default) (then ())))\"\n      ].join(\"\\n\")\n      )\n    end\n\n    it \"case $a { a : {$b = 10 $c = 20}}\" do\n      expect(dump(parse(\"case $a { a : {$b = 10 $c = 20}}\"))).to eq(\n      [\"(case $a\",\n       \"  (when (a) (then (block\",\n       \"    (= $b 10)\",\n       \"    (= $c 20)\",\n       \"  ))))\"\n      ].join(\"\\n\")\n      )\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_containers_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/parser_rspec_helper')\n\ndescribe \"egrammar parsing containers\" do\n  include ParserRspecHelper\n\n  context \"When parsing file scope\" do\n    it \"$a = 10 $b = 20\" do\n      expect(dump(parse(\"$a = 10 $b = 20\"))).to eq([\n        \"(block\",\n        \"  (= $a 10)\",\n        \"  (= $b 20)\",\n        \")\"\n        ].join(\"\\n\"))\n    end\n\n    it \"$a = 10\" do\n      expect(dump(parse(\"$a = 10\"))).to eq(\"(= $a 10)\")\n    end\n  end\n\n  context \"When parsing class\" do\n    it \"class foo {}\" do\n      expect(dump(parse(\"class foo {}\"))).to eq(\"(class foo ())\")\n    end\n\n    it \"class foo { class bar {} }\" do\n      expect(dump(parse(\"class foo { class bar {}}\"))).to eq([\n        \"(class foo (block\",\n        \"  (class foo::bar ())\",\n        \"))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"class foo::bar {}\" do\n      expect(dump(parse(\"class foo::bar {}\"))).to eq(\"(class foo::bar ())\")\n    end\n\n    it \"class foo inherits bar {}\" do\n      expect(dump(parse(\"class foo inherits bar {}\"))).to eq(\"(class foo (inherits bar) ())\")\n    end\n\n    it \"class foo($a) {}\" do\n      expect(dump(parse(\"class foo($a) {}\"))).to eq(\"(class foo (parameters a) ())\")\n    end\n\n    it \"class foo($a, $b) {}\" do\n      expect(dump(parse(\"class foo($a, $b) {}\"))).to eq(\"(class foo (parameters a b) ())\")\n    end\n\n    it \"class foo($a, $b=10) {}\" do\n      expect(dump(parse(\"class foo($a, $b=10) {}\"))).to eq(\"(class foo (parameters a (= b 10)) ())\")\n    end\n\n    it \"class foo($a, $b) inherits belgo::bar {}\" do\n      expect(dump(parse(\"class foo($a, $b) inherits belgo::bar{}\"))).to eq(\"(class foo (inherits belgo::bar) (parameters a b) ())\")\n    end\n\n    it \"class foo {$a = 10 $b = 20}\" do\n      expect(dump(parse(\"class foo {$a = 10 $b = 20}\"))).to eq([\n        \"(class foo (block\",\n        \"  (= $a 10)\",\n        \"  (= $b 20)\",\n        \"))\"\n        ].join(\"\\n\"))\n    end\n\n    context \"it should handle '3x weirdness'\" do\n      it \"class class {} # a class named 'class'\" do\n        # Not as much weird as confusing that it is possible to name a class 'class'. Can have\n        # a very confusing effect when resolving relative names, getting the global hardwired \"Class\"\n        # instead of some foo::class etc.\n        # This was allowed in 3.x.\n        expect { parse(\"class class {}\") }.to raise_error(/'class' keyword not allowed at this location/)\n      end\n\n      it \"class default {} # a class named 'default'\" do\n        # The weirdness here is that a class can inherit 'default' but not declare a class called default.\n        # (It will work with relative names i.e. foo::default though). The whole idea with keywords as\n        # names is flawed to begin with - it generally just a very bad idea.\n        expect { parse(\"class default {}\") }.to raise_error(Puppet::ParseError)\n      end\n\n      it \"class 'foo' {} # a string as class name\" do\n        # A common error is to put quotes around the class name, the parser should provide the error message at the right location\n        # See PUP-7471\n        expect { parse(\"class 'foo' {}\") }.to raise_error(/A quoted string is not valid as a name here \\(line: 1, column: 7\\)/)\n      end\n\n\n      it \"class foo::default {} # a nested name 'default'\" do\n        expect(dump(parse(\"class foo::default {}\"))).to eq(\"(class foo::default ())\")\n      end\n\n      it \"class class inherits default {} # inherits default\" do\n        expect { parse(\"class class inherits default {}\") }.to raise_error(/'class' keyword not allowed at this location/)\n      end\n\n      it \"class foo inherits class\" do\n        expect { parse(\"class foo inherits class {}\") }.to raise_error(/'class' keyword not allowed at this location/)\n      end\n    end\n\n    context 'it should allow keywords as attribute names' do\n      ['and', 'case', 'class', 'default', 'define', 'else', 'elsif', 'if', 'in', 'inherits', 'node', 'or',\n        'undef', 'unless', 'type', 'attr', 'function', 'private', 'plan', 'apply'].each do |keyword|\n        it \"such as #{keyword}\" do\n          expect { parse(\"class x ($#{keyword}){} class { x: #{keyword} => 1 }\") }.to_not raise_error\n        end\n      end\n    end\n\n  end\n\n  context \"When the parser parses define\" do\n    it \"define foo {}\" do\n      expect(dump(parse(\"define foo {}\"))).to eq(\"(define foo ())\")\n    end\n\n    it \"class foo { define bar {}}\" do\n      expect(dump(parse(\"class foo {define bar {}}\"))).to eq([\n        \"(class foo (block\",\n        \"  (define foo::bar ())\",\n        \"))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"define foo { define bar {}}\" do\n      # This is illegal, but handled as part of validation\n      expect(dump(parse(\"define foo { define bar {}}\"))).to eq([\n        \"(define foo (block\",\n        \"  (define bar ())\",\n        \"))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"define foo::bar {}\" do\n      expect(dump(parse(\"define foo::bar {}\"))).to eq(\"(define foo::bar ())\")\n    end\n\n    it \"define foo($a) {}\" do\n      expect(dump(parse(\"define foo($a) {}\"))).to eq(\"(define foo (parameters a) ())\")\n    end\n\n    it \"define foo($a, $b) {}\" do\n      expect(dump(parse(\"define foo($a, $b) {}\"))).to eq(\"(define foo (parameters a b) ())\")\n    end\n\n    it \"define foo($a, $b=10) {}\" do\n      expect(dump(parse(\"define foo($a, $b=10) {}\"))).to eq(\"(define foo (parameters a (= b 10)) ())\")\n    end\n\n    it \"define foo {$a = 10 $b = 20}\" do\n      expect(dump(parse(\"define foo {$a = 10 $b = 20}\"))).to eq([\n        \"(define foo (block\",\n        \"  (= $a 10)\",\n        \"  (= $b 20)\",\n        \"))\"\n        ].join(\"\\n\"))\n    end\n\n    context \"it should handle '3x weirdness'\" do\n      it \"define class {} # a define named 'class'\" do\n        # This is weird because Class already exists, and instantiating this define will probably not\n        # work\n        expect { parse(\"define class {}\") }.to raise_error(/'class' keyword not allowed at this location/)\n      end\n\n      it \"define default {} # a define named 'default'\" do\n        # Check unwanted ability to define 'default'.\n        # The expression below is not allowed (which is good).\n        expect { parse(\"define default {}\") }.to raise_error(Puppet::ParseError)\n      end\n    end\n\n    context 'it should allow keywords as attribute names' do\n      ['and', 'case', 'class', 'default', 'define', 'else', 'elsif', 'if', 'in', 'inherits', 'node', 'or',\n        'undef', 'unless', 'type', 'attr', 'function', 'private', 'plan', 'apply'].each do |keyword|\n        it \"such as #{keyword}\" do\n          expect {parse(\"define x ($#{keyword}){} x { y: #{keyword} => 1 }\")}.to_not raise_error\n        end\n      end\n    end\n  end\n\n  context \"When parsing node\" do\n    it \"node foo {}\" do\n      expect(dump(parse(\"node foo {}\"))).to eq(\"(node (matches 'foo') ())\")\n    end\n\n    it \"node foo, {} # trailing comma\" do\n      expect(dump(parse(\"node foo, {}\"))).to eq(\"(node (matches 'foo') ())\")\n    end\n\n    it \"node kermit.example.com {}\" do\n      expect(dump(parse(\"node kermit.example.com {}\"))).to eq(\"(node (matches 'kermit.example.com') ())\")\n    end\n\n    it \"node kermit . example . com {}\" do\n      expect(dump(parse(\"node kermit . example . com {}\"))).to eq(\"(node (matches 'kermit.example.com') ())\")\n    end\n\n    it \"node foo, x::bar, default {}\" do\n      expect(dump(parse(\"node foo, x::bar, default {}\"))).to eq(\"(node (matches 'foo' 'x::bar' :default) ())\")\n    end\n\n    it \"node 'foo' {}\" do\n      expect(dump(parse(\"node 'foo' {}\"))).to eq(\"(node (matches 'foo') ())\")\n    end\n\n    it \"node foo inherits x::bar {}\" do\n      expect(dump(parse(\"node foo inherits x::bar {}\"))).to eq(\"(node (matches 'foo') (parent 'x::bar') ())\")\n    end\n\n    it \"node foo inherits 'bar' {}\" do\n      expect(dump(parse(\"node foo inherits 'bar' {}\"))).to eq(\"(node (matches 'foo') (parent 'bar') ())\")\n    end\n\n    it \"node foo inherits default {}\" do\n      expect(dump(parse(\"node foo inherits default {}\"))).to eq(\"(node (matches 'foo') (parent :default) ())\")\n    end\n\n    it \"node /web.*/ {}\" do\n      expect(dump(parse(\"node /web.*/ {}\"))).to eq(\"(node (matches /web.*/) ())\")\n    end\n\n    it \"node /web.*/, /do\\.wop.*/, and.so.on {}\" do\n      expect(dump(parse(\"node /web.*/, /do\\.wop.*/, 'and.so.on' {}\"))).to eq(\"(node (matches /web.*/ /do\\.wop.*/ 'and.so.on') ())\")\n    end\n\n    it \"node wat inherits /apache.*/ {}\" do\n      expect(dump(parse(\"node wat inherits /apache.*/ {}\"))).to eq(\"(node (matches 'wat') (parent /apache.*/) ())\")\n    end\n\n    it \"node foo inherits bar {$a = 10 $b = 20}\" do\n      expect(dump(parse(\"node foo inherits bar {$a = 10 $b = 20}\"))).to eq([\n        \"(node (matches 'foo') (parent 'bar') (block\",\n        \"  (= $a 10)\",\n        \"  (= $b 20)\",\n        \"))\"\n        ].join(\"\\n\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_functions_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire_relative 'parser_rspec_helper'\n\ndescribe 'egrammar parsing function definitions' do\n  include ParserRspecHelper\n\n  context 'without return type' do\n    it 'function foo() { 1 }' do\n      expect(dump(parse('function foo() { 1 }'))).to eq(\"(function foo (block\\n  1\\n))\")\n    end\n  end\n\n  context 'with return type' do\n    it 'function foo() >> Integer { 1 }' do\n      expect(dump(parse('function foo() >> Integer { 1 }'))).to eq(\"(function foo (return_type Integer) (block\\n  1\\n))\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_heredoc_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/parser_rspec_helper')\n\ndescribe \"egrammar parsing heredoc\" do\n  include ParserRspecHelper\n\n  it \"parses plain heredoc\" do\n    expect(dump(parse(\"@(END)\\nThis is\\nheredoc text\\nEND\\n\"))).to eq([\n      \"(@()\",\n      \"  'This is\\nheredoc text\\n'\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses heredoc with margin\" do\n    src = [\n    \"@(END)\",\n    \"   This is\",\n    \"   heredoc text\",\n    \"   | END\",\n    \"\"\n    ].join(\"\\n\")\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  'This is\\nheredoc text\\n'\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses heredoc with margin and right newline trim\" do\n    src = [\n    \"@(END)\",\n    \"   This is\",\n    \"   heredoc text\",\n    \"   |- END\",\n    \"\"\n    ].join(\"\\n\")\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  'This is\\nheredoc text'\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses syntax and escape specification\" do\n    src = <<-CODE\n    @(END:syntax/t)\n    Tex\\\\tt\\\\n\n    |- END\n    CODE\n    expect(dump(parse(src))).to eq([\n      \"(@(syntax)\",\n      \"  'Tex\\tt\\\\n'\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses interpolated heredoc expression\" do\n    src = <<-CODE\n    @(\"END\")\n    Hello $name\n    |- END\n    CODE\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  (cat 'Hello ' (str $name))\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses interpolated heredoc expression containing escapes\" do\n    src = <<-CODE\n    @(\"END\")\n    Hello \\\\$name\n    |- END\n    CODE\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  (cat 'Hello \\\\' (str $name))\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses interpolated heredoc expression containing escapes when escaping other things than $\" do\n    src = <<-CODE\n    @(\"END\"/t)\n    Hello \\\\$name\n    |- END\n    CODE\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  (cat 'Hello \\\\' (str $name))\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses with escaped newlines without preceding whitespace\" do\n    src = <<-CODE\n    @(END/L)\n    First Line\\\\\n     Second Line\n    |- END\n    CODE\n    parse(src)\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  'First Line Second Line'\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses with escaped newlines with proper margin\" do\n    src = <<-CODE\n    @(END/L)\n     First Line\\\\\n      Second Line\n    |- END\n    CODE\n    parse(src)\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  ' First Line  Second Line'\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses interpolated heredoc expression with false start on $\" do\n    src = <<-CODE\n    @(\"END\")\n    Hello $name$%a\n    |- END\n    CODE\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  (cat 'Hello ' (str $name) '$%a')\",\n      \")\"\n    ].join(\"\\n\"))\n  end\n\n  it \"parses interpolated [] expression by looking at the correct preceding char for space when there is no heredoc margin\" do\n    # NOTE: Important not to use the left margin feature here\n    src = <<-CODE\n$xxxxxxx = @(\"END\")\n${facts['os']['family']}\nXXXXXXX XXX\nEND\nCODE\n    expect(dump(parse(src))).to eq([\n      \"(= $xxxxxxx (@()\",\n      \"  (cat (str (slice (slice $facts 'os') 'family')) '\",\n      \"XXXXXXX XXX\",\n      \"')\",\n      \"))\"].join(\"\\n\"))\n  end\n\n  it \"parses interpolated [] expression by looking at the correct preceding char for space when there is a heredoc margin\" do\n    # NOTE: Important not to use the left margin feature here - the problem in PUP 9303 is triggered by lines and text before\n    # an interpolation containing []. \n    src = <<-CODE\n# comment\n# comment\n$xxxxxxx = @(\"END\")\n    1\n    2\n    3\n    4\n    5\n    YYYYY${facts['fqdn']}\n    XXXXXXX XXX\n    | END\nCODE\n    expect(dump(parse(src))).to eq([\n      \"(= $xxxxxxx (@()\",\n      \"  (cat '1\", \"2\", \"3\", \"4\", \"5\",\n      \"YYYYY' (str (slice $facts 'fqdn')) '\",\n      \"XXXXXXX XXX\",\n      \"')\",\n      \"))\"].join(\"\\n\"))\n  end\n\n  it \"correctly reports an error location in a nested heredoc with margin\" do\n    # NOTE: Important not to use the left margin feature here - the problem in PUP 9303 is triggered by lines and text before\n    # an interpolation containing []. \n    src = <<-CODE\n# comment\n# comment\n$xxxxxxx = @(\"END\")\n  1\n  2\n  3\n  4\n  5\n  YYYYY${facts]}\n  XXXXXXX XXX\n  | END\nCODE\n    expect{parse(src)}.to raise_error(/Syntax error at '\\]' \\(line: 9, column: 15\\)/)\n  end\n\n  it \"correctly reports an error location in a heredoc with line endings escaped\" do\n    # DO NOT CHANGE INDENTATION OF THIS HEREDOC\n    src = <<-CODE\n    # line one\n    # line two\n    @(\"END\"/L)\n    First Line\\\\\n    Second Line ${facts]}\n    |- END\n    CODE\n    expect{parse(src)}.to raise_error(/Syntax error at '\\]' \\(line: 5, column: 24\\)/)\n  end\n\n  it \"correctly reports an error location in a heredoc with line endings escaped when there is text in the margin\" do\n    # DO NOT CHANGE INDENTATION OR SPACING OF THIS HEREDOC\n    src = <<-CODE\n    # line one\n    # line two\n    @(\"END\"/L)\n    First Line\\\\\n    Second Line\n  x Third Line ${facts]}\n    |- END\n    # line 8\n    # line 9\n    CODE\n    expect{parse(src)}.to raise_error(/Syntax error at '\\]' \\(line: 6, column: 23\\)/)\n  end\n\n  it \"correctly reports an error location in a heredoc with line endings escaped when there is text in the margin\" do\n    # DO NOT CHANGE INDENTATION OR SPACING OF THIS HEREDOC\n    src = <<-CODE\n    @(END)\nAAA\n BBB\n  CCC\n   DDD\n    EEE\n     FFF\n    |- END\n    CODE\n    expect(dump(parse(src))).to eq([\n      \"(@()\",\n      \"  'AAA\", # no left space trimmed\n      \" BBB\",\n      \"  CCC\",\n      \"   DDD\",\n      \"EEE\", # left space trimmed\n      \" FFF'\", # indented one because it is one in from margin marker\n      \")\"].join(\"\\n\"))\n  end\n\n  it 'parses multiple heredocs on the same line' do\n    src = <<-CODE\n    notice({ @(foo) => @(bar) })\n    hello\n    -foo\n    world\n    -bar\n    notice '!'\n    CODE\n    expect(dump(parse(src))).to eq([\n      '(block',\n      '  (invoke notice ({} ((@()',\n      '    \\'    hello\\'',\n      '  ) (@()',\n      '    \\'    world\\'',\n      '  ))))',\n      '  (invoke notice \\'!\\')',\n      ')'\n    ].join(\"\\n\"))\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_lambda_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire_relative 'parser_rspec_helper'\n\ndescribe 'egrammar parsing lambda definitions' do\n  include ParserRspecHelper\n\n  context 'without return type' do\n    it 'f() |$x| { 1 }' do\n      expect(dump(parse('f() |$x| { 1 }'))).to eq(\"(invoke f (lambda (parameters x) (block\\n  1\\n)))\")\n    end\n  end\n\n  context 'with return type' do\n    it 'f() |$x| >> Integer { 1 }' do\n      expect(dump(parse('f() |$x| >> Integer { 1 }'))).to eq(\"(invoke f (lambda (parameters x) (return_type Integer) (block\\n  1\\n)))\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_plan_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire_relative 'parser_rspec_helper'\n\ndescribe \"egrammar parsing of 'plan'\" do\n  include ParserRspecHelper\n\n  context 'with --tasks' do\n    before(:each) do\n      Puppet[:tasks] = true\n    end\n\n    it \"an empty body\" do\n      expect(dump(parse(\"plan foo { }\"))).to eq(\"(plan foo ())\")\n    end\n\n    it \"a non empty body\" do\n      prog = <<-EPROG\n  plan foo {\n    $a = 10\n    $b = 20\n  }\n  EPROG\n      expect(dump(parse(prog))).to eq( [\n  \"(plan foo (block\",\n  \"  (= $a 10)\",\n  \"  (= $b 20)\",\n  \"))\",\n  ].join(\"\\n\"))\n    end\n\n    it \"accepts parameters\" do\n      s = \"plan foo($p1 = 'yo', $p2) { }\"\n      expect(dump(parse(s))).to eq(\"(plan foo (parameters (= p1 'yo') p2) ())\")\n    end\n  end\n\n  context 'with --no-tasks' do\n    before(:each) do\n      Puppet[:tasks] = false\n    end\n\n    it \"the keyword 'plan' is a name\" do\n      expect(dump(parse(\"$a = plan\"))).to eq(\"(= $a plan)\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parse_resource_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\nrequire File.join(File.dirname(__FILE__), '/parser_rspec_helper')\n\ndescribe \"egrammar parsing resource declarations\" do\n  include ParserRspecHelper\n\n  context \"When parsing regular resource\" do\n    [\"File\", \"file\"].each do |word|\n      it \"#{word} { 'title': }\" do\n        expect(dump(parse(\"#{word} { 'title': }\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title'))\"\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { 'title': path => '/somewhere', mode => '0777'}\" do\n        expect(dump(parse(\"#{word} { 'title': path => '/somewhere', mode => '0777'}\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title'\",\n          \"    (path => '/somewhere')\",\n          \"    (mode => '0777')))\"\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { 'title': path => '/somewhere', }\" do\n        expect(dump(parse(\"#{word} { 'title': path => '/somewhere', }\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title'\",\n          \"    (path => '/somewhere')))\"\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { 'title': , }\" do\n        expect(dump(parse(\"#{word} { 'title': , }\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title'))\"\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { 'title': ; }\" do\n        expect(dump(parse(\"#{word} { 'title': ; }\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title'))\"\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { 'title': ; 'other_title': }\" do\n        expect(dump(parse(\"#{word} { 'title': ; 'other_title': }\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title')\",\n          \"  ('other_title'))\"\n        ].join(\"\\n\"))\n      end\n\n      # PUP-2898, trailing ';'\n      it \"#{word} { 'title': ; 'other_title': ; }\" do\n        expect(dump(parse(\"#{word} { 'title': ; 'other_title': ; }\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title')\",\n          \"  ('other_title'))\"\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { 'title1': path => 'x'; 'title2': path => 'y'}\" do\n        expect(dump(parse(\"#{word} { 'title1': path => 'x'; 'title2': path => 'y'}\"))).to eq([\n          \"(resource #{word}\",\n          \"  ('title1'\",\n          \"    (path => 'x'))\",\n          \"  ('title2'\",\n          \"    (path => 'y')))\",\n        ].join(\"\\n\"))\n      end\n\n      it \"#{word} { title: * => {mode => '0777'} }\" do\n        expect(dump(parse(\"#{word} { title: * => {mode => '0777'}}\"))).to eq([\n          \"(resource #{word}\",\n          \"  (title\",\n          \"    (* => ({} (mode '0777')))))\"\n        ].join(\"\\n\"))\n      end\n    end\n  end\n\n  context \"When parsing (type based) resource defaults\" do\n    it \"File {  }\" do\n      expect(dump(parse(\"File { }\"))).to eq(\"(resource-defaults File)\")\n    end\n\n    it \"File { mode => '0777' }\" do\n      expect(dump(parse(\"File { mode => '0777'}\"))).to eq([\n        \"(resource-defaults File\",\n        \"  (mode => '0777'))\"\n      ].join(\"\\n\"))\n    end\n\n    it \"File { * => {mode => '0777'} } (even if validated to be illegal)\" do\n      expect(dump(parse(\"File { * => {mode => '0777'}}\"))).to eq([\n        \"(resource-defaults File\",\n        \"  (* => ({} (mode '0777'))))\"\n      ].join(\"\\n\"))\n    end\n  end\n\n  context \"When parsing resource override\" do\n    it \"File['x'] {  }\" do\n      expect(dump(parse(\"File['x'] { }\"))).to eq(\"(override (slice File 'x'))\")\n    end\n\n    it \"File['x'] { x => 1 }\" do\n      expect(dump(parse(\"File['x'] { x => 1}\"))).to eq([\n        \"(override (slice File 'x')\",\n        \"  (x => 1))\"\n        ].join(\"\\n\"))\n    end\n\n\n    it \"File['x', 'y'] { x => 1 }\" do\n      expect(dump(parse(\"File['x', 'y'] { x => 1}\"))).to eq([\n        \"(override (slice File ('x' 'y'))\",\n        \"  (x => 1))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"File['x'] { x => 1, y => 2 }\" do\n      expect(dump(parse(\"File['x'] { x => 1, y=> 2}\"))).to eq([\n        \"(override (slice File 'x')\",\n        \"  (x => 1)\",\n        \"  (y => 2))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"File['x'] { x +> 1 }\" do\n      expect(dump(parse(\"File['x'] { x +> 1}\"))).to eq([\n        \"(override (slice File 'x')\",\n        \"  (x +> 1))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"File['x'] { * => {mode => '0777'} } (even if validated to be illegal)\" do\n      expect(dump(parse(\"File['x'] { * => {mode => '0777'}}\"))).to eq([\n        \"(override (slice File 'x')\",\n        \"  (* => ({} (mode '0777'))))\"\n      ].join(\"\\n\"))\n    end\n  end\n\n  context \"When parsing virtual and exported resources\" do\n    it \"parses exported @@file { 'title': }\" do\n      expect(dump(parse(\"@@file { 'title': }\"))).to eq(\"(exported-resource file\\n  ('title'))\")\n    end\n\n    it \"parses virtual @file { 'title': }\" do\n      expect(dump(parse(\"@file { 'title': }\"))).to eq(\"(virtual-resource file\\n  ('title'))\")\n    end\n\n    it \"nothing before the title colon is a syntax error\" do\n      expect do\n        parse(\"@file {: mode => '0777' }\")\n      end.to raise_error(/Syntax error/)\n    end\n\n    it \"raises error for user error; not a resource\" do\n      # The expression results in VIRTUAL, CALL FUNCTION('file', HASH) since the resource body has\n      # no title.\n      expect do\n        parse(\"@file { mode => '0777' }\")\n      end.to raise_error(/Virtual \\(@\\) can only be applied to a Resource Expression/)\n    end\n\n    it \"parses global defaults with @ (even if validated to be illegal)\" do\n      expect(dump(parse(\"@File { mode => '0777' }\"))).to eq([\n        \"(virtual-resource-defaults File\",\n        \"  (mode => '0777'))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"parses global defaults with @@ (even if validated to be illegal)\" do\n      expect(dump(parse(\"@@File { mode => '0777' }\"))).to eq([\n        \"(exported-resource-defaults File\",\n        \"  (mode => '0777'))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"parses override with @ (even if validated to be illegal)\" do\n      expect(dump(parse(\"@File[foo] { mode => '0777' }\"))).to eq([\n        \"(virtual-override (slice File foo)\",\n        \"  (mode => '0777'))\"\n        ].join(\"\\n\"))\n    end\n\n    it \"parses override combined with @@ (even if validated to be illegal)\" do\n      expect(dump(parse(\"@@File[foo] { mode => '0777' }\"))).to eq([\n        \"(exported-override (slice File foo)\",\n        \"  (mode => '0777'))\"\n        ].join(\"\\n\"))\n    end\n  end\n\n  context \"When parsing class resource\" do\n    it \"class { 'cname': }\" do\n      expect(dump(parse(\"class { 'cname': }\"))).to eq([\n        \"(resource class\",\n        \"  ('cname'))\"\n      ].join(\"\\n\"))\n    end\n\n    it \"@class { 'cname': }\" do\n      expect(dump(parse(\"@class { 'cname': }\"))).to eq([\n        \"(virtual-resource class\",\n        \"  ('cname'))\"\n      ].join(\"\\n\"))\n    end\n\n    it \"@@class { 'cname': }\" do\n      expect(dump(parse(\"@@class { 'cname': }\"))).to eq([\n        \"(exported-resource class\",\n        \"  ('cname'))\"\n      ].join(\"\\n\"))\n    end\n\n    it \"class { 'cname': x => 1, y => 2}\" do\n      expect(dump(parse(\"class { 'cname': x => 1, y => 2}\"))).to eq([\n        \"(resource class\",\n        \"  ('cname'\",\n        \"    (x => 1)\",\n        \"    (y => 2)))\"\n      ].join(\"\\n\"))\n    end\n\n    it \"class { 'cname1': x => 1; 'cname2': y => 2}\" do\n      expect(dump(parse(\"class { 'cname1': x => 1; 'cname2': y => 2}\"))).to eq([\n        \"(resource class\",\n        \"  ('cname1'\",\n        \"    (x => 1))\",\n        \"  ('cname2'\",\n        \"    (y => 2)))\",\n      ].join(\"\\n\"))\n    end\n  end\n\n  context \"reported issues in 3.x\" do\n    it \"should not screw up on brackets in title of resource #19632\" do\n      expect(dump(parse('notify { \"thisisa[bug]\": }'))).to eq([\n        \"(resource notify\",\n        \"  ('thisisa[bug]'))\",\n      ].join(\"\\n\"))\n    end\n  end\n\n  context \"When parsing Relationships\" do\n    it \"File[a] -> File[b]\" do\n      expect(dump(parse(\"File[a] -> File[b]\"))).to eq(\"(-> (slice File a) (slice File b))\")\n    end\n\n    it \"File[a] <- File[b]\" do\n      expect(dump(parse(\"File[a] <- File[b]\"))).to eq(\"(<- (slice File a) (slice File b))\")\n    end\n\n    it \"File[a] ~> File[b]\" do\n      expect(dump(parse(\"File[a] ~> File[b]\"))).to eq(\"(~> (slice File a) (slice File b))\")\n    end\n\n    it \"File[a] <~ File[b]\" do\n      expect(dump(parse(\"File[a] <~ File[b]\"))).to eq(\"(<~ (slice File a) (slice File b))\")\n    end\n\n    it \"Should chain relationships\" do\n      expect(dump(parse(\"a -> b -> c\"))).to eq(\n      \"(-> (-> a b) c)\"\n      )\n    end\n\n    it \"Should chain relationships\" do\n      expect(dump(parse(\"File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]\"))).to eq(\n      \"(<~ (<- (~> (-> (slice File a) (slice File b)) (slice File c)) (slice File d)) (slice File e))\"\n      )\n    end\n\n    it \"should create relationships between collects\" do\n      expect(dump(parse(\"File <| mode == 0644 |> -> File <| mode == 0755 |>\"))).to eq(\n      \"(-> (collect File\\n  (<| |> (== mode 0644))) (collect File\\n  (<| |> (== mode 0755))))\"\n      )\n    end\n  end\n\n  context \"When parsing collection\" do\n    context \"of virtual resources\" do\n      it \"File <| |>\" do\n        expect(dump(parse(\"File <| |>\"))).to eq(\"(collect File\\n  (<| |>))\")\n      end\n    end\n\n    context \"of exported resources\" do\n      it \"File <<| |>>\" do\n        expect(dump(parse(\"File <<| |>>\"))).to eq(\"(collect File\\n  (<<| |>>))\")\n      end\n    end\n\n    context \"queries are parsed with correct precedence\" do\n      it \"File <| tag == 'foo' |>\" do\n        expect(dump(parse(\"File <| tag == 'foo' |>\"))).to eq(\"(collect File\\n  (<| |> (== tag 'foo')))\")\n      end\n\n      it \"File <| tag == 'foo' and mode != '0777' |>\" do\n        expect(dump(parse(\"File <| tag == 'foo' and mode != '0777' |>\"))).to eq(\"(collect File\\n  (<| |> (&& (== tag 'foo') (!= mode '0777'))))\")\n      end\n\n      it \"File <| tag == 'foo' or mode != '0777' |>\" do\n        expect(dump(parse(\"File <| tag == 'foo' or mode != '0777' |>\"))).to eq(\"(collect File\\n  (<| |> (|| (== tag 'foo') (!= mode '0777'))))\")\n      end\n\n      it \"File <| tag == 'foo' or tag == 'bar' and mode != '0777' |>\" do\n        expect(dump(parse(\"File <| tag == 'foo' or tag == 'bar' and mode != '0777' |>\"))).to eq(\n        \"(collect File\\n  (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode '0777')))))\"\n        )\n      end\n\n      it \"File <| (tag == 'foo' or tag == 'bar') and mode != '0777' |>\" do\n        expect(dump(parse(\"File <| (tag == 'foo' or tag == 'bar') and mode != '0777' |>\"))).to eq(\n        \"(collect File\\n  (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode '0777'))))\"\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parser_rspec_helper.rb",
    "content": "require 'puppet/pops'\n\nrequire File.join(File.dirname(__FILE__), '/../factory_rspec_helper')\n\nmodule ParserRspecHelper\n  include FactoryRspecHelper\n\n  def parse(code)\n    parser = Puppet::Pops::Parser::Parser.new()\n    parser.parse_string(code)\n  end\n\n  def parse_epp(code)\n    parser = Puppet::Pops::Parser::EppParser.new()\n    parser.parse_string(code)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe Puppet::Pops::Parser::Parser do\n  it \"should instantiate a parser\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    expect(parser.class).to eq(Puppet::Pops::Parser::Parser)\n  end\n\n  it \"should parse a code string and return a model\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"$a = 10\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.class).to eq(Puppet::Pops::Model::AssignmentExpression)\n  end\n\n  it \"should accept empty input and return a model\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.class).to eq(Puppet::Pops::Model::Nop)\n  end\n\n  it \"should accept empty input containing only comments and report location at end of input\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"# comment\\n\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.class).to eq(Puppet::Pops::Model::Nop)\n    expect(model.body.offset).to eq(10)\n    expect(model.body.length).to eq(0)\n  end\n\n  it \"should give single resource expressions the correct offset inside an if/else statement\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(<<-EOF).model\nclass firewall {\n  if(true) {\n    service { 'if service':\n      ensure    => stopped\n    }\n  } else {\n    service { 'else service':\n      ensure    => running\n    }\n  }\n}\n    EOF\n\n    then_service = model.body.body.statements[0].then_expr\n    expect(then_service.class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(then_service.offset).to eq(34)\n\n    else_service = model.body.body.statements[0].else_expr\n    expect(else_service.class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(else_service.offset).to eq(106)\n  end\n\n  it \"should give block expressions and their contained resources the correct offset inside an if/else statement\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(<<-EOF).model\nclass firewall {\n  if(true) {\n    service { 'if service 1':\n      ensure    => running\n    }\n\n    service { 'if service 2':\n      ensure    => stopped\n    }\n  } else {\n    service { 'else service 1':\n      ensure    => running\n    }\n\n    service { 'else service 2':\n      ensure    => stopped\n    }\n  }\n}\n    EOF\n\n    if_expr = model.body.body.statements[0]\n    block_expr = model.body.body.statements[0].then_expr\n    expect(if_expr.class).to eq(Puppet::Pops::Model::IfExpression)\n    expect(if_expr.offset).to eq(19)\n    expect(block_expr.class).to eq(Puppet::Pops::Model::BlockExpression)\n    expect(block_expr.offset).to eq(28)\n    expect(block_expr.statements[0].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[0].offset).to eq(34)\n    expect(block_expr.statements[1].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[1].offset).to eq(98)\n\n    block_expr = model.body.body.statements[0].else_expr\n    expect(block_expr.class).to eq(Puppet::Pops::Model::BlockExpression)\n    expect(block_expr.offset).to eq(166)\n    expect(block_expr.statements[0].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[0].offset).to eq(172)\n    expect(block_expr.statements[1].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[1].offset).to eq(238)\n  end\n\n  it \"should give single resource expressions the correct offset inside an unless/else statement\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(<<-EOF).model\nclass firewall {\n  unless(true) {\n    service { 'if service':\n      ensure    => stopped\n    }\n  } else {\n    service { 'else service':\n      ensure    => running\n    }\n  }\n}\n    EOF\n\n    then_service = model.body.body.statements[0].then_expr\n    expect(then_service.class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(then_service.offset).to eq(38)\n\n    else_service = model.body.body.statements[0].else_expr\n    expect(else_service.class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(else_service.offset).to eq(110)\n  end\n\n  it \"should give block expressions and their contained resources the correct offset inside an unless/else statement\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(<<-EOF).model\nclass firewall {\n  unless(true) {\n    service { 'if service 1':\n      ensure    => running\n    }\n\n    service { 'if service 2':\n      ensure    => stopped\n    }\n  } else {\n    service { 'else service 1':\n      ensure    => running\n    }\n\n    service { 'else service 2':\n      ensure    => stopped\n    }\n  }\n}\n    EOF\n\n    if_expr = model.body.body.statements[0]\n    block_expr = model.body.body.statements[0].then_expr\n    expect(if_expr.class).to eq(Puppet::Pops::Model::UnlessExpression)\n    expect(if_expr.offset).to eq(19)\n    expect(block_expr.class).to eq(Puppet::Pops::Model::BlockExpression)\n    expect(block_expr.offset).to eq(32)\n    expect(block_expr.statements[0].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[0].offset).to eq(38)\n    expect(block_expr.statements[1].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[1].offset).to eq(102)\n\n    block_expr = model.body.body.statements[0].else_expr\n    expect(block_expr.class).to eq(Puppet::Pops::Model::BlockExpression)\n    expect(block_expr.offset).to eq(170)\n    expect(block_expr.statements[0].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[0].offset).to eq(176)\n    expect(block_expr.statements[1].class).to eq(Puppet::Pops::Model::ResourceExpression)\n    expect(block_expr.statements[1].offset).to eq(242)\n  end\n\n  it \"multi byte characters in a comment are counted as individual bytes\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    model = parser.parse_string(\"# \\u{0400}comment\\n\").model\n    expect(model.class).to eq(Puppet::Pops::Model::Program)\n    expect(model.body.class).to eq(Puppet::Pops::Model::Nop)\n    expect(model.body.offset).to eq(12)\n    expect(model.body.length).to eq(0)\n  end\n\n  it \"should raise an error with position information when error is raised from within parser\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    the_error = nil\n    begin\n      parser.parse_string(\"File [1] { }\", 'fakefile.pp')\n    rescue Puppet::ParseError => e\n      the_error = e\n    end\n    expect(the_error).to be_a(Puppet::ParseError)\n    expect(the_error.file).to eq('fakefile.pp')\n    expect(the_error.line).to eq(1)\n    expect(the_error.pos).to eq(6)\n  end\n\n  it \"should raise an error with position information when error is raised on token\" do\n    parser = Puppet::Pops::Parser::Parser.new()\n    the_error = nil\n    begin\n      parser.parse_string(<<-EOF, 'fakefile.pp')\nclass whoops($a,$b,$c) {\n  $d = \"oh noes\",  \"b\"\n}\n      EOF\n    rescue Puppet::ParseError => e\n      the_error = e\n    end\n    expect(the_error).to be_a(Puppet::ParseError)\n    expect(the_error.file).to eq('fakefile.pp')\n    expect(the_error.line).to eq(2)\n    expect(the_error.pos).to eq(17)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/parsing_typed_parameters_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\nrequire 'puppet/pops/evaluator/evaluator_impl'\nrequire 'puppet_spec/pops'\nrequire 'puppet_spec/scope'\nrequire 'puppet/parser/e4_parser_adapter'\n\n\n# relative to this spec file (./) does not work as this file is loaded by rspec\n#require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')\n\ndescribe 'Puppet::Pops::Evaluator::EvaluatorImpl' do\n  include PuppetSpec::Pops\n  include PuppetSpec::Scope\n\n  let(:parser) {  Puppet::Pops::Parser::EvaluatingParser.new }\n\n  context \"captures-rest parameter\" do\n    it 'is allowed in lambda when placed last' do\n      source = <<-CODE\n        foo() |$a, *$b| { $a + $b[0] }\n      CODE\n      expect do\n        parser.parse_string(source, __FILE__)\n      end.to_not raise_error()\n    end\n\n    it 'allows a type annotation' do\n      source = <<-CODE\n        foo() |$a, Integer *$b| { $a + $b[0] }\n      CODE\n      expect do\n        parser.parse_string(source, __FILE__)\n      end.to_not raise_error()\n    end\n\n    it 'is not allowed in lambda except last' do\n      source = <<-CODE\n        foo() |*$a, $b| { $a + $b[0] }\n      CODE\n      expect do\n        parser.parse_string(source, __FILE__)\n      end.to raise_error(Puppet::ParseError, /Parameter \\$a is not last, and has 'captures rest'/)\n    end\n\n    it 'is not allowed in define' do\n      source = <<-CODE\n        define foo(*$a) { }\n      CODE\n      expect do\n        parser.parse_string(source, __FILE__)\n      end.to raise_error(Puppet::ParseError, /Parameter \\$a has 'captures rest' - not supported in a 'define'/)\n    end\n\n    it 'is not allowed in class' do\n      source = <<-CODE\n        class foo(*$a) { }\n      CODE\n      expect do\n        parser.parse_string(source, __FILE__)\n      end.to raise_error(Puppet::ParseError, /Parameter \\$a has 'captures rest' - not supported in a Host Class Definition/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/parser/pn_parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/pops/pn'\n\nmodule Puppet::Pops\nmodule Parser\n\ndescribe 'Puppet::Pops::Parser::PNParser' do\n  context 'parses the text' do\n    it '\"true\" to PN::Literal(true)' do\n      expect(PNParser.new.parse('true')).to eql(lit(true))\n    end\n\n    it '\"false\" to PN::Literal(false)' do\n      expect(PNParser.new.parse('false')).to eql(lit(false))\n    end\n\n    it '\"nil\" to PN::Literal(nil)' do\n      expect(PNParser.new.parse('nil')).to eql(lit(nil))\n    end\n\n    it '\"123\" to PN::Literal(123)' do\n      expect(PNParser.new.parse('123')).to eql(lit(123))\n    end\n\n    it '\"-123\" to PN::Literal(-123)' do\n      expect(PNParser.new.parse('-123')).to eql(lit(-123))\n    end\n\n    it '\"123.45\" to PN::Literal(123.45)' do\n      expect(PNParser.new.parse('123.45')).to eql(lit(123.45))\n    end\n\n    it '\"-123.45\" to PN::Literal(-123.45)' do\n      expect(PNParser.new.parse('-123.45')).to eql(lit(-123.45))\n    end\n\n    it '\"123.45e12\" to PN::Literal(123.45e12)' do\n      expect(PNParser.new.parse('123.45e12')).to eql(lit(123.45e12))\n    end\n\n    it '\"123.45e+12\" to PN::Literal(123.45e+12)' do\n      expect(PNParser.new.parse('123.45e+12')).to eql(lit(123.45e+12))\n    end\n\n    it '\"123.45e-12\" to PN::Literal(123.45e-12)' do\n      expect(PNParser.new.parse('123.45e-12')).to eql(lit(123.45e-12))\n    end\n\n    it '\"hello\" to PN::Literal(\"hello\")' do\n      expect(PNParser.new.parse('\"hello\"')).to eql(lit('hello'))\n    end\n\n    it '\"\\t\" to PN::Literal(\"\\t\")' do\n      expect(PNParser.new.parse('\"\\t\"')).to eql(lit(\"\\t\"))\n    end\n\n    it '\"\\r\" to PN::Literal(\"\\r\")' do\n      expect(PNParser.new.parse('\"\\r\"')).to eql(lit(\"\\r\"))\n    end\n\n    it '\"\\n\" to PN::Literal(\"\\n\")' do\n      expect(PNParser.new.parse('\"\\n\"')).to eql(lit(\"\\n\"))\n    end\n\n    it '\"\\\"\" to PN::Literal(\"\\\"\")' do\n      expect(PNParser.new.parse('\"\\\"\"')).to eql(lit('\"'))\n    end\n\n    it '\"\\\\\\\\\" to PN::Literal(\"\\\\\")' do\n      expect(PNParser.new.parse('\"\\\\\\\\\"')).to eql(lit(\"\\\\\"))\n    end\n\n    it '\"\\o024\" to PN::Literal(\"\\u{14}\")' do\n      expect(PNParser.new.parse('\"\\o024\"')).to eql(lit(\"\\u{14}\"))\n    end\n  end\n\n  it 'parses elements enclosed in brackets to a PN::List' do\n    expect(PNParser.new.parse('[1 \"2\" true false nil]')).to eql(PN::List.new([lit(1), lit('2'), lit(true), lit(false), lit(nil)]))\n  end\n\n  it 'parses elements enclosed in parenthesis to a PN::Call' do\n    expect(PNParser.new.parse('(+ 1 2)')).to eql(PN::Call.new('+', lit(1), lit(2)))\n  end\n\n  it 'parses entries enclosed in curly braces to a PN::Map' do\n    expect(PNParser.new.parse('{:a 1 :b \"2\" :c true}')).to eql(PN::Map.new([entry('a', 1), entry('b', '2'), entry('c', true)]))\n  end\n\n  def entry(k, v)\n    PN::Entry.new(k, lit(v))\n  end\n\n  def lit(v)\n    PN::Literal.new(v)\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/pn_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet/pops/pn'\n\nmodule Puppet::Pops\nmodule PN\n\ndescribe 'Puppet::Pops::PN' do\n  context 'Literal' do\n    context 'containing the value' do\n      it 'true produces the text \"true\"' do\n        expect(lit(true).to_s).to eql('true')\n      end\n\n      it 'false produces the text \"false\"' do\n        expect(lit(false).to_s).to eql('false')\n      end\n\n      it 'nil produces the text \"nil\"' do\n        expect(lit(nil).to_s).to eql('nil')\n      end\n\n      it '34 produces the text \"34\"' do\n        expect(lit(34).to_s).to eql('34')\n      end\n\n      it '3.0 produces the text \"3.0\"' do\n        expect(lit(3.0).to_s).to eql('3.0')\n      end\n    end\n\n    context 'produces a double quoted text from a string such that' do\n      it '\"plain\" produces \"plain\"' do\n        expect(lit('plain').to_s).to eql('\"plain\"')\n      end\n\n      it '\"\\t\" produces a text containing a tab character' do\n        expect(lit(\"\\t\").to_s).to eql('\"\\t\"')\n      end\n\n      it '\"\\r\" produces a text containing a return character' do\n        expect(lit(\"\\r\").to_s).to eql('\"\\r\"')\n      end\n\n      it '\"\\n\" produces a text containing a newline character' do\n        expect(lit(\"\\n\").to_s).to eql('\"\\n\"')\n      end\n\n      it '\"\\\"\" produces a text containing a double quote' do\n        expect(lit(\"\\\"\").to_s).to eql('\"\\\"\"')\n      end\n\n      it '\"\\\\\" produces a text containing a backslash' do\n        expect(lit(\"\\\\\").to_s).to eql('\"\\\\\\\\\"')\n      end\n\n      it '\"\\u{14}\" produces \"\\o024\"' do\n        expect(lit(\"\\u{14}\").to_s).to eql('\"\\o024\"')\n      end\n    end\n  end\n\n  context 'List' do\n    it 'produces a text where its elements are enclosed in brackets' do\n      expect(List.new([lit(3), lit('a'), lit(true)]).to_s).to eql('[3 \"a\" true]')\n    end\n\n    it 'produces a text where the elements of nested lists are enclosed in brackets' do\n      expect(List.new([lit(3), lit('a'), List.new([lit(true), lit(false)])]).to_s).to eql('[3 \"a\" [true false]]')\n    end\n\n    context 'with indent' do\n      it 'produces a text where the each element is on an indented line ' do\n        s = ''\n        List.new([lit(3), lit('a'), List.new([lit(true), lit(false)])]).format(Indent.new('  '), s)\n        expect(s).to eql(<<-RESULT.unindent[0..-2]) # unindent and strip last newline\n        [\n          3\n          \"a\"\n          [\n            true\n            false]]\n        RESULT\n      end\n    end\n  end\n\n  context 'Map' do\n    it 'raises error when illegal keys are used' do\n      expect { Map.new([Entry.new('123', lit(3))]) }.to raise_error(ArgumentError, /key 123 does not conform to pattern/)\n    end\n\n    it 'produces a text where entries are enclosed in curly braces' do\n      expect(Map.new([Entry.new('a', lit(3))]).to_s).to eql('{:a 3}')\n    end\n\n    it 'produces a text where the entries of nested maps are enclosed in curly braces' do\n      expect(Map.new([\n        Map.new([Entry.new('a', lit(3))]).with_name('o')]).to_s).to eql('{:o {:a 3}}')\n    end\n\n    context 'with indent' do\n      it 'produces a text where the each element is on an indented line ' do\n        s = ''\n        Map.new([\n          Map.new([Entry.new('a', lit(3)), Entry.new('b', lit(5))]).with_name('o')]).format(Indent.new('  '), s)\n        expect(s).to eql(<<-RESULT.unindent[0..-2]) # unindent and strip last newline\n        {\n          :o {\n            :a 3\n            :b 5}}\n        RESULT\n      end\n    end\n  end\n\n  context 'Call' do\n    it 'produces a text where elements are enclosed in parenthesis' do\n      expect(Call.new('+', lit(3), lit(5)).to_s).to eql('(+ 3 5)')\n    end\n\n    it 'produces a text where the elements of nested calls are enclosed in parenthesis' do\n      expect(Map.new([\n        Call.new('+', lit(3), lit(5)).with_name('o')]).to_s).to eql('{:o (+ 3 5)}')\n    end\n\n    context 'with indent' do\n      it 'produces a text where the each element is on an indented line ' do\n        s = ''\n        Call.new('+', lit(3), lit(Call.new('-', lit(10), lit(5)))).format(Indent.new('  '), s)\n        expect(s).to eql(<<-RESULT.unindent[0..-2]) # unindent and strip last newline\n        (+\n          3\n          (-\n            10\n            5))\n        RESULT\n      end\n    end\n  end\n\n  def lit(v)\n    v.is_a?(PN) ? v : Literal.new(v)\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/puppet_stack_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/pops'\n\ndescribe 'Puppet::Pops::PuppetStack' do\n  class StackTraceTest\n    def get_stacktrace\n      Puppet::Pops::PuppetStack.stacktrace\n    end\n\n    def get_top_of_stack\n      Puppet::Pops::PuppetStack.top_of_stack\n    end\n\n    def one_level\n      Puppet::Pops::PuppetStack.stack(\"one_level.pp\", 1234, self, :get_stacktrace, [])\n    end\n\n    def one_level_top\n      Puppet::Pops::PuppetStack.stack(\"one_level.pp\", 1234, self, :get_top_of_stack, [])\n    end\n\n    def two_levels\n      Puppet::Pops::PuppetStack.stack(\"two_levels.pp\", 1237, self, :level2, [])\n    end\n\n    def two_levels_top\n      Puppet::Pops::PuppetStack.stack(\"two_levels.pp\", 1237, self, :level2_top, [])\n    end\n\n    def level2\n      Puppet::Pops::PuppetStack.stack(\"level2.pp\", 1240, self, :get_stacktrace, [])\n    end\n\n    def level2_top\n      Puppet::Pops::PuppetStack.stack(\"level2.pp\", 1240, self, :get_top_of_stack, [])\n    end\n\n    def gets_block(a, &block)\n      block.call(a)\n    end\n\n    def gets_args_and_block(a, b, &block)\n      block.call(a, b)\n    end\n\n    def with_nil_file\n      Puppet::Pops::PuppetStack.stack(nil, 1250, self, :get_stacktrace, [])\n    end\n\n    def with_empty_string_file\n      Puppet::Pops::PuppetStack.stack('', 1251, self, :get_stacktrace, [])\n    end\n  end\n\n  it 'returns an empty array from stacktrace when there is nothing on the stack' do\n    expect(Puppet::Pops::PuppetStack.stacktrace).to eql([])\n  end\n\n\n  context 'full stacktrace' do\n    it 'returns a one element array with file, line from stacktrace when there is one entry on the stack' do\n      expect(StackTraceTest.new.one_level).to eql([['one_level.pp', 1234]])\n    end\n\n    it 'returns an array from stacktrace with information about each level with oldest frame last' do\n      expect(StackTraceTest.new.two_levels).to eql([['level2.pp', 1240], ['two_levels.pp', 1237]])\n    end\n\n    it 'returns an empty array from top_of_stack when there is nothing on the stack' do\n      expect(Puppet::Pops::PuppetStack.top_of_stack).to eql([])\n    end\n  end\n\n  context 'top of stack' do\n    it 'returns a one element array with file, line from top_of_stack when there is one entry on the stack' do\n      expect(StackTraceTest.new.one_level_top).to eql(['one_level.pp', 1234])\n    end\n\n    it 'returns newest entry from top_of_stack' do\n      expect(StackTraceTest.new.two_levels_top).to eql(['level2.pp', 1240])\n    end\n\n    it 'accepts file to be nil' do\n      expect(StackTraceTest.new.with_nil_file).to eql([['unknown', 1250]])\n    end\n  end\n\n  it 'accepts file to be empty_string' do\n    expect(StackTraceTest.new.with_empty_string_file).to eql([['unknown', 1251]])\n  end\n\n  it 'stacktrace is empty when call has returned' do\n    StackTraceTest.new.two_levels\n    expect(Puppet::Pops::PuppetStack.stacktrace).to eql([])\n  end\n\n  it 'allows passing a block to the stack call' do\n    expect(Puppet::Pops::PuppetStack.stack(\"test.pp\", 1, StackTraceTest.new, :gets_block, ['got_it']) {|x| x }).to eql('got_it')\n  end\n\n  it 'allows passing multiple variables and a block' do\n    expect(\n      Puppet::Pops::PuppetStack.stack(\"test.pp\", 1, StackTraceTest.new, :gets_args_and_block, ['got_it', 'again']) {|x, y| [x,y].join(' ')}\n      ).to eql('got_it again')\n  end\n\nend"
  },
  {
    "path": "spec/unit/pops/resource/resource_type_impl_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Resource\ndescribe \"Puppet::Pops::Resource\" do\n  include PuppetSpec::Compiler\n\n  let!(:pp_parser) { Parser::EvaluatingParser.new }\n  let(:loader) { Loader::BaseLoader.new(nil, 'type_parser_unit_test_loader') }\n  let(:factory) { TypeFactory }\n\n  context 'when creating resources' do\n    let!(:resource_type) { ResourceTypeImpl._pcore_type }\n\n    it 'can create an instance of a ResourceType' do\n      code = <<-CODE\n        $rt = Puppet::Resource::ResourceType3.new('notify', [], [Puppet::Resource::Param.new(String, 'message')])\n        assert_type(Puppet::Resource::ResourceType3, $rt)\n        notice('looks like we made it')\n      CODE\n      rt = nil\n      notices = eval_and_collect_notices(code) do |scope, _|\n        rt = scope['rt']\n      end\n      expect(notices).to eq(['looks like we made it'])\n      expect(rt).to be_a(ResourceTypeImpl)\n      expect(rt.valid_parameter?(:nonesuch)).to be_falsey\n      expect(rt.valid_parameter?(:message)).to be_truthy\n      expect(rt.valid_parameter?(:loglevel)).to be_truthy\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/serialization/packer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops/serialization'\n\nmodule Puppet::Pops\nmodule Serialization\n[JSON].each do |packer_module|\ndescribe \"the Puppet::Pops::Serialization when using #{packer_module.name}\" do\n  let(:io) { StringIO.new }\n  let(:reader_class) { packer_module::Reader }\n  let(:writer_class) { packer_module::Writer }\n\n  def write(*values)\n    io.reopen\n    serializer = writer_class.new(io)\n    values.each { |val| serializer.write(val) }\n    serializer.finish\n    io.rewind\n  end\n\n  def read(count = nil)\n    @deserializer = reader_class.new(io)\n    count.nil? ? @deserializer.read : Array.new(count) { @deserializer.read }\n  end\n\n  context 'can write and read a' do\n    it 'String' do\n      val = 'the value'\n      write(val)\n      val2 = read\n      expect(val2).to be_a(String)\n      expect(val2).to eql(val)\n    end\n\n    it 'positive Integer' do\n      val = 2**63-1\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Integer)\n      expect(val2).to eql(val)\n    end\n\n    it 'negative Integer' do\n      val = -2**63\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Integer)\n      expect(val2).to eql(val)\n    end\n\n    it 'Float' do\n      val = 32.45\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Float)\n      expect(val2).to eql(val)\n    end\n\n    it 'true' do\n      val = true\n      write(val)\n      val2 = read\n      expect(val2).to be_a(TrueClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'false' do\n      val = false\n      write(val)\n      val2 = read\n      expect(val2).to be_a(FalseClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'nil' do\n      val = nil\n      write(val)\n      val2 = read\n      expect(val2).to be_a(NilClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'Regexp' do\n      val = /match me/\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Regexp)\n      expect(val2).to eql(val)\n    end\n\n    it 'Timespan' do\n      val = Time::Timespan.from_fields(false, 3, 12, 40, 31, 123)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Time::Timespan)\n      expect(val2).to eql(val)\n    end\n\n    it 'Timestamp' do\n      val = Time::Timestamp.now\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Time::Timestamp)\n      expect(val2).to eql(val)\n    end\n\n    it 'Version' do\n      val = SemanticPuppet::Version.parse('1.2.3-alpha2')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(SemanticPuppet::Version)\n      expect(val2).to eql(val)\n    end\n\n    it 'VersionRange' do\n      val = SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(SemanticPuppet::VersionRange)\n      expect(val2).to eql(val)\n    end\n\n    it 'Binary' do\n      val = Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PBinaryType::Binary)\n      expect(val2).to eql(val)\n    end\n\n    it 'URI' do\n      val = URI('http://bob:ewing@dallas.example.com:8080/oil/baron?crude=cash#leftovers')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(URI)\n      expect(val2).to eql(val)\n    end\n  end\n\n  context 'will fail on attempts to write' do\n    it 'Integer larger than 2**63-1' do\n      expect { write(2**63) }.to raise_error(SerializationError, 'Integer out of bounds')\n    end\n\n    it 'Integer smaller than -2**63' do\n      expect { write(-2**63-1) }.to raise_error(SerializationError, 'Integer out of bounds')\n    end\n\n    it 'objects unknown to Puppet serialization' do\n      expect { write(\"\".class) }.to raise_error(SerializationError, 'Unable to serialize a Class')\n    end\n  end\n\n  it 'should be able to write and read primitives using tabulation' do\n    val = 'the value'\n    write(val, val)\n    expect(read(2)).to eql([val, val])\n    expect(@deserializer.primitive_count).to eql(1)\n  end\nend\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/serialization/serialization_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\nmodule Serialization\n[JSON].each do |packer_module|\n  describe \"the Puppet::Pops::Serialization over #{packer_module.name}\" do\n  let!(:dumper) { Model::ModelTreeDumper.new }\n  let(:io) { StringIO.new }\n  let(:parser) { Parser::EvaluatingParser.new }\n  let(:env) { Puppet::Node::Environment.create(:testing, []) }\n  let(:loaders) { Puppet::Pops::Loaders.new(env) }\n  let(:loader) { loaders.find_loader(nil) }\n  let(:reader_class) { packer_module::Reader }\n  let(:writer_class) { packer_module::Writer }\n  let(:serializer) { Serializer.new(writer_class.new(io)) }\n  let(:deserializer) { Deserializer.new(reader_class.new(io), loaders.find_loader(nil)) }\n\n  before(:each) do\n    Puppet.push_context(:loaders => loaders, :current_environment => env)\n  end\n\n  after(:each) do\n    Puppet.pop_context()\n  end\n\n  def write(*values)\n    io.reopen\n    values.each { |val| serializer.write(val) }\n    serializer.finish\n    io.rewind\n  end\n\n  def read\n    deserializer.read\n  end\n\n  def parse(string)\n    parser.parse_string(string, '/home/tester/experiments/manifests/init.pp')\n  end\n\n  context 'can write and read a' do\n    it 'String' do\n      val = 'the value'\n      write(val)\n      val2 = read\n      expect(val2).to be_a(String)\n      expect(val2).to eql(val)\n    end\n\n    it 'Integer' do\n      val = 32\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Integer)\n      expect(val2).to eql(val)\n    end\n\n    it 'Float' do\n      val = 32.45\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Float)\n      expect(val2).to eql(val)\n    end\n\n    it 'true' do\n      val = true\n      write(val)\n      val2 = read\n      expect(val2).to be_a(TrueClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'false' do\n      val = false\n      write(val)\n      val2 = read\n      expect(val2).to be_a(FalseClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'nil' do\n      val = nil\n      write(val)\n      val2 = read\n      expect(val2).to be_a(NilClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'Regexp' do\n      val = /match me/\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Regexp)\n      expect(val2).to eql(val)\n    end\n\n    it 'Sensitive' do\n      sval = 'the sensitive value'\n      val = Types::PSensitiveType::Sensitive.new(sval)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PSensitiveType::Sensitive)\n      expect(val2.unwrap).to eql(sval)\n    end\n\n    it 'Timespan' do\n      val = Time::Timespan.from_fields(false, 3, 12, 40, 31, 123)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Time::Timespan)\n      expect(val2).to eql(val)\n    end\n\n    it 'Timestamp' do\n      val = Time::Timestamp.now\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Time::Timestamp)\n      expect(val2).to eql(val)\n    end\n\n    it 'Version' do\n      # It does succeed on rare occasions, so we need to repeat\n      val = SemanticPuppet::Version.parse('1.2.3-alpha2')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(SemanticPuppet::Version)\n      expect(val2).to eql(val)\n    end\n\n    it 'VersionRange' do\n      # It does succeed on rare occasions, so we need to repeat\n      val = SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(SemanticPuppet::VersionRange)\n      expect(val2).to eql(val)\n    end\n\n    it 'Binary' do\n      val = Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PBinaryType::Binary)\n      expect(val2).to eql(val)\n    end\n\n    it 'URI' do\n      val = URI('http://bob:ewing@dallas.example.com:8080/oil/baron?crude=cash#leftovers')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(URI)\n      expect(val2).to eql(val)\n    end\n\n    it 'Sensitive with rich data' do\n      sval = Time::Timestamp.now\n      val = Types::PSensitiveType::Sensitive.new(sval)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PSensitiveType::Sensitive)\n      expect(val2.unwrap).to be_a(Time::Timestamp)\n      expect(val2.unwrap).to eql(sval)\n    end\n  end\n\n  context 'can write and read' do\n    include_context 'types_setup'\n\n    all_types.each do |t|\n      it \"the default for type #{t.name}\" do\n        val = t::DEFAULT\n        write(val)\n        val2 = read\n        expect(val2).to be_a(t)\n        expect(val2).to eql(val)\n      end\n    end\n\n    context 'a parameterized' do\n      it 'String' do\n        val = Types::TypeFactory.string(Types::TypeFactory.range(1, :default))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PStringType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Regex' do\n        val = Types::TypeFactory.regexp(/foo/)\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PRegexpType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Collection' do\n        val = Types::TypeFactory.collection(Types::TypeFactory.range(0, 20))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PCollectionType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Array' do\n        val = Types::TypeFactory.array_of(Types::TypeFactory.integer, Types::TypeFactory.range(0, 20))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PArrayType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Hash' do\n        val = Types::TypeFactory.hash_kv(Types::TypeFactory.string, Types::TypeFactory.integer, Types::TypeFactory.range(0, 20))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PHashType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Variant' do\n        val = Types::TypeFactory.variant(Types::TypeFactory.string, Types::TypeFactory.range(1, :default))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PVariantType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Object' do\n        val = Types::TypeParser.singleton.parse('Pcore::StringType', loader)\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PObjectType)\n        expect(val2).to eql(val)\n      end\n\n      context 'ObjectType' do\n        let(:type) do\n          Types::PObjectType.new({\n            'name' => 'MyType',\n            'type_parameters' => {\n              'x' => Types::PIntegerType::DEFAULT\n            },\n            'attributes' => {\n              'x' => Types::PIntegerType::DEFAULT\n            }\n          })\n        end\n\n        it 'with preserved parameters' do\n          val = type.create(34)._pcore_type\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Types::PObjectTypeExtension)\n          expect(val2).to eql(val)\n        end\n      end\n    end\n\n    it 'Array of rich data' do\n      # Sensitive omitted because it doesn't respond to ==\n      val = [\n        Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n        Time::Timestamp.now,\n        SemanticPuppet::Version.parse('1.2.3-alpha2'),\n        SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n        Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      ]\n      write(val)\n      val2 = read\n      expect(val2).to eql(val)\n    end\n\n    it 'Hash of rich data' do\n      # Sensitive omitted because it doesn't respond to ==\n      val = {\n        'duration' => Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n        'time' => Time::Timestamp.now,\n        'version' => SemanticPuppet::Version.parse('1.2.3-alpha2'),\n        'range' => SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n        'binary' => Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      }\n      write(val)\n      val2 = read\n      expect(val2).to eql(val)\n    end\n\n    context 'an AST model' do\n      it \"Locator\" do\n        val = Parser::Locator::Locator19.new('here is some text', '/tmp/foo', [5])\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Parser::Locator::Locator19)\n        expect(val2).to eql(val)\n      end\n\n      it 'nested Expression' do\n        expr = parse(<<-CODE)\n          $rootgroup = $os['family'] ? {\n              'Solaris'          => 'wheel',\n              /(Darwin|FreeBSD)/ => 'wheel',\n              default            => 'root',\n          }\n\n          file { '/etc/passwd':\n            ensure => file,\n            owner  => 'root',\n            group  => $rootgroup,\n          }\n        CODE\n        write(expr)\n        expr2 = read\n        expect(dumper.dump(expr)).to eq(dumper.dump(expr2))\n      end\n    end\n\n    context 'PuppetObject' do\n      before(:each) do\n        class DerivedArray < Array\n          include Types::PuppetObject\n\n          def self._pcore_type\n            @type\n          end\n\n          def self.register_ptype(loader, ir)\n            @type = Pcore.create_object_type(loader, ir, DerivedArray, 'DerivedArray', nil, 'values' => Types::PArrayType::DEFAULT)\n              .resolve(loader)\n          end\n\n          def initialize(values)\n            concat(values)\n          end\n\n          def values\n            Array.new(self)\n          end\n        end\n\n        class DerivedHash < Hash\n          include Types::PuppetObject\n\n          def self._pcore_type\n            @type\n          end\n\n          def self.register_ptype(loader, ir)\n            @type = Pcore.create_object_type(loader, ir, DerivedHash, 'DerivedHash', nil, '_pcore_init_hash' => Types::PHashType::DEFAULT)\n              .resolve(loader)\n          end\n\n          def initialize(_pcore_init_hash)\n            merge!(_pcore_init_hash)\n          end\n\n          def _pcore_init_hash\n            result = {}\n            result.merge!(self)\n            result\n          end\n        end\n      end\n\n      after(:each) do\n        x = Puppet::Pops::Serialization\n        x.send(:remove_const, :DerivedArray) if x.const_defined?(:DerivedArray)\n        x.send(:remove_const, :DerivedHash) if x.const_defined?(:DerivedHash)\n      end\n\n      it 'derived from Array' do\n        DerivedArray.register_ptype(loader, loaders.implementation_registry)\n\n        # Sensitive omitted because it doesn't respond to ==\n        val = DerivedArray.new([\n          Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n          Time::Timestamp.now,\n          SemanticPuppet::Version.parse('1.2.3-alpha2'),\n          SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n          Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n        ])\n        write(val)\n        val2 = read\n        expect(val2).to eql(val)\n      end\n\n      it 'derived from Hash' do\n        DerivedHash.register_ptype(loader, loaders.implementation_registry)\n\n        # Sensitive omitted because it doesn't respond to ==\n        val = DerivedHash.new({\n          'duration' => Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n          'time' => Time::Timestamp.now,\n          'version' => SemanticPuppet::Version.parse('1.2.3-alpha2'),\n          'range' => SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n          'binary' => Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n        })\n        write(val)\n        val2 = read\n        expect(val2).to eql(val)\n      end\n    end\n  end\n\n  context 'deserializing an instance whose Object type was serialized by reference' do\n    let(:serializer) { Serializer.new(writer_class.new(io), :type_by_reference => true) }\n    let(:type) do\n      Types::PObjectType.new({\n        'name' => 'MyType',\n        'attributes' => {\n          'x' => Types::PIntegerType::DEFAULT\n        }\n      })\n    end\n\n    it 'fails when deserializer is unaware of the referenced type' do\n      write(type.create(32))\n\n      # Should fail since no loader knows about 'MyType'\n      expect{ read }.to raise_error(Puppet::Error, 'No implementation mapping found for Puppet Type MyType')\n    end\n\n    it 'succeeds when deserializer is aware of the referenced type' do\n      obj = type.create(32)\n      write(obj)\n      expect(loaders.find_loader(nil)).to receive(:load).with(:type, 'mytype').and_return(type)\n      expect(read).to eql(obj)\n    end\n  end\n\n  context 'When debugging' do\n    let(:debug_io) { StringIO.new }\n    let(:writer) { reader_class.new(io, { :debug_io => debug_io, :tabulate => false, :verbose => true }) }\n\n    it 'can write and read an AST expression' do\n      expr = parse(<<-CODE)\n        $rootgroup = $os['family'] ? {\n            'Solaris'          => 'wheel',\n            /(Darwin|FreeBSD)/ => 'wheel',\n            default            => 'root',\n        }\n\n        file { '/etc/passwd':\n          ensure => file,\n          owner  => 'root',\n          group  => $rootgroup,\n        }\n      CODE\n      write(expr)\n      expr2 = read\n      expect(dumper.dump(expr)).to eq(dumper.dump(expr2))\n    end\n  end\nend\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/serialization/to_from_hr_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\nmodule Serialization\n  describe 'Passing values through ToDataConverter/FromDataConverter' do\n  let(:dumper) { Model::ModelTreeDumper.new }\n  let(:io) { StringIO.new }\n  let(:parser) { Parser::EvaluatingParser.new }\n  let(:env) { Puppet::Node::Environment.create(:testing, []) }\n  let(:loaders) { Puppet::Pops::Loaders.new(env) }\n  let(:loader) { loaders.find_loader(nil) }\n  let(:to_converter) { ToDataConverter.new(:rich_data => true) }\n  let(:from_converter) { FromDataConverter.new(:loader => loader) }\n\n  before(:each) do\n    Puppet.lookup(:environments).clear_all\n    Puppet.push_context(:loaders => loaders, :current_environment => env)\n  end\n\n  after(:each) do\n    Puppet.pop_context\n  end\n\n  def write(value)\n    io.reopen\n    value = to_converter.convert(value)\n    expect(Types::TypeFactory.data).to be_instance(value)\n    io << [value].to_json\n    io.rewind\n  end\n\n  def write_raw(value)\n    io.reopen\n    expect(Types::TypeFactory.data).to be_instance(value)\n    io << [value].to_json\n    io.rewind\n  end\n\n  def read\n    from_converter.convert(::JSON.parse(io.read)[0])\n  end\n\n  def parse(string)\n    parser.parse_string(string, '/home/tester/experiments/manifests/init.pp')\n  end\n\n  context 'can write and read a' do\n    it 'String' do\n      val = 'the value'\n      write(val)\n      val2 = read\n      expect(val2).to be_a(String)\n      expect(val2).to eql(val)\n    end\n\n    it 'Integer' do\n      val = 32\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Integer)\n      expect(val2).to eql(val)\n    end\n\n    it 'Float' do\n      val = 32.45\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Float)\n      expect(val2).to eql(val)\n    end\n\n    it 'true' do\n      val = true\n      write(val)\n      val2 = read\n      expect(val2).to be_a(TrueClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'false' do\n      val = false\n      write(val)\n      val2 = read\n      expect(val2).to be_a(FalseClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'nil' do\n      val = nil\n      write(val)\n      val2 = read\n      expect(val2).to be_a(NilClass)\n      expect(val2).to eql(val)\n    end\n\n    it 'Regexp' do\n      val = /match me/\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Regexp)\n      expect(val2).to eql(val)\n    end\n\n    it 'Sensitive' do\n      sval = 'the sensitive value'\n      val = Types::PSensitiveType::Sensitive.new(sval)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PSensitiveType::Sensitive)\n      expect(val2.unwrap).to eql(sval)\n    end\n\n    it 'Timespan' do\n      val = Time::Timespan.from_fields(false, 3, 12, 40, 31, 123)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Time::Timespan)\n      expect(val2).to eql(val)\n    end\n\n    it 'Timestamp' do\n      val = Time::Timestamp.now\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Time::Timestamp)\n      expect(val2).to eql(val)\n    end\n\n    it 'Version' do\n      # It does succeed on rare occasions, so we need to repeat\n      val = SemanticPuppet::Version.parse('1.2.3-alpha2')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(SemanticPuppet::Version)\n      expect(val2).to eql(val)\n    end\n\n    it 'VersionRange' do\n      # It does succeed on rare occasions, so we need to repeat\n      val = SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(SemanticPuppet::VersionRange)\n      expect(val2).to eql(val)\n    end\n\n    it 'Binary' do\n      val = Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PBinaryType::Binary)\n      expect(val2).to eql(val)\n    end\n\n    it 'ACII_8BIT String as Binary' do\n      val = Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      strval = val.binary_buffer\n      write(strval)\n      val2 = read\n      expect(val2).to be_a(Types::PBinaryType::Binary)\n      expect(val2).to eql(val)\n    end\n\n    it 'URI' do\n      val = URI('http://bob:ewing@dallas.example.com:8080/oil/baron?crude=cash#leftovers')\n      write(val)\n      val2 = read\n      expect(val2).to be_a(URI)\n      expect(val2).to eql(val)\n    end\n\n    it 'Sensitive with rich data' do\n      sval = Time::Timestamp.now\n      val = Types::PSensitiveType::Sensitive.new(sval)\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Types::PSensitiveType::Sensitive)\n      expect(val2.unwrap).to be_a(Time::Timestamp)\n      expect(val2.unwrap).to eql(sval)\n    end\n\n    it 'Hash with Symbol keys' do\n      val = { :one => 'one', :two => 'two' }\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Hash)\n      expect(val2).to eql(val)\n    end\n\n    it 'Hash with Integer keys' do\n      val = { 1 => 'one', 2 => 'two' }\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Hash)\n      expect(val2).to eql(val)\n    end\n\n    it 'A Hash that references itself' do\n      val = {}\n      val['myself'] = val\n      write(val)\n      val2 = read\n      expect(val2).to be_a(Hash)\n      expect(val2['myself']).to equal(val2)\n    end\n  end\n\n  context 'can write and read' do\n    include_context 'types_setup'\n\n    all_types.each do |t|\n      it \"the default for type #{t.name}\" do\n        val = t::DEFAULT\n        write(val)\n        val2 = read\n        expect(val2).to be_a(t)\n        expect(val2).to eql(val)\n      end\n    end\n\n    context 'a parameterized' do\n      it 'String' do\n        val = Types::TypeFactory.string(Types::TypeFactory.range(1, :default))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PStringType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Regex' do\n        val = Types::TypeFactory.regexp(/foo/)\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PRegexpType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Collection' do\n        val = Types::TypeFactory.collection(Types::TypeFactory.range(0, 20))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PCollectionType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Array' do\n        val = Types::TypeFactory.array_of(Types::TypeFactory.integer, Types::TypeFactory.range(0, 20))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PArrayType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Hash' do\n        val = Types::TypeFactory.hash_kv(Types::TypeFactory.string, Types::TypeFactory.integer, Types::TypeFactory.range(0, 20))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PHashType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Variant' do\n        val = Types::TypeFactory.variant(Types::TypeFactory.string, Types::TypeFactory.range(1, :default))\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PVariantType)\n        expect(val2).to eql(val)\n      end\n\n      it 'Object' do\n        val = Types::TypeParser.singleton.parse('Pcore::StringType', loader)\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Types::PObjectType)\n        expect(val2).to eql(val)\n      end\n\n      context 'ObjectType' do\n        let(:type) do\n          Types::PObjectType.new({\n            'name' => 'MyType',\n            'type_parameters' => {\n              'x' => Types::PIntegerType::DEFAULT\n            },\n            'attributes' => {\n              'x' => Types::PIntegerType::DEFAULT\n            }\n          })\n        end\n\n        it 'with preserved parameters' do\n          val = type.create(34)._pcore_type\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Types::PObjectTypeExtension)\n          expect(val2).to eql(val)\n        end\n\n        it 'with POjbectTypeExtension type being converted' do\n          val = { 'ObjectExtension' => type.create(34) }\n          expect(to_converter.convert(val))\n            .to eq({\"ObjectExtension\"=>{\"__ptype\"=>\"MyType\", \"x\"=>34}})\n        end\n      end\n    end\n\n    it 'Array of rich data' do\n      # Sensitive omitted because it doesn't respond to ==\n      val = [\n        Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n        Time::Timestamp.now,\n        SemanticPuppet::Version.parse('1.2.3-alpha2'),\n        SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n        Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      ]\n      write(val)\n      val2 = read\n      expect(val2).to eql(val)\n    end\n\n    it 'Hash of rich data' do\n      # Sensitive omitted because it doesn't respond to ==\n      val = {\n        'duration' => Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n        'time' => Time::Timestamp.now,\n        'version' => SemanticPuppet::Version.parse('1.2.3-alpha2'),\n        'range' => SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n        'binary' => Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n      }\n      write(val)\n      val2 = read\n      expect(val2).to eql(val)\n    end\n\n    context 'an AST model' do\n      it \"Locator\" do\n        val = Parser::Locator::Locator19.new('here is some text', '/tmp/foo', [5])\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Parser::Locator::Locator19)\n        expect(val2).to eql(val)\n      end\n\n      it 'nested Expression' do\n        expr = parse(<<-CODE)\n          $rootgroup = $os['family'] ? {\n              'Solaris'          => 'wheel',\n              /(Darwin|FreeBSD)/ => 'wheel',\n              default            => 'root',\n          }\n\n          file { '/etc/passwd':\n            ensure => file,\n            owner  => 'root',\n            group  => $rootgroup,\n          }\n        CODE\n        write(expr)\n        expr2 = read\n        expect(dumper.dump(expr)).to eq(dumper.dump(expr2))\n      end\n    end\n\n    context 'PuppetObject' do\n      before(:each) do\n        class DerivedArray < Array\n          include Types::PuppetObject\n\n          def self._pcore_type\n            @type\n          end\n\n          def self.register_ptype(loader, ir)\n            @type = Pcore.create_object_type(loader, ir, DerivedArray, 'DerivedArray', nil, 'values' => Types::PArrayType::DEFAULT)\n              .resolve(loader)\n          end\n\n          def initialize(values)\n            concat(values)\n          end\n\n          def values\n            Array.new(self)\n          end\n        end\n\n        class DerivedHash < Hash\n          include Types::PuppetObject\n\n          def self._pcore_type\n            @type\n          end\n\n          def self.register_ptype(loader, ir)\n            @type = Pcore.create_object_type(loader, ir, DerivedHash, 'DerivedHash', nil, '_pcore_init_hash' => Types::PHashType::DEFAULT)\n              .resolve(loader)\n          end\n\n          def initialize(_pcore_init_hash)\n            merge!(_pcore_init_hash)\n          end\n\n          def _pcore_init_hash\n            result = {}\n            result.merge!(self)\n            result\n          end\n        end\n      end\n\n      after(:each) do\n        x = Puppet::Pops::Serialization\n        x.send(:remove_const, :DerivedArray) if x.const_defined?(:DerivedArray)\n        x.send(:remove_const, :DerivedHash) if x.const_defined?(:DerivedHash)\n      end\n\n      it 'derived from Array' do\n        DerivedArray.register_ptype(loader, loaders.implementation_registry)\n\n        # Sensitive omitted because it doesn't respond to ==\n        val = DerivedArray.new([\n          Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n          Time::Timestamp.now,\n          SemanticPuppet::Version.parse('1.2.3-alpha2'),\n          SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n          Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n        ])\n        write(val)\n        val2 = read\n        expect(val2).to eql(val)\n      end\n\n      it 'derived from Hash' do\n        DerivedHash.register_ptype(loader, loaders.implementation_registry)\n\n        # Sensitive omitted because it doesn't respond to ==\n        val = DerivedHash.new({\n          'duration' => Time::Timespan.from_fields(false, 3, 12, 40, 31, 123),\n          'time' => Time::Timestamp.now,\n          'version' => SemanticPuppet::Version.parse('1.2.3-alpha2'),\n          'range' => SemanticPuppet::VersionRange.parse('>=1.2.3-alpha2 <1.2.4'),\n          'binary' => Types::PBinaryType::Binary.from_base64('w5ZzdGVuIG1lZCByw7ZzdGVuCg==')\n        })\n        write(val)\n        val2 = read\n        expect(val2).to eql(val)\n      end\n    end\n  end\n\n  context 'deserializing an instance whose Object type was serialized by reference' do\n    let(:to_converter) { ToDataConverter.new(:type_by_reference => true, :rich_data => true) }\n    let(:type) do\n      Types::PObjectType.new({\n        'name' => 'MyType',\n        'attributes' => {\n          'x' => Types::PIntegerType::DEFAULT\n        }\n      })\n    end\n\n    context 'fails when deserializer is unaware of the referenced type' do\n      it 'fails by default' do\n        write(type.create(32))\n\n        # Should fail since no loader knows about 'MyType'\n        expect{ read }.to raise_error(Puppet::Error, 'No implementation mapping found for Puppet Type MyType')\n      end\n\n      it 'succeeds and produces a hash when a read __ptype key is something other than a string or a hash' do\n        # i.e. this is for content that is not produced by ToDataHash - writing on-the-wire-format directly\n        # to test that it does not crash.\n        write_raw({'__ptype' => 10, 'x'=> 32})\n        expect(read).to eql({'__ptype'=>10, 'x'=>32})\n      end\n\n      context \"succeeds but produces an rich_type hash when deserializer has 'allow_unresolved' set to true\" do\n        let(:from_converter) { FromDataConverter.new(:allow_unresolved => true) }\n        it do\n          write(type.create(32))\n          expect(read).to eql({'__ptype'=>'MyType', 'x'=>32})\n        end\n      end\n    end\n\n    it 'succeeds when deserializer is aware of the referenced type' do\n      obj = type.create(32)\n      write(obj)\n      expect(loaders.find_loader(nil)).to receive(:load).with(:type, 'mytype').and_return(type)\n      expect(read).to eql(obj)\n    end\n  end\n\n  context 'with rich_data set to false' do\n    before :each do\n      # strict mode off so behavior is not stubbed\n      Puppet[:strict] = :warning\n    end\n    let(:to_converter) { ToDataConverter.new(:message_prefix => 'Test Hash', :rich_data => false) }\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n    it 'A Hash with Symbol keys is converted to hash with String keys with warning' do\n      val = { :one => 'one', :two => 'two' }\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Hash)\n        expect(val2).to eql({ 'one' => 'one', 'two' => 'two' })\n      end\n      expect(warnings).to eql([\n        \"Test Hash contains a hash with a Symbol key. It will be converted to the String 'one'\",\n        \"Test Hash contains a hash with a Symbol key. It will be converted to the String 'two'\"])\n    end\n\n    it 'A Hash with Version keys is converted to hash with String keys with warning' do\n      val = { SemanticPuppet::Version.parse('1.0.0') => 'one', SemanticPuppet::Version.parse('2.0.0') => 'two' }\n      Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n        write(val)\n        val2 = read\n        expect(val2).to be_a(Hash)\n        expect(val2).to eql({ '1.0.0' => 'one', '2.0.0' => 'two' })\n      end\n      expect(warnings).to eql([\n        \"Test Hash contains a hash with a SemanticPuppet::Version key. It will be converted to the String '1.0.0'\",\n        \"Test Hash contains a hash with a SemanticPuppet::Version key. It will be converted to the String '2.0.0'\"])\n      end\n\n    it 'A Hash with rich data is not converted and raises error by default' do\n      Puppet[:strict] = :error\n      expect do\n        val = { SemanticPuppet::Version.parse('1.0.0') => 'one', SemanticPuppet::Version.parse('2.0.0') => 'two' }\n        write(val)\n      end.to raise_error(/Evaluation Error: Test Hash contains a hash with a SemanticPuppet::Version key. It will be converted to the String '1.0.0'/)\n    end\n\n    context 'and symbol_as_string is set to true' do\n      let(:to_converter) { ToDataConverter.new(:rich_data => false, :symbol_as_string => true) }\n\n      it 'A Hash with Symbol keys is silently converted to hash with String keys' do\n        val = { :one => 'one', :two => 'two' }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ 'one' => 'one', 'two' => 'two' })\n        end\n        expect(warnings).to be_empty\n      end\n\n      it 'A Hash with Symbol values is silently converted to hash with String values' do\n        val = { 'one' => :one, 'two' => :two  }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ 'one' => 'one', 'two' => 'two' })\n        end\n        expect(warnings).to be_empty\n      end\n\n      it 'A Hash with default values will have the values converted to string with a warning' do\n        val = { 'key' => :default  }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ 'key' => 'default' })\n        end\n        expect(warnings).to eql([\"['key'] contains the special value default. It will be converted to the String 'default'\"])\n      end\n    end\n  end\n\n  context 'with rich_data is set to true' do\n    let(:to_converter) { ToDataConverter.new(:message_prefix => 'Test Hash', :rich_data => true) }\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n\n    context 'and symbol_as_string is set to true' do\n      let(:to_converter) { ToDataConverter.new(:rich_data => true, :symbol_as_string => true) }\n\n      it 'A Hash with Symbol keys is silently converted to hash with String keys' do\n        val = { :one => 'one', :two => 'two' }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ 'one' => 'one', 'two' => 'two' })\n        end\n        expect(warnings).to be_empty\n      end\n\n      it 'A Hash with Symbol values is silently converted to hash with String values' do\n        val = { 'one' => :one, 'two' => :two  }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ 'one' => 'one', 'two' => 'two' })\n        end\n        expect(warnings).to be_empty\n      end\n\n      it 'A Hash with __ptype, __pvalue keys will not be taken as a pcore meta tag' do\n        val = { '__ptype' => 42, '__pvalue' => 43  }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ '__ptype' => 42, '__pvalue' => 43 })\n        end\n        expect(warnings).to be_empty\n      end\n\n      it 'A Hash with default values will not loose type information' do\n        val = { 'key' => :default  }\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          write(val)\n          val2 = read\n          expect(val2).to be_a(Hash)\n          expect(val2).to eql({ 'key' => :default })\n        end\n        expect(warnings).to be_empty\n      end\n    end\n  end\n\n  context 'with local_reference set to false' do\n    let(:to_converter) { ToDataConverter.new(:local_reference => false) }\n\n    it 'A self referencing value will trigger an endless recursion error' do\n      val = {}\n      val['myself'] = val\n      expect { write(val) }.to raise_error(/Endless recursion detected when attempting to serialize value of class Hash/)\n    end\n  end\n\n  context 'will fail when' do\n    it 'the value of a type description is something other than a String or a Hash' do\n      expect do\n        from_converter.convert({ '__ptype' => { '__ptype' => 'Pcore::TimestampType', '__pvalue' => 12345 }})\n      end.to raise_error(/Cannot create a Pcore::TimestampType from a Integer/)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/serialization/to_stringified_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\ndescribe 'ToStringifiedConverter' do\n  include PuppetSpec::Compiler\n\n  after(:all) { Puppet::Pops::Loaders.clear }\n\n  let(:env) { Puppet::Node::Environment.create(:testing, []) }\n  let(:loaders) { Puppet::Pops::Loaders.new(env) }\n  let(:node) { Puppet::Node.new(env, environment: env) }\n\n  def transform(v)\n    Puppet::Pops::Serialization::ToStringifiedConverter.convert(v)\n  end\n\n  def eval_transform(source)\n    Puppet.override(:current_environment => env, :loaders => loaders) do\n      transform(evaluate(source: source, node: node))\n    end\n  end\n\n  it 'converts undef to to nil' do\n    expect(transform(nil)).to be_nil\n    expect(eval_transform('undef')).to be_nil\n  end\n\n  it \"converts literal default to the string 'default'\" do\n    expect(transform(:default)).to eq('default')\n    expect(eval_transform('default')).to eq('default')\n  end\n\n  it \"does not convert an integer\" do\n    expect(transform(42)).to eq(42)\n  end\n\n  it \"does not convert a float\" do\n    expect(transform(3.14)).to eq(3.14)\n  end\n\n  it \"does not convert a boolean\" do\n    expect(transform(true)).to be(true)\n    expect(transform(false)).to be(false)\n  end\n\n  it \"does not convert a string (that is free from encoding issues)\" do\n    expect(transform(\"hello\")).to eq(\"hello\")\n  end\n\n  it \"converts a regexp to a string\" do\n    expect(transform(/this|that.*/)).to eq(\"/this|that.*/\")\n    expect(eval_transform('/this|that.*/')).to eq(\"/this|that.*/\")\n  end\n\n  it 'handles a string with an embedded single quote' do\n    expect(transform(\"ta'phoenix\")).to eq(\"ta'phoenix\")\n  end\n\n  it 'handles a string with embedded double quotes' do\n    expect(transform('he said \"hi\"')).to eq(\"he said \\\"hi\\\"\")\n  end\n\n  it 'converts a user defined object to its string representation including attributes' do\n    result = evaluate(code: \"type Car = Object[attributes=>{regnbr => String}]\", source: \"Car(abc123)\")\n    expect(transform(result)).to eq(\"Car({'regnbr' => 'abc123'})\")\n  end\n\n  it 'converts a Deferred object to its string representation including attributes' do\n    expect(eval_transform(\"Deferred(func, [1,2,3])\")).to eq(\"Deferred({'name' => 'func', 'arguments' => [1, 2, 3]})\")\n  end\n\n  it 'converts a Sensitive to type + redacted plus id' do\n    sensitive = evaluate(source: \"Sensitive('hush')\")\n    id = sensitive.object_id\n    expect(transform(sensitive)).to eq(\"#<Sensitive [value redacted]:#{id}>\")\n  end\n\n  it 'converts a Timestamp to String' do\n    expect(eval_transform(\"Timestamp('2018-09-03T19:45:33.697066000 UTC')\")).to eq(\"2018-09-03T19:45:33.697066000 UTC\")\n  end\n\n  it 'does not convert an array' do\n    expect(transform([1,2,3])).to eq([1,2,3])\n  end\n\n  it 'converts the content of an array - for example a Sensitive value' do\n    sensitive = evaluate(source: \"Sensitive('hush')\")\n    id = sensitive.object_id\n    expect(transform([sensitive])).to eq([\"#<Sensitive [value redacted]:#{id}>\"])\n  end\n\n  it 'converts the content of a hash - for example a Sensitive value' do\n    sensitive = evaluate(source: \"Sensitive('hush')\")\n    id = sensitive.object_id\n    expect(transform({'x' => sensitive})).to eq({'x' => \"#<Sensitive [value redacted]:#{id}>\"})\n  end\n\n  it 'does not convert a hash' do\n    expect(transform({'a' => 10, 'b' => 20})).to eq({'a' => 10, 'b' => 20})\n  end\n\n  it 'converts non Data compliant hash key to string' do\n    expect(transform({['funky', 'key'] => 10})).to eq({'[\"funky\", \"key\"]' => 10})\n  end\n\n  it 'converts reserved __ptype hash key to different string' do\n    expect(transform({'__ptype' => 10})).to eq({'reserved key: __ptype' => 10})\n  end\n\n  it 'converts reserved __pvalue hash key to different string' do\n    expect(transform({'__pvalue' => 10})).to eq({'reserved key: __pvalue' => 10})\n  end\n\n  it 'converts a Binary to Base64 string' do\n    expect(eval_transform(\"Binary('hello', '%s')\")).to eq(\"aGVsbG8=\")\n  end\n\n  it 'converts an ASCII-8BIT String to Base64 String' do\n    binary_string = \"\\x02\\x03\\x04hello\".force_encoding('ascii-8bit')\n    expect(transform(binary_string)).to eq(\"AgMEaGVsbG8=\")\n  end\n\n  it 'converts Runtime (alien) object to its to_s' do\n    class Alien\n    end\n    an_alien = Alien.new\n    expect(transform(an_alien)).to eq(an_alien.to_s)\n  end\n\n  it 'converts a runtime Symbol to String' do\n    expect(transform(:symbolic)).to eq(\"symbolic\")\n  end\n\n  it 'converts a datatype (such as Integer[0,10]) to its puppet source string form' do\n    expect(eval_transform(\"Integer[0,100]\")).to eq(\"Integer[0, 100]\")\n  end\n\n  it 'converts a user defined datatype (such as Car) to its puppet source string form' do\n    result = evaluate(code: \"type Car = Object[attributes=>{regnbr => String}]\", source: \"Car\")\n    expect(transform(result)).to eq(\"Car\")\n  end\n\n  it 'converts a self referencing user defined datatype by using named references for cyclic entries' do\n    result = evaluate(code: \"type Tree = Array[Variant[String, Tree]]\", source: \"Tree\")\n    expect(transform(result)).to eq(\"Tree = Array[Variant[String, Tree]]\")\n  end\n\n  it 'converts illegal char sequence in an encoding to Unicode Illegal' do\n    invalid_sequence = [0xc2, 0xc2].pack(\"c*\").force_encoding(Encoding::UTF_8)\n    expect(transform(invalid_sequence)).to eq(\"��\")\n  end\n\n  it 'does not convert unassigned char - glyph is same for all unassigned' do\n    unassigned = [243, 176, 128, 128].pack(\"C*\").force_encoding(Encoding::UTF_8) \n    expect(transform(unassigned)).to eq(\"󰀀\")\n  end\n\n  it 'converts ProcessOutput objects to string' do\n    object = Puppet::Util::Execution::ProcessOutput.new('object', 0)\n    expect(transform(object)).to be_instance_of(String)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/time/timespan_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Time\ndescribe 'Timespan' do\n  include PuppetSpec::Compiler\n\n  let! (:simple) { Timespan.from_fields(false, 1, 3, 10, 11) }\n  let! (:all_fields_hash) { {'days' => 1, 'hours' => 7, 'minutes' => 10, 'seconds' => 11, 'milliseconds' => 123, 'microseconds' => 456, 'nanoseconds' => 789} }\n  let! (:complex) { Timespan.from_fields_hash(all_fields_hash) }\n\n  context 'can be created from a String' do\n    it 'using default format' do\n      expect(Timespan.parse('1-03:10:11')).to eql(simple)\n    end\n\n    it 'using explicit format' do\n      expect(Timespan.parse('1-7:10:11.123456789', '%D-%H:%M:%S.%N')).to eql(complex)\n    end\n\n    it 'using leading minus and explicit format' do\n      expect(Timespan.parse('-1-7:10:11.123456789', '%D-%H:%M:%S.%N')).to eql(-complex)\n    end\n\n    it 'using %H as the biggest quantity' do\n      expect(Timespan.parse('27:10:11', '%H:%M:%S')).to eql(simple)\n    end\n\n    it 'using %M as the biggest quantity' do\n      expect(Timespan.parse('1630:11', '%M:%S')).to eql(simple)\n    end\n\n    it 'using %S as the biggest quantity' do\n      expect(Timespan.parse('97811', '%S')).to eql(simple)\n    end\n\n    it 'where biggest quantity is not frist' do\n      expect(Timespan.parse('11:1630', '%S:%M')).to eql(simple)\n    end\n\n    it 'raises an error when using %L as the biggest quantity' do\n      expect { Timespan.parse('123', '%L') }.to raise_error(ArgumentError, /denotes fractions and must be used together with a specifier of higher magnitude/)\n    end\n\n    it 'raises an error when using %N as the biggest quantity' do\n      expect { Timespan.parse('123', '%N') }.to raise_error(ArgumentError, /denotes fractions and must be used together with a specifier of higher magnitude/)\n    end\n\n    it 'where %L is treated as fractions of a second' do\n      expect(Timespan.parse('0.4', '%S.%L')).to eql(Timespan.from_fields(false, 0, 0, 0, 0, 400))\n    end\n\n    it 'where %N is treated as fractions of a second' do\n      expect(Timespan.parse('0.4', '%S.%N')).to eql(Timespan.from_fields(false, 0, 0, 0, 0, 400))\n    end\n  end\n\n  context 'when presented as a String' do\n    it 'uses default format for #to_s' do\n      expect(simple.to_s).to eql('1-03:10:11.0')\n    end\n\n    context 'using a format' do\n      it 'produces a string containing all components' do\n        expect(complex.format('%D-%H:%M:%S.%N')).to eql('1-07:10:11.123456789')\n      end\n\n      it 'produces a literal % for %%' do\n        expect(complex.format('%D%%%H:%M:%S')).to eql('1%07:10:11')\n      end\n\n      it 'produces a leading dash for negative instance' do\n        expect((-complex).format('%D-%H:%M:%S')).to eql('-1-07:10:11')\n      end\n\n      it 'produces a string without trailing zeros for %-N' do\n        expect(Timespan.parse('2.345', '%S.%N').format('%-S.%-N')).to eql('2.345')\n      end\n\n      it 'produces a string with trailing zeros for %N' do\n        expect(Timespan.parse('2.345', '%S.%N').format('%-S.%N')).to eql('2.345000000')\n      end\n\n      it 'produces a string with trailing zeros for %0N' do\n        expect(Timespan.parse('2.345', '%S.%N').format('%-S.%0N')).to eql('2.345000000')\n      end\n\n      it 'produces a string with trailing spaces for %_N' do\n        expect(Timespan.parse('2.345', '%S.%N').format('%-S.%_N')).to eql('2.345      ')\n      end\n    end\n  end\n\n  context 'when converted to a hash' do\n    it 'produces a hash with all numeric keys' do\n      hash = complex.to_hash\n      expect(hash).to eql(all_fields_hash)\n    end\n\n    it 'produces a compact hash with seconds and nanoseconds for #to_hash(true)' do\n      hash = complex.to_hash(true)\n      expect(hash).to eql({'seconds' => 112211, 'nanoseconds' => 123456789})\n    end\n\n    context 'from a negative value' do\n      it 'produces a hash with all numeric keys and negative = true' do\n        hash = (-complex).to_hash\n        expect(hash).to eql(all_fields_hash.merge('negative' => true))\n      end\n\n      it 'produces a compact hash with negative seconds and negative nanoseconds for #to_hash(true)' do\n        hash = (-complex).to_hash(true)\n        expect(hash).to eql({'seconds' => -112211, 'nanoseconds' => -123456789})\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/time/timestamp_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\n  module Time\n    describe 'Timestamp' do\n      it 'Does not loose microsecond precision when converted to/from String' do\n        ts = Timestamp.new(1495789430910161286)\n        expect(Timestamp.parse(ts.to_s)).to eql(ts)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/types/class_loader_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe 'the Puppet::Pops::Types::ClassLoader' do\n  it 'should produce path alternatives for CamelCase classes' do\n    expected_paths = ['puppet_x/some_thing', 'puppetx/something']\n    # path_for_name method is private\n    expect(Puppet::Pops::Types::ClassLoader.send(:paths_for_name, ['PuppetX', 'SomeThing'])).to include(*expected_paths)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/types/deferred_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\n  module Types\n    describe 'Deferred type' do\n      context 'when used in Puppet expressions' do\n        include PuppetSpec::Compiler\n        it 'is equal to itself only' do\n          expect(eval_and_collect_notices(<<-CODE)).to eq(%w(true true false false))\n            $t = Deferred\n            notice(Deferred =~ Type[Deferred])\n            notice(Deferred == Deferred)\n            notice(Deferred < Deferred)\n            notice(Deferred > Deferred)\n            CODE\n        end\n\n        context 'a Deferred instance' do\n          it 'can be created using positional arguments' do\n            code = <<-CODE\n              $o = Deferred('michelangelo', [1,2,3])\n              notice($o)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\n              \"Deferred({'name' => 'michelangelo', 'arguments' => [1, 2, 3]})\"\n            ])\n          end\n\n          it 'can be created using named arguments' do\n            code = <<-CODE\n              $o = Deferred(name =>'michelangelo', arguments => [1,2,3])\n              notice($o)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\n              \"Deferred({'name' => 'michelangelo', 'arguments' => [1, 2, 3]})\"\n            ])\n          end\n\n          it 'is inferred to have the type Deferred' do\n            pending 'bug in type() function outputs the entire pcore definition'\n            code = <<-CODE\n              $o = Deferred('michelangelo', [1,2,3])\n              notice(type($o))\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\n              \"Deferred\"\n            ])\n          end\n\n          it 'exposes name' do\n            code = <<-CODE\n              $o = Deferred(name =>'michelangelo', arguments => [1,2,3])\n              notice($o.name)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\"michelangelo\"])\n          end\n\n          it 'exposes arguments' do\n            code = <<-CODE\n              $o = Deferred(name =>'michelangelo', arguments => [1,2,3])\n              notice($o.arguments)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\"[1, 2, 3]\"])\n          end\n\n          it 'is an instance of its inferred type' do\n            code = <<-CODE\n              $o = Deferred(name =>'michelangelo', arguments => [1,2,3])\n              notice($o =~ type($o))\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['true'])\n          end\n\n          it 'is an instance of Deferred' do\n            code = <<-CODE\n              $o = Deferred(name =>'michelangelo', arguments => [1,2,3])\n              notice($o =~ Deferred)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['true'])\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/types/error_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\n  module Types\n    describe 'Error type' do\n      context 'when used in Puppet expressions' do\n        include PuppetSpec::Compiler\n        it 'is equal to itself only' do\n          expect(eval_and_collect_notices(<<-CODE)).to eq(%w(true true false false))\n            $t = Error\n            notice(Error =~ Type[Error])\n            notice(Error == Error)\n            notice(Error < Error)\n            notice(Error > Error)\n            CODE\n        end\n\n        context \"when parameterized\" do\n          it 'is equal other types with the same parameterization' do\n            code = <<-CODE\n              notice(Error['puppet/error'] == Error['puppet/error', default])\n              notice(Error['puppet/error', 'ouch'] == Error['puppet/error', 'ouch'])\n              notice(Error['puppet/error', 'ouch'] != Error['puppet/error', 'ouch!'])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(%w(true true true))\n          end\n\n          it 'is assignable from more qualified types' do\n            expect(eval_and_collect_notices(<<-CODE)).to eq(%w(true true true))\n              notice(Error > Error['puppet/error'])\n              notice(Error['puppet/error'] > Error['puppet/error', 'ouch'])\n              notice(Error['puppet/error', default] > Error['puppet/error', 'ouch'])\n              CODE\n          end\n\n          it 'is not assignable unless kind is assignable' do\n            expect(eval_and_collect_notices(<<-CODE)).to eq(%w(true false true false true false true))\n              notice(Error[/a/] > Error['hah'])\n              notice(Error[/a/] > Error['hbh'])\n              notice(Error[Enum[a,b,c]] > Error[a])\n              notice(Error[Enum[a,b,c]] > Error[d])\n              notice(Error[Pattern[/a/, /b/]] > Error[a])\n              notice(Error[Pattern[/a/, /b/]] > Error[c])\n              notice(Error[Pattern[/a/, /b/]] > Error[Enum[a, b]])\n              CODE\n          end\n\n          it 'presents parsable string form' do\n            code = <<-CODE\n              notice(Error['a'])\n              notice(Error[/a/])\n              notice(Error[Enum['a', 'b']])\n              notice(Error[Pattern[/a/, /b/]])\n              notice(Error['a', default])\n              notice(Error[/a/, default])\n              notice(Error[Enum['a', 'b'], default])\n              notice(Error[Pattern[/a/, /b/], default])\n              notice(Error[default,'a'])\n              notice(Error[default,/a/])\n              notice(Error[default,Enum['a', 'b']])\n              notice(Error[default,Pattern[/a/, /b/]])\n              notice(Error['a','a'])\n              notice(Error[/a/,/a/])\n              notice(Error[Enum['a', 'b'],Enum['a', 'b']])\n              notice(Error[Pattern[/a/, /b/],Pattern[/a/, /b/]])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\n              \"Error['a']\",\n              'Error[/a/]',\n              \"Error[Enum['a', 'b']]\",\n              \"Error[Pattern[/a/, /b/]]\",\n              \"Error['a']\",\n              'Error[/a/]',\n              \"Error[Enum['a', 'b']]\",\n              \"Error[Pattern[/a/, /b/]]\",\n              \"Error[default, 'a']\",\n              'Error[default, /a/]',\n              \"Error[default, Enum['a', 'b']]\",\n              \"Error[default, Pattern[/a/, /b/]]\",\n              \"Error['a', 'a']\",\n              'Error[/a/, /a/]',\n              \"Error[Enum['a', 'b'], Enum['a', 'b']]\",\n              \"Error[Pattern[/a/, /b/], Pattern[/a/, /b/]]\",\n            ])\n          end\n        end\n\n        context 'an Error instance' do\n          it 'can be created using positional arguments' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppet/error', {'detail' => 'val'}, 'OOPS')\n              notice($o)\n              notice(type($o))\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\n              \"Error({'msg' => 'bad things happened', 'kind' => 'puppet/error', 'details' => {'detail' => 'val'}, 'issue_code' => 'OOPS'})\",\n              \"Error['puppet/error', 'OOPS']\"\n            ])\n          end\n\n          it 'can be created using named arguments' do\n            code = <<-CODE\n              $o = Error(msg => 'Sorry, not implemented', kind => 'puppet/error', issue_code => 'NOT_IMPLEMENTED')\n              notice($o)\n              notice(type($o))\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\n              \"Error({'msg' => 'Sorry, not implemented', 'kind' => 'puppet/error', 'issue_code' => 'NOT_IMPLEMENTED'})\",\n              \"Error['puppet/error', 'NOT_IMPLEMENTED']\"\n            ])\n          end\n\n          it 'exposes message' do\n            code = <<-CODE\n              $o = Error(msg => 'Sorry, not implemented', kind => 'puppet/error', issue_code => 'NOT_IMPLEMENTED')\n              notice($o.message)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\"Sorry, not implemented\"])\n          end\n\n          it 'exposes kind' do\n            code = <<-CODE\n              $o = Error(msg => 'Sorry, not implemented', kind => 'puppet/error', issue_code => 'NOT_IMPLEMENTED')\n              notice($o.kind)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\"puppet/error\"])\n          end\n\n          it 'exposes issue_code' do\n            code = <<-CODE\n              $o = Error(msg => 'Sorry, not implemented', kind => 'puppet/error', issue_code => 'NOT_IMPLEMENTED')\n              notice($o.issue_code)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\"NOT_IMPLEMENTED\"])\n          end\n\n          it 'exposes details' do\n            code = <<-CODE\n              $o = Error(msg => 'Sorry, not implemented', kind => 'puppet/error', details => { 'detailk' => 'detailv' })\n              notice($o.details)\n              CODE\n            expect(eval_and_collect_notices(code)).to eq([\"{detailk => detailv}\"])\n          end\n\n          it 'is an instance of its inferred type' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppet/error')\n              notice($o =~ type($o))\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['true'])\n          end\n\n          it 'is an instance of Error with matching kind' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppet/error')\n              notice($o =~ Error[/puppet\\\\/error/])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['true'])\n          end\n\n          it 'is an instance of Error with matching issue_code' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppet/error', {}, 'FEE')\n              notice($o =~ Error[default, 'FEE'])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['true'])\n          end\n\n          it 'is an instance of Error with matching kind and issue_code' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppet/error', {}, 'FEE')\n              notice($o =~ Error['puppet/error', 'FEE'])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['true'])\n          end\n\n          it 'is not an instance of Error unless kind matches' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppetlabs/error')\n              notice($o =~ Error[/puppet\\\\/error/])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['false'])\n          end\n\n          it 'is not an instance of Error unless issue_code matches' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppetlabs/error', {}, 'BAR')\n              notice($o =~ Error[default, 'FOO'])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['false'])\n          end\n\n          it 'is not an instance of Error unless both kind and issue is a match' do\n            code = <<-CODE\n              $o = Error('bad things happened', 'puppet/error', {}, 'FEE')\n              notice($o =~ Error['puppetlabs/error', 'FEE'])\n              notice($o =~ Error['puppet/error', 'FUM'])\n              CODE\n            expect(eval_and_collect_notices(code)).to eq(['false', 'false'])\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/types/iterable_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops::Types\ndescribe 'The iterable support' do\n\n  [\n    0,\n    5,\n    (3..10),\n    %w(a b c),\n    {'a'=>2},\n    'hello',\n    PIntegerType.new(1, 4),\n    PEnumType.new(%w(yes no))\n  ].each do |obj|\n    it \"should consider instances of #{obj.class.name} to be Iterable\" do\n      expect(PIterableType::DEFAULT.instance?(obj)).to eq(true)\n    end\n\n    it \"should yield an Iterable instance when Iterable.on is called with a #{obj.class.name}\" do\n      expect(Iterable.on(obj)).to be_a(Iterable)\n    end\n  end\n\n  {\n    -1 => 'a negative Integer',\n    5.times => 'an Enumerable',\n    PIntegerType.new(nil, nil) => 'an unbounded Integer type'\n  }.each_pair do |obj, desc|\n    it \"does not consider #{desc} to be Iterable\" do\n      expect(PIterableType::DEFAULT.instance?(obj)).to eq(false)\n    end\n\n    it \"does not yield an Iterable when Iterable.on is called with #{desc}\" do\n      expect(Iterable.on(obj)).to be_nil\n    end\n  end\n\n  context 'when testing assignability' do\n    iterable_types = [\n      PIntegerType::DEFAULT,\n      PStringType::DEFAULT,\n      PIterableType::DEFAULT,\n      PIteratorType::DEFAULT,\n      PCollectionType::DEFAULT,\n      PArrayType::DEFAULT,\n      PHashType::DEFAULT,\n      PTupleType::DEFAULT,\n      PStructType::DEFAULT,\n      PUnitType::DEFAULT\n    ]\n    iterable_types << PTypeType.new(PIntegerType.new(0, 10))\n    iterable_types << PTypeType.new(PEnumType.new(%w(yes no)))\n    iterable_types << PRuntimeType.new(:ruby, 'Puppet::Pops::Types::Iterator')\n    iterable_types << PVariantType.new(iterable_types.clone)\n\n    not_iterable_types = [\n      PAnyType::DEFAULT,\n      PBooleanType::DEFAULT,\n      PCallableType::DEFAULT,\n      PCatalogEntryType::DEFAULT,\n      PDefaultType::DEFAULT,\n      PFloatType::DEFAULT,\n      PClassType::DEFAULT,\n      PNotUndefType::DEFAULT,\n      PNumericType::DEFAULT,\n      POptionalType::DEFAULT,\n      PPatternType::DEFAULT,\n      PRegexpType::DEFAULT,\n      PResourceType::DEFAULT,\n      PRuntimeType::DEFAULT,\n      PScalarType::DEFAULT,\n      PScalarDataType::DEFAULT,\n      PTypeType::DEFAULT,\n      PUndefType::DEFAULT\n    ]\n    not_iterable_types << PTypeType.new(PIntegerType::DEFAULT)\n    not_iterable_types << PVariantType.new([iterable_types[0], not_iterable_types[0]])\n\n    iterable_types.each do |type|\n      it \"should consider #{type} to be assignable to Iterable type\" do\n        expect(PIterableType::DEFAULT.assignable?(type)).to eq(true)\n      end\n    end\n\n    not_iterable_types.each do |type|\n      it \"should not consider #{type} to be assignable to Iterable type\" do\n        expect(PIterableType::DEFAULT.assignable?(type)).to eq(false)\n      end\n    end\n\n    it \"should consider Type[Integer[0,5]] to be assignable to Iterable[Integer[0,5]]\" do\n      expect(PIterableType.new(PIntegerType.new(0,5)).assignable?(PTypeType.new(PIntegerType.new(0,5)))).to eq(true)\n    end\n\n    it \"should consider Type[Enum[yes,no]] to be assignable to Iterable[Enum[yes,no]]\" do\n      expect(PIterableType.new(PEnumType.new(%w(yes no))).assignable?(PTypeType.new(PEnumType.new(%w(yes no))))).to eq(true)\n    end\n\n    it \"should not consider Type[Enum[ok,fail]] to be assignable to Iterable[Enum[yes,no]]\" do\n      expect(PIterableType.new(PEnumType.new(%w(ok fail))).assignable?(PTypeType.new(PEnumType.new(%w(yes no))))).to eq(false)\n    end\n\n    it \"should not consider Type[String] to be assignable to Iterable[String]\" do\n      expect(PIterableType.new(PStringType::DEFAULT).assignable?(PTypeType.new(PStringType::DEFAULT))).to eq(false)\n    end\n  end\n\n  it 'does not wrap an Iterable in another Iterable' do\n    x = Iterable.on(5)\n    expect(Iterable.on(x)).to equal(x)\n  end\n\n  it 'produces a \"times\" iterable on integer' do\n    expect{ |b| Iterable.on(3).each(&b) }.to yield_successive_args(0,1,2)\n  end\n\n  it 'produces an iterable with element type Integer[0,X-1] for an iterable on an integer X' do\n    expect(Iterable.on(3).element_type).to eq(PIntegerType.new(0,2))\n  end\n\n  it 'produces a step iterable on an integer' do\n    expect{ |b| Iterable.on(8).step(3, &b) }.to yield_successive_args(0, 3, 6)\n  end\n\n  it 'produces a reverse iterable on an integer' do\n    expect{ |b| Iterable.on(5).reverse_each(&b) }.to yield_successive_args(4,3,2,1,0)\n  end\n\n  it 'produces an iterable on a integer range' do\n    expect{ |b| Iterable.on(2..7).each(&b) }.to yield_successive_args(2,3,4,5,6,7)\n  end\n\n  it 'produces an iterable with element type Integer[X,Y] for an iterable on an integer range (X..Y)' do\n    expect(Iterable.on(2..7).element_type).to eq(PIntegerType.new(2,7))\n  end\n\n  it 'produces an iterable on a character range' do\n    expect{ |b| Iterable.on('a'..'f').each(&b) }.to yield_successive_args('a', 'b', 'c', 'd', 'e', 'f')\n  end\n\n  it 'produces a step iterable on a range' do\n    expect{ |b| Iterable.on(1..5).step(2, &b) }.to yield_successive_args(1,3,5)\n  end\n\n  it 'produces a reverse iterable on a range' do\n    expect{ |b| Iterable.on(2..7).reverse_each(&b) }.to yield_successive_args(7,6,5,4,3,2)\n  end\n\n  it 'produces an iterable with element type String with a size constraint for an iterable on a character range' do\n    expect(Iterable.on('a'..'fe').element_type).to eq(PStringType.new(PIntegerType.new(1,2)))\n  end\n\n  it 'produces an iterable on a bounded Integer type' do\n    expect{ |b| Iterable.on(PIntegerType.new(2,7)).each(&b) }.to yield_successive_args(2,3,4,5,6,7)\n  end\n\n  it 'produces an iterable with element type Integer[X,Y] for an iterable on Integer[X,Y]' do\n    expect(Iterable.on(PIntegerType.new(2,7)).element_type).to eq(PIntegerType.new(2,7))\n  end\n\n  it 'produces an iterable on String' do\n    expect{ |b| Iterable.on('eat this').each(&b) }.to yield_successive_args('e', 'a', 't', ' ', 't', 'h', 'i', 's')\n  end\n\n  it 'produces an iterable with element type String[1,1] for an iterable created on a String' do\n    expect(Iterable.on('eat this').element_type).to eq(PStringType.new(PIntegerType.new(1,1)))\n end\n\n  it 'produces an iterable on Array' do\n    expect{ |b| Iterable.on([1,5,9]).each(&b) }.to yield_successive_args(1,5,9)\n  end\n\n  it 'produces an iterable with element type inferred from the array elements for an iterable on Array' do\n    expect(Iterable.on([1,5,5,9,9,9]).element_type).to eq(PVariantType.new([PIntegerType.new(1,1), PIntegerType.new(5,5), PIntegerType.new(9,9)]))\n  end\n\n  it 'can chain reverse_each after step on Iterable' do\n    expect{ |b| Iterable.on(6).step(2).reverse_each(&b) }.to yield_successive_args(4,2,0)\n  end\n\n  it 'can chain reverse_each after step on Integer range' do\n    expect{ |b| Iterable.on(PIntegerType.new(0, 5)).step(2).reverse_each(&b) }.to yield_successive_args(4,2,0)\n  end\n\n  it 'can chain step after reverse_each on Iterable' do\n    expect{ |b| Iterable.on(6).reverse_each.step(2, &b) }.to yield_successive_args(5,3,1)\n  end\n\n  it 'can chain step after reverse_each on Integer range' do\n    expect{ |b| Iterable.on(PIntegerType.new(0, 5)).reverse_each.step(2, &b) }.to yield_successive_args(5,3,1)\n  end\n\n  it 'will produce the same result for each as for reverse_each.reverse_each' do\n    x1 = Iterable.on(5)\n    x2 = Iterable.on(5)\n    expect(x1.reduce([]) { |a,i| a << i; a}).to eq(x2.reverse_each.reverse_each.reduce([]) { |a,i| a << i; a})\n  end\n\n  it 'can chain many nested step/reverse_each calls' do\n    # x = Iterable.on(18).step(3) (0, 3, 6, 9, 12, 15)\n    # x = x.reverse_each (15, 12, 9, 6, 3, 0)\n    # x = x.step(2) (15, 9, 3)\n    # x = x.reverse_each(3, 9, 15)\n    expect{ |b| Iterable.on(18).step(3).reverse_each.step(2).reverse_each(&b) }.to yield_successive_args(3, 9, 15)\n  end\n\n  it 'can chain many nested step/reverse_each calls on Array iterable' do\n    expect{ |b| Iterable.on(18.times.to_a).step(3).reverse_each.step(2).reverse_each(&b) }.to yield_successive_args(3, 9, 15)\n  end\n\n  it 'produces an steppable iterable for Array' do\n    expect{ |b| Iterable.on(%w(a b c d e f g h i)).step(3, &b) }.to yield_successive_args('a', 'd', 'g')\n  end\n\n  it 'produces an reverse steppable iterable for Array' do\n    expect{ |b| Iterable.on(%w(a b c d e f g h i)).reverse_each.step(3, &b) }.to yield_successive_args('i', 'f', 'c')\n  end\n\n  it 'responds false when a bounded Iterable is passed to Iterable.unbounded?' do\n    expect(Iterable.unbounded?(Iterable.on(%w(a b c d e f g h i)))).to eq(false)\n  end\n\n  it 'can create an Array from a bounded Iterable' do\n    expect(Iterable.on(%w(a b c d e f g h i)).to_a).to eq(%w(a b c d e f g h i))\n  end\n\n  class TestUnboundedIterator\n    include Enumerable\n    include Iterable\n\n    def step(step_size)\n      if block_given?\n        begin\n          current = 0\n          loop do\n            yield(@current)\n            current = current + step_size\n          end\n        rescue StopIteration\n        end\n      end\n      self\n    end\n  end\n\n  it 'responds true when an unbounded Iterable is passed to Iterable.unbounded?' do\n    ubi = TestUnboundedIterator.new\n    expect(Iterable.unbounded?(Iterable.on(ubi))).to eq(true)\n  end\n\n  it 'can not create an Array from an unbounded Iterable' do\n    ubi = TestUnboundedIterator.new\n    expect{ Iterable.on(ubi).to_a }.to raise_error(Puppet::Error, /Attempt to create an Array from an unbounded Iterable/)\n  end\n\n  it 'will produce the string Iterator[T] on to_s on an iterator instance with element type T' do\n    expect(Iterable.on(18).to_s).to eq('Iterator[Integer]-Value')\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_binary_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Binary Type' do\n  include PuppetSpec::Compiler\n\n  context 'as a type' do\n    it 'can be created with the type factory' do\n      t = TypeFactory.binary()\n      expect(t).to be_a(PBinaryType)\n      expect(t).to eql(PBinaryType::DEFAULT)\n    end\n  end\n\n  context 'a Binary instance' do\n    it 'can be created from a raw String using %r, raw string mode' do\n      str = [0xF1].pack(\"C*\")\n      code = <<-CODE\n        $x = Binary($testing, '%r')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code, Puppet::Node.new('foonode'), { 'testing' => str })).to eql(['8Q=='])\n    end\n\n    it 'can be created from a String using %s, string mode' do\n      # the text 'binar' needs padding with '='\n      code = <<-CODE\n        $x = Binary('binary', '%s')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXJ5'])\n    end\n\n    it 'format %s errors if encoding is bad in given string when using format %s and a broken UTF-8 string' do\n      str = [0xF1].pack(\"C*\")\n      str.force_encoding('UTF-8')\n      code = <<-CODE\n        $x = Binary($testing, '%s')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect {\n        eval_and_collect_notices(code, Puppet::Node.new('foonode'), { 'testing' => str })\n      }.to raise_error(/.*The given string in encoding 'UTF-8' is invalid\\. Cannot create a Binary UTF-8 representation.*/)\n    end\n\n    it 'format %s errors if encoding is correct but string cannot be transcoded to UTF-8' do\n      str = [0xF1].pack(\"C*\")\n      code = <<-CODE\n        $x = Binary($testing, '%s')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect {\n        eval_and_collect_notices(code, Puppet::Node.new('foonode'), { 'testing' => str })\n      }.to raise_error(/.*\"\\\\xF1\" from ASCII-8BIT to UTF-8.*/)\n    end\n\n    it 'can be created from a strict Base64 encoded String using default format' do\n      code = <<-CODE\n        $x = Binary('YmluYXI=')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXI='])\n    end\n\n    it 'will error creation in strict mode if padding is missing when using default format' do\n      # the text 'binar' needs padding with '=' (missing here to trigger error\n      code = <<-CODE\n        $x = Binary('YmluYXI')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect{ eval_and_collect_notices(code) }.to raise_error(/invalid base64/)\n    end\n\n    it 'can be created from a Base64 encoded String using %B, strict mode' do\n      # the text 'binar' needs padding with '='\n      code = <<-CODE\n        $x = Binary('YmluYXI=', '%B')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXI='])\n    end\n\n    it 'will error creation in strict mode if padding is missing' do\n      # the text 'binar' needs padding with '=' (missing here to trigger error\n      code = <<-CODE\n        $x = Binary('YmluYXI', '%B')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect{ eval_and_collect_notices(code) }.to raise_error(/invalid base64/)\n    end\n\n    it 'will not error creation in base mode if padding is missing' do\n      # the text 'binar' needs padding with '=' (missing here to trigger possible error)\n      code = <<-CODE\n        $x = Binary('YmluYXI', '%b')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXI='])\n    end\n\n    it 'will not error creation in base mode if padding is not required' do\n      # the text 'binary' does not need padding with '='\n      code = <<-CODE\n        $x = Binary('YmluYXJ5', '%b')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXJ5'])\n    end\n\n    it 'can be compared to another instance for equality' do\n      code = <<-CODE\n        $x = Binary('YmluYXJ5')\n        $y = Binary('YmluYXJ5')\n        notice($x == $y)\n        notice($x != $y)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true', 'false'])\n    end\n\n    it 'can be created from an array of byte values' do\n      # the text 'binar' needs padding with '='\n      code = <<-CODE\n        $x = Binary([251, 239, 255])\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['++//'])\n    end\n\n    it \"can be created from an hash with value and format\" do\n      # the text 'binar' needs padding with '='\n      code = <<-CODE\n        $x = Binary({value => '--__', format => '%u'})\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['++//'])\n    end\n\n    it \"can be created from an hash with value and default format\" do\n      code = <<-CODE\n        $x = Binary({value => 'YmluYXI='})\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXI='])\n    end\n\n    it 'can be created from a hash with value being an array' do\n      # the text 'binar' needs padding with '='\n      code = <<-CODE\n        $x = Binary({value => [251, 239, 255]})\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['++//'])\n    end\n\n    it \"can be created from an Base64 using URL safe encoding by specifying '%u' format'\" do\n      # the text 'binar' needs padding with '='\n      code = <<-CODE\n        $x = Binary('--__', '%u')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['++//'])\n    end\n\n    it \"when created with URL safe encoding chars in '%b' format, these are skipped\" do\n      code = <<-CODE\n        $x = Binary('--__YmluYXJ5', '%b')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['YmluYXJ5'])\n    end\n\n    it \"will error in strict format if string contains URL safe encoded chars\" do\n      code = <<-CODE\n        $x = Binary('--__YmluYXJ5', '%B')\n        notice(assert_type(Binary, $x))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/invalid base64/)\n    end\n\n    [   '<',\n        '<=',\n        '>',\n        '>='\n    ].each do |op|\n      it \"cannot be compared to another instance for magnitude using #{op}\" do\n        code = <<-\"CODE\"\n          $x = Binary('YmluYXJ5')\n          $y = Binary('YmluYXJ5')\n          $x #{op} $y\n        CODE\n        expect { eval_and_collect_notices(code)}.to raise_error(/Comparison of: Binary #{op} Binary, is not possible/)\n      end\n    end\n\n    it 'can be matched against a Binary in case expression' do\n      code = <<-CODE\n        case Binary('YmluYXJ5') {\n          Binary('YWxpZW4='): {\n            notice('nope')\n          }\n          Binary('YmluYXJ5'): {\n            notice('yay')\n          }\n          default: {\n            notice('nope')\n          }\n        }\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['yay'])\n    end\n\n    it \"can be matched against a Binary subsequence using 'in' expression\" do\n      # finding 'one' in 'one two three'\n      code = <<-CODE\n        notice(Binary(\"b25l\") in Binary(\"b25lIHR3byB0aHJlZQ==\"))\n        notice(Binary(\"c25l\") in Binary(\"b25lIHR3byB0aHJlZQ==\"))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true', 'false'])\n    end\n\n    it \"can be matched against a byte value using 'in' expression\" do\n      # finding 'e' (ascii 101) in 'one two three'\n      code = <<-CODE\n        notice(101 in Binary(\"b25lIHR3byB0aHJlZQ==\"))\n        notice(101.0 in Binary(\"b25lIHR3byB0aHJlZQ==\"))\n        notice(102 in Binary(\"b25lIHR3byB0aHJlZQ==\"))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true', 'true', 'false'])\n    end\n\n    it \"has a length method in ruby returning the length measured in bytes\" do\n      # \\u{1f452} is \"woman's hat emoji - 4 bytes in UTF-8\"\n      a_binary = PBinaryType::Binary.new(\"\\u{1f452}\")\n      expect(a_binary.length).to be(4)\n    end\n  end\n\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_init_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'The Init Type' do\n  include PuppetSpec::Compiler\n  include_context 'types_setup'\n\n  context 'when used in Puppet expressions' do\n    it 'an unparameterized type can be used' do\n      code = <<-CODE\n      notice(type(Init))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['Type[Init]'])\n    end\n\n    it 'a parameterized type can be used' do\n      code = <<-CODE\n      notice(type(Init[Integer]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['Type[Init[Integer]]'])\n    end\n\n    it 'a parameterized type can have additional arguments' do\n      code = <<-CODE\n      notice(type(Init[Integer, 16]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['Type[Init[Integer, 16]]'])\n    end\n\n    (all_types - abstract_types - internal_types).map { |tc| tc::DEFAULT.name }.each do |type_name|\n      it \"can be created on a #{type_name}\" do\n        code = <<-CODE\n        notice(type(Init[#{type_name}]))\n        CODE\n        expect(eval_and_collect_notices(code)).to eql([\"Type[Init[#{type_name}]]\"])\n      end\n    end\n\n    (abstract_types - internal_types).map { |tc| tc::DEFAULT.name }.each do |type_name|\n      it \"cannot be created on a #{type_name}\" do\n        code = <<-CODE\n        type(Init[#{type_name}]('x'))\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(/Creation of new instance of type '#{type_name}' is not supported/)\n      end\n    end\n\n    it 'an Init[Integer, 16] can create an instance from a String' do\n      code = <<-CODE\n      notice(Init[Integer, 16]('100'))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['256'])\n    end\n\n    it \"an Init[String,'%x'] can create an instance from an Integer\" do\n      code = <<-CODE\n      notice(Init[String,'%x'](128))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['80'])\n    end\n\n    it \"an Init[String,23] raises an error because no available dispatcher exists\" do\n      code = <<-CODE\n      notice(Init[String,23](128))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(\n        /The type 'Init\\[String, 23\\]' does not represent a valid set of parameters for String\\.new\\(\\)/)\n    end\n\n    it 'an Init[String] can create an instance from an Integer' do\n      code = <<-CODE\n      notice(Init[String](128))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['128'])\n    end\n\n    it 'an Init[String] can create an instance from an array with an Integer' do\n      code = <<-CODE\n      notice(Init[String]([128]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['128'])\n    end\n\n    it 'an Init[String] can create an instance from an array with an Integer and a format' do\n      code = <<-CODE\n      notice(Init[String]([128, '%x']))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['80'])\n    end\n\n    it 'an Init[String] can create an instance from an array with an array' do\n      code = <<-CODE\n      notice(Init[String]([[128, '%x']]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\"[128, '%x']\"])\n    end\n\n    it 'an Init[Binary] can create an instance from a string' do\n      code = <<-CODE\n      notice(Init[Binary]('b25lIHR3byB0aHJlZQ=='))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['b25lIHR3byB0aHJlZQ=='])\n    end\n\n    it 'an Init[String] can not create an instance from an Integer and a format unless given as an array argument' do\n      code = <<-CODE\n      notice(Init[String](128, '%x'))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/'new_Init' expects 1 argument, got 2/)\n    end\n\n    it 'the array [128] is an instance of Init[String]' do\n      code = <<-CODE\n      notice(assert_type(Init[String], [128]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['[128]'])\n    end\n\n    it 'the value 128 is an instance of Init[String]' do\n      code = <<-CODE\n      notice(assert_type(Init[String], 128))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['128'])\n    end\n\n    it \"the array [128] is an instance of Init[String]\" do\n      code = <<-CODE\n      notice(assert_type(Init[String], [128]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['[128]'])\n    end\n\n    it \"the array [128, '%x'] is an instance of Init[String]\" do\n      code = <<-CODE\n      notice(assert_type(Init[String], [128, '%x']))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['[128, %x]'])\n    end\n\n    it \"the array [[128, '%x']] is an instance of Init[String]\" do\n      code = <<-CODE\n      notice(assert_type(Init[String], [[128, '%x']]))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['[[128, %x]]'])\n    end\n\n    it 'RichData is assignable to Init' do\n      code = <<-CODE\n      notice(Init > RichData)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'Runtime is not assignable to Init' do\n      code = <<-CODE\n      notice(Init > Runtime['ruby', 'Time'])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['false'])\n    end\n\n    it 'Init is not assignable to RichData' do\n      code = <<-CODE\n      notice(Init < RichData)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['false'])\n    end\n\n    it 'Init[T1] is assignable to Init[T2] when T1 is assignable to T2' do\n      code = <<-CODE\n      notice(Init[Integer] < Init[Numeric])\n      notice(Init[Tuple[Integer]] < Init[Array[Integer]])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true', 'true'])\n    end\n\n    it 'Init[T1] is not assignable to Init[T2] unless T1 is assignable to T2' do\n      code = <<-CODE\n      notice(Init[Integer] > Init[Numeric])\n      notice(Init[Tuple[Integer]] > Init[Array[Integer]])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['false', 'false'])\n    end\n\n    it 'T is assignable to Init[T]' do\n      code = <<-CODE\n      notice(Integer < Init[Integer])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'T1 is assignable to Init[T2] if T2 can be created from instance of T1' do\n      code = <<-CODE\n      notice(Integer < Init[String])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'a RichData value is an instance of Init' do\n      code = <<-CODE\n      notice(\n        String(assert_type(Init, { 'a' => [ 1, 2, Sensitive(3) ], 2 => Timestamp('2014-12-01T13:15:00') }),\n          { Any => { format => '%s', string_formats => { Any => '%s' }}}))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['{a => [1, 2, Sensitive [value redacted]], 2 => 2014-12-01T13:15:00.000000000 UTC}'])\n    end\n\n    it 'an Init[Sensitive[String]] can create an instance from a String' do\n      code = <<-CODE\n      notice(Init[Sensitive[String]]('256'))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['Sensitive [value redacted]'])\n    end\n\n    it 'an Init[Type] can create an instance from a String' do\n      code = <<-CODE\n      notice(Init[Type]('Integer[2,3]'))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['Integer[2, 3]'])\n    end\n\n    it 'an Init[Type[Numeric]] can not create an unassignable type from a String' do\n      code = <<-CODE\n      notice(Init[Type[Numeric]]('String'))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(\n        /Converted value from Type\\[Numeric\\]\\.new\\(\\) has wrong type, expects a Type\\[Numeric\\] value, got Type\\[String\\]/)\n    end\n\n    it 'an Init with a custom object type can create an instance from a Hash' do\n      code = <<-CODE\n      type MyType = Object[{\n        attributes => {\n          granularity => String,\n          price => String,\n          category => Integer\n        }\n      }]\n      notice(Init[MyType]({ granularity => 'fine', price => '$10', category => 23 }))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\"MyType({'granularity' => 'fine', 'price' => '$10', 'category' => 23})\"])\n    end\n\n    it 'an Init with a custom object type and additional parameters can create an instance from a value' do\n      code = <<-CODE\n      type MyType = Object[{\n        attributes => {\n          granularity => String,\n          price => String,\n          category => Integer\n        }\n      }]\n      notice(Init[MyType,'$5',20]('coarse'))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\"MyType({'granularity' => 'coarse', 'price' => '$5', 'category' => 20})\"])\n    end\n\n    it 'an Init with a custom object with one Array parameter, can be created from an Array' do\n      code = <<-CODE\n      type MyType = Object[{\n        attributes => { array => Array[String] }\n      }]\n      notice(Init[MyType](['a']))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\"MyType({'array' => ['a']})\"])\n    end\n\n    it 'an Init type can be used recursively' do\n      # Even if it doesn't make sense, it should not crash\n      code = <<-CODE\n        type One = Object[{\n          attributes => {\n            init_one => Variant[Init[One],String]\n          }\n        }]\n        notice(Init[One]('w'))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\"One({'init_one' => 'w'})\"])\n    end\n\n    context 'computes if x is an instance such that' do\n      %w(true false True False TRUE FALSE tRuE FaLsE Yes No yes no YES NO YeS nO y n Y N).each do |str|\n        it \"string '#{str}' is an instance of Init[Boolean]\" do\n          expect(eval_and_collect_notices(\"notice('#{str}' =~ Init[Boolean])\")).to eql(['true'])\n        end\n      end\n\n      it 'arbitrary string is not an instance of Init[Boolean]' do\n        expect(eval_and_collect_notices(\"notice('blue' =~ Init[Boolean])\")).to eql(['false'])\n      end\n\n      it 'empty string is not an instance of Init[Boolean]' do\n        expect(eval_and_collect_notices(\"notice('' =~ Init[Boolean])\")).to eql(['false'])\n      end\n\n      it 'undef string is not an instance of Init[Boolean]' do\n        expect(eval_and_collect_notices(\"notice(undef =~ Init[Boolean])\")).to eql(['false'])\n      end\n\n      %w(0 1 0634 0x3b -0xba 0b1001 +0b1111 23.14 -2.3 2e-21 1.23e18  -0.23e18).each do |str|\n        it \"string '#{str}' is an instance of Init[Numeric]\" do\n          expect(eval_and_collect_notices(\"notice('#{str}' =~ Init[Numeric])\")).to eql(['true'])\n        end\n      end\n\n      it 'non numeric string is not an instance of Init[Numeric]' do\n        expect(eval_and_collect_notices(\"notice('blue' =~ Init[Numeric])\")).to eql(['false'])\n      end\n\n      it 'empty string is not an instance of Init[Numeric]' do\n        expect(eval_and_collect_notices(\"notice('' =~ Init[Numeric])\")).to eql(['false'])\n      end\n\n      it 'undef is not an instance of Init[Numeric]' do\n        expect(eval_and_collect_notices(\"notice(undef =~ Init[Numeric])\")).to eql(['false'])\n      end\n\n      %w(0 1 0634 0x3b -0xba 0b1001 +0b1111 23.14 -2.3 2e-21 1.23e18  -0.23e18).each do |str|\n        it \"string '#{str}' is an instance of Init[Float]\" do\n          expect(eval_and_collect_notices(\"notice('#{str}' =~ Init[Float])\")).to eql(['true'])\n        end\n      end\n\n      it 'non numeric string is not an instance of Init[Float]' do\n        expect(eval_and_collect_notices(\"notice('blue' =~ Init[Float])\")).to eql(['false'])\n      end\n\n      it 'empty string is not an instance of Init[Float]' do\n        expect(eval_and_collect_notices(\"notice('' =~ Init[Float])\")).to eql(['false'])\n      end\n\n      it 'undef is not an instance of Init[Float]' do\n        expect(eval_and_collect_notices(\"notice(undef =~ Init[Float])\")).to eql(['false'])\n      end\n\n      %w(0 1 0634 0x3b -0xba 0b1001 0b1111).each do |str|\n        it \"string '#{str}' is an instance of Init[Integer]\" do\n          expect(eval_and_collect_notices(\"notice('#{str}' =~ Init[Integer])\")).to eql(['true'])\n        end\n      end\n\n      %w(23.14 -2.3 2e-21 1.23e18  -0.23e18).each do |str|\n        it \"valid float string '#{str}' is not an instance of Init[Integer]\" do\n          expect(eval_and_collect_notices(\"notice('#{str}' =~ Init[Integer])\")).to eql(['false'])\n        end\n      end\n\n      it 'non numeric string is not an instance of Init[Integer]' do\n        expect(eval_and_collect_notices(\"notice('blue' =~ Init[Integer])\")).to eql(['false'])\n      end\n\n      it 'empty string is not an instance of Init[Integer]' do\n        expect(eval_and_collect_notices(\"notice('' =~ Init[Integer])\")).to eql(['false'])\n      end\n\n      it 'undef is not an instance of Init[Integer]' do\n        expect(eval_and_collect_notices(\"notice(undef =~ Init[Integer])\")).to eql(['false'])\n      end\n\n      %w(1.2.3 1.1.1-a3 1.2.3+b3 1.2.3-a3+b3).each do |str|\n        it \"string '#{str}' is an instance of Init[SemVer]\" do\n          expect(eval_and_collect_notices(\"notice('#{str}' =~ Init[SemVer])\")).to eql(['true'])\n        end\n      end\n\n      it 'non SemVer compliant string is not an instance of Init[SemVer]' do\n        expect(eval_and_collect_notices(\"notice('blue' =~ Init[SemVer])\")).to eql(['false'])\n      end\n\n      it 'empty string is not an instance of Init[SemVer]' do\n        expect(eval_and_collect_notices(\"notice('' =~ Init[SemVer])\")).to eql(['false'])\n      end\n\n      it 'undef is not an instance of Init[SemVer]' do\n        expect(eval_and_collect_notices(\"notice(undef =~ Init[SemVer])\")).to eql(['false'])\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_object_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'The Object Type' do\n  include PuppetSpec::Compiler\n\n  let(:parser) { TypeParser.singleton }\n  let(:pp_parser) { Parser::EvaluatingParser.new }\n  let(:env) { Puppet::Node::Environment.create(:testing, []) }\n  let(:node) { Puppet::Node.new('testnode', :environment => env) }\n  let(:loader) { Loaders.find_loader(nil) }\n  let(:factory) { TypeFactory }\n\n  before(:each) do\n    Puppet.push_context(:loaders => Loaders.new(env))\n  end\n\n  after(:each) do\n    Puppet.pop_context()\n  end\n\n  def type_object_t(name, body_string)\n    object = PObjectType.new(name, pp_parser.parse_string(\"{#{body_string}}\").body)\n    loader.set_entry(Loader::TypedName.new(:type, name), object)\n    object\n  end\n\n  def parse_object(name, body_string)\n    type_object_t(name, body_string)\n    parser.parse(name, loader).resolve(loader)\n  end\n\n  context 'when dealing with attributes' do\n    it 'raises an error when the attribute type is not a type' do\n      obj = <<-OBJECT\n        attributes => {\n          a => 23\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,\n        /attribute MyObject\\[a\\] has wrong type, expects a Type value, got Integer/)\n    end\n\n    it 'raises an error if the type is missing' do\n      obj = <<-OBJECT\n        attributes => {\n          a => { kind => derived }\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,\n        /expects a value for key 'type'/)\n    end\n\n    it 'raises an error when value is of incompatible type' do\n      obj = <<-OBJECT\n        attributes => {\n          a => { type => Integer, value => 'three' }\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,\n        /attribute MyObject\\[a\\] value has wrong type, expects an Integer value, got String/)\n    end\n\n    it 'raises an error if the kind is invalid' do\n      obj = <<-OBJECT\n        attributes => {\n          a => { type => String, kind => derivd }\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,\n        /expects a match for Enum\\['constant', 'derived', 'given_or_derived', 'reference'\\], got 'derivd'/)\n    end\n\n    it 'raises an error if the name is __ptype' do\n      obj = <<-OBJECT\n        attributes => {\n          __ptype => String\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        /The attribute '__ptype' is reserved and cannot be used/)\n    end\n\n    it 'raises an error if the name is __pvalue' do\n      obj = <<-OBJECT\n        attributes => {\n          __pvalue => String\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        /The attribute '__pvalue' is reserved and cannot be used/)\n    end\n\n    it 'stores value in attribute' do\n      tp = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer, value => 3 }\n        }\n      OBJECT\n      attr = tp['a']\n      expect(attr).to be_a(PObjectType::PAttribute)\n      expect(attr.value).to eql(3)\n    end\n\n    it 'attribute with defined value responds true to value?' do\n      tp = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer, value => 3 }\n        }\n      OBJECT\n      attr = tp['a']\n      expect(attr.value?).to be_truthy\n    end\n\n    it 'attribute value can be defined using heredoc?' do\n      tp = parse_object('MyObject', <<-OBJECT.unindent)\n        attributes => {\n          a => { type => String, value => @(END) }\n            The value is some\n            multiline text\n            |-END\n        }\n      OBJECT\n      attr = tp['a']\n      expect(attr.value).to eql(\"The value is some\\nmultiline text\")\n    end\n\n    it 'attribute without defined value responds false to value?' do\n      tp = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      attr = tp['a']\n      expect(attr.value?).to be_falsey\n    end\n\n    it 'attribute without defined value but optional type responds true to value?' do\n      tp = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => Optional[Integer]\n        }\n      OBJECT\n      attr = tp['a']\n      expect(attr.value?).to be_truthy\n      expect(attr.value).to be_nil\n    end\n\n    it 'raises an error when value is requested from an attribute that has no value' do\n      tp = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      expect { tp['a'].value }.to raise_error(Puppet::Error, 'attribute MyObject[a] has no value')\n    end\n\n    context 'that are constants' do\n      context 'and declared under key \"constants\"' do\n        it 'sets final => true' do\n          tp = parse_object('MyObject', <<-OBJECT)\n            constants => {\n              a => 3\n            }\n          OBJECT\n          expect(tp['a'].final?).to be_truthy\n        end\n\n        it 'sets kind => constant' do\n          tp = parse_object('MyObject', <<-OBJECT)\n            constants => {\n              a => 3\n            }\n          OBJECT\n          expect(tp['a'].constant?).to be_truthy\n        end\n\n        it 'infers generic type from value' do\n          tp = parse_object('MyObject', <<-OBJECT)\n            constants => {\n              a => 3\n            }\n          OBJECT\n          expect(tp['a'].type.to_s).to eql('Integer')\n        end\n\n        it 'cannot have the same name as an attribute' do\n          obj = <<-OBJECT\n            constants => {\n              a => 3\n            },\n            attributes => {\n              a => Integer\n            }\n          OBJECT\n          expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n            'attribute MyObject[a] is defined as both a constant and an attribute')\n        end\n      end\n\n      context 'and declared under key \"attributes\"' do\n        it 'sets final => true when declard in attributes' do\n          tp = parse_object('MyObject', <<-OBJECT)\n            attributes => {\n              a => {\n                type => Integer,\n                kind => constant,\n                value => 3\n              }\n            }\n          OBJECT\n          expect(tp['a'].final?).to be_truthy\n        end\n\n        it 'raises an error when no value is declared' do\n          obj = <<-OBJECT\n            attributes => {\n              a => {\n                type => Integer,\n                kind => constant\n              }\n            }\n          OBJECT\n          expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n            \"attribute MyObject[a] of kind 'constant' requires a value\")\n        end\n\n        it 'raises an error when final => false' do\n          obj = <<-OBJECT\n            attributes => {\n              a => {\n                type => Integer,\n                kind => constant,\n                final => false\n              }\n            }\n          OBJECT\n          expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n            \"attribute MyObject[a] of kind 'constant' cannot be combined with final => false\")\n        end\n      end\n    end\n  end\n\n  context 'when dealing with functions' do\n    it 'raises an error unless the function type is a Type[Callable]' do\n      obj = <<-OBJECT\n        functions => {\n          a => String\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,\n        /function MyObject\\[a\\] has wrong type, expects a Type\\[Callable\\] value, got Type\\[String\\]/)\n    end\n\n    it 'raises an error when a function has the same name as an attribute' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer\n        },\n        functions => {\n          a => Callable\n        }\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        'function MyObject[a] conflicts with attribute with the same name')\n    end\n  end\n\n  context 'when dealing with overrides' do\n    it 'can redefine inherited member to assignable type' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          a => { type => Integer[0,10], override => true }\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      tp = parse_object('MyDerivedObject', obj)\n      expect(tp['a'].type).to eql(PIntegerType.new(0,10))\n    end\n\n    it 'can redefine inherited constant to assignable type' do\n      parent = <<-OBJECT\n        constants => {\n          a => 23\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        constants => {\n          a => 46\n        }\n      OBJECT\n      tp = parse_object('MyObject', parent)\n      td = parse_object('MyDerivedObject', obj)\n      expect(tp['a'].value).to eql(23)\n      expect(td['a'].value).to eql(46)\n    end\n\n    it 'raises an error when an attribute overrides a function' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        functions => {\n          a => { type => Callable, override => true }\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        'function MyDerivedObject[a] attempts to override attribute MyObject[a]')\n    end\n\n    it 'raises an error when the a function overrides an attribute' do\n      parent = <<-OBJECT\n        functions => {\n          a => Callable\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          a => { type => Integer, override => true }\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        'attribute MyDerivedObject[a] attempts to override function MyObject[a]')\n    end\n\n    it 'raises an error on attempts to redefine inherited member to unassignable type' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          a => { type => String, override => true }\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        'attribute MyDerivedObject[a] attempts to override attribute MyObject[a] with a type that does not match')\n    end\n\n    it 'raises an error when an attribute overrides a final attribute' do\n      parent = <<-OBJECT\n        attributes => {\n          a => { type => Integer, final => true }\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          a => { type => Integer, override => true }\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        'attribute MyDerivedObject[a] attempts to override final attribute MyObject[a]')\n    end\n\n    it 'raises an error when an overriding attribute is not declared with override => true' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        'attribute MyDerivedObject[a] attempts to override attribute MyObject[a] without having override => true')\n    end\n\n    it 'raises an error when an attribute declared with override => true does not override' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          b => { type => Integer, override => true }\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        \"expected attribute MyDerivedObject[b] to override an inherited attribute, but no such attribute was found\")\n    end\n  end\n\n  context 'when dealing with equality' do\n    it 'the attributes can be declared as an array of names' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        },\n        equality => [a,b]\n      OBJECT\n      tp = parse_object('MyObject', obj)\n      expect(tp.equality).to eq(['a','b'])\n      expect(tp.equality_attributes.keys).to eq(['a','b'])\n    end\n\n    it 'a single [<name>] can be declared as <name>' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        },\n        equality => a\n      OBJECT\n      tp = parse_object('MyObject', obj)\n      expect(tp.equality).to eq(['a'])\n    end\n\n    it 'includes all non-constant attributes by default' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => { type => Integer, kind => constant, value => 3 },\n          c => Integer\n        }\n      OBJECT\n      tp = parse_object('MyObject', obj)\n      expect(tp.equality).to be_nil\n      expect(tp.equality_attributes.keys).to eq(['a','c'])\n    end\n\n    it 'equality_include_type is true by default' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer\n         },\n        equality => a\n      OBJECT\n      expect(parse_object('MyObject', obj).equality_include_type?).to be_truthy\n    end\n\n    it 'will allow an empty list of attributes' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        },\n        equality => []\n      OBJECT\n      tp = parse_object('MyObject', obj)\n      expect(tp.equality).to be_empty\n      expect(tp.equality_attributes).to be_empty\n    end\n\n    it 'will extend default equality in parent' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          c => Integer,\n          d => Integer\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      tp = parse_object('MyDerivedObject', obj)\n      expect(tp.equality).to be_nil\n      expect(tp.equality_attributes.keys).to eq(['a','b','c','d'])\n    end\n\n    it 'extends equality declared in parent' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        },\n        equality => a\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          c => Integer,\n          d => Integer\n        }\n      OBJECT\n      parse_object('MyObject', parent)\n      tp = parse_object('MyDerivedObject', obj)\n      expect(tp.equality).to be_nil\n      expect(tp.equality_attributes.keys).to eq(['a','c','d'])\n    end\n\n    it 'parent defined attributes can be included in equality if not already included by a parent' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        },\n        equality => a\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          c => Integer,\n          d => Integer\n        },\n        equality => [b,c]\n      OBJECT\n      parse_object('MyObject', parent)\n      tp = parse_object('MyDerivedObject', obj)\n      expect(tp.equality).to eq(['b','c'])\n      expect(tp.equality_attributes.keys).to eq(['a','b','c'])\n    end\n\n    it 'raises an error when attempting to extend default equality in parent' do\n      parent = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => Integer\n        }\n      OBJECT\n      obj = <<-OBJECT\n        parent => MyObject,\n        attributes => {\n          c => Integer,\n          d => Integer\n        },\n        equality => a\n      OBJECT\n      parse_object('MyObject', parent)\n      expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,\n        \"MyDerivedObject equality is referencing attribute MyObject[a] which is included in equality of MyObject\")\n    end\n\n    it 'raises an error when equality references a constant attribute' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer,\n          b => { type => Integer, kind => constant, value => 3 }\n        },\n        equality => [a,b]\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        'MyObject equality is referencing constant attribute MyObject[b]. Reference to constant is not allowed in equality')\n    end\n\n    it 'raises an error when equality references a function' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer,\n        },\n        functions => {\n          b => Callable\n        },\n        equality => [a,b]\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        'MyObject equality is referencing function MyObject[b]. Only attribute references are allowed')\n    end\n\n    it 'raises an error when equality references a non existent attributes' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer\n        },\n        equality => [a,b]\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        \"MyObject equality is referencing non existent attribute 'b'\")\n    end\n\n    it 'raises an error when equality_include_type = false and attributes are provided' do\n      obj = <<-OBJECT\n        attributes => {\n          a => Integer\n        },\n        equality => a,\n        equality_include_type => false\n      OBJECT\n      expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,\n        'equality_include_type = false cannot be combined with non empty equality specification')\n    end\n  end\n\n  it 'raises an error when initialization hash contains invalid keys' do\n    obj = <<-OBJECT\n      attribrutes => {\n        a => Integer\n      }\n    OBJECT\n    expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError, /object initializer has wrong type, unrecognized key 'attribrutes'/)\n  end\n\n  it 'raises an error when attribute contains invalid keys' do\n    obj = <<-OBJECT\n      attributes => {\n        a => { type => Integer, knid => constant }\n      }\n    OBJECT\n    expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError, /initializer for attribute MyObject\\[a\\] has wrong type, unrecognized key 'knid'/)\n  end\n\n  context 'when inheriting from a another Object type' do\n    let(:parent) { <<-OBJECT }\n      attributes => {\n        a => Integer\n      },\n      functions => {\n        b => Callable\n      }\n    OBJECT\n\n    let(:derived) { <<-OBJECT }\n      parent => MyObject,\n      attributes => {\n        c => String,\n        d => Boolean\n      }\n    OBJECT\n\n    it 'includes the inherited type and its members' do\n      parse_object('MyObject', parent)\n      t = parse_object('MyDerivedObject', derived)\n      members = t.members.values\n      expect{ |b| members.each {|m| m.name.tap(&b) }}.to yield_successive_args('c', 'd')\n      expect{ |b| members.each {|m| m.type.simple_name.tap(&b) }}.to yield_successive_args('String', 'Boolean')\n      members = t.members(true).values\n      expect{ |b| members.each {|m| m.name.tap(&b) }}.to yield_successive_args('a', 'b', 'c', 'd')\n      expect{ |b| members.each {|m| m.type.simple_name.tap(&b) }}.to(yield_successive_args('Integer', 'Callable', 'String', 'Boolean'))\n    end\n\n    it 'is assignable to its inherited type' do\n      p = parse_object('MyObject', parent)\n      t = parse_object('MyDerivedObject', derived)\n      expect(p).to be_assignable(t)\n    end\n\n    it 'does not consider inherited type to be assignable' do\n      p = parse_object('MyObject', parent)\n      d = parse_object('MyDerivedObject', derived)\n      expect(d).not_to be_assignable(p)\n    end\n\n    it 'ruby access operator can retrieve parent member' do\n      p = parse_object('MyObject', parent)\n      d = parse_object('MyDerivedObject', derived)\n      expect(d['b'].container).to equal(p)\n    end\n\n    context 'that in turn inherits another Object type' do\n      let(:derived2) { <<-OBJECT }\n        parent => MyDerivedObject,\n        attributes => {\n          e => String,\n          f => Boolean\n        }\n      OBJECT\n\n      it 'is assignable to all inherited types' do\n        p = parse_object('MyObject', parent)\n        d1 = parse_object('MyDerivedObject', derived)\n        d2 = parse_object('MyDerivedObject2', derived2)\n        expect(p).to be_assignable(d2)\n        expect(d1).to be_assignable(d2)\n      end\n\n      it 'does not consider any of the inherited types to be assignable' do\n        p = parse_object('MyObject', parent)\n        d1 = parse_object('MyDerivedObject', derived)\n        d2 = parse_object('MyDerivedObject2', derived2)\n        expect(d2).not_to be_assignable(p)\n        expect(d2).not_to be_assignable(d1)\n      end\n    end\n  end\n\n  context 'when producing an init_hash_type' do\n    it 'produces a struct of all attributes that are not derived or constant' do\n      t = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer },\n          b => { type => Integer, kind => given_or_derived },\n          c => { type => Integer, kind => derived },\n          d => { type => Integer, kind => constant, value => 4 }\n        }\n      OBJECT\n      expect(t.init_hash_type).to eql(factory.struct({\n        'a' => factory.integer,\n        'b' => factory.integer\n      }))\n    end\n\n    it 'produces a struct where optional entires are denoted with an optional key' do\n      t = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer },\n          b => { type => Integer, value => 4 }\n        }\n      OBJECT\n      expect(t.init_hash_type).to eql(factory.struct({\n        'a' => factory.integer,\n        factory.optional('b') => factory.integer\n      }))\n    end\n\n    it 'produces a struct that includes parameters from parent type' do\n      t1 = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer }\n        }\n      OBJECT\n      t2 = parse_object('MyDerivedObject', <<-OBJECT)\n        parent => MyObject,\n        attributes => {\n          b => { type => Integer }\n        }\n      OBJECT\n      expect(t1.init_hash_type).to eql(factory.struct({ 'a' => factory.integer }))\n      expect(t2.init_hash_type).to eql(factory.struct({ 'a' => factory.integer, 'b' => factory.integer }))\n    end\n\n    it 'produces a struct that reflects overrides made in derived type' do\n      t1 = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer },\n          b => { type => Integer }\n        }\n      OBJECT\n      t2 = parse_object('MyDerivedObject', <<-OBJECT)\n        parent => MyObject,\n        attributes => {\n          b => { type => Integer, override => true, value => 5 }\n        }\n      OBJECT\n      expect(t1.init_hash_type).to eql(factory.struct({ 'a' => factory.integer, 'b' => factory.integer }))\n      expect(t2.init_hash_type).to eql(factory.struct({ 'a' => factory.integer, factory.optional('b') => factory.integer }))\n    end\n  end\n\n  context 'with attributes and parameters of its own type' do\n    it 'resolves an attribute type' do\n      t = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => MyObject\n        }\n      OBJECT\n      expect(t['a'].type).to equal(t)\n    end\n\n    it 'resolves a parameter type' do\n      t = parse_object('MyObject', <<-OBJECT)\n        functions => {\n          a => Callable[MyObject]\n        }\n      OBJECT\n      expect(t['a'].type).to eql(PCallableType.new(PTupleType.new([t])))\n    end\n  end\n\n  context 'when using the initialization hash' do\n    it 'produced hash that contains features using short form (type instead of detailed hash when only type is declared)' do\n      obj = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer }\n        }\n      OBJECT\n      expect(obj.to_s).to eql(\"Object[{name => 'MyObject', attributes => {'a' => Integer}}]\")\n    end\n\n    it 'produced hash that does not include default for equality_include_type' do\n      obj = parse_object('MyObject', <<-OBJECT)\n        attributes => { a => Integer },\n        equality_include_type => true\n      OBJECT\n      expect(obj.to_s).to eql(\"Object[{name => 'MyObject', attributes => {'a' => Integer}}]\")\n    end\n\n    it 'constants are presented in a separate hash if they use a generic type' do\n      obj = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer, value => 23, kind => constant },\n        },\n      OBJECT\n      expect(obj.to_s).to eql(\"Object[{name => 'MyObject', constants => {'a' => 23}}]\")\n    end\n\n    it 'constants are not presented in a separate hash unless they use a generic type' do\n      obj = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer[0, 30], value => 23, kind => constant },\n        },\n      OBJECT\n      expect(obj.to_s).to eql(\"Object[{name => 'MyObject', attributes => {'a' => {type => Integer[0, 30], kind => constant, value => 23}}}]\")\n    end\n\n    it 'can create an equal copy from produced hash' do\n      obj = parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Struct[{x => Integer, y => Integer}], value => {x => 4, y => 9}, kind => constant },\n          b => Integer\n        },\n        functions => {\n          x => Callable[MyObject,Integer]\n        },\n        equality => [b]\n      OBJECT\n      obj2 = PObjectType.new(obj._pcore_init_hash)\n      expect(obj).to eql(obj2)\n    end\n  end\n\n  context 'when stringifying created instances' do\n    it 'outputs a Puppet constructor using the initializer hash' do\n      code = <<-CODE\n      type Spec::MyObject = Object[{attributes => { a => Integer }}]\n      type Spec::MySecondObject = Object[{parent => Spec::MyObject, attributes => { b => String }}]\n      notice(Spec::MySecondObject(42, 'Meaning of life'))\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\"Spec::MySecondObject({'a' => 42, 'b' => 'Meaning of life'})\"])\n    end\n  end\n\n  context 'when used from Ruby' do\n    it 'can create an instance without scope using positional arguments' do\n      parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer }\n        }\n      OBJECT\n\n      t = Puppet::Pops::Types::TypeParser.singleton.parse('MyObject', Puppet::Pops::Loaders.find_loader(nil))\n      instance = t.create(32)\n      expect(instance.a).to eql(32)\n    end\n\n    it 'can create an instance without scope using initialization hash' do\n      parse_object('MyObject', <<-OBJECT)\n        attributes => {\n          a => { type => Integer }\n        }\n      OBJECT\n\n      t = Puppet::Pops::Types::TypeParser.singleton.parse('MyObject', Puppet::Pops::Loaders.find_loader(nil))\n      instance = t.from_hash('a' => 32)\n      expect(instance.a).to eql(32)\n    end\n  end\n\n  context 'when used in Puppet expressions' do\n    it 'two anonymous empty objects are equal' do\n      code = <<-CODE\n      $x = Object[{}]\n      $y = Object[{}]\n      notice($x == $y)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'two objects where one object inherits another object are different' do\n      code = <<-CODE\n      type MyFirstObject = Object[{}]\n      type MySecondObject = Object[{ parent => MyFirstObject }]\n      notice(MyFirstObject == MySecondObject)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['false'])\n    end\n\n    it 'two anonymous objects that inherits the same parent are equal' do\n      code = <<-CODE\n      type MyFirstObject = Object[{}]\n      $x = Object[{ parent => MyFirstObject }]\n      $y = Object[{ parent => MyFirstObject }]\n      notice($x == $y)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'declared Object type is assignable to default Object type' do\n      code = <<-CODE\n      type MyObject = Object[{ attributes => { a => Integer }}]\n      notice(MyObject < Object)\n      notice(MyObject <= Object)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true', 'true'])\n    end\n\n    it 'default Object type not is assignable to declared Object type' do\n      code = <<-CODE\n      type MyObject = Object[{ attributes => { a => Integer }}]\n      notice(Object < MyObject)\n      notice(Object <= MyObject)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['false', 'false'])\n    end\n\n    it 'default Object type is assignable to itself' do\n      code = <<-CODE\n      notice(Object < Object)\n      notice(Object <= Object)\n      notice(Object > Object)\n      notice(Object >= Object)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['false', 'true', 'false', 'true'])\n    end\n\n    it 'an object type is an instance of an object type type' do\n      code = <<-CODE\n      type MyObject = Object[{ attributes => { a => Integer }}]\n      notice(MyObject =~ Type[MyObject])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'an object that inherits another object is an instance of the type of its parent' do\n      code = <<-CODE\n      type MyFirstObject = Object[{}]\n      type MySecondObject = Object[{ parent => MyFirstObject }]\n      notice(MySecondObject =~ Type[MyFirstObject])\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'a named object is not added to the loader unless a type <name> = <definition> is made' do\n      code = <<-CODE\n      $x = Object[{ name => 'MyFirstObject' }]\n      notice($x == MyFirstObject)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Resource type not found: MyFirstObject/)\n    end\n\n    it 'a type alias on a named object overrides the name' do\n      code = <<-CODE\n      type MyObject = Object[{ name => 'MyFirstObject', attributes => { a => { type => Integer, final => true }}}]\n      type MySecondObject = Object[{ parent => MyObject, attributes => { a => { type => Integer[10], override => true }}}]\n      notice(MySecondObject =~ Type)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error,\n        /attribute MySecondObject\\[a\\] attempts to override final attribute MyObject\\[a\\]/)\n    end\n\n    it 'a type cannot be created using an unresolved parent' do\n      code = <<-CODE\n      notice(Object[{ name => 'MyObject', parent => Type('NoneSuch'), attributes => { a => String}}].new('hello'))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error,\n        /reference to unresolved type 'NoneSuch'/)\n    end\n\n    context 'type alias using bracket-less (implicit Object) form' do\n      let(:logs) { [] }\n      let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }\n      let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n      let(:node) { Puppet::Node.new('example.com') }\n      let(:compiler) { Puppet::Parser::Compiler.new(node) }\n\n      def compile(code)\n        Puppet[:code] = code\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) { compiler.compile }\n      end\n\n      it 'Object is implicit' do\n        compile(<<-CODE)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => Integer}}\n          notice(MyObject =~ Type)\n          notice(MyObject(3))\n        CODE\n        expect(warnings).to be_empty\n        expect(notices).to eql(['true', \"MyObject({'a' => 3})\"])\n      end\n\n      it 'Object can be specified' do\n        compile(<<-CODE)\n          type MyObject = Object { name => 'MyFirstObject', attributes => { a =>Integer }}\n          notice(MyObject =~ Type)\n          notice(MyObject(3))\n        CODE\n        expect(warnings).to be_empty\n        expect(notices).to eql(['true', \"MyObject({'a' => 3})\"])\n      end\n\n      it 'parent can be specified before the hash' do\n        compile(<<-CODE)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = MyObject { attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n          notice(MySecondObject < MyObject)\n          notice(MyObject('hi'))\n          notice(MySecondObject('hello', 'world'))\n        CODE\n        expect(warnings).to be_empty\n        expect(notices).to eql(\n          ['true', 'true', \"MyObject({'a' => 'hi'})\", \"MySecondObject({'a' => 'hello', 'b' => 'world'})\"])\n      end\n\n      it 'parent can be specified in the hash' do\n        Puppet[:strict] = 'warning'\n        compile(<<-CODE)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = { parent => MyObject, attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n        CODE\n        expect(warnings).to be_empty\n        expect(notices).to eql(['true'])\n      end\n\n      it 'Object before the hash and parent inside the hash can be combined' do\n        Puppet[:strict] = 'warning'\n        compile(<<-CODE)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = Object { parent => MyObject, attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n        CODE\n        expect(warnings).to be_empty\n        expect(notices).to eql(['true'])\n      end\n\n      it 'if strict == warning, a warning is issued when the same is parent specified both before and inside the hash' do\n        Puppet[:strict] = 'warning'\n        compile(<<-CODE)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = MyObject { parent => MyObject, attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n        CODE\n        expect(notices).to eql(['true'])\n        expect(warnings).to eql([\"The key 'parent' is declared more than once\"])\n      end\n\n      it 'if strict == warning, a warning is issued when different parents are specified before and inside the hash. The former overrides the latter' do\n        Puppet[:strict] = 'warning'\n        compile(<<-CODE)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = MyObject { parent => MyObject, attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n        CODE\n        expect(notices).to eql(['true'])\n        expect(warnings).to eql([\"The key 'parent' is declared more than once\"])\n      end\n\n      it 'if strict == error, an error is raised when the same parent is specified both before and inside the hash' do\n        Puppet[:strict] = 'error'\n        expect { compile(<<-CODE) }.to raise_error(/The key 'parent' is declared more than once/)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = MyObject { parent => MyObject, attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n        CODE\n      end\n\n      it 'if strict == error, an error is raised when different parents are specified before and inside the hash' do\n        Puppet[:strict] = 'error'\n        expect { compile(<<-CODE) }.to raise_error(/The key 'parent' is declared more than once/)\n          type MyObject = { name => 'MyFirstObject', attributes => { a => String }}\n          type MySecondObject = MyObject { parent => MyOtherType, attributes => { b => String }}\n          notice(MySecondObject =~ Type)\n        CODE\n      end\n    end\n\n    it 'can inherit from an aliased type' do\n      code = <<-CODE\n      type MyObject = Object[{ name => 'MyFirstObject', attributes => { a => Integer }}]\n      type MyObjectAlias = MyObject\n      type MySecondObject = Object[{ parent => MyObjectAlias, attributes => { b => String }}]\n      notice(MySecondObject < MyObjectAlias)\n      notice(MySecondObject < MyObject)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql(['true', 'true'])\n    end\n\n    it 'detects equality duplication when inherited from an aliased type' do\n      code = <<-CODE\n      type MyObject = Object[{ name => 'MyFirstObject', attributes => { a => Integer }}]\n      type MyObjectAlias = MyObject\n      type MySecondObject = Object[{ parent => MyObjectAlias, attributes => { b => String }, equality => a}]\n      notice(MySecondObject < MyObject)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error,\n        /MySecondObject equality is referencing attribute MyObject\\[a\\] which is included in equality of MyObject/)\n    end\n\n    it 'raises an error when object when circular inheritance is detected' do\n      code = <<-CODE\n      type MyFirstObject = Object[{\n        parent => MySecondObject\n      }]\n      type MySecondObject = Object[{\n        parent => MyFirstObject\n      }]\n      notice(MySecondObject =~ Type[MyFirstObject])\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /inherits from itself/)\n    end\n\n    it 'notices the expanded string form expected content' do\n      code = <<-CODE\n      type MyFirstObject = Object[{\n        attributes => {\n          first_a => Integer,\n          first_b => { type => String, kind => constant, value => 'the first constant' },\n          first_c => { type => String, final => true, kind => derived },\n          first_d => { type => String, kind => given_or_derived },\n          first_e => { type => String }\n        },\n        functions => {\n          first_x => Callable[Integer],\n          first_y => Callable[String]\n        },\n        equality => first_a\n      }]\n      type MySecondObject = Object[{\n        parent => MyFirstObject,\n        attributes => {\n          second_a => Integer,\n          second_b => { type => String, kind => constant, value => 'the second constant' },\n          first_e => { type => Enum[foo,fee,fum], final => true, override => true, value => 'fee' }\n        },\n        functions => {\n          second_x => Callable[Integer],\n          second_y => Callable[String]\n        },\n        equality => second_a\n      }]\n      notice(MyFirstObject)\n      notice(MySecondObject)\n      CODE\n      expect(eval_and_collect_notices(code)).to eql([\n        \"Object[{\"+\n          \"name => 'MyFirstObject', \"+\n          \"attributes => {\"+\n          \"'first_a' => Integer, \"+\n          \"'first_c' => {type => String, final => true, kind => derived}, \"+\n          \"'first_d' => {type => String, kind => given_or_derived}, \"+\n          \"'first_e' => String\"+\n          \"}, \"+\n          \"constants => {\"+\n          \"'first_b' => 'the first constant'\"+\n          \"}, \"+\n          \"functions => {\"+\n          \"'first_x' => Callable[Integer], \"+\n          \"'first_y' => Callable[String]\"+\n          \"}, \"+\n          \"equality => ['first_a']\"+\n          \"}]\",\n        \"Object[{\"+\n          \"name => 'MySecondObject', \"+\n          \"parent => MyFirstObject, \"+\n          \"attributes => {\"+\n          \"'second_a' => Integer, \"+\n          \"'first_e' => {type => Enum['fee', 'foo', 'fum'], final => true, override => true, value => 'fee'}\"+\n          \"}, \"+\n          \"constants => {\"+\n          \"'second_b' => 'the second constant'\"+\n          \"}, \"+\n          \"functions => {\"+\n          \"'second_x' => Callable[Integer], \"+\n          \"'second_y' => Callable[String]\"+\n          \"}, \"+\n          \"equality => ['second_a']\"+\n          \"}]\"\n        ])\n    end\n\n    context 'object with type parameters' do\n      it 'can be declared' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['ok'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String\n          }]\n        notice('ok')\n        PUPPET\n      end\n\n      it 'can be referenced' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql([\"MyType['hello']\"])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String\n          }]\n\n        notice(MyType['hello'])\n        PUPPET\n      end\n\n      it 'leading unset parameters are represented as default in string representation' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql([\"MyType[default, 'world']\"])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String,\n            p2 => String,\n          }]\n\n        notice(MyType[default, 'world'])\n        PUPPET\n      end\n\n      it 'trailing unset parameters are skipped in string representation' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql([\"MyType['my']\"])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String,\n            p2 => String,\n          }]\n\n        notice(MyType['my'])\n        PUPPET\n      end\n\n      it 'a type with more than 2 type parameters uses named arguments in string representation' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql([\"MyType[{'p1' => 'my'}]\"])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String,\n            p2 => String,\n            p3 => String,\n          }]\n\n        notice(MyType['my'])\n        PUPPET\n      end\n\n      it 'can be used without parameters' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql([\"Object[{name => 'MyType', type_parameters => {'p1' => String}}]\"])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String\n          }]\n\n        notice(MyType)\n        PUPPET\n      end\n\n      it 'involves type parameter values when testing instance of' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true', 'false', 'true'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String\n          },\n          attributes => {\n            p1 => String\n          }]\n\n        $x = MyType('world')\n        notice($x =~ MyType)\n        notice($x =~ MyType['hello'])\n        notice($x =~ MyType['world'])\n        PUPPET\n      end\n\n      it 'involves type parameter values when testing assignability' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true', 'false', 'true', 'true', 'false', 'true'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String\n          },\n          attributes => {\n            p1 => String\n          }]\n\n        $x = MyType['world']\n        notice($x <= MyType)\n        notice($x <= MyType['hello'])\n        notice($x <= MyType['world'])\n\n        notice(MyType >= $x)\n        notice(MyType['hello'] >= $x)\n        notice(MyType['world'] >= $x)\n        PUPPET\n      end\n\n      it 'parameters can be types' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true', 'true', 'true', 'true', 'false'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => Variant[String,Regexp,Type[Enum],Type[Pattern],Type[NotUndef]],\n            p2 => Variant[String,Regexp,Type[Enum],Type[Pattern],Type[NotUndef]],\n          },\n          attributes => {\n            p1 => String,\n            p2 => String\n          }]\n        $x = MyType('good bye', 'cruel world')\n        notice($x =~ MyType)\n        notice($x =~ MyType[Enum['hello', 'good bye']])\n        notice($x =~ MyType[Enum['hello', 'good bye'], Pattern[/world/, /universe/]])\n        notice($x =~ MyType[NotUndef, NotUndef])\n        notice($x =~ MyType[Enum['hello', 'yo']])\n        PUPPET\n      end\n\n      it 'parameters can be provided using named arguments' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true', 'false', 'true'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => String,\n            p2 => String\n          },\n          attributes => {\n            p1 => String,\n            p2 => String\n          }]\n        $x = MyType('good bye', 'cruel world')\n        notice($x =~ MyType)\n        notice($x =~ MyType[p1 => 'hello', p2 => 'cruel world'])\n        notice($x =~ MyType[p1 => 'good bye', p2 => 'cruel world'])\n        PUPPET\n      end\n\n      it 'at least one parameter must be given' do\n        expect{eval_and_collect_notices(<<-PUPPET, node)}.to raise_error(/The MyType-Type cannot be parameterized using an empty parameter list/)\n        type MyType = Object[\n          type_parameters => {\n            p1 => Variant[String,Regexp,Type[Enum],Type[Pattern]],\n          },\n          attributes => {\n            p1 => String,\n          }]\n        notice(MyType[default])\n        PUPPET\n      end\n\n      it 'undef is a valid value for a type parameter' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true', 'false'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => Optional[String],\n          },\n          attributes => {\n            p1 => Optional[String],\n          }]\n        notice(MyType() =~ MyType[undef])\n        notice(MyType('hello') =~ MyType[undef])\n        PUPPET\n      end\n\n      it 'Type parameters does not mean that type must be parameterized' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n            p2 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n          },\n          attributes => {\n            p1 => String,\n            p2 => String\n          }]\n        notice(MyType('hello', 'world') =~ MyType)\n        PUPPET\n      end\n\n      it 'A parameterized type is assignable to another parameterized type if base type and parameters are assignable' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n            p2 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n          },\n          attributes => {\n            p1 => String,\n            p2 => String\n          }]\n        notice(MyType[Pattern[/a/,/b/]] > MyType[Enum['a','b']])\n        PUPPET\n      end\n\n      it 'Instance is inferred to parameterized type' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['true', 'true', 'true', 'true', 'true'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n            p2 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n          },\n          attributes => {\n            p1 => String,\n            p2 => String\n          }]\n        $x = MyType('hello', 'world')\n        notice(type($x, generalized) == MyType)\n        notice(type($x) < MyType)\n        notice(type($x) < MyType['hello'])\n        notice(type($x) < MyType[/hello/, /world/])\n        notice(type($x) == MyType['hello', 'world'])\n        PUPPET\n      end\n\n      it 'Attributes of instance of parameterized type can be accessed using function calls' do\n        expect(eval_and_collect_notices(<<-PUPPET, node)).to eql(['hello', 'world'])\n        type MyType = Object[\n          type_parameters => {\n            p1 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n            p2 => Variant[Undef,String,Regexp,Type[Enum],Type[Pattern]],\n          },\n          attributes => {\n            p1 => String,\n            p2 => String\n          }]\n        $x = MyType('hello', 'world')\n        notice($x.p1)\n        notice($x.p2)\n        PUPPET\n      end\n    end\n  end\n\n  context \"when used with function 'new'\" do\n    context 'with ordered parameters' do\n      it 'creates an instance with initialized attributes' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => Integer,\n            b => String\n          }\n        }]\n        $obj = MyFirstObject.new(3, 'hi')\n        notice($obj.a)\n        notice($obj.b)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['3', 'hi'])\n      end\n\n      it 'creates an instance with default attribute values' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, value => 'the default' }\n          }\n        }]\n        $obj = MyFirstObject.new\n        notice($obj.a)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['the default'])\n      end\n\n      it 'creates an instance with constant attributes' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, kind => constant, value => 'the constant' }\n          }\n        }]\n        $obj = MyFirstObject.new\n        notice($obj.a)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['the constant'])\n      end\n\n      it 'creates an instance with overridden attribute defaults' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, value => 'the default' }\n          }\n        }]\n        $obj = MyFirstObject.new('not default')\n        notice($obj.a)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['not default'])\n      end\n\n      it 'fails on an attempt to provide a constant attribute value' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, kind => constant, value => 'the constant' }\n          }\n        }]\n        $obj = MyFirstObject.new('not constant')\n        notice($obj.a)\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects no arguments/)\n      end\n\n      it 'fails when a required key is missing' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => String\n          }\n        }]\n        $obj = MyFirstObject.new\n        notice($obj.a)\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects 1 argument, got none/)\n      end\n\n      it 'creates a derived instance with initialized attributes' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => Integer,\n            b => { type => String, kind => constant, value => 'the first constant' },\n            c => String\n          }\n        }]\n        type MySecondObject = Object[{\n          parent => MyFirstObject,\n          attributes => {\n            d => { type => Integer, value => 34 }\n          }\n        }]\n        $obj = MySecondObject.new(3, 'hi')\n        notice($obj.a)\n        notice($obj.b)\n        notice($obj.c)\n        notice($obj.d)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['3', 'the first constant', 'hi', '34'])\n      end\n    end\n\n    context 'with named parameters' do\n      it 'creates an instance with initialized attributes' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => Integer,\n            b => String\n          }\n        }]\n        $obj = MyFirstObject.new({b => 'hi', a => 3})\n        notice($obj.a)\n        notice($obj.b)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['3', 'hi'])\n      end\n\n      it 'creates an instance with default attribute values' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, value => 'the default' }\n          }\n        }]\n        $obj = MyFirstObject.new({})\n        notice($obj.a)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['the default'])\n      end\n\n      it 'creates an instance with constant attributes' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, kind => constant, value => 'the constant' }\n          }\n        }]\n        $obj = MyFirstObject.new({})\n        notice($obj.a)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['the constant'])\n      end\n\n      it 'creates an instance with overridden attribute defaults' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, value => 'the default' }\n          }\n        }]\n        $obj = MyFirstObject.new({a => 'not default'})\n        notice($obj.a)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['not default'])\n      end\n\n      it 'fails on an attempt to provide a constant attribute value' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => { type => String, kind => constant, value => 'the constant' }\n          }\n        }]\n        $obj = MyFirstObject.new({a => 'not constant'})\n        notice($obj.a)\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /unrecognized key 'a'/)\n      end\n\n      it 'fails when a required key is missing' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => String\n          }\n        }]\n        $obj = MyFirstObject.new({})\n        notice($obj.a)\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects size to be 1, got 0/)\n      end\n\n      it 'creates a derived instance with initialized attributes' do\n        code = <<-CODE\n        type MyFirstObject = Object[{\n          attributes => {\n            a => Integer,\n            b => { type => String, kind => constant, value => 'the first constant' },\n            c => String\n          }\n        }]\n        type MySecondObject = Object[{\n          parent => MyFirstObject,\n          attributes => {\n            d => { type => Integer, value => 34 }\n          }\n        }]\n        $obj = MySecondObject.new({c => 'hi', a => 3})\n        notice($obj.a)\n        notice($obj.b)\n        notice($obj.c)\n        notice($obj.d)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['3', 'the first constant', 'hi', '34'])\n      end\n    end\n  end\n\n  context 'is assigned to all PAnyType classes such that' do\n    include_context 'types_setup'\n\n    def find_parent(tc, parent_name)\n      p = tc._pcore_type\n      while p.is_a?(PObjectType) && p.name != parent_name\n        p = p.parent\n      end\n      expect(p).to be_a(PObjectType), \"did not find #{parent_name} in parent chain of #{tc.name}\"\n      p\n    end\n\n    it 'the class has a _pcore_type method' do\n      all_types.each do |tc|\n        expect(tc).to respond_to(:_pcore_type).with(0).arguments\n      end\n    end\n\n    it 'the _pcore_type method returns a PObjectType instance' do\n      all_types.each do |tc|\n        expect(tc._pcore_type).to be_a(PObjectType)\n      end\n    end\n\n    it 'the instance returned by _pcore_type is a descendant from Pcore::AnyType' do\n      all_types.each { |tc| expect(find_parent(tc, 'Pcore::AnyType').name).to eq('Pcore::AnyType') }\n    end\n\n    it 'PScalarType classes _pcore_type returns a descendant from Pcore::ScalarType' do\n      scalar_types.each { |tc| expect(find_parent(tc, 'Pcore::ScalarType').name).to eq('Pcore::ScalarType') }\n    end\n\n    it 'PNumericType classes _pcore_type returns a descendant from Pcore::NumberType' do\n      numeric_types.each { |tc| expect(find_parent(tc, 'Pcore::NumericType').name).to eq('Pcore::NumericType') }\n    end\n\n    it 'PCollectionType classes _pcore_type returns a descendant from Pcore::CollectionType' do\n      coll_descendants = collection_types - [PTupleType, PStructType]\n      coll_descendants.each { |tc| expect(find_parent(tc, 'Pcore::CollectionType').name).to eq('Pcore::CollectionType') }\n    end\n  end\n\n  context 'when dealing with annotations' do\n    let(:annotation) { <<-PUPPET }\n      type MyAdapter = Object[{\n        parent => Annotation,\n        attributes => {\n          id => Integer,\n          value => String[1]\n        }\n      }]\n    PUPPET\n\n    it 'the Annotation type can be used as parent' do\n      code = <<-PUPPET\n        #{annotation}\n        notice(MyAdapter < Annotation)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql(['true'])\n    end\n\n    it 'an annotation can be added to an Object type' do\n      code = <<-PUPPET\n        #{annotation}\n        type MyObject = Object[{\n          annotations => {\n            MyAdapter => { 'id' => 2, 'value' => 'annotation value' }\n          }\n        }]\n        notice(MyObject)\n      PUPPET\n      expect(eval_and_collect_notices(code)).to eql([\n        \"Object[{annotations => {MyAdapter => {'id' => 2, 'value' => 'annotation value'}}, name => 'MyObject'}]\"])\n    end\n\n    it 'other types can not be used as annotations' do\n      code = <<-PUPPET\n        type NotAnAnnotation = Object[{}]\n        type MyObject = Object[{\n          annotations => {\n            NotAnAnnotation => {}\n          }\n        }]\n        notice(MyObject)\n      PUPPET\n      expect{eval_and_collect_notices(code)}.to raise_error(/entry 'annotations' expects a value of type Undef or Hash/)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_sem_ver_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Semantic Versions' do\n  include PuppetSpec::Compiler\n\n  context 'the SemVer type' do\n    it 'is normalized in a Variant' do\n      t = TypeFactory.variant(TypeFactory.sem_ver('>=1.0.0 <2.0.0'), TypeFactory.sem_ver('>=1.5.0 <4.0.0')).normalize\n      expect(t).to be_a(PSemVerType)\n      expect(t).to eql(TypeFactory.sem_ver('>=1.0.0 <4.0.0'))\n    end\n\n    context 'convert method' do\n      it 'returns nil on a nil argument' do\n        expect(PSemVerType.convert(nil)).to be_nil\n      end\n\n      it 'returns its argument when the argument is a version' do\n        v = SemanticPuppet::Version.new(1,0,0)\n        expect(PSemVerType.convert(v)).to equal(v)\n      end\n\n      it 'converts a valid version string argument to a version' do\n        v = SemanticPuppet::Version.new(1,0,0)\n        expect(PSemVerType.convert('1.0.0')).to eq(v)\n      end\n\n      it 'raises an error string that does not represent a valid version' do\n        expect{PSemVerType.convert('1-3')}.to raise_error(ArgumentError)\n      end\n    end\n  end\n\n  context 'the SemVerRange type' do\n     context 'convert method' do\n      it 'returns nil on a nil argument' do\n        expect(PSemVerRangeType.convert(nil)).to be_nil\n      end\n\n      it 'returns its argument when the argument is a version range' do\n        vr = SemanticPuppet::VersionRange.parse('1.x')\n        expect(PSemVerRangeType.convert(vr)).to equal(vr)\n      end\n\n      it 'converts a valid version string argument to a version range' do\n        vr = SemanticPuppet::VersionRange.parse('1.x')\n        expect(PSemVerRangeType.convert('1.x')).to eq(vr)\n      end\n\n      it 'raises an error string that does not represent a valid version range' do\n        expect{PSemVerRangeType.convert('x3')}.to raise_error(ArgumentError)\n      end\n    end\n  end\n\n  context 'when used in Puppet expressions' do\n\n    context 'the SemVer type' do\n      it 'can have multiple range arguments' do\n        code = <<-CODE\n          $t = SemVer[SemVerRange('>=1.0.0 <2.0.0'), SemVerRange('>=3.0.0 <4.0.0')]\n          notice(SemVer('1.2.3') =~ $t)\n          notice(SemVer('2.3.4') =~ $t)\n          notice(SemVer('3.4.5') =~ $t)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true', 'false', 'true'])\n      end\n\n      it 'can have multiple range arguments in string form' do\n        code = <<-CODE\n          $t = SemVer['>=1.0.0 <2.0.0', '>=3.0.0 <4.0.0']\n          notice(SemVer('1.2.3') =~ $t)\n          notice(SemVer('2.3.4') =~ $t)\n          notice(SemVer('3.4.5') =~ $t)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true', 'false', 'true'])\n      end\n\n      it 'range arguments are normalized' do\n        code = <<-CODE\n          notice(SemVer['>=1.0.0 <2.0.0', '>=1.5.0 <4.0.0'] == SemVer['>=1.0.0 <4.0.0'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n\n      it 'is assignable to a type containing ranges with a merged range that is assignable but individual ranges are not' do\n        code = <<-CODE\n          $x = SemVer['>=1.0.0 <2.0.0', '>=1.5.0 <3.0.0']\n          $y = SemVer['>=1.2.0 <2.8.0']\n          notice($y < $x)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n     end\n    end\n\n    context 'the SemVerRange type' do\n      it 'a range is an instance of the type' do\n        code = <<-CODE\n          notice(SemVerRange('3.0.0 - 4.0.0') =~ SemVerRange)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n    end\n\n    context 'a SemVer instance' do\n      it 'can be created from a String' do\n        code = <<-CODE\n          $x = SemVer('1.2.3')\n          notice(assert_type(SemVer, $x))\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['1.2.3'])\n      end\n\n      it 'can be compared to another instance for equality' do\n        code = <<-CODE\n          $x = SemVer('1.2.3')\n          $y = SemVer('1.2.3')\n          notice($x == $y)\n          notice($x != $y)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true', 'false'])\n      end\n\n      it 'can be compared to another instance created from arguments' do\n        code = <<-CODE\n          $x = SemVer('1.2.3-rc4+5')\n          $y = SemVer(1, 2, 3, 'rc4', '5')\n          notice($x == $y)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n\n      it 'can be compared to another instance created from a hash' do\n        code = <<-CODE\n          $x = SemVer('1.2.3-rc4+5')\n          $y = SemVer(major => 1, minor => 2, patch => 3, prerelease => 'rc4', build => '5')\n          notice($x == $y)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n\n      it 'can be compared to another instance for magnitude' do\n        code = <<-CODE\n          $x = SemVer('1.1.1')\n          $y = SemVer('1.2.3')\n          notice($x < $y)\n          notice($x <= $y)\n          notice($x > $y)\n          notice($x >= $y)\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true', 'true', 'false', 'false'])\n      end\n\n      it 'can be matched against a version range' do\n        code = <<-CODE\n          $v = SemVer('1.1.1')\n          notice($v =~ SemVerRange('>1.0.0'))\n          notice($v =~ SemVerRange('>1.1.1'))\n          notice($v =~ SemVerRange('>=1.1.1'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true', 'false', 'true'])\n      end\n\n      it 'can be matched against a SemVerRange in case expression' do\n        code = <<-CODE\n          case SemVer('1.1.1') {\n            SemVerRange('>1.1.1'): {\n              notice('high')\n            }\n            SemVerRange('>1.0.0'): {\n              notice('mid')\n            }\n            default: {\n              notice('low')\n            }\n          }\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['mid'])\n      end\n\n      it 'can be matched against a SemVer in case expression' do\n        code = <<-CODE\n          case SemVer('1.1.1') {\n            SemVer('1.1.0'): {\n              notice('high')\n            }\n            SemVer('1.1.1'): {\n              notice('mid')\n            }\n            default: {\n              notice('low')\n            }\n          }\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['mid'])\n      end\n\n      it \"can be matched against a versions in 'in' expression\" do\n        code = <<-CODE\n          notice(SemVer('1.1.1') in [SemVer('1.0.0'), SemVer('1.1.1'), SemVer('2.3.4')])\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n\n      it \"can be matched against a VersionRange using an 'in' expression\" do\n        code = <<-CODE\n          notice(SemVer('1.1.1') in SemVerRange('>1.0.0'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n\n      it \"can be matched against multiple VersionRanges using an 'in' expression\" do\n        code = <<-CODE\n          notice(SemVer('1.1.1') in [SemVerRange('>=1.0.0 <1.0.2'), SemVerRange('>=1.1.0 <1.1.2')])\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n    end\n\n    context 'a String representing a SemVer' do\n      it 'can be matched against a version range' do\n        code = <<-CODE\n          $v = '1.1.1'\n          notice($v =~ SemVerRange('>1.0.0'))\n          notice($v =~ SemVerRange('>1.1.1'))\n          notice($v =~ SemVerRange('>=1.1.1'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true', 'false', 'true'])\n      end\n\n      it 'can be matched against a SemVerRange in case expression' do\n        code = <<-CODE\n          case '1.1.1' {\n            SemVerRange('>1.1.1'): {\n              notice('high')\n            }\n            SemVerRange('>1.0.0'): {\n              notice('mid')\n            }\n            default: {\n              notice('low')\n            }\n          }\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['mid'])\n      end\n\n      it 'can be matched against a SemVer in case expression' do\n        code = <<-CODE\n          case '1.1.1' {\n            SemVer('1.1.0'): {\n              notice('high')\n            }\n            SemVer('1.1.1'): {\n              notice('mid')\n            }\n            default: {\n              notice('low')\n            }\n          }\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['mid'])\n      end\n\n      it \"can be matched against a VersionRange using an 'in' expression\" do\n        code = <<-CODE\n          notice('1.1.1' in SemVerRange('>1.0.0'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n\n      it \"can be matched against multiple VersionRanges using an 'in' expression\" do\n        code = <<-CODE\n          notice('1.1.1' in [SemVerRange('>=1.0.0 <1.0.2'), SemVerRange('>=1.1.0 <1.1.2')])\n        CODE\n        expect(eval_and_collect_notices(code)).to eql(['true'])\n      end\n    end\n\n    context 'matching SemVer' do\n      suitability = {\n        [ '1.2.3',         '1.2.2' ] => false,\n        [ '>=1.2.3',       '1.2.2' ] => false,\n        [ '<=1.2.3',       '1.2.2' ] => true,\n        [ '1.2.3 - 1.2.4', '1.2.2' ] => false,\n        [ '~1.2.3',        '1.2.2' ] => false,\n        [ '~1.2',          '1.2.2' ] => true,\n        [ '~1',            '1.2.2' ] => true,\n        [ '1.2.x',         '1.2.2' ] => true,\n        [ '1.x',           '1.2.2' ] => true,\n\n        [ '1.2.3-alpha',   '1.2.3-alpha' ] => true,\n        [ '>=1.2.3-alpha', '1.2.3-alpha' ] => true,\n        [ '<=1.2.3-alpha', '1.2.3-alpha' ] => true,\n        [ '<=1.2.3-alpha', '1.2.3-a'     ] => true,\n        [ '>1.2.3-alpha',  '1.2.3-alpha' ] => false,\n        [ '>1.2.3-a',      '1.2.3-alpha' ] => true,\n        [ '<1.2.3-alpha',  '1.2.3-alpha' ] => false,\n        [ '<1.2.3-alpha',  '1.2.3-a'     ] => true,\n        [ '1.2.3-alpha - 1.2.4', '1.2.3-alpha' ] => true,\n        [ '1.2.3 - 1.2.4-alpha', '1.2.4-alpha' ] => true,\n        [ '1.2.3 - 1.2.4', '1.2.5-alpha' ] => false,\n        [ '~1.2.3-alhpa',        '1.2.3-alpha' ] => true,\n        [ '~1.2.3-alpha',        '1.3.0-alpha' ] => false,\n\n        [ '1.2.3',         '1.2.3' ] => true,\n        [ '>=1.2.3',       '1.2.3' ] => true,\n        [ '<=1.2.3',       '1.2.3' ] => true,\n        [ '1.2.3 - 1.2.4', '1.2.3' ] => true,\n        [ '~1.2.3',        '1.2.3' ] => true,\n        [ '~1.2',          '1.2.3' ] => true,\n        [ '~1',            '1.2.3' ] => true,\n        [ '1.2.x',         '1.2.3' ] => true,\n        [ '1.x',           '1.2.3' ] => true,\n\n        [ '1.2.3',         '1.2.4' ] => false,\n        [ '>=1.2.3',       '1.2.4' ] => true,\n        [ '<=1.2.3',       '1.2.4' ] => false,\n        [ '1.2.3 - 1.2.4', '1.2.4' ] => true,\n#        [ '~1.2.3',        '1.2.4' ] => true, Awaits fix for PUP-6242\n        [ '~1.2',          '1.2.4' ] => true,\n        [ '~1',            '1.2.4' ] => true,\n        [ '1.2.x',         '1.2.4' ] => true,\n        [ '1.x',           '1.2.4' ] => true,\n      }\n      suitability.each do |arguments, expected|\n        it \"'#{arguments[1]}' against SemVerRange '#{arguments[0]}', yields #{expected}\" do\n          code = \"notice(SemVer('#{arguments[1]}') =~ SemVerRange('#{arguments[0]}'))\"\n          expect(eval_and_collect_notices(code)).to eql([expected.to_s])\n        end\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_sensitive_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Sensitive Type' do\n  include PuppetSpec::Compiler\n\n  context 'as a type' do\n    it 'can be created without a parameter with the type factory' do\n      t = TypeFactory.sensitive\n      expect(t).to be_a(PSensitiveType)\n      expect(t).to eql(PSensitiveType::DEFAULT)\n    end\n\n    it 'can be created with a parameter with the type factory' do\n      t = TypeFactory.sensitive(PIntegerType::DEFAULT)\n      expect(t).to be_a(PSensitiveType)\n      expect(t.type).to eql(PIntegerType::DEFAULT)\n    end\n\n    it 'string representation of unparameterized instance is \"Sensitive\"' do\n      expect(PSensitiveType::DEFAULT.to_s).to eql('Sensitive')\n    end\n\n    context 'when used in Puppet expressions' do\n      it 'is equal to itself only' do\n        code = <<-CODE\n          $t = Sensitive\n          notice(Sensitive =~ Type[ Sensitive ])\n          notice(Sensitive == Sensitive)\n          notice(Sensitive < Sensitive)\n          notice(Sensitive > Sensitive)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['true', 'true', 'false', 'false'])\n      end\n\n      context \"when parameterized\" do\n        it 'is equal other types with the same parameterization' do\n          code = <<-CODE\n            notice(Sensitive[String] == Sensitive[String])\n            notice(Sensitive[Numeric] != Sensitive[Integer])\n          CODE\n          expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n        end\n\n        it 'orders parameterized types based on the type system hierarchy' do\n          code = <<-CODE\n            notice(Sensitive[Numeric] > Sensitive[Integer])\n            notice(Sensitive[Numeric] < Sensitive[Integer])\n          CODE\n          expect(eval_and_collect_notices(code)).to eq(['true', 'false'])\n        end\n\n        it 'does not order incomparable parameterized types' do\n          code = <<-CODE\n            notice(Sensitive[String] < Sensitive[Integer])\n            notice(Sensitive[String] > Sensitive[Integer])\n          CODE\n          expect(eval_and_collect_notices(code)).to eq(['false', 'false'])\n        end\n\n        it 'generalizes passed types to prevent information leakage' do\n          code =<<-CODE\n            $it = String[7, 7]\n            $st = Sensitive[$it]\n            notice(type($st))\n          CODE\n          expect(eval_and_collect_notices(code)).to eq(['Type[Sensitive[String]]'])\n        end\n      end\n    end\n  end\n\n  context 'a Sensitive instance' do\n    it 'can be created from a string and does not leak its contents' do\n      code =<<-CODE\n        $o = Sensitive(\"hunter2\")\n        notice($o)\n        notice(type($o))\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive [value redacted]', 'Sensitive[String]'])\n    end\n\n    it 'matches the appropriate parameterized type' do\n      code =<<-CODE\n        $o = Sensitive(\"hunter2\")\n        notice(assert_type(Sensitive[String], $o))\n        notice(assert_type(Sensitive[String[7, 7]], $o))\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive [value redacted]', 'Sensitive [value redacted]'])\n    end\n\n    it 'verifies the constrains of the parameterized type' do\n      pending \"the ability to enforce constraints without leaking information\"\n      code =<<-CODE\n        $o = Sensitive(\"hunter2\")\n        notice(assert_type(Sensitive[String[10, 20]], $o))\n      CODE\n      expect {\n        eval_and_collect_notices(code)\n      }.to raise_error(Puppet::Error, /expects a Sensitive\\[String\\[10, 20\\]\\] value, got Sensitive\\[String\\[7, 7\\]\\]/)\n    end\n\n    it 'does not match an inappropriate parameterized type' do\n      code =<<-CODE\n        $o = Sensitive(\"hunter2\")\n        notice(assert_type(Sensitive[Integer], $o) |$expected, $actual| {\n          \"$expected != $actual\"\n        })\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive[Integer] != Sensitive[String]'])\n    end\n\n    it 'equals another instance with the same value' do\n      code =<<-CODE\n        $i = Sensitive('secret')\n        $o = Sensitive('secret')\n        notice($i == $o)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true'])\n    end\n\n    it 'has equal hash keys for same values' do\n      code =<<-CODE\n        $i = Sensitive('secret')\n        $o = Sensitive('secret')\n        notice({$i => 1} == {$o => 1})\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true'])\n    end\n\n    it 'can be created from another sensitive instance ' do\n      code =<<-CODE\n        $o = Sensitive(\"hunter2\")\n        $x = Sensitive($o)\n        notice(assert_type(Sensitive[String], $x))\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive [value redacted]'])\n    end\n\n    it 'can be given to a user defined resource as a parameter' do\n      code =<<-CODE\n        define keeper_of_secrets(Sensitive $x) {\n          notice(assert_type(Sensitive[String], $x))\n        }\n        keeper_of_secrets { 'test': x => Sensitive(\"long toe\") }\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive [value redacted]'])\n    end\n\n    it 'can be given to a class as a parameter' do\n      code =<<-CODE\n        class keeper_of_secrets(Sensitive $x) {\n          notice(assert_type(Sensitive[String], $x))\n        }\n        class { 'keeper_of_secrets': x => Sensitive(\"long toe\") }\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive [value redacted]'])\n    end\n\n    it 'can be given to a function as a parameter' do\n      code =<<-CODE\n        function keeper_of_secrets(Sensitive $x) {\n          notice(assert_type(Sensitive[String], $x))\n        }\n        keeper_of_secrets(Sensitive(\"long toe\"))\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['Sensitive [value redacted]'])\n    end\n  end\n\n  it \"enforces wrapped type constraints\" do\n    pending \"the ability to enforce constraints without leaking information\"\n    code =<<-CODE\n        class secrets_handler(Sensitive[Array[String[4, 8]]] $pwlist) {\n            notice($pwlist)\n        }\n\n        class { \"secrets_handler\":\n            pwlist => Sensitive(['hi', 'longlonglong'])\n        }\n    CODE\n    expect {\n      expect(eval_and_collect_notices(code))\n    }.to raise_error(Puppet::Error, /expects a String\\[4, 8\\], got String/)\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_timespan_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Timespan type' do\n  it 'is normalized in a Variant' do\n    t = TypeFactory.variant(TypeFactory.timespan('10:00:00', '15:00:00'), TypeFactory.timespan('14:00:00', '17:00:00')).normalize\n    expect(t).to be_a(PTimespanType)\n    expect(t).to eql(TypeFactory.timespan('10:00:00', '17:00:00'))\n  end\n\n  context 'when used in Puppet expressions' do\n    include PuppetSpec::Compiler\n    it 'is equal to itself only' do\n      code = <<-CODE\n          $t = Timespan\n          notice(Timespan =~ Type[Timespan])\n          notice(Timespan == Timespan)\n          notice(Timespan < Timespan)\n          notice(Timespan > Timespan)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(%w(true true false false))\n    end\n\n    it 'does not consider an Integer to be an instance' do\n      code = <<-CODE\n        notice(assert_type(Timespan, 1234))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/expects a Timespan value, got Integer/)\n    end\n\n    it 'does not consider a Float to be an instance' do\n      code = <<-CODE\n        notice(assert_type(Timespan, 1.234))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/expects a Timespan value, got Float/)\n    end\n\n    context \"when parameterized\" do\n      it 'is equal other types with the same parameterization' do\n        code = <<-CODE\n            notice(Timespan['01:00:00', '13:00:00'] == Timespan['01:00:00', '13:00:00'])\n            notice(Timespan['01:00:00', '13:00:00'] != Timespan['01:12:20', '13:00:00'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true))\n      end\n\n      it 'using just one parameter is the same as using default for the second parameter' do\n        code = <<-CODE\n            notice(Timespan['01:00:00'] == Timespan['01:00:00', default])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true))\n      end\n\n      it 'if the second parameter is default, it is unlimited' do\n        code = <<-CODE\n            notice(Timespan('12345-23:59:59') =~ Timespan['01:00:00', default])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true))\n      end\n\n      it 'orders parameterized types based on range inclusion' do\n        code = <<-CODE\n            notice(Timespan['01:00:00', '13:00:00'] < Timespan['00:00:00', '14:00:00'])\n            notice(Timespan['01:00:00', '13:00:00'] > Timespan['00:00:00', '14:00:00'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false))\n      end\n\n      it 'accepts integer values when specifying the range' do\n        code = <<-CODE\n            notice(Timespan(1) =~ Timespan[1, 2])\n            notice(Timespan(3) =~ Timespan[1])\n            notice(Timespan(0) =~ Timespan[default, 2])\n            notice(Timespan(0) =~ Timespan[1, 2])\n            notice(Timespan(3) =~ Timespan[1, 2])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true true false false))\n      end\n\n      it 'accepts float values when specifying the range' do\n        code = <<-CODE\n            notice(Timespan(1.0) =~ Timespan[1.0, 2.0])\n            notice(Timespan(3.0) =~ Timespan[1.0])\n            notice(Timespan(0.0) =~ Timespan[default, 2.0])\n            notice(Timespan(0.0) =~ Timespan[1.0, 2.0])\n            notice(Timespan(3.0) =~ Timespan[1.0, 2.0])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true true false false))\n      end\n    end\n\n    context 'a Timespan instance' do\n      it 'can be created from a string' do\n        code = <<-CODE\n            $o = Timespan('3-11:00')\n            notice($o)\n            notice(type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(3-11:00:00.0 Timespan['3-11:00:00.0']))\n      end\n\n      it 'can be created from a string and format' do\n        code = <<-CODE\n            $o = Timespan('1d11h23m', '%Dd%Hh%Mm')\n            notice($o)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(1-11:23:00.0))\n      end\n\n      it 'can be created from a hash with string and format' do\n        code = <<-CODE\n            $o = Timespan({string => '1d11h23m', format => '%Dd%Hh%Mm'})\n            notice($o)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(1-11:23:00.0))\n      end\n\n      it 'can be created from a string and array of formats' do\n        code = <<-CODE\n            $fmts = ['%Dd%Hh%Mm%Ss', '%Hh%Mm%Ss', '%Dd%Hh%Mm', '%Dd%Hh', '%Hh%Mm', '%Mm%Ss', '%Dd', '%Hh', '%Mm', '%Ss' ]\n            notice(Timespan('1d11h23m13s', $fmts))\n            notice(Timespan('11h23m13s', $fmts))\n            notice(Timespan('1d11h23m', $fmts))\n            notice(Timespan('1d11h', $fmts))\n            notice(Timespan('11h23m', $fmts))\n            notice(Timespan('23m13s', $fmts))\n            notice(Timespan('1d', $fmts))\n            notice(Timespan('11h', $fmts))\n            notice(Timespan('23m', $fmts))\n            notice(Timespan('13s', $fmts))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(\n          %w(1-11:23:13.0 0-11:23:13.0 1-11:23:00.0 1-11:00:00.0 0-11:23:00.0 0-00:23:13.0 1-00:00:00.0 0-11:00:00.0 0-00:23:00.0 0-00:00:13.0))\n      end\n\n      it 'it cannot be created using an empty formats array' do\n        code = <<-CODE\n            notice(Timespan('1d11h23m13s', []))\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /parameter 'format' variant 1 expects size to be at least 1, got 0/)\n      end\n\n      it 'can be created from a integer that represents seconds' do\n        code = <<-CODE\n            $o = Timespan(6800)\n            notice(Integer($o) == 6800)\n            notice($o == Timespan('01:53:20'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true))\n      end\n\n      it 'can be created from a float that represents seconds with fraction' do\n        code = <<-CODE\n            $o = Timespan(6800.123456789)\n            notice(Float($o) == 6800.123456789)\n            notice($o == Timespan('01:53:20.123456789', '%H:%M:%S.%N'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true))\n      end\n\n      it 'matches the appropriate parameterized type' do\n        code = <<-CODE\n            $o = Timespan('3-11:12:13')\n            notice(assert_type(Timespan['3-00:00:00', '4-00:00:00'], $o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['3-11:12:13.0'])\n      end\n\n      it 'does not match an inappropriate parameterized type' do\n        code = <<-CODE\n            $o = Timespan('1-03:04:05')\n            notice(assert_type(Timespan['2-00:00:00', '3-00:00:00'], $o) |$e, $a| { 'nope' })\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['nope'])\n      end\n\n      it 'can be compared to other instances' do\n        code = <<-CODE\n            $o1 = Timespan('00:00:01')\n            $o2 = Timespan('00:00:02')\n            $o3 = Timespan('00:00:02')\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o1 == $o3)\n            notice($o1 != $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n            notice($o2 == $o3)\n            notice($o2 != $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false true false false true true true false))\n      end\n\n      it 'can be compared to integer that represents seconds' do\n        code = <<-CODE\n            $o1 = Timespan('00:00:01')\n            $o2 = Timespan('00:00:02')\n            $o3 = 2\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'integer that represents seconds can be compared to it' do\n        code = <<-CODE\n            $o1 = 1\n            $o2 = 2\n            $o3 = Timespan('00:00:02')\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'is equal to integer that represents seconds' do\n        code = <<-CODE\n            $o1 = Timespan('02', '%S')\n            $o2 = 2\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice(Integer($o1) == $o2)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'integer that represents seconds is equal to it' do\n        code = <<-CODE\n            $o1 = 2\n            $o2 = Timespan('02', '%S')\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice($o1 == Integer($o2))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'can be compared to float that represents seconds with fraction' do\n        code = <<-CODE\n            $o1 = Timespan('01.123456789', '%S.%N')\n            $o2 = Timespan('02.123456789', '%S.%N')\n            $o3 = 2.123456789\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'float that represents seconds with fraction can be compared to it' do\n        code = <<-CODE\n            $o1 = 1.123456789\n            $o2 = 2.123456789\n            $o3 = Timespan('02.123456789', '%S.%N')\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'is equal to float that represents seconds with fraction' do\n        code = <<-CODE\n            $o1 = Timespan('02.123456789', '%S.%N')\n            $o2 = 2.123456789\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice(Float($o1) == $o2)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'float that represents seconds with fraction is equal to it' do\n        code = <<-CODE\n            $o1 = 2.123456789\n            $o2 = Timespan('02.123456789', '%S.%N')\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice($o1 == Float($o2))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'it cannot be compared to a Timestamp' do\n        code = <<-CODE\n            notice(Timespan(3) < Timestamp())\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Timespans are only comparable to Timespans, Integers, and Floats/)\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_timestamp_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Timestamp type' do\n\n  it 'is normalized in a Variant' do\n    t = TypeFactory.variant(TypeFactory.timestamp('2015-03-01', '2016-01-01'), TypeFactory.timestamp('2015-11-03', '2016-12-24')).normalize\n    expect(t).to be_a(PTimestampType)\n    expect(t).to eql(TypeFactory.timestamp('2015-03-01', '2016-12-24'))\n  end\n\n  it 'DateTime#_strptime creates hash with :leftover field' do\n    expect(DateTime._strptime('2015-05-04 and bogus', '%F')).to include(:leftover)\n    expect(DateTime._strptime('2015-05-04T10:34:11.003 UTC and bogus', '%FT%T.%N %Z')).to include(:leftover)\n  end\n\n  context 'when used in Puppet expressions' do\n    include PuppetSpec::Compiler\n    it 'is equal to itself only' do\n      code = <<-CODE\n          $t = Timestamp\n          notice(Timestamp =~ Type[Timestamp])\n          notice(Timestamp == Timestamp)\n          notice(Timestamp < Timestamp)\n          notice(Timestamp > Timestamp)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(%w(true true false false))\n    end\n\n    it 'does not consider an Integer to be an instance' do\n      code = <<-CODE\n        notice(assert_type(Timestamp, 1234))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/expects a Timestamp value, got Integer/)\n    end\n\n    it 'does not consider a Float to be an instance' do\n      code = <<-CODE\n        notice(assert_type(Timestamp, 1.234))\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/expects a Timestamp value, got Float/)\n    end\n\n    context \"when parameterized\" do\n      it 'is equal other types with the same parameterization' do\n        code = <<-CODE\n            notice(Timestamp['2015-03-01', '2016-01-01'] == Timestamp['2015-03-01', '2016-01-01'])\n            notice(Timestamp['2015-03-01', '2016-01-01'] != Timestamp['2015-11-03', '2016-12-24'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true))\n      end\n\n      it 'using just one parameter is the same as using default for the second parameter' do\n        code = <<-CODE\n            notice(Timestamp['2015-03-01'] == Timestamp['2015-03-01', default])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true))\n      end\n\n      it 'if the second parameter is default, it is unlimited' do\n        code = <<-CODE\n            notice(Timestamp('5553-12-31') =~ Timestamp['2015-03-01', default])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true))\n      end\n\n      it 'orders parameterized types based on range inclusion' do\n        code = <<-CODE\n            notice(Timestamp['2015-03-01', '2015-09-30'] < Timestamp['2015-02-01', '2015-10-30'])\n            notice(Timestamp['2015-03-01', '2015-09-30'] > Timestamp['2015-02-01', '2015-10-30'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false))\n      end\n\n      it 'accepts integer values when specifying the range' do\n        code = <<-CODE\n            notice(Timestamp(1) =~ Timestamp[1, 2])\n            notice(Timestamp(3) =~ Timestamp[1])\n            notice(Timestamp(0) =~ Timestamp[default, 2])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true true))\n      end\n\n      it 'accepts float values when specifying the range' do\n        code = <<-CODE\n            notice(Timestamp(1.0) =~ Timestamp[1.0, 2.0])\n            notice(Timestamp(3.0) =~ Timestamp[1.0])\n            notice(Timestamp(0.0) =~ Timestamp[default, 2.0])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true true))\n      end\n\n    end\n\n    context 'a Timestamp instance' do\n      it 'can be created from a string with just a date' do\n        code = <<-CODE\n            $o = Timestamp('2015-03-01')\n            notice($o)\n            notice(type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2015-03-01T00:00:00.000000000 UTC', \"Timestamp['2015-03-01T00:00:00.000000000 UTC']\"])\n      end\n\n      it 'can be created from a string and time separated by \"T\"' do\n        code = <<-CODE\n            notice(Timestamp('2015-03-01T11:12:13'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2015-03-01T11:12:13.000000000 UTC'])\n      end\n\n      it 'can be created from a string and time separated by space' do\n        code = <<-CODE\n            notice(Timestamp('2015-03-01 11:12:13'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2015-03-01T11:12:13.000000000 UTC'])\n      end\n\n      it 'should error when none of the default formats can parse the string' do\n        code = <<-CODE\n            notice(Timestamp('2015#03#01 11:12:13'))\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(/Unable to parse/)\n      end\n\n      it 'should error when only part of the string is parsed' do\n        code = <<-CODE\n            notice(Timestamp('2015-03-01T11:12:13 bogus after'))\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(/Unable to parse/)\n      end\n\n      it 'can be created from a string and format' do\n        code = <<-CODE\n            $o = Timestamp('Sunday, 28 August, 2016', '%A, %d %B, %Y')\n            notice($o)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2016-08-28T00:00:00.000000000 UTC'])\n      end\n\n      it 'can be created from a string, format, and a timezone' do\n        code = <<-CODE\n            $o = Timestamp('Sunday, 28 August, 2016', '%A, %d %B, %Y', 'EST')\n            notice($o)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2016-08-28T05:00:00.000000000 UTC'])\n      end\n\n      it 'can be not be created from a string, format with timezone designator, and a timezone' do\n        code = <<-CODE\n            $o = Timestamp('Sunday, 28 August, 2016 UTC', '%A, %d %B, %Y %z', 'EST')\n            notice($o)\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(\n          /Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument/)\n      end\n\n      it 'can be created from a hash with string and format' do\n        code = <<-CODE\n            $o = Timestamp({ string => 'Sunday, 28 August, 2016', format => '%A, %d %B, %Y' })\n            notice($o)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2016-08-28T00:00:00.000000000 UTC'])\n      end\n\n      it 'can be created from a hash with string, format, and a timezone' do\n        code = <<-CODE\n            $o = Timestamp({ string => 'Sunday, 28 August, 2016', format => '%A, %d %B, %Y', timezone => 'EST' })\n            notice($o)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['2016-08-28T05:00:00.000000000 UTC'])\n      end\n\n      it 'can be created from a string and array of formats' do\n        code = <<-CODE\n            $fmts = [\n              '%A, %d %B, %Y at %r',\n              '%b %d, %Y, %l:%M %P',\n              '%y-%m-%d %H:%M:%S %z'\n            ]\n            notice(Timestamp('Sunday, 28 August, 2016 at 12:15:00 PM', $fmts))\n            notice(Timestamp('Jul 24, 2016, 1:20 am', $fmts))\n            notice(Timestamp('16-06-21 18:23:15 UTC', $fmts))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(\n          ['2016-08-28T12:15:00.000000000 UTC', '2016-07-24T01:20:00.000000000 UTC', '2016-06-21T18:23:15.000000000 UTC'])\n      end\n\n      it 'it cannot be created using an empty formats array' do\n        code = <<-CODE\n            notice(Timestamp('2015-03-01T11:12:13', []))\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /parameter 'format' variant 1 expects size to be at least 1, got 0/)\n      end\n\n      it 'can be created from a string, array of formats, and a timezone' do\n        code = <<-CODE\n            $fmts = [\n              '%A, %d %B, %Y at %r',\n              '%b %d, %Y, %l:%M %P',\n              '%y-%m-%d %H:%M:%S'\n            ]\n            notice(Timestamp('Sunday, 28 August, 2016 at 12:15:00 PM', $fmts, 'CET'))\n            notice(Timestamp('Jul 24, 2016, 1:20 am', $fmts, 'CET'))\n            notice(Timestamp('16-06-21 18:23:15', $fmts, 'CET'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(\n          ['2016-08-28T11:15:00.000000000 UTC', '2016-07-24T00:20:00.000000000 UTC', '2016-06-21T17:23:15.000000000 UTC'])\n      end\n\n      it 'can be created from a integer that represents seconds since epoch' do\n        code = <<-CODE\n            $o = Timestamp(1433116800)\n            notice(Integer($o) == 1433116800)\n            notice($o == Timestamp('2015-06-01T00:00:00 UTC'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true))\n      end\n\n      it 'can be created from a float that represents seconds with fraction since epoch' do\n        code = <<-CODE\n            $o = Timestamp(1433116800.123456)\n            notice(Float($o) == 1433116800.123456)\n            notice($o == Timestamp('2015-06-01T00:00:00.123456 UTC'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true))\n      end\n\n      it 'matches the appropriate parameterized type' do\n        code = <<-CODE\n            $o = Timestamp('2015-05-01')\n            notice(assert_type(Timestamp['2015-03-01', '2015-09-30'], $o))\n         CODE\n        expect(eval_and_collect_notices(code)).to eq(['2015-05-01T00:00:00.000000000 UTC'])\n      end\n\n      it 'does not match an inappropriate parameterized type' do\n        code = <<-CODE\n            $o = Timestamp('2015-05-01')\n            notice(assert_type(Timestamp['2016-03-01', '2016-09-30'], $o) |$e, $a| { 'nope' })\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['nope'])\n      end\n\n      it 'can be compared to other instances' do\n        code = <<-CODE\n            $o1 = Timestamp('2015-05-01')\n            $o2 = Timestamp('2015-06-01')\n            $o3 = Timestamp('2015-06-01')\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o1 == $o3)\n            notice($o1 != $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n            notice($o2 == $o3)\n            notice($o2 != $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false true false false true true true false))\n      end\n\n      it 'can be compared to integer that represents seconds since epoch' do\n        code = <<-CODE\n            $o1 = Timestamp('2015-05-01')\n            $o2 = Timestamp('2015-06-01')\n            $o3 = 1433116800\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'integer that represents seconds since epoch can be compared to it' do\n        code = <<-CODE\n            $o1 = 1430438400\n            $o2 = 1433116800\n            $o3 = Timestamp('2015-06-01')\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'is equal to integer that represents seconds since epoch' do\n        code = <<-CODE\n            $o1 = Timestamp('2015-06-01T00:00:00 UTC')\n            $o2 = 1433116800\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice(Integer($o1) == $o2)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'integer that represents seconds is equal to it' do\n        code = <<-CODE\n            $o1 = 1433116800\n            $o2 = Timestamp('2015-06-01T00:00:00 UTC')\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice($o1 == Integer($o2))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'can be compared to float that represents seconds with fraction since epoch' do\n        code = <<-CODE\n            $o1 = Timestamp('2015-05-01T00:00:00.123456789 UTC')\n            $o2 = Timestamp('2015-06-01T00:00:00.123456789 UTC')\n            $o3 = 1433116800.123456789\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'float that represents seconds with fraction since epoch can be compared to it' do\n        code = <<-CODE\n            $o1 = 1430438400.123456789\n            $o2 = 1433116800.123456789\n            $o3 = Timestamp('2015-06-01T00:00:00.123456789 UTC')\n            notice($o1 > $o3)\n            notice($o1 >= $o3)\n            notice($o1 < $o3)\n            notice($o1 <= $o3)\n            notice($o2 > $o3)\n            notice($o2 < $o3)\n            notice($o2 >= $o3)\n            notice($o2 <= $o3)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(false false true true false false true true))\n      end\n\n      it 'is equal to float that represents seconds with fraction since epoch' do\n        code = <<-CODE\n            $o1 = Timestamp('2015-06-01T00:00:00.123456789 UTC')\n            $o2 = 1433116800.123456789\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice(Float($o1) == $o2)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'float that represents seconds with fraction is equal to it' do\n        code = <<-CODE\n            $o1 = 1433116800.123456789\n            $o2 = Timestamp('2015-06-01T00:00:00.123456789 UTC')\n            notice($o1 == $o2)\n            notice($o1 != $o2)\n            notice($o1 == Float($o2))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true false true))\n      end\n\n      it 'it cannot be compared to a Timespan' do\n        code = <<-CODE\n            notice(Timestamp() > Timespan(3))\n        CODE\n        expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Timestamps are only comparable to Timestamps, Integers, and Floats/)\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_type_set_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\n  module Types\n    describe 'The TypeSet Type' do\n      include PuppetSpec::Compiler\n\n      let(:parser) { TypeParser.singleton }\n      let(:pp_parser) { Parser::EvaluatingParser.new }\n      let(:env) { Puppet::Node::Environment.create('test', []) }\n      let(:loaders) { Loaders.new(env) }\n      let(:loader) { loaders.find_loader(nil) }\n\n      def type_set_t(name, body_string, name_authority)\n        init_literal_hash = pp_parser.parse_string(\"{#{body_string}}\").body\n        typeset = PTypeSetType.new(name, init_literal_hash, name_authority)\n        loader.set_entry(Loader::TypedName.new(:type, name, name_authority), typeset)\n        typeset\n      end\n\n      # Creates and parses an alias type declaration of a TypeSet, e.g.\n      # ```\n      # type <name> = TypeSet[{<body_string>}]\n      # ```\n      # The declaration implies the name authority {Pcore::RUNTIME_NAME_AUTHORITY}\n      #\n      # @param name [String] the name of the type set\n      # @param body [String] the body (initialization hash) of the type-set\n      # @return [PTypeSetType] the created type set\n      def parse_type_set(name, body, name_authority = Pcore::RUNTIME_NAME_AUTHORITY)\n        type_set_t(name, body, name_authority)\n        parser.parse(name, loader)\n      end\n\n      context 'when validating the initialization hash' do\n        context 'it will allow that it' do\n          it 'has no types and no references' do\n            ts = <<-OBJECT\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            OBJECT\n            expect { parse_type_set('MySet', ts) }.not_to raise_error\n          end\n\n          it 'has only references' do\n            parse_type_set('FirstSet', <<-OBJECT)\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Car => Object[{}]\n              }\n            OBJECT\n\n            expect { parse_type_set('SecondSet', <<-OBJECT) }.not_to raise_error\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              references => {\n                First => {\n                  name => 'FirstSet',\n                  version_range => '1.x'\n                }\n              }\n            OBJECT\n          end\n\n          it 'has multiple references to equally named TypeSets using different name authorities' do\n            parse_type_set('FirstSet', <<-OBJECT, 'http://example.com/ns1')\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Car => Object[{}]\n              }\n            OBJECT\n\n            parse_type_set('FirstSet', <<-OBJECT, 'http://example.com/ns2')\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Car => Object[{}]\n              }\n            OBJECT\n\n            expect { parse_type_set('SecondSet', <<-OBJECT) }.not_to raise_error\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              references => {\n                First_1 => {\n                  name_authority => 'http://example.com/ns1',\n                  name => 'FirstSet',\n                  version_range => '1.x'\n                },\n                First_2 => {\n                  name => 'FirstSet',\n                  name_authority => 'http://example.com/ns2',\n                  version_range => '1.x'\n                }\n              }\n            OBJECT\n          end\n        end\n\n        context 'it raises an error when' do\n          it 'pcore_version is missing' do\n            ts = <<-OBJECT\n            version => '1.0.0',\n            OBJECT\n            expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n              /expects a value for key 'pcore_version'/)\n          end\n\n          it 'the version is an invalid semantic version' do\n            ts = <<-OBJECT\n            version => '1.x',\n            pcore_version => '1.0.0',\n            OBJECT\n            expect { parse_type_set('MySet', ts) }.to raise_error(SemanticPuppet::Version::ValidationFailure)\n          end\n\n          it 'the pcore_version is an invalid semantic version' do\n            ts = <<-OBJECT\n            version => '1.0.0',\n            pcore_version => '1.x',\n            OBJECT\n            expect { parse_type_set('MySet', ts) }.to raise_error(SemanticPuppet::Version::ValidationFailure)\n          end\n\n          it 'the pcore_version is outside of the range of that is parsable by this runtime' do\n            ts = <<-OBJECT\n            version => '1.0.0',\n            pcore_version => '2.0.0',\n            OBJECT\n            expect { parse_type_set('MySet', ts) }.to raise_error(ArgumentError,\n              /The pcore version for TypeSet 'MySet' is not understood by this runtime. Expected range 1\\.x, got 2\\.0\\.0/)\n          end\n\n          it 'the name authority is an invalid URI' do\n            ts = <<-OBJECT\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            name_authority => 'not a valid URI'\n            OBJECT\n            expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n              /entry 'name_authority' expects a match for Pattern\\[.*\\], got 'not a valid URI'/m)\n          end\n\n          context 'the types map' do\n            it 'is empty' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                types => {}\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                /entry 'types' expects size to be at least 1, got 0/)\n            end\n\n            it 'is not a map' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                types => []\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(Puppet::Error,\n                /entry 'types' expects a Hash value, got Array/)\n            end\n\n            it 'contains values that are not types' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                types => {\n                  Car => 'brum'\n                }\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(Puppet::Error,\n                /The expression <'brum'> is not a valid type specification/)\n            end\n\n            it 'contains keys that are not SimpleNames' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                types => {\n                  car => Integer\n                }\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                /key of entry 'car' expects a match for Pattern\\[\\/\\\\A\\[A-Z\\]\\\\w\\*\\\\z\\/\\], got 'car'/)\n            end\n          end\n\n          context 'the references hash' do\n            it 'is empty' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                references => {}\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                /entry 'references' expects size to be at least 1, got 0/)\n            end\n\n            it 'is not a hash' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                references => []\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                /entry 'references' expects a Hash value, got Array/)\n            end\n\n            it 'contains something other than reference initialization maps' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                references => {Ref => 2}\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                /entry 'references' entry 'Ref' expects a Struct value, got Integer/)\n            end\n\n            it 'contains several initialization that refers to the same TypeSet' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                references => {\n                  A => { name => 'Vehicle::Cars', version_range => '1.x' },\n                  V => { name => 'Vehicle::Cars', version_range => '1.x' },\n                }\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(ArgumentError,\n                /references TypeSet 'http:\\/\\/puppet\\.com\\/2016\\.1\\/runtime\\/Vehicle::Cars' more than once using overlapping version ranges/)\n            end\n\n            it 'contains an initialization maps with an alias that collides with a type name' do\n              ts = <<-OBJECT\n                pcore_version => '1.0.0',\n                version => '1.0.0',\n                types => {\n                  Car => Object[{}]\n                },\n                references => {\n                  Car => { name => 'Vehicle::Car', version_range => '1.x' }\n                }\n              OBJECT\n              expect { parse_type_set('MySet', ts) }.to raise_error(ArgumentError,\n                /references a TypeSet using alias 'Car'. The alias collides with the name of a declared type/)\n            end\n\n            context 'contains an initialization map that' do\n              it 'has no version range' do\n                ts = <<-OBJECT\n                  pcore_version => '1.0.0',\n                  version => '1.0.0',\n                  references => { Ref => { name => 'X' } }\n                OBJECT\n                expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                  /entry 'references' entry 'Ref' expects a value for key 'version_range'/)\n              end\n\n              it 'has no name' do\n                ts = <<-OBJECT\n                  pcore_version => '1.0.0',\n                  version => '1.0.0',\n                  references => { Ref => { version_range => '1.x' } }\n                OBJECT\n                expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                  /entry 'references' entry 'Ref' expects a value for key 'name'/)\n              end\n\n              it 'has a name that is not a QRef' do\n                ts = <<-OBJECT\n                  pcore_version => '1.0.0',\n                  version => '1.0.0',\n                  references => { Ref => { name => 'cars', version_range => '1.x' } }\n                OBJECT\n                expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                  /entry 'references' entry 'Ref' entry 'name' expects a match for Pattern\\[\\/\\\\A\\[A-Z\\]\\\\w\\*\\(\\?:::\\[A-Z\\]\\\\w\\*\\)\\*\\\\z\\/\\], got 'cars'/)\n              end\n\n              it 'has a version_range that is not a valid SemVer range' do\n                ts = <<-OBJECT\n                  pcore_version => '1.0.0',\n                  version => '1.0.0',\n                  references => { Ref => { name => 'Cars', version_range => 'N' } }\n                OBJECT\n                expect { parse_type_set('MySet', ts) }.to raise_error(ArgumentError,\n                  /Unparsable version range: \"N\"/)\n              end\n\n              it 'has an alias that is not a SimpleName' do\n                ts = <<-OBJECT\n                  pcore_version => '1.0.0',\n                  version => '1.0.0',\n                  references => { 'cars' => { name => 'X', version_range => '1.x' } }\n                OBJECT\n                expect { parse_type_set('MySet', ts) }.to raise_error(TypeAssertionError,\n                  /entry 'references' key of entry 'cars' expects a match for Pattern\\[\\/\\\\A\\[A-Z\\]\\\\w\\*\\\\z\\/\\], got 'cars'/)\n              end\n            end\n          end\n        end\n      end\n\n      context 'when declaring types' do\n        it 'can declare a type Alias' do\n          expect { parse_type_set('TheSet', <<-OBJECT) }.not_to raise_error\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            types => { PositiveInt => Integer[0, default] }\n          OBJECT\n        end\n\n        it 'can declare an Object type using Object[{}]' do\n          expect { parse_type_set('TheSet', <<-OBJECT) }.not_to raise_error\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            types => { Complex => Object[{}] }\n          OBJECT\n        end\n\n        it 'can declare an Object type that references other types in the same set' do\n          expect { parse_type_set('TheSet', <<-OBJECT) }.not_to raise_error\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            types => {\n              Real => Float,\n              Complex => Object[{\n                attributes => {\n                  real => Real,\n                  imaginary => Real\n                }\n              }]\n            }\n          OBJECT\n        end\n\n        it 'can declare an alias that references itself' do\n          expect { parse_type_set('TheSet', <<-OBJECT) }.not_to raise_error\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            types => {\n              Tree => Hash[String,Variant[String,Tree]]\n            }\n          OBJECT\n        end\n\n        it 'can declare a type that references types in another type set' do\n          parse_type_set('Vehicles', <<-OBJECT)\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Car => Object[{}],\n                Bicycle => Object[{}]\n              }\n          OBJECT\n          expect { parse_type_set('TheSet', <<-OBJECT) }.not_to raise_error\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Transports => Variant[Vecs::Car,Vecs::Bicycle]\n              },\n              references => {\n                Vecs => {\n                  name => 'Vehicles',\n                  version_range => '1.x'\n                }\n              }\n          OBJECT\n        end\n\n        it 'can declare a type that references types in a type set referenced by another type set' do\n          parse_type_set('Vehicles', <<-OBJECT)\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Car => Object[{}],\n                Bicycle => Object[{}]\n              }\n          OBJECT\n          parse_type_set('Transports', <<-OBJECT)\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                Transports => Variant[Vecs::Car,Vecs::Bicycle]\n              },\n              references => {\n                Vecs => {\n                  name => 'Vehicles',\n                  version_range => '1.x'\n                }\n              }\n          OBJECT\n          expect { parse_type_set('TheSet', <<-OBJECT) }.not_to raise_error\n              version => '1.0.0',\n              pcore_version => '1.0.0',\n              types => {\n                MotorPowered => Variant[T::Vecs::Car],\n                Pedaled => Variant[T::Vecs::Bicycle],\n                All => T::Transports\n              },\n              references => {\n                T => {\n                  name => 'Transports',\n                  version_range => '1.x'\n                }\n              }\n          OBJECT\n        end\n\n        context 'allows bracket-less form' do\n          let(:logs) { [] }\n          let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }\n          let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n          let(:node) { Puppet::Node.new('example.com') }\n          let(:compiler) { Puppet::Parser::Compiler.new(node) }\n\n          def compile(code)\n            Puppet[:code] = code\n            Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) { compiler.compile }\n          end\n\n          it 'on the TypeSet declaration itself' do\n            compile(<<-PUPPET)\n              type TS = TypeSet { pcore_version => '1.0.0' }\n              notice(TS =~ Type[TypeSet])\n            PUPPET\n            expect(warnings).to be_empty\n            expect(notices).to eql(['true'])\n          end\n\n          it 'without prefix on declared types (implies Object)' do\n            compile(<<-PUPPET)\n              type TS = TypeSet {\n                pcore_version => '1.0.0',\n                types => {\n                  MyObject => { attributes => { a => Integer} }\n                }\n              }\n              notice(TS =~ Type[TypeSet])\n              notice(TS::MyObject =~ Type)\n              notice(TS::MyObject(3))\n            PUPPET\n            expect(warnings).to be_empty\n            expect(notices).to eql(['true', 'true', \"TS::MyObject({'a' => 3})\"])\n          end\n\n          it \"prefixed with QREF 'Object' on declared types\" do\n            compile(<<-PUPPET)\n              type TS = TypeSet {\n                pcore_version => '1.0.0',\n                types => {\n                  MyObject => Object { attributes => { a => Integer} }\n                }\n              }\n              notice(TS =~ Type[TypeSet])\n              notice(TS::MyObject =~ Type)\n              notice(TS::MyObject(3))\n            PUPPET\n            expect(warnings).to be_empty\n            expect(notices).to eql(['true', 'true', \"TS::MyObject({'a' => 3})\"])\n          end\n\n          it 'prefixed with QREF to declare parent on declared types' do\n            compile(<<-PUPPET)\n              type TS = TypeSet {\n                pcore_version => '1.0.0',\n                types => {\n                  MyObject => { attributes => { a => String }},\n                  MySecondObject => MyObject { attributes => { b => String }}\n                }\n              }\n              notice(TS =~ Type[TypeSet])\n              notice(TS::MySecondObject =~ Type)\n              notice(TS::MySecondObject < TS::MyObject)\n              notice(TS::MyObject('hi'))\n              notice(TS::MySecondObject('hello', 'world'))\n            PUPPET\n            expect(warnings).to be_empty\n            expect(notices).to eql(\n              ['true', 'true', 'true', \"TS::MyObject({'a' => 'hi'})\", \"TS::MySecondObject({'a' => 'hello', 'b' => 'world'})\"])\n          end\n\n          it 'and warns when parent is specified both before and inside the hash if strict == warning' do\n            Puppet[:strict] = 'warning'\n            compile(<<-PUPPET)\n              type TS = TypeSet {\n                pcore_version => '1.0.0',\n                types => {\n                  MyObject => { attributes => { a => String }},\n                  MySecondObject => MyObject { parent => MyObject, attributes => { b => String }}\n                }\n              }\n              notice(TS =~ Type[TypeSet])\n            PUPPET\n            expect(warnings).to eql([\"The key 'parent' is declared more than once\"])\n            expect(notices).to eql(['true'])\n          end\n\n          it 'and errors when parent is specified both before and inside the hash if strict == error' do\n            Puppet[:strict] = 'error'\n            expect{ compile(<<-PUPPET) }.to raise_error(/The key 'parent' is declared more than once/)\n              type TS = TypeSet {\n                pcore_version => '1.0.0',\n                types => {\n                  MyObject => { attributes => { a => String }},\n                  MySecondObject => MyObject { parent => MyObject, attributes => { b => String }}\n                }\n              }\n              notice(TS =~ Type[TypeSet])\n            PUPPET\n          end\n        end\n      end\n\n      it '#name_for method reports the name of deeply nested type correctly' do\n        tv = parse_type_set('Vehicles', <<-OBJECT)\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            types => { Car => Object[{}] }\n        OBJECT\n        parse_type_set('Transports', <<-OBJECT)\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            references => {\n              Vecs => {\n                name => 'Vehicles',\n                version_range => '1.x'\n              }\n            }\n        OBJECT\n        ts = parse_type_set('TheSet', <<-OBJECT)\n            version => '1.0.0',\n            pcore_version => '1.0.0',\n            references => {\n              T => {\n                name => 'Transports',\n                version_range => '1.x'\n              }\n            }\n        OBJECT\n        expect(ts.name_for(tv['Car'], nil)).to eql('T::Vecs::Car')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/types/p_uri_type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'URI type' do\n  context 'when used in Puppet expressions' do\n    include PuppetSpec::Compiler\n    it 'is equal to itself only' do\n      expect(eval_and_collect_notices(<<-CODE)).to eq(%w(true true false false))\n          $t = URI\n          notice(URI =~ Type[URI])\n          notice(URI == URI)\n          notice(URI < URI)\n          notice(URI > URI)\n      CODE\n    end\n\n    context \"when parameterized\" do\n      it 'is equal other types with the same parameterization' do\n        code = <<-CODE\n            notice(URI == URI[{}])\n            notice(URI['http://example.com'] == URI[scheme => http, host => 'example.com'])\n            notice(URI['urn:a:b:c'] == URI[scheme => urn, opaque => 'a:b:c'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(%w(true true true))\n      end\n\n      it 'is assignable from more qualified types' do\n        expect(eval_and_collect_notices(<<-CODE)).to eq(%w(true true true))\n          notice(URI > URI['http://example.com'])\n          notice(URI['http://example.com'] > URI['http://example.com/path'])\n          notice(URI[scheme => Enum[http, https]] > URI['http://example.com'])\n        CODE\n      end\n\n      it 'is not assignable unless scheme is assignable' do\n        expect(eval_and_collect_notices(<<-CODE)).to eq(%w(false))\n          notice(URI[scheme => Enum[http, https]] > URI[scheme => 'ftp'])\n        CODE\n      end\n\n      it 'presents parsable string form' do\n        code = <<-CODE\n          notice(URI['https://user:password@www.example.com:3000/some/path?x=y#frag'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\n          \"URI[{'scheme' => 'https', 'userinfo' => 'user:password', 'host' => 'www.example.com', 'port' => '3000', 'path' => '/some/path', 'query' => 'x=y', 'fragment' => 'frag'}]\",\n        ])\n      end\n    end\n\n    context 'a URI instance' do\n      it 'can be created from a string' do\n        code = <<-CODE\n            $o = URI('https://example.com/a/b')\n            notice(String($o, '%p'))\n            notice(type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\n          \"URI('https://example.com/a/b')\",\n          \"URI[{'scheme' => 'https', 'host' => 'example.com', 'path' => '/a/b'}]\"\n        ])\n      end\n\n      it 'which is opaque, can be created from a string' do\n        code = <<-CODE\n            $o = URI('urn:a:b:c')\n            notice(String($o, '%p'))\n            notice(type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\n          \"URI('urn:a:b:c')\",\n          \"URI[{'scheme' => 'urn', 'opaque' => 'a:b:c'}]\"\n        ])\n      end\n\n      it 'can be created from a hash' do\n        code = <<-CODE\n            $o = URI(scheme => 'https', host => 'example.com', path => '/a/b')\n            notice(String($o, '%p'))\n            notice(type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\n          \"URI('https://example.com/a/b')\",\n          \"URI[{'scheme' => 'https', 'host' => 'example.com', 'path' => '/a/b'}]\"\n        ])\n      end\n\n      it 'which is opaque, can be created from a hash' do\n        code = <<-CODE\n            $o = URI(scheme => 'urn', opaque => 'a:b:c')\n            notice(String($o, '%p'))\n            notice(type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\n          \"URI('urn:a:b:c')\",\n          \"URI[{'scheme' => 'urn', 'opaque' => 'a:b:c'}]\"\n        ])\n      end\n\n      it 'is an instance of its type' do\n        code = <<-CODE\n            $o = URI('https://example.com/a/b')\n            notice($o =~ type($o))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['true'])\n      end\n\n      it 'is an instance of matching parameterized URI' do\n        code = <<-CODE\n            $o = URI('https://example.com/a/b')\n            notice($o =~ URI[scheme => https, host => 'example.com'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['true'])\n      end\n\n      it 'is an instance of matching default URI' do\n        code = <<-CODE\n            $o = URI('https://example.com/a/b')\n            notice($o =~ URI)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['true'])\n      end\n\n      it 'path is not matched by opaque' do\n        code = <<-CODE\n            $o = URI('urn:a:b:c')\n            notice($o =~ URI[path => 'a:b:c'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['false'])\n      end\n\n      it 'opaque is not matched by path' do\n        code = <<-CODE\n            $o = URI('https://example.com/a/b')\n            notice($o =~ URI[opaque => '/a/b'])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['false'])\n      end\n\n      it 'is not an instance unless parameters matches' do\n        code = <<-CODE\n            $o = URI('https://example.com/a/b')\n            notice($o =~ URI[scheme => http])\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['false'])\n      end\n\n      it 'individual parts of URI can be accessed using accessor methods' do\n        code = <<-CODE\n            $o = URI('https://bob:pw@example.com:8080/a/b?a=b#frag')\n            notice($o.scheme)\n            notice($o.userinfo)\n            notice($o.host)\n            notice($o.port)\n            notice($o.path)\n            notice($o.query)\n            notice($o.fragment)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['https', 'bob:pw', 'example.com', '8080', '/a/b', 'a=b', 'frag'])\n      end\n\n      it 'individual parts of opaque URI can be accessed using accessor methods' do\n        code = <<-CODE\n            $o = URI('urn:a:b:c')\n            notice($o.scheme)\n            notice($o.opaque)\n        CODE\n        expect(eval_and_collect_notices(code)).to eq(['urn', 'a:b:c'])\n      end\n\n      it 'An URI can be merged with a String using the + operator' do\n        code = <<-CODE\n          notice(String(URI('https://example.com') + '/a/b', '%p'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\"URI('https://example.com/a/b')\"])\n      end\n\n      it 'An URI can be merged with another URI using the + operator' do\n        code = <<-CODE\n          notice(String(URI('https://example.com') + URI('/a/b'), '%p'))\n        CODE\n        expect(eval_and_collect_notices(code)).to eq([\"URI('https://example.com/a/b')\"])\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/recursion_guard_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops/types/recursion_guard'\n\nmodule Puppet::Pops::Types\ndescribe 'the RecursionGuard' do\n  let(:guard) { RecursionGuard.new }\n\n  it \"should detect recursion in 'this' context\" do\n    x = Object.new\n    expect(guard.add_this(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n    expect(guard.add_this(x)).to eq(RecursionGuard::SELF_RECURSION_IN_THIS)\n  end\n\n  it \"should detect recursion in 'that' context\" do\n    x = Object.new\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n    expect(guard.add_that(x)).to eq(RecursionGuard::SELF_RECURSION_IN_THAT)\n  end\n\n  it \"should keep 'this' and 'that' context separate\" do\n    x = Object.new\n    expect(guard.add_this(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n  end\n\n  it \"should detect when there's a recursion in both 'this' and 'that' context\" do\n    x = Object.new\n    y = Object.new\n    expect(guard.add_this(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n    expect(guard.add_that(y)).to eq(RecursionGuard::NO_SELF_RECURSION)\n    expect(guard.add_this(x)).to eq(RecursionGuard::SELF_RECURSION_IN_THIS)\n    expect(guard.add_that(y)).to eq(RecursionGuard::SELF_RECURSION_IN_BOTH)\n  end\n\n  it \"should report that 'this' is recursive after a recursion has been detected\" do\n    x = Object.new\n    guard.add_this(x)\n    guard.add_this(x)\n    expect(guard.recursive_this?(x)).to be_truthy\n  end\n\n  it \"should report that 'that' is recursive after a recursion has been detected\" do\n    x = Object.new\n    guard.add_that(x)\n    guard.add_that(x)\n    expect(guard.recursive_that?(x)).to be_truthy\n  end\n\n  it \"should not report that 'this' is recursive after a recursion of 'that' has been detected\" do\n    x = Object.new\n    guard.add_that(x)\n    guard.add_that(x)\n    expect(guard.recursive_this?(x)).to be_falsey\n  end\n\n  it \"should not report that 'that' is recursive after a recursion of 'this' has been detected\" do\n    x = Object.new\n    guard.add_that(x)\n    guard.add_that(x)\n    expect(guard.recursive_this?(x)).to be_falsey\n  end\n\n  it \"should not call 'hash' on an added instance\" do\n    x = double()\n    expect(x).not_to receive(:hash)\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n  end\n\n  it \"should not call '==' on an added instance\" do\n    x = double()\n    expect(x).not_to receive(:==)\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n  end\n\n  it \"should not call 'eq?' on an added instance\" do\n    x = double()\n    expect(x).not_to receive(:eq?)\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n  end\n\n  it \"should not call 'eql?' on an added instance\" do\n    x = double()\n    expect(x).not_to receive(:eql?)\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n  end\n\n  it \"should not call 'equal?' on an added instance\" do\n    x = double()\n    expect(x).not_to receive(:equal?)\n    expect(guard.add_that(x)).to eq(RecursionGuard::NO_SELF_RECURSION)\n  end\nend\nend"
  },
  {
    "path": "spec/unit/pops/types/ruby_generator_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/pops/types/ruby_generator'\n\ndef root_binding\n  return binding\nend\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Puppet Ruby Generator' do\n  include PuppetSpec::Compiler\n\n  let!(:parser) { TypeParser.singleton }\n  let(:generator) { RubyGenerator.new }\n\n  context 'when generating classes for Objects having attribute names that are Ruby reserved words' do\n    let (:source) { <<-PUPPET }\n      type MyObject = Object[{\n        attributes => {\n          alias => String,\n          begin => String,\n          break => String,\n          def => String,\n          do => String,\n          end => String,\n          ensure => String,\n          for => String,\n          module => String,\n          next => String,\n          nil => String,\n          not => String,\n          redo => String,\n          rescue => String,\n          retry => String,\n          return => String,\n          self => String,\n          super => String,\n          then => String,\n          until => String,\n          when => String,\n          while => String,\n          yield => String,\n        },\n      }]\n      $x = MyObject({\n        alias => 'value of alias',\n        begin => 'value of begin',\n        break => 'value of break',\n        def => 'value of def',\n        do => 'value of do',\n        end => 'value of end',\n        ensure => 'value of ensure',\n        for => 'value of for',\n        module => 'value of module',\n        next => 'value of next',\n        nil => 'value of nil',\n        not => 'value of not',\n        redo => 'value of redo',\n        rescue => 'value of rescue',\n        retry => 'value of retry',\n        return => 'value of return',\n        self => 'value of self',\n        super => 'value of super',\n        then => 'value of then',\n        until => 'value of until',\n        when => 'value of when',\n        while => 'value of while',\n        yield => 'value of yield',\n      })\n      notice($x.alias)\n      notice($x.begin)\n      notice($x.break)\n      notice($x.def)\n      notice($x.do)\n      notice($x.end)\n      notice($x.ensure)\n      notice($x.for)\n      notice($x.module)\n      notice($x.next)\n      notice($x.nil)\n      notice($x.not)\n      notice($x.redo)\n      notice($x.rescue)\n      notice($x.retry)\n      notice($x.return)\n      notice($x.self)\n      notice($x.super)\n      notice($x.then)\n      notice($x.until)\n      notice($x.when)\n      notice($x.while)\n      notice($x.yield)\n    PUPPET\n\n    it 'can create an instance and access all attributes' do\n      expect(eval_and_collect_notices(source)).to eql([\n        'value of alias',\n        'value of begin',\n        'value of break',\n        'value of def',\n        'value of do',\n        'value of end',\n        'value of ensure',\n        'value of for',\n        'value of module',\n        'value of next',\n        'value of nil',\n        'value of not',\n        'value of redo',\n        'value of rescue',\n        'value of retry',\n        'value of return',\n        'value of self',\n        'value of super',\n        'value of then',\n        'value of until',\n        'value of when',\n        'value of while',\n        'value of yield',\n      ])\n    end\n  end\n\n  context 'when generating classes for Objects having function names that are Ruby reserved words' do\n    let (:source) { <<-PUPPET }\n      type MyObject = Object[{\n        functions => {\n          alias  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of alias'\" }}},\n          begin  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of begin'\" }}},\n          break  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of break'\" }}},\n          def    => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of def'\" }}},\n          do     => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of do'\" }}},\n          end    => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of end'\" }}},\n          ensure => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of ensure'\" }}},\n          for    => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of for'\" }}},\n          module => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of module'\" }}},\n          next   => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of next'\" }}},\n          nil    => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of nil'\" }}},\n          not    => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of not'\" }}},\n          redo   => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of redo'\" }}},\n          rescue => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of rescue'\" }}},\n          retry  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of retry'\" }}},\n          return => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of return'\" }}},\n          self   => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of self'\" }}},\n          super  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of super'\" }}},\n          then   => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of then'\" }}},\n          until  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of until'\" }}},\n          when   => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of when'\" }}},\n          while  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of while'\" }}},\n          yield  => { type => Callable[[0,0],String], annotations => {RubyMethod => { 'body' => \"'value of yield'\" }}},\n        },\n      }]\n      $x = MyObject()\n      notice($x.alias)\n      notice($x.begin)\n      notice($x.break)\n      notice($x.def)\n      notice($x.do)\n      notice($x.end)\n      notice($x.ensure)\n      notice($x.for)\n      notice($x.module)\n      notice($x.next)\n      notice($x.nil)\n      notice($x.not)\n      notice($x.redo)\n      notice($x.rescue)\n      notice($x.retry)\n      notice($x.return)\n      notice($x.self)\n      notice($x.super)\n      notice($x.then)\n      notice($x.until)\n      notice($x.when)\n      notice($x.while)\n      notice($x.yield)\n    PUPPET\n\n    it 'can create an instance and call all functions' do\n      expect(eval_and_collect_notices(source)).to eql([\n        'value of alias',\n        'value of begin',\n        'value of break',\n        'value of def',\n        'value of do',\n        'value of end',\n        'value of ensure',\n        'value of for',\n        'value of module',\n        'value of next',\n        'value of nil',\n        'value of not',\n        'value of redo',\n        'value of rescue',\n        'value of retry',\n        'value of return',\n        'value of self',\n        'value of super',\n        'value of then',\n        'value of until',\n        'value of when',\n        'value of while',\n        'value of yield',\n      ])\n    end\n  end\n\n  context 'when generating from Object types' do\n    let (:type_decls) { <<-CODE.unindent }\n      type MyModule::FirstGenerated = Object[{\n        attributes => {\n          name => String,\n          age  => { type => Integer, value => 30 },\n          what => { type => String, value => 'what is this', kind => constant },\n          uc_name => {\n            type => String,\n            kind => derived,\n            annotations => {\n              RubyMethod => { body => '@name.upcase' }\n            }\n          },\n          other_name => {\n            type => String,\n            kind => derived\n          },\n        },\n        functions => {\n          some_other => {\n            type => Callable[1,1]\n          },\n          name_and_age => {\n            type => Callable[1,1],\n            annotations => {\n              RubyMethod => {\n                parameters => 'joiner',\n                body => '\"\\#{@name}\\#{joiner}\\#{@age}\"'\n              }\n            }\n          },\n          '[]' => {\n            type => Callable[1,1],\n            annotations => {\n              RubyMethod => {\n                parameters => 'key',\n                body => @(EOF)\n                  case key\n                  when 'name'\n                    name\n                  when 'age'\n                    age\n                  else\n                    nil\n                  end\n                |-EOF\n              }\n            }\n          }\n        }\n      }]\n      type MyModule::SecondGenerated = Object[{\n        parent => MyModule::FirstGenerated,\n        attributes => {\n          address => String,\n          zipcode => String,\n          email => String,\n          another => { type => Optional[MyModule::FirstGenerated], value => undef },\n          number => Integer,\n          aref => { type => Optional[MyModule::FirstGenerated], value => undef, kind => reference }\n        }\n      }]\n      CODE\n\n    let(:type_usage) { '' }\n    let(:source) { type_decls + type_usage }\n\n    context 'when generating anonymous classes' do\n\n      loader = nil\n\n      let(:first_type) { parser.parse('MyModule::FirstGenerated', loader) }\n      let(:second_type) { parser.parse('MyModule::SecondGenerated', loader) }\n      let(:first) { generator.create_class(first_type) }\n      let(:second) { generator.create_class(second_type) }\n      let(:notices) { [] }\n\n      before(:each) do\n        notices.concat(eval_and_collect_notices(source) do |topscope|\n          loader = topscope.compiler.loaders.find_loader(nil)\n        end)\n      end\n\n      context 'the generated class' do\n        it 'inherits the PuppetObject module' do\n          expect(first < PuppetObject).to be_truthy\n        end\n\n        it 'is the superclass of a generated subclass' do\n          expect(second < first).to be_truthy\n        end\n      end\n\n      context 'the #create class method' do\n        it 'has an arity that reflects optional arguments' do\n          expect(first.method(:create).arity).to eql(-2)\n          expect(second.method(:create).arity).to eql(-6)\n        end\n\n        it 'creates an instance of the class' do\n          inst = first.create('Bob Builder', 52)\n          expect(inst).to be_a(first)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'created instance has a [] method' do\n          inst = first.create('Bob Builder', 52)\n          expect(inst['name']).to eq('Bob Builder')\n          expect(inst['age']).to eq(52)\n        end\n\n        it 'will perform type assertion of the arguments' do\n          expect { first.create('Bob Builder', '52') }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got String')\n          )\n        end\n\n        it 'will not accept nil as given value for an optional parameter that does not accept nil' do\n          expect { first.create('Bob Builder', nil) }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got Undef')\n          )\n        end\n\n        it 'reorders parameters to but the optional parameters last' do\n          inst = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.address).to eq('42 Cool Street')\n          expect(inst.zipcode).to eq('12345')\n          expect(inst.email).to eq('bob@example.com')\n          expect(inst.number).to eq(23)\n          expect(inst.what).to eql('what is this')\n          expect(inst.age).to eql(30)\n          expect(inst.another).to be_nil\n        end\n\n        it 'generates a code body for derived attribute from a RubyMethod body attribute' do\n          inst = first.create('Bob Builder', 52)\n          expect(inst.uc_name).to eq('BOB BUILDER')\n        end\n\n        it \"generates a code body with 'not implemented' in the absense of a RubyMethod body attribute\" do\n          inst = first.create('Bob Builder', 52)\n          expect { inst.other_name }.to raise_error(/no method is implemented for derived attribute MyModule::FirstGenerated\\[other_name\\]/)\n        end\n\n        it 'generates parameter list and a code body for derived function from a RubyMethod body attribute' do\n          inst = first.create('Bob Builder', 52)\n          expect(inst.name_and_age(' of age ')).to eq('Bob Builder of age 52')\n        end\n      end\n\n      context 'the #from_hash class method' do\n        it 'has an arity of one' do\n          expect(first.method(:from_hash).arity).to eql(1)\n          expect(second.method(:from_hash).arity).to eql(1)\n        end\n\n        it 'creates an instance of the class' do\n          inst = first.from_hash('name' => 'Bob Builder', 'age' => 52)\n          expect(inst).to be_a(first)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'accepts an initializer where optional keys are missing' do\n          inst = first.from_hash('name' => 'Bob Builder')\n          expect(inst).to be_a(first)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(30)\n        end\n\n        it 'does not accept an initializer where optional values are nil and type does not accept nil' do\n          expect { first.from_hash('name' => 'Bob Builder', 'age' => nil) }.to(\n            raise_error(TypeAssertionError,\n              \"MyModule::FirstGenerated initializer has wrong type, entry 'age' expects an Integer value, got Undef\")\n          )\n        end\n      end\n\n      context 'creates an instance' do\n        it 'that the TypeCalculator infers to the Object type' do\n          expect(TypeCalculator.infer(first.from_hash('name' => 'Bob Builder'))).to eq(first_type)\n        end\n\n        it \"where attributes of kind 'reference' are not considered part of #_pcore_all_contents\" do\n          inst = first.from_hash('name' => 'Bob Builder')\n          wrinst = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23, 40, inst, inst)\n          results = []\n          wrinst._pcore_all_contents([]) { |v| results << v }\n          expect(results).to eq([inst])\n        end\n      end\n\n      context 'when used from Puppet' do\n        let(:type_usage) { <<-PUPPET.unindent }\n          $i = MyModule::FirstGenerated('Bob Builder', 52)\n          notice($i['name'])\n          notice($i['age'])\n        PUPPET\n\n        it 'The [] method is present on a created instance' do\n          expect(notices).to eql(['Bob Builder', '52'])\n        end\n      end\n    end\n\n    context 'when generating static code' do\n      module_def = nil\n\n      before(:each) do\n        # Ideally, this would be in a before(:all) but that is impossible since lots of Puppet\n        # environment specific settings are configured by the spec_helper in before(:each)\n        if module_def.nil?\n          first_type = nil\n          second_type = nil\n          eval_and_collect_notices(source) do\n            first_type = parser.parse('MyModule::FirstGenerated')\n            second_type = parser.parse('MyModule::SecondGenerated')\n\n            Loaders.implementation_registry.register_type_mapping(\n              PRuntimeType.new(:ruby, [/^PuppetSpec::RubyGenerator::(\\w+)$/, 'MyModule::\\1']),\n              [/^MyModule::(\\w+)$/, 'PuppetSpec::RubyGenerator::\\1'])\n\n            module_def = generator.module_definition([first_type, second_type], 'Generated stuff')\n          end\n          Loaders.clear\n          Puppet[:code] = nil\n\n          # Create the actual classes in the PuppetSpec::RubyGenerator module\n          Puppet.override(:loaders => Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))) do\n            eval(module_def, root_binding)\n          end\n        end\n      end\n\n      after(:all) do\n        # Don't want generated module to leak outside this test\n        PuppetSpec.send(:remove_const, :RubyGenerator) if PuppetSpec.const_defined?(:RubyGenerator)\n      end\n\n      it 'the #_pcore_type class method returns a resolved Type' do\n        first_type = PuppetSpec::RubyGenerator::FirstGenerated._pcore_type\n        expect(first_type).to be_a(PObjectType)\n        second_type = PuppetSpec::RubyGenerator::SecondGenerated._pcore_type\n        expect(second_type).to be_a(PObjectType)\n        expect(second_type.parent).to eql(first_type)\n      end\n\n      context 'the #create class method' do\n        it 'has an arity that reflects optional arguments' do\n          expect(PuppetSpec::RubyGenerator::FirstGenerated.method(:create).arity).to eql(-2)\n          expect(PuppetSpec::RubyGenerator::SecondGenerated.method(:create).arity).to eql(-6)\n        end\n\n        it 'creates an instance of the class' do\n          inst = PuppetSpec::RubyGenerator::FirstGenerated.create('Bob Builder', 52)\n          expect(inst).to be_a(PuppetSpec::RubyGenerator::FirstGenerated)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'will perform type assertion of the arguments' do\n          expect { PuppetSpec::RubyGenerator::FirstGenerated.create('Bob Builder', '52') }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got String')\n          )\n        end\n\n        it 'will not accept nil as given value for an optional parameter that does not accept nil' do\n          expect { PuppetSpec::RubyGenerator::FirstGenerated.create('Bob Builder', nil) }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got Undef')\n          )\n        end\n\n        it 'reorders parameters to but the optional parameters last' do\n          inst = PuppetSpec::RubyGenerator::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.address).to eq('42 Cool Street')\n          expect(inst.zipcode).to eq('12345')\n          expect(inst.email).to eq('bob@example.com')\n          expect(inst.number).to eq(23)\n          expect(inst.what).to eql('what is this')\n          expect(inst.age).to eql(30)\n          expect(inst.another).to be_nil\n        end\n      end\n\n      context 'the #from_hash class method' do\n        it 'has an arity of one' do\n          expect(PuppetSpec::RubyGenerator::FirstGenerated.method(:from_hash).arity).to eql(1)\n          expect(PuppetSpec::RubyGenerator::SecondGenerated.method(:from_hash).arity).to eql(1)\n        end\n\n        it 'creates an instance of the class' do\n          inst = PuppetSpec::RubyGenerator::FirstGenerated.from_hash('name' => 'Bob Builder', 'age' => 52)\n          expect(inst).to be_a(PuppetSpec::RubyGenerator::FirstGenerated)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'accepts an initializer where optional keys are missing' do\n          inst = PuppetSpec::RubyGenerator::FirstGenerated.from_hash('name' => 'Bob Builder')\n          expect(inst).to be_a(PuppetSpec::RubyGenerator::FirstGenerated)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(30)\n        end\n\n        it 'does not accept an initializer where optional values are nil and type does not accept nil' do\n          expect { PuppetSpec::RubyGenerator::FirstGenerated.from_hash('name' => 'Bob Builder', 'age' => nil) }.to(\n            raise_error(TypeAssertionError,\n              \"MyModule::FirstGenerated initializer has wrong type, entry 'age' expects an Integer value, got Undef\")\n          )\n        end\n      end\n    end\n  end\n\n  context 'when generating from TypeSets' do\n    def source\n      <<-CODE\n        type MyModule = TypeSet[{\n          pcore_version => '1.0.0',\n          version => '1.0.0',\n          types   => {\n            MyInteger => Integer,\n            FirstGenerated => Object[{\n              attributes => {\n                name => String,\n                age  => { type => Integer, value => 30 },\n                what => { type => String, value => 'what is this', kind => constant }\n              }\n            }],\n            SecondGenerated => Object[{\n              parent => FirstGenerated,\n              attributes => {\n                address => String,\n                zipcode => String,\n                email => String,\n                another => { type => Optional[FirstGenerated], value => undef },\n                number => MyInteger\n              }\n            }]\n          },\n        }]\n\n        type OtherModule = TypeSet[{\n          pcore_version => '1.0.0',\n          version => '1.0.0',\n          types   => {\n            MyFloat => Float,\n            ThirdGenerated => Object[{\n              attributes => {\n                first => My::FirstGenerated\n              }\n            }],\n            FourthGenerated => Object[{\n              parent => My::SecondGenerated,\n              attributes => {\n                complex => { type => Optional[ThirdGenerated], value => undef },\n                n1 => My::MyInteger,\n                n2 => MyFloat\n              }\n            }]\n          },\n          references => {\n            My => { name => 'MyModule', version_range => '1.x' }\n          }\n        }]\n      CODE\n    end\n\n    context 'when generating anonymous classes' do\n\n      typeset = nil\n\n      let(:first_type) { typeset['My::FirstGenerated'] }\n      let(:second_type) { typeset['My::SecondGenerated'] }\n      let(:third_type) { typeset['ThirdGenerated'] }\n      let(:fourth_type) { typeset['FourthGenerated'] }\n      let(:first) { generator.create_class(first_type) }\n      let(:second) { generator.create_class(second_type) }\n      let(:third) { generator.create_class(third_type) }\n      let(:fourth) { generator.create_class(fourth_type) }\n\n      before(:each) do\n        eval_and_collect_notices(source) do\n          typeset = parser.parse('OtherModule')\n        end\n      end\n\n      after(:each) { typeset = nil }\n\n      context 'the typeset' do\n        it 'produces expected string representation' do\n          expect(typeset.to_s).to eq(\n            \"TypeSet[{pcore_version => '1.0.0', name_authority => 'http://puppet.com/2016.1/runtime', name => 'OtherModule', version => '1.0.0', types => {\"+\n              \"MyFloat => Float, \"+\n              \"ThirdGenerated => Object[{attributes => {'first' => My::FirstGenerated}}], \"+\n              \"FourthGenerated => Object[{parent => My::SecondGenerated, attributes => {\"+\n                \"'complex' => {type => Optional[ThirdGenerated], value => undef}, \"+\n                \"'n1' => My::MyInteger, \"+\n                \"'n2' => MyFloat\"+\n              \"}}]}, references => {My => {'name' => 'MyModule', 'version_range' => '1.x'}}}]\")\n        end\n      end\n\n      context 'the generated class' do\n        it 'inherits the PuppetObject module' do\n          expect(first < PuppetObject).to be_truthy\n        end\n\n        it 'is the superclass of a generated subclass' do\n          expect(second < first).to be_truthy\n        end\n      end\n\n      context 'the #create class method' do\n        it 'has an arity that reflects optional arguments' do\n          expect(first.method(:create).arity).to eql(-2)\n          expect(second.method(:create).arity).to eql(-6)\n          expect(third.method(:create).arity).to eql(1)\n          expect(fourth.method(:create).arity).to eql(-8)\n        end\n\n        it 'creates an instance of the class' do\n          inst = first.create('Bob Builder', 52)\n          expect(inst).to be_a(first)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'will perform type assertion of the arguments' do\n          expect { first.create('Bob Builder', '52') }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got String')\n          )\n        end\n\n        it 'will not accept nil as given value for an optional parameter that does not accept nil' do\n          expect { first.create('Bob Builder', nil) }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got Undef')\n          )\n        end\n\n        it 'reorders parameters to but the optional parameters last' do\n          inst = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.address).to eq('42 Cool Street')\n          expect(inst.zipcode).to eq('12345')\n          expect(inst.email).to eq('bob@example.com')\n          expect(inst.number).to eq(23)\n          expect(inst.what).to eql('what is this')\n          expect(inst.age).to eql(30)\n          expect(inst.another).to be_nil\n        end\n\n        it 'two instances with the same attribute values are equal using #eql?' do\n          inst1 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst1.eql?(inst2)).to be_truthy\n        end\n\n        it 'two instances with the same attribute values are equal using #==' do\n          inst1 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst1 == inst2).to be_truthy\n        end\n\n        it 'two instances with the different attribute in super class values are different' do\n          inst1 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = second.create('Bob Engineer', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst1 == inst2).to be_falsey\n        end\n\n        it 'two instances with the different attribute in sub class values are different' do\n          inst1 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = second.create('Bob Builder', '42 Cool Street', '12345', 'bob@other.com', 23)\n          expect(inst1 == inst2).to be_falsey\n        end\n      end\n\n      context 'the #from_hash class method' do\n        it 'has an arity of one' do\n          expect(first.method(:from_hash).arity).to eql(1)\n          expect(second.method(:from_hash).arity).to eql(1)\n        end\n\n        it 'creates an instance of the class' do\n          inst = first.from_hash('name' => 'Bob Builder', 'age' => 52)\n          expect(inst).to be_a(first)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'accepts an initializer where optional keys are missing' do\n          inst = first.from_hash('name' => 'Bob Builder')\n          expect(inst).to be_a(first)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(30)\n        end\n\n        it 'does not accept an initializer where optional values are nil and type does not accept nil' do\n          expect { first.from_hash('name' => 'Bob Builder', 'age' => nil) }.to(\n            raise_error(TypeAssertionError,\n              \"MyModule::FirstGenerated initializer has wrong type, entry 'age' expects an Integer value, got Undef\")\n          )\n        end\n      end\n\n      context 'creates an instance' do\n        it 'that the TypeCalculator infers to the Object type' do\n          expect(TypeCalculator.infer(first.from_hash('name' => 'Bob Builder'))).to eq(first_type)\n        end\n      end\n    end\n\n    context 'when generating static code' do\n      module_def = nil\n      module_def2 = nil\n\n      before(:each) do\n        # Ideally, this would be in a before(:all) but that is impossible since lots of Puppet\n        # environment specific settings are configured by the spec_helper in before(:each)\n        if module_def.nil?\n          eval_and_collect_notices(source) do\n            typeset1 = parser.parse('MyModule')\n            typeset2 = parser.parse('OtherModule')\n\n            Loaders.implementation_registry.register_type_mapping(\n              PRuntimeType.new(:ruby, [/^PuppetSpec::RubyGenerator::My::(\\w+)$/, 'MyModule::\\1']),\n              [/^MyModule::(\\w+)$/, 'PuppetSpec::RubyGenerator::My::\\1'])\n\n            Loaders.implementation_registry.register_type_mapping(\n              PRuntimeType.new(:ruby, [/^PuppetSpec::RubyGenerator::Other::(\\w+)$/, 'OtherModule::\\1']),\n              [/^OtherModule::(\\w+)$/, 'PuppetSpec::RubyGenerator::Other::\\1'])\n\n            module_def = generator.module_definition_from_typeset(typeset1)\n            module_def2 = generator.module_definition_from_typeset(typeset2)\n          end\n          Loaders.clear\n          Puppet[:code] = nil\n\n          # Create the actual classes in the PuppetSpec::RubyGenerator module\n          Puppet.override(:loaders => Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))) do\n            eval(module_def, root_binding)\n            eval(module_def2, root_binding)\n          end\n        end\n      end\n\n      after(:all) do\n        # Don't want generated module to leak outside this test\n        PuppetSpec.send(:remove_const, :RubyGenerator) if PuppetSpec.const_defined?(:RubyGenerator)\n      end\n\n      it 'the #_pcore_type class method returns a resolved Type' do\n        first_type = PuppetSpec::RubyGenerator::My::FirstGenerated._pcore_type\n        expect(first_type).to be_a(PObjectType)\n        second_type = PuppetSpec::RubyGenerator::My::SecondGenerated._pcore_type\n        expect(second_type).to be_a(PObjectType)\n        expect(second_type.parent).to eql(first_type)\n      end\n\n      context 'the #create class method' do\n        it 'has an arity that reflects optional arguments' do\n          expect(PuppetSpec::RubyGenerator::My::FirstGenerated.method(:create).arity).to eql(-2)\n          expect(PuppetSpec::RubyGenerator::My::SecondGenerated.method(:create).arity).to eql(-6)\n        end\n\n        it 'creates an instance of the class' do\n          inst = PuppetSpec::RubyGenerator::My::FirstGenerated.create('Bob Builder', 52)\n          expect(inst).to be_a(PuppetSpec::RubyGenerator::My::FirstGenerated)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'will perform type assertion of the arguments' do\n          expect { PuppetSpec::RubyGenerator::My::FirstGenerated.create('Bob Builder', '52') }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got String')\n          )\n        end\n\n        it 'will not accept nil as given value for an optional parameter that does not accept nil' do\n          expect { PuppetSpec::RubyGenerator::My::FirstGenerated.create('Bob Builder', nil) }.to(\n            raise_error(TypeAssertionError,\n              'MyModule::FirstGenerated[age] has wrong type, expects an Integer value, got Undef')\n          )\n        end\n\n        it 'reorders parameters to but the optional parameters last' do\n          inst = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.address).to eq('42 Cool Street')\n          expect(inst.zipcode).to eq('12345')\n          expect(inst.email).to eq('bob@example.com')\n          expect(inst.number).to eq(23)\n          expect(inst.what).to eql('what is this')\n          expect(inst.age).to eql(30)\n          expect(inst.another).to be_nil\n        end\n\n        it 'two instances with the same attribute values are equal using #eql?' do\n          inst1 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst1.eql?(inst2)).to be_truthy\n        end\n\n        it 'two instances with the same attribute values are equal using #==' do\n          inst1 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst1 == inst2).to be_truthy\n        end\n\n        it 'two instances with the different attribute in super class values are different' do\n          inst1 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Engineer', '42 Cool Street', '12345', 'bob@example.com', 23)\n          expect(inst1 == inst2).to be_falsey\n        end\n\n        it 'two instances with the different attribute in sub class values are different' do\n          inst1 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@example.com', 23)\n          inst2 = PuppetSpec::RubyGenerator::My::SecondGenerated.create('Bob Builder', '42 Cool Street', '12345', 'bob@other.com', 23)\n          expect(inst1 == inst2).to be_falsey\n        end\n      end\n\n      context 'the #from_hash class method' do\n        it 'has an arity of one' do\n          expect(PuppetSpec::RubyGenerator::My::FirstGenerated.method(:from_hash).arity).to eql(1)\n          expect(PuppetSpec::RubyGenerator::My::SecondGenerated.method(:from_hash).arity).to eql(1)\n        end\n\n        it 'creates an instance of the class' do\n          inst = PuppetSpec::RubyGenerator::My::FirstGenerated.from_hash('name' => 'Bob Builder', 'age' => 52)\n          expect(inst).to be_a(PuppetSpec::RubyGenerator::My::FirstGenerated)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(52)\n        end\n\n        it 'accepts an initializer where optional keys are missing' do\n          inst = PuppetSpec::RubyGenerator::My::FirstGenerated.from_hash('name' => 'Bob Builder')\n          expect(inst).to be_a(PuppetSpec::RubyGenerator::My::FirstGenerated)\n          expect(inst.name).to eq('Bob Builder')\n          expect(inst.age).to eq(30)\n        end\n\n        it 'does not accept an initializer where optional values are nil and type does not accept nil' do\n          expect { PuppetSpec::RubyGenerator::My::FirstGenerated.from_hash('name' => 'Bob Builder', 'age' => nil) }.to(\n            raise_error(TypeAssertionError,\n              \"MyModule::FirstGenerated initializer has wrong type, entry 'age' expects an Integer value, got Undef\")\n          )\n        end\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/string_converter_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe 'The string converter' do\n  let(:converter) { Puppet::Pops::Types::StringConverter.singleton }\n  let(:factory) { Puppet::Pops::Types::TypeFactory }\n  let(:format) { Puppet::Pops::Types::StringConverter::Format }\n  let(:binary) { Puppet::Pops::Types::PBinaryType::Binary }\n\n  describe 'helper Format' do\n    it 'parses a single character like \"%d\" as a format' do\n      fmt = format.new(\"%d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.alt()).to be(false)\n      expect(fmt.left()).to be(false)\n      expect(fmt.width()).to be_nil\n      expect(fmt.prec()).to be_nil\n      expect(fmt.plus()).to eq(:ignore)\n    end\n\n    it 'alternative form can be given with \"%#d\"' do\n      fmt = format.new(\"%#d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.alt()).to be(true)\n    end\n\n    it 'left adjust can be given with \"%-d\"' do\n      fmt = format.new(\"%-d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.left()).to be(true)\n    end\n\n    it 'plus sign can be used to indicate how positive numbers are displayed' do\n      fmt = format.new(\"%+d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.plus()).to eq(:plus)\n    end\n\n    it 'a space can be used to output \" \" instead of \"+\" for positive numbers' do\n      fmt = format.new(\"% d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.plus()).to eq(:space)\n    end\n\n    it 'padding with zero can be specified with a \"0\" flag' do\n      fmt = format.new(\"%0d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.zero_pad()).to be(true)\n    end\n\n    it 'width can be specified as an integer >= 1' do\n      fmt = format.new(\"%1d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.width()).to be(1)\n      fmt = format.new(\"%10d\")\n      expect(fmt.width()).to be(10)\n    end\n\n    it 'precision can be specified as an integer >= 0' do\n      fmt = format.new(\"%.0d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.prec()).to be(0)\n      fmt = format.new(\"%.10d\")\n      expect(fmt.prec()).to be(10)\n    end\n\n    it 'width and precision can both be specified' do\n      fmt = format.new(\"%2.3d\")\n      expect(fmt.format()).to be(:d)\n      expect(fmt.width()).to be(2)\n      expect(fmt.prec()).to be(3)\n    end\n\n    [\n      '[', '{', '(', '<', '|',\n    ].each do | delim |\n      it \"a container delimiter pair can be set with '#{delim}'\" do\n        fmt = format.new(\"%#{delim}d\")\n        expect(fmt.format()).to be(:d)\n        expect(fmt.delimiters()).to eql(delim)\n      end\n    end\n\n    it \"Is an error to specify different delimiters at the same time\" do\n      expect do\n        format.new(\"%[{d\")\n      end.to raise_error(/Only one of the delimiters/)\n    end\n\n    it \"Is an error to have trailing characters after the format\" do\n      expect do\n        format.new(\"%dv\")\n      end.to raise_error(/The format '%dv' is not a valid format/)\n    end\n\n    it \"Is an error to specify the same flag more than once\" do\n      expect do\n        format.new(\"%[[d\")\n      end.to raise_error(/The same flag can only be used once/)\n    end\n  end\n\n  context 'when converting to string' do\n    {\n      42                   => \"42\",\n      3.14                 => \"3.140000\",\n      \"hello world\"        => \"hello world\",\n      \"hello\\tworld\"       => \"hello\\tworld\",\n    }.each do |from, to|\n      it \"the string value of #{from} is '#{to}'\" do\n        expect(converter.convert(from, :default)).to eq(to)\n      end\n    end\n\n    it 'float point value decimal digits can be specified' do\n      string_formats = { Puppet::Pops::Types::PFloatType::DEFAULT => '%.2f'}\n      expect(converter.convert(3.14, string_formats)).to eq('3.14')\n    end\n\n    it 'when specifying format for one type, other formats are not affected' do\n      string_formats = { Puppet::Pops::Types::PFloatType::DEFAULT => '%.2f'}\n      expect(converter.convert('3.1415', string_formats)).to eq('3.1415')\n    end\n\n    context 'The %p format for string produces' do\n      let!(:string_formats) { { Puppet::Pops::Types::PStringType::DEFAULT => '%p'} }\n      it 'double quoted result for string that contains control characters' do\n         expect(converter.convert(\"hello\\tworld.\\r\\nSun is brigth today.\", string_formats)).to eq('\"hello\\\\tworld.\\\\r\\\\nSun is brigth today.\"')\n      end\n\n      it 'single quoted result for string that is plain ascii without \\\\, $ or control characters' do\n        expect(converter.convert('hello world', string_formats)).to eq(\"'hello world'\")\n      end\n\n      it 'double quoted result when using %#p if it would otherwise be single quoted' do\n        expect(converter.convert('hello world', '%#p')).to eq('\"hello world\"')\n      end\n\n      it 'quoted 5-byte unicode chars' do\n        expect(converter.convert(\"smile \\u{1f603}.\", string_formats)).to eq(\"'smile \\u{1F603}.'\")\n      end\n\n      it 'quoted 2-byte unicode chars' do\n        expect(converter.convert(\"esc \\u{1b}.\", string_formats)).to eq('\"esc \\\\u{1B}.\"')\n      end\n\n      it 'escape for $ in double quoted string' do\n        # Use \\n in string to force double quotes\n        expect(converter.convert(\"escape the $ sign\\n\", string_formats)).to eq('\"escape the \\$ sign\\n\"')\n      end\n\n      it 'no escape for $ in single quoted string' do\n        expect(converter.convert('don\\'t escape the $ sign', string_formats)).to eq(\"'don\\\\'t escape the $ sign'\")\n      end\n\n      it 'escape for double quote but not for single quote in double quoted string' do\n        # Use \\n in string to force double quotes\n        expect(converter.convert(\"the ' single and \\\" double quote\\n\", string_formats)).to eq('\"the \\' single and \\\\\" double quote\\n\"')\n      end\n\n      it 'escape for single quote but not for double quote in single quoted string' do\n        expect(converter.convert('the \\' single and \" double quote', string_formats)).to eq(\"'the \\\\' single and \\\" double quote'\")\n      end\n\n      it 'no escape for #' do\n        expect(converter.convert('do not escape #{x}', string_formats)).to eq('\\'do not escape #{x}\\'')\n      end\n\n      it 'escape for last \\\\' do\n        expect(converter.convert('escape the last \\\\', string_formats)).to eq(\"'escape the last \\\\'\")\n      end\n    end\n\n    {\n      '%s'   => 'hello::world',\n      '%p'   => \"'hello::world'\",\n      '%c'   => 'Hello::world',\n      '%#c'   => \"'Hello::world'\",\n      '%u'   => 'HELLO::WORLD',\n      '%#u'   => \"'HELLO::WORLD'\",\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => fmt}\n        expect(converter.convert('hello::world', string_formats)).to eq(result)\n      end\n    end\n\n    {\n      '%c'   => 'Hello::world',\n      '%#c'  => \"'Hello::world'\",\n      '%d'   => 'hello::world',\n      '%#d'  => \"'hello::world'\",\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => fmt}\n        expect(converter.convert('HELLO::WORLD', string_formats)).to eq(result)\n      end\n    end\n\n    {\n      [nil, '%.1p']  => 'u',\n      [nil, '%#.2p'] => '\"u',\n      [:default, '%.1p'] => 'd',\n      [true, '%.2s'] => 'tr',\n      [true, '%.2y'] => 'ye',\n    }.each do |args, result |\n      it \"the format #{args[1]} produces #{result} for value #{args[0]}\" do\n        expect(converter.convert(*args)).to eq(result)\n      end\n    end\n\n    {\n      '   a b  ' => 'a b',\n      'a b  '    => 'a b',\n      '   a b'   => 'a b',\n    }.each do |input, result |\n      it \"the input #{input} is trimmed to #{result} by using format '%t'\" do\n        string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%t'}\n        expect(converter.convert(input, string_formats)).to eq(result)\n      end\n    end\n\n    {\n      '   a b  ' => \"'a b'\",\n      'a b  '    => \"'a b'\",\n      '   a b'   => \"'a b'\",\n    }.each do |input, result |\n      it \"the input #{input} is trimmed to #{result} by using format '%#t'\" do\n        string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%#t'}\n        expect(converter.convert(input, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => \"%k\"}\n      converter.convert('wat', string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of String type - expected one of the characters 'cCudspt'/)\n    end\n\n    it 'Width pads a string left with spaces to given width' do\n      string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%6s'}\n      expect(converter.convert(\"abcd\", string_formats)).to eq('  abcd')\n    end\n\n    it 'Width pads a string right with spaces to given width and - flag' do\n      string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%-6s'}\n      expect(converter.convert(\"abcd\", string_formats)).to eq('abcd  ')\n    end\n\n    it 'Precision truncates the string if precision < length' do\n      string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%-6.2s'}\n      expect(converter.convert(\"abcd\", string_formats)).to eq('ab    ')\n    end\n\n    {\n      '%4.2s'   => '  he',\n      '%4.2p'   => \"  'h\",\n      '%4.2c'   => '  He',\n      '%#4.2c'  => \"  'H\",\n      '%4.2u'   => '  HE',\n      '%#4.2u'  => \"  'H\",\n      '%4.2d'   => '  he',\n      '%#4.2d'  => \"  'h\"\n    }.each do |fmt, result |\n      it \"width and precision can be combined with #{fmt}\" do\n        string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => fmt}\n        expect(converter.convert('hello::world', string_formats)).to eq(result)\n      end\n    end\n\n  end\n\n  context 'when converting integer' do\n    it 'the default string representation is decimal' do\n      expect(converter.convert(42, :default)).to eq('42')\n    end\n\n    {\n      '%s'      => '18',\n      '%4.1s'   => '   1',\n      '%p'      => '18',\n      '%4.2p'   => '  18',\n      '%4.1p'   => '   1',\n      '%#s'     => '\"18\"',\n      '%#6.4s'  => '  \"18\"',\n      '%#p'     => '18',\n      '%#6.4p'  => '    18',\n      '%d'      => '18',\n      '%4.1d'   => '  18',\n      '%4.3d'   => ' 018',\n      '%x'      => '12',\n      '%4.3x'   => ' 012',\n      '%#x'     => '0x12',\n      '%#6.4x'  => '0x0012',\n      '%X'      => '12',\n      '%4.3X'   => ' 012',\n      '%#X'     => '0X12',\n      '%#6.4X'  => '0X0012',\n      '%o'      => '22',\n      '%4.2o'   => '  22',\n      '%#o'     => '022',\n      '%b'      => '10010',\n      '%7.6b'   => ' 010010',\n      '%#b'     => '0b10010',\n      '%#9.6b'  => ' 0b010010',\n      '%#B'     => '0B10010',\n      '%#9.6B'  => ' 0B010010',\n      # Integer to float then a float format - fully tested for float\n      '%e'      => '1.800000e+01',\n      '%E'      => '1.800000E+01',\n      '%f'      => '18.000000',\n      '%g'      => '18',\n      '%a'      => '0x1.2p+4',\n      '%A'      => '0X1.2P+4',\n      '%.1f'    => '18.0',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        pending(\"PUP-8612 %a and %A not support on JRuby\") if RUBY_PLATFORM == 'java' && fmt =~ /^%[aA]$/\n        string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => fmt}\n        expect(converter.convert(18, string_formats)).to eq(result)\n      end\n    end\n\n    it 'the format %#6.4o produces 0022' do\n      string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => '%#6.4o' }\n      result = RUBY_PLATFORM == 'java' ? ' 00022' : '  0022'\n      expect(converter.convert(18, string_formats)).to eq(result)\n    end\n\n    it 'produces a unicode char string by using format %c' do\n      string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => '%c'}\n      expect(converter.convert(0x1F603, string_formats)).to eq(\"\\u{1F603}\")\n    end\n\n    it 'produces a quoted unicode char string by using format %#c' do\n      string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => '%#c'}\n      expect(converter.convert(0x1F603, string_formats)).to eq(\"\\\"\\u{1F603}\\\"\")\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => \"%k\"}\n      converter.convert(18, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Integer type - expected one of the characters 'dxXobBeEfgGaAspc'/)\n    end\n  end\n\n  context 'when converting float' do\n    it 'the default string representation is decimal' do\n      expect(converter.convert(42.0, :default)).to eq('42.000000')\n    end\n\n    {\n      '%s'      => '18.0',\n      '%#s'     => '\"18.0\"',\n      '%5s'     => ' 18.0',\n      '%#8s'    => '  \"18.0\"',\n      '%05.1s'  => '    1',\n      '%p'      => '18.0',\n      '%7.2p'   => '     18',\n\n      '%e'      => '1.800000e+01',\n      '%+e'     => '+1.800000e+01',\n      '% e'     => ' 1.800000e+01',\n      '%.2e'    => '1.80e+01',\n      '%10.2e'  => '  1.80e+01',\n      '%-10.2e' => '1.80e+01  ',\n      '%010.2e' => '001.80e+01',\n\n      '%E'      => '1.800000E+01',\n      '%+E'     => '+1.800000E+01',\n      '% E'     => ' 1.800000E+01',\n      '%.2E'    => '1.80E+01',\n      '%10.2E'  => '  1.80E+01',\n      '%-10.2E' => '1.80E+01  ',\n      '%010.2E' => '001.80E+01',\n\n      '%f'      => '18.000000',\n      '%+f'     => '+18.000000',\n      '% f'     => ' 18.000000',\n      '%.2f'    => '18.00',\n      '%10.2f'  => '     18.00',\n      '%-10.2f' => '18.00     ',\n      '%010.2f' => '0000018.00',\n\n      '%g'      => '18',\n      '%5g'     => '   18',\n      '%05g'    => '00018',\n      '%-5g'    => '18   ',\n      '%5.4g'   => '   18',  # precision has no effect\n\n      '%a'      => '0x1.2p+4',\n      '%.4a'    => '0x1.2000p+4',\n      '%10a'    => '  0x1.2p+4',\n      '%010a'    => '0x001.2p+4',\n      '%-10a'   => '0x1.2p+4  ',\n      '%A'      => '0X1.2P+4',\n      '%.4A'      => '0X1.2000P+4',\n      '%10A'    => '  0X1.2P+4',\n      '%-10A'   => '0X1.2P+4  ',\n      '%010A'   => '0X001.2P+4',\n\n      # integer formats fully tested for integer\n      '%d'      => '18',\n      '%x'      => '12',\n      '%X'      => '12',\n      '%o'      => '22',\n      '%b'      => '10010',\n      '%#B'     => '0B10010',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        pending(\"PUP-8612 %a and %A not support on JRuby\") if RUBY_PLATFORM == 'java' && fmt =~ /^%[-.014]*[aA]$/\n        string_formats = { Puppet::Pops::Types::PFloatType::DEFAULT => fmt}\n        expect(converter.convert(18.0, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PFloatType::DEFAULT => \"%k\"}\n      converter.convert(18.0, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Float type - expected one of the characters 'dxXobBeEfgGaAsp'/)\n    end\n  end\n\n  context 'when converting undef' do\n    it 'the default string representation is empty string' do\n      expect(converter.convert(nil, :default)).to eq('')\n    end\n\n    { \"%u\"  => \"undef\",\n      \"%#u\" => \"undefined\",\n      \"%s\"  => \"\",\n      \"%#s\"  => '\"\"',\n      \"%p\"  => 'undef',\n      \"%#p\"  => '\"undef\"',\n      \"%n\"  => 'nil',\n      \"%#n\" => 'null',\n      \"%v\"  => 'n/a',\n      \"%V\"  => 'N/A',\n      \"%d\"  => 'NaN',\n      \"%x\"  => 'NaN',\n      \"%o\"  => 'NaN',\n      \"%b\"  => 'NaN',\n      \"%B\"  => 'NaN',\n      \"%e\"  => 'NaN',\n      \"%E\"  => 'NaN',\n      \"%f\"  => 'NaN',\n      \"%g\"  => 'NaN',\n      \"%G\"  => 'NaN',\n      \"%a\"  => 'NaN',\n      \"%A\"  => 'NaN',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PUndefType::DEFAULT => fmt}\n        expect(converter.convert(nil, string_formats)).to eq(result)\n      end\n    end\n\n    { \"%7u\"    => \"  undef\",\n      \"%-7u\"   => \"undef  \",\n      \"%#10u\"  => \" undefined\",\n      \"%#-10u\" => \"undefined \",\n      \"%7.2u\"  => \"     un\",\n      \"%4s\"    => \"    \",\n      \"%#4s\"   => '  \"\"',\n      \"%7p\"    => '  undef',\n      \"%7.1p\"  => '      u',\n      \"%#8p\"   => ' \"undef\"',\n      \"%5n\"    => '  nil',\n      \"%.1n\"   => 'n',\n      \"%-5n\"   => 'nil  ',\n      \"%#5n\"   => ' null',\n      \"%#-5n\"  => 'null ',\n      \"%5v\"    => '  n/a',\n      \"%5.2v\"  => '   n/',\n      \"%-5v\"   => 'n/a  ',\n      \"%5V\"    => '  N/A',\n      \"%5.1V\"  => '    N',\n      \"%-5V\"   => 'N/A  ',\n      \"%5d\"    => '  NaN',\n      \"%5.2d\"  => '   Na',\n      \"%-5d\"   => 'NaN  ',\n      \"%5x\"    => '  NaN',\n      \"%5.2x\"  => '   Na',\n      \"%-5x\"   => 'NaN  ',\n      \"%5o\"    => '  NaN',\n      \"%5.2o\"  => '   Na',\n      \"%-5o\"   => 'NaN  ',\n      \"%5b\"    => '  NaN',\n      \"%5.2b\"  => '   Na',\n      \"%-5b\"   => 'NaN  ',\n      \"%5B\"    => '  NaN',\n      \"%5.2B\"  => '   Na',\n      \"%-5B\"   => 'NaN  ',\n      \"%5e\"    => '  NaN',\n      \"%5.2e\"  => '   Na',\n      \"%-5e\"   => 'NaN  ',\n      \"%5E\"    => '  NaN',\n      \"%5.2E\"  => '   Na',\n      \"%-5E\"   => 'NaN  ',\n      \"%5f\"    => '  NaN',\n      \"%5.2f\"  => '   Na',\n      \"%-5f\"   => 'NaN  ',\n      \"%5g\"    => '  NaN',\n      \"%5.2g\"  => '   Na',\n      \"%-5g\"   => 'NaN  ',\n      \"%5G\"    => '  NaN',\n      \"%5.2G\"  => '   Na',\n      \"%-5G\"   => 'NaN  ',\n      \"%5a\"    => '  NaN',\n      \"%5.2a\"  => '   Na',\n      \"%-5a\"   => 'NaN  ',\n      \"%5A\"    => '  NaN',\n      \"%5.2A\"  => '   Na',\n      \"%-5A\"   => 'NaN  ',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PUndefType::DEFAULT => fmt}\n        expect(converter.convert(nil, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PUndefType::DEFAULT => \"%k\"}\n      converter.convert(nil, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Undef type - expected one of the characters 'nudxXobBeEfgGaAvVsp'/)\n    end\n  end\n\n  context 'when converting default' do\n    it 'the default string representation is unquoted \"default\"' do\n      expect(converter.convert(:default, :default)).to eq('default')\n    end\n\n    { \"%d\"  => 'default',\n      \"%D\"  => 'Default',\n      \"%#d\" => '\"default\"',\n      \"%#D\" => '\"Default\"',\n      \"%s\"  => 'default',\n      \"%p\"  => 'default',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PDefaultType::DEFAULT => fmt}\n        expect(converter.convert(:default, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PDefaultType::DEFAULT => \"%k\"}\n      converter.convert(:default, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Default type - expected one of the characters 'dDsp'/)\n    end\n  end\n\n  context 'when converting boolean true' do\n    it 'the default string representation is unquoted \"true\"' do\n      expect(converter.convert(true, :default)).to eq('true')\n    end\n\n    { \"%t\"   => 'true',\n      \"%#t\"  => 't',\n      \"%T\"   => 'True',\n      \"%#T\"  => 'T',\n      \"%s\"   => 'true',\n      \"%p\"   => 'true',\n      \"%d\"   => '1',\n      \"%x\"   => '1',\n      \"%#x\"  => '0x1',\n      \"%o\"   => '1',\n      \"%#o\"  => '01',\n      \"%b\"   => '1',\n      \"%#b\"  => '0b1',\n      \"%#B\"  => '0B1',\n      \"%e\"   => '1.000000e+00',\n      \"%f\"   => '1.000000',\n      \"%g\"   => '1',\n      \"%a\"   => '0x1p+0',\n      \"%A\"   => '0X1P+0',\n      \"%.1f\" => '1.0',\n      \"%y\"   => 'yes',\n      \"%Y\"   => 'Yes',\n      \"%#y\"  => 'y',\n      \"%#Y\"  => 'Y',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        pending(\"PUP-8612 %a and %A not support on JRuby\") if RUBY_PLATFORM == 'java' && fmt =~ /^%[aA]$/\n        string_formats = { Puppet::Pops::Types::PBooleanType::DEFAULT => fmt}\n        expect(converter.convert(true, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PBooleanType::DEFAULT => \"%k\"}\n      converter.convert(true, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Boolean type - expected one of the characters 'tTyYdxXobBeEfgGaAsp'/)\n    end\n\n  end\n\n  context 'when converting boolean false' do\n    it 'the default string representation is unquoted \"false\"' do\n      expect(converter.convert(false, :default)).to eq('false')\n    end\n\n    { \"%t\"   => 'false',\n      \"%#t\"  => 'f',\n      \"%T\"   => 'False',\n      \"%#T\"  => 'F',\n      \"%s\"   => 'false',\n      \"%p\"   => 'false',\n      \"%d\"   => '0',\n      \"%x\"   => '0',\n      \"%#x\"  => '0',\n      \"%o\"   => '0',\n      \"%#o\"  => '0',\n      \"%b\"   => '0',\n      \"%#b\"  => '0',\n      \"%#B\"  => '0',\n      \"%e\"   => '0.000000e+00',\n      \"%E\"   => '0.000000E+00',\n      \"%f\"   => '0.000000',\n      \"%g\"   => '0',\n      \"%a\"   => '0x0p+0',\n      \"%A\"   => '0X0P+0',\n      \"%.1f\" => '0.0',\n      \"%y\"   => 'no',\n      \"%Y\"   => 'No',\n      \"%#y\"  => 'n',\n      \"%#Y\"  => 'N',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        pending(\"PUP-8612 %a and %A not support on JRuby\") if RUBY_PLATFORM == 'java' && fmt =~ /^%[aA]$/\n        string_formats = { Puppet::Pops::Types::PBooleanType::DEFAULT => fmt}\n        expect(converter.convert(false, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PBooleanType::DEFAULT => \"%k\"}\n      converter.convert(false, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Boolean type - expected one of the characters 'tTyYdxXobBeEfgGaAsp'/)\n    end\n  end\n\n  context 'when converting array' do\n    it 'the default string representation is using [] delimiters, joins with ',' and uses %p for values' do\n      expect(converter.convert([\"hello\", \"world\"], :default)).to eq(\"['hello', 'world']\")\n    end\n\n    { \"%s\"  => \"[1, 'hello']\",\n      \"%p\"  => \"[1, 'hello']\",\n      \"%a\"  => \"[1, 'hello']\",\n      \"%<a\"  => \"<1, 'hello'>\",\n      \"%[a\"  => \"[1, 'hello']\",\n      \"%(a\"  => \"(1, 'hello')\",\n      \"%{a\"  => \"{1, 'hello'}\",\n      \"% a\"  => \"1, 'hello'\",\n\n      {'format' => '%(a',\n        'separator' => ' '\n      } => \"(1 'hello')\",\n\n      {'format' => '%(a',\n        'separator' => ''\n      } => \"(1'hello')\",\n\n      {'format' => '%|a',\n        'separator' => ' '\n      } => \"|1 'hello'|\",\n\n      {'format' => '%(a',\n        'separator' => ' ',\n        'string_formats' => {Puppet::Pops::Types::PIntegerType::DEFAULT => '%#x'}\n      } => \"(0x1 'hello')\",\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => fmt}\n        expect(converter.convert([1, \"hello\"], string_formats)).to eq(result)\n      end\n    end\n\n    it \"multiple rules selects most specific\" do\n      short_array_t = factory.array_of(factory.integer, factory.range(1,2))\n      long_array_t = factory.array_of(factory.integer, factory.range(3,100))\n      string_formats = {\n        short_array_t => \"%(a\",\n        long_array_t  => \"%[a\",\n      }\n      expect(converter.convert([1, 2], string_formats)).to eq('(1, 2)') unless RUBY_PLATFORM == 'java' # PUP-8615\n      expect(converter.convert([1, 2, 3], string_formats)).to eq('[1, 2, 3]')\n    end\n\n    it 'indents elements in alternate mode' do\n      string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#a', 'separator' =>\", \" } }\n      # formatting matters here\n      result = [\n       \"[1, 2, 9, 9,\",\n       \"  [3, 4],\",\n       \"  [5,\",\n       \"    [6, 7]],\",\n       \"  8, 9]\"\n       ].join(\"\\n\")\n\n     formatted = converter.convert([1, 2, 9, 9, [3, 4], [5, [6, 7]], 8, 9], string_formats)\n     expect(formatted).to eq(result)\n    end\n\n    it 'applies a short form format' do\n      result = [\n        \"[1,\",\n        \"  [2, 3],\",\n        \"  4]\"\n        ].join(\"\\n\")\n      expect(converter.convert([1, [2,3], 4], '%#a')).to eq(result)\n    end\n\n    it 'treats hashes as nested arrays wrt indentation' do\n      string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#a', 'separator' =>\", \" } }\n      # formatting matters here\n      result = [\n       \"[1, 2, 9, 9,\",\n       \"  {3 => 4, 5 => 6},\",\n       \"  [5,\",\n       \"    [6, 7]],\",\n       \"  8, 9]\"\n       ].join(\"\\n\")\n\n     formatted = converter.convert([1, 2, 9, 9, {3  => 4, 5 => 6}, [5, [6, 7]], 8, 9], string_formats)\n     expect(formatted).to eq(result)\n    end\n\n    it 'indents and breaks when a sequence > given width, in alternate mode' do\n      string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#3a', 'separator' =>\", \" } }\n      # formatting matters here\n      result = [\n       \"[ 1,\",\n       \"  2,\",\n       \"  90,\", # this sequence has length 4 (1,2,90) which is > 3\n       \"  [3, 4],\",\n       \"  [5,\",\n       \"    [6, 7]],\",\n       \"  8,\",\n       \"  9]\",\n       ].join(\"\\n\")\n\n     formatted = converter.convert([1, 2, 90, [3, 4], [5, [6, 7]], 8, 9], string_formats)\n     expect(formatted).to eq(result)\n    end\n\n    it 'indents and breaks when a sequence (placed last) > given width, in alternate mode' do\n      string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#3a', 'separator' =>\", \" } }\n      # formatting matters here\n      result = [\n       \"[ 1,\",\n       \"  2,\",\n       \"  9,\", # this sequence has length 3 (1,2,9) which does not cause breaking on each\n       \"  [3, 4],\",\n       \"  [5,\",\n       \"    [6, 7]],\",\n       \"  8,\",\n       \"  900]\", # this sequence has length 4 (8, 900) which causes breaking on each\n       ].join(\"\\n\")\n\n     formatted = converter.convert([1, 2, 9, [3, 4], [5, [6, 7]], 8, 900], string_formats)\n     expect(formatted).to eq(result)\n    end\n\n    it 'indents and breaks nested sequences when one is placed first' do\n      string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#a', 'separator' =>\", \" } }\n      # formatting matters here\n      result = [\n       \"[\",\n       \"  [\",\n       \"    [1, 2,\",\n       \"      [3, 4]]],\",\n       \"  [5,\",\n       \"    [6, 7]],\",\n       \"  8, 900]\",\n       ].join(\"\\n\")\n\n     formatted = converter.convert([[[1, 2, [3, 4]]], [5, [6, 7]], 8, 900], string_formats)\n     expect(formatted).to eq(result)\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => \"%k\"}\n      converter.convert([1,2], string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Array type - expected one of the characters 'asp'/)\n    end\n  end\n\n  context 'when converting hash' do\n    it 'the default string representation is using {} delimiters, joins with '=>' and uses %p for values' do\n      expect(converter.convert({\"hello\" => \"world\"}, :default)).to eq(\"{'hello' => 'world'}\")\n    end\n\n    { \"%s\"  => \"{1 => 'world'}\",\n      \"%p\"  => \"{1 => 'world'}\",\n      \"%h\"  => \"{1 => 'world'}\",\n      \"%a\"  => \"[[1, 'world']]\",\n      \"%<h\"  => \"<1 => 'world'>\",\n      \"%[h\"  => \"[1 => 'world']\",\n      \"%(h\"  => \"(1 => 'world')\",\n      \"%{h\"  => \"{1 => 'world'}\",\n      \"% h\"  => \"1 => 'world'\",\n\n      {'format' => '%(h',\n        'separator2' => ' '\n      } => \"(1 'world')\",\n\n      {'format' => '%(h',\n        'separator2' => ' ',\n        'string_formats' => {Puppet::Pops::Types::PIntegerType::DEFAULT => '%#x'}\n      } => \"(0x1 'world')\",\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PHashType::DEFAULT => fmt}\n        expect(converter.convert({1 => \"world\"}, string_formats)).to eq(result)\n      end\n    end\n\n    {  \"%s\"  => \"{1 => 'hello', 2 => 'world'}\",\n\n       {'format' => '%(h',\n         'separator2' => ' '\n       } => \"(1 'hello', 2 'world')\",\n\n       {'format' => '%(h',\n         'separator' => '',\n         'separator2' => ''\n       } => \"(1'hello'2'world')\",\n\n       {'format' => '%(h',\n         'separator' => ' >> ',\n         'separator2' => ' <=> ',\n         'string_formats' => {Puppet::Pops::Types::PIntegerType::DEFAULT => '%#x'}\n       } => \"(0x1 <=> 'hello' >> 0x2 <=> 'world')\",\n     }.each do |fmt, result |\n       it \"the format #{fmt} produces #{result}\" do\n         string_formats = { Puppet::Pops::Types::PHashType::DEFAULT => fmt}\n         expect(converter.convert({1 => \"hello\", 2 => \"world\"}, string_formats)).to eq(result)\n       end\n     end\n\n    it 'indents elements in alternative mode #' do\n      string_formats = {\n        Puppet::Pops::Types::PHashType::DEFAULT => {\n          'format' => '%#h',\n        }\n      }\n      # formatting matters here\n      result = [\n       \"{\",\n       \"  1 => 'hello',\",\n       \"  2 => {\",\n       \"    3 => 'world'\",\n       \"  }\",\n       \"}\"\n       ].join(\"\\n\")\n\n      expect(converter.convert({1 => \"hello\", 2 => {3=> \"world\"}}, string_formats)).to eq(result)\n    end\n\n    it 'applies a short form format' do\n      result = [\n        \"{\",\n        \"  1 => {\",\n        \"    2 => 3\",\n        \"  },\",\n        \"  4 => 5\",\n        \"}\"].join(\"\\n\")\n      expect(converter.convert({1 => {2 => 3}, 4 => 5}, '%#h')).to eq(result)\n    end\n\n    context \"containing an array\" do\n      it 'the hash and array renders without breaks and indentation by default' do\n        result = \"{1 => [1, 2, 3]}\"\n        formatted = converter.convert({ 1 => [1, 2, 3] }, :default)\n        expect(formatted).to eq(result)\n      end\n\n      it 'the array renders with breaks if so specified' do\n        string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#1a', 'separator' =>\",\" } }\n        result = [\n        \"{1 => [ 1,\",\n        \"    2,\",\n        \"    3]}\"\n        ].join(\"\\n\")\n        formatted = converter.convert({ 1 => [1, 2, 3] }, string_formats)\n        expect(formatted).to eq(result)\n      end\n\n      it 'both hash and array renders with breaks and indentation if so specified for both' do\n        string_formats = {\n          Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%#1a', 'separator' =>\", \" },\n          Puppet::Pops::Types::PHashType::DEFAULT => { 'format' => '%#h', 'separator' =>\",\" }\n        }\n        result = [\n        \"{\",\n        \"  1 => [ 1,\",\n        \"    2,\",\n        \"    3]\",\n        \"}\"\n        ].join(\"\\n\")\n        formatted = converter.convert({ 1 => [1, 2, 3] }, string_formats)\n        expect(formatted).to eq(result)\n      end\n\n      it 'hash, but not array is rendered with breaks and indentation if so specified only for the hash' do\n        string_formats = {\n          Puppet::Pops::Types::PArrayType::DEFAULT => { 'format' => '%a', 'separator' =>\", \" },\n          Puppet::Pops::Types::PHashType::DEFAULT => { 'format' => '%#h', 'separator' =>\",\" }\n        }\n        result = [\n        \"{\",\n        \"  1 => [1, 2, 3]\",\n        \"}\"\n        ].join(\"\\n\")\n        formatted = converter.convert({ 1 => [1, 2, 3] }, string_formats)\n        expect(formatted).to eq(result)\n      end\n    end\n\n    context 'that is subclassed' do\n      let(:array) { ['a', 2] }\n      let(:derived_array) do\n        Class.new(Array) do\n          def to_a\n            self # Dead wrong! Should return a plain Array copy\n          end\n        end.new(array)\n      end\n      let(:derived_with_to_a) do\n        Class.new(Array) do\n          def to_a\n            super\n          end\n        end.new(array)\n      end\n\n      let(:hash) { {'first' => 1, 'second' => 2} }\n      let(:derived_hash) do\n        Class.new(Hash)[hash]\n      end\n      let(:derived_with_to_hash) do\n        Class.new(Hash) do\n          def to_hash\n            {}.merge(self)\n          end\n        end[hash]\n      end\n\n      it 'formats a derived array as a Runtime' do\n        expect(converter.convert(array)).to eq('[\\'a\\', 2]')\n        expect(converter.convert(derived_array)).to eq('[\"a\", 2]')\n      end\n\n      it 'formats a derived array with #to_a retuning plain Array as an Array' do\n        expect(converter.convert(derived_with_to_a)).to eq('[\\'a\\', 2]')\n      end\n\n      it 'formats a derived hash as a Runtime' do\n        expect(converter.convert(hash)).to eq('{\\'first\\' => 1, \\'second\\' => 2}')\n        expect(converter.convert(derived_hash)).to eq('{\"first\"=>1, \"second\"=>2}')\n      end\n\n      it 'formats a derived hash with #to_hash retuning plain Hash as a Hash' do\n        expect(converter.convert(derived_with_to_hash, '%p')).to eq('{\\'first\\' => 1, \\'second\\' => 2}')\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PHashType::DEFAULT => \"%k\"}\n      converter.convert({1 => 2}, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Hash type - expected one of the characters 'hasp'/)\n    end\n  end\n\n  context 'when converting a runtime type' do\n    [ :sym, (1..3), Time.now ].each do |value|\n      it \"the default string representation for #{value} is #to_s\" do\n        expect(converter.convert(value, :default)).to eq(value.to_s)\n      end\n\n      it \"the '%q' string representation for #{value} is #inspect\" do\n        expect(converter.convert(value, '%q')).to eq(value.inspect)\n      end\n\n      it \"the '%p' string representation for #{value} is quoted #to_s\" do\n        expect(converter.convert(value, '%p')).to eq(\"'#{value}'\")\n      end\n    end\n\n    it 'an unknown format raises an error' do\n      expect { converter.convert(:sym, '%b') }.to raise_error(\"Illegal format 'b' specified for value of Runtime type - expected one of the characters 'spq'\")\n    end\n  end\n\n  context 'when converting regexp' do\n    it 'the default string representation is \"regexp\"' do\n      expect(converter.convert(/.*/, :default)).to eq('.*')\n    end\n\n    { \"%s\"   => '.*',\n      \"%6s\"  => '    .*',\n      \"%.1s\" => '.',\n      \"%-6s\" => '.*    ',\n      \"%p\"   => '/.*/',\n      \"%6p\"  => '  /.*/',\n      \"%-6p\" => '/.*/  ',\n      \"%.2p\" => '/.',\n      \"%#s\"  => \"'.*'\",\n      \"%#p\"  => '/.*/'\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => fmt}\n        expect(converter.convert(/.*/, string_formats)).to eq(result)\n      end\n    end\n\n    context 'that contains flags' do\n      it 'the format %s produces \\'(?m-ix:[a-z]\\s*)\\' for expression /[a-z]\\s*/m' do\n        string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%s'}\n        expect(converter.convert(/[a-z]\\s*/m, string_formats)).to eq('(?m-ix:[a-z]\\s*)')\n      end\n\n      it 'the format %p produces \\'/(?m-ix:[a-z]\\s*)/\\' for expression /[a-z]\\s*/m' do\n        string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%p'}\n        expect(converter.convert(/[a-z]\\s*/m, string_formats)).to eq('/(?m-ix:[a-z]\\s*)/')\n      end\n\n      it 'the format %p produces \\'/foo\\/bar/\\' for expression /foo\\/bar/' do\n        string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%p'}\n        expect(converter.convert(/foo\\/bar/, string_formats)).to eq('/foo\\/bar/')\n      end\n\n      context 'and slashes' do\n        it 'the format %s produces \\'(?m-ix:foo/bar)\\' for expression /foo\\/bar/m' do\n          string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%s'}\n          expect(converter.convert(/foo\\/bar/m, string_formats)).to eq('(?m-ix:foo/bar)')\n        end\n\n        it 'the format %s produces \\'(?m-ix:foo\\/bar)\\' for expression /foo\\\\\\/bar/m' do\n          string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%s'}\n          expect(converter.convert(/foo\\\\\\/bar/m, string_formats)).to eq('(?m-ix:foo\\\\\\\\/bar)')\n        end\n\n        it 'the format %p produces \\'(?m-ix:foo\\/bar)\\' for expression /foo\\/bar/m' do\n          string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%p'}\n          expect(converter.convert(/foo\\/bar/m, string_formats)).to eq('/(?m-ix:foo\\/bar)/')\n        end\n\n        it 'the format %p produces \\'(?m-ix:foo\\/bar)\\' for expression /(?m-ix:foo\\/bar)/' do\n          string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%p'}\n          expect(converter.convert(/(?m-ix:foo\\/bar)/, string_formats)).to eq('/(?m-ix:foo\\/bar)/')\n        end\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n      string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => \"%k\"}\n      converter.convert(/.*/, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Regexp type - expected one of the characters 'sp'/)\n    end\n  end\n\n  context 'when converting binary' do\n    let(:sample) { binary.from_binary_string('binary') }\n\n    it 'the binary is converted to strict base64 string unquoted by default (same as %B)' do\n      expect(converter.convert(sample, :default)).to eq(\"YmluYXJ5\")\n    end\n\n    it 'the binary is converted using %p by default when contained in an array' do\n      expect(converter.convert([sample], :default)).to eq(\"[Binary(\\\"YmluYXJ5\\\")]\")\n    end\n\n    it '%B formats in base64 strict mode (same as default)' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%B'}\n      expect(converter.convert(sample, string_formats)).to eq(\"YmluYXJ5\")\n    end\n\n    it '%b formats in base64 relaxed mode, and adds newline' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%b'}\n      expect(converter.convert(sample, string_formats)).to eq(\"YmluYXJ5\\n\")\n    end\n\n    it '%u formats in base64 urlsafe mode' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%u'}\n      expect(converter.convert(binary.from_base64(\"++//\"), string_formats)).to eq(\"--__\")\n    end\n\n    it '%p formats with type name' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%p'}\n      expect(converter.convert(sample, string_formats)).to eq(\"Binary(\\\"YmluYXJ5\\\")\")\n    end\n\n    it '%#s formats as quoted string with escaped non printable bytes' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%#s'}\n      expect(converter.convert(binary.from_base64(\"apa=\"), string_formats)).to eq(\"\\\"j\\\\x96\\\"\")\n    end\n\n    it '%s formats as unquoted string with valid UTF-8 chars' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%s'}\n      # womans hat emoji is E318, a three byte UTF-8 char EE 8C 98\n      expect(converter.convert(binary.from_binary_string(\"\\xEE\\x8C\\x98\"), string_formats)).to eq(\"\\uE318\")\n    end\n\n    it '%s errors if given non UTF-8 bytes' do\n      string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => '%s'}\n      expect {\n        converter.convert(binary.from_base64(\"apa=\"), string_formats)\n      }.to raise_error(Encoding::UndefinedConversionError)\n    end\n\n    { \"%s\"    => 'binary',\n      \"%#s\"   => '\"binary\"',\n      \"%8s\"   => '  binary',\n      \"%.2s\"  => 'bi',\n      \"%-8s\"  => 'binary  ',\n      \"%p\"    => 'Binary(\"YmluYXJ5\")',\n      \"%10p\"  => 'Binary(\"  YmluYXJ5\")',\n      \"%-10p\" => 'Binary(\"YmluYXJ5  \")',\n      \"%.2p\"  => 'Binary(\"Ym\")',\n      \"%b\"    => \"YmluYXJ5\\n\",\n      \"%11b\"  => \"  YmluYXJ5\\n\",\n      \"%-11b\" => \"YmluYXJ5\\n  \",\n      \"%.2b\"  => \"Ym\",\n      \"%B\"    => \"YmluYXJ5\",\n      \"%11B\"  => \"   YmluYXJ5\",\n      \"%-11B\" => \"YmluYXJ5   \",\n      \"%.2B\"  => \"Ym\",\n      \"%u\"    => \"YmluYXJ5\",\n      \"%11u\"  => \"   YmluYXJ5\",\n      \"%-11u\" => \"YmluYXJ5   \",\n      \"%.2u\"  => \"Ym\",\n      \"%t\"    => 'Binary',\n      \"%#t\"   => '\"Binary\"',\n      \"%8t\"   => '  Binary',\n      \"%-8t\"  => 'Binary  ',\n      \"%.3t\"  => 'Bin',\n      \"%T\"    => 'BINARY',\n      \"%#T\"   => '\"BINARY\"',\n      \"%8T\"   => '  BINARY',\n      \"%-8T\"  => 'BINARY  ',\n      \"%.3T\"  => 'BIN',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PBinaryType::DEFAULT => fmt}\n        expect(converter.convert(sample, string_formats)).to eq(result)\n      end\n    end\n  end\n\n  context 'when converting iterator' do\n    it 'the iterator is transformed to an array and formatted using array rules' do\n      itor = Puppet::Pops::Types::Iterator.new(Puppet::Pops::Types::PIntegerType::DEFAULT, [1,2,3]).reverse_each\n      expect(converter.convert(itor, :default)).to eq('[3, 2, 1]')\n    end\n  end\n\n  context 'when converting type' do\n    it 'the default string representation of a type is its programmatic string form' do\n      expect(converter.convert(factory.integer, :default)).to eq('Integer')\n    end\n\n    { \"%s\"  => 'Integer',\n      \"%p\"  => 'Integer',\n      \"%#s\" => '\"Integer\"',\n      \"%#p\" => 'Integer',\n    }.each do |fmt, result |\n      it \"the format #{fmt} produces #{result}\" do\n        string_formats = { Puppet::Pops::Types::PTypeType::DEFAULT => fmt}\n        expect(converter.convert(factory.integer, string_formats)).to eq(result)\n      end\n    end\n\n    it 'errors when format is not recognized' do\n      expect do\n        string_formats = { Puppet::Pops::Types::PTypeType::DEFAULT => \"%k\"}\n        converter.convert(factory.integer, string_formats)\n      end.to raise_error(/Illegal format 'k' specified for value of Type type - expected one of the characters 'sp'/)\n    end\n  end\n\n  it \"allows format to be directly given (instead of as a type=> format hash)\" do\n    expect(converter.convert('hello', '%5.1s')).to eq('    h')\n  end\n\n  it 'an explicit format for a type will override more specific defaults' do\n    expect(converter.convert({ 'x' => 'X' }, { Puppet::Pops::Types::PCollectionType::DEFAULT => '%#p' })).to eq(\"{\\n  'x' => 'X'\\n}\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/types/task_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/parser/script_compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'The Task Type' do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  context 'when loading' do\n    let(:testing_env) do\n      {\n        'testing' => {\n          'modules' => modules,\n          'manifests' => manifests\n        }\n      }\n    end\n\n    let(:manifests) { {} }\n    let(:environments_dir) { Puppet[:environmentpath] }\n\n    let(:testing_env_dir) do\n      dir_contained_in(environments_dir, testing_env)\n      env_dir = File.join(environments_dir, 'testing')\n      PuppetSpec::Files.record_tmp(env_dir)\n      env_dir\n    end\n\n    let(:modules_dir) { File.join(testing_env_dir, 'modules') }\n    let(:env) { Puppet::Node::Environment.create(:testing, [modules_dir]) }\n    let(:node) { Puppet::Node.new('test', :environment => env) }\n    let(:logs) { [] }\n    let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }\n    let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }\n    let(:task_t) { TypeFactory.task }\n    before(:each) { Puppet[:tasks] = true }\n\n    context 'tasks' do\n      let(:compiler) { Puppet::Parser::ScriptCompiler.new(env, node.name) }\n\n      let(:modules) do\n        { 'testmodule' => testmodule }\n      end\n\n      let(:module_loader) { Puppet.lookup(:loaders).find_loader('testmodule') }\n\n      def compile(code = nil)\n        Puppet[:code] = code\n        Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do\n          compiler.compile do |catalog|\n            yield if block_given?\n            catalog\n          end\n        end\n      end\n\n      context 'without metadata' do\n        let(:testmodule) {\n          {\n            'tasks' => {\n              'hello' => <<-RUBY\n            require 'json'\n            args = JSON.parse(STDIN.read)\n            puts({message: args['message']}.to_json)\n            exit 0\n            RUBY\n            }\n          }\n        }\n\n        it 'loads task without metadata as a generic Task' do\n          compile do\n            task = module_loader.load(:task, 'testmodule::hello')\n            expect(task_t.instance?(task)).to be_truthy\n            expect(task.name).to eq('testmodule::hello')\n            expect(task._pcore_type).to eq(task_t)\n          end\n        end\n\n        context 'without --tasks' do\n          before(:each) { Puppet[:tasks] = false }\n\n          it 'evaluator does not recognize generic tasks' do\n            compile do\n              expect(module_loader.load(:task, 'testmodule::hello')).to be_nil\n            end\n          end\n        end\n      end\n\n      context 'with metadata' do\n        let(:testmodule) {\n          {\n            'tasks' => {\n              'hello.rb' => <<-RUBY,\n                require 'json'\n                args = JSON.parse(STDIN.read)\n                puts({message: args['message']}.to_json)\n                exit 0\n                RUBY\n              'hello.json' => <<-JSON,\n                {\n                  \"puppet_task_version\": 1,\n                  \"supports_noop\": true,\n                  \"parameters\": {\n                     \"message\": {\n                       \"type\": \"String\",\n                       \"description\": \"the message\",\n                       \"sensitive\": false\n                     },\n                     \"font\": {\n                       \"type\": \"Optional[String]\"\n                     }\n                }}\n                JSON\n              'non_data.rb' => <<-RUBY,\n                require 'json'\n                args = JSON.parse(STDIN.read)\n                puts({message: args['message']}.to_json)\n                exit 0\n                RUBY\n              'non_data.json' => <<-JSON\n                {\n                  \"puppet_task_version\": 1,\n                  \"supports_noop\": true,\n                  \"parameters\": {\n                     \"arg\": {\n                       \"type\": \"Hash\",\n                       \"description\": \"the non data param\"\n                     }\n                }}\n                JSON\n            }\n          }\n        }\n\n        it 'loads a task with parameters' do\n          compile do\n            task = module_loader.load(:task, 'testmodule::hello')\n            expect(task_t.instance?(task)).to be_truthy\n            expect(task.name).to eq('testmodule::hello')\n            expect(task._pcore_type).to eq(task_t)\n            expect(task.metadata['supports_noop']).to eql(true)\n            expect(task.metadata['puppet_task_version']).to eql(1)\n            expect(task.files).to eql([{\"name\" => \"hello.rb\", \"path\" => \"#{modules_dir}/testmodule/tasks/hello.rb\"}])\n\n            expect(task.metadata['parameters']['message']['description']).to eql('the message')\n            expect(task.parameters['message']).to be_a(Puppet::Pops::Types::PStringType)\n          end\n        end\n\n        it 'loads a task with non-Data parameter' do\n          compile do\n            task = module_loader.load(:task, 'testmodule::non_data')\n            expect(task_t.instance?(task)).to be_truthy\n            expect(task.parameters['arg']).to be_a(Puppet::Pops::Types::PHashType)\n          end\n        end\n\n        context 'without an implementation file' do\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'init.json' => '{}'\n              }\n            }\n          }\n\n          it 'fails to load the task' do\n            compile do\n              expect {\n                module_loader.load(:task, 'testmodule')\n              }.to raise_error(Puppet::Module::Task::InvalidTask, /No source besides task metadata was found/)\n            end\n          end\n        end\n\n        context 'with multiple implementation files' do\n          let(:metadata) { '{}' }\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'init.sh' => '',\n                'init.ps1' => '',\n                'init.json' => metadata,\n              }\n            }\n          }\n\n          it \"fails if metadata doesn't specify implementations\" do\n            compile do\n              expect {\n                module_loader.load(:task, 'testmodule')\n              }.to raise_error(Puppet::Module::Task::InvalidTask, /Multiple executables were found .*/)\n            end\n          end\n\n          it \"returns the implementations if metadata lists them all\" do\n            impls = [{'name' => 'init.sh', 'requirements' => ['shell']},\n                     {'name' => 'init.ps1', 'requirements' => ['powershell']}]\n            metadata.replace({'implementations' => impls}.to_json)\n\n            compile do\n              task = module_loader.load(:task, 'testmodule')\n              expect(task_t.instance?(task)).to be_truthy\n              expect(task.files).to eql([\n                {\"name\" => \"init.sh\", \"path\" => \"#{modules_dir}/testmodule/tasks/init.sh\"},\n                {\"name\" => \"init.ps1\", \"path\" => \"#{modules_dir}/testmodule/tasks/init.ps1\"}\n              ])\n              expect(task.metadata['implementations']).to eql([\n                {\"name\" => \"init.sh\", \"requirements\" => ['shell']},\n                {\"name\" => \"init.ps1\", \"requirements\" => ['powershell']}\n              ])\n            end\n          end\n\n          it \"returns a single implementation if metadata only specifies one implementation\" do\n            impls = [{'name' => 'init.ps1', 'requirements' => ['powershell']}]\n            metadata.replace({'implementations' => impls}.to_json)\n\n            compile do\n              task = module_loader.load(:task, 'testmodule')\n              expect(task_t.instance?(task)).to be_truthy\n              expect(task.files).to eql([\n                {\"name\" => \"init.ps1\", \"path\" => \"#{modules_dir}/testmodule/tasks/init.ps1\"}\n              ])\n              expect(task.metadata['implementations']).to eql([\n                {\"name\" => \"init.ps1\", \"requirements\" => ['powershell']}\n              ])\n            end\n          end\n\n          it \"fails if a specified implementation doesn't exist\" do\n            impls = [{'name' => 'init.sh', 'requirements' => ['shell']},\n                     {'name' => 'init.ps1', 'requirements' => ['powershell']},\n                     {'name' => 'init.rb', 'requirements' => ['puppet-agent']}]\n            metadata.replace({'implementations' => impls}.to_json)\n\n            compile do\n              expect {\n                module_loader.load(:task, 'testmodule')\n              }.to raise_error(Puppet::Module::Task::InvalidTask, /Task metadata for task testmodule specifies missing implementation init\\.rb/)\n            end\n          end\n\n          it \"fails if the implementations key isn't an array\" do\n            metadata.replace({'implementations' => {'init.rb' => []}}.to_json)\n\n            compile do\n              expect {\n                module_loader.load(:task, 'testmodule')\n              }.to raise_error(Puppet::Module::Task::InvalidMetadata, /Task metadata for task testmodule does not specify implementations as an array/)\n            end\n          end\n        end\n\n        context 'with multiple tasks sharing executables' do\n          let(:foo_metadata) { '{}' }\n          let(:bar_metadata) { '{}' }\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'foo.sh' => '',\n                'foo.ps1' => '',\n                'foo.json' => foo_metadata,\n                'bar.json' => bar_metadata,\n                'baz.json' => bar_metadata,\n              }\n            }\n          }\n\n          it 'loads a task that uses executables named after another task' do\n            metadata = {\n              implementations: [\n                {name: 'foo.sh', requirements: ['shell']},\n                {name: 'foo.ps1', requirements: ['powershell']},\n              ]\n            }\n            bar_metadata.replace(metadata.to_json)\n\n            compile do\n              task = module_loader.load(:task, 'testmodule::bar')\n              expect(task.files).to eql([\n                {'name' => 'foo.sh', 'path' => \"#{modules_dir}/testmodule/tasks/foo.sh\"},\n                {'name' => 'foo.ps1', 'path' => \"#{modules_dir}/testmodule/tasks/foo.ps1\"},\n              ])\n            end\n          end\n\n          it 'fails to load the task if it has no implementations section and no associated executables' do\n            compile do\n              expect {\n                module_loader.load(:task, 'testmodule::bar')\n              }.to raise_error(Puppet::Module::Task::InvalidTask, /No source besides task metadata was found/)\n            end\n          end\n\n          it 'fails to load the task if it has no files at all' do\n            compile do\n              expect(module_loader.load(:task, 'testmodule::qux')).to be_nil\n            end\n          end\n        end\n\n        context 'with adjacent directory for init task' do\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'init' => {\n                  'foo.sh' => 'echo hello'\n                },\n                'init.sh' => 'echo hello',\n                'init.json' => <<-JSON\n                {\n                  \"supports_noop\": true,\n                  \"parameters\": {\n                     \"message\": { \"type\": \"String\" }\n                  }\n                }\n              JSON\n              }\n            }\n          }\n\n          it 'loads the init task with parameters and implementations' do\n            compile do\n              task = module_loader.load(:task, 'testmodule')\n              expect(task_t.instance?(task)).to be_truthy\n              expect(task.files).to eql([{\"name\" => \"init.sh\", \"path\" => \"#{modules_dir}/testmodule/tasks/init.sh\"}])\n              expect(task.metadata['parameters']).to be_a(Hash)\n              expect(task.parameters['message']).to be_a(Puppet::Pops::Types::PStringType)\n            end\n          end\n        end\n\n        context 'with adjacent directory for named task' do\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'hello' => {\n                  'foo.sh' => 'echo hello'\n                },\n                'hello.sh' => 'echo hello',\n                'hello.json' => <<-JSON\n                {\n                  \"supports_noop\": true,\n                  \"parameters\": {\n                     \"message\": { \"type\": \"String\" }\n                  }\n                }\n              JSON\n              }\n            }\n          }\n\n          it 'loads a named task with parameters and implementations' do\n            compile do\n              task = module_loader.load(:task, 'testmodule::hello')\n              expect(task_t.instance?(task)).to be_truthy\n              expect(task.files).to eql([{\"name\" => \"hello.sh\", \"path\" => \"#{modules_dir}/testmodule/tasks/hello.sh\"}])\n              expect(task.metadata['parameters']).to be_a(Hash)\n              expect(task.parameters['message']).to be_a(Puppet::Pops::Types::PStringType)\n            end\n          end\n        end\n\n        context 'using more than two segments in the name' do\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'hello' => {\n                  'foo.sh' => 'echo hello'\n                }\n              }\n            }\n          }\n\n          it 'task is not found' do\n            compile do\n              expect(module_loader.load(:task, 'testmodule::hello::foo')).to be_nil\n            end\n          end\n        end\n\n        context 'that has no parameters' do\n          let(:testmodule) {\n            {\n              'tasks' => {\n                'hello' => 'echo hello',\n                'hello.json' => '{ \"supports_noop\": false }'             }\n            }\n          }\n\n          it 'loads the task with parameters set to undef' do\n            compile do\n              task = module_loader.load(:task, 'testmodule::hello')\n              expect(task_t.instance?(task)).to be_truthy\n              expect(task.metadata['parameters']).to be_nil\n            end\n          end\n        end\n      end\n    end\n  end\nend\nend\nend\n\n"
  },
  {
    "path": "spec/unit/pops/types/type_acceptor_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops/types/type_acceptor'\n\nclass PuppetSpec::TestTypeAcceptor\n  include Puppet::Pops::Types::TypeAcceptor\n  attr_reader :visitors, :guard\n\n  def initialize\n    @visitors = []\n    @guard = nil\n  end\n\n  def visit(type, guard)\n    @visitors << type\n    @guard = guard\n  end\nend\n\nmodule Puppet::Pops::Types\ndescribe 'the Puppet::Pops::Types::TypeAcceptor' do\n\n  let!(:acceptor_class) { PuppetSpec::TestTypeAcceptor }\n\n  let(:acceptor) { acceptor_class.new }\n  let(:guard) { RecursionGuard.new }\n\n  it \"should get a visit from the type that accepts it\" do\n    PAnyType::DEFAULT.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(PAnyType::DEFAULT)\n  end\n\n  it \"should receive the guard as an argument\" do\n    PAnyType::DEFAULT.accept(acceptor, guard)\n    expect(acceptor.guard).to equal(guard)\n  end\n\n  it \"should get a visit from the type of a Type that accepts it\" do\n    t = PTypeType.new(PAnyType::DEFAULT)\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PAnyType::DEFAULT)\n  end\n\n  [\n    PTypeType,\n    PNotUndefType,\n    PIterableType,\n    PIteratorType,\n    POptionalType\n  ].each do |tc|\n    it \"should get a visit from the contained type of an #{tc.class.name} that accepts it\" do\n      t = tc.new(PStringType::DEFAULT)\n      t.accept(acceptor, nil)\n      expect(acceptor.visitors).to include(t, PStringType::DEFAULT)\n    end\n  end\n\n  it \"should get a visit from the size type of String type that accepts it\" do\n    sz = PIntegerType.new(0,4)\n    t = PStringType.new(sz)\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, sz)\n  end\n\n  it \"should get a visit from all contained types of an Array type that accepts it\" do\n    sz = PIntegerType.new(0,4)\n    t = PArrayType.new(PAnyType::DEFAULT, sz)\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PAnyType::DEFAULT, sz)\n  end\n\n  it \"should get a visit from all contained types of a Hash type that accepts it\" do\n    sz = PIntegerType.new(0,4)\n    t = PHashType.new(PStringType::DEFAULT, PAnyType::DEFAULT, sz)\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PStringType::DEFAULT, PAnyType::DEFAULT, sz)\n  end\n\n  it \"should get a visit from all contained types of a Tuple type that accepts it\" do\n    sz = PIntegerType.new(0,4)\n    t = PTupleType.new([PStringType::DEFAULT, PIntegerType::DEFAULT], sz)\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PStringType::DEFAULT, PIntegerType::DEFAULT, sz)\n  end\n\n  it \"should get a visit from all contained types of a Struct type that accepts it\" do\n    t = PStructType.new([PStructElement.new(PStringType::DEFAULT, PIntegerType::DEFAULT)])\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PStringType::DEFAULT, PIntegerType::DEFAULT)\n  end\n\n  it \"should get a visit from all contained types of a Callable type that accepts it\" do\n    sz = PIntegerType.new(0,4)\n    args = PTupleType.new([PStringType::DEFAULT, PIntegerType::DEFAULT], sz)\n    block = PCallableType::DEFAULT\n    t = PCallableType.new(args, block)\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PStringType::DEFAULT, PIntegerType::DEFAULT, sz, args, block)\n  end\n\n  it \"should get a visit from all contained types of a Variant type that accepts it\" do\n    t = PVariantType.new([PStringType::DEFAULT, PIntegerType::DEFAULT])\n    t.accept(acceptor, nil)\n    expect(acceptor.visitors).to include(t, PStringType::DEFAULT, PIntegerType::DEFAULT)\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/type_asserter_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops::Types\ndescribe 'the type asserter' do\n  let!(:asserter) { TypeAsserter }\n\n  context 'when deferring formatting of subject'\n  it 'can use an array' do\n    expect{ asserter.assert_instance_of(['The %s in the %s', 'gizmo', 'gadget'], PIntegerType::DEFAULT, 'lens') }.to(\n    raise_error(TypeAssertionError, 'The gizmo in the gadget has wrong type, expects an Integer value, got String'))\n  end\n\n  it 'can use an array obtained from block' do\n    expect do\n      asserter.assert_instance_of('gizmo', PIntegerType::DEFAULT, 'lens') { |s| ['The %s in the %s', s, 'gadget'] }\n    end.to(raise_error(TypeAssertionError, 'The gizmo in the gadget has wrong type, expects an Integer value, got String'))\n  end\n\n  it 'can use an subject obtained from zero argument block' do\n    expect do\n      asserter.assert_instance_of(nil, PIntegerType::DEFAULT, 'lens') { 'The gizmo in the gadget' }\n    end.to(raise_error(TypeAssertionError, 'The gizmo in the gadget has wrong type, expects an Integer value, got String'))\n  end\n\n  it 'does not produce a string unless the assertion fails' do\n    expect(TypeAsserter).not_to receive(:report_type_mismatch)\n    asserter.assert_instance_of(nil, PIntegerType::DEFAULT, 1)\n  end\n\n  it 'does not format string unless the assertion fails' do\n    fmt_string = 'The %s in the %s'\n    expect(fmt_string).not_to receive(:'%')\n    asserter.assert_instance_of([fmt_string, 'gizmo', 'gadget'], PIntegerType::DEFAULT, 1)\n  end\n\n  it 'does not call block unless the assertion fails' do\n    expect do\n      asserter.assert_instance_of(nil, PIntegerType::DEFAULT, 1) { |s| raise Error }\n    end.not_to raise_error\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/type_calculator_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'The type calculator' do\n  let(:calculator) { TypeCalculator.new }\n\n  def range_t(from, to)\n   PIntegerType.new(from, to)\n  end\n\n  def pattern_t(*patterns)\n    TypeFactory.pattern(*patterns)\n  end\n\n  def regexp_t(pattern)\n    TypeFactory.regexp(pattern)\n  end\n\n  def string_t(string = nil)\n    TypeFactory.string(string)\n  end\n\n  def constrained_string_t(size_type)\n    TypeFactory.string(size_type)\n  end\n\n  def callable_t(*params)\n    TypeFactory.callable(*params)\n  end\n\n  def all_callables_t\n    TypeFactory.all_callables\n  end\n\n  def enum_t(*strings)\n    TypeFactory.enum(*strings)\n  end\n\n  def variant_t(*types)\n    TypeFactory.variant(*types)\n  end\n\n  def empty_variant_t()\n    t = TypeFactory.variant()\n    # assert this to ensure we did get an empty variant (or tests may pass when not being empty)\n    raise 'typefactory did not return empty variant' unless t.types.empty?\n    t\n  end\n\n  def type_alias_t(name, type_string)\n    type_expr = Parser::EvaluatingParser.new.parse_string(type_string)\n    TypeFactory.type_alias(name, type_expr)\n  end\n\n  def type_reference_t(type_string)\n    TypeFactory.type_reference(type_string)\n  end\n\n  def integer_t\n    TypeFactory.integer\n  end\n\n  def array_t(t, s = nil)\n    TypeFactory.array_of(t, s)\n  end\n\n  def empty_array_t\n    array_t(unit_t, range_t(0,0))\n  end\n\n  def hash_t(k,v,s = nil)\n    TypeFactory.hash_of(v, k, s)\n  end\n\n  def data_t\n    TypeFactory.data\n  end\n\n  def factory\n    TypeFactory\n  end\n\n  def collection_t(size_type = nil)\n    TypeFactory.collection(size_type)\n  end\n\n  def tuple_t(*types)\n    TypeFactory.tuple(types)\n  end\n\n  def constrained_tuple_t(size_type, *types)\n    TypeFactory.tuple(types, size_type)\n  end\n\n  def struct_t(type_hash)\n    TypeFactory.struct(type_hash)\n  end\n\n  def any_t\n    TypeFactory.any\n  end\n\n  def optional_t(t)\n    TypeFactory.optional(t)\n  end\n\n  def type_t(t)\n    TypeFactory.type_type(t)\n  end\n\n  def not_undef_t(t = nil)\n    TypeFactory.not_undef(t)\n  end\n\n  def undef_t\n    TypeFactory.undef\n  end\n\n  def unit_t\n    # Cannot be created via factory, the type is private to the type system\n    PUnitType::DEFAULT\n  end\n\n  def runtime_t(t, c)\n    TypeFactory.runtime(t, c)\n  end\n\n  def object_t(hash)\n    TypeFactory.object(hash)\n  end\n\n  def iterable_t(t = nil)\n    TypeFactory.iterable(t)\n  end\n\n  def types\n    Types\n  end\n\n  context 'when inferring ruby' do\n\n    it 'integer translates to PIntegerType' do\n      expect(calculator.infer(1).class).to eq(PIntegerType)\n    end\n\n    it 'large integer translates to PIntegerType' do\n      expect(calculator.infer(2**33).class).to eq(PIntegerType)\n    end\n\n    it 'float translates to PFloatType' do\n      expect(calculator.infer(1.3).class).to eq(PFloatType)\n    end\n\n    it 'string translates to PStringType' do\n      expect(calculator.infer('foo').class).to eq(PStringType)\n    end\n\n    it 'inferred string type knows the string value' do\n      t = calculator.infer('foo')\n      expect(t.class).to eq(PStringType)\n      expect(t.value).to eq('foo')\n    end\n\n    it 'boolean true translates to PBooleanType::TRUE' do\n      expect(calculator.infer(true)).to eq(PBooleanType::TRUE)\n    end\n\n    it 'boolean false translates to PBooleanType::FALSE' do\n      expect(calculator.infer(false)).to eq(PBooleanType::FALSE)\n    end\n\n    it 'regexp translates to PRegexpType' do\n      expect(calculator.infer(/^a regular expression$/).class).to eq(PRegexpType)\n    end\n\n    it 'iterable translates to PIteratorType' do\n      expect(calculator.infer(Iterable.on(1))).to be_a(PIteratorType)\n    end\n\n    it 'nil translates to PUndefType' do\n      expect(calculator.infer(nil).class).to eq(PUndefType)\n    end\n\n    it ':undef translates to PUndefType' do\n      expect(calculator.infer(:undef).class).to eq(PUndefType)\n    end\n\n    it 'an instance of class Foo translates to PRuntimeType[ruby, Foo]' do\n      ::Foo = Class.new\n      begin\n        t = calculator.infer(::Foo.new)\n        expect(t.class).to eq(PRuntimeType)\n        expect(t.runtime).to eq(:ruby)\n        expect(t.runtime_type_name).to eq('Foo')\n      ensure\n        Object.send(:remove_const, :Foo)\n      end\n    end\n\n    it 'Class Foo translates to PTypeType[PRuntimeType[ruby, Foo]]' do\n      ::Foo = Class.new\n      begin\n        t = calculator.infer(::Foo)\n        expect(t.class).to eq(PTypeType)\n        tt = t.type\n        expect(tt.class).to eq(PRuntimeType)\n        expect(tt.runtime).to eq(:ruby)\n        expect(tt.runtime_type_name).to eq('Foo')\n      ensure\n        Object.send(:remove_const, :Foo)\n      end\n    end\n\n    it 'Module FooModule translates to PTypeType[PRuntimeType[ruby, FooModule]]' do\n      ::FooModule = Module.new\n      begin\n        t = calculator.infer(::FooModule)\n        expect(t.class).to eq(PTypeType)\n        tt = t.type\n        expect(tt.class).to eq(PRuntimeType)\n        expect(tt.runtime).to eq(:ruby)\n        expect(tt.runtime_type_name).to eq('FooModule')\n      ensure\n        Object.send(:remove_const, :FooModule)\n      end\n    end\n\n    context 'sensitive' do\n      it 'translates to PSensitiveType' do\n        expect(calculator.infer(PSensitiveType::Sensitive.new(\"hunter2\")).class).to eq(PSensitiveType)\n      end\n    end\n\n    context 'binary' do\n      it 'translates to PBinaryType' do\n        expect(calculator.infer(PBinaryType::Binary.from_binary_string(\"binary\")).class).to eq(PBinaryType)\n      end\n    end\n\n    context 'version' do\n      it 'translates to PVersionType' do\n        expect(calculator.infer(SemanticPuppet::Version.new(1,0,0)).class).to eq(PSemVerType)\n      end\n\n      it 'range translates to PVersionRangeType' do\n        expect(calculator.infer(SemanticPuppet::VersionRange.parse('1.x')).class).to eq(PSemVerRangeType)\n      end\n\n      it 'translates to a limited PVersionType by infer_set' do\n        v = SemanticPuppet::Version.new(1,0,0)\n        t = calculator.infer_set(v)\n        expect(t.class).to eq(PSemVerType)\n        expect(t.ranges.size).to eq(1)\n        expect(t.ranges[0].begin).to eq(v)\n        expect(t.ranges[0].end).to eq(v)\n      end\n    end\n\n    context 'timespan' do\n      it 'translates to PTimespanType' do\n        expect(calculator.infer(Time::Timespan.from_fields_hash('days' => 2))).to be_a(PTimespanType)\n      end\n\n      it 'translates to a limited PTimespanType by infer_set' do\n        ts = Time::Timespan.from_fields_hash('days' => 2)\n        t = calculator.infer_set(ts)\n        expect(t.class).to eq(PTimespanType)\n        expect(t.from).to be(ts)\n        expect(t.to).to be(ts)\n      end\n    end\n\n    context 'timestamp' do\n      it 'translates to PTimespanType' do\n        expect(calculator.infer(Time::Timestamp.now)).to be_a(PTimestampType)\n      end\n\n      it 'translates to a limited PTimespanType by infer_set' do\n        ts = Time::Timestamp.now\n        t = calculator.infer_set(ts)\n        expect(t.class).to eq(PTimestampType)\n        expect(t.from).to be(ts)\n        expect(t.to).to be(ts)\n      end\n    end\n\n    context 'array' do\n      let(:derived) do\n        Class.new(Array).new([1,2])\n      end\n\n      let(:derived_object) do\n        Class.new(Array) do\n          include PuppetObject\n\n          def self._pcore_type\n            @type ||= TypeFactory.object('name' => 'DerivedObjectArray')\n          end\n        end.new([1,2])\n      end\n\n      it 'translates to PArrayType' do\n        expect(calculator.infer([1,2]).class).to eq(PArrayType)\n      end\n\n      it 'translates derived Array to PRuntimeType' do\n        expect(calculator.infer(derived).class).to eq(PRuntimeType)\n      end\n\n      it 'translates derived Puppet Object Array to PObjectType' do\n        expect(calculator.infer(derived_object).class).to eq(PObjectType)\n      end\n\n      it 'Instance of derived Array class is not instance of Array type' do\n        expect(PArrayType::DEFAULT).not_to be_instance(derived)\n      end\n\n      it 'Instance of derived Array class is instance of Runtime type' do\n        expect(runtime_t('ruby', nil)).to be_instance(derived)\n      end\n\n      it 'Instance of derived Puppet Object Array class is not instance of Array type' do\n        expect(PArrayType::DEFAULT).not_to be_instance(derived_object)\n      end\n\n      it 'Instance of derived Puppet Object Array class is instance of Object type' do\n        expect(object_t('name' => 'DerivedObjectArray')).to be_instance(derived_object)\n      end\n\n      it 'with integer values translates to PArrayType[PIntegerType]' do\n        expect(calculator.infer([1,2]).element_type.class).to eq(PIntegerType)\n      end\n\n      it 'with 32 and 64 bit integer values translates to PArrayType[PIntegerType]' do\n        expect(calculator.infer([1,2**33]).element_type.class).to eq(PIntegerType)\n      end\n\n      it 'Range of integer values are computed' do\n        t = calculator.infer([-3,0,42]).element_type\n        expect(t.class).to eq(PIntegerType)\n        expect(t.from).to eq(-3)\n        expect(t.to).to eq(42)\n      end\n\n      it 'Compound string values are converted to enums' do\n        t = calculator.infer(['a','b', 'c']).element_type\n        expect(t.class).to eq(PEnumType)\n        expect(t.values).to eq(['a', 'b', 'c'])\n      end\n\n      it 'with integer and float values translates to PArrayType[PNumericType]' do\n        expect(calculator.infer([1,2.0]).element_type.class).to eq(PNumericType)\n      end\n\n      it 'with integer and string values translates to PArrayType[PScalarDataType]' do\n        expect(calculator.infer([1,'two']).element_type.class).to eq(PScalarDataType)\n      end\n\n      it 'with float and string values translates to PArrayType[PScalarDataType]' do\n        expect(calculator.infer([1.0,'two']).element_type.class).to eq(PScalarDataType)\n      end\n\n      it 'with integer, float, and string values translates to PArrayType[PScalarDataType]' do\n        expect(calculator.infer([1, 2.0,'two']).element_type.class).to eq(PScalarDataType)\n      end\n\n      it 'with integer and regexp values translates to PArrayType[PScalarType]' do\n        expect(calculator.infer([1, /two/]).element_type.class).to eq(PScalarType)\n      end\n\n      it 'with string and regexp values translates to PArrayType[PScalarType]' do\n        expect(calculator.infer(['one', /two/]).element_type.class).to eq(PScalarType)\n      end\n\n      it 'with string and symbol values translates to PArrayType[PAnyType]' do\n        expect(calculator.infer(['one', :two]).element_type.class).to eq(PAnyType)\n      end\n\n      it 'with integer and nil values translates to PArrayType[PIntegerType]' do\n        expect(calculator.infer([1, nil]).element_type.class).to eq(PIntegerType)\n      end\n\n      it 'with integer value, and array of string values, translates to Array[Data]' do\n        expect(calculator.infer([1, ['two']]).element_type.name).to eq('Data')\n      end\n\n      it 'with integer value, and hash of string => string values, translates to Array[Data]' do\n        expect(calculator.infer([1, {'two' => 'three'} ]).element_type.name).to eq('Data')\n      end\n\n      it 'with integer value, and hash of integer => string values, translates to Array[RichData]' do\n        expect(calculator.infer([1, {2 => 'three'} ]).element_type.name).to eq('RichData')\n      end\n\n      it 'with integer, regexp, and binary values translates to Array[RichData]' do\n        expect(calculator.infer([1, /two/, PBinaryType::Binary.from_string('three')]).element_type.name).to eq('RichData')\n      end\n\n      it 'with arrays of string values translates to PArrayType[PArrayType[PStringType]]' do\n        et = calculator.infer([['first', 'array'], ['second','array']])\n        expect(et.class).to eq(PArrayType)\n        et = et.element_type\n        expect(et.class).to eq(PArrayType)\n        et = et.element_type\n        expect(et.class).to eq(PEnumType)\n      end\n\n      it 'with array of string values and array of integers translates to PArrayType[PArrayType[PScalarDataType]]' do\n        et = calculator.infer([['first', 'array'], [1,2]])\n        expect(et.class).to eq(PArrayType)\n        et = et.element_type\n        expect(et.class).to eq(PArrayType)\n        et = et.element_type\n        expect(et.class).to eq(PScalarDataType)\n      end\n\n      it 'with hashes of string values translates to PArrayType[PHashType[PEnumType]]' do\n        et = calculator.infer([{:first => 'first', :second => 'second' }, {:first => 'first', :second => 'second' }])\n        expect(et.class).to eq(PArrayType)\n        et = et.element_type\n        expect(et.class).to eq(PHashType)\n        et = et.value_type\n        expect(et.class).to eq(PEnumType)\n      end\n\n      it 'with hash of string values and hash of integers translates to PArrayType[PHashType[PScalarDataType]]' do\n        et = calculator.infer([{:first => 'first', :second => 'second' }, {:first => 1, :second => 2 }])\n        expect(et.class).to eq(PArrayType)\n        et = et.element_type\n        expect(et.class).to eq(PHashType)\n        et = et.value_type\n        expect(et.class).to eq(PScalarDataType)\n      end\n    end\n\n    context 'hash' do\n      let(:derived) do\n        Class.new(Hash)[:first => 1, :second => 2]\n      end\n\n      let(:derived_object) do\n        Class.new(Hash) do\n          include PuppetObject\n\n          def self._pcore_type\n            @type ||= TypeFactory.object('name' => 'DerivedObjectHash')\n          end\n        end[:first => 1, :second => 2]\n      end\n\n      it 'translates to PHashType' do\n        expect(calculator.infer({:first => 1, :second => 2}).class).to eq(PHashType)\n      end\n\n      it 'translates derived Hash to PRuntimeType' do\n        expect(calculator.infer(derived).class).to eq(PRuntimeType)\n      end\n\n      it 'translates derived Puppet Object Hash to PObjectType' do\n        expect(calculator.infer(derived_object).class).to eq(PObjectType)\n      end\n\n      it 'Instance of derived Hash class is not instance of Hash type' do\n        expect(PHashType::DEFAULT).not_to be_instance(derived)\n      end\n\n      it 'Instance of derived Hash class is instance of Runtime type' do\n        expect(runtime_t('ruby', nil)).to be_instance(derived)\n      end\n\n      it 'Instance of derived Puppet Object Hash class is not instance of Hash type' do\n        expect(PHashType::DEFAULT).not_to be_instance(derived_object)\n      end\n\n      it 'Instance of derived Puppet Object Hash class is instance of Object type' do\n        expect(object_t('name' => 'DerivedObjectHash')).to be_instance(derived_object)\n      end\n\n      it 'with symbolic keys translates to PHashType[PRuntimeType[ruby, Symbol], value]' do\n        k = calculator.infer({:first => 1, :second => 2}).key_type\n        expect(k.class).to eq(PRuntimeType)\n        expect(k.runtime).to eq(:ruby)\n        expect(k.runtime_type_name).to eq('Symbol')\n      end\n\n      it 'with string keys translates to PHashType[PEnumType, value]' do\n        expect(calculator.infer({'first' => 1, 'second' => 2}).key_type.class).to eq(PEnumType)\n      end\n\n      it 'with integer values translates to PHashType[key, PIntegerType]' do\n        expect(calculator.infer({:first => 1, :second => 2}).value_type.class).to eq(PIntegerType)\n      end\n\n      it 'when empty infers a type that answers true to is_the_empty_hash?' do\n        expect(calculator.infer({}).is_the_empty_hash?).to eq(true)\n        expect(calculator.infer_set({}).is_the_empty_hash?).to eq(true)\n      end\n\n      it 'when empty is assignable to any PHashType' do\n        expect(calculator.assignable?(hash_t(string_t, string_t), calculator.infer({}))).to eq(true)\n      end\n\n      it 'when empty is not assignable to a PHashType with from size > 0' do\n        expect(calculator.assignable?(hash_t(string_t,string_t,range_t(1, 1)), calculator.infer({}))).to eq(false)\n      end\n\n      context 'using infer_set' do\n        it \"with 'first' and 'second' keys translates to PStructType[{first=>value,second=>value}]\" do\n          t = calculator.infer_set({'first' => 1, 'second' => 2})\n          expect(t.class).to eq(PStructType)\n          expect(t.elements.size).to eq(2)\n          expect(t.elements.map { |e| e.name }.sort).to eq(['first', 'second'])\n        end\n\n        it 'with string keys and string and array values translates to PStructType[{key1=>PStringType,key2=>PTupleType}]' do\n          t = calculator.infer_set({ 'mode' => 'read', 'path' => ['foo', 'fee' ] })\n          expect(t.class).to eq(PStructType)\n          expect(t.elements.size).to eq(2)\n          els = t.elements.map { |e| e.value_type }.sort {|a,b| a.to_s <=> b.to_s }\n          expect(els[0].class).to eq(PStringType)\n          expect(els[1].class).to eq(PTupleType)\n        end\n\n        it 'with mixed string and non-string keys translates to PHashType' do\n          t = calculator.infer_set({ 1 => 'first', 'second' => 'second' })\n          expect(t.class).to eq(PHashType)\n        end\n\n        it 'with empty string keys translates to PHashType' do\n          t = calculator.infer_set({ '' => 'first', 'second' => 'second' })\n          expect(t.class).to eq(PHashType)\n        end\n      end\n    end\n\n    it 'infers an instance of an anonymous class to Runtime[ruby]' do\n      cls = Class.new do\n        attr_reader :name\n        def initialize(name)\n          @name = name\n        end\n      end\n      t = calculator.infer(cls.new('test'))\n      expect(t.class).to eql(PRuntimeType)\n      expect(t.runtime).to eql(:ruby)\n      expect(t.name_or_pattern).to eql(nil)\n    end\n  end\n\n  context 'patterns' do\n    it 'constructs a PPatternType' do\n      t = pattern_t('a(b)c')\n      expect(t.class).to eq(PPatternType)\n      expect(t.patterns.size).to eq(1)\n      expect(t.patterns[0].class).to eq(PRegexpType)\n      expect(t.patterns[0].pattern).to eq('a(b)c')\n      expect(t.patterns[0].regexp.match('abc')[1]).to eq('b')\n    end\n\n    it 'constructs a PEnumType with multiple strings' do\n      t = enum_t('a', 'b', 'c', 'abc')\n      expect(t.values).to eq(['a', 'b', 'c', 'abc'].sort)\n    end\n  end\n\n  # Deal with cases not covered by computing common type\n  context 'when computing common type' do\n    it 'computes given resource type commonality' do\n      r1 = PResourceType.new('File', nil)\n      r2 = PResourceType.new('File', nil)\n      expect(calculator.common_type(r1, r2).to_s).to eq('File')\n\n\n      r2 = PResourceType.new('File', '/tmp/foo')\n      expect(calculator.common_type(r1, r2).to_s).to eq('File')\n\n      r1 = PResourceType.new('File', '/tmp/foo')\n      expect(calculator.common_type(r1, r2).to_s).to eq(\"File['/tmp/foo']\")\n\n      r1 = PResourceType.new('File', '/tmp/bar')\n      expect(calculator.common_type(r1, r2).to_s).to eq('File')\n\n      r2 = PResourceType.new('Package', 'apache')\n      expect(calculator.common_type(r1, r2).to_s).to eq('Resource')\n    end\n\n    it 'computes given hostclass type commonality' do\n      r1 = PClassType.new('foo')\n      r2 = PClassType.new('foo')\n      expect(calculator.common_type(r1, r2).to_s).to eq('Class[foo]')\n\n      r2 = PClassType.new('bar')\n      expect(calculator.common_type(r1, r2).to_s).to eq('Class')\n\n      r2 = PClassType.new(nil)\n      expect(calculator.common_type(r1, r2).to_s).to eq('Class')\n\n      r1 = PClassType.new(nil)\n      expect(calculator.common_type(r1, r2).to_s).to eq('Class')\n    end\n\n    context 'of strings' do\n      it 'computes commonality' do\n        t1 = string_t('abc')\n        t2 = string_t('xyz')\n        common_t = calculator.common_type(t1,t2)\n        expect(common_t.class).to eq(PEnumType)\n        expect(common_t.values).to eq(['abc', 'xyz'])\n      end\n\n      it 'computes common size_type' do\n        t1 = constrained_string_t(range_t(3,6))\n        t2 = constrained_string_t(range_t(2,4))\n        common_t = calculator.common_type(t1,t2)\n        expect(common_t.class).to eq(PStringType)\n        expect(common_t.size_type).to eq(range_t(2,6))\n      end\n\n      it 'computes common size_type to be undef when one of the types has no size_type' do\n        t1 = string_t\n        t2 = constrained_string_t(range_t(2,4))\n        common_t = calculator.common_type(t1,t2)\n        expect(common_t.class).to eq(PStringType)\n        expect(common_t.size_type).to be_nil\n      end\n\n      it 'computes values to be empty if the one has empty values' do\n        t1 = string_t('apa')\n        t2 = constrained_string_t(range_t(2,4))\n        common_t = calculator.common_type(t1,t2)\n        expect(common_t.class).to eq(PStringType)\n        expect(common_t.value).to be_nil\n      end\n    end\n\n    it 'computes pattern commonality' do\n      t1 = pattern_t('abc')\n      t2 = pattern_t('xyz')\n      common_t = calculator.common_type(t1,t2)\n      expect(common_t.class).to eq(PPatternType)\n      expect(common_t.patterns.map { |pr| pr.pattern }).to eq(['abc', 'xyz'])\n      expect(common_t.to_s).to eq('Pattern[/abc/, /xyz/]')\n    end\n\n    it 'computes enum commonality to value set sum' do\n      t1 = enum_t('a', 'b', 'c')\n      t2 = enum_t('x', 'y', 'z')\n      common_t = calculator.common_type(t1, t2)\n      expect(common_t).to eq(enum_t('a', 'b', 'c', 'x', 'y', 'z'))\n    end\n\n    it 'computed variant commonality to type union where added types are not sub-types' do\n      a_t1 = integer_t\n      a_t2 = enum_t('b')\n      v_a = variant_t(a_t1, a_t2)\n      b_t1 = integer_t\n      b_t2 = enum_t('a')\n      v_b = variant_t(b_t1, b_t2)\n      common_t = calculator.common_type(v_a, v_b)\n      expect(common_t.class).to eq(PVariantType)\n      expect(Set.new(common_t.types)).to  eq(Set.new([a_t1, a_t2, b_t1, b_t2]))\n    end\n\n    it 'computed variant commonality to type union where added types are sub-types' do\n      a_t1 = integer_t\n      a_t2 = string_t\n      v_a = variant_t(a_t1, a_t2)\n      b_t1 = integer_t\n      b_t2 = enum_t('a')\n      v_b = variant_t(b_t1, b_t2)\n      common_t = calculator.common_type(v_a, v_b)\n      expect(common_t.class).to eq(PVariantType)\n      expect(Set.new(common_t.types)).to  eq(Set.new([a_t1, a_t2]))\n    end\n\n    context 'commonality of scalar data types' do\n      it 'Numeric and String == ScalarData' do\n        expect(calculator.common_type(PNumericType::DEFAULT, PStringType::DEFAULT).class).to eq(PScalarDataType)\n      end\n\n      it 'Numeric and Boolean == ScalarData' do\n        expect(calculator.common_type(PNumericType::DEFAULT, PBooleanType::DEFAULT).class).to eq(PScalarDataType)\n      end\n\n      it 'String and Boolean == ScalarData' do\n        expect(calculator.common_type(PStringType::DEFAULT, PBooleanType::DEFAULT).class).to eq(PScalarDataType)\n      end\n    end\n\n    context 'commonality of scalar types' do\n      it 'Regexp and Integer == Scalar' do\n        expect(calculator.common_type(PRegexpType::DEFAULT, PScalarDataType::DEFAULT).class).to eq(PScalarType)\n      end\n\n      it 'Regexp and SemVer == ScalarData' do\n        expect(calculator.common_type(PRegexpType::DEFAULT, PSemVerType::DEFAULT).class).to eq(PScalarType)\n      end\n\n      it 'Timestamp and Timespan == ScalarData' do\n        expect(calculator.common_type(PTimestampType::DEFAULT, PTimespanType::DEFAULT).class).to eq(PScalarType)\n      end\n\n      it 'Timestamp and Boolean == ScalarData' do\n        expect(calculator.common_type(PTimestampType::DEFAULT, PBooleanType::DEFAULT).class).to eq(PScalarType)\n      end\n    end\n\n    context 'of callables' do\n      it 'incompatible instances => generic callable' do\n        t1 = callable_t(String)\n        t2 = callable_t(Integer)\n        common_t = calculator.common_type(t1, t2)\n        expect(common_t.class).to be(PCallableType)\n        expect(common_t.param_types).to be_nil\n        expect(common_t.block_type).to be_nil\n      end\n\n      it 'compatible instances => the most specific' do\n        t1 = callable_t(String)\n        scalar_t = PScalarType.new\n        t2 = callable_t(scalar_t)\n        common_t = calculator.common_type(t1, t2)\n        expect(common_t.class).to be(PCallableType)\n        expect(common_t.param_types.class).to be(PTupleType)\n        expect(common_t.param_types.types).to eql([string_t])\n        expect(common_t.block_type).to be_nil\n      end\n\n      it 'block_type is included in the check (incompatible block)' do\n        b1 = callable_t(String)\n        b2 = callable_t(Integer)\n        t1 = callable_t(String, b1)\n        t2 = callable_t(String, b2)\n        common_t = calculator.common_type(t1, t2)\n        expect(common_t.class).to be(PCallableType)\n        expect(common_t.param_types).to be_nil\n        expect(common_t.block_type).to be_nil\n      end\n\n      it 'block_type is included in the check (compatible block)' do\n        b1 = callable_t(String)\n        t1 = callable_t(String, b1)\n        scalar_t = PScalarType::DEFAULT\n        b2 = callable_t(scalar_t)\n        t2 = callable_t(String, b2)\n        common_t = calculator.common_type(t1, t2)\n        expect(common_t.param_types.class).to be(PTupleType)\n        expect(common_t.block_type).to eql(callable_t(scalar_t))\n      end\n\n      it 'return_type is included in the check (incompatible return_type)' do\n        t1 = callable_t([String], String)\n        t2 = callable_t([String], Integer)\n        common_t = calculator.common_type(t1, t2)\n        expect(common_t.class).to be(PCallableType)\n        expect(common_t.param_types).to be_nil\n        expect(common_t.return_type).to be_nil\n      end\n\n      it 'return_type is included in the check (compatible return_type)' do\n        t1 = callable_t([String], Numeric)\n        t2 = callable_t([String], Integer)\n        common_t = calculator.common_type(t1, t2)\n        expect(common_t.class).to be(PCallableType)\n        expect(common_t.param_types).to be_a(PTupleType)\n        expect(common_t.return_type).to eql(PNumericType::DEFAULT)\n      end\n    end\n  end\n\n  context 'computes assignability' do\n    include_context 'types_setup'\n\n    it 'such that all types are assignable to themselves' do\n      all_types.each do |tc|\n        t = tc::DEFAULT\n        expect(t).to be_assignable_to(t)\n      end\n    end\n\n    context 'for Unit, such that' do\n      it 'all types are assignable to Unit' do\n        t = PUnitType::DEFAULT\n        all_types.each { |t2| expect(t2::DEFAULT).to be_assignable_to(t) }\n      end\n\n      it 'Unit is assignable to all other types' do\n        t = PUnitType::DEFAULT\n        all_types.each { |t2| expect(t).to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Unit is assignable to Unit' do\n        t = PUnitType::DEFAULT\n        t2 = PUnitType::DEFAULT\n        expect(t).to be_assignable_to(t2)\n      end\n    end\n\n    context 'for Any, such that' do\n      it 'all types are assignable to Any' do\n        t = PAnyType::DEFAULT\n        all_types.each { |t2| expect(t2::DEFAULT).to be_assignable_to(t) }\n      end\n\n      it 'Any is not assignable to anything but Any and Optional (implied Optional[Any])' do\n        tested_types = all_types() - [PAnyType, POptionalType]\n        t = PAnyType::DEFAULT\n        tested_types.each { |t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context \"for NotUndef, such that\" do\n      it 'all types except types assignable from Undef are assignable to NotUndef' do\n        t = not_undef_t\n        tc = TypeCalculator.singleton\n        undef_t = PUndefType::DEFAULT\n        all_types.each do |c|\n          t2 = c::DEFAULT\n          if tc.assignable?(t2, undef_t)\n            expect(t2).not_to be_assignable_to(t)\n          else\n            expect(t2).to be_assignable_to(t)\n          end\n        end\n      end\n\n      it 'type NotUndef[T] is assignable from T unless T is assignable from Undef ' do\n        tc = TypeCalculator.singleton\n        undef_t = PUndefType::DEFAULT\n        all_types().select do |c|\n          t2 = c::DEFAULT\n          not_undef_t = not_undef_t(t2)\n          if tc.assignable?(t2, undef_t)\n            expect(t2).not_to be_assignable_to(not_undef_t)\n          else\n            expect(t2).to be_assignable_to(not_undef_t)\n          end\n        end\n      end\n\n      it 'type T is assignable from NotUndef[T] unless T is assignable from Undef' do\n        tc = TypeCalculator.singleton\n        undef_t = PUndefType::DEFAULT\n        all_types().select do |c|\n          t2 = c::DEFAULT\n          not_undef_t = not_undef_t(t2)\n          unless tc.assignable?(t2, undef_t)\n            expect(not_undef_t).to be_assignable_to(t2)\n          end\n        end\n      end\n    end\n\n    context \"for TypeReference, such that\" do\n      it 'no other type is assignable' do\n        t = PTypeReferenceType::DEFAULT\n        all_instances = (all_types - [\n          PTypeReferenceType, # Avoid comparison with t\n          PTypeAliasType      # DEFAULT resolves to PTypeReferenceType::DEFAULT, i.e. t\n        ]).map {|c| c::DEFAULT }\n\n        # Add a non-empty variant\n        all_instances << variant_t(PAnyType::DEFAULT, PUnitType::DEFAULT)\n        # Add a type alias that doesn't resolve to 't'\n        all_instances << type_alias_t('MyInt', 'Integer').resolve(nil)\n\n        all_instances.each { |i| expect(i).not_to be_assignable_to(t) }\n      end\n\n      it 'a TypeReference to the exact same type is assignable' do\n        expect(type_reference_t('Integer[0,10]')).to be_assignable_to(type_reference_t('Integer[0,10]'))\n      end\n\n      it 'a TypeReference to the different type is not assignable' do\n        expect(type_reference_t('String')).not_to be_assignable_to(type_reference_t('Integer'))\n      end\n\n      it 'a TypeReference to the different type is not assignable even if the referenced type is' do\n        expect(type_reference_t('Integer[1,2]')).not_to be_assignable_to(type_reference_t('Integer[0,3]'))\n      end\n    end\n\n    context 'for Data, such that' do\n      let(:data) { TypeFactory.data }\n      data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|\n        it \"it is assignable from #{tc.name}\" do\n          expect(tc).to be_assignable_to(data)\n        end\n      end\n\n      data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|\n        it \"it is assignable from Optional[#{tc.name}]\" do\n          expect(optional_t(tc)).to be_assignable_to(data)\n        end\n      end\n\n      it 'it is not assignable to any of its subtypes' do\n        types_to_test = data_compatible_types\n        types_to_test.each {|t2| expect(data).not_to be_assignable_to(type_from_class(t2)) }\n      end\n\n      it 'it is not assignable to any disjunct type' do\n        tested_types = all_types - [PAnyType, POptionalType, PInitType] - scalar_data_types\n        tested_types.each {|t2| expect(data).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context 'for Rich Data, such that' do\n      let(:rich_data) { TypeFactory.rich_data }\n      rich_data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|\n        it \"it is assignable from #{tc.name}\" do\n          expect(tc).to be_assignable_to(rich_data)\n        end\n      end\n\n      rich_data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|\n        it \"it is assignable from Optional[#{tc.name}]\" do\n          expect(optional_t(tc)).to be_assignable_to(rich_data)\n        end\n      end\n\n      it 'it is not assignable to any of its subtypes' do\n        types_to_test = rich_data_compatible_types\n        types_to_test.each {|t2| expect(rich_data).not_to be_assignable_to(type_from_class(t2)) }\n      end\n\n      it 'it is not assignable to any disjunct type' do\n        tested_types = all_types - [PAnyType, POptionalType, PInitType] - scalar_types\n        tested_types.each {|t2| expect(rich_data).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context 'for Variant, such that' do\n      it 'it is assignable to a type if all contained types are assignable to that type' do\n        v = variant_t(range_t(10, 12),range_t(14, 20))\n        expect(v).to be_assignable_to(integer_t)\n        expect(v).to be_assignable_to(range_t(10, 20))\n\n        # test that both types are assignable to one of the variants OK\n        expect(v).to be_assignable_to(variant_t(range_t(10, 20), range_t(30, 40)))\n\n        # test where each type is assignable to different types in a variant is OK\n        expect(v).to be_assignable_to(variant_t(range_t(10, 13), range_t(14, 40)))\n\n        # not acceptable\n        expect(v).not_to be_assignable_to(range_t(0, 4))\n        expect(v).not_to be_assignable_to(string_t)\n      end\n\n      it 'an empty Variant is assignable to another empty Variant' do\n        expect(empty_variant_t).to be_assignable_to(empty_variant_t)\n      end\n\n      it 'an empty Variant is assignable to Any' do\n        expect(empty_variant_t).to be_assignable_to(PAnyType::DEFAULT)\n      end\n\n      it 'an empty Variant is assignable to Unit' do\n        expect(empty_variant_t).to be_assignable_to(PUnitType::DEFAULT)\n      end\n\n      it 'an empty Variant is not assignable to any type except empty Variant, Any, NotUndef, and Unit' do\n        assignables = [PUnitType, PAnyType, PNotUndefType]\n        unassignables = all_types - assignables\n        unassignables.each {|t2| expect(empty_variant_t).not_to be_assignable_to(t2::DEFAULT) }\n        assignables.each   {|t2| expect(empty_variant_t).to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'an empty Variant is not assignable to Optional[Any] since it is not assignable to Undef' do\n        opt_any = optional_t(any_t)\n        expect(empty_variant_t).not_to be_assignable_to(opt_any)\n      end\n\n      it 'an Optional[Any] is not assignable to empty Variant' do\n        opt_any = optional_t(any_t)\n        expect(opt_any).not_to be_assignable_to(empty_variant_t)\n      end\n\n      it 'an empty Variant is assignable to NotUndef[Variant] since Variant is not Undef' do\n        not_undef_variant = not_undef_t(empty_variant_t)\n        expect(empty_variant_t).to be_assignable_to(not_undef_variant)\n      end\n\n      it 'a NotUndef[Variant] is assignable to empty Variant' do\n        not_undef_variant = not_undef_t(empty_variant_t)\n        expect(not_undef_variant).to be_assignable_to(empty_variant_t)\n      end\n    end\n\n    context 'for Scalar, such that' do\n      it 'all scalars are assignable to Scalar' do\n        t = PScalarType::DEFAULT\n        scalar_types.each {|t2| expect(t2::DEFAULT).to be_assignable_to(t) }\n      end\n\n      it 'Scalar is not assignable to any of its subtypes' do\n        t = PScalarType::DEFAULT\n        types_to_test = scalar_types - [PScalarType]\n        types_to_test.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Scalar is not assignable to any disjunct type' do\n        tested_types = all_types - [PAnyType, POptionalType, PInitType, PNotUndefType] - scalar_types\n        t = PScalarType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context 'for Numeric, such that' do\n      it 'all numerics are assignable to Numeric' do\n        t = PNumericType::DEFAULT\n        numeric_types.each {|t2| expect(t2::DEFAULT).to be_assignable_to(t) }\n      end\n\n      it 'Numeric is not assignable to any of its subtypes' do\n        t = PNumericType::DEFAULT\n        types_to_test = numeric_types - [PNumericType]\n        types_to_test.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Numeric does not consider ruby Rational to be an instance' do\n        t = PNumericType::DEFAULT\n        expect(t).not_to be_instance(Rational(2,3))\n      end\n\n      it 'Numeric is not assignable to any disjunct type' do\n        tested_types = all_types - [\n          PAnyType,\n          POptionalType,\n          PInitType,\n          PNotUndefType,\n          PScalarType,\n          PScalarDataType,\n          ] - numeric_types\n        t = PNumericType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context 'for Collection, such that' do\n      it 'all collections are assignable to Collection' do\n        t = PCollectionType::DEFAULT\n        collection_types.each {|t2| expect(t2::DEFAULT).to be_assignable_to(t) }\n      end\n\n      it 'Collection is not assignable to any of its subtypes' do\n        t = PCollectionType::DEFAULT\n        types_to_test = collection_types - [PCollectionType]\n        types_to_test.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Collection is not assignable to any disjunct type' do\n        tested_types = all_types - [\n          PAnyType,\n          POptionalType,\n          PNotUndefType,\n          PIterableType] - collection_types\n        t = PCollectionType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context 'for Array, such that' do\n      it 'Array is not assignable to non Array based Collection type' do\n        t = PArrayType::DEFAULT\n        tested_types = collection_types - [\n          PCollectionType,\n          PNotUndefType,\n          PArrayType,\n          PTupleType]\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Array is not assignable to any disjunct type' do\n        tested_types = all_types - [\n          PAnyType,\n          POptionalType,\n          PNotUndefType,\n          PIterableType] - collection_types\n        t = PArrayType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Empty Array is assignable to an array that accepts 0 entries' do\n        expect(empty_array_t).to be_assignable_to(array_t(string_t))\n        expect(empty_array_t).to be_assignable_to(array_t(integer_t))\n      end\n\n      it 'A Tuple is assignable to an array' do\n        expect(tuple_t(String)).to be_assignable_to(array_t(String))\n      end\n\n      it 'A Tuple with <n> elements is assignable to an array with min size <n>' do\n        expect(tuple_t(String,String)).to be_assignable_to(array_t(String, range_t(2, :default)))\n      end\n\n      it 'A Tuple with <n> elements where the last 2 are optional is assignable to an array with size <n> - 2' do\n        expect(constrained_tuple_t(range_t(2, :default), String,String,String,String)).to be_assignable_to(array_t(String, range_t(2, :default)))\n      end\n    end\n\n    context 'for Enum, such that' do\n      it 'Enum is assignable to an Enum with all contained options' do\n        expect(enum_t('a', 'b')).to be_assignable_to(enum_t('a', 'b', 'c'))\n      end\n\n      it 'Enum is not assignable to an Enum with fewer contained options' do\n        expect(enum_t('a', 'b')).not_to be_assignable_to(enum_t('a'))\n      end\n\n      it 'case insensitive Enum is not assignable to case sensitive Enum' do\n        expect(enum_t('a', 'b', true)).not_to be_assignable_to(enum_t('a', 'b'))\n      end\n\n      it 'case sensitive Enum is assignable to case insensitive Enum' do\n        expect(enum_t('a', 'b')).to be_assignable_to(enum_t('a', 'b', true))\n      end\n\n      it 'case sensitive Enum is not assignable to case sensitive Enum using different case' do\n        expect(enum_t('a', 'b')).not_to be_assignable_to(enum_t('A', 'B'))\n      end\n\n      it 'case sensitive Enum is assignable to case insensitive Enum using different case' do\n        expect(enum_t('a', 'b')).to be_assignable_to(enum_t('A', 'B', true))\n      end\n    end\n\n    context 'for Hash, such that' do\n      it 'Hash is not assignable to any other Collection type' do\n        t = PHashType::DEFAULT\n        tested_types = collection_types - [\n          PCollectionType,\n          PStructType,\n          PHashType]\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Hash is not assignable to any disjunct type' do\n        tested_types = all_types - [\n          PAnyType,\n          POptionalType,\n          PNotUndefType,\n          PIterableType] - collection_types\n        t = PHashType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Struct is assignable to Hash with Pattern that matches all keys' do\n        expect(struct_t({'x' => integer_t, 'y' => integer_t})).to be_assignable_to(hash_t(pattern_t(/^\\w+$/), factory.any))\n      end\n\n      it 'Struct is assignable to Hash with Enum that matches all keys' do\n        expect(struct_t({'x' => integer_t, 'y' => integer_t})).to be_assignable_to(hash_t(enum_t('x', 'y', 'z'), factory.any))\n      end\n\n      it 'Struct is not assignable to Hash with Pattern unless all keys match' do\n        expect(struct_t({'a' => integer_t, 'A' => integer_t})).not_to be_assignable_to(hash_t(pattern_t(/^[A-Z]+$/), factory.any))\n      end\n\n      it 'Struct is not assignable to Hash with Enum unless all keys match' do\n        expect(struct_t({'a' => integer_t, 'y' => integer_t})).not_to be_assignable_to(hash_t(enum_t('x', 'y', 'z'), factory.any))\n      end\n    end\n\n    context 'for Timespan such that' do\n      it 'Timespan is assignable to less constrained Timespan' do\n        t1 = PTimespanType.new('00:00:10', '00:00:20')\n        t2 = PTimespanType.new('00:00:11', '00:00:19')\n        expect(t2).to be_assignable_to(t1)\n      end\n\n      it 'Timespan is not assignable to more constrained Timespan' do\n        t1 = PTimespanType.new('00:00:10', '00:00:20')\n        t2 = PTimespanType.new('00:00:11', '00:00:19')\n        expect(t1).not_to be_assignable_to(t2)\n      end\n    end\n\n    context 'for Timestamp such that' do\n      it 'Timestamp is assignable to less constrained Timestamp' do\n        t1 = PTimestampType.new('2016-01-01', '2016-12-31')\n        t2 = PTimestampType.new('2016-02-01', '2016-11-30')\n        expect(t2).to be_assignable_to(t1)\n      end\n\n      it 'Timestamp is not assignable to more constrained Timestamp' do\n        t1 = PTimestampType.new('2016-01-01', '2016-12-31')\n        t2 = PTimestampType.new('2016-02-01', '2016-11-30')\n        expect(t1).not_to be_assignable_to(t2)\n      end\n    end\n\n    context 'for Tuple, such that' do\n      it 'Tuple is not assignable to any other non Array based Collection type' do\n        t = PTupleType::DEFAULT\n        tested_types = collection_types - [\n          PCollectionType,\n          PTupleType,\n          PArrayType]\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'A tuple with parameters is assignable to the default Tuple' do\n        t = PTupleType::DEFAULT\n        t2 = PTupleType.new([PStringType::DEFAULT])\n        expect(t2).to be_assignable_to(t)\n      end\n\n      it 'Tuple is not assignable to any disjunct type' do\n        tested_types = all_types - [\n          PAnyType,\n          POptionalType,\n          PInitType,\n          PNotUndefType,\n          PIterableType] - collection_types\n        t = PTupleType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n    end\n\n    context 'for Struct, such that' do\n      it 'Struct is not assignable to any other non Hashed based Collection type' do\n        t = PStructType::DEFAULT\n        tested_types = collection_types - [\n          PCollectionType,\n          PStructType,\n          PHashType,\n          PInitType]\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Struct is not assignable to any disjunct type' do\n        tested_types = all_types - [\n          PAnyType,\n          POptionalType,\n          PNotUndefType,\n          PIterableType,\n          PInitType] - collection_types\n        t = PStructType::DEFAULT\n        tested_types.each {|t2| expect(t).not_to be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'Default key optionality is controlled by value assignability to undef' do\n        t1 = struct_t({'member' => string_t})\n        expect(t1.elements[0].key_type).to eq(string_t('member'))\n        t1 = struct_t({'member' => any_t})\n        expect(t1.elements[0].key_type).to eq(optional_t(string_t('member')))\n      end\n\n      it \"NotUndef['key'] becomes String['key'] (since its implied that String is required)\" do\n        t1 = struct_t({not_undef_t('member') => string_t})\n        expect(t1.elements[0].key_type).to eq(string_t('member'))\n      end\n\n      it \"Optional['key'] becomes Optional[String['key']]\" do\n        t1 = struct_t({optional_t('member') => string_t})\n        expect(t1.elements[0].key_type).to eq(optional_t(string_t('member')))\n      end\n\n      it 'Optional members are not required' do\n        t1 = struct_t({optional_t('optional_member') => string_t, not_undef_t('other_member') => string_t})\n        t2 = struct_t({not_undef_t('other_member') => string_t})\n        expect(t2).to be_assignable_to(t1)\n      end\n\n      it 'Required members not optional even when value is' do\n        t1 = struct_t({not_undef_t('required_member') => any_t, not_undef_t('other_member') => string_t})\n        t2 = struct_t({not_undef_t('other_member') => string_t})\n        expect(t2).not_to be_assignable_to(t1)\n      end\n\n      it 'A hash of string is not assignable to struct with integer value' do\n        t1 = struct_t({'foo' => integer_t, 'bar' => string_t})\n        t2 = hash_t(string_t, string_t, range_t(2, 2))\n        expect(t1.assignable?(t2)).to eql(false)\n      end\n\n      it 'A hash of with integer key is not assignable to struct with string key' do\n        t1 = struct_t({'foo' => string_t, 'bar' => string_t})\n        t2 = hash_t(integer_t, string_t, range_t(2, 2))\n        expect(t1.assignable?(t2)).to eql(false)\n      end\n    end\n\n    context 'for Callable, such that' do\n      it 'Callable is not assignable to any disjunct type' do\n        t = PCallableType::DEFAULT\n        tested_types = all_types - [\n          PCallableType,\n          PAnyType,\n          POptionalType,\n          PNotUndefType]\n        tested_types.each {|t2| expect(t).to_not be_assignable_to(t2::DEFAULT) }\n      end\n\n      it 'a callable with parameter is assignable to the default callable' do\n        expect(callable_t(string_t)).to be_assignable_to(PCallableType::DEFAULT)\n      end\n\n      it 'the default callable is not assignable to a callable with parameter' do\n        expect(PCallableType::DEFAULT).not_to be_assignable_to(callable_t(string_t))\n      end\n\n      it 'a callable with a return type is assignable to the default callable' do\n        expect(callable_t([], string_t)).to be_assignable_to(PCallableType::DEFAULT)\n      end\n\n      it 'the default callable is not assignable to a callable with a return type' do\n        expect(PCallableType::DEFAULT).not_to be_assignable_to(callable_t([],string_t))\n      end\n\n      it 'a callable with a return type Any is assignable to the default callable' do\n        expect(callable_t([], any_t)).to be_assignable_to(PCallableType::DEFAULT)\n      end\n\n      it 'a callable with a return type Any is equal to a callable with the same parameters and no return type' do\n        expect(callable_t([string_t], any_t)).to eql(callable_t(string_t))\n      end\n\n      it 'a callable with a return type different than Any is not equal to a callable with the same parameters and no return type' do\n        expect(callable_t([string_t], string_t)).not_to eql(callable_t(string_t))\n      end\n\n      it 'a callable with a return type is assignable from another callable with an assignable return type' do\n        expect(callable_t([], string_t)).to be_assignable_to(callable_t([], PScalarType::DEFAULT))\n      end\n\n      it 'a callable with a return type is not assignable from another callable unless the return type is assignable' do\n        expect(callable_t([], string_t)).not_to be_assignable_to(callable_t([], integer_t))\n      end\n    end\n\n    it 'should recognize mapped ruby types' do\n      { Integer    => PIntegerType::DEFAULT,\n        Float      => PFloatType::DEFAULT,\n        Numeric    => PNumericType::DEFAULT,\n        NilClass   => PUndefType::DEFAULT,\n        TrueClass  => PBooleanType::DEFAULT,\n        FalseClass => PBooleanType::DEFAULT,\n        String     => PStringType::DEFAULT,\n        Regexp     => PRegexpType::DEFAULT,\n        Regexp     => PRegexpType::DEFAULT,\n        Array      => TypeFactory.array_of_any,\n        Hash       => TypeFactory.hash_of_any\n      }.each do |ruby_type, puppet_type |\n          expect(ruby_type).to be_assignable_to(puppet_type)\n      end\n    end\n\n    context 'when dealing with integer ranges' do\n      it 'should accept an equal range' do\n        expect(calculator.assignable?(range_t(2,5), range_t(2,5))).to eq(true)\n      end\n\n      it 'should accept a narrower range' do\n        expect(calculator.assignable?(range_t(2,10), range_t(3,5))).to eq(true)\n      end\n\n      it 'should reject a wider range' do\n        expect(calculator.assignable?(range_t(3,5), range_t(2,10))).to eq(false)\n      end\n\n      it 'should reject a partially overlapping range' do\n        expect(calculator.assignable?(range_t(3,5), range_t(2,4))).to eq(false)\n        expect(calculator.assignable?(range_t(3,5), range_t(4,6))).to eq(false)\n      end\n    end\n\n    context 'when dealing with patterns' do\n      it 'should accept a string matching a pattern' do\n        p_t = pattern_t('abc')\n        p_s = string_t('XabcY')\n        expect(calculator.assignable?(p_t, p_s)).to eq(true)\n      end\n\n      it 'should accept a regexp matching a pattern' do\n        p_t = pattern_t(/abc/)\n        p_s = string_t('XabcY')\n        expect(calculator.assignable?(p_t, p_s)).to eq(true)\n      end\n\n      it 'should accept a pattern matching a pattern' do\n        p_t = pattern_t(pattern_t('abc'))\n        p_s = string_t('XabcY')\n        expect(calculator.assignable?(p_t, p_s)).to eq(true)\n      end\n\n      it 'should accept a regexp matching a pattern' do\n        p_t = pattern_t(regexp_t('abc'))\n        p_s = string_t('XabcY')\n        expect(calculator.assignable?(p_t, p_s)).to eq(true)\n      end\n\n      it 'should accept a string matching all patterns' do\n        p_t = pattern_t('abc', 'ab', 'c')\n        p_s = string_t('XabcY')\n        expect(calculator.assignable?(p_t, p_s)).to eq(true)\n      end\n\n      it 'should accept multiple strings if they all match any patterns' do\n        p_t = pattern_t('X', 'Y', 'abc')\n        p_s = enum_t('Xa', 'aY', 'abc')\n        expect(calculator.assignable?(p_t, p_s)).to eq(true)\n      end\n\n      it 'should reject a string not matching any patterns' do\n        p_t = pattern_t('abc', 'ab', 'c')\n        p_s = string_t('XqqqY')\n        expect(calculator.assignable?(p_t, p_s)).to eq(false)\n      end\n\n      it 'should reject multiple strings if not all match any patterns' do\n        p_t = pattern_t('abc', 'ab', 'c', 'q')\n        p_s = enum_t('X', 'Y', 'Z')\n        expect(calculator.assignable?(p_t, p_s)).to eq(false)\n      end\n\n      it 'should accept enum matching patterns as instanceof' do\n        enum = enum_t('XS', 'S', 'M', 'L' 'XL', 'XXL')\n        pattern = pattern_t('S', 'M', 'L')\n        expect(calculator.assignable?(pattern, enum)).to  eq(true)\n      end\n\n      it 'pattern should accept a variant where all variants are acceptable' do\n        pattern = pattern_t(/^\\w+$/)\n        expect(calculator.assignable?(pattern, variant_t(string_t('a'), string_t('b')))).to eq(true)\n      end\n\n      it 'pattern representing all patterns should accept any pattern' do\n        expect(calculator.assignable?(pattern_t, pattern_t('a'))).to eq(true)\n        expect(calculator.assignable?(pattern_t, pattern_t)).to eq(true)\n      end\n\n      it 'pattern representing all patterns should accept any enum' do\n        expect(calculator.assignable?(pattern_t, enum_t('a'))).to eq(true)\n        expect(calculator.assignable?(pattern_t, enum_t)).to eq(true)\n      end\n\n      it 'pattern representing all patterns should accept any string' do\n        expect(calculator.assignable?(pattern_t, string_t('a'))).to eq(true)\n        expect(calculator.assignable?(pattern_t, string_t)).to eq(true)\n      end\n\n    end\n\n    context 'when dealing with enums' do\n      it 'should accept a string with matching content' do\n        expect(calculator.assignable?(enum_t('a', 'b'), string_t('a'))).to eq(true)\n        expect(calculator.assignable?(enum_t('a', 'b'), string_t('b'))).to eq(true)\n        expect(calculator.assignable?(enum_t('a', 'b'), string_t('c'))).to eq(false)\n      end\n\n      it 'should accept an enum with matching enum' do\n        expect(calculator.assignable?(enum_t('a', 'b'), enum_t('a', 'b'))).to eq(true)\n        expect(calculator.assignable?(enum_t('a', 'b'), enum_t('a'))).to eq(true)\n        expect(calculator.assignable?(enum_t('a', 'b'), enum_t('c'))).to eq(false)\n      end\n\n      it 'non parameterized enum accepts any other enum but not the reverse' do\n        expect(calculator.assignable?(enum_t, enum_t('a'))).to eq(true)\n        expect(calculator.assignable?(enum_t('a'), enum_t)).to eq(false)\n      end\n\n      it 'enum should accept a variant where all variants are acceptable' do\n        enum = enum_t('a', 'b')\n        expect(calculator.assignable?(enum, variant_t(string_t('a'), string_t('b')))).to eq(true)\n      end\n    end\n\n    context 'when dealing with string and enum combinations' do\n      it 'should accept assigning any enum to unrestricted string' do\n        expect(calculator.assignable?(string_t, enum_t('blue'))).to eq(true)\n        expect(calculator.assignable?(string_t, enum_t('blue', 'red'))).to eq(true)\n      end\n\n      it 'should not accept assigning longer enum value to size restricted string' do\n        expect(calculator.assignable?(constrained_string_t(range_t(2,2)), enum_t('a','blue'))).to eq(false)\n      end\n\n      it 'should accept assigning any string to empty enum' do\n        expect(calculator.assignable?(enum_t, string_t)).to eq(true)\n      end\n\n      it 'should accept assigning empty enum to any string' do\n        expect(calculator.assignable?(string_t, enum_t)).to eq(true)\n      end\n\n      it 'should not accept assigning empty enum to size constrained string' do\n        expect(calculator.assignable?(constrained_string_t(range_t(2,2)), enum_t)).to eq(false)\n      end\n    end\n\n    context 'when dealing with string/pattern/enum combinations' do\n      it 'any string is equal to any enum is equal to any pattern' do\n        expect(calculator.assignable?(string_t, enum_t)).to eq(true)\n        expect(calculator.assignable?(string_t, pattern_t)).to eq(true)\n        expect(calculator.assignable?(enum_t, string_t)).to eq(true)\n        expect(calculator.assignable?(enum_t, pattern_t)).to eq(true)\n        expect(calculator.assignable?(pattern_t, string_t)).to eq(true)\n        expect(calculator.assignable?(pattern_t, enum_t)).to eq(true)\n      end\n    end\n\n    context 'when dealing with tuples' do\n      it 'matches empty tuples' do\n        tuple1 = tuple_t\n        tuple2 = tuple_t\n\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(true)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(true)\n      end\n\n      it 'accepts an empty tuple as assignable to a tuple with a min size of 0' do\n        tuple1 = constrained_tuple_t(range_t(0, :default))\n        tuple2 = tuple_t()\n\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(true)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(true)\n      end\n\n      it 'should accept matching tuples' do\n        tuple1 = tuple_t(1,2)\n        tuple2 = tuple_t(Integer,Integer)\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(true)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(true)\n      end\n\n      it 'should accept matching tuples where one is more general than the other' do\n        tuple1 = tuple_t(1,2)\n        tuple2 = tuple_t(Numeric,Numeric)\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(false)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(true)\n      end\n\n      it 'should accept ranged tuples' do\n        tuple1 = constrained_tuple_t(range_t(5,5), 1)\n        tuple2 = tuple_t(Integer,Integer, Integer, Integer, Integer)\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(true)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(true)\n      end\n\n      it 'should reject ranged tuples when ranges does not match' do\n        tuple1 = constrained_tuple_t(range_t(4, 5), 1)\n        tuple2 = tuple_t(Integer,Integer, Integer, Integer, Integer)\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(true)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(false)\n      end\n\n      it 'should reject ranged tuples when ranges does not match (using infinite upper bound)' do\n        tuple1 = constrained_tuple_t(range_t(4, :default), 1)\n        tuple2 = tuple_t(Integer,Integer, Integer, Integer, Integer)\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(true)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(false)\n      end\n\n      it 'should accept matching tuples with optional entries by repeating last' do\n        tuple1 = constrained_tuple_t(range_t(0, :default), 1,2)\n        tuple2 = constrained_tuple_t(range_t(0, :default), Numeric,Numeric)\n        expect(calculator.assignable?(tuple1, tuple2)).to eq(false)\n        expect(calculator.assignable?(tuple2, tuple1)).to eq(true)\n      end\n\n      it 'should accept matching tuples with optional entries' do\n        tuple1 = constrained_tuple_t(range_t(1, 3), Integer, Integer, String)\n        array2 = array_t(Integer, range_t(2,2))\n        expect(calculator.assignable?(tuple1, array2)).to eq(true)\n        tuple1 = constrained_tuple_t(range_t(3, 3), tuple1.types)\n        expect(calculator.assignable?(tuple1, array2)).to eq(false)\n      end\n\n      it 'should accept matching array' do\n        tuple1 = tuple_t(1,2)\n        array = array_t(Integer, range_t(2, 2))\n        expect(calculator.assignable?(tuple1, array)).to eq(true)\n        expect(calculator.assignable?(array, tuple1)).to eq(true)\n      end\n\n      it 'should accept empty array when tuple allows min of 0' do\n        tuple1 = constrained_tuple_t(range_t(0, 1), Integer)\n        array = array_t(unit_t, range_t(0, 0))\n        expect(calculator.assignable?(tuple1, array)).to eq(true)\n        expect(calculator.assignable?(array, tuple1)).to eq(false)\n      end\n    end\n\n    context 'when dealing with structs' do\n      it 'should accept matching structs' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer})\n        struct2 = struct_t({'a'=>Integer, 'b'=>Integer})\n        expect(calculator.assignable?(struct1, struct2)).to eq(true)\n        expect(calculator.assignable?(struct2, struct1)).to eq(true)\n      end\n\n      it 'should accept matching structs with less elements when unmatched elements are optional' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})\n        struct2 = struct_t({'a'=>Integer, 'b'=>Integer})\n        expect(calculator.assignable?(struct1, struct2)).to eq(true)\n      end\n\n      it 'should reject matching structs with more elements even if excess elements are optional' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer})\n        struct2 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})\n        expect(calculator.assignable?(struct1, struct2)).to eq(false)\n      end\n\n      it 'should accept matching structs where one is more general than the other with respect to optional' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})\n        struct2 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>Integer})\n        expect(calculator.assignable?(struct1, struct2)).to eq(true)\n      end\n\n      it 'should reject matching structs where one is more special than the other with respect to optional' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>Integer})\n        struct2 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})\n        expect(calculator.assignable?(struct1, struct2)).to eq(false)\n      end\n\n      it 'should accept matching structs where one is more general than the other' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer})\n        struct2 = struct_t({'a'=>Numeric, 'b'=>Numeric})\n        expect(calculator.assignable?(struct1, struct2)).to eq(false)\n        expect(calculator.assignable?(struct2, struct1)).to eq(true)\n      end\n\n      it 'should accept matching hash' do\n        struct1 = struct_t({'a'=>Integer, 'b'=>Integer})\n        non_empty_string = constrained_string_t(range_t(1, nil))\n        hsh = hash_t(non_empty_string, Integer, range_t(2,2))\n        expect(calculator.assignable?(struct1, hsh)).to eq(true)\n        expect(calculator.assignable?(hsh, struct1)).to eq(true)\n      end\n\n      it 'should accept empty hash with key_type unit' do\n        struct1 = struct_t({'a'=>optional_t(Integer)})\n        hsh = hash_t(unit_t, unit_t, range_t(0, 0))\n        expect(calculator.assignable?(struct1, hsh)).to eq(true)\n      end\n    end\n\n    it 'should recognize ruby type inheritance' do\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      fooType = calculator.infer(Foo.new)\n      barType = calculator.infer(Bar.new)\n\n      expect(calculator.assignable?(fooType, fooType)).to eq(true)\n      expect(calculator.assignable?(Foo, fooType)).to eq(true)\n\n      expect(calculator.assignable?(fooType, barType)).to eq(true)\n      expect(calculator.assignable?(Foo, barType)).to eq(true)\n\n      expect(calculator.assignable?(barType, fooType)).to eq(false)\n      expect(calculator.assignable?(Bar, fooType)).to eq(false)\n    end\n\n    it 'should allow host class with same name' do\n      hc1 = TypeFactory.host_class('the_name')\n      hc2 = TypeFactory.host_class('the_name')\n      expect(calculator.assignable?(hc1, hc2)).to eq(true)\n    end\n\n    it 'should allow host class with name assigned to hostclass without name' do\n      hc1 = TypeFactory.host_class\n      hc2 = TypeFactory.host_class('the_name')\n      expect(calculator.assignable?(hc1, hc2)).to eq(true)\n    end\n\n    it 'should reject host classes with different names' do\n      hc1 = TypeFactory.host_class('the_name')\n      hc2 = TypeFactory.host_class('another_name')\n      expect(calculator.assignable?(hc1, hc2)).to eq(false)\n    end\n\n    it 'should reject host classes without name assigned to host class with name' do\n      hc1 = TypeFactory.host_class('the_name')\n      hc2 = TypeFactory.host_class\n      expect(calculator.assignable?(hc1, hc2)).to eq(false)\n    end\n\n    it 'should allow resource with same type_name and title' do\n      r1 = TypeFactory.resource('file', 'foo')\n      r2 = TypeFactory.resource('file', 'foo')\n      expect(calculator.assignable?(r1, r2)).to eq(true)\n    end\n\n    it 'should allow more specific resource assignment' do\n      r1 = TypeFactory.resource\n      r2 = TypeFactory.resource('file')\n      expect(calculator.assignable?(r1, r2)).to eq(true)\n      r2 = TypeFactory.resource('file', '/tmp/foo')\n      expect(calculator.assignable?(r1, r2)).to eq(true)\n      r1 = TypeFactory.resource('file')\n      expect(calculator.assignable?(r1, r2)).to eq(true)\n    end\n\n    it 'should reject less specific resource assignment' do\n      r1 = TypeFactory.resource('file', '/tmp/foo')\n      r2 = TypeFactory.resource('file')\n      expect(calculator.assignable?(r1, r2)).to eq(false)\n      r2 = TypeFactory.resource\n      expect(calculator.assignable?(r1, r2)).to eq(false)\n    end\n\n    context 'for TypeAlias, such that' do\n      let!(:parser) { TypeParser.singleton }\n\n      it 'it is assignable to the type that it is an alias for' do\n        t = type_alias_t('Alias', 'Integer').resolve(nil)\n        expect(calculator.assignable?(integer_t, t)).to be_truthy\n      end\n\n      it 'the type that it is an alias for is assignable to it' do\n        t = type_alias_t('Alias', 'Integer').resolve(nil)\n        expect(calculator.assignable?(t, integer_t)).to be_truthy\n      end\n\n      it 'a recursive alias can be assignable from a conformant type with any depth' do\n        scope = {}\n\n        t = type_alias_t('Tree', 'Hash[String,Variant[String,Tree]]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'tree').and_return(t)\n\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t.resolve(scope)\n        expect(calculator.assignable?(t, parser.parse('Hash[String,Variant[String,Hash[String,Variant[String,String]]]]'))).to be_truthy\n      end\n\n\n      it 'similar recursive aliases are assignable' do\n        scope = {}\n\n        t1 = type_alias_t('Tree1', 'Hash[String,Variant[String,Tree1]]')\n        t2 = type_alias_t('Tree2', 'Hash[String,Variant[String,Tree2]]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'tree1').and_return(t1)\n        expect(loader).to receive(:load).with(:type, 'tree2').and_return(t2)\n\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1.resolve(scope)\n        t2.resolve(scope)\n        expect(calculator.assignable?(t1, t2)).to be_truthy\n      end\n\n      it 'crossing recursive aliases are assignable' do\n        t1 = type_alias_t('Tree1', 'Hash[String,Variant[String,Tree2]]')\n        t2 = type_alias_t('Tree2', 'Hash[String,Variant[String,Tree1]]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'tree1').and_return(t1)\n        expect(loader).to receive(:load).with(:type, 'tree2').and_return(t2)\n\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1.resolve(loader)\n        t2.resolve(loader)\n        expect(calculator.assignable?(t1, t2)).to be_truthy\n      end\n\n      it 'Type[T] is assignable to Type[AT] when AT is an alias for T' do\n        scope = {}\n\n        ta = type_alias_t('PositiveInteger', 'Integer[0,default]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'positiveinteger').and_return(ta)\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1 = type_t(range_t(0, :default))\n        t2 = parser.parse('Type[PositiveInteger]', scope)\n        expect(calculator.assignable?(t2, t1)).to be_truthy\n      end\n\n      it 'Type[T] is assignable to AT when AT is an alias for Type[T]' do\n        scope = {}\n\n        ta = type_alias_t('PositiveIntegerType', 'Type[Integer[0,default]]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'positiveintegertype').and_return(ta)\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1 = type_t(range_t(0, :default))\n        t2 = parser.parse('PositiveIntegerType', scope)\n        expect(calculator.assignable?(t2, t1)).to be_truthy\n      end\n\n      it 'Type[Type[T]] is assignable to Type[Type[AT]] when AT is an alias for T' do\n        scope = {}\n\n        ta = type_alias_t('PositiveInteger', 'Integer[0,default]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'positiveinteger').and_return(ta)\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1 = type_t(type_t(range_t(0, :default)))\n        t2 = parser.parse('Type[Type[PositiveInteger]]', scope)\n        expect(calculator.assignable?(t2, t1)).to be_truthy\n      end\n\n      it 'Type[Type[T]] is assignable to Type[AT] when AT is an alias for Type[T]' do\n        scope = {}\n\n        ta = type_alias_t('PositiveIntegerType', 'Type[Integer[0,default]]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'positiveintegertype').and_return(ta)\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1 = type_t(type_t(range_t(0, :default)))\n        t2 = parser.parse('Type[PositiveIntegerType]', scope)\n        expect(calculator.assignable?(t2, t1)).to be_truthy\n      end\n\n      it 'An alias for a Type that describes an Iterable instance is assignable to Iterable' do\n        t = type_alias_t('MyType', 'Enum[a,b]').resolve(nil)\n\n        # True because String is iterable and an instance of Enum is a String\n        expect(calculator.assignable?(iterable_t, t)).to be_truthy\n      end\n    end\n  end\n\n  context 'when testing if x is instance of type t' do\n    include_context 'types_setup'\n\n    it 'should consider undef to be instance of Any, NilType, and optional' do\n      expect(calculator.instance?(PUndefType::DEFAULT, nil)).to    eq(true)\n      expect(calculator.instance?(PAnyType::DEFAULT, nil)).to eq(true)\n      expect(calculator.instance?(POptionalType::DEFAULT, nil)).to eq(true)\n    end\n\n    it 'all types should be (ruby) instance of PAnyType' do\n      all_types.each do |t|\n        expect(t::DEFAULT.is_a?(PAnyType)).to eq(true)\n      end\n    end\n\n    it \"should infer :undef to be Undef\" do\n      expect(calculator.infer(:undef)).to be_assignable_to(undef_t)\n    end\n\n    it \"should not consider :default to be instance of Runtime['ruby', 'Symbol]\" do\n      expect(calculator.instance?(PRuntimeType.new(:ruby, 'Symbol'), :default)).to eq(false)\n    end\n\n    it \"should not consider :undef to be instance of Runtime['ruby', 'Symbol]\" do\n      expect(calculator.instance?(PRuntimeType.new(:ruby, 'Symbol'), :undef)).to eq(false)\n    end\n\n    it 'should consider :undef to be instance of an Optional type' do\n      expect(calculator.instance?(POptionalType::DEFAULT, :undef)).to eq(true)\n    end\n\n    it 'should not consider undef to be an instance of any other type than Any, Undef, Optional, and Init' do\n      types_to_test = all_types - [\n        PAnyType,\n        PUndefType,\n        POptionalType,\n        PInitType\n        ]\n\n      types_to_test.each {|t| expect(calculator.instance?(t::DEFAULT, nil)).to eq(false) }\n      types_to_test.each {|t| expect(calculator.instance?(t::DEFAULT, :undef)).to eq(false) }\n    end\n\n    it 'should consider default to be instance of Default and Any' do\n      expect(calculator.instance?(PDefaultType::DEFAULT, :default)).to eq(true)\n      expect(calculator.instance?(PAnyType::DEFAULT, :default)).to eq(true)\n    end\n\n    it 'should not consider \"default\" to be an instance of anything but Default, Init, NotUndef, and Any' do\n      types_to_test = all_types - [\n        PAnyType,\n        PNotUndefType,\n        PDefaultType,\n        PInitType,\n        ]\n\n      types_to_test.each {|t| expect(calculator.instance?(t::DEFAULT, :default)).to eq(false) }\n    end\n\n    it 'should consider integer instanceof PIntegerType' do\n      expect(calculator.instance?(PIntegerType::DEFAULT, 1)).to eq(true)\n    end\n\n    it 'should consider integer instanceof Integer' do\n      expect(calculator.instance?(Integer, 1)).to eq(true)\n    end\n\n    it 'should consider integer in range' do\n      range = range_t(0,10)\n      expect(calculator.instance?(range, 1)).to eq(true)\n      expect(calculator.instance?(range, 10)).to eq(true)\n      expect(calculator.instance?(range, -1)).to eq(false)\n      expect(calculator.instance?(range, 11)).to eq(false)\n    end\n\n    it 'should consider string in length range' do\n      range = constrained_string_t(range_t(1,3))\n      expect(calculator.instance?(range, 'a')).to    eq(true)\n      expect(calculator.instance?(range, 'abc')).to  eq(true)\n      expect(calculator.instance?(range, '')).to     eq(false)\n      expect(calculator.instance?(range, 'abcd')).to eq(false)\n    end\n\n    it 'should consider string values' do\n      string = string_t('a')\n      expect(calculator.instance?(string, 'a')).to eq(true)\n      expect(calculator.instance?(string, 'c')).to eq(false)\n    end\n\n    it 'should consider array in length range' do\n      range = array_t(integer_t, range_t(1,3))\n      expect(calculator.instance?(range, [1])).to    eq(true)\n      expect(calculator.instance?(range, [1,2,3])).to  eq(true)\n      expect(calculator.instance?(range, [])).to     eq(false)\n      expect(calculator.instance?(range, [1,2,3,4])).to eq(false)\n    end\n\n    it 'should consider hash in length range' do\n      range = hash_t(integer_t, integer_t, range_t(1,2))\n      expect(calculator.instance?(range, {1=>1})).to             eq(true)\n      expect(calculator.instance?(range, {1=>1, 2=>2})).to       eq(true)\n      expect(calculator.instance?(range, {})).to                 eq(false)\n      expect(calculator.instance?(range, {1=>1, 2=>2, 3=>3})).to eq(false)\n    end\n\n    it 'should consider collection in length range for array ' do\n      range = collection_t(range_t(1,3))\n      expect(calculator.instance?(range, [1])).to    eq(true)\n      expect(calculator.instance?(range, [1,2,3])).to  eq(true)\n      expect(calculator.instance?(range, [])).to     eq(false)\n      expect(calculator.instance?(range, [1,2,3,4])).to eq(false)\n    end\n\n    it 'should consider collection in length range for hash' do\n      range = collection_t(range_t(1,2))\n      expect(calculator.instance?(range, {1=>1})).to             eq(true)\n      expect(calculator.instance?(range, {1=>1, 2=>2})).to       eq(true)\n      expect(calculator.instance?(range, {})).to                 eq(false)\n      expect(calculator.instance?(range, {1=>1, 2=>2, 3=>3})).to eq(false)\n    end\n\n    it 'should consider string matching enum as instanceof' do\n      enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0')\n      expect(calculator.instance?(enum, 'XS')).to  eq(true)\n      expect(calculator.instance?(enum, 'S')).to   eq(true)\n      expect(calculator.instance?(enum, 'XXL')).to eq(false)\n      expect(calculator.instance?(enum, '')).to    eq(false)\n      expect(calculator.instance?(enum, '0')).to   eq(true)\n      expect(calculator.instance?(enum, 0)).to     eq(false)\n    end\n\n    it 'should consider array[string] as instance of Array[Enum] when strings are instance of Enum' do\n      enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0')\n      array = array_t(enum)\n      expect(calculator.instance?(array, ['XS', 'S', 'XL'])).to  eq(true)\n      expect(calculator.instance?(array, ['XS', 'S', 'XXL'])).to eq(false)\n    end\n\n    it 'should consider array[mixed] as instance of Variant[mixed] when mixed types are listed in Variant' do\n      enum = enum_t('XS', 'S', 'M', 'L', 'XL')\n      sizes = range_t(30, 50)\n      array = array_t(variant_t(enum, sizes))\n      expect(calculator.instance?(array, ['XS', 'S', 30, 50])).to  eq(true)\n      expect(calculator.instance?(array, ['XS', 'S', 'XXL'])).to   eq(false)\n      expect(calculator.instance?(array, ['XS', 'S', 29])).to      eq(false)\n    end\n\n    it 'should consider array[seq] as instance of Tuple[seq] when elements of seq are instance of' do\n      tuple = tuple_t(Integer, String, Float)\n      expect(calculator.instance?(tuple, [1, 'a', 3.14])).to       eq(true)\n      expect(calculator.instance?(tuple, [1.2, 'a', 3.14])).to     eq(false)\n      expect(calculator.instance?(tuple, [1, 1, 3.14])).to         eq(false)\n      expect(calculator.instance?(tuple, [1, 'a', 1])).to          eq(false)\n    end\n\n    it 'should not consider ProcessOutput objects as instanceof PScalarDataType' do\n      object = Puppet::Util::Execution::ProcessOutput.new('object', 0)\n      \n      expect(calculator.instance?(PScalarDataType::DEFAULT, object)).to eq(false)\n    end\n\n    context 'and t is Struct' do\n      it 'should consider hash[cont] as instance of Struct[cont-t]' do\n        struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>Float})\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>3.14})).to       eq(true)\n        expect(calculator.instance?(struct, {'a'=>1.2, 'b'=>'a', 'c'=>3.14})).to     eq(false)\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>1, 'c'=>3.14})).to         eq(false)\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>1})).to          eq(false)\n      end\n\n      it 'should consider empty hash as instance of Struct[x=>Optional[String]]' do\n        struct = struct_t({'a'=>optional_t(String)})\n        expect(calculator.instance?(struct, {})).to eq(true)\n      end\n\n      it 'should consider hash[cont] as instance of Struct[cont-t,optionals]' do\n        struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>optional_t(Float)})\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>'a'})).to eq(true)\n      end\n\n      it 'should consider hash[cont] as instance of Struct[cont-t,variants with optionals]' do\n        struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>variant_t(String, optional_t(Float))})\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>'a'})).to eq(true)\n      end\n\n      it 'should not consider hash[cont,cont2] as instance of Struct[cont-t]' do\n        struct = struct_t({'a'=>Integer, 'b'=>String})\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>'x'})).to eq(false)\n      end\n\n      it 'should not consider hash[cont,cont2] as instance of Struct[cont-t,optional[cont3-t]' do\n        struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>optional_t(Float)})\n        expect(calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>'x'})).to eq(false)\n      end\n\n      it 'should consider nil to be a valid element value' do\n        struct = struct_t({not_undef_t('a') => any_t, 'b'=>String})\n        expect(calculator.instance?(struct, {'a'=>nil , 'b'=>'a'})).to eq(true)\n      end\n\n      it 'should consider nil to be a valid element value but subject to value type' do\n        struct = struct_t({not_undef_t('a') => String, 'b'=>String})\n        expect(calculator.instance?(struct, {'a'=>nil , 'b'=>'a'})).to eq(false)\n      end\n\n      it 'should consider nil to be a valid element value but subject to value type even when key is optional' do\n        struct = struct_t({optional_t('a') => String, 'b'=>String})\n        expect(calculator.instance?(struct, {'a'=>nil , 'b'=>'a'})).to eq(false)\n      end\n\n      it 'should consider a hash where optional key is missing as assignable even if value of optional key is required' do\n        struct = struct_t({optional_t('a') => String, 'b'=>String})\n        expect(calculator.instance?(struct, {'b'=>'a'})).to eq(true)\n      end\n    end\n\n    context 'and t is Data' do\n      it 'undef should be considered instance of Data' do\n        expect(calculator.instance?(data_t, nil)).to eq(true)\n      end\n\n      it 'other symbols should not be considered instance of Data' do\n        expect(calculator.instance?(data_t, :love)).to eq(false)\n      end\n\n      it 'an empty array should be considered instance of Data' do\n        expect(calculator.instance?(data_t, [])).to eq(true)\n      end\n\n      it 'an empty hash should be considered instance of Data' do\n        expect(calculator.instance?(data_t, {})).to eq(true)\n      end\n\n      it 'a hash with nil/undef data should be considered instance of Data' do\n        expect(calculator.instance?(data_t, {'a' => nil})).to eq(true)\n      end\n\n      it 'a hash with nil/default key should not considered instance of Data' do\n        expect(calculator.instance?(data_t, {nil => 10})).to eq(false)\n        expect(calculator.instance?(data_t, {:default => 10})).to eq(false)\n      end\n\n      it 'an array with nil entries should be considered instance of Data' do\n        expect(calculator.instance?(data_t, [nil])).to eq(true)\n      end\n\n      it 'an array with nil + data entries should be considered instance of Data' do\n        expect(calculator.instance?(data_t, [1, nil, 'a'])).to eq(true)\n      end\n    end\n\n    context 'and t is something Callable' do\n\n      it 'a Closure should be considered a Callable' do\n        factory = Model::Factory\n        params = [factory.PARAM('a')]\n        the_block = factory.LAMBDA(params,factory.literal(42), nil).model\n        the_closure = Evaluator::Closure::Dynamic.new(:fake_evaluator, the_block, :fake_scope)\n        expect(calculator.instance?(all_callables_t, the_closure)).to be_truthy\n        expect(calculator.instance?(callable_t(any_t), the_closure)).to be_truthy\n        expect(calculator.instance?(callable_t(any_t, any_t), the_closure)).to be_falsey\n      end\n\n      it 'a Function instance should be considered a Callable' do\n        fc = Puppet::Functions.create_function(:foo) do\n          dispatch :foo do\n            param 'String', :a\n          end\n\n          def foo(a)\n            a\n          end\n        end\n        f = fc.new(:closure_scope, :loader)\n        # Any callable\n        expect(calculator.instance?(all_callables_t, f)).to be_truthy\n        # Callable[String]\n        expect(calculator.instance?(callable_t(String), f)).to be_truthy\n      end\n    end\n\n    context 'and t is a TypeAlias' do\n      let!(:parser) { TypeParser.singleton }\n\n      it 'should consider x an instance of the aliased simple type' do\n        t = type_alias_t('Alias', 'Integer').resolve(nil)\n        expect(calculator.instance?(t, 15)).to be_truthy\n      end\n\n      it 'should consider x an instance of the aliased parameterized type' do\n        t = type_alias_t('Alias', 'Integer[0,20]').resolve(nil)\n        expect(calculator.instance?(t, 15)).to be_truthy\n      end\n\n      it 'should consider t an instance of Iterable when aliased type is Iterable' do\n        t = type_alias_t('Alias', 'Enum[a, b]').resolve(nil)\n        expect(calculator.instance?(iterable_t, t)).to be_truthy\n      end\n\n      it 'should consider x an instance of the aliased type that uses self recursion' do\n        t = type_alias_t('Tree', 'Hash[String,Variant[String,Tree]]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'tree').and_return(t)\n\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t.resolve(loader)\n        expect(calculator.instance?(t, {'a'=>{'aa'=>{'aaa'=>'aaaa'}}, 'b'=>'bb'})).to be_truthy\n      end\n\n      it 'should consider x an instance of the aliased type that uses contains an alias that causes self recursion' do\n        t1 = type_alias_t('Tree', 'Hash[String,Variant[String,OtherTree]]')\n        t2 = type_alias_t('OtherTree', 'Hash[String,Tree]')\n        loader = double\n        expect(loader).to receive(:load).with(:type, 'tree').and_return(t1)\n        expect(loader).to receive(:load).with(:type, 'othertree').and_return(t2)\n\n        expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).at_least(:once).and_return(loader)\n\n        t1.resolve(loader)\n        expect(calculator.instance?(t1, {'a'=>{'aa'=>{'aaa'=>'aaaa'}}, 'b'=>'bb'})).to be_truthy\n      end\n    end\n  end\n\n  context 'when converting a ruby class' do\n    it 'should yield \\'PIntegerType\\' for Integer' do\n      expect(calculator.type(Integer).class).to eq(PIntegerType)\n    end\n\n    it 'should yield \\'PFloatType\\' for Float' do\n      expect(calculator.type(Float).class).to eq(PFloatType)\n    end\n\n    it 'should yield \\'PBooleanType\\' for FalseClass and TrueClass' do\n      [FalseClass,TrueClass].each do |c|\n        expect(calculator.type(c).class).to eq(PBooleanType)\n      end\n    end\n\n    it 'should yield \\'PUndefType\\' for NilClass' do\n      expect(calculator.type(NilClass).class).to eq(PUndefType)\n    end\n\n    it 'should yield \\'PStringType\\' for String' do\n      expect(calculator.type(String).class).to eq(PStringType)\n    end\n\n    it 'should yield \\'PRegexpType\\' for Regexp' do\n      expect(calculator.type(Regexp).class).to eq(PRegexpType)\n    end\n\n    it 'should yield \\'PArrayType[PAnyType]\\' for Array' do\n      t = calculator.type(Array)\n      expect(t.class).to eq(PArrayType)\n      expect(t.element_type.class).to eq(PAnyType)\n    end\n\n    it 'should yield \\'PHashType[PAnyType,PAnyType]\\' for Hash' do\n      t = calculator.type(Hash)\n      expect(t.class).to eq(PHashType)\n      expect(t.key_type.class).to eq(PAnyType)\n      expect(t.value_type.class).to eq(PAnyType)\n    end\n\n    it 'type should yield \\'PRuntimeType[ruby,Rational]\\' for Rational' do\n      t = calculator.type(Rational)\n      expect(t.class).to eq(PRuntimeType)\n      expect(t.runtime).to eq(:ruby)\n      expect(t.runtime_type_name).to eq('Rational')\n    end\n\n    it 'infer should yield \\'PRuntimeType[ruby,Rational]\\' for Rational instance' do\n      t = calculator.infer(Rational(2, 3))\n      expect(t.class).to eq(PRuntimeType)\n      expect(t.runtime).to eq(:ruby)\n      expect(t.runtime_type_name).to eq('Rational')\n    end\n  end\n\n  context 'when processing meta type' do\n    it 'should infer PTypeType as the type of all other types' do\n      ptype = PTypeType\n      expect(calculator.infer(PUndefType::DEFAULT     ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PScalarType::DEFAULT    ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PScalarDataType::DEFAULT).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PStringType::DEFAULT    ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PNumericType::DEFAULT   ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PIntegerType::DEFAULT   ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PFloatType::DEFAULT     ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PRegexpType::DEFAULT    ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PBooleanType::DEFAULT   ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PCollectionType::DEFAULT).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PArrayType::DEFAULT     ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PHashType::DEFAULT      ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PIterableType::DEFAULT  ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PRuntimeType::DEFAULT   ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PClassType::DEFAULT ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PResourceType::DEFAULT  ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PEnumType::DEFAULT      ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PPatternType::DEFAULT   ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PVariantType::DEFAULT   ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PTupleType::DEFAULT     ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(POptionalType::DEFAULT  ).is_a?(ptype)).to eq(true)\n      expect(calculator.infer(PCallableType::DEFAULT  ).is_a?(ptype)).to eq(true)\n    end\n\n    it 'should infer PTypeType as the type of all other types' do\n      expect(calculator.infer(PUndefType::DEFAULT     ).to_s).to eq('Type[Undef]')\n      expect(calculator.infer(PScalarType::DEFAULT    ).to_s).to eq('Type[Scalar]')\n      expect(calculator.infer(PScalarDataType::DEFAULT).to_s).to eq('Type[ScalarData]')\n      expect(calculator.infer(PStringType::DEFAULT    ).to_s).to eq('Type[String]')\n      expect(calculator.infer(PNumericType::DEFAULT   ).to_s).to eq('Type[Numeric]')\n      expect(calculator.infer(PIntegerType::DEFAULT   ).to_s).to eq('Type[Integer]')\n      expect(calculator.infer(PFloatType::DEFAULT     ).to_s).to eq('Type[Float]')\n      expect(calculator.infer(PRegexpType::DEFAULT    ).to_s).to eq('Type[Regexp]')\n      expect(calculator.infer(PBooleanType::DEFAULT   ).to_s).to eq('Type[Boolean]')\n      expect(calculator.infer(PCollectionType::DEFAULT).to_s).to eq('Type[Collection]')\n      expect(calculator.infer(PArrayType::DEFAULT     ).to_s).to eq('Type[Array]')\n      expect(calculator.infer(PHashType::DEFAULT      ).to_s).to eq('Type[Hash]')\n      expect(calculator.infer(PIterableType::DEFAULT  ).to_s).to eq('Type[Iterable]')\n      expect(calculator.infer(PRuntimeType::DEFAULT   ).to_s).to eq('Type[Runtime]')\n      expect(calculator.infer(PClassType::DEFAULT ).to_s).to eq('Type[Class]')\n      expect(calculator.infer(PResourceType::DEFAULT  ).to_s).to eq('Type[Resource]')\n      expect(calculator.infer(PEnumType::DEFAULT      ).to_s).to eq('Type[Enum]')\n      expect(calculator.infer(PVariantType::DEFAULT   ).to_s).to eq('Type[Variant]')\n      expect(calculator.infer(PPatternType::DEFAULT   ).to_s).to eq('Type[Pattern]')\n      expect(calculator.infer(PTupleType::DEFAULT     ).to_s).to eq('Type[Tuple]')\n      expect(calculator.infer(POptionalType::DEFAULT  ).to_s).to eq('Type[Optional]')\n      expect(calculator.infer(PCallableType::DEFAULT  ).to_s).to eq('Type[Callable]')\n\n      expect(calculator.infer(PResourceType.new('foo::fee::fum')).to_s).to eq('Type[Foo::Fee::Fum]')\n      expect(calculator.infer(PResourceType.new('foo::fee::fum')).to_s).to eq('Type[Foo::Fee::Fum]')\n      expect(calculator.infer(PResourceType.new('Foo::Fee::Fum')).to_s).to eq('Type[Foo::Fee::Fum]')\n    end\n\n    it \"computes the common type of PTypeType's type parameter\" do\n      int_t    = PIntegerType::DEFAULT\n      string_t = PStringType::DEFAULT\n      expect(calculator.infer([int_t]).to_s).to eq('Array[Type[Integer], 1, 1]')\n      expect(calculator.infer([int_t, string_t]).to_s).to eq('Array[Type[ScalarData], 2, 2]')\n    end\n\n    it 'should infer PTypeType as the type of ruby classes' do\n      class Foo\n      end\n      [Object, Numeric, Integer, Float, String, Regexp, Array, Hash, Foo].each do |c|\n        expect(calculator.infer(c).is_a?(PTypeType)).to eq(true)\n      end\n    end\n\n    it 'should infer PTypeType as the type of PTypeType (meta regression short-circuit)' do\n      expect(calculator.infer(PTypeType::DEFAULT).is_a?(PTypeType)).to eq(true)\n    end\n\n    it 'computes instance? to be true if parameterized and type match' do\n      int_t    = PIntegerType::DEFAULT\n      type_t   = TypeFactory.type_type(int_t)\n      type_type_t   = TypeFactory.type_type(type_t)\n      expect(calculator.instance?(type_type_t, type_t)).to eq(true)\n    end\n\n    it 'computes instance? to be false if parameterized and type do not match' do\n      int_t    = PIntegerType::DEFAULT\n      string_t = PStringType::DEFAULT\n      type_t   = TypeFactory.type_type(int_t)\n      type_t2   = TypeFactory.type_type(string_t)\n      type_type_t   = TypeFactory.type_type(type_t)\n      # i.e. Type[Integer] =~ Type[Type[Integer]] # false\n      expect(calculator.instance?(type_type_t, type_t2)).to eq(false)\n    end\n\n    it 'computes instance? to be true if unparameterized and matched against a type[?]' do\n      int_t    = PIntegerType::DEFAULT\n      type_t   = TypeFactory.type_type(int_t)\n      expect(calculator.instance?(PTypeType::DEFAULT, type_t)).to eq(true)\n    end\n  end\n\n  context 'when asking for an iterable ' do\n    it 'should produce an iterable for an Integer range that is finite' do\n      t = PIntegerType.new(1, 10)\n      expect(calculator.iterable(t).respond_to?(:each)).to eq(true)\n    end\n\n    it 'should not produce an iterable for an Integer range that has an infinite side' do\n      t = PIntegerType.new(nil, 10)\n      expect(calculator.iterable(t)).to eq(nil)\n\n      t = PIntegerType.new(1, nil)\n      expect(calculator.iterable(t)).to eq(nil)\n    end\n\n    it 'all but Integer range are not iterable' do\n      [Object, Numeric, Float, String, Regexp, Array, Hash].each do |t|\n        expect(calculator.iterable(calculator.type(t))).to eq(nil)\n      end\n    end\n\n    it 'should produce an iterable for a type alias of an Iterable type' do\n      t = PTypeAliasType.new('MyAlias', nil, PIntegerType.new(1, 10))\n      expect(calculator.iterable(t).respond_to?(:each)).to eq(true)\n    end\n  end\n\n  context 'when dealing with different types of inference' do\n    it 'an instance specific inference is produced by infer' do\n      expect(calculator.infer(['a','b']).element_type.values).to eq(['a', 'b'])\n    end\n\n    it 'a generic inference is produced using infer_generic' do\n      expect(calculator.infer_generic(['a','b']).element_type).to eql(string_t(range_t(1,1)))\n    end\n\n    it 'a generic result is created by generalize given an instance specific result for an Array' do\n      generic = calculator.infer(['a','b'])\n      expect(generic.element_type.values).to eq(['a','b'])\n      generic = generic.generalize\n      expect(generic.element_type).to eql(string_t(range_t(1,1)))\n    end\n\n    it 'a generic result is created by generalize given an instance specific result for a Hash' do\n      generic = calculator.infer({'a' =>1,'bcd' => 2})\n      expect(generic.key_type.values.sort).to eq(['a', 'bcd'])\n      expect(generic.value_type.from).to eq(1)\n      expect(generic.value_type.to).to eq(2)\n      generic = generic.generalize\n      expect(generic.key_type.size_type).to eq(range_t(1,3))\n      expect(generic.value_type.from).to eq(nil)\n      expect(generic.value_type.to).to eq(nil)\n    end\n\n    it 'ensures that Struct key types are not generalized' do\n      generic = struct_t({'a' => any_t}).generalize\n      expect(generic.to_s).to eq(\"Struct[{'a' => Any}]\")\n      generic = struct_t({not_undef_t('a') => any_t}).generalize\n      expect(generic.to_s).to eq(\"Struct[{NotUndef['a'] => Any}]\")\n      generic = struct_t({optional_t('a') => string_t}).generalize\n      expect(generic.to_s).to eq(\"Struct[{Optional['a'] => String}]\")\n    end\n\n    it 'ensures that Struct value types are generalized' do\n      generic = struct_t({'a' => range_t(1, 3)}).generalize\n      expect(generic.to_s).to eq(\"Struct[{'a' => Integer}]\")\n    end\n\n    it \"does not reduce by combining types when using infer_set\" do\n      element_type = calculator.infer(['a','b',1,2]).element_type\n      expect(element_type.class).to eq(PScalarDataType)\n      inferred_type = calculator.infer_set(['a','b',1,2])\n      expect(inferred_type.class).to eq(PTupleType)\n      element_types = inferred_type.types\n      expect(element_types[0].class).to eq(PStringType)\n      expect(element_types[1].class).to eq(PStringType)\n      expect(element_types[2].class).to eq(PIntegerType)\n      expect(element_types[3].class).to eq(PIntegerType)\n    end\n\n    it 'does not reduce by combining types when using infer_set and values are undef' do\n      element_type = calculator.infer(['a',nil]).element_type\n      expect(element_type.class).to eq(PStringType)\n      inferred_type = calculator.infer_set(['a',nil])\n      expect(inferred_type.class).to eq(PTupleType)\n      element_types = inferred_type.types\n      expect(element_types[0].class).to eq(PStringType)\n      expect(element_types[1].class).to eq(PUndefType)\n    end\n\n    it 'infers on an empty Array produces Array[Unit,0,0]' do\n      inferred_type = calculator.infer([])\n      expect(inferred_type.element_type.class).to eq(PUnitType)\n      expect(inferred_type.size_range).to eq([0, 0])\n    end\n\n    it 'infer_set on an empty Array produces Array[Unit,0,0]' do\n      inferred_type = calculator.infer_set([])\n      expect(inferred_type.element_type.class).to eq(PUnitType)\n      expect(inferred_type.size_range).to eq([0, 0])\n    end\n  end\n\n  context 'when determening callability' do\n    context 'and given is exact' do\n      it 'with callable' do\n        required = callable_t(string_t)\n        given = callable_t(string_t)\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n\n      it 'with args tuple' do\n        required = callable_t(string_t)\n        given = tuple_t(string_t)\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n\n      it 'with args tuple having a block' do\n        required = callable_t(string_t, callable_t(string_t))\n        given = tuple_t(string_t, callable_t(string_t))\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n\n      it 'with args array' do\n        required = callable_t(string_t)\n        given = array_t(string_t, range_t(1, 1))\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n    end\n\n    context 'and given is more generic' do\n      it 'with callable' do\n        required = callable_t(string_t)\n        given = callable_t(any_t)\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n\n      it 'with args tuple' do\n        required = callable_t(string_t)\n        given = tuple_t(any_t)\n        expect(calculator.callable?(required, given)).to eq(false)\n      end\n\n      it 'with args tuple having a block' do\n        required = callable_t(string_t, callable_t(string_t))\n        given = tuple_t(string_t, callable_t(any_t))\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n\n      it 'with args tuple having a block with captures rest' do\n        required = callable_t(string_t, callable_t(string_t))\n        given = tuple_t(string_t, callable_t(any_t, 0, :default))\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n    end\n\n    context 'and given is more specific' do\n      it 'with callable' do\n        required = callable_t(any_t)\n        given = callable_t(string_t)\n        expect(calculator.callable?(required, given)).to eq(false)\n      end\n\n      it 'with args tuple' do\n        required = callable_t(any_t)\n        given = tuple_t(string_t)\n        expect(calculator.callable?(required, given)).to eq(true)\n      end\n\n      it 'with args tuple having a block' do\n        required = callable_t(string_t, callable_t(any_t))\n        given = tuple_t(string_t, callable_t(string_t))\n        expect(calculator.callable?(required, given)).to eq(false)\n      end\n\n      it 'with args tuple having a block with captures rest' do\n        required = callable_t(string_t, callable_t(any_t))\n        given = tuple_t(string_t, callable_t(string_t, 0, :default))\n        expect(calculator.callable?(required, given)).to eq(false)\n      end\n    end\n  end\n\n  matcher :be_assignable_to do |type|\n    match do |actual|\n      type.is_a?(PAnyType) && type.assignable?(actual)\n    end\n\n    failure_message do |actual|\n      \"#{TypeFormatter.string(actual)} should be assignable to #{TypeFormatter.string(type)}\"\n    end\n\n    failure_message_when_negated do |actual|\n      \"#{TypeFormatter.string(actual)} is assignable to #{TypeFormatter.string(type)} when it should not\"\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/type_factory_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'The type factory' do\n  context 'when creating' do\n    it 'integer() returns PIntegerType' do\n      expect(TypeFactory.integer().class()).to eq(PIntegerType)\n    end\n\n    it 'float() returns PFloatType' do\n      expect(TypeFactory.float().class()).to eq(PFloatType)\n    end\n\n    it 'string() returns PStringType' do\n      expect(TypeFactory.string().class()).to eq(PStringType)\n    end\n\n    it 'boolean() returns PBooleanType' do\n      expect(TypeFactory.boolean().class()).to eq(PBooleanType)\n    end\n\n    it 'pattern() returns PPatternType' do\n      expect(TypeFactory.pattern().class()).to eq(PPatternType)\n    end\n\n    it 'regexp() returns PRegexpType' do\n      expect(TypeFactory.regexp().class()).to eq(PRegexpType)\n    end\n\n    it 'enum() returns PEnumType' do\n      expect(TypeFactory.enum().class()).to eq(PEnumType)\n    end\n\n    it 'variant() returns PVariantType' do\n      expect(TypeFactory.variant().class()).to eq(PVariantType)\n    end\n\n    it 'scalar() returns PScalarType' do\n      expect(TypeFactory.scalar().class()).to eq(PScalarType)\n    end\n\n    it 'data() returns Data' do\n      expect(TypeFactory.data.name).to eq('Data')\n    end\n\n    it 'optional() returns POptionalType' do\n      expect(TypeFactory.optional().class()).to eq(POptionalType)\n    end\n\n    it 'collection() returns PCollectionType' do\n      expect(TypeFactory.collection().class()).to eq(PCollectionType)\n    end\n\n    it 'catalog_entry() returns PCatalogEntryType' do\n      expect(TypeFactory.catalog_entry().class()).to eq(PCatalogEntryType)\n    end\n\n    it 'struct() returns PStructType' do\n      expect(TypeFactory.struct().class()).to eq(PStructType)\n    end\n\n    it \"object() returns PObjectType\" do\n      expect(TypeFactory.object.class).to eq(PObjectType)\n    end\n\n    it 'tuple() returns PTupleType' do\n      expect(TypeFactory.tuple.class()).to eq(PTupleType)\n    end\n\n    it 'undef() returns PUndefType' do\n      expect(TypeFactory.undef().class()).to eq(PUndefType)\n    end\n\n    it 'type_alias() returns PTypeAliasType' do\n      expect(TypeFactory.type_alias().class()).to eq(PTypeAliasType)\n    end\n\n    it 'sem_ver() returns PSemVerType' do\n      expect(TypeFactory.sem_ver.class).to eq(PSemVerType)\n    end\n\n    it 'sem_ver(r1, r2) returns constrained PSemVerType' do\n      expect(TypeFactory.sem_ver('1.x', '3.x').ranges).to include(SemanticPuppet::VersionRange.parse('1.x'), SemanticPuppet::VersionRange.parse('3.x'))\n    end\n\n    it 'sem_ver_range() returns PSemVerRangeType' do\n      expect(TypeFactory.sem_ver_range.class).to eq(PSemVerRangeType)\n    end\n\n    it 'default() returns PDefaultType' do\n      expect(TypeFactory.default().class()).to eq(PDefaultType)\n    end\n\n    it 'range(to, from) returns PIntegerType' do\n      t = TypeFactory.range(1,2)\n      expect(t.class()).to eq(PIntegerType)\n      expect(t.from).to eq(1)\n      expect(t.to).to eq(2)\n    end\n\n    it 'range(default, default) returns PIntegerType' do\n      t = TypeFactory.range(:default,:default)\n      expect(t.class()).to eq(PIntegerType)\n      expect(t.from).to eq(nil)\n      expect(t.to).to eq(nil)\n    end\n\n    it 'float_range(to, from) returns PFloatType' do\n      t = TypeFactory.float_range(1.0, 2.0)\n      expect(t.class()).to eq(PFloatType)\n      expect(t.from).to eq(1.0)\n      expect(t.to).to eq(2.0)\n    end\n\n    it 'float_range(default, default) returns PFloatType' do\n      t = TypeFactory.float_range(:default, :default)\n      expect(t.class()).to eq(PFloatType)\n      expect(t.from).to eq(nil)\n      expect(t.to).to eq(nil)\n    end\n\n    it 'resource() creates a generic PResourceType' do\n      pr = TypeFactory.resource()\n      expect(pr.class()).to eq(PResourceType)\n      expect(pr.type_name).to eq(nil)\n    end\n\n    it 'resource(x) creates a PResourceType[x]' do\n      pr = TypeFactory.resource('x')\n      expect(pr.class()).to eq(PResourceType)\n      expect(pr.type_name).to eq('X')\n    end\n\n    it 'host_class() creates a generic PClassType' do\n      hc = TypeFactory.host_class()\n      expect(hc.class()).to eq(PClassType)\n      expect(hc.class_name).to eq(nil)\n    end\n\n    it 'host_class(x) creates a PClassType[x]' do\n      hc = TypeFactory.host_class('x')\n      expect(hc.class()).to eq(PClassType)\n      expect(hc.class_name).to eq('x')\n    end\n\n    it 'host_class(::x) creates a PClassType[x]' do\n      hc = TypeFactory.host_class('::x')\n      expect(hc.class()).to eq(PClassType)\n      expect(hc.class_name).to eq('x')\n    end\n\n    it 'array_of(integer) returns PArrayType[PIntegerType]' do\n      at = TypeFactory.array_of(1)\n      expect(at.class()).to eq(PArrayType)\n      expect(at.element_type.class).to eq(PIntegerType)\n    end\n\n    it 'array_of(PIntegerType) returns PArrayType[PIntegerType]' do\n      at = TypeFactory.array_of(PIntegerType::DEFAULT)\n      expect(at.class()).to eq(PArrayType)\n      expect(at.element_type.class).to eq(PIntegerType)\n    end\n\n    it 'array_of_data returns Array[Data]' do\n      at = TypeFactory.array_of_data\n      expect(at.class()).to eq(PArrayType)\n      expect(at.element_type.name).to eq('Data')\n    end\n\n    it 'hash_of_data returns Hash[String,Data]' do\n      ht = TypeFactory.hash_of_data\n      expect(ht.class()).to eq(PHashType)\n      expect(ht.key_type.class).to eq(PStringType)\n      expect(ht.value_type.name).to eq('Data')\n    end\n\n    it 'ruby(1) returns PRuntimeType[ruby, \\'Integer\\']' do\n      ht = TypeFactory.ruby(1)\n      expect(ht.class()).to eq(PRuntimeType)\n      expect(ht.runtime).to eq(:ruby)\n      expect(ht.runtime_type_name).to eq(1.class.name)\n    end\n\n    it 'a size constrained collection can be created from array' do\n      t = TypeFactory.array_of(TypeFactory.data, TypeFactory.range(1,2))\n      expect(t.size_type.class).to eq(PIntegerType)\n      expect(t.size_type.from).to eq(1)\n      expect(t.size_type.to).to eq(2)\n    end\n\n    it 'a size constrained collection can be created from hash' do\n      t = TypeFactory.hash_of(TypeFactory.scalar, TypeFactory.data, TypeFactory.range(1,2))\n      expect(t.size_type.class).to eq(PIntegerType)\n      expect(t.size_type.from).to eq(1)\n      expect(t.size_type.to).to eq(2)\n    end\n\n    it 'a typed empty array, the resulting array erases the type' do\n      t = Puppet::Pops::Types::TypeFactory.array_of(Puppet::Pops::Types::TypeFactory.data, Puppet::Pops::Types::TypeFactory.range(0,0))\n      expect(t.size_type.class).to eq(Puppet::Pops::Types::PIntegerType)\n      expect(t.size_type.from).to eq(0)\n      expect(t.size_type.to).to eq(0)\n      expect(t.element_type).to eq(Puppet::Pops::Types::PUnitType::DEFAULT)\n    end\n\n    it 'a typed empty hash, the resulting hash erases the key and value type' do\n      t = Puppet::Pops::Types::TypeFactory.hash_of(Puppet::Pops::Types::TypeFactory.scalar, Puppet::Pops::Types::TypeFactory.data, Puppet::Pops::Types::TypeFactory.range(0,0))\n      expect(t.size_type.class).to eq(Puppet::Pops::Types::PIntegerType)\n      expect(t.size_type.from).to eq(0)\n      expect(t.size_type.to).to eq(0)\n      expect(t.key_type).to eq(Puppet::Pops::Types::PUnitType::DEFAULT)\n      expect(t.value_type).to eq(Puppet::Pops::Types::PUnitType::DEFAULT)\n    end\n\n    context 'callable types' do\n      it 'the callable methods produces a Callable' do\n        t = TypeFactory.callable()\n        expect(t.class).to be(PCallableType)\n        expect(t.param_types.class).to be(PTupleType)\n        expect(t.param_types.types).to be_empty\n        expect(t.block_type).to be_nil\n      end\n\n      it 'callable method with types produces the corresponding Tuple for parameters and generated names' do\n        tf = TypeFactory\n        t = tf.callable(tf.integer, tf.string)\n        expect(t.class).to be(PCallableType)\n        expect(t.param_types.class).to be(PTupleType)\n        expect(t.param_types.types).to eql([tf.integer, tf.string])\n        expect(t.block_type).to be_nil\n      end\n\n      it 'callable accepts min range to be given' do\n        tf = TypeFactory\n        t = tf.callable(tf.integer, tf.string, 1)\n        expect(t.class).to be(PCallableType)\n        expect(t.param_types.class).to be(PTupleType)\n        expect(t.param_types.size_type.from).to eql(1)\n        expect(t.param_types.size_type.to).to be_nil\n      end\n\n      it 'callable accepts max range to be given' do\n        tf = TypeFactory\n        t = tf.callable(tf.integer, tf.string, 1, 3)\n        expect(t.class).to be(PCallableType)\n        expect(t.param_types.class).to be(PTupleType)\n        expect(t.param_types.size_type.from).to eql(1)\n        expect(t.param_types.size_type.to).to eql(3)\n      end\n\n      it 'callable accepts max range to be given as :default' do\n        tf = TypeFactory\n        t = tf.callable(tf.integer, tf.string, 1, :default)\n        expect(t.class).to be(PCallableType)\n        expect(t.param_types.class).to be(PTupleType)\n        expect(t.param_types.size_type.from).to eql(1)\n        expect(t.param_types.size_type.to).to be_nil\n      end\n\n      it 'the all_callables method produces a Callable matching any Callable' do\n        t = TypeFactory.all_callables()\n        expect(t.class).to be(PCallableType)\n        expect(t.param_types).to be_nil\n        expect(t.block_type).to be_nil\n      end\n\n      it 'with block are created by placing a Callable last' do\n        block_t = TypeFactory.callable(String)\n        t = TypeFactory.callable(String, block_t)\n        expect(t.block_type).to be(block_t)\n      end\n\n      it 'min size constraint can be used with a block last' do\n        block_t = TypeFactory.callable(String)\n        t = TypeFactory.callable(String, 1, block_t)\n        expect(t.block_type).to be(block_t)\n        expect(t.param_types.size_type.from).to eql(1)\n        expect(t.param_types.size_type.to).to be_nil\n      end\n\n      it 'min, max size constraint can be used with a block last' do\n        block_t = TypeFactory.callable(String)\n        t = TypeFactory.callable(String, 1, 3, block_t)\n        expect(t.block_type).to be(block_t)\n        expect(t.param_types.size_type.from).to eql(1)\n        expect(t.param_types.size_type.to).to eql(3)\n      end\n\n      it 'the with_block methods decorates a Callable with a block_type' do\n        t = TypeFactory.callable\n        t2 = TypeFactory.callable(t)\n        block_t = t2.block_type\n        # given t is returned after mutation\n        expect(block_t).to be(t)\n        expect(block_t.class).to be(PCallableType)\n        expect(block_t.param_types.class).to be(PTupleType)\n        expect(block_t.param_types.types).to be_empty\n        expect(block_t.block_type).to be_nil\n      end\n\n      it 'the with_optional_block methods decorates a Callable with an optional block_type' do\n        b = TypeFactory.callable\n        t = TypeFactory.optional(b)\n        t2 = TypeFactory.callable(t)\n        opt_t = t2.block_type\n        expect(opt_t.class).to be(POptionalType)\n        block_t = opt_t.optional_type\n        # given t is returned after mutation\n        expect(opt_t).to be(t)\n        expect(block_t).to be(b)\n        expect(block_t.class).to be(PCallableType)\n        expect(block_t.param_types.class).to be(PTupleType)\n        expect(block_t.param_types.types).to be_empty\n        expect(block_t.block_type).to be_nil\n      end\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/type_formatter_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops::Types\ndescribe 'The type formatter' do\n  let(:s) { TypeFormatter.new }\n  let(:f) { TypeFactory }\n\n  def drop_indent(str)\n    ld = str.index(\"\\n\")\n    if ld.nil?\n      str\n    else\n      str.gsub(Regexp.compile(\"^ {#{ld - 1}}\"), '')\n    end\n  end\n\n  context 'when representing a literal as a string' do\n    {\n      'true' => true,\n      'false' => false,\n      'undef' => nil,\n      '23.4' => 23.4,\n      '145' => 145,\n      \"'string'\" => 'string',\n      '/expr/' => /expr/,\n      '[1, 2, 3]' => [1, 2, 3],\n      \"{'a' => 32, 'b' => [1, 2, 3]}\" => {'a' => 32,'b' => [1, 2, 3]}\n    }.each_pair do |str, value|\n      it \"should yield '#{str}' for a value of #{str}\" do\n        expect(s.string(value)).to eq(str)\n      end\n    end\n  end\n\n  context 'when using indent' do\n    it 'should put hash entries on new indented lines' do\n      expect(s.indented_string({'a' => 32,'b' => [1, 2, {'c' => 'd'}]})).to eq(<<-FORMATTED)\n{\n  'a' => 32,\n  'b' => [\n    1,\n    2,\n    {\n      'c' => 'd'\n    }\n  ]\n}\nFORMATTED\n    end\n\n    it 'should start on given indent level' do\n      expect(s.indented_string({'a' => 32,'b' => [1, 2, {'c' => 'd'}]}, 3)).to eq(<<-FORMATTED)\n      {\n        'a' => 32,\n        'b' => [\n          1,\n          2,\n          {\n            'c' => 'd'\n          }\n        ]\n      }\nFORMATTED\n    end\n\n    it 'should use given indent width' do\n      expect(s.indented_string({'a' => 32,'b' => [1, 2, {'c' => 'd'}]}, 2, 4)).to eq(<<-FORMATTED)\n        {\n            'a' => 32,\n            'b' => [\n                1,\n                2,\n                {\n                    'c' => 'd'\n                }\n            ]\n        }\nFORMATTED\n    end\n  end\n\n  context 'when representing the type as string' do\n    include_context 'types_setup'\n\n    it \"should yield 'Type' for PTypeType\" do\n      expect(s.string(f.type_type)).to eq('Type')\n    end\n\n    it \"should yield 'Any' for PAnyType\" do\n      expect(s.string(f.any)).to eq('Any')\n    end\n\n    it \"should yield 'Scalar' for PScalarType\" do\n      expect(s.string(f.scalar)).to eq('Scalar')\n    end\n\n    it \"should yield 'Boolean' for PBooleanType\" do\n      expect(s.string(f.boolean)).to eq('Boolean')\n    end\n\n    it \"should yield 'Boolean[true]' for PBooleanType parameterized with true\" do\n      expect(s.string(f.boolean(true))).to eq('Boolean[true]')\n    end\n\n    it \"should yield 'Boolean[false]' for PBooleanType parameterized with false\" do\n      expect(s.string(f.boolean(false))).to eq('Boolean[false]')\n    end\n\n    it \"should yield 'Data' for the Data type\" do\n      expect(s.string(f.data)).to eq('Data')\n    end\n\n    it \"should yield 'Numeric' for PNumericType\" do\n      expect(s.string(f.numeric)).to eq('Numeric')\n    end\n\n    it \"should yield 'Integer' and from/to for PIntegerType\" do\n      expect(s.string(f.integer)).to eq('Integer')\n      expect(s.string(f.range(1,1))).to eq('Integer[1, 1]')\n      expect(s.string(f.range(1,2))).to eq('Integer[1, 2]')\n      expect(s.string(f.range(:default, 2))).to eq('Integer[default, 2]')\n      expect(s.string(f.range(2, :default))).to eq('Integer[2]')\n    end\n\n    it \"should yield 'Float' for PFloatType\" do\n      expect(s.string(f.float)).to eq('Float')\n    end\n\n    it \"should yield 'Regexp' for PRegexpType\" do\n      expect(s.string(f.regexp)).to eq('Regexp')\n    end\n\n    it \"should yield 'Regexp[/pat/]' for parameterized PRegexpType\" do\n      expect(s.string(f.regexp('a/b'))).to eq('Regexp[/a\\/b/]')\n    end\n\n    it \"should yield 'String' for PStringType\" do\n      expect(s.string(f.string)).to eq('String')\n    end\n\n    it \"should yield 'String' for PStringType with value\" do\n      expect(s.string(f.string('a'))).to eq('String')\n    end\n\n    it \"should yield 'String['a']' for PStringType with value if printed with debug_string\" do\n      expect(s.debug_string(f.string('a'))).to eq(\"String['a']\")\n    end\n\n    it \"should yield 'String' and from/to for PStringType\" do\n      expect(s.string(f.string(f.range(1,1)))).to eq('String[1, 1]')\n      expect(s.string(f.string(f.range(1,2)))).to eq('String[1, 2]')\n      expect(s.string(f.string(f.range(:default, 2)))).to eq('String[0, 2]')\n      expect(s.string(f.string(f.range(2, :default)))).to eq('String[2]')\n    end\n\n    it \"should yield 'Array[Integer]' for PArrayType[PIntegerType]\" do\n      expect(s.string(f.array_of(f.integer))).to eq('Array[Integer]')\n    end\n\n    it \"should yield 'Array' for PArrayType::DEFAULT\" do\n      expect(s.string(f.array_of_any)).to eq('Array')\n    end\n\n    it \"should yield 'Array[0, 0]' for an empty array\" do\n      t = f.array_of(PUnitType::DEFAULT, f.range(0,0))\n      expect(s.string(t)).to eq('Array[0, 0]')\n    end\n\n    it \"should yield 'Hash[0, 0]' for an empty hash\" do\n      t = f.hash_of(PUnitType::DEFAULT, PUnitType::DEFAULT, f.range(0,0))\n      expect(s.string(t)).to eq('Hash[0, 0]')\n    end\n\n    it \"should yield 'Collection' and from/to for PCollectionType\" do\n      expect(s.string(f.collection(f.range(1,1)))).to eq('Collection[1, 1]')\n      expect(s.string(f.collection(f.range(1,2)))).to eq('Collection[1, 2]')\n      expect(s.string(f.collection(f.range(:default, 2)))).to eq('Collection[0, 2]')\n      expect(s.string(f.collection(f.range(2, :default)))).to eq('Collection[2]')\n    end\n\n    it \"should yield 'Array' and from/to for PArrayType\" do\n      expect(s.string(f.array_of(f.string, f.range(1,1)))).to eq('Array[String, 1, 1]')\n      expect(s.string(f.array_of(f.string, f.range(1,2)))).to eq('Array[String, 1, 2]')\n      expect(s.string(f.array_of(f.string, f.range(:default, 2)))).to eq('Array[String, 0, 2]')\n      expect(s.string(f.array_of(f.string, f.range(2, :default)))).to eq('Array[String, 2]')\n    end\n\n    it \"should yield 'Iterable' for PIterableType\" do\n      expect(s.string(f.iterable)).to eq('Iterable')\n    end\n\n    it \"should yield 'Iterable[Integer]' for PIterableType[PIntegerType]\" do\n      expect(s.string(f.iterable(f.integer))).to eq('Iterable[Integer]')\n    end\n\n    it \"should yield 'Iterator' for PIteratorType\" do\n      expect(s.string(f.iterator)).to eq('Iterator')\n    end\n\n    it \"should yield 'Iterator[Integer]' for PIteratorType[PIntegerType]\" do\n      expect(s.string(f.iterator(f.integer))).to eq('Iterator[Integer]')\n    end\n\n    it \"should yield 'Timespan' for PTimespanType\" do\n      expect(s.string(f.timespan())).to eq('Timespan')\n    end\n\n    it \"should yield 'Timespan[{hours => 1}] for PTimespanType[Timespan]\" do\n      expect(s.string(f.timespan({'hours' => 1}))).to eq(\"Timespan['0-01:00:00.0']\")\n    end\n\n    it \"should yield 'Timespan[default, {hours => 2}] for PTimespanType[nil, Timespan]\" do\n      expect(s.string(f.timespan(nil, {'hours' => 2}))).to eq(\"Timespan[default, '0-02:00:00.0']\")\n    end\n\n    it \"should yield 'Timespan[{hours => 1}, {hours => 2}] for PTimespanType[Timespan, Timespan]\" do\n      expect(s.string(f.timespan({'hours' => 1}, {'hours' => 2}))).to eq(\"Timespan['0-01:00:00.0', '0-02:00:00.0']\")\n    end\n\n    it \"should yield 'Timestamp' for PTimestampType\" do\n      expect(s.string(f.timestamp())).to eq('Timestamp')\n    end\n\n    it \"should yield 'Timestamp['2016-09-05T13:00:00.000 UTC'] for PTimestampType[Timestamp]\" do\n      expect(s.string(f.timestamp('2016-09-05T13:00:00.000 UTC'))).to eq(\"Timestamp['2016-09-05T13:00:00.000000000 UTC']\")\n    end\n\n    it \"should yield 'Timestamp[default, '2016-09-05T13:00:00.000 UTC'] for PTimestampType[nil, Timestamp]\" do\n      expect(s.string(f.timestamp(nil, '2016-09-05T13:00:00.000 UTC'))).to eq(\"Timestamp[default, '2016-09-05T13:00:00.000000000 UTC']\")\n    end\n\n    it \"should yield 'Timestamp['2016-09-05T13:00:00.000 UTC', '2016-12-01T00:00:00.000 UTC'] for PTimestampType[Timestamp, Timestamp]\" do\n      expect(s.string(f.timestamp('2016-09-05T13:00:00.000 UTC', '2016-12-01T00:00:00.000 UTC'))).to(\n        eq(\"Timestamp['2016-09-05T13:00:00.000000000 UTC', '2016-12-01T00:00:00.000000000 UTC']\"))\n    end\n\n    it \"should yield 'Tuple[Integer]' for PTupleType[PIntegerType]\" do\n      expect(s.string(f.tuple([f.integer]))).to eq('Tuple[Integer]')\n    end\n\n    it \"should yield 'Tuple[T, T,..]' for PTupleType[T, T, ...]\" do\n      expect(s.string(f.tuple([f.integer, f.integer, f.string]))).to eq('Tuple[Integer, Integer, String]')\n    end\n\n    it \"should yield 'Tuple' and from/to for PTupleType\" do\n      types = [f.string]\n      expect(s.string(f.tuple(types, f.range(1,1)))).to eq('Tuple[String, 1, 1]')\n      expect(s.string(f.tuple(types, f.range(1,2)))).to eq('Tuple[String, 1, 2]')\n      expect(s.string(f.tuple(types, f.range(:default, 2)))).to eq('Tuple[String, 0, 2]')\n      expect(s.string(f.tuple(types, f.range(2, :default)))).to eq('Tuple[String, 2]')\n    end\n\n    it \"should yield 'Struct' and details for PStructType\" do\n      struct_t = f.struct({'a'=>Integer, 'b'=>String})\n      expect(s.string(struct_t)).to eq(\"Struct[{'a' => Integer, 'b' => String}]\")\n      struct_t = f.struct({})\n      expect(s.string(struct_t)).to eq('Struct')\n    end\n\n    it \"should yield 'Hash[String, Integer]' for PHashType[PStringType, PIntegerType]\" do\n      expect(s.string(f.hash_of(f.integer, f.string))).to eq('Hash[String, Integer]')\n    end\n\n    it \"should yield 'Hash' and from/to for PHashType\" do\n      expect(s.string(f.hash_of(f.string, f.string, f.range(1,1)))).to eq('Hash[String, String, 1, 1]')\n      expect(s.string(f.hash_of(f.string, f.string, f.range(1,2)))).to eq('Hash[String, String, 1, 2]')\n      expect(s.string(f.hash_of(f.string, f.string, f.range(:default, 2)))).to eq('Hash[String, String, 0, 2]')\n      expect(s.string(f.hash_of(f.string, f.string, f.range(2, :default)))).to eq('Hash[String, String, 2]')\n    end\n\n    it \"should yield 'Hash' for PHashType::DEFAULT\" do\n      expect(s.string(f.hash_of_any)).to eq('Hash')\n    end\n\n    it \"should yield 'Class' for a PClassType\" do\n      expect(s.string(f.host_class)).to eq('Class')\n    end\n\n    it \"should yield 'Class[x]' for a PClassType[x]\" do\n      expect(s.string(f.host_class('x'))).to eq('Class[x]')\n    end\n\n    it \"should yield 'Resource' for a PResourceType\" do\n      expect(s.string(f.resource)).to eq('Resource')\n    end\n\n    it \"should yield 'File' for a PResourceType['File']\" do\n      expect(s.string(f.resource('File'))).to eq('File')\n    end\n\n    it \"should yield 'File['/tmp/foo']' for a PResourceType['File', '/tmp/foo']\" do\n      expect(s.string(f.resource('File', '/tmp/foo'))).to eq(\"File['/tmp/foo']\")\n    end\n\n    it \"should yield 'Enum[s,...]' for a PEnumType[s,...]\" do\n      t = f.enum('a', 'b', 'c')\n      expect(s.string(t)).to eq(\"Enum['a', 'b', 'c']\")\n    end\n\n    it \"should yield 'Enum[s,...]' for a PEnumType[s,...,false]\" do\n      t = f.enum('a', 'b', 'c', false)\n      expect(s.string(t)).to eq(\"Enum['a', 'b', 'c']\")\n    end\n\n    it \"should yield 'Enum[s,...,true]' for a PEnumType[s,...,true]\" do\n      t = f.enum('a', 'b', 'c', true)\n      expect(s.string(t)).to eq(\"Enum['a', 'b', 'c', true]\")\n    end\n\n    it \"should yield 'Pattern[/pat/,...]' for a PPatternType['pat',...]\" do\n      t = f.pattern('a')\n      t2 = f.pattern('a', 'b', 'c')\n      expect(s.string(t)).to eq('Pattern[/a/]')\n      expect(s.string(t2)).to eq('Pattern[/a/, /b/, /c/]')\n    end\n\n    it \"should escape special characters in the string for a PPatternType['pat',...]\" do\n      t = f.pattern('a/b')\n      expect(s.string(t)).to eq(\"Pattern[/a\\\\/b/]\")\n    end\n\n    it \"should yield 'Variant[t1,t2,...]' for a PVariantType[t1, t2,...]\" do\n      t1 = f.string\n      t2 = f.integer\n      t3 = f.pattern('a')\n      t = f.variant(t1, t2, t3)\n      expect(s.string(t)).to eq('Variant[String, Integer, Pattern[/a/]]')\n    end\n\n    it \"should yield 'Callable' for generic callable\" do\n      expect(s.string(f.all_callables)).to eql('Callable')\n    end\n\n    it \"should yield 'Callable[0,0]' for callable without params\" do\n      expect(s.string(f.callable)).to eql('Callable[0, 0]')\n    end\n\n    it \"should yield 'Callable[[0,0],rt]' for callable without params but with return type\" do\n      expect(s.string(f.callable([], Float))).to eql('Callable[[0, 0], Float]')\n    end\n\n    it \"should yield 'Callable[t,t]' for callable with typed parameters\" do\n      expect(s.string(f.callable(String, Integer))).to eql('Callable[String, Integer]')\n    end\n\n    it \"should yield 'Callable[[t,t],rt]' for callable with typed parameters and return type\" do\n      expect(s.string(f.callable([String, Integer], Float))).to eql('Callable[[String, Integer], Float]')\n    end\n\n    it \"should yield 'Callable[t,min,max]' for callable with size constraint (infinite max)\" do\n      expect(s.string(f.callable(String, 0))).to eql('Callable[String, 0]')\n    end\n\n    it \"should yield 'Callable[t,min,max]' for callable with size constraint (capped max)\" do\n      expect(s.string(f.callable(String, 0, 3))).to eql('Callable[String, 0, 3]')\n    end\n\n    it \"should yield 'Callable[min,max]' callable with size > 0\" do\n      expect(s.string(f.callable(0, 0))).to eql('Callable[0, 0]')\n      expect(s.string(f.callable(0, 1))).to eql('Callable[0, 1]')\n      expect(s.string(f.callable(0, :default))).to eql('Callable[0]')\n    end\n\n    it \"should yield 'Callable[Callable]' for callable with block\" do\n      expect(s.string(f.callable(f.all_callables))).to eql('Callable[0, 0, Callable]')\n      expect(s.string(f.callable(f.string, f.all_callables))).to eql('Callable[String, Callable]')\n      expect(s.string(f.callable(f.string, 1,1, f.all_callables))).to eql('Callable[String, 1, 1, Callable]')\n    end\n\n    it 'should yield Unit for a Unit type' do\n      expect(s.string(PUnitType::DEFAULT)).to eql('Unit')\n    end\n\n    it \"should yield 'NotUndef' for a PNotUndefType\" do\n      t = f.not_undef\n      expect(s.string(t)).to eq('NotUndef')\n    end\n\n    it \"should yield 'NotUndef[T]' for a PNotUndefType[T]\" do\n      t = f.not_undef(f.data)\n      expect(s.string(t)).to eq('NotUndef[Data]')\n    end\n\n    it \"should yield 'NotUndef['string']' for a PNotUndefType['string']\" do\n      t = f.not_undef('hey')\n      expect(s.string(t)).to eq(\"NotUndef['hey']\")\n    end\n\n    it \"should yield the name of an unparameterized type reference\" do\n      t = f.type_reference('What')\n      expect(s.string(t)).to eq(\"TypeReference['What']\")\n    end\n\n    it \"should yield the name and arguments of an parameterized type reference\" do\n      t = f.type_reference('What[Undef, String]')\n      expect(s.string(t)).to eq(\"TypeReference['What[Undef, String]']\")\n    end\n\n    it \"should yield the name of a type alias\" do\n      t = f.type_alias('Alias', 'Integer')\n      expect(s.string(t)).to eq('Alias')\n    end\n\n    it \"should yield 'Type[Runtime[ruby, Puppet]]' for the Puppet module\" do\n      expect(s.string(Puppet)).to eq(\"Runtime[ruby, 'Puppet']\")\n    end\n\n    it \"should yield 'Type[Runtime[ruby, Puppet::Pops]]' for the Puppet::Resource class\" do\n      expect(s.string(Puppet::Resource)).to eq(\"Runtime[ruby, 'Puppet::Resource']\")\n    end\n\n    it \"should yield \\\"SemVer['1.x', '3.x']\\\" for the PSemVerType['1.x', '3.x']\" do\n      expect(s.string(PSemVerType.new(['1.x', '3.x']))).to eq(\"SemVer['1.x', '3.x']\")\n    end\n\n    it 'should present a valid simple name' do\n      (all_types - [PTypeType, PClassType]).each do |t|\n        name = t::DEFAULT.simple_name\n        expect(t.name).to match(\"^Puppet::Pops::Types::P#{name}Type$\")\n      end\n      expect(PTypeType::DEFAULT.simple_name).to eql('Type')\n      expect(PClassType::DEFAULT.simple_name).to eql('Class')\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/type_mismatch_describer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\nrequire 'puppet/loaders'\n\nmodule Puppet::Pops\nmodule Types\n\ndescribe 'the type mismatch describer' do\n  include PuppetSpec::Compiler, PuppetSpec::Files\n\n  context 'with deferred functions' do\n    let(:env_name) { 'spec' }\n    let(:code_dir) { Puppet[:environmentpath] }\n    let(:env_dir) { File.join(code_dir, env_name) }\n    let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_code_dir, env_name, 'modules')]) }\n    let(:node) { Puppet::Node.new('fooname', environment: env) }\n    let(:populated_code_dir) do\n      dir_contained_in(code_dir, env_name => env_content)\n      PuppetSpec::Files.record_tmp(env_dir)\n      code_dir\n    end\n\n    let(:env_content) {\n      {\n        'lib' => {\n          'puppet' => {\n            'functions' => {\n              'string_return.rb' => <<-RUBY.unindent,\n              Puppet::Functions.create_function(:string_return) do\n                dispatch :string_return do\n                  param 'String', :arg1\n                  return_type 'String'\n                end\n                def string_return(arg1)\n                  arg1\n                end\n              end\n              RUBY\n              'variant_return.rb' => <<-RUBY.unindent,\n              Puppet::Functions.create_function(:variant_return) do\n                dispatch :variant_return do\n                  param 'String', :arg1\n                  return_type 'Variant[Integer,Float]'\n                end\n                def variant_return(arg1)\n                  arg1\n                end\n              end\n              RUBY\n              'no_return.rb' => <<-RUBY.unindent,\n              Puppet::Functions.create_function(:no_return) do\n                dispatch :no_return do\n                  param 'String', :arg1\n                end\n                def variant_return(arg1)\n                  arg1\n                end\n              end\n              RUBY\n            }\n          }\n        }\n      }\n    }\n\n    before(:each) do\n      Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))\n    end\n\n    after(:each) do\n      Puppet.pop_context\n    end\n\n    it 'will compile when the parameter type matches the function return_type' do\n      code = <<-CODE\n        $d = Deferred(\"string_return\", ['/a/non/existing/path'])\n        class testclass(String $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code, node) }.to_not raise_error\n    end\n\n    it \"will compile when a Variant parameter's types matches the return type\" do\n      code = <<-CODE\n        $d = Deferred(\"string_return\", ['/a/non/existing/path'])\n        class testclass(Variant[String, Float] $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code, node) }.to_not raise_error\n    end\n\n    it \"will compile with a union of a Variant parameters' types and Variant return types\" do\n      code = <<-CODE\n        $d = Deferred(\"variant_return\", ['/a/non/existing/path'])\n        class testclass(Variant[Any,Float] $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code, node) }.to_not raise_error\n    end\n\n    it 'will warn when there is no defined return_type for the function definition' do\n      code = <<-CODE\n        $d = Deferred(\"no_return\", ['/a/non/existing/path'])\n        class testclass(Variant[String,Boolean] $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect(Puppet).to receive(:warn_once).with(anything, anything, /.+function no_return has no return_type/).at_least(:once)\n      expect { eval_and_collect_notices(code, node) }.to_not raise_error\n    end\n\n    it 'will report a mismatch between a deferred function return type and class parameter value' do\n      code = <<-CODE\n        $d = Deferred(\"string_return\", ['/a/non/existing/path'])\n        class testclass(Integer $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code, node) }.to raise_error(Puppet::Error, /.+'classparam' expects an Integer value, got String/)\n    end\n\n    it 'will report an argument error when no matching arity is found' do\n      code = <<-CODE\n        $d = Deferred(\"string_return\", ['/a/non/existing/path', 'second-invalid-arg'])\n        class testclass(Integer $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code,node) }.to raise_error(Puppet::Error, /.+ No matching arity found for string_return/)\n    end\n\n    it 'will error with no matching Variant class parameters and return_type' do\n      code = <<-CODE\n        $d = Deferred(\"string_return\", ['/a/non/existing/path'])\n        class testclass(Variant[Integer,Float] $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code,node) }.to raise_error(Puppet::Error, /.+'classparam' expects a value of type Integer or Float, got String/)\n    end\n\n    # This test exposes a shortcoming in the #message function for Puppet::Pops::Type::TypeMismatch\n    # where the `actual` is not introspected for the list of Variant types, so the error message\n    # shows that the list of expected types does not match Variant, instead of a list of actual types.\n    it 'will error with no matching Variant class parameters and Variant return_type' do\n      code = <<-CODE\n        $d = Deferred(\"variant_return\", ['/a/non/existing/path'])\n        class testclass(Variant[String,Boolean] $classparam) {\n        }\n        class { 'testclass':\n          classparam => $d\n        }\n      CODE\n      expect { eval_and_collect_notices(code, node) }.to raise_error(Puppet::Error, /.+'classparam' expects a value of type String or Boolean, got Variant/)\n    end\n  end\n\n  it 'will report a mismatch between a hash and a struct with details' do\n    code = <<-CODE\n      function f(Hash[String,String] $h) {\n         $h['a']\n      }\n      f({'a' => 'a', 'b' => 23})\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /'f' parameter 'h' entry 'b' expects a String value, got Integer/)\n  end\n\n  it 'will report a mismatch between a array and tuple with details' do\n    code = <<-CODE\n      function f(Array[String] $h) {\n         $h[0]\n      }\n      f(['a', 23])\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /'f' parameter 'h' index 1 expects a String value, got Integer/)\n  end\n\n  it 'will not report details for a mismatch between an array and a struct' do\n    code = <<-CODE\n      function f(Array[String] $h) {\n         $h[0]\n      }\n      f({'a' => 'a string', 'b' => 23})\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects an Array value, got Struct/)\n  end\n\n  it 'will not report details for a mismatch between a hash and a tuple' do\n    code = <<-CODE\n      function f(Hash[String,String] $h) {\n         $h['a']\n      }\n      f(['a', 23])\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects a Hash value, got Tuple/)\n  end\n\n  it 'will report an array size mismatch' do\n    code = <<-CODE\n      function f(Array[String,1,default] $h) {\n        $h[0]\n      }\n      f([])\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects size to be at least 1, got 0/)\n  end\n\n  it 'will report a hash size mismatch' do\n    code = <<-CODE\n      function f(Hash[String,String,1,default] $h) {\n         $h['a']\n      }\n      f({})\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects size to be at least 1, got 0/)\n  end\n\n  it 'will include the aliased type when reporting a mismatch that involves an alias' do\n    code = <<-CODE\n      type UnprivilegedPort = Integer[1024,65537]\n\n      function check_port(UnprivilegedPort $port) {}\n      check_port(34)\n    CODE\n    expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /parameter 'port' expects an UnprivilegedPort = Integer\\[1024, 65537\\] value, got Integer\\[34, 34\\]/)\n  end\n\n  it 'will include the aliased type when reporting a mismatch that involves an alias nested in another type' do\n    code = <<-CODE\n      type UnprivilegedPort = Integer[1024,65537]\n      type PortMap = Hash[UnprivilegedPort,String]\n\n      function check_port(PortMap $ports) {}\n      check_port({ 34 => 'some service'})\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'ports' expects a PortMap = Hash\\[UnprivilegedPort = Integer\\[1024, 65537\\], String\\] value, got Hash\\[Integer\\[34, 34\\], String\\]/))\n  end\n\n  it 'will not include the aliased type more than once when reporting a mismatch that involves an alias that is self recursive' do\n    code = <<-CODE\n      type Tree = Hash[String,Tree]\n\n      function check_tree(Tree $tree) {}\n      check_tree({ 'x' => {'y' => {32 => 'n'}}})\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'tree' entry 'x' entry 'y' expects a Tree = Hash\\[String, Tree\\] value, got Hash\\[Integer\\[32, 32\\], String\\]/))\n  end\n\n  it 'will use type normalization' do\n    code = <<-CODE\n      type EVariants = Variant[Enum[a,b],Enum[b,c],Enum[c,d]]\n\n      function check_enums(EVariants $evars) {}\n      check_enums('n')\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n       /parameter 'evars' expects a match for EVariants = Enum\\['a', 'b', 'c', 'd'\\], got 'n'/))\n  end\n\n  it \"will not generalize a string that doesn't match an enum in a function call\" do\n    code = <<-CODE\n      function check_enums(Enum[a,b] $arg) {}\n      check_enums('c')\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'arg' expects a match for Enum\\['a', 'b'\\], got 'c'/))\n  end\n\n  it \"will not disclose a Sensitive that doesn't match an enum in a function call\" do\n    code = <<-CODE\n      function check_enums(Enum[a,b] $arg) {}\n      check_enums(Sensitive('c'))\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'arg' expects a match for Enum\\['a', 'b'\\], got Sensitive/))\n  end\n\n  it \"reports errors on the first failing parameter when that parameter is not the first in order\" do\n    code = <<-CODE\n      type Abc = Enum['a', 'b', 'c']\n      type Cde = Enum['c', 'd', 'e']\n      function two_params(Abc $a, Cde $b) {}\n      two_params('a', 'x')\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'b' expects a match for Cde = Enum\\['c', 'd', 'e'\\], got 'x'/))\n  end\n\n  it \"will not generalize a string that doesn't match an enum in a define call\" do\n    code = <<-CODE\n      define check_enums(Enum[a,b] $arg) {}\n      check_enums { x: arg => 'c' }\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'arg' expects a match for Enum\\['a', 'b'\\], got 'c'/))\n  end\n\n  it \"will include Undef when describing a mismatch against a Variant where one of the types is Undef\" do\n    code = <<-CODE\n      define check(Variant[Undef,String,Integer,Hash,Array] $arg) {}\n      check{ x: arg => 2.4 }\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'arg' expects a value of type Undef, String, Integer, Hash, or Array/))\n  end\n\n  it \"will not disclose a Sensitive that doesn't match an enum in a define call\" do\n    code = <<-CODE\n      define check_enums(Enum[a,b] $arg) {}\n      check_enums { x: arg => Sensitive('c') }\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /parameter 'arg' expects a match for Enum\\['a', 'b'\\], got Sensitive/))\n  end\n\n  it \"will report the parameter of Type[<type alias>] using the alias name\" do\n    code = <<-CODE\n      type Custom = String[1]\n      Custom.each |$x| { notice($x) }\n    CODE\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /expects an Iterable value, got Type\\[Custom\\]/))\n  end\n\n  context 'when reporting a mismatch between' do\n    let(:parser) { TypeParser.singleton }\n    let(:subject) { TypeMismatchDescriber.singleton }\n\n    context 'hash and struct' do\n      it 'reports a size mismatch when hash has unlimited size' do\n        expected = parser.parse('Struct[{a=>Integer,b=>Integer}]')\n        actual = parser.parse('Hash[String,Integer]')\n        expect(subject.describe_mismatch('', expected, actual)).to eq('expects size to be 2, got unlimited')\n      end\n\n      it 'reports a size mismatch when hash has specified but incorrect size' do\n        expected = parser.parse('Struct[{a=>Integer,b=>Integer}]')\n        actual = parser.parse('Hash[String,Integer,1,1]')\n        expect(subject.describe_mismatch('', expected, actual)).to eq('expects size to be 2, got 1')\n      end\n\n      it 'reports a full type mismatch when size is correct but hash value type is incorrect' do\n        expected = parser.parse('Struct[{a=>Integer,b=>String}]')\n        actual = parser.parse('Hash[String,Integer,2,2]')\n        expect(subject.describe_mismatch('', expected, actual)).to eq(\"expects a Struct[{'a' => Integer, 'b' => String}] value, got Hash[String, Integer]\")\n      end\n    end\n\n    it 'reports a missing parameter as \"has no parameter\"' do\n      t = parser.parse('Struct[{a=>String}]')\n      expect { subject.validate_parameters('v', t, {'a'=>'a','b'=>'b'}, false) }.to raise_error(Puppet::Error, \"v: has no parameter named 'b'\")\n    end\n\n    it 'reports a missing value as \"expects a value\"' do\n      t = parser.parse('Struct[{a=>String,b=>String}]')\n      expect { subject.validate_parameters('v', t, {'a'=>'a'}, false) }.to raise_error(Puppet::Error, \"v: expects a value for parameter 'b'\")\n    end\n\n    it 'reports a missing block as \"expects a block\"' do\n      callable = parser.parse('Callable[String,String,Callable]')\n      args_tuple = parser.parse('Tuple[String,String]')\n      dispatch = Functions::Dispatch.new(callable, 'foo', ['a','b'], false, 'block')\n      expect(subject.describe_signatures('function', [dispatch], args_tuple)).to eq(\"'function' expects a block\")\n    end\n\n    it 'reports an unexpected block as \"does not expect a block\"' do\n      callable = parser.parse('Callable[String,String]')\n      args_tuple = parser.parse('Tuple[String,String,Callable]')\n      dispatch = Functions::Dispatch.new(callable, 'foo', ['a','b'])\n      expect(subject.describe_signatures('function', [dispatch], args_tuple)).to eq(\"'function' does not expect a block\")\n    end\n\n    it 'reports a block return type mismatch' do\n      callable = parser.parse('Callable[[0,0,Callable[ [0,0],String]],Undef]')\n      args_tuple = parser.parse('Tuple[Callable[[0,0],Integer]]')\n      dispatch = Functions::Dispatch.new(callable, 'foo', [], false, 'block')\n      expect(subject.describe_signatures('function', [dispatch], args_tuple)).to eq(\"'function' block return expects a String value, got Integer\")\n    end\n  end\n\n  it \"reports struct mismatch correctly when hash doesn't contain required keys\" do\n    code = <<-PUPPET\n      type Test::Options = Struct[{\n        var => String\n      }]\n      class test(String $var, Test::Options $opts) {}\n      class { 'test': var => 'hello', opts => {} }\n    PUPPET\n    expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,\n      /Class\\[Test\\]: parameter 'opts' expects size to be 1, got 0/))\n  end\n\n  it \"treats Optional as Optional[Any]\" do\n    code = <<-PUPPET\n      class test(Optional $var=undef) {}\n      class { 'test': var => 'hello' }\n    PUPPET\n    expect { eval_and_collect_notices(code) }.not_to raise_error\n  end\n\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/type_parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\nmodule Puppet::Pops\nmodule Types\ndescribe TypeParser do\n  extend RSpec::Matchers::DSL\n\n  let(:parser) { TypeParser.singleton }\n  let(:types)  { TypeFactory }\n  it \"rejects a puppet expression\" do\n    expect { parser.parse(\"1 + 1\") }.to raise_error(Puppet::ParseError, /The expression <1 \\+ 1> is not a valid type specification/)\n  end\n\n  it \"rejects a empty type specification\" do\n    expect { parser.parse(\"\") }.to raise_error(Puppet::ParseError, /The expression <> is not a valid type specification/)\n  end\n\n  it \"rejects an invalid type simple type\" do\n    expect { parser.parse(\"notAType\") }.to raise_error(Puppet::ParseError, /The expression <notAType> is not a valid type specification/)\n  end\n\n  it \"rejects an unknown parameterized type\" do\n    expect { parser.parse(\"notAType[Integer]\") }.to raise_error(Puppet::ParseError,\n      /The expression <notAType\\[Integer\\]> is not a valid type specification/)\n  end\n\n  it \"rejects an unknown type parameter\" do\n    expect { parser.parse(\"Array[notAType]\") }.to raise_error(Puppet::ParseError,\n      /The expression <Array\\[notAType\\]> is not a valid type specification/)\n  end\n\n  it \"rejects an unknown type parameter in a variant\" do\n    expect { parser.parse(\"Variant[Integer,'not a type']\") }.to raise_error(Puppet::ParseError,\n      /The expression <Variant\\[Integer,'not a type'\\]> is not a valid type specification/)\n  end\n\n  [\n    'Any', 'Data', 'CatalogEntry', 'Scalar', 'Undef', 'Numeric', 'Default'\n  ].each do |name|\n    it \"does not support parameterizing unparameterized type <#{name}>\" do\n      expect { parser.parse(\"#{name}[Integer]\") }.to raise_unparameterized_error_for(name)\n    end\n  end\n\n  it \"parses a simple, unparameterized type into the type object\" do\n    expect(the_type_parsed_from(types.any)).to be_the_type(types.any)\n    expect(the_type_parsed_from(types.integer)).to be_the_type(types.integer)\n    expect(the_type_parsed_from(types.float)).to be_the_type(types.float)\n    expect(the_type_parsed_from(types.string)).to be_the_type(types.string)\n    expect(the_type_parsed_from(types.boolean)).to be_the_type(types.boolean)\n    expect(the_type_parsed_from(types.pattern)).to be_the_type(types.pattern)\n    expect(the_type_parsed_from(types.data)).to be_the_type(types.data)\n    expect(the_type_parsed_from(types.catalog_entry)).to be_the_type(types.catalog_entry)\n    expect(the_type_parsed_from(types.collection)).to be_the_type(types.collection)\n    expect(the_type_parsed_from(types.tuple)).to be_the_type(types.tuple)\n    expect(the_type_parsed_from(types.struct)).to be_the_type(types.struct)\n    expect(the_type_parsed_from(types.optional)).to be_the_type(types.optional)\n    expect(the_type_parsed_from(types.default)).to be_the_type(types.default)\n  end\n\n  it \"interprets an unparameterized Array as an Array of Any\" do\n    expect(parser.parse(\"Array\")).to be_the_type(types.array_of_any)\n  end\n\n  it \"interprets an unparameterized Hash as a Hash of Any, Any\" do\n    expect(parser.parse(\"Hash\")).to be_the_type(types.hash_of_any)\n  end\n\n  it \"interprets a parameterized Array[0, 0] as an empty hash with no key and value type\" do\n    expect(parser.parse(\"Array[0, 0]\")).to be_the_type(types.array_of(types.default, types.range(0, 0)))\n  end\n\n  it \"interprets a parameterized Hash[0, 0] as an empty hash with no key and value type\" do\n    expect(parser.parse(\"Hash[0, 0]\")).to be_the_type(types.hash_of(types.default, types.default, types.range(0, 0)))\n  end\n\n  it \"interprets a parameterized Hash[t] as a Hash of Scalar to t\" do\n    expect(parser.parse(\"Hash[Scalar, Integer]\")).to be_the_type(types.hash_of(types.integer))\n  end\n\n  it 'interprets an Boolean with a true parameter to represent boolean true' do\n    expect(parser.parse('Boolean[true]')).to be_the_type(types.boolean(true))\n  end\n\n  it 'interprets an Boolean with a false parameter to represent boolean false' do\n    expect(parser.parse('Boolean[false]')).to be_the_type(types.boolean(false))\n  end\n\n  it 'does not accept non-boolean parameters' do\n    expect{parser.parse('Boolean[\"false\"]')}.to raise_error(/Boolean parameter must be true or false/)\n  end\n\n  it 'interprets an Integer with one parameter to have unbounded upper range' do\n    expect(parser.parse('Integer[0]')).to eq(parser.parse('Integer[0,default]'))\n  end\n\n  it 'interprets a Float with one parameter to have unbounded upper range' do\n    expect(parser.parse('Float[0]')).to eq(parser.parse('Float[0,default]'))\n  end\n\n  it \"parses a parameterized type into the type object\" do\n    parameterized_array = types.array_of(types.integer)\n    parameterized_hash = types.hash_of(types.integer, types.boolean)\n\n    expect(the_type_parsed_from(parameterized_array)).to be_the_type(parameterized_array)\n    expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash)\n  end\n\n  it \"parses a size constrained collection using capped range\" do\n    parameterized_array = types.array_of(types.integer, types.range(1,2))\n    parameterized_hash = types.hash_of(types.integer, types.boolean, types.range(1,2))\n\n    expect(the_type_parsed_from(parameterized_array)).to be_the_type(parameterized_array)\n    expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash)\n  end\n\n  it \"parses a size constrained collection with open range\" do\n    parameterized_array = types.array_of(types.integer, types.range(1, :default))\n    parameterized_hash = types.hash_of(types.integer, types.boolean, types.range(1, :default))\n\n    expect(the_type_parsed_from(parameterized_array)).to be_the_type(parameterized_array)\n    expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash)\n  end\n\n  it \"parses optional type\" do\n    opt_t = types.optional(Integer)\n    expect(the_type_parsed_from(opt_t)).to be_the_type(opt_t)\n  end\n\n  it \"parses timespan type\" do\n    timespan_t = types.timespan\n    expect(the_type_parsed_from(timespan_t)).to be_the_type(timespan_t)\n  end\n\n  it \"parses timestamp type\" do\n    timestamp_t = types.timestamp\n    expect(the_type_parsed_from(timestamp_t)).to be_the_type(timestamp_t)\n  end\n\n  it \"parses tuple type\" do\n    tuple_t = types.tuple([Integer, String])\n    expect(the_type_parsed_from(tuple_t)).to be_the_type(tuple_t)\n  end\n\n  it \"parses tuple type with occurrence constraint\" do\n    tuple_t = types.tuple([Integer, String], types.range(2, 5))\n    expect(the_type_parsed_from(tuple_t)).to be_the_type(tuple_t)\n  end\n\n  it \"parses struct type\" do\n    struct_t = types.struct({'a'=>Integer, 'b'=>String})\n    expect(the_type_parsed_from(struct_t)).to be_the_type(struct_t)\n  end\n\n  describe \"handles parsing of patterns and regexp\" do\n    { 'Pattern[/([a-z]+)([1-9]+)/]'        => [:pattern, [/([a-z]+)([1-9]+)/]],\n      'Pattern[\"([a-z]+)([1-9]+)\"]'        => [:pattern, [/([a-z]+)([1-9]+)/]],\n      'Regexp[/([a-z]+)([1-9]+)/]'         => [:regexp,  [/([a-z]+)([1-9]+)/]],\n      'Pattern[/x9/, /([a-z]+)([1-9]+)/]'  => [:pattern, [/x9/, /([a-z]+)([1-9]+)/]],\n    }.each do |source, type|\n      it \"such that the source '#{source}' yields the type #{type.to_s}\" do\n        expect(parser.parse(source)).to be_the_type(TypeFactory.send(type[0], *type[1]))\n      end\n    end\n  end\n\n  it \"rejects an collection spec with the wrong number of parameters\" do\n    expect { parser.parse(\"Array[Integer, 1,2,3]\") }.to raise_the_parameter_error(\"Array\", \"1 to 3\", 4)\n    expect { parser.parse(\"Hash[Integer, Integer, 1,2,3]\") }.to raise_the_parameter_error(\"Hash\", \"2 to 4\", 5)\n  end\n\n  context 'with scope context and loader' do\n    let!(:scope) { {} }\n    let(:loader) { double }\n\n    before :each do\n      expect(Adapters::LoaderAdapter).to receive(:loader_for_model_object).and_return(loader)\n    end\n\n    it 'interprets anything that is not found by the loader to be a type reference' do\n      expect(loader).to receive(:load).with(:type, 'nonesuch').and_return(nil)\n      expect(parser.parse('Nonesuch', scope)).to be_the_type(types.type_reference('Nonesuch'))\n    end\n\n    it 'interprets anything that is found by the loader to be what the loader found' do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse('File', scope)).to be_the_type(types.resource('File'))\n    end\n\n    it \"parses a resource type with title\" do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse(\"File['/tmp/foo']\", scope)).to be_the_type(types.resource('file', '/tmp/foo'))\n    end\n\n    it \"parses a resource type using 'Resource[type]' form\" do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse(\"Resource[File]\", scope)).to be_the_type(types.resource('file'))\n    end\n\n    it \"parses a resource type with title using 'Resource[type, title]'\" do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(nil)\n      expect(parser.parse(\"Resource[File, '/tmp/foo']\", scope)).to be_the_type(types.resource('file', '/tmp/foo'))\n    end\n\n    it \"parses a resource type with title using 'Resource[Type[title]]'\" do\n      expect(loader).to receive(:load).with(:type, 'nonesuch').and_return(nil)\n      expect(parser.parse(\"Resource[Nonesuch['fife']]\", scope)).to be_the_type(types.resource('nonesuch', 'fife'))\n    end\n  end\n\n  context 'with loader context' do\n    let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n    let(:loader) { Puppet::Pops::Loader::BaseLoader.new(nil, \"type_parser_unit_test_loader\", environment) }\n\n    it 'interprets anything that is not found by the loader to be a type reference' do\n      expect(loader).to receive(:load).with(:type, 'nonesuch').and_return(nil)\n      expect(parser.parse('Nonesuch', loader)).to be_the_type(types.type_reference('Nonesuch'))\n    end\n\n    it 'interprets anything that is found by the loader to be what the loader found' do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse('File', loader)).to be_the_type(types.resource('file'))\n    end\n\n    it \"parses a resource type with title\" do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse(\"File['/tmp/foo']\", loader)).to be_the_type(types.resource('file', '/tmp/foo'))\n    end\n\n    it \"parses a resource type using 'Resource[type]' form\" do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse(\"Resource[File]\", loader)).to be_the_type(types.resource('file'))\n    end\n\n    it \"parses a resource type with title using 'Resource[type, title]'\" do\n      expect(loader).to receive(:load).with(:type, 'file').and_return(types.resource('File'))\n      expect(parser.parse(\"Resource[File, '/tmp/foo']\", loader)).to be_the_type(types.resource('file', '/tmp/foo'))\n    end\n  end\n\n  context 'without a scope' do\n    it \"interprets anything that is not a built in type to be a type reference\" do\n      expect(parser.parse('TestType')).to eq(types.type_reference('TestType'))\n    end\n\n    it \"interprets anything that is not a built in type with parameterers to be type reference with parameters\" do\n      expect(parser.parse(\"TestType['/tmp/foo']\")).to eq(types.type_reference(\"TestType['/tmp/foo']\"))\n    end\n  end\n\n  it \"parses a host class type\" do\n    expect(parser.parse(\"Class\")).to be_the_type(types.host_class())\n  end\n\n  it \"parses a parameterized host class type\" do\n    expect(parser.parse(\"Class[foo::bar]\")).to be_the_type(types.host_class('foo::bar'))\n  end\n\n  it 'parses an integer range' do\n   expect(parser.parse(\"Integer[1,2]\")).to be_the_type(types.range(1,2))\n  end\n\n  it 'parses a negative integer range' do\n    expect(parser.parse(\"Integer[-3,-1]\")).to be_the_type(types.range(-3,-1))\n  end\n\n  it 'parses a float range' do\n   expect(parser.parse(\"Float[1.0,2.0]\")).to be_the_type(types.float_range(1.0,2.0))\n  end\n\n  it 'parses a collection size range' do\n   expect(parser.parse(\"Collection[1,2]\")).to be_the_type(types.collection(types.range(1,2)))\n  end\n\n  it 'parses a timespan type' do\n    expect(parser.parse(\"Timespan\")).to be_the_type(types.timespan)\n  end\n\n  it 'parses a timespan type with a lower bound' do\n    expect(parser.parse(\"Timespan[{hours => 3}]\")).to be_the_type(types.timespan({'hours' => 3}))\n  end\n\n  it 'parses a timespan type with an upper bound' do\n    expect(parser.parse(\"Timespan[default, {hours => 9}]\")).to be_the_type(types.timespan(nil, {'hours' => 9}))\n  end\n\n  it 'parses a timespan type with both lower and upper bounds' do\n    expect(parser.parse(\"Timespan[{hours => 3}, {hours => 9}]\")).to be_the_type(types.timespan({'hours' => 3}, {'hours' => 9}))\n  end\n\n  it 'parses a timestamp type' do\n    expect(parser.parse(\"Timestamp\")).to be_the_type(types.timestamp)\n  end\n\n  it 'parses a timestamp type with a lower bound' do\n    expect(parser.parse(\"Timestamp['2014-12-12T13:14:15 CET']\")).to be_the_type(types.timestamp('2014-12-12T13:14:15 CET'))\n  end\n\n  it 'parses a timestamp type with an upper bound' do\n    expect(parser.parse(\"Timestamp[default, '2014-12-12T13:14:15 CET']\")).to be_the_type(types.timestamp(nil, '2014-12-12T13:14:15 CET'))\n  end\n\n  it 'parses a timestamp type with both lower and upper bounds' do\n    expect(parser.parse(\"Timestamp['2014-12-12T13:14:15 CET', '2016-08-23T17:50:00 CET']\")).to be_the_type(types.timestamp('2014-12-12T13:14:15 CET', '2016-08-23T17:50:00 CET'))\n  end\n\n  it 'parses a type type' do\n    expect(parser.parse(\"Type[Integer]\")).to be_the_type(types.type_type(types.integer))\n  end\n\n  it 'parses a ruby type' do\n    expect(parser.parse(\"Runtime[ruby, 'Integer']\")).to be_the_type(types.ruby_type('Integer'))\n  end\n\n  it 'parses a callable type' do\n    t = parser.parse(\"Callable\")\n    expect(t).to be_the_type(types.all_callables())\n    expect(t.return_type).to be_nil\n  end\n\n  it 'parses a parameterized callable type' do\n    t = parser.parse(\"Callable[String, Integer]\")\n    expect(t).to be_the_type(types.callable(String, Integer))\n    expect(t.return_type).to be_nil\n  end\n\n  it 'parses a parameterized callable type with min/max' do\n    t = parser.parse(\"Callable[String, Integer, 1, default]\")\n    expect(t).to be_the_type(types.callable(String, Integer, 1, :default))\n    expect(t.return_type).to be_nil\n  end\n\n  it 'parses a parameterized callable type with block' do\n    t = parser.parse(\"Callable[String, Callable[Boolean]]\")\n    expect(t).to be_the_type(types.callable(String, types.callable(true)))\n    expect(t.return_type).to be_nil\n  end\n\n  it 'parses a callable with no parameters and return type' do\n    expect(parser.parse(\"Callable[[],Float]\")).to be_the_type(types.callable([],Float))\n  end\n\n  it 'parses a parameterized callable type with return type' do\n    expect(parser.parse(\"Callable[[String, Integer],Float]\")).to be_the_type(types.callable([String, Integer],Float))\n  end\n\n  it 'parses a parameterized callable type with min/max and return type' do\n    expect(parser.parse(\"Callable[[String, Integer, 1, default],Float]\")).to be_the_type(types.callable([String, Integer, 1, :default], Float))\n  end\n\n  it 'parses a parameterized callable type with block and return type' do\n    expect(parser.parse(\"Callable[[String, Callable[Boolean]],Float]\")).to be_the_type(types.callable([String, types.callable(true)], Float))\n  end\n\n  it 'parses a parameterized callable type with 0 min/max' do\n    t = parser.parse(\"Callable[0,0]\")\n    expect(t).to be_the_type(types.callable(0,0))\n    expect(t.param_types.types).to be_empty\n    expect(t.return_type).to be_nil\n  end\n\n  it 'parses a parameterized callable type with 0 min/max and return_type' do\n    t = parser.parse(\"Callable[[0,0],Float]\")\n    expect(t).to be_the_type(types.callable([0,0],Float))\n    expect(t.param_types.types).to be_empty\n    expect(t.return_type).to be_the_type(types.float)\n  end\n\n  it 'parses a parameterized callable type with >0 min/max' do\n    t = parser.parse(\"Callable[0,1]\")\n    expect(t).to be_the_type(types.callable(0,1))\n    # Contains a Unit type to indicate \"called with what you accept\"\n    expect(t.param_types.types[0]).to be_the_type(PUnitType.new())\n    expect(t.return_type).to be_nil\n  end\n\n  it 'parses a parameterized callable type with >0 min/max and a return type' do\n    t = parser.parse(\"Callable[[0,1],Float]\")\n    expect(t).to be_the_type(types.callable([0,1], Float))\n    # Contains a Unit type to indicate \"called with what you accept\"\n    expect(t.param_types.types[0]).to be_the_type(PUnitType.new())\n    expect(t.return_type).to be_the_type(types.float)\n  end\n\n  it 'parses all known literals' do\n    t = parser.parse('Nonesuch[{a=>undef,b=>true,c=>false,d=>default,e=>\"string\",f=>0,g=>1.0,h=>[1,2,3]}]')\n    expect(t).to be_a(PTypeReferenceType)\n    expect(t.type_string).to eql('Nonesuch[{a=>undef,b=>true,c=>false,d=>default,e=>\"string\",f=>0,g=>1.0,h=>[1,2,3]}]')\n  end\n\n  it 'parses a parameterized Enum using identifiers' do\n    t = parser.parse('Enum[a, b]')\n    expect(t).to be_a(PEnumType)\n    expect(t.to_s).to eql(\"Enum['a', 'b']\")\n  end\n\n  it 'parses a parameterized Enum using strings' do\n    t = parser.parse(\"Enum['a', 'b']\")\n    expect(t).to be_a(PEnumType)\n    expect(t.to_s).to eql(\"Enum['a', 'b']\")\n  end\n\n  it 'rejects a parameterized Enum using type refs' do\n    expect { parser.parse('Enum[A, B]') }.to raise_error(/Enum parameters must be identifiers or strings/)\n  end\n\n  it 'rejects a parameterized Enum using integers' do\n    expect { parser.parse('Enum[1, 2]') }.to raise_error(/Enum parameters must be identifiers or strings/)\n  end\n\n  matcher :be_the_type do |type|\n    calc = TypeCalculator.new\n\n    match do |actual|\n      calc.assignable?(actual, type) && calc.assignable?(type, actual)\n    end\n\n    failure_message do |actual|\n      \"expected #{calc.string(type)}, but was #{calc.string(actual)}\"\n    end\n  end\n\n  def raise_the_parameter_error(type, required, given)\n    raise_error(Puppet::ParseError, /#{type} requires #{required}, #{given} provided/)\n  end\n\n  def raise_type_error_for(type_name)\n    raise_error(Puppet::ParseError, /Unknown type <#{type_name}>/)\n  end\n\n  def raise_unparameterized_error_for(type_name)\n    raise_error(Puppet::ParseError, /Not a parameterized type <#{type_name}>/)\n  end\n\n  def the_type_parsed_from(type)\n    parser.parse(the_type_spec_for(type))\n  end\n\n  def the_type_spec_for(type)\n    TypeFormatter.string(type)\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/types/types_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/compiler'\n\nmodule Puppet::Pops\nmodule Types\ndescribe 'Puppet Type System' do\n  include PuppetSpec::Compiler\n\n  let(:tf) { TypeFactory }\n  context 'Integer type' do\n    let!(:a) { tf.range(10, 20) }\n    let!(:b) { tf.range(18, 28) }\n    let!(:c) { tf.range( 2, 12) }\n    let!(:d) { tf.range(12, 18) }\n    let!(:e) { tf.range( 8, 22) }\n    let!(:f) { tf.range( 8,  9) }\n    let!(:g) { tf.range(21, 22) }\n    let!(:h) { tf.range(30, 31) }\n    let!(:i) { tf.float_range(1.0, 30.0) }\n    let!(:j) { tf.float_range(1.0, 9.0) }\n\n    context 'when testing if ranges intersect' do\n      it 'detects an intersection when self is before its argument' do\n        expect(a.intersect?(b)).to be_truthy\n      end\n\n      it 'detects an intersection when self is after its argument' do\n        expect(a.intersect?(c)).to be_truthy\n      end\n\n      it 'detects an intersection when self covers its argument' do\n        expect(a.intersect?(d)).to be_truthy\n      end\n\n      it 'detects an intersection when self equals its argument' do\n        expect(a.intersect?(a)).to be_truthy\n      end\n\n      it 'detects an intersection when self is covered by its argument' do\n        expect(a.intersect?(e)).to be_truthy\n      end\n\n      it 'does not consider an adjacent range to be intersecting' do\n        [f, g].each {|x| expect(a.intersect?(x)).to be_falsey }\n      end\n\n      it 'does not consider an range that is apart to be intersecting' do\n        expect(a.intersect?(h)).to be_falsey\n      end\n\n      it 'does not consider an overlapping float range to be intersecting' do\n        expect(a.intersect?(i)).to be_falsey\n      end\n    end\n\n    context 'when testing if ranges are adjacent' do\n      it 'detects an adjacent type when self is after its argument' do\n        expect(a.adjacent?(f)).to be_truthy\n      end\n\n      it 'detects an adjacent type when self is before its argument' do\n        expect(a.adjacent?(g)).to be_truthy\n      end\n\n      it 'does not consider overlapping types to be adjacent' do\n        [a, b, c, d, e].each { |x| expect(a.adjacent?(x)).to be_falsey }\n      end\n\n      it 'does not consider an range that is apart to be adjacent' do\n        expect(a.adjacent?(h)).to be_falsey\n      end\n\n      it 'does not consider an adjacent float range to be adjancent' do\n        expect(a.adjacent?(j)).to be_falsey\n      end\n    end\n\n    context 'when merging ranges' do\n      it 'will merge intersecting ranges' do\n        expect(a.merge(b)).to eq(tf.range(10, 28))\n      end\n\n      it 'will merge adjacent ranges' do\n        expect(a.merge(g)).to eq(tf.range(10, 22))\n      end\n\n      it 'will not merge ranges that are apart' do\n        expect(a.merge(h)).to be_nil\n      end\n\n      it 'will not merge overlapping float ranges' do\n        expect(a.merge(i)).to be_nil\n      end\n\n      it 'will not merge adjacent float ranges' do\n        expect(a.merge(j)).to be_nil\n      end\n    end\n  end\n\n  context 'Float type' do\n    let!(:a) { tf.float_range(10.0, 20.0) }\n    let!(:b) { tf.float_range(18.0, 28.0) }\n    let!(:c) { tf.float_range( 2.0, 12.0) }\n    let!(:d) { tf.float_range(12.0, 18.0) }\n    let!(:e) { tf.float_range( 8.0, 22.0) }\n    let!(:f) { tf.float_range(30.0, 31.0) }\n    let!(:g) { tf.range(1, 30) }\n\n    context 'when testing if ranges intersect' do\n      it 'detects an intersection when self is before its argument' do\n        expect(a.intersect?(b)).to be_truthy\n      end\n\n      it 'detects an intersection when self is after its argument' do\n        expect(a.intersect?(c)).to be_truthy\n      end\n\n      it 'detects an intersection when self covers its argument' do\n        expect(a.intersect?(d)).to be_truthy\n      end\n\n      it 'detects an intersection when self equals its argument' do\n        expect(a.intersect?(a)).to be_truthy\n      end\n\n      it 'detects an intersection when self is covered by its argument' do\n        expect(a.intersect?(e)).to be_truthy\n      end\n\n      it 'does not consider an range that is apart to be intersecting' do\n        expect(a.intersect?(f)).to be_falsey\n      end\n\n      it 'does not consider an overlapping integer range to be intersecting' do\n        expect(a.intersect?(g)).to be_falsey\n      end\n    end\n\n    context 'when merging ranges' do\n      it 'will merge intersecting ranges' do\n        expect(a.merge(b)).to eq(tf.float_range(10.0, 28.0))\n      end\n\n      it 'will not merge ranges that are apart' do\n        expect(a.merge(f)).to be_nil\n      end\n\n      it 'will not merge overlapping integer ranges' do\n        expect(a.merge(g)).to be_nil\n      end\n    end\n  end\n\n  context 'Boolean type' do\n    it 'parameterized type is assignable to base type' do\n      code = <<-CODE\n        notice(Boolean[true] < Boolean)\n        notice(Boolean[false] < Boolean)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n    end\n\n    it 'boolean literals are instances of the base type' do\n      code = <<-CODE\n        notice(true =~ Boolean)\n        notice(false =~ Boolean)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n    end\n\n    it 'boolean literals are instances of type parameterized with the same literal' do\n      code = <<-CODE\n        notice(true =~ Boolean[true])\n        notice(false =~ Boolean[false])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n    end\n\n    it 'boolean literals are not instances of type parameterized with a different literal' do\n      code = <<-CODE\n        notice(true =~ Boolean[false])\n        notice(false =~ Boolean[true])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['false', 'false'])\n    end\n  end\n\n  context 'Enum type' do\n    it 'sorts its entries' do\n      code = <<-CODE\n        Enum[c,b,a].each |$e| { notice $e }\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['a', 'b', 'c'])\n    end\n\n    it 'makes entries unique' do\n      code = <<-CODE\n        Enum[a,b,c,b,a].each |$e| { notice $e }\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['a', 'b', 'c'])\n    end\n\n    it 'is case sensitive by default' do\n      code = <<-CODE\n        notice('A' =~ Enum[a,b])\n        notice('a' =~ Enum[a,b])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['false', 'true'])\n    end\n\n    it 'is case insensitive when last parameter argument is true' do\n      code = <<-CODE\n        notice('A' =~ Enum[a,b,true])\n        notice('a' =~ Enum[a,b,true])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n    end\n  end\n\n  context 'Regexp type' do\n    it 'parameterized type is assignable to base type' do\n      code = <<-CODE\n        notice(Regexp[a] < Regexp)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true'])\n    end\n\n    it 'new function creates a new regexp' do\n      code = <<-CODE\n        $r = Regexp('abc')\n        notice('abc' =~ $r)\n        notice(type($r) =~ Type[Regexp])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n    end\n\n    it 'special characters are escaped with second parameter to Regexp.new set to true' do\n      code = <<-CODE\n        $r = Regexp('.[]', true)\n        notice('.[]' =~ $r)\n        notice(String($r) == \"\\\\.\\\\[\\\\]\")\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true'])\n    end\n  end\n\n  context 'Iterable type' do\n    it 'can be parameterized with element type' do\n      code = <<-CODE\n      function foo(Iterable[String] $x) {\n        $x.each |$e| {\n          notice $e\n        }\n      }\n      foo([bar, baz, cake])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['bar', 'baz', 'cake'])\n    end\n  end\n\n  context 'Iterator type' do\n    let!(:iterint) { tf.iterator(tf.integer) }\n\n    context 'when testing instance?' do\n      it 'will consider an iterable on an integer is an instance of Iterator[Integer]' do\n        expect(iterint.instance?(Iterable.on(3))).to be_truthy\n      end\n\n      it 'will consider an iterable on string to be an instance of Iterator[Integer]' do\n        expect(iterint.instance?(Iterable.on('string'))).to be_falsey\n      end\n    end\n\n    context 'when testing assignable?' do\n      it 'will consider an iterator with an assignable type as assignable' do\n        expect(tf.iterator(tf.numeric).assignable?(iterint)).to be_truthy\n      end\n\n      it 'will not consider an iterator with a non assignable type as assignable' do\n        expect(tf.iterator(tf.string).assignable?(iterint)).to be_falsey\n      end\n    end\n\n    context 'when asked for an iterable type' do\n      it 'the default iterator type returns the default iterable type' do\n        expect(PIteratorType::DEFAULT.iterable_type).to be(PIterableType::DEFAULT)\n      end\n\n      it 'a typed iterator type returns the an equally typed iterable type' do\n        expect(iterint.iterable_type).to eq(tf.iterable(tf.integer))\n      end\n    end\n\n    it 'can be parameterized with an element type' do\n      code = <<-CODE\n      function foo(Iterator[String] $x) {\n        $x.each |$e| {\n          notice $e\n        }\n      }\n      foo([bar, baz, cake].reverse_each)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['cake', 'baz', 'bar'])\n    end\n  end\n\n  context 'Collection type' do\n    it 'can be parameterized with a range' do\n      code = <<-CODE\n      notice(Collection[5, default] == Collection[5])\n      notice(Collection[5, 5] > Tuple[Integer, 0, 10])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'false'])\n    end\n  end\n\n  context 'Struct type' do\n    context 'can be used as key in hash' do\n      it 'compacts optional in optional in optional to just optional' do\n        key1 = tf.struct({'foo' => tf.string})\n        key2 = tf.struct({'foo' => tf.string})\n        expect({key1 => 'hi'}[key2]).to eq('hi')\n      end\n    end\n  end\n\n  context 'Optional type' do\n    let!(:overlapping_ints) { tf.variant(tf.range(10, 20), tf.range(18, 28)) }\n    let!(:optoptopt) { tf.optional(tf.optional(tf.optional(overlapping_ints))) }\n    let!(:optnu) { tf.optional(tf.not_undef(overlapping_ints)) }\n\n    context 'when normalizing' do\n      it 'compacts optional in optional in optional to just optional' do\n        expect(optoptopt.normalize).to eq(tf.optional(tf.range(10, 28)))\n      end\n    end\n\n    it 'compacts NotUndef in Optional to just Optional' do\n      expect(optnu.normalize).to eq(tf.optional(tf.range(10, 28)))\n    end\n  end\n\n  context 'NotUndef type' do\n    let!(:nununu) { tf.not_undef(tf.not_undef(tf.not_undef(tf.any))) }\n    let!(:nuopt) { tf.not_undef(tf.optional(tf.any)) }\n    let!(:nuoptint) { tf.not_undef(tf.optional(tf.integer)) }\n\n    context 'when normalizing' do\n      it 'compacts NotUndef in NotUndef in NotUndef to just NotUndef' do\n        expect(nununu.normalize).to eq(tf.not_undef(tf.any))\n      end\n\n      it 'compacts Optional in NotUndef to just NotUndef' do\n        expect(nuopt.normalize).to eq(tf.not_undef(tf.any))\n      end\n\n      it 'compacts NotUndef[Optional[Integer]] in NotUndef to just Integer' do\n        expect(nuoptint.normalize).to eq(tf.integer)\n      end\n    end\n  end\n\n  context 'Variant type' do\n    let!(:overlapping_ints) { tf.variant(tf.range(10, 20), tf.range(18, 28)) }\n    let!(:adjacent_ints) { tf.variant(tf.range(10, 20), tf.range(8, 9)) }\n    let!(:mix_ints) { tf.variant(overlapping_ints, adjacent_ints) }\n    let!(:overlapping_floats) { tf.variant(tf.float_range(10.0, 20.0), tf.float_range(18.0, 28.0)) }\n    let!(:enums) { tf.variant(tf.enum('a', 'b'), tf.enum('b', 'c')) }\n    let!(:enums_s_is) { tf.variant(tf.enum('a', 'b'), tf.enum('b', 'c', true)) }\n    let!(:enums_is_is) { tf.variant(tf.enum('A', 'b', true), tf.enum('B', 'c', true)) }\n    let!(:patterns) { tf.variant(tf.pattern('a', 'b'), tf.pattern('b', 'c')) }\n    let!(:with_undef) { tf.variant(tf.undef, tf.range(1,10)) }\n    let!(:all_optional) { tf.variant(tf.optional(tf.range(1,10)), tf.optional(tf.range(11,20))) }\n    let!(:groups) { tf.variant(mix_ints, overlapping_floats, enums, patterns, with_undef, all_optional) }\n\n    context 'when normalizing contained types that' do\n      it 'are overlapping ints, the result is a range' do\n        expect(overlapping_ints.normalize).to eq(tf.range(10, 28))\n      end\n\n      it 'are adjacent ints, the result is a range' do\n        expect(adjacent_ints.normalize).to eq(tf.range(8, 20))\n      end\n\n      it 'are mixed variants with adjacent and overlapping ints, the result is a range' do\n        expect(mix_ints.normalize).to eq(tf.range(8, 28))\n      end\n\n      it 'are overlapping floats, the result is a float range' do\n        expect(overlapping_floats.normalize).to eq(tf.float_range(10.0, 28.0))\n      end\n\n      it 'are enums, the result is an enum' do\n        expect(enums.normalize).to eq(tf.enum('a', 'b', 'c'))\n      end\n\n      it 'are case sensitive versus case insensitive enums, does not merge the enums' do\n        expect(enums_s_is.normalize).to eq(enums_s_is)\n      end\n\n      it 'are case insensitive enums, result is case insensitive and unique irrespective of case' do\n        expect(enums_is_is.normalize).to eq(tf.enum('a', 'b', 'c', true))\n      end\n\n      it 'are patterns, the result is a pattern' do\n        expect(patterns.normalize).to eq(tf.pattern('a', 'b', 'c'))\n      end\n\n      it 'contains an Undef, the result is Optional' do\n        expect(with_undef.normalize).to eq(tf.optional(tf.range(1,10)))\n      end\n\n      it 'are all Optional, the result is an Optional with normalized type' do\n        expect(all_optional.normalize).to eq(tf.optional(tf.range(1,20)))\n      end\n\n      it 'can be normalized in groups, the result is a Variant containing the resulting normalizations' do\n        expect(groups.normalize).to eq(tf.optional(\n          tf.variant(\n            tf.range(1, 28),\n            tf.float_range(10.0, 28.0),\n            tf.enum('a', 'b', 'c'),\n            tf.pattern('a', 'b', 'c'))))\n      end\n    end\n\n    context 'when generalizing' do\n      it 'will generalize and compact contained types' do\n        expect(tf.variant(tf.string(tf.range(3,3)), tf.string(tf.range(5,5))).generalize).to eq(tf.variant(tf.string))\n      end\n    end\n  end\n\n  context 'Runtime type' do\n    it 'can be created with a runtime and a runtime type name' do\n      expect(tf.runtime('ruby', 'Hash').to_s).to eq(\"Runtime[ruby, 'Hash']\")\n    end\n\n    it 'can be created with a runtime and, puppet name pattern, and runtime replacement' do\n      expect(tf.runtime('ruby', [/^MyPackage::(.*)$/, 'MyModule::\\1']).to_s).to eq(\"Runtime[ruby, [/^MyPackage::(.*)$/, 'MyModule::\\\\1']]\")\n    end\n\n    it 'will map a Puppet name to a runtime type' do\n      t = tf.runtime('ruby', [/^MyPackage::(.*)$/, 'MyModule::\\1'])\n      expect(t.from_puppet_name('MyPackage::MyType').to_s).to eq(\"Runtime[ruby, 'MyModule::MyType']\")\n    end\n\n    it 'with parameters is assignable to the default Runtime type' do\n      code = <<-CODE\n      notice(Runtime[ruby, 'Symbol'] < Runtime)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true'])\n    end\n\n    it 'with parameters is not assignable from the default Runtime type' do\n      code = <<-CODE\n      notice(Runtime < Runtime[ruby, 'Symbol'])\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['false'])\n    end\n\n    it 'default is assignable to itself' do\n      code = <<-CODE\n      notice(Runtime < Runtime)\n      notice(Runtime <= Runtime)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['false', 'true'])\n    end\n  end\n\n  context 'Type aliases' do\n    it 'will resolve nested objects using self recursion' do\n      code = <<-CODE\n      type Tree = Hash[String,Variant[String,Tree]]\n      notice({a => {b => {c => d}}} =~ Tree)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true'])\n    end\n\n    it 'will find mismatches using self recursion' do\n      code = <<-CODE\n      type Tree = Hash[String,Variant[String,Tree]]\n      notice({a => {b => {c => 1}}} =~ Tree)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['false'])\n    end\n\n    it 'will not allow an alias chain to only contain aliases' do\n      code = <<-CODE\n      type Foo = Bar\n      type Fee = Foo\n      type Bar = Fee\n      notice(0 =~ Bar)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Type alias 'Bar' cannot be resolved to a real type/)\n    end\n\n    it 'will not allow an alias chain that contains nothing but aliases and variants' do\n      code = <<-CODE\n      type Foo = Bar\n      type Fee = Foo\n      type Bar = Variant[Fee,Foo]\n      notice(0 =~ Bar)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Type alias 'Bar' cannot be resolved to a real type/)\n    end\n\n    it 'will not allow an alias to directly reference itself' do\n      code = <<-CODE\n      type Foo = Foo\n      notice(0 =~ Foo)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Type alias 'Foo' cannot be resolved to a real type/)\n    end\n\n    it 'will allow an alias to directly reference itself in a variant with other types' do\n      code = <<-CODE\n      type Foo = Variant[Foo,String]\n      notice(a =~ Foo)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true'])\n    end\n\n    it 'will allow an alias where a variant references an alias with a variant that references itself' do\n      code = <<-CODE\n      type X = Variant[Y, Integer]\n      type Y = Variant[X, String]\n\n      notice(X >= X)\n      notice(X >= Y)\n      notice(Y >= X)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true','true','true'])\n    end\n\n    it 'will detect a mismatch in an alias that directly references itself in a variant with other types' do\n      code = <<-CODE\n      type Foo = Variant[Foo,String]\n      notice(3 =~ Foo)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['false'])\n    end\n\n    it 'will normalize a Variant containing a self reference so that the self reference is removed' do\n      code = <<-CODE\n      type Foo = Variant[Foo,String,Integer]\n      assert_type(Foo, /x/)\n      CODE\n      expect { eval_and_collect_notices(code) }.to raise_error(/expects a Foo = Variant\\[String, Integer\\] value, got Regexp/)\n    end\n\n    it 'will handle a scalar correctly in combinations of nested aliased variants' do\n      code = <<-CODE\n      type Bar = Variant[Foo,Integer]\n      type Foo = Variant[Bar,String]\n      notice(a =~ Foo)\n      notice(1 =~ Foo)\n      notice(/x/ =~ Foo)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true', 'false'])\n    end\n\n    it 'will handle a non scalar correctly in combinations of nested aliased array with nested variants' do\n      code = <<-CODE\n      type Bar = Variant[Foo,Integer]\n      type Foo = Array[Variant[Bar,String]]\n      notice([a] =~ Foo)\n      notice([1] =~ Foo)\n      notice([/x/] =~ Foo)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true', 'false'])\n    end\n\n    it 'will handle a non scalar correctly in combinations of nested aliased variants with array' do\n      code = <<-CODE\n      type Bar = Variant[Foo,Array[Integer]]\n      type Foo = Variant[Bar,Array[String]]\n      notice([a] =~ Foo)\n      notice([1] =~ Foo)\n      notice([/x/] =~ Foo)\n      CODE\n      expect(eval_and_collect_notices(code)).to eq(['true', 'true', 'false'])\n    end\n\n    it 'will not allow dynamic constructs in type definition' do\n      code = <<-CODE\n      type Foo = Enum[$facts[os][family]]\n      notice(Foo)\n      CODE\n      expect{ eval_and_collect_notices(code) }.to raise_error(Puppet::Error,\n        /The expression <\\$facts\\[os\\]\\[family\\]> is not a valid type specification/)\n    end\n  end\n\n  context 'Type mappings' do\n    it 'can register a singe type mapping' do\n      source = <<-CODE\n        type MyModule::ImplementationRegistry = Object[{}]\n        type Runtime[ruby, 'Puppet::Pops::Types::ImplementationRegistry'] = MyModule::ImplementationRegistry\n        notice(true)\n      CODE\n      collect_notices(source) do |compiler|\n        compiler.compile do |catalog|\n          type = Loaders.implementation_registry.type_for_module(ImplementationRegistry)\n          expect(type).to be_a(PObjectType)\n          expect(type.name).to eql('MyModule::ImplementationRegistry')\n          catalog\n        end\n      end\n    end\n\n    it 'can register a regexp based mapping' do\n      source = <<-CODE\n        type MyModule::TypeMismatchDescriber = Object[{}]\n        type Runtime[ruby, [/^Puppet::Pops::Types::(\\\\w+)$/, 'MyModule::\\\\1']] = [/^MyModule::(\\\\w+)$/, 'Puppet::Pops::Types::\\\\1']\n        notice(true)\n      CODE\n      collect_notices(source) do |compiler|\n        compiler.compile do |catalog|\n          type = Loaders.implementation_registry.type_for_module(TypeMismatchDescriber)\n          expect(type).to be_a(PObjectType)\n          expect(type.name).to eql('MyModule::TypeMismatchDescriber')\n          catalog\n        end\n      end\n    end\n\n    it 'a type mapping affects type inference' do\n      source = <<-CODE\n        type MyModule::ImplementationRegistry = Object[{}]\n        type Runtime[ruby, 'Puppet::Pops::Types::ImplementationRegistry'] = MyModule::ImplementationRegistry\n        notice(true)\n      CODE\n      collect_notices(source) do |compiler|\n        compiler.compile do |catalog|\n          type = TypeCalculator.singleton.infer(Loaders.implementation_registry)\n          expect(type).to be_a(PObjectType)\n          expect(type.name).to eql('MyModule::ImplementationRegistry')\n          catalog\n        end\n      end\n    end\n  end\n\n  context 'When attempting to redefine a built in type' do\n    it 'such as Integer, an error is raised' do\n      code = <<-CODE\n        type Integer = String\n        notice 'hello' =~ Integer\n      CODE\n      expect{ eval_and_collect_notices(code) }.to raise_error(/Attempt to redefine entity 'http:\\/\\/puppet\\.com\\/2016\\.1\\/runtime\\/type\\/integer'. Originally set by Puppet-Type-System\\/Static-Loader/)\n    end\n  end\n\n  context 'instantiation via new_function is supported by' do\n    let(:loader) { Loader::BaseLoader.new(nil, \"types_unit_test_loader\") }\n    it 'Integer' do\n      func_class = tf.integer.new_function\n      expect(func_class).to be_a(Class)\n      expect(func_class.superclass).to be(Puppet::Functions::Function)\n    end\n\n    it 'Optional[Integer]' do\n      func_class = tf.optional(tf.integer).new_function\n      expect(func_class).to be_a(Class)\n      expect(func_class.superclass).to be(Puppet::Functions::Function)\n    end\n\n    it 'Regexp' do\n      func_class = tf.regexp.new_function\n      expect(func_class).to be_a(Class)\n      expect(func_class.superclass).to be(Puppet::Functions::Function)\n    end\n  end\n\n  context 'instantiation via new_function is not supported by' do\n    let(:loader) { Loader::BaseLoader.new(nil, \"types_unit_test_loader\") }\n\n      it 'Any, Scalar, Collection' do\n        [tf.any, tf.scalar, tf.collection ].each do |t|\n        expect { t.new_function\n        }.to raise_error(ArgumentError, /Creation of new instance of type '#{t.to_s}' is not supported/)\n      end\n    end\n  end\n\n  context 'instantiation via ruby create function' do\n    before(:each) do\n      Puppet.push_context(:loaders => Loaders.new(Puppet::Node::Environment.create(:testing, []))) do\n      end\n    end\n\n    after(:each) do\n      Puppet.pop_context()\n    end\n\n    it 'is supported by Integer' do\n      int = tf.integer.create('32')\n      expect(int).to eq(32)\n    end\n\n    it 'is supported by Regexp' do\n      rx = tf.regexp.create('[a-z]+')\n      expect(rx).to eq(/[a-z]+/)\n    end\n\n    it 'is supported by Optional[Integer]' do\n      int = tf.optional(tf.integer).create('32')\n      expect(int).to eq(32)\n    end\n\n    it 'is not supported by Any, Scalar, Collection' do\n      [tf.any, tf.scalar, tf.collection ].each do |t|\n        expect { t.create }.to raise_error(ArgumentError, /Creation of new instance of type '#{t.to_s}' is not supported/)\n      end\n    end\n  end\n\n  context 'creation of parameterized type via ruby create function on class' do\n    before(:each) do\n      Puppet.push_context(:loaders => Loaders.new(Puppet::Node::Environment.create(:testing, []))) do\n      end\n    end\n\n    after(:each) do\n      Puppet.pop_context()\n    end\n\n    it 'is supported by Integer' do\n      int_type = tf.integer.class.create(0, 32)\n      expect(int_type).to eq(tf.range(0, 32))\n    end\n\n    it 'is supported by Regexp' do\n      rx_type = tf.regexp.class.create('[a-z]+')\n      expect(rx_type).to eq(tf.regexp(/[a-z]+/))\n    end\n  end\n\n  context 'backward compatibility' do\n    it 'PTypeType can be accessed from PType' do\n      # should appoint the exact same instance\n      expect(PType).to equal(PTypeType)\n    end\n\n    it 'PClassType can be accessed from PHostClassType' do\n      # should appoint the exact same instance\n      expect(PHostClassType).to equal(PClassType)\n    end\n  end\nend\nend\nend\n"
  },
  {
    "path": "spec/unit/pops/utils_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe 'pops utils' do\n  context 'when converting strings to numbers' do\n    it 'should convert \"0\" to 0' do\n      expect(Puppet::Pops::Utils.to_n(\"0\")).to eq(0)\n    end\n\n    it 'should convert \"0\" to 0 with radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0\")).to eq([0, 10])\n    end\n\n    it 'should convert \"0.0\" to 0.0' do\n      expect(Puppet::Pops::Utils.to_n(\"0.0\")).to eq(0.0)\n    end\n\n    it 'should convert \"0.0\" to 0.0 with radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0.0\")).to eq([0.0, 10])\n    end\n\n    it 'should convert \"0.01e1\" to 0.01e1' do\n      expect(Puppet::Pops::Utils.to_n(\"0.01e1\")).to eq(0.01e1)\n      expect(Puppet::Pops::Utils.to_n(\"0.01E1\")).to eq(0.01e1)\n    end\n\n    it 'should convert \"0.01e1\" to 0.01e1 with radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0.01e1\")).to eq([0.01e1, 10])\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0.01E1\")).to eq([0.01e1, 10])\n    end\n\n    it 'should not convert \"0e1\" to floating point' do\n      expect(Puppet::Pops::Utils.to_n(\"0e1\")).to be_nil\n      expect(Puppet::Pops::Utils.to_n(\"0E1\")).to be_nil\n    end\n\n    it 'should not convert \"0e1\" to floating point with radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0e1\")).to be_nil\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0E1\")).to be_nil\n    end\n\n    it 'should not convert \"0.0e1\" to floating point' do\n      expect(Puppet::Pops::Utils.to_n(\"0.0e1\")).to be_nil\n      expect(Puppet::Pops::Utils.to_n(\"0.0E1\")).to be_nil\n    end\n\n    it 'should not convert \"0.0e1\" to floating point with radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0.0e1\")).to be_nil\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"0.0E1\")).to be_nil\n    end\n\n    it 'should not convert \"000000.0000e1\" to floating point' do\n      expect(Puppet::Pops::Utils.to_n(\"000000.0000e1\")).to be_nil\n      expect(Puppet::Pops::Utils.to_n(\"000000.0000E1\")).to be_nil\n    end\n\n    it 'should not convert \"000000.0000e1\" to floating point with radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"000000.0000e1\")).to be_nil\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"000000.0000E1\")).to be_nil\n    end\n\n    it 'should not convert infinite values to floating point' do\n      expect(Puppet::Pops::Utils.to_n(\"4e999\")).to be_nil\n    end\n\n    it 'should not convert infinite values to floating point with_radix' do\n      expect(Puppet::Pops::Utils.to_n_with_radix(\"4e999\")).to be_nil\n    end\n  end\nend"
  },
  {
    "path": "spec/unit/pops/validation_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe 'Puppet::Pops::Validation::Diagnostic' do\n\n  # Mocks a SourcePosAdapter as it is used in these use cases\n  # of a Diagnostic\n  #\n  class MockSourcePos\n    attr_reader :offset\n    def initialize(offset)\n      @offset = offset\n    end\n  end\n\n  it \"computes equal hash value ignoring arguments\" do\n    issue = Puppet::Pops::Issues::DIV_BY_ZERO\n    source_pos = MockSourcePos.new(10)\n    d1 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos, {:foo => 10})\n    d2 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos.clone, {:bar => 20})\n    expect(d1.hash).to eql(d2.hash)\n  end\n\n  it \"computes non equal hash value for different severities\" do\n    issue = Puppet::Pops::Issues::DIV_BY_ZERO\n    source_pos = MockSourcePos.new(10)\n    d1 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos, {})\n    d2 = Puppet::Pops::Validation::Diagnostic.new(:error, issue, \"foo\", source_pos.clone, {})\n    expect(d1.hash).to_not eql(d2.hash)\n  end\n\n  it \"computes non equal hash value for different offsets\" do\n    issue = Puppet::Pops::Issues::DIV_BY_ZERO\n    source_pos1 = MockSourcePos.new(10)\n    source_pos2 = MockSourcePos.new(11)\n    d1 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos1, {})\n    d2 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos2, {})\n    expect(d1.hash).to_not eql(d2.hash)\n  end\n\n  it \"can be used in a set\" do\n    the_set = Set.new()\n    issue = Puppet::Pops::Issues::DIV_BY_ZERO\n    source_pos = MockSourcePos.new(10)\n    d1 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos, {})\n    d2 = Puppet::Pops::Validation::Diagnostic.new(:warning, issue, \"foo\", source_pos.clone, {})\n    d3 = Puppet::Pops::Validation::Diagnostic.new(:error, issue, \"foo\", source_pos.clone, {})\n    expect(the_set.add?(d1)).to_not be_nil\n    expect(the_set.add?(d2)).to be_nil\n    expect(the_set.add?(d3)).to_not be_nil\n  end\n\nend\n\ndescribe \"Puppet::Pops::Validation::SeverityProducer\" do\n  it 'sets default severity given in initializer' do\n    producer = Puppet::Pops::Validation::SeverityProducer.new(:warning)\n    expect(producer.severity(Puppet::Pops::Issues::DIV_BY_ZERO)).to be(:warning)\n  end\n\n  it 'sets default severity to :error if not given' do\n    producer = Puppet::Pops::Validation::SeverityProducer.new()\n    expect(producer.severity(Puppet::Pops::Issues::DIV_BY_ZERO)).to be(:error)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/pops/validator/validator_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\nrequire 'puppet_spec/pops'\nrequire_relative '../parser/parser_rspec_helper'\n\ndescribe \"validating 4x\" do\n  include ParserRspecHelper\n  include PuppetSpec::Pops\n\n  let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() }\n  let(:validator) { Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) }\n  let(:environment) { Puppet::Node::Environment.create(:bar, ['path']) }\n\n  def validate(factory)\n    validator.validate(factory.model)\n    acceptor\n  end\n\n  def deprecation_count(acceptor)\n    acceptor.diagnostics.select {|d| d.severity == :deprecation }.count\n  end\n\n  def with_environment(environment, env_params = {})\n    override_env = environment\n    override_env = environment.override_with({\n      modulepath:     env_params[:modulepath]     || environment.full_modulepath,\n      manifest:       env_params[:manifest]       || environment.manifest,\n      config_version: env_params[:config_version] || environment.config_version\n    }) if env_params.count > 0\n    Puppet.override(current_environment: override_env) do\n      yield\n    end\n  end\n\n  it 'should raise error for illegal class names' do\n    expect(validate(parse('class aaa::_bbb {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n    expect(validate(parse('class Aaa {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n    expect(validate(parse('class ::aaa {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n  end\n\n  it 'should raise error for illegal define names' do\n    expect(validate(parse('define aaa::_bbb {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n    expect(validate(parse('define Aaa {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n    expect(validate(parse('define ::aaa {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n  end\n\n  it 'should raise error for illegal function names' do\n    expect(validate(parse('function aaa::_bbb() {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n    expect(validate(parse('function Aaa() {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n    expect(validate(parse('function ::aaa() {}'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n  end\n\n  it 'should raise error for illegal definition locations' do\n    with_environment(environment) do\n      expect(validate(parse('function aaa::ccc() {}', 'path/aaa/manifests/bbb.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('class bbb() {}', 'path/aaa/manifests/init.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('define aaa::bbb::ccc::eee() {}', 'path/aaa/manifests/bbb/ddd.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for legal definition locations' do\n    with_environment(environment) do\n      expect(validate(parse('function aaa::bbb() {}',      'path/aaa/manifests/bbb.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('class aaa() {}',      'path/aaa/manifests/init.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('function aaa::bbB::ccc() {}', 'path/aaa/manifests/bBb.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('function aaa::bbb::ccc() {}', 'path/aaa/manifests/bbb/CCC.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for class locations when not parsing a file' do\n    #nil/'' file means eval or some other way to get puppet language source code into the catalog\n    with_environment(environment) do\n      expect(validate(parse('function aaa::ccc() {}', nil))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('function aaa::ccc() {}', ''))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for definitions inside initial --manifest file' do\n    with_environment(environment, :manifest => 'a/manifest/file.pp') do\n      expect(validate(parse('class aaa() {}', 'a/manifest/file.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for definitions inside initial --manifest directory' do\n    with_environment(environment, :manifest => 'a/manifest/dir') do\n      expect(validate(parse('class aaa() {}', 'a/manifest/dir/file1.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('class bbb::ccc::ddd() {}', 'a/manifest/dir/and/more/stuff.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for definitions not inside initial --manifest but also not in modulepath' do\n    with_environment(environment, :manifest => 'a/manifest/somewhere/else') do\n      expect(validate(parse('class aaa() {}', 'a/random/dir/file1.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for empty files in modulepath' do\n    with_environment(environment) do\n      expect(validate(parse('', 'path/aaa/manifests/init.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n      expect(validate(parse('#this is a comment', 'path/aaa/manifests/init.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n    end\n  end\n\n  it 'should raise error if the file is in the modulepath but is not well formed' do\n    with_environment(environment) do\n      expect(validate(parse('class aaa::bbb::ccc() {}', 'path/manifest/aaa/bbb.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      expect(validate(parse('class aaa::bbb::ccc() {}', 'path/aaa/bbb/manifest/ccc.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error for definitions not inside initial --manifest but also not in modulepath because of only a case difference' do\n    with_environment(environment) do\n        expect(validate(parse('class aaa::bb() {}', 'Path/aaa/manifests/ccc.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error when one modulepath is a substring of another' do\n    with_environment(environment, modulepath: ['path', 'pathplus']) do\n      expect(validate(parse('class aaa::ccc() {}', 'pathplus/aaa/manifests/ccc.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should not raise error when a modulepath ends with a file separator' do\n    with_environment(environment, modulepath: ['path/']) do\n      expect(validate(parse('class aaa::ccc() {}', 'pathplus/aaa/manifests/ccc.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n    end\n  end\n\n  it 'should raise error for illegal type names' do\n    expect(validate(parse('type ::Aaa = Any'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_NAME)\n  end\n\n  it 'should raise error for illegal variable names' do\n    expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)\n    expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)\n    expect(validate(fqn('aaa::_aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)\n  end\n\n  it 'should not raise error for variable name with underscore first in first name segment' do\n    expect(validate(fqn('_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)\n    expect(validate(fqn('::_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)\n  end\n\n  context 'with the default settings for --strict' do\n    it 'produces an error for duplicate keys in a literal hash' do\n      acceptor = validate(parse('{ a => 1, a => 2 }'))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_KEY)\n    end\n\n    it 'produces an error for illegal function locations' do\n      with_environment(environment) do\n        acceptor = validate(parse('function aaa::ccc() {}', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      end\n    end\n\n    it 'produces an error for illegal top level constructs' do\n      with_environment(environment) do\n        acceptor = validate(parse('$foo = 1', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n      end\n    end\n  end\n\n  context 'with --strict set to warning' do\n    before(:each) { Puppet[:strict] = :warning }\n    it 'produces a warning for duplicate keys in a literal hash' do\n      acceptor = validate(parse('{ a => 1, a => 2 }'))\n      expect(acceptor.warning_count).to eql(1)\n      expect(acceptor.error_count).to eql(0)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_KEY)\n    end\n\n    it 'produces an error for virtual class resource' do\n      acceptor = validate(parse('@class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'produces an error for exported class resource' do\n      acceptor = validate(parse('@@class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'produces an error for illegal function locations' do\n      with_environment(environment) do\n        acceptor = validate(parse('function aaa::ccc() {}', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      end\n    end\n\n    it 'produces an error for illegal top level constructs' do\n      with_environment(environment) do\n        acceptor = validate(parse('$foo = 1', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n      end\n    end\n  end\n\n  context 'with --strict set to error' do\n    before(:each) { Puppet[:strict] = :error }\n    it 'produces an error for duplicate keys in a literal hash' do\n      acceptor = validate(parse('{ a => 1, a => 2 }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_KEY)\n    end\n\n    it 'produces an error for virtual class resource' do\n      acceptor = validate(parse('@class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'does not produce an error for regular class resource' do\n      acceptor = validate(parse('class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(0)\n      expect(acceptor).not_to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'produces an error for exported class resource' do\n      acceptor = validate(parse('@@class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'produces an error for illegal function locations' do\n      with_environment(environment) do\n        acceptor = validate(parse('function aaa::ccc() {}', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      end\n    end\n\n    it 'produces an error for illegal top level constructs' do\n      with_environment(environment) do\n        acceptor = validate(parse('$foo = 1', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n      end\n    end\n  end\n\n  context 'with --strict set to off' do\n    before(:each) { Puppet[:strict] = :off }\n    it 'does not produce an error or warning for duplicate keys in a literal hash' do\n      acceptor = validate(parse('{ a => 1, a => 2 }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(0)\n      expect(acceptor).to_not have_issue(Puppet::Pops::Issues::DUPLICATE_KEY)\n    end\n\n    it 'produces an error for illegal function locations' do\n      with_environment(environment) do\n        acceptor = validate(parse('function aaa::ccc() {}', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      end\n    end\n\n    it 'produces an error for illegal top level constructs' do\n      with_environment(environment) do\n        acceptor = validate(parse('$foo = 1', 'path/aaa/manifests/bbb.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n      end\n    end\n  end\n\n  context 'irrespective of --strict' do\n    it 'produces an error for duplicate default in a case expression' do\n      acceptor = validate(parse('case 1 { default: {1} default : {2} }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_DEFAULT)\n    end\n\n    it 'produces an error for duplicate default in a selector expression' do\n      acceptor = validate(parse(' 1 ? { default => 1, default => 2 }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_DEFAULT)\n    end\n\n    it 'produces an error for virtual class resource' do\n      acceptor = validate(parse('@class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'produces an error for exported class resource' do\n      acceptor = validate(parse('@@class { test: }'))\n      expect(acceptor.warning_count).to eql(0)\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)\n    end\n\n    it 'produces a deprecation warning for non-literal class parameters' do\n      acceptor = validate(parse('class test(Integer[2-1] $port) {}'))\n      expect(deprecation_count(acceptor)).to eql(1)\n      expect(acceptor.warning_count).to eql(1)\n      expect(acceptor.error_count).to eql(0)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::ILLEGAL_NONLITERAL_PARAMETER_TYPE)\n    end\n  end\n\n  context 'with --tasks set' do\n    before(:each) { Puppet[:tasks] = true }\n\n    it 'raises an error for illegal plan names' do\n      with_environment(environment) do\n        expect(validate(parse('plan aaa::ccc::eee() {}', 'path/aaa/plans/bbb/ccc/eee.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n        expect(validate(parse('plan aaa() {}', 'path/aaa/plans/aaa.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n        expect(validate(parse('plan aaa::bbb() {}', 'path/aaa/plans/bbb/bbb.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      end\n    end\n\n    it 'accepts legal plan names' do\n      with_environment(environment) do\n        expect(validate(parse('plan aaa::ccc::eee() {}', 'path/aaa/plans/ccc/eee.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n        expect(validate(parse('plan aaa() {}', 'path/aaa/plans/init.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n        expect(validate(parse('plan aaa::bbb() {}', 'path/aaa/plans/bbb.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_DEFINITION_LOCATION)\n      end\n    end\n\n    it 'produces an error for collect expressions with virtual query' do\n      acceptor = validate(parse(\"User <| title == 'admin' |>\"))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for collect expressions with exported query' do\n      acceptor = validate(parse(\"User <<| title == 'admin' |>>\"))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for class expressions' do\n      acceptor = validate(parse('class test {}'))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for node expressions' do\n      acceptor = validate(parse('node default {}'))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for relationship expressions' do\n      acceptor = validate(parse('$x -> $y'))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for resource expressions' do\n      acceptor = validate(parse('notify { nope: }'))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for resource default expressions' do\n      acceptor = validate(parse(\"File { mode => '0644' }\"))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for resource override expressions' do\n      acceptor = validate(parse(\"File['/tmp/foo'] { mode => '0644' }\"))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    it 'produces an error for resource definitions' do\n      acceptor = validate(parse('define foo($a) {}'))\n      expect(acceptor.error_count).to eql(1)\n      expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n    end\n\n    context 'validating apply() blocks' do\n      it 'allows empty apply() blocks' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'allows apply() with a single expression' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { include foo }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'allows apply() with multiple expressions' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { $message = \"hello!\"; notify { $message: } }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'allows apply to be used as a resource attribute name' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { sometype { \"resourcetitle\": apply => \"applyvalue\" } }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'accepts multiple arguments' do\n        acceptor = validate(parse('apply([\"foo.example.com\"], { \"other\" => \"args\" }) { }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'allows virtual resource collectors' do\n        acceptor = validate(parse(\"apply('foo.example.com') { @user { 'foo': }; User <| title == 'foo' |> }\"))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'rejects exported resource collectors' do\n        acceptor = validate(parse(\"apply('foo.example.com') { @@user { 'foo': }; User <<| title == 'foo' |>> }\"))\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_COMPILING)\n      end\n\n      it 'allows relationship expressions' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { $x -> $y }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'allows resource default expressions' do\n        acceptor = validate(parse(\"apply('foo.example.com') { File { mode => '0644' } }\"))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'allows resource override expressions' do\n        acceptor = validate(parse(\"apply('foo.example.com') { File['/tmp/foo'] { mode => '0644' } }\"))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'can be assigned' do\n        acceptor = validate(parse('$result = apply(\"foo.example.com\") { notify { \"hello!\": } }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'produces an error for class expressions' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { class test {} }'))\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n      end\n\n      it 'allows node expressions' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { node default {} }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n\n      it 'produces an error for node expressions nested in a block' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { if true { node default {} } }'))\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::NOT_TOP_LEVEL)\n      end\n\n      it 'produces an error for resource definitions' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { define foo($a) {} }'))\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_SCRIPTING)\n      end\n\n      it 'produces an error for apply() inside apply()' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { apply(\"foo.example.com\") { } }'))\n        expect(acceptor.error_count).to eql(1)\n        expect(acceptor).to have_issue(Puppet::Pops::Issues::EXPRESSION_NOT_SUPPORTED_WHEN_COMPILING)\n      end\n\n      it 'allows multiple consecutive apply() blocks' do\n        acceptor = validate(parse('apply(\"foo.example.com\") { } apply(\"foo.example.com\") { }'))\n        expect(acceptor.error_count).to eql(0)\n      end\n    end\n  end\n\n  context 'for non productive expressions' do\n    [ '1',\n      '3.14',\n      \"'a'\",\n      '\"a\"',\n      '\"${$a=10}\"', # interpolation with side effect\n      'false',\n      'true',\n      'default',\n      'undef',\n      '[1,2,3]',\n      '{a=>10}',\n      'if 1 {2}',\n      'if 1 {2} else {3}',\n      'if 1 {2} elsif 3 {4}',\n      'unless 1 {2}',\n      'unless 1 {2} else {3}',\n      '1 ? 2 => 3',\n      '1 ? { 2 => 3}',\n      '-1',\n      '-foo()', # unary minus on productive\n      '1+2',\n      '1<2',\n      '(1<2)',\n      '!true',\n      '!foo()', # not on productive\n      '$a',\n      '$a[1]',\n      'name',\n      'Type',\n      'Type[foo]'\n      ].each do |expr|\n      it \"produces error for non productive: #{expr}\" do\n        source = \"#{expr}; $a = 10\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::IDEM_EXPRESSION_NOT_LAST)\n      end\n\n      it \"does not produce error when last for non productive: #{expr}\" do\n        source = \" $a = 10; #{expr}\"\n        expect(validate(parse(source))).to_not have_issue(Puppet::Pops::Issues::IDEM_EXPRESSION_NOT_LAST)\n      end\n    end\n\n    [\n      'if 1 {$a = 1}',\n      'if 1 {2} else {$a=1}',\n      'if 1 {2} elsif 3 {$a=1}',\n      'unless 1 {$a=1}',\n      'unless 1 {2} else {$a=1}',\n      '$a = 1 ? 2 => 3',\n      '$a = 1 ? { 2 => 3}',\n      'Foo[a] -> Foo[b]',\n      '($a=1)',\n      'foo()',\n      '$a.foo()',\n      '\"foo\" =~ /foo/', # may produce or modify $n vars\n      '\"foo\" !~ /foo/', # may produce or modify $n vars\n      ].each do |expr|\n\n      it \"does not produce error when for productive: #{expr}\" do\n        source = \"#{expr}; $x = 1\"\n        expect(validate(parse(source))).to_not have_issue(Puppet::Pops::Issues::IDEM_EXPRESSION_NOT_LAST)\n      end\n    end\n\n    ['class', 'define', 'node'].each do |type|\n      it \"flags non productive expression last in #{type}\" do\n        source = <<-SOURCE\n          #{type} nope {\n            1\n          }\n          end\n        SOURCE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::IDEM_NOT_ALLOWED_LAST)\n      end\n\n      it \"detects a resource declared without title in #{type} when it is the only declaration present\" do\n        source = <<-SOURCE\n          #{type} nope {\n            notify { message => 'Nope' }\n          }\n        SOURCE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESOURCE_WITHOUT_TITLE)\n      end\n\n      it \"detects a resource declared without title in #{type} when it is in between other declarations\" do\n        source = <<-SOURCE\n        #{type} nope {\n            notify { succ: message => 'Nope' }\n            notify { message => 'Nope' }\n            notify { pred: message => 'Nope' }\n          }\n        SOURCE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESOURCE_WITHOUT_TITLE)\n      end\n\n      it \"detects a resource declared without title in #{type} when it is declarated first\" do\n        source = <<-SOURCE\n          #{type} nope {\n            notify { message => 'Nope' }\n            notify { pred: message => 'Nope' }\n          }\n        SOURCE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESOURCE_WITHOUT_TITLE)\n      end\n\n      it \"detects a resource declared without title in #{type} when it is declarated last\" do\n        source = <<-SOURCE\n          #{type} nope {\n            notify { succ: message => 'Nope' }\n            notify { message => 'Nope' }\n          }\n        SOURCE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESOURCE_WITHOUT_TITLE)\n      end\n    end\n  end\n\n  context 'for reserved words' do\n    ['private', 'attr'].each do |word|\n      it \"produces an error for the word '#{word}'\" do\n        source = \"$a = #{word}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_WORD)\n      end\n    end\n  end\n\n  context 'for reserved type names' do\n    [# type/Type, is a reserved name but results in syntax error because it is a keyword in lower case form\n    'any',\n    'unit',\n    'scalar',\n    'boolean',\n    'numeric',\n    'integer',\n    'float',\n    'collection',\n    'array',\n    'hash',\n    'tuple',\n    'struct',\n    'variant',\n    'optional',\n    'enum',\n    'regexp',\n    'pattern',\n    'runtime',\n    'init',\n    'object',\n    'sensitive',\n    'semver',\n    'semverrange',\n    'string',\n    'timestamp',\n    'timespan',\n    'typeset',\n    ].each do |name|\n\n      it \"produces an error for 'class #{name}'\" do\n        source = \"class #{name} {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_TYPE_NAME)\n      end\n\n      it \"produces an error for 'define #{name}'\" do\n        source = \"define #{name} {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_TYPE_NAME)\n      end\n    end\n  end\n\n  context 'for keywords' do\n    it \"should allow using the 'type' as the name of a function with no parameters\" do\n      source = \"type()\"\n      expect(validate(parse(source))).not_to have_any_issues\n    end\n\n    it \"should allow using the keyword 'type' as the name of a function with parameters\" do\n      source = \"type('a', 'b')\"\n      expect(validate(parse(source))).not_to have_any_issues\n    end\n\n    it \"should allow using the 'type' as the name of a function with no parameters and a block\" do\n      source = \"type() |$x| { $x }\"\n      expect(validate(parse(source))).not_to have_any_issues\n    end\n\n    it \"should allow using the keyword 'type' as the name of a function with parameters and a block\" do\n      source = \"type('a', 'b') |$x| { $x }\"\n      expect(validate(parse(source))).not_to have_any_issues\n    end\n  end\n\n  context 'for hash keys' do\n    it \"should not allow reassignment of hash keys\" do\n      source = \"$my_hash = {'one' => '1', 'two' => '2' }; $my_hash['one']='1.5'\"\n      expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_INDEXED_ASSIGNMENT)\n    end\n  end\n\n  context 'for parameter names' do\n    ['class', 'define'].each do |word|\n      it \"should require that #{word} parameter names are unique\" do\n        expect(validate(parse(\"#{word} foo($a = 10, $a = 20) {}\"))).to have_issue(Puppet::Pops::Issues::DUPLICATE_PARAMETER)\n      end\n    end\n\n    it \"should require that template parameter names are unique\" do\n      expect(validate(parse_epp(\"<%-| $a, $a |-%><%= $a == doh %>\"))).to have_issue(Puppet::Pops::Issues::DUPLICATE_PARAMETER)\n    end\n  end\n\n  context 'for parameter defaults' do\n    ['class', 'define'].each do |word|\n      it \"should not permit assignments in #{word} parameter default expressions\" do\n        expect { parse(\"#{word} foo($a = $x = 10) {}\") }.to raise_error(Puppet::ParseErrorWithIssue, /Syntax error at '='/)\n      end\n    end\n\n    ['class', 'define'].each do |word|\n      it \"should not permit assignments in #{word} parameter default nested expressions\" do\n        expect(validate(parse(\"#{word} foo($a = [$x = 10]) {}\"))).to have_issue(Puppet::Pops::Issues::ILLEGAL_ASSIGNMENT_CONTEXT)\n      end\n\n      it \"should not permit assignments to subsequently declared parameters in #{word} parameter default nested expressions\" do\n        expect(validate(parse(\"#{word} foo($a = ($b = 3), $b = 5) {}\"))).to have_issue(Puppet::Pops::Issues::ILLEGAL_ASSIGNMENT_CONTEXT)\n      end\n\n      it \"should not permit assignments to previously declared parameters in #{word} parameter default nested expressions\" do\n        expect(validate(parse(\"#{word} foo($a = 10, $b = ($a = 10)) {}\"))).to have_issue(Puppet::Pops::Issues::ILLEGAL_ASSIGNMENT_CONTEXT)\n      end\n\n      it \"should permit assignments in #{word} parameter default inside nested lambda expressions\" do\n        expect(validate(parse(\n          \"#{word} foo($a = [1,2,3], $b = 0, $c = $a.map |$x| { $b = $x; $b * $a.reduce |$x, $y| {$x + $y}}) {}\"))).not_to(\n          have_issue(Puppet::Pops::Issues::ILLEGAL_ASSIGNMENT_CONTEXT))\n      end\n    end\n  end\n\n  context 'for reserved parameter names' do\n    ['name', 'title'].each do |word|\n      it \"produces an error when $#{word} is used as a parameter in a class\" do\n        source = \"class x ($#{word}) {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_PARAMETER)\n      end\n\n      it \"produces an error when $#{word} is used as a parameter in a define\" do\n        source = \"define x ($#{word}) {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_PARAMETER)\n      end\n    end\n\n  end\n\n  context 'for numeric parameter names' do\n    ['1', '0x2', '03'].each do |word|\n      it \"produces an error when $#{word} is used as a parameter in a class\" do\n        source = \"class x ($#{word}) {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NUMERIC_PARAMETER)\n      end\n    end\n  end\n\n  context 'for badly formed non-numeric parameter names' do\n    ['Ateam', 'a::team'].each do |word|\n      it \"produces an error when $#{word} is used as a parameter in a class\" do\n        source = \"class x ($#{word}) {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_PARAM_NAME)\n      end\n\n      it \"produces an error when $#{word} is used as a parameter in a define\" do\n        source = \"define x ($#{word}) {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_PARAM_NAME)\n      end\n\n      it \"produces an error when $#{word} is used as a parameter in a lambda\" do\n        source = \"with() |$#{word}| {}\"\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_PARAM_NAME)\n      end\n    end\n  end\n\n  context 'top level constructs' do\n    def issue(at_top)\n      at_top ? Puppet::Pops::Issues::NOT_ABSOLUTE_TOP_LEVEL : Puppet::Pops::Issues::NOT_TOP_LEVEL\n    end\n\n    # Top level. Defines the expressions that are tested inside of other things\n    {\n      'a class' => ['class x{}', false],\n      'a define' => ['define x{}', false],\n      'a node' => ['node x{}', false],\n      'a function' => ['function x() {}', true],\n      'a type alias' => ['type A = Data', true],\n      'a type alias for a complex type' => ['type C = Hash[String[1],Integer]', true],\n      'a type definition' => ['type A {}', true]\n    }.each_pair do |word, (decl, at_top)|\n      # Nesting level. Defines how each of the top level expressions are nested in\n      # another expression\n      {\n        'a define' => [\"define y{ #{decl} }\", at_top],\n        'a function' => [\"function y() { #{decl} }\", at_top],\n        'a type definition' => [\"type A { #{decl} }\", at_top],\n        'an if expression' => [\"if true { #{decl} }\", false],\n        'an if-else expression' => [\"if false {} else { #{decl} }\", false],\n        'an unless' => [\"unless false { #{decl} }\", false]\n      }.each_pair do |nester, (source, abs_top)|\n        # Tests each top level expression in each nested expression\n        it \"produces an error when #{word} is nested in #{nester}\" do\n          expect(validate(parse(source))).to have_issue(issue(abs_top))\n        end\n      end\n\n      # Test that the expression can exist anywhere in a top level block\n\n      it \"will allow #{word} as the only statement in a top level block\" do\n        expect(validate(parse(decl))).not_to have_issue(issue(at_top))\n      end\n\n      it \"will allow #{word} as the last statement in a top level block\" do\n        source = \"$a = 10\\n#{decl}\"\n        expect(validate(parse(source))).not_to have_issue(issue(at_top))\n      end\n\n      it \"will allow #{word} as the first statement in a top level block\" do\n        source = \"#{decl}\\n$a = 10\"\n        expect(validate(parse(source))).not_to have_issue(issue(at_top))\n      end\n\n      it \"will allow #{word} in between other statements in a top level block\" do\n        source = \"$a = 10\\n#{decl}\\n$b = 20\"\n        expect(validate(parse(source))).not_to have_issue(issue(at_top))\n      end\n    end\n\n    context 'that are type aliases' do\n      it 'raises errors when RHS is a name that is an invalid reference' do\n        source = 'type MyInt = integer'\n        expect { parse(source) }.to raise_error(/Syntax error at 'integer'/)\n      end\n\n      it 'raises errors when RHS is an AccessExpression with a name that is an invalid reference on LHS' do\n        source = 'type IntegerArray = array[Integer]'\n        expect { parse(source) }.to raise_error(/Syntax error at 'array'/)\n      end\n    end\n\n    context 'that are functions' do\n      it 'accepts typed parameters' do\n        source = <<-CODE\n          function f(Integer $a) { $a }\n        CODE\n        expect(validate(parse(source))).not_to have_any_issues\n      end\n\n      it 'accepts return types' do\n        source = <<-CODE\n          function f() >> Integer { 42 }\n        CODE\n        expect(validate(parse(source))).not_to have_any_issues\n      end\n\n      it 'accepts block with return types' do\n        source = <<-CODE\n          map([1,2]) |Integer $x| >> Integer { $x + 3 }\n        CODE\n        expect(validate(parse(source))).not_to have_any_issues\n      end\n    end\n\n    context 'that are type mappings' do\n      it 'accepts a valid type mapping expression' do\n        source = <<-CODE\n          type Runtime[ruby, 'MyModule::MyObject'] = MyPackage::MyObject\n          notice(true)\n        CODE\n        expect(validate(parse(source))).not_to have_any_issues\n      end\n\n      it 'accepts a valid regexp based type mapping expression' do\n        source = <<-CODE\n          type Runtime[ruby, [/^MyPackage::(\\w+)$/, 'MyModule::\\1']] = [/^MyModule::(\\w+)$/, 'MyPackage::\\1']\n          notice(true)\n        CODE\n        expect(validate(parse(source))).not_to have_any_issues\n      end\n\n      it 'raises an error when a regexp based Runtime type is paired with a Puppet Type' do\n        source = <<-CODE\n          type Runtime[ruby, [/^MyPackage::(\\w+)$/, 'MyModule::\\1']] = MyPackage::MyObject\n          notice(true)\n        CODE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_REGEXP_TYPE_MAPPING)\n      end\n\n      it 'raises an error when a singleton Runtime type is paired with replacement pattern' do\n        source = <<-CODE\n          type Runtime[ruby, 'MyModule::MyObject'] = [/^MyModule::(\\w+)$/, 'MyPackage::\\1']\n          notice(true)\n        CODE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_SINGLE_TYPE_MAPPING)\n      end\n\n      it 'raises errors unless LHS is Runtime type' do\n        source = <<-CODE\n          type Pattern[/^MyPackage::(\\w+)$/, 'MyModule::\\1'] = [/^MyModule::(\\w+)$/, 'MyPackage::\\1']\n          notice(true)\n        CODE\n        expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::UNSUPPORTED_EXPRESSION)\n      end\n    end\n  end\n\n  context 'top level constructs' do\n    {\n      'a class' => 'class x{}',\n      'a define' => 'define x{}',\n      'a function' => 'function x() {}',\n      'a type alias' => 'type A = Data',\n      'a type alias for a complex type' => 'type C = Hash[String[1],Integer]',\n      'a type definition' => 'type A {}',\n    }.each_pair do |word, source|\n      it \"will not have an issue with #{word} at the top level in a module\" do\n        with_environment(environment) do\n          expect(validate(parse(source, 'path/x/manifests/init.pp'))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n        end\n      end\n    end\n  end\n\n  context 'non-top level constructs' do\n    {\n      'an assignment' => '$foo = 1',\n      'a resource' => 'notify { nope: }',\n      'a resource default' => \"Notify { message => 'yo' }\",\n      'a function call' => \"include 'foo'\",\n      'a node definition' => 'node default {}',\n      'an expression' => '1+1',\n      'a conditional' => 'if true {42}',\n      'a literal value' => '42',\n      'a virtual collector' => 'User <| tag == web |>',\n      'an exported collector' => 'Sshkey <<| |>>',\n    }.each_pair do |word, source|\n      it \"will have an issue with #{word} at the top level in a module\" do\n        with_environment(environment) do\n          expect(validate(parse(source, 'path/x/manifests/init.pp'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n        end\n      end\n\n      it \"will not have an issue with #{word} at top level not in a module\" do\n        with_environment(environment) do\n          expect(validate(parse(source))).not_to have_issue(Puppet::Pops::Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION)\n        end\n      end\n    end\n\n    it \"allows a reserved type name\" do\n      source = <<-SOURCE\n      class foo {\n        define string() {}\n        foo::string { 'a': }\n      }\n      SOURCE\n\n      acceptor = validate(parse(source, 'path/foo/manifests/init.pp'))\n      expect(acceptor.error_count).to eql(0)\n    end\n\n    it \"will give multiple errors in one file with multiple issues\" do\n      source = <<-SOURCE\n      class foo {}\n      notify { nope: }\n      node bar {}\n\n      $a = 7\n      SOURCE\n\n      with_environment(environment) do\n        acceptor = validate(parse(source, 'path/foo/manifests/init.pp'))\n        expect(deprecation_count(acceptor)).to eql(0)\n        expect(acceptor.warning_count).to eql(0)\n        expect(acceptor.error_count).to eql(3)\n\n        expect(acceptor.errors[0].source_pos.line).to eql(2)\n        expect(acceptor.errors[1].source_pos.line).to eql(3)\n        expect(acceptor.errors[2].source_pos.line).to eql(5)\n      end\n    end\n    [\n      \"application\",\n      \"consumes\",\n      \"produces\",\n      \"site\",\n    ].each do |kw|\n      it \"allow usage of #{kw} keyword (it was reserved in puppet 6.x)\" do\n        source = <<-SOURCE\n       class foo (\n         $#{kw},\n       ) {\n         notice $#{kw}\n       }\n\n       class { foo:\n         #{kw} => 'bar'\n       }\n     SOURCE\n\n        acceptor = validate(parse(source, 'path/foo/manifests/init.pp'))\n        expect(acceptor.error_count).to eql(0)\n      end\n    end\n  end\n\n  context 'literal values' do\n    it 'rejects a literal integer outside of max signed 64 bit range' do\n      expect(validate(parse(\"0x8000000000000000\"))).to have_issue(Puppet::Pops::Issues::NUMERIC_OVERFLOW)\n    end\n\n    it 'rejects a literal integer outside of min signed 64 bit range' do\n      expect(validate(parse(\"-0x8000000000000001\"))).to have_issue(Puppet::Pops::Issues::NUMERIC_OVERFLOW)\n    end\n  end\n\n  context 'uses a var pattern that is performant' do\n    it 'such that illegal VAR_NAME is not too slow' do\n      t = Time.now.nsec\n      result = '$hg_oais::archivematica::requirements::automation_tools::USER' =~ Puppet::Pops::Patterns::VAR_NAME\n      t2 = Time.now.nsec\n      expect(result).to be(nil)\n      expect(t2-t).to be < 1000000 # one ms as a check for very slow operation, is in fact at ~< 10 microsecond\n    end\n  end\n\n  def parse(source, path=nil)\n    Puppet::Pops::Parser::Parser.new.parse_string(source, path)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pops/visitor_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/pops'\n\ndescribe Puppet::Pops::Visitor do\n  describe \"A visitor and a visitable in a configuration with min and max args set to 0\" do\n    class DuckProcessor\n      def initialize\n        @friend_visitor = Puppet::Pops::Visitor.new(self, \"friend\", 0, 0)\n      end\n\n      def hi(o, *args)\n        @friend_visitor.visit(o, *args)\n      end\n\n      def friend_Duck(o)\n        \"Hi #{o.class}\"\n      end\n\n      def friend_Numeric(o)\n        \"Howdy #{o.class}\"\n      end\n    end\n\n    class Duck\n      include Puppet::Pops::Visitable\n    end\n\n    it \"should select the expected method when there are no arguments\" do\n      duck = Duck.new\n      duck_processor = DuckProcessor.new\n      expect(duck_processor.hi(duck)).to eq(\"Hi Duck\")\n    end\n\n    it \"should fail if there are too many arguments\" do\n      duck = Duck.new\n      duck_processor = DuckProcessor.new\n      expect { duck_processor.hi(duck, \"how are you?\") }.to raise_error(/^Visitor Error: Too many.*/)\n    end\n\n    it \"should select method for superclass\" do\n      duck_processor = DuckProcessor.new\n      expect(duck_processor.hi(42)).to match(/Howdy Integer/)\n    end\n\n    it \"should select method for superclass\" do\n      duck_processor = DuckProcessor.new\n      expect(duck_processor.hi(42.0)).to eq(\"Howdy Float\")\n    end\n\n    it \"should fail if class not handled\" do\n      duck_processor = DuckProcessor.new\n      expect { duck_processor.hi(\"wassup?\") }.to raise_error(/Visitor Error: the configured.*/)\n    end\n  end\n\n  describe \"A visitor and a visitable in a configuration with min =1, and max args set to 2\" do\n    class DuckProcessor2\n      def initialize\n        @friend_visitor = Puppet::Pops::Visitor.new(self, \"friend\", 1, 2)\n      end\n\n      def hi(o, *args)\n        @friend_visitor.visit(o, *args)\n      end\n\n      def friend_Duck(o, drink, eat=\"grain\")\n        \"Hi #{o.class}, drink=#{drink}, eat=#{eat}\"\n      end\n    end\n\n    class Duck\n      include Puppet::Pops::Visitable\n    end\n\n    it \"should select the expected method when there are is one arguments\" do\n      duck = Duck.new\n      duck_processor = DuckProcessor2.new\n      expect(duck_processor.hi(duck, \"water\")).to eq(\"Hi Duck, drink=water, eat=grain\")\n    end\n\n    it \"should fail if there are too many arguments\" do\n      duck = Duck.new\n      duck_processor = DuckProcessor2.new\n      expect { duck_processor.hi(duck, \"scotch\", \"soda\", \"peanuts\") }.to raise_error(/^Visitor Error: Too many.*/)\n    end\n\n    it \"should fail if there are too few arguments\" do\n      duck = Duck.new\n      duck_processor = DuckProcessor2.new\n      expect { duck_processor.hi(duck) }.to raise_error(/^Visitor Error: Too few.*/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/property/boolean_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/property/boolean'\n\ndescribe Puppet::Property::Boolean do\n  let (:resource) { double('resource') }\n  subject { described_class.new(:resource => resource) }\n\n  [ true, :true, 'true', :yes, 'yes', 'TrUe', 'yEs' ].each do |arg|\n    it \"should munge #{arg.inspect} as true\" do\n      expect(subject.munge(arg)).to eq(true)\n    end\n  end\n  [ false, :false, 'false', :no, 'no', 'FaLSE', 'nO' ].each do |arg|\n    it \"should munge #{arg.inspect} as false\" do\n      expect(subject.munge(arg)).to eq(false)\n    end\n  end\n  [ nil, :undef, 'undef', '0', 0, '1', 1, 9284 ].each do |arg|\n    it \"should fail to munge #{arg.inspect}\" do\n      expect { subject.munge(arg) }.to raise_error Puppet::Error\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/property/ensure_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/property/ensure'\n\nklass = Puppet::Property::Ensure\n\ndescribe klass do\n  it \"should be a subclass of Property\" do\n    expect(klass.superclass).to eq(Puppet::Property)\n  end\nend\n"
  },
  {
    "path": "spec/unit/property/keyvalue_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/property/keyvalue'\n\ndescribe 'Puppet::Property::KeyValue' do \n  let(:klass) { Puppet::Property::KeyValue }\n\n  it \"should be a subclass of Property\" do\n    expect(klass.superclass).to eq(Puppet::Property)\n  end\n\n  describe \"as an instance\" do\n    before do\n      # Wow that's a messy interface to the resource.\n      klass.initvars\n      @resource = double('resource', :[]= => nil, :property => nil)\n      @property = klass.new(:resource => @resource)\n      klass.log_only_changed_or_new_keys = false\n    end\n\n    it \"should have a , as default delimiter\" do\n      expect(@property.delimiter).to eq(\";\")\n    end\n\n    it \"should have a = as default separator\" do\n      expect(@property.separator).to eq(\"=\")\n    end\n\n    it \"should have a :membership as default membership\" do\n      expect(@property.membership).to eq(:key_value_membership)\n    end\n\n    it \"should return the same value passed into should_to_s\" do\n      @property.should_to_s({:foo => \"baz\", :bar => \"boo\"}) == \"foo=baz;bar=boo\"\n    end\n\n    it \"should return the passed in hash values joined with the delimiter from is_to_s\" do\n      s = @property.is_to_s({\"foo\" => \"baz\" , \"bar\" => \"boo\"})\n\n      # We can't predict the order the hash is processed in...\n      expect([\"foo=baz;bar=boo\", \"bar=boo;foo=baz\"]).to be_include s\n    end\n\n    describe \"when calling hash_to_key_value_s\" do\n      let(:input) do\n        {\n          :key1 => \"value1\",\n          :key2 => \"value2\",\n          :key3 => \"value3\"\n        }\n      end\n\n      before(:each) do\n        @property.instance_variable_set(:@changed_or_new_keys, [:key1, :key2])\n      end\n\n      it \"returns only the changed or new keys if log_only_changed_or_new_keys is set\" do\n        klass.log_only_changed_or_new_keys = true\n\n        expect(@property.hash_to_key_value_s(input)).to eql(\"key1=value1;key2=value2\")\n      end\n    end\n\n    describe \"when calling inclusive?\" do\n      it \"should use the membership method to look up on the @resource\" do\n        expect(@property).to receive(:membership).and_return(:key_value_membership)\n        expect(@resource).to receive(:[]).with(:key_value_membership)\n        @property.inclusive?\n      end\n\n      it \"should return true when @resource[membership] == inclusive\" do\n        allow(@property).to receive(:membership).and_return(:key_value_membership)\n        allow(@resource).to receive(:[]).with(:key_value_membership).and_return(:inclusive)\n        expect(@property.inclusive?).to eq(true)\n      end\n\n      it \"should return false when @resource[membership] != inclusive\" do\n        allow(@property).to receive(:membership).and_return(:key_value_membership)\n        allow(@resource).to receive(:[]).with(:key_value_membership).and_return(:minimum)\n        expect(@property.inclusive?).to eq(false)\n      end\n    end\n\n    describe \"when calling process_current_hash\" do\n      it \"should return {} if hash is :absent\" do\n        expect(@property.process_current_hash(:absent)).to eq({})\n      end\n\n      it \"should set every key to nil if inclusive?\" do\n        allow(@property).to receive(:inclusive?).and_return(true)\n        expect(@property.process_current_hash({:foo => \"bar\", :do => \"re\"})).to eq({ :foo => nil, :do => nil })\n      end\n\n      it \"should return the hash if !inclusive?\" do\n        allow(@property).to receive(:inclusive?).and_return(false)\n        expect(@property.process_current_hash({:foo => \"bar\", :do => \"re\"})).to eq({:foo => \"bar\", :do => \"re\"})\n      end\n    end\n\n    describe \"when calling should\" do\n      it \"should return nil if @should is nil\" do\n        expect(@property.should).to eq(nil)\n      end\n\n      it \"should call process_current_hash\" do\n        @property.should = [\"foo=baz\", \"bar=boo\"]\n        allow(@property).to receive(:retrieve).and_return({:do => \"re\", :mi => \"fa\" })\n        expect(@property).to receive(:process_current_hash).and_return({})\n        @property.should\n      end\n\n      it \"should return the hashed values of @should and the nilled values of retrieve if inclusive\" do\n        @property.should = [\"foo=baz\", \"bar=boo\"]\n        expect(@property).to receive(:retrieve).and_return({:do => \"re\", :mi => \"fa\" })\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property.should).to eq({ :foo => \"baz\", :bar => \"boo\", :do => nil, :mi => nil })\n      end\n\n      it \"should return the hashed @should + the unique values of retrieve if !inclusive\" do\n        @property.should = [\"foo=baz\", \"bar=boo\"]\n        expect(@property).to receive(:retrieve).and_return({:foo => \"diff\", :do => \"re\", :mi => \"fa\"})\n        expect(@property).to receive(:inclusive?).and_return(false)\n        expect(@property.should).to eq({ :foo => \"baz\", :bar => \"boo\", :do => \"re\", :mi => \"fa\" })\n      end\n\n      it \"should mark the keys that will change or be added as a result of our Puppet run\" do\n        @property.should = {\n          :key1 => \"new_value1\",\n          :key2 => \"value2\",\n          :key3 => \"new_value3\",\n          :key4 => \"value4\"\n        }\n        allow(@property).to receive(:retrieve).and_return(\n          {\n            :key1 => \"value1\",\n            :key2 => \"value2\",\n            :key3 => \"value3\"\n          }\n        )\n        allow(@property).to receive(:inclusive?).and_return(false)\n\n        @property.should\n        expect(@property.instance_variable_get(:@changed_or_new_keys)).to eql([:key1, :key3, :key4])\n      end\n    end\n\n    describe \"when calling retrieve\" do\n      before do\n        @provider = double(\"provider\")\n        allow(@property).to receive(:provider).and_return(@provider)\n      end\n\n      it \"should send 'name' to the provider\" do\n        expect(@provider).to receive(:send).with(:keys)\n        expect(@property).to receive(:name).and_return(:keys)\n        @property.retrieve\n      end\n\n      it \"should return a hash with the provider returned info\" do\n        allow(@provider).to receive(:send).with(:keys).and_return({\"do\" => \"re\", \"mi\" => \"fa\" })\n        allow(@property).to receive(:name).and_return(:keys)\n        @property.retrieve == {\"do\" => \"re\", \"mi\" => \"fa\" }\n      end\n\n      it \"should return :absent when the provider returns :absent\" do\n        allow(@provider).to receive(:send).with(:keys).and_return(:absent)\n        allow(@property).to receive(:name).and_return(:keys)\n        @property.retrieve == :absent\n      end\n    end\n\n    describe \"when calling hashify_should\" do\n      it \"should return the underlying hash if the user passed in a hash\" do\n        @property.should = { \"foo\" => \"bar\" }\n        expect(@property.hashify_should).to eql({ :foo => \"bar\" })\n      end\n\n      it \"should hashify the array of key/value pairs if that is what our user passed in\" do\n        @property.should = [ \"foo=baz\", \"bar=boo\" ]\n        expect(@property.hashify_should).to eq({ :foo => \"baz\", :bar => \"boo\" })\n      end\n    end\n\n    describe \"when calling safe_insync?\" do\n      before do\n        @provider = double(\"provider\")\n        allow(@property).to receive(:provider).and_return(@provider)\n        allow(@property).to receive(:name).and_return(:prop_name)\n      end\n\n      it \"should return true unless @should is defined and not nil\" do\n        @property.safe_insync?(\"foo\") == true\n      end\n\n      it \"should return true if the passed in values is nil\" do\n        @property.safe_insync?(nil) == true\n      end\n\n      it \"should return true if hashified should value == (retrieved) value passed in\" do\n        allow(@provider).to receive(:prop_name).and_return({ :foo => \"baz\", :bar => \"boo\" })\n        @property.should = [\"foo=baz\", \"bar=boo\"]\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property.safe_insync?({ :foo => \"baz\", :bar => \"boo\" })).to eq(true)\n      end\n\n      it \"should return false if prepared value != should value\" do\n        allow(@provider).to receive(:prop_name).and_return({ \"foo\" => \"bee\", \"bar\" => \"boo\" })\n        @property.should = [\"foo=baz\", \"bar=boo\"]\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property.safe_insync?({ \"foo\" => \"bee\", \"bar\" => \"boo\" })).to eq(false)\n      end\n    end\n\n    describe 'when validating a passed-in property value' do\n      it 'should raise a Puppet::Error if the property value is anything but a Hash or a String' do\n        expect { @property.validate(5) }.to raise_error do |error|\n          expect(error).to be_a(Puppet::Error)\n          expect(error.message).to match(\"specified as a hash or an array\")\n        end\n      end\n\n      it 'should accept a Hash property value' do\n        @property.validate({ 'foo' => 'bar' })\n      end\n\n      it \"should raise a Puppet::Error if the property value isn't a key/value pair\" do\n        expect { @property.validate('foo') }.to raise_error do |error|\n          expect(error).to be_a(Puppet::Error)\n          expect(error.message).to match(\"separated by '='\")\n        end\n      end\n\n      it 'should accept a valid key/value pair property value' do\n        @property.validate('foo=bar')\n      end\n    end\n\n    describe 'when munging a passed-in property value' do\n      it 'should return the value as-is if it is a string' do\n        expect(@property.munge('foo=bar')).to eql('foo=bar')\n      end\n\n      it 'should stringify + symbolize the keys and stringify the values if it is a hash' do\n        input = {\n          1     => 2,\n          true  => false,\n          '   foo   ' => 'bar'\n        }\n        expected_output = {\n          :'1'    => '2',\n          :true => 'false',\n          :foo  => 'bar'\n        }\n\n        expect(@property.munge(input)).to eql(expected_output)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/property/list_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/property/list'\n\nlist_class = Puppet::Property::List\n\ndescribe list_class do\n\n  it \"should be a subclass of Property\" do\n    expect(list_class.superclass).to eq(Puppet::Property)\n  end\n\n  describe \"as an instance\" do\n    before do\n      # Wow that's a messy interface to the resource.\n      list_class.initvars\n      @resource = double('resource', :[]= => nil, :property => nil)\n      @property = list_class.new(:resource => @resource)\n    end\n\n    it \"should have a , as default delimiter\" do\n      expect(@property.delimiter).to eq(\",\")\n    end\n\n    it \"should have a :membership as default membership\" do\n      expect(@property.membership).to eq(:membership)\n    end\n\n    it \"should return the same value passed into should_to_s\" do\n      @property.should_to_s(\"foo\") == \"foo\"\n    end\n\n    it \"should return the passed in array values joined with the delimiter from is_to_s\" do\n      expect(@property.is_to_s([\"foo\",\"bar\"])).to eq(\"foo,bar\")\n    end\n\n    it \"should be able to correctly convert ':absent' to a quoted string\" do\n      expect(@property.is_to_s(:absent)).to eq(\"'absent'\")\n    end\n\n    describe \"when adding should to current\" do\n      it \"should add the arrays when current is an array\" do\n        expect(@property.add_should_with_current([\"foo\"], [\"bar\"])).to eq([\"foo\", \"bar\"])\n      end\n\n      it \"should return should if current is not an array\" do\n        expect(@property.add_should_with_current([\"foo\"], :absent)).to eq([\"foo\"])\n      end\n\n      it \"should return only the uniq elements\" do\n        expect(@property.add_should_with_current([\"foo\", \"bar\"], [\"foo\", \"baz\"])).to eq([\"foo\", \"bar\", \"baz\"])\n      end\n    end\n\n    describe \"when calling inclusive?\" do\n      it \"should use the membership method to look up on the @resource\" do\n        expect(@property).to receive(:membership).and_return(:membership)\n        expect(@resource).to receive(:[]).with(:membership)\n        @property.inclusive?\n      end\n\n      it \"should return true when @resource[membership] == inclusive\" do\n        allow(@property).to receive(:membership).and_return(:membership)\n        allow(@resource).to receive(:[]).with(:membership).and_return(:inclusive)\n        expect(@property.inclusive?).to eq(true)\n      end\n\n      it \"should return false when @resource[membership] != inclusive\" do\n        allow(@property).to receive(:membership).and_return(:membership)\n        allow(@resource).to receive(:[]).with(:membership).and_return(:minimum)\n        expect(@property.inclusive?).to eq(false)\n      end\n    end\n\n    describe \"when calling should\" do\n      it \"should return nil if @should is nil\" do\n        expect(@property.should).to eq(nil)\n      end\n\n      it \"should return the sorted values of @should as a string if inclusive\" do\n        @property.should = [\"foo\", \"bar\"]\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property.should).to eq(\"bar,foo\")\n      end\n\n      it \"should return the uniq sorted values of @should + retrieve as a string if !inclusive\" do\n        @property.should = [\"foo\", \"bar\"]\n        expect(@property).to receive(:inclusive?).and_return(false)\n        expect(@property).to receive(:retrieve).and_return([\"foo\",\"baz\"])\n        expect(@property.should).to eq(\"bar,baz,foo\")\n      end\n    end\n\n    describe \"when calling retrieve\" do\n      let(:provider) { @provider }\n      before do\n        @provider = double(\"provider\")\n        allow(@property).to receive(:provider).and_return(provider)\n      end\n\n      it \"should send 'name' to the provider\" do\n        expect(@provider).to receive(:send).with(:group)\n        expect(@property).to receive(:name).and_return(:group)\n        @property.retrieve\n      end\n\n      it \"should return an array with the provider returned info\" do\n        allow(@provider).to receive(:send).with(:group).and_return(\"foo,bar,baz\")\n        allow(@property).to receive(:name).and_return(:group)\n        @property.retrieve == [\"foo\", \"bar\", \"baz\"]\n      end\n\n      it \"should return :absent when the provider returns :absent\" do\n        allow(@provider).to receive(:send).with(:group).and_return(:absent)\n        allow(@property).to receive(:name).and_return(:group)\n        @property.retrieve == :absent\n      end\n\n      context \"and provider is nil\" do\n        let(:provider) { nil }\n        it \"should return :absent\" do\n          @property.retrieve == :absent\n        end\n      end\n    end\n\n    describe \"when calling safe_insync?\" do\n      it \"should return true unless @should is defined and not nil\" do\n        expect(@property).to be_safe_insync(\"foo\")\n      end\n\n      it \"should return true unless the passed in values is not nil\" do\n        @property.should = \"foo\"\n        expect(@property).to be_safe_insync(nil)\n      end\n\n      it \"should call prepare_is_for_comparison with value passed in and should\" do\n        @property.should = \"foo\"\n        expect(@property).to receive(:prepare_is_for_comparison).with(\"bar\")\n        expect(@property).to receive(:should)\n        @property.safe_insync?(\"bar\")\n      end\n\n      it \"should return true if 'is' value is array of comma delimited should values\" do\n        @property.should = \"bar,foo\"\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property).to be_safe_insync([\"bar\",\"foo\"])\n      end\n\n      it \"should return true if 'is' value is :absent and should value is empty string\" do\n        @property.should = \"\"\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property).to be_safe_insync([])\n      end\n\n      it \"should return false if prepared value != should value\" do\n        @property.should = \"bar,baz,foo\"\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property).to_not be_safe_insync([\"bar\",\"foo\"])\n      end\n    end\n\n    describe \"when calling dearrayify\" do\n      it \"should sort and join the array with 'delimiter'\" do\n        array = double(\"array\")\n        expect(array).to receive(:sort).and_return(array)\n        expect(array).to receive(:join).with(@property.delimiter)\n        @property.dearrayify(array)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/property/ordered_list_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/property/ordered_list'\n\ndescribe Puppet::Property::OrderedList do\n  it \"should be a subclass of List\" do\n    expect(described_class.superclass).to eq(Puppet::Property::List)\n  end\n\n  describe \"as an instance\" do\n    before do\n      # Wow that's a messy interface to the resource.\n      described_class.initvars\n      @resource = double('resource', :[]= => nil, :property => nil)\n      @property = described_class.new(:resource => @resource)\n    end\n\n    describe \"when adding should to current\" do\n      it \"should add the arrays when current is an array\" do\n        expect(@property.add_should_with_current([\"should\"], [\"current\"])).to eq([\"should\", \"current\"])\n      end\n\n      it \"should return 'should' if current is not an array\" do\n        expect(@property.add_should_with_current([\"should\"], :absent)).to eq([\"should\"])\n      end\n\n      it \"should return only the uniq elements leading with the order of 'should'\" do\n        expect(@property.add_should_with_current([\"this\", \"is\", \"should\"], [\"is\", \"this\", \"current\"])).to eq([\"this\", \"is\", \"should\", \"current\"])\n      end\n    end\n\n    describe \"when calling should\" do\n      it \"should return nil if @should is nil\" do\n        expect(@property.should).to eq(nil)\n      end\n\n      it \"should return the values of @should (without sorting) as a string if inclusive\" do\n        @property.should = [\"foo\", \"bar\"]\n        expect(@property).to receive(:inclusive?).and_return(true)\n        expect(@property.should).to eq(\"foo,bar\")\n      end\n\n      it \"should return the uniq values of @should + retrieve as a string if !inclusive with the @ values leading\" do\n        @property.should = [\"foo\", \"bar\"]\n        expect(@property).to receive(:inclusive?).and_return(false)\n        expect(@property).to receive(:retrieve).and_return([\"foo\",\"baz\"])\n        expect(@property.should).to eq(\"foo,bar,baz\")\n      end\n    end\n\n    describe \"when calling dearrayify\" do\n      it \"should join the array with the delimiter\" do\n        array = double(\"array\")\n        expect(array).to receive(:join).with(@property.delimiter)\n        @property.dearrayify(array)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/property_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/property'\n\nPuppet::Type.newtype(:property_test) do\n  newparam(:name, isnamevar: true)\nend\nPuppet::Type.type(:property_test).provide(:property_test) do\n  attr_accessor :foo\nend\n\ndescribe Puppet::Property do\n  let :resource do\n    Puppet::Type.type(:property_test).new(:name => \"foo\")\n  end\n\n  let :subclass do\n    # We need a completely fresh subclass every time, because we modify both\n    # class and instance level things inside the tests.\n    subclass = Class.new(Puppet::Property) do\n      class << self\n        attr_accessor :name\n      end\n      @name = :foo\n    end\n    subclass.initvars\n    subclass\n  end\n\n  let :property do subclass.new :resource => resource end\n\n  it \"should be able to look up the modified name for a given value\" do\n    subclass.newvalue(:foo)\n    expect(subclass.value_name(\"foo\")).to eq(:foo)\n  end\n\n  it \"should be able to look up the modified name for a given value matching a regex\" do\n    subclass.newvalue(%r{.})\n    expect(subclass.value_name(\"foo\")).to eq(%r{.})\n  end\n\n  it \"should be able to look up a given value option\" do\n    subclass.newvalue(:foo, :event => :whatever)\n    expect(subclass.value_option(:foo, :event)).to eq(:whatever)\n  end\n\n  it \"should be able to specify required features\" do\n    expect(subclass).to respond_to(:required_features=)\n  end\n\n  {\"one\" => [:one],:one => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value|\n    it \"should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})\" do\n      subclass.required_features = in_value\n      expect(subclass.required_features).to eq(out_value)\n    end\n  }\n\n  it \"should return its name as a string when converted to a string\" do\n    expect(property.to_s).to eq(property.name.to_s)\n  end\n\n  describe \"when returning the default event name\" do\n    it \"should use the current 'should' value to pick the event name\" do\n      expect(property).to receive(:should).and_return(\"myvalue\")\n      expect(subclass).to receive(:value_option).with('myvalue', :event).and_return(:event_name)\n\n      property.event_name\n    end\n\n    it \"should return any event defined with the specified value\" do\n      expect(property).to receive(:should).and_return(:myval)\n      expect(subclass).to receive(:value_option).with(:myval, :event).and_return(:event_name)\n\n      expect(property.event_name).to eq(:event_name)\n    end\n\n    describe \"and the property is 'ensure'\" do\n      before :each do\n        allow(property).to receive(:name).and_return(:ensure)\n        expect(resource).to receive(:type).and_return(:mytype)\n      end\n\n      it \"should use <type>_created if the 'should' value is 'present'\" do\n        expect(property).to receive(:should).and_return(:present)\n        expect(property.event_name).to eq(:mytype_created)\n      end\n\n      it \"should use <type>_removed if the 'should' value is 'absent'\" do\n        expect(property).to receive(:should).and_return(:absent)\n        expect(property.event_name).to eq(:mytype_removed)\n      end\n\n      it \"should use <type>_changed if the 'should' value is not 'absent' or 'present'\" do\n        expect(property).to receive(:should).and_return(:foo)\n        expect(property.event_name).to eq(:mytype_changed)\n      end\n\n      it \"should use <type>_changed if the 'should value is nil\" do\n        expect(property).to receive(:should).and_return(nil)\n        expect(property.event_name).to eq(:mytype_changed)\n      end\n    end\n\n    it \"should use <property>_changed if the property is not 'ensure'\" do\n      allow(property).to receive(:name).and_return(:myparam)\n      expect(property).to receive(:should).and_return(:foo)\n      expect(property.event_name).to eq(:myparam_changed)\n    end\n\n    it \"should use <property>_changed if no 'should' value is set\" do\n      allow(property).to receive(:name).and_return(:myparam)\n      expect(property).to receive(:should).and_return(nil)\n      expect(property.event_name).to eq(:myparam_changed)\n    end\n  end\n\n  describe \"when creating an event\" do\n    before :each do\n      allow(property).to receive(:should).and_return(\"myval\")\n    end\n\n    it \"should use an event from the resource as the base event\" do\n      event = Puppet::Transaction::Event.new\n      expect(resource).to receive(:event).and_return(event)\n\n      expect(property.event).to equal(event)\n    end\n\n    it \"should have the default event name\" do\n      expect(property).to receive(:event_name).and_return(:my_event)\n      expect(property.event.name).to eq(:my_event)\n    end\n\n    it \"should have the property's name\" do\n      expect(property.event.property).to eq(property.name.to_s)\n    end\n\n    it \"should have the 'should' value set\" do\n      allow(property).to receive(:should).and_return(\"foo\")\n      expect(property.event.desired_value).to eq(\"foo\")\n    end\n\n    it \"should provide its path as the source description\" do\n      allow(property).to receive(:path).and_return(\"/my/param\")\n      expect(property.event.source_description).to eq(\"/my/param\")\n    end\n\n    it \"should have the 'invalidate_refreshes' value set if set on a value\" do\n      allow(property).to receive(:event_name).and_return(:my_event)\n      allow(property).to receive(:should).and_return(\"foo\")\n      foo = double()\n      expect(foo).to receive(:invalidate_refreshes).and_return(true)\n      collection = double()\n      expect(collection).to receive(:match?).with(\"foo\").and_return(foo)\n      allow(property.class).to receive(:value_collection).and_return(collection)\n      expect(property.event.invalidate_refreshes).to be_truthy\n    end\n\n    it \"sets the redacted field on the event when the property is sensitive\" do\n      property.sensitive = true\n      expect(property.event.redacted).to eq true\n    end\n  end\n\n  describe \"when defining new values\" do\n    it \"should define a method for each value created with a block that's not a regex\" do\n      subclass.newvalue(:foo) { }\n      expect(property).to respond_to(:set_foo)\n    end\n  end\n\n  describe \"when assigning the value\" do\n    it \"should just set the 'should' value\" do\n      property.value = \"foo\"\n      expect(property.should).to eq(\"foo\")\n    end\n\n    it \"should validate each value separately\" do\n      expect(property).to receive(:validate).with(\"one\")\n      expect(property).to receive(:validate).with(\"two\")\n\n      property.value = %w{one two}\n    end\n\n    it \"should munge each value separately and use any result as the actual value\" do\n      expect(property).to receive(:munge).with(\"one\").and_return(:one)\n      expect(property).to receive(:munge).with(\"two\").and_return(:two)\n\n      # Do this so we get the whole array back.\n      subclass.array_matching = :all\n\n      property.value = %w{one two}\n      expect(property.should).to eq([:one, :two])\n    end\n\n    it \"should return any set value\" do\n      expect(property.value = :one).to eq(:one)\n    end\n  end\n\n  describe \"when returning the value\" do\n    it \"should return nil if no value is set\" do\n      expect(property.should).to be_nil\n    end\n\n    it \"should return the first set 'should' value if :array_matching is set to :first\" do\n      subclass.array_matching = :first\n      property.should = %w{one two}\n      expect(property.should).to eq(\"one\")\n    end\n\n    it \"should return all set 'should' values as an array if :array_matching is set to :all\" do\n      subclass.array_matching = :all\n      property.should = %w{one two}\n      expect(property.should).to eq(%w{one two})\n    end\n\n    it \"should default to :first array_matching\" do\n      expect(subclass.array_matching).to eq(:first)\n    end\n\n    it \"should unmunge the returned value if :array_matching is set to :first\" do\n      property.class.unmunge do |v| v.to_sym end\n      subclass.array_matching = :first\n      property.should = %w{one two}\n\n      expect(property.should).to eq(:one)\n    end\n\n    it \"should unmunge all the returned values if :array_matching is set to :all\" do\n      property.class.unmunge do |v| v.to_sym end\n      subclass.array_matching = :all\n      property.should = %w{one two}\n\n      expect(property.should).to eq([:one, :two])\n    end\n  end\n\n  describe \"when validating values\" do\n    it \"should do nothing if no values or regexes have been defined\" do\n      expect { property.should = \"foo\" }.not_to raise_error\n    end\n\n    it \"should fail if the value is not a defined value or alias and does not match a regex\" do\n      subclass.newvalue(:foo)\n\n      expect { property.should = \"bar\" }.to raise_error(Puppet::Error, /Invalid value \"bar\"./)\n    end\n\n    it \"should succeeed if the value is one of the defined values\" do\n      subclass.newvalue(:foo)\n\n      expect { property.should = :foo }.not_to raise_error\n    end\n\n    it \"should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string\" do\n      subclass.newvalue(:foo)\n\n      expect { property.should = \"foo\" }.not_to raise_error\n    end\n\n    it \"should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol\" do\n      subclass.newvalue(\"foo\")\n\n      expect { property.should = :foo }.not_to raise_error\n    end\n\n    it \"should succeed if the value is one of the defined aliases\" do\n      subclass.newvalue(\"foo\")\n      subclass.aliasvalue(\"bar\", \"foo\")\n\n      expect { property.should = :bar }.not_to raise_error\n    end\n\n    it \"should succeed if the value matches one of the regexes\" do\n      subclass.newvalue(/./)\n\n      expect { property.should = \"bar\" }.not_to raise_error\n    end\n\n    it \"should validate that all required features are present\" do\n      subclass.newvalue(:foo, :required_features => [:a, :b])\n\n      expect(resource.provider).to receive(:satisfies?).with([:a, :b]).and_return(true)\n\n      property.should = :foo\n    end\n\n    it \"should fail if required features are missing\" do\n      subclass.newvalue(:foo, :required_features => [:a, :b])\n\n      expect(resource.provider).to receive(:satisfies?).with([:a, :b]).and_return(false)\n\n      expect { property.should = :foo }.to raise_error(Puppet::Error)\n    end\n\n    it \"should internally raise an ArgumentError if required features are missing\" do\n      subclass.newvalue(:foo, :required_features => [:a, :b])\n\n      expect(resource.provider).to receive(:satisfies?).with([:a, :b]).and_return(false)\n\n      expect { property.validate_features_per_value :foo }.to raise_error(ArgumentError)\n    end\n\n    it \"should validate that all required features are present for regexes\" do\n      subclass.newvalue(/./, :required_features => [:a, :b])\n\n      expect(resource.provider).to receive(:satisfies?).with([:a, :b]).and_return(true)\n\n      property.should = \"foo\"\n    end\n\n    it \"should support specifying an individual required feature\" do\n      subclass.newvalue(/./, :required_features => :a)\n\n      expect(resource.provider).to receive(:satisfies?).and_return(true)\n\n      property.should = \"foo\"\n    end\n  end\n\n  describe \"when munging values\" do\n    it \"should do nothing if no values or regexes have been defined\" do\n      expect(property.munge(\"foo\")).to eq(\"foo\")\n    end\n\n    it \"should return return any matching defined values\" do\n      subclass.newvalue(:foo)\n      expect(property.munge(\"foo\")).to eq(:foo)\n    end\n\n    it \"should return any matching aliases\" do\n      subclass.newvalue(:foo)\n      subclass.aliasvalue(:bar, :foo)\n      expect(property.munge(\"bar\")).to eq(:foo)\n    end\n\n    it \"should return the value if it matches a regex\" do\n      subclass.newvalue(/./)\n      expect(property.munge(\"bar\")).to eq(\"bar\")\n    end\n\n    it \"should return the value if no other option is matched\" do\n      subclass.newvalue(:foo)\n      expect(property.munge(\"bar\")).to eq(\"bar\")\n    end\n  end\n\n  describe \"when syncing the 'should' value\" do\n    it \"should set the value\" do\n      subclass.newvalue(:foo)\n      property.should = :foo\n      expect(property).to receive(:set).with(:foo)\n      property.sync\n    end\n  end\n\n  describe \"when setting a value\" do\n    it \"should catch exceptions and raise Puppet::Error\" do\n      subclass.newvalue(:foo) { raise \"eh\" }\n      expect { property.set(:foo) }.to raise_error(Puppet::Error)\n    end\n\n    it \"fails when the provider does not handle the attribute\" do\n      subclass.name = \"unknown\"\n      expect { property.set(:a_value) }.to raise_error(Puppet::Error)\n    end\n\n    it \"propogates the errors about missing methods from the provider\" do\n      provider = resource.provider\n      def provider.bad_method=(value)\n        value.this_method_does_not_exist\n      end\n\n      subclass.name = :bad_method\n      expect { property.set(:a_value) }.to raise_error(NoMethodError, /this_method_does_not_exist/)\n    end\n\n    describe \"that was defined without a block\" do\n      it \"should call the settor on the provider\" do\n        subclass.newvalue(:bar)\n        expect(resource.provider).to receive(:foo=).with(:bar)\n        property.set(:bar)\n      end\n\n       it \"should generate setter named from :method argument and propagate call to the provider\" do\n        subclass.newvalue(:bar, :method => 'set_vv')\n        expect(resource.provider).to receive(:foo=).with(:bar)\n        property.set_vv(:bar)\n      end\n    end\n\n    describe \"that was defined with a block\" do\n      it \"should call the method created for the value if the value is not a regex\" do\n        subclass.newvalue(:bar) {}\n        expect(property).to receive(:set_bar)\n        property.set(:bar)\n      end\n\n      it \"should call the provided block if the value is a regex\" do\n        thing = double\n        subclass.newvalue(/./) { thing.test }\n        expect(thing).to receive(:test)\n        property.set(\"foo\")\n      end\n    end\n  end\n\n  describe \"when producing a change log\" do\n    it \"should say 'defined' when the current value is 'absent'\" do\n      expect(property.change_to_s(:absent, \"foo\")).to match(/^defined/)\n    end\n\n    it \"should say 'undefined' when the new value is 'absent'\" do\n      expect(property.change_to_s(\"foo\", :absent)).to match(/^undefined/)\n    end\n\n    it \"should say 'changed' when neither value is 'absent'\" do\n      expect(property.change_to_s(\"foo\", \"bar\")).to match(/changed/)\n    end\n  end\n\n  shared_examples_for \"#insync?\" do\n    # We share a lot of behaviour between the all and first matching, so we\n    # use a shared behaviour set to emulate that.  The outside world makes\n    # sure the class, etc, point to the right content.\n    [[], [12], [12, 13]].each do |input|\n      it \"should return true if should is empty with is => #{input.inspect}\" do\n        property.should = []\n        expect(property).to be_insync(input)\n        expect(property.insync_values?([], input)).to be true\n      end\n    end\n  end\n\n  describe \"#insync?\" do\n    context \"array_matching :all\" do\n      # `@should` is an array of scalar values, and `is` is an array of scalar values.\n      before :each do\n        property.class.array_matching = :all\n      end\n\n      it_should_behave_like \"#insync?\"\n\n      context \"if the should value is an array\" do\n        let(:input) { [1,2] }\n        before :each do property.should = input end\n\n        it \"should match if is exactly matches\" do\n          val = [1, 2]\n          expect(property).to be_insync val\n          expect(property.insync_values?(input, val)).to be true\n        end\n\n        it \"should match if it matches, but all stringified\" do\n          val = [\"1\", \"2\"]\n          expect(property).to be_insync val\n          expect(property.insync_values?(input, val)).to be true\n        end\n\n        it \"should not match if some-but-not-all values are stringified\" do\n          val = [\"1\", 2]\n          expect(property).to_not be_insync val\n          expect(property.insync_values?(input, val)).to_not be true\n          val = [1, \"2\"]\n          expect(property).to_not be_insync val\n          expect(property.insync_values?(input, val)).to_not be true\n        end\n\n        it \"should not match if order is different but content the same\" do\n          val = [2, 1]\n          expect(property).to_not be_insync val\n          expect(property.insync_values?(input, val)).to_not be true\n        end\n\n        it \"should not match if there are more items in should than is\" do\n          val = [1]\n          expect(property).to_not be_insync val\n          expect(property.insync_values?(input, val)).to_not be true\n        end\n\n        it \"should not match if there are less items in should than is\" do\n          val = [1, 2, 3]\n          expect(property).to_not be_insync val\n          expect(property.insync_values?(input, val)).to_not be true\n        end\n\n        it \"should not match if `is` is empty but `should` isn't\" do\n          val = []\n          expect(property).to_not be_insync val\n          expect(property.insync_values?(input, val)).to_not be true\n        end\n      end\n    end\n\n    context \"array_matching :first\" do\n      # `@should` is an array of scalar values, and `is` is a scalar value.\n      before :each do\n        property.class.array_matching = :first\n      end\n\n      it_should_behave_like \"#insync?\"\n\n      [[1],                     # only the value\n       [1, 2],                  # matching value first\n       [2, 1],                  # matching value last\n       [0, 1, 2],               # matching value in the middle\n      ].each do |input|\n        it \"should by true if one unmodified should value of #{input.inspect} matches what is\" do\n          val = 1\n          property.should = input\n          expect(property).to be_insync val\n          expect(property.insync_values?(input, val)).to be true\n        end\n\n        it \"should be true if one stringified should value of #{input.inspect} matches what is\" do\n          val = \"1\"\n          property.should = input\n          expect(property).to be_insync val\n          expect(property.insync_values?(input, val)).to be true\n        end\n      end\n\n      it \"should not match if we expect a string but get the non-stringified value\" do\n        property.should = [\"1\"]\n        expect(property).to_not be_insync 1\n        expect(property.insync_values?([\"1\"], 1)).to_not be true\n      end\n\n      [[0], [0, 2]].each do |input|\n        it \"should not match if no should values match what is\" do\n          property.should = input\n          expect(property).to_not be_insync 1\n          expect(property.insync_values?(input, 1)).to_not be true\n          expect(property).to_not be_insync \"1\" # shouldn't match either.\n          expect(property.insync_values?(input, \"1\")).to_not be true\n        end\n      end\n    end\n  end\n\n  describe \"#property_matches?\" do\n    [1, \"1\", [1], :one].each do |input|\n      it \"should treat two equal objects as equal (#{input.inspect})\" do\n        expect(property.property_matches?(input, input)).to be_truthy\n      end\n    end\n\n    it \"should treat two objects as equal if the first argument is the stringified version of the second\" do\n      expect(property.property_matches?(\"1\", 1)).to be_truthy\n    end\n\n    it \"should NOT treat two objects as equal if the first argument is not a string, and the second argument is a string, even if it stringifies to the first\" do\n      expect(property.property_matches?(1, \"1\")).to be_falsey\n    end\n  end\n\n  describe \"#insync_values?\" do\n    it \"should log an exception when insync? throws one\" do\n      expect(property).to receive(:insync?).and_raise(ArgumentError)\n      expect(property.insync_values?(\"foo\",\"bar\")).to be nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/README.markdown",
    "content": "Provider Specs\n==============\n\nDefine specs for your providers under this directory.\n"
  },
  {
    "path": "spec/unit/provider/aix_object_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/aix_object'\n\ndescribe 'Puppet::Provider::AixObject' do\n  let(:resource) do\n    Puppet::Type.type(:user).new(\n      :name   => 'test_aix_user',\n      :ensure => :present\n    )\n  end\n  let(:klass) { Puppet::Provider::AixObject }\n  let(:provider) do\n    Puppet::Provider::AixObject.new(resource)\n  end\n\n  # Clear out the class-level + instance-level mappings\n  def clear_attributes\n    klass.instance_variable_set(:@mappings, nil)\n  end\n\n  before(:each) do\n    clear_attributes\n  end\n\n  describe '.mapping' do\n    let(:puppet_property) { :uid }\n    let(:aix_attribute) { :id }\n    let(:info) do\n      {\n        :puppet_property => puppet_property,\n        :aix_attribute   => aix_attribute\n      }\n    end\n\n    shared_examples 'a mapping' do |from, to|\n      context \"<#{from}> => <#{to}>\" do\n        let(:from_suffix) { from.to_s.split(\"_\")[-1] }\n        let(:to_suffix)   { to.to_s.split(\"_\")[-1] }\n        let(:conversion_fn) do\n          \"convert_#{from_suffix}_value\".to_sym\n        end\n\n        it 'creates the mapping for a pure conversion function and defines it' do\n          conversion_fn_lambda = \"#{from_suffix}_to_#{to_suffix}\".to_sym\n          info[conversion_fn_lambda] = lambda { |x| x.to_s }\n          provider.class.mapping(info)\n    \n          mappings = provider.class.mappings[to]\n          expect(mappings).to include(info[from])\n\n          mapping = mappings[info[from]]\n          expect(mapping.public_methods).to include(conversion_fn)\n\n          expect(mapping.send(conversion_fn, 3)).to eql('3')\n        end\n\n        it 'creates the mapping for an impure conversion function without defining it' do\n          conversion_fn_lambda = \"#{from_suffix}_to_#{to_suffix}\".to_sym\n          info[conversion_fn_lambda] = lambda { |provider, x| x.to_s }\n          provider.class.mapping(info)\n    \n          mappings = provider.class.mappings[to]\n          expect(mappings).to include(info[from])\n\n          mapping = mappings[info[from]]\n          expect(mapping.public_methods).not_to include(conversion_fn)\n        end\n  \n        it 'uses the identity function as the conversion function if none is provided' do\n          provider.class.mapping(info)\n\n    \n          mappings = provider.class.mappings[to]\n          expect(mappings).to include(info[from])\n\n          mapping = mappings[info[from]]\n          expect(mapping.public_methods).to include(conversion_fn)\n\n          expect(mapping.send(conversion_fn, 3)).to eql(3)\n        end\n      end\n    end\n\n    include_examples 'a mapping',\n                     :puppet_property,\n                     :aix_attribute\n\n    include_examples 'a mapping',\n                     :aix_attribute,\n                     :puppet_property\n\n    it 'sets the AIX attribute to the Puppet property if it is not provided' do\n      info[:aix_attribute] = nil\n      provider.class.mapping(info)\n\n      mappings = provider.class.mappings[:puppet_property]\n      expect(mappings).to include(info[:puppet_property])\n    end\n  end\n\n  describe '.numeric_mapping' do\n    let(:info) do\n      info_hash = {\n        :puppet_property => :uid,\n        :aix_attribute   => :id\n      }\n      provider.class.numeric_mapping(info_hash)\n\n      info_hash\n    end\n    let(:aix_attribute) do\n      provider.class.mappings[:aix_attribute][info[:puppet_property]]\n    end\n    let(:puppet_property) do\n      provider.class.mappings[:puppet_property][info[:aix_attribute]]\n    end\n\n    it 'raises an ArgumentError for a non-numeric Puppet property value' do\n      value = 'foo'\n      expect do\n        aix_attribute.convert_property_value(value) \n      end.to raise_error do |error|\n        expect(error).to be_a(ArgumentError)\n\n        expect(error.message).to match(value)\n        expect(error.message).to match(info[:puppet_property].to_s)\n      end\n    end\n\n    it 'converts the numeric Puppet property to a numeric AIX attribute' do\n      expect(aix_attribute.convert_property_value(10)).to eql('10')\n    end\n\n    it 'converts the numeric AIX attribute to a numeric Puppet property' do\n      expect(puppet_property.convert_attribute_value('10')).to eql(10)\n    end\n  end\n\n  describe '.mk_resource_methods' do\n    before(:each) do\n      # Add some Puppet properties\n      provider.class.mapping(\n        puppet_property: :foo,\n        aix_attribute: :foo\n      )\n      provider.class.mapping(\n        puppet_property: :bar,\n        aix_attribute: :bar\n      )\n\n      provider.class.mk_resource_methods\n    end\n\n    it 'defines the property getters' do\n      provider = Puppet::Provider::AixObject.new(resource)\n      provider.instance_variable_set(:@object_info, { :foo => 'foo', :baz => 'baz' })\n\n      (provider.class.mappings[:aix_attribute].keys + [:attributes]).each do |property|\n        expect(provider).to receive(:get).with(property).and_return('value')\n\n        expect(provider.send(property)).to eql('value')\n      end\n    end\n\n    it 'defines the property setters' do\n      provider = Puppet::Provider::AixObject.new(resource)\n\n      value = '15'\n      provider.class.mappings[:aix_attribute].keys.each do |property|\n        expect(provider).to receive(:set).with(property, value)\n\n        provider.send(\"#{property}=\".to_sym, value)\n      end\n    end\n  end\n\n  describe '.parse_colon_separated_list' do\n    it 'parses a single empty item' do\n      input = ''\n      output = ['']\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it 'parses a single nonempty item' do\n      input = 'item'\n      output = ['item']\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it \"parses an escaped ':'\" do\n      input = '#!:'\n      output = [':']\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it \"parses a single item with an escaped ':'\" do\n      input = 'fd8c#!:215d#!:178#!:'\n      output = ['fd8c:215d:178:']\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it \"parses multiple items that do not have an escaped ':'\" do\n      input = \"foo:bar baz:buu:1234\"\n      output = [\"foo\", \"bar baz\", \"buu\", \"1234\"]\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it \"parses multiple items some of which have escaped ':'\" do\n      input = \"1234#!:567:foo bar#!:baz:buu#!bob:sally:fd8c#!:215d#!:178\"\n      output = [\"1234:567\", \"foo bar:baz\", \"buu#!bob\", \"sally\", 'fd8c:215d:178']\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it \"parses a list with several empty items\" do\n      input = \"foo:::bar:baz:boo:\"\n      output = [\"foo\", \"\", \"\", \"bar\", \"baz\", \"boo\", \"\"]\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it \"parses a list with an escaped ':' and empty item at the end\" do\n      input = \"foo:bar#!::\"\n      output = [\"foo\", \"bar:\", \"\"]\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n\n    it 'parses a real world example' do\n      input = File.read(my_fixture('aix_colon_list_real_world_input.out')).chomp\n      output = Object.instance_eval(File.read(my_fixture('aix_colon_list_real_world_output.out')))\n\n      expect(provider.class.parse_colon_separated_list(input)).to eql(output)\n    end\n  end\n\n  describe '.parse_aix_objects' do\n    # parse_colon_separated_list is well tested, so we don't need to be\n    # as strict on the formatting of the output here. Main point of these\n    # tests is to capture the 'wholemeal' parsing that's going on, i.e.\n    # that we can parse a bunch of objects together.\n    let(:output) do\n      <<-AIX_OBJECTS\n#name:id:pgrp:groups\nroot:0:system:system,bin,sys,security,cron,audit,lp\n#name:id:pgrp:groups:home:gecos\nuser:10000:staff:staff:/home/user3:Some User\nAIX_OBJECTS\n    end\n\n    let(:expected_aix_attributes) do\n      [\n        {\n          :name => 'root',\n          :attributes => {\n            :id     => '0',\n            :pgrp   => 'system',\n            :groups => 'system,bin,sys,security,cron,audit,lp',\n          }\n        },\n        {\n          :name => 'user',\n          :attributes => {\n            :id     => '10000',\n            :pgrp   => 'staff',\n            :groups => 'staff',\n            :home   => '/home/user3',\n            :gecos  => 'Some User'\n          }\n        }\n      ]\n    end\n\n    it 'parses the AIX attributes from the command output' do\n      expect(provider.class.parse_aix_objects(output)).to eql(expected_aix_attributes)\n    end\n  end\n\n  describe 'list_all' do\n    let(:output) do\n      <<-OUTPUT\n#name:id\nsystem:0\n#name:id\nstaff:1\n#name:id\nbin:2\n      OUTPUT\n    end\n\n    it 'lists all of the objects' do\n      lscmd = 'lsgroups'\n      allow(provider.class).to receive(:command).with(:list).and_return(lscmd)\n      allow(provider.class).to receive(:execute).with([lscmd, '-c', '-a', 'id', 'ALL']).and_return(output)\n\n      expected_objects = [\n        { :name => 'system', :id => '0' },\n        { :name => 'staff', :id => '1' },\n        { :name => 'bin', :id => '2' }\n      ]\n      expect(provider.class.list_all).to eql(expected_objects)\n    end\n  end\n\n  describe '.instances' do\n    let(:objects) do\n      [\n        { :name => 'group1', :id => '1' },\n        { :name => 'group2', :id => '2' }\n      ]\n    end\n\n    it 'returns all of the available instances' do\n      allow(provider.class).to receive(:list_all).and_return(objects)\n\n      expect(provider.class.instances.map(&:name)).to eql(['group1', 'group2'])\n    end\n  end\n\n  describe '#mappings' do\n    # Returns a pair [ instance_level_mapped_object, class_level_mapped_object ]\n    def mapped_objects(type, input)\n      [\n        provider.mappings[type][input],\n        provider.class.mappings[type][input]\n      ]\n    end\n\n    before(:each) do\n      # Create a pure mapping\n      provider.class.numeric_mapping(\n        puppet_property: :pure_puppet_property,\n        aix_attribute: :pure_aix_attribute\n      )\n\n      # Create an impure mapping\n      impure_conversion_fn = lambda do |provider, value|\n        \"Provider instance's name is #{provider.name}\"\n      end\n      provider.class.mapping(\n        puppet_property: :impure_puppet_property,\n        aix_attribute: :impure_aix_attribute,\n        property_to_attribute: impure_conversion_fn,\n        attribute_to_property: impure_conversion_fn\n      )\n    end\n\n    it 'memoizes the result' do\n      provider.instance_variable_set(:@mappings, 'memoized')\n      expect(provider.mappings).to eql('memoized')\n    end\n\n    it 'creates the instance-level mappings with the same structure as the class-level one' do\n      expect(provider.mappings.keys).to eql(provider.class.mappings.keys)\n      provider.mappings.keys.each do |type|\n        expect(provider.mappings[type].keys).to eql(provider.class.mappings[type].keys)\n      end\n    end\n    \n    shared_examples 'uses the right mapped object for a given mapping' do |from_type, to_type|\n      context \"<#{from_type}> => <#{to_type}>\" do\n        it 'shares the class-level mapped object for pure mappings' do\n          input = \"pure_#{from_type}\".to_sym\n\n          instance_level_mapped_object, class_level_mapped_object = mapped_objects(to_type, input)\n          expect(instance_level_mapped_object.object_id).to eql(class_level_mapped_object.object_id)\n        end\n\n        it 'dups the class-level mapped object for impure mappings' do\n          input = \"impure_#{from_type}\".to_sym\n\n          instance_level_mapped_object, class_level_mapped_object = mapped_objects(to_type, input)\n          expect(instance_level_mapped_object.object_id).to_not eql(\n            class_level_mapped_object.object_id\n          )\n        end\n\n        it 'defines the conversion function for impure mappings' do\n          from_type_suffix = from_type.to_s.split(\"_\")[-1]\n          conversion_fn = \"convert_#{from_type_suffix}_value\".to_sym\n\n          input = \"impure_#{from_type}\".to_sym\n          mapped_object, _ = mapped_objects(to_type, input)\n\n          expect(mapped_object.public_methods).to include(conversion_fn)\n          expect(mapped_object.send(conversion_fn, 3)).to match(provider.name)\n        end\n      end\n    end\n\n    include_examples 'uses the right mapped object for a given mapping',\n                     :puppet_property,\n                     :aix_attribute\n\n    include_examples 'uses the right mapped object for a given mapping',\n                     :aix_attribute,\n                     :puppet_property\n  end\n\n  describe '#attributes_to_args' do\n    let(:attributes) do\n      {\n        :attribute1 => 'value1',\n        :attribute2 => 'value2'\n      }\n    end\n\n    it 'converts the attributes hash to CLI arguments' do\n      expect(provider.attributes_to_args(attributes)).to eql(\n        [\"attribute1=value1\", \"attribute2=value2\"]\n      )\n    end\n  end\n\n  describe '#ia_module_args' do\n    it 'returns no arguments if ia_load_module parameter or forcelocal parameter are not specified' do\n      allow(provider.resource).to receive(:[]).with(:ia_load_module).and_return(nil)\n      allow(provider.resource).to receive(:[]).with(:forcelocal).and_return(nil)\n      expect(provider.ia_module_args).to eql([])\n    end\n\n    it 'returns the ia_load_module as a CLI argument when ia_load_module is specified' do\n      allow(provider.resource).to receive(:[]).with(:ia_load_module).and_return('module')\n      allow(provider.resource).to receive(:[]).with(:forcelocal).and_return(nil)\n      expect(provider.ia_module_args).to eql(['-R', 'module'])\n    end\n\n    it 'returns \"files\" as a CLI argument when forcelocal is specified' do\n      allow(provider.resource).to receive(:[]).with(:ia_load_module).and_return(nil)\n      allow(provider.resource).to receive(:[]).with(:forcelocal).and_return(true)\n      expect(provider.ia_module_args).to eql(['-R', 'files'])\n    end\n\n    it 'raises argument error when both ia_load_module and forcelocal parameters are set' do\n      allow(provider.resource).to receive(:[]).with(:ia_load_module).and_return('files')\n      allow(provider.resource).to receive(:[]).with(:forcelocal).and_return(true)\n      expect { provider.ia_module_args }.to raise_error(ArgumentError, \"Cannot have both 'forcelocal' and 'ia_load_module' at the same time!\")\n    end\n  end\n\n  describe '#lscmd' do\n    it 'returns the lscmd' do\n      allow(provider.class).to receive(:command).with(:list).and_return('list')\n      allow(provider).to receive(:ia_module_args).and_return(['ia_module_args'])\n\n      expect(provider.lscmd).to eql(\n        ['list', '-c', 'ia_module_args', provider.resource.name]\n      )\n    end\n  end\n\n  describe '#addcmd' do\n    let(:attributes) do\n      {\n        :attribute1 => 'value1',\n        :attribute2 => 'value2'\n      }\n    end\n\n    it 'returns the addcmd passing in the attributes as CLI arguments' do\n      allow(provider.class).to receive(:command).with(:add).and_return('add')\n      allow(provider).to receive(:ia_module_args).and_return(['ia_module_args'])\n\n      expect(provider.addcmd(attributes)).to eql(\n        ['add', 'ia_module_args', 'attribute1=value1', 'attribute2=value2', provider.resource.name]\n      )\n    end\n  end\n\n  describe '#deletecmd' do\n    it 'returns the lscmd' do\n      allow(provider.class).to receive(:command).with(:delete).and_return('delete')\n      allow(provider).to receive(:ia_module_args).and_return(['ia_module_args'])\n\n      expect(provider.deletecmd).to eql(\n        ['delete', 'ia_module_args', provider.resource.name]\n      )\n    end\n  end\n\n  describe '#modifycmd' do\n    let(:attributes) do\n      {\n        :attribute1 => 'value1',\n        :attribute2 => 'value2'\n      }\n    end\n\n    it 'returns the addcmd passing in the attributes as CLI arguments' do\n      allow(provider.class).to receive(:command).with(:modify).and_return('modify')\n      allow(provider).to receive(:ia_module_args).and_return(['ia_module_args'])\n\n      expect(provider.modifycmd(attributes)).to eql(\n        ['modify', 'ia_module_args', 'attribute1=value1', 'attribute2=value2', provider.resource.name]\n      )\n    end\n  end\n\n  describe '#modify_object' do\n    let(:new_attributes) do\n      {\n        :nofiles => 10000,\n        :fsize   => 30000\n      }\n    end\n\n    it 'modifies the AIX object with the new attributes' do\n      allow(provider).to receive(:modifycmd).with(new_attributes).and_return('modify_cmd')\n      expect(provider).to receive(:execute).with('modify_cmd')\n      expect(provider).to receive(:object_info).with(true)\n\n      provider.modify_object(new_attributes)\n    end\n  end\n\n  describe '#get' do\n    # Input\n    let(:property) { :uid }\n    \n    let!(:object_info) do\n      hash = {}\n\n      provider.instance_variable_set(:@object_info, hash)\n      hash\n    end\n\n    it 'returns :absent if the AIX object does not exist' do\n      allow(provider).to receive(:exists?).and_return(false)\n      object_info[property] = 15\n      \n      expect(provider.get(property)).to eql(:absent)\n    end\n\n    it 'returns :absent if the property is not present on the system' do\n      allow(provider).to receive(:exists?).and_return(true)\n\n      expect(provider.get(property)).to eql(:absent)\n    end\n\n    it \"returns the property's value\" do\n      allow(provider).to receive(:exists?).and_return(true)\n      object_info[property] = 15\n\n      expect(provider.get(property)).to eql(15)\n    end\n  end\n\n  describe '#set' do\n    # Input\n    let(:property) { :uid }\n    let(:value) { 10 }\n\n    # AIX attribute params\n    let(:aix_attribute) { :id }\n    let(:property_to_attribute) do\n      lambda { |x| x.to_s }\n    end\n\n    before(:each) do\n      # Add an attribute\n      provider.class.mapping(\n        puppet_property: property,\n        aix_attribute: aix_attribute,\n        property_to_attribute: property_to_attribute\n      )\n    end\n\n    it \"raises a Puppet::Error if it fails to set the property's value\" do\n      allow(provider).to receive(:modify_object)\n        .with({ :id => value.to_s })\n        .and_raise(Puppet::ExecutionFailure, 'failed to modify the AIX object!')\n\n      expect { provider.set(property, value) }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n      end\n    end\n\n    it \"sets the given property's value to the passed-in value\" do\n      expect(provider).to receive(:modify_object).with({ :id => value.to_s })\n\n      provider.set(property, value)\n    end\n  end\n\n  describe '#validate_new_attributes' do\n    let(:new_attributes) do\n      {\n        :nofiles => 10000,\n        :fsize   => 100000\n      }\n    end\n\n    it 'raises a Puppet::Error if a specified attributes corresponds to a Puppet property, reporting all of the attribute-property conflicts' do\n      provider.class.mapping(puppet_property: :uid, aix_attribute: :id)\n      provider.class.mapping(puppet_property: :groups, aix_attribute: :groups)\n\n      new_attributes[:id] = '25'\n      new_attributes[:groups] = 'groups'\n\n      expect { provider.validate_new_attributes(new_attributes) }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match(\"'uid', 'groups'\")\n        expect(error.message).to match(\"'id', 'groups'\")\n      end\n    end\n  end\n\n  describe '#attributes=' do\n    let(:new_attributes) do\n      {\n        :nofiles => 10000,\n        :fsize   => 100000\n      }\n    end\n\n    it 'raises a Puppet::Error if one of the specified attributes corresponds to a Puppet property' do\n      provider.class.mapping(puppet_property: :uid, aix_attribute: :id)\n      new_attributes[:id] = '25'\n\n      expect { provider.attributes = new_attributes }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match('uid')\n        expect(error.message).to match('id')\n      end\n    end\n\n    it 'raises a Puppet::Error if it fails to set the new AIX attributes' do\n      allow(provider).to receive(:modify_object)\n        .with(new_attributes)\n        .and_raise(Puppet::ExecutionFailure, 'failed to modify the AIX object!')\n      \n      expect { provider.attributes = new_attributes }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match('failed to modify the AIX object!')\n      end\n    end\n\n    it 'sets the new AIX attributes' do\n      expect(provider).to receive(:modify_object).with(new_attributes)\n\n      provider.attributes = new_attributes\n    end\n  end\n\n  describe '#object_info' do\n    before(:each) do\n      # Add some Puppet properties\n      provider.class.mapping(\n        puppet_property: :uid,\n        aix_attribute: :id,\n        attribute_to_property: lambda { |x| x.to_i },\n      )\n      provider.class.mapping(\n        puppet_property: :groups,\n        aix_attribute: :groups\n      )\n\n      # Mock out our lscmd\n      allow(provider).to receive(:lscmd).and_return(\"lsuser #{resource[:name]}\")\n    end\n\n    it 'memoizes the result' do\n      provider.instance_variable_set(:@object_info, {})\n      expect(provider.object_info).to eql({})\n    end\n\n    it 'returns nil if the AIX object does not exist' do\n      allow(provider).to receive(:execute).with(provider.lscmd).and_raise(\n        Puppet::ExecutionFailure, 'lscmd failed!'\n      )\n\n      expect(provider.object_info).to be_nil\n    end\n\n    it 'collects the Puppet properties' do\n      output = 'mock_output'\n      allow(provider).to receive(:execute).with(provider.lscmd).and_return(output)\n\n      # Mock the AIX attributes on the system\n      mock_attributes = {\n        :id         => '1',\n        :groups     => 'foo,bar,baz',\n        :attribute1 => 'value1',\n        :attribute2 => 'value2'\n      }\n      allow(provider.class).to receive(:parse_aix_objects)\n        .with(output)\n        .and_return([{ :name => resource.name, :attributes => mock_attributes }])\n\n      expected_property_values = {\n        :uid        => 1,\n        :groups     => 'foo,bar,baz',\n        :attributes => {\n          :attribute1 => 'value1',\n          :attribute2 => 'value2'\n        }\n      }\n      provider.object_info\n      expect(provider.instance_variable_get(:@object_info)).to eql(expected_property_values)\n    end\n  end\n\n  describe '#exists?' do\n    it 'should return true if the AIX object exists' do\n      allow(provider).to receive(:object_info).and_return({})\n      expect(provider.exists?).to be(true)\n    end\n\n    it 'should return false if the AIX object does not exist' do\n      allow(provider).to receive(:object_info).and_return(nil)\n      expect(provider.exists?).to be(false)\n    end\n  end\n\n  describe \"#create\" do\n    let(:property_attributes) do\n      {}\n    end\n\n    def stub_attributes_property(attributes)\n      allow(provider.resource).to receive(:should).with(:attributes).and_return(attributes)\n    end\n\n    def set_property(puppet_property, aix_attribute, property_to_attribute, should_value = nil)\n      property_to_attribute ||= lambda { |x| x }\n\n      provider.class.mapping(\n        puppet_property: puppet_property,\n        aix_attribute: aix_attribute,\n        property_to_attribute: property_to_attribute\n      )\n      allow(provider.resource).to receive(:should).with(puppet_property).and_return(should_value)\n\n      if should_value\n        property_attributes[aix_attribute] = property_to_attribute.call(should_value)\n      end\n    end\n\n    before(:each) do\n      clear_attributes\n      \n      # Clear out the :attributes property. We will be setting this later.\n      stub_attributes_property(nil)\n\n      # Add some properties\n      set_property(:uid, :id, lambda { |x| x.to_s }, 10)\n      set_property(:groups, :groups, nil, 'group1,group2,group3')\n      set_property(:shell, :shell, nil)\n    end\n\n    it 'raises a Puppet::Error if one of the specified attributes corresponds to a Puppet property' do\n      stub_attributes_property({ :id => 15 })\n      provider.class.mapping(puppet_property: :uid, aix_attribute: :id)\n\n      expect { provider.create }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match('uid')\n        expect(error.message).to match('id')\n      end\n    end\n\n    it \"raises a Puppet::Error if it fails to create the AIX object\" do\n      allow(provider).to receive(:addcmd)\n      allow(provider).to receive(:execute).and_raise(\n        Puppet::ExecutionFailure, \"addcmd failed!\"\n      )\n\n      expect { provider.create }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match(\"not create\")\n      end\n    end\n\n    it \"creates the AIX object with the given AIX attributes + Puppet properties\" do\n      attributes = { :fsize => 1000 }\n      stub_attributes_property(attributes)\n\n      expect(provider).to receive(:addcmd)\n        .with(attributes.merge(property_attributes))\n        .and_return('addcmd')\n      expect(provider).to receive(:execute).with('addcmd')\n\n      provider.create\n    end\n  end\n \n  describe \"#delete\" do\n    before(:each) do\n      allow(provider).to receive(:deletecmd).and_return('deletecmd')\n    end\n\n    it \"raises a Puppet::Error if it fails to delete the AIX object\" do\n      allow(provider).to receive(:execute).with(provider.deletecmd).and_raise(\n        Puppet::ExecutionFailure, \"deletecmd failed!\"\n      )\n\n      expect { provider.delete }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match(\"not delete\")\n      end\n    end\n\n    it \"deletes the AIX object\" do\n      expect(provider).to receive(:execute).with(provider.deletecmd)\n      expect(provider).to receive(:object_info).with(true)\n\n      provider.delete\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/command_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/provider/command'\n\ndescribe Puppet::Provider::Command do\n  let(:name) { \"the name\" }\n  let(:the_options) { { :option => 1 } }\n  let(:no_options) { {} }\n  let(:executable) { \"foo\" }\n  let(:executable_absolute_path) { \"/foo/bar\" }\n\n  let(:executor) { double('executor') }\n  let(:resolver) { double('resolver') }\n\n  let(:path_resolves_to_itself) do\n    resolves = Object.new\n    class << resolves\n      def which(path)\n        path\n      end\n    end\n    resolves\n  end\n\n  it \"executes a simple command\" do\n    expect(executor).to receive(:execute).with([executable], no_options)\n\n    command = Puppet::Provider::Command.new(name, executable, path_resolves_to_itself, executor)\n    command.execute()\n  end\n\n  it \"executes a command with extra options\" do\n    expect(executor).to receive(:execute).with([executable], the_options)\n\n    command = Puppet::Provider::Command.new(name, executable, path_resolves_to_itself, executor, the_options)\n    command.execute()\n  end\n\n  it \"executes a command with arguments\" do\n    expect(executor).to receive(:execute).with([executable, \"arg1\", \"arg2\"], no_options)\n\n    command = Puppet::Provider::Command.new(name, executable, path_resolves_to_itself, executor)\n    command.execute(\"arg1\", \"arg2\")\n  end\n\n  it \"resolves to an absolute path for better execution\" do\n    expect(resolver).to receive(:which).with(executable).and_return(executable_absolute_path)\n    expect(executor).to receive(:execute).with([executable_absolute_path], no_options)\n\n    command = Puppet::Provider::Command.new(name, executable, resolver, executor)\n    command.execute()\n  end\n\n  it \"errors when the executable resolves to nothing\" do\n    expect(resolver).to receive(:which).with(executable).and_return(nil)\n    expect(executor).not_to receive(:execute)\n\n    command = Puppet::Provider::Command.new(name, executable, resolver, executor)\n\n    expect { command.execute() }.to raise_error(Puppet::Error, \"Command #{name} is missing\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/exec/posix_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:exec).provider(:posix), :if => Puppet.features.posix? do\n  include PuppetSpec::Files\n\n  def make_exe\n    cmdpath = tmpdir('cmdpath')\n    exepath = tmpfile('my_command', cmdpath)\n    FileUtils.touch(exepath)\n    File.chmod(0755, exepath)\n    exepath\n  end\n\n  let(:resource) { Puppet::Type.type(:exec).new(:title => '/foo', :provider => :posix) }\n  let(:provider) { described_class.new(resource) }\n\n  describe \"#validatecmd\" do\n    it \"should fail if no path is specified and the command is not fully qualified\" do\n      expect { provider.validatecmd(\"foo\") }.to raise_error(\n        Puppet::Error,\n        \"'foo' is not qualified and no path was specified. Please qualify the command or specify a path.\"\n      )\n    end\n\n    it \"should pass if a path is given\" do\n      provider.resource[:path] = ['/bogus/bin']\n      provider.validatecmd(\"../foo\")\n    end\n\n    it \"should pass if command is fully qualifed\" do\n      provider.resource[:path] = ['/bogus/bin']\n      provider.validatecmd(\"/bin/blah/foo\")\n    end\n  end\n\n  describe \"#run\" do\n    describe \"when the command is an absolute path\" do\n      let(:command) { tmpfile('foo') }\n\n      it \"should fail if the command doesn't exist\" do\n        expect { provider.run(command) }.to raise_error(ArgumentError, \"Could not find command '#{command}'\")\n      end\n\n      it \"should fail if the command isn't a file\" do\n        FileUtils.mkdir(command)\n        FileUtils.chmod(0755, command)\n\n        expect { provider.run(command) }.to raise_error(ArgumentError, \"'#{command}' is a directory, not a file\")\n      end\n\n      it \"should fail if the command isn't executable\" do\n        FileUtils.touch(command)\n        allow(File).to receive(:executable?).with(command).and_return(false)\n\n        expect { provider.run(command) }.to raise_error(ArgumentError, \"'#{command}' is not executable\")\n      end\n    end\n\n    describe \"when the command is a relative path\" do\n      it \"should execute the command if it finds it in the path and is executable\" do\n        command = make_exe\n        provider.resource[:path] = [File.dirname(command)]\n        filename = File.basename(command)\n\n        expect(Puppet::Util::Execution).to receive(:execute).with(filename, instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n        provider.run(filename)\n      end\n\n      it \"should fail if the command isn't in the path\" do\n        resource[:path] = [\"/fake/path\"]\n\n        expect { provider.run('foo') }.to raise_error(ArgumentError, \"Could not find command 'foo'\")\n      end\n\n      it \"should fail if the command is in the path but not executable\" do\n        command = make_exe\n        File.chmod(0644, command)\n        allow(FileTest).to receive(:executable?).with(command).and_return(false)\n        resource[:path] = [File.dirname(command)]\n        filename = File.basename(command)\n\n        expect { provider.run(filename) }.to raise_error(ArgumentError, \"Could not find command '#{filename}'\")\n      end\n    end\n\n    it \"should not be able to execute shell builtins\" do\n      provider.resource[:path] = ['/bogus/bin']\n      expect { provider.run(\"cd ..\") }.to raise_error(ArgumentError, \"Could not find command 'cd'\")\n    end\n\n    it \"should execute the command if the command given includes arguments or subcommands\" do\n      provider.resource[:path] = ['/bogus/bin']\n      command = make_exe\n\n      expect(Puppet::Util::Execution).to receive(:execute).with(\"#{command} bar --sillyarg=true --blah\", instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n      provider.run(\"#{command} bar --sillyarg=true --blah\")\n    end\n\n    it \"should fail if quoted command doesn't exist\" do\n      provider.resource[:path] = ['/bogus/bin']\n      command = \"/foo bar --sillyarg=true --blah\"\n\n      expect { provider.run(%Q[\"#{command}\"]) }.to raise_error(ArgumentError, \"Could not find command '#{command}'\")\n    end\n\n    it \"should warn if you're overriding something in environment\" do\n      provider.resource[:environment] = ['WHATEVER=/something/else', 'WHATEVER=/foo']\n      command = make_exe\n\n      expect(Puppet::Util::Execution).to receive(:execute).with(command, instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n      provider.run(command)\n\n      expect(@logs.map {|l| \"#{l.level}: #{l.message}\" }).to eq([\"warning: Overriding environment setting 'WHATEVER' with '/foo'\"])\n    end\n\n    it \"should warn when setting an empty environment variable\" do\n      provider.resource[:environment] = ['WHATEVER=']\n      command = make_exe\n\n      expect(Puppet::Util::Execution).to receive(:execute).with(command, instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n      provider.run(command)\n\n      expect(@logs.map(&:to_s).join).to match(/Empty environment setting 'WHATEVER'\\n\\s+\\(file & line not available\\)/m)\n    end\n\n    it \"should warn when setting an empty environment variable (within a manifest)\" do\n      provider.resource[:environment] = ['WHATEVER=']\n      provider.resource.file = '/tmp/foobar'\n      provider.resource.line = 42\n      command = make_exe\n\n      expect(Puppet::Util::Execution).to receive(:execute).with(command, instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n      provider.run(command)\n\n      expect(@logs.map(&:to_s).join).to match(/Empty environment setting 'WHATEVER'\\n\\s+\\(file: \\/tmp\\/foobar, line: 42\\)/m)\n    end\n\n    it \"should set umask before execution if umask parameter is in use\" do\n      provider.resource[:umask] = '0027'\n      expect(Puppet::Util).to receive(:withumask).with(0027)\n      provider.run(provider.resource[:command])\n    end\n\n    describe \"posix locale settings\", :unless => RUBY_PLATFORM == 'java' do\n      # a sentinel value that we can use to emulate what locale environment variables might be set to on an international\n      # system.\n      lang_sentinel_value = \"en_US.UTF-8\"\n      # a temporary hash that contains sentinel values for each of the locale environment variables that we override in\n      # \"exec\"\n      locale_sentinel_env = {}\n      Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| locale_sentinel_env[var] = lang_sentinel_value }\n\n      command = \"/bin/echo $%s\"\n\n      it \"should not override user's locale during execution\" do\n        # we'll do this once without any sentinel values, to give us a little more test coverage\n        orig_env = {}\n        Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| orig_env[var] = ENV[var] if ENV[var] }\n\n        orig_env.keys.each do |var|\n          output, _ = provider.run(command % var)\n          expect(output.strip).to eq(orig_env[var])\n        end\n\n        # now, once more... but with our sentinel values\n        Puppet::Util.withenv(locale_sentinel_env) do\n          Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n            output, _ = provider.run(command % var)\n            expect(output.strip).to eq(locale_sentinel_env[var])\n          end\n        end\n      end\n\n      it \"should respect locale overrides in user's 'environment' configuration\" do\n        provider.resource[:environment] = ['LANG=C', 'LC_ALL=C']\n        output, _ = provider.run(command % 'LANG')\n        expect(output.strip).to eq('C')\n        output, _ = provider.run(command % 'LC_ALL')\n        expect(output.strip).to eq('C')\n      end\n    end\n\n    describe \"posix user-related environment vars\", :unless => RUBY_PLATFORM == 'java' do\n      # a temporary hash that contains sentinel values for each of the user-related environment variables that we\n      # are expected to unset during an \"exec\"\n      user_sentinel_env = {}\n      Puppet::Util::POSIX::USER_ENV_VARS.each { |var| user_sentinel_env[var] = \"Abracadabra\" }\n\n      command = \"/bin/echo $%s\"\n\n      it \"should unset user-related environment vars during execution\" do\n        # first we set up a temporary execution environment with sentinel values for the user-related environment vars\n        # that we care about.\n        Puppet::Util.withenv(user_sentinel_env) do\n          # with this environment, we loop over the vars in question\n          Puppet::Util::POSIX::USER_ENV_VARS.each do |var|\n            # ensure that our temporary environment is set up as we expect\n            expect(ENV[var]).to eq(user_sentinel_env[var])\n\n            # run an \"exec\" via the provider and ensure that it unsets the vars\n            output, _ = provider.run(command % var)\n            expect(output.strip).to eq(\"\")\n\n            # ensure that after the exec, our temporary env is still intact\n            expect(ENV[var]).to eq(user_sentinel_env[var])\n          end\n\n        end\n      end\n\n      it \"should respect overrides to user-related environment vars in caller's 'environment' configuration\" do\n        sentinel_value = \"Abracadabra\"\n        # set the \"environment\" property of the resource, populating it with a hash containing sentinel values for\n        # each of the user-related posix environment variables\n        provider.resource[:environment] = Puppet::Util::POSIX::USER_ENV_VARS.collect { |var| \"#{var}=#{sentinel_value}\"}\n\n        # loop over the posix user-related environment variables\n        Puppet::Util::POSIX::USER_ENV_VARS.each do |var|\n          # run an 'exec' to get the value of each variable\n          output, _ = provider.run(command % var)\n          # ensure that it matches our expected sentinel value\n          expect(output.strip).to eq(sentinel_value)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/exec/shell_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:exec).provider(:shell),\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:resource) { Puppet::Type.type(:exec).new(:title => 'foo', :provider => 'shell') }\n  let(:provider) { described_class.new(resource) }\n\n  describe \"#run\" do\n    it \"should be able to run builtin shell commands\" do\n      output, status = provider.run(\"if [ 1 = 1 ]; then echo 'blah'; fi\")\n      expect(status.exitstatus).to eq(0)\n      expect(output).to eq(\"blah\\n\")\n    end\n\n    it \"should be able to run commands with single quotes in them\" do\n      output, status = provider.run(\"echo 'foo  bar'\")\n      expect(status.exitstatus).to eq(0)\n      expect(output).to eq(\"foo  bar\\n\")\n    end\n\n    it \"should be able to run commands with double quotes in them\" do\n      output, status = provider.run('echo \"foo  bar\"')\n      expect(status.exitstatus).to eq(0)\n      expect(output).to eq(\"foo  bar\\n\")\n    end\n\n    it \"should be able to run multiple commands separated by a semicolon\" do\n      output, status = provider.run(\"echo 'foo' ; echo 'bar'\")\n      expect(status.exitstatus).to eq(0)\n      expect(output).to eq(\"foo\\nbar\\n\")\n    end\n\n    it \"should be able to read values from the environment parameter\" do\n      resource[:environment] = \"FOO=bar\"\n      output, status = provider.run(\"echo $FOO\")\n      expect(status.exitstatus).to eq(0)\n      expect(output).to eq(\"bar\\n\")\n    end\n\n    it \"#14060: should interpolate inside the subshell, not outside it\" do\n      resource[:environment] = \"foo=outer\"\n      output, status = provider.run(\"foo=inner; echo \\\"foo is $foo\\\"\")\n      expect(status.exitstatus).to eq(0)\n      expect(output).to eq(\"foo is inner\\n\")\n    end\n  end\n\n  describe \"#validatecmd\" do\n    it \"should always return true because builtins don't need path or to be fully qualified\" do\n      expect(provider.validatecmd('whateverdoesntmatter')).to eq(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/exec/windows_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:exec).provider(:windows), :if => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n\n  let(:resource) { Puppet::Type.type(:exec).new(:title => 'C:\\foo', :provider => :windows) }\n  let(:provider) { described_class.new(resource) }\n\n  after :all do\n    # This provider may not be suitable on some machines, so we want to reset\n    # the default so it isn't used by mistake in future specs.\n    Puppet::Type.type(:exec).defaultprovider = nil\n  end\n\n  describe \"#extractexe\" do\n    describe \"when the command has no arguments\" do\n      it \"should return the command if it's quoted\" do\n        expect(provider.extractexe('\"foo\"')).to eq('foo')\n      end\n\n      it \"should return the command if it's quoted and contains spaces\" do\n        expect(provider.extractexe('\"foo bar\"')).to eq('foo bar')\n      end\n\n      it \"should return the command if it's not quoted\" do\n        expect(provider.extractexe('foo')).to eq('foo')\n      end\n    end\n\n    describe \"when the command has arguments\" do\n      it \"should return the command if it's quoted\" do\n        expect(provider.extractexe('\"foo\" bar baz')).to eq('foo')\n      end\n\n      it \"should return the command if it's quoted and contains spaces\" do\n        expect(provider.extractexe('\"foo bar\" baz \"quux quiz\"')).to eq('foo bar')\n      end\n\n      it \"should return the command if it's not quoted\" do\n        expect(provider.extractexe('foo bar baz')).to eq('foo')\n      end\n    end\n  end\n\n  describe \"#checkexe\" do\n    describe \"when the command is absolute\", :if => Puppet::Util::Platform.windows? do\n      it \"should return if the command exists and is a file\" do\n        command = tmpfile('command')\n        FileUtils.touch(command)\n\n        expect(provider.checkexe(command)).to eq(nil)\n      end\n      it \"should fail if the command doesn't exist\" do\n        command = tmpfile('command')\n\n        expect { provider.checkexe(command) }.to raise_error(ArgumentError, \"Could not find command '#{command}'\")\n      end\n      it \"should fail if the command isn't a file\" do\n        command = tmpfile('command')\n        FileUtils.mkdir(command)\n\n        expect { provider.checkexe(command) }.to raise_error(ArgumentError, \"'#{command}' is a directory, not a file\")\n      end\n    end\n\n    describe \"when the command is relative\" do\n      describe \"and a path is specified\" do\n        before :each do\n          allow(provider).to receive(:which)\n        end\n\n        it \"should search for executables with no extension\" do\n          provider.resource[:path] = [File.expand_path('/bogus/bin')]\n          expect(provider).to receive(:which).with('foo').and_return('foo')\n\n          provider.checkexe('foo')\n        end\n\n        it \"should fail if the command isn't in the path\" do\n          expect { provider.checkexe('foo') }.to raise_error(ArgumentError, \"Could not find command 'foo'\")\n        end\n      end\n\n      it \"should fail if no path is specified\" do\n        expect { provider.checkexe('foo') }.to raise_error(ArgumentError, \"Could not find command 'foo'\")\n      end\n    end\n  end\n\n  describe \"#validatecmd\" do\n    it \"should fail if the command isn't absolute and there is no path\" do\n      expect { provider.validatecmd('foo') }.to raise_error(Puppet::Error, /'foo' is not qualified and no path was specified/)\n    end\n\n    it \"should not fail if the command is absolute and there is no path\" do\n      expect(provider.validatecmd('C:\\foo')).to eq(nil)\n    end\n\n    it \"should not fail if the command is not absolute and there is a path\" do\n      resource[:path] = 'C:\\path;C:\\another_path'\n\n      expect(provider.validatecmd('foo')).to eq(nil)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/exec_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/exec'\nrequire 'puppet_spec/compiler'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Provider::Exec do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n\n  describe \"#extractexe\" do\n    it \"should return the first element of an array\" do\n      expect(subject.extractexe(['one', 'two'])).to eq('one')\n    end\n\n    {\n      # double-quoted commands\n      %q{\"/has whitespace\"}            => \"/has whitespace\",\n      %q{\"/no/whitespace\"}             => \"/no/whitespace\",\n      # singe-quoted commands\n      %q{'/has whitespace'}            => \"/has whitespace\",\n      %q{'/no/whitespace'}             => \"/no/whitespace\",\n      # combinations\n      %q{\"'/has whitespace'\"}          => \"'/has whitespace'\",\n      %q{'\"/has whitespace\"'}          => '\"/has whitespace\"',\n      %q{\"/has 'special' characters\"}  => \"/has 'special' characters\",\n      %q{'/has \"special\" characters'}  => '/has \"special\" characters',\n      # whitespace split commands\n      %q{/has whitespace}              => \"/has\",\n      %q{/no/whitespace}               => \"/no/whitespace\",\n    }.each do |base_command, exe|\n      ['', ' and args', ' \"and args\"', \" 'and args'\"].each do |args|\n        command = base_command + args\n        it \"should extract #{exe.inspect} from #{command.inspect}\" do\n          expect(subject.extractexe(command)).to eq(exe)\n        end\n      end\n    end\n  end\n\n  context \"when handling sensitive data\" do\n    before :each do\n      Puppet[:log_level] = 'debug'\n    end\n\n    let(:supersecret) { 'supersecret' }\n    let(:path) do\n      if Puppet::Util::Platform.windows?\n        # The `apply_compiled_manifest` helper doesn't add the `path` fact, so\n        # we can't reference that in our manifest. Windows PATHs can contain\n        # double quotes and trailing backslashes, which confuse HEREDOC\n        # interpolation below. So sanitize it:\n        ENV['PATH'].split(File::PATH_SEPARATOR)\n                   .map { |dir| dir.gsub(/\"/, '\\\"').gsub(/\\\\$/, '') }\n                   .map { |dir| Pathname.new(dir).cleanpath.to_s }\n                   .join(File::PATH_SEPARATOR)\n      else\n        ENV['PATH']\n      end\n    end\n\n    def ruby_exit_0\n      \"ruby -e 'exit 0'\"\n    end\n\n    def echo_from_ruby_exit_0(message)\n      # Escape double quotes due to HEREDOC interpolation below\n      \"ruby -e 'puts \\\"#{message}\\\"; exit 0'\".gsub(/\"/, '\\\"')\n    end\n\n    def echo_from_ruby_exit_1(message)\n      # Escape double quotes due to HEREDOC interpolation below\n      \"ruby -e 'puts \\\"#{message}\\\"; exit 1'\".gsub(/\"/, '\\\"')\n    end\n\n    context \"when validating the command\" do\n      it \"redacts the arguments if the command is relative\" do\n        expect {\n          apply_compiled_manifest(<<-MANIFEST)\n            exec { 'echo':\n              command => Sensitive.new('echo #{supersecret}')\n            }\n          MANIFEST\n        }.to raise_error do |err|\n          expect(err).to be_a(Puppet::Error)\n          expect(err.message).to match(/'echo' is not qualified and no path was specified. Please qualify the command or specify a path./)\n          expect(err.message).to_not match(/#{supersecret}/)\n        end\n      end\n\n      it \"redacts the arguments if the command is a directory\" do\n        dir = tmpdir('exec')\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'echo':\n            command => Sensitive.new('#{dir} #{supersecret}'),\n          }\n        MANIFEST\n        expect(@logs).to include(an_object_having_attributes(level: :err, message: /'#{dir}' is a directory, not a file/))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n      end\n\n      it \"redacts the arguments if the command isn't executable\" do\n        file = tmpfile('exec')\n        Puppet::FileSystem.touch(file)\n        Puppet::FileSystem.chmod(0644, file)\n\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'echo':\n            command => Sensitive.new('#{file} #{supersecret}'),\n          }\n        MANIFEST\n        # Execute permission works differently on Windows, but execute will fail since the\n        # file doesn't have a valid extension and isn't a valid executable. The raised error\n        # will be Errno::EIO, which is not useful. The Windows execute code needs to raise\n        # Puppet::Util::Windows::Error so the Win32 error message is preserved.\n        pending(\"PUP-3561 Needs to raise a meaningful Puppet::Error\") if Puppet::Util::Platform.windows?\n        expect(@logs).to include(an_object_having_attributes(level: :err, message: /'#{file}' is not executable/))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n      end\n\n      it \"redacts the arguments if the relative command cannot be resolved using the path parameter\" do\n        file = File.basename(tmpfile('exec'))\n        dir = tmpdir('exec')\n\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'echo':\n            command => Sensitive.new('#{file} #{supersecret}'),\n            path    => \"#{dir}\",\n          }\n        MANIFEST\n        expect(@logs).to include(an_object_having_attributes(level: :err, message: /Could not find command '#{file}'/))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n      end\n    end\n\n    it \"redacts the command on success\", unless: Puppet::Util::Platform.jruby? do\n      command = echo_from_ruby_exit_0(supersecret)\n\n      apply_compiled_manifest(<<-MANIFEST)\n        exec { 'true':\n          command => Sensitive.new(\"#{command}\"),\n          path    => \"#{path}\",\n        }\n      MANIFEST\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing '[redacted]'\", source: /Exec\\[true\\]/))\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\", source: \"Puppet\"))\n      expect(@logs).to include(an_object_having_attributes(level: :notice, message: \"executed successfully\"))\n      expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n    end\n\n    it \"redacts the command on failure\", unless: Puppet::Util::Platform.jruby? do\n      command = echo_from_ruby_exit_1(supersecret)\n\n      apply_compiled_manifest(<<-MANIFEST)\n        exec { 'false':\n          command => Sensitive.new(\"#{command}\"),\n          path    => \"#{path}\",\n        }\n      MANIFEST\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing '[redacted]'\", source: /Exec\\[false\\]/))\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\", source: \"Puppet\"))\n      expect(@logs).to include(an_object_having_attributes(level: :err, message: \"[command redacted] returned 1 instead of one of [0]\"))\n      expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n    end\n\n    context \"when handling checks\", unless: Puppet::Util::Platform.jruby? do\n      let(:onlyifsecret) { \"onlyifsecret\" }\n      let(:unlesssecret) { \"unlesssecret\" }\n\n      it \"redacts command and onlyif outputs\" do\n        onlyif = echo_from_ruby_exit_0(onlyifsecret)\n\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'true':\n            command => Sensitive.new(\"#{ruby_exit_0}\"),\n            onlyif  => Sensitive.new(\"#{onlyif}\"),\n            path    => \"#{path}\",\n          }\n        MANIFEST\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing check '[redacted]'\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing '[redacted]'\", source: /Exec\\[true\\]/))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\", source: \"Puppet\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"[output redacted]\"))\n        expect(@logs).to include(an_object_having_attributes(level: :notice, message: \"executed successfully\"))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{onlyifsecret}/))\n      end\n\n      it \"redacts the command that would have been executed but didn't due to onlyif\" do\n        command = echo_from_ruby_exit_0(supersecret)\n        onlyif = echo_from_ruby_exit_1(onlyifsecret)\n\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'true':\n            command => Sensitive.new(\"#{command}\"),\n            onlyif  => Sensitive.new(\"#{onlyif}\"),\n            path    => \"#{path}\",\n          }\n        MANIFEST\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing check '[redacted]'\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\", source: \"Puppet\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"[output redacted]\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"'[command redacted]' won't be executed because of failed check 'onlyif'\"))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{onlyifsecret}/))\n      end\n\n      it \"redacts command and unless outputs\" do\n        unlesscmd = echo_from_ruby_exit_1(unlesssecret)\n\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'true':\n            command => Sensitive.new(\"#{ruby_exit_0}\"),\n            unless  => Sensitive.new(\"#{unlesscmd}\"),\n            path    => \"#{path}\",\n          }\n        MANIFEST\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing check '[redacted]'\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing '[redacted]'\", source: /Exec\\[true\\]/))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\", source: \"Puppet\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"[output redacted]\"))\n        expect(@logs).to include(an_object_having_attributes(level: :notice, message: \"executed successfully\"))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{unlesssecret}/))\n      end\n\n      it \"redacts the command that would have been executed but didn't due to unless\" do\n        command = echo_from_ruby_exit_0(supersecret)\n        unlesscmd = echo_from_ruby_exit_0(unlesssecret)\n\n        apply_compiled_manifest(<<-MANIFEST)\n          exec { 'true':\n            command => Sensitive.new(\"#{command}\"),\n            unless  => Sensitive.new(\"#{unlesscmd}\"),\n            path    => \"#{path}\",\n          }\n        MANIFEST\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing check '[redacted]'\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Executing: '[redacted]'\", source: \"Puppet\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"[output redacted]\"))\n        expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"'[command redacted]' won't be executed because of failed check 'unless'\"))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))\n        expect(@logs).to_not include(an_object_having_attributes(message: /#{unlesssecret}/))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/file/posix_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).provider(:posix), :if => Puppet.features.posix? do\n  include PuppetSpec::Files\n\n  let(:path) { tmpfile('posix_file_spec') }\n  let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => '0777', :provider => described_class.name }\n  let(:provider) { resource.provider }\n\n  describe \"#mode\" do\n    it \"should return a string with the higher-order bits stripped away\" do\n      FileUtils.touch(path)\n      File.chmod(0644, path)\n\n      expect(provider.mode).to eq('0644')\n    end\n\n    it \"should return absent if the file doesn't exist\" do\n      expect(provider.mode).to eq(:absent)\n    end\n  end\n\n  describe \"#mode=\" do\n    it \"should chmod the file to the specified value\" do\n      FileUtils.touch(path)\n      File.chmod(0644, path)\n\n      provider.mode = '0755'\n\n      expect(provider.mode).to eq('0755')\n    end\n\n    it \"should pass along any errors encountered\" do\n      expect do\n        provider.mode = '0644'\n      end.to raise_error(Puppet::Error, /failed to set mode/)\n    end\n  end\n\n  describe \"#uid2name\" do\n    it \"should return the name of the user identified by the id\" do\n      allow(Etc).to receive(:getpwuid).with(501).and_return(Etc::Passwd.new('jilluser', nil, 501))\n\n      expect(provider.uid2name(501)).to eq('jilluser')\n    end\n\n    it \"should return the argument if it's already a name\" do\n      expect(provider.uid2name('jilluser')).to eq('jilluser')\n    end\n\n    it \"should return nil if the argument is above the maximum uid\" do\n      expect(provider.uid2name(Puppet[:maximum_uid] + 1)).to eq(nil)\n    end\n\n    it \"should return nil if the user doesn't exist\" do\n      expect(Etc).to receive(:getpwuid).and_raise(ArgumentError, \"can't find user for 999\")\n\n      expect(provider.uid2name(999)).to eq(nil)\n    end\n  end\n\n  describe \"#name2uid\" do\n    it \"should return the id of the user if it exists\" do\n      passwd = Etc::Passwd.new('bobbo', nil, 502)\n\n      allow(Etc).to receive(:getpwnam).with('bobbo').and_return(passwd)\n      allow(Etc).to receive(:getpwuid).with(502).and_return(passwd)\n\n      expect(provider.name2uid('bobbo')).to eq(502)\n    end\n\n    it \"should return the argument if it's already an id\" do\n      expect(provider.name2uid('503')).to eq(503)\n    end\n\n    it \"should return false if the user doesn't exist\" do\n      allow(Etc).to receive(:getpwnam).with('chuck').and_raise(ArgumentError, \"can't find user for chuck\")\n\n      expect(provider.name2uid('chuck')).to eq(false)\n    end\n  end\n\n  describe \"#owner\" do\n    it \"should return the uid of the file owner\" do\n      FileUtils.touch(path)\n      owner = Puppet::FileSystem.stat(path).uid\n\n      expect(provider.owner).to eq(owner)\n    end\n\n    it \"should return absent if the file can't be statted\" do\n      expect(provider.owner).to eq(:absent)\n    end\n\n    it \"should warn and return :silly if the value is beyond the maximum uid\" do\n      stat = double('stat', :uid => Puppet[:maximum_uid] + 1)\n      allow(resource).to receive(:stat).and_return(stat)\n\n      expect(provider.owner).to eq(:silly)\n      expect(@logs).to be_any {|log| log.level == :warning and log.message =~ /Apparently using negative UID/}\n    end\n  end\n\n  describe \"#owner=\" do\n    it \"should set the owner but not the group of the file\" do\n      expect(File).to receive(:lchown).with(15, nil, resource[:path])\n\n      provider.owner = 15\n    end\n\n    it \"should chown a link if managing links\" do\n      resource[:links] = :manage\n      expect(File).to receive(:lchown).with(20, nil, resource[:path])\n\n      provider.owner = 20\n    end\n\n    it \"should chown a link target if following links\" do\n      resource[:links] = :follow\n      expect(File).to receive(:chown).with(20, nil, resource[:path])\n\n      provider.owner = 20\n    end\n\n    it \"should pass along any error encountered setting the owner\" do\n      expect(File).to receive(:lchown).and_raise(ArgumentError)\n\n      expect { provider.owner = 25 }.to raise_error(Puppet::Error, /Failed to set owner to '25'/)\n    end\n  end\n\n  describe \"#gid2name\" do\n    it \"should return the name of the group identified by the id\" do\n      allow(Etc).to receive(:getgrgid).with(501).and_return(Etc::Passwd.new('unicorns', nil, nil, 501))\n\n      expect(provider.gid2name(501)).to eq('unicorns')\n    end\n\n    it \"should return the argument if it's already a name\" do\n      expect(provider.gid2name('leprechauns')).to eq('leprechauns')\n    end\n\n    it \"should return nil if the argument is above the maximum gid\" do\n      expect(provider.gid2name(Puppet[:maximum_uid] + 1)).to eq(nil)\n    end\n\n    it \"should return nil if the group doesn't exist\" do\n      expect(Etc).to receive(:getgrgid).and_raise(ArgumentError, \"can't find group for 999\")\n\n      expect(provider.gid2name(999)).to eq(nil)\n    end\n  end\n\n  describe \"#name2gid\" do\n    it \"should return the id of the group if it exists\" do\n      passwd = Etc::Passwd.new('penguins', nil, nil, 502)\n\n      allow(Etc).to receive(:getgrnam).with('penguins').and_return(passwd)\n      allow(Etc).to receive(:getgrgid).with(502).and_return(passwd)\n\n      expect(provider.name2gid('penguins')).to eq(502)\n    end\n\n    it \"should return the argument if it's already an id\" do\n      expect(provider.name2gid('503')).to eq(503)\n    end\n\n    it \"should return false if the group doesn't exist\" do\n      allow(Etc).to receive(:getgrnam).with('wombats').and_raise(ArgumentError, \"can't find group for wombats\")\n\n      expect(provider.name2gid('wombats')).to eq(false)\n    end\n\n  end\n\n  describe \"#group\" do\n    it \"should return the gid of the file group\" do\n      FileUtils.touch(path)\n      group = Puppet::FileSystem.stat(path).gid\n\n      expect(provider.group).to eq(group)\n    end\n\n    it \"should return absent if the file can't be statted\" do\n      expect(provider.group).to eq(:absent)\n    end\n\n    it \"should warn and return :silly if the value is beyond the maximum gid\" do\n      stat = double('stat', :gid => Puppet[:maximum_uid] + 1)\n      allow(resource).to receive(:stat).and_return(stat)\n\n      expect(provider.group).to eq(:silly)\n      expect(@logs).to be_any {|log| log.level == :warning and log.message =~ /Apparently using negative GID/}\n    end\n  end\n\n  describe \"#group=\" do\n    it \"should set the group but not the owner of the file\" do\n      expect(File).to receive(:lchown).with(nil, 15, resource[:path])\n\n      provider.group = 15\n    end\n\n    it \"should change the group for a link if managing links\" do\n      resource[:links] = :manage\n      expect(File).to receive(:lchown).with(nil, 20, resource[:path])\n\n      provider.group = 20\n    end\n\n    it \"should change the group for a link target if following links\" do\n      resource[:links] = :follow\n      expect(File).to receive(:chown).with(nil, 20, resource[:path])\n\n      provider.group = 20\n    end\n\n    it \"should pass along any error encountered setting the group\" do\n      expect(File).to receive(:lchown).and_raise(ArgumentError)\n\n      expect { provider.group = 25 }.to raise_error(Puppet::Error, /Failed to set group to '25'/)\n    end\n  end\n\n  describe \"when validating\" do\n    it \"should not perform any validation\" do\n      resource.validate\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/file/windows_spec.rb",
    "content": "require 'spec_helper'\nif Puppet::Util::Platform.windows?\n  require 'puppet/util/windows'\n  class WindowsSecurity\n    extend Puppet::Util::Windows::Security\n  end\nend\n\ndescribe Puppet::Type.type(:file).provider(:windows), :if => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n\n  let(:path) { tmpfile('windows_file_spec') }\n  let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => '0777', :provider => described_class.name }\n  let(:provider) { resource.provider }\n  let(:sid)      { 'S-1-1-50' }\n  let(:account)  { 'quinn' }\n\n  describe \"#mode\" do\n    it \"should return a string representing the mode in 4-digit octal notation\" do\n      FileUtils.touch(path)\n      WindowsSecurity.set_mode(0644, path)\n\n      expect(provider.mode).to eq('0644')\n    end\n\n    it \"should return absent if the file doesn't exist\" do\n      expect(provider.mode).to eq(:absent)\n    end\n  end\n\n  describe \"#mode=\" do\n    it \"should chmod the file to the specified value\" do\n      FileUtils.touch(path)\n      WindowsSecurity.set_mode(0644, path)\n\n      provider.mode = '0755'\n\n      expect(provider.mode).to eq('0755')\n    end\n\n    it \"should pass along any errors encountered\" do\n      expect do\n        provider.mode = '0644'\n      end.to raise_error(Puppet::Error, /failed to set mode/)\n    end\n  end\n\n  describe \"#id2name\" do\n    it \"should return the name of the user identified by the sid\" do\n      expect(Puppet::Util::Windows::SID).to receive(:valid_sid?).with(sid).and_return(true)\n      expect(Puppet::Util::Windows::SID).to receive(:sid_to_name).with(sid).and_return(account)\n\n      expect(provider.id2name(sid)).to eq(account)\n    end\n\n    it \"should return the argument if it's already a name\" do\n      expect(Puppet::Util::Windows::SID).to receive(:valid_sid?).with(account).and_return(false)\n      expect(Puppet::Util::Windows::SID).not_to receive(:sid_to_name)\n\n      expect(provider.id2name(account)).to eq(account)\n    end\n\n    it \"should return nil if the user doesn't exist\" do\n      expect(Puppet::Util::Windows::SID).to receive(:valid_sid?).with(sid).and_return(true)\n      expect(Puppet::Util::Windows::SID).to receive(:sid_to_name).with(sid).and_return(nil)\n\n      expect(provider.id2name(sid)).to eq(nil)\n    end\n  end\n\n  describe \"#name2id\" do\n    it \"should delegate to name_to_sid\" do\n      expect(Puppet::Util::Windows::SID).to receive(:name_to_sid).with(account).and_return(sid)\n\n      expect(provider.name2id(account)).to eq(sid)\n    end\n  end\n\n  describe \"#owner\" do\n    it \"should return the sid of the owner if the file does exist\" do\n      FileUtils.touch(resource[:path])\n      allow(provider).to receive(:get_owner).with(resource[:path]).and_return(sid)\n\n      expect(provider.owner).to eq(sid)\n    end\n\n    it \"should return absent if the file doesn't exist\" do\n      expect(provider.owner).to eq(:absent)\n    end\n  end\n\n  describe \"#owner=\" do\n    it \"should set the owner to the specified value\" do\n      expect(provider).to receive(:set_owner).with(sid, resource[:path])\n      provider.owner = sid\n    end\n\n    it \"should propagate any errors encountered when setting the owner\" do\n      allow(provider).to receive(:set_owner).and_raise(ArgumentError)\n\n      expect {\n        provider.owner = sid\n      }.to raise_error(Puppet::Error, /Failed to set owner/)\n    end\n  end\n\n  describe \"#group\" do\n    it \"should return the sid of the group if the file does exist\" do\n      FileUtils.touch(resource[:path])\n      allow(provider).to receive(:get_group).with(resource[:path]).and_return(sid)\n\n      expect(provider.group).to eq(sid)\n    end\n\n    it \"should return absent if the file doesn't exist\" do\n      expect(provider.group).to eq(:absent)\n    end\n  end\n\n  describe \"#group=\" do\n    it \"should set the group to the specified value\" do\n      expect(provider).to receive(:set_group).with(sid, resource[:path])\n      provider.group = sid\n    end\n\n    it \"should propagate any errors encountered when setting the group\" do\n      allow(provider).to receive(:set_group).and_raise(ArgumentError)\n\n      expect {\n        provider.group = sid\n      }.to raise_error(Puppet::Error, /Failed to set group/)\n    end\n  end\n\n  describe \"when validating\" do\n    {:owner => 'foo', :group => 'foo', :mode => '0777'}.each do |k,v|\n      it \"should fail if the filesystem doesn't support ACLs and we're managing #{k}\" do\n        allow_any_instance_of(described_class).to receive(:supports_acl?).and_return(false)\n\n        expect {\n          Puppet::Type.type(:file).new :path => path, k => v\n        }.to raise_error(Puppet::Error, /Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS/)\n      end\n    end\n\n    it \"should not fail if the filesystem doesn't support ACLs and we're not managing permissions\" do\n      allow_any_instance_of(described_class).to receive(:supports_acl?).and_return(false)\n\n      Puppet::Type.type(:file).new :path => path\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/group/aix_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Group::Provider::Aix' do\n  let(:provider_class) { Puppet::Type.type(:group).provider(:aix) }\n\n  let(:resource) do\n    Puppet::Type.type(:group).new(\n      :name   => 'test_aix_user',\n      :ensure => :present\n    )\n  end\n\n  let(:provider) do\n    provider_class.new(resource)\n  end\n\n  describe '.find' do\n    let(:groups) do\n      objects = [\n        { :name => 'group1', :id => '1' },\n        { :name => 'group2', :id => '2' }\n      ]\n\n      objects\n    end\n\n    let(:ia_module_args) { [ '-R', 'module' ] }\n\n    let(:expected_group) do\n      {\n        :name => 'group1',\n        :gid  => 1\n      }\n    end\n\n    before(:each) do\n      allow(provider_class).to receive(:list_all).with(ia_module_args).and_return(groups)\n    end\n\n    it 'raises an ArgumentError if the group does not exist' do\n      expect do\n        provider_class.find('non_existent_group', ia_module_args)\n      end.to raise_error do |error|\n        expect(error).to be_a(ArgumentError)\n\n        expect(error.message).to match('non_existent_group')\n      end\n    end\n\n    it 'can find the group when passed-in a group name' do\n      expect(provider_class.find('group1', ia_module_args)).to eql(expected_group)\n    end\n\n    it 'can find the group when passed-in the gid' do\n      expect(provider_class.find(1, ia_module_args)).to eql(expected_group)\n    end\n  end\n\n  describe '.users_to_members' do\n    it 'converts the users attribute to the members property' do\n      expect(provider_class.users_to_members('foo,bar'))\n        .to eql(['foo', 'bar'])\n    end\n  end\n\n  describe '.members_to_users' do\n    context 'when auth_membership == true' do\n      before(:each) do\n        resource[:auth_membership] = true\n      end\n\n      it 'returns only the passed-in members' do\n        expect(provider_class.members_to_users(provider, ['user1', 'user2']))\n          .to eql('user1,user2')\n      end\n    end\n\n    context 'when auth_membership == false' do\n      before(:each) do\n        resource[:auth_membership] = false\n\n        allow(provider).to receive(:members).and_return(['user3', 'user1'])\n      end\n\n      it 'adds the passed-in members to the current list of members, filtering out any duplicates' do\n        expect(provider_class.members_to_users(provider, ['user1', 'user2']))\n          .to eql('user1,user2,user3')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/group/directoryservice_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:group).provider(:directoryservice) do\n  let :resource do\n    Puppet::Type.type(:group).new(\n      :title => 'testgroup',\n      :provider => :directoryservice,\n    )\n  end\n\n  let(:provider) { resource.provider }\n\n  it 'should return true for same lists of unordered members' do\n    expect(provider.members_insync?(['user1', 'user2'], ['user2', 'user1'])).to be_truthy\n  end\n\n  it 'should return false when the group currently has no members' do\n    expect(provider.members_insync?([], ['user2', 'user1'])).to be_falsey\n  end\n\n  it 'should return true for the same lists of members irrespective of duplicates' do\n    expect(provider.members_insync?(['user1', 'user2', 'user2'], ['user1', 'user2'])).to be_truthy\n  end\n\n  it \"should return true when current and should members are empty lists\" do\n    expect(provider.members_insync?([], [])).to be_truthy\n  end\n\n  it \"should return true when current is :absent and should members is empty list\" do\n    expect(provider.members_insync?(:absent, [])).to be_truthy\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/provider/group/groupadd_spec.rb",
    "content": "require 'spec_helper'\n\nRSpec::Matchers.define_negated_matcher :excluding, :include\n\ndescribe Puppet::Type.type(:group).provider(:groupadd) do\n  before do\n    allow(described_class).to receive(:command).with(:add).and_return('/usr/sbin/groupadd')\n    allow(described_class).to receive(:command).with(:delete).and_return('/usr/sbin/groupdel')\n    allow(described_class).to receive(:command).with(:modify).and_return('/usr/sbin/groupmod')\n    allow(described_class).to receive(:command).with(:localadd).and_return('/usr/sbin/lgroupadd')\n    allow(described_class).to receive(:command).with(:localdelete).and_return('/usr/sbin/lgroupdel')\n    allow(described_class).to receive(:command).with(:localmodify).and_return('/usr/sbin/lgroupmod')\n  end\n\n  let(:resource) { Puppet::Type.type(:group).new(:name => 'mygroup', :provider => provider) }\n  let(:provider) { described_class.new(:name => 'mygroup') }\n  let(:members) { ['user2', 'user1', 'user3'] }\n\n  describe \"#create\" do\n    before do\n      allow(provider).to receive(:exists?).and_return(false)\n    end\n\n    it \"should add -o when allowdupe is enabled and the group is being created\" do\n      resource[:allowdupe] = :true\n      expect(provider).to receive(:execute).with(['/usr/sbin/groupadd', '-o', 'mygroup'], kind_of(Hash))\n      provider.create\n    end\n\n    describe \"on system that feature system_groups\", :if => described_class.system_groups? do\n      it \"should add -r when system is enabled and the group is being created\" do\n        resource[:system] = :true\n        expect(provider).to receive(:execute).with(['/usr/sbin/groupadd', '-r', 'mygroup'], kind_of(Hash))\n        provider.create\n      end\n    end\n\n    describe \"on system that do not feature system_groups\", :unless => described_class.system_groups? do\n      it \"should not add -r when system is enabled and the group is being created\" do\n        resource[:system] = :true\n        expect(provider).to receive(:execute).with(['/usr/sbin/groupadd', 'mygroup'], kind_of(Hash))\n        provider.create\n      end\n    end\n\n    describe \"on systems with libuser\" do\n      before do\n        allow(Puppet.features).to receive(:libuser?).and_return(true)\n      end\n\n      describe \"with forcelocal=true\" do\n        before do\n          described_class.has_feature(:manages_local_users_and_groups)\n          resource[:forcelocal] = :true\n        end\n\n        it \"should use lgroupadd instead of groupadd\" do\n          expect(provider).to receive(:execute).with(including('/usr/sbin/lgroupadd'), hash_including(:custom_environment => hash_including('LIBUSER_CONF')))\n          provider.create\n        end\n\n        it \"should NOT pass -o to lgroupadd\" do\n          resource[:allowdupe] = :true\n          expect(provider).to receive(:execute).with(excluding('-o'), hash_including(:custom_environment => hash_including('LIBUSER_CONF')))\n          provider.create\n        end\n\n        it \"should raise an exception for duplicate GID if allowdupe is not set and duplicate GIDs exist\" do\n          resource[:gid] = 505\n          allow(provider).to receive(:findgroup).and_return(true)\n          expect { provider.create }.to raise_error(Puppet::Error, \"GID 505 already exists, use allowdupe to force group creation\")\n        end\n      end\n\n      describe \"with a list of members\" do\n        before do\n          members.each { |m| allow(Etc).to receive(:getpwnam).with(m).and_return(true) }\n          allow(provider).to receive(:flag).and_return('-M')\n          described_class.has_feature(:manages_members)\n          resource[:forcelocal] = false\n          resource[:members] = members\n        end\n\n        it \"should use lgroupmod to add the members\" do\n          allow(provider).to receive(:execute).with(['/usr/sbin/groupadd', 'mygroup'], hash_including({:failonfail => true, :combine => true, :custom_environment => {}})).and_return(true)\n          expect(provider).to receive(:execute).with(['/usr/sbin/lgroupmod', '-M', members.join(','), 'mygroup'], hash_including(:custom_environment => hash_including('LIBUSER_CONF')))\n          provider.create\n        end\n      end\n    end\n  end\n\n  describe \"#modify\" do\n    before do\n      allow(provider).to receive(:exists?).and_return(true)\n    end\n\n    describe \"on systems with libuser\" do\n      before do\n        allow(Puppet.features).to receive(:libuser?).and_return(true)\n      end\n\n      describe \"with forcelocal=false\" do\n        before do\n          described_class.has_feature(:manages_local_users_and_groups)\n          resource[:forcelocal] = :false\n        end\n\n        it \"should use groupmod\" do\n          expect(provider).to receive(:execute).with(['/usr/sbin/groupmod', '-g', 150, 'mygroup'], hash_including({:failonfail => true, :combine => true, :custom_environment => {}}))\n          provider.gid = 150\n        end\n\n        it \"should pass -o to groupmod\" do\n          resource[:allowdupe] = :true\n          expect(provider).to receive(:execute).with(['/usr/sbin/groupmod', '-g', 150, '-o', 'mygroup'], hash_including({:failonfail => true, :combine => true, :custom_environment => {}}))\n          provider.gid = 150\n        end\n      end\n\n      describe \"with forcelocal=true\" do\n        before do\n          described_class.has_feature(:manages_local_users_and_groups)\n          resource[:forcelocal] = :true\n        end\n\n        it \"should use lgroupmod instead of groupmod\" do\n          expect(provider).to receive(:execute).with(['/usr/sbin/lgroupmod', '-g', 150, 'mygroup'], hash_including(:custom_environment => hash_including('LIBUSER_CONF')))\n          provider.gid = 150\n        end\n\n        it \"should NOT pass -o to lgroupmod\" do\n          resource[:allowdupe] = :true\n          expect(provider).to receive(:execute).with(['/usr/sbin/lgroupmod', '-g', 150, 'mygroup'], hash_including(:custom_environment => hash_including('LIBUSER_CONF')))\n          provider.gid = 150\n        end\n\n        it \"should raise an exception for duplicate GID if allowdupe is not set and duplicate GIDs exist\" do\n          resource[:gid] = 150\n          resource[:allowdupe] = :false\n          allow(provider).to receive(:findgroup).and_return(true)\n          expect { provider.gid = 150 }.to raise_error(Puppet::Error, \"GID 150 already exists, use allowdupe to force group creation\")\n        end\n      end\n\n      describe \"with members=something\" do\n        before do\n          described_class.has_feature(:manages_members)\n          allow(Etc).to receive(:getpwnam).and_return(true)\n          resource[:members] = members\n        end\n\n        describe \"with auth_membership on\" do\n          before { resource[:auth_membership] = true }\n\n          it \"should purge existing users before adding\" do\n            allow(provider).to receive(:members).and_return(members)\n            expect(provider).to receive(:localmodify).with('-m', members.join(','), 'mygroup')\n            provider.modifycmd(:members, ['user1'])\n          end\n        end\n\n        describe \"with auth_membership off\" do\n          before { resource[:auth_membership] = false }\n\n          it \"should add to the existing users\" do\n            allow(provider).to receive(:flag).and_return('-M')\n            new_members = ['user1', 'user2', 'user3', 'user4']\n            allow(provider).to receive(:members).and_return(members)\n            expect(provider).not_to receive(:localmodify).with('-m', members.join(','), 'mygroup')\n            expect(provider).to receive(:execute).with(['/usr/sbin/lgroupmod', '-M', new_members.join(','), 'mygroup'], kind_of(Hash))\n            provider.members = new_members\n          end\n        end\n\n        it \"should validate members\" do\n          expect(Etc).to receive(:getpwnam).with('user3').and_return(true)\n          provider.modifycmd(:members, ['user3'])\n        end\n\n        it \"should validate members list \" do\n          expect(Etc).to receive(:getpwnam).with('user3').and_return(true)\n          expect(Etc).to receive(:getpwnam).with('user4').and_return(true)\n          provider.modifycmd(:members, ['user3', 'user4'])\n        end\n\n        it \"should validate members list separated by commas\" do\n          expect(Etc).to receive(:getpwnam).with('user3').and_return(true)\n          expect(Etc).to receive(:getpwnam).with('user4').and_return(true)\n          provider.modifycmd(:members, ['user3, user4'])\n        end\n\n        it \"should raise is validation fails\" do\n          expect(Etc).to receive(:getpwnam).with('user3').and_throw(ArgumentError)\n          expect { provider.modifycmd(:members, ['user3']) }.to raise_error(ArgumentError)\n        end\n      end\n    end\n  end\n\n  describe \"#gid=\" do\n    it \"should add -o when allowdupe is enabled and the gid is being modified\" do\n      resource[:allowdupe] = :true\n      expect(provider).to receive(:execute).with(['/usr/sbin/groupmod', '-g', 150, '-o', 'mygroup'], hash_including({:failonfail => true, :combine => true, :custom_environment => {}}))\n      provider.gid = 150\n    end\n  end\n\n  describe \"#findgroup\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/group').and_return(true)\n      allow(Puppet::FileSystem).to receive(:each_line).with('/etc/group').and_yield(content)\n    end\n\n    let(:content) { \"sample_group_name:sample_password:sample_gid:sample_user_list\" }\n    let(:output) do\n      {\n        group_name: 'sample_group_name',\n        password: 'sample_password',\n        gid: 'sample_gid',\n        user_list: 'sample_user_list',\n      }\n    end\n\n    [:group_name, :password, :gid, :user_list].each do |key|\n      it \"finds a group by #{key} when asked\" do\n        expect(provider.send(:findgroup, key, \"sample_#{key}\")).to eq(output)\n      end\n    end\n\n    it \"returns false when specified key/value pair is not found\" do\n      expect(provider.send(:findgroup, :group_name, 'invalid_group_name')).to eq(false)\n    end\n\n    it \"reads the group file only once per resource\" do\n      expect(Puppet::FileSystem).to receive(:each_line).with('/etc/group').once\n      5.times { provider.send(:findgroup, :group_name, 'sample_group_name') }\n    end\n  end\n\n  describe \"#delete\" do\n    before do\n      allow(provider).to receive(:exists?).and_return(true)\n    end\n\n    describe \"on systems with the libuser and forcelocal=false\" do\n      before do\n        allow(Puppet.features).to receive(:libuser?).and_return(true)\n      end\n\n      before do\n        described_class.has_feature(:manages_local_users_and_groups)\n        resource[:forcelocal] = :false\n      end\n\n      it \"should use groupdel\" do\n        expect(provider).to receive(:execute).with(['/usr/sbin/groupdel', 'mygroup'], hash_including({:failonfail => true, :combine => true, :custom_environment => {}}))\n        provider.delete\n      end\n    end\n\n    describe \"on systems with the libuser and forcelocal=true\" do\n      before do\n        allow(Puppet.features).to receive(:libuser?).and_return(true)\n      end\n\n      before do\n        described_class.has_feature(:manages_local_users_and_groups)\n        resource[:forcelocal] = :true\n      end\n\n      it \"should use lgroupdel instead of groupdel\" do\n        expect(provider).to receive(:execute).with(['/usr/sbin/lgroupdel', 'mygroup'], hash_including(:custom_environment => hash_including('LIBUSER_CONF')))\n        provider.delete\n      end\n    end\n  end\n\n  describe \"group type :members property helpers\" do\n    describe \"#members_to_s\" do\n      it \"should return an empty string on non-array input\" do\n        [Object.new, {}, 1, :symbol, ''].each do |input|\n          expect(provider.members_to_s(input)).to be_empty\n        end\n      end\n\n      it \"should return an empty string on empty or nil users\" do\n        expect(provider.members_to_s([])).to be_empty\n        expect(provider.members_to_s(nil)).to be_empty\n      end\n\n      it \"should return a user string for a single user\" do\n        expect(provider.members_to_s(['user1'])).to eq('user1')\n      end\n\n      it \"should return a user string for multiple users\" do\n        expect(provider.members_to_s(['user1', 'user2'])).to eq('user1,user2')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/group/ldap_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:group).provider(:ldap) do\n  it \"should have the Ldap provider class as its baseclass\" do\n    expect(described_class.superclass).to equal(Puppet::Provider::Ldap)\n  end\n\n  it \"should manage :posixGroup objectclass\" do\n    expect(described_class.manager.objectclasses).to eq([:posixGroup])\n  end\n\n  it \"should use 'ou=Groups' as its relative base\" do\n    expect(described_class.manager.location).to eq(\"ou=Groups\")\n  end\n\n  it \"should use :cn as its rdn\" do\n    expect(described_class.manager.rdn).to eq(:cn)\n  end\n\n  it \"should map :name to 'cn'\" do\n    expect(described_class.manager.ldap_name(:name)).to eq('cn')\n  end\n\n  it \"should map :gid to 'gidNumber'\" do\n    expect(described_class.manager.ldap_name(:gid)).to eq('gidNumber')\n  end\n\n  it \"should map :members to 'memberUid', to be used by the user ldap provider\" do\n    expect(described_class.manager.ldap_name(:members)).to eq('memberUid')\n  end\n\n  describe \"when being created\" do\n    before do\n      # So we don't try to actually talk to ldap\n      @connection = double('connection')\n      allow(described_class.manager).to receive(:connect).and_yield(@connection)\n    end\n\n    describe \"with no gid specified\" do\n      it \"should pick the first available GID after the largest existing GID\" do\n        low = {:name=>[\"luke\"], :gid=>[\"600\"]}\n        high = {:name=>[\"testing\"], :gid=>[\"640\"]}\n        expect(described_class.manager).to receive(:search).and_return([low, high])\n\n        resource = double('resource', :should => %w{whatever})\n        allow(resource).to receive(:should).with(:gid).and_return(nil)\n        allow(resource).to receive(:should).with(:ensure).and_return(:present)\n        instance = described_class.new(:name => \"luke\", :ensure => :absent)\n        allow(instance).to receive(:resource).and_return(resource)\n\n        expect(@connection).to receive(:add).with(anything, hash_including(\"gidNumber\" => [\"641\"]))\n\n        instance.create\n        instance.flush\n      end\n\n      it \"should pick '501' as its GID if no groups are found\" do\n        expect(described_class.manager).to receive(:search).and_return(nil)\n\n        resource = double('resource', :should => %w{whatever})\n        allow(resource).to receive(:should).with(:gid).and_return(nil)\n        allow(resource).to receive(:should).with(:ensure).and_return(:present)\n        instance = described_class.new(:name => \"luke\", :ensure => :absent)\n        allow(instance).to receive(:resource).and_return(resource)\n\n        expect(@connection).to receive(:add).with(anything, hash_including(\"gidNumber\" => [\"501\"]))\n\n        instance.create\n        instance.flush\n      end\n    end\n  end\n\n  it \"should have a method for converting group names to GIDs\" do\n    expect(described_class).to respond_to(:name2id)\n  end\n\n  describe \"when converting from a group name to GID\" do\n    it \"should use the ldap manager to look up the GID\" do\n      expect(described_class.manager).to receive(:search).with(\"cn=foo\")\n      described_class.name2id(\"foo\")\n    end\n\n    it \"should return nil if no group is found\" do\n      expect(described_class.manager).to receive(:search).with(\"cn=foo\").and_return(nil)\n      expect(described_class.name2id(\"foo\")).to be_nil\n      expect(described_class.manager).to receive(:search).with(\"cn=bar\").and_return([])\n      expect(described_class.name2id(\"bar\")).to be_nil\n    end\n\n    # We shouldn't ever actually have more than one gid, but it doesn't hurt\n    # to test for the possibility.\n    it \"should return the first gid from the first returned group\" do\n      expect(described_class.manager).to receive(:search).with(\"cn=foo\").and_return([{:name => \"foo\", :gid => [10, 11]}, {:name => :bar, :gid => [20, 21]}])\n      expect(described_class.name2id(\"foo\")).to eq(10)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/group/pw_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:group).provider(:pw) do\n  let :resource do\n    Puppet::Type.type(:group).new(:name => \"testgroup\", :provider => :pw)\n  end\n\n  let :provider do\n    resource.provider\n  end\n\n  describe \"when creating groups\" do\n    let :provider do\n      prov = resource.provider\n      expect(prov).to receive(:exists?).and_return(nil)\n      prov\n    end\n\n    it \"should run pw with no additional flags when no properties are given\" do\n      expect(provider.addcmd).to eq([described_class.command(:pw), \"groupadd\", \"testgroup\"])\n      expect(provider).to receive(:execute).with([described_class.command(:pw), \"groupadd\", \"testgroup\"], kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -o when allowdupe is enabled\" do\n      resource[:allowdupe] = true\n      expect(provider).to receive(:execute).with(include(\"-o\"), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -g with the correct argument when the gid property is set\" do\n      resource[:gid] = 12345\n      expect(provider).to receive(:execute).with(include(\"-g\") & include(12345), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -M with the correct argument when the members property is set\" do\n      resource[:members] = \"user1\"\n      expect(provider).to receive(:execute).with(include(\"-M\") & include(\"user1\"), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -M with all the given users when the members property is set to an array\" do\n      resource[:members] = [\"user1\", \"user2\"]\n      expect(provider).to receive(:execute).with(include(\"-M\") & include(\"user1,user2\"), kind_of(Hash))\n      provider.create\n    end\n  end\n\n  describe \"when deleting groups\" do\n    it \"should run pw with no additional flags\" do\n      expect(provider).to receive(:exists?).and_return(true)\n      expect(provider.deletecmd).to eq([described_class.command(:pw), \"groupdel\", \"testgroup\"])\n      expect(provider).to receive(:execute).with([described_class.command(:pw), \"groupdel\", \"testgroup\"], hash_including(:custom_environment => {}))\n      provider.delete\n    end\n  end\n\n  describe \"when modifying groups\" do\n    it \"should run pw with the correct arguments\" do\n      expect(provider.modifycmd(\"gid\", 12345)).to eq([described_class.command(:pw), \"groupmod\", \"testgroup\", \"-g\", 12345])\n      expect(provider).to receive(:execute).with([described_class.command(:pw), \"groupmod\", \"testgroup\", \"-g\", 12345], hash_including(:custom_environment => {}))\n      provider.gid = 12345\n    end\n\n    it \"should use -M with the correct argument when the members property is changed\" do\n      resource[:members] = \"user1\"\n      expect(provider).to receive(:execute).with(include(\"-M\") & include(\"user2\"), hash_including(:custom_environment, {}))\n      provider.members = \"user2\"\n    end\n\n    it \"should use -M with all the given users when the members property is changed with an array\" do\n      resource[:members] = [\"user1\", \"user2\"]\n      expect(provider).to receive(:execute).with(include(\"-M\") & include(\"user3,user4\"), hash_including(:custom_environment, {}))\n      provider.members = [\"user3\", \"user4\"]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/group/windows_adsi_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:group).provider(:windows_adsi), :if => Puppet::Util::Platform.windows? do\n  let(:resource) do\n    Puppet::Type.type(:group).new(\n      :title => 'testers',\n      :provider => :windows_adsi\n    )\n  end\n\n  let(:provider) { resource.provider }\n  let(:connection) { double('connection') }\n\n  before :each do\n    allow(Puppet::Util::Windows::ADSI).to receive(:computer_name).and_return('testcomputername')\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(connection)\n    # this would normally query the system, but not needed for these tests\n    allow(Puppet::Util::Windows::ADSI::Group).to receive(:localized_domains).and_return([])\n  end\n\n  describe \".instances\" do\n    it \"should enumerate all groups\" do\n      names = ['group1', 'group2', 'group3']\n      stub_groups = names.map{|n| double(:name => n)}\n\n      allow(connection).to receive(:execquery).with('select name from win32_group where localaccount = \"TRUE\"').and_return(stub_groups)\n\n      expect(described_class.instances.map(&:name)).to match(names)\n    end\n  end\n\n  describe \"group type :members property helpers\" do\n    let(:user1) { double(:account => 'user1', :domain => '.', :sid => 'user1sid') }\n    let(:user2) { double(:account => 'user2', :domain => '.', :sid => 'user2sid') }\n    let(:user3) { double(:account => 'user3', :domain => '.', :sid => 'user3sid') }\n    let(:user_without_domain) { double(:account => 'user_without_domain', :domain => nil, :sid => 'user_without_domain_sid') }\n\n    let(:invalid_user) { SecureRandom.uuid }\n    let(:invalid_user_principal) { double(:account => \"#{invalid_user}\", :domain => nil, :sid => \"#{invalid_user}\") }\n\n    before :each do\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user1', any_args).and_return(user1)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user2', any_args).and_return(user2)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user3', any_args).and_return(user3)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user_without_domain', any_args).and_return(user_without_domain)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(invalid_user).and_return(nil)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(invalid_user, true).and_return(invalid_user_principal)\n    end\n\n    describe \"#members_insync?\" do\n      it \"should return true for same lists of members\" do\n        current = [\n          'user1',\n          'user2',\n        ]\n        expect(provider.members_insync?(current, ['user1', 'user2'])).to be_truthy\n      end\n\n      it \"should return true for same lists of unordered members\" do\n        current = [\n          'user1',\n          'user2',\n        ]\n        expect(provider.members_insync?(current, ['user2', 'user1'])).to be_truthy\n      end\n\n      it \"should return true for same lists of members irrespective of duplicates\" do\n        current = [\n          'user1',\n          'user2',\n          'user2',\n        ]\n        expect(provider.members_insync?(current, ['user2', 'user1', 'user1'])).to be_truthy\n      end\n\n      it \"should return true when current and should members are empty lists\" do\n        expect(provider.members_insync?([], [])).to be_truthy\n      end\n\n      # invalid scenarios\n      #it \"should return true when current and should members are nil lists\" do\n      #it \"should return true when current members is nil and should members is empty\" do\n\n      it \"should return true when current members is empty and should members is nil\" do\n        expect(provider.members_insync?([], nil)).to be_truthy\n      end\n\n      context \"when auth_membership => true\" do\n        before :each do\n          resource[:auth_membership] = true\n        end\n\n        it \"should return true when current and should contain the same users in a different order\" do\n          current = [\n            'user1',\n            'user2',\n            'user3',\n          ]\n          expect(provider.members_insync?(current, ['user3', 'user1', 'user2'])).to be_truthy\n        end\n\n        it \"should return false when current is nil\" do\n          expect(provider.members_insync?(nil, ['user2'])).to be_falsey\n        end\n\n        it \"should return false when should is nil\" do\n          current = [\n            'user1',\n          ]\n          expect(provider.members_insync?(current, nil)).to be_falsey\n        end\n\n        it \"should return false when current contains different users than should\" do\n          current = [\n            'user1',\n          ]\n          expect(provider.members_insync?(current, ['user2'])).to be_falsey\n        end\n\n        it \"should return false when current contains members and should is empty\" do\n          current = [\n            'user1',\n          ]\n          expect(provider.members_insync?(current, [])).to be_falsey\n        end\n\n        it \"should return false when current is empty and should contains members\" do\n          expect(provider.members_insync?([], ['user2'])).to be_falsey\n        end\n\n        it \"should return false when should user(s) are not the only items in the current\" do\n          current = [\n            'user1',\n            'user2',\n          ]\n          expect(provider.members_insync?(current, ['user1'])).to be_falsey\n        end\n\n        it \"should return false when current user(s) is not empty and should is an empty list\" do\n          current = [\n            'user1',\n            'user2',\n          ]\n          expect(provider.members_insync?(current, [])).to be_falsey\n        end\n      end\n\n      context \"when auth_membership => false\" do\n        before :each do\n          # this is also the default\n          resource[:auth_membership] = false\n        end\n\n        it \"should return false when current is nil\" do\n          expect(provider.members_insync?(nil, ['user2'])).to be_falsey\n        end\n\n        it \"should return true when should is nil\" do\n          current = [\n            'user1',\n          ]\n          expect(provider.members_insync?(current, nil)).to be_truthy\n        end\n\n        it \"should return false when current contains different users than should\" do\n          current = [\n            'user1',\n          ]\n          expect(provider.members_insync?(current, ['user2'])).to be_falsey\n        end\n\n        it \"should return true when current contains members and should is empty\" do\n          current = [\n            'user1',\n          ]\n          expect(provider.members_insync?(current, [])).to be_truthy\n        end\n\n        it \"should return false when current is empty and should contains members\" do\n          expect(provider.members_insync?([], ['user2'])).to be_falsey\n        end\n\n        it \"should return true when current user(s) contains at least the should list\" do\n          current = [\n            'user1',\n            'user2',\n          ]\n          expect(provider.members_insync?(current, ['user1'])).to be_truthy\n        end\n\n        it \"should return true when current user(s) is not empty and should is an empty list\" do\n          current = [\n            'user1',\n            'user2',\n          ]\n          expect(provider.members_insync?(current, [])).to be_truthy\n        end\n\n        it \"should return true when current user(s) contains at least the should list, even unordered\" do\n          current = [\n            'user3',\n            'user1',\n            'user2',\n          ]\n          expect(provider.members_insync?(current, ['user2','user1'])).to be_truthy\n        end\n\n      it \"should return true even if a current user is unresolvable if should is included\" do\n        current = [\n            \"#{invalid_user}\",\n            'user2',\n          ]\n          expect(provider.members_insync?(current, ['user2'])).to be_truthy\n      end\n     end\n    end\n\n    describe \"#members_to_s\" do\n      it \"should return an empty string on non-array input\" do\n        [Object.new, {}, 1, :symbol, ''].each do |input|\n          expect(provider.members_to_s(input)).to be_empty\n        end\n      end\n\n      it \"should return an empty string on empty or nil users\" do\n        expect(provider.members_to_s([])).to be_empty\n        expect(provider.members_to_s(nil)).to be_empty\n      end\n\n      it \"should return a user string like DOMAIN\\\\USER\" do\n        expect(provider.members_to_s(['user1'])).to eq('.\\user1')\n      end\n\n      it \"should return a user string like DOMAIN\\\\USER,DOMAIN2\\\\USER2\" do\n        expect(provider.members_to_s(['user1', 'user2'])).to eq('.\\user1,.\\user2')\n      end\n\n      it \"should return a user string without domain if domain is not set\" do\n        expect(provider.members_to_s(['user_without_domain'])).to eq('user_without_domain')\n      end\n\n      it \"should return the username when it cannot be resolved to a SID (for the sake of resource_harness error messages)\" do\n        expect(provider.members_to_s([invalid_user])).to eq(\"#{invalid_user}\")\n      end\n    end\n  end\n\n  describe \"when managing members\" do\n    let(:user1) { double(:account => 'user1', :domain => '.', :sid => 'user1sid') }\n    let(:user2) { double(:account => 'user2', :domain => '.', :sid => 'user2sid') }\n    let(:user3) { double(:account => 'user3', :domain => '.', :sid => 'user3sid') }\n\n    let(:invalid_user) { SecureRandom.uuid }\n    let(:invalid_user_principal) { double(:account => \"#{invalid_user}\", :domain => nil, :sid => \"#{invalid_user}\") }\n\n    before :each do\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user1', any_args).and_return(user1)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user2', any_args).and_return(user2)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user3', any_args).and_return(user3)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(invalid_user, true).and_return(invalid_user_principal)\n\n      resource[:auth_membership] = true\n    end\n\n    it \"should be able to provide a list of members\" do\n      allow(provider.group).to receive(:members).and_return([\n        'user1',\n        'user2',\n        'user3',\n      ])\n\n      expected_member_sids = [user1.sid, user2.sid, user3.sid]\n      expected_members = ['user1', 'user2', 'user3']\n      allow(provider).to receive(:members_to_s)\n        .with(expected_member_sids)\n        .and_return(expected_members.join(','))\n\n      expect(provider.members).to match_array(expected_members)\n    end\n\n    it \"should be able to handle unresolvable SID in list of members\" do\n      allow(provider.group).to receive(:members).and_return([\n        'user1',\n        \"#{invalid_user}\",\n        'user3',\n      ])\n\n      expected_member_sids = [user1.sid, invalid_user_principal.sid, user3.sid]\n      expected_members = ['user1', \"#{invalid_user}\", 'user3']\n      allow(provider).to receive(:members_to_s)\n        .with(expected_member_sids)\n        .and_return(expected_members.join(','))\n\n      expect(provider.members).to match_array(expected_members)\n    end\n\n    it \"should be able to set group members\" do\n      allow(provider.group).to receive(:members).and_return(['user1', 'user2'])\n\n      member_sids = [\n        double(:account => 'user1', :domain => 'testcomputername', :sid => 1),\n        double(:account => 'user2', :domain => 'testcomputername', :sid => 2),\n        double(:account => 'user3', :domain => 'testcomputername', :sid => 3),\n      ]\n\n      allow(provider.group).to receive(:member_sids).and_return(member_sids[0..1])\n\n      expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user2', any_args).and_return(member_sids[1])\n      expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user3', any_args).and_return(member_sids[2])\n\n      expect(provider.group).to receive(:remove_member_sids).with(member_sids[0])\n      expect(provider.group).to receive(:add_member_sids).with(member_sids[2])\n\n      provider.members = ['user2', 'user3']\n    end\n  end\n\n  describe 'when creating groups' do\n    it \"should be able to create a group\" do\n      resource[:members] = ['user1', 'user2']\n\n      group = double('group')\n      expect(Puppet::Util::Windows::ADSI::Group).to receive(:create).with('testers').and_return(group)\n\n      expect(group).to receive(:commit).ordered\n      # due to PUP-1967, defaultto false will set the default to nil\n      expect(group).to receive(:set_members).with(['user1', 'user2'], nil).ordered\n\n      provider.create\n    end\n\n    it 'should not create a group if a user by the same name exists' do\n      expect(Puppet::Util::Windows::ADSI::Group).to receive(:create).with('testers').and_raise(Puppet::Error.new(\"Cannot create group if user 'testers' exists.\"))\n      expect{ provider.create }.to raise_error( Puppet::Error,\n        /Cannot create group if user 'testers' exists./ )\n    end\n\n    it \"should fail with an actionable message when trying to create an active directory group\" do\n      resource[:name] = 'DOMAIN\\testdomaingroup'\n      expect(Puppet::Util::Windows::ADSI::User).to receive(:exists?).with(resource[:name]).and_return(false)\n      expect(connection).to receive(:Create)\n      expect(connection).to receive(:SetInfo).and_raise( WIN32OLERuntimeError.new(\"(in OLE method `SetInfo': )\\n    OLE error code:8007089A in Active Directory\\n      The specified username is invalid.\\r\\n\\n    HRESULT error code:0x80020009\\n      Exception occurred.\"))\n\n      expect{ provider.create }.to raise_error(Puppet::Error)\n    end\n\n    it 'should commit a newly created group' do\n      expect(provider.group).to receive( :commit )\n\n      provider.flush\n    end\n  end\n\n  it \"should be able to test whether a group exists\" do\n    allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).and_return(nil)\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(double('connection', :Class => 'Group'))\n    expect(provider).to be_exists\n\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(nil)\n    expect(provider).not_to be_exists\n  end\n\n  it \"should be able to delete a group\" do\n    expect(connection).to receive(:Delete).with('group', 'testers')\n\n    provider.delete\n  end\n\n  it 'should not run commit on a deleted group' do\n    expect(connection).to receive(:Delete).with('group', 'testers')\n    expect(connection).not_to receive(:SetInfo)\n\n    provider.delete\n    provider.flush\n  end\n\n  it \"should report the group's SID as gid\" do\n    expect(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('testers').and_return('S-1-5-32-547')\n    expect(provider.gid).to eq('S-1-5-32-547')\n  end\n\n  it \"should fail when trying to manage the gid property\" do\n    expect(provider).to receive(:fail).with(/gid is read-only/)\n    provider.send(:gid=, 500)\n  end\n\n  it \"should prefer the domain component from the resolved SID\" do\n    # must lookup well known S-1-5-32-544 as actual 'Administrators' name may be localized\n    admins_sid_bytes = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]\n    admins_group = Puppet::Util::Windows::SID::Principal.lookup_account_sid(admins_sid_bytes)\n    # prefix just the name like .\\Administrators\n    converted = provider.members_to_s([\".\\\\#{admins_group.account}\"])\n\n    # and ensure equivalent of BUILTIN\\Administrators, without a leading .\n    expect(converted).to eq(admins_group.domain_account)\n    expect(converted[0]).to_not eq('.')\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/ldap_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/provider/ldap'\n\ndescribe Puppet::Provider::Ldap do\n  before do\n    @class = Class.new(Puppet::Provider::Ldap)\n  end\n\n  it \"should be able to define its manager\" do\n    manager = double('manager')\n    expect(Puppet::Util::Ldap::Manager).to receive(:new).and_return(manager)\n    allow(@class).to receive(:mk_resource_methods)\n    expect(manager).to receive(:manages).with(:one)\n    expect(@class.manages(:one)).to equal(manager)\n    expect(@class.manager).to equal(manager)\n  end\n\n  it \"should be able to prefetch instances from ldap\" do\n    expect(@class).to respond_to(:prefetch)\n  end\n\n  it \"should create its resource getter/setter methods when the manager is defined\" do\n    manager = double('manager')\n    expect(Puppet::Util::Ldap::Manager).to receive(:new).and_return(manager)\n    expect(@class).to receive(:mk_resource_methods)\n    allow(manager).to receive(:manages)\n    expect(@class.manages(:one)).to equal(manager)\n  end\n\n  it \"should have an instances method\" do\n    expect(@class).to respond_to(:instances)\n  end\n\n  describe \"when providing a list of instances\" do\n    it \"should convert all results returned from the manager's :search method into provider instances\" do\n      manager = double('manager')\n      allow(@class).to receive(:manager).and_return(manager)\n\n      expect(manager).to receive(:search).and_return(%w{one two three})\n\n      expect(@class).to receive(:new).with(\"one\").and_return(1)\n      expect(@class).to receive(:new).with(\"two\").and_return(2)\n      expect(@class).to receive(:new).with(\"three\").and_return(3)\n\n      expect(@class.instances).to eq([1,2,3])\n    end\n  end\n\n  it \"should have a prefetch method\" do\n    expect(@class).to respond_to(:prefetch)\n  end\n\n  describe \"when prefetching\" do\n    before do\n      @manager = double('manager')\n      allow(@class).to receive(:manager).and_return(@manager)\n\n      @resource = double('resource')\n\n      @resources = {\"one\" => @resource}\n    end\n\n    it \"should find an entry for each passed resource\" do\n      expect(@manager).to receive(:find).with(\"one\").and_return(nil)\n\n      allow(@class).to receive(:new)\n      allow(@resource).to receive(:provider=)\n      @class.prefetch(@resources)\n    end\n\n    describe \"resources that do not exist\" do\n      it \"should create a provider with :ensure => :absent\" do\n        expect(@manager).to receive(:find).with(\"one\").and_return(nil)\n\n        expect(@class).to receive(:new).with({:ensure => :absent}).and_return(\"myprovider\")\n\n        expect(@resource).to receive(:provider=).with(\"myprovider\")\n\n        @class.prefetch(@resources)\n      end\n    end\n\n    describe \"resources that exist\" do\n      it \"should create a provider with the results of the find\" do\n        expect(@manager).to receive(:find).with(\"one\").and_return(\"one\" => \"two\")\n\n        expect(@class).to receive(:new).with({\"one\" => \"two\", :ensure => :present}).and_return(\"myprovider\")\n\n        expect(@resource).to receive(:provider=).with(\"myprovider\")\n\n        @class.prefetch(@resources)\n      end\n\n      it \"should set :ensure to :present in the returned values\" do\n        expect(@manager).to receive(:find).with(\"one\").and_return(\"one\" => \"two\")\n\n        expect(@class).to receive(:new).with({\"one\" => \"two\", :ensure => :present}).and_return(\"myprovider\")\n\n        expect(@resource).to receive(:provider=).with(\"myprovider\")\n\n        @class.prefetch(@resources)\n      end\n    end\n  end\n\n  describe \"when being initialized\" do\n    it \"should fail if no manager has been defined\" do\n      expect { @class.new }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should fail if the manager is invalid\" do\n      manager = double(\"manager\", :valid? => false)\n      allow(@class).to receive(:manager).and_return(manager)\n      expect { @class.new }.to raise_error(Puppet::DevError)\n    end\n\n    describe \"with a hash\" do\n      before do\n        @manager = double(\"manager\", :valid? => true)\n        allow(@class).to receive(:manager).and_return(@manager)\n\n        @resource_class = double('resource_class')\n        allow(@class).to receive(:resource_type).and_return(@resource_class)\n\n        @property_class = double('property_class', :array_matching => :all, :superclass => Puppet::Property)\n        allow(@resource_class).to receive(:attrclass).with(:one).and_return(@property_class)\n        allow(@resource_class).to receive(:valid_parameter?).and_return(true)\n      end\n\n      it \"should store a copy of the hash as its ldap_properties\" do\n        instance = @class.new(:one => :two)\n        expect(instance.ldap_properties).to eq({:one => :two})\n      end\n\n      it \"should only store the first value of each value array for those attributes that do not match all values\" do\n        expect(@property_class).to receive(:array_matching).and_return(:first)\n        instance = @class.new(:one => %w{two three})\n        expect(instance.properties).to eq({:one => \"two\"})\n      end\n\n      it \"should store the whole value array for those attributes that match all values\" do\n        expect(@property_class).to receive(:array_matching).and_return(:all)\n        instance = @class.new(:one => %w{two three})\n        expect(instance.properties).to eq({:one => %w{two three}})\n      end\n\n      it \"should only use the first value for attributes that are not properties\" do\n        # Yay.  hackish, but easier than mocking everything.\n        expect(@resource_class).to receive(:attrclass).with(:a).and_return(Puppet::Type.type(:user).attrclass(:name))\n        allow(@property_class).to receive(:array_matching).and_return(:all)\n\n        instance = @class.new(:one => %w{two three}, :a => %w{b c})\n        expect(instance.properties).to eq({:one => %w{two three}, :a => \"b\"})\n      end\n\n      it \"should discard any properties not valid in the resource class\" do\n        expect(@resource_class).to receive(:valid_parameter?).with(:a).and_return(false)\n        allow(@property_class).to receive(:array_matching).and_return(:all)\n\n        instance = @class.new(:one => %w{two three}, :a => %w{b})\n        expect(instance.properties).to eq({:one => %w{two three}})\n      end\n    end\n  end\n\n  describe \"when an instance\" do\n    before do\n      @manager = double(\"manager\", :valid? => true)\n      allow(@class).to receive(:manager).and_return(@manager)\n      @instance = @class.new\n\n      @property_class = double('property_class', :array_matching => :all, :superclass => Puppet::Property)\n      @resource_class = double('resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:one, :two])\n      allow(@class).to receive(:resource_type).and_return(@resource_class)\n    end\n\n    it \"should have a method for creating the ldap entry\" do\n      expect(@instance).to respond_to(:create)\n    end\n\n    it \"should have a method for removing the ldap entry\" do\n      expect(@instance).to respond_to(:delete)\n    end\n\n    it \"should have a method for returning the class's manager\" do\n      expect(@instance.manager).to equal(@manager)\n    end\n\n    it \"should indicate when the ldap entry already exists\" do\n      @instance = @class.new(:ensure => :present)\n      expect(@instance.exists?).to be_truthy\n    end\n\n    it \"should indicate when the ldap entry does not exist\" do\n      @instance = @class.new(:ensure => :absent)\n      expect(@instance.exists?).to be_falsey\n    end\n\n    describe \"is being flushed\" do\n      it \"should call the manager's :update method with its name, current attributes, and desired attributes\" do\n        allow(@instance).to receive(:name).and_return(\"myname\")\n        allow(@instance).to receive(:ldap_properties).and_return(:one => :two)\n        allow(@instance).to receive(:properties).and_return(:three => :four)\n        expect(@manager).to receive(:update).with(@instance.name, {:one => :two}, {:three => :four})\n        @instance.flush\n      end\n    end\n\n    describe \"is being created\" do\n      before do\n        @rclass = double('resource_class')\n        allow(@rclass).to receive(:validproperties).and_return([:one, :two])\n        @resource = double('resource')\n        allow(@resource).to receive(:class).and_return(@rclass)\n        allow(@resource).to receive(:should).and_return(nil)\n        allow(@instance).to receive(:resource).and_return(@resource)\n      end\n\n      it \"should set its :ensure value to :present\" do\n        @instance.create\n        expect(@instance.properties[:ensure]).to eq(:present)\n      end\n\n      it \"should set all of the other attributes from the resource\" do\n        expect(@resource).to receive(:should).with(:one).and_return(\"oneval\")\n        expect(@resource).to receive(:should).with(:two).and_return(\"twoval\")\n\n        @instance.create\n        expect(@instance.properties[:one]).to eq(\"oneval\")\n        expect(@instance.properties[:two]).to eq(\"twoval\")\n      end\n    end\n\n    describe \"is being deleted\" do\n      it \"should set its :ensure value to :absent\" do\n        @instance.delete\n        expect(@instance.properties[:ensure]).to eq(:absent)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/nameservice/directoryservice_spec.rb",
    "content": "require 'spec_helper'\n\nmodule Puppet::Util::Plist\nend\n\n# We use this as a reasonable way to obtain all the support infrastructure.\n[:group].each do |type_for_this_round|\n  describe Puppet::Type.type(type_for_this_round).provider(:directoryservice) do\n    before do\n      @resource = double(\"resource\")\n      allow(@resource).to receive(:[]).with(:name)\n      @provider = described_class.new(@resource)\n    end\n\n    it \"[#6009] should handle nested arrays of members\" do\n      current = [\"foo\", \"bar\", \"baz\"]\n      desired = [\"foo\", [\"quux\"], \"qorp\"]\n      group   = 'example'\n\n      allow(@resource).to receive(:[]).with(:name).and_return(group)\n      allow(@resource).to receive(:[]).with(:auth_membership).and_return(true)\n      @provider.instance_variable_set(:@property_value_cache_hash,\n                                      { :members => current })\n\n      %w{bar baz}.each do |del|\n        expect(@provider).to receive(:execute).once.\n          with([:dseditgroup, '-o', 'edit', '-n', '.', '-d', del, group])\n      end\n\n      %w{quux qorp}.each do |add|\n        expect(@provider).to receive(:execute).once.\n          with([:dseditgroup, '-o', 'edit', '-n', '.', '-a', add, group])\n      end\n\n      expect { @provider.set(:members, desired) }.to_not raise_error\n    end\n  end\nend\n\ndescribe Puppet::Provider::NameService::DirectoryService do\n  context '.single_report' do\n    it 'should use plist data' do\n      allow(Puppet::Provider::NameService::DirectoryService).to receive(:get_ds_path).and_return('Users')\n      allow(Puppet::Provider::NameService::DirectoryService).to receive(:list_all_present).and_return(\n        ['root', 'user1', 'user2', 'resource_name']\n      )\n      allow(Puppet::Provider::NameService::DirectoryService).to receive(:generate_attribute_hash)\n      allow(Puppet::Provider::NameService::DirectoryService).to receive(:execute)\n      expect(Puppet::Provider::NameService::DirectoryService).to receive(:parse_dscl_plist_data)\n\n      Puppet::Provider::NameService::DirectoryService.single_report('resource_name')\n    end\n  end\n\n  context '.get_exec_preamble' do\n    it 'should use plist data' do\n      allow(Puppet::Provider::NameService::DirectoryService).to receive(:get_ds_path).and_return('Users')\n\n      expect(Puppet::Provider::NameService::DirectoryService.get_exec_preamble('-list')).to include(\"-plist\")\n    end\n  end\n\n  context 'password behavior' do\n    # The below is a binary plist containing a ShadowHashData key which CONTAINS\n    # another binary plist. The nested binary plist contains a 'SALTED-SHA512'\n    # key that contains a base64 encoded salted-SHA512 password hash...\n    let (:binary_plist) { \"bplist00\\324\\001\\002\\003\\004\\005\\006\\a\\bXCRAM-MD5RNT]SALTED-SHA512[RECOVERABLEO\\020 \\231k2\\3360\\200GI\\201\\355J\\216\\202\\215y\\243\\001\\206J\\300\\363\\032\\031\\022\\006\\2359\\024\\257\\217<\\361O\\020\\020F\\353\\at\\377\\277\\226\\276c\\306\\254\\031\\037J(\\235O\\020D\\335\\006{\\3744g@\\377z\\204\\322\\r\\332t\\021\\330\\n\\003\\246K\\223\\356\\034!P\\261\\305t\\035\\346\\352p\\206\\003n\\247MMA\\310\\301Z<\\366\\246\\023\\0161W3\\340\\357\\000\\317T\\t\\301\\311+\\204\\246L7\\276\\370\\320*\\245O\\021\\002\\000k\\024\\221\\270x\\353\\001\\237\\346D}\\377?\\265]\\356+\\243\\v[\\350\\316a\\340h\\376<\\322\\266\\327\\016\\306n\\272r\\t\\212A\\253L\\216\\214\\205\\016\\241 [\\360/\\335\\002#\\\\A\\372\\241a\\261\\346\\346\\\\\\251\\330\\312\\365\\016\\n\\341\\017\\016\\225&;\\322\\\\\\004*\\ru\\316\\372\\a \\362?8\\031\\247\\231\\030\\030\\267\\315\\023\\v\\343{@\\227\\301s\\372h\\212\\000a\\244&\\231\\366\\nt\\277\\2036,\\027bZ+\\223W\\212g\\333`\\264\\331N\\306\\307\\362\\257(^~ b\\262\\247&\\231\\261t\\341\\231%\\244\\247\\203eOt\\365\\271\\201\\273\\330\\350\\363C^A\\327F\\214!\\217hgf\\e\\320k\\260n\\315u~\\336\\371M\\t\\235k\\230S\\375\\311\\303\\240\\351\\037d\\273\\321y\\335=K\\016`_\\317\\230\\2612_\\023K\\036\\350\\v\\232\\323Y\\310\\317_\\035\\227%\\237\\v\\340\\023\\016\\243\\233\\025\\306:\\227\\351\\370\\364x\\234\\231\\266\\367\\016w\\275\\333-\\351\\210}\\375x\\034\\262\\272kRuHa\\362T/F!\\347B\\231O`K\\304\\037'k$$\\245h)e\\363\\365mT\\b\\317\\\\2\\361\\026\\351\\254\\375Jl1~\\r\\371\\267\\352\\2322I\\341\\272\\376\\243^Un\\266E7\\230[VocUJ\\220N\\2116D/\\025f=\\213\\314\\325\\vG}\\311\\360\\377DT\\307m\\261&\\263\\340\\272\\243_\\020\\271rG^BW\\210\\030l\\344\\0324\\335\\233\\300\\023\\272\\225Im\\330\\n\\227*Yv[\\006\\315\\330y'\\a\\321\\373\\273A\\240\\305F{S\\246I#/\\355\\2425\\031\\031GGF\\270y\\n\\331\\004\\023G@\\331\\000\\361\\343\\350\\264$\\032\\355_\\210y\\000\\205\\342\\375\\212q\\024\\004\\026W:\\205 \\363v?\\035\\270L-\\270=\\022\\323\\2003\\v\\336\\277\\t\\237\\356\\374\\n\\267n\\003\\367\\342\\330;\\371S\\326\\016`B6@Njm>\\240\\021%\\336\\345\\002(P\\204Yn\\3279l\\0228\\264\\254\\304\\2528t\\372h\\217\\347sA\\314\\345\\245\\337)]\\000\\b\\000\\021\\000\\032\\000\\035\\000+\\0007\\000Z\\000m\\000\\264\\000\\000\\000\\000\\000\\000\\002\\001\\000\\000\\000\\000\\000\\000\\000\\t\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\002\\270\" }\n\n    # The below is a base64 encoded salted-SHA512 password hash.\n    let (:pw_string) { \"\\335\\006{\\3744g@\\377z\\204\\322\\r\\332t\\021\\330\\n\\003\\246K\\223\\356\\034!P\\261\\305t\\035\\346\\352p\\206\\003n\\247MMA\\310\\301Z<\\366\\246\\023\\0161W3\\340\\357\\000\\317T\\t\\301\\311+\\204\\246L7\\276\\370\\320*\\245\" }\n\n    # The below is a salted-SHA512 password hash in hex.\n    let (:sha512_hash) { 'dd067bfc346740ff7a84d20dda7411d80a03a64b93ee1c2150b1c5741de6ea7086036ea74d4d41c8c15a3cf6a6130e315733e0ef00cf5409c1c92b84a64c37bef8d02aa5' }\n\n    let :plist_path do\n      '/var/db/dslocal/nodes/Default/users/jeff.plist'\n    end\n\n    let :ds_provider do\n      described_class\n    end\n\n    let :shadow_hash_data do\n      {'ShadowHashData' => [binary_plist]}\n    end\n\n    it 'should execute convert_binary_to_hash once when getting the password' do\n      expect(described_class).to receive(:convert_binary_to_hash).and_return({'SALTED-SHA512' => pw_string})\n      expect(Puppet::FileSystem).to receive(:exist?).with(plist_path).once.and_return(true)\n      expect(Puppet::Util::Plist).to receive(:read_plist_file).and_return(shadow_hash_data)\n      described_class.get_password('uid', 'jeff')\n    end\n\n    it 'should fail if a salted-SHA512 password hash is not passed in' do\n      expect {\n        described_class.set_password('jeff', 'uid', 'badpassword')\n      }.to raise_error(RuntimeError, /OS X 10.7 requires a Salted SHA512 hash password of 136 characters./)\n    end\n\n    it 'should convert xml-to-binary and binary-to-xml when setting the pw on >= 10.7' do\n      expect(described_class).to receive(:convert_binary_to_hash).and_return({'SALTED-SHA512' => pw_string})\n      expect(described_class).to receive(:convert_hash_to_binary).and_return(binary_plist)\n      expect(Puppet::FileSystem).to receive(:exist?).with(plist_path).once.and_return(true)\n      expect(Puppet::Util::Plist).to receive(:read_plist_file).and_return(shadow_hash_data)\n      expect(Puppet::Util::Plist).to receive(:write_plist_file).with(shadow_hash_data, plist_path, :binary)\n      described_class.set_password('jeff', 'uid', sha512_hash)\n    end\n\n    it '[#13686] should handle an empty ShadowHashData field in the users plist' do\n      expect(described_class).to receive(:convert_hash_to_binary).and_return(binary_plist)\n      expect(Puppet::FileSystem).to receive(:exist?).with(plist_path).once.and_return(true)\n      expect(Puppet::Util::Plist).to receive(:read_plist_file).and_return({'ShadowHashData' => nil})\n      expect(Puppet::Util::Plist).to receive(:write_plist_file)\n      described_class.set_password('jeff', 'uid', sha512_hash)\n    end\n  end\n\n  context '(#4855) directoryservice group resource failure' do\n    let :provider_class do\n      Puppet::Type.type(:group).provider(:directoryservice)\n    end\n\n    let :group_members do\n      ['root','jeff']\n    end\n\n    let :user_account do\n      ['root']\n    end\n\n    let :stub_resource do\n      double('resource')\n    end\n\n    subject do\n      provider_class.new(stub_resource)\n    end\n\n    before :each do\n      @resource = double(\"resource\")\n      allow(@resource).to receive(:[]).with(:name)\n      @provider = provider_class.new(@resource)\n    end\n\n    it 'should delete a group member if the user does not exist' do\n      allow(stub_resource).to receive(:[]).with(:name).and_return('fake_group')\n      allow(stub_resource).to receive(:name).and_return('fake_group')\n      expect(subject).to receive(:execute).with([:dseditgroup, '-o', 'edit', '-n', '.',\n                                                '-d', 'jeff',\n                                                'fake_group']).and_raise(Puppet::ExecutionFailure, 'it broke')\n      expect(subject).to receive(:execute).with([:dscl, '.', '-delete',\n                                                '/Groups/fake_group', 'GroupMembership',\n                                                'jeff'])\n      subject.remove_unwanted_members(group_members, user_account)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/nameservice_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/nameservice'\nrequire 'puppet/etc'\nrequire 'puppet_spec/character_encoding'\n\nPuppet::Type.newtype(:nameservice_dummytype) do\n  newparam(:name)\n  ensurable\n  newproperty(:foo)\n  newproperty(:bar)\nend\n\nPuppet::Type.type(:nameservice_dummytype).provide(:nameservice_dummyprovider, parent: Puppet::Provider::NameService) do\n  def posixmethod(param)\n    param\n  end\n\n  def modifycmd(param, value)\n    []\n  end\nend\n\ndescribe Puppet::Provider::NameService do\n\n  before :each do\n    provider.class.initvars\n    provider.class.resource_type = faketype\n  end\n\n  # These are values getpwent might give you\n  let :users do\n    [\n      Etc::Passwd.new('root', 'x', 0, 0),\n      Etc::Passwd.new('foo', 'x', 1000, 2000),\n      nil\n    ]\n  end\n\n  # These are values getgrent might give you\n  let :groups do\n    [\n      Etc::Group.new('root', 'x', 0, %w{root}),\n      Etc::Group.new('bin', 'x', 1, %w{root bin daemon}),\n      nil\n    ]\n  end\n\n  # A fake struct besides Etc::Group and Etc::Passwd\n  let :fakestruct do\n    Struct.new(:foo, :bar)\n  end\n\n  # A fake value get<foo>ent might return\n  let :fakeetcobject do\n    fakestruct.new('fooval', 'barval')\n  end\n\n  # The provider sometimes relies on @resource for valid properties so let's\n  # create a fake type with properties that match our fake struct.\n  let :faketype do\n    Puppet::Type.type(:nameservice_dummytype)\n  end\n\n  let :provider do\n    Puppet::Type.type(:nameservice_dummytype).provider(:nameservice_dummyprovider)\n      .new(:name => 'bob', :foo => 'fooval', :bar => 'barval')\n  end\n\n  let :resource do\n    resource = faketype.new(:name => 'bob', :ensure => :present)\n    resource.provider = provider\n    resource\n  end\n\n  # These values simulate what Ruby Etc would return from a host with the \"same\"\n  # user represented in different encodings on disk.\n  let(:utf_8_jose) { \"Jos\\u00E9\"}\n  let(:utf_8_labeled_as_latin_1_jose) { utf_8_jose.dup.force_encoding(Encoding::ISO_8859_1) }\n  let(:valid_latin1_jose) { utf_8_jose.encode(Encoding::ISO_8859_1)}\n  let(:invalid_utf_8_jose) { valid_latin1_jose.dup.force_encoding(Encoding::UTF_8) }\n  let(:escaped_utf_8_jose) { \"Jos\\uFFFD\".force_encoding(Encoding::UTF_8) }\n\n  let(:utf_8_mixed_users) {\n    [\n      Etc::Passwd.new('root', 'x', 0, 0),\n      Etc::Passwd.new('foo', 'x', 1000, 2000),\n      Etc::Passwd.new(utf_8_jose, utf_8_jose, 1001, 2000), # UTF-8 character\n      # In a UTF-8 environment, ruby will return strings labeled as UTF-8 even if they're not valid in UTF-8\n      Etc::Passwd.new(invalid_utf_8_jose, invalid_utf_8_jose, 1002, 2000),\n      nil\n    ]\n  }\n\n  let(:latin_1_mixed_users) {\n    [\n      # In a LATIN-1 environment, ruby will return *all* strings labeled as LATIN-1\n      Etc::Passwd.new('root'.force_encoding(Encoding::ISO_8859_1), 'x', 0, 0),\n      Etc::Passwd.new('foo'.force_encoding(Encoding::ISO_8859_1), 'x', 1000, 2000),\n      Etc::Passwd.new(utf_8_labeled_as_latin_1_jose, utf_8_labeled_as_latin_1_jose, 1002, 2000),\n      Etc::Passwd.new(valid_latin1_jose, valid_latin1_jose, 1001, 2000), # UTF-8 character\n      nil\n    ]\n  }\n\n  describe \"#options\" do\n    it \"should add options for a valid property\" do\n      provider.class.options :foo, :key1 => 'val1', :key2 => 'val2'\n      provider.class.options :bar, :key3 => 'val3'\n      expect(provider.class.option(:foo, :key1)).to eq('val1')\n      expect(provider.class.option(:foo, :key2)).to eq('val2')\n      expect(provider.class.option(:bar, :key3)).to eq('val3')\n    end\n\n    it \"should raise an error for an invalid property\" do\n      expect { provider.class.options :baz, :key1 => 'val1' }.to raise_error(\n        Puppet::Error, 'baz is not a valid attribute for nameservice_dummytype')\n    end\n  end\n\n  describe \"#option\" do\n    it \"should return the correct value\" do\n      provider.class.options :foo, :key1 => 'val1', :key2 => 'val2'\n      expect(provider.class.option(:foo, :key2)).to eq('val2')\n    end\n\n    it \"should symbolize the name first\" do\n      provider.class.options :foo, :key1 => 'val1', :key2 => 'val2'\n      expect(provider.class.option('foo', :key2)).to eq('val2')\n    end\n\n    it \"should return nil if no option has been specified earlier\" do\n      expect(provider.class.option(:foo, :key2)).to be_nil\n    end\n\n    it \"should return nil if no option for that property has been specified earlier\" do\n      provider.class.options :bar, :key2 => 'val2'\n      expect(provider.class.option(:foo, :key2)).to be_nil\n    end\n\n    it \"should return nil if no matching key can be found for that property\" do\n      provider.class.options :foo, :key3 => 'val2'\n      expect(provider.class.option(:foo, :key2)).to be_nil\n    end\n  end\n\n  describe \"#section\" do\n    it \"should raise an error if resource_type has not been set\" do\n      expect(provider.class).to receive(:resource_type).and_return(nil)\n      expect { provider.class.section }.to raise_error Puppet::Error, 'Cannot determine Etc section without a resource type'\n    end\n\n    # the return values are hard coded so I am using types that actually make\n    # use of the nameservice provider\n    it \"should return pw for users\" do\n      provider.class.resource_type = Puppet::Type.type(:user)\n      expect(provider.class.section).to eq('pw')\n    end\n\n    it \"should return gr for groups\" do\n      provider.class.resource_type = Puppet::Type.type(:group)\n      expect(provider.class.section).to eq('gr')\n    end\n  end\n\n  describe \"instances\" do\n    it \"should return a list of objects in UTF-8 with any invalid characters replaced with '?'\" do\n      # These two tests simulate an environment where there are two users with\n      # the same name on disk, but each name is stored on disk in a different\n      # encoding\n      allow(Etc).to receive(:getpwent).and_return(*utf_8_mixed_users)\n      result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do\n        provider.class.instances\n      end\n      expect(result.map(&:name)).to eq(\n        [\n          'root'.force_encoding(Encoding::UTF_8), # started as UTF-8 on disk, returned unaltered as UTF-8\n          'foo'.force_encoding(Encoding::UTF_8), # started as UTF-8 on disk, returned unaltered as UTF-8\n          utf_8_jose, # started as UTF-8 on disk, returned unaltered as UTF-8\n          escaped_utf_8_jose # started as LATIN-1 on disk, but Etc returned as UTF-8 and we escaped invalid chars\n        ]\n      )\n    end\n\n    it \"should have object names in their original encoding/bytes if they would not be valid UTF-8\" do\n      allow(Etc).to receive(:getpwent).and_return(*latin_1_mixed_users)\n      result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ISO_8859_1) do\n        provider.class.instances\n      end\n      expect(result.map(&:name)).to eq(\n        [\n          'root'.force_encoding(Encoding::UTF_8), # started as LATIN-1 on disk, we overrode to UTF-8\n          'foo'.force_encoding(Encoding::UTF_8), # started as LATIN-1 on disk, we overrode to UTF-8\n          utf_8_jose, # started as UTF-8 on disk, returned by Etc as LATIN-1, and we overrode to UTF-8\n          valid_latin1_jose # started as LATIN-1 on disk, returned by Etc as valid LATIN-1, and we leave as LATIN-1\n        ]\n      )\n    end\n\n    it \"should pass the Puppet::Etc :canonical_name Struct member to the constructor\" do\n      users = [ Etc::Passwd.new(invalid_utf_8_jose, invalid_utf_8_jose, 1002, 2000), nil ]\n      allow(Etc).to receive(:getpwent).and_return(*users)\n      expect(provider.class).to receive(:new).with({:name => escaped_utf_8_jose, :canonical_name => invalid_utf_8_jose, :ensure => :present})\n      provider.class.instances\n    end\n  end\n\n  describe \"validate\" do\n    it \"should pass if no check is registered at all\" do\n      expect { provider.class.validate(:foo, 300) }.to_not raise_error\n      expect { provider.class.validate('foo', 300) }.to_not raise_error\n    end\n\n    it \"should pass if no check for that property is registered\" do\n      provider.class.verify(:bar, 'Must be 100') { |val| val == 100 }\n      expect { provider.class.validate(:foo, 300) }.to_not raise_error\n      expect { provider.class.validate('foo', 300) }.to_not raise_error\n    end\n\n    it \"should pass if the value is valid\" do\n      provider.class.verify(:foo, 'Must be 100') { |val| val == 100 }\n      expect { provider.class.validate(:foo, 100) }.to_not raise_error\n      expect { provider.class.validate('foo', 100) }.to_not raise_error\n    end\n\n    it \"should raise an error if the value is invalid\" do\n      provider.class.verify(:foo, 'Must be 100') { |val| val == 100 }\n      expect { provider.class.validate(:foo, 200) }.to raise_error(ArgumentError, 'Invalid value 200: Must be 100')\n      expect { provider.class.validate('foo', 200) }.to raise_error(ArgumentError, 'Invalid value 200: Must be 100')\n    end\n  end\n\n  describe \"getinfo\" do\n    before :each do\n      # with section=foo we'll call Etc.getfoonam instead of getpwnam or getgrnam\n      allow(provider.class).to receive(:section).and_return('foo')\n      resource # initialize the resource so our provider has a @resource instance variable\n    end\n\n    it \"should return a hash if we can retrieve something\" do\n      expect(Puppet::Etc).to receive(:send).with(:getfoonam, 'bob').and_return(fakeetcobject)\n      expect(provider).to receive(:info2hash).with(fakeetcobject).and_return(:foo => 'fooval', :bar => 'barval')\n      expect(provider.getinfo(true)).to eq({:foo => 'fooval', :bar => 'barval'})\n    end\n\n    it \"should return nil if we cannot retrieve anything\" do\n      expect(Puppet::Etc).to receive(:send).with(:getfoonam, 'bob').and_raise(ArgumentError, \"can't find bob\")\n      expect(provider).not_to receive(:info2hash)\n      expect(provider.getinfo(true)).to be_nil\n    end\n\n    # Nameservice instances track the original resource name on disk, before\n    # overriding to UTF-8, in @canonical_name for querying that state on disk\n    # again if needed\n    it \"should use the instance's @canonical_name to query the system\" do\n      provider_instance = provider.class.new(:name => 'foo', :canonical_name => 'original_foo', :ensure => :present)\n      expect(Puppet::Etc).to receive(:send).with(:getfoonam, 'original_foo')\n      provider_instance.getinfo(true)\n    end\n\n    it \"should use the instance's name instead of canonical_name if not supplied during instantiation\" do\n      provider_instance = provider.class.new(:name => 'foo', :ensure => :present)\n      expect(Puppet::Etc).to receive(:send).with(:getfoonam, 'foo')\n      provider_instance.getinfo(true)\n    end\n  end\n\n  describe \"info2hash\" do\n    it \"should return a hash with all properties\" do\n      expect(provider.info2hash(fakeetcobject)).to eq({ :foo => 'fooval', :bar => 'barval' })\n    end\n  end\n\n  describe \"munge\" do\n    it \"should return the input value if no munge method has be defined\" do\n      expect(provider.munge(:foo, 100)).to eq(100)\n    end\n\n    it \"should return the munged value otherwise\" do\n      provider.class.options(:foo, :munge => proc { |x| x*2 })\n      expect(provider.munge(:foo, 100)).to eq(200)\n    end\n  end\n\n  describe \"unmunge\" do\n    it \"should return the input value if no unmunge method has been defined\" do\n      expect(provider.unmunge(:foo, 200)).to eq(200)\n    end\n\n    it \"should return the unmunged value otherwise\" do\n      provider.class.options(:foo, :unmunge => proc { |x| x/2 })\n      expect(provider.unmunge(:foo, 200)).to eq(100)\n    end\n  end\n\n\n  describe \"exists?\" do\n    it \"should return true if we can retrieve anything\" do\n      expect(provider).to receive(:getinfo).with(true).and_return(:foo => 'fooval', :bar => 'barval')\n      expect(provider).to be_exists\n    end\n    it \"should return false if we cannot retrieve anything\" do\n      expect(provider).to receive(:getinfo).with(true).and_return(nil)\n      expect(provider).not_to be_exists\n    end\n  end\n\n  describe \"get\" do\n    it \"should return the correct getinfo value\" do\n      expect(provider).to receive(:getinfo).with(false).and_return(:foo => 'fooval', :bar => 'barval')\n      expect(provider.get(:bar)).to eq('barval')\n    end\n\n    it \"should unmunge the value first\" do\n      provider.class.options(:bar, :munge => proc { |x| x*2}, :unmunge => proc {|x| x/2})\n      expect(provider).to receive(:getinfo).with(false).and_return(:foo => 200, :bar => 500)\n      expect(provider.get(:bar)).to eq(250)\n    end\n\n    it \"should return nil if getinfo cannot retrieve the value\" do\n      expect(provider).to receive(:getinfo).with(false).and_return(:foo => 'fooval', :bar => 'barval')\n      expect(provider.get(:no_such_key)).to be_nil\n    end\n\n  end\n\n  describe \"set\" do\n    before :each do\n      resource # initialize resource so our provider has a @resource object\n      provider.class.verify(:foo, 'Must be 100') { |val| val == 100 }\n    end\n\n    it \"should raise an error on invalid values\" do\n      expect { provider.set(:foo, 200) }.to raise_error(ArgumentError, 'Invalid value 200: Must be 100')\n    end\n\n    it \"should execute the modify command on valid values\" do\n      expect(provider).to receive(:modifycmd).with(:foo, 100).and_return(['/bin/modify', '-f', '100' ])\n      expect(provider).to receive(:execute).with(['/bin/modify', '-f', '100'], hash_including(custom_environment: {}))\n      provider.set(:foo, 100)\n    end\n\n    it \"should munge the value first\" do\n      provider.class.options(:foo, :munge => proc { |x| x*2}, :unmunge => proc {|x| x/2})\n      expect(provider).to receive(:modifycmd).with(:foo, 200).and_return(['/bin/modify', '-f', '200' ])\n      expect(provider).to receive(:execute).with(['/bin/modify', '-f', '200'], hash_including(custom_environment: {}))\n      provider.set(:foo, 100)\n    end\n\n    it \"should fail if the modify command fails\" do\n      expect(provider).to receive(:modifycmd).with(:foo, 100).and_return(['/bin/modify', '-f', '100' ])\n      expect(provider).to receive(:execute).with(['/bin/modify', '-f', '100'], kind_of(Hash)).and_raise(Puppet::ExecutionFailure, \"Execution of '/bin/modify' returned 1: some_failure\")\n      expect { provider.set(:foo, 100) }.to raise_error Puppet::Error, /Could not set foo/\n    end\n  end\n\n  describe \"comments_insync?\" do\n    # comments_insync? overrides Puppet::Property#insync? and will act on an\n    # array containing a should value (the expected value of Puppet::Property\n    # @should)\n    context \"given strings with compatible encodings\" do\n      it \"should return false if the is-value and should-value are not equal\" do\n        is_value = \"foo\"\n        should_value = [\"bar\"]\n        expect(provider.comments_insync?(is_value, should_value)).to be_falsey\n      end\n\n      it \"should return true if the is-value and should-value are equal\" do\n        is_value = \"foo\"\n        should_value = [\"foo\"]\n        expect(provider.comments_insync?(is_value, should_value)).to be_truthy\n      end\n    end\n\n    context \"given strings with incompatible encodings\" do\n      let(:snowman_iso) { \"\\u2603\".force_encoding(Encoding::ISO_8859_1) }\n      let(:snowman_utf8) { \"\\u2603\".force_encoding(Encoding::UTF_8) }\n      let(:snowman_binary) { \"\\u2603\".force_encoding(Encoding::ASCII_8BIT) }\n      let(:arabic_heh_utf8) { \"\\u06FF\".force_encoding(Encoding::UTF_8) }\n\n      it \"should be able to compare unequal strings and return false\" do\n        expect(Encoding.compatible?(snowman_iso, arabic_heh_utf8)).to be_falsey\n        expect(provider.comments_insync?(snowman_iso, [arabic_heh_utf8])).to be_falsey\n      end\n\n      it \"should be able to compare equal strings and return true\" do\n        expect(Encoding.compatible?(snowman_binary, snowman_utf8)).to be_falsey\n        expect(provider.comments_insync?(snowman_binary, [snowman_utf8])).to be_truthy\n      end\n\n      it \"should not manipulate the actual encoding of either string\" do\n        expect(Encoding.compatible?(snowman_binary, snowman_utf8)).to be_falsey\n        provider.comments_insync?(snowman_binary, [snowman_utf8])\n        expect(snowman_binary.encoding).to eq(Encoding::ASCII_8BIT)\n        expect(snowman_utf8.encoding).to eq(Encoding::UTF_8)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/aix_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:aix) do\n  before(:each) do\n    # Create a mock resource\n    @resource = Puppet::Type.type(:package).new(:name => 'mypackage', :ensure => :installed, :source => 'mysource', :provider => :aix)\n\n    @provider = @resource.provider\n  end\n\n  [:install, :uninstall, :latest, :query, :update].each do |method|\n    it \"should have a #{method} method\" do\n      expect(@provider).to respond_to(method)\n    end\n  end\n\n  it \"should uninstall a package\" do\n    expect(@provider).to receive(:installp).with('-gu', 'mypackage')\n    expect(@provider.class).to receive(:pkglist).with({:pkgname => 'mypackage'}).and_return(nil)\n    @provider.uninstall\n  end\n\n  context \"when installing\" do\n    it \"should install a package\" do\n      allow(@provider).to receive(:query).and_return({:name => 'mypackage', :ensure => 'present', :status => :committed})\n      expect(@provider).to receive(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage')\n      @provider.install\n    end\n\n    it \"should install a specific package version\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3.4\")\n      allow(@provider).to receive(:query).and_return({:name => 'mypackage', :ensure => '1.2.3.4', :status => :committed})\n      expect(@provider).to receive(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage 1.2.3.4')\n      @provider.install\n    end\n\n    [:broken, :inconsistent].each do |state|\n      it \"should fail if the installation resulted in a '#{state}' state\" do\n        allow(@provider).to receive(:query).and_return({:name => 'mypackage', :ensure => 'present', :status => state})\n        expect(@provider).to receive(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage')\n        expect { @provider.install }.to raise_error(Puppet::Error, \"Package 'mypackage' is in a #{state} state and requires manual intervention\")\n      end\n    end\n\n    it \"should fail if the specified version is superseded\" do\n      @resource[:ensure] = '1.2.3.3'\n      allow(@provider).to receive(:installp).and_return(<<-OUTPUT)\n+-----------------------------------------------------------------------------+\n                    Pre-installation Verification...\n+-----------------------------------------------------------------------------+\nVerifying selections...done\nVerifying requisites...done\nResults...\n\nWARNINGS\n--------\n  Problems described in this section are not likely to be the source of any\n  immediate or serious failures, but further actions may be necessary or\n  desired.\n\n  Already Installed\n  -----------------\n  The number of selected filesets that are either already installed\n  or effectively installed through superseding filesets is 1.  See\n  the summaries at the end of this installation for details.\n\n  NOTE:  Base level filesets may be reinstalled using the \"Force\"\n  option (-F flag), or they may be removed, using the deinstall or\n  \"Remove Software Products\" facility (-u flag), and then reinstalled.\n\n  << End of Warning Section >>\n\n+-----------------------------------------------------------------------------+\n                   BUILDDATE Verification ...\n+-----------------------------------------------------------------------------+\nVerifying build dates...done\nFILESET STATISTICS\n------------------\n    1  Selected to be installed, of which:\n        1  Already installed (directly or via superseding filesets)\n  ----\n    0  Total to be installed\n\n\nPre-installation Failure/Warning Summary\n----------------------------------------\nName                      Level           Pre-installation Failure/Warning\n-------------------------------------------------------------------------------\nmypackage                 1.2.3.3         Already superseded by 1.2.3.4\n      OUTPUT\n\n      expect { @provider.install }.to raise_error(Puppet::Error, \"aix package provider is unable to downgrade packages\")\n    end\n  end\n\n  context \"when finding the latest version\" do\n    it \"should return the current version when no later version is present\" do\n      allow(@provider).to receive(:latest_info).and_return(nil)\n      allow(@provider).to receive(:properties).and_return({ :ensure => \"1.2.3.4\" })\n      expect(@provider.latest).to eq(\"1.2.3.4\")\n    end\n\n    it \"should return the latest version of a package\" do\n      allow(@provider).to receive(:latest_info).and_return({ :version => \"1.2.3.5\" })\n      expect(@provider.latest).to eq(\"1.2.3.5\")\n    end\n\n    it \"should prefetch the right values\" do\n      allow(Process).to receive(:euid).and_return(0)\n      resource = Puppet::Type.type(:package).\n          new(:name => 'sudo.rte', :ensure => :latest,\n              :source => 'mysource', :provider => :aix)\n\n      allow(resource).to receive(:should).with(:ensure).and_return(:latest)\n      resource.should(:ensure)\n\n      allow(resource.provider.class).to receive(:execute).and_return(<<-END.chomp)\nsudo:sudo.rte:1.7.10.4::I:C:::::N:Configurable super-user privileges runtime::::0::\nsudo:sudo.rte:1.8.6.4::I:T:::::N:Configurable super-user privileges runtime::::0::\nEND\n\n      resource.provider.class.prefetch('sudo.rte' => resource)\n      expect(resource.provider.latest).to eq('1.8.6.4')\n    end\n  end\n\n  it \"update should install a package\" do\n    expect(@provider).to receive(:install).with(false)\n    @provider.update\n  end\n\n  it \"should prefetch when some packages lack sources\" do\n    latest = Puppet::Type.type(:package).new(:name => 'mypackage', :ensure => :latest, :source => 'mysource', :provider => :aix)\n    absent = Puppet::Type.type(:package).new(:name => 'otherpackage', :ensure => :absent, :provider => :aix)\n    allow(Process).to receive(:euid).and_return(0)\n    expect(described_class).to receive(:execute).and_return('mypackage:mypackage.rte:1.8.6.4::I:T:::::N:A Super Cool Package::::0::\\n')\n    described_class.prefetch({ 'mypackage' => latest, 'otherpackage' => absent })\n  end\n\n  context \"when querying instances\" do\n    before(:each) do\n      allow(described_class).to receive(:execute).and_return(<<-END.chomp)\nsysmgt.cim.providers:sysmgt.cim.providers.metrics:2.12.1.1: : :B: :Metrics Providers for AIX OS: : : : : : :1:0:/:\nsysmgt.cim.providers:sysmgt.cim.providers.osbase:2.12.1.1: : :C: :Base Providers for AIX OS: : : : : : :1:0:/:\nopenssl.base:openssl.base:1.0.2.1800: : :?: :Open Secure Socket Layer: : : : : : :0:0:/:\nEND\n    end\n\n    it \"should treat installed packages in broken and inconsistent state as absent\" do\n      installed_packages = described_class.instances.map { |package| package.properties }\n      expected_packages = [{:name => 'sysmgt.cim.providers.metrics', :ensure => :absent, :status => :broken, :provider => :aix},\n                           {:name => 'sysmgt.cim.providers.osbase', :ensure => '2.12.1.1', :status => :committed, :provider => :aix},\n                           {:name => 'openssl.base', :ensure => :absent, :status => :inconsistent, :provider => :aix}]\n\n      expect(installed_packages).to eql(expected_packages)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/appdmg_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:appdmg) do\n  let(:resource) { Puppet::Type.type(:package).new(:name => 'foo', :provider => :appdmg) }\n  let(:provider) { described_class.new(resource) }\n\n  describe \"when installing an appdmg\" do\n    let(:fake_mountpoint) { \"/tmp/dmg.foo\" }\n    let(:fake_hdiutil_plist) { {\"system-entities\" => [{\"mount-point\" => fake_mountpoint}]} }\n\n    before do\n      fh = double('filehandle', path: '/tmp/foo')\n      resource[:source] = \"foo.dmg\"\n      allow(File).to receive(:open).and_yield(fh)\n      allow(Dir).to receive(:mktmpdir).and_return(\"/tmp/testtmp123\")\n      allow(FileUtils).to receive(:remove_entry_secure)\n    end\n\n    describe \"from a remote source\" do\n      let(:tmpdir) { \"/tmp/good123\" }\n\n      before :each do\n        resource[:source] = \"http://fake.puppetlabs.com/foo.dmg\"\n      end\n\n      it \"should call tmpdir and use the returned directory\" do\n        expect(Dir).to receive(:mktmpdir).and_return(tmpdir)\n        allow(Dir).to receive(:entries).and_return([\"foo.app\"])\n        expect(described_class).to receive(:curl) do |*args|\n          expect(args[0]).to eq(\"-o\")\n          expect(args[1]).to include(tmpdir)\n          expect(args).not_to include(\"-k\")\n        end\n        allow(described_class).to receive(:hdiutil).and_return('a plist')\n        expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n        expect(described_class).to receive(:installapp)\n\n        provider.install\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/apt_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:apt) do\n  let(:name) { 'asdf' }\n\n  let(:resource) do\n    Puppet::Type.type(:package).new(\n      :name     => name,\n      :provider => 'apt'\n    )\n  end\n\n  let(:provider) do\n    resource.provider\n  end\n\n  it \"should be the default provider on 'os.family' => Debian\" do\n    expect(Facter).to receive(:value).with('os.family').and_return(\"Debian\")\n    expect(described_class.default?).to be_truthy\n  end\n\n  it \"should be versionable\" do\n    expect(described_class).to be_versionable\n  end\n\n  it \"should use :install to update\" do\n    expect(provider).to receive(:install)\n    provider.update\n  end\n\n  it \"should use 'apt-get remove' to uninstall\" do\n    expect(provider).to receive(:aptget).with(\"-y\", \"-q\", :remove, name)\n    expect(provider).to receive(:properties).and_return({:mark => :none})\n    provider.uninstall\n  end\n\n  it \"should use 'apt-get purge' and 'dpkg purge' to purge\" do\n    expect(provider).to receive(:aptget).with(\"-y\", \"-q\", :remove, \"--purge\", name)\n    expect(provider).to receive(:dpkg).with(\"--purge\", name)\n    expect(provider).to receive(:properties).and_return({:mark => :none})\n    provider.purge\n  end\n\n  it \"should use 'apt-cache policy' to determine the latest version of a package\" do\n    expect(provider).to receive(:aptcache).with(:policy, name).and_return(<<-HERE)\n#{name}:\nInstalled: 1:1.0\nCandidate: 1:1.1\nVersion table:\n1:1.0\n  650 http://ftp.osuosl.org testing/main Packages\n*** 1:1.1\n  100 /var/lib/dpkg/status\n    HERE\n\n    expect(provider.latest).to eq(\"1:1.1\")\n  end\n\n  it \"should print and error and return nil if no policy is found\" do\n    expect(provider).to receive(:aptcache).with(:policy, name).and_return(\"#{name}:\")\n\n    expect(provider).to receive(:err)\n    expect(provider.latest).to be_nil\n  end\n\n  it \"should be able to preseed\" do\n    expect(provider).to respond_to(:run_preseed)\n  end\n\n  it \"should preseed with the provided responsefile when preseeding is called for\" do\n    resource[:responsefile] = '/my/file'\n    expect(Puppet::FileSystem).to receive(:exist?).with('/my/file').and_return(true)\n\n    expect(provider).to receive(:info)\n    expect(provider).to receive(:preseed).with('/my/file')\n\n    provider.run_preseed\n  end\n\n  it \"should not preseed if no responsefile is provided\" do\n    expect(provider).to receive(:info)\n    expect(provider).not_to receive(:preseed)\n\n    provider.run_preseed\n  end\n\n  describe \".instances\" do\n    before do\n      allow(Puppet::Type::Package::ProviderDpkg).to receive(:instances).and_return([provider])\n    end\n\n    context \"when package is manual marked\" do\n      before do\n        allow(described_class).to receive(:aptmark).with('showmanual').and_return(\"#{resource.name}\\n\")\n      end\n\n      it 'sets mark to manual' do\n        expect(described_class.instances.map(&:mark)).to eq([:manual])\n      end\n    end\n\n    context 'when package is not manual marked ' do\n      before do\n        allow(described_class).to receive(:aptmark).with('showmanual').and_return('')\n      end\n\n      it 'does not set mark to manual' do\n        expect(described_class.instances.map(&:mark)).to eq([nil])\n      end\n    end\n  end\n\n  describe 'query' do\n    before do\n      allow(provider).to receive(:dpkgquery).and_return(\"name: #{resource.name}\" )\n    end\n\n\n    context 'when package is not installed on the system' do\n      it 'does not set mark to manual' do\n        result = provider.query\n\n        expect(described_class).not_to receive(:aptmark)\n        expect(result[:mark]).to be_nil\n      end\n    end\n  end\n\n  describe 'flush' do\n\n    context \"when package is manual marked\" do\n      before do\n        provider.mark = :manual\n      end\n\n      it 'does not call aptmark' do\n        expect(provider).not_to receive(:aptmark)\n        provider.flush\n      end\n    end\n\n    context 'when package is not manual marked ' do\n      it 'calls aptmark' do\n        expect(described_class).to receive(:aptmark).with('manual', resource.name)\n        provider.flush\n      end\n    end\n  end\n\n  describe \"when installing\" do\n    it \"should preseed if a responsefile is provided\" do\n      resource[:responsefile] = \"/my/file\"\n      expect(provider).to receive(:run_preseed)\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n      allow(provider).to receive(:aptget)\n      provider.install\n    end\n\n    it \"should check for a cdrom\" do\n      expect(provider).to receive(:checkforcdrom)\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n      allow(provider).to receive(:aptget)\n      provider.install\n    end\n\n    it \"should use 'apt-get install' with the package name if no version is asked for\" do\n      resource[:ensure] = :installed\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command[-1]).to eq(name)\n        expect(command[-2]).to eq(:install)\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should specify the package version if one is asked for\" do\n      resource[:ensure] = '1.0'\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command[-1]).to eq(\"#{name}=1.0\")\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should select latest available version if range is specified\" do\n      resource[:ensure] = '>60.0'\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command[-1]).to eq(\"#{name}=72.0.1+build1-0ubuntu0.19.04.1\")\n      end\n      expect(provider).to receive(:aptcache).with(:madison, name).and_return(<<-HERE)\n   #{name} | 72.0.1+build1-0ubuntu0.19.04.1 | http://ro.archive.ubuntu.com/ubuntu disco-updates/main amd64 Packages\n   #{name} | 72.0.1+build1-0ubuntu0.19.04.1 | http://security.ubuntu.com/ubuntu disco-security/main amd64 Packages\n   #{name} | 66.0.3+build1-0ubuntu1 | http://ro.archive.ubuntu.com/ubuntu disco/main amd64 Packages\n    HERE\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should pass through ensure is no version can be selected\" do\n      resource[:ensure] = '>74.0'\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command[-1]).to eq(\"#{name}=>74.0\")\n      end\n      expect(provider).to receive(:aptcache).with(:madison, name).and_return(<<-HERE)\n   #{name} | 72.0.1+build1-0ubuntu0.19.04.1 | http://ro.archive.ubuntu.com/ubuntu disco-updates/main amd64 Packages\n   #{name} | 72.0.1+build1-0ubuntu0.19.04.1 | http://security.ubuntu.com/ubuntu disco-security/main amd64 Packages\n   #{name} | 66.0.3+build1-0ubuntu1 | http://ro.archive.ubuntu.com/ubuntu disco/main amd64 Packages\n    HERE\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should use --force-yes if a package version is specified\" do\n      resource[:ensure] = '1.0'\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command).to include(\"--force-yes\")\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should do a quiet install\" do\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command).to include(\"-q\")\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should default to 'yes' for all questions\" do\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command).to include(\"-y\")\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should keep config files if asked\" do\n      resource[:configfiles] = :keep\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command).to include(\"DPkg::Options::=--force-confold\")\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should replace config files if asked\" do\n      resource[:configfiles] = :replace\n      expect(provider).to receive(:aptget) do |*command|\n        expect(command).to include(\"DPkg::Options::=--force-confnew\")\n      end\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it 'should support string install options' do\n      resource[:install_options] = ['--foo', '--bar']\n      expect(provider).to receive(:aptget).with('-q', '-y', '-o', 'DPkg::Options::=--force-confold', '--foo', '--bar', :install, name)\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it 'should support hash install options' do\n      resource[:install_options] = ['--foo', { '--bar' => 'baz', '--baz' => 'foo' }]\n      expect(provider).to receive(:aptget).with('-q', '-y', '-o', 'DPkg::Options::=--force-confold', '--foo', '--bar=baz', '--baz=foo', :install, name)\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should install using the source attribute if present\" do\n      resource[:ensure] = :installed\n      resource[:source] = '/my/local/package/file'\n\n      expect(provider).to receive(:aptget).with(any_args, :install, resource[:source])\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n\n      provider.install\n    end\n\n    it \"should install specific version using the source attribute if present\" do\n      resource[:ensure] = '1.2.3'\n      resource[:source] = '/my/local/package/file'\n\n      expect(provider).to receive(:aptget).with(any_args, :install, resource[:source])\n      expect(provider).to receive(:properties).and_return({:mark => :none})\n      expect(provider).to receive(:query).and_return({:ensure => '1.2.3'})\n\n      provider.install\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/aptitude_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:aptitude) do\n  let :type do Puppet::Type.type(:package) end\n  let :pkg do\n    type.new(:name => 'faff', :provider => :aptitude, :source => '/tmp/faff.deb')\n  end\n\n  it { is_expected.to be_versionable }\n\n  context \"when retrieving ensure\" do\n    let(:dpkgquery_path) { '/bin/dpkg-query' }\n\n    before do\n      allow(Puppet::Util).to receive(:which).with('/usr/bin/dpkg-query').and_return(dpkgquery_path)\n      allow(described_class).to receive(:aptmark).with('showmanual', 'faff').and_return(\"\")\n    end\n\n    { :absent   => \"deinstall ok config-files faff 1.2.3-1\\n\",\n      \"1.2.3-1\" => \"install ok installed faff 1.2.3-1\\n\",\n    }.each do |expect, output|\n      it \"detects #{expect} packages\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          [dpkgquery_path, '-W', '--showformat', \"'${Status} ${Package} ${Version}\\\\n'\", 'faff'],\n          {:failonfail => true, :combine => true, :custom_environment => {}}\n        ).and_return(Puppet::Util::Execution::ProcessOutput.new(output, 0))\n\n        expect(pkg.property(:ensure).retrieve).to eq(expect)\n      end\n    end\n  end\n\n  it \"installs when asked\" do\n    expect(pkg.provider).to receive(:aptitude).\n      with('-y', '-o', 'DPkg::Options::=--force-confold', :install, 'faff').\n      and_return(0)\n    expect(pkg.provider).to receive(:properties).and_return({:mark => :none})\n\n    pkg.provider.install\n  end\n\n  it \"purges when asked\" do\n    expect(pkg.provider).to receive(:aptitude).with('-y', 'purge', 'faff').and_return(0)\n    pkg.provider.purge\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/aptrpm_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:aptrpm) do\n  let :type do Puppet::Type.type(:package) end\n  let :pkg do\n    type.new(:name => 'faff', :provider => :aptrpm, :source => '/tmp/faff.rpm')\n  end\n\n  it { is_expected.to be_versionable }\n\n  context \"when retrieving ensure\" do\n    before(:each) do\n      allow(Puppet::Util).to receive(:which).with(\"rpm\").and_return(\"/bin/rpm\")\n      allow(pkg.provider).to receive(:which).with(\"rpm\").and_return(\"/bin/rpm\")\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/bin/rpm\", \"--version\"], {:combine => true, :custom_environment => {}, :failonfail => true}).and_return(Puppet::Util::Execution::ProcessOutput.new(\"4.10.1\\n\", 0)).at_most(:once)\n    end\n\n    def rpm_args\n      ['-q', 'faff', '--nosignature', '--nodigest', '--qf', \"%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\\\\n\"]\n    end\n\n    it \"should report purged packages\" do\n      expect(pkg.provider).to receive(:rpm).and_raise(Puppet::ExecutionFailure, \"couldn't find rpm\")\n      expect(pkg.property(:ensure).retrieve).to eq(:purged)\n    end\n\n    it \"should report present packages correctly\" do\n      expect(pkg.provider).to receive(:rpm).and_return(\"faff-1.2.3-1 0 1.2.3-1 5 i686\\n\")\n      expect(pkg.property(:ensure).retrieve).to eq(\"1.2.3-1-5\")\n    end\n  end\n\n  it \"should try and install when asked\" do\n    expect(pkg.provider).to receive(:aptget).with('-q', '-y', 'install', 'faff').and_return(0)\n    pkg.provider.install\n  end\n\n  it \"should try and purge when asked\" do\n    expect(pkg.provider).to receive(:aptget).with('-y', '-q', 'remove', '--purge', 'faff').and_return(0)\n    pkg.provider.purge\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/base_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/package'\n\nPuppet::Type.type(:package).provide(:test_base_provider, parent: Puppet::Provider::Package) do\n  def query; end\nend\n\ndescribe Puppet::Provider::Package do\n  let(:provider) {  Puppet::Type.type(:package).provider(:test_base_provider).new }\n\n  it 'returns absent for uninstalled packages when not purgeable' do\n    expect(provider.properties[:ensure]).to eq(:absent)\n  end\n\n  it 'returns purged for uninstalled packages when purgeable' do\n    expect(provider.class).to receive(:feature?).with(:purgeable).and_return(true)\n    expect(provider.properties[:ensure]).to eq(:purged)\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/dnf_spec.rb",
    "content": "require 'spec_helper'\n\n# Note that much of the functionality of the dnf provider is already tested with yum provider tests,\n# as yum is the parent provider.\ndescribe Puppet::Type.type(:package).provider(:dnf) do\n  context 'default' do\n    (19..21).each do |ver|\n      it \"should not be the default provider on fedora#{ver}\" do\n        allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n        allow(Facter).to receive(:value).with('os.name').and_return(:fedora)\n        allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n        expect(described_class).to_not be_default\n      end\n    end\n\n    (22..26).each do |ver|\n      it \"should be the default provider on fedora#{ver}\" do\n        allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n        allow(Facter).to receive(:value).with('os.name').and_return(:fedora)\n        allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n        expect(described_class).to be_default\n      end\n    end\n\n    it \"should not be the default provider on rhel7\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"7\")\n      expect(described_class).to_not be_default\n    end\n\n    it \"should be the default provider on some random future fedora\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:fedora)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"8675\")\n      expect(described_class).to be_default\n    end\n\n    it \"should be the default provider on rhel8\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"8\")\n      expect(described_class).to be_default\n    end\n\n    it \"should be the default provider on Amazon Linux 2023\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:amazon)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"2023\")\n      expect(described_class).to be_default\n    end\n  end\n\n  describe 'provider features' do\n    it { is_expected.to be_versionable }\n    it { is_expected.to be_install_options }\n    it { is_expected.to be_virtual_packages }\n    it { is_expected.to be_install_only }\n  end\n\n  it_behaves_like 'RHEL package provider', described_class, 'dnf'\nend\n"
  },
  {
    "path": "spec/unit/provider/package/dnfmodule_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:dnfmodule) do\n  include PuppetSpec::Fixtures\n\n  let(:dnf_version) do\n    <<-DNF_OUTPUT\n    4.0.9\n      Installed: dnf-0:4.0.9.2-5.el8.noarch at Wed 29 May 2019 07:05:05 AM GMT\n      Built    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> at Thu 14 Feb 2019 12:04:07 PM GMT\n\n      Installed: rpm-0:4.14.2-9.el8.x86_64 at Wed 29 May 2019 07:04:33 AM GMT\n      Built    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> at Thu 20 Dec 2018 01:30:03 PM GMT\n    DNF_OUTPUT\n  end\n\n  let(:execute_options) do\n    {:failonfail => true, :combine => true, :custom_environment => {}}\n  end\n\n  let(:packages) { File.read(my_fixture(\"dnf-module-list.txt\")) }\n  let(:dnf_path) { '/usr/bin/dnf' }\n\n  before(:each) { allow(Puppet::Util).to receive(:which).with('/usr/bin/dnf').and_return(dnf_path) }\n\n  it \"should have lower specificity\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n    allow(Facter).to receive(:value).with('os.name').and_return(:redhat)\n    allow(Facter).to receive(:value).with('os.release.major').and_return('8')\n    expect(described_class.specificity).to be < 200\n  end\n\n  describe \"should be an opt-in provider\" do\n    Array(4..8).each do |ver|\n      it \"should not be default for redhat #{ver}\" do\n        allow(Facter).to receive(:value).with('os.name').and_return('redhat')\n        allow(Facter).to receive(:value).with('os.family').and_return('redhat')\n        allow(Facter).to receive(:value).with('os.release.major').and_return(ver.to_s)\n        expect(described_class).not_to be_default\n      end\n    end\n  end\n\n  describe \"handling dnf versions\" do\n    before(:each) do\n      expect(Puppet::Type::Package::ProviderDnfmodule).to receive(:execute)\n          .with([\"/usr/bin/dnf\", \"--version\"])\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(dnf_version, 0)).at_most(:once)\n      expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/usr/bin/dnf\", \"--version\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(dnf_version, 0))\n    end\n\n    before(:each) { described_class.instance_variable_set(\"@current_version\", nil) }\n\n    describe \"with a supported dnf version\" do\n      it \"correctly parses the version\" do\n        expect(described_class.current_version).to eq('4.0.9')\n      end\n    end\n\n    describe \"with an unsupported dnf version\" do\n      let(:dnf_version) do\n        <<-DNF_OUTPUT\n        2.7.5\n          Installed: dnf-0:2.7.5-12.fc28.noarch at Mon 13 Aug 2018 11:05:27 PM GMT\n          Built    : Fedora Project at Wed 18 Apr 2018 02:29:51 PM GMT\n\n          Installed: rpm-0:4.14.1-7.fc28.x86_64 at Mon 13 Aug 2018 11:05:25 PM GMT\n          Built    : Fedora Project at Mon 19 Feb 2018 09:29:01 AM GMT\n        DNF_OUTPUT\n      end\n\n      it \"correctly parses the version\" do\n        expect(described_class.current_version).to eq('2.7.5')\n      end\n\n      it \"raises an error when attempting prefetch\" do\n        expect { described_class.prefetch('anything') }.to raise_error(Puppet::Error, \"Modules are not supported on DNF versions lower than 3.0.1\")\n      end\n    end\n  end\n\n  describe \"when ensuring a module\" do\n    let(:name) { 'baz' }\n\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name => name,\n        :provider => 'dnfmodule',\n      )\n    end\n\n    let(:provider) do\n      provider = described_class.new\n      provider.resource = resource\n      provider\n    end\n\n    describe 'provider features' do\n      it { is_expected.to be_versionable }\n      it { is_expected.to be_installable }\n      it { is_expected.to be_uninstallable }\n    end\n\n    context \"when installing a new module\" do\n      before do\n        provider.instance_variable_get('@property_hash')[:ensure] = :absent\n      end\n\n      it \"should not reset the module stream when package is absent\" do\n        resource[:ensure] = :present\n        expect(provider).not_to receive(:uninstall)\n        expect(provider).to receive(:execute)\n        provider.install\n      end\n\n      it \"should not reset the module stream when package is purged\" do\n        provider.instance_variable_get('@property_hash')[:ensure] = :purged\n        resource[:ensure] = :present\n        expect(provider).not_to receive(:uninstall)\n        expect(provider).to receive(:execute)\n        provider.install\n      end\n\n      it \"should just enable the module if it has no default profile (missing groups or modules)\" do\n        dnf_exception = Puppet::ExecutionFailure.new(\"Error: Problems in request:\\nmissing groups or modules: #{resource[:name]}\")\n        allow(provider).to receive(:execute).with(array_including('install')).and_raise(dnf_exception)\n        resource[:ensure] = :present\n        expect(provider).to receive(:execute).with(array_including('install')).ordered\n        expect(provider).to receive(:execute).with(array_including('enable')).ordered\n        provider.install\n      end\n\n      it \"should just enable the module with the right stream if it has no default profile (missing groups or modules)\" do\n        stream = '12.3'\n        dnf_exception = Puppet::ExecutionFailure.new(\"Error: Problems in request:\\nmissing groups or modules: #{resource[:name]}:#{stream}\")\n        allow(provider).to receive(:execute).with(array_including('install')).and_raise(dnf_exception)\n        resource[:ensure] = stream\n        expect(provider).to receive(:execute).with(array_including('install')).ordered\n        expect(provider).to receive(:execute).with(array_including('enable')).ordered\n        provider.install\n      end\n\n      it \"should just enable the module if it has no default profile (broken groups or modules)\" do\n        dnf_exception = Puppet::ExecutionFailure.new(\"Error: Problems in request:\\nbroken groups or modules: #{resource[:name]}\")\n        allow(provider).to receive(:execute).with(array_including('install')).and_raise(dnf_exception)\n        resource[:ensure] = :present\n        expect(provider).to receive(:execute).with(array_including('install')).ordered\n        expect(provider).to receive(:execute).with(array_including('enable')).ordered\n        provider.install\n      end\n\n      it \"should just enable the module with the right stream if it has no default profile (broken groups or modules)\" do\n        stream = '12.3'\n        dnf_exception = Puppet::ExecutionFailure.new(\"Error: Problems in request:\\nbroken groups or modules: #{resource[:name]}:#{stream}\")\n        allow(provider).to receive(:execute).with(array_including('install')).and_raise(dnf_exception)\n        resource[:ensure] = stream\n        expect(provider).to receive(:execute).with(array_including('install')).ordered\n        expect(provider).to receive(:execute).with(array_including('enable')).ordered\n        provider.install\n      end\n\n      it \"should just enable the module if enable_only = true\" do\n        resource[:ensure] = :present\n        resource[:enable_only] = true\n        expect(provider).to receive(:execute).with(array_including('enable'))\n        expect(provider).not_to receive(:execute).with(array_including('install'))\n        provider.install\n      end\n\n      it \"should install the default stream and flavor\" do\n        resource[:ensure] = :present\n        expect(provider).to receive(:execute).with(array_including('baz'))\n        provider.install\n      end\n\n      it \"should install a specific stream\" do\n        resource[:ensure] = '9.6'\n        expect(provider).to receive(:execute).with(array_including('baz:9.6'))\n        provider.install\n      end\n\n      it \"should install a specific flavor\" do\n        resource[:ensure] = :present\n        resource[:flavor] = 'minimal'\n        expect(provider).to receive(:execute).with(array_including('baz/minimal'))\n        provider.install\n      end\n\n      it \"should install a specific flavor and stream\" do\n        resource[:ensure] = '9.6'\n        resource[:flavor] = 'minimal'\n        expect(provider).to receive(:execute).with(array_including('baz:9.6/minimal'))\n        provider.install\n      end\n    end\n\n    context \"when ensuring a specific version on top of another stream\" do\n      before do\n        provider.instance_variable_get('@property_hash')[:ensure] = '9.6'\n      end\n\n      it \"should remove existing packages and reset the module stream before installing\" do\n        resource[:ensure] = '10'\n        expect(provider).to receive(:execute).thrice.with(array_including(/remove|reset|install/))\n        provider.install\n      end\n    end\n\n    context \"with an installed flavor\" do\n      before do\n        provider.instance_variable_get('@property_hash')[:flavor] = 'minimal'\n      end\n\n      it \"should remove existing packages and reset the module stream before installing another flavor\" do\n        resource[:flavor] = 'common'\n        expect(provider).to receive(:execute).thrice.with(array_including(/remove|reset|install/))\n        provider.flavor = resource[:flavor]\n      end\n\n      it \"should not do anything if the flavor doesn't change\" do\n        resource[:flavor] = 'minimal'\n        expect(provider).not_to receive(:execute)\n        provider.flavor = resource[:flavor]\n      end\n\n      it \"should return the existing flavor\" do\n        expect(provider.flavor).to eq('minimal')\n      end\n    end\n\n    context \"when disabling a module\" do\n\n      it \"executed the disable command\" do\n        resource[:ensure] = :disabled\n        expect(provider).to receive(:execute).with(array_including('disable'))\n        provider.disable\n      end\n\n      it \"does not try to disable if package is already disabled\" do\n        allow(described_class).to receive(:command).with(:dnf).and_return(dnf_path)\n        allow(Puppet::Util::Execution).to receive(:execute)\n          .with(\"/usr/bin/dnf module list -y -d 0 -e 1\")\n          .and_return(\"baz 1.2 [d][x] common [d], complete  Package Description\")\n        resource[:ensure] = :disabled\n        expect(provider).to be_insync(:disabled)\n      end\n    end\n  end\n\n  context \"parsing the output of module list\" do\n    before { allow(described_class).to receive(:command).with(:dnf).and_return(dnf_path) }\n\n    it \"returns an array of enabled modules\" do\n      allow(Puppet::Util::Execution).to receive(:execute)\n        .with(\"/usr/bin/dnf module list -y -d 0 -e 1\")\n        .and_return(packages)\n\n      enabled_packages = described_class.instances.map { |package| package.properties }\n      expected_packages = [{name: \"389-ds\", ensure: \"1.4\", flavor: :absent, provider: :dnfmodule},\n                           {name: \"gimp\", ensure: \"2.8\", flavor: \"devel\", provider: :dnfmodule},\n                           {name: \"mariadb\", ensure: \"10.3\", flavor: \"client\", provider: :dnfmodule},\n                           {name: \"nodejs\", ensure: \"10\", flavor: \"minimal\", provider: :dnfmodule},\n                           {name: \"perl\", ensure: \"5.26\", flavor: \"minimal\", provider: :dnfmodule},\n                           {name: \"postgresql\", ensure: \"10\", flavor: \"server\", provider: :dnfmodule},\n                           {name: \"ruby\", ensure: \"2.5\", flavor: :absent, provider: :dnfmodule},\n                           {name: \"rust-toolset\", ensure: \"rhel8\", flavor: \"common\", provider: :dnfmodule},\n                           {name: \"subversion\", ensure: \"1.10\", flavor: \"server\", provider: :dnfmodule},\n                           {name: \"swig\", ensure: :disabled, flavor: :absent, provider: :dnfmodule},\n                           {name: \"virt\", ensure: :disabled, flavor: :absent, provider: :dnfmodule}]\n\n      expect(enabled_packages).to eql(expected_packages)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/dpkg_spec.rb",
    "content": "require 'spec_helper'\nrequire 'stringio'\n\ndescribe Puppet::Type.type(:package).provider(:dpkg), unless: Puppet::Util::Platform.jruby? do\n  let(:bash_version) { '4.2-5ubuntu3' }\n  let(:bash_installed_output) { \"install ok installed bash #{bash_version}\\n\" }\n  let(:bash_installed_io) { StringIO.new(bash_installed_output) }\n  let(:vim_installed_output) { \"install ok installed vim 2:7.3.547-6ubuntu5\\n\" }\n  let(:all_installed_io) { StringIO.new([bash_installed_output, vim_installed_output].join) }\n  let(:args) { ['-W', '--showformat', %Q{'${Status} ${Package} ${Version}\\\\n'}] }\n  let(:args_with_provides) { ['/bin/dpkg-query','-W', '--showformat', %Q{'${Status} ${Package} ${Version} [${Provides}]\\\\n'}]}\n  let(:execute_options) do\n    {:failonfail => true, :combine => true, :custom_environment => {}}\n  end\n  let(:resource_name) { 'python' }\n  let(:resource) { double('resource', :[] => resource_name) }\n  let(:dpkg_query_result) { 'install ok installed python 2.7.13' }\n  let(:provider) { described_class.new(resource) }\n\n  it \"has documentation\" do\n    expect(described_class.doc).to be_instance_of(String)\n  end\n\n  context \"when listing all instances\" do\n    let(:execpipe_args) { args.unshift('myquery') }\n\n    before do\n      allow(described_class).to receive(:command).with(:dpkgquery).and_return('myquery')\n    end\n\n    it \"creates and return an instance for a single dpkg-query entry\" do\n      expect(Puppet::Util::Execution).to receive(:execpipe).with(execpipe_args).and_yield(bash_installed_io)\n\n      installed = double('bash')\n      expect(described_class).to receive(:new).with({:ensure => \"4.2-5ubuntu3\", :error => \"ok\", :desired => \"install\", :name => \"bash\", :mark => :none, :status => \"installed\", :provider => :dpkg}).and_return(installed)\n\n      expect(described_class.instances).to eq([installed])\n    end\n\n    it \"parses multiple dpkg-query multi-line entries in the output\" do\n      expect(Puppet::Util::Execution).to receive(:execpipe).with(execpipe_args).and_yield(all_installed_io)\n\n      bash = double('bash')\n      expect(described_class).to receive(:new).with({:ensure => \"4.2-5ubuntu3\", :error => \"ok\", :desired => \"install\", :name => \"bash\", :mark => :none, :status => \"installed\", :provider => :dpkg}).and_return(bash)\n      vim = double('vim')\n      expect(described_class).to receive(:new).with({:ensure => \"2:7.3.547-6ubuntu5\", :error => \"ok\", :desired => \"install\", :name => \"vim\", :mark => :none, :status => \"installed\", :provider => :dpkg}).and_return(vim)\n\n      expect(described_class.instances).to eq([bash, vim])\n    end\n\n    it \"continues without failing if it encounters bad lines between good entries\" do\n      expect(Puppet::Util::Execution).to receive(:execpipe).with(execpipe_args).and_yield(StringIO.new([bash_installed_output, \"foobar\\n\", vim_installed_output].join))\n\n      bash = double('bash')\n      vim = double('vim')\n      expect(described_class).to receive(:new).twice.and_return(bash, vim)\n\n      expect(described_class.instances).to eq([bash, vim])\n    end\n  end\n\n  context \"when querying the current state\" do\n    let(:dpkgquery_path) { '/bin/dpkg-query' }\n    let(:query_args) do\n      args.unshift(dpkgquery_path)\n      args.push(resource_name)\n    end\n\n    def dpkg_query_execution_returns(output)\n      expect(Puppet::Util::Execution).to receive(:execute).with(query_args, execute_options).and_return(Puppet::Util::Execution::ProcessOutput.new(output, 0))\n    end\n\n    def dpkg_query_execution_with_multiple_args_returns(output, *args)\n      args.each do |arg|\n        allow(Puppet::Util::Execution).to receive(:execute).with(arg, execute_options).and_return(Puppet::Util::Execution::ProcessOutput.new(output, 0))\n      end\n    end\n\n    before do\n      allow(Puppet::Util).to receive(:which).with('/usr/bin/dpkg-query').and_return(dpkgquery_path)\n    end\n\n    it \"considers the package purged if dpkg-query fails\" do\n      allow(resource).to receive(:allow_virtual?).and_return(false)\n      allow(Puppet::Util::Execution).to receive(:execute).with(query_args, execute_options).and_raise(Puppet::ExecutionFailure.new(\"eh\"))\n\n      expect(provider.query[:ensure]).to eq(:purged)\n    end\n\n    context \"allow_virtual true\" do\n      before do\n        allow(resource).to receive(:allow_virtual?).and_return(true)\n      end\n\n      context \"virtual_packages\" do\n        let(:query_output) { 'install ok installed python 2.7.13 [python-ctypes, python-email, python-importlib, python-profiler, python-wsgiref, python-gold]' }\n        let(:virtual_packages_query_args) do\n          result = args_with_provides.dup\n          result.push(resource_name)\n        end\n\n        it \"considers the package purged if dpkg-query fails\" do\n          allow(Puppet::Util::Execution).to receive(:execute).with(args_with_provides, execute_options).and_raise(Puppet::ExecutionFailure.new(\"eh\"))\n          expect(provider.query[:ensure]).to eq(:purged)\n        end\n\n        it \"returns a hash of the found package status for an installed package\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output, args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result, args, query_args)\n          expect(provider.query).to eq(:ensure => \"2.7.13\", :error => \"ok\", :desired => \"install\", :name => \"python\", :mark => :none, :status => \"installed\", :provider => :dpkg)\n        end\n\n        it \"considers the package absent if the dpkg-query result cannot be interpreted\" do\n          dpkg_query_execution_with_multiple_args_returns('some-bad-data',args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns('some-bad-data', args, query_args)\n          expect(provider.query[:ensure]).to eq(:absent)\n        end\n\n        it \"fails if an error is discovered\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"ok\",\"error\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"ok\",\"error\"), args, query_args)\n          expect { provider.query }.to raise_error(Puppet::Error,  /Package python, version 2.7.13 is in error state: error/)\n        end\n\n        it \"considers the package purged if it is marked 'not-installed\" do\n          not_installed_query = query_output.gsub(\"installed\", \"not-installed\").delete!('2.7.13')\n          dpkg_query_execution_with_multiple_args_returns(not_installed_query, args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"installed\", \"not-installed\").delete!('2.7.13'), args, query_args)\n          expect(provider.query[:ensure]).to eq(:purged)\n        end\n\n        it \"considers the package absent if it is marked 'config-files'\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"installed\",\"config-files\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"installed\",\"config-files\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(:absent)\n        end\n\n        it \"considers the package absent if it is marked 'half-installed'\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"installed\",\"half-installed\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"installed\",\"half-installed\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(:absent)\n        end\n\n        it \"considers the package absent if it is marked 'unpacked'\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"installed\",\"unpacked\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"installed\",\"unpacked\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(:absent)\n        end\n\n        it \"considers the package absent if it is marked 'half-configured'\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"installed\",\"half-configured\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"installed\",\"half-configured\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(:absent)\n        end\n\n        it \"considers the package held if its state is 'hold'\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"install\",\"hold\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"install\",\"hold\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(\"2.7.13\")\n          expect(provider.query[:mark]).to eq(:hold)\n        end\n\n        it \"considers the package held if its state is 'hold'\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"install\",\"hold\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"install\",\"hold\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(\"2.7.13\")\n          expect(provider.query[:mark]).to eq(:hold)\n        end\n\n        it \"considers mark status to be none if package is not held\" do\n          dpkg_query_execution_with_multiple_args_returns(query_output.gsub(\"install\",\"ok\"),args_with_provides,virtual_packages_query_args)\n          dpkg_query_execution_with_multiple_args_returns(dpkg_query_result.gsub(\"install\",\"ok\"), args, query_args)\n          expect(provider.query[:ensure]).to eq(\"2.7.13\")\n          expect(provider.query[:mark]).to eq(:none)\n        end\n\n        context \"regex check for query search\" do\n          let(:resource_name) { 'python-email' }\n          let(:resource) { instance_double('Puppet::Type::Package') }\n          before do\n            allow(resource).to receive(:[]).with(:name).and_return(resource_name)\n            allow(resource).to receive(:[]=)\n          end\n\n          it \"checks if virtual package regex for query is correct and physical package is installed\" do\n            dpkg_query_execution_with_multiple_args_returns(query_output,args_with_provides,virtual_packages_query_args)\n            dpkg_query_execution_with_multiple_args_returns(dpkg_query_result, args, query_args)\n            expect(provider.query).to match({:desired => \"install\", :ensure => \"2.7.13\", :error => \"ok\", :name => \"python\", :mark => :none, :provider => :dpkg, :status => \"installed\"})\n          end\n\n          context \"regex check with no partial matching\" do\n            let(:resource_name) { 'python-em' }\n\n            it \"checks if virtual package regex for query is correct and regext dosen't make partial matching\" do\n              expect(provider).to receive(:dpkgquery).with('-W', '--showformat', %Q{'${Status} ${Package} ${Version} [${Provides}]\\\\n'}).and_return(query_output)\n              expect(provider).to receive(:dpkgquery).with('-W', '--showformat', %Q{'${Status} ${Package} ${Version}\\\\n'}, resource_name).and_return(\"#{dpkg_query_result} #{resource_name}\")\n\n              provider.query\n            end\n\n            context \"regex check with special characters\" do\n              let(:resource_name) { 'g++' }\n\n              it \"checks if virtual package regex for query is correct and regext dosen't make partial matching\" do\n                expect(Puppet).to_not receive(:info).with(/is virtual/)\n                expect(provider).to receive(:dpkgquery).with('-W', '--showformat', %Q{'${Status} ${Package} ${Version} [${Provides}]\\\\n'}).and_return(query_output)\n                expect(provider).to receive(:dpkgquery).with('-W', '--showformat', %Q{'${Status} ${Package} ${Version}\\\\n'}, resource_name).and_return(\"#{dpkg_query_result} #{resource_name}\")\n\n                provider.query\n              end\n            end\n          end\n        end\n      end\n    end\n\n    context \"allow_virtual false\" do\n      before do\n        allow(resource).to receive(:allow_virtual?).and_return(false)\n      end\n\n      it \"returns a hash of the found package status for an installed package\" do\n        dpkg_query_execution_returns(bash_installed_output)\n\n        expect(provider.query).to eq({:ensure => \"4.2-5ubuntu3\", :error => \"ok\", :desired => \"install\", :name => \"bash\", :mark => :none, :status => \"installed\", :provider => :dpkg})\n      end\n\n      it \"considers the package absent if the dpkg-query result cannot be interpreted\" do\n        allow(resource).to receive(:allow_virtual?).and_return(false)\n        dpkg_query_execution_returns('some-bad-data')\n        expect(provider.query[:ensure]).to eq(:absent)\n      end\n\n      it \"fails if an error is discovered\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"ok\",\"error\"))\n        expect { provider.query }.to raise_error(Puppet::Error)\n      end\n\n      it \"considers the package purged if it is marked 'not-installed'\" do\n        not_installed_bash = bash_installed_output.gsub(\"installed\", \"not-installed\")\n        not_installed_bash.gsub!(bash_version, \"\")\n        dpkg_query_execution_returns(not_installed_bash)\n        expect(provider.query[:ensure]).to eq(:purged)\n      end\n\n      it \"considers the package held if its state is 'hold'\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"install\",\"hold\"))\n        query=provider.query\n        expect(query[:ensure]).to eq(\"4.2-5ubuntu3\")\n        expect(query[:mark]).to eq(:hold)\n      end\n\n      it \"considers the package absent if it is marked 'config-files'\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"installed\",\"config-files\"))\n        expect(provider.query[:ensure]).to eq(:absent)\n      end\n\n      it \"considers the package absent if it is marked 'half-installed'\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"installed\",\"half-installed\"))\n        expect(provider.query[:ensure]).to eq(:absent)\n      end\n\n      it \"considers the package absent if it is marked 'unpacked'\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"installed\",\"unpacked\"))\n        expect(provider.query[:ensure]).to eq(:absent)\n      end\n\n      it \"considers the package absent if it is marked 'half-configured'\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"installed\",\"half-configured\"))\n        expect(provider.query[:ensure]).to eq(:absent)\n      end\n\n      it \"considers the package held if its state is 'hold'\" do\n        dpkg_query_execution_returns(bash_installed_output.gsub(\"install\",\"hold\"))\n        query=provider.query\n        expect(query[:ensure]).to eq(\"4.2-5ubuntu3\")\n        expect(query[:mark]).to eq(:hold)\n      end\n\n      context \"parsing tests\" do\n        let(:resource_name) { 'name' }\n        let(:package_hash) do\n          {\n            :desired => 'desired',\n            :error => 'ok',\n            :status => 'status',\n            :name => resource_name,\n            :mark => :none,\n            :ensure => 'ensure',\n            :provider => :dpkg,\n          }\n        end\n\n        let(:package_not_found_hash) do\n          {:ensure => :purged, :status => 'missing', :name => resource_name, :error => 'ok'}\n        end\n\n        let(:output) {'an unexpected dpkg msg with an exit code of 0'}\n\n        def parser_test(dpkg_output_string, gold_hash, number_of_debug_logs = 0)\n          dpkg_query_execution_returns(dpkg_output_string)\n          expect(Puppet).not_to receive(:warning)\n          expect(Puppet).to receive(:debug).exactly(number_of_debug_logs).times\n\n          expect(provider.query).to eq(gold_hash)\n        end\n\n        it \"parses properly even if optional ensure field is missing\" do\n          no_ensure = 'desired ok status name '\n          parser_test(no_ensure, package_hash.merge(:ensure => ''))\n        end\n\n        it \"provides debug logging of unparsable lines with allow_virtual enabled\" do\n          allow(resource).to receive(:allow_virtual?).and_return(true)\n          dpkg_query_execution_with_multiple_args_returns(output, args_with_provides, query_args)\n          expect(Puppet).not_to receive(:warning)\n          expect(Puppet).to receive(:debug).exactly(1).times\n          expect(provider.query).to eq(package_not_found_hash.merge(:ensure => :absent))\n        end\n\n        it \"provides debug logging of unparsable lines\" do\n          parser_test('an unexpected dpkg msg with an exit code of 0', package_not_found_hash.merge(:ensure => :absent), 1)\n        end\n\n        it \"does not log if execution returns with non-zero exit code with allow_virtual enabled\" do\n          allow(resource).to receive(:allow_virtual?).and_return(true)\n          expect(Puppet::Util::Execution).to receive(:execute).with(args_with_provides, execute_options).and_raise(Puppet::ExecutionFailure.new(\"failed\"))\n          expect(Puppet).not_to receive(:debug)\n          expect(provider.query).to eq(package_not_found_hash)\n        end\n\n        it \"does not log if execution returns with non-zero exit code\" do\n          expect(Puppet::Util::Execution).to receive(:execute).with(query_args, execute_options).and_raise(Puppet::ExecutionFailure.new(\"failed\"))\n          expect(Puppet).not_to receive(:debug)\n\n          expect(provider.query).to eq(package_not_found_hash)\n        end\n      end\n    end\n  end\n\n  context \"when installing\" do\n    before do\n      allow(resource).to receive(:[]).with(:source).and_return(\"mypkg\")\n    end\n\n    it \"fails to install if no source is specified in the resource\" do\n      expect(resource).to receive(:[]).with(:source).and_return(nil)\n\n      expect { provider.install }.to raise_error(ArgumentError)\n    end\n\n    it \"uses 'dpkg -i' to install the package\" do\n      expect(resource).to receive(:[]).with(:source).and_return(\"mypackagefile\")\n      expect(provider).to receive(:properties).and_return({:mark => :hold})\n      expect(provider).to receive(:unhold)\n      expect(provider).to receive(:dpkg).with(any_args, \"-i\", \"mypackagefile\")\n      provider.install\n    end\n\n    it \"keeps old config files if told to do so\" do\n      expect(resource).to receive(:[]).with(:configfiles).and_return(:keep)\n      expect(provider).to receive(:properties).and_return({:mark => :hold})\n      expect(provider).to receive(:unhold)\n      expect(provider).to receive(:dpkg).with(\"--force-confold\", any_args)\n\n      provider.install\n    end\n\n    it \"replaces old config files if told to do so\" do\n      expect(resource).to receive(:[]).with(:configfiles).and_return(:replace)\n      expect(provider).to receive(:properties).and_return({:mark => :hold})\n      expect(provider).to receive(:unhold)\n      expect(provider).to receive(:dpkg).with(\"--force-confnew\", any_args)\n\n      provider.install\n    end\n\n    it \"ensures any hold is removed\" do\n      expect(provider).to receive(:properties).and_return({:mark => :hold})\n      expect(provider).to receive(:unhold).once\n      expect(provider).to receive(:dpkg)\n      provider.install\n    end\n  end\n\n  context \"when holding or unholding\" do\n    let(:tempfile) { double('tempfile', :print => nil, :close => nil, :flush => nil, :path => \"/other/file\") }\n\n    before do\n      allow(tempfile).to receive(:write)\n      allow(Tempfile).to receive(:open).and_yield(tempfile)\n    end\n\n    it \"executes dpkg --set-selections when holding\" do\n      allow(provider).to receive(:install)\n      expect(provider).to receive(:execute).with([:dpkg, '--set-selections'], {:failonfail => false, :combine => false, :stdinfile => tempfile.path}).once\n      provider.hold\n    end\n\n    it \"executes dpkg --set-selections when unholding\" do\n      allow(provider).to receive(:install)\n      expect(provider).to receive(:execute).with([:dpkg, '--set-selections'], {:failonfail => false, :combine => false, :stdinfile => tempfile.path}).once\n      provider.hold\n    end\n  end\n\n  it \"uses :install to update\" do\n    expect(provider).to receive(:install)\n    provider.update\n  end\n\n  context \"when determining latest available version\" do\n    it \"returns the version found by dpkg-deb\" do\n      expect(resource).to receive(:[]).with(:source).and_return(\"python\")\n      expect(provider).to receive(:dpkg_deb).with('--show', \"python\").and_return(\"package\\t1.0\")\n      expect(provider.latest).to eq(\"1.0\")\n    end\n\n    it \"warns if the package file contains a different package\" do\n      expect(provider).to receive(:dpkg_deb).and_return(\"foo\\tversion\")\n      expect(provider).to receive(:warning)\n      provider.latest\n    end\n\n    it \"copes with names containing ++\" do\n      resource = double('resource', :[] => \"package++\")\n      provider = described_class.new(resource)\n      expect(provider).to receive(:dpkg_deb).and_return(\"package++\\t1.0\")\n      expect(provider.latest).to eq(\"1.0\")\n    end\n  end\n\n  it \"uses 'dpkg -r' to uninstall\" do\n    expect(provider).to receive(:dpkg).with(\"-r\", resource_name)\n    provider.uninstall\n  end\n\n  it \"uses 'dpkg --purge' to purge\" do\n    expect(provider).to receive(:dpkg).with(\"--purge\", resource_name)\n    provider.purge\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/provider/package/freebsd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:freebsd) do\n  before :each do\n    # Create a mock resource\n    @resource = double('resource')\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name and source\n    allow(@resource).to receive(:[]).with(:name).and_return(\"mypackage\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:installed)\n\n    @provider = subject()\n    @provider.resource = @resource\n  end\n\n  it \"should have an install method\" do\n    @provider = subject()\n    expect(@provider).to respond_to(:install)\n  end\n\n  context \"when installing\" do\n    before :each do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:installed)\n    end\n\n    it \"should install a package from a path to a directory\" do\n      # For better or worse, trailing '/' is needed. --daniel 2011-01-26\n      path = '/path/to/directory/'\n      allow(@resource).to receive(:[]).with(:source).and_return(path)\n      expect(Puppet::Util).to receive(:withenv).once.with({:PKG_PATH => path}).and_yield\n      expect(@provider).to receive(:pkgadd).once.with(\"mypackage\")\n\n      expect { @provider.install }.to_not raise_error\n    end\n\n    %w{http https ftp}.each do |protocol|\n      it \"should install a package via #{protocol}\" do\n        # For better or worse, trailing '/' is needed. --daniel 2011-01-26\n        path = \"#{protocol}://localhost/\"\n        allow(@resource).to receive(:[]).with(:source).and_return(path)\n        expect(Puppet::Util).to receive(:withenv).once.with({:PACKAGESITE => path}).and_yield\n        expect(@provider).to receive(:pkgadd).once.with('-r', \"mypackage\")\n\n        expect { @provider.install }.to_not raise_error\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/gem_spec.rb",
    "content": "require 'spec_helper'\n\ncontext Puppet::Type.type(:package).provider(:gem) do\n\n  it { is_expected.to be_installable }\n  it { is_expected.to be_uninstallable }\n  it { is_expected.to be_upgradeable }\n  it { is_expected.to be_versionable }\n  it { is_expected.to be_install_options }\n  it { is_expected.to be_targetable }\n\n  let(:provider_gem_cmd) { '/provider/gem' }\n  let(:execute_options) { {:failonfail => true, :combine => true, :custom_environment => {\"HOME\"=>ENV[\"HOME\"]}} }\n\n  before do\n    allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n  end\n\n  context 'installing myresource' do\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name     => 'myresource',\n        :ensure   => :installed\n      )\n    end\n\n    let(:provider) do\n      provider = described_class.new\n      provider.resource = resource\n      provider\n    end\n\n    before :each do\n      resource.provider = provider\n      allow(described_class).to receive(:command).with(:gemcmd).and_return(provider_gem_cmd)\n    end\n\n    context \"when installing\" do\n      before :each do\n        allow(provider).to receive(:rubygem_version).and_return('1.9.9')\n      end\n\n      context 'on windows' do\n        let(:path) do\n          \"C:\\\\Program Files\\\\Puppet Labs\\\\Puppet\\\\puppet\\\\bin;C:\\\\Program Files\\\\Puppet Labs\\\\Puppet\\\\bin;C:\\\\Ruby26-x64\\\\bin;C:\\\\Windows\\\\system32\\\\bin\"\n        end\n\n        let(:expected_path) do\n          \"C:\\\\Program Files\\\\Puppet Labs\\\\Puppet\\\\bin;C:\\\\Ruby26-x64\\\\bin;C:\\\\Windows\\\\system32\\\\bin\"\n        end\n\n        before do\n          allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n          allow(ENV).to receive(:[]).and_call_original\n          allow(ENV).to receive(:[]).with('PATH').and_return(path)\n          allow(described_class).to receive(:validate_command).with(provider_gem_cmd)\n          stub_const('::File::PATH_SEPARATOR', ';')\n        end\n\n        it 'removes puppet/bin from PATH' do\n          expect(described_class).to receive(:execute) \\\n            .with(\n              anything,\n              hash_including(custom_environment: hash_including(PATH: expected_path))\n            )\n            .and_return(\"\")\n          provider.install\n        end\n      end\n\n      it \"should use the path to the gem command\" do\n        allow(described_class).to receive(:validate_command).with(provider_gem_cmd)\n        expect(described_class).to receive(:execute).with(be_a(Array), execute_options) { |args| expect(args[0]).to eq(provider_gem_cmd) }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should specify that the gem is being installed\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[0]).to eq(\"install\") }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should specify that --rdoc should not be included when gem version is < 2.0.0\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[1]).to eq(\"--no-rdoc\") }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should specify that --ri should not be included when gem version is < 2.0.0\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[2]).to eq(\"--no-ri\") }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should specify that --document should not be included when gem version is >= 2.0.0\" do\n        allow(provider).to receive(:rubygem_version).and_return('2.0.0')\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[1]).to eq(\"--no-document\") }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should specify the package name\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[3]).to eq(\"myresource\") }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should not append install_options by default\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args.length).to eq(4) }.and_return(\"\")\n        provider.install\n      end\n\n      it \"should allow setting an install_options parameter\" do\n        resource[:install_options] = [ '--force', {'--bindir' => '/usr/bin' } ]\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) do |cmd, args|\n          expect(args[1]).to eq('--force')\n          expect(args[2]).to eq('--bindir=/usr/bin')\n        end.and_return(\"\")\n        provider.install\n      end\n\n      context \"when a source is specified\" do\n        context \"as a normal file\" do\n          it \"should use the file name instead of the gem name\" do\n            resource[:source] = \"/my/file\"\n            expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, array_including(\"/my/file\")).and_return(\"\")\n            provider.install\n          end\n        end\n\n        context \"as a file url\" do\n          it \"should use the file name instead of the gem name\" do\n            resource[:source] = \"file:///my/file\"\n            expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, array_including(\"/my/file\")).and_return(\"\")\n            provider.install\n          end\n        end\n\n        context \"as a puppet url\" do\n          it \"should fail\" do\n            resource[:source] = \"puppet://my/file\"\n            expect { provider.install }.to raise_error(Puppet::Error)\n          end\n        end\n\n        context \"as a non-file and non-puppet url\" do\n          it \"should treat the source as a gem repository\" do\n            resource[:source] = \"http://host/my/file\"\n            expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[3..5]).to eq([\"--source\", \"http://host/my/file\", \"myresource\"]) }.and_return(\"\")\n            provider.install\n          end\n        end\n\n        context \"as a windows path on windows\", :if => Puppet::Util::Platform.windows? do\n          it \"should treat the source as a local path\" do\n            resource[:source] = \"c:/this/is/a/path/to/a/gem.gem\"\n            expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, array_including(\"c:/this/is/a/path/to/a/gem.gem\")).and_return(\"\")\n            provider.install\n          end\n        end\n\n        context \"with an invalid uri\" do\n          it \"should fail\" do\n            expect(URI).to receive(:parse).and_raise(ArgumentError)\n            resource[:source] = \"http:::::uppet:/:/my/file\"\n            expect { provider.install }.to raise_error(Puppet::Error)\n          end\n        end\n      end\n    end\n\n    context \"#latest\" do\n      it \"should return a single value for 'latest'\" do\n        #gemlist is used for retrieving both local and remote version numbers, and there are cases\n        # (particularly local) where it makes sense for it to return an array.  That doesn't make\n        # sense for '#latest', though.\n        expect(provider.class).to receive(:gemlist).with({:command => provider_gem_cmd, :justme => 'myresource'}).and_return({\n          :name     => 'myresource',\n          :ensure   => [\"3.0\"],\n          :provider => :gem,\n        })\n        expect(provider.latest).to eq(\"3.0\")\n      end\n\n      it \"should list from the specified source repository\" do\n        resource[:source] = \"http://foo.bar.baz/gems\"\n        expect(provider.class).to receive(:gemlist).\n          with({:command => provider_gem_cmd, :justme => 'myresource', :source => \"http://foo.bar.baz/gems\"}).\n          and_return({\n            :name     => 'myresource',\n            :ensure   => [\"3.0\"],\n            :provider => :gem,\n          })\n        expect(provider.latest).to eq(\"3.0\")\n      end\n    end\n\n    context \"#instances\" do\n      before do\n        allow(described_class).to receive(:command).with(:gemcmd).and_return(provider_gem_cmd)\n      end\n\n      it \"should return an empty array when no gems installed\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, %w{list --local}).and_return(\"\\n\")\n        expect(described_class.instances).to eq([])\n      end\n\n      it \"should return ensure values as an array of installed versions\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, %w{list --local}).and_return(<<-HEREDOC.gsub(/        /, ''))\n        systemu (1.2.0)\n        vagrant (0.8.7, 0.6.9)\n        HEREDOC\n\n        expect(described_class.instances.map {|p| p.properties}).to eq([\n          {:name => \"systemu\", :provider => :gem, :command => provider_gem_cmd, :ensure => [\"1.2.0\"]},\n          {:name => \"vagrant\", :provider => :gem, :command => provider_gem_cmd, :ensure => [\"0.8.7\", \"0.6.9\"]}\n        ])\n      end\n\n      it \"should ignore platform specifications\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, %w{list --local}).and_return(<<-HEREDOC.gsub(/        /, ''))\n        systemu (1.2.0)\n        nokogiri (1.6.1 ruby java x86-mingw32 x86-mswin32-60, 1.4.4.1 x86-mswin32)\n        HEREDOC\n\n        expect(described_class.instances.map {|p| p.properties}).to eq([\n          {:name => \"systemu\",  :provider => :gem, :command => provider_gem_cmd, :ensure => [\"1.2.0\"]},\n          {:name => \"nokogiri\", :provider => :gem, :command => provider_gem_cmd, :ensure => [\"1.6.1\", \"1.4.4.1\"]}\n        ])\n      end\n\n      it \"should not list 'default: ' text from rubygems''\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, %w{list --local}).and_return(<<-HEREDOC.gsub(/        /, ''))\n        bundler (1.16.1, default: 1.16.0, 1.15.1)\n        HEREDOC\n\n        expect(described_class.instances.map {|p| p.properties}).to eq([\n          {:name => \"bundler\", :provider => :gem, :command => provider_gem_cmd, :ensure => [\"1.16.1\", \"1.16.0\", \"1.15.1\"]}\n        ])\n      end\n\n      it \"should not fail when an unmatched line is returned\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, %w{list --local}).and_return(File.read(my_fixture('line-with-1.8.5-warning')))\n\n        expect(described_class.instances.map {|p| p.properties}).\n          to eq([{:name=>\"columnize\",          :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"0.3.2\"]},\n                 {:name=>\"diff-lcs\",           :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"1.1.3\"]},\n                 {:name=>\"metaclass\",          :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"0.0.1\"]},\n                 {:name=>\"mocha\",              :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"0.10.5\"]},\n                 {:name=>\"rake\",               :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"0.8.7\"]},\n                 {:name=>\"rspec-core\",         :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"2.9.0\"]},\n                 {:name=>\"rspec-expectations\", :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"2.9.1\"]},\n                 {:name=>\"rspec-mocks\",        :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"2.9.0\"]},\n                 {:name=>\"rubygems-bundler\",   :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"0.9.0\"]},\n                 {:name=>\"rvm\",                :provider=>:gem, :command => provider_gem_cmd, :ensure=>[\"1.11.3.3\"]}])\n      end\n    end\n\n    context \"listing gems\" do\n      context \"searching for a single package\" do\n        it \"searches for an exact match\" do\n          expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, array_including('\\Abundler\\z')).and_return(File.read(my_fixture('gem-list-single-package')))\n          expected = {:name=>\"bundler\", :provider=>:gem, :ensure=>[\"1.6.2\"]}\n          expect(described_class.gemlist({:command => provider_gem_cmd, :justme => 'bundler'})).to eq(expected)\n        end\n      end\n    end\n\n    context 'insync?' do\n      context 'for array of versions' do\n        let(:is) { ['1.3.4', '3.6.1', '5.1.2'] }\n\n        it 'returns true for ~> 1.3' do\n          resource[:ensure] = '~> 1.3'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for ~> 2' do\n          resource[:ensure] = '~> 2'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns true for > 4' do\n          resource[:ensure] = '> 4'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns true for 3.6.1' do\n          resource[:ensure] = '3.6.1'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for 3.6.2' do\n          resource[:ensure] = '3.6.2'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns true for >2, <4' do\n          resource[:ensure] = '>2, <4'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for >=4, <5' do\n          resource[:ensure] = '>=4, <5'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns true for >2 <4' do\n          resource[:ensure] = '>2 <4'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for >=4 <5' do\n          resource[:ensure] = '>=4 <5'\n          expect(provider).to_not be_insync(is)\n        end\n      end\n\n      context 'for string version' do\n        let(:is) { '1.3.4' }\n\n        it 'returns true for ~> 1.3' do\n          resource[:ensure] = '~> 1.3'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for ~> 2' do\n          resource[:ensure] = '~> 2'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns false for > 4' do\n          resource[:ensure] = '> 4'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns true for 1.3.4' do\n          resource[:ensure] = '1.3.4'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for 3.6.1' do\n          resource[:ensure] = '3.6.1'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns true for >=1.3, <2' do\n          resource[:ensure] = '>=1.3, <2'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for >1, <=1.3' do\n          resource[:ensure] = '>1, <=1.3'\n          expect(provider).to_not be_insync(is)\n        end\n\n        it 'returns true for >=1.3 <2' do\n          resource[:ensure] = '>=1.3 <2'\n          expect(provider).to be_insync(is)\n        end\n\n        it 'returns false for >1 <=1.3' do\n          resource[:ensure] = '>1 <=1.3'\n          expect(provider).to_not be_insync(is)\n        end\n      end\n\n      it 'should return false for bad version specifiers' do\n        resource[:ensure] = 'not a valid gem specifier'\n        expect(provider).to_not be_insync('1.0')\n      end\n\n      it 'should return false for :absent' do\n        resource[:ensure] = '~> 1.0'\n        expect(provider).to_not be_insync(:absent)\n      end\n    end\n  end\n\n  context 'installing myresource with a target command' do\n    let(:resource_gem_cmd) { '/resource/gem' }\n\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name     => \"myresource\",\n        :ensure   => :installed,\n      )\n    end\n\n    let(:provider) do\n      provider = described_class.new\n      provider.resource = resource\n      provider\n    end\n\n    before :each do\n      resource.provider = provider\n    end\n\n    context \"when installing with a target command\" do\n      before :each do\n        allow(described_class).to receive(:which).with(resource_gem_cmd).and_return(resource_gem_cmd)\n      end\n\n      it \"should use the path to the other gem\" do\n        resource::original_parameters[:command] = resource_gem_cmd\n        expect(described_class).to receive(:execute_gem_command).with(resource_gem_cmd, be_a(Array)).twice.and_return(\"\")\n        provider.install\n      end\n    end\n  end\n\n  context 'uninstalling myresource' do\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name     => 'myresource',\n        :ensure   => :absent\n      )\n    end\n\n    let(:provider) do\n      provider = described_class.new\n      provider.resource = resource\n      provider\n    end\n\n    before :each do\n      resource.provider = provider\n      allow(described_class).to receive(:command).with(:gemcmd).and_return(provider_gem_cmd)\n    end\n\n    context \"when uninstalling\" do\n      it \"should use the path to the gem command\" do\n        allow(described_class).to receive(:validate_command).with(provider_gem_cmd)\n        expect(described_class).to receive(:execute).with(be_a(Array), execute_options) { |args| expect(args[0]).to eq(provider_gem_cmd) }.and_return(\"\")\n        provider.uninstall\n      end\n\n      it \"should specify that the gem is being uninstalled\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[0]).to eq(\"uninstall\") }.and_return(\"\")\n        provider.uninstall\n      end\n\n      it \"should specify that the relevant executables should be removed without confirmation\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[1]).to eq(\"--executables\") }.and_return(\"\")\n        provider.uninstall\n      end\n\n      it \"should specify that all the matching versions should be removed\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[2]).to eq(\"--all\") }.and_return(\"\")\n        provider.uninstall\n      end\n\n      it \"should specify the package name\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args[3]).to eq(\"myresource\") }.and_return(\"\")\n        provider.uninstall\n      end\n\n      it \"should not append uninstall_options by default\" do\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) { |cmd, args| expect(args.length).to eq(4) }.and_return(\"\")\n        provider.uninstall\n      end\n\n      it \"should allow setting an uninstall_options parameter\" do\n        resource[:uninstall_options] = [ '--ignore-dependencies', {'--version' => '0.1.1' } ]\n        expect(described_class).to receive(:execute_gem_command).with(provider_gem_cmd, be_a(Array)) do |cmd, args|\n          expect(args[4]).to eq('--ignore-dependencies')\n          expect(args[5]).to eq('--version=0.1.1')\n        end.and_return('')\n        provider.uninstall\n      end\n    end\n  end\n\n  context 'calculated specificity' do\n    include_context 'provider specificity'\n\n    context 'when is not defaultfor' do\n      subject { described_class.specificity }\n      it { is_expected.to eql 1 }\n    end\n\n    context 'when is defaultfor' do\n      let(:os) { Puppet.runtime[:facter].value('os.name') }\n      subject do\n        described_class.defaultfor('os.name': os)\n        described_class.specificity\n      end\n      it { is_expected.to be > 100 }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/hpux_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:hpux) do\n  before(:each) do\n    # Create a mock resource\n    @resource = double('resource')\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name and source\n    allow(@resource).to receive(:[]).with(:name).and_return(\"mypackage\")\n    allow(@resource).to receive(:[]).with(:source).and_return(\"mysource\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:installed)\n\n    @provider = subject()\n    allow(@provider).to receive(:resource).and_return(@resource)\n  end\n\n  it \"should have an install method\" do\n    @provider = subject()\n    expect(@provider).to respond_to(:install)\n  end\n\n  it \"should have an uninstall method\" do\n    @provider = subject()\n    expect(@provider).to respond_to(:uninstall)\n  end\n\n  it \"should have a swlist method\" do\n    @provider = subject()\n    expect(@provider).to respond_to(:swlist)\n  end\n\n  context \"when installing\" do\n    it \"should use a command-line like 'swinstall -x mount_all_filesystems=false -s SOURCE PACKAGE-NAME'\" do\n      expect(@provider).to receive(:swinstall).with('-x', 'mount_all_filesystems=false', '-s', 'mysource', 'mypackage')\n      @provider.install\n    end\n  end\n\n  context \"when uninstalling\" do\n    it \"should use a command-line like 'swremove -x mount_all_filesystems=false PACKAGE-NAME'\" do\n      expect(@provider).to receive(:swremove).with('-x', 'mount_all_filesystems=false', 'mypackage')\n      @provider.uninstall\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/macports_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:macports) do\n  let :resource_name do\n    \"foo\"\n  end\n\n  let :resource do\n    Puppet::Type.type(:package).new(:name => resource_name, :provider => :macports)\n  end\n\n  let :provider do\n    prov = resource.provider\n    expect(prov).not_to receive(:execute)\n    prov\n  end\n\n  let :current_hash do\n    {:name => resource_name, :ensure => \"1.2.3\", :revision => \"1\", :provider => :macports}\n  end\n\n  context \"provider features\" do\n    subject { provider }\n\n    it { is_expected.to be_installable }\n    it { is_expected.to be_uninstallable }\n    it { is_expected.to be_upgradeable }\n    it { is_expected.to be_versionable }\n  end\n\n  context \"when listing all instances\" do\n    it \"should call port -q installed\" do\n      expect(described_class).to receive(:port).with(\"-q\", :installed).and_return(\"\")\n      described_class.instances\n    end\n\n    it \"should create instances from active ports\" do\n      expect(described_class).to receive(:port).and_return(\"foo @1.234.5_2 (active)\")\n      expect(described_class.instances.size).to eq(1)\n    end\n\n    it \"should ignore ports that aren't activated\" do\n      expect(described_class).to receive(:port).and_return(\"foo @1.234.5_2\")\n      expect(described_class.instances.size).to eq(0)\n    end\n\n    it \"should ignore variants\" do\n      expect(described_class.parse_installed_query_line(\"bar @1.0beta2_38_1+x11+java (active)\")).\n        to eq({:provider=>:macports, :revision=>\"1\", :name=>\"bar\", :ensure=>\"1.0beta2_38\"})\n    end\n\n  end\n\n  context \"when installing\" do\n    it \"should not specify a version when ensure is set to latest\" do\n      resource[:ensure] = :latest\n      # version would be the 4th argument, if provided.\n      expect(provider).to receive(:port).with(anything, anything, anything)\n\n      provider.install\n    end\n\n    it \"should not specify a version when ensure is set to present\" do\n      resource[:ensure] = :present\n      # version would be the 4th argument, if provided.\n      expect(provider).to receive(:port).with(anything, anything, anything)\n\n      provider.install\n    end\n\n    it \"should specify a version when ensure is set to a version\" do\n      resource[:ensure] = \"1.2.3\"\n      expect(provider).to receive(:port).with(anything, anything, anything, '@1.2.3')\n\n      provider.install\n    end\n  end\n\n  context \"when querying for the latest version\" do\n    # Redefine provider to avoid the \"expect(prov).not_to receive(:execute)\"\n    # that was set in the original definition.\n    let(:provider) { resource.provider }\n    let(:new_info_line) do\n      \"1.2.3 2\"\n    end\n    let(:infoargs) do\n      [\"/opt/local/bin/port\", \"-q\", :info, \"--line\", \"--version\", \"--revision\",  resource_name]\n    end\n    let(:arguments) do\n      {:failonfail => false, :combine => false}\n    end\n\n    before :each do\n      allow(provider).to receive(:command).with(:port).and_return(\"/opt/local/bin/port\")\n    end\n\n    it \"should return nil when the package cannot be found\" do\n      resource[:name] = resource_name\n      expect(provider).to receive(:execute).with(infoargs, arguments).and_return(\"\")\n      expect(provider.latest).to eq(nil)\n    end\n\n    it \"should return the current version if the installed port has the same revision\" do\n      current_hash[:revision] = \"2\"\n      expect(provider).to receive(:execute).with(infoargs, arguments).and_return(new_info_line)\n      expect(provider).to receive(:query).and_return(current_hash)\n      expect(provider.latest).to eq(current_hash[:ensure])\n    end\n\n    it \"should return the new version_revision if the installed port has a lower revision\" do\n      current_hash[:revision] = \"1\"\n      expect(provider).to receive(:execute).with(infoargs, arguments).and_return(new_info_line)\n      expect(provider).to receive(:query).and_return(current_hash)\n      expect(provider.latest).to eq(\"1.2.3_2\")\n    end\n\n    it \"should return the newest version if the port is not installed\" do\n      resource[:name] = resource_name\n      expect(provider).to receive(:execute).with(infoargs, arguments).and_return(new_info_line)\n      expect(provider).to receive(:execute).with([\"/opt/local/bin/port\", \"-q\", :installed, resource[:name]], arguments).and_return(\"\")\n      expect(provider.latest).to eq(\"1.2.3_2\")\n    end\n  end\n\n  context \"when updating a port\" do\n    it \"should execute port install if the port is installed\" do\n      resource[:name] = resource_name\n      resource[:ensure] = :present\n      allow(provider).to receive(:query).and_return(current_hash)\n      expect(provider).to receive(:port).with(\"-q\", :install, resource_name)\n      provider.update\n    end\n\n    it \"should execute port install if the port is not installed\" do\n      resource[:name] = resource_name\n      resource[:ensure] = :present\n      allow(provider).to receive(:query).and_return(\"\")\n      expect(provider).to receive(:port).with(\"-q\", :install, resource_name)\n      provider.update\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/nim_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:nim) do\n  before(:each) do\n    @resource = double('resource')\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name and source\n    allow(@resource).to receive(:[]).with(:name).and_return(\"mypackage.foo\")\n    allow(@resource).to receive(:[]).with(:source).and_return(\"mysource\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:installed)\n\n    @provider = subject()\n    @provider.resource = @resource\n  end\n\n  it \"should have an install method\" do\n    @provider = subject()\n    expect(@provider).to respond_to(:install)\n  end\n\n  let(:bff_showres_output) {\n    Puppet::Util::Execution::ProcessOutput.new(<<END, 0)\nmypackage.foo                                                           ALL  @@I:mypackage.foo _all_filesets\n @ 1.2.3.1  MyPackage Runtime Environment                       @@I:mypackage.foo 1.2.3.1\n + 1.2.3.4  MyPackage Runtime Environment                       @@I:mypackage.foo 1.2.3.4\n + 1.2.3.8  MyPackage Runtime Environment                       @@I:mypackage.foo 1.2.3.8\n\nEND\n  }\n\n  let(:rpm_showres_output) {\n    Puppet::Util::Execution::ProcessOutput.new(<<END, 0)\nmypackage.foo                                                                ALL  @@R:mypackage.foo _all_filesets\n @@R:mypackage.foo-1.2.3-1 1.2.3-1\n @@R:mypackage.foo-1.2.3-4 1.2.3-4\n @@R:mypackage.foo-1.2.3-8 1.2.3-8\n\nEND\n  }\n\n  context \"when installing\" do\n    it \"should install a package\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:installed)\n      expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/sbin/nimclient -o showres -a resource=mysource |/usr/bin/grep -p -E 'mypackage\\\\.foo'\").and_return(bff_showres_output)\n      expect(@provider).to receive(:nimclient).with(\"-o\", \"cust\", \"-a\", \"installp_flags=acgwXY\", \"-a\", \"lpp_source=mysource\", \"-a\", \"filesets=mypackage.foo 1.2.3.8\")\n      @provider.install\n    end\n\n    context \"when installing versioned packages\" do\n      it \"should fail if the package is not available on the lpp source\" do\n        nimclient_showres_output = \"\"\n\n        allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3.4\")\n        expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/sbin/nimclient -o showres -a resource=mysource |/usr/bin/grep -p -E 'mypackage\\\\.foo( |-)1\\\\.2\\\\.3\\\\.4'\").and_return(nimclient_showres_output)\n        expect {\n          @provider.install\n        }.to raise_error(Puppet::Error, \"Unable to find package 'mypackage.foo' with version '1.2.3.4' on lpp_source 'mysource'\")\n      end\n\n      it \"should succeed if a BFF/installp package is available on the lpp source\" do\n        allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3.4\")\n        expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/sbin/nimclient -o showres -a resource=mysource |/usr/bin/grep -p -E 'mypackage\\\\.foo( |-)1\\\\.2\\\\.3\\\\.4'\").and_return(bff_showres_output).ordered\n        expect(@provider).to receive(:nimclient).with(\"-o\", \"cust\", \"-a\", \"installp_flags=acgwXY\", \"-a\", \"lpp_source=mysource\", \"-a\", \"filesets=mypackage.foo 1.2.3.4\").ordered\n        @provider.install\n      end\n\n      it \"should fail if the specified version of a BFF package is superseded\" do\n        install_output = <<OUTPUT\n+-----------------------------------------------------------------------------+\n                    Pre-installation Verification...\n+-----------------------------------------------------------------------------+\nVerifying selections...done\nVerifying requisites...done\nResults...\n\nWARNINGS\n--------\n  Problems described in this section are not likely to be the source of any\n  immediate or serious failures, but further actions may be necessary or\n  desired.\n\n  Already Installed\n  -----------------\n  The number of selected filesets that are either already installed\n  or effectively installed through superseding filesets is 1.  See\n  the summaries at the end of this installation for details.\n\n  NOTE:  Base level filesets may be reinstalled using the \"Force\"\n  option (-F flag), or they may be removed, using the deinstall or\n  \"Remove Software Products\" facility (-u flag), and then reinstalled.\n\n  << End of Warning Section >>\n\n+-----------------------------------------------------------------------------+\n                   BUILDDATE Verification ...\n+-----------------------------------------------------------------------------+\nVerifying build dates...done\nFILESET STATISTICS\n------------------\n    1  Selected to be installed, of which:\n        1  Already installed (directly or via superseding filesets)\n  ----\n    0  Total to be installed\n\n\nPre-installation Failure/Warning Summary\n----------------------------------------\nName                      Level           Pre-installation Failure/Warning\n-------------------------------------------------------------------------------\nmypackage.foo              1.2.3.1         Already superseded by 1.2.3.4\nOUTPUT\n\n        allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3.1\")\n        expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/sbin/nimclient -o showres -a resource=mysource |/usr/bin/grep -p -E 'mypackage\\\\.foo( |-)1\\\\.2\\\\.3\\\\.1'\").and_return(bff_showres_output).ordered\n        expect(@provider).to receive(:nimclient).with(\"-o\", \"cust\", \"-a\", \"installp_flags=acgwXY\", \"-a\", \"lpp_source=mysource\", \"-a\", \"filesets=mypackage.foo 1.2.3.1\").and_return(install_output).ordered\n\n        expect { @provider.install }.to raise_error(Puppet::Error, \"NIM package provider is unable to downgrade packages\")\n    end\n\n    it \"should succeed if an RPM package is available on the lpp source\" do\n        allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3-4\")\n        expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/sbin/nimclient -o showres -a resource=mysource |/usr/bin/grep -p -E 'mypackage\\\\.foo( |-)1\\\\.2\\\\.3\\\\-4'\").and_return(rpm_showres_output).ordered\n        expect(@provider).to receive(:nimclient).with(\"-o\", \"cust\", \"-a\", \"installp_flags=acgwXY\", \"-a\", \"lpp_source=mysource\", \"-a\", \"filesets=mypackage.foo-1.2.3-4\").ordered\n        @provider.install\n      end\n    end\n\n    it \"should fail if the specified version of a RPM package is superseded\" do\n      install_output = <<OUTPUT\n\n\nValidating RPM package selections ...\n\nPlease wait...\n+-----------------------------------------------------------------------------+\n                          RPM  Error Summary:\n+-----------------------------------------------------------------------------+\nThe following RPM packages were requested for installation\nbut they are already installed or superseded by a package installed\nat a higher level:\nmypackage.foo-1.2.3-1 is superseded by mypackage.foo-1.2.3-4\n\n\nOUTPUT\n\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3-1\")\n      expect(Puppet::Util::Execution).to receive(:execute).with(\"/usr/sbin/nimclient -o showres -a resource=mysource |/usr/bin/grep -p -E 'mypackage\\\\.foo( |-)1\\\\.2\\\\.3\\\\-1'\").and_return(rpm_showres_output)\n      expect(@provider).to receive(:nimclient).with(\"-o\", \"cust\", \"-a\", \"installp_flags=acgwXY\", \"-a\", \"lpp_source=mysource\", \"-a\", \"filesets=mypackage.foo-1.2.3-1\").and_return(install_output)\n\n      expect { @provider.install }.to raise_error(Puppet::Error, \"NIM package provider is unable to downgrade packages\")\n    end\n  end\n\n  context \"when uninstalling\" do\n    it \"should call installp to uninstall a bff package\" do\n      expect(@provider).to receive(:lslpp).with(\"-qLc\", \"mypackage.foo\").and_return(\"#bos.atm:bos.atm.atmle:7.1.2.0: : :C: :ATM LAN Emulation Client Support : : : : : : :0:0:/:1241\")\n      expect(@provider).to receive(:installp).with(\"-gu\", \"mypackage.foo\")\n      expect(@provider.class).to receive(:pkglist).with({:pkgname => 'mypackage.foo'}).and_return(nil)\n      @provider.uninstall\n    end\n\n    it \"should call rpm to uninstall an rpm package\" do\n      expect(@provider).to receive(:lslpp).with(\"-qLc\", \"mypackage.foo\").and_return(\"cdrecord:cdrecord-1.9-6:1.9-6: : :C:R:A command line CD/DVD recording program.: :/bin/rpm -e cdrecord: : : : :0: :/opt/freeware:Wed Jun 29 09:41:32 PDT 2005\")\n      expect(@provider).to receive(:rpm).with(\"-e\", \"mypackage.foo\")\n      expect(@provider.class).to receive(:pkglist).with({:pkgname => 'mypackage.foo'}).and_return(nil)\n      @provider.uninstall\n    end\n  end\n\n  context \"when parsing nimclient showres output\" do\n    describe \"#parse_showres_output\" do\n      it \"should be able to parse installp/BFF package listings\" do\n        packages = subject.send(:parse_showres_output, bff_showres_output)\n        expect(Set.new(packages.keys)).to eq(Set.new(['mypackage.foo']))\n        versions = packages['mypackage.foo']\n        ['1.2.3.1', '1.2.3.4', '1.2.3.8'].each do |version|\n          expect(versions.has_key?(version)).to eq(true)\n          expect(versions[version]).to eq(:installp)\n        end\n      end\n\n      it \"should be able to parse RPM package listings\" do\n        packages = subject.send(:parse_showres_output, rpm_showres_output)\n        expect(Set.new(packages.keys)).to eq(Set.new(['mypackage.foo']))\n        versions = packages['mypackage.foo']\n        ['1.2.3-1', '1.2.3-4', '1.2.3-8'].each do |version|\n          expect(versions.has_key?(version)).to eq(true)\n          expect(versions[version]).to eq(:rpm)\n        end\n      end\n\n      it \"should be able to parse RPM package listings with letters in version\" do\n        showres_output = <<END\ncairo                                                              ALL  @@R:cairo _all_filesets\n   @@R:cairo-1.14.6-2waixX11 1.14.6-2waixX11\nEND\n        packages = subject.send(:parse_showres_output, showres_output)\n        expect(Set.new(packages.keys)).to eq(Set.new(['cairo']))\n        versions = packages['cairo']\n        expect(versions.has_key?('1.14.6-2waixX11')).to eq(true)\n        expect(versions['1.14.6-2waixX11']).to eq(:rpm)\n      end\n\n      it \"should raise error when parsing invalid RPM package listings\" do\n              showres_output = <<END\ncairo                                                              ALL  @@R:cairo _all_filesets\n   @@R:cairo-invalid_version invalid_version\nEND\n        expect{ subject.send(:parse_showres_output, showres_output) }.to raise_error(Puppet::Error,\n          /Unable to parse output from nimclient showres: package string does not match expected rpm package string format/)\n      end\n    end\n\n    context \"#determine_latest_version\" do\n      context \"when there are multiple versions\" do\n        it \"should return the latest version\" do\n          expect(subject.send(:determine_latest_version, rpm_showres_output, 'mypackage.foo')).to eq([:rpm, '1.2.3-8'])\n        end\n      end\n\n      context \"when there is only one version\" do\n        it \"should return the type specifier and `nil` for the version number\" do\n          nimclient_showres_output = <<END\nmypackage.foo                                                                ALL  @@R:mypackage.foo _all_filesets\n @@R:mypackage.foo-1.2.3-4 1.2.3-4\n\nEND\n          expect(subject.send(:determine_latest_version, nimclient_showres_output, 'mypackage.foo')).to eq([:rpm, nil])\n        end\n      end\n    end\n\n    context \"#determine_package_type\" do\n      it \"should return :rpm for rpm packages\" do\n        expect(subject.send(:determine_package_type, rpm_showres_output, 'mypackage.foo', '1.2.3-4')).to eq(:rpm)\n      end\n\n      it \"should return :installp for installp/bff packages\" do\n        expect(subject.send(:determine_package_type, bff_showres_output, 'mypackage.foo', '1.2.3.4')).to eq(:installp)\n      end\n\n      it \"should return :installp for security updates\" do\n        nimclient_showres_output = <<END\nbos.net                                                            ALL  @@S:bos.net _all_filesets\n + 7.2.0.1  TCP/IP ntp Applications                                     @@S:bos.net.tcp.ntp 7.2.0.1\n + 7.2.0.2  TCP/IP ntp Applications                                     @@S:bos.net.tcp.ntp 7.2.0.2\n\nEND\n        expect(subject.send(:determine_package_type, nimclient_showres_output, 'bos.net.tcp.ntp', '7.2.0.2')).to eq(:installp)\n      end\n\n      it \"should raise error when invalid header format is given\" do\n        nimclient_showres_output = <<END\nbos.net                                                            ALL  @@INVALID_TYPE:bos.net _all_filesets\n + 7.2.0.1  TCP/IP ntp Applications                                     @@INVALID_TYPE:bos.net.tcp.ntp 7.2.0.1\n + 7.2.0.2  TCP/IP ntp Applications                                     @@INVALID_TYPE:bos.net.tcp.ntp 7.2.0.2\n\nEND\n        expect{ subject.send(:determine_package_type, nimclient_showres_output, 'bos.net.tcp.ntp', '7.2.0.2') }.to raise_error(\n          Puppet::Error, /Unable to parse output from nimclient showres: line does not match expected package header format/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/openbsd_spec.rb",
    "content": "require 'spec_helper'\nrequire 'stringio'\n\ndescribe Puppet::Type.type(:package).provider(:openbsd) do\n  let(:package) { Puppet::Type.type(:package).new(:name => 'bash', :provider => 'openbsd') }\n  let(:provider) { described_class.new(package) }\n\n  def expect_read_from_pkgconf(lines)\n    pkgconf = double(:readlines => lines)\n    expect(Puppet::FileSystem).to receive(:exist?).with('/etc/pkg.conf').and_return(true)\n    expect(File).to receive(:open).with('/etc/pkg.conf', 'rb').and_return(pkgconf)\n  end\n\n  def expect_pkgadd_with_source(source)\n    expect(provider).to receive(:pkgadd).with([source]) do\n      expect(ENV).not_to have_key('PKG_PATH')\n    end\n  end\n\n  def expect_pkgadd_with_env_and_name(source, &block)\n    expect(ENV).not_to have_key('PKG_PATH')\n\n    expect(provider).to receive(:pkgadd).with([provider.resource[:name]]) do\n      expect(ENV).to have_key('PKG_PATH')\n      expect(ENV['PKG_PATH']).to eq(source)\n    end\n    expect(provider).to receive(:execpipe).with(['/bin/pkg_info', '-I', provider.resource[:name]]).and_yield('')\n\n    yield\n\n    expect(ENV).not_to be_key('PKG_PATH')\n  end\n\n  context 'provider features' do\n    it { is_expected.to be_installable }\n    it { is_expected.to be_install_options }\n    it { is_expected.to be_uninstallable }\n    it { is_expected.to be_uninstall_options }\n    it { is_expected.to be_upgradeable }\n    it { is_expected.to be_versionable }\n  end\n\n  before :each do\n    # Stub some provider methods to avoid needing the actual software\n    # installed, so we can test on whatever platform we want.\n    allow(described_class).to receive(:command).with(:pkginfo).and_return('/bin/pkg_info')\n    allow(described_class).to receive(:command).with(:pkgadd).and_return('/bin/pkg_add')\n    allow(described_class).to receive(:command).with(:pkgdelete).and_return('/bin/pkg_delete')\n\n    allow(Puppet::FileSystem).to receive(:exist?)\n  end\n\n  context \"#instances\" do\n    it \"should return nil if execution failed\" do\n      expect(described_class).to receive(:execpipe).and_raise(Puppet::ExecutionFailure, 'wawawa')\n      expect(described_class.instances).to be_nil\n    end\n\n    it \"should return the empty set if no packages are listed\" do\n      expect(described_class).to receive(:execpipe).with(%w{/bin/pkg_info -a}).and_yield(StringIO.new(''))\n      expect(described_class.instances).to be_empty\n    end\n\n    it \"should return all packages when invoked\" do\n      fixture = File.read(my_fixture('pkginfo.list'))\n      expect(described_class).to receive(:execpipe).with(%w{/bin/pkg_info -a}).and_yield(fixture)\n      expect(described_class.instances.map(&:name).sort).to eq(\n        %w{bash bzip2 expat gettext libiconv lzo openvpn python vim wget}.sort\n      )\n    end\n\n    it \"should return all flavors if set\" do\n      fixture = File.read(my_fixture('pkginfo_flavors.list'))\n      expect(described_class).to receive(:execpipe).with(%w{/bin/pkg_info -a}).and_yield(fixture)\n      instances = described_class.instances.map {|p| {:name => p.get(:name),\n        :ensure => p.get(:ensure), :flavor => p.get(:flavor)}}\n      expect(instances.size).to eq(2)\n      expect(instances[0]).to eq({:name => 'bash', :ensure => '3.1.17', :flavor => 'static'})\n      expect(instances[1]).to eq({:name => 'vim',  :ensure => '7.0.42', :flavor => 'no_x11'})\n    end\n  end\n\n  context \"#install\" do\n    it \"should fail if the resource doesn't have a source\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with('/etc/pkg.conf').and_return(false)\n\n      expect {\n        provider.install\n      }.to raise_error(Puppet::Error, /must specify a package source/)\n    end\n\n    it \"should fail if /etc/pkg.conf exists, but is not readable\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with('/etc/pkg.conf').and_return(true)\n      expect(File).to receive(:open).with('/etc/pkg.conf', 'rb').and_raise(Errno::EACCES)\n\n      expect {\n        provider.install\n      }.to raise_error(Errno::EACCES, /Permission denied/)\n    end\n\n    it \"should fail if /etc/pkg.conf exists, but there is no installpath\" do\n      expect_read_from_pkgconf([])\n      expect {\n        provider.install\n      }.to raise_error(Puppet::Error, /No valid installpath found in \\/etc\\/pkg\\.conf and no source was set/)\n    end\n\n    it \"should install correctly when given a directory-unlike source\" do\n      source = '/whatever.tgz'\n      provider.resource[:source] = source\n      expect_pkgadd_with_source(source)\n\n      provider.install\n    end\n\n    it \"should install correctly when given a directory-like source\" do\n      source = '/whatever/'\n      provider.resource[:source] = source\n      expect_pkgadd_with_env_and_name(source) do\n        provider.install\n      end\n    end\n\n    it \"should install correctly when given a CDROM installpath\" do\n      dir = '/mnt/cdrom/5.2/packages/amd64/'\n      expect_read_from_pkgconf([\"installpath = #{dir}\"])\n      expect_pkgadd_with_env_and_name(dir) do\n        provider.install\n      end\n    end\n\n    it \"should install correctly when given a ftp mirror\" do\n      url = 'ftp://your.ftp.mirror/pub/OpenBSD/5.2/packages/amd64/'\n      expect_read_from_pkgconf([\"installpath = #{url}\"])\n      expect_pkgadd_with_env_and_name(url) do\n        provider.install\n      end\n    end\n\n    it \"should set the resource's source parameter\" do\n      url = 'ftp://your.ftp.mirror/pub/OpenBSD/5.2/packages/amd64/'\n      expect_read_from_pkgconf([\"installpath = #{url}\"])\n      expect_pkgadd_with_env_and_name(url) do\n        provider.install\n      end\n\n      expect(provider.resource[:source]).to eq(url)\n    end\n\n    it \"should strip leading whitespace in installpath\" do\n      dir = '/one/'\n      lines = [\"# Notice the extra spaces after the ='s\\n\",\n               \"installpath =   #{dir}\\n\",\n               \"# And notice how each line ends with a newline\\n\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_env_and_name(dir) do\n        provider.install\n      end\n    end\n\n    it \"should not require spaces around the equals\" do\n      dir = '/one/'\n      lines = [\"installpath=#{dir}\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_env_and_name(dir) do\n        provider.install\n      end\n    end\n\n    it \"should be case-insensitive\" do\n      dir = '/one/'\n      lines = [\"INSTALLPATH = #{dir}\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_env_and_name(dir) do\n        provider.install\n      end\n    end\n\n    it \"should ignore unknown keywords\" do\n      dir = '/one/'\n      lines = [\"foo = bar\\n\",\n               \"installpath = #{dir}\\n\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_env_and_name(dir) do\n        provider.install\n      end\n    end\n\n    it \"should preserve trailing spaces\" do\n      dir = '/one/   '\n      lines = [\"installpath = #{dir}\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_source(dir)\n\n      provider.install\n    end\n\n    it \"should append installpath\" do\n      urls = [\"ftp://your.ftp.mirror/pub/OpenBSD/5.2/packages/amd64/\",\n              \"http://another.ftp.mirror/pub/OpenBSD/5.2/packages/amd64/\"]\n      lines = [\"installpath  = #{urls[0]}\\n\",\n               \"installpath += #{urls[1]}\\n\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_env_and_name(urls.join(\":\")) do\n        provider.install\n      end\n    end\n\n    it \"should handle append on first installpath\" do\n      url = \"ftp://your.ftp.mirror/pub/OpenBSD/5.2/packages/amd64/\"\n      lines = [\"installpath += #{url}\\n\"]\n\n      expect_read_from_pkgconf(lines)\n      expect_pkgadd_with_env_and_name(url) do\n        provider.install\n      end\n    end\n\n    %w{ installpath installpath= installpath+=}.each do |line|\n      it \"should reject '#{line}'\" do\n        expect_read_from_pkgconf([line])\n        expect {\n          provider.install\n        }.to raise_error(Puppet::Error, /No valid installpath found in \\/etc\\/pkg\\.conf and no source was set/)\n      end\n    end\n\n    it 'should use install_options as Array' do\n      provider.resource[:source] = '/tma1/'\n      provider.resource[:install_options] = ['-r', '-z']\n      expect(provider).to receive(:pkgadd).with(['-r', '-z', 'bash'])\n      provider.install\n    end\n  end\n\n  context \"#latest\"  do\n    before do\n      provider.resource[:source] = '/tmp/tcsh.tgz'\n      provider.resource[:name] = 'tcsh'\n      allow(provider).to receive(:pkginfo).with('tcsh')\n    end\n\n    it \"should return the ensure value if the package is already installed\" do\n      allow(provider).to receive(:properties).and_return({:ensure => '4.2.45'})\n      allow(provider).to receive(:pkginfo).with('-Q', 'tcsh')\n      expect(provider.latest).to eq('4.2.45')\n    end\n\n    it \"should recognize a new version\" do\n      pkginfo_query = 'tcsh-6.18.01p1'\n      allow(provider).to receive(:pkginfo).with('-Q', 'tcsh').and_return(pkginfo_query)\n      expect(provider.latest).to eq('6.18.01p1')\n    end\n\n    it \"should recognize a newer version\" do\n      allow(provider).to receive(:properties).and_return({:ensure => '1.6.8'})\n      pkginfo_query = 'tcsh-1.6.10'\n      allow(provider).to receive(:pkginfo).with('-Q', 'tcsh').and_return(pkginfo_query)\n      expect(provider.latest).to eq('1.6.10')\n    end\n\n    it \"should recognize a package that is already the newest\" do\n      pkginfo_query = 'tcsh-6.18.01p0 (installed)'\n      allow(provider).to receive(:pkginfo).with('-Q', 'tcsh').and_return(pkginfo_query)\n      expect(provider.latest).to eq('6.18.01p0')\n    end\n  end\n\n  context \"#get_full_name\" do\n    it \"should return the full unversioned package name when updating with a flavor\" do\n      provider.resource[:ensure] = 'latest'\n      provider.resource[:flavor] = 'static'\n      expect(provider.get_full_name).to eq('bash--static')\n    end\n\n    it \"should return the full unversioned package name when updating without a flavor\" do\n        provider.resource[:name] = 'puppet'\n        provider.resource[:ensure] = 'latest'\n        expect(provider.get_full_name).to eq('puppet')\n    end\n\n    it \"should use the ensure parameter if it is numeric\" do\n      provider.resource[:name] = 'zsh'\n      provider.resource[:ensure] = '1.0'\n      expect(provider.get_full_name).to eq('zsh-1.0')\n    end\n\n    it \"should lookup the correct version\" do\n      output = 'bash-3.1.17         GNU Bourne Again Shell'\n      expect(provider).to receive(:execpipe).with(%w{/bin/pkg_info -I bash}).and_yield(output)\n      expect(provider.get_full_name).to eq('bash-3.1.17')\n    end\n\n    it \"should lookup the correction version with flavors\" do\n      provider.resource[:name] = 'fossil'\n      provider.resource[:flavor] = 'static'\n      output = 'fossil-1.29v0-static simple distributed software configuration management'\n      expect(provider).to receive(:execpipe).with(%w{/bin/pkg_info -I fossil}).and_yield(output)\n      expect(provider.get_full_name).to eq('fossil-1.29v0-static')\n    end\n  end\n\n  context \"#get_version\" do\n    it \"should return nil if execution fails\" do\n      expect(provider).to receive(:execpipe).and_raise(Puppet::ExecutionFailure, 'wawawa')\n      expect(provider.get_version).to be_nil\n    end\n\n    it \"should return the package version if in the output\" do\n      output = 'bash-3.1.17         GNU Bourne Again Shell'\n      expect(provider).to receive(:execpipe).with(%w{/bin/pkg_info -I bash}).and_yield(output)\n      expect(provider.get_version).to eq('3.1.17')\n    end\n\n    it \"should return the empty string if the package is not present\" do\n      provider.resource[:name] = 'zsh'\n      expect(provider).to receive(:execpipe).with(%w{/bin/pkg_info -I zsh}).and_yield(StringIO.new(''))\n      expect(provider.get_version).to eq('')\n    end\n  end\n\n  context \"#query\" do\n    it \"should return the installed version if present\" do\n      fixture = File.read(my_fixture('pkginfo.detail'))\n      expect(provider).to receive(:pkginfo).with('bash').and_return(fixture)\n      expect(provider.query).to eq({ :ensure => '3.1.17' })\n    end\n\n    it \"should return nothing if not present\" do\n      provider.resource[:name] = 'zsh'\n      expect(provider).to receive(:pkginfo).with('zsh').and_return('')\n      expect(provider.query).to be_nil\n    end\n  end\n\n  context \"#install_options\" do\n    it \"should return nill by default\" do\n      expect(provider.install_options).to be_nil\n    end\n\n    it \"should return install_options when set\" do\n      provider.resource[:install_options] = ['-n']\n      expect(provider.resource[:install_options]).to eq(['-n'])\n    end\n\n    it \"should return multiple install_options when set\" do\n      provider.resource[:install_options] = ['-L', '/opt/puppet']\n      expect(provider.resource[:install_options]).to eq(['-L', '/opt/puppet'])\n    end\n\n    it 'should return install_options when set as hash' do\n      provider.resource[:install_options] = { '-Darch' => 'vax' }\n      expect(provider.install_options).to eq(['-Darch=vax'])\n    end\n  end\n\n  context \"#uninstall_options\" do\n    it \"should return nill by default\" do\n      expect(provider.uninstall_options).to be_nil\n    end\n\n    it \"should return uninstall_options when set\" do\n      provider.resource[:uninstall_options] = ['-n']\n      expect(provider.resource[:uninstall_options]).to eq(['-n'])\n    end\n\n    it \"should return multiple uninstall_options when set\" do\n      provider.resource[:uninstall_options] = ['-q', '-c']\n      expect(provider.resource[:uninstall_options]).to eq(['-q', '-c'])\n    end\n\n    it 'should return uninstall_options when set as hash' do\n      provider.resource[:uninstall_options] = { '-Dbaddepend' => '1' }\n      expect(provider.uninstall_options).to eq(['-Dbaddepend=1'])\n    end\n  end\n\n  context \"#uninstall\" do\n    describe 'when uninstalling' do\n      it 'should use erase to purge' do\n        expect(provider).to receive(:pkgdelete).with('-c', '-q', 'bash')\n        provider.purge\n      end\n    end\n\n    describe 'with uninstall_options' do\n      it 'should use uninstall_options as Array' do\n        provider.resource[:uninstall_options] = ['-q', '-c']\n        expect(provider).to receive(:pkgdelete).with(['-q', '-c'], 'bash')\n        provider.uninstall\n      end\n    end\n  end\n\n  context \"#flavor\" do\n    before do\n      provider.instance_variable_get('@property_hash')[:flavor] = 'no_x11-python'\n    end\n\n    it 'should return the existing flavor' do\n      expect(provider.flavor).to eq('no_x11-python')\n    end\n\n    it 'should remove and install the new flavor if different' do\n      provider.resource[:flavor] = 'no_x11-ruby'\n      expect(provider).to receive(:uninstall).ordered\n      expect(provider).to receive(:install).ordered\n      provider.flavor = provider.resource[:flavor]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/opkg_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:opkg) do\n\n  let(:resource) do\n    Puppet::Type.type(:package).new(:name => 'package')\n  end\n\n  let(:provider) { described_class.new(resource) }\n\n  before do\n    allow(Puppet::Util).to receive(:which).with(\"opkg\").and_return(\"/bin/opkg\")\n    allow(provider).to receive(:package_lists).and_return(['.', '..', 'packages'])\n  end\n\n  describe \"when installing\" do\n    before do\n      allow(provider).to receive(:query).and_return({ :ensure => '1.0' })\n    end\n\n    context \"when the package list is absent\" do\n      before do\n        allow(provider).to receive(:package_lists).and_return(['.', '..'])  #empty, no package list\n      end\n\n      it \"fetches the package list when installing\" do\n        expect(provider).to receive(:opkg).with('update')\n        expect(provider).to receive(:opkg).with(\"--force-overwrite\", \"install\", resource[:name])\n\n        provider.install\n      end\n    end\n\n    context \"when the package list is present\" do\n      before do\n        allow(provider).to receive(:package_lists).and_return(['.', '..', 'lists'])  # With a pre-downloaded package list\n      end\n\n      it \"fetches the package list when installing\" do\n        expect(provider).not_to receive(:opkg).with('update')\n        expect(provider).to receive(:opkg).with(\"--force-overwrite\", \"install\", resource[:name])\n\n        provider.install\n      end\n    end\n\n    it \"should call opkg install\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/bin/opkg\", \"--force-overwrite\", \"install\", resource[:name]], {:failonfail => true, :combine => true, :custom_environment => {}})\n      provider.install\n    end\n\n    context \"when :source is specified\" do\n      context \"works on valid urls\" do\n        %w{\n          /some/package/file\n          http://some.package.in/the/air\n          ftp://some.package.in/the/air\n        }.each do |source|\n          it \"should install #{source} directly\" do\n            resource[:source] = source\n            expect(Puppet::Util::Execution).to receive(:execute).with([\"/bin/opkg\", \"--force-overwrite\", \"install\", resource[:source]], {:failonfail => true, :combine => true, :custom_environment => {}})\n            provider.install\n          end\n        end\n      end\n\n      context \"as a file:// URL\" do\n        before do\n          @package_file = \"file:///some/package/file\"\n          @actual_file_path = \"/some/package/file\"\n          resource[:source] = @package_file\n        end\n\n        it \"should install from the path segment of the URL\" do\n          expect(Puppet::Util::Execution).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new(\"\", 0))\n          provider.install\n        end\n      end\n\n      context \"with invalid URL for opkg\" do\n        before do\n          # Emulate the `opkg` command returning a non-zero exit value\n          allow(Puppet::Util::Execution).to receive(:execute).and_raise(Puppet::ExecutionFailure, 'oops')\n        end\n\n        context \"puppet://server/whatever\" do\n          before do\n            resource[:source] = \"puppet://server/whatever\"\n          end\n\n          it \"should fail\" do\n            expect { provider.install }.to raise_error Puppet::ExecutionFailure\n          end\n        end\n\n        context \"as a malformed URL\" do\n          before do\n            resource[:source] = \"blah://\"\n          end\n\n          it \"should fail\" do\n            expect { provider.install }.to raise_error Puppet::ExecutionFailure\n          end\n        end\n      end\n    end # end when source is specified\n  end # end when installing\n\n  describe \"when updating\" do\n    it \"should call install\" do\n      expect(provider).to receive(:install).and_return(\"install return value\")\n      expect(provider.update).to eq(\"install return value\")\n    end\n  end\n\n  describe \"when uninstalling\" do\n    it \"should run opkg remove bla\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with([\"/bin/opkg\", \"remove\", resource[:name]], {:failonfail => true, :combine => true, :custom_environment => {}})\n      provider.uninstall\n    end\n  end\n\n  describe \"when querying\" do\n    describe \"self.instances\" do\n      let (:packages) do\n        <<-OPKG_OUTPUT\ndropbear - 2011.54-2\nkernel - 3.3.8-1-ba5cdb2523b4fc7722698b4a7ece6702\nuhttpd - 2012-10-30-e57bf6d8bfa465a50eea2c30269acdfe751a46fd\nOPKG_OUTPUT\n      end\n\n      it \"returns an array of packages\" do\n        allow(Puppet::Util).to receive(:which).with(\"opkg\").and_return(\"/bin/opkg\")\n        allow(described_class).to receive(:which).with(\"opkg\").and_return(\"/bin/opkg\")\n        expect(described_class).to receive(:execpipe).with(\"/bin/opkg list-installed\").and_yield(packages)\n\n        installed_packages = described_class.instances\n        expect(installed_packages.length).to eq(3)\n\n        expect(installed_packages[0].properties).to eq(\n          {\n            :provider => :opkg,\n            :name => \"dropbear\",\n            :ensure => \"2011.54-2\"\n          }\n        )\n        expect(installed_packages[1].properties).to eq(\n          {\n            :provider => :opkg,\n            :name => \"kernel\",\n            :ensure => \"3.3.8-1-ba5cdb2523b4fc7722698b4a7ece6702\"\n          }\n        )\n        expect(installed_packages[2].properties).to eq(\n          {\n            :provider => :opkg,\n            :name => \"uhttpd\",\n            :ensure => \"2012-10-30-e57bf6d8bfa465a50eea2c30269acdfe751a46fd\"\n          }\n        )\n      end\n    end\n\n    it \"should return a nil if the package isn't found\" do\n      expect(Puppet::Util::Execution).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new(\"\", 0))\n      expect(provider.query).to be_nil\n    end\n\n    it \"should return a hash indicating that the package is missing on error\" do\n      expect(Puppet::Util::Execution).to receive(:execute).and_raise(Puppet::ExecutionFailure.new(\"ERROR!\"))\n      expect(provider.query).to eq({\n        :ensure => :purged,\n        :status => 'missing',\n        :name => resource[:name],\n        :error => 'ok',\n      })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pacman_spec.rb",
    "content": "require 'spec_helper'\nrequire 'stringio'\n\ndescribe Puppet::Type.type(:package).provider(:pacman) do\n  let(:no_extra_options) { { :failonfail => true, :combine => true, :custom_environment => {} } }\n  let(:executor) { Puppet::Util::Execution }\n  let(:resolver) { Puppet::Util }\n\n  let(:resource) { Puppet::Type.type(:package).new(:name => 'package', :provider => 'pacman') }\n  let(:provider) { described_class.new(resource) }\n\n  before do\n    allow(resolver).to receive(:which).with('/usr/bin/pacman').and_return('/usr/bin/pacman')\n    allow(described_class).to receive(:which).with('/usr/bin/pacman').and_return('/usr/bin/pacman')\n    allow(resolver).to receive(:which).with('/usr/bin/yaourt').and_return('/usr/bin/yaourt')\n    allow(described_class).to receive(:which).with('/usr/bin/yaourt').and_return('/usr/bin/yaourt')\n    allow(described_class).to receive(:group?).and_return(false)\n    allow(described_class).to receive(:yaourt?).and_return(false)\n  end\n\n  describe \"when installing\" do\n    before do\n      allow(provider).to receive(:query).and_return({\n        :ensure => '1.0'\n      })\n    end\n\n    it \"should call pacman to install the right package quietly when yaourt is not installed\" do\n      args = ['--noconfirm', '--needed', '--noprogressbar', '--sync', resource[:name]]\n      expect(provider).to receive(:pacman).at_least(:once).with(*args).and_return('')\n      provider.install\n    end\n\n    it \"should call yaourt to install the right package quietly when yaourt is installed\" do\n      without_partial_double_verification do\n        allow(described_class).to receive(:yaourt?).and_return(true)\n        args = ['--noconfirm', '--needed', '--noprogressbar', '--sync', resource[:name]]\n        expect(provider).to receive(:yaourt).at_least(:once).with(*args).and_return('')\n        provider.install\n      end\n    end\n\n    it \"should raise an Puppet::Error if the installation failed\" do\n      allow(executor).to receive(:execute).and_return(\"\")\n      expect(provider).to receive(:query).and_return(nil)\n\n      expect {\n        provider.install\n      }.to raise_exception(Puppet::Error, /Could not find package/)\n    end\n\n    it \"should raise an Puppet::Error when trying to install a group and allow_virtual is false\" do\n      allow(described_class).to receive(:group?).and_return(true)\n      resource[:allow_virtual] = false\n      expect {\n        provider.install\n      }.to raise_error(Puppet::Error, /Refusing to install package group/)\n    end\n\n    it \"should not raise an Puppet::Error when trying to install a group and allow_virtual is true\" do\n      allow(described_class).to receive(:group?).and_return(true)\n      resource[:allow_virtual] = true\n      allow(executor).to receive(:execute).and_return(\"\")\n      provider.install\n    end\n\n    describe \"and install_options are given\" do\n      before do\n        resource[:install_options] = ['-x', {'--arg' => 'value'}]\n      end\n\n      it \"should call pacman to install the right package quietly when yaourt is not installed\" do\n        args = ['--noconfirm', '--needed', '--noprogressbar', '-x', '--arg=value', '--sync', resource[:name]]\n        expect(provider).to receive(:pacman).at_least(:once).with(*args).and_return('')\n        provider.install\n      end\n\n      it \"should call yaourt to install the right package quietly when yaourt is installed\" do\n        without_partial_double_verification do\n          expect(described_class).to receive(:yaourt?).and_return(true)\n          args = ['--noconfirm', '--needed', '--noprogressbar', '-x', '--arg=value', '--sync', resource[:name]]\n          expect(provider).to receive(:yaourt).at_least(:once).with(*args).and_return('')\n          provider.install\n        end\n      end\n    end\n\n    context \"when :source is specified\" do\n      let(:install_seq) { sequence(\"install\") }\n\n      context \"recognizable by pacman\" do\n        %w{\n          /some/package/file\n          http://some.package.in/the/air\n          ftp://some.package.in/the/air\n        }.each do |source|\n          it \"should install #{source} directly\" do\n            resource[:source] = source\n\n            expect(executor).to receive(:execute).\n              with(include(\"--update\") & include(source), no_extra_options).\n              ordered.\n              and_return(\"\")\n\n            provider.install\n          end\n        end\n      end\n\n      context \"as a file:// URL\" do\n        let(:actual_file_path) { \"/some/package/file\" }\n\n        before do\n          resource[:source] = \"file:///some/package/file\"\n        end\n\n        it \"should install from the path segment of the URL\" do\n          expect(executor).to receive(:execute).\n            with(include(\"--update\") & include(actual_file_path), no_extra_options).\n            ordered.\n            and_return(\"\")\n\n          provider.install\n        end\n      end\n\n      context \"as a puppet URL\" do\n        before do\n          resource[:source] = \"puppet://server/whatever\"\n        end\n\n        it \"should fail\" do\n          expect {\n            provider.install\n          }.to raise_error(Puppet::Error, /puppet:\\/\\/ URL is not supported/)\n        end\n      end\n\n      context \"as an unsupported URL scheme\" do\n        before do\n          resource[:source] = \"blah://foo.com\"\n        end\n\n        it \"should fail\" do\n          expect {\n            provider.install\n          }.to raise_error(Puppet::Error, /Source blah:\\/\\/foo\\.com is not supported/)\n        end\n      end\n    end\n  end\n\n  describe \"when updating\" do\n    it \"should call install\" do\n      expect(provider).to receive(:install).and_return(\"install return value\")\n      expect(provider.update).to eq(\"install return value\")\n    end\n  end\n\n  describe \"when purging\" do\n    it \"should call pacman to remove the right package and configs quietly\" do\n      args = [\"/usr/bin/pacman\", \"--noconfirm\", \"--noprogressbar\", \"--remove\", \"--nosave\", resource[:name]]\n      expect(executor).to receive(:execute).with(args, no_extra_options).and_return(\"\")\n      provider.purge\n    end\n  end\n\n  describe \"when uninstalling\" do\n    it \"should call pacman to remove the right package quietly\" do\n      args = [\"/usr/bin/pacman\", \"--noconfirm\", \"--noprogressbar\", \"--remove\", resource[:name]]\n      expect(executor).to receive(:execute).with(args, no_extra_options).and_return(\"\")\n      provider.uninstall\n    end\n\n    it \"should call yaourt to remove the right package quietly\" do\n      without_partial_double_verification do\n        allow(described_class).to receive(:yaourt?).and_return(true)\n        args = [\"--noconfirm\", \"--noprogressbar\", \"--remove\", resource[:name]]\n        expect(provider).to receive(:yaourt).with(*args)\n        provider.uninstall\n      end\n    end\n\n    it \"adds any uninstall_options\" do\n      resource[:uninstall_options] = ['-x', {'--arg' => 'value'}]\n      args = [\"/usr/bin/pacman\", \"--noconfirm\", \"--noprogressbar\", \"-x\", \"--arg=value\", \"--remove\", resource[:name]]\n      expect(executor).to receive(:execute).with(args, no_extra_options).and_return(\"\")\n      provider.uninstall\n    end\n\n    it \"should recursively remove packages when given a package group\" do\n      allow(described_class).to receive(:group?).and_return(true)\n      args = [\"/usr/bin/pacman\", \"--noconfirm\", \"--noprogressbar\", \"--remove\", \"--recursive\", resource[:name]]\n      expect(executor).to receive(:execute).with(args, no_extra_options).and_return(\"\")\n      provider.uninstall\n    end\n  end\n\n  describe \"when querying\" do\n    it \"should query pacman\" do\n      expect(executor).to receive(:execpipe).with([\"/usr/bin/pacman\", '--query'])\n      expect(executor).to receive(:execpipe).with([\"/usr/bin/pacman\", '--sync', '-gg', 'package'])\n      provider.query\n    end\n\n    it \"should return the version\" do\n      expect(executor).to receive(:execpipe).\n          with([\"/usr/bin/pacman\", \"--query\"]).and_yield(<<EOF)\notherpackage 1.2.3.4\npackage 1.01.3-2\nyetanotherpackage 1.2.3.4\nEOF\n      expect(executor).to receive(:execpipe).with(['/usr/bin/pacman', '--sync', '-gg', 'package']).and_yield('')\n\n      expect(provider.query).to eq({ :name => 'package', :ensure => '1.01.3-2', :provider => :pacman,  })\n    end\n\n    it \"should return a hash indicating that the package is missing\" do\n      expect(executor).to receive(:execpipe).twice.and_yield(\"\")\n      expect(provider.query).to be_nil\n    end\n\n    it \"should raise an error if execpipe fails\" do\n      expect(executor).to receive(:execpipe).and_raise(Puppet::ExecutionFailure.new(\"ERROR!\"))\n\n      expect { provider.query }.to raise_error(RuntimeError)\n    end\n\n    describe 'when querying a group' do\n      before :each do\n        expect(executor).to receive(:execpipe).with(['/usr/bin/pacman', '--query']).and_yield('foo 1.2.3')\n        expect(executor).to receive(:execpipe).with(['/usr/bin/pacman', '--sync', '-gg', 'package']).and_yield('package foo')\n      end\n\n      it 'should warn when allow_virtual is false' do\n        resource[:allow_virtual] = false\n        expect(provider).to receive(:warning)\n        provider.query\n      end\n\n      it 'should not warn allow_virtual is true' do\n        resource[:allow_virtual] = true\n        expect(described_class).not_to receive(:warning)\n        provider.query\n      end\n    end\n  end\n\n  describe \"when determining instances\" do\n    it \"should retrieve installed packages and groups\" do\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--query'])\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--sync', '-gg'])\n      described_class.instances\n    end\n\n    it \"should return installed packages\" do\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--query']).and_yield(StringIO.new(\"package1 1.23-4\\npackage2 2.00\\n\"))\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--sync', '-gg']).and_yield(\"\")\n      instances = described_class.instances\n\n      expect(instances.length).to eq(2)\n\n      expect(instances[0].properties).to eq({\n          :provider => :pacman,\n          :ensure => '1.23-4',\n          :name => 'package1'\n      })\n\n      expect(instances[1].properties).to eq({\n          :provider => :pacman,\n          :ensure => '2.00',\n          :name => 'package2'\n      })\n    end\n\n    it \"should return completely installed groups with a virtual version together with packages\" do\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--query']).and_yield(<<EOF)\npackage1 1.00\npackage2 1.00\nEOF\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--sync', '-gg']).and_yield(<<EOF)\ngroup1 package1\ngroup1 package2\nEOF\n      instances = described_class.instances\n\n      expect(instances.length).to eq(3)\n\n      expect(instances[0].properties).to eq({\n        :provider => :pacman,\n        :ensure   => '1.00',\n        :name     => 'package1'\n      })\n      expect(instances[1].properties).to eq({\n        :provider => :pacman,\n        :ensure   => '1.00',\n        :name     => 'package2'\n      })\n      expect(instances[2].properties).to eq({\n        :provider => :pacman,\n        :ensure   => 'package1 1.00, package2 1.00',\n        :name     => 'group1'\n      })\n    end\n\n    it \"should not return partially installed packages\" do\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--query']).and_yield(<<EOF)\npackage1 1.00\nEOF\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/pacman\", '--sync', '-gg']).and_yield(<<EOF)\ngroup1 package1\ngroup1 package2\nEOF\n      instances = described_class.instances\n\n      expect(instances.length).to eq(1)\n\n      expect(instances[0].properties).to eq({\n        :provider => :pacman,\n        :ensure   => '1.00',\n        :name     => 'package1'\n      })\n    end\n\n    it 'should sort package names for installed groups' do\n      expect(described_class).to receive(:execpipe).with(['/usr/bin/pacman', '--sync', '-gg', 'group1']).and_yield(<<EOF)\ngroup1 aa\ngroup1 b\ngroup1 a\nEOF\n      package_versions= {\n        'a' => '1',\n        'aa' => '1',\n        'b' => '1',\n      }\n\n      virtual_group_version = described_class.get_installed_groups(package_versions, 'group1')\n      expect(virtual_group_version).to eq({ 'group1' => 'a 1, aa 1, b 1' })\n    end\n\n    it \"should return nil on error\" do\n      expect(described_class).to receive(:execpipe).and_raise(Puppet::ExecutionFailure.new(\"ERROR!\"))\n      expect { described_class.instances }.to raise_error(RuntimeError)\n    end\n\n    it \"should warn on invalid input\" do\n      expect(described_class).to receive(:execpipe).twice.and_yield(StringIO.new(\"blah\"))\n      expect(described_class).to receive(:warning).with(\"Failed to match line 'blah'\")\n      expect(described_class.instances).to eq([])\n    end\n  end\n\n  describe \"when determining the latest version\" do\n    it \"should get query pacman for the latest version\" do\n      expect(executor).to receive(:execute).\n        ordered.\n        with(['/usr/bin/pacman', '--sync', '--print', '--print-format', '%v', resource[:name]], no_extra_options).\n        and_return(\"\")\n\n      provider.latest\n    end\n\n    it \"should return the version number from pacman\" do\n      expect(executor).to receive(:execute).at_least(:once).and_return(\"1.00.2-3\\n\")\n\n      expect(provider.latest).to eq(\"1.00.2-3\")\n    end\n\n    it \"should return a virtual group version when resource is a package group\" do\n      allow(described_class).to receive(:group?).and_return(true)\n      expect(executor).to receive(:execute).with(['/usr/bin/pacman', '--sync', '--print', '--print-format', '%n %v', resource[:name]], no_extra_options).ordered.\n        and_return(<<EOF)\npackage2 1.0.1\npackage1 1.0.0\nEOF\n      expect(provider.latest).to eq('package1 1.0.0, package2 1.0.1')\n    end\n  end\n\n  describe 'when determining if a resource is a group' do\n    before do\n      allow(described_class).to receive(:group?).and_call_original\n    end\n\n    it 'should return false on non-zero pacman exit' do\n      allow(executor).to receive(:execute).with(['/usr/bin/pacman', '--sync', '--groups', 'git'], {:failonfail => true, :combine => true, :custom_environment => {}}).and_raise(Puppet::ExecutionFailure, 'error')\n      expect(described_class.group?('git')).to eq(false)\n    end\n\n    it 'should return false on empty pacman output' do\n      allow(executor).to receive(:execute).with(['/usr/bin/pacman', '--sync', '--groups', 'git'], {:failonfail => true, :combine => true, :custom_environment => {}}).and_return('')\n      expect(described_class.group?('git')).to eq(false)\n    end\n\n    it 'should return true on non-empty pacman output' do\n      allow(executor).to receive(:execute).with(['/usr/bin/pacman', '--sync', '--groups', 'vim-plugins'], {:failonfail => true, :combine => true, :custom_environment => {}}).and_return('vim-plugins vim-a')\n      expect(described_class.group?('vim-plugins')).to eq(true)\n    end\n  end\n\n  describe 'when querying installed groups' do\n    let(:installed_packages) { {'package1' => '1.0', 'package2' => '2.0', 'package3' => '3.0'} }\n    let(:groups) { [['foo package1'], ['foo package2'], ['bar package3'], ['bar package4'], ['baz package5']] }\n\n    it 'should raise an error on non-zero pacman exit without a filter' do\n      expect(executor).to receive(:open).with('| /usr/bin/pacman --sync -gg 2>&1').and_return('error!')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(1)\n      expect { described_class.get_installed_groups(installed_packages) }.to raise_error(Puppet::ExecutionFailure, 'error!')\n    end\n\n    it 'should return empty groups on non-zero pacman exit with a filter' do\n      expect(executor).to receive(:open).with('| /usr/bin/pacman --sync -gg git 2>&1').and_return('')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(1)\n      expect(described_class.get_installed_groups(installed_packages, 'git')).to eq({})\n    end\n\n    it 'should return empty groups on empty pacman output' do\n      pipe = double()\n      expect(pipe).to receive(:each_line)\n      expect(executor).to receive(:open).with('| /usr/bin/pacman --sync -gg 2>&1').and_yield(pipe).and_return('')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(0)\n      expect(described_class.get_installed_groups(installed_packages)).to eq({})\n    end\n\n    it 'should return groups on non-empty pacman output' do\n      pipe = double()\n      pipe_expectation = receive(:each_line)\n      groups.each { |group| pipe_expectation = pipe_expectation.and_yield(*group) }\n      expect(pipe).to pipe_expectation\n      expect(executor).to receive(:open).with('| /usr/bin/pacman --sync -gg 2>&1').and_yield(pipe).and_return('')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(0)\n      expect(described_class.get_installed_groups(installed_packages)).to eq({'foo' => 'package1 1.0, package2 2.0'})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pip2_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:pip2) do\n\n  it { is_expected.to be_installable }\n  it { is_expected.to be_uninstallable }\n  it { is_expected.to be_upgradeable }\n  it { is_expected.to be_versionable }\n  it { is_expected.to be_install_options }\n  it { is_expected.to be_targetable }\n\n  it \"should inherit most things from pip provider\" do\n    expect(described_class < Puppet::Type.type(:package).provider(:pip))\n  end\n\n  it \"should use pip2 command\" do\n    expect(described_class.cmd).to eq([\"pip2\"])\n  end\n\n  context 'calculated specificity' do\n    include_context 'provider specificity'\n\n    context 'when is not defaultfor' do\n      subject { described_class.specificity }\n      it { is_expected.to eql 1 }\n    end\n\n    context 'when is defaultfor' do\n      let(:os) { Puppet.runtime[:facter].value('os.name') }\n      subject do\n        described_class.defaultfor('os.name': os)\n        described_class.specificity\n      end\n      it { is_expected.to be > 100 }\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pip3_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:pip3) do\n\n  it { is_expected.to be_installable }\n  it { is_expected.to be_uninstallable }\n  it { is_expected.to be_upgradeable }\n  it { is_expected.to be_versionable }\n  it { is_expected.to be_install_options }\n  it { is_expected.to be_targetable }\n\n  it \"should inherit most things from pip provider\" do\n    expect(described_class < Puppet::Type.type(:package).provider(:pip))\n  end\n\n  it \"should use pip3 command\" do\n    expect(described_class.cmd).to eq([\"pip3\"])\n  end\n\n  context 'calculated specificity' do\n    include_context 'provider specificity'\n\n    context 'when is not defaultfor' do\n      subject { described_class.specificity }\n      it { is_expected.to eql 1 }\n    end\n\n    context 'when is defaultfor' do\n      let(:os) { Puppet.runtime[:facter].value('os.name') }\n      subject do\n        described_class.defaultfor('os.name': os)\n        described_class.specificity\n      end\n      it { is_expected.to be > 100 }\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pip_spec.rb",
    "content": "require 'spec_helper'\n\nosfamilies = { 'windows' => ['pip.exe'], 'other' => ['pip', 'pip-python', 'pip2', 'pip-2'] }\npip_path_with_spaces = 'C:\\Program Files (x86)\\Python\\Scripts\\pip.exe'\n\ndescribe Puppet::Type.type(:package).provider(:pip) do\n\n  it { is_expected.to be_installable }\n  it { is_expected.to be_uninstallable }\n  it { is_expected.to be_upgradeable }\n  it { is_expected.to be_versionable }\n  it { is_expected.to be_install_options }\n  it { is_expected.to be_targetable }\n  it { is_expected.to be_version_ranges }\n\n  before do\n    @resource = Puppet::Type.type(:package).new(name: \"fake_package\", provider: :pip)\n    @provider = @resource.provider\n    @client = double('client')\n    allow(@client).to receive(:call).with('package_releases', 'real_package').and_return([\"1.3\", \"1.2.5\", \"1.2.4\"])\n    allow(@client).to receive(:call).with('package_releases', 'fake_package').and_return([])\n  end\n\n  context \"parse\" do\n    it \"should return a hash on valid input\" do\n      expect(described_class.parse(\"real_package==1.2.5\")).to eq({\n        :ensure   => \"1.2.5\",\n        :name     => \"real_package\",\n        :provider => :pip,\n      })\n    end\n\n    it \"should correctly parse arbitrary equality\" do\n      expect(described_class.parse(\"real_package===1.2.5\")).to eq({\n        :ensure   => \"1.2.5\",\n        :name     => \"real_package\",\n        :provider => :pip,\n      })\n    end\n\n    it \"should correctly parse URL format\" do\n      expect(described_class.parse(\"real_package @ git+https://github.com/example/test.git@6b4e203b66c1de7345984882e2b13bf87c700095\")).to eq({\n        :ensure   => \"6b4e203b66c1de7345984882e2b13bf87c700095\",\n        :name     => \"real_package\",\n        :provider => :pip,\n      })\n    end\n\n    it \"should return nil on invalid input\" do\n      expect(described_class.parse(\"foo\")).to eq(nil)\n    end\n  end\n\n  context \"cmd\" do\n    it \"should return 'pip.exe' by default on Windows systems\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n      expect(described_class.cmd[0]).to eq('pip.exe')\n    end\n\n    it \"could return pip-python on legacy redhat systems which rename pip\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      expect(described_class.cmd[1]).to eq('pip-python')\n    end\n\n    it \"should return pip by default on other systems\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      expect(described_class.cmd[0]).to eq('pip')\n    end\n  end\n\n  context \"instances\" do\n    osfamilies.each do |osfamily, pip_cmds|\n      it \"should return an array on #{osfamily} systems when #{pip_cmds.join(' or ')} is present\" do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(osfamily == 'windows')\n        pip_cmds.each do |pip_cmd|\n          pip_cmds.each do |cmd|\n            unless cmd == pip_cmd\n              expect(described_class).to receive(:which).with(cmd).and_return(nil)\n            end\n          end\n          allow(described_class).to receive(:pip_version).with(pip_cmd).and_return('8.0.1')\n          expect(described_class).to receive(:which).with(pip_cmd).and_return(pip_cmd)\n          p = double(\"process\")\n          expect(p).to receive(:collect).and_yield(\"real_package==1.2.5\")\n          expect(described_class).to receive(:execpipe).with([pip_cmd, [\"freeze\"]]).and_yield(p)\n          described_class.instances\n        end\n      end\n\n      context \"with pip version >= 8.1.0\" do\n        versions = ['8.1.0', '9.0.1']\n        versions.each do |version|\n          it \"should use the --all option when version is '#{version}'\" do\n            allow(Puppet::Util::Platform).to receive(:windows?).and_return(osfamily == 'windows')\n            allow(described_class).to receive(:provider_command).and_return('/fake/bin/pip')\n            allow(described_class).to receive(:pip_version).with('/fake/bin/pip').and_return(version)\n            p = double(\"process\")\n            expect(p).to receive(:collect).and_yield(\"real_package==1.2.5\")\n            expect(described_class).to receive(:execpipe).with([\"/fake/bin/pip\", [\"freeze\", \"--all\"]]).and_yield(p)\n            described_class.instances\n          end\n        end\n      end\n\n      it \"should return an empty array on #{osfamily} systems when #{pip_cmds.join(' and ')} are missing\" do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(osfamily == 'windows')\n        pip_cmds.each do |cmd|\n          expect(described_class).to receive(:which).with(cmd).and_return(nil)\n        end\n        expect(described_class.instances).to eq([])\n      end\n    end\n\n    context \"when pip path location contains spaces\" do\n      it \"should quote the command before doing execpipe\" do\n        allow(described_class).to receive(:which).and_return(pip_path_with_spaces)\n        allow(described_class).to receive(:pip_version).with(pip_path_with_spaces).and_return('8.0.1')\n\n        expect(described_class).to receive(:execpipe).with([\"\\\"#{pip_path_with_spaces}\\\"\", [\"freeze\"]])\n        described_class.instances\n      end\n    end\n  end\n\n  context \"query\" do\n    before do\n      @resource[:name] = \"real_package\"\n      allow(described_class).to receive(:provider_command).and_return('/fake/bin/pip')\n      allow(described_class).to receive(:validate_command).with('/fake/bin/pip')\n    end\n\n    it \"should return a hash when pip and the package are present\" do\n      expect(described_class).to receive(:instances).and_return([described_class.new({\n        :ensure   => \"1.2.5\",\n        :name     => \"real_package\",\n        :provider => :pip,\n        :command  => '/fake/bin/pip',\n      })])\n\n      expect(@provider.query).to eq({\n        :ensure   => \"1.2.5\",\n        :name     => \"real_package\",\n        :provider => :pip,\n        :command  => '/fake/bin/pip',\n      })\n    end\n\n    it \"should return nil when the package is missing\" do\n      expect(described_class).to receive(:instances).and_return([])\n      expect(@provider.query).to eq(nil)\n    end\n\n    it \"should be case insensitive\" do\n      @resource[:name] = \"Real_Package\"\n\n      expect(described_class).to receive(:instances).and_return([described_class.new({\n        :ensure   => \"1.2.5\",\n        :name     => \"real_package\",\n        :provider => :pip,\n        :command  => '/fake/bin/pip',\n      })])\n\n      expect(@provider.query).to eq({\n        :ensure   => \"1.2.5\",\n        :name     => \"real_package\",\n        :provider => :pip,\n        :command  => '/fake/bin/pip',\n      })\n    end\n  end\n\n  context \"when comparing versions\" do\n    it \"an abnormal version should still be compared (using default implementation) but a debug message should also be printed regarding it\" do\n      expect(Puppet).to receive(:debug).with(\"Cannot compare 1.0 and abnormal-version.0.1. abnormal-version.0.1 is not a valid python package version. Please refer to https://www.python.org/dev/peps/pep-0440/. Falling through default comparison mechanism.\")\n      expect(Puppet::Util::Package).to receive(:versioncmp).with('1.0', 'abnormal-version.0.1')\n      expect{ described_class.compare_pip_versions('1.0', 'abnormal-version.0.1') }.not_to raise_error\n    end\n  end\n\n  context \"latest\" do\n    before do\n      allow(described_class).to receive(:pip_version).with(pip_path).and_return(pip_version)\n      allow(described_class).to receive(:which).with('pip').and_return(pip_path)\n      allow(described_class).to receive(:which).with('pip-python').and_return(pip_path)\n      allow(described_class).to receive(:which).with('pip.exe').and_return(pip_path)\n      allow(described_class).to receive(:provider_command).and_return(pip_path)\n      allow(described_class).to receive(:validate_command).with(pip_path)\n    end\n\n    context \"with pip version < 1.5.4\" do\n      let(:pip_version) { '1.0.1' }\n      let(:pip_path) { '/fake/bin/pip' }\n\n      it \"should find a version number for new_pip_package\" do\n        p = StringIO.new(\n          <<-EOS\n          Downloading/unpacking fake-package\n            Using version 0.10.1 (newest of versions: 0.10.1, 0.10, 0.9, 0.8.1, 0.8, 0.7.2, 0.7.1, 0.7, 0.6.1, 0.6, 0.5.2, 0.5.1, 0.5, 0.4, 0.3.1, 0.3, 0.2, 0.1)\n            Downloading real-package-0.10.1.tar.gz (544Kb): 544Kb downloaded\n          Saved ./foo/real-package-0.10.1.tar.gz\n          Successfully downloaded real-package\n          EOS\n        )\n        expect(Puppet::Util::Execution).to receive(:execpipe).and_yield(p).once\n        @resource[:name] = \"real_package\"\n        expect(@provider.latest).to eq('0.10.1')\n      end\n\n      it \"should not find a version number for fake_package\" do\n        p = StringIO.new(\n          <<-EOS\n          Downloading/unpacking fake-package\n            Could not fetch URL http://pypi.python.org/simple/fake_package: HTTP Error 404: Not Found\n            Will skip URL http://pypi.python.org/simple/fake_package when looking for download links for fake-package\n            Could not fetch URL http://pypi.python.org/simple/fake_package/: HTTP Error 404: Not Found\n            Will skip URL http://pypi.python.org/simple/fake_package/ when looking for download links for fake-package\n            Could not find any downloads that satisfy the requirement fake-package\n          No distributions at all found for fake-package\n          Exception information:\n          Traceback (most recent call last):\n            File \"/usr/lib/python2.7/dist-packages/pip/basecommand.py\", line 126, in main\n              self.run(options, args)\n            File \"/usr/lib/python2.7/dist-packages/pip/commands/install.py\", line 223, in run\n              requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)\n            File \"/usr/lib/python2.7/dist-packages/pip/req.py\", line 948, in prepare_files\n              url = finder.find_requirement(req_to_install, upgrade=self.upgrade)\n            File \"/usr/lib/python2.7/dist-packages/pip/index.py\", line 152, in find_requirement\n              raise DistributionNotFound('No distributions at all found for %s' % req)\n          DistributionNotFound: No distributions at all found for fake-package\n\n          Storing complete log in /root/.pip/pip.log\n          EOS\n        )\n        expect(Puppet::Util::Execution).to receive(:execpipe).and_yield(p).once\n        @resource[:name] = \"fake_package\"\n        expect(@provider.latest).to eq(nil)\n      end\n\n      it \"should handle out-of-order version numbers for real_package\" do\n        p = StringIO.new(\n          <<-EOS\n          Downloading/unpacking fake-package\n            Using version 2!2.3.4.alpha5.rev6.dev7+abc89 (newest of versions: 1.11, 13.0.3, 1.6, 1!1.2.rev33+123456, 1.9, 1.3.2, 14.0.1, 12.0.7, 13.0.3, 1.7.2, 1.8.4, 1.2+123abc456, 1.6.1, 0.9.2, 1.3, 1.8.3, 12.1.1, 1.1, 1.11.6, 1.2+123456, 1.4.8, 1.6.3, 1!1.0b2.post345.dev456, 1.10.1, 14.0.2, 1.11.3, 14.0.3, 1.4rc1, 1.0b2.post345.dev456, 0.8.4, 1.0, 1!1.0.post456, 12.0.5, 14.0.6, 1.11.5, 1.0rc2, 1.7.1.1, 1.11.4, 13.0.1, 13.1.2, 1.3.3, 0.8.2, 14.0.0, 12.0, 1.8, 1.3.4, 12.0, 1.2, 12.0.6, 0.9.1, 13.1.1, 2!2.3.4.alpha5.rev6.dev7+abc89, 14.0.5, 15.0.2, 15.0.0, 1.4.5, 1.4.3, 13.1.1, 1.11.2, 13.1.2, 1.2+abc123def, 1.3.1, 13.1.0, 12.0.2, 1.11.1, 12.0.1, 12.1.0, 0.9, 1.4.4, 1.2+abc123, 13.0.0, 1.4.9, 1.1.dev1, 12.1.0, 1.7.1, 1.4.2, 14.0.5, 0.8.1, 1.4.6, 0.8.3, 1.11.3, 1.5.1, 1.4.7, 13.0.2, 12.0.7, 1!13.0, 0!13.0, 1.9.1, 1.0.post456.dev34, 1.8.2, 14.0.1, 14.0.0, 1.2.rev33+123456, 14.0.4, 1.6.2, 15.0.1, 13.1.0, 0.8, 1.2+1234.abc, 1.7, 15.0.2, 12.0.5, 13.0.1, 1.8.1, 1.11.6, 15.0.1, 12.0.4, 1.2+123abc, 12.1.1, 13.0.2, 1.11.4, 1.10, 1.2.r32+123456, 14.0.4, 14.0.6, 1.4.1, 1.4, 1.5.2, 12.0.2, 12.0.1, 14.0.3, 14.0.2, 1.11.1, 1.7.1.2, 15.0.0, 12.0.4, 1.6.4, 1.11.2, 1.5, 0.1, 0.10, 0.10.1, 0.10.1.0.1, 1.0.dev456, 1.0a1, 1.0a2.dev456, 1.0a12.dev456, 1.0a12, 1.0b1.dev456, 1.0b2, 1.0b2.post345, 1.0b2-346, 1.0c1.dev456, 1.0c1, 1.0c3, 1.0, 1.0.post456, 1.2+abc, 1!1.0, 1!1.0.post456.dev34)\n            Downloading real-package-2!2.3.4.alpha5.rev6.dev7+abc89.tar.gz (544Kb): 544Kb downloaded\n          Saved ./foo/real-package-2!2.3.4.alpha5.rev6.dev7+abc89.tar.gz\n          Successfully downloaded real-package\n          EOS\n        )\n        expect(Puppet::Util::Execution).to receive(:execpipe).and_yield(p).once\n        @resource[:name] = \"real_package\"\n        expect(@provider.latest).to eq('2!2.3.4.alpha5.rev6.dev7+abc89')\n      end\n\n      it \"should use 'install_options' when specified\" do\n        expect(Puppet::Util::Execution).to receive(:execpipe).with(array_including([[\"--index=https://fake.example.com\"]])).once\n        @resource[:name] = \"fake_package\"\n        @resource[:install_options] = ['--index' => 'https://fake.example.com']\n        expect(@provider.latest).to eq(nil)\n      end\n\n      context \"when pip path location contains spaces\" do\n        let(:pip_path) { pip_path_with_spaces }\n\n        it \"should quote the command before doing execpipe\" do\n          expect(Puppet::Util::Execution).to receive(:execpipe).with(array_including(\"\\\"#{pip_path}\\\"\"))\n          @provider.latest\n        end\n      end\n    end\n\n    context \"with pip version >= 1.5.4\" do\n      # For Pip 1.5.4 and above, you can get a version list from CLI - which allows for native pip behavior\n      # with regards to custom repositories, proxies and the like\n      let(:pip_version) { '1.5.4' }\n      let(:pip_path) { '/fake/bin/pip' }\n\n      context \"with pip version >= 20.3 and < 21.1\" do\n        let(:pip_version) { '20.3.1' }\n        let(:pip_path) { '/fake/bin/pip' }\n\n        it \"should use legacy-resolver argument\" do\n          p = StringIO.new(\n            <<-EOS\n            Collecting real-package==9!0dev0+x\n              Could not find a version that satisfies the requirement real-package==9!0dev0+x (from versions: 1.1.3, 1.0, 1.9b1)\n            No matching distribution found for real-package==9!0dev0+x\n            EOS\n          )\n          expect(Puppet::Util::Execution).to receive(:execpipe).with([\"/fake/bin/pip\", \"install\", \"real_package==9!0dev0+x\",\n            \"--use-deprecated=legacy-resolver\"]).and_yield(p).once\n          @resource[:name] = \"real_package\"\n          @provider.latest\n        end\n      end\n\n      context \"with pip version >= 21.1\" do\n        let(:pip_version) { '21.1' }\n        let(:pip_path) { '/fake/bin/pip' }\n\n        it \"should not use legacy-resolver argument\" do\n          p = StringIO.new(\n            <<-EOS\n            Collecting real-package==9!0dev0+x\n              Could not find a version that satisfies the requirement real-package==9!0dev0+x (from versions: 1.1.3, 1.0, 1.9b1)\n            No matching distribution found for real-package==9!0dev0+x\n            EOS\n          )\n          expect(Puppet::Util::Execution).to receive(:execpipe).with([\"/fake/bin/pip\", \"install\", \"real_package==9!0dev0+x\"]).and_yield(p).once\n          @resource[:name] = \"real_package\"\n          @provider.latest\n        end\n      end\n\n      it \"should find a version number for real_package\" do\n        p = StringIO.new(\n          <<-EOS\n          Collecting real-package==9!0dev0+x\n            Could not find a version that satisfies the requirement real-package==9!0dev0+x (from versions: 1.1.3, 1.2, 1.9b1)\n          No matching distribution found for real-package==9!0dev0+x\n          EOS\n        )\n        expect(Puppet::Util::Execution).to receive(:execpipe).with([\"/fake/bin/pip\", \"install\", \"real_package==9!0dev0+x\"]).and_yield(p).once\n        @resource[:name] = \"real_package\"\n        latest = @provider.latest\n        expect(latest).to eq('1.9b1')\n      end\n\n      it \"should not find a version number for fake_package\" do\n        p = StringIO.new(\n          <<-EOS\n          Collecting fake-package==9!0dev0+x\n            Could not find a version that satisfies the requirement fake-package==9!0dev0+x (from versions: )\n          No matching distribution found for fake-package==9!0dev0+x\n          EOS\n        )\n        expect(Puppet::Util::Execution).to receive(:execpipe).with([\"/fake/bin/pip\", \"install\", \"fake_package==9!0dev0+x\"]).and_yield(p).once\n        @resource[:name] = \"fake_package\"\n        expect(@provider.latest).to eq(nil)\n      end\n\n      it \"should handle out-of-order version numbers for real_package\" do\n        p = StringIO.new(\n          <<-EOS\n          Collecting real-package==9!0dev0+x\n            Could not find a version that satisfies the requirement real-package==9!0dev0+x (from versions: 1.11, 13.0.3, 1.6, 1!1.2.rev33+123456, 1.9, 1.3.2, 14.0.1, 12.0.7, 13.0.3, 1.7.2, 1.8.4, 1.2+123abc456, 1.6.1, 0.9.2, 1.3, 1.8.3, 12.1.1, 1.1, 1.11.6, 1.2+123456, 1.4.8, 1.6.3, 1!1.0b2.post345.dev456, 1.10.1, 14.0.2, 1.11.3, 14.0.3, 1.4rc1, 1.0b2.post345.dev456, 0.8.4, 1.0, 1!1.0.post456, 12.0.5, 14.0.6, 1.11.5, 1.0rc2, 1.7.1.1, 1.11.4, 13.0.1, 13.1.2, 1.3.3, 0.8.2, 14.0.0, 12.0, 1.8, 1.3.4, 12.0, 1.2, 12.0.6, 0.9.1, 13.1.1, 2!2.3.4.alpha5.rev6.dev7+abc89, 14.0.5, 15.0.2, 15.0.0, 1.4.5, 1.4.3, 13.1.1, 1.11.2, 13.1.2, 1.2+abc123def, 1.3.1, 13.1.0, 12.0.2, 1.11.1, 12.0.1, 12.1.0, 0.9, 1.4.4, 1.2+abc123, 13.0.0, 1.4.9, 1.1.dev1, 12.1.0, 1.7.1, 1.4.2, 14.0.5, 0.8.1, 1.4.6, 0.8.3, 1.11.3, 1.5.1, 1.4.7, 13.0.2, 12.0.7, 1!13.0, 0!13.0, 1.9.1, 1.0.post456.dev34, 1.8.2, 14.0.1, 14.0.0, 1.2.rev33+123456, 14.0.4, 1.6.2, 15.0.1, 13.1.0, 0.8, 1.2+1234.abc, 1.7, 15.0.2, 12.0.5, 13.0.1, 1.8.1, 1.11.6, 15.0.1, 12.0.4, 1.2+123abc, 12.1.1, 13.0.2, 1.11.4, 1.10, 1.2.r32+123456, 14.0.4, 14.0.6, 1.4.1, 1.4, 1.5.2, 12.0.2, 12.0.1, 14.0.3, 14.0.2, 1.11.1, 1.7.1.2, 15.0.0, 12.0.4, 1.6.4, 1.11.2, 1.5, 0.1, 0.10, 0.10.1, 0.10.1.0.1, 1.0.dev456, 1.0a1, 1.0a2.dev456, 1.0a12.dev456, 1.0a12, 1.0b1.dev456, 1.0b2, 1.0b2.post345, 1.0b2-346, 1.0c1.dev456, 1.0c1, 1.0c3, 1.0, 1.0.post456, 1.2+abc, 1!1.0, 1!1.0.post456.dev34)\n          No distributions matching the version for real-package==9!0dev0+x\n          EOS\n        )\n        expect(Puppet::Util::Execution).to receive(:execpipe).with([\"/fake/bin/pip\", \"install\", \"real_package==9!0dev0+x\"]).and_yield(p).once\n        @resource[:name] = \"real_package\"\n        expect(@provider.latest).to eq('2!2.3.4.alpha5.rev6.dev7+abc89')\n      end\n\n      it \"should use 'install_options' when specified\" do\n        expect(Puppet::Util::Execution).to receive(:execpipe).with(array_including([[\"--index=https://fake.example.com\"]])).once\n        @resource[:name] = \"fake_package\"\n        @resource[:install_options] = ['--index' => 'https://fake.example.com']\n        expect(@provider.latest).to eq(nil)\n      end\n\n      context \"when pip path location contains spaces\" do\n        let(:pip_path) { pip_path_with_spaces }\n\n        it \"should quote the command before doing execpipe\" do\n          expect(Puppet::Util::Execution).to receive(:execpipe).with(array_including(\"\\\"#{pip_path}\\\"\"))\n          @provider.latest\n        end\n      end\n    end\n  end\n\n  context \"install\" do\n    before do\n      @resource[:name] = \"fake_package\"\n      @url = \"git+https://example.com/fake_package.git\"\n      allow(described_class).to receive(:provider_command).and_return('/fake/bin/pip')\n      allow(described_class).to receive(:validate_command).with('/fake/bin/pip')\n    end\n\n    it \"should install\" do\n      @resource[:ensure] = :installed\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"install\", \"-q\", \"fake_package\"]])\n      @provider.install\n    end\n\n    it \"omits the -e flag (GH-1256)\" do\n      # The -e flag makes the provider non-idempotent\n      @resource[:ensure] = :installed\n      @resource[:source] = @url\n      # TJK\n      expect(@provider).to receive(:execute) do |*args|\n        expect(args).not_to include(\"-e\")\n      end\n      @provider.install\n    end\n\n    it \"should install from SCM\" do\n      @resource[:ensure] = :installed\n      @resource[:source] = @url\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"install\", \"-q\", \"#{@url}#egg=fake_package\"]])\n      @provider.install\n    end\n\n    it \"should install a particular SCM revision\" do\n      @resource[:ensure] = \"0123456\"\n      @resource[:source] = @url\n      # TJK\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"install\", \"-q\", \"#{@url}@0123456#egg=fake_package\"]])\n      @provider.install\n    end\n\n    it \"should install a particular version\" do\n      @resource[:ensure] = \"0.0.0\"\n      # TJK\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"install\", \"-q\", \"fake_package==0.0.0\"]])\n      @provider.install\n    end\n\n    it \"should upgrade\" do\n      @resource[:ensure] = :latest\n      # TJK\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"install\", \"-q\", \"--upgrade\", \"fake_package\"]])\n      @provider.install\n    end\n\n    it \"should handle install options\" do\n      @resource[:ensure] = :installed\n      @resource[:install_options] = [{\"--timeout\" => \"10\"}, \"--no-index\"]\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"install\", \"-q\", \"--timeout=10\", \"--no-index\", \"fake_package\"]])\n      @provider.install\n    end\n  end\n\n  context \"uninstall\" do\n    before do\n      allow(described_class).to receive(:provider_command).and_return('/fake/bin/pip')\n      allow(described_class).to receive(:validate_command).with('/fake/bin/pip')\n    end\n\n    it \"should uninstall\" do\n      @resource[:name] = \"fake_package\"\n      expect(@provider).to receive(:execute).with([\"/fake/bin/pip\", [\"uninstall\", \"-y\", \"-q\", \"fake_package\"]])\n      @provider.uninstall\n    end\n  end\n\n  context \"update\" do\n    it \"should just call install\" do\n      expect(@provider).to receive(:install).and_return(nil)\n      @provider.update\n    end\n  end\n\n  context \"pip_version\" do\n    let(:pip) { '/fake/bin/pip' }\n\n    it \"should look up version if pip is present\" do\n      allow(described_class).to receive(:cmd).and_return(pip)\n      process = ['pip 8.0.2 from /usr/local/lib/python2.7/dist-packages (python 2.7)']\n      allow(described_class).to receive(:execpipe).with([pip, '--version']).and_yield(process)\n\n      expect(described_class.pip_version(pip)).to eq('8.0.2')\n    end\n\n    it \"parses multiple lines of output\" do\n      allow(described_class).to receive(:cmd).and_return(pip)\n      process = [\n        \"/usr/local/lib/python2.7/dist-packages/urllib3/contrib/socks.py:37: DependencyWarning: SOCKS support in urllib3 requires the installation of optional dependencies: specifically, PySocks. For more information, see https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies\",\n        \"  DependencyWarning\",\n        \"pip 1.5.6 from /usr/lib/python2.7/dist-packages (python 2.7)\"\n      ]\n      allow(described_class).to receive(:execpipe).with([pip, '--version']).and_yield(process)\n\n      expect(described_class.pip_version(pip)).to eq('1.5.6')\n    end\n\n    it \"raises if there isn't a version string\" do\n      allow(described_class).to receive(:cmd).and_return(pip)\n      allow(described_class).to receive(:execpipe).with([pip, '--version']).and_yield([\"\"])\n      expect {\n        described_class.pip_version(pip)\n      }.to raise_error(Puppet::Error, 'Cannot resolve pip version')\n    end\n\n    it \"quotes commands with spaces\" do\n      pip = 'C:\\Program Files\\Python27\\Scripts\\pip.exe'\n      allow(described_class).to receive(:cmd).and_return(pip)\n      process = [\"pip 18.1 from c:\\program files\\python27\\lib\\site-packages\\pip (python 2.7)\\r\\n\"]\n      allow(described_class).to receive(:execpipe).with([\"\\\"#{pip}\\\"\", '--version']).and_yield(process)\n\n      expect(described_class.pip_version(pip)).to eq('18.1')\n    end\n  end\n\n  context 'calculated specificity' do\n    include_context 'provider specificity'\n\n    context 'when is not defaultfor' do\n      subject { described_class.specificity }\n      it { is_expected.to eql 1 }\n    end\n\n    context 'when is defaultfor' do\n      let(:os) { Puppet.runtime[:facter].value('os.name') }\n      subject do\n        described_class.defaultfor('os.name': os)\n        described_class.specificity\n      end\n      it { is_expected.to be > 100 }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pkg_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:pkg), unless: Puppet::Util::Platform.jruby? do\n  let (:resource) { Puppet::Resource.new(:package, 'dummy', :parameters => {:name => 'dummy', :ensure => :latest}) }\n  let (:provider) { described_class.new(resource) }\n\n  before :each do\n    allow(described_class).to receive(:command).with(:pkg).and_return('/bin/pkg')\n  end\n\n  def self.it_should_respond_to(*actions)\n    actions.each do |action|\n      it \"should respond to :#{action}\" do\n        expect(provider).to respond_to(action)\n      end\n    end\n  end\n\n  it_should_respond_to :install, :uninstall, :update, :query, :latest\n\n  context 'default' do\n    [ 10 ].each do |ver|\n      it \"should not be the default provider on Solaris #{ver}\" do\n        allow(Facter).to receive(:value).with('os.family').and_return(:Solaris)\n        allow(Facter).to receive(:value).with(:kernelrelease).and_return(\"5.#{ver}\")\n        allow(Facter).to receive(:value).with('os.name').and_return(:Solaris)\n        allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n        expect(described_class).to_not be_default\n      end\n    end\n\n    [ 11, 12 ].each do |ver|\n      it \"should be the default provider on Solaris #{ver}\" do\n        allow(Facter).to receive(:value).with('os.family').and_return(:Solaris)\n        allow(Facter).to receive(:value).with(:kernelrelease).and_return(\"5.#{ver}\")\n        allow(Facter).to receive(:value).with('os.name').and_return(:Solaris)\n        allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n        expect(described_class).to be_default\n      end\n    end\n  end\n\n  it \"should be versionable\" do\n    expect(described_class).to be_versionable\n  end\n\n  describe \"#methods\" do\n    context \":pkg_state\" do\n      it \"should raise error on unknown values\" do\n        expect {\n          expect(described_class.pkg_state('extra')).to\n        }.to raise_error(ArgumentError, /Unknown format/)\n      end\n\n      ['known', 'installed'].each do |k|\n        it \"should return known values\" do\n          expect(described_class.pkg_state(k)).to eq({:status => k})\n        end\n      end\n    end\n\n    context \":ifo_flag\" do\n      it \"should raise error on unknown values\" do\n        expect {\n          expect(described_class.ifo_flag('x--')).to\n        }.to raise_error(ArgumentError, /Unknown format/)\n      end\n\n      {'i--' => 'installed', '---'=> 'known'}.each do |k, v|\n        it \"should return known values\" do\n          expect(described_class.ifo_flag(k)).to eq({:status => v})\n        end\n      end\n    end\n\n    context \":parse_line\" do\n      it \"should raise error on unknown values\" do\n        expect {\n          expect(described_class.parse_line('pkg (mypkg) 1.2.3.4 i-- zzz')).to\n        }.to raise_error(ArgumentError, /Unknown line format/)\n      end\n\n      {\n        'pkg://omnios/SUNWcs@0.5.11,5.11-0.151006:20130506T161045Z    i--' => {:name => 'SUNWcs', :ensure => '0.5.11,5.11-0.151006:20130506T161045Z', :status => 'installed', :provider => :pkg, :publisher => 'omnios'},\n        'pkg://omnios/incorporation/jeos/illumos-gate@11,5.11-0.151006:20130506T183443Z if-' => {:name => 'incorporation/jeos/illumos-gate', :ensure => \"11,5.11-0.151006:20130506T183443Z\", :mark => :hold, :status => 'installed', :provider => :pkg, :publisher => 'omnios'},\n        'pkg://solaris/SUNWcs@0.5.11,5.11-0.151.0.1:20101105T001108Z      installed  -----' => {:name => 'SUNWcs', :ensure => '0.5.11,5.11-0.151.0.1:20101105T001108Z', :status => 'installed', :provider => :pkg, :publisher => 'solaris'},\n       }.each do |k, v|\n        it \"[#{k}] should correctly parse\" do\n          expect(described_class.parse_line(k)).to eq(v)\n        end\n      end\n    end\n\n    context \":latest\" do\n      before do\n        expect(described_class).to receive(:pkg).with(:refresh)\n      end\n\n      it \"should work correctly for ensure latest on solaris 11 (UFOXI) when there are no further packages to install\" do\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.installed')))\n        expect(provider.latest).to eq('1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z')\n      end\n\n      it \"should work correctly for ensure latest on solaris 11 in the presence of a certificate expiration warning\" do\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.certificate_warning')))\n        expect(provider.latest).to eq(\"1.0.6-0.175.0.0.0.2.537\")\n      end\n\n      it \"should work correctly for ensure latest on solaris 11(known UFOXI)\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(['/bin/pkg', 'update', '-n', 'dummy'], {:failonfail => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.known')))\n        expect(provider.latest).to eq('1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z')\n      end\n\n      it \"should work correctly for ensure latest on solaris 11 (IFO)\" do\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.ifo.installed')))\n        expect(provider.latest).to eq('1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z')\n      end\n\n      it \"should work correctly for ensure latest on solaris 11(known IFO)\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(['/bin/pkg', 'update', '-n', 'dummy'], {:failonfail => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.ifo.known')))\n        expect(provider.latest).to eq('1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z')\n      end\n\n      it \"issues a warning when the certificate has expired\" do\n        warning = \"Certificate '/var/pkg/ssl/871b4ed0ade09926e6adf95f86bf17535f987684' for publisher 'solarisstudio', needed to access 'https://pkg.oracle.com/solarisstudio/release/', will expire in '29' days.\"\n        expect(Puppet).to receive(:warning).with(\"pkg warning: #{warning}\")\n\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.certificate_warning')))\n        provider.latest\n      end\n\n      it \"doesn't issue a warning when the certificate hasn't expired\" do\n        expect(Puppet).not_to receive(:warning).with(/pkg warning/)\n\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.installed')))\n        provider.latest\n      end\n\n      it \"applies install options if available\" do\n        resource[:install_options] = ['--foo', {'--bar' => 'baz'}]\n        expect(described_class).to receive(:pkg).with(:list,'-Hvn','dummy').and_return(File.read(my_fixture('dummy_solaris11.known')))\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(['/bin/pkg', 'update', '-n', '--foo', '--bar=baz', 'dummy'], {failonfail: false, combine: true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        provider.latest\n      end\n    end\n\n    context \":instances\" do\n      it \"should correctly parse lines on solaris 11\" do\n        expect(described_class).to receive(:pkg).with(:list, '-Hv').and_return(File.read(my_fixture('solaris11')))\n        expect(described_class).not_to receive(:warning)\n        instances = described_class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure) }}\n        expect(instances.size).to eq(2)\n        expect(instances[0]).to eq({:name => 'dummy/dummy', :ensure => '3.0,5.11-0.175.0.0.0.2.537:20131230T130000Z'})\n        expect(instances[1]).to eq({:name => 'dummy/dummy2', :ensure => '1.8.1.2-0.175.0.0.0.2.537:20131230T130000Z'})\n      end\n\n      it \"should fail on incorrect lines\" do\n        fake_output = File.read(my_fixture('incomplete'))\n        expect(described_class).to receive(:pkg).with(:list,'-Hv').and_return(fake_output)\n        expect {\n          described_class.instances\n        }.to raise_error(ArgumentError, /Unknown line format pkg/)\n      end\n\n      it \"should fail on unknown package status\" do\n        expect(described_class).to receive(:pkg).with(:list,'-Hv').and_return(File.read(my_fixture('unknown_status')))\n        expect {\n          described_class.instances\n        }.to raise_error(ArgumentError, /Unknown format pkg/)\n      end\n    end\n\n    context \":query\" do\n      context \"on solaris 10\" do\n        it \"should find the package\" do\n          expect(Puppet::Util::Execution).to receive(:execute)\n            .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n            .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_solaris10')), 0))\n          expect(provider.query).to eq({\n            :name      => 'dummy',\n            :ensure    => '2.5.5,5.10-0.111:20131230T130000Z',\n            :publisher => 'solaris',\n            :status    => 'installed',\n            :provider  => :pkg,\n          })\n        end\n\n        it \"should return :absent when the package is not found\" do\n          expect(Puppet::Util::Execution).to receive(:execute)\n            .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n            .and_return(Puppet::Util::Execution::ProcessOutput.new('', 1))\n          expect(provider.query).to eq({:ensure => :absent, :name => \"dummy\"})\n        end\n      end\n\n      context \"on solaris 11\" do\n        it \"should find the package\" do\n          expect(Puppet::Util::Execution).to receive(:execute)\n            .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n            .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_solaris11.installed')), 0))\n          expect(provider.query).to eq({\n            :name      => 'dummy',\n            :status    => 'installed',\n            :ensure    => '1.0.6,5.11-0.175.0.0.0.2.537:20131230T130000Z',\n            :publisher => 'solaris',\n            :provider  => :pkg,\n          })\n        end\n\n        it \"should return :absent when the package is not found\" do\n          expect(Puppet::Util::Execution).to receive(:execute)\n            .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n            .and_return(Puppet::Util::Execution::ProcessOutput.new('', 1))\n          expect(provider.query).to eq({:ensure => :absent, :name => \"dummy\"})\n        end\n      end\n\n      it \"should return fail when the packageline cannot be parsed\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('incomplete')), 0))\n        expect {\n          provider.query\n        }.to raise_error(ArgumentError, /Unknown line format/)\n      end\n    end\n\n    context \":install\" do\n      [\n        { :osrel => '11.0', :flags => ['--accept'] },\n        { :osrel => '11.2', :flags => ['--accept', '--sync-actuators-timeout', '900'] },\n      ].each do |hash|\n        context \"with 'os.release.full' #{hash[:osrel]}\" do\n          before :each do\n            allow(Facter).to receive(:value).with('os.release.full').and_return(hash[:osrel])\n          end\n\n          it \"should support install options\" do\n            resource[:install_options] = ['--foo', {'--bar' => 'baz'}]\n            expect(provider).to receive(:query).and_return({:ensure => :absent})\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(provider).to receive(:unhold)\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', *hash[:flags], '--foo', '--bar=baz', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"should accept all licenses\" do\n            expect(provider).to receive(:query).with(no_args).and_return({:ensure => :absent})\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', *hash[:flags], 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'unfreeze', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"should install specific version(1)\" do\n            # Should install also check if the version installed is the same version we are asked to install? or should we rely on puppet for that?\n            resource[:ensure] = '0.0.7,5.11-0.151006:20131230T130000Z'\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'unfreeze', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('pkg://foo/dummy@0.0.6,5.11-0.151006:20131230T130000Z  installed -----', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'update', *hash[:flags], 'dummy@0.0.7,5.11-0.151006:20131230T130000Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"should install specific version(2)\" do\n            resource[:ensure] = '0.0.8'\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'unfreeze', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('pkg://foo/dummy@0.0.7,5.11-0.151006:20131230T130000Z  installed -----', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'update', *hash[:flags], 'dummy@0.0.8'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"should downgrade to specific version\" do\n            resource[:ensure] = '0.0.7'\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(provider).to receive(:query).with(no_args).and_return({:ensure => '0.0.8,5.11-0.151106:20131230T130000Z'})\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'unfreeze', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'update', *hash[:flags], 'dummy@0.0.7'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"should install any if version is not specified\" do\n            resource[:ensure] = :present\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(provider).to receive(:query).with(no_args).and_return({:ensure => :absent})\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', *hash[:flags], 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'unfreeze', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"should install if no version was previously installed, and a specific version was requested\" do\n            resource[:ensure] = '0.0.7'\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(provider).to receive(:query).with(no_args).and_return({:ensure => :absent})\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'unfreeze', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', *hash[:flags], 'dummy@0.0.7'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.install\n          end\n\n          it \"installs the latest matching version when given implicit version, and none are installed\" do\n            resource[:ensure] = '1.0-0.151006'\n            is = :absent\n            expect(provider).to receive(:query).with(no_args).and_return({:ensure => is})\n            expect(provider).to receive(:properties).and_return({:mark => :hold}).exactly(3).times\n\n            expect(described_class).to receive(:pkg)\n              .with(:freeze, 'dummy')\n            expect(described_class).to receive(:pkg)\n              .with(:list, '-Hvfa', 'dummy@1.0-0.151006')\n              .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(provider).to receive(:unhold).with(no_args).twice\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', *hash[:flags], 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.insync?(is)\n            provider.install\n          end\n\n          it \"updates to the latest matching version when given implicit version\" do\n            resource[:ensure] = '1.0-0.151006'\n            is = '1.0,5.11-0.151006:20140219T191204Z'\n            expect(provider).to receive(:query).with(no_args).and_return({:ensure => is})\n            expect(provider).to receive(:properties).and_return({:mark => :hold}).exactly(3).times\n\n            expect(described_class).to receive(:pkg)\n              .with(:freeze, 'dummy')\n            expect(described_class).to receive(:pkg)\n              .with(:list, '-Hvfa', 'dummy@1.0-0.151006')\n              .and_return(File.read(my_fixture('dummy_implicit_version')))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'update', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            expect(provider).to receive(:unhold).with(no_args).twice\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'update', *hash[:flags], 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.insync?(is)\n            provider.install\n          end\n\n          it \"issues a warning when an implicit version number is used, and in sync\" do\n            resource[:ensure] = '1.0-0.151006'\n            is = '1.0,5.11-0.151006:20140220T084443Z'\n            expect(provider).to receive(:warning).with(\"Implicit version 1.0-0.151006 has 3 possible matches\")\n            expect(described_class).to receive(:pkg)\n              .with(:list, '-Hvfa', 'dummy@1.0-0.151006')\n              .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'update', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 4))\n            provider.insync?(is)\n          end\n\n          it \"issues a warning when choosing a version number for an implicit match\" do\n            resource[:ensure] = '1.0-0.151006'\n            is = :absent\n            expect(provider).to receive(:warning).with(\"Implicit version 1.0-0.151006 has 3 possible matches\")\n            expect(provider).to receive(:warning).with(\"Selecting version '1.0,5.11-0.151006:20140220T084443Z' for implicit '1.0-0.151006'\")\n            expect(described_class).to receive(:pkg)\n              .with(:list, '-Hvfa', 'dummy@1.0-0.151006')\n              .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n            provider.insync?(is)\n          end\n\n          it \"should try 5 times to install and fail when all tries failed\" do\n            allow_any_instance_of(Kernel).to receive(:sleep)\n\n            expect(provider).to receive(:query).and_return({:ensure => :absent})\n            expect(provider).to receive(:properties).and_return({:mark => :hold})\n            expect(provider).to receive(:unhold)\n            expect(Puppet::Util::Execution).to receive(:execute)\n              .with(['/bin/pkg', 'install', *hash[:flags], 'dummy'], {:failonfail => false, :combine => true})\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', 7))\n              .exactly(5).times\n            expect {\n              provider.update\n            }.to raise_error(Puppet::Error, /Pkg could not install dummy after 5 tries. Aborting run/)\n          end\n        end\n      end\n    end\n\n    context \":update\" do\n      it \"should not raise error if not necessary\" do\n        expect(provider).to receive(:install).with(true).and_return({:exit => 0})\n        provider.update\n      end\n\n      it \"should not raise error if not necessary (2)\" do\n        expect(provider).to receive(:install).with(true).and_return({:exit => 4})\n        provider.update\n      end\n\n      it \"should raise error if necessary\" do\n        expect(provider).to receive(:install).with(true).and_return({:exit => 1})\n        expect {\n          provider.update\n        }.to raise_error(Puppet::Error, /Unable to update/)\n      end\n    end\n\n    context \":uninstall\" do\n      it \"should support current pkg version\" do\n        expect(described_class).to receive(:pkg).with(:version).and_return('630e1ffc7a19')\n        expect(described_class).to receive(:pkg).with([:uninstall, resource[:name]])\n        expect(provider).to receive(:properties).and_return({:hold => false})\n\n        provider.uninstall\n      end\n\n      it \"should support original pkg commands\" do\n        expect(described_class).to receive(:pkg).with(:version).and_return('052adf36c3f4')\n        expect(described_class).to receive(:pkg).with([:uninstall, '-r', resource[:name]])\n        expect(provider).to receive(:properties).and_return({:hold => false})\n\n        provider.uninstall\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pkgdmg_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:pkgdmg) do\n  let(:resource) { Puppet::Type.type(:package).new(:name => 'foo', :provider => :pkgdmg) }\n  let(:provider) { described_class.new(resource) }\n\n  it { is_expected.not_to be_versionable }\n  it { is_expected.not_to be_uninstallable }\n\n  describe \"when installing it should fail when\" do\n    it \"no source is specified\" do\n      expect { provider.install }.to raise_error(Puppet::Error, /must specify a package source/)\n    end\n\n    it \"the source does not end in .dmg or .pkg\" do\n      resource[:source] = \"bar\"\n      expect { provider.install }.to raise_error(Puppet::Error, /must specify a source string ending in .*dmg.*pkg/)\n    end\n  end\n\n  # These tests shouldn't be this messy. The pkgdmg provider needs work...\n  describe \"when installing a pkgdmg\" do\n    let(:fake_mountpoint) { \"/tmp/dmg.foo\" }\n    let(:fake_hdiutil_plist) { {\"system-entities\" => [{\"mount-point\" => fake_mountpoint}]} }\n\n    before do\n      fh = double('filehandle')\n      allow(fh).to receive(:path).and_return(\"/tmp/foo\")\n      resource[:source] = \"foo.dmg\"\n      allow(File).to receive(:open).and_yield(fh)\n      allow(Dir).to receive(:mktmpdir).and_return(\"/tmp/testtmp123\")\n      allow(FileUtils).to receive(:remove_entry_secure)\n    end\n\n    it \"should fail when a disk image with no system entities is mounted\" do\n      allow(described_class).to receive(:hdiutil).and_return('empty plist')\n      expect(Puppet::Util::Plist).to receive(:parse_plist).with('empty plist').and_return({})\n      expect { provider.install }.to raise_error(Puppet::Error, /No disk entities/)\n    end\n\n    it \"should call hdiutil to mount and eject the disk image\" do\n      allow(Dir).to receive(:entries).and_return([])\n      expect(provider.class).to receive(:hdiutil).with(\"eject\", fake_mountpoint).and_return(0)\n      expect(provider.class).to receive(:hdiutil).with(\"mount\", \"-plist\", \"-nobrowse\", \"-readonly\", \"-mountrandom\", \"/tmp\", '/tmp/foo').and_return('a plist')\n      expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n      provider.install\n    end\n\n    it \"should call installpkg if a pkg/mpkg is found on the dmg\" do\n      allow(Dir).to receive(:entries).and_return([\"foo.pkg\"])\n      allow(provider.class).to receive(:hdiutil).and_return('a plist')\n      expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n      expect(provider.class).to receive(:installpkg).with(\"#{fake_mountpoint}/foo.pkg\", resource[:name], \"foo.dmg\").and_return(\"\")\n      provider.install\n    end\n\n    describe \"from a remote source\" do\n      let(:tmpdir) { \"/tmp/good123\" }\n\n      before :each do\n        resource[:source] = \"http://fake.puppetlabs.com/foo.dmg\"\n      end\n\n      it \"should call tmpdir and then call curl with that directory\" do\n        expect(Dir).to receive(:mktmpdir).and_return(tmpdir)\n        allow(Dir).to receive(:entries).and_return([\"foo.pkg\"])\n        expect(described_class).to receive(:curl) do |*args|\n          expect(args[0]).to eq(\"-o\")\n          expect(args[1]).to include(tmpdir)\n          expect(args).to include(\"--fail\")\n          expect(args).not_to include(\"-k\")\n        end\n        allow(described_class).to receive(:hdiutil).and_return('a plist')\n        expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n        expect(described_class).to receive(:installpkg)\n\n        provider.install\n      end\n\n      it \"should use an http proxy host and port if specified\" do\n        expect(Puppet::Util::HttpProxy).to receive(:no_proxy?).and_return(false)\n        expect(Puppet::Util::HttpProxy).to receive(:http_proxy_host).and_return('some_host')\n        expect(Puppet::Util::HttpProxy).to receive(:http_proxy_port).and_return('some_port')\n        expect(Dir).to receive(:mktmpdir).and_return(tmpdir)\n        allow(Dir).to receive(:entries).and_return([\"foo.pkg\"])\n        expect(described_class).to receive(:curl) do |*args|\n          expect(args).to include('some_host:some_port')\n          expect(args).to include('--proxy')\n        end\n        allow(described_class).to receive(:hdiutil).and_return('a plist')\n        expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n        expect(described_class).to receive(:installpkg)\n\n        provider.install\n      end\n\n      it \"should use an http proxy host only if specified\" do\n        expect(Puppet::Util::HttpProxy).to receive(:no_proxy?).and_return(false)\n        expect(Puppet::Util::HttpProxy).to receive(:http_proxy_host).and_return('some_host')\n        expect(Puppet::Util::HttpProxy).to receive(:http_proxy_port).and_return(nil)\n        expect(Dir).to receive(:mktmpdir).and_return(tmpdir)\n        allow(Dir).to receive(:entries).and_return([\"foo.pkg\"])\n        expect(described_class).to receive(:curl) do |*args|\n          expect(args).to include('some_host')\n          expect(args).to include('--proxy')\n        end\n        allow(described_class).to receive(:hdiutil).and_return('a plist')\n        expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n        expect(described_class).to receive(:installpkg)\n\n        provider.install\n      end\n\n      it \"should not use the configured proxy if no_proxy contains a match for the destination\" do\n        expect(Puppet::Util::HttpProxy).to receive(:no_proxy?).and_return(true)\n        expect(Puppet::Util::HttpProxy).not_to receive(:http_proxy_host)\n        expect(Puppet::Util::HttpProxy).not_to receive(:http_proxy_port)\n        expect(Dir).to receive(:mktmpdir).and_return(tmpdir)\n        allow(Dir).to receive(:entries).and_return([\"foo.pkg\"])\n        expect(described_class).to receive(:curl) do |*args|\n          expect(args).not_to include('some_host:some_port')\n          expect(args).not_to include('--proxy')\n          true\n        end\n        allow(described_class).to receive(:hdiutil).and_return('a plist')\n        expect(Puppet::Util::Plist).to receive(:parse_plist).with('a plist').and_return(fake_hdiutil_plist)\n        expect(described_class).to receive(:installpkg)\n\n        provider.install\n      end\n    end\n  end\n\n  describe \"when installing flat pkg file\" do\n    describe \"with a local source\" do\n      it \"should call installpkg if a flat pkg file is found instead of a .dmg image\" do\n        resource[:source] = \"/tmp/test.pkg\"\n        resource[:name] = \"testpkg\"\n        expect(provider.class).to receive(:installpkgdmg).with(\"/tmp/test.pkg\", \"testpkg\").and_return(\"\")\n        provider.install\n      end\n    end\n\n    describe \"with a remote source\" do\n      let(:remote_source) { 'http://fake.puppetlabs.com/test.pkg' }\n      let(:tmpdir) { '/path/to/tmpdir' }\n      let(:tmpfile) { File.join(tmpdir, 'testpkg.pkg') }\n\n      before do\n        resource[:name]   = 'testpkg'\n        resource[:source] = remote_source\n\n        allow(Dir).to receive(:mktmpdir).and_return(tmpdir)\n      end\n\n      it \"should call installpkg if a flat pkg file is found instead of a .dmg image\" do\n        expect(described_class).to receive(:curl) do |*args|\n          expect(args).to include(tmpfile)\n          expect(args).to include(remote_source)\n        end\n        expect(provider.class).to receive(:installpkg).with(tmpfile, 'testpkg', remote_source)\n        provider.install\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pkgin_spec.rb",
    "content": "require \"spec_helper\"\n\ndescribe Puppet::Type.type(:package).provider(:pkgin) do\n  let(:resource) { Puppet::Type.type(:package).new(:name => \"vim\", :provider => :pkgin) }\n  subject        { resource.provider }\n\n  describe \"Puppet provider interface\" do\n    it \"can return the list of all packages\" do\n      expect(described_class).to respond_to(:instances)\n    end\n  end\n\n  describe \"#install\" do\n   describe \"a package not installed\" do\n    before { resource[:ensure] = :absent }\n\n    it \"uses pkgin install to install\" do\n      expect(subject).to receive(:pkgin).with(\"-y\", :install, \"vim\").once()\n      subject.install\n    end\n   end\n\n   describe \"a package with a fixed version\" do\n    before { resource[:ensure] = '7.2.446' }\n\n    it \"uses pkgin install to install a fixed version\" do\n      expect(subject).to receive(:pkgin).with(\"-y\", :install, \"vim-7.2.446\").once()\n      subject.install\n    end\n   end\n  end\n\n  describe \"#uninstall\" do\n    it \"uses pkgin remove to uninstall\" do\n      expect(subject).to receive(:pkgin).with(\"-y\", :remove, \"vim\").once()\n      subject.uninstall\n    end\n  end\n\n  describe \"#instances\" do\n    let(:pkgin_ls_output) do\n      \"zlib-1.2.3;General purpose data compression library\\nzziplib-0.13.59;Library for ZIP archive handling\\n\"\n    end\n\n    before do\n      allow(described_class).to receive(:pkgin).with(:list).and_return(pkgin_ls_output)\n    end\n\n    it \"returns an array of providers for each package\" do\n      instances = described_class.instances\n      expect(instances.count).to eq 2\n      instances.each do |instance|\n        expect(instance).to be_a(described_class)\n      end\n    end\n\n    it \"populates each provider with an installed package\" do\n      zlib_provider, zziplib_provider = described_class.instances\n      expect(zlib_provider.get(:name)).to eq(\"zlib\")\n      expect(zlib_provider.get(:ensure)).to eq(\"1.2.3\")\n      expect(zziplib_provider.get(:name)).to eq(\"zziplib\")\n      expect(zziplib_provider.get(:ensure)).to eq(\"0.13.59\")\n    end\n  end\n\n  describe \"#latest\" do\n    before do\n      allow(described_class).to receive(:pkgin).with(:search, \"vim\").and_return(pkgin_search_output)\n    end\n\n    context \"when the package is installed\" do\n      let(:pkgin_search_output) do\n        \"vim-7.2.446;=;Vim editor (vi clone) without GUI\\nvim-share-7.2.446;=;Data files for the vim editor (vi clone)\\n\\n=: package is installed and up-to-date\\n<: package is installed but newer version is available\\n>: installed package has a greater version than available package\\n\"\n      end\n\n      it \"returns installed version\" do\n        expect(subject).to receive(:properties).and_return({ :ensure => \"7.2.446\" })\n        expect(subject.latest).to eq(\"7.2.446\")\n      end\n    end\n\n    context \"when the package is out of date\" do\n      let(:pkgin_search_output) do\n        \"vim-7.2.447;<;Vim editor (vi clone) without GUI\\nvim-share-7.2.447;<;Data files for the vim editor (vi clone)\\n\\n=: package is installed and up-to-date\\n<: package is installed but newer version is available\\n>: installed package has a greater version than available package\\n\"\n      end\n\n      it \"returns the version to be installed\" do\n        expect(subject.latest).to eq(\"7.2.447\")\n      end\n    end\n\n    context \"when the package is ahead of date\" do\n      let(:pkgin_search_output) do\n        \"vim-7.2.446;>;Vim editor (vi clone) without GUI\\nvim-share-7.2.446;>;Data files for the vim editor (vi clone)\\n\\n=: package is installed and up-to-date\\n<: package is installed but newer version is available\\n>: installed package has a greater version than available package\\n\"\n      end\n\n      it \"returns current version\" do\n        expect(subject).to receive(:properties).and_return({ :ensure => \"7.2.446\" })\n        expect(subject.latest).to eq(\"7.2.446\")\n      end\n    end\n\n    context \"when multiple candidates do exists\" do\n      let(:pkgin_search_output) do\n        <<-SEARCH\nvim-7.1;>;Vim editor (vi clone) without GUI\nvim-share-7.1;>;Data files for the vim editor (vi clone)\nvim-7.2.446;=;Vim editor (vi clone) without GUI\nvim-share-7.2.446;=;Data files for the vim editor (vi clone)\nvim-7.3;<;Vim editor (vi clone) without GUI\nvim-share-7.3;<;Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\nSEARCH\n      end\n\n      it \"returns the newest available version\" do\n        allow(described_class).to receive(:pkgin).with(:search, \"vim\").and_return(pkgin_search_output)\n        expect(subject.latest).to eq(\"7.3\")\n      end\n    end\n\n    context \"when the package cannot be found\" do\n      let(:pkgin_search_output) do\n        \"No results found for is-puppet\"\n      end\n\n      it \"returns nil\" do\n        expect { subject.latest }.to raise_error(Puppet::Error, \"No candidate to be installed\")\n      end\n    end\n  end\n\n  describe \"#parse_pkgin_line\" do\n    context \"with an installed package\" do\n      let(:package) { \"vim-7.2.446;=;Vim editor (vi clone) without GUI\" }\n\n      it \"extracts the name and status\" do\n        expect(described_class.parse_pkgin_line(package)).to eq({ :name => \"vim\" ,\n                                                             :status => \"=\" ,\n                                                             :ensure => \"7.2.446\" })\n      end\n    end\n\n    context \"with an installed package with a hyphen in the name\" do\n      let(:package) { \"ruby18-puppet-0.25.5nb1;>;Configuration management framework written in Ruby\" }\n\n      it \"extracts the name and status\" do\n        expect(described_class.parse_pkgin_line(package)).to eq({ :name =>  \"ruby18-puppet\",\n                                                             :status => \">\" ,\n                                                             :ensure => \"0.25.5nb1\" })\n      end\n    end\n\n    context \"with an installed package with a hyphen in the name and package description\" do\n      let(:package) { \"ruby200-facter-2.4.3nb1;=;Cross-platform Ruby library for retrieving facts from OS\" }\n\n      it \"extracts the name and status\" do\n        expect(described_class.parse_pkgin_line(package)).to eq({ :name =>  \"ruby200-facter\",\n                                                             :status => \"=\" ,\n                                                             :ensure => \"2.4.3nb1\" })\n      end\n    end\n\n    context \"with a package not yet installed\" do\n      let(:package) { \"vim-7.2.446;Vim editor (vi clone) without GUI\" }\n\n      it \"extracts the name and status\" do\n        expect(described_class.parse_pkgin_line(package)).to eq({ :name => \"vim\" ,\n                                                             :status => nil ,\n                                                             :ensure => \"7.2.446\" })\n      end\n    end\n\n    context \"with an invalid package\" do\n      let(:package) { \"\" }\n\n      it \"returns nil\" do\n        expect(described_class.parse_pkgin_line(package)).to be_nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pkgng_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/package/pkgng'\n\ndescribe Puppet::Type.type(:package).provider(:pkgng) do\n  let(:name) { 'bash' }\n  let(:installed_name) { 'zsh' }\n  let(:pkgng) { 'pkgng' }\n\n  let(:resource) do\n    # When bash is not present\n    Puppet::Type.type(:package).new(:name => name, :provider => pkgng)\n  end\n\n  let(:installed_resource) do\n    # When zsh is present\n    Puppet::Type.type(:package).new(:name => installed_name, :provider => pkgng)\n  end\n\n  let(:latest_resource) do\n    # When curl is installed but not the latest\n    Puppet::Type.type(:package).new(:name => 'ftp/curl', :provider => pkgng, :ensure => latest)\n  end\n\n  let (:provider) { resource.provider }\n  let (:installed_provider) { installed_resource.provider }\n\n  def run_in_catalog(*resources)\n    catalog = Puppet::Resource::Catalog.new\n    catalog.host_config = false\n    resources.each do |resource|\n      catalog.add_resource(resource)\n    end\n    catalog.apply\n  end\n\n  before do\n    allow(described_class).to receive(:command).with(:pkg).and_return('/usr/local/sbin/pkg')\n\n    info = File.read(my_fixture('pkg.query'))\n    allow(described_class).to receive(:get_query).and_return(info)\n\n    version_list = File.read(my_fixture('pkg.version'))\n    allow(described_class).to receive(:get_version_list).and_return(version_list)\n  end\n\n  context \"#instances\" do\n    it \"should return the empty set if no packages are listed\" do\n      allow(described_class).to receive(:get_query).and_return('')\n      allow(described_class).to receive(:get_version_list).and_return('')\n      expect(described_class.instances).to be_empty\n    end\n\n    it \"should return all packages when invoked\" do\n      expect(described_class.instances.map(&:name).sort).to eq(\n        %w{ca_root_nss curl nmap pkg gnupg zsh tac_plus}.sort)\n    end\n\n    it \"should set latest to current version when no upgrade available\" do\n      nmap = described_class.instances.find {|i| i.properties[:origin] == 'security/nmap' }\n\n      expect(nmap.properties[:version]).to eq(nmap.properties[:latest])\n    end\n\n    it \"should return an empty array when pkg calls raise an exception\" do\n      allow(described_class).to receive(:get_query).and_raise(Puppet::ExecutionFailure, 'An error occurred.')\n      expect(described_class.instances).to eq([])\n    end\n\n    describe \"version\" do\n      it \"should retrieve the correct version of the current package\" do\n        zsh = described_class.instances.find {|i| i.properties[:origin] == 'shells/zsh' }\n        expect(zsh.properties[:version]).to eq('5.0.2_1')\n      end\n    end\n  end\n\n  context \"#install\" do\n    it \"should call pkg with the specified package version given an origin for package name\" do\n      resource = Puppet::Type.type(:package).new(\n        :name     => 'ftp/curl',\n        :provider => :pkgng,\n        :ensure   => '7.33.1'\n      )\n      expect(resource.provider).to receive(:pkg) do |arg|\n        expect(arg).to include('curl-7.33.1')\n      end\n      resource.provider.install\n    end\n\n    it \"should call pkg with the specified package version\" do\n      resource = Puppet::Type.type(:package).new(\n        :name     => 'curl',\n        :provider => :pkgng,\n        :ensure   => '7.33.1'\n      )\n      expect(resource.provider).to receive(:pkg) do |arg|\n        expect(arg).to include('curl-7.33.1')\n      end\n      resource.provider.install\n    end\n\n    it \"should call pkg with the specified package repo\" do\n      resource = Puppet::Type.type(:package).new(\n        :name     => 'curl',\n        :provider => :pkgng,\n        :source   => 'urn:freebsd:repo:FreeBSD'\n      )\n      expect(resource.provider).to receive(:pkg) do |arg|\n        expect(arg).to include('FreeBSD')\n      end\n      resource.provider.install\n    end\n\n    it \"should call pkg with the specified install options string\" do\n      resource = Puppet::Type.type(:package).new(\n        :name            => 'curl',\n        :provider        => :pkgng,\n        :install_options => ['--foo', '--bar']\n      )\n      expect(resource.provider).to receive(:pkg) do |arg|\n        expect(arg).to include('--foo', '--bar')\n      end\n      resource.provider.install\n    end\n\n    it \"should call pkg with the specified install options hash\" do\n      resource = Puppet::Type.type(:package).new(\n        :name            => 'curl',\n        :provider        => :pkgng,\n        :install_options => ['--foo', { '--bar' => 'baz', '--baz' => 'foo' }]\n      )\n      expect(resource.provider).to receive(:pkg) do |arg|\n        expect(arg).to include('--foo', '--bar=baz', '--baz=foo')\n      end\n      resource.provider.install\n    end\n  end\n\n  context \"#prefetch\" do\n    it \"should fail gracefully when \" do\n      allow(described_class).to receive(:instances).and_return([])\n      expect{ described_class.prefetch({}) }.to_not raise_error\n    end\n  end\n\n  context \"#query\" do\n    it \"should return the installed version if present\" do\n      pkg_query_zsh = File.read(my_fixture('pkg.query.zsh'))\n      allow(described_class).to receive(:get_resource_info).with('zsh').and_return(pkg_query_zsh)\n      described_class.prefetch({installed_name => installed_resource})\n      expect(installed_provider.query).to be >= {:version=>'5.0.2_1'}\n    end\n\n    it \"should return nil if not present\" do\n      allow(described_class).to receive(:get_resource_info).with('bash').and_raise(Puppet::ExecutionFailure, 'An error occurred')\n\n      expect(provider.query).to equal(nil)\n    end\n  end\n\n  describe \"latest\" do\n    it \"should retrieve the correct version of the latest package\" do\n      described_class.prefetch( { installed_name => installed_resource })\n      expect(installed_provider.latest).not_to be_nil\n    end\n\n    it \"should set latest to newer package version when available\" do\n      instances = described_class.instances\n      curl = instances.find {|i| i.properties[:origin] == 'ftp/curl' }\n      expect(curl.properties[:latest]).to eq('7.33.0_2')\n    end\n\n    it \"should call update to upgrade the version\" do\n      allow(described_class).to receive(:get_resource_info).with('ftp/curl').and_return('curl 7.61.1 ftp/curl')\n\n      resource = Puppet::Type.type(:package).new(\n        :name     => 'ftp/curl',\n        :provider => pkgng,\n        :ensure   => :latest\n      )\n\n      expect(resource.provider).to receive(:update)\n\n      resource.property(:ensure).sync\n    end\n  end\n\n  describe \"get_latest_version\" do\n    it \"should rereturn nil when the current package is the latest\" do\n      version_list = File.read(my_fixture('pkg.version'))\n      allow(described_class).to receive(:get_version_list).and_return(version_list)\n      nmap_latest_version = described_class.get_latest_version('security/nmap')\n\n      expect(nmap_latest_version).to be_nil\n    end\n\n    it \"should match the package name exactly\" do\n      version_list = File.read(my_fixture('pkg.version'))\n      allow(described_class).to receive(:get_version_list).and_return(version_list)\n      bash_comp_latest_version = described_class.get_latest_version('shells/bash-completion')\n\n      expect(bash_comp_latest_version).to eq('2.1_3')\n    end\n\n    it \"should return nil when the package is orphaned\" do\n      version_list = File.read(my_fixture('pkg.version'))\n      allow(described_class).to receive(:get_version_list).and_return(version_list)\n      orphan_latest_version = described_class.get_latest_version('sysutils/orphan')\n      expect(orphan_latest_version).to be_nil\n    end\n\n    it \"should return nil when the package is broken\" do\n      version_list = File.read(my_fixture('pkg.version'))\n      allow(described_class).to receive(:get_version_list).and_return(version_list)\n      broken_latest_version = described_class.get_latest_version('sysutils/broken')\n      expect(broken_latest_version).to be_nil\n    end\n  end\n\n  describe \"confine\" do\n    context \"on FreeBSD\" do\n      it \"should be the default provider\" do\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(:freebsd)\n        expect(described_class).to be_default\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/pkgutil_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:pkgutil) do\n  before(:each) do\n    @resource = Puppet::Type.type(:package).new(\n      :name     => \"TESTpkg\",\n      :ensure   => :present,\n      :provider => :pkgutil\n    )\n    @provider = described_class.new(@resource)\n\n    # Stub all file and config tests\n    allow(described_class).to receive(:healthcheck)\n  end\n\n  it \"should have an install method\" do\n    expect(@provider).to respond_to(:install)\n  end\n\n  it \"should have a latest method\" do\n    expect(@provider).to respond_to(:uninstall)\n  end\n\n  it \"should have an update method\" do\n    expect(@provider).to respond_to(:update)\n  end\n\n  it \"should have a latest method\" do\n    expect(@provider).to respond_to(:latest)\n  end\n\n  describe \"when installing\" do\n    it \"should use a command without versioned package\" do\n      @resource[:ensure] = :latest\n      expect(@provider).to receive(:pkguti).with('-y', '-i', 'TESTpkg')\n      @provider.install\n    end\n\n    it \"should support a single temp repo URL\" do\n      @resource[:ensure] = :latest\n      @resource[:source] = \"http://example.net/repo\"\n      expect(@provider).to receive(:pkguti).with('-t', 'http://example.net/repo', '-y', '-i', 'TESTpkg')\n      @provider.install\n    end\n\n    it \"should support multiple temp repo URLs as array\" do\n      @resource[:ensure] = :latest\n      @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ]\n      expect(@provider).to receive(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-i', 'TESTpkg')\n      @provider.install\n    end\n  end\n\n  describe \"when updating\" do\n    it \"should use a command without versioned package\" do\n      expect(@provider).to receive(:pkguti).with('-y', '-u', 'TESTpkg')\n      @provider.update\n    end\n\n    it \"should support a single temp repo URL\" do\n      @resource[:source] = \"http://example.net/repo\"\n      expect(@provider).to receive(:pkguti).with('-t', 'http://example.net/repo', '-y', '-u', 'TESTpkg')\n      @provider.update\n    end\n\n    it \"should support multiple temp repo URLs as array\" do\n      @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ]\n      expect(@provider).to receive(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-u', 'TESTpkg')\n      @provider.update\n    end\n  end\n\n  describe \"when uninstalling\" do\n    it \"should call the remove operation\" do\n      expect(@provider).to receive(:pkguti).with('-y', '-r', 'TESTpkg')\n      @provider.uninstall\n    end\n\n    it \"should support a single temp repo URL\" do\n      @resource[:source] = \"http://example.net/repo\"\n      expect(@provider).to receive(:pkguti).with('-t', 'http://example.net/repo', '-y', '-r', 'TESTpkg')\n      @provider.uninstall\n    end\n\n    it \"should support multiple temp repo URLs as array\" do\n      @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ]\n      expect(@provider).to receive(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-r', 'TESTpkg')\n      @provider.uninstall\n    end\n  end\n\n  describe \"when getting latest version\" do\n    it \"should return TESTpkg's version string\" do\n      fake_data = \"\nnoisy output here\nTESTpkg                   1.4.5,REV=2007.11.18      1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.latest).to eq(\"1.4.5,REV=2007.11.20\")\n    end\n\n    it \"should support a temp repo URL\" do\n      @resource[:source] = \"http://example.net/repo\"\n      fake_data = \"\nnoisy output here\nTESTpkg                   1.4.5,REV=2007.11.18      1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-t', 'http://example.net/repo', '-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.latest).to eq(\"1.4.5,REV=2007.11.20\")\n    end\n\n    it \"should handle TESTpkg's 'SAME' version string\" do\n      fake_data = \"\nnoisy output here\nTESTpkg                   1.4.5,REV=2007.11.18      SAME\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.latest).to eq(\"1.4.5,REV=2007.11.18\")\n    end\n\n    it \"should handle a non-existent package\" do\n      fake_data = \"noisy output here\nNot in catalog\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.latest).to eq(nil)\n    end\n\n    it \"should warn on unknown pkgutil noise\" do\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(\"testingnoise\")\n      expect(@provider.latest).to eq(nil)\n    end\n\n    it \"should ignore pkgutil noise/headers to find TESTpkg\" do\n      fake_data = \"# stuff\n=> Fetching new catalog and descriptions (http://mirror.opencsw.org/opencsw/unstable/i386/5.11) if available ...\n2011-02-19 23:05:46 URL:http://mirror.opencsw.org/opencsw/unstable/i386/5.11/catalog [534635/534635] -> \\\"/var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11.tmp\\\" [1]\nChecking integrity of /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 with gpg.\ngpg: Signature made February 17, 2011 05:27:53 PM GMT using DSA key ID E12E9D2F\ngpg: Good signature from \\\"Distribution Manager <dm@blastwave.org>\\\"\n==> 2770 packages loaded from /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11\npackage                   installed                 catalog\nTESTpkg                   1.4.5,REV=2007.11.18      1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.latest).to eq(\"1.4.5,REV=2007.11.20\")\n    end\n\n    it \"should find REALpkg via an alias (TESTpkg)\" do\n      fake_data = \"\nnoisy output here\nREALpkg                   1.4.5,REV=2007.11.18      1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.query[:name]).to eq(\"TESTpkg\")\n    end\n  end\n\n  describe \"when querying current version\" do\n    it \"should return TESTpkg's version string\" do\n      fake_data = \"TESTpkg  1.4.5,REV=2007.11.18  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.query[:ensure]).to eq(\"1.4.5,REV=2007.11.18\")\n    end\n\n    it \"should handle a package that isn't installed\" do\n      fake_data = \"TESTpkg  notinst  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.query[:ensure]).to eq(:absent)\n    end\n\n    it \"should handle a non-existent package\" do\n      fake_data = \"noisy output here\nNot in catalog\"\n      expect(described_class).to receive(:pkguti).with('-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.query[:ensure]).to eq(:absent)\n    end\n\n    it \"should support a temp repo URL\" do\n      @resource[:source] = \"http://example.net/repo\"\n      fake_data = \"TESTpkg  1.4.5,REV=2007.11.18  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with('-t', 'http://example.net/repo', '-c', '--single', 'TESTpkg').and_return(fake_data)\n      expect(@provider.query[:ensure]).to eq(\"1.4.5,REV=2007.11.18\")\n    end\n  end\n\n  describe \"when querying current instances\" do\n    it \"should warn on unknown pkgutil noise\" do\n      expect(described_class).to receive(:pkguti).with(['-a']).and_return(\"testingnoise\")\n      expect(described_class).to receive(:pkguti).with(['-c']).and_return(\"testingnoise\")\n      expect(Puppet).to receive(:warning).twice\n      expect(described_class).not_to receive(:new)\n      expect(described_class.instances).to eq([])\n    end\n\n    it \"should return TESTpkg's version string\" do\n      fake_data = \"TESTpkg  TESTpkg  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with(['-a']).and_return(fake_data)\n\n      fake_data = \"TESTpkg  1.4.5,REV=2007.11.18  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with(['-c']).and_return(fake_data)\n\n      testpkg = double('pkg1')\n      expect(described_class).to receive(:new).with({:ensure => \"1.4.5,REV=2007.11.18\", :name => \"TESTpkg\", :provider => :pkgutil}).and_return(testpkg)\n      expect(described_class.instances).to eq([testpkg])\n    end\n\n    it \"should also return both TESTpkg and mypkg alias instances\" do\n      fake_data = \"mypkg  TESTpkg  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with(['-a']).and_return(fake_data)\n\n      fake_data = \"TESTpkg  1.4.5,REV=2007.11.18  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with(['-c']).and_return(fake_data)\n\n      testpkg = double('pkg1')\n      expect(described_class).to receive(:new).with({:ensure => \"1.4.5,REV=2007.11.18\", :name => \"TESTpkg\", :provider => :pkgutil}).and_return(testpkg)\n\n      aliaspkg = double('pkg2')\n      expect(described_class).to receive(:new).with({:ensure => \"1.4.5,REV=2007.11.18\", :name => \"mypkg\", :provider => :pkgutil}).and_return(aliaspkg)\n\n      expect(described_class.instances).to eq([testpkg,aliaspkg])\n    end\n\n    it \"shouldn't mind noise in the -a output\" do\n      fake_data = \"noisy output here\"\n      expect(described_class).to receive(:pkguti).with(['-a']).and_return(fake_data)\n\n      fake_data = \"TESTpkg  1.4.5,REV=2007.11.18  1.4.5,REV=2007.11.20\"\n      expect(described_class).to receive(:pkguti).with(['-c']).and_return(fake_data)\n\n      testpkg = double('pkg1')\n      expect(described_class).to receive(:new).with({:ensure => \"1.4.5,REV=2007.11.18\", :name => \"TESTpkg\", :provider => :pkgutil}).and_return(testpkg)\n\n      expect(described_class.instances).to eq([testpkg])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/portage_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:portage) do\n  before do\n    packagename = \"sl\"\n    @resource = double('resource', :should => true)\n    allow(@resource).to receive(:[]).with(:name).and_return(packagename)\n    allow(@resource).to receive(:[]).with(:install_options).and_return(['--foo', '--bar'])\n    allow(@resource).to receive(:[]).with(:uninstall_options).and_return(['--foo', { '--bar' => 'baz', '--baz' => 'foo' }])\n\n    unslotted_packagename = \"dev-lang/ruby\"\n    @unslotted_resource = double('resource', :should => true)\n    allow(@unslotted_resource).to receive(:should).with(:ensure).and_return(:latest)\n    allow(@unslotted_resource).to receive(:[]).with(:name).and_return(unslotted_packagename)\n    allow(@unslotted_resource).to receive(:[]).with(:install_options).and_return([])\n\n    slotted_packagename = \"dev-lang/ruby:2.1\"\n    @slotted_resource = double('resource', :should => true)\n    allow(@slotted_resource).to receive(:[]).with(:name).and_return(slotted_packagename)\n    allow(@slotted_resource).to receive(:[]).with(:install_options).and_return(['--foo', { '--bar' => 'baz', '--baz' => 'foo' }])\n\n    versioned_packagename = \"=dev-lang/ruby-1.9.3\"\n    @versioned_resource = double('resource', :should => true)\n    allow(@versioned_resource).to receive(:[]).with(:name).and_return(versioned_packagename)\n    allow(@versioned_resource).to receive(:[]).with(:install_options).and_return([])\n    allow(@versioned_resource).to receive(:[]).with(:uninstall_options).and_return([])\n\n    versioned_slotted_packagename = \"=dev-lang/ruby-1.9.3:1.9\"\n    @versioned_slotted_resource = double('resource', :should => true)\n    allow(@versioned_slotted_resource).to receive(:[]).with(:name).and_return(versioned_slotted_packagename)\n    allow(@versioned_slotted_resource).to receive(:[]).with(:install_options).and_return([])\n    allow(@versioned_slotted_resource).to receive(:[]).with(:uninstall_options).and_return([])\n\n    set_packagename = \"@system\"\n    @set_resource = double('resource', :should => true)\n    allow(@set_resource).to receive(:[]).with(:name).and_return(set_packagename)\n    allow(@set_resource).to receive(:[]).with(:install_options).and_return([])\n\n    package_sets = \"system\\nworld\\n\"\n    @provider = described_class.new(@resource)\n    allow(@provider).to receive(:qatom).and_return({:category=>nil, :pn=>\"sl\", :pv=>nil, :pr=>nil, :slot=>nil, :pfx=>nil, :sfx=>nil})\n    allow(@provider.class).to receive(:emerge).with('--list-sets').and_return(package_sets)\n    @unslotted_provider = described_class.new(@unslotted_resource)\n    allow(@unslotted_provider).to receive(:qatom).and_return({:category=>\"dev-lang\", :pn=>\"ruby\", :pv=>nil, :pr=>nil, :slot=>nil, :pfx=>nil, :sfx=>nil})\n    allow(@unslotted_provider.class).to receive(:emerge).with('--list-sets').and_return(package_sets)\n    @slotted_provider = described_class.new(@slotted_resource)\n    allow(@slotted_provider).to receive(:qatom).and_return({:category=>\"dev-lang\", :pn=>\"ruby\", :pv=>nil, :pr=>nil, :slot=>\"2.1\", :pfx=>nil, :sfx=>nil})\n    allow(@slotted_provider.class).to receive(:emerge).with('--list-sets').and_return(package_sets)\n    @versioned_provider = described_class.new(@versioned_resource)\n    allow(@versioned_provider).to receive(:qatom).and_return({:category=>\"dev-lang\", :pn=>\"ruby\", :pv=>\"1.9.3\", :pr=>nil, :slot=>nil, :pfx=>\"=\", :sfx=>nil})\n    allow(@versioned_provider.class).to receive(:emerge).with('--list-sets').and_return(package_sets)\n    @versioned_slotted_provider = described_class.new(@versioned_slotted_resource)\n    allow(@versioned_slotted_provider).to receive(:qatom).and_return({:category=>\"dev-lang\", :pn=>\"ruby\", :pv=>\"1.9.3\", :pr=>nil, :slot=>\"1.9\", :pfx=>\"=\", :sfx=>nil})\n    allow(@versioned_slotted_provider.class).to receive(:emerge).with('--list-sets').and_return(package_sets)\n    @set_provider = described_class.new(@set_resource)\n    allow(@set_provider).to receive(:qatom).and_return({:category=>nil, :pn=>\"@system\", :pv=>nil, :pr=>nil, :slot=>nil, :pfx=>nil, :sfx=>nil})\n    allow(@set_provider.class).to receive(:emerge).with('--list-sets').and_return(package_sets)\n\n    portage   = double(:executable => \"foo\",:execute => true)\n    allow(Puppet::Provider::CommandDefiner).to receive(:define).and_return(portage)\n\n    @nomatch_result = \"\"\n    @match_result    = \"app-misc sl [] [5.02] [] [] [5.02] [5.02:0] http://www.tkl.iis.u-tokyo.ac.jp/~toyoda/index_e.html https://github.com/mtoyoda/sl/ sophisticated graphical program which corrects your miss typing\\n\"\n    @slot_match_result = \"dev-lang ruby [2.1.8] [2.1.9] [2.1.8:2.1] [2.1.8] [2.1.9,,,,,,,] [2.1.9:2.1] http://www.ruby-lang.org/ An object-oriented scripting language\\n\"\n  end\n\n  it \"is versionable\" do\n    expect(described_class).to be_versionable\n  end\n\n  it \"is reinstallable\" do\n    expect(described_class).to be_reinstallable\n  end\n\n  it \"should be the default provider on 'os.family' => Gentoo\" do\n    expect(Facter).to receive(:value).with('os.family').and_return(\"Gentoo\")\n    expect(described_class.default?).to be_truthy\n  end\n\n  it 'should support string install options' do\n    expect(@provider).to receive(:emerge).with('--foo', '--bar', @resource[:name])\n\n    @provider.install\n  end\n\n  it 'should support updating' do\n    expect(@unslotted_provider).to receive(:emerge).with('--update', @unslotted_resource[:name])\n\n    @unslotted_provider.install\n  end\n\n  it 'should support hash install options' do\n    expect(@slotted_provider).to receive(:emerge).with('--foo', '--bar=baz', '--baz=foo', @slotted_resource[:name])\n\n    @slotted_provider.install\n  end\n\n  it 'should support hash uninstall options' do\n    expect(@provider).to receive(:emerge).with('--rage-clean', '--foo', '--bar=baz', '--baz=foo', @resource[:name])\n\n    @provider.uninstall\n  end\n\n  it 'should support install of specific version' do\n    expect(@versioned_provider).to receive(:emerge).with(@versioned_resource[:name])\n\n    @versioned_provider.install\n  end\n\n  it 'should support install of specific version and slot' do\n    expect(@versioned_slotted_provider).to receive(:emerge).with(@versioned_slotted_resource[:name])\n\n    @versioned_slotted_provider.install\n  end\n\n  it 'should support uninstall of specific version' do\n    expect(@versioned_provider).to receive(:emerge).with('--rage-clean', @versioned_resource[:name])\n\n    @versioned_provider.uninstall\n  end\n\n  it 'should support uninstall of specific version and slot' do\n    expect(@versioned_slotted_provider).to receive(:emerge).with('--rage-clean', @versioned_slotted_resource[:name])\n\n    @versioned_slotted_provider.uninstall\n  end\n\n  it \"uses :emerge to install packages\" do\n    expect(@provider).to receive(:emerge)\n\n    @provider.install\n  end\n\n  it \"uses query to find the latest package\" do\n    expect(@provider).to receive(:query).and_return({:versions_available => \"myversion\"})\n\n    @provider.latest\n  end\n\n  it \"uses eix to search the lastest version of a package\" do\n    allow(@provider).to receive(:update_eix)\n    expect(@provider).to receive(:eix).and_return(StringIO.new(@match_result))\n\n    @provider.query\n  end\n\n  it \"allows to emerge package sets\" do\n    expect(@set_provider).to receive(:emerge).with(@set_resource[:name])\n\n    @set_provider.install\n  end\n\n  it \"allows to emerge and update package sets\" do\n    allow(@set_resource).to receive(:should).with(:ensure).and_return(:latest)\n    expect(@set_provider).to receive(:emerge).with('--update', @set_resource[:name])\n\n    @set_provider.install\n  end\n\n  it \"eix arguments must not include --stable\" do\n    expect(@provider.class.eix_search_arguments).not_to include(\"--stable\")\n  end\n\n  it \"eix arguments must not include --exact\" do\n    expect(@provider.class.eix_search_arguments).not_to include(\"--exact\")\n  end\n\n  it \"query uses default arguments\" do\n    allow(@provider).to receive(:update_eix)\n    expect(@provider).to receive(:eix).and_return(StringIO.new(@match_result))\n    expect(@provider.class).to receive(:eix_search_arguments).and_return([])\n\n    @provider.query\n  end\n\n  it \"can handle search output with empty square brackets\" do\n    allow(@provider).to receive(:update_eix)\n    expect(@provider).to receive(:eix).and_return(StringIO.new(@match_result))\n\n    expect(@provider.query[:name]).to eq(\"sl\")\n  end\n\n  it \"can provide the package name without slot\" do\n    expect(@unslotted_provider.qatom[:slot]).to be_nil\n  end\n\n  it \"can extract the slot from the package name\" do\n    expect(@slotted_provider.qatom[:slot]).to eq('2.1')\n  end\n\n  it \"returns nil for as the slot when no slot is specified\" do\n    expect(@provider.qatom[:slot]).to be_nil\n  end\n\n  it \"provides correct package atoms for unslotted packages\" do\n    expect(@versioned_provider.qatom[:pv]).to eq('1.9.3')\n  end\n\n  it \"provides correct package atoms for slotted packages\" do\n    expect(@versioned_slotted_provider.qatom[:pfx]).to eq('=')\n    expect(@versioned_slotted_provider.qatom[:category]).to eq('dev-lang')\n    expect(@versioned_slotted_provider.qatom[:pn]).to eq('ruby')\n    expect(@versioned_slotted_provider.qatom[:pv]).to eq('1.9.3')\n    expect(@versioned_slotted_provider.qatom[:slot]).to eq('1.9')\n  end\n\n  it \"can handle search output with slots for unslotted packages\" do\n    allow(@unslotted_provider).to receive(:update_eix)\n    expect(@unslotted_provider).to receive(:eix).and_return(StringIO.new(@slot_match_result))\n\n    result = @unslotted_provider.query\n    expect(result[:name]).to eq('ruby')\n    expect(result[:ensure]).to eq('2.1.8')\n    expect(result[:version_available]).to eq('2.1.9')\n  end\n\n  it \"can handle search output with slots\" do\n    allow(@slotted_provider).to receive(:update_eix)\n    expect(@slotted_provider).to receive(:eix).and_return(StringIO.new(@slot_match_result))\n\n    result = @slotted_provider.query\n    expect(result[:name]).to eq('ruby')\n    expect(result[:ensure]).to eq('2.1.8')\n    expect(result[:version_available]).to eq('2.1.9')\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/puppet_gem_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:puppet_gem) do\n  let(:resource) do\n    Puppet::Type.type(:package).new(\n      :name     => 'myresource',\n      :ensure   => :installed\n    )\n  end\n\n  let(:provider) do\n    provider = described_class.new\n    provider.resource = resource\n    provider\n  end\n\n  if Puppet::Util::Platform.windows?\n    let(:provider_gem_cmd) { 'C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin\\gem.bat' }\n  else\n    let(:provider_gem_cmd) { '/opt/puppetlabs/puppet/bin/gem' }\n  end\n\n  let(:execute_options) do\n    {\n      failonfail: true,\n      combine: true,\n      custom_environment: {\n        'HOME'=>ENV['HOME'],\n        'PKG_CONFIG_PATH' => '/opt/puppetlabs/puppet/lib/pkgconfig'\n      }\n    }\n  end\n\n  before :each do\n    resource.provider = provider\n    if Puppet::Util::Platform.windows?\n      # provider is loaded before we can stub, so stub the class we're testing\n      allow(provider.class).to receive(:command).with(:gemcmd).and_return(provider_gem_cmd)\n    else\n      allow(provider.class).to receive(:which).with(provider_gem_cmd).and_return(provider_gem_cmd)\n    end\n    allow(File).to receive(:file?).with(provider_gem_cmd).and_return(true)\n  end\n\n  context \"when installing\" do\n    before :each do\n      allow(provider).to receive(:rubygem_version).and_return('1.9.9')\n    end\n\n    it \"should use the path to the gem command\" do\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, be_an(Array)], be_a(Hash)).and_return('')\n      provider.install\n    end\n\n    it \"should not append install_options by default\" do\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, %w{install --no-rdoc --no-ri myresource}], anything).and_return('')\n      provider.install\n    end\n\n    it \"should allow setting an install_options parameter\" do\n      resource[:install_options] = [ '--force', {'--bindir' => '/usr/bin' } ]\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, %w{install --force --bindir=/usr/bin --no-rdoc --no-ri myresource}], anything).and_return('')\n      provider.install\n    end\n  end\n\n  context \"when uninstalling\" do\n    it \"should use the path to the gem command\" do\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, be_an(Array)], be_a(Hash)).and_return('')\n      provider.uninstall\n    end\n\n    it \"should not append uninstall_options by default\" do\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, %w{uninstall --executables --all myresource}], anything).and_return('')\n      provider.uninstall\n    end\n\n    it \"should allow setting an uninstall_options parameter\" do\n      resource[:uninstall_options] = [ '--force', {'--bindir' => '/usr/bin' } ]\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, %w{uninstall --executables --all myresource --force --bindir=/usr/bin}], anything).and_return('')\n      provider.uninstall\n    end\n\n    it 'should invalidate the rubygems cache' do\n      gem_source = double('gem_source')\n      allow(Puppet::Util::Autoload).to receive(:gem_source).and_return(gem_source)\n      expect(described_class).to receive(:execute).with([provider_gem_cmd, %w{uninstall --executables --all myresource}], anything).and_return('')\n      expect(gem_source).to receive(:clear_paths)\n      provider.uninstall\n    end\n  end\n\n  context 'calculated specificity' do\n    include_context 'provider specificity'\n\n    context 'when is not defaultfor' do\n      subject { described_class.specificity }\n      it { is_expected.to eql 1 }\n    end\n\n    context 'when is defaultfor' do\n      let(:os) { Puppet.runtime[:facter].value('os.name') }\n      subject do\n        described_class.defaultfor('os.name': os)\n        described_class.specificity\n      end\n      it { is_expected.to be > 100 }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/puppetserver_gem_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:puppetserver_gem) do\n  let(:resource) do\n    Puppet::Type.type(:package).new(\n      name: 'myresource',\n      ensure: :installed\n    )\n  end\n\n  let(:provider) do\n    provider = described_class.new\n    provider.resource = resource\n    provider\n  end\n\n  let(:provider_gem_cmd) { '/opt/puppetlabs/bin/puppetserver' }\n\n  let(:execute_options) do\n    { failonfail: true, combine: true, custom_environment: { 'HOME' => ENV['HOME'] } }\n  end\n\n  before :each do\n    resource.provider = provider\n    allow(Puppet::Util).to receive(:which).with(provider_gem_cmd).and_return(provider_gem_cmd)\n    allow(File).to receive(:file?).with(provider_gem_cmd).and_return(true)\n  end\n\n  describe \"#install\" do\n    it \"uses the path to the gem command\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, be_an(Array)], be_a(Hash)).and_return('')\n      provider.install\n    end\n\n    it \"appends version if given\" do\n      resource[:ensure] = ['1.2.1']\n      expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem install -v 1.2.1 --no-document myresource}], anything).and_return('')\n      provider.install\n    end\n\n    context \"with install_options\" do\n      it \"does not append the parameter by default\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem install --no-document myresource}], anything).and_return('')\n        provider.install\n      end\n\n      it \"allows setting the parameter\" do\n        resource[:install_options] = [ '--force', {'--bindir' => '/usr/bin' } ]\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem install --force --bindir=/usr/bin --no-document myresource}], anything).and_return('')\n        provider.install\n      end\n    end\n\n    context \"with source\" do\n      it \"correctly sets http source\" do\n        resource[:source] = 'http://rubygems.com'\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem install --no-document --source http://rubygems.com myresource}], anything).and_return('')\n        provider.install\n      end\n\n      it \"correctly sets local file source\" do\n        resource[:source] = 'paint-2.2.0.gem'\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem install --no-document paint-2.2.0.gem}], anything).and_return('')\n        provider.install\n      end\n\n      it \"correctly sets local file source with URI scheme\" do\n        resource[:source] = 'file:///root/paint-2.2.0.gem'\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem install --no-document /root/paint-2.2.0.gem}], anything).and_return('')\n        provider.install\n      end\n\n      it \"raises if given a puppet URI scheme\" do\n        resource[:source] = 'puppet:///paint-2.2.0.gem'\n        expect { provider.install }.to raise_error(Puppet::Error, 'puppet:// URLs are not supported as gem sources')\n      end\n\n      it \"raises if given an invalid URI\" do\n        resource[:source] = 'h;ttp://rubygems.com'\n        expect { provider.install }.to raise_error(Puppet::Error, /Invalid source '': bad URI \\(is not URI\\?\\)/)\n      end\n    end\n  end\n\n  describe \"#uninstall\" do\n    it \"uses the path to the gem command\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, be_an(Array)], be_a(Hash)).and_return('')\n      provider.uninstall\n    end\n\n    context \"with uninstall_options\" do\n      it \"does not append the parameter by default\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem uninstall --executables --all myresource}], anything).and_return('')\n        provider.uninstall\n      end\n\n      it \"allows setting the parameter\" do\n        resource[:uninstall_options] = [ '--force', {'--bindir' => '/usr/bin' } ]\n        expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem uninstall --executables --all myresource --force --bindir=/usr/bin}], anything).and_return('')\n        provider.uninstall\n      end\n    end\n  end\n\n  describe \".gemlist\" do\n    context \"listing installed packages\" do\n      it \"uses the puppet_gem provider_command to list local gems\" do\n        allow(Puppet::Type::Package::ProviderPuppet_gem).to receive(:provider_command).and_return('/opt/puppetlabs/puppet/bin/gem')\n        allow(described_class).to receive(:validate_command).with('/opt/puppetlabs/puppet/bin/gem')\n\n        expected = { name: 'world_airports', provider: :puppetserver_gem, ensure: ['1.1.3'] }\n        expect(Puppet::Util::Execution).to receive(:execute).with(['/opt/puppetlabs/puppet/bin/gem', %w[list --local]], anything).and_return(File.read(my_fixture('gem-list-local-packages')))\n        expect(described_class.gemlist({ local: true })).to include(expected)\n      end\n    end\n\n    it \"appends the gem source if given\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with([provider_gem_cmd, %w{gem list --remote --source https://rubygems.com}], anything).and_return('')\n      described_class.gemlist({ source: 'https://rubygems.com' })\n    end\n  end\n\n  context 'calculated specificity' do\n    include_context 'provider specificity'\n\n    context 'when is not defaultfor' do\n      subject { described_class.specificity }\n      it { is_expected.to eql 1 }\n    end\n\n    context 'when is defaultfor' do\n      let(:os) {  Puppet.runtime[:facter].value('os.name') }\n      subject do\n        described_class.defaultfor('os.name': os)\n        described_class.specificity\n      end\n      it { is_expected.to be > 100 }\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/provider/package/rpm_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:rpm) do\n  let (:packages) do\n    <<-RPM_OUTPUT\n    'cracklib-dicts 0 2.8.9 3.3 x86_64\n    basesystem 0 8.0 5.1.1.el5.centos noarch\n    chkconfig 0 1.3.30.2 2.el5 x86_64\n    myresource 0 1.2.3.4 5.el4 noarch\n    mysummaryless 0 1.2.3.4 5.el4 noarch\n    tomcat 1 1.2.3.4 5.el4 x86_64\n    kernel 1 1.2.3.4 5.el4 x86_64\n    kernel 1 1.2.3.6 5.el4 x86_64\n    '\n    RPM_OUTPUT\n  end\n\n  let(:resource_name) { 'myresource' }\n  let(:resource) do\n    Puppet::Type.type(:package).new(\n      :name     => resource_name,\n      :ensure   => :installed,\n      :provider => 'rpm'\n    )\n  end\n\n  let(:provider) do\n    provider = subject()\n    provider.resource = resource\n    provider\n  end\n\n  let(:nevra_format) { %Q{%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\\\\n} }\n  let(:execute_options) do\n    {:failonfail => true, :combine => true, :custom_environment => {}}\n  end\n  let(:rpm_version) { \"RPM version 5.0.0\\n\" }\n\n  before(:each) do\n    allow(Puppet::Util).to receive(:which).with(\"rpm\").and_return(\"/bin/rpm\")\n    allow(described_class).to receive(:which).with(\"rpm\").and_return(\"/bin/rpm\")\n    described_class.instance_variable_set(\"@current_version\", nil)\n    expect(Puppet::Type::Package::ProviderRpm).to receive(:execute)\n      .with([\"/bin/rpm\", \"--version\"])\n      .and_return(rpm_version).at_most(:once)\n    expect(Puppet::Util::Execution).to receive(:execute)\n      .with([\"/bin/rpm\", \"--version\"], execute_options)\n      .and_return(Puppet::Util::Execution::ProcessOutput.new(rpm_version, 0)).at_most(:once)\n  end\n\n  describe 'provider features' do\n    it { is_expected.to be_versionable }\n    it { is_expected.to be_install_options }\n    it { is_expected.to be_uninstall_options }\n    it { is_expected.to be_virtual_packages }\n  end\n\n  describe \"self.instances\" do\n    describe \"with a modern version of RPM\" do\n      it \"includes all the modern flags\" do\n        expect(Puppet::Util::Execution).to receive(:execpipe)\n          .with(\"/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}' | sort\")\n          .and_yield(packages)\n\n        described_class.instances\n      end\n    end\n\n    describe \"with a version of RPM < 4.1\" do\n      let(:rpm_version) { \"RPM version 4.0.2\\n\" }\n\n      it \"excludes the --nosignature flag\" do\n        expect(Puppet::Util::Execution).to receive(:execpipe)\n          .with(\"/bin/rpm -qa  --nodigest --qf '#{nevra_format}' | sort\")\n          .and_yield(packages)\n\n        described_class.instances\n      end\n    end\n\n    describe \"with a version of RPM < 4.0.2\" do\n      let(:rpm_version) { \"RPM version 3.0.5\\n\" }\n\n      it \"excludes the --nodigest flag\" do\n        expect(Puppet::Util::Execution).to receive(:execpipe)\n        .with(\"/bin/rpm -qa   --qf '#{nevra_format}' | sort\")\n        .and_yield(packages)\n\n        described_class.instances\n      end\n    end\n\n    it \"returns an array of packages\" do\n      expect(Puppet::Util::Execution).to receive(:execpipe)\n        .with(\"/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}' | sort\")\n        .and_yield(packages)\n\n      installed_packages = described_class.instances\n\n      expect(installed_packages[0].properties).to eq(\n        {\n          :provider => :rpm,\n          :name => \"cracklib-dicts\",\n          :epoch => \"0\",\n          :version => \"2.8.9\",\n          :release => \"3.3\",\n          :arch => \"x86_64\",\n          :ensure => \"2.8.9-3.3\",\n        }\n      )\n      expect(installed_packages[1].properties).to eq(\n        {\n          :provider => :rpm,\n          :name => \"basesystem\",\n          :epoch => \"0\",\n          :version => \"8.0\",\n          :release => \"5.1.1.el5.centos\",\n          :arch => \"noarch\",\n          :ensure => \"8.0-5.1.1.el5.centos\",\n        }\n      )\n      expect(installed_packages[2].properties).to eq(\n        {\n          :provider => :rpm,\n          :name => \"chkconfig\",\n          :epoch => \"0\",\n          :version => \"1.3.30.2\",\n          :release => \"2.el5\",\n          :arch => \"x86_64\",\n          :ensure => \"1.3.30.2-2.el5\",\n        }\n      )\n      expect(installed_packages[3].properties).to eq(\n        {\n          :provider => :rpm,\n          :name => \"myresource\",\n          :epoch => \"0\",\n          :version => \"1.2.3.4\",\n          :release => \"5.el4\",\n          :arch => \"noarch\",\n          :ensure => \"1.2.3.4-5.el4\",\n        }\n      )\n      expect(installed_packages[4].properties).to eq(\n        {\n          :provider    => :rpm,\n          :name        => \"mysummaryless\",\n          :epoch       => \"0\",\n          :version     => \"1.2.3.4\",\n          :release     => \"5.el4\",\n          :arch        => \"noarch\",\n          :ensure      => \"1.2.3.4-5.el4\",\n        }\n      )\n      expect(installed_packages[5].properties).to eq(\n        {\n          :provider    => :rpm,\n          :name        => \"tomcat\",\n          :epoch       => \"1\",\n          :version     => \"1.2.3.4\",\n          :release     => \"5.el4\",\n          :arch        => \"x86_64\",\n          :ensure      => \"1:1.2.3.4-5.el4\",\n        }\n      )\n      expect(installed_packages[6].properties).to eq(\n        {\n          :provider    => :rpm,\n          :name        => \"kernel\",\n          :epoch       => \"1\",\n          :version     => \"1.2.3.4\",\n          :release     => \"5.el4\",\n          :arch        => \"x86_64\",\n          :ensure      => \"1:1.2.3.4-5.el4; 1:1.2.3.6-5.el4\",\n        }\n      )\n    end\n  end\n\n  describe \"#install\" do\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name     => 'myresource',\n        :ensure   => :installed,\n        :source   => '/path/to/package'\n      )\n    end\n\n    describe \"when not already installed\" do\n      it \"only includes the '-i' flag\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-i\"], '/path/to/package'], execute_options)\n        provider.install\n      end\n    end\n\n    describe \"when installed with options\" do\n      let(:resource) do\n        Puppet::Type.type(:package).new(\n          :name            => resource_name,\n          :ensure          => :installed,\n          :provider        => 'rpm',\n          :source          => '/path/to/package',\n          :install_options => ['-D', {'--test' => 'value'}, '-Q']\n        )\n      end\n\n      it \"includes the options\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-i\", \"-D\", \"--test=value\", \"-Q\"], '/path/to/package'], execute_options)\n        provider.install\n      end\n    end\n\n    describe \"when an older version is installed\" do\n      before(:each) do\n        # Force the provider to think a version of the package is already installed\n        # This is real hacky. I'm sorry.  --jeffweiss 25 Jan 2013\n        provider.instance_variable_get('@property_hash')[:ensure] = '1.2.3.3'\n      end\n\n      it \"includes the '-U --oldpackage' flags\" do\n         expect(Puppet::Util::Execution).to receive(:execute)\n           .with([\"/bin/rpm\", [\"-U\", \"--oldpackage\"], '/path/to/package'], execute_options)\n         provider.install\n      end\n    end\n  end\n\n  describe \"#latest\" do\n    it \"retrieves version string after querying rpm for version from source file\" do\n      expect(resource).to receive(:[]).with(:source).and_return('source-string')\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with([\"/bin/rpm\", \"-q\", \"--qf\", \"#{nevra_format}\", \"-p\", \"source-string\"])\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(\"myresource 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      expect(provider.latest).to eq(\"1.2.3.4-5.el4\")\n    end\n\n    it \"raises an error if the rpm command fails\" do\n      expect(resource).to receive(:[]).with(:source).and_return('source-string')\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with([\"/bin/rpm\", \"-q\", \"--qf\", \"#{nevra_format}\", \"-p\", \"source-string\"])\n        .and_raise(Puppet::ExecutionFailure, 'rpm command failed')\n\n      expect {\n        provider.latest\n      }.to raise_error(Puppet::Error, 'rpm command failed')\n    end\n  end\n\n  describe \"#uninstall\" do\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name   => resource_name,\n        :ensure => :installed\n      )\n    end\n\n    describe \"on an ancient RPM\" do\n      let(:rpm_version) { \"RPM version 3.0.6\\n\" }\n\n      before(:each) do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", \"-q\", resource_name, '', '', '--qf', \"#{nevra_format}\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"#{resource_name} 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      end\n\n      it \"excludes the architecture from the package name\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-e\"], resource_name], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0)).at_most(:once)\n        provider.uninstall\n      end\n    end\n\n    describe \"on a modern RPM\" do\n      let(:rpm_version) { \"RPM version 4.10.0\\n\" }\n\n\n      before(:each) do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", \"-q\", resource_name, '--nosignature', '--nodigest', \"--qf\", \"#{nevra_format}\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"#{resource_name} 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      end\n\n      it \"excludes the architecture from the package name\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-e\"], resource_name], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0)).at_most(:once)\n        provider.uninstall\n      end\n    end\n\n    describe \"on a modern RPM when architecture is specified\" do\n      let(:rpm_version) { \"RPM version 4.10.0\\n\" }\n\n      let(:resource) do\n        Puppet::Type.type(:package).new(\n          :name   => \"#{resource_name}.noarch\",\n          :ensure => :absent,\n        )\n      end\n\n      before(:each) do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", \"-q\", \"#{resource_name}.noarch\", '--nosignature', '--nodigest', \"--qf\", \"#{nevra_format}\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"#{resource_name} 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      end\n\n      it \"includes the architecture in the package name\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-e\"], \"#{resource_name}.noarch\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0)).at_most(:once)\n        provider.uninstall\n      end\n    end\n\n    describe \"when version and release are specified\" do\n      let(:resource) do\n        Puppet::Type.type(:package).new(\n          :name   => \"#{resource_name}-1.2.3.4-5.el4\",\n          :ensure => :absent,\n        )\n      end\n\n      before(:each) do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", \"-q\", \"#{resource_name}-1.2.3.4-5.el4\", '--nosignature', '--nodigest', \"--qf\", \"#{nevra_format}\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"#{resource_name} 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      end\n\n      it \"includes the version and release in the package name\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-e\"], \"#{resource_name}-1.2.3.4-5.el4\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0)).at_most(:once)\n        provider.uninstall\n      end\n    end\n\n    describe \"when only version is specified\" do\n      let(:resource) do\n        Puppet::Type.type(:package).new(\n          :name   => \"#{resource_name}-1.2.3.4\",\n          :ensure => :absent,\n        )\n      end\n\n      before(:each) do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", \"-q\", \"#{resource_name}-1.2.3.4\", '--nosignature', '--nodigest', \"--qf\", \"#{nevra_format}\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"#{resource_name} 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      end\n\n      it \"includes the version in the package name\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-e\"], \"#{resource_name}-1.2.3.4\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0)).at_most(:once)\n        provider.uninstall\n      end\n    end\n\n    describe \"when uninstalled with options\" do\n      let(:resource) do\n        Puppet::Type.type(:package).new(\n          :name              => resource_name,\n          :ensure            => :absent,\n          :provider          => 'rpm',\n          :uninstall_options => ['--nodeps']\n        )\n      end\n\n      before(:each) do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", \"-q\", resource_name, '--nosignature', '--nodigest', \"--qf\", \"#{nevra_format}\"], execute_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"#{resource_name} 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      end\n\n      it \"includes the options\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/bin/rpm\", [\"-e\", \"--nodeps\"], resource_name], execute_options)\n        provider.uninstall\n      end\n    end\n  end\n\n  describe \"parsing\" do\n    def parser_test(rpm_output_string, gold_hash, number_of_debug_logs = 0)\n      expect(Puppet).to receive(:debug).exactly(number_of_debug_logs).times()\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with([\"/bin/rpm\", \"-q\", resource_name, \"--nosignature\", \"--nodigest\", \"--qf\", \"#{nevra_format}\"], execute_options)\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(rpm_output_string, 0))\n      expect(provider.query).to eq(gold_hash)\n    end\n\n    let(:resource_name) { 'name' }\n    let('delimiter') { ':DESC:' }\n    let(:package_hash) do\n      {\n        :name => 'name',\n        :epoch => 'epoch',\n        :version => 'version',\n        :release => 'release',\n        :arch => 'arch',\n        :provider => :rpm,\n        :ensure => 'epoch:version-release',\n      }\n    end\n    let(:line) { 'name epoch version release arch' }\n\n    ['name', 'epoch', 'version', 'release', 'arch'].each do |field|\n      it \"still parses if #{field} is replaced by delimiter\" do\n        parser_test(\n          line.gsub(field, delimiter),\n          package_hash.merge(\n            field.to_sym => delimiter,\n            :ensure => 'epoch:version-release'.gsub(field, delimiter)\n          )\n        )\n      end\n    end\n\n    it \"does not fail if line is unparseable, but issues a debug log\" do\n      parser_test('bad data', {}, 1)\n    end\n\n    describe \"when the package is not found\" do\n      before do\n        expect(Puppet).not_to receive(:debug)\n        expected_args = [\"/bin/rpm\", \"-q\", resource_name, \"--nosignature\", \"--nodigest\", \"--qf\", \"#{nevra_format}\"]\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(expected_args, execute_options)\n          .and_raise(Puppet::ExecutionFailure.new(\"package #{resource_name} is not installed\"))\n      end\n\n      it \"does not log or fail if allow_virtual is false\" do\n        resource[:allow_virtual] = false\n        expect(provider.query).to be_nil\n      end\n\n      it \"does not log or fail if allow_virtual is true\" do\n        resource[:allow_virtual] = true\n        expected_args = ['/bin/rpm', '-q', resource_name, '--nosignature', '--nodigest', '--qf', \"#{nevra_format}\", '--whatprovides']\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(expected_args, execute_options)\n          .and_raise(Puppet::ExecutionFailure.new(\"package #{resource_name} is not provided\"))\n        expect(provider.query).to be_nil\n      end\n    end\n\n    it \"parses virtual package\" do\n      provider.resource[:allow_virtual] = true\n      expected_args = [\"/bin/rpm\", \"-q\", resource_name, \"--nosignature\", \"--nodigest\", \"--qf\", \"#{nevra_format}\"]\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(expected_args, execute_options)\n        .and_raise(Puppet::ExecutionFailure.new(\"package #{resource_name} is not installed\"))\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(expected_args + [\"--whatprovides\"], execute_options)\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(\"myresource 0 1.2.3.4 5.el4 noarch\\n\", 0))\n      expect(provider.query).to eq({\n        :name     => \"myresource\",\n        :epoch    => \"0\",\n        :version  => \"1.2.3.4\",\n        :release  => \"5.el4\",\n        :arch     => \"noarch\",\n        :provider => :rpm,\n        :ensure   => \"1.2.3.4-5.el4\"\n      })\n    end\n  end\n\n  describe \"#install_options\" do\n    it \"returns nil by default\" do\n      expect(provider.install_options).to eq(nil)\n    end\n\n    it \"returns install_options when set\" do\n      provider.resource[:install_options] = ['-n']\n      expect(provider.install_options).to eq(['-n'])\n    end\n\n    it \"returns multiple install_options when set\" do\n      provider.resource[:install_options] = ['-L', '/opt/puppet']\n      expect(provider.install_options).to eq(['-L', '/opt/puppet'])\n    end\n\n    it 'returns install_options when set as hash' do\n      provider.resource[:install_options] = [{ '-Darch' => 'vax' }]\n      expect(provider.install_options).to eq(['-Darch=vax'])\n    end\n\n    it 'returns install_options when an array with hashes' do\n      provider.resource[:install_options] = [ '-L', { '-Darch' => 'vax' }]\n      expect(provider.install_options).to eq(['-L', '-Darch=vax'])\n    end\n  end\n\n  describe \"#uninstall_options\" do\n    it \"returns nil by default\" do\n      expect(provider.uninstall_options).to eq(nil)\n    end\n\n    it \"returns uninstall_options when set\" do\n      provider.resource[:uninstall_options] = ['-n']\n      expect(provider.uninstall_options).to eq(['-n'])\n    end\n\n    it \"returns multiple uninstall_options when set\" do\n      provider.resource[:uninstall_options] = ['-L', '/opt/puppet']\n      expect(provider.uninstall_options).to eq(['-L', '/opt/puppet'])\n    end\n\n    it 'returns uninstall_options when set as hash' do\n      provider.resource[:uninstall_options] = [{ '-Darch' => 'vax' }]\n      expect(provider.uninstall_options).to eq(['-Darch=vax'])\n    end\n    it 'returns uninstall_options when an array with hashes' do\n      provider.resource[:uninstall_options] = [ '-L', { '-Darch' => 'vax' }]\n      expect(provider.uninstall_options).to eq(['-L', '-Darch=vax'])\n    end\n  end\n\n  describe \".nodigest\" do\n    { '4.0'   => nil,\n      '4.0.1' => nil,\n      '4.0.2' => '--nodigest',\n      '4.0.3' => '--nodigest',\n      '4.1'   => '--nodigest',\n      '5'     => '--nodigest',\n    }.each do |version, expected|\n      describe \"when current version is #{version}\" do\n        it \"returns #{expected.inspect}\" do\n          allow(described_class).to receive(:current_version).and_return(version)\n          expect(described_class.nodigest).to eq(expected)\n        end\n      end\n    end\n  end\n\n  describe \".nosignature\" do\n    { '4.0.3' => nil,\n      '4.1'   => '--nosignature',\n      '4.1.1' => '--nosignature',\n      '4.2'   => '--nosignature',\n      '5'     => '--nosignature',\n    }.each do |version, expected|\n      describe \"when current version is #{version}\" do\n        it \"returns #{expected.inspect}\" do\n          allow(described_class).to receive(:current_version).and_return(version)\n          expect(described_class.nosignature).to eq(expected)\n        end\n      end\n    end\n  end\n\n  describe 'insync?' do\n    context 'for multiple versions' do\n      let(:is) { '1:1.2.3.4-5.el4; 1:5.6.7.8-5.el4' }\n      it 'returns true if there is match and feature is enabled' do\n        resource[:install_only] = true\n        resource[:ensure] = '1:1.2.3.4-5.el4'\n        expect(provider).to be_insync(is)\n      end\n      it 'returns false if there is match and feature is not enabled' do\n        resource[:ensure] = '1:1.2.3.4-5.el4'\n        expect(provider).to_not be_insync(is)\n      end\n      it 'returns false if no match and feature is enabled' do\n        resource[:install_only] = true\n        resource[:ensure] = '1:1.2.3.6-5.el4'\n        expect(provider).to_not be_insync(is)\n      end\n      it 'returns false if no match and feature is not enabled' do\n        resource[:ensure] = '1:1.2.3.6-5.el4'\n        expect(provider).to_not be_insync(is)\n      end\n    end\n    context 'for simple versions' do\n      let(:is) { '1:1.2.3.4-5.el4' }\n      it 'returns true if there is match and feature is enabled' do\n        resource[:install_only] = true\n        resource[:ensure] = '1:1.2.3.4-5.el4'\n        expect(provider).to be_insync(is)\n      end\n      it 'returns true if there is match and feature is not enabled' do\n        resource[:ensure] = '1:1.2.3.4-5.el4'\n        expect(provider).to be_insync(is)\n      end\n      it 'returns false if no match and feature is enabled' do\n        resource[:install_only] = true\n        resource[:ensure] = '1:1.2.3.6-5.el4'\n        expect(provider).to_not be_insync(is)\n      end\n      it 'returns false if no match and feature is not enabled' do\n        resource[:ensure] = '1:1.2.3.6-5.el4'\n        expect(provider).to_not be_insync(is)\n      end\n    end\n  end\n\n  describe 'rpm multiversion to hash' do\n    it 'should return empty hash for empty imput' do\n      package_hash = described_class.nevra_to_multiversion_hash('')\n      expect(package_hash).to eq({})\n    end\n\n    it 'should return package hash for one package input' do\n      package_list = <<-RPM_OUTPUT\nkernel-devel 1 1.2.3.4 5.el4 x86_64\nRPM_OUTPUT\n      package_hash = described_class.nevra_to_multiversion_hash(package_list)\n      expect(package_hash).to eq(\n        {\n          :arch => \"x86_64\",\n          :ensure => \"1:1.2.3.4-5.el4\",\n          :epoch => \"1\",\n          :name => \"kernel-devel\",\n          :provider => :rpm,\n          :release => \"5.el4\",\n          :version => \"1.2.3.4\",\n        }\n      )\n    end\n\n    it 'should return package hash with versions concatenated in ensure for two package input' do\n      package_list = <<-RPM_OUTPUT\nkernel-devel 1 1.2.3.4 5.el4 x86_64\nkernel-devel 1 5.6.7.8 5.el4 x86_64\nRPM_OUTPUT\n      package_hash = described_class.nevra_to_multiversion_hash(package_list)\n      expect(package_hash).to eq(\n        {\n          :arch => \"x86_64\",\n          :ensure => \"1:1.2.3.4-5.el4; 1:5.6.7.8-5.el4\",\n          :epoch => \"1\",\n          :name => \"kernel-devel\",\n          :provider => :rpm,\n          :release => \"5.el4\",\n          :version => \"1.2.3.4\",\n        }\n      )\n    end\n\n    it 'should return list of packages for one multiversion and one package input' do\n      package_list = <<-RPM_OUTPUT\nkernel-devel 1 1.2.3.4 5.el4 x86_64\nkernel-devel 1 5.6.7.8 5.el4 x86_64\nbasesystem 0 8.0 5.1.1.el5.centos noarch\nRPM_OUTPUT\n      package_hash = described_class.nevra_to_multiversion_hash(package_list)\n      expect(package_hash).to eq(\n        [\n          {\n            :arch => \"x86_64\",\n            :ensure => \"1:1.2.3.4-5.el4; 1:5.6.7.8-5.el4\",\n            :epoch => \"1\",\n            :name => \"kernel-devel\",\n            :provider => :rpm,\n            :release => \"5.el4\",\n            :version => \"1.2.3.4\",\n          },\n          {\n            :provider => :rpm,\n            :name => \"basesystem\",\n            :epoch => \"0\",\n            :version => \"8.0\",\n            :release => \"5.1.1.el5.centos\",\n            :arch => \"noarch\",\n            :ensure => \"8.0-5.1.1.el5.centos\",\n          }\n        ]\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/sun_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:sun) do\n  let(:resource) { Puppet::Type.type(:package).new(:name => 'dummy', :ensure => :installed, :provider => :sun) }\n  let(:provider) { resource.provider }\n\n  describe 'provider features' do\n    it { is_expected.to be_installable }\n    it { is_expected.to be_uninstallable }\n    it { is_expected.to be_upgradeable }\n    it { is_expected.not_to be_versionable }\n  end\n\n  [:install, :uninstall, :latest, :query, :update].each do |method|\n    it \"should have a #{method} method\" do\n      expect(provider).to respond_to(method)\n    end\n  end\n\n  context '#install' do\n    it \"should install a package\" do\n      resource[:ensure] = :installed\n      resource[:source] = '/cdrom'\n      expect(provider).to receive(:pkgadd).with(['-d', '/cdrom', '-n', 'dummy'])\n      provider.install\n    end\n\n    it \"should install a package if it is not present on update\" do\n      expect(provider).to receive(:pkginfo).with('-l', 'dummy').and_return(File.read(my_fixture('dummy.server')))\n      expect(provider).to receive(:pkgrm).with(['-n', 'dummy'])\n      expect(provider).to receive(:install)\n      provider.update\n    end\n\n     it \"should install a package on global zone if -G specified\" do\n      resource[:ensure] = :installed\n      resource[:source] = '/cdrom'\n      resource[:install_options] = '-G'\n      expect(provider).to receive(:pkgadd).with(['-d', '/cdrom', '-G', '-n', 'dummy'])\n      provider.install\n    end\n  end\n\n  context '#uninstall' do\n    it \"should uninstall a package\" do\n      expect(provider).to receive(:pkgrm).with(['-n','dummy'])\n      provider.uninstall\n    end\n  end\n\n  context '#update' do\n    it \"should call uninstall if not :absent on info2hash\" do\n      allow(provider).to receive(:info2hash).and_return({:name => 'SUNWdummy', :ensure => \"11.11.0,REV=2010.10.12.04.23\"})\n      expect(provider).to receive(:uninstall)\n      expect(provider).to receive(:install)\n      provider.update\n    end\n\n    it \"should not call uninstall if :absent on info2hash\" do\n      allow(provider).to receive(:info2hash).and_return({:name => 'SUNWdummy', :ensure => :absent})\n      expect(provider).to receive(:install)\n      provider.update\n    end\n  end\n\n  context '#query' do\n    it \"should find the package on query\" do\n      expect(provider).to receive(:pkginfo).with('-l', 'dummy').and_return(File.read(my_fixture('dummy.server')))\n      expect(provider.query).to eq({\n        :name     => 'SUNWdummy',\n        :category=>\"system\",\n        :platform=>\"i386\",\n        :ensure   => \"11.11.0,REV=2010.10.12.04.23\",\n        :root=>\"/\",\n        :description=>\"Dummy server (9.6.1-P3)\",\n        :vendor => \"Oracle Corporation\",\n      })\n    end\n\n    it \"shouldn't find the package on query if it is not present\" do\n      expect(provider).to receive(:pkginfo).with('-l', 'dummy').and_raise(Puppet::ExecutionFailure, \"Execution of 'pkginfo -l dummy' returned 3: ERROR: information for \\\"dummy\\\" not found.\")\n      expect(provider.query).to eq({:ensure => :absent})\n    end\n\n    it \"unknown message should raise error.\" do\n      expect(provider).to receive(:pkginfo).with('-l', 'dummy').and_return('RANDOM')\n      expect { provider.query }.to raise_error Puppet::Error\n    end\n  end\n\n  context '#instance' do\n    it \"should list instances when there are packages in the system\" do\n      expect(described_class).to receive(:pkginfo).with('-l').and_return(File.read(my_fixture('simple')))\n      instances = provider.class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure)} }\n      expect(instances.size).to eq(2)\n      expect(instances[0]).to eq({\n        :name     => 'SUNWdummy',\n        :ensure   => \"11.11.0,REV=2010.10.12.04.23\",\n      })\n      expect(instances[1]).to eq({\n        :name     => 'SUNWdummyc',\n        :ensure   => \"11.11.0,REV=2010.10.12.04.24\",\n      })\n    end\n\n    it \"should return empty if there were no packages\" do\n      expect(described_class).to receive(:pkginfo).with('-l').and_return('')\n      instances = provider.class.instances\n      expect(instances.size).to eq(0)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/tdnf_spec.rb",
    "content": "require 'spec_helper'\n\n# Note that much of the functionality of the tdnf provider is already tested with yum provider tests,\n# as yum is the parent provider, via dnf\ndescribe Puppet::Type.type(:package).provider(:tdnf) do\n  it_behaves_like 'RHEL package provider', described_class, 'tdnf'\n\n  context 'default' do\n    it 'should be the default provider on PhotonOS' do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(\"PhotonOS\")\n      expect(described_class).to be_default\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/up2date_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'up2date package provider' do\n  # This sets the class itself as the subject rather than\n  # an instance of the class.\n  subject do\n    Puppet::Type.type(:package).provider(:up2date)\n  end\n\n  osfamilies = [ 'redhat' ]\n  releases = [ '2.1', '3', '4' ]\n\n  osfamilies.each do |osfamily|\n    releases.each do |release|\n      it \"should be the default provider on #{osfamily} #{release}\" do\n        allow(Facter).to receive(:value).with('os.family').and_return(osfamily)\n        allow(Facter).to receive(:value).with('os.distro.release.full').and_return(release)\n        expect(subject.default?).to be_truthy\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/urpmi_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:urpmi) do\n  before do\n    %w[rpm urpmi urpme urpmq].each do |executable|\n      allow(Puppet::Util).to receive(:which).with(executable).and_return(executable)\n    end\n    allow(Puppet::Util::Execution).to receive(:execute)\n      .with(['rpm', '--version'], anything)\n      .and_return(Puppet::Util::Execution::ProcessOutput.new('RPM version 4.9.1.3', 0))\n  end\n\n  let(:resource) do\n    Puppet::Type.type(:package).new(:name => 'foopkg', :provider => :urpmi)\n  end\n\n  before do\n    subject.resource = resource\n    allow(Puppet::Type.type(:package)).to receive(:defaultprovider).and_return(described_class)\n  end\n\n  describe '#install' do\n    before do\n      allow(subject).to receive(:rpm).with('-q', 'foopkg', any_args).and_return(\"foopkg 0 1.2.3.4 5 noarch :DESC:\\n\")\n    end\n\n    describe 'without a version' do\n      it 'installs the unversioned package' do\n        resource[:ensure] = :present\n        expect(Puppet::Util::Execution).to receive(:execute).with(['urpmi', '--auto', 'foopkg'], anything)\n        subject.install\n      end\n    end\n\n    describe 'with a version' do\n      it 'installs the versioned package' do\n        resource[:ensure] = '4.5.6'\n        expect(Puppet::Util::Execution).to receive(:execute).with(['urpmi', '--auto', 'foopkg-4.5.6'], anything)\n        subject.install\n      end\n    end\n\n    describe \"and the package install fails\" do\n      it \"raises an error\" do\n        allow(Puppet::Util::Execution).to receive(:execute).with(['urpmi', '--auto', 'foopkg'], anything)\n        allow(subject).to receive(:query)\n        expect { subject.install }.to raise_error Puppet::Error, /Package \\S+ was not present after trying to install it/\n      end\n    end\n  end\n\n  describe '#latest' do\n    let(:urpmq_output) { 'foopkg : Lorem ipsum dolor sit amet, consectetur adipisicing elit ( 7.8.9-1.mga2 )' }\n\n    it \"uses urpmq to determine the latest package\" do\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['urpmq', '-S', 'foopkg'], anything)\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(urpmq_output, 0))\n      expect(subject.latest).to eq('7.8.9-1.mga2')\n    end\n\n    it \"falls back to the current version\" do\n      resource[:ensure] = '5.4.3'\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['urpmq', '-S', 'foopkg'], anything)\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      expect(subject.latest).to eq('5.4.3')\n    end\n  end\n\n  describe '#update' do\n    it 'delegates to #install' do\n      expect(subject).to receive(:install)\n      subject.update\n    end\n  end\n\n  describe '#purge' do\n    it 'uses urpme to purge packages' do\n      expect(Puppet::Util::Execution).to receive(:execute).with(['urpme', '--auto', 'foopkg'], anything)\n      subject.purge\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/windows/exe_package_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/package/windows/exe_package'\nrequire 'puppet/provider/package/windows'\n\ndescribe Puppet::Provider::Package::Windows::ExePackage do\n  let (:name)        { 'Git version 1.7.11' }\n  let (:version)     { '1.7.11' }\n  let (:source)      { 'E:\\Git-1.7.11.exe' }\n  let (:uninstall)   { '\"C:\\Program Files (x86)\\Git\\unins000.exe\" /SP-' }\n\n  context '::from_registry' do\n    it 'should return an instance of ExePackage' do\n      expect(described_class).to receive(:valid?).and_return(true)\n\n      pkg = described_class.from_registry('', {'DisplayName' => name, 'DisplayVersion' => version, 'UninstallString' => uninstall})\n      expect(pkg.name).to eq(name)\n      expect(pkg.version).to eq(version)\n      expect(pkg.uninstall_string).to eq(uninstall)\n    end\n\n    it 'should return nil if it is not a valid executable' do\n      expect(described_class).to receive(:valid?).and_return(false)\n\n      expect(described_class.from_registry('', {})).to be_nil\n    end\n  end\n\n  context '::valid?' do\n    let(:name)   { 'myproduct' }\n    let(:values) do { 'DisplayName' => name, 'UninstallString' => uninstall } end\n\n    {\n      'DisplayName'      => ['My App', ''],\n      'UninstallString'  => ['E:\\uninstall.exe', ''],\n      'WindowsInstaller' => [nil, 1],\n      'ParentKeyName'    => [nil, 'Uber Product'],\n      'Security Update'  => [nil, 'KB890830'],\n      'Update Rollup'    => [nil, 'Service Pack 42'],\n      'Hotfix'           => [nil, 'QFE 42']\n    }.each_pair do |k, arr|\n      it \"should accept '#{k}' with value '#{arr[0]}'\" do\n        values[k] = arr[0]\n        expect(described_class.valid?(name, values)).to be_truthy\n      end\n\n      it \"should reject '#{k}' with value '#{arr[1]}'\" do\n        values[k] = arr[1]\n        expect(described_class.valid?(name, values)).to be_falsey\n      end\n    end\n\n    it 'should reject packages whose name starts with \"KBXXXXXX\"' do\n      expect(described_class.valid?('KB890830', values)).to be_falsey\n    end\n\n    it 'should accept packages whose name does not start with \"KBXXXXXX\"' do\n      expect(described_class.valid?('My Update (KB890830)', values)).to be_truthy\n    end\n  end\n\n  context '#match?' do\n    let(:pkg) { described_class.new(name, version, uninstall) }\n\n    it 'should match product name' do\n      expect(pkg.match?({:name => name})).to be_truthy\n    end\n\n    it 'should return false otherwise' do\n      expect(pkg.match?({:name => 'not going to find it'})).to be_falsey\n    end\n  end\n\n  context '#install_command' do\n    it 'should install using the source' do\n      allow(Puppet::FileSystem).to receive(:exist?).with(source).and_return(true)\n      cmd = described_class.install_command({:source => source})\n\n      expect(cmd).to eq(source)\n    end\n\n    it 'should raise error when URI is invalid' do\n      web_source = 'https://www.t e s t.test/test.exe'\n\n      expect do\n        described_class.install_command({:source => web_source, :name => name})\n      end.to raise_error(Puppet::Error, /Error when installing #{name}:/)\n    end\n\n    it 'should download package from source file before installing', if: Puppet::Util::Platform.windows? do\n      web_source = 'https://www.test.test/test.exe'\n      stub_request(:get, web_source).to_return(status: 200, body: 'package binaries')\n      cmd = described_class.install_command({:source => web_source})\n      expect(File.read(cmd)).to eq('package binaries')\n    end\n  end\n\n  context '#uninstall_command' do\n    ['C:\\uninstall.exe', 'C:\\Program Files\\uninstall.exe'].each do |exe|\n      it \"should quote #{exe}\" do\n        expect(described_class.new(name, version, exe).uninstall_command).to eq(\n          \"\\\"#{exe}\\\"\"\n        )\n      end\n    end\n\n    ['\"C:\\Program Files\\uninstall.exe\"', '\"C:\\Program Files (x86)\\Git\\unins000.exe\" /SILENT\"'].each do |exe|\n      it \"should not quote #{exe}\" do\n        expect(described_class.new(name, version, exe).uninstall_command).to eq(\n          exe\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/windows/msi_package_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/package/windows/msi_package'\n\ndescribe Puppet::Provider::Package::Windows::MsiPackage do\n  let (:name)        { 'mysql-5.1.58-win-x64' }\n  let (:version)     { '5.1.58' }\n  let (:source)      { 'E:\\mysql-5.1.58-win-x64.msi' }\n  let (:productcode) { '{E437FFB6-5C49-4DAC-ABAE-33FF065FE7CC}' }\n  let (:packagecode) { '{5A6FD560-763A-4BC1-9E03-B18DFFB7C72C}' }\n\n  def expect_installer\n    inst = double()\n    expect(inst).to receive(:ProductState).and_return(5)\n    expect(inst).to receive(:ProductInfo).with(productcode, 'PackageCode').and_return(packagecode)\n    expect(described_class).to receive(:installer).and_return(inst)\n  end\n\n  context '::installer', :if => Puppet::Util::Platform.windows? do\n    it 'should return an instance of the COM interface' do\n      expect(described_class.installer).not_to be_nil\n    end\n  end\n\n  context '::from_registry' do\n    it 'should return an instance of MsiPackage' do\n      expect(described_class).to receive(:valid?).and_return(true)\n      expect_installer\n\n      pkg = described_class.from_registry(productcode, {'DisplayName' => name, 'DisplayVersion' => version})\n      expect(pkg.name).to eq(name)\n      expect(pkg.version).to eq(version)\n      expect(pkg.productcode).to eq(productcode)\n      expect(pkg.packagecode).to eq(packagecode)\n    end\n\n    it 'should return nil if it is not a valid MSI' do\n      expect(described_class).to receive(:valid?).and_return(false)\n\n      expect(described_class.from_registry(productcode, {})).to be_nil\n    end\n  end\n\n  context '::valid?' do\n    let(:values) do { 'DisplayName' => name, 'DisplayVersion' => version, 'WindowsInstaller' => 1 } end\n\n    {\n      'DisplayName'      => ['My App', ''],\n      'WindowsInstaller' => [1, nil],\n    }.each_pair do |k, arr|\n      it \"should accept '#{k}' with value '#{arr[0]}'\" do\n        values[k] = arr[0]\n        expect(described_class.valid?(productcode, values)).to be_truthy\n      end\n\n      it \"should reject '#{k}' with value '#{arr[1]}'\" do\n        values[k] = arr[1]\n        expect(described_class.valid?(productcode, values)).to be_falsey\n      end\n    end\n\n    it 'should reject packages whose name is not a productcode' do\n     expect(described_class.valid?('AddressBook', values)).to be_falsey\n   end\n\n   it 'should accept packages whose name is a productcode' do\n     expect(described_class.valid?(productcode, values)).to be_truthy\n   end\n  end\n\n  context '#match?' do\n    it 'should match package codes case-insensitively' do\n      pkg = described_class.new(name, version, productcode, packagecode.upcase)\n\n      expect(pkg.match?({:name => packagecode.downcase})).to be_truthy\n    end\n\n    it 'should match product codes case-insensitively' do\n      pkg = described_class.new(name, version, productcode.upcase, packagecode)\n\n      expect(pkg.match?({:name => productcode.downcase})).to be_truthy\n    end\n\n    it 'should match product name' do\n      pkg = described_class.new(name, version, productcode, packagecode)\n\n      expect(pkg.match?({:name => name})).to be_truthy\n    end\n\n    it 'should return false otherwise' do\n      pkg = described_class.new(name, version, productcode, packagecode)\n\n      expect(pkg.match?({:name => 'not going to find it'})).to be_falsey\n    end\n  end\n\n  context '#install_command' do\n    it 'should install using the source' do\n      cmd = described_class.install_command({:source => source})\n\n      expect(cmd).to eq(['msiexec.exe', '/qn', '/norestart', '/i', source])\n    end\n  end\n\n  context '#uninstall_command' do\n    it 'should uninstall using the productcode' do\n      pkg = described_class.new(name, version, productcode, packagecode)\n\n      expect(pkg.uninstall_command).to eq(['msiexec.exe', '/qn', '/norestart', '/x', productcode])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/windows/package_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/provider/package/windows/package'\n\ndescribe Puppet::Provider::Package::Windows::Package do\n  let(:hklm) { 'HKEY_LOCAL_MACHINE' }\n  let(:hkcu) { 'HKEY_CURRENT_USER' }\n  let(:path) { 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall' }\n  let(:key)  { double('key', :name => \"#{hklm}\\\\#{path}\\\\Google\") }\n  let(:package)  { double('package') }\n\n  context '::each' do\n    it 'should generate an empty enumeration' do\n      expect(described_class).to receive(:with_key)\n\n      expect(described_class.to_a).to be_empty\n    end\n\n    it 'should yield each package it finds' do\n      expect(described_class).to receive(:with_key).and_yield(key, {})\n\n      expect(Puppet::Provider::Package::Windows::MsiPackage).to receive(:from_registry).with('Google', {}).and_return(package)\n\n      yielded = nil\n      described_class.each do |pkg|\n        yielded = pkg\n      end\n\n      expect(yielded).to eq(package)\n    end\n  end\n\n  context '::with_key', :if => Puppet::Util::Platform.windows? do\n    it 'should search HKLM (64 & 32) and HKCU (64 & 32)' do\n      expect(described_class).to receive(:open).with(hklm, path, described_class::KEY64 | described_class::KEY_READ).ordered\n      expect(described_class).to receive(:open).with(hklm, path, described_class::KEY32 | described_class::KEY_READ).ordered\n      expect(described_class).to receive(:open).with(hkcu, path, described_class::KEY64 | described_class::KEY_READ).ordered\n      expect(described_class).to receive(:open).with(hkcu, path, described_class::KEY32 | described_class::KEY_READ).ordered\n\n      described_class.with_key { |key, values| }\n    end\n\n    it 'should ignore file not found exceptions' do\n      ex = Puppet::Util::Windows::Error.new('Failed to open registry key', Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND)\n\n      # make sure we don't stop after the first exception\n      expect(described_class).to receive(:open).exactly(4).times().and_raise(ex)\n\n      keys = []\n      described_class.with_key { |key, values| keys << key }\n      expect(keys).to be_empty\n    end\n\n    it 'should raise other types of exceptions' do\n      ex = Puppet::Util::Windows::Error.new('Failed to open registry key', Puppet::Util::Windows::Error::ERROR_ACCESS_DENIED)\n      expect(described_class).to receive(:open).and_raise(ex)\n\n      expect {\n        described_class.with_key{ |key, values| }\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(5) # ERROR_ACCESS_DENIED\n      end\n    end\n  end\n\n  context '::installer_class' do\n    it 'should require the source parameter' do\n      expect {\n        described_class.installer_class({})\n      }.to raise_error(Puppet::Error, /The source parameter is required when using the Windows provider./)\n    end\n\n    context 'MSI' do\n      let (:klass) { Puppet::Provider::Package::Windows::MsiPackage }\n\n      it 'should accept source ending in .msi' do\n        expect(described_class.installer_class({:source => 'foo.msi'})).to eq(klass)\n      end\n\n      it 'should accept quoted source ending in .msi' do\n        expect(described_class.installer_class({:source => '\"foo.msi\"'})).to eq(klass)\n      end\n\n      it 'should accept source case insensitively' do\n        expect(described_class.installer_class({:source => '\"foo.MSI\"'})).to eq(klass)\n      end\n\n      it 'should reject source containing msi in the name' do\n        expect {\n          described_class.installer_class({:source => 'mymsi.txt'})\n        }.to raise_error(Puppet::Error, /Don't know how to install 'mymsi.txt'/)\n      end\n    end\n\n    context 'Unknown' do\n      it 'should reject packages it does not know about' do\n        expect {\n          described_class.installer_class({:source => 'basram'})\n        }.to raise_error(Puppet::Error, /Don't know how to install 'basram'/)\n      end\n    end\n  end\n\n  context '::munge' do\n    it 'should shell quote strings with spaces and fix forward slashes' do\n      expect(described_class.munge('c:/windows/the thing')).to eq('\"c:\\windows\\the thing\"')\n    end\n    it 'should leave properly formatted paths alone' do\n      expect(described_class.munge('c:\\windows\\thething')).to eq('c:\\windows\\thething')\n    end\n  end\n\n  context '::replace_forward_slashes' do\n    it 'should replace forward with back slashes' do\n      expect(described_class.replace_forward_slashes('c:/windows/thing/stuff')).to eq('c:\\windows\\thing\\stuff')\n    end\n  end\n\n  context '::quote' do\n    it 'should shell quote strings with spaces' do\n      expect(described_class.quote('foo bar')).to eq('\"foo bar\"')\n    end\n\n    it 'should shell quote strings with spaces and quotes' do\n      expect(described_class.quote('\"foo bar\" baz')).to eq('\"\\\"foo bar\\\" baz\"')\n    end\n\n    it 'should not shell quote strings without spaces' do\n      expect(described_class.quote('\"foobar\"')).to eq('\"foobar\"')\n    end\n  end\n\n  context '::get_display_name' do\n    it 'should return nil if values is nil' do\n      expect(described_class.get_display_name(nil)).to be_nil\n    end\n\n    it 'should return empty if values is empty' do\n      reg_values =  {}\n      expect(described_class.get_display_name(reg_values)).to eq('')\n    end\n\n    it 'should return DisplayName when available' do\n      reg_values =  { 'DisplayName' => 'Google' }\n      expect(described_class.get_display_name(reg_values)).to eq('Google')\n    end\n\n    it 'should return DisplayName when available, even when QuietDisplayName is also available' do\n      reg_values =  { 'DisplayName' => 'Google', 'QuietDisplayName' => 'Google Quiet' }\n      expect(described_class.get_display_name(reg_values)).to eq('Google')\n    end\n\n    it 'should return QuietDisplayName when available if DisplayName is empty' do\n      reg_values =  { 'DisplayName' => '', 'QuietDisplayName' =>'Google Quiet' }\n      expect(described_class.get_display_name(reg_values)).to eq('Google Quiet')\n    end\n\n    it 'should return QuietDisplayName when DisplayName is not available' do\n      reg_values =  { 'QuietDisplayName' =>'Google Quiet' }\n      expect(described_class.get_display_name(reg_values)).to eq('Google Quiet')\n    end\n\n    it 'should return empty when DisplayName is empty and QuietDisplay name is not available' do\n      reg_values =  { 'DisplayName' => '' }\n      expect(described_class.get_display_name(reg_values)).to eq('')\n    end\n\n    it 'should return empty when DisplayName is empty and QuietDisplay name is empty' do\n      reg_values =  { 'DisplayName' => '', 'QuietDisplayName' =>'' }\n      expect(described_class.get_display_name(reg_values)).to eq('')\n    end\n  end\n\n  it 'should implement instance methods' do\n    pkg = described_class.new('orca', '5.0')\n\n    expect(pkg.name).to eq('orca')\n    expect(pkg.version).to eq('5.0')\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/windows_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:windows), :if => Puppet.features.microsoft_windows? do\n  let(:name)        { 'mysql-5.1.58-win-x64' }\n  let(:source)      { 'E:\\Rando\\Directory\\mysql-5.1.58-win-x64.msi' }\n  let(:resource)    {  Puppet::Type.type(:package).new(:name => name, :provider => :windows, :source => source) }\n  let(:provider)    { resource.provider }\n  let(:execute_options) do {:failonfail => false, :combine => true, :suppress_window => true} end\n\n  before(:each) do\n    # make sure we never try to execute anything\n    @times_execute_called = 0\n    allow(provider).to receive(:execute) { @times_execute_called += 1}\n  end\n\n  after(:each) do\n    expect(@times_execute_called).to eq(0)\n  end\n\n  def expect_execute(command, status)\n    expect(provider).to receive(:execute).with(command, execute_options).and_return(Puppet::Util::Execution::ProcessOutput.new('',status))\n  end\n\n  describe 'provider features' do\n    it { is_expected.to be_installable }\n    it { is_expected.to be_uninstallable }\n    it { is_expected.to be_install_options }\n    it { is_expected.to be_uninstall_options }\n    it { is_expected.to be_versionable }\n  end\n\n  describe 'on Windows', :if => Puppet::Util::Platform.windows? do\n    it 'should be the default provider' do\n      expect(Puppet::Type.type(:package).defaultprovider).to eq(subject.class)\n    end\n  end\n\n  context '::instances' do\n    it 'should return an array of provider instances' do\n      pkg1 = double('pkg1')\n      pkg2 = double('pkg2')\n\n      prov1 = double('prov1', :name => 'pkg1', :version => '1.0.0', :package => pkg1)\n      prov2 = double('prov2', :name => 'pkg2', :version => nil, :package => pkg2)\n\n      expect(Puppet::Provider::Package::Windows::Package).to receive(:map).and_yield(prov1).and_yield(prov2).and_return([prov1, prov2])\n\n      providers = provider.class.instances\n      expect(providers.count).to eq(2)\n      expect(providers[0].name).to eq('pkg1')\n      expect(providers[0].version).to eq('1.0.0')\n      expect(providers[0].package).to eq(pkg1)\n\n      expect(providers[1].name).to eq('pkg2')\n      expect(providers[1].version).to be_nil\n      expect(providers[1].package).to eq(pkg2)\n    end\n\n    it 'should return an empty array if none found' do\n      expect(Puppet::Provider::Package::Windows::Package).to receive(:map).and_return([])\n\n      expect(provider.class.instances).to eq([])\n    end\n  end\n\n  context '#query' do\n    it 'should return the hash of the matched packaged' do\n      pkg = double(:name => 'pkg1', :version => nil)\n      expect(pkg).to receive(:match?).and_return(true)\n      expect(Puppet::Provider::Package::Windows::Package).to receive(:find).and_yield(pkg)\n\n      expect(provider.query).to eq({ :name => 'pkg1', :ensure => :installed, :provider => :windows })\n    end\n\n    it 'should include the version string when present' do\n      pkg = double(:name => 'pkg1', :version => '1.0.0')\n      expect(pkg).to receive(:match?).and_return(true)\n      expect(Puppet::Provider::Package::Windows::Package).to receive(:find).and_yield(pkg)\n\n      expect(provider.query).to eq({ :name => 'pkg1', :ensure => '1.0.0', :provider => :windows })\n    end\n\n    it 'should return nil if no package was found' do\n      expect(Puppet::Provider::Package::Windows::Package).to receive(:find)\n\n      expect(provider.query).to be_nil\n    end\n  end\n\n  context '#install' do\n    let(:command) { 'blarg.exe /S' }\n    let(:klass) { double('installer', :install_command => ['blarg.exe', '/S'] ) }\n    let(:execute_options) do {:failonfail => false, :combine => true, :cwd => nil, :suppress_window => true} end\n    before :each do\n      expect(Puppet::Provider::Package::Windows::Package).to receive(:installer_class).and_return(klass)\n    end\n\n    it 'should join the install command and options' do\n      resource[:install_options] = { 'INSTALLDIR' => 'C:\\mysql-5.1' }\n\n      expect_execute(\"#{command} INSTALLDIR=C:\\\\mysql-5.1\", 0)\n\n      provider.install\n    end\n\n    it 'should compact nil install options' do\n      expect_execute(command, 0)\n\n      provider.install\n    end\n\n    it 'should not warn if the package install succeeds' do\n      expect_execute(command, 0)\n      expect(provider).not_to receive(:warning)\n\n      provider.install\n    end\n\n    it 'should warn if reboot initiated' do\n      expect_execute(command, 1641)\n      expect(provider).to receive(:warning).with('The package installed successfully and the system is rebooting now.')\n\n      provider.install\n    end\n\n    it 'should warn if reboot required' do\n      expect_execute(command, 3010)\n      expect(provider).to receive(:warning).with('The package installed successfully, but the system must be rebooted.')\n\n      provider.install\n    end\n\n    it 'should fail otherwise', :if => Puppet::Util::Platform.windows? do\n      expect_execute(command, 5)\n\n      expect do\n        provider.install\n      end.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(5) # ERROR_ACCESS_DENIED\n      end\n    end\n\n    context 'With a real working dir' do\n      let(:execute_options) do {:failonfail => false, :combine => true, :cwd => 'E:\\Rando\\Directory', :suppress_window => true} end\n\n      it 'should not try to set the working directory' do\n        expect(Puppet::FileSystem).to receive(:exist?).with('E:\\Rando\\Directory').and_return(true)\n        expect_execute(command, 0)\n\n        provider.install\n      end\n    end\n  end\n\n  context '#uninstall' do\n    let(:command) { 'unblarg.exe /Q' }\n    let(:package) { double('package', :uninstall_command => ['unblarg.exe', '/Q'] ) }\n\n    before :each do\n      resource[:ensure] = :absent\n      provider.package = package\n    end\n\n    it 'should join the uninstall command and options' do\n      resource[:uninstall_options] = { 'INSTALLDIR' => 'C:\\mysql-5.1' }\n      expect_execute(\"#{command} INSTALLDIR=C:\\\\mysql-5.1\", 0)\n\n      provider.uninstall\n    end\n\n    it 'should compact nil install options' do\n      expect_execute(command, 0)\n\n      provider.uninstall\n    end\n\n    it 'should not warn if the package install succeeds' do\n      expect_execute(command, 0)\n      expect(provider).not_to receive(:warning)\n\n      provider.uninstall\n    end\n\n    it 'should warn if reboot initiated' do\n      expect_execute(command, 1641)\n      expect(provider).to receive(:warning).with('The package uninstalled successfully and the system is rebooting now.')\n\n      provider.uninstall\n    end\n\n    it 'should warn if reboot required' do\n      expect_execute(command, 3010)\n      expect(provider).to receive(:warning).with('The package uninstalled successfully, but the system must be rebooted.')\n\n      provider.uninstall\n    end\n\n    it 'should fail otherwise', :if => Puppet::Util::Platform.windows? do\n      expect_execute(command, 5)\n\n      expect do\n        provider.uninstall\n      end.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(5) # ERROR_ACCESS_DENIED\n      end\n    end\n  end\n\n  context '#validate_source' do\n    it 'should fail if the source parameter is empty' do\n      expect do\n        resource[:source] = ''\n      end.to raise_error(Puppet::Error, /The source parameter cannot be empty when using the Windows provider/)\n    end\n\n    it 'should accept a source' do\n      resource[:source] = source\n    end\n  end\n\n  context '#install_options' do\n    it 'should return nil by default' do\n      expect(provider.install_options).to be_nil\n    end\n\n    it 'should return the options' do\n      resource[:install_options] = { 'INSTALLDIR' => 'C:\\mysql-here' }\n\n      expect(provider.install_options).to eq(['INSTALLDIR=C:\\mysql-here'])\n    end\n\n    it 'should only quote if needed' do\n      resource[:install_options] = { 'INSTALLDIR' => 'C:\\mysql here' }\n\n      expect(provider.install_options).to eq(['INSTALLDIR=\"C:\\mysql here\"'])\n    end\n\n    it 'should escape embedded quotes in install_options values with spaces' do\n      resource[:install_options] = { 'INSTALLDIR' => 'C:\\mysql \"here\"' }\n\n      expect(provider.install_options).to eq(['INSTALLDIR=\"C:\\mysql \\\"here\\\"\"'])\n    end\n  end\n\n  context '#uninstall_options' do\n    it 'should return nil by default' do\n      expect(provider.uninstall_options).to be_nil\n    end\n\n    it 'should return the options' do\n      resource[:uninstall_options] = { 'INSTALLDIR' => 'C:\\mysql-here' }\n\n      expect(provider.uninstall_options).to eq(['INSTALLDIR=C:\\mysql-here'])\n    end\n  end\n\n  context '#join_options' do\n    it 'should return nil if there are no options' do\n      expect(provider.join_options(nil)).to be_nil\n    end\n\n    it 'should sort hash keys' do\n      expect(provider.join_options([{'b' => '2', 'a' => '1', 'c' => '3'}])).to eq(['a=1', 'b=2', 'c=3'])\n    end\n\n    it 'should return strings and hashes' do\n      expect(provider.join_options([{'a' => '1'}, 'b'])).to eq(['a=1', 'b'])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/xbps_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"stringio\"\n\ndescribe Puppet::Type.type(:package).provider(:xbps) do\n  before do\n    @resource = Puppet::Type.type(:package).new(name: \"gcc\", provider: \"xbps\")\n    @provider = described_class.new(@resource)\n    @resolver = Puppet::Util\n\n    allow(described_class).to receive(:which).with(\"/usr/bin/xbps-install\").and_return(\"/usr/bin/xbps-install\")\n    allow(described_class).to receive(:which).with(\"/usr/bin/xbps-remove\").and_return(\"/usr/bin/xbps-remove\")\n    allow(described_class).to receive(:which).with(\"/usr/bin/xbps-query\").and_return(\"/usr/bin/xbps-query\")\n  end\n\n  it { is_expected.to be_installable }\n  it { is_expected.to be_uninstallable }\n  it { is_expected.to be_install_options }\n  it { is_expected.to be_uninstall_options }\n  it { is_expected.to be_upgradeable }\n  it { is_expected.to be_holdable }\n  it { is_expected.to be_virtual_packages }\n\n  it \"should be the default provider on 'os.name' => Void\" do\n    expect(Facter).to receive(:value).with('os.name').and_return(\"Void\")\n    expect(described_class.default?).to be_truthy\n  end\n\n  describe \"when determining instances\" do\n    it \"should return installed packages\" do\n      sample_installed_packages = %{\nii gcc-12.2.0_1                            GNU Compiler Collection\nii ruby-devel-3.1.3_1                      Ruby programming language - development files\n}\n\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/xbps-query\", \"-l\"])\n                                   .and_yield(StringIO.new(sample_installed_packages))\n\n      instances = described_class.instances\n      expect(instances.length).to eq(2)\n\n      expect(instances[0].properties).to eq({\n        :name => \"gcc\",\n        :ensure => \"12.2.0_1\",\n        :provider => :xbps,\n      })\n\n      expect(instances[1].properties).to eq({\n        :name => \"ruby-devel\",\n        :ensure => \"3.1.3_1\",\n        :provider => :xbps,\n      })\n    end\n\n    it \"should warn on invalid input\" do\n      expect(described_class).to receive(:execpipe).and_yield(StringIO.new(\"blah\"))\n      expect(described_class).to receive(:warning).with('Failed to match line \\'blah\\'')\n      expect(described_class.instances).to eq([])\n    end\n  end\n\n  describe \"when installing\" do\n    it \"and install_options are given it should call xbps to install the package quietly with the passed options\" do\n      @resource[:install_options] = [\"-x\", { \"--arg\" => \"value\" }]\n      args = [\"-S\", \"-y\", \"-x\", \"--arg=value\", @resource[:name]]\n      expect(@provider).to receive(:xbps_install).with(*args).and_return(\"\")\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/xbps-query\", \"-l\"])\n\n      @provider.install\n    end\n\n    it \"and source is given it should call xbps to install the package from the source as repository\" do\n      @resource[:source] = \"/path/to/xbps/containing/directory\"\n      args = [\"-S\", \"-y\", \"--repository=#{@resource[:source]}\", @resource[:name]]\n      expect(@provider).to receive(:xbps_install).at_least(:once).with(*args).and_return(\"\")\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/xbps-query\", \"-l\"])\n\n      @provider.install\n    end\n  end\n\n  describe \"when updating\" do\n    it \"should call install\" do\n      expect(@provider).to receive(:install).and_return(\"ran install\")\n      expect(@provider.update).to eq(\"ran install\")\n    end\n  end\n\n  describe \"when uninstalling\" do\n    it \"should call xbps to remove the right package quietly\" do\n      args = [\"-R\", \"-y\", @resource[:name]]\n      expect(@provider).to receive(:xbps_remove).with(*args).and_return(\"\")\n      @provider.uninstall\n    end\n\n    it \"adds any uninstall_options\" do\n      @resource[:uninstall_options] = [\"-x\", { \"--arg\" => \"value\" }]\n      args = [\"-R\", \"-y\", \"-x\", \"--arg=value\", @resource[:name]]\n      expect(@provider).to receive(:xbps_remove).with(*args).and_return(\"\")\n      @provider.uninstall\n    end\n  end\n\n  describe \"when determining the latest version\" do\n    it \"should return the latest version number of the package\" do\n      @resource[:name] = \"ruby-devel\"\n\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/xbps-query\", \"-l\"]).and_yield(StringIO.new(%{\nii ruby-devel-3.1.3_1                      Ruby programming language - development files\n}))\n\n      expect(@provider.latest).to eq(\"3.1.3_1\")\n    end\n  end\n\n  describe \"when querying\" do\n    it \"should call self.instances and return nil if the package is missing\" do\n      expect(described_class).to receive(:instances)\n                                   .and_return([])\n\n      expect(@provider.query).to be_nil\n    end\n\n    it \"should get real-package in case allow_virtual is true\" do\n      @resource[:name] = \"nodejs-runtime\"\n      @resource[:allow_virtual] = true\n\n      expect(described_class).to receive(:execpipe).with([\"/usr/bin/xbps-query\", \"-l\"])\n                                   .and_yield(StringIO.new(\"\"))\n\n      args = [\"-Rs\", @resource[:name]]\n      expect(@provider).to receive(:xbps_query).with(*args).and_return(%{\n[*] nodejs-16.19.0_1      Evented I/O for V8 javascript\n[-] nodejs-lts-12.22.10_2 Evented I/O for V8 javascript'\n})\n\n      expect(@provider.query).to eq({\n        :name => \"nodejs\",\n        :ensure => \"16.19.0_1\",\n        :provider => :xbps,\n      })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/yum_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:yum) do\n  include PuppetSpec::Fixtures\n\n  let(:resource_name) { 'myresource' }\n  let(:resource) do\n    Puppet::Type.type(:package).new(\n      :name     => resource_name,\n      :ensure   => :installed,\n      :provider => 'yum'\n    )\n  end\n\n  let(:provider) { Puppet::Type.type(:package).provider(:yum).new(resource) }\n\n  it_behaves_like 'RHEL package provider', described_class, 'yum'\n\n  it \"should have lower specificity\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n    allow(Facter).to receive(:value).with('os.name').and_return(:fedora)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"22\")\n    expect(described_class.specificity).to be < 200\n  end\n\n  describe \"should have logical defaults\" do\n    [2, 2018].each do |ver|\n      it \"should be the default provider on Amazon Linux #{ver}\" do\n        allow(Facter).to receive(:value).with('os.name').and_return('amazon')\n        allow(Facter).to receive(:value).with('os.family').and_return('redhat')\n        allow(Facter).to receive(:value).with('os.release.major').and_return(ver)\n        expect(described_class).to be_default\n      end\n    end\n\n    Array(4..7).each do |ver|\n      it \"should be default for redhat #{ver}\" do\n        allow(Facter).to receive(:value).with('os.name').and_return('redhat')\n        allow(Facter).to receive(:value).with('os.family').and_return('redhat')\n        allow(Facter).to receive(:value).with('os.release.major').and_return(ver.to_s)\n        expect(described_class).to be_default\n      end\n    end\n\n    it \"should not be default for redhat 8\" do\n      allow(Facter).to receive(:value).with('os.name').and_return('redhat')\n      allow(Facter).to receive(:value).with('os.family').and_return('redhat')\n      allow(Facter).to receive(:value).with('os.release.major').and_return('8')\n      expect(described_class).not_to be_default\n    end\n\n    it \"should not be default for Ubuntu 16.04\" do\n      allow(Facter).to receive(:value).with('os.name').and_return('ubuntu')\n      allow(Facter).to receive(:value).with('os.family').and_return('ubuntu')\n      allow(Facter).to receive(:value).with('os.release.major').and_return('16.04')\n      expect(described_class).not_to be_default\n    end\n  end\n\n  describe \"when supplied the source param\" do\n    let(:name) { 'baz' }\n\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name => name,\n        :provider => 'yum',\n      )\n    end\n\n    let(:provider) do\n      provider = described_class.new\n      provider.resource = resource\n      provider\n    end\n\n    before { allow(described_class).to receive(:command).with(:cmd).and_return(\"/usr/bin/yum\") }\n\n  describe 'provider features' do\n    it { is_expected.to be_versionable }\n    it { is_expected.to be_install_options }\n    it { is_expected.to be_virtual_packages }\n    it { is_expected.to be_install_only }\n  end\n\n    context \"when installing\" do\n      it \"should use the supplied source as the explicit path to a package to install\" do\n        resource[:ensure] = :present\n        resource[:source] = \"/foo/bar/baz-1.1.0.rpm\"\n        expect(provider).to receive(:execute) do |arr|\n          expect(arr[-2..-1]).to eq([:install, \"/foo/bar/baz-1.1.0.rpm\"])\n        end\n        provider.install\n      end\n    end\n\n    context \"when ensuring a specific version\" do\n      it \"should use the suppplied source as the explicit path to the package to update\" do\n        # The first query response informs yum provider that package 1.1.0 is\n        # already installed, and the second that it's been upgraded\n        expect(provider).to receive(:query).twice.and_return({:ensure => \"1.1.0\"}, {:ensure => \"1.2.0\"})\n        resource[:ensure] = \"1.2.0\"\n        resource[:source] = \"http://foo.repo.com/baz-1.2.0.rpm\"\n        expect(provider).to receive(:execute) do |arr|\n          expect(arr[-2..-1]).to eq(['update', \"http://foo.repo.com/baz-1.2.0.rpm\"])\n        end\n        provider.install\n      end\n    end\n\n    describe 'with install_options' do\n      it 'can parse disable-repo with array of strings' do\n          resource[:install_options] = ['--disable-repo=dev*', '--disable-repo=prod*']\n          expect(provider).to receive(:execute) do | arr|\n            expect(arr[-3]).to eq([\"--disable-repo=dev*\", \"--disable-repo=prod*\"])\n          end\n          provider.install\n      end\n\n      it 'can parse disable-repo with array of hashes' do\n        resource[:install_options] = [{'--disable-repo' => 'dev*'}, {'--disable-repo' => 'prod*'}]\n        expect(provider).to receive(:execute) do | arr|\n          expect(arr[-3]).to eq([\"--disable-repo=dev*\", \"--disable-repo=prod*\"])\n        end\n        provider.install\n      end\n\n      it 'can parse enable-repo with array of strings' do\n          resource[:install_options] = ['--enable-repo=dev*', '--enable-repo=prod*']\n          expect(provider).to receive(:execute) do | arr|\n            expect(arr[-3]).to eq([\"--enable-repo=dev*\", \"--enable-repo=prod*\"])\n          end\n          provider.install\n      end\n\n      it 'can parse enable-repo with array of hashes' do\n        resource[:install_options] = [{'--enable-repo' => 'dev*'}, {'--disable-repo' => 'prod*'}]\n        expect(provider).to receive(:execute) do | arr|\n          expect(arr[-3]).to eq([\"--enable-repo=dev*\", \"--disable-repo=prod*\"])\n        end\n        provider.install\n      end\n\n      it 'can parse enable-repo with single hash' do\n        resource[:install_options] = [{'--enable-repo' => 'dev*','--disable-repo' => 'prod*'}]\n        expect(provider).to receive(:execute) do | arr|\n          expect(arr[-3]).to eq([\"--disable-repo=prod*\", \"--enable-repo=dev*\"])\n        end\n        provider.install\n      end\n\n      it 'can parse enable-repo with empty array' do\n        resource[:install_options] = []\n        expect(provider).to receive(:execute) do | arr|\n          expect(arr[-3]).to eq([])\n        end\n        provider.install\n      end\n    end\n  end\n\n  context \"latest\" do\n    let(:name) { 'baz' }\n\n    let(:resource) do\n      Puppet::Type.type(:package).new(\n        :name => name,\n        :provider => 'yum',\n      )\n    end\n\n    let(:provider) do\n      provider = described_class.new\n      provider.resource = resource\n      provider\n    end\n\n    before {\n      allow(described_class).to receive(:command).with(:cmd).and_return(\"/usr/bin/yum\")\n      Puppet[:log_level] = 'debug'\n    }\n\n    it \"should print a debug message with the current version if newer package is not available\" do\n      expect(provider).to receive(:query).and_return({:ensure => \"1.2.3\"})\n      expect(described_class).to receive(:latest_package_version).and_return(nil)\n      resource[:ensure] = :present\n\n      provider.latest\n      expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Yum didn't find updates, current version (1.2.3) is the latest\"))\n    end\n  end\n\n  context \"parsing the output of check-update\" do\n    context \"with no multiline entries\" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-simple.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it 'creates an entry for each package keyed on the package name' do\n        expect(output['curl']).to eq([{:name => 'curl', :epoch => '0', :version => '7.32.0', :release => '10.fc20', :arch => 'i686'}, {:name => 'curl', :epoch => '0', :version => '7.32.0', :release => '10.fc20', :arch => 'x86_64'}])\n        expect(output['gawk']).to eq([{:name => 'gawk', :epoch => '0', :version => '4.1.0', :release => '3.fc20', :arch => 'i686'}])\n        expect(output['dhclient']).to eq([{:name => 'dhclient', :epoch => '12', :version => '4.1.1', :release => '38.P1.fc20', :arch => 'i686'}])\n        expect(output['selinux-policy']).to eq([{:name => 'selinux-policy', :epoch => '0', :version => '3.12.1', :release => '163.fc20', :arch => 'noarch'}])\n      end\n\n      it 'creates an entry for each package keyed on the package name and package architecture' do\n        expect(output['curl.i686']).to eq([{:name => 'curl', :epoch => '0', :version => '7.32.0', :release => '10.fc20', :arch => 'i686'}])\n        expect(output['curl.x86_64']).to eq([{:name => 'curl', :epoch => '0', :version => '7.32.0', :release => '10.fc20', :arch => 'x86_64'}])\n        expect(output['gawk.i686']).to eq([{:name => 'gawk', :epoch => '0', :version => '4.1.0', :release => '3.fc20', :arch => 'i686'}])\n        expect(output['dhclient.i686']).to eq([{:name => 'dhclient', :epoch => '12', :version => '4.1.1', :release => '38.P1.fc20', :arch => 'i686'}])\n        expect(output['selinux-policy.noarch']).to eq([{:name => 'selinux-policy', :epoch => '0', :version => '3.12.1', :release => '163.fc20', :arch => 'noarch'}])\n        expect(output['java-1.8.0-openjdk.x86_64']).to eq([{:name => 'java-1.8.0-openjdk', :epoch => '1', :version => '1.8.0.131', :release => '2.b11.el7_3', :arch => 'x86_64'}])\n      end\n    end\n\n    context \"with multiline entries\" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-multiline.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it \"parses multi-line values as a single package tuple\" do\n        expect(output['libpcap']).to eq([{:name => 'libpcap', :epoch => '14', :version => '1.4.0', :release => '1.20130826git2dbcaa1.el6', :arch => 'x86_64'}])\n      end\n    end\n\n    context \"with obsoleted packages\" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-obsoletes.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it \"ignores all entries including and after 'Obsoleting Packages'\" do\n        expect(output).not_to include(\"Obsoleting\")\n        expect(output).not_to include(\"NetworkManager-bluetooth.x86_64\")\n        expect(output).not_to include(\"1:1.0.0-14.git20150121.b4ea599c.el7\")\n      end\n    end\n\n    context \"with security notifications\" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-security.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it \"ignores all entries including and after 'Security'\" do\n        expect(output).not_to include(\"Security\")\n      end\n\n      it \"includes updates before 'Security'\" do\n        expect(output).to include(\"yum-plugin-fastestmirror.noarch\")\n      end\n    end\n\n    context \"with broken update notices\" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-broken-notices.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it \"ignores all entries including and after 'Update'\" do\n        expect(output).not_to include(\"Update\")\n      end\n\n      it \"includes updates before 'Update'\" do\n        expect(output).to include(\"yum-plugin-fastestmirror.noarch\")\n      end\n    end\n\n    context \"with improper package names in output\" do\n      it \"raises an exception parsing package name\" do\n        expect {\n          described_class.update_to_hash('badpackagename', '1')\n        }.to raise_exception(Exception, /Failed to parse/)\n      end\n    end\n\n    context \"with trailing plugin output\" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-plugin-output.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it \"parses correctly formatted entries\" do\n        expect(output['bash']).to eq([{:name => 'bash', :epoch => '0', :version => '4.2.46', :release => '12.el7', :arch => 'x86_64'}])\n      end\n\n      it \"ignores all mentions of plugin output\" do\n        expect(output).not_to include(\"Random plugin\")\n      end\n    end\n\n    context \"with subscription manager enabled \" do\n      let(:check_update) { File.read(my_fixture(\"yum-check-update-subscription-manager.txt\")) }\n      let(:output) { described_class.parse_updates(check_update) }\n\n      it \"parses correctly formatted entries\" do\n        expect(output['curl.x86_64']).to eq([{:name => 'curl', :epoch => '0', :version => '7.32.0', :release => '10.fc20', :arch => 'x86_64'}])\n      end\n    end\n  end\n\n  describe 'insync?' do\n    context 'when version is not a valid RPM version' do\n      let(:is) { '>===a:123' }\n\n      before do\n        resource[:ensure] = is\n      end\n\n      it 'logs a debug message' do\n        expect(Puppet).to receive(:debug).with(\"Cannot parse #{is} as a RPM version range\")\n        provider.insync?(is)\n      end\n    end\n\n    context 'with valid semantic versions' do\n      let(:is) { '1:1.2.3.4-5.el4' }\n\n      it 'returns true if the current version matches the given semantic version' do\n        resource[:ensure] = is\n        expect(provider).to be_insync(is)\n      end\n\n      it 'returns false if the current version does not match the given semantic version' do\n        resource[:ensure] = '999r'\n        expect(provider).not_to be_insync(is)\n      end\n\n      it 'no debug logs if the current version matches the given semantic version' do\n        resource[:ensure] = is\n        expect(Puppet).not_to receive(:debug)\n        provider.insync?(is)\n      end\n\n      it 'returns true if current version matches the greater or equal semantic version in ensure' do\n        resource[:ensure] = '<=1:1.2.3.4-5.el4'\n        expect(provider).to be_insync(is)\n      end\n\n      it 'returns true if current version matches the lesser semantic version in ensure' do\n        resource[:ensure] = '>1:1.0.0'\n        expect(provider).to be_insync(is)\n      end\n\n      it 'returns true if current version matches two semantic conditions' do\n        resource[:ensure] = '>1:1.1.3.4-5.el4 <1:1.3.3.6-5.el4'\n        expect(provider).to be_insync(is)\n      end\n\n      it 'returns false if current version does not match matches two semantic conditions' do\n        resource[:ensure] = '<1:1.1.3.4-5.el4 <1:1.3.3.6-5.el4'\n        expect(provider).not_to be_insync(is)\n      end\n    end\n  end\n\n  describe 'install' do\n    before do\n      resource[:ensure] = ensure_value\n      allow(Facter).to receive(:value).with('os.release.major').and_return('7')\n      allow(described_class).to receive(:command).with(:cmd).and_return('/usr/bin/yum')\n      allow(provider).to receive(:query).twice.and_return(nil, ensure: '18.3.2')\n      allow(provider).to receive(:insync?).with('18.3.2').and_return(true)\n    end\n\n    context 'with version range' do\n      before do\n        allow(provider).to receive(:available_versions).and_return(available_versions)\n      end\n\n      context 'without epoch' do\n        let(:ensure_value) { '>18.1 <19' }\n        let(:available_versions) { ['17.5.2', '18.0', 'a:23', '18.3', '18.3.2', '19.0', '3:18.4'] }\n\n        it 'selects best_version' do\n          expect(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, 'myresource-18.3.2']\n          )\n          provider.install\n        end\n\n        context 'when comparing with available packages that do not have epoch' do\n          let(:ensure_value) { '>18' }\n          let(:available_versions) { ['18.3.3', '3:18.3.2'] }\n\n          it 'treats no epoch as zero' do\n            expect(provider).to receive(:execute).with(\n              ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, 'myresource-18.3.2']\n              )\n            provider.install\n          end\n        end\n      end\n\n      context 'with epoch' do\n        let(:ensure_value) { '>18.1 <3:19' }\n        let(:available_versions) { ['3:17.5.2', '3:18.0', 'a:23', '18.3.3', '3:18.3.2', '3:19.0', '19.1'] }\n\n        it 'selects best_version and removes epoch' do\n          expect(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, 'myresource-18.3.2']\n          )\n          provider.install\n        end\n      end\n\n      context 'when no suitable version in range' do\n        let(:ensure_value) { '>18.1 <19' }\n        let(:available_versions) { ['3:17.5.2', '3:18.0', 'a:23' '18.3', '3:18.3.2', '3:19.0', '19.1'] }\n\n        it 'uses requested version' do\n          expect(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, \"myresource->18.1 <19\"]\n          )\n          provider.install\n        end\n\n        it 'logs a debug message' do\n          allow(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, \"myresource->18.1 <19\"]\n          )\n\n          expect(Puppet).to receive(:debug).with(\n            \"No available version for package myresource is included in range >18.1 <19\"\n          )\n          provider.install\n        end\n      end\n    end\n\n    context 'with fix version' do\n      let(:ensure_value) { '1:18.12' }\n\n      it 'passes the version to yum command' do\n        expect(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, \"myresource-1:18.12\"]\n          )\n          provider.install\n      end\n    end\n\n    context 'when upgrading' do\n      let(:ensure_value) { '>18.1 <19' }\n      let(:available_versions) { ['17.5.2', '18.0', 'a:23' '18.3', '18.3.2', '19.0', '3:18.4'] }\n\n      before do\n        allow(provider).to receive(:available_versions).and_return(available_versions)\n        allow(provider).to receive(:query).twice\n          .and_return({ ensure: '17.0' }, { ensure: '18.3.2' })\n      end\n\n      it 'adds update flag to install command' do\n        expect(provider).to receive(:execute).with(\n          ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', 'update', 'myresource-18.3.2']\n        )\n        provider.install\n      end\n    end\n\n    context 'when dowgrading' do\n      let(:ensure_value) { '>18.1 <19' }\n      let(:available_versions) { ['17.5.2', '18.0', 'a:23' '18.3', '18.3.2', '19.0', '3:18.4'] }\n\n      before do\n        allow(provider).to receive(:available_versions).and_return(available_versions)\n        allow(provider).to receive(:query).twice\n          .and_return({ ensure: '19.0' }, { ensure: '18.3.2' })\n      end\n\n      it 'adds downgrade flag to install command' do\n        expect(provider).to receive(:execute).with(\n          ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :downgrade, 'myresource-18.3.2']\n        )\n        provider.install\n      end\n    end\n\n    context 'on failure' do\n      let(:ensure_value) { '20' }\n\n      context 'when execute command fails' do\n        before do\n          allow(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, \"myresource-20\"]\n          ).and_return('No package myresource-20 available.')\n        end\n\n        it 'raises Puppet::Error' do\n          expect { provider.install }.to \\\n            raise_error(Puppet::Error, 'Could not find package myresource-20')\n        end\n      end\n\n      context 'when package is not found' do\n        before do\n          allow(provider).to receive(:query)\n          allow(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, \"myresource-20\"]\n          )\n        end\n\n        it 'raises Puppet::Error' do\n          expect { provider.install }.to \\\n            raise_error(Puppet::Error, 'Could not find package myresource')\n        end\n      end\n\n      context 'when package is not installed' do\n        before do\n          allow(provider).to receive(:execute).with(\n            ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', :install, \"myresource-20\"]\n          )\n          allow(provider).to receive(:insync?).and_return(false)\n        end\n\n        it 'raises Puppet::Error' do\n          expect { provider.install }.to \\\n            raise_error(Puppet::Error, 'Failed to update to version 20, got version 18.3.2 instead')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package/zypper_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package).provider(:zypper) do\n  before(:each) do\n    # Create a mock resource\n    @resource = double('resource')\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name and source\n    allow(@resource).to receive(:[]).with(:name).and_return(\"mypackage\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:installed)\n    allow(@resource).to receive(:command).with(:zypper).and_return(\"/usr/bin/zypper\")\n\n    @provider = described_class.new(@resource)\n  end\n\n  it \"should have an install method\" do\n    @provider = described_class.new\n    expect(@provider).to respond_to(:install)\n  end\n\n  it \"should have an uninstall method\" do\n    @provider = described_class.new\n    expect(@provider).to respond_to(:uninstall)\n  end\n\n  it \"should have an update method\" do\n    @provider = described_class.new\n    expect(@provider).to respond_to(:update)\n  end\n\n  it \"should have a latest method\" do\n    @provider = described_class.new\n    expect(@provider).to respond_to(:latest)\n  end\n\n  it \"should have a install_options method\" do\n    @provider = described_class.new\n    expect(@provider).to respond_to(:install_options)\n  end\n\n  context \"when installing with zypper version >= 1.0\" do\n    it \"should use a command-line with versioned package'\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3-4.5.6\")\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"1.2.8\")\n\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage-1.2.3-4.5.6')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n\n    it \"should use a command-line without versioned package\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:latest)\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"1.2.8\")\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', '--name', 'mypackage')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n  end\n\n  context \"when installing with zypper version = 0.6.104\" do\n    it \"should use a command-line with versioned package'\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3-4.5.6\")\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"0.6.104\")\n\n      expect(@provider).to receive(:zypper).with('--terse', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage-1.2.3-4.5.6')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n\n    it \"should use a command-line without versioned package\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:latest)\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"0.6.104\")\n      expect(@provider).to receive(:zypper).with('--terse', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n  end\n\n  context \"when installing with zypper version = 0.6.13\" do\n    it \"should use a command-line with versioned package'\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"1.2.3-4.5.6\")\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"0.6.13\")\n\n      expect(@provider).to receive(:zypper).with('--terse', :install, '--no-confirm', 'mypackage-1.2.3-4.5.6')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n\n    it \"should use a command-line without versioned package\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:latest)\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"0.6.13\")\n      expect(@provider).to receive(:zypper).with('--terse', :install, '--no-confirm', 'mypackage')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n  end\n\n  context \"when updating\" do\n    it \"should call install method of instance\" do\n      expect(@provider).to receive(:install)\n      @provider.update\n    end\n  end\n\n  context \"when getting latest version\" do\n    after { described_class.reset! }\n\n    context \"when the package has available update\" do\n      it \"should return a version string with valid list-updates data from SLES11sp1\" do\n        fake_data = File.read(my_fixture('zypper-list-updates-SLES11sp1.out'))\n        allow(@resource).to receive(:[]).with(:name).and_return(\"at\")\n        expect(described_class).to receive(:zypper).with(\"list-updates\").and_return(fake_data)\n        expect(@provider.latest).to eq(\"3.1.8-1069.18.2\")\n      end\n    end\n\n    context \"when the package is in the latest version\" do\n      it \"should return nil with valid list-updates data from SLES11sp1\" do\n        fake_data = File.read(my_fixture('zypper-list-updates-SLES11sp1.out'))\n        allow(@resource).to receive(:[]).with(:name).and_return(\"zypper-log\")\n        expect(described_class).to receive(:zypper).with(\"list-updates\").and_return(fake_data)\n        expect(@provider.latest).to eq(nil)\n      end\n    end\n\n    context \"when there are no updates available\" do\n      it \"should return nil\" do\n        fake_data_empty = File.read(my_fixture('zypper-list-updates-empty.out'))\n        allow(@resource).to receive(:[]).with(:name).and_return(\"at\")\n        expect(described_class).to receive(:zypper).with(\"list-updates\").and_return(fake_data_empty)\n        expect(@provider.latest).to eq(nil)\n      end\n    end\n  end\n\n  context \"should install a virtual package\" do\n    it \"when zypper version = 0.6.13\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:installed)\n      allow(@resource).to receive(:allow_virtual?).and_return(true)\n      allow(@provider).to receive(:zypper_version).and_return(\"0.6.13\")\n      expect(@provider).to receive(:zypper).with('--terse', :install, '--no-confirm', 'mypackage')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n\n    it \"when zypper version >= 1.0.0\" do\n      allow(@resource).to receive(:should).with(:ensure).and_return(:installed)\n      allow(@resource).to receive(:allow_virtual?).and_return(true)\n      allow(@provider).to receive(:zypper_version).and_return(\"1.2.8\")\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage')\n      expect(@provider).to receive(:query).and_return(\"mypackage 0 1.2.3 4.5.6 x86_64\")\n      @provider.install\n    end\n  end\n\n  context \"when installing with zypper install options\" do\n    it \"should install the package without checking keys\" do\n      allow(@resource).to receive(:[]).with(:name).and_return(\"php5\")\n      allow(@resource).to receive(:[]).with(:install_options).and_return(['--no-gpg-check', {'-p' => '/vagrant/files/localrepo/'}])\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"5.4.10-4.5.6\")\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"1.2.8\")\n\n      expect(@provider).to receive(:zypper).with('--quiet', '--no-gpg-check', :install,\n        '--auto-agree-with-licenses', '--no-confirm', '-p=/vagrant/files/localrepo/', 'php5-5.4.10-4.5.6')\n      expect(@provider).to receive(:query).and_return(\"php5 0 5.4.10 4.5.6 x86_64\")\n      @provider.install\n    end\n\n    it \"should install the package with --no-gpg-checks\" do\n      allow(@resource).to receive(:[]).with(:name).and_return(\"php5\")\n      allow(@resource).to receive(:[]).with(:install_options).and_return(['--no-gpg-checks', {'-p' => '/vagrant/files/localrepo/'}])\n      allow(@resource).to receive(:should).with(:ensure).and_return(\"5.4.10-4.5.6\")\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return(\"1.2.8\")\n\n      expect(@provider).to receive(:zypper).with('--quiet', '--no-gpg-checks', :install,\n        '--auto-agree-with-licenses', '--no-confirm', '-p=/vagrant/files/localrepo/', 'php5-5.4.10-4.5.6')\n      expect(@provider).to receive(:query).and_return(\"php5 0 5.4.10 4.5.6 x86_64\")\n      @provider.install\n    end\n\n    it \"should install package with hash install options\" do\n      allow(@resource).to receive(:[]).with(:name).and_return('vim')\n      allow(@resource).to receive(:[]).with(:install_options).and_return([{ '--a' => 'foo', '--b' => '\"quoted bar\"' }])\n      allow(@resource).to receive(:should).with(:ensure).and_return(:present)\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n\n      allow(@provider).to receive(:zypper_version).and_return('1.2.8')\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', '--a=foo', '--b=\"quoted bar\"', '--name', 'vim')\n      expect(@provider).to receive(:query).and_return('package vim is not installed')\n      @provider.install\n    end\n\n    it \"should install package with array install options\" do\n      allow(@resource).to receive(:[]).with(:name).and_return('vim')\n      allow(@resource).to receive(:[]).with(:install_options).and_return([['--a', '--b', '--c']])\n      allow(@resource).to receive(:should).with(:ensure).and_return(:present)\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n\n      allow(@provider).to receive(:zypper_version).and_return('1.2.8')\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', '--a', '--b', '--c', '--name', 'vim')\n      expect(@provider).to receive(:query).and_return('package vim is not installed')\n      @provider.install\n    end\n\n    it \"should install package with string install options\" do\n      allow(@resource).to receive(:[]).with(:name).and_return('vim')\n      allow(@resource).to receive(:[]).with(:install_options).and_return(['--a --b --c'])\n      allow(@resource).to receive(:should).with(:ensure).and_return(:present)\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n\n      allow(@provider).to receive(:zypper_version).and_return('1.2.8')\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', '--a --b --c', '--name', 'vim')\n      expect(@provider).to receive(:query).and_return('package vim is not installed')\n      @provider.install\n    end\n  end\n\n  context 'when uninstalling' do\n    it 'should use remove to uninstall on zypper version 1.6 and above' do\n      allow(@provider).to receive(:zypper_version).and_return('1.6.308')\n      expect(@provider).to receive(:zypper).with(:remove, '--no-confirm', 'mypackage')\n      @provider.uninstall\n    end\n\n    it 'should use remove  --force-solution to uninstall on zypper versions between 1.0 and 1.6' do\n      allow(@provider).to receive(:zypper_version).and_return('1.0.2')\n      expect(@provider).to receive(:zypper).with(:remove, '--no-confirm', '--force-resolution', 'mypackage')\n      @provider.uninstall\n    end\n  end\n\n  context 'when installing with VersionRange' do\n    let(:search_output) { File.read(my_fixture('zypper-search-uninstalled.out')) }\n\n    before(:each) do\n      allow(@resource).to receive(:[]).with(:name).and_return('vim')\n      allow(@resource).to receive(:allow_virtual?).and_return(false)\n      allow(@provider).to receive(:zypper_version).and_return('1.0.2')\n\n      expect(@provider).to receive(:zypper).with('search', '--match-exact', '--type', 'package', '--uninstalled-only', '-s', 'vim')\n                                           .and_return(search_output)\n    end\n\n    it 'does install the package if version is available' do\n      expect(@resource).to receive(:should).with(:ensure).and_return('>1.0')\n\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', 'vim-1.0.20040813-19.9')\n      expect(@provider).to receive(:query).and_return('vim 0 1.0.20040813 19.9 x86_64')\n\n      @provider.install\n    end\n\n    it 'does consider range as version if version in range is not available' do\n      allow(@resource).to receive(:should).with(:ensure).and_return('>2.0')\n\n      expect(@provider).to receive(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', 'vim->2.0')\n                             .and_raise(Puppet::ExecutionFailure.new('My Error'))\n\n      expect { @provider.install }.to raise_error(Puppet::ExecutionFailure, 'My Error')\n    end\n  end\n\n  describe 'insync?' do\n    subject { @provider.insync?('1.19-2') }\n\n    context 'when versions are matching' do\n      before { allow(@resource).to receive(:[]).with(:ensure).and_return('1.19-2') }\n\n      it { is_expected.to be true }\n    end\n\n    context 'when version are not matching' do\n      before { allow(@resource).to receive(:[]).with(:ensure).and_return('1.19-3') }\n\n      it { is_expected.to be false }\n    end\n\n    context 'when version is in gt range' do\n      before { allow(@resource).to receive(:[]).with(:ensure).and_return('>1.19-0') }\n\n      it { is_expected.to be true }\n    end\n\n    context 'when version is not in gt range' do\n      before { allow(@resource).to receive(:[]).with(:ensure).and_return('>1.19-2') }\n\n      it { is_expected.to be false }\n    end\n\n    context 'when version is in min-max range' do\n      before { allow(@resource).to receive(:[]).with(:ensure).and_return('>1.19-0 <1.19-3') }\n\n      it { is_expected.to be true }\n    end\n\n    context 'when version is not in min-max range' do\n      before { allow(@resource).to receive(:[]).with(:ensure).and_return('>1.19-0 <1.19-2') }\n\n      it { is_expected.to be false }\n    end\n\n    context 'when using eq range' do\n      context 'when ensure without release' do\n        before { allow(@resource).to receive(:[]).with(:ensure).and_return('1.19') }\n\n        it { is_expected.to be true }\n      end\n\n      context 'when ensure with release' do\n        before { allow(@resource).to receive(:[]).with(:ensure).and_return('1.19-2') }\n\n        it { is_expected.to be true }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/package_targetable_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet'\nrequire 'puppet/provider/package_targetable'\nrequire 'puppet/provider/package/gem'\n\ndescribe Puppet::Provider::Package::Targetable do\n  let(:provider) { Puppet::Type.type(:package).provider(:gem) }\n  let(:command)  { '/opt/bin/gem' }\n\n  describe \"when prefetching\" do\n    context \"with a package without a command attribute\" do\n      let(:resource) { Puppet::Type.type(:package).new(:name => 'noo', :provider => 'gem', :ensure => :present) }\n      let(:catalog)  { Puppet::Resource::Catalog.new }\n      let(:instance) { provider.new(resource) }\n      let(:packages) { { 'noo' => resource } }\n\n      it \"should pass a command to the instances method of the provider\" do\n        catalog.add_resource(resource)\n        expect(provider).to receive(:instances).with(nil).and_return([instance])\n        expect(provider.prefetch(packages)).to eq([nil]) # prefetch arbitrarily returns the array of commands for a provider in the catalog\n      end\n    end\n\n    context \"with a package with a command attribute\" do\n      let(:resource) { Puppet::Type.type(:package).new(:name => 'noo', :provider => 'gem', :ensure => :present) }\n      let(:resource_targeted) { Puppet::Type.type(:package).new(:name => 'yes', :provider => 'gem', :command => command, :ensure => :present) }\n      let(:catalog)  { Puppet::Resource::Catalog.new }\n      let(:instance) { provider.new(resource) }\n      let(:instance_targeted) { provider.new(resource_targeted) }\n      let(:packages) { { 'noo' => resource, 'yes' => resource_targeted } }\n\n      it \"should pass the command to the instances method of the provider\" do\n        catalog.add_resource(resource)\n        catalog.add_resource(resource_targeted)\n        expect(provider).to receive(:instances).with(nil).and_return([instance])\n        expect(provider).to receive(:instances).with(command).and_return([instance_targeted]).once\n        expect(provider.prefetch(packages)).to eq([nil, command]) # prefetch arbitrarily returns the array of commands for a provider in the catalog\n      end\n    end\n  end\n\n  describe \"when validating a command\" do\n    context \"with no command\" do\n      it \"report not functional\" do\n        expect { provider.validate_command(nil) }.to raise_error(Puppet::Error, \"Provider gem package command is not functional on this host\")\n      end\n    end\n    context \"with a missing command\" do\n      it \"report does not exist\" do\n        expect { provider.validate_command(command) }.to raise_error(Puppet::Error, \"Provider gem package command '#{command}' does not exist on this host\")\n      end\n    end\n    context \"with an existing command\" do\n      it \"validates\" do\n        allow(File).to receive(:file?).with(command).and_return(true)\n        expect { provider.validate_command(command) }.not_to raise_error\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/parsedfile_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\nrequire 'puppet'\nrequire 'puppet/provider/parsedfile'\n\nPuppet::Type.newtype(:parsedfile_type) do\n  newparam(:name)\n  newproperty(:target)\nend\n\n# Most of the tests for this are still in test/ral/provider/parsedfile.rb.\ndescribe Puppet::Provider::ParsedFile do\n  # The ParsedFile provider class is meant to be used as an abstract base class\n  # but also stores a lot of state within the singleton class. To avoid\n  # sharing data between classes we construct an anonymous class that inherits\n  # the ParsedFile provider instead of directly working with the ParsedFile\n  # provider itself.\n  let(:parsed_type) do\n    Puppet::Type.type(:parsedfile_type)\n  end\n\n  let!(:provider) { parsed_type.provide(:parsedfile_provider, :parent => described_class) }\n\n  describe \"when looking up records loaded from disk\" do\n    it \"should return nil if no records have been loaded\" do\n      expect(provider.record?(\"foo\")).to be_nil\n    end\n  end\n\n  describe \"when generating a list of instances\" do\n    it \"should return an instance for each record parsed from all of the registered targets\" do\n      expect(provider).to receive(:targets).and_return(%w{/one /two})\n      allow(provider).to receive(:skip_record?).and_return(false)\n      one = [:uno1, :uno2]\n      two = [:dos1, :dos2]\n      expect(provider).to receive(:prefetch_target).with(\"/one\").and_return(one)\n      expect(provider).to receive(:prefetch_target).with(\"/two\").and_return(two)\n\n      results = []\n      (one + two).each do |inst|\n        results << inst.to_s + \"_instance\"\n        expect(provider).to receive(:new).with(inst).and_return(results[-1])\n      end\n\n      expect(provider.instances).to eq(results)\n    end\n\n    it \"should ignore target when retrieve fails\" do\n      expect(provider).to receive(:targets).and_return(%w{/one /two /three})\n      allow(provider).to receive(:skip_record?).and_return(false)\n      expect(provider).to receive(:retrieve).with(\"/one\").and_return([\n        {:name => 'target1_record1'},\n        {:name => 'target1_record2'}\n      ])\n      expect(provider).to receive(:retrieve).with(\"/two\").and_raise(Puppet::Util::FileType::FileReadError, \"some error\")\n      expect(provider).to receive(:retrieve).with(\"/three\").and_return([\n        {:name => 'target3_record1'},\n        {:name => 'target3_record2'}\n      ])\n      expect(Puppet).to receive(:err).with('Could not prefetch parsedfile_type provider \\'parsedfile_provider\\' target \\'/two\\': some error. Treating as empty')\n      expect(provider).to receive(:new).with({:name => 'target1_record1', :on_disk => true, :target => '/one', :ensure => :present}).and_return('r1')\n      expect(provider).to receive(:new).with({:name => 'target1_record2', :on_disk => true, :target => '/one', :ensure => :present}).and_return('r2')\n      expect(provider).to receive(:new).with({:name => 'target3_record1', :on_disk => true, :target => '/three', :ensure => :present}).and_return('r3')\n      expect(provider).to receive(:new).with({:name => 'target3_record2', :on_disk => true, :target => '/three', :ensure => :present}).and_return('r4')\n\n      expect(provider.instances).to eq(%w{r1 r2 r3 r4})\n    end\n\n    it \"should skip specified records\" do\n      expect(provider).to receive(:targets).and_return(%w{/one})\n      expect(provider).to receive(:skip_record?).with(:uno).and_return(false)\n      expect(provider).to receive(:skip_record?).with(:dos).and_return(true)\n      one = [:uno, :dos]\n      expect(provider).to receive(:prefetch_target).and_return(one)\n\n      expect(provider).to receive(:new).with(:uno).and_return(\"eh\")\n      expect(provider).not_to receive(:new).with(:dos)\n\n      provider.instances\n    end\n\n    it \"should raise if parsing returns nil\" do\n      expect(provider).to receive(:targets).and_return(%w{/one})\n      expect_any_instance_of(Puppet::Util::FileType::FileTypeFlat).to receive(:read).and_return('a=b')\n      expect(provider).to receive(:parse).and_return(nil)\n\n      expect {\n        provider.instances\n      }.to raise_error(Puppet::DevError, %r{Prefetching /one for provider parsedfile_provider returned nil})\n    end\n  end\n\n  describe \"when matching resources to existing records\" do\n    let(:first_resource) { double(:one, :name => :one) }\n    let(:second_resource) { double(:two, :name => :two) }\n\n    let(:resources) {{:one => first_resource, :two => second_resource}}\n\n    it \"returns a resource if the record name matches the resource name\" do\n      record = {:name => :one}\n      expect(provider.resource_for_record(record, resources)).to be first_resource\n    end\n\n    it \"doesn't return a resource if the record name doesn't match any resource names\" do\n      record = {:name => :three}\n      expect(provider.resource_for_record(record, resources)).to be_nil\n    end\n  end\n\n  describe \"when flushing a file's records to disk\" do\n    before do\n      # This way we start with some @records, like we would in real life.\n      allow(provider).to receive(:retrieve).and_return([])\n      provider.default_target = \"/foo/bar\"\n      provider.initvars\n      provider.prefetch\n\n      @filetype = Puppet::Util::FileType.filetype(:flat).new(\"/my/file\")\n      allow(Puppet::Util::FileType.filetype(:flat)).to receive(:new).with(\"/my/file\").and_return(@filetype)\n\n      allow(@filetype).to receive(:write)\n    end\n\n    it \"should back up the file being written if the filetype can be backed up\" do\n      expect(@filetype).to receive(:backup)\n\n      provider.flush_target(\"/my/file\")\n    end\n\n    it \"should not try to back up the file if the filetype cannot be backed up\" do\n      @filetype = Puppet::Util::FileType.filetype(:ram).new(\"/my/file\")\n      expect(Puppet::Util::FileType.filetype(:flat)).to receive(:new).and_return(@filetype)\n\n      allow(@filetype).to receive(:write)\n\n      provider.flush_target(\"/my/file\")\n    end\n\n    it \"should not back up the file more than once between calls to 'prefetch'\" do\n      expect(@filetype).to receive(:backup).once\n\n      provider.flush_target(\"/my/file\")\n      provider.flush_target(\"/my/file\")\n    end\n\n    it \"should back the file up again once the file has been reread\" do\n      expect(@filetype).to receive(:backup).twice\n\n      provider.flush_target(\"/my/file\")\n      provider.prefetch\n      provider.flush_target(\"/my/file\")\n    end\n  end\n\n  describe \"when flushing multiple files\" do\n    describe \"and an error is encountered\" do\n      it \"the other file does not fail\" do\n        allow(provider).to receive(:backup_target)\n\n        bad_file = 'broken'\n        good_file = 'writable'\n\n        bad_writer = double('bad')\n        expect(bad_writer).to receive(:write).and_raise(Exception, \"Failed to write to bad file\")\n\n        good_writer = double('good')\n        expect(good_writer).to receive(:write).and_return(nil)\n\n        allow(provider).to receive(:target_object).with(bad_file).and_return(bad_writer)\n        allow(provider).to receive(:target_object).with(good_file).and_return(good_writer)\n\n        bad_resource = parsed_type.new(:name => 'one', :target => bad_file)\n        good_resource = parsed_type.new(:name => 'two', :target => good_file)\n\n        expect {\n          bad_resource.flush\n        }.to raise_error(Exception, \"Failed to write to bad file\")\n\n        good_resource.flush\n      end\n    end\n  end\nend\n\ndescribe \"A very basic provider based on ParsedFile\" do\n  include PuppetSpec::Files\n\n  let(:input_text) { File.read(my_fixture('simple.txt')) }\n  let(:target) { tmpfile('parsedfile_spec') }\n\n  let(:provider) do\n    example_provider_class = Class.new(Puppet::Provider::ParsedFile)\n    example_provider_class.default_target = target\n    # Setup some record rules\n    example_provider_class.instance_eval do\n      text_line :text, :match => %r{.}\n    end\n    example_provider_class.initvars\n    example_provider_class.prefetch\n    # evade a race between multiple invocations of the header method\n    allow(example_provider_class).to receive(:header).\n      and_return(\"# HEADER As added by puppet.\\n\")\n    example_provider_class\n  end\n\n  context \"writing file contents back to disk\" do\n    it \"should not change anything except from adding a header\" do\n      input_records = provider.parse(input_text)\n      expect(provider.to_file(input_records)).\n        to match provider.header + input_text\n    end\n  end\n\n  context \"rewriting a file containing a native header\" do\n    let(:regex) { %r/^# HEADER.*third party\\.\\n/ }\n    let(:input_records) { provider.parse(input_text) }\n\n    before :each do\n      allow(provider).to receive(:native_header_regex).and_return(regex)\n    end\n\n    it \"should move the native header to the top\" do\n      expect(provider.to_file(input_records)).not_to match(/\\A#{provider.header}/)\n    end\n\n    context \"and dropping native headers found in input\" do\n      before :each do\n        allow(provider).to receive(:drop_native_header).and_return(true)\n      end\n\n      it \"should not include the native header in the output\" do\n        expect(provider.to_file(input_records)).not_to match regex\n      end\n    end\n  end\n\n  context 'parsing a record type' do\n    let(:input_text) { File.read(my_fixture('aliases.txt')) }\n    let(:target) { tmpfile('parsedfile_spec') }\n    let(:provider) do\n      example_provider_class = Class.new(Puppet::Provider::ParsedFile)\n      example_provider_class.default_target = target\n      # Setup some record rules\n      example_provider_class.instance_eval do\n        record_line :aliases, :fields =>  %w{manager alias}, :separator => ':'\n      end\n      example_provider_class.initvars\n      example_provider_class.prefetch\n      example_provider_class\n    end\n    let(:expected_result) do\n      [\n        {:manager=>\"manager\", :alias=>\"  root\", :record_type=>:aliases},\n        {:manager=>\"dumper\", :alias=>\"   postmaster\", :record_type=>:aliases}\n      ]\n    end\n\n    subject { provider.parse(input_text) }\n\n    it { is_expected.to  match_array(expected_result) }\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/base_spec.rb",
    "content": "require 'spec_helper'\nrequire 'rbconfig'\nrequire 'fileutils'\n\ndescribe \"base service provider\" do\n  include PuppetSpec::Files\n\n  let :type do Puppet::Type.type(:service) end\n  let :provider do type.provider(:base) end\n  let(:executor) { Puppet::Util::Execution }\n  let(:start_command) { 'start' }\n  let(:status_command) { 'status' }\n  let(:stop_command) { 'stop' }\n\n  subject { provider }\n\n  context \"basic operations\" do\n    subject do\n      type.new(\n         :name  => \"test\",\n         :provider => :base,\n         :start  => start_command,\n         :status => status_command,\n         :stop   => stop_command\n      ).provider\n    end\n\n    def execute_command(command, options)\n      case command.shift\n      when start_command\n        expect(options[:failonfail]).to eq(true)\n        raise(Puppet::ExecutionFailure, 'failed to start') if @running\n        @running = true\n        Puppet::Util::Execution::ProcessOutput.new('started', 0)\n      when status_command\n        expect(options[:failonfail]).to eq(false)\n        if @running\n          Puppet::Util::Execution::ProcessOutput.new('running', 0)\n        else\n          Puppet::Util::Execution::ProcessOutput.new('not running', 1)\n        end\n      when stop_command\n        expect(options[:failonfail]).to eq(true)\n        raise(Puppet::ExecutionFailure, 'failed to stop') unless @running\n        @running = false\n        Puppet::Util::Execution::ProcessOutput.new('stopped', 0)\n      else\n        raise \"unexpected command execution: #{command}\"\n      end\n    end\n\n    before :each do\n      @running = false\n      expect(executor).to receive(:execute).at_least(:once) { |command, options| execute_command(command, options) }\n    end\n\n    it \"should invoke the start command if not running\" do\n      subject.start\n    end\n\n    it \"should be stopped before being started\" do\n      expect(subject.status).to eq(:stopped)\n    end\n\n    it \"should be running after being started\" do\n      subject.start\n      expect(subject.status).to eq(:running)\n    end\n\n    it \"should invoke the stop command when asked\" do\n      subject.start\n      expect(subject.status).to eq(:running)\n      subject.stop\n      expect(subject.status).to eq(:stopped)\n    end\n\n    it \"should raise an error if started twice\" do\n      subject.start\n      expect {subject.start }.to raise_error(Puppet::Error, 'Could not start Service[test]: failed to start')\n    end\n\n    it \"should raise an error if stopped twice\" do\n      subject.start\n      subject.stop\n      expect {subject.stop }.to raise_error(Puppet::Error, 'Could not stop Service[test]: failed to stop')\n    end\n  end\n\n  context \"when hasstatus is false\" do\n    subject do\n      type.new(\n         :name  => \"status test\",\n         :provider => :base,\n         :hasstatus => false,\n         :pattern => \"majestik m\\u00f8\\u00f8se\",\n      ).provider\n    end\n\n    it \"retrieves a PID from the process table\" do\n      allow(Facter).to receive(:value).and_call_original\n      allow(Facter).to receive(:value).with('os.name').and_return(\"CentOS\")\n      ps_output = File.binread(my_fixture(\"ps_ef.mixed_encoding\")).force_encoding(Encoding::UTF_8)\n\n      expect(executor).to receive(:execute).with(\"ps -ef\").and_return(ps_output)\n\n      expect(subject.status).to eq(:running)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/bsd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Bsd',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:bsd) }\n\n  before :each do\n    allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class)\n    allow(Facter).to receive(:value).with('os.name').and_return(:netbsd)\n    allow(Facter).to receive(:value).with('os.family').and_return('NetBSD')\n    allow(provider_class).to receive(:defpath).and_return('/etc/rc.conf.d')\n    @provider = provider_class.new\n    allow(@provider).to receive(:initscript)\n  end\n\n  context \"#instances\" do\n    it \"should have an instances method\" do\n      expect(provider_class).to respond_to :instances\n    end\n\n    it \"should use defpath\" do\n      expect(provider_class.instances).to be_all { |provider| provider.get(:path) == provider_class.defpath }\n    end\n  end\n\n  context \"#disable\" do\n    it \"should have a disable method\" do\n      expect(@provider).to respond_to(:disable)\n    end\n\n    it \"should remove a service file to disable\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.d/sshd').and_return(true)\n      expect(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.d/sshd').and_return(true)\n      allow(File).to receive(:delete).with('/etc/rc.conf.d/sshd')\n      provider.disable\n    end\n\n    it \"should not remove a service file if it doesn't exist\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(File).to receive(:exist?).with('/etc/rc.conf.d/sshd').and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.d/sshd').and_return(false)\n      provider.disable\n    end\n  end\n\n  context \"#enable\" do\n    it \"should have an enable method\" do\n      expect(@provider).to respond_to(:enable)\n    end\n\n    it \"should set the proper contents to enable\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Dir).to receive(:mkdir).with('/etc/rc.conf.d')\n      fh = double('fh')\n      allow(File).to receive(:open).with('/etc/rc.conf.d/sshd', File::WRONLY | File::APPEND | File::CREAT, 0644).and_yield(fh)\n      expect(fh).to receive(:<<).with(\"sshd_enable=\\\"YES\\\"\\n\")\n      provider.enable\n    end\n\n    it \"should set the proper contents to enable when disabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Dir).to receive(:mkdir).with('/etc/rc.conf.d')\n      allow(File).to receive(:read).with('/etc/rc.conf.d/sshd').and_return(\"sshd_enable=\\\"NO\\\"\\n\")\n      fh = double('fh')\n      allow(File).to receive(:open).with('/etc/rc.conf.d/sshd', File::WRONLY | File::APPEND | File::CREAT, 0644).and_yield(fh)\n      expect(fh).to receive(:<<).with(\"sshd_enable=\\\"YES\\\"\\n\")\n      provider.enable\n    end\n  end\n\n  context \"#enabled?\" do\n    it \"should have an enabled? method\" do\n      expect(@provider).to respond_to(:enabled?)\n    end\n\n    it \"should return false if the service file does not exist\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.d/sshd').and_return(false)\n      expect(provider.enabled?).to eq(:false)\n    end\n\n    it \"should return true if the service file exists\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.d/sshd').and_return(true)\n      expect(provider.enabled?).to eq(:true)\n    end\n  end\n\n  context \"#startcmd\" do\n    it \"should have a startcmd method\" do\n      expect(@provider).to respond_to(:startcmd)\n    end\n\n    it \"should use the supplied start command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n\n    it \"should start the serviced directly otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/etc/rc.d/sshd', :onestart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:search).with('sshd').and_return('/etc/rc.d/sshd')\n      provider.start\n    end\n  end\n\n  context \"#stopcmd\" do\n    it \"should have a stopcmd method\" do\n      expect(@provider).to respond_to(:stopcmd)\n    end\n\n    it \"should use the supplied stop command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n\n    it \"should stop the serviced directly otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/etc/rc.d/sshd', :onestop], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:search).with('sshd').and_return('/etc/rc.d/sshd')\n      provider.stop\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/daemontools_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Daemontools',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:daemontools) }\n\n  before(:each) do\n    # Create a mock resource\n    @resource = double('resource')\n\n    @provider = provider_class.new\n    @servicedir = \"/etc/service\"\n    @provider.servicedir=@servicedir\n    @daemondir = \"/var/lib/service\"\n    @provider.class.defpath=@daemondir\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name, source and path (because we won't run\n    # the thing that will fetch the resource path from the provider)\n    allow(@resource).to receive(:[]).with(:name).and_return(\"myservice\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:enabled)\n    allow(@resource).to receive(:[]).with(:path).and_return(@daemondir)\n    allow(@resource).to receive(:ref).and_return(\"Service[myservice]\")\n\n    @provider.resource = @resource\n\n    allow(@provider).to receive(:command).with(:svc).and_return(\"svc\")\n    allow(@provider).to receive(:command).with(:svstat).and_return(\"svstat\")\n\n    allow(@provider).to receive(:svc)\n    allow(@provider).to receive(:svstat)\n  end\n\n  it \"should have a restart method\" do\n    expect(@provider).to respond_to(:restart)\n  end\n\n  it \"should have a start method\" do\n    expect(@provider).to respond_to(:start)\n  end\n\n  it \"should have a stop method\" do\n    expect(@provider).to respond_to(:stop)\n  end\n\n  it \"should have an enabled? method\" do\n    expect(@provider).to respond_to(:enabled?)\n  end\n\n  it \"should have an enable method\" do\n    expect(@provider).to respond_to(:enable)\n  end\n\n  it \"should have a disable method\" do\n    expect(@provider).to respond_to(:disable)\n  end\n\n  context \"when starting\" do\n    it \"should use 'svc' to start the service\" do\n      allow(@provider).to receive(:enabled?).and_return(:true)\n      expect(@provider).to receive(:svc).with(\"-u\", \"/etc/service/myservice\")\n\n      @provider.start\n    end\n\n    it \"should enable the service if it is not enabled\" do\n      allow(@provider).to receive(:svc)\n\n      expect(@provider).to receive(:enabled?).and_return(:false)\n      expect(@provider).to receive(:enable)\n\n      @provider.start\n    end\n  end\n\n  context \"when stopping\" do\n    it \"should use 'svc' to stop the service\" do\n      allow(@provider).to receive(:disable)\n      expect(@provider).to receive(:svc).with(\"-d\", \"/etc/service/myservice\")\n\n      @provider.stop\n    end\n  end\n\n  context \"when restarting\" do\n    it \"should use 'svc' to restart the service\" do\n      expect(@provider).to receive(:svc).with(\"-t\", \"/etc/service/myservice\")\n\n      @provider.restart\n    end\n  end\n\n  context \"when enabling\" do\n    it \"should create a symlink between daemon dir and service dir\", :if => Puppet.features.manages_symlinks?  do\n      daemon_path = File.join(@daemondir, \"myservice\")\n      service_path = File.join(@servicedir, \"myservice\")\n      expect(Puppet::FileSystem).to receive(:symlink?).with(service_path).and_return(false)\n      expect(Puppet::FileSystem).to receive(:symlink).with(daemon_path, service_path).and_return(0)\n\n      @provider.enable\n    end\n  end\n\n  context \"when disabling\" do\n    it \"should remove the symlink between daemon dir and service dir\" do\n      allow(FileTest).to receive(:directory?).and_return(false)\n      path = File.join(@servicedir,\"myservice\")\n      expect(Puppet::FileSystem).to receive(:symlink?).with(path).and_return(true)\n      expect(Puppet::FileSystem).to receive(:unlink).with(path)\n      allow(@provider).to receive(:execute).and_return(\"\")\n      @provider.disable\n    end\n\n    it \"should stop the service\" do\n      allow(FileTest).to receive(:directory?).and_return(false)\n      expect(Puppet::FileSystem).to receive(:symlink?).and_return(true)\n      allow(Puppet::FileSystem).to receive(:unlink)\n      expect(@provider).to receive(:stop)\n      @provider.disable\n    end\n  end\n\n  context \"when checking if the service is enabled?\" do\n    it \"should return true if it is running\" do\n      allow(@provider).to receive(:status).and_return(:running)\n\n      expect(@provider.enabled?).to eq(:true)\n    end\n\n    [true, false].each do |t|\n      it \"should return #{t} if the symlink exists\" do\n        allow(@provider).to receive(:status).and_return(:stopped)\n        path = File.join(@servicedir,\"myservice\")\n        expect(Puppet::FileSystem).to receive(:symlink?).with(path).and_return(t)\n\n        expect(@provider.enabled?).to eq(\"#{t}\".to_sym)\n      end\n    end\n  end\n\n  context \"when checking status\" do\n    it \"should call the external command 'svstat /etc/service/myservice'\" do\n      expect(@provider).to receive(:svstat).with(File.join(@servicedir,\"myservice\"))\n      @provider.status\n    end\n  end\n\n  context \"when checking status\" do\n    it \"and svstat fails, properly raise a Puppet::Error\" do\n      expect(@provider).to receive(:svstat).with(File.join(@servicedir,\"myservice\")).and_raise(Puppet::ExecutionFailure, \"failure\")\n      expect { @provider.status }.to raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: failure')\n    end\n\n    it \"and svstat returns up, then return :running\" do\n      expect(@provider).to receive(:svstat).with(File.join(@servicedir,\"myservice\")).and_return(\"/etc/service/myservice: up (pid 454) 954326 seconds\")\n      expect(@provider.status).to eq(:running)\n    end\n\n    it \"and svstat returns not running, then return :stopped\" do\n      expect(@provider).to receive(:svstat).with(File.join(@servicedir,\"myservice\")).and_return(\"/etc/service/myservice: supervise not running\")\n      expect(@provider.status).to  eq(:stopped)\n    end\n  end\n\n  context '.instances' do\n    before do\n      allow(provider_class).to receive(:defpath).and_return(path)\n    end\n\n    context 'when defpath is nil' do\n      let(:path) { nil }\n\n      it 'returns info message' do\n        expect(Puppet).to receive(:info).with(/daemontools is unsuitable because service directory is nil/)\n        provider_class.instances\n      end\n    end\n\n    context 'when defpath does not exist' do\n      let(:path) { '/inexistent_path' }\n\n      it 'returns notice about missing path' do\n        expect(Puppet).to receive(:notice).with(/Service path #{path} does not exist/)\n        provider_class.instances\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/debian_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Debian',\n         unless: Puppet::Util::Platform.jruby? || Puppet::Util::Platform.windows? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:debian) }\n\n  before(:each) do\n    # Create a mock resource\n    @resource = double('resource')\n\n    @provider = provider_class.new\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name, source and path\n    allow(@resource).to receive(:[]).with(:name).and_return(\"myservice\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:enabled)\n    allow(@resource).to receive(:ref).and_return(\"Service[myservice]\")\n\n    @provider.resource = @resource\n\n    allow(@provider).to receive(:command).with(:update_rc).and_return(\"update_rc\")\n    allow(@provider).to receive(:command).with(:invoke_rc).and_return(\"invoke_rc\")\n    allow(@provider).to receive(:command).with(:service).and_return(\"service\")\n\n    allow(@provider).to receive(:update_rc)\n    allow(@provider).to receive(:invoke_rc)\n  end\n\n  ['1','2'].each do |version|\n    it \"should be the default provider on CumulusLinux #{version}\" do\n      expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return('CumulusLinux')\n      expect(Facter).to receive(:value).with('os.release.major').and_return(version)\n      expect(provider_class.default?).to be_truthy\n    end\n  end\n\n  it \"should be the default provider on Devuan\" do\n    expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return('Devuan')\n    expect(provider_class.default?).to be_truthy\n  end\n\n  it \"should be the default provider on Debian\" do\n    expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return('Debian')\n    expect(Facter).to receive(:value).with('os.release.major').and_return('7')\n    expect(provider_class.default?).to be_truthy\n  end\n\n  it \"should have an enabled? method\" do\n    expect(@provider).to respond_to(:enabled?)\n  end\n\n  it \"should have an enable method\" do\n    expect(@provider).to respond_to(:enable)\n  end\n\n  it \"should have a disable method\" do\n    expect(@provider).to respond_to(:disable)\n  end\n\n  context \"when enabling\" do\n    it \"should call update-rc.d twice\" do\n      expect(@provider).to receive(:update_rc).twice\n      @provider.enable\n    end\n  end\n\n  context \"when disabling\" do\n    it \"should be able to disable services with newer sysv-rc versions\" do\n      allow(@provider).to receive(:`).with(\"dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?\").and_return(\"0\")\n\n      expect(@provider).to receive(:update_rc).with(@resource[:name], \"disable\")\n\n      @provider.disable\n    end\n\n    it \"should be able to enable services with older sysv-rc versions\" do\n      allow(@provider).to receive(:`).with(\"dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?\").and_return(\"1\")\n\n      expect(@provider).to receive(:update_rc).with(\"-f\", @resource[:name], \"remove\")\n      expect(@provider).to receive(:update_rc).with(@resource[:name], \"stop\", \"00\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \".\")\n\n      @provider.disable\n    end\n  end\n\n  context \"when checking whether it is enabled\" do\n    it \"should execute the query command\" do\n      expect(@provider).to receive(:execute).with([\"/usr/sbin/invoke-rc.d\", \"--quiet\", \"--query\", @resource[:name], \"start\"], {:failonfail => false})\n                             .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      @provider.enabled?\n    end\n\n    it \"should return true when invoke-rc.d exits with 104 status\" do\n      expect(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', 104))\n      expect(@provider.enabled?).to eq(:true)\n    end\n\n    it \"should return true when invoke-rc.d exits with 106 status\" do\n      expect(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', 106))\n      expect(@provider.enabled?).to eq(:true)\n    end\n\n    it \"should consider nonexistent services to be disabled\" do\n      @provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'doesnotexist'))\n      expect(@provider).to receive(:execute).with([\"/usr/sbin/invoke-rc.d\", \"--quiet\", \"--query\", \"doesnotexist\", \"start\"], {:failonfail => false})\n                             .and_return(Puppet::Util::Execution::ProcessOutput.new(\"\", 1))\n      expect(@provider.enabled?).to be(:false)\n    end\n\n    shared_examples \"manually queries service status\" do |status|\n      it \"links count is 4\" do\n        allow(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', status))\n        allow(@provider).to receive(:get_start_link_count).and_return(4)\n        expect(@provider.enabled?).to eq(:true)\n      end\n      it \"links count is less than 4\" do\n        allow(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', status))\n        allow(@provider).to receive(:get_start_link_count).and_return(3)\n        expect(@provider.enabled?).to eq(:false)\n      end\n    end\n\n    context \"when invoke-rc.d exits with 101 status\" do\n      it_should_behave_like \"manually queries service status\", 101\n    end\n\n    context \"when invoke-rc.d exits with 105 status\" do\n      it_should_behave_like \"manually queries service status\", 105\n    end\n\n    context \"when invoke-rc.d exits with 101 status\" do\n      it_should_behave_like \"manually queries service status\", 101\n    end\n\n    context \"when invoke-rc.d exits with 105 status\" do\n      it_should_behave_like \"manually queries service status\", 105\n    end\n\n    # pick a range of non-[104.106] numbers, strings and booleans to test with.\n    [-100, -1, 0, 1, 100, \"foo\", \"\", :true, :false].each do |exitstatus|\n      it \"should return false when invoke-rc.d exits with #{exitstatus} status\" do\n        allow(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', exitstatus))\n        expect(@provider.enabled?).to eq(:false)\n      end\n    end\n  end\n\n  context \"when checking service status\" do\n    it \"should use the service command\" do\n      allow(Facter).to receive(:value).with('os.name').and_return('Debian')\n      allow(Facter).to receive(:value).with('os.release.major').and_return('8')\n      allow(@resource).to receive(:[]).with(:hasstatus).and_return(:true)\n      expect(@provider.statuscmd).to eq([\"service\", @resource[:name], \"status\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/freebsd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Freebsd',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:freebsd) }\n\n  before :each do\n    @provider = provider_class.new\n    allow(@provider).to receive(:initscript)\n    allow(Facter).to receive(:value).with('os.family').and_return('FreeBSD')\n  end\n\n  it \"should correctly parse rcvar for FreeBSD < 7\" do\n    allow(@provider).to receive(:execute).and_return(<<OUTPUT)\n# ntpd\n$ntpd_enable=YES\nOUTPUT\n    expect(@provider.rcvar).to eq(['# ntpd', 'ntpd_enable=YES'])\n  end\n\n  it \"should correctly parse rcvar for FreeBSD 7 to 8\" do\n    allow(@provider).to receive(:execute).and_return(<<OUTPUT)\n# ntpd\nntpd_enable=YES\nOUTPUT\n    expect(@provider.rcvar).to eq(['# ntpd', 'ntpd_enable=YES'])\n  end\n\n  it \"should correctly parse rcvar for FreeBSD >= 8.1\" do\n    allow(@provider).to receive(:execute).and_return(<<OUTPUT)\n# ntpd\n#\nntpd_enable=\"YES\"\n#   (default: \"\")\nOUTPUT\n    expect(@provider.rcvar).to eq(['# ntpd', 'ntpd_enable=\"YES\"', '#   (default: \"\")'])\n  end\n\n  it \"should correctly parse rcvar for DragonFly BSD\" do\n    allow(@provider).to receive(:execute).and_return(<<OUTPUT)\n# ntpd\n$ntpd=YES\nOUTPUT\n    expect(@provider.rcvar).to eq(['# ntpd', 'ntpd=YES'])\n  end\n\n  it 'should parse service names with a description' do\n    allow(@provider).to receive(:execute).and_return(<<OUTPUT)\n# local_unbound : local caching forwarding resolver\nlocal_unbound_enable=\"YES\"\nOUTPUT\n    expect(@provider.service_name).to eq('local_unbound')\n  end\n\n  it 'should parse service names without a description' do\n    allow(@provider).to receive(:execute).and_return(<<OUTPUT)\n# local_unbound\nlocal_unbound=\"YES\"\nOUTPUT\n    expect(@provider.service_name).to eq('local_unbound')\n  end\n\n  it \"should find the right rcvar_value for FreeBSD < 7\" do\n    allow(@provider).to receive(:rcvar).and_return(['# ntpd', 'ntpd_enable=YES'])\n\n    expect(@provider.rcvar_value).to eq(\"YES\")\n  end\n\n  it \"should find the right rcvar_value for FreeBSD >= 7\" do\n    allow(@provider).to receive(:rcvar).and_return(['# ntpd', 'ntpd_enable=\"YES\"', '#   (default: \"\")'])\n\n    expect(@provider.rcvar_value).to eq(\"YES\")\n  end\n\n  it \"should find the right rcvar_name\" do\n    allow(@provider).to receive(:rcvar).and_return(['# ntpd', 'ntpd_enable=\"YES\"'])\n\n    expect(@provider.rcvar_name).to eq(\"ntpd\")\n  end\n\n  it \"should enable only the selected service\" do\n    allow(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf').and_return(true)\n    allow(File).to receive(:read).with('/etc/rc.conf').and_return(\"openntpd_enable=\\\"NO\\\"\\nntpd_enable=\\\"NO\\\"\\n\")\n    fh = double('fh')\n    allow(Puppet::FileSystem).to receive(:replace_file).with('/etc/rc.conf').and_yield(fh)\n    expect(fh).to receive(:<<).with(\"openntpd_enable=\\\"NO\\\"\\nntpd_enable=\\\"YES\\\"\\n\")\n    allow(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.local').and_return(false)\n    allow(Puppet::FileSystem).to receive(:exist?).with('/etc/rc.conf.d/ntpd').and_return(false)\n\n    @provider.rc_replace('ntpd', 'ntpd', 'YES')\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/gentoo_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Gentoo',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:gentoo) }\n\n  before :each do\n    allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class)\n    allow(Puppet::FileSystem).to receive(:file?).with('/sbin/rc-update').and_return(true)\n    allow(Puppet::FileSystem).to receive(:executable?).with('/sbin/rc-update').and_return(true)\n    allow(Facter).to receive(:value).with('os.name').and_return('Gentoo')\n    allow(Facter).to receive(:value).with('os.family').and_return('Gentoo')\n\n    # The initprovider (parent of the gentoo provider) does a stat call\n    # before it even tries to execute an initscript. We use sshd in all the\n    # tests so make sure it is considered present.\n    sshd_path = '/etc/init.d/sshd'\n    allow(Puppet::FileSystem).to receive(:stat).with(sshd_path).and_return(double('stat'))\n  end\n\n  let :initscripts do\n    [\n      'alsasound',\n      'bootmisc',\n      'functions.sh',\n      'hwclock',\n      'reboot.sh',\n      'rsyncd',\n      'shutdown.sh',\n      'sshd',\n      'vixie-cron',\n      'wpa_supplicant',\n      'xdm-setup'\n    ]\n  end\n\n  let :helperscripts do\n    [\n      'functions.sh',\n      'reboot.sh',\n      'shutdown.sh'\n    ]\n  end\n\n  let :process_output do\n    Puppet::Util::Execution::ProcessOutput.new('', 0)\n  end\n\n  describe \".instances\" do\n    it \"should have an instances method\" do\n      expect(provider_class).to respond_to(:instances)\n    end\n\n    it \"should get a list of services from /etc/init.d but exclude helper scripts\" do\n      allow(Puppet::FileSystem).to receive(:directory?).and_call_original\n      allow(Puppet::FileSystem).to receive(:directory?).with('/etc/init.d').and_return(true)\n      expect(Dir).to receive(:entries).with('/etc/init.d').and_return(initscripts)\n      (initscripts - helperscripts).each do |script|\n        expect(Puppet::FileSystem).to receive(:executable?).with(\"/etc/init.d/#{script}\").and_return(true)\n      end\n      helperscripts.each do |script|\n        expect(Puppet::FileSystem).not_to receive(:executable?).with(\"/etc/init.d/#{script}\")\n      end\n\n      allow(Puppet::FileSystem).to receive(:symlink?).and_return(false)\n      expect(provider_class.instances.map(&:name)).to eq([\n        'alsasound',\n        'bootmisc',\n        'hwclock',\n        'rsyncd',\n        'sshd',\n        'vixie-cron',\n        'wpa_supplicant',\n        'xdm-setup'\n      ])\n    end\n  end\n\n  describe \"#start\" do\n    it \"should use the supplied start command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n\n    it \"should start the service with <initscript> start otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:start], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')\n      provider.start\n    end\n  end\n\n  describe \"#stop\" do\n    it \"should use the supplied stop command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n\n    it \"should stop the service with <initscript> stop otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:stop], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')\n      provider.stop\n    end\n  end\n\n  describe \"#enabled?\" do\n    before :each do\n      allow_any_instance_of(provider_class).to receive(:update).with(:show).and_return(File.read(my_fixture('rc_update_show')))\n    end\n\n    it \"should run rc-update show to get a list of enabled services\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:update).with(:show).and_return(\"\\n\")\n      provider.enabled?\n    end\n\n    ['hostname', 'net.lo', 'procfs'].each do |service|\n      it \"should consider service #{service} in runlevel boot as enabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:true)\n      end\n    end\n\n    ['alsasound', 'xdm', 'netmount'].each do |service|\n      it \"should consider service #{service} in runlevel default as enabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:true)\n      end\n    end\n\n    ['rsyncd', 'lighttpd', 'mysql'].each do |service|\n      it \"should consider unused service #{service} as disabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:false)\n      end\n    end\n\n  end\n\n  describe \"#enable\" do\n    it \"should run rc-update add to enable a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:update).with(:add, 'sshd', :default)\n      provider.enable\n    end\n  end\n\n  describe \"#disable\" do\n    it \"should run rc-update del to disable a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:update).with(:del, 'sshd', :default)\n      provider.disable\n    end\n  end\n\n  describe \"#status\" do\n    describe \"when a special status command is specified\" do\n      it \"should use the status command from the resource\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n        expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        provider.status\n      end\n\n      it \"should return :stopped when the status command returns with a non-zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n        expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 3))\n        expect(provider.status).to eq(:stopped)\n      end\n\n      it \"should return :running when the status command returns with a zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n        expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    describe \"when hasstatus is false\" do\n      it \"should return running if a pid can be found\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))\n        expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:getpid).and_return(1000)\n        expect(provider.status).to eq(:running)\n      end\n\n      it \"should return stopped if no pid can be found\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))\n        expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:getpid).and_return(nil)\n        expect(provider.status).to eq(:stopped)\n      end\n    end\n\n    describe \"when hasstatus is true\" do\n      it \"should return running if <initscript> status exits with a zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))\n        expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')\n        expect(provider).to receive(:execute)\n          .with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        expect(provider.status).to eq(:running)\n      end\n\n      it \"should return stopped if <initscript> status exits with a non-zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))\n        expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')\n        expect(provider).to receive(:execute)\n          .with(['/etc/init.d/sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 3))\n        expect(provider.status).to eq(:stopped)\n      end\n    end\n  end\n\n  describe \"#restart\" do\n    it \"should use the supplied restart command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))\n      expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:restart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n\n    it \"should restart the service with <initscript> restart if hasrestart is true\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true))\n      expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')\n      expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:restart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n\n    it \"should restart the service with <initscript> stop/start if hasrestart is false\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false))\n      expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')\n      expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:restart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:stop], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:start], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/init_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Init',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:init) }\n\n  before do\n    Puppet::Type.type(:service).defaultprovider = provider_class\n  end\n\n  after do\n    Puppet::Type.type(:service).defaultprovider = nil\n  end\n\n  let :provider do\n    resource.provider\n  end\n\n  let :resource do\n    Puppet::Type.type(:service).new(\n      :name     => 'myservice',\n      :ensure   => :running,\n      :path     => paths\n    )\n  end\n\n  let :paths do\n    [\"/service/path\",\"/alt/service/path\"]\n  end\n\n  let :excludes do\n    # Taken from redhat, gentoo, and debian\n    %w{functions.sh reboot.sh shutdown.sh functions halt killall single linuxconf reboot boot wait-for-state rcS module-init-tools}\n  end\n\n  let :process_output do\n    Puppet::Util::Execution::ProcessOutput.new('', 0)\n  end\n\n  describe \"when running on FreeBSD\" do\n    before :each do\n      allow(Facter).to receive(:value).with('os.name').and_return('FreeBSD')\n      allow(Facter).to receive(:value).with('os.family').and_return('FreeBSD')\n    end\n\n    it \"should set its default path to include /etc/rc.d and /usr/local/etc/rc.d\" do\n      expect(provider_class.defpath).to eq([\"/etc/rc.d\", \"/usr/local/etc/rc.d\"])\n    end\n  end\n\n  describe \"when running on HP-UX\" do\n    before :each do\n      allow(Facter).to receive(:value).with('os.name').and_return('HP-UX')\n    end\n\n    it \"should set its default path to include /sbin/init.d\" do\n      expect(provider_class.defpath).to eq(\"/sbin/init.d\")\n    end\n  end\n\n  describe \"when running on Archlinux\" do\n    before :each do\n      allow(Facter).to receive(:value).with('os.name').and_return('Archlinux')\n    end\n\n    it \"should set its default path to include /etc/rc.d\" do\n      expect(provider_class.defpath).to eq(\"/etc/rc.d\")\n    end\n  end\n\n  describe \"when not running on FreeBSD, HP-UX or Archlinux\" do\n    before :each do\n      allow(Facter).to receive(:value).with('os.name').and_return('RedHat')\n    end\n\n    it \"should set its default path to include /etc/init.d\" do\n      expect(provider_class.defpath).to eq(\"/etc/init.d\")\n    end\n  end\n\n  describe \"when getting all service instances\" do\n    before :each do\n      allow(provider_class).to receive(:defpath).and_return('tmp')\n\n      @services = ['one', 'two', 'three', 'four', 'umountfs']\n      allow(Dir).to receive(:entries).and_call_original\n      allow(Dir).to receive(:entries).with('tmp').and_return(@services)\n      allow(Puppet::FileSystem).to receive(:directory?).and_call_original\n      allow(Puppet::FileSystem).to receive(:directory?).with('tmp').and_return(true)\n      allow(Puppet::FileSystem).to receive(:executable?).and_return(true)\n    end\n\n    it \"should return instances for all services\" do\n      expect(provider_class.instances.map(&:name)).to eq(@services)\n    end\n\n    it \"should omit directories from the service list\" do\n      expect(Puppet::FileSystem).to receive(:directory?).with('tmp/four').and_return(true)\n      expect(provider_class.instances.map(&:name)).to eq(@services - ['four'])\n    end\n\n    it \"should omit an array of services from exclude list\" do\n      exclude = ['two', 'four']\n      expect(provider_class.get_services(provider_class.defpath, exclude).map(&:name)).to eq(@services - exclude)\n    end\n\n    it \"should omit a single service from the exclude list\" do\n      exclude = 'two'\n      expect(provider_class.get_services(provider_class.defpath, exclude).map(&:name)).to eq(@services - [exclude])\n    end\n\n    it \"should omit Yocto services on cisco-wrlinux\" do\n      allow(Facter).to receive(:value).with('os.family').and_return('cisco-wrlinux')\n      exclude = 'umountfs'\n      expect(provider_class.get_services(provider_class.defpath).map(&:name)).to eq(@services - [exclude])\n    end\n\n    it \"should not omit Yocto services on non cisco-wrlinux platforms\" do\n      expect(provider_class.get_services(provider_class.defpath).map(&:name)).to eq(@services)\n    end\n\n    it \"should use defpath\" do\n      expect(provider_class.instances).to be_all { |provider| provider.get(:path) == provider_class.defpath }\n    end\n\n    it \"should set hasstatus to true for providers\" do\n      expect(provider_class.instances).to be_all { |provider| provider.get(:hasstatus) == true }\n    end\n\n    it \"should discard upstart jobs\", :if => Puppet.features.manages_symlinks? do\n      not_init_service, *valid_services = @services\n      path = \"tmp/#{not_init_service}\"\n      allow(Puppet::FileSystem).to receive(:symlink?).at_least(:once).and_return(false)\n      allow(Puppet::FileSystem).to receive(:symlink?).with(Puppet::FileSystem.pathname(path)).and_return(true)\n      allow(Puppet::FileSystem).to receive(:readlink).with(Puppet::FileSystem.pathname(path)).and_return(\"/lib/init/upstart-job\")\n      expect(provider_class.instances.map(&:name)).to eq(valid_services)\n    end\n\n    it \"should discard non-initscript scripts\" do\n      valid_services = @services\n      all_services = valid_services + excludes\n      expect(Dir).to receive(:entries).with('tmp').and_return(all_services)\n      expect(provider_class.instances.map(&:name)).to match_array(valid_services)\n    end\n  end\n\n  describe \"when checking valid paths\" do\n    it \"should discard paths that do not exist\" do\n      expect(Puppet::FileSystem).to receive(:directory?).with(paths[0]).and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with(paths[0]).and_return(false)\n      expect(Puppet::FileSystem).to receive(:directory?).with(paths[1]).and_return(true)\n\n      expect(provider.paths).to eq([paths[1]])\n    end\n\n    it \"should discard paths that are not directories\" do\n      paths.each do |path|\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n        expect(Puppet::FileSystem).to receive(:directory?).with(path).and_return(false)\n      end\n      expect(provider.paths).to be_empty\n    end\n  end\n\n  describe \"when searching for the init script\" do\n    before :each do\n      paths.each {|path| expect(Puppet::FileSystem).to receive(:directory?).with(path).and_return(true) }\n    end\n\n    it \"should be able to find the init script in the service path\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"#{paths[0]}/myservice\").and_return(true)\n      expect(Puppet::FileSystem).not_to receive(:exist?).with(\"#{paths[1]}/myservice\") # first one wins\n      expect(provider.initscript).to eq(\"/service/path/myservice\")\n    end\n\n    it \"should be able to find the init script in an alternate service path\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"#{paths[0]}/myservice\").and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"#{paths[1]}/myservice\").and_return(true)\n      expect(provider.initscript).to eq(\"/alt/service/path/myservice\")\n    end\n\n    it \"should be able to find the init script if it ends with .sh\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"#{paths[0]}/myservice\").and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"#{paths[1]}/myservice\").and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"#{paths[0]}/myservice.sh\").and_return(true)\n      expect(provider.initscript).to eq(\"/service/path/myservice.sh\")\n    end\n\n    it \"should fail if the service isn't there\" do\n      paths.each do |path|\n        expect(Puppet::FileSystem).to receive(:exist?).with(\"#{path}/myservice\").and_return(false)\n        expect(Puppet::FileSystem).to receive(:exist?).with(\"#{path}/myservice.sh\").and_return(false)\n      end\n      expect { provider.initscript }.to raise_error(Puppet::Error, \"Could not find init script for 'myservice'\")\n    end\n  end\n\n  describe \"if the init script is present\" do\n    before :each do\n      allow(Puppet::FileSystem).to receive(:directory?).and_call_original\n      allow(Puppet::FileSystem).to receive(:directory?).with(\"/service/path\").and_return(true)\n      allow(Puppet::FileSystem).to receive(:directory?).with(\"/alt/service/path\").and_return(true)\n      allow(Puppet::FileSystem).to receive(:exist?).with(\"/service/path/myservice\").and_return(true)\n    end\n\n    [:start, :stop, :status, :restart].each do |method|\n      it \"should have a #{method} method\" do\n        expect(provider).to respond_to(method)\n      end\n\n      describe \"when running #{method}\" do\n        before :each do\n          allow(provider).to receive(:execute).and_return(process_output)\n        end\n\n        it \"should use any provided explicit command\" do\n          resource[method] = \"/user/specified/command\"\n          expect(provider).to receive(:execute).with([\"/user/specified/command\"], any_args).and_return(process_output)\n\n          provider.send(method)\n        end\n\n        it \"should pass #{method} to the init script when no explicit command is provided\" do\n          resource[:hasrestart] = :true\n          resource[:hasstatus] = :true\n          expect(provider).to receive(:execute).with([\"/service/path/myservice\", method], any_args).and_return(process_output)\n\n          provider.send(method)\n        end\n      end\n    end\n\n    describe \"when checking status\" do\n      describe \"when hasstatus is :true\" do\n        before :each do\n          resource[:hasstatus] = :true\n        end\n\n        it \"should execute the command\" do\n          expect(provider).to receive(:execute)\n            .with(['/service/path/myservice', :status], hash_including(failonfail: false))\n            .and_return(process_output)\n          provider.status\n        end\n\n        it \"should consider the process running if the command returns 0\" do\n          expect(provider).to receive(:execute)\n            .with(['/service/path/myservice', :status], hash_including(failonfail: false))\n            .and_return(process_output)\n          expect(provider.status).to eq(:running)\n        end\n\n        [-10,-1,1,10].each { |ec|\n          it \"should consider the process stopped if the command returns something non-0\" do\n            expect(provider).to receive(:execute)\n              .with(['/service/path/myservice', :status], hash_including(failonfail: false))\n              .and_return(Puppet::Util::Execution::ProcessOutput.new('', ec))\n            expect(provider.status).to eq(:stopped)\n          end\n        }\n      end\n\n      describe \"when hasstatus is not :true\" do\n        before :each do\n          resource[:hasstatus] = :false\n        end\n\n        it \"should consider the service :running if it has a pid\" do\n          expect(provider).to receive(:getpid).and_return(\"1234\")\n          expect(provider.status).to eq(:running)\n        end\n\n        it \"should consider the service :stopped if it doesn't have a pid\" do\n          expect(provider).to receive(:getpid).and_return(nil)\n          expect(provider.status).to eq(:stopped)\n        end\n      end\n    end\n\n    describe \"when restarting and hasrestart is not :true\" do\n      before :each do\n        resource[:hasrestart] = :false\n      end\n\n      it \"should stop and restart the process\" do\n        expect(provider).to receive(:execute)\n          .with(['/service/path/myservice', :stop], hash_including(failonfail: true))\n          .and_return(process_output)\n        expect(provider).to receive(:execute)\n          .with(['/service/path/myservice', :start], hash_including(failonfail: true))\n          .and_return(process_output)\n        provider.restart\n      end\n    end\n\n    describe \"when starting a service on Solaris\" do\n      it \"should use ctrun\" do\n        allow(Facter).to receive(:value).with('os.family').and_return('Solaris')\n        expect(provider).to receive(:execute)\n          .with('/usr/bin/ctrun -l child /service/path/myservice start', {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        provider.start\n      end\n    end\n\n    describe \"when starting a service on RedHat\" do\n      it \"should not use ctrun\" do\n        allow(Facter).to receive(:value).with('os.family').and_return('RedHat')\n        expect(provider).to receive(:execute)\n          .with(['/service/path/myservice', :start], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        provider.start\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/launchd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Launchd',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:launchd) }\n  let (:plistlib) { Puppet::Util::Plist }\n  let (:joblabel) { \"com.foo.food\" }\n  let (:provider) { subject.class }\n  let (:resource) { Puppet::Type.type(:service).new(:name => joblabel, :provider => :launchd) }\n  let (:launchd_overrides_6_9) { '/var/db/launchd.db/com.apple.launchd/overrides.plist' }\n  let (:launchd_overrides_10_) { '/var/db/com.apple.xpc.launchd/disabled.plist' }\n\n  subject { resource.provider }\n\n  after :each do\n    provider.instance_variable_set(:@job_list, nil)\n  end\n\n  describe \"the type interface\" do\n    %w{ start stop enabled? enable disable status}.each do |method|\n      it { is_expected.to respond_to method.to_sym }\n    end\n  end\n\n  describe 'the status of the services' do\n    it \"should call the external command 'launchctl list' once\" do\n      expect(provider).to receive(:launchctl).with(:list).and_return(joblabel)\n      expect(provider).to receive(:jobsearch).and_return({joblabel => \"/Library/LaunchDaemons/#{joblabel}\"})\n      provider.prefetch({})\n    end\n\n    it \"should return stopped if not listed in launchctl list output\" do\n      expect(provider).to receive(:launchctl).with(:list).and_return('com.bar.is_running')\n      expect(provider).to receive(:jobsearch).and_return({'com.bar.is_not_running' => \"/Library/LaunchDaemons/com.bar.is_not_running\"})\n      expect(provider.prefetch({}).last.status).to eq(:stopped)\n    end\n\n    it \"should return running if listed in launchctl list output\" do\n      expect(provider).to receive(:launchctl).with(:list).and_return('com.bar.is_running')\n      expect(provider).to receive(:jobsearch).and_return({'com.bar.is_running' => \"/Library/LaunchDaemons/com.bar.is_running\"})\n      expect(provider.prefetch({}).last.status).to eq(:running)\n    end\n\n    describe \"when hasstatus is set to false\" do\n      before :each do\n        resource[:hasstatus] = :false\n      end\n\n      it \"should use the user-provided status command if present and return running if true\" do\n        resource[:status] = '/bin/true'\n        expect(subject).to receive(:execute)\n          .with([\"/bin/true\"], hash_including(failonfail: false))\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        expect(subject.status).to eq(:running)\n      end\n\n      it \"should use the user-provided status command if present and return stopped if false\" do\n        resource[:status] = '/bin/false'\n        expect(subject).to receive(:execute)\n          .with([\"/bin/false\"], hash_including(failonfail: false))\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 1))\n        expect(subject.status).to eq(:stopped)\n      end\n\n      it \"should fall back to getpid if no status command is provided\" do\n        expect(subject).to receive(:getpid).and_return(123)\n        expect(subject.status).to eq(:running)\n      end\n    end\n  end\n\n  [[10, '10.6'], [13, '10.9']].each do |kernel, version|\n    describe \"when checking whether the service is enabled on OS X #{version}\" do\n      it \"should return true if the job plist says disabled is true and the global overrides says disabled is false\" do\n        expect(provider).to receive(:get_os_version).and_return(kernel).at_least(:once)\n        expect(subject).to receive(:plist_from_label).and_return([joblabel, {\"Disabled\" => true}])\n        expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_6_9).and_return({joblabel => {\"Disabled\" => false}})\n        expect(FileTest).to receive(:file?).with(launchd_overrides_6_9).and_return(true)\n        expect(subject.enabled?).to eq(:true)\n      end\n\n      it \"should return false if the job plist says disabled is false and the global overrides says disabled is true\" do\n        expect(provider).to receive(:get_os_version).and_return(kernel).at_least(:once)\n        expect(subject).to receive(:plist_from_label).and_return([joblabel, {\"Disabled\" => false}])\n        expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_6_9).and_return({joblabel => {\"Disabled\" => true}})\n        expect(FileTest).to receive(:file?).with(launchd_overrides_6_9).and_return(true)\n        expect(subject.enabled?).to eq(:false)\n      end\n\n      it \"should return true if the job plist and the global overrides have no disabled keys\" do\n        expect(provider).to receive(:get_os_version).and_return(kernel).at_least(:once)\n        expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n        expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_6_9).and_return({})\n        expect(FileTest).to receive(:file?).with(launchd_overrides_6_9).and_return(true)\n        expect(subject.enabled?).to eq(:true)\n      end\n    end\n  end\n\n  describe \"when checking whether the service is enabled on OS X 10.10\" do\n    it \"should return true if the job plist says disabled is true and the global overrides says disabled is false\" do\n      expect(provider).to receive(:get_os_version).and_return(14).at_least(:once)\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {\"Disabled\" => true}])\n      expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_10_).and_return({joblabel => false})\n      expect(FileTest).to receive(:file?).with(launchd_overrides_10_).and_return(true)\n      expect(subject.enabled?).to eq(:true)\n    end\n\n    it \"should return false if the job plist says disabled is false and the global overrides says disabled is true\" do\n      expect(provider).to receive(:get_os_version).and_return(14).at_least(:once)\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {\"Disabled\" => false}])\n      expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_10_).and_return({joblabel => true})\n      expect(FileTest).to receive(:file?).with(launchd_overrides_10_).and_return(true)\n      expect(subject.enabled?).to eq(:false)\n    end\n\n    it \"should return true if the job plist and the global overrides have no disabled keys\" do\n      expect(provider).to receive(:get_os_version).and_return(14).at_least(:once)\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n      expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_10_).and_return({})\n      expect(FileTest).to receive(:file?).with(launchd_overrides_10_).and_return(true)\n      expect(subject.enabled?).to eq(:true)\n    end\n  end\n\n  describe \"when starting the service\" do\n    let(:services) { \"12345 0 #{joblabel}\"  }\n\n    it \"should call any explicit 'start' command\" do\n      resource[:start] = \"/bin/false\"\n      expect(subject).to receive(:execute).with([\"/bin/false\"], hash_including(failonfail: true))\n      subject.start\n    end\n\n    it \"should look for the relevant plist once\" do\n      allow(provider).to receive(:launchctl).with(:list).and_return(services)\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}]).once\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:execute).with([:launchctl, :load, \"-w\", joblabel])\n      subject.start\n    end\n\n    it \"should execute 'launchctl load' once without writing to the plist if the job is enabled\" do\n      allow(provider).to receive(:launchctl).with(:list).and_return(services)\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:execute).with([:launchctl, :load, \"-w\", joblabel]).once\n      subject.start\n    end\n\n    it \"should execute 'launchctl load' with writing to the plist once if the job is disabled\" do\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n      expect(subject).to receive(:enabled?).and_return(:false)\n      expect(subject).to receive(:execute).with([:launchctl, :load, \"-w\", joblabel]).once\n      subject.start\n    end\n\n    it \"should disable the job once if the job is disabled and should be disabled at boot\" do\n      resource[:enable] = false\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {\"Disabled\" => true}])\n      expect(subject).to receive(:enabled?).and_return(:false)\n      expect(subject).to receive(:execute).with([:launchctl, :load, \"-w\", joblabel])\n      expect(subject).to receive(:disable).once\n      subject.start\n    end\n\n    it \"(#2773) should execute 'launchctl load -w' if the job is enabled but stopped\" do\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:status).and_return(:stopped)\n      expect(subject).to receive(:execute).with([:launchctl, :load, '-w', joblabel])\n      subject.start\n    end\n\n    it \"(#16271) Should stop and start the service when a restart is called\" do\n      expect(subject).to receive(:stop)\n      expect(subject).to receive(:start)\n      subject.restart\n    end\n  end\n\n  describe \"when stopping the service\" do\n    it \"should call any explicit 'stop' command\" do\n      resource[:stop] = \"/bin/false\"\n      expect(subject).to receive(:execute).with([\"/bin/false\"], hash_including(failonfail: true))\n      subject.stop\n    end\n\n    it \"should look for the relevant plist once\" do\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}]).once\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:execute).with([:launchctl, :unload, '-w', joblabel])\n      subject.stop\n    end\n\n    it \"should execute 'launchctl unload' once without writing to the plist if the job is disabled\" do\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n      expect(subject).to receive(:enabled?).and_return(:false)\n      expect(subject).to receive(:execute).with([:launchctl, :unload, joblabel]).once\n      subject.stop\n    end\n\n    it \"should execute 'launchctl unload' with writing to the plist once if the job is enabled\" do\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}])\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:execute).with([:launchctl, :unload, '-w', joblabel]).once\n      subject.stop\n    end\n\n    it \"should enable the job once if the job is enabled and should be enabled at boot\" do\n      resource[:enable] = true\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {\"Disabled\" => false}])\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:execute).with([:launchctl, :unload, \"-w\", joblabel])\n      expect(subject).to receive(:enable).once\n      subject.stop\n    end\n  end\n\n  describe \"when enabling the service\" do\n    it \"should look for the relevant plist once\" do   ### Do we need this test?  Differentiating it?\n      resource[:enable] = true\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}]).once\n      expect(subject).to receive(:enabled?).and_return(:false)\n      expect(subject).to receive(:execute).with([:launchctl, :unload, joblabel])\n      subject.stop\n    end\n\n    it \"should check if the job is enabled once\" do\n      resource[:enable] = true\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}]).once\n      expect(subject).to receive(:enabled?).once\n      expect(subject).to receive(:execute).with([:launchctl, :unload, joblabel])\n      subject.stop\n    end\n  end\n\n  describe \"when disabling the service\" do\n    it \"should look for the relevant plist once\" do\n      resource[:enable] = false\n      expect(subject).to receive(:plist_from_label).and_return([joblabel, {}]).once\n      expect(subject).to receive(:enabled?).and_return(:true)\n      expect(subject).to receive(:execute).with([:launchctl, :unload, '-w', joblabel])\n      subject.stop\n    end\n  end\n\n  describe \"when a service is unavailable\" do\n    let(:map) { {\"some.random.job\" => \"/path/to/job.plist\"} }\n    \n    before :each do\n      allow(provider).to receive(:make_label_to_path_map).and_return(map)\n    end\n\n    it \"should fail when searching for the unavailable service\" do\n      expect { provider.jobsearch(\"NOSUCH\") }.to raise_error(Puppet::Error)\n    end\n\n    it \"should return false when enabling the service\" do\n      expect(subject.enabled?).to eq(:false)\n    end\n\n    it \"should fail when starting the service\" do\n      expect { subject.start }.to raise_error(Puppet::Error)\n    end\n\n    it \"should fail when starting the service\" do\n      expect { subject.stop }.to raise_error(Puppet::Error)\n    end\n  end\n\n  [[10, \"10.6\"], [13, \"10.9\"]].each do |kernel, version|\n    describe \"when enabling the service on OS X #{version}\" do\n      it \"should write to the global launchd overrides file once\" do\n        resource[:enable] = true\n        expect(provider).to receive(:get_os_version).and_return(kernel).at_least(:once)\n        expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_6_9).and_return({})\n        expect(plistlib).to receive(:write_plist_file).with(hash_including(resource[:name] => {'Disabled' => false}), launchd_overrides_6_9).once\n        subject.enable\n      end\n    end\n\n    describe \"when disabling the service on OS X #{version}\" do\n      it \"should write to the global launchd overrides file once\" do\n        resource[:enable] = false\n        expect(provider).to receive(:get_os_version).and_return(kernel).at_least(:once)\n        expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_6_9).and_return({})\n        expect(plistlib).to receive(:write_plist_file).with(hash_including(resource[:name] => {'Disabled' => true}), launchd_overrides_6_9).once\n        subject.disable\n      end\n    end\n  end\n\n  describe \"when enabling the service on OS X 10.10\" do\n    it \"should write to the global launchd overrides file once\" do\n      resource[:enable] = true\n      expect(provider).to receive(:get_os_version).and_return(14).at_least(:once)\n      expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_10_).and_return({})\n      expect(plistlib).to receive(:write_plist_file).with(hash_including(resource[:name] => false), launchd_overrides_10_).once\n      subject.enable\n    end\n  end\n\n  describe \"when disabling the service on OS X 10.10\" do\n    it \"should write to the global launchd overrides file once\" do\n      resource[:enable] = false\n      expect(provider).to receive(:get_os_version).and_return(14).at_least(:once)\n      expect(plistlib).to receive(:read_plist_file).with(launchd_overrides_10_).and_return({})\n      expect(plistlib).to receive(:write_plist_file).with(hash_including(resource[:name] => true), launchd_overrides_10_).once\n      subject.disable\n    end\n  end\n\n  describe \"make_label_to_path_map\" do\n    before do\n      # clear out this class variable between runs\n      if provider.instance_variable_defined? :@label_to_path_map\n        provider.send(:remove_instance_variable, :@label_to_path_map)\n      end\n    end\n\n    describe \"when encountering malformed plists\" do\n      let(:plist_without_label) do\n        {\n          'LimitLoadToSessionType' => 'Aqua'\n        }\n      end\n      let(:plist_without_label_not_hash) { 'just a string' }\n      let(:busted_plist_path) { '/Library/LaunchAgents/org.busted.plist' }\n      let(:binary_plist_path) { '/Library/LaunchAgents/org.binary.plist' }\n\n      it \"[17624] should warn that the plist in question is being skipped\" do\n        expect(provider).to receive(:launchd_paths).and_return(['/Library/LaunchAgents'])\n        expect(provider).to receive(:return_globbed_list_of_file_paths).with('/Library/LaunchAgents').and_return([busted_plist_path])\n        expect(plistlib).to receive(:read_plist_file).with(busted_plist_path).and_return(plist_without_label)\n        expect(Puppet).to receive(:debug).with(\"Reading launchd plist #{busted_plist_path}\")\n        expect(Puppet).to receive(:debug).with(\"The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it\")\n        provider.make_label_to_path_map\n      end\n\n      it \"it should warn that the malformed plist in question is being skipped\" do\n        expect(provider).to receive(:launchd_paths).and_return(['/Library/LaunchAgents'])\n        expect(provider).to receive(:return_globbed_list_of_file_paths).with('/Library/LaunchAgents').and_return([busted_plist_path])\n        expect(plistlib).to receive(:read_plist_file).with(busted_plist_path).and_return(plist_without_label_not_hash)\n        expect(Puppet).to receive(:debug).with(\"Reading launchd plist #{busted_plist_path}\")\n        expect(Puppet).to receive(:debug).with(\"The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it\")\n        provider.make_label_to_path_map\n      end\n\n    end\n\n    it \"should return the cached value when available\" do\n      provider.instance_variable_set(:@label_to_path_map, {'xx'=>'yy'})\n      expect(provider.make_label_to_path_map).to eq({'xx'=>'yy'})\n    end\n\n    describe \"when successful\" do\n      let(:launchd_dir) { '/Library/LaunchAgents' }\n      let(:plist) { launchd_dir + '/foo.bar.service.plist' }\n      let(:label) { 'foo.bar.service' }\n\n      before do\n        provider.instance_variable_set(:@label_to_path_map, nil)\n        expect(provider).to receive(:launchd_paths).and_return([launchd_dir])\n        expect(provider).to receive(:return_globbed_list_of_file_paths).with(launchd_dir).and_return([plist])\n        expect(plistlib).to receive(:read_plist_file).with(plist).and_return({'Label'=>'foo.bar.service'})\n      end\n\n      it \"should read the plists and return their contents\" do\n        expect(provider.make_label_to_path_map).to eq({label=>plist})\n      end\n\n      it \"should re-read the plists and return their contents when refreshed\" do\n        provider.instance_variable_set(:@label_to_path_map, {'xx'=>'yy'})\n        expect(provider.make_label_to_path_map(true)).to eq({label=>plist})\n      end\n    end\n  end\n\n  describe \"jobsearch\" do\n    let(:map) { {\"org.mozilla.puppet\" => \"/path/to/puppet.plist\",\n                 \"org.mozilla.python\" => \"/path/to/python.plist\"} }\n\n                 it \"returns the entire map with no args\" do\n      expect(provider).to receive(:make_label_to_path_map).and_return(map)\n      expect(provider.jobsearch).to eq(map)\n    end\n\n    it \"returns a singleton hash when given a label\" do\n      expect(provider).to receive(:make_label_to_path_map).and_return(map)\n      expect(provider.jobsearch(\"org.mozilla.puppet\")).to eq({ \"org.mozilla.puppet\" => \"/path/to/puppet.plist\" })\n    end\n\n    it \"refreshes the label_to_path_map when label is not found\" do\n      expect(provider).to receive(:make_label_to_path_map).and_return(map)\n      expect(provider.jobsearch(\"org.mozilla.puppet\")).to eq({ \"org.mozilla.puppet\" => \"/path/to/puppet.plist\" })\n    end\n\n    it \"raises Puppet::Error when the label is still not found\" do\n      allow(provider).to receive(:make_label_to_path_map).and_return(map)\n      expect { provider.jobsearch(\"NOSUCH\") }.to raise_error(Puppet::Error)\n    end\n  end\n\n  describe \"read_overrides\" do\n    before do\n      allow(Kernel).to receive(:sleep)\n    end\n\n    it \"should read overrides\" do\n      expect(provider).to receive(:read_plist).once.and_return({})\n      expect(provider.read_overrides).to eq({})\n    end\n\n    it \"should retry if read_plist fails\" do\n      allow(provider).to receive(:read_plist).and_return({}, nil)\n      expect(provider.read_overrides).to eq({})\n    end\n\n    it \"raises Puppet::Error after 20 attempts\" do\n      expect(provider).to receive(:read_plist).exactly(20).times().and_return(nil)\n      expect { provider.read_overrides }.to raise_error(Puppet::Error)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/openbsd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Openbsd',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:openbsd) }\n\n  before :each do\n    allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class)\n    allow(Facter).to receive(:value).with('os.name').and_return(:openbsd)\n    allow(Facter).to receive(:value).with('os.family').and_return('OpenBSD')\n    allow(FileTest).to receive(:file?).with('/usr/sbin/rcctl').and_return(true)\n    allow(FileTest).to receive(:executable?).with('/usr/sbin/rcctl').and_return(true)\n  end\n\n  context \"#instances\" do\n    it \"should have an instances method\" do\n      expect(provider_class).to respond_to :instances\n    end\n\n    it \"should list all available services\" do\n      allow(provider_class).to receive(:execpipe).with(['/usr/sbin/rcctl', :getall]).and_yield(File.read(my_fixture('rcctl_getall')))\n      expect(provider_class.instances.map(&:name)).to eq([\n        'accounting', 'pf', 'postgresql', 'tftpd', 'wsmoused', 'xdm',\n      ])\n    end\n  end\n\n  context \"#start\" do\n    it \"should use the supplied start command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n\n    it \"should start the service otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', '-f', :start, 'sshd'], hash_including(failonfail: true))\n      provider.start\n    end\n  end\n\n  context \"#stop\" do\n    it \"should use the supplied stop command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n\n    it \"should stop the service otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', :stop, 'sshd'], hash_including(failonfail: true))\n      provider.stop\n    end\n  end\n\n  context \"#status\" do\n    it \"should use the status command from the resource\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n      expect(provider).not_to receive(:execute).with(['/usr/sbin/rcctl', :get, 'sshd', :status], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute)\n       .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n       .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      provider.status\n    end\n\n    it \"should return :stopped when status command returns with a non-zero exitcode\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n      expect(provider).not_to receive(:execute).with(['/usr/sbin/rcctl', :get, 'sshd', :status], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute)\n        .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 3))\n      expect(provider.status).to eq(:stopped)\n    end\n\n    it \"should return :running when status command returns with a zero exitcode\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n      expect(provider).not_to receive(:execute).with(['/usr/sbin/rcctl', :get, 'sshd', :status], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute)\n        .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      expect(provider.status).to eq(:running)\n    end\n  end\n\n  context \"#restart\" do\n    it \"should use the supplied restart command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))\n      expect(provider).not_to receive(:execute).with(['/usr/sbin/rcctl', '-f', :restart, 'sshd'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute)\n        .with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      provider.restart\n    end\n\n    it \"should restart the service with rcctl restart if hasrestart is true\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', '-f', :restart, 'sshd'], hash_including(failonfail: true))\n      provider.restart\n    end\n\n    it \"should restart the service with rcctl stop/start if hasrestart is false\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false))\n      expect(provider).not_to receive(:execute).with(['/usr/sbin/rcctl', '-f', :restart, 'sshd'], any_args)\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', :stop, 'sshd'], hash_including(failonfail: true))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', '-f', :start, 'sshd'], hash_including(failonfail: true))\n      provider.restart\n    end\n  end\n\n  context \"#enabled?\" do\n    it \"should return :true if the service is enabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'get', 'sshd', 'status'], {:failonfail => false, :combine => false, :squelch => false}).and_return(double(:exitstatus => 0))\n      expect(provider.enabled?).to eq(:true)\n    end\n\n    it \"should return :false if the service is disabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'get', 'sshd', 'status'], {:failonfail => false, :combine => false, :squelch => false}).and_return(Puppet::Util::Execution::ProcessOutput.new('NO', 1))\n      expect(provider.enabled?).to eq(:false)\n    end\n  end\n\n  context \"#enable\" do\n    it \"should run rcctl enable to enable the service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:rcctl).with(:enable, 'sshd')\n      provider.enable\n    end\n\n    it \"should run rcctl enable with flags if provided\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :flags => '-6'))\n      expect(provider).to receive(:rcctl).with(:enable, 'sshd')\n      expect(provider).to receive(:rcctl).with(:set, 'sshd', :flags, '-6')\n      provider.enable\n    end\n  end\n\n  context \"#disable\" do\n    it \"should run rcctl disable to disable the service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:rcctl).with(:disable, 'sshd')\n      provider.disable\n    end\n  end\n\n  context \"#running?\" do\n    it \"should run rcctl check to check the service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'check', 'sshd'], {:failonfail => false, :combine => false, :squelch => false}).and_return('sshd(ok)')\n      expect(provider.running?).to be_truthy\n    end\n\n    it \"should return true if the service is running\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'check', 'sshd'], {:failonfail => false, :combine => false, :squelch => false}).and_return('sshd(ok)')\n      expect(provider.running?).to be_truthy\n    end\n\n    it \"should return nil if the service is not running\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'check', 'sshd'], {:failonfail => false, :combine => false, :squelch => false}).and_return('sshd(failed)')\n      expect(provider.running?).to be_nil\n    end\n  end\n\n  context \"#flags\" do\n    it \"should return flags when set\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :flags => '-6'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'get', 'sshd', 'flags'], {:failonfail => false, :combine => false, :squelch => false}).and_return('-6')\n      provider.flags\n    end\n\n    it \"should return empty flags\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'get', 'sshd', 'flags'], {:failonfail => false, :combine => false, :squelch => false}).and_return('')\n      provider.flags\n    end\n\n    it \"should return flags for special services\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'pf'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'get', 'pf', 'flags'], {:failonfail => false, :combine => false, :squelch => false}).and_return('YES')\n      provider.flags\n    end\n  end\n\n  context \"#flags=\" do\n    it \"should run rcctl to set flags\", unless: Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'get', 'sshd', 'flags'], any_args).and_return('')\n      expect(provider).to receive(:rcctl).with(:set, 'sshd', :flags, '-4')\n      expect(provider).to receive(:execute).with(['/usr/sbin/rcctl', 'check', 'sshd'], any_args).and_return('')\n      provider.flags = '-4'\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/openrc_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Openrc',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:openrc) }\n\n  before :each do\n    allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class)\n    ['/sbin/rc-service', '/bin/rc-status', '/sbin/rc-update'].each do |command|\n      # Puppet::Util is both mixed in to providers and is also invoked directly\n      # by Puppet::Provider::CommandDefiner, so we have to stub both out.\n      allow(provider_class).to receive(:which).with(command).and_return(command)\n      allow(Puppet::Util).to receive(:which).with(command).and_return(command)\n    end\n  end\n\n  describe \".instances\" do\n    it \"should have an instances method\" do\n      expect(provider_class).to respond_to :instances\n    end\n\n    it \"should get a list of services from rc-service --list\" do\n      expect(provider_class).to receive(:rcservice).with('-C','--list').and_return(File.read(my_fixture('rcservice_list')))\n      expect(provider_class.instances.map(&:name)).to eq([\n        'alsasound',\n        'consolefont',\n        'lvm-monitoring',\n        'pydoc-2.7',\n        'pydoc-3.2',\n        'wpa_supplicant',\n        'xdm',\n        'xdm-setup'\n      ])\n    end\n  end\n\n  describe \"#start\" do\n    it \"should use the supplied start command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n\n    it \"should start the service with rc-service start otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/sbin/rc-service','sshd',:start], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n  end\n\n  describe \"#stop\" do\n    it \"should use the supplied stop command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n\n    it \"should stop the service with rc-service stop otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:execute).with(['/sbin/rc-service','sshd',:stop], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n  end\n\n  describe 'when invoking `rc-status`' do\n    subject { provider_class.new(Puppet::Type.type(:service).new(:name => 'urandom')) }\n\n    it \"clears the RC_SVCNAME environment variable\" do\n      Puppet::Util.withenv(:RC_SVCNAME => 'puppet') do\n        expect(Puppet::Util::Execution).to receive(:execute).with(\n          include('/bin/rc-status'),\n          hash_including(custom_environment:  hash_including(RC_SVCNAME: nil))\n        ).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n\n        subject.enabled?\n      end\n    end\n  end\n\n  describe \"#enabled?\" do\n    before :each do\n      allow_any_instance_of(provider_class).to receive(:rcstatus).with('-C','-a').and_return(File.read(my_fixture('rcstatus')))\n    end\n\n    it \"should run rc-status to get a list of enabled services\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:rcstatus).with('-C','-a').and_return(\"\\n\")\n      provider.enabled?\n    end\n\n    ['hwclock', 'modules', 'urandom'].each do |service|\n      it \"should consider service #{service} in runlevel boot as enabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:true)\n      end\n    end\n\n    ['netmount', 'xdm', 'local', 'foo_with_very_very_long_servicename_no_still_not_the_end_wait_for_it_almost_there_almost_there_now_finally_the_end'].each do |service|\n      it \"should consider service #{service} in runlevel default as enabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:true)\n      end\n    end\n\n    ['net.eth0', 'pcscd'].each do |service|\n      it \"should consider service #{service} in dynamic runlevel: hotplugged as disabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:false)\n      end\n    end\n\n    ['sysfs', 'udev-mount'].each do |service|\n      it \"should consider service #{service} in dynamic runlevel: needed as disabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:false)\n      end\n    end\n\n    ['sshd'].each do |service|\n      it \"should consider service #{service} in dynamic runlevel: manual as disabled\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => service))\n        expect(provider.enabled?).to eq(:false)\n      end\n    end\n\n  end\n\n  describe \"#enable\" do\n    it \"should run rc-update add to enable a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:rcupdate).with('-C', :add, 'sshd')\n      provider.enable\n    end\n  end\n\n  describe \"#disable\" do\n    it \"should run rc-update del to disable a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(provider).to receive(:rcupdate).with('-C', :del, 'sshd')\n      provider.disable\n    end\n  end\n\n  describe \"#status\" do\n    describe \"when a special status command if specified\" do\n      it \"should use the status command from the resource\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n        expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        provider.status\n      end\n\n      it \"should return :stopped when status command returns with a non-zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n        expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 3))\n        expect(provider.status).to eq(:stopped)\n      end\n\n      it \"should return :running when status command returns with a zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))\n        expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    describe \"when hasstatus is false\" do\n      it \"should return running if a pid can be found\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))\n        expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:getpid).and_return(1000)\n        expect(provider.status).to eq(:running)\n      end\n\n      it \"should return stopped if no pid can be found\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))\n        expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        expect(provider).to receive(:getpid).and_return(nil)\n        expect(provider.status).to eq(:stopped)\n      end\n    end\n\n    describe \"when hasstatus is true\" do\n      it \"should return running if rc-service status exits with a zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))\n        expect(provider).to receive(:execute)\n          .with(['/sbin/rc-service','sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        expect(provider.status).to eq(:running)\n      end\n\n      it \"should return stopped if rc-service status exits with a non-zero exitcode\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))\n        expect(provider).to receive(:execute)\n          .with(['/sbin/rc-service','sshd',:status], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 3))\n        expect(provider.status).to eq(:stopped)\n      end\n    end\n  end\n\n  describe \"#restart\" do\n    it \"should use the supplied restart command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))\n      expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:restart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n\n    it \"should restart the service with rc-service restart if hasrestart is true\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true))\n      expect(provider).to receive(:execute).with(['/sbin/rc-service','sshd',:restart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n\n    it \"should restart the service with rc-service stop/start if hasrestart is false\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false))\n      expect(provider).not_to receive(:execute).with(['/sbin/rc-service','sshd',:restart], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute).with(['/sbin/rc-service','sshd',:stop], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      expect(provider).to receive(:execute).with(['/sbin/rc-service','sshd',:start], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/openwrt_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Openwrt',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n\n  let(:provider_class) { Puppet::Type.type(:service).provider(:openwrt) }\n\n  let(:resource) do\n    Puppet::Type.type(:service).new(\n      :name       => 'myservice',\n      :path       => '/etc/init.d',\n      :hasrestart => true,\n    )\n  end\n\n  let(:provider) do\n    provider = provider_class.new\n    provider.resource = resource\n    provider\n  end\n\n  before :each do\n    resource.provider = provider\n\n    # All OpenWrt tests operate on the init script directly. It must exist.\n    allow(Puppet::FileSystem).to receive(:directory?).and_call_original\n    allow(Puppet::FileSystem).to receive(:directory?).with('/etc/init.d').and_return(true)\n\n    allow(Puppet::FileSystem).to receive(:exist?).with('/etc/init.d/myservice').and_return(true)\n    allow(Puppet::FileSystem).to receive(:file?).and_call_original\n    allow(Puppet::FileSystem).to receive(:file?).with('/etc/init.d/myservice').and_return(true)\n    allow(Puppet::FileSystem).to receive(:executable?).with('/etc/init.d/myservice').and_return(true)\n  end\n\n  it \"should be the default provider on 'openwrt'\" do\n    expect(Facter).to receive(:value).with('os.name').and_return('openwrt')\n    expect(provider_class.default?).to be_truthy\n  end\n\n  # test self.instances\n  describe \"when getting all service instances\" do\n    let(:services) { ['dnsmasq', 'dropbear', 'firewall', 'led', 'puppet', 'uhttpd' ] }\n\n    before :each do\n      allow(Dir).to receive(:entries).and_call_original\n      allow(Dir).to receive(:entries).with('/etc/init.d').and_return(services)\n      allow(Puppet::FileSystem).to receive(:executable?).and_return(true)\n    end\n\n    it \"should return instances for all services\" do\n      services.each do |inst|\n        expect(provider_class).to receive(:new).with(hash_including(:name => inst, :path => '/etc/init.d')).and_return(\"#{inst}_instance\")\n      end\n      results = services.collect { |x| \"#{x}_instance\"}\n      expect(provider_class.instances).to eq(results)\n    end\n  end\n\n  it \"should have an enabled? method\" do\n    expect(provider).to respond_to(:enabled?)\n  end\n\n  it \"should have an enable method\" do\n    expect(provider).to respond_to(:enable)\n  end\n\n  it \"should have a disable method\" do\n    expect(provider).to respond_to(:disable)\n  end\n\n  [:start, :stop, :restart].each do |method|\n    it \"should have a #{method} method\" do\n      expect(provider).to respond_to(method)\n    end\n\n    describe \"when running #{method}\" do\n      it \"should use any provided explicit command\" do\n        resource[method] = '/user/specified/command'\n        expect(provider).to receive(:execute).with(['/user/specified/command'], any_args)\n        provider.send(method)\n      end\n\n      it \"should execute the init script with #{method} when no explicit command is provided\" do\n        expect(provider).to receive(:execute).with(['/etc/init.d/myservice', method], any_args)\n        provider.send(method)\n      end\n    end\n  end\n\n  describe \"when checking status\" do\n    it \"should consider the service :running if it has a pid\" do\n      expect(provider).to receive(:getpid).and_return(\"1234\")\n      expect(provider.status).to eq(:running)\n    end\n\n    it \"should consider the service :stopped if it doesn't have a pid\" do\n      expect(provider).to receive(:getpid).and_return(nil)\n      expect(provider.status).to eq(:stopped)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/rcng_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Rcng',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:rcng) }\n\n  before :each do\n    allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class)\n    allow(Facter).to receive(:value).with('os.name').and_return(:netbsd)\n    allow(Facter).to receive(:value).with('os.family').and_return('NetBSD')\n    allow(provider_class).to receive(:defpath).and_return('/etc/rc.d')\n    @provider = provider_class.new\n    allow(@provider).to receive(:initscript)\n  end\n\n  context \"#enable\" do\n    it \"should have an enable method\" do\n      expect(@provider).to respond_to(:enable)\n    end\n\n    it \"should set the proper contents to enable\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Dir).to receive(:mkdir).with('/etc/rc.conf.d')\n      fh = double('fh')\n      expect(Puppet::Util).to receive(:replace_file).with('/etc/rc.conf.d/sshd', 0644).and_yield(fh)\n      expect(fh).to receive(:puts).with(\"sshd=${sshd:=YES}\\n\")\n      provider.enable\n    end\n\n    it \"should set the proper contents to enable when disabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      allow(Dir).to receive(:mkdir).with('/etc/rc.conf.d')\n      allow(File).to receive(:read).with('/etc/rc.conf.d/sshd').and_return(\"sshd_enable=\\\"NO\\\"\\n\")\n      fh = double('fh')\n      expect(Puppet::Util).to receive(:replace_file).with('/etc/rc.conf.d/sshd', 0644).and_yield(fh)\n      expect(fh).to receive(:puts).with(\"sshd=${sshd:=YES}\\n\")\n      provider.enable\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/redhat_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Redhat',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:redhat) }\n\n  before :each do\n    @class = Puppet::Type.type(:service).provider(:redhat)\n    @resource = double('resource')\n    allow(@resource).to receive(:[]).and_return(nil)\n    allow(@resource).to receive(:[]).with(:name).and_return(\"myservice\")\n    @provider = provider_class.new\n    allow(@resource).to receive(:provider).and_return(@provider)\n    @provider.resource = @resource\n    allow(@provider).to receive(:get).with(:hasstatus).and_return(false)\n    allow(FileTest).to receive(:file?).with('/sbin/service').and_return(true)\n    allow(FileTest).to receive(:executable?).with('/sbin/service').and_return(true)\n    allow(Facter).to receive(:value).with('os.name').and_return('CentOS')\n    allow(Facter).to receive(:value).with('os.family').and_return('RedHat')\n  end\n\n  [4, 5, 6].each do |ver|\n    it \"should be the default provider on rhel#{ver}\" do\n      allow(Facter).to receive(:value).with('os.name').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(ver)\n      expect(provider_class.default?).to be_truthy\n    end\n  end\n\n  it \"should be the default provider on sles11\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.name').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"11\")\n    expect(provider_class.default?).to be_truthy\n  end\n\n  # test self.instances\n  context \"when getting all service instances\" do\n    before :each do\n      @services = ['one', 'two', 'three', 'four', 'kudzu', 'functions', 'halt', 'killall', 'single', 'linuxconf', 'boot', 'reboot']\n      @not_services = ['functions', 'halt', 'killall', 'single', 'linuxconf', 'reboot', 'boot']\n      allow(Dir).to receive(:entries).and_return(@services)\n      allow(Puppet::FileSystem).to receive(:directory?).and_call_original\n      allow(Puppet::FileSystem).to receive(:directory?).with('/etc/init.d').and_return(true)\n      allow(Puppet::FileSystem).to receive(:executable?).and_return(true)\n    end\n\n    it \"should return instances for all services\" do\n      (@services-@not_services).each do |inst|\n        expect(@class).to receive(:new).with(hash_including(name: inst, path: '/etc/init.d')).and_return(\"#{inst}_instance\")\n      end\n      results = (@services-@not_services).collect {|x| \"#{x}_instance\"}\n      expect(@class.instances).to eq(results)\n    end\n\n    it \"should call service status when initialized from provider\" do\n      allow(@resource).to receive(:[]).with(:status).and_return(nil)\n      allow(@provider).to receive(:get).with(:hasstatus).and_return(true)\n      expect(@provider).to receive(:execute)\n        .with(['/sbin/service', 'myservice', 'status'], any_args)\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      @provider.send(:status)\n    end\n  end\n\n  it \"should use '--add' and 'on' when calling enable\" do\n    expect(provider_class).to receive(:chkconfig).with(\"--add\", @resource[:name])\n    expect(provider_class).to receive(:chkconfig).with(@resource[:name], :on)\n    @provider.enable\n  end\n\n  it \"(#15797) should explicitly turn off the service in all run levels\" do\n    expect(provider_class).to receive(:chkconfig).with(\"--level\", \"0123456\", @resource[:name], :off)\n    @provider.disable\n  end\n\n  it \"should have an enabled? method\" do\n    expect(@provider).to respond_to(:enabled?)\n  end\n\n  context \"when checking enabled? on Suse\" do\n    before :each do\n      expect(Facter).to receive(:value).with('os.family').and_return('Suse')\n    end\n\n    it \"should check for on\" do\n      allow(provider_class).to receive(:chkconfig).with(@resource[:name]).and_return(\"#{@resource[:name]}  on\")\n      expect(@provider.enabled?).to eq(:true)\n    end\n\n    it \"should check for B\" do\n      allow(provider_class).to receive(:chkconfig).with(@resource[:name]).and_return(\"#{@resource[:name]}  B\")\n      expect(@provider.enabled?).to eq(:true)\n    end\n\n    it \"should check for off\" do\n      allow(provider_class).to receive(:chkconfig).with(@resource[:name]).and_return(\"#{@resource[:name]}  off\")\n      expect(@provider.enabled?).to eq(:false)\n    end\n\n    it \"should check for unknown service\" do\n      allow(provider_class).to receive(:chkconfig).with(@resource[:name]).and_return(\"#{@resource[:name]}: unknown service\")\n      expect(@provider.enabled?).to eq(:false)\n    end\n  end\n\n  it \"should have an enable method\" do\n    expect(@provider).to respond_to(:enable)\n  end\n\n  it \"should have a disable method\" do\n    expect(@provider).to respond_to(:disable)\n  end\n\n  [:start, :stop, :status, :restart].each do |method|\n    it \"should have a #{method} method\" do\n      expect(@provider).to respond_to(method)\n    end\n\n    describe \"when running #{method}\" do\n      it \"should use any provided explicit command\" do\n        allow(@resource).to receive(:[]).with(method).and_return(\"/user/specified/command\")\n        expect(@provider).to receive(:execute)\n          .with([\"/user/specified/command\"], any_args)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        @provider.send(method)\n      end\n\n      it \"should execute the service script with #{method} when no explicit command is provided\" do\n        allow(@resource).to receive(:[]).with(\"has#{method}\".intern).and_return(:true)\n        expect(@provider).to receive(:execute)\n          .with(['/sbin/service', 'myservice', method.to_s], any_args)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        @provider.send(method)\n      end\n    end\n  end\n\n  context \"when checking status\" do\n    context \"when hasstatus is :true\" do\n      before :each do\n        allow(@resource).to receive(:[]).with(:hasstatus).and_return(:true)\n      end\n\n      it \"should execute the service script with fail_on_failure false\" do\n        expect(@provider).to receive(:execute)\n          .with(['/sbin/service', 'myservice', 'status'], any_args)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        @provider.status\n      end\n\n      it \"should consider the process running if the command returns 0\" do\n        expect(@provider).to receive(:execute)\n          .with(['/sbin/service', 'myservice', 'status'], hash_including(failonfail: false))\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n        expect(@provider.status).to eq(:running)\n      end\n\n      [-10,-1,1,10].each { |ec|\n        it \"should consider the process stopped if the command returns something non-0\" do\n          expect(@provider).to receive(:execute)\n            .with(['/sbin/service', 'myservice', 'status'], hash_including(failonfail: false))\n            .and_return(Puppet::Util::Execution::ProcessOutput.new('', ec))\n          expect(@provider.status).to eq(:stopped)\n        end\n      }\n    end\n\n    context \"when hasstatus is not :true\" do\n      it \"should consider the service :running if it has a pid\" do\n        expect(@provider).to receive(:getpid).and_return(\"1234\")\n        expect(@provider.status).to eq(:running)\n      end\n\n      it \"should consider the service :stopped if it doesn't have a pid\" do\n        expect(@provider).to receive(:getpid).and_return(nil)\n        expect(@provider.status).to eq(:stopped)\n      end\n    end\n  end\n\n  context \"when restarting and hasrestart is not :true\" do\n    it \"should stop and restart the process with the server script\" do\n      expect(@provider).to receive(:execute).with(['/sbin/service', 'myservice', 'stop'], hash_including(failonfail: true))\n      expect(@provider).to receive(:execute).with(['/sbin/service', 'myservice', 'start'], hash_including(failonfail: true))\n      @provider.restart\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/runit_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Runit',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:runit) }\n\n  before(:each) do\n    # Create a mock resource\n    @resource = double('resource')\n\n    @provider = provider_class.new\n    @servicedir = \"/etc/service\"\n    @provider.servicedir=@servicedir\n    @daemondir = \"/etc/sv\"\n    @provider.class.defpath=@daemondir\n\n    # A catch all; no parameters set\n    allow(@resource).to receive(:[]).and_return(nil)\n\n    # But set name, source and path (because we won't run\n    # the thing that will fetch the resource path from the provider)\n    allow(@resource).to receive(:[]).with(:name).and_return(\"myservice\")\n    allow(@resource).to receive(:[]).with(:ensure).and_return(:enabled)\n    allow(@resource).to receive(:[]).with(:path).and_return(@daemondir)\n    allow(@resource).to receive(:ref).and_return(\"Service[myservice]\")\n\n    allow(@provider).to receive(:sv)\n\n    allow(@provider).to receive(:resource).and_return(@resource)\n  end\n\n  it \"should have a restart method\" do\n    expect(@provider).to respond_to(:restart)\n  end\n\n  it \"should have a restartcmd method\" do\n    expect(@provider).to respond_to(:restartcmd)\n  end\n\n  it \"should have a start method\" do\n    expect(@provider).to respond_to(:start)\n  end\n\n  it \"should have a stop method\" do\n    expect(@provider).to respond_to(:stop)\n  end\n\n  it \"should have an enabled? method\" do\n    expect(@provider).to respond_to(:enabled?)\n  end\n\n  it \"should have an enable method\" do\n    expect(@provider).to respond_to(:enable)\n  end\n\n  it \"should have a disable method\" do\n    expect(@provider).to respond_to(:disable)\n  end\n\n  context \"when starting\" do\n    it \"should enable the service if it is not enabled\" do\n      allow(@provider).to receive(:sv)\n\n      expect(@provider).to receive(:enabled?).and_return(:false)\n      expect(@provider).to receive(:enable)\n      allow(@provider).to receive(:sleep)\n\n      @provider.start\n    end\n\n    it \"should execute external command 'sv start /etc/service/myservice'\" do\n      allow(@provider).to receive(:enabled?).and_return(:true)\n      expect(@provider).to receive(:sv).with(\"start\", \"/etc/service/myservice\")\n      @provider.start\n    end\n  end\n\n  context \"when stopping\" do\n    it \"should execute external command 'sv stop /etc/service/myservice'\" do\n      expect(@provider).to receive(:sv).with(\"stop\", \"/etc/service/myservice\")\n      @provider.stop\n    end\n  end\n\n  context \"when restarting\" do\n    it \"should call 'sv restart /etc/service/myservice'\" do\n      expect(@provider).to receive(:sv).with(\"restart\",\"/etc/service/myservice\")\n      @provider.restart\n    end\n  end\n\n  context \"when enabling\" do\n    it \"should create a symlink between daemon dir and service dir\", :if => Puppet.features.manages_symlinks? do\n      daemon_path = File.join(@daemondir,\"myservice\")\n      service_path = File.join(@servicedir,\"myservice\")\n      expect(Puppet::FileSystem).to receive(:symlink?).with(service_path).and_return(false)\n      expect(Puppet::FileSystem).to receive(:symlink).with(daemon_path, File.join(@servicedir,\"myservice\")).and_return(0)\n      @provider.enable\n    end\n  end\n\n  context \"when disabling\" do\n    it \"should remove the '/etc/service/myservice' symlink\" do\n      path = File.join(@servicedir,\"myservice\")\n      allow(FileTest).to receive(:directory?).and_return(false)\n      expect(Puppet::FileSystem).to receive(:symlink?).with(path).and_return(true)\n      expect(Puppet::FileSystem).to receive(:unlink).with(path).and_return(0)\n      @provider.disable\n    end\n  end\n\n  context \"when checking status\" do\n    it \"should call the external command 'sv status /etc/sv/myservice'\" do\n      expect(@provider).to receive(:sv).with('status',File.join(@daemondir,\"myservice\"))\n      @provider.status\n    end\n  end\n\n  context \"when checking status\" do\n    it \"and sv status fails, properly raise a Puppet::Error\" do\n      expect(@provider).to receive(:sv).with('status',File.join(@daemondir,\"myservice\")).and_raise(Puppet::ExecutionFailure, \"fail: /etc/sv/myservice: file not found\")\n      expect { @provider.status }.to raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found')\n    end\n\n    it \"and sv status returns up, then return :running\" do\n      expect(@provider).to receive(:sv).with('status',File.join(@daemondir,\"myservice\")).and_return(\"run: /etc/sv/myservice: (pid 9029) 6s\")\n      expect(@provider.status).to eq(:running)\n    end\n\n    it \"and sv status returns not running, then return :stopped\" do\n      expect(@provider).to receive(:sv).with('status',File.join(@daemondir,\"myservice\")).and_return(\"fail: /etc/sv/myservice: runsv not running\")\n      expect(@provider.status).to eq(:stopped)\n    end\n\n    it \"and sv status returns a warning, then return :stopped\" do\n      expect(@provider).to receive(:sv).with('status',File.join(@daemondir,\"myservice\")).and_return(\"warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist\")\n      expect(@provider.status).to eq(:stopped)\n    end\n  end\n\n  context '.instances' do\n    before do\n      allow(provider_class).to receive(:defpath).and_return(path)\n    end\n\n    context 'when defpath is nil' do\n      let(:path) { nil }\n\n      it 'returns info message' do\n        expect(Puppet).to receive(:info).with(/runit is unsuitable because service directory is nil/)\n        provider_class.instances\n      end\n    end\n\n    context 'when defpath does not exist' do\n      let(:path) { '/inexistent_path' }\n\n      it 'returns notice about missing path' do\n        expect(Puppet).to receive(:notice).with(/Service path #{path} does not exist/)\n        provider_class.instances\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/smf_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Smf',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:smf) }\n\n  def set_resource_params(params = {})\n    params.each do |param, value|\n      if value.nil?\n        @provider.resource.delete(param) if @provider.resource[param]\n      else\n        @provider.resource[param] = value\n      end\n    end\n  end\n\n  before(:each) do\n    # Create a mock resource\n    @resource = Puppet::Type.type(:service).new(\n      :name => \"/system/myservice\", :ensure => :running, :enable => :true)\n    @provider = provider_class.new(@resource)\n\n    allow(FileTest).to receive(:file?).with('/usr/sbin/svcadm').and_return(true)\n    allow(FileTest).to receive(:executable?).with('/usr/sbin/svcadm').and_return(true)\n    allow(FileTest).to receive(:file?).with('/usr/bin/svcs').and_return(true)\n    allow(FileTest).to receive(:executable?).with('/usr/bin/svcs').and_return(true)\n    allow(Facter).to receive(:value).with('os.name').and_return('Solaris')\n    allow(Facter).to receive(:value).with('os.family').and_return('Solaris')\n    allow(Facter).to receive(:value).with('os.release.full').and_return('11.2')\n  end\n  context \".instances\" do\n    it \"should have an instances method\" do\n      expect(provider_class).to respond_to :instances\n    end\n\n    it \"should get a list of services (excluding legacy)\" do\n      expect(provider_class).to receive(:svcs).with('-H', '-o', 'state,fmri').and_return(File.read(my_fixture('svcs_instances.out')))\n      instances = provider_class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure)} }\n      # we dont manage legacy\n      expect(instances.size).to eq(3)\n      expect(instances[0]).to eq({:name => 'svc:/system/svc/restarter:default', :ensure => :running })\n      expect(instances[1]).to eq({:name => 'svc:/network/cswrsyncd:default', :ensure => :maintenance })\n      expect(instances[2]).to eq({:name => 'svc:/network/dns/client:default', :ensure => :degraded })\n    end\n  end\n\n  describe '#service_exists?' do\n    it 'returns true if the service exists' do\n      expect(@provider).to receive(:service_fmri)\n      expect(@provider.service_exists?).to be(true)\n    end\n\n    it 'returns false if the service does not exist' do\n      expect(@provider).to receive(:service_fmri).and_raise(\n        Puppet::ExecutionFailure, 'svcs failed!'\n      )\n\n      expect(@provider.service_exists?).to be(false)\n    end\n  end\n\n  describe '#setup_service' do\n    it 'noops if the service resource does not have the manifest parameter passed-in' do\n      expect(@provider).not_to receive(:svccfg)\n\n      set_resource_params({ :manifest => nil })\n      @provider.setup_service\n    end\n\n    context 'when the service resource has a manifest parameter passed-in' do\n      let(:manifest) { 'foo' }\n      before(:each) { set_resource_params({ :manifest => manifest }) }\n\n      it 'noops if the service resource already exists' do\n        expect(@provider).not_to receive(:svccfg)\n\n        expect(@provider).to receive(:service_exists?).and_return(true)\n        @provider.setup_service\n      end\n\n      it \"imports the service resource's manifest\" do\n        expect(@provider).to receive(:service_exists?).and_return(false)\n\n        expect(@provider).to receive(:svccfg).with(:import, manifest)\n        @provider.setup_service\n      end\n\n      it 'raises a Puppet::Error if SMF fails to import the manifest' do\n        expect(@provider).to receive(:service_exists?).and_return(false)\n\n        failure_reason = 'svccfg failed!'\n        expect(@provider).to receive(:svccfg).with(:import, manifest).and_raise(Puppet::ExecutionFailure, failure_reason)\n        expect { @provider.setup_service }.to raise_error do |error|\n          expect(error).to be_a(Puppet::Error)\n          expect(error.message).to match(failure_reason)\n        end\n      end\n    end\n  end\n\n  describe '#service_fmri' do\n    it 'returns the memoized the fmri if it exists' do\n      @provider.instance_variable_set(:@fmri, 'resource_fmri')\n      expect(@provider.service_fmri).to eql('resource_fmri')\n    end\n\n    it 'raises a Puppet::Error if the service resource matches multiple FMRIs' do\n      expect(@provider).to receive(:svcs).with('-l', @provider.resource[:name]).and_return(File.read(my_fixture('svcs_multiple_fmris.out')))\n\n      expect { @provider.service_fmri }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n        expect(error.message).to match(@provider.resource[:name])\n        expect(error.message).to match('multiple')\n\n        matched_fmris = [\"svc:/application/tstapp:one\", \"svc:/application/tstapp:two\"]\n        expect(error.message).to match(matched_fmris.join(', '))\n      end\n    end\n\n    it 'raises a Puppet:ExecutionFailure if svcs fails' do\n      expect(@provider).to receive(:svcs).with('-l', @provider.resource[:name]).and_raise(\n        Puppet::ExecutionFailure, 'svcs failed!'\n      )\n\n      expect { @provider.service_fmri }.to raise_error do |error|\n        expect(error).to be_a(Puppet::ExecutionFailure)\n        expect(error.message).to match('svcs failed!')\n      end\n    end\n\n    it \"returns the service resource's fmri and memoizes it\" do\n      expect(@provider).to receive(:svcs).with('-l', @provider.resource[:name]).and_return(File.read(my_fixture('svcs_fmri.out')))\n\n      expected_fmri = 'svc:/application/tstapp:default'\n\n      expect(@provider.service_fmri).to eql(expected_fmri)\n      expect(@provider.instance_variable_get(:@fmri)).to eql(expected_fmri)\n    end\n  end\n\n  describe '#enabled?' do\n    let(:fmri) { 'resource_fmri' }\n    before(:each) do\n      allow(@provider).to receive(:service_fmri).and_return(fmri)\n    end\n\n    it 'returns :true if the service is enabled' do\n      expect(@provider).to receive(:svccfg).with('-s', fmri, 'listprop', 'general/enabled').and_return(\n        'general/enabled boolean  true'\n      )\n\n      expect(@provider.enabled?).to be(:true)\n    end\n\n    it 'return :false if the service is not enabled' do\n      expect(@provider).to receive(:svccfg).with('-s', fmri, 'listprop', 'general/enabled').and_return(\n        'general/enabled boolean  false'\n      )\n\n      expect(@provider.enabled?).to be(:false)\n    end\n\n    it 'returns :false if the service does not exist' do\n      expect(@provider).to receive(:service_exists?).and_return(false)\n      expect(@provider.enabled?).to be(:false)\n    end\n  end\n\n  describe '#restartcmd' do\n    let(:fmri) { 'resource_fmri' }\n    before(:each) do\n      allow(@provider).to receive(:service_fmri).and_return(fmri)\n    end\n\n    it 'returns the right command for restarting the service for Solaris versions newer than 11.2' do\n      expect(Facter).to receive(:value).with('os.release.full').and_return('11.3')\n\n      expect(@provider.restartcmd).to eql([@provider.command(:adm), :restart, '-s', fmri])\n    end\n\n    it 'returns the right command for restarting the service on Solaris 11.2' do\n      expect(Facter).to receive(:value).with('os.release.full').and_return('11.2')\n\n      expect(@provider.restartcmd).to eql([@provider.command(:adm), :restart, '-s', fmri])\n    end\n\n    it 'returns the right command for restarting the service for Solaris versions older than Solaris 11.2' do\n      expect(Facter).to receive(:value).with('os.release.full').and_return('10.3')\n\n      expect(@provider.restartcmd).to eql([@provider.command(:adm), :restart, fmri])\n    end\n  end\n\n  describe '#service_states' do\n    let(:fmri) { 'resource_fmri' }\n    before(:each) do\n      allow(@provider).to receive(:service_fmri).and_return(fmri)\n    end\n\n    it 'returns the current and next states of the service' do\n      expect(@provider).to receive(:svcs).with('-H', '-o', 'state,nstate', fmri).and_return(\n        'online         disabled'\n      )\n\n      expect(@provider.service_states).to eql({ :current => 'online', :next => 'disabled' })\n    end\n\n    it \"returns nil for the next state if svcs marks it as '-'\" do\n      expect(@provider).to receive(:svcs).with('-H', '-o', 'state,nstate', fmri).and_return(\n        'online         -'\n      )\n\n      expect(@provider.service_states).to eql({ :current => 'online', :next => nil })\n    end\n  end\n\n  describe '#wait' do\n    # TODO: Document this method!\n    def transition_service(from, to, tries)\n      intermediate_returns = [{ :current => from, :next => to }] * (tries - 1)\n      final_return = { :current => to, :next => nil }\n\n      allow(@provider).to receive(:service_states).and_return(*intermediate_returns.push(final_return))\n    end\n\n    before(:each) do\n      allow(Timeout).to receive(:timeout).and_yield\n      allow(Kernel).to receive(:sleep)\n    end\n\n    it 'waits for the service to enter the desired state' do\n      transition_service('online', 'disabled', 1)\n      @provider.wait('offline', 'disabled', 'uninitialized')\n    end\n\n    it 'times out and raises a Puppet::Error after sixty seconds' do\n      expect(Timeout).to receive(:timeout).with(60).and_raise(Timeout::Error, 'method timed out!')\n\n      expect { @provider.wait('online') }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n        expect(error.message).to match(@provider.resource[:name])\n      end\n    end\n\n    it 'sleeps a bit before querying the service state' do\n      transition_service('disabled', 'online', 10)\n      expect(Kernel).to receive(:sleep).with(1).exactly(9).times\n\n      @provider.wait('online')\n    end\n  end\n\n  describe '#restart' do\n    let(:fmri) { 'resource_fmri' }\n\n    before(:each) do\n      allow(@provider).to receive(:service_fmri).and_return(fmri)\n      allow(@provider).to receive(:execute)\n      allow(@provider).to receive(:wait)\n    end\n\n    it 'should restart the service' do\n      expect(@provider).to receive(:execute)\n      @provider.restart\n    end\n\n    it 'should wait for the service to restart' do\n      expect(@provider).to receive(:wait).with('online')\n      @provider.restart\n    end\n  end\n\n  describe '#status' do\n    let(:states) do\n      {\n        :current => 'online',\n        :next    => nil\n      }\n    end\n\n    before(:each) do\n      allow(@provider).to receive(:service_states).and_return(states)\n\n      allow(Facter).to receive(:value).with('os.release.full').and_return('10.3')\n    end\n\n    it \"should run the status command if it's passed in\" do\n      set_resource_params({ :status => 'status_cmd' })\n      expect(@provider).to receive(:execute)\n        .with([\"status_cmd\"], hash_including(failonfail: false))\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      expect(@provider).not_to receive(:service_states)\n\n      expect(@provider.status).to eql(:running)\n    end\n\n    shared_examples 'returns the right status' do |svcs_state, expected_state|\n      it \"returns '#{expected_state}' if the svcs state is '#{svcs_state}'\" do\n\n        states[:current] = svcs_state\n        expect(@provider.status).to eql(expected_state)\n      end\n    end\n\n    include_examples 'returns the right status', 'online', :running\n    include_examples 'returns the right status', 'offline', :stopped\n    include_examples 'returns the right status', 'disabled', :stopped\n    include_examples 'returns the right status', 'uninitialized', :stopped\n    include_examples 'returns the right status', 'maintenance', :maintenance\n    include_examples 'returns the right status', 'degraded', :degraded\n\n    it \"raises a Puppet::Error if the svcs state is 'legacy_run'\" do\n      states[:current] = 'legacy_run'\n      expect { @provider.status }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n        expect(error.message).to match('legacy')\n      end\n    end\n\n    it \"raises a Puppet::Error if the svcs state is unmanageable\" do\n      states[:current] = 'unmanageable state'\n      expect { @provider.status }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n        expect(error.message).to match(states[:current])\n      end\n    end\n\n    it \"returns 'stopped' if the service does not exist\" do\n      expect(@provider).to receive(:service_states).and_raise(Puppet::ExecutionFailure, 'service does not exist!')\n      expect(@provider.status).to eql(:stopped)\n    end\n\n    it \"uses the current state for comparison if the next state is not provided\" do\n      states[:next] = 'disabled'\n      expect(@provider.status).to eql(:stopped)\n    end\n\n    it \"should return stopped for an incomplete service on Solaris 11\" do\n      allow(Facter).to receive(:value).with('os.release.full').and_return('11.3')\n      allow(@provider).to receive(:complete_service?).and_return(false)\n      allow(@provider).to receive(:svcs).with('-l', @provider.resource[:name]).and_return(File.read(my_fixture('svcs_fmri.out')))\n      expect(@provider.status).to eq(:stopped)\n    end\n  end\n\n  describe '#maybe_clear_service_then_svcadm' do\n    let(:fmri) { 'resource_fmri' }\n    before(:each) do\n      allow(@provider).to receive(:service_fmri).and_return(fmri)\n    end\n\n    it 'applies the svcadm subcommand with the given flags' do\n      expect(@provider).to receive(:adm).with('enable', '-rst', fmri)\n      @provider.maybe_clear_service_then_svcadm(:stopped, 'enable', '-rst')\n    end\n\n    [:maintenance, :degraded].each do |status|\n      it \"clears the service before applying the svcadm subcommand if the service status is #{status}\" do\n        expect(@provider).to receive(:adm).with('clear', fmri)\n        expect(@provider).to receive(:adm).with('enable', '-rst', fmri)\n\n        @provider.maybe_clear_service_then_svcadm(status, 'enable', '-rst')\n      end\n    end\n  end\n\n  describe '#flush' do\n    def mark_property_for_syncing(property, value)\n      properties_to_sync = @provider.instance_variable_get(:@properties_to_sync)\n      properties_to_sync[property] = value\n    end\n\n    it 'should noop if enable and ensure do not need to be syncd' do\n      expect(@provider).not_to receive(:setup_service)\n      @provider.flush\n    end\n\n    context 'enable or ensure need to be syncd' do\n      let(:stopped_states) do\n        ['offline', 'disabled', 'uninitialized']\n      end\n\n      let(:fmri) { 'resource_fmri' }\n      let(:mock_status) { :maintenance }\n      before(:each) do\n        allow(@provider).to receive(:setup_service)\n        allow(@provider).to receive(:service_fmri).and_return(fmri)\n\n        # We will update this mock on a per-test basis.\n        allow(@provider).to receive(:status).and_return(mock_status)\n        allow(@provider).to receive(:wait)\n      end\n\n      context 'only ensure needs to be syncd' do\n        it 'stops the service if ensure == stopped' do\n          mark_property_for_syncing(:ensure, :stopped)\n\n          expect(@provider).to receive(:maybe_clear_service_then_svcadm).with(mock_status, 'disable', '-st')\n          expect(@provider).to receive(:wait).with(*stopped_states)\n\n          @provider.flush\n        end\n\n        it 'starts the service if ensure == running' do\n          mark_property_for_syncing(:ensure, :running)\n\n          expect(@provider).to receive(:maybe_clear_service_then_svcadm).with(mock_status, 'enable', '-rst')\n          expect(@provider).to receive(:wait).with('online')\n\n          @provider.flush\n        end\n      end\n\n      context 'enable needs to be syncd' do\n        before(:each) do\n          # We will stub this value out later, this default is useful\n          # for the final state tests.\n          mark_property_for_syncing(:enable, true)\n        end\n\n        it 'enables the service' do\n          mark_property_for_syncing(:enable, true)\n\n          expect(@provider).to receive(:maybe_clear_service_then_svcadm).with(mock_status, 'enable', '-rs')\n\n          expect(@provider).to receive(:adm).with('mark', '-I', 'maintenance', fmri)\n\n          @provider.flush\n        end\n\n        it 'disables the service' do\n          mark_property_for_syncing(:enable, false)\n\n          expect(@provider).to receive(:maybe_clear_service_then_svcadm).with(mock_status, 'disable', '-s')\n\n          expect(@provider).to receive(:adm).with('mark', '-I', 'maintenance', fmri)\n\n          @provider.flush\n        end\n\n        context 'when the final service state is running' do\n          before(:each) do\n            allow(@provider).to receive(:status).and_return(:running)\n          end\n\n          it 'starts the service if enable was false' do\n            mark_property_for_syncing(:enable, false)\n\n            expect(@provider).to receive(:adm).with('disable', '-s', fmri)\n            expect(@provider).to receive(:adm).with('enable', '-rst', fmri)\n            expect(@provider).to receive(:wait).with('online')\n\n            @provider.flush\n          end\n\n          it 'waits for the service to start if enable was true' do\n            mark_property_for_syncing(:enable, true)\n\n            expect(@provider).to receive(:adm).with('enable', '-rs', fmri)\n            expect(@provider).to receive(:wait).with('online')\n\n            @provider.flush\n          end\n        end\n\n        context 'when the final service state is stopped' do\n          before(:each) do\n            allow(@provider).to receive(:status).and_return(:stopped)\n          end\n\n          it 'stops the service if enable was true' do\n            mark_property_for_syncing(:enable, true)\n\n            expect(@provider).to receive(:adm).with('enable', '-rs', fmri)\n            expect(@provider).to receive(:adm).with('disable', '-st', fmri)\n            expect(@provider).to receive(:wait).with(*stopped_states)\n\n            @provider.flush\n          end\n\n          it 'waits for the service to stop if enable was false' do\n            mark_property_for_syncing(:enable, false)\n\n            expect(@provider).to_not receive(:adm).with('disable', '-st', fmri)\n            expect(@provider).to receive(:wait).with(*stopped_states)\n\n            @provider.flush\n          end\n        end\n\n        it 'marks the service as being under maintenance if the final state is maintenance' do\n          expect(@provider).to receive(:status).and_return(:maintenance)\n\n          expect(@provider).to receive(:adm).with('clear', fmri)\n          expect(@provider).to receive(:adm).with('enable', '-rs', fmri)\n\n          expect(@provider).to receive(:adm).with('mark', '-I', 'maintenance', fmri)\n          expect(@provider).to receive(:wait).with('maintenance')\n\n          @provider.flush\n        end\n\n        it 'uses the ensure value as the final state if ensure also needs to be syncd' do\n          mark_property_for_syncing(:ensure, :running)\n          expect(@provider).to receive(:status).and_return(:stopped)\n\n          expect(@provider).to receive(:adm).with('enable', '-rs', fmri)\n          expect(@provider).to receive(:wait).with('online')\n\n          @provider.flush\n        end\n\n        it 'marks the final state of a degraded service as running' do\n          expect(@provider).to receive(:status).and_return(:degraded)\n\n          expect(@provider).to receive(:adm).with('clear', fmri)\n          expect(@provider).to receive(:adm).with('enable', '-rs', fmri)\n\n          expect(@provider).to receive(:wait).with('online')\n\n          @provider.flush\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/src_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Src',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:src) }\n\n  before :each do\n    @resource = double('resource')\n    allow(@resource).to receive(:[]).and_return(nil)\n    allow(@resource).to receive(:[]).with(:name).and_return(\"myservice\")\n\n    @provider = provider_class.new\n    @provider.resource = @resource\n\n    allow(@provider).to receive(:command).with(:stopsrc).and_return(\"/usr/bin/stopsrc\")\n    allow(@provider).to receive(:command).with(:startsrc).and_return(\"/usr/bin/startsrc\")\n    allow(@provider).to receive(:command).with(:lssrc).and_return(\"/usr/bin/lssrc\")\n    allow(@provider).to receive(:command).with(:refresh).and_return(\"/usr/bin/refresh\")\n    allow(@provider).to receive(:command).with(:lsitab).and_return(\"/usr/sbin/lsitab\")\n    allow(@provider).to receive(:command).with(:mkitab).and_return(\"/usr/sbin/mkitab\")\n    allow(@provider).to receive(:command).with(:rmitab).and_return(\"/usr/sbin/rmitab\")\n    allow(@provider).to receive(:command).with(:chitab).and_return(\"/usr/sbin/chitab\")\n\n    allow(@provider).to receive(:stopsrc)\n    allow(@provider).to receive(:startsrc)\n    allow(@provider).to receive(:lssrc)\n    allow(@provider).to receive(:refresh)\n    allow(@provider).to receive(:lsitab)\n    allow(@provider).to receive(:mkitab)\n    allow(@provider).to receive(:rmitab)\n    allow(@provider).to receive(:chitab)\n  end\n\n  context \".instances\" do\n    it \"should have a .instances method\" do\n      expect(provider_class).to respond_to :instances\n    end\n\n    it \"should get a list of running services\" do\n      sample_output = <<_EOF_\n#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname:\nmyservice.1:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip:\nmyservice.2:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip:\nmyservice.3:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip:\nmyservice.4:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip:\n_EOF_\n      allow(provider_class).to receive(:lssrc).and_return(sample_output)\n      expect(provider_class.instances.map(&:name)).to eq([\n        'myservice.1',\n        'myservice.2',\n        'myservice.3',\n        'myservice.4'\n      ])\n    end\n  end\n\n  context \"when starting a service\" do\n    it \"should execute the startsrc command\" do\n      expect(@provider).to receive(:execute).with(['/usr/bin/startsrc', '-s', \"myservice\"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true})\n      expect(@provider).to receive(:status).and_return(:running)\n      @provider.start\n    end\n\n    it \"should error if timeout occurs while stopping the service\" do\n      expect(@provider).to receive(:execute).with(['/usr/bin/startsrc', '-s', \"myservice\"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true})\n      expect(Timeout).to receive(:timeout).with(60).and_raise(Timeout::Error)\n      expect { @provider.start }.to raise_error Puppet::Error, ('Timed out waiting for myservice to transition states')\n    end\n  end\n\n  context \"when stopping a service\" do\n    it \"should execute the stopsrc command\" do\n      expect(@provider).to receive(:execute).with(['/usr/bin/stopsrc', '-s', \"myservice\"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true})\n      expect(@provider).to receive(:status).and_return(:stopped)\n      @provider.stop\n    end\n\n    it \"should error if timeout occurs while stopping the service\" do\n      expect(@provider).to receive(:execute).with(['/usr/bin/stopsrc', '-s', \"myservice\"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true})\n      expect(Timeout).to receive(:timeout).with(60).and_raise(Timeout::Error)\n      expect { @provider.stop }.to raise_error Puppet::Error, ('Timed out waiting for myservice to transition states')\n    end\n  end\n\n  context \"should have a set of methods\" do\n    [:enabled?, :enable, :disable, :start, :stop, :status, :restart].each do |method|\n      it \"should have a #{method} method\" do\n        expect(@provider).to respond_to(method)\n      end\n    end\n  end\n\n  context \"when enabling\" do\n    it \"should execute the mkitab command\" do\n      expect(@provider).to receive(:mkitab).with(\"myservice:2:once:/usr/bin/startsrc -s myservice\").once\n      @provider.enable\n    end\n  end\n\n  context \"when disabling\" do\n    it \"should execute the rmitab command\" do\n      expect(@provider).to receive(:rmitab).with(\"myservice\")\n      @provider.disable\n    end\n  end\n\n  context \"when checking if it is enabled\" do\n    it \"should execute the lsitab command\" do\n      expect(@provider).to receive(:execute)\n        .with(['/usr/sbin/lsitab', 'myservice'], {:combine => true, :failonfail => false})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      @provider.enabled?\n    end\n\n    it \"should return false when lsitab returns non-zero\" do\n      expect(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', 1))\n      expect(@provider.enabled?).to eq(:false)\n    end\n\n    it \"should return true when lsitab returns zero\" do\n      allow(@provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))\n      expect(@provider.enabled?).to eq(:true)\n    end\n  end\n\n  context \"when checking a subsystem's status\" do\n    it \"should execute status and return running if the subsystem is active\" do\n      sample_output = <<_EOF_\n  Subsystem         Group            PID          Status\n  myservice         tcpip            1234         active\n_EOF_\n\n      expect(@provider).to receive(:execute).with(['/usr/bin/lssrc', '-s', \"myservice\"]).and_return(sample_output)\n      expect(@provider.status).to eq(:running)\n    end\n\n    it \"should execute status and return stopped if the subsystem is inoperative\" do\n      sample_output = <<_EOF_\n  Subsystem         Group            PID          Status\n  myservice         tcpip                         inoperative\n_EOF_\n\n      expect(@provider).to receive(:execute).with(['/usr/bin/lssrc', '-s', \"myservice\"]).and_return(sample_output)\n      expect(@provider.status).to eq(:stopped)\n    end\n\n    it \"should execute status and return nil if the status is not known\" do\n      sample_output = <<_EOF_\n  Subsystem         Group            PID          Status\n  myservice         tcpip                         randomdata\n_EOF_\n\n      expect(@provider).to receive(:execute).with(['/usr/bin/lssrc', '-s', \"myservice\"]).and_return(sample_output)\n      expect(@provider.status).to eq(nil)\n    end\n\n    it \"should consider a non-existing service to be have a status of :stopped\" do\n      expect(@provider).to receive(:execute).with(['/usr/bin/lssrc', '-s', 'myservice']).and_raise(Puppet::ExecutionFailure, \"fail\")\n      expect(@provider.status).to eq(:stopped)\n    end\n  end\n\n  context \"when restarting a service\" do\n    it \"should execute restart which runs refresh\" do\n      sample_output = <<_EOF_\n#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname:\nmyservice:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip:\n_EOF_\n      expect(@provider).to receive(:execute).with(['/usr/bin/lssrc', '-Ss', \"myservice\"]).and_return(sample_output)\n      expect(@provider).to receive(:execute).with(['/usr/bin/refresh', '-s', \"myservice\"])\n      @provider.restart\n    end\n\n    it \"should execute restart which runs stop then start\" do\n      sample_output =  <<_EOF_\n#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname:\nmyservice::--no-daemonize:/usr/sbin/puppetd:0:0:/dev/null:/var/log/puppet.log:/var/log/puppet.log:-O:-Q:-S:0:0:20:15:9:-d:20::\"\n_EOF_\n\n      expect(@provider).to receive(:execute).with(['/usr/bin/lssrc', '-Ss', \"myservice\"]).and_return(sample_output)\n      expect(@provider).to receive(:stop)\n      expect(@provider).to receive(:start)\n      @provider.restart\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/systemd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Systemd',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n\n  let(:provider_class) { Puppet::Type.type(:service).provider(:systemd) }\n\n  before :each do\n    allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class)\n    allow(provider_class).to receive(:which).with('systemctl').and_return('/bin/systemctl')\n  end\n\n  let :provider do\n    provider_class.new(:name => 'sshd.service')\n  end\n\n  let :process_output do\n    Puppet::Util::Execution::ProcessOutput.new('', 0)\n  end\n\n  osfamilies = %w[archlinux coreos gentoo]\n\n  osfamilies.each do |osfamily|\n    it \"should be the default provider on #{osfamily}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(osfamily)\n      allow(Facter).to receive(:value).with('os.name').and_return(osfamily)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"1234\")\n      expect(provider_class).to be_default\n    end\n  end\n\n  [7, 8, 9].each do |ver|\n    it \"should be the default provider on rhel#{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(ver.to_s)\n      expect(provider_class).to be_default\n    end\n  end\n\n  [4, 5, 6].each do |ver|\n    it \"should not be the default provider on rhel#{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n      expect(provider_class).not_to be_default\n    end\n  end\n\n  (17..23).to_a.each do |ver|\n    it \"should be the default provider on fedora#{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:fedora)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n      expect(provider_class).to be_default\n    end\n  end\n\n  [2, 2023].each do |ver|\n    it \"should be the default provider on Amazon Linux #{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n      allow(Facter).to receive(:value).with('os.name').and_return(:amazon)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n      expect(provider_class).to be_default\n    end\n  end\n\n  it \"should not be the default provider on Amazon Linux 2017.09\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n    allow(Facter).to receive(:value).with('os.name').and_return(:amazon)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"2017\")\n    expect(provider_class).not_to be_default\n  end\n\n  it \"should be the default provider on cumulus3\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return('CumulusLinux')\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"3\")\n    expect(provider_class).to be_default\n  end\n\n  it \"should be the default provider on sles12\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.name').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"12\")\n    expect(provider_class).to be_default\n  end\n\n  it \"should be the default provider on opensuse13\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.name').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"13\")\n    expect(provider_class).to be_default\n  end\n\n  # tumbleweed is a rolling release with date-based major version numbers\n  it \"should be the default provider on tumbleweed\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.name').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"20150829\")\n    expect(provider_class).to be_default\n  end\n\n  # leap is the next generation suse release\n  it \"should be the default provider on leap\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:suse)\n    allow(Facter).to receive(:value).with('os.name').and_return(:leap)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"42\")\n    expect(provider_class).to be_default\n  end\n\n  it \"should not be the default provider on debian7\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"7\")\n    expect(provider_class).not_to be_default\n  end\n\n  it \"should be the default provider on debian8\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"8\")\n    expect(provider_class).to be_default\n  end\n\n  it \"should be the default provider on debian11\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"11\")\n    expect(provider_class).to be_default\n  end\n\n  it \"should be the default provider on debian bookworm/sid\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"bookworm/sid\")\n    expect(provider_class).to be_default\n  end\n\n  it \"should not be the default provider on ubuntu14.04\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return(:ubuntu)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"14.04\")\n    expect(provider_class).not_to be_default\n  end\n\n  %w[15.04 15.10 16.04 16.10 17.04 17.10 18.04].each do |ver|\n    it \"should be the default provider on ubuntu#{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n      allow(Facter).to receive(:value).with('os.name').and_return(:ubuntu)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n      expect(provider_class).to be_default\n    end\n  end\n\n  ('10'..'17').to_a.each do |ver|\n    it \"should not be the default provider on LinuxMint#{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n      allow(Facter).to receive(:value).with('os.name').and_return(:LinuxMint)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n      expect(provider_class).not_to be_default\n    end\n  end\n\n  ['18', '19'].each do |ver|\n    it \"should be the default provider on LinuxMint#{ver}\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n      allow(Facter).to receive(:value).with('os.name').and_return(:LinuxMint)\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"#{ver}\")\n      expect(provider_class).to be_default\n    end\n  end\n\n  it \"should be the default provider on raspbian12\" do\n    allow(Facter).to receive(:value).with('os.family').and_return(:debian)\n    allow(Facter).to receive(:value).with('os.name').and_return(:raspbian)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"12\")\n    expect(provider_class).to be_default\n  end\n\n  %i[enabled? daemon_reload? enable disable start stop status restart].each do |method|\n    it \"should have a #{method} method\" do\n      expect(provider).to respond_to(method)\n    end\n  end\n\n  describe \".instances\" do\n    it \"should have an instances method\" do\n      expect(provider_class).to respond_to :instances\n    end\n\n    it \"should return only services\" do\n      expect(provider_class).to receive(:systemctl).with('list-unit-files', '--type', 'service', '--full', '--all', '--no-pager').and_return(File.read(my_fixture('list_unit_files_services')))\n      expect(provider_class.instances.map(&:name)).to match_array(%w{\n        arp-ethers.service\n        auditd.service\n        autovt@.service\n        avahi-daemon.service\n        blk-availability.service\n        apparmor.service\n        umountnfs.service\n        urandom.service\n        brandbot.service\n      })\n    end\n\n    it \"correctly parses services when list-unit-files has an additional column\" do\n      expect(provider_class).to receive(:systemctl).with('list-unit-files', '--type', 'service', '--full', '--all', '--no-pager').and_return(File.read(my_fixture('list_unit_files_services_vendor_preset')))\n      expect(provider_class.instances.map(&:name)).to match_array(%w{\n        arp-ethers.service\n        auditd.service\n        dbus.service\n        umountnfs.service\n        urandom.service\n      })\n    end\n\n    it \"should print a debug message when a service with the state `bad` is found\" do\n      expect(provider_class).to receive(:systemctl).with('list-unit-files', '--type', 'service', '--full', '--all', '--no-pager').and_return(File.read(my_fixture('list_unit_files_services')))\n      expect(Puppet).to receive(:debug).with(\"apparmor.service marked as bad by `systemctl`. It is recommended to be further checked.\")\n      provider_class.instances\n    end\n  end\n\n  describe \"#start\" do\n    it \"should use the supplied start command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :start => '/bin/foo'))\n      expect(provider).to receive(:daemon_reload?).and_return('no')\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n\n    it \"should start the service with systemctl start otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:systemctl).with(:unmask, '--', 'sshd.service')\n      expect(provider).to receive(:daemon_reload?).and_return('no')\n      expect(provider).to receive(:execute).with(['/bin/systemctl','start', '--', 'sshd.service'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.start\n    end\n\n    it \"should show journald logs on failure\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:systemctl).with(:unmask, '--', 'sshd.service')\n      expect(provider).to receive(:daemon_reload?).and_return('no')\n      expect(provider).to receive(:execute).with(['/bin/systemctl','start', '--', 'sshd.service'],{:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n        .and_raise(Puppet::ExecutionFailure, \"Failed to start sshd.service: Unit sshd.service failed to load: Invalid argument. See system logs and 'systemctl status sshd.service' for details.\")\n      journalctl_logs = <<-EOS\n-- Logs begin at Tue 2016-06-14 11:59:21 UTC, end at Tue 2016-06-14 21:45:02 UTC. --\nJun 14 21:41:34 foo.example.com systemd[1]: Stopping sshd Service...\nJun 14 21:41:35 foo.example.com systemd[1]: Starting sshd Service...\nJun 14 21:43:23 foo.example.com systemd[1]: sshd.service lacks both ExecStart= and ExecStop= setting. Refusing.\n      EOS\n      expect(provider).to receive(:execute).with(\"journalctl -n 50 --since '5 minutes ago' -u sshd.service --no-pager\").and_return(journalctl_logs)\n      expect { provider.start }.to raise_error(Puppet::Error, /Systemd start for sshd.service failed![\\n]+journalctl log for sshd.service:[\\n]+-- Logs begin at Tue 2016-06-14 11:59:21 UTC, end at Tue 2016-06-14 21:45:02 UTC. --/m)\n    end\n  end\n\n  describe \"#stop\" do\n    it \"should use the supplied stop command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :stop => '/bin/foo'))\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n\n    it \"should stop the service with systemctl stop otherwise\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','stop', '--', 'sshd.service'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.stop\n    end\n\n    it \"should show journald logs on failure\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','stop', '--', 'sshd.service'],{:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n        .and_raise(Puppet::ExecutionFailure, \"Failed to stop sshd.service: Unit sshd.service failed to load: Invalid argument. See system logs and 'systemctl status sshd.service' for details.\")\n      journalctl_logs = <<-EOS\n-- Logs begin at Tue 2016-06-14 11:59:21 UTC, end at Tue 2016-06-14 21:45:02 UTC. --\nJun 14 21:41:34 foo.example.com systemd[1]: Stopping sshd Service...\nJun 14 21:41:35 foo.example.com systemd[1]: Starting sshd Service...\nJun 14 21:43:23 foo.example.com systemd[1]: sshd.service lacks both ExecStart= and ExecStop= setting. Refusing.\n      EOS\n      expect(provider).to receive(:execute).with(\"journalctl -n 50 --since '5 minutes ago' -u sshd.service --no-pager\").and_return(journalctl_logs)\n      expect { provider.stop }.to raise_error(Puppet::Error, /Systemd stop for sshd.service failed![\\n]+journalctl log for sshd.service:[\\n]-- Logs begin at Tue 2016-06-14 11:59:21 UTC, end at Tue 2016-06-14 21:45:02 UTC. --/m)\n    end\n  end\n\n  describe \"#daemon_reload?\" do\n    it \"should skip the systemctl daemon_reload if not required by the service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl', 'show', '--property=NeedDaemonReload', '--', 'sshd.service'], {:failonfail => false}).and_return(\"no\")\n      provider.daemon_reload?\n    end\n    it \"should run a systemctl daemon_reload if the service has been modified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl', 'show', '--property=NeedDaemonReload', '--', 'sshd.service'], {:failonfail => false}).and_return(\"yes\")\n      expect(provider).to receive(:execute).with(['/bin/systemctl', 'daemon-reload'], {:failonfail => false})\n      provider.daemon_reload?\n    end\n  end\n\n  describe \"#enabled?\" do\n    it \"should return :true if the service is enabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled', '--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"enabled\\n\", 0))\n      expect(provider.enabled?).to eq(:true)\n    end\n\n    it \"should return :true if the service is static\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled','--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"static\\n\", 0))\n      expect(provider.enabled?).to eq(:true)\n    end\n\n    it \"should return :false if the service is disabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled', '--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"disabled\\n\", 1))\n      expect(provider.enabled?).to eq(:false)\n    end\n\n    it \"should return :false if the service is indirect\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled', '--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"indirect\\n\", 0))\n      expect(provider.enabled?).to eq(:false)\n    end\n\n    it \"should return :false if the service is masked and the resource is attempting to be disabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :enable => false))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled', '--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"masked\\n\", 1))\n      expect(provider.enabled?).to eq(:false)\n    end\n\n    it \"should return :mask if the service is masked and the resource is attempting to be masked\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :enable => 'mask'))\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled', '--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"masked\\n\", 1))\n      expect(provider.enabled?).to eq(:mask)\n    end\n\n    it \"should consider nonexistent services to be disabled\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'doesnotexist'))\n      allow(Facter).to receive(:value).with('os.family').and_return('debian')\n      expect(provider).to receive(:execute).with(['/bin/systemctl','is-enabled', '--', 'doesnotexist'], {:failonfail => false})\n                            .and_return(Puppet::Util::Execution::ProcessOutput.new(\"\", 1))\n      expect(provider).to receive(:execute).with([\"/usr/sbin/invoke-rc.d\", \"--quiet\", \"--query\", \"doesnotexist\", \"start\"], {:failonfail => false})\n                            .and_return(Puppet::Util::Execution::ProcessOutput.new(\"\", 1))\n\n      expect(provider.enabled?).to be(:false)\n    end\n  end\n\n  describe \"#enable\" do\n    it \"should run systemctl enable to enable a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:systemctl).with(:unmask, '--', 'sshd.service')\n      expect(provider).to receive(:systemctl).with(:enable, '--', 'sshd.service')\n      provider.enable\n    end\n  end\n\n  describe \"#disable\" do\n    it \"should run systemctl disable to disable a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:systemctl).with(:disable, '--', 'sshd.service')\n      provider.disable\n    end\n  end\n\n  describe \"#mask\" do\n    it \"should run systemctl to disable and mask a service\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute).\n                            with(['/bin/systemctl','cat', '--', 'sshd.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"# /lib/systemd/system/sshd.service\\n...\", 0))\n      # :disable is the only call in the provider that uses a symbol instead of\n      # a string.\n      # This should be made consistent in the future and all tests updated.\n      expect(provider).to receive(:systemctl).with(:disable, '--', 'sshd.service')\n      expect(provider).to receive(:systemctl).with(:mask, '--', 'sshd.service')\n      provider.mask\n    end\n\n    it \"masks a service that doesn't exist\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'doesnotexist.service'))\n      expect(provider).to receive(:execute).\n                            with(['/bin/systemctl','cat', '--', 'doesnotexist.service'], {:failonfail => false}).\n                            and_return(Puppet::Util::Execution::ProcessOutput.new(\"No files found for doesnotexist.service.\\n\", 1))\n      expect(provider).to receive(:systemctl).with(:mask, '--', 'doesnotexist.service')\n      provider.mask\n    end\n  end\n\n  # Note: systemd provider does not care about hasstatus or a custom status\n  # command. I just assume that it does not make sense for systemd.\n  describe \"#status\" do\n    it \"should return running if the command returns 0\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:execute)\n        .with(['/bin/systemctl','is-active', '--', 'sshd.service'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(\"active\\n\", 0))\n      expect(provider.status).to eq(:running)\n    end\n\n    [-10,-1,3,10].each { |ec|\n      it \"should return stopped if the command returns something non-0\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n        expect(provider).to receive(:execute)\n          .with(['/bin/systemctl','is-active', '--', 'sshd.service'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"inactive\\n\", ec))\n        expect(provider.status).to eq(:stopped)\n      end\n    }\n\n    it \"should use the supplied status command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :status => '/bin/foo'))\n      expect(provider).to receive(:execute)\n        .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n        .and_return(process_output)\n      provider.status\n    end\n  end\n\n  # Note: systemd provider does not care about hasrestart. I just assume it\n  # does not make sense for systemd\n  describe \"#restart\" do\n    it \"should use the supplied restart command if specified\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))\n      expect(provider).to receive(:daemon_reload?).and_return('no')\n      expect(provider).to receive(:execute).with(['/bin/systemctl','restart', '--', 'sshd.service'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true}).never\n      expect(provider).to receive(:execute).with(['/bin/foo'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n\n    it \"should restart the service with systemctl restart\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:daemon_reload?).and_return('no')\n      expect(provider).to receive(:execute).with(['/bin/systemctl','restart','--','sshd.service'], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n      provider.restart\n    end\n\n    it \"should show journald logs on failure\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(provider).to receive(:daemon_reload?).and_return('no')\n      expect(provider).to receive(:execute).with(['/bin/systemctl','restart','--','sshd.service'],{:failonfail => true, :override_locale => false, :squelch => false, :combine => true})\n        .and_raise(Puppet::ExecutionFailure, \"Failed to restart sshd.service: Unit sshd.service failed to load: Invalid argument. See system logs and 'systemctl status sshd.service' for details.\")\n      journalctl_logs = <<-EOS\n-- Logs begin at Tue 2016-06-14 11:59:21 UTC, end at Tue 2016-06-14 21:45:02 UTC. --\nJun 14 21:41:34 foo.example.com systemd[1]: Stopping sshd Service...\nJun 14 21:41:35 foo.example.com systemd[1]: Starting sshd Service...\nJun 14 21:43:23 foo.example.com systemd[1]: sshd.service lacks both ExecStart= and ExecStop= setting. Refusing.\n      EOS\n      expect(provider).to receive(:execute).with(\"journalctl -n 50 --since '5 minutes ago' -u sshd.service --no-pager\").and_return(journalctl_logs)\n      expect { provider.restart }.to raise_error(Puppet::Error, /Systemd restart for sshd.service failed![\\n]+journalctl log for sshd.service:[\\n]+-- Logs begin at Tue 2016-06-14 11:59:21 UTC, end at Tue 2016-06-14 21:45:02 UTC. --/m)\n    end\n  end\n\n  describe \"#debian_enabled?\" do\n    [104, 106].each do |status|\n      it \"should return true when invoke-rc.d returns #{status}\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n        allow(provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', status))\n        expect(provider.debian_enabled?).to eq(:true)\n      end\n    end\n\n    [101, 105].each do |status|\n      it \"should return true when status is #{status} and there are at least 4 start links\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n        allow(provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', status))\n        expect(provider).to receive(:get_start_link_count).and_return(4)\n        expect(provider.debian_enabled?).to eq(:true)\n      end\n\n      it \"should return false when status is #{status} and there are less than 4 start links\" do\n        provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n        allow(provider).to receive(:execute).and_return(Puppet::Util::Execution::ProcessOutput.new('', status))\n        expect(provider).to receive(:get_start_link_count).and_return(1)\n        expect(provider.debian_enabled?).to eq(:false)\n      end\n    end\n  end\n\n  describe \"#insync_enabled?\" do\n    let(:provider) do\n      provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :enable => false))\n    end\n\n    before do\n      allow(provider).to receive(:cached_enabled?).and_return({ output: service_state, exitcode: 0 })\n    end\n\n    context 'when service state is static' do\n      let(:service_state) { 'static' }\n\n      context 'when enable is not mask' do\n        it 'is always enabled_insync even if current value is the same as expected' do\n          expect(provider).to be_enabled_insync(:false)\n        end\n\n        it 'is always enabled_insync even if current value is not the same as expected' do\n          expect(provider).to be_enabled_insync(:true)\n        end\n\n        it 'logs a debug messsage' do\n          expect(Puppet).to receive(:debug).with(\"Unable to enable or disable static service sshd.service\")\n          provider.enabled_insync?(:true)\n        end\n      end\n\n      context 'when enable is mask' do\n        let(:provider) do\n          provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service',\n                                                             :enable => 'mask'))\n        end\n\n        it 'is enabled_insync if current value is the same as expected' do\n          expect(provider).to be_enabled_insync(:mask)\n        end\n\n        it 'is not enabled_insync if current value is not the same as expected' do\n          expect(provider).not_to be_enabled_insync(:true)\n        end\n\n        it 'logs no debug messsage' do\n          expect(Puppet).not_to receive(:debug)\n          provider.enabled_insync?(:true)\n        end\n      end\n    end\n\n    context 'when service state is indirect' do\n      let(:service_state) { 'indirect' }\n\n      it 'is always enabled_insync even if current value is the same as expected' do\n        expect(provider).to be_enabled_insync(:false)\n      end\n\n      it 'is always enabled_insync even if current value is not the same as expected' do\n        expect(provider).to be_enabled_insync(:true)\n      end\n\n      it 'logs a debug messsage' do\n        expect(Puppet).to receive(:debug).with(\"Service sshd.service is in 'indirect' state and cannot be enabled/disabled\")\n        provider.enabled_insync?(:true)\n      end\n    end\n\n    context 'when service state is enabled' do\n      let(:service_state) { 'enabled' }\n\n      it 'is enabled_insync if current value is the same as expected' do\n        expect(provider).to be_enabled_insync(:false)\n      end\n\n      it 'is not enabled_insync if current value is not the same as expected' do\n        expect(provider).not_to be_enabled_insync(:true)\n      end\n\n      it 'logs no debug messsage' do\n        expect(Puppet).not_to receive(:debug)\n        provider.enabled_insync?(:true)\n      end\n    end\n  end\n\n  describe \"#get_start_link_count\" do\n    it \"should strip the '.service' from the search if present in the resource name\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))\n      expect(Dir).to receive(:glob).with(\"/etc/rc*.d/S??sshd\").and_return(['files'])\n      provider.get_start_link_count\n    end\n\n    it \"should use the full service name if it does not include '.service'\" do\n      provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))\n      expect(Dir).to receive(:glob).with(\"/etc/rc*.d/S??sshd\").and_return(['files'])\n      provider.get_start_link_count\n    end\n  end\n\n  it \"(#16451) has command systemctl without being fully qualified\" do\n    expect(provider_class.instance_variable_get(:@commands)).to include(:systemctl => 'systemctl')\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/upstart_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Upstart',\n         unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n  let(:manual) { \"\\nmanual\" }\n  let(:start_on_default_runlevels) {  \"\\nstart on runlevel [2,3,4,5]\" }\n  let!(:provider_class) { Puppet::Type.type(:service).provider(:upstart) }\n  let(:process_output) { Puppet::Util::Execution::ProcessOutput.new('', 0) }\n\n  def given_contents_of(file, content)\n    File.open(file, 'w') do |f|\n      f.write(content)\n    end\n  end\n\n  def then_contents_of(file)\n    File.open(file).read\n  end\n\n  def lists_processes_as(output)\n    allow(Puppet::Util::Execution).to receive(:execpipe).with(\"/sbin/initctl list\").and_yield(output)\n    allow(provider_class).to receive(:which).with(\"/sbin/initctl\").and_return(\"/sbin/initctl\")\n  end\n\n  it \"should be the default provider on Ubuntu\" do\n    expect(Facter).to receive(:value).with('os.name').and_return(\"Ubuntu\")\n    expect(Facter).to receive(:value).with('os.release.major').and_return(\"12.04\")\n    expect(provider_class.default?).to be_truthy\n  end\n\n  context \"upstart daemon existence confine\" do\n    let(:initctl_version) { ['/sbin/initctl', 'version', '--quiet'] }\n\n    before(:each) do\n      allow(Puppet::Util).to receive(:which).with('/sbin/initctl').and_return('/sbin/initctl')\n    end\n\n    it \"should return true when the daemon is running\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with(initctl_version, instance_of(Hash))\n\n      expect(provider_class).to be_has_initctl\n    end\n\n    it \"should return false when the daemon is not running\" do\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(initctl_version, instance_of(Hash))\n        .and_raise(Puppet::ExecutionFailure, \"initctl failed!\")\n\n      expect(provider_class).to_not be_has_initctl\n    end\n  end\n\n  describe \"excluding services\" do\n    it \"ignores tty and serial on Redhat systems\" do\n      allow(Facter).to receive(:value).with('os.family').and_return('RedHat')\n      expect(provider_class.excludes).to include 'serial'\n      expect(provider_class.excludes).to include 'tty'\n    end\n  end\n\n  describe \"#instances\" do\n    it \"should be able to find all instances\" do\n      lists_processes_as(\"rc stop/waiting\\nssh start/running, process 712\")\n\n      expect(provider_class.instances.map {|provider| provider.name}).to match_array([\"rc\",\"ssh\"])\n    end\n\n    it \"should attach the interface name for network interfaces\" do\n      lists_processes_as(\"network-interface (eth0)\")\n\n      expect(provider_class.instances.first.name).to eq(\"network-interface INTERFACE=eth0\")\n    end\n\n    it \"should attach the job name for network interface security\" do\n      processes = \"network-interface-security (network-interface/eth0)\"\n      allow(provider_class).to receive(:execpipe).and_yield(processes)\n      expect(provider_class.instances.first.name).to eq(\"network-interface-security JOB=network-interface/eth0\")\n    end\n\n    it \"should not find excluded services\" do\n      processes = \"wait-for-state stop/waiting\"\n      processes += \"\\nportmap-wait start/running\"\n      processes += \"\\nidmapd-mounting stop/waiting\"\n      processes += \"\\nstartpar-bridge start/running\"\n      processes += \"\\ncryptdisks-udev stop/waiting\"\n      processes += \"\\nstatd-mounting stop/waiting\"\n      processes += \"\\ngssd-mounting stop/waiting\"\n      allow(provider_class).to receive(:execpipe).and_yield(processes)\n      expect(provider_class.instances).to be_empty\n    end\n  end\n\n  describe \"#search\" do\n    it \"searches through paths to find a matching conf file\" do\n      allow(File).to receive(:directory?).and_return(true)\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/etc/init/foo-bar.conf\").and_return(true)\n      resource = Puppet::Type.type(:service).new(:name => \"foo-bar\", :provider => :upstart)\n      provider = provider_class.new(resource)\n\n      expect(provider.initscript).to eq(\"/etc/init/foo-bar.conf\")\n    end\n\n    it \"searches for just the name of a compound named service\" do\n      allow(File).to receive(:directory?).and_return(true)\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(false)\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/etc/init/network-interface.conf\").and_return(true)\n      resource = Puppet::Type.type(:service).new(:name => \"network-interface INTERFACE=lo\", :provider => :upstart)\n      provider = provider_class.new(resource)\n\n      expect(provider.initscript).to eq(\"/etc/init/network-interface.conf\")\n    end\n  end\n\n  describe \"#status\" do\n    it \"should use the default status command if none is specified\" do\n      resource = Puppet::Type.type(:service).new(:name => \"foo\", :provider => :upstart)\n      provider = provider_class.new(resource)\n      allow(provider).to receive(:is_upstart?).and_return(true)\n\n      expect(provider).to receive(:status_exec)\n        .with([\"foo\"])\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(\"foo start/running, process 1000\", 0))\n      expect(provider.status).to eq(:running)\n    end\n\n    describe \"when a special status command is specifed\" do\n      it \"should use the provided status command\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :provider => :upstart, :status => '/bin/foo')\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        provider.status\n      end\n\n      it \"should return :stopped when the provided status command return non-zero\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :provider => :upstart, :status => '/bin/foo')\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 1))\n        expect(provider.status).to eq(:stopped)\n      end\n\n      it \"should return :running when the provided status command return zero\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :provider => :upstart, :status => '/bin/foo')\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    describe \"when :hasstatus is set to false\" do\n      it \"should return :stopped if the pid can not be found\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :hasstatus => false, :provider => :upstart)\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:getpid).and_return(nil)\n        expect(provider.status).to eq(:stopped)\n      end\n\n      it \"should return :running if the pid can be found\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :hasstatus => false, :provider => :upstart)\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:getpid).and_return(2706)\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    describe \"when a special status command is specifed\" do\n      it \"should use the provided status command\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :provider => :upstart, :status => '/bin/foo')\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        provider.status\n      end\n\n      it \"should return :stopped when the provided status command return non-zero\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :provider => :upstart, :status => '/bin/foo')\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(Puppet::Util::Execution::ProcessOutput.new('', 1))\n        expect(provider.status).to eq(:stopped)\n      end\n\n      it \"should return :running when the provided status command return zero\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :provider => :upstart, :status => '/bin/foo')\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:execute)\n          .with(['/bin/foo'], {:failonfail => false, :override_locale => false, :squelch => false, :combine => true})\n          .and_return(process_output)\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    describe \"when :hasstatus is set to false\" do\n      it \"should return :stopped if the pid can not be found\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :hasstatus => false, :provider => :upstart)\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:getpid).and_return(nil)\n        expect(provider.status).to eq(:stopped)\n      end\n\n      it \"should return :running if the pid can be found\" do\n        resource = Puppet::Type.type(:service).new(:name => 'foo', :hasstatus => false, :provider => :upstart)\n        provider = provider_class.new(resource)\n        allow(provider).to receive(:is_upstart?).and_return(true)\n\n        expect(provider).not_to receive(:status_exec).with(['foo'])\n        expect(provider).to receive(:getpid).and_return(2706)\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    it \"should properly handle services with 'start' in their name\" do\n      resource = Puppet::Type.type(:service).new(:name => \"foostartbar\", :provider => :upstart)\n      provider = provider_class.new(resource)\n      allow(provider).to receive(:is_upstart?).and_return(true)\n\n      expect(provider).to receive(:status_exec)\n        .with([\"foostartbar\"]).and_return(\"foostartbar stop/waiting\")\n        .and_return(process_output)\n      expect(provider.status).to eq(:stopped)\n    end\n  end\n\n  describe \"inheritance\" do\n    let :resource do\n      Puppet::Type.type(:service).new(:name => \"foo\", :provider => :upstart)\n    end\n\n    let :provider do\n      provider_class.new(resource)\n    end\n\n    describe \"when upstart job\" do\n      before(:each) do\n        allow(provider).to receive(:is_upstart?).and_return(true)\n      end\n\n      [\"start\", \"stop\"].each do |action|\n        it \"should return the #{action}cmd of its parent provider\" do\n          expect(provider.send(\"#{action}cmd\".to_sym)).to eq([provider.command(action.to_sym), resource.name])\n        end\n      end\n\n      it \"should return nil for the statuscmd\" do\n        expect(provider.statuscmd).to be_nil\n      end\n    end\n  end\n\n  describe \"should be enableable\" do\n    let :resource do\n      Puppet::Type.type(:service).new(:name => \"foo\", :provider => :upstart)\n    end\n\n    let :provider do\n      provider_class.new(resource)\n    end\n\n    let :init_script do\n      PuppetSpec::Files.tmpfile(\"foo.conf\")\n    end\n\n    let :over_script do\n      PuppetSpec::Files.tmpfile(\"foo.override\")\n    end\n\n    let :disabled_content do\n      \"\\t #  \\t start on\\nother file stuff\"\n    end\n\n    let :multiline_disabled do\n      \"# \\t  start on other file stuff (\\n\" +\n       \"#   more stuff ( # )))))inline comment\\n\" +\n       \"#   finishing up )\\n\" +\n       \"#   and done )\\n\" +\n       \"this line shouldn't be touched\\n\"\n    end\n\n    let :multiline_disabled_bad do\n      \"# \\t  start on other file stuff (\\n\" +\n       \"#   more stuff ( # )))))inline comment\\n\" +\n       \"#   finishing up )\\n\" +\n       \"#   and done )\\n\" +\n       \"#   this is a comment i want to be a comment\\n\" +\n       \"this line shouldn't be touched\\n\"\n    end\n\n    let :multiline_enabled_bad do\n      \" \\t  start on other file stuff (\\n\" +\n       \"   more stuff ( # )))))inline comment\\n\" +\n       \"   finishing up )\\n\" +\n       \"   and done )\\n\" +\n       \"#   this is a comment i want to be a comment\\n\" +\n       \"this line shouldn't be touched\\n\"\n    end\n\n    let :multiline_enabled do\n      \" \\t  start on other file stuff (\\n\" +\n       \"   more stuff ( # )))))inline comment\\n\" +\n       \"   finishing up )\\n\" +\n       \"   and done )\\n\" +\n       \"this line shouldn't be touched\\n\"\n    end\n\n    let :multiline_enabled_standalone do\n      \" \\t  start on other file stuff (\\n\" +\n       \"   more stuff ( # )))))inline comment\\n\" +\n       \"   finishing up )\\n\" +\n       \"   and done )\\n\"\n    end\n\n    let :enabled_content do\n      \"\\t   \\t start on\\nother file stuff\"\n    end\n\n    let :content do\n      \"just some text\"\n    end\n\n    describe \"Upstart version < 0.6.7\" do\n      before(:each) do\n        allow(provider).to receive(:is_upstart?).and_return(true)\n        allow(provider).to receive(:upstart_version).and_return(\"0.6.5\")\n        allow(provider).to receive(:search).and_return(init_script)\n      end\n\n      [:enabled?,:enable,:disable].each do |enableable|\n        it \"should respond to #{enableable}\" do\n          expect(provider).to respond_to(enableable)\n        end\n      end\n\n      describe \"when enabling\" do\n        it \"should open and uncomment the '#start on' line\" do\n          given_contents_of(init_script, disabled_content)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(enabled_content)\n        end\n\n        it \"should add a 'start on' line if none exists\" do\n          given_contents_of(init_script, \"this is a file\")\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(\"this is a file\" + start_on_default_runlevels)\n        end\n\n        it \"should handle multiline 'start on' stanzas\" do\n          given_contents_of(init_script, multiline_disabled)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled)\n        end\n\n        it \"should leave not 'start on' comments alone\" do\n          given_contents_of(init_script, multiline_disabled_bad)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled_bad)\n        end\n      end\n\n      describe \"when disabling\" do\n        it \"should open and comment the 'start on' line\" do\n          given_contents_of(init_script, enabled_content)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(\"#\" + enabled_content)\n        end\n\n        it \"should handle multiline 'start on' stanzas\" do\n          given_contents_of(init_script, multiline_enabled)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(multiline_disabled)\n        end\n      end\n\n      describe \"when checking whether it is enabled\" do\n        it \"should consider 'start on ...' to be enabled\" do\n          given_contents_of(init_script, enabled_content)\n\n          expect(provider.enabled?).to eq(:true)\n        end\n\n        it \"should consider '#start on ...' to be disabled\" do\n          given_contents_of(init_script, disabled_content)\n\n          expect(provider.enabled?).to eq(:false)\n        end\n\n        it \"should consider no start on line to be disabled\" do\n          given_contents_of(init_script, content)\n\n          expect(provider.enabled?).to eq(:false)\n        end\n      end\n      end\n\n    describe \"Upstart version < 0.9.0\" do\n      before(:each) do\n        allow(provider).to receive(:is_upstart?).and_return(true)\n        allow(provider).to receive(:upstart_version).and_return(\"0.7.0\")\n        allow(provider).to receive(:search).and_return(init_script)\n      end\n\n      [:enabled?,:enable,:disable].each do |enableable|\n        it \"should respond to #{enableable}\" do\n          expect(provider).to respond_to(enableable)\n        end\n      end\n\n      describe \"when enabling\" do\n        it \"should open and uncomment the '#start on' line\" do\n          given_contents_of(init_script, disabled_content)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(enabled_content)\n        end\n\n        it \"should add a 'start on' line if none exists\" do\n          given_contents_of(init_script, \"this is a file\")\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(\"this is a file\" + start_on_default_runlevels)\n        end\n\n        it \"should handle multiline 'start on' stanzas\" do\n          given_contents_of(init_script, multiline_disabled)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled)\n        end\n\n        it \"should remove manual stanzas\" do\n          given_contents_of(init_script, multiline_enabled + manual)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled)\n        end\n\n        it \"should leave not 'start on' comments alone\" do\n          given_contents_of(init_script, multiline_disabled_bad)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled_bad)\n        end\n      end\n\n      describe \"when disabling\" do\n        it \"should add a manual stanza\" do\n          given_contents_of(init_script, enabled_content)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(enabled_content + manual)\n        end\n\n        it \"should remove manual stanzas before adding new ones\" do\n          given_contents_of(init_script, multiline_enabled + manual + \"\\n\" + multiline_enabled)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled + \"\\n\" + multiline_enabled + manual)\n        end\n\n        it \"should handle multiline 'start on' stanzas\" do\n          given_contents_of(init_script, multiline_enabled)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled + manual)\n        end\n      end\n\n      describe \"when checking whether it is enabled\" do\n        describe \"with no manual stanza\" do\n          it \"should consider 'start on ...' to be enabled\" do\n            given_contents_of(init_script, enabled_content)\n\n            expect(provider.enabled?).to eq(:true)\n          end\n\n          it \"should consider '#start on ...' to be disabled\" do\n            given_contents_of(init_script, disabled_content)\n\n            expect(provider.enabled?).to eq(:false)\n          end\n\n          it \"should consider no start on line to be disabled\" do\n            given_contents_of(init_script, content)\n\n            expect(provider.enabled?).to eq(:false)\n          end\n        end\n\n        describe \"with manual stanza\" do\n          it \"should consider 'start on ...' to be disabled if there is a trailing manual stanza\" do\n            given_contents_of(init_script, enabled_content + manual + \"\\nother stuff\")\n\n            expect(provider.enabled?).to eq(:false)\n          end\n\n          it \"should consider two start on lines with a manual in the middle to be enabled\" do\n            given_contents_of(init_script, enabled_content + manual + \"\\n\" + enabled_content)\n\n            expect(provider.enabled?).to eq(:true)\n          end\n        end\n      end\n    end\n\n    describe \"Upstart version > 0.9.0\" do\n      before(:each) do\n        allow(provider).to receive(:is_upstart?).and_return(true)\n        allow(provider).to receive(:upstart_version).and_return(\"0.9.5\")\n        allow(provider).to receive(:search).and_return(init_script)\n        allow(provider).to receive(:overscript).and_return(over_script)\n      end\n\n      [:enabled?,:enable,:disable].each do |enableable|\n        it \"should respond to #{enableable}\" do\n          expect(provider).to respond_to(enableable)\n        end\n      end\n\n      describe \"when enabling\" do\n        it \"should add a 'start on' line if none exists\" do\n          given_contents_of(init_script, \"this is a file\")\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(\"this is a file\")\n          expect(then_contents_of(over_script)).to eq(start_on_default_runlevels)\n        end\n\n        it \"should handle multiline 'start on' stanzas\" do\n          given_contents_of(init_script, multiline_disabled)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_disabled)\n          expect(then_contents_of(over_script)).to eq(start_on_default_runlevels)\n        end\n\n        it \"should remove any manual stanzas from the override file\" do\n          given_contents_of(over_script, manual)\n          given_contents_of(init_script, enabled_content)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(enabled_content)\n          expect(then_contents_of(over_script)).to eq(\"\")\n        end\n\n        it \"should copy existing start on from conf file if conf file is disabled\" do\n          given_contents_of(init_script, multiline_enabled_standalone + manual)\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled_standalone + manual)\n          expect(then_contents_of(over_script)).to eq(multiline_enabled_standalone)\n        end\n\n        it \"should leave not 'start on' comments alone\" do\n          given_contents_of(init_script, multiline_disabled_bad)\n          given_contents_of(over_script, \"\")\n\n          provider.enable\n\n          expect(then_contents_of(init_script)).to eq(multiline_disabled_bad)\n          expect(then_contents_of(over_script)).to eq(start_on_default_runlevels)\n        end\n      end\n\n      describe \"when disabling\" do\n        it \"should add a manual stanza to the override file\" do\n          given_contents_of(init_script, enabled_content)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(enabled_content)\n          expect(then_contents_of(over_script)).to eq(manual)\n        end\n\n        it \"should handle multiline 'start on' stanzas\" do\n          given_contents_of(init_script, multiline_enabled)\n\n          provider.disable\n\n          expect(then_contents_of(init_script)).to eq(multiline_enabled)\n          expect(then_contents_of(over_script)).to eq(manual)\n        end\n      end\n\n      describe \"when checking whether it is enabled\" do\n        describe \"with no override file\" do\n          it \"should consider 'start on ...' to be enabled\" do\n            given_contents_of(init_script, enabled_content)\n\n            expect(provider.enabled?).to eq(:true)\n          end\n\n          it \"should consider '#start on ...' to be disabled\" do\n            given_contents_of(init_script, disabled_content)\n\n            expect(provider.enabled?).to eq(:false)\n          end\n\n          it \"should consider no start on line to be disabled\" do\n            given_contents_of(init_script, content)\n\n            expect(provider.enabled?).to eq(:false)\n          end\n        end\n\n        describe \"with override file\" do\n          it \"should consider 'start on ...' to be disabled if there is manual in override file\" do\n            given_contents_of(init_script, enabled_content)\n            given_contents_of(over_script, manual + \"\\nother stuff\")\n\n            expect(provider.enabled?).to eq(:false)\n          end\n\n          it \"should consider '#start on ...' to be enabled if there is a start on in the override file\" do\n            given_contents_of(init_script, disabled_content)\n            given_contents_of(over_script, \"start on stuff\")\n\n            expect(provider.enabled?).to eq(:true)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/service/windows_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::Service::Provider::Windows',\n    :if => Puppet::Util::Platform.windows? && !Puppet::Util::Platform.jruby? do\n  let(:provider_class) { Puppet::Type.type(:service).provider(:windows) }\n  let(:name)     { 'nonexistentservice' }\n  let(:resource) { Puppet::Type.type(:service).new(:name => name, :provider => :windows) }\n  let(:provider) { resource.provider }\n  let(:config)   { Struct::ServiceConfigInfo.new }\n  let(:status)   { Struct::ServiceStatus.new }\n  let(:service_util) { Puppet::Util::Windows::Service }\n  let(:service_handle) { double() }\n\n  before :each do\n    # make sure we never actually execute anything (there are two execute methods)\n    allow(provider.class).to receive(:execute)\n    allow(provider).to receive(:execute)\n\n    allow(service_util).to receive(:exists?).with(resource[:name]).and_return(true)\n  end\n\n  describe \".instances\" do\n    it \"should enumerate all services\" do\n      list_of_services = {'snmptrap' => {}, 'svchost' => {}, 'sshd' => {}}\n      expect(service_util).to receive(:services).and_return(list_of_services)\n\n      expect(provider_class.instances.map(&:name)).to match_array(['snmptrap', 'svchost', 'sshd'])\n    end\n  end\n\n  describe \"#start\" do\n    before(:each) do\n      allow(provider).to receive(:status).and_return(:stopped)\n    end\n\n    it \"should resume a paused service\" do\n      allow(provider).to receive(:status).and_return(:paused)\n      expect(service_util).to receive(:resume)\n      provider.start\n    end\n\n    it \"should start the service\" do\n      expect(service_util).to receive(:service_start_type).with(name).and_return(:SERVICE_AUTO_START)\n      expect(service_util).to receive(:start)\n      provider.start\n    end\n\n    context \"when the service is disabled\" do\n      before :each do\n        expect(service_util).to receive(:service_start_type).with(name).and_return(:SERVICE_DISABLED)\n      end\n\n      it \"should refuse to start if not managing enable\" do\n        expect { provider.start }.to raise_error(Puppet::Error, /Will not start disabled service/)\n      end\n\n      it \"should enable if managing enable and enable is true\" do\n        resource[:enable] = :true\n        expect(service_util).to receive(:start)\n        expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_AUTO_START})\n\n        provider.start\n      end\n\n      it \"should manual start if managing enable and enable is false\" do\n        resource[:enable] = :false\n        expect(service_util).to receive(:start)\n        expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_DEMAND_START})\n\n        provider.start\n      end\n    end\n  end\n\n  describe \"#stop\" do\n    it \"should stop a running service\" do\n      expect(service_util).to receive(:stop)\n\n      provider.stop\n    end\n  end\n\n  describe \"#status\" do\n    it \"should report a nonexistent service as stopped\" do\n      allow(service_util).to receive(:exists?).with(resource[:name]).and_return(false)\n\n      expect(provider.status).to eql(:stopped)\n    end\n\n    it \"should report service as stopped when status cannot be retrieved\" do\n      allow(service_util).to receive(:exists?).with(resource[:name]).and_return(true)\n      allow(service_util).to receive(:service_state).with(name).and_raise(Puppet::Error.new('Service query failed: The specified path is invalid.'))\n\n      expect(Puppet).to receive(:warning).with(\"Status for service #{resource[:name]} could not be retrieved: Service query failed: The specified path is invalid.\")\n      expect(provider.status).to eql(:stopped)\n    end\n\n    [\n      :SERVICE_PAUSED,\n      :SERVICE_PAUSE_PENDING\n    ].each do |state|\n      it \"should report a #{state} service as paused\" do\n        expect(service_util).to receive(:service_state).with(name).and_return(state)\n        expect(provider.status).to eq(:paused)\n      end\n    end\n\n    [\n      :SERVICE_STOPPED,\n      :SERVICE_STOP_PENDING\n    ].each do |state|\n      it \"should report a #{state} service as stopped\" do\n        expect(service_util).to receive(:service_state).with(name).and_return(state)\n        expect(provider.status).to eq(:stopped)\n      end\n    end\n\n    [\n      :SERVICE_RUNNING,\n      :SERVICE_CONTINUE_PENDING,\n      :SERVICE_START_PENDING,\n    ].each do |state|\n      it \"should report a #{state} service as running\" do\n        expect(service_util).to receive(:service_state).with(name).and_return(state)\n\n        expect(provider.status).to eq(:running)\n      end\n    end\n\n    context 'when querying lmhosts', if: Puppet::Util::Platform.windows? do\n      # This service should be ubiquitous across all supported Windows platforms\n      let(:service) { Puppet::Type.type(:service).new(:name => 'lmhosts') }\n\n      before :each do\n        allow(service_util).to receive(:exists?).with(service.name).and_call_original\n      end\n\n      it \"reports if the service is enabled\" do\n        expect([:true, :false, :manual]).to include(service.provider.enabled?)\n      end\n\n      it \"reports on the service status\" do\n        expect(\n          [\n            :running,\n            :'continue pending',\n            :'pause pending',\n            :paused,\n            :running,\n            :'start pending',\n            :'stop pending',\n            :stopped\n          ]\n        ).to include(service.provider.status)\n      end\n    end\n  end\n\n  describe \"#restart\" do\n    it \"should use the supplied restart command if specified\" do\n      resource[:restart] = 'c:/bin/foo'\n\n      expect(provider).to receive(:execute).with(['c:/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)\n\n      provider.restart\n    end\n\n    it \"should restart the service\" do\n      expect(provider).to receive(:stop).ordered\n      expect(provider).to receive(:start).ordered\n\n      provider.restart\n    end\n  end\n\n  describe \"#enabled?\" do\n    it \"should report a nonexistent service as false\" do\n      allow(service_util).to receive(:exists?).with(resource[:name]).and_return(false)\n\n      expect(provider.enabled?).to eql(:false)\n    end\n\n    it \"should report a service with a startup type of manual as manual\" do\n      expect(service_util).to receive(:service_start_type).with(name).and_return(:SERVICE_DEMAND_START)\n      expect(provider.enabled?).to eq(:manual)\n    end\n\n    it \"should report a service with a startup type of delayed as delayed\" do\n      expect(service_util).to receive(:service_start_type).with(name).and_return(:SERVICE_DELAYED_AUTO_START)\n      expect(provider.enabled?).to eq(:delayed)\n    end\n\n    it \"should report a service with a startup type of disabled as false\" do\n      expect(service_util).to receive(:service_start_type).with(name).and_return(:SERVICE_DISABLED)\n      expect(provider.enabled?).to eq(:false)\n    end\n\n    # We need to guard this section explicitly since rspec will always\n    # construct all examples, even if it isn't going to run them.\n    if Puppet.features.microsoft_windows?\n      [\n        :SERVICE_AUTO_START,\n        :SERVICE_BOOT_START,\n        :SERVICE_SYSTEM_START\n      ].each do |start_type|\n        it \"should report a service with a startup type of '#{start_type}' as true\" do\n          expect(service_util).to receive(:service_start_type).with(name).and_return(start_type)\n          expect(provider.enabled?).to eq(:true)\n        end\n      end\n    end\n  end\n\n  describe \"#enable\" do\n    it \"should set service start type to Service_Auto_Start when enabled\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_AUTO_START})\n      provider.enable\n    end\n\n    it \"raises an error if set_startup_configuration fails\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_AUTO_START}).and_raise(Puppet::Error.new('foobar'))\n\n      expect {\n        provider.enable\n      }.to raise_error(Puppet::Error, /Cannot enable #{name}/)\n    end\n  end\n\n  describe \"#disable\" do\n    it \"should set service start type to Service_Disabled when disabled\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_DISABLED})\n      provider.disable\n    end\n\n    it \"raises an error if set_startup_configuration fails\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_DISABLED}).and_raise(Puppet::Error.new('foobar'))\n\n      expect {\n        provider.disable\n      }.to raise_error(Puppet::Error, /Cannot disable #{name}/)\n    end\n  end\n\n  describe \"#manual_start\" do\n    it \"should set service start type to Service_Demand_Start (manual) when manual\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_DEMAND_START})\n      provider.manual_start\n    end\n\n    it \"raises an error if set_startup_configuration fails\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_DEMAND_START}).and_raise(Puppet::Error.new('foobar'))\n\n      expect {\n        provider.manual_start\n      }.to raise_error(Puppet::Error, /Cannot enable #{name}/)\n    end\n  end\n\n  describe \"#delayed_start\" do\n    it \"should set service start type to Service_Config_Delayed_Auto_Start (delayed) when delayed\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_AUTO_START, delayed: true})\n      provider.delayed_start\n    end\n\n    it \"raises an error if set_startup_configuration fails\" do\n      expect(service_util).to receive(:set_startup_configuration).with(name, options: {startup_type: :SERVICE_AUTO_START, delayed: true}).and_raise(Puppet::Error.new('foobar'))\n\n      expect {\n        provider.delayed_start\n      }.to raise_error(Puppet::Error, /Cannot enable #{name}/)\n    end\n  end\n\n  describe \"when managing logon credentials\" do\n    before do\n      allow(Puppet::Util::Windows::ADSI).to receive(:computer_name).and_return(computer_name)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).and_return(principal)\n      allow(Puppet::Util::Windows::Service).to receive(:set_startup_configuration).and_return(nil)\n    end\n\n    let(:computer_name) { 'myPC' }\n\n    describe \"#logonaccount=\" do\n      before do\n        allow(Puppet::Util::Windows::User).to receive(:password_is?).and_return(true)\n        resource[:logonaccount] = user_input\n        provider.logonaccount_insync?(user_input)\n      end\n\n      let(:user_input) { principal.account }\n      let(:principal) do\n        Puppet::Util::Windows::SID::Principal.new(\"myUser\", nil, nil, computer_name, :SidTypeUser)\n      end\n\n      context \"when given user is 'myUser'\" do\n        it \"should fail when the `Log On As A Service` right is missing from given user\" do\n          allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return(\"\")\n          expect { provider.logonaccount=(user_input) }.to raise_error(Puppet::Error, /\".\\\\#{principal.account}\" is missing the 'Log On As A Service' right./)\n        end\n\n        it \"should fail when the `Log On As A Service` right is set to denied for given user\" do\n          allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return(\"SeDenyServiceLogonRight\")\n          expect { provider.logonaccount=(user_input) }.to raise_error(Puppet::Error, /\".\\\\#{principal.account}\" has the 'Log On As A Service' right set to denied./)\n        end\n\n        it \"should not fail when given user has the `Log On As A Service` right\" do\n          allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return(\"SeServiceLogonRight\")\n          expect { provider.logonaccount=(user_input) }.not_to raise_error\n        end\n\n        ['myUser', 'myPC\\\\myUser', \".\\\\myUser\", \"MYPC\\\\mYuseR\"].each do |user_input_variant|\n          let(:user_input) { user_input_variant }\n\n          it \"should succesfully munge #{user_input_variant} to '.\\\\myUser'\" do\n            allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return(\"SeServiceLogonRight\")\n            expect { provider.logonaccount=(user_input) }.not_to raise_error\n            expect(resource[:logonaccount]).to eq(\".\\\\myUser\")\n          end\n        end\n      end\n\n      context \"when given user is a system account\" do\n        before do\n          allow(Puppet::Util::Windows::User).to receive(:default_system_account?).and_return(true)\n        end\n\n        let(:user_input) { principal.account }\n        let(:principal) do\n          Puppet::Util::Windows::SID::Principal.new(\"LOCAL SERVICE\", nil, nil, \"NT AUTHORITY\", :SidTypeUser)\n        end\n\n        it \"should not fail when given user is a default system account even if the `Log On As A Service` right is missing\" do\n          expect(Puppet::Util::Windows::User).not_to receive(:get_rights)\n          expect { provider.logonaccount=(user_input) }.not_to raise_error\n        end\n\n        ['LocalSystem', '.\\LocalSystem', 'myPC\\LocalSystem', 'lOcALsysTem'].each do |user_input_variant|\n          let(:user_input) { user_input_variant }\n\n          it \"should succesfully munge #{user_input_variant} to 'LocalSystem'\" do\n            expect { provider.logonaccount=(user_input) }.not_to raise_error\n            expect(resource[:logonaccount]).to eq('LocalSystem')\n          end\n        end\n      end\n\n      context \"when domain is different from computer name\" do\n        before do\n          allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return(\"SeServiceLogonRight\")\n        end\n\n        context \"when given user is from AD\" do\n          let(:user_input) { 'myRemoteUser' }\n          let(:principal) do\n            Puppet::Util::Windows::SID::Principal.new(\"myRemoteUser\", nil, nil, \"AD\", :SidTypeUser)\n          end\n\n          it \"should not raise any error\" do\n            expect { provider.logonaccount=(user_input) }.not_to raise_error\n          end\n\n          it \"should succesfully be munged\" do\n            expect { provider.logonaccount=(user_input) }.not_to raise_error\n            expect(resource[:logonaccount]).to eq('AD\\myRemoteUser')\n          end\n        end\n\n        context \"when given user is LocalService\" do\n          let(:user_input) { 'LocalService' }\n          let(:principal) do\n            Puppet::Util::Windows::SID::Principal.new(\"LOCAL SERVICE\", nil, nil, \"NT AUTHORITY\", :SidTypeWellKnownGroup)\n          end\n\n          it \"should succesfully munge well known user\" do\n            expect { provider.logonaccount=(user_input) }.not_to raise_error\n            expect(resource[:logonaccount]).to eq('NT AUTHORITY\\LOCAL SERVICE')\n          end\n        end\n\n        context \"when given user is in SID form\" do\n          let(:user_input) { 'S-1-5-20' }\n          let(:principal) do\n            Puppet::Util::Windows::SID::Principal.new(\"NETWORK SERVICE\", nil, nil, \"NT AUTHORITY\", :SidTypeUser)\n          end\n\n          it \"should succesfully munge\" do\n            expect { provider.logonaccount=(user_input) }.not_to raise_error\n            expect(resource[:logonaccount]).to eq('NT AUTHORITY\\NETWORK SERVICE')\n          end\n        end\n\n        context \"when given user is actually a group\" do\n          let(:principal) do\n            Puppet::Util::Windows::SID::Principal.new(\"Administrators\", nil, nil, \"BUILTIN\", :SidTypeAlias)\n          end\n          let(:user_input) { 'Administrators' }\n\n          it \"should fail when sid type is not user or well known user\" do\n            expect { provider.logonaccount=(user_input) }.to raise_error(Puppet::Error, /\"BUILTIN\\\\#{user_input}\" is not a valid account/)\n          end\n        end\n      end\n    end\n\n    describe \"#logonpassword=\" do\n      before do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('SeServiceLogonRight')\n        resource[:logonaccount] = account\n        resource[:logonpassword] = user_input\n        provider.logonaccount_insync?(account)\n      end\n\n      let(:account) { 'LocalSystem' }\n\n      describe \"when given logonaccount is a predefined_local_account\" do\n        let(:user_input) { 'pass' }\n        let(:principal) { nil }\n\n        it \"should pass validation when given account is 'LocalSystem'\" do\n          allow(Puppet::Util::Windows::User).to receive(:localsystem?).with('LocalSystem').and_return(true)\n          allow(Puppet::Util::Windows::User).to receive(:default_system_account?).with('LocalSystem').and_return(true)\n\n          expect(Puppet::Util::Windows::User).not_to receive(:password_is?)\n          expect { provider.logonpassword=(user_input) }.not_to raise_error\n        end\n\n        ['LOCAL SERVICE', 'NETWORK SERVICE', 'SYSTEM'].each do |predefined_local_account|\n          describe \"when given account is #{predefined_local_account}\" do\n            let(:account) { 'predefined_local_account' }\n            let(:principal) do\n              Puppet::Util::Windows::SID::Principal.new(account, nil, nil, \"NT AUTHORITY\", :SidTypeUser)\n            end\n\n            it \"should pass validation\" do\n              allow(Puppet::Util::Windows::User).to receive(:localsystem?).with(principal.account).and_return(false)\n              allow(Puppet::Util::Windows::User).to receive(:localsystem?).with(principal.domain_account).and_return(false)\n              expect(Puppet::Util::Windows::User).to receive(:default_system_account?).with(principal.domain_account).and_return(true).twice\n\n              expect(Puppet::Util::Windows::User).not_to receive(:password_is?)\n              expect { provider.logonpassword=(user_input) }.not_to raise_error\n            end\n          end\n        end\n      end\n\n      describe \"when given logonaccount is not a predefined local account\" do\n        before do\n          allow(Puppet::Util::Windows::User).to receive(:localsystem?).with(\".\\\\#{principal.account}\").and_return(false)\n          allow(Puppet::Util::Windows::User).to receive(:default_system_account?).with(\".\\\\#{principal.account}\").and_return(false)\n        end\n\n        let(:account) { 'myUser' }\n        let(:principal) do\n          Puppet::Util::Windows::SID::Principal.new(account, nil, nil, computer_name, :SidTypeUser)\n        end\n\n        describe \"when password is proven correct\" do\n          let(:user_input) { 'myPass' }\n          it \"should pass validation\" do\n            allow(Puppet::Util::Windows::User).to receive(:password_is?).with('myUser', 'myPass', '.').and_return(true)\n            expect { provider.logonpassword=(user_input) }.not_to raise_error\n          end\n        end\n\n        describe \"when password is not proven correct\" do\n          let(:user_input) { 'myWrongPass' }\n          it \"should not pass validation\" do\n            allow(Puppet::Util::Windows::User).to receive(:password_is?).with('myUser', 'myWrongPass', '.').and_return(false)\n            expect { provider.logonpassword=(user_input) }.to raise_error(Puppet::Error, /The given password is invalid for user '.\\\\myUser'/)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/aix_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Type::User::Provider::Aix' do\n  let(:provider_class) { Puppet::Type.type(:user).provider(:aix) }\n  let(:group_provider_class) { Puppet::Type.type(:group).provider(:aix) }\n\n  let(:resource) do\n    Puppet::Type.type(:user).new(\n      :name   => 'test_aix_user',\n      :ensure => :present\n    )\n  end\n  let(:provider) do\n    provider_class.new(resource)\n  end\n\n  describe '.pgrp_to_gid' do\n    it \"finds the primary group's gid\" do\n      allow(provider).to receive(:ia_module_args).and_return(['-R', 'module'])\n\n      expect(group_provider_class).to receive(:list_all)\n        .with(provider.ia_module_args)\n        .and_return([{ :name => 'group', :id => 1}])\n\n      expect(provider_class.pgrp_to_gid(provider, 'group')).to eql(1)\n    end\n  end\n\n  describe '.gid_to_pgrp' do\n    it \"finds the gid's primary group\" do\n      allow(provider).to receive(:ia_module_args).and_return(['-R', 'module'])\n\n      expect(group_provider_class).to receive(:list_all)\n        .with(provider.ia_module_args)\n        .and_return([{ :name => 'group', :id => 1}])\n\n      expect(provider_class.gid_to_pgrp(provider, 1)).to eql('group')\n    end\n  end\n\n  describe '.expires_to_expiry' do\n    it 'returns absent if expires is 0' do\n      expect(provider_class.expires_to_expiry(provider, '0')).to eql(:absent)\n    end\n\n    it 'returns absent if the expiry attribute is not formatted properly' do\n      expect(provider_class.expires_to_expiry(provider, 'bad_format')).to eql(:absent)\n    end\n\n    it 'returns the password expiration date' do\n      expect(provider_class.expires_to_expiry(provider, '0910122314')).to eql('2014-09-10')\n    end\n  end\n\n  describe '.expiry_to_expires' do\n    it 'returns 0 if the expiry date is 0000-00-00' do\n      expect(provider_class.expiry_to_expires('0000-00-00')).to eql('0')\n    end\n\n    it 'returns 0 if the expiry date is \"absent\"' do\n      expect(provider_class.expiry_to_expires('absent')).to eql('0')\n    end\n\n    it 'returns 0 if the expiry date is :absent' do\n      expect(provider_class.expiry_to_expires(:absent)).to eql('0')\n    end\n\n    it 'returns the expires attribute value' do\n      expect(provider_class.expiry_to_expires('2014-09-10')).to eql('0910000014')\n    end\n  end\n\n  describe '.groups_attribute_to_property' do\n    it \"reads the user's groups from the etc/groups file\" do\n      groups = ['system', 'adm']\n      allow(Puppet::Util::POSIX).to receive(:groups_of).with(resource[:name]).and_return(groups)\n\n      actual_groups = provider_class.groups_attribute_to_property(provider, 'unused_value')\n      expected_groups = groups.join(',')\n\n      expect(actual_groups).to eql(expected_groups)\n    end\n  end\n\n  describe '.groups_property_to_attribute' do\n    it 'raises an ArgumentError if the groups are space-separated' do\n      groups = \"foo bar baz\"\n      expect do\n        provider_class.groups_property_to_attribute(groups)\n      end.to raise_error do |error|\n        expect(error).to be_a(ArgumentError)\n\n        expect(error.message).to match(groups)\n        expect(error.message).to match(\"Groups\")\n      end\n    end\n  end\n\n  describe '#gid=' do\n    let(:value) { 'new_pgrp' }\n    let(:old_pgrp) { 'old_pgrp' }\n    let(:cur_groups) { 'system,adm' }\n\n    before(:each) do\n      allow(provider).to receive(:gid).and_return(old_pgrp)\n      allow(provider).to receive(:groups).and_return(cur_groups)\n      allow(provider).to receive(:set)\n    end\n\n    it 'raises a Puppet::Error if it fails to set the groups property' do\n      allow(provider).to receive(:set)\n        .with(:groups, cur_groups)\n        .and_raise(Puppet::ExecutionFailure, 'failed to reset the groups!')\n\n      expect { provider.gid = value }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n\n        expect(error.message).to match('groups')\n        expect(error.message).to match(cur_groups)\n        expect(error.message).to match(old_pgrp)\n        expect(error.message).to match(value)\n      end\n    end\n  end\n\n  describe '#parse_password' do\n    def call_parse_password\n      File.open(my_fixture('aix_passwd_file.out')) do |f|\n        provider.parse_password(f)\n      end\n    end\n\n    it \"returns :absent if the user stanza doesn't exist\" do\n      resource[:name] = 'nonexistent_user'\n      expect(call_parse_password).to eql(:absent)\n    end\n\n    it \"returns absent if the user does not have a password\" do\n      resource[:name] = 'no_password_user'\n      expect(call_parse_password).to eql(:absent)\n    end\n\n    it \"returns the user's password\" do\n      expect(call_parse_password).to eql('some_password')\n    end\n\n    it \"returns the user's password with tabs\" do\n      resource[:name] = 'tab_password_user'\n      expect(call_parse_password).to eql('some_password')\n    end\n  end\n\n  # TODO: If we move from using Mocha to rspec's mocks,\n  # or a better and more robust mocking library, we should\n  # remove #parse_password and copy over its tests to here.\n  describe '#password' do\n  end\n\n  describe '#password=' do\n    let(:mock_tempfile) do\n      mock_tempfile_obj = double()\n      allow(mock_tempfile_obj).to receive(:<<)\n      allow(mock_tempfile_obj).to receive(:close)\n      allow(mock_tempfile_obj).to receive(:delete)\n      allow(mock_tempfile_obj).to receive(:path).and_return('tempfile_path')\n\n      allow(Tempfile).to receive(:new)\n        .with(\"puppet_#{provider.name}_pw\", {:encoding => Encoding::ASCII})\n        .and_return(mock_tempfile_obj)\n\n      mock_tempfile_obj\n    end\n    let(:cmd) do\n      [provider.class.command(:chpasswd), *provider.ia_module_args, '-e', '-c']\n    end\n    let(:execute_options) do\n      {\n        :failonfail => false,\n        :combine => true,\n        :stdinfile => mock_tempfile.path\n      }\n    end\n\n    it 'raises a Puppet::Error if chpasswd fails' do\n      allow(provider).to receive(:execute).with(cmd, execute_options).and_return(\"failed to change passwd!\")\n      expect { provider.password = 'foo' }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n        expect(error.message).to match(\"failed to change passwd!\")\n      end\n    end\n\n    it \"changes the user's password\" do\n      expect(provider).to receive(:execute).with(cmd, execute_options).and_return(\"\")\n      provider.password = 'foo'\n    end\n\n    it \"closes and deletes the tempfile\" do\n      allow(provider).to receive(:execute).with(cmd, execute_options).and_return(\"\")\n\n      expect(mock_tempfile).to receive(:close).twice\n      expect(mock_tempfile).to receive(:delete)\n\n      provider.password = 'foo'\n    end\n  end\n\n  describe '#create' do\n    it 'should create the user' do\n      allow(provider.resource).to receive(:should).with(anything).and_return(nil)\n      allow(provider.resource).to receive(:should).with(:groups).and_return('g1,g2')\n      allow(provider.resource).to receive(:should).with(:password).and_return('password')\n\n      expect(provider).to receive(:execute)\n      expect(provider).to receive(:groups=).with('g1,g2')\n      expect(provider).to receive(:password=).with('password')\n\n      provider.create\n    end\n  end\n\n  describe '#list_all_homes' do\n    it \"should return empty array and output debug on failure\" do\n      allow(Puppet::Util::Execution).to receive(:execute).and_raise(Puppet::ExecutionFailure, 'Execution failed')\n      expect(Puppet).to receive(:debug).with('Could not list home of all users: Execution failed')\n      expect(provider.list_all_homes).to eql({})\n    end\n  end\n\n  describe '#delete' do\n    before(:each) do\n      allow(File).to receive(:realpath).and_call_original\n      allow(FileUtils).to receive(:remove_entry_secure).and_call_original\n\n      allow(provider.resource).to receive(:should).with(anything).and_return(nil)\n      allow(provider).to receive(:home).and_return(Dir.tmpdir)\n      allow(provider).to receive(:execute).and_return(nil)\n      allow(provider).to receive(:object_info).and_return(nil)\n      allow(FileUtils).to receive(:remove_entry_secure).with(Dir.tmpdir, true).and_return(nil)\n    end\n\n    context 'with managehome true' do\n      before(:each) do\n        allow(provider.resource).to receive(:managehome?).and_return(true)\n        allow(provider).to receive(:list_all_homes).and_return([])\n      end\n\n      it 'should delete the user without error' do\n        expect{ provider.delete }.not_to raise_error\n      end\n\n      it \"should not remove home when relative\" do\n        allow(provider).to receive(:home).and_return('relative_path')\n\n        expect(Puppet).to receive(:debug).with(/Please make sure the path is not relative, symlink or '\\/'./)\n        provider.delete\n      end\n\n      it \"should not remove home when '/'\" do\n        allow(provider).to receive(:home).and_return('/')\n\n        expect(Puppet).to receive(:debug).with(/Please make sure the path is not relative, symlink or '\\/'./)\n        provider.delete\n      end\n\n      it \"should not remove home when symlink\" do\n        allow(Puppet::FileSystem).to receive(:symlink?).with(Dir.tmpdir).and_return(true)\n\n        expect(Puppet).to receive(:debug).with(/Please make sure the path is not relative, symlink or '\\/'./)\n        provider.delete\n      end\n\n      it \"should not remove home when other users would be affected\" do\n        allow(provider).to receive(:home).and_return('/special')\n        allow(File).to receive(:realpath).with('/special').and_return('/special')\n        allow(Puppet::Util).to receive(:absolute_path?).with('/special').and_return(true)\n        allow(provider).to receive(:list_all_homes).and_return([{:name => 'other_user', :home => '/special/other_user'}])\n\n        expect(Puppet).to receive(:debug).with(/it would remove the home directory '\\/special\\/other_user' of user 'other_user' also./)\n        provider.delete\n      end\n\n      it 'should remove homedir' do\n        expect(FileUtils).to receive(:remove_entry_secure).with(Dir.tmpdir, true)\n        provider.delete\n      end\n    end\n\n    context 'with managehome false' do\n      before(:each) do\n        allow(provider.resource).to receive(:managehome?).and_return(false)\n      end\n\n      it 'should delete the user without error' do\n        expect{ provider.delete }.not_to raise_error\n      end\n\n      it 'should not remove homedir' do\n        expect(FileUtils).not_to receive(:remove_entry_secure).with(Dir.tmpdir, true)\n      end\n\n      it 'should not print manage home debug messages' do\n        expect(Puppet).not_to receive(:debug).with(/Please make sure the path is not relative, symlink or '\\/'./)\n        expect(Puppet).not_to receive(:debug).with(/it would remove the home directory '\\/special\\/other_user' of user 'other_user' also./)\n\n        provider.delete\n      end\n    end\n  end\n\n  describe '#deletecmd' do\n    it 'uses the -p flag when removing the user' do\n      allow(provider.class).to receive(:command).with(:delete).and_return('delete')\n      allow(provider).to receive(:ia_module_args).and_return(['ia_module_args'])\n\n      expect(provider.deletecmd).to eql(\n        ['delete', '-p', 'ia_module_args', provider.resource.name]\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/directoryservice_spec.rb",
    "content": "# encoding: ASCII-8BIT\nrequire 'spec_helper'\n\nmodule Puppet::Util::Plist\nend\n\ndescribe Puppet::Type.type(:user).provider(:directoryservice), :if => Puppet.features.cfpropertylist? do \n  let(:username) { 'nonexistent_user' }\n  let(:user_path) { \"/Users/#{username}\" }\n  let(:resource) do\n    Puppet::Type.type(:user).new(\n      :name     => username,\n      :provider => :directoryservice\n    )\n  end\n  let(:provider) { resource.provider }\n  let(:users_plist_dir) { '/var/db/dslocal/nodes/Default/users' }\n\n  # This is the output of doing `dscl -plist . read /Users/<username>` which\n  # will return a hash of keys whose values are all arrays.\n  let(:user_plist_xml) do\n    '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n    <plist version=\"1.0\">\n    <dict>\n            <key>dsAttrTypeStandard:NFSHomeDirectory</key>\n            <array>\n            <string>/Users/nonexistent_user</string>\n            </array>\n            <key>dsAttrTypeStandard:RealName</key>\n            <array>\n            <string>nonexistent_user</string>\n            </array>\n            <key>dsAttrTypeStandard:PrimaryGroupID</key>\n            <array>\n            <string>22</string>\n            </array>\n            <key>dsAttrTypeStandard:UniqueID</key>\n            <array>\n            <string>1000</string>\n            </array>\n            <key>dsAttrTypeStandard:RecordName</key>\n            <array>\n            <string>nonexistent_user</string>\n            </array>\n    </dict>\n    </plist>'\n  end\n\n  # This is the same as above, however in a native Ruby hash instead\n  # of XML\n  let(:user_plist_hash) do\n    {\n      \"dsAttrTypeStandard:RealName\"         => [username],\n      \"dsAttrTypeStandard:NFSHomeDirectory\" => [user_path],\n      \"dsAttrTypeStandard:PrimaryGroupID\"   => [\"22\"],\n      \"dsAttrTypeStandard:UniqueID\"         => [\"1000\"],\n      \"dsAttrTypeStandard:RecordName\"       => [username]\n    }\n  end\n\n  let(:sha512_shadowhashdata_array) do\n    ['62706c69 73743030 d101025d 53414c54 45442d53 48413531 324f1044 7ea7d592 131f57b2 c8f8bdbc '\\\n     'ec8d9df1 2128a386 393a4f00 c7619bac 2622a44d 451419d1 1da512d5 915ab98e 39718ac9 4083fe2e '\\\n     'fd6bf710 a54d477f 8ff735b1 2587192d 080b1900 00000000 00010100 00000000 00000300 00000000 '\\\n     '00000000 00000000 000060']\n  end\n  # The below value is the result of executing\n  # `dscl -plist . read /Users/<username> ShadowHashData` on a 10.7\n  # system and converting it to a native Ruby Hash with Plist.parse_xml\n  let(:sha512_shadowhashdata_hash) do\n    {\n      'dsAttrTypeNative:ShadowHashData' => sha512_shadowhashdata_array\n    }\n  end\n\n  # The below is a binary plist that is stored in the ShadowHashData key\n  # on a 10.7 system.\n  let(:sha512_embedded_bplist) do\n    \"bplist00\\321\\001\\002]SALTED-SHA512O\\020D~\\247\\325\\222\\023\\037W\\262\\310\\370\\275\\274\\354\\215\\235\\361!(\\243\\2069:O\\000\\307a\\233\\254&\\\"\\244ME\\024\\031\\321\\035\\245\\022\\325\\221Z\\271\\2169q\\212\\311@\\203\\376.\\375k\\367\\020\\245MG\\177\\217\\3675\\261%\\207\\031-\\b\\v\\031\\000\\000\\000\\000\\000\\000\\001\\001\\000\\000\\000\\000\\000\\000\\000\\003\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000`\"\n  end\n\n  # The below is a Base64 encoded string representing a salted-SHA512 password\n  # hash.\n  let(:sha512_pw_string) do\n    \"~\\247\\325\\222\\023\\037W\\262\\310\\370\\275\\274\\354\\215\\235\\361!(\\243\\2069:O\\000\\307a\\233\\254&\\\"\\244ME\\024\\031\\321\\035\\245\\022\\325\\221Z\\271\\2169q\\212\\311@\\203\\376.\\375k\\367\\020\\245MG\\177\\217\\3675\\261%\\207\\031-\"\n  end\n\n  # The below is the result of converting sha512_embedded_bplist to XML and\n  # parsing it with Plist.parse_xml. It is a Ruby Hash whose value is a\n  # Base64 encoded salted-SHA512 password hash.\n  let(:sha512_embedded_bplist_hash) do\n    { 'SALTED-SHA512' => sha512_pw_string }\n  end\n\n  # The value below is the result of converting sha512_pw_string to Hex.\n  let(:sha512_password_hash) do\n    '7ea7d592131f57b2c8f8bdbcec8d9df12128a386393a4f00c7619bac2622a44d451419d11da512d5915ab98e39718ac94083fe2efd6bf710a54d477f8ff735b12587192d'\n  end\n\n  let(:pbkdf2_shadowhashdata_array) do\n    ['62706c69 73743030 d101025f 10145341 4c544544 2d534841 3531322d 50424b44 4632d303 04050607 '\\\n     '0857656e 74726f70 79547361 6c745a69 74657261 74696f6e 734f1080 0590ade1 9e6953c1 35ae872a '\\\n     'e7761823 5df7d46c 63de7f9a 0fcdf2cd 9e7d85e4 b7ca8681 01235b61 58e05a30 9805ee48 14b027a4 '\\\n     'be9c23ec 2926bc81 72269aff ba5c9a59 85e81091 fa689807 6d297f1f aa75fa61 7551ef16 71d75200 '\\\n     '55c4a0d9 7b9b9c58 05aa322b aedbcd8e e9c52381 1653ac2e a9e9c8d8 f1ac519a 0f2b595e 4f102093 '\\\n     '77c46908 a1c8ac2c 3e45c0d4 4da8ad0f cd85ec5c 14d9a59f fc40c9da 31f0ec11 60b0080b 22293136 '\\\n     '41c4e700 00000000 00010100 00000000 00000900 00000000 00000000 00000000 0000ea']\n  end\n  # The below value is the result of executing\n  # `dscl -plist . read /Users/<username> ShadowHashData` on a 10.8\n  # system and converting it to a native Ruby Hash with Plist.parse_xml\n  let(:pbkdf2_shadowhashdata_hash) do\n    {\n      \"dsAttrTypeNative:ShadowHashData\"=> pbkdf2_shadowhashdata_array\n    }\n  end\n\n  # The below value is the result of converting pbkdf2_embedded_bplist to XML and\n  # parsing it with Plist.parse_xml.\n  let(:pbkdf2_embedded_bplist_hash) do\n    {\n      'SALTED-SHA512-PBKDF2' => {\n        'entropy'    => pbkdf2_pw_string,\n        'salt'       => pbkdf2_salt_string,\n        'iterations' => pbkdf2_iterations_value\n      }\n    }\n  end\n\n  # The value below is the result of converting pbkdf2_pw_string to Hex.\n  let(:pbkdf2_password_hash) do\n    '0590ade19e6953c135ae872ae77618235df7d46c63de7f9a0fcdf2cd9e7d85e4b7ca868101235b6158e05a309805ee4814b027a4be9c23ec2926bc8172269affba5c9a5985e81091fa6898076d297f1faa75fa617551ef1671d7520055c4a0d97b9b9c5805aa322baedbcd8ee9c523811653ac2ea9e9c8d8f1ac519a0f2b595e'\n  end\n\n  # The below is a binary plist that is stored in the ShadowHashData key\n  # of a 10.8 system.\n  let(:pbkdf2_embedded_plist) do\n    \"bplist00\\321\\001\\002_\\020\\024SALTED-SHA512-PBKDF2\\323\\003\\004\\005\\006\\a\\bWentropyTsaltZiterationsO\\020\\200\\005\\220\\255\\341\\236iS\\3015\\256\\207*\\347v\\030#]\\367\\324lc\\336\\177\\232\\017\\315\\362\\315\\236}\\205\\344\\267\\312\\206\\201\\001#[aX\\340Z0\\230\\005\\356H\\024\\260'\\244\\276\\234#\\354)&\\274\\201r&\\232\\377\\272\\\\\\232Y\\205\\350\\020\\221\\372h\\230\\am)\\177\\037\\252u\\372auQ\\357\\026q\\327R\\000U\\304\\240\\331{\\233\\234X\\005\\2522+\\256\\333\\315\\216\\351\\305#\\201\\026S\\254.\\251\\351\\310\\330\\361\\254Q\\232\\017+Y^O\\020 \\223w\\304i\\b\\241\\310\\254,>E\\300\\324M\\250\\255\\017\\315\\205\\354\\\\\\024\\331\\245\\237\\374@\\311\\3321\\360\\354\\021`\\260\\b\\v\\\")16A\\304\\347\\000\\000\\000\\000\\000\\000\\001\\001\\000\\000\\000\\000\\000\\000\\000\\t\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\352\"\n  end\n\n  # The below value is a Base64 encoded string representing a PBKDF2 password\n  # hash.\n  let(:pbkdf2_pw_string) do\n    \"\\005\\220\\255\\341\\236iS\\3015\\256\\207*\\347v\\030#]\\367\\324lc\\336\\177\\232\\017\\315\\362\\315\\236}\\205\\344\\267\\312\\206\\201\\001#[aX\\340Z0\\230\\005\\356H\\024\\260'\\244\\276\\234#\\354)&\\274\\201r&\\232\\377\\272\\\\\\232Y\\205\\350\\020\\221\\372h\\230\\am)\\177\\037\\252u\\372auQ\\357\\026q\\327R\\000U\\304\\240\\331{\\233\\234X\\005\\2522+\\256\\333\\315\\216\\351\\305#\\201\\026S\\254.\\251\\351\\310\\330\\361\\254Q\\232\\017+Y^\"\n  end\n\n  # The below value is a Base64 encoded string representing a PBKDF2 salt\n  # string.\n  let(:pbkdf2_salt_string) do\n    \"\\223w\\304i\\b\\241\\310\\254,>E\\300\\324M\\250\\255\\017\\315\\205\\354\\\\\\024\\331\\245\\237\\374@\\311\\3321\\360\\354\"\n  end\n\n  # The below value represents the Hex value of a PBKDF2 salt string\n  let(:pbkdf2_salt_value) do\n    \"9377c46908a1c8ac2c3e45c0d44da8ad0fcd85ec5c14d9a59ffc40c9da31f0ec\"\n  end\n\n  # The below value is an Integer iterations value used in the PBKDF2\n  # key stretching algorithm\n  let(:pbkdf2_iterations_value) do\n    24752\n  end\n\n  let(:pbkdf2_and_ssha512_shadowhashdata_array) do\n    ['62706c69 73743030 d2010203 0a5f1014 53414c54 45442d53 48413531 322d5042 4b444632 5d53414c '\\\n     '5445442d 53484135 3132d304 05060708 0957656e 74726f70 79547361 6c745a69 74657261 74696f6e '\\\n     '734f1080 0590ade1 9e6953c1 35ae872a e7761823 5df7d46c 63de7f9a 0fcdf2cd 9e7d85e4 b7ca8681 '\\\n     '01235b61 58e05a30 9805ee48 14b027a4 be9c23ec 2926bc81 72269aff ba5c9a59 85e81091 fa689807 '\\\n     '6d297f1f aa75fa61 7551ef16 71d75200 55c4a0d9 7b9b9c58 05aa322b aedbcd8e e9c52381 1653ac2e '\\\n     'a9e9c8d8 f1ac519a 0f2b595e 4f102093 77c46908 a1c8ac2c 3e45c0d4 4da8ad0f cd85ec5c 14d9a59f '\\\n     'fc40c9da 31f0ec11 60b04f10 447ea7d5 92131f57 b2c8f8bd bcec8d9d f12128a3 86393a4f 00c7619b '\\\n     'ac2622a4 4d451419 d11da512 d5915ab9 8e39718a c94083fe 2efd6bf7 10a54d47 7f8ff735 b1258719 '\\\n     '2d000800 0d002400 32003900 41004600 5100d400 f700fa00 00000000 00020100 00000000 00000b00 '\\\n     '00000000 00000000 00000000 000141']\n  end\n\n  let(:pbkdf2_and_ssha512_shadowhashdata_hash) do\n    {\n      'dsAttrTypeNative:ShadowHashData' => pbkdf2_and_ssha512_shadowhashdata_array\n    }\n  end\n\n  let (:pbkdf2_and_ssha512_embedded_plist) do\n    \"bplist00\\xD2\\x01\\x02\\x03\\n_\\x10\\x14SALTED-SHA512-PBKDF2]SALTED-SHA512\\xD3\\x04\\x05\\x06\\a\\b\\tWentropyTsaltZiterationsO\\x10\\x80\\x05\\x90\\xAD\\xE1\\x9EiS\\xC15\\xAE\\x87*\\xE7v\\x18#]\\xF7\\xD4lc\\xDE\\x7F\\x9A\\x0F\\xCD\\xF2\\xCD\\x9E}\\x85\\xE4\\xB7\\xCA\\x86\\x81\\x01#[aX\\xE0Z0\\x98\\x05\\xEEH\\x14\\xB0'\\xA4\\xBE\\x9C#\\xEC)&\\xBC\\x81r&\\x9A\\xFF\\xBA\\\\\\x9AY\\x85\\xE8\\x10\\x91\\xFAh\\x98\\am)\\x7F\\x1F\\xAAu\\xFAauQ\\xEF\\x16q\\xD7R\\x00U\\xC4\\xA0\\xD9{\\x9B\\x9CX\\x05\\xAA2+\\xAE\\xDB\\xCD\\x8E\\xE9\\xC5#\\x81\\x16S\\xAC.\\xA9\\xE9\\xC8\\xD8\\xF1\\xACQ\\x9A\\x0F+Y^O\\x10 \\x93w\\xC4i\\b\\xA1\\xC8\\xAC,>E\\xC0\\xD4M\\xA8\\xAD\\x0F\\xCD\\x85\\xEC\\\\\\x14\\xD9\\xA5\\x9F\\xFC@\\xC9\\xDA1\\xF0\\xEC\\x11`\\xB0O\\x10D~\\xA7\\xD5\\x92\\x13\\x1FW\\xB2\\xC8\\xF8\\xBD\\xBC\\xEC\\x8D\\x9D\\xF1!(\\xA3\\x869:O\\x00\\xC7a\\x9B\\xAC&\\\"\\xA4ME\\x14\\x19\\xD1\\x1D\\xA5\\x12\\xD5\\x91Z\\xB9\\x8E9q\\x8A\\xC9@\\x83\\xFE.\\xFDk\\xF7\\x10\\xA5MG\\x7F\\x8F\\xF75\\xB1%\\x87\\x19-\\x00\\b\\x00\\r\\x00$\\x002\\x009\\x00A\\x00F\\x00Q\\x00\\xD4\\x00\\xF7\\x00\\xFA\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\v\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01A\"\n  end\n\n  let (:pbkdf2_and_ssha512_embedded_bplist_hash) do\n    {\n      \"SALTED-SHA512-PBKDF2\" => {\n        \"entropy\"     => pbkdf2_pw_string,\n        \"salt\"        => pbkdf2_salt_value,\n        \"iterations\"  => pbkdf2_iterations_value,\n      },\n      \"SALTED-SHA512\" => sha512_password_hash\n    }\n  end\n\n  let (:dsimport_preamble) do\n    '0x0A 0x5C 0x3A 0x2C dsRecTypeStandard:Users 2 dsAttrTypeStandard:RecordName base64:dsAttrTypeNative:ShadowHashData'\n  end\n\n  let (:dsimport_contents) do\n    <<-DSIMPORT\n#{dsimport_preamble}\n#{username}:#{Base64.strict_encode64(sha512_embedded_bplist)}\n    DSIMPORT\n  end\n\n  # The below represents output of 'dscl -plist . readall /Users' converted to\n  # a native Ruby hash if only one user were installed on the system.\n  # This lets us check the behavior of all the methods necessary to return a\n  # user's groups property by controlling the data provided by dscl\n  let(:testuser_base) do\n    {\n      \"dsAttrTypeStandard:RecordName\"             =>[\"nonexistent_user\"],\n      \"dsAttrTypeStandard:UniqueID\"               =>[\"1000\"],\n      \"dsAttrTypeStandard:AuthenticationAuthority\"=>\n       [\";Kerberosv5;;testuser@LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81;LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81\",\n        \";ShadowHash;HASHLIST:<SALTED-SHA512>\"],\n      \"dsAttrTypeStandard:AppleMetaNodeLocation\"  =>[\"/Local/Default\"],\n      \"dsAttrTypeStandard:NFSHomeDirectory\"       =>[\"/Users/nonexistent_user\"],\n      \"dsAttrTypeStandard:RecordType\"             =>[\"dsRecTypeStandard:Users\"],\n      \"dsAttrTypeStandard:RealName\"               =>[\"nonexistent_user\"],\n      \"dsAttrTypeStandard:Password\"               =>[\"********\"],\n      \"dsAttrTypeStandard:PrimaryGroupID\"         =>[\"22\"],\n      \"dsAttrTypeStandard:GeneratedUID\"           =>[\"0A7D5B63-3AD4-4CA7-B03E-85876F1D1FB3\"],\n      \"dsAttrTypeStandard:AuthenticationHint\"     =>[\"\"],\n      \"dsAttrTypeNative:KerberosKeys\"             =>\n       [\"30820157 a1030201 02a08201 4e308201 4a3074a1 2b3029a0 03020112 a1220420 54af3992 1c198bf8 94585a6b 2fba445b c8482228 0dcad666 ea62e038 99e59c45 a2453043 a0030201 03a13c04 3a4c4b44 433a5348 41312e34 33383345 31353244 39443339 34414133 32443133 41453938 46364636 45314645 38443030 46383174 65737475 73657230 64a11b30 19a00302 0111a112 04106375 7d97b2ce ca8343a6 3b0f73d5 1001a245 3043a003 020103a1 3c043a4c 4b44433a 53484131 2e343338 33453135 32443944 33393441 41333244 31334145 39384636 46364531 46453844 30304638 31746573 74757365 72306ca1 233021a0 03020110 a11a0418 67b09be3 5131b670 f8e9265e 62459b4c 19435419 fe918519 a2453043 a0030201 03a13c04 3a4c4b44 433a5348 41312e34 33383345 31353244 39443339 34414133 32443133 41453938 46364636 45314645 38443030 46383174 65737475 736572\"],\n      \"dsAttrTypeStandard:PasswordPolicyOptions\"  =>\n       [\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n          <!DOCTYPE plist PUBLIC \\\"-//Apple//DTD PLIST 1.0//EN\\\" \\\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\\">\\n          <plist version=\\\"1.0\\\">\\n          <dict>\\n            <key>failedLoginCount</key>\\n            <integer>0</integer>\\n            <key>failedLoginTimestamp</key>\\n            <date>2001-01-01T00:00:00Z</date>\\n            <key>lastLoginTimestamp</key>\\n            <date>2001-01-01T00:00:00Z</date>\\n            <key>passwordTimestamp</key>\\n            <date>2012-08-10T23:53:50Z</date>\\n          </dict>\\n          </plist>\\n          \"],\n      \"dsAttrTypeStandard:UserShell\"              =>[\"/bin/bash\"],\n      \"dsAttrTypeNative:ShadowHashData\"           =>\n       [\"62706c69 73743030 d101025d 53414c54 45442d53 48413531 324f1044 7ea7d592 131f57b2 c8f8bdbc ec8d9df1 2128a386 393a4f00 c7619bac 2622a44d 451419d1 1da512d5 915ab98e 39718ac9 4083fe2e fd6bf710 a54d477f 8ff735b1 2587192d 080b1900 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000060\"]\n     }\n  end\n  let(:testuser_hash) do\n    [\n      testuser_base.merge(sha512_shadowhashdata_hash),\n      testuser_base.merge(pbkdf2_shadowhashdata_hash),\n    ]\n  end\n\n  # The below represents the result of running Plist.parse_xml on XML\n  # data returned from the `dscl -plist . readall /Groups` command.\n  # (AKA: What the get_list_of_groups method returns)\n  let(:group_plist_hash_guid) do\n    [{\n      'dsAttrTypeStandard:RecordName'      => ['testgroup'],\n      'dsAttrTypeStandard:GroupMembership' => [\n                                                username,\n                                                'jeff',\n                                                'zack'\n                                              ],\n      'dsAttrTypeStandard:GroupMembers'    => [\n                                                \"guid#{username}\",\n                                                'guidtestuser',\n                                                'guidjeff',\n                                                'guidzack'\n                                              ],\n    },\n    {\n      'dsAttrTypeStandard:RecordName'      => ['second'],\n      'dsAttrTypeStandard:GroupMembership' => [\n                                                'jeff',\n                                                'zack'\n                                              ],\n      'dsAttrTypeStandard:GroupMembers'    => [\n                                                \"guid#{username}\",\n                                                'guidjeff',\n                                                'guidzack'\n                                              ],\n    },\n    {\n      'dsAttrTypeStandard:RecordName'      => ['third'],\n      'dsAttrTypeStandard:GroupMembership' => [\n                                                username,\n                                                'jeff',\n                                                'zack'\n                                              ],\n      'dsAttrTypeStandard:GroupMembers'    => [\n                                                \"guid#{username}\",\n                                                'guidtestuser',\n                                                'guidjeff',\n                                                'guidzack'\n                                              ],\n    }]\n  end\n\n\n  describe 'Creating a user that does not exist' do\n    # These are the defaults that the provider will use if a user does\n    # not provide a value\n    let(:defaults) do\n      {\n        'UniqueID'         => '1000',\n        'RealName'         => resource[:name],\n        'PrimaryGroupID'   => 20,\n        'UserShell'        => '/bin/bash',\n        'NFSHomeDirectory' => \"/Users/#{resource[:name]}\"\n      }\n    end\n\n    before :each do\n      # Stub out all calls to dscl with default values from above\n      defaults.each do |key, val|\n        allow(provider).to receive(:create_attribute_with_dscl).with('Users', username, key, val)\n      end\n\n      # Mock the rest of the dscl calls. We can't assume that our Linux\n      # build system will have the dscl binary\n      allow(provider).to receive(:create_new_user).with(username)\n      allow(provider.class).to receive(:get_attribute_from_dscl).with('Users', username, 'GeneratedUID').and_return({'dsAttrTypeStandard:GeneratedUID' => ['GUID']})\n      allow(provider).to receive(:next_system_id).and_return('1000')\n    end\n\n    it 'should not raise any errors when creating a user with default values' do\n      provider.create\n    end\n\n    %w{password iterations salt}.each do |value|\n      it \"should call ##{value}= if a #{value} attribute is specified\" do\n        resource[value.intern] = 'somevalue'\n        setter = (value << '=').intern\n        expect(provider).to receive(setter).with('somevalue')\n        provider.create\n      end\n    end\n\n    it 'should merge the GroupMembership and GroupMembers dscl values if a groups attribute is specified' do\n      resource[:groups] = 'somegroup'\n      expect(provider).to receive(:merge_attribute_with_dscl).with('Groups', 'somegroup', 'GroupMembership', username)\n      expect(provider).to receive(:merge_attribute_with_dscl).with('Groups', 'somegroup', 'GroupMembers', 'GUID')\n      provider.create\n    end\n\n    it 'should convert group names into integers' do\n      resource[:gid] = 'somegroup'\n      expect(Puppet::Util).to receive(:gid).with('somegroup').and_return(21)\n      expect(provider).to receive(:create_attribute_with_dscl).with('Users', username, 'PrimaryGroupID', 21)\n      provider.create\n    end\n  end\n\n  describe 'Update existing user' do\n    describe 'home=' do\n      context 'on OS X 10.14' do\n        before do\n          provider.instance_variable_set(:@property_hash, { home: 'value' })\n          allow(provider.class).to receive(:get_os_version).and_return('10.14')\n        end\n\n        it 'raises error' do\n          expect { provider.home = 'new' }.to \\\n            raise_error(Puppet::Error, \"OS X version 10.14 does not allow changing home using puppet\")\n        end\n      end\n  end\n\n  describe 'uid=' do\n    context 'on OS X 10.14' do\n      before do\n        provider.instance_variable_set(:@property_hash, { uid: 'value' })\n        allow(provider.class).to receive(:get_os_version).and_return('10.14')\n      end\n\n      it 'raises error' do\n        expect { provider.uid = 'new' }.to \\\n          raise_error(Puppet::Error, \"OS X version 10.14 does not allow changing uid using puppet\")\n      end\n    end\n  end\nend\n\n  describe 'self#instances' do\n    it 'should create an array of provider instances' do\n      expect(provider.class).to receive(:get_all_users).and_return(['foo', 'bar'])\n      ['foo', 'bar'].each do |user|\n        expect(provider.class).to receive(:generate_attribute_hash).with(user).and_return({})\n      end\n      instances = provider.class.instances\n\n      expect(instances).to be_a_kind_of Array\n      instances.each do |instance|\n        expect(instance).to be_a_kind_of Puppet::Provider\n      end\n    end\n  end\n\n  describe 'self#get_all_users', :if => Puppet.features.cfpropertylist? do\n    let(:empty_plist) do\n      '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n      <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n      <plist version=\"1.0\">\n      <dict>\n      </dict>\n      </plist>'\n    end\n\n    it 'should return a hash of user attributes' do\n      expect(provider.class).to receive(:dscl).with('-plist', '.', 'readall', '/Users').and_return(user_plist_xml)\n      expect(provider.class.get_all_users).to eq(user_plist_hash)\n    end\n\n    it 'should return a hash when passed an empty plist' do\n      expect(provider.class).to receive(:dscl).with('-plist', '.', 'readall', '/Users').and_return(empty_plist)\n      expect(provider.class.get_all_users).to eq({})\n    end\n  end\n\n  describe 'self#generate_attribute_hash' do\n    let(:user_plist_resource) do\n      {\n        :ensure         => :present,\n        :provider       => :directoryservice,\n        :groups         => 'testgroup,third',\n        :comment        => username,\n        :password       => sha512_password_hash,\n        :shadowhashdata => sha512_shadowhashdata_array,\n        :name           => username,\n        :uid            => 1000,\n        :gid            => 22,\n        :home           => user_path\n      }\n    end\n\n    before :each do\n      allow(provider.class).to receive(:get_os_version).and_return('10.7')\n      allow(provider.class).to receive(:get_all_users).and_return(testuser_hash)\n      allow(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash_guid)\n      allow(provider.class).to receive(:convert_binary_to_hash).with(sha512_embedded_bplist).and_return(sha512_embedded_bplist_hash)\n      allow(provider.class).to receive(:convert_binary_to_hash).with(pbkdf2_embedded_plist).and_return(pbkdf2_embedded_bplist_hash)\n      provider.class.prefetch({})\n    end\n\n    it 'should return :uid values as an Integer' do\n      expect(provider.class.generate_attribute_hash(user_plist_hash)[:uid]).to be_a Integer\n    end\n\n    it 'should return :gid values as an Integer' do\n      expect(provider.class.generate_attribute_hash(user_plist_hash)[:gid]).to be_a Integer\n    end\n\n    it 'should return a hash of resource attributes' do\n      expect(provider.class.generate_attribute_hash(user_plist_hash.merge(sha512_shadowhashdata_hash))).to eq(user_plist_resource)\n    end\n  end\n\n  describe 'self#generate_attribute_hash with pbkdf2 and ssha512' do\n    let(:user_plist_resource) do\n      {\n        :ensure         => :present,\n        :provider       => :directoryservice,\n        :groups         => 'testgroup,third',\n        :comment        => username,\n        :password       => pbkdf2_password_hash,\n        :iterations     => pbkdf2_iterations_value,\n        :salt           => pbkdf2_salt_value,\n        :shadowhashdata => pbkdf2_and_ssha512_shadowhashdata_array,\n        :name           => username,\n        :uid            => 1000,\n        :gid            => 22,\n        :home           => user_path\n      }\n    end\n\n    before :each do\n      allow(provider.class).to receive(:get_os_version).and_return('10.7')\n      allow(provider.class).to receive(:get_all_users).and_return(testuser_hash)\n      allow(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash_guid)\n      provider.class.prefetch({})\n    end\n\n    it 'should return a hash of resource attributes' do\n      expect(provider.class.generate_attribute_hash(user_plist_hash.merge(pbkdf2_and_ssha512_shadowhashdata_hash))).to eq(user_plist_resource)\n    end\n  end\n\n  describe 'self#generate_attribute_hash empty shadowhashdata' do\n    let(:user_plist_resource) do\n      {\n        :ensure         => :present,\n        :provider       => :directoryservice,\n        :groups         => 'testgroup,third',\n        :comment        => username,\n        :password       => '*',\n        :shadowhashdata => nil,\n        :name           => username,\n        :uid            => 1000,\n        :gid            => 22,\n        :home           => user_path\n      }\n    end\n\n    it 'should handle empty shadowhashdata' do\n      allow(provider.class).to receive(:get_os_version).and_return('10.7')\n      allow(provider.class).to receive(:get_all_users).and_return([testuser_base])\n      allow(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash_guid)\n      provider.class.prefetch({})\n      expect(provider.class.generate_attribute_hash(user_plist_hash)).to eq(user_plist_resource)\n    end\n  end\n\n  describe '#delete' do\n    it 'should call dscl when destroying/deleting a resource' do\n      expect(provider).to receive(:dscl).with('.', '-delete', user_path)\n      provider.delete\n    end\n  end\n\n  describe 'the groups property' do\n    # The below represents the result of running Plist.parse_xml on XML\n    # data returned from the `dscl -plist . readall /Groups` command.\n    # (AKA: What the get_list_of_groups method returns)\n    let(:group_plist_hash) do\n      [{\n        'dsAttrTypeStandard:RecordName'      => ['testgroup'],\n        'dsAttrTypeStandard:GroupMembership' => [\n                                                  'testuser',\n                                                  username,\n                                                  'jeff',\n                                                  'zack'\n                                                ],\n        'dsAttrTypeStandard:GroupMembers'    => [\n                                                  'guidtestuser',\n                                                  'guidjeff',\n                                                  'guidzack'\n                                                ],\n      },\n      {\n        'dsAttrTypeStandard:RecordName'      => ['second'],\n        'dsAttrTypeStandard:GroupMembership' => [\n                                                  username,\n                                                  'testuser',\n                                                  'jeff',\n                                                ],\n        'dsAttrTypeStandard:GroupMembers'    => [\n                                                  'guidtestuser',\n                                                  'guidjeff',\n                                                ],\n      },\n      {\n        'dsAttrTypeStandard:RecordName'      => ['third'],\n        'dsAttrTypeStandard:GroupMembership' => [\n                                                  'jeff',\n                                                  'zack'\n                                                ],\n        'dsAttrTypeStandard:GroupMembers'    => [\n                                                  'guidjeff',\n                                                  'guidzack'\n                                                ],\n      }]\n    end\n\n    before :each do\n      allow(provider.class).to receive(:get_all_users).and_return(testuser_hash)\n      allow(provider.class).to receive(:get_os_version).and_return('10.7')\n    end\n\n    it \"should return a list of groups if the user's name matches GroupMembership\" do\n      expect(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash)\n      expect(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash)\n      expect(provider.class.prefetch({}).first.groups).to eq('second,testgroup')\n    end\n\n    it \"should return a list of groups if the user's GUID matches GroupMembers\" do\n      expect(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash_guid)\n      expect(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash_guid)\n      expect(provider.class.prefetch({}).first.groups).to eq('testgroup,third')\n    end\n  end\n\n  describe '#groups=' do\n    let(:group_plist_one_two_three) do\n      [{\n        'dsAttrTypeStandard:RecordName'      => ['one'],\n        'dsAttrTypeStandard:GroupMembership' => [\n                                                  'jeff',\n                                                  'zack'\n                                                ],\n        'dsAttrTypeStandard:GroupMembers'    => [\n                                                  'guidjeff',\n                                                  'guidzack'\n                                                ],\n      },\n      {\n        'dsAttrTypeStandard:RecordName'      => ['two'],\n        'dsAttrTypeStandard:GroupMembership' => [\n                                                  'jeff',\n                                                  'zack',\n                                                  username\n                                                ],\n        'dsAttrTypeStandard:GroupMembers'    => [\n                                                  'guidjeff',\n                                                  'guidzack'\n                                                ],\n      },\n      {\n        'dsAttrTypeStandard:RecordName'      => ['three'],\n        'dsAttrTypeStandard:GroupMembership' => [\n                                                  'jeff',\n                                                  'zack',\n                                                  username\n                                                ],\n        'dsAttrTypeStandard:GroupMembers'    => [\n                                                  'guidjeff',\n                                                  'guidzack'\n                                                ],\n      }]\n    end\n\n    before :each do\n      allow(provider.class).to receive(:get_all_users).and_return(testuser_hash)\n      allow(provider.class).to receive(:get_list_of_groups).and_return(group_plist_one_two_three)\n    end\n\n    it 'should call dscl to add necessary groups' do\n      expect(provider.class).to receive(:get_attribute_from_dscl).with('Users', username, 'GeneratedUID').and_return({'dsAttrTypeStandard:GeneratedUID' => ['guidnonexistent_user']})\n      expect(provider).to receive(:groups).and_return('two,three')\n      expect(provider).to receive(:dscl).with('.', '-merge', '/Groups/one', 'GroupMembership', 'nonexistent_user')\n      expect(provider).to receive(:dscl).with('.', '-merge', '/Groups/one', 'GroupMembers', 'guidnonexistent_user')\n      provider.class.prefetch({})\n      provider.groups= 'one,two,three'\n    end\n\n    it 'should call the get_salted_sha512 method on 10.7 and return the correct hash' do\n      expect(provider.class).to receive(:convert_binary_to_hash).with(sha512_embedded_bplist).and_return(sha512_embedded_bplist_hash)\n      expect(provider.class).to receive(:convert_binary_to_hash).with(pbkdf2_embedded_plist).and_return(pbkdf2_embedded_bplist_hash)\n      expect(provider.class.prefetch({}).first.password).to eq(sha512_password_hash)\n    end\n\n    it 'should call the get_salted_sha512_pbkdf2 method on 10.8 and return the correct hash' do\n      expect(provider.class).to receive(:convert_binary_to_hash).with(sha512_embedded_bplist).and_return(sha512_embedded_bplist_hash)\n      expect(provider.class).to receive(:convert_binary_to_hash).with(pbkdf2_embedded_plist).and_return(pbkdf2_embedded_bplist_hash)\n      expect(provider.class.prefetch({}).last.password).to eq(pbkdf2_password_hash)\n    end\n  end\n\n  describe '#password=' do\n    before :each do\n      allow(provider).to receive(:sleep)\n      allow(provider).to receive(:flush_dscl_cache)\n    end\n\n    it 'should call write_password_to_users_plist when setting the password' do\n      allow(provider.class).to receive(:get_os_version).and_return('10.7')\n      expect(provider).to receive(:write_password_to_users_plist).with(sha512_password_hash)\n      provider.password = sha512_password_hash\n    end\n\n    it 'should call write_password_to_users_plist when setting the password' do\n      allow(provider.class).to receive(:get_os_version).and_return('10.8')\n      resource[:salt] = pbkdf2_salt_value\n      resource[:iterations] = pbkdf2_iterations_value\n      resource[:password] = pbkdf2_password_hash\n\n      expect(provider).to receive(:write_password_to_users_plist).with(pbkdf2_password_hash)\n\n      provider.password = resource[:password]\n    end\n\n\n    it \"should raise an error on 10.7 if a password hash that doesn't contain 136 characters is passed\" do\n      allow(provider.class).to receive(:get_os_version).and_return('10.7')\n      expect { provider.password = 'password' }.to raise_error Puppet::Error, /OS X 10\\.7 requires a Salted SHA512 hash password of 136 characters\\.  Please check your password and try again/\n    end\n  end\n\n  describe \"passwords on 10.8\" do\n    before :each do\n      allow(provider.class).to receive(:get_os_version).and_return('10.8')\n    end\n\n    it \"should raise an error on 10.8 if a password hash that doesn't contain 256 characters is passed\" do\n      expect do\n        provider.password = 'password'\n      end.to raise_error(Puppet::Error, /OS X versions > 10\\.7 require a Salted SHA512 PBKDF2 password hash of 256 characters\\. Please check your password and try again\\./)\n    end\n\n    it \"fails if a password is given but not salt and iterations\" do\n      resource[:password] = pbkdf2_password_hash\n\n      expect do\n        provider.password = resource[:password]\n      end.to raise_error(Puppet::Error, /OS X versions > 10\\.7 use PBKDF2 password hashes, which requires all three of salt, iterations, and password hash\\. This resource is missing: salt, iterations\\./)\n    end\n\n    it \"fails if salt is given but not password and iterations\" do\n      resource[:salt] = pbkdf2_salt_value\n\n      expect do\n        provider.salt = resource[:salt]\n      end.to raise_error(Puppet::Error, /OS X versions > 10\\.7 use PBKDF2 password hashes, which requires all three of salt, iterations, and password hash\\. This resource is missing: password, iterations\\./)\n    end\n\n    it \"fails if iterations is given but not password and salt\" do\n      resource[:iterations] = pbkdf2_iterations_value\n\n      expect do\n        provider.iterations = resource[:iterations]\n      end.to raise_error(Puppet::Error, /OS X versions > 10\\.7 use PBKDF2 password hashes, which requires all three of salt, iterations, and password hash\\. This resource is missing: password, salt\\./)\n    end\n  end\n\n  describe '#get_list_of_groups', :if => Puppet.features.cfpropertylist? do\n    # The below value is the result of running `dscl -plist . readall /Groups`\n    # on an OS X system.\n    let(:groups_xml) do\n      '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n       <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n       <plist version=\"1.0\">\n       <array>\n         <dict>\n           <key>dsAttrTypeStandard:AppleMetaNodeLocation</key>\n           <array>\n             <string>/Local/Default</string>\n           </array>\n           <key>dsAttrTypeStandard:GeneratedUID</key>\n           <array>\n             <string>ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000053</string>\n           </array>\n           <key>dsAttrTypeStandard:Password</key>\n           <array>\n             <string>*</string>\n           </array>\n           <key>dsAttrTypeStandard:PrimaryGroupID</key>\n           <array>\n             <string>83</string>\n           </array>\n           <key>dsAttrTypeStandard:RealName</key>\n           <array>\n             <string>SPAM Assassin Group 2</string>\n           </array>\n           <key>dsAttrTypeStandard:RecordName</key>\n           <array>\n             <string>_amavisd</string>\n             <string>amavisd</string>\n           </array>\n           <key>dsAttrTypeStandard:RecordType</key>\n           <array>\n             <string>dsRecTypeStandard:Groups</string>\n           </array>\n         </dict>\n        </array>\n      </plist>'\n    end\n\n    # The below value is the result of executing Plist.parse_xml on\n    # groups_xml\n    let(:groups_hash) do\n      [{ 'dsAttrTypeStandard:AppleMetaNodeLocation' => ['/Local/Default'],\n           'dsAttrTypeStandard:GeneratedUID'          => ['ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000053'],\n           'dsAttrTypeStandard:Password'              => ['*'],\n           'dsAttrTypeStandard:PrimaryGroupID'        => ['83'],\n           'dsAttrTypeStandard:RealName'              => ['SPAM Assassin Group 2'],\n           'dsAttrTypeStandard:RecordName'            => ['_amavisd', 'amavisd'],\n           'dsAttrTypeStandard:RecordType'            => ['dsRecTypeStandard:Groups']\n        }]\n    end\n\n    before :each do\n      # Ensure we don't have a value cached from another spec\n      provider.class.instance_variable_set(:@groups, nil) if provider.class.instance_variable_defined? :@groups\n    end\n\n    it 'should return an array of hashes containing group data' do\n      expect(provider.class).to receive(:dscl).with('-plist', '.', 'readall', '/Groups').and_return(groups_xml)\n      expect(provider.class.get_list_of_groups).to eq(groups_hash)\n    end\n  end\n\n  describe '#get_attribute_from_dscl', :if => Puppet.features.cfpropertylist? do\n    # The below value is the result of executing\n    # `dscl -plist . read /Users/<username/ GeneratedUID`\n    # on an OS X system.\n    let(:user_guid_xml) do\n      '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n       <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n       <plist version=\"1.0\">\n       <dict>\n         <key>dsAttrTypeStandard:GeneratedUID</key>\n         <array>\n           <string>DCC660C6-F5A9-446D-B9FF-3C0258AB5BA0</string>\n         </array>\n       </dict>\n       </plist>'\n    end\n\n    # The below value is the result of parsing user_guid_xml with\n    # Plist.parse_xml\n    let(:user_guid_hash) do\n      { 'dsAttrTypeStandard:GeneratedUID' => ['DCC660C6-F5A9-446D-B9FF-3C0258AB5BA0'] }\n    end\n\n    it 'should return a hash containing a user\\'s dscl attribute data' do\n      expect(provider.class).to receive(:dscl).with('-plist', '.', 'read', user_path, 'GeneratedUID').and_return(user_guid_xml)\n      expect(provider.class.get_attribute_from_dscl('Users', username, 'GeneratedUID')).to eq(user_guid_hash)\n    end\n  end\n\n  describe '#convert_hash_to_binary' do\n    it 'should use plutil to successfully convert an xml plist to a binary plist' do\n      expect(Puppet::Util::Plist).to receive(:dump_plist).with('ruby_hash', :binary).and_return('binary_plist_data')\n      expect(provider.class.convert_hash_to_binary('ruby_hash')).to eq('binary_plist_data')\n    end\n  end\n\n  describe '#convert_binary_to_hash' do\n    it 'should accept a binary plist and return a ruby hash containing the plist data' do\n      expect(Puppet::Util::Plist).to receive(:parse_plist).with('binary_plist_data').and_return(user_plist_hash)\n      expect(provider.class.convert_binary_to_hash('binary_plist_data')).to eq(user_plist_hash)\n    end\n  end\n\n  describe '#next_system_id' do\n    it 'should return the next available UID number that is not in the list obtained from dscl and is greater than the passed integer value' do\n      expect(provider).to receive(:dscl).with('.', '-list', '/Users', 'uid').and_return(\"kathee 312\\ngary 11\\ntanny 33\\njohn 9\\nzach 5\")\n      expect(provider.next_system_id(30)).to eq(34)\n    end\n  end\n\n  describe '#get_salted_sha512' do\n    it \"should accept a hash whose 'SALTED-SHA512' key contains a base64 encoded salted-SHA512 password hash and return the hex value of that password hash\" do\n      expect(provider.class.get_salted_sha512(sha512_embedded_bplist_hash)).to eq(sha512_password_hash)\n    end\n  end\n\n  describe '#get_salted_sha512_pbkdf2' do\n    it \"should accept a hash containing a PBKDF2 password hash, salt, and iterations value and return the correct password hash\" do\n        expect(provider.class.get_salted_sha512_pbkdf2('entropy', pbkdf2_embedded_bplist_hash)).to eq(pbkdf2_password_hash)\n    end\n    it \"should accept a hash containing a PBKDF2 password hash, salt, and iterations value and return the correct salt value\" do\n        expect(provider.class.get_salted_sha512_pbkdf2('salt', pbkdf2_embedded_bplist_hash)).to eq(pbkdf2_salt_value)\n    end\n    it \"should accept a hash containing a PBKDF2 password hash, salt, and iterations value and return the correct iterations value\" do\n        expect(provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash)).to eq(pbkdf2_iterations_value)\n    end\n    it \"should return an Integer value when looking up the PBKDF2 iterations value\" do\n        expect(provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash)).to be_a(Integer)\n    end\n    it \"should raise an error if a field other than 'entropy', 'salt', or 'iterations' is passed\" do\n      expect { provider.class.get_salted_sha512_pbkdf2('othervalue', pbkdf2_embedded_bplist_hash, 'test_user') }.to raise_error(Puppet::Error, /Puppet has tried to read an incorrect value from the user test_user in the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'/)\n    end\n  end\n\n  describe '#get_sha1' do\n    let(:password_hash_file) { '/var/db/shadow/hash/user_guid' }\n    let(:stub_password_file) { double('connection') }\n\n    it 'should return a sha1 hash read from disk' do\n      expect(Puppet::FileSystem).to receive(:exist?).with(password_hash_file).and_return(true)\n      expect(File).to receive(:file?).with(password_hash_file).and_return(true)\n      expect(File).to receive(:readable?).with(password_hash_file).and_return(true)\n      expect(File).to receive(:new).with(password_hash_file).and_return(stub_password_file)\n      expect(stub_password_file).to receive(:read).and_return('sha1_password_hash')\n      expect(stub_password_file).to receive(:close)\n      expect(provider.class.get_sha1('user_guid')).to eq('sha1_password_hash')\n    end\n\n    it 'should return nil if the password_hash_file does not exist' do\n      expect(Puppet::FileSystem).to receive(:exist?).with(password_hash_file).and_return(false)\n      expect(provider.class.get_sha1('user_guid')).to eq(nil)\n    end\n\n    it 'should return nil if the password_hash_file is not a file' do\n      expect(Puppet::FileSystem).to receive(:exist?).with(password_hash_file).and_return(true)\n      expect(File).to receive(:file?).with(password_hash_file).and_return(false)\n      expect(provider.class.get_sha1('user_guid')).to eq(nil)\n    end\n\n    it 'should raise an error if the password_hash_file is not readable' do\n      expect(Puppet::FileSystem).to receive(:exist?).with(password_hash_file).and_return(true)\n      expect(File).to receive(:file?).with(password_hash_file).and_return(true)\n      expect(File).to receive(:readable?).with(password_hash_file).and_return(false)\n      expect { expect(provider.class.get_sha1('user_guid')).to eq(nil) }.to raise_error(Puppet::Error, /Could not read password hash file at #{password_hash_file}/)\n    end\n  end\n\n  describe '#write_password_to_users_plist' do\n    let(:sha512_plist_xml) do\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<!DOCTYPE plist PUBLIC \\\"-//Apple//DTD PLIST 1.0//EN\\\" \\\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\\">\\n<plist version=\\\"1.0\\\">\\n<dict>\\n\\t<key>KerberosKeys</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tMIIBS6EDAgEBoIIBQjCCAT4wcKErMCmgAwIBEqEiBCCS/0Im7BAps/YhX/ED\\n\\t\\tKOpDeSMFkUsu3UzEa6gqDu35BKJBMD+gAwIBA6E4BDZMS0RDOlNIQTEuNDM4\\n\\t\\tM0UxNTJEOUQzOTRBQTMyRDEzQUU5OEY2RjZFMUZFOEQwMEY4MWplZmYwYKEb\\n\\t\\tMBmgAwIBEaESBBAk8a3rrFk5mHAdEU5nRgFwokEwP6ADAgEDoTgENkxLREM6\\n\\t\\tU0hBMS40MzgzRTE1MkQ5RDM5NEFBMzJEMTNBRTk4RjZGNkUxRkU4RDAwRjgx\\n\\t\\tamVmZjBooSMwIaADAgEQoRoEGFg71irsV+9ddRNPSn9houo3Q6jZuj55XaJB\\n\\t\\tMD+gAwIBA6E4BDZMS0RDOlNIQTEuNDM4M0UxNTJEOUQzOTRBQTMyRDEzQUU5\\n\\t\\tOEY2RjZFMUZFOEQwMEY4MWplZmY=\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>ShadowHashData</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tYnBsaXN0MDDRAQJdU0FMVEVELVNIQTUxMk8QRFNL0iuruijP6becUWe43GTX\\n\\t\\t5WTgOTi2emx41DMnwnB4vbKieVOE4eNHiyocX5c0GX1LWJ6VlZqZ9EnDLsuA\\n\\t\\tNC5Ga9qlCAsZAAAAAAAAAQEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAGA=\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>authentication_authority</key>\\n\\t<array>\\n\\t\\t<string>;Kerberosv5;;jeff@LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81;LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81</string>\\n\\t\\t<string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512&gt;</string>\\n\\t</array>\\n\\t<key>dsAttrTypeStandard:ShadowHashData</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tYnBsaXN0MDDRAQJdU0FMVEVELVNIQTUxMk8QRH6n1ZITH1eyyPi9vOyNnfEh\\n\\t\\tKKOGOTpPAMdhm6wmIqRNRRQZ0R2lEtWRWrmOOXGKyUCD/i79a/cQpU1Hf4/3\\n\\t\\tNbElhxktCAsZAAAAAAAAAQEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAGA=\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>generateduid</key>\\n\\t<array>\\n\\t\\t<string>3AC74939-C14F-45DD-B6A9-D1A82373F0B0</string>\\n\\t</array>\\n\\t<key>name</key>\\n\\t<array>\\n\\t\\t<string>jeff</string>\\n\\t</array>\\n\\t<key>passwd</key>\\n\\t<array>\\n\\t\\t<string>********</string>\\n\\t</array>\\n\\t<key>passwordpolicyoptions</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU\\n\\t\\tWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO\\n\\t\\tIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w\\n\\t\\tLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp\\n\\t\\tbGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr\\n\\t\\tZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt\\n\\t\\tMDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8\\n\\t\\tL2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtl\\n\\t\\teT5wYXNzd29yZFRpbWVzdGFtcDwva2V5PgoJPGRhdGU+MjAxMi0wOC0xMVQw\\n\\t\\tMDozNTo1MFo8L2RhdGU+CjwvZGljdD4KPC9wbGlzdD4K\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>uid</key>\\n\\t<array>\\n\\t\\t<string>28</string>\\n\\t</array>\\n</dict>\\n</plist>\"\n    end\n\n    let(:pbkdf2_plist_xml) do\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<!DOCTYPE plist PUBLIC \\\"-//Apple//DTD PLIST 1.0//EN\\\" \\\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\\">\\n<plist version=\\\"1.0\\\">\\n<dict>\\n\\t<key>KerberosKeys</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tMIIBS6EDAgEBoIIBQjCCAT4wcKErMCmgAwIBEqEiBCDrboPy0gxu7oTZR/Pc\\n\\t\\tYdCBC9ivXo1k05gt036/aNe5VqJBMD+gAwIBA6E4BDZMS0RDOlNIQTEuNDEz\\n\\t\\tQTMwRjU5MEVFREM3ODdENTMyOTgxODUwQTk3NTI0NUIwQTcyM2plZmYwYKEb\\n\\t\\tMBmgAwIBEaESBBCm02SYYdsxo2fiDP4KuPtmokEwP6ADAgEDoTgENkxLREM6\\n\\t\\tU0hBMS40MTNBMzBGNTkwRUVEQzc4N0Q1MzI5ODE4NTBBOTc1MjQ1QjBBNzIz\\n\\t\\tamVmZjBooSMwIaADAgEQoRoEGHPBc7Dg7zjaE8g+YXObwupiBLMIlCrN5aJB\\n\\t\\tMD+gAwIBA6E4BDZMS0RDOlNIQTEuNDEzQTMwRjU5MEVFREM3ODdENTMyOTgx\\n\\t\\tODUwQTk3NTI0NUIwQTcyM2plZmY=\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>ShadowHashData</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tYnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhXZW50\\n\\t\\tcm9weVRzYWx0Wml0ZXJhdGlvbnNPEIAFkK3hnmlTwTWuhyrndhgjXffUbGPe\\n\\t\\tf5oPzfLNnn2F5LfKhoEBI1thWOBaMJgF7kgUsCekvpwj7CkmvIFyJpr/ulya\\n\\t\\tWYXoEJH6aJgHbSl/H6p1+mF1Ue8WcddSAFXEoNl7m5xYBaoyK67bzY7pxSOB\\n\\t\\tFlOsLqnpyNjxrFGaDytZXk8QIJN3xGkIocisLD5FwNRNqK0PzYXsXBTZpZ/8\\n\\t\\tQMnaMfDsEWCwCAsiKTE2QcTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA\\n\\t\\tAAAAAOo=\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>authentication_authority</key>\\n\\t<array>\\n\\t\\t<string>;Kerberosv5;;jeff@LKDC:SHA1.413A30F590EEDC787D532981850A975245B0A723;LKDC:SHA1.413A30F590EEDC787D532981850A975245B0A723</string>\\n\\t\\t<string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512-PBKDF2&gt;</string>\\n\\t</array>\\n\\t<key>generateduid</key>\\n\\t<array>\\n\\t\\t<string>1CB825D1-2DF7-43CC-B874-DB6BBB76C402</string>\\n\\t</array>\\n\\t<key>gid</key>\\n\\t<array>\\n\\t\\t<string>21</string>\\n\\t</array>\\n\\t<key>name</key>\\n\\t<array>\\n\\t\\t<string>jeff</string>\\n\\t</array>\\n\\t<key>passwd</key>\\n\\t<array>\\n\\t\\t<string>********</string>\\n\\t</array>\\n\\t<key>passwordpolicyoptions</key>\\n\\t<array>\\n\\t\\t<data>\\n\\t\\tPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU\\n\\t\\tWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO\\n\\t\\tIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w\\n\\t\\tLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp\\n\\t\\tbGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr\\n\\t\\tZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt\\n\\t\\tMDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8\\n\\t\\tL2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtl\\n\\t\\teT5wYXNzd29yZExhc3RTZXRUaW1lPC9rZXk+Cgk8ZGF0ZT4yMDEyLTA3LTI1\\n\\t\\tVDE4OjQ3OjU5WjwvZGF0ZT4KPC9kaWN0Pgo8L3BsaXN0Pgo=\\n\\t\\t</data>\\n\\t</array>\\n\\t<key>uid</key>\\n\\t<array>\\n\\t\\t<string>28</string>\\n\\t</array>\\n</dict>\\n</plist>\"\n    end\n\n    let(:sha512_shadowhashdata) do\n      {\n        'SALTED-SHA512' => 'blankvalue'\n      }\n    end\n\n    let(:pbkdf2_shadowhashdata) do\n      {\n        'SALTED-SHA512-PBKDF2' => {\n          'entropy'    => 'blank_entropy',\n          'salt'       => 'blank_salt',\n          'iterations' => 100\n        }\n      }\n    end\n\n    let(:sample_users_plist) do\n      {\n        \"shell\"                    => [\"/bin/zsh\"],\n        \"passwd\"                   => [\"********\"],\n        \"picture\"                  => [\"/Library/User Pictures/Animals/Eagle.tif\"],\n        \"_writers_LinkedIdentity\"  => [\"puppet\"], \"name\"=>[\"puppet\"],\n        \"home\"                     => [\"/Users/puppet\"],\n        \"_writers_UserCertificate\" => [\"puppet\"],\n        \"_writers_passwd\"          => [\"puppet\"],\n        \"gid\"                      => [\"20\"],\n        \"generateduid\"             => [\"DA8A0E67-E9BE-4B4F-B34E-8977BAE0D3D4\"],\n        \"realname\"                 => [\"Puppet\"],\n        \"_writers_picture\"         => [\"puppet\"],\n        \"uid\"                      => [\"501\"],\n        \"hint\"                     => [\"\"],\n        \"authentication_authority\" => [\";ShadowHash;HASHLIST:<SALTED-SHA512>\",\n          \";Kerberosv5;;puppet@LKDC:S HA1.35580B1D6366D2890A35D430373FF653297F377D;LKDC:SHA1.35580B1D6366D2890A35D430373FF653297F377D\"],\n        \"_writers_realname\"        => [\"puppet\"],\n        \"_writers_hint\"            => [\"puppet\"],\n        \"ShadowHashData\"           => ['blank']\n      }\n    end\n\n    before do\n      allow(provider).to receive(:merge_attribute_with_dscl).with('Users', username, 'AuthenticationAuthority', any_args)\n    end\n\n    describe 'when on macOS 11 (Big Sur) or greater' do\n      before do\n        allow(provider.class).to receive(:get_os_version).and_return('11.0.0')\n      end\n\n      it 'should add salted_sha512_pbkdf2 AuthenticationAuthority key if missing' do\n        expect(provider).to receive(:get_users_plist).and_return(sample_users_plist)\n        expect(provider).to receive(:get_shadow_hash_data).with(sample_users_plist).and_return(pbkdf2_shadowhashdata)\n        expect(provider).to receive(:set_salted_pbkdf2).with(sample_users_plist, pbkdf2_shadowhashdata, 'entropy', pbkdf2_password_hash)\n        expect(provider).to receive(:needs_sha512_pbkdf2_authentication_authority_to_be_added?).and_return(true)\n\n        expect(Puppet).to receive(:debug).with(\"Adding 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash to user 'nonexistent_user'\")\n        provider.write_password_to_users_plist(pbkdf2_password_hash)\n      end\n\n      it 'should not add salted_sha512_pbkdf2 AuthenticationAuthority key if not missing' do\n        expect(provider).to receive(:get_users_plist).and_return(sample_users_plist)\n        expect(provider).to receive(:get_shadow_hash_data).with(sample_users_plist).and_return(pbkdf2_shadowhashdata)\n        expect(provider).to receive(:set_salted_pbkdf2).with(sample_users_plist, pbkdf2_shadowhashdata, 'entropy', pbkdf2_password_hash)\n        expect(provider).to receive(:needs_sha512_pbkdf2_authentication_authority_to_be_added?).and_return(false)\n\n        expect(Puppet).not_to receive(:debug).with(\"Adding 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash to user 'nonexistent_user'\")\n        provider.write_password_to_users_plist(pbkdf2_password_hash)\n      end\n    end\n\n    describe 'when on macOS version lower than 11' do\n      before do\n        allow(provider.class).to receive(:get_os_version)\n        allow(provider).to receive(:needs_sha512_pbkdf2_authentication_authority_to_be_added?).and_return(false)\n      end\n\n      it 'should not add salted_sha512_pbkdf2 AuthenticationAuthority' do\n        expect(provider).to receive(:get_users_plist).and_return(sample_users_plist)\n        expect(provider).to receive(:get_shadow_hash_data).with(sample_users_plist).and_return(pbkdf2_shadowhashdata)\n        expect(provider).to receive(:set_salted_pbkdf2).with(sample_users_plist, pbkdf2_shadowhashdata, 'entropy', pbkdf2_password_hash)\n        expect(provider).to receive(:needs_sha512_pbkdf2_authentication_authority_to_be_added?).and_return(false)\n\n        expect(Puppet).not_to receive(:debug).with(\"Adding 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash to user 'nonexistent_user'\")\n        provider.write_password_to_users_plist(pbkdf2_password_hash)\n      end\n\n      it 'should call set_salted_sha512 on 10.7 when given a salted-SHA512 password hash' do\n        expect(provider).to receive(:get_users_plist).and_return(sample_users_plist)\n        expect(provider).to receive(:get_shadow_hash_data).with(sample_users_plist).and_return(sha512_shadowhashdata)\n        expect(provider.class).to receive(:get_os_version).and_return('10.7')\n        expect(provider).to receive(:set_salted_sha512).with(sample_users_plist, sha512_shadowhashdata, sha512_password_hash)\n        provider.write_password_to_users_plist(sha512_password_hash)\n      end\n\n      it 'should call set_salted_pbkdf2 on 10.8 when given a PBKDF2 password hash' do\n        expect(provider).to receive(:get_users_plist).and_return(sample_users_plist)\n        expect(provider).to receive(:get_shadow_hash_data).with(sample_users_plist).and_return(pbkdf2_shadowhashdata)\n        expect(provider.class).to receive(:get_os_version).and_return('10.8')\n        expect(provider).to receive(:set_salted_pbkdf2).with(sample_users_plist, pbkdf2_shadowhashdata, 'entropy', pbkdf2_password_hash)\n        provider.write_password_to_users_plist(pbkdf2_password_hash)\n      end\n\n      it \"should delete the SALTED-SHA512 key in the shadow_hash_data hash if it exists on a 10.8 system and write_password_to_users_plist has been called to set the user's password\" do\n        expect(provider).to receive(:get_users_plist).and_return('users_plist')\n        expect(provider).to receive(:get_shadow_hash_data).with('users_plist').and_return(sha512_shadowhashdata)\n        expect(provider.class).to receive(:get_os_version).and_return('10.8')\n        expect(provider).to receive(:set_salted_pbkdf2).with('users_plist', {}, 'entropy', pbkdf2_password_hash)\n        provider.write_password_to_users_plist(pbkdf2_password_hash)\n      end\n    end\n  end\n\n  describe '#set_salted_sha512' do\n    let(:users_plist) { {'ShadowHashData' => ['string_data'] } }\n    let(:sha512_shadow_hash_data) do\n      {\n        'SALTED-SHA512' => sha512_pw_string\n      }\n    end\n\n    it 'should set the SALTED-SHA512 password hash for a user in 10.7 and call the set_shadow_hash_data method to write the plist to disk' do\n      expect(provider.class).to receive(:convert_hash_to_binary).with(sha512_embedded_bplist_hash).and_return(sha512_embedded_bplist)\n      expect(provider).to receive(:set_shadow_hash_data).with(users_plist, sha512_embedded_bplist)\n      provider.set_salted_sha512(users_plist, sha512_embedded_bplist_hash, sha512_password_hash)\n    end\n\n    it 'should set the salted-SHA512 password, even if a blank shadow_hash_data hash is passed' do\n      expect(provider.class).to receive(:convert_hash_to_binary).with(sha512_shadow_hash_data).and_return(sha512_embedded_bplist)\n      expect(provider).to receive(:set_shadow_hash_data).with(users_plist, sha512_embedded_bplist)\n      provider.set_salted_sha512(users_plist, false, sha512_password_hash)\n    end\n  end\n\n  describe '#set_shadow_hash_data' do\n    let(:users_plist) { {'ShadowHashData' => ['string_data'] } }\n\n    it 'should flush the plist data to a temporary file' do\n      expect(provider).to receive(:write_and_import_shadow_hash_data)\n      provider.set_shadow_hash_data(users_plist, pbkdf2_embedded_plist)\n    end\n  end\n\n  describe '#set_salted_pbkdf2' do\n    let(:users_plist) { {'ShadowHashData' => ['string_data'] } }\n    let(:entropy_shadow_hash_data) do\n      {\n        'SALTED-SHA512-PBKDF2' =>\n        {\n          'entropy' => 'binary_string'\n        }\n      }\n    end\n\n    # This will also catch the edge-case where a 10.6-style user exists on\n    # a 10.8 system and Puppet attempts to set a password\n    it 'should not fail if shadow_hash_data is not a Hash' do\n      expect(Puppet::Util::Plist).to receive(:string_to_blob).with(provider.base64_decode_string(pbkdf2_password_hash)).and_return('binary_string')\n      expect(provider.class).to receive(:convert_hash_to_binary).with(entropy_shadow_hash_data).and_return('binary_plist')\n      expect(provider).to receive(:set_shadow_hash_data).with({'passwd' => '********'}, 'binary_plist')\n      provider.set_salted_pbkdf2({}, false, 'entropy', pbkdf2_password_hash)\n    end\n\n    it \"should set the PBKDF2 password hash when the 'entropy' field is passed with a valid password hash\" do\n      expect(Puppet::Util::Plist).to receive(:string_to_blob).with(provider.base64_decode_string(pbkdf2_password_hash))\n      expect(provider.class).to receive(:convert_hash_to_binary).with(pbkdf2_embedded_bplist_hash).and_return(pbkdf2_embedded_plist)\n      expect(provider).to receive(:set_shadow_hash_data).with(users_plist, pbkdf2_embedded_plist)\n      expect(users_plist).to receive(:[]=).with('passwd', '********')\n      provider.set_salted_pbkdf2(users_plist, pbkdf2_embedded_bplist_hash, 'entropy', pbkdf2_password_hash)\n    end\n\n    it \"should set the PBKDF2 password hash when the 'salt' field is passed with a valid password hash\" do\n      expect(Puppet::Util::Plist).to receive(:string_to_blob).with(provider.base64_decode_string(pbkdf2_salt_value))\n      expect(provider.class).to receive(:convert_hash_to_binary).with(pbkdf2_embedded_bplist_hash).and_return(pbkdf2_embedded_plist)\n      expect(provider).to receive(:set_shadow_hash_data).with(users_plist, pbkdf2_embedded_plist)\n      expect(users_plist).to receive(:[]=).with('passwd', '********')\n      provider.set_salted_pbkdf2(users_plist, pbkdf2_embedded_bplist_hash, 'salt', pbkdf2_salt_value)\n    end\n\n    it \"should set the PBKDF2 password hash when the 'iterations' field is passed with a valid password hash\" do\n      expect(provider.class).to receive(:convert_hash_to_binary).with(pbkdf2_embedded_bplist_hash).and_return(pbkdf2_embedded_plist)\n      expect(provider).to receive(:set_shadow_hash_data).with(users_plist, pbkdf2_embedded_plist)\n      expect(users_plist).to receive(:[]=).with('passwd', '********')\n      provider.set_salted_pbkdf2(users_plist, pbkdf2_embedded_bplist_hash, 'iterations', pbkdf2_iterations_value)\n    end\n  end\n\n  describe '#write_and_import_shadow_hash_data' do\n    it 'should save the passed plist to a temporary file and import it' do\n      tmpfile = double('tempfile', :path => \"/tmp/dsimport_#{username}\", :flush => nil)\n      allow(Tempfile).to receive(:create).and_yield(tmpfile)\n      allow(provider).to receive(:dscl).with('.', 'delete', user_path, 'ShadowHashData')\n\n      expect(tmpfile).to receive(:write).with(dsimport_contents)\n      expect(provider).to receive(:dsimport).with(tmpfile.path, '/Local/Default', 'M')\n      provider.write_and_import_shadow_hash_data(sha512_embedded_bplist)\n    end\n  end\n\n  describe '#merge_attribute_with_dscl' do\n    it 'should raise an error if a dscl command raises an error' do\n      expect(provider).to receive(:dscl).with('.', '-merge', user_path, 'GeneratedUID', 'GUID').and_raise(Puppet::ExecutionFailure, 'boom')\n      expect { provider.merge_attribute_with_dscl('Users', username, 'GeneratedUID', 'GUID') }.to raise_error Puppet::Error, /Could not set the dscl GeneratedUID key with value: GUID/\n    end\n  end\n\n  describe '#create_attribute_with_dscl' do\n    it 'should raise an error if a dscl command raises an error' do\n      expect(provider).to receive(:dscl).with('.', '-create', user_path, 'GeneratedUID', 'GUID').and_raise(Puppet::ExecutionFailure, 'boom')\n      expect { provider.create_attribute_with_dscl('Users', username, 'GeneratedUID', 'GUID') }.to raise_error Puppet::Error, /Could not set the dscl GeneratedUID key with value: GUID/\n    end\n  end\n\n  describe '#get_users_plist' do\n    let(:test_hash) do\n      {\n        'user'  => 'puppet',\n        'shell' => '/bin/bash'\n      }\n    end\n\n    it 'should convert a plist to a valid Ruby hash' do\n      expect(Puppet::Util::Plist).to receive(:read_plist_file).with(\"#{users_plist_dir}/#{username}.plist\").and_return(test_hash)\n      expect(provider.get_users_plist(username)).to eq(test_hash, )\n    end\n  end\n\n  describe '#get_shadow_hash_data' do\n    let(:shadow_hash) do\n      {\n        'ShadowHashData' => ['test']\n      }\n    end\n\n    let(:no_shadow_hash) do\n      {\n        'no' => 'Shadow Hash Data'\n      }\n    end\n\n    it 'should return false if the passed users_plist does NOT have a ShadowHashData key' do\n      expect(provider.get_shadow_hash_data(no_shadow_hash)).to eq(false)\n    end\n\n    it 'should call convert_binary_to_hash() with the string ' +\n       'located in the first element of the array of the ShadowHashData key if the ' +\n       'passed users_plist contains a ShadowHashData key' do\n      expect(provider.class).to receive(:convert_binary_to_hash).with('test').and_return('returnvalue')\n      expect(provider.get_shadow_hash_data(shadow_hash)).to eq('returnvalue')\n    end\n  end\n\n  describe 'self#get_os_version' do\n    before :each do\n      # Ensure we don't have a value cached from another spec\n      provider.class.instance_variable_set(:@os_version, nil) if provider.class.instance_variable_defined? :@os_version\n    end\n\n    it 'should call Puppet.runtime[:facter].value(os.macosx.version.major) ONLY ONCE no matter how ' +\n       'many times get_os_version() is called' do\n      expect(Facter).to receive(:value).with('os.macosx.version.major').once.and_return('10.8')\n      expect(provider.class.get_os_version).to eq('10.8')\n      expect(provider.class.get_os_version).to eq('10.8')\n      expect(provider.class.get_os_version).to eq('10.8')\n      expect(provider.class.get_os_version).to eq('10.8')\n    end\n  end\n\n  describe '#base64_decode_string' do\n    it 'should return a Base64-decoded string appropriate for use in a user\\'s plist' do\n      expect(provider.base64_decode_string(sha512_password_hash)).to eq(sha512_pw_string)\n    end\n  end\n\n  describe '(#12833) 10.6-style users on 10.8' do\n    # The below represents output of 'dscl -plist . readall /Users'\n    # converted to a Ruby hash if only one user were installed on the system.\n    # This lets us check the behavior of all the methods necessary to return\n    # a user's groups property by controlling the data provided by dscl. The\n    # differentiating aspect about this plist is that it's from a 10.6-style\n    # user. There's an edge case whereby a user that was created in 10.6, but\n    # who hasn't attempted to login to the system until after it's been\n    # upgraded to 10.8, will experience errors due to assumptions in Puppet\n    # based solely on operatingsystem.\n    let(:all_users_hash) do\n      [\n        {\n          \"dsAttrTypeNative:_writers_UserCertificate\"  => [\"testuser\"],\n          \"dsAttrTypeStandard:RealName\"                => [\"testuser\"],\n          \"dsAttrTypeStandard:NFSHomeDirectory\"        => [\"/Users/testuser\"],\n          \"dsAttrTypeNative:_writers_realname\"         => [\"testuser\"],\n          \"dsAttrTypeNative:_writers_picture\"          => [\"testuser\"],\n          \"dsAttrTypeStandard:AppleMetaNodeLocation\"   => [\"/Local/Default\"],\n          \"dsAttrTypeStandard:PrimaryGroupID\"          => [\"20\"],\n          \"dsAttrTypeNative:_writers_LinkedIdentity\"   => [\"testuser\"],\n          \"dsAttrTypeStandard:UserShell\"               => [\"/bin/bash\"],\n          \"dsAttrTypeStandard:UniqueID\"                => [\"1234\"],\n          \"dsAttrTypeStandard:RecordName\"              => [\"testuser\"],\n          \"dsAttrTypeStandard:Password\"                => [\"********\"],\n          \"dsAttrTypeNative:_writers_jpegphoto\"        => [\"testuser\"],\n          \"dsAttrTypeNative:_writers_hint\"             => [\"testuser\"],\n          \"dsAttrTypeNative:_writers_passwd\"           => [\"testuser\"],\n          \"dsAttrTypeStandard:RecordType\"              => [\"dsRecTypeStandard:Users\"],\n          \"dsAttrTypeStandard:AuthenticationAuthority\" => [\n            \";ShadowHash;\",\n            \";Kerberosv5;;testuser@LKDC:SHA1.48AC4BCFEFE9 D66847B5E7D813BC4B12C5513A07;LKDC:SHA1.48AC4BCFEFE9D66847B5E7D813BC4B12C5513A07;\"\n                                                          ],\n          \"dsAttrTypeStandard:GeneratedUID\"            => [\"D1AC2ECC-F177-4B45-8B18-59CF002F97FF\"]\n        }\n      ]\n    end\n\n    let(:username) { 'testuser' }\n    let(:user_path) { \"/Users/#{username}\" }\n    let(:resource) do\n      Puppet::Type.type(:user).new(\n        :name     => username,\n        :provider => :directoryservice\n      )\n    end\n    let(:provider) { resource.provider }\n\n    # The below represents the result of get_users_plist on the testuser\n    # account from the 'all_users_hash' helper method. The get_users_plist\n    # method calls the `plutil` binary to do its work, so we want to stub\n    # that out\n    let(:user_plist_hash) do\n      {\n        'realname'                 => ['testuser'],\n        'authentication_authority' => [';ShadowHash;', ';Kerberosv5;;testuser@LKDC:SHA1.48AC4BCFEFE9D66847B5E7D813BC4B12C5513A07;LKDC:SHA1.48AC4BCFEFE9D66847B5E7D813BC4B12C5513A07;'],\n        'home'                     => ['/Users/testuser'],\n        '_writers_realname'        => ['testuser'],\n        'passwd'                   => '********',\n        '_writers_LinkedIdentity'  => ['testuser'],\n        '_writers_picture'         => ['testuser'],\n        'gid'                      => ['20'],\n        '_writers_passwd'          => ['testuser'],\n        '_writers_hint'            => ['testuser'],\n        '_writers_UserCertificate' => ['testuser'],\n        '_writers_jpegphoto'       => ['testuser'],\n        'shell'                    => ['/bin/bash'],\n        'uid'                      => ['1234'],\n        'generateduid'             => ['D1AC2ECC-F177-4B45-8B18-59CF002F97FF'],\n        'name'                     => ['testuser']\n      }\n    end\n\n    before :each do\n      allow(provider.class).to receive(:get_all_users).and_return(all_users_hash)\n      allow(provider.class).to receive(:get_list_of_groups).and_return(group_plist_hash_guid)\n      allow(provider).to receive(:merge_attribute_with_dscl).with('Users', username, 'AuthenticationAuthority', any_args)\n      provider.class.prefetch({})\n    end\n\n    it 'should not raise an error if the password=() method is called on ' +\n       'a user without a ShadowHashData key in their user\\'s plist on OS X ' +\n       'version 10.8' do\n      resource[:salt] = pbkdf2_salt_value\n      resource[:iterations] = pbkdf2_iterations_value\n      resource[:password] = pbkdf2_password_hash\n      allow(provider.class).to receive(:get_os_version).and_return('10.8')\n      allow(provider).to receive(:sleep)\n      allow(provider).to receive(:flush_dscl_cache)\n\n      expect(provider).to receive(:get_users_plist).with('testuser').and_return(user_plist_hash)\n      expect(provider).to receive(:set_salted_pbkdf2).with(user_plist_hash, false, 'entropy', pbkdf2_password_hash)\n      provider.password = resource[:password]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/hpux_spec.rb",
    "content": "require 'spec_helper'\nrequire 'etc'\n\ndescribe Puppet::Type.type(:user).provider(:hpuxuseradd),\n         unless: Puppet::Util::Platform.windows? do\n  let :resource do\n    Puppet::Type.type(:user).new(\n      :title => 'testuser',\n      :comment => 'Test J. User',\n      :provider => :hpuxuseradd\n    )\n  end\n  let(:provider) { resource.provider }\n\n  it \"should add -F when modifying a user\" do\n    allow(resource).to receive(:allowdupe?).and_return(true)\n    allow(provider).to receive(:trusted).and_return(true)\n    expect(provider).to receive(:execute).with(include(\"-F\"), anything)\n    provider.uid = 1000\n  end\n\n  it \"should add -F when deleting a user\" do\n    allow(provider).to receive(:exists?).and_return(true)\n    expect(provider).to receive(:execute).with(include(\"-F\"), anything)\n    provider.delete\n  end\n\n  context \"managing passwords\" do\n    let :pwent do\n      Etc::Passwd.new(\"testuser\", \"foopassword\")\n    end\n\n    before :each do\n      allow(Etc).to receive(:getpwent).and_return(pwent)\n      allow(Etc).to receive(:getpwnam).and_return(pwent)\n      allow(provider).to receive(:command).with(:modify).and_return('/usr/sam/lbin/usermod.sam')\n    end\n\n    it \"should have feature manages_passwords\" do\n      expect(described_class).to be_manages_passwords\n    end\n\n    it \"should return nil if user does not exist\" do\n      allow(Etc).to receive(:getpwent).and_return(nil)\n      expect(provider.password).to be_nil\n    end\n\n    it \"should return password entry if exists\" do\n      expect(provider.password).to eq(\"foopassword\")\n    end\n  end\n\n  context \"check for trusted computing\" do\n    before :each do\n      allow(provider).to receive(:command).with(:modify).and_return('/usr/sam/lbin/usermod.sam')\n    end\n\n    it \"should add modprpw to modifycmd if Trusted System\" do\n      allow(resource).to receive(:allowdupe?).and_return(true)\n      expect(provider).to receive(:exec_getprpw).with('root','-m uid').and_return('uid=0')\n      expect(provider).to receive(:execute).with(['/usr/sam/lbin/usermod.sam', '-F', '-u', 1000, '-o', 'testuser', ';', '/usr/lbin/modprpw', '-v', '-l', 'testuser'], hash_including(custom_environment: {}))\n      provider.uid = 1000\n    end\n\n    it \"should not add modprpw if not Trusted System\" do\n      allow(resource).to receive(:allowdupe?).and_return(true)\n      expect(provider).to receive(:exec_getprpw).with('root','-m uid').and_return('System is not trusted')\n      expect(provider).to receive(:execute).with(['/usr/sam/lbin/usermod.sam', '-F', '-u', 1000, '-o', 'testuser'], hash_including(custom_environment: {}))\n      provider.uid = 1000\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/ldap_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:user).provider(:ldap) do\n  it \"should have the Ldap provider class as its baseclass\" do\n    expect(described_class.superclass).to equal(Puppet::Provider::Ldap)\n  end\n\n  it \"should manage :posixAccount and :person objectclasses\" do\n    expect(described_class.manager.objectclasses).to eq([:posixAccount, :person])\n  end\n\n  it \"should use 'ou=People' as its relative base\" do\n    expect(described_class.manager.location).to eq(\"ou=People\")\n  end\n\n  it \"should use :uid as its rdn\" do\n    expect(described_class.manager.rdn).to eq(:uid)\n  end\n\n  it \"should be able to manage passwords\" do\n    expect(described_class).to be_manages_passwords\n  end\n\n  {:name => \"uid\",\n    :password => \"userPassword\",\n    :comment => \"cn\",\n    :uid => \"uidNumber\",\n    :gid => \"gidNumber\",\n    :home => \"homeDirectory\",\n    :shell => \"loginShell\"\n  }.each do |puppet, ldap|\n    it \"should map :#{puppet.to_s} to '#{ldap}'\" do\n      expect(described_class.manager.ldap_name(puppet)).to eq(ldap)\n    end\n  end\n\n  context \"when being created\" do\n    before do\n      # So we don't try to actually talk to ldap\n      @connection = double('connection')\n      allow(described_class.manager).to receive(:connect).and_yield(@connection)\n    end\n\n    it \"should generate the sn as the last field of the cn\" do\n      expect(Puppet::Type.type(:group).provider(:ldap)).to receive(:name2id).with([\"whatever\"]).and_return([123])\n\n      resource = double('resource', :should => %w{whatever})\n      allow(resource).to receive(:should).with(:comment).and_return([\"Luke Kanies\"])\n      allow(resource).to receive(:should).with(:ensure).and_return(:present)\n      instance = described_class.new(:name => \"luke\", :ensure => :absent)\n\n      allow(instance).to receive(:resource).and_return(resource)\n\n      expect(@connection).to receive(:add).with(anything, hash_including(\"sn\" => [\"Kanies\"]))\n\n      instance.create\n      instance.flush\n    end\n\n    it \"should translate a group name to the numeric id\" do\n      expect(Puppet::Type.type(:group).provider(:ldap)).to receive(:name2id).with(\"bar\").and_return(101)\n\n      resource = double('resource', :should => %w{whatever})\n      allow(resource).to receive(:should).with(:gid).and_return('bar')\n      allow(resource).to receive(:should).with(:ensure).and_return(:present)\n      instance = described_class.new(:name => \"luke\", :ensure => :absent)\n      allow(instance).to receive(:resource).and_return(resource)\n\n      expect(@connection).to receive(:add).with(anything, hash_including(\"gidNumber\" => [\"101\"]))\n\n      instance.create\n      instance.flush\n    end\n\n    context \"with no uid specified\" do\n      it \"should pick the first available UID after the largest existing UID\" do\n        expect(Puppet::Type.type(:group).provider(:ldap)).to receive(:name2id).with([\"whatever\"]).and_return([123])\n\n        low = {:name=>[\"luke\"], :shell=>:absent, :uid=>[\"600\"], :home=>[\"/h\"], :gid=>[\"1000\"], :password=>[\"blah\"], :comment=>[\"l k\"]}\n        high = {:name=>[\"testing\"], :shell=>:absent, :uid=>[\"640\"], :home=>[\"/h\"], :gid=>[\"1000\"], :password=>[\"blah\"], :comment=>[\"t u\"]}\n        expect(described_class.manager).to receive(:search).and_return([low, high])\n\n        resource = double('resource', :should => %w{whatever})\n        allow(resource).to receive(:should).with(:uid).and_return(nil)\n        allow(resource).to receive(:should).with(:ensure).and_return(:present)\n        instance = described_class.new(:name => \"luke\", :ensure => :absent)\n        allow(instance).to receive(:resource).and_return(resource)\n\n        expect(@connection).to receive(:add).with(anything, hash_including(\"uidNumber\" => [\"641\"]))\n\n        instance.create\n        instance.flush\n      end\n\n      it \"should pick 501 of no users exist\" do\n        expect(Puppet::Type.type(:group).provider(:ldap)).to receive(:name2id).with([\"whatever\"]).and_return([123])\n\n        expect(described_class.manager).to receive(:search).and_return(nil)\n\n        resource = double('resource', :should => %w{whatever})\n        allow(resource).to receive(:should).with(:uid).and_return(nil)\n        allow(resource).to receive(:should).with(:ensure).and_return(:present)\n        instance = described_class.new(:name => \"luke\", :ensure => :absent)\n        allow(instance).to receive(:resource).and_return(resource)\n\n        expect(@connection).to receive(:add).with(anything, hash_including(\"uidNumber\" => [\"501\"]))\n\n        instance.create\n        instance.flush\n      end\n    end\n  end\n\n  context \"when flushing\" do\n    before do\n      allow(described_class).to receive(:suitable?).and_return(true)\n\n      @instance = described_class.new(:name => \"myname\", :groups => %w{whatever}, :uid => \"400\")\n    end\n\n    it \"should remove the :groups value before updating\" do\n      expect(@instance.class.manager).to receive(:update).with(anything, anything, hash_excluding(:groups))\n\n      @instance.flush\n    end\n\n    it \"should empty the property hash\" do\n      allow(@instance.class.manager).to receive(:update)\n\n      @instance.flush\n\n      expect(@instance.uid).to eq(:absent)\n    end\n\n    it \"should empty the ldap property hash\" do\n      allow(@instance.class.manager).to receive(:update)\n\n      @instance.flush\n\n      expect(@instance.ldap_properties[:uid]).to be_nil\n    end\n  end\n\n  context \"when checking group membership\" do\n    before do\n      @groups = Puppet::Type.type(:group).provider(:ldap)\n      @group_manager = @groups.manager\n      allow(described_class).to receive(:suitable?).and_return(true)\n\n      @instance = described_class.new(:name => \"myname\")\n    end\n\n    it \"should show its group membership as the sorted list of all groups returned by an ldap query of group memberships\" do\n      one = {:name => \"one\"}\n      two = {:name => \"two\"}\n      expect(@group_manager).to receive(:search).with(\"memberUid=myname\").and_return([two, one])\n\n      expect(@instance.groups).to eq(\"one,two\")\n    end\n\n    it \"should show its group membership as :absent if no matching groups are found in ldap\" do\n      expect(@group_manager).to receive(:search).with(\"memberUid=myname\").and_return(nil)\n\n      expect(@instance.groups).to eq(:absent)\n    end\n\n    it \"should cache the group value\" do\n      expect(@group_manager).to receive(:search).with(\"memberUid=myname\").once.and_return(nil)\n\n      @instance.groups\n      expect(@instance.groups).to eq(:absent)\n    end\n  end\n\n  context \"when modifying group membership\" do\n    before do\n      @groups = Puppet::Type.type(:group).provider(:ldap)\n      @group_manager = @groups.manager\n      allow(described_class).to receive(:suitable?).and_return(true)\n\n      @one = {:name => \"one\", :gid => \"500\"}\n      allow(@group_manager).to receive(:find).with(\"one\").and_return(@one)\n\n      @two = {:name => \"one\", :gid => \"600\"}\n      allow(@group_manager).to receive(:find).with(\"two\").and_return(@two)\n\n      @instance = described_class.new(:name => \"myname\")\n\n      allow(@instance).to receive(:groups).and_return(:absent)\n    end\n\n    it \"should fail if the group does not exist\" do\n      expect(@group_manager).to receive(:find).with(\"mygroup\").and_return(nil)\n\n      expect { @instance.groups = \"mygroup\" }.to raise_error(Puppet::Error)\n    end\n\n    it \"should only pass the attributes it cares about to the group manager\" do\n      expect(@group_manager).to receive(:update).with(anything, hash_excluding(:gid), anything)\n\n      @instance.groups = \"one\"\n    end\n\n    it \"should always include :ensure => :present in the current values\" do\n      expect(@group_manager).to receive(:update).with(anything, hash_including(ensure: :present), anything)\n\n      @instance.groups = \"one\"\n    end\n\n    it \"should always include :ensure => :present in the desired values\" do\n      expect(@group_manager).to receive(:update).with(anything, anything, hash_including(ensure: :present))\n\n      @instance.groups = \"one\"\n    end\n\n    it \"should always pass the group's original member list\" do\n      @one[:members] = %w{yay ness}\n      expect(@group_manager).to receive(:update).with(anything, hash_including(members: %w{yay ness}), anything)\n\n      @instance.groups = \"one\"\n    end\n\n    it \"should find the group again when resetting its member list, so it has the full member list\" do\n      expect(@group_manager).to receive(:find).with(\"one\").and_return(@one)\n\n      allow(@group_manager).to receive(:update)\n\n      @instance.groups = \"one\"\n    end\n\n    context \"for groups that have no members\" do\n      it \"should create a new members attribute with its value being the user's name\" do\n        expect(@group_manager).to receive(:update).with(anything, anything, hash_including(members: %w{myname}))\n\n        @instance.groups = \"one\"\n      end\n    end\n\n    context \"for groups it is being removed from\" do\n      it \"should replace the group's member list with one missing the user's name\" do\n        @one[:members] = %w{myname a}\n        @two[:members] = %w{myname b}\n\n        expect(@group_manager).to receive(:update).with(\"two\", anything, hash_including(members: %w{b}))\n\n        allow(@instance).to receive(:groups).and_return(\"one,two\")\n        @instance.groups = \"one\"\n      end\n\n      it \"should mark the member list as empty if there are no remaining members\" do\n        @one[:members] = %w{myname}\n        @two[:members] = %w{myname b}\n\n        expect(@group_manager).to receive(:update).with(\"one\", anything, hash_including(members: :absent))\n\n        allow(@instance).to receive(:groups).and_return(\"one,two\")\n        @instance.groups = \"two\"\n      end\n    end\n\n    context \"for groups that already have members\" do\n      it \"should replace each group's member list with a new list including the user's name\" do\n        @one[:members] = %w{a b}\n        expect(@group_manager).to receive(:update).with(anything, anything, hash_including(members: %w{a b myname}))\n        @two[:members] = %w{b c}\n        expect(@group_manager).to receive(:update).with(anything, anything, hash_including(members: %w{b c myname}))\n\n        @instance.groups = \"one,two\"\n      end\n    end\n\n    context \"for groups of which it is a member\" do\n      it \"should do nothing\" do\n        @one[:members] = %w{a b}\n        expect(@group_manager).to receive(:update).with(anything, anything, hash_including(members: %w{a b myname}))\n\n        @two[:members] = %w{c myname}\n        expect(@group_manager).not_to receive(:update).with(\"two\", any_args)\n\n        allow(@instance).to receive(:groups).and_return(\"two\")\n\n        @instance.groups = \"one,two\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/openbsd_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:user).provider(:openbsd) do\n  before :each do\n    allow(described_class).to receive(:command).with(:password).and_return('/usr/sbin/passwd')\n    allow(described_class).to receive(:command).with(:add).and_return('/usr/sbin/useradd')\n    allow(described_class).to receive(:command).with(:modify).and_return('/usr/sbin/usermod')\n    allow(described_class).to receive(:command).with(:delete).and_return('/usr/sbin/userdel')\n  end\n\n  let(:resource) do\n    Puppet::Type.type(:user).new(\n      :name       => 'myuser',\n      :managehome => :false,\n      :system     => :false,\n      :loginclass => 'staff',\n      :provider   => provider\n    )\n  end\n\n  let(:provider) { described_class.new(:name => 'myuser') }\n\n  let(:shadow_entry) {\n    return unless Puppet.features.libshadow?\n    entry = Etc::PasswdEntry.new\n    entry[:sp_namp]   = 'myuser' # login name\n    entry[:sp_loginclass] = 'staff' # login class\n    entry\n  }\n\n  describe \"#expiry=\" do\n    it \"should pass expiry to usermod as MM/DD/YY\" do\n      resource[:expiry] = '2014-11-05'\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-e', 'November 05 2014', 'myuser'], hash_including(custom_environment: {}))\n      provider.expiry = '2014-11-05'\n    end\n\n    it \"should use -e with an empty string when the expiry property is removed\" do\n      resource[:expiry] = :absent\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-e', '', 'myuser'], hash_including(custom_environment: {}))\n      provider.expiry = :absent\n    end\n  end\n\n  describe \"#addcmd\" do\n    it \"should return an array with the full command and expiry as MM/DD/YY\" do\n      allow(Facter).to receive(:value).with('os.family').and_return('OpenBSD')\n      allow(Facter).to receive(:value).with('os.release.major')\n      resource[:expiry] = \"1997-06-01\"\n      expect(provider.addcmd).to eq(['/usr/sbin/useradd', '-e', 'June 01 1997', 'myuser'])\n    end\n  end\n\n  describe \"#loginclass\" do\n    before :each do\n      resource\n    end\n\n    it \"should return the loginclass if set\", :if => Puppet.features.libshadow? do\n      expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(shadow_entry)\n      provider.send(:loginclass).should == 'staff'\n    end\n\n    it \"should return the empty string when loginclass isn't set\", :if => Puppet.features.libshadow? do\n      shadow_entry[:sp_loginclass] = ''\n      expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(shadow_entry)\n      provider.send(:loginclass).should == ''\n    end\n\n    it \"should return nil when loginclass isn't available\", :if => Puppet.features.libshadow? do\n      shadow_entry[:sp_loginclass] = nil\n      expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(shadow_entry)\n      provider.send(:loginclass).should be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/pw_spec.rb",
    "content": "require 'spec_helper'\nrequire 'open3'\n\nRSpec::Matchers.define_negated_matcher :excluding, :include\n\ndescribe Puppet::Type.type(:user).provider(:pw) do\n  let :resource do\n    Puppet::Type.type(:user).new(:name => \"testuser\", :provider => :pw)\n  end\n\n  context \"when creating users\" do\n    let :provider do\n      prov = resource.provider\n      expect(prov).to receive(:exists?).and_return(nil)\n      prov\n    end\n\n    it \"should run pw with no additional flags when no properties are given\" do\n      expect(provider.addcmd).to eq([described_class.command(:pw), \"useradd\", \"testuser\"])\n      expect(provider).to receive(:execute).with([described_class.command(:pw), \"useradd\", \"testuser\"], kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -o when allowdupe is enabled\" do\n      resource[:allowdupe] = true\n      expect(provider).to receive(:execute).with(include(\"-o\"), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -c with the correct argument when the comment property is set\" do\n      resource[:comment] = \"Testuser Name\"\n      expect(provider).to receive(:execute).with(include(\"-c\").and(include(\"Testuser Name\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -e with the correct argument when the expiry property is set\" do\n      resource[:expiry] = \"2010-02-19\"\n      expect(provider).to receive(:execute).with(include(\"-e\").and(include(\"19-02-2010\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -e 00-00-0000 if the expiry property has to be removed\" do\n      resource[:expiry] = :absent\n      expect(provider).to receive(:execute).with(include(\"-e\").and(include(\"00-00-0000\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -g with the correct argument when the gid property is set\" do\n      resource[:gid] = 12345\n      expect(provider).to receive(:execute).with(include(\"-g\").and(include(12345)), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -G with the correct argument when the groups property is set\" do\n      resource[:groups] = \"group1\"\n      allow(Puppet::Util::POSIX).to receive(:groups_of).with('testuser').and_return([])\n      expect(provider).to receive(:execute).with(include(\"-G\").and(include(\"group1\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -G with all the given groups when the groups property is set to an array\" do\n      resource[:groups] = [\"group1\", \"group2\"]\n      allow(Puppet::Util::POSIX).to receive(:groups_of).with('testuser').and_return([])\n      expect(provider).to receive(:execute).with(include(\"-G\").and(include(\"group1,group2\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -d with the correct argument when the home property is set\" do\n      resource[:home] = \"/home/testuser\"\n      expect(provider).to receive(:execute).with(include(\"-d\").and(include(\"/home/testuser\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -m when the managehome property is enabled\" do\n      resource[:managehome] = true\n      expect(provider).to receive(:execute).with(include(\"-m\"), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should call the password set function with the correct argument when the password property is set\" do\n      resource[:password] = \"*\"\n      expect(provider).to receive(:execute)\n      expect(provider).to receive(:password=).with(\"*\")\n      provider.create\n    end\n\n    it \"should call execute with sensitive true when the password property is set\" do\n      Puppet::Util::Log.level = :debug\n      resource[:password] = \"abc123\"\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: true))\n      popen = double(\"popen\", :puts => nil, :close => nil)\n      expect(Open3).to receive(:popen3).and_return(popen)\n      expect(popen).to receive(:puts).with(\"abc123\")\n      provider.create\n      expect(@logs).not_to be_any {|log| log.level == :debug and log.message =~ /abc123/}\n    end\n\n    it \"should call execute with sensitive false when a non-sensitive property is set\" do\n      resource[:managehome] = true\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: false))\n      provider.create\n    end\n\n    it \"should use -s with the correct argument when the shell property is set\" do\n      resource[:shell] = \"/bin/sh\"\n      expect(provider).to receive(:execute).with(include(\"-s\").and(include(\"/bin/sh\")), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should use -u with the correct argument when the uid property is set\" do\n      resource[:uid] = 12345\n      expect(provider).to receive(:execute).with(include(\"-u\").and(include(12345)), kind_of(Hash))\n      provider.create\n    end\n\n    # (#7500) -p should not be used to set a password (it means something else)\n    it \"should not use -p when a password is given\" do\n      resource[:password] = \"*\"\n      expect(provider.addcmd).not_to include(\"-p\")\n      expect(provider).to receive(:password=)\n      expect(provider).to receive(:execute).with(excluding(\"-p\"), kind_of(Hash))\n      provider.create\n    end\n  end\n\n  context \"when deleting users\" do\n    it \"should run pw with no additional flags\" do\n      provider = resource.provider\n      expect(provider).to receive(:exists?).and_return(true)\n      expect(provider.deletecmd).to eq([described_class.command(:pw), \"userdel\", \"testuser\"])\n      expect(provider).to receive(:execute).with([described_class.command(:pw), \"userdel\", \"testuser\"], hash_including(custom_environment: {}))\n      provider.delete\n    end\n\n    # The above test covers this, but given the consequences of\n    # accidentally deleting a user's home directory it seems better to\n    # have an explicit test.\n    it \"should not use -r when managehome is not set\" do\n      provider = resource.provider\n      expect(provider).to receive(:exists?).and_return(true)\n      resource[:managehome] = false\n      expect(provider).to receive(:execute).with(excluding(\"-r\"), hash_including(custom_environment: {}))\n      provider.delete\n    end\n\n    it \"should use -r when managehome is set\" do\n      provider = resource.provider\n      expect(provider).to receive(:exists?).and_return(true)\n      resource[:managehome] = true\n      expect(provider).to receive(:execute).with(include(\"-r\"), hash_including(custom_environment: {}))\n      provider.delete\n    end\n  end\n\n  context \"when modifying users\" do\n    let :provider do\n      resource.provider\n    end\n\n    it \"should run pw with the correct arguments\" do\n      expect(provider.modifycmd(\"uid\", 12345)).to eq([described_class.command(:pw), \"usermod\", \"testuser\", \"-u\", 12345])\n      expect(provider).to receive(:execute).with([described_class.command(:pw), \"usermod\", \"testuser\", \"-u\", 12345], hash_including(custom_environment: {}))\n      provider.uid = 12345\n    end\n\n    it \"should use -c with the correct argument when the comment property is changed\" do\n      resource[:comment] = \"Testuser Name\"\n      expect(provider).to receive(:execute).with(include(\"-c\").and(include(\"Testuser New Name\")), hash_including(custom_environment: {}))\n      provider.comment = \"Testuser New Name\"\n    end\n\n    it \"should use -e with the correct argument when the expiry property is changed\" do\n      resource[:expiry] = \"2010-02-19\"\n      expect(provider).to receive(:execute).with(include(\"-e\").and(include(\"19-02-2011\")), hash_including(custom_environment: {}))\n      provider.expiry = \"2011-02-19\"\n    end\n\n    it \"should use -e with the correct argument when the expiry property is removed\" do\n      resource[:expiry] = :absent\n      expect(provider).to receive(:execute).with(include(\"-e\").and(include(\"00-00-0000\")), hash_including(custom_environment: {}))\n      provider.expiry = :absent\n    end\n\n    it \"should use -g with the correct argument when the gid property is changed\" do\n      resource[:gid] = 12345\n      expect(provider).to receive(:execute).with(include(\"-g\").and(include(54321)), hash_including(custom_environment: {}))\n      provider.gid = 54321\n    end\n\n    it \"should use -G with the correct argument when the groups property is changed\" do\n      resource[:groups] = \"group1\"\n      expect(provider).to receive(:execute).with(include(\"-G\").and(include(\"group2\")), hash_including(custom_environment: {}))\n      provider.groups = \"group2\"\n    end\n\n    it \"should use -G with all the given groups when the groups property is changed with an array\" do\n      resource[:groups] = [\"group1\", \"group2\"]\n      expect(provider).to receive(:execute).with(include(\"-G\").and(include(\"group3,group4\")), hash_including(custom_environment: {}))\n      provider.groups = \"group3,group4\"\n    end\n\n    it \"should use -d with the correct argument when the home property is changed\" do\n      resource[:home] = \"/home/testuser\"\n      expect(provider).to receive(:execute).with(include(\"-d\").and(include(\"/newhome/testuser\")), hash_including(custom_environment: {}))\n      provider.home = \"/newhome/testuser\"\n    end\n\n    it \"should use -m and -d with the correct argument when the home property is changed and managehome is enabled\" do\n      resource[:home] = \"/home/testuser\"\n      resource[:managehome] = true\n      expect(provider).to receive(:execute).with(include(\"-d\").and(include(\"/newhome/testuser\")).and(include(\"-m\")), hash_including(custom_environment: {}))\n      provider.home = \"/newhome/testuser\"\n    end\n\n    it \"should call the password set function with the correct argument when the password property is changed\" do\n      resource[:password] = \"*\"\n      expect(provider).to receive(:password=).with(\"!\")\n      provider.password = \"!\"\n    end\n\n    it \"should use -s with the correct argument when the shell property is changed\" do\n      resource[:shell] = \"/bin/sh\"\n      expect(provider).to receive(:execute).with(include(\"-s\").and(include(\"/bin/tcsh\")), hash_including(custom_environment: {}))\n      provider.shell = \"/bin/tcsh\"\n    end\n\n    it \"should use -u with the correct argument when the uid property is changed\" do\n      resource[:uid] = 12345\n      expect(provider).to receive(:execute).with(include(\"-u\").and(include(54321)), hash_including(custom_environment: {}))\n      provider.uid = 54321\n    end\n\n    it \"should print a debug message with sensitive data redacted when the password property is set\" do\n      Puppet::Util::Log.level = :debug\n      resource[:password] = \"*\"\n      popen = double(\"popen\", :puts => nil, :close => nil)\n      expect(Open3).to receive(:popen3).and_return(popen)\n      expect(popen).to receive(:puts).with(\"abc123\")\n      provider.password = \"abc123\"\n\n      expect(@logs).not_to be_any {|log| log.level == :debug and log.message =~ /abc123/}\n     end\n\n    it \"should call execute with sensitive false when a non-sensitive property is set\" do\n      Puppet::Util::Log.level = :debug\n      resource[:home] = \"/home/testuser\"\n      resource[:managehome] = true\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: false))\n      provider.home = \"/newhome/testuser\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/user_role_add_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'tempfile'\n\ndescribe Puppet::Type.type(:user).provider(:user_role_add), :unless => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n  let(:resource) { Puppet::Type.type(:user).new(:name => 'myuser', :managehome => false, :allowdupe => false) }\n  let(:provider) { described_class.new(resource) }\n\n  before do\n    allow(resource).to receive(:should).and_return(\"fakeval\")\n    allow(resource).to receive(:should).with(:keys).and_return(Hash.new)\n    allow(resource).to receive(:[]).and_return(\"fakeval\")\n  end\n\n  describe \"#command\" do\n    before do\n      klass = double(\"provider\")\n      allow(klass).to receive(:superclass)\n      allow(klass).to receive(:command).with(:foo).and_return(\"userfoo\")\n      allow(klass).to receive(:command).with(:role_foo).and_return(\"rolefoo\")\n      allow(provider).to receive(:class).and_return(klass)\n    end\n\n    it \"should use the command if not a role and ensure!=role\" do\n      allow(provider).to receive(:is_role?).and_return(false)\n      allow(provider).to receive(:exists?).and_return(false)\n      allow(resource).to receive(:[]).with(:ensure).and_return(:present)\n      allow(provider.class).to receive(:foo)\n      expect(provider.command(:foo)).to eq(\"userfoo\")\n    end\n\n    it \"should use the role command when a role\" do\n      allow(provider).to receive(:is_role?).and_return(true)\n      expect(provider.command(:foo)).to eq(\"rolefoo\")\n    end\n\n    it \"should use the role command when !exists and ensure=role\" do\n      allow(provider).to receive(:is_role?).and_return(false)\n      allow(provider).to receive(:exists?).and_return(false)\n      allow(resource).to receive(:[]).with(:ensure).and_return(:role)\n      expect(provider.command(:foo)).to eq(\"rolefoo\")\n    end\n  end\n\n  describe \"#transition\" do\n    it \"should return the type set to whatever is passed in\" do\n      expect(provider).to receive(:command).with(:modify).and_return(\"foomod\")\n      provider.transition(\"bar\").include?(\"type=bar\")\n    end\n  end\n\n  describe \"#create\" do\n    before do\n      allow(provider).to receive(:password=)\n    end\n\n    it \"should use the add command when the user is not a role\" do\n      allow(provider).to receive(:is_role?).and_return(false)\n      expect(provider).to receive(:addcmd).and_return(\"useradd\")\n      expect(provider).to receive(:run).at_least(:once)\n      provider.create\n    end\n\n    it \"should use transition(normal) when the user is a role\" do\n      allow(provider).to receive(:is_role?).and_return(true)\n      expect(provider).to receive(:transition).with(\"normal\")\n      expect(provider).to receive(:run)\n      provider.create\n    end\n\n    it \"should set password age rules\" do\n      resource = Puppet::Type.type(:user).new :name => \"myuser\", :password_min_age => 5, :password_max_age => 10, :password_warn_days => 15, :provider => :user_role_add\n      provider = described_class.new(resource)\n      allow(provider).to receive(:user_attributes)\n      allow(provider).to receive(:execute)\n      expect(provider).to receive(:execute).with([anything, \"-n\", 5, \"-x\", 10, '-w', 15, \"myuser\"])\n      provider.create\n    end\n  end\n\n  describe \"#destroy\" do\n    it \"should use the delete command if the user exists and is not a role\" do\n      allow(provider).to receive(:exists?).and_return(true)\n      allow(provider).to receive(:is_role?).and_return(false)\n      expect(provider).to receive(:deletecmd)\n      expect(provider).to receive(:run)\n      provider.destroy\n    end\n\n    it \"should use the delete command if the user is a role\" do\n      allow(provider).to receive(:exists?).and_return(true)\n      allow(provider).to receive(:is_role?).and_return(true)\n      expect(provider).to receive(:deletecmd)\n      expect(provider).to receive(:run)\n      provider.destroy\n    end\n  end\n\n  describe \"#create_role\" do\n    it \"should use the transition(role) if the user exists\" do\n      allow(provider).to receive(:exists?).and_return(true)\n      allow(provider).to receive(:is_role?).and_return(false)\n      expect(provider).to receive(:transition).with(\"role\")\n      expect(provider).to receive(:run)\n      provider.create_role\n    end\n\n    it \"should use the add command when role doesn't exists\" do\n      allow(provider).to receive(:exists?).and_return(false)\n      expect(provider).to receive(:addcmd)\n      expect(provider).to receive(:run)\n      provider.create_role\n    end\n  end\n\n  describe \"with :allow_duplicates\" do\n    before do\n      allow(resource).to receive(:allowdupe?).and_return(true)\n      allow(provider).to receive(:is_role?).and_return(false)\n      allow(provider).to receive(:execute)\n      allow(resource).to receive(:system?).and_return(false)\n      expect(provider).to receive(:execute).with(include(\"-o\"), any_args)\n    end\n\n    it \"should add -o when the user is being created\" do\n      allow(provider).to receive(:password=)\n      provider.create\n    end\n\n    it \"should add -o when the uid is being modified\" do\n      provider.uid = 150\n    end\n  end\n\n  [:roles, :auths, :profiles].each do |val|\n    context \"#send\" do\n      describe \"when getting #{val}\" do\n        it \"should get the user_attributes\" do\n          expect(provider).to receive(:user_attributes)\n          provider.send(val)\n        end\n\n        it \"should get the #{val} attribute\" do\n          attributes = double(\"attributes\")\n          expect(attributes).to receive(:[]).with(val)\n          allow(provider).to receive(:user_attributes).and_return(attributes)\n          provider.send(val)\n        end\n      end\n    end\n  end\n\n  describe \"#keys\" do\n    it \"should get the user_attributes\" do\n      expect(provider).to receive(:user_attributes)\n      provider.keys\n    end\n\n    it \"should call removed_managed_attributes\" do\n      allow(provider).to receive(:user_attributes).and_return({ :type => \"normal\", :foo => \"something\" })\n      expect(provider).to receive(:remove_managed_attributes)\n      provider.keys\n    end\n\n    it \"should removed managed attribute (type, auths, roles, etc)\" do\n      allow(provider).to receive(:user_attributes).and_return({ :type => \"normal\", :foo => \"something\" })\n      expect(provider.keys).to eq({ :foo => \"something\" })\n    end\n  end\n\n  describe \"#add_properties\" do\n    it \"should call build_keys_cmd\" do\n      allow(resource).to receive(:should).and_return(\"\")\n      expect(resource).to receive(:should).with(:keys).and_return({ :foo => \"bar\" })\n      expect(provider).to receive(:build_keys_cmd).and_return([])\n      provider.add_properties\n    end\n\n    it \"should add the elements of the keys hash to an array\" do\n      allow(resource).to receive(:should).and_return(\"\")\n      expect(resource).to receive(:should).with(:keys).and_return({ :foo => \"bar\"})\n      expect(provider.add_properties).to eq([\"-K\", \"foo=bar\"])\n    end\n  end\n\n  describe \"#build_keys_cmd\" do\n    it \"should build cmd array with keypairs separated by -K ending with user\" do\n      expect(provider.build_keys_cmd({\"foo\" => \"bar\", \"baz\" => \"boo\"})).to eq([\"-K\", \"foo=bar\", \"-K\", \"baz=boo\"])\n    end\n  end\n\n  describe \"#keys=\" do\n    before do\n      allow(provider).to receive(:is_role?).and_return(false)\n    end\n\n    it \"should run a command\" do\n      expect(provider).to receive(:run)\n      provider.keys=({})\n    end\n\n    it \"should build the command\" do\n      allow(resource).to receive(:[]).with(:name).and_return(\"someuser\")\n      allow(provider).to receive(:command).and_return(\"usermod\")\n      expect(provider).to receive(:build_keys_cmd).and_return([\"-K\", \"foo=bar\"])\n      expect(provider).to receive(:run).with([\"usermod\", \"-K\", \"foo=bar\", \"someuser\"], \"modify attribute key pairs\")\n      provider.keys=({})\n    end\n  end\n\n  describe \"#password\" do\n    before do\n      @array = double(\"array\")\n    end\n\n    it \"should readlines of /etc/shadow\" do\n      expect(File).to receive(:readlines).with(\"/etc/shadow\").and_return([])\n      provider.password\n    end\n\n    it \"should reject anything that doesn't start with alpha numerics\" do\n      expect(@array).to receive(:reject).and_return([])\n      allow(File).to receive(:readlines).with(\"/etc/shadow\").and_return(@array)\n      provider.password\n    end\n\n    it \"should collect splitting on ':'\" do\n      allow(@array).to receive(:reject).and_return(@array)\n      expect(@array).to receive(:collect).and_return([])\n      allow(File).to receive(:readlines).with(\"/etc/shadow\").and_return(@array)\n      provider.password\n    end\n\n    it \"should find the matching user\" do\n      allow(resource).to receive(:[]).with(:name).and_return(\"username\")\n      allow(@array).to receive(:reject).and_return(@array)\n      allow(@array).to receive(:collect).and_return([[\"username\", \"hashedpassword\"], [\"someoneelse\", \"theirpassword\"]])\n      allow(File).to receive(:readlines).with(\"/etc/shadow\").and_return(@array)\n      expect(provider.password).to eq(\"hashedpassword\")\n    end\n\n    it \"should get the right password\" do\n      allow(resource).to receive(:[]).with(:name).and_return(\"username\")\n      allow(File).to receive(:readlines).with(\"/etc/shadow\").and_return([\"#comment\", \"   nonsense\", \"  \", \"username:hashedpassword:stuff:foo:bar:::\", \"other:pword:yay:::\"])\n      expect(provider.password).to eq(\"hashedpassword\")\n    end\n  end\n\n  describe \"#password=\" do\n    let(:path) { tmpfile('etc-shadow') }\n\n    before :each do\n      allow(provider).to receive(:target_file_path).and_return(path)\n    end\n\n    def write_fixture(content)\n      File.open(path, 'w') { |f| f.print(content) }\n    end\n\n    it \"should update the target user\" do\n      write_fixture <<FIXTURE\nfakeval:seriously:15315:0:99999:7:::\nFIXTURE\n      provider.password = \"totally\"\n      expect(File.read(path)).to match(/^fakeval:totally:/)\n    end\n\n    it \"should only update the target user\" do\n      expect(Date).to receive(:today).and_return(Date.new(2011,12,07))\n      write_fixture <<FIXTURE\nbefore:seriously:15315:0:99999:7:::\nfakeval:seriously:15315:0:99999:7:::\nfakevalish:seriously:15315:0:99999:7:::\nafter:seriously:15315:0:99999:7:::\nFIXTURE\n      provider.password = \"totally\"\n      expect(File.read(path)).to eq <<EOT\nbefore:seriously:15315:0:99999:7:::\nfakeval:totally:15315:0:99999:7:::\nfakevalish:seriously:15315:0:99999:7:::\nafter:seriously:15315:0:99999:7:::\nEOT\n    end\n\n    # This preserves the current semantics, but is it right? --daniel 2012-02-05\n    it \"should do nothing if the target user is missing\" do\n      fixture = <<FIXTURE\nbefore:seriously:15315:0:99999:7:::\nfakevalish:seriously:15315:0:99999:7:::\nafter:seriously:15315:0:99999:7:::\nFIXTURE\n\n      write_fixture fixture\n      provider.password = \"totally\"\n      expect(File.read(path)).to eq(fixture)\n    end\n\n    it \"should update the lastchg field\" do\n      expect(Date).to receive(:today).and_return(Date.new(2013,5,12)) # 15837 days after 1970-01-01\n      write_fixture <<FIXTURE\nbefore:seriously:15315:0:99999:7:::\nfakeval:seriously:15629:0:99999:7:::\nfakevalish:seriously:15315:0:99999:7:::\nafter:seriously:15315:0:99999:7:::\nFIXTURE\n      provider.password = \"totally\"\n      expect(File.read(path)).to eq <<EOT\nbefore:seriously:15315:0:99999:7:::\nfakeval:totally:15837:0:99999:7:::\nfakevalish:seriously:15315:0:99999:7:::\nafter:seriously:15315:0:99999:7:::\nEOT\n    end\n  end\n\n  describe \"#shadow_entry\" do\n    it \"should return the line for the right user\" do\n      allow(File).to receive(:readlines).and_return([\"someuser:!:10:5:20:7:1::\\n\", \"fakeval:*:20:10:30:7:2::\\n\", \"testuser:*:30:15:40:7:3::\\n\"])\n      expect(provider.shadow_entry).to eq([\"fakeval\", \"*\", \"20\", \"10\", \"30\", \"7\", \"2\", \"\", \"\"])\n    end\n  end\n\n  describe \"#password_max_age\" do\n    it \"should return a maximum age number\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345:0:50::::\\n\"])\n      expect(provider.password_max_age).to eq(\"50\")\n    end\n\n    it \"should return -1 for no maximum\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345::::::\\n\"])\n      expect(provider.password_max_age).to eq(-1)\n    end\n\n    it \"should return -1 for no maximum when failed attempts are present\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345::::::3\\n\"])\n      expect(provider.password_max_age).to eq(-1)\n    end\n  end\n\n  describe \"#password_min_age\" do\n    it \"should return a minimum age number\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345:10:50::::\\n\"])\n      expect(provider.password_min_age).to eq(\"10\")\n    end\n\n    it \"should return -1 for no minimum\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345::::::\\n\"])\n      expect(provider.password_min_age).to eq(-1)\n    end\n\n    it \"should return -1 for no minimum when failed attempts are present\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345::::::3\\n\"])\n      expect(provider.password_min_age).to eq(-1)\n    end\n  end\n\n  describe \"#password_warn_days\" do\n    it \"should return a warn days number\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345:10:50:30:::\\n\"])\n      expect(provider.password_warn_days).to eq(\"30\")\n    end\n\n    it \"should return -1 for no warn days\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345::::::\\n\"])\n      expect(provider.password_warn_days).to eq(-1)\n    end\n\n    it \"should return -1 for no warn days when failed attempts are present\" do\n      allow(File).to receive(:readlines).and_return([\"fakeval:NP:12345::::::3\\n\"])\n      expect(provider.password_warn_days).to eq(-1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/useradd_spec.rb",
    "content": "require 'spec_helper'\n\nRSpec::Matchers.define_negated_matcher :excluding, :include\n\ndescribe Puppet::Type.type(:user).provider(:useradd) do\n  before :each do\n    allow(Puppet::Util::POSIX).to receive(:groups_of).and_return([])\n    allow(described_class).to receive(:command).with(:password).and_return('/usr/bin/chage')\n    allow(described_class).to receive(:command).with(:localpassword).and_return('/usr/sbin/lchage')\n    allow(described_class).to receive(:command).with(:add).and_return('/usr/sbin/useradd')\n    allow(described_class).to receive(:command).with(:localadd).and_return('/usr/sbin/luseradd')\n    allow(described_class).to receive(:command).with(:modify).and_return('/usr/sbin/usermod')\n    allow(described_class).to receive(:command).with(:localmodify).and_return('/usr/sbin/lusermod')\n    allow(described_class).to receive(:command).with(:delete).and_return('/usr/sbin/userdel')\n    allow(described_class).to receive(:command).with(:localdelete).and_return('/usr/sbin/luserdel')\n    allow(described_class).to receive(:command).with(:chpasswd).and_return('/usr/sbin/chpasswd')\n  end\n\n  let(:resource) do\n    Puppet::Type.type(:user).new(\n      :name       => 'myuser',\n      :managehome => :false,\n      :system     => :false,\n      :provider   => provider\n    )\n  end\n\n  let(:provider) { described_class.new(:name => 'myuser') }\n\n  let(:shadow_entry) {\n    return unless Puppet.features.libshadow?\n    entry = Etc::PasswdEntry.new\n    entry[:sp_namp]   = 'myuser' # login name\n    entry[:sp_pwdp]   = '$6$FvW8Ib8h$qQMI/CR9m.QzIicZKutLpBgCBBdrch1IX0rTnxuI32K1pD9.RXZrmeKQlaC.RzODNuoUtPPIyQDufunvLOQWF0' # encrypted password\n    entry[:sp_lstchg] = 15573    # date of last password change\n    entry[:sp_min]    = 10       # minimum password age\n    entry[:sp_max]    = 20       # maximum password age\n    entry[:sp_warn]   = 7        # password warning period\n    entry[:sp_inact]  = -1       # password inactivity period\n    entry[:sp_expire] = 15706    # account expiration date\n    entry\n  }\n\n  describe \"#create\" do\n    before do\n      allow(provider).to receive(:exists?).and_return(false)\n    end\n\n    it \"should not redact the command from debug logs if there is no password\" do\n      described_class.has_feature :manages_passwords\n      resource[:ensure] = :present\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: false))\n      provider.create\n    end\n\n    it \"should redact the command from debug logs if there is a password\" do\n      described_class.has_feature :manages_passwords\n      resource2 = Puppet::Type.type(:user).new(\n        :name       => 'myuser',\n        :password   => 'a pass word',\n        :managehome => :false,\n        :system     => :false,\n        :provider   => provider,\n      )\n      resource2[:ensure] = :present\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: true)).twice\n      provider.create\n    end\n\n    it \"should add -g when no gid is specified and group already exists\" do\n      allow(Puppet::Util).to receive(:gid).and_return(true)\n      resource[:ensure] = :present\n      expect(provider).to receive(:execute).with(include('-g'), kind_of(Hash))\n      provider.create\n    end\n\n    context \"when setting groups\" do\n      it \"uses -G to set groups\" do\n        allow(Facter).to receive(:value).with('os.family').and_return('Solaris')\n        allow(Facter).to receive(:value).with('os.release.major')\n        resource[:ensure] = :present\n        resource[:groups] = ['group1', 'group2']\n        expect(provider).to receive(:execute).with(['/usr/sbin/useradd', '-G', 'group1,group2', 'myuser'], kind_of(Hash))\n        provider.create\n      end\n\n      it \"uses -G to set groups with -M on supported systems\" do\n        allow(Facter).to receive(:value).with('os.family').and_return('RedHat')\n        allow(Facter).to receive(:value).with('os.release.major')\n        resource[:ensure] = :present\n        resource[:groups] = ['group1', 'group2']\n        expect(provider).to receive(:execute).with(['/usr/sbin/useradd', '-G', 'group1,group2', '-M', 'myuser'], kind_of(Hash))\n        provider.create\n      end\n    end\n\n    it \"should add -o when allowdupe is enabled and the user is being created\" do\n      resource[:allowdupe] = true\n      expect(provider).to receive(:execute).with(include('-o'), kind_of(Hash))\n      provider.create\n    end\n\n    describe \"on systems that support has_system\", :if => described_class.system_users? do\n      it \"should add -r when system is enabled\" do\n        resource[:system] = :true\n        expect(provider).to be_system_users\n        expect(provider).to receive(:execute).with(include('-r'), kind_of(Hash))\n        provider.create\n      end\n    end\n\n    describe \"on systems that do not support has_system\", :unless => described_class.system_users? do\n      it \"should not add -r when system is enabled\" do\n        resource[:system] = :true\n        expect(provider).not_to be_system_users\n        expect(provider).to receive(:execute).with(['/usr/sbin/useradd', 'myuser'], kind_of(Hash))\n        provider.create\n      end\n    end\n\n    it \"should set password age rules\" do\n      described_class.has_feature :manages_password_age\n      resource[:password_min_age] = 5\n      resource[:password_max_age] = 10\n      resource[:password_warn_days] = 15\n      expect(provider).to receive(:execute).with(include('/usr/sbin/useradd'), kind_of(Hash))\n      expect(provider).to receive(:execute).with(['/usr/bin/chage', '-m', 5, '-M', 10, '-W', 15, 'myuser'], hash_including(failonfail: true, combine: true, custom_environment: {}))\n      provider.create\n    end\n\n    describe \"on systems with the libuser and forcelocal=true\" do\n      before do\n         described_class.has_feature :manages_local_users_and_groups\n         resource[:forcelocal] = true\n      end\n\n      it \"should use luseradd instead of useradd\" do\n        expect(provider).to receive(:execute).with(include('/usr/sbin/luseradd'), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.create\n      end\n\n      it \"should NOT use -o when allowdupe=true\" do\n        resource[:allowdupe] = :true\n        expect(provider).to receive(:execute).with(excluding('-o'), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.create\n      end\n\n      it \"should raise an exception for duplicate UIDs\" do\n        resource[:uid] = 505\n        allow(provider).to receive(:finduser).and_return(true)\n        expect { provider.create }.to raise_error(Puppet::Error, \"UID 505 already exists, use allowdupe to force user creation\")\n      end\n\n      it \"should not use -G for luseradd and should call usermod with -G after luseradd when groups property is set\" do\n        resource[:groups] = ['group1', 'group2']\n        allow(provider).to receive(:localgroups)\n        expect(provider).to receive(:execute).with(include('/usr/sbin/luseradd').and(excluding('-G')), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        expect(provider).to receive(:execute).with(include('/usr/sbin/usermod').and(include('-G')), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.create\n      end\n\n      it \"should not use -m when managehome set\" do\n        resource[:managehome] = :true\n        expect(provider).to receive(:execute).with(excluding('-m'), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.create\n      end\n\n      it \"should not use -e with luseradd, should call usermod with -e after luseradd when expiry is set\" do\n        resource[:expiry] = '2038-01-24'\n        expect(provider).to receive(:execute).with(include('/usr/sbin/luseradd').and(excluding('-e')), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        expect(provider).to receive(:execute).with(include('/usr/sbin/usermod').and(include('-e')), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.create\n      end\n\n      it 'should set password age rules locally' do\n        described_class.has_feature :manages_password_age\n        resource[:password_min_age] = 5\n        resource[:password_max_age] = 10\n        resource[:password_warn_days] = 15\n        expect(provider).to receive(:execute).with(include('/usr/sbin/luseradd'), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        expect(provider).to receive(:execute).with(['/usr/sbin/lchage', '-m', 5, '-M', 10, '-W', 15, 'myuser'], hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.create\n      end\n    end\n\n    describe \"on systems that allow to set shell\" do\n      it \"should trigger shell validation\" do\n        resource[:shell] = '/bin/bash'\n        expect(provider).to receive(:check_valid_shell)\n        expect(provider).to receive(:execute).with(include('-s'), kind_of(Hash))\n        provider.create\n      end\n    end\n  end\n\n  describe 'when modifying the password' do\n    before do\n      described_class.has_feature :manages_local_users_and_groups\n      described_class.has_feature :manages_passwords\n      #Setting any resource value here initializes needed variables and methods in the resource and provider\n      #Setting a password value here initializes the existence and management of the password parameter itself\n      #Otherwise, this value would not need to be initialized for the test\n      resource[:password] = ''\n    end\n\n    it \"should not call execute with sensitive if non-sensitive data is changed\" do\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: false))\n      provider.home = 'foo/bar'\n    end\n\n    it \"should call execute with sensitive if sensitive data is changed\" do\n      expect(provider).to receive(:execute).with(kind_of(Array), hash_including(sensitive: true)).and_return('')\n      provider.password = 'bird bird bird'\n    end\n  end\n\n  describe '#modify' do\n    describe \"on systems with the libuser and forcelocal=false\" do\n      before do\n         described_class.has_feature :manages_local_users_and_groups\n         resource[:forcelocal] = false\n      end\n\n      it \"should use usermod\" do\n        expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-u', 150, 'myuser'], hash_including(failonfail: true, combine: true, custom_environment: {}))\n        provider.uid = 150\n      end\n\n      it \"should use -o when allowdupe=true\" do\n        resource[:allowdupe] = :true\n        expect(provider).to receive(:execute).with(include('-o'), hash_including(failonfail: true, combine: true, custom_environment: {}))\n        provider.uid = 505\n      end\n\n      it 'should use chage for password_min_age' do\n        expect(provider).to receive(:execute).with(['/usr/bin/chage', '-m', 100, 'myuser'], hash_including(failonfail: true, combine: true, custom_environment: {}))\n        provider.password_min_age = 100\n      end\n\n      it 'should use chage for password_max_age' do\n        expect(provider).to receive(:execute).with(['/usr/bin/chage', '-M', 101, 'myuser'], hash_including(failonfail: true, combine: true, custom_environment: {}))\n        provider.password_max_age = 101\n      end\n\n      it 'should use chage for password_warn_days' do\n        expect(provider).to receive(:execute).with(['/usr/bin/chage', '-W', 99, 'myuser'], hash_including(failonfail: true, combine: true, custom_environment: {}))\n        provider.password_warn_days = 99\n      end\n\n      it 'should not call check_allow_dup if not modifying the uid' do\n        expect(provider).not_to receive(:check_allow_dup)\n        expect(provider).to receive(:execute)\n        provider.home = 'foo/bar'\n      end\n    end\n\n    describe \"on systems with the libuser and forcelocal=true\" do\n      before do\n         described_class.has_feature :libuser\n         resource[:forcelocal] = true\n      end\n\n      it \"should use lusermod and not usermod\" do\n        expect(provider).to receive(:execute).with(['/usr/sbin/lusermod', '-u', 150, 'myuser'], hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.uid = 150\n      end\n\n      it \"should NOT use -o when allowdupe=true\" do\n        resource[:allowdupe] = :true\n        expect(provider).to receive(:execute).with(excluding('-o'), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.uid = 505\n      end\n\n      it \"should raise an exception for duplicate UIDs\" do\n        resource[:uid] = 505\n        allow(provider).to receive(:finduser).and_return(true)\n        expect { provider.uid = 505 }.to raise_error(Puppet::Error, \"UID 505 already exists, use allowdupe to force user creation\")\n      end\n\n      it 'should use lchage for password_warn_days' do\n        expect(provider).to receive(:execute).with(['/usr/sbin/lchage', '-W', 99, 'myuser'], hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.password_warn_days = 99\n      end\n\n      it 'should use lchage for password_min_age' do\n        expect(provider).to receive(:execute).with(['/usr/sbin/lchage', '-m', 100, 'myuser'], hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.password_min_age = 100\n      end\n\n      it 'should use lchage for password_max_age' do\n        expect(provider).to receive(:execute).with(['/usr/sbin/lchage', '-M', 101, 'myuser'], hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.password_max_age = 101\n      end\n    end\n  end\n\n  describe \"#uid=\" do\n    it \"should add -o when allowdupe is enabled and the uid is being modified\" do\n      resource[:allowdupe] = :true\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-u', 150, '-o', 'myuser'], hash_including(custom_environment: {}))\n      provider.uid = 150\n    end\n  end\n\n  describe \"#expiry=\" do\n    it \"should pass expiry to usermod as MM/DD/YY when on Solaris\" do\n      expect(Facter).to receive(:value).with('os.name').and_return('Solaris')\n      resource[:expiry] = '2012-10-31'\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-e', '10/31/2012', 'myuser'], hash_including(custom_environment: {}))\n      provider.expiry = '2012-10-31'\n    end\n\n    it \"should pass expiry to usermod as YYYY-MM-DD when not on Solaris\" do\n      expect(Facter).to receive(:value).with('os.name').and_return('not_solaris')\n      resource[:expiry] = '2012-10-31'\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-e', '2012-10-31', 'myuser'], hash_including(custom_environment: {}))\n      provider.expiry = '2012-10-31'\n    end\n\n    it \"should use -e with an empty string when the expiry property is removed\" do\n      resource[:expiry] = :absent\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-e', '', 'myuser'], hash_including(custom_environment: {}))\n      provider.expiry = :absent\n    end\n\n    it \"should use -e with -1 when the expiry property is removed on SLES11\" do\n      allow(Facter).to receive(:value).with('os.name').and_return('SLES')\n      allow(Facter).to receive(:value).with('os.release.major').and_return('11')\n      resource[:expiry] = :absent\n      expect(provider).to receive(:execute).with(['/usr/sbin/usermod', '-e', -1, 'myuser'], hash_including(custom_environment: {}))\n      provider.expiry = :absent\n    end\n  end\n\n  describe \"#comment\" do\n    before { described_class.has_feature :manages_local_users_and_groups }\n\n    let(:content) { \"myuser:x:x:x:local comment:x:x\" }\n\n    it \"should return the local comment string when forcelocal is true\" do\n      resource[:forcelocal] = true\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/passwd').and_return(true)\n      allow(Puppet::FileSystem).to receive(:each_line).with('/etc/passwd').and_yield(content)\n      expect(provider.comment).to eq('local comment')\n    end\n\n    it \"should fall back to nameservice comment string when forcelocal is false\" do\n      resource[:forcelocal] = false\n      allow(provider).to receive(:get).with(:comment).and_return('remote comment')\n      expect(provider).not_to receive(:localcomment)\n      expect(provider.comment).to eq('remote comment')\n    end\n  end\n\n  describe \"#shell\" do\n    before { described_class.has_feature :manages_local_users_and_groups }\n\n    let(:content) { \"myuser:x:x:x:x:x:/bin/local_shell\" }\n\n    it \"should return the local shell string when forcelocal is true\" do\n      resource[:forcelocal] = true\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/passwd').and_return(true)\n      allow(Puppet::FileSystem).to receive(:each_line).with('/etc/passwd').and_yield(content)\n      expect(provider.shell).to eq('/bin/local_shell')\n    end\n\n    it \"should fall back to nameservice shell string when forcelocal is false\" do\n      resource[:forcelocal] = false\n      allow(provider).to receive(:get).with(:shell).and_return('/bin/remote_shell')\n      expect(provider).not_to receive(:localshell)\n      expect(provider.shell).to eq('/bin/remote_shell')\n    end\n  end\n\n  describe \"#home\" do\n    before { described_class.has_feature :manages_local_users_and_groups }\n\n    let(:content) { \"myuser:x:x:x:x:/opt/local_home:x\" }\n\n    it \"should return the local home string when forcelocal is true\" do\n      resource[:forcelocal] = true\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/passwd').and_return(true)\n      allow(Puppet::FileSystem).to receive(:each_line).with('/etc/passwd').and_yield(content)\n      expect(provider.home).to eq('/opt/local_home')\n    end\n\n    it \"should fall back to nameservice home string when forcelocal is false\" do\n      resource[:forcelocal] = false\n      allow(provider).to receive(:get).with(:home).and_return('/opt/remote_home')\n      expect(provider).not_to receive(:localhome)\n      expect(provider.home).to eq('/opt/remote_home')\n    end\n  end\n\n  describe \"#gid\" do\n    before { described_class.has_feature :manages_local_users_and_groups }\n\n    let(:content) { \"myuser:x:x:999:x:x:x\" }\n\n    it \"should return the local GID when forcelocal is true\" do\n      resource[:forcelocal] = true\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/passwd').and_return(true)\n      allow(Puppet::FileSystem).to receive(:each_line).with('/etc/passwd').and_yield(content)\n      expect(provider.gid).to eq(999)\n    end\n\n    it \"should fall back to nameservice GID when forcelocal is false\" do\n      resource[:forcelocal] = false\n      allow(provider).to receive(:get).with(:gid).and_return(1234)\n      expect(provider).not_to receive(:localgid)\n      expect(provider.gid).to eq(1234)\n    end\n  end\n\n  describe \"#groups\" do\n    before { described_class.has_feature :manages_local_users_and_groups }\n\n    let(:content) do\n      StringIO.new(<<~EOF)\n      group1:x:0:myuser\n      group2:x:999:\n      group3:x:998:myuser\n      EOF\n    end\n\n    let(:content_with_empty_line) do\n      StringIO.new(<<~EOF)\n      group1:x:0:myuser\n      group2:x:999:\n      group3:x:998:myuser\n\n      EOF\n    end\n\n    it \"should return the local groups string when forcelocal is true\" do\n      resource[:forcelocal] = true\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/group').and_return(true)\n      allow(File).to receive(:open).with(Pathname.new('/etc/group')).and_yield(content)\n      expect(provider.groups).to eq(['group1', 'group3'])\n    end\n\n    it \"does not raise when parsing empty lines in /etc/group\" do\n      resource[:forcelocal] = true\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/group').and_return(true)\n      allow(File).to receive(:open).with(Pathname.new('/etc/group')).and_yield(content_with_empty_line)\n      expect { provider.groups }.not_to raise_error\n    end\n\n    it \"should fall back to nameservice groups when forcelocal is false\" do\n      resource[:forcelocal] = false\n      allow(Puppet::Util::POSIX).to receive(:groups_of).with('myuser').and_return(['remote groups'])\n      expect(provider).not_to receive(:localgroups)\n      expect(provider.groups).to eq('remote groups')\n    end\n  end\n\n  describe \"#finduser\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).with('/etc/passwd').and_return(true)\n      allow(Puppet::FileSystem).to receive(:each_line).with('/etc/passwd').and_yield(content)\n    end\n\n    let(:content) { \"sample_account:sample_password:sample_uid:sample_gid:sample_gecos:sample_directory:sample_shell\" }\n    let(:output) do\n      {\n        account: 'sample_account',\n        password: 'sample_password',\n        uid: 'sample_uid',\n        gid: 'sample_gid',\n        gecos: 'sample_gecos',\n        directory: 'sample_directory',\n        shell: 'sample_shell',\n      }\n    end\n\n    [:account, :password, :uid, :gid, :gecos, :directory, :shell].each do |key|\n      it \"finds an user by #{key} when asked\" do\n        expect(provider.finduser(key, \"sample_#{key}\")).to eq(output)\n      end\n    end\n\n    it \"returns false when specified key/value pair is not found\" do\n      expect(provider.finduser(:account, 'invalid_account')).to eq(false)\n    end\n\n    it \"reads the user file only once per resource\" do\n      expect(Puppet::FileSystem).to receive(:each_line).with('/etc/passwd').once\n      5.times { provider.finduser(:account, 'sample_account') }\n    end\n  end\n\n  describe \"#check_allow_dup\" do\n    it \"should return an array with a flag if dup is allowed\" do\n      resource[:allowdupe] = :true\n      expect(provider.check_allow_dup).to eq([\"-o\"])\n    end\n\n    it \"should return an empty array if no dup is allowed\" do\n      resource[:allowdupe] = :false\n      expect(provider.check_allow_dup).to eq([])\n    end\n  end\n\n  describe \"#check_system_users\" do\n    it \"should check system users\" do\n      expect(described_class).to receive(:system_users?).and_return(true)\n      expect(resource).to receive(:system?)\n      provider.check_system_users\n    end\n\n    it \"should return an array with a flag if it's a system user\" do\n      expect(described_class).to receive(:system_users?).and_return(true)\n      resource[:system] = :true\n      expect(provider.check_system_users).to eq([\"-r\"])\n    end\n\n    it \"should return an empty array if it's not a system user\" do\n      expect(described_class).to receive(:system_users?).and_return(true)\n      resource[:system] = :false\n      expect(provider.check_system_users).to eq([])\n    end\n\n    it \"should return an empty array if system user is not featured\" do\n      expect(described_class).to receive(:system_users?).and_return(false)\n      resource[:system] = :true\n      expect(provider.check_system_users).to eq([])\n    end\n  end\n\n  describe \"#check_manage_home\" do\n    it \"should return an array with -m flag if home is managed\" do\n      resource[:managehome] = :true\n      expect(provider).to receive(:execute).with(include('-m'), hash_including(custom_environment: {}))\n      provider.create\n    end\n\n    it \"should return an array with -r flag if home is managed\" do\n      resource[:managehome] = :true\n      resource[:ensure] = :absent\n      allow(provider).to receive(:exists?).and_return(true)\n      expect(provider).to receive(:execute).with(include('-r'), hash_including(custom_environment: {}))\n      provider.delete\n    end\n\n    it \"should use -M flag if home is not managed on a supported system\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(\"RedHat\")\n      allow(Facter).to receive(:value).with('os.release.major')\n      resource[:managehome] = :false\n      expect(provider).to receive(:execute).with(include('-M'), kind_of(Hash))\n      provider.create\n    end\n\n    it \"should not use -M flag if home is not managed on an unsupported system\" do\n      allow(Facter).to receive(:value).with('os.family').and_return(\"Suse\")\n      allow(Facter).to receive(:value).with('os.release.major').and_return(\"11\")\n      resource[:managehome] = :false\n      expect(provider).to receive(:execute).with(excluding('-M'), kind_of(Hash))\n      provider.create\n    end\n  end\n\n  describe \"#addcmd\" do\n    before do\n      resource[:allowdupe] = :true\n      resource[:managehome] = :true\n      resource[:system] = :true\n      resource[:groups] = [ 'somegroup' ]\n    end\n\n    it \"should call command with :add\" do\n      expect(provider).to receive(:command).with(:add)\n      provider.addcmd\n    end\n\n    it \"should add properties\" do\n      expect(provider).to receive(:add_properties).and_return(['-foo_add_properties'])\n      expect(provider.addcmd).to include '-foo_add_properties'\n    end\n\n    it \"should check and add if dup allowed\" do\n      expect(provider).to receive(:check_allow_dup).and_return(['-allow_dup_flag'])\n      expect(provider.addcmd).to include '-allow_dup_flag'\n    end\n\n    it \"should check and add if home is managed\" do\n      expect(provider).to receive(:check_manage_home).and_return(['-manage_home_flag'])\n      expect(provider.addcmd).to include '-manage_home_flag'\n    end\n\n    it \"should add the resource :name\" do\n      expect(provider.addcmd).to include 'myuser'\n    end\n\n    describe \"on systems featuring system_users\", :if => described_class.system_users? do\n      it \"should return an array with -r if system? is true\" do\n        resource[:system] = :true\n        expect(provider.addcmd).to include(\"-r\")\n      end\n\n      it \"should return an array without -r if system? is false\" do\n        resource[:system] = :false\n        expect(provider.addcmd).not_to include(\"-r\")\n      end\n    end\n\n    describe \"on systems not featuring system_users\", :unless => described_class.system_users? do\n      [:false, :true].each do |system|\n        it \"should return an array without -r if system? is #{system}\" do\n          resource[:system] = system\n          expect(provider.addcmd).not_to include(\"-r\")\n        end\n      end\n    end\n\n    it \"should return an array with the full command and expiry as MM/DD/YY when on Solaris\" do\n      allow(Facter).to receive(:value).with('os.name').and_return('Solaris')\n      expect(described_class).to receive(:system_users?).and_return(true)\n      resource[:expiry] = \"2012-08-18\"\n      expect(provider.addcmd).to eq(['/usr/sbin/useradd', '-e', '08/18/2012', '-G', 'somegroup', '-o', '-m', '-r', 'myuser'])\n    end\n\n    it \"should return an array with the full command and expiry as YYYY-MM-DD when not on Solaris\" do\n      allow(Facter).to receive(:value).with('os.name').and_return('not_solaris')\n      expect(described_class).to receive(:system_users?).and_return(true)\n      resource[:expiry] = \"2012-08-18\"\n      expect(provider.addcmd).to eq(['/usr/sbin/useradd', '-e', '2012-08-18', '-G', 'somegroup', '-o', '-m', '-r', 'myuser'])\n    end\n\n    it \"should return an array without -e if expiry is undefined full command\" do\n      expect(described_class).to receive(:system_users?).and_return(true)\n      expect(provider.addcmd).to eq([\"/usr/sbin/useradd\", \"-G\", \"somegroup\", \"-o\", \"-m\", \"-r\", \"myuser\"])\n    end\n\n    it \"should pass -e \\\"\\\" if the expiry has to be removed\" do\n      expect(described_class).to receive(:system_users?).and_return(true)\n      resource[:expiry] = :absent\n\n      expect(provider.addcmd).to eq(['/usr/sbin/useradd', '-e', '', '-G', 'somegroup', '-o', '-m', '-r', 'myuser'])\n    end\n\n    it \"should use lgroupadd with forcelocal=true\" do\n      resource[:forcelocal] = :true\n      expect(provider.addcmd[0]).to eq('/usr/sbin/luseradd')\n    end\n\n    it \"should not pass -o with forcelocal=true and allowdupe=true\" do\n      resource[:forcelocal] = :true\n      resource[:allowdupe] = :true\n      expect(provider.addcmd).not_to include(\"-o\")\n    end\n\n    context 'when forcelocal=true' do\n      before do\n        resource[:forcelocal] = :true\n      end\n\n      it 'does not pass lchage options to luseradd for password_max_age' do\n        resource[:password_max_age] = 100\n        expect(provider.addcmd).not_to include('-M')\n      end\n\n      it 'does not pass lchage options to luseradd for password_min_age' do\n        resource[:managehome] = false  # This needs to be set so that we don't pass in -m to create the home\n        resource[:password_min_age] = 100\n        expect(provider.addcmd).not_to include('-m')\n      end\n\n      it 'does not pass lchage options to luseradd for password_warn_days' do\n        resource[:password_warn_days] = 100\n        expect(provider.addcmd).not_to include('-W')\n      end\n    end\n  end\n\n  {\n    :password_min_age   => 10,\n    :password_max_age   => 20,\n    :password_warn_days => 30,\n    :password           => '$6$FvW8Ib8h$qQMI/CR9m.QzIicZKutLpBgCBBdrch1IX0rTnxuI32K1pD9.RXZrmeKQlaC.RzODNuoUtPPIyQDufunvLOQWF0'\n  }.each_pair do |property, expected_value|\n    describe \"##{property}\" do\n      before :each do\n        resource # just to link the resource to the provider\n      end\n\n      it \"should return absent if libshadow feature is not present\" do\n        allow(Puppet.features).to receive(:libshadow?).and_return(false)\n        # Shadow::Passwd.expects(:getspnam).never # if we really don't have libshadow we dont have Shadow::Passwd either\n        expect(provider.send(property)).to eq(:absent)\n      end\n\n      it \"should return absent if user cannot be found\", :if => Puppet.features.libshadow? do\n        expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(nil)\n        expect(provider.send(property)).to eq(:absent)\n      end\n\n      it \"should return the correct value if libshadow is present\", :if => Puppet.features.libshadow? do\n        expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(shadow_entry)\n        expect(provider.send(property)).to eq(expected_value)\n      end\n\n      # nameservice provider instances are initialized with a @canonical_name\n      # instance variable to track the original name of the instance on disk\n      # before converting it to UTF-8 if appropriate. When re-querying the\n      # system for attributes of this user such as password info, we need to\n      # supply the pre-UTF8-converted value.\n      it \"should query using the canonical_name attribute of the user\", :if => Puppet.features.libshadow? do\n        canonical_name = [253, 241].pack('C*').force_encoding(Encoding::EUC_KR)\n        provider = described_class.new(:name => '??', :canonical_name => canonical_name)\n\n        expect(Shadow::Passwd).to receive(:getspnam).with(canonical_name).and_return(shadow_entry)\n        provider.password\n      end\n    end\n  end\n\n  describe '#expiry' do\n    before :each do\n      resource # just to link the resource to the provider\n    end\n\n    it \"should return absent if libshadow feature is not present\" do\n      allow(Puppet.features).to receive(:libshadow?).and_return(false)\n      expect(provider.expiry).to eq(:absent)\n    end\n\n    it \"should return absent if user cannot be found\", :if => Puppet.features.libshadow? do\n      expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(nil)\n      expect(provider.expiry).to eq(:absent)\n    end\n\n    it \"should return absent if expiry is -1\", :if => Puppet.features.libshadow? do\n      shadow_entry.sp_expire = -1\n      expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(shadow_entry)\n      expect(provider.expiry).to eq(:absent)\n    end\n\n    it \"should convert to YYYY-MM-DD\", :if => Puppet.features.libshadow? do\n      expect(Shadow::Passwd).to receive(:getspnam).with('myuser').and_return(shadow_entry)\n      expect(provider.expiry).to eq('2013-01-01')\n    end\n  end\n\n  describe \"#passcmd\" do\n    before do\n      resource[:allowdupe] = :true\n      resource[:managehome] = :true\n      resource[:system] = :true\n      described_class.has_feature :manages_password_age\n    end\n\n    it \"should call command with :pass\" do\n      # command(:password) is only called inside passcmd if\n      # password_min_age or password_max_age is set\n      resource[:password_min_age] = 123\n      expect(provider).to receive(:command).with(:password)\n      provider.passcmd\n    end\n\n    it \"should return nil if neither min nor max is set\" do\n      expect(provider.passcmd).to be_nil\n    end\n\n    it \"should return a chage command array with -m <value> and the user name if password_min_age is set\" do\n      resource[:password_min_age] = 123\n      expect(provider.passcmd).to eq(['/usr/bin/chage', '-m', 123, 'myuser'])\n    end\n\n    it \"should return a chage command array with -M <value> if password_max_age is set\" do\n      resource[:password_max_age] = 999\n      expect(provider.passcmd).to eq(['/usr/bin/chage', '-M', 999, 'myuser'])\n    end\n\n    it \"should return a chage command array with -W <value> if password_warn_days is set\" do\n      resource[:password_warn_days] = 999\n      expect(provider.passcmd).to eq(['/usr/bin/chage', '-W', 999, 'myuser'])\n    end\n\n    it \"should return a chage command array with -M <value> -m <value> if both password_min_age and password_max_age are set\" do\n      resource[:password_min_age] = 123\n      resource[:password_max_age] = 999\n      expect(provider.passcmd).to eq(['/usr/bin/chage', '-m', 123, '-M', 999, 'myuser'])\n    end\n\n    it \"should return a chage command array with -M <value> -m <value> -W <value> if password_min_age, password_max_age and password_warn_days are set\" do\n      resource[:password_min_age] = 123\n      resource[:password_max_age] = 999\n      resource[:password_warn_days] = 555\n      expect(provider.passcmd).to eq(['/usr/bin/chage', '-m', 123, '-M', 999, '-W', 555, 'myuser'])\n    end\n\n    context 'with forcelocal=true' do\n      before do\n        resource[:forcelocal] = true\n      end\n\n      it 'should return a lchage command array with -M <value> -m <value> -W <value> if password_min_age, password_max_age and password_warn_days are set' do\n        resource[:password_min_age] = 123\n        resource[:password_max_age] = 999\n        resource[:password_warn_days] = 555\n        expect(provider.passcmd).to eq(['/usr/sbin/lchage', '-m', 123, '-M', 999, '-W', 555, 'myuser'])\n      end\n    end\n  end\n\n  describe \"#check_valid_shell\" do\n    it \"should raise an error if shell does not exist\" do\n      resource[:shell] = 'foo/bin/bash'\n      expect { provider.check_valid_shell }.to raise_error(Puppet::Error, /Shell foo\\/bin\\/bash must exist/)\n    end\n\n    it \"should raise an error if the shell is not executable\" do\n      allow(FileTest).to receive(:executable?).with('LICENSE').and_return(false)\n      resource[:shell] = 'LICENSE'\n      expect { provider.check_valid_shell }.to raise_error(Puppet::Error, /Shell LICENSE must be executable/)\n    end\n  end\n\n  describe \"#delete\" do\n    before do\n       allow(provider).to receive(:exists?).and_return(true)\n       resource[:ensure] = :absent\n    end\n\n    describe \"on systems with the libuser and forcelocal=false\" do\n      before do\n         described_class.has_feature :manages_local_users_and_groups\n         resource[:forcelocal] = false\n      end\n\n      it \"should use userdel to delete users\" do\n        expect(provider).to receive(:execute).with(include('/usr/sbin/userdel'), hash_including(custom_environment: {}))\n        provider.delete\n      end\n    end\n\n    describe \"on systems with the libuser and forcelocal=true\" do\n      before do\n         described_class.has_feature :manages_local_users_and_groups\n         resource[:forcelocal] = true\n      end\n\n      it \"should use luserdel to delete users\" do\n        expect(provider).to receive(:execute).with(include('/usr/sbin/luserdel'), hash_including(custom_environment: hash_including('LIBUSER_CONF')))\n        provider.delete\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider/user/windows_adsi_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:user).provider(:windows_adsi), :if => Puppet::Util::Platform.windows? do\n  let(:resource) do\n    Puppet::Type.type(:user).new(\n      :title => 'testuser',\n      :comment => 'Test J. User',\n      :provider => :windows_adsi\n    )\n  end\n\n  let(:provider) { resource.provider }\n\n  let(:connection) { double('connection') }\n\n  before :each do\n    allow(Puppet::Util::Windows::ADSI).to receive(:computer_name).and_return('testcomputername')\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(connection)\n    # this would normally query the system, but not needed for these tests\n    allow(Puppet::Util::Windows::ADSI::User).to receive(:localized_domains).and_return([])\n  end\n\n  describe \".instances\" do\n    it \"should enumerate all users\" do\n      names = ['user1', 'user2', 'user3']\n      stub_users = names.map {|n| double(:name => n)}\n      allow(connection).to receive(:execquery).with('select name from win32_useraccount where localaccount = \"TRUE\"').and_return(stub_users)\n\n      expect(described_class.instances.map(&:name)).to match(names)\n    end\n  end\n\n  it \"should provide access to a Puppet::Util::Windows::ADSI::User object\" do\n    expect(provider.user).to be_a(Puppet::Util::Windows::ADSI::User)\n  end\n\n  describe \"when retrieving the password property\" do\n    context \"when the resource has a nil password\" do\n      it \"should never issue a logon attempt\" do\n        allow(resource).to receive(:[]).with(eq(:name).or(eq(:password))).and_return(nil)\n        expect(Puppet::Util::Windows::User).not_to receive(:logon_user)\n        provider.password\n      end\n    end\n  end\n\n  describe \"when managing groups\" do\n    it 'should return the list of groups as an array of strings' do\n      allow(provider.user).to receive(:groups).and_return(nil)\n      groups = {'group1' => nil, 'group2' => nil, 'group3' => nil}\n      expect(Puppet::Util::Windows::ADSI::Group).to receive(:name_sid_hash).and_return(groups)\n\n      expect(provider.groups).to eq(groups.keys)\n    end\n\n    it \"should return an empty array if there are no groups\" do\n      allow(provider.user).to receive(:groups).and_return([])\n\n      expect(provider.groups).to eq([])\n    end\n\n    it 'should be able to add a user to a set of groups' do\n      resource[:membership] = :minimum\n      expect(provider.user).to receive(:set_groups).with('group1,group2', true)\n\n      provider.groups = 'group1,group2'\n\n      resource[:membership] = :inclusive\n      expect(provider.user).to receive(:set_groups).with('group1,group2', false)\n\n      provider.groups = 'group1,group2'\n    end\n  end\n\n  describe \"when setting roles\" do\n    context \"when role_membership => minimum\" do\n      before :each do\n        resource[:role_membership] = :minimum\n      end\n\n      it \"should set the given role when user has no roles\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('')\n\n        expect(Puppet::Util::Windows::User).to receive(:set_rights).with('testuser', ['givenRole1']).and_return(nil)\n        provider.roles = 'givenRole1'\n      end\n\n      it \"should set only the misssing role when user already has other roles\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('givenRole1')\n\n        expect(Puppet::Util::Windows::User).to receive(:set_rights).with('testuser', ['givenRole2']).and_return(nil)\n        provider.roles = 'givenRole1,givenRole2'\n      end\n\n      it \"should never remove any roles\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('givenRole1')\n        allow(Puppet::Util::Windows::User).to receive(:set_rights).and_return(nil)\n\n        expect(Puppet::Util::Windows::User).not_to receive(:remove_rights)\n        provider.roles = 'givenRole1,givenRole2'\n      end\n    end\n\n    context \"when role_membership => inclusive\" do\n      before :each do\n        resource[:role_membership] = :inclusive\n      end\n\n      it \"should remove the unwanted role\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('givenRole1,givenRole2')\n\n        expect(Puppet::Util::Windows::User).to receive(:remove_rights).with('testuser', ['givenRole2']).and_return(nil)\n        provider.roles = 'givenRole1'\n      end\n\n      it \"should add the missing role and remove the unwanted one\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('givenRole1,givenRole2')\n\n        expect(Puppet::Util::Windows::User).to receive(:set_rights).with('testuser', ['givenRole3']).and_return(nil)\n        expect(Puppet::Util::Windows::User).to receive(:remove_rights).with('testuser', ['givenRole2']).and_return(nil)\n        provider.roles = 'givenRole1,givenRole3'\n      end\n\n      it \"should not set any roles when the user already has given role\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('givenRole1,givenRole2')\n        allow(Puppet::Util::Windows::User).to receive(:remove_rights).with('testuser', ['givenRole2']).and_return(nil)\n\n        expect(Puppet::Util::Windows::User).not_to receive(:set_rights)\n        provider.roles = 'givenRole1'\n      end\n\n      it \"should set the given role when user has no roles\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('')\n\n        expect(Puppet::Util::Windows::User).to receive(:set_rights).with('testuser', ['givenRole1']).and_return(nil)\n        provider.roles = 'givenRole1'\n      end\n\n      it \"should not remove any roles when user has no roles\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('')\n        allow(Puppet::Util::Windows::User).to receive(:set_rights).with('testuser', ['givenRole1']).and_return(nil)\n\n        expect(Puppet::Util::Windows::User).not_to receive(:remove_rights)\n        provider.roles = 'givenRole1'\n      end\n\n      it \"should remove all roles when none given\" do\n        allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('givenRole1,givenRole2')\n\n        expect(Puppet::Util::Windows::User).not_to receive(:set_rights)\n        expect(Puppet::Util::Windows::User).to receive(:remove_rights).with('testuser', ['givenRole1', 'givenRole2']).and_return(nil)\n        provider.roles = ''\n      end\n    end\n  end\n\n  describe \"#groups_insync?\" do\n    let(:group1) { double(:account => 'group1', :domain => '.', :sid => 'group1sid') }\n    let(:group2) { double(:account => 'group2', :domain => '.', :sid => 'group2sid') }\n    let(:group3) { double(:account => 'group3', :domain => '.', :sid => 'group3sid') }\n\n    before :each do\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('group1', any_args).and_return(group1)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('group2', any_args).and_return(group2)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('group3', any_args).and_return(group3)\n    end\n\n    it \"should return true for same lists of members\" do\n      expect(provider.groups_insync?(['group1', 'group2'], ['group1', 'group2'])).to be_truthy\n    end\n\n    it \"should return true for same lists of unordered members\" do\n      expect(provider.groups_insync?(['group1', 'group2'], ['group2', 'group1'])).to be_truthy\n    end\n\n    it \"should return true for same lists of members irrespective of duplicates\" do\n      expect(provider.groups_insync?(['group1', 'group2', 'group2'], ['group2', 'group1', 'group1'])).to be_truthy\n    end\n\n    it \"should return true when current group(s) and should group(s) are empty lists\" do\n      expect(provider.groups_insync?([], [])).to be_truthy\n    end\n\n    it \"should return true when current groups is empty and should groups is nil\" do\n      expect(provider.groups_insync?([], nil)).to be_truthy\n    end\n\n    context \"when membership => inclusive\" do\n      before :each do\n        resource[:membership] = :inclusive\n      end\n\n      it \"should return true when current and should contain the same groups in a different order\" do\n        expect(provider.groups_insync?(['group1', 'group2', 'group3'], ['group3', 'group1', 'group2'])).to be_truthy\n      end\n\n      it \"should return false when current contains different groups than should\" do\n        expect(provider.groups_insync?(['group1'], ['group2'])).to be_falsey\n      end\n\n      it \"should return false when current is nil\" do\n        expect(provider.groups_insync?(nil, ['group2'])).to be_falsey\n      end\n\n      it \"should return false when should is nil\" do\n        expect(provider.groups_insync?(['group1'], nil)).to be_falsey\n      end\n\n      it \"should return false when current contains members and should is empty\" do\n        expect(provider.groups_insync?(['group1'], [])).to be_falsey\n      end\n\n      it \"should return false when current is empty and should contains members\" do\n        expect(provider.groups_insync?([], ['group2'])).to be_falsey\n      end\n\n      it \"should return false when should groups(s) are not the only items in the current\" do\n        expect(provider.groups_insync?(['group1', 'group2'], ['group1'])).to be_falsey\n      end\n\n      it \"should return false when current group(s) is not empty and should is an empty list\" do\n        expect(provider.groups_insync?(['group1','group2'], [])).to be_falsey\n      end\n    end\n\n    context \"when membership => minimum\" do\n      before :each do\n        # this is also the default\n        resource[:membership] = :minimum\n      end\n\n      it \"should return false when current contains different groups than should\" do\n        expect(provider.groups_insync?(['group1'], ['group2'])).to be_falsey\n      end\n\n      it \"should return false when current is nil\" do\n        expect(provider.groups_insync?(nil, ['group2'])).to be_falsey\n      end\n\n      it \"should return true when should is nil\" do\n        expect(provider.groups_insync?(['group1'], nil)).to be_truthy\n      end\n\n      it \"should return true when current contains members and should is empty\" do\n        expect(provider.groups_insync?(['group1'], [])).to be_truthy\n      end\n\n      it \"should return false when current is empty and should contains members\" do\n        expect(provider.groups_insync?([], ['group2'])).to be_falsey\n      end\n\n      it \"should return true when current group(s) contains at least the should list\" do\n        expect(provider.groups_insync?(['group1','group2'], ['group1'])).to be_truthy\n      end\n\n      it \"should return true when current group(s) is not empty and should is an empty list\" do\n        expect(provider.groups_insync?(['group1','group2'], [])).to be_truthy\n      end\n\n      it \"should return true when current group(s) contains at least the should list, even unordered\" do\n        expect(provider.groups_insync?(['group3','group1','group2'], ['group2','group1'])).to be_truthy\n      end\n    end\n  end\n\n  describe \"when creating a user\" do\n    it \"should create the user on the system and set its other properties\" do\n      resource[:groups]     = ['group1', 'group2']\n      resource[:membership] = :inclusive\n      resource[:comment]    = 'a test user'\n      resource[:home]       = 'C:\\Users\\testuser'\n\n      user = double('user')\n      expect(Puppet::Util::Windows::ADSI::User).to receive(:create).with('testuser').and_return(user)\n\n      allow(user).to receive(:groups).and_return(['group2', 'group3'])\n\n      expect(user).to receive(:password=).ordered\n      expect(user).to receive(:commit).ordered\n      expect(user).to receive(:set_groups).with('group1,group2', false).ordered\n      expect(user).to receive(:[]=).with('Description', 'a test user')\n      expect(user).to receive(:[]=).with('HomeDirectory', 'C:\\Users\\testuser')\n\n      provider.create\n    end\n\n    it \"should load the profile if managehome is set\" do\n      resource[:password] = '0xDeadBeef'\n      resource[:managehome] = true\n\n      user = double('user')\n      allow(user).to receive(:password=)\n      allow(user).to receive(:commit)\n      allow(user).to receive(:[]=)\n      expect(Puppet::Util::Windows::ADSI::User).to receive(:create).with('testuser').and_return(user)\n      expect(Puppet::Util::Windows::User).to receive(:load_profile).with('testuser', '0xDeadBeef')\n\n      provider.create\n    end\n\n    it \"should set a user's password\" do\n      expect(provider.user).to receive(:disabled?).and_return(false)\n      expect(provider.user).to receive(:locked_out?).and_return(false)\n      expect(provider.user).to receive(:expired?).and_return(false)\n      expect(provider.user).to receive(:password=).with('plaintextbad')\n\n      provider.password = \"plaintextbad\"\n    end\n\n    it \"should test a valid user password\" do\n      resource[:password] = 'plaintext'\n      expect(provider.user).to receive(:password_is?).with('plaintext').and_return(true)\n\n      expect(provider.password).to eq('plaintext')\n\n    end\n\n    it \"should test a bad user password\" do\n      resource[:password] = 'plaintext'\n      expect(provider.user).to receive(:password_is?).with('plaintext').and_return(false)\n\n      expect(provider.password).to be_nil\n    end\n\n    it \"should test a blank user password\" do\n      resource[:password] = ''\n      expect(provider.user).to receive(:password_is?).with('').and_return(true)\n\n      expect(provider.password).to eq('')\n    end\n\n    it 'should not create a user if a group by the same name exists' do\n      expect(Puppet::Util::Windows::ADSI::User).to receive(:create).with('testuser').and_raise(Puppet::Error.new(\"Cannot create user if group 'testuser' exists.\"))\n      expect{ provider.create }.to raise_error( Puppet::Error,\n        /Cannot create user if group 'testuser' exists./ )\n    end\n\n    it \"should fail with an actionable message when trying to create an active directory user\" do\n      resource[:name] = 'DOMAIN\\testdomainuser'\n      expect(Puppet::Util::Windows::ADSI::Group).to receive(:exists?).with(resource[:name]).and_return(false)\n      expect(connection).to receive(:Create)\n      expect(connection).to receive(:Get).with('UserFlags')\n      expect(connection).to receive(:Put).with('UserFlags', true)\n      expect(connection).to receive(:SetInfo).and_raise(WIN32OLERuntimeError.new(\"(in OLE method `SetInfo': )\\n    OLE error code:8007089A in Active Directory\\n      The specified username is invalid.\\r\\n\\n    HRESULT error code:0x80020009\\n      Exception occurred.\"))\n\n      expect{ provider.create }.to raise_error(Puppet::Error)\n    end\n  end\n\n  it 'should be able to test whether a user exists' do\n    allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).and_return(nil)\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(double('connection', :Class => 'User'))\n    expect(provider).to be_exists\n\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(nil)\n    expect(provider).not_to be_exists\n  end\n\n  it 'should be able to delete a user' do\n    expect(connection).to receive(:Delete).with('user', 'testuser')\n\n    provider.delete\n  end\n\n  it 'should not run commit on a deleted user' do\n    expect(connection).to receive(:Delete).with('user', 'testuser')\n    expect(connection).not_to receive(:SetInfo)\n\n    provider.delete\n    provider.flush\n  end\n\n  it 'should delete the profile if managehome is set' do\n    resource[:managehome] = true\n\n    sid = 'S-A-B-C'\n    expect(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('testuser').and_return(sid)\n    expect(Puppet::Util::Windows::ADSI::UserProfile).to receive(:delete).with(sid)\n    expect(connection).to receive(:Delete).with('user', 'testuser')\n\n    provider.delete\n  end\n\n  it \"should commit the user when flushed\" do\n    expect(provider.user).to receive(:commit)\n\n    provider.flush\n  end\n\n  it \"should return the user's SID as uid\" do\n    expect(Puppet::Util::Windows::SID).to receive(:name_to_sid).with('testuser').and_return('S-1-5-21-1362942247-2130103807-3279964888-1111')\n\n    expect(provider.uid).to eq('S-1-5-21-1362942247-2130103807-3279964888-1111')\n  end\n\n  it \"should fail when trying to manage the uid property\" do\n    expect(provider).to receive(:fail).with(/uid is read-only/)\n    provider.send(:uid=, 500)\n  end\n\n  [:gid, :shell].each do |prop|\n    it \"should fail when trying to manage the #{prop} property\" do\n      expect(provider).to receive(:fail).with(/No support for managing property #{prop}/)\n      provider.send(\"#{prop}=\", 'foo')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/provider_spec.rb",
    "content": "require 'spec_helper'\n\ndef existing_command\n  Puppet::Util::Platform.windows? ? \"cmd\" : \"echo\"\nend\n\ndescribe Puppet::Provider do\n  before :each do\n    Puppet::Type.newtype(:test) do\n      newparam(:name) { isnamevar }\n    end\n  end\n\n  after :each do\n    Puppet::Type.type(:test).provider_hash.clear\n    Puppet::Type.rmtype(:test)\n  end\n\n  let :type do Puppet::Type.type(:test) end\n  let :provider do type.provide(:default) {} end\n\n  subject { provider }\n\n  describe \"has command\" do\n    it \"installs a method to run the command specified by the path\" do\n      echo_command = expect_command_executed(:echo, \"/bin/echo\", \"an argument\")\n      allow_creation_of(echo_command)\n\n      provider = provider_of do\n        has_command(:echo, \"/bin/echo\")\n      end\n\n      provider.echo(\"an argument\")\n    end\n\n    it \"installs a command that is run with a given environment\" do\n      echo_command = expect_command_executed(:echo, \"/bin/echo\", \"an argument\")\n      allow_creation_of(echo_command, {\n        :EV => \"value\",\n        :OTHER => \"different\"\n      })\n\n      provider = provider_of do\n        has_command(:echo, \"/bin/echo\") do\n          environment :EV => \"value\", :OTHER => \"different\"\n        end\n      end\n\n      provider.echo(\"an argument\")\n    end\n\n    it \"is required by default\" do\n      provider = provider_of do\n        has_command(:does_not_exist, \"/does/not/exist\")\n      end\n\n      expect(provider).not_to be_suitable\n    end\n\n    it \"is required by default\" do\n      provider = provider_of do\n        has_command(:does_exist, File.expand_path(\"/exists/somewhere\"))\n      end\n\n      file_exists_and_is_executable(File.expand_path(\"/exists/somewhere\"))\n\n      expect(provider).to be_suitable\n    end\n\n    it \"can be specified as optional\" do\n      provider = provider_of do\n        has_command(:does_not_exist, \"/does/not/exist\") do\n          is_optional\n        end\n      end\n\n      expect(provider).to be_suitable\n    end\n  end\n\n  describe \"has required commands\" do\n    it \"installs methods to run executables by path\" do\n      echo_command = expect_command_executed(:echo, \"/bin/echo\", \"an argument\")\n      ls_command = expect_command_executed(:ls, \"/bin/ls\")\n\n      allow_creation_of(echo_command)\n      allow_creation_of(ls_command)\n\n      provider = provider_of do\n        commands :echo => \"/bin/echo\", :ls => \"/bin/ls\"\n      end\n\n      provider.echo(\"an argument\")\n      provider.ls\n    end\n\n    it \"allows the provider to be suitable if the executable is present\" do\n      provider = provider_of do\n        commands :always_exists => File.expand_path(\"/this/command/exists\")\n      end\n\n      file_exists_and_is_executable(File.expand_path(\"/this/command/exists\"))\n\n      expect(provider).to be_suitable\n    end\n\n    it \"does not allow the provider to be suitable if the executable is not present\" do\n      provider = provider_of do\n        commands :does_not_exist => \"/this/command/does/not/exist\"\n      end\n\n      expect(provider).not_to be_suitable\n    end\n  end\n\n  describe \"has optional commands\" do\n    it \"installs methods to run executables\" do\n      echo_command = expect_command_executed(:echo, \"/bin/echo\", \"an argument\")\n      ls_command = expect_command_executed(:ls, \"/bin/ls\")\n\n      allow_creation_of(echo_command)\n      allow_creation_of(ls_command)\n\n      provider = provider_of do\n        optional_commands :echo => \"/bin/echo\", :ls => \"/bin/ls\"\n      end\n\n      provider.echo(\"an argument\")\n      provider.ls\n    end\n\n    it \"allows the provider to be suitable even if the executable is not present\" do\n      provider = provider_of do\n        optional_commands :does_not_exist => \"/this/command/does/not/exist\"\n      end\n\n      expect(provider).to be_suitable\n    end\n  end\n\n  it \"should have a specifity class method\" do\n    expect(Puppet::Provider).to respond_to(:specificity)\n  end\n\n  it \"should be Comparable\" do\n    res = Puppet::Type.type(:notify).new(:name => \"res\")\n\n    # Normally I wouldn't like the stubs, but the only way to name a class\n    # otherwise is to assign it to a constant, and that hurts more here in\n    # testing world. --daniel 2012-01-29\n    a = Class.new(Puppet::Provider).new(res)\n    allow(a.class).to receive(:name).and_return(\"Puppet::Provider::Notify::A\")\n\n    b = Class.new(Puppet::Provider).new(res)\n    allow(b.class).to receive(:name).and_return(\"Puppet::Provider::Notify::B\")\n\n    c = Class.new(Puppet::Provider).new(res)\n    allow(c.class).to receive(:name).and_return(\"Puppet::Provider::Notify::C\")\n\n    [[a, b, c], [a, c, b], [b, a, c], [b, c, a], [c, a, b], [c, b, a]].each do |this|\n      expect(this.sort).to eq([a, b, c])\n    end\n\n    expect(a).to be < b\n    expect(a).to be < c\n    expect(b).to be > a\n    expect(b).to be < c\n    expect(c).to be > a\n    expect(c).to be > b\n\n    [a, b, c].each {|x| expect(a).to be <= x }\n    [a, b, c].each {|x| expect(c).to be >= x }\n\n    expect(b).to be_between(a, c)\n  end\n\n  context \"when creating instances\" do\n    context \"with a resource\" do\n      let :resource do type.new(:name => \"fred\") end\n      subject { provider.new(resource) }\n\n      it \"should set the resource correctly\" do\n        expect(subject.resource).to equal resource\n      end\n\n      it \"should set the name from the resource\" do\n        expect(subject.name).to eq(resource.name)\n      end\n    end\n\n    context \"with a hash\" do\n      subject { provider.new(:name => \"fred\") }\n\n      it \"should set the name\" do\n        expect(subject.name).to eq(\"fred\")\n      end\n\n      it \"should not have a resource\" do expect(subject.resource).to be_nil end\n    end\n\n    context \"with no arguments\" do\n      subject { provider.new }\n\n      it \"should raise an internal error if asked for the name\" do\n        expect { subject.name }.to raise_error Puppet::DevError\n      end\n\n      it \"should not have a resource\" do expect(subject.resource).to be_nil end\n    end\n  end\n\n  context \"when confining\" do\n    it \"should be suitable by default\" do\n      expect(subject).to be_suitable\n    end\n\n    it \"should not be default by default\" do\n      expect(subject).not_to be_default\n    end\n\n    { { :true => true } => true,\n      { :true => false } => false,\n      { :false => false } => true,\n      { :false => true } => false,\n      { 'os.name' => Puppet.runtime[:facter].value('os.name') } => true,\n      { 'os.name' => :yayness } => false,\n      { :nothing => :yayness } => false,\n      { :exists => Puppet::Util.which(existing_command) } => true,\n      { :exists => \"/this/file/does/not/exist\" } => false,\n      { :true => true, :exists => Puppet::Util.which(existing_command) } => true,\n      { :true => true, :exists => \"/this/file/does/not/exist\" } => false,\n      { 'os.name' => Puppet.runtime[:facter].value('os.name'),\n        :exists => Puppet::Util.which(existing_command) } => true,\n      { 'os.name' => :yayness,\n        :exists => Puppet::Util.which(existing_command) } => false,\n      { 'os.name' => Puppet.runtime[:facter].value('os.name'),\n        :exists => \"/this/file/does/not/exist\" } => false,\n      { 'os.name' => :yayness,\n        :exists => \"/this/file/does/not/exist\" } => false,\n    }.each do |confines, result|\n      it \"should confine #{confines.inspect} to #{result}\" do\n        confines.each {|test, value| subject.confine test => value }\n        if result\n          expect(subject).to be_suitable\n        else\n          expect(subject).to_not be_suitable\n        end\n      end\n    end\n\n    it \"should not override a confine even if a second has the same type\" do\n      subject.confine :true => false\n      expect(subject).not_to be_suitable\n\n      subject.confine :true => true\n      expect(subject).not_to be_suitable\n    end\n\n    it \"should not be suitable if any confine fails\" do\n      subject.confine :true => false\n      expect(subject).not_to be_suitable\n\n      10.times do\n        subject.confine :true => true\n        expect(subject).not_to be_suitable\n      end\n    end\n\n  end\n\n  context \"default providers\" do\n    let :os do Puppet.runtime[:facter].value('os.name') end\n\n    it { is_expected.to respond_to :specificity }\n\n    it \"should find the default provider\" do\n      type.provide(:nondefault) {}\n      subject.defaultfor 'os.name' => os\n      expect(subject.name).to eq(type.defaultprovider.name)\n    end\n\n    describe \"regex matches\" do\n      it \"should match a singular regex\" do\n        expect(Facter).to receive(:value).with('os.family').at_least(:once).and_return(\"solaris\")\n\n        one = type.provide(:one) do\n          defaultfor 'os.family' => /solaris/\n        end\n\n        expect(one).to be_default\n      end\n\n      it \"should not match a non-matching regex \" do\n        expect(Facter).to receive(:value).with('os.family').at_least(:once).and_return(\"redhat\")\n\n        one = type.provide(:one) do\n          defaultfor 'os.family' => /solaris/\n        end\n\n        expect(one).to_not be_default\n      end\n\n      it \"should allow a mix of regex and string\" do\n\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(\"fedora\")\n        expect(Facter).to receive(:value).with('os.release.major').at_least(:once).and_return(\"24\")\n\n        one = type.provide(:one) do\n          defaultfor 'os.name' => \"fedora\", 'os.release.major' => /^2[2-9]$/\n        end\n\n        two = type.provide(:two) do\n          defaultfor 'os.name' => /fedora/, 'os.release.major' => '24'\n        end\n\n        expect(one).to be_default\n        expect(two).to be_default\n      end\n    end\n\n    describe \"when there are multiple defaultfor's of equal specificity\" do\n      before :each do\n        subject.defaultfor 'os.name' => :os1\n        subject.defaultfor 'os.name' => :os2\n      end\n\n      let(:alternate) { type.provide(:alternate) {} }\n\n      it \"should be default for the first defaultfor\" do\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(:os1)\n\n        expect(provider).to be_default\n        expect(alternate).not_to be_default\n      end\n\n      it \"should be default for the last defaultfor\" do\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(:os2)\n\n        expect(provider).to be_default\n        expect(alternate).not_to be_default\n      end\n    end\n\n    describe \"when there are multiple defaultfor's with different specificity\" do\n      before :each do\n        subject.defaultfor 'os.name' => :os1\n        subject.defaultfor 'os.name' => :os2, 'os.release.major' => \"42\"\n        subject.defaultfor 'os.name' => :os3, 'os.release.major' => /^4[2-9]$/\n      end\n\n      let(:alternate) { type.provide(:alternate) {} }\n\n      it \"should be default for a more specific, but matching, defaultfor\" do\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(:os2)\n        expect(Facter).to receive(:value).with('os.release.major').at_least(:once).and_return(\"42\")\n\n        expect(provider).to be_default\n        expect(alternate).not_to be_default\n      end\n\n      it \"should be default for a more specific, but matching, defaultfor with regex\" do\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(:os3)\n        expect(Facter).to receive(:value).with('os.release.major').at_least(:once).and_return(\"42\")\n\n        expect(provider).to be_default\n        expect(alternate).not_to be_default\n      end\n\n      it \"should be default for a less specific, but matching, defaultfor\" do\n        expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(:os1)\n\n        expect(provider).to be_default\n        expect(alternate).not_to be_default\n      end\n    end\n\n    it \"should consider any true value enough to be default\" do\n      alternate = type.provide(:alternate) {}\n\n      subject.defaultfor 'os.name' => [:one, :two, :three, os]\n      expect(subject.name).to eq(type.defaultprovider.name)\n\n      expect(subject).to be_default\n      expect(alternate).not_to be_default\n    end\n\n    it \"should not be default if the defaultfor doesn't match\" do\n      expect(subject).not_to be_default\n      subject.defaultfor 'os.name' => :one\n      expect(subject).not_to be_default\n    end\n\n    it \"should not be default if the notdefaultfor does match\" do\n      expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(\"fedora\")\n      expect(Facter).to receive(:value).with('os.release.major').at_least(:once).and_return(\"24\")\n\n      one = type.provide(:one) do\n        defaultfor 'os.name' => \"fedora\"\n        notdefaultfor 'os.name' => \"fedora\", 'os.release.major' => 24\n      end\n\n      expect(one).not_to be_default\n    end\n\n    it \"should be default if the notdefaultfor doesn't match\" do\n      expect(Facter).to receive(:value).with('os.name').at_least(:once).and_return(\"fedora\")\n      expect(Facter).to receive(:value).with('os.release.major').at_least(:once).and_return(\"24\")\n\n      one = type.provide(:one) do\n        defaultfor 'os.name' => \"fedora\"\n        notdefaultfor 'os.name' => \"fedora\", 'os.release.major' => 42\n      end\n\n      expect(one).to be_default\n    end\n\n    # Key: spec has 4 required and 1 optional part:\n    # one-defaultfor, one-notdefaultfor, two-defaultfor, two-notdefaultfor\n    # d = defaultfor, n = notdefaultfor,\n    # d2 - two clauses in defaultfor constraint,\n    # ! = constraint exists but doesn't match\n    # none = no constraint\n    # d+/!d+/none+ - provider class has deeper inheritence\n\n    context \"defaultfor/notdefaultfor configurable tests\" do\n      [\n        # Two default? group - ties go to first to register\n        %w{d    none d     none pickone},\n        # Two default? group - second is selected for specificity\n        %w{d    !n   d2     !n         },\n        %w{d    !n   d2     none       },\n        # Two default? group - second is selected for inheritence\n        %w{d    !n   d+     !n         },\n        %w{d    !n   d+     none       },\n        # One default? group - second (only default?) always is selected\n        %w{!d   !n   d     none        },\n        %w{!d   !n   d     !n          },\n        %w{!d   n    d     none        },\n        %w{!d   n    d     !n          },\n        %w{d    n    d     none        },\n        %w{d    n    d     !n          },\n        # No default? group:\n        %w{d    !n   d     !n   pickone},\n        %w{d    !n   d     none pickone},\n        %w{!d   !n   !d    !n   pickone},\n        %w{!d   !n   !d    none pickone},\n        %w{!d   none !d    none pickone},\n        %w{none !n   none  !n   pickone},\n        %w{none none none  none pickone},\n        # No default? but deeper class inheritence group:\n        %w{!d   !n   !d+   !n          },\n        %w{!d   !n   !d+   none        },\n        %w{!d   none !d+   none        },\n        %w{none !n   none+ !n          },\n        %w{none none none+ none        },\n      ].each do |thisspec|\n\n        defaultforspec = {\n          :one => {},\n          :two => {},\n          :expect_one => false #Default expectation is to expect provider two for these tests\n        }\n\n        fail \"Inheritence not supported on first provider\" if thisspec[0].end_with?('+')\n\n        case thisspec[0] # First provider defaultfor spec\n        when 'd'\n          defaultforspec[:one][:defaultfor] = true\n        when '!d'\n          defaultforspec[:one][:defaultfor] = false\n        when 'none'\n          # Do not include a defaultfor constraint\n        else\n          fail \"Did not understand first spec: %{spec}\" % { spec: thisspec[0] }\n        end\n\n        case thisspec[1] # First provider notdefaultfor spec\n        when 'n'\n          defaultforspec[:one][:notdefaultfor] = true\n        when '!n'\n          defaultforspec[:one][:notdefaultfor] = false\n        when 'none'\n          # Do not include a notdefaultfor constraint\n        else\n          fail \"Did not understand second spec: %{spec}\" % { spec: thisspec[1] }\n        end\n\n        if thisspec[2].end_with?('+') then # d+ !d+ none+\n          defaultforspec[:two][:derived] = true\n          thisspec[2] = thisspec[2][0 .. -2]\n        end\n\n        case thisspec[2]\n        when 'd'\n          defaultforspec[:two][:defaultfor] = true\n        when 'd2'\n          defaultforspec[:two][:extradefaultfor] = true\n        when '!d'\n          defaultforspec[:two][:defaultfor] = false\n        when 'none'\n          # Do not include a defaultfor constraint\n        else\n          fail \"Did not understand third spec: %{spec}\" % { spec: thisspec[2] }\n        end\n\n        case thisspec[3] # Second provider notdefaultfor spec\n        when 'n'\n          defaultforspec[:two][:notdefaultfor] = true\n        when '!n'\n          defaultforspec[:two][:notdefaultfor] = false\n        when 'none'\n          # Do not include a notdefaultfor constraint\n        else\n          fail \"Did not understand fourth spec: %{spec}\" % { spec: thisspec[3] }\n        end\n\n        if thisspec.length == 5 && thisspec[4] == \"pickone\" then\n          defaultforspec[:expect_one] = true\n        end\n\n        it \"with the specification: %{spec}\" % { spec: thisspec.join(', ') } do\n          allow(Facter).to receive(:value).with('os.family').and_return(\"redhat\")\n          allow(Facter).to receive(:value).with('os.name').and_return(\"centos\")\n          allow(Facter).to receive(:value).with('os.release.full').and_return(\"27\")\n\n          one = type.provide(:one) do\n            if defaultforspec[:one].key?(:defaultfor)\n              defaultfor    'os.family'               => \"redhat\" if  defaultforspec[:one][:defaultfor]\n              defaultfor    'os.family'               => \"ubuntu\" if !defaultforspec[:one][:defaultfor]\n            end\n            if defaultforspec[:one].key?(:notdefaultfor)\n              notdefaultfor 'os.name'        => \"centos\" if  defaultforspec[:one][:notdefaultfor]\n              notdefaultfor 'os.name'        => \"ubuntu\" if !defaultforspec[:one][:notdefaultfor]\n            end\n          end\n\n          provider_options = {}\n          provider_options[:parent] = one if defaultforspec[:two][:derived] # :two inherits from one, if spec'd\n          two = type.provide(:two, provider_options) do\n            if defaultforspec[:two].key?(:defaultfor) || defaultforspec[:two].key?(:extradefaultfor)\n              defaultfor    'os.family'               => \"redhat\" if  defaultforspec[:two][:defaultfor]\n              defaultfor    'os.family'               => \"redhat\",#   defaultforspec[:two][:extradefaultfor] has two parts\n                            'os.name'        => \"centos\" if  defaultforspec[:two][:extradefaultfor]\n              defaultfor    'os.family'               => \"ubuntu\" if !defaultforspec[:two][:defaultfor]\n            end\n            if defaultforspec[:two].key?(:notdefaultfor)\n              notdefaultfor 'os.release.full' => \"27\" if  defaultforspec[:two][:notdefaultfor]\n              notdefaultfor 'os.release.full' => \"99\" if !defaultforspec[:two][:notdefaultfor]\n            end\n          end\n\n          if defaultforspec[:expect_one] then\n            expect(Puppet).to receive(:warning).with(/Found multiple default providers/)\n            expect(type.defaultprovider).to eq(one)\n          else\n            expect(type.defaultprovider).to eq(two)\n          end\n        end\n      end\n    end\n\n    describe \"using a :feature key\" do\n      before :each do\n        Puppet.features.add(:yay) do true end\n        Puppet.features.add(:boo) do false end\n      end\n\n      it \"is default for an available feature\" do\n        one = type.provide(:one) do\n          defaultfor :feature => :yay\n        end\n\n        expect(one).to be_default\n      end\n\n      it \"is not default for a missing feature\" do\n        two = type.provide(:two) do\n          defaultfor :feature => :boo\n        end\n\n        expect(two).not_to be_default\n      end\n    end\n  end\n\n  context \"provider commands\" do\n    it \"should raise for unknown commands\" do\n      expect { subject.command(:something) }.to raise_error(Puppet::DevError)\n    end\n\n    it \"should handle command inheritance\" do\n      parent = type.provide(\"parent\")\n      child  = type.provide(\"child\", :parent => parent.name)\n\n      command = Puppet::Util.which('sh') || Puppet::Util.which('cmd.exe')\n      parent.commands :sh => command\n\n      expect(Puppet::FileSystem.exist?(parent.command(:sh))).to be_truthy\n      expect(parent.command(:sh)).to match(/#{Regexp.escape(command)}$/)\n\n      expect(Puppet::FileSystem.exist?(child.command(:sh))).to be_truthy\n      expect(child.command(:sh)).to match(/#{Regexp.escape(command)}$/)\n    end\n\n    it \"#1197: should find commands added in the same run\" do\n      subject.commands :testing => \"puppet-bug-1197\"\n      expect(subject.command(:testing)).to be_nil\n\n      allow(subject).to receive(:which).with(\"puppet-bug-1197\").and_return(\"/puppet-bug-1197\")\n      expect(subject.command(:testing)).to eq(\"/puppet-bug-1197\")\n\n      # Ideally, we would also test that `suitable?` returned the right thing\n      # here, but it is impossible to get access to the methods that do that\n      # without digging way down into the implementation. --daniel 2012-03-20\n    end\n\n    context \"with optional commands\" do\n      before :each do\n        subject.optional_commands :cmd => \"/no/such/binary/exists\"\n      end\n\n      it { is_expected.to be_suitable }\n\n      it \"should not be suitable if a mandatory command is also missing\" do\n        subject.commands :foo => \"/no/such/binary/either\"\n        expect(subject).not_to be_suitable\n      end\n\n      it \"should define a wrapper for the command\" do\n        expect(subject).to respond_to(:cmd)\n      end\n\n      it \"should return nil if the command is requested\" do\n        expect(subject.command(:cmd)).to be_nil\n      end\n\n      it \"should raise if the command is invoked\" do\n        expect { subject.cmd }.to raise_error(Puppet::Error, /Command cmd is missing/)\n      end\n    end\n  end\n\n  context \"execution\" do\n    before :each do\n      expect(Puppet).not_to receive(:deprecation_warning)\n    end\n\n    it \"delegates instance execute to Puppet::Util::Execution\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with(\"a_command\", { :option => \"value\" })\n\n      provider.new.execute(\"a_command\", { :option => \"value\" })\n    end\n\n    it \"delegates class execute to Puppet::Util::Execution\" do\n      expect(Puppet::Util::Execution).to receive(:execute).with(\"a_command\", { :option => \"value\" })\n\n      provider.execute(\"a_command\", { :option => \"value\" })\n    end\n\n    it \"delegates instance execpipe to Puppet::Util::Execution\" do\n      allow(Puppet::Util::Execution).to receive(:execpipe).with(\"a_command\", true).and_yield('some output')\n\n      expect { |b| provider.new.execpipe(\"a_command\", true, &b) }.to yield_with_args('some output')\n    end\n\n    it \"delegates class execpipe to Puppet::Util::Execution\" do\n      allow(Puppet::Util::Execution).to receive(:execpipe).with(\"a_command\", true).and_yield('some output')\n\n      expect { |b| provider.execpipe(\"a_command\", true, &b) }.to yield_with_args('some output')\n    end\n  end\n\n  context \"mk_resource_methods\" do\n    before :each do\n      type.newproperty(:prop)\n      type.newparam(:param)\n      provider.mk_resource_methods\n    end\n\n    let(:instance) { provider.new(nil) }\n\n    it \"defaults to :absent\" do\n      expect(instance.prop).to eq(:absent)\n      expect(instance.param).to eq(:absent)\n    end\n\n    it \"should update when set\" do\n      instance.prop = 'hello'\n      instance.param = 'goodbye'\n\n      expect(instance.prop).to eq('hello')\n      expect(instance.param).to eq('goodbye')\n    end\n\n    it \"treats nil the same as absent\" do\n      instance.prop = \"value\"\n      instance.param = \"value\"\n\n      instance.prop = nil\n      instance.param = nil\n\n      expect(instance.prop).to eq(:absent)\n      expect(instance.param).to eq(:absent)\n    end\n\n    it \"preserves false as false\" do\n      instance.prop = false\n      instance.param = false\n\n      expect(instance.prop).to eq(false)\n      expect(instance.param).to eq(false)\n    end\n  end\n\n  context \"source\" do\n    it \"should default to the provider name\" do\n      expect(subject.source).to eq(:default)\n    end\n\n    it \"should default to the provider name for a child provider\" do\n      expect(type.provide(:sub, :parent => subject.name).source).to eq(:sub)\n    end\n\n    it \"should override if requested\" do\n      provider = type.provide(:sub, :parent => subject.name, :source => subject.source)\n      expect(provider.source).to eq(subject.source)\n    end\n\n    it \"should override to anything you want\" do\n      expect { subject.source = :banana }.to change { subject.source }.\n        from(:default).to(:banana)\n    end\n  end\n\n  context \"features\" do\n    before :each do\n      type.feature :numeric,   '', :methods => [:one, :two]\n      type.feature :alpha,     '', :methods => [:a, :b]\n      type.feature :nomethods, ''\n    end\n\n    { :no      => { :alpha => false, :numeric => false, :methods => [] },\n      :numeric => { :alpha => false, :numeric => true,  :methods => [:one, :two] },\n      :alpha   => { :alpha => true,  :numeric => false, :methods => [:a, :b] },\n      :all     => {\n        :alpha => true,  :numeric => true,\n        :methods => [:a, :b, :one, :two]\n      },\n      :alpha_and_partial   => {\n        :alpha => true, :numeric => false,\n        :methods => [:a, :b, :one]\n      },\n      :numeric_and_partial => {\n        :alpha => false, :numeric => true,\n        :methods => [:a, :one, :two]\n      },\n      :all_partial    => { :alpha => false, :numeric => false, :methods => [:a, :one] },\n      :other_and_none => { :alpha => false, :numeric => false, :methods => [:foo, :bar] },\n      :other_and_alpha => {\n        :alpha => true, :numeric => false,\n        :methods => [:foo, :bar, :a, :b]\n      },\n    }.each do |name, setup|\n      context \"with #{name.to_s.gsub('_', ' ')} features\" do\n        let :provider do\n          provider = type.provide(name)\n          setup[:methods].map do |method|\n            provider.send(:define_method, method) do true end\n          end\n          type.provider(name)\n        end\n\n        context \"provider class\" do\n          subject { provider }\n\n          it { is_expected.to respond_to(:has_features) }\n          it { is_expected.to respond_to(:has_feature) }\n\n          it { is_expected.to respond_to(:nomethods?) }\n          it { is_expected.not_to be_nomethods }\n\n          it { is_expected.to respond_to(:numeric?) }\n          if setup[:numeric]\n            it { is_expected.to be_numeric }\n            it { is_expected.to be_satisfies(:numeric) }\n          else\n            it { is_expected.not_to be_numeric }\n            it { is_expected.not_to be_satisfies(:numeric) }\n          end\n\n          it { is_expected.to respond_to(:alpha?) }\n          if setup[:alpha]\n            it { is_expected.to be_alpha }\n            it { is_expected.to be_satisfies(:alpha) }\n          else\n            it { is_expected.not_to be_alpha }\n            it { is_expected.not_to be_satisfies(:alpha) }\n          end\n        end\n\n        context \"provider instance\" do\n          subject { provider.new }\n\n          it { is_expected.to respond_to(:numeric?) }\n          if setup[:numeric]\n            it { is_expected.to be_numeric }\n            it { is_expected.to be_satisfies(:numeric) }\n          else\n            it { is_expected.not_to be_numeric }\n            it { is_expected.not_to be_satisfies(:numeric) }\n          end\n\n          it { is_expected.to respond_to(:alpha?) }\n          if setup[:alpha]\n            it { is_expected.to be_alpha }\n            it { is_expected.to be_satisfies(:alpha) }\n          else\n            it { is_expected.not_to be_alpha }\n            it { is_expected.not_to be_satisfies(:alpha) }\n          end\n        end\n      end\n    end\n\n    context \"feature with no methods\" do\n      before :each do\n        type.feature :undemanding, ''\n      end\n\n      it { is_expected.to respond_to(:undemanding?) }\n\n      context \"when the feature is not declared\" do\n        it { is_expected.not_to be_undemanding }\n        it { is_expected.not_to be_satisfies(:undemanding) }\n      end\n\n      context \"when the feature is declared\" do\n        before :each do\n          subject.has_feature :undemanding\n        end\n\n        it { is_expected.to be_undemanding }\n        it { is_expected.to be_satisfies(:undemanding) }\n      end\n    end\n\n    context \"supports_parameter?\" do\n      before :each do\n        type.newparam(:no_feature)\n        type.newparam(:one_feature,  :required_features => :alpha)\n        type.newparam(:two_features, :required_features => [:alpha, :numeric])\n      end\n\n      let :providers do\n        {\n          :zero => type.provide(:zero),\n          :one  => type.provide(:one) do has_features :alpha end,\n          :two  => type.provide(:two) do has_features :alpha, :numeric end\n        }\n      end\n\n      { :zero => { :yes => [:no_feature], :no => [:one_feature, :two_features] },\n        :one  => { :yes => [:no_feature, :one_feature], :no => [:two_features] },\n        :two  => { :yes => [:no_feature, :one_feature, :two_features], :no => [] }\n      }.each do |name, data|\n        data[:yes].each do |param|\n          it \"should support #{param} with provider #{name}\" do\n            expect(providers[name]).to be_supports_parameter(param)\n          end\n        end\n\n        data[:no].each do |param|\n          it \"should not support #{param} with provider #{name}\" do\n            expect(providers[name]).not_to be_supports_parameter(param)\n          end\n        end\n      end\n    end\n  end\n\n  def provider_of(options = {}, &block)\n    type = Puppet::Type.newtype(:dummy) do\n      provide(:dummy, options, &block)\n    end\n\n    type.provider(:dummy)\n  end\n\n  def expect_command_executed(name, path, *args)\n    command = Puppet::Provider::Command.new(name, path, Puppet::Util, Puppet::Util::Execution)\n    args = [no_args] if args.empty?\n    expect(command).to receive(:execute).with(*args)\n    command\n  end\n\n  def allow_creation_of(command, environment = {})\n      allow(Puppet::Provider::Command).to receive(:new).with(command.name, command.executable, Puppet::Util, Puppet::Util::Execution, { :failonfail => true, :combine => true, :custom_environment => environment }).and_return(command)\n  end\n\n  def file_exists_and_is_executable(path)\n    expect(FileTest).to receive(:file?).with(path).and_return(true)\n    expect(FileTest).to receive(:executable?).with(path).and_return(true)\n  end\nend\n"
  },
  {
    "path": "spec/unit/puppet_pal_2pec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_pal'\n\ndescribe 'Puppet Pal' do\n  include PuppetSpec::Files\n\n  let(:testing_env) do\n    {\n      'pal_env' => {\n        'functions' => functions,\n        'lib' => { 'puppet' => lib_puppet },\n        'manifests' => manifests,\n        'modules' => modules,\n        'plans' => plans,\n        'tasks' => tasks,\n        'types' => types,\n      },\n      'other_env1' => { 'modules' => {} },\n      'other_env2' => { 'modules' => {} },\n    }\n  end\n\n  let(:functions) { {} }\n  let(:manifests) { {} }\n  let(:modules) { {} }\n  let(:plans) { {} }\n  let(:lib_puppet) { {} }\n  let(:tasks) { {} }\n  let(:types) { {} }\n\n  let(:environments_dir) { Puppet[:environmentpath] }\n\n  let(:testing_env_dir) do\n    dir_contained_in(environments_dir, testing_env)\n    env_dir = File.join(environments_dir, 'pal_env')\n    PuppetSpec::Files.record_tmp(env_dir)\n    PuppetSpec::Files.record_tmp(File.join(environments_dir, 'other_env1'))\n    PuppetSpec::Files.record_tmp(File.join(environments_dir, 'other_env2'))\n    env_dir\n  end\n\n  let(:modules_dir) { File.join(testing_env_dir, 'modules') }\n\n  # Without any facts - this speeds up the tests that do not require $facts to have any values\n  let(:node_facts) { Hash.new }\n\n  # TODO: to be used in examples for running in an existing env\n  #  let(:env) { Puppet::Node::Environment.create(:testing, [modules_dir]) }\n\n  context 'in general - without code in modules or env' do\n    let(:modulepath) { [] }\n\n    context 'deprecated PAL API methods work and' do\n      it '\"evaluate_script_string\" evaluates a code string in a given tmp environment' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n          ctx.evaluate_script_string('1+2+3')\n        end\n        expect(result).to eq(6)\n      end\n\n      it '\"evaluate_script_manifest\" evaluates a manifest file in a given tmp environment' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n          manifest = file_containing('testing.pp', \"1+2+3+4\")\n          ctx.evaluate_script_manifest(manifest)\n        end\n        expect(result).to eq(10)\n      end\n    end\n\n    context \"with a script compiler\" do\n      it 'errors if given both configured_by_env and manifest_file' do\n        expect {\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler(configured_by_env: true, manifest_file: 'undef.pp') {|c|  }\n          end\n        }.to raise_error(/manifest_file or code_string cannot be given when configured_by_env is true/)\n      end\n\n      it 'errors if given both configured_by_env and code_string' do\n        expect {\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler(configured_by_env: true, code_string: 'undef') {|c|  }\n          end\n        }.to raise_error(/manifest_file or code_string cannot be given when configured_by_env is true/)\n      end\n\n      context \"evaluate_string method\" do\n        it 'evaluates code string in a given tmp environment' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.evaluate_string('1+2+3') }\n          end\n          expect(result).to eq(6)\n        end\n\n        it 'can be evaluated more than once in a given tmp environment - each in fresh compiler' do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            expect(  ctx.with_script_compiler {|c| c.evaluate_string('$a = 1+2+3')}).to eq(6)\n            expect { ctx.with_script_compiler {|c| c.evaluate_string('$a') }}.to raise_error(/Unknown variable: 'a'/)\n          end\n        end\n\n        it 'instantiates definitions in the given code string' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n            pal.with_script_compiler do |compiler|\n              compiler.evaluate_string(<<-CODE)\n                function run_me() { \"worked1\" }\n                run_me()\n                CODE\n            end\n          end\n          expect(result).to eq('worked1')\n        end\n      end\n\n      context \"evaluate_file method\" do\n        it 'evaluates a manifest file in a given tmp environment' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('testing.pp', \"1+2+3+4\")\n            ctx.with_script_compiler {|c| c.evaluate_file(manifest) }\n          end\n          expect(result).to eq(10)\n        end\n\n        it 'instantiates definitions in the given code string' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n            pal.with_script_compiler do |compiler|\n              manifest = file_containing('testing.pp', (<<-CODE))\n                function run_me() { \"worked1\" }\n                run_me()\n                CODE\n              pal.with_script_compiler {|c| c.evaluate_file(manifest) }\n            end\n          end\n          expect(result).to eq('worked1')\n        end\n      end\n\n      context \"variables are supported such that\" do\n        it 'they can be set in any scope' do\n          vars = {'a'=> 10, 'x::y' => 20}\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n            ctx.with_script_compiler {|c| c.evaluate_string(\"1+2+3+4+$a+$x::y\")}\n          end\n          expect(result).to eq(40)\n        end\n\n        it 'an error is raised if a variable name is illegal' do\n          vars = {'_a::b'=> 10}\n          expect do\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n              manifest = file_containing('testing.pp', \"ok\")\n              ctx.with_script_compiler {|c| c.evaluate_file(manifest) }\n            end\n          end.to raise_error(/has illegal name/)\n        end\n\n        it 'an error is raised if variable value is not RichData compliant' do\n          vars = {'a'=> ArgumentError.new(\"not rich data\")}\n          expect do\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n              ctx.with_script_compiler {|c|  }\n            end\n          end.to raise_error(/has illegal type - got: ArgumentError/)\n        end\n\n        it 'variable given to script_compiler overrides those given for environment' do\n          vars = {'a'=> 10, 'x::y' => 20}\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n            ctx.with_script_compiler(variables: {'x::y' => 40}) {|c| c.evaluate_string(\"1+2+3+4+$a+$x::y\")}\n          end\n          expect(result).to eq(60)\n        end\n      end\n\n      context \"functions are supported such that\" do\n        it '\"call_function\" calls a function' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc($a) { $a * 2 } \")\n            ctx.with_script_compiler(manifest_file: manifest) {|c| c.call_function('myfunc', 6) }\n          end\n          expect(result).to eq(12)\n        end\n\n        it '\"call_function\" accepts a call with a ruby block' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.call_function('with', 6) {|x| x * 2} }\n          end\n          expect(result).to eq(12)\n        end\n\n        it '\"function_signature\" returns a signature of a function' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              c.function_signature('myfunc')\n            end\n          end\n          expect(result.class).to eq(Puppet::Pal::FunctionSignature)\n        end\n\n        it '\"FunctionSignature#callable_with?\" returns boolean if function is callable with given argument values' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              signature = c.function_signature('myfunc')\n              [ signature.callable_with?([10]),\n                signature.callable_with?(['nope'])\n              ]\n            end\n          end\n          expect(result).to eq([true, false])\n        end\n\n        it '\"FunctionSignature#callable_with?\" calls a given lambda if there is an error' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              signature = c.function_signature('myfunc')\n              local_result = 'not yay'\n              signature.callable_with?(['nope']) {|error| local_result = error }\n              local_result\n            end\n          end\n          expect(result).to match(/'myfunc' parameter 'a' expects an Integer value, got String/)\n        end\n\n        it '\"FunctionSignature#callable_with?\" does not call a given lambda when there is no error' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              signature = c.function_signature('myfunc')\n              local_result = 'yay'\n              signature.callable_with?([10]) {|error| local_result = 'not yay' }\n              local_result\n            end\n          end\n          expect(result).to eq('yay')\n        end\n\n        it '\"function_signature\" gets the signatures from a ruby function with multiple dispatch' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.function_signature('lookup') }\n          end\n          # check two different signatures of the lookup function\n          expect(result.callable_with?(['key'])).to eq(true)\n          expect(result.callable_with?(['key'], lambda() {|k| })).to eq(true)\n        end\n\n        it '\"function_signature\" returns nil if function is not found' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.function_signature('no_where_to_be_found') }\n          end\n          expect(result).to eq(nil)\n        end\n\n        it '\"FunctionSignature#callables\" returns an array of callables' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              c.function_signature('myfunc').callables\n            end\n          end\n          expect(result.class).to eq(Array)\n          expect(result.all? {|c| c.is_a?(Puppet::Pops::Types::PCallableType)}).to eq(true)\n        end\n\n        it '\"list_functions\" returns an array with all function names that can be loaded' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.list_functions() }\n          end\n          expect(result.is_a?(Array)).to eq(true)\n          expect(result.all? {|s| s.is_a?(Puppet::Pops::Loader::TypedName) }).to eq(true)\n          # there are certainly more than 30 functions in puppet - (56 when writing this, but some refactoring\n          # may take place, so don't want an exact number here - jsut make sure it found \"all of them\"\n          expect(result.count).to be > 30\n        end\n\n        it '\"list_functions\" filters on name based on a given regexp' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.list_functions(/epp/) }\n          end\n          expect(result.is_a?(Array)).to eq(true)\n          expect(result.all? {|s| s.is_a?(Puppet::Pops::Loader::TypedName) }).to eq(true)\n          # there are two functions currently that have 'epp' in their name\n          expect(result.count).to eq(2)\n        end\n\n      end\n\n      context 'supports plans such that' do\n        it '\"plan_signature\" returns the signatures of a plan' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"plan myplan(Integer $a) {  } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              signature = c.plan_signature('myplan')\n              [ signature.callable_with?({'a' => 10}),\n                signature.callable_with?({'a' => 'nope'})\n              ]\n            end\n          end\n          expect(result).to eq([true, false])\n        end\n\n        it 'a PlanSignature.callable_with? calls a given lambda with any errors as a formatted string' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"plan myplan(Integer $a, Integer $b) {  } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              signature = c.plan_signature('myplan')\n              local_result = nil\n              signature.callable_with?({'a' => 'nope'}) {|errors| local_result = errors }\n              local_result\n            end\n          end\n          # Note that errors are indented one space and on separate lines\n          #\n          expect(result).to eq(\" parameter 'a' expects an Integer value, got String\\n expects a value for parameter 'b'\")\n        end\n\n        it 'a PlanSignature.callable_with? does not call a given lambda if there are no errors' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"plan myplan(Integer $a) {  } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              signature = c.plan_signature('myplan')\n              local_result = 'yay'\n              signature.callable_with?({'a' => 1}) {|errors| local_result = 'not yay' }\n              local_result\n            end\n          end\n          expect(result).to eq('yay')\n        end\n\n        it '\"plan_signature\" returns nil if plan is not found' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.plan_signature('no_where_to_be_found') }\n          end\n          expect(result).to be(nil)\n        end\n\n        it '\"PlanSignature#params_type\" returns a map of all parameters and their types' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"plan myplan(Integer $a, String $b) {  } \")\n            ctx.with_script_compiler(manifest_file: manifest) do |c|\n              c.plan_signature('myplan').params_type\n            end\n          end\n          expect(result.class).to eq(Puppet::Pops::Types::PStructType)\n          expect(result.to_s).to eq(\"Struct[{'a' => Integer, 'b' => String}]\")\n        end\n      end\n\n      context 'supports puppet data types such that' do\n        it '\"type\" parses and returns a Type from a string specification' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            manifest = file_containing('main.pp', \"type MyType = Float\")\n            ctx.with_script_compiler(manifest_file: manifest) {|c| c.type('Variant[Integer, Boolean, MyType]') }\n          end\n          expect(result.is_a?(Puppet::Pops::Types::PVariantType)).to eq(true)\n          expect(result.types.size).to eq(3)\n          expect(result.instance?(3.14)).to eq(true)\n        end\n\n        it '\"create\" creates a new object from a puppet data type and args' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_script_compiler { |c| c.create(Puppet::Pops::Types::PIntegerType::DEFAULT, '0x10') }\n          end\n          expect(result).to eq(16)\n        end\n\n        it '\"create\" creates a new object from puppet data type in string form and args' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_script_compiler { |c| c.create('Integer', '010') }\n          end\n          expect(result).to eq(8)\n        end\n      end\n    end\n\n    context 'supports parsing such that' do\n      it '\"parse_string\" parses a puppet language string' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_script_compiler { |c| c.parse_string('$a = 10') }\n        end\n        expect(result.class).to eq(Puppet::Pops::Model::Program)\n      end\n\n      {  nil      => Puppet::Error,\n        '0xWAT'   => Puppet::ParseErrorWithIssue,\n        '$0 = 1'  => Puppet::ParseErrorWithIssue,\n        'else 32' => Puppet::ParseErrorWithIssue,\n      }.each_pair do |input, error_class|\n        it \"'parse_string' raises an error for invalid input: '#{input}'\" do\n          expect {\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_script_compiler { |c| c.parse_string(input) }\n          end\n          }.to raise_error(error_class)\n        end\n      end\n\n      it '\"parse_file\" parses a puppet language string' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          manifest = file_containing('main.pp', \"$a = 10\")\n          ctx.with_script_compiler { |c| c.parse_file(manifest) }\n        end\n        expect(result.class).to eq(Puppet::Pops::Model::Program)\n      end\n\n      it \"'parse_file' raises an error for invalid input: 'else 32'\" do\n        expect {\n        Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          manifest = file_containing('main.pp', \"else 32\")\n          ctx.with_script_compiler { |c| c.parse_file(manifest) }\n        end\n        }.to raise_error(Puppet::ParseErrorWithIssue)\n      end\n\n      it \"'parse_file' raises an error for invalid input, file is not a string\" do\n        expect {\n        Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_script_compiler { |c| c.parse_file(42) }\n        end\n        }.to raise_error(Puppet::Error)\n      end\n\n      it 'the \"evaluate\" method evaluates the parsed AST' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_script_compiler { |c| c.evaluate(c.parse_string('10 + 20')) }\n        end\n        expect(result).to eq(30)\n      end\n\n      it 'the \"evaluate\" method instantiates definitions when given a Program' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_script_compiler { |c| c.evaluate(c.parse_string('function foo() { \"yay\"}; foo()')) }\n        end\n        expect(result).to eq('yay')\n      end\n\n      it 'the \"evaluate\" method does not instantiates definitions when given ast other than Program' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_script_compiler do |c|\n              program= c.parse_string('function foo() { \"yay\"}; foo()')\n              c.evaluate(program.body)\n            end\n          end\n        end.to raise_error(/Unknown function: 'foo'/)\n      end\n\n      it 'the \"evaluate_literal\" method evaluates AST being a representation of a literal value' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_script_compiler { |c| c.evaluate_literal(c.parse_string('{10 => \"hello\"}')) }\n        end\n        expect(result).to eq({10 => 'hello'})\n      end\n\n      it 'the \"evaluate_literal\" method errors if ast is not representing a literal value' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_script_compiler { |c| c.evaluate_literal(c.parse_string('{10+1 => \"hello\"}')) }\n          end\n        end.to raise_error(/does not represent a literal value/)\n      end\n\n      it 'the \"evaluate_literal\" method errors if ast contains definitions' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_script_compiler { |c| c.evaluate_literal(c.parse_string('function foo() { }; 42')) }\n          end\n        end.to raise_error(/does not represent a literal value/)\n      end\n\n    end\n  end\n\n  context 'with code in modules and env' do\n    let(:modulepath) { [modules_dir] }\n\n    let(:metadata_json_a) {\n      {\n        'name' => 'example/a',\n        'version' => '0.1.0',\n        'source' => 'git@github.com/example/example-a.git',\n        'dependencies' => [{'name' => 'c', 'version_range' => '>=0.1.0'}],\n        'author' => 'Bob the Builder',\n        'license' => 'Apache-2.0'\n      }\n    }\n\n    let(:metadata_json_b) {\n      {\n        'name' => 'example/b',\n        'version' => '0.1.0',\n        'source' => 'git@github.com/example/example-b.git',\n        'dependencies' => [{'name' => 'c', 'version_range' => '>=0.1.0'}],\n        'author' => 'Bob the Builder',\n        'license' => 'Apache-2.0'\n      }\n    }\n\n    let(:metadata_json_c) {\n      {\n        'name' => 'example/c',\n        'version' => '0.1.0',\n        'source' => 'git@github.com/example/example-c.git',\n        'dependencies' => [],\n        'author' => 'Bob the Builder',\n        'license' => 'Apache-2.0'\n      }\n    }\n\n    # TODO: there is something amiss with the metadata wrt dependencies - when metadata is present there is an error\n    #       that dependencies could not be resolved. Metadata is therefore commented out.\n    #       Dependency based visibility is probably something that we should remove...\n    let(:modules) {\n      {\n        'a' => {\n        'functions' => a_functions,\n        'lib' => { 'puppet' => a_lib_puppet },\n        'plans' => a_plans,\n        'tasks' => a_tasks,\n        'types' => a_types,\n#        'metadata.json' => metadata_json_a.to_json\n        },\n        'b' => {\n        'functions' => b_functions,\n        'lib' => b_lib,\n        'plans' => b_plans,\n        'tasks' => b_tasks,\n        'types' => b_types,\n#        'metadata.json' => metadata_json_b.to_json\n        },\n        'c' => {\n        'types' => c_types,\n#        'metadata.json' => metadata_json_c.to_json\n        },\n      }\n    }\n\n    let(:a_plans) {\n      {\n        'aplan.pp' => <<-PUPPET.unindent,\n        plan a::aplan() { 'a::aplan value' }\n        PUPPET\n      }\n    }\n\n    let(:a_types) {\n      {\n        'atype.pp' => <<-PUPPET.unindent,\n        type A::Atype = Integer\n        PUPPET\n      }\n    }\n\n    let(:a_tasks) {\n      {\n        'atask' => '',\n      }\n    }\n\n    let(:a_functions) {\n      {\n        'afunc.pp' => 'function a::afunc() { \"a::afunc value\" }',\n      }\n    }\n\n    let(:a_lib_puppet) {\n      {\n        'functions' => {\n          'a' => {\n            'arubyfunc.rb' => <<-RUBY.unindent,\n              require 'stuff/something'\n              Puppet::Functions.create_function(:'a::arubyfunc') do\n                def arubyfunc\n                  Stuff::SOMETHING\n                end\n              end\n              RUBY\n            'myscriptcompilerfunc.rb' => <<-RUBY.unindent,\n              Puppet::Functions.create_function(:'a::myscriptcompilerfunc', Puppet::Functions::InternalFunction) do\n                dispatch :myscriptcompilerfunc do\n                  script_compiler_param\n                  param 'String',:name\n                end\n\n                def myscriptcompilerfunc(script_compiler, name)\n                  script_compiler.is_a?(Puppet::Pal::ScriptCompiler) ? name : 'no go'\n                end\n              end\n              RUBY\n          }\n        }\n      }\n    }\n\n    let(:b_plans) {\n      {\n        'aplan.pp' => <<-PUPPET.unindent,\n        plan b::aplan() {}\n        PUPPET\n      }\n    }\n\n    let(:b_types) {\n      {\n        'atype.pp' => <<-PUPPET.unindent,\n        type B::Atype = Integer\n        PUPPET\n      }\n    }\n\n    let(:b_tasks) {\n      {\n        'atask' => \"# doing exactly nothing\\n\",\n        'atask.json' => <<-JSONTEXT.unindent\n          {\n            \"description\": \"test task b::atask\",\n            \"input_method\": \"stdin\",\n            \"parameters\": {\n              \"string_param\": {\n                \"description\": \"A string parameter\",\n                \"type\": \"String[1]\"\n              },\n              \"int_param\": {\n                \"description\": \"An integer parameter\",\n                \"type\": \"Integer\"\n              }\n            }\n          }\n        JSONTEXT\n      }\n    }\n\n    let(:b_functions) {\n      {\n        'afunc.pp' => 'function b::afunc() {}',\n      }\n    }\n\n    let(:b_lib) {\n      {\n        'puppet' => b_lib_puppet,\n        'stuff' => {\n          'something.rb' => \"module Stuff; SOMETHING = 'something'; end\"\n        }\n      }\n    }\n\n    let(:b_lib_puppet) {\n      {\n        'functions' => {\n        'b' => {\n        'arubyfunc.rb' => \"Puppet::Functions.create_function(:'b::arubyfunc') { def arubyfunc; 'arubyfunc_value'; end }\",\n        }\n        }\n      }\n    }\n\n    let(:c_types) {\n      {\n        'atype.pp' => <<-PUPPET.unindent,\n        type C::Atype = Integer\n        PUPPET\n      }\n    }\n    context 'configured as temporary environment such that' do\n      it 'modules are available' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n          ctx.with_script_compiler {|c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      it 'libs in a given \"modulepath\" are added to the Ruby $LOAD_PATH' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n          ctx.with_script_compiler {|c| c.evaluate_string('a::arubyfunc()') }\n        end\n        expect(result).to eql('something')\n      end\n\n      it 'errors if a block is not given to in_tmp_environment' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts)\n        end.to raise_error(/A block must be given to 'in_tmp_environment/)\n      end\n\n      it 'errors if an env_name is given and is not a String[1]' do\n        expect do\n          Puppet::Pal.in_tmp_environment('', modulepath: modulepath, facts: node_facts) { |ctx| }\n        end.to raise_error(/temporary environment name has wrong type/)\n\n        expect do\n          Puppet::Pal.in_tmp_environment(32, modulepath: modulepath, facts: node_facts) { |ctx| }\n        end.to raise_error(/temporary environment name has wrong type/)\n      end\n\n      { 'a hash'                => {'a' => 'hm'},\n        'an integer'            => 32,\n        'separated strings'     => 'dir1;dir2',\n        'empty string in array' => ['']\n      }.each_pair do |what, value|\n        it \"errors if modulepath is #{what}\" do\n          expect do\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: value, facts: node_facts) { |ctx| }\n          end.to raise_error(/modulepath has wrong type/)\n        end\n      end\n\n      context 'facts are supported such that' do\n        it 'they are obtained if they are not given' do\n          facts = Puppet::Node::Facts.new(Puppet[:certname], 'puppetversion' => Puppet.version)\n          Puppet::Node::Facts.indirection.save(facts)\n\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath ) do |ctx|\n            ctx.with_script_compiler {|c| c.evaluate_string(\"$facts =~ Hash and $facts[puppetversion] == '#{Puppet.version}'\") }\n          end\n          expect(result).to eq(true)\n        end\n\n        it 'can be given as a hash when creating the environment' do\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: { 'myfact' => 42 }) do |ctx|\n            ctx.with_script_compiler {|c| c.evaluate_string(\"$facts =~ Hash and $facts[myfact] == 42\") }\n          end\n          expect(result).to eq(true)\n        end\n\n        it 'can be overridden with a hash when creating a script compiler' do\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: { 'myfact' => 42 }) do |ctx|\n            ctx.with_script_compiler(facts: { 'myfact' => 43 }) {|c| c.evaluate_string(\"$facts =~ Hash and $facts[myfact] == 43\") }\n          end\n          expect(result).to eq(true)\n        end\n\n        it 'can be disabled with the :set_local_facts option' do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: { 'myfact' => 42}) do |ctx|\n            ctx.with_script_compiler(facts: { 'myfact' => 42 }, set_local_facts: false) do |compiler|\n              expect { compiler.evaluate_string('$facts') }.to raise_error(\n                Puppet::PreformattedError,\n                /Unknown variable: 'facts'/\n              )\n            end\n          end\n        end\n      end\n\n      context 'supports tasks such that' do\n        it '\"task_signature\" returns the signatures of a generic task' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler do |c|\n              signature = c.task_signature('a::atask')\n              [ signature.runnable_with?('whatever' => 10),\n                signature.runnable_with?('anything_goes' => 'foo')\n              ]\n            end\n          end\n          expect(result).to eq([true, true])\n        end\n\n        it '\"TaskSignature#runnable_with?\" calls a given lambda if there is an error in a generic task' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler do |c|\n              signature = c.task_signature('a::atask')\n              local_result = 'not yay'\n              signature.runnable_with?('string_param' => /not data/) {|error| local_result = error }\n              local_result\n            end\n          end\n          expect(result).to match(/Task a::atask:\\s+entry 'string_param' expects a Data value, got Regexp/m)\n        end\n\n        it '\"task_signature\" returns the signatures of a task defined with metadata' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler do |c|\n              signature = c.task_signature('b::atask')\n              [ signature.runnable_with?('string_param' => 'foo', 'int_param' => 10),\n                signature.runnable_with?('anything_goes' => 'foo'),\n                signature.task_hash['name'],\n                signature.task_hash['metadata']['parameters']['string_param']['description'],\n                signature.task_hash['metadata']['description'],\n                signature.task_hash['metadata']['parameters']['int_param']['type'],\n              ]\n            end\n          end\n          expect(result).to eq([true, false, 'b::atask', 'A string parameter', 'test task b::atask', 'Integer'])\n        end\n\n        it '\"TaskSignature#runnable_with?\" calls a given lambda if there is an error' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler do |c|\n              signature = c.task_signature('b::atask')\n              local_result = 'not yay'\n              signature.runnable_with?('string_param' => 10) {|error| local_result = error }\n              local_result\n            end\n          end\n          expect(result).to match(/Task b::atask:\\s+parameter 'string_param' expects a String value, got Integer/m)\n        end\n\n        it '\"task_signature\" returns nil if task is not found' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.task_signature('no_where_to_be_found') }\n          end\n          expect(result).to be(nil)\n        end\n\n        it 'default task input_method is nil' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler do |c|\n              signature = c.task_signature('a::atask')\n              signature.task_hash\n            end\n          end\n          expect(result['metadata']['input_method']).to be_nil\n        end\n\n        it 'task input_method is parsed from task metadata' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler do |c|\n              signature = c.task_signature('b::atask')\n              signature.task_hash\n            end\n          end\n          expect(result['metadata']['input_method']).to eq('stdin')\n        end\n\n        it '\"list_tasks\" returns an array with all tasks that can be loaded' do\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.list_tasks() }\n          end\n          expect(result.is_a?(Array)).to eq(true)\n          expect(result.all? {|s| s.is_a?(Puppet::Pops::Loader::TypedName) }).to eq(true)\n          expect(result.map {|tn| tn.name}).to contain_exactly('a::atask', 'b::atask')\n        end\n\n        it '\"list_tasks\" filters on name based on a given regexp' do\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.list_tasks(/^a::/) }\n          end\n          expect(result.is_a?(Array)).to eq(true)\n          expect(result.all? {|s| s.is_a?(Puppet::Pops::Loader::TypedName) }).to eq(true)\n          expect(result.map {|tn| tn.name}).to eq(['a::atask'])\n        end\n      end\n\n    end\n\n    context 'configured as an existing given environment directory such that' do\n      it 'modules in it are available from its \"modules\" directory' do\n        result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, facts: node_facts) do |ctx|\n          ctx.with_script_compiler {|c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      it 'libs in a given \"modulepath\" are added to the Ruby $LOAD_PATH' do\n        result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, facts: node_facts) do |ctx|\n          ctx.with_script_compiler {|c| c.evaluate_string('a::arubyfunc()') }\n        end\n        expect(result).to eql('something')\n      end\n\n      it 'a given \"modulepath\" overrides the default' do\n        expect do\n          Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, modulepath: [], facts: node_facts) do |ctx|\n            ctx.with_script_compiler {|c| c.evaluate_string('a::afunc()') }\n          end\n        end.to raise_error(/Unknown function: 'a::afunc'/)\n      end\n\n      it 'a \"pre_modulepath\" is prepended and a \"post_modulepath\" is appended to the effective modulepath' do\n        other_modules1 = File.join(environments_dir, 'other_env1/modules')\n        other_modules2 = File.join(environments_dir, 'other_env2/modules')\n        result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir,\n          pre_modulepath: [other_modules1],\n          post_modulepath: [other_modules2],\n          facts: node_facts\n        ) do |ctx|\n          the_modulepath = Puppet.lookup(:environments).get('pal_env').modulepath\n          the_modulepath[0] == other_modules1 && the_modulepath[-1] == other_modules2\n        end\n        expect(result).to be(true)\n      end\n\n      it 'can set variables in any scope' do\n        vars = {'a'=> 10, 'x::y' => 20}\n        result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, facts: node_facts, variables: vars) do |ctx|\n          ctx.with_script_compiler { |c| c.evaluate_string(\"1+2+3+4+$a+$x::y\") }\n        end\n        expect(result).to eq(40)\n      end\n\n      it 'errors in a meaningful way when a non existing env name is given' do\n        testing_env_dir # creates the structure\n        expect do\n          Puppet::Pal.in_environment('blah_env', env_dir: testing_env_dir.chop, facts: node_facts) { |ctx| }\n        end.to raise_error(/The environment directory '.*' does not exist/)\n      end\n\n      it 'errors if an env_name is given and is not a String[1]' do\n        expect do\n          Puppet::Pal.in_environment('', env_dir: testing_env_dir, facts: node_facts)  { |ctx| }\n        end.to raise_error(/env_name has wrong type/)\n\n        expect do\n          Puppet::Pal.in_environment(32, env_dir: testing_env_dir, facts: node_facts)  { |ctx| }\n        end.to raise_error(/env_name has wrong type/)\n      end\n\n      { 'a hash'                => {'a' => 'hm'},\n        'an integer'            => 32,\n        'separated strings'     => 'dir1;dir2',\n        'empty string in array' => ['']\n      }.each_pair do |what, value|\n        it \"errors if modulepath is #{what}\" do\n          expect do\n            Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, modulepath: {'a' => 'hm'}, facts: node_facts) { |ctx| }\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: value, facts: node_facts) { |ctx| }\n          end.to raise_error(/modulepath has wrong type/)\n        end\n      end\n\n      it 'errors if env_dir and envpath are both given' do\n        testing_env_dir # creates the structure\n        expect do\n          Puppet::Pal.in_environment('blah_env', env_dir: testing_env_dir, envpath: environments_dir, facts: node_facts) { |ctx| }\n        end.to raise_error(/Cannot use 'env_dir' and 'envpath' at the same time/)\n      end\n    end\n\n    context 'configured as existing given envpath such that' do\n      it 'modules in it are available from its \"modules\" directory' do\n        testing_env_dir # creates the structure\n        result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n          ctx.with_script_compiler { |c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      it 'a given \"modulepath\" overrides the default' do\n        testing_env_dir # creates the structure\n        expect do\n          Puppet::Pal.in_environment('pal_env', envpath: environments_dir, modulepath: [], facts: node_facts) do |ctx|\n            ctx.with_script_compiler { |c| c.evaluate_string('a::afunc()') }\n          end\n        end.to raise_error(/Unknown function: 'a::afunc'/)\n      end\n\n      it 'a \"pre_modulepath\" is prepended and a \"post_modulepath\" is appended to the effective modulepath' do\n        testing_env_dir # creates the structure\n        other_modules1 = File.join(environments_dir, 'other_env1/modules')\n        other_modules2 = File.join(environments_dir, 'other_env2/modules')\n        result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir,\n          pre_modulepath: [other_modules1],\n          post_modulepath: [other_modules2],\n          facts: node_facts\n        ) do |ctx|\n          the_modulepath = Puppet.lookup(:environments).get('pal_env').modulepath\n          the_modulepath[0] == other_modules1 && the_modulepath[-1] == other_modules2\n        end\n        expect(result).to be(true)\n      end\n\n      it 'the envpath can have multiple entries - that are searched for the given env' do\n        testing_env_dir # creates the structure\n        result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n          ctx.with_script_compiler {|c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      it 'errors in a meaningful way when a non existing env name is given' do\n        testing_env_dir # creates the structure\n        expect do\n          Puppet::Pal.in_environment('blah_env', envpath: environments_dir, facts: node_facts) { |ctx| }\n        end.to raise_error(/No directory found for the environment 'blah_env' on the path '.*'/)\n      end\n\n      it 'errors if a block is not given to in_environment' do\n        expect do\n          Puppet::Pal.in_environment('blah_env', envpath: environments_dir, facts: node_facts)\n        end.to raise_error(/A block must be given to 'in_environment/)\n      end\n\n      it 'errors if envpath is something other than a string' do\n        testing_env_dir # creates the structure\n        expect do\n          Puppet::Pal.in_environment('blah_env', envpath: '', facts: node_facts)  { |ctx| }\n        end.to raise_error(/envpath has wrong type/)\n\n        expect do\n          Puppet::Pal.in_environment('blah_env', envpath: [environments_dir], facts: node_facts) { |ctx| }\n        end.to raise_error(/envpath has wrong type/)\n      end\n\n      context 'with a script compiler' do\n        it 'uses configured manifest_file if configured_by_env is true and Puppet[:code] is unset' do\n          testing_env_dir # creates the structure\n          Puppet[:manifest] = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n            ctx.with_script_compiler(configured_by_env: true) {|c|  c.call_function('myfunc', 4)}\n          end\n          expect(result).to eql(8)\n        end\n\n        it 'uses Puppet[:code] if configured_by_env is true and Puppet[:code] is set' do\n          testing_env_dir # creates the structure\n          Puppet[:manifest] = file_containing('amanifest.pp', \"$a = 20\")\n          Puppet[:code] = '$a = 40'\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n            ctx.with_script_compiler(configured_by_env: true) {|c|  c.evaluate_string('$a')}\n          end\n          expect(result).to eql(40)\n        end\n\n        it 'makes the pal ScriptCompiler available as script_compiler_param to Function dispatcher' do\n          testing_env_dir # creates the structure\n          Puppet[:manifest] = file_containing('noop.pp', \"undef\")\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n            ctx.with_script_compiler(configured_by_env: true) {|c|  c.call_function('a::myscriptcompilerfunc', 'go')}\n          end\n          expect(result).to eql('go')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/puppet_pal_catalog_spec.rb",
    "content": "#! /usr/bin/env ruby\nrequire 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_pal'\n\ndescribe 'Puppet Pal' do\n  include PuppetSpec::Files\n\n  let(:testing_env) do\n    {\n      'pal_env' => {\n        'functions' => functions,\n        'lib' => { 'puppet' => lib_puppet },\n        'manifests' => manifests,\n        'modules' => modules,\n        'plans' => plans,\n        'tasks' => tasks,\n        'types' => types,\n      },\n      'other_env1' => { 'modules' => {} },\n      'other_env2' => { 'modules' => {} },\n    }\n  end\n\n  let(:functions) { {} }\n  let(:manifests) { {} }\n  let(:modules) { {} }\n  let(:plans) { {} }\n  let(:lib_puppet) { {} }\n  let(:tasks) { {} }\n  let(:types) { {} }\n\n  let(:environments_dir) { Puppet[:environmentpath] }\n\n  let(:testing_env_dir) do\n    dir_contained_in(environments_dir, testing_env)\n    env_dir = File.join(environments_dir, 'pal_env')\n    PuppetSpec::Files.record_tmp(env_dir)\n    PuppetSpec::Files.record_tmp(File.join(environments_dir, 'other_env1'))\n    PuppetSpec::Files.record_tmp(File.join(environments_dir, 'other_env2'))\n    env_dir\n  end\n\n  let(:modules_dir) { File.join(testing_env_dir, 'modules') }\n\n  # Without any facts - this speeds up the tests that do not require $facts to have any values\n  let(:node_facts) { Hash.new }\n\n  # TODO: to be used in examples for running in an existing env\n  #  let(:env) { Puppet::Node::Environment.create(:testing, [modules_dir]) }\n\n  context 'in general - without code in modules or env' do\n    let(:modulepath) { [] }\n\n    context \"with a catalog compiler\" do\n      it 'errors if given both configured_by_env and manifest_file' do\n        expect {\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler(configured_by_env: true, manifest_file: 'undef.pp') {|c|  }\n          end\n        }.to raise_error(/manifest_file or code_string cannot be given when configured_by_env is true/)\n      end\n\n      it 'errors if given both configured_by_env and code_string' do\n        expect {\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler(configured_by_env: true, code_string: 'undef') {|c|  }\n          end\n        }.to raise_error(/manifest_file or code_string cannot be given when configured_by_env is true/)\n      end\n\n      it 'shadows target variables that collide with plan variables' do\n        facts       = { 'var' => 'fact' }\n        target_vars = { 'var' => 'target' }\n\n        expect(Puppet).to receive(:warning).with(/Target variable \\$var will be overridden by fact of the same name/)\n\n        result = Puppet::Pal.in_tmp_environment('pal_env', facts: {}) do |ctx|\n          ctx.with_catalog_compiler(facts: facts, target_variables: target_vars ) do |c|\n            c.evaluate_string('$var')\n          end\n        end\n\n        expect(result).to eq('fact')\n      end\n\n      it 'shadows target variables that collide with facts' do\n        plan_vars   = { 'var' => 'plan' }\n        target_vars = { 'var' => 'target' }\n\n        expect(Puppet).to receive(:warning).with(/Target variable \\$var will be overridden by plan variable of the same name/)\n        \n        result = Puppet::Pal.in_tmp_environment('pal_env', facts: {}) do |ctx|\n          ctx.with_catalog_compiler(variables: plan_vars, target_variables: target_vars ) do |c|\n            c.evaluate_string('$var')\n          end\n        end\n\n        expect(result).to eq('plan')\n      end\n\n      it 'shadows plan variables that collide with facts' do\n        facts     = { 'var' => 'fact' }\n        plan_vars = { 'var' => 'plan' }\n\n        expect(Puppet).to receive(:warning).with(/Plan variable \\$var will be overridden by fact of the same name/)\n        \n        result = Puppet::Pal.in_tmp_environment('pal_env', facts: {}) do |ctx|\n          ctx.with_catalog_compiler(facts: facts, variables: plan_vars ) do |c|\n            c.evaluate_string('$var')\n          end\n        end\n\n        expect(result).to eq('fact')\n      end\n\n      context \"evaluate_string method\" do\n        it 'evaluates code string in a given tmp environment' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string('1+2+3') }\n          end\n          expect(result).to eq(6)\n        end\n\n        it 'can be evaluated more than once in a given tmp environment - each in fresh compiler' do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            expect(  ctx.with_catalog_compiler {|c| c.evaluate_string('$a = 1+2+3')}).to eq(6)\n            expect { ctx.with_catalog_compiler {|c| c.evaluate_string('$a') }}.to raise_error(/Unknown variable: 'a'/)\n          end\n        end\n\n        it 'instantiates a function definition in the given code string' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n            pal.with_catalog_compiler do |compiler|\n              compiler.evaluate_string(<<-CODE)\n                function run_me() { \"worked1\" }\n                run_me()\n                CODE\n            end\n          end\n          expect(result).to eq('worked1')\n        end\n\n        it 'instantiates a user defined resource definition in the given code string' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n            pal.with_catalog_compiler do |compiler|\n              compiler.evaluate_string(<<-CODE)\n                define run_me() { }\n                run_me { test: }\n                CODE\n            end\n          end\n          resource = result[0]\n          expect(resource).to be_a(Puppet::Pops::Types::PResourceType)\n          expect(resource.type_name).to eq(\"Run_me\")\n          expect(resource.title).to eq('test')\n        end\n\n        context 'catalog_data_hash' do\n          it 'produces a data_hash encoding of a catalog' do\n            result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n              pal.with_catalog_compiler {|c|\n                c.evaluate_string(\"notify {'test': message => /a regexp/}\")\n                c.catalog_data_hash\n              }\n            end\n            expect(result['resources']).to include(include('type' => 'Notify'))\n          end\n        end\n\n        context 'the with_json_encoding()' do\n\n          it 'produces json for a catalog' do\n            result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n              pal.with_catalog_compiler {|c|\n                c.evaluate_string(\"notify {'test': message => /a regexp/}\")\n                c.with_json_encoding() {|encoder| encoder.encode }\n              }\n            end\n            parsed = JSON.parse(result)\n            expect(parsed['resources']).to include(include('type' => 'Notify'))\n          end\n\n          it 'produces pretty json by default' do\n            result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n              pal.with_catalog_compiler {|c|\n                c.evaluate_string(\"notify {'test': message => /a regexp/}\")\n                c.with_json_encoding() {|encoder| encoder.encode }\n              }\n            end\n            expect(result.count(\"\\n\")).to be > 10\n          end\n\n          it 'produces compact (non pretty) json when pretty is false' do\n            result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n              pal.with_catalog_compiler {|c|\n                c.evaluate_string(\"notify {'test': message => /a regexp/}\")\n                c.with_json_encoding(pretty: false) {|encoder| encoder.encode }\n              }\n            end\n            expect(result.count(\"\\n\")).to be < 10\n          end\n\n          it 'produces json for an individual resource by giving type and title to encode_resource()' do\n            result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n              pal.with_catalog_compiler {|c|\n                c.evaluate_string(\"notify {'test': message => 'yay'}\")\n                c.with_json_encoding() {|encoder| encoder.encode_resource('notify', 'test') }\n              }\n            end\n            expect(result).to match(/\"message\":\"yay\"/)\n          end\n\n          it 'encodes values as rich data when needed' do\n            result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n              pal.with_catalog_compiler {|c|\n                c.evaluate_string(\"notify {'test': message => /a regexp/}\")\n                c.with_json_encoding(pretty: true) {|encoder| encoder.encode_resource('notify', 'test') }\n              }\n            end\n            expect(result).to match(/\"__ptype\":\"Regexp\"/)\n          end\n        end\n      end\n\n      context \"evaluate_file method\" do\n        it 'evaluates a manifest file in a given tmp environment' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('testing.pp', \"1+2+3+4\")\n            ctx.with_catalog_compiler {|c| c.evaluate_file(manifest) }\n          end\n          expect(result).to eq(10)\n        end\n\n        it 'instantiates definitions in the given code string' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |pal|\n            pal.with_catalog_compiler do |compiler|\n              manifest = file_containing('testing.pp', (<<-CODE))\n                function run_me() { \"worked1\" }\n                run_me()\n                CODE\n              pal.with_catalog_compiler {|c| c.evaluate_file(manifest) }\n            end\n          end\n          expect(result).to eq('worked1')\n        end\n      end\n\n      context \"variables are supported such that\" do\n        it 'they can be set in any scope' do\n          vars = {'a'=> 10, 'x::y' => 20}\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string(\"1+2+3+4+$a+$x::y\")}\n          end\n          expect(result).to eq(40)\n        end\n\n        it 'an error is raised if a variable name is illegal' do\n          vars = {'_a::b'=> 10}\n          expect do\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n              manifest = file_containing('testing.pp', \"ok\")\n              ctx.with_catalog_compiler {|c| c.evaluate_file(manifest) }\n            end\n          end.to raise_error(/has illegal name/)\n        end\n\n        it 'an error is raised if variable value is not RichData compliant' do\n          vars = {'a'=> ArgumentError.new(\"not rich data\")}\n          expect do\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n              ctx.with_catalog_compiler {|c|  }\n            end\n          end.to raise_error(/has illegal type - got: ArgumentError/)\n        end\n\n        it 'variable given to script_compiler overrides those given for environment' do\n          vars = {'a'=> 10, 'x::y' => 20}\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts, variables: vars) do |ctx|\n            ctx.with_catalog_compiler(variables: {'x::y' => 40}) {|c| c.evaluate_string(\"1+2+3+4+$a+$x::y\")}\n          end\n          expect(result).to eq(60)\n        end\n      end\n\n      context \"functions are supported such that\" do\n        it '\"call_function\" calls a function' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc($a) { $a * 2 } \")\n            ctx.with_catalog_compiler(manifest_file: manifest) {|c| c.call_function('myfunc', 6) }\n          end\n          expect(result).to eq(12)\n        end\n\n        it '\"call_function\" accepts a call with a ruby block' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.call_function('with', 6) {|x| x * 2} }\n          end\n          expect(result).to eq(12)\n        end\n\n        it '\"function_signature\" returns a signature of a function' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_catalog_compiler(manifest_file: manifest) do |c|\n              c.function_signature('myfunc')\n            end\n          end\n          expect(result.class).to eq(Puppet::Pal::FunctionSignature)\n        end\n\n        it '\"FunctionSignature#callable_with?\" returns boolean if function is callable with given argument values' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_catalog_compiler(manifest_file: manifest) do |c|\n              signature = c.function_signature('myfunc')\n              [ signature.callable_with?([10]),\n                signature.callable_with?(['nope'])\n              ]\n            end\n          end\n          expect(result).to eq([true, false])\n        end\n\n        it '\"FunctionSignature#callable_with?\" calls a given lambda if there is an error' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_catalog_compiler(manifest_file: manifest) do |c|\n              signature = c.function_signature('myfunc')\n              local_result = 'not yay'\n              signature.callable_with?(['nope']) {|error| local_result = error }\n              local_result\n            end\n          end\n          expect(result).to match(/'myfunc' parameter 'a' expects an Integer value, got String/)\n        end\n\n        it '\"FunctionSignature#callable_with?\" does not call a given lambda when there is no error' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_catalog_compiler(manifest_file: manifest) do |c|\n              signature = c.function_signature('myfunc')\n              local_result = 'yay'\n              signature.callable_with?([10]) {|error| local_result = 'not yay' }\n              local_result\n            end\n          end\n          expect(result).to eq('yay')\n        end\n\n        it '\"function_signature\" gets the signatures from a ruby function with multiple dispatch' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.function_signature('lookup') }\n          end\n          # check two different signatures of the lookup function\n          expect(result.callable_with?(['key'])).to eq(true)\n          expect(result.callable_with?(['key'], lambda() {|k| })).to eq(true)\n        end\n\n        it '\"function_signature\" returns nil if function is not found' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.function_signature('no_where_to_be_found') }\n          end\n          expect(result).to eq(nil)\n        end\n\n        it '\"FunctionSignature#callables\" returns an array of callables' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            manifest = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n            ctx.with_catalog_compiler(manifest_file: manifest) do |c|\n              c.function_signature('myfunc').callables\n            end\n          end\n          expect(result.class).to eq(Array)\n          expect(result.all? {|c| c.is_a?(Puppet::Pops::Types::PCallableType)}).to eq(true)\n        end\n\n        it '\"list_functions\" returns an array with all function names that can be loaded' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.list_functions() }\n          end\n          expect(result.is_a?(Array)).to eq(true)\n          expect(result.all? {|s| s.is_a?(Puppet::Pops::Loader::TypedName) }).to eq(true)\n          # there are certainly more than 30 functions in puppet - (56 when writing this, but some refactoring\n          # may take place, so don't want an exact number here - jsut make sure it found \"all of them\"\n          expect(result.count).to be > 30\n        end\n\n        it '\"list_functions\" filters on name based on a given regexp' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.list_functions(/epp/) }\n          end\n          expect(result.is_a?(Array)).to eq(true)\n          expect(result.all? {|s| s.is_a?(Puppet::Pops::Loader::TypedName) }).to eq(true)\n          # there are two functions currently that have 'epp' in their name\n          expect(result.count).to eq(2)\n        end\n\n      end\n\n      context 'supports puppet data types such that' do\n        it '\"type\" parses and returns a Type from a string specification' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            manifest = file_containing('main.pp', \"type MyType = Float\")\n            ctx.with_catalog_compiler(manifest_file: manifest) {|c| c.type('Variant[Integer, Boolean, MyType]') }\n          end\n          expect(result.is_a?(Puppet::Pops::Types::PVariantType)).to eq(true)\n          expect(result.types.size).to eq(3)\n          expect(result.instance?(3.14)).to eq(true)\n        end\n\n        it '\"create\" creates a new object from a puppet data type and args' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler { |c| c.create(Puppet::Pops::Types::PIntegerType::DEFAULT, '0x10') }\n          end\n          expect(result).to eq(16)\n        end\n\n        it '\"create\" creates a new object from puppet data type in string form and args' do\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler { |c| c.create('Integer', '010') }\n          end\n          expect(result).to eq(8)\n        end\n      end\n    end\n\n    context 'supports parsing such that' do\n      it '\"parse_string\" parses a puppet language string' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler { |c| c.parse_string('$a = 10') }\n        end\n        expect(result.class).to eq(Puppet::Pops::Model::Program)\n      end\n\n      {  nil      => Puppet::Error,\n        '0xWAT'   => Puppet::ParseErrorWithIssue,\n        '$0 = 1'  => Puppet::ParseErrorWithIssue,\n        'else 32' => Puppet::ParseErrorWithIssue,\n      }.each_pair do |input, error_class|\n        it \"'parse_string' raises an error for invalid input: '#{input}'\" do\n          expect {\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler { |c| c.parse_string(input) }\n          end\n          }.to raise_error(error_class)\n        end\n      end\n\n      it '\"parse_file\" parses a puppet language string' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          manifest = file_containing('main.pp', \"$a = 10\")\n          ctx.with_catalog_compiler { |c| c.parse_file(manifest) }\n        end\n        expect(result.class).to eq(Puppet::Pops::Model::Program)\n      end\n\n      it \"'parse_file' raises an error for invalid input: 'else 32'\" do\n        expect {\n        Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          manifest = file_containing('main.pp', \"else 32\")\n          ctx.with_catalog_compiler { |c| c.parse_file(manifest) }\n        end\n        }.to raise_error(Puppet::ParseErrorWithIssue)\n      end\n\n      it \"'parse_file' raises an error for invalid input, file is not a string\" do\n        expect {\n        Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler { |c| c.parse_file(42) }\n        end\n        }.to raise_error(Puppet::Error)\n      end\n\n      it 'the \"evaluate\" method evaluates the parsed AST' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler { |c| c.evaluate(c.parse_string('10 + 20')) }\n        end\n        expect(result).to eq(30)\n      end\n\n      it 'the \"evaluate\" method instantiates definitions when given a Program' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler { |c| c.evaluate(c.parse_string('function foo() { \"yay\"}; foo()')) }\n        end\n        expect(result).to eq('yay')\n      end\n\n      it 'the \"evaluate\" method does not instantiates definitions when given ast other than Program' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler do |c|\n              program= c.parse_string('function foo() { \"yay\"}; foo()')\n              c.evaluate(program.body)\n            end\n          end\n        end.to raise_error(/Unknown function: 'foo'/)\n      end\n\n      it 'the \"evaluate_literal\" method evaluates AST being a representation of a literal value' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler { |c| c.evaluate_literal(c.parse_string('{10 => \"hello\"}')) }\n        end\n        expect(result).to eq({10 => 'hello'})\n      end\n\n      it 'the \"evaluate_literal\" method errors if ast is not representing a literal value' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler { |c| c.evaluate_literal(c.parse_string('{10+1 => \"hello\"}')) }\n          end\n        end.to raise_error(/does not represent a literal value/)\n      end\n\n      it 'the \"evaluate_literal\" method errors if ast contains definitions' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler { |c| c.evaluate_literal(c.parse_string('function foo() { }; 42')) }\n          end\n        end.to raise_error(/does not represent a literal value/)\n      end\n\n      it 'the \"evaluate\" method evaluates but does not evaluate lazy constructs' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler do |c|\n            c.evaluate(c.parse_string('define foo() { notify {nope: }} foo { test: }'))\n            c.with_json_encoding() {|encoder| encoder.encode }\n          end\n        end\n        parsed = JSON.parse(result)\n        expect(parsed['resources']).to_not include(include('type' => 'Notify'))\n      end\n\n      it 'an \"evaluate\" followed by \"compile_additions\" evaluates lazy constructs' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler do |c|\n            c.evaluate(c.parse_string('define foo() { notify {nope: }} foo { test: }'))\n            c.compile_additions\n            c.with_json_encoding() {|encoder| encoder.encode }\n          end\n        end\n        parsed = JSON.parse(result)\n        expect(parsed['resources']).to include(include('type' => 'Notify'))\n      end\n\n      it 'an \"evaluate\" followed by \"compile_additions\" validates the result' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler do |c|\n              c.evaluate(c.parse_string('define foo() { notify {nope: }} foo { test: before =>\"Bar[nope]\"}'))\n              c.compile_additions\n            end\n          end\n        end.to raise_error(Puppet::Error, /Could not find resource 'Bar\\[nope\\]'/)\n      end\n\n      it 'an \"evaluate\" followed by \"evaluate_additions\" does not validate the result' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler do |c|\n            c.evaluate(c.parse_string('define foo() { notify {nope: }} foo { test: before =>\"Bar[nope]\"}'))\n            c.evaluate_additions\n            c.with_json_encoding() {|encoder| encoder.encode }\n          end\n        end\n        parsed = JSON.parse(result)\n        expect(parsed['resources']).to include(include('type' => 'Notify'))\n      end\n\n      it 'an \"evaluate\" followed by \"evaluate_additions\" and \"validate\" validates the result' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n            ctx.with_catalog_compiler do |c|\n              c.evaluate(c.parse_string('define foo() { notify {nope: }} foo { test: before =>\"Bar[nope]\"}'))\n              c.compile_additions\n              c.validate\n            end\n          end\n        end.to raise_error(Puppet::Error, /Could not find resource 'Bar\\[nope\\]'/)\n      end\n\n      it 'an \"evaluate\" followed by \"evaluate_ast_node\" will correctly parse a node definition' do\n        Puppet[:node_name_value] = 'testing_node'\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do | ctx|\n          ctx.with_catalog_compiler do |c|\n            c.evaluate(c.parse_string(\"node 'testing_node' { notify {'PASSED': } }\"))\n            c.evaluate_ast_node\n            c.compile_additions\n            c.with_json_encoding() {|encoder| encoder.encode }\n          end\n        end\n        parsed = JSON.parse(result)\n        expect(parsed['resources']).to include(include('type' => 'Notify'))\n      end\n    end\n  end\n\n  context 'with code in modules and env' do\n    let(:modulepath) { [modules_dir] }\n\n    let(:metadata_json_a) {\n      {\n        'name' => 'example/a',\n        'version' => '0.1.0',\n        'source' => 'git@github.com/example/example-a.git',\n        'dependencies' => [{'name' => 'c', 'version_range' => '>=0.1.0'}],\n        'author' => 'Bob the Builder',\n        'license' => 'Apache-2.0'\n      }\n    }\n\n    let(:metadata_json_b) {\n      {\n        'name' => 'example/b',\n        'version' => '0.1.0',\n        'source' => 'git@github.com/example/example-b.git',\n        'dependencies' => [{'name' => 'c', 'version_range' => '>=0.1.0'}],\n        'author' => 'Bob the Builder',\n        'license' => 'Apache-2.0'\n      }\n    }\n\n    let(:metadata_json_c) {\n      {\n        'name' => 'example/c',\n        'version' => '0.1.0',\n        'source' => 'git@github.com/example/example-c.git',\n        'dependencies' => [],\n        'author' => 'Bob the Builder',\n        'license' => 'Apache-2.0'\n      }\n    }\n\n    # TODO: there is something amiss with the metadata wrt dependencies - when metadata is present there is an error\n    #       that dependencies could not be resolved. Metadata is therefore commented out.\n    #       Dependency based visibility is probably something that we should remove...\n    let(:modules) {\n      {\n        'a' => {\n        'functions' => a_functions,\n        'lib' => { 'puppet' => a_lib_puppet },\n        'types' => a_types,\n        },\n        'b' => {\n        'functions' => b_functions,\n        'lib' => b_lib,\n        'types' => b_types,\n        },\n        'c' => {\n        'types' => c_types,\n        },\n      }\n    }\n\n    let(:a_types) {\n      {\n        'atype.pp' => <<-PUPPET.unindent,\n        type A::Atype = Integer\n        PUPPET\n      }\n    }\n\n    let(:a_functions) {\n      {\n        'afunc.pp' => 'function a::afunc() { \"a::afunc value\" }',\n      }\n    }\n\n    let(:a_lib_puppet) {\n      {\n        'functions' => {\n          'a' => {\n            'arubyfunc.rb' => <<-RUBY.unindent,\n              require 'stuff/something'\n              Puppet::Functions.create_function(:'a::arubyfunc') do\n                def arubyfunc\n                  Stuff::SOMETHING\n                end\n              end\n              RUBY\n            'mycatalogcompilerfunc.rb' => <<-RUBY.unindent,\n              Puppet::Functions.create_function(:'a::mycatalogcompilerfunc', Puppet::Functions::InternalFunction) do\n                dispatch :mycatalogcompilerfunc do\n                  compiler_param\n                  param 'String',:name\n                end\n\n                def mycatalogcompilerfunc(the_compiler, name)\n                  the_compiler.is_a?(Puppet::Pal::CatalogCompiler) ? name : 'no go'\n                end\n              end\n              RUBY\n          }\n        },\n        'datatypes' => {\n          'mytype.rb' => <<-RUBY.unindent,\n            Puppet::DataTypes.create_type('Mytype') do\n              interface <<-PUPPET\n                attributes => {\n                  name => { type => String },\n                  year_of_birth => { type => Integer },\n                  age => { type => Integer, kind => derived },\n                }\n                PUPPET\n\n              implementation do\n                def age\n                  DateTime.now.year - @year_of_birth\n                end\n              end\n            end\n            RUBY\n        }\n      }\n    }\n\n    let(:b_types) {\n      {\n        'atype.pp' => <<-PUPPET.unindent,\n        type B::Atype = Integer\n        PUPPET\n      }\n    }\n\n    let(:b_functions) {\n      {\n        'afunc.pp' => 'function b::afunc() {}',\n      }\n    }\n\n    let(:b_lib) {\n      {\n        'puppet' => b_lib_puppet,\n        'stuff' => {\n          'something.rb' => \"module Stuff; SOMETHING = 'something'; end\"\n        }\n      }\n    }\n\n    let(:b_lib_puppet) {\n      {\n        'functions' => {\n        'b' => {\n        'arubyfunc.rb' => \"Puppet::Functions.create_function(:'b::arubyfunc') { def arubyfunc; 'arubyfunc_value'; end }\",\n        }\n        }\n      }\n    }\n\n    let(:c_types) {\n      {\n        'atype.pp' => <<-PUPPET.unindent,\n        type C::Atype = Integer\n        PUPPET\n      }\n    }\n\n    context 'configured as temporary environment such that' do\n      it 'modules are available' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n          ctx.with_catalog_compiler {|c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      it 'libs in a given \"modulepath\" are added to the Ruby $LOAD_PATH' do\n        result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts) do |ctx|\n          ctx.with_catalog_compiler {|c| c.evaluate_string('a::arubyfunc()') }\n        end\n        expect(result).to eql('something')\n      end\n\n      it 'errors if a block is not given to in_tmp_environment' do\n        expect do\n          Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: node_facts)\n        end.to raise_error(/A block must be given to 'in_tmp_environment/)\n      end\n\n      it 'errors if an env_name is given and is not a String[1]' do\n        expect do\n          Puppet::Pal.in_tmp_environment('', modulepath: modulepath, facts: node_facts) { |ctx| }\n        end.to raise_error(/temporary environment name has wrong type/)\n\n        expect do\n          Puppet::Pal.in_tmp_environment(32, modulepath: modulepath, facts: node_facts) { |ctx| }\n        end.to raise_error(/temporary environment name has wrong type/)\n      end\n\n      { 'a hash'                => {'a' => 'hm'},\n        'an integer'            => 32,\n        'separated strings'     => 'dir1;dir2',\n        'empty string in array' => ['']\n      }.each_pair do |what, value|\n        it \"errors if modulepath is #{what}\" do\n          expect do\n            Puppet::Pal.in_tmp_environment('pal_env', modulepath: value, facts: node_facts) { |ctx| }\n          end.to raise_error(/modulepath has wrong type/)\n        end\n      end\n\n      context 'facts are supported such that' do\n        it 'they are obtained if they are not given' do\n          facts = Puppet::Node::Facts.new(Puppet[:certname], 'puppetversion' => Puppet.version)\n          Puppet::Node::Facts.indirection.save(facts)\n\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath ) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string(\"$facts =~ Hash and $facts[puppetversion] == '#{Puppet.version}'\") }\n          end\n          expect(result).to eq(true)\n        end\n\n        it 'can be given as a hash when creating the environment' do\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: { 'myfact' => 42 }) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string(\"$facts =~ Hash and $facts[myfact] == 42\") }\n          end\n          expect(result).to eq(true)\n        end\n\n        it 'can be overridden with a hash when creating a script compiler' do\n          testing_env_dir # creates the structure\n          result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: modulepath, facts: { 'myfact' => 42 }) do |ctx|\n            ctx.with_catalog_compiler(facts: { 'myfact' => 43 }) {|c| c.evaluate_string(\"$facts =~ Hash and $facts[myfact] == 43\") }\n          end\n          expect(result).to eq(true)\n        end\n      end\n\n      context 'datatypes are supported such that' do\n        it 'datatypes defined as pcore in a module are deserialized' do\n          testing_env_dir\n          vars = {\"bobs_age\"=>{\"__ptype\"=>\"Mytype\", \"name\"=>\"Bob\", \"year_of_birth\"=>1984}}\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts, variables: vars) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string(\"$bobs_age.age\") }\n          end\n          expect(result).to eq(DateTime.now.year - 1984)\n        end\n      end\n\n      context 'configured as an existing given environment directory such that' do\n        it 'modules in it are available from its \"modules\" directory' do\n          result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string('a::afunc()') }\n          end\n          expect(result).to eq(\"a::afunc value\")\n        end\n\n        it 'libs in a given \"modulepath\" are added to the Ruby $LOAD_PATH' do\n          result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler {|c| c.evaluate_string('a::arubyfunc()') }\n          end\n          expect(result).to eql('something')\n        end\n\n        it 'a given \"modulepath\" overrides the default' do\n          expect do\n            Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, modulepath: [], facts: node_facts) do |ctx|\n              ctx.with_catalog_compiler {|c| c.evaluate_string('a::afunc()') }\n            end\n          end.to raise_error(/Unknown function: 'a::afunc'/)\n        end\n\n        it 'a \"pre_modulepath\" is prepended and a \"post_modulepath\" is appended to the effective modulepath' do\n          other_modules1 = File.join(environments_dir, 'other_env1/modules')\n          other_modules2 = File.join(environments_dir, 'other_env2/modules')\n          result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir,\n            pre_modulepath: [other_modules1],\n            post_modulepath: [other_modules2],\n            facts: node_facts\n          ) do |ctx|\n            the_modulepath = Puppet.lookup(:environments).get('pal_env').modulepath\n            the_modulepath[0] == other_modules1 && the_modulepath[-1] == other_modules2\n          end\n          expect(result).to be(true)\n        end\n\n        it 'can set variables in any scope' do\n          vars = {'a'=> 10, 'x::y' => 20}\n          result = Puppet::Pal.in_environment('pal_env', env_dir: testing_env_dir, facts: node_facts, variables: vars) do |ctx|\n            ctx.with_catalog_compiler { |c| c.evaluate_string(\"1+2+3+4+$a+$x::y\") }\n          end\n          expect(result).to eq(40)\n        end\n\n        it 'errors in a meaningful way when a non existing env name is given' do\n          testing_env_dir # creates the structure\n          expect do\n            Puppet::Pal.in_environment('blah_env', env_dir: testing_env_dir.chop, facts: node_facts) { |ctx| }\n          end.to raise_error(/The environment directory '.*' does not exist/)\n        end\n\n        it 'errors if an env_name is given and is not a String[1]' do\n          expect do\n            Puppet::Pal.in_environment('', env_dir: testing_env_dir, facts: node_facts)  { |ctx| }\n          end.to raise_error(/env_name has wrong type/)\n\n          expect do\n            Puppet::Pal.in_environment(32, env_dir: testing_env_dir, facts: node_facts)  { |ctx| }\n          end.to raise_error(/env_name has wrong type/)\n        end\n      end\n    end\n\n    context 'configured as existing given envpath such that' do\n      it 'modules in it are available from its \"modules\" directory' do\n        testing_env_dir # creates the structure\n        result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n          ctx.with_catalog_compiler { |c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      it 'a given \"modulepath\" overrides the default' do\n        testing_env_dir # creates the structure\n        expect do\n          Puppet::Pal.in_environment('pal_env', envpath: environments_dir, modulepath: [], facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler { |c| c.evaluate_string('a::afunc()') }\n          end\n        end.to raise_error(/Unknown function: 'a::afunc'/)\n      end\n\n      it 'the envpath can have multiple entries - that are searched for the given env' do\n        testing_env_dir # creates the structure\n        result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n          ctx.with_catalog_compiler {|c| c.evaluate_string('a::afunc()') }\n        end\n        expect(result).to eq(\"a::afunc value\")\n      end\n\n      context 'with a catalog compiler' do\n        it 'uses configured manifest_file if configured_by_env is true and Puppet[:code] is unset' do\n          testing_env_dir # creates the structure\n          Puppet[:manifest] = file_containing('afunc.pp', \"function myfunc(Integer $a) { $a * 2 } \")\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler(configured_by_env: true) {|c|  c.call_function('myfunc', 4)}\n          end\n          expect(result).to eql(8)\n        end\n\n        it 'uses Puppet[:code] if configured_by_env is true and Puppet[:code] is set' do\n          testing_env_dir # creates the structure\n          Puppet[:manifest] = file_containing('amanifest.pp', \"$a = 20\")\n          Puppet[:code] = '$a = 40'\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler(configured_by_env: true) {|c|  c.evaluate_string('$a')}\n          end\n          expect(result).to eql(40)\n        end\n\n        it 'makes the pal CatalogCompiler available as catalog_compiler_param to Function dispatcher' do\n          testing_env_dir # creates the structure\n          Puppet[:manifest] = file_containing('noop.pp', \"undef\")\n          result = Puppet::Pal.in_environment('pal_env', envpath: environments_dir, facts: node_facts) do |ctx|\n            ctx.with_catalog_compiler(configured_by_env: true) {|c|  c.call_function('a::mycatalogcompilerfunc', 'go')}\n          end\n          expect(result).to eql('go')\n        end\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/puppet_pal_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire_relative 'puppet_pal_2pec'\n"
  },
  {
    "path": "spec/unit/puppet_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet'\nrequire 'puppet_spec/files'\n\ndescribe Puppet do\n  include PuppetSpec::Files\n\n  context \"#version\" do\n    it \"should be valid semver\" do\n      expect(SemanticPuppet::Version).to be_valid Puppet.version\n    end\n  end\n\n  Puppet::Util::Log.eachlevel do |level|\n    it \"should have a method for sending '#{level}' logs\" do\n      expect(Puppet).to respond_to(level)\n    end\n  end\n\n  it \"should be able to change the path\" do\n    newpath = ENV[\"PATH\"] + File::PATH_SEPARATOR + \"/something/else\"\n    Puppet[:path] = newpath\n    expect(ENV[\"PATH\"]).to eq(newpath)\n  end\n\n  it 'should propagate --modulepath to base environment' do\n    expect(Puppet::Node::Environment).to receive(:create).with(\n      be_a(Symbol), ['/my/modules'], Puppet::Node::Environment::NO_MANIFEST)\n\n    Puppet.base_context({\n      :environmentpath => '/envs',\n      :basemodulepath => '/base/modules',\n      :modulepath => '/my/modules'\n    })\n  end\n\n  it 'empty modulepath does not override basemodulepath' do\n    expect(Puppet::Node::Environment).to receive(:create).with(\n      be_a(Symbol), ['/base/modules'], Puppet::Node::Environment::NO_MANIFEST)\n\n    Puppet.base_context({\n      :environmentpath => '/envs',\n      :basemodulepath => '/base/modules',\n      :modulepath => ''\n    })\n  end\n\n  it 'nil modulepath does not override basemodulepath' do\n    expect(Puppet::Node::Environment).to receive(:create).with(\n      be_a(Symbol), ['/base/modules'], Puppet::Node::Environment::NO_MANIFEST)\n\n    Puppet.base_context({\n      :environmentpath => '/envs',\n      :basemodulepath => '/base/modules',\n      :modulepath => nil\n    })\n  end\n\n  context \"Puppet::OLDEST_RECOMMENDED_RUBY_VERSION\" do\n    it \"should have an oldest recommended ruby version constant\" do\n      expect(Puppet::OLDEST_RECOMMENDED_RUBY_VERSION).not_to be_nil\n    end\n\n    it \"should be a string\" do\n      expect(Puppet::OLDEST_RECOMMENDED_RUBY_VERSION).to be_a_kind_of(String)\n    end\n\n    it \"should match a semver version\" do\n      expect(SemanticPuppet::Version).to be_valid(Puppet::OLDEST_RECOMMENDED_RUBY_VERSION)\n    end\n  end\n\n  context \"Settings\" do\n    before(:each) do\n      @old_settings = Puppet.settings\n    end\n    after(:each) do\n      Puppet.replace_settings_object(@old_settings)\n    end\n    it \"should allow for settings to be redefined with a custom object\" do\n      new_settings = double()\n      Puppet.replace_settings_object(new_settings)\n      expect(Puppet.settings).to eq(new_settings)\n    end\n  end\n\n  context 'when registering implementations' do\n    it 'does not register an implementation by default' do\n      Puppet.initialize_settings\n\n      expect(Puppet.runtime[:http]).to be_an_instance_of(Puppet::HTTP::Client)\n    end\n\n    it 'allows a http implementation to be registered' do\n      http_impl = double('http')\n      Puppet.initialize_settings([], true, true, http: http_impl)\n\n\n      expect(Puppet.runtime[:http]).to eq(http_impl)\n    end\n\n    it 'allows a facter implementation to be registered' do\n      facter_impl = double('facter')\n      Puppet.initialize_settings([], true, true, facter: facter_impl)\n\n\n      expect(Puppet.runtime[:facter]).to eq(facter_impl)\n    end\n  end\n\n  context \"initializing $LOAD_PATH\" do\n    it \"should add libdir and module paths to the load path\" do\n      libdir = tmpdir('libdir_test')\n      vendor_dir = tmpdir('vendor_modules')\n      module_libdir = File.join(vendor_dir, 'amodule_core', 'lib')\n      FileUtils.mkdir_p(module_libdir)\n\n      Puppet[:libdir] = libdir\n      Puppet[:vendormoduledir] = vendor_dir\n      Puppet.initialize_settings\n\n      expect($LOAD_PATH).to include(libdir)\n      expect($LOAD_PATH).to include(module_libdir)\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/unit/relationship_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/relationship'\n\ndescribe Puppet::Relationship do\n  before do\n    @edge = Puppet::Relationship.new(:a, :b)\n  end\n\n  it \"should have a :source attribute\" do\n    expect(@edge).to respond_to(:source)\n  end\n\n  it \"should have a :target attribute\" do\n    expect(@edge).to respond_to(:target)\n  end\n\n  it \"should have a :callback attribute\" do\n    @edge.callback = :foo\n    expect(@edge.callback).to eq(:foo)\n  end\n\n  it \"should have an :event attribute\" do\n    @edge.event = :NONE\n    expect(@edge.event).to eq(:NONE)\n  end\n\n  it \"should require a callback if a non-NONE event is specified\" do\n    expect { @edge.event = :something }.to raise_error(ArgumentError)\n  end\n\n  it \"should have a :label attribute\" do\n    expect(@edge).to respond_to(:label)\n  end\n\n  it \"should provide a :ref method that describes the edge\" do\n    @edge = Puppet::Relationship.new(\"a\", \"b\")\n    expect(@edge.ref).to eq(\"a => b\")\n  end\n\n  it \"should be able to produce a label as a hash with its event and callback\" do\n    @edge.callback = :foo\n    @edge.event = :bar\n\n    expect(@edge.label).to eq({:callback => :foo, :event => :bar})\n  end\n\n  it \"should work if nil options are provided\" do\n    expect { Puppet::Relationship.new(\"a\", \"b\", nil) }.not_to raise_error\n  end\nend\n\ndescribe Puppet::Relationship, \" when initializing\" do\n  before do\n    @edge = Puppet::Relationship.new(:a, :b)\n  end\n\n  it \"should use the first argument as the source\" do\n    expect(@edge.source).to eq(:a)\n  end\n\n  it \"should use the second argument as the target\" do\n    expect(@edge.target).to eq(:b)\n  end\n\n  it \"should set the rest of the arguments as the event and callback\" do\n    @edge = Puppet::Relationship.new(:a, :b, :callback => :foo, :event => :bar)\n    expect(@edge.callback).to eq(:foo)\n    expect(@edge.event).to eq(:bar)\n  end\n\n  it \"should accept events specified as strings\" do\n    @edge = Puppet::Relationship.new(:a, :b, \"event\" => :NONE)\n    expect(@edge.event).to eq(:NONE)\n  end\n\n  it \"should accept callbacks specified as strings\" do\n    @edge = Puppet::Relationship.new(:a, :b, \"callback\" => :foo)\n    expect(@edge.callback).to eq(:foo)\n  end\nend\n\ndescribe Puppet::Relationship, \" when matching edges with no specified event\" do\n  before do\n    @edge = Puppet::Relationship.new(:a, :b)\n  end\n\n  it \"should not match :NONE\" do\n    expect(@edge).not_to be_match(:NONE)\n  end\n\n  it \"should not match :ALL_EVENTS\" do\n    expect(@edge).not_to be_match(:ALL_EVENTS)\n  end\n\n  it \"should not match any other events\" do\n    expect(@edge).not_to be_match(:whatever)\n  end\nend\n\ndescribe Puppet::Relationship, \" when matching edges with :NONE as the event\" do\n  before do\n    @edge = Puppet::Relationship.new(:a, :b, :event => :NONE)\n  end\n  it \"should not match :NONE\" do\n    expect(@edge).not_to be_match(:NONE)\n  end\n\n  it \"should not match :ALL_EVENTS\" do\n    expect(@edge).not_to be_match(:ALL_EVENTS)\n  end\n\n  it \"should not match other events\" do\n    expect(@edge).not_to be_match(:yayness)\n  end\nend\n\ndescribe Puppet::Relationship, \" when matching edges with :ALL as the event\" do\n  before do\n    @edge = Puppet::Relationship.new(:a, :b, :event => :ALL_EVENTS, :callback => :whatever)\n  end\n\n  it \"should not match :NONE\" do\n    expect(@edge).not_to be_match(:NONE)\n  end\n\n  it \"should match :ALL_EVENTS\" do\n    expect(@edge).to be_match(:ALL_EVENTS)\n  end\n\n  it \"should match all other events\" do\n    expect(@edge).to be_match(:foo)\n  end\nend\n\ndescribe Puppet::Relationship, \" when matching edges with a non-standard event\" do\n  before do\n    @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever)\n  end\n\n  it \"should not match :NONE\" do\n    expect(@edge).not_to be_match(:NONE)\n  end\n\n  it \"should not match :ALL_EVENTS\" do\n    expect(@edge).not_to be_match(:ALL_EVENTS)\n  end\n\n  it \"should match events with the same name\" do\n    expect(@edge).to be_match(:random)\n  end\nend\n\ndescribe Puppet::Relationship, \"when converting to json\" do\n  before do\n    @edge = Puppet::Relationship.new('a', 'b', :event => :random, :callback => :whatever)\n  end\n\n  it \"should store the stringified source as the source in the data\" do\n    expect(JSON.parse(@edge.to_json)[\"source\"]).to eq(\"a\")\n  end\n\n  it \"should store the stringified target as the target in the data\" do\n    expect(JSON.parse(@edge.to_json)['target']).to eq(\"b\")\n  end\n\n  it \"should store the jsonified event as the event in the data\" do\n    expect(JSON.parse(@edge.to_json)[\"event\"]).to eq(\"random\")\n  end\n\n  it \"should not store an event when none is set\" do\n    @edge.event = nil\n    expect(JSON.parse(@edge.to_json)).not_to include('event')\n  end\n\n  it \"should store the jsonified callback as the callback in the data\" do\n    @edge.callback = \"whatever\"\n    expect(JSON.parse(@edge.to_json)[\"callback\"]).to eq(\"whatever\")\n  end\n\n  it \"should not store a callback when none is set in the edge\" do\n    @edge.callback = nil\n    expect(JSON.parse(@edge.to_json)).not_to include('callback')\n  end\nend\n\ndescribe Puppet::Relationship, \"when converting from json\" do\n  it \"should pass the source in as the first argument\" do\n    expect(Puppet::Relationship.from_data_hash(\"source\" => \"mysource\", \"target\" => \"mytarget\").source).to eq('mysource')\n  end\n\n  it \"should pass the target in as the second argument\" do\n    expect(Puppet::Relationship.from_data_hash(\"source\" => \"mysource\", \"target\" => \"mytarget\").target).to eq('mytarget')\n  end\n\n  it \"should pass the event as an argument if it's provided\" do\n    expect(Puppet::Relationship.from_data_hash(\"source\" => \"mysource\", \"target\" => \"mytarget\", \"event\" => \"myevent\", \"callback\" => \"eh\").event).to eq(:myevent)\n  end\n\n  it \"should pass the callback as an argument if it's provided\" do\n    expect(Puppet::Relationship.from_data_hash(\"source\" => \"mysource\", \"target\" => \"mytarget\", \"callback\" => \"mycallback\").callback).to eq(:mycallback)\n  end\nend\n"
  },
  {
    "path": "spec/unit/reports/http_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/reports'\n\ndescribe Puppet::Reports.report(:http) do\n  subject { Puppet::Transaction::Report.new.extend(described_class) }\n\n  let(:url) { \"https://puppet.example.com/report/upload\" }\n\n  before :each do\n    Puppet[:reporturl] = url\n  end\n\n  describe \"when setting up the connection\" do\n    it \"raises if the connection fails\" do\n      stub_request(:post, url).to_raise(Errno::ECONNREFUSED.new('Connection refused - connect(2)'))\n\n      expect {\n        subject.process\n      }.to raise_error(Puppet::HTTP::HTTPError, /Request to #{url} failed after .* seconds: .*Connection refused/)\n    end\n\n    it \"configures the connection for ssl when using https\" do\n      stub_request(:post, url)\n\n      expect_any_instance_of(Net::HTTP).to receive(:start) do |http|\n        expect(http).to be_use_ssl\n        expect(http.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)\n      end\n\n      subject.process\n    end\n\n    it \"does not configure the connection for ssl when using http\" do\n      Puppet[:reporturl] = 'http://puppet.example.com:8080/the/path'\n      stub_request(:post, Puppet[:reporturl])\n\n      expect_any_instance_of(Net::HTTP).to receive(:start) do |http|\n        expect(http).to_not be_use_ssl\n      end\n\n      subject.process\n    end\n  end\n\n  describe \"when making a request\" do\n    it \"uses the path specified by the 'reporturl' setting\" do\n      req = stub_request(:post, url)\n\n      subject.process\n\n      expect(req).to have_been_requested\n    end\n\n    it \"uses the username and password specified by the 'reporturl' setting\" do\n      Puppet[:reporturl] = \"https://user:pass@puppet.example.com/report/upload\"\n\n      req = stub_request(:post, %r{/report/upload}).with(basic_auth: ['user', 'pass'])\n\n      subject.process\n\n      expect(req).to have_been_requested\n    end\n\n    it \"passes metric_id options\" do\n      stub_request(:post, url)\n\n      expect(Puppet.runtime[:http]).to receive(:post).with(anything, anything, hash_including(options: hash_including(metric_id: [:puppet, :report, :http]))).and_call_original\n\n      subject.process\n    end\n\n    it \"passes the report as YAML\" do\n      req = stub_request(:post, url).with(body: subject.to_yaml)\n\n      subject.process\n\n      expect(req).to have_been_requested\n    end\n\n    it \"sets content-type to 'application/x-yaml'\" do\n      req = stub_request(:post, url).with(headers: {'Content-Type' => 'application/x-yaml'})\n\n      subject.process\n\n      expect(req).to have_been_requested\n    end\n\n    it \"doesn't log anything if the request succeeds\" do\n      req = stub_request(:post, url).to_return(status: [200, \"OK\"])\n\n      subject.process\n\n      expect(req).to have_been_requested\n      expect(@logs).to eq([])\n    end\n\n    it \"follows redirects\" do\n      location = {headers: {'Location' => url}}\n\n      req = stub_request(:post, url)\n              .to_return(**location, status: [301, \"Moved Permanently\"]).then\n              .to_return(**location, status: [302, \"Found\"]).then\n              .to_return(**location, status: [307, \"Temporary Redirect\"]).then\n              .to_return(status: [200, \"OK\"])\n\n      subject.process\n\n      expect(req).to have_been_requested.times(4)\n    end\n\n    it \"logs an error if the request fails\" do\n      stub_request(:post, url).to_return(status: [500, \"Internal Server Error\"])\n\n      subject.process\n\n      expect(@logs).to include(having_attributes(level: :err, message: \"Unable to submit report to #{url} [500] Internal Server Error\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/reports/store_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/reports'\nrequire 'time'\nrequire 'pathname'\nrequire 'tempfile'\nrequire 'fileutils'\n\ndescribe Puppet::Reports.report(:store) do\n  describe \"#process\" do\n    include PuppetSpec::Files\n\n    before :each do\n      Puppet[:reportdir] = File.join(tmpdir('reports'), 'reports')\n    end\n\n    let(:report) do\n      # It's possible to install Psych 5 with Ruby 2.7, so check whether underlying YAML supports\n      # unsafe_load_file. This check can be removed in PUP-11718\n      if YAML.respond_to?(:unsafe_load_file)\n        report = YAML.unsafe_load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml'))\n      else\n        report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml'))\n      end\n      report.extend(described_class)\n      report\n    end\n\n    it \"should create a report directory for the client if one doesn't exist\" do\n      report.process\n\n      expect(File).to be_directory(File.join(Puppet[:reportdir], report.host))\n    end\n\n    it \"should write the report to the file in YAML\" do\n      allow(Time).to receive(:now).and_return(Time.utc(2011,01,06,12,00,00))\n      report.process\n\n      expect(File.read(File.join(Puppet[:reportdir], report.host, \"201101061200.yaml\"))).to eq(report.to_yaml)\n    end\n\n    it \"rejects invalid hostnames\" do\n      report.host = \"..\"\n      expect(Puppet::FileSystem).not_to receive(:exist?)\n      expect { report.process }.to raise_error(ArgumentError, /Invalid node/)\n    end\n  end\n\n  describe \"::destroy\" do\n    it \"rejects invalid hostnames\" do\n      expect(Puppet::FileSystem).not_to receive(:unlink)\n      expect { described_class.destroy(\"..\") }.to raise_error(ArgumentError, /Invalid node/)\n    end\n  end\n\n  describe \"::validate_host\" do\n    ['..', 'hello/', '/hello', 'he/llo', 'hello/..', '.'].each do |node|\n      it \"rejects #{node.inspect}\" do\n        expect { described_class.validate_host(node) }.to raise_error(ArgumentError, /Invalid node/)\n      end\n    end\n\n    ['.hello', 'hello.', '..hi', 'hi..'].each do |node|\n      it \"accepts #{node.inspect}\" do\n        described_class.validate_host(node)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/reports_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/reports'\n\ndescribe Puppet::Reports do\n  it \"should instance-load report types\" do\n    expect(Puppet::Reports.instance_loader(:report)).to be_instance_of(Puppet::Util::Autoload)\n  end\n\n  it \"should have a method for registering report types\" do\n    expect(Puppet::Reports).to respond_to(:register_report)\n  end\n\n  it \"should have a method for retrieving report types by name\" do\n    expect(Puppet::Reports).to respond_to(:report)\n  end\n\n  it \"should provide a method for returning documentation for all reports\" do\n    expect(Puppet::Reports).to receive(:loaded_instances).with(:report).and_return([:one, :two])\n    one = double('one', :doc => \"onedoc\")\n    two = double('two', :doc => \"twodoc\")\n    expect(Puppet::Reports).to receive(:report).with(:one).and_return(one)\n    expect(Puppet::Reports).to receive(:report).with(:two).and_return(two)\n\n    doc = Puppet::Reports.reportdocs\n    expect(doc.include?(\"onedoc\")).to be_truthy\n    expect(doc.include?(\"twodoc\")).to be_truthy\n  end\nend\n\ndescribe Puppet::Reports, \" when loading report types\" do\n  it \"should use the instance loader to retrieve report types\" do\n    expect(Puppet::Reports).to receive(:loaded_instance).with(:report, :myreporttype)\n    Puppet::Reports.report(:myreporttype)\n  end\nend\n\ndescribe Puppet::Reports, \" when registering report types\" do\n  it \"should evaluate the supplied block as code for a module\" do\n    expect(Puppet::Reports).to receive(:genmodule).and_return(Module.new)\n    Puppet::Reports.register_report(:testing) { }\n  end\n\n  it \"should allow a successful report to be reloaded\" do\n    Puppet::Reports.register_report(:testing) { }\n    Puppet::Reports.register_report(:testing) { }\n  end\n\n  it \"should allow a failed report to be reloaded and show the correct exception both times\" do\n    expect { Puppet::Reports.register_report(:testing) { raise TypeError, 'failed report' } }.to raise_error(TypeError)\n    expect { Puppet::Reports.register_report(:testing) { raise TypeError, 'failed report' } }.to raise_error(TypeError)\n  end\n\n  it \"should extend the report type with the Puppet::Util::Docs module\" do\n    mod = double('module', :define_method => true)\n\n    expect(Puppet::Reports).to receive(:genmodule).with(anything, hash_including(extend: Puppet::Util::Docs)).and_return(mod)\n    Puppet::Reports.register_report(:testing) { }\n  end\n\n  it \"should define a :report_name method in the module that returns the name of the report\" do\n    mod = double('module')\n    expect(mod).to receive(:define_method).with(:report_name)\n\n    expect(Puppet::Reports).to receive(:genmodule).and_return(mod)\n    Puppet::Reports.register_report(:testing) { }\n  end\nend\n"
  },
  {
    "path": "spec/unit/resource/catalog_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\n\nrequire 'matchers/json'\n\ndescribe Puppet::Resource::Catalog, \"when compiling\" do\n  include JSONMatchers\n  include PuppetSpec::Files\n\n  before do\n    @basepath = make_absolute(\"/somepath\")\n    # stub this to not try to create state.yaml\n    allow(Puppet::Util::Storage).to receive(:store)\n  end\n\n  it \"should support json, dot, yaml\" do\n    # msgpack and pson are optional, so using include instead of eq\n    expect(Puppet::Resource::Catalog.supported_formats).to include(:json, :dot, :yaml)\n  end\n\n  # audit only resources are unmanaged\n  # as are resources without properties with should values\n  it \"should write its managed resources' types, namevars\" do\n    catalog = Puppet::Resource::Catalog.new(\"host\")\n\n    resourcefile = tmpfile('resourcefile')\n    Puppet[:resourcefile] = resourcefile\n\n    res = Puppet::Type.type('file').new(:title => File.expand_path('/tmp/sam'), :ensure => 'present')\n    res.file = 'site.pp'\n    res.line = 21\n\n    res2 = Puppet::Type.type('exec').new(:title => 'bob', :command => \"#{File.expand_path('/bin/rm')} -rf /\")\n    res2.file = File.expand_path('/modules/bob/manifests/bob.pp')\n    res2.line = 42\n\n    res3 = Puppet::Type.type('file').new(:title => File.expand_path('/tmp/susan'), :audit => 'all')\n    res3.file = 'site.pp'\n    res3.line = 63\n\n    res4 = Puppet::Type.type('file').new(:title => File.expand_path('/tmp/lilly'))\n    res4.file = 'site.pp'\n    res4.line = 84\n\n    comp_res = Puppet::Type.type('component').new(:title => 'Class[Main]')\n\n    catalog.add_resource(res, res2, res3, res4, comp_res)\n    catalog.write_resource_file\n    expect(File.readlines(resourcefile).map(&:chomp)).to match_array([\n      \"file[#{res.title.downcase}]\",\n      \"exec[#{res2.title.downcase}]\"\n    ])\n  end\n\n  it \"should log an error if unable to write to the resource file\" do\n    catalog = Puppet::Resource::Catalog.new(\"host\")\n    Puppet[:resourcefile] = File.expand_path('/not/writable/file')\n\n    catalog.add_resource(Puppet::Type.type('file').new(:title => File.expand_path('/tmp/foo')))\n    catalog.write_resource_file\n    expect(@logs.size).to eq(1)\n    expect(@logs.first.message).to match(/Could not create resource file/)\n    expect(@logs.first.level).to eq(:err)\n  end\n\n  it \"should be able to write its list of classes to the class file\" do\n    @catalog = Puppet::Resource::Catalog.new(\"host\")\n\n    @catalog.add_class \"foo\", \"bar\"\n\n    Puppet[:classfile] = File.expand_path(\"/class/file\")\n\n    fh = double('filehandle')\n    classfile = Puppet.settings.setting(:classfile)\n    expect(Puppet::FileSystem).to receive(:open).with(classfile.value, classfile.mode.to_i(8), \"w:UTF-8\").and_yield(fh)\n\n    expect(fh).to receive(:puts).with(\"foo\\nbar\")\n\n    @catalog.write_class_file\n  end\n\n  it \"should have a client_version attribute\" do\n    @catalog = Puppet::Resource::Catalog.new(\"host\")\n    @catalog.client_version = 5\n    expect(@catalog.client_version).to eq(5)\n  end\n\n  it \"should have a server_version attribute\" do\n    @catalog = Puppet::Resource::Catalog.new(\"host\")\n    @catalog.server_version = 5\n    expect(@catalog.server_version).to eq(5)\n  end\n\n  it \"defaults code_id to nil\" do\n    catalog = Puppet::Resource::Catalog.new(\"host\")\n    expect(catalog.code_id).to be_nil\n  end\n\n  it \"should include a catalog_uuid\" do\n    allow(SecureRandom).to receive(:uuid).and_return(\"827a74c8-cf98-44da-9ff7-18c5e4bee41e\")\n    catalog = Puppet::Resource::Catalog.new(\"host\")\n    expect(catalog.catalog_uuid).to eq(\"827a74c8-cf98-44da-9ff7-18c5e4bee41e\")\n  end\n\n  it \"should include the current catalog_format\" do\n    catalog = Puppet::Resource::Catalog.new(\"host\")\n    expect(catalog.catalog_format).to eq(2)\n  end\n\n  describe \"when compiling\" do\n    it \"should accept tags\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      config.tag(\"one\")\n      expect(config).to be_tagged(\"one\")\n    end\n\n    it \"should accept multiple tags at once\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      config.tag(\"one\", \"two\")\n      expect(config).to be_tagged(\"one\")\n      expect(config).to be_tagged(\"two\")\n    end\n\n    it \"should convert all tags to strings\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      config.tag(\"one\", :two)\n      expect(config).to be_tagged(\"one\")\n      expect(config).to be_tagged(\"two\")\n    end\n\n    it \"should tag with both the qualified name and the split name\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      config.tag(\"one::two\")\n      expect(config).to be_tagged(\"one\")\n      expect(config).to be_tagged(\"one::two\")\n    end\n\n    it \"should accept classes\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      config.add_class(\"one\")\n      expect(config.classes).to eq(%w{one})\n      config.add_class(\"two\", \"three\")\n      expect(config.classes).to eq(%w{one two three})\n    end\n\n    it \"should tag itself with passed class names\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      config.add_class(\"one\")\n      expect(config).to be_tagged(\"one\")\n    end\n\n    it \"handles resource titles with brackets\" do\n      config = Puppet::Resource::Catalog.new(\"mynode\")\n      expect(config.title_key_for_ref(\"Notify[[foo]bar]\")).to eql([\"Notify\", \"[foo]bar\"])\n    end\n  end\n\n  describe \"when converting to a RAL catalog\" do\n    before do\n      @original = Puppet::Resource::Catalog.new(\"mynode\")\n      @original.tag(*%w{one two three})\n      @original.add_class(*%w{four five six})\n\n      @top            = Puppet::Resource.new :class, 'top'\n      @topobject      = Puppet::Resource.new :file, @basepath+'/topobject'\n      @middle         = Puppet::Resource.new :class, 'middle'\n      @middleobject   = Puppet::Resource.new :file, @basepath+'/middleobject'\n      @bottom         = Puppet::Resource.new :class, 'bottom'\n      @bottomobject   = Puppet::Resource.new :file, @basepath+'/bottomobject'\n\n      @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject]\n\n      @original.add_resource(*@resources)\n\n      @original.add_edge(@top, @topobject)\n      @original.add_edge(@top, @middle)\n      @original.add_edge(@middle, @middleobject)\n      @original.add_edge(@middle, @bottom)\n      @original.add_edge(@bottom, @bottomobject)\n\n      @original.catalog_format = 1\n      @catalog = @original.to_ral\n    end\n\n    it \"should add all resources as RAL instances\" do\n      @resources.each do |resource|\n        # Warning: a failure here will result in \"global resource iteration is\n        # deprecated\" being raised, because the rspec rendering to get the\n        # result tries to call `each` on the resource, and that raises.\n        expect(@catalog.resource(resource.ref)).to be_a_kind_of(Puppet::Type)\n      end\n    end\n\n    it \"should raise if an unknown resource is being converted\" do\n      @new_res = Puppet::Resource.new \"Unknown\", \"type\", :kind => 'compilable_type'\n      @resource_array = [@new_res]\n\n      @original.add_resource(*@resource_array)\n      @original.add_edge(@bottomobject, @new_res)\n\n      @original.catalog_format = 2\n\n      expect { @original.to_ral }.to raise_error(Puppet::Error, \"Resource type 'Unknown' was not found\")\n    end\n\n    it \"should copy the tag list to the new catalog\" do\n      expect(@catalog.tags.sort).to eq(@original.tags.sort)\n    end\n\n    it \"should copy the class list to the new catalog\" do\n      expect(@catalog.classes).to eq(@original.classes)\n    end\n\n    it \"should duplicate the original edges\" do\n      @original.edges.each do |edge|\n        expect(@catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref))).to be_truthy\n      end\n    end\n\n    it \"should set itself as the catalog for each converted resource\" do\n      @catalog.vertices.each { |v| expect(v.catalog.object_id).to eql(@catalog.object_id) }\n    end\n\n    # This tests #931.\n    it \"should not lose track of resources whose names vary\" do\n      changer = Puppet::Resource.new :file, @basepath+'/test/', :parameters => {:ensure => :directory}\n\n      config = Puppet::Resource::Catalog.new('test')\n      config.add_resource(changer)\n      config.add_resource(@top)\n\n      config.add_edge(@top, changer)\n\n      catalog = config.to_ral\n      expect(catalog.resource(\"File[#{@basepath}/test/]\")).to equal(catalog.resource(\"File[#{@basepath}/test]\"))\n    end\n\n    after do\n      # Remove all resource instances.\n      @catalog.clear(true)\n    end\n  end\n\n  describe \"when filtering\" do\n    before :each do\n      @original = Puppet::Resource::Catalog.new(\"mynode\")\n      @original.tag(*%w{one two three})\n      @original.add_class(*%w{four five six})\n\n      @r1 = double(\n        'r1',\n        :ref      => \"File[/a]\",\n        :[]       => nil,\n        :virtual  => nil,\n        :catalog= => nil,\n      )\n      allow(@r1).to receive(:respond_to?)\n      allow(@r1).to receive(:respond_to?).with(:ref).and_return(true)\n      allow(@r1).to receive(:copy_as_resource).and_return(@r1)\n      allow(@r1).to receive(:is_a?).with(Puppet::Resource).and_return(true)\n\n      @r2 = double(\n        'r2',\n        :ref      => \"File[/b]\",\n        :[]       => nil,\n        :virtual  => nil,\n        :catalog= => nil,\n      )\n      allow(@r2).to receive(:respond_to?)\n      allow(@r2).to receive(:respond_to?).with(:ref).and_return(true)\n      allow(@r2).to receive(:copy_as_resource).and_return(@r2)\n      allow(@r2).to receive(:is_a?).with(Puppet::Resource).and_return(true)\n\n      @resources = [@r1,@r2]\n\n      @original.add_resource(@r1,@r2)\n    end\n\n    it \"should transform the catalog to a resource catalog\" do\n      expect(@original).to receive(:to_catalog).with(:to_resource)\n\n      @original.filter\n    end\n\n    it \"should scan each catalog resource in turn and apply filtering block\" do\n      @resources.each { |r| expect(r).to receive(:test?) }\n      @original.filter do |r|\n        r.test?\n      end\n    end\n\n    it \"should filter out resources which produce true when the filter block is evaluated\" do\n      expect(@original.filter do |r|\n        r == @r1\n      end.resource(\"File[/a]\")).to be_nil\n    end\n\n    it \"should not consider edges against resources that were filtered out\" do\n      @original.add_edge(@r1,@r2)\n      expect(@original.filter do |r|\n        r == @r1\n      end.edge?(@r1,@r2)).not_to be\n    end\n\n    it \"copies the version\" do\n      @original.version = '123'\n      expect(@original.filter.version).to eq(@original.version)\n    end\n\n    it 'copies the code_id' do\n      @original.code_id = 'b59e5df0578ef411f773ee6c33d8073c50e7b8fe'\n      expect(@original.filter.code_id).to eq(@original.code_id)\n    end\n\n    it 'copies the catalog_uuid' do\n      @original.catalog_uuid = '827a74c8-cf98-44da-9ff7-18c5e4bee41e'\n      expect(@original.filter.catalog_uuid).to eq(@original.catalog_uuid)\n    end\n\n    it 'copies the catalog_format' do\n      @original.catalog_format = 42\n      expect(@original.filter.catalog_format).to eq(@original.catalog_format)\n    end\n  end\n\n  describe \"when functioning as a resource container\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new(\"host\")\n      @one = Puppet::Type.type(:notify).new :name => \"one\"\n      @two = Puppet::Type.type(:notify).new :name => \"two\"\n      @three = Puppet::Type.type(:notify).new :name => \"three\"\n      @dupe = Puppet::Type.type(:notify).new :name => \"one\"\n    end\n\n    it \"should provide a method to add one or more resources\" do\n      @catalog.add_resource @one, @two\n      expect(@catalog.resource(@one.ref)).to equal(@one)\n      expect(@catalog.resource(@two.ref)).to equal(@two)\n    end\n\n    it \"should add resources to the relationship graph if it exists\" do\n      relgraph = @catalog.relationship_graph\n\n      @catalog.add_resource @one\n\n      expect(relgraph).to be_vertex(@one)\n    end\n\n    it \"should set itself as the resource's catalog if it is not a relationship graph\" do\n      expect(@one).to receive(:catalog=).with(@catalog)\n      @catalog.add_resource @one\n    end\n\n    it \"should make all vertices available by resource reference\" do\n      @catalog.add_resource(@one)\n      expect(@catalog.resource(@one.ref)).to equal(@one)\n      expect(@catalog.vertices.find { |r| r.ref == @one.ref }).to equal(@one)\n    end\n\n    it \"tracks the container through edges\" do\n      @catalog.add_resource(@two)\n      @catalog.add_resource(@one)\n\n      @catalog.add_edge(@one, @two)\n\n      expect(@catalog.container_of(@two)).to eq(@one)\n    end\n\n    it \"a resource without a container is contained in nil\" do\n      @catalog.add_resource(@one)\n\n      expect(@catalog.container_of(@one)).to be_nil\n    end\n\n    it \"should canonize how resources are referred to during retrieval when both type and title are provided\" do\n      @catalog.add_resource(@one)\n      expect(@catalog.resource(\"notify\", \"one\")).to equal(@one)\n    end\n\n    it \"should canonize how resources are referred to during retrieval when just the title is provided\" do\n      @catalog.add_resource(@one)\n      expect(@catalog.resource(\"notify[one]\", nil)).to equal(@one)\n    end\n\n    it \"adds resources before an existing resource\" do\n      @catalog.add_resource(@one)\n      @catalog.add_resource_before(@one, @two, @three)\n\n      expect(@catalog.resources).to eq([@two, @three, @one])\n    end\n\n    it \"raises if adding a resource before a resource not in the catalog\" do\n      expect {\n        @catalog.add_resource_before(@one, @two)\n      }.to raise_error(ArgumentError, \"Cannot add resource Notify[two] before Notify[one] because Notify[one] is not yet in the catalog\")\n    end\n\n    it \"adds resources after an existing resource in reverse order\" do\n      @catalog.add_resource(@one)\n      @catalog.add_resource_after(@one, @two, @three)\n\n      expect(@catalog.resources).to eq([@one, @three, @two])\n    end\n\n    it \"raises if adding a resource after a resource not in the catalog\" do\n      expect {\n        @catalog.add_resource_after(@one, @two)\n      }.to raise_error(ArgumentError, \"Cannot add resource Notify[two] after Notify[one] because Notify[one] is not yet in the catalog\")\n    end\n\n    describe 'with a duplicate resource' do\n      def resource_at(type, name, file, line)\n        resource = Puppet::Resource.new(type, name)\n        resource.file = file\n        resource.line = line\n\n        Puppet::Type.type(type).new(resource)\n      end\n\n      let(:orig) { resource_at(:notify, 'duplicate-title', '/path/to/orig/file', 42) }\n      let(:dupe) { resource_at(:notify, 'duplicate-title', '/path/to/dupe/file', 314) }\n\n      it \"should print the locations of the original duplicated resource\" do\n        @catalog.add_resource(orig)\n\n        expect { @catalog.add_resource(dupe) }.to raise_error { |error|\n          expect(error).to be_a Puppet::Resource::Catalog::DuplicateResourceError\n\n          expect(error.message).to match %r[Duplicate declaration: Notify\\[duplicate-title\\] is already declared]\n          expect(error.message).to match %r[at \\(file: /path/to/orig/file, line: 42\\)]\n          expect(error.message).to match %r[cannot redeclare]\n          expect(error.message).to match %r[\\(file: /path/to/dupe/file, line: 314\\)]\n        }\n      end\n    end\n\n    it \"should remove all resources when asked\" do\n      @catalog.add_resource @one\n      @catalog.add_resource @two\n      expect(@one).to receive(:remove)\n      expect(@two).to receive(:remove)\n      @catalog.clear(true)\n    end\n\n    it \"should support a mechanism for finishing resources\" do\n      expect(@one).to receive(:finish)\n      expect(@two).to receive(:finish)\n      @catalog.add_resource @one\n      @catalog.add_resource @two\n\n      @catalog.finalize\n    end\n\n    it \"should make default resources when finalizing\" do\n      expect(@catalog).to receive(:make_default_resources)\n      @catalog.finalize\n    end\n\n    it \"should add default resources to the catalog upon creation\" do\n      @catalog.make_default_resources\n      expect(@catalog.resource(:schedule, \"daily\")).not_to be_nil\n    end\n\n    it \"should optionally support an initialization block and should finalize after such blocks\" do\n      expect(@one).to receive(:finish)\n      expect(@two).to receive(:finish)\n      Puppet::Resource::Catalog.new(\"host\") do |conf|\n        conf.add_resource @one\n        conf.add_resource @two\n      end\n    end\n\n    it \"should inform the resource that it is the resource's catalog\" do\n      expect(@one).to receive(:catalog=).with(@catalog)\n      @catalog.add_resource @one\n    end\n\n    it \"should be able to find resources by reference\" do\n      @catalog.add_resource @one\n      expect(@catalog.resource(@one.ref)).to equal(@one)\n    end\n\n    it \"should be able to find resources by reference or by type/title tuple\" do\n      @catalog.add_resource @one\n      expect(@catalog.resource(\"notify\", \"one\")).to equal(@one)\n    end\n\n    it \"should have a mechanism for removing resources\" do\n      @catalog.add_resource(@one)\n      expect(@catalog.resource(@one.ref)).to be\n      expect(@catalog.vertex?(@one)).to be_truthy\n\n      @catalog.remove_resource(@one)\n      expect(@catalog.resource(@one.ref)).to be_nil\n      expect(@catalog.vertex?(@one)).to be_falsey\n    end\n\n    it \"should have a method for creating aliases for resources\" do\n      @catalog.add_resource @one\n      @catalog.alias(@one, \"other\")\n      expect(@catalog.resource(\"notify\", \"other\")).to equal(@one)\n    end\n\n    it \"should ignore conflicting aliases that point to the aliased resource\" do\n      @catalog.alias(@one, \"other\")\n      expect { @catalog.alias(@one, \"other\") }.not_to raise_error\n    end\n\n    it \"should create aliases for isomorphic resources whose names do not match their titles\" do\n      resource = Puppet::Type::File.new(:title => \"testing\", :path => @basepath+\"/something\")\n\n      @catalog.add_resource(resource)\n\n      expect(@catalog.resource(:file, @basepath+\"/something\")).to equal(resource)\n    end\n\n    it \"should not create aliases for non-isomorphic resources whose names do not match their titles\" do\n      resource = Puppet::Type.type(:exec).new(:title => \"testing\", :command => \"echo\", :path => %w{/bin /usr/bin /usr/local/bin})\n\n      @catalog.add_resource(resource)\n\n      # Yay, I've already got a 'should' method\n      expect(@catalog.resource(:exec, \"echo\").object_id).to eq(nil.object_id)\n    end\n\n    # This test is the same as the previous, but the behaviour should be explicit.\n    it \"should alias using the class name from the resource reference, not the resource class name\" do\n      @catalog.add_resource @one\n      @catalog.alias(@one, \"other\")\n      expect(@catalog.resource(\"notify\", \"other\")).to equal(@one)\n    end\n\n    it \"should fail to add an alias if the aliased name already exists\" do\n      @catalog.add_resource @one\n      expect { @catalog.alias @two, \"one\" }.to raise_error(ArgumentError)\n    end\n\n    it \"should not fail when a resource has duplicate aliases created\" do\n      @catalog.add_resource @one\n      expect { @catalog.alias @one, \"one\" }.not_to raise_error\n    end\n\n    it \"should not create aliases that point back to the resource\" do\n      @catalog.alias(@one, \"one\")\n      expect(@catalog.resource(:notify, \"one\")).to be_nil\n    end\n\n    it \"should be able to look resources up by their aliases\" do\n      @catalog.add_resource @one\n      @catalog.alias @one, \"two\"\n      expect(@catalog.resource(:notify, \"two\")).to equal(@one)\n    end\n\n    it \"should remove resource aliases when the target resource is removed\" do\n      @catalog.add_resource @one\n      @catalog.alias(@one, \"other\")\n      expect(@one).to receive(:remove)\n      @catalog.remove_resource(@one)\n      expect(@catalog.resource(\"notify\", \"other\")).to be_nil\n    end\n\n    it \"should add an alias for the namevar when the title and name differ on isomorphic resource types\" do\n      resource = Puppet::Type.type(:file).new :path => @basepath+\"/something\", :title => \"other\", :content => \"blah\"\n      expect(resource).to receive(:isomorphic?).and_return(true)\n      @catalog.add_resource(resource)\n      expect(@catalog.resource(:file, \"other\")).to equal(resource)\n      expect(@catalog.resource(:file, @basepath+\"/something\").ref).to eq(resource.ref)\n    end\n\n    it \"should not add an alias for the namevar when the title and name differ on non-isomorphic resource types\" do\n      resource = Puppet::Type.type(:file).new :path => @basepath+\"/something\", :title => \"other\", :content => \"blah\"\n      expect(resource).to receive(:isomorphic?).and_return(false)\n      @catalog.add_resource(resource)\n      expect(@catalog.resource(:file, resource.title)).to equal(resource)\n      # We can't use .should here, because the resources respond to that method.\n      raise \"Aliased non-isomorphic resource\" if @catalog.resource(:file, resource.name)\n    end\n\n    it \"should provide a method to create additional resources that also registers the resource\" do\n      args = {:name => \"/yay\", :ensure => :file}\n      resource = double('file', :ref => \"File[/yay]\", :catalog= => @catalog, :title => \"/yay\", :[] => \"/yay\")\n      expect(Puppet::Type.type(:file)).to receive(:new).with(args).and_return(resource)\n      @catalog.create_resource :file, args\n      expect(@catalog.resource(\"File[/yay]\")).to equal(resource)\n    end\n\n    describe \"when adding resources with multiple namevars\" do\n      before :each do\n        Puppet::Type.newtype(:multiple) do\n          newparam(:color, :namevar => true)\n          newparam(:designation, :namevar => true)\n\n          def self.title_patterns\n            [ [\n                /^(\\w+) (\\w+)$/,\n                [\n                  [:color,  lambda{|x| x}],\n                  [:designation, lambda{|x| x}]\n                ]\n            ] ]\n          end\n        end\n      end\n\n      it \"should add an alias using the uniqueness key\" do\n        @resource = Puppet::Type.type(:multiple).new(:title => \"some resource\", :color => \"red\", :designation => \"5\")\n\n        @catalog.add_resource(@resource)\n        expect(@catalog.resource(:multiple, \"some resource\")).to eq(@resource)\n        expect(@catalog.resource(\"Multiple[some resource]\")).to eq(@resource)\n        expect(@catalog.resource(\"Multiple[red 5]\")).to eq(@resource)\n      end\n\n      it \"should conflict with a resource with the same uniqueness key\" do\n        @resource = Puppet::Type.type(:multiple).new(:title => \"some resource\", :color => \"red\", :designation => \"5\")\n        @other    = Puppet::Type.type(:multiple).new(:title => \"another resource\", :color => \"red\", :designation => \"5\")\n\n        @catalog.add_resource(@resource)\n        expect { @catalog.add_resource(@other) }.to raise_error(ArgumentError, /Cannot alias Multiple\\[another resource\\] to \\[\"red\", \"5\"\\].*resource \\[\"Multiple\", \"red\", \"5\"\\] already declared/)\n      end\n\n      it \"should conflict when its uniqueness key matches another resource's title\" do\n        path = make_absolute(\"/tmp/foo\")\n        @resource = Puppet::Type.type(:file).new(:title => path)\n        @other    = Puppet::Type.type(:file).new(:title => \"another file\", :path => path)\n\n        @catalog.add_resource(@resource)\n        expect { @catalog.add_resource(@other) }.to raise_error(ArgumentError, /Cannot alias File\\[another file\\] to \\[\"#{Regexp.escape(path)}\"\\].*resource \\[\"File\", \"#{Regexp.escape(path)}\"\\] already declared/)\n      end\n\n      it \"should conflict when its uniqueness key matches the uniqueness key derived from another resource's title\" do\n        @resource = Puppet::Type.type(:multiple).new(:title => \"red leader\")\n        @other    = Puppet::Type.type(:multiple).new(:title => \"another resource\", :color => \"red\", :designation => \"leader\")\n\n        @catalog.add_resource(@resource)\n        expect { @catalog.add_resource(@other) }.to raise_error(ArgumentError, /Cannot alias Multiple\\[another resource\\] to \\[\"red\", \"leader\"\\].*resource \\[\"Multiple\", \"red\", \"leader\"\\] already declared/)\n      end\n    end\n  end\n\n  describe \"when applying\" do\n    before :each do\n      @catalog = Puppet::Resource::Catalog.new(\"host\")\n\n      @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n      allow(Puppet::Transaction).to receive(:new).and_return(@transaction)\n      allow(@transaction).to receive(:evaluate)\n      allow(@transaction).to receive(:for_network_device=)\n\n      allow(Puppet.settings).to receive(:use)\n    end\n\n    it \"should create and evaluate a transaction\" do\n      expect(@transaction).to receive(:evaluate)\n      @catalog.apply\n    end\n\n    it \"should add a transaction evalution time to the report\" do\n      expect(@transaction.report).to receive(:add_times).with(:transaction_evaluation, kind_of(Numeric))\n      @catalog.apply\n    end\n\n    it \"should return the transaction\" do\n      expect(@catalog.apply).to equal(@transaction)\n    end\n\n    it \"should yield the transaction if a block is provided\" do\n      @catalog.apply do |trans|\n        expect(trans).to equal(@transaction)\n      end\n    end\n\n    it \"should default to being a host catalog\" do\n      expect(@catalog.host_config).to be_truthy\n    end\n\n    it \"should be able to be set to a non-host_config\" do\n      @catalog.host_config = false\n      expect(@catalog.host_config).to be_falsey\n    end\n\n    it \"should pass supplied tags on to the transaction\" do\n      expect(@transaction).to receive(:tags=).with(%w{one two})\n      @catalog.apply(:tags => %w{one two})\n    end\n\n    it \"should set ignoreschedules on the transaction if specified in apply()\" do\n      expect(@transaction).to receive(:ignoreschedules=).with(true)\n      @catalog.apply(:ignoreschedules => true)\n    end\n\n    it \"should detect transaction failure and report it\" do\n      allow(@transaction).to receive(:evaluate).and_raise(RuntimeError, 'transaction failed.')\n      report = Puppet::Transaction::Report.new('apply')\n\n      expect { @catalog.apply(:report => report) }.to raise_error(RuntimeError)\n      report.finalize_report\n\n      expect(report.status).to eq('failed')\n    end\n\n    describe \"host catalogs\" do\n      # super() doesn't work in the setup method for some reason\n      before do\n        @catalog.host_config = true\n        allow(Puppet::Util::Storage).to receive(:store)\n      end\n\n      it \"should initialize the state database before applying a catalog\" do\n        expect(Puppet::Util::Storage).to receive(:load)\n\n        # Short-circuit the apply, so we know we're loading before the transaction\n        expect(Puppet::Transaction).to receive(:new).and_raise(ArgumentError)\n        expect { @catalog.apply }.to raise_error(ArgumentError)\n      end\n\n      it \"should sync the state database after applying\" do\n        expect(Puppet::Util::Storage).to receive(:store)\n        allow(@transaction).to receive(:any_failed?).and_return(false)\n        @catalog.apply\n      end\n    end\n\n    describe \"non-host catalogs\" do\n      before do\n        @catalog.host_config = false\n      end\n\n      it \"should never send reports\" do\n        Puppet[:report] = true\n        Puppet[:summarize] = true\n        @catalog.apply\n      end\n\n      it \"should never modify the state database\" do\n        expect(Puppet::Util::Storage).not_to receive(:load)\n        expect(Puppet::Util::Storage).not_to receive(:store)\n        @catalog.apply\n      end\n    end\n  end\n\n  describe \"when creating a relationship graph\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new(\"host\")\n    end\n\n    it \"should get removed when the catalog is cleaned up\" do\n      expect(@catalog.relationship_graph).to receive(:clear)\n\n      @catalog.clear\n\n      expect(@catalog.instance_variable_get(\"@relationship_graph\")).to be_nil\n    end\n  end\n\n  describe \"when writing dot files\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new(\"host\")\n      @name = :test\n      @file = File.join(Puppet[:graphdir], @name.to_s + \".dot\")\n    end\n\n    it \"should only write when it is a host catalog\" do\n      expect(Puppet::FileSystem).not_to receive(:open).with(@file, 0640, \"w:UTF-8\")\n      @catalog.host_config = false\n      Puppet[:graph] = true\n      @catalog.write_graph(@name)\n    end\n  end\n\n  describe \"when indirecting\" do\n    before do\n      @real_indirection = Puppet::Resource::Catalog.indirection\n\n      @indirection = double('indirection', :name => :catalog)\n    end\n\n    it \"should use the value of the 'catalog_terminus' setting to determine its terminus class\" do\n      # Puppet only checks the terminus setting the first time you ask\n      # so this returns the object to the clean state\n      # at the expense of making this test less pure\n      Puppet::Resource::Catalog.indirection.reset_terminus_class\n\n      Puppet.settings[:catalog_terminus] = \"rest\"\n      expect(Puppet::Resource::Catalog.indirection.terminus_class).to eq(:rest)\n    end\n\n    it \"should allow the terminus class to be set manually\" do\n      Puppet::Resource::Catalog.indirection.terminus_class = :rest\n      expect(Puppet::Resource::Catalog.indirection.terminus_class).to eq(:rest)\n    end\n\n    after do\n      @real_indirection.reset_terminus_class\n    end\n  end\n\n  describe \"when converting to yaml\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new(\"me\")\n      @catalog.add_edge(\"one\", \"two\")\n    end\n\n    it \"should be able to be dumped to yaml\" do\n      expect(YAML.dump(@catalog)).to be_instance_of(String)\n    end\n  end\n\n  describe \"when converting from yaml\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new(\"me\")\n      @catalog.add_edge(\"one\", \"two\")\n\n      text = YAML.dump(@catalog)\n      @newcatalog = Puppet::Util::Yaml.safe_load(text, [Puppet::Resource::Catalog])\n    end\n\n    it \"should get converted back to a catalog\" do\n      expect(@newcatalog).to be_instance_of(Puppet::Resource::Catalog)\n    end\n\n    it \"should have all vertices\" do\n      expect(@newcatalog.vertex?(\"one\")).to be_truthy\n      expect(@newcatalog.vertex?(\"two\")).to be_truthy\n    end\n\n    it \"should have all edges\" do\n      expect(@newcatalog.edge?(\"one\", \"two\")).to be_truthy\n    end\n  end\nend\n\ndescribe Puppet::Resource::Catalog, \"when converting a resource catalog to json\" do\n  include JSONMatchers\n  include PuppetSpec::Compiler\n\n  it \"should validate an empty catalog against the schema\" do\n    empty_catalog = compile_to_catalog(\"\")\n    expect(empty_catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a noop catalog against the schema\" do\n    noop_catalog = compile_to_catalog(\"create_resources('file', {})\")\n    expect(noop_catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a single resource catalog against the schema\" do\n    catalog = compile_to_catalog(\"create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}})\")\n    expect(catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a virtual resource catalog against the schema\" do\n    catalog = compile_to_catalog(\"create_resources('@file', {'/etc/foo'=>{'ensure'=>'present'}})\\nrealize(File['/etc/foo'])\")\n    expect(catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a single exported resource catalog against the schema\" do\n    catalog = compile_to_catalog(\"create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}})\")\n    expect(catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a single sensitive parameter resource catalog against the schema\" do\n    catalog = compile_to_catalog(\"create_resources('file', {'/etc/foo'=>{'ensure'=>'present','content'=>Sensitive('hunter2')}})\")\n    expect(catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a two resource catalog against the schema\" do\n    catalog = compile_to_catalog(\"create_resources('notify', {'foo'=>{'message'=>'one'}, 'bar'=>{'message'=>'two'}})\")\n    expect(catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  it \"should validate a two parameter class catalog against the schema\" do\n    catalog = compile_to_catalog(<<-MANIFEST)\n      class multi_param_class ($one, $two) {\n        notify {'foo':\n          message => \"One is $one, two is $two\",\n        }\n      }\n\n      class {'multi_param_class':\n        one => 'hello',\n        two => 'world',\n      }\n    MANIFEST\n    expect(catalog.to_json).to validate_against('api/schemas/catalog.json')\n  end\n\n  context 'when dealing with parameters that have non-Data values' do\n    context 'and rich_data is enabled' do\n      before(:each) do\n        Puppet.push_context(\n          :loaders => Puppet::Pops::Loaders.new(Puppet.lookup(:environments).get(Puppet[:environment])),\n          :rich_data => true)\n      end\n\n      after(:each) do\n        Puppet.pop_context\n      end\n\n      let(:catalog_w_regexp)  { compile_to_catalog(\"notify {'foo': message => /[a-z]+/ }\") }\n\n      it 'should generate rich value hash for parameter values that are not Data' do\n        s = catalog_w_regexp.to_json\n        expect(s).to include('\"parameters\":{\"message\":{\"__ptype\":\"Regexp\",\"__pvalue\":\"[a-z]+\"}}')\n      end\n\n      it 'should read and convert rich value hash containing Regexp from json' do\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog_w_regexp.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Regexp)\n        expect(message).to eql(/[a-z]+/)\n      end\n\n      it 'should read and convert rich value hash containing Version from json' do\n        catalog = compile_to_catalog(\"notify {'foo': message => SemVer('1.0.0') }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(SemanticPuppet::Version)\n        expect(message).to eql(SemanticPuppet::Version.parse('1.0.0'))\n      end\n\n      it 'should read and convert rich value hash containing VersionRange from json' do\n        catalog = compile_to_catalog(\"notify {'foo': message => SemVerRange('>=1.0.0') }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(SemanticPuppet::VersionRange)\n        expect(message).to eql(SemanticPuppet::VersionRange.parse('>=1.0.0'))\n      end\n\n      it 'should read and convert rich value hash containing Timespan from json' do\n        catalog = compile_to_catalog(\"notify {'foo': message => Timespan(1234) }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Puppet::Pops::Time::Timespan)\n        expect(message).to eql(Puppet::Pops::Time::Timespan.parse('1234', '%S'))\n      end\n\n      it 'should read and convert rich value hash containing Timestamp from json' do\n        catalog = compile_to_catalog(\"notify {'foo': message => Timestamp('2016-09-15T08:32:16.123 UTC') }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Puppet::Pops::Time::Timestamp)\n        expect(message).to eql(Puppet::Pops::Time::Timestamp.parse('2016-09-15T08:32:16.123 UTC'))\n      end\n\n      it 'should read and convert rich value hash containing hash with rich data from json' do\n        catalog = compile_to_catalog(\"notify {'foo': message => { 'version' => SemVer('1.0.0'), 'time' => Timestamp('2016-09-15T08:32:16.123 UTC') }}\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Hash)\n        expect(message['version']).to eql(SemanticPuppet::Version.parse('1.0.0'))\n        expect(message['time']).to eql(Puppet::Pops::Time::Timestamp.parse('2016-09-15T08:32:16.123 UTC'))\n      end\n\n      it 'should read and convert rich value hash containing an array with rich data from json' do\n        catalog = compile_to_catalog(\"notify {'foo': message => [ SemVer('1.0.0'), Timestamp('2016-09-15T08:32:16.123 UTC') ] }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Array)\n        expect(message[0]).to eql(SemanticPuppet::Version.parse('1.0.0'))\n        expect(message[1]).to eql(Puppet::Pops::Time::Timestamp.parse('2016-09-15T08:32:16.123 UTC'))\n      end\n    end\n\n    context 'and rich_data is disabled' do\n      before(:each) do\n        Puppet[:strict] = :warning # do not want to stub out behavior in tests\n      end\n\n      around(:each) do |test|\n        Puppet.override(rich_data: false) do\n          test.run\n        end\n      end\n\n      let(:catalog_w_regexp)  { compile_to_catalog(\"notify {'foo': message => /[a-z]+/ }\") }\n\n      it 'should not generate rich value hash for parameter values that are not Data' do\n        expect(catalog_w_regexp.to_json).not_to include('\"__ptype\"')\n      end\n\n      it 'should convert parameter containing Regexp into strings' do\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog_w_regexp.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(String)\n        expect(message).to eql('/[a-z]+/')\n      end\n\n      it 'should convert parameter containing Version into string' do\n        catalog = compile_to_catalog(\"notify {'foo': message => SemVer('1.0.0') }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(String)\n        expect(message).to eql('1.0.0')\n      end\n\n      it 'should convert parameter containing VersionRange into string' do\n        catalog = compile_to_catalog(\"notify {'foo': message => SemVerRange('>=1.0.0') }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(String)\n        expect(message).to eql('>=1.0.0')\n      end\n\n      it 'should convert parameter containing Timespan into string' do\n        catalog = compile_to_catalog(\"notify {'foo': message => Timespan(1234) }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(String)\n        expect(message).to eql('0-00:20:34.0')\n      end\n\n      it 'should convert parameter containing Timestamp into string' do\n        catalog = compile_to_catalog(\"notify {'foo': message => Timestamp('2016-09-15T08:32:16.123 UTC') }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(String)\n        expect(message).to eql('2016-09-15T08:32:16.123000000 UTC')\n      end\n\n      it 'should convert param containing array with :undef entries' do\n        catalog = compile_to_catalog(\"notify {'foo': message => [ 10, undef, 20 ] }\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Array)\n        expect(message[0]).to eql(10)\n        expect(message[1]).to eql(nil)\n        expect(message[2]).to eql(20)\n      end\n\n      it 'should convert param containing hash with :undef entries' do\n        catalog = compile_to_catalog(\"notify {'foo': message => {a => undef, b => 10}}\")\n        catalog2 = Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json))\n        message = catalog2.resource('notify', 'foo')['message']\n        expect(message).to be_a(Hash)\n        expect(message.has_key?('a')).to eql(true)\n        expect(message['a']).to eql(nil)\n        expect(message['b']).to eql(10)\n      end\n\n      it 'should raise an error by default when trying to convert parameter with rich data' do\n        Puppet[:strict] = :error\n        expect {\n          catalog = compile_to_catalog(\"notify {'foo': message => Timestamp('2016-09-15T08:32:16.123 UTC') }\")\n          Puppet::Resource::Catalog.from_data_hash(JSON.parse(catalog.to_json)) \n        }.to raise_error(/Evaluation Error: Notify\\[foo\\]\\['message'\\] contains a Puppet::Pops::Time::Timestamp value. It will be converted to the String '2016-09-15T08:32:16.123000000 UTC'/)\n      end\n    end\n  end\nend\n\ndescribe Puppet::Resource::Catalog, \"when converting to json\" do\n  before do\n    @catalog = Puppet::Resource::Catalog.new(\"myhost\")\n  end\n\n  { :name => 'myhost',\n    :version => 42,\n    :code_id => 'b59e5df0578ef411f773ee6c33d8073c50e7b8fe',\n    :catalog_uuid => '827a74c8-cf98-44da-9ff7-18c5e4bee41e',\n    :catalog_format => 42\n  }.each do |param, value|\n    it \"emits a #{param} equal to #{value.inspect}\" do\n      @catalog.send(param.to_s + \"=\", value)\n      json = JSON.parse(@catalog.to_json)\n\n      expect(json[param.to_s]).to eq(@catalog.send(param))\n    end\n  end\n\n  it \"emits an array of classes\" do\n    @catalog.add_class('foo')\n    json = JSON.parse(@catalog.to_json)\n\n    expect(json['classes']).to eq(['foo'])\n  end\n\n  it \"should convert its resources to a JSON-encoded array and store it as the 'resources' data\" do\n    one = double('one', :to_data_hash => \"one_resource\", :ref => \"Foo[one]\")\n    two = double('two', :to_data_hash => \"two_resource\", :ref => \"Foo[two]\")\n\n    expect(one).to receive(:'[]').with(:alias).and_return(nil)\n    expect(two).to receive(:'[]').with(:alias).and_return(nil)\n\n    @catalog.add_resource(one)\n    @catalog.add_resource(two)\n\n    # TODO this should really guarantee sort order\n    expect(JSON.parse(@catalog.to_json,:create_additions => false)['resources'].sort).to eq([\"one_resource\", \"two_resource\"].sort)\n  end\n\n  it \"should convert its edges to a JSON-encoded array and store it as the 'edges' data\" do\n    one   = double('one',   :to_data_hash => \"one_resource\",   :ref => 'Foo[one]')\n    two   = double('two',   :to_data_hash => \"two_resource\",   :ref => 'Foo[two]')\n    three = double('three', :to_data_hash => \"three_resource\", :ref => 'Foo[three]')\n\n    @catalog.add_edge(one, two)\n    @catalog.add_edge(two, three)\n\n    expect(@catalog.edges_between(one, two  )[0]).to receive(:to_data_hash).and_return(\"one_two_json\")\n    expect(@catalog.edges_between(two, three)[0]).to receive(:to_data_hash).and_return(\"two_three_json\")\n\n    expect(JSON.parse(@catalog.to_json,:create_additions => false)['edges'].sort).to eq(%w{one_two_json two_three_json}.sort)\n  end\nend\n\ndescribe Puppet::Resource::Catalog, \"when converting from json\" do\n  before do\n    @data = {\n      'name' => \"myhost\"\n    }\n  end\n\n  it 'should create it with the provided name' do\n    @data['version'] = 50\n    @data['code_id'] = 'b59e5df0578ef411f773ee6c33d8073c50e7b8fe'\n    @data['catalog_uuid'] = '827a74c8-cf98-44da-9ff7-18c5e4bee41e'\n    @data['catalog_format'] = 42\n    @data['tags'] = %w{one two}\n    @data['classes'] = %w{one two}\n    @data['edges'] = [Puppet::Relationship.new('File[/foo]', 'File[/bar]',\n                                               :event => :one,\n                                               :callback => :refresh).to_data_hash]\n    @data['resources'] = [Puppet::Resource.new(:file, '/foo').to_data_hash,\n                          Puppet::Resource.new(:file, '/bar').to_data_hash]\n\n\n    catalog = Puppet::Resource::Catalog.from_data_hash JSON.parse @data.to_json\n\n    expect(catalog.name).to eq('myhost')\n    expect(catalog.version).to eq(@data['version'])\n    expect(catalog.code_id).to eq(@data['code_id'])\n    expect(catalog.catalog_uuid).to eq(@data['catalog_uuid'])\n    expect(catalog.catalog_format).to eq(@data['catalog_format'])\n    expect(catalog).to be_tagged('one')\n    expect(catalog).to be_tagged('two')\n\n    expect(catalog.classes).to eq(@data['classes'])\n    expect(catalog.resources.collect(&:ref)).to eq(['File[/foo]', 'File[/bar]'])\n\n    expect(catalog.edges.collect(&:event)).to eq([:one])\n    expect(catalog.edges[0].source).to eq(catalog.resource(:file, '/foo'))\n    expect(catalog.edges[0].target).to eq(catalog.resource(:file, '/bar'))\n  end\n\n  it \"defaults the catalog_format to 0\" do\n    catalog = Puppet::Resource::Catalog.from_data_hash JSON.parse @data.to_json\n    expect(catalog.catalog_format).to eq(0)\n  end\n\n  it \"should fail if the source resource cannot be found\" do\n    @data['edges'] = [Puppet::Relationship.new(\"File[/missing]\", \"File[/bar]\").to_data_hash]\n    @data['resources'] = [Puppet::Resource.new(:file, \"/bar\").to_data_hash]\n\n    expect { Puppet::Resource::Catalog.from_data_hash JSON.parse @data.to_json }.to raise_error(ArgumentError, /Could not find relationship source/)\n  end\n\n  it \"should fail if the target resource cannot be found\" do\n    @data['edges'] = [Puppet::Relationship.new(\"File[/bar]\", \"File[/missing]\").to_data_hash]\n    @data['resources'] = [Puppet::Resource.new(:file, \"/bar\").to_data_hash]\n\n    expect { Puppet::Resource::Catalog.from_data_hash JSON.parse @data.to_json }.to raise_error(ArgumentError, /Could not find relationship target/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/resource/status_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/resource/status'\n\ndescribe Puppet::Resource::Status do\n  include PuppetSpec::Files\n\n  let(:resource) { Puppet::Type.type(:file).new(:path => make_absolute(\"/my/file\")) }\n  let(:containment_path) { [\"foo\", \"bar\", \"baz\"] }\n  let(:status) { Puppet::Resource::Status.new(resource) }\n\n  before do\n    allow(resource).to receive(:pathbuilder).and_return(containment_path)\n  end\n\n  it \"should compute type and title correctly\" do\n    expect(status.resource_type).to eq(\"File\")\n    expect(status.title).to eq(make_absolute(\"/my/file\"))\n  end\n\n  [:file, :line, :evaluation_time].each do |attr|\n    it \"should support #{attr}\" do\n      status.send(attr.to_s + \"=\", \"foo\")\n      expect(status.send(attr)).to eq(\"foo\")\n    end\n  end\n\n  [:skipped, :failed, :restarted, :failed_to_restart, :changed, :out_of_sync, :scheduled].each do |attr|\n    it \"should support #{attr}\" do\n      status.send(attr.to_s + \"=\", \"foo\")\n      expect(status.send(attr)).to eq(\"foo\")\n    end\n\n    it \"should have a boolean method for determining whether it was #{attr}\" do\n      status.send(attr.to_s + \"=\", \"foo\")\n      expect(status).to send(\"be_#{attr}\")\n    end\n  end\n\n  it \"should accept a resource at initialization\" do\n    expect(Puppet::Resource::Status.new(resource).resource).not_to be_nil\n  end\n\n  it \"should set its source description to the resource's path\" do\n    expect(resource).to receive(:path).and_return(\"/my/path\")\n    expect(Puppet::Resource::Status.new(resource).source_description).to eq(\"/my/path\")\n  end\n\n  it \"should set its containment path\" do\n  expect(Puppet::Resource::Status.new(resource).containment_path).to eq(containment_path)\n  end\n\n  [:file, :line].each do |attr|\n    it \"should copy the resource's #{attr}\" do\n      expect(resource).to receive(attr).and_return(\"foo\")\n      expect(Puppet::Resource::Status.new(resource).send(attr)).to eq(\"foo\")\n    end\n  end\n\n  it \"should copy the resource's tags\" do\n    resource.tag('foo', 'bar')\n    status = Puppet::Resource::Status.new(resource)\n    expect(status).to be_tagged(\"foo\")\n    expect(status).to be_tagged(\"bar\")\n  end\n\n  it \"should always convert the resource to a string\" do\n    expect(resource).to receive(:to_s).and_return(\"foo\")\n    expect(Puppet::Resource::Status.new(resource).resource).to eq(\"foo\")\n  end\n\n  it 'should set the provider_used correctly' do\n    expected_name = if Puppet::Util::Platform.windows?\n                      'windows'\n                    else\n                      'posix'\n                    end\n    expect(status.provider_used).to eq(expected_name)\n  end\n\n  it \"should support tags\" do\n    expect(Puppet::Resource::Status.ancestors).to include(Puppet::Util::Tagging)\n  end\n\n  it \"should create a timestamp at its creation time\" do\n    expect(status.time).to be_instance_of(Time)\n  end\n\n  it \"should support adding events\" do\n    event = Puppet::Transaction::Event.new(:name => :foobar)\n    status.add_event(event)\n    expect(status.events).to eq([event])\n  end\n\n  it \"should use '<<' to add events\" do\n    event = Puppet::Transaction::Event.new(:name => :foobar)\n    expect(status << event).to equal(status)\n    expect(status.events).to eq([event])\n  end\n\n  it \"fails and records a failure event with a given message\" do\n    status.fail_with_event(\"foo fail\")\n    event = status.events[0]\n\n    expect(event.message).to eq(\"foo fail\")\n    expect(event.status).to eq(\"failure\")\n    expect(event.name).to eq(:resource_error)\n    expect(status.failed?).to be_truthy\n  end\n\n  it \"fails and records a failure event with a given exception\" do\n    error = StandardError.new(\"the message\")\n    expect(resource).to receive(:log_exception).with(error, \"Could not evaluate: the message\")\n    expect(status).to receive(:fail_with_event).with(\"the message\")\n\n    status.failed_because(error)\n  end\n\n  it \"should count the number of successful events and set changed\" do\n    3.times{ status << Puppet::Transaction::Event.new(:status => 'success') }\n    expect(status.change_count).to eq(3)\n\n    expect(status.changed).to eq(true)\n    expect(status.out_of_sync).to eq(true)\n  end\n\n  it \"should not start with any changes\" do\n    expect(status.change_count).to eq(0)\n\n    expect(status.changed).to eq(false)\n    expect(status.out_of_sync).to eq(false)\n  end\n\n  it \"should not treat failure, audit, or noop events as changed\" do\n    ['failure', 'audit', 'noop'].each do |s| status << Puppet::Transaction::Event.new(:status => s) end\n    expect(status.change_count).to eq(0)\n    expect(status.changed).to eq(false)\n  end\n\n  it \"should not treat audit events as out of sync\" do\n    status << Puppet::Transaction::Event.new(:status => 'audit')\n    expect(status.out_of_sync_count).to eq(0)\n    expect(status.out_of_sync).to eq(false)\n  end\n\n  ['failure', 'noop', 'success'].each do |event_status|\n    it \"should treat #{event_status} events as out of sync\" do\n      3.times do status << Puppet::Transaction::Event.new(:status => event_status) end\n      expect(status.out_of_sync_count).to eq(3)\n      expect(status.out_of_sync).to eq(true)\n    end\n  end\n\n  context 'when serializing' do\n    let(:status) do\n      s = Puppet::Resource::Status.new(resource)\n      s.file = '/foo.rb'\n      s.line = 27\n      s.evaluation_time = 2.7\n      s.tags = %w{one two}\n      s << Puppet::Transaction::Event.new(:name => :mode_changed, :status => 'audit')\n      s.failed = false\n      s.changed = true\n      s.out_of_sync = true\n      s.skipped = false\n      s.provider_used = 'provider_used_class_name'\n      s.failed_to_restart = false\n      s\n    end\n\n    it 'should round trip through json' do\n      expect(status.containment_path).to eq(containment_path)\n\n      tripped = Puppet::Resource::Status.from_data_hash(JSON.parse(status.to_json))\n\n      expect(tripped.title).to eq(status.title)\n      expect(tripped.containment_path).to eq(status.containment_path)\n      expect(tripped.file).to eq(status.file)\n      expect(tripped.line).to eq(status.line)\n      expect(tripped.resource).to eq(status.resource)\n      expect(tripped.resource_type).to eq(status.resource_type)\n      expect(tripped.provider_used).to eq(status.provider_used)\n      expect(tripped.evaluation_time).to eq(status.evaluation_time)\n      expect(tripped.tags).to eq(status.tags)\n      expect(tripped.time).to eq(status.time)\n      expect(tripped.failed).to eq(status.failed)\n      expect(tripped.changed).to eq(status.changed)\n      expect(tripped.out_of_sync).to eq(status.out_of_sync)\n      expect(tripped.skipped).to eq(status.skipped)\n      expect(tripped.failed_to_restart).to eq(status.failed_to_restart)\n\n      expect(tripped.change_count).to eq(status.change_count)\n      expect(tripped.out_of_sync_count).to eq(status.out_of_sync_count)\n      expect(events_as_hashes(tripped)).to eq(events_as_hashes(status))\n    end\n\n    it 'to_data_hash returns value that is instance of to Data' do\n      expect(Puppet::Pops::Types::TypeFactory.data.instance?(status.to_data_hash)).to be_truthy\n    end\n\n    def events_as_hashes(report)\n      report.events.collect do |e|\n        {\n          :audited => e.audited,\n          :property => e.property,\n          :previous_value => e.previous_value,\n          :desired_value => e.desired_value,\n          :historical_value => e.historical_value,\n          :message => e.message,\n          :name => e.name,\n          :status => e.status,\n          :time => e.time,\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/resource/type_collection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/resource/type_collection'\nrequire 'puppet/resource/type'\n\ndescribe Puppet::Resource::TypeCollection do\n  include PuppetSpec::Files\n\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n\n  before do\n    @instance = Puppet::Resource::Type.new(:hostclass, \"foo\")\n    @code = Puppet::Resource::TypeCollection.new(environment)\n  end\n\n  it \"should consider '<<' to be an alias to 'add' but should return self\" do\n    expect(@code).to receive(:add).with(\"foo\")\n    expect(@code).to receive(:add).with(\"bar\")\n    @code << \"foo\" << \"bar\"\n  end\n\n  it \"should set itself as the code collection for added resource types\" do\n    node = Puppet::Resource::Type.new(:node, \"foo\")\n\n    @code.add(node)\n    expect(@code.node(\"foo\")).to equal(node)\n\n    expect(node.resource_type_collection).to equal(@code)\n  end\n\n  it \"should store node resource types as nodes\" do\n    node = Puppet::Resource::Type.new(:node, \"foo\")\n\n    @code.add(node)\n    expect(@code.node(\"foo\")).to equal(node)\n  end\n\n  it \"should fail if a duplicate node is added\" do\n    @code.add(Puppet::Resource::Type.new(:node, \"foo\"))\n\n    expect do\n      @code.add(Puppet::Resource::Type.new(:node, \"foo\"))\n    end.to raise_error(Puppet::ParseError, /cannot redefine/)\n  end\n\n  it \"should fail if a hostclass duplicates a node\" do\n    @code.add(Puppet::Resource::Type.new(:node, \"foo\"))\n\n    expect do\n      @code.add(Puppet::Resource::Type.new(:hostclass, \"foo\"))\n    end.to raise_error(Puppet::ParseError, /Node 'foo' is already defined; cannot be redefined as a class/)\n  end\n\n  it \"should store hostclasses as hostclasses\" do\n    klass = Puppet::Resource::Type.new(:hostclass, \"foo\")\n\n    @code.add(klass)\n    expect(@code.hostclass(\"foo\")).to equal(klass)\n  end\n\n  it \"errors if an attempt is made to merge hostclasses of the same name\" do\n    klass1 = Puppet::Resource::Type.new(:hostclass, \"foo\", :doc => \"first\")\n    klass2 = Puppet::Resource::Type.new(:hostclass, \"foo\", :doc => \"second\")\n\n    expect {\n      @code.add(klass1)\n      @code.add(klass2)\n    }.to raise_error(/.*is already defined; cannot redefine/)\n  end\n\n  it \"should fail if a node duplicates a hostclass\" do\n    @code.add(Puppet::Resource::Type.new(:hostclass, \"foo\"))\n\n    expect do\n      @code.add(Puppet::Resource::Type.new(:node, \"foo\"))\n    end.to raise_error(Puppet::ParseError, /Class 'foo' is already defined; cannot be redefined as a node/)\n  end\n\n  it \"should store definitions as definitions\" do\n    define = Puppet::Resource::Type.new(:definition, \"foo\")\n\n    @code.add(define)\n    expect(@code.definition(\"foo\")).to equal(define)\n  end\n\n  it \"should fail if a duplicate definition is added\" do\n    @code.add(Puppet::Resource::Type.new(:definition, \"foo\"))\n\n    expect do\n      @code.add(Puppet::Resource::Type.new(:definition, \"foo\"))\n    end.to raise_error(Puppet::ParseError, /cannot be redefined/)\n  end\n\n  it \"should remove all nodes, classes and definitions when cleared\" do\n    loader = Puppet::Resource::TypeCollection.new(environment)\n    loader.add Puppet::Resource::Type.new(:hostclass, \"class\")\n    loader.add Puppet::Resource::Type.new(:definition, \"define\")\n    loader.add Puppet::Resource::Type.new(:node, \"node\")\n\n    loader.clear\n    expect(loader.hostclass(\"class\")).to be_nil\n    expect(loader.definition(\"define\")).to be_nil\n    expect(loader.node(\"node\")).to be_nil\n  end\n\n  describe \"when looking up names\" do\n    before do\n      @type = Puppet::Resource::Type.new(:hostclass, \"ns::klass\")\n    end\n\n    it \"should not attempt to import anything when the type is already defined\" do\n      @code.add @type\n      expect(@code.loader).not_to receive(:import)\n      expect(@code.find_hostclass(\"ns::klass\")).to equal(@type)\n    end\n\n    describe \"that need to be loaded\" do\n      it \"should use the loader to load the files\" do\n        expect(@code.loader).to receive(:try_load_fqname).with(:hostclass, \"klass\")\n        @code.find_hostclass(\"klass\")\n      end\n\n      it \"should use the loader to load the files\" do\n        expect(@code.loader).to receive(:try_load_fqname).with(:hostclass, \"ns::klass\")\n        @code.find_hostclass(\"ns::klass\")\n      end\n\n      it \"should downcase the name and downcase and array-fy the namespaces before passing to the loader\" do\n        expect(@code.loader).to receive(:try_load_fqname).with(:hostclass, \"ns::klass\")\n        @code.find_hostclass(\"ns::klass\")\n      end\n\n      it \"should use the class returned by the loader\" do\n        expect(@code.loader).to receive(:try_load_fqname).and_return(:klass)\n        expect(@code).to receive(:hostclass).with(\"ns::klass\").and_return(false)\n        expect(@code.find_hostclass(\"ns::klass\")).to eq(:klass)\n      end\n\n      it \"should return nil if the name isn't found\" do\n        allow(@code.loader).to receive(:try_load_fqname).and_return(nil)\n        expect(@code.find_hostclass(\"Ns::Klass\")).to be_nil\n      end\n\n      it \"already-loaded names at broader scopes should not shadow autoloaded names\" do\n        @code.add Puppet::Resource::Type.new(:hostclass, \"bar\")\n        expect(@code.loader).to receive(:try_load_fqname).with(:hostclass, \"foo::bar\").and_return(:foobar)\n        expect(@code.find_hostclass(\"foo::bar\")).to eq(:foobar)\n      end\n\n      context 'when debugging' do\n        # This test requires that debugging is on, it will otherwise not make a call to debug,\n        # which is the easiest way to detect that that a certain path has been taken.\n        before(:each) do\n          Puppet.debug = true\n        end\n\n        after (:each) do\n          Puppet.debug = false\n        end\n\n        it \"should not try to autoload names that we couldn't autoload in a previous step if ignoremissingtypes is enabled\" do\n          Puppet[:ignoremissingtypes] = true\n          expect(@code.loader).to receive(:try_load_fqname).with(:hostclass, \"ns::klass\").and_return(nil)\n          expect(@code.find_hostclass(\"ns::klass\")).to be_nil\n          expect(Puppet).to receive(:debug).at_least(:once).with(/Not attempting to load hostclass/)\n          expect(@code.find_hostclass(\"ns::klass\")).to be_nil\n        end\n      end\n    end\n  end\n\n  KINDS = %w{hostclass node definition}\n  KINDS.each do |data|\n    describe \"behavior of add for #{data}\" do\n\n      it \"should return the added #{data}\" do\n        loader = Puppet::Resource::TypeCollection.new(environment)\n        instance = Puppet::Resource::Type.new(data, \"foo\")\n\n        expect(loader.add(instance)).to equal(instance)\n      end\n\n      it \"should retrieve #{data} insensitive to case\" do\n        loader = Puppet::Resource::TypeCollection.new(environment)\n        instance = Puppet::Resource::Type.new(data, \"Bar\")\n\n        loader.add instance\n\n        expect(loader.send(data, \"bAr\")).to equal(instance)\n      end\n\n      it \"should return nil when asked for a #{data} that has not been added\" do\n        expect(Puppet::Resource::TypeCollection.new(environment).send(data, \"foo\")).to be_nil\n      end\n    end\n  end\n\n  describe \"when finding a qualified instance\" do\n    it \"should return any found instance if the instance name is fully qualified\" do\n      loader = Puppet::Resource::TypeCollection.new(environment)\n      instance = Puppet::Resource::Type.new(:hostclass, \"foo::bar\")\n      loader.add instance\n      expect(loader.find_hostclass(\"::foo::bar\")).to equal(instance)\n    end\n\n    it \"should return nil if the instance name is fully qualified and no such instance exists\" do\n      loader = Puppet::Resource::TypeCollection.new(environment)\n      expect(loader.find_hostclass(\"::foo::bar\")).to be_nil\n    end\n\n    it \"should be able to find classes in the base namespace\" do\n      loader = Puppet::Resource::TypeCollection.new(environment)\n      instance = Puppet::Resource::Type.new(:hostclass, \"foo\")\n      loader.add instance\n      expect(loader.find_hostclass(\"foo\")).to equal(instance)\n    end\n\n    it \"should return the unqualified object if it exists in a provided namespace\" do\n      loader = Puppet::Resource::TypeCollection.new(environment)\n      instance = Puppet::Resource::Type.new(:hostclass, \"foo::bar\")\n      loader.add instance\n      expect(loader.find_hostclass(\"foo::bar\")).to equal(instance)\n    end\n\n    it \"should return nil if the object cannot be found\" do\n      loader = Puppet::Resource::TypeCollection.new(environment)\n      instance = Puppet::Resource::Type.new(:hostclass, \"foo::bar::baz\")\n      loader.add instance\n      expect(loader.find_hostclass(\"foo::bar::eh\")).to be_nil\n    end\n\n    describe \"when topscope has a class that has the same name as a local class\" do\n      before do\n        @loader = Puppet::Resource::TypeCollection.new(environment)\n        [ \"foo::bar\", \"bar\" ].each do |name|\n          @loader.add Puppet::Resource::Type.new(:hostclass, name)\n        end\n      end\n\n      it \"looks up the given name, no more, no less\" do\n        expect(@loader.find_hostclass(\"bar\").name).to eq('bar')\n        expect(@loader.find_hostclass(\"::bar\").name).to eq('bar')\n        expect(@loader.find_hostclass(\"foo::bar\").name).to eq('foo::bar')\n        expect(@loader.find_hostclass(\"::foo::bar\").name).to eq('foo::bar')\n      end\n    end\n\n    it \"should not look in the local scope for classes when the name is qualified\" do\n        @loader = Puppet::Resource::TypeCollection.new(environment)\n        @loader.add Puppet::Resource::Type.new(:hostclass, \"foo::bar\")\n\n        expect(@loader.find_hostclass(\"::bar\")).to eq(nil)\n    end\n  end\n\n  it \"should be able to find nodes\" do\n    node = Puppet::Resource::Type.new(:node, \"bar\")\n    loader = Puppet::Resource::TypeCollection.new(environment)\n    loader.add(node)\n    expect(loader.find_node(\"bar\")).to eq(node)\n  end\n\n  it \"should indicate whether any nodes are defined\" do\n    loader = Puppet::Resource::TypeCollection.new(environment)\n    loader.add_node(Puppet::Resource::Type.new(:node, \"foo\"))\n    expect(loader).to be_nodes\n  end\n\n  it \"should indicate whether no nodes are defined\" do\n    expect(Puppet::Resource::TypeCollection.new(environment)).not_to be_nodes\n  end\n\n  describe \"when finding nodes\" do\n    before :each do\n      @loader = Puppet::Resource::TypeCollection.new(environment)\n    end\n\n    it \"should return any node whose name exactly matches the provided node name\" do\n      node = Puppet::Resource::Type.new(:node, \"foo\")\n      @loader << node\n\n      expect(@loader.node(\"foo\")).to equal(node)\n    end\n\n    it \"should return the first regex node whose regex matches the provided node name\" do\n      node1 = Puppet::Resource::Type.new(:node, /\\w/)\n      node2 = Puppet::Resource::Type.new(:node, /\\d/)\n      @loader << node1 << node2\n\n      expect(@loader.node(\"foo10\")).to equal(node1)\n    end\n\n    it \"should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name\" do\n      node1 = Puppet::Resource::Type.new(:node, /\\w/)\n      node2 = Puppet::Resource::Type.new(:node, \"foo\")\n      @loader << node1 << node2\n\n      expect(@loader.node(\"foo\")).to equal(node2)\n    end\n  end\n\n  describe \"when determining the configuration version\" do\n    before do\n      @code = Puppet::Resource::TypeCollection.new(environment)\n    end\n\n    it \"should default to the current time\" do\n      time = Time.now\n\n      allow(Time).to receive(:now).and_return(time)\n      expect(@code.version).to eq(time.to_i)\n    end\n\n    context \"when config_version script is specified\" do\n      let(:environment) { Puppet::Node::Environment.create(:testing, [], '', '/my/foo') }\n\n      it \"should use the output of the environment's config_version setting if one is provided\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with([\"/my/foo\"])\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(\"output\\n\", 0))\n        expect(@code.version).to be_instance_of(String)\n        expect(@code.version).to eq(\"output\")\n      end\n\n      it \"should raise a puppet parser error if executing config_version fails\" do\n        expect(Puppet::Util::Execution).to receive(:execute).and_raise(Puppet::ExecutionFailure.new(\"msg\"))\n\n        expect { @code.version }.to raise_error(Puppet::ParseError)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/resource/type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/resource/type'\nrequire 'puppet/pops'\nrequire 'matchers/json'\n\ndescribe Puppet::Resource::Type do\n  include JSONMatchers\n\n  it \"should have a 'name' attribute\" do\n    expect(Puppet::Resource::Type.new(:hostclass, \"foo\").name).to eq(\"foo\")\n  end\n\n  [:code, :doc, :line, :file, :resource_type_collection].each do |attr|\n    it \"should have a '#{attr}' attribute\" do\n      type = Puppet::Resource::Type.new(:hostclass, \"foo\")\n      type.send(attr.to_s + \"=\", \"yay\")\n      expect(type.send(attr)).to eq(\"yay\")\n    end\n  end\n\n  [:hostclass, :node, :definition].each do |type|\n    it \"should know when it is a #{type}\" do\n      expect(Puppet::Resource::Type.new(type, \"foo\").send(\"#{type}?\")).to be_truthy\n    end\n  end\n\n  describe \"when a node\"  do\n    it \"should allow a regex as its name\" do\n      expect { Puppet::Resource::Type.new(:node, /foo/) }.not_to raise_error\n    end\n\n    it \"should allow an AST::HostName instance as its name\" do\n      regex = Puppet::Parser::AST::Regex.new(:value => /foo/)\n      name = Puppet::Parser::AST::HostName.new(:value => regex)\n      expect { Puppet::Resource::Type.new(:node, name) }.not_to raise_error\n    end\n\n    it \"should match against the regexp in the AST::HostName when a HostName instance is provided\" do\n      regex = Puppet::Parser::AST::Regex.new(:value => /\\w/)\n      name = Puppet::Parser::AST::HostName.new(:value => regex)\n      node = Puppet::Resource::Type.new(:node, name)\n\n      expect(node.match(\"foo\")).to be_truthy\n    end\n\n    it \"should return the value of the hostname if provided a string-form AST::HostName instance as the name\" do\n      name = Puppet::Parser::AST::HostName.new(:value => \"foo\")\n      node = Puppet::Resource::Type.new(:node, name)\n\n      expect(node.name).to eq(\"foo\")\n    end\n\n    describe \"and the name is a regex\" do\n      it \"should have a method that indicates that this is the case\" do\n        expect(Puppet::Resource::Type.new(:node, /w/)).to be_name_is_regex\n      end\n\n      it \"should set its namespace to ''\" do\n        expect(Puppet::Resource::Type.new(:node, /w/).namespace).to eq(\"\")\n      end\n\n      it \"should return the regex converted to a string when asked for its name\" do\n        expect(Puppet::Resource::Type.new(:node, /ww/).name).to eq(\"__node_regexp__ww\")\n      end\n\n      it \"should downcase the regex when returning the name as a string\" do\n        expect(Puppet::Resource::Type.new(:node, /W/).name).to eq(\"__node_regexp__w\")\n      end\n\n      it \"should remove non-alpha characters when returning the name as a string\" do\n        expect(Puppet::Resource::Type.new(:node, /w*w/).name).not_to include(\"*\")\n      end\n\n      it \"should remove leading dots when returning the name as a string\" do\n        expect(Puppet::Resource::Type.new(:node, /.ww/).name).not_to match(/^\\./)\n      end\n\n      it \"should have a method for matching its regex name against a provided name\" do\n        expect(Puppet::Resource::Type.new(:node, /.ww/)).to respond_to(:match)\n      end\n\n      it \"should return true when its regex matches the provided name\" do\n        expect(Puppet::Resource::Type.new(:node, /\\w/).match(\"foo\")).to be_truthy\n      end\n\n      it \"should return true when its regex matches the provided name\" do\n        expect(Puppet::Resource::Type.new(:node, /\\w/).match(\"foo\")).to be_truthy\n      end\n\n      it \"should return false when its regex does not match the provided name\" do\n        expect(!!Puppet::Resource::Type.new(:node, /\\d/).match(\"foo\")).to be_falsey\n      end\n\n      it \"should return true when its name, as a string, is matched against an equal string\" do\n        expect(Puppet::Resource::Type.new(:node, \"foo\").match(\"foo\")).to be_truthy\n      end\n\n      it \"should return false when its name is matched against an unequal string\" do\n        expect(Puppet::Resource::Type.new(:node, \"foo\").match(\"bar\")).to be_falsey\n      end\n\n      it \"should match names insensitive to case\" do\n        expect(Puppet::Resource::Type.new(:node, \"fOo\").match(\"foO\")).to be_truthy\n      end\n    end\n  end\n\n  describe \"when initializing\" do\n    it \"should require a resource super type\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"foo\").type).to eq(:hostclass)\n    end\n\n    it \"should fail if provided an invalid resource super type\" do\n      expect { Puppet::Resource::Type.new(:nope, \"foo\") }.to raise_error(ArgumentError)\n    end\n\n    it \"should set its name to the downcased, stringified provided name\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"Foo::Bar\".intern).name).to eq(\"foo::bar\")\n    end\n\n    it \"should set its namespace to the downcased, stringified qualified name for classes\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"Foo::Bar::Baz\".intern).namespace).to eq(\"foo::bar::baz\")\n    end\n\n    [:definition, :node].each do |type|\n      it \"should set its namespace to the downcased, stringified qualified portion of the name for #{type}s\" do\n        expect(Puppet::Resource::Type.new(type, \"Foo::Bar::Baz\".intern).namespace).to eq(\"foo::bar\")\n      end\n    end\n\n    %w{code line file doc}.each do |arg|\n      it \"should set #{arg} if provided\" do\n        type = Puppet::Resource::Type.new(:hostclass, \"foo\", arg.to_sym => \"something\")\n        expect(type.send(arg)).to eq(\"something\")\n      end\n    end\n\n    it \"should set any provided arguments with the keys as symbols\" do\n      type = Puppet::Resource::Type.new(:hostclass, \"foo\", :arguments => {:foo => \"bar\", :baz => \"biz\"})\n      expect(type).to be_valid_parameter(\"foo\")\n      expect(type).to be_valid_parameter(\"baz\")\n    end\n\n    it \"should set any provided arguments with they keys as strings\" do\n      type = Puppet::Resource::Type.new(:hostclass, \"foo\", :arguments => {\"foo\" => \"bar\", \"baz\" => \"biz\"})\n      expect(type).to be_valid_parameter(:foo)\n      expect(type).to be_valid_parameter(:baz)\n    end\n\n    it \"should function if provided no arguments\" do\n      type = Puppet::Resource::Type.new(:hostclass, \"foo\")\n      expect(type).not_to be_valid_parameter(:foo)\n    end\n  end\n\n  describe \"when testing the validity of an attribute\" do\n    it \"should return true if the parameter was typed at initialization\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"foo\", :arguments => {\"foo\" => \"bar\"})).to be_valid_parameter(\"foo\")\n    end\n\n    it \"should return true if it is a metaparam\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"foo\")).to be_valid_parameter(\"require\")\n    end\n\n    it \"should return true if the parameter is named 'name'\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"foo\")).to be_valid_parameter(\"name\")\n    end\n\n    it \"should return false if it is not a metaparam and was not provided at initialization\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"foo\")).not_to be_valid_parameter(\"yayness\")\n    end\n  end\n\n  describe \"when setting its parameters in the scope\" do\n    let(:parser) { Puppet::Pops::Parser::Parser.new() }\n\n    def wrap3x(expression)\n      Puppet::Parser::AST::PopsBridge::Expression.new(:value => expression.model)\n    end\n\n    def parse_expression(expr_string)\n      wrap3x(parser.parse_string(expr_string))\n    end\n\n    def number_expression(number)\n      wrap3x(Puppet::Pops::Model::Factory.NUMBER(number))\n    end\n\n    def variable_expression(name)\n      wrap3x(Puppet::Pops::Model::Factory.QNAME(name).var())\n    end\n\n    def matchref_expression(number)\n      wrap3x(Puppet::Pops::Model::Factory.NUMBER(number).var())\n    end\n\n    before(:each) do\n      compiler = Puppet::Parser::Compiler.new(Puppet::Node.new(\"foo\"))\n      @scope = Puppet::Parser::Scope.new(compiler, :source => double(\"source\"))\n      @resource = Puppet::Parser::Resource.new(:foo, \"bar\", :scope => @scope)\n      @type = Puppet::Resource::Type.new(:definition, \"foo\")\n      @resource.environment.known_resource_types.add @type\n      Puppet.push_context(:loaders => compiler.loaders)\n    end\n\n    after(:each) do\n      Puppet.pop_context\n    end\n\n    ['module_name', 'name', 'title'].each do |variable|\n      it \"should allow #{variable} to be evaluated as param default\" do\n        @type.instance_eval { @module_name = \"bar\" }\n        @type.set_arguments :foo => variable_expression(variable)\n        @type.set_resource_parameters(@resource, @scope)\n        expect(@scope['foo']).to eq('bar')\n      end\n    end\n\n    # this test is to clarify a crazy edge case\n    # if you specify these special names as params, the resource\n    # will override the special variables\n    it \"should allow the resource to override defaults\" do\n      @type.set_arguments :name => nil\n      @resource[:name] = 'foobar'\n      @type.set_arguments :foo => variable_expression('name')\n      @type.set_resource_parameters(@resource, @scope)\n      expect(@scope['foo']).to eq('foobar')\n    end\n\n    context 'referencing a variable to the left of the default expression' do\n      it 'is possible when the referenced variable uses a default' do\n        @type.set_arguments({\n          :first => number_expression(10),\n          :second => variable_expression('first'),\n        })\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq(10)\n        expect(@scope['second']).to eq(10)\n      end\n\n      it 'is possible when the referenced variable is given a value' do\n        @type.set_arguments({\n          :first => number_expression(10),\n          :second => variable_expression('first'),\n        })\n        @resource[:first] = 2\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq(2)\n        expect(@scope['second']).to eq(2)\n      end\n\n      it 'is possible when the referenced variable is an array produced by match function' do\n        @type.set_arguments({\n          :first => parse_expression(\"'hello'.match(/(h)(.*)/)\"),\n          :second => parse_expression('$first[0]'),\n          :third => parse_expression('$first[1]')\n        })\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq(['hello', 'h', 'ello'])\n        expect(@scope['second']).to eq('hello')\n        expect(@scope['third']).to eq('h')\n      end\n\n      it 'fails when the referenced variable is unassigned' do\n        @type.set_arguments({\n          :first => nil,\n          :second => variable_expression('first'),\n        })\n        expect { @type.set_resource_parameters(@resource, @scope) }.to raise_error(\n          Puppet::Error, 'Foo[bar]: expects a value for parameter $first')\n      end\n\n      it 'does not clobber a given value' do\n        @type.set_arguments({\n          :first => number_expression(10),\n          :second => variable_expression('first'),\n        })\n        @resource[:first] = 2\n        @resource[:second] = 5\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq(2)\n        expect(@scope['second']).to eq(5)\n      end\n    end\n\n    context 'referencing a variable to the right of the default expression' do\n      before :each do\n        @type.set_arguments({\n          :first => number_expression(10),\n          :second => variable_expression('third'),\n          :third => number_expression(20)\n        })\n      end\n\n      it 'no error is raised when no defaults are evaluated' do\n        @resource[:first] = 1\n        @resource[:second] = 2\n        @resource[:third] = 3\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq(1)\n        expect(@scope['second']).to eq(2)\n        expect(@scope['third']).to eq(3)\n      end\n\n      it 'no error is raised unless the referencing default expression is evaluated' do\n        @resource[:second] = 2\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq(10)\n        expect(@scope['second']).to eq(2)\n        expect(@scope['third']).to eq(20)\n      end\n\n      it 'fails when the default expression is evaluated' do\n        @resource[:first] = 1\n        expect { @type.set_resource_parameters(@resource, @scope) }.to raise_error(Puppet::Error, 'Foo[bar]: default expression for $second tries to illegally access not yet evaluated $third')\n      end\n    end\n\n    it 'does not allow a variable to be referenced from its own default expression' do\n      @type.set_arguments({\n        :first => variable_expression('first')\n      })\n      expect { @type.set_resource_parameters(@resource, @scope) }.to raise_error(Puppet::Error, 'Foo[bar]: default expression for $first tries to illegally access not yet evaluated $first')\n    end\n\n    context 'when using match scope' do\n      it '$n evaluates to undef at the top level' do\n        @type.set_arguments({\n          :first => matchref_expression('0'),\n          :second => matchref_expression('1'),\n        })\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope).not_to include('first')\n        expect(@scope).not_to include('second')\n      end\n\n      it 'a match scope to the left of a parameter is not visible to it' do\n        @type.set_arguments({\n          :first => parse_expression(\"['hello' =~ /(h)(.*)/, $1, $2]\"),\n          :second => matchref_expression('1'),\n        })\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq([true, 'h', 'ello'])\n        expect(@scope['second']).to be_nil\n      end\n\n      it 'match scopes nests per parameter' do\n        @type.set_arguments({\n          :first => parse_expression(\"['hi' =~ /(h)(.*)/, $1, if 'foo' =~ /f(oo)/ { $1 }, $1, $2]\"),\n          :second => matchref_expression('0'),\n        })\n        @type.set_resource_parameters(@resource, @scope)\n\n        expect(@scope['first']).to eq([true, 'h', 'oo', 'h', 'i'])\n        expect(@scope['second']).to be_nil\n      end\n    end\n\n    it \"should set each of the resource's parameters as variables in the scope\" do\n      @type.set_arguments :foo => nil, :boo => nil\n      @resource[:foo] = \"bar\"\n      @resource[:boo] = \"baz\"\n\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@scope['foo']).to eq(\"bar\")\n      expect(@scope['boo']).to eq(\"baz\")\n    end\n\n    it \"should set the variables as strings\" do\n      @type.set_arguments :foo => nil\n      @resource[:foo] = \"bar\"\n\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@scope['foo']).to eq(\"bar\")\n    end\n\n    it \"should fail if any of the resource's parameters are not valid attributes\" do\n      @type.set_arguments :foo => nil\n      @resource[:boo] = \"baz\"\n\n      expect { @type.set_resource_parameters(@resource, @scope) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should evaluate and set its default values as variables for parameters not provided by the resource\" do\n      @type.set_arguments :foo => Puppet::Parser::AST::Leaf.new(:value => \"something\")\n      @type.set_resource_parameters(@resource, @scope)\n      expect(@scope['foo']).to eq(\"something\")\n    end\n\n    it \"should set all default values as parameters in the resource\" do\n      @type.set_arguments :foo => Puppet::Parser::AST::Leaf.new(:value => \"something\")\n\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@resource[:foo]).to eq(\"something\")\n    end\n\n    it \"should fail if the resource does not provide a value for a required argument\" do\n      @type.set_arguments :foo => nil\n\n      expect { @type.set_resource_parameters(@resource, @scope) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should set the resource's title as a variable if not otherwise provided\" do\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@scope['title']).to eq(\"bar\")\n    end\n\n    it \"should set the resource's name as a variable if not otherwise provided\" do\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@scope['name']).to eq(\"bar\")\n    end\n\n    it \"should set its module name in the scope if available\" do\n      @type.instance_eval { @module_name = \"mymod\" }\n\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@scope[\"module_name\"]).to eq(\"mymod\")\n    end\n\n    it \"should set its caller module name in the scope if available\" do\n      expect(@scope).to receive(:parent_module_name).and_return(\"mycaller\")\n\n      @type.set_resource_parameters(@resource, @scope)\n\n      expect(@scope[\"caller_module_name\"]).to eq(\"mycaller\")\n    end\n  end\n\n  describe \"when describing and managing parent classes\" do\n    before do\n      environment = Puppet::Node::Environment.create(:testing, [])\n      @krt = environment.known_resource_types\n      @parent = Puppet::Resource::Type.new(:hostclass, \"bar\")\n      @krt.add @parent\n\n      @child = Puppet::Resource::Type.new(:hostclass, \"foo\", :parent => \"bar\")\n      @krt.add @child\n\n      @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new(\"foo\", :environment => environment)))\n    end\n\n    it \"should be able to define a parent\" do\n      Puppet::Resource::Type.new(:hostclass, \"foo\", :parent => \"bar\")\n    end\n\n    it \"should use the code collection to find the parent resource type\" do\n      expect(@child.parent_type(@scope)).to equal(@parent)\n    end\n\n    it \"should be able to find parent nodes\" do\n      parent = Puppet::Resource::Type.new(:node, \"node_bar\")\n      @krt.add parent\n      child = Puppet::Resource::Type.new(:node, \"node_foo\", :parent => \"node_bar\")\n      @krt.add child\n\n      expect(child.parent_type(@scope)).to equal(parent)\n    end\n\n    it \"should cache a reference to the parent type\" do\n      allow(@krt).to receive(:hostclass).with(\"foo::bar\").and_return(nil)\n      expect(@krt).to receive(:hostclass).with(\"bar\").once.and_return(@parent)\n      @child.parent_type(@scope)\n      @child.parent_type\n    end\n\n    it \"should correctly state when it is another type's child\" do\n      @child.parent_type(@scope)\n      expect(@child).to be_child_of(@parent)\n    end\n\n    it \"should be considered the child of a parent's parent\" do\n      @grandchild = Puppet::Resource::Type.new(:hostclass, \"baz\", :parent => \"foo\")\n      @krt.add @grandchild\n\n      @child.parent_type(@scope)\n      @grandchild.parent_type(@scope)\n\n      expect(@grandchild).to be_child_of(@parent)\n    end\n\n    it \"should correctly state when it is not another type's child\" do\n      @notchild = Puppet::Resource::Type.new(:hostclass, \"baz\")\n      @krt.add @notchild\n\n      expect(@notchild).not_to be_child_of(@parent)\n    end\n  end\n\n  describe \"when evaluating its code\" do\n    before do\n      @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new(\"mynode\"))\n      @scope = Puppet::Parser::Scope.new @compiler\n      @resource = Puppet::Parser::Resource.new(:class, \"foo\", :scope => @scope)\n\n      # This is so the internal resource lookup works, yo.\n      @compiler.catalog.add_resource @resource\n\n      @type = Puppet::Resource::Type.new(:hostclass, \"foo\")\n      @resource.environment.known_resource_types.add @type\n    end\n\n    it \"should add node regex captures to its scope\" do\n      @type = Puppet::Resource::Type.new(:node, /f(\\w)o(.*)$/)\n      match = @type.match('foo')\n\n      code = double('code')\n      allow(@type).to receive(:code).and_return(code)\n\n      subscope = double('subscope', :compiler => @compiler)\n      expect(@scope).to receive(:newscope).with({:source => @type, :resource => @resource}).and_return(subscope)\n\n      expect(subscope).to receive(:with_guarded_scope).and_yield\n      expect(subscope).to receive(:ephemeral_from).with(match, nil, nil).and_return(subscope)\n      expect(code).to receive(:safeevaluate).with(subscope)\n\n      # Just to keep the stub quiet about intermediate calls\n      expect(@type).to receive(:set_resource_parameters).with(@resource, subscope)\n\n      @type.evaluate_code(@resource)\n    end\n\n    it \"should add hostclass names to the classes list\" do\n      @type.evaluate_code(@resource)\n      expect(@compiler.catalog.classes).to be_include(\"foo\")\n    end\n\n    it \"should not add defined resource names to the classes list\" do\n      @type = Puppet::Resource::Type.new(:definition, \"foo\")\n      @type.evaluate_code(@resource)\n      expect(@compiler.catalog.classes).not_to be_include(\"foo\")\n    end\n\n    it \"should set all of its parameters in a subscope\" do\n      subscope = double('subscope', :compiler => @compiler)\n      expect(@scope).to receive(:newscope).with({:source => @type, :resource => @resource}).and_return(subscope)\n      expect(@type).to receive(:set_resource_parameters).with(@resource, subscope)\n\n      @type.evaluate_code(@resource)\n    end\n\n    it \"should not create a subscope for the :main class\" do\n      allow(@resource).to receive(:title).and_return(:main)\n      expect(@scope).not_to receive(:newscope)\n      expect(@type).to receive(:set_resource_parameters).with(@resource, @scope)\n\n      @type.evaluate_code(@resource)\n    end\n\n    it \"should store the class scope\" do\n      @type.evaluate_code(@resource)\n      expect(@scope.class_scope(@type)).to be_instance_of(@scope.class)\n    end\n\n    it \"should still create a scope but not store it if the type is a definition\" do\n      @type = Puppet::Resource::Type.new(:definition, \"foo\")\n      @type.evaluate_code(@resource)\n      expect(@scope.class_scope(@type)).to be_nil\n    end\n\n    it \"should evaluate the AST code if any is provided\" do\n      code = double('code')\n      allow(@type).to receive(:code).and_return(code)\n      expect(code).to receive(:safeevaluate).with(kind_of(Puppet::Parser::Scope))\n\n      @type.evaluate_code(@resource)\n    end\n\n    it \"should noop if there is no code\" do\n      expect(@type).to receive(:code).and_return(nil)\n\n      @type.evaluate_code(@resource)\n    end\n\n    describe \"and it has a parent class\" do\n      before do\n        @parent_type = Puppet::Resource::Type.new(:hostclass, \"parent\")\n        @type.parent = \"parent\"\n        @parent_resource = Puppet::Parser::Resource.new(:class, \"parent\", :scope => @scope)\n\n        @compiler.add_resource @scope, @parent_resource\n\n        @type.resource_type_collection = @scope.environment.known_resource_types\n        @type.resource_type_collection.add @parent_type\n      end\n\n      it \"should evaluate the parent's resource\" do\n        @type.parent_type(@scope)\n\n        @type.evaluate_code(@resource)\n\n        expect(@scope.class_scope(@parent_type)).not_to be_nil\n      end\n\n      it \"should not evaluate the parent's resource if it has already been evaluated\" do\n        @parent_resource.evaluate\n\n        @type.parent_type(@scope)\n\n        expect(@parent_resource).not_to receive(:evaluate)\n\n        @type.evaluate_code(@resource)\n      end\n\n      it \"should use the parent's scope as its base scope\" do\n        @type.parent_type(@scope)\n\n        @type.evaluate_code(@resource)\n\n        expect(@scope.class_scope(@type).parent.object_id).to eq(@scope.class_scope(@parent_type).object_id)\n      end\n    end\n\n    describe \"and it has a parent node\" do\n      before do\n        @type = Puppet::Resource::Type.new(:node, \"foo\")\n        @parent_type = Puppet::Resource::Type.new(:node, \"parent\")\n        @type.parent = \"parent\"\n        @parent_resource = Puppet::Parser::Resource.new(:node, \"parent\", :scope => @scope)\n\n        @compiler.add_resource @scope, @parent_resource\n\n        @type.resource_type_collection = @scope.environment.known_resource_types\n        @type.resource_type_collection.add(@parent_type)\n      end\n\n      it \"should evaluate the parent's resource\" do\n        @type.parent_type(@scope)\n\n        @type.evaluate_code(@resource)\n\n        expect(@scope.class_scope(@parent_type)).not_to be_nil\n      end\n\n      it \"should not evaluate the parent's resource if it has already been evaluated\" do\n        @parent_resource.evaluate\n\n        @type.parent_type(@scope)\n\n        expect(@parent_resource).not_to receive(:evaluate)\n\n        @type.evaluate_code(@resource)\n      end\n\n      it \"should use the parent's scope as its base scope\" do\n        @type.parent_type(@scope)\n\n        @type.evaluate_code(@resource)\n\n        expect(@scope.class_scope(@type).parent.object_id).to eq(@scope.class_scope(@parent_type).object_id)\n      end\n    end\n  end\n\n  describe \"when creating a resource\" do\n    before do\n      env = Puppet::Node::Environment.create('env', [])\n      @node = Puppet::Node.new(\"foo\", :environment => env)\n      @compiler = Puppet::Parser::Compiler.new(@node)\n      @scope = Puppet::Parser::Scope.new(@compiler)\n\n      @top = Puppet::Resource::Type.new :hostclass, \"top\"\n      @middle = Puppet::Resource::Type.new :hostclass, \"middle\", :parent => \"top\"\n\n      @code = env.known_resource_types\n      @code.add @top\n      @code.add @middle\n    end\n\n    it \"should create a resource instance\" do\n      expect(@top.ensure_in_catalog(@scope)).to be_instance_of(Puppet::Parser::Resource)\n    end\n\n    it \"should set its resource type to 'class' when it is a hostclass\" do\n      expect(Puppet::Resource::Type.new(:hostclass, \"top\").ensure_in_catalog(@scope).type).to eq(\"Class\")\n    end\n\n    it \"should set its resource type to 'node' when it is a node\" do\n      expect(Puppet::Resource::Type.new(:node, \"top\").ensure_in_catalog(@scope).type).to eq(\"Node\")\n    end\n\n    it \"should fail when it is a definition\" do\n      expect { Puppet::Resource::Type.new(:definition, \"top\").ensure_in_catalog(@scope) }.to raise_error(ArgumentError)\n    end\n\n    it \"should add the created resource to the scope's catalog\" do\n      @top.ensure_in_catalog(@scope)\n\n      expect(@compiler.catalog.resource(:class, \"top\")).to be_instance_of(Puppet::Parser::Resource)\n    end\n\n    it \"should add specified parameters to the resource\" do\n      @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'})\n      expect(@compiler.catalog.resource(:class, \"top\")['one']).to eq('1')\n      expect(@compiler.catalog.resource(:class, \"top\")['two']).to eq('2')\n    end\n\n    it \"should not require params for a param class\" do\n      @top.ensure_in_catalog(@scope, {})\n      expect(@compiler.catalog.resource(:class, \"top\")).to be_instance_of(Puppet::Parser::Resource)\n    end\n\n    it \"should evaluate the parent class if one exists\" do\n      @middle.ensure_in_catalog(@scope)\n\n      expect(@compiler.catalog.resource(:class, \"top\")).to be_instance_of(Puppet::Parser::Resource)\n    end\n\n    it \"should evaluate the parent class if one exists\" do\n      @middle.ensure_in_catalog(@scope, {})\n\n      expect(@compiler.catalog.resource(:class, \"top\")).to be_instance_of(Puppet::Parser::Resource)\n    end\n\n    it \"should fail if you try to create duplicate class resources\" do\n      othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope )\n      # add the same class resource to the catalog\n      @compiler.catalog.add_resource(othertop)\n      expect { @top.ensure_in_catalog(@scope, {}) }.to raise_error(Puppet::Resource::Catalog::DuplicateResourceError)\n    end\n\n    it \"should fail to evaluate if a parent class is defined but cannot be found\" do\n      othertop = Puppet::Resource::Type.new :hostclass, \"something\", :parent => \"yay\"\n      @code.add othertop\n      expect { othertop.ensure_in_catalog(@scope) }.to raise_error(Puppet::ParseError)\n    end\n\n    it \"should not create a new resource if one already exists\" do\n      expect(@compiler.catalog).to receive(:resource).with(:class, \"top\").and_return(\"something\")\n      expect(@compiler.catalog).not_to receive(:add_resource)\n      @top.ensure_in_catalog(@scope)\n    end\n\n    it \"should return the existing resource when not creating a new one\" do\n      expect(@compiler.catalog).to receive(:resource).with(:class, \"top\").and_return(\"something\")\n      expect(@compiler.catalog).not_to receive(:add_resource)\n      expect(@top.ensure_in_catalog(@scope)).to eq(\"something\")\n    end\n\n    it \"should not create a new parent resource if one already exists and it has a parent class\" do\n      @top.ensure_in_catalog(@scope)\n\n      top_resource = @compiler.catalog.resource(:class, \"top\")\n\n      @middle.ensure_in_catalog(@scope)\n\n      expect(@compiler.catalog.resource(:class, \"top\")).to equal(top_resource)\n    end\n\n    # #795 - tag before evaluation.\n    it \"should tag the catalog with the resource tags when it is evaluated\" do\n      @middle.ensure_in_catalog(@scope)\n\n      expect(@compiler.catalog).to be_tagged(\"middle\")\n    end\n\n    it \"should tag the catalog with the parent class tags when it is evaluated\" do\n      @middle.ensure_in_catalog(@scope)\n\n      expect(@compiler.catalog).to be_tagged(\"top\")\n    end\n  end\n\n  describe \"when merging code from another instance\" do\n    def code(str)\n      Puppet::Pops::Model::Factory.literal(str)\n    end\n\n    it \"should fail unless it is a class\" do\n      expect { Puppet::Resource::Type.new(:node, \"bar\").merge(\"foo\") }.to raise_error(Puppet::Error)\n    end\n\n    it \"should fail unless the source instance is a class\" do\n      dest = Puppet::Resource::Type.new(:hostclass, \"bar\")\n      source = Puppet::Resource::Type.new(:node, \"foo\")\n      expect { dest.merge(source) }.to raise_error(Puppet::Error)\n    end\n\n    it \"should fail if both classes have different parent classes\" do\n      code = Puppet::Resource::TypeCollection.new(\"env\")\n      {\"a\" => \"b\", \"c\" => \"d\"}.each do |parent, child|\n        code.add Puppet::Resource::Type.new(:hostclass, parent)\n        code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent)\n      end\n      expect { code.hostclass(\"b\").merge(code.hostclass(\"d\")) }.to raise_error(Puppet::Error)\n    end\n\n    context 'when \"freeze_main\" is enabled and a merge is done into the main class' do\n      it \"an error is raised if there is something other than definitions in the merged class\" do\n        Puppet.settings[:freeze_main] = true\n        code = Puppet::Resource::TypeCollection.new(\"env\")\n        code.add Puppet::Resource::Type.new(:hostclass, \"\")\n        other = Puppet::Resource::Type.new(:hostclass, \"\")\n        mock = double()\n        expect(mock).to receive(:is_definitions_only?).and_return(false)\n        expect(other).to receive(:code).and_return(mock)\n        expect { code.hostclass(\"\").merge(other) }.to raise_error(Puppet::Error)\n      end\n\n      it \"an error is not raised if the merged class contains nothing but definitions\" do\n        Puppet.settings[:freeze_main] = true\n        code = Puppet::Resource::TypeCollection.new(\"env\")\n        code.add Puppet::Resource::Type.new(:hostclass, \"\")\n        other = Puppet::Resource::Type.new(:hostclass, \"\")\n        mock = double()\n        expect(mock).to receive(:is_definitions_only?).and_return(true)\n        expect(other).to receive(:code).at_least(:once).and_return(mock)\n        expect { code.hostclass(\"\").merge(other) }.not_to raise_error\n      end\n    end\n\n    it \"should copy the other class's parent if it has not parent\" do\n      dest = Puppet::Resource::Type.new(:hostclass, \"bar\")\n\n      Puppet::Resource::Type.new(:hostclass, \"parent\")\n      source = Puppet::Resource::Type.new(:hostclass, \"foo\", :parent => \"parent\")\n      dest.merge(source)\n\n      expect(dest.parent).to eq(\"parent\")\n    end\n\n    it \"should copy the other class's documentation as its docs if it has no docs\" do\n      dest = Puppet::Resource::Type.new(:hostclass, \"bar\")\n      source = Puppet::Resource::Type.new(:hostclass, \"foo\", :doc => \"yayness\")\n      dest.merge(source)\n\n      expect(dest.doc).to eq(\"yayness\")\n    end\n\n    it \"should append the other class's docs to its docs if it has any\" do\n      dest = Puppet::Resource::Type.new(:hostclass, \"bar\", :doc => \"fooness\")\n      source = Puppet::Resource::Type.new(:hostclass, \"foo\", :doc => \"yayness\")\n      dest.merge(source)\n\n      expect(dest.doc).to eq(\"foonessyayness\")\n    end\n\n    it \"should set the other class's code as its code if it has none\" do\n      dest = Puppet::Resource::Type.new(:hostclass, \"bar\")\n      source = Puppet::Resource::Type.new(:hostclass, \"foo\", :code => code(\"bar\").model)\n\n      dest.merge(source)\n\n      expect(dest.code.value).to eq(\"bar\")\n    end\n\n    it \"should append the other class's code to its code if it has any\" do\n      # PUP-3274, the code merging at the top still uses AST::BlockExpression\n      # But does not do mutating changes to code blocks, instead a new block is created\n      # with references to the two original blocks.\n      # TODO: fix this when the code merging is changed at the very top in 4x.\n      #\n      dcode = Puppet::Parser::AST::BlockExpression.new(:children => [code(\"dest\")])\n      dest = Puppet::Resource::Type.new(:hostclass, \"bar\", :code => dcode)\n\n      scode = Puppet::Parser::AST::BlockExpression.new(:children => [code(\"source\")])\n      source = Puppet::Resource::Type.new(:hostclass, \"foo\", :code => scode)\n\n      dest.merge(source)\n      expect(dest.code.children.map { |c| c.value }).to eq(%w{dest source})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/resource_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/resource'\n\ndescribe Puppet::Resource do\n  include PuppetSpec::Files\n\n  let(:basepath) { make_absolute(\"/somepath\") }\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n\n  def expect_lookup(key, options = {})\n    expectation = receive(:unchecked_key_lookup).with(Puppet::Pops::Lookup::LookupKey.new(key), any_args)\n    expectation = expectation.and_throw(options[:throws]) if options[:throws]\n    expectation = expectation.and_raise(*options[:raises]) if options[:raises]\n    expectation = expectation.and_return(options[:returns]) if options[:returns]\n\n    expect_any_instance_of(Puppet::Pops::Lookup::GlobalDataProvider).to expectation\n  end\n\n  [:catalog, :file, :line].each do |attr|\n    it \"should have an #{attr} attribute\" do\n      resource = Puppet::Resource.new(\"file\", \"/my/file\")\n      expect(resource).to respond_to(attr)\n      expect(resource).to respond_to(attr.to_s + \"=\")\n    end\n  end\n\n  it \"should have a :title attribute\" do\n    expect(Puppet::Resource.new(:user, \"foo\").title).to eq(\"foo\")\n  end\n\n  it \"should require the type and title\" do\n    expect { Puppet::Resource.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should canonize types to capitalized strings\" do\n    expect(Puppet::Resource.new(:user, \"foo\").type).to eq(\"User\")\n  end\n\n  it \"should canonize qualified types so all strings are capitalized\" do\n    expect(Puppet::Resource.new(\"foo::bar\", \"foo\").type).to eq(\"Foo::Bar\")\n  end\n\n  it \"should tag itself with its type\" do\n    expect(Puppet::Resource.new(\"file\", \"/f\")).to be_tagged(\"file\")\n  end\n\n  it \"should tag itself with its title if the title is a valid tag\" do\n    expect(Puppet::Resource.new(\"user\", \"bar\")).to be_tagged(\"bar\")\n  end\n\n  it \"should not tag itself with its title if the title is a not valid tag\" do\n    expect(Puppet::Resource.new(\"file\", \"/bar\")).not_to be_tagged(\"/bar\")\n  end\n\n  it \"should allow setting of attributes\" do\n    expect(Puppet::Resource.new(\"file\", \"/bar\", :file => \"/foo\").file).to eq(\"/foo\")\n    expect(Puppet::Resource.new(\"file\", \"/bar\", :exported => true)).to be_exported\n  end\n\n  it \"should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it\" do\n    ref = Puppet::Resource.new(:component, \"foo\")\n    expect(ref.type).to eq(\"Class\")\n    expect(ref.title).to eq(\"Foo\")\n  end\n\n  it \"should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets\" do\n    ref = Puppet::Resource.new(:component, \"foo::bar[yay]\")\n    expect(ref.type).to eq(\"Foo::Bar\")\n    expect(ref.title).to eq(\"yay\")\n  end\n\n  it \"should set the type to 'Class' if it is nil and the title contains no square brackets\" do\n    ref = Puppet::Resource.new(nil, \"yay\")\n    expect(ref.type).to eq(\"Class\")\n    expect(ref.title).to eq(\"Yay\")\n  end\n\n  it \"should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets\" do\n    ref = Puppet::Resource.new(nil, \"foo::bar[yay]\")\n    expect(ref.type).to eq(\"Foo::Bar\")\n    expect(ref.title).to eq(\"yay\")\n  end\n\n  it \"should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets\" do\n    ref = Puppet::Resource.new(nil, \"foo::bar[baz[yay]]\")\n    expect(ref.type).to eq(\"Foo::Bar\")\n    expect(ref.title).to eq(\"baz[yay]\")\n  end\n\n  it \"should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets\" do\n    ref = Puppet::Resource.new(\"foo::bar[baz]\")\n    expect(ref.type).to eq(\"Foo::Bar\")\n    expect(ref.title).to eq(\"baz\")\n  end\n\n  it \"should not interpret the title as a reference if the type is a non component or whit reference\" do\n    ref = Puppet::Resource.new(\"Notify\", \"foo::bar[baz]\")\n    expect(ref.type).to eq(\"Notify\")\n    expect(ref.title).to eq(\"foo::bar[baz]\")\n  end\n\n  it \"should be able to extract its information from a Puppet::Type instance\" do\n    ral = Puppet::Type.type(:file).new :path => basepath+\"/foo\"\n    ref = Puppet::Resource.new(ral)\n    expect(ref.type).to eq(\"File\")\n    expect(ref.title).to eq(basepath+\"/foo\")\n  end\n\n\n  it \"should fail if the title is nil and the type is not a valid resource reference string\" do\n    expect { Puppet::Resource.new(\"resource-spec-foo\") }.to raise_error(ArgumentError)\n  end\n\n  it 'should fail if strict is set and type does not exist' do\n    expect { Puppet::Resource.new('resource-spec-foo', 'title', {:strict=>true}) }.to raise_error(ArgumentError, 'Invalid resource type resource-spec-foo')\n  end\n\n  it 'should fail if strict is set and class does not exist' do\n    expect { Puppet::Resource.new('Class', 'resource-spec-foo', {:strict=>true}) }.to raise_error(ArgumentError, 'Could not find declared class resource-spec-foo')\n  end\n\n  it \"should fail if the title is a hash and the type is not a valid resource reference string\" do\n    expect { Puppet::Resource.new({:type => \"resource-spec-foo\", :title => \"bar\"}) }.\n      to raise_error ArgumentError, /Puppet::Resource.new does not take a hash/\n  end\n\n  it \"should be taggable\" do\n    expect(Puppet::Resource.ancestors).to be_include(Puppet::Util::Tagging)\n  end\n\n  it \"should have an 'exported' attribute\" do\n    resource = Puppet::Resource.new(\"file\", \"/f\")\n    resource.exported = true\n    expect(resource.exported).to eq(true)\n    expect(resource).to be_exported\n  end\n\n  describe \"and munging its type and title\" do\n    describe \"when modeling a builtin resource\" do\n      it \"should be able to find the resource type\" do\n        expect(Puppet::Resource.new(\"file\", \"/my/file\").resource_type).to equal(Puppet::Type.type(:file))\n      end\n\n      it \"should set its type to the capitalized type name\" do\n        expect(Puppet::Resource.new(\"file\", \"/my/file\").type).to eq(\"File\")\n      end\n    end\n\n    describe \"when modeling a defined resource\" do\n      describe \"that exists\" do\n        before do\n          @type = Puppet::Resource::Type.new(:definition, \"foo::bar\")\n          environment.known_resource_types.add @type\n        end\n\n        it \"should set its type to the capitalized type name\" do\n          expect(Puppet::Resource.new(\"foo::bar\", \"/my/file\", :environment => environment).type).to eq(\"Foo::Bar\")\n        end\n\n        it \"should be able to find the resource type\" do\n          expect(Puppet::Resource.new(\"foo::bar\", \"/my/file\", :environment => environment).resource_type).to equal(@type)\n        end\n\n        it \"should set its title to the provided title\" do\n          expect(Puppet::Resource.new(\"foo::bar\", \"/my/file\", :environment => environment).title).to eq(\"/my/file\")\n        end\n      end\n\n      describe \"that does not exist\" do\n        it \"should set its resource type to the capitalized resource type name\" do\n          expect(Puppet::Resource.new(\"foo::bar\", \"/my/file\").type).to eq(\"Foo::Bar\")\n        end\n      end\n    end\n\n    describe \"when modeling a node\" do\n      # Life's easier with nodes, because they can't be qualified.\n      it \"should set its type to 'Node' and its title to the provided title\" do\n        node = Puppet::Resource.new(\"node\", \"foo\")\n        expect(node.type).to eq(\"Node\")\n        expect(node.title).to eq(\"foo\")\n      end\n    end\n\n    describe \"when modeling a class\" do\n      it \"should set its type to 'Class'\" do\n        expect(Puppet::Resource.new(\"class\", \"foo\").type).to eq(\"Class\")\n      end\n\n      describe \"that exists\" do\n        before do\n          @type = Puppet::Resource::Type.new(:hostclass, \"foo::bar\")\n          environment.known_resource_types.add @type\n        end\n\n        it \"should set its title to the capitalized, fully qualified resource type\" do\n          expect(Puppet::Resource.new(\"class\", \"foo::bar\", :environment => environment).title).to eq(\"Foo::Bar\")\n        end\n\n        it \"should be able to find the resource type\" do\n          expect(Puppet::Resource.new(\"class\", \"foo::bar\", :environment => environment).resource_type).to equal(@type)\n        end\n      end\n\n      describe \"that does not exist\" do\n        it \"should set its type to 'Class' and its title to the capitalized provided name\" do\n          klass = Puppet::Resource.new(\"class\", \"foo::bar\")\n          expect(klass.type).to eq(\"Class\")\n          expect(klass.title).to eq(\"Foo::Bar\")\n        end\n      end\n\n      describe \"and its name is set to the empty string\" do\n        it \"should set its title to :main\" do\n          expect(Puppet::Resource.new(\"class\", \"\").title).to eq(:main)\n        end\n\n        describe \"and a class exists whose name is the empty string\" do # this was a bit tough to track down\n          it \"should set its title to :main\" do\n            @type = Puppet::Resource::Type.new(:hostclass, \"\")\n            environment.known_resource_types.add @type\n\n            expect(Puppet::Resource.new(\"class\", \"\", :environment => environment).title).to eq(:main)\n          end\n        end\n      end\n\n      describe \"and its name is set to :main\" do\n        it \"should set its title to :main\" do\n          expect(Puppet::Resource.new(\"class\", :main).title).to eq(:main)\n        end\n\n        describe \"and a class exists whose name is the empty string\" do # this was a bit tough to track down\n          it \"should set its title to :main\" do\n            @type = Puppet::Resource::Type.new(:hostclass, \"\")\n            environment.known_resource_types.add @type\n\n            expect(Puppet::Resource.new(\"class\", :main, :environment => environment).title).to eq(:main)\n          end\n        end\n      end\n    end\n  end\n\n  it \"should return nil when looking up resource types that don't exist\" do\n    expect(Puppet::Resource.new(\"foobar\", \"bar\").resource_type).to be_nil\n  end\n\n  it \"should not fail when an invalid parameter is used and strict mode is disabled\" do\n    type = Puppet::Resource::Type.new(:definition, \"foobar\")\n    environment.known_resource_types.add type\n    resource = Puppet::Resource.new(\"foobar\", \"/my/file\", :environment => environment)\n    resource[:yay] = true\n  end\n\n  it \"should be considered equivalent to another resource if their type and title match and no parameters are set\" do\n    expect(Puppet::Resource.new(\"file\", \"/f\")).to eq(Puppet::Resource.new(\"file\", \"/f\"))\n  end\n\n  it \"should be considered equivalent to another resource if their type, title, and parameters are equal\" do\n    expect(Puppet::Resource.new(\"file\", \"/f\", :parameters => {:foo => \"bar\"})).to eq(Puppet::Resource.new(\"file\", \"/f\", :parameters => {:foo => \"bar\"}))\n  end\n\n  it \"should not be considered equivalent to another resource if their type and title match but parameters are different\" do\n    expect(Puppet::Resource.new(\"file\", \"/f\", :parameters => {:fee => \"baz\"})).not_to eq(Puppet::Resource.new(\"file\", \"/f\", :parameters => {:foo => \"bar\"}))\n  end\n\n  it \"should not be considered equivalent to a non-resource\" do\n    expect(Puppet::Resource.new(\"file\", \"/f\")).not_to eq(\"foo\")\n  end\n\n  it \"should not be considered equivalent to another resource if their types do not match\" do\n    expect(Puppet::Resource.new(\"file\", \"/f\")).not_to eq(Puppet::Resource.new(\"exec\", \"/f\"))\n  end\n\n  it \"should not be considered equivalent to another resource if their titles do not match\" do\n    expect(Puppet::Resource.new(\"file\", \"/foo\")).not_to eq(Puppet::Resource.new(\"file\", \"/f\"))\n  end\n\n  describe \"when setting default parameters\" do\n    let(:foo_node) { Puppet::Node.new('foo', :environment => environment) }\n    let(:compiler) { Puppet::Parser::Compiler.new(foo_node) }\n    let(:scope)    { Puppet::Parser::Scope.new(compiler) }\n\n    def ast_leaf(value)\n      Puppet::Parser::AST::Leaf.new(value: value)\n    end\n\n    describe \"when the resource type is :hostclass\" do\n      let(:environment_name) { \"testing env\" }\n      let(:fact_values) { { 'a' => 1 } }\n      let(:port) { Puppet::Parser::AST::Leaf.new(:value => '80') }\n\n      def inject_and_set_defaults(resource, scope)\n        resource.resource_type.set_resource_parameters(resource, scope)\n      end\n\n      before do\n        environment.known_resource_types.add(apache)\n        scope.set_facts(fact_values)\n      end\n\n      context 'with a default value expression' do\n        let(:apache) { Puppet::Resource::Type.new(:hostclass, 'apache', :arguments => { 'port' => port }) }\n\n        context \"when no value is provided\" do\n          let(:resource) do\n            Puppet::Parser::Resource.new(\"class\", \"apache\", :scope => scope)\n          end\n\n          it \"should query the data_binding terminus using a namespaced key\" do\n            expect_lookup('lookup_options', throws: :no_such_key)\n            expect_lookup('apache::port')\n            inject_and_set_defaults(resource, scope)\n          end\n\n          it \"should use the value from the data_binding terminus\" do\n            expect_lookup('lookup_options', throws: :no_such_key)\n            expect_lookup('apache::port', returns: '443')\n\n            inject_and_set_defaults(resource, scope)\n\n            expect(resource[:port]).to eq('443')\n          end\n\n          it 'should use the default value if no value is found using the data_binding terminus' do\n            expect_lookup('lookup_options', throws: :no_such_key)\n            expect_lookup('apache::port', throws: :no_such_key)\n\n            inject_and_set_defaults(resource, scope)\n\n            expect(resource[:port]).to eq('80')\n          end\n\n          it 'should use the default value if an undef value is found using the data_binding terminus' do\n            expect_lookup('lookup_options', throws: :no_such_key)\n            expect_lookup('apache::port', returns: nil)\n\n            inject_and_set_defaults(resource, scope)\n\n            expect(resource[:port]).to eq('80')\n          end\n\n          it \"should fail with error message about data binding on a hiera failure\" do\n            expect_lookup('lookup_options', throws: :no_such_key)\n            expect_lookup('apache::port', raises: [Puppet::DataBinding::LookupError, 'Forgettabotit'])\n            expect {\n              inject_and_set_defaults(resource, scope)\n            }.to raise_error(Puppet::Error, /Lookup of key 'apache::port' failed: Forgettabotit/)\n          end\n        end\n\n        context \"when a value is provided\" do\n          let(:port_parameter) do\n            Puppet::Parser::Resource::Param.new(\n              name: 'port', value: '8080'\n            )\n          end\n\n          let(:resource) do\n            Puppet::Parser::Resource.new(\"class\", \"apache\", :scope => scope,\n              :parameters => [port_parameter])\n          end\n\n          it \"should not query the data_binding terminus\" do\n            expect(Puppet::DataBinding.indirection).not_to receive(:find)\n            inject_and_set_defaults(resource, scope)\n          end\n\n          it \"should use the value provided\" do\n            expect(Puppet::DataBinding.indirection).not_to receive(:find)\n            expect(resource[:port]).to eq('8080')\n          end\n\n          it \"should use the value from the data_binding terminus when provided value is undef\" do\n            expect_lookup('lookup_options', throws: :no_such_key)\n            expect_lookup('apache::port', returns: '443')\n\n            rs = Puppet::Parser::Resource.new(\"class\", \"apache\", :scope => scope,\n              :parameters => [Puppet::Parser::Resource::Param.new(name: 'port', value: nil)])\n\n            rs.resource_type.set_resource_parameters(rs, scope)\n            expect(rs[:port]).to eq('443')\n          end\n        end\n      end\n\n      context 'without a default value expression' do\n        let(:apache) { Puppet::Resource::Type.new(:hostclass, 'apache', :arguments => { 'port' => nil }) }\n        let(:resource) { Puppet::Parser::Resource.new(\"class\", \"apache\", :scope => scope) }\n\n        it \"should use the value from the data_binding terminus\" do\n          expect_lookup('lookup_options', throws: :no_such_key)\n          expect_lookup('apache::port', returns: '443')\n\n          inject_and_set_defaults(resource, scope)\n\n          expect(resource[:port]).to eq('443')\n        end\n\n        it \"should use an undef value from the data_binding terminus\" do\n          expect_lookup('lookup_options', throws: :no_such_key)\n          expect_lookup('apache::port', returns: nil)\n\n          inject_and_set_defaults(resource, scope)\n\n          expect(resource[:port]).to be_nil\n        end\n      end\n    end\n  end\n\n  describe \"when referring to a resource with name canonicalization\" do\n    it \"should canonicalize its own name\" do\n      res = Puppet::Resource.new(\"file\", \"/path/\")\n      expect(res.uniqueness_key).to eq([\"/path\"])\n      expect(res.ref).to eq(\"File[/path/]\")\n    end\n  end\n\n  describe \"when running in strict mode\" do\n    it \"should be strict\" do\n      expect(Puppet::Resource.new(\"file\", \"/path\", :strict => true)).to be_strict\n    end\n\n    it \"should fail if invalid parameters are used\" do\n      expect { Puppet::Resource.new(\"file\", \"/path\", :strict => true, :parameters => {:nosuchparam => \"bar\"}) }.to raise_error(Puppet::Error, /no parameter named 'nosuchparam'/)\n    end\n\n    it \"should fail if the resource type cannot be resolved\" do\n      expect { Puppet::Resource.new(\"nosuchtype\", \"/path\", :strict => true) }.to raise_error(ArgumentError, /Invalid resource type/)\n    end\n  end\n\n  describe \"when managing parameters\" do\n    before do\n      @resource = Puppet::Resource.new(\"file\", \"/my/file\")\n    end\n\n    it \"should correctly detect when provided parameters are not valid for builtin types\" do\n      expect(Puppet::Resource.new(\"file\", \"/my/file\")).not_to be_valid_parameter(\"foobar\")\n    end\n\n    it \"should correctly detect when provided parameters are valid for builtin types\" do\n      expect(Puppet::Resource.new(\"file\", \"/my/file\")).to be_valid_parameter(\"mode\")\n    end\n\n    it \"should correctly detect when provided parameters are not valid for defined resource types\" do\n      type = Puppet::Resource::Type.new(:definition, \"foobar\")\n      environment.known_resource_types.add type\n      expect(Puppet::Resource.new(\"foobar\", \"/my/file\", :environment => environment)).not_to be_valid_parameter(\"myparam\")\n    end\n\n    it \"should correctly detect when provided parameters are valid for defined resource types\" do\n      type = Puppet::Resource::Type.new(:definition, \"foobar\", :arguments => {\"myparam\" => nil})\n      environment.known_resource_types.add type\n      expect(Puppet::Resource.new(\"foobar\", \"/my/file\", :environment => environment)).to be_valid_parameter(\"myparam\")\n    end\n\n    it \"should allow setting and retrieving of parameters\" do\n      @resource[:foo] = \"bar\"\n      expect(@resource[:foo]).to eq(\"bar\")\n    end\n\n    it \"should allow setting of parameters at initialization\" do\n      expect(Puppet::Resource.new(\"file\", \"/my/file\", :parameters => {:foo => \"bar\"})[:foo]).to eq(\"bar\")\n    end\n\n    it \"should canonicalize retrieved parameter names to treat symbols and strings equivalently\" do\n      @resource[:foo] = \"bar\"\n      expect(@resource[\"foo\"]).to eq(\"bar\")\n    end\n\n    it \"should canonicalize set parameter names to treat symbols and strings equivalently\" do\n      @resource[\"foo\"] = \"bar\"\n      expect(@resource[:foo]).to eq(\"bar\")\n    end\n\n    it \"should set the namevar when asked to set the name\" do\n      resource = Puppet::Resource.new(\"user\", \"bob\")\n      allow(Puppet::Type.type(:user)).to receive(:key_attributes).and_return([:myvar])\n      resource[:name] = \"bob\"\n      expect(resource[:myvar]).to eq(\"bob\")\n    end\n\n    it \"should return the namevar when asked to return the name\" do\n      resource = Puppet::Resource.new(\"user\", \"bob\")\n      allow(Puppet::Type.type(:user)).to receive(:key_attributes).and_return([:myvar])\n      resource[:myvar] = \"test\"\n      expect(resource[:name]).to eq(\"test\")\n    end\n\n    it \"should be able to set the name for non-builtin types\" do\n      resource = Puppet::Resource.new(:foo, \"bar\")\n      resource[:name] = \"eh\"\n      expect { resource[:name] = \"eh\" }.to_not raise_error\n    end\n\n    it \"should be able to return the name for non-builtin types\" do\n      resource = Puppet::Resource.new(:foo, \"bar\")\n      resource[:name] = \"eh\"\n      expect(resource[:name]).to eq(\"eh\")\n    end\n\n    it \"should be able to iterate over parameters\" do\n      @resource[:foo] = \"bar\"\n      @resource[:fee] = \"bare\"\n      params = {}\n      @resource.each do |key, value|\n        params[key] = value\n      end\n      expect(params).to eq({:foo => \"bar\", :fee => \"bare\"})\n    end\n\n    it \"should include Enumerable\" do\n      expect(@resource.class.ancestors).to be_include(Enumerable)\n    end\n\n    it \"should have a method for testing whether a parameter is included\" do\n      @resource[:foo] = \"bar\"\n      expect(@resource).to be_has_key(:foo)\n      expect(@resource).not_to be_has_key(:eh)\n    end\n\n    it \"should have a method for providing the list of parameters\" do\n      @resource[:foo] = \"bar\"\n      @resource[:bar] = \"foo\"\n      keys = @resource.keys\n      expect(keys).to be_include(:foo)\n      expect(keys).to be_include(:bar)\n    end\n\n    it \"should have a method for providing the number of parameters\" do\n      @resource[:foo] = \"bar\"\n      expect(@resource.length).to eq(1)\n    end\n\n    it \"should have a method for deleting parameters\" do\n      @resource[:foo] = \"bar\"\n      @resource.delete(:foo)\n      expect(@resource[:foo]).to be_nil\n    end\n\n    it \"should have a method for testing whether the parameter list is empty\" do\n      expect(@resource).to be_empty\n      @resource[:foo] = \"bar\"\n      expect(@resource).not_to be_empty\n    end\n\n    it \"should be able to produce a hash of all existing parameters\" do\n      @resource[:foo] = \"bar\"\n      @resource[:fee] = \"yay\"\n\n      hash = @resource.to_hash\n      expect(hash[:foo]).to eq(\"bar\")\n      expect(hash[:fee]).to eq(\"yay\")\n    end\n\n    it \"should not provide direct access to the internal parameters hash when producing a hash\" do\n      hash = @resource.to_hash\n      hash[:foo] = \"bar\"\n      expect(@resource[:foo]).to be_nil\n    end\n\n    it \"should use the title as the namevar to the hash if no namevar is present\" do\n      resource = Puppet::Resource.new(\"user\", \"bob\")\n      allow(Puppet::Type.type(:user)).to receive(:key_attributes).and_return([:myvar])\n      expect(resource.to_hash[:myvar]).to eq(\"bob\")\n    end\n\n    it \"should set :name to the title if :name is not present for non-existent types\" do\n      resource = Puppet::Resource.new :doesnotexist, \"bar\"\n      expect(resource.to_hash[:name]).to eq(\"bar\")\n    end\n\n    it \"should set :name to the title if :name is not present for a definition\" do\n      type = Puppet::Resource::Type.new(:definition, :foo)\n      environment.known_resource_types.add(type)\n      resource = Puppet::Resource.new :foo, \"bar\", :environment => environment\n      expect(resource.to_hash[:name]).to eq(\"bar\")\n    end\n  end\n\n  describe \"when serializing a native type\" do\n    before do\n      @resource = Puppet::Resource.new(\"file\", \"/my/file\")\n      @resource[\"one\"] = \"test\"\n      @resource[\"two\"] = \"other\"\n    end\n\n    # PUP-3272, needs to work becuse serialization is not only to network\n    #\n    it \"should produce an equivalent yaml object\" do\n      text = @resource.render('yaml')\n\n      newresource = Puppet::Resource.convert_from('yaml', text)\n      expect(newresource).to equal_resource_attributes_of(@resource)\n    end\n\n    # PUP-3272, since serialization to network is done in json, not yaml\n    it \"should produce an equivalent json object\" do\n      text = @resource.render('json')\n\n      newresource = Puppet::Resource.convert_from('json', text)\n      expect(newresource).to equal_resource_attributes_of(@resource)\n    end\n  end\n\n  describe \"when serializing a defined type\" do\n    before do\n      type = Puppet::Resource::Type.new(:definition, \"foo::bar\")\n      environment.known_resource_types.add type\n\n      @resource = Puppet::Resource.new('foo::bar', 'xyzzy', :environment => environment)\n      @resource['one'] = 'test'\n      @resource['two'] = 'other'\n      @resource.resource_type\n    end\n\n    it \"doesn't include transient instance variables (#4506)\" do\n      expect(@resource.to_data_hash.keys).to_not include('rstype')\n    end\n\n    it \"produces an equivalent json object\" do\n      text = @resource.render('json')\n\n      newresource = Puppet::Resource.convert_from('json', text)\n      expect(newresource).to equal_resource_attributes_of(@resource)\n    end\n\n    it 'to_data_hash returns value that is instance of Data' do\n      Puppet::Pops::Types::TypeAsserter.assert_instance_of('', Puppet::Pops::Types::TypeFactory.data, @resource.to_data_hash)\n      expect(Puppet::Pops::Types::TypeFactory.data.instance?(@resource.to_data_hash)).to be_truthy\n    end\n  end\n\n  describe \"when converting to a RAL resource\" do\n    it \"should use the resource type's :new method to create the resource if the resource is of a builtin type\" do\n      resource = Puppet::Resource.new(\"file\", basepath+\"/my/file\")\n      result = resource.to_ral\n\n      expect(result).to be_instance_of(Puppet::Type.type(:file))\n      expect(result[:path]).to eq(basepath+\"/my/file\")\n    end\n\n    it \"should convert to a component instance if the resource is not a compilable_type\" do\n      resource = Puppet::Resource.new(\"foobar\", \"somename\")\n      result = resource.to_ral\n\n      expect(result).to be_instance_of(Puppet::Type.type(:component))\n      expect(result.title).to eq(\"Foobar[somename]\")\n    end\n\n    it \"should convert to a component instance if the resource is a class\" do\n      resource = Puppet::Resource.new(\"Class\", \"somename\")\n      result = resource.to_ral\n\n      expect(result).to be_instance_of(Puppet::Type.type(:component))\n      expect(result.title).to eq(\"Class[Somename]\")\n    end\n\n    it \"should convert to component when the resource is a defined_type\" do\n      resource = Puppet::Resource.new(\"Unknown\", \"type\", :kind => 'defined_type')\n\n      result = resource.to_ral\n      expect(result).to be_instance_of(Puppet::Type.type(:component))\n    end\n\n    it \"should raise if a resource type is a compilable_type and it wasn't found\" do\n      resource = Puppet::Resource.new(\"Unknown\", \"type\", :kind => 'compilable_type')\n\n      expect { resource.to_ral }.to raise_error(Puppet::Error, \"Resource type 'Unknown' was not found\")\n    end\n\n    it \"should use the old behaviour when the catalog_format is equal to 1\" do\n      resource = Puppet::Resource.new(\"Unknown\", \"type\")\n      catalog = Puppet::Resource::Catalog.new(\"mynode\")\n\n      resource.catalog = catalog\n      resource.catalog.catalog_format = 1\n\n      result = resource.to_ral\n      expect(result).to be_instance_of(Puppet::Type.type(:component))\n    end\n\n    it \"should use the new behaviour and fail when the catalog_format is greater than 1\" do\n      resource = Puppet::Resource.new(\"Unknown\", \"type\", :kind => 'compilable_type')\n      catalog = Puppet::Resource::Catalog.new(\"mynode\")\n\n      resource.catalog = catalog\n      resource.catalog.catalog_format = 2\n\n      expect { resource.to_ral }.to raise_error(Puppet::Error, \"Resource type 'Unknown' was not found\")\n    end\n\n    it \"should use the resource type when the resource doesn't respond to kind and the resource type can be found\" do\n      resource = Puppet::Resource.new(\"file\", basepath+\"/my/file\")\n\n      result = resource.to_ral\n      expect(result).to be_instance_of(Puppet::Type.type(:file))\n    end\n  end\n  describe \"when converting to puppet code\" do\n    before do\n      @resource = Puppet::Resource.new(\"one::two\", \"/my/file\",\n        :parameters => {\n          :noop => true,\n          :foo => %w{one two},\n          :ensure => 'present',\n        }\n      )\n    end\n\n    it \"should escape internal single quotes in a title\" do\n      singlequote_resource = Puppet::Resource.new(\"one::two\", \"/my/file'b'a'r\",\n        :parameters => {\n          :ensure => 'present',\n        }\n      )\n      expect(singlequote_resource.to_manifest).to eq(<<-HEREDOC.gsub(/^\\s{8}/, '').gsub(/\\n$/, ''))\n        one::two { '/my/file\\\\'b\\\\'a\\\\'r':\n          ensure => 'present',\n        }\n      HEREDOC\n\n    end\n\n    it \"should align, sort and add trailing commas to attributes with ensure first\" do\n      expect(@resource.to_manifest).to eq(<<-HEREDOC.gsub(/^\\s{8}/, '').gsub(/\\n$/, ''))\n        one::two { '/my/file':\n          ensure => 'present',\n          foo    => ['one', 'two'],\n          noop   => true,\n        }\n      HEREDOC\n    end\n  end\n\n  describe \"when converting to Yaml for Hiera\" do\n    before do\n      @resource = Puppet::Resource.new(\"one::two\", \"/my/file\",\n        :parameters => {\n          :noop => true,\n          :foo => [:one, \"two\"],\n          :bar => 'a\\'b',\n          :ensure => 'present',\n        }\n      )\n    end\n\n    it \"should align and sort to attributes with ensure first\" do\n      expect(@resource.to_hierayaml).to eq(<<-HEREDOC.gsub(/^\\s{8}/, ''))\n          /my/file:\n            ensure: 'present'\n            bar   : 'a\\\\'b'\n            foo   : ['one', 'two']\n            noop  : true\n      HEREDOC\n    end\n\n    it \"should convert some types to String\" do\n      expect(@resource.to_hiera_hash).to eq(\n        \"/my/file\" => {\n          'ensure' => \"present\",\n          'bar'    => \"a'b\",\n          'foo'    => [\"one\", \"two\"],\n          'noop'   => true\n        }\n      )\n    end\n\n    it \"accepts symbolic titles\" do\n      res = Puppet::Resource.new(:file, \"/my/file\", :parameters => { 'ensure' => \"present\" })\n\n      expect(res.to_hiera_hash.keys).to eq([\"/my/file\"])\n    end\n\n    it \"emits an empty parameters hash\" do\n      res = Puppet::Resource.new(:file, \"/my/file\")\n\n      expect(res.to_hiera_hash).to eq({\"/my/file\" => {}})\n    end\n  end\n\n  describe \"when converting to json\" do\n    # LAK:NOTE For all of these tests, we convert back to the resource so we can\n    # trap the actual data structure then.\n\n    it \"should set its type to the provided type\" do\n      expect(Puppet::Resource.from_data_hash(JSON.parse(Puppet::Resource.new(\"File\", \"/foo\").to_json)).type).to eq(\"File\")\n    end\n\n    it \"should set its title to the provided title\" do\n      expect(Puppet::Resource.from_data_hash(JSON.parse(Puppet::Resource.new(\"File\", \"/foo\").to_json)).title).to eq(\"/foo\")\n    end\n\n    it \"should include all tags from the resource\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource.tag(\"yay\")\n\n      expect(Puppet::Resource.from_data_hash(JSON.parse(resource.to_json)).tags).to eq(resource.tags)\n    end\n\n    it \"should include the file if one is set\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource.file = \"/my/file\"\n\n      expect(Puppet::Resource.from_data_hash(JSON.parse(resource.to_json)).file).to eq(\"/my/file\")\n    end\n\n    it \"should include the line if one is set\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource.line = 50\n\n      expect(Puppet::Resource.from_data_hash(JSON.parse(resource.to_json)).line).to eq(50)\n    end\n\n    it \"should include the kind if one is set\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource.kind = 'im_a_file'\n\n      expect(Puppet::Resource.from_data_hash(JSON.parse(resource.to_json)).kind).to eq('im_a_file')\n    end\n\n    it \"should include the 'exported' value if one is set\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource.exported = true\n\n      expect(Puppet::Resource.from_data_hash(JSON.parse(resource.to_json)).exported?).to be_truthy\n    end\n\n    it \"should set 'exported' to false if no value is set\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n\n      expect(Puppet::Resource.from_data_hash(JSON.parse(resource.to_json)).exported?).to be_falsey\n    end\n\n    it \"should set all of its parameters as the 'parameters' entry\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource[:foo] = %w{bar eh}\n      resource[:fee] = %w{baz}\n\n      result = Puppet::Resource.from_data_hash(JSON.parse(resource.to_json))\n      expect(result[\"foo\"]).to eq(%w{bar eh})\n      expect(result[\"fee\"]).to eq(%w{baz})\n    end\n\n    it \"should set sensitive parameters as an array of strings\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\", :sensitive_parameters => [:foo, :fee])\n      result = JSON.parse(resource.to_json)\n      expect(result[\"sensitive_parameters\"]).to eq([\"foo\", \"fee\"])\n    end\n\n    it \"should serialize relationships as reference strings\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource[:requires] = Puppet::Resource.new(\"File\", \"/bar\")\n      result = Puppet::Resource.from_data_hash(JSON.parse(resource.to_json))\n      expect(result[:requires]).to eq(\"File[/bar]\")\n    end\n\n    it \"should serialize multiple relationships as arrays of reference strings\" do\n      resource = Puppet::Resource.new(\"File\", \"/foo\")\n      resource[:requires] = [Puppet::Resource.new(\"File\", \"/bar\"), Puppet::Resource.new(\"File\", \"/baz\")]\n      result = Puppet::Resource.from_data_hash(JSON.parse(resource.to_json))\n      expect(result[:requires]).to eq([ \"File[/bar]\",  \"File[/baz]\" ])\n    end\n  end\n\n  describe 'when converting to data_hash with stringified parameters' do\n    before(:each) do\n      Puppet.push_context({:stringify_rich => true}, 'resource_spec.rb')\n    end\n\n    after(:each) do\n      Puppet.pop_context\n    end\n\n    let(:resource) do\n      type = Puppet::Resource::Type.new(:definition, \"rich::thing\")\n      environment.known_resource_types.add type\n\n      r = Puppet::Resource.new('rich::thing', 'stringified', :environment => environment)\n      r['binary'] = Puppet::Pops::Types::PBinaryType::Binary.from_binary_string('hello')\n      r['timestamp'] = Puppet::Pops::Time::Timestamp.parse('2018-09-03T19:45:33.697066000 UTC')\n      r['reference'] = Puppet::Resource.new('File', 'dummy', :environment => environment)\n      r.resource_type\n      r\n    end\n\n    let(:parameters) do\n      resource.to_data_hash['parameters']\n    end\n\n    it 'has Base64 string content for a binary' do\n      expect(parameters['binary']).to eq('aGVsbG8=')\n    end\n\n    it 'has string content for a timestamp' do\n      expect(parameters['timestamp']).to eq('2018-09-03T19:45:33.697066000 UTC')\n    end\n\n    it 'has string content for a Resource instance' do\n      expect(parameters['reference']).to eq('File[dummy]')\n    end\n\n    # Note: to_stringified_spec.rb has tests for all other data types\n  end\n\n  describe 'when serializing resources' do\n    require 'puppet_spec/compiler'\n    include PuppetSpec::Compiler\n\n    it 'serializes rich data' do\n      resource = compile_to_catalog('notify {\"foo\": message => Deferred(\"func\", [\"a\", \"b\", \"c\"])}')\n\n      # This assume rich_data is true by default\n      expect(resource.to_data_hash.class).to be(Hash)\n    end\n\n    it 'raises when rich data is disabled' do\n      resource = compile_to_catalog('notify {\"foo\": message => Deferred(\"func\", [\"a\", \"b\", \"c\"])}')\n      expect {\n        Puppet.override(rich_data: false) do\n          resource.to_data_hash\n        end\n      }.to raise_error(Puppet::PreformattedError)\n    end\n  end\n\n  describe \"when converting from json\" do\n    before do\n      @data = {\n        'type' => \"file\",\n        'title' => basepath+\"/yay\",\n      }\n    end\n\n    it \"should set its type to the provided type\" do\n      expect(Puppet::Resource.from_data_hash(@data).type).to eq(\"File\")\n    end\n\n    it \"should set its title to the provided title\" do\n      expect(Puppet::Resource.from_data_hash(@data).title).to eq(basepath+\"/yay\")\n    end\n\n    it \"should tag the resource with any provided tags\" do\n      @data['tags'] = %w{foo bar}\n      resource = Puppet::Resource.from_data_hash(@data)\n      expect(resource.tags).to be_include(\"foo\")\n      expect(resource.tags).to be_include(\"bar\")\n    end\n\n    it \"should set its file to the provided file\" do\n      @data['file'] = \"/foo/bar\"\n      expect(Puppet::Resource.from_data_hash(@data).file).to eq(\"/foo/bar\")\n    end\n\n    it \"should set its line to the provided line\" do\n      @data['line'] = 50\n      expect(Puppet::Resource.from_data_hash(@data).line).to eq(50)\n    end\n\n    it \"should 'exported' to true if set in the json data\" do\n      @data['exported'] = true\n      expect(Puppet::Resource.from_data_hash(@data).exported).to be_truthy\n    end\n\n    it \"should 'exported' to false if not set in the json data\" do\n      expect(Puppet::Resource.from_data_hash(@data).exported).to be_falsey\n    end\n\n    it \"should fail if no title is provided\" do\n      @data.delete('title')\n      expect { Puppet::Resource.from_data_hash(@data) }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if no type is provided\" do\n      @data.delete('type')\n      expect { Puppet::Resource.from_data_hash(@data) }.to raise_error(ArgumentError)\n    end\n\n    it \"should set each of the provided parameters\" do\n      @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}}\n      resource = Puppet::Resource.from_data_hash(@data)\n      expect(resource['foo']).to eq(%w{one two})\n      expect(resource['fee']).to eq(%w{three four})\n    end\n\n    it \"should convert single-value array parameters to normal values\" do\n      @data['parameters'] = {'foo' => %w{one}}\n      resource = Puppet::Resource.from_data_hash(@data)\n      expect(resource['foo']).to eq(%w{one})\n    end\n\n    it \"converts deserialized sensitive parameters as symbols\" do\n      @data['sensitive_parameters'] = ['content', 'mode']\n      expect(Puppet::Resource.from_data_hash(@data).sensitive_parameters).to eq [:content, :mode]\n    end\n  end\n\n  it \"implements copy_as_resource\" do\n    resource = Puppet::Resource.new(\"file\", \"/my/file\")\n    expect(resource.copy_as_resource).to eq(resource)\n  end\n\n  describe \"when copying resources\" do\n    it \"deep copies over 'sensitive' values\" do\n      rhs = Puppet::Resource.new(\"file\", \"/my/file\", {:parameters => {:content => \"foo\"}, :sensitive_parameters => [:content]})\n      lhs = Puppet::Resource.new(rhs)\n      expect(lhs.sensitive_parameters).to eq [:content]\n    end\n  end\n\n  describe \"because it is an indirector model\" do\n    it \"should include Puppet::Indirector\" do\n      expect(Puppet::Resource).to be_is_a(Puppet::Indirector)\n    end\n\n    it \"should have a default terminus\" do\n      expect(Puppet::Resource.indirection.terminus_class).to be\n    end\n\n    it \"should have a name\" do\n      expect(Puppet::Resource.new(\"file\", \"/my/file\").name).to eq(\"File//my/file\")\n    end\n  end\n\n  describe \"when resolving resources with a catalog\" do\n    it \"should resolve all resources using the catalog\" do\n      catalog = double('catalog')\n      resource = Puppet::Resource.new(\"foo::bar\", \"yay\")\n      resource.catalog = catalog\n\n      expect(catalog).to receive(:resource).with(\"Foo::Bar[yay]\").and_return(:myresource)\n\n      expect(resource.resolve).to eq(:myresource)\n    end\n  end\n\n  describe \"when generating the uniqueness key\" do\n    it \"should include all of the key_attributes in alphabetical order by attribute name\" do\n      allow(Puppet::Type.type(:file)).to receive(:key_attributes).and_return([:myvar, :owner, :path])\n      allow(Puppet::Type.type(:file)).to receive(:title_patterns).and_return(\n        [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ]\n      )\n      res = Puppet::Resource.new(\"file\", \"/my/file\", :parameters => {:owner => 'root', :content => 'hello'})\n      expect(res.uniqueness_key).to eq([ nil, 'root', '/my/file'])\n    end\n  end\n\n  describe '#parse_title' do\n    describe 'with a composite namevar' do\n      before do\n        Puppet::Type.newtype(:composite) do\n\n          newparam(:name)\n          newparam(:value)\n\n          # Configure two title patterns to match a title that is either\n          # separated with a colon or exclamation point. The first capture\n          # will be used for the :name param, and the second capture will be\n          # used for the :value param.\n          def self.title_patterns\n            identity = lambda {|x| x }\n            reverse  = lambda {|x| x.reverse }\n            [\n              [\n                /^(.*?):(.*?)$/,\n                [\n                  [:name, identity],\n                  [:value, identity],\n                ]\n              ],\n              [\n                /^(.*?)!(.*?)$/,\n                [\n                  [:name, reverse],\n                  [:value, reverse],\n                ]\n              ],\n            ]\n          end\n        end\n      end\n\n      describe \"with no matching title patterns\" do\n        subject { Puppet::Resource.new(:composite, 'unmatching title')}\n\n        it \"should raise an exception if no title patterns match\" do\n          expect do\n            subject.to_hash\n          end.to raise_error(Puppet::Error, /No set of title patterns matched/)\n        end\n      end\n\n      describe \"with a matching title pattern\" do\n        subject { Puppet::Resource.new(:composite, 'matching:title') }\n\n        it \"should not raise an exception if there was a match\" do\n          expect do\n            subject.to_hash\n          end.to_not raise_error\n        end\n\n        it \"should set the resource parameters from the parsed title values\" do\n          h = subject.to_hash\n          expect(h[:name]).to eq('matching')\n          expect(h[:value]).to eq('title')\n        end\n      end\n\n      describe \"and multiple title patterns\" do\n        subject { Puppet::Resource.new(:composite, 'matching!title') }\n\n        it \"should use the first title pattern that matches\" do\n          h = subject.to_hash\n          expect(h[:name]).to eq('gnihctam')\n          expect(h[:value]).to eq('eltit')\n        end\n      end\n    end\n  end\n\n  describe \"#prune_parameters\" do\n    before do\n      Puppet::Type.newtype('blond') do\n        newproperty(:ensure)\n        newproperty(:height)\n        newproperty(:weight)\n        newproperty(:sign)\n        newproperty(:friends)\n        newparam(:admits_to_dying_hair)\n        newparam(:admits_to_age)\n        newparam(:name)\n      end\n\n      Puppet::Type.newtype('brown') do\n        newproperty(:ensure)\n        newparam(:admits_to_dying_hair)\n        newparam(:admits_to_age)\n        newparam(:hair_length)\n        newparam(:name)\n\n        def self.parameters_to_include\n          [:admits_to_dying_hair, :admits_to_age]\n        end\n      end\n    end\n\n    it \"should strip all parameters and strip properties that are nil, empty or absent except for ensure\" do\n      resource = Puppet::Resource.new(\"blond\", \"Bambi\", :parameters => {\n        :ensure               => 'absent',\n        :height               => '',\n        :weight               => 'absent',\n        :friends              => [],\n        :admits_to_age        => true,\n        :admits_to_dying_hair => false\n      })\n\n      pruned_resource = resource.prune_parameters\n      expect(pruned_resource).to eq(Puppet::Resource.new(\"blond\", \"Bambi\", :parameters => {:ensure => 'absent'}))\n    end\n\n    it \"should leave parameters alone if in parameters_to_include\" do\n      resource = Puppet::Resource.new(\"blond\", \"Bambi\", :parameters => {\n        :admits_to_age        => true,\n        :admits_to_dying_hair => false\n      })\n\n      pruned_resource = resource.prune_parameters(:parameters_to_include => [:admits_to_dying_hair])\n      expect(pruned_resource).to eq(Puppet::Resource.new(\"blond\", \"Bambi\", :parameters => {:admits_to_dying_hair => false}))\n    end\n\n    it \"should leave properties if not nil, absent or empty\" do\n      resource = Puppet::Resource.new(\"blond\", \"Bambi\", :parameters => {\n        :ensure          => 'silly',\n        :height          => '7 ft 5 in',\n        :friends         => ['Oprah'],\n      })\n\n      pruned_resource = resource.prune_parameters\n      expect(pruned_resource).to eq(\n      resource = Puppet::Resource.new(\"blond\", \"Bambi\", :parameters => {\n        :ensure          => 'silly',\n        :height          => '7 ft 5 in',\n        :friends         => ['Oprah'],\n      })\n      )\n    end\n\n    context \"when the resource type has a default set of parameters it wants to include\" do\n      it \"should leave those parameters alone\" do\n        resource = Puppet::Resource.new(\"brown\", \"Esmeralda\", :parameters => {\n          :admits_to_age        => true,\n          :admits_to_dying_hair => false,\n          :hair_length          => 10\n        })\n\n        pruned_resource = resource.prune_parameters\n        expected_resource = Puppet::Resource.new(\n          \"brown\",\n          \"Esmeralda\",\n          :parameters => { :admits_to_age => true, :admits_to_dying_hair => false }\n        )\n  \n        expect(pruned_resource).to eq(expected_resource)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/scheduler/job_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/scheduler'\n\ndescribe Puppet::Scheduler::Job do\n  let(:run_interval) { 10 }\n  let(:job) { described_class.new(run_interval) }\n\n  it \"has a minimum run interval of 0\" do\n    expect(Puppet::Scheduler::Job.new(-1).run_interval).to eq(0)\n  end\n\n  describe \"when not run yet\" do\n    it \"is ready\" do\n      expect(job.ready?(2)).to be\n    end\n\n    it \"gives the time to next run as 0\" do\n      expect(job.interval_to_next_from(2)).to eq(0)\n    end\n  end\n\n  describe \"when run at least once\" do\n    let(:last_run) { 50 }\n\n    before(:each) do\n      job.run(last_run)\n    end\n\n    it \"is ready when the time is greater than the last run plus the interval\" do\n      expect(job.ready?(last_run + run_interval + 1)).to be\n    end\n\n    it \"is ready when the time is equal to the last run plus the interval\" do\n      expect(job.ready?(last_run + run_interval)).to be\n    end\n\n    it \"is not ready when the time is less than the last run plus the interval\" do\n      expect(job.ready?(last_run + run_interval - 1)).not_to be\n    end\n\n    context \"when calculating the next run\" do\n      it \"returns the run interval if now == last run\" do\n        expect(job.interval_to_next_from(last_run)).to eq(run_interval)\n      end\n\n      it \"when time is between the last and next runs gives the remaining portion of the run_interval\" do\n        time_since_last_run = 2\n        now = last_run + time_since_last_run\n        expect(job.interval_to_next_from(now)).to eq(run_interval - time_since_last_run)\n      end\n\n      it \"when time is later than last+interval returns 0\" do\n        time_since_last_run = run_interval + 5\n        now = last_run + time_since_last_run\n        expect(job.interval_to_next_from(now)).to eq(0)\n      end\n    end\n  end\n\n  it \"starts enabled\" do\n    expect(job.enabled?).to be\n  end\n\n  it \"can be disabled\" do\n    job.disable\n    expect(job.enabled?).not_to be\n  end\n\n  it \"has the job instance as a parameter\" do\n    passed_job = nil\n    job = Puppet::Scheduler::Job.new(run_interval) do |j|\n      passed_job = j\n    end\n    job.run(5)\n\n    expect(passed_job).to eql(job)\n  end\nend\n"
  },
  {
    "path": "spec/unit/scheduler/scheduler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/scheduler'\n\ndescribe Puppet::Scheduler::Scheduler do\n  let(:now) { 183550 }\n  let(:timer) { MockTimer.new(now) }\n\n  class MockTimer\n    attr_reader :wait_for_calls\n\n    def initialize(start=1729)\n      @now = start\n      @wait_for_calls = []\n    end\n\n    def wait_for(seconds)\n      @wait_for_calls << seconds\n      @now += seconds\n    end\n\n    def now\n      @now\n    end\n  end\n\n  def one_time_job(interval)\n    Puppet::Scheduler::Job.new(interval) { |j| j.disable }\n  end\n\n  def disabled_job(interval)\n    job = Puppet::Scheduler::Job.new(interval) { |j| j.disable }\n    job.disable\n    job\n  end\n\n  let(:scheduler) { Puppet::Scheduler::Scheduler.new(timer) }\n\n  it \"uses the minimum interval\" do\n    later_job = one_time_job(7)\n    earlier_job = one_time_job(2)\n    later_job.last_run = now\n    earlier_job.last_run = now\n\n    scheduler.run_loop([later_job, earlier_job])\n\n    expect(timer.wait_for_calls).to eq([2, 5])\n  end\n\n  it \"ignores disabled jobs when calculating intervals\" do\n    enabled = one_time_job(7)\n    enabled.last_run = now\n    disabled = disabled_job(2)\n\n    scheduler.run_loop([enabled, disabled])\n\n    expect(timer.wait_for_calls).to eq([7])\n  end\n\n  it \"asks the timer to wait for the job interval\" do\n    job = one_time_job(5)\n    job.last_run = now\n\n    scheduler.run_loop([job])\n\n    expect(timer.wait_for_calls).to eq([5])\n  end\n\n  it \"does not run when there are no jobs\" do\n    scheduler.run_loop([])\n\n    expect(timer.wait_for_calls).to be_empty\n  end\n\n  it \"does not run when there are only disabled jobs\" do\n    disabled_job = Puppet::Scheduler::Job.new(0)\n    disabled_job.disable\n\n    scheduler.run_loop([disabled_job])\n\n    expect(timer.wait_for_calls).to be_empty\n  end\n\n  it \"stops running when there are no more enabled jobs\" do\n    disabling_job = Puppet::Scheduler::Job.new(0) do |j|\n      j.disable\n    end\n\n    scheduler.run_loop([disabling_job])\n\n    expect(timer.wait_for_calls.size).to eq(1)\n  end\n\n  it \"marks the start of the run loop\" do\n    disabled_job = Puppet::Scheduler::Job.new(0)\n\n    disabled_job.disable\n\n    scheduler.run_loop([disabled_job])\n\n    expect(disabled_job.start_time).to eq(now)\n  end\n\n  it \"calculates the next interval from the start of a job\" do\n    countdown = 2\n    slow_job = Puppet::Scheduler::Job.new(10) do |job|\n      timer.wait_for(3)\n      countdown -= 1\n      job.disable if countdown == 0\n    end\n\n    scheduler.run_loop([slow_job])\n\n    expect(timer.wait_for_calls).to eq([0, 3, 7, 3])\n  end\nend\n"
  },
  {
    "path": "spec/unit/scheduler/splay_job_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/scheduler'\n\ndescribe Puppet::Scheduler::SplayJob do\n  let(:run_interval) { 10 }\n  let(:last_run) { 50 }\n  let(:splay_limit) { 5 }\n  let(:start_time) { 23 }\n  let(:job) { described_class.new(run_interval, splay_limit) }\n\n  it \"does not apply a splay after the first run\" do\n    job.run(last_run)\n    expect(job.interval_to_next_from(last_run)).to eq(run_interval)\n  end\n\n  it \"calculates the first run splayed from the start time\" do\n    job.start_time = start_time\n\n    expect(job.interval_to_next_from(start_time)).to eq(job.splay)\n  end\n\n  it \"interval to the next run decreases as time advances\" do\n    time_passed = 3\n    job.start_time = start_time\n\n    expect(job.interval_to_next_from(start_time + time_passed)).to eq(job.splay - time_passed)\n  end\n\n  it \"is not immediately ready if splayed\" do\n    job.start_time = start_time\n    expect(job).to receive(:splay).and_return(6)\n    expect(job.ready?(start_time)).not_to be\n  end\n\n  it \"does not apply a splay if the splaylimit is unchanged\" do\n    old_splay = job.splay\n    job.splay_limit = splay_limit\n    expect(job.splay).to eq(old_splay)\n  end\n\n  it \"applies a splay if the splaylimit is changed\" do\n    new_splay = 999\n    allow(job).to receive(:rand).and_return(new_splay)\n    job.splay_limit = splay_limit + 1\n    expect(job.splay).to eq(new_splay)\n  end\n\n  it \"returns the splay_limit\" do\n    expect(job.splay_limit).to eq(splay_limit)\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/array_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/array_setting'\n\ndescribe Puppet::Settings::ArraySetting do\n  subject { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  it \"is of type :array\" do\n    expect(subject.type).to eq :array\n  end\n\n  describe \"munging the value\" do\n    describe \"when given a string\" do\n      it \"splits multiple values into an array\" do\n        expect(subject.munge(\"foo,bar\")).to eq %w[foo bar]\n      end\n      it \"strips whitespace between elements\" do\n        expect(subject.munge(\"foo , bar\")).to eq %w[foo bar]\n      end\n\n      it \"creates an array when one item is given\" do\n        expect(subject.munge(\"foo\")).to eq %w[foo]\n      end\n    end\n\n    describe \"when given an array\" do\n      it \"returns the array\" do\n        expect(subject.munge(%w[foo])).to eq %w[foo]\n      end\n    end\n\n    it \"raises an error when given an unexpected object type\" do\n        expect {\n          subject.munge({:foo => 'bar'})\n        }.to raise_error(ArgumentError, \"Expected an Array or String, got a Hash\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/autosign_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/autosign_setting'\n\ndescribe Puppet::Settings::AutosignSetting do\n  let(:settings) do\n    s = double('settings')\n    allow(s).to receive(:[]).with(:mkusers).and_return(true)\n    allow(s).to receive(:[]).with(:user).and_return('puppet')\n    allow(s).to receive(:[]).with(:group).and_return('puppet')\n    allow(s).to receive(:[]).with(:manage_internal_file_permissions).and_return(true)\n    s\n  end\n\n  let(:setting) { described_class.new(:name => 'autosign', :section => 'section', :settings => settings, :desc => \"test\") }\n\n  it \"is of type :file\" do\n    expect(setting.type).to eq :file\n  end\n\n  describe \"when munging the setting\" do\n    it \"passes boolean values through\" do\n      expect(setting.munge(true)).to eq true\n      expect(setting.munge(false)).to eq false\n    end\n\n    it \"converts nil to false\" do\n      expect(setting.munge(nil)).to eq false\n    end\n\n    it \"munges string 'true' to boolean true\" do\n      expect(setting.munge('true')).to eq true\n    end\n\n    it \"munges string 'false' to boolean false\" do\n      expect(setting.munge('false')).to eq false\n    end\n\n    it \"passes absolute paths through\" do\n      path = File.expand_path('/path/to/autosign.conf')\n      expect(setting.munge(path)).to eq path\n    end\n\n    it \"fails if given anything else\" do\n      cases = [1.0, 'sometimes', 'relative/autosign.conf']\n\n      cases.each do |invalid|\n        expect {\n          setting.munge(invalid)\n        }.to raise_error Puppet::Settings::ValidationError, /Invalid autosign value/\n      end\n    end\n  end\n\n  describe \"setting additional setting values\" do\n    it \"can set the file mode\" do\n      setting.mode = '0664'\n      expect(setting.mode).to eq '0664'\n    end\n\n    it \"can set the file owner\" do\n      setting.owner = 'service'\n      expect(setting.owner).to eq 'puppet'\n    end\n\n    it \"can set the file group\" do\n      setting.group = 'service'\n      expect(setting.group).to eq 'puppet'\n    end\n  end\n\n  describe \"converting the setting to a resource\" do\n    it \"converts the file path to a file resource\", :if => !Puppet::Util::Platform.windows? do\n      path = File.expand_path('/path/to/autosign.conf')\n      allow(settings).to receive(:value).with('autosign', nil, false).and_return(path)\n      allow(Puppet::FileSystem).to receive(:exist?).and_call_original\n      allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n      expect(Puppet.features).to receive(:root?).and_return(true)\n\n      setting.mode = '0664'\n      setting.owner = 'service'\n      setting.group = 'service'\n\n      resource = setting.to_resource\n\n      expect(resource.title).to eq path\n      expect(resource[:ensure]).to eq :file\n      expect(resource[:mode]).to eq '664'\n      expect(resource[:owner]).to eq 'puppet'\n      expect(resource[:group]).to eq 'puppet'\n    end\n\n    it \"returns nil when the setting is a boolean\" do\n      allow(settings).to receive(:value).with('autosign', nil, false).and_return('true')\n\n      setting.mode = '0664'\n      setting.owner = 'service'\n      setting.group = 'service'\n\n      expect(setting.to_resource).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/certificate_revocation_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/certificate_revocation_setting'\n\ndescribe Puppet::Settings::CertificateRevocationSetting do\n  subject { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  it \"is of type :certificate_revocation\" do\n    expect(subject.type).to eq :certificate_revocation\n  end\n\n  describe \"munging the value\" do\n    ['true', true, 'chain'].each do |setting|\n      it \"munges #{setting.inspect} to :chain\" do\n        expect(subject.munge(setting)).to eq :chain\n      end\n    end\n\n    it \"munges 'leaf' to :leaf\" do\n      expect(subject.munge(\"leaf\")).to eq :leaf\n    end\n\n    ['false', false, nil].each do |setting|\n      it \"munges #{setting.inspect} to false\" do\n        expect(subject.munge(setting)).to eq false\n      end\n    end\n\n    it \"raises an error when given an unexpected object type\" do\n        expect {\n          subject.munge(1)\n        }.to raise_error(Puppet::Settings::ValidationError, \"Invalid certificate revocation value 1: must be one of 'true', 'chain', 'leaf', or 'false'\")\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/settings/config_file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/settings/config_file'\n\ndescribe Puppet::Settings::ConfigFile do\n  NOTHING = {}\n\n  def the_parse_of(*lines)\n    config.parse_file(filename, lines.join(\"\\n\"))\n  end\n\n  let(:identity_transformer) { Proc.new { |value| value } }\n  let(:config) { Puppet::Settings::ConfigFile.new(identity_transformer) }\n\n  let(:filename) { \"a/fake/filename.conf\" }\n\n  Conf = Puppet::Settings::ConfigFile::Conf\n  Section = Puppet::Settings::ConfigFile::Section\n  Meta = Puppet::Settings::ConfigFile::Meta\n  NO_META = Puppet::Settings::ConfigFile::NO_META\n\n  it \"interprets an empty file to contain a main section with no entries\" do\n    result = the_parse_of(\"\")\n\n    expect(result).to eq(Conf.new.with_section(Section.new(:main)))\n  end\n\n  it \"interprets an empty main section the same as an empty file\" do\n    expect(the_parse_of(\"\")).to eq(config.parse_file(filename, \"[main]\"))\n  end\n\n  it \"places an entry in no section in main\" do\n    result = the_parse_of(\"var = value\")\n\n    expect(result).to eq(Conf.new.with_section(Section.new(:main).with_setting(:var, \"value\", NO_META)))\n  end\n\n  it \"places an entry after a section header in that section\" do\n    result = the_parse_of(\"[agent]\", \"var = value\")\n\n    expect(result).to eq(Conf.new.\n                         with_section(Section.new(:main)).\n                         with_section(Section.new(:agent).\n                                      with_setting(:var, \"value\", NO_META)))\n  end\n\n  it \"does not include trailing whitespace in the value\" do\n    result = the_parse_of(\"var = value\\t \")\n\n    expect(result).to eq(Conf.new.\n                         with_section(Section.new(:main).\n                                      with_setting(:var, \"value\", NO_META)))\n  end\n\n  it \"does not include leading whitespace in the name\" do\n    result = the_parse_of(\"  \\t var=value\")\n\n    expect(result).to eq(Conf.new.\n                         with_section(Section.new(:main).\n                                      with_setting(:var, \"value\", NO_META)))\n  end\n\n  it \"skips lines that are commented out\" do\n    result = the_parse_of(\"#var = value\")\n\n    expect(result).to eq(Conf.new.with_section(Section.new(:main)))\n  end\n\n  it \"skips lines that are entirely whitespace\" do\n    result = the_parse_of(\"   \\t \")\n\n    expect(result).to eq(Conf.new.with_section(Section.new(:main)))\n  end\n\n  it \"errors when a line is not a known form\" do\n    expect { the_parse_of(\"unknown\") }.to raise_error Puppet::Settings::ParseError, /Could not match line/\n  end\n\n  it \"errors providing correct line number when line is not a known form\" do\n    multi_line_config = <<-EOF\n[main]\nfoo=bar\nbadline\n    EOF\n    expect { the_parse_of(multi_line_config) }.to(\n        raise_error(Puppet::Settings::ParseError, /Could not match line/) do |exception|\n          expect(exception.line).to eq(3)\n        end\n      )\n  end\n\n  it \"stores file meta information in the _meta section\" do\n    result = the_parse_of(\"var = value { owner = me, group = you, mode = 0666 }\")\n\n    expect(result).to eq(Conf.new.with_section(Section.new(:main).\n                                               with_setting(:var, \"value\",\n                                                            Meta.new(\"me\", \"you\", \"0666\"))))\n  end\n\n  it \"errors when there is unknown meta information\" do\n    expect { the_parse_of(\"var = value { unknown = no }\") }.\n      to raise_error ArgumentError, /Invalid file option 'unknown'/\n  end\n\n  it \"errors when the mode is not numeric\" do\n    expect { the_parse_of(\"var = value { mode = no }\") }.\n      to raise_error ArgumentError, \"File modes must be numbers\"\n  end\n\n  it \"errors when the options are not key-value pairs\" do\n    expect { the_parse_of(\"var = value { mode }\") }.\n      to raise_error ArgumentError, \"Could not parse 'value { mode }'\"\n  end\n\n  it \"may specify legal sections\" do\n    text = <<-EOF\n      [legal]\n      a = 'b'\n      [illegal]\n      one = 'e'\n      two = 'f'\n    EOF\n\n    expect { config.parse_file(filename, text, [:legal]) }.\n      to raise_error Puppet::Error,\n        /Illegal section 'legal' in config file at \\(file: #{filename}, line: 1\\)/\n  end\n\n  it \"transforms values with the given function\" do\n    config = Puppet::Settings::ConfigFile.new(Proc.new { |value| value + \" changed\" })\n\n    result = config.parse_file(filename, \"var = value\")\n\n    expect(result).to eq(Conf.new.\n                            with_section(Section.new(:main).\n                                         with_setting(:var, \"value changed\", NO_META)))\n  end\n\n  it \"accepts non-UTF8 encoded text\" do\n    result = the_parse_of(\"var = value\".encode(\"UTF-16LE\"))\n\n    expect(result).to eq(Conf.new.\n                           with_section(Section.new(:main).\n                                          with_setting(:var, \"value\", NO_META)))\n\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/settings/directory_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/directory_setting'\n\ndescribe Puppet::Settings::DirectorySetting do\n  DirectorySetting = Puppet::Settings::DirectorySetting\n\n  include PuppetSpec::Files\n\n  before do\n    @basepath = make_absolute(\"/somepath\")\n  end\n\n  describe \"when being converted to a resource\" do\n    before do\n      @settings = double('settings')\n      @dir = Puppet::Settings::DirectorySetting.new(\n          :settings => @settings, :desc => \"eh\", :name => :mydir, :section => \"mysect\")\n      allow(@settings).to receive(:value).with(:mydir).and_return(@basepath)\n    end\n\n    it \"should return :directory as its type\" do\n      expect(@dir.type).to eq(:directory)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/duration_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/duration_setting'\n\ndescribe Puppet::Settings::DurationSetting do\n  subject { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  describe \"when munging the setting\" do\n    it \"should return the same value if given an integer\" do\n      expect(subject.munge(5)).to eq(5)\n    end\n\n    it \"should return the same value if given nil\" do\n      expect(subject.munge(nil)).to be_nil\n    end\n\n    it \"should return an integer if given a decimal string\" do\n      expect(subject.munge(\"12\")).to eq(12)\n    end\n\n    it \"should fail if given anything but a well-formed string, integer, or nil\" do\n      [ '', 'foo', '2 d', '2d ', true, Time.now, 8.3, [] ].each do |value|\n        expect { subject.munge(value) }.to raise_error(Puppet::Settings::ValidationError)\n      end\n    end\n\n    it \"should parse strings with units of 'y', 'd', 'h', 'm', or 's'\" do\n      # Note: the year value won't jive with most methods of calculating\n      # year due to the Julian calandar having 365.25 days in a year\n      {\n        '3y' => 94608000,\n        '3d' => 259200,\n        '3h' => 10800,\n        '3m' => 180,\n        '3s' => 3\n      }.each do |value, converted_value|\n        # subject.munge(value).should == converted_value\n        expect(subject.munge(value)).to eq(converted_value)\n      end\n    end\n\n    # This is to support the `filetimeout` setting\n    it \"should allow negative values\" do\n      expect(subject.munge(-1)).to eq(-1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/enum_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\n\ndescribe Puppet::Settings::EnumSetting do\n  it \"allows a configured value\" do\n    setting = enum_setting_allowing(\"allowed\")\n\n    expect(setting.munge(\"allowed\")).to eq(\"allowed\")\n  end\n\n  it \"disallows a value that is not configured\" do\n    setting = enum_setting_allowing(\"allowed\", \"also allowed\")\n\n    expect do\n      setting.munge(\"disallowed\")\n    end.to raise_error(Puppet::Settings::ValidationError,\n                       \"Invalid value 'disallowed' for parameter testing. Allowed values are 'allowed', 'also allowed'\")\n  end\n\n  def enum_setting_allowing(*values)\n    Puppet::Settings::EnumSetting.new(:settings => double('settings'),\n                                      :name => \"testing\",\n                                      :desc => \"description of testing\",\n                                      :values => values)\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/environment_conf_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/settings/environment_conf.rb'\n\ndescribe Puppet::Settings::EnvironmentConf do\n\n  def setup_environment_conf(config, conf_hash)\n    conf_hash.each do |setting,value|\n      expect(config).to receive(:setting).with(setting).and_return(\n        double('setting', :value => value)\n      )\n    end\n  end\n\n  context \"with config\" do\n    let(:config) { double('config') }\n    let(:envconf) { Puppet::Settings::EnvironmentConf.new(\"/some/direnv\", config, [\"/global/modulepath\"]) }\n\n    it \"reads a modulepath from config and does not include global_module_path\" do\n      setup_environment_conf(config, :modulepath => '/some/modulepath')\n\n      expect(envconf.modulepath).to eq(File.expand_path('/some/modulepath'))\n    end\n\n    it \"reads a manifest from config\" do\n      setup_environment_conf(config, :manifest => '/some/manifest')\n\n      expect(envconf.manifest).to eq(File.expand_path('/some/manifest'))\n    end\n\n    it \"reads a config_version from config\" do\n      setup_environment_conf(config, :config_version => '/some/version.sh')\n\n      expect(envconf.config_version).to eq(File.expand_path('/some/version.sh'))\n    end\n\n    it \"reads an environment_timeout from config\" do\n      setup_environment_conf(config, :environment_timeout => '3m')\n\n      expect(envconf.environment_timeout).to eq(180)\n    end\n\n    it \"reads a static_catalogs from config\" do\n      setup_environment_conf(config, :static_catalogs => true)\n\n      expect(envconf.static_catalogs).to eq(true)\n    end\n\n    it \"can retrieve untruthy settings\" do\n      Puppet[:static_catalogs] = true\n      setup_environment_conf(config, :static_catalogs => false)\n\n      expect(envconf.static_catalogs).to eq(false)\n    end\n\n    it \"can retrieve raw settings\" do\n      setup_environment_conf(config, :manifest => 'manifest.pp')\n\n      expect(envconf.raw_setting(:manifest)).to eq('manifest.pp')\n    end\n  end\n\n  context \"without config\" do\n    let(:envconf) { Puppet::Settings::EnvironmentConf.new(\"/some/direnv\", nil, [\"/global/modulepath\"]) }\n\n    it \"returns a default modulepath when config has none, with global_module_path\" do\n      expect(envconf.modulepath).to eq(\n        [File.expand_path('/some/direnv/modules'),\n        File.expand_path('/global/modulepath')].join(File::PATH_SEPARATOR)\n      )\n    end\n\n    it \"returns a default manifest when config has none\" do\n      expect(envconf.manifest).to eq(File.expand_path('/some/direnv/manifests'))\n    end\n\n    it \"returns nothing for config_version when config has none\" do\n      expect(envconf.config_version).to be_nil\n    end\n\n    it \"returns a default of 0 for environment_timeout when config has none\" do\n      expect(envconf.environment_timeout).to eq(0)\n    end\n\n    it \"returns default of true for static_catalogs when config has none\" do\n      expect(envconf.static_catalogs).to eq(true)\n    end\n\n    it \"can still retrieve raw setting\" do\n      expect(envconf.raw_setting(:manifest)).to be_nil\n    end\n  end\n\n  describe \"with disable_per_environment_manifest\" do\n    let(:config) { double('config') }\n    let(:envconf) { Puppet::Settings::EnvironmentConf.new(\"/some/direnv\", config, [\"/global/modulepath\"]) }\n\n    context \"set true\" do\n      before(:each) do\n        Puppet[:default_manifest] = File.expand_path('/default/manifest')\n        Puppet[:disable_per_environment_manifest] = true\n      end\n\n      it \"ignores environment.conf manifest\" do\n        setup_environment_conf(config, :manifest => '/some/manifest.pp')\n\n        expect(envconf.manifest).to eq(File.expand_path('/default/manifest'))\n      end\n\n      it \"logs error when environment.conf has manifest set\" do\n        setup_environment_conf(config, :manifest => '/some/manifest.pp')\n\n        envconf.manifest\n        expect(@logs.first.to_s).to match(/disable_per_environment_manifest.*true.*environment.conf.*does not match the default_manifest/)\n      end\n\n      it \"does not log an error when environment.conf does not have a manifest set\" do\n        setup_environment_conf(config, :manifest => nil)\n\n        expect(envconf.manifest).to eq(File.expand_path('/default/manifest'))\n        expect(@logs).to be_empty\n      end\n    end\n\n    it \"uses environment.conf when false\" do\n      setup_environment_conf(config, :manifest => '/some/manifest.pp')\n\n      Puppet[:default_manifest] = File.expand_path('/default/manifest')\n      Puppet[:disable_per_environment_manifest] = false\n\n      expect(envconf.manifest).to eq(File.expand_path('/some/manifest.pp'))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/file_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/file_setting'\n\ndescribe Puppet::Settings::FileSetting do\n  FileSetting = Puppet::Settings::FileSetting\n\n  include PuppetSpec::Files\n\n  describe \"when controlling permissions\" do\n    def settings(wanted_values = {})\n       real_values = {\n        :user => 'root',\n        :group => 'root',\n        :mkusers => false,\n        :service_user_available? => false,\n        :service_group_available? => false\n      }.merge(wanted_values)\n\n      settings = double(\"settings\")\n\n      allow(settings).to receive(:[]).with(:user).and_return(real_values[:user])\n      allow(settings).to receive(:[]).with(:group).and_return(real_values[:group])\n      allow(settings).to receive(:[]).with(:mkusers).and_return(real_values[:mkusers])\n      allow(settings).to receive(:service_user_available?).and_return(real_values[:service_user_available?])\n      allow(settings).to receive(:service_group_available?).and_return(real_values[:service_group_available?])\n\n      settings\n    end\n\n    context \"owner\" do\n      it \"can always be root\" do\n        settings = settings(:user => \"the_service\", :mkusers => true)\n\n        setting = FileSetting.new(:settings => settings, :owner => \"root\", :desc => \"a setting\")\n\n        expect(setting.owner).to eq(\"root\")\n      end\n\n      it \"is the service user if we are making users\" do\n        settings = settings(:user => \"the_service\", :mkusers => true, :service_user_available? => false)\n\n        setting = FileSetting.new(:settings => settings, :owner => \"service\", :desc => \"a setting\")\n\n        expect(setting.owner).to eq(\"the_service\")\n      end\n\n      it \"is the service user if the user is available on the system\" do\n        settings = settings(:user => \"the_service\", :mkusers => false, :service_user_available? => true)\n\n        setting = FileSetting.new(:settings => settings, :owner => \"service\", :desc => \"a setting\")\n\n        expect(setting.owner).to eq(\"the_service\")\n      end\n\n      it \"is root when the setting specifies service and the user is not available on the system\" do\n        settings = settings(:user => \"the_service\", :mkusers => false, :service_user_available? => false)\n\n        setting = FileSetting.new(:settings => settings, :owner => \"service\", :desc => \"a setting\")\n\n        expect(setting.owner).to eq(\"root\")\n      end\n\n      it \"is unspecified when no specific owner is wanted\" do\n        expect(FileSetting.new(:settings => settings(), :desc => \"a setting\").owner).to be_nil\n      end\n\n      it \"does not allow other owners\" do\n        expect { FileSetting.new(:settings => settings(), :desc => \"a setting\", :name => \"testing\", :default => \"the default\", :owner => \"invalid\") }.\n          to raise_error(FileSetting::SettingError, /The :owner parameter for the setting 'testing' must be either 'root' or 'service'/)\n      end\n    end\n\n    context \"group\" do\n      it \"is unspecified when no specific group is wanted\" do\n        setting = FileSetting.new(:settings => settings(), :desc => \"a setting\")\n\n        expect(setting.group).to be_nil\n      end\n\n      it \"is root if root is requested\" do\n        settings = settings(:group => \"the_group\")\n\n        setting = FileSetting.new(:settings => settings, :group => \"root\", :desc => \"a setting\")\n\n        expect(setting.group).to eq(\"root\")\n      end\n\n      it \"is the service group if we are making users\" do\n        settings = settings(:group => \"the_service\", :mkusers => true)\n\n        setting = FileSetting.new(:settings => settings, :group => \"service\", :desc => \"a setting\")\n\n        expect(setting.group).to eq(\"the_service\")\n      end\n\n      it \"is the service user if the group is available on the system\" do\n        settings = settings(:group => \"the_service\", :mkusers => false, :service_group_available? => true)\n\n        setting = FileSetting.new(:settings => settings, :group => \"service\", :desc => \"a setting\")\n\n        expect(setting.group).to eq(\"the_service\")\n      end\n\n      it \"is unspecified when the setting specifies service and the group is not available on the system\" do\n        settings = settings(:group => \"the_service\", :mkusers => false, :service_group_available? => false)\n\n        setting = FileSetting.new(:settings => settings, :group => \"service\", :desc => \"a setting\")\n\n        expect(setting.group).to be_nil\n      end\n\n      it \"does not allow other groups\" do\n        expect { FileSetting.new(:settings => settings(), :group => \"invalid\", :name => 'testing', :desc => \"a setting\") }.\n          to raise_error(FileSetting::SettingError, /The :group parameter for the setting 'testing' must be either 'root' or 'service'/)\n      end\n    end\n  end\n\n  it \"should be able to be converted into a resource\" do\n    expect(FileSetting.new(:settings => double(\"settings\"), :desc => \"eh\")).to respond_to(:to_resource)\n  end\n\n  describe \"when being converted to a resource\" do\n    before do\n      @basepath = make_absolute(\"/somepath\")\n      allow(Puppet::FileSystem).to receive(:exist?).and_call_original\n      allow(Puppet::FileSystem).to receive(:exist?).with(@basepath).and_return(true)\n      @settings = double('settings')\n      @file = Puppet::Settings::FileSetting.new(:settings => @settings, :desc => \"eh\", :name => :myfile, :section => \"mysect\")\n      allow(@settings).to receive(:value).with(:myfile, nil, false).and_return(@basepath)\n    end\n\n    it \"should return :file as its type\" do\n      expect(@file.type).to eq(:file)\n    end\n\n    it \"skips non-existent files\" do\n      expect(@file).to receive(:type).and_return(:file)\n      expect(Puppet::FileSystem).to receive(:exist?).with(@basepath).and_return(false)\n      expect(@file.to_resource).to be_nil\n    end\n\n    it \"manages existing files\" do\n      expect(@file).to receive(:type).and_return(:file)\n      expect(@file.to_resource).to be_instance_of(Puppet::Resource)\n    end\n\n    it \"always manages directories\" do\n      expect(@file).to receive(:type).and_return(:directory)\n      expect(@file.to_resource).to be_instance_of(Puppet::Resource)\n    end\n\n    describe \"on POSIX systems\", :if => Puppet.features.posix? do\n      it \"should skip files in /dev\" do\n        allow(@settings).to receive(:value).with(:myfile, nil, false).and_return(\"/dev/file\")\n        expect(@file.to_resource).to be_nil\n      end\n    end\n\n    it \"should skip files whose paths are not strings\" do\n      allow(@settings).to receive(:value).with(:myfile, nil, false).and_return(:foo)\n      expect(@file.to_resource).to be_nil\n    end\n\n    it \"should return a file resource with the path set appropriately\" do\n      resource = @file.to_resource\n      expect(resource.type).to eq(\"File\")\n      expect(resource.title).to eq(@basepath)\n    end\n\n    it \"should have a working directory with a root directory not called dev\", :if => Puppet::Util::Platform.windows? do\n      # Although C:\\Dev\\.... is a valid path on Windows, some other code may regard it as a path to be ignored.  e.g. /dev/null resolves to C:\\dev\\null on Windows.\n      path = File.expand_path('somefile')\n      expect(path).to_not match(/^[A-Z]:\\/dev/i)\n    end\n\n    it \"should fully qualified returned files if necessary (#795)\" do\n      allow(@settings).to receive(:value).with(:myfile, nil, false).and_return(\"myfile\")\n      path = File.expand_path('myfile')\n      allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n      expect(@file.to_resource.title).to eq(path)\n    end\n\n    it \"should set the mode on the file if a mode is provided as an octal number\" do\n      Puppet[:manage_internal_file_permissions] = true\n      @file.mode = 0755\n\n      expect(@file.to_resource[:mode]).to eq('755')\n    end\n\n    it \"should set the mode on the file if a mode is provided as a string\" do\n      Puppet[:manage_internal_file_permissions] = true\n      @file.mode = '0755'\n\n      expect(@file.to_resource[:mode]).to eq('755')\n    end\n\n    it \"should not set the mode on a the file if manage_internal_file_permissions is disabled\" do\n      Puppet[:manage_internal_file_permissions] = false\n\n      allow(@file).to receive(:mode).and_return(0755)\n\n      expect(@file.to_resource[:mode]).to eq(nil)\n    end\n\n    it \"should set the owner if running as root and the owner is provided\" do\n      Puppet[:manage_internal_file_permissions] = true\n      expect(Puppet.features).to receive(:root?).and_return(true)\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      allow(@file).to receive(:owner).and_return(\"foo\")\n\n      expect(@file.to_resource[:owner]).to eq(\"foo\")\n    end\n\n    it \"should not set the owner if manage_internal_file_permissions is disabled\" do\n      Puppet[:manage_internal_file_permissions] = false\n      allow(Puppet.features).to receive(:root?).and_return(true)\n      allow(@file).to receive(:owner).and_return(\"foo\")\n\n      expect(@file.to_resource[:owner]).to eq(nil)\n    end\n\n    it \"should set the group if running as root and the group is provided\" do\n      Puppet[:manage_internal_file_permissions] = true\n      expect(Puppet.features).to receive(:root?).and_return(true)\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      allow(@file).to receive(:group).and_return(\"foo\")\n\n      expect(@file.to_resource[:group]).to eq(\"foo\")\n    end\n\n    it \"should not set the group if manage_internal_file_permissions is disabled\" do\n      Puppet[:manage_internal_file_permissions] = false\n      allow(Puppet.features).to receive(:root?).and_return(true)\n      allow(@file).to receive(:group).and_return(\"foo\")\n\n      expect(@file.to_resource[:group]).to eq(nil)\n    end\n\n    it \"should not set owner if not running as root\" do\n      Puppet[:manage_internal_file_permissions] = true\n      expect(Puppet.features).to receive(:root?).and_return(false)\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      allow(@file).to receive(:owner).and_return(\"foo\")\n\n      expect(@file.to_resource[:owner]).to be_nil\n    end\n\n    it \"should not set group if not running as root\" do\n      Puppet[:manage_internal_file_permissions] = true\n      expect(Puppet.features).to receive(:root?).and_return(false)\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      allow(@file).to receive(:group).and_return(\"foo\")\n\n      expect(@file.to_resource[:group]).to be_nil\n    end\n\n    describe \"on Microsoft Windows systems\" do\n      before :each do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n      end\n\n      it \"should not set owner\" do\n        allow(@file).to receive(:owner).and_return(\"foo\")\n        expect(@file.to_resource[:owner]).to be_nil\n      end\n\n      it \"should not set group\" do\n        allow(@file).to receive(:group).and_return(\"foo\")\n        expect(@file.to_resource[:group]).to be_nil\n      end\n    end\n\n    it \"should set :ensure to the file type\" do\n      expect(@file).to receive(:type).and_return(:directory)\n      expect(@file.to_resource[:ensure]).to eq(:directory)\n    end\n\n    it \"should set the loglevel to :debug\" do\n      expect(@file.to_resource[:loglevel]).to eq(:debug)\n    end\n\n    it \"should set the backup to false\" do\n      expect(@file.to_resource[:backup]).to be_falsey\n    end\n\n    it \"should tag the resource with the settings section\" do\n      expect(@file).to receive(:section).and_return(\"mysect\")\n      expect(@file.to_resource).to be_tagged(\"mysect\")\n    end\n\n    it \"should tag the resource with the setting name\" do\n      expect(@file.to_resource).to be_tagged(\"myfile\")\n    end\n\n    it \"should tag the resource with 'settings'\" do\n      expect(@file.to_resource).to be_tagged(\"settings\")\n    end\n\n    it \"should set links to 'follow'\" do\n      expect(@file.to_resource[:links]).to eq(:follow)\n    end\n  end\n\n  describe \"#munge\" do\n    it 'does not expand the path of the special value :memory: so we can set dblocation to an in-memory database' do\n      filesetting = FileSetting.new(:settings => double(\"settings\"), :desc => \"eh\")\n      expect(filesetting.munge(':memory:')).to eq(':memory:')\n    end\n  end\n\n  context \"when opening\", :unless => Puppet::Util::Platform.windows? do\n    let(:path) do\n      tmpfile('file_setting_spec')\n    end\n\n    let(:setting) do\n      settings = double(\"settings\", :value => path)\n      FileSetting.new(:name => :mysetting, :desc => \"creates a file\", :settings => settings)\n    end\n\n    it \"creates a file with mode 0640\" do\n      setting.mode = '0640'\n\n      expect(File).to_not be_exist(path)\n      setting.open('w')\n\n      expect(File).to be_exist(path)\n      expect(Puppet::FileSystem.stat(path).mode & 0777).to eq(0640)\n    end\n\n    it \"preserves the mode of an existing file\" do\n      setting.mode = '0640'\n\n      Puppet::FileSystem.touch(path)\n      Puppet::FileSystem.chmod(0644, path)\n      setting.open('w')\n\n      expect(Puppet::FileSystem.stat(path).mode & 0777).to eq(0644)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/http_extra_headers_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/http_extra_headers_setting'\n\ndescribe Puppet::Settings::HttpExtraHeadersSetting do\n  subject { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  it \"is of type :http_extra_headers\" do\n    expect(subject.type).to eq :http_extra_headers\n  end\n\n  describe \"munging the value\" do\n    let(:final_value) { [['header1', 'foo'], ['header2', 'bar']] }\n\n    describe \"when given a string\" do\n      it \"splits multiple values into an array\" do\n        expect(subject.munge(\"header1:foo,header2:bar\")).to match_array(final_value)\n      end\n\n      it \"strips whitespace between elements\" do\n        expect(subject.munge(\"header1:foo , header2:bar\")).to match_array(final_value)\n      end\n\n      it \"creates an array when one item is given\" do\n        expect(subject.munge(\"header1:foo\")).to match_array([['header1', 'foo']])\n      end\n    end\n\n    describe \"when given an array of strings\" do\n      it \"returns an array of arrays\" do\n        expect(subject.munge(['header1:foo', 'header2:bar'])).to match_array(final_value)\n      end\n    end\n\n    describe \"when given an array of arrays\" do\n      it \"returns an array of arrays\" do\n        expect(subject.munge([['header1', 'foo'], ['header2', 'bar']])).to match_array(final_value)\n      end\n    end\n\n    describe \"when given a hash\" do\n      it \"returns the hash\" do\n        expect(subject.munge({'header1' => 'foo', 'header2' => 'bar'})).to match_array(final_value)\n      end\n    end\n\n    describe 'raises an error when' do\n      it 'is given an unexpected object type' do\n        expect {\n          subject.munge(65)\n          }.to raise_error(ArgumentError, /^Expected an Array, String, or Hash, got a Integer/)\n      end\n\n      it 'is given an array of unexpected object types' do\n        expect {\n          subject.munge([65, 82])\n          }.to raise_error(ArgumentError, /^Expected an Array or String, got a Integer/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/ini_file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'stringio'\n\nrequire 'puppet/settings/ini_file'\n\ndescribe Puppet::Settings::IniFile do\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ𠜎\n\n  it \"preserves the file when no changes are made\" do\n    original_config = <<-CONFIG\n    # comment\n    [section]\n    name = value\n    CONFIG\n    config_fh = a_config_file_containing(original_config)\n\n    Puppet::Settings::IniFile.update(config_fh) do; end\n\n    expect(config_fh.string).to eq original_config\n  end\n\n  it \"adds a set name and value to an empty file\" do\n    config_fh = a_config_file_containing(\"\")\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"the_section\", \"name\", \"value\")\n    end\n\n    expect(config_fh.string).to eq \"[the_section]\\nname = value\\n\"\n  end\n\n  it \"adds a UTF-8 name and value to an empty file\" do\n    config_fh = a_config_file_containing(\"\")\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"the_section\", mixed_utf8, mixed_utf8.reverse)\n    end\n\n    expect(config_fh.string).to eq \"[the_section]\\n#{mixed_utf8} = #{mixed_utf8.reverse}\\n\"\n  end\n\n  it \"adds a [main] section to a file when it's needed\" do\n    config_fh = a_config_file_containing(<<-CONF)\n    [section]\n    name = different value\n    CONF\n\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"main\", \"name\", \"value\")\n    end\n\n    expect(config_fh.string).to eq(<<-CONF)\n[main]\nname = value\n    [section]\n    name = different value\n    CONF\n  end\n\n  it \"can update values within a UTF-8 section of an existing file\" do\n    config_fh = a_config_file_containing(<<-CONF)\n    [#{mixed_utf8}]\n    foo = default\n    CONF\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(mixed_utf8, 'foo', 'bar')\n    end\n\n    expect(config_fh.string).to eq(<<-CONF)\n    [#{mixed_utf8}]\n    foo = bar\n    CONF\n  end\n\n  it \"preserves comments when writing a new name and value\" do\n    config_fh = a_config_file_containing(\"# this is a comment\")\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"the_section\", \"name\", \"value\")\n    end\n\n    expect(config_fh.string).to eq \"# this is a comment\\n[the_section]\\nname = value\\n\"\n  end\n\n  it \"updates existing names and values in place\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    # this is the preceding comment\n     [section]\n    name = original value\n    # this is the trailing comment\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"name\", \"changed value\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    # this is the preceding comment\n     [section]\n    name = changed value\n    # this is the trailing comment\n    CONFIG\n  end\n\n  it \"updates existing empty settings\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    # this is the preceding comment\n     [section]\n    name = \n    # this is the trailing comment\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"name\", \"changed value\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    # this is the preceding comment\n     [section]\n    name = changed value\n    # this is the trailing comment\n    CONFIG\n  end\n\n  it \"can set empty settings\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    # this is the preceding comment\n     [section]\n    name = original value\n    # this is the trailing comment\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"name\", \"\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    # this is the preceding comment\n     [section]\n    name = \n    # this is the trailing comment\n    CONFIG\n  end\n\n  it \"updates existing UTF-8 name / values in place\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    # this is the preceding comment\n     [section]\n    ascii = foo\n    A\\u06FF\\u16A0\\u{2070E} = bar\n    # this is the trailing comment\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"ascii\", mixed_utf8)\n      config.set(\"section\", mixed_utf8, mixed_utf8.reverse)\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    # this is the preceding comment\n     [section]\n    ascii = #{mixed_utf8}\n    #{mixed_utf8} = #{mixed_utf8.reverse}\n    # this is the trailing comment\n    CONFIG\n  end\n\n  it \"updates only the value in the selected section\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    [other_section]\n    name = does not change\n    [section]\n    name = original value\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"name\", \"changed value\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    [other_section]\n    name = does not change\n    [section]\n    name = changed value\n    CONFIG\n  end\n\n  it \"considers settings found outside a section to be in section 'main'\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    name = original value\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"main\", \"name\", \"changed value\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n[main]\n    name = changed value\n    CONFIG\n  end\n\n  it \"adds new settings to an existing section\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    [section]\n    original = value\n\n    # comment about 'other' section\n    [other]\n    dont = change\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"updated\", \"new\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    [section]\n    original = value\nupdated = new\n\n    # comment about 'other' section\n    [other]\n    dont = change\n    CONFIG\n  end\n\n  it \"adds a new setting into an existing, yet empty section\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    [section]\n    [other]\n    dont = change\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"updated\", \"new\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    [section]\nupdated = new\n    [other]\n    dont = change\n    CONFIG\n  end\n\n  it \"finds settings when the section is split up\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    [section]\n    name = original value\n    [different]\n    name = other value\n    [section]\n    other_name = different original value\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"name\", \"changed value\")\n      config.set(\"section\", \"other_name\", \"other changed value\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    [section]\n    name = changed value\n    [different]\n    name = other value\n    [section]\n    other_name = other changed value\n    CONFIG\n  end\n\n  it \"adds a new setting to the appropriate section, when it would be added behind a setting with an identical value in a preceeding section\" do\n    config_fh = a_config_file_containing(<<-CONFIG)\n    [different]\n    name = some value\n    [section]\n    name = some value\n    CONFIG\n\n    Puppet::Settings::IniFile.update(config_fh) do |config|\n      config.set(\"section\", \"new\", \"new value\")\n    end\n\n    expect(config_fh.string).to eq <<-CONFIG\n    [different]\n    name = some value\n    [section]\n    name = some value\nnew = new value\n    CONFIG\n  end\n\n  context 'config with no main section' do\n    it 'file does not change when there are no sections or entries' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'missing')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      CONFIG\n    end\n\n    it 'when there is only 1 entry we can delete it' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      base = value\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      CONFIG\n    end\n\n    it 'we delete 1 entry from the default section and add the [main] section header' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      base = value\n      other = another value\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n[main]\n      other = another value\n      CONFIG\n    end\n\n    it 'we add [main] to the config file when attempting to delete a setting in another section' do\n      config_fh = a_config_file_containing(<<-CONF)\n      name = different value\n      CONF\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('section', 'name')\n      end\n\n      expect(config_fh.string).to eq(<<-CONF)\n[main]\n      name = different value\n      CONF\n    end\n  end\n\n  context 'config with 1 section' do\n    it 'file does not change when entry to delete does not exist' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      base = value\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'missing')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      base = value\n      CONFIG\n    end\n\n    it 'deletes the 1 entry in the section' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      base = DELETING\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      CONFIG\n    end\n\n    it 'deletes the entry and leaves another entry' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      base = DELETING\n      after = value to keep\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      after = value to keep\n      CONFIG\n    end\n\n    it 'deletes the entry while leaving other entries' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      before = value to keep before\n      base = DELETING\n      after = value to keep\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      before = value to keep before\n      after = value to keep\n      CONFIG\n    end\n\n    it 'when there are two entries of the same setting name delete one of them' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      base = value\n      base = value\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('main', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      base = value\n      CONFIG\n    end\n  end\n\n  context 'with 2 sections' do\n    it 'file does not change when entry to delete does not exist' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      base = value\n      [section]\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('section', 'missing')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      base = value\n      [section]\n      CONFIG\n    end\n\n    it 'deletes the 1 entry in the specified section' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      base = value\n      [section]\n      base = value\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('section', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      base = value\n      [section]\n      CONFIG\n    end\n\n    it 'deletes the entry while leaving other entries' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      before = value also staying\n      base = value staying\n      after = value to keep\n      [section]\n      before value in section keeping\n      base = DELETING\n      after = value to keep\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('section', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      before = value also staying\n      base = value staying\n      after = value to keep\n      [section]\n      before value in section keeping\n      after = value to keep\n      CONFIG\n    end\n  end\n\n  context 'with 2 sections' do\n    it 'deletes the entry while leaving other entries' do\n      config_fh = a_config_file_containing(<<-CONFIG)\n      [main]\n      before = value also staying\n      base = value staying\n      after = value to keep\n      [section]\n      before value in section keeping\n      base = DELETING\n      after = value to keep\n      [otherSection]\n      before value in section keeping\n      base = value to keep really\n      after = value to keep\n      CONFIG\n\n      Puppet::Settings::IniFile.update(config_fh) do |config|\n        config.delete('section', 'base')\n      end\n\n      expect(config_fh.string).to eq <<-CONFIG\n      [main]\n      before = value also staying\n      base = value staying\n      after = value to keep\n      [section]\n      before value in section keeping\n      after = value to keep\n      [otherSection]\n      before value in section keeping\n      base = value to keep really\n      after = value to keep\n      CONFIG\n    end\n  end\n\n  def a_config_file_containing(text)\n    StringIO.new(text.encode(Encoding::UTF_8))\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/integer_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/integer_setting'\n\ndescribe Puppet::Settings::IntegerSetting do\n  let(:setting) { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  it \"is of type :integer\" do\n    expect(setting.type).to eq(:integer)\n  end\n\n  describe \"when munging the setting\" do\n    it \"returns the same value if given a positive integer\" do\n      expect(setting.munge(5)).to eq(5)\n    end\n\n    it \"returns the same value if given a negative integer\" do\n      expect(setting.munge(-25)).to eq(-25)\n    end\n\n    it \"returns an integer if given a valid integer as string\" do\n      expect(setting.munge('12')).to eq(12)\n    end\n\n    it \"returns an integer if given a valid negative integer as string\" do\n      expect(setting.munge('-12')).to eq(-12)\n    end\n\n    it \"returns an integer if given a valid positive integer as string\" do\n      expect(setting.munge('+12')).to eq(12)\n    end\n\n    it \"raises if given an invalid value\" do\n      expect { setting.munge('a5') }.to raise_error(Puppet::Settings::ValidationError)\n    end\n\n    it \"raises if given nil\" do\n      expect { setting.munge(nil) }.to raise_error(Puppet::Settings::ValidationError)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/path_setting_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Settings::PathSetting do\n  subject { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  context \"#munge\" do\n    it \"should expand all path elements\" do\n      munged = subject.munge(\"hello#{File::PATH_SEPARATOR}good/morning#{File::PATH_SEPARATOR}goodbye\")\n      munged.split(File::PATH_SEPARATOR).each do |p|\n        expect(Puppet::Util).to be_absolute_path(p)\n      end\n    end\n\n    it \"should leave nil as nil\" do\n      expect(subject.munge(nil)).to be_nil\n    end\n\n    context \"on Windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should convert \\\\ to /\" do\n        expect(subject.munge('C:\\test\\directory')).to eq('C:/test/directory')\n      end\n\n      it \"should work with UNC paths\" do\n        expect(subject.munge('//localhost/some/path')).to eq('//localhost/some/path')\n        expect(subject.munge('\\\\\\\\localhost\\some\\path')).to eq('//localhost/some/path')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/port_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/port_setting'\n\ndescribe Puppet::Settings::PortSetting do\n  let(:setting) { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  it \"is of type :port\" do\n    expect(setting.type).to eq(:port)\n  end\n\n  describe \"when munging the setting\" do\n    it \"returns the same value if given a valid port as integer\" do\n      expect(setting.munge(5)).to eq(5)\n    end\n\n    it \"returns an integer if given valid port as string\" do\n      expect(setting.munge('12')).to eq(12)\n    end\n\n    it \"raises if given a negative port number\" do\n      expect { setting.munge('-5') }.to raise_error(Puppet::Settings::ValidationError)\n    end\n\n    it \"raises if the port number is too high\" do\n      expect { setting.munge(65536) }.to raise_error(Puppet::Settings::ValidationError)\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/priority_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/priority_setting'\nrequire 'puppet/util/platform'\n\ndescribe Puppet::Settings::PrioritySetting do\n  let(:setting) { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  it \"is of type :priority\" do\n    expect(setting.type).to eq(:priority)\n  end\n\n  describe \"when munging the setting\" do\n    it \"passes nil through\" do\n      expect(setting.munge(nil)).to be_nil\n    end\n\n    it \"returns the same value if given an integer\" do\n      expect(setting.munge(5)).to eq(5)\n    end\n\n    it \"returns an integer if given a decimal string\" do\n      expect(setting.munge('12')).to eq(12)\n    end\n\n    it \"returns a negative integer if given a negative integer string\" do\n      expect(setting.munge('-5')).to eq(-5)\n    end\n\n    it \"fails if given anything else\" do\n      [ 'foo', 'realtime', true, 8.3, [] ].each do |value|\n        expect {\n          setting.munge(value)\n        }.to raise_error(Puppet::Settings::ValidationError)\n      end\n    end\n\n    describe \"on a Unix-like platform it\", :unless => Puppet::Util::Platform.windows? do\n      it \"parses high, normal, low, and idle priorities\" do\n        {\n          'high'   => -10,\n          'normal' => 0,\n          'low'    => 10,\n          'idle'   => 19\n        }.each do |value, converted_value|\n          expect(setting.munge(value)).to eq(converted_value)\n        end\n      end\n    end\n\n    describe \"on a Windows-like platform it\", :if => Puppet::Util::Platform.windows? do\n      it \"parses high, normal, low, and idle priorities\" do\n        {\n          'high'   => Puppet::FFI::Windows::Constants::HIGH_PRIORITY_CLASS,\n          'normal' => Puppet::FFI::Windows::Constants::NORMAL_PRIORITY_CLASS,\n          'low'    => Puppet::FFI::Windows::Constants::BELOW_NORMAL_PRIORITY_CLASS,\n          'idle'   => Puppet::FFI::Windows::Constants::IDLE_PRIORITY_CLASS\n        }.each do |value, converted_value|\n          expect(setting.munge(value)).to eq(converted_value)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/server_list_setting_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/settings/server_list_setting'\n\ndescribe Puppet::Settings::ServerListSetting do\n\n    it \"prints strings as strings\" do\n      settings = Puppet::Settings.new\n      settings.define_settings(:main, neptune: {type: :server_list, desc: 'list of servers'})\n      server_list_setting = settings.setting(:neptune)\n      expect(server_list_setting.print(\"jupiter,mars\")).to eq(\"jupiter,mars\")\n    end\n\n    it \"prints arrays as strings\" do\n      settings = Puppet::Settings.new\n      settings.define_settings(:main, neptune: {type: :server_list, desc: 'list of servers'})\n      server_list_setting = settings.setting(:neptune)\n      expect(server_list_setting.print([[\"main\", 1234],[\"production\", 8140]])).to eq(\"main:1234,production:8140\")\n      expect(server_list_setting.print([])).to eq(\"\")\n    end\n\nend"
  },
  {
    "path": "spec/unit/settings/string_setting_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/settings'\nrequire 'puppet/settings/string_setting'\n\ndescribe Puppet::Settings::StringSetting do\n  StringSetting = Puppet::Settings::StringSetting\n\n  before(:each) do\n    @test_setting_name = :test_setting\n    @test_setting_default = \"my_crazy_default/$var\"\n    @application_setting = \"application/$var\"\n    @application_defaults = { }\n    Puppet::Settings::REQUIRED_APP_SETTINGS.each do |key|\n      @application_defaults[key] = \"foo\"\n    end\n    @application_defaults[:run_mode] = :user\n    @settings = Puppet::Settings.new\n    @application_defaults.each { |k,v| @settings.define_settings :main, k => {:default=>\"\", :desc => \"blah\"} }\n    @settings.define_settings :main, :var               => {  :default => \"interpolate!\",\n                                                              :type => :string,\n                                                              :desc => \"my var desc\" },\n                                     @test_setting_name => {  :default => @test_setting_default,\n                                                              :type => :string,\n                                                              :desc => \"my test desc\" }\n    @test_setting = @settings.setting(@test_setting_name)\n  end\n\n  describe \"#default\" do\n    describe \"with no arguments\" do\n      it \"should return the setting default\" do\n        expect(@test_setting.default).to eq(@test_setting_default)\n      end\n\n      it \"should be uninterpolated\" do\n        expect(@test_setting.default).not_to match(/interpolate/)\n      end\n    end\n\n    describe \"checking application defaults first\" do\n      describe \"if application defaults set\" do\n        before(:each) do\n          @settings.initialize_app_defaults @application_defaults.merge @test_setting_name => @application_setting\n        end\n\n        it \"should return the application-set default\" do\n          expect(@test_setting.default(true)).to eq(@application_setting)\n        end\n\n        it \"should be uninterpolated\" do\n          expect(@test_setting.default(true)).not_to match(/interpolate/)\n        end\n\n      end\n\n      describe \"if application defaults not set\" do\n        it \"should return the regular default\" do\n          expect(@test_setting.default(true)).to eq(@test_setting_default)\n        end\n\n        it \"should be uninterpolated\" do\n          expect(@test_setting.default(true)).not_to match(/interpolate/)\n        end\n      end\n    end\n  end\n\n  describe \"#value\" do\n    it \"should be interpolated\" do\n      expect(@test_setting.value).to match(/interpolate/)\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/settings/terminus_setting_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Settings::TerminusSetting do\n  let(:setting) { described_class.new(:settings => double('settings'), :desc => \"test\") }\n\n  describe \"#munge\" do\n    it \"converts strings to symbols\" do\n      expect(setting.munge(\"string\")).to eq(:string)\n    end\n\n    it \"converts '' to nil\" do\n      expect(setting.munge('')).to be_nil\n    end\n\n    it \"preserves symbols\" do\n      expect(setting.munge(:symbol)).to eq(:symbol)\n    end\n\n    it \"preserves nil\" do\n      expect(setting.munge(nil)).to be_nil\n    end\n\n    it \"does not allow unknown types through\" do\n      expect { setting.munge([\"not a terminus type\"]) }.to raise_error Puppet::Settings::ValidationError\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings/value_translator_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/settings/value_translator'\n\ndescribe Puppet::Settings::ValueTranslator do\n  let(:translator) { Puppet::Settings::ValueTranslator.new }\n\n  context \"booleans\" do\n    it \"translates strings representing booleans to booleans\" do\n      expect(translator['true']).to eq(true)\n      expect(translator['false']).to eq(false)\n    end\n\n    it \"translates boolean values into themselves\" do\n      expect(translator[true]).to eq(true)\n      expect(translator[false]).to eq(false)\n    end\n\n    it \"leaves a boolean string with whitespace as a string\" do\n      expect(translator[' true']).to eq(\" true\")\n      expect(translator['true ']).to eq(\"true\")\n\n      expect(translator[' false']).to eq(\" false\")\n      expect(translator['false ']).to eq(\"false\")\n    end\n  end\n\n  context \"numbers\" do\n    it \"leaves integer strings\" do\n      expect(translator[\"1\"]).to eq(\"1\")\n    end\n\n    it \"leaves octal numbers as strings\" do\n      expect(translator[\"011\"]).to eq(\"011\")\n    end\n\n    it \"leaves hex numbers as strings\" do\n      expect(translator[\"0x11\"]).to eq(\"0x11\")\n    end\n  end\n\n  context \"arbitrary strings\" do\n    it \"translates an empty string as the empty string\" do\n      expect(translator[\"\"]).to eq(\"\")\n    end\n\n\n    it \"strips double quotes\" do\n      expect(translator['\"a string\"']).to eq('a string')\n    end\n\n    it \"strips single quotes\" do\n      expect(translator[\"'a string'\"]).to eq(\"a string\")\n    end\n\n    it \"does not strip preceding whitespace\" do\n      expect(translator[\" \\ta string\"]).to eq(\" \\ta string\")\n    end\n\n    it \"strips trailing whitespace\" do\n      expect(translator[\"a string\\t \"]).to eq(\"a string\")\n    end\n\n    it \"leaves leading quote that is preceded by whitespace\" do\n      expect(translator[\" 'a string'\"]).to eq(\" 'a string\")\n    end\n\n    it \"leaves trailing quote that is succeeded by whitespace\" do\n      expect(translator[\"'a string' \"]).to eq(\"a string'\")\n    end\n\n    it \"leaves quotes that are not at the beginning or end of the string\" do\n      expect(translator[\"a st'\\\"ring\"]).to eq(\"a st'\\\"ring\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/settings_spec.rb",
    "content": "require 'spec_helper'\nrequire 'ostruct'\nrequire 'puppet/settings/errors'\nrequire 'puppet_spec/files'\nrequire 'matchers/resource'\n\ndescribe Puppet::Settings do\n  include PuppetSpec::Files\n  include Matchers::Resource\n\n  let(:main_config_file_default_location) do\n    File.join(Puppet::Util::RunMode[:server].conf_dir, \"puppet.conf\")\n  end\n\n  let(:user_config_file_default_location) do\n    File.join(Puppet::Util::RunMode[:user].conf_dir, \"puppet.conf\")\n  end\n\n  # Return a given object's file metadata.\n  def metadata(setting)\n    if setting.is_a?(Puppet::Settings::FileSetting)\n      {\n        :owner => setting.owner,\n        :group => setting.group,\n        :mode => setting.mode\n      }.delete_if { |key, value| value.nil? }\n    else\n      nil\n    end\n  end\n\n  def stub_config_with(content)\n    allow(Puppet.features).to receive(:root?).and_return(false)\n    expect(Puppet::FileSystem).to receive(:exist?).\n      with(user_config_file_default_location).\n      and_return(true).ordered\n    expect(@settings).to receive(:read_file).\n      with(user_config_file_default_location).\n      and_return(content).ordered\n    @settings.send(:parse_config_files)\n  end\n\n  describe \"when specifying defaults\" do\n    before do\n      @settings = Puppet::Settings.new\n    end\n\n    it \"should start with no defined sections or parameters\" do\n      # Note this relies on undocumented side effect that eachsection returns the Settings internal\n      # configuration on which keys returns all parameters.\n      expect(@settings.eachsection.keys.length).to eq(0)\n    end\n\n    it \"should not allow specification of default values associated with a section as an array\" do\n      expect {\n        @settings.define_settings(:section, :myvalue => [\"defaultval\", \"my description\"])\n      }.to raise_error(ArgumentError, /setting definition for 'myvalue' is not a hash!/)\n    end\n\n    it \"should not allow duplicate parameter specifications\" do\n      @settings.define_settings(:section, :myvalue => { :default => \"a\", :desc => \"b\" })\n      expect { @settings.define_settings(:section, :myvalue => { :default => \"c\", :desc => \"d\" }) }.to raise_error(ArgumentError)\n    end\n\n    it \"should allow specification of default values associated with a section as a hash\" do\n      @settings.define_settings(:section, :myvalue => {:default => \"defaultval\", :desc => \"my description\"})\n    end\n\n    it \"should consider defined parameters to be valid\" do\n      @settings.define_settings(:section, :myvalue => { :default => \"defaultval\", :desc => \"my description\" })\n      expect(@settings.valid?(:myvalue)).to be_truthy\n    end\n\n    it \"should require a description when defaults are specified with a hash\" do\n      expect { @settings.define_settings(:section, :myvalue => {:default => \"a value\"}) }.to raise_error(ArgumentError)\n    end\n\n    it \"should support specifying owner, group, and mode when specifying files\" do\n      @settings.define_settings(:section, :myvalue => {:type => :file, :default => \"/some/file\", :owner => \"service\", :mode => \"boo\", :group => \"service\", :desc => \"whatever\"})\n    end\n\n    it \"should support specifying a short name\" do\n      @settings.define_settings(:section, :myvalue => {:default => \"w\", :desc => \"b\", :short => \"m\"})\n    end\n\n    it \"should support specifying the setting type\" do\n      @settings.define_settings(:section, :myvalue => {:default => \"/w\", :desc => \"b\", :type => :string})\n      expect(@settings.setting(:myvalue)).to be_instance_of(Puppet::Settings::StringSetting)\n    end\n\n    it \"should fail if an invalid setting type is specified\" do\n      expect { @settings.define_settings(:section, :myvalue => {:default => \"w\", :desc => \"b\", :type => :foo}) }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail when short names conflict\" do\n      @settings.define_settings(:section, :myvalue => {:default => \"w\", :desc => \"b\", :short => \"m\"})\n      expect { @settings.define_settings(:section, :myvalue => {:default => \"w\", :desc => \"b\", :short => \"m\"}) }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"when initializing application defaults do\" do\n    before do\n      @settings = Puppet::Settings.new\n      @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n    end\n\n    it \"should fail if the app defaults hash is missing any required values\" do\n      expect {\n        @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.reject { |key, _| key == :confdir })\n      }.to raise_error(Puppet::Settings::SettingsError)\n    end\n\n    # ultimately I'd like to stop treating \"run_mode\" as a normal setting, because it has so many special\n    #  case behaviors / uses.  However, until that time... we need to make sure that our private run_mode=\n    #  setter method gets properly called during app initialization.\n    it \"sets the preferred run mode when initializing the app defaults\" do\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :server))\n\n      expect(@settings.preferred_run_mode).to eq(:server)\n    end\n\n    it \"creates ancestor directories for all required app settings\" do\n      # initialize_app_defaults is called in spec_helper, before we even\n      # get here, but call it here to make it explicit what we're trying\n      # to do.\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n\n      Puppet::Settings::REQUIRED_APP_SETTINGS.each do |key|\n        expect(File).to exist(File.dirname(Puppet[key]))\n      end\n    end\n  end\n\n  describe \"#call_hooks_deferred_to_application_initialization\" do\n    let(:good_default) { \"yay\" }\n    let(:bad_default) { \"$doesntexist\" }\n    before(:each) do\n      @settings = Puppet::Settings.new\n    end\n\n    describe \"when ignoring dependency interpolation errors\" do\n      let(:options) { {:ignore_interpolation_dependency_errors => true} }\n\n      describe \"if interpolation error\" do\n        it \"should not raise an error\" do\n          hook_values = []\n          @settings.define_settings(:section, :badhook => {:default => bad_default, :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v  }})\n          expect do\n            @settings.send(:call_hooks_deferred_to_application_initialization, options)\n          end.to_not raise_error\n        end\n      end\n\n      describe \"if no interpolation error\" do\n        it \"should not raise an error\" do\n          hook_values = []\n          @settings.define_settings(:section, :goodhook => {:default => good_default, :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v  }})\n          expect do\n            @settings.send(:call_hooks_deferred_to_application_initialization, options)\n          end.to_not raise_error\n        end\n      end\n    end\n\n    describe \"when not ignoring dependency interpolation errors\" do\n      [ {}, {:ignore_interpolation_dependency_errors => false}].each do |options|\n        describe \"if interpolation error\" do\n          it \"should raise an error\" do\n            hook_values = []\n            @settings.define_settings(\n              :section,\n              :badhook => {\n                :default => bad_default,\n                :desc => \"boo\",\n                :call_hook => :on_initialize_and_write,\n                :hook => lambda { |v| hook_values << v }\n              }\n            )\n            expect do\n              @settings.send(:call_hooks_deferred_to_application_initialization, options)\n            end.to raise_error(Puppet::Settings::InterpolationError)\n          end\n\n          it \"should contain the setting name in error message\" do\n            hook_values = []\n            @settings.define_settings(\n              :section,\n              :badhook => {\n                :default => bad_default,\n                :desc => \"boo\",\n                :call_hook => :on_initialize_and_write,\n                :hook => lambda { |v| hook_values << v }\n              }\n            )\n            expect do\n              @settings.send(:call_hooks_deferred_to_application_initialization, options)\n            end.to raise_error(Puppet::Settings::InterpolationError, /badhook/)\n          end\n        end\n\n        describe \"if no interpolation error\" do\n          it \"should not raise an error\" do\n            hook_values = []\n            @settings.define_settings(\n              :section,\n              :goodhook => {\n                :default => good_default,\n                :desc => \"boo\",\n                :call_hook => :on_initialize_and_write,\n                :hook => lambda { |v| hook_values << v }\n              }\n            )\n            expect do\n              @settings.send(:call_hooks_deferred_to_application_initialization, options)\n            end.to_not raise_error\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when setting values\" do\n    before do\n      @settings = Puppet::Settings.new\n      @settings.define_settings :main, :myval => { :default => \"val\", :desc => \"desc\" }\n      @settings.define_settings :main, :bool => { :type => :boolean, :default => true, :desc => \"desc\" }\n    end\n\n    it \"should provide a method for setting values from other objects\" do\n      @settings[:myval] = \"something else\"\n      expect(@settings[:myval]).to eq(\"something else\")\n    end\n\n    it \"should support a getopt-specific mechanism for setting values\" do\n      @settings.handlearg(\"--myval\", \"newval\")\n      expect(@settings[:myval]).to eq(\"newval\")\n    end\n\n    it \"should support a getopt-specific mechanism for turning booleans off\" do\n      @settings.override_default(:bool, true)\n      @settings.handlearg(\"--no-bool\", \"\")\n      expect(@settings[:bool]).to eq(false)\n    end\n\n    it \"should support a getopt-specific mechanism for turning booleans on\" do\n      # Turn it off first\n      @settings.override_default(:bool, false)\n      @settings.handlearg(\"--bool\", \"\")\n      expect(@settings[:bool]).to eq(true)\n    end\n\n    it \"should consider a cli setting with no argument to be a boolean\" do\n      # Turn it off first\n      @settings.override_default(:bool, false)\n      @settings.handlearg(\"--bool\")\n      expect(@settings[:bool]).to eq(true)\n    end\n\n    it \"should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean\" do\n      @settings.override_default(:myval, \"bob\")\n      @settings.handlearg(\"--myval\", \"\")\n      expect(@settings[:myval]).to eq(\"\")\n    end\n\n    it \"should consider a cli setting with a boolean as an argument to be a boolean\" do\n      # Turn it off first\n      @settings.override_default(:bool, false)\n      @settings.handlearg(\"--bool\", \"true\")\n      expect(@settings[:bool]).to eq(true)\n    end\n\n    it \"should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean\" do\n      @settings.override_default(:myval, \"bob\")\n      @settings.handlearg(\"--no-myval\", \"\")\n      expect(@settings[:myval]).to eq(\"\")\n    end\n\n    it \"should retrieve numeric settings from the CLI\" do\n      @settings.handlearg(\"--myval\", \"12\")\n      expect(@settings.set_by_cli(:myval)).to eq(\"12\")\n      expect(@settings.set_by_cli?(:myval)).to be true\n    end\n\n    it \"should retrieve string settings from the CLI\" do\n      @settings.handlearg(\"--myval\", \"something\")\n      expect(@settings.set_by_cli(:myval)).to eq(\"something\")\n      expect(@settings.set_by_cli?(:myval)).to be true\n    end\n\n    it \"should retrieve bool settings from the CLI\" do\n      @settings.handlearg(\"--bool\")\n      expect(@settings.set_by_cli(:bool)).to be true\n      expect(@settings.set_by_cli?(:bool)).to be true\n    end\n\n    it \"should not retrieve settings set in memory as from CLI\" do\n      @settings[:myval] = \"12\"\n      expect(@settings.set_by_cli?(:myval)).to be false\n    end\n\n    it \"should find no configured settings by default\" do\n      expect(@settings.set_by_config?(:myval)).to be false\n    end\n\n    it \"should identify configured settings in memory\" do\n      expect(@settings.instance_variable_get(:@value_sets)[:memory]).to receive(:lookup).with(:myval).and_return('foo')\n      expect(@settings.set_by_config?(:myval)).to be_truthy\n    end\n\n    it \"should identify configured settings from CLI\" do\n      expect(@settings.instance_variable_get(:@value_sets)[:cli]).to receive(:lookup).with(:myval).and_return('foo')\n      expect(@settings.set_by_config?(:myval)).to be_truthy\n    end\n\n    it \"should not identify configured settings from environment by default\" do\n      expect(Puppet.lookup(:environments)).not_to receive(:get_conf).with(Puppet[:environment].to_sym)\n      expect(@settings.set_by_config?(:manifest)).to be_falsey\n    end\n\n    it \"should identify configured settings from environment by when an environment is specified\" do\n      foo = double('environment', :manifest => 'foo')\n      expect(Puppet.lookup(:environments)).to receive(:get_conf).with(Puppet[:environment].to_sym).and_return(foo)\n      expect(@settings.set_by_config?(:manifest, Puppet[:environment])).to be_truthy\n    end\n\n    context \"when handling puppet.conf\" do\n      describe \"#set_by_config?\" do\n        it \"should identify configured settings from the preferred run mode\" do\n          stub_config_with(<<~CONFIG)\n          [#{@settings.preferred_run_mode}]\n          myval = foo\n          CONFIG\n\n          expect(@settings.set_by_config?(:myval)).to be_truthy\n        end\n\n        it \"should identify configured settings from the specified run mode\" do\n          stub_config_with(<<~CONFIG)\n          [server]\n          myval = foo\n          CONFIG\n\n          expect(@settings.set_by_config?(:myval, nil, :server)).to be_truthy\n        end\n\n        it \"should not identify configured settings from an unspecified run mode\" do\n          stub_config_with(<<~CONFIG)\n          [zaz]\n          myval = foo\n          CONFIG\n\n          expect(@settings.set_by_config?(:myval)).to be_falsey\n        end\n\n        it \"should identify configured settings from the main section\" do\n          stub_config_with(<<~CONFIG)\n          [main]\n          myval = foo\n          CONFIG\n\n          expect(@settings.set_by_config?(:myval)).to be_truthy\n        end\n      end\n\n      describe \"#set_in_section\" do\n        it \"should retrieve configured settings from the specified section\" do\n          stub_config_with(<<~CONFIG)\n          [agent]\n          myval = foo\n          CONFIG\n\n          expect(@settings.set_in_section(:myval, :agent)).to eq(\"foo\")\n          expect(@settings.set_in_section?(:myval, :agent)).to be true\n        end\n\n        it \"should not retrieve configured settings from a different section\" do\n          stub_config_with(<<~CONFIG)\n          [main]\n          myval = foo\n          CONFIG\n\n          expect(@settings.set_in_section(:myval, :agent)).to be nil\n          expect(@settings.set_in_section?(:myval, :agent)).to be false\n        end\n      end\n    end\n\n    it \"should clear the cache when setting getopt-specific values\" do\n      @settings.define_settings :mysection,\n          :one => { :default => \"whah\", :desc => \"yay\" },\n          :two => { :default => \"$one yay\", :desc => \"bah\" }\n      expect(@settings).to receive(:unsafe_flush_cache)\n      expect(@settings[:two]).to eq(\"whah yay\")\n      @settings.handlearg(\"--one\", \"else\")\n      expect(@settings[:two]).to eq(\"else yay\")\n    end\n\n    it \"should clear the cache when the preferred_run_mode is changed\" do\n      expect(@settings).to receive(:flush_cache)\n      @settings.preferred_run_mode = :server\n    end\n\n    it \"should not clear other values when setting getopt-specific values\" do\n      @settings[:myval] = \"yay\"\n      @settings.handlearg(\"--no-bool\", \"\")\n      expect(@settings[:myval]).to eq(\"yay\")\n    end\n\n    it \"should clear the list of used sections\" do\n      expect(@settings).to receive(:clearused)\n      @settings[:myval] = \"yay\"\n    end\n\n    describe \"call_hook\" do\n      let(:config_file) { tmpfile('config') }\n\n      before :each do\n        # We can't specify the config file to read from using `Puppet[:config] =`\n        # or pass it as an arg to Puppet.initialize_global_settings, because\n        # both of those will set the value on the `Puppet.settings` instance\n        # which is different from the `@settings` instance created in the test.\n        # Instead, we define a `:config` setting and set its default value to\n        # the `config_file` temp file, and then access the `config_file` within\n        # each test.\n        @settings.define_settings(:main, :config => { :type => :file, :desc => \"config file\", :default => config_file })\n      end\n\n      Puppet::Settings::StringSetting.available_call_hook_values.each do |val|\n        describe \"when :#{val}\" do\n          describe \"and definition invalid\" do\n            it \"should raise error if no hook defined\" do\n              expect do\n                @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => val})\n              end.to raise_error(ArgumentError, /no :hook/)\n            end\n\n            it \"should include the setting name in the error message\" do\n              expect do\n                @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => val})\n              end.to raise_error(ArgumentError, /for :setting/)\n            end\n          end\n\n          describe \"and definition valid\" do\n            before(:each) do\n              hook_values = []\n              @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => val, :hook => lambda { |v| hook_values << v  }})\n            end\n\n            it \"should call the hook when value written\" do\n              expect(@settings.setting(:setting)).to receive(:handle).with(\"something\").once\n              @settings[:setting] = \"something\"\n            end\n          end\n        end\n      end\n\n      it \"should have a default value of :on_write_only\" do\n        @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| hook_values << v  }})\n        expect(@settings.setting(:setting).call_hook).to eq(:on_write_only)\n      end\n\n      describe \"when nil\" do\n        it \"should generate a warning\" do\n          expect(Puppet).to receive(:warning)\n          @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => nil, :hook => lambda { |v| hook_values << v  }})\n        end\n\n        it \"should use default\" do\n          @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => nil, :hook => lambda { |v| hook_values << v  }})\n          expect(@settings.setting(:setting).call_hook).to eq(:on_write_only)\n        end\n      end\n\n      describe \"when invalid\" do\n        it \"should raise an error\" do\n          expect do\n            @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :foo, :hook => lambda { |v| hook_values << v  }})\n          end.to raise_error(ArgumentError, /invalid.*call_hook/i)\n        end\n      end\n\n      describe \"when :on_write_only\" do\n        it \"returns its hook type\" do\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |_| }})\n\n          expect(@settings.setting(:setting).call_hook).to eq(:on_write_only)\n        end\n\n        it \"should not call the hook at definition\" do\n          hook_values = []\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| hook_values << v  }})\n\n          expect(hook_values).to eq(%w[])\n        end\n\n        it \"calls the hook when initializing global defaults with the value from the `main` section\" do\n          hook_values = []\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| hook_values << v  }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n          END\n          @settings.initialize_global_settings\n\n          expect(@settings[:setting]).to eq('in_main')\n          expect(hook_values).to eq(%w[in_main])\n        end\n\n        it \"doesn't call the hook when initializing app defaults\" do\n          hook_values = []\n          @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| hook_values << v }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n            [agent]\n            setting=in_agent\n          END\n          @settings.initialize_global_settings\n\n          hook_values.clear\n\n          @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n\n          expect(@settings[:setting]).to eq('in_main')\n          expect(hook_values).to eq(%w[])\n        end\n\n        it \"doesn't call the hook with value from a section that matches the run_mode\" do\n          hook_values = []\n          @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| hook_values << v  }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n            [agent]\n            setting=in_agent\n          END\n          @settings.initialize_global_settings\n\n          hook_values.clear\n\n          @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :agent))\n\n          expect(@settings[:setting]).to eq('in_agent')\n          expect(hook_values).to eq(%w[])\n        end\n      end\n\n      describe \"when :on_define_and_write\" do\n        it \"returns its hook type\" do\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |_| }})\n\n          expect(@settings.setting(:setting).call_hook).to eq(:on_define_and_write)\n        end\n\n        it \"should call the hook at definition with the default value\" do\n          hook_values = []\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |v| hook_values << v  }})\n\n          expect(hook_values).to eq(%w[yay])\n        end\n\n        it \"calls the hook when initializing global defaults with the value from the `main` section\" do\n          hook_values = []\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |v| hook_values << v  }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n          END\n          @settings.initialize_global_settings\n\n          expect(@settings[:setting]).to eq('in_main')\n          expect(hook_values).to eq(%w[yay in_main])\n        end\n\n        it \"doesn't call the hook when initializing app defaults\" do\n          hook_values = []\n          @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |v| hook_values << v  }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n            [agent]\n            setting=in_agent\n          END\n          @settings.initialize_global_settings\n\n          hook_values.clear\n\n          @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n\n          expect(@settings[:setting]).to eq('in_main')\n          expect(hook_values).to eq([])\n        end\n\n        it \"doesn't call the hook with value from a section that matches the run_mode\" do\n          hook_values = []\n          @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |v| hook_values << v  }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n            [agent]\n            setting=in_agent\n          END\n\n          @settings.initialize_global_settings\n\n          hook_values.clear\n\n          @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :agent))\n\n          # The correct value is returned\n          expect(@settings[:setting]).to eq('in_agent')\n\n          # but the hook is never called, seems like a bug!\n          expect(hook_values).to eq([])\n        end\n      end\n\n      describe \"when :on_initialize_and_write\" do\n        it \"returns its hook type\" do\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |_| }})\n\n          expect(@settings.setting(:setting).call_hook).to eq(:on_initialize_and_write)\n        end\n\n        it \"should not call the hook at definition\" do\n          hook_values = []\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }})\n          expect(hook_values).to eq([])\n        end\n\n        it \"calls the hook when initializing global defaults with the value from the `main` section\" do\n          hook_values = []\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n          END\n          @settings.initialize_global_settings\n\n          expect(@settings[:setting]).to eq('in_main')\n          expect(hook_values).to eq(%w[in_main])\n        end\n\n        it \"calls the hook when initializing app defaults\" do\n          hook_values = []\n          @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n            [agent]\n            setting=in_agent\n          END\n          @settings.initialize_global_settings\n\n          hook_values.clear\n\n          @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n\n          expect(@settings[:setting]).to eq('in_main')\n          expect(hook_values).to eq(%w[in_main])\n        end\n\n        it \"calls the hook with the overridden value from a section that matches the run_mode\" do\n          hook_values = []\n          @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n          @settings.define_settings(:main, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v  }})\n\n          File.write(config_file, <<~END)\n            [main]\n            setting=in_main\n            [agent]\n            setting=in_agent\n          END\n          @settings.initialize_global_settings\n\n          hook_values.clear\n\n          @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :agent))\n\n          expect(@settings[:setting]).to eq('in_agent')\n          expect(hook_values).to eq(%w[in_agent])\n        end\n      end\n    end\n\n    it \"should call passed blocks when values are set\" do\n      values = []\n      @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| values << v }})\n      expect(values).to eq([])\n\n      @settings[:setting] = \"something\"\n      expect(values).to eq(%w{something})\n    end\n\n    it \"should call passed blocks when values are set via the command line\" do\n      values = []\n      @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :hook => lambda { |v| values << v }})\n      expect(values).to eq([])\n\n      @settings.handlearg(\"--setting\", \"yay\")\n\n      expect(values).to eq(%w{yay})\n    end\n\n    it \"should provide an option to call passed blocks during definition\" do\n      values = []\n      @settings.define_settings(:section, :setting => {:default => \"yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |v| values << v }})\n      expect(values).to eq(%w{yay})\n    end\n\n    it \"should pass the fully interpolated value to the hook when called on definition\" do\n      values = []\n      @settings.define_settings(:section, :one => { :default => \"test\", :desc => \"a\" })\n      @settings.define_settings(:section, :setting => {:default => \"$one/yay\", :desc => \"boo\", :call_hook => :on_define_and_write, :hook => lambda { |v| values << v }})\n      expect(values).to eq(%w{test/yay})\n    end\n\n    it \"should munge values using the setting-specific methods\" do\n      @settings[:bool] = \"false\"\n      expect(@settings[:bool]).to eq(false)\n    end\n\n    it \"should prefer values set in ruby to values set on the cli\" do\n      @settings[:myval] = \"memarg\"\n      @settings.handlearg(\"--myval\", \"cliarg\")\n\n      expect(@settings[:myval]).to eq(\"memarg\")\n    end\n\n    it \"should raise an error if we try to set a setting that hasn't been defined'\" do\n      expect{\n        @settings[:why_so_serious] = \"foo\"\n      }.to raise_error(ArgumentError, /unknown setting/)\n    end\n\n    it \"allows overriding cli args based on the cli-set value\" do\n      @settings.handlearg(\"--myval\", \"cliarg\")\n      @settings.patch_value(:myval, \"modified #{@settings[:myval]}\", :cli)\n      expect(@settings[:myval]).to eq(\"modified cliarg\")\n    end\n  end\n\n  describe \"when returning values\" do\n    before do\n      @settings = Puppet::Settings.new\n      @settings.define_settings :section,\n          :config => { :type => :file, :default => \"/my/file\", :desc => \"eh\" },\n          :one    => { :default => \"ONE\", :desc => \"a\" },\n          :two    => { :default => \"$one TWO\", :desc => \"b\"},\n          :three  => { :default => \"$one $two THREE\", :desc => \"c\"},\n          :four   => { :default => \"$two $three FOUR\", :desc => \"d\"},\n          :five   => { :default => nil, :desc => \"e\" },\n          :code   => { :default => \"\", :desc => \"my code\"}\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    end\n\n    it \"should provide a mechanism for returning set values\" do\n      @settings[:one] = \"other\"\n      expect(@settings[:one]).to eq(\"other\")\n    end\n\n    it \"setting a value to nil causes it to return to its default\" do\n      @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:one => \"skipped value\"))\n      @settings[:one] = \"value will disappear\"\n\n      @settings[:one] = nil\n\n      expect(@settings[:one]).to eq(\"ONE\")\n    end\n\n    it \"should interpolate default values for other parameters into returned parameter values\" do\n      expect(@settings[:one]).to eq(\"ONE\")\n      expect(@settings[:two]).to eq(\"ONE TWO\")\n      expect(@settings[:three]).to eq(\"ONE ONE TWO THREE\")\n    end\n\n    it \"should interpolate default values that themselves need to be interpolated\" do\n      expect(@settings[:four]).to eq(\"ONE TWO ONE ONE TWO THREE FOUR\")\n    end\n\n    it \"should provide a method for returning uninterpolated values\" do\n      @settings[:two] = \"$one tw0\"\n      expect(@settings.value(:two, nil, true)).to  eq(\"$one tw0\")\n      expect(@settings.value(:four, nil, true)).to eq(\"$two $three FOUR\")\n    end\n\n    it \"should interpolate set values for other parameters into returned parameter values\" do\n      @settings[:one] = \"on3\"\n      @settings[:two] = \"$one tw0\"\n      @settings[:three] = \"$one $two thr33\"\n      @settings[:four] = \"$one $two $three f0ur\"\n      expect(@settings[:one]).to eq(\"on3\")\n      expect(@settings[:two]).to eq(\"on3 tw0\")\n      expect(@settings[:three]).to eq(\"on3 on3 tw0 thr33\")\n      expect(@settings[:four]).to eq(\"on3 on3 tw0 on3 on3 tw0 thr33 f0ur\")\n    end\n\n    it \"should not cache interpolated values such that stale information is returned\" do\n      expect(@settings[:two]).to eq(\"ONE TWO\")\n      @settings[:one] = \"one\"\n      expect(@settings[:two]).to eq(\"one TWO\")\n    end\n\n    it \"should not interpolate the value of the :code setting\" do\n      @code = @settings.setting(:code)\n      expect(@code).not_to receive(:munge)\n\n      expect(@settings[:code]).to eq(\"\")\n    end\n\n    it \"should have a run_mode that defaults to user\" do\n      expect(@settings.preferred_run_mode).to eq(:user)\n    end\n\n    it \"interpolates a boolean false without raising an error\" do\n      @settings.define_settings(:section,\n          :trip_wire => { :type => :boolean, :default => false, :desc => \"a trip wire\" },\n          :tripping => { :default => '$trip_wire', :desc => \"once tripped if interpolated was false\" })\n      expect(@settings[:tripping]).to eq(\"false\")\n    end\n  end\n\n  describe \"when choosing which value to return\" do\n    let(:config_file) { tmpfile('settings') }\n\n    before do\n      @settings = Puppet::Settings.new\n      @settings.define_settings :section,\n        :config => { :type => :file, :default => config_file, :desc => \"a\" },\n        :one => { :default => \"ONE\", :desc => \"a\" },\n        :two => { :default => \"TWO\", :desc => \"b\" }\n      @settings.preferred_run_mode = :agent\n    end\n\n    it \"should return default values if no values have been set\" do\n      expect(@settings[:one]).to eq(\"ONE\")\n    end\n\n    it \"should return values set on the cli before values set in the configuration file\" do\n      File.write(config_file, \"[main]\\none = fileval\\n\")\n      @settings.handlearg(\"--one\", \"clival\")\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq(\"clival\")\n    end\n\n    it \"should return values set in the mode-specific section before values set in the main section\" do\n      File.write(config_file, \"[main]\\none = mainval\\n[agent]\\none = modeval\\n\")\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq(\"modeval\")\n    end\n\n    [:master, :server].each do |run_mode|\n      describe \"when run mode is '#{run_mode}'\" do\n        before(:each) { @settings.preferred_run_mode = run_mode }\n\n        it \"returns values set in the 'master' section if the 'server' section does not exist\" do\n          File.write(config_file, \"[main]\\none = mainval\\n[master]\\none = modeval\\n\")\n          @settings.initialize_global_settings\n\n          expect(@settings[:one]).to eq(\"modeval\")\n        end\n\n        it \"prioritizes values set in the 'server' section if set\" do\n          File.write(config_file,  \"[main]\\none = mainval\\n[server]\\none = serverval\\n[master]\\none = masterval\\n\")\n          @settings.initialize_global_settings\n\n          expect(@settings[:one]).to eq(\"serverval\")\n        end\n      end\n    end\n\n    it \"should not return values outside of its search path\" do\n      File.write(config_file, \"[other]\\none = oval\\n\")\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq(\"ONE\")\n    end\n\n    it 'should use the current environment for $environment' do\n      @settings.define_settings :main, :config_version => { :default => \"$environment/foo\", :desc => \"mydocs\" }\n\n      expect(@settings.value(:config_version, \"myenv\")).to eq(\"myenv/foo\")\n    end\n  end\n\n  describe \"when locating config files\" do\n    before do\n      @settings = Puppet::Settings.new\n    end\n\n    describe \"when root\" do\n      it \"should look for the main config file default location config settings haven't been overridden'\" do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        expect(Puppet::FileSystem).to receive(:exist?).with(main_config_file_default_location).and_return(false)\n        expect(Puppet::FileSystem).not_to receive(:exist?).with(user_config_file_default_location)\n\n        @settings.initialize_global_settings\n      end\n    end\n\n    describe \"when not root\" do\n      it \"should look for user config file default location if config settings haven't been overridden'\" do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n\n        expect(Puppet::FileSystem).to receive(:exist?).with(user_config_file_default_location).and_return(false)\n\n        @settings.initialize_global_settings\n      end\n    end\n\n    describe \"when the file exists\" do\n      it \"fails if the file is not readable\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(user_config_file_default_location).and_return(true)\n        expect(@settings).to receive(:read_file).and_raise('Permission denied')\n\n        expect{ @settings.initialize_global_settings }.to raise_error(RuntimeError, /Could not load #{user_config_file_default_location}: Permission denied/)\n      end\n\n      it \"does not fail if the file is not readable and when `require_config` is false\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(user_config_file_default_location).and_return(true)\n        expect(@settings).to receive(:read_file).and_raise('Permission denied')\n\n        expect(@settings).not_to receive(:parse_config)\n        expect(Puppet).to receive(:log_exception)\n\n        expect{ @settings.initialize_global_settings([], false) }.not_to raise_error\n      end\n\n      it \"reads the file if it is readable\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(user_config_file_default_location).and_return(true)\n        expect(@settings).to receive(:read_file).and_return('server = host.string')\n        expect(@settings).to receive(:parse_config)\n\n        @settings.initialize_global_settings\n      end\n    end\n\n    describe \"when the file does not exist\" do\n      it \"does not attempt to parse the config file\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(user_config_file_default_location).and_return(false)\n        expect(@settings).not_to receive(:parse_config)\n\n        @settings.initialize_global_settings\n      end\n    end\n  end\n\n  describe \"when parsing its configuration\" do\n    before do\n      @settings = Puppet::Settings.new\n      allow(@settings).to receive(:service_user_available?).and_return(true)\n      allow(@settings).to receive(:service_group_available?).and_return(true)\n      @file = tmpfile(\"somefile\")\n      @settings.define_settings :section, :user => { :default => \"suser\", :desc => \"doc\" }, :group => { :default => \"sgroup\", :desc => \"doc\" }\n      @settings.define_settings :section,\n          :config => { :type => :file, :default => @file, :desc => \"eh\" },\n          :one => { :default => \"ONE\", :desc => \"a\" },\n          :two => { :default => \"$one TWO\", :desc => \"b\" },\n          :three => { :default => \"$one $two THREE\", :desc => \"c\" }\n\n      userconfig = tmpfile(\"userconfig\")\n      allow(@settings).to receive(:user_config_file).and_return(userconfig)\n    end\n\n    it \"should not ignore the report setting\" do\n      @settings.define_settings :section, :report => { :default => \"false\", :desc => \"a\" }\n      File.write(@file, <<~CONF)\n        [puppetd]\n        report=true\n      CONF\n\n      @settings.initialize_global_settings\n\n      expect(@settings[:report]).to be_truthy\n    end\n\n    it \"should use its current ':config' value for the file to parse\" do\n      myfile = tmpfile('myfile')\n      File.write(myfile, <<~CONF)\n        [main]\n        one=myfile\n      CONF\n\n      @settings[:config] = myfile\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq('myfile')\n    end\n\n    it \"should not try to parse non-existent files\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(@file).and_return(false)\n\n      expect(File).not_to receive(:read).with(@file)\n\n      @settings.initialize_global_settings\n    end\n\n    it \"should return values set in the configuration file\" do\n      File.write(@file, <<~CONF)\n        [main]\n        one = fileval\n      CONF\n\n      @settings.initialize_global_settings\n      expect(@settings[:one]).to eq(\"fileval\")\n    end\n\n    #484 - this should probably be in the regression area\n    it \"should not throw an exception on unknown parameters\" do\n      File.write(@file, <<~CONF)\n        [main]\n        nosuchparam = mval\n      CONF\n\n      expect { @settings.initialize_global_settings }.not_to raise_error\n    end\n\n    it \"should convert booleans in the configuration file into Ruby booleans\" do\n      File.write(@file, <<~CONF)\n        [main]\n        one = true\n        two = false\n      CONF\n\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq(true)\n      expect(@settings[:two]).to eq(false)\n    end\n\n    it \"doesn't convert integers in the configuration file\" do\n      File.write(@file, <<~CONF)\n        [main]\n        one = 65\n      CONF\n\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq('65')\n    end\n\n    it \"should support specifying all metadata (owner, group, mode) in the configuration file\" do\n      @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute(\"/myfile\"), :desc => \"a\" }\n\n      otherfile = make_absolute(\"/other/file\")\n      @settings.parse_config(<<-CONF)\n      [main]\n      myfile = #{otherfile} {owner = service, group = service, mode = 644}\n      CONF\n\n      expect(@settings[:myfile]).to eq(otherfile)\n      expect(metadata(@settings.setting(:myfile))).to eq({:owner => \"suser\", :group => \"sgroup\", :mode => \"644\"})\n    end\n\n    it \"should support specifying a single piece of metadata (owner, group, or mode) in the configuration file\" do\n      @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute(\"/myfile\"), :desc => \"a\" }\n\n      otherfile = make_absolute(\"/other/file\")\n      @settings.parse_config(<<-CONF)\n      [main]\n      myfile = #{otherfile} {owner = service}\n      CONF\n\n      expect(@settings[:myfile]).to eq(otherfile)\n      expect(metadata(@settings.setting(:myfile))).to eq({:owner => \"suser\"})\n    end\n\n    it \"should support loading metadata (owner, group, or mode) from a run_mode section in the configuration file\" do\n      @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS\n      @settings.define_settings :server, :myfile => { :type => :file, :default => make_absolute(\"/myfile\"), :desc => \"a\" }\n\n      otherfile = make_absolute(\"/other/file\")\n      File.write(@file, <<~CONF)\n        [server]\n        myfile = #{otherfile} {mode = 664}\n      CONF\n\n      # will start initialization as user\n      expect(@settings.preferred_run_mode).to eq(:user)\n      @settings.initialize_global_settings\n\n      # change app run_mode to server\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :server))\n      expect(@settings.preferred_run_mode).to eq(:server)\n\n      # initializing the app should have reloaded the metadata based on run_mode\n      expect(@settings[:myfile]).to eq(otherfile)\n      expect(metadata(@settings.setting(:myfile))).to eq({:mode => \"664\"})\n    end\n\n    context \"when setting serverport and masterport\" do\n      before(:each) do\n        @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS\n        @settings.define_settings :server, :masterport => { :desc => \"a\", :default => 1000, :type => :port }\n        @settings.define_settings :server, :serverport => { :type => :alias, :alias_for => :masterport }\n        @settings.define_settings :server, :ca_port => { :desc => \"a\", :default => \"$serverport\", :type => :port }\n        @settings.define_settings :server, :report_port => { :desc => \"a\", :default => \"$serverport\", :type => :port }\n\n        config_file = tmpfile('config')\n        @settings[:config] = config_file\n        File.write(config_file, text)\n\n        @settings.initialize_global_settings\n        @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :agent))\n        expect(@settings.preferred_run_mode).to eq(:agent)\n      end\n\n      context 'with serverport in main and masterport in agent' do\n        let(:text) do\n          \"[main]\n      serverport = 444\n      [agent]\n      masterport = 445\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(444) }\n        it { expect(@settings[:ca_port]).to eq(444) }\n        it { expect(@settings[:report_port]).to eq(444) }\n        it { expect(@settings[:masterport]).to eq(445) }\n      end\n\n      context 'with serverport and masterport in main' do\n        let(:text) do\n          \"[main]\n      serverport = 445\n      masterport = 444\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(444) }\n      end\n\n      context 'with serverport and masterport in agent' do\n        let(:text) do\n          \"[agent]\n      serverport = 445\n      masterport = 444\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(444) }\n      end\n\n      context 'with both serverport and masterport in main and agent' do\n        let(:text) do\n          \"[main]\n      serverport = 447\n      masterport = 442\n      [agent]\n      serverport = 445\n      masterport = 444\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(444) }\n      end\n\n      context 'with serverport in agent and masterport in main' do\n        let(:text) do\n          \"[agent]\n      serverport = 444\n      [main]\n      masterport = 445\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(444) }\n        it { expect(@settings[:ca_port]).to eq(444) }\n        it { expect(@settings[:report_port]).to eq(444) }\n        it { expect(@settings[:masterport]).to eq(445) }\n      end\n\n      context 'with masterport in main' do\n        let(:text) do\n          \"[main]\n      masterport = 445\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(445) }\n      end\n\n      context 'with masterport in agent' do\n        let(:text) do\n          \"[agent]\n      masterport = 445\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(445) }\n      end\n\n      context 'with serverport in agent' do\n        let(:text) do\n          \"[agent]\n      serverport = 445\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(1000) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n      end\n\n      context 'with serverport in main' do\n        let(:text) do\n          \"[main]\n      serverport = 445\n      \"\n        end\n\n        it { expect(@settings[:serverport]).to eq(445) }\n        it { expect(@settings[:masterport]).to eq(1000) }\n        it { expect(@settings[:ca_port]).to eq(445) }\n        it { expect(@settings[:report_port]).to eq(445) }\n      end\n    end\n\n    it \"does not use the metadata from the same setting in a different section\" do\n      file = make_absolute(\"/file\")\n      default_mode = \"0600\"\n      @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS\n      @settings.define_settings :server, :myfile => { :type => :file, :default => file, :desc => \"a\", :mode => default_mode }\n\n      File.write(@file, <<~CONF)\n        [server]\n        myfile = #{file}/foo\n        [agent]\n        myfile = #{file} {mode = 664}\n      CONF\n\n      # will start initialization as user\n      expect(@settings.preferred_run_mode).to eq(:user)\n      @settings.initialize_global_settings\n\n      # change app run_mode to server\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:run_mode => :server))\n      expect(@settings.preferred_run_mode).to eq(:server)\n\n      # initializing the app should have reloaded the metadata based on run_mode\n      expect(@settings[:myfile]).to eq(\"#{file}/foo\")\n      expect(metadata(@settings.setting(:myfile))).to eq({ :mode => default_mode })\n    end\n\n    it \"should call hooks associated with values set in the configuration file\" do\n      values = []\n      @settings.define_settings :section, :mysetting => {:default => \"defval\", :desc => \"a\", :hook => proc { |v| values << v }}\n\n      File.write(@file, <<~CONF)\n        [main]\n        mysetting = setval\n      CONF\n      @settings.initialize_global_settings\n\n      expect(values).to eq([\"setval\"])\n    end\n\n    it \"should not call the same hook for values set multiple times in the configuration file\" do\n      values = []\n      @settings.define_settings :section, :mysetting => {:default => \"defval\", :desc => \"a\", :hook => proc { |v| values << v }}\n\n      File.write(@file, <<~CONF)\n        [user]\n        mysetting = setval\n        [main]\n        mysetting = other\n      CONF\n      @settings.initialize_global_settings\n\n      expect(values).to eq([\"setval\"])\n    end\n\n    it \"should pass the interpolated value to the hook when one is available\" do\n      values = []\n      @settings.define_settings :section, :base => {:default => \"yay\", :desc => \"a\", :hook => proc { |v| values << v }}\n      @settings.define_settings :section, :mysetting => {:default => \"defval\", :desc => \"a\", :hook => proc { |v| values << v }}\n\n      File.write(@file, <<~CONF)\n        [main]\n        mysetting = $base/setval\n      CONF\n      @settings.initialize_global_settings\n\n      expect(values).to eq([\"yay/setval\"])\n    end\n\n    it \"should allow hooks invoked at parse time to be deferred\" do\n      hook_invoked = false\n      @settings.define_settings :section, :deferred  => {:desc => '',\n                                                         :hook => proc { |v| hook_invoked = true },\n                                                         :call_hook => :on_initialize_and_write, }\n\n      # This test relies on `confdir` defaulting to nil which causes the default\n      # value of `deferred=$confdir/goose` to raise an interpolation error during\n      # global initialization, and the hook to be skipped\n      @settings.define_settings(:main,\n                                PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS.merge(\n                                  :confdir => { :type => :directory, :default => nil, :desc => \"confdir\" }))\n\n      File.write(@file, <<~EOD)\n        [main]\n        deferred=$confdir/goose\n      EOD\n\n      @settings.initialize_global_settings\n\n      expect(hook_invoked).to be_falsey\n\n      # And now that we initialize app defaults with `confdir`, then `deferred`\n      # can be interpolated and its hook called\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:confdir => '/path/to/confdir'))\n\n      expect(hook_invoked).to be_truthy\n      expect(@settings[:deferred]).to eq(File.expand_path('/path/to/confdir/goose'))\n    end\n\n    it \"does not require the value for a setting without a hook to resolve during global setup\" do\n      @settings.define_settings :section, :can_cause_problems  => {:desc => '' }\n\n      @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n\n      File.write(@file, <<~EOD)\n      [main]\n      can_cause_problems=$confdir/goose\n      EOD\n\n      @settings.initialize_global_settings\n      @settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES.merge(:confdir => '/path/to/confdir'))\n\n      expect(@settings[:can_cause_problems]).to eq('/path/to/confdir/goose')\n    end\n\n    it \"should allow empty values\" do\n      @settings.define_settings :section, :myarg => { :default => \"myfile\", :desc => \"a\" }\n\n      File.write(@file, <<~CONF)\n        [main]\n        myarg =\n      CONF\n      @settings.initialize_global_settings\n\n      expect(@settings[:myarg]).to eq(\"\")\n    end\n\n    describe \"deprecations\" do\n      let(:settings) { Puppet::Settings.new }\n\n      def assert_accessing_setting_is_deprecated(settings, setting)\n        expect(Puppet).to receive(:deprecation_warning).with(\"Accessing '#{setting}' as a setting is deprecated.\")\n        expect(Puppet).to receive(:deprecation_warning).with(\"Modifying '#{setting}' as a setting is deprecated.\")\n        settings[setting.intern] = apath = File.expand_path('foo')\n        expect(settings[setting.intern]).to eq(apath)\n      end\n\n      before(:each) do\n        settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)\n      end\n\n      context \"complete\" do\n        let(:completely_deprecated_settings) do\n          settings.define_settings(:main, {\n            :completely_deprecated_setting => {\n              :default => 'foo',\n              :desc    => 'a deprecated setting',\n              :deprecated => :completely,\n            }\n          })\n          settings\n        end\n\n        it \"warns when set in puppet.conf\" do\n          expect(Puppet).to receive(:deprecation_warning).with(/completely_deprecated_setting is deprecated\\./, 'setting-completely_deprecated_setting')\n\n          completely_deprecated_settings.parse_config(<<-CONF)\n            completely_deprecated_setting='should warn'\n          CONF\n          completely_deprecated_settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n        end\n\n        it \"warns when set on the commandline\" do\n          expect(Puppet).to receive(:deprecation_warning).with(/completely_deprecated_setting is deprecated\\./, 'setting-completely_deprecated_setting')\n\n          args = [\"--completely_deprecated_setting\", \"/some/value\"]\n          completely_deprecated_settings.send(:parse_global_options, args)\n          completely_deprecated_settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n        end\n\n        it \"warns when set in code\" do\n          assert_accessing_setting_is_deprecated(completely_deprecated_settings, 'completely_deprecated_setting')\n        end\n      end\n\n      context \"partial\" do\n        let(:partially_deprecated_settings) do\n          settings.define_settings(:main, {\n            :partially_deprecated_setting => {\n              :default => 'foo',\n              :desc    => 'a partially deprecated setting',\n              :deprecated => :allowed_on_commandline,\n            }\n          })\n          settings\n        end\n\n        it \"warns for a deprecated setting allowed on the command line set in puppet.conf\" do\n          expect(Puppet).to receive(:deprecation_warning).with(/partially_deprecated_setting is deprecated in puppet\\.conf/, 'puppet-conf-setting-partially_deprecated_setting')\n          partially_deprecated_settings.parse_config(<<-CONF)\n            partially_deprecated_setting='should warn'\n          CONF\n          partially_deprecated_settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n        end\n\n        it \"does not warn when manifest is set on command line\" do\n          expect(Puppet).not_to receive(:deprecation_warning)\n\n          args = [\"--partially_deprecated_setting\", \"/some/value\"]\n          partially_deprecated_settings.send(:parse_global_options, args)\n          partially_deprecated_settings.initialize_app_defaults(PuppetSpec::Settings::TEST_APP_DEFAULT_VALUES)\n        end\n\n        it \"warns when set in code\" do\n          assert_accessing_setting_is_deprecated(partially_deprecated_settings, 'partially_deprecated_setting')\n        end\n      end\n    end\n  end\n\n  describe \"when there are multiple config files\" do\n    let(:main_config_text) { \"[main]\\none = main\\ntwo = main2\" }\n    let(:user_config_text) { \"[main]\\none = user\\n\" }\n\n    before :each do\n      @settings = Puppet::Settings.new\n      @settings.define_settings(:section,\n          { :confdir => { :default => nil,                    :desc => \"Conf dir\" },\n            :config  => { :default => \"$confdir/puppet.conf\", :desc => \"Config\" },\n            :one     => { :default => \"ONE\",                  :desc => \"a\" },\n            :two     => { :default => \"TWO\",                  :desc => \"b\" }, })\n    end\n\n    context \"running non-root without explicit config file\" do\n      before :each do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n        expect(Puppet::FileSystem).to receive(:exist?).\n          with(user_config_file_default_location).\n          and_return(true).ordered\n        expect(@settings).to receive(:read_file).\n          with(user_config_file_default_location).\n          and_return(user_config_text).ordered\n      end\n\n      it \"should return values from the user config file\" do\n        @settings.send(:parse_config_files)\n        expect(@settings[:one]).to eq(\"user\")\n      end\n\n      it \"should not return values from the main config file\" do\n        @settings.send(:parse_config_files)\n        expect(@settings[:two]).to eq(\"TWO\")\n      end\n    end\n\n    context \"running as root without explicit config file\" do\n      before :each do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        expect(Puppet::FileSystem).to receive(:exist?).\n          with(main_config_file_default_location).\n          and_return(true).ordered\n        expect(@settings).to receive(:read_file).\n          with(main_config_file_default_location).\n          and_return(main_config_text).ordered\n      end\n\n      it \"should return values from the main config file\" do\n        @settings.send(:parse_config_files)\n        expect(@settings[:one]).to eq(\"main\")\n      end\n\n      it \"should not return values from the user config file\" do\n        @settings.send(:parse_config_files)\n        expect(@settings[:two]).to eq(\"main2\")\n      end\n    end\n\n    context \"running with an explicit config file as a user (e.g. Apache + Passenger)\" do\n      before :each do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n        @settings[:confdir] = File.dirname(main_config_file_default_location)\n        expect(Puppet::FileSystem).to receive(:exist?).\n          with(main_config_file_default_location).\n          and_return(true).ordered\n        expect(@settings).to receive(:read_file).\n          with(main_config_file_default_location).\n          and_return(main_config_text).ordered\n      end\n\n      it \"should return values from the main config file\" do\n        @settings.send(:parse_config_files)\n        expect(@settings[:one]).to eq(\"main\")\n      end\n\n      it \"should not return values from the user config file\" do\n        @settings.send(:parse_config_files)\n        expect(@settings[:two]).to eq(\"main2\")\n      end\n    end\n  end\n\n  describe \"when reparsing its configuration\" do\n    before do\n      @file = tmpfile(\"testfile\")\n      Puppet::FileSystem.touch(@file)\n\n      @settings = Puppet::Settings.new\n      @settings.define_settings :section,\n          :config => { :type => :file, :default => @file, :desc => \"a\" },\n          :one => { :default => \"ONE\", :desc => \"a\" },\n          :two => { :default => \"$one TWO\", :desc => \"b\" },\n          :three => { :default => \"$one $two THREE\", :desc => \"c\" }\n\n      userconfig = tmpfile(\"userconfig\")\n      allow(@settings).to receive(:user_config_file).and_return(userconfig)\n    end\n\n    it \"does not create the WatchedFile instance and should not parse if the file does not exist\" do\n      Puppet::FileSystem.unlink(@file)\n\n      expect(Puppet::Util::WatchedFile).not_to receive(:new)\n      expect(@settings).not_to receive(:parse_config_files)\n\n      @settings.reparse_config_files\n    end\n\n    context \"and watched file exists\" do\n      before do\n        @watched_file = Puppet::Util::WatchedFile.new(@file)\n        expect(Puppet::Util::WatchedFile).to receive(:new).with(@file).and_return(@watched_file)\n      end\n\n      it \"uses a WatchedFile instance to determine if the file has changed\" do\n        expect(@watched_file).to receive(:changed?)\n\n        @settings.reparse_config_files\n      end\n\n      it \"does not reparse if the file has not changed\" do\n        expect(@watched_file).to receive(:changed?).and_return(false)\n\n        expect(@settings).not_to receive(:parse_config_files)\n\n        @settings.reparse_config_files\n      end\n\n      it \"reparses if the file has changed\" do\n        expect(@watched_file).to receive(:changed?).and_return(true)\n\n        expect(@settings).to receive(:parse_config_files)\n\n        @settings.reparse_config_files\n      end\n\n      it \"replaces in-memory values with on-file values\" do\n        allow(@watched_file).to receive(:changed?).and_return(true)\n        @settings[:one] = \"init\"\n\n        # Now replace the value\n        File.write(@file, \"[main]\\none = disk-replace\\n\")\n\n        @settings.reparse_config_files\n        expect(@settings[:one]).to eq(\"disk-replace\")\n      end\n    end\n\n    it \"should retain parameters set by cli when configuration files are reparsed\" do\n      @settings.handlearg(\"--one\", \"clival\")\n\n      File.write(@file, \"[main]\\none = on-disk\\n\")\n      @settings.initialize_global_settings\n\n      expect(@settings[:one]).to eq(\"clival\")\n    end\n\n    it \"should remove in-memory values that are no longer set in the file\" do\n      # Init the value\n      File.write(@file, \"[main]\\none = disk-init\\n\")\n      @settings.send(:parse_config_files)\n      expect(@settings[:one]).to eq(\"disk-init\")\n\n      # Now replace the value\n      File.write(@file, \"[main]\\ntwo = disk-replace\\n\")\n      @settings.send(:parse_config_files)\n\n      # The originally-overridden value should be replaced with the default\n      expect(@settings[:one]).to eq(\"ONE\")\n\n      # and we should now have the new value in memory\n      expect(@settings[:two]).to eq(\"disk-replace\")\n    end\n\n    it \"should retain in-memory values if the file has a syntax error\" do\n      # Init the value\n      File.write(@file, \"[main]\\none = initial-value\\n\")\n      @settings.initialize_global_settings\n      expect(@settings[:one]).to eq(\"initial-value\")\n\n      # Now replace the value with something bogus\n      File.write(@file, \"[main]\\nkenny = killed-by-what-follows\\n1 is 2, blah blah florp\\n\")\n      @settings.send(:parse_config_files)\n\n      # The originally-overridden value should not be replaced with the default\n      expect(@settings[:one]).to eq(\"initial-value\")\n\n      # and we should not have the new value in memory\n      expect(@settings[:kenny]).to be_nil\n    end\n  end\n\n  it \"should provide a method for creating a catalog of resources from its configuration\" do\n    expect(Puppet::Settings.new.to_catalog).to be_an_instance_of(Puppet::Resource::Catalog)\n  end\n\n  describe \"when creating a catalog\" do\n    let(:maindir) { make_absolute('/maindir') }\n    let(:seconddir) { make_absolute('/seconddir') }\n    let(:otherdir) { make_absolute('/otherdir') }\n\n    before do\n      @settings = Puppet::Settings.new\n      allow(@settings).to receive(:service_user_available?).and_return(true)\n    end\n\n    it \"should add all file resources to the catalog if no sections have been specified\" do\n      @settings.define_settings :main,\n          :maindir => { :type => :directory, :default => maindir, :desc => \"a\"},\n          :seconddir => { :type => :directory, :default => seconddir, :desc => \"a\"}\n      @settings.define_settings :other,\n          :otherdir => { :type => :directory, :default => otherdir, :desc => \"a\" }\n\n      catalog = @settings.to_catalog\n\n      [maindir, seconddir, otherdir].each do |path|\n        expect(catalog.resource(:file, path)).to be_instance_of(Puppet::Resource)\n      end\n    end\n\n    it \"should add only files in the specified sections if section names are provided\" do\n      @settings.define_settings :main, :maindir => { :type => :directory, :default => maindir, :desc => \"a\" }\n      @settings.define_settings :other, :otherdir => { :type => :directory, :default => otherdir, :desc => \"a\" }\n      catalog = @settings.to_catalog(:main)\n      expect(catalog.resource(:file, otherdir)).to be_nil\n      expect(catalog.resource(:file, maindir)).to be_instance_of(Puppet::Resource)\n    end\n\n    it \"should not try to add the same file twice\" do\n      @settings.define_settings :main, :maindir => { :type => :directory, :default => maindir, :desc => \"a\" }\n      @settings.define_settings :other, :otherdir => { :type => :directory, :default => maindir, :desc => \"a\" }\n      expect { @settings.to_catalog }.not_to raise_error\n    end\n\n    it \"should ignore files whose :to_resource method returns nil\" do\n      @settings.define_settings :main, :maindir => { :type => :directory, :default => maindir, :desc => \"a\" }\n      expect(@settings.setting(:maindir)).to receive(:to_resource).and_return(nil)\n\n      expect_any_instance_of(Puppet::Resource::Catalog).not_to receive(:add_resource)\n      @settings.to_catalog\n    end\n\n    describe \"on Microsoft Windows\", :if => Puppet::Util::Platform.windows? do\n      before :each do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n\n        @settings.define_settings :foo,\n            :mkusers => { :type => :boolean, :default => true, :desc => \"e\" },\n            :user => { :default => \"suser\", :desc => \"doc\" },\n            :group => { :default => \"sgroup\", :desc => \"doc\" }\n        @settings.define_settings :other,\n            :otherdir => { :type => :directory, :default => \"/otherdir\", :desc => \"a\", :owner => \"service\", :group => \"service\"}\n\n        @catalog = @settings.to_catalog\n      end\n\n      it \"it should not add users and groups to the catalog\" do\n        expect(@catalog.resource(:user, \"suser\")).to be_nil\n        expect(@catalog.resource(:group, \"sgroup\")).to be_nil\n      end\n    end\n\n    describe \"adding default directory environment to the catalog\" do\n      let(:tmpenv) { tmpdir(\"envs\") }\n      let(:default_path) { \"#{tmpenv}/environments\" }\n\n      before(:each) do\n        @settings.define_settings :main,\n          :environment     => { :default => \"production\", :desc => \"env\"},\n          :environmentpath => { :type => :path, :default => default_path, :desc => \"envpath\"}\n      end\n\n      it \"adds if environmentpath exists\" do\n        envpath = \"#{tmpenv}/custom_envpath\"\n        @settings[:environmentpath] = envpath\n        Dir.mkdir(envpath)\n        catalog = @settings.to_catalog\n        expect(catalog.resource_keys).to include([\"File\", \"#{envpath}/production\"])\n      end\n\n      it \"adds the first directory of environmentpath\" do\n        envdir = \"#{tmpenv}/custom_envpath\"\n        envpath = \"#{envdir}#{File::PATH_SEPARATOR}/some/other/envdir\"\n        @settings[:environmentpath] = envpath\n        Dir.mkdir(envdir)\n        catalog = @settings.to_catalog\n        expect(catalog.resource_keys).to include([\"File\", \"#{envdir}/production\"])\n      end\n\n      it 'adds the creation of the production directory when not run as root' do\n        envdir = \"#{tmpenv}/custom_envpath\"\n        envpath = \"#{envdir}#{File::PATH_SEPARATOR}/some/other/envdir\"\n        @settings[:environmentpath] = envpath\n        Dir.mkdir(envdir)\n        allow(Puppet.features).to receive(:root?).and_return(false)\n        catalog = @settings.to_catalog\n        resource = catalog.resource('File', File.join(envdir, 'production'))\n        expect(resource[:mode]).to eq('0750')\n        expect(resource[:owner]).to be_nil\n        expect(resource[:group]).to be_nil\n      end\n\n      it 'adds the creation of the production directory with service owner and group information when available' do\n        envdir = \"#{tmpenv}/custom_envpath\"\n        envpath = \"#{envdir}#{File::PATH_SEPARATOR}/some/other/envdir\"\n        @settings[:environmentpath] = envpath\n        Dir.mkdir(envdir)\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        allow(@settings).to receive(:service_user_available?).and_return(true)\n        allow(@settings).to receive(:service_group_available?).and_return(true)\n        catalog = @settings.to_catalog\n        resource = catalog.resource('File', File.join(envdir, 'production'))\n        expect(resource[:mode]).to eq('0750')\n        expect(resource[:owner]).to eq('puppet')\n        expect(resource[:group]).to eq('puppet')\n      end\n\n      it 'adds the creation of the production directory without service owner and group when not available' do\n        envdir = \"#{tmpenv}/custom_envpath\"\n        envpath = \"#{envdir}#{File::PATH_SEPARATOR}/some/other/envdir\"\n        @settings[:environmentpath] = envpath\n        Dir.mkdir(envdir)\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        allow(@settings).to receive(:service_user_available?).and_return(false)\n        allow(@settings).to receive(:service_group_available?).and_return(false)\n        catalog = @settings.to_catalog\n        resource = catalog.resource('File', File.join(envdir, 'production'))\n        expect(resource[:mode]).to eq('0750')\n        expect(resource[:owner]).to be_nil\n        expect(resource[:group]).to be_nil\n      end\n\n      it \"handles a non-existent environmentpath\" do\n        catalog = @settings.to_catalog\n        expect(catalog.resource_keys).to be_empty\n      end\n\n      it \"handles a default environmentpath\" do\n        Dir.mkdir(default_path)\n        catalog = @settings.to_catalog\n        expect(catalog.resource_keys).to include([\"File\", \"#{default_path}/production\"])\n      end\n\n      it \"does not add if the path to the default directory environment exists as a symlink\", :if => Puppet.features.manages_symlinks? do\n        Dir.mkdir(default_path)\n        Puppet::FileSystem.symlink(\"#{tmpenv}/nowhere\", File.join(default_path, 'production'))\n        catalog = @settings.to_catalog\n        expect(catalog.resource_keys).to_not include([\"File\", \"#{default_path}/production\"])\n      end\n    end\n\n    describe \"when adding users and groups to the catalog\" do\n      before :all do\n        # when this spec is run in isolation to build a settings catalog\n        # it will not be able to autorequire and load types for the first time\n        # on Windows with windows? stubbed to false, because\n        # Puppet::Util.path_to_uri is called to generate a URI to load code\n        # and it manipulates the path based on OS\n        # so instead we forcefully \"prime\" the cached types\n        Puppet::Type.type(:user).new(:name => 'foo')\n        Puppet::Type.type(:group).new(:name => 'bar')\n        Puppet::Type.type(:file).new(:name => Dir.pwd) # appropriate for OS\n      end\n\n      before do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        # stubbed to false, as Windows catalogs don't add users / groups\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n\n        @settings.define_settings :foo,\n            :mkusers => { :type => :boolean, :default => true, :desc => \"e\" },\n            :user => { :default => \"suser\", :desc => \"doc\" },\n            :group => { :default => \"sgroup\", :desc => \"doc\" }\n        @settings.define_settings :other, :otherdir => {:type => :directory, :default => \"/otherdir\", :desc => \"a\", :owner => \"service\", :group => \"service\"}\n\n        @catalog = @settings.to_catalog\n      end\n\n      it \"should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root\" do\n        expect(@catalog.resource(:user, \"suser\")).to be_instance_of(Puppet::Resource)\n        expect(@catalog.resource(:group, \"sgroup\")).to be_instance_of(Puppet::Resource)\n      end\n\n      it \"should only add users and groups to the catalog from specified sections\" do\n        @settings.define_settings :yay, :yaydir => { :type => :directory, :default => \"/yaydir\", :desc => \"a\", :owner => \"service\", :group => \"service\"}\n        catalog = @settings.to_catalog(:other)\n        expect(catalog.resource(:user, \"jane\")).to be_nil\n        expect(catalog.resource(:group, \"billy\")).to be_nil\n      end\n\n      it \"should not add users or groups to the catalog if :mkusers not running as root\" do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n\n        catalog = @settings.to_catalog\n        expect(catalog.resource(:user, \"suser\")).to be_nil\n        expect(catalog.resource(:group, \"sgroup\")).to be_nil\n      end\n\n      it \"should not add users or groups to the catalog if :mkusers is not a valid setting\" do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        settings = Puppet::Settings.new\n        settings.define_settings :other, :otherdir => {:type => :directory, :default => \"/otherdir\", :desc => \"a\", :owner => \"service\", :group => \"service\"}\n\n        catalog = settings.to_catalog\n        expect(catalog.resource(:user, \"suser\")).to be_nil\n        expect(catalog.resource(:group, \"sgroup\")).to be_nil\n      end\n\n      it \"should not add users or groups to the catalog if :mkusers is a valid setting but is disabled\" do\n        @settings[:mkusers] = false\n        allow(@settings).to receive(:service_user_available?).and_return(false)\n        allow(@settings).to receive(:service_group_available?).and_return(false)\n\n        catalog = @settings.to_catalog\n        expect(catalog.resource(:user, \"suser\")).to be_nil\n        expect(catalog.resource(:group, \"sgroup\")).to be_nil\n      end\n\n      it \"should not try to add users or groups to the catalog twice\" do\n        @settings.define_settings :yay, :yaydir => {:type => :directory, :default => \"/yaydir\", :desc => \"a\", :owner => \"service\", :group => \"service\"}\n\n        # This would fail if users/groups were added twice\n        expect { @settings.to_catalog }.not_to raise_error\n      end\n\n      it \"should set :ensure to :present on each created user and group\" do\n        expect(@catalog.resource(:user, \"suser\")[:ensure]).to eq(:present)\n        expect(@catalog.resource(:group, \"sgroup\")[:ensure]).to eq(:present)\n      end\n\n      it \"should set each created user's :gid to the service group\" do\n        expect(@settings.to_catalog.resource(:user, \"suser\")[:gid]).to eq(\"sgroup\")\n      end\n\n      it \"should not attempt to manage the root user\" do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n        @settings.define_settings :foo, :foodir => {:type => :directory, :default => \"/foodir\", :desc => \"a\", :owner => \"root\", :group => \"service\"}\n\n        expect(@settings.to_catalog.resource(:user, \"root\")).to be_nil\n      end\n    end\n  end\n\n  it \"should be able to be converted to a manifest\" do\n    expect(Puppet::Settings.new).to respond_to(:to_manifest)\n  end\n\n  describe \"when being converted to a manifest\" do\n    it \"should produce a string with the code for each resource joined by two carriage returns\" do\n      @settings = Puppet::Settings.new\n      @settings.define_settings :main,\n          :maindir => { :type => :directory, :default => \"/maindir\", :desc => \"a\"},\n          :seconddir => { :type => :directory, :default => \"/seconddir\", :desc => \"a\"}\n\n      main = double('main_resource', :ref => \"File[/maindir]\")\n      expect(main).to receive(:to_manifest).and_return(\"maindir\")\n      expect(main).to receive(:'[]').with(:alias).and_return(nil)\n      second = double('second_resource', :ref => \"File[/seconddir]\")\n      expect(second).to receive(:to_manifest).and_return(\"seconddir\")\n      expect(second).to receive(:'[]').with(:alias).and_return(nil)\n\n      expect(@settings.setting(:maindir)).to receive(:to_resource).and_return(main)\n      expect(@settings.setting(:seconddir)).to receive(:to_resource).and_return(second)\n\n      expect(@settings.to_manifest.split(\"\\n\\n\").sort).to eq(%w{maindir seconddir})\n    end\n  end\n\n  describe \"when using sections of the configuration to manage the local host\" do\n    before do\n      @settings = Puppet::Settings.new\n      allow(@settings).to receive(:service_user_available?).and_return(true)\n      allow(@settings).to receive(:service_group_available?).and_return(true)\n      @settings.define_settings :main, :noop => { :default => false, :desc => \"\", :type => :boolean }\n      @settings.define_settings :main,\n          :maindir => { :type => :directory, :default => make_absolute(\"/maindir\"), :desc => \"a\" },\n          :seconddir => { :type => :directory, :default => make_absolute(\"/seconddir\"), :desc => \"a\"}\n      @settings.define_settings :main, :user => { :default => \"suser\", :desc => \"doc\" }, :group => { :default => \"sgroup\", :desc => \"doc\" }\n      @settings.define_settings :other, :otherdir => {:type => :directory, :default => make_absolute(\"/otherdir\"), :desc => \"a\", :owner => \"service\", :group => \"service\", :mode => '0755'}\n      @settings.define_settings :third, :thirddir => { :type => :directory, :default => make_absolute(\"/thirddir\"), :desc => \"b\"}\n      @settings.define_settings :files, :myfile => {:type => :file, :default => make_absolute(\"/myfile\"), :desc => \"a\", :mode => '0755'}\n    end\n\n    it \"should create a catalog with the specified sections\" do\n      expect(@settings).to receive(:to_catalog).with(:main, :other).and_return(Puppet::Resource::Catalog.new(\"foo\"))\n      @settings.use(:main, :other)\n    end\n\n    it \"should canonicalize the sections\" do\n      expect(@settings).to receive(:to_catalog).with(:main, :other).and_return(Puppet::Resource::Catalog.new(\"foo\"))\n      @settings.use(\"main\", \"other\")\n    end\n\n    it \"should ignore sections that have already been used\" do\n      expect(@settings).to receive(:to_catalog).with(:main).and_return(Puppet::Resource::Catalog.new(\"foo\"))\n      @settings.use(:main)\n      expect(@settings).to receive(:to_catalog).with(:other).and_return(Puppet::Resource::Catalog.new(\"foo\"))\n      @settings.use(:main, :other)\n    end\n\n    it \"should convert the created catalog to a RAL catalog\" do\n      @catalog = Puppet::Resource::Catalog.new(\"foo\")\n      expect(@settings).to receive(:to_catalog).with(:main).and_return(@catalog)\n\n      expect(@catalog).to receive(:to_ral).and_return(@catalog)\n      @settings.use(:main)\n    end\n\n    it \"should specify that it is not managing a host catalog\" do\n      catalog = Puppet::Resource::Catalog.new(\"foo\")\n      expect(catalog).to receive(:apply)\n      expect(@settings).to receive(:to_catalog).and_return(catalog)\n\n      allow(catalog).to receive(:to_ral).and_return(catalog)\n\n      expect(catalog).to receive(:host_config=).with(false)\n\n      @settings.use(:main)\n    end\n\n    it \"should support a method for re-using all currently used sections\" do\n      expect(@settings).to receive(:to_catalog).with(:main, :third).exactly(2).times.and_return(Puppet::Resource::Catalog.new(\"foo\"))\n\n      @settings.use(:main, :third)\n      @settings.reuse\n    end\n\n    it \"should fail with an appropriate message if any resources fail\" do\n      @catalog = Puppet::Resource::Catalog.new(\"foo\")\n      allow(@catalog).to receive(:to_ral).and_return(@catalog)\n      expect(@settings).to receive(:to_catalog).and_return(@catalog)\n\n      @trans = double(\"transaction\")\n      expect(@catalog).to receive(:apply).and_yield(@trans)\n\n      expect(@trans).to receive(:any_failed?).and_return(true)\n\n      resource = Puppet::Type.type(:notify).new(:title => 'failed')\n      status = Puppet::Resource::Status.new(resource)\n      event = Puppet::Transaction::Event.new(\n        :name => 'failure',\n        :status => 'failure',\n        :message => 'My failure')\n      status.add_event(event)\n\n      report = Puppet::Transaction::Report.new('apply')\n      report.add_resource_status(status)\n\n      expect(@trans).to receive(:report).and_return(report)\n\n      expect(@settings).to receive(:raise).with(/My failure/)\n      @settings.use(:whatever)\n    end\n  end\n\n  describe 'when settings_catalog is disabled' do\n    let(:settings) { Puppet::Settings.new }\n    before do\n      allow(Puppet).to receive(:[]).with(:settings_catalog).and_return(false)\n    end\n\n    it 'does not compile and apply settings catalog' do\n      expect(settings).not_to receive(:to_catalog)\n      settings.use(:main)\n    end\n\n    it 'logs a message that settings catalog is skipped' do\n      expect(Puppet).to receive(:debug).with('Skipping settings catalog for sections main')\n      settings.use(:main)\n    end\n  end\n\n  describe \"when dealing with printing configs\" do\n    before do\n      @settings = Puppet::Settings.new\n      #these are the magic default values\n      allow(@settings).to receive(:value).with(:configprint).and_return(\"\")\n      allow(@settings).to receive(:value).with(:genconfig).and_return(false)\n      allow(@settings).to receive(:value).with(:genmanifest).and_return(false)\n      allow(@settings).to receive(:value).with(:environment).and_return(nil)\n    end\n\n    describe \"when checking print_config?\" do\n      it \"should return false when the :configprint, :genconfig and :genmanifest are not set\" do\n        expect(@settings.print_configs?).to be_falsey\n      end\n\n      it \"should return true when :configprint has a value\" do\n        allow(@settings).to receive(:value).with(:configprint).and_return(\"something\")\n        expect(@settings.print_configs?).to be_truthy\n      end\n\n      it \"should return true when :genconfig has a value\" do\n        allow(@settings).to receive(:value).with(:genconfig).and_return(true)\n        expect(@settings.print_configs?).to be_truthy\n      end\n\n      it \"should return true when :genmanifest has a value\" do\n        allow(@settings).to receive(:value).with(:genmanifest).and_return(true)\n        expect(@settings.print_configs?).to be_truthy\n      end\n    end\n\n    describe \"when printing configs\" do\n      describe \"when :configprint has a value\" do\n        it \"should call print_config_options\" do\n          allow(@settings).to receive(:value).with(:configprint).and_return(\"something\")\n          expect(@settings).to receive(:print_config_options)\n          @settings.print_configs\n        end\n\n        it \"should get the value of the option using the environment\" do\n          allow(@settings).to receive(:value).with(:configprint).and_return(\"something\")\n          allow(@settings).to receive(:include?).with(\"something\").and_return(true)\n          expect(@settings).to receive(:value).with(:environment).and_return(\"env\")\n          expect(@settings).to receive(:value).with(\"something\", \"env\").and_return(\"foo\")\n          allow(@settings).to receive(:puts).with(\"foo\")\n          @settings.print_configs\n        end\n\n        it \"should print the value of the option\" do\n          allow(@settings).to receive(:value).with(:configprint).and_return(\"something\")\n          allow(@settings).to receive(:include?).with(\"something\").and_return(true)\n          allow(@settings).to receive(:value).with(\"something\", nil).and_return(\"foo\")\n          expect(@settings).to receive(:puts).with(\"foo\")\n          @settings.print_configs\n        end\n\n        it \"should print the value pairs if there are multiple options\" do\n          allow(@settings).to receive(:value).with(:configprint).and_return(\"bar,baz\")\n          allow(@settings).to receive(:include?).with(\"bar\").and_return(true)\n          allow(@settings).to receive(:include?).with(\"baz\").and_return(true)\n          allow(@settings).to receive(:value).with(\"bar\", nil).and_return(\"foo\")\n          allow(@settings).to receive(:value).with(\"baz\", nil).and_return(\"fud\")\n          expect(@settings).to receive(:puts).with(\"bar = foo\")\n          expect(@settings).to receive(:puts).with(\"baz = fud\")\n          @settings.print_configs\n        end\n\n        it \"should return true after printing\" do\n          allow(@settings).to receive(:value).with(:configprint).and_return(\"something\")\n          allow(@settings).to receive(:include?).with(\"something\").and_return(true)\n          allow(@settings).to receive(:value).with(\"something\", nil).and_return(\"foo\")\n          allow(@settings).to receive(:puts).with(\"foo\")\n          expect(@settings.print_configs).to be_truthy\n        end\n\n        it \"should return false if a config param is not found\" do\n          allow(@settings).to receive(:puts)\n          allow(@settings).to receive(:value).with(:configprint).and_return(\"something\")\n          allow(@settings).to receive(:include?).with(\"something\").and_return(false)\n          expect(@settings.print_configs).to be_falsey\n        end\n      end\n\n      describe \"when genconfig is true\" do\n        before do\n          allow(@settings).to receive(:puts)\n        end\n\n        it \"should call to_config\" do\n          allow(@settings).to receive(:value).with(:genconfig).and_return(true)\n          expect(@settings).to receive(:to_config)\n          @settings.print_configs\n        end\n\n        it \"should return true from print_configs\" do\n          allow(@settings).to receive(:value).with(:genconfig).and_return(true)\n          allow(@settings).to receive(:to_config)\n          expect(@settings.print_configs).to be_truthy\n        end\n      end\n\n      describe \"when genmanifest is true\" do\n        before do\n          allow(@settings).to receive(:puts)\n        end\n\n        it \"should call to_config\" do\n          allow(@settings).to receive(:value).with(:genmanifest).and_return(true)\n          expect(@settings).to receive(:to_manifest)\n          @settings.print_configs\n        end\n\n        it \"should return true from print_configs\" do\n          allow(@settings).to receive(:value).with(:genmanifest).and_return(true)\n          allow(@settings).to receive(:to_manifest)\n          expect(@settings.print_configs).to be_truthy\n        end\n      end\n    end\n  end\n\n  describe \"when determining if the service user is available\" do\n    let(:settings) do\n      settings = Puppet::Settings.new\n      settings.define_settings :main, :user => { :default => nil, :desc => \"doc\" }\n      settings\n    end\n\n    def a_user_type_for(username)\n      user = double('user', 'suitable?': true, to_s: \"User[#{username}]\")\n      expect(Puppet::Type.type(:user)).to receive(:new).with(hash_including(name: username)).and_return(user)\n      user\n    end\n\n    it \"should return false if there is no user setting\" do\n      expect(settings).not_to be_service_user_available\n    end\n\n    it \"should return false if the user provider says the user is missing\" do\n      settings[:user] = \"foo\"\n\n      expect(a_user_type_for(\"foo\")).to receive(:exists?).and_return(false)\n\n      expect(settings).not_to be_service_user_available\n    end\n\n    it \"should return true if the user provider says the user is present\" do\n      settings[:user] = \"foo\"\n\n      expect(a_user_type_for(\"foo\")).to receive(:exists?).and_return(true)\n\n      expect(settings).to be_service_user_available\n    end\n\n    it \"caches the result of determining if the user is present\" do\n      settings[:user] = \"foo\"\n\n      expect(a_user_type_for(\"foo\")).to receive(:exists?).and_return(true)\n      expect(settings).to be_service_user_available\n\n      expect(settings).to be_service_user_available\n    end\n\n    it \"raises if the user is not suitable\" do\n      settings[:user] = \"foo\"\n\n      expect(a_user_type_for(\"foo\")).to receive(:suitable?).and_return(false)\n\n      expect {\n        settings.service_user_available?\n      }.to raise_error(Puppet::Error, /Cannot manage owner permissions, because the provider for 'User\\[foo\\]' is not functional/)\n    end\n  end\n\n  describe \"when determining if the service group is available\" do\n    let(:settings) do\n      settings = Puppet::Settings.new\n      settings.define_settings :main, :group => { :default => nil, :desc => \"doc\" }\n      settings\n    end\n\n    def a_group_type_for(groupname)\n      group = double('group', 'suitable?': true, to_s: \"Group[#{groupname}]\")\n      expect(Puppet::Type.type(:group)).to receive(:new).with(hash_including(name: groupname)).and_return(group)\n      group\n    end\n\n    it \"should return false if there is no group setting\" do\n      expect(settings).not_to be_service_group_available\n    end\n\n    it \"should return false if the group provider says the group is missing\" do\n      settings[:group] = \"foo\"\n\n      expect(a_group_type_for(\"foo\")).to receive(:exists?).and_return(false)\n\n      expect(settings).not_to be_service_group_available\n    end\n\n    it \"should return true if the group provider says the group is present\" do\n      settings[:group] = \"foo\"\n\n      expect(a_group_type_for(\"foo\")).to receive(:exists?).and_return(true)\n\n      expect(settings).to be_service_group_available\n    end\n\n    it \"caches the result of determining if the group is present\" do\n      settings[:group] = \"foo\"\n\n      expect(a_group_type_for(\"foo\")).to receive(:exists?).and_return(true)\n      expect(settings).to be_service_group_available\n\n      expect(settings).to be_service_group_available\n    end\n\n    it \"raises if the group is not suitable\" do\n      settings[:group] = \"foo\"\n\n      expect(a_group_type_for(\"foo\")).to receive(:suitable?).and_return(false)\n\n      expect {\n        settings.service_group_available?\n      }.to raise_error(Puppet::Error, /Cannot manage group permissions, because the provider for 'Group\\[foo\\]' is not functional/)\n    end\n  end\n\n  describe \"when dealing with command-line options\" do\n    let(:settings) { Puppet::Settings.new }\n\n    it \"should get options from Puppet.settings.optparse_addargs\" do\n      expect(settings).to receive(:optparse_addargs).and_return([])\n\n      settings.send(:parse_global_options, [])\n    end\n\n    it \"should add options to OptionParser\" do\n      allow(settings).to receive(:optparse_addargs).and_return([[\"--option\",\"-o\", \"Funny Option\", :NONE]])\n      expect(settings).to receive(:handlearg).with(\"--option\", true)\n      settings.send(:parse_global_options, [\"--option\"])\n    end\n\n    it \"should not die if it sees an unrecognized option, because the app/face may handle it later\" do\n      expect { settings.send(:parse_global_options, [\"--topuppet\", \"value\"]) } .to_not raise_error\n    end\n\n    it \"should not pass an unrecognized option to handleargs\" do\n      expect(settings).not_to receive(:handlearg).with(\"--topuppet\", \"value\")\n      expect { settings.send(:parse_global_options, [\"--topuppet\", \"value\"]) } .to_not raise_error\n    end\n\n    it \"should pass valid puppet settings options to handlearg even if they appear after an unrecognized option\" do\n      allow(settings).to receive(:optparse_addargs).and_return([[\"--option\",\"-o\", \"Funny Option\", :NONE]])\n      expect(settings).to receive(:handlearg).with(\"--option\", true)\n      settings.send(:parse_global_options, [\"--invalidoption\", \"--option\"])\n    end\n\n    it \"should transform boolean option to normal form\" do\n      expect(Puppet::Settings.clean_opt(\"--[no-]option\", true)).to eq([\"--option\", true])\n    end\n\n    it \"should transform boolean option to no- form\" do\n      expect(Puppet::Settings.clean_opt(\"--[no-]option\", false)).to eq([\"--no-option\", false])\n    end\n\n    it \"should set preferred run mode from --run_mode <foo> string without error\" do\n      args = [\"--run_mode\", \"server\"]\n      expect(settings).not_to receive(:handlearg).with(\"--run_mode\", \"server\")\n      expect { settings.send(:parse_global_options, args) } .to_not raise_error\n      expect(Puppet.settings.preferred_run_mode).to eq(:server)\n      expect(args.empty?).to eq(true)\n    end\n\n    it \"should set preferred run mode from --run_mode=<foo> string without error\" do\n      args = [\"--run_mode=server\"]\n      expect(settings).not_to receive(:handlearg).with(\"--run_mode\", \"server\")\n      expect { settings.send(:parse_global_options, args) }.to_not raise_error\n      expect(Puppet.settings.preferred_run_mode).to eq(:server)\n      expect(args.empty?).to eq(true)\n    end\n  end\n\n  describe \"default_certname\" do\n    describe \"using hostname and domain\" do\n      before :each do\n        allow(Puppet::Settings).to receive(:hostname_fact).and_return(\"testhostname\")\n        allow(Puppet::Settings).to receive(:domain_fact).and_return(\"domain.test.\")\n      end\n\n      it \"should use both to generate fqdn\" do\n        expect(Puppet::Settings.default_certname).to match(/testhostname\\.domain\\.test/)\n      end\n      it \"should remove trailing dots from fqdn\" do\n        expect(Puppet::Settings.default_certname).to eq('testhostname.domain.test')\n      end\n    end\n\n    describe \"using just hostname\" do\n      before :each do\n        allow(Puppet::Settings).to receive(:hostname_fact).and_return(\"testhostname\")\n        allow(Puppet::Settings).to receive(:domain_fact).and_return(\"\")\n      end\n\n      it \"should use only hostname to generate fqdn\" do\n        expect(Puppet::Settings.default_certname).to eq(\"testhostname\")\n      end\n      it \"should removing trailing dots from fqdn\" do\n        expect(Puppet::Settings.default_certname).to eq(\"testhostname\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/base_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/ssl/certificate'\n\nclass TestCertificate < Puppet::SSL::Base\n    wraps(Puppet::SSL::Certificate)\nend\n\ndescribe Puppet::SSL::Certificate do\n  before :each do\n    @base = TestCertificate.new(\"name\")\n    @class = TestCertificate\n  end\n\n  describe \"when creating new instances\" do\n    it \"should fail if given an object that is not an instance of the wrapped class\" do\n      obj = double('obj', :is_a? => false)\n      expect { @class.from_instance(obj) }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if a name is not supplied and can't be determined from the object\" do\n      obj = double('obj', :is_a? => true)\n      expect { @class.from_instance(obj) }.to raise_error(ArgumentError)\n    end\n\n    it \"should determine the name from the object if it has a subject\" do\n      obj = double('obj', :is_a? => true, :subject => '/CN=foo')\n\n      inst = double('base')\n      expect(inst).to receive(:content=).with(obj)\n\n      expect(@class).to receive(:new).with('foo').and_return(inst)\n      expect(@class).to receive(:name_from_subject).with('/CN=foo').and_return('foo')\n\n      expect(@class.from_instance(obj)).to eq(inst)\n    end\n  end\n\n  describe \"when determining a name from a certificate subject\" do\n    it \"should extract only the CN and not any other components\" do\n      name = OpenSSL::X509::Name.parse('/CN=host.domain.com/L=Portland/ST=Oregon')\n      expect(@class.name_from_subject(name)).to eq('host.domain.com')\n    end\n  end\n\n  describe \"when initializing wrapped class from a file with #read\" do\n    it \"should open the file with ASCII encoding\" do\n      path = '/foo/bar/cert'\n      expect(Puppet::FileSystem).to receive(:read).with(path, {:encoding => Encoding::ASCII}).and_return(\"bar\")\n      @base.read(path)\n    end\n  end\n\n  describe \"#digest_algorithm\" do\n    let(:content) { double('content') }\n    let(:base) {\n      b = Puppet::SSL::Base.new('base')\n      b.content = content\n      b\n    }\n\n    # Some known signature algorithms taken from RFC 3279, 5758, and browsing\n    # objs_dat.h in openssl\n    {\n      'md5WithRSAEncryption' => 'md5',\n      'sha1WithRSAEncryption' => 'sha1',\n      'md4WithRSAEncryption' => 'md4',\n      'sha256WithRSAEncryption' => 'sha256',\n      'ripemd160WithRSA' => 'ripemd160',\n      'ecdsa-with-SHA1' => 'sha1',\n      'ecdsa-with-SHA224' => 'sha224',\n      'ecdsa-with-SHA256' => 'sha256',\n      'ecdsa-with-SHA384' => 'sha384',\n      'ecdsa-with-SHA512' => 'sha512',\n      'dsa_with_SHA224' => 'sha224',\n      'dsaWithSHA1' => 'sha1',\n    }.each do |signature, digest|\n      it \"returns '#{digest}' for signature algorithm '#{signature}'\" do\n        allow(content).to receive(:signature_algorithm).and_return(signature)\n        expect(base.digest_algorithm).to eq(digest)\n      end\n    end\n\n    it \"raises an error on an unknown signature algorithm\" do\n      allow(content).to receive(:signature_algorithm).and_return(\"nonsense\")\n      expect {\n        base.digest_algorithm\n      }.to raise_error(Puppet::Error, \"Unknown signature algorithm 'nonsense'\")\n    end\n  end\n\n  describe \"when getting a CN from a subject\" do\n    def parse(dn)\n      OpenSSL::X509::Name.parse(dn)\n    end\n\n    def cn_from(subject)\n      @class.name_from_subject(subject)\n    end\n\n    it \"should correctly parse a subject containing only a CN\" do\n      subj = parse('/CN=foo')\n      expect(cn_from(subj)).to eq('foo')\n    end\n\n    it \"should correctly parse a subject containing other components\" do\n      subj = parse('/CN=Root CA/OU=Server Operations/O=Example Org')\n      expect(cn_from(subj)).to eq('Root CA')\n    end\n\n    it \"should correctly parse a subject containing other components with CN not first\" do\n      subj = parse('/emailAddress=foo@bar.com/CN=foo.bar.com/O=Example Org')\n      expect(cn_from(subj)).to eq('foo.bar.com')\n    end\n\n    it \"should return nil for a subject with no CN\" do\n      subj = parse('/OU=Server Operations/O=Example Org')\n      expect(cn_from(subj)).to eq(nil)\n    end\n\n    it \"should return nil for a bare string\" do\n      expect(cn_from(\"/CN=foo\")).to eq(nil)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/certificate_request_attributes_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/ssl/certificate_request_attributes'\n\ndescribe Puppet::SSL::CertificateRequestAttributes do\n  include PuppetSpec::Files\n\n  let(:expected) do\n    {\n      \"custom_attributes\" => {\n        \"1.3.6.1.4.1.34380.2.2\"=>[3232235521, 3232235777], # system IPs in hex\n        \"1.3.6.1.4.1.34380.2.0\"=>\"hostname.domain.com\",\n        \"1.3.6.1.4.1.34380.1.1.3\"=>:node_image_name,\n        # different UTF-8 widths\n        # 1-byte A\n        # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n        # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n        # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n        \"1.2.840.113549.1.9.7\"=>\"utf8passwordA\\u06FF\\u16A0\\u{2070E}\"\n      }\n    }\n  end\n  let(:csr_attributes_hash) { expected.dup }\n  let(:csr_attributes_path) { tmpfile('csr_attributes.yaml') }\n  let(:csr_attributes) { Puppet::SSL::CertificateRequestAttributes.new(csr_attributes_path) }\n\n  it \"initializes with a path\" do\n    expect(csr_attributes.path).to eq(csr_attributes_path)\n  end\n\n  describe \"loading\" do\n    it \"returns nil when loading from a non-existent file\" do\n      nonexistent = Puppet::SSL::CertificateRequestAttributes.new('/does/not/exist.yaml')\n      expect(nonexistent.load).to be_falsey\n    end\n\n    context \"with an available attributes file\" do\n      before do\n        Puppet::Util::Yaml.dump(csr_attributes_hash, csr_attributes_path)\n      end\n\n      it \"loads csr attributes from a file when the file is present\" do\n        expect(csr_attributes.load).to be_truthy\n      end\n\n      it \"exposes custom_attributes\" do\n        csr_attributes.load\n        expect(csr_attributes.custom_attributes).to eq(expected['custom_attributes'])\n      end\n\n      it \"returns an empty hash if custom_attributes points to nil\" do\n        Puppet::Util::Yaml.dump({'custom_attributes' => nil }, csr_attributes_path)\n        csr_attributes.load\n        expect(csr_attributes.custom_attributes).to eq({})\n      end\n\n      it \"returns an empty hash if custom_attributes key is not present\" do\n        Puppet::Util::Yaml.dump({}, csr_attributes_path)\n        csr_attributes.load\n        expect(csr_attributes.custom_attributes).to eq({})\n      end\n\n      it \"raises a Puppet::Error if an unexpected root key is defined\" do\n        csr_attributes_hash['unintentional'] = 'data'\n        Puppet::Util::Yaml.dump(csr_attributes_hash, csr_attributes_path)\n        expect {\n          csr_attributes.load\n        }.to raise_error(Puppet::Error, /unexpected attributes.*unintentional/)\n      end\n\n      it \"raises a Puppet::Util::Yaml::YamlLoadError if an unexpected ruby object is present\" do\n        csr_attributes_hash['custom_attributes']['whoops'] = Object.new\n        Puppet::Util::Yaml.dump(csr_attributes_hash, csr_attributes_path)\n        expect {\n          csr_attributes.load\n        }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /Tried to load unspecified class: Object/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/certificate_request_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/ssl/certificate_request'\n\ndescribe Puppet::SSL::CertificateRequest do\n  let(:request) { described_class.new(\"myname\") }\n  let(:key) { OpenSSL::PKey::RSA.new(Puppet[:keylength]) }\n\n  it \"should use any provided name as its name\" do\n    expect(described_class.new(\"myname\").name).to eq(\"myname\")\n  end\n\n  it \"should only support the text format\" do\n    expect(described_class.supported_formats).to eq([:s])\n  end\n\n  describe \"when converting from a string\" do\n    it \"should create a CSR instance with its name set to the CSR subject and its content set to the extracted CSR\" do\n      csr = double('csr',\n        :subject => OpenSSL::X509::Name.parse(\"/CN=Foo.madstop.com\"),\n        :is_a? => true)\n      expect(OpenSSL::X509::Request).to receive(:new).with(\"my csr\").and_return(csr)\n\n      mycsr = double('sslcsr')\n      expect(mycsr).to receive(:content=).with(csr)\n\n      expect(described_class).to receive(:new).with(\"Foo.madstop.com\").and_return(mycsr)\n\n      described_class.from_s(\"my csr\")\n    end\n  end\n\n  describe \"when managing instances\" do\n    it \"should have a name attribute\" do\n      expect(request.name).to eq(\"myname\")\n    end\n\n    it \"should downcase its name\" do\n      expect(described_class.new(\"MyName\").name).to eq(\"myname\")\n    end\n\n    it \"should have a content attribute\" do\n      expect(request).to respond_to(:content)\n    end\n\n    it \"should be able to read requests from disk\" do\n      path = \"/my/path\"\n      expect(Puppet::FileSystem).to receive(:read).with(path, {:encoding => Encoding::ASCII}).and_return(\"my request\")\n      my_req = double('request')\n      expect(OpenSSL::X509::Request).to receive(:new).with(\"my request\").and_return(my_req)\n      expect(request.read(path)).to equal(my_req)\n      expect(request.content).to equal(my_req)\n    end\n\n    it \"should return an empty string when converted to a string with no request\" do\n      expect(request.to_s).to eq(\"\")\n    end\n\n    it \"should convert the request to pem format when converted to a string\", :unless => RUBY_PLATFORM == 'java' do\n      request.generate(key)\n      expect(request.to_s).to eq(request.content.to_pem)\n    end\n\n    it \"should have a :to_text method that it delegates to the actual key\" do\n      real_request = double('request')\n      expect(real_request).to receive(:to_text).and_return(\"requesttext\")\n      request.content = real_request\n      expect(request.to_text).to eq(\"requesttext\")\n    end\n  end\n\n  describe \"when generating\", :unless => RUBY_PLATFORM == 'java' do\n    it \"should verify the CSR using the public key associated with the private key\" do\n      request.generate(key)\n      expect(request.content.verify(key.public_key)).to be_truthy\n    end\n\n    it \"should set the version to 0\" do\n      request.generate(key)\n      expect(request.content.version).to eq(0)\n    end\n\n    it \"should set the public key to the provided key's public key\" do\n      request.generate(key)\n      # The openssl bindings do not define equality on keys so we use to_s\n      expect(request.content.public_key.to_s).to eq(key.public_key.to_s)\n    end\n\n    context \"without subjectAltName / dns_alt_names\" do\n      before :each do\n        Puppet[:dns_alt_names] = \"\"\n      end\n\n      [\"extreq\", \"msExtReq\"].each do |name|\n        it \"should not add any #{name} attribute\" do\n          request.generate(key)\n          expect(request.content.attributes.find do |attr|\n            attr.oid == name\n          end).not_to be\n        end\n\n        it \"should return no subjectAltNames\" do\n          request.generate(key)\n          expect(request.subject_alt_names).to be_empty\n        end\n      end\n    end\n\n    context \"with dns_alt_names\" do\n      before :each do\n        Puppet[:dns_alt_names] = \"one, two, three\"\n      end\n\n      [\"extreq\", \"msExtReq\"].each do |name|\n        it \"should not add any #{name} attribute\" do\n          request.generate(key)\n          expect(request.content.attributes.find do |attr|\n            attr.oid == name\n          end).not_to be\n        end\n\n        it \"should return no subjectAltNames\" do\n          request.generate(key)\n          expect(request.subject_alt_names).to be_empty\n        end\n      end\n    end\n\n    context \"with subjectAltName to generate request\" do\n      before :each do\n        Puppet[:dns_alt_names] = \"\"\n      end\n\n      it \"should add an extreq attribute\" do\n        request.generate(key, :dns_alt_names => 'one, two')\n        extReq = request.content.attributes.find do |attr|\n          attr.oid == 'extReq'\n        end\n\n        expect(extReq).to be\n        extReq.value.value.all? do |x|\n          x.value.all? do |y|\n            expect(y.value[0].value).to eq(\"subjectAltName\")\n          end\n        end\n      end\n\n      it \"should return the subjectAltName values\" do\n        request.generate(key, :dns_alt_names => 'one,two')\n        expect(request.subject_alt_names).to match_array([\"DNS:myname\", \"DNS:one\", \"DNS:two\"])\n      end\n    end\n\n    context \"with DNS and IP SAN specified\" do\n      before :each do\n        Puppet[:dns_alt_names] = \"\"\n      end\n\n      it \"should return the subjectAltName values\" do\n        request.generate(key, :dns_alt_names => 'DNS:foo, bar, IP:172.16.254.1')\n        expect(request.subject_alt_names).to match_array([\"DNS:bar\", \"DNS:foo\", \"DNS:myname\", \"IP Address:172.16.254.1\"])\n      end\n    end\n\n    context \"with custom CSR attributes\" do\n\n      it \"adds attributes with single values\" do\n        csr_attributes = {\n          '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',\n          '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',\n        }\n\n        request.generate(key, :csr_attributes => csr_attributes)\n\n        attrs = request.custom_attributes\n        expect(attrs).to include({'oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'CSR specific info'})\n        expect(attrs).to include({'oid' => '1.3.6.1.4.1.34380.1.2.2', 'value' => 'more CSR specific info'})\n      end\n\n      ['extReq', '1.2.840.113549.1.9.14'].each do |oid|\n        it \"doesn't overwrite standard PKCS#9 CSR attribute '#{oid}'\" do\n          expect do\n            request.generate(key, :csr_attributes => {oid => 'data'})\n          end.to raise_error ArgumentError, /Cannot specify.*#{oid}/\n        end\n      end\n\n      ['msExtReq', '1.3.6.1.4.1.311.2.1.14'].each do |oid|\n        it \"doesn't overwrite Microsoft extension request OID '#{oid}'\" do\n          expect do\n            request.generate(key, :csr_attributes => {oid => 'data'})\n          end.to raise_error ArgumentError, /Cannot specify.*#{oid}/\n        end\n      end\n\n      it \"raises an error if an attribute cannot be created\" do\n        csr_attributes = { \"thats.no.moon\" => \"death star\" }\n\n        expect do\n          request.generate(key, :csr_attributes => csr_attributes)\n        end.to raise_error Puppet::Error, /Cannot create CSR with attribute thats\\.no\\.moon: /\n      end\n\n      it \"should support old non-DER encoded extensions\" do\n        csr = OpenSSL::X509::Request.new(File.read(my_fixture(\"old-style-cert-request.pem\")))\n        wrapped_csr = Puppet::SSL::CertificateRequest.from_instance csr\n        exts = wrapped_csr.request_extensions()\n\n        expect(exts.find { |ext| ext['oid'] == 'pp_uuid' }['value']).to eq('I-AM-A-UUID')\n        expect(exts.find { |ext| ext['oid'] == 'pp_instance_id' }['value']).to eq('i_am_an_id')\n        expect(exts.find { |ext| ext['oid'] == 'pp_image_name' }['value']).to eq('i_am_an_image_name')\n      end\n    end\n\n    context \"with extension requests\" do\n      let(:extension_data) do\n        {\n          '1.3.6.1.4.1.34380.1.1.31415' => 'pi',\n          '1.3.6.1.4.1.34380.1.1.2718'  => 'e',\n        }\n      end\n\n      it \"adds an extreq attribute to the CSR\" do\n        request.generate(key, :extension_requests => extension_data)\n\n        exts = request.content.attributes.select { |attr| attr.oid = 'extReq' }\n        expect(exts.length).to eq(1)\n      end\n\n      it \"adds an extension for each entry in the extension request structure\" do\n        request.generate(key, :extension_requests => extension_data)\n\n        exts = request.request_extensions\n\n        expect(exts).to include('oid' => '1.3.6.1.4.1.34380.1.1.31415', 'value' => 'pi')\n        expect(exts).to include('oid' => '1.3.6.1.4.1.34380.1.1.2718', 'value' => 'e')\n      end\n\n      it \"defines the extensions as non-critical\" do\n        request.generate(key, :extension_requests => extension_data)\n        request.request_extensions.each do |ext|\n          expect(ext['critical']).to be_falsey\n        end\n      end\n\n      it \"rejects the subjectAltNames extension\" do\n        san_names = ['subjectAltName', '2.5.29.17']\n        san_field = 'DNS:first.tld, DNS:second.tld'\n\n        san_names.each do |name|\n          expect do\n            request.generate(key, :extension_requests => {name => san_field})\n          end.to raise_error Puppet::Error, /conflicts with internally used extension/\n        end\n      end\n\n      it \"merges the extReq attribute with the subjectAltNames extension\" do\n        request.generate(key,\n                         :dns_alt_names => 'first.tld, second.tld',\n                         :extension_requests => extension_data)\n        exts = request.request_extensions\n\n        expect(exts).to include('oid' => '1.3.6.1.4.1.34380.1.1.31415', 'value' => 'pi')\n        expect(exts).to include('oid' => '1.3.6.1.4.1.34380.1.1.2718', 'value' => 'e')\n        expect(exts).to include('oid' => 'subjectAltName', 'value' => 'DNS:first.tld, DNS:myname, DNS:second.tld')\n\n        expect(request.subject_alt_names).to eq ['DNS:first.tld', 'DNS:myname', 'DNS:second.tld']\n      end\n\n      it \"raises an error if the OID could not be created\" do\n        exts = {\"thats.no.moon\" => \"death star\"}\n        expect do\n          request.generate(key, :extension_requests => exts)\n        end.to raise_error Puppet::Error, /Cannot create CSR with extension request thats\\.no\\.moon.*: /\n      end\n    end\n\n    it \"should sign the csr with the provided key\" do\n      request.generate(key)\n      expect(request.content.verify(key.public_key)).to be_truthy\n    end\n\n    it \"should verify the generated request using the public key\" do\n      # Stupid keys don't have a competent == method.\n      expect_any_instance_of(OpenSSL::X509::Request).to receive(:verify) do |public_key|\n        public_key.to_s == key.public_key.to_s\n      end.and_return(true)\n      request.generate(key)\n    end\n\n    it \"should fail if verification fails\" do\n      expect_any_instance_of(OpenSSL::X509::Request).to receive(:verify) do |public_key|\n        public_key.to_s == key.public_key.to_s\n      end.and_return(false)\n\n      expect do\n        request.generate(key)\n      end.to raise_error(Puppet::Error, /CSR sign verification failed/)\n    end\n\n    it \"should log the fingerprint\" do\n      allow_any_instance_of(Puppet::SSL::Digest).to receive(:to_hex).and_return(\"FINGERPRINT\")\n      allow(Puppet).to receive(:info)\n      expect(Puppet).to receive(:info).with(/FINGERPRINT/)\n      request.generate(key)\n    end\n\n    it \"should return the generated request\" do\n      generated = request.generate(key)\n      expect(generated).to be_a(OpenSSL::X509::Request)\n      expect(generated).to be(request.content)\n    end\n\n    it \"should use SHA1 to sign the csr when SHA256 isn't available\" do\n      csr = OpenSSL::X509::Request.new\n      csr.public_key = key.public_key\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA256\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA1\").and_return(true)\n      signer = Puppet::SSL::CertificateSigner.new\n      signer.sign(csr, key)\n      expect(csr.verify(key)).to be_truthy\n    end\n\n    it \"should use SHA512 to sign the csr when SHA256 and SHA1 aren't available\" do\n      key = OpenSSL::PKey::RSA.new(2048)\n      csr = OpenSSL::X509::Request.new\n      csr.public_key = key.public_key\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA256\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA1\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA512\").and_return(true)\n      signer = Puppet::SSL::CertificateSigner.new\n      signer.sign(csr, key)\n      expect(csr.verify(key)).to be_truthy\n    end\n\n    it \"should use SHA384 to sign the csr when SHA256/SHA1/SHA512 aren't available\" do\n      key = OpenSSL::PKey::RSA.new(2048)\n      csr = OpenSSL::X509::Request.new\n      csr.public_key = key.public_key\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA256\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA1\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA512\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA384\").and_return(true)\n      signer = Puppet::SSL::CertificateSigner.new\n      signer.sign(csr, key)\n      expect(csr.verify(key)).to be_truthy\n    end\n\n    it \"should use SHA224 to sign the csr when SHA256/SHA1/SHA512/SHA384 aren't available\" do\n      csr = OpenSSL::X509::Request.new\n      csr.public_key = key.public_key\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA256\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA1\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA512\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA384\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA224\").and_return(true)\n      signer = Puppet::SSL::CertificateSigner.new\n      signer.sign(csr, key)\n      expect(csr.verify(key)).to be_truthy\n    end\n\n    it \"should raise an error if neither SHA256/SHA1/SHA512/SHA384/SHA224 are available\" do\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA256\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA1\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA512\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA384\").and_return(false)\n      expect(OpenSSL::Digest).to receive(:const_defined?).with(\"SHA224\").and_return(false)\n      expect {\n        Puppet::SSL::CertificateSigner.new\n      }.to raise_error(Puppet::Error)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/certificate_signer_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::SSL::CertificateSigner do\n  include PuppetSpec::Files\n\n  let(:wrong_key) { OpenSSL::PKey::RSA.new(512) }\n  let(:client_cert) { cert_fixture('signed.pem') }\n\n  # jruby-openssl >= 0.13.0 (JRuby >= 9.3.5.0) raises an error when signing a\n  # certificate when there is a discrepancy between the certificate and key.\n  it 'raises if client cert signature is invalid', if: Puppet::Util::Platform.jruby? && RUBY_VERSION.to_f >= 2.6 do\n    expect {\n      client_cert.sign(wrong_key, OpenSSL::Digest::SHA256.new)\n    }.to raise_error(OpenSSL::X509::CertificateError,\n                     'invalid public key data')\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/certificate_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/certificate_factory'\n\nrequire 'puppet/ssl/certificate'\n\ndescribe Puppet::SSL::Certificate do\n  let :key do OpenSSL::PKey::RSA.new(Puppet[:keylength]) end\n\n  # Sign the provided cert so that it can be DER-decoded later\n  def sign_wrapped_cert(cert)\n    signer = Puppet::SSL::CertificateSigner.new\n    signer.sign(cert.content, key)\n  end\n\n  before do\n    @class = Puppet::SSL::Certificate\n  end\n\n  it \"should only support the text format\" do\n    expect(@class.supported_formats).to eq([:s])\n  end\n\n  describe \"when converting from a string\" do\n    it \"should create a certificate instance with its name set to the certificate subject and its content set to the extracted certificate\" do\n      cert = double(\n        'certificate',\n        :subject => OpenSSL::X509::Name.parse(\"/CN=Foo.madstop.com\"),\n        :is_a? => true\n      )\n      expect(OpenSSL::X509::Certificate).to receive(:new).with(\"my certificate\").and_return(cert)\n\n      mycert = double('sslcert')\n      expect(mycert).to receive(:content=).with(cert)\n\n      expect(@class).to receive(:new).with(\"Foo.madstop.com\").and_return(mycert)\n\n      @class.from_s(\"my certificate\")\n    end\n\n    it \"should create multiple certificate instances when asked\" do\n      cert1 = double('cert1')\n      expect(@class).to receive(:from_s).with(\"cert1\").and_return(cert1)\n      cert2 = double('cert2')\n      expect(@class).to receive(:from_s).with(\"cert2\").and_return(cert2)\n\n      expect(@class.from_multiple_s(\"cert1\\n---\\ncert2\")).to eq([cert1, cert2])\n    end\n  end\n\n  describe \"when converting to a string\" do\n    before do\n      @certificate = @class.new(\"myname\")\n    end\n\n    it \"should return an empty string when it has no certificate\" do\n      expect(@certificate.to_s).to eq(\"\")\n    end\n\n    it \"should convert the certificate to pem format\" do\n      certificate = double('certificate', :to_pem => \"pem\")\n      @certificate.content = certificate\n      expect(@certificate.to_s).to eq(\"pem\")\n    end\n\n    it \"should be able to convert multiple instances to a string\" do\n      cert2 = @class.new(\"foo\")\n      expect(@certificate).to receive(:to_s).and_return(\"cert1\")\n      expect(cert2).to receive(:to_s).and_return(\"cert2\")\n\n      expect(@class.to_multiple_s([@certificate, cert2])).to eq(\"cert1\\n---\\ncert2\")\n\n    end\n  end\n\n  describe \"when managing instances\" do\n    def build_cert(opts)\n      key = OpenSSL::PKey::RSA.new(Puppet[:keylength])\n      csr = Puppet::SSL::CertificateRequest.new('quux')\n      csr.generate(key, opts)\n\n      raw_cert = Puppet::CertificateFactory.build('client', csr, csr.content, 14)\n      @class.from_instance(raw_cert)\n    end\n\n    before do\n      @certificate = @class.new(\"myname\")\n    end\n\n    it \"should have a name attribute\" do\n      expect(@certificate.name).to eq(\"myname\")\n    end\n\n    it \"should convert its name to a string and downcase it\" do\n      expect(@class.new(:MyName).name).to eq(\"myname\")\n    end\n\n    it \"should have a content attribute\" do\n      expect(@certificate).to respond_to(:content)\n    end\n\n    describe \"#subject_alt_names\", :unless => RUBY_PLATFORM == 'java' do\n      it \"should list all alternate names when the extension is present\" do\n        certificate = build_cert(:dns_alt_names => 'foo, bar,baz')\n        expect(certificate.subject_alt_names).\n          to match_array(['DNS:foo', 'DNS:bar', 'DNS:baz', 'DNS:quux'])\n      end\n\n      it \"should return an empty list of names if the extension is absent\" do\n        certificate = build_cert({})\n        expect(certificate.subject_alt_names).to be_empty\n      end\n    end\n\n    describe \"custom extensions\", :unless => RUBY_PLATFORM == 'java' do\n      it \"returns extensions under the ppRegCertExt\" do\n        exts = {'pp_uuid' => 'abcdfd'}\n        cert = build_cert(:extension_requests => exts)\n        sign_wrapped_cert(cert)\n        expect(cert.custom_extensions).to include('oid' => 'pp_uuid', 'value' => 'abcdfd')\n      end\n\n      it \"returns extensions under the ppPrivCertExt\" do\n        exts = {'1.3.6.1.4.1.34380.1.2.1' => 'x509 :('}\n        cert = build_cert(:extension_requests => exts)\n        sign_wrapped_cert(cert)\n        expect(cert.custom_extensions).to include('oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'x509 :(')\n      end\n\n      it \"returns extensions under the ppAuthCertExt\" do\n        exts = {'pp_auth_role' => 'taketwo'}\n        cert = build_cert(:extension_requests => exts)\n        sign_wrapped_cert(cert)\n        expect(cert.custom_extensions).to include('oid' => 'pp_auth_role', 'value' => 'taketwo')\n      end\n\n      it \"doesn't return standard extensions\" do\n        cert = build_cert(:dns_alt_names => 'foo')\n        expect(cert.custom_extensions).to be_empty\n      end\n    end\n\n    it \"should return a nil expiration if there is no actual certificate\" do\n      allow(@certificate).to receive(:content).and_return(nil)\n\n      expect(@certificate.expiration).to be_nil\n    end\n\n    it \"should use the expiration of the certificate as its expiration date\" do\n      cert = double('cert')\n      allow(@certificate).to receive(:content).and_return(cert)\n\n      expect(cert).to receive(:not_after).and_return(\"sometime\")\n\n      expect(@certificate.expiration).to eq(\"sometime\")\n    end\n\n    it \"should be able to read certificates from disk\" do\n      path = \"/my/path\"\n      expect(Puppet::FileSystem).to receive(:read).with(path, {:encoding => Encoding::ASCII}).and_return(\"my certificate\")\n      certificate = double('certificate')\n      expect(OpenSSL::X509::Certificate).to receive(:new).with(\"my certificate\").and_return(certificate)\n      expect(@certificate.read(path)).to equal(certificate)\n      expect(@certificate.content).to equal(certificate)\n    end\n\n    it \"should have a :to_text method that it delegates to the actual key\" do\n      real_certificate = double('certificate')\n      expect(real_certificate).to receive(:to_text).and_return(\"certificatetext\")\n      @certificate.content = real_certificate\n      expect(@certificate.to_text).to eq(\"certificatetext\")\n    end\n\n    it \"should parse the old non-DER encoded extension values\" do\n      cert = OpenSSL::X509::Certificate.new(File.read(my_fixture(\"old-style-cert-exts.pem\")))\n      wrapped_cert = Puppet::SSL::Certificate.from_instance cert\n      exts = wrapped_cert.custom_extensions\n\n      expect(exts.find { |ext| ext['oid'] == 'pp_uuid'}['value']).to eq('I-AM-A-UUID')\n      expect(exts.find { |ext| ext['oid'] == 'pp_instance_id'}['value']).to eq('i_am_an_id')\n      expect(exts.find { |ext| ext['oid'] == 'pp_image_name'}['value']).to eq('i_am_an_image_name')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/digest_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/ssl/digest'\n\ndescribe Puppet::SSL::Digest do\n  it \"defaults to sha256\" do\n    digest = described_class.new(nil, 'blah')\n    expect(digest.name).to eq('SHA256')\n    expect(digest.digest.hexdigest).to eq(\"8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52\")\n  end\n\n  describe '#name' do\n    it \"prints the hashing algorithm used by the openssl digest\" do\n      expect(described_class.new('SHA224', 'blah').name).to eq('SHA224')\n    end\n\n    it \"upcases the hashing algorithm\" do\n      expect(described_class.new('sha224', 'blah').name).to eq('SHA224')\n    end\n  end\n\n  describe '#to_hex' do\n    it \"returns ':' separated upper case hex pairs\" do\n      described_class.new(nil, 'blah').to_hex =~ /\\A([A-Z0-9]:)+[A-Z0-9]\\Z/\n    end\n  end\n\n  describe '#to_s' do\n    it \"formats the digest algorithm and the digest as a string\" do\n      digest = described_class.new('sha512', 'some content')\n      expect(digest.to_s).to eq(\"(#{digest.name}) #{digest.to_hex}\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/oids_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/ssl/oids'\n\ndescribe Puppet::SSL::Oids do\n  describe \"defining application OIDs\" do\n\n    {\n      'puppetlabs'          => '1.3.6.1.4.1.34380',\n      'ppCertExt'           => '1.3.6.1.4.1.34380.1',\n      'ppRegCertExt'        => '1.3.6.1.4.1.34380.1.1',\n      'pp_uuid'             => '1.3.6.1.4.1.34380.1.1.1',\n      'pp_instance_id'      => '1.3.6.1.4.1.34380.1.1.2',\n      'pp_image_name'       => '1.3.6.1.4.1.34380.1.1.3',\n      'pp_preshared_key'    => '1.3.6.1.4.1.34380.1.1.4',\n      'pp_cost_center'      => '1.3.6.1.4.1.34380.1.1.5',\n      'pp_product'          => '1.3.6.1.4.1.34380.1.1.6',\n      'pp_project'          => '1.3.6.1.4.1.34380.1.1.7',\n      'pp_application'      => '1.3.6.1.4.1.34380.1.1.8',\n      'pp_service'          => '1.3.6.1.4.1.34380.1.1.9',\n      'pp_employee'         => '1.3.6.1.4.1.34380.1.1.10',\n      'pp_created_by'       => '1.3.6.1.4.1.34380.1.1.11',\n      'pp_environment'      => '1.3.6.1.4.1.34380.1.1.12',\n      'pp_role'             => '1.3.6.1.4.1.34380.1.1.13',\n      'pp_software_version' => '1.3.6.1.4.1.34380.1.1.14',\n      'pp_department'       => '1.3.6.1.4.1.34380.1.1.15',\n      'pp_cluster'          => '1.3.6.1.4.1.34380.1.1.16',\n      'pp_provisioner'      => '1.3.6.1.4.1.34380.1.1.17',\n      'pp_region'           => '1.3.6.1.4.1.34380.1.1.18',\n      'pp_datacenter'       => '1.3.6.1.4.1.34380.1.1.19',\n      'pp_zone'             => '1.3.6.1.4.1.34380.1.1.20',\n      'pp_network'          => '1.3.6.1.4.1.34380.1.1.21',\n      'pp_securitypolicy'   => '1.3.6.1.4.1.34380.1.1.22',\n      'pp_cloudplatform'    => '1.3.6.1.4.1.34380.1.1.23',\n      'pp_apptier'          => '1.3.6.1.4.1.34380.1.1.24',\n      'pp_hostname'         => '1.3.6.1.4.1.34380.1.1.25',\n      'pp_owner'            => '1.3.6.1.4.1.34380.1.1.26',\n      'ppPrivCertExt'       => '1.3.6.1.4.1.34380.1.2',\n      'ppAuthCertExt'       => '1.3.6.1.4.1.34380.1.3',\n      'pp_authorization'    => '1.3.6.1.4.1.34380.1.3.1',\n      'pp_auth_role'        => '1.3.6.1.4.1.34380.1.3.13',\n    }.each_pair do |sn, oid|\n      it \"defines #{sn} as #{oid}\" do\n        object_id = OpenSSL::ASN1::ObjectId.new(sn)\n        expect(object_id.oid).to eq oid\n      end\n    end\n  end\n\n  describe \"checking if an OID is a subtree of another OID\" do\n\n    it \"can determine if an OID is contained in another OID\" do\n      expect(described_class.subtree_of?('1.3.6.1', '1.3.6.1.4.1')).to be_truthy\n      expect(described_class.subtree_of?('1.3.6.1.4.1', '1.3.6.1')).to be_falsey\n    end\n\n    it \"returns true if an OID is compared against itself and exclusive is false\" do\n      expect(described_class.subtree_of?('1.3.6.1', '1.3.6.1', false)).to be_truthy\n    end\n\n    it \"returns false if an OID is compared against itself and exclusive is true\" do\n      expect(described_class.subtree_of?('1.3.6.1', '1.3.6.1', true)).to be_falsey\n    end\n\n    it \"can compare OIDs defined as short names\" do\n      expect(described_class.subtree_of?('IANA', '1.3.6.1.4.1')).to be_truthy\n      expect(described_class.subtree_of?('1.3.6.1', 'enterprises')).to be_truthy\n    end\n\n    it \"returns false when an invalid OID shortname is passed\" do\n      expect(described_class.subtree_of?('IANA', 'bananas')).to be_falsey\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/ssl_provider_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::SSL::SSLProvider do\n  include PuppetSpec::Files\n\n  let(:global_cacerts) { [ cert_fixture('ca.pem'), cert_fixture('intermediate.pem') ] }\n  let(:global_crls) { [ crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem') ] }\n  let(:wrong_key) { OpenSSL::PKey::RSA.new(512) }\n\n  context 'when creating an insecure context' do\n    let(:sslctx) { subject.create_insecure_context }\n\n    it 'has an empty list of trusted certs' do\n      expect(sslctx.cacerts).to eq([])\n    end\n\n    it 'has an empty list of crls' do\n      expect(sslctx.crls).to eq([])\n    end\n\n    it 'has an empty chain' do\n      expect(sslctx.client_chain).to eq([])\n    end\n\n    it 'has a nil private key and cert' do\n      expect(sslctx.private_key).to be_nil\n      expect(sslctx.client_cert).to be_nil\n    end\n\n    it 'does not authenticate the server' do\n      expect(sslctx.verify_peer).to eq(false)\n    end\n\n    it 'raises if the frozen context is modified' do\n      expect {\n        sslctx.cacerts = []\n      }.to raise_error(/can't modify frozen/)\n    end\n  end\n\n  context 'when creating an root ssl context with CA certs' do\n    let(:config) { { cacerts: [], crls: [], revocation: false } }\n\n    it 'accepts empty list of certs and crls' do\n      sslctx = subject.create_root_context(**config)\n      expect(sslctx.cacerts).to eq([])\n      expect(sslctx.crls).to eq([])\n    end\n\n    it 'accepts valid root certs' do\n      certs = [cert_fixture('ca.pem')]\n      sslctx = subject.create_root_context(**config.merge(cacerts: certs))\n      expect(sslctx.cacerts).to eq(certs)\n    end\n\n    it 'accepts valid intermediate certs' do\n      certs = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]\n      sslctx = subject.create_root_context(**config.merge(cacerts: certs))\n      expect(sslctx.cacerts).to eq(certs)\n    end\n\n    it 'accepts expired CA certs' do\n      expired = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]\n      expired.each { |x509| x509.not_after = Time.at(0) }\n\n      sslctx = subject.create_root_context(**config.merge(cacerts: expired))\n      expect(sslctx.cacerts).to eq(expired)\n    end\n\n    it 'raises if the frozen context is modified' do\n      sslctx = subject.create_root_context(**config)\n      expect {\n        sslctx.verify_peer = false\n      }.to raise_error(/can't modify frozen/)\n    end\n\n    it 'verifies peer' do\n      sslctx = subject.create_root_context(**config)\n      expect(sslctx.verify_peer).to eq(true)\n    end\n  end\n\n  context 'when creating a system ssl context' do\n    it 'accepts empty list of CA certs' do\n      sslctx = subject.create_system_context(cacerts: [])\n      expect(sslctx.cacerts).to eq([])\n    end\n\n    it 'accepts valid root certs' do\n      certs = [cert_fixture('ca.pem')]\n      sslctx = subject.create_system_context(cacerts: certs)\n      expect(sslctx.cacerts).to eq(certs)\n    end\n\n    it 'accepts valid intermediate certs' do\n      certs = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]\n      sslctx = subject.create_system_context(cacerts: certs)\n      expect(sslctx.cacerts).to eq(certs)\n    end\n\n    it 'accepts expired CA certs' do\n      expired = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]\n      expired.each { |x509| x509.not_after = Time.at(0) }\n\n      sslctx = subject.create_system_context(cacerts: expired)\n      expect(sslctx.cacerts).to eq(expired)\n    end\n\n    it 'raises if the frozen context is modified' do\n      sslctx = subject.create_system_context(cacerts: [])\n      expect {\n        sslctx.verify_peer = false\n      }.to raise_error(/can't modify frozen/)\n    end\n\n    it 'trusts system ca store by default' do\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths)\n\n      subject.create_system_context(cacerts: [])\n    end\n\n    it 'trusts an external ca store' do\n      path = tmpfile('system_cacerts')\n      File.write(path, cert_fixture('ca.pem').to_pem)\n\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:add_file).with(path)\n\n      subject.create_system_context(cacerts: [], path: path)\n    end\n\n    it 'verifies peer' do\n      sslctx = subject.create_system_context(cacerts: [])\n      expect(sslctx.verify_peer).to eq(true)\n    end\n\n    it 'disable revocation' do\n      sslctx = subject.create_system_context(cacerts: [])\n      expect(sslctx.revocation).to eq(false)\n    end\n\n    it 'sets client cert and private key to nil' do\n      sslctx = subject.create_system_context(cacerts: [])\n      expect(sslctx.client_cert).to be_nil\n      expect(sslctx.private_key).to be_nil\n    end\n\n    it 'includes the client cert and private key when requested' do\n      Puppet[:hostcert] = fixtures('ssl/signed.pem')\n      Puppet[:hostprivkey] = fixtures('ssl/signed-key.pem')\n      sslctx = subject.create_system_context(cacerts: [], include_client_cert: true)\n      expect(sslctx.client_cert).to be_an(OpenSSL::X509::Certificate)\n      expect(sslctx.private_key).to be_an(OpenSSL::PKey::RSA)\n    end\n\n    it 'ignores non-existent client cert and private key when requested' do\n      Puppet[:certname] = 'doesnotexist'\n      sslctx = subject.create_system_context(cacerts: [], include_client_cert: true)\n      expect(sslctx.client_cert).to be_nil\n      expect(sslctx.private_key).to be_nil\n    end\n\n    it 'warns if the client cert does not exist' do\n      Puppet[:certname] = 'missingcert'\n      Puppet[:hostprivkey] = fixtures('ssl/signed-key.pem')\n\n      expect(Puppet).to receive(:warning).with(\"Client certificate for 'missingcert' does not exist\")\n      subject.create_system_context(cacerts: [], include_client_cert: true)\n    end\n\n    it 'warns if the private key does not exist' do\n      Puppet[:certname] = 'missingkey'\n      Puppet[:hostcert] = fixtures('ssl/signed.pem')\n\n      expect(Puppet).to receive(:warning).with(\"Private key for 'missingkey' does not exist\")\n      subject.create_system_context(cacerts: [], include_client_cert: true)\n    end\n\n    it 'raises if client cert and private key are mismatched' do\n      Puppet[:hostcert] = fixtures('ssl/signed.pem')\n      Puppet[:hostprivkey] = fixtures('ssl/127.0.0.1-key.pem')\n\n      expect {\n        subject.create_system_context(cacerts: [], include_client_cert: true)\n      }.to raise_error(Puppet::SSL::SSLError,\n        \"The certificate for 'CN=signed' does not match its private key\")\n    end\n\n    it 'trusts additional system certs' do\n      path = tmpfile('system_cacerts')\n      File.write(path, cert_fixture('ca.pem').to_pem)\n\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:add_file).with(path)\n\n      subject.create_system_context(cacerts: [], path: path)\n    end\n\n    it 'ignores empty files' do\n      path = tmpfile('system_cacerts')\n      FileUtils.touch(path)\n\n      subject.create_system_context(cacerts: [], path: path)\n\n      expect(@logs).to eq([])\n    end\n\n    it 'prints an error if it is not a file' do\n      path = tmpdir('system_cacerts')\n\n      subject.create_system_context(cacerts: [], path: path)\n\n      expect(@logs).to include(an_object_having_attributes(level: :warning, message: /^The 'ssl_trust_store' setting does not refer to a file and will be ignored/))\n    end\n  end\n\n  context 'when creating an ssl context with crls' do\n    let(:config) { { cacerts: global_cacerts, crls: global_crls} }\n\n    it 'accepts valid CRLs' do\n      certs = [cert_fixture('ca.pem')]\n      crls = [crl_fixture('crl.pem')]\n      sslctx = subject.create_root_context(**config.merge(cacerts: certs, crls: crls))\n      expect(sslctx.crls).to eq(crls)\n    end\n\n    it 'accepts valid CRLs for intermediate certs' do\n      certs = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]\n      crls = [crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem')]\n      sslctx = subject.create_root_context(**config.merge(cacerts: certs, crls: crls))\n      expect(sslctx.crls).to eq(crls)\n    end\n\n    it 'accepts expired CRLs' do\n      expired = [crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem')]\n      expired.each { |x509| x509.last_update = Time.at(0) }\n\n      sslctx = subject.create_root_context(**config.merge(crls: expired))\n      expect(sslctx.crls).to eq(expired)\n    end\n\n    it 'verifies peer' do\n      sslctx = subject.create_root_context(**config)\n      expect(sslctx.verify_peer).to eq(true)\n    end\n  end\n\n  context 'when creating an ssl context with client certs' do\n    let(:client_cert) { cert_fixture('signed.pem') }\n    let(:private_key) { key_fixture('signed-key.pem') }\n    let(:config) { { cacerts: global_cacerts, crls: global_crls, client_cert: client_cert, private_key: private_key } }\n\n    it 'raises if CA certs are missing' do\n      expect {\n        subject.create_context(**config.merge(cacerts: nil))\n      }.to raise_error(ArgumentError, /CA certs are missing/)\n    end\n\n    it 'raises if CRLs are missing' do\n      expect {\n        subject.create_context(**config.merge(crls: nil))\n      }.to raise_error(ArgumentError, /CRLs are missing/)\n    end\n\n    it 'raises if private key is missing' do\n      expect {\n        subject.create_context(**config.merge(private_key: nil))\n      }.to raise_error(ArgumentError, /Private key is missing/)\n    end\n\n    it 'raises if client cert is missing' do\n      expect {\n        subject.create_context(**config.merge(client_cert: nil))\n      }.to raise_error(ArgumentError, /Client cert is missing/)\n    end\n\n    it 'accepts RSA keys' do\n      sslctx = subject.create_context(**config)\n      expect(sslctx.private_key).to eq(private_key)\n    end\n\n    it 'accepts EC keys' do\n      ec_key = ec_key_fixture('ec-key.pem')\n      ec_cert = cert_fixture('ec.pem')\n      sslctx = subject.create_context(**config.merge(client_cert: ec_cert, private_key: ec_key))\n      expect(sslctx.private_key).to eq(ec_key)\n    end\n\n    it 'raises if private key is unsupported' do\n      dsa_key = OpenSSL::PKey::DSA.new\n      expect {\n        subject.create_context(**config.merge(private_key: dsa_key))\n      }.to raise_error(Puppet::SSL::SSLError, /Unsupported key 'OpenSSL::PKey::DSA'/)\n    end\n\n    it 'resolves the client chain from leaf to root' do\n      sslctx = subject.create_context(**config)\n      expect(\n        sslctx.client_chain.map(&:subject).map(&:to_utf8)\n      ).to eq(['CN=signed', 'CN=Test CA Subauthority', 'CN=Test CA'])\n    end\n\n    it 'raises if client cert signature is invalid' do\n      client_cert.public_key = wrong_key.public_key\n      client_cert.sign(wrong_key, OpenSSL::Digest::SHA256.new)\n      expect {\n        subject.create_context(**config.merge(client_cert: client_cert))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"Invalid signature for certificate 'CN=signed'\")\n    end\n\n    it 'raises if client cert and private key are mismatched' do\n      expect {\n        subject.create_context(**config.merge(private_key: wrong_key))\n      }.to raise_error(Puppet::SSL::SSLError,\n                       \"The certificate for 'CN=signed' does not match its private key\")\n    end\n\n    it \"raises if client cert's public key has been replaced\" do\n      expect {\n        subject.create_context(**config.merge(client_cert: cert_fixture('tampered-cert.pem')))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"Invalid signature for certificate 'CN=signed'\")\n    end\n\n    # This option is only available in openssl 1.1\n    # OpenSSL 1.1.1h no longer reports expired root CAs when using \"verify\".\n    # This regression was fixed in 1.1.1i, so only skip this test if we're on\n    # the affected version.\n    # See: https://github.com/openssl/openssl/pull/13585\n    if Puppet::Util::Package.versioncmp(OpenSSL::OPENSSL_LIBRARY_VERSION.split[1], '1.1.1h') != 0\n      it 'raises if root cert signature is invalid', if: defined?(OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE) do\n        ca = global_cacerts.first\n        ca.sign(wrong_key, OpenSSL::Digest::SHA256.new)\n\n        expect {\n          subject.create_context(**config.merge(cacerts: global_cacerts))\n        }.to raise_error(Puppet::SSL::CertVerifyError,\n                         \"Invalid signature for certificate 'CN=Test CA'\")\n      end\n    end\n\n    it 'raises if intermediate CA signature is invalid', unless: Puppet::Util::Platform.jruby? && RUBY_VERSION.to_f >= 2.6 do\n      int = global_cacerts.last\n      int.public_key = wrong_key.public_key if Puppet::Util::Platform.jruby?\n      int.sign(wrong_key, OpenSSL::Digest::SHA256.new)\n\n      expect {\n        subject.create_context(**config.merge(cacerts: global_cacerts))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"Invalid signature for certificate 'CN=Test CA Subauthority'\")\n    end\n\n    it 'raises if CRL signature for root CA is invalid', unless: Puppet::Util::Platform.jruby? do\n      crl = global_crls.first\n      crl.sign(wrong_key, OpenSSL::Digest::SHA256.new)\n\n      expect {\n        subject.create_context(**config.merge(crls: global_crls))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"Invalid signature for CRL issued by 'CN=Test CA'\")\n    end\n\n    it 'raises if CRL signature for intermediate CA is invalid', unless: Puppet::Util::Platform.jruby? do\n      crl = global_crls.last\n      crl.sign(wrong_key, OpenSSL::Digest::SHA256.new)\n\n      expect {\n        subject.create_context(**config.merge(crls: global_crls))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"Invalid signature for CRL issued by 'CN=Test CA Subauthority'\")\n    end\n\n    it 'raises if client cert is revoked' do\n      expect {\n        subject.create_context(**config.merge(private_key: key_fixture('revoked-key.pem'), client_cert: cert_fixture('revoked.pem')))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"Certificate 'CN=revoked' is revoked\")\n    end\n\n    it 'warns if intermediate issuer is missing' do\n      expect(Puppet).to receive(:warning).with(\"The issuer 'CN=Test CA Subauthority' of certificate 'CN=signed' cannot be found locally\")\n\n      subject.create_context(**config.merge(cacerts: [cert_fixture('ca.pem')]))\n    end\n\n    it 'raises if root issuer is missing' do\n      expect {\n        subject.create_context(**config.merge(cacerts: [cert_fixture('intermediate.pem')]))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The issuer 'CN=Test CA' of certificate 'CN=Test CA Subauthority' is missing\")\n    end\n\n    it 'raises if cert is not valid yet', unless: Puppet::Util::Platform.jruby? do\n      client_cert.not_before = Time.now + (5 * 60 * 60)\n      int_key = key_fixture('intermediate-key.pem')\n      client_cert.sign(int_key, OpenSSL::Digest::SHA256.new)\n\n      expect {\n        subject.create_context(**config.merge(client_cert: client_cert))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The certificate 'CN=signed' is not yet valid, verify time is synchronized\")\n    end\n\n    it 'raises if cert is expired', unless: Puppet::Util::Platform.jruby? do\n      client_cert.not_after = Time.at(0)\n      int_key = key_fixture('intermediate-key.pem')\n      client_cert.sign(int_key, OpenSSL::Digest::SHA256.new)\n\n      expect {\n        subject.create_context(**config.merge(client_cert: client_cert))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The certificate 'CN=signed' has expired, verify time is synchronized\")\n    end\n\n    it 'raises if crl is not valid yet', unless: Puppet::Util::Platform.jruby? do\n      future_crls = global_crls\n      # invalidate the CRL issued by the root\n      future_crls.first.last_update = Time.now + (5 * 60 * 60)\n\n      expect {\n        subject.create_context(**config.merge(crls: future_crls))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The CRL issued by 'CN=Test CA' is not yet valid, verify time is synchronized\")\n    end\n\n    it 'raises if crl is expired', unless: Puppet::Util::Platform.jruby? do\n      past_crls = global_crls\n      # invalidate the CRL issued by the root\n      past_crls.first.next_update = Time.at(0)\n\n      expect {\n        subject.create_context(**config.merge(crls: past_crls))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The CRL issued by 'CN=Test CA' has expired, verify time is synchronized\")\n    end\n\n    it 'raises if the root CRL is missing' do\n      crls = [crl_fixture('intermediate-crl.pem')]\n      expect {\n        subject.create_context(**config.merge(crls: crls, revocation: :chain))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The CRL issued by 'CN=Test CA' is missing\")\n    end\n\n    it 'raises if the intermediate CRL is missing' do\n      crls = [crl_fixture('crl.pem')]\n      expect {\n        subject.create_context(**config.merge(crls: crls))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       \"The CRL issued by 'CN=Test CA Subauthority' is missing\")\n    end\n\n    it \"doesn't raise if the root CRL is missing and we're just checking the leaf\" do\n      crls = [crl_fixture('intermediate-crl.pem')]\n      subject.create_context(**config.merge(crls: crls, revocation: :leaf))\n    end\n\n    it \"doesn't raise if the intermediate CRL is missing and revocation checking is disabled\" do\n      crls = [crl_fixture('crl.pem')]\n      subject.create_context(**config.merge(crls: crls, revocation: false))\n    end\n\n    it \"doesn't raise if both CRLs are missing and revocation checking is disabled\" do\n      subject.create_context(**config.merge(crls: [], revocation: false))\n    end\n\n    # OpenSSL < 1.1 does not verify basicConstraints\n    it \"raises if root CA's isCA basic constraint is false\", unless: Puppet::Util::Platform.jruby? || OpenSSL::OPENSSL_VERSION_NUMBER < 0x10100000 do\n      certs = [cert_fixture('bad-basic-constraints.pem'), cert_fixture('intermediate.pem')]\n\n      # openssl 3 returns 79\n      # define X509_V_ERR_NO_ISSUER_PUBLIC_KEY                 24\n      # define X509_V_ERR_INVALID_CA                           79\n      expect {\n        subject.create_context(**config.merge(cacerts: certs, crls: [], revocation: false))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       /Certificate 'CN=Test CA' failed verification \\((24|79)\\): invalid CA certificate/)\n    end\n\n    # OpenSSL < 1.1 does not verify basicConstraints\n    it \"raises if intermediate CA's isCA basic constraint is false\", unless: Puppet::Util::Platform.jruby? || OpenSSL::OPENSSL_VERSION_NUMBER < 0x10100000 do\n      certs = [cert_fixture('ca.pem'), cert_fixture('bad-int-basic-constraints.pem')]\n\n      expect {\n        subject.create_context(**config.merge(cacerts: certs, crls: [], revocation: false))\n      }.to raise_error(Puppet::SSL::CertVerifyError,\n                       /Certificate 'CN=Test CA Subauthority' failed verification \\((24|79)\\): invalid CA certificate/)\n    end\n\n    it 'accepts CA certs in any order' do\n      sslctx = subject.create_context(**config.merge(cacerts: global_cacerts.reverse))\n      # certs in ruby+openssl 1.0.x are not comparable, so compare subjects\n      expect(sslctx.client_chain.map(&:subject).map(&:to_utf8)).to contain_exactly('CN=Test CA', 'CN=Test CA Subauthority', 'CN=signed')\n    end\n\n    it 'accepts CRLs in any order' do\n      sslctx = subject.create_context(**config.merge(crls: global_crls.reverse))\n      # certs in ruby+openssl 1.0.x are not comparable, so compare subjects\n      expect(sslctx.client_chain.map(&:subject).map(&:to_utf8)).to contain_exactly('CN=Test CA', 'CN=Test CA Subauthority', 'CN=signed')\n    end\n\n    it 'raises if the frozen context is modified' do\n      sslctx = subject.create_context(**config)\n      expect {\n        sslctx.verify_peer = false\n      }.to raise_error(/can't modify frozen/)\n    end\n\n    it 'verifies peer' do\n      sslctx = subject.create_context(**config)\n      expect(sslctx.verify_peer).to eq(true)\n    end\n\n    it 'does not trust the system ca store by default' do\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths).never\n\n      subject.create_context(**config)\n    end\n\n    it 'trusts the system ca store' do\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths)\n\n      subject.create_context(**config.merge(include_system_store: true))\n    end\n  end\n\n  context 'when loading an ssl context' do\n    let(:client_cert) { cert_fixture('signed.pem') }\n    let(:private_key) { key_fixture('signed-key.pem') }\n    let(:doesnt_exist) { '/does/not/exist' }\n\n    before :each do\n      Puppet[:localcacert] = file_containing('global_cacerts', global_cacerts.first.to_pem)\n      Puppet[:hostcrl] = file_containing('global_crls', global_crls.first.to_pem)\n\n      Puppet[:certname] = 'signed'\n      Puppet[:privatekeydir] = tmpdir('privatekeydir')\n      File.write(File.join(Puppet[:privatekeydir], 'signed.pem'), private_key.to_pem)\n\n      Puppet[:certdir] = tmpdir('privatekeydir')\n      File.write(File.join(Puppet[:certdir], 'signed.pem'), client_cert.to_pem)\n    end\n\n    it 'raises if CA certs are missing' do\n      Puppet[:localcacert] = doesnt_exist\n\n      expect {\n        subject.load_context\n      }.to raise_error(Puppet::Error, /The CA certificates are missing from/)\n    end\n\n    it 'raises if the CRL is missing' do\n      Puppet[:hostcrl] = doesnt_exist\n\n      expect {\n        subject.load_context\n      }.to raise_error(Puppet::Error, /The CRL is missing from/)\n    end\n\n    it 'does not raise if the CRL is missing and revocation is disabled' do\n      Puppet[:hostcrl] = doesnt_exist\n\n      subject.load_context(revocation: false)\n    end\n\n    it 'raises if the private key is missing' do\n      Puppet[:privatekeydir] = doesnt_exist\n\n      expect {\n        subject.load_context\n      }.to raise_error(Puppet::Error, /The private key is missing from/)\n    end\n\n    it 'raises if the client cert is missing' do\n      Puppet[:certdir] = doesnt_exist\n\n      expect {\n        subject.load_context\n      }.to raise_error(Puppet::Error, /The client certificate is missing from/)\n    end\n\n    context 'loading private keys', unless: RUBY_PLATFORM == 'java' do\n      it 'loads the private key and client cert' do\n        ssl_context = subject.load_context\n\n        expect(ssl_context.private_key).to be_an(OpenSSL::PKey::RSA)\n        expect(ssl_context.client_cert).to be_an(OpenSSL::X509::Certificate)\n      end\n\n      it 'loads a password protected key and client cert' do\n        FileUtils.cp(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'encrypted-key.pem'), File.join(Puppet[:privatekeydir], 'signed.pem'))\n\n        ssl_context = subject.load_context(password: '74695716c8b6')\n\n        expect(ssl_context.private_key).to be_an(OpenSSL::PKey::RSA)\n        expect(ssl_context.client_cert).to be_an(OpenSSL::X509::Certificate)\n      end\n\n      it 'raises if the password is incorrect' do\n        FileUtils.cp(File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'encrypted-key.pem'), File.join(Puppet[:privatekeydir], 'signed.pem'))\n\n        expect {\n          subject.load_context(password: 'wrongpassword')\n        }.to raise_error(Puppet::SSL::SSLError, /Failed to load private key for host 'signed': Could not parse PKey/)\n      end\n    end\n\n    it 'does not trust the system ca store by default' do\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths).never\n\n      subject.load_context\n    end\n\n    it 'trusts the system ca store' do\n      expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths)\n\n      subject.load_context(include_system_store: true)\n    end\n  end\n\n  context 'when verifying requests' do\n    let(:csr) { request_fixture('request.pem') }\n\n    it 'accepts valid requests' do\n      private_key = key_fixture('request-key.pem')\n      expect(subject.verify_request(csr, private_key.public_key)).to eq(csr)\n    end\n\n    it \"raises if the CSR was signed by a private key that doesn't match public key\" do\n      expect {\n        subject.verify_request(csr, wrong_key.public_key)\n      }.to raise_error(Puppet::SSL::SSLError,\n                       \"The CSR for host 'CN=pending' does not match the public key\")\n    end\n\n    it \"raises if the CSR was tampered with\" do\n      csr = request_fixture('tampered-csr.pem')\n      expect {\n        subject.verify_request(csr, csr.public_key)\n      }.to raise_error(Puppet::SSL::SSLError,\n                       \"The CSR for host 'CN=signed' does not match the public key\")\n    end\n  end\n\n  context 'printing' do\n    let(:client_cert) { cert_fixture('signed.pem') }\n    let(:private_key) { key_fixture('signed-key.pem') }\n    let(:config) { { cacerts: global_cacerts, crls: global_crls, client_cert: client_cert, private_key: private_key } }\n\n    it 'prints in debug' do\n      Puppet[:log_level] = 'debug'\n\n      ctx = subject.create_context(**config)\n      subject.print(ctx)\n      expect(@logs.map(&:message)).to include(\n        /Verified CA certificate 'CN=Test CA' fingerprint/,\n        /Verified CA certificate 'CN=Test CA Subauthority' fingerprint/,\n        /Verified client certificate 'CN=signed' fingerprint/,\n        /Using CRL 'CN=Test CA' authorityKeyIdentifier '(keyid:)?[A-Z0-9:]{59}' crlNumber '0'/,\n        /Using CRL 'CN=Test CA Subauthority' authorityKeyIdentifier '(keyid:)?[A-Z0-9:]{59}' crlNumber '0'/\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/state_machine_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\n\nrequire 'puppet/ssl'\n\ndescribe Puppet::SSL::StateMachine, unless: Puppet::Util::Platform.jruby? do\n  include PuppetSpec::Files\n\n  let(:privatekeydir) { tmpdir('privatekeydir') }\n  let(:certdir) { tmpdir('certdir') }\n  let(:requestdir) { tmpdir('requestdir') }\n  let(:machine) { described_class.new }\n  let(:cert_provider) { Puppet::X509::CertProvider.new(privatekeydir: privatekeydir, certdir: certdir, requestdir: requestdir) }\n  let(:ssl_provider) { Puppet::SSL::SSLProvider.new }\n  let(:machine) { described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider) }\n\n  let(:cacert_pem) { cacert.to_pem }\n  let(:cacert) { cert_fixture('ca.pem') }\n  let(:cacerts) { [cacert, cert_fixture('intermediate.pem')] }\n\n  let(:crl_pem) { crl.to_pem }\n  let(:crl) { crl_fixture('crl.pem') }\n  let(:crls) { [crl, crl_fixture('intermediate-crl.pem')] }\n  let(:private_key) { key_fixture('signed-key.pem') }\n  let(:client_cert) { cert_fixture('signed.pem') }\n\n  let(:refused_message) { %r{Connection refused|No connection could be made because the target machine actively refused it} }\n\n  before(:each) do\n    Puppet[:daemonize] = false\n    Puppet[:ssl_lockfile] = tmpfile('ssllock')\n    allow(Kernel).to receive(:sleep)\n    future = Time.now + (5 * 60)\n    allow_any_instance_of(Puppet::X509::CertProvider).to receive(:crl_last_update).and_return(future)\n    allow_any_instance_of(Puppet::X509::CertProvider).to receive(:ca_last_update).and_return(future)\n  end\n\n  def expected_digest(name, content)\n    OpenSSL::Digest.new(name).hexdigest(content)\n  end\n\n  def to_fingerprint(digest)\n    digest.scan(/../).join(':').upcase\n  end\n\n  context 'when passing keyword arguments' do\n    it \"accepts digest\" do\n      expect(described_class.new(digest: 'SHA512').digest).to eq('SHA512')\n    end\n\n    it \"accepts ca_fingerprint\" do\n      expect(described_class.new(ca_fingerprint: 'CAFE').ca_fingerprint).to eq('CAFE')\n    end\n  end\n\n  context 'when ensuring CA certs and CRLs' do\n    it 'returns an SSLContext with the loaded CA certs and CRLs' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n\n      ssl_context = machine.ensure_ca_certificates\n\n      expect(ssl_context[:cacerts]).to eq(cacerts)\n      expect(ssl_context[:crls]).to eq(crls)\n      expect(ssl_context[:verify_peer]).to eq(true)\n    end\n\n    context 'when exceptions occur' do\n      it 'raises in onetime mode' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca})\n          .to_raise(Errno::ECONNREFUSED)\n\n        machine = described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider, onetime: true)\n        expect {\n          machine.ensure_ca_certificates\n        }.to raise_error(Puppet::Error, refused_message)\n      end\n\n      it 'retries CA cert download' do\n        # allow cert to be saved to disk\n        FileUtils.mkdir_p(Puppet[:certdir])\n        allow(cert_provider).to receive(:load_crls).and_return(crls)\n\n        req = stub_request(:get, %r{puppet-ca/v1/certificate/ca})\n                .to_raise(Errno::ECONNREFUSED).then\n                .to_return(status: 200, body: cacert_pem)\n\n        machine.ensure_ca_certificates\n\n        expect(req).to have_been_made.twice\n        expect(@logs).to include(an_object_having_attributes(message: refused_message))\n      end\n\n      it 'retries CRL download' do\n        # allow crl to be saved to disk\n        FileUtils.mkdir_p(Puppet[:ssldir])\n        allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n\n        req = stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca})\n                .to_raise(Errno::ECONNREFUSED).then\n                .to_return(status: 200, body: crl_pem)\n\n        machine.ensure_ca_certificates\n\n        expect(req).to have_been_made.twice\n        expect(@logs).to include(an_object_having_attributes(message: refused_message))\n      end\n    end\n  end\n\n  context 'when ensuring a client cert' do\n    it 'returns an SSLContext with the loaded CA certs, CRLs, private key and client cert' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n      allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n      allow(cert_provider).to receive(:load_client_cert).and_return(client_cert)\n\n      ssl_context = machine.ensure_client_certificate\n\n      expect(ssl_context[:cacerts]).to eq(cacerts)\n      expect(ssl_context[:crls]).to eq(crls)\n      expect(ssl_context[:verify_peer]).to eq(true)\n      expect(ssl_context[:private_key]).to eq(private_key)\n      expect(ssl_context[:client_cert]).to eq(client_cert)\n    end\n\n    it 'uses the specified digest to log the cert chain fingerprints' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n      allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n      allow(cert_provider).to receive(:load_client_cert).and_return(client_cert)\n\n      Puppet[:log_level] = :debug\n      machine = described_class.new(cert_provider: cert_provider, digest: 'SHA512')\n      machine.ensure_client_certificate\n\n      expect(@logs).to include(\n        an_object_having_attributes(message: /Verified CA certificate 'CN=Test CA' fingerprint \\(SHA512\\)/),\n        an_object_having_attributes(message: /Verified CA certificate 'CN=Test CA Subauthority' fingerprint \\(SHA512\\)/),\n        an_object_having_attributes(message: /Verified client certificate 'CN=signed' fingerprint \\(SHA512\\)/)\n      )\n    end\n\n    context 'when exceptions occur' do\n      before :each do\n        allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n        allow(cert_provider).to receive(:load_crls).and_return(crls)\n      end\n\n      it 'retries CSR submission' do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n        allow($stdout).to receive(:puts).with(/Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate/)\n\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}})\n          .to_return(status: 200, body: client_cert.to_pem)\n        # first request raises, second succeeds\n        req = stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}})\n                .to_raise(Errno::ECONNREFUSED).then\n                .to_return(status: 200)\n\n        machine.ensure_client_certificate\n\n        expect(req).to have_been_made.twice\n        expect(@logs).to include(an_object_having_attributes(message: refused_message))\n      end\n\n      it 'retries client cert download' do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n\n        # first request raises, second succeeds\n        req = stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}})\n                .to_raise(Errno::ECONNREFUSED).then\n                .to_return(status: 200, body: client_cert.to_pem)\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)\n\n        machine.ensure_client_certificate\n\n        expect(req).to have_been_made.twice\n        expect(@logs).to include(an_object_having_attributes(message: refused_message))\n      end\n\n      it 'retries when client cert and private key are mismatched' do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n\n        # return mismatched cert the first time, correct cert second time\n        req = stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}})\n                .to_return(status: 200, body: cert_fixture('pluto.pem').to_pem)\n                .to_return(status: 200, body: client_cert.to_pem)\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)\n\n        machine.ensure_client_certificate\n\n        expect(req).to have_been_made.twice\n        expect(@logs).to include(an_object_having_attributes(message: %r{The certificate for 'CN=pluto' does not match its private key}))\n      end\n\n      it 'raises in onetime mode' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}})\n          .to_raise(Errno::ECONNREFUSED)\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}})\n          .to_return(status: 200)\n\n        machine = described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider, onetime: true)\n        expect {\n          machine.ensure_client_certificate\n        }.to raise_error(Puppet::Error, refused_message)\n      end\n    end\n  end\n\n  context 'when locking' do\n    let(:lockfile) { Puppet::Util::Pidlock.new(Puppet[:ssl_lockfile]) }\n    let(:machine) { described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider, lockfile: lockfile) }\n\n    # lockfile is deleted before `ensure_ca_certificates` returns, so\n    # verify lockfile contents while state machine is running\n    def expect_lockfile_to_contain(pid)\n      allow(cert_provider).to receive(:load_cacerts) do\n        expect(File.read(Puppet[:ssl_lockfile])).to eq(pid.to_s)\n      end.and_return(cacerts)\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n    end\n\n    it 'locks the file prior to running the state machine and unlocks when done' do\n      expect(lockfile).to receive(:lock).and_call_original.ordered\n      expect(cert_provider).to receive(:load_cacerts).and_return(cacerts).ordered\n      expect(cert_provider).to receive(:load_crls).and_return(crls).ordered\n      expect(lockfile).to receive(:unlock).ordered\n\n      machine.ensure_ca_certificates\n    end\n\n    it 'deletes the lockfile when finished' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n\n      machine = described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider)\n      machine.ensure_ca_certificates\n\n      expect(File).to_not be_exist(Puppet[:ssl_lockfile])\n    end\n\n    it 'acquires an empty lockfile' do\n      Puppet::FileSystem.touch(Puppet[:ssl_lockfile])\n\n      expect_lockfile_to_contain(Process.pid)\n\n      machine = described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider)\n      machine.ensure_ca_certificates\n    end\n\n    it 'acquires its own lockfile' do\n      File.write(Puppet[:ssl_lockfile], Process.pid.to_s)\n\n      expect_lockfile_to_contain(Process.pid)\n\n      machine = described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider)\n      machine.ensure_ca_certificates\n    end\n\n    it 'overwrites a stale lockfile' do\n      # 2**31 - 1 chosen to not conflict with existing pid\n      File.write(Puppet[:ssl_lockfile], \"2147483647\")\n\n      expect_lockfile_to_contain(Process.pid)\n\n      machine = described_class.new(cert_provider: cert_provider, ssl_provider: ssl_provider)\n      machine.ensure_ca_certificates\n    end\n\n    context 'and another puppet process is running' do\n      let(:now) { Time.now }\n      let(:future) { now + (5 * 60)} # 5 mins in the future\n\n      before :each do\n        allow(lockfile).to receive(:lock).and_return(false)\n      end\n\n      it 'raises a puppet exception' do\n        expect {\n          machine.ensure_ca_certificates\n        }.to raise_error(Puppet::Error, /Another puppet instance is already running and the waitforlock setting is set to 0; exiting/)\n      end\n\n      it 'sleeps and retries successfully' do\n        machine = described_class.new(lockfile: lockfile, cert_provider: cert_provider, waitforlock: 1, maxwaitforlock: 10)\n        allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n        allow(cert_provider).to receive(:load_crls).and_return(crls)\n        allow(Time).to receive(:now).and_return(now, future)\n\n        expect(Kernel).to receive(:sleep).with(1)\n        expect(Puppet).to receive(:info).with(\"Another puppet instance is already running; waiting for it to finish\")\n        expect(Puppet).to receive(:info).with(\"Will try again in 1 seconds.\")\n\n        allow(lockfile).to receive(:lock).and_return(false, true)\n\n        expect(machine.ensure_ca_certificates).to be_an_instance_of(Puppet::SSL::SSLContext)\n      end\n\n      it 'sleeps and retries unsuccessfully until the deadline is exceeded' do\n        machine = described_class.new(lockfile: lockfile, waitforlock: 1, maxwaitforlock: 10)\n        allow(Time).to receive(:now).and_return(now, future)\n\n        expect(Kernel).to receive(:sleep).with(1)\n        expect(Puppet).to receive(:info).with(\"Another puppet instance is already running; waiting for it to finish\")\n        expect(Puppet).to receive(:info).with(\"Will try again in 1 seconds.\")\n\n        allow(lockfile).to receive(:lock).and_return(false)\n        expect {\n          machine.ensure_ca_certificates\n        }.to raise_error(Puppet::Error, /Another puppet instance is already running and the maxwaitforlock timeout has been exceeded; exiting/)\n      end\n\n      it 'defaults the waitlock deadline to 60 seconds' do\n        allow(Time).to receive(:now).and_return(now)\n\n        machine = described_class.new\n        expect(machine.waitlock_deadline).to eq(now.to_i + 60)\n      end\n    end\n  end\n\n  context 'NeedCACerts' do\n    let(:state) { Puppet::SSL::StateMachine::NeedCACerts.new(machine) }\n\n    before :each do\n      Puppet[:localcacert] = tmpfile('needcacerts')\n    end\n\n    it 'transitions to NeedCRLs state' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n\n      expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCRLs)\n    end\n\n    it 'loads existing CA certs' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(cacerts)\n\n      st = state.next_state\n      expect(st.ssl_context[:cacerts]).to eq(cacerts)\n    end\n\n    it 'fetches and saves CA certs' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(nil)\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: cacert_pem)\n\n      st = state.next_state\n      expect(st.ssl_context[:cacerts].map(&:to_pem)).to eq([cacert_pem])\n      expect(File).to be_exist(Puppet[:localcacert])\n    end\n\n    it \"does not verify the server's cert if there are no local CA certs\" do\n      allow(cert_provider).to receive(:load_cacerts).and_return(nil)\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: cacert_pem)\n      allow(cert_provider).to receive(:save_cacerts)\n\n      receive_count = 0\n      allow_any_instance_of(Net::HTTP).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE) { receive_count += 1 }\n\n      state.next_state\n\n      expect(receive_count).to eq(2)\n    end\n\n    it 'returns an Error if the server returns 404' do\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 404)\n\n      st = state.next_state\n      expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n      expect(st.message).to eq(\"CA certificate is missing from the server\")\n    end\n\n    it 'returns an Error if there is a different exception' do\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: [500, 'Internal Server Error'])\n\n      st = state.next_state\n      expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n      expect(st.message).to eq(\"Could not download CA certificate: Internal Server Error\")\n    end\n\n    it 'returns an Error if CA certs are invalid' do\n      allow(cert_provider).to receive(:load_cacerts).and_return(nil)\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: '')\n\n      st = state.next_state\n      expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n      expect(st.error).to be_an_instance_of(OpenSSL::X509::CertificateError)\n    end\n\n    it 'does not save invalid CA certs' do\n      stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: <<~END)\n        -----BEGIN CERTIFICATE-----\n        MIIBpDCCAQ2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\n      END\n\n      state.next_state rescue OpenSSL::X509::CertificateError\n\n      expect(File).to_not exist(Puppet[:localcacert])\n    end\n\n    it 'skips CA refresh if it has not expired' do\n      Puppet[:ca_refresh_interval] = '1y'\n      Puppet::FileSystem.touch(Puppet[:localcacert], mtime: Time.now)\n\n      allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_cacerts).and_return(cacerts)\n\n      # we're expecting a net/http request to never be made\n      state.next_state\n    end\n\n    context 'when verifying CA cert bundle' do\n      before :each do\n        allow(cert_provider).to receive(:load_cacerts).and_return(nil)\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: cacert_pem)\n        allow(cert_provider).to receive(:save_cacerts)\n      end\n\n      it 'verifies CA cert bundle if a ca_fingerprint is given case-insensitively' do\n        Puppet[:log_level] = :info\n\n        digest = expected_digest('SHA256', cacert_pem)\n        fingerprint = to_fingerprint(digest)\n        machine = described_class.new(digest: 'SHA256', ca_fingerprint: digest.downcase)\n        state = Puppet::SSL::StateMachine::NeedCACerts.new(machine)\n        state.next_state\n\n        expect(@logs).to include(an_object_having_attributes(message: \"Verified CA bundle with digest (SHA256) #{fingerprint}\"))\n      end\n\n      it 'verifies CA cert bundle using non-default fingerprint' do\n        Puppet[:log_level] = :info\n\n        digest = expected_digest('SHA512', cacert_pem)\n        machine = described_class.new(digest: 'SHA512', ca_fingerprint: digest)\n        state = Puppet::SSL::StateMachine::NeedCACerts.new(machine)\n        state.next_state\n\n        expect(@logs).to include(an_object_having_attributes(message: \"Verified CA bundle with digest (SHA512) #{to_fingerprint(digest)}\"))\n      end\n\n      it 'returns an error if verification fails' do\n        machine = described_class.new(digest: 'SHA256', ca_fingerprint: 'wrong!')\n        state = Puppet::SSL::StateMachine::NeedCACerts.new(machine)\n\n        fingerprint = to_fingerprint(expected_digest('SHA256', cacert_pem))\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to eq(\"CA bundle with digest (SHA256) #{fingerprint} did not match expected digest WR:ON:G!\")\n      end\n    end\n\n    context 'when refreshing a CA bundle' do\n      before :each do\n        Puppet[:ca_refresh_interval] = '1s'\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_cacerts).and_return(cacerts)\n\n        yesterday = Time.now - (24 * 60 * 60)\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:ca_last_update).and_return(yesterday)\n      end\n\n      let(:new_ca_bundle) do\n        # add 'unknown' cert to the bundle\n        [cacert, cert_fixture('intermediate.pem'), cert_fixture('unknown-ca.pem')].map(&:to_pem)\n      end\n\n      it 'uses the local CA if it has not been modified' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 304)\n\n        expect(state.next_state.ssl_context.cacerts).to eq(cacerts)\n      end\n\n      it 'uses the local CA if refreshing fails in HTTP layer' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 503)\n\n        expect(state.next_state.ssl_context.cacerts).to eq(cacerts)\n      end\n\n      it 'uses the local CA if refreshing fails in TCP layer' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_raise(Errno::ECONNREFUSED)\n\n        expect(state.next_state.ssl_context.cacerts).to eq(cacerts)\n      end\n\n      it 'uses the updated crl for the future requests' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: new_ca_bundle.join)\n\n        expect(state.next_state.ssl_context.cacerts.map(&:to_pem)).to eq(new_ca_bundle)\n      end\n\n      it 'updates the `last_update` time on successful CA refresh' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: new_ca_bundle.join)\n\n        expect_any_instance_of(Puppet::X509::CertProvider).to receive(:ca_last_update=).with(be_within(60).of(Time.now))\n\n        state.next_state\n      end\n\n      it \"does not update the `last_update` time when CA refresh fails\" do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_raise(Errno::ECONNREFUSED)\n\n        expect_any_instance_of(Puppet::X509::CertProvider).to receive(:ca_last_update=).never\n\n        state.next_state\n      end\n\n      it 'forces the NeedCRLs to refresh' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: new_ca_bundle.join)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCRLs)\n        expect(st.force_crl_refresh).to eq(true)\n      end\n    end\n  end\n\n  context 'NeedCRLs' do\n    let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts)}\n    let(:state) { Puppet::SSL::StateMachine::NeedCRLs.new(machine, ssl_context) }\n\n    before :each do\n      Puppet[:hostcrl] = tmpfile('needcrls')\n    end\n\n    it 'transitions to NeedKey state' do\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n\n      expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedKey)\n    end\n\n    it 'loads existing CRLs' do\n      allow(cert_provider).to receive(:load_crls).and_return(crls)\n\n      st = state.next_state\n      expect(st.ssl_context[:crls]).to eq(crls)\n    end\n\n    it 'fetches and saves CRLs' do\n      allow(cert_provider).to receive(:load_crls).and_return(nil)\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_pem)\n\n      st = state.next_state\n      expect(st.ssl_context[:crls].map(&:to_pem)).to eq([crl_pem])\n      expect(File).to be_exist(Puppet[:hostcrl])\n    end\n\n    it \"verifies the server's certificate when fetching the CRL\" do\n      allow(cert_provider).to receive(:load_crls).and_return(nil)\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_pem)\n      allow(cert_provider).to receive(:save_crls)\n\n      receive_count = 0\n      allow_any_instance_of(Net::HTTP).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) { receive_count += 1 }\n\n      state.next_state\n\n      expect(receive_count).to eq(2)\n    end\n\n    it 'returns an Error if the server returns 404' do\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 404)\n\n      st = state.next_state\n      expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n      expect(st.message).to eq(\"CRL is missing from the server\")\n    end\n\n    it 'returns an Error if there is a different exception' do\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: [500, 'Internal Server Error'])\n\n      st = state.next_state\n      expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n      expect(st.message).to eq(\"Could not download CRLs: Internal Server Error\")\n    end\n\n    it 'returns an Error if CRLs are invalid' do\n      allow(cert_provider).to receive(:load_crls).and_return(nil)\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: '')\n\n      st = state.next_state\n      expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n      expect(st.error).to be_an_instance_of(OpenSSL::X509::CRLError)\n    end\n\n    it 'does not save invalid CRLs' do\n      stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: <<~END)\n        -----BEGIN X509 CRL-----\n        MIIBCjB1AgEBMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EXDTcw\n      END\n\n      state.next_state rescue OpenSSL::X509::CRLError\n\n      expect(File).to_not exist(Puppet[:hostcrl])\n    end\n\n    it 'skips CRL download when revocation is disabled' do\n      Puppet[:certificate_revocation] = false\n\n      expect(cert_provider).not_to receive(:load_crls)\n\n      state.next_state\n\n      expect(File).to_not exist(Puppet[:hostcrl])\n    end\n\n    it 'skips CRL refresh if it has not expired' do\n      Puppet[:crl_refresh_interval] = '1y'\n      Puppet::FileSystem.touch(Puppet[:hostcrl], mtime: Time.now)\n\n      allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_crls).and_return(crls)\n\n      # we're expecting a net/http request to never be made\n      state.next_state\n    end\n\n    context 'when refreshing a CRL' do\n      before :each do\n        Puppet[:crl_refresh_interval] = '1s'\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:load_crls).and_return(crls)\n\n        yesterday = Time.now - (24 * 60 * 60)\n        allow_any_instance_of(Puppet::X509::CertProvider).to receive(:crl_last_update).and_return(yesterday)\n      end\n\n      let(:new_crl_bundle) do\n        # add intermediate crl to the bundle\n        int_crl = crl_fixture('intermediate-crl.pem')\n        [crl, int_crl].map(&:to_pem)\n      end\n\n      it 'uses the local crl if it has not been modified' do\n        stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 304)\n\n        expect(state.next_state.ssl_context.crls).to eq(crls)\n      end\n\n      it 'uses the local crl if refreshing fails in HTTP layer' do\n        stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 503)\n\n        expect(state.next_state.ssl_context.crls).to eq(crls)\n      end\n\n      it 'uses the local crl if refreshing fails in TCP layer' do\n        stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_raise(Errno::ECONNREFUSED)\n\n        expect(state.next_state.ssl_context.crls).to eq(crls)\n      end\n\n      it 'uses the updated crl for the future requests' do\n        stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: new_crl_bundle.join)\n\n        expect(state.next_state.ssl_context.crls.map(&:to_pem)).to eq(new_crl_bundle)\n      end\n\n      it 'updates the `last_update` time' do\n        stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: new_crl_bundle.join)\n\n        expect_any_instance_of(Puppet::X509::CertProvider).to receive(:crl_last_update=).with(be_within(60).of(Time.now))\n\n        state.next_state\n      end\n    end\n  end\n\n  context 'when ensuring a client cert' do\n    context 'in state NeedKey' do\n      let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, crls: crls)}\n      let(:state) { Puppet::SSL::StateMachine::NeedKey.new(machine, ssl_context) }\n\n      it 'loads an existing private key and passes it to the next state' do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedSubmitCSR)\n        expect(st.private_key).to eq(private_key)\n      end\n\n      it 'loads a matching private key and cert' do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n        allow(cert_provider).to receive(:load_client_cert).and_return(client_cert)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::Done)\n      end\n\n      it 'raises if the client cert is mismatched' do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n        allow(cert_provider).to receive(:load_client_cert).and_return(cert_fixture('tampered-cert.pem'))\n\n        ssl_context = Puppet::SSL::SSLContext.new(cacerts: [cacert], crls: [crl])\n        state = Puppet::SSL::StateMachine::NeedKey.new(machine, ssl_context)\n        expect {\n          state.next_state\n        }.to raise_error(Puppet::SSL::SSLError, %r{The certificate for 'CN=signed' does not match its private key})\n      end\n\n      it 'generates a new RSA private key, saves it and passes it to the next state' do\n        allow(cert_provider).to receive(:load_private_key).and_return(nil)\n        expect(cert_provider).to receive(:save_private_key)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedSubmitCSR)\n        expect(st.private_key).to be_instance_of(OpenSSL::PKey::RSA)\n        expect(st.private_key).to be_private\n      end\n\n      it 'generates a new EC private key, saves it and passes it to the next state' do\n        Puppet[:key_type] = 'ec'\n        allow(cert_provider).to receive(:load_private_key).and_return(nil)\n        expect(cert_provider).to receive(:save_private_key)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedSubmitCSR)\n        expect(st.private_key).to be_instance_of(OpenSSL::PKey::EC)\n        expect(st.private_key).to be_private\n        expect(st.private_key.group.curve_name).to eq('prime256v1')\n      end\n\n      it 'generates a new EC private key with curve `secp384r1`, saves it and passes it to the next state' do\n        Puppet[:key_type] = 'ec'\n        Puppet[:named_curve] = 'secp384r1'\n        allow(cert_provider).to receive(:load_private_key).and_return(nil)\n        expect(cert_provider).to receive(:save_private_key)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedSubmitCSR)\n        expect(st.private_key).to be_instance_of(OpenSSL::PKey::EC)\n        expect(st.private_key).to be_private\n        expect(st.private_key.group.curve_name).to eq('secp384r1')\n      end\n\n      it 'raises if the named curve is unsupported' do\n        Puppet[:key_type] = 'ec'\n        Puppet[:named_curve] = 'infiniteloop'\n        allow(cert_provider).to receive(:load_private_key).and_return(nil)\n\n        expect {\n          state.next_state\n        }.to raise_error(OpenSSL::PKey::ECError, /(invalid|unknown) curve name/)\n      end\n\n      it 'raises an error if it fails to load the key' do\n        allow(cert_provider).to receive(:load_private_key).and_raise(OpenSSL::PKey::RSAError)\n\n        expect {\n          state.next_state\n        }.to raise_error(OpenSSL::PKey::RSAError)\n      end\n\n      it \"transitions to Done if current time plus renewal interval is less than cert's \\\"NotAfter\\\" time\" do\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n        allow(cert_provider).to receive(:load_client_cert).and_return(client_cert)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::Done)\n      end\n\n      it \"returns NeedRenewedCert if current time plus renewal interval is greater than cert's \\\"NotAfter\\\" time\" do\n        client_cert.not_after=(Time.now + 300)\n        allow(cert_provider).to receive(:load_private_key).and_return(private_key)\n        allow(cert_provider).to receive(:load_client_cert).and_return(client_cert)\n\n        ssl_context = Puppet::SSL::SSLContext.new(cacerts: [cacert], client_cert: client_cert, crls: [crl])\n        state = Puppet::SSL::StateMachine::NeedKey.new(machine, ssl_context)\n\n        st = state.next_state\n        expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedRenewedCert)\n      end\n    end\n\n    context 'in state NeedSubmitCSR' do\n      let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, crls: crls)}\n      let(:state) { Puppet::SSL::StateMachine::NeedSubmitCSR.new(machine, ssl_context, private_key) }\n\n      def write_csr_attributes(data)\n        file_containing('state_machine_csr', YAML.dump(data))\n      end\n\n      before :each do\n        allow(cert_provider).to receive(:save_request)\n      end\n\n      it 'submits the CSR and transitions to NeedCert' do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)\n      end\n\n      it 'saves the CSR and transitions to NeedCert' do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)\n\n        expect(cert_provider).to receive(:save_request).with(Puppet[:certname], instance_of(OpenSSL::X509::Request))\n\n        state.next_state\n      end\n\n      it 'includes DNS alt names' do\n        Puppet[:dns_alt_names] = \"one,IP:192.168.0.1,DNS:two.com\"\n\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).with do |request|\n          csr = Puppet::SSL::CertificateRequest.from_instance(OpenSSL::X509::Request.new(request.body))\n          expect(\n            csr.subject_alt_names\n          ).to contain_exactly('DNS:one', 'IP Address:192.168.0.1', 'DNS:two.com', \"DNS:#{Puppet[:certname]}\")\n        end.to_return(status: 200)\n\n        state.next_state\n      end\n\n      it 'includes CSR attributes', :unless => RUBY_PLATFORM == 'java' do\n        Puppet[:csr_attributes] = write_csr_attributes(\n          'custom_attributes' => {\n              '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',\n              '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info'\n            }\n        )\n\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).with do |request|\n          csr = Puppet::SSL::CertificateRequest.from_instance(OpenSSL::X509::Request.new(request.body))\n          expect(\n            csr.custom_attributes\n          ).to contain_exactly(\n                 {'oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'CSR specific info'},\n                 {'oid' => '1.3.6.1.4.1.34380.1.2.2', 'value' => 'more CSR specific info'},\n                 {'oid' => 'pp_auth_auto_renew', 'value' => 'true'}\n               )\n        end.to_return(status: 200)\n\n        state.next_state\n      end\n\n      it 'includes CSR extension requests' do\n        Puppet[:csr_attributes] = write_csr_attributes(\n          {\n            'extension_requests' => {\n              '1.3.6.1.4.1.34380.1.1.31415' => 'pi',\n              '1.3.6.1.4.1.34380.1.1.2718'  => 'e',\n            }\n          }\n        )\n\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).with do |request|\n          csr = Puppet::SSL::CertificateRequest.from_instance(OpenSSL::X509::Request.new(request.body))\n          expect(\n            csr.request_extensions\n          ).to contain_exactly(\n                 {'oid' => '1.3.6.1.4.1.34380.1.1.31415', 'value' => 'pi'},\n                 {'oid' => '1.3.6.1.4.1.34380.1.1.2718', 'value' => 'e'}\n               )\n        end.to_return(status: 200)\n\n        state.next_state\n      end\n\n      it 'transitions to NeedCert if the server has a requested certificate' do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 400, body: \"#{Puppet[:certname]} already has a requested certificate\")\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)\n      end\n\n      it 'transitions to NeedCert if the server has a signed certificate' do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 400, body: \"#{Puppet[:certname]} already has a signed certificate\")\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)\n      end\n\n      it 'transitions to NeedCert if the server has a revoked certificate' do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 400, body: \"#{Puppet[:certname]} already has a revoked certificate\")\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)\n      end\n\n      it 'raises if the server errors' do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 500)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to eq(\"Failed to submit the CSR, HTTP response was 500\")\n      end\n\n      it \"verifies the server's certificate when submitting the CSR\" do\n        stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)\n\n        receive_count = 0\n        allow_any_instance_of(Net::HTTP).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) { receive_count += 1 }\n\n        state.next_state\n\n        expect(receive_count).to eq(2)\n      end\n    end\n\n    context 'in state NeedCert' do\n      let(:ca_chain) { [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')] }\n      let(:crl_chain) { [crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem')] }\n      let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: ca_chain, crls: crl_chain)}\n      let(:state) { Puppet::SSL::StateMachine::NeedCert.new(machine, ssl_context, private_key) }\n\n      it 'transitions to Done if the cert is signed and matches our private key' do\n        allow(cert_provider).to receive(:save_client_cert)\n        allow(cert_provider).to receive(:save_request)\n\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: client_cert.to_pem)\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::Done)\n      end\n\n      it \"prints a message if the cert isn't signed yet\" do\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 404)\n\n        expect {\n          state.next_state\n        }.to output(/Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate \\(#{Puppet[:certname]}\\)/).to_stdout\n      end\n\n      it 'transitions to Error if the cert does not match our private key' do\n        wrong_cert = cert_fixture('127.0.0.1.pem')\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: wrong_cert.to_pem)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to eq(\"The certificate for 'CN=127.0.0.1' does not match its private key\")\n      end\n\n      it 'transitions to Wait if the server returns non-200' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 404)\n\n        allow($stdout).to receive(:puts).with(/Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate/)\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::Wait)\n      end\n\n      it 'transitions to Error if the server returns 500' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 500)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to match(/Failed to retrieve certificate/)\n      end\n\n      it \"verifies the server's certificate when getting the client cert\" do\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: client_cert.to_pem)\n        allow(cert_provider).to receive(:save_client_cert)\n        allow(cert_provider).to receive(:save_request)\n\n        receive_count = 0\n        allow_any_instance_of(Net::HTTP).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) { receive_count += 1 }\n\n        state.next_state\n\n        expect(receive_count).to eq(2)\n      end\n\n      it 'does not save an invalid client cert' do\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: <<~END)\n          -----BEGIN CERTIFICATE-----\n          MIIBpDCCAQ2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\n        END\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to match(/Failed to parse certificate:/)\n        expect(File).to_not exist(Puppet[:hostcert])\n      end\n\n      it 'does not save a mismatched client cert' do\n        wrong_cert = cert_fixture('127.0.0.1.pem').to_pem\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: wrong_cert)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to eq(\"The certificate for 'CN=127.0.0.1' does not match its private key\")\n        expect(File).to_not exist(Puppet[:hostcert])\n      end\n\n      it 'does not save a revoked client cert' do\n        revoked_cert = cert_fixture('revoked.pem').to_pem\n        stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: revoked_cert)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Error)\n        expect(st.message).to eq(\"Certificate 'CN=revoked' is revoked\")\n        expect(File).to_not exist(Puppet[:hostcert])\n      end\n    end\n\n    context 'in state Wait' do\n      let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, crls: crls)}\n\n      it 'exits with 1 if waitforcert is 0' do\n        machine = described_class.new(waitforcert: 0)\n\n        expect {\n          expect {\n            Puppet::SSL::StateMachine::Wait.new(machine).next_state\n          }.to exit_with(1)\n        }.to output(/Exiting now because the waitforcert setting is set to 0./).to_stdout\n      end\n\n      it 'sleeps and transitions to NeedLock' do\n        machine = described_class.new(waitforcert: 15)\n\n        state = Puppet::SSL::StateMachine::Wait.new(machine)\n        expect(Kernel).to receive(:sleep).with(15)\n\n        expect(Puppet).to receive(:info).with(/Will try again in 15 seconds./)\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedLock)\n      end\n\n      it 'sleeps and transitions to NeedLock when maxwaitforcert is set' do\n        machine = described_class.new(waitforcert: 15, maxwaitforcert: 30)\n\n        state = Puppet::SSL::StateMachine::Wait.new(machine)\n        expect(Kernel).to receive(:sleep).with(15)\n\n        expect(Puppet).to receive(:info).with(/Will try again in 15 seconds./)\n\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedLock)\n      end\n\n      it 'waits indefinitely by default' do\n        machine = described_class.new\n        expect(machine.wait_deadline).to eq(Float::INFINITY)\n      end\n\n      it 'exits with 1 if maxwaitforcert is exceeded' do\n        machine = described_class.new(maxwaitforcert: 1)\n\n        # 5 minutes in the future\n        future = Time.now + (5 * 60)\n        allow(Time).to receive(:now).and_return(future)\n\n        expect {\n          expect {\n            Puppet::SSL::StateMachine::Wait.new(machine).next_state\n          }.to exit_with(1)\n        }.to output(/Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate \\(.*\\). Exiting now because the maxwaitforcert timeout has been exceeded./).to_stdout\n      end\n\n      it 'closes the pool before sleeping' do\n        machine = described_class.new(waitforcert: 15)\n\n        state = Puppet::SSL::StateMachine::Wait.new(machine)\n        expect(Puppet.runtime[:http].pool).to receive(:close).and_call_original\n        expect(Kernel).to receive(:sleep).with(15).ordered\n\n        state.next_state\n      end\n\n      it 'releases the lock while sleeping' do\n        lockfile = Puppet::Util::Pidlock.new(Puppet[:ssl_lockfile])\n        machine = described_class.new(lockfile: lockfile)\n        state = Puppet::SSL::StateMachine::Wait.new(machine)\n\n        # pidlock should be unlocked while sleeping\n        allow(Kernel).to receive(:sleep) do\n          expect(lockfile).to_not be_locked\n        end\n\n        # lock before running the state\n        lockfile.lock\n        state.next_state\n      end\n    end\n\n    context 'in state NeedLock' do\n      let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: []) }\n      let(:lockfile) { Puppet::Util::Pidlock.new(Puppet[:ssl_lockfile]) }\n      let(:machine) { described_class.new(lockfile: lockfile) }\n      let(:state) { Puppet::SSL::StateMachine::NeedLock.new(machine) }\n\n      it 'acquires the lock and transitions to NeedCACerts' do\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCACerts)\n        expect(lockfile).to be_locked\n      end\n\n      it 'transitions to LockFailure if it fails to acquire the lock' do\n        expect(lockfile).to receive(:lock).and_return(false)\n        expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::LockFailure)\n      end\n    end\n\n    context 'in state NeedRenewedCert' do\n      before :each do\n        client_cert.not_after=(Time.now + 300)\n      end\n\n      let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, client_cert: client_cert, crls: crls,)}\n      let(:state) { Puppet::SSL::StateMachine::NeedRenewedCert.new(machine, ssl_context, private_key) }\n      let(:renewed_cert) { cert_fixture('renewed.pem') }\n\n      it 'returns Done with renewed cert when successful' do\n        allow(cert_provider).to receive(:save_client_cert)\n        stub_request(:post, %r{puppet-ca/v1/certificate_renewal}).to_return(status: 200, body: renewed_cert.to_pem)\n\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Done)\n        expect(st.ssl_context[:client_cert]).to eq(renewed_cert)\n      end\n\n      it 'logs a warning message when failing with a non-404 status' do\n        stub_request(:post, %r{puppet-ca/v1/certificate_renewal}).to_return(status: 400, body: 'Failed to automatically renew certificate: 400 Bad request')\n\n        expect(Puppet).to receive(:warning).with(/Failed to automatically renew certificate/)\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Done)\n      end\n\n      it 'logs an info message when failing with 404' do\n        stub_request(:post, %r{puppet-ca/v1/certificate_renewal}).to_return(status: 404, body: 'Certificate autorenewal has not been enabled on the server.')\n\n        expect(Puppet).to receive(:info).with('Certificate autorenewal has not been enabled on the server.')\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Done)\n      end\n\n      it 'logs a warning message when failing with no HTTP status' do\n        stub_request(:post, %r{puppet-ca/v1/certificate_renewal}).to_raise(Errno::ECONNREFUSED)\n\n        expect(Puppet).to receive(:warning).with(/Unable to automatically renew certificate:/)\n        st = state.next_state\n        expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Done)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/ssl/verifier_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::SSL::Verifier do\n  let(:options) { {} }\n  let(:ssl_context) { Puppet::SSL::SSLContext.new(options) }\n  let(:host) { 'example.com' }\n  let(:http) { Net::HTTP.new(host) }\n  let(:verifier) { described_class.new(host, ssl_context) }\n\n  context '#reusable?' do\n    it 'Verifiers with the same ssl_context are reusable' do\n      expect(verifier).to be_reusable(described_class.new(host, ssl_context))\n    end\n\n    it 'Verifiers with different ssl_contexts are not reusable' do\n      expect(verifier).to_not be_reusable(described_class.new(host, Puppet::SSL::SSLContext.new))\n    end\n  end\n\n  context '#setup_connection' do\n    it 'copies parameters from the ssl_context to the connection' do\n      store = double('store')\n      options.merge!(store: store)\n      verifier.setup_connection(http)\n\n      expect(http.cert_store).to eq(store)\n    end\n\n    it 'defaults to VERIFY_PEER' do\n      expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)\n\n      verifier.setup_connection(http)\n    end\n\n    it 'only uses VERIFY_NONE if explicitly disabled' do\n      options.merge!(verify_peer: false)\n\n      expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)\n\n      verifier.setup_connection(http)\n    end\n\n    it 'registers a verify callback' do\n      verifier.setup_connection(http)\n\n      expect(http.verify_callback).to eq(verifier)\n    end\n  end\n\n  context '#handle_connection_error' do\n    let(:peer_cert) { cert_fixture('127.0.0.1.pem') }\n    let(:chain) { [peer_cert] }\n    let(:ssl_error) { OpenSSL::SSL::SSLError.new(\"certificate verify failed\") }\n\n    it \"raises a verification error for a CA cert\" do\n      store_context = double('store_context', current_cert: peer_cert, chain: [peer_cert], error: OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, error_string: \"unable to get local issuer certificate\")\n      verifier.call(false, store_context)\n\n      expect {\n        verifier.handle_connection_error(http, ssl_error)\n      }.to raise_error(Puppet::SSL::CertVerifyError, \"certificate verify failed [unable to get local issuer certificate for CN=127.0.0.1]\")\n    end\n\n    it \"raises a verification error for the server cert\" do\n      store_context = double('store_context', current_cert: peer_cert, chain: chain, error: OpenSSL::X509::V_ERR_CERT_REJECTED, error_string: \"certificate rejected\")\n      verifier.call(false, store_context)\n\n      expect {\n        verifier.handle_connection_error(http, ssl_error)\n      }.to raise_error(Puppet::SSL::CertVerifyError, \"certificate verify failed [certificate rejected for CN=127.0.0.1]\")\n    end\n\n    it \"raises cert mismatch error on ruby < 2.4\" do\n      expect(http).to receive(:peer_cert).and_return(peer_cert)\n\n      store_context = double('store_context')\n      verifier.call(true, store_context)\n\n      ssl_error = OpenSSL::SSL::SSLError.new(\"hostname 'example'com' does not match the server certificate\")\n\n      expect {\n        verifier.handle_connection_error(http, ssl_error)\n      }.to raise_error(Puppet::Error, \"Server hostname 'example.com' did not match server certificate; expected one of 127.0.0.1, DNS:127.0.0.1, DNS:127.0.0.2\")\n    end\n\n    it \"raises cert mismatch error on ruby >= 2.4\" do\n      store_context = double('store_context', current_cert: peer_cert, chain: chain, error: OpenSSL::X509::V_OK, error_string: \"ok\")\n      verifier.call(false, store_context)\n\n      expect {\n        verifier.handle_connection_error(http, ssl_error)\n      }.to raise_error(Puppet::Error, \"Server hostname 'example.com' did not match server certificate; expected one of 127.0.0.1, DNS:127.0.0.1, DNS:127.0.0.2\")\n    end\n\n    it 're-raises other ssl connection errors' do\n      err = OpenSSL::SSL::SSLError.new(\"This version of OpenSSL does not support FIPS mode\")\n      expect {\n        verifier.handle_connection_error(http, err)\n      }.to raise_error(err)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/task_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/files'\nrequire 'puppet_spec/modules'\nrequire 'puppet/module/task'\n\ndescribe Puppet::Module::Task do\n  include PuppetSpec::Files\n\n  let(:modpath) { tmpdir('task_modpath') }\n  let(:mymodpath) { File.join(modpath, 'mymod') }\n  let(:othermodpath) { File.join(modpath, 'othermod') }\n  let(:mymod) { Puppet::Module.new('mymod', mymodpath, nil) }\n  let(:othermod) { Puppet::Module.new('othermod', othermodpath, nil) }\n  let(:tasks_path) { File.join(mymodpath, 'tasks') }\n  let(:other_tasks_path) { File.join(othermodpath, 'tasks') }\n  let(:tasks_glob) { File.join(mymodpath, 'tasks', '*') }\n\n  it \"cannot construct tasks with illegal names\" do\n    expect { Puppet::Module::Task.new(mymod, \"iLegal\", []) }\n      .to raise_error(Puppet::Module::Task::InvalidName,\n                      \"Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores\")\n  end\n\n  it \"constructs tasks as expected when every task has a metadata file with the same name (besides extension)\" do\n    task_files = %w{task1.json task1 task2.json task2.exe task3.json task3.sh}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(task_files)\n    task_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({})\n\n    expect(tasks.count).to eq(3)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1 mymod::task2 mymod::task3})\n    expect(tasks.map{|t| t.metadata_file}).to eq([\"#{tasks_path}/task1.json\",\n                                                  \"#{tasks_path}/task2.json\",\n                                                  \"#{tasks_path}/task3.json\"])\n    expect(tasks.map{|t| t.files.map { |f| f[\"path\"] } }).to eq([[\"#{tasks_path}/task1\"],\n                                                                 [\"#{tasks_path}/task2.exe\"],\n                                                                 [\"#{tasks_path}/task3.sh\"]])\n    expect(tasks.map{|t| t.files.map { |f| f[\"name\"] } }).to eq([[\"task1\"],\n                                                                 [\"task2.exe\"],\n                                                                 [\"task3.sh\"]])\n\n    tasks.map{|t| t.metadata_file}.each do |metadata_file|\n      expect(metadata_file).to eq(File.absolute_path(metadata_file))\n    end\n\n    tasks.map{|t| t.files}.each do |file_data|\n      path = file_data[0]['path']\n      expect(path).to eq(File.absolute_path(path))\n    end\n  end\n\n  it \"constructs tasks as expected when some tasks don't have a metadata file\" do\n    task_files = %w{task1 task2.exe task3.json task3.sh}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(task_files)\n    task_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({})\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n    expect(tasks.count).to eq(3)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1 mymod::task2 mymod::task3})\n    expect(tasks.map{|t| t.metadata_file}).to eq([nil, nil, \"#{tasks_path}/task3.json\"])\n    expect(tasks.map{|t| t.files.map { |f| f[\"path\"] } }).to eq([[\"#{tasks_path}/task1\"],\n                                          [\"#{tasks_path}/task2.exe\"],\n                                          [\"#{tasks_path}/task3.sh\"]])\n  end\n\n  it \"constructs a task as expected when a task has implementations\" do\n    task_files = %w{task1.elf task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(task_files)\n    task_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'implementations' => [{\"name\" => \"task1.sh\"}]})\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1})\n    expect(tasks.map{|t| t.metadata_file}).to eq([\"#{tasks_path}/task1.json\"])\n    expect(tasks.map{|t| t.files.map{ |f| f[\"path\"] } }).to eq([[\"#{tasks_path}/task1.sh\"]])\n  end\n\n  it \"constructs a task as expected when task metadata declares additional files\" do\n    task_files = %w{task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(task_files)\n    task_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    expect(Puppet::Module::Task).to receive(:find_extra_files).and_return([{'name' => 'mymod/lib/file0.elf', 'path' => \"/path/to/file0.elf\"}])\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'files' => [\"mymod/lib/file0.elf\"]})\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1})\n    expect(tasks.map{|t| t.metadata_file}).to eq([\"#{tasks_path}/task1.json\"])\n    expect(tasks.map{|t| t.files.map{ |f| f[\"path\"] } }).to eq([[\"#{tasks_path}/task1.sh\", \"/path/to/file0.elf\"]])\n  end\n\n  it \"constructs a task as expected when a task implementation declares additional files\" do\n    task_files = %w{task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(task_files)\n    task_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    expect(Puppet::Module::Task).to receive(:find_extra_files).and_return([{'name' => 'mymod/lib/file0.elf', 'path' => \"/path/to/file0.elf\"}])\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'implementations' => [{\"name\" => \"task1.sh\", \"files\" => [\"mymod/lib/file0.elf\"]}]})\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1})\n    expect(tasks.map{|t| t.metadata_file}).to eq([\"#{tasks_path}/task1.json\"])\n    expect(tasks.map{|t| t.files.map{ |f| f[\"path\"] } }).to eq([[\"#{tasks_path}/task1.sh\", \"/path/to/file0.elf\"]])\n  end\n\n  it \"constructs a task as expected when task metadata and a task implementation both declare additional files\" do\n    task_files = %w{task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(task_files)\n    task_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    expect(Puppet::Module::Task).to receive(:find_extra_files).and_return([\n      {'name' => 'mymod/lib/file0.elf', 'path' => \"/path/to/file0.elf\"},\n      {'name' => 'yourmod/files/file1.txt', 'path' => \"/other/path/to/file1.txt\"}\n    ])\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'implementations' => [{\"name\" => \"task1.sh\", \"files\" => [\"mymod/lib/file0.elf\"]}]})\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1})\n    expect(tasks.map{|t| t.metadata_file}).to eq([\"#{tasks_path}/task1.json\"])\n    expect(tasks.map{|t| t.files.map{ |f| f[\"path\"] } }).to eq([[\n      \"#{tasks_path}/task1.sh\",\n      \"/path/to/file0.elf\",\n      \"/other/path/to/file1.txt\"\n    ]])\n  end\n\n  it \"constructs a task as expected when a task has files\" do\n    og_files = %w{task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n    og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    expect(File).to receive(:exist?).with(any_args).and_return(true).at_least(:once)\n\n    expect(Puppet::Module).to receive(:find).with(othermod.name, \"production\").and_return(othermod).at_least(:once)\n    short_files = %w{other_task.sh other_task.json task_2.sh}.map { |bn| \"#{othermod.name}/tasks/#{bn}\" }\n    long_files = %w{other_task.sh other_task.json task_2.sh}.map { |bn| \"#{other_tasks_path}/#{bn}\" }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'files' => short_files})\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.files.map{ |f| f[\"path\"] } }).to eq([[\"#{tasks_path}/task1.sh\"] + long_files])\n  end\n\n  it \"fails to load a task if its metadata specifies a non-existent file\" do\n    og_files = %w{task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    allow(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n    og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    allow(File).to receive(:exist?).with(any_args).and_return(true)\n\n    expect(Puppet::Module).to receive(:find).with(othermod.name, \"production\").and_return(nil).at_least(:once)\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n    allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'files' => [\"#{othermod.name}/files/test\"]})\n\n    expect { tasks.first.files }.to raise_error(Puppet::Module::Task::InvalidMetadata, /Could not find module #{othermod.name} containing task file test/)\n  end\n\n  it \"finds files whose names (besides extensions) are valid task names\" do\n    og_files = %w{task task_1 xx_t_a_s_k_2_xx}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n    og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n    expect(tasks.count).to eq(3)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::task mymod::task_1 mymod::xx_t_a_s_k_2_xx})\n  end\n\n  it \"ignores files that have names (besides extensions) that are not valid task names\" do\n    og_files = %w{.nottask.exe .wat !runme _task 2task2furious def_a_task_PSYCH Fake_task not-a-task realtask}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n    og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::realtask})\n  end\n\n  it \"ignores files that have names ending in .conf and .md\" do\n    og_files = %w{ginuwine_task task.conf readme.md other_task.md}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n    og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n    expect(tasks.count).to eq(1)\n    expect(tasks.map{|t| t.name}).to eq(%w{mymod::ginuwine_task})\n  end\n\n  it \"ignores files which are not regular files\" do\n    og_files = %w{foo}.map { |bn| \"#{tasks_path}/#{bn}\" }\n    expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n    og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(false) }\n    tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n    expect(tasks.count).to eq(0)\n  end\n\n  it \"gives the 'init' task a name that is just the module's name\" do\n    expect(Puppet::Module::Task.new(mymod, 'init', [\"#{tasks_path}/init.sh\"]).name).to eq('mymod')\n  end\n\n  describe :metadata do\n    it \"loads metadata for a task\" do\n      metadata  = {'desciption': 'some info'}\n      og_files = %w{task1.exe task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      allow(Puppet::Module::Task).to receive(:read_metadata).and_return(metadata)\n\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n      expect(tasks.count).to eq(1)\n      expect(tasks[0].metadata).to eq(metadata)\n    end\n\n    it 'returns nil for metadata if no file is present' do\n      og_files = %w{task1.exe}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n      expect(tasks.count).to eq(1)\n      expect(tasks[0].metadata).to be_nil\n    end\n\n    it 'raises InvalidMetadata if the json metadata is invalid' do\n      FileUtils.mkdir_p(tasks_path)\n      File.open(File.join(tasks_path, 'task.json'), 'w') { |f| f.write '{ \"whoops\"' }\n      FileUtils.touch(File.join(tasks_path, 'task'))\n\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n      expect(tasks.count).to eq(1)\n\n      expect {\n        tasks[0].metadata\n      }.to raise_error(Puppet::Module::Task::InvalidMetadata, /whoops/)\n    end\n\n    it 'returns empty hash for metadata when json metadata file is empty' do\n      FileUtils.mkdir_p(tasks_path)\n      FileUtils.touch(File.join(tasks_path, 'task.json'))\n      FileUtils.touch(File.join(tasks_path, 'task'))\n\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n      expect(tasks.count).to eq(1)\n      expect(tasks[0].metadata).to eq({})\n    end\n  end\n\n  describe :validate do\n    it \"validates when there is no metadata\" do\n      og_files = %w{task1.exe}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n      expect(tasks.count).to eq(1)\n      expect(tasks[0].validate).to eq(true)\n    end\n\n    it \"validates when an implementation isn't used\" do\n      metadata  = {'desciption' => 'some info',\n        'implementations' => [ {\"name\" => \"task1.exe\"}, ] }\n      og_files = %w{task1.exe task1.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      allow(Puppet::Module::Task).to receive(:read_metadata).and_return(metadata)\n\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n      expect(tasks.count).to eq(1)\n      expect(tasks[0].validate).to be(true)\n    end\n\n    it \"validates when an implementation is another task\" do\n      metadata  = {'desciption' => 'some info',\n                   'implementations' => [ {\"name\" => \"task2.sh\"}, ] }\n      og_files = %w{task1.exe task2.sh task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      allow(Puppet::Module::Task).to receive(:read_metadata).and_return(metadata)\n\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n\n      expect(tasks.count).to eq(2)\n      expect(tasks.map(&:validate)).to eq([true, true])\n    end\n\n    it \"fails validation when there is no metadata and multiple task files\" do\n      og_files = %w{task1.elf task1.exe task1.json task2.ps1 task2.sh}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n      allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({})\n\n      tasks.each do |task|\n        expect {task.validate}.to raise_error(Puppet::Module::Task::InvalidTask)\n      end\n    end\n\n    it \"fails validation when an implementation references a non-existant file\" do\n      og_files = %w{task1.elf task1.exe task1.json}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n      allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'implementations' => [ { 'name' => 'task1.sh' } ] })\n\n      tasks.each do |task|\n        expect {task.validate}.to raise_error(Puppet::Module::Task::InvalidTask)\n      end\n    end\n\n    it 'fails validation when there is metadata but no executable' do\n      og_files = %w{task1.json task2.sh}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n      allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({})\n\n      expect { tasks.find { |t| t.name == 'mymod::task1' }.validate }.to raise_error(Puppet::Module::Task::InvalidTask)\n    end\n\n    it 'fails validation when the implementations are not an array' do\n      og_files = %w{task1.json task2.sh}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n      allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({\"implemenations\" => {}})\n\n      expect { tasks.find { |t| t.name == 'mymod::task1' }.validate }.to raise_error(Puppet::Module::Task::InvalidTask)\n    end\n\n    it 'fails validation when the implementation is json' do\n      og_files = %w{task1.json task1.sh}.map { |bn| \"#{tasks_path}/#{bn}\" }\n      expect(Dir).to receive(:glob).with(tasks_glob).and_return(og_files)\n      og_files.each { |f| expect(File).to receive(:file?).with(f).and_return(true) }\n      tasks = Puppet::Module::Task.tasks_in_module(mymod)\n      allow_any_instance_of(Puppet::Module::Task).to receive(:metadata).and_return({'implementations' => [ { 'name' => 'task1.json' } ] })\n\n      expect { tasks.find { |t| t.name == 'mymod::task1' }.validate }.to raise_error(Puppet::Module::Task::InvalidTask)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/test/test_helper_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"TestHelper\" do\n  context \"#after_each_test\" do\n    it \"restores the original environment\" do\n      varname = 'test_helper_spec-test_variable'\n      ENV[varname] = \"\\u16A0\"\n\n      expect(ENV[varname]).to eq(\"\\u16A0\")\n\n      # Prematurely trigger the after_each_test method\n      Puppet::Test::TestHelper.after_each_test\n\n      expect(ENV[varname]).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/transaction/additional_resource_generator_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/transaction'\nrequire 'puppet_spec/compiler'\nrequire 'matchers/relationship_graph_matchers'\nrequire 'matchers/include_in_order'\nrequire 'matchers/resource'\n\ndescribe Puppet::Transaction::AdditionalResourceGenerator do\n  include PuppetSpec::Compiler\n  include PuppetSpec::Files\n  include RelationshipGraphMatchers\n  include Matchers::Resource\n\n  let(:prioritizer) { Puppet::Graph::SequentialPrioritizer.new }\n  let(:env) { Puppet::Node::Environment.create(:testing, []) }\n  let(:node) { Puppet::Node.new('test', :environment => env) }\n  let(:loaders) { Puppet::Pops::Loaders.new(env) }\n\n  before(:each) do\n    allow_any_instance_of(Puppet::Parser::Compiler).to receive(:loaders).and_return(loaders)\n    Puppet.push_context({:loaders => loaders, :current_environment => env})\n    Puppet::Type.newtype(:generator) do\n      include PuppetSpec::Compiler\n\n      newparam(:name) do\n        isnamevar\n      end\n\n      newparam(:kind) do\n        defaultto :eval_generate\n        newvalues(:eval_generate, :generate)\n      end\n\n      newparam(:code)\n\n      def eval_generate\n        eval_code\n      end\n\n      def generate\n        eval_code\n      end\n\n      def eval_code\n        if self[:code]\n          compile_to_ral(self[:code]).resources.select { |r| r.ref =~ /Notify/ }\n        else\n          []\n        end\n      end\n    end\n\n    Puppet::Type.newtype(:autorequire) do\n      newparam(:name) do\n        isnamevar\n      end\n\n      autorequire(:notify) do\n        self[:name]\n      end\n    end\n\n    Puppet::Type.newtype(:gen_auto) do\n      newparam(:name) do\n        isnamevar\n      end\n\n      newparam(:eval_after) do\n      end\n\n      def generate()\n        [ Puppet::Type.type(:autorequire).new(:name => self[:eval_after]) ]\n      end\n    end\n\n    Puppet::Type.newtype(:empty) do\n      newparam(:name) do\n        isnamevar\n      end\n    end\n\n    Puppet::Type.newtype(:gen_empty) do\n      newparam(:name) do\n        isnamevar\n      end\n\n      newparam(:eval_after) do\n      end\n\n      def generate()\n        [ Puppet::Type.type(:empty).new(:name => self[:eval_after], :require => \"Notify[#{self[:eval_after]}]\") ]\n      end\n    end\n  end\n\n  after(:each) do\n    Puppet::Type.rmtype(:gen_empty)\n    Puppet::Type.rmtype(:eval_after)\n    Puppet::Type.rmtype(:autorequire)\n    Puppet::Type.rmtype(:generator)\n    Puppet.pop_context()\n  end\n\n  def find_vertex(graph, type, title)\n    graph.vertices.find {|v| v.type == type and v.title == title}\n  end\n\n  context \"when applying eval_generate\" do\n    it \"should add the generated resources to the catalog\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        generator { thing:\n          code => 'notify { hello: }'\n        }\n      MANIFEST\n\n      eval_generate_resources_in(catalog, relationship_graph_for(catalog), 'Generator[thing]')\n\n      expect(catalog).to have_resource('Notify[hello]')\n    end\n\n    it \"should add a sentinel whit for the resource\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          code => 'notify { hello: }'\n        }\n      MANIFEST\n\n      expect(find_vertex(graph, :whit, \"completed_thing\")).to be_a(Puppet::Type.type(:whit))\n    end\n\n    it \"should replace dependencies on the resource with dependencies on the sentinel\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          code => 'notify { hello: }'\n        }\n\n        notify { last: require => Generator['thing'] }\n      MANIFEST\n\n      expect(graph).to enforce_order_with_edge(\n        'Whit[completed_thing]', 'Notify[last]')\n    end\n\n    it \"should add an edge from the nearest ancestor to the generated resource\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          code => 'notify { hello: } notify { goodbye: }'\n        }\n      MANIFEST\n\n      expect(graph).to enforce_order_with_edge(\n        'Generator[thing]', 'Notify[hello]')\n      expect(graph).to enforce_order_with_edge(\n        'Generator[thing]', 'Notify[goodbye]')\n    end\n\n    it \"should add an edge from each generated resource to the sentinel\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          code => 'notify { hello: } notify { goodbye: }'\n        }\n      MANIFEST\n\n      expect(graph).to enforce_order_with_edge(\n        'Notify[hello]', 'Whit[completed_thing]')\n      expect(graph).to enforce_order_with_edge(\n        'Notify[goodbye]', 'Whit[completed_thing]')\n    end\n\n    it \"should add an edge from the resource to the sentinel\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          code => 'notify { hello: }'\n        }\n      MANIFEST\n\n      expect(graph).to enforce_order_with_edge(\n        'Generator[thing]', 'Whit[completed_thing]')\n    end\n\n    it \"should tag the sentinel with the tags of the resource\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          code => 'notify { hello: }',\n          tag  => 'foo',\n        }\n      MANIFEST\n      whit = find_vertex(graph, :whit, \"completed_thing\")\n      expect(whit.tags).to be_superset(['thing', 'foo', 'generator'].to_set)\n    end\n\n    it \"should contain the generated resources in the same container as the generator\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        class container {\n          generator { thing:\n            code => 'notify { hello: }'\n          }\n        }\n\n        include container\n      MANIFEST\n\n      eval_generate_resources_in(catalog, relationship_graph_for(catalog), 'Generator[thing]')\n\n      expect(catalog).to contain_resources_equally('Generator[thing]', 'Notify[hello]')\n    end\n\n    it \"should return false if an error occurred when generating resources\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        generator { thing:\n          code => 'fail(\"not a good generation\")'\n        }\n      MANIFEST\n\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph_for(catalog), prioritizer)\n\n      expect(generator.eval_generate(catalog.resource('Generator[thing]'))).\n        to eq(false)\n    end\n\n    it \"should return true if resources were generated\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        generator { thing:\n          code => 'notify { hello: }'\n        }\n      MANIFEST\n\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph_for(catalog), prioritizer)\n\n      expect(generator.eval_generate(catalog.resource('Generator[thing]'))).\n        to eq(true)\n    end\n\n    it \"should not add a sentinel if no resources are generated\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        generator { thing: }\n      MANIFEST\n      relationship_graph = relationship_graph_for(catalog)\n\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph, prioritizer)\n\n      expect(generator.eval_generate(catalog.resource('Generator[thing]'))).\n        to eq(false)\n      expect(find_vertex(relationship_graph, :whit, \"completed_thing\")).to be_nil\n    end\n\n    it \"orders generated resources with the generator\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        generator { thing:\n          code => 'notify { hello: }'\n        }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[before]\", \"Generator[thing]\", \"Notify[hello]\", \"Notify[after]\"))\n    end\n\n    it \"orders the generator in manifest order with dependencies\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        generator { thing:\n          code => 'notify { hello: } notify { goodbye: }'\n        }\n        notify { third: require => Generator['thing'] }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[before]\",\n                         \"Generator[thing]\",\n                         \"Notify[hello]\",\n                         \"Notify[goodbye]\",\n                         \"Notify[third]\",\n                         \"Notify[after]\"))\n    end\n\n    it \"duplicate generated resources are made dependent on the generator\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        notify { hello: }\n        generator { thing:\n          code => 'notify { before: }'\n        }\n        notify { third: require => Generator['thing'] }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[hello]\", \"Generator[thing]\", \"Notify[before]\", \"Notify[third]\", \"Notify[after]\"))\n    end\n\n    it \"preserves dependencies on duplicate generated resources\" do\n      graph = relationships_after_eval_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        generator { thing:\n          code => 'notify { hello: } notify { before: }',\n          require => 'Notify[before]'\n        }\n        notify { third: require => Generator['thing'] }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[before]\", \"Generator[thing]\", \"Notify[hello]\", \"Notify[third]\", \"Notify[after]\"))\n    end\n\n    it \"sets resources_failed_to_generate to true if resource#eval_generate raises an exception\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        generator { thing: }\n      MANIFEST\n\n      allow(catalog.resource(\"Generator[thing]\")).to receive(:eval_generate).and_raise(RuntimeError)\n      relationship_graph = relationship_graph_for(catalog)\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph, prioritizer)\n      generator.eval_generate(catalog.resource(\"Generator[thing]\"))\n\n      expect(generator.resources_failed_to_generate).to be_truthy\n    end\n\n    def relationships_after_eval_generating(manifest, resource_to_generate)\n      catalog = compile_to_ral(manifest)\n      relationship_graph = relationship_graph_for(catalog)\n\n      eval_generate_resources_in(catalog, relationship_graph, resource_to_generate)\n\n      relationship_graph\n    end\n\n    def eval_generate_resources_in(catalog, relationship_graph, resource_to_generate)\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph, prioritizer)\n      generator.eval_generate(catalog.resource(resource_to_generate))\n    end\n  end\n\n  context \"when applying generate\" do\n    it \"should add the generated resources to the catalog\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        generator { thing:\n          kind => generate,\n          code => 'notify { hello: }'\n        }\n      MANIFEST\n\n      generate_resources_in(catalog, relationship_graph_for(catalog), 'Generator[thing]')\n\n      expect(catalog).to have_resource('Notify[hello]')\n    end\n\n    it \"should contain the generated resources in the same container as the generator\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        class container {\n          generator { thing:\n            kind => generate,\n            code => 'notify { hello: }'\n          }\n        }\n\n        include container\n      MANIFEST\n\n      generate_resources_in(catalog, relationship_graph_for(catalog), 'Generator[thing]')\n\n      expect(catalog).to contain_resources_equally('Generator[thing]', 'Notify[hello]')\n    end\n\n    it \"should add an edge from the nearest ancestor to the generated resource\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Generator[thing]')\n        generator { thing:\n          kind => generate,\n          code => 'notify { hello: } notify { goodbye: }'\n        }\n      MANIFEST\n\n      expect(graph).to enforce_order_with_edge(\n        'Generator[thing]', 'Notify[hello]')\n      expect(graph).to enforce_order_with_edge(\n        'Generator[thing]', 'Notify[goodbye]')\n    end\n\n    it \"orders generated resources with the generator\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        generator { thing:\n          kind => generate,\n          code => 'notify { hello: }'\n        }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[before]\", \"Generator[thing]\", \"Notify[hello]\", \"Notify[after]\"))\n    end\n\n    it \"duplicate generated resources are made dependent on the generator\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        notify { hello: }\n        generator { thing:\n          kind => generate,\n          code => 'notify { before: }'\n        }\n        notify { third: require => Generator['thing'] }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[hello]\", \"Generator[thing]\", \"Notify[before]\", \"Notify[third]\", \"Notify[after]\"))\n    end\n\n    it \"preserves dependencies on duplicate generated resources\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        generator { thing:\n          kind => generate,\n          code => 'notify { hello: } notify { before: }',\n          require => 'Notify[before]'\n        }\n        notify { third: require => Generator['thing'] }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[before]\", \"Generator[thing]\", \"Notify[hello]\", \"Notify[third]\", \"Notify[after]\"))\n    end\n\n    it \"orders the generator in manifest order with dependencies\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Generator[thing]')\n        notify { before: }\n        generator { thing:\n          kind => generate,\n          code => 'notify { hello: } notify { goodbye: }'\n        }\n        notify { third: require => Generator['thing'] }\n        notify { after: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Notify[before]\",\n                         \"Generator[thing]\",\n                         \"Notify[hello]\",\n                         \"Notify[goodbye]\",\n                         \"Notify[third]\",\n                         \"Notify[after]\"))\n    end\n\n    it \"runs autorequire on the generated resource\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Gen_auto[thing]')\n        gen_auto { thing:\n          eval_after => hello,\n        }\n\n        notify { hello: }\n        notify { goodbye: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Gen_auto[thing]\",\n                         \"Notify[hello]\",\n                         \"Autorequire[hello]\",\n                         \"Notify[goodbye]\"))\n    end\n\n    it \"evaluates metaparameters on the generated resource\" do\n      graph = relationships_after_generating(<<-MANIFEST, 'Gen_empty[thing]')\n        gen_empty { thing:\n          eval_after => hello,\n        }\n\n        notify { hello: }\n        notify { goodbye: }\n      MANIFEST\n\n      expect(order_resources_traversed_in(graph)).to(\n        include_in_order(\"Gen_empty[thing]\",\n                         \"Notify[hello]\",\n                         \"Empty[hello]\",\n                         \"Notify[goodbye]\"))\n    end\n\n    it \"sets resources_failed_to_generate to true if resource#generate raises an exception\" do\n      catalog = compile_to_ral(<<-MANIFEST)\n        user { 'foo':\n          ensure => present,\n        }\n      MANIFEST\n\n      allow(catalog.resource(\"User[foo]\")).to receive(:generate).and_raise(RuntimeError)\n      relationship_graph = relationship_graph_for(catalog)\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph, prioritizer)\n      generator.generate_additional_resources(catalog.resource(\"User[foo]\"))\n\n      expect(generator.resources_failed_to_generate).to be_truthy\n    end\n\n    def relationships_after_generating(manifest, resource_to_generate)\n      catalog = compile_to_ral(manifest)\n      generate_resources_in(catalog, nil, resource_to_generate)\n      relationship_graph_for(catalog)\n    end\n\n    def generate_resources_in(catalog, relationship_graph, resource_to_generate)\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, relationship_graph, prioritizer)\n      generator.generate_additional_resources(catalog.resource(resource_to_generate))\n    end\n  end\n\n  def relationship_graph_for(catalog)\n    relationship_graph = Puppet::Graph::RelationshipGraph.new(prioritizer)\n    relationship_graph.populate_from(catalog)\n    relationship_graph\n  end\n\n  def order_resources_traversed_in(relationships)\n    order_seen = []\n    relationships.traverse { |resource| order_seen << resource.ref }\n    order_seen\n  end\n\n  RSpec::Matchers.define :contain_resources_equally do |*resource_refs|\n    match do |catalog|\n      @containers = resource_refs.collect do |resource_ref|\n        catalog.container_of(catalog.resource(resource_ref)).ref\n      end\n\n      @containers.all? { |resource_ref| resource_ref == @containers[0] }\n    end\n\n    def failure_message\n      \"expected #{@expected.join(', ')} to all be contained in the same resource but the containment was #{@expected.zip(@containers).collect { |(res, container)| res + ' => ' + container }.join(', ')}\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/transaction/event_manager_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/transaction/event_manager'\n\ndescribe Puppet::Transaction::EventManager do\n  include PuppetSpec::Files\n\n  describe \"at initialization\" do\n    it \"should require a transaction\" do\n      expect(Puppet::Transaction::EventManager.new(\"trans\").transaction).to eq(\"trans\")\n    end\n  end\n\n  it \"should delegate its relationship graph to the transaction\" do\n    transaction = double('transaction')\n    manager = Puppet::Transaction::EventManager.new(transaction)\n\n    expect(transaction).to receive(:relationship_graph).and_return(\"mygraph\")\n\n    expect(manager.relationship_graph).to eq(\"mygraph\")\n  end\n\n  describe \"when queueing events\" do\n    before do\n      @manager = Puppet::Transaction::EventManager.new(@transaction)\n\n      @resource = Puppet::Type.type(:file).new :path => make_absolute(\"/my/file\")\n\n      @graph = double('graph', :matching_edges => [], :resource => @resource)\n      allow(@manager).to receive(:relationship_graph).and_return(@graph)\n\n      @event = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource)\n    end\n\n    it \"should store all of the events in its event list\" do\n      @event2 = Puppet::Transaction::Event.new(:name => :bar, :resource => @resource)\n      @manager.queue_events(@resource, [@event, @event2])\n\n      expect(@manager.events).to include(@event)\n      expect(@manager.events).to include(@event2)\n    end\n\n    it \"should queue events for the target and callback of any matching edges\" do\n      edge1 = double(\"edge1\", :callback => :c1, :source => double(\"s1\"), :target => double(\"t1\", :c1 => nil))\n      edge2 = double(\"edge2\", :callback => :c2, :source => double(\"s2\"), :target => double(\"t2\", :c2 => nil))\n\n      expect(@graph).to receive(:matching_edges).with(@event, anything).and_return([edge1, edge2])\n\n      expect(@manager).to receive(:queue_events_for_resource).with(@resource, edge1.target, edge1.callback, [@event])\n      expect(@manager).to receive(:queue_events_for_resource).with(@resource, edge2.target, edge2.callback, [@event])\n\n      @manager.queue_events(@resource, [@event])\n    end\n\n    it \"should queue events for the changed resource if the resource is self-refreshing and not being deleted\" do\n      allow(@graph).to receive(:matching_edges).and_return([])\n\n      expect(@resource).to receive(:self_refresh?).and_return(true)\n      expect(@resource).to receive(:deleting?).and_return(false)\n      expect(@manager).to receive(:queue_events_for_resource).with(@resource, @resource, :refresh, [@event])\n\n      @manager.queue_events(@resource, [@event])\n    end\n\n    it \"should not queue events for the changed resource if the resource is not self-refreshing\" do\n      allow(@graph).to receive(:matching_edges).and_return([])\n\n      expect(@resource).to receive(:self_refresh?).and_return(false)\n      allow(@resource).to receive(:deleting?).and_return(false)\n      expect(@manager).not_to receive(:queue_events_for_resource)\n\n      @manager.queue_events(@resource, [@event])\n    end\n\n    it \"should not queue events for the changed resource if the resource is being deleted\" do\n      allow(@graph).to receive(:matching_edges).and_return([])\n\n      expect(@resource).to receive(:self_refresh?).and_return(true)\n      expect(@resource).to receive(:deleting?).and_return(true)\n      expect(@manager).not_to receive(:queue_events_for_resource)\n\n      @manager.queue_events(@resource, [@event])\n    end\n\n    it \"should ignore edges that don't have a callback\" do\n      edge1 = double(\"edge1\", :callback => :nil, :source => double(\"s1\"), :target => double(\"t1\", :c1 => nil))\n\n      expect(@graph).to receive(:matching_edges).and_return([edge1])\n\n      expect(@manager).not_to receive(:queue_events_for_resource)\n\n      @manager.queue_events(@resource, [@event])\n    end\n\n    it \"should ignore targets that don't respond to the callback\" do\n      edge1 = double(\"edge1\", :callback => :c1, :source => double(\"s1\"), :target => double(\"t1\"))\n\n      expect(@graph).to receive(:matching_edges).and_return([edge1])\n\n      expect(@manager).not_to receive(:queue_events_for_resource)\n\n      @manager.queue_events(@resource, [@event])\n    end\n\n    it \"should dequeue events for the changed resource if an event with invalidate_refreshes is processed\" do\n      @event2 = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource, :invalidate_refreshes => true)\n\n      allow(@graph).to receive(:matching_edges).and_return([])\n\n      expect(@manager).to receive(:dequeue_events_for_resource).with(@resource, :refresh)\n\n      @manager.queue_events(@resource, [@event, @event2])\n    end\n  end\n\n  describe \"when queueing events for a resource\" do\n    before do\n      @transaction = double('transaction')\n      @manager = Puppet::Transaction::EventManager.new(@transaction)\n    end\n\n    it \"should do nothing if no events are queued\" do\n      @manager.queued_events(double(\"target\")) { |callback, events| raise \"should never reach this\" }\n    end\n\n    it \"should yield the callback and events for each callback\" do\n      target = double(\"target\")\n\n      2.times do |i|\n        @manager.queue_events_for_resource(double(\"source\", :info => nil), target, \"callback#{i}\", [\"event#{i}\"])\n      end\n\n      @manager.queued_events(target) { |callback, events| }\n    end\n\n    it \"should use the source to log that it's scheduling a refresh of the target\" do\n      target = double(\"target\")\n      source = double('source')\n      expect(source).to receive(:info)\n\n      @manager.queue_events_for_resource(source, target, \"callback\", [\"event\"])\n\n      @manager.queued_events(target) { |callback, events| }\n    end\n  end\n\n  describe \"when processing events for a given resource\" do\n    before do\n      @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)\n      @manager = Puppet::Transaction::EventManager.new(@transaction)\n      allow(@manager).to receive(:queue_events)\n\n      @resource = Puppet::Type.type(:file).new :path => make_absolute(\"/my/file\")\n      @event = Puppet::Transaction::Event.new(:name => :event, :resource => @resource)\n\n      @resource.class.send(:define_method, :callback1) {}\n      @resource.class.send(:define_method, :callback2) {}\n    end\n\n    it \"should call the required callback once for each set of associated events\" do\n      expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event]).and_yield(:callback2, [@event])\n\n      expect(@resource).to receive(:callback1)\n      expect(@resource).to receive(:callback2)\n\n      @manager.process_events(@resource)\n    end\n\n    it \"should set the 'restarted' state on the resource status\" do\n      expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event])\n\n      allow(@resource).to receive(:callback1)\n\n      @manager.process_events(@resource)\n\n      expect(@transaction.resource_status(@resource)).to be_restarted\n    end\n\n    it \"should have an event on the resource status\" do\n      expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event])\n\n      allow(@resource).to receive(:callback1)\n\n      @manager.process_events(@resource)\n\n      expect(@transaction.resource_status(@resource).events.length).to eq(1)\n    end\n\n    it \"should queue a 'restarted' event generated by the resource\" do\n      expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event])\n\n      allow(@resource).to receive(:callback1)\n\n      expect(@resource).to receive(:event).with({:message => \"Triggered 'callback1' from 1 event\", :status => 'success', :name => 'callback1'})\n      expect(@resource).to receive(:event).with({:name => :restarted, :status => \"success\"}).and_return(\"myevent\")\n      expect(@manager).to receive(:queue_events).with(@resource, [\"myevent\"])\n\n      @manager.process_events(@resource)\n    end\n\n    it \"should log that it restarted\" do\n      expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event])\n\n      allow(@resource).to receive(:callback1)\n\n      expect(@resource).to receive(:notice).with(/Triggered 'callback1'/)\n\n      @manager.process_events(@resource)\n    end\n\n    describe \"and the events include a noop event and at least one non-noop event\" do\n      before do\n        allow(@event).to receive(:status).and_return(\"noop\")\n        @event2 = Puppet::Transaction::Event.new(:name => :event, :resource => @resource)\n        @event2.status = \"success\"\n        expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event, @event2])\n        @resource.class.send(:define_method, :callback1) {}\n      end\n\n      it \"should call the callback\" do\n\n        expect(@resource).to receive(:callback1)\n\n        @manager.process_events(@resource)\n      end\n    end\n\n    describe \"and the events are all noop events\" do\n      before do\n        allow(@event).to receive(:status).and_return(\"noop\")\n        allow(@resource).to receive(:event).and_return(Puppet::Transaction::Event.new)\n        expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event])\n        @resource.class.send(:define_method, :callback1) {}\n      end\n\n      it \"should log\" do\n        expect(@resource).to receive(:notice).with(/Would have triggered 'callback1'/)\n\n        @manager.process_events(@resource)\n      end\n\n      it \"should not call the callback\" do\n        expect(@resource).not_to receive(:callback1)\n\n        @manager.process_events(@resource)\n      end\n\n      it \"should queue a new noop event generated from the resource\" do\n        event = Puppet::Transaction::Event.new\n        expect(@resource).to receive(:event).with({:status => \"noop\", :name => :noop_restart}).and_return(event)\n        expect(@manager).to receive(:queue_events).with(@resource, [event])\n\n        @manager.process_events(@resource)\n      end\n    end\n\n    describe \"and the resource has noop set to true\" do\n      before do\n        allow(@event).to receive(:status).and_return(\"success\")\n        allow(@resource).to receive(:event).and_return(Puppet::Transaction::Event.new)\n        allow(@resource).to receive(:noop?).and_return(true)\n        expect(@manager).to receive(:queued_events).with(@resource).and_yield(:callback1, [@event])\n        @resource.class.send(:define_method, :callback1) {}\n      end\n\n      it \"should log\" do\n        expect(@resource).to receive(:notice).with(/Would have triggered 'callback1'/)\n\n        @manager.process_events(@resource)\n      end\n\n      it \"should not call the callback\" do\n        expect(@resource).not_to receive(:callback1)\n\n        @manager.process_events(@resource)\n      end\n\n      it \"should queue a new noop event generated from the resource\" do\n        event = Puppet::Transaction::Event.new\n        expect(@resource).to receive(:event).with({:status => \"noop\", :name => :noop_restart}).and_return(event)\n        expect(@manager).to receive(:queue_events).with(@resource, [event])\n\n        @manager.process_events(@resource)\n      end\n    end\n\n    describe \"and the callback fails\" do\n      before do\n        @resource.class.send(:define_method, :callback1) { raise \"a failure\" }\n\n        expect(@manager).to receive(:queued_events).and_yield(:callback1, [@event])\n      end\n\n      it \"should emit an error and log but not fail\" do\n        expect(@resource).to receive(:err).with('Failed to call callback1: a failure').and_call_original\n\n        @manager.process_events(@resource)\n\n        expect(@logs).to include(an_object_having_attributes(level: :err, message: 'a failure'))\n      end\n\n      it \"should set the 'failed_restarts' state on the resource status\" do\n        @manager.process_events(@resource)\n        expect(@transaction.resource_status(@resource)).to be_failed_to_restart\n      end\n\n      it \"should set the 'failed' state on the resource status\" do\n        @manager.process_events(@resource)\n        expect(@transaction.resource_status(@resource)).to be_failed\n      end\n\n      it \"should record a failed event on the resource status\" do\n        @manager.process_events(@resource)\n\n        expect(@transaction.resource_status(@resource).events.length).to eq(1)\n        expect(@transaction.resource_status(@resource).events[0].status).to eq('failure')\n      end\n\n      it \"should not queue a 'restarted' event\" do\n        expect(@manager).not_to receive(:queue_events)\n        @manager.process_events(@resource)\n      end\n\n      it \"should set the 'restarted' state on the resource status\" do\n        @manager.process_events(@resource)\n        expect(@transaction.resource_status(@resource)).not_to be_restarted\n      end\n    end\n  end\n\n  describe \"when queueing then processing events for a given resource\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @target = Puppet::Type.type(:exec).new(name: 'target', path: ENV['PATH'])\n      @resource = Puppet::Type.type(:exec).new(name: 'resource', path: ENV['PATH'], notify: @target)\n      @catalog.add_resource(@resource, @target)\n\n      @manager = Puppet::Transaction::EventManager.new(Puppet::Transaction.new(@catalog, nil, nil))\n\n      @event  = Puppet::Transaction::Event.new(:name => :notify, :resource => @target)\n      @event2 = Puppet::Transaction::Event.new(:name => :service_start, :resource => @target, :invalidate_refreshes => true)\n    end\n\n    it \"should succeed when there's no invalidated event\" do\n      @manager.queue_events(@target, [@event2])\n    end\n\n    describe \"and the events were dequeued/invalidated\" do\n      before do\n        expect(@resource).to receive(:info).with(/Scheduling refresh/)\n        expect(@target).to receive(:info).with(/Unscheduling/)\n      end\n\n      it \"should not run an event or log\" do\n        expect(@target).not_to receive(:notice).with(/Would have triggered 'refresh'/)\n        expect(@target).not_to receive(:refresh)\n\n        @manager.queue_events(@resource, [@event])\n        @manager.queue_events(@target, [@event2])\n        @manager.process_events(@resource)\n        @manager.process_events(@target)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/transaction/event_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/transaction/event'\n\nclass TestResource\n  def to_s\n    \"Foo[bar]\"\n  end\n  def [](v)\n    nil\n  end\nend\n\ndescribe Puppet::Transaction::Event do\n  include PuppetSpec::Files\n\n  it \"should support resource\" do\n    event = Puppet::Transaction::Event.new\n    event.resource = TestResource.new\n    expect(event.resource).to eq(\"Foo[bar]\")\n  end\n\n  it \"should always convert the property to a string\" do\n    expect(Puppet::Transaction::Event.new(:property => :foo).property).to eq(\"foo\")\n  end\n\n  it \"should always convert the resource to a string\" do\n    expect(Puppet::Transaction::Event.new(:resource => TestResource.new).resource).to eq(\"Foo[bar]\")\n  end\n\n  it \"should produce the message when converted to a string\" do\n    event = Puppet::Transaction::Event.new\n    expect(event).to receive(:message).and_return(\"my message\")\n    expect(event.to_s).to eq(\"my message\")\n  end\n\n  it \"should support 'status'\" do\n    event = Puppet::Transaction::Event.new\n    event.status = \"success\"\n    expect(event.status).to eq(\"success\")\n  end\n\n  it \"should fail if the status is not to 'audit', 'noop', 'success', or 'failure\" do\n    event = Puppet::Transaction::Event.new\n    expect { event.status = \"foo\" }.to raise_error(ArgumentError)\n  end\n\n  it \"should support tags\" do\n    expect(Puppet::Transaction::Event.ancestors).to include(Puppet::Util::Tagging)\n  end\n\n  it \"should create a timestamp at its creation time\" do\n    expect(Puppet::Transaction::Event.new.time).to be_instance_of(Time)\n  end\n\n  describe \"audit property\" do\n    it \"should default to false\" do\n      expect(Puppet::Transaction::Event.new.audited).to eq(false)\n    end\n  end\n\n  describe \"when sending logs\" do\n    before do\n      allow(Puppet::Util::Log).to receive(:new)\n    end\n\n    it \"should set the level to the resources's log level if the event status is 'success' and a resource is available\" do\n      resource = double('resource')\n      expect(resource).to receive(:[]).with(:loglevel).and_return(:myloglevel)\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(level: :myloglevel))\n      Puppet::Transaction::Event.new(:status => \"success\", :resource => resource).send_log\n    end\n\n    it \"should set the level to 'notice' if the event status is 'success' and no resource is available\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(level: :notice))\n      Puppet::Transaction::Event.new(:status => \"success\").send_log\n    end\n\n    it \"should set the level to 'notice' if the event status is 'noop'\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(level: :notice))\n      Puppet::Transaction::Event.new(:status => \"noop\").send_log\n    end\n\n    it \"should set the level to 'err' if the event status is 'failure'\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(level: :err))\n      Puppet::Transaction::Event.new(:status => \"failure\").send_log\n    end\n\n    it \"should set the 'message' to the event log\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(message: \"my message\"))\n      Puppet::Transaction::Event.new(:message => \"my message\").send_log\n    end\n\n    it \"should set the tags to the event tags\" do\n      expect(Puppet::Util::Log).to receive(:new) do |args|\n        expect(args[:tags].to_a).to match_array(%w{one two})\n      end\n      Puppet::Transaction::Event.new(:tags => %w{one two}).send_log\n    end\n\n    [:file, :line].each do |attr|\n      it \"should pass the #{attr}\" do\n        expect(Puppet::Util::Log).to receive(:new).with(hash_including(attr => \"my val\"))\n        Puppet::Transaction::Event.new(attr => \"my val\").send_log\n      end\n    end\n\n    it \"should use the source description as the source if one is set\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(source: \"/my/param\"))\n      Puppet::Transaction::Event.new(:source_description => \"/my/param\", :resource => TestResource.new, :property => \"foo\").send_log\n    end\n\n    it \"should use the property as the source if one is available and no source description is set\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(source: \"foo\"))\n      Puppet::Transaction::Event.new(:resource => TestResource.new, :property => \"foo\").send_log\n    end\n\n    it \"should use the property as the source if one is available and no property or source description is set\" do\n      expect(Puppet::Util::Log).to receive(:new).with(hash_including(source: \"Foo[bar]\"))\n      Puppet::Transaction::Event.new(:resource => TestResource.new).send_log\n    end\n  end\n\n  describe \"When converting to YAML\" do\n    let(:resource) { Puppet::Type.type(:file).new(:title => make_absolute('/tmp/foo')) }\n    let(:event) do\n      Puppet::Transaction::Event.new(:source_description => \"/my/param\", :resource => resource,\n        :file => \"/foo.rb\", :line => 27, :tags => %w{one two},\n        :desired_value => 7, :historical_value => 'Brazil',\n        :message => \"Help I'm trapped in a spec test\",\n        :name => :mode_changed, :previous_value => 6, :property => :mode,\n        :status => 'success',\n        :redacted => false,\n        :corrective_change => false)\n    end\n\n    it 'to_data_hash returns value that is instance of to Data' do\n      expect(Puppet::Pops::Types::TypeFactory.data.instance?(event.to_data_hash)).to be_truthy\n    end\n  end\n\n  it \"should round trip through json\" do\n      resource = Puppet::Type.type(:file).new(:title => make_absolute(\"/tmp/foo\"))\n      event = Puppet::Transaction::Event.new(\n        :source_description => \"/my/param\",\n        :resource => resource,\n        :file => \"/foo.rb\",\n        :line => 27,\n        :tags => %w{one two},\n        :desired_value => 7,\n        :historical_value => 'Brazil',\n        :message => \"Help I'm trapped in a spec test\",\n        :name => :mode_changed,\n        :previous_value => 6,\n        :property => :mode,\n        :status => 'success')\n\n      tripped = Puppet::Transaction::Event.from_data_hash(JSON.parse(event.to_json))\n\n      expect(tripped.audited).to eq(event.audited)\n      expect(tripped.property).to eq(event.property)\n      expect(tripped.previous_value).to eq(event.previous_value)\n      expect(tripped.desired_value).to eq(event.desired_value)\n      expect(tripped.historical_value).to eq(event.historical_value)\n      expect(tripped.message).to eq(event.message)\n      expect(tripped.name).to eq(event.name)\n      expect(tripped.status).to eq(event.status)\n      expect(tripped.time).to eq(event.time)\n  end\n\n  it \"should round trip an event for an inspect report through json\" do\n      resource = Puppet::Type.type(:file).new(:title => make_absolute(\"/tmp/foo\"))\n      event = Puppet::Transaction::Event.new(\n        :audited => true,\n        :source_description => \"/my/param\",\n        :resource => resource,\n        :file => \"/foo.rb\",\n        :line => 27,\n        :tags => %w{one two},\n        :message => \"Help I'm trapped in a spec test\",\n        :previous_value => 6,\n        :property => :mode,\n        :status => 'success')\n\n      tripped = Puppet::Transaction::Event.from_data_hash(JSON.parse(event.to_json))\n\n      expect(tripped.desired_value).to be_nil\n      expect(tripped.historical_value).to be_nil\n      expect(tripped.name).to be_nil\n\n      expect(tripped.audited).to eq(event.audited)\n      expect(tripped.property).to eq(event.property)\n      expect(tripped.previous_value).to eq(event.previous_value)\n      expect(tripped.message).to eq(event.message)\n      expect(tripped.status).to eq(event.status)\n      expect(tripped.time).to eq(event.time)\n  end\nend\n"
  },
  {
    "path": "spec/unit/transaction/persistence_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'yaml'\nrequire 'fileutils'\nrequire 'puppet/transaction/persistence'\n\ndescribe Puppet::Transaction::Persistence do\n  include PuppetSpec::Files\n\n  before(:each) do\n    @basepath = File.expand_path(\"/somepath\")\n  end\n\n  describe \"when loading from file\" do\n    before do\n      allow(Puppet.settings).to receive(:use).and_return(true)\n    end\n\n    describe \"when the file/directory does not exist\" do\n      before(:each) do\n        @path = tmpfile('storage_test')\n      end\n\n      it \"should not fail to load\" do\n        expect(Puppet::FileSystem.exist?(@path)).to be_falsey\n        Puppet[:statedir] = @path\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n        Puppet[:transactionstorefile] = @path\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n      end\n    end\n\n    describe \"when the file/directory exists\" do\n      before(:each) do\n        @tmpfile = tmpfile('storage_test')\n        Puppet[:transactionstorefile] = @tmpfile\n      end\n\n      def write_state_file(contents)\n        File.open(@tmpfile, 'w') { |f| f.write(contents) }\n      end\n\n      it \"should overwrite its internal state if load() is called\" do\n        resource = \"Foo[bar]\"\n        property = \"my\"\n        value = \"something\"\n\n        expect(Puppet).not_to receive(:err)\n\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.set_system_value(resource, property, value)\n\n        persistence.load\n\n        expect(persistence.get_system_value(resource, property)).to eq(nil)\n      end\n\n      it \"should restore its internal state if the file contains valid YAML\" do\n        test_yaml = {\"resources\"=>{\"a\"=>\"b\"}}\n        write_state_file(test_yaml.to_yaml)\n\n        expect(Puppet).not_to receive(:err)\n\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n\n        expect(persistence.data).to eq(test_yaml)\n      end\n\n      it \"should initialize with a clear internal state if the file does not contain valid YAML\" do\n        write_state_file('{ invalid')\n\n        expect(Puppet).to receive(:send_log).with(:err, /Transaction store file .* is corrupt/)\n\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n\n        expect(persistence.data).to eq({})\n      end\n\n      it \"should initialize with a clear internal state if the file does not contain a hash of data\" do\n        write_state_file(\"not_a_hash\")\n\n        expect(Puppet).to receive(:err).with(/Transaction store file .* is valid YAML but not returning a hash/)\n\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n\n        expect(persistence.data).to eq({})\n      end\n\n      it \"should raise an error if the file does not contain valid YAML and cannot be renamed\" do\n        write_state_file('{ invalid')\n\n        expect(File).to receive(:rename).and_raise(SystemCallError)\n\n        expect(Puppet).to receive(:send_log).with(:err, /Transaction store file .* is corrupt/)\n        expect(Puppet).to receive(:send_log).with(:err, /Unable to rename/)\n\n        persistence = Puppet::Transaction::Persistence.new\n        expect { persistence.load }.to raise_error(Puppet::Error, /Could not rename/)\n      end\n\n      it \"should attempt to rename the file if the file is corrupted\" do\n        write_state_file('{ invalid')\n\n        expect(File).to receive(:rename).at_least(:once)\n\n        expect(Puppet).to receive(:send_log).with(:err, /Transaction store file .* is corrupt/)\n\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n      end\n\n      it \"should fail gracefully on load() if the file is not a regular file\" do\n        FileUtils.rm_f(@tmpfile)\n        Dir.mkdir(@tmpfile)\n\n        expect(Puppet).to receive(:warning).with(/Transaction store file .* is not a file/)\n\n        persistence = Puppet::Transaction::Persistence.new\n        persistence.load\n      end\n\n      it 'should load Time and Symbols' do\n        write_state_file(<<~END)\n          File[/tmp/audit]:\n            parameters:\n              mtime:\n                system_value:\n                  - 2020-07-15 05:38:12.427678398 +00:00\n              ensure:\n                system_value:\n        END\n\n        persistence = Puppet::Transaction::Persistence.new\n        expect(persistence.load.dig(\"File[/tmp/audit]\", \"parameters\", \"mtime\", \"system_value\")).to contain_exactly(be_a(Time))\n      end\n\n      it 'should load Regexp' do\n        write_state_file(<<~END)\n          system_value:\n            - !ruby/regexp /regexp/\n        END\n\n        persistence = Puppet::Transaction::Persistence.new\n        expect(persistence.load.dig(\"system_value\")).to contain_exactly(be_a(Regexp))\n      end\n\n      it 'should load semantic puppet version' do\n        write_state_file(<<~END)\n          system_value:\n            - !ruby/object:SemanticPuppet::Version\n              major: 1\n              minor: 0\n              patch: 0\n              prerelease: \n              build: \n        END\n\n        persistence = Puppet::Transaction::Persistence.new\n        expect(persistence.load.dig(\"system_value\")).to contain_exactly(be_a(SemanticPuppet::Version))\n      end\n\n      it 'should load puppet time related objects' do\n        write_state_file(<<~END)\n          system_value:\n            - !ruby/object:Puppet::Pops::Time::Timestamp\n              nsecs: 1638316135955087259\n            - !ruby/object:Puppet::Pops::Time::TimeData\n              nsecs: 1495789430910161286\n            - !ruby/object:Puppet::Pops::Time::Timespan\n              nsecs: 1495789430910161286\n        END\n\n        persistence = Puppet::Transaction::Persistence.new\n        expect(persistence.load.dig(\"system_value\")).to contain_exactly(be_a(Puppet::Pops::Time::Timestamp), be_a(Puppet::Pops::Time::TimeData), be_a(Puppet::Pops::Time::Timespan))\n      end\n\n      it 'should load binary objects' do\n        write_state_file(<<~END)\n          system_value:\n            - !ruby/object:Puppet::Pops::Types::PBinaryType::Binary\n              binary_buffer: ''\n        END\n\n        persistence = Puppet::Transaction::Persistence.new\n        expect(persistence.load.dig(\"system_value\")).to contain_exactly(be_a(Puppet::Pops::Types::PBinaryType::Binary))\n      end\n    end\n  end\n\n  describe \"when storing to the file\" do\n    before(:each) do\n      @tmpfile = tmpfile('persistence_test')\n      @saved = Puppet[:transactionstorefile]\n      Puppet[:transactionstorefile] = @tmpfile\n    end\n\n    it \"should create the file if it does not exist\" do\n      expect(Puppet::FileSystem.exist?(Puppet[:transactionstorefile])).to be_falsey\n\n      persistence = Puppet::Transaction::Persistence.new\n      persistence.save\n\n      expect(Puppet::FileSystem.exist?(Puppet[:transactionstorefile])).to be_truthy\n    end\n\n    it \"should raise an exception if the file is not a regular file\" do\n      Dir.mkdir(Puppet[:transactionstorefile])\n      persistence = Puppet::Transaction::Persistence.new\n\n      expect { persistence.save }.to raise_error(Errno::EISDIR, /Is a directory/)\n\n      Dir.rmdir(Puppet[:transactionstorefile])\n    end\n\n    it \"should load the same information that it saves\" do\n      resource = \"File[/tmp/foo]\"\n      property = \"content\"\n      value = \"foo\"\n\n      persistence = Puppet::Transaction::Persistence.new\n      persistence.set_system_value(resource, property, value)\n\n      persistence.save\n      persistence.load\n\n      expect(persistence.get_system_value(resource, property)).to eq(value)\n    end\n  end\n\n  describe \"when checking if persistence is enabled\" do\n    let(:mock_catalog) do\n      double()\n    end\n\n    let (:persistence) do\n      Puppet::Transaction::Persistence.new\n    end\n\n    before :all do\n      @preferred_run_mode = Puppet.settings.preferred_run_mode\n    end\n\n    after :all do\n      Puppet.settings.preferred_run_mode = @preferred_run_mode\n    end\n\n    it \"should not be enabled when not running in agent mode\" do\n      Puppet.settings.preferred_run_mode = :user\n      allow(mock_catalog).to receive(:host_config?).and_return(true)\n      expect(persistence.enabled?(mock_catalog)).to be false\n    end\n\n    it \"should not be enabled when the catalog is not the host catalog\" do\n      Puppet.settings.preferred_run_mode = :agent\n      allow(mock_catalog).to receive(:host_config?).and_return(false)\n      expect(persistence.enabled?(mock_catalog)).to be false\n    end\n\n    it \"should not be enabled outside of agent mode and the catalog is not the host catalog\" do\n      Puppet.settings.preferred_run_mode = :user\n      allow(mock_catalog).to receive(:host_config?).and_return(false)\n      expect(persistence.enabled?(mock_catalog)).to be false\n    end\n\n    it \"should be enabled in agent mode and when the catalog is the host catalog\" do\n      Puppet.settings.preferred_run_mode = :agent\n      allow(mock_catalog).to receive(:host_config?).and_return(true)\n      expect(persistence.enabled?(mock_catalog)).to be true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/transaction/report_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet'\nrequire 'puppet/transaction/report'\nrequire 'matchers/json'\n\ndescribe Puppet::Transaction::Report do\n  include JSONMatchers\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  before do\n    allow(Puppet::Util::Storage).to receive(:store)\n  end\n\n  it \"should set its host name to the node_name_value\" do\n    Puppet[:node_name_value] = 'mynode'\n    expect(Puppet::Transaction::Report.new.host).to eq(\"mynode\")\n  end\n\n  it \"should return its host name as its name\" do\n    r = Puppet::Transaction::Report.new\n    expect(r.name).to eq(r.host)\n  end\n\n  it \"should create an initialization timestamp\" do\n    expect(Time).to receive(:now).and_return(\"mytime\")\n    expect(Puppet::Transaction::Report.new.time).to eq(\"mytime\")\n  end\n\n  it \"should take a 'configuration_version' as an argument\" do\n    expect(Puppet::Transaction::Report.new(\"some configuration version\", \"some environment\").configuration_version).to eq(\"some configuration version\")\n  end\n\n  it \"should take a 'transaction_uuid' as an argument\" do\n    expect(Puppet::Transaction::Report.new(\"some configuration version\", \"some environment\", \"some transaction uuid\").transaction_uuid).to eq(\"some transaction uuid\")\n  end\n\n  it \"should take a 'job_id' as an argument\" do\n    expect(Puppet::Transaction::Report.new('cv', 'env', 'tid', 'some job id').job_id).to eq('some job id')\n  end\n\n  it \"should take a 'start_time' as an argument\" do\n    expect(Puppet::Transaction::Report.new('cv', 'env', 'tid', 'some job id', 'my start time').time).to eq('my start time')\n  end\n\n  it \"should be able to set configuration_version\" do\n    report = Puppet::Transaction::Report.new\n    report.configuration_version = \"some version\"\n    expect(report.configuration_version).to eq(\"some version\")\n  end\n\n  it \"should be able to set transaction_uuid\" do\n    report = Puppet::Transaction::Report.new\n    report.transaction_uuid = \"some transaction uuid\"\n    expect(report.transaction_uuid).to eq(\"some transaction uuid\")\n  end\n\n  it \"should be able to set job_id\" do\n    report = Puppet::Transaction::Report.new\n    report.job_id = \"some job id\"\n    expect(report.job_id).to eq(\"some job id\")\n  end\n\n  it \"should be able to set code_id\" do\n    report = Puppet::Transaction::Report.new\n    report.code_id = \"some code id\"\n    expect(report.code_id).to eq(\"some code id\")\n  end\n\n  it \"should be able to set catalog_uuid\" do\n    report = Puppet::Transaction::Report.new\n    report.catalog_uuid = \"some catalog uuid\"\n    expect(report.catalog_uuid).to eq(\"some catalog uuid\")\n  end\n\n  it \"should be able to set cached_catalog_status\" do\n    report = Puppet::Transaction::Report.new\n    report.cached_catalog_status = \"explicitly_requested\"\n    expect(report.cached_catalog_status).to eq(\"explicitly_requested\")\n  end\n\n  it \"should set noop to true if Puppet[:noop] is true\" do\n    Puppet[:noop] = true\n    report = Puppet::Transaction::Report.new\n    expect(report.noop).to be_truthy\n  end\n\n  it \"should set noop to false if Puppet[:noop] is false\" do\n    Puppet[:noop] = false\n    report = Puppet::Transaction::Report.new\n    expect(report.noop).to be_falsey\n  end\n\n  it \"should set noop to false if Puppet[:noop] is unset\" do\n    Puppet[:noop] = nil\n    report = Puppet::Transaction::Report.new\n    expect(report.noop).to be_falsey\n  end\n\n  it \"should take 'environment' as an argument\" do\n    expect(Puppet::Transaction::Report.new(\"some configuration version\", \"some environment\").environment).to eq(\"some environment\")\n  end\n\n  it \"should be able to set environment\" do\n    report = Puppet::Transaction::Report.new\n    report.environment = \"some environment\"\n    expect(report.environment).to eq(\"some environment\")\n  end\n\n  it \"should be able to set resources_failed_to_generate\" do\n    report = Puppet::Transaction::Report.new\n    report.resources_failed_to_generate = true\n    expect(report.resources_failed_to_generate).to be_truthy\n  end\n\n  it \"resources_failed_to_generate should not be true by default\" do\n    report = Puppet::Transaction::Report.new\n    expect(report.resources_failed_to_generate).to be_falsey\n  end\n\n  it \"should not include whits\" do\n    allow(Puppet::FileBucket::File.indirection).to receive(:save)\n\n    filename = tmpfile('whit_test')\n    file = Puppet::Type.type(:file).new(:path => filename)\n\n    catalog = Puppet::Resource::Catalog.new\n    catalog.add_resource(file)\n\n    report = Puppet::Transaction::Report.new\n\n    catalog.apply(:report => report)\n    report.finalize_report\n\n    expect(report.resource_statuses.values.any? {|res| res.resource_type =~ /whit/i}).to be_falsey\n    expect(report.metrics['time'].values.any? {|metric| metric.first =~ /whit/i}).to be_falsey\n  end\n\n  describe \"when exclude_unchanged_resources is true\" do\n    let(:test_dir) { tmpdir('unchanged_resources') }\n    let(:test_dir2) { tmpdir('unchanged_resources') }\n    let(:test_file) { tmpfile('some_path')}\n    it 'should still list \"changed\" resource statuses but remove \"unchanged\"' do\n      transaction = apply_compiled_manifest(<<-END)\n        notify { \"hi\": } ~>\n        exec { \"/bin/this_command_does_not_exist\":\n          command => \"#{make_absolute('/bin/this_command_does_not_exist')}\",\n          refreshonly => true,\n        }\n        file { '#{test_dir}':\n          ensure => directory\n        }\n        file { 'failing_file':\n          path => '#{test_dir2}',\n          ensure => file\n        }\n        file { 'skipped_file':\n          path => '#{test_file}',\n          require => File[failing_file]\n        }\n      END\n      rs = transaction.report.to_data_hash['resource_statuses']\n      expect(rs[\"Notify[hi]\"]['out_of_sync']).to be true\n      expect(rs[\"Exec[/bin/this_command_does_not_exist]\"]['failed_to_restart']).to be true\n      expect(rs[\"File[failing_file]\"]['failed']).to be true\n      expect(rs[\"File[skipped_file]\"]['skipped']).to be true\n      expect(rs).to_not have_key([\"File[#{test_dir}]\"])\n    end\n  end\n\n  describe\"when exclude_unchanged_resources is false\" do\n    before do\n      Puppet[:exclude_unchanged_resources] = false\n    end\n\n    let(:test_dir) { tmpdir('unchanged_resources') }\n    let(:test_dir2) { tmpdir('unchanged_resources') }\n    let(:test_file) { tmpfile('some_path')}\n    it 'should list all resource statuses' do\n      transaction = apply_compiled_manifest(<<-END)\n        notify { \"hi\": } ~>\n        exec { \"/bin/this_command_does_not_exist\":\n          command => \"#{make_absolute('/bin/this_command_does_not_exist')}\",\n          refreshonly => true,\n        }\n        file { '#{test_dir}':\n          ensure => directory\n        }\n        file { 'failing_file':\n          path => '#{test_dir2}',\n          ensure => file\n        }\n        file { 'skipped_file':\n          path => '#{test_file}',\n          require => File[failing_file]\n        }\n      END\n      rs = transaction.report.to_data_hash['resource_statuses']\n      expect(rs[\"Notify[hi]\"]['out_of_sync']).to be true\n      expect(rs[\"Exec[/bin/this_command_does_not_exist]\"]['failed_to_restart']).to be true\n      expect(rs[\"File[failing_file]\"]['failed']).to be true\n      expect(rs[\"File[skipped_file]\"]['skipped']).to be true\n      expect(rs[\"File[#{test_dir}]\"]['changed']).to be false\n    end\n  end\n\n  describe \"when accepting logs\" do\n    before do\n      @report = Puppet::Transaction::Report.new\n    end\n\n    it \"should add new logs to the log list\" do\n      @report << \"log\"\n      expect(@report.logs[-1]).to eq(\"log\")\n    end\n\n    it \"should return self\" do\n      r = @report << \"log\"\n      expect(r).to equal(@report)\n    end\n  end\n\n  describe \"#as_logging_destination\" do\n    it \"makes the report collect logs during the block \" do\n      log_string = 'Hello test report!'\n      report = Puppet::Transaction::Report.new\n      report.as_logging_destination do\n        Puppet.err(log_string)\n      end\n\n      expect(report.logs.collect(&:message)).to include(log_string)\n    end\n  end\n\n  describe \"when accepting resource statuses\" do\n    before do\n      @report = Puppet::Transaction::Report.new\n    end\n\n    it \"should add each status to its status list\" do\n      status = double('status', :resource => \"foo\")\n      @report.add_resource_status status\n      expect(@report.resource_statuses[\"foo\"]).to equal(status)\n    end\n  end\n\n  describe \"when using the indirector\" do\n    it \"should redirect :save to the indirection\" do\n      allow(Facter).to receive(:value).and_return(\"eh\")\n      @indirection = double('indirection', :name => :report)\n      allow(Puppet::Transaction::Report).to receive(:indirection).and_return(@indirection)\n      report = Puppet::Transaction::Report.new\n      expect(@indirection).to receive(:save)\n      Puppet::Transaction::Report.indirection.save(report)\n    end\n\n    it \"should default to the 'processor' terminus\" do\n      expect(Puppet::Transaction::Report.indirection.terminus_class).to eq(:processor)\n    end\n\n    it \"should delegate its name attribute to its host method\" do\n      report = Puppet::Transaction::Report.new\n      expect(report).to receive(:host).and_return(\"me\")\n      expect(report.name).to eq(\"me\")\n    end\n  end\n\n  describe \"when computing exit status\" do\n    it \"should produce -1 if no metrics are present\" do\n      report = Puppet::Transaction::Report.new(\"apply\")\n      expect(report.exit_status).to eq(-1)\n    end\n\n    it \"should produce 2 if changes are present\" do\n      report = Puppet::Transaction::Report.new\n      report.add_metric(\"changes\", {\"total\" => 1})\n      report.add_metric(\"resources\", {\"failed\" => 0})\n      expect(report.exit_status).to eq(2)\n    end\n\n    it \"should produce 4 if failures are present\" do\n      report = Puppet::Transaction::Report.new\n      report.add_metric(\"changes\", {\"total\" => 0})\n      report.add_metric(\"resources\", {\"failed\" => 1})\n      expect(report.exit_status).to eq(4)\n    end\n\n    it \"should produce 4 if failures to restart are present\" do\n      report = Puppet::Transaction::Report.new\n      report.add_metric(\"changes\", {\"total\" => 0})\n      report.add_metric(\"resources\", {\"failed\" => 0})\n      report.add_metric(\"resources\", {\"failed_to_restart\" => 1})\n      expect(report.exit_status).to eq(4)\n    end\n\n    it \"should produce 6 if both changes and failures are present\" do\n      report = Puppet::Transaction::Report.new\n      report.add_metric(\"changes\", {\"total\" => 1})\n      report.add_metric(\"resources\", {\"failed\" => 1})\n      expect(report.exit_status).to eq(6)\n    end\n  end\n\n  describe \"before finalizing the report\" do\n    it \"should have a status of 'failed'\" do\n      report = Puppet::Transaction::Report.new\n      expect(report.status).to eq('failed')\n    end\n  end\n\n  describe \"when finalizing the report\" do\n    before do\n      @report = Puppet::Transaction::Report.new\n    end\n\n    def metric(name, value)\n      if metric = @report.metrics[name.to_s]\n        metric[value]\n      else\n        nil\n      end\n    end\n\n    def add_statuses(count, type = :file)\n      count.times do |i|\n        status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => make_absolute(\"/my/path#{i}\")))\n        yield status if block_given?\n        @report.add_resource_status status\n      end\n    end\n\n    it \"should be unchanged if there are no other failures or changes and the transaction completed\" do\n      @report.transaction_completed = true\n      @report.finalize_report\n\n      expect(@report.status).to eq(\"unchanged\")\n    end\n\n    it \"should be failed if there are no other failures or changes and the transaction did not complete\" do\n      @report.finalize_report\n\n      expect(@report.status).to eq(\"failed\")\n    end\n\n    [:time, :resources, :changes, :events].each do |type|\n      it \"should add #{type} metrics\" do\n        @report.finalize_report\n        expect(@report.metrics[type.to_s]).to be_instance_of(Puppet::Transaction::Metric)\n      end\n    end\n\n    describe \"for resources\" do\n      it \"should provide the total number of resources\" do\n        add_statuses(3)\n\n        @report.finalize_report\n        expect(metric(:resources, \"total\")).to eq(3)\n      end\n\n      Puppet::Resource::Status::STATES.each do |state|\n        it \"should provide the number of #{state} resources as determined by the status objects\" do\n          add_statuses(3) { |status| status.send(state.to_s + \"=\", true) }\n\n          @report.finalize_report\n          expect(metric(:resources, state.to_s)).to eq(3)\n        end\n\n        it \"should provide 0 for states not in status\" do\n          @report.finalize_report\n          expect(metric(:resources, state.to_s)).to eq(0)\n        end\n      end\n\n      it \"should mark the report as 'failed' if there are failing resources\" do\n        add_statuses(1) { |status| status.failed = true }\n        @report.transaction_completed = true\n        @report.finalize_report\n        expect(@report.status).to eq('failed')\n      end\n\n      it \"should mark the report as 'failed' if resources failed to restart\" do\n        add_statuses(1) { |status| status.failed_to_restart = true }\n        @report.finalize_report\n        expect(@report.status).to eq('failed')\n      end\n\n      it \"should mark the report as 'failed' if resources_failed_to_generate\" do\n        @report.resources_failed_to_generate = true\n        @report.transaction_completed = true\n        @report.finalize_report\n        expect(@report.status).to eq('failed')\n      end\n    end\n\n    describe \"for changes\" do\n      it \"should provide the number of changes from the resource statuses and mark the report as 'changed'\" do\n        add_statuses(3) { |status| 3.times { status << Puppet::Transaction::Event.new(:status => 'success') } }\n        @report.transaction_completed = true\n        @report.finalize_report\n        expect(metric(:changes, \"total\")).to eq(9)\n        expect(@report.status).to eq('changed')\n      end\n\n      it \"should provide a total even if there are no changes, and mark the report as 'unchanged'\" do\n        @report.transaction_completed = true\n        @report.finalize_report\n        expect(metric(:changes, \"total\")).to eq(0)\n        expect(@report.status).to eq('unchanged')\n      end\n    end\n\n    describe \"for times\" do\n      it \"should provide the total amount of time for each resource type\" do\n        add_statuses(3, :file) do |status|\n          status.evaluation_time = 1\n        end\n        add_statuses(3, :exec) do |status|\n          status.evaluation_time = 2\n        end\n        add_statuses(3, :tidy) do |status|\n          status.evaluation_time = 3\n        end\n\n        @report.finalize_report\n\n        expect(metric(:time, \"file\")).to eq(3)\n        expect(metric(:time, \"exec\")).to eq(6)\n        expect(metric(:time, \"tidy\")).to eq(9)\n      end\n\n      it \"should accrue times when called for one resource more than once\" do\n        @report.add_times :foobar, 50\n        @report.add_times :foobar, 30\n        @report.finalize_report\n        expect(metric(:time, \"foobar\")).to eq(80)\n      end\n\n      it \"should not accrue times when called for one resource more than once when set\" do\n        @report.add_times :foobar, 50, false\n        @report.add_times :foobar, 30, false\n        @report.finalize_report\n        expect(metric(:time, \"foobar\")).to eq(30)\n      end\n\n      it \"should add any provided times from external sources\" do\n        @report.add_times :foobar, 50\n        @report.finalize_report\n        expect(metric(:time, \"foobar\")).to eq(50)\n      end\n    end\n\n    describe \"for events\" do\n      it \"should provide the total number of events\" do\n        add_statuses(3) do |status|\n          3.times { |i| status.add_event(Puppet::Transaction::Event.new :status => 'success') }\n        end\n        @report.finalize_report\n        expect(metric(:events, \"total\")).to eq(9)\n      end\n\n      it \"should provide the total even if there are no events\" do\n        @report.finalize_report\n        expect(metric(:events, \"total\")).to eq(0)\n      end\n\n      Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name|\n        it \"should provide the number of #{status_name} events\" do\n          add_statuses(3) do |status|\n            3.times do |i|\n              event = Puppet::Transaction::Event.new\n              event.status = status_name\n              status.add_event(event)\n            end\n          end\n\n          @report.finalize_report\n          expect(metric(:events, status_name)).to eq(9)\n        end\n      end\n    end\n\n    describe \"for noop events\" do\n      it \"should have 'noop_pending == false' when no events are available\" do\n        add_statuses(3)\n        @report.finalize_report\n        expect(@report.noop_pending).to be_falsey\n      end\n\n      it \"should have 'noop_pending == false' when no 'noop' events are available\" do\n        add_statuses(3) do |status|\n          ['success', 'audit'].each do |status_name|\n            event = Puppet::Transaction::Event.new\n            event.status = status_name\n            status.add_event(event)\n          end\n        end\n        @report.finalize_report\n        expect(@report.noop_pending).to be_falsey\n      end\n\n      it \"should have 'noop_pending == true' when 'noop' events are available\" do\n        add_statuses(3) do |status|\n          ['success', 'audit', 'noop'].each do |status_name|\n            event = Puppet::Transaction::Event.new\n            event.status = status_name\n            status.add_event(event)\n          end\n        end\n        @report.finalize_report\n        expect(@report.noop_pending).to be_truthy\n      end\n\n      it \"should have 'noop_pending == true' when 'noop' and 'failure' events are available\" do\n        add_statuses(3) do |status|\n          ['success', 'failure', 'audit', 'noop'].each do |status_name|\n            event = Puppet::Transaction::Event.new\n            event.status = status_name\n            status.add_event(event)\n          end\n        end\n        @report.finalize_report\n        expect(@report.noop_pending).to be_truthy\n      end\n    end\n  end\n\n  describe \"when producing a summary\" do\n    before do\n      allow(Benchmark).to receive(:realtime).and_return(5.05683418)\n      resource = Puppet::Type.type(:notify).new(:name => \"testing\")\n      catalog = Puppet::Resource::Catalog.new\n      catalog.add_resource resource\n      catalog.version = 1234567\n      trans = catalog.apply\n\n      @report = trans.report\n      @report.add_times(:total, \"8675\") #Report total is now measured, not calculated.\n      @report.finalize_report\n    end\n\n    %w{changes time resources events version}.each do |main|\n      it \"should include the key #{main} in the raw summary hash\" do\n        expect(@report.raw_summary).to be_key main\n      end\n    end\n\n    it \"should include the last run time in the raw summary hash\" do\n      allow(Time).to receive(:now).and_return(Time.utc(2010,11,10,12,0,24))\n      expect(@report.raw_summary[\"time\"][\"last_run\"]).to eq(1289390424)\n    end\n\n    it \"should include all resource statuses\" do\n      resources_report = @report.raw_summary[\"resources\"]\n      Puppet::Resource::Status::STATES.each do |state|\n        expect(resources_report).to be_include(state.to_s)\n      end\n    end\n\n    %w{total failure success}.each do |r|\n      it \"should include event #{r}\" do\n        events_report = @report.raw_summary[\"events\"]\n        expect(events_report).to be_include(r)\n      end\n    end\n\n    it \"should include config version\" do\n      expect(@report.raw_summary[\"version\"][\"config\"]).to eq(1234567)\n    end\n\n    it \"should include puppet version\" do\n      expect(@report.raw_summary[\"version\"][\"puppet\"]).to eq(Puppet.version)\n    end\n\n    %w{Changes Total Resources Time Events}.each do |main|\n      it \"should include information on #{main} in the textual summary\" do\n        expect(@report.summary).to be_include(main)\n      end\n    end\n\n    it 'should sort total at the very end of the time metrics' do\n      expect(@report.summary).to match(/\n         Last run: \\d+\n   Transaction evaluation: \\d+.\\d{2}\n            Total: \\d+.\\d{2}\nVersion:\n/)\n    end\n  end\n\n  describe \"when outputting yaml\" do\n    it \"should not include @external_times\" do\n      report = Puppet::Transaction::Report.new\n      report.add_times('config_retrieval', 1.0)\n      expect(report.to_data_hash.keys).not_to include('external_times')\n    end\n\n    it \"should not include @resources_failed_to_generate\" do\n      report = Puppet::Transaction::Report.new\n      report.resources_failed_to_generate = true\n      expect(report.to_data_hash.keys).not_to include('resources_failed_to_generate')\n    end\n\n    it 'to_data_hash returns value that is instance of to Data' do\n      expect(Puppet::Pops::Types::TypeFactory.data.instance?(generate_report.to_data_hash)).to be_truthy\n    end\n  end\n\n  it \"defaults to serializing to json\" do\n    expect(Puppet::Transaction::Report.default_format).to eq(:json)\n  end\n\n  it \"supports both json and yaml\" do\n    # msgpack and pson are optional, so using include instead of eq\n    expect(Puppet::Transaction::Report.supported_formats).to include(:json, :yaml)\n  end\n\n  context 'can make a round trip through' do\n    before(:each) do\n      Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(Puppet.lookup(:current_environment)))\n    end\n\n    after(:each) { Puppet.pop_context }\n\n    it 'pson', if: Puppet.features.pson? do\n      report = generate_report\n\n      tripped = Puppet::Transaction::Report.convert_from(:pson, report.render)\n\n      expect_equivalent_reports(tripped, report)\n    end\n\n    it 'json' do\n      report = generate_report\n\n      tripped = Puppet::Transaction::Report.convert_from(:json, report.render)\n\n      expect_equivalent_reports(tripped, report)\n    end\n\n    it 'yaml' do\n      report = generate_report\n\n      yaml_output = report.render(:yaml)\n      tripped = Puppet::Transaction::Report.convert_from(:yaml, yaml_output)\n\n      expect(yaml_output).to match(/^--- /)\n      expect_equivalent_reports(tripped, report)\n    end\n  end\n\n  it \"generates json which validates against the report schema\" do\n    report = generate_report\n    expect(report.render).to validate_against('api/schemas/report.json')\n  end\n\n  it \"generates json for error report which validates against the report schema\" do\n    error_report = generate_report_with_error\n    expect(error_report.render).to validate_against('api/schemas/report.json')\n  end\n\n  def expect_equivalent_reports(tripped, report)\n    expect(tripped.host).to eq(report.host)\n    expect(tripped.time.to_i).to eq(report.time.to_i)\n    expect(tripped.configuration_version).to eq(report.configuration_version)\n    expect(tripped.transaction_uuid).to eq(report.transaction_uuid)\n    expect(tripped.job_id).to eq(report.job_id)\n    expect(tripped.code_id).to eq(report.code_id)\n    expect(tripped.catalog_uuid).to eq(report.catalog_uuid)\n    expect(tripped.cached_catalog_status).to eq(report.cached_catalog_status)\n    expect(tripped.report_format).to eq(report.report_format)\n    expect(tripped.puppet_version).to eq(report.puppet_version)\n    expect(tripped.status).to eq(report.status)\n    expect(tripped.transaction_completed).to eq(report.transaction_completed)\n    expect(tripped.environment).to eq(report.environment)\n    expect(tripped.corrective_change).to eq(report.corrective_change)\n\n    expect(logs_as_strings(tripped)).to eq(logs_as_strings(report))\n    expect(metrics_as_hashes(tripped)).to eq(metrics_as_hashes(report))\n    expect_equivalent_resource_statuses(tripped.resource_statuses, report.resource_statuses)\n  end\n\n  def logs_as_strings(report)\n    report.logs.map(&:to_report)\n  end\n\n  def metrics_as_hashes(report)\n    Hash[*report.metrics.collect do |name, m|\n      [name, { :name => m.name, :label => m.label, :value => m.value }]\n    end.flatten]\n  end\n\n  def expect_equivalent_resource_statuses(tripped, report)\n    expect(tripped.keys.sort).to eq(report.keys.sort)\n\n    tripped.each_pair do |name, status|\n      expected = report[name]\n\n      expect(status.title).to eq(expected.title)\n      expect(status.file).to eq(expected.file)\n      expect(status.line).to eq(expected.line)\n      expect(status.resource).to eq(expected.resource)\n      expect(status.resource_type).to eq(expected.resource_type)\n      expect(status.provider_used).to eq(expected.provider_used)\n      expect(status.containment_path).to eq(expected.containment_path)\n      expect(status.evaluation_time).to eq(expected.evaluation_time)\n      expect(status.tags).to eq(expected.tags)\n      expect(status.time.to_i).to eq(expected.time.to_i)\n      expect(status.failed).to eq(expected.failed)\n      expect(status.changed).to eq(expected.changed)\n      expect(status.out_of_sync).to eq(expected.out_of_sync)\n      expect(status.skipped).to eq(expected.skipped)\n      expect(status.change_count).to eq(expected.change_count)\n      expect(status.out_of_sync_count).to eq(expected.out_of_sync_count)\n      expect(status.events).to eq(expected.events)\n    end\n  end\n\n  def generate_report\n    # An Event cannot contain rich data - thus its \"to_data_hash\" stringifies the result.\n    # (This means it cannot be deserialized with intact data types).\n    # Here it is simulated that the values are after stringification.\n    stringifier = Puppet::Pops::Serialization::ToStringifiedConverter\n    event_hash = {\n      :audited => false,\n      :property => stringifier.convert('message'),\n      :previous_value => stringifier.convert(SemanticPuppet::VersionRange.parse('>=1.0.0')),\n      :desired_value => stringifier.convert(SemanticPuppet::VersionRange.parse('>=1.2.0')),\n      :historical_value => stringifier.convert(nil),\n      :message => stringifier.convert(\"defined 'message' as 'a resource'\"),\n      :name => :message_changed, # the name \n      :status => stringifier.convert('success'),\n    }\n\n    event = Puppet::Transaction::Event.new(**event_hash)\n\n    status = Puppet::Resource::Status.new(Puppet::Type.type(:notify).new(:title => \"a resource\"))\n    status.changed = true\n    status.add_event(event)\n\n    report = Puppet::Transaction::Report.new(1357986, 'test_environment', \"df34516e-4050-402d-a166-05b03b940749\", '42')\n    report << Puppet::Util::Log.new(:level => :warning, :message => \"log message\")\n    report.add_times(\"timing\", 4)\n    report.code_id = \"some code id\"\n    report.catalog_uuid = \"some catalog uuid\"\n    report.cached_catalog_status = \"not_used\"\n    report.server_used = \"test:000\"\n    report.add_resource_status(status)\n    report.transaction_completed = true\n    report.finalize_report\n    report\n  end\n\n  def generate_report_with_error\n    status = Puppet::Resource::Status.new(Puppet::Type.type(:notify).new(:title => \"a resource\"))\n    status.changed = true\n    status.failed_because(\"bad stuff happened\")\n\n    report = Puppet::Transaction::Report.new(1357986, 'test_environment', \"df34516e-4050-402d-a166-05b03b940749\", '42')\n    report << Puppet::Util::Log.new(:level => :warning, :message => \"log message\")\n    report.add_times(\"timing\", 4)\n    report.code_id = \"some code id\"\n    report.catalog_uuid = \"some catalog uuid\"\n    report.cached_catalog_status = \"not_used\"\n    report.server_used = \"test:000\"\n    report.add_resource_status(status)\n    report.transaction_completed = true\n    report.finalize_report\n    report\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/transaction/resource_harness_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/transaction/resource_harness'\n\ndescribe Puppet::Transaction::ResourceHarness do\n  include PuppetSpec::Files\n\n  before do\n    @mode_750 = Puppet::Util::Platform.windows? ? '644' : '750'\n    @mode_755 = Puppet::Util::Platform.windows? ? '644' : '755'\n    path = make_absolute(\"/my/file\")\n\n    @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)\n    @resource = Puppet::Type.type(:file).new :path => path\n    @harness = Puppet::Transaction::ResourceHarness.new(@transaction)\n    @current_state = Puppet::Resource.new(:file, path)\n    allow(@resource).to receive(:retrieve).and_return(@current_state)\n  end\n\n  it \"should accept a transaction at initialization\" do\n    harness = Puppet::Transaction::ResourceHarness.new(@transaction)\n    expect(harness.transaction).to equal(@transaction)\n  end\n\n  it \"should delegate to the transaction for its relationship graph\" do\n    expect(@transaction).to receive(:relationship_graph).and_return(\"relgraph\")\n    expect(Puppet::Transaction::ResourceHarness.new(@transaction).relationship_graph).to eq(\"relgraph\")\n  end\n\n  describe \"when evaluating a resource\" do\n    it \"produces a resource state that describes what happened with the resource\" do\n      status = @harness.evaluate(@resource)\n\n      expect(status.resource).to eq(@resource.ref)\n      expect(status).not_to be_failed\n      expect(status.events).to be_empty\n    end\n\n    it \"retrieves the current state of the resource\" do\n      expect(@resource).to receive(:retrieve).and_return(@current_state)\n\n      @harness.evaluate(@resource)\n    end\n\n    it \"produces a failure status for the resource when an error occurs\" do\n      the_message = \"retrieve failed in testing\"\n      expect(@resource).to receive(:retrieve).and_raise(ArgumentError.new(the_message))\n\n      status = @harness.evaluate(@resource)\n\n      expect(status).to be_failed\n      expect(events_to_hash(status.events).collect do |event|\n        { :@status => event[:@status], :@message => event[:@message] }\n      end).to eq([{ :@status => \"failure\", :@message => the_message }])\n    end\n\n    it \"records the time it took to evaluate the resource\" do\n      before = Time.now\n      status = @harness.evaluate(@resource)\n      after = Time.now\n\n      expect(status.evaluation_time).to be <= after - before\n    end\n  end\n\n  def events_to_hash(events)\n    events.map do |event|\n      hash = {}\n      event.instance_variables.each do |varname|\n        hash[varname.to_sym] = event.instance_variable_get(varname)\n      end\n      hash\n    end\n  end\n\n  def make_stub_provider\n    stubProvider = Class.new(Puppet::Type)\n    stubProvider.instance_eval do\n      initvars\n\n      newparam(:name) do\n        desc \"The name var\"\n        isnamevar\n      end\n\n      newproperty(:foo) do\n        desc \"A property that can be changed successfully\"\n        def sync\n        end\n\n        def retrieve\n          :absent\n        end\n\n        def insync?(reference_value)\n          false\n        end\n      end\n\n      newproperty(:bar) do\n        desc \"A property that raises an exception when you try to change it\"\n        def sync\n          raise ZeroDivisionError.new('bar')\n        end\n\n        def retrieve\n          :absent\n        end\n\n        def insync?(reference_value)\n          false\n        end\n      end\n\n      newproperty(:baz) do\n        desc \"A property that raises an Exception (not StandardError) when you try to change it\"\n        def sync\n          raise Exception.new('baz')\n        end\n\n        def retrieve\n          :absent\n        end\n\n        def insync?(reference_value)\n          false\n        end\n      end\n\n      newproperty(:brillig) do\n        desc \"A property that raises a StandardError exception when you test if it's insync?\"\n        def sync\n        end\n\n        def retrieve\n          :absent\n        end\n\n        def insync?(reference_value)\n          raise ZeroDivisionError.new('brillig')\n        end\n      end\n\n      newproperty(:slithy) do\n        desc \"A property that raises an Exception when you test if it's insync?\"\n        def sync\n        end\n\n        def retrieve\n          :absent\n        end\n\n        def insync?(reference_value)\n          raise Exception.new('slithy')\n        end\n      end\n    end\n    stubProvider\n  end\n\n\n  context \"interaction of ensure with other properties\" do\n    def an_ensurable_resource_reacting_as(behaviors)\n      stub_type = Class.new(Puppet::Type)\n      stub_type.class_eval do\n        initvars\n        ensurable do\n          def sync\n            (@resource.behaviors[:on_ensure] || proc {}).call\n          end\n\n          def insync?(value)\n            @resource.behaviors[:ensure_insync?]\n          end\n\n          def should_to_s(value)\n            (@resource.behaviors[:on_should_to_s] || proc { \"'#{value}'\" }).call\n          end\n\n          def change_to_s(value, should)\n            \"some custom insync message\"\n          end\n        end\n\n        newparam(:name) do\n          desc \"The name var\"\n          isnamevar\n        end\n\n        newproperty(:prop) do\n          newvalue(\"new\") do\n            #noop\n          end\n\n          def retrieve\n            \"old\"\n          end\n        end\n\n        attr_reader :behaviors\n\n        def initialize(options)\n          @behaviors = options.delete(:behaviors)\n          super\n        end\n\n        def exists?\n          @behaviors[:present?]\n        end\n\n        def present?(resource)\n          @behaviors[:present?]\n        end\n\n        def self.name\n          \"Testing\"\n        end\n      end\n      stub_type.new(:behaviors => behaviors,\n                    :ensure => :present,\n                    :name => \"testing\",\n                    :prop => \"new\")\n    end\n\n    it \"ensure errors means that the rest doesn't happen\" do\n      resource = an_ensurable_resource_reacting_as(:ensure_insync? => false, :on_ensure => proc { raise StandardError }, :present? => true)\n\n      status = @harness.evaluate(resource)\n\n      expect(status.events.length).to eq(1)\n      expect(status.events[0].property).to eq('ensure')\n      expect(status.events[0].name.to_s).to eq('Testing_created')\n      expect(status.events[0].status).to eq('failure')\n    end\n\n    it \"ensure fails completely means that the rest doesn't happen\" do\n      resource = an_ensurable_resource_reacting_as(:ensure_insync? => false, :on_ensure => proc { raise Exception }, :present? => false)\n\n      expect do\n        @harness.evaluate(resource)\n      end.to raise_error(Exception)\n\n      expect(@logs.first.message).to eq(\"change from 'absent' to 'present' failed: Exception\")\n      expect(@logs.first.level).to eq(:err)\n    end\n\n    it \"ensure succeeds means that the rest doesn't happen\" do\n      resource = an_ensurable_resource_reacting_as(:ensure_insync? => false, :on_ensure => proc { }, :present? => true)\n\n      status = @harness.evaluate(resource)\n\n      expect(status.events.length).to eq(1)\n      expect(status.events[0].property).to eq('ensure')\n      expect(status.events[0].name.to_s).to eq('Testing_created')\n      expect(status.events[0].status).to eq('success')\n      expect(status.events[0].message).to eq 'some custom insync message'\n    end\n\n    it \"ensure is in sync means that the rest *does* happen\" do\n      resource = an_ensurable_resource_reacting_as(:ensure_insync? => true, :present? => true)\n\n      status = @harness.evaluate(resource)\n\n      expect(status.events.length).to eq(1)\n      expect(status.events[0].property).to eq('prop')\n      expect(status.events[0].name.to_s).to eq('prop_changed')\n      expect(status.events[0].status).to eq('success')\n    end\n\n    it \"ensure is in sync but resource not present, means that the rest doesn't happen\" do\n      resource = an_ensurable_resource_reacting_as(:ensure_insync? => true, :present? => false)\n\n      status = @harness.evaluate(resource)\n\n      expect(status.events).to be_empty\n    end\n\n    it \"ensure errors in message still get a log entry\" do\n      resource = an_ensurable_resource_reacting_as(:ensure_insync? => false, :on_ensure => proc { raise StandardError }, :on_should_to_s => proc { raise StandardError }, :present? => true)\n\n      status = @harness.evaluate(resource)\n\n      expect(status.events.length).to eq(2)\n      testing_errors = status.events.find_all { |x| x.name.to_s == \"Testing_created\" }\n      resource_errors = status.events.find_all { |x| x.name.to_s == \"resource_error\" }\n      expect(testing_errors.length).to eq(1)\n      expect(resource_errors.length).to eq(1)\n      expect(testing_errors[0].message).not_to be_nil\n      expect(resource_errors[0].message).not_to eq(\"Puppet::Util::Log requires a message\")\n    end\n\n    it \"displays custom insync message in noop\" do\n      resource = an_ensurable_resource_reacting_as(:present? => true)\n      resource[:noop] = true\n      status = @harness.evaluate(resource)\n      sync_event = status.events[0]\n      expect(sync_event.message).to eq 'some custom insync message (noop)'\n    end\n  end\n\n  describe \"when a caught error occurs\" do\n    before :each do\n      stub_provider = make_stub_provider\n      resource = stub_provider.new :name => 'name', :foo => 1, :bar => 2\n      expect(resource).not_to receive(:err)\n      @status = @harness.evaluate(resource)\n    end\n\n    it \"should record previous successful events\" do\n      expect(@status.events[0].property).to eq('foo')\n      expect(@status.events[0].status).to eq('success')\n    end\n\n    it \"should record a failure event\" do\n      expect(@status.events[1].property).to eq('bar')\n      expect(@status.events[1].status).to eq('failure')\n    end\n  end\n\n  describe \"when an Exception occurs during sync\" do\n    before :each do\n      stub_provider = make_stub_provider\n      @resource = stub_provider.new :name => 'name', :baz => 1\n      expect(@resource).not_to receive(:err)\n    end\n\n    it \"should log and pass the exception through\" do\n      expect { @harness.evaluate(@resource) }.to raise_error(Exception, /baz/)\n      expect(@logs.first.message).to eq(\"change from 'absent' to 1 failed: baz\")\n      expect(@logs.first.level).to eq(:err)\n    end\n  end\n\n  describe \"when a StandardError exception occurs during insync?\" do\n    before :each do\n      stub_provider = make_stub_provider\n      @resource = stub_provider.new :name => 'name', :brillig => 1\n      expect(@resource).not_to receive(:err)\n    end\n\n    it \"should record a failure event\" do\n      @status = @harness.evaluate(@resource)\n      expect(@status.events[0].name.to_s).to eq('brillig_changed')\n      expect(@status.events[0].property).to eq('brillig')\n      expect(@status.events[0].status).to eq('failure')\n    end\n  end\n\n  describe \"when an Exception occurs during insync?\" do\n    before :each do\n      stub_provider = make_stub_provider\n      @resource = stub_provider.new :name => 'name', :slithy => 1\n      expect(@resource).not_to receive(:err)\n    end\n\n    it \"should log and pass the exception through\" do\n      expect { @harness.evaluate(@resource) }.to raise_error(Exception, /slithy/)\n      expect(@logs.first.message).to eq(\"change from 'absent' to 1 failed: slithy\")\n      expect(@logs.first.level).to eq(:err)\n    end\n  end\n\n  describe \"when auditing\" do\n    it \"should not call insync? on parameters that are merely audited\" do\n      stub_provider = make_stub_provider\n      resource = stub_provider.new :name => 'name', :audit => ['foo']\n      expect(resource.property(:foo)).not_to receive(:insync?)\n      status = @harness.evaluate(resource)\n\n      expect(status.events).to be_empty\n    end\n\n    it \"should be able to audit a file's group\" do # see bug #5710\n      test_file = tmpfile('foo')\n      File.open(test_file, 'w').close\n      resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['group'], :backup => false\n      expect(resource).not_to receive(:err) # make sure no exceptions get swallowed\n\n      status = @harness.evaluate(resource)\n\n      status.events.each do |event|\n        expect(event.status).to != 'failure'\n      end\n    end\n\n    it \"should not ignore microseconds when auditing a file's mtime\" do\n      test_file = tmpfile('foo')\n      File.open(test_file, 'w').close\n      resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['mtime'], :backup => false\n\n      # construct a property hash with nanosecond resolution as would be\n      # found on an ext4 file system\n      time_with_nsec_resolution = Time.at(1000, 123456.999)\n      current_from_filesystem    = {:mtime => time_with_nsec_resolution}\n\n      # construct a property hash with a 1 microsecond difference from above\n      time_with_usec_resolution = Time.at(1000, 123457.000)\n      historical_from_state_yaml = {:mtime => time_with_usec_resolution}\n\n      # set up the sequence of stubs; yeah, this is pretty\n      # brittle, so this might need to be adjusted if the\n      # resource_harness logic changes\n      expect(resource).to receive(:retrieve).and_return(current_from_filesystem)\n      allow(Puppet::Util::Storage).to receive(:cache).with(resource).\n        and_return(historical_from_state_yaml, current_from_filesystem, current_from_filesystem)\n\n      # there should be an audit change recorded, since the two\n      # timestamps differ by at least 1 microsecond\n      status = @harness.evaluate(resource)\n      expect(status.events).not_to be_empty\n      status.events.each do |event|\n        expect(event.message).to match(/audit change: previously recorded/)\n      end\n    end\n\n    it \"should ignore nanoseconds when auditing a file's mtime\" do\n      test_file = tmpfile('foo')\n      File.open(test_file, 'w').close\n      resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['mtime'], :backup => false\n\n      # construct a property hash with nanosecond resolution as would be\n      # found on an ext4 file system\n      time_with_nsec_resolution = Time.at(1000, 123456.789)\n      current_from_filesystem    = {:mtime => time_with_nsec_resolution}\n\n      # construct a property hash with the same timestamp as above,\n      # truncated to microseconds, as would be read back from state.yaml\n      time_with_usec_resolution = Time.at(1000, 123456.000)\n      historical_from_state_yaml = {:mtime => time_with_usec_resolution}\n\n      # set up the sequence of stubs; yeah, this is pretty\n      # brittle, so this might need to be adjusted if the\n      # resource_harness logic changes\n      expect(resource).to receive(:retrieve).and_return(current_from_filesystem)\n      allow(Puppet::Util::Storage).to receive(:cache).with(resource).\n        and_return(historical_from_state_yaml, current_from_filesystem, current_from_filesystem)\n\n      # there should be no audit change recorded, despite the\n      # slight difference in the two timestamps\n      status = @harness.evaluate(resource)\n      status.events.each do |event|\n        expect(event.message).not_to match(/audit change: previously recorded/)\n      end\n    end\n  end\n\n  describe \"handling sensitive properties\" do\n    describe 'when syncing' do\n      let(:test_file) do\n        tmpfile('foo').tap do |path|\n          File.open(path, 'w') { |fh| fh.write(\"goodbye world\") }\n        end\n      end\n\n      let(:resource) do\n        Puppet::Type.type(:file).new(:path => test_file, :backup => false, :content => \"hello world\").tap do |r|\n          r.parameter(:content).sensitive = true\n        end\n      end\n\n      it \"redacts event messages for sensitive properties\" do\n        status = @harness.evaluate(resource)\n        sync_event = status.events[0]\n        expect(sync_event.message).to eq 'changed [redacted] to [redacted]'\n      end\n\n      it \"redacts event contents for sensitive properties\" do\n        status = @harness.evaluate(resource)\n        sync_event = status.events[0]\n        expect(sync_event.previous_value).to eq '[redacted]'\n        expect(sync_event.desired_value).to eq '[redacted]'\n      end\n\n      it \"redacts event messages for sensitive properties when simulating noop changes\" do\n        resource[:noop] = true\n        status = @harness.evaluate(resource)\n        sync_event = status.events[0]\n        expect(sync_event.message).to eq 'current_value [redacted], should be [redacted] (noop)'\n      end\n\n      describe 'auditing' do\n        before do\n          resource[:audit] = ['content']\n        end\n\n        it \"redacts notices when a parameter is newly audited\" do\n          expect(resource.property(:content)).to receive(:notice).with(\"audit change: newly-recorded value [redacted]\")\n          @harness.evaluate(resource)\n        end\n\n        it \"redacts event messages for sensitive properties\" do\n          allow(Puppet::Util::Storage).to receive(:cache).with(resource).and_return({:content => \"historical world\"})\n          status = @harness.evaluate(resource)\n          sync_event = status.events[0]\n          expect(sync_event.message).to eq 'changed [redacted] to [redacted] (previously recorded value was [redacted])'\n        end\n\n        it \"redacts audit event messages for sensitive properties when simulating noop changes\" do\n          allow(Puppet::Util::Storage).to receive(:cache).with(resource).and_return({:content => \"historical world\"})\n          resource[:noop] = true\n          status = @harness.evaluate(resource)\n          sync_event = status.events[0]\n          expect(sync_event.message).to eq 'current_value [redacted], should be [redacted] (noop) (previously recorded value was [redacted])'\n        end\n\n        it \"redacts event contents for sensitive properties\" do\n          allow(Puppet::Util::Storage).to receive(:cache).with(resource).and_return({:content => \"historical world\"})\n          status = @harness.evaluate(resource)\n          sync_event = status.events[0]\n          expect(sync_event.historical_value).to eq '[redacted]'\n        end\n      end\n    end\n\n    describe 'handling errors' do\n      it \"redacts event messages generated when syncing a param raises a StandardError\" do\n        stub_provider = make_stub_provider\n        resource = stub_provider.new :name => 'name', :bar => 1\n        resource.parameter(:bar).sensitive = true\n        status = @harness.evaluate(resource)\n\n        error_event = status.events[0]\n        expect(error_event.message).to eq \"change from [redacted] to [redacted] failed: bar\"\n      end\n\n      it \"redacts event messages generated when syncing a param raises an Exception\" do\n        stub_provider = make_stub_provider\n        resource = stub_provider.new :name => 'name', :baz => 1\n        resource.parameter(:baz).sensitive = true\n\n        expect { @harness.evaluate(resource) }.to raise_error(Exception, 'baz')\n\n        expect(@logs.first.message).to eq \"change from [redacted] to [redacted] failed: baz\"\n      end\n    end\n  end\n\n  describe \"when finding the schedule\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @resource.catalog = @catalog\n    end\n\n    it \"should warn and return nil if the resource has no catalog\" do\n      @resource.catalog = nil\n      expect(@resource).to receive(:warning)\n\n      expect(@harness.schedule(@resource)).to be_nil\n    end\n\n    it \"should return nil if the resource specifies no schedule\" do\n      expect(@harness.schedule(@resource)).to be_nil\n    end\n\n    it \"should fail if the named schedule cannot be found\" do\n      @resource[:schedule] = \"whatever\"\n      expect(@resource).to receive(:fail)\n      @harness.schedule(@resource)\n    end\n\n    it \"should return the named schedule if it exists\" do\n      sched = Puppet::Type.type(:schedule).new(:name => \"sched\")\n      @catalog.add_resource(sched)\n      @resource[:schedule] = \"sched\"\n      expect(@harness.schedule(@resource).to_s).to eq(sched.to_s)\n    end\n  end\n\n  describe \"when determining if a resource is scheduled\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @resource.catalog = @catalog\n    end\n\n    it \"should return true if 'ignoreschedules' is set\" do\n      Puppet[:ignoreschedules] = true\n      @resource[:schedule] = \"meh\"\n      expect(@harness).to be_scheduled(@resource)\n    end\n\n    it \"should return true if the resource has no schedule set\" do\n      expect(@harness).to be_scheduled(@resource)\n    end\n\n    it \"should return the result of matching the schedule with the cached 'checked' time if a schedule is set\" do\n      t = Time.now\n      expect(@harness).to receive(:cached).with(@resource, :checked).and_return(t)\n\n      sched = Puppet::Type.type(:schedule).new(:name => \"sched\")\n      @catalog.add_resource(sched)\n      @resource[:schedule] = \"sched\"\n\n      expect(sched).to receive(:match?).with(t.to_i).and_return(\"feh\")\n\n      expect(@harness.scheduled?(@resource)).to eq(\"feh\")\n    end\n  end\n\n  it \"should be able to cache data in the Storage module\" do\n    data = {}\n    expect(Puppet::Util::Storage).to receive(:cache).with(@resource).and_return(data)\n    @harness.cache(@resource, :foo, \"something\")\n\n    expect(data[:foo]).to eq(\"something\")\n  end\n\n  it \"should be able to retrieve data from the cache\" do\n    data = {:foo => \"other\"}\n    expect(Puppet::Util::Storage).to receive(:cache).with(@resource).and_return(data)\n    expect(@harness.cached(@resource, :foo)).to eq(\"other\")\n  end\n\n  describe \"successful event message\" do\n    let(:test_file) do\n      tmpfile('foo').tap do |path|\n        File.open(path, 'w') { |fh| fh.write(\"old contents\") }\n      end\n    end\n\n    let(:resource) do\n      Puppet::Type.type(:file).new(:path => test_file, :backup => false, :content => \"hello world\")\n    end\n\n    it \"contains (corrective) when corrective change\" do\n      allow_any_instance_of(Puppet::Transaction::Event).to receive(:corrective_change).and_return(true)\n      status = @harness.evaluate(resource)\n      sync_event = status.events[0]\n      expect(sync_event.message).to match(/content changed '{sha256}[0-9a-f]+' to '{sha256}[0-9a-f]+' \\(corrective\\)/)\n    end\n\n    it \"contains no modifier when intentional change\" do\n      allow_any_instance_of(Puppet::Transaction::Event).to receive(:corrective_change).and_return(false)\n      status = @harness.evaluate(resource)\n      sync_event = status.events[0]\n      expect(sync_event.message).to match(/content changed '{sha256}[0-9a-f]+' to '{sha256}[0-9a-f]+'$/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/transaction_spec.rb",
    "content": "require 'spec_helper'\nrequire 'matchers/include_in_order'\nrequire 'puppet_spec/compiler'\n\nrequire 'puppet/transaction'\nrequire 'fileutils'\n\ndescribe Puppet::Transaction do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  def catalog_with_resource(resource)\n    catalog = Puppet::Resource::Catalog.new\n    catalog.add_resource(resource)\n    catalog\n  end\n\n  def transaction_with_resource(resource)\n    transaction = Puppet::Transaction.new(catalog_with_resource(resource), nil, Puppet::Graph::SequentialPrioritizer.new)\n    transaction\n  end\n\n  before(:all) do\n    Puppet::Type.newtype(:transaction_generator) do\n      newparam(:name) { isnamevar }\n\n      def generate\n      end\n    end\n  end\n\n  after(:all) do\n    Puppet::Type.rmtype(:transaction_generator)\n  end\n\n  before do\n    @basepath = make_absolute(\"/what/ever\")\n    @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, Puppet::Graph::SequentialPrioritizer.new)\n  end\n\n  it \"should be able to look resource status up by resource reference\" do\n    resource = Puppet::Type.type(:notify).new :title => \"foobar\"\n    transaction = transaction_with_resource(resource)\n    transaction.evaluate\n\n    expect(transaction.resource_status(resource.to_s)).to be_changed\n  end\n\n  # This will basically only ever be used during testing.\n  it \"should automatically create resource statuses if asked for a non-existent status\" do\n    resource = Puppet::Type.type(:notify).new :title => \"foobar\"\n    transaction = transaction_with_resource(resource)\n    expect(transaction.resource_status(resource)).to be_instance_of(Puppet::Resource::Status)\n  end\n\n  it \"should add provided resource statuses to its report\" do\n    resource = Puppet::Type.type(:notify).new :title => \"foobar\"\n    transaction = transaction_with_resource(resource)\n    transaction.evaluate\n\n    status = transaction.resource_status(resource)\n    expect(transaction.report.resource_statuses[resource.to_s]).to equal(status)\n  end\n\n  it \"should not consider there to be failed or failed_to_restart resources if no statuses are marked failed\" do\n    resource = Puppet::Type.type(:notify).new :title => \"foobar\"\n    transaction = transaction_with_resource(resource)\n    transaction.evaluate\n\n    expect(transaction).not_to be_any_failed\n  end\n\n  it \"should use the provided report object\" do\n    report = Puppet::Transaction::Report.new\n    transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, report, nil)\n\n    expect(transaction.report).to eq(report)\n  end\n\n  it \"should create a report if none is provided\" do\n    transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)\n\n    expect(transaction.report).to be_kind_of Puppet::Transaction::Report\n  end\n\n  describe \"when initializing\" do\n    it \"should create an event manager\" do\n      transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)\n      expect(transaction.event_manager).to be_instance_of(Puppet::Transaction::EventManager)\n      expect(transaction.event_manager.transaction).to equal(transaction)\n    end\n\n    it \"should create a resource harness\" do\n      transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)\n      expect(transaction.resource_harness).to be_instance_of(Puppet::Transaction::ResourceHarness)\n      expect(transaction.resource_harness.transaction).to equal(transaction)\n    end\n\n    it \"should set retrieval time on the report\" do\n      catalog = Puppet::Resource::Catalog.new\n      report = Puppet::Transaction::Report.new\n      catalog.retrieval_duration = 5\n\n      expect(report).to receive(:add_times).with(:config_retrieval, 5)\n\n      Puppet::Transaction.new(catalog, report, nil)\n    end\n  end\n\n  describe \"when evaluating a resource\" do\n    let(:resource) { Puppet::Type.type(:file).new :path => @basepath }\n\n    it \"should process events\" do\n      transaction = transaction_with_resource(resource)\n\n      expect(transaction).to receive(:skip?).with(resource).and_return(false)\n      expect(transaction.event_manager).to receive(:process_events).with(resource)\n\n      transaction.evaluate\n    end\n\n    describe \"and the resource should be skipped\" do\n      it \"should mark the resource's status as skipped\" do\n        transaction = transaction_with_resource(resource)\n\n        expect(transaction).to receive(:skip?).with(resource).and_return(true)\n\n        transaction.evaluate\n        expect(transaction.resource_status(resource)).to be_skipped\n      end\n\n      it \"does not process any scheduled events\" do\n        transaction = transaction_with_resource(resource)\n        expect(transaction).to receive(:skip?).with(resource).and_return(true)\n        expect(transaction.event_manager).not_to receive(:process_events).with(resource)\n        transaction.evaluate\n      end\n\n      it \"dequeues all events scheduled on that resource\" do\n        transaction = transaction_with_resource(resource)\n        expect(transaction).to receive(:skip?).with(resource).and_return(true)\n        expect(transaction.event_manager).to receive(:dequeue_all_events_for_resource).with(resource)\n        transaction.evaluate\n      end\n    end\n  end\n\n  describe \"when evaluating a skipped resource for corrective change it\" do\n    before :each do\n      # Enable persistence during tests\n      allow_any_instance_of(Puppet::Transaction::Persistence).to receive(:enabled?).and_return(true)\n    end\n\n    it \"should persist in the transactionstore\" do\n      Puppet[:transactionstorefile] = tmpfile('persistence_test')\n\n      resource = Puppet::Type.type(:notify).new :title => \"foobar\"\n      transaction = transaction_with_resource(resource)\n      transaction.evaluate\n      expect(transaction.resource_status(resource)).to be_changed\n\n      transaction = transaction_with_resource(resource)\n      expect(transaction).to receive(:skip?).with(resource).and_return(true)\n      expect(transaction.event_manager).not_to receive(:process_events).with(resource)\n      transaction.evaluate\n      expect(transaction.resource_status(resource)).to be_skipped\n\n      persistence = Puppet::Transaction::Persistence.new\n      persistence.load\n      expect(persistence.get_system_value(resource.ref, \"message\")).to eq([\"foobar\"])\n    end\n  end\n\n  describe \"when applying a resource\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @resource = Puppet::Type.type(:file).new :path => @basepath\n      @catalog.add_resource(@resource)\n      @status = Puppet::Resource::Status.new(@resource)\n\n      @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n      allow(@transaction.event_manager).to receive(:queue_events)\n    end\n\n    it \"should use its resource harness to apply the resource\" do\n      expect(@transaction.resource_harness).to receive(:evaluate).with(@resource)\n      @transaction.evaluate\n    end\n\n    it \"should add the resulting resource status to its status list\" do\n      allow(@transaction.resource_harness).to receive(:evaluate).and_return(@status)\n      @transaction.evaluate\n      expect(@transaction.resource_status(@resource)).to be_instance_of(Puppet::Resource::Status)\n    end\n\n    it \"should queue any events added to the resource status\" do\n      allow(@transaction.resource_harness).to receive(:evaluate).and_return(@status)\n      expect(@status).to receive(:events).and_return(%w{a b})\n      expect(@transaction.event_manager).to receive(:queue_events).with(@resource, [\"a\", \"b\"])\n      @transaction.evaluate\n    end\n\n    it \"should log and skip any resources that cannot be applied\" do\n      expect(@resource).to receive(:properties).and_raise(ArgumentError)\n      @transaction.evaluate\n      expect(@transaction.report.resource_statuses[@resource.to_s]).to be_failed\n    end\n\n    it \"should report any_failed if any resources failed\" do\n      expect(@resource).to receive(:properties).and_raise(ArgumentError)\n      @transaction.evaluate\n\n      expect(@transaction).to be_any_failed\n    end\n\n    it \"should report any_failed if any resources failed to restart\" do\n      @transaction.evaluate\n      @transaction.report.resource_statuses[@resource.to_s].failed_to_restart = true\n\n      expect(@transaction).to be_any_failed\n    end\n  end\n\n  describe \"#unblock\" do\n    let(:graph) { @transaction.relationship_graph }\n    let(:resource) { Puppet::Type.type(:notify).new(:name => 'foo') }\n\n    it \"should calculate the number of blockers if it's not known\" do\n      graph.add_vertex(resource)\n      3.times do |i|\n        other = Puppet::Type.type(:notify).new(:name => i.to_s)\n        graph.add_vertex(other)\n        graph.add_edge(other, resource)\n      end\n\n      graph.unblock(resource)\n\n      expect(graph.blockers[resource]).to eq(2)\n    end\n\n    it \"should decrement the number of blockers if there are any\" do\n      graph.blockers[resource] = 40\n\n      graph.unblock(resource)\n\n      expect(graph.blockers[resource]).to eq(39)\n    end\n\n    it \"should warn if there are no blockers\" do\n      vertex = double('vertex')\n      expect(vertex).to receive(:warning).with(\"appears to have a negative number of dependencies\")\n      graph.blockers[vertex] = 0\n\n      graph.unblock(vertex)\n    end\n\n    it \"should return true if the resource is now unblocked\" do\n      graph.blockers[resource] = 1\n\n      expect(graph.unblock(resource)).to eq(true)\n    end\n\n    it \"should return false if the resource is still blocked\" do\n      graph.blockers[resource] = 2\n\n      expect(graph.unblock(resource)).to eq(false)\n    end\n  end\n\n  describe \"when traversing\" do\n    let(:path) { tmpdir('eval_generate') }\n    let(:resource) { Puppet::Type.type(:file).new(:path => path, :recurse => true) }\n\n    before :each do\n      @transaction.catalog.add_resource(resource)\n    end\n\n    it \"should yield the resource even if eval_generate is called\" do\n      expect_any_instance_of(Puppet::Transaction::AdditionalResourceGenerator).to receive(:eval_generate).with(resource).and_return(true)\n\n      yielded = false\n      @transaction.evaluate do |res|\n        yielded = true if res == resource\n      end\n\n      expect(yielded).to eq(true)\n    end\n\n    it \"should prefetch the provider if necessary\" do\n      expect(@transaction).to receive(:prefetch_if_necessary).with(resource)\n\n      @transaction.evaluate {}\n    end\n\n    it \"traverses independent resources before dependent resources\" do\n      dependent = Puppet::Type.type(:notify).new(:name => \"hello\", :require => resource)\n      @transaction.catalog.add_resource(dependent)\n\n      seen = []\n      @transaction.evaluate do |res|\n        seen << res\n      end\n\n      expect(seen).to include_in_order(resource, dependent)\n    end\n\n    it \"traverses completely independent resources in the order they appear in the catalog\" do\n      independent = Puppet::Type.type(:notify).new(:name => \"hello\", :require => resource)\n      @transaction.catalog.add_resource(independent)\n\n      seen = []\n      @transaction.evaluate do |res|\n        seen << res\n      end\n\n      expect(seen).to include_in_order(resource, independent)\n    end\n\n    it \"should fail unsuitable resources and go on if it gets blocked\" do\n      dependent = Puppet::Type.type(:notify).new(:name => \"hello\", :require => resource)\n      @transaction.catalog.add_resource(dependent)\n\n      allow(resource).to receive(:suitable?).and_return(false)\n\n      evaluated = []\n      @transaction.evaluate do |res|\n        evaluated << res\n      end\n\n      # We should have gone on to evaluate the children\n      expect(evaluated).to eq([dependent])\n      expect(@transaction.resource_status(resource)).to be_failed\n    end\n  end\n\n  describe \"when generating resources before traversal\" do\n    let(:catalog) { Puppet::Resource::Catalog.new }\n    let(:transaction) { Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new) }\n    let(:generator) { Puppet::Type.type(:transaction_generator).new :title => \"generator\" }\n    let(:generated) do\n      %w[a b c].map { |name| Puppet::Type.type(:transaction_generator).new(:name => name) }\n    end\n\n    before :each do\n      catalog.add_resource generator\n      allow(generator).to receive(:generate).and_return(generated)\n      # avoid crude failures because of nil resources that result\n      # from implicit containment and lacking containers\n      allow(catalog).to receive(:container_of).and_return(generator)\n    end\n\n    it \"should call 'generate' on all created resources\" do\n      generated.each { |res| expect(res).to receive(:generate) }\n\n      transaction.evaluate\n    end\n\n    it \"should finish all resources\" do\n      generated.each { |res| expect(res).to receive(:finish) }\n\n      transaction.evaluate\n    end\n\n    it \"should copy all tags to the newly generated resources\" do\n      generator.tag('one', 'two')\n\n      transaction.evaluate\n\n      generated.each do |res|\n        expect(res).to be_tagged(*generator.tags)\n      end\n    end\n  end\n\n  describe \"after resource traversal\" do\n    let(:catalog) { Puppet::Resource::Catalog.new }\n    let(:prioritizer) { Puppet::Graph::SequentialPrioritizer.new }\n    let(:report) { Puppet::Transaction::Report.new }\n    let(:transaction) { Puppet::Transaction.new(catalog, report, prioritizer) }\n    let(:generator) { Puppet::Transaction::AdditionalResourceGenerator.new(catalog, nil, prioritizer) }\n\n    before :each do\n      generator = Puppet::Transaction::AdditionalResourceGenerator.new(catalog, nil, prioritizer)\n      allow(Puppet::Transaction::AdditionalResourceGenerator).to receive(:new).and_return(generator)\n    end\n\n    it \"should should query the generator for whether resources failed to generate\" do\n      relationship_graph = Puppet::Graph::RelationshipGraph.new(prioritizer)\n      allow(catalog).to receive(:relationship_graph).and_return(relationship_graph)\n\n      expect(relationship_graph).to receive(:traverse).ordered\n      expect(generator).to receive(:resources_failed_to_generate).ordered\n\n      transaction.evaluate\n    end\n\n    it \"should report that resources failed to generate\" do\n      expect(generator).to receive(:resources_failed_to_generate).and_return(true)\n      expect(report).to receive(:resources_failed_to_generate=).with(true)\n\n      transaction.evaluate\n    end\n\n    it \"should not report that resources failed to generate if none did\" do\n      expect(generator).to receive(:resources_failed_to_generate).and_return(false)\n      expect(report).not_to receive(:resources_failed_to_generate=)\n\n      transaction.evaluate\n    end\n  end\n\n  describe \"when performing pre-run checks\" do\n    let(:resource) { Puppet::Type.type(:notify).new(:title => \"spec\") }\n    let(:transaction) { transaction_with_resource(resource) }\n    let(:spec_exception) { 'spec-exception' }\n\n    it \"should invoke each resource's hook and apply the catalog after no failures\" do\n      expect(resource).to receive(:pre_run_check)\n\n      transaction.evaluate\n    end\n\n    it \"should abort the transaction on failure\" do\n      expect(resource).to receive(:pre_run_check).and_raise(Puppet::Error, spec_exception)\n\n      expect { transaction.evaluate }.to raise_error(Puppet::Error, /Some pre-run checks failed/)\n    end\n\n    it \"should log the resource-specific exception\" do\n      expect(resource).to receive(:pre_run_check).and_raise(Puppet::Error, spec_exception)\n      expect(resource).to receive(:log_exception).with(have_attributes(message: match(/#{spec_exception}/)))\n\n      expect { transaction.evaluate }.to raise_error(Puppet::Error)\n    end\n  end\n\n  describe \"when skipping a resource\" do\n    before :each do\n      @resource = Puppet::Type.type(:notify).new :name => \"foo\"\n      @catalog = Puppet::Resource::Catalog.new\n      @resource.catalog = @catalog\n      @transaction = Puppet::Transaction.new(@catalog, nil, nil)\n    end\n\n    it \"should skip resource with missing tags\" do\n      allow(@transaction).to receive(:missing_tags?).and_return(true)\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should skip resources tagged with the skip tags\" do\n      allow(@transaction).to receive(:skip_tags?).and_return(true)\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should skip unscheduled resources\" do\n      allow(@transaction).to receive(:scheduled?).and_return(false)\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should skip resources with failed dependencies\" do\n      allow(@transaction).to receive(:failed_dependencies?).and_return(true)\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should skip virtual resource\" do\n      allow(@resource).to receive(:virtual?).and_return(true)\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should skip device only resouce on normal host\" do\n      allow(@resource).to receive(:appliable_to_host?).and_return(false)\n      allow(@resource).to receive(:appliable_to_device?).and_return(true)\n      @transaction.for_network_device = false\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should not skip device only resouce on remote device\" do\n      allow(@resource).to receive(:appliable_to_host?).and_return(false)\n      allow(@resource).to receive(:appliable_to_device?).and_return(true)\n      @transaction.for_network_device = true\n      expect(@transaction).not_to be_skip(@resource)\n    end\n\n    it \"should skip host resouce on device\" do\n      allow(@resource).to receive(:appliable_to_host?).and_return(true)\n      allow(@resource).to receive(:appliable_to_device?).and_return(false)\n      @transaction.for_network_device = true\n      expect(@transaction).to be_skip(@resource)\n    end\n\n    it \"should not skip resouce available on both device and host when on device\" do\n      allow(@resource).to receive(:appliable_to_host?).and_return(true)\n      allow(@resource).to receive(:appliable_to_device?).and_return(true)\n      @transaction.for_network_device = true\n      expect(@transaction).not_to be_skip(@resource)\n    end\n\n    it \"should not skip resouce available on both device and host when on host\" do\n      allow(@resource).to receive(:appliable_to_host?).and_return(true)\n      allow(@resource).to receive(:appliable_to_device?).and_return(true)\n      @transaction.for_network_device = false\n      expect(@transaction).not_to be_skip(@resource)\n    end\n  end\n\n  describe \"when determining if tags are missing\" do\n    before :each do\n      @resource = Puppet::Type.type(:notify).new :name => \"foo\"\n      @catalog = Puppet::Resource::Catalog.new\n      @resource.catalog = @catalog\n      @transaction = Puppet::Transaction.new(@catalog, nil, nil)\n\n      allow(@transaction).to receive(:ignore_tags?).and_return(false)\n    end\n\n    it \"should not be missing tags if tags are being ignored\" do\n      expect(@transaction).to receive(:ignore_tags?).and_return(true)\n\n      expect(@resource).not_to receive(:tagged?)\n\n      expect(@transaction).not_to be_missing_tags(@resource)\n    end\n\n    it \"should not be missing tags if the transaction tags are empty\" do\n      @transaction.tags = []\n      expect(@resource).not_to receive(:tagged?)\n      expect(@transaction).not_to be_missing_tags(@resource)\n    end\n\n    it \"should otherwise let the resource determine if it is missing tags\" do\n      tags = ['one', 'two']\n      @transaction.tags = tags\n      expect(@transaction).to be_missing_tags(@resource)\n    end\n  end\n\n  describe \"when determining if a resource should be scheduled\" do\n    before :each do\n      @resource = Puppet::Type.type(:notify).new :name => \"foo\"\n      @catalog = Puppet::Resource::Catalog.new\n      @catalog.add_resource(@resource)\n      @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    end\n\n    it \"should always schedule resources if 'ignoreschedules' is set\" do\n      @transaction.ignoreschedules = true\n      expect(@transaction.resource_harness).not_to receive(:scheduled?)\n\n      @transaction.evaluate\n      expect(@transaction.resource_status(@resource)).to be_changed\n    end\n\n    it \"should let the resource harness determine whether the resource should be scheduled\" do\n      expect(@transaction.resource_harness).to receive(:scheduled?).with(@resource).and_return(\"feh\")\n\n      @transaction.evaluate\n    end\n  end\n\n  describe \"when prefetching\" do\n    let(:catalog) { Puppet::Resource::Catalog.new }\n    let(:transaction) { Puppet::Transaction.new(catalog, nil, nil) }\n    let(:resource) { Puppet::Type.type(:package).new :title => \"foo\", :name => \"bar\", :provider => :pkgng }\n    let(:resource2) { Puppet::Type.type(:package).new :title => \"blah\", :provider => :apt }\n\n    before :each do\n      allow(resource).to receive(:suitable?).and_return(true)\n      catalog.add_resource resource\n      catalog.add_resource resource2\n    end\n\n    it \"should match resources by name, not title\" do\n      expect(resource.provider.class).to receive(:prefetch).with({\"bar\" => resource})\n\n      transaction.prefetch_if_necessary(resource)\n    end\n\n    it \"should not prefetch a provider which has already been prefetched\" do\n      transaction.prefetched_providers[:package][:pkgng] = true\n\n      expect(resource.provider.class).not_to receive(:prefetch)\n\n      transaction.prefetch_if_necessary(resource)\n    end\n\n    it \"should mark the provider prefetched\" do\n      allow(resource.provider.class).to receive(:prefetch)\n\n      transaction.prefetch_if_necessary(resource)\n\n      expect(transaction.prefetched_providers[:package][:pkgng]).to be_truthy\n    end\n\n    it \"should prefetch resources without a provider if prefetching the default provider\" do\n      other = Puppet::Type.type(:package).new :name => \"other\"\n      other.instance_variable_set(:@provider, nil)\n\n      catalog.add_resource other\n\n      allow(resource.class).to receive(:defaultprovider).and_return(resource.provider.class)\n      expect(resource.provider.class).to receive(:prefetch).with({'bar' => resource, 'other' => other})\n\n      transaction.prefetch_if_necessary(resource)\n    end\n\n    it \"should not prefetch a provider which has failed\" do\n      transaction.prefetch_failed_providers[:package][:pkgng] = true\n\n      expect(resource.provider.class).not_to receive(:prefetch)\n\n      transaction.prefetch_if_necessary(resource)\n    end\n\n    it \"should not rescue SystemExit\" do\n      expect(resource.provider.class).to receive(:prefetch).and_raise(SystemExit, \"SystemMessage\")\n      expect { transaction.prefetch_if_necessary(resource) }.to raise_error(SystemExit, \"SystemMessage\")\n    end\n\n    it \"should mark resources as failed when prefetching raises LoadError\" do\n      expect(resource.provider.class).to receive(:prefetch).and_raise(LoadError, \"LoadMessage\")\n      transaction.prefetch_if_necessary(resource)\n      expect(transaction.prefetched_providers[:package][:pkgng]).to be_truthy\n    end\n\n    describe \"and prefetching raises Puppet::Error\" do\n      before :each do\n        expect(resource.provider.class).to receive(:prefetch).and_raise(Puppet::Error, \"message\")\n      end\n\n      it \"should rescue prefetch executions\" do\n        transaction.prefetch_if_necessary(resource)\n\n        expect(transaction.prefetched_providers[:package][:pkgng]).to be_truthy\n      end\n\n      it \"should mark resources as failed\", :unless => RUBY_PLATFORM == 'java' do\n        transaction.evaluate\n\n        expect(transaction.resource_status(resource).failed?).to be_truthy\n      end\n\n      it \"should mark a provider that has failed prefetch\" do\n        transaction.prefetch_if_necessary(resource)\n\n        expect(transaction.prefetch_failed_providers[:package][:pkgng]).to be_truthy\n      end\n\n      describe \"and new resources are generated\" do\n        let(:generator) { Puppet::Type.type(:transaction_generator).new :title => \"generator\" }\n        let(:generated) do\n          %w[a b c].map { |name| Puppet::Type.type(:package).new :title => \"foo\", :name => name, :provider => :apt }\n        end\n\n        before :each do\n          catalog.add_resource generator\n          allow(generator).to receive(:generate).and_return(generated)\n          allow(catalog).to receive(:container_of).and_return(generator)\n        end\n\n        it \"should not evaluate resources with a failed provider, even if the prefetch is rescued\" do\n          #Only the generator resource should be applied, all the other resources are failed, and skipped.\n          catalog.remove_resource resource2\n          expect(transaction).to receive(:apply).once\n\n          transaction.evaluate\n        end\n\n        it \"should not fail other resources added after the failing resource\", :unless => RUBY_PLATFORM == 'java' do\n          new_resource = Puppet::Type.type(:notify).new :name => \"baz\"\n          catalog.add_resource(new_resource)\n\n          transaction.evaluate\n\n          expect(transaction.resource_status(new_resource).failed?).to be_falsey\n        end\n\n        it \"should fail other resources that require the failing resource\" do\n          new_resource = Puppet::Type.type(:notify).new(:name => \"baz\", :require => resource)\n          catalog.add_resource(new_resource)\n\n          catalog.remove_resource resource2\n          expect(transaction).to receive(:apply).once\n\n          transaction.evaluate\n\n          expect(transaction.resource_status(resource).failed?).to be_truthy\n          expect(transaction.resource_status(new_resource).dependency_failed?).to be_truthy\n          expect(transaction.skip?(new_resource)).to be_truthy\n        end\n      end\n    end\n  end\n\n  describe \"during teardown\" do\n    let(:catalog) { Puppet::Resource::Catalog.new }\n    let(:transaction) do\n      Puppet::Transaction.new(catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    end\n\n    let(:teardown_type) do\n      Puppet::Type.newtype(:teardown_test) do\n        newparam(:name) {}\n      end\n    end\n\n    before :each do\n      teardown_type.provide(:teardown_provider) do\n        class << self\n          attr_reader :result\n\n          def post_resource_eval\n            @result = 'passed'\n          end\n        end\n      end\n    end\n\n    it \"should call ::post_resource_eval on provider classes that support it\" do\n      resource = teardown_type.new(:title => \"foo\", :provider => :teardown_provider)\n\n      transaction = transaction_with_resource(resource)\n      transaction.evaluate\n\n      expect(resource.provider.class.result).to eq('passed')\n    end\n\n    it \"should call ::post_resource_eval even if other providers' ::post_resource_eval fails\" do\n      teardown_type.provide(:always_fails) do\n        class << self\n          attr_reader :result\n\n          def post_resource_eval\n            @result = 'failed'\n            raise Puppet::Error, \"This provider always fails\"\n          end\n        end\n      end\n\n      good_resource = teardown_type.new(:title => \"bloo\", :provider => :teardown_provider)\n      bad_resource  = teardown_type.new(:title => \"blob\", :provider => :always_fails)\n\n      catalog.add_resource(bad_resource)\n      catalog.add_resource(good_resource)\n\n      transaction.evaluate\n\n      expect(good_resource.provider.class.result).to eq('passed')\n      expect(bad_resource.provider.class.result).to eq('failed')\n    end\n\n    it \"should call ::post_resource_eval even if one of the resources fails\" do\n      resource = teardown_type.new(:title => \"foo\", :provider => :teardown_provider)\n      allow(resource).to receive(:retrieve_resource).and_raise\n      catalog.add_resource resource\n\n      expect(resource.provider.class).to receive(:post_resource_eval)\n\n      transaction.evaluate\n    end\n\n    it \"should call Selinux.selabel_close in case Selinux is enabled\", :if => Puppet.features.posix? do\n      handle = double('selinux_handle')\n      selinux = class_double('selinux', is_selinux_enabled: 1, selabel_close: nil, selabel_open: handle, selabel_lookup: -1)\n      stub_const('Selinux', selinux)\n      stub_const('Selinux::SELABEL_CTX_FILE', 0)\n      resource = Puppet::Type.type(:file).new(:path => make_absolute(\"/tmp/foo\"))\n      transaction = transaction_with_resource(resource)\n\n      expect(Selinux).to receive(:selabel_close).with(handle)\n\n      transaction.evaluate\n    end\n  end\n\n  describe 'when checking application run state' do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::SequentialPrioritizer.new)\n    end\n\n    context \"when stop is requested\" do\n      before :each do\n        allow(Puppet::Application).to receive(:stop_requested?).and_return(true)\n      end\n\n      it 'should return true for :stop_processing?' do\n        expect(@transaction).to be_stop_processing\n      end\n\n      it 'always evaluates non-host_config catalogs' do\n        @catalog.host_config = false\n        expect(@transaction).not_to be_stop_processing\n      end\n    end\n\n    it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do\n      allow(Puppet::Application).to receive(:stop_requested?).and_return(false)\n      expect(@transaction.stop_processing?).to be_falsey\n    end\n\n    describe 'within an evaluate call' do\n      before do\n        @resource = Puppet::Type.type(:notify).new :title => \"foobar\"\n        @catalog.add_resource @resource\n      end\n\n      it 'should stop processing if :stop_processing? is true' do\n        allow(@transaction).to receive(:stop_processing?).and_return(true)\n        expect(@transaction).not_to receive(:eval_resource)\n        @transaction.evaluate\n      end\n\n      it 'should continue processing if :stop_processing? is false' do\n        allow(@transaction).to receive(:stop_processing?).and_return(false)\n        expect(@transaction).to receive(:eval_resource).and_return(nil)\n        @transaction.evaluate\n      end\n    end\n  end\n\n  it \"errors with a dependency cycle for a resource that requires itself\" do\n    expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:.*\\(Notify\\[cycle\\] => Notify\\[cycle\\]\\)/m)\n    expect do\n      apply_compiled_manifest(<<-MANIFEST)\n        notify { cycle: require => Notify[cycle] }\n      MANIFEST\n    end.to raise_error(Puppet::Error, 'One or more resource dependency cycles detected in graph')\n  end\n\n  it \"errors with a dependency cycle for a self-requiring resource also required by another resource\" do\n    expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:.*\\(Notify\\[cycle\\] => Notify\\[cycle\\]\\)/m)\n    expect do\n      apply_compiled_manifest(<<-MANIFEST)\n        notify { cycle: require => Notify[cycle] }\n        notify { other: require => Notify[cycle] }\n      MANIFEST\n    end.to raise_error(Puppet::Error, 'One or more resource dependency cycles detected in graph')\n  end\n\n  it \"errors with a dependency cycle for a resource that requires itself and another resource\" do\n    expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:.*\\(Notify\\[cycle\\] => Notify\\[cycle\\]\\)/m)\n    expect do\n      apply_compiled_manifest(<<-MANIFEST)\n        notify { cycle:\n          require => [Notify[other], Notify[cycle]]\n        }\n        notify { other: }\n      MANIFEST\n    end.to raise_error(Puppet::Error, 'One or more resource dependency cycles detected in graph')\n  end\n\n  it \"errors with a dependency cycle for a resource that is later modified to require itself\" do\n    expect(Puppet).to receive(:err).with(/Found 1 dependency cycle:.*\\(Notify\\[cycle\\] => Notify\\[cycle\\]\\)/m)\n    expect do\n      apply_compiled_manifest(<<-MANIFEST)\n        notify { cycle: }\n        Notify <| title == 'cycle' |> {\n          require => Notify[cycle]\n        }\n      MANIFEST\n    end.to raise_error(Puppet::Error, 'One or more resource dependency cycles detected in graph')\n  end\n\n  context \"when generating a report for a transaction with a dependency cycle\" do\n    let(:catalog) do\n      compile_to_ral(<<-MANIFEST)\n        notify { foo: require => Notify[bar] }\n        notify { bar: require => Notify[foo] }\n      MANIFEST\n    end\n\n    let(:prioritizer) { Puppet::Graph::SequentialPrioritizer.new }\n    let(:transaction) { Puppet::Transaction.new(catalog,\n                                          Puppet::Transaction::Report.new(\"apply\"),\n                                          prioritizer) }\n\n    before(:each) do\n      expect { transaction.evaluate }.to raise_error(Puppet::Error)\n      transaction.report.finalize_report\n    end\n\n    it \"should report resources involved in a dependency cycle as failed\" do\n      expect(transaction.report.resource_statuses['Notify[foo]']).to be_failed\n      expect(transaction.report.resource_statuses['Notify[bar]']).to be_failed\n    end\n\n    it \"should generate a failure event for a resource in a dependency cycle\" do\n      status = transaction.report.resource_statuses['Notify[foo]']\n      expect(status.events.first.status).to eq('failure')\n      expect(status.events.first.message).to eq('resource is part of a dependency cycle')\n    end\n\n    it \"should report that the transaction is failed\" do\n      expect(transaction.report.status).to eq('failed')\n    end\n  end\n\n  it \"reports a changed resource with a successful run\" do\n    transaction = apply_compiled_manifest(\"notify { one: }\")\n\n    expect(transaction.report.status).to eq('changed')\n    expect(transaction.report.resource_statuses['Notify[one]']).to be_changed\n  end\n\n  describe \"when interrupted\" do\n    it \"marks unprocessed resources as skipped\" do\n      Puppet::Application.stop!\n\n      transaction = apply_compiled_manifest(<<-MANIFEST)\n        notify { a: } ->\n        notify { b: }\n      MANIFEST\n\n      expect(transaction.report.resource_statuses['Notify[a]']).to be_skipped\n      expect(transaction.report.resource_statuses['Notify[b]']).to be_skipped\n    end\n  end\n\n  describe \"failed dependency is depended on multiple times\" do\n    it \"notifies the failed dependency once\" do\n      command_string = File.expand_path('/my/command')\n      allow(Puppet::Util::Execution).to receive(:execute).with([command_string]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      allow_any_instance_of(Puppet::Type::Notify).to receive(:send_log).with(:notice, \"Dependency Exec[exec1] has failures: true\")\n      allow_any_instance_of(Puppet::Type::Notify).to receive(:send_log).with(:notice, \"Dependency Exec[exec2] has failures: true\")\n      allow_any_instance_of(Puppet::Type::Notify).to receive(:send_log).with(:notice, \"Dependency Exec[exec3] has failures: true\")\n      allow_any_instance_of(Puppet::Type::Notify).to receive(:send_log).with(:notice, \"Dependency Exec[exec4] has failures: true\")\n      allow_any_instance_of(Puppet::Type::Notify).to receive(:send_log).with(:notice, \"Dependency Exec[exec5] has failures: true\")\n\n      times_send_log_with_skipping_called = 0\n      allow_any_instance_of(Puppet::Type::Notify).to receive(:send_log) {times_send_log_with_skipping_called += 1; nil}.with(:warning, \"Skipping because of failed dependencies\")\n\n      apply_compiled_manifest(<<-MANIFEST)\n        exec { ['exec1', 'exec2', 'exec3', 'exec4', 'exec5']:\n          command => '#{command_string}'\n        } ->\n        notify { ['notify1', 'notify2', 'notify3']: }\n      MANIFEST\n      expect(times_send_log_with_skipping_called).to eq(3)\n    end\n  end\n\n  describe \"failed dependency is depended on multiple times\" do\n    it \"notifies and warns the failed class dependency once\" do\n      Puppet.settings[:merge_dependency_warnings] = true\n\n      command_string = File.expand_path('/my/command')\n      allow(Puppet::Util::Execution).to receive(:execute).with([command_string]).and_raise(Puppet::ExecutionFailure, \"Failed\")\n\n      # Exec['exec1'] is outside of a class, so it's warning is not subject to being coalesced.\n      times_send_log_with_skipping_called = 0\n      allow_any_instance_of(Puppet::Type::Exec).to receive(:send_log) {times_send_log_with_skipping_called += 1; nil}.with(:warning, \"Skipping because of failed dependencies\")\n\n      # Class['declared_class'] depends upon Class['required_class'] which contains a resource with a failure.\n      times_send_log_with_class_dependency_called = 0\n      allow_any_instance_of(Puppet::Type).to receive(:send_log) {times_send_log_with_class_dependency_called += 1; nil}.with(:notice, \"Class dependency Exec[exec2] has failures: true\")\n      times_send_log_with_class_skipping_called = 0\n      allow_any_instance_of(Puppet::Type).to receive(:send_log) {times_send_log_with_class_skipping_called += 1; nil}.with(:warning, \"Skipping resources in class because of failed class dependencies\")\n\n      apply_compiled_manifest(<<-MANIFEST)\n        class required_class {\n          exec { 'exec2':\n            command => '#{command_string}'\n          }\n        }\n        class declared_class {\n          require required_class\n          exec { 'exec3':\n            command => '#{command_string}'\n          }\n          exec { 'exec4':\n            command => '#{command_string}'\n          }\n        }\n        exec { 'exec1':\n          command => '#{command_string}',\n          require => Exec['exec2']\n        }\n        include declared_class\n      MANIFEST\n\n      expect(times_send_log_with_skipping_called).to eq(1)\n      expect(times_send_log_with_class_dependency_called).to eq(1)\n      expect(times_send_log_with_class_skipping_called).to eq(1)\n    end\n  end\nend\n\ndescribe Puppet::Transaction, \" when determining tags\" do\n  before do\n    @config = Puppet::Resource::Catalog.new\n    @transaction = Puppet::Transaction.new(@config, nil, nil)\n  end\n\n  it \"should default to the tags specified in the :tags setting\" do\n    Puppet[:tags] = \"one\"\n    expect(@transaction).to be_tagged(\"one\")\n  end\n\n  it \"should split tags based on ','\" do\n    Puppet[:tags] = \"one,two\"\n    expect(@transaction).to be_tagged(\"one\")\n    expect(@transaction).to be_tagged(\"two\")\n  end\n\n  it \"should use any tags set after creation\" do\n    Puppet[:tags] = \"\"\n    @transaction.tags = %w{one two}\n    expect(@transaction).to be_tagged(\"one\")\n    expect(@transaction).to be_tagged(\"two\")\n  end\n\n  it \"should always convert assigned tags to an array\" do\n    @transaction.tags = \"one::two\"\n    expect(@transaction).to be_tagged(\"one::two\")\n  end\n\n  it \"should tag one::two only as 'one::two' and not 'one', 'two', and 'one::two'\" do\n    @transaction.tags = \"one::two\"\n    expect(@transaction).to be_tagged(\"one::two\")\n    expect(@transaction).to_not be_tagged(\"one\")\n    expect(@transaction).to_not be_tagged(\"two\")\n  end\n\n  it \"should accept a comma-delimited string\" do\n    @transaction.tags = \"one, two\"\n    expect(@transaction).to be_tagged(\"one\")\n    expect(@transaction).to be_tagged(\"two\")\n  end\n\n  it \"should accept an empty string\" do\n    @transaction.tags = \"one, two\"\n    expect(@transaction).to be_tagged(\"one\")\n    @transaction.tags = \"\"\n    expect(@transaction).not_to be_tagged(\"one\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/README.markdown",
    "content": "Resource Type Specs\n===================\n\nDefine specs for your resource types in this directory.\n"
  },
  {
    "path": "spec/unit/type/component_spec.rb",
    "content": "require 'spec_helper'\n\ncomponent = Puppet::Type.type(:component)\n\ndescribe component do\n  it \"should have a :name attribute\" do\n    expect(component.attrclass(:name)).not_to be_nil\n  end\n\n  it \"should use Class as its type when a normal string is provided as the title\" do\n    expect(component.new(:name => \"bar\").ref).to eq(\"Class[Bar]\")\n  end\n\n  it \"should always produce a resource reference string as its title\" do\n    expect(component.new(:name => \"bar\").title).to eq(\"Class[Bar]\")\n  end\n\n  it \"should have a reference string equivalent to its title\" do\n    comp = component.new(:name => \"Foo[bar]\")\n    expect(comp.title).to eq(comp.ref)\n  end\n\n  it \"should not fail when provided an invalid value\" do\n    comp = component.new(:name => \"Foo[bar]\")\n    expect { comp[:yayness] = \"ey\" }.not_to raise_error\n  end\n\n  it \"should return previously provided invalid values\" do\n    comp = component.new(:name => \"Foo[bar]\")\n    comp[:yayness] = \"eh\"\n    expect(comp[:yayness]).to eq(\"eh\")\n  end\n\n  it \"should correctly support metaparameters\" do\n    comp = component.new(:name => \"Foo[bar]\", :require => \"Foo[bar]\")\n    expect(comp.parameter(:require)).to be_instance_of(component.attrclass(:require))\n  end\n\n  describe \"when building up the path\" do\n    it \"should produce the class name if the component models a class\" do\n      expect(component.new(:name => \"Class[foo]\").pathbuilder).to eq([\"Foo\"])\n    end\n\n    it \"should produce the class name even for the class named main\" do\n      expect(component.new(:name => \"Class[main]\").pathbuilder).to eq([\"Main\"])\n    end\n\n    it \"should produce a resource reference if the component does not model a class\" do\n      expect(component.new(:name => \"Foo[bar]\").pathbuilder).to eq([\"Foo[bar]\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/exec_spec.rb",
    "content": "require 'spec_helper'\n\nRSpec.describe Puppet::Type.type(:exec) do\n  include PuppetSpec::Files\n\n  def exec_tester(command, exitstatus = 0, rest = {})\n    allow(Puppet.features).to receive(:root?).and_return(true)\n\n    output = rest.delete(:output) || ''\n\n    output = Puppet::Util::Execution::ProcessOutput.new(output, exitstatus)\n    tries  = rest[:tries] || 1\n\n    type_args = {\n      :name      => command,\n      :path      => @example_path,\n      :logoutput => false,\n      :loglevel  => :err,\n      :returns   => 0\n    }.merge(rest)\n\n    exec = Puppet::Type.type(:exec).new(type_args)\n    expect(Puppet::Util::Execution).to receive(:execute) do |cmd, options|\n      expect(cmd).to eq(command)\n      expect(options[:override_locale]).to eq(false)\n      expect(options).to have_key(:custom_environment)\n\n      output\n    end.exactly(tries).times\n\n    return exec\n  end\n\n  def exec_stub(options = {})\n    command = options.delete(:command) || @command\n    #unless_val = options.delete(:unless) || :true\n    type_args = {\n      :name   => command,\n      #:unless => unless_val,\n    }.merge(options)\n\n    # Chicken, meet egg:\n    # Provider methods have to be stubbed before resource init or checks fail\n    # We have to set 'unless' in resource init or it can not be marked sensitive correctly.\n    # So: we create a dummy ahead of time and use 'any_instance' to stub out provider methods.\n    dummy = Puppet::Type.type(:exec).new(:name => @command)\n    allow_any_instance_of(dummy.provider.class).to receive(:validatecmd)\n    allow_any_instance_of(dummy.provider.class).to receive(:checkexe).and_return(true)\n    pass_status = double('status', :exitstatus => 0, :split => [\"pass output\"])\n    fail_status = double('status', :exitstatus => 1, :split => [\"fail output\"])\n    allow(Puppet::Util::Execution).to receive(:execute).with(:true, anything).and_return(pass_status)\n    allow(Puppet::Util::Execution).to receive(:execute).with(:false, anything).and_return(fail_status)\n\n    test = Puppet::Type.type(:exec).new(type_args)\n\n    Puppet::Util::Log.level = :debug\n\n    return test\n  end\n\n  before do\n    @command = make_absolute('/bin/true whatever')\n    @executable = make_absolute('/bin/true')\n    @bogus_cmd = make_absolute('/bogus/cmd')\n  end\n\n  describe \"when not stubbing the provider\" do\n    before do\n      path = tmpdir('path')\n      ext = Puppet::Util::Platform.windows? ? '.exe' : ''\n      true_cmd = File.join(path, \"true#{ext}\")\n      false_cmd = File.join(path, \"false#{ext}\")\n\n      FileUtils.touch(true_cmd)\n      FileUtils.touch(false_cmd)\n\n      File.chmod(0755, true_cmd)\n      File.chmod(0755, false_cmd)\n\n      @example_path = [path]\n    end\n\n    it \"should return :executed_command as its event\" do\n      resource = Puppet::Type.type(:exec).new :command => @command\n      expect(resource.parameter(:returns).event.name).to eq(:executed_command)\n    end\n\n    describe \"when execing\" do\n      it \"should use the 'execute' method to exec\" do\n        expect(exec_tester(\"true\").refresh).to eq(:executed_command)\n      end\n\n      it \"should report a failure\" do\n        expect { exec_tester('false', 1).refresh }.\n          to raise_error(Puppet::Error, /^'false' returned 1 instead of/)\n      end\n\n      it \"should redact sensitive commands on failure\" do\n        expect { exec_tester('false', 1, :sensitive_parameters => [:command]).refresh }.\n            to raise_error(Puppet::Error, /^\\[command redacted\\] returned 1 instead of/)\n      end\n\n      it \"should not report a failure if the exit status is specified in a returns array\" do\n        expect { exec_tester(\"false\", 1, :returns => [0, 1]).refresh }.to_not raise_error\n      end\n\n      it \"should report a failure if the exit status is not specified in a returns array\" do\n        expect { exec_tester('false', 1, :returns => [0, 100]).refresh }.\n          to raise_error(Puppet::Error, /^'false' returned 1 instead of/)\n      end\n\n      it \"should report redact sensitive commands if the exit status is not specified in a returns array\" do\n        expect { exec_tester('false', 1, :returns => [0, 100], :sensitive_parameters => [:command]).refresh }.\n            to raise_error(Puppet::Error, /^\\[command redacted\\] returned 1 instead of/)\n      end\n\n      it \"should log the output on success\" do\n        output = \"output1\\noutput2\\n\"\n        exec_tester('false', 0, :output => output, :logoutput => true).refresh\n        output.split(\"\\n\").each do |line|\n          log = @logs.shift\n          expect(log.level).to eq(:err)\n          expect(log.message).to eq(line)\n        end\n      end\n\n      it \"should log the output on failure\" do\n        output = \"output1\\noutput2\\n\"\n        expect { exec_tester('false', 1, :output => output, :logoutput => true).refresh }.\n          to raise_error(Puppet::Error)\n\n        output.split(\"\\n\").each do |line|\n          log = @logs.shift\n          expect(log.level).to eq(:err)\n          expect(log.message).to eq(line)\n        end\n      end\n    end\n\n    describe \"when logoutput=>on_failure is set\" do\n      it \"should log the output on failure\" do\n        output = \"output1\\noutput2\\n\"\n        expect { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }.\n          to raise_error(Puppet::Error, /^'false' returned 1 instead of/)\n\n        output.split(\"\\n\").each do |line|\n          log = @logs.shift\n          expect(log.level).to eq(:err)\n          expect(log.message).to eq(line)\n        end\n      end\n\n      it \"should redact the sensitive command on failure\" do\n        output = \"output1\\noutput2\\n\"\n        expect { exec_tester('false', 1, :output => output, :logoutput => :on_failure, :sensitive_parameters => [:command]).refresh }.\n            to raise_error(Puppet::Error, /^\\[command redacted\\] returned 1 instead of/)\n\n        expect(@logs).to include(an_object_having_attributes(level: :err, message: '[output redacted]'))\n        expect(@logs).to_not include(an_object_having_attributes(message: /output1|output2/))\n      end\n\n      it \"should log the output on failure when returns is specified as an array\" do\n        output = \"output1\\noutput2\\n\"\n\n        expect {\n          exec_tester('false', 1, :output => output, :returns => [0, 100],\n               :logoutput => :on_failure).refresh\n        }.to raise_error(Puppet::Error, /^'false' returned 1 instead of/)\n\n        output.split(\"\\n\").each do |line|\n          log = @logs.shift\n          expect(log.level).to eq(:err)\n          expect(log.message).to eq(line)\n        end\n      end\n\n      it \"should redact the sensitive command on failure when returns is specified as an array\" do\n        output = \"output1\\noutput2\\n\"\n\n        expect {\n          exec_tester('false', 1, :output => output, :returns => [0, 100],\n                      :logoutput => :on_failure, :sensitive_parameters => [:command]).refresh\n        }.to raise_error(Puppet::Error, /^\\[command redacted\\] returned 1 instead of/)\n\n        expect(@logs).to include(an_object_having_attributes(level: :err, message: '[output redacted]'))\n        expect(@logs).to_not include(an_object_having_attributes(message: /output1|output2/))\n      end\n\n      it \"shouldn't log the output on success\" do\n        exec_tester('true', 0, :output => \"a\\nb\\nc\\n\", :logoutput => :on_failure).refresh\n        expect(@logs).to eq([])\n      end\n    end\n\n    it \"shouldn't log the output on success when non-zero exit status is in a returns array\" do\n      exec_tester(\"true\", 100, :output => \"a\\n\", :logoutput => :on_failure, :returns => [1, 100]).refresh\n      expect(@logs).to eq([])\n    end\n\n    describe \"when checks stop execution when debugging\" do\n      [[:unless, :true], [:onlyif, :false]].each do |check, result|\n        it \"should log a message with the command when #{check} is #{result}\" do\n          output = \"'#{@command}' won't be executed because of failed check '#{check}'\"\n          test = exec_stub({:command => @command, check => result})\n          expect(test.check_all_attributes).to eq(false)\n          expect(@logs).to include(an_object_having_attributes(level: :debug, message: output))\n        end\n\n        it \"should log a message with a redacted command and check if #{check} is sensitive\" do\n          output1 = \"Executing check '[redacted]'\"\n          output2 = \"'[command redacted]' won't be executed because of failed check '#{check}'\"\n          test = exec_stub({:command => @command, check => result, :sensitive_parameters => [check]})\n          expect(test.check_all_attributes).to eq(false)\n          expect(@logs).to include(an_object_having_attributes(level: :debug, message: output1))\n          expect(@logs).to include(an_object_having_attributes(level: :debug, message: output2))\n        end\n      end\n    end\n\n    describe \" when multiple tries are set,\" do\n      it \"should repeat the command attempt 'tries' times on failure and produce an error\" do\n        tries = 5\n        resource = exec_tester(\"false\", 1, :tries => tries, :try_sleep => 0)\n        expect { resource.refresh }.to raise_error(Puppet::Error)\n      end\n    end\n  end\n\n  it \"should be able to autorequire files mentioned in the command\" do\n    foo = make_absolute('/bin/foo')\n    catalog = Puppet::Resource::Catalog.new\n    tmp = Puppet::Type.type(:file).new(:name => foo)\n    execer = Puppet::Type.type(:exec).new(:name => foo)\n\n    catalog.add_resource tmp\n    catalog.add_resource execer\n    dependencies = execer.autorequire(catalog)\n\n    expect(dependencies.collect(&:to_s)).to eq([Puppet::Relationship.new(tmp, execer).to_s])\n  end\n\n  it \"should be able to autorequire files mentioned in the array command\" do\n    foo = make_absolute('/bin/foo')\n    catalog = Puppet::Resource::Catalog.new\n    tmp = Puppet::Type.type(:file).new(:name => foo)\n    execer = Puppet::Type.type(:exec).new(:name => 'test array', :command => [foo, 'bar'])\n\n    catalog.add_resource tmp\n    catalog.add_resource execer\n    dependencies = execer.autorequire(catalog)\n\n    expect(dependencies.collect(&:to_s)).to eq([Puppet::Relationship.new(tmp, execer).to_s])\n  end\n\n  it \"skips autorequire for deferred commands\" do\n    foo = make_absolute('/bin/foo')\n    catalog = Puppet::Resource::Catalog.new\n    tmp = Puppet::Type.type(:file).new(:name => foo)\n    execer = Puppet::Type.type(:exec).new(:name => 'test array', :command => Puppet::Pops::Evaluator::DeferredValue.new(nil))\n\n    catalog.add_resource tmp\n    catalog.add_resource execer\n    dependencies = execer.autorequire(catalog)\n\n    expect(dependencies.collect(&:to_s)).to eq([])\n  end\n\n  describe \"when handling the path parameter\" do\n    expect = %w{one two three four}\n    { \"an array\"                                      => expect,\n      \"a path-separator delimited list\"               => expect.join(File::PATH_SEPARATOR),\n      \"both array and path-separator delimited lists\" => [\"one\", \"two#{File::PATH_SEPARATOR}three\", \"four\"],\n    }.each do |test, input|\n      it \"should accept #{test}\" do\n        type = Puppet::Type.type(:exec).new(:name => @command, :path => input)\n        expect(type[:path]).to eq(expect)\n      end\n    end\n\n    describe \"on platforms where path separator is not :\" do\n      before :each do\n        stub_const('File::PATH_SEPARATOR', 'q')\n      end\n\n      it \"should use the path separator of the current platform\" do\n        type = Puppet::Type.type(:exec).new(:name => @command, :path => \"fooqbarqbaz\")\n        expect(type[:path]).to eq(%w[foo bar baz])\n      end\n    end\n  end\n\n  describe \"when setting user\" do\n    describe \"on POSIX systems\", :if => Puppet.features.posix? do\n      it \"should fail if we are not root\" do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n        expect {\n          Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => 'input')\n        }.to raise_error Puppet::Error, /Parameter user failed/\n      end\n\n      it \"accepts the current user\" do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n        allow(Etc).to receive(:getpwuid).and_return(Etc::Passwd.new('input'))\n\n        type = Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => 'input')\n\n        expect(type[:user]).to eq('input')\n      end\n\n      ['one', 2, 'root', 4294967295, 4294967296].each do |value|\n        it \"should accept '#{value}' as user if we are root\" do\n          allow(Puppet.features).to receive(:root?).and_return(true)\n          type = Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => value)\n          expect(type[:user]).to eq(value)\n        end\n      end\n    end\n\n    describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n      before :each do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n      end\n\n      it \"should reject user parameter\" do\n        expect {\n          Puppet::Type.type(:exec).new(:name => 'c:\\windows\\notepad.exe', :user => 'input')\n        }.to raise_error Puppet::Error, /Unable to execute commands as other users on Windows/\n      end\n    end\n  end\n\n  describe \"when setting group\" do\n    shared_examples_for \"exec[:group]\" do\n      ['one', 2, 'wheel', 4294967295, 4294967296].each do |value|\n        it \"should accept '#{value}' without error or judgement\" do\n          type = Puppet::Type.type(:exec).new(:name => @command, :group => value)\n          expect(type[:group]).to eq(value)\n        end\n      end\n    end\n\n    describe \"when running as root\" do\n      before(:each) do\n        allow(Puppet.features).to receive(:root?).and_return(true)\n      end\n      it_behaves_like \"exec[:group]\"\n    end\n\n    describe \"when not running as root\" do\n      before(:each) do\n        allow(Puppet.features).to receive(:root?).and_return(false)\n      end\n      it_behaves_like \"exec[:group]\"\n    end\n  end\n\n  describe \"when setting cwd\" do\n    it_should_behave_like \"all path parameters\", :cwd, :array => false do\n      def instance(path)\n        # Specify shell provider so we don't have to care about command validation\n        Puppet::Type.type(:exec).new(:name => @executable, :cwd => path, :provider => :shell)\n      end\n    end\n  end\n\n  shared_examples_for \"all exec command parameters\" do |param|\n    array_cmd = [\"/bin/example\", \"*\"]\n    array_cmd = [[\"/bin/example\", \"*\"]] if [:onlyif, :unless].include?(param)\n\n    commands = { \"relative\" => \"example\", \"absolute\" => \"/bin/example\" }\n    commands[\"array\"] = array_cmd\n\n    commands.sort.each do |name, command|\n      describe \"if command is #{name}\" do\n        before :each do\n          @param = param\n        end\n\n        def test(command, valid)\n          if @param == :name then\n            instance = Puppet::Type.type(:exec).new()\n          else\n            instance = Puppet::Type.type(:exec).new(:name => @executable)\n          end\n          if valid then\n            expect(instance.provider).to receive(:validatecmd).and_return(true)\n          else\n            expect(instance.provider).to receive(:validatecmd).and_raise(Puppet::Error, \"from a stub\")\n          end\n          instance[@param] = command\n        end\n\n        it \"should work if the provider calls the command valid\" do\n          expect { test(command, true) }.to_not raise_error\n        end\n\n        it \"should fail if the provider calls the command invalid\" do\n          expect { test(command, false) }.\n            to raise_error Puppet::Error, /Parameter #{@param} failed on Exec\\[.*\\]: from a stub/\n        end\n      end\n    end\n  end\n\n  shared_examples_for \"all exec command parameters that take arrays\" do |param|\n    [\n      %w{one two three},\n      [%w{one -a}, %w{two, -b}, 'three']\n    ].each do |input|\n      context \"when given #{input.inspect} as input\" do\n        let(:resource) { Puppet::Type.type(:exec).new(:name => @executable) }\n\n        it \"accepts the array when all commands return valid\" do\n          input = %w{one two three}\n          allow(resource.provider).to receive(:validatecmd).exactly(input.length).times.and_return(true)\n          resource[param] = input\n          expect(resource[param]).to eq(input)\n        end\n\n        it \"rejects the array when any commands return invalid\" do\n          input = %w{one two three}\n          allow(resource.provider).to receive(:validatecmd).with(input[0]).and_return(true)\n          allow(resource.provider).to receive(:validatecmd).with(input[1]).and_raise(Puppet::Error)\n\n          expect { resource[param] = input }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)\n        end\n\n        it \"stops at the first invalid command\" do\n          input = %w{one two three}\n          allow(resource.provider).to receive(:validatecmd).with(input[0]).and_raise(Puppet::Error)\n\n          expect(resource.provider).not_to receive(:validatecmd).with(input[1])\n          expect(resource.provider).not_to receive(:validatecmd).with(input[2])\n          expect { resource[param] = input }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)\n        end\n      end\n    end\n  end\n\n  describe \"when setting command\" do\n    subject { described_class.new(:name => @command) }\n    it \"fails when passed a Hash\" do\n      expect { subject[:command] = {} }.to raise_error Puppet::Error, /Command must be a String or Array<String>/\n    end\n  end\n\n  describe \"when setting refresh\" do\n    it_should_behave_like \"all exec command parameters\", :refresh\n  end\n\n  describe \"for simple parameters\" do\n    before :each do\n      @exec = Puppet::Type.type(:exec).new(:name => @executable)\n    end\n\n    describe \"when setting environment\" do\n      { \"single values\"   => \"foo=bar\",\n        \"multiple values\" => [\"foo=bar\", \"baz=quux\"],\n      }.each do |name, data|\n        it \"should accept #{name}\" do\n          @exec[:environment] = data\n          expect(@exec[:environment]).to eq(data)\n        end\n      end\n\n      { \"single values\" => \"foo\",\n        \"only values\"   => [\"foo\", \"bar\"],\n        \"any values\"    => [\"foo=bar\", \"baz\"]\n      }.each do |name, data|\n        it \"should reject #{name} without assignment\" do\n          expect { @exec[:environment] = data }.\n            to raise_error Puppet::Error, /Invalid environment setting/\n        end\n      end\n    end\n\n    describe \"when setting timeout\" do\n      [0, 0.1, 1, 10, 4294967295].each do |valid|\n        it \"should accept '#{valid}' as valid\" do\n          @exec[:timeout] = valid\n          expect(@exec[:timeout]).to eq(valid)\n        end\n\n        it \"should accept '#{valid}' in an array as valid\" do\n          @exec[:timeout] = [valid]\n          expect(@exec[:timeout]).to eq(valid)\n        end\n      end\n\n      ['1/2', '', 'foo', '5foo'].each do |invalid|\n        it \"should reject '#{invalid}' as invalid\" do\n          expect { @exec[:timeout] = invalid }.\n            to raise_error Puppet::Error, /The timeout must be a number/\n        end\n\n        it \"should reject '#{invalid}' in an array as invalid\" do\n          expect { @exec[:timeout] = [invalid] }.\n            to raise_error Puppet::Error, /The timeout must be a number/\n        end\n      end\n\n      describe 'when timeout is exceeded' do\n        subject do\n          ruby_path = Puppet::Util::Execution.ruby_path()\n          Puppet::Type.type(:exec).new(:name => \"#{ruby_path} -e 'sleep 1'\", :timeout => '0.1')\n        end\n\n        context 'on POSIX', :unless => Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n          it 'sends a SIGTERM and raises a Puppet::Error' do\n            expect(Process).to receive(:kill).at_least(:once)\n            expect { subject.refresh }.to raise_error Puppet::Error, \"Command exceeded timeout\"\n          end\n        end\n\n        context 'on Windows', :if => Puppet::Util::Platform.windows? do\n          it 'raises a Puppet::Error' do\n            expect { subject.refresh }.to raise_error Puppet::Error, \"Command exceeded timeout\"\n          end\n        end\n      end\n\n      it \"should convert timeout to a float\" do\n        command = make_absolute('/bin/false')\n        resource = Puppet::Type.type(:exec).new :command => command, :timeout => \"12\"\n        expect(resource[:timeout]).to be_a(Float)\n        expect(resource[:timeout]).to eq(12.0)\n      end\n\n      it \"should munge negative timeouts to 0.0\" do\n        command = make_absolute('/bin/false')\n        resource = Puppet::Type.type(:exec).new :command => command, :timeout => \"-12.0\"\n        expect(resource.parameter(:timeout).value).to be_a(Float)\n        expect(resource.parameter(:timeout).value).to eq(0.0)\n      end\n    end\n\n    describe \"when setting tries\" do\n      [1, 10, 4294967295].each do |valid|\n        it \"should accept '#{valid}' as valid\" do\n          @exec[:tries] = valid\n          expect(@exec[:tries]).to eq(valid)\n        end\n\n        if \"REVISIT: too much test log spam\" == \"a good thing\" then\n          it \"should accept '#{valid}' in an array as valid\" do\n            pending \"inconsistent, but this is not supporting arrays, unlike timeout\"\n            @exec[:tries] = [valid]\n            expect(@exec[:tries]).to eq(valid)\n          end\n        end\n      end\n\n      [-3.5, -1, 0, 0.2, '1/2', '1_000_000', '+12', '', 'foo'].each do |invalid|\n        it \"should reject '#{invalid}' as invalid\" do\n          expect { @exec[:tries] = invalid }.\n            to raise_error Puppet::Error, /Tries must be an integer/\n        end\n\n        if \"REVISIT: too much test log spam\" == \"a good thing\" then\n          it \"should reject '#{invalid}' in an array as invalid\" do\n            pending \"inconsistent, but this is not supporting arrays, unlike timeout\"\n            expect { @exec[:tries] = [invalid] }.\n              to raise_error Puppet::Error, /Tries must be an integer/\n          end\n        end\n      end\n    end\n\n    describe \"when setting try_sleep\" do\n      [0, 0.2, 1, 10, 4294967295].each do |valid|\n        it \"should accept '#{valid}' as valid\" do\n          @exec[:try_sleep] = valid\n          expect(@exec[:try_sleep]).to eq(valid)\n        end\n\n        if \"REVISIT: too much test log spam\" == \"a good thing\" then\n          it \"should accept '#{valid}' in an array as valid\" do\n            pending \"inconsistent, but this is not supporting arrays, unlike timeout\"\n            @exec[:try_sleep] = [valid]\n            expect(@exec[:try_sleep]).to eq(valid)\n          end\n        end\n      end\n\n      { -3.5        => \"cannot be a negative number\",\n        -1          => \"cannot be a negative number\",\n        '1/2'       => 'must be a number',\n        '1_000_000' => 'must be a number',\n        '+12'       => 'must be a number',\n        ''          => 'must be a number',\n        'foo'       => 'must be a number',\n      }.each do |invalid, error|\n        it \"should reject '#{invalid}' as invalid\" do\n          expect { @exec[:try_sleep] = invalid }.\n            to raise_error Puppet::Error, /try_sleep #{error}/\n        end\n\n        if \"REVISIT: too much test log spam\" == \"a good thing\" then\n          it \"should reject '#{invalid}' in an array as invalid\" do\n            pending \"inconsistent, but this is not supporting arrays, unlike timeout\"\n            expect { @exec[:try_sleep] = [invalid] }.\n              to raise_error Puppet::Error, /try_sleep #{error}/\n          end\n        end\n      end\n    end\n\n    describe \"when setting refreshonly\" do\n      [:true, :false].each do |value|\n        it \"should accept '#{value}'\" do\n          @exec[:refreshonly] = value\n          expect(@exec[:refreshonly]).to eq(value)\n        end\n      end\n\n      [1, 0, \"1\", \"0\", \"yes\", \"y\", \"no\", \"n\"].each do |value|\n        it \"should reject '#{value}'\" do\n          expect { @exec[:refreshonly] = value }.\n            to raise_error(Puppet::Error,\n              /Invalid value #{value.inspect}\\. Valid values are true, false/\n            )\n        end\n      end\n    end\n  end\n\n  describe \"when setting creates\" do\n    it_should_behave_like \"all path parameters\", :creates, :array => true do\n      def instance(path)\n        # Specify shell provider so we don't have to care about command validation\n        Puppet::Type.type(:exec).new(:name => @executable, :creates => path, :provider => :shell)\n      end\n    end\n  end\n\n  describe \"when setting unless\" do\n    it_should_behave_like \"all exec command parameters\", :unless\n    it_should_behave_like \"all exec command parameters that take arrays\", :unless\n  end\n\n  describe \"when setting onlyif\" do\n    it_should_behave_like \"all exec command parameters\", :onlyif\n    it_should_behave_like \"all exec command parameters that take arrays\", :onlyif\n  end\n\n  describe \"#check\" do\n    before :each do\n      @test = Puppet::Type.type(:exec).new(:name => @executable)\n    end\n\n    describe \":refreshonly\" do\n      { :true => false, :false => true }.each do |input, result|\n        it \"should return '#{result}' when given '#{input}'\" do\n          @test[:refreshonly] = input\n          expect(@test.check_all_attributes).to eq(result)\n        end\n      end\n    end\n\n    describe \":creates\" do\n      before :each do\n        @exist   = tmpfile('exist')\n        FileUtils.touch(@exist)\n        @unexist = tmpfile('unexist')\n      end\n\n      context \"with a single item\" do\n        it \"should run when the item does not exist\" do\n          @test[:creates] = @unexist\n          expect(@test.check_all_attributes).to eq(true)\n        end\n\n        it \"should not run when the item exists\" do\n          @test[:creates] = @exist\n          expect(@test.check_all_attributes).to eq(false)\n        end\n      end\n\n      context \"with an array with one item\" do\n        it \"should run when the item does not exist\" do\n          @test[:creates] = [@unexist]\n          expect(@test.check_all_attributes).to eq(true)\n        end\n\n        it \"should not run when the item exists\" do\n          @test[:creates] = [@exist]\n          expect(@test.check_all_attributes).to eq(false)\n        end\n      end\n\n      context \"with an array with multiple items\" do\n        it \"should run when all items do not exist\" do\n          @test[:creates] = [@unexist] * 3\n          expect(@test.check_all_attributes).to eq(true)\n        end\n\n        it \"should not run when one item exists\" do\n          @test[:creates] = [@unexist, @exist, @unexist]\n          expect(@test.check_all_attributes).to eq(false)\n        end\n\n        it \"should not run when all items exist\" do\n          @test[:creates] = [@exist] * 3\n        end\n      end\n\n      context \"when creates is being checked\" do\n        it \"should be logged to debug when the path does exist\" do\n          Puppet::Util::Log.level = :debug\n          @test[:creates] = @exist\n          expect(@test.check_all_attributes).to eq(false)\n          expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Checking that 'creates' path '#{@exist}' exists\"))\n        end\n\n        it \"should be logged to debug when the path does not exist\" do\n          Puppet::Util::Log.level = :debug\n          @test[:creates] = @unexist\n          expect(@test.check_all_attributes).to eq(true)\n          expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"Checking that 'creates' path '#{@unexist}' exists\"))\n        end\n      end\n    end\n\n    { :onlyif => { :pass => false, :fail => true  },\n      :unless => { :pass => true,  :fail => false },\n    }.each do |param, sense|\n      describe \":#{param}\" do\n        before :each do\n          @pass = make_absolute(\"/magic/pass\")\n          @fail = make_absolute(\"/magic/fail\")\n\n          @pass_status = double('status', :exitstatus => sense[:pass] ? 0 : 1)\n          @fail_status = double('status', :exitstatus => sense[:fail] ? 0 : 1)\n\n          allow(@test.provider).to receive(:checkexe).and_return(true)\n          [true, false].each do |check|\n            allow(@test.provider).to receive(:run).with(@pass, check).\n              and_return(['test output', @pass_status])\n            allow(@test.provider).to receive(:run).with(@fail, check).\n              and_return(['test output', @fail_status])\n          end\n        end\n\n        context \"with a single item\" do\n          it \"should run if the command exits non-zero\" do\n            @test[param] = @fail\n            expect(@test.check_all_attributes).to eq(true)\n          end\n\n          it \"should not run if the command exits zero\" do\n            @test[param] = @pass\n            expect(@test.check_all_attributes).to eq(false)\n          end\n        end\n\n        context \"with an array with a single item\" do\n          it \"should run if the command exits non-zero\" do\n            @test[param] = [@fail]\n            expect(@test.check_all_attributes).to eq(true)\n          end\n\n          it \"should not run if the command exits zero\" do\n            @test[param] = [@pass]\n            expect(@test.check_all_attributes).to eq(false)\n          end\n        end\n\n        context \"with an array with multiple items\" do\n          it \"should run if all the commands exits non-zero\" do\n            @test[param] = [@fail] * 3\n            expect(@test.check_all_attributes).to eq(true)\n          end\n\n          it \"should not run if one command exits zero\" do\n            @test[param] = [@pass, @fail, @pass]\n            expect(@test.check_all_attributes).to eq(false)\n          end\n\n          it \"should not run if all command exits zero\" do\n            @test[param] = [@pass] * 3\n            expect(@test.check_all_attributes).to eq(false)\n          end\n        end\n\n        context 'with an array of arrays with multiple items' do\n          before do\n            [true, false].each do |check|\n              allow(@test.provider).to receive(:run).with([@pass, '--flag'], check).\n                and_return(['test output', @pass_status])\n              allow(@test.provider).to receive(:run).with([@fail, '--flag'], check).\n                and_return(['test output', @fail_status])\n              allow(@test.provider).to receive(:run).with([@pass], check).\n                and_return(['test output', @pass_status])\n              allow(@test.provider).to receive(:run).with([@fail], check).\n                and_return(['test output', @fail_status])\n            end\n          end\n          it \"runs if all the commands exits non-zero\" do\n            @test[param] = [[@fail, '--flag'], [@fail], [@fail, '--flag']]\n            expect(@test.check_all_attributes).to eq(true)\n          end\n\n          it \"does not run if one command exits zero\" do\n            @test[param] = [[@pass, '--flag'], [@pass], [@fail, '--flag']]\n            expect(@test.check_all_attributes).to eq(false)\n          end\n\n          it \"does not run if all command exits zero\" do\n            @test[param] = [[@pass, '--flag'], [@pass], [@pass, '--flag']]\n            expect(@test.check_all_attributes).to eq(false)\n          end\n        end\n\n        it \"should emit output to debug\" do\n          Puppet::Util::Log.level = :debug\n          @test[param] = @fail\n          expect(@test.check_all_attributes).to eq(true)\n          expect(@logs.shift.message).to eq(\"test output\")\n        end\n\n        it \"should not emit output to debug if sensitive is true\" do\n          Puppet::Util::Log.level = :debug\n          @test[param] = @fail\n          allow(@test.parameters[param]).to receive(:sensitive).and_return(true)\n          expect(@test.check_all_attributes).to eq(true)\n          expect(@logs).not_to include(an_object_having_attributes(level: :debug, message: \"test output\"))\n          expect(@logs).to include(an_object_having_attributes(level: :debug, message: \"[output redacted]\"))\n        end\n      end\n    end\n  end\n\n  describe \"#retrieve\" do\n    before :each do\n      @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd)\n    end\n\n    it \"should return :notrun when check_all_attributes returns true\" do\n      allow(@exec_resource).to receive(:check_all_attributes).and_return(true)\n      expect(@exec_resource.retrieve[:returns]).to eq(:notrun)\n    end\n\n    it \"should return default exit code 0 when check_all_attributes returns false\" do\n      allow(@exec_resource).to receive(:check_all_attributes).and_return(false)\n      expect(@exec_resource.retrieve[:returns]).to eq(['0'])\n    end\n\n    it \"should return the specified exit code when check_all_attributes returns false\" do\n      allow(@exec_resource).to receive(:check_all_attributes).and_return(false)\n      @exec_resource[:returns] = 42\n      expect(@exec_resource.retrieve[:returns]).to eq([\"42\"])\n    end\n  end\n\n  describe \"#output\" do\n    before :each do\n      @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd)\n    end\n\n    it \"should return the provider's run output\" do\n      provider = double('provider')\n      status = double('process_status')\n      allow(status).to receive(:exitstatus).and_return(\"0\")\n      expect(provider).to receive(:run).and_return([\"silly output\", status])\n      allow(@exec_resource).to receive(:provider).and_return(provider)\n\n      @exec_resource.refresh\n      expect(@exec_resource.output).to eq('silly output')\n    end\n  end\n\n  describe \"#refresh\" do\n    before :each do\n      @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd)\n    end\n\n    it \"should call provider run with the refresh parameter if it is set\" do\n      myother_bogus_cmd = make_absolute('/myother/bogus/cmd')\n      provider = double('provider')\n      allow(@exec_resource).to receive(:provider).and_return(provider)\n      allow(@exec_resource).to receive(:[]).with(:refresh).and_return(myother_bogus_cmd)\n      expect(provider).to receive(:run).with(myother_bogus_cmd)\n\n      @exec_resource.refresh\n    end\n\n    it \"should call provider run with the specified command if the refresh parameter is not set\" do\n      provider = double('provider')\n      status = double('process_status')\n      allow(status).to receive(:exitstatus).and_return(\"0\")\n      expect(provider).to receive(:run).with(@bogus_cmd).and_return([\"silly output\", status])\n      allow(@exec_resource).to receive(:provider).and_return(provider)\n\n      @exec_resource.refresh\n    end\n\n    it \"should not run the provider if check_all_attributes is false\" do\n      allow(@exec_resource).to receive(:check_all_attributes).and_return(false)\n      provider = double('provider')\n      expect(provider).not_to receive(:run)\n      allow(@exec_resource).to receive(:provider).and_return(provider)\n\n      @exec_resource.refresh\n    end\n  end\n\n  describe \"relative and absolute commands vs path\" do\n    let :type do Puppet::Type.type(:exec) end\n    let :rel  do 'echo' end\n    let :abs  do make_absolute('/bin/echo') end\n    let :path do make_absolute('/bin') end\n\n    it \"should fail with relative command and no path\" do\n      expect { type.new(:command => rel) }.\n        to raise_error Puppet::Error, /no path was specified/\n    end\n\n    it \"should accept a relative command with a path\" do\n      expect(type.new(:command => rel, :path => path)).to be\n    end\n\n    it \"should accept an absolute command with no path\" do\n      expect(type.new(:command => abs)).to be\n    end\n\n    it \"should accept an absolute command with a path\" do\n      expect(type.new(:command => abs, :path => path)).to be\n    end\n  end\n  describe \"when providing a umask\" do\n    it \"should fail if an invalid umask is used\" do\n      resource = Puppet::Type.type(:exec).new :command => @command\n      expect { resource[:umask] = '0028'}.to raise_error(Puppet::ResourceError, /umask specification is invalid/)\n      expect { resource[:umask] = '28' }.to raise_error(Puppet::ResourceError, /umask specification is invalid/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/checksum_spec.rb",
    "content": "require 'spec_helper'\n\nchecksum = Puppet::Type.type(:file).attrclass(:checksum)\ndescribe checksum do\n  before do\n    @path = Puppet::Util::Platform.windows? ? \"c:/foo/bar\" : \"/foo/bar\"\n    @resource = Puppet::Type.type(:file).new :path => @path\n    @checksum = @resource.parameter(:checksum)\n  end\n\n  it \"should be a parameter\" do\n    expect(checksum.superclass).to eq(Puppet::Parameter)\n  end\n\n  it \"should use its current value when asked to sum content\" do\n    @checksum.value = :md5lite\n    expect(@checksum).to receive(:md5lite).with(\"foobar\").and_return(\"yay\")\n    @checksum.sum(\"foobar\")\n  end\n\n  it \"should use :sha256 to sum when no value is set\" do\n    expect(@checksum).to receive(:sha256).with(\"foobar\").and_return(\"yay\")\n    @checksum.sum(\"foobar\")\n  end\n\n  it \"should return the summed contents with a checksum label\" do\n    sum = Digest::MD5.hexdigest(\"foobar\")\n    @resource[:checksum] = :md5\n    expect(@checksum.sum(\"foobar\")).to eq(\"{md5}#{sum}\")\n  end\n\n  it \"when using digest_algorithm 'sha256' should return the summed contents with a checksum label\" do\n    sum = Digest::SHA256.hexdigest(\"foobar\")\n    @resource[:checksum] = :sha256\n    expect(@checksum.sum(\"foobar\")).to eq(\"{sha256}#{sum}\")\n  end\n\n  it \"when using digest_algorithm 'sha512' should return the summed contents with a checksum label\" do\n    sum = Digest::SHA512.hexdigest(\"foobar\")\n    @resource[:checksum] = :sha512\n    expect(@checksum.sum(\"foobar\")).to eq(\"{sha512}#{sum}\")\n  end\n\n  it \"when using digest_algorithm 'sha384' should return the summed contents with a checksum label\" do\n    sum = Digest::SHA384.hexdigest(\"foobar\")\n    @resource[:checksum] = :sha384\n    expect(@checksum.sum(\"foobar\")).to eq(\"{sha384}#{sum}\")\n  end\n\n  it \"should use :sha256 as its default type\" do\n    expect(@checksum.default).to eq(:sha256)\n  end\n\n  it \"should use its current value when asked to sum a file's content\" do\n    @checksum.value = :md5lite\n    expect(@checksum).to receive(:md5lite_file).with(@path).and_return(\"yay\")\n    @checksum.sum_file(@path)\n  end\n\n  it \"should use :sha256 to sum a file when no value is set\" do\n    expect(@checksum).to receive(:sha256_file).with(@path).and_return(\"yay\")\n    @checksum.sum_file(@path)\n  end\n\n  it \"should convert all sums to strings when summing files\" do\n    @checksum.value = :mtime\n    expect(@checksum).to receive(:mtime_file).with(@path).and_return(Time.now)\n    expect { @checksum.sum_file(@path) }.not_to raise_error\n  end\n\n  it \"should return the summed contents of a file with a checksum label\" do\n    @resource[:checksum] = :md5\n    expect(@checksum).to receive(:md5_file).and_return(\"mysum\")\n    expect(@checksum.sum_file(@path)).to eq(\"{md5}mysum\")\n  end\n\n  it \"should return the summed contents of a stream with a checksum label\" do\n    @resource[:checksum] = :md5\n    expect(@checksum).to receive(:md5_stream).and_return(\"mysum\")\n    expect(@checksum.sum_stream).to eq(\"{md5}mysum\")\n  end\n\n  it \"should yield the sum_stream block to the underlying checksum\" do\n    @resource[:checksum] = :md5\n    expect(@checksum).to receive(:md5_stream).and_yield(\"something\").and_return(\"mysum\")\n    @checksum.sum_stream do |sum|\n      expect(sum).to eq(\"something\")\n    end\n  end\n\n  it 'should use values allowed by the supported_checksum_types setting' do\n    values = checksum.value_collection.values.reject {|v| v == :none}.map {|v| v.to_s}\n    Puppet.settings[:supported_checksum_types] = values\n    expect(Puppet.settings[:supported_checksum_types]).to eq(values)\n  end\n\n  it 'rejects md5 checksums in FIPS mode' do\n    allow(Puppet::Util::Platform).to receive(:fips_enabled?).and_return(true)\n    expect {\n      @resource[:checksum] = :md5\n    }.to raise_error(Puppet::ResourceError,\n                     /Parameter checksum failed.* MD5 is not supported in FIPS mode/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/checksum_value_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:checksum_value), :uses_checksums => true do\n  include PuppetSpec::Files\n  include_context 'with supported checksum types'\n\n  let(:path) { tmpfile('foo_bar') }\n  let(:source_file) { file_containing('temp_foo', 'nothing at all') }\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n  let(:catalog) { Puppet::Resource::Catalog.new(:test, environment) }\n  let(:resource) { Puppet::Type.type(:file).new(:path => path, :catalog => catalog) }\n\n  it \"should be a property\" do\n    expect(described_class.superclass).to eq(Puppet::Property)\n  end\n\n  describe \"when retrieving the current checksum_value\" do\n    let(:checksum_value) { described_class.new(:resource => resource) }\n\n    it \"should not compute a checksum if source is absent\" do\n      expect(resource).not_to receive(:stat)\n      expect(checksum_value.retrieve).to be_nil\n    end\n\n    describe \"when using a source\" do\n      before do\n        resource[:source] = source_file\n      end\n\n      it \"should return :absent if the target does not exist\" do\n        expect(resource).to receive(:stat).and_return(nil)\n\n        expect(checksum_value.retrieve).to eq(:absent)\n      end\n\n      it \"should not manage content on directories\" do\n        stat = double('stat', :ftype => \"directory\")\n        expect(resource).to receive(:stat).and_return(stat)\n\n        expect(checksum_value.retrieve).to be_nil\n      end\n\n      it \"should not manage content on links\" do\n        stat = double('stat', :ftype => \"link\")\n        expect(resource).to receive(:stat).and_return(stat)\n\n        expect(checksum_value.retrieve).to be_nil\n      end\n\n      it \"should always return the checksum as a string\" do\n        resource[:checksum] = :mtime\n\n        stat = double('stat', :ftype => \"file\")\n        expect(resource).to receive(:stat).and_return(stat)\n\n        time = Time.now\n        expect(resource.parameter(:checksum)).to receive(:mtime_file).with(resource[:path]).and_return(time)\n\n        expect(checksum_value.retrieve).to eq(time.to_s)\n      end\n    end\n\n    with_digest_algorithms do\n      it \"should return the checksum of the target if it exists and is a normal file\" do\n        stat = double('stat', :ftype => \"file\")\n        expect(resource).to receive(:stat).and_return(stat)\n        expect(resource.parameter(:checksum)).to receive(\"#{digest_algorithm}_file\".intern).with(resource[:path]).and_return(\"mysum\")\n        resource[:source] = source_file\n\n        expect(checksum_value.retrieve).to eq(\"mysum\")\n      end\n    end\n  end\n\n  describe \"when testing whether the checksum_value is in sync\" do\n    let(:checksum_value) { described_class.new(:resource => resource) }\n\n    before do\n      resource[:ensure] = :file\n    end\n\n    it \"should return true if source is not specified\" do\n      checksum_value.should = \"foo\"\n      expect(checksum_value).to be_safe_insync(\"whatever\")\n    end\n\n    describe \"when a source is provided\" do\n      before do\n        resource[:source] = source_file\n      end\n\n      with_digest_algorithms do\n        before(:each) do\n          resource[:checksum] = digest_algorithm\n        end\n\n        it \"should return true if the resource shouldn't be a regular file\" do\n          expect(resource).to receive(:should_be_file?).and_return(false)\n          checksum_value.should = \"foo\"\n          expect(checksum_value).to be_safe_insync(\"whatever\")\n        end\n\n        it \"should return false if the current checksum_value is :absent\" do\n          checksum_value.should = \"foo\"\n          expect(checksum_value).not_to be_safe_insync(:absent)\n        end\n\n        it \"should return false if the file should be a file but is not present\" do\n          expect(resource).to receive(:should_be_file?).and_return(true)\n          checksum_value.should = \"foo\"\n\n          expect(checksum_value).not_to be_safe_insync(:absent)\n        end\n\n        describe \"and the file exists\" do\n          before do\n            allow(resource).to receive(:stat).and_return(double(\"stat\"))\n            checksum_value.should = \"somechecksum\"\n          end\n\n          it \"should return false if the current checksum_value is different from the desired checksum_value\" do\n            expect(checksum_value).not_to be_safe_insync(\"otherchecksum\")\n          end\n\n          it \"should return true if the current checksum_value is the same as the desired checksum_value\" do\n            expect(checksum_value).to be_safe_insync(\"somechecksum\")\n          end\n\n          it \"should include the diff module\" do\n            expect(checksum_value.respond_to?(\"diff\")).to eq(false)\n          end\n\n          [true, false].product([true, false]).each do |cfg, param|\n            describe \"and Puppet[:show_diff] is #{cfg} and show_diff => #{param}\" do\n              before do\n                Puppet[:show_diff] = cfg\n                allow(resource).to receive(:show_diff?).and_return(param)\n                resource[:loglevel] = \"debug\"\n              end\n\n              if cfg and param\n                it \"should display a diff\" do\n                  expect(checksum_value).to receive(:diff).and_return(\"my diff\").once\n                  expect(checksum_value).to receive(:debug).with(\"\\nmy diff\").once\n                  expect(checksum_value).not_to be_safe_insync(\"otherchecksum\")\n                end\n              else\n                it \"should not display a diff\" do\n                  expect(checksum_value).not_to receive(:diff)\n                  expect(checksum_value).not_to be_safe_insync(\"otherchecksum\")\n                end\n              end\n            end\n          end\n        end\n      end\n\n      let(:saved_time) { Time.now }\n      [:ctime, :mtime].each do |time_stat|\n        [[\"older\", -1, false], [\"same\", 0, true], [\"newer\", 1, true]].each do\n          |compare, target_time, success|\n          describe \"with #{compare} target #{time_stat} compared to source\" do\n            before do\n              resource[:checksum] = time_stat\n              checksum_value.should = saved_time.to_s\n            end\n\n            it \"should return #{success}\" do\n              if success\n                expect(checksum_value).to be_safe_insync((saved_time+target_time).to_s)\n              else\n                expect(checksum_value).not_to be_safe_insync((saved_time+target_time).to_s)\n              end\n            end\n          end\n        end\n\n        describe \"with #{time_stat}\" do\n          before do\n            resource[:checksum] = time_stat\n          end\n\n          it \"should not be insync if trying to create it\" do\n            checksum_value.should = saved_time.to_s\n            expect(checksum_value).not_to be_safe_insync(:absent)\n          end\n\n          it \"should raise an error if checksum_value is not a checksum\" do\n            checksum_value.should = \"some content\"\n            expect {\n              checksum_value.safe_insync?(saved_time.to_s)\n            }.to raise_error(/Resource with checksum_type #{time_stat} didn't contain a date in/)\n          end\n\n          it \"should not be insync even if checksum_value is the absent symbol\" do\n            checksum_value.should = :absent\n            expect(checksum_value).not_to be_safe_insync(:absent)\n          end\n        end\n      end\n\n      describe \"and :replace is false\" do\n        before do\n          allow(resource).to receive(:replace?).and_return(false)\n        end\n\n        it \"should be insync if the file exists and the checksum_value is different\" do\n          allow(resource).to receive(:stat).and_return(double('stat'))\n\n          expect(checksum_value).to be_safe_insync(\"whatever\")\n        end\n\n        it \"should be insync if the file exists and the checksum_value is right\" do\n          allow(resource).to receive(:stat).and_return(double('stat'))\n\n          expect(checksum_value).to be_safe_insync(\"something\")\n        end\n\n        it \"should not be insync if the file does not exist\" do\n          checksum_value.should = \"foo\"\n          expect(checksum_value).not_to be_safe_insync(:absent)\n        end\n      end\n    end\n  end\n\n  describe \"when testing whether the checksum_value is initialized in the resource and in sync\" do\n    CHECKSUM_TYPES_TO_TRY.each do |checksum_type, checksum|\n      describe \"sync with checksum type #{checksum_type} and the file exists\" do\n        before do\n          @new_resource = Puppet::Type.type(:file).new :ensure => :file, :path => path, :catalog => catalog,\n            :checksum_value => checksum, :checksum => checksum_type, :source => source_file\n          allow(@new_resource).to receive(:stat).and_return(double('stat'))\n        end\n\n        it \"should return false if the current checksum_value is different from the desired checksum_value\" do\n          expect(@new_resource.parameters[:checksum_value]).not_to be_safe_insync(\"abcdef\")\n        end\n\n        it \"should return true if the current checksum_value is the same as the desired checksum_value\" do\n          expect(@new_resource.parameters[:checksum_value]).to be_safe_insync(checksum)\n        end\n      end\n    end\n  end\n\n  describe \"when changing the checksum_value\" do\n    let(:checksum_value) { described_class.new(:resource => resource) }\n\n    before do\n      allow(resource).to receive(:[]).with(:path).and_return(\"/boo\")\n      allow(resource).to receive(:stat).and_return(\"eh\")\n    end\n\n    it \"should raise if source is absent\" do\n      expect(resource).not_to receive(:write)\n\n      expect { checksum_value.sync }.to raise_error \"checksum_value#sync should not be called without a source parameter\"\n    end\n\n    describe \"when using a source\" do\n      before do\n        resource[:source] = source_file\n      end\n\n      it \"should use the file's :write method to write the checksum_value\" do\n        expect(resource).to receive(:write).with(resource.parameter(:source))\n\n        checksum_value.sync\n      end\n\n      it \"should return :file_changed if the file already existed\" do\n        expect(resource).to receive(:stat).and_return(\"something\")\n        allow(resource).to receive(:write)\n        expect(checksum_value.sync).to eq(:file_changed)\n      end\n\n      it \"should return :file_created if the file did not exist\" do\n        expect(resource).to receive(:stat).and_return(nil)\n        allow(resource).to receive(:write)\n        expect(checksum_value.sync).to eq(:file_created)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/content_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:content), :uses_checksums => true do\n  include PuppetSpec::Files\n  include_context 'with supported checksum types'\n\n  let(:filename) { tmpfile('testfile') }\n  let(:environment) { Puppet::Node::Environment.create(:testing, []) }\n  let(:catalog) { Puppet::Resource::Catalog.new(:test, environment) }\n  let(:resource) { Puppet::Type.type(:file).new :path => filename, :catalog => catalog, :backup => 'puppet' }\n\n  before do\n    File.open(filename, 'w') {|f| f.write \"initial file content\"}\n  end\n\n  around do |example|\n    Puppet.override(:environments => Puppet::Environments::Static.new(environment)) do\n      example.run\n    end\n  end\n\n  describe \"when determining the actual content to write\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    it \"should use the set content if available\" do\n      content.should = \"ehness\"\n      expect(content.actual_content).to eq(\"ehness\")\n    end\n\n    it \"should not use the content from the source if the source is set\" do\n      expect(resource).not_to receive(:parameter).with(:source)\n\n      expect(content.actual_content).to be_nil\n    end\n  end\n\n  describe \"when setting the desired content\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    before do\n      allow_any_instance_of(Puppet::Type.type(:file)).to receive(:file).and_return('my/file.pp')\n      allow_any_instance_of(Puppet::Type.type(:file)).to receive(:line).and_return(5)\n    end\n\n    it \"should make the actual content available via an attribute\" do\n      content.should = \"this is some content\"\n\n      expect(content.actual_content).to eq(\"this is some content\")\n    end\n\n    with_digest_algorithms do\n      it \"should store the checksum as the desired content\" do\n        d = digest(\"this is some content\")\n\n        content.should = \"this is some content\"\n\n        expect(content.should).to eq(\"{#{digest_algorithm}}#{d}\")\n      end\n\n      it \"should not checksum 'absent'\" do\n        content.should = :absent\n\n        expect(content.should).to eq(:absent)\n      end\n\n      it \"should accept a checksum as the desired content\" do\n        d = digest(\"this is some content\")\n\n        string = \"{#{digest_algorithm}}#{d}\"\n        content.should = string\n\n        expect(content.should).to eq(string)\n      end\n    end\n\n    it \"should convert the value to ASCII-8BIT\" do\n      content.should= \"Let's make a \\u{2603}\"\n\n      expect(content.actual_content).to eq(\"Let's make a \\xE2\\x98\\x83\".force_encoding(Encoding::ASCII_8BIT))\n    end\n  end\n\n  describe \"when retrieving the current content\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    it \"should return :absent if the file does not exist\" do\n      expect(resource).to receive(:stat).and_return(nil)\n\n      expect(content.retrieve).to eq(:absent)\n    end\n\n    it \"should not manage content on directories\" do\n      stat = double('stat', :ftype => \"directory\")\n      expect(resource).to receive(:stat).and_return(stat)\n\n      expect(content.retrieve).to be_nil\n    end\n\n    it \"should not manage content on links\" do\n      stat = double('stat', :ftype => \"link\")\n      expect(resource).to receive(:stat).and_return(stat)\n\n      expect(content.retrieve).to be_nil\n    end\n\n    it \"should always return the checksum as a string\" do\n      resource[:checksum] = :mtime\n\n      stat = double('stat', :ftype => \"file\")\n      expect(resource).to receive(:stat).and_return(stat)\n\n      time = Time.now\n      expect(resource.parameter(:checksum)).to receive(:mtime_file).with(resource[:path]).and_return(time)\n\n      expect(content.retrieve).to eq(\"{mtime}#{time}\")\n    end\n\n    with_digest_algorithms do\n      it \"should return the checksum of the file if it exists and is a normal file\" do\n        stat = double('stat', :ftype => \"file\")\n        expect(resource).to receive(:stat).and_return(stat)\n        expect(resource.parameter(:checksum)).to receive(\"#{digest_algorithm}_file\".intern).with(resource[:path]).and_return(\"mysum\")\n\n        expect(content.retrieve).to eq(\"{#{digest_algorithm}}mysum\")\n      end\n    end\n  end\n\n  describe \"when testing whether the content is in sync\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    before do\n      resource[:ensure] = :file\n    end\n\n    with_digest_algorithms do\n      before(:each) do\n        resource[:checksum] = digest_algorithm\n      end\n\n      it \"should return true if the resource shouldn't be a regular file\" do\n        expect(resource).to receive(:should_be_file?).and_return(false)\n        content.should = \"foo\"\n        expect(content).to be_safe_insync(\"whatever\")\n      end\n\n      it \"should warn that no content will be synced to links when ensure is :present\" do\n        resource[:ensure] = :present\n        resource[:content] = 'foo'\n        allow(resource).to receive(:should_be_file?).and_return(false)\n        allow(resource).to receive(:stat).and_return(double(\"stat\", :ftype => \"link\"))\n\n        expect(resource).to receive(:warning).with(/Ensure set to :present but file type is/)\n\n        content.insync? :present\n      end\n\n      it \"should return false if the current content is :absent\" do\n        content.should = \"foo\"\n        expect(content).not_to be_safe_insync(:absent)\n      end\n\n      it \"should return false if the file should be a file but is not present\" do\n        expect(resource).to receive(:should_be_file?).and_return(true)\n        content.should = \"foo\"\n\n        expect(content).not_to be_safe_insync(:absent)\n      end\n\n      describe \"and the file exists\" do\n        before do\n          allow(resource).to receive(:stat).and_return(double(\"stat\"))\n          content.should = \"some content\"\n        end\n\n        it \"should return false if the current contents are different from the desired content\" do\n          expect(content).not_to be_safe_insync(\"other content\")\n        end\n\n        it \"should return true if the sum for the current contents is the same as the sum for the desired content\" do\n          expect(content).to be_safe_insync(\"{#{digest_algorithm}}\" + digest(\"some content\"))\n        end\n\n        it \"should include the diff module\" do\n          expect(content.respond_to?(\"diff\")).to eq(false)\n        end\n\n        describe \"showing the diff\" do\n          it \"doesn't show the diff when #show_diff? is false\" do\n            expect(content).to receive(:show_diff?).and_return(false)\n            expect(content).not_to receive(:diff)\n            expect(content).not_to be_safe_insync(\"other content\")\n          end\n\n          describe \"and #show_diff? is true\" do\n            before do\n              expect(content).to receive(:show_diff?).and_return(true)\n              resource[:loglevel] = \"debug\"\n            end\n\n            it \"prints the diff\" do\n              expect(content).to receive(:diff).and_return(\"my diff\")\n              expect(content).to receive(:debug).with(\"\\nmy diff\")\n              expect(content).not_to be_safe_insync(\"other content\")\n            end\n\n            it \"prints binary file notice if diff is not valid encoding\" do\n              expect(content).to receive(:diff).and_return(\"\\xc7\\xd1\\xfc\\x84\")\n              expect(content).to receive(:debug).with(/\\nBinary files #{filename} and .* differ/)\n              expect(content).not_to be_safe_insync(\"other content\")\n            end\n\n            it \"redacts the diff when the property is sensitive\" do\n              content.sensitive = true\n              expect(content).not_to receive(:diff)\n              expect(content).to receive(:debug).with(\"[diff redacted]\")\n              expect(content).not_to be_safe_insync(\"other content\")\n            end\n          end\n        end\n      end\n    end\n\n    let(:saved_time) { Time.now }\n    [:ctime, :mtime].each do |time_stat|\n      [[\"older\", -1, false], [\"same\", 0, true], [\"newer\", 1, true]].each do\n        |compare, target_time, success|\n        describe \"with #{compare} target #{time_stat} compared to source\" do\n          before do\n            resource[:checksum] = time_stat\n            resource[:source] = make_absolute('/temp/foo')\n            content.should = \"{#{time_stat}}#{saved_time}\"\n          end\n\n          it \"should return #{success}\" do\n            if success\n              expect(content).to be_safe_insync(\"{#{time_stat}}#{saved_time+target_time}\")\n            else\n              expect(content).not_to be_safe_insync(\"{#{time_stat}}#{saved_time+target_time}\")\n            end\n          end\n        end\n      end\n\n      describe \"with #{time_stat}\" do\n        before do\n          resource[:checksum] = time_stat\n          resource[:source] = make_absolute('/temp/foo')\n        end\n\n        it \"should not be insync if trying to create it\" do\n          content.should = \"{#{time_stat}}#{saved_time}\"\n          expect(content).not_to be_safe_insync(:absent)\n        end\n\n        it \"should raise an error if content is not a checksum\" do\n          content.should = \"some content\"\n          expect {\n            content.safe_insync?(\"{#{time_stat}}#{saved_time}\")\n          }.to raise_error(/Resource with checksum_type #{time_stat} didn't contain a date in/)\n        end\n\n        it \"should not be insync even if content is the absent symbol\" do\n          content.should = :absent\n          expect(content).not_to be_safe_insync(:absent)\n        end\n\n        it \"should warn that no content will be synced to links when ensure is :present\" do\n          resource[:ensure] = :present\n          resource[:content] = 'foo'\n          allow(resource).to receive(:should_be_file?).and_return(false)\n          allow(resource).to receive(:stat).and_return(double(\"stat\", :ftype => \"link\"))\n\n          expect(resource).to receive(:warning).with(/Ensure set to :present but file type is/)\n\n          content.insync? :present\n        end\n      end\n    end\n\n    describe \"and :replace is false\" do\n      before do\n        allow(resource).to receive(:replace?).and_return(false)\n      end\n\n      it \"should be insync if the file exists and the content is different\" do\n        allow(resource).to receive(:stat).and_return(double('stat'))\n\n        expect(content).to be_safe_insync(\"whatever\")\n      end\n\n      it \"should be insync if the file exists and the content is right\" do\n        allow(resource).to receive(:stat).and_return(double('stat'))\n\n        expect(content).to be_safe_insync(\"something\")\n      end\n\n      it \"should not be insync if the file does not exist\" do\n        content.should = \"foo\"\n        expect(content).not_to be_safe_insync(:absent)\n      end\n    end\n  end\n\n  describe \"when testing whether the content is initialized in the resource and in sync\" do\n    CHECKSUM_TYPES_TO_TRY.each do |checksum_type, checksum|\n      describe \"sync with checksum type #{checksum_type} and the file exists\" do\n        before do\n          @new_resource = Puppet::Type.type(:file).new :ensure => :file, :path => filename, :catalog => catalog,\n            :content => CHECKSUM_PLAINTEXT, :checksum => checksum_type\n          allow(@new_resource).to receive(:stat).and_return(double('stat'))\n        end\n\n        it \"should return false if the sum for the current contents are different from the desired content\" do\n          expect(@new_resource.parameters[:content]).not_to be_safe_insync(\"other content\")\n        end\n\n        it \"should return true if the sum for the current contents is the same as the sum for the desired content\" do\n          expect(@new_resource.parameters[:content]).to be_safe_insync(\"{#{checksum_type}}#{checksum}\")\n        end\n      end\n    end\n  end\n\n  describe \"determining if a diff should be shown\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    before do\n      Puppet[:show_diff] = true\n      resource[:show_diff] = true\n    end\n\n    it \"is true if there are changes and the global and per-resource show_diff settings are true\" do\n      expect(content.show_diff?(true)).to be_truthy\n    end\n\n    it \"is false if there are no changes\" do\n      expect(content.show_diff?(false)).to be_falsey\n    end\n\n    it \"is false if show_diff is globally disabled\" do\n      Puppet[:show_diff] = false\n      expect(content.show_diff?(false)).to be_falsey\n    end\n\n    it \"is false if show_diff is disabled on the resource\" do\n      resource[:show_diff] = false\n      expect(content.show_diff?(false)).to be_falsey\n    end\n  end\n\n  describe \"when changing the content\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    before do\n      allow(resource).to receive(:[]).with(:path).and_return(\"/boo\")\n      allow(resource).to receive(:stat).and_return(\"eh\")\n    end\n\n    it \"should use the file's :write method to write the content\" do\n      expect(resource).to receive(:write).with(content)\n\n      content.sync\n    end\n\n    it \"should return :file_changed if the file already existed\" do\n      expect(resource).to receive(:stat).and_return(\"something\")\n      allow(resource).to receive(:write)\n      expect(content.sync).to eq(:file_changed)\n    end\n\n    it \"should return :file_created if the file did not exist\" do\n      expect(resource).to receive(:stat).and_return(nil)\n      allow(resource).to receive(:write)\n      expect(content.sync).to eq(:file_created)\n    end\n  end\n\n  describe \"when writing\" do\n    let(:content) { described_class.new(:resource => resource) }\n\n    let(:fh) { File.open(filename, 'wb') }\n\n    before do\n      allow_any_instance_of(Puppet::Type.type(:file)).to receive(:file).and_return('my/file.pp')\n      allow_any_instance_of(Puppet::Type.type(:file)).to receive(:line).and_return(5)\n    end\n\n    it \"should attempt to read from the filebucket if no actual content nor source exists\" do\n      content.should = \"{md5}foo\"\n      allow_any_instance_of(content.resource.bucket.class).to receive(:getfile).and_return(\"foo\")\n      content.write(fh)\n      fh.close\n    end\n\n    describe \"from actual content\" do\n      before(:each) do\n        allow(content).to receive(:actual_content).and_return(\"this is content\")\n      end\n\n      it \"should write to the given file handle\" do\n        fh = double('filehandle')\n        expect(fh).to receive(:print).with(\"this is content\")\n        content.write(fh)\n      end\n\n      it \"should return the current checksum value\" do\n        expect(resource.parameter(:checksum)).to receive(:sum_stream).and_return(\"checksum\")\n        expect(content.write(fh)).to eq(\"checksum\")\n      end\n    end\n\n    describe \"from a file bucket\" do\n      it \"should fail if a file bucket cannot be retrieved\" do\n        content.should = \"{md5}foo\"\n        expect(content.resource).to receive(:bucket).and_return(nil)\n        expect { content.write(fh) }.to raise_error(Puppet::Error)\n      end\n\n      it \"should fail if the file bucket cannot find any content\" do\n        content.should = \"{md5}foo\"\n        bucket = double('bucket')\n        expect(content.resource).to receive(:bucket).and_return(bucket)\n        expect(bucket).to receive(:getfile).with(\"foo\").and_raise(\"foobar\")\n        expect { content.write(fh) }.to raise_error(Puppet::Error)\n      end\n\n      it \"should write the returned content to the file\" do\n        content.should = \"{md5}foo\"\n        bucket = double('bucket')\n        expect(content.resource).to receive(:bucket).and_return(bucket)\n        expect(bucket).to receive(:getfile).with(\"foo\").and_return(\"mycontent\")\n\n        fh = double('filehandle')\n        expect(fh).to receive(:print).with(\"mycontent\")\n        content.write(fh)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/ctime_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:ctime) do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  before do\n    @filename = tmpfile('ctime')\n    @resource = Puppet::Type.type(:file).new({:name => @filename})\n  end\n\n  it \"should be able to audit the file's ctime\" do\n    File.open(@filename, \"w\"){ }\n\n    @resource[:audit] = [:ctime]\n\n    # this .to_resource audit behavior is magical :-(\n    expect(@resource.to_resource[:ctime]).to eq(Puppet::FileSystem.stat(@filename).ctime.to_s)\n  end\n\n  it \"should return absent if auditing an absent file\" do\n    @resource[:audit] = [:ctime]\n\n    expect(@resource.to_resource[:ctime]).to eq(:absent)\n  end\n\n  it \"should prevent the user from trying to set the ctime\" do\n    expect {\n      @resource[:ctime] = Time.now.to_s\n    }.to raise_error(Puppet::Error, /ctime is read-only/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/type/file/ensure_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:ensure) do\n  include PuppetSpec::Files\n\n  let(:path) { tmpfile('file_ensure') }\n  let(:resource) { Puppet::Type.type(:file).new(:ensure => 'file', :path => path, :replace => true) }\n  let(:property) { resource.property(:ensure) }\n\n  it \"should be a subclass of Ensure\" do\n    expect(described_class.superclass).to eq(Puppet::Property::Ensure)\n  end\n\n  describe \"when retrieving the current state\" do\n    it \"should return :absent if the file does not exist\" do\n      expect(resource).to receive(:stat).and_return(nil)\n\n      expect(property.retrieve).to eq(:absent)\n    end\n\n    it \"should return the current file type if the file exists\" do\n      stat = double('stat', :ftype => \"directory\")\n      expect(resource).to receive(:stat).and_return(stat)\n\n      expect(property.retrieve).to eq(:directory)\n    end\n  end\n\n  describe \"when testing whether :ensure is in sync\" do\n    it \"should always be in sync if replace is 'false' unless the file is missing\" do\n      property.should = :file\n      expect(resource).to receive(:replace?).and_return(false)\n      expect(property.safe_insync?(:link)).to be_truthy\n    end\n\n    it \"should be in sync if :ensure is set to :absent and the file does not exist\" do\n      property.should = :absent\n\n      expect(property).to be_safe_insync(:absent)\n    end\n\n    it \"should not be in sync if :ensure is set to :absent and the file exists\" do\n      property.should = :absent\n\n      expect(property).not_to be_safe_insync(:file)\n    end\n\n    it \"should be in sync if a normal file exists and :ensure is set to :present\" do\n      property.should = :present\n\n      expect(property).to be_safe_insync(:file)\n    end\n\n    it \"should be in sync if a directory exists and :ensure is set to :present\" do\n      property.should = :present\n\n      expect(property).to be_safe_insync(:directory)\n    end\n\n    it \"should be in sync if a symlink exists and :ensure is set to :present\" do\n      property.should = :present\n\n      expect(property).to be_safe_insync(:link)\n    end\n\n    it \"should not be in sync if :ensure is set to :file and a directory exists\" do\n      property.should = :file\n\n      expect(property).not_to be_safe_insync(:directory)\n    end\n  end\n\n  describe \"#sync\" do\n    context \"directory\" do\n      before :each do\n        resource[:ensure] = :directory\n      end\n\n      it \"should raise if the parent directory doesn't exist\" do\n        newpath = File.join(path, 'nonexistentparent', 'newdir')\n        resource[:path] = newpath\n\n        expect {\n          property.sync\n        }.to raise_error(Puppet::Error, /Cannot create #{newpath}; parent directory #{File.dirname(newpath)} does not exist/)\n      end\n\n      it \"should accept octal mode as integer\" do\n        resource[:mode] = '0700'\n        expect(resource).to receive(:property_fix)\n        expect(Dir).to receive(:mkdir).with(path, 0700)\n\n        property.sync\n      end\n\n      it \"should accept octal mode as string\" do\n        resource[:mode] = \"700\"\n        expect(resource).to receive(:property_fix)\n        expect(Dir).to receive(:mkdir).with(path, 0700)\n\n        property.sync\n      end\n\n      it \"should accept octal mode as string with leading zero\" do\n        resource[:mode] = \"0700\"\n        expect(resource).to receive(:property_fix)\n        expect(Dir).to receive(:mkdir).with(path, 0700)\n\n        property.sync\n      end\n\n      it \"should accept symbolic mode\" do\n        resource[:mode] = \"u=rwx,go=x\"\n        expect(resource).to receive(:property_fix)\n        expect(Dir).to receive(:mkdir).with(path, 0711)\n\n        property.sync\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/group_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:group) do\n  include PuppetSpec::Files\n\n  let(:path) { tmpfile('mode_spec') }\n  let(:resource) { Puppet::Type.type(:file).new :path => path, :group => 'users' }\n  let(:group) { resource.property(:group) }\n\n  before :each do\n    # If the provider was already loaded without root, it won't have the\n    # feature, so we have to add it here to test.\n    Puppet::Type.type(:file).defaultprovider.has_feature :manages_ownership\n  end\n\n  describe \"#insync?\" do\n    before :each do\n      resource[:group] = ['foos', 'bars']\n\n      allow(resource.provider).to receive(:name2gid).with('foos').and_return(1001)\n      allow(resource.provider).to receive(:name2gid).with('bars').and_return(1002)\n    end\n\n    it \"should fail if a group's id can't be found by name\" do\n      allow(resource.provider).to receive(:name2gid).and_return(nil)\n\n      expect { group.insync?(5) }.to raise_error(/Could not find group foos/)\n    end\n\n    it \"should return false if a group's id can't be found by name in noop\" do\n      Puppet[:noop] = true\n      allow(resource.provider).to receive(:name2gid).and_return(nil)\n\n      expect(group.insync?('notcreatedyet')).to eq(false)\n    end\n\n    it \"should use the id for comparisons, not the name\" do\n      expect(group.insync?('foos')).to be_falsey\n    end\n\n    it \"should return true if the current group is one of the desired group\" do\n      expect(group.insync?(1001)).to be_truthy\n    end\n\n    it \"should return false if the current group is not one of the desired group\" do\n      expect(group.insync?(1003)).to be_falsey\n    end\n  end\n\n  %w[is_to_s should_to_s].each do |prop_to_s|\n    describe \"##{prop_to_s}\" do\n      it \"should use the name of the user if it can find it\" do\n        allow(resource.provider).to receive(:gid2name).with(1001).and_return('foos')\n\n        expect(group.send(prop_to_s, 1001)).to eq(\"'foos'\")\n      end\n\n      it \"should use the id of the user if it can't\" do\n        allow(resource.provider).to receive(:gid2name).with(1001).and_return(nil)\n\n        expect(group.send(prop_to_s, 1001)).to eq('1001')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/mode_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:mode) do\n  include PuppetSpec::Files\n\n  let(:path) { tmpfile('mode_spec') }\n  let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => '0644' }\n  let(:mode) { resource.property(:mode) }\n\n  describe \"#validate\" do\n    it \"should reject non-string values\" do\n      expect {\n        mode.value = 0755\n      }.to raise_error(Puppet::Error, /The file mode specification must be a string, not 'Integer'/)\n    end\n\n    it \"should accept values specified as octal numbers in strings\" do\n      expect { mode.value = '0755' }.not_to raise_error\n    end\n\n    it \"should accept valid symbolic strings\" do\n      expect { mode.value = 'g+w,u-x' }.not_to raise_error\n    end\n\n    it \"should not accept strings other than octal numbers\" do\n      expect do\n        mode.value = 'readable please!'\n      end.to raise_error(Puppet::Error, /The file mode specification is invalid/)\n    end\n  end\n\n  describe \"#munge\" do\n    # This is sort of a redundant test, but its spec is important.\n    it \"should return the value as a string\" do\n      expect(mode.munge('0644')).to be_a(String)\n    end\n\n    it \"should accept strings as arguments\" do\n      expect(mode.munge('0644')).to eq('644')\n    end\n\n    it \"should accept symbolic strings as arguments and return them intact\" do\n      expect(mode.munge('u=rw,go=r')).to eq('u=rw,go=r')\n    end\n\n    it \"should accept integers are arguments\" do\n      expect(mode.munge(0644)).to eq('644')\n    end\n  end\n\n  describe \"#dirmask\" do\n    before :each do\n      Dir.mkdir(path)\n    end\n\n    it \"should add execute bits corresponding to read bits for directories\" do\n      expect(mode.dirmask('0644')).to eq('755')\n    end\n\n    it \"should not add an execute bit when there is no read bit\" do\n      expect(mode.dirmask('0600')).to eq('700')\n    end\n\n    it \"should not add execute bits for files that aren't directories\" do\n      resource[:path] = tmpfile('other_file')\n      expect(mode.dirmask('0644')).to eq('0644')\n    end\n  end\n\n  describe \"#insync?\" do\n    it \"should return true if the mode is correct\" do\n      FileUtils.touch(path)\n\n      expect(mode).to be_insync('644')\n    end\n\n    it \"should return false if the mode is incorrect\" do\n      FileUtils.touch(path)\n\n      expect(mode).to_not be_insync('755')\n    end\n\n    it \"should return true if the file is a link and we are managing links\", :if => Puppet.features.manages_symlinks? do\n      Puppet::FileSystem.symlink('anything', path)\n\n      expect(mode).to be_insync('644')\n    end\n\n    describe \"with a symbolic mode\" do\n      let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'u+w,g-w' }\n      let(:mode_sym) { resource_sym.property(:mode) }\n\n      it \"should return true if the mode matches, regardless of other bits\" do\n        FileUtils.touch(path)\n\n        expect(mode_sym).to be_insync('644')\n      end\n\n      it \"should return false if the mode requires 0's where there are 1's\" do\n        FileUtils.touch(path)\n\n        expect(mode_sym).to_not be_insync('624')\n      end\n\n      it \"should return false if the mode requires 1's where there are 0's\" do\n        FileUtils.touch(path)\n\n        expect(mode_sym).to_not be_insync('044')\n      end\n    end\n  end\n\n  describe \"#retrieve\" do\n    it \"should return absent if the resource doesn't exist\" do\n      resource[:path] = File.expand_path(\"/does/not/exist\")\n      expect(mode.retrieve).to eq(:absent)\n    end\n\n    it \"should retrieve the directory mode from the provider\" do\n      Dir.mkdir(path)\n\n      expect(mode).to receive(:dirmask).with('644').and_return('755')\n      expect(resource.provider).to receive(:mode).and_return('755')\n\n      expect(mode.retrieve).to eq('755')\n    end\n\n    it \"should retrieve the file mode from the provider\" do\n      FileUtils.touch(path)\n\n      expect(mode).to receive(:dirmask).with('644').and_return('644')\n      expect(resource.provider).to receive(:mode).and_return('644')\n\n      expect(mode.retrieve).to eq('644')\n    end\n  end\n\n  describe '#should_to_s' do\n    describe 'with a 3-digit mode' do\n      it 'returns a 4-digit mode with a leading zero' do\n        expect(mode.should_to_s('755')).to eq(\"'0755'\")\n      end\n    end\n\n    describe 'with a 4-digit mode' do\n      it 'returns the 4-digit mode when the first digit is a zero' do\n        expect(mode.should_to_s('0755')).to eq(\"'0755'\")\n      end\n\n      it 'returns the 4-digit mode when the first digit is not a zero' do\n        expect(mode.should_to_s('1755')).to eq(\"'1755'\")\n      end\n    end\n  end\n\n  describe '#is_to_s' do\n    describe 'with a 3-digit mode' do\n      it 'returns a 4-digit mode with a leading zero' do\n        expect(mode.is_to_s('755')).to eq(\"'0755'\")\n      end\n    end\n\n    describe 'with a 4-digit mode' do\n      it 'returns the 4-digit mode when the first digit is a zero' do\n        expect(mode.is_to_s('0755')).to eq(\"'0755'\")\n      end\n\n      it 'returns the 4-digit mode when the first digit is not a zero' do\n        expect(mode.is_to_s('1755')).to eq(\"'1755'\")\n      end\n    end\n\n    describe 'when passed :absent' do\n      it \"returns 'absent'\" do\n        expect(mode.is_to_s(:absent)).to eq(\"'absent'\")\n      end\n    end\n  end\n\n  describe \"#sync with a symbolic mode\" do\n    let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'u+w,g-w' }\n    let(:mode_sym) { resource_sym.property(:mode) }\n\n    before { FileUtils.touch(path) }\n\n    it \"changes only the requested bits\" do\n      # lower nibble must be set to 4 for the sake of passing on Windows\n      Puppet::FileSystem.chmod(0464, path)\n\n      mode_sym.sync\n      stat = Puppet::FileSystem.stat(path)\n      expect((stat.mode & 0777).to_s(8)).to eq(\"644\")\n    end\n  end\n\n  describe '#sync with a symbolic mode of +X for a file' do\n    let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'g+wX' }\n    let(:mode_sym) { resource_sym.property(:mode) }\n\n    before { FileUtils.touch(path) }\n\n    it 'does not change executable bit if no executable bit is set' do\n      Puppet::FileSystem.chmod(0644, path)\n\n      mode_sym.sync\n\n      stat = Puppet::FileSystem.stat(path)\n      expect((stat.mode & 0777).to_s(8)).to eq('664')\n    end\n\n    it 'does change executable bit if an executable bit is set' do\n      Puppet::FileSystem.chmod(0744, path)\n\n      mode_sym.sync\n\n      stat = Puppet::FileSystem.stat(path)\n      expect((stat.mode & 0777).to_s(8)).to eq('774')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/mtime_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:mtime) do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  before do\n    @filename = tmpfile('mtime')\n    @resource = Puppet::Type.type(:file).new({:name => @filename})\n  end\n\n  it \"should be able to audit the file's mtime\" do\n    File.open(@filename, \"w\"){ }\n\n    @resource[:audit] = [:mtime]\n\n    # this .to_resource audit behavior is magical :-(\n    expect(@resource.to_resource[:mtime]).to eq(Puppet::FileSystem.stat(@filename).mtime.to_s)\n  end\n\n  it \"should return absent if auditing an absent file\" do\n    @resource[:audit] = [:mtime]\n\n    expect(@resource.to_resource[:mtime]).to eq(:absent)\n  end\n\n  it \"should prevent the user from trying to set the mtime\" do\n    expect {\n      @resource[:mtime] = Time.now.to_s\n    }.to raise_error(Puppet::Error, /mtime is read-only/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/type/file/owner_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:owner) do\n  include PuppetSpec::Files\n\n  let(:path) { tmpfile('mode_spec') }\n  let(:resource) { Puppet::Type.type(:file).new :path => path, :owner => 'joeuser' }\n  let(:owner) { resource.property(:owner) }\n\n  before :each do\n    allow(Puppet.features).to receive(:root?).and_return(true)\n  end\n\n  describe \"#insync?\" do\n    before :each do\n      resource[:owner] = ['foo', 'bar']\n\n      allow(resource.provider).to receive(:name2uid).with('foo').and_return(1001)\n      allow(resource.provider).to receive(:name2uid).with('bar').and_return(1002)\n    end\n\n    it \"should fail if an owner's id can't be found by name\" do\n      allow(resource.provider).to receive(:name2uid).and_return(nil)\n\n      expect { owner.insync?(5) }.to raise_error(/Could not find user foo/)\n    end\n\n    it \"should return false if an owner's id can't be found by name in noop\" do\n      Puppet[:noop] = true\n      allow(resource.provider).to receive(:name2uid).and_return(nil)\n\n      expect(owner.insync?('notcreatedyet')).to eq(false)\n    end\n\n    it \"should use the id for comparisons, not the name\" do\n      expect(owner.insync?('foo')).to be_falsey\n    end\n\n    it \"should return true if the current owner is one of the desired owners\" do\n      expect(owner.insync?(1001)).to be_truthy\n    end\n\n    it \"should return false if the current owner is not one of the desired owners\" do\n      expect(owner.insync?(1003)).to be_falsey\n    end\n  end\n\n  %w[is_to_s should_to_s].each do |prop_to_s|\n    describe \"##{prop_to_s}\" do\n      it \"should use the name of the user if it can find it\" do\n        allow(resource.provider).to receive(:uid2name).with(1001).and_return('foo')\n\n        expect(owner.send(prop_to_s, 1001)).to eq(\"'foo'\")\n      end\n\n      it \"should use the id of the user if it can't\" do\n        allow(resource.provider).to receive(:uid2name).with(1001).and_return(nil)\n\n        expect(owner.send(prop_to_s, 1001)).to eq('1001')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/selinux_spec.rb",
    "content": "require 'spec_helper'\n\n[:seluser, :selrole, :seltype, :selrange].each do |param|\n  property = Puppet::Type.type(:file).attrclass(param)\n  describe property do\n    include PuppetSpec::Files\n\n    before do\n      @path = make_absolute(\"/my/file\")\n      @resource = Puppet::Type.type(:file).new(:path => @path, :ensure => :file)\n      @sel = property.new :resource => @resource\n    end\n\n    it \"retrieve on #{param} should return :absent if the file isn't statable\" do\n      expect(@resource).to receive(:stat).and_return(nil)\n      expect(@sel.retrieve).to eq(:absent)\n    end\n\n    it \"should retrieve nil for #{param} if there is no SELinux support\" do\n      stat = double('stat', :ftype => \"foo\")\n      expect(@resource).to receive(:stat).and_return(stat)\n      expect(@sel).to receive(:get_selinux_current_context).with(@path).and_return(nil)\n      expect(@sel.retrieve).to be_nil\n    end\n\n    it \"should retrieve #{param} if a SELinux context is found with a range\" do\n      stat = double('stat', :ftype => \"foo\")\n      expect(@resource).to receive(:stat).and_return(stat)\n      expect(@sel).to receive(:get_selinux_current_context).with(@path).and_return(\"user_u:role_r:type_t:s0\")\n      expectedresult = case param\n        when :seluser; \"user_u\"\n        when :selrole; \"role_r\"\n        when :seltype; \"type_t\"\n        when :selrange; \"s0\"\n      end\n      expect(@sel.retrieve).to eq(expectedresult)\n    end\n\n    it \"should retrieve #{param} if a SELinux context is found without a range\" do\n      stat = double('stat', :ftype => \"foo\")\n      expect(@resource).to receive(:stat).and_return(stat)\n      expect(@sel).to receive(:get_selinux_current_context).with(@path).and_return(\"user_u:role_r:type_t\")\n      expectedresult = case param\n        when :seluser; \"user_u\"\n        when :selrole; \"role_r\"\n        when :seltype; \"type_t\"\n        when :selrange; nil\n      end\n      expect(@sel.retrieve).to eq(expectedresult)\n    end\n\n    it \"should handle no default gracefully\" do\n      skip if Puppet::Util::Platform.windows?\n      expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, nil, :file).and_return(nil)\n      expect(@sel.default).to be_nil\n    end\n\n    it \"should be able to detect default context on platforms other than Windows\", unless: Puppet::Util::Platform.windows? do\n      allow(@sel).to receive(:debug)\n      hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n      allow(@sel.provider.class).to receive(:selinux_handle).and_return(hnd)\n      expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, hnd, :file).and_return(\"user_u:role_r:type_t:s0\")\n      expectedresult = case param\n        when :seluser; \"user_u\"\n        when :selrole; \"role_r\"\n        when :seltype; \"type_t\"\n        when :selrange; \"s0\"\n      end\n      expect(@sel.default).to eq(expectedresult)\n    end\n\n    it \"returns nil default context on Windows\", if: Puppet::Util::Platform.windows? do\n      expect(@sel).to receive(:retrieve_default_context)\n      expect(@sel.default).to be_nil\n    end\n\n    it \"should return nil for defaults if selinux_ignore_defaults is true\" do\n      @resource[:selinux_ignore_defaults] = :true\n      expect(@sel.default).to be_nil\n    end\n\n    it \"should be able to set a new context\" do\n      @sel.should = %w{newone}\n      expect(@sel).to receive(:set_selinux_context).with(@path, [\"newone\"], param)\n      @sel.sync\n    end\n\n    it \"should do nothing for safe_insync? if no SELinux support\" do\n      @sel.should = %{newcontext}\n      expect(@sel).to receive(:selinux_support?).and_return(false)\n      expect(@sel.safe_insync?(\"oldcontext\")).to eq(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/source_spec.rb",
    "content": "require 'spec_helper'\nrequire 'uri'\nrequire 'puppet/network/http_pool'\n\ndescribe Puppet::Type.type(:file).attrclass(:source), :uses_checksums => true do\n  include PuppetSpec::Files\n  include_context 'with supported checksum types'\n\n  around :each do |example|\n    Puppet.override(:environments => Puppet::Environments::Static.new) do\n      example.run\n    end\n  end\n\n  let(:filename) { tmpfile('file_source_validate') }\n  let(:environment) { Puppet::Node::Environment.remote(\"myenv\") }\n  let(:catalog) { Puppet::Resource::Catalog.new(:test, environment) }\n  let(:resource) { Puppet::Type.type(:file).new :path => filename, :catalog => catalog }\n\n  before do\n    @foobar = make_absolute(\"/foo/bar baz\")\n    @feebooz = make_absolute(\"/fee/booz baz\")\n\n    @foobar_uri  = Puppet::Util.uri_unescape(Puppet::Util.path_to_uri(@foobar).to_s)\n    @feebooz_uri = Puppet::Util.uri_unescape(Puppet::Util.path_to_uri(@feebooz).to_s)\n  end\n\n  it \"should be a subclass of Parameter\" do\n    expect(described_class.superclass).to eq(Puppet::Parameter)\n  end\n\n  describe \"#validate\" do\n    it \"should fail if the set values are not URLs\" do\n      expect(URI).to receive(:parse).with('foo').and_raise(RuntimeError)\n\n      expect { resource[:source] = %w{foo} }.to raise_error(Puppet::Error)\n    end\n\n    it \"should fail if the URI is not a local file, file URI, or puppet URI\" do\n      expect { resource[:source] = %w{ftp://foo/bar} }.to raise_error(Puppet::Error, /Cannot use URLs of type 'ftp' as source for fileserving/)\n    end\n\n    it \"should strip trailing forward slashes\", :unless => Puppet::Util::Platform.windows? do\n      resource[:source] = \"/foo/bar\\\\//\"\n      expect(resource[:source].first).to match(%r{/foo/bar\\\\$})\n    end\n\n    it \"should strip trailing forward and backslashes\", :if => Puppet::Util::Platform.windows? do\n      resource[:source] = \"X:/foo/bar\\\\//\"\n      expect(resource[:source].first).to match(/(file\\:|file\\:\\/\\/)\\/X:\\/foo\\/bar$/)\n    end\n\n    it \"should accept an array of sources\" do\n      resource[:source] = %w{file:///foo/bar puppet://host:8140/foo/bar}\n      expect(resource[:source]).to eq(%w{file:///foo/bar puppet://host:8140/foo/bar})\n    end\n\n    it \"should accept file path characters that are not valid in URI\" do\n      resource[:source] = 'file:///foo bar'\n    end\n\n    it \"should reject relative URI sources\" do\n      expect { resource[:source] = 'foo/bar' }.to raise_error(Puppet::Error)\n    end\n\n    it \"should reject opaque sources\" do\n      expect { resource[:source] = 'mailto:foo@com' }.to raise_error(Puppet::Error)\n    end\n\n    it \"should accept URI authority component\" do\n      resource[:source] = 'file://host/foo'\n      expect(resource[:source]).to eq(%w{file://host/foo})\n    end\n\n    it \"should accept when URI authority is absent\" do\n      resource[:source] = 'file:///foo/bar'\n      expect(resource[:source]).to eq(%w{file:///foo/bar})\n    end\n  end\n\n  describe \"#munge\" do\n    it \"should prefix file scheme to absolute paths\" do\n      resource[:source] = filename\n      expect(resource[:source]).to eq([Puppet::Util.uri_unescape(Puppet::Util.path_to_uri(filename).to_s)])\n    end\n\n    %w[file puppet].each do |scheme|\n      it \"should not prefix valid #{scheme} URIs\" do\n        resource[:source] = \"#{scheme}:///foo bar\"\n        expect(resource[:source]).to eq([\"#{scheme}:///foo bar\"])\n      end\n    end\n  end\n\n  describe \"when returning the metadata\" do\n    before do\n      @metadata = double('metadata', :source= => nil)\n      allow(resource).to receive(:[]).with(:links).and_return(:manage)\n      allow(resource).to receive(:[]).with(:source_permissions).and_return(:use)\n      allow(resource).to receive(:[]).with(:checksum).and_return(:checksum)\n    end\n\n    it \"should return already-available metadata\" do\n      @source = described_class.new(:resource => resource)\n      @source.metadata = \"foo\"\n      expect(@source.metadata).to eq(\"foo\")\n    end\n\n    it \"should return nil if no @should value is set and no metadata is available\" do\n      @source = described_class.new(:resource => resource)\n      expect(@source.metadata).to be_nil\n    end\n\n    it \"should collect its metadata using the Metadata class if it is not already set\" do\n      @source = described_class.new(:resource => resource, :value => @foobar)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find) do |uri, options|\n        expect(uri).to eq(@foobar_uri)\n        expect(options[:environment]).to eq(environment)\n        expect(options[:links]).to eq(:manage)\n        expect(options[:checksum_type]).to eq(:checksum)\n\n        @metadata\n      end\n\n      @source.metadata\n    end\n\n    it \"should use the metadata from the first found source\" do\n      metadata = double('metadata', :source= => nil)\n      @source = described_class.new(:resource => resource, :value => [@foobar, @feebooz])\n      options = {\n        :environment => environment,\n        :links => :manage,\n        :source_permissions => :use,\n        :checksum_type => :checksum\n      }\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(@foobar_uri, options).and_return(nil)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(@feebooz_uri, options).and_return(metadata)\n      expect(@source.metadata).to equal(metadata)\n    end\n\n    it \"should store the found source as the metadata's source\" do\n      metadata = double('metadata')\n      @source = described_class.new(:resource => resource, :value => @foobar)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find) do |uri, options|\n        expect(uri).to eq(@foobar_uri)\n        expect(options[:environment]).to eq(environment)\n        expect(options[:links]).to eq(:manage)\n        expect(options[:checksum_type]).to eq(:checksum)\n\n        metadata\n      end\n\n      expect(metadata).to receive(:source=).with(@foobar_uri)\n      @source.metadata\n    end\n\n    it \"should fail intelligently if an exception is encountered while querying for metadata\" do\n      @source = described_class.new(:resource => resource, :value => @foobar)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find) do |uri, options|\n        expect(uri).to eq(@foobar_uri)\n        expect(options[:environment]).to eq(environment)\n        expect(options[:links]).to eq(:manage)\n        expect(options[:checksum_type]).to eq(:checksum)\n      end.and_raise(RuntimeError)\n\n      expect(@source).to receive(:fail).and_raise(ArgumentError)\n      expect { @source.metadata }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if no specified sources can be found\" do\n      @source = described_class.new(:resource => resource, :value => @foobar)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find)  do |uri, options|\n        expect(uri).to eq(@foobar_uri)\n        expect(options[:environment]).to eq(environment)\n        expect(options[:links]).to eq(:manage)\n        expect(options[:checksum_type]).to eq(:checksum)\n\n        nil\n      end\n\n      expect(@source).to receive(:fail).and_raise(RuntimeError)\n\n      expect { @source.metadata }.to raise_error(RuntimeError)\n    end\n  end\n\n  it \"should have a method for setting the desired values on the resource\" do\n    expect(described_class.new(:resource => resource)).to respond_to(:copy_source_values)\n  end\n\n  describe \"when copying the source values\" do\n    before do\n      allow_any_instance_of(Puppet::Type.type(:file)).to receive(:file).and_return('my/file.pp')\n      allow_any_instance_of(Puppet::Type.type(:file)).to receive(:line).and_return(5)\n    end\n\n    before :each do\n      @resource = Puppet::Type.type(:file).new :path => @foobar\n\n      @source = described_class.new(:resource => @resource)\n      @metadata = double('metadata', :owner => 100, :group => 200, :mode => \"173\", :checksum => \"{md5}asdfasdf\", :checksum_type => \"md5\", :ftype => \"file\", :source => @foobar)\n      allow(@source).to receive(:metadata).and_return(@metadata)\n\n      allow(Puppet.features).to receive(:root?).and_return(true)\n    end\n\n    it \"should not issue an error - except on Windows - if the source mode value is a Numeric\" do\n      allow(@metadata).to receive(:mode).and_return(0173)\n      @resource[:source_permissions] = :use\n      if Puppet::Util::Platform.windows?\n        expect { @source.copy_source_values }.to raise_error(\"Should not have tried to use source owner/mode/group on Windows (file: my/file.pp, line: 5)\")\n      else\n        expect { @source.copy_source_values }.not_to raise_error\n      end\n    end\n\n    it \"should not issue an error - except on Windows - if the source mode value is a String\" do\n      allow(@metadata).to receive(:mode).and_return(\"173\")\n      @resource[:source_permissions] = :use\n      if Puppet::Util::Platform.windows?\n        expect { @source.copy_source_values }.to raise_error(\"Should not have tried to use source owner/mode/group on Windows (file: my/file.pp, line: 5)\")\n      else\n        expect { @source.copy_source_values }.not_to raise_error\n      end\n    end\n\n    it \"should fail if there is no metadata\" do\n      allow(@source).to receive(:metadata).and_return(nil)\n      expect(@source).to receive(:devfail).and_raise(ArgumentError)\n      expect { @source.copy_source_values }.to raise_error(ArgumentError)\n    end\n\n    it \"should set :ensure to the file type\" do\n      allow(@metadata).to receive(:ftype).and_return(\"file\")\n\n      @source.copy_source_values\n      expect(@resource[:ensure]).to eq(:file)\n    end\n\n    it \"should not set 'ensure' if it is already set to 'absent'\" do\n      allow(@metadata).to receive(:ftype).and_return(\"file\")\n\n      @resource[:ensure] = :absent\n      @source.copy_source_values\n      expect(@resource[:ensure]).to eq(:absent)\n    end\n\n    describe \"and the source is a file\" do\n      before do\n        allow(@metadata).to receive(:ftype).and_return(\"file\")\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      end\n\n      context \"when source_permissions is `use`\" do\n        before :each do\n          @resource[:source_permissions] = \"use\"\n          @resource[:checksum] = :sha256\n        end\n\n        it \"should copy the metadata's owner, group, checksum, checksum_type, and mode to the resource if they are not set on the resource\" do\n          @source.copy_source_values\n\n          expect(@resource[:owner]).to eq(100)\n          expect(@resource[:group]).to eq(200)\n          expect(@resource[:mode]).to eq(\"0173\")\n\n          # Metadata calls it checksum and checksum_type, we call it content and checksum.\n          expect(@resource[:content]).to eq(@metadata.checksum)\n          expect(@resource[:checksum]).to eq(@metadata.checksum_type.to_sym)\n        end\n\n        it \"should not copy the metadata's owner, group, checksum, checksum_type, and mode to the resource if they are already set\" do\n          @resource[:owner] = 1\n          @resource[:group] = 2\n          @resource[:mode] = '173'\n          @resource[:content] = \"foobar\"\n\n          @source.copy_source_values\n\n          expect(@resource[:owner]).to eq(1)\n          expect(@resource[:group]).to eq(2)\n          expect(@resource[:mode]).to eq('0173')\n          expect(@resource[:content]).not_to eq(@metadata.checksum)\n          expect(@resource[:checksum]).not_to eq(@metadata.checksum_type.to_sym)\n        end\n\n        describe \"and puppet is not running as root\" do\n          before do\n            allow(Puppet.features).to receive(:root?).and_return(false)\n          end\n\n          it \"should not try to set the owner\" do\n            @source.copy_source_values\n            expect(@resource[:owner]).to be_nil\n          end\n\n          it \"should not try to set the group\" do\n            @source.copy_source_values\n            expect(@resource[:group]).to be_nil\n          end\n        end\n      end\n\n      context \"when source_permissions is `use_when_creating`\" do\n        before :each do\n          @resource[:source_permissions] = \"use_when_creating\"\n          expect(Puppet.features).to receive(:root?).and_return(true)\n          allow(@source).to receive(:local?).and_return(false)\n        end\n\n        context \"when managing a new file\" do\n          it \"should copy owner and group from local sources\" do\n            allow(@source).to receive(:local?).and_return(true)\n\n            @source.copy_source_values\n\n            expect(@resource[:owner]).to eq(100)\n            expect(@resource[:group]).to eq(200)\n            expect(@resource[:mode]).to eq(\"0173\")\n          end\n\n          it \"copies the remote owner\" do\n            @source.copy_source_values\n\n            expect(@resource[:owner]).to eq(100)\n          end\n\n          it \"copies the remote group\" do\n            @source.copy_source_values\n\n            expect(@resource[:group]).to eq(200)\n          end\n\n          it \"copies the remote mode\" do\n            @source.copy_source_values\n\n            expect(@resource[:mode]).to eq(\"0173\")\n          end\n        end\n\n        context \"when managing an existing file\" do\n          before :each do\n            allow(Puppet::FileSystem).to receive(:exist?).with(@resource[:path]).and_return(true)\n          end\n\n          it \"should not copy owner, group or mode from local sources\" do\n            allow(@source).to receive(:local?).and_return(true)\n\n            @source.copy_source_values\n\n            expect(@resource[:owner]).to be_nil\n            expect(@resource[:group]).to be_nil\n            expect(@resource[:mode]).to be_nil\n          end\n\n          it \"preserves the local owner\" do\n            @source.copy_source_values\n\n            expect(@resource[:owner]).to be_nil\n          end\n\n          it \"preserves the local group\" do\n            @source.copy_source_values\n\n            expect(@resource[:group]).to be_nil\n          end\n\n          it \"preserves the local mode\" do\n            @source.copy_source_values\n\n            expect(@resource[:mode]).to be_nil\n          end\n        end\n      end\n\n      context \"when source_permissions is default\" do\n        before :each do\n          allow(@source).to receive(:local?).and_return(false)\n          expect(Puppet.features).to receive(:root?).and_return(true)\n        end\n\n        it \"should not copy owner, group or mode from local sources\" do\n          allow(@source).to receive(:local?).and_return(true)\n\n          @source.copy_source_values\n\n          expect(@resource[:owner]).to be_nil\n          expect(@resource[:group]).to be_nil\n          expect(@resource[:mode]).to be_nil\n        end\n\n        it \"preserves the local owner\" do\n          @source.copy_source_values\n\n          expect(@resource[:owner]).to be_nil\n        end\n\n        it \"preserves the local group\" do\n          @source.copy_source_values\n\n          expect(@resource[:group]).to be_nil\n        end\n\n        it \"preserves the local mode\" do\n          @source.copy_source_values\n\n          expect(@resource[:mode]).to be_nil\n        end\n      end\n    end\n\n    describe \"and the source is a link\" do\n      before do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      end\n\n      it \"should set the target to the link destination\" do\n        allow(@metadata).to receive(:ftype).and_return(\"link\")\n        allow(@metadata).to receive(:links).and_return(\"manage\")\n        allow(@metadata).to receive(:checksum_type).and_return(nil)\n        allow(@resource).to receive(:[])\n        allow(@resource).to receive(:[]=)\n\n        expect(@metadata).to receive(:destination).and_return(\"/path/to/symlink\")\n\n        expect(@resource).to receive(:[]=).with(:target, \"/path/to/symlink\")\n        @source.copy_source_values\n      end\n    end\n  end\n\n  it \"should have a local? method\" do\n    expect(described_class.new(:resource => resource)).to be_respond_to(:local?)\n  end\n\n  context \"when accessing source properties\" do\n    let(:catalog) { Puppet::Resource::Catalog.new }\n    let(:path) { tmpfile('file_resource') }\n    let(:resource) { Puppet::Type.type(:file).new(:path => path, :catalog => catalog) }\n    let(:sourcepath) { tmpfile('file_source') }\n\n    describe \"for local sources\" do\n      before :each do\n        FileUtils.touch(sourcepath)\n      end\n\n      describe \"on POSIX systems\", :if => Puppet.features.posix? do\n        ['', \"file:\", \"file://\"].each do |prefix|\n          it \"with prefix '#{prefix}' should be local\" do\n            resource[:source] = \"#{prefix}#{sourcepath}\"\n            expect(resource.parameter(:source)).to be_local\n          end\n\n          it \"should be able to return the metadata source full path\" do\n            resource[:source] = \"#{prefix}#{sourcepath}\"\n            expect(resource.parameter(:source).full_path).to eq(sourcepath)\n          end\n        end\n      end\n\n      describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n        ['', \"file:/\", \"file:///\"].each do |prefix|\n          it \"should be local with prefix '#{prefix}'\" do\n            resource[:source] = \"#{prefix}#{sourcepath}\"\n            expect(resource.parameter(:source)).to be_local\n          end\n\n          it \"should be able to return the metadata source full path\" do\n            resource[:source] = \"#{prefix}#{sourcepath}\"\n            expect(resource.parameter(:source).full_path).to eq(sourcepath)\n          end\n\n          it \"should convert backslashes to forward slashes\" do\n            resource[:source] = \"#{prefix}#{sourcepath.gsub(/\\\\/, '/')}\"\n          end\n        end\n\n        it \"should be UNC with two slashes\"\n      end\n    end\n\n    %w{puppet http}.each do |scheme|\n      describe \"for remote (#{scheme}) sources\" do\n        let(:sourcepath) { \"/path/to/source\" }\n        let(:uri) { URI::Generic.build(:scheme => scheme, :host => 'server', :port => 8192, :path => sourcepath).to_s }\n\n        before(:each) do\n          metadata = Puppet::FileServing::Metadata.new(path, :source => uri, 'type' => 'file')\n          allow(Puppet::FileServing::Metadata.indirection).to receive(:find).\n            with(uri, include(:environment, :links)).and_return(metadata)\n          allow(Puppet::FileServing::Metadata.indirection).to receive(:find).\n            with(uri, include(:environment, :links)).and_return(metadata)\n          resource[:source] = uri\n        end\n\n        it \"should not be local\" do\n          expect(resource.parameter(:source)).not_to be_local\n        end\n\n        it \"should be able to return the metadata source full path\" do\n          expect(resource.parameter(:source).full_path).to eq(\"/path/to/source\")\n        end\n\n        it \"should be able to return the source server\" do\n          expect(resource.parameter(:source).server).to eq(\"server\")\n        end\n\n        it \"should be able to return the source port\" do\n          expect(resource.parameter(:source).port).to eq(8192)\n        end\n\n        if scheme == 'puppet'\n          describe \"which don't specify server or port\" do\n            let(:uri) { \"puppet:///path/to/source\" }\n\n            it \"should return the default source server\" do\n              Puppet[:server] = \"myserver\"\n              expect(resource.parameter(:source).server).to eq(\"myserver\")\n            end\n\n            it \"should return the default source port\" do\n              Puppet[:serverport] = 1234\n              expect(resource.parameter(:source).port).to eq(1234)\n            end\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when writing\" do\n    describe \"as puppet apply\" do\n      let(:source_content) { \"source file content\\r\\n\"*10 }\n      let(:modulepath) { File.join(Puppet[:environmentpath], 'testing', 'modules') }\n      let(:env) { Puppet::Node::Environment.create(:testing, [modulepath]) }\n      let(:catalog) { Puppet::Resource::Catalog.new(:test, env) }\n\n      before do\n        Puppet[:default_file_terminus] = \"file_server\"\n      end\n\n      it \"should copy content from the source to the file\" do\n        resource = Puppet::Type.type(:file).new(path: filename, catalog: catalog, source: file_containing('apply', source_content))\n        source = resource.parameter(:source)\n        resource.write(source)\n\n        expect(Puppet::FileSystem.binread(filename)).to eq(source_content)\n      end\n\n      it 'should use the in-process fileserver if source starts with puppet:///' do\n        path = File.join(modulepath, 'mymodule', 'files', 'path')\n        Puppet::FileSystem.dir_mkpath(path)\n        File.open(path, 'wb') { |f| f.write(source_content) }\n        resource = Puppet::Type.type(:file).new(path: filename, catalog: catalog, source: 'puppet:///modules/mymodule/path')\n\n        source = resource.parameter(:source)\n        resource.write(source)\n\n        expect(Puppet::FileSystem.binread(filename)).to eq(source_content)\n      end\n\n      it 'follows symlinks when retrieving content from the in-process fileserver' do\n        # create a 'link' that points to 'target' in the 'mymodule' module\n        link = File.join(modulepath, 'mymodule', 'files', 'link')\n        target = File.join(modulepath, 'mymodule', 'files', 'target')\n        Puppet::FileSystem.dir_mkpath(target)\n        File.open(target, 'wb') { |f| f.write(source_content) }\n        Puppet::FileSystem.symlink(target, link)\n        resource = Puppet::Type.type(:file).new(path: filename, catalog: catalog, source: 'puppet:///modules/mymodule/link')\n\n        source = resource.parameter(:source)\n        resource.write(source)\n\n        # 'filename' should be a file containing the contents of the followed link\n        expect(Puppet::FileSystem.binread(filename)).to eq(source_content)\n      end\n\n      with_digest_algorithms do\n        it \"should return the checksum computed\" do\n          resource = Puppet::Type.type(:file).new(path: filename, catalog: catalog, source: file_containing('apply', source_content))\n\n          File.open(filename, 'wb') do |file|\n            source = resource.parameter(:source)\n            resource[:checksum] = digest_algorithm\n            expect(source.write(file)).to eq(\"{#{digest_algorithm}}#{digest(source_content)}\")\n          end\n        end\n      end\n    end\n\n    describe \"from local source\" do\n      let(:source_content) { \"source file content\\r\\n\"*10 }\n      before do\n        resource[:backup] = false\n        resource[:source] = file_containing('source', source_content)\n      end\n\n      it \"should copy content from the source to the file\" do\n        source = resource.parameter(:source)\n        resource.write(source)\n\n        expect(Puppet::FileSystem.binread(filename)).to eq(source_content)\n      end\n\n      with_digest_algorithms do\n        it \"should return the checksum computed\" do\n          File.open(filename, 'wb') do |file|\n            source = resource.parameter(:source)\n            resource[:checksum] = digest_algorithm\n            expect(source.write(file)).to eq(\"{#{digest_algorithm}}#{digest(source_content)}\")\n          end\n        end\n      end\n    end\n\n    describe 'from remote source' do\n      let(:source_content) { \"source file content\\n\"*10 }\n      let(:source) {\n        attr = resource.newattr(:source)\n        attr.metadata = metadata\n        attr\n      }\n      let(:metadata) {\n        Puppet::FileServing::Metadata.new(\n          '/modules/:module/foo',\n          {\n            'type' => 'file',\n            'source' => 'puppet:///modules/:module/foo'\n          }\n        )\n      }\n\n      before do\n        resource[:backup] = false\n      end\n\n      it 'should use an explicit fileserver if source starts with puppet://' do\n        metadata.source = \"puppet://somehostname:8140/modules/:module/foo\"\n\n        stub_request(:get, %r{https://somehostname:8140/puppet/v3/file_content/modules/:module/foo})\n          .to_return(status: 200, body: metadata.to_json, headers: { 'Content-Type' => 'application/json' })\n\n        resource.write(source)\n      end\n\n      it 'should use the default fileserver if source starts with puppet:///' do\n        stub_request(:get, %r{https://#{Puppet[:server]}:8140/puppet/v3/file_content/modules/:module/foo})\n          .to_return(status: 200, body: metadata.to_json, headers: { 'Content-Type' => 'application/json' })\n\n        resource.write(source)\n      end\n\n      it 'should percent encode reserved characters' do\n        metadata.source = 'puppet:///modules/:module/foo bar'\n\n        stub_request(:get, %r{/puppet/v3/file_content/modules/:module/foo%20bar})\n          .to_return(status: 200, body: metadata.to_json, headers: { 'Content-Type' => 'application/json' })\n\n        resource.write(source)\n      end\n\n      it 'should request binary content' do\n        stub_request(:get, %r{/puppet/v3/file_content/modules/:module/foo}) do |request|\n          expect(request.headers).to include({'Accept' => 'application/octet-stream'})\n        end.to_return(status: 200, body: '', headers: { 'Content-Type' => 'application/octet-stream' })\n\n        resource.write(source)\n      end\n\n      it \"should request file content from the catalog's environment\" do\n        Puppet[:environment] = 'doesntexist'\n\n        stub_request(:get, %r{/puppet/v3/file_content})\n          .with(query: hash_including(\"environment\" => \"myenv\"))\n          .to_return(status: 200, body: '', headers: { 'Content-Type' => 'application/octet-stream' })\n\n        resource.write(source)\n      end\n\n      it 'should request static file content' do\n        metadata.content_uri = \"puppet://#{Puppet[:server]}:8140/path/to/file\"\n\n        stub_request(:get, %r{/puppet/v3/static_file_content/path/to/file})\n          .to_return(status: 200, body: '', headers: { 'Content-Type' => 'application/octet-stream' })\n\n        resource.write(source)\n      end\n\n      describe 'when handling file_content responses' do\n        before do\n          File.open(filename, 'w') {|f| f.write \"initial file content\"}\n        end\n\n        it 'should not write anything if source is not found' do\n          stub_request(:get, %r{/puppet/v3/file_content/modules/:module/foo}).to_return(status: 404)\n\n          expect { resource.write(source) }.to raise_error(Net::HTTPError, /Error 404 on SERVER:/)\n          expect(File.read(filename)).to eq('initial file content')\n        end\n\n        it 'should raise an HTTP error in case of server error' do\n          stub_request(:get, %r{/puppet/v3/file_content/modules/:module/foo}).to_return(status: 500)\n\n          expect { resource.write(source) }.to raise_error(Net::HTTPError, /Error 500 on SERVER/)\n        end\n\n        context 'and the request was successful' do\n          before do\n            stub_request(:get, %r{/puppet/v3/file_content/modules/:module/foo}).to_return(status: 200, body: source_content)\n          end\n\n          it 'should write the contents to the file' do\n            resource.write(source)\n            expect(Puppet::FileSystem.binread(filename)).to eq(source_content)\n          end\n\n          with_digest_algorithms do\n            it 'should return the checksum computed' do\n              File.open(filename, 'w') do |file|\n                resource[:checksum] = digest_algorithm\n                expect(source.write(file)).to eq(\"{#{digest_algorithm}}#{digest(source_content)}\")\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/file/type_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:file).attrclass(:type) do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  before do\n    @filename = tmpfile('type')\n    @resource = Puppet::Type.type(:file).new({:name => @filename})\n  end\n\n  it \"should prevent the user from trying to set the type\" do\n    expect {\n      @resource[:type] = \"fifo\"\n    }.to raise_error(Puppet::Error, /type is read-only/)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/type/file_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\n\ndescribe Puppet::Type.type(:file) do\n  include PuppetSpec::Files\n\n  # precomputed checksum values for FILE_CONTENT\n  FILE_CONTENT = 'file content'.freeze\n  CHECKSUM_VALUES = {\n    md5: 'd10b4c3ff123b26dc068d43a8bef2d23',\n    md5lite: 'd10b4c3ff123b26dc068d43a8bef2d23',\n    sha256: 'e0ac3601005dfa1864f5392aabaf7d898b1b5bab854f1acb4491bcd806b76b0c',\n    sha256lite: 'e0ac3601005dfa1864f5392aabaf7d898b1b5bab854f1acb4491bcd806b76b0c',\n    sha1: '87758871f598e1a3b4679953589ae2f57a0bb43c',\n    sha1lite: '87758871f598e1a3b4679953589ae2f57a0bb43c',\n    sha224: '2aefaaa5f4d8f17f82f3e1bb407e190cede9aa1311fa4533ce505531',\n    sha384: '61c7783501ebd90233650357fefbe5a141b7618f907b8f043bbaa92c0f610c785a641ddd479fa81d650cd86e29aa6858',\n    sha512: '2fb1877301854ac92dd518018f97407a0a88bb696bfef0a51e9efbd39917353500009e15bd72c3f0e4bf690115870bfab926565d5ad97269d922dbbb41261221',\n    mtime: 'Jan 26 13:59:49 2016',\n    ctime: 'Jan 26 13:59:49 2016'\n  }.freeze\n\n  INVALID_CHECKSUM_VALUES = {\n    md5: '00000000000000000000000000000000',\n    md5lite: '00000000000000000000000000000000',\n    sha256: '0000000000000000000000000000000000000000000000000000000000000000',\n    sha256lite: '0000000000000000000000000000000000000000000000000000000000000000',\n    sha1: '0000000000000000000000000000000000000000',\n    sha1lite: '0000000000000000000000000000000000000000',\n    sha224: '00000000000000000000000000000000000000000000000000000000',\n    sha384: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',\n    sha512: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'\n  }.freeze\n\n  let(:path) { tmpfile('file_testing') }\n  let(:file) { described_class.new(:path => path, :catalog => catalog) }\n  let(:provider) { file.provider }\n  let(:catalog) { Puppet::Resource::Catalog.new }\n\n  before do\n    allow(Puppet.features).to receive(\"posix?\").and_return(true)\n  end\n\n  describe \"the path parameter\" do\n    describe \"on POSIX systems\", :if => Puppet.features.posix? do\n      it \"should remove trailing slashes\" do\n        file[:path] = \"/foo/bar/baz/\"\n        expect(file[:path]).to eq(\"/foo/bar/baz\")\n      end\n\n      it \"should remove double slashes\" do\n        file[:path] = \"/foo/bar//baz\"\n        expect(file[:path]).to eq(\"/foo/bar/baz\")\n      end\n\n      it \"should remove triple slashes\" do\n        file[:path] = \"/foo/bar///baz\"\n        expect(file[:path]).to eq(\"/foo/bar/baz\")\n      end\n\n      it \"should remove trailing double slashes\" do\n        file[:path] = \"/foo/bar/baz//\"\n        expect(file[:path]).to eq(\"/foo/bar/baz\")\n      end\n\n      it \"should leave a single slash alone\" do\n        file[:path] = \"/\"\n        expect(file[:path]).to eq(\"/\")\n      end\n\n      it \"should accept and collapse a double-slash at the start of the path\" do\n        file[:path] = \"//tmp/xxx\"\n        expect(file[:path]).to eq('/tmp/xxx')\n      end\n\n      it \"should accept and collapse a triple-slash at the start of the path\" do\n        file[:path] = \"///tmp/xxx\"\n        expect(file[:path]).to eq('/tmp/xxx')\n      end\n    end\n\n    describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n      it \"should remove trailing slashes\" do\n        file[:path] = \"X:/foo/bar/baz/\"\n        expect(file[:path]).to eq(\"X:/foo/bar/baz\")\n      end\n\n      it \"should remove double slashes\" do\n        file[:path] = \"X:/foo/bar//baz\"\n        expect(file[:path]).to eq(\"X:/foo/bar/baz\")\n      end\n\n      it \"should remove trailing double slashes\" do\n        file[:path] = \"X:/foo/bar/baz//\"\n        expect(file[:path]).to eq(\"X:/foo/bar/baz\")\n      end\n\n      it \"should leave a drive letter with a slash alone\" do\n        file[:path] = \"X:/\"\n        expect(file[:path]).to eq(\"X:/\")\n      end\n\n      it \"should not accept a drive letter without a slash\" do\n        expect { file[:path] = \"X:\" }.to raise_error(/File paths must be fully qualified/)\n      end\n\n      describe \"when using UNC filenames\", :if => Puppet::Util::Platform.windows? do\n        it \"should remove trailing slashes\" do\n          file[:path] = \"//localhost/foo/bar/baz/\"\n          expect(file[:path]).to eq(\"//localhost/foo/bar/baz\")\n        end\n\n        it \"should remove double slashes\" do\n          file[:path] = \"//localhost/foo/bar//baz\"\n          expect(file[:path]).to eq(\"//localhost/foo/bar/baz\")\n        end\n\n        it \"should remove trailing double slashes\" do\n          file[:path] = \"//localhost/foo/bar/baz//\"\n          expect(file[:path]).to eq(\"//localhost/foo/bar/baz\")\n        end\n\n        it \"should remove a trailing slash from a sharename\" do\n          file[:path] = \"//localhost/foo/\"\n          expect(file[:path]).to eq(\"//localhost/foo\")\n        end\n\n        it \"should not modify a sharename\" do\n          file[:path] = \"//localhost/foo\"\n          expect(file[:path]).to eq(\"//localhost/foo\")\n        end\n      end\n    end\n  end\n\n  describe \"the backup parameter\" do\n    it 'should be disabled by default' do\n      expect(file[:backup]).to eq(nil)\n    end\n\n    [false, 'false', :false].each do |value|\n      it \"should disable backup if the value is #{value.inspect}\" do\n        file[:backup] = value\n        expect(file[:backup]).to eq(false)\n      end\n    end\n\n    [true, 'true', '.puppet-bak'].each do |value|\n      it \"should use .puppet-bak if the value is #{value.inspect}\" do\n        file[:backup] = value\n        expect(file[:backup]).to eq('.puppet-bak')\n      end\n    end\n\n    it \"should use the provided value if it's any other string\" do\n      file[:backup] = \"over there\"\n      expect(file[:backup]).to eq(\"over there\")\n    end\n\n    it \"should fail if backup is set to anything else\" do\n      expect do\n        file[:backup] = 97\n      end.to raise_error(Puppet::Error, /Invalid backup type 97/)\n    end\n  end\n\n  describe \"the recurse parameter\" do\n    it \"should default to recursion being disabled\" do\n      expect(file[:recurse]).to be_falsey\n    end\n\n    [true, \"true\", \"remote\"].each do |value|\n      it \"should consider #{value} to enable recursion\" do\n        file[:recurse] = value\n        expect(file[:recurse]).to be_truthy\n      end\n    end\n\n    it \"should not allow numbers\" do\n      expect { file[:recurse] = 10 }.to raise_error(\n        Puppet::Error, /Parameter recurse failed on File\\[[^\\]]+\\]: Invalid recurse value 10/)\n    end\n\n    [false, \"false\"].each do |value|\n      it \"should consider #{value} to disable recursion\" do\n        file[:recurse] = value\n        expect(file[:recurse]).to be_falsey\n      end\n    end\n  end\n\n  describe \"the recurselimit parameter\" do\n    it \"should accept integers\" do\n      file[:recurselimit] = 12\n      expect(file[:recurselimit]).to eq(12)\n    end\n\n    it \"should munge string numbers to number numbers\" do\n      file[:recurselimit] = '12'\n      expect(file[:recurselimit]).to eq(12)\n    end\n\n    it \"should fail if given a non-number\" do\n      expect do\n        file[:recurselimit] = 'twelve'\n      end.to raise_error(Puppet::Error, /Invalid value \"twelve\"/)\n    end\n  end\n\n  describe \"the replace parameter\" do\n    [true, :true, :yes].each do |value|\n      it \"should consider #{value} to be true\" do\n        file[:replace] = value\n        expect(file[:replace]).to be_truthy\n      end\n    end\n\n    [false, :false, :no].each do |value|\n      it \"should consider #{value} to be false\" do\n        file[:replace] = value\n        expect(file[:replace]).to be_falsey\n      end\n    end\n  end\n\n  describe \".instances\" do\n    it \"should return an empty array\" do\n      expect(described_class.instances).to eq([])\n    end\n  end\n\n  describe \"#bucket\" do\n    it \"should return nil if backup is off\" do\n      file[:backup] = false\n      expect(file.bucket).to eq(nil)\n    end\n\n    it \"should not return a bucket if using a file extension for backup\" do\n      file[:backup] = '.backup'\n\n      expect(file.bucket).to eq(nil)\n    end\n\n    it \"should return the default filebucket if using the 'puppet' filebucket\" do\n      file[:backup] = 'puppet'\n      bucket = double('bucket')\n      allow(file).to receive(:default_bucket).and_return(bucket)\n\n      expect(file.bucket).to eq(bucket)\n    end\n\n    it \"should fail if using a remote filebucket and no catalog exists\" do\n      file.catalog = nil\n      file[:backup] = 'my_bucket'\n\n      expect { file.bucket }.to raise_error(Puppet::Error, \"Can not find filebucket for backups without a catalog\")\n    end\n\n    it \"should fail if the specified filebucket isn't in the catalog\" do\n      file[:backup] = 'my_bucket'\n\n      expect { file.bucket }.to raise_error(Puppet::Error, \"Could not find filebucket my_bucket specified in backup\")\n    end\n\n    it \"should use the specified filebucket if it is in the catalog\" do\n      file[:backup] = 'my_bucket'\n      filebucket = Puppet::Type.type(:filebucket).new(:name => 'my_bucket')\n      catalog.add_resource(filebucket)\n\n      expect(file.bucket).to eq(filebucket.bucket)\n    end\n  end\n\n  describe \"#asuser\" do\n    before :each do\n      # Mocha won't let me just stub SUIDManager.asuser to yield and return,\n      # but it will do exactly that if we're not root.\n      allow(Puppet::Util::SUIDManager).to receive(:root?).and_return(false)\n    end\n\n    it \"should return the desired owner if they can write to the parent directory\" do\n      file[:owner] = 1001\n      allow(FileTest).to receive(:writable?).with(File.dirname file[:path]).and_return(true)\n\n      expect(file.asuser).to eq(1001)\n    end\n\n    it \"should return nil if the desired owner can't write to the parent directory\" do\n      file[:owner] = 1001\n      allow(FileTest).to receive(:writable?).with(File.dirname file[:path]).and_return(false)\n\n      expect(file.asuser).to eq(nil)\n    end\n\n    it \"should return nil if not managing owner\" do\n      expect(file.asuser).to eq(nil)\n    end\n  end\n\n  describe \"#exist?\" do\n    it \"should be considered existent if it can be stat'ed\" do\n      expect(file).to receive(:stat).and_return(double('stat'))\n      expect(file).to be_exist\n    end\n\n    it \"should be considered nonexistent if it can not be stat'ed\" do\n      expect(file).to receive(:stat).and_return(nil)\n      expect(file).to_not be_exist\n    end\n  end\n\n  describe \"#eval_generate\" do\n    before do\n      @graph = double('graph', :add_edge => nil)\n      allow(catalog).to receive(:relationship_graph).and_return(@graph)\n    end\n\n    it \"should recurse if recursion is enabled\" do\n      resource = double('resource', :[] => 'resource')\n      expect(file).to receive(:recurse).and_return([resource])\n\n      file[:recurse] = true\n\n      expect(file.eval_generate).to eq([resource])\n    end\n\n    it \"should not recurse if recursion is disabled\" do\n      expect(file).not_to receive(:recurse)\n\n      file[:recurse] = false\n\n      expect(file.eval_generate).to eq([])\n    end\n  end\n\n  describe \"#ancestors\" do\n    it \"should return the ancestors of the file, in ascending order\" do\n      file = described_class.new(:path => make_absolute(\"/tmp/foo/bar/baz/qux\"))\n\n      pieces = %W[#{make_absolute('/')} tmp foo bar baz]\n\n      ancestors = file.ancestors\n\n      expect(ancestors).not_to be_empty\n      ancestors.reverse.each_with_index do |path,i|\n        expect(path).to eq(File.join(*pieces[0..i]))\n      end\n    end\n  end\n\n  describe \"#flush\" do\n    it \"should reset its stat reference\" do\n      FileUtils.touch(path)\n      stat1 = file.stat\n\n      expect(file.stat).to equal(stat1)\n\n      file.flush\n\n      expect(file.stat).not_to equal(stat1)\n    end\n  end\n\n  describe \"#initialize\" do\n    it \"should remove a trailing slash from the title to create the path\" do\n      title = File.expand_path(\"/abc/\\n\\tdef/\")\n      file = described_class.new(:title => title)\n      expect(file[:path]).to eq(title)\n    end\n\n    it \"should allow a single slash for a title and create the path\" do\n      title = File.expand_path(\"/\")\n      file = described_class.new(:title => title)\n      expect(file[:path]).to eq(title)\n    end\n\n    it \"should allow multiple slashes for a title and create the path\" do\n      title = File.expand_path(\"/\") + \"//\"\n      file = described_class.new(:title => title)\n      expect(file[:path]).to eq(File.expand_path(\"/\"))\n    end\n\n    it \"should set a desired 'ensure' value if none is set and 'content' is set\" do\n      file = described_class.new(:path => path, :content => \"/foo/bar\")\n      expect(file[:ensure]).to eq(:file)\n    end\n\n    it \"should set a desired 'ensure' value if none is set and 'target' is set\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n      file = described_class.new(:path => path, :target => File.expand_path(__FILE__))\n      expect(file[:ensure]).to eq(:link)\n    end\n\n    describe \"marking parameters as sensitive\" do\n      it \"marks sensitive, content, and ensure as sensitive when source is sensitive\" do\n        resource = Puppet::Resource.new(:file, make_absolute(\"/tmp/foo\"), :parameters => {:source => make_absolute('/tmp/bar')}, :sensitive_parameters => [:source])\n        file = described_class.new(resource)\n        expect(file.parameter(:source).sensitive).to eq true\n        expect(file.property(:content).sensitive).to eq true\n        expect(file.property(:ensure).sensitive).to eq true\n      end\n\n      it \"marks ensure as sensitive when content is sensitive\" do\n        resource = Puppet::Resource.new(:file, make_absolute(\"/tmp/foo\"), :parameters => {:content => 'hello world!'}, :sensitive_parameters => [:content])\n        file = described_class.new(resource)\n        expect(file.property(:ensure).sensitive).to eq true\n      end\n    end\n  end\n\n  describe \"#mark_children_for_purging\" do\n    it \"should set each child's ensure to absent\" do\n      paths = %w[foo bar baz]\n      children = {}\n      paths.each do |child|\n        children[child] = described_class.new(:path => File.join(path, child), :ensure => :present)\n      end\n\n      file.mark_children_for_purging(children)\n\n      expect(children.length).to eq(3)\n      children.values.each do |child|\n        expect(child[:ensure]).to eq(:absent)\n      end\n    end\n\n    it \"should skip children which have a source\" do\n      child = described_class.new(:path => path, :ensure => :present, :source => File.expand_path(__FILE__))\n\n      file.mark_children_for_purging('foo' => child)\n\n      expect(child[:ensure]).to eq(:present)\n    end\n  end\n\n  describe \"#newchild\" do\n    it \"should create a new resource relative to the parent\" do\n      child = file.newchild('bar')\n\n      expect(child).to be_a(described_class)\n      expect(child[:path]).to eq(File.join(file[:path], 'bar'))\n    end\n\n    {\n      :ensure => :present,\n      :recurse => true,\n      :recurselimit => 5,\n      :target => \"some_target\",\n      :source => File.expand_path(\"some_source\"),\n    }.each do |param, value|\n      it \"should omit the #{param} parameter\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n        # Make a new file, because we have to set the param at initialization\n        # or it wouldn't be copied regardless.\n        file = described_class.new(:path => path, param => value)\n        child = file.newchild('bar')\n        expect(child[param]).not_to eq(value)\n      end\n    end\n\n    it \"should copy all of the parent resource's 'should' values that were set at initialization\" do\n      parent = described_class.new(:path => path, :owner => 'root', :group => 'wheel')\n\n      child = parent.newchild(\"my/path\")\n\n      expect(child[:owner]).to eq('root')\n      expect(child[:group]).to eq('wheel')\n    end\n\n    it \"should not copy default values to the new child\" do\n      child = file.newchild(\"my/path\")\n      expect(child.original_parameters).not_to include(:backup)\n    end\n\n    it \"should not copy values to the child which were set by the source\" do\n      source = File.expand_path(__FILE__)\n      file[:source] = source\n      metadata = double('metadata', :owner => \"root\", :group => \"root\", :mode => '0755', :ftype => \"file\", :checksum => \"{md5}whatever\", :checksum_type => \"md5\", :source => source)\n      allow(file.parameter(:source)).to receive(:metadata).and_return(metadata)\n\n      file.parameter(:source).copy_source_values\n\n      expect(file.class).to receive(:new) do |arg|\n        expect(arg[:group]).to be_nil\n      end\n      file.newchild(\"my/path\")\n    end\n  end\n\n  describe \"#purge?\" do\n    it \"should return false if purge is not set\" do\n      expect(file).to_not be_purge\n    end\n\n    it \"should return true if purge is set to true\" do\n      file[:purge] = true\n\n      expect(file).to be_purge\n    end\n\n    it \"should return false if purge is set to false\" do\n      file[:purge] = false\n\n      expect(file).to_not be_purge\n    end\n  end\n\n  describe \"#recurse\" do\n    let(:name) { 'bar' }\n    let(:child) { double('puppet_type_file') }\n\n    before do\n      file[:recurse] = true\n      @metadata = Puppet::FileServing::Metadata\n    end\n\n    describe \"and a source is set\" do\n      it \"should pass the already-discovered resources to recurse_remote\" do\n        file[:source] = File.expand_path(__FILE__)\n        allow(child).to receive(:[]).with(:path).and_return(name)\n        allow(file).to receive(:recurse_local).and_return({name => child})\n        expect(file).to receive(:recurse_remote).with({name => child}).and_return([])\n        file.recurse\n      end\n    end\n\n    describe \"and a target is set\" do\n      it \"should use recurse_link\" do\n        file[:target] = File.expand_path(__FILE__)\n        allow(child).to receive(:[]).with(:path).and_return(name)\n        allow(file).to receive(:recurse_local).and_return({name => child})\n        expect(file).to receive(:recurse_link).with({name => child}).and_return([])\n        file.recurse\n      end\n    end\n\n    it \"should use recurse_local if recurse is not remote\" do\n      expect(file).to receive(:recurse_local).and_return({})\n      file.recurse\n    end\n\n    it \"should not use recurse_local if recurse is remote\" do\n      file[:recurse] = :remote\n      expect(file).not_to receive(:recurse_local)\n      file.recurse\n    end\n\n    it \"should return the generated resources as an array sorted by file path\" do\n      one = double('one', :[] => \"/one\")\n      two = double('two', :[] => \"/one/two\")\n      three = double('three', :[] => \"/three\")\n      expect(file).to receive(:recurse_local).and_return(:one => one, :two => two, :three => three)\n      expect(file.recurse).to eq([one, two, three])\n    end\n\n    describe \"and purging is enabled\" do\n      before do\n        file[:purge] = true\n      end\n\n      it \"should mark each file for removal\" do\n        local = described_class.new(:path => path, :ensure => :present)\n        expect(file).to receive(:recurse_local).and_return(\"local\" => local)\n\n        file.recurse\n        expect(local[:ensure]).to eq(:absent)\n      end\n\n      it \"should not remove files that exist in the remote repository\" do\n        pending(\"FIXME: This test has been broken since it was introduced in c189b46e3f1 because of = vs ==\")\n        file[:source] = File.expand_path(__FILE__)\n        expect(file).to receive(:recurse_local).and_return({})\n\n        remote = described_class.new(:path => path, :source => File.expand_path(__FILE__), :ensure => :present)\n\n        expect(file).to receive(:recurse_remote).with(hash_including(\"remote\" => remote))\n\n        file.recurse\n\n        expect(remote[:ensure]).not_to eq(:absent)\n      end\n    end\n\n  end\n\n  describe \"#remove_less_specific_files\" do\n    it \"should remove any nested files that are already in the catalog\" do\n      foo = described_class.new :path => File.join(file[:path], 'foo')\n      bar = described_class.new :path => File.join(file[:path], 'bar')\n      baz = described_class.new :path => File.join(file[:path], 'baz')\n\n      catalog.add_resource(foo)\n      catalog.add_resource(bar)\n\n      expect(file.remove_less_specific_files([foo, bar, baz])).to eq([baz])\n    end\n\n  end\n\n  describe \"#recurse?\" do\n    it \"should be true if recurse is true\" do\n      file[:recurse] = true\n      expect(file).to be_recurse\n    end\n\n    it \"should be true if recurse is remote\" do\n      file[:recurse] = :remote\n      expect(file).to be_recurse\n    end\n\n    it \"should be false if recurse is false\" do\n      file[:recurse] = false\n      expect(file).to_not be_recurse\n    end\n  end\n\n  describe \"#recurse_link\" do\n    before do\n      @first = double('first', :relative_path => \"first\", :full_path => \"/my/first\", :ftype => \"directory\")\n      @second = double('second', :relative_path => \"second\", :full_path => \"/my/second\", :ftype => \"file\")\n\n      @resource = double('file', :[]= => nil)\n    end\n\n    it \"should pass its target to the :perform_recursion method\" do\n      file[:target] = \"mylinks\"\n      expect(file).to receive(:perform_recursion).with(\"mylinks\").and_return([@first])\n      allow(file).to receive(:newchild).and_return(@resource)\n      file.recurse_link({})\n    end\n\n    it \"should ignore the recursively-found '.' file and configure the top-level file to create a directory\" do\n      allow(@first).to receive(:relative_path).and_return(\".\")\n      file[:target] = \"mylinks\"\n      expect(file).to receive(:perform_recursion).with(\"mylinks\").and_return([@first])\n      expect(file).not_to receive(:newchild)\n      expect(file).to receive(:[]=).with(:ensure, :directory)\n      file.recurse_link({})\n    end\n\n    it \"should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash\" do\n      expect(file).to receive(:perform_recursion).and_return([@first, @second])\n      expect(file).to receive(:newchild).with(@first.relative_path).and_return(@resource)\n      file.recurse_link(\"second\" => @resource)\n    end\n\n    it \"should not create a new child resource for paths that already exist in the children hash\" do\n      expect(file).to receive(:perform_recursion).and_return([@first])\n      expect(file).not_to receive(:newchild)\n      file.recurse_link(\"first\" => @resource)\n    end\n\n    it \"should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n      allow(file).to receive(:perform_recursion).and_return([@first, @second])\n      file.recurse_link(\"first\" => @resource, \"second\" => file)\n\n      expect(file[:ensure]).to eq(:link)\n      expect(file[:target]).to eq(\"/my/second\")\n    end\n\n    it \"should :ensure to :directory if the file is a directory\" do\n      allow(file).to receive(:perform_recursion).and_return([@first, @second])\n      file.recurse_link(\"first\" => file, \"second\" => @resource)\n\n      expect(file[:ensure]).to eq(:directory)\n    end\n\n    it \"should return a hash with both created and existing resources with the relative paths as the hash keys\" do\n      expect(file).to receive(:perform_recursion).and_return([@first, @second])\n      allow(file).to receive(:newchild).and_return(file)\n      expect(file.recurse_link(\"second\" => @resource)).to eq({\"second\" => @resource, \"first\" => file})\n    end\n  end\n\n  describe \"#recurse_local\" do\n    before do\n      @metadata = double('metadata', :relative_path => \"my/file\")\n    end\n\n    it \"should pass its path to the :perform_recursion method\" do\n      expect(file).to receive(:perform_recursion).with(file[:path]).and_return([@metadata])\n      allow(file).to receive(:newchild)\n      file.recurse_local\n    end\n\n    it \"should return an empty hash if the recursion returns nothing\" do\n      expect(file).to receive(:perform_recursion).and_return(nil)\n      expect(file.recurse_local).to eq({})\n    end\n\n    it \"should create a new child resource with each generated metadata instance's relative path\" do\n      expect(file).to receive(:perform_recursion).and_return([@metadata])\n      expect(file).to receive(:newchild).with(@metadata.relative_path).and_return(\"fiebar\")\n      file.recurse_local\n    end\n\n    it \"should not create a new child resource for the '.' directory\" do\n      allow(@metadata).to receive(:relative_path).and_return(\".\")\n\n      expect(file).to receive(:perform_recursion).and_return([@metadata])\n      expect(file).not_to receive(:newchild)\n      file.recurse_local\n    end\n\n    it \"should return a hash of the created resources with the relative paths as the hash keys\" do\n      expect(file).to receive(:perform_recursion).and_return([@metadata])\n      expect(file).to receive(:newchild).with(\"my/file\").and_return(\"fiebar\")\n      expect(file.recurse_local).to eq({\"my/file\" => \"fiebar\"})\n    end\n\n    it \"should set checksum_type to none if this file checksum is none\" do\n      file[:checksum] = :none\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(checksum_type: :none)).and_return([@metadata])\n      expect(file).to receive(:newchild).with(\"my/file\").and_return(\"fiebar\")\n      file.recurse_local\n    end\n  end\n\n  describe \"#recurse_remote\" do\n    let(:my) { File.expand_path('/my') }\n\n    before do\n      file[:source] = \"puppet://foo/bar\"\n\n      @first = Puppet::FileServing::Metadata.new(my, :relative_path => \"first\")\n      @second = Puppet::FileServing::Metadata.new(my, :relative_path => \"second\")\n      allow(@first).to receive(:ftype).and_return(\"directory\")\n      allow(@second).to receive(:ftype).and_return(\"directory\")\n\n      @parameter = double('property', :metadata= => nil)\n      @resource = double('file', :[]= => nil, :parameter => @parameter)\n    end\n\n    it \"should pass its source to the :perform_recursion method\" do\n      data = Puppet::FileServing::Metadata.new(File.expand_path(\"/whatever\"), :relative_path => \"foobar\")\n      expect(file).to receive(:perform_recursion).with(\"puppet://foo/bar\").and_return([data])\n      allow(file).to receive(:newchild).and_return(@resource)\n      file.recurse_remote({})\n    end\n\n    it \"should not recurse when the remote file is not a directory\" do\n      data = Puppet::FileServing::Metadata.new(File.expand_path(\"/whatever\"), :relative_path => \".\")\n      allow(data).to receive(:ftype).and_return(\"file\")\n      expect(file).to receive(:perform_recursion).with(\"puppet://foo/bar\").and_return([data])\n      expect(file).not_to receive(:newchild)\n      file.recurse_remote({})\n    end\n\n    it \"should set the source of each returned file to the searched-for URI plus the found relative path\" do\n      expect(@first).to receive(:source=).with(File.join(\"puppet://foo/bar\", @first.relative_path))\n      expect(file).to receive(:perform_recursion).and_return([@first])\n      allow(file).to receive(:newchild).and_return(@resource)\n      file.recurse_remote({})\n    end\n\n    it \"should create a new resource for any relative file paths that do not already have a resource\" do\n      allow(file).to receive(:perform_recursion).and_return([@first])\n      expect(file).to receive(:newchild).with(\"first\").and_return(@resource)\n      expect(file.recurse_remote({})).to eq({\"first\" => @resource})\n    end\n\n    it \"should not create a new resource for any relative file paths that do already have a resource\" do\n      allow(file).to receive(:perform_recursion).and_return([@first])\n      expect(file).not_to receive(:newchild)\n      file.recurse_remote(\"first\" => @resource)\n    end\n\n    it \"should set the source of each resource to the source of the metadata\" do\n      allow(file).to receive(:perform_recursion).and_return([@first])\n      allow(@resource).to receive(:[]=)\n      expect(@resource).to receive(:[]=).with(:source, File.join(\"puppet://foo/bar\", @first.relative_path))\n      file.recurse_remote(\"first\" => @resource)\n    end\n\n    it \"should set the checksum parameter based on the metadata\" do\n      allow(file).to receive(:perform_recursion).and_return([@first])\n      allow(@resource).to receive(:[]=)\n      expect(@resource).to receive(:[]=).with(:checksum, \"sha256\")\n      file.recurse_remote(\"first\" => @resource)\n    end\n\n    it \"should store the metadata in the source property for each resource so the source does not have to requery the metadata\" do\n      allow(file).to receive(:perform_recursion).and_return([@first])\n      expect(@resource).to receive(:parameter).with(:source).and_return(@parameter)\n\n      expect(@parameter).to receive(:metadata=).with(@first)\n\n      file.recurse_remote(\"first\" => @resource)\n    end\n\n    it \"should not create a new resource for the '.' file\" do\n      allow(@first).to receive(:relative_path).and_return(\".\")\n      allow(file).to receive(:perform_recursion).and_return([@first])\n\n      expect(file).not_to receive(:newchild)\n\n      file.recurse_remote({})\n    end\n\n    it \"should store the metadata in the main file's source property if the relative path is '.'\" do\n      allow(@first).to receive(:relative_path).and_return(\".\")\n      allow(file).to receive(:perform_recursion).and_return([@first])\n\n      expect(file.parameter(:source)).to receive(:metadata=).with(@first)\n\n      file.recurse_remote(\"first\" => @resource)\n    end\n\n    it \"should update the main file's checksum parameter if the relative path is '.'\" do\n      allow(@first).to receive(:relative_path).and_return(\".\")\n      allow(file).to receive(:perform_recursion).and_return([@first])\n\n      allow(file).to receive(:[]=)\n      expect(file). to receive(:[]=).with(:checksum, \"sha256\")\n\n      file.recurse_remote(\"first\" => @resource)\n    end\n\n    describe \"and multiple sources are provided\" do\n      let(:sources) do\n        h = {}\n        %w{/a /b /c /d}.each do |key|\n          h[key] = Puppet::Util.uri_unescape(Puppet::Util.path_to_uri(File.expand_path(key)).to_s)\n        end\n        h\n      end\n\n      describe \"and :sourceselect is set to :first\" do\n        it \"should create file instances for the results for the first source to return any values\" do\n          data = Puppet::FileServing::Metadata.new(File.expand_path(\"/whatever\"), :relative_path => \"foobar\")\n          file[:source] = sources.keys.sort.map { |key| File.expand_path(key) }\n          expect(file).to receive(:perform_recursion).with(sources['/a']).and_return(nil)\n          expect(file).to receive(:perform_recursion).with(sources['/b']).and_return([])\n          expect(file).to receive(:perform_recursion).with(sources['/c']).and_return([data])\n          expect(file).not_to receive(:perform_recursion).with(sources['/d'])\n          expect(file).to receive(:newchild).with(\"foobar\").and_return(@resource)\n          file.recurse_remote({})\n        end\n      end\n\n      describe \"and :sourceselect is set to :all\" do\n        before do\n          file[:sourceselect] = :all\n        end\n\n        it \"should return every found file that is not in a previous source\" do\n          klass = Puppet::FileServing::Metadata\n\n          file[:source] = abs_path = %w{/a /b /c /d}.map {|f| File.expand_path(f) }\n          allow(file).to receive(:newchild).and_return(@resource)\n\n          one = [klass.new(abs_path[0], :relative_path => \"a\")]\n          expect(file).to receive(:perform_recursion).with(sources['/a']).and_return(one)\n          expect(file).to receive(:newchild).with(\"a\").and_return(@resource)\n\n          two = [klass.new(abs_path[1], :relative_path => \"a\"), klass.new(abs_path[1], :relative_path => \"b\")]\n          expect(file).to receive(:perform_recursion).with(sources['/b']).and_return(two)\n          expect(file).to receive(:newchild).with(\"b\").and_return(@resource)\n\n          three = [klass.new(abs_path[2], :relative_path => \"a\"), klass.new(abs_path[2], :relative_path => \"c\")]\n          expect(file).to receive(:perform_recursion).with(sources['/c']).and_return(three)\n          expect(file).to receive(:newchild).with(\"c\").and_return(@resource)\n          expect(file).to receive(:perform_recursion).with(sources['/d']).and_return([])\n\n          file.recurse_remote({})\n        end\n      end\n    end\n  end\n\n  describe \"#perform_recursion\", :uses_checksums => true do\n    it \"should use Metadata to do its recursion\" do\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search)\n      file.perform_recursion(file[:path])\n    end\n\n    it \"should use the provided path as the key to the search\" do\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(\"/foo\", anything)\n      file.perform_recursion(\"/foo\")\n    end\n\n    it \"should return the results of the metadata search\" do\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).and_return(\"foobar\")\n      expect(file.perform_recursion(file[:path])).to eq(\"foobar\")\n    end\n\n    it \"should pass its recursion value to the search\" do\n      file[:recurse] = true\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(recurse: true))\n      file.perform_recursion(file[:path])\n    end\n\n    it \"should pass true if recursion is remote\" do\n      file[:recurse] = :remote\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(recurse: true))\n      file.perform_recursion(file[:path])\n    end\n\n    it \"should pass its recursion limit value to the search\" do\n      file[:recurselimit] = 10\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(recurselimit: 10))\n      file.perform_recursion(file[:path])\n    end\n\n    it \"should configure the search to ignore or manage links\" do\n      file[:links] = :manage\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(links: :manage))\n      file.perform_recursion(file[:path])\n    end\n\n    it \"should pass its 'ignore' setting to the search if it has one\" do\n      file[:ignore] = %w{.svn CVS}\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(ignore: %w{.svn CVS}))\n      file.perform_recursion(file[:path])\n    end\n\n    with_digest_algorithms do\n      it \"it should pass its 'checksum' setting #{metadata[:digest_algorithm]} to the search\" do\n        file[:source] = File.expand_path('/foo')\n        expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(anything, hash_including(checksum_type: digest_algorithm.intern))\n        file.perform_recursion(file[:path])\n      end\n    end\n  end\n\n  describe \"#remove_existing\" do\n    it \"should do nothing if the file doesn't exist\" do\n      expect(file.remove_existing(:file)).to eq(false)\n    end\n\n    it \"should fail if it can't backup the file\" do\n      file[:backup] = true\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => 'file'))\n      allow(file).to receive(:perform_backup).and_return(false)\n\n      expect { file.remove_existing(:file) }.to raise_error(Puppet::Error, /Could not back up; will not remove/)\n    end\n\n    describe \"backing up directories\" do\n      it \"should not backup directories if backup is true and force is false\" do\n        file[:backup] = true\n        file[:force] = false\n        allow(file).to receive(:stat).and_return(double('stat', :ftype => 'directory'))\n\n        expect(file).not_to receive(:perform_backup)\n        expect(file).to receive(:warning).with(\"Could not back up file of type directory\")\n        expect(file.remove_existing(:file)).to eq(false)\n      end\n\n      it \"should backup directories if backup is true and force is true\" do\n        file[:backup] = true\n        file[:force] = true\n        allow(file).to receive(:stat).and_return(double('stat', :ftype => 'directory'))\n\n        expect(FileUtils).to receive(:rmtree).with(file[:path])\n        expect(file).to receive(:perform_backup).and_return(true)\n\n        expect(file.remove_existing(:file)).to eq(true)\n      end\n    end\n\n    it \"should not do anything if the file is already the right type and not a link\" do\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => 'file'))\n\n      expect(file.remove_existing(:file)).to eq(false)\n    end\n\n    it \"should not remove directories and should not invalidate the stat unless force is true\" do\n      file[:force] = false\n      # Actually call stat to set @needs_stat to nil\n      file.stat\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => 'directory'))\n\n      expect(file.instance_variable_get(:@stat)).to eq(nil)\n    end\n\n    it \"should remove a directory if backup is true and force is true\" do\n      file[:backup] = true\n      file[:force] = true\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => 'directory'))\n\n      expect(FileUtils).to receive(:rmtree).with(file[:path])\n\n      expect(file.remove_existing(:file)).to eq(true)\n    end\n\n    it \"should remove an existing file\" do\n      allow(file).to receive(:perform_backup).and_return(true)\n      FileUtils.touch(path)\n\n      expect(file.remove_existing(:directory)).to eq(true)\n\n      expect(Puppet::FileSystem.exist?(file[:path])).to eq(false)\n    end\n\n    it \"should remove an existing link\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n      allow(file).to receive(:perform_backup).and_return(true)\n\n      target = tmpfile('link_target')\n      FileUtils.touch(target)\n      Puppet::FileSystem.symlink(target, path)\n      file[:target] = target\n\n      expect(file.remove_existing(:directory)).to eq(true)\n\n      expect(Puppet::FileSystem.exist?(file[:path])).to eq(false)\n    end\n\n    it \"should fail if the file is not a directory, link, file, fifo, socket, or is unknown\" do\n      file[:backup] = 'puppet'\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => 'blockSpecial'))\n\n      expect(file).to receive(:warning).with(\"Could not back up file of type blockSpecial\")\n      expect { file.remove_existing(:file) }.to raise_error(Puppet::Error, /Could not remove files of type blockSpecial/)\n    end\n\n    it \"should invalidate the existing stat of the file\" do\n      # Actually call stat to set @needs_stat to nil\n      file.stat\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => 'file'))\n\n      allow(Puppet::FileSystem).to receive(:unlink)\n\n      expect(file.remove_existing(:directory)).to eq(true)\n      expect(file.instance_variable_get(:@stat)).to eq(:needs_stat)\n    end\n  end\n\n  describe \"#retrieve\" do\n    it \"should copy the source values if the 'source' parameter is set\" do\n      file[:source] = File.expand_path('/foo/bar')\n      expect(file.parameter(:source)).to receive(:copy_source_values)\n      file.retrieve\n    end\n  end\n\n  describe \"#should_be_file?\" do\n    it \"should have a method for determining if the file should be a normal file\" do\n      expect(file).to respond_to(:should_be_file?)\n    end\n\n    it \"should be a file if :ensure is set to :file\" do\n      file[:ensure] = :file\n      expect(file).to be_should_be_file\n    end\n\n    it \"should be a file if :ensure is set to :present and the file exists as a normal file\" do\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => \"file\"))\n      file[:ensure] = :present\n      expect(file).to be_should_be_file\n    end\n\n    it \"should not be a file if :ensure is set to something other than :file\" do\n      file[:ensure] = :directory\n      expect(file).to_not be_should_be_file\n    end\n\n    it \"should not be a file if :ensure is set to :present and the file exists but is not a normal file\" do\n      allow(file).to receive(:stat).and_return(double('stat', :ftype => \"directory\"))\n      file[:ensure] = :present\n      expect(file).to_not be_should_be_file\n    end\n\n    it \"should be a file if :ensure is not set and :content is\" do\n      file[:content] = \"foo\"\n      expect(file).to be_should_be_file\n    end\n\n    it \"should be a file if neither :ensure nor :content is set but the file exists as a normal file\" do\n      allow(file).to receive(:stat).and_return(double(\"stat\", :ftype => \"file\"))\n      expect(file).to be_should_be_file\n    end\n\n    it \"should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file\" do\n      allow(file).to receive(:stat).and_return(double(\"stat\", :ftype => \"directory\"))\n      expect(file).to_not be_should_be_file\n    end\n  end\n\n  describe \"#stat\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n    before do\n      target = tmpfile('link_target')\n      FileUtils.touch(target)\n      Puppet::FileSystem.symlink(target, path)\n\n      file[:target] = target\n      file[:links] = :manage # so we always use :lstat\n    end\n\n    it \"should stat the target if it is following links\" do\n      file[:links] = :follow\n\n      expect(file.stat.ftype).to eq('file')\n    end\n\n    it \"should stat the link if is it not following links\" do\n      file[:links] = :manage\n\n      expect(file.stat.ftype).to eq('link')\n    end\n\n    it \"should return nil if the file does not exist\" do\n      file[:path] = make_absolute('/foo/bar/baz/non-existent')\n\n      expect(file.stat).to be_nil\n    end\n\n    it \"should return nil if the file cannot be stat'ed\" do\n      dir = tmpfile('link_test_dir')\n      child = File.join(dir, 'some_file')\n      Dir.mkdir(dir)\n      File.chmod(0, dir)\n\n      file[:path] = child\n\n      expect(file.stat).to be_nil\n\n      # chmod it back so we can clean it up\n      File.chmod(0777, dir)\n    end\n\n    it \"should return nil if parts of path are no directories\" do\n      regular_file = tmpfile('ENOTDIR_test')\n      FileUtils.touch(regular_file)\n      impossible_child = File.join(regular_file, 'some_file')\n\n      file[:path] = impossible_child\n      expect(file.stat).to be_nil\n    end\n\n    it \"should return the stat instance\" do\n      expect(file.stat).to be_a(File::Stat)\n    end\n\n    it \"should cache the stat instance\" do\n      expect(file.stat.object_id).to eql(file.stat.object_id)\n    end\n  end\n\n  describe \"#write\" do\n    describe \"when resource mode is supplied\" do\n      before do\n        allow(file).to receive(:property_fix)\n      end\n\n      context \"and writing temporary files\" do\n        before do\n          allow(file).to receive(:write_temporary_file?).and_return(true)\n        end\n\n        it \"should convert symbolic mode to int\" do\n          file[:mode] = 'oga=r'\n          expect(Puppet::Util).to receive(:replace_file).with(file[:path], 0444, { :staging_location => nil, :validate_callback => nil })\n          file.write\n        end\n\n        it \"should support int modes\" do\n          file[:mode] = '0444'\n          expect(Puppet::Util).to receive(:replace_file).with(file[:path], 0444, { :staging_location => nil, :validate_callback => nil })\n          file.write\n        end\n      end\n\n      context \"and not writing temporary files\" do\n        before do\n          allow(file).to receive(:write_temporary_file?).and_return(false)\n        end\n\n        it \"should set a umask of 0\" do\n          file[:mode] = 'oga=r'\n          expect(Puppet::Util).to receive(:withumask).with(0)\n          file.write\n        end\n\n        it \"should convert symbolic mode to int\" do\n          file[:mode] = 'oga=r'\n          expect(File).to receive(:open).with(file[:path], anything, 0444)\n          file.write\n        end\n\n        it \"should support int modes\" do\n          file[:mode] = '0444'\n          expect(File).to receive(:open).with(file[:path], anything, 0444)\n          file.write\n        end\n      end\n    end\n\n    describe \"when resource mode is not supplied\" do\n      context \"and content is supplied\" do\n        it \"should default to 0644 mode\" do\n          file = described_class.new(:path => path, :content => FILE_CONTENT)\n\n          file.write file.parameter(:content)\n\n          expect(File.stat(file[:path]).mode & 0777).to eq(0644)\n        end\n      end\n\n      context \"and no content is supplied\" do\n        it \"should use puppet's default umask of 022\" do\n          file = described_class.new(:path => path)\n\n          umask_from_the_user = 0777\n          Puppet::Util.withumask(umask_from_the_user) do\n            file.write\n          end\n\n          expect(File.stat(file[:path]).mode & 0777).to eq(0644)\n        end\n      end\n    end\n  end\n\n  describe \"#write_temporary_file?\" do\n    it \"should be true if the file has specified content\" do\n      file[:content] = 'some content'\n\n      expect(file.send(:write_temporary_file?)).to be_truthy\n    end\n\n    it \"should be true if the file has specified source\" do\n      file[:source] = File.expand_path('/tmp/foo')\n\n      expect(file.send(:write_temporary_file?)).to be_truthy\n    end\n\n    it \"should be false if the file has neither content nor source\" do\n      expect(file.send(:write_temporary_file?)).to be_falsey\n    end\n\n    context \"and staging_location is provided\" do\n      it \"should write the file first to the staging location\" do\n        file[:content] = 'some content'\n        file[:staging_location] = Dir.tmpdir()\n        expect(Puppet::Util).to receive(:replace_file).with(file[:path], nil, { staging_location: Dir.tmpdir(), validate_callback: nil })\n        file.write\n      end\n    end\n  end\n\n  describe \"#property_fix\" do\n    {\n      :mode     => '0777',\n      :owner    => 'joeuser',\n      :group    => 'joeusers',\n      :seluser  => 'seluser',\n      :selrole  => 'selrole',\n      :seltype  => 'seltype',\n      :selrange => 'selrange'\n    }.each do |name,value|\n      it \"should sync the #{name} property if it's not in sync\" do\n        file[name] = value\n\n        prop = file.property(name)\n        expect(prop).to receive(:retrieve)\n        expect(prop).to receive(:safe_insync?).and_return(false)\n        expect(prop).to receive(:sync)\n\n        file.send(:property_fix)\n      end\n    end\n  end\n\n  describe \"when autorequiring\" do\n    describe \"target\" do\n      it \"should require file resource when specified with the target property\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n        file = described_class.new(:path => File.expand_path(\"/foo\"), :ensure => :directory)\n        link = described_class.new(:path => File.expand_path(\"/bar\"), :ensure => :link, :target => File.expand_path(\"/foo\"))\n        catalog.add_resource file\n        catalog.add_resource link\n        reqs = link.autorequire\n        expect(reqs.size).to eq(1)\n        expect(reqs[0].source).to eq(file)\n        expect(reqs[0].target).to eq(link)\n      end\n\n      it \"should require file resource when specified with the ensure property\" do\n        file = described_class.new(:path => File.expand_path(\"/foo\"), :ensure => :directory)\n        link = described_class.new(:path => File.expand_path(\"/bar\"), :ensure => File.expand_path(\"/foo\"))\n        catalog.add_resource file\n        catalog.add_resource link\n        reqs = link.autorequire\n        expect(reqs.size).to eq(1)\n        expect(reqs[0].source).to eq(file)\n        expect(reqs[0].target).to eq(link)\n      end\n\n      it \"should not require target if target is not managed\", :if => described_class.defaultprovider.feature?(:manages_symlinks) do\n        link = described_class.new(:path => File.expand_path('/foo'), :ensure => :link, :target => '/bar')\n        catalog.add_resource link\n        expect(link.autorequire.size).to eq(0)\n      end\n    end\n\n    describe \"directories\" do\n      it \"should autorequire its parent directory\" do\n        dir = described_class.new(:path => File.dirname(path))\n        catalog.add_resource file\n        catalog.add_resource dir\n        reqs = file.autorequire\n        expect(reqs[0].source).to eq(dir)\n        expect(reqs[0].target).to eq(file)\n      end\n\n      it \"should autorequire its nearest ancestor directory\" do\n        dir = described_class.new(:path => File.dirname(path))\n        grandparent = described_class.new(:path => File.dirname(File.dirname(path)))\n        catalog.add_resource file\n        catalog.add_resource dir\n        catalog.add_resource grandparent\n        reqs = file.autorequire\n        expect(reqs.length).to eq(1)\n        expect(reqs[0].source).to eq(dir)\n        expect(reqs[0].target).to eq(file)\n      end\n\n      it \"should not autorequire anything when there is no nearest ancestor directory\" do\n        catalog.add_resource file\n        expect(file.autorequire).to be_empty\n      end\n\n      it \"should not autorequire its parent dir if its parent dir is itself\" do\n        file[:path] = File.expand_path('/')\n        catalog.add_resource file\n        expect(file.autorequire).to be_empty\n      end\n\n      describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n        describe \"when using UNC filenames\" do\n          it \"should autorequire its parent directory\" do\n            file[:path] = '//localhost/foo/bar/baz'\n            dir = described_class.new(:path => \"//localhost/foo/bar\")\n            catalog.add_resource file\n            catalog.add_resource dir\n            reqs = file.autorequire\n            expect(reqs[0].source).to eq(dir)\n            expect(reqs[0].target).to eq(file)\n          end\n\n          it \"should autorequire its nearest ancestor directory\" do\n            file = described_class.new(:path => \"//localhost/foo/bar/baz/qux\")\n            dir = described_class.new(:path => \"//localhost/foo/bar/baz\")\n            grandparent = described_class.new(:path => \"//localhost/foo/bar\")\n            catalog.add_resource file\n            catalog.add_resource dir\n            catalog.add_resource grandparent\n            reqs = file.autorequire\n            expect(reqs.length).to eq(1)\n            expect(reqs[0].source).to eq(dir)\n            expect(reqs[0].target).to eq(file)\n          end\n\n          it \"should not autorequire anything when there is no nearest ancestor directory\" do\n            file = described_class.new(:path => \"//localhost/foo/bar/baz/qux\")\n            catalog.add_resource file\n            expect(file.autorequire).to be_empty\n          end\n\n          it \"should not autorequire its parent dir if its parent dir is itself\" do\n            file = described_class.new(:path => \"//localhost/foo\")\n            catalog.add_resource file\n            puts file.autorequire\n            expect(file.autorequire).to be_empty\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when managing links\", :if => Puppet.features.manages_symlinks? do\n    require 'tempfile'\n\n    before :each do\n      Dir.mkdir(path)\n      @target = File.join(path, \"target\")\n      @link   = File.join(path, \"link\")\n\n      target = described_class.new(\n        :ensure => :file, :path => @target,\n        :catalog => catalog, :content => 'yayness',\n        :mode => '0644')\n      catalog.add_resource target\n\n      @link_resource = described_class.new(\n        :ensure => :link, :path => @link,\n        :target => @target, :catalog => catalog,\n        :mode => '0755')\n      catalog.add_resource @link_resource\n\n      # to prevent the catalog from trying to write state.yaml\n      allow(Puppet::Util::Storage).to receive(:store)\n    end\n\n    it \"should preserve the original file mode and ignore the one set by the link\" do\n      @link_resource[:links] = :manage # default\n      catalog.apply\n\n      # I convert them to strings so they display correctly if there's an error.\n      expect((Puppet::FileSystem.stat(@target).mode & 007777).to_s(8)).to eq('644')\n    end\n\n    it \"should manage the mode of the followed link\" do\n      if Puppet::Util::Platform.windows?\n        skip \"Windows cannot presently manage the mode when following symlinks\"\n      else\n        @link_resource[:links] = :follow\n        catalog.apply\n\n        expect((Puppet::FileSystem.stat(@target).mode & 007777).to_s(8)).to eq('755')\n      end\n    end\n  end\n\n  describe 'when using source' do\n    # different UTF-8 widths\n    # 1-byte A\n    # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n    # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n    # 4-byte <U+070E> - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n    let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ<U+070E>\n\n    it 'should allow UTF-8 characters and return a UTF-8 uri' do\n      filename = \"/bar #{mixed_utf8}\"\n      source = \"puppet://foo#{filename}\"\n      file[:source] = source\n\n      # intercept the indirector call to provide back mocked metadata for the given URI\n      metadata = double('metadata', :source => source)\n      expect(metadata).to receive(:source=)\n      expect(Puppet::FileServing::Metadata.indirection).to receive(:find).with(source, anything).and_return(metadata)\n\n      uri = file.parameters[:source].uri\n      expect(Puppet::Util.uri_unescape(uri.path)).to eq(filename)\n      expect(uri.path.encoding).to eq(Encoding::UTF_8)\n    end\n\n    matcher :request_key do |expected|\n      match do |actual|\n        values_match? expected, actual.key\n      end\n    end\n\n    it 'should allow UTF-8 characters inside the indirector / terminus code' do\n      filename = \"/bar #{mixed_utf8}\"\n      source = \"puppet://foo#{filename}\"\n      file[:source] = source\n\n      # for this test to properly trigger previously errant behavior, the code for\n      # Puppet::FileServing::Metadata.indirection.find must run and produce an\n      # instance of Puppet::Indirector::FileMetadata::Rest that can be amended\n      metadata = double('metadata', :source => source)\n      expect(metadata).to receive(:source=)\n      require 'puppet/indirector/file_metadata/rest'\n      expect_any_instance_of(Puppet::Indirector::FileMetadata::Rest).to receive(:find).with(request_key(filename[1..-1])).and_return(metadata)\n\n      uri = file.parameters[:source].uri\n      expect(Puppet::Util.uri_unescape(uri.path)).to eq(filename)\n      expect(uri.path.encoding).to eq(Encoding::UTF_8)\n    end\n  end\n\n  describe \"when using source\" do\n    let(:source) { tmpfile('file_source') }\n\n    before do\n      file[:source] = source\n    end\n\n    Puppet::Type::File::ParameterChecksum.value_collection.values.reject {|v| v == :none}.each do |checksum_type|\n      describe \"with checksum '#{checksum_type}'\" do\n        before do\n          file[:checksum] = checksum_type\n        end\n\n        it 'should validate' do\n          expect { file.validate }.to_not raise_error\n        end\n\n        it 'should fail on an invalid checksum_value' do\n          file[:checksum_value] = ''\n          expect { file.validate }.to raise_error(Puppet::Error, \"Checksum value '' is not a valid checksum type #{checksum_type}\")\n        end\n\n        it 'should validate a valid checksum_value' do\n          file[:checksum_value] = CHECKSUM_VALUES[checksum_type]\n          expect { file.validate }.to_not raise_error\n        end\n\n        it 'fails if the checksum_value parameter and written file do not match' do\n          skip if checksum_type =~ /^(ctime|mtime)/\n\n          File.write(source, FILE_CONTENT)\n          file[:checksum_value] = INVALID_CHECKSUM_VALUES[checksum_type]\n\n          expect {\n            file.property(:checksum_value).sync\n          }.to raise_error(Puppet::Error, /File written to disk did not match desired checksum/)\n\n          expect(Puppet::FileSystem).to_not be_exist(file[:path])\n        end\n\n        it 'fails if the checksum_value parameter does not match, but the metadata does' do\n          skip if checksum_type =~ /^(ctime|mtime)/\n\n          File.write(source, FILE_CONTENT)\n          file[:checksum_value] = INVALID_CHECKSUM_VALUES[checksum_type]\n          allow(file.parameter(:source).metadata).to receive(:checksum).and_return(file[:checksum_value])\n\n          expect {\n            file.property(:checksum_value).sync\n          }.to raise_error(Puppet::Error, /File written to disk did not match desired checksum/)\n\n          expect(Puppet::FileSystem).to_not be_exist(file[:path])\n        end\n\n        it 'replaces a file from a source when the checksum matches' do\n          File.write(source, FILE_CONTENT)\n          file[:checksum_value] = CHECKSUM_VALUES[checksum_type]\n\n          file.property(:checksum_value).sync\n          checksum = file.parameter(:checksum).sum_file(file[:path])\n\n          if checksum_type =~ /^(ctime|mtime)/\n            # file on disk ctime/mtime will be later than expected time\n            expect(checksum).to match(/{#{checksum_type}}/)\n          else\n            expect(checksum).to eq(\"{#{checksum_type}}#{file[:checksum_value]}\")\n          end\n        end\n      end\n    end\n\n    describe \"on Windows when source_permissions is `use`\" do\n      before :each do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n\n        file[:source_permissions] = \"use\"\n      end\n      let(:err_message) { \"Copying owner/mode/group from the\" <<\n                          \" source file on Windows is not supported;\" <<\n                          \" use source_permissions => ignore.\" }\n\n      it \"should issue error when retrieving\" do\n        expect { file.retrieve }.to raise_error(err_message)\n      end\n\n      it \"should issue error when retrieving if only user is unspecified\" do\n        file[:group] = 2\n        file[:mode] = \"0003\"\n\n        expect { file.retrieve }.to raise_error(err_message)\n      end\n\n      it \"should issue error when retrieving if only group is unspecified\" do\n        file[:owner] = 1\n        file[:mode] = \"0003\"\n\n        expect { file.retrieve }.to raise_error(err_message)\n      end\n\n      it \"should issue error when retrieving if only mode is unspecified\" do\n        file[:owner] = 1\n        file[:group] = 2\n\n        expect { file.retrieve }.to raise_error(err_message)\n      end\n\n      it \"should issue warning when retrieve if group, owner, and mode are all specified\" do\n        file[:owner] = 1\n        file[:group] = 2\n        file[:mode] = \"0003\"\n\n        expect(file.parameter(:source)).to receive(:copy_source_values)\n        expect(file).to receive(:warning).with(err_message)\n        expect { file.retrieve }.not_to raise_error\n      end\n    end\n\n    describe \"with checksum 'none'\" do\n      before do\n        file[:checksum] = :none\n      end\n\n      it 'should raise an exception when validating' do\n        expect { file.validate }.to raise_error(/You cannot specify source when using checksum 'none'/)\n      end\n    end\n  end\n\n  describe \"when using content\" do\n    before :each do\n      file[:content] = FILE_CONTENT\n    end\n\n    (Puppet::Type::File::ParameterChecksum.value_collection.values - SOURCE_ONLY_CHECKSUMS).each do |checksum_type|\n      describe \"with checksum '#{checksum_type}'\" do\n        before do\n          file[:checksum] = checksum_type\n        end\n\n        it 'should validate' do\n          expect { file.validate }.to_not raise_error\n        end\n\n        it 'should fail on an invalid checksum_value' do\n          file[:checksum_value] = ''\n          expect { file.validate }.to raise_error(Puppet::Error, \"Checksum value '' is not a valid checksum type #{checksum_type}\")\n        end\n\n        it 'should validate a valid checksum_value' do\n          file[:checksum_value] = CHECKSUM_VALUES[checksum_type]\n          expect { file.validate }.to_not raise_error\n        end\n\n        it 'fails if the checksum_value parameter and written file do not match' do\n          file[:checksum_value] = INVALID_CHECKSUM_VALUES[checksum_type]\n\n          expect {\n            file.property(:content).sync\n          }.to raise_error(Puppet::Error, /File written to disk did not match desired checksum/)\n\n          expect(Puppet::FileSystem).to_not be_exist(file[:path])\n        end\n\n        it 'fails if the calculated checksum for the content and written file do not match' do\n          allow(file.parameter(:checksum)).to receive(:sum).and_return(INVALID_CHECKSUM_VALUES[checksum_type])\n\n          expect {\n            file.property(:content).sync\n          }.to raise_error(Puppet::Error, /File written to disk did not match desired checksum/)\n\n          expect(Puppet::FileSystem).to_not be_exist(file[:path])\n        end\n\n        it 'replaces a file from content when the checksum matches' do\n          file[:checksum_value] = CHECKSUM_VALUES[checksum_type]\n\n          file.property(:content).sync\n          checksum = file.parameter(:checksum).sum_file(file[:path])\n\n          if checksum_type =~ /^(ctime|mtime)/\n            # file on disk ctime/mtime will be later than expected time\n            expect(checksum).to match(/{#{checksum_type}}/)\n          else\n            expect(checksum).to eq(\"{#{checksum_type}}#{file[:checksum_value]}\")\n          end\n        end\n      end\n    end\n\n    SOURCE_ONLY_CHECKSUMS.each do |checksum_type|\n      describe \"with checksum '#{checksum_type}'\" do\n        it 'should raise an exception when validating' do\n          file[:checksum] = checksum_type\n\n          expect { file.validate }.to raise_error(/You cannot specify content when using checksum '#{checksum_type}'/)\n        end\n      end\n    end\n  end\n\n  describe \"when checksum is none\" do\n    before do\n      file[:checksum] = :none\n    end\n\n    it 'should validate' do\n      expect { file.validate }.to_not raise_error\n    end\n\n    it 'should fail on an invalid checksum_value' do\n      file[:checksum_value] = 'boo'\n      expect { file.validate }.to raise_error(Puppet::Error, \"Checksum value 'boo' is not a valid checksum type none\")\n    end\n\n    it 'should validate a valid checksum_value' do\n      file[:checksum_value] = ''\n      expect { file.validate }.to_not raise_error\n    end\n\n    it 'writes a file' do\n      file[:ensure] = :file\n      file.property(:ensure).sync\n\n      expect(file.parameter(:checksum).sum_file(file[:path])).to eq('{none}')\n    end\n  end\n\n  describe \"when auditing\" do\n    before :each do\n      # to prevent the catalog from trying to write state.yaml\n      allow(Puppet::Util::Storage).to receive(:store)\n    end\n\n    it \"should not fail if creating a new file if group is not set\" do\n      file = described_class.new(:path => path, :audit => 'all', :content => 'content')\n      catalog.add_resource(file)\n\n      report = catalog.apply.report\n\n      expect(report.resource_statuses[\"File[#{path}]\"]).not_to be_failed\n      expect(File.read(path)).to eq('content')\n    end\n\n    it \"should not log errors if creating a new file with ensure present and no content\" do\n      file[:audit]  = 'content'\n      file[:ensure] = 'present'\n      catalog.add_resource(file)\n\n      catalog.apply\n\n      expect(Puppet::FileSystem.exist?(path)).to be_truthy\n      expect(@logs).not_to be_any { |l|\n        # the audit metaparameter is deprecated and logs a warning\n        l.level != :notice\n      }\n    end\n  end\n\n  describe \"when specifying both source and checksum\" do\n    it 'should use the specified checksum when source is first' do\n      file[:source] = File.expand_path('/foo')\n      file[:checksum] = :md5lite\n\n      expect(file[:checksum]).to eq(:md5lite)\n    end\n\n    it 'should use the specified checksum when source is last' do\n      file[:checksum] = :md5lite\n      file[:source] = File.expand_path('/foo')\n\n      expect(file[:checksum]).to eq(:md5lite)\n    end\n  end\n\n  describe \"when validating\" do\n    [[:source, :target], [:source, :content], [:target, :content]].each do |prop1,prop2|\n      it \"should fail if both #{prop1} and #{prop2} are specified\" do\n          file[prop1] = prop1 == :source ? File.expand_path(\"prop1 value\") : \"prop1 value\"\n          file[prop2] = \"prop2 value\"\n        expect do\n          file.validate\n        end.to raise_error(Puppet::Error, /You cannot specify more than one of/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/filebucket_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:filebucket) do\n  include PuppetSpec::Files\n\n  describe \"when validating attributes\" do\n    %w{name server port path}.each do |attr|\n      it \"should have a '#{attr}' parameter\" do\n        expect(Puppet::Type.type(:filebucket).attrtype(attr.intern)).to eq(:param)\n      end\n    end\n\n    it \"should have its 'name' attribute set as its namevar\" do\n      expect(Puppet::Type.type(:filebucket).key_attributes).to eq([:name])\n    end\n  end\n\n  it \"should use the clientbucketdir as the path by default path\" do\n    Puppet.settings[:clientbucketdir] = \"/my/bucket\"\n    expect(Puppet::Type.type(:filebucket).new(:name => \"main\")[:path]).to eq(Puppet[:clientbucketdir])\n  end\n\n  it \"should not have a default port\" do\n    Puppet.settings[:serverport] = 50\n    expect(Puppet::Type.type(:filebucket).new(:name => \"main\")[:port]).to eq(nil)\n  end\n\n  it \"should not have a default server\" do\n    Puppet.settings[:server] = \"myserver\"\n    expect(Puppet::Type.type(:filebucket).new(:name => \"main\")[:server]).to eq(nil)\n  end\n\n  it \"be local by default\" do\n    bucket = Puppet::Type.type(:filebucket).new :name => \"main\"\n\n    expect(bucket.bucket).to be_local\n  end\n\n  describe \"path\" do\n    def bucket(hash)\n      Puppet::Type.type(:filebucket).new({:name => 'main'}.merge(hash))\n    end\n\n    it \"should accept false as a value\" do\n      expect { bucket(:path => false) }.not_to raise_error\n    end\n\n    it \"should accept true as a value\" do\n      expect { bucket(:path => true) }.not_to raise_error\n    end\n\n    it \"should fail when given an array of values\" do\n      expect { bucket(:path => ['one', 'two']) }.\n        to raise_error Puppet::Error, /only have one filebucket path/\n    end\n\n    %w{one ../one one/two}.each do |path|\n      it \"should fail if given a relative path of #{path.inspect}\" do\n        expect { bucket(:path => path) }.\n          to raise_error Puppet::Error, /Filebucket paths must be absolute/\n      end\n    end\n\n    it \"should succeed if given an absolute path\" do\n      expect { bucket(:path => make_absolute('/tmp/bucket')) }.not_to raise_error\n    end\n\n    it \"not be local if path is false\" do\n      expect(bucket(:path => false).bucket).not_to be_local\n    end\n\n    it \"be local if both a path and a server are specified\" do\n      expect(bucket(:server => \"puppet\", :path => make_absolute(\"/my/path\")).bucket).to be_local\n    end\n  end\n\n  describe \"when creating the filebucket\" do\n    before do\n      @bucket = double('bucket', :name= => nil)\n    end\n\n    it \"should use any provided path\" do\n      path = make_absolute(\"/foo/bar\")\n      bucket = Puppet::Type.type(:filebucket).new :name => \"main\", :path => path\n      expect(Puppet::FileBucket::Dipper).to receive(:new).with({:Path => path}).and_return(@bucket)\n      bucket.bucket\n    end\n\n    it \"should use any provided server and port\" do\n      bucket = Puppet::Type.type(:filebucket).new :name => \"main\", :server => \"myserv\", :port => \"myport\", :path => false\n      expect(Puppet::FileBucket::Dipper).to receive(:new).with({:Server => \"myserv\", :Port => \"myport\"}).and_return(@bucket)\n      bucket.bucket\n    end\n\n    it \"should not try to guess server or port if the path is unset and no server is provided\" do\n      Puppet.settings[:server] = \"myserv\"\n      Puppet.settings[:server_list] = ['server_list_0', 'server_list_1']\n      expect(Puppet::FileBucket::Dipper).to receive(:new).with({:Server => nil, :Port => nil}).and_return(@bucket)\n\n      bucket = Puppet::Type.type(:filebucket).new :name => \"main\", :path => false\n      bucket.bucket\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/group_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:group) do\n  before do\n    @class = Puppet::Type.type(:group)\n  end\n\n  it \"should have a system_groups feature\" do\n    expect(@class.provider_feature(:system_groups)).not_to be_nil\n  end\n\n  it 'should default to `present`' do\n    expect(@class.new(:name => \"foo\")[:ensure]).to eq(:present)\n  end\n\n  it 'should set ensure to whatever is passed in' do\n    expect(@class.new(:name => \"foo\", :ensure => 'absent')[:ensure]).to eq(:absent)\n  end\n\n  describe \"when validating attributes\" do\n    [:name, :allowdupe].each do |param|\n      it \"should have a #{param} parameter\" do\n        expect(@class.attrtype(param)).to eq(:param)\n      end\n    end\n\n    [:ensure, :gid].each do |param|\n      it \"should have a #{param} property\" do\n        expect(@class.attrtype(param)).to eq(:property)\n      end\n    end\n\n    it \"should convert gids provided as strings into integers\" do\n      expect(@class.new(:name => \"foo\", :gid => \"15\")[:gid]).to eq(15)\n    end\n\n    it \"should accepts gids provided as integers\" do\n      expect(@class.new(:name => \"foo\", :gid => 15)[:gid]).to eq(15)\n    end\n  end\n\n  it \"should have a boolean method for determining if duplicates are allowed\" do\n    expect(@class.new(:name => \"foo\")).to respond_to \"allowdupe?\"\n  end\n\n  it \"should have a boolean method for determining if system groups are allowed\" do\n    expect(@class.new(:name => \"foo\")).to respond_to \"system?\"\n  end\n\n  it \"should call 'create' to create the group\" do\n    group = @class.new(:name => \"foo\", :ensure => :present)\n    expect(group.provider).to receive(:create)\n    group.parameter(:ensure).sync\n  end\n\n  it \"should call 'delete' to remove the group\" do\n    group = @class.new(:name => \"foo\", :ensure => :absent)\n    expect(group.provider).to receive(:delete)\n    group.parameter(:ensure).sync\n  end\n\n  it \"delegates the existence check to its provider\" do\n    provider = @class.provide(:testing) do\n      def exists?\n        true\n      end\n    end\n    provider_instance = provider.new\n\n    type = @class.new(:name => \"group\", :provider => provider_instance)\n\n    expect(type.exists?).to eq(true)\n  end\n\n  describe \"should delegate :members implementation to the provider:\"  do\n    let (:provider) do\n      @class.provide(:testing) do\n        has_features :manages_members\n\n        def members\n          []\n        end\n\n        def members_insync?(current, should)\n          current == should\n        end\n\n        def members_to_s(values)\n          values.map { |v| \"#{v} ()\" }.join(', ')\n        end\n      end\n    end\n    let (:provider_instance) { provider.new }\n    let (:type) { @class.new(:name => \"group\", :provider => provider_instance, :members => ['user1']) }\n\n    it \"insync? calls members_insync?\" do\n      expect(type.property(:members).insync?(['user1'])).to be_truthy\n    end\n\n    it \"is_to_s and should_to_s call members_to_s\" do\n      expect(type.property(:members).is_to_s('user1')).to eq('user1 ()')\n      expect(type.property(:members).should_to_s('user1,user2')).to eq('user1 (), user2 ()')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/noop_metaparam_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/type'\n\ndescribe Puppet::Type.type(:file).attrclass(:noop) do\n  include PuppetSpec::Files\n\n  before do\n    allow(Puppet.settings).to receive(:use)\n    @file = Puppet::Type.newfile :path => make_absolute(\"/what/ever\")\n  end\n\n  it \"should accept true as a value\" do\n    expect { @file[:noop] = true }.not_to raise_error\n  end\n\n  it \"should accept false as a value\" do\n    expect { @file[:noop] = false }.not_to raise_error\n  end\n\n  describe \"when set on a resource\" do\n    it \"should default to the :noop setting\" do\n      Puppet[:noop] = true\n      expect(@file.noop).to eq(true)\n    end\n\n    it \"should prefer true values from the attribute\" do\n      @file[:noop] = true\n      expect(@file.noop).to be_truthy\n    end\n\n    it \"should prefer false values from the attribute\" do\n      @file[:noop] = false\n      expect(@file.noop).to be_falsey\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/package/package_settings_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package) do\n  before do\n    allow(Puppet::Util::Storage).to receive(:store)\n  end\n\n  it \"should have a :package_settings feature that requires :package_settings_insync?, :package_settings and :package_settings=\" do\n    expect(described_class.provider_feature(:package_settings).methods).to eq([:package_settings_insync?, :package_settings, :package_settings=])\n  end\n\n  context \"when validating attributes\" do\n    it \"should have a package_settings property\" do\n      expect(described_class.attrtype(:package_settings)).to eq(:property)\n    end\n  end\n\n  context \"when validating attribute values\" do\n    let(:provider) do\n      double('provider',\n             :class => described_class.defaultprovider,\n             :clear => nil,\n             :validate_source => false)\n    end\n\n    before do\n      allow(provider.class).to receive(:supports_parameter?).and_return(true)\n      allow(described_class.defaultprovider).to receive(:new).and_return(provider)\n    end\n\n    describe 'package_settings' do\n      context \"with a minimalistic provider supporting package_settings\" do\n        context \"and {:package_settings => :settings}\" do\n          let(:resource) do \n            described_class.new :name => 'foo', :package_settings => :settings\n          end\n\n          it { expect { resource }.to_not raise_error }\n\n          it \"should set package_settings to :settings\" do\n            expect(resource.value(:package_settings)).to be :settings\n          end\n        end\n      end\n\n      context \"with a provider that supports validation of the package_settings\" do\n        context \"and {:package_settings => :valid_value}\" do\n          before do\n            expect(provider).to receive(:package_settings_validate).once.with(:valid_value).and_return(true)\n          end\n\n          let(:resource) do \n            described_class.new :name => 'foo', :package_settings => :valid_value\n          end\n\n          it { expect { resource }.to_not raise_error }\n\n          it \"should set package_settings to :valid_value\" do\n            expect(resource.value(:package_settings)).to eq(:valid_value)\n          end\n        end\n\n        context \"and {:package_settings => :invalid_value}\" do\n          before do\n            msg = \"package_settings must be a Hash, not Symbol\"\n            expect(provider).to receive(:package_settings_validate).once.\n              with(:invalid_value).and_raise(ArgumentError, msg)\n          end\n\n          let(:resource) do \n            described_class.new :name => 'foo', :package_settings => :invalid_value\n          end\n\n          it do\n            expect { resource }.to raise_error Puppet::Error,\n              /package_settings must be a Hash, not Symbol/\n          end\n        end\n      end\n\n      context \"with a provider that supports munging of the package_settings\" do\n        context \"and {:package_settings => 'A'}\" do\n          before do\n            expect(provider).to receive(:package_settings_munge).once.with('A').and_return(:a)\n          end\n\n          let(:resource) do \n            described_class.new :name => 'foo', :package_settings => 'A'\n          end\n\n          it do\n            expect { resource }.to_not raise_error \n          end\n\n          it \"should set package_settings to :a\" do\n            expect(resource.value(:package_settings)).to be :a\n          end\n        end\n      end\n    end\n  end\n\n  describe \"package_settings property\" do\n    let(:provider) do\n      double('provider',\n             :class => described_class.defaultprovider,\n             :clear => nil,\n             :validate_source => false)\n    end\n\n    before do\n      allow(provider.class).to receive(:supports_parameter?).and_return(true)\n      allow(described_class.defaultprovider).to receive(:new).and_return(provider)\n    end\n\n    context \"with {package_settings => :should}\" do\n      let(:resource) do \n        described_class.new :name => 'foo', :package_settings => :should\n      end\n\n      describe \"#insync?(:is)\" do\n        it \"returns the result of provider.package_settings_insync?(:should,:is)\" do\n          expect(resource.provider).to receive(:package_settings_insync?).once.with(:should,:is).and_return(:ok1)\n          expect(resource.property(:package_settings).insync?(:is)).to be :ok1\n        end\n      end\n\n      describe \"#should_to_s(:newvalue)\" do\n        it \"returns the result of provider.package_settings_should_to_s(:should,:newvalue)\" do\n          expect(resource.provider).to receive(:package_settings_should_to_s).once.with(:should,:newvalue).and_return(:ok2)\n          expect(resource.property(:package_settings).should_to_s(:newvalue)).to be :ok2\n        end\n      end\n\n      describe \"#is_to_s(:currentvalue)\" do\n        it \"returns the result of provider.package_settings_is_to_s(:should,:currentvalue)\" do\n          expect(resource.provider).to receive(:package_settings_is_to_s).once.with(:should,:currentvalue).and_return(:ok3)\n          expect(resource.property(:package_settings).is_to_s(:currentvalue)).to be :ok3\n        end\n      end\n    end\n\n    context \"with any non-nil package_settings\" do\n      describe \"#change_to_s(:currentvalue,:newvalue)\" do\n        let(:resource) do \n          described_class.new :name => 'foo', :package_settings => {}\n        end\n\n        it \"returns the result of provider.package_settings_change_to_s(:currentvalue,:newvalue)\" do\n          expect(resource.provider).to receive(:package_settings_change_to_s).once.with(:currentvalue,:newvalue).and_return(:ok4)\n          expect(resource.property(:package_settings).change_to_s(:currentvalue,:newvalue)).to be :ok4\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/package_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:package) do\n  before do\n    allow(Process).to receive(:euid).and_return(0)\n    allow(Puppet::Util::Storage).to receive(:store)\n  end\n\n  it \"should have a :reinstallable feature that requires the :reinstall method\" do\n    expect(Puppet::Type.type(:package).provider_feature(:reinstallable).methods).to eq([:reinstall])\n  end\n\n  it \"should have an :installable feature that requires the :install method\" do\n    expect(Puppet::Type.type(:package).provider_feature(:installable).methods).to eq([:install])\n  end\n\n  it \"should have an :uninstallable feature that requires the :uninstall method\" do\n    expect(Puppet::Type.type(:package).provider_feature(:uninstallable).methods).to eq([:uninstall])\n  end\n\n  it \"should have an :upgradeable feature that requires :update and :latest methods\" do\n    expect(Puppet::Type.type(:package).provider_feature(:upgradeable).methods).to eq([:update, :latest])\n  end\n\n  it \"should have a :purgeable feature that requires the :purge latest method\" do\n    expect(Puppet::Type.type(:package).provider_feature(:purgeable).methods).to eq([:purge])\n  end\n\n  it \"should have a :versionable feature\" do\n    expect(Puppet::Type.type(:package).provider_feature(:versionable)).not_to be_nil\n  end\n\n  it \"should have a :supports_flavors feature\" do\n    expect(Puppet::Type.type(:package).provider_feature(:supports_flavors)).not_to be_nil\n  end\n\n  it \"should have a :package_settings feature that requires :package_settings_insync?, :package_settings and :package_settings=\" do\n    expect(Puppet::Type.type(:package).provider_feature(:package_settings).methods).to eq([:package_settings_insync?, :package_settings, :package_settings=])\n  end\n\n  it \"should default to being installed\" do\n    pkg = Puppet::Type.type(:package).new(:name => \"yay\", :provider => :apt)\n    expect(pkg.should(:ensure)).to eq(:present)\n  end\n\n  describe \"when validating attributes\" do\n    [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom, :allow_virtual, :reinstall_on_refresh].each do |param|\n      it \"should have a #{param} parameter\" do\n        expect(Puppet::Type.type(:package).attrtype(param)).to eq(:param)\n      end\n    end\n\n    it \"should have an ensure property\" do\n      expect(Puppet::Type.type(:package).attrtype(:ensure)).to eq(:property)\n    end\n\n    it \"should have a package_settings property\" do\n      expect(Puppet::Type.type(:package).attrtype(:package_settings)).to eq(:property)\n    end\n\n    it \"should have a flavor property\" do\n      expect(Puppet::Type.type(:package).attrtype(:flavor)).to eq(:property)\n    end\n  end\n\n  describe \"when validating attribute values\" do\n    before :each do\n      @provider = double(\n        'provider',\n        :class           => Puppet::Type.type(:package).defaultprovider,\n        :clear           => nil,\n        :validate_source => nil\n      )\n      allow(Puppet::Type.type(:package).defaultprovider).to receive(:new).and_return(@provider)\n    end\n\n    after :each do\n      Puppet::Type.type(:package).defaultprovider = nil\n    end\n\n    it \"should support :present as a value to :ensure\" do\n      Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :present)\n    end\n\n    it \"should alias :installed to :present as a value to :ensure\" do\n      pkg = Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :installed)\n      expect(pkg.should(:ensure)).to eq(:present)\n    end\n\n    it \"should support :absent as a value to :ensure\" do\n      Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :absent)\n    end\n\n    it \"should support :purged as a value to :ensure if the provider has the :purgeable feature\" do\n      expect(@provider).to receive(:satisfies?).with([:purgeable]).and_return(true)\n      Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :purged)\n    end\n\n    it \"should not support :purged as a value to :ensure if the provider does not have the :purgeable feature\" do\n      expect(@provider).to receive(:satisfies?).with([:purgeable]).and_return(false)\n      expect { Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :purged) }.to raise_error(Puppet::Error)\n    end\n\n    it \"should support :latest as a value to :ensure if the provider has the :upgradeable feature\" do\n      expect(@provider).to receive(:satisfies?).with([:upgradeable]).and_return(true)\n      Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :latest)\n    end\n\n    it \"should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature\" do\n      expect(@provider).to receive(:satisfies?).with([:upgradeable]).and_return(false)\n      expect { Puppet::Type.type(:package).new(:name => \"yay\", :ensure => :latest) }.to raise_error(Puppet::Error)\n    end\n\n    it \"should support version numbers as a value to :ensure if the provider has the :versionable feature\" do\n      expect(@provider).to receive(:satisfies?).with([:versionable]).and_return(true)\n      Puppet::Type.type(:package).new(:name => \"yay\", :ensure => \"1.0\")\n    end\n\n    it \"should not support version numbers as a value to :ensure if the provider does not have the :versionable feature\" do\n      expect(@provider).to receive(:satisfies?).with([:versionable]).and_return(false)\n      expect { Puppet::Type.type(:package).new(:name => \"yay\", :ensure => \"1.0\") }.to raise_error(Puppet::Error)\n    end\n\n    it \"should accept any string as an argument to :source\" do\n      expect { Puppet::Type.type(:package).new(:name => \"yay\", :source => \"stuff\") }.to_not raise_error\n    end\n\n    it \"should not accept a non-string name\" do\n      expect do\n        Puppet::Type.type(:package).new(:name => [\"error\"])\n      end.to raise_error(Puppet::ResourceError, /Name must be a String/)\n    end\n  end\n\n  module PackageEvaluationTesting\n    def setprops(properties)\n      allow(@provider).to receive(:properties).and_return(properties)\n    end\n  end\n\n  describe Puppet::Type.type(:package) do\n    before :each do\n      @provider = double(\n        'provider',\n        :class           => Puppet::Type.type(:package).defaultprovider,\n        :clear           => nil,\n        :satisfies?      => true,\n        :name            => :mock,\n        :validate_source => nil\n      )\n      allow(Puppet::Type.type(:package).defaultprovider).to receive(:new).and_return(@provider)\n      allow(Puppet::Type.type(:package).defaultprovider).to receive(:instances).and_return([])\n      @package = Puppet::Type.type(:package).new(:name => \"yay\")\n\n      @catalog = Puppet::Resource::Catalog.new\n      @catalog.add_resource(@package)\n    end\n\n    describe Puppet::Type.type(:package), \"when it should be purged\" do\n      include PackageEvaluationTesting\n\n      before { @package[:ensure] = :purged }\n\n      it \"should do nothing if it is :purged\" do\n        expect(@provider).to receive(:properties).and_return(:ensure => :purged).at_least(:once)\n        @catalog.apply\n      end\n\n      [:absent, :installed, :present, :latest].each do |state|\n        it \"should purge if it is #{state.to_s}\" do\n          allow(@provider).to receive(:properties).and_return(:ensure => state)\n          expect(@provider).to receive(:purge)\n          @catalog.apply\n        end\n      end\n    end\n\n    describe Puppet::Type.type(:package), \"when it should be absent\" do\n      include PackageEvaluationTesting\n\n      before { @package[:ensure] = :absent }\n\n      [:purged, :absent].each do |state|\n        it \"should do nothing if it is #{state.to_s}\" do\n          expect(@provider).to receive(:properties).and_return(:ensure => state).at_least(:once)\n          @catalog.apply\n        end\n      end\n\n      [:installed, :present, :latest].each do |state|\n        it \"should uninstall if it is #{state.to_s}\" do\n          allow(@provider).to receive(:properties).and_return(:ensure => state)\n          expect(@provider).to receive(:uninstall)\n          @catalog.apply\n        end\n      end\n    end\n\n    describe Puppet::Type.type(:package), \"when it should be present\" do\n      include PackageEvaluationTesting\n\n      before { @package[:ensure] = :present }\n\n      [:present, :latest, \"1.0\"].each do |state|\n        it \"should do nothing if it is #{state.to_s}\" do\n          expect(@provider).to receive(:properties).and_return(:ensure => state).at_least(:once)\n          @catalog.apply\n        end\n      end\n\n      [:purged, :absent].each do |state|\n        it \"should install if it is #{state.to_s}\" do\n          allow(@provider).to receive(:properties).and_return(:ensure => state)\n          expect(@provider).to receive(:install)\n          @catalog.apply\n        end\n      end\n    end\n\n    describe Puppet::Type.type(:package), \"when it should be latest\" do\n      include PackageEvaluationTesting\n\n      before { @package[:ensure] = :latest }\n\n      [:purged, :absent].each do |state|\n        it \"should upgrade if it is #{state.to_s}\" do\n          allow(@provider).to receive(:properties).and_return(:ensure => state)\n          expect(@provider).to receive(:update)\n          @catalog.apply\n        end\n      end\n\n      it \"should upgrade if the current version is not equal to the latest version\" do\n        allow(@provider).to receive(:properties).and_return(:ensure => \"1.0\")\n        allow(@provider).to receive(:latest).and_return(\"2.0\")\n        expect(@provider).to receive(:update)\n        @catalog.apply\n      end\n\n      it \"should do nothing if it is equal to the latest version\" do\n        allow(@provider).to receive(:properties).and_return(:ensure => \"1.0\")\n        allow(@provider).to receive(:latest).and_return(\"1.0\")\n        expect(@provider).not_to receive(:update)\n        @catalog.apply\n      end\n\n      it \"should do nothing if the provider returns :present as the latest version\" do\n        allow(@provider).to receive(:properties).and_return(:ensure => :present)\n        allow(@provider).to receive(:latest).and_return(\"1.0\")\n        expect(@provider).not_to receive(:update)\n        @catalog.apply\n      end\n    end\n\n    describe Puppet::Type.type(:package), \"when it should be a specific version\" do\n      include PackageEvaluationTesting\n\n      before { @package[:ensure] = \"1.0\" }\n\n      [:purged, :absent].each do |state|\n        it \"should install if it is #{state.to_s}\" do\n          allow(@provider).to receive(:properties).and_return(:ensure => state)\n          expect(@package.property(:ensure).insync?(state)).to be_falsey\n          expect(@provider).to receive(:install)\n          @catalog.apply\n        end\n      end\n\n      it \"should do nothing if the current version is equal to the desired version\" do\n        allow(@provider).to receive(:properties).and_return(:ensure => \"1.0\")\n        expect(@package.property(:ensure).insync?('1.0')).to be_truthy\n        expect(@provider).not_to receive(:install)\n        @catalog.apply\n      end\n\n      it \"should install if the current version is not equal to the specified version\" do\n        allow(@provider).to receive(:properties).and_return(:ensure => \"2.0\")\n        expect(@package.property(:ensure).insync?('2.0')).to be_falsey\n        expect(@provider).to receive(:install)\n        @catalog.apply\n      end\n\n      describe \"when current value is an array\" do\n        let(:installed_versions) { [\"1.0\", \"2.0\", \"3.0\"] }\n\n        before (:each) do\n          allow(@provider).to receive(:properties).and_return(:ensure => installed_versions)\n        end\n\n        it \"should install if value not in the array\" do\n          @package[:ensure] = \"1.5\"\n          expect(@package.property(:ensure).insync?(installed_versions)).to be_falsey\n          expect(@provider).to receive(:install)\n          @catalog.apply\n        end\n\n        it \"should not install if value is in the array\" do\n          @package[:ensure] = \"2.0\"\n          expect(@package.property(:ensure).insync?(installed_versions)).to be_truthy\n          expect(@provider).not_to receive(:install)\n          @catalog.apply\n        end\n\n        describe \"when ensure is set to 'latest'\" do\n          it \"should not install if the value is in the array\" do\n            expect(@provider).to receive(:latest).and_return(\"3.0\")\n            @package[:ensure] = \"latest\"\n            expect(@package.property(:ensure).insync?(installed_versions)).to be_truthy\n            expect(@provider).not_to receive(:install)\n            @catalog.apply\n          end\n        end\n      end\n    end\n\n    describe Puppet::Type.type(:package), \"when responding to refresh\" do\n      include PackageEvaluationTesting\n\n      it \"should support :true as a value to :reinstall_on_refresh\" do\n        srv = Puppet::Type.type(:package).new(:name => \"yay\", :reinstall_on_refresh => :true)\n        expect(srv[:reinstall_on_refresh]).to eq(:true)\n      end\n\n      it \"should support :false as a value to :reinstall_on_refresh\" do\n        srv = Puppet::Type.type(:package).new(:name => \"yay\", :reinstall_on_refresh => :false)\n        expect(srv[:reinstall_on_refresh]).to eq(:false)\n      end\n\n      it \"should specify :false as the default value of :reinstall_on_refresh\" do\n        srv = Puppet::Type.type(:package).new(:name => \"yay\")\n        expect(srv[:reinstall_on_refresh]).to eq(:false)\n      end\n\n      [:latest, :present, :installed].each do |state|\n        it \"should reinstall if it should be #{state.to_s} and reinstall_on_refresh is true\" do\n          @package[:ensure] = state\n          @package[:reinstall_on_refresh] = :true\n          allow(@provider).to receive(:reinstallable?).and_return(true)\n          expect(@provider).to receive(:reinstall).once\n          @package.refresh\n        end\n\n        it \"should reinstall if it should be #{state.to_s} and reinstall_on_refresh is false\" do\n          @package[:ensure] = state\n          @package[:reinstall_on_refresh] = :false\n          allow(@provider).to receive(:reinstallable?).and_return(true)\n          expect(@provider).not_to receive(:reinstall)\n          @package.refresh\n        end\n      end\n\n      [:purged, :absent].each do |state|\n        it \"should not reinstall if it should be #{state.to_s} and reinstall_on_refresh is true\" do\n          @package[:ensure] = state\n          allow(@provider).to receive(:reinstallable?).and_return(true)\n          expect(@provider).not_to receive(:reinstall)\n          @package.refresh\n        end\n\n        it \"should not reinstall if it should be #{state.to_s} and reinstall_on_refresh is false\" do\n          @package[:ensure] = state\n          allow(@provider).to receive(:reinstallable?).and_return(true)\n          expect(@provider).not_to receive(:reinstall)\n          @package.refresh\n        end\n      end\n    end\n  end\n\n  it \"should select dnf over yum for dnf supported fedora versions\" do\n    dnf = Puppet::Type.type(:package).provider(:dnf)\n    yum = Puppet::Type.type(:package).provider(:yum)\n    allow(Facter).to receive(:value).with('os.family').and_return(:redhat)\n    allow(Facter).to receive(:value).with('os.name').and_return(:fedora)\n    allow(Facter).to receive(:value).with('os.release.major').and_return(\"22\")\n\n    expect(dnf.specificity).to be > yum.specificity\n  end\n\n  describe \"allow_virtual\" do\n    it \"defaults to true on platforms that support virtual packages\" do\n      pkg = Puppet::Type.type(:package).new(:name => 'yay', :provider => :yum)\n      expect(pkg[:allow_virtual]).to eq true\n    end\n\n    it \"defaults to false on dpkg provider\" do\n      pkg = Puppet::Type.type(:package).new(:name => 'yay', :provider => :dpkg)\n      expect(pkg[:allow_virtual]).to be_nil\n    end\n\n    it \"defaults to false on platforms that do not support virtual packages\" do\n      pkg = Puppet::Type.type(:package).new(:name => 'yay', :provider => :apple)\n      expect(pkg[:allow_virtual]).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/resources_spec.rb",
    "content": "require 'spec_helper'\n\n# A type and provider that can be purged\nPuppet::Type.newtype(:purgeable_test) do\n  ensurable\n  newparam(:name) {}\nend\nPuppet::Type.type(:purgeable_test).provide(:purgeable_test) do\n  def self.instances\n    []\n  end\nend\n\nresources = Puppet::Type.type(:resources)\n\n# There are still plenty of tests to port over from test/.\ndescribe resources do\n  before :each do\n    described_class.reset_system_users_max_uid!\n  end\n\n  context \"when initializing\" do\n    it \"should fail if the specified resource type does not exist\" do\n      allow(Puppet::Type).to receive(:type) do\n        expect(x.to_s.downcase).to eq(\"resources\")\n      end.and_return(resources)\n      expect(Puppet::Type).to receive(:type).with(\"nosuchtype\").and_return(nil)\n      expect { resources.new :name => \"nosuchtype\" }.to raise_error(Puppet::Error)\n    end\n\n    it \"should not fail when the specified resource type exists\" do\n      expect { resources.new :name => \"file\" }.not_to raise_error\n    end\n\n    it \"should set its :resource_type attribute\" do\n      expect(resources.new(:name => \"file\").resource_type).to eq(Puppet::Type.type(:file))\n    end\n  end\n\n  context \"purge\" do\n    let (:instance) { described_class.new(:name => 'file') }\n\n    it \"defaults to false\" do\n      expect(instance[:purge]).to be_falsey\n    end\n\n    it \"can be set to false\" do\n      instance[:purge] = 'false'\n    end\n\n    it \"cannot be set to true for a resource type that does not accept ensure\" do\n      allow(instance.resource_type).to receive(:validproperty?).with(:ensure).and_return(false)\n      expect { instance[:purge] = 'yes' }.to raise_error Puppet::Error, /Purging is only supported on types that accept 'ensure'/\n    end\n\n    it \"cannot be set to true for a resource type that does not have instances\" do\n      allow(instance.resource_type).to receive(:respond_to?).with(:instances).and_return(false)\n      expect { instance[:purge] = 'yes' }.to raise_error Puppet::Error, /Purging resources of type file is not supported/\n    end\n\n    it \"can be set to true for a resource type that has instances and can accept ensure\" do\n      allow(instance.resource_type).to receive(:validproperty?).and_return(true)\n      expect { instance[:purge] = 'yes' }.to_not raise_error\n    end\n  end\n\n  context \"#check_user purge behaviour\" do\n    context \"with unless_system_user => true\" do\n      before do\n        @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true\n        @res.catalog = Puppet::Resource::Catalog.new\n        allow(Puppet::FileSystem).to receive(:exist?).with('/etc/login.defs').and_return(false)\n      end\n\n      it \"should never purge hardcoded system users\" do\n        %w{root nobody bin noaccess daemon sys}.each do |sys_user|\n          expect(@res.user_check(Puppet::Type.type(:user).new(:name => sys_user))).to be_falsey\n        end\n      end\n\n      it \"should not purge system users if unless_system_user => true\" do\n        user_hash = {:name => 'system_user', :uid => 125, :system => true}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(@res.user_check(user)).to be_falsey\n      end\n\n      it \"should purge non-system users if unless_system_user => true\" do\n        user_hash = {:name => 'system_user', :uid => described_class.system_users_max_uid + 1, :system => true}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(@res.user_check(user)).to be_truthy\n      end\n\n      it \"should not purge system users under 600 if unless_system_user => 600\" do\n        res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => 600\n        res.catalog = Puppet::Resource::Catalog.new\n        user_hash = {:name => 'system_user', :uid => 500, :system => true}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(res.user_check(user)).to be_falsey\n      end\n\n      it \"should not purge Windows system users\" do\n        res = Puppet::Type.type(:resources).new :name => :user, :purge => true\n        res.catalog = Puppet::Resource::Catalog.new\n        user_hash = {:name => 'Administrator', :uid => 'S-1-5-21-12345-500'}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(res.user_check(user)).to be_falsey\n      end\n\n      it \"should not purge Windows system users\" do\n        res = Puppet::Type.type(:resources).new :name => :user, :purge => true\n        res.catalog = Puppet::Resource::Catalog.new\n        user_hash = {:name => 'other', :uid => 'S-1-5-21-12345-1001'}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(res.user_check(user)).to be_truthy\n      end\n    end\n\n    %w(FreeBSD OpenBSD).each do |os|\n      context \"on #{os}\" do\n        before :each do\n          allow(Facter).to receive(:value).with(:kernel).and_return(os)\n          allow(Facter).to receive(:value).with('os.name').and_return(os)\n          allow(Facter).to receive(:value).with('os.family').and_return(os)\n          allow(Puppet::FileSystem).to receive(:exist?).with('/etc/login.defs').and_return(false)\n          @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true\n          @res.catalog = Puppet::Resource::Catalog.new\n        end\n\n        it \"should not purge system users under 1000\" do\n          user_hash = {:name => 'system_user', :uid => 999}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_falsey\n        end\n\n        it \"should purge users over 999\" do\n          user_hash = {:name => 'system_user', :uid => 1000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_truthy\n        end\n      end\n    end\n\n    context 'with login.defs present' do\n      before :each do\n        expect(Puppet::FileSystem).to receive(:exist?).with('/etc/login.defs').and_return(true)\n        expect(Puppet::FileSystem).to receive(:each_line).with('/etc/login.defs').and_yield(' UID_MIN         1234 # UID_MIN comment ')\n        @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true\n        @res.catalog = Puppet::Resource::Catalog.new\n      end\n\n      it 'should not purge a system user' do\n        user_hash = {:name => 'system_user', :uid => 1233}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(@res.user_check(user)).to be_falsey\n      end\n\n      it 'should purge a non-system user' do\n        user_hash = {:name => 'system_user', :uid => 1234}\n        user = Puppet::Type.type(:user).new(user_hash)\n        allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n        expect(@res.user_check(user)).to be_truthy\n      end\n    end\n\n    context \"with unless_uid\" do\n      context \"with a uid array\" do\n        before do\n          @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [15_000, 15_001, 15_002]\n          @res.catalog = Puppet::Resource::Catalog.new\n        end\n\n        it \"should purge uids that are not in a specified array\" do\n          user_hash = {:name => 'special_user', :uid => 25_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_truthy\n        end\n\n        it \"should not purge uids that are in a specified array\" do\n          user_hash = {:name => 'special_user', :uid => 15000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_falsey\n        end\n      end\n\n      context \"with a single integer uid\" do\n        before do\n          @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => 15_000\n          @res.catalog = Puppet::Resource::Catalog.new\n        end\n\n        it \"should purge uids that are not specified\" do\n          user_hash = {:name => 'special_user', :uid => 25_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_truthy\n        end\n\n        it \"should not purge uids that are specified\" do\n          user_hash = {:name => 'special_user', :uid => 15_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_falsey\n        end\n      end\n\n      context \"with a single string uid\" do\n        before do\n          @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => '15000'\n          @res.catalog = Puppet::Resource::Catalog.new\n        end\n\n        it \"should purge uids that are not specified\" do\n          user_hash = {:name => 'special_user', :uid => 25_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_truthy\n        end\n\n        it \"should not purge uids that are specified\" do\n          user_hash = {:name => 'special_user', :uid => 15_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_falsey\n        end\n      end\n\n      context \"with a mixed uid array\" do\n        before do\n          @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => ['15000', 16_666]\n          @res.catalog = Puppet::Resource::Catalog.new\n        end\n\n        it \"should not purge ids in the range\" do\n          user_hash = {:name => 'special_user', :uid => 15_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_falsey\n        end\n\n        it \"should not purge specified ids\" do\n          user_hash = {:name => 'special_user', :uid => 16_666}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_falsey\n        end\n\n        it \"should purge unspecified ids\" do\n          user_hash = {:name => 'special_user', :uid => 17_000}\n          user = Puppet::Type.type(:user).new(user_hash)\n          allow(user).to receive(:retrieve_resource).and_return(Puppet::Resource.new(\"user\", user_hash[:name], :parameters => user_hash))\n          expect(@res.user_check(user)).to be_truthy\n        end\n      end\n    end\n  end\n\n  context \"#generate\" do\n    before do\n      @purgee = Puppet::Type.type(:purgeable_test).new(:name => 'localhost')\n      @catalog = Puppet::Resource::Catalog.new\n    end\n\n    context \"when the catalog contains a purging resource with an alias\" do\n      before do\n        @resource = Puppet::Type.type(:resources).new(:name => \"purgeable_test\", :purge => true)\n        @catalog.add_resource @resource\n        @catalog.alias(@resource, \"purgeable_test_alias\")\n      end\n\n      it \"should not copy the alias metaparameter\" do\n        allow(Puppet::Type.type(:purgeable_test)).to receive(:instances).and_return([@purgee])\n        generated = @resource.generate.first\n        expect(generated[:alias]).to be_nil\n      end\n    end\n\n    context \"when dealing with non-purging resources\" do\n      before do\n        @resources = Puppet::Type.type(:resources).new(:name => 'purgeable_test')\n      end\n\n      it \"should not generate any resource\" do\n        expect(@resources.generate).to be_empty\n      end\n    end\n\n    context \"when the catalog contains a purging resource\" do\n      before do\n        @resources = Puppet::Type.type(:resources).new(:name => 'purgeable_test', :purge => true)\n        @purgeable_resource = Puppet::Type.type(:purgeable_test).new(:name => 'localhost')\n        @catalog.add_resource @resources\n      end\n\n      it \"should not generate a duplicate of that resource\" do\n        allow(Puppet::Type.type(:purgeable_test)).to receive(:instances).and_return([@purgee])\n        @catalog.add_resource @purgee\n        expect(@resources.generate.collect { |r| r.ref }).not_to include(@purgee.ref)\n      end\n\n      it \"should not include the skipped system users\" do\n        res = Puppet::Type.type(:resources).new :name => :user, :purge => true\n        res.catalog = Puppet::Resource::Catalog.new\n\n        root = Puppet::Type.type(:user).new(:name => \"root\")\n        expect(Puppet::Type.type(:user)).to receive(:instances).and_return([root])\n\n        list = res.generate\n\n        names = list.collect { |r| r[:name] }\n        expect(names).not_to be_include(\"root\")\n      end\n\n      context \"when generating a purgeable resource\" do\n        it \"should be included in the generated resources\" do\n          allow(Puppet::Type.type(:purgeable_test)).to receive(:instances).and_return([@purgeable_resource])\n          expect(@resources.generate.collect { |r| r.ref }).to include(@purgeable_resource.ref)\n        end\n\n        context \"when the instance's do not have an ensure property\" do\n          it \"should not be included in the generated resources\" do\n            @no_ensure_resource = Puppet::Type.type(:exec).new(:name => \"#{File.expand_path('/usr/bin/env')} echo\")\n            allow(Puppet::Type.type(:purgeable_test)).to receive(:instances).and_return([@no_ensure_resource])\n            expect(@resources.generate.collect { |r| r.ref }).not_to include(@no_ensure_resource.ref)\n          end\n        end\n\n        context \"when the instance's ensure property does not accept absent\" do\n          it \"should not be included in the generated resources\" do\n            # We have a :confine block that calls execute in our upstart provider, which fails\n            # on jruby. Thus, we stub it out here since we don't care to do any assertions on it.\n            # This is only an issue if you're running these unit tests on a platform where upstart\n            # is a default provider, like Ubuntu trusty.\n            allow(Puppet::Util::Execution).to receive(:execute)\n\n            @no_absent_resource = Puppet::Type.type(:service).new(:name => 'foobar')\n            allow(Puppet::Type.type(:purgeable_test)).to receive(:instances).and_return([@no_absent_resource])\n            expect(@resources.generate.collect { |r| r.ref }).not_to include(@no_absent_resource.ref)\n          end\n        end\n\n        context \"when checking the instance fails\" do\n          it \"should not be included in the generated resources\" do\n            @purgeable_resource = Puppet::Type.type(:purgeable_test).new(:name => 'foobar')\n            allow(Puppet::Type.type(:purgeable_test)).to receive(:instances).and_return([@purgeable_resource])\n            expect(@resources).to receive(:check).with(@purgeable_resource).and_return(false)\n            expect(@resources.generate.collect { |r| r.ref }).not_to include(@purgeable_resource.ref)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/schedule_spec.rb",
    "content": "require 'spec_helper'\n\nmodule ScheduleTesting\n  def diff(unit, incr, method, count)\n    diff = Time.now.to_i.send(method, incr * count)\n    Time.at(diff)\n  end\n\n  def day(method, count)\n    diff(:hour, 3600 * 24, method, count)\n  end\n\n  def hour(method, count)\n    diff(:hour, 3600, method, count)\n  end\n\n  def min(method, count)\n    diff(:min, 60, method, count)\n  end\nend\n\ndescribe Puppet::Type.type(:schedule) do\n  include ScheduleTesting\n\n  before :each do\n    Puppet[:ignoreschedules] = false\n\n    @schedule = Puppet::Type.type(:schedule).new(:name => \"testing\")\n  end\n\n  describe Puppet::Type.type(:schedule) do\n    it \"should apply to device\" do\n      expect(@schedule).to be_appliable_to_device\n    end\n\n    it \"should apply to host\" do\n      expect(@schedule).to be_appliable_to_host\n    end\n\n    it \"should default to :distance for period-matching\" do\n      expect(@schedule[:periodmatch]).to eq(:distance)\n    end\n\n    it \"should default to a :repeat of 1\" do\n      expect(@schedule[:repeat]).to eq(1)\n    end\n\n    it \"should never match when the period is :never\" do\n      @schedule[:period] = :never\n      expect(@schedule).to_not be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when producing default schedules\" do\n    %w{hourly daily weekly monthly never}.each do |period|\n      period = period.to_sym\n      it \"should produce a #{period} schedule with the period set appropriately\" do\n        schedules = Puppet::Type.type(:schedule).mkdefaultschedules\n        expect(schedules.find { |s| s[:name] == period.to_s and s[:period] == period }).to be_instance_of(Puppet::Type.type(:schedule))\n      end\n    end\n\n    it \"should not produce default schedules when default_schedules is false\"  do\n      Puppet[:default_schedules] = false\n      schedules = Puppet::Type.type(:schedule).mkdefaultschedules\n      expect(schedules).to be_empty\n    end\n\n    it \"should produce a schedule named puppet with a period of hourly and a repeat of 2\" do\n      schedules = Puppet::Type.type(:schedule).mkdefaultschedules\n      expect(schedules.find { |s|\n        s[:name] == \"puppet\" and s[:period] == :hourly and s[:repeat] == 2\n      }).to be_instance_of(Puppet::Type.type(:schedule))\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges\" do\n    before do\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should match when the start time is before the current time and the end time is after the current time\" do\n      @schedule[:range] = \"10:59:50 - 11:00:10\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the start time is after the current time\" do\n      @schedule[:range] = \"11:00:05 - 11:00:10\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when the end time is previous to the current time\" do\n      @schedule[:range] = \"10:59:50 - 10:59:55\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match the current time fails between an array of ranges\" do\n      @schedule[:range] = [\"4-6\", \"20-23\"]\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should match the lower array of ranges\" do\n      @schedule[:range] = [\"9-11\", \"14-16\"]\n      expect(@schedule).to be_match\n    end\n\n    it \"should match the upper array of ranges\" do\n      @schedule[:range] = [\"11:30 - 6\", \"11-12\"]\n      expect(@schedule).to be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges with abbreviated time specifications\" do\n    before do\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 45, 59))\n    end\n\n    it \"should match when just an hour is specified\" do\n      @schedule[:range] = \"11-12\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the ending hour is the current hour\" do\n      @schedule[:range] = \"10-11\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when the ending minute is the current minute\" do\n      @schedule[:range] = \"10:00 - 11:45\"\n      expect(@schedule).to_not be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges with abbreviated time specifications, edge cases part 1\" do\n    before do\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 00, 00))\n    end\n\n    it \"should match when the current time is the start of the range using hours\" do\n      @schedule[:range] = \"11 - 12\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should match when the current time is the end of the range using hours\" do\n      @schedule[:range] = \"10 - 11\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should match when the current time is the start of the range using hours and minutes\" do\n      @schedule[:range] = \"11:00 - 12:00\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should match when the current time is the end of the range using hours and minutes\" do\n      @schedule[:range] = \"10:00 - 11:00\"\n      expect(@schedule).to be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges with abbreviated time specifications, edge cases part 2\" do\n    before do\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 00, 01))\n    end\n\n    it \"should match when the current time is just past the start of the range using hours\" do\n      @schedule[:range] = \"11 - 12\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the current time is just past the end of the range using hours\" do\n      @schedule[:range] = \"10 - 11\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should match when the current time is just past the start of the range using hours and minutes\" do\n      @schedule[:range] = \"11:00 - 12:00\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the current time is just past the end of the range using hours and minutes\" do\n      @schedule[:range] = \"10:00 - 11:00\"\n      expect(@schedule).to_not be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges with abbreviated time specifications, edge cases part 3\" do\n    before do\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 10, 59, 59))\n    end\n\n    it \"should not match when the current time is just before the start of the range using hours\" do\n      @schedule[:range] = \"11 - 12\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should match when the current time is just before the end of the range using hours\" do\n      @schedule[:range] = \"10 - 11\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the current time is just before the start of the range using hours and minutes\" do\n      @schedule[:range] = \"11:00 - 12:00\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should match when the current time is just before the end of the range using hours and minutes\" do\n      @schedule[:range] = \"10:00 - 11:00\"\n      expect(@schedule).to be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges spanning days, day 1\" do\n    before do\n      # Test with the current time at a month's end boundary to ensure we are\n      # advancing the day properly when we push the ending limit out a day.\n      # For example, adding 1 to 31 would throw an error instead of advancing\n      # the date.\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"mar\", 31, 22, 30, 0))\n    end\n\n    it \"should match when the start time is before current time and the end time is the following day\" do\n      @schedule[:range] = \"22:00:00 - 02:00:00\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the current time is outside the range\" do\n      @schedule[:range] = \"23:30:00 - 21:00:00\"\n      expect(@schedule).to_not be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching ranges spanning days, day 2\" do\n    before do\n      # Test with the current time at a month's end boundary to ensure we are\n      # advancing the day properly when we push the ending limit out a day.\n      # For example, adding 1 to 31 would throw an error instead of advancing\n      # the date.\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"mar\", 31, 1, 30, 0))\n    end\n\n    it \"should match when the start time is the day before the current time and the end time is after the current time\" do\n      @schedule[:range] = \"22:00:00 - 02:00:00\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the start time is after the current time\" do\n      @schedule[:range] = \"02:00:00 - 00:30:00\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when the end time is before the current time\" do\n      @schedule[:range] = \"22:00:00 - 01:00:00\"\n      expect(@schedule).to_not be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching hourly by distance\" do\n    before do\n      @schedule[:period] = :hourly\n      @schedule[:periodmatch] = :distance\n\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should match when the previous time was an hour ago\" do\n      expect(@schedule).to be_match(hour(\"-\", 1))\n    end\n\n    it \"should not match when the previous time was now\" do\n      expect(@schedule).to_not be_match(Time.now)\n    end\n\n    it \"should not match when the previous time was 59 minutes ago\" do\n      expect(@schedule).to_not be_match(min(\"-\", 59))\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching daily by distance\" do\n    before do\n      @schedule[:period] = :daily\n      @schedule[:periodmatch] = :distance\n\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should match when the previous time was one day ago\" do\n      expect(@schedule).to be_match(day(\"-\", 1))\n    end\n\n    it \"should not match when the previous time is now\" do\n      expect(@schedule).to_not be_match(Time.now)\n    end\n\n    it \"should not match when the previous time was 23 hours ago\" do\n      expect(@schedule).to_not be_match(hour(\"-\", 23))\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching weekly by distance\" do\n    before do\n      @schedule[:period] = :weekly\n      @schedule[:periodmatch] = :distance\n\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should match when the previous time was seven days ago\" do\n      expect(@schedule).to be_match(day(\"-\", 7))\n    end\n\n    it \"should not match when the previous time was now\" do\n      expect(@schedule).to_not be_match(Time.now)\n    end\n\n    it \"should not match when the previous time was six days ago\" do\n      expect(@schedule).to_not be_match(day(\"-\", 6))\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching monthly by distance\" do\n    before do\n      @schedule[:period] = :monthly\n      @schedule[:periodmatch] = :distance\n\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should match when the previous time was 32 days ago\" do\n      expect(@schedule).to be_match(day(\"-\", 32))\n    end\n\n    it \"should not match when the previous time was now\" do\n      expect(@schedule).to_not be_match(Time.now)\n    end\n\n    it \"should not match when the previous time was 27 days ago\" do\n      expect(@schedule).to_not be_match(day(\"-\", 27))\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching hourly by number\" do\n    before do\n      @schedule[:period] = :hourly\n      @schedule[:periodmatch] = :number\n    end\n\n    it \"should match if the times are one minute apart and the current minute is 0\" do\n      current = Time.utc(2008, 1, 1, 0, 0, 0)\n      previous = Time.utc(2007, 12, 31, 23, 59, 0)\n\n      allow(Time).to receive(:now).and_return(current)\n      expect(@schedule).to be_match(previous)\n    end\n\n    it \"should not match if the times are 59 minutes apart and the current minute is 59\" do\n      current = Time.utc(2009, 2, 1, 12, 59, 0)\n      previous = Time.utc(2009, 2, 1, 12, 0, 0)\n\n      allow(Time).to receive(:now).and_return(current)\n      expect(@schedule).to_not be_match(previous)\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching daily by number\" do\n    before do\n      @schedule[:period] = :daily\n      @schedule[:periodmatch] = :number\n    end\n\n    it \"should match if the times are one minute apart and the current minute and hour are 0\" do\n      current = Time.utc(2010, \"nov\", 7, 0, 0, 0)\n\n      # Now set the previous time to one minute before that\n      previous = current - 60\n\n      allow(Time).to receive(:now).and_return(current)\n      expect(@schedule).to be_match(previous)\n    end\n\n    it \"should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59\" do\n\n      # Reset the previous time to 00:00:00\n      previous = Time.utc(2010, \"nov\", 7, 0, 0, 0)\n\n      # Set the current time to 23:59\n      now = previous + (23 * 3600) + (59 * 60)\n\n      allow(Time).to receive(:now).and_return(now)\n      expect(@schedule).to_not be_match(previous)\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching weekly by number\" do\n    before do\n      @schedule[:period] = :weekly\n      @schedule[:periodmatch] = :number\n    end\n\n    it \"should match if the previous time is prior to the most recent Sunday\" do\n      now = Time.utc(2010, \"nov\", 11, 0, 0, 0) # Thursday\n      allow(Time).to receive(:now).and_return(now)\n      previous = Time.utc(2010, \"nov\", 6, 23, 59, 59) # Sat\n\n      expect(@schedule).to be_match(previous)\n    end\n\n    it \"should not match if the previous time is after the most recent Saturday\" do\n      now = Time.utc(2010, \"nov\", 11, 0, 0, 0) # Thursday\n      allow(Time).to receive(:now).and_return(now)\n      previous = Time.utc(2010, \"nov\", 7, 0, 0, 0) # Sunday\n\n      expect(@schedule).to_not be_match(previous)\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching monthly by number\" do\n    before do\n      @schedule[:period] = :monthly\n      @schedule[:periodmatch] = :number\n    end\n\n    it \"should match when the previous time is prior to the first day of this month\" do\n      now = Time.utc(2010, \"nov\", 8, 00, 59, 59)\n      allow(Time).to receive(:now).and_return(now)\n      previous = Time.utc(2010, \"oct\", 31, 23, 59, 59)\n\n      expect(@schedule).to be_match(previous)\n    end\n\n    it \"should not match when the previous time is after the last day of last month\" do\n      now = Time.utc(2010, \"nov\", 8, 00, 59, 59)\n      allow(Time).to receive(:now).and_return(now)\n      previous = Time.utc(2010, \"nov\", 1, 0, 0, 0)\n\n      expect(@schedule).to_not be_match(previous)\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching with a repeat greater than one\" do\n    before do\n      @schedule[:period] = :daily\n      @schedule[:repeat] = 2\n\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should fail if the periodmatch is 'number'\" do\n      @schedule[:periodmatch] = :number\n      expect {\n        @schedule[:repeat] = 2\n      }.to raise_error(Puppet::Error)\n    end\n\n    it \"should match if the previous run was further away than the distance divided by the repeat\" do\n      previous = Time.now - (3600 * 13)\n      expect(@schedule).to be_match(previous)\n    end\n\n    it \"should not match if the previous run was closer than the distance divided by the repeat\" do\n      previous = Time.now - (3600 * 11)\n      expect(@schedule).to_not be_match(previous)\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching days of the week\" do\n    before do\n      # 2011-05-23 is a Monday\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"may\", 23, 11, 0, 0))\n    end\n\n    it \"should raise an error if the weekday is 'Someday'\" do\n      expect { @schedule[:weekday] = \"Someday\" }.to raise_error(Puppet::Error)\n    end\n\n    it \"should raise an error if the weekday is '7'\" do\n      expect { @schedule[:weekday] = \"7\" }.to raise_error(Puppet::Error)\n    end\n\n    it \"should accept all full weekday names as valid values\" do\n      expect { @schedule[:weekday] = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',\n          'Thursday', 'Friday', 'Saturday'] }.not_to raise_error\n    end\n\n    it \"should accept all short weekday names as valid values\" do\n      expect { @schedule[:weekday] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu',\n          'Fri', 'Sat'] }.not_to raise_error\n    end\n\n    it \"should accept all integers 0-6 as valid values\" do\n      expect {@schedule[:weekday] = [0, 1, 2, 3, 4,\n      5, 6] }.not_to raise_error\n    end\n\n    it \"should match if the weekday is 'Monday'\" do\n      @schedule[:weekday] = \"Monday\"\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should match if the weekday is 'Mon'\" do\n      @schedule[:weekday] = \"Mon\"\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should match if the weekday is '1'\" do\n      @schedule[:weekday] = \"1\"\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should match if weekday is 1\" do\n      @schedule[:weekday] = 1\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match if the weekday is Tuesday\" do\n      @schedule[:weekday] = \"Tuesday\"\n      expect(@schedule).not_to be_match\n    end\n\n    it \"should match if weekday is ['Sun', 'Mon']\" do\n      @schedule[:weekday] = [\"Sun\", \"Mon\"]\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should not match if weekday is ['Sun', 'Tue']\" do\n      @schedule[:weekday] = [\"Sun\", \"Tue\"]\n      expect(@schedule).not_to be_match\n    end\n\n    it \"should match if the weekday is 'Monday'\" do\n      @schedule[:weekday] = \"Monday\"\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should match if the weekday is 'Mon'\" do\n      @schedule[:weekday] = \"Mon\"\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should match if the weekday is '1'\" do\n      @schedule[:weekday] = \"1\"\n      expect(@schedule.match?).to be_truthy\n    end\n\n    it \"should not match if the weekday is Tuesday\" do\n      @schedule[:weekday] = \"Tuesday\"\n      expect(@schedule).not_to be_match\n    end\n\n    it \"should match if weekday is ['Sun', 'Mon']\" do\n      @schedule[:weekday] = [\"Sun\", \"Mon\"]\n      expect(@schedule.match?).to be_truthy\n    end\n  end\n\n  it \"should raise an error if the weekday is an int higher than 6\" do\n    expect { @schedule[:weekday] = 7 }.to raise_error(Puppet::ResourceError, /7 is not a valid day of the week/)\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching days of week and ranges spanning days, day 1\" do\n    before do\n      # Test with ranges and days-of-week both set. 2011-03-31 was a Thursday.\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"mar\", 31, 22, 30, 0))\n    end\n\n    it \"should match when the range and day of week matches\" do\n      @schedule[:range] = \"22:00:00 - 02:00:00\"\n      @schedule[:weekday] = \"Thursday\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the range doesn't match even if the day-of-week matches\" do\n      @schedule[:range] = \"23:30:00 - 21:00:00\"\n      @schedule[:weekday] = \"Thursday\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when day-of-week doesn't match even if the range matches (1 day later)\" do\n      @schedule[:range] = \"22:00:00 - 01:00:00\"\n      @schedule[:weekday] = \"Friday\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when day-of-week doesn't match even if the range matches (1 day earlier)\" do\n      @schedule[:range] = \"22:00:00 - 01:00:00\"\n      @schedule[:weekday] = \"Wednesday\"\n      expect(@schedule).to_not be_match\n    end\n  end\n\n  describe Puppet::Type.type(:schedule), \"when matching days of week and ranges spanning days, day 2\" do\n    before do\n      # 2011-03-31 was a Thursday. As the end-time of a day spanning match, that means\n      # we need to match on Wednesday.\n      allow(Time).to receive(:now).and_return(Time.local(2011, \"mar\", 31, 1, 30, 0))\n    end\n\n    it \"should match when the range matches and the day of week should match\" do\n      @schedule[:range] = \"22:00:00 - 02:00:00\"\n      @schedule[:weekday] = \"Wednesday\"\n      expect(@schedule).to be_match\n    end\n\n    it \"should not match when the range does not match and the day of week should match\" do\n      @schedule[:range] = \"22:00:00 - 01:00:00\"\n      @schedule[:weekday] = \"Thursday\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when the range matches but the day-of-week does not (1 day later)\" do\n      @schedule[:range] = \"22:00:00 - 02:00:00\"\n      @schedule[:weekday] = \"Thursday\"\n      expect(@schedule).to_not be_match\n    end\n\n    it \"should not match when the range matches but the day-of-week does not (1 day later)\" do\n      @schedule[:range] = \"22:00:00 - 02:00:00\"\n      @schedule[:weekday] = \"Tuesday\"\n      expect(@schedule).to_not be_match\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/service_spec.rb",
    "content": "require 'spec_helper'\n\ndef safely_load_service_type\n  before(:each) do\n    # We have a :confine block that calls execute in our upstart provider, which fails\n    # on jruby. Thus, we stub it out here since we don't care to do any assertions on it.\n    # This is only an issue if you're running these unit tests on a platform where upstart\n    # is a default provider, like Ubuntu trusty.\n    allow(Puppet::Util::Execution).to receive(:execute)\n    Puppet::Type.type(:service)\n  end\nend\n\ntest_title = 'Puppet::Type::Service'\n\ndescribe test_title do\n  safely_load_service_type\n\n  it \"should have an :enableable feature that requires the :enable, :disable, and :enabled? methods\" do\n    expect(Puppet::Type.type(:service).provider_feature(:enableable).methods).to eq([:disable, :enable, :enabled?])\n  end\n\n  it \"should have a :refreshable feature that requires the :restart method\" do\n    expect(Puppet::Type.type(:service).provider_feature(:refreshable).methods).to eq([:restart])\n  end\nend\n\ndescribe test_title, \"when validating attributes\" do\n  safely_load_service_type\n\n  [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control, :timeout].each do |param|\n    it \"should have a #{param} parameter\" do\n      expect(Puppet::Type.type(:service).attrtype(param)).to eq(:param)\n    end\n  end\n\n  [:ensure, :enable].each do |param|\n    it \"should have an #{param} property\" do\n      expect(Puppet::Type.type(:service).attrtype(param)).to eq(:property)\n    end\n  end\nend\n\ndescribe test_title, \"when validating attribute values\" do\n  safely_load_service_type\n\n  before do\n    @provider = double('provider', :class => Puppet::Type.type(:service).defaultprovider, :clear => nil, :controllable? => false)\n    allow(Puppet::Type.type(:service).defaultprovider).to receive(:new).and_return(@provider)\n  end\n\n  it \"should support :running as a value to :ensure\" do\n    Puppet::Type.type(:service).new(:name => \"yay\", :ensure => :running)\n  end\n\n  it \"should support :stopped as a value to :ensure\" do\n    Puppet::Type.type(:service).new(:name => \"yay\", :ensure => :stopped)\n  end\n\n  it \"should alias the value :true to :running in :ensure\" do\n    svc = Puppet::Type.type(:service).new(:name => \"yay\", :ensure => true)\n    expect(svc.should(:ensure)).to eq(:running)\n  end\n\n  it \"should alias the value :false to :stopped in :ensure\" do\n    svc = Puppet::Type.type(:service).new(:name => \"yay\", :ensure => false)\n    expect(svc.should(:ensure)).to eq(:stopped)\n  end\n\n  describe \"the enable property\" do\n    before :each do\n      allow(@provider.class).to receive(:supports_parameter?).and_return(true)\n    end\n\n    describe \"for value without required features\" do\n      before :each do\n        allow(@provider).to receive(:satisfies?)\n      end\n\n      it \"should not support :mask as a value\" do\n        expect { Puppet::Type.type(:service).new(:name => \"yay\", :enable => :mask) }.to raise_error(\n          Puppet::ResourceError,\n          /Provider .+ must have features 'maskable' to set 'enable' to 'mask'/\n        )\n      end\n\n      it \"should not support :manual as a value\" do\n        expect { Puppet::Type.type(:service).new(:name => \"yay\", :enable => :manual) }.to raise_error(\n          Puppet::ResourceError,\n          /Provider .+ must have features 'manual_startable' to set 'enable' to 'manual'/\n        )\n      end\n\n      it \"should not support :mask as a value\" do\n        expect { Puppet::Type.type(:service).new(:name => \"yay\", :enable => :delayed) }.to raise_error(\n          Puppet::ResourceError,\n          /Provider .+ must have features 'delayed_startable' to set 'enable' to 'delayed'/\n        )\n      end\n    end\n\n    describe \"for value with required features\" do\n      before :each do\n        allow(@provider).to receive(:satisfies?).and_return(:true)\n      end\n\n      it \"should support :true as a value\" do\n        srv = Puppet::Type.type(:service).new(:name => \"yay\", :enable => :true)\n        expect(srv.should(:enable)).to eq(:true)\n      end\n\n      it \"should support :false as a value\" do\n        srv = Puppet::Type.type(:service).new(:name => \"yay\", :enable => :false)\n        expect(srv.should(:enable)).to eq(:false)\n      end\n\n      it \"should support :mask as a value\" do\n        srv = Puppet::Type.type(:service).new(:name => \"yay\", :enable => :mask)\n        expect(srv.should(:enable)).to eq(:mask)\n      end\n\n      it \"should support :manual as a value on Windows\" do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n        srv = Puppet::Type.type(:service).new(:name => \"yay\", :enable => :manual)\n        expect(srv.should(:enable)).to eq(:manual)\n      end\n\n      it \"should support :delayed as a value on Windows\" do\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n\n        srv = Puppet::Type.type(:service).new(:name => \"yay\", :enable => :delayed)\n        expect(srv.should(:enable)).to eq(:delayed)\n      end\n    end\n  end\n\n  describe \"the timeout parameter\" do\n    before do\n      provider_class_with_timeout = Puppet::Type.type(:service).provide(:simple) do\n        has_features :configurable_timeout\n      end\n      allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class_with_timeout)\n    end\n\n    it \"should fail when timeout is not an integer\" do\n      expect { Puppet::Type.type(:service).new(:name => \"yay\", :timeout => 'foobar') }.to raise_error(Puppet::Error)\n    end\n\n    [-999, -1, 0].each do |int|\n      it \"should not support #{int} as a value to :timeout\" do\n        expect { Puppet::Type.type(:service).new(:name => \"yay\", :timeout => int) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    [1, 30, 999].each do |int|\n      it \"should support #{int} as a value to :timeout\" do\n        srv = Puppet::Type.type(:service).new(:name => \"yay\", :timeout => int)\n        expect(srv[:timeout]).to eq(int)\n      end\n    end\n\n    it \"should default :timeout to 10 when provider has no default value\" do\n      srv = Puppet::Type.type(:service).new(:name => \"yay\")\n      expect(srv[:timeout]).to eq(10)\n    end\n\n    it \"should default :timeout to provider given default time when it has one\" do\n      provider_class_with_timeout = Puppet::Type.type(:service).provide(:simple) do\n        has_features :configurable_timeout\n        def default_timeout\n          30\n        end\n      end\n      allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class_with_timeout)\n\n      srv = Puppet::Type.type(:service).new(:name => \"yay\")\n      expect(srv[:timeout]).to eq(30)\n    end\n\n    it \"should accept string as value\" do\n      srv = Puppet::Type.type(:service).new(:name => \"yay\", :timeout => \"25\")\n      expect(srv[:timeout]).to eq(25)\n    end\n\n    it \"should not support values that cannot be converted to Integer such as Array\" do\n      expect { Puppet::Type.type(:service).new(:name => \"yay\", :timeout => [25]) }.to raise_error(Puppet::Error)\n    end\n  end\n\n  describe \"the service logon credentials\" do \n    before do\n      provider_class_with_logon_credentials = Puppet::Type.type(:service).provide(:simple) do\n        has_features :manages_logon_credentials\n        def logonpassword=(value) end\n        def logonaccount_insync?(current) end\n      end\n      allow(Puppet::Type.type(:service)).to receive(:defaultprovider).and_return(provider_class_with_logon_credentials)\n    end\n\n    describe \"the 'logonaccount' property\" do\n      let(:service) {Puppet::Type.type(:service).new(:name => \"yay\", :logonaccount => 'myUser')}\n\n      it \"should let superclass implementation resolve insyncness when provider does not respond to the 'logonaccount_insync?' method\" do\n        allow(service.provider).to receive(:respond_to?).with(:logonaccount_insync?).and_return(false)\n        expect(service.property(:logonaccount).insync?('myUser')).to eq(true)\n      end\n\n      it \"should let provider resolve insyncness when provider responds to the 'logonaccount_insync?' method\" do\n        allow(service.provider).to receive(:respond_to?).with(:logonaccount_insync?, any_args).and_return(true)\n        allow(service.provider).to receive(:logonaccount_insync?).and_return(false)\n        \n        expect(service.property(:logonaccount).insync?('myUser')).to eq(false)\n      end\n    end\n\n    describe \"the logonpassword parameter\" do\n      it \"should fail when logonaccount is not being managed as well\" do\n        expect { Puppet::Type.type(:service).new(:name => \"yay\", :logonpassword => 'myPass') }.to raise_error(Puppet::Error, /The 'logonaccount' parameter is mandatory when setting 'logonpassword'./)\n      end\n\n      it \"should default to empty string when only logonaccount is being managed\" do\n        service = Puppet::Type.type(:service).new(:name => \"yay\", :logonaccount => 'myUser')\n\n        expect { service }.not_to raise_error\n        expect(service[:logonpassword]).to eq(\"\")\n      end\n\n      it \"should default to nil when not even logonaccount is being managed\" do\n        service = Puppet::Type.type(:service).new(:name => \"yay\")\n        expect(service[:logonpassword]).to eq(nil)\n      end\n\n      it \"should fail when logonpassword includes the ':' character\" do\n        expect { Puppet::Type.type(:service).new(:name => \"yay\", :logonaccount => 'myUser', :logonpassword => 'my:Pass') }.to raise_error(Puppet::Error, /Passwords cannot include ':'/)\n      end\n    end\n  end\n\n  it \"should support :true as a value to :hasstatus\" do\n    srv = Puppet::Type.type(:service).new(:name => \"yay\", :hasstatus => :true)\n    expect(srv[:hasstatus]).to eq(:true)\n  end\n\n  it \"should support :false as a value to :hasstatus\" do\n    srv = Puppet::Type.type(:service).new(:name => \"yay\", :hasstatus => :false)\n    expect(srv[:hasstatus]).to eq(:false)\n  end\n\n  it \"should specify :true as the default value of hasstatus\" do\n    srv = Puppet::Type.type(:service).new(:name => \"yay\")\n    expect(srv[:hasstatus]).to eq(:true)\n  end\n\n  it \"should support :true as a value to :hasrestart\" do\n    srv = Puppet::Type.type(:service).new(:name => \"yay\", :hasrestart => :true)\n    expect(srv[:hasrestart]).to eq(:true)\n  end\n\n  it \"should support :false as a value to :hasrestart\" do\n    srv = Puppet::Type.type(:service).new(:name => \"yay\", :hasrestart => :false)\n    expect(srv[:hasrestart]).to eq(:false)\n  end\n\n  it \"should allow setting the :enable parameter if the provider has the :enableable feature\" do\n    allow(Puppet::Type.type(:service).defaultprovider).to receive(:supports_parameter?).and_return(true)\n    expect(Puppet::Type.type(:service).defaultprovider).to receive(:supports_parameter?).with(Puppet::Type.type(:service).attrclass(:enable)).and_return(true)\n    svc = Puppet::Type.type(:service).new(:name => \"yay\", :enable => true)\n    expect(svc.should(:enable)).to eq(:true)\n  end\n\n  it \"should not allow setting the :enable parameter if the provider is missing the :enableable feature\" do\n    allow(Puppet::Type.type(:service).defaultprovider).to receive(:supports_parameter?).and_return(true)\n    expect(Puppet::Type.type(:service).defaultprovider).to receive(:supports_parameter?).with(Puppet::Type.type(:service).attrclass(:enable)).and_return(false)\n    svc = Puppet::Type.type(:service).new(:name => \"yay\", :enable => true)\n    expect(svc.should(:enable)).to be_nil\n  end\n\n  it \"should split paths on '#{File::PATH_SEPARATOR}'\" do\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    allow(FileTest).to receive(:directory?).and_return(true)\n    svc = Puppet::Type.type(:service).new(:name => \"yay\", :path => \"/one/two#{File::PATH_SEPARATOR}/three/four\")\n    expect(svc[:path]).to eq(%w{/one/two /three/four})\n  end\n\n  it \"should accept arrays of paths joined by '#{File::PATH_SEPARATOR}'\" do\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n    allow(FileTest).to receive(:directory?).and_return(true)\n    svc = Puppet::Type.type(:service).new(:name => \"yay\", :path => [\"/one#{File::PATH_SEPARATOR}/two\", \"/three#{File::PATH_SEPARATOR}/four\"])\n    expect(svc[:path]).to eq(%w{/one /two /three /four})\n  end\nend\n\ndescribe test_title, \"when setting default attribute values\" do\n  safely_load_service_type\n\n  it \"should default to the provider's default path if one is available\" do\n    allow(FileTest).to receive(:directory?).and_return(true)\n    allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n    allow(Puppet::Type.type(:service).defaultprovider).to receive(:respond_to?).and_return(true)\n    allow(Puppet::Type.type(:service).defaultprovider).to receive(:defpath).and_return(\"testing\")\n    svc = Puppet::Type.type(:service).new(:name => \"other\")\n    expect(svc[:path]).to eq([\"testing\"])\n  end\n\n  it \"should default 'pattern' to the binary if one is provided\" do\n    svc = Puppet::Type.type(:service).new(:name => \"other\", :binary => \"/some/binary\")\n    expect(svc[:pattern]).to eq(\"/some/binary\")\n  end\n\n  it \"should default 'pattern' to the name if no pattern is provided\" do\n    svc = Puppet::Type.type(:service).new(:name => \"other\")\n    expect(svc[:pattern]).to eq(\"other\")\n  end\n\n  it \"should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature\" do\n    provider = double('provider', :controllable? => true, :class => Puppet::Type.type(:service).defaultprovider, :clear => nil)\n    allow(Puppet::Type.type(:service).defaultprovider).to receive(:new).and_return(provider)\n    svc = Puppet::Type.type(:service).new(:name => \"nfs.client\")\n    expect(svc[:control]).to eq(\"NFS_CLIENT_START\")\n  end\nend\n\ndescribe test_title, \"when retrieving the host's current state\" do\n  safely_load_service_type\n\n  before do\n    @service = Puppet::Type.type(:service).new(:name => \"yay\")\n  end\n\n  it \"should use the provider's status to determine whether the service is running\" do\n    expect(@service.provider).to receive(:status).and_return(:yepper)\n    @service[:ensure] = :running\n    expect(@service.property(:ensure).retrieve).to eq(:yepper)\n  end\n\n  it \"should ask the provider whether it is enabled\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    expect(@service.provider).to receive(:enabled?).and_return(:yepper)\n    @service[:enable] = true\n    expect(@service.property(:enable).retrieve).to eq(:yepper)\n  end\nend\n\ndescribe test_title, \"when changing the host\" do\n  safely_load_service_type\n\n  before do\n    @service = Puppet::Type.type(:service).new(:name => \"yay\")\n  end\n\n  it \"should start the service if it is supposed to be running\" do\n    @service[:ensure] = :running\n    expect(@service.provider).to receive(:start)\n    @service.property(:ensure).sync\n  end\n\n  it \"should stop the service if it is supposed to be stopped\" do\n    @service[:ensure] = :stopped\n    expect(@service.provider).to receive(:stop)\n    @service.property(:ensure).sync\n  end\n\n  it \"should enable the service if it is supposed to be enabled\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    @service[:enable] = true\n    expect(@service.provider).to receive(:enable)\n    @service.property(:enable).sync\n  end\n\n  it \"should disable the service if it is supposed to be disabled\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    @service[:enable] = false\n    expect(@service.provider).to receive(:disable)\n    @service.property(:enable).sync\n  end\n\n  it \"should let superclass implementation resolve insyncness when provider does not respond to the 'enabled_insync?' method\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    @service[:enable] = true\n    allow(@service.provider).to receive(:respond_to?).with(:enabled_insync?).and_return(false)\n\n    expect(@service.property(:enable).insync?(:true)).to eq(true)\n  end\n\n  it \"insyncness should be resolved by provider instead of superclass implementation when provider responds to the 'enabled_insync?' method\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    @service[:enable] = true\n    allow(@service.provider).to receive(:respond_to?).with(:enabled_insync?, any_args).and_return(true)\n    allow(@service.provider).to receive(:enabled_insync?).and_return(false)\n\n    expect(@service.property(:enable).insync?(:true)).to eq(false)\n  end\n\n  it \"should sync the service's enable state when changing the state of :ensure if :enable is being managed\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    @service[:enable] = false\n    @service[:ensure] = :stopped\n\n    expect(@service.property(:enable)).to receive(:retrieve).and_return(\"whatever\")\n    expect(@service.property(:enable)).to receive(:insync?).and_return(false)\n    expect(@service.property(:enable)).to receive(:sync)\n\n    allow(@service.provider).to receive(:stop)\n\n    @service.property(:ensure).sync\n  end\n\n  it \"should sync the service's logonaccount state when changing the state of :ensure if :logonaccount is being managed\" do\n    allow(@service.provider.class).to receive(:supports_parameter?).and_return(true)\n    allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n\n    @service[:ensure] = :stopped\n    @service[:logonaccount] = 'LocalSystem'\n\n    expect(@service.property(:logonaccount)).to receive(:retrieve).and_return(\"MyUser\")\n    expect(@service.property(:logonaccount)).to receive(:insync?).and_return(false)\n    expect(@service.property(:logonaccount)).to receive(:sync)\n\n    allow(@service.provider).to receive(:stop)\n\n    @service.property(:ensure).sync\n  end\nend\n\ndescribe test_title, \"when refreshing the service\" do\n  safely_load_service_type\n\n  before do\n    @service = Puppet::Type.type(:service).new(:name => \"yay\")\n  end\n\n  it \"should restart the service if it is running\" do\n    @service[:ensure] = :running\n    expect(@service.provider).to receive(:status).and_return(:running)\n    expect(@service.provider).to receive(:restart)\n    @service.refresh\n  end\n\n  it \"should restart the service if it is running, even if it is supposed to stopped\" do\n    @service[:ensure] = :stopped\n    expect(@service.provider).to receive(:status).and_return(:running)\n    expect(@service.provider).to receive(:restart)\n    @service.refresh\n  end\n\n  it \"should not restart the service if it is not running\" do\n    @service[:ensure] = :running\n    expect(@service.provider).to receive(:status).and_return(:stopped)\n    @service.refresh\n  end\n\n  it \"should add :ensure as a property if it is not being managed\" do\n    expect(@service.provider).to receive(:status).and_return(:running)\n    expect(@service.provider).to receive(:restart)\n    @service.refresh\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/stage_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Type.type(:stage) do\n  it \"should have a 'name' parameter'\" do\n    expect(Puppet::Type.type(:stage).new(:name => :foo)[:name]).to eq(:foo)\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/tidy_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/file_bucket/dipper'\n\ntidy = Puppet::Type.type(:tidy)\n\ndescribe tidy do\n  include PuppetSpec::Files\n\n  before do\n    @basepath = make_absolute(\"/what/ever\")\n    allow(Puppet.settings).to receive(:use)\n  end\n\n  context \"when normalizing 'path' on windows\", :if => Puppet::Util::Platform.windows? do\n    it \"replaces backslashes with forward slashes\" do\n      resource = tidy.new(:path => 'c:\\directory')\n      expect(resource[:path]).to eq('c:/directory')\n    end\n  end\n\n  it \"should use :lstat when stating a file\" do\n    path = '/foo/bar'\n    stat = double('stat')\n    expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(stat)\n\n    resource = tidy.new :path => path, :age => \"1d\"\n\n    expect(resource.stat(path)).to eq(stat)\n  end\n\n  [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param|\n    it \"should have a #{param} parameter\" do\n      expect(Puppet::Type.type(:tidy).attrclass(param).ancestors).to be_include(Puppet::Parameter)\n    end\n\n    it \"should have documentation for its #{param} param\" do\n      expect(Puppet::Type.type(:tidy).attrclass(param).doc).to be_instance_of(String)\n    end\n  end\n\n  describe \"when validating parameter values\" do\n    describe \"for 'recurse'\" do\n      before do\n        @tidy = Puppet::Type.type(:tidy).new :path => \"/tmp\", :age => \"100d\"\n      end\n\n      it \"should allow 'true'\" do\n        expect { @tidy[:recurse] = true }.not_to raise_error\n      end\n\n      it \"should allow 'false'\" do\n        expect { @tidy[:recurse] = false }.not_to raise_error\n      end\n\n      it \"should allow integers\" do\n        expect { @tidy[:recurse] = 10 }.not_to raise_error\n      end\n\n      it \"should allow string representations of integers\" do\n        expect { @tidy[:recurse] = \"10\" }.not_to raise_error\n      end\n\n      it \"should allow 'inf'\" do\n        expect { @tidy[:recurse] = \"inf\" }.not_to raise_error\n      end\n\n      it \"should not allow arbitrary values\" do\n        expect { @tidy[:recurse] = \"whatever\" }.to raise_error(Puppet::ResourceError, /Parameter recurse failed/)\n      end\n    end\n\n    describe \"for 'matches'\" do\n      before do\n        @tidy = Puppet::Type.type(:tidy).new :path => \"/tmp\", :age => \"100d\"\n      end\n\n      it \"should object if matches is given with recurse is not specified\" do\n        expect { @tidy[:matches] = '*.doh' }.to raise_error(Puppet::ResourceError, /Parameter matches failed/)\n      end\n      it \"should object if matches is given and recurse is 0\" do\n        expect { @tidy[:recurse] = 0; @tidy[:matches] = '*.doh' }.to raise_error(Puppet::ResourceError, /Parameter matches failed/)\n      end\n      it \"should object if matches is given and recurse is false\" do\n        expect { @tidy[:recurse] = false; @tidy[:matches] = '*.doh' }.to raise_error(Puppet::ResourceError, /Parameter matches failed/)\n      end\n      it \"should not object if matches is given and recurse is > 0\" do\n        expect { @tidy[:recurse] = 1; @tidy[:matches] = '*.doh' }.not_to raise_error\n      end\n      it \"should not object if matches is given and recurse is true\" do\n        expect { @tidy[:recurse] = true; @tidy[:matches] = '*.doh' }.not_to raise_error\n      end\n    end\n  end\n\n  describe \"when matching files by age\" do\n    convertors = {\n      :second => 1,\n      :minute => 60\n    }\n\n    convertors[:hour] = convertors[:minute] * 60\n    convertors[:day] = convertors[:hour] * 24\n    convertors[:week] = convertors[:day] * 7\n\n    convertors.each do |unit, multiple|\n      it \"should consider a #{unit} to be #{multiple} seconds\" do\n        @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :age => \"5#{unit.to_s[0..0]}\"\n\n        expect(@tidy[:age]).to eq(5 * multiple)\n      end\n    end\n  end\n\n  describe \"when matching files by size\" do\n    convertors = {\n      :b => 0,\n      :kb => 1,\n      :mb => 2,\n      :gb => 3,\n      :tb => 4\n    }\n\n    convertors.each do |unit, multiple|\n      it \"should consider a #{unit} to be 1024^#{multiple} bytes\" do\n        @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :size => \"5#{unit}\"\n\n        total = 5\n        multiple.times { total *= 1024 }\n        expect(@tidy[:size]).to eq(total)\n      end\n    end\n  end\n\n  describe \"when tidying\" do\n    before do\n      @tidy = Puppet::Type.type(:tidy).new :path => @basepath\n      @stat = double('stat', :ftype => \"directory\")\n      lstat_is(@basepath, @stat)\n    end\n\n    describe \"and generating files\" do\n      it \"should set the backup on the file if backup is set on the tidy instance\" do\n        @tidy[:backup] = \"whatever\"\n\n        expect(@tidy.mkfile(@basepath)[:backup]).to eq(\"whatever\")\n      end\n\n      it \"should set the file's path to the tidy's path\" do\n        expect(@tidy.mkfile(@basepath)[:path]).to eq(@basepath)\n      end\n\n      it \"should configure the file for deletion\" do\n        expect(@tidy.mkfile(@basepath)[:ensure]).to eq(:absent)\n      end\n\n      it \"should force deletion on the file\" do\n        expect(@tidy.mkfile(@basepath)[:force]).to eq(true)\n      end\n\n      it \"should do nothing if the targeted file does not exist\" do\n        lstat_raises(@basepath, Errno::ENOENT)\n\n        expect(@tidy.generate).to eq([])\n      end\n    end\n\n    describe \"and recursion is not used\" do\n      it \"should generate a file resource if the file should be tidied\" do\n        expect(@tidy).to receive(:tidy?).with(@basepath).and_return(true)\n        file = Puppet::Type.type(:file).new(:path => @basepath+\"/eh\")\n        expect(@tidy).to receive(:mkfile).with(@basepath).and_return(file)\n\n        expect(@tidy.generate).to eq([file])\n      end\n\n      it \"should do nothing if the file should not be tidied\" do\n        expect(@tidy).to receive(:tidy?).with(@basepath).and_return(false)\n        expect(@tidy).not_to receive(:mkfile)\n\n        expect(@tidy.generate).to eq([])\n      end\n    end\n\n    describe \"and recursion is used\" do\n      before do\n        @tidy[:recurse] = true\n        @fileset = Puppet::FileServing::Fileset.new(@basepath)\n        allow(Puppet::FileServing::Fileset).to receive(:new).and_return(@fileset)\n      end\n\n      it \"should use a Fileset with default max_files for infinite recursion\" do\n        expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :max_files=>0}).and_return(@fileset)\n        expect(@fileset).to receive(:files).and_return(%w{. one two})\n        allow(@tidy).to receive(:tidy?).and_return(false)\n\n        @tidy.generate\n      end\n\n      it \"should use a Fileset with default max_files for limited recursion\" do\n        @tidy[:recurse] = 42\n        expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :recurselimit => 42, :max_files=>0}).and_return(@fileset)\n        expect(@fileset).to receive(:files).and_return(%w{. one two})\n        allow(@tidy).to receive(:tidy?).and_return(false)\n\n        @tidy.generate\n      end\n\n      it \"should use a Fileset with max_files for limited recursion\" do\n        @tidy[:recurse] = 42\n        @tidy[:max_files] = 9876\n        expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :recurselimit => 42, :max_files=>9876}).and_return(@fileset)\n        expect(@fileset).to receive(:files).and_return(%w{. one two})\n        allow(@tidy).to receive(:tidy?).and_return(false)\n\n        @tidy.generate\n      end\n\n      it \"should generate a file resource for every file that should be tidied but not for files that should not be tidied\" do\n        expect(@fileset).to receive(:files).and_return(%w{. one two})\n\n        expect(@tidy).to receive(:tidy?).with(@basepath).and_return(true)\n        expect(@tidy).to receive(:tidy?).with(@basepath+\"/one\").and_return(true)\n        expect(@tidy).to receive(:tidy?).with(@basepath+\"/two\").and_return(false)\n\n        file = Puppet::Type.type(:file).new(:path => @basepath+\"/eh\")\n        expect(@tidy).to receive(:mkfile).with(@basepath).and_return(file)\n        expect(@tidy).to receive(:mkfile).with(@basepath+\"/one\").and_return(file)\n\n        @tidy.generate\n      end\n    end\n\n    describe \"and determining whether a file matches provided glob patterns\" do\n      before do\n        @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :recurse => 1\n        @tidy[:matches] = %w{*foo* *bar*}\n\n        @stat = double('stat')\n\n        @matcher = @tidy.parameter(:matches)\n      end\n\n      it \"should always convert the globs to an array\" do\n        @matcher.value = \"*foo*\"\n        expect(@matcher.value).to eq(%w{*foo*})\n      end\n\n      it \"should return true if any pattern matches the last part of the file\" do\n        @matcher.value = %w{*foo* *bar*}\n        expect(@matcher).to be_tidy(\"/file/yaybarness\", @stat)\n      end\n\n      it \"should return false if no pattern matches the last part of the file\" do\n        @matcher.value = %w{*foo* *bar*}\n        expect(@matcher).not_to be_tidy(\"/file/yayness\", @stat)\n      end\n    end\n\n    describe \"and determining whether a file is too old\" do\n      before do\n        @tidy = Puppet::Type.type(:tidy).new :path => @basepath\n        @stat = double('stat')\n\n        @tidy[:age] = \"1s\"\n        @tidy[:type] = \"mtime\"\n        @ager = @tidy.parameter(:age)\n      end\n\n      it \"should use the age type specified\" do\n        @tidy[:type] = :ctime\n        expect(@stat).to receive(:ctime).and_return(Time.now)\n\n        @ager.tidy?(@basepath, @stat)\n      end\n\n      it \"should return true if the specified age is 0\" do\n        @tidy[:age] = \"0\"\n        expect(@stat).to receive(:mtime).and_return(Time.now)\n\n        expect(@ager).to be_tidy(@basepath, @stat)\n      end\n\n      it \"should return false if the file is more recent than the specified age\" do\n        expect(@stat).to receive(:mtime).and_return(Time.now)\n\n        expect(@ager).not_to be_tidy(@basepath, @stat)\n      end\n\n      it \"should return true if the file is older than the specified age\" do\n        expect(@stat).to receive(:mtime).and_return(Time.now - 10)\n\n        expect(@ager).to be_tidy(@basepath, @stat)\n      end\n    end\n\n    describe \"and determining whether a file is too large\" do\n      before do\n        @tidy = Puppet::Type.type(:tidy).new :path => @basepath\n        @stat = double('stat', :ftype => \"file\")\n\n        @tidy[:size] = \"1kb\"\n        @sizer = @tidy.parameter(:size)\n      end\n\n      it \"should return false if the file is smaller than the specified size\" do\n        expect(@stat).to receive(:size).and_return(4) # smaller than a kilobyte\n\n        expect(@sizer).not_to be_tidy(@basepath, @stat)\n      end\n\n      it \"should return true if the file is larger than the specified size\" do\n        expect(@stat).to receive(:size).and_return(1500) # larger than a kilobyte\n\n        expect(@sizer).to be_tidy(@basepath, @stat)\n      end\n\n      it \"should return true if the file is equal to the specified size\" do\n        expect(@stat).to receive(:size).and_return(1024)\n\n        expect(@sizer).to be_tidy(@basepath, @stat)\n      end\n    end\n\n    describe \"and determining whether a file should be tidied\" do\n      before do\n        @tidy = Puppet::Type.type(:tidy).new :path => @basepath\n        @catalog = Puppet::Resource::Catalog.new\n        @tidy.catalog = @catalog\n        @stat = double('stat', :ftype => \"file\")\n        lstat_is(@basepath, @stat)\n      end\n\n      it \"should not try to recurse if the file does not exist\" do\n        @tidy[:recurse] = true\n\n        lstat_is(@basepath, nil)\n\n        expect(@tidy.generate).to eq([])\n      end\n\n      it \"should not be tidied if the file does not exist\" do\n        lstat_raises(@basepath, Errno::ENOENT)\n\n        expect(@tidy).not_to be_tidy(@basepath)\n      end\n\n      it \"should not be tidied if the user has no access to the file\" do\n        lstat_raises(@basepath, Errno::EACCES)\n\n        expect(@tidy).not_to be_tidy(@basepath)\n      end\n\n      it \"should not be tidied if it is a directory and rmdirs is set to false\" do\n        stat = double('stat', :ftype => \"directory\")\n        lstat_is(@basepath, stat)\n\n        expect(@tidy).not_to be_tidy(@basepath)\n      end\n\n      it \"should return false if it does not match any provided globs\" do\n        @tidy[:recurse] = 1\n        @tidy[:matches] = \"globs\"\n\n        matches = @tidy.parameter(:matches)\n        expect(matches).to receive(:tidy?).with(@basepath, @stat).and_return(false)\n        expect(@tidy).not_to be_tidy(@basepath)\n      end\n\n      it \"should return false if it does not match aging requirements\" do\n        @tidy[:age] = \"1d\"\n\n        ager = @tidy.parameter(:age)\n        expect(ager).to receive(:tidy?).with(@basepath, @stat).and_return(false)\n        expect(@tidy).not_to be_tidy(@basepath)\n      end\n\n      it \"should return false if it does not match size requirements\" do\n        @tidy[:size] = \"1b\"\n\n        sizer = @tidy.parameter(:size)\n        expect(sizer).to receive(:tidy?).with(@basepath, @stat).and_return(false)\n        expect(@tidy).not_to be_tidy(@basepath)\n      end\n\n      it \"should tidy a file if age and size are set but only size matches\" do\n        @tidy[:size] = \"1b\"\n        @tidy[:age] = \"1d\"\n\n        allow(@tidy.parameter(:size)).to receive(:tidy?).and_return(true)\n        allow(@tidy.parameter(:age)).to receive(:tidy?).and_return(false)\n        expect(@tidy).to be_tidy(@basepath)\n      end\n\n      it \"should tidy a file if age and size are set but only age matches\" do\n        @tidy[:size] = \"1b\"\n        @tidy[:age] = \"1d\"\n\n        allow(@tidy.parameter(:size)).to receive(:tidy?).and_return(false)\n        allow(@tidy.parameter(:age)).to receive(:tidy?).and_return(true)\n        expect(@tidy).to be_tidy(@basepath)\n      end\n\n      it \"should tidy all files if neither age nor size is set\" do\n        expect(@tidy).to be_tidy(@basepath)\n      end\n\n      it \"should sort the results inversely by path length, so files are added to the catalog before their directories\" do\n        @tidy[:recurse] = true\n        @tidy[:rmdirs] = true\n        fileset = Puppet::FileServing::Fileset.new(@basepath)\n        expect(Puppet::FileServing::Fileset).to receive(:new).and_return(fileset)\n        expect(fileset).to receive(:files).and_return(%w{. one one/two})\n\n        allow(@tidy).to receive(:tidy?).and_return(true)\n\n        expect(@tidy.generate.collect { |r| r[:path] }).to eq([@basepath+\"/one/two\", @basepath+\"/one\", @basepath])\n      end\n    end\n\n    it \"should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first\" do\n      @tidy[:recurse] = true\n      @tidy[:rmdirs] = true\n      fileset = double('fileset')\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :max_files=>0}).and_return(fileset)\n      expect(fileset).to receive(:files).and_return(%w{. one two one/subone two/subtwo one/subone/ssone})\n      allow(@tidy).to receive(:tidy?).and_return(true)\n\n      result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash }\n      {\n        @basepath => [ @basepath+\"/one\", @basepath+\"/two\" ],\n        @basepath+\"/one\" => [@basepath+\"/one/subone\"],\n        @basepath+\"/two\" => [@basepath+\"/two/subtwo\"],\n        @basepath+\"/one/subone\" => [@basepath+\"/one/subone/ssone\"]\n      }.each do |parent, children|\n        children.each do |child|\n          ref = Puppet::Resource.new(:file, child)\n          expect(result[parent][:require].find { |req| req.to_s == ref.to_s }).not_to be_nil\n        end\n      end\n    end\n\n    it \"should configure directories to require their contained files in sorted order\" do\n      @tidy[:recurse] = true\n      @tidy[:rmdirs] = true\n      fileset = double('fileset')\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :max_files=>0}).and_return(fileset)\n      expect(fileset).to receive(:files).and_return(%w{. a a/2 a/1 a/3})\n      allow(@tidy).to receive(:tidy?).and_return(true)\n\n      result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash }\n      expect(result[@basepath + '/a'][:require].collect{|a| a.name[('File//a/' + @basepath).length..-1]}.join()).to eq('321')\n    end\n\n    it \"generates resources whose noop parameter matches the managed resource's noop parameter\" do\n      @tidy[:recurse] = true\n      @tidy[:noop] = true\n\n      fileset = double('fileset')\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :max_files=>0}).and_return(fileset)\n      expect(fileset).to receive(:files).and_return(%w{. a a/2 a/1 a/3})\n      allow(@tidy).to receive(:tidy?).and_return(true)\n\n      result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash }\n\n      expect(result.values).to all(be_noop)\n    end\n\n    it \"generates resources whose schedule parameter matches the managed resource's schedule parameter\" do\n      @tidy[:recurse] = true\n      @tidy[:schedule] = 'fake_schedule'\n\n      fileset = double('fileset')\n      expect(Puppet::FileServing::Fileset).to receive(:new).with(@basepath, {:recurse => true, :max_files=>0}).and_return(fileset)\n      expect(fileset).to receive(:files).and_return(%w{. a a/2 a/1 a/3})\n      allow(@tidy).to receive(:tidy?).and_return(true)\n\n      result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash }\n\n      result.each do |file_resource|\n        expect(file_resource[1][:schedule]).to eq('fake_schedule')\n      end\n\n    end\n  end\n\n  def lstat_is(path, stat)\n    allow(Puppet::FileSystem).to receive(:lstat).with(path).and_return(stat)\n  end\n\n  def lstat_raises(path, error_class)\n    expect(Puppet::FileSystem).to receive(:lstat).with(path).and_raise(Errno::ENOENT)\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/user_spec.rb",
    "content": "# encoding: utf-8\nrequire 'spec_helper'\n\ndescribe Puppet::Type.type(:user) do\n  before :each do\n    @provider_class = described_class.provide(:simple) do\n      has_features :manages_expiry, :manages_password_age, :manages_passwords, :manages_solaris_rbac, :manages_roles, :manages_shell\n      mk_resource_methods\n      def create; end\n      def delete; end\n      def exists?; get(:ensure) != :absent; end\n      def flush; end\n      def self.instances; []; end\n    end\n    allow(described_class).to receive(:defaultprovider).and_return(@provider_class)\n  end\n\n  it \"should be able to create an instance\" do\n    expect(described_class.new(:name => \"foo\")).not_to be_nil\n  end\n\n  it \"should have an allows_duplicates feature\" do\n    expect(described_class.provider_feature(:allows_duplicates)).not_to be_nil\n  end\n\n  it \"should have a manages_homedir feature\" do\n    expect(described_class.provider_feature(:manages_homedir)).not_to be_nil\n  end\n\n  it \"should have a manages_passwords feature\" do\n    expect(described_class.provider_feature(:manages_passwords)).not_to be_nil\n  end\n\n  it \"should have a manages_solaris_rbac feature\" do\n    expect(described_class.provider_feature(:manages_solaris_rbac)).not_to be_nil\n  end\n\n  it \"should have a manages_roles feature\" do\n    expect(described_class.provider_feature(:manages_roles)).not_to be_nil\n  end\n\n  it \"should have a manages_expiry feature\" do\n    expect(described_class.provider_feature(:manages_expiry)).not_to be_nil\n  end\n\n  it \"should have a manages_password_age feature\" do\n    expect(described_class.provider_feature(:manages_password_age)).not_to be_nil\n  end\n\n  it \"should have a system_users feature\" do\n    expect(described_class.provider_feature(:system_users)).not_to be_nil\n  end\n\n  it \"should have a manages_shell feature\" do\n    expect(described_class.provider_feature(:manages_shell)).not_to be_nil\n  end\n\n  context \"managehome\" do\n    let (:provider) { @provider_class.new(:name => 'foo', :ensure => :absent) }\n    let (:instance) { described_class.new(:name => 'foo', :provider => provider) }\n\n    it \"defaults to false\" do\n      expect(instance[:managehome]).to be_falsey\n    end\n\n    it \"can be set to false\" do\n      instance[:managehome] = 'false'\n    end\n\n    it \"cannot be set to true for a provider that does not manage homedirs\" do\n      allow(provider.class).to receive(:manages_homedir?).and_return(false)\n      expect { instance[:managehome] = 'yes' }.to raise_error(Puppet::Error, /can not manage home directories/)\n    end\n\n    it \"can be set to true for a provider that does manage homedirs\" do\n      allow(provider.class).to receive(:manages_homedir?).and_return(true)\n      instance[:managehome] = 'yes'\n    end\n  end\n\n  describe \"instances\" do\n    it \"should delegate existence questions to its provider\" do\n      @provider = @provider_class.new(:name => 'foo', :ensure => :absent)\n      instance = described_class.new(:name => \"foo\", :provider => @provider)\n      expect(instance.exists?).to eq(false)\n\n      @provider.set(:ensure => :present)\n      expect(instance.exists?).to eq(true)\n    end\n  end\n\n  properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :password_min_age, :password_max_age, :password_warn_days, :groups, :roles, :auths, :profiles, :project, :keys, :expiry]\n\n  properties.each do |property|\n    it \"should have a #{property} property\" do\n      expect(described_class.attrclass(property).ancestors).to be_include(Puppet::Property)\n    end\n\n    it \"should have documentation for its #{property} property\" do\n      expect(described_class.attrclass(property).doc).to be_instance_of(String)\n    end\n  end\n\n  list_properties = [:groups, :roles, :auths]\n\n  list_properties.each do |property|\n    it \"should have a list '#{property}'\" do\n      expect(described_class.attrclass(property).ancestors).to be_include(Puppet::Property::List)\n    end\n  end\n\n  it \"should have an ordered list 'profiles'\" do\n    expect(described_class.attrclass(:profiles).ancestors).to be_include(Puppet::Property::OrderedList)\n  end\n\n  it \"should have key values 'keys'\" do\n    expect(described_class.attrclass(:keys).ancestors).to be_include(Puppet::Property::KeyValue)\n  end\n\n  describe \"when retrieving all current values\" do\n    before do\n      @provider = @provider_class.new(:name => 'foo', :ensure => :present, :uid => 15, :gid => 15)\n      @user = described_class.new(:name => \"foo\", :uid => 10, :provider => @provider)\n    end\n\n    it \"should return a hash containing values for all set properties\" do\n      @user[:gid] = 10\n      values = @user.retrieve\n      [@user.property(:uid), @user.property(:gid)].each { |property| expect(values).to be_include(property) }\n    end\n\n    it \"should set all values to :absent if the user is absent\" do\n      expect(@user.property(:ensure)).to receive(:retrieve).and_return(:absent)\n      expect(@user.property(:uid)).not_to receive(:retrieve)\n      expect(@user.retrieve[@user.property(:uid)]).to eq(:absent)\n    end\n\n    it \"should include the result of retrieving each property's current value if the user is present\" do\n      expect(@user.retrieve[@user.property(:uid)]).to eq(15)\n    end\n  end\n\n  describe \"when managing the ensure property\" do\n    it \"should support a :present value\" do\n      expect { described_class.new(:name => 'foo', :ensure => :present) }.to_not raise_error\n    end\n\n    it \"should support an :absent value\" do\n      expect { described_class.new(:name => 'foo', :ensure => :absent) }.to_not raise_error\n    end\n\n    it \"should call :create on the provider when asked to sync to the :present state\" do\n      @provider = @provider_class.new(:name => 'foo', :ensure => :absent)\n      expect(@provider).to receive(:create)\n      described_class.new(:name => 'foo', :ensure => :present, :provider => @provider).parameter(:ensure).sync\n    end\n\n    it \"should call :delete on the provider when asked to sync to the :absent state\" do\n      @provider = @provider_class.new(:name => 'foo', :ensure => :present)\n      expect(@provider).to receive(:delete)\n      described_class.new(:name => 'foo', :ensure => :absent, :provider => @provider).parameter(:ensure).sync\n    end\n\n    describe \"and determining the current state\" do\n      it \"should return :present when the provider indicates the user exists\" do\n        @provider = @provider_class.new(:name => 'foo', :ensure => :present)\n        expect(described_class.new(:name => 'foo', :ensure => :absent, :provider => @provider).parameter(:ensure).retrieve).to eq(:present)\n      end\n\n      it \"should return :absent when the provider indicates the user does not exist\" do\n        @provider = @provider_class.new(:name => 'foo', :ensure => :absent)\n        expect(described_class.new(:name => 'foo', :ensure => :present, :provider => @provider).parameter(:ensure).retrieve).to eq(:absent)\n      end\n    end\n  end\n\n  describe \"when managing the uid property\" do\n    it \"should convert number-looking strings into actual numbers\" do\n      expect(described_class.new(:name => 'foo', :uid => '50')[:uid]).to eq(50)\n    end\n\n    it \"should support UIDs as numbers\" do\n      expect(described_class.new(:name => 'foo', :uid => 50)[:uid]).to eq(50)\n    end\n\n    it \"should support :absent as a value\" do\n      expect(described_class.new(:name => 'foo', :uid => :absent)[:uid]).to eq(:absent)\n    end\n  end\n\n  describe \"when managing the gid\" do\n    it \"should support :absent as a value\" do\n      expect(described_class.new(:name => 'foo', :gid => :absent)[:gid]).to eq(:absent)\n    end\n\n    it \"should convert number-looking strings into actual numbers\" do\n      expect(described_class.new(:name => 'foo', :gid => '50')[:gid]).to eq(50)\n    end\n\n    it \"should support GIDs specified as integers\" do\n      expect(described_class.new(:name => 'foo', :gid => 50)[:gid]).to eq(50)\n    end\n\n    it \"should support groups specified by name\" do\n      expect(described_class.new(:name => 'foo', :gid => 'foo')[:gid]).to eq('foo')\n    end\n\n    describe \"when testing whether in sync\" do\n      it \"should return true if no 'should' values are set\" do\n        # this is currently not the case because gid has no default value, so we would never even\n        # call insync? on that property\n        if param = described_class.new(:name => 'foo').parameter(:gid)\n          expect(param).to be_safe_insync(500)\n        end\n      end\n\n      it \"should return true if any of the specified groups are equal to the current integer\" do\n        expect(Puppet::Util).to receive(:gid).with(\"foo\").and_return(300)\n        expect(Puppet::Util).to receive(:gid).with(\"bar\").and_return(500)\n        expect(described_class.new(:name => 'baz', :gid => [ 'foo', 'bar' ]).parameter(:gid)).to be_safe_insync(500)\n      end\n\n      it \"should return false if none of the specified groups are equal to the current integer\" do\n        expect(Puppet::Util).to receive(:gid).with(\"foo\").and_return(300)\n        expect(Puppet::Util).to receive(:gid).with(\"bar\").and_return(500)\n        expect(described_class.new(:name => 'baz', :gid => [ 'foo', 'bar' ]).parameter(:gid)).to_not be_safe_insync(700)\n      end\n    end\n\n    describe \"when syncing\" do\n      it \"should use the first found, specified group as the desired value and send it to the provider\" do\n        expect(Puppet::Util).to receive(:gid).with(\"foo\").and_return(nil)\n        expect(Puppet::Util).to receive(:gid).with(\"bar\").and_return(500)\n\n        @provider = @provider_class.new(:name => 'foo')\n        resource = described_class.new(:name => 'foo', :provider => @provider, :gid => [ 'foo', 'bar' ])\n\n        expect(@provider).to receive(:gid=).with(500)\n        resource.parameter(:gid).sync\n      end\n    end\n  end\n\n  describe \"when managing groups\" do\n    it \"should support a singe group\" do\n      expect { described_class.new(:name => 'foo', :groups => 'bar') }.to_not raise_error\n    end\n\n    it \"should support multiple groups as an array\" do\n      expect { described_class.new(:name => 'foo', :groups => [ 'bar' ]) }.to_not raise_error\n      expect { described_class.new(:name => 'foo', :groups => [ 'bar', 'baz' ]) }.to_not raise_error\n    end\n\n    it \"should not support a comma separated list\" do\n      expect { described_class.new(:name => 'foo', :groups => 'bar,baz') }.to raise_error(Puppet::Error, /Group names must be provided as an array/)\n    end\n\n    it \"should not support an empty string\" do\n      expect { described_class.new(:name => 'foo', :groups => '') }.to raise_error(Puppet::Error, /Group names must not be empty/)\n    end\n\n    describe \"when testing is in sync\" do\n      before :each do\n        # the useradd provider uses a single string to represent groups and so does Puppet::Property::List when converting to should values\n        @provider = @provider_class.new(:name => 'foo', :groups => 'a,b,e,f')\n      end\n\n      it \"should not care about order\" do\n        @property = described_class.new(:name => 'foo', :groups => [ 'a', 'c', 'b' ]).property(:groups)\n        expect(@property).to be_safe_insync([ 'a', 'b', 'c' ])\n        expect(@property).to be_safe_insync([ 'a', 'c', 'b' ])\n        expect(@property).to be_safe_insync([ 'b', 'a', 'c' ])\n        expect(@property).to be_safe_insync([ 'b', 'c', 'a' ])\n        expect(@property).to be_safe_insync([ 'c', 'a', 'b' ])\n        expect(@property).to be_safe_insync([ 'c', 'b', 'a' ])\n      end\n\n      it \"should merge current value and desired value if membership minimal\" do\n        @instance = described_class.new(:name => 'foo', :groups => [ 'a', 'c', 'b' ], :provider => @provider)\n        @instance[:membership] = :minimum\n        expect(@instance[:groups]).to eq('a,b,c,e,f')\n      end\n\n      it \"should not treat a subset of groups insync if membership inclusive\" do\n        @instance = described_class.new(:name => 'foo', :groups => [ 'a', 'c', 'b' ], :provider => @provider)\n        @instance[:membership] = :inclusive\n        expect(@instance[:groups]).to eq('a,b,c')\n      end\n    end\n  end\n\n  describe \"when managing the purge_ssh_keys property\" do\n    context \"with valid input\" do\n      ['true', :true, true].each do |input|\n        it \"should support #{input} as value\" do\n          expect { described_class.new(:name => 'foo', :purge_ssh_keys => input) }.to_not raise_error\n        end\n      end\n\n      ['false', :false, false].each do |input|\n        it \"should support #{input} as value\" do\n          expect { described_class.new(:name => 'foo', :purge_ssh_keys => input) }.to_not raise_error\n        end\n      end\n\n      it \"should support a String value\" do\n        expect { described_class.new(:name => 'foo', :purge_ssh_keys => File.expand_path('home/foo/.ssh/authorized_keys')) }.to_not raise_error\n      end\n\n      it \"should support an Array value\" do\n        expect { described_class.new(:name => 'foo', :purge_ssh_keys => [File.expand_path('home/foo/.ssh/authorized_keys'),\n          File.expand_path('custom/authorized_keys')]) }.to_not raise_error\n      end\n    end\n\n    context \"with faulty input\" do\n      it \"should raise error for relative path\" do\n        expect { described_class.new(:name => 'foo', :purge_ssh_keys => 'home/foo/.ssh/authorized_keys') }.to raise_error(Puppet::ResourceError,\n          /Paths to keyfiles must be absolute/ )\n      end\n\n      it \"should raise error for invalid type\" do\n        expect { described_class.new(:name => 'foo', :purge_ssh_keys => :invalid) }.to raise_error(Puppet::ResourceError,\n          /purge_ssh_keys must be true, false, or an array of file names/ )\n      end\n\n      it \"should raise error for array with relative path\" do\n        expect { described_class.new(:name => 'foo', :purge_ssh_keys => ['home/foo/.ssh/authorized_keys',\n          File.expand_path('custom/authorized_keys')]) }.to raise_error(Puppet::ResourceError,\n          /Paths to keyfiles must be absolute/ )\n      end\n\n      it \"should raise error for array with invalid type\" do\n        expect { described_class.new(:name => 'foo', :purge_ssh_keys => [:invalid,\n          File.expand_path('custom/authorized_keys')]) }.to raise_error(Puppet::ResourceError,\n          /Each entry for purge_ssh_keys must be a string/ )\n      end\n    end\n\n    context \"homedir retrieval\" do\n      it \"should accept the home provided\" do\n        expect(Puppet).not_to receive(:debug).with(\"User 'foo' does not exist\")\n        described_class.new(:name => 'foo', :purge_ssh_keys => true, :home => '/my_home')\n      end\n\n      it \"should accept the home provided\" do\n        expect(Dir).to receive(:home).with('foo').and_return('/my_home')\n        expect(Puppet).not_to receive(:debug).with(\"User 'foo' does not exist\")\n        described_class.new(:name => 'foo', :purge_ssh_keys => true)\n      end\n\n      it \"should output debug message when home directory cannot be retrieved\" do\n        allow(Dir).to receive(:home).with('foo').and_raise(ArgumentError)\n        expect(Puppet).to receive(:debug).with(\"User 'foo' does not exist\")\n        described_class.new(:name => 'foo', :purge_ssh_keys => true)\n      end\n    end\n  end\n\n  describe \"when managing expiry\" do\n    it \"should fail if given an invalid date\" do\n      expect { described_class.new(:name => 'foo', :expiry => \"200-20-20\") }.to raise_error(Puppet::Error, /Expiry dates must be YYYY-MM-DD/)\n    end\n  end\n\n  describe \"when managing minimum password age\" do\n    it \"should accept a negative minimum age\" do\n      expect { described_class.new(:name => 'foo', :password_min_age => '-1') }.to_not raise_error\n    end\n\n    it \"should fail with an empty minimum age\" do\n      expect { described_class.new(:name => 'foo', :password_min_age => '') }.to raise_error(Puppet::Error, /minimum age must be provided as a number/)\n    end\n  end\n\n  describe \"when managing maximum password age\" do\n    it \"should accept a negative maximum age\" do\n      expect { described_class.new(:name => 'foo', :password_max_age => '-1') }.to_not raise_error\n    end\n\n    it \"should fail with an empty maximum age\" do\n      expect { described_class.new(:name => 'foo', :password_max_age => '') }.to raise_error(Puppet::Error, /maximum age must be provided as a number/)\n    end\n  end\n\n  describe \"when managing warning password days\" do\n    it \"should accept a negative warning days\" do\n      expect { described_class.new(:name => 'foo', :password_warn_days => '-1') }.to_not raise_error\n    end\n\n    it \"should fail with an empty warning days\" do\n      expect { described_class.new(:name => 'foo', :password_warn_days => '') }.to raise_error(Puppet::Error, /warning days must be provided as a number/)\n    end\n  end\n\n  describe \"when managing passwords\" do\n    let(:transaction) { Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil) }\n    let(:harness) { Puppet::Transaction::ResourceHarness.new(transaction) }\n    let(:provider) { @provider_class.new(:name => 'foo', :ensure => :present) }\n    let(:resource) { described_class.new(:name => 'foo', :ensure => :present, :password => 'top secret', :provider => provider) }\n\n    it \"should not include the password in the change log when adding the password\" do\n      status = harness.evaluate(resource)\n      sync_event = status.events[0]\n      expect(sync_event.message).not_to include('top secret')\n      expect(sync_event.message).to eql('changed [redacted] to [redacted]')\n    end\n\n    it \"should not include the password in the change log when changing the password\" do\n      resource[:password] = 'super extra classified'\n      status = harness.evaluate(resource)\n      sync_event = status.events[0]\n      expect(sync_event.message).not_to include('super extra classified')\n      expect(sync_event.message).to eql('changed [redacted] to [redacted]')\n    end\n\n    it \"should fail if a ':' is included in the password\" do\n      expect { described_class.new(:name => 'foo', :password => \"some:thing\") }.to raise_error(Puppet::Error, /Passwords cannot include ':'/)\n    end\n\n    it \"should allow the value to be set to :absent\" do\n      expect { described_class.new(:name => 'foo', :password => :absent) }.to_not raise_error\n    end\n  end\n\n  describe \"when managing comment\" do\n    before :each do\n      @value = 'abcd™'\n      expect(@value.encoding).to eq(Encoding::UTF_8)\n      @user = described_class.new(:name => 'foo', :comment => @value)\n    end\n\n    describe \"#insync\" do\n      it \"should delegate to the provider's #comments_insync? method if defined\" do\n        # useradd subclasses nameservice and thus inherits #comments_insync?\n        user = described_class.new(:name => 'foo', :comment => @value, :provider => :useradd)\n        comment_property = user.properties.find {|p| p.name == :comment}\n        expect(user.provider).to receive(:comments_insync?)\n        comment_property.insync?('bar')\n      end\n\n      describe \"#change_to_s\" do\n        let(:is) { \"\\u2603\" }\n        let(:should) { \"\\u06FF\" }\n        let(:comment_property) { @user.properties.find { |p| p.name == :comment } }\n\n        context \"given is and should strings with incompatible encoding\" do\n          it \"should return a formatted string\" do\n            is.force_encoding(Encoding::ASCII_8BIT)\n            should.force_encoding(Encoding::UTF_8)\n            expect(Encoding.compatible?(is, should)).to be_falsey\n            expect(comment_property.change_to_s(is,should)).to match(/changed '\\u{E2}\\u{98}\\u{83}' to '\\u{DB}\\u{BF}'/)\n          end\n        end\n\n        context \"given is and should strings with compatible encoding\" do\n          it \"should return a formatted string\" do\n            is.force_encoding(Encoding::UTF_8)\n            should.force_encoding(Encoding::UTF_8)\n            expect(Encoding.compatible?(is, should)).to be_truthy\n            expect(comment_property.change_to_s(is,should)).to match(/changed '\\u{2603}' to '\\u{6FF}'/u)\n          end\n        end\n      end\n    end\n  end\n\n  describe \"when manages_solaris_rbac is enabled\" do\n    it \"should support a :role value for ensure\" do\n      expect { described_class.new(:name => 'foo', :ensure => :role) }.to_not raise_error\n    end\n  end\n\n  describe \"when user has roles\" do\n    it \"should autorequire roles on non-Windows\", :unless => Puppet::Util::Platform.windows? do\n      testuser = described_class.new(:name => \"testuser\", :roles => ['testrole'] )\n      testrole = described_class.new(:name => \"testrole\")\n\n      Puppet::Resource::Catalog.new :testing do |conf|\n        [testuser, testrole].each { |resource| conf.add_resource resource }\n      end\n\n      rel = testuser.autorequire[0]\n      expect(rel.source.ref).to eq(testrole.ref)\n      expect(rel.target.ref).to eq(testuser.ref)\n    end\n\n    it \"should not autorequire roles on Windows\", :if => Puppet::Util::Platform.windows? do\n      testuser = described_class.new(:name => \"testuser\", :roles => ['testrole'] )\n      testrole = described_class.new(:name => \"testrole\")\n\n      Puppet::Resource::Catalog.new :testing do |conf|\n        [testuser, testrole].each { |resource| conf.add_resource resource }\n      end\n\n      expect(testuser.autorequire).to be_empty\n    end\n\n    it \"should sync the user roles when changing the state of :ensure if :roles is being managed\" do\n      user = Puppet::Type.type(:user).new(:name => \"myUser\", :ensure => :present)\n      user[:roles] = 'testRole'\n\n      allow(user.provider.class).to receive(:supports_parameter?).and_return(true)\n      expect(user.property(:roles)).to receive(:retrieve).and_return(\"other\")\n      expect(user.property(:roles)).to receive(:insync?).and_return(false)\n      expect(user.property(:roles)).to receive(:sync)\n\n      allow(user.provider).to receive(:create)\n\n      user.property(:ensure).sync\n    end\n  end\n\n  describe \"when setting shell\" do\n    before :each do\n      @shell_provider_class = described_class.provide(:shell_manager) do\n        has_features :manages_shell\n        mk_resource_methods\n        def create; check_valid_shell;end\n        def shell=(value); check_valid_shell; end\n        def delete; end\n        def exists?; get(:ensure) != :absent; end\n        def flush; end\n        def self.instances; []; end\n        def check_valid_shell; end\n      end\n\n      allow(described_class).to receive(:defaultprovider).and_return(@shell_provider_class)\n    end\n\n    it \"should call :check_valid_shell on the provider when changing shell value\" do\n      @provider = @shell_provider_class.new(:name => 'foo', :shell => '/bin/bash', :ensure => :present)\n      expect(@provider).to receive(:check_valid_shell)\n      resource = described_class.new(:name => 'foo', :shell => '/bin/zsh', :provider => @provider)\n      allow(Puppet::Util::Storage).to receive(:load)\n      allow(Puppet::Util::Storage).to receive(:store)\n      catalog = Puppet::Resource::Catalog.new\n      catalog.add_resource resource\n      catalog.apply\n    end\n\n    it \"should call :check_valid_shell on the provider when changing ensure from present to absent\" do\n      @provider = @shell_provider_class.new(:name => 'foo', :shell => '/bin/bash', :ensure => :absent)\n      expect(@provider).to receive(:check_valid_shell)\n      resource = described_class.new(:name => 'foo', :shell => '/bin/zsh', :provider => @provider)\n      allow(Puppet::Util::Storage).to receive(:load)\n      allow(Puppet::Util::Storage).to receive(:store)\n      catalog = Puppet::Resource::Catalog.new\n      catalog.add_resource resource\n      catalog.apply\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/type/whit_spec.rb",
    "content": "require 'spec_helper'\n\nwhit = Puppet::Type.type(:whit)\n\ndescribe whit do\n  it \"should stringify in a way that users will regognise\" do\n    expect(whit.new(:name => \"Foo::Bar\").to_s).to eq(\"Foo::Bar\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/type_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet_spec/compiler'\nrequire 'puppet/property/boolean'\n\nPuppet::Type.newtype(:type_test) do\n  ensurable\n  newparam(:name, isnamevar: true)\n  newproperty(:device)\n  newproperty(:blockdevice)\n  newproperty(:fstype)\n  newproperty(:options)\n  newproperty(:pass)\n  newproperty(:atboot, parent: Puppet::Property::Boolean) do\n    def munge(value)\n      munged = super\n      if munged\n        :yes\n      else\n        :no\n      end\n    end\n  end\n  newparam(:remounts) do\n    newvalues(:true, :false)\n    defaultto do\n      true\n    end\n  end\nend\nPuppet::Type.type(:type_test).provide(:type_test) do\n  mk_resource_methods\nend\n\ndescribe Puppet::Type, :unless => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n  include PuppetSpec::Compiler\n\n  let(:resource_type) { :type_test }\n  let(:klass) { Puppet::Type.type(resource_type) }\n  let(:ref_type) { klass.name.to_s.capitalize }\n\n  it \"should be Comparable\" do\n    a = Puppet::Type.type(:notify).new(:name => \"a\")\n    b = Puppet::Type.type(:notify).new(:name => \"b\")\n    c = Puppet::Type.type(:notify).new(:name => \"c\")\n\n    [[a, b, c], [a, c, b], [b, a, c], [b, c, a], [c, a, b], [c, b, a]].each do |this|\n      expect(this.sort).to eq([a, b, c])\n    end\n\n    expect(a).to be < b\n    expect(a).to be < c\n    expect(b).to be > a\n    expect(b).to be < c\n    expect(c).to be > a\n    expect(c).to be > b\n\n    [a, b, c].each {|x| expect(a).to be <= x }\n    [a, b, c].each {|x| expect(c).to be >= x }\n\n    expect(b).to be_between(a, c)\n  end\n\n  it \"should consider a parameter to be valid if it is a valid parameter\" do\n    expect(klass).to be_valid_parameter(:name)\n  end\n\n  it \"should consider a parameter to be valid if it is a valid property\" do\n    expect(klass).to be_valid_parameter(:fstype)\n  end\n\n  it \"should consider a parameter to be valid if it is a valid metaparam\" do\n    expect(klass).to be_valid_parameter(:noop)\n  end\n\n  it \"should be able to retrieve a property by name\" do\n    resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present)\n    expect(resource.property(:fstype)).to be_instance_of(klass.attrclass(:fstype))\n  end\n\n  it \"should be able to retrieve a parameter by name\" do\n    resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present)\n    expect(resource.parameter(:name)).to be_instance_of(klass.attrclass(:name))\n  end\n\n  it \"should be able to retrieve a property by name using the :parameter method\" do\n    resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present)\n    expect(resource.parameter(:fstype)).to be_instance_of(klass.attrclass(:fstype))\n  end\n\n  it \"should be able to retrieve all set properties\" do\n    resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present)\n    props = resource.properties\n    expect(props).not_to be_include(nil)\n    [:fstype, :ensure, :pass].each do |name|\n      expect(props).to be_include(resource.parameter(name))\n    end\n  end\n\n  it \"can retrieve all set parameters\" do\n    resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present, :tag => 'foo')\n    params = resource.parameters_with_value\n    [:name, :provider, :ensure, :fstype, :pass, :loglevel, :tag].each do |name|\n      expect(params).to be_include(resource.parameter(name))\n    end\n  end\n\n  it \"can not return any `nil` values when retrieving all set parameters\" do\n    resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present, :tag => 'foo')\n    params = resource.parameters_with_value\n    expect(params).not_to be_include(nil)\n  end\n\n  it \"can return an iterator for all set parameters\" do\n    resource = Puppet::Type.type(:notify).new(:name=>'foo',:message=>'bar',:tag=>'baz',:require=> \"File['foo']\")\n    params = [:name, :message, :withpath, :loglevel, :tag, :require]\n    resource.eachparameter { |param|\n      expect(params).to be_include(param.to_s.to_sym)\n    }\n  end\n\n  it \"should have a method for setting default values for resources\" do\n    expect(klass.new(:name => \"foo\")).to respond_to(:set_default)\n  end\n\n  it \"should do nothing for attributes that have no defaults and no specified value\" do\n    expect(klass.new(:name => \"foo\").parameter(:noop)).to be_nil\n  end\n\n  it \"should have a method for adding tags\" do\n    expect(klass.new(:name => \"foo\")).to respond_to(:tags)\n  end\n\n  it \"should use the tagging module\" do\n    expect(klass.ancestors).to be_include(Puppet::Util::Tagging)\n  end\n\n  it \"should delegate to the tagging module when tags are added\" do\n    resource = klass.new(:name => \"foo\")\n    allow(resource).to receive(:tag).with(resource_type)\n\n    expect(resource).to receive(:tag).with(:tag1, :tag2)\n\n    resource.tags = [:tag1,:tag2]\n  end\n\n  it \"should add the current type as tag\" do\n    resource = klass.new(:name => \"foo\")\n    allow(resource).to receive(:tag)\n\n    expect(resource).to receive(:tag).with(resource_type)\n\n    resource.tags = [:tag1,:tag2]\n  end\n\n  it \"should have a method to know if the resource is exported\" do\n    expect(klass.new(:name => \"foo\")).to respond_to(:exported?)\n  end\n\n  it \"should have a method to know if the resource is virtual\" do\n    expect(klass.new(:name => \"foo\")).to respond_to(:virtual?)\n  end\n\n  it \"should consider its version to be zero if it has no catalog\" do\n    expect(klass.new(:name => \"foo\").version).to eq(0)\n  end\n\n  it \"reports the correct path even after path is used during setup of the type\" do\n    Puppet::Type.newtype(:testing) do\n      newparam(:name) do\n        isnamevar\n        validate do |value|\n          path # forces the computation of the path\n        end\n      end\n    end\n\n    ral = compile_to_ral(<<-MANIFEST)\n      class something {\n        testing { something: }\n      }\n      include something\n    MANIFEST\n\n    expect(ral.resource(\"Testing[something]\").path).to eq(\"/Stage[main]/Something/Testing[something]\")\n  end\n\n  context \"alias metaparam\" do\n    it \"creates a new name that can be used for resource references\" do\n      ral = compile_to_ral(<<-MANIFEST)\n        notify { a: alias => c }\n      MANIFEST\n\n      expect(ral.resource(\"Notify[a]\")).to eq(ral.resource(\"Notify[c]\"))\n    end\n  end\n\n  context 'aliased resource' do\n    it 'fails if a resource is defined and then redefined using name that results in the same alias' do\n      drive = Puppet::Util::Platform.windows? ? 'C:' : ''\n      code = <<~PUPPET\n        $dir='#{drive}/tmp/test'\n        $same_dir='#{drive}/tmp/test/'\n\n        file {$dir:\n          ensure => directory\n        }\n\n        file { $same_dir:\n          ensure => directory\n        }\n      PUPPET\n\n      expect { compile_to_ral(code) }.to raise_error(/resource \\[\"File\", \"#{drive}\\/tmp\\/test\"\\] already declared/)\n    end\n  end\n\n  context \"resource attributes\" do\n    let(:resource) {\n      resource = klass.new(:name => \"foo\")\n      catalog = Puppet::Resource::Catalog.new\n      catalog.version = 50\n      catalog.add_resource resource\n      resource\n    }\n\n    it \"should consider its version to be its catalog version\" do\n      expect(resource.version).to eq(50)\n    end\n\n    it \"should have tags\" do\n      expect(resource).to be_tagged(resource_type.to_s)\n      expect(resource).to be_tagged(\"foo\")\n    end\n\n    it \"should have a path\" do\n      expect(resource.path).to eq(\"/#{ref_type}[foo]\")\n    end\n  end\n\n  it \"should consider its type to be the name of its class\" do\n    expect(klass.new(:name => \"foo\").type).to eq(resource_type)\n  end\n\n  it \"should use any provided noop value\" do\n    expect(klass.new(:name => \"foo\", :noop => true)).to be_noop\n  end\n\n  it \"should use the global noop value if none is provided\" do\n    Puppet[:noop] = true\n    expect(klass.new(:name => \"foo\")).to be_noop\n  end\n\n  it \"should not be noop if in a non-host_config catalog\" do\n    resource = klass.new(:name => \"foo\")\n    catalog = Puppet::Resource::Catalog.new\n    catalog.add_resource resource\n    expect(resource).not_to be_noop\n  end\n\n  describe \"when creating an event\" do\n    before do\n      @resource = klass.new :name => \"foo\"\n    end\n\n    it \"should have the resource's reference as the resource\" do\n      expect(@resource.event.resource).to eq(\"#{ref_type}[foo]\")\n    end\n\n    it \"should have the resource's log level as the default log level\" do\n      @resource[:loglevel] = :warning\n      expect(@resource.event.default_log_level).to eq(:warning)\n    end\n\n    {:file => \"/my/file\", :line => 50}.each do |attr, value|\n      it \"should set the #{attr}\" do\n        allow(@resource).to receive(attr).and_return(value)\n        expect(@resource.event.send(attr)).to eq(value)\n      end\n    end\n\n    it \"should set the tags\" do\n      @resource.tag(\"abc\", \"def\")\n      expect(@resource.event).to be_tagged(\"abc\")\n      expect(@resource.event).to be_tagged(\"def\")\n    end\n\n    it \"should allow specification of event attributes\" do\n      expect(@resource.event(:status => \"noop\").status).to eq(\"noop\")\n    end\n  end\n\n  describe \"when creating a provider\" do\n    before :each do\n      @type = Puppet::Type.newtype(:provider_test_type) do\n        newparam(:name) { isnamevar }\n        newparam(:foo)\n        newproperty(:bar)\n      end\n    end\n\n    after :each do\n      @type.provider_hash.clear\n    end\n\n    describe \"when determining if instances of the type are managed\" do\n      it \"should not consider audit only resources to be managed\" do\n        expect(@type.new(:name => \"foo\", :audit => 'all').managed?).to be_falsey\n      end\n\n      it \"should not consider resources with only parameters to be managed\" do\n        expect(@type.new(:name => \"foo\", :foo => 'did someone say food?').managed?).to be_falsey\n      end\n\n      it \"should consider resources with any properties set to be managed\" do\n        expect(@type.new(:name => \"foo\", :bar => 'Let us all go there').managed?).to be_truthy\n      end\n    end\n\n    it \"should have documentation for the 'provider' parameter if there are providers\" do\n      @type.provide(:test_provider)\n      expect(@type.paramdoc(:provider)).to match(/`provider_test_type`[\\s]+resource/)\n    end\n\n    it \"should not have documentation for the 'provider' parameter if there are no providers\" do\n      expect { @type.paramdoc(:provider) }.to raise_error(NoMethodError)\n    end\n\n    it \"should create a subclass of Puppet::Provider for the provider\" do\n      provider = @type.provide(:test_provider)\n\n      expect(provider.ancestors).to include(Puppet::Provider)\n    end\n\n    it \"should use a parent class if specified\" do\n      parent_provider = @type.provide(:parent_provider)\n      child_provider  = @type.provide(:child_provider, :parent => parent_provider)\n\n      expect(child_provider.ancestors).to include(parent_provider)\n    end\n\n    it \"should use a parent class if specified by name\" do\n      parent_provider = @type.provide(:parent_provider)\n      child_provider  = @type.provide(:child_provider, :parent => :parent_provider)\n\n      expect(child_provider.ancestors).to include(parent_provider)\n    end\n\n    it \"should raise an error when the parent class can't be found\" do\n      expect {\n        @type.provide(:child_provider, :parent => :parent_provider)\n      }.to raise_error(Puppet::DevError, /Could not find parent provider.+parent_provider/)\n    end\n\n    it \"should ensure its type has a 'provider' parameter\" do\n      @type.provide(:test_provider)\n\n      expect(@type.parameters).to include(:provider)\n    end\n\n    it \"should remove a previously registered provider with the same name\" do\n      old_provider = @type.provide(:test_provider)\n      new_provider = @type.provide(:test_provider)\n\n      expect(old_provider).not_to equal(new_provider)\n    end\n\n    it \"should register itself as a provider for the type\" do\n      provider = @type.provide(:test_provider)\n\n      expect(provider).to eq(@type.provider(:test_provider))\n    end\n\n    it \"should create a provider when a provider with the same name previously failed\" do\n      @type.provide(:test_provider) do\n        raise \"failed to create this provider\"\n      end rescue nil\n\n      provider = @type.provide(:test_provider)\n\n      expect(provider.ancestors).to include(Puppet::Provider)\n      expect(provider).to eq(@type.provider(:test_provider))\n    end\n\n    describe \"with a parent class from another type\" do\n      before :each do\n        @parent_type = Puppet::Type.newtype(:provider_parent_type) do\n          newparam(:name) { isnamevar }\n        end\n        @parent_provider = @parent_type.provide(:parent_provider)\n      end\n\n      it \"should be created successfully\" do\n        child_provider = @type.provide(:child_provider, :parent => @parent_provider)\n        expect(child_provider.ancestors).to include(@parent_provider)\n      end\n\n      it \"should be registered as a provider of the child type\" do\n        @type.provide(:child_provider, :parent => @parent_provider)\n        expect(@type.providers).to include(:child_provider)\n        expect(@parent_type.providers).not_to include(:child_provider)\n      end\n    end\n  end\n\n  describe \"when choosing a default provider\" do\n    it \"should choose the provider with the highest specificity\" do\n      # Make a fake type\n      type = Puppet::Type.newtype(:defaultprovidertest) do\n        newparam(:name) do end\n      end\n\n      basic = type.provide(:basic) {}\n      greater = type.provide(:greater) {}\n\n      allow(basic).to receive(:specificity).and_return(1)\n      allow(greater).to receive(:specificity).and_return(2)\n\n      expect(type.defaultprovider).to equal(greater)\n    end\n  end\n\n  context \"autorelations\" do\n    before :each do\n      Puppet::Type.newtype(:autorelation_one) do\n        newparam(:name) { isnamevar }\n      end\n    end\n\n    describe \"when building autorelations\" do\n      it \"should be able to autorequire puppet resources\" do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autorequire(:autorelation_one) { Puppet::Type.type(:notify).new(name: 'test') }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'Notify[test]': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Notify[test]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:NONE)\n      end\n\n      it \"should be able to autorequire resources\" do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autorequire(:autorelation_one) { ['foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:NONE)\n      end\n\n      it 'should not fail autorequire contains undef entries' do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autorequire(:autorelation_one) { [nil, 'foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:NONE)\n      end\n\n      it \"should be able to autosubscribe resources\" do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autosubscribe(:autorelation_one) { ['foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:ALL_EVENTS)\n      end\n\n      it 'should not fail if autosubscribe contains undef entries' do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autosubscribe(:autorelation_one) { [nil, 'foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:ALL_EVENTS)\n      end\n\n      it \"should be able to autobefore resources\" do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autobefore(:autorelation_one) { ['foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:NONE)\n      end\n\n      it \"should not fail when autobefore contains undef entries\" do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autobefore(:autorelation_one) { [nil, 'foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:NONE)\n      end\n\n      it \"should be able to autonotify resources\" do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autonotify(:autorelation_one) { ['foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:ALL_EVENTS)\n      end\n\n      it 'should not fail if autonotify contains undef entries' do\n        Puppet::Type.newtype(:autorelation_two) do\n          newparam(:name) { isnamevar }\n          autonotify(:autorelation_one) { [nil, 'foo'] }\n        end\n\n        relationship_graph = compile_to_relationship_graph(<<-MANIFEST)\n          autorelation_one { 'foo': }\n          autorelation_two { 'bar': }\n        MANIFEST\n\n        src = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_two[bar]' }.first\n        dst = relationship_graph.vertices.select{ |x| x.ref.to_s == 'Autorelation_one[foo]' }.first\n\n        expect(relationship_graph.edge?(src,dst)).to be_truthy\n        expect(relationship_graph.edges_between(src,dst).first.event).to eq(:ALL_EVENTS)\n      end\n    end\n  end\n\n  describe \"when initializing\" do\n    describe \"and passed a Puppet::Resource instance\" do\n      it \"should set its title to the title of the resource if the resource type is equal to the current type\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\", :parameters => {:name => \"/other\"})\n        expect(klass.new(resource).title).to eq(\"/foo\")\n      end\n\n      it \"should set its title to the resource reference if the resource type is not equal to the current type\" do\n        resource = Puppet::Resource.new(:user, \"foo\")\n        expect(klass.new(resource).title).to eq(\"User[foo]\")\n      end\n\n      [:line, :file, :catalog, :exported, :virtual].each do |param|\n        it \"should copy '#{param}' from the resource if present\" do\n          resource = Puppet::Resource.new(resource_type, \"/foo\")\n          resource.send(param.to_s + \"=\", \"foo\")\n          resource.send(param.to_s + \"=\", \"foo\")\n          expect(klass.new(resource).send(param)).to eq(\"foo\")\n        end\n      end\n\n      it \"should copy any tags from the resource\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\")\n        resource.tag \"one\", \"two\"\n        tags = klass.new(resource).tags\n        expect(tags).to be_include(\"one\")\n        expect(tags).to be_include(\"two\")\n      end\n\n      it \"should copy the resource's parameters as its own\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\", :parameters => {:atboot => :yes, :fstype => \"boo\"})\n        params = klass.new(resource).to_hash\n        expect(params[:fstype]).to eq(\"boo\")\n        expect(params[:atboot]).to eq(:yes)\n      end\n\n      it \"copies sensitive parameters to the appropriate properties\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\",\n                                        :parameters => {:atboot => :yes, :fstype => \"boo\"},\n                                        :sensitive_parameters => [:fstype])\n        type = klass.new(resource)\n        expect(type.property(:fstype).sensitive).to eq true\n      end\n\n      it \"logs a warning when a parameter is marked as sensitive\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\",\n                                        :parameters => {:atboot => :yes, :fstype => \"boo\", :remounts => true},\n                                        :sensitive_parameters => [:remounts])\n        expect_any_instance_of(klass).to receive(:warning).with(/Unable to mark 'remounts' as sensitive: remounts is a parameter and not a property/)\n        klass.new(resource)\n      end\n\n      it \"logs a warning when a property is not set but is marked as sensitive\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\",\n                                        :parameters => {:atboot => :yes, :fstype => \"boo\"},\n                                        :sensitive_parameters => [:device])\n        expect_any_instance_of(klass).to receive(:warning).with(\"Unable to mark 'device' as sensitive: the property itself was not assigned a value.\")\n        klass.new(resource)\n      end\n\n      it \"logs an error when a property is not defined on the type but is marked as sensitive\" do\n        resource = Puppet::Resource.new(resource_type, \"/foo\",\n                                        :parameters => {:atboot => :yes, :fstype => \"boo\"},\n                                        :sensitive_parameters => [:content])\n        expect_any_instance_of(klass).to receive(:err).with(\"Unable to mark 'content' as sensitive: the property itself is not defined on #{resource_type}.\")\n        klass.new(resource)\n      end\n    end\n\n    describe \"and passed a Hash\" do\n      it \"should extract the title from the hash\" do\n        expect(klass.new(:title => \"/yay\").title).to eq(\"/yay\")\n      end\n\n      it \"should work when hash keys are provided as strings\" do\n        expect(klass.new(\"title\" => \"/yay\").title).to eq(\"/yay\")\n      end\n\n      it \"should work when hash keys are provided as symbols\" do\n        expect(klass.new(:title => \"/yay\").title).to eq(\"/yay\")\n      end\n\n      it \"should use the name from the hash as the title if no explicit title is provided\" do\n        expect(klass.new(:name => \"/yay\").title).to eq(\"/yay\")\n      end\n\n      it \"should use the Resource Type's namevar to determine how to find the name in the hash\" do\n        yay = make_absolute('/yay')\n        expect(Puppet::Type.type(:file).new(:path => yay).title).to eq(yay)\n      end\n\n      [:catalog].each do |param|\n        it \"should extract '#{param}' from the hash if present\" do\n          expect(klass.new(:name => \"/yay\", param => \"foo\").send(param)).to eq(\"foo\")\n        end\n      end\n\n      it \"should use any remaining hash keys as its parameters\" do\n        resource = klass.new(:title => \"/foo\", :catalog => \"foo\", :atboot => :yes, :fstype => \"boo\")\n        expect(resource[:fstype]).to eq(\"boo\")\n        expect(resource[:atboot]).to eq(:yes)\n      end\n    end\n\n    it \"should fail if any invalid attributes have been provided\" do\n      expect { klass.new(:title => \"/foo\", :nosuchattr => \"whatever\") }.to raise_error(Puppet::Error, /no parameter named 'nosuchattr'/)\n    end\n\n    context \"when an attribute fails validation\" do\n      it \"should fail with Puppet::ResourceError when PuppetError raised\" do\n        expect { Puppet::Type.type(:file).new(:title => \"/foo\", :source => \"unknown:///\") }.to raise_error(Puppet::ResourceError, /Parameter source failed on File\\[.*foo\\]/)\n      end\n\n      it \"should fail with Puppet::ResourceError when ArgumentError raised\" do\n        expect { Puppet::Type.type(:file).new(:title => \"/foo\", :mode => \"abcdef\") }.to raise_error(Puppet::ResourceError, /Parameter mode failed on File\\[.*foo\\]/)\n      end\n\n      it \"should include the file/line in the error\" do\n        allow_any_instance_of(Puppet::Type.type(:file)).to receive(:file).and_return(\"example.pp\")\n        allow_any_instance_of(Puppet::Type.type(:file)).to receive(:line).and_return(42)\n        expect { Puppet::Type.type(:file).new(:title => \"/foo\", :source => \"unknown:///\") }.to raise_error(Puppet::ResourceError, /\\(file: example\\.pp, line: 42\\)/)\n      end\n    end\n\n    it \"should set its name to the resource's title if the resource does not have a :name or namevar parameter set\" do\n      resource = Puppet::Resource.new(resource_type, \"/foo\")\n\n      expect(klass.new(resource).name).to eq(\"/foo\")\n    end\n\n    it \"should fail if no title, name, or namevar are provided\" do\n      expect { klass.new(:atboot => :yes) }.to raise_error(Puppet::Error)\n    end\n\n    it \"should set the attributes in the order returned by the class's :allattrs method\" do\n      allow(klass).to receive(:allattrs).and_return([:name, :atboot, :noop])\n      resource = Puppet::Resource.new(resource_type, \"/foo\", :parameters => {:name => \"myname\", :atboot => :yes, :noop => \"whatever\"})\n\n      set = []\n\n      allow_any_instance_of(klass).to receive(:newattr) do |_, param, hash|\n        set << param\n        double(\"a property\", :value= => nil, :default => nil, :name => nil)\n      end\n\n      klass.new(resource)\n\n      expect(set[-1]).to eq(:noop)\n      expect(set[-2]).to eq(:atboot)\n    end\n\n    it \"should always set the name and then default provider before anything else\" do\n      allow(klass).to receive(:allattrs).and_return([:provider, :name, :atboot])\n      resource = Puppet::Resource.new(resource_type, \"/foo\", :parameters => {:name => \"myname\", :atboot => :yes})\n\n      set = []\n\n      allow_any_instance_of(klass).to receive(:newattr) do |_, param, hash|\n        set << param\n        double(\"a property\", :value= => nil, :default => nil, :name => nil)\n      end\n\n      klass.new(resource)\n      expect(set[0]).to eq(:name)\n      expect(set[1]).to eq(:provider)\n    end\n\n    # This one is really hard to test :/\n    it \"should set each default immediately if no value is provided\" do\n      # We have a :confine block that calls execute in our upstart provider, which fails\n      # on jruby. Thus, we stub it out here since we don't care to do any assertions on it.\n      # This is only an issue if you're running these unit tests on a platform where upstart\n      # is a default provider, like Ubuntu trusty.\n      allow(Puppet::Util::Execution).to receive(:execute)\n\n      defaults = []\n      allow_any_instance_of(Puppet::Type.type(:service)).to receive(:set_default) { |_, value| defaults << value }\n\n      Puppet::Type.type(:service).new :name => \"whatever\"\n\n      expect(defaults[0]).to eq(:provider)\n    end\n\n    it \"should retain a copy of the originally provided parameters\" do\n      expect(klass.new(:name => \"foo\", :atboot => :yes, :noop => false).original_parameters).to eq({:atboot => :yes, :noop => false})\n    end\n\n    it \"should delete the name via the namevar from the originally provided parameters\" do\n      expect(Puppet::Type.type(:file).new(:name => make_absolute('/foo')).original_parameters[:path]).to be_nil\n    end\n\n    context \"when validating the resource\" do\n      it \"should call the type's validate method if present\" do\n        expect_any_instance_of(Puppet::Type.type(:file)).to receive(:validate)\n        Puppet::Type.type(:file).new(:name => make_absolute('/foo'))\n      end\n\n      it \"should raise Puppet::ResourceError with resource name when Puppet::Error raised\" do\n        expect do\n          Puppet::Type.type(:file).new(\n            :name => make_absolute('/foo'),\n            :source => \"puppet:///\",\n            :content => \"foo\"\n          )\n        end.to raise_error(Puppet::ResourceError, /Validation of File\\[.*foo.*\\]/)\n      end\n\n      it \"should raise Puppet::ResourceError with manifest file and line on failure\" do\n        allow_any_instance_of(Puppet::Type.type(:file)).to receive(:file).and_return(\"example.pp\")\n        allow_any_instance_of(Puppet::Type.type(:file)).to receive(:line).and_return(42)\n        expect do\n          Puppet::Type.type(:file).new(\n            :name => make_absolute('/foo'),\n            :source => \"puppet:///\",\n            :content => \"foo\"\n          )\n        end.to raise_error(Puppet::ResourceError, /Validation.*\\(file: example\\.pp, line: 42\\)/)\n      end\n    end\n  end\n\n  describe \"#set_sensitive_parameters\" do\n    let(:sensitive_type) do\n      Puppet::Type.newtype(:sensitive_test) do\n        newparam(:name) { isnamevar }\n        newproperty(:secret) do\n          newvalues(/.*/)\n          sensitive true\n        end\n        newproperty(:transparency) do\n          newvalues(/.*/)\n          sensitive false\n        end\n        newproperty(:things) { newvalues(/.*/) }\n      end\n    end\n\n    it \"should mark properties as sensitive\" do\n      resource = sensitive_type.new(:name => 'foo', :secret => 'uber classified')\n      expect(resource.parameters[:secret].sensitive).to be true\n    end\n\n    it \"should not have a sensitive flag when not set\" do\n      resource = sensitive_type.new(:name => 'foo', :things => '1337')\n      expect(resource.parameters[:things].sensitive).to be_nil\n    end\n\n    it \"should define things as not sensitive\" do\n      resource = sensitive_type.new(:name => 'foo', :transparency => 'public knowledge')\n      expect(resource.parameters[:transparency].sensitive).to be false\n    end\n\n    it \"should honor when sensitivity is set in a manifest\" do\n      resource = sensitive_type.new(:name => 'foo',\n                                    :transparency => Puppet::Pops::Types::PSensitiveType::Sensitive.new('top secret'),\n                                    :sensitive_parameters => [:transparency]\n                                    )\n      expect(resource.parameters[:transparency].sensitive).to be true\n    end\n  end\n\n  describe \"when #finish is called on a type\" do\n    let(:post_hook_type) do\n      Puppet::Type.newtype(:finish_test) do\n        newparam(:name) { isnamevar }\n\n        newparam(:post) do\n          def post_compile\n            raise \"post_compile hook ran\"\n          end\n        end\n      end\n    end\n\n    let(:post_hook_resource) do\n      post_hook_type.new(:name => 'foo',:post => 'fake_value')\n    end\n\n    it \"should call #post_compile on parameters that implement it\" do\n      expect { post_hook_resource.finish }.to raise_error(RuntimeError, \"post_compile hook ran\")\n    end\n  end\n\n  it \"should have a class method for converting a hash into a Puppet::Resource instance\" do\n    expect(klass).to respond_to(:hash2resource)\n  end\n\n  describe \"when converting a hash to a Puppet::Resource instance\" do\n    before do\n      @type = klass\n    end\n\n    it \"should treat a :title key as the title of the resource\" do\n      expect(@type.hash2resource(:name => \"/foo\", :title => \"foo\").title).to eq(\"foo\")\n    end\n\n    it \"should use the name from the hash as the title if no explicit title is provided\" do\n      expect(@type.hash2resource(:name => \"foo\").title).to eq(\"foo\")\n    end\n\n    it \"should use the Resource Type's namevar to determine how to find the name in the hash\" do\n      allow(@type).to receive(:key_attributes).and_return([ :myname ])\n\n      expect(@type.hash2resource(:myname => \"foo\").title).to eq(\"foo\")\n    end\n\n    [:catalog].each do |attr|\n      it \"should use any provided #{attr}\" do\n        expect(@type.hash2resource(:name => \"foo\", attr => \"eh\").send(attr)).to eq(\"eh\")\n      end\n    end\n\n    it \"should set all provided parameters on the resource\" do\n      expect(@type.hash2resource(:name => \"foo\", :fstype => \"boo\", :boot => \"fee\").to_hash).to eq({:name => \"foo\", :fstype => \"boo\", :boot => \"fee\"})\n    end\n\n    it \"should not set the title as a parameter on the resource\" do\n      expect(@type.hash2resource(:name => \"foo\", :title => \"eh\")[:title]).to be_nil\n    end\n\n    it \"should not set the catalog as a parameter on the resource\" do\n      expect(@type.hash2resource(:name => \"foo\", :catalog => \"eh\")[:catalog]).to be_nil\n    end\n\n    it \"should treat hash keys equivalently whether provided as strings or symbols\" do\n      resource = @type.hash2resource(\"name\" => \"foo\", \"title\" => \"eh\", \"fstype\" => \"boo\")\n      expect(resource.title).to eq(\"eh\")\n      expect(resource[:name]).to eq(\"foo\")\n      expect(resource[:fstype]).to eq(\"boo\")\n    end\n  end\n\n  describe \"when retrieving current property values\" do\n    before do\n      @resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present)\n      allow(@resource.property(:ensure)).to receive(:retrieve).and_return(:absent)\n    end\n\n    it \"should always retrieve the ensure value by default\" do\n      @ensurable_resource = Puppet::Type.type(:file).new(:name => \"/not/existent\", :mode => \"0644\")\n      # the ensure property is lazily metaprogrammed...\n      allow_any_instance_of(Puppet::Type::File::Ensure).to receive(:retrieve).and_return(:absent)\n      @ensurable_resource.retrieve_resource\n    end\n\n    it \"should not retrieve the ensure value if specified\" do\n      @ensurable_resource = Puppet::Type.type(:service).new(:name => \"DummyService\", :enable => true)\n      @ensurable_resource.properties.each { |prop| allow(prop).to receive(:retrieve) }\n      expect_any_instance_of(Puppet::Type::Service::Ensure).not_to receive(:retrieve)\n      @ensurable_resource.retrieve_resource\n    end\n\n    it \"should fail if its provider is unsuitable\" do\n      @resource = klass.new(:name => \"foo\", :fstype => \"bar\", :pass => 1, :ensure => :present)\n      expect(@resource.provider.class).to receive(:suitable?).and_return(false)\n      expect { @resource.retrieve_resource }.to raise_error(Puppet::Error)\n    end\n\n    it \"should return a Puppet::Resource instance with its type and title set appropriately\" do\n      result = @resource.retrieve_resource\n      expect(result).to be_instance_of(Puppet::Resource)\n      expect(result.type).to eq(ref_type)\n      expect(result.title).to eq(\"foo\")\n    end\n\n    it \"should set the name of the returned resource if its own name and title differ\" do\n      @resource[:name] = \"myname\"\n      @resource.title = \"other name\"\n      expect(@resource.retrieve_resource[:name]).to eq(\"myname\")\n    end\n\n    it \"should provide a value for all set properties\" do\n      values = @resource.retrieve_resource\n      [:ensure, :fstype, :pass].each { |property| expect(values[property]).not_to be_nil }\n    end\n\n    it \"should provide a value for 'ensure' even if no desired value is provided\" do\n      @resource = Puppet::Type.type(:file).new(:path => make_absolute(\"/my/file/that/can't/exist\"))\n    end\n\n    it \"should not call retrieve on non-ensure properties if the resource is absent and should consider the property absent\" do\n      expect(@resource.property(:ensure)).to receive(:retrieve).and_return(:absent)\n      expect(@resource.property(:fstype)).not_to receive(:retrieve)\n      expect(@resource.retrieve_resource[:fstype]).to eq(:absent)\n    end\n\n    it \"should include the result of retrieving each property's current value if the resource is present\" do\n      expect(@resource.property(:ensure)).to receive(:retrieve).and_return(:present)\n      expect(@resource.property(:fstype)).to receive(:retrieve).and_return(15)\n      @resource.retrieve_resource[:fstype] == 15\n    end\n  end\n\n  describe \"#to_resource\" do\n    it \"should return a Puppet::Resource that includes properties, parameters and tags\" do\n      type_resource = klass.new(\n        :ensure   => :present,\n        :name     => \"foo\",\n        :fstype   => \"bar\",\n        :remounts => true\n      )\n      type_resource.tags = %w{bar baz}\n\n      # If it's not a property it's a parameter\n      expect(type_resource.parameters[:remounts]).not_to be_a(Puppet::Property)\n      expect(type_resource.parameters[:fstype].is_a?(Puppet::Property)).to be_truthy\n\n      expect(type_resource.property(:ensure)).to receive(:retrieve).and_return(:present)\n      expect(type_resource.property(:fstype)).to receive(:retrieve).and_return(15)\n\n      resource = type_resource.to_resource\n\n      expect(resource).to be_a Puppet::Resource\n      expect(resource[:fstype]).to   eq(15)\n      expect(resource[:remounts]).to eq(:true)\n      expect(resource.tags).to eq(Puppet::Util::TagSet.new(%w{foo bar baz} + [resource_type.to_s]))\n    end\n  end\n\n  describe \".title_patterns\" do\n    describe \"when there's one namevar\" do\n      before do\n        @type_class = Puppet::Type.type(:notify)\n        allow(@type_class).to receive(:key_attributes).and_return([:one])\n      end\n\n      it \"should have a default pattern for when there's one namevar\" do\n        patterns = @type_class.title_patterns\n        expect(patterns.length).to eq(1)\n        expect(patterns[0].length).to eq(2)\n      end\n\n      it \"should have a regexp that captures the entire string\" do\n        patterns = @type_class.title_patterns\n        string = \"abc\\n\\tdef\"\n        patterns[0][0] =~ string\n        expect($1).to eq(\"abc\\n\\tdef\")\n      end\n    end\n  end\n\n  describe \"when in a catalog\" do\n    before do\n      @catalog = Puppet::Resource::Catalog.new\n      @container = Puppet::Type.type(:component).new(:name => \"container\")\n      @one = Puppet::Type.type(:file).new(:path => make_absolute(\"/file/one\"))\n      @two = Puppet::Type.type(:file).new(:path => make_absolute(\"/file/two\"))\n\n      @catalog.add_resource @container\n      @catalog.add_resource @one\n      @catalog.add_resource @two\n      @catalog.add_edge @container, @one\n      @catalog.add_edge @container, @two\n    end\n\n    it \"should have no parent if there is no in edge\" do\n      expect(@container.parent).to be_nil\n    end\n\n    it \"should set its parent to its in edge\" do\n      expect(@one.parent.ref).to eq(@container.ref)\n    end\n\n    after do\n      @catalog.clear(true)\n    end\n  end\n\n  it \"should have a 'stage' metaparam\" do\n    expect(Puppet::Type.metaparamclass(:stage)).to be_instance_of(Class)\n  end\n\n  describe \"#suitable?\" do\n    let(:type) { Puppet::Type.type(:file) }\n    let(:resource) { type.new :path => tmpfile('suitable') }\n    let(:provider) { resource.provider }\n\n    it \"should be suitable if its type doesn't use providers\" do\n      allow(type).to receive(:paramclass).with(:provider).and_return(nil)\n      expect(resource).to be_suitable\n    end\n\n    it \"should be suitable if it has a provider which is suitable\" do\n      expect(resource).to be_suitable\n    end\n\n    it \"should not be suitable if it has a provider which is not suitable\" do\n      allow(provider.class).to receive(:suitable?).and_return(false)\n      expect(resource).not_to be_suitable\n    end\n\n    it \"should be suitable if it does not have a provider and there is a default provider\" do\n      allow(resource).to receive(:provider).and_return(nil)\n      expect(resource).to be_suitable\n    end\n\n    it \"should not be suitable if it doesn't have a provider and there is not default provider\" do\n      allow(resource).to receive(:provider).and_return(nil)\n      allow(type).to receive(:defaultprovider).and_return(nil)\n\n      expect(resource).not_to be_suitable\n    end\n  end\n\n  describe \"::instances\" do\n    after :each do Puppet::Type.rmtype(:type_spec_fake_type) end\n    let :type do\n      Puppet::Type.newtype(:type_spec_fake_type) do\n        newparam(:name) do\n          isnamevar\n        end\n\n        newproperty(:prop1) {}\n      end\n\n      Puppet::Type.type(:type_spec_fake_type)\n    end\n\n    it \"should not fail if no suitable providers are found\" do\n      type.provide(:fake1) do\n        confine :exists => '/no/such/file'\n        mk_resource_methods\n      end\n\n      expect { expect(type.instances).to eq([]) }.to_not raise_error\n    end\n\n    context \"with a composite namevar type\" do\n      let :type do\n        Puppet::Type.newtype(:type_spec_fake_type) do\n          newparam(:name) do\n            isnamevar\n          end\n\n          newparam(:path) do\n            isnamevar\n          end\n\n          def self.title_patterns\n            [[%r{^(.*)@(.*)$}, [:name, :path]], [%r{^([^@]+)$}, [:name]]]\n          end\n\n          newproperty(:prop1) {}\n        end\n\n        Puppet::Type.type(:type_spec_fake_type)\n      end\n\n      before :each do\n        type.provide(:default) do\n          defaultfor 'os.name' => Puppet.runtime[:facter].value('os.name')\n          mk_resource_methods\n          class << self\n            attr_accessor :params\n          end\n\n          def title\n            \"#{@property_hash[:name]}@#{@property_hash[:path]}\"\n          end\n\n          def self.instance(name, path)\n            new(:name => name, :path => path, :ensure => :present)\n          end\n\n          def self.instances\n            @instances ||= params.collect { |param| instance(param.first.to_s, param.last.to_s) }\n          end\n\n          @params = [[:name_one, :path_one], [:name_two, :path_two]]\n        end\n      end\n\n      it \"should return composite titles for the instances\" do\n        expect(type.instances.map(&:title)).to eq([\"name_one@path_one\", \"name_two@path_two\"])\n      end\n    end\n\n    context \"with a default provider\" do\n      before :each do\n        type.provide(:default) do\n          defaultfor 'os.name' => Puppet.runtime[:facter].value('os.name')\n          mk_resource_methods\n          class << self\n            attr_accessor :names\n          end\n          def self.instance(name)\n            new(:name => name, :ensure => :present)\n          end\n          def self.instances\n            @instances ||= names.collect { |name| instance(name.to_s) }\n          end\n\n          @names = [:one, :two]\n        end\n      end\n\n      it \"should return only instances of the type\" do\n        expect(type.instances).to be_all {|x| x.is_a? type }\n      end\n\n      it \"should return instances from the default provider\" do\n        expect(type.instances.map(&:name)).to eq([\"one\", \"two\"])\n      end\n\n      it \"should return instances from all providers\" do\n        type.provide(:fake1, :parent => :default) { @names = [:three, :four] }\n        expect(type.instances.map(&:name)).to eq([\"one\", \"two\", \"three\", \"four\"])\n      end\n\n      it \"should not return instances from unsuitable providers\" do\n        type.provide(:fake1, :parent => :default) do\n          @names = [:three, :four]\n          confine :exists => \"/no/such/file\"\n        end\n\n        expect(type.instances.map(&:name)).to eq([\"one\", \"two\"])\n      end\n    end\n  end\n\n\n  describe \"::ensurable?\" do\n    before :each do\n      class TestEnsurableType < Puppet::Type\n        def exists?; end\n        def create; end\n        def destroy; end\n      end\n    end\n\n    it \"is true if the class has exists?, create, and destroy methods defined\" do\n      expect(TestEnsurableType).to be_ensurable\n    end\n\n    it \"is false if exists? is not defined\" do\n      TestEnsurableType.class_eval { remove_method(:exists?) }\n      expect(TestEnsurableType).not_to be_ensurable\n    end\n\n    it \"is false if create is not defined\" do\n      TestEnsurableType.class_eval { remove_method(:create) }\n      expect(TestEnsurableType).not_to be_ensurable\n    end\n\n    it \"is false if destroy is not defined\" do\n      TestEnsurableType.class_eval { remove_method(:destroy) }\n      expect(TestEnsurableType).not_to be_ensurable\n    end\n  end\nend\n\ndescribe Puppet::Type::RelationshipMetaparam do\n  include PuppetSpec::Files\n\n  it \"should be a subclass of Puppet::Parameter\" do\n    expect(Puppet::Type::RelationshipMetaparam.superclass).to equal(Puppet::Parameter)\n  end\n\n  it \"should be able to produce a list of subclasses\" do\n    expect(Puppet::Type::RelationshipMetaparam).to respond_to(:subclasses)\n  end\n\n  describe \"when munging relationships\" do\n    before do\n      @path = File.expand_path('/foo')\n      @resource = Puppet::Type.type(:file).new :name => @path\n      @metaparam = Puppet::Type.metaparamclass(:require).new :resource => @resource\n    end\n\n    it \"should accept Puppet::Resource instances\" do\n      ref = Puppet::Resource.new(:file, @path)\n      expect(@metaparam.munge(ref)[0]).to equal(ref)\n    end\n\n    it \"should turn any string into a Puppet::Resource\" do\n      expect(@metaparam.munge(\"File[/ref]\")[0]).to be_instance_of(Puppet::Resource)\n    end\n  end\n\n  it \"should be able to validate relationships\" do\n    expect(Puppet::Type.metaparamclass(:require).new(:resource => double(\"resource\"))).to respond_to(:validate_relationship)\n  end\n\n  describe 'if any specified resource is not in the catalog' do\n    let(:catalog) { double('catalog') }\n\n    let(:resource) do\n      double(\n        'resource',\n        :catalog => catalog,\n        :ref     => 'resource',\n        :line=   => nil,\n        :line    => nil,\n        :file=   => nil,\n        :file    => nil\n      )\n    end\n\n    let(:param) { Puppet::Type.metaparamclass(:require).new(:resource => resource, :value => %w{Foo[bar] Class[test]}) }\n\n    before do\n      expect(catalog).to receive(:resource).with(\"Foo[bar]\").and_return(\"something\")\n      expect(catalog).to receive(:resource).with(\"Class[Test]\").and_return(nil)\n    end\n\n    describe \"and the resource doesn't have a file or line number\" do\n      it \"raises an error\" do\n        expect { param.validate_relationship }.to raise_error do |error|\n          expect(error).to be_a Puppet::ResourceError\n          expect(error.message).to match %r[Class\\[Test\\]]\n        end\n      end\n    end\n\n    describe \"and the resource has a file or line number\" do\n      before do\n        allow(resource).to receive(:line).and_return('42')\n        allow(resource).to receive(:file).and_return('/hitchhikers/guide/to/the/galaxy')\n      end\n\n      it \"raises an error with context\" do\n        expect { param.validate_relationship }.to raise_error do |error|\n          expect(error).to be_a Puppet::ResourceError\n          expect(error.message).to match %r[Class\\[Test\\]]\n          expect(error.message).to match %r[\\(file: /hitchhikers/guide/to/the/galaxy, line: 42\\)]\n        end\n      end\n    end\n  end\nend\n\ndescribe Puppet::Type.metaparamclass(:audit) do\n  include PuppetSpec::Files\n\n  before do\n    @resource = Puppet::Type.type(:file).new :path => make_absolute('/foo')\n  end\n\n  it \"should default to being nil\" do\n    expect(@resource[:audit]).to be_nil\n  end\n\n  it \"should specify all possible properties when asked to audit all properties\" do\n    @resource[:audit] = :all\n\n    list = @resource.class.properties.collect { |p| p.name }\n    expect(@resource[:audit]).to eq(list)\n  end\n\n  it \"should accept the string 'all' to specify auditing all possible properties\" do\n    @resource[:audit] = 'all'\n\n    list = @resource.class.properties.collect { |p| p.name }\n    expect(@resource[:audit]).to eq(list)\n  end\n\n  it \"should fail if asked to audit an invalid property\" do\n    expect { @resource[:audit] = :foobar }.to raise_error(Puppet::Error)\n  end\n\n  it \"should create an attribute instance for each auditable property\" do\n    @resource[:audit] = :mode\n    expect(@resource.parameter(:mode)).not_to be_nil\n  end\n\n  it \"should accept properties specified as a string\" do\n    @resource[:audit] = \"mode\"\n    expect(@resource.parameter(:mode)).not_to be_nil\n  end\n\n  it \"should not create attribute instances for parameters, only properties\" do\n    @resource[:audit] = :noop\n    expect(@resource.parameter(:noop)).to be_nil\n  end\n\n  describe \"when generating the uniqueness key\" do\n    it \"should include all of the key_attributes in alphabetical order by attribute name\" do\n      allow(Puppet::Type.type(:file)).to receive(:key_attributes).and_return([:path, :mode, :owner])\n      allow(Puppet::Type.type(:file)).to receive(:title_patterns).and_return(\n        [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ]\n      )\n      myfile = make_absolute('/my/file')\n      res = Puppet::Type.type(:file).new( :title => myfile, :path => myfile, :owner => 'root', :content => 'hello' )\n      expect(res.uniqueness_key).to eq([ nil, 'root', myfile])\n    end\n  end\n\n  context \"type attribute bracket methods\" do\n    after :each do Puppet::Type.rmtype(:attributes)     end\n    let   :type do\n      Puppet::Type.newtype(:attributes) do\n        newparam(:name) {}\n      end\n    end\n\n    it \"should work with parameters\" do\n      type.newparam(:param) {}\n      instance = type.new(:name => 'test')\n\n      expect { instance[:param] = true }.to_not raise_error\n      expect { instance[\"param\"] = true }.to_not raise_error\n      expect(instance[:param]).to eq(true)\n      expect(instance[\"param\"]).to eq(true)\n    end\n\n    it \"should work with meta-parameters\" do\n      instance = type.new(:name => 'test')\n\n      expect { instance[:noop] = true }.to_not raise_error\n      expect { instance[\"noop\"] = true }.to_not raise_error\n      expect(instance[:noop]).to eq(true)\n      expect(instance[\"noop\"]).to eq(true)\n    end\n\n    it \"should work with properties\" do\n      type.newproperty(:property) {}\n      instance = type.new(:name => 'test')\n\n      expect { instance[:property] = true }.to_not raise_error\n      expect { instance[\"property\"] = true }.to_not raise_error\n      expect(instance.property(:property)).to be\n      expect(instance.should(:property)).to be_truthy\n    end\n\n    it \"should handle proprieties correctly\" do\n      # Order of assignment is significant in this test.\n      [:one, :two, :three].each {|prop| type.newproperty(prop) {} }\n      instance = type.new(:name => \"test\")\n\n      instance[:one] = \"boo\"\n      one = instance.property(:one)\n      expect(instance.properties).to eq [one]\n\n      instance[:three] = \"rah\"\n      three = instance.property(:three)\n      expect(instance.properties).to eq [one, three]\n\n      instance[:two] = \"whee\"\n      two = instance.property(:two)\n      expect(instance.properties).to eq [one, two, three]\n    end\n\n    it \"newattr should handle required features correctly\" do\n      Puppet::Util::Log.level = :debug\n\n      type.feature :feature1, \"one\"\n      type.feature :feature2, \"two\"\n\n      type.newproperty(:none) {}\n      type.newproperty(:one, :required_features => :feature1) {}\n      type.newproperty(:two, :required_features => [:feature1, :feature2]) {}\n\n      nope  = type.provide(:nope)  {}\n      maybe = type.provide(:maybe) { has_features :feature1 }\n      yep   = type.provide(:yep)   { has_features :feature1, :feature2 }\n\n      [nope, maybe, yep].each_with_index do |provider, i|\n        rsrc = type.new(:provider => provider.name, :name => \"test#{i}\",\n                        :none => \"a\", :one => \"b\", :two => \"c\")\n\n        expect(rsrc.should(:none)).to be\n\n        if provider.declared_feature? :feature1\n          expect(rsrc.should(:one)).to be\n        else\n          expect(rsrc.should(:one)).to_not be\n          expect(@logs.find {|l| l.message =~ /not managing attribute one/ }).to be\n        end\n\n        if provider.declared_feature? :feature2\n          expect(rsrc.should(:two)).to be\n        else\n          expect(rsrc.should(:two)).to_not be\n          expect(@logs.find {|l| l.message =~ /not managing attribute two/ }).to be\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/at_fork_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Puppet::Util::AtFork' do\n  EXPECTED_HANDLER_METHODS = [:prepare, :parent, :child]\n\n  before :each do\n    Puppet::Util.class_exec do\n      remove_const(:AtFork) if defined?(Puppet::Util::AtFork)\n      const_set(:AtFork, Module.new)\n    end\n  end\n\n  after :each do\n    Puppet::Util.class_exec do\n      remove_const(:AtFork)\n    end\n  end\n\n  describe '.get_handler' do\n    context 'when on Solaris' do\n      before :each do\n        expect(Puppet::Util::Platform).to receive(:solaris?).and_return(true)\n      end\n\n      after :each do\n        Object.class_exec do\n          remove_const(:Fiddle) if const_defined?(:Fiddle)\n        end\n      end\n\n      def stub_solaris_handler(stub_noop_too = false)\n        allow(Puppet::Util::AtFork).to receive(:require_relative).with(anything) do |lib|\n          if lib == 'at_fork/solaris'\n            load 'puppet/util/at_fork/solaris.rb'\n            true\n          elsif stub_noop_too && lib == 'at_fork/noop'\n            Puppet::Util::AtFork.class_exec do\n              const_set(:Noop, Class.new)\n            end\n            true\n          else\n            false\n          end\n        end.and_return(true)\n\n        unless stub_noop_too\n          Object.class_exec do\n            const_set(:Fiddle, Module.new do\n              const_set(:TYPE_VOIDP, nil)\n              const_set(:TYPE_VOID,  nil)\n              const_set(:TYPE_INT,   nil)\n              const_set(:DLError,    Class.new(StandardError))\n              const_set(:Handle,     Class.new { def initialize(library = nil, flags = 0); end })\n              const_set(:Function,   Class.new { def initialize(ptr, args, ret_type, abi = 0); end })\n            end)\n          end\n        end\n\n        allow(TOPLEVEL_BINDING.eval('self')).to receive(:require).with(anything) do |lib|\n          if lib == 'fiddle'\n            raise LoadError, 'no fiddle' if stub_noop_too\n          else\n            Kernel.require lib\n          end\n          true\n        end.and_return(true)\n      end\n\n      it %q(should return the Solaris specific AtFork handler) do\n        allow(Puppet::Util::AtFork).to receive(:require_relative).with(anything) do |lib|\n          if lib == 'at_fork/solaris'\n            Puppet::Util::AtFork.class_exec do\n              const_set(:Solaris, Class.new)\n            end\n            true\n          else\n            false\n          end\n        end.and_return(true)\n        load 'puppet/util/at_fork.rb'\n        expect(Puppet::Util::AtFork.get_handler.class).to eq(Puppet::Util::AtFork::Solaris)\n      end\n\n      it %q(should return the Noop handler when Fiddle could not be loaded) do\n        stub_solaris_handler(true)\n        load 'puppet/util/at_fork.rb'\n        expect(Puppet::Util::AtFork.get_handler.class).to eq(Puppet::Util::AtFork::Noop)\n      end\n\n      it %q(should fail when libcontract cannot be loaded) do\n        stub_solaris_handler\n        expect(Fiddle::Handle).to receive(:new).with(/^libcontract.so.*/).and_raise(Fiddle::DLError, 'no such library')\n        expect { load 'puppet/util/at_fork.rb' }.to raise_error(Fiddle::DLError, 'no such library')\n      end\n\n      it %q(should fail when libcontract doesn't define all the necessary functions) do\n        stub_solaris_handler\n        handle = double('Fiddle::Handle')\n        expect(Fiddle::Handle).to receive(:new).with(/^libcontract.so.*/).and_return(handle)\n        expect(handle).to receive(:[]).and_raise(Fiddle::DLError, 'no such method')\n        expect { load 'puppet/util/at_fork.rb' }.to raise_error(Fiddle::DLError, 'no such method')\n      end\n\n      it %q(the returned Solaris specific handler should respond to the expected methods) do\n        stub_solaris_handler\n        handle = double('Fiddle::Handle')\n        expect(Fiddle::Handle).to receive(:new).with(/^libcontract.so.*/).and_return(handle)\n        allow(handle).to receive(:[]).and_return(nil)\n        allow(Fiddle::Function).to receive(:new).and_return(Proc.new {})\n        load 'puppet/util/at_fork.rb'\n        expect(Puppet::Util::AtFork.get_handler.public_methods).to include(*EXPECTED_HANDLER_METHODS)\n      end\n    end\n\n    context 'when NOT on Solaris' do\n      before :each do\n        expect(Puppet::Util::Platform).to receive(:solaris?).and_return(false)\n      end\n\n      def stub_noop_handler(namespace_only = false)\n        allow(Puppet::Util::AtFork).to receive(:require_relative).with(anything) do |lib|\n          if lib == 'at_fork/noop'\n            if namespace_only\n              Puppet::Util::AtFork.class_exec do\n                const_set(:Noop, Class.new)\n              end\n            else\n              load 'puppet/util/at_fork/noop.rb'\n            end\n            true\n          else\n            false\n          end\n        end.and_return(true)\n      end\n\n      it %q(should return the Noop AtFork handler) do\n        stub_noop_handler(true)\n        load 'puppet/util/at_fork.rb'\n        expect(Puppet::Util::AtFork.get_handler.class).to eq(Puppet::Util::AtFork::Noop)\n      end\n\n      it %q(the returned Noop handler should respond to the expected methods) do\n        stub_noop_handler\n        load 'puppet/util/at_fork.rb'\n        expect(Puppet::Util::AtFork.get_handler.public_methods).to include(*EXPECTED_HANDLER_METHODS)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/autoload_spec.rb",
    "content": "require 'spec_helper'\nrequire 'fileutils'\n\nrequire 'puppet/util/autoload'\n\ndescribe Puppet::Util::Autoload do\n  include PuppetSpec::Files\n\n  let(:env) { Puppet::Node::Environment.create(:foo, []) }\n\n  before do\n    @autoload = Puppet::Util::Autoload.new(\"foo\", \"tmp\")\n\n    @loaded = {}\n    allow(@autoload.class).to receive(:loaded).and_return(@loaded)\n  end\n\n  describe \"when building the search path\" do\n    before :each do\n      ## modulepath/libdir can't be used until after app settings are initialized, so we need to simulate that:\n      allow(Puppet.settings).to receive(:app_defaults_initialized?).and_return(true)\n    end\n\n    def with_libdir(libdir)\n      begin\n        old_loadpath = $LOAD_PATH.dup\n        old_libdir = Puppet[:libdir]\n        Puppet[:libdir] = libdir\n        $LOAD_PATH.unshift(libdir)\n        yield\n      ensure\n        Puppet[:libdir] = old_libdir\n        $LOAD_PATH.clear\n        $LOAD_PATH.concat(old_loadpath)\n      end\n    end\n\n    it \"should collect all of the lib directories that exist in the current environment's module path\" do\n      dira = dir_containing('dir_a', {\n        \"one\" => {},\n        \"two\" => { \"lib\" => {} }\n      })\n\n      dirb = dir_containing('dir_a', {\n        \"one\" => {},\n        \"two\" => { \"lib\" => {} }\n      })\n\n      environment = Puppet::Node::Environment.create(:foo, [dira, dirb])\n\n      expect(@autoload.class.module_directories(environment)).to eq([\"#{dira}/two/lib\", \"#{dirb}/two/lib\"])\n    end\n\n    it \"ignores missing module directories\" do\n      environment = Puppet::Node::Environment.create(:foo, [File.expand_path('does/not/exist')])\n\n      expect(@autoload.class.module_directories(environment)).to be_empty\n    end\n\n    it \"ignores the configured environment when it doesn't exist\" do\n      Puppet[:environment] = 'nonexistent'\n\n      env = Puppet::Node::Environment.create(:dev, [])\n      Puppet.override(environments: Puppet::Environments::Static.new(env)) do\n        expect(@autoload.class.module_directories(Puppet.lookup(:current_environment))).to be_empty\n      end\n    end\n\n    it \"raises when no environment is given\" do\n      Puppet[:environment] = 'nonexistent'\n\n      Puppet.override(environments: Puppet::Environments::Static.new) do\n        expect {\n          @autoload.class.module_directories(nil)\n        }.to raise_error(ArgumentError, /Autoloader requires an environment/)\n      end\n    end\n\n    it \"should include the module directories, the Puppet libdir, Ruby load directories, and vendored modules\" do\n      vendor_dir = tmpdir('vendor_modules')\n      module_libdir = File.join(vendor_dir, 'amodule_core', 'lib')\n      FileUtils.mkdir_p(module_libdir)\n\n      libdir = File.expand_path('/libdir1')\n      Puppet[:vendormoduledir] = vendor_dir\n      Puppet.initialize_settings\n\n      with_libdir(libdir) do\n        expect(@autoload.class).to receive(:gem_directories).and_return(%w{/one /two})\n        expect(@autoload.class).to receive(:module_directories).and_return(%w{/three /four})\n        dirs = @autoload.class.search_directories(nil)\n        expect(dirs[0..4]).to eq(%w{/one /two /three /four} + [libdir])\n        expect(dirs.last).to eq(module_libdir)\n      end\n    end\n\n    it \"does not split the Puppet[:libdir]\" do\n      dir = File.expand_path(\"/libdir1#{File::PATH_SEPARATOR}/libdir2\")\n      with_libdir(dir) do\n        expect(@autoload.class).to receive(:gem_directories).and_return(%w{/one /two})\n        expect(@autoload.class).to receive(:module_directories).and_return(%w{/three /four})\n        dirs = @autoload.class.search_directories(nil)\n        expect(dirs).to include(dir)\n      end\n    end\n  end\n\n  describe \"when loading a file\" do\n    before do\n      allow(@autoload.class).to receive(:search_directories).and_return([make_absolute(\"/a\")])\n      allow(FileTest).to receive(:directory?).and_return(true)\n      @time_a = Time.utc(2010, 'jan', 1, 6, 30)\n      allow(File).to receive(:mtime).and_return(@time_a)\n    end\n\n    after(:each) do\n      $LOADED_FEATURES.delete(\"/a/tmp/myfile.rb\")\n    end\n\n    [RuntimeError, LoadError, SyntaxError].each do |error|\n      it \"should die with Puppet::Error if a #{error.to_s} exception is thrown\" do\n        allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n\n        expect(Kernel).to receive(:load).and_raise(error)\n\n        expect { @autoload.load(\"foo\", env) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    it \"should not raise an error if the file is missing\" do\n      expect(@autoload.load(\"foo\", env)).to eq(false)\n    end\n\n    it \"should register loaded files with the autoloader\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(Kernel).to receive(:load)\n      @autoload.load(\"myfile\", env)\n\n      expect(@autoload.class.loaded?(\"tmp/myfile.rb\")).to be\n    end\n\n    it \"should be seen by loaded? on the instance using the short name\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(Kernel).to receive(:load)\n      @autoload.load(\"myfile\", env)\n\n      expect(@autoload.loaded?(\"myfile.rb\")).to be\n    end\n\n    it \"should register loaded files with the main loaded file list so they are not reloaded by ruby\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(Kernel).to receive(:load)\n\n      @autoload.load(\"myfile\", env)\n\n      expect($LOADED_FEATURES).to be_include(make_absolute(\"/a/tmp/myfile.rb\"))\n    end\n\n    it \"should load the first file in the searchpath\" do\n      allow(@autoload.class).to receive(:search_directories).and_return([make_absolute(\"/a\"), make_absolute(\"/b\")])\n      allow(FileTest).to receive(:directory?).and_return(true)\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      expect(Kernel).to receive(:load).with(make_absolute(\"/a/tmp/myfile.rb\"), any_args)\n\n      @autoload.load(\"myfile\", env)\n    end\n\n    it \"should treat equivalent paths to a loaded file as loaded\" do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(Kernel).to receive(:load)\n      @autoload.load(\"myfile\", env)\n\n      expect(@autoload.class.loaded?(\"tmp/myfile\")).to be\n      expect(@autoload.class.loaded?(\"tmp/./myfile.rb\")).to be\n      expect(@autoload.class.loaded?(\"./tmp/myfile.rb\")).to be\n      expect(@autoload.class.loaded?(\"tmp/../tmp/myfile.rb\")).to be\n    end\n  end\n\n  describe \"when loading all files\" do\n    let(:basedir) { tmpdir('autoloader') }\n    let(:path) { File.join(basedir, @autoload.path, 'file.rb') }\n\n    before do\n      FileUtils.mkdir_p(File.dirname(path))\n      FileUtils.touch(path)\n\n      allow(@autoload.class).to receive(:search_directories).and_return([basedir])\n    end\n\n    [RuntimeError, LoadError, SyntaxError].each do |error|\n      it \"should die an if a #{error.to_s} exception is thrown\" do\n        expect(Kernel).to receive(:load).and_raise(error)\n\n        expect { @autoload.loadall(env) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    it \"should require the full path to the file\" do\n      expect(Kernel).to receive(:load).with(path, any_args)\n\n      @autoload.loadall(env)\n    end\n\n    it \"autoloads from a directory whose ancestor is Windows 8.3\", if: Puppet::Util::Platform.windows? do\n      pending(\"GH runners seem to have disabled 8.3 support\")\n\n      # File.expand_path will expand ~ in the last directory component only(!)\n      # so create an ancestor directory with a long path\n      dir = File.join(tmpdir('longpath'), 'short')\n      path = File.join(dir, @autoload.path, 'file.rb')\n\n      FileUtils.mkdir_p(File.dirname(path))\n      FileUtils.touch(path)\n\n      dir83 = File.join(File.dirname(basedir), 'longpa~1', 'short')\n      path83 = File.join(dir83, @autoload.path, 'file.rb')\n\n      allow(@autoload.class).to receive(:search_directories).and_return([dir83])\n      expect(Kernel).to receive(:load).with(path83, any_args)\n\n      @autoload.loadall(env)\n    end\n  end\n\n  describe \"when reloading files\" do\n    before :each do\n      @file_a = make_absolute(\"/a/file.rb\")\n      @file_b = make_absolute(\"/b/file.rb\")\n      @first_time = Time.utc(2010, 'jan', 1, 6, 30)\n      @second_time = @first_time + 60\n    end\n\n    after :each do\n      $LOADED_FEATURES.delete(\"a/file.rb\")\n      $LOADED_FEATURES.delete(\"b/file.rb\")\n    end\n\n    it \"#changed? should return true for a file that was not loaded\" do\n      expect(@autoload.class.changed?(@file_a, env)).to be\n    end\n\n    it \"changes should be seen by changed? on the instance using the short name\" do\n      allow(File).to receive(:mtime).and_return(@first_time)\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(Kernel).to receive(:load)\n      @autoload.load(\"myfile\", env)\n      expect(@autoload.loaded?(\"myfile\")).to be\n      expect(@autoload.changed?(\"myfile\", env)).not_to be\n\n      allow(File).to receive(:mtime).and_return(@second_time)\n      expect(@autoload.changed?(\"myfile\", env)).to be\n\n      $LOADED_FEATURES.delete(\"tmp/myfile.rb\")\n    end\n\n    describe \"in one directory\" do\n      before :each do\n        allow(@autoload.class).to receive(:search_directories).and_return([make_absolute(\"/a\")])\n        expect(File).to receive(:mtime).with(@file_a).and_return(@first_time)\n        @autoload.class.mark_loaded(\"file\", @file_a)\n      end\n\n      it \"should reload if mtime changes\" do\n        allow(File).to receive(:mtime).with(@file_a).and_return(@first_time + 60)\n        allow(Puppet::FileSystem).to receive(:exist?).with(@file_a).and_return(true)\n        expect(Kernel).to receive(:load).with(@file_a, any_args)\n        @autoload.class.reload_changed(env)\n      end\n\n      it \"should do nothing if the file is deleted\" do\n        allow(File).to receive(:mtime).with(@file_a).and_raise(Errno::ENOENT)\n        allow(Puppet::FileSystem).to receive(:exist?).with(@file_a).and_return(false)\n        expect(Kernel).not_to receive(:load)\n        @autoload.class.reload_changed(env)\n      end\n    end\n\n    describe \"in two directories\" do\n      before :each do\n        allow(@autoload.class).to receive(:search_directories).and_return([make_absolute(\"/a\"), make_absolute(\"/b\")])\n      end\n\n      it \"should load b/file when a/file is deleted\" do\n        expect(File).to receive(:mtime).with(@file_a).and_return(@first_time)\n        @autoload.class.mark_loaded(\"file\", @file_a)\n        allow(File).to receive(:mtime).with(@file_a).and_raise(Errno::ENOENT)\n        allow(Puppet::FileSystem).to receive(:exist?).with(@file_a).and_return(false)\n        allow(Puppet::FileSystem).to receive(:exist?).with(@file_b).and_return(true)\n        allow(File).to receive(:mtime).with(@file_b).and_return(@first_time)\n        expect(Kernel).to receive(:load).with(@file_b, any_args)\n        @autoload.class.reload_changed(env)\n        expect(@autoload.class.send(:loaded)[\"file\"]).to eq([@file_b, @first_time])\n      end\n\n      it \"should load a/file when b/file is loaded and a/file is created\" do\n        allow(File).to receive(:mtime).with(@file_b).and_return(@first_time)\n        allow(Puppet::FileSystem).to receive(:exist?).with(@file_b).and_return(true)\n        @autoload.class.mark_loaded(\"file\", @file_b)\n\n        allow(File).to receive(:mtime).with(@file_a).and_return(@first_time)\n        allow(Puppet::FileSystem).to receive(:exist?).with(@file_a).and_return(true)\n        expect(Kernel).to receive(:load).with(@file_a, any_args)\n        @autoload.class.reload_changed(env)\n        expect(@autoload.class.send(:loaded)[\"file\"]).to eq([@file_a, @first_time])\n      end\n    end\n  end\n\n  describe \"#cleanpath\" do\n    it \"should leave relative paths relative\" do\n      path = \"hello/there\"\n      expect(Puppet::Util::Autoload.cleanpath(path)).to eq(path)\n    end\n\n    describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should convert c:\\ to c:/\" do\n        expect(Puppet::Util::Autoload.cleanpath('c:\\\\')).to eq('c:/')\n      end\n\n      it \"should convert all backslashes to forward slashes\" do\n        expect(Puppet::Util::Autoload.cleanpath('c:\\projects\\ruby\\bug\\test.rb')).to eq('c:/projects/ruby/bug/test.rb')\n      end\n    end\n  end\n\n  describe \"#expand\" do\n    it \"should expand relative to the autoloader's prefix\" do\n      expect(@autoload.expand('bar')).to eq('tmp/bar')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/backups_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/backups'\n\ndescribe Puppet::Util::Backups do\n  include PuppetSpec::Files\n\n  let(:bucket) { double('bucket', :name => \"foo\") }\n  let!(:file) do\n    f = Puppet::Type.type(:file).new(:name => path, :backup => 'foo')\n    allow(f).to receive(:bucket).and_return(bucket)\n    f\n  end\n\n  describe \"when backing up a file\" do\n    let(:path) { make_absolute('/no/such/file') }\n\n    it \"should noop if the file does not exist\" do\n      expect(file).not_to receive(:bucket)\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(false)\n\n      file.perform_backup\n    end\n\n    it \"should succeed silently if self[:backup] is false\" do\n      file = Puppet::Type.type(:file).new(:name => path, :backup => false)\n\n      expect(file).not_to receive(:bucket)\n      expect(Puppet::FileSystem).not_to receive(:exist?)\n\n      file.perform_backup\n    end\n\n    it \"a bucket should be used when provided\" do\n      lstat_path_as(path, 'file')\n      expect(bucket).to receive(:backup).with(path).and_return(\"mysum\")\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n      file.perform_backup\n    end\n\n    it \"should propagate any exceptions encountered when backing up to a filebucket\" do\n      lstat_path_as(path, 'file')\n      expect(bucket).to receive(:backup).and_raise(ArgumentError)\n      expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n      expect { file.perform_backup }.to raise_error(ArgumentError)\n    end\n\n    describe \"and local backup is configured\" do\n      let(:ext) { 'foobkp' }\n      let(:backup) { path + '.' + ext }\n      let(:file) { Puppet::Type.type(:file).new(:name => path, :backup => '.'+ext) }\n\n      it \"should remove any local backup if one exists\" do\n        lstat_path_as(backup, 'file')\n        expect(Puppet::FileSystem).to receive(:unlink).with(backup)\n        allow(FileUtils).to receive(:cp_r)\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n        file.perform_backup\n      end\n\n      it \"should fail when the old backup can't be removed\" do\n        lstat_path_as(backup, 'file')\n        expect(Puppet::FileSystem).to receive(:unlink).with(backup).and_raise(ArgumentError)\n        expect(FileUtils).not_to receive(:cp_r)\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n        expect { file.perform_backup }.to raise_error(Puppet::Error)\n      end\n\n      it \"should not try to remove backups that don't exist\" do\n        expect(Puppet::FileSystem).to receive(:lstat).with(backup).and_raise(Errno::ENOENT)\n        expect(Puppet::FileSystem).not_to receive(:unlink).with(backup)\n        allow(FileUtils).to receive(:cp_r)\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n        file.perform_backup\n      end\n\n      it \"a copy should be created in the local directory\" do\n        expect(FileUtils).to receive(:cp_r).with(path, backup, {:preserve => true})\n        allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n        expect(file.perform_backup).to be_truthy\n      end\n\n      it \"should propagate exceptions if no backup can be created\" do\n        expect(FileUtils).to receive(:cp_r).and_raise(ArgumentError)\n\n        allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n        expect { file.perform_backup }.to raise_error(Puppet::Error)\n      end\n    end\n  end\n\n  describe \"when backing up a directory\" do\n    let(:path) { make_absolute('/my/dir') }\n    let(:filename) { File.join(path, 'file') }\n\n    it \"a bucket should work when provided\" do\n      allow(File).to receive(:file?).with(filename).and_return(true)\n      expect(Find).to receive(:find).with(path).and_yield(filename)\n\n      expect(bucket).to receive(:backup).with(filename).and_return(true)\n\n      lstat_path_as(path, 'directory')\n\n      allow(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n      allow(Puppet::FileSystem).to receive(:exist?).with(filename).and_return(true)\n\n      file.perform_backup\n    end\n\n    it \"should do nothing when recursing\" do\n      file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true)\n\n      expect(bucket).not_to receive(:backup)\n      allow(Puppet::FileSystem).to receive(:stat).with(path).and_return(double('stat', :ftype => 'directory'))\n      expect(Find).not_to receive(:find)\n\n      file.perform_backup\n    end\n  end\n\n  def lstat_path_as(path, ftype)\n    expect(Puppet::FileSystem).to receive(:lstat).with(path).and_return(double('File::Stat', :ftype => ftype))\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/character_encoding_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/character_encoding'\nrequire 'puppet_spec/character_encoding'\n\ndescribe Puppet::Util::CharacterEncoding do\n  describe \"::convert_to_utf_8\" do\n    context \"when passed a string that is already UTF-8\" do\n      context \"with valid encoding\" do\n        let(:utf8_string) { \"\\u06FF\\u2603\".force_encoding(Encoding::UTF_8) }\n\n        it \"should return the string unmodified\" do\n          expect(Puppet::Util::CharacterEncoding.convert_to_utf_8(utf8_string)).to eq(\"\\u06FF\\u2603\".force_encoding(Encoding::UTF_8))\n        end\n\n        it \"should not mutate the original string\" do\n          expect(utf8_string).to eq(\"\\u06FF\\u2603\".force_encoding(Encoding::UTF_8))\n        end\n      end\n\n      context \"with invalid encoding\" do\n        let(:invalid_utf8_string) { \"\\xfd\\xf1\".force_encoding(Encoding::UTF_8) }\n\n        it \"should issue a debug message\" do\n          expect(Puppet).to receive(:debug) { |&b| expect(b.call).to match(/encoding is invalid/) }\n          Puppet::Util::CharacterEncoding.convert_to_utf_8(invalid_utf8_string)\n        end\n\n        it \"should return the string unmodified\" do\n          expect(Puppet::Util::CharacterEncoding.convert_to_utf_8(invalid_utf8_string)).to eq(\"\\xfd\\xf1\".force_encoding(Encoding::UTF_8))\n        end\n\n        it \"should not mutate the original string\" do\n          Puppet::Util::CharacterEncoding.convert_to_utf_8(invalid_utf8_string)\n          expect(invalid_utf8_string).to eq(\"\\xfd\\xf1\".force_encoding(Encoding::UTF_8))\n        end\n      end\n    end\n\n    context \"when passed a string in BINARY encoding\" do\n      context \"that is valid in Encoding.default_external\" do\n        # When received as BINARY are not transcodable, but by \"guessing\"\n        # Encoding.default_external can transcode to UTF-8\n        let(:win_31j) { [130, 187].pack('C*') } # pack('C*') returns string in BINARY\n\n        it \"should be able to convert to UTF-8 by labeling as Encoding.default_external\" do\n          # そ - HIRAGANA LETTER SO\n          # In Windows_31J: \\x82 \\xbb - 130 187\n          # In Unicode: \\u305d - \\xe3 \\x81 \\x9d - 227 129 157\n          result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::Windows_31J) do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(win_31j)\n          end\n          expect(result).to eq(\"\\u305d\")\n          expect(result.bytes.to_a).to eq([227, 129, 157])\n        end\n\n        it \"should not mutate the original string\" do\n          PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::Windows_31J) do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(win_31j)\n          end\n          expect(win_31j).to eq([130, 187].pack('C*'))\n        end\n      end\n\n      context \"that is invalid in Encoding.default_external\" do\n        let(:invalid_win_31j) { [255, 254, 253].pack('C*') } # these bytes are not valid windows_31j\n\n        it \"should return the string umodified\" do\n          result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::Windows_31J) do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(invalid_win_31j)\n          end\n          expect(result.bytes.to_a).to eq([255, 254, 253])\n          expect(result.encoding).to eq(Encoding::BINARY)\n        end\n\n        it \"should not mutate the original string\" do\n          PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::Windows_31J) do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(invalid_win_31j)\n          end\n          expect(invalid_win_31j).to eq([255, 254, 253].pack('C*'))\n        end\n\n        it \"should issue a debug message that the string was not transcodable\" do\n          expect(Puppet).to receive(:debug) { |&b| expect(b.call).to match(/cannot be transcoded/) }\n          PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::Windows_31J) do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(invalid_win_31j)\n          end\n        end\n      end\n\n      context \"Given a string labeled as neither UTF-8 nor BINARY\" do\n        context \"that is transcodable\" do\n          let (:shift_jis) { [130, 174].pack('C*').force_encoding(Encoding::Shift_JIS) }\n\n          it \"should return a copy of the string transcoded to UTF-8 if it is transcodable\" do\n            # http://www.fileformat.info/info/unicode/char/3050/index.htm\n            # ぐ - HIRAGANA LETTER GU\n            # In Shift_JIS: \\x82 \\xae - 130 174\n            # In Unicode: \\u3050 - \\xe3 \\x81 \\x90 - 227 129 144\n            # if we were only ruby > 2.3.0, we could do String.new(\"\\x82\\xae\", :encoding => Encoding::Shift_JIS)\n\n            result = Puppet::Util::CharacterEncoding.convert_to_utf_8(shift_jis)\n            expect(result).to eq(\"\\u3050\".force_encoding(Encoding::UTF_8))\n            # largely redundant but reinforces the point - this was transcoded:\n            expect(result.bytes.to_a).to eq([227, 129, 144])\n          end\n\n          it \"should not mutate the original string\" do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(shift_jis)\n            expect(shift_jis).to eq([130, 174].pack('C*').force_encoding(Encoding::Shift_JIS))\n          end\n        end\n\n        context \"when not transcodable\" do\n          # An admittedly contrived case, but perhaps not so improbable\n          # http://www.fileformat.info/info/unicode/char/5e0c/index.htm\n          # 希 Han Character 'rare; hope, expect, strive for'\n          # In EUC_KR: \\xfd \\xf1 - 253 241\n          # In Unicode: \\u5e0c - \\xe5 \\xb8 \\x8c - 229 184 140\n\n          # In this case, this EUC_KR character has been read in as ASCII and is\n          # invalid in that encoding. This would raise an EncodingError\n          # exception on transcode but we catch this issue a debug message -\n          # leaving the original string unaltered.\n          let(:euc_kr) { [253, 241].pack('C*').force_encoding(Encoding::ASCII) }\n\n          it \"should issue a debug message\" do\n            expect(Puppet).to receive(:debug) { |&b| expect(b.call).to match(/cannot be transcoded/) }\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(euc_kr)\n          end\n\n          it \"should return the original string unmodified\" do\n            result = Puppet::Util::CharacterEncoding.convert_to_utf_8(euc_kr)\n            expect(result).to eq([253, 241].pack('C*').force_encoding(Encoding::ASCII))\n          end\n\n          it \"should not mutate the original string\" do\n            Puppet::Util::CharacterEncoding.convert_to_utf_8(euc_kr)\n            expect(euc_kr).to eq([253, 241].pack('C*').force_encoding(Encoding::ASCII))\n          end\n        end\n      end\n    end\n  end\n\n  describe \"::override_encoding_to_utf_8\" do\n    context \"given a string with bytes that represent valid UTF-8\" do\n      # ☃ - unicode snowman\n      # \\u2603 - \\xe2 \\x98 \\x83 - 226 152 131\n      let(:snowman) { [226, 152, 131].pack('C*') }\n\n      it \"should return a copy of the string with external encoding of the string to UTF-8\" do\n        result = Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(snowman)\n        expect(result).to eq(\"\\u2603\")\n        expect(result.encoding).to eq(Encoding::UTF_8)\n      end\n\n      it \"should not modify the original string\" do\n        Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(snowman)\n        expect(snowman).to eq([226, 152, 131].pack('C*'))\n      end\n    end\n\n    context \"given a string with bytes that do not represent valid UTF-8\" do\n      # Ø - Latin capital letter O with stroke\n      # In ISO-8859-1: \\xd8 - 216\n      # Invalid in UTF-8 without transcoding\n      let(:oslash) { [216].pack('C*').force_encoding(Encoding::ISO_8859_1) }\n      let(:foo) { 'foo' }\n\n      it \"should issue a debug message\" do\n        expect(Puppet).to receive(:debug) { |&b| expect(b.call).to match(/not valid UTF-8/) }\n        Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(oslash)\n      end\n\n      it \"should return the original string unmodified\" do\n        result = Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(oslash)\n        expect(result).to eq([216].pack('C*').force_encoding(Encoding::ISO_8859_1))\n      end\n\n      it \"should not modify the string\" do\n        Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(oslash)\n        expect(oslash).to eq([216].pack('C*').force_encoding(Encoding::ISO_8859_1))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/checksums_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/checksums'\n\ndescribe Puppet::Util::Checksums do\n  include PuppetSpec::Files\n\n  before do\n    @summer = Puppet::Util::Checksums\n  end\n\n  content_sums = [:md5, :md5lite, :sha1, :sha1lite, :sha256, :sha256lite, :sha512, :sha384, :sha224]\n  file_only = [:ctime, :mtime, :none]\n\n  content_sums.each do |sumtype|\n    it \"should be able to calculate #{sumtype} sums from strings\" do\n      expect(@summer).to be_respond_to(sumtype)\n    end\n  end\n\n  content_sums.each do |sumtype|\n    it \"should know the expected length of #{sumtype} sums\" do\n      expect(@summer).to be_respond_to(sumtype.to_s + \"_hex_length\")\n    end\n  end\n\n  [content_sums, file_only].flatten.each do |sumtype|\n    it \"should be able to calculate #{sumtype} sums from files\" do\n      expect(@summer).to be_respond_to(sumtype.to_s + \"_file\")\n    end\n  end\n\n  [content_sums, file_only].flatten.each do |sumtype|\n    it \"should be able to calculate #{sumtype} sums from stream\" do\n      expect(@summer).to be_respond_to(sumtype.to_s + \"_stream\")\n    end\n  end\n\n  it \"should have a method for determining whether a given string is a checksum\" do\n    expect(@summer).to respond_to(:checksum?)\n  end\n\n  %w{{md5}asdfasdf {sha1}asdfasdf {ctime}asdasdf {mtime}asdfasdf \n     {sha256}asdfasdf {sha256lite}asdfasdf {sha512}asdfasdf {sha384}asdfasdf {sha224}asdfasdf}.each do |sum|\n    it \"should consider #{sum} to be a checksum\" do\n      expect(@summer).to be_checksum(sum)\n    end\n  end\n\n  %w{{nosuchsumthislong}asdfasdf {a}asdfasdf {ctime}}.each do |sum|\n    it \"should not consider #{sum} to be a checksum\" do\n      expect(@summer).not_to be_checksum(sum)\n    end\n  end\n\n  it \"should have a method for stripping a sum type from an existing checksum\" do\n    expect(@summer.sumtype(\"{md5}asdfasdfa\")).to eq(\"md5\")\n  end\n\n  it \"should have a method for stripping the data from a checksum\" do\n    expect(@summer.sumdata(\"{md5}asdfasdfa\")).to eq(\"asdfasdfa\")\n  end\n\n  it \"should return a nil sumtype if the checksum does not mention a checksum type\" do\n    expect(@summer.sumtype(\"asdfasdfa\")).to be_nil\n  end\n\n  it \"has a list of known checksum types\" do\n    expect(@summer.known_checksum_types).to match_array(content_sums + file_only)\n  end\n\n  it \"returns true if the checksum is valid\" do\n    expect(@summer).to be_valid_checksum('sha1', 'fcc1715b22278a9dae322b0a34935f10d1608b9f')\n  end\n\n  it \"returns false if the checksum is known but invalid\" do\n    expect(@summer).to_not be_valid_checksum('sha1', 'wronglength')\n  end\n\n  it \"returns false if the checksum type is unknown\" do\n    expect(@summer).to_not be_valid_checksum('rot13', 'doesntmatter')\n  end\n\n  {:md5 => Digest::MD5, :sha1 => Digest::SHA1, :sha256 => Digest::SHA256, :sha512 => Digest::SHA512, :sha384 => Digest::SHA384}.each do |sum, klass|\n    describe(\"when using #{sum}\") do\n      it \"should use #{klass} to calculate string checksums\" do\n        expect(klass).to receive(:hexdigest).with(\"mycontent\").and_return(\"whatever\")\n        expect(@summer.send(sum, \"mycontent\")).to eq(\"whatever\")\n      end\n\n      it \"should use incremental #{klass} sums to calculate file checksums\" do\n        digest = double('digest')\n        expect(klass).to receive(:new).and_return(digest)\n\n        file = \"/path/to/my/file\"\n\n        fh = double('filehandle')\n        expect(fh).to receive(:read).with(4096).exactly(3).times().and_return(\"firstline\", \"secondline\", nil)\n\n        expect(File).to receive(:open).with(file, \"rb\").and_yield(fh)\n\n        expect(digest).to receive(:<<).with(\"firstline\")\n        expect(digest).to receive(:<<).with(\"secondline\")\n        expect(digest).to receive(:hexdigest).and_return(:mydigest)\n\n        expect(@summer.send(sum.to_s + \"_file\", file)).to eq(:mydigest)\n      end\n\n      it \"should behave like #{klass} to calculate stream checksums\" do\n        digest = double('digest')\n        expect(klass).to receive(:new).and_return(digest)\n        expect(digest).to receive(:<<).with \"firstline\"\n        expect(digest).to receive(:<<).with \"secondline\"\n        expect(digest).to receive(:hexdigest).and_return(:mydigest)\n\n        expect(@summer.send(sum.to_s + \"_stream\") do |checksum|\n          checksum << \"firstline\"\n          checksum << \"secondline\"\n        end).to eq(:mydigest)\n      end\n    end\n  end\n\n  {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1, :sha256lite => Digest::SHA256}.each do |sum, klass|\n    describe(\"when using #{sum}\") do\n      it \"should use #{klass} to calculate string checksums from the first 512 characters of the string\" do\n        content = \"this is a test\" * 100\n        expect(klass).to receive(:hexdigest).with(content[0..511]).and_return(\"whatever\")\n        expect(@summer.send(sum, content)).to eq(\"whatever\")\n      end\n\n      it \"should use #{klass} to calculate a sum from the first 512 characters in the file\" do\n        digest = double('digest')\n        expect(klass).to receive(:new).and_return(digest)\n\n        file = \"/path/to/my/file\"\n\n        fh = double('filehandle')\n        expect(fh).to receive(:read).with(512).and_return('my content')\n\n        expect(File).to receive(:open).with(file, \"rb\").and_yield(fh)\n\n        expect(digest).to receive(:<<).with(\"my content\")\n        expect(digest).to receive(:hexdigest).and_return(:mydigest)\n\n        expect(@summer.send(sum.to_s + \"_file\", file)).to eq(:mydigest)\n      end\n\n      it \"should use #{klass} to calculate a sum from the first 512 characters in a stream\" do\n        digest = double('digest')\n        content = \"this is a test\" * 100\n        expect(klass).to receive(:new).and_return(digest)\n        expect(digest).to receive(:<<).with(content[0..511])\n        expect(digest).to receive(:hexdigest).and_return(:mydigest)\n\n        expect(@summer.send(sum.to_s + \"_stream\") do |checksum|\n          checksum << content\n        end).to eq(:mydigest)\n      end\n\n      it \"should use #{klass} to calculate a sum from the first 512 characters in a multi-part stream\" do\n        digest = double('digest')\n        content = \"this is a test\" * 100\n        expect(klass).to receive(:new).and_return(digest)\n        expect(digest).to receive(:<<).with(content[0..5])\n        expect(digest).to receive(:<<).with(content[6..510])\n        expect(digest).to receive(:<<).with(content[511..511])\n        expect(digest).to receive(:hexdigest).and_return(:mydigest)\n\n        expect(@summer.send(sum.to_s + \"_stream\") do |checksum|\n          checksum << content[0..5]\n          checksum << content[6..510]\n          checksum << content[511..-1]\n        end).to eq(:mydigest)\n      end\n    end\n  end\n\n  [:ctime, :mtime].each do |sum|\n    describe(\"when using #{sum}\") do\n      it \"should use the '#{sum}' on the file to determine the ctime\" do\n        file = \"/my/file\"\n        stat = double('stat', sum => \"mysum\")\n        expect(Puppet::FileSystem).to receive(:stat).with(file).and_return(stat)\n\n        expect(@summer.send(sum.to_s + \"_file\", file)).to eq(\"mysum\")\n      end\n\n      it \"should return nil for streams\" do\n        expectation = double(\"expectation\")\n        expect(expectation).to receive(:do_something!).at_least(:once)\n        expect(@summer.send(sum.to_s + \"_stream\"){ |checksum| checksum << \"anything\" ; expectation.do_something!  }).to be_nil\n      end\n    end\n  end\n\n  describe \"when using the none checksum\" do\n    it \"should return an empty string\" do\n      expect(@summer.none_file(\"/my/file\")).to eq(\"\")\n    end\n\n    it \"should return an empty string for streams\" do\n      expectation = double(\"expectation\")\n      expect(expectation).to receive(:do_something!).at_least(:once)\n      expect(@summer.none_stream{ |checksum| checksum << \"anything\" ; expectation.do_something!  }).to eq(\"\")\n    end\n  end\n\n  {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass|\n    describe \"when using #{sum}\" do\n      let(:content) { \"hello\\r\\nworld\" }\n      let(:path) do\n        path = tmpfile(\"checksum_#{sum}\")\n        File.open(path, 'wb') {|f| f.write(content)}\n        path\n      end\n\n      it \"should preserve nl/cr sequences\" do\n        expect(@summer.send(sum.to_s + \"_file\", path)).to eq(klass.hexdigest(content))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/colors_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::Colors do\n  include Puppet::Util::Colors\n\n  let (:message) { 'a message' }\n  let (:color) { :black }\n  let (:subject) { self }\n\n  describe \".console_color\" do\n    it { is_expected.to respond_to :console_color }\n\n    it \"should generate ANSI escape sequences\" do\n      expect(subject.console_color(color, message)).to eq(\"\\e[0;30m#{message}\\e[0m\")\n    end\n  end\n\n  describe \".html_color\" do\n    it { is_expected.to respond_to :html_color }\n\n    it \"should generate an HTML span element and style attribute\" do\n      expect(subject.html_color(color, message)).to match(/<span style=\\\"color: #FFA0A0\\\">#{message}<\\/span>/)\n    end\n  end\n\n  describe \".colorize\" do\n    it { is_expected.to respond_to :colorize }\n\n    context \"ansicolor supported\" do\n      it \"should colorize console output\" do\n        Puppet[:color] = true\n\n        expect(subject).to receive(:console_color).with(color, message)\n        subject.colorize(:black, message)\n      end\n\n      it \"should not colorize unknown color schemes\" do\n        Puppet[:color] = :thisisanunknownscheme\n\n        expect(subject.colorize(:black, message)).to eq(message)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/command_line_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/face'\nrequire 'puppet/util/command_line'\n\ndescribe Puppet::Util::CommandLine do\n  include PuppetSpec::Files\n\n  context \"#initialize\" do\n    it \"should pull off the first argument if it looks like a subcommand\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", %w{ client --help whatever.pp })\n\n      expect(command_line.subcommand_name).to eq(\"client\")\n      expect(command_line.args).to            eq(%w{ --help whatever.pp })\n    end\n\n    it \"should return nil if the first argument looks like a .pp file\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", %w{ whatever.pp })\n\n      expect(command_line.subcommand_name).to eq(nil)\n      expect(command_line.args).to            eq(%w{ whatever.pp })\n    end\n\n    it \"should return nil if the first argument looks like a flag\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", %w{ --debug })\n\n      expect(command_line.subcommand_name).to eq(nil)\n      expect(command_line.args).to            eq(%w{ --debug })\n    end\n\n    it \"should return nil if the first argument is -\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", %w{ - })\n\n      expect(command_line.subcommand_name).to eq(nil)\n      expect(command_line.args).to            eq(%w{ - })\n    end\n\n    it \"should return nil if the first argument is --help\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", %w{ --help })\n\n      expect(command_line.subcommand_name).to eq(nil)\n    end\n\n\n    it \"should return nil if there are no arguments\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", [])\n\n      expect(command_line.subcommand_name).to eq(nil)\n      expect(command_line.args).to            eq([])\n    end\n\n    it \"should pick up changes to the array of arguments\" do\n      args = %w{subcommand}\n      command_line = Puppet::Util::CommandLine.new(\"puppet\", args)\n      args[0] = 'different_subcommand'\n      expect(command_line.subcommand_name).to eq('different_subcommand')\n    end\n  end\n\n  context \"#execute\" do\n    %w{--version -V}.each do |arg|\n      it \"should print the version and exit if #{arg} is given\" do\n        expect do\n          described_class.new(\"puppet\", [arg]).execute\n        end.to output(/^#{Regexp.escape(Puppet.version)}$/).to_stdout\n      end\n    end\n\n    %w{--help -h help}.each do|arg|\n      it \"should print help and exit if #{arg} is given\" do\n        commandline = Puppet::Util::CommandLine.new(\"puppet\", [arg])\n        expect(commandline).not_to receive(:exec)\n\n        expect {\n          commandline.execute\n        }.to exit_with(0)\n         .and output(/Usage: puppet <subcommand> \\[options\\] <action> \\[options\\]/).to_stdout\n      end\n    end\n\n    it \"should fail if the config file isn't readable and we're running a subcommand that requires a readable config file\" do\n      allow(Puppet::FileSystem).to receive(:exist?).with(Puppet[:config]).and_return(true)\n      allow_any_instance_of(Puppet::Settings).to receive(:read_file).and_return('')\n      expect_any_instance_of(Puppet::Settings).to receive(:read_file).with(Puppet[:config]).and_raise('Permission denied')\n\n      expect{ described_class.new(\"puppet\", ['config']).execute }.to raise_error(SystemExit)\n    end\n\n    it \"should not fail if the config file isn't readable and we're running a subcommand that does not require a readable config file\" do\n      allow(Puppet::FileSystem).to receive(:exist?)\n      allow(Puppet::FileSystem).to receive(:exist?).with(Puppet[:config]).and_return(true)\n      allow_any_instance_of(Puppet::Settings).to receive(:read_file).and_return('')\n      expect_any_instance_of(Puppet::Settings).to receive(:read_file).with(Puppet[:config]).and_raise('Permission denied')\n\n      commandline = described_class.new(\"puppet\", ['help'])\n\n      expect {\n        commandline.execute\n      }.to exit_with(0)\n       .and output(/Usage: puppet <subcommand> \\[options\\] <action> \\[options\\]/).to_stdout\n    end\n  end\n\n  describe \"when dealing with puppet commands\" do\n    it \"should return the executable name if it is not puppet\" do\n      command_line = Puppet::Util::CommandLine.new(\"puppetmasterd\", [])\n      expect(command_line.subcommand_name).to eq(\"puppetmasterd\")\n    end\n\n    describe \"when the subcommand is not implemented\" do\n      it \"should find and invoke an executable with a hyphenated name\" do\n        commandline = Puppet::Util::CommandLine.new(\"puppet\", ['whatever', 'argument'])\n        expect(Puppet::Util).to receive(:which).with('puppet-whatever').\n          and_return('/dev/null/puppet-whatever')\n\n        expect(Kernel).to receive(:exec).with('/dev/null/puppet-whatever', 'argument')\n\n        commandline.execute\n      end\n\n      describe \"and an external implementation cannot be found\" do\n        it \"should abort and show the usage message\" do\n          expect(Puppet::Util).to receive(:which).with('puppet-whatever').and_return(nil)\n          commandline = Puppet::Util::CommandLine.new(\"puppet\", ['whatever', 'argument'])\n          expect(commandline).not_to receive(:exec)\n\n          expect {\n            commandline.execute\n          }.to exit_with(1)\n           .and output(/Unknown Puppet subcommand 'whatever'/).to_stdout\n        end\n\n        it \"should abort and show the help message\" do\n          expect(Puppet::Util).to receive(:which).with('puppet-whatever').and_return(nil)\n          commandline = Puppet::Util::CommandLine.new(\"puppet\", ['whatever', 'argument'])\n          expect(commandline).not_to receive(:exec)\n\n          expect {\n            commandline.execute\n          }.to exit_with(1)\n           .and output(/See 'puppet help' for help on available puppet subcommands/).to_stdout\n        end\n\n        %w{--version -V}.each do |arg|\n          it \"should abort and display #{arg} information\" do\n            expect(Puppet::Util).to receive(:which).with('puppet-whatever').and_return(nil)\n            commandline = Puppet::Util::CommandLine.new(\"puppet\", ['whatever', arg])\n            expect(commandline).not_to receive(:exec)\n\n            expect {\n              commandline.execute\n            }.to exit_with(1)\n             .and output(%r[^#{Regexp.escape(Puppet.version)}$]).to_stdout\n          end\n        end\n      end\n    end\n\n    describe 'when setting process priority' do\n      let(:command_line) do\n        Puppet::Util::CommandLine.new(\"puppet\", %w{ agent })\n      end\n\n      before :each do\n        allow_any_instance_of(Puppet::Util::CommandLine::ApplicationSubcommand).to receive(:run)\n      end\n\n      it 'should never set priority by default' do\n        expect(Process).not_to receive(:setpriority)\n\n        command_line.execute\n      end\n\n      it 'should lower the process priority if one has been specified' do\n        Puppet[:priority] = 10\n\n        expect(Process).to receive(:setpriority).with(0, Process.pid, 10)\n        command_line.execute\n      end\n\n      it 'should warn if trying to raise priority, but not privileged user' do\n        Puppet[:priority] = -10\n\n        expect(Process).to receive(:setpriority).and_raise(Errno::EACCES, 'Permission denied')\n        expect(Puppet).to receive(:warning).with(\"Failed to set process priority to '-10'\")\n\n        command_line.execute\n      end\n\n      it \"should warn if the platform doesn't support `Process.setpriority`\" do\n        Puppet[:priority] = 15\n\n        expect(Process).to receive(:setpriority).and_raise(NotImplementedError, 'NotImplementedError: setpriority() function is unimplemented on this machine')\n        expect(Puppet).to receive(:warning).with(\"Failed to set process priority to '15'\")\n\n        command_line.execute\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/command_line_utils/puppet_option_parser_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/command_line/puppet_option_parser'\n\ndescribe Puppet::Util::CommandLine::PuppetOptionParser do\n  let(:option_parser) { described_class.new }\n\n  describe \"an option with a value\" do\n    it \"parses a 'long' option with a value\" do\n      parses(\n        :option => [\"--angry\", \"Angry\", :REQUIRED],\n        :from_arguments => [\"--angry\", \"foo\"],\n        :expects => \"foo\"\n      )\n      expect(@logs).to be_empty\n    end\n\n    it \"parses a 'long' option with a value and converts '-' to '_' & warns\" do\n      parses(\n        :option => [\"--an_gry\", \"Angry\", :REQUIRED],\n        :from_arguments => [\"--an-gry\", \"foo\"],\n        :expects => \"foo\"\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --an_gry, got --an-gry. Partial argument matching is deprecated and will be removed in a future release./)\n    end\n\n    it \"parses a 'long' option with a value and converts '_' to '-' & warns\" do\n      parses(\n        :option => [\"--an-gry\", \"Angry\", :REQUIRED],\n        :from_arguments => [\"--an_gry\", \"foo\"],\n        :expects => \"foo\"\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --an-gry, got --an_gry. Partial argument matching is deprecated and will be removed in a future release./)\n    end\n\n    it \"parses a 'short' option with a value\" do\n      parses(\n        :option => [\"--angry\", \"-a\", \"Angry\", :REQUIRED],\n        :from_arguments => [\"-a\", \"foo\"],\n        :expects => \"foo\"\n      )\n      expect(@logs).to be_empty\n    end\n\n    it \"overrides a previous argument with a later one\" do\n      parses(\n        :option => [\"--later\", \"Later\", :REQUIRED],\n        :from_arguments => [\"--later\", \"tomorrow\", \"--later\", \"morgen\"],\n        :expects => \"morgen\"\n      )\n      expect(@logs).to be_empty\n    end\n  end\n\n  describe \"an option without a value\" do\n    it \"parses a 'long' option\" do\n      parses(\n        :option => [\"--angry\", \"Angry\", :NONE],\n        :from_arguments => [\"--angry\"],\n        :expects => true\n      )\n    end\n\n    it \"converts '_' to '-' with a 'long' option & warns\" do\n      parses(\n        :option => [\"--an-gry\", \"Angry\", :NONE],\n        :from_arguments => [\"--an_gry\"],\n        :expects => true\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --an-gry, got --an_gry. Partial argument matching is deprecated and will be removed in a future release./)\n    end\n\n    it \"converts '-' to '_' with a 'long' option & warns\" do\n      parses(\n        :option => [\"--an_gry\", \"Angry\", :NONE],\n        :from_arguments => [\"--an-gry\"],\n        :expects => true\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --an_gry, got --an-gry. Partial argument matching is deprecated and will be removed in a future release./)\n    end\n\n    it \"parses a 'short' option\" do\n      parses(\n        :option => [\"--angry\", \"-a\", \"Angry\", :NONE],\n        :from_arguments => [\"-a\"],\n        :expects => true\n      )\n    end\n\n    it \"supports the '--no-blah' syntax\" do\n      parses(\n        :option => [\"--[no-]rage\", \"Rage\", :NONE],\n        :from_arguments => [\"--no-rage\"],\n        :expects => false\n      )\n      expect(@logs).to be_empty\n    end\n\n    it \"resolves '-' to '_' with '--no-blah' syntax\" do\n      parses(\n        :option => [\"--[no-]an_gry\", \"Angry\", :NONE],\n        :from_arguments => [\"--no-an-gry\"],\n        :expects => false\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --\\[no-\\]an_gry, got --no-an-gry. Partial argument matching is deprecated and will be removed in a future release./)\n    end\n\n    it \"resolves '_' to '-' with '--no-blah' syntax\" do\n      parses(\n        :option => [\"--[no-]an-gry\", \"Angry\", :NONE],\n        :from_arguments => [\"--no-an_gry\"],\n        :expects => false\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --\\[no-\\]an-gry, got --no-an_gry. Partial argument matching is deprecated and will be removed in a future release./)\n    end\n\n    it \"resolves '-' to '_' & warns when option is defined with '--no-blah syntax' but argument is given in '--option' syntax\" do\n      parses(\n        :option => [\"--[no-]rag-e\", \"Rage\", :NONE],\n        :from_arguments => [\"--rag_e\"],\n        :expects => true\n      )\n      expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --\\[no-\\]rag-e, got --rag_e. Partial argument matching is deprecated and will be removed in a future release./)\n  end\n\n  it \"resolves '_' to '-' & warns when option is defined with '--no-blah syntax' but argument is given in '--option' syntax\" do\n    parses(\n      :option => [\"--[no-]rag_e\", \"Rage\", :NONE],\n      :from_arguments => [\"--rag-e\"],\n      :expects => true\n    )\n    expect(@logs).to have_matching_log(/Partial argument match detected: correct argument is --\\[no-\\]rag_e, got --rag-e. Partial argument matching is deprecated and will be removed in a future release./)\n  end\n\n    it \"overrides a previous argument with a later one\" do\n      parses(\n        :option => [\"--[no-]rage\", \"Rage\", :NONE],\n        :from_arguments => [\"--rage\", \"--no-rage\"],\n        :expects => false\n      )\n      expect(@logs).to be_empty\n    end\n  end\n\n  it \"does not accept an unknown option specification\" do\n    expect {\n      option_parser.on(\"not\", \"enough\")\n    }.to raise_error(ArgumentError, /this method only takes 3 or 4 arguments/)\n  end\n\n  it \"does not modify the original argument array\" do\n    option_parser.on(\"--foo\", \"Foo\", :NONE) { |val| }\n    args = [\"--foo\"]\n\n    option_parser.parse(args)\n\n    expect(args.length).to eq(1)\n  end\n\n  # The ruby stdlib OptionParser has an awesome \"feature\" that you cannot disable, whereby if\n  #  it sees a short option that you haven't specifically registered with it (e.g., \"-r\"), it\n  #  will automatically attempt to expand it out to whatever long options that you might have\n  #  registered.  Since we need to do our option parsing in two passes (one pass against only\n  #  the global/puppet-wide settings definitions, and then a second pass that includes the\n  #  application or face settings--because we can't load the app/face until we've determined\n  #  the libdir), it is entirely possible that we intend to define our \"short\" option as part\n  #  of the second pass.  Therefore, if the option parser attempts to expand it out into a\n  #  long option during the first pass, terrible things will happen.\n  #\n  # A long story short: we need to have the ability to control this kind of behavior in our\n  #  option parser, and this test simply affirms that we do.\n  it \"does not try to expand short options that weren't explicitly registered\" do\n\n    [\n     [\"--ridiculous\", \"This is ridiculous\", :REQUIRED],\n     [\"--rage-inducing\", \"This is rage-inducing\", :REQUIRED]\n    ].each do |option|\n      option_parser.on(*option) {}\n    end\n\n    expect { option_parser.parse([\"-r\"]) }.to raise_error(Puppet::Util::CommandLine::PuppetOptionError)\n  end\n\n  it \"respects :ignore_invalid_options\" do\n    option_parser.ignore_invalid_options = true\n    expect { option_parser.parse([\"--unknown-option\"]) }.not_to raise_error\n  end\n\n  it \"raises if there is an invalid option and :ignore_invalid_options is not set\" do\n    expect { option_parser.parse([\"--unknown-option\"]) }.to raise_error(Puppet::Util::CommandLine::PuppetOptionError)\n  end\n\n  def parses(option_case)\n    option = option_case[:option]\n    expected_value = option_case[:expects]\n    arguments = option_case[:from_arguments]\n\n    seen_value = nil\n    option_parser.on(*option) do |val|\n      seen_value = val\n    end\n\n    option_parser.parse(arguments)\n\n    expect(seen_value).to eq(expected_value)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/constant_inflector_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/constant_inflector'\n\ndescribe Puppet::Util::ConstantInflector, \"when converting file names to constants\" do\n  it \"should capitalize terms\" do\n    expect(subject.file2constant(\"file\")).to eq(\"File\")\n  end\n\n  it \"should switch all '/' characters to double colons\" do\n    expect(subject.file2constant(\"file/other\")).to eq(\"File::Other\")\n  end\n\n  it \"should remove underscores and capitalize the proceeding letter\" do\n    expect(subject.file2constant(\"file_other\")).to eq(\"FileOther\")\n  end\n\n  it \"should correctly replace as many underscores as exist in the file name\" do\n    expect(subject.file2constant(\"two_under_scores/with_some_more_underscores\")).to eq(\"TwoUnderScores::WithSomeMoreUnderscores\")\n  end\n\n  it \"should collapse multiple underscores\" do\n    expect(subject.file2constant(\"many___scores\")).to eq(\"ManyScores\")\n  end\n\n  it \"should correctly handle file names deeper than two directories\" do\n    expect(subject.file2constant(\"one_two/three_four/five_six\")).to eq(\"OneTwo::ThreeFour::FiveSix\")\n  end\nend\n\ndescribe Puppet::Util::ConstantInflector, \"when converting constnats to file names\" do\n  it \"should convert them to a string if necessary\" do\n    expect(subject.constant2file(Puppet::Util::ConstantInflector)).to be_instance_of(String)\n  end\n\n  it \"should accept string inputs\" do\n    expect(subject.constant2file(\"Puppet::Util::ConstantInflector\")).to be_instance_of(String)\n  end\n\n  it \"should downcase all terms\" do\n    expect(subject.constant2file(\"Puppet\")).to eq(\"puppet\")\n  end\n\n  it \"should convert '::' to '/'\" do\n    expect(subject.constant2file(\"Puppet::Util::Constant\")).to eq(\"puppet/util/constant\")\n  end\n\n  it \"should convert mid-word capitalization to an underscore\" do\n    expect(subject.constant2file(\"OneTwo::ThreeFour\")).to eq(\"one_two/three_four\")\n  end\n\n  it \"should correctly handle constants with more than two parts\" do\n    expect(subject.constant2file(\"OneTwoThree::FourFiveSixSeven\")).to eq(\"one_two_three/four_five_six_seven\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/diff_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/diff'\nrequire 'puppet/util/execution'\n\ndescribe Puppet::Util::Diff do\n  let(:baz_output) { Puppet::Util::Execution::ProcessOutput.new('baz', 0) }\n\n  describe \".diff\" do\n    it \"should execute the diff command with arguments\" do\n      Puppet[:diff] = 'foo'\n      Puppet[:diff_args] = 'bar'\n\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['foo', 'bar', 'a', 'b'], {:failonfail => false, :combine => false})\n        .and_return(baz_output)\n      expect(subject.diff('a', 'b')).to eq('baz')\n    end\n\n    it \"should execute the diff command with multiple arguments\" do\n      Puppet[:diff] = 'foo'\n      Puppet[:diff_args] = 'bar qux'\n\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['foo', 'bar', 'qux', 'a', 'b'], anything)\n        .and_return(baz_output)\n      expect(subject.diff('a', 'b')).to eq('baz')\n    end\n\n    it \"should omit diff arguments if none are specified\" do\n      Puppet[:diff] = 'foo'\n      Puppet[:diff_args] = ''\n\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['foo', 'a', 'b'], {:failonfail => false, :combine => false})\n        .and_return(baz_output)\n      expect(subject.diff('a', 'b')).to eq('baz')\n    end\n\n    it \"should return empty string if the diff command is empty\" do\n      Puppet[:diff] = ''\n\n      expect(Puppet::Util::Execution).not_to receive(:execute)\n      expect(subject.diff('a', 'b')).to eq('')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/docs_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::Docs do\n\n  describe '.scrub' do\n    let(:my_cleaned_output) do\n      %q{This resource type uses the prescribed native tools for creating\ngroups and generally uses POSIX APIs for retrieving information\nabout them.  It does not directly modify `/etc/passwd` or anything.\n\n* Just for fun, we'll add a list.\n* list item two,\n  which has some add'l lines included in it.\n\nAnd here's a code block:\n\n    this is the piece of code\n    it does something cool\n\n**Autorequires:** I would be listing autorequired resources here.}\n    end\n\n    it \"strips the least common indent from multi-line strings, without mangling indentation beyond the least common indent\" do\n      input = <<EOT\n        This resource type uses the prescribed native tools for creating\n        groups and generally uses POSIX APIs for retrieving information\n        about them.  It does not directly modify `/etc/passwd` or anything.\n\n        * Just for fun, we'll add a list.\n        * list item two,\n          which has some add'l lines included in it.\n\n        And here's a code block:\n\n            this is the piece of code\n            it does something cool\n\n        **Autorequires:** I would be listing autorequired resources here.\nEOT\n      output = Puppet::Util::Docs.scrub(input)\n      expect(output).to eq my_cleaned_output\n    end\n\n    it \"ignores the first line when calculating least common indent\" do\n      input = \"This resource type uses the prescribed native tools for creating\n        groups and generally uses POSIX APIs for retrieving information\n        about them.  It does not directly modify `/etc/passwd` or anything.\n\n        * Just for fun, we'll add a list.\n        * list item two,\n          which has some add'l lines included in it.\n\n        And here's a code block:\n\n            this is the piece of code\n            it does something cool\n\n        **Autorequires:** I would be listing autorequired resources here.\"\n      output = Puppet::Util::Docs.scrub(input)\n      expect(output).to eq my_cleaned_output\n    end\n\n    it \"strips trailing whitespace from each line, and strips trailing newlines at end\" do\n      input = \"This resource type uses the prescribed native tools for creating  \\n        groups and generally uses POSIX APIs for retrieving information \\n        about them.  It does not directly modify `/etc/passwd` or anything.  \\n\\n        * Just for fun, we'll add a list. \\n        * list item two,\\n          which has some add'l lines included in it.    \\n\\n        And here's a code block:\\n\\n            this is the piece of code \\n            it does something cool \\n\\n        **Autorequires:** I would be listing autorequired resources here. \\n\\n\"\n      output = Puppet::Util::Docs.scrub(input)\n      expect(output).to eq my_cleaned_output\n    end\n\n    it \"has no side effects on original input string\" do\n      input       = \"First line \\n        second line \\n        \\n            indented line \\n        \\n        last line\\n\\n\"\n      clean_input = \"First line \\n        second line \\n        \\n            indented line \\n        \\n        last line\\n\\n\"\n      Puppet::Util::Docs.scrub(input)\n      expect(input).to eq clean_input\n    end\n\n    it \"does not include whitespace-only lines when calculating least common indent\" do\n      input           = \"First line\\n        second line\\n  \\n            indented line\\n\\n        last line\"\n      expected_output = \"First line\\nsecond line\\n\\n    indented line\\n\\nlast line\"\n      #bogus_output   = \"First line\\nsecond line\\n\\n  indented line\\n\\nlast line\"\n      output = Puppet::Util::Docs.scrub(input)\n      expect(output).to eq expected_output\n    end\n\n    it \"accepts a least common indent of zero, thus not adding errors when input string is already scrubbed\" do\n      expect(Puppet::Util::Docs.scrub(my_cleaned_output)).to eq my_cleaned_output\n    end\n\n    it \"trims leading space from one-liners (even when they're buffered with extra newlines)\" do\n      input = \"\n        Updates values in the `puppet.conf` configuration file.\n      \"\n      expected_output = \"Updates values in the `puppet.conf` configuration file.\"\n      output = Puppet::Util::Docs.scrub(input)\n      expect(output).to eq expected_output\n    end\n\n\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/util/errors_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/errors'\n\nclass ErrorTester\n  include Puppet::Util::Errors\n  attr_accessor :line, :file\nend\n\ndescribe Puppet::Util::Errors do\n  before do\n    @tester = ErrorTester.new\n  end\n\n  it \"should provide a 'fail' method\" do\n    expect(@tester).to respond_to(:fail)\n  end\n\n  it \"should provide a 'devfail' method\" do\n    expect(@tester).to respond_to(:devfail)\n  end\n\n  it \"should raise any provided error when failing\" do\n    expect { @tester.fail(Puppet::ParseError, \"stuff\") }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should default to Puppet::Error when failing\" do\n    expect { @tester.fail(\"stuff\") }.to raise_error(Puppet::Error)\n  end\n\n  it \"should have a method for converting error context into a string\" do\n    @tester.file = \"/my/file\"\n    @tester.line = 50\n    expect(@tester.error_context).to eq(\" (file: /my/file, line: 50)\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/execution_spec.rb",
    "content": "# encoding: UTF-8\nrequire 'spec_helper'\nrequire 'puppet/file_system/uniquefile'\nrequire 'puppet_spec/character_encoding'\n\ndescribe Puppet::Util::Execution, if: !Puppet::Util::Platform.jruby? do\n  include Puppet::Util::Execution\n\n  # utility methods to help us test some private methods without being quite so verbose\n  def call_exec_posix(command, arguments, stdin, stdout, stderr)\n    Puppet::Util::Execution.send(:execute_posix, command, arguments, stdin, stdout, stderr)\n  end\n\n  def call_exec_windows(command, arguments, stdin, stdout, stderr)\n    Puppet::Util::Execution.send(:execute_windows, command, arguments, stdin, stdout, stderr)\n  end\n\n  describe \"execution methods\" do\n    let(:pid) { 5501 }\n    let(:process_handle) { 0xDEADBEEF }\n    let(:thread_handle) { 0xCAFEBEEF }\n    let(:proc_info_stub) { double('processinfo', :process_handle => process_handle, :thread_handle => thread_handle, :process_id => pid) }\n    let(:null_file) { Puppet::Util::Platform.windows? ? 'NUL' : '/dev/null' }\n\n    def stub_process_wait(exitstatus)\n      if Puppet::Util::Platform.windows?\n        allow(Puppet::Util::Windows::Process).to receive(:wait_process).with(process_handle).and_return(exitstatus)\n        allow(FFI::WIN32).to receive(:CloseHandle).with(process_handle)\n        allow(FFI::WIN32).to receive(:CloseHandle).with(thread_handle)\n      else\n        allow(Process).to receive(:waitpid2).with(pid, Process::WNOHANG).and_return(nil, [pid, double('child_status', :exitstatus => exitstatus)])\n        allow(Process).to receive(:waitpid2).with(pid, 0).and_return(nil, [pid, double('child_status', :exitstatus => exitstatus)])\n        allow(Process).to receive(:waitpid2).with(pid).and_return([pid, double('child_status', :exitstatus => exitstatus)])\n      end\n    end\n\n    describe \"#execute_posix (stubs)\", :unless => Puppet::Util::Platform.windows? do\n      before :each do\n        # Most of the things this method does are bad to do during specs. :/\n        allow(Kernel).to receive(:fork).and_return(pid).and_yield\n        allow(Process).to receive(:setsid)\n        allow(Kernel).to receive(:exec)\n        allow(Puppet::Util::SUIDManager).to receive(:change_user)\n        allow(Puppet::Util::SUIDManager).to receive(:change_group)\n\n        # ensure that we don't really close anything!\n        allow(IO).to receive(:new)\n\n        allow($stdin).to receive(:reopen)\n        allow($stdout).to receive(:reopen)\n        allow($stderr).to receive(:reopen)\n\n        @stdin  = File.open(null_file, 'r')\n        @stdout = Puppet::FileSystem::Uniquefile.new('stdout')\n        @stderr = File.open(null_file, 'w')\n\n        # there is a danger here that ENV will be modified by exec_posix.  Normally it would only affect the ENV\n        #  of a forked process, but here, we're stubbing Kernel.fork, so the method has the ability to override the\n        #  \"real\" ENV.  To guard against this, we'll capture a snapshot of ENV before each test.\n        @saved_env = ENV.to_hash\n\n        # Now, we're going to effectively \"mock\" the magic ruby 'ENV' variable by creating a local definition of it\n        #  inside of the module we're testing.\n        Puppet::Util::Execution::ENV = {}\n      end\n\n      after :each do\n        # And here we remove our \"mock\" version of 'ENV', which will allow us to validate that the real ENV has been\n        #  left unharmed.\n        Puppet::Util::Execution.send(:remove_const, :ENV)\n\n        # capture the current environment and make sure it's the same as it was before the test\n        cur_env = ENV.to_hash\n\n        # we will get some fairly useless output if we just use the raw == operator on the hashes here, so we'll\n        #  be a bit more explicit and laborious in the name of making the error more useful...\n        @saved_env.each_pair { |key,val| expect(cur_env[key]).to eq(val) }\n        expect(cur_env.keys - @saved_env.keys).to eq([])\n\n      end\n\n\n      it \"should fork a child process to execute the command\" do\n        expect(Kernel).to receive(:fork).and_return(pid).and_yield\n        expect(Kernel).to receive(:exec).with('test command')\n\n        call_exec_posix('test command', {}, @stdin, @stdout, @stderr)\n      end\n\n      it \"should start a new session group\" do\n        expect(Process).to receive(:setsid)\n\n        call_exec_posix('test command', {}, @stdin, @stdout, @stderr)\n      end\n\n      it \"should permanently change to the correct user and group if specified\" do\n        expect(Puppet::Util::SUIDManager).to receive(:change_group).with(55, true)\n        expect(Puppet::Util::SUIDManager).to receive(:change_user).with(50, true)\n\n        call_exec_posix('test command', {:uid => 50, :gid => 55}, @stdin, @stdout, @stderr)\n      end\n\n      it \"should exit failure if there is a problem execing the command\" do\n        expect(Kernel).to receive(:exec).with('test command').and_raise(\"failed to execute!\")\n        allow(Puppet::Util::Execution).to receive(:puts)\n        expect(Puppet::Util::Execution).to receive(:exit!).with(1)\n\n        call_exec_posix('test command', {}, @stdin, @stdout, @stderr)\n      end\n\n      it \"should properly execute commands specified as arrays\" do\n        expect(Kernel).to receive(:exec).with('test command', 'with', 'arguments')\n\n        call_exec_posix(['test command', 'with', 'arguments'], {:uid => 50, :gid => 55}, @stdin, @stdout, @stderr)\n      end\n\n      it \"should properly execute string commands with embedded newlines\" do\n        expect(Kernel).to receive(:exec).with(\"/bin/echo 'foo' ; \\n /bin/echo 'bar' ;\")\n\n        call_exec_posix(\"/bin/echo 'foo' ; \\n /bin/echo 'bar' ;\", {:uid => 50, :gid => 55}, @stdin, @stdout, @stderr)\n      end\n\n      context 'cwd option' do\n        let(:cwd) { 'cwd' }\n\n        it 'should run the command in the specified working directory' do\n          expect(Dir).to receive(:chdir).with(cwd)\n          expect(Kernel).to receive(:exec).with('test command')\n\n          call_exec_posix('test command', { :cwd => cwd }, @stdin, @stdout, @stderr)\n        end\n\n        it \"should not change the current working directory if cwd is unspecified\" do\n          expect(Dir).to receive(:chdir).never\n          expect(Kernel).to receive(:exec).with('test command')\n\n          call_exec_posix('test command', {}, @stdin, @stdout, @stderr)\n        end\n      end\n\n      it \"should return the pid of the child process\" do\n        expect(call_exec_posix('test command', {}, @stdin, @stdout, @stderr)).to eq(pid)\n      end\n    end\n\n    describe \"#execute_windows (stubs)\", :if => Puppet::Util::Platform.windows? do\n      before :each do\n        allow(Process).to receive(:create).and_return(proc_info_stub)\n        stub_process_wait(0)\n\n        @stdin  = File.open(null_file, 'r')\n        @stdout = Puppet::FileSystem::Uniquefile.new('stdout')\n        @stderr = File.open(null_file, 'w')\n      end\n\n      it \"should create a new process for the command\" do\n        expect(Process).to receive(:create).with({\n          :command_line => \"test command\",\n          :startup_info => {:stdin => @stdin, :stdout => @stdout, :stderr => @stderr},\n          :close_handles => false\n        }).and_return(proc_info_stub)\n\n        call_exec_windows('test command', {}, @stdin, @stdout, @stderr)\n      end\n\n      context 'cwd option' do\n        let(:cwd) { 'cwd' }\n        it \"should execute the command in the specified working directory\" do\n          expect(Process).to receive(:create).with({\n            :command_line => \"test command\",\n            :startup_info => {\n              :stdin => @stdin,\n              :stdout => @stdout,\n              :stderr => @stderr\n            },\n            :close_handles => false,\n            :cwd => cwd\n          })\n\n          call_exec_windows('test command', { :cwd => cwd }, @stdin, @stdout, @stderr)\n        end\n\n        it \"should not change the current working directory if cwd is unspecified\" do\n          expect(Dir).to receive(:chdir).never\n          expect(Process).to receive(:create) do |args|\n            expect(args[:cwd]).to be_nil\n          end\n\n          call_exec_windows('test command', {}, @stdin, @stdout, @stderr)\n        end\n      end\n\n      context 'suppress_window option' do\n        let(:cwd) { 'cwd' }\n        it \"should execute the command in the specified working directory\" do\n          expect(Process).to receive(:create).with({\n            :command_line => \"test command\",\n            :startup_info => {\n              :stdin => @stdin,\n              :stdout => @stdout,\n              :stderr => @stderr\n            },\n            :close_handles => false,\n            :creation_flags => Puppet::Util::Windows::Process::CREATE_NO_WINDOW\n          })\n\n          call_exec_windows('test command', { :suppress_window => true }, @stdin, @stdout, @stderr)\n        end\n      end\n\n      it \"should return the process info of the child process\" do\n        expect(call_exec_windows('test command', {}, @stdin, @stdout, @stderr)).to eq(proc_info_stub)\n      end\n\n      it \"should quote arguments containing spaces if command is specified as an array\" do\n        expect(Process).to receive(:create).with(hash_including(command_line: '\"test command\" with some \"arguments \\\"with spaces\"')).and_return(proc_info_stub)\n\n        call_exec_windows(['test command', 'with', 'some', 'arguments \"with spaces'], {}, @stdin, @stdout, @stderr)\n      end\n    end\n\n    describe \"#execute (stubs)\" do\n      before :each do\n        stub_process_wait(0)\n      end\n\n      describe \"when an execution stub is specified\" do\n        before :each do\n          Puppet::Util::ExecutionStub.set do |command,args,stdin,stdout,stderr|\n            \"execution stub output\"\n          end\n        end\n\n        it \"should call the block on the stub\" do\n          expect(Puppet::Util::Execution.execute(\"/usr/bin/run_my_execute_stub\")).to eq(\"execution stub output\")\n        end\n\n        it \"should not actually execute anything\" do\n          expect(Puppet::Util::Execution).not_to receive(:execute_posix)\n          expect(Puppet::Util::Execution).not_to receive(:execute_windows)\n\n          Puppet::Util::Execution.execute(\"/usr/bin/run_my_execute_stub\")\n        end\n      end\n\n      describe \"when setting up input and output files\" do\n        include PuppetSpec::Files\n        let(:executor) { Puppet::Util::Platform.windows? ? 'execute_windows' : 'execute_posix' }\n        let(:rval) { Puppet::Util::Platform.windows? ? proc_info_stub : pid }\n\n        before :each do\n          allow(Puppet::Util::Execution).to receive(:wait_for_output)\n        end\n\n        it \"should set stdin to the stdinfile if specified\" do\n          input = tmpfile('stdin')\n          FileUtils.touch(input)\n\n          expect(Puppet::Util::Execution).to receive(executor) do |_,_,stdin,_,_|\n            expect(stdin.path).to eq(input)\n            rval\n          end\n\n          Puppet::Util::Execution.execute('test command', :stdinfile => input)\n        end\n\n        it \"should set stdin to the null file if not specified\" do\n          expect(Puppet::Util::Execution).to receive(executor) do |_,_,stdin,_,_|\n            expect(stdin.path).to eq(null_file)\n            rval\n          end\n\n          Puppet::Util::Execution.execute('test command')\n        end\n\n        describe \"when squelch is set\" do\n          it \"should set stdout and stderr to the null file\" do\n            expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n              expect(stdout.path).to eq(null_file)\n              expect(stderr.path).to eq(null_file)\n              rval\n            end\n\n            Puppet::Util::Execution.execute('test command', :squelch => true)\n          end\n        end\n\n        describe \"cwd option\" do\n          def expect_cwd_to_be(cwd)\n            expect(Puppet::Util::Execution).to receive(executor).with(\n              anything,\n              hash_including(cwd: cwd),\n              anything,\n              anything,\n              anything\n            ).and_return(rval)\n          end\n\n          it 'should raise an ArgumentError if the specified working directory does not exist' do\n            cwd = 'cwd'\n            allow(Puppet::FileSystem).to receive(:directory?).with(cwd).and_return(false)\n\n            expect {\n              Puppet::Util::Execution.execute('test command', cwd: cwd)\n            }.to raise_error do |error|\n              expect(error).to be_a(ArgumentError)\n              expect(error.message).to match(cwd)\n            end\n          end\n\n          it \"should set the cwd to the user-specified one\" do\n            allow(Puppet::FileSystem).to receive(:directory?).with('cwd').and_return(true)\n            expect_cwd_to_be('cwd')\n            Puppet::Util::Execution.execute('test command', cwd: 'cwd')\n          end\n        end\n\n        describe \"on POSIX\", :if => Puppet.features.posix? do\n          describe \"when squelch is not set\" do\n            it \"should set stdout to a pipe\" do\n              expect(Puppet::Util::Execution).to receive(executor).with(anything, anything, anything, be_a(IO), anything).and_return(rval)\n\n              Puppet::Util::Execution.execute('test command', :squelch => false)\n            end\n\n            it \"should set stderr to the same file as stdout if combine is true\" do\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout).to eq(stderr)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :squelch => false, :combine => true)\n            end\n\n            it \"should set stderr to the null device if combine is false\" do\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.class).to eq(IO)\n                expect(stderr.path).to eq(null_file)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :squelch => false, :combine => false)\n            end\n\n            it \"should default combine to true when no options are specified\" do\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout).to eq(stderr)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command')\n            end\n\n            it \"should default combine to false when options are specified, but combine is not\" do\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.class).to eq(IO)\n                expect(stderr.path).to eq(null_file)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :failonfail => false)\n            end\n\n            it \"should default combine to false when an empty hash of options is specified\" do\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.class).to eq(IO)\n                expect(stderr.path).to eq(null_file)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', {})\n            end\n          end\n        end\n\n        describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n          describe \"when squelch is not set\" do\n            it \"should set stdout to a temporary output file\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,_|\n                expect(stdout.path).to eq(outfile.path)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :squelch => false)\n            end\n\n            it \"should set stderr to the same file as stdout if combine is true\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.path).to eq(outfile.path)\n                expect(stderr.path).to eq(outfile.path)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :squelch => false, :combine => true)\n            end\n\n            it \"should set stderr to the null device if combine is false\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.path).to eq(outfile.path)\n                expect(stderr.path).to eq(null_file)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :squelch => false, :combine => false)\n            end\n\n            it \"should combine stdout and stderr if combine is true\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.path).to eq(outfile.path)\n                expect(stderr.path).to eq(outfile.path)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :combine => true)\n            end\n\n            it \"should default combine to true when no options are specified\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.path).to eq(outfile.path)\n                expect(stderr.path).to eq(outfile.path)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command')\n            end\n\n            it \"should default combine to false when options are specified, but combine is not\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.path).to eq(outfile.path)\n                expect(stderr.path).to eq(null_file)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', :failonfail => false)\n            end\n\n            it \"should default combine to false when an empty hash of options is specified\" do\n              outfile = Puppet::FileSystem::Uniquefile.new('stdout')\n              allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(outfile)\n\n              expect(Puppet::Util::Execution).to receive(executor) do |_,_,_,stdout,stderr|\n                expect(stdout.path).to eq(outfile.path)\n                expect(stderr.path).to eq(null_file)\n                rval\n              end\n\n              Puppet::Util::Execution.execute('test command', {})\n            end\n          end\n        end\n      end\n\n      describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n        it \"should always close the process and thread handles\" do\n          allow(Puppet::Util::Execution).to receive(:execute_windows).and_return(proc_info_stub)\n\n          expect(Puppet::Util::Windows::Process).to receive(:wait_process).with(process_handle).and_raise('whatever')\n          expect(FFI::WIN32).to receive(:CloseHandle).with(thread_handle)\n          expect(FFI::WIN32).to receive(:CloseHandle).with(process_handle)\n\n          expect { Puppet::Util::Execution.execute('test command') }.to raise_error(RuntimeError)\n        end\n\n        it \"should return the correct exit status even when exit status is greater than 256\" do\n          real_exit_status = 3010\n\n          allow(Puppet::Util::Execution).to receive(:execute_windows).and_return(proc_info_stub)\n          stub_process_wait(real_exit_status)\n          allow(Puppet::Util::Execution).to receive(:exitstatus).and_return(real_exit_status % 256) # The exitstatus is changed to be mod 256 so that ruby can fit it into 8 bits.\n\n          expect(Puppet::Util::Execution.execute('test command', :failonfail => false).exitstatus).to eq(real_exit_status)\n        end\n      end\n    end\n\n    describe \"#execute (posix locale)\", :unless => Puppet::Util::Platform.windows? do\n      before :each do\n        # there is a danger here that ENV will be modified by exec_posix.  Normally it would only affect the ENV\n        #  of a forked process, but, in some of the previous tests in this file we're stubbing Kernel.fork., which could\n        #  allow the method to override the \"real\" ENV.  This shouldn't be a problem for these tests because they are\n        #  not stubbing Kernel.fork, but, better safe than sorry... so, to guard against this, we'll capture a snapshot\n        #  of ENV before each test.\n        @saved_env = ENV.to_hash\n      end\n\n      after :each do\n        # capture the current environment and make sure it's the same as it was before the test\n        cur_env = ENV.to_hash\n        # we will get some fairly useless output if we just use the raw == operator on the hashes here, so we'll\n        #  be a bit more explicit and laborious in the name of making the error more useful...\n        @saved_env.each_pair { |key,val| expect(cur_env[key]).to eq(val) }\n        expect(cur_env.keys - @saved_env.keys).to eq([])\n      end\n\n      # build up a printf-style string that contains a command to get the value of an environment variable\n      # from the operating system.  We can substitute into this with the names of the desired environment variables later.\n      get_env_var_cmd = 'echo $%s'\n\n      # a sentinel value that we can use to emulate what locale environment variables might be set to on an international\n      # system.\n      lang_sentinel_value = \"en_US.UTF-8\"\n      # a temporary hash that contains sentinel values for each of the locale environment variables that we override in\n      # \"execute\"\n      locale_sentinel_env = {}\n      Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| locale_sentinel_env[var] = lang_sentinel_value }\n\n      it \"should override the locale environment variables when :override_locale is not set (defaults to true)\" do\n        # temporarily override the locale environment vars with a sentinel value, so that we can confirm that\n        # execute is actually setting them.\n        Puppet::Util.withenv(locale_sentinel_env) do\n          Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n            # we expect that all of the POSIX vars will have been cleared except for LANG and LC_ALL\n            expected_value = (['LANG', 'LC_ALL'].include?(var)) ? \"C\" : \"\"\n            expect(Puppet::Util::Execution.execute(get_env_var_cmd % var).strip).to eq(expected_value)\n          end\n        end\n      end\n\n      it \"should override the LANG environment variable when :override_locale is set to true\" do\n        # temporarily override the locale environment vars with a sentinel value, so that we can confirm that\n        # execute is actually setting them.\n        Puppet::Util.withenv(locale_sentinel_env) do\n          Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n            # we expect that all of the POSIX vars will have been cleared except for LANG and LC_ALL\n            expected_value = (['LANG', 'LC_ALL'].include?(var)) ? \"C\" : \"\"\n            expect(Puppet::Util::Execution.execute(get_env_var_cmd % var, {:override_locale => true}).strip).to eq(expected_value)\n          end\n        end\n      end\n\n      it \"should *not* override the LANG environment variable when :override_locale is set to false\" do\n        # temporarily override the locale environment vars with a sentinel value, so that we can confirm that\n        # execute is not setting them.\n        Puppet::Util.withenv(locale_sentinel_env) do\n          Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n            expect(Puppet::Util::Execution.execute(get_env_var_cmd % var, {:override_locale => false}).strip).to eq(lang_sentinel_value)\n          end\n        end\n      end\n\n      it \"should have restored the LANG and locale environment variables after execution\" do\n        # we'll do this once without any sentinel values, to give us a little more test coverage\n        orig_env_vals = {}\n        Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n          orig_env_vals[var] = ENV[var]\n        end\n        # now we can really execute any command--doesn't matter what it is...\n        Puppet::Util::Execution.execute(get_env_var_cmd % 'anything', {:override_locale => true})\n        # now we check and make sure the original environment was restored\n        Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n          expect(ENV[var]).to eq(orig_env_vals[var])\n        end\n\n        # now, once more... but with our sentinel values\n        Puppet::Util.withenv(locale_sentinel_env) do\n          # now we can really execute any command--doesn't matter what it is...\n          Puppet::Util::Execution.execute(get_env_var_cmd % 'anything', {:override_locale => true})\n          # now we check and make sure the original environment was restored\n          Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|\n            expect(ENV[var]).to eq(locale_sentinel_env[var])\n          end\n        end\n\n      end\n    end\n\n    describe \"#execute (posix user env vars)\", :unless => Puppet::Util::Platform.windows? do\n      # build up a printf-style string that contains a command to get the value of an environment variable\n      # from the operating system.  We can substitute into this with the names of the desired environment variables later.\n      get_env_var_cmd = 'echo $%s'\n\n      # a sentinel value that we can use to emulate what locale environment variables might be set to on an international\n      # system.\n      user_sentinel_value = \"Abracadabra\"\n      # a temporary hash that contains sentinel values for each of the locale environment variables that we override in\n      # \"execute\"\n      user_sentinel_env = {}\n      Puppet::Util::POSIX::USER_ENV_VARS.each { |var| user_sentinel_env[var] = user_sentinel_value }\n\n      it \"should unset user-related environment vars during execution\" do\n        # first we set up a temporary execution environment with sentinel values for the user-related environment vars\n        # that we care about.\n        Puppet::Util.withenv(user_sentinel_env) do\n          # with this environment, we loop over the vars in question\n          Puppet::Util::POSIX::USER_ENV_VARS.each do |var|\n            # ensure that our temporary environment is set up as we expect\n            expect(ENV[var]).to eq(user_sentinel_env[var])\n\n            # run an \"exec\" via the provider and ensure that it unsets the vars\n            expect(Puppet::Util::Execution.execute(get_env_var_cmd % var).strip).to eq(\"\")\n\n            # ensure that after the exec, our temporary env is still intact\n            expect(ENV[var]).to eq(user_sentinel_env[var])\n          end\n        end\n      end\n\n      it \"should have restored the user-related environment variables after execution\" do\n        # we'll do this once without any sentinel values, to give us a little more test coverage\n        orig_env_vals = {}\n        Puppet::Util::POSIX::USER_ENV_VARS.each do |var|\n          orig_env_vals[var] = ENV[var]\n        end\n        # now we can really execute any command--doesn't matter what it is...\n        Puppet::Util::Execution.execute(get_env_var_cmd % 'anything')\n        # now we check and make sure the original environment was restored\n        Puppet::Util::POSIX::USER_ENV_VARS.each do |var|\n          expect(ENV[var]).to eq(orig_env_vals[var])\n        end\n\n        # now, once more... but with our sentinel values\n        Puppet::Util.withenv(user_sentinel_env) do\n          # now we can really execute any command--doesn't matter what it is...\n          Puppet::Util::Execution.execute(get_env_var_cmd % 'anything')\n          # now we check and make sure the original environment was restored\n          Puppet::Util::POSIX::USER_ENV_VARS.each do |var|\n            expect(ENV[var]).to eq(user_sentinel_env[var])\n          end\n        end\n      end\n    end\n\n    describe \"#execute (debug logging)\" do\n      before :each do\n        Puppet[:log_level] = 'debug'\n\n        stub_process_wait(0)\n\n        if Puppet::Util::Platform.windows?\n          allow(Puppet::Util::Execution).to receive(:execute_windows).and_return(proc_info_stub)\n        else\n          allow(Puppet::Util::Execution).to receive(:execute_posix).and_return(pid)\n        end\n      end\n\n      it \"should log if no uid or gid specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello')\n      end\n\n      it \"should log numeric uid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with uid=100: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:uid => 100})\n      end\n\n      it \"should log numeric gid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with gid=500: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:gid => 500})\n      end\n\n      it \"should log numeric uid and gid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with uid=100 gid=500: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:uid => 100, :gid => 500})\n      end\n\n      it \"should log string uid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with uid=myuser: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:uid => 'myuser'})\n      end\n\n      it \"should log string gid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with gid=mygroup: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:gid => 'mygroup'})\n      end\n\n      it \"should log string uid and gid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with uid=myuser gid=mygroup: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:uid => 'myuser', :gid => 'mygroup'})\n      end\n\n      it \"should log numeric uid and string gid if specified\" do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing with uid=100 gid=mygroup: 'echo hello'\")\n        Puppet::Util::Execution.execute('echo hello', {:uid => 100, :gid => 'mygroup'})\n      end\n\n      it 'should redact commands in debug output when passed sensitive option' do\n        expect(Puppet).to receive(:send_log).with(:debug, \"Executing: '[redacted]'\")\n        Puppet::Util::Execution.execute('echo hello', {:sensitive => true})\n      end\n    end\n\n    describe \"after execution\" do\n      before :each do\n        stub_process_wait(0)\n\n        if Puppet::Util::Platform.windows?\n          allow(Puppet::Util::Execution).to receive(:execute_windows).and_return(proc_info_stub)\n        else\n          allow(Puppet::Util::Execution).to receive(:execute_posix).and_return(pid)\n        end\n      end\n\n      it \"should wait for the child process to exit\" do\n        allow(Puppet::Util::Execution).to receive(:wait_for_output)\n\n        Puppet::Util::Execution.execute('test command')\n      end\n\n      it \"should close the stdin/stdout/stderr files used by the child\" do\n        stdin = double('file')\n        stdout = double('file')\n        stderr = double('file')\n        [stdin, stdout, stderr].each {|io| expect(io).to receive(:close).at_least(:once)}\n\n        expect(File).to receive(:open).\n            exactly(3).times().\n            and_return(stdin, stdout, stderr)\n\n        Puppet::Util::Execution.execute('test command', {:squelch => true, :combine => false})\n      end\n\n      describe \"on POSIX\", :if => Puppet.features.posix? do\n        context \"reading the output\" do\n          before :each do\n            r, w = IO.pipe\n            expect(IO).to receive(:pipe).and_return([r, w])\n            w.write(\"My expected \\u2744 command output\")\n          end\n\n          it \"should return output with external encoding ISO_8859_1\" do\n            result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ISO_8859_1) do\n              Puppet::Util::Execution.execute('test command')\n            end\n            expect(result.encoding).to eq(Encoding::ISO_8859_1)\n            expect(result).to eq(\"My expected \\u2744 command output\".force_encoding(Encoding::ISO_8859_1))\n          end\n\n          it \"should return output with external encoding UTF_8\" do\n            result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do\n              Puppet::Util::Execution.execute('test command')\n            end\n            expect(result.encoding).to eq(Encoding::UTF_8)\n            expect(result).to eq(\"My expected \\u2744 command output\")\n          end\n        end\n\n        it \"should not read the output if squelch is true\" do\n          expect(IO).not_to receive(:pipe)\n\n          expect(Puppet::Util::Execution.execute('test command', :squelch => true)).to eq('')\n        end\n\n        it \"should close the pipe used for output if squelch is false\" do\n          r, w = IO.pipe\n          expect(IO).to receive(:pipe).and_return([r, w])\n\n          expect(Puppet::Util::Execution.execute('test command')).to eq(\"\")\n          expect(r.closed?)\n          expect(w.closed?)\n        end\n\n        it \"should close the pipe used for output if squelch is false and an error is raised\" do\n          r, w = IO.pipe\n          expect(IO).to receive(:pipe).and_return([r, w])\n\n          if Puppet::Util::Platform.windows?\n            expect(Puppet::Util::Execution).to receive(:execute_windows).and_raise(Exception, 'execution failed')\n          else\n            expect(Puppet::Util::Execution).to receive(:execute_posix).and_raise(Exception, 'execution failed')\n          end\n\n          expect {\n            subject.execute('fail command')\n          }.to raise_error(Exception, 'execution failed')\n          expect(r.closed?)\n          expect(w.closed?)\n        end\n      end\n\n      describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n        context \"reading the output\" do\n          before :each do\n            stdout = Puppet::FileSystem::Uniquefile.new('test')\n            allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(stdout)\n            stdout.write(\"My expected \\u2744 command output\")\n          end\n\n          it \"should return output with external encoding ISO_8859_1\" do\n            result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ISO_8859_1) do\n              Puppet::Util::Execution.execute('test command')\n            end\n            expect(result.encoding).to eq(Encoding::ISO_8859_1)\n            expect(result).to eq(\"My expected \\u2744 command output\".force_encoding(Encoding::ISO_8859_1))\n          end\n\n          it \"should return output with external encoding UTF_8\" do\n            result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do\n              Puppet::Util::Execution.execute('test command')\n            end\n            expect(result.encoding).to eq(Encoding::UTF_8)\n            expect(result).to eq(\"My expected \\u2744 command output\")\n          end\n        end\n\n        it \"should not read the output if squelch is true\" do\n          stdout = Puppet::FileSystem::Uniquefile.new('test')\n          allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(stdout)\n          stdout.write(\"My expected command output\")\n\n          expect(Puppet::Util::Execution.execute('test command', :squelch => true)).to eq('')\n        end\n\n        it \"should delete the file used for output if squelch is false\" do\n          stdout = Puppet::FileSystem::Uniquefile.new('test')\n          path = stdout.path\n          allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(stdout)\n\n          Puppet::Util::Execution.execute('test command')\n\n          expect(Puppet::FileSystem.exist?(path)).to be_falsey\n        end\n\n        it \"should not raise an error if the file is open\" do\n          stdout = Puppet::FileSystem::Uniquefile.new('test')\n          allow(Puppet::FileSystem::Uniquefile).to receive(:new).and_return(stdout)\n\n          Puppet::Util::Execution.execute('test command')\n        end\n\n        it \"should raise if it fails to create a Uniquefile for stdout\" do\n          allow(Puppet::FileSystem::Uniquefile).to receive(:new)\n            .and_raise(Errno::ENOENT, 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\doesnotexist')\n\n          expect {\n            Puppet::Util::Execution.execute('test command')\n          }.to raise_error(Errno::ENOENT, 'No such file or directory - C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\doesnotexist')\n        end\n      end\n\n      it \"should raise an error if failonfail is true and the child failed\" do\n        stub_process_wait(1)\n\n        expect {\n          subject.execute('fail command', :failonfail => true)\n        }.to raise_error(Puppet::ExecutionFailure, /Execution of 'fail command' returned 1/)\n      end\n\n      it \"should raise an error with redacted sensitive command if failonfail is true and the child failed\" do\n        stub_process_wait(1)\n\n        expect {\n          subject.execute('fail command', :failonfail => true, :sensitive => true)\n        }.to raise_error(Puppet::ExecutionFailure, /Execution of '\\[redacted\\]' returned 1/)\n      end\n\n      it \"should not raise an error if failonfail is false and the child failed\" do\n        stub_process_wait(1)\n\n        subject.execute('fail command', :failonfail => false)\n      end\n\n      it \"should not raise an error if failonfail is true and the child succeeded\" do\n        stub_process_wait(0)\n\n        subject.execute('fail command', :failonfail => true)\n      end\n\n      it \"should not raise an error if failonfail is false and the child succeeded\" do\n        stub_process_wait(0)\n\n        subject.execute('fail command', :failonfail => false)\n      end\n\n      it \"should default failonfail to true when no options are specified\" do\n        stub_process_wait(1)\n\n        expect {\n          subject.execute('fail command')\n        }.to raise_error(Puppet::ExecutionFailure, /Execution of 'fail command' returned 1/)\n      end\n\n      it \"should default failonfail to false when options are specified, but failonfail is not\" do\n        stub_process_wait(1)\n\n        subject.execute('fail command', { :combine => true })\n      end\n\n      it \"should default failonfail to false when an empty hash of options is specified\" do\n        stub_process_wait(1)\n\n        subject.execute('fail command', {})\n      end\n\n      it \"should raise an error if a nil option is specified\" do\n        expect {\n          Puppet::Util::Execution.execute('fail command', nil)\n        }.to raise_error(TypeError, /(can\\'t convert|no implicit conversion of) nil into Hash/)\n      end\n    end\n  end\n\n  describe \"#execpipe\" do\n    it \"should execute a string as a string\" do\n      expect(Puppet::Util::Execution).to receive(:open).with('| echo hello 2>&1').and_return('hello')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(0)\n      expect(Puppet::Util::Execution.execpipe('echo hello')).to eq('hello')\n    end\n\n    it \"should print meaningful debug message for string argument\" do\n      Puppet[:log_level] = 'debug'\n      expect(Puppet).to receive(:send_log).with(:debug, \"Executing 'echo hello'\")\n      expect(Puppet::Util::Execution).to receive(:open).with('| echo hello 2>&1').and_return('hello')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(0)\n      Puppet::Util::Execution.execpipe('echo hello')\n    end\n\n    it \"should print meaningful debug message for array argument\" do\n      Puppet[:log_level] = 'debug'\n      expect(Puppet).to receive(:send_log).with(:debug, \"Executing 'echo hello'\")\n      expect(Puppet::Util::Execution).to receive(:open).with('| echo hello 2>&1').and_return('hello')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(0)\n      Puppet::Util::Execution.execpipe(['echo','hello'])\n    end\n\n    it \"should execute an array by pasting together with spaces\" do\n      expect(Puppet::Util::Execution).to receive(:open).with('| echo hello 2>&1').and_return('hello')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(0)\n      expect(Puppet::Util::Execution.execpipe(['echo', 'hello'])).to eq('hello')\n    end\n\n    it \"should fail if asked to fail, and the child does\" do\n      allow(Puppet::Util::Execution).to receive(:open).with('| echo hello 2>&1').and_return('error message')\n      expect(Puppet::Util::Execution).to receive(:exitstatus).and_return(1)\n      expect {\n        Puppet::Util::Execution.execpipe('echo hello')\n      }.to raise_error Puppet::ExecutionFailure, /error message/\n    end\n\n    it \"should not fail if asked not to fail, and the child does\" do\n      allow(Puppet::Util::Execution).to receive(:open).and_return('error message')\n      expect(Puppet::Util::Execution.execpipe('echo hello', false)).to eq('error message')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/execution_stub_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::ExecutionStub do\n  it \"should use the provided stub code when 'set' is called\" do\n    Puppet::Util::ExecutionStub.set do |command, options|\n      expect(command).to eq(['/bin/foo', 'bar'])\n      \"stub output\"\n    end\n    expect(Puppet::Util::ExecutionStub.current_value).not_to eq(nil)\n    expect(Puppet::Util::Execution.execute(['/bin/foo', 'bar'])).to eq(\"stub output\")\n  end\n\n  it \"should automatically restore normal execution at the conclusion of each spec test\" do\n    # Note: this test relies on the previous test creating a stub.\n    expect(Puppet::Util::ExecutionStub.current_value).to eq(nil)\n  end\n\n  it \"should restore normal execution after 'reset' is called\", unless: Puppet::Util::Platform.jruby? do\n    # Note: \"true\" exists at different paths in different OSes\n    if Puppet::Util::Platform.windows?\n      true_command = [Puppet::Util.which('cmd.exe').tr('/', '\\\\'), '/c', 'exit 0']\n    else\n      true_command = [Puppet::Util.which('true')]\n    end\n    stub_call_count = 0\n    Puppet::Util::ExecutionStub.set do |command, options|\n      expect(command).to eq(true_command)\n      stub_call_count += 1\n      'stub called'\n    end\n    expect(Puppet::Util::Execution.execute(true_command)).to eq('stub called')\n    expect(stub_call_count).to eq(1)\n    Puppet::Util::ExecutionStub.reset\n    expect(Puppet::Util::ExecutionStub.current_value).to eq(nil)\n    expect(Puppet::Util::Execution.execute(true_command)).to eq('')\n    expect(stub_call_count).to eq(1)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/feature_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/feature'\n\ndescribe Puppet::Util::Feature do\n  before do\n    @features = Puppet::Util::Feature.new(\"features\")\n    allow(@features).to receive(:warn)\n  end\n\n  it \"should not call associated code when adding a feature\" do\n    $loaded_feature = false\n    @features.add(:myfeature) { $loaded_feature = true}\n    expect($loaded_feature).to eq(false)\n  end\n\n  it \"should consider a feature absent when the feature load fails\" do\n    @features.add(:failer) { raise \"foo\" }\n    expect(@features.failer?).to eq(false)\n  end\n\n  it \"should consider a feature to be absent when the feature load returns false\" do\n    @features.add(:failer) { false }\n    expect(@features.failer?).to eq(false)\n  end\n\n  it \"should consider a feature to be absent when the feature load returns nil\" do\n    @features.add(:failer) { nil }\n    expect(@features.failer?).to eq(false)\n  end\n\n  it \"should consider a feature to be present when the feature load returns true\" do\n    @features.add(:available) { true }\n    expect(@features.available?).to eq(true)\n  end\n\n  it \"should consider a feature to be present when the feature load returns truthy\" do\n    @features.add(:available) { \"yes\" }\n    expect(@features.available?).to eq(true)\n  end\n\n  it \"should cache the results of a feature load via code block when the block returns true\" do\n    $loaded_feature = 0\n    @features.add(:myfeature) { $loaded_feature += 1; true }\n    @features.myfeature?\n    @features.myfeature?\n    expect($loaded_feature).to eq(1)\n  end\n\n  it \"should cache the results of a feature load via code block when the block returns false\" do\n    $loaded_feature = 0\n    @features.add(:myfeature) { $loaded_feature += 1; false }\n    @features.myfeature?\n    @features.myfeature?\n    expect($loaded_feature).to eq(1)\n  end\n\n  it \"should not cache the results of a feature load via code block when the block returns nil\" do\n    $loaded_feature = 0\n    @features.add(:myfeature) { $loaded_feature += 1; nil }\n    @features.myfeature?\n    @features.myfeature?\n    expect($loaded_feature).to eq(2)\n  end\n\n  it \"should invalidate the cache for the feature when loading\" do\n    @features.add(:myfeature) { false }\n    expect(@features).not_to be_myfeature\n    @features.add(:myfeature)\n    expect(@features).to be_myfeature\n  end\n\n  it \"should support features with libraries\" do\n    expect { @features.add(:puppet, :libs => %w{puppet}) }.not_to raise_error\n  end\n\n  it \"should consider a feature to be present if all of its libraries are present\" do\n    @features.add(:myfeature, :libs => %w{foo bar})\n    expect(@features).to receive(:require).with(\"foo\")\n    expect(@features).to receive(:require).with(\"bar\")\n\n    expect(@features).to be_myfeature\n  end\n\n  it \"should log and consider a feature to be absent if any of its libraries are absent\" do\n    @features.add(:myfeature, :libs => %w{foo bar})\n    expect(@features).to receive(:require).with(\"foo\").and_raise(LoadError)\n    allow(@features).to receive(:require).with(\"bar\")\n\n    expect(@features).to receive(:debug_once)\n\n    expect(@features).not_to be_myfeature\n  end\n\n  it \"should change the feature to be present when its libraries become available\" do\n    @features.add(:myfeature, :libs => %w{foo bar})\n    times_feature_require_called = 0\n    expect(@features).to receive(:require).twice().with(\"foo\") do\n      times_feature_require_called += 1\n      if times_feature_require_called == 1\n        raise LoadError\n      else\n        nil\n      end\n    end\n    allow(@features).to receive(:require).with(\"bar\")\n    allow(Puppet::Util::RubyGems::Source).to receive(:source).and_return(Puppet::Util::RubyGems::Gems18Source)\n    times_clear_paths_called = 0\n    allow_any_instance_of(Puppet::Util::RubyGems::Gems18Source).to receive(:clear_paths) { times_clear_paths_called += 1 }\n\n    expect(@features).to receive(:debug_once)\n\n    expect(@features).not_to be_myfeature\n    expect(@features).to be_myfeature\n    expect(times_clear_paths_called).to eq(3)\n  end\n\n  it \"should cache load failures when configured to do so\" do\n    Puppet[:always_retry_plugins] = false\n\n    @features.add(:myfeature, :libs => %w{foo bar})\n    expect(@features).to receive(:require).with(\"foo\").and_raise(LoadError)\n\n    expect(@features).not_to be_myfeature\n    # second call would cause an expectation exception if 'require' was\n    # called a second time\n    expect(@features).not_to be_myfeature\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/filetype_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/filetype'\n\n# XXX Import all of the tests into this file.\ndescribe Puppet::Util::FileType do\n  describe \"the flat filetype\" do\n    let(:path) { '/my/file' }\n    let(:type) { Puppet::Util::FileType.filetype(:flat) }\n    let(:file) { type.new(path) }\n\n    it \"should exist\" do\n      expect(type).not_to be_nil\n    end\n\n    describe \"when the file already exists\" do\n      it \"should return the file's contents when asked to read it\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n        expect(Puppet::FileSystem).to receive(:read).with(path, {:encoding => Encoding.default_external}).and_return(\"my text\")\n\n        expect(file.read).to eq(\"my text\")\n      end\n\n      it \"should unlink the file when asked to remove it\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n        expect(Puppet::FileSystem).to receive(:unlink).with(path)\n\n        file.remove\n      end\n    end\n\n    describe \"when the file does not exist\" do\n      it \"should return an empty string when asked to read the file\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(false)\n\n        expect(file.read).to eq(\"\")\n      end\n    end\n\n    describe \"when writing the file\" do\n      let(:tempfile) { double('tempfile', :print => nil, :close => nil, :flush => nil, :path => \"/other/file\") }\n\n      before do\n        allow(FileUtils).to receive(:cp)\n        allow(Tempfile).to receive(:new).and_return(tempfile)\n      end\n\n      it \"should first create a temp file and copy its contents over to the file location\" do\n        expect(Tempfile).to receive(:new).with(\"puppet\", {:encoding => Encoding.default_external}).and_return(tempfile)\n        expect(tempfile).to receive(:print).with(\"my text\")\n        expect(tempfile).to receive(:flush)\n        expect(tempfile).to receive(:close)\n        expect(FileUtils).to receive(:cp).with(tempfile.path, path)\n\n        file.write \"my text\"\n      end\n\n      it \"should set the selinux default context on the file\" do\n        expect(file).to receive(:set_selinux_default_context).with(path)\n        file.write \"eh\"\n      end\n    end\n\n    describe \"when backing up a file\" do\n      it \"should do nothing if the file does not exist\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(false)\n        expect(file).not_to receive(:bucket)\n        file.backup\n      end\n\n      it \"should use its filebucket to backup the file if it exists\" do\n        expect(Puppet::FileSystem).to receive(:exist?).with(path).and_return(true)\n\n        bucket = double('bucket')\n        expect(bucket).to receive(:backup).with(path)\n\n        expect(file).to receive(:bucket).and_return(bucket)\n        file.backup\n      end\n\n      it \"should use the default filebucket\" do\n        bucket = double('bucket')\n        expect(bucket).to receive(:bucket).and_return(\"mybucket\")\n\n        expect(Puppet::Type.type(:filebucket)).to receive(:mkdefaultbucket).and_return(bucket)\n\n        expect(file.bucket).to eq(\"mybucket\")\n      end\n    end\n  end\n\n  shared_examples_for \"crontab provider\" do\n    let(:cron)         { type.new('no_such_user') }\n    let(:crontab)      { File.read(my_fixture(crontab_output)) }\n    let(:options)      { { :failonfail => true, :combine => true } }\n    let(:uid)          { 'no_such_user' }\n    let(:user_options) { options.merge({:uid => uid}) }\n\n    it \"should exist\" do\n      expect(type).not_to be_nil\n    end\n\n    # make Puppet::Util::SUIDManager return something deterministic, not the\n    # uid of the user running the tests, except where overridden below.\n    before :each do\n      allow(Puppet::Util::SUIDManager).to receive(:uid).and_return(1234)\n    end\n\n    describe \"#read\" do\n      before(:each) do\n        allow(Puppet::Util).to receive(:uid).with(uid).and_return(9000)\n      end\n\n      it \"should run crontab -l as the target user\" do\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(['crontab', '-l'], user_options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(crontab, 0))\n        expect(cron.read).to eq(crontab)\n      end\n\n      it \"should not switch user if current user is the target user\" do\n        expect(Puppet::Util).to receive(:uid).with(uid).twice.and_return(9000)\n        expect(Puppet::Util::SUIDManager).to receive(:uid).and_return(9000)\n        expect(Puppet::Util::Execution).to receive(:execute)\n          .with(['crontab', '-l'], options)\n          .and_return(Puppet::Util::Execution::ProcessOutput.new(crontab, 0))\n        expect(cron.read).to eq(crontab)\n      end\n\n      it \"should treat an absent crontab as empty\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(['crontab', '-l'], user_options).and_raise(Puppet::ExecutionFailure, absent_crontab)\n        expect(cron.read).to eq('')\n      end\n\n      it \"should treat a nonexistent user's crontab as empty\" do\n        expect(Puppet::Util).to receive(:uid).with(uid).and_return(nil)\n\n        expect(cron.read).to eq('')\n      end\n\n      it \"should return empty if the user is not authorized to use cron\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(['crontab', '-l'], user_options).and_raise(Puppet::ExecutionFailure, unauthorized_crontab)\n        expect(cron.read).to eq('')\n      end\n    end\n\n    describe \"#remove\" do\n      it \"should run crontab -r as the target user\" do\n        expect(Puppet::Util::Execution).to receive(:execute).with(['crontab', '-r'], user_options)\n        cron.remove\n      end\n\n      it \"should not switch user if current user is the target user\" do\n        expect(Puppet::Util).to receive(:uid).with(uid).and_return(9000)\n        expect(Puppet::Util::SUIDManager).to receive(:uid).and_return(9000)\n        expect(Puppet::Util::Execution).to receive(:execute).with(['crontab','-r'], options)\n        cron.remove\n      end\n    end\n\n    describe \"#write\" do\n      before :each do\n        @tmp_cron = Tempfile.new(\"puppet_crontab_spec\")\n        @tmp_cron_path = @tmp_cron.path\n        allow(Puppet::Util).to receive(:uid).with(uid).and_return(9000)\n        expect(Tempfile).to receive(:new).with(\"puppet_#{name}\", {:encoding => Encoding.default_external}).and_return(@tmp_cron)\n      end\n\n      after :each do\n        expect(Puppet::FileSystem.exist?(@tmp_cron_path)).to be_falsey\n      end\n\n      it \"should run crontab as the target user on a temporary file\" do\n        expect(File).to receive(:chown).with(9000, nil, @tmp_cron_path)\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"crontab\", @tmp_cron_path], user_options)\n\n        expect(@tmp_cron).to receive(:print).with(\"foo\\n\")\n        cron.write \"foo\\n\"\n      end\n\n      it \"should not switch user if current user is the target user\" do\n        expect(Puppet::Util::SUIDManager).to receive(:uid).and_return(9000)\n        expect(File).to receive(:chown).with(9000, nil, @tmp_cron_path)\n        expect(Puppet::Util::Execution).to receive(:execute).with([\"crontab\", @tmp_cron_path], options)\n\n        expect(@tmp_cron).to receive(:print).with(\"foo\\n\")\n        cron.write \"foo\\n\"\n      end\n    end\n  end\n\n  describe \"the suntab filetype\", :unless => Puppet::Util::Platform.windows? do\n    let(:type)           { Puppet::Util::FileType.filetype(:suntab) }\n    let(:name)           { type.name }\n    let(:crontab_output) { 'suntab_output' }\n\n    # possible crontab output was taken from here:\n    # https://docs.oracle.com/cd/E19082-01/819-2380/sysrescron-60/index.html\n    let(:absent_crontab) do\n      'crontab: can\\'t open your crontab file'\n    end\n    let(:unauthorized_crontab) do\n      'crontab: you are not authorized to use cron. Sorry.'\n    end\n\n    it_should_behave_like \"crontab provider\"\n  end\n\n  describe \"the aixtab filetype\", :unless => Puppet::Util::Platform.windows? do\n    let(:type)           { Puppet::Util::FileType.filetype(:aixtab) }\n    let(:name)           { type.name }\n    let(:crontab_output) { 'aixtab_output' }\n\n    let(:absent_crontab) do\n      '0481-103 Cannot open a file in the /var/spool/cron/crontabs directory.'\n    end\n    let(:unauthorized_crontab) do\n      '0481-109 You are not authorized to use the cron command.'\n    end\n\n    it_should_behave_like \"crontab provider\"\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/inifile_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/inifile'\n\ndescribe Puppet::Util::IniConfig::Section do\n\n  subject { described_class.new('testsection', '/some/imaginary/file') }\n\n  describe \"determining if the section is dirty\" do\n    it \"is not dirty on creation\" do\n      expect(subject).to_not be_dirty\n    end\n\n    it \"is dirty if a key is changed\" do\n      subject['hello'] = 'world'\n      expect(subject).to be_dirty\n    end\n\n    it \"is dirty if the section has been explicitly marked as dirty\" do\n      subject.mark_dirty\n      expect(subject).to be_dirty\n    end\n\n    it \"is dirty if the section is marked for deletion\" do\n      subject.destroy = true\n      expect(subject).to be_dirty\n    end\n\n    it \"is clean if the section has been explicitly marked as clean\" do\n      subject['hello'] = 'world'\n      subject.mark_clean\n      expect(subject).to_not be_dirty\n    end\n  end\n\n  describe \"reading an entry\" do\n    it \"returns nil if the key is not present\" do\n      expect(subject['hello']).to be_nil\n    end\n\n    it \"returns the value if the key is specified\" do\n      subject.entries << ['hello', 'world']\n      expect(subject['hello']).to eq 'world'\n    end\n\n    it \"ignores comments when looking for a match\" do\n      subject.entries << '#this = comment'\n      expect(subject['#this']).to be_nil\n    end\n  end\n\n  describe \"formatting the section\" do\n    it \"prefixes the output with the section header\" do\n      expect(subject.format).to eq \"[testsection]\\n\"\n    end\n\n    it \"restores comments and blank lines\" do\n      subject.entries << \"#comment\\n\"\n      subject.entries << \"    \"\n      expect(subject.format).to eq(\n        \"[testsection]\\n\" +\n        \"#comment\\n\" +\n        \"    \"\n      )\n    end\n\n    it \"adds all keys that have values\" do\n      subject.entries << ['somekey', 'somevalue']\n      expect(subject.format).to eq(\"[testsection]\\nsomekey=somevalue\\n\")\n    end\n\n    it \"excludes keys that have a value of nil\" do\n      subject.entries << ['empty', nil]\n      expect(subject.format).to eq(\"[testsection]\\n\")\n    end\n\n    it \"preserves the order of the section\" do\n      subject.entries << ['firstkey', 'firstval']\n      subject.entries << \"# I am a comment, hear me roar\\n\"\n      subject.entries << ['secondkey', 'secondval']\n\n      expect(subject.format).to eq(\n        \"[testsection]\\n\" +\n        \"firstkey=firstval\\n\" +\n        \"# I am a comment, hear me roar\\n\" +\n        \"secondkey=secondval\\n\"\n      )\n    end\n\n    it \"is empty if the section is marked for deletion\" do\n      subject.entries << ['firstkey', 'firstval']\n      subject.destroy = true\n      expect(subject.format).to eq('')\n    end\n  end\nend\n\ndescribe Puppet::Util::IniConfig::PhysicalFile do\n  subject { described_class.new('/some/nonexistent/file') }\n\n  let(:first_sect) do\n    sect = Puppet::Util::IniConfig::Section.new('firstsection', '/some/imaginary/file')\n    sect.entries << \"# comment\\n\" << ['onefish', 'redfish'] << \"\\n\"\n    sect\n  end\n\n  let(:second_sect) do\n    sect = Puppet::Util::IniConfig::Section.new('secondsection', '/some/imaginary/file')\n    sect.entries << ['twofish', 'bluefish']\n    sect\n  end\n\n  describe \"when reading a file\" do\n    it \"raises an error if the file does not exist\" do\n      allow(subject.filetype).to receive(:read)\n      expect {\n        subject.read\n      }.to raise_error(%r[Cannot read nonexistent file .*/some/nonexistent/file])\n    end\n\n    it \"passes the contents of the file to #parse\" do\n      allow(subject.filetype).to receive(:read).and_return(\"[section]\")\n      expect(subject).to receive(:parse).with(\"[section]\")\n\n      subject.read\n    end\n  end\n\n  describe \"when parsing a file\" do\n    describe \"parsing sections\" do\n      it \"creates new sections the first time that the section is found\" do\n        text = \"[mysect]\\n\"\n\n        subject.parse(text)\n\n        expect(subject.contents.count).to eq 1\n        sect = subject.contents[0]\n        expect(sect.name).to eq \"mysect\"\n      end\n\n      it \"raises an error if a section is redefined in the file\" do\n        text = \"[mysect]\\n[mysect]\\n\"\n\n        expect {\n          subject.parse(text)\n        }.to raise_error(Puppet::Util::IniConfig::IniParseError,\n                         /Section \"mysect\" is already defined, cannot redefine/)\n      end\n\n      it \"raises an error if a section is redefined in the file collection\" do\n        subject.file_collection = double('file collection', :get_section => true)\n        text = \"[mysect]\\n[mysect]\\n\"\n\n        expect {\n          subject.parse(text)\n        }.to raise_error(Puppet::Util::IniConfig::IniParseError,\n                         /Section \"mysect\" is already defined, cannot redefine/)\n      end\n    end\n\n    describe 'parsing properties' do\n      it 'raises an error if the property is not within a section' do\n        text = \"key=val\\n\"\n\n        expect {\n          subject.parse(text)\n        }.to raise_error(Puppet::Util::IniConfig::IniParseError,\n                         /Property with key \"key\" outside of a section/)\n      end\n\n      it 'adds the property to the current section' do\n        text = \"[main]\\nkey=val\\n\"\n\n        subject.parse(text)\n        expect(subject.contents.count).to eq 1\n        sect = subject.contents[0]\n        expect(sect['key']).to eq 'val'\n      end\n\n      context 'with white space' do\n        let(:section) do\n          text = <<-INIFILE\n[main]\n  leading_white_space=value1\nwhite_space_after_key =value2\nwhite_space_after_equals= value3\nwhite_space_after_value=value4\\t\nINIFILE\n          subject.parse(text)\n          expect(subject.contents.count).to eq 1\n          subject.contents[0]\n        end\n\n        it 'allows and ignores white space before the key' do\n          expect(section['leading_white_space']).to eq('value1')\n        end\n\n        it 'allows and ignores white space before the equals' do\n          expect(section['white_space_after_key']).to eq('value2')\n        end\n\n        it 'allows and ignores white space after the equals' do\n          expect(section['white_space_after_equals']).to eq('value3')\n        end\n\n        it 'allows and ignores white spaces after the value' do\n          expect(section['white_space_after_value']).to eq('value4')\n        end\n      end\n    end\n\n    describe \"parsing line continuations\" do\n      it \"adds the continued line to the last parsed property\" do\n        text = \"[main]\\nkey=val\\n moreval\"\n\n        subject.parse(text)\n        expect(subject.contents.count).to eq 1\n        sect = subject.contents[0]\n        expect(sect['key']).to eq \"val\\n moreval\"\n      end\n    end\n\n    describe \"parsing comments and whitespace\" do\n      it \"treats # as a comment leader\" do\n        text = \"# octothorpe comment\"\n\n        subject.parse(text)\n        expect(subject.contents).to eq [\"# octothorpe comment\"]\n      end\n\n      it \"treats ; as a comment leader\" do\n        text = \"; semicolon comment\"\n\n        subject.parse(text)\n        expect(subject.contents).to eq [\"; semicolon comment\"]\n      end\n\n      it \"treates 'rem' as a comment leader\" do\n        text = \"rem rapid eye movement comment\"\n\n        subject.parse(text)\n        expect(subject.contents).to eq [\"rem rapid eye movement comment\"]\n      end\n\n      it \"stores comments and whitespace in a section in the correct section\" do\n        text = \"[main]\\n; main section comment\"\n\n        subject.parse(text)\n\n        sect = subject.get_section(\"main\")\n        expect(sect.entries).to eq [\"; main section comment\"]\n      end\n    end\n  end\n\n  it \"can return all sections\" do\n    text = \"[first]\\n\" +\n           \"; comment\\n\" +\n           \"[second]\\n\" +\n           \"key=value\"\n\n    subject.parse(text)\n\n    sections = subject.sections\n    expect(sections.count).to eq 2\n    expect(sections[0].name).to eq \"first\"\n    expect(sections[1].name).to eq \"second\"\n  end\n\n  it \"can retrieve a specific section\" do\n    text = \"[first]\\n\" +\n           \"; comment\\n\" +\n           \"[second]\\n\" +\n           \"key=value\"\n\n    subject.parse(text)\n\n    section = subject.get_section(\"second\")\n    expect(section.name).to eq \"second\"\n    expect(section[\"key\"]).to eq \"value\"\n  end\n\n  describe \"formatting\" do\n    it \"concatenates each formatted section in order\" do\n      subject.contents << first_sect << second_sect\n\n      expected = \"[firstsection]\\n\" +\n        \"# comment\\n\" +\n        \"onefish=redfish\\n\" +\n        \"\\n\" +\n        \"[secondsection]\\n\" +\n        \"twofish=bluefish\\n\"\n\n      expect(subject.format).to eq expected\n    end\n\n    it \"includes comments that are not within a section\" do\n      subject.contents << \"# This comment is not in a section\\n\" << first_sect << second_sect\n\n      expected = \"# This comment is not in a section\\n\" +\n        \"[firstsection]\\n\" +\n        \"# comment\\n\" +\n        \"onefish=redfish\\n\" +\n        \"\\n\" +\n        \"[secondsection]\\n\" +\n        \"twofish=bluefish\\n\"\n\n      expect(subject.format).to eq expected\n    end\n\n    it \"excludes sections that are marked to be destroyed\" do\n      subject.contents << first_sect << second_sect\n      first_sect.destroy = true\n\n      expected = \"[secondsection]\\n\" + \"twofish=bluefish\\n\"\n\n      expect(subject.format).to eq expected\n    end\n  end\n\n  describe \"storing the file\" do\n    describe \"with empty contents\" do\n      describe \"and destroy_empty is true\" do\n        before { subject.destroy_empty = true }\n        it \"removes the file if there are no sections\" do\n          expect(File).to receive(:unlink)\n          subject.store\n        end\n\n        it \"removes the file if all sections are marked to be destroyed\" do\n          subject.contents << first_sect << second_sect\n          first_sect.destroy = true\n          second_sect.destroy = true\n\n          expect(File).to receive(:unlink)\n          subject.store\n        end\n\n        it \"doesn't remove the file if not all sections are marked to be destroyed\" do\n          subject.contents << first_sect << second_sect\n          first_sect.destroy = true\n          second_sect.destroy = false\n\n          expect(File).not_to receive(:unlink)\n          allow(subject.filetype).to receive(:write)\n          subject.store\n        end\n      end\n\n      it \"rewrites the file if destroy_empty is false\" do\n        subject.contents << first_sect << second_sect\n        first_sect.destroy = true\n        second_sect.destroy = true\n\n        expect(File).not_to receive(:unlink)\n        allow(subject).to receive(:format).and_return(\"formatted\")\n        expect(subject.filetype).to receive(:write).with(\"formatted\")\n        subject.store\n      end\n    end\n\n    it \"rewrites the file if any section is dirty\" do\n      subject.contents << first_sect << second_sect\n      first_sect.mark_dirty\n      second_sect.mark_clean\n\n      allow(subject).to receive(:format).and_return(\"formatted\")\n      expect(subject.filetype).to receive(:write).with(\"formatted\")\n      subject.store\n    end\n\n    it \"doesn't modify the file if all sections are clean\" do\n      subject.contents << first_sect << second_sect\n      first_sect.mark_clean\n      second_sect.mark_clean\n\n      allow(subject).to receive(:format).and_return(\"formatted\")\n      expect(subject.filetype).not_to receive(:write)\n      subject.store\n    end\n  end\nend\n\ndescribe Puppet::Util::IniConfig::FileCollection do\n  let(:path_a) { '/some/nonexistent/file/a' }\n  let(:path_b) { '/some/nonexistent/file/b' }\n\n  let(:file_a) { Puppet::Util::IniConfig::PhysicalFile.new(path_a) }\n  let(:file_b) { Puppet::Util::IniConfig::PhysicalFile.new(path_b) }\n\n  let(:sect_a1) { Puppet::Util::IniConfig::Section.new('sect_a1', path_a) }\n  let(:sect_a2) { Puppet::Util::IniConfig::Section.new('sect_a2', path_a) }\n\n  let(:sect_b1) { Puppet::Util::IniConfig::Section.new('sect_b1', path_b) }\n  let(:sect_b2) { Puppet::Util::IniConfig::Section.new('sect_b2', path_b) }\n\n  before do\n    file_a.contents << sect_a1 << sect_a2\n    file_b.contents << sect_b1 << sect_b2\n  end\n\n  describe \"reading a file\" do\n    let(:stub_file) { double('Physical file') }\n\n    it \"creates a new PhysicalFile and uses that to read the file\" do\n      expect(stub_file).to receive(:read)\n      expect(stub_file).to receive(:file_collection=)\n      expect(Puppet::Util::IniConfig::PhysicalFile).to receive(:new).with(path_a).and_return(stub_file)\n\n      subject.read(path_a)\n    end\n\n    it \"stores the PhysicalFile and the path to the file\" do\n      allow(stub_file).to receive(:read)\n      allow(stub_file).to receive(:file_collection=)\n      allow(Puppet::Util::IniConfig::PhysicalFile).to receive(:new).with(path_a).and_return(stub_file)\n      subject.read(path_a)\n\n      path, physical_file = subject.files.first\n\n      expect(path).to eq(path_a)\n      expect(physical_file).to eq stub_file\n    end\n  end\n\n  describe \"storing all files\" do\n    before do\n      subject.files[path_a] = file_a\n      subject.files[path_b] = file_b\n    end\n\n    it \"stores all files in the collection\" do\n      expect(file_a).to receive(:store).once\n      expect(file_b).to receive(:store).once\n\n      subject.store\n    end\n  end\n\n  describe \"iterating over sections\" do\n    before do\n      subject.files[path_a] = file_a\n      subject.files[path_b] = file_b\n    end\n\n    it \"yields every section from every file\" do\n      expect { |b|\n        subject.each_section(&b)\n      }.to yield_successive_args(sect_a1, sect_a2, sect_b1, sect_b2)\n    end\n  end\n\n  describe \"iterating over files\" do\n    before do\n      subject.files[path_a] = file_a\n      subject.files[path_b] = file_b\n    end\n\n    it \"yields the path to every file in the collection\" do\n      expect { |b|\n        subject.each_file(&b)\n      }.to yield_successive_args(path_a, path_b)\n    end\n  end\n\n  describe \"retrieving a specific section\" do\n    before do\n      subject.files[path_a] = file_a\n      subject.files[path_b] = file_b\n    end\n\n    it \"retrieves the first section defined\" do\n      expect(subject.get_section('sect_b1')).to eq sect_b1\n    end\n\n    it \"returns nil if there was no section with the given name\" do\n      expect(subject.get_section('nope')).to be_nil\n    end\n\n    it \"allows #[] to be used as an alias to #get_section\" do\n      expect(subject['b2']).to eq subject.get_section('b2')\n    end\n  end\n\n  describe \"checking if a section has been defined\" do\n    before do\n      subject.files[path_a] = file_a\n      subject.files[path_b] = file_b\n    end\n\n    it \"is true if a section with the given name is defined\" do\n      expect(subject.include?('sect_a1')).to be_truthy\n    end\n\n    it \"is false if a section with the given name can't be found\" do\n      expect(subject.include?('nonexistent')).to be_falsey\n    end\n  end\n\n  describe \"adding a new section\" do\n    before do\n      subject.files[path_a] = file_a\n      subject.files[path_b] = file_b\n    end\n\n    it \"adds the section to the appropriate file\" do\n      expect(file_a).to receive(:add_section).with('newsect')\n      subject.add_section('newsect', path_a)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/json_lockfile_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/json_lockfile'\n\ndescribe Puppet::Util::JsonLockfile do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  before(:each) do\n    @lockfile = tmpfile(\"lock\")\n    @lock = Puppet::Util::JsonLockfile.new(@lockfile)\n  end\n\n  describe \"#lock\" do\n    it \"should create a lock file containing a json hash\" do\n      data = { \"foo\" => \"foofoo\", \"bar\" => \"barbar\" }\n      @lock.lock(data)\n\n      expect(JSON.parse(File.read(@lockfile))).to eq(data)\n    end\n  end\n\n  describe \"reading lock data\" do\n    it \"returns deserialized JSON from the lockfile\" do\n      data = { \"foo\" => \"foofoo\", \"bar\" => \"barbar\" }\n      @lock.lock(data)\n      expect(@lock.lock_data).to eq data\n    end\n\n    it \"returns nil if the file read returned nil\" do\n      @lock.lock\n      allow(File).to receive(:read).and_return(nil)\n      expect(@lock.lock_data).to be_nil\n    end\n\n    it \"returns nil if the file was empty\" do\n      @lock.lock\n      allow(File).to receive(:read).and_return('')\n      expect(@lock.lock_data).to be_nil\n    end\n\n    it \"returns nil if the file was not in PSON\" do\n      @lock.lock\n      allow(File).to receive(:read).and_return('][')\n      expect(@lock.lock_data).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/json_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/util/json'\n\ndescribe Puppet::Util::Json do\n  include PuppetSpec::Files\n\n  shared_examples_for 'json file loader' do |load_method|\n    it 'reads a JSON file from disk' do\n      file_path = file_containing('input', JSON.dump({ \"my\" => \"data\" }))\n\n      expect(load_method.call(file_path)).to eq({ \"my\" => \"data\" })\n    end\n\n    it 'reads JSON as UTF-8' do\n      file_path = file_containing('input', JSON.dump({ \"my\" => \"𠜎\" }))\n\n      expect(load_method.call(file_path)).to eq({ \"my\" => \"𠜎\" })\n    end\n  end\n\n  context \"#load\" do\n    it 'raises an error if JSON is invalid' do\n      expect {\n        Puppet::Util::Json.load('{ invalid')\n      }.to raise_error(Puppet::Util::Json::ParseError, /unexpected token at '{ invalid'/)\n    end\n\n    it 'raises an error if the content is empty' do\n      expect {\n        Puppet::Util::Json.load('')\n      }.to raise_error(Puppet::Util::Json::ParseError)\n    end\n\n    it 'loads true' do\n      expect(Puppet::Util::Json.load('true')).to eq(true)\n    end\n\n    it 'loads false' do\n      expect(Puppet::Util::Json.load('false')).to eq(false)\n    end\n\n    it 'loads a numeric' do\n      expect(Puppet::Util::Json.load('42')).to eq(42)\n    end\n\n    it 'loads a string' do\n      expect(Puppet::Util::Json.load('\"puppet\"')).to eq('puppet')\n    end\n\n    it 'loads an array' do\n      expect(Puppet::Util::Json.load(<<~JSON)).to eq([1, 2])\n        [1, 2]\n      JSON\n    end\n\n    it 'loads a hash' do\n      expect(Puppet::Util::Json.load(<<~JSON)).to eq('a' => 1, 'b' => 2)\n        {\n          \"a\": 1,\n          \"b\": 2\n        }\n      JSON\n    end\n  end\n\n  context \"load_file_if_valid\" do\n    before do\n      Puppet[:log_level] = 'debug'\n    end\n\n    it_should_behave_like 'json file loader', Puppet::Util::Json.method(:load_file_if_valid)\n\n    it 'returns nil when the file is invalid JSON and debug logs about it' do\n      file_path = file_containing('input', '{ invalid')\n      expect(Puppet).to receive(:debug)\n        .with(/Could not retrieve JSON content .+: unexpected token at '{ invalid'/).and_call_original\n\n      expect(Puppet::Util::Json.load_file_if_valid(file_path)).to eql(nil)\n    end\n\n    it 'returns nil when the filename is illegal and debug logs about it' do\n      expect(Puppet).to receive(:debug)\n        .with(/Could not retrieve JSON content .+: pathname contains null byte/).and_call_original\n\n      expect(Puppet::Util::Json.load_file_if_valid(\"not\\0allowed\")).to eql(nil)\n    end\n\n    it 'returns nil when the file does not exist and debug logs about it' do\n      expect(Puppet).to receive(:debug)\n        .with(/Could not retrieve JSON content .+: No such file or directory/).and_call_original\n\n      expect(Puppet::Util::Json.load_file_if_valid('does/not/exist.json')).to eql(nil)\n    end\n  end\n\n  context '#load_file' do\n    it_should_behave_like 'json file loader', Puppet::Util::Json.method(:load_file)\n\n    it 'raises an error when the file is invalid JSON' do\n      file_path = file_containing('input', '{ invalid')\n\n      expect {\n        Puppet::Util::Json.load_file(file_path)\n      }.to raise_error(Puppet::Util::Json::ParseError, /unexpected token at '{ invalid'/)\n    end\n\n    it 'raises an error when the filename is illegal' do\n      expect {\n        Puppet::Util::Json.load_file(\"not\\0allowed\")\n      }.to raise_error(ArgumentError, /null byte/)\n    end\n\n    it 'raises an error when the file does not exist' do\n      expect {\n        Puppet::Util::Json.load_file('does/not/exist.json')\n      }.to raise_error(Errno::ENOENT, /No such file or directory/)\n    end\n\n    it 'writes data formatted as JSON to disk' do\n      file_path = file_containing('input', Puppet::Util::Json.dump({ \"my\" => \"data\" }))\n\n      expect(Puppet::Util::Json.load_file(file_path)).to eq({ \"my\" => \"data\" })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/ldap/connection_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/ldap/connection'\n\n# So our mocks and such all work, even when ldap isn't available.\nunless Puppet.features.ldap?\n  class LDAP\n    class Conn\n      def initialize(*args)\n      end\n    end\n    class SSLConn < Conn; end\n\n    LDAP_OPT_PROTOCOL_VERSION = 1\n    LDAP_OPT_REFERRALS = 2\n    LDAP_OPT_ON = 3\n  end\nend\n\ndescribe Puppet::Util::Ldap::Connection do\n  before do\n    allow(Puppet.features).to receive(:ldap?).and_return(true)\n\n    @ldapconn = double(\n      'ldap',\n      set_option: nil,\n      simple_bind: nil,\n    )\n    allow(LDAP::Conn).to receive(:new).and_return(@ldapconn)\n    allow(LDAP::SSLConn).to receive(:new).and_return(@ldapconn)\n\n    @connection = Puppet::Util::Ldap::Connection.new(\"host\", 1234)\n  end\n\n\n  describe \"when creating connections\" do\n    it \"should require the host and port\" do\n      expect { Puppet::Util::Ldap::Connection.new(\"myhost\") }.to raise_error(ArgumentError)\n    end\n\n    it \"should allow specification of a user and password\" do\n      expect { Puppet::Util::Ldap::Connection.new(\"myhost\", 1234, :user => \"blah\", :password => \"boo\") }.not_to raise_error\n    end\n\n    it \"should allow specification of ssl\" do\n      expect { Puppet::Util::Ldap::Connection.new(\"myhost\", 1234, :ssl => :tsl) }.not_to raise_error\n    end\n\n    it \"should support requiring a new connection\" do\n      expect { Puppet::Util::Ldap::Connection.new(\"myhost\", 1234, :reset => true) }.not_to raise_error\n    end\n\n    it \"should fail if ldap is unavailable\" do\n      expect(Puppet.features).to receive(:ldap?).and_return(false)\n\n      expect { Puppet::Util::Ldap::Connection.new(\"host\", 1234) }.to raise_error(Puppet::Error)\n    end\n\n    it \"should use neither ssl nor tls by default\" do\n      expect(LDAP::Conn).to receive(:new).with(\"host\", 1234).and_return(@ldapconn)\n\n      @connection.start\n    end\n\n    it \"should use LDAP::SSLConn if ssl is requested\" do\n      expect(LDAP::SSLConn).to receive(:new).with(\"host\", 1234).and_return(@ldapconn)\n\n      @connection.ssl = true\n\n      @connection.start\n    end\n\n    it \"should use LDAP::SSLConn and tls if tls is requested\" do\n      expect(LDAP::SSLConn).to receive(:new).with(\"host\", 1234, true).and_return(@ldapconn)\n\n      @connection.ssl = :tls\n\n      @connection.start\n    end\n\n    it \"should set the protocol version to 3 and enable referrals\" do\n      expect(@ldapconn).to receive(:set_option).with(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)\n      expect(@ldapconn).to receive(:set_option).with(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)\n      @connection.start\n    end\n\n    it \"should bind with the provided user and password\" do\n      @connection.user = \"myuser\"\n      @connection.password = \"mypassword\"\n      expect(@ldapconn).to receive(:simple_bind).with(\"myuser\", \"mypassword\")\n\n      @connection.start\n    end\n\n    it \"should bind with no user and password if none has been provided\" do\n      expect(@ldapconn).to receive(:simple_bind).with(nil, nil)\n      @connection.start\n    end\n  end\n\n  describe \"when closing connections\" do\n    it \"should not close connections that are not open\" do\n      allow(@connection).to receive(:connection).and_return(@ldapconn)\n\n      expect(@ldapconn).to receive(:bound?).and_return(false)\n      expect(@ldapconn).not_to receive(:unbind)\n\n      @connection.close\n    end\n  end\n\n  it \"should have a class-level method for creating a default connection\" do\n    expect(Puppet::Util::Ldap::Connection).to respond_to(:instance)\n  end\n\n  describe \"when creating a default connection\" do\n    it \"should use the :ldapserver setting to determine the host\" do\n      Puppet[:ldapserver] = \"myserv\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(\"myserv\", anything, anything)\n      Puppet::Util::Ldap::Connection.instance\n    end\n\n    it \"should use the :ldapport setting to determine the port\" do\n      Puppet[:ldapport] = 456\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, 456, anything)\n      Puppet::Util::Ldap::Connection.instance\n    end\n\n    it \"should set ssl to :tls if tls is enabled\" do\n      Puppet[:ldaptls] = true\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(ssl: :tls))\n      Puppet::Util::Ldap::Connection.instance\n    end\n\n    it \"should set ssl to 'true' if ssl is enabled and tls is not\" do\n      Puppet[:ldaptls] = false\n      Puppet[:ldapssl] = true\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(ssl: true))\n      Puppet::Util::Ldap::Connection.instance\n    end\n\n    it \"should set ssl to false if neither ssl nor tls are enabled\" do\n      Puppet[:ldaptls] = false\n      Puppet[:ldapssl] = false\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(ssl: false))\n      Puppet::Util::Ldap::Connection.instance\n    end\n\n    it \"should set the ldapuser if one is set\" do\n      Puppet[:ldapuser] = \"foo\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(user: \"foo\"))\n      Puppet::Util::Ldap::Connection.instance\n    end\n\n    it \"should set the ldapuser and ldappassword if both is set\" do\n      Puppet[:ldapuser] = \"foo\"\n      Puppet[:ldappassword] = \"bar\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(user: \"foo\", password: \"bar\"))\n      Puppet::Util::Ldap::Connection.instance\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/ldap/generator_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/ldap/generator'\n\ndescribe Puppet::Util::Ldap::Generator do\n  before do\n    @generator = Puppet::Util::Ldap::Generator.new(:uno)\n  end\n\n  it \"should require a parameter name at initialization\" do\n    expect { Puppet::Util::Ldap::Generator.new }.to raise_error(ArgumentError, /wrong number of arguments/)\n  end\n\n  it \"should always return its name as a string\" do\n    g = Puppet::Util::Ldap::Generator.new(:myname)\n    expect(g.name).to eq(\"myname\")\n  end\n\n  it \"should provide a method for declaring the source parameter\" do\n    @generator.from(:dos)\n  end\n\n  it \"should always return a set source as a string\" do\n    @generator.from(:dos)\n    expect(@generator.source).to eq(\"dos\")\n  end\n\n  it \"should return the source as nil if there is no source\" do\n    expect(@generator.source).to be_nil\n  end\n\n  it \"should return itself when declaring the source\" do\n    expect(@generator.from(:dos)).to equal(@generator)\n  end\n\n  it \"should run the provided block when asked to generate the value\" do\n    @generator.with { \"yayness\" }\n    expect(@generator.generate).to eq(\"yayness\")\n  end\n\n  it \"should pass in any provided value to the block\" do\n    @generator.with { |value| value.upcase }\n    expect(@generator.generate(\"myval\")).to eq(\"MYVAL\")\n  end\n\n  it \"should return itself when declaring the code used for generating\" do\n    expect(@generator.with { |value| value.upcase }).to equal(@generator)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/ldap/manager_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/ldap/manager'\n\ndescribe Puppet::Util::Ldap::Manager, :if => Puppet.features.ldap? do\n  before do\n    @manager = Puppet::Util::Ldap::Manager.new\n  end\n\n  it \"should return self when specifying objectclasses\" do\n    expect(@manager.manages(:one, :two)).to equal(@manager)\n  end\n\n  it \"should allow specification of what objectclasses are managed\" do\n    expect(@manager.manages(:one, :two).objectclasses).to eq([:one, :two])\n  end\n\n  it \"should return self when specifying the relative base\" do\n    expect(@manager.at(\"yay\")).to equal(@manager)\n  end\n\n  it \"should allow specification of the relative base\" do\n    expect(@manager.at(\"yay\").location).to eq(\"yay\")\n  end\n\n  it \"should return self when specifying the attribute map\" do\n    expect(@manager.maps(:one => :two)).to equal(@manager)\n  end\n\n  it \"should allow specification of the rdn attribute\" do\n    expect(@manager.named_by(:uid).rdn).to eq(:uid)\n  end\n\n  it \"should allow specification of the attribute map\" do\n    expect(@manager.maps(:one => :two).puppet2ldap).to eq({:one => :two})\n  end\n\n  it \"should have a no-op 'and' method that just returns self\" do\n    expect(@manager.and).to equal(@manager)\n  end\n\n  it \"should allow specification of generated attributes\" do\n    expect(@manager.generates(:thing)).to be_instance_of(Puppet::Util::Ldap::Generator)\n  end\n\n  describe \"when generating attributes\" do\n    before do\n      @generator = double('generator', :source => \"one\", :name => \"myparam\")\n\n      allow(Puppet::Util::Ldap::Generator).to receive(:new).with(:myparam).and_return(@generator)\n    end\n\n    it \"should create a generator to do the parameter generation\" do\n      expect(Puppet::Util::Ldap::Generator).to receive(:new).with(:myparam).and_return(@generator)\n      @manager.generates(:myparam)\n    end\n\n    it \"should return the generator from the :generates method\" do\n      expect(@manager.generates(:myparam)).to equal(@generator)\n    end\n\n    it \"should not replace already present values\" do\n      @manager.generates(:myparam)\n\n      attrs = {\"myparam\" => \"testing\"}\n      expect(@generator).not_to receive(:generate)\n\n      @manager.generate attrs\n\n      expect(attrs[\"myparam\"]).to eq(\"testing\")\n    end\n\n    it \"should look for the parameter as a string, not a symbol\" do\n      @manager.generates(:myparam)\n      expect(@generator).to receive(:generate).with(\"yay\").and_return(%w{double yay})\n      attrs = {\"one\" => \"yay\"}\n      @manager.generate attrs\n\n      expect(attrs[\"myparam\"]).to eq(%w{double yay})\n    end\n\n    it \"should fail if a source is specified and no source value is not defined\" do\n      @manager.generates(:myparam)\n      expect { @manager.generate \"two\" => \"yay\" }.to raise_error(ArgumentError)\n    end\n\n    it \"should use the source value to generate the new value if a source attribute is specified\" do\n      @manager.generates(:myparam)\n      expect(@generator).to receive(:generate).with(\"yay\").and_return(%w{double yay})\n      @manager.generate \"one\" => \"yay\"\n    end\n\n    it \"should not pass in any value if no source attribute is specified\" do\n      allow(@generator).to receive(:source).and_return(nil)\n      @manager.generates(:myparam)\n      expect(@generator).to receive(:generate).and_return(%w{double yay})\n      @manager.generate \"one\" => \"yay\"\n    end\n\n    it \"should convert any results to arrays of strings if necessary\" do\n      expect(@generator).to receive(:generate).and_return(:test)\n      @manager.generates(:myparam)\n\n      attrs = {\"one\" => \"two\"}\n      @manager.generate(attrs)\n      expect(attrs[\"myparam\"]).to eq([\"test\"])\n    end\n\n    it \"should add the result to the passed-in attribute hash\" do\n      expect(@generator).to receive(:generate).and_return(%w{test})\n      @manager.generates(:myparam)\n\n      attrs = {\"one\" => \"two\"}\n      @manager.generate(attrs)\n      expect(attrs[\"myparam\"]).to eq(%w{test})\n    end\n  end\n\n  it \"should be considered invalid if it is missing a location\" do\n    @manager.manages :me\n    @manager.maps :me => :you\n    expect(@manager).not_to be_valid\n  end\n\n  it \"should be considered invalid if it is missing an objectclass list\" do\n    @manager.maps :me => :you\n    @manager.at \"ou=yayness\"\n    expect(@manager).not_to be_valid\n  end\n\n  it \"should be considered invalid if it is missing an attribute map\" do\n    @manager.manages :me\n    @manager.at \"ou=yayness\"\n    expect(@manager).not_to be_valid\n  end\n\n  it \"should be considered valid if it has an attribute map, location, and objectclass list\" do\n    @manager.maps :me => :you\n    @manager.manages :me\n    @manager.at \"ou=yayness\"\n    expect(@manager).to be_valid\n  end\n\n  it \"should calculate an instance's dn using the :ldapbase setting and the relative base\" do\n    Puppet[:ldapbase] = \"dc=testing\"\n    @manager.at \"ou=mybase\"\n    expect(@manager.dn(\"me\")).to eq(\"cn=me,ou=mybase,dc=testing\")\n  end\n\n  it \"should use the specified rdn when calculating an instance's dn\" do\n    Puppet[:ldapbase] = \"dc=testing\"\n    @manager.named_by :uid\n    @manager.at \"ou=mybase\"\n    expect(@manager.dn(\"me\")).to match(/^uid=me/)\n  end\n\n  it \"should calculate its base using the :ldapbase setting and the relative base\" do\n    Puppet[:ldapbase] = \"dc=testing\"\n    @manager.at \"ou=mybase\"\n    expect(@manager.base).to eq(\"ou=mybase,dc=testing\")\n  end\n\n  describe \"when generating its search filter\" do\n    it \"should using a single 'objectclass=<name>' filter if a single objectclass is specified\" do\n      @manager.manages(\"testing\")\n      expect(@manager.filter).to eq(\"objectclass=testing\")\n    end\n\n    it \"should create an LDAP AND filter if multiple objectclasses are specified\" do\n      @manager.manages \"testing\", \"okay\", \"done\"\n      expect(@manager.filter).to eq(\"(&(objectclass=testing)(objectclass=okay)(objectclass=done))\")\n    end\n  end\n\n  it \"should have a method for converting a Puppet attribute name to an LDAP attribute name as a string\" do\n    @manager.maps :puppet_attr => :ldap_attr\n    expect(@manager.ldap_name(:puppet_attr)).to eq(\"ldap_attr\")\n  end\n\n  it \"should have a method for converting an LDAP attribute name to a Puppet attribute name\" do\n    @manager.maps :puppet_attr => :ldap_attr\n    expect(@manager.puppet_name(:ldap_attr)).to eq(:puppet_attr)\n  end\n\n  it \"should have a :create method for creating ldap entries\" do\n    expect(@manager).to respond_to(:create)\n  end\n\n  it \"should have a :delete method for deleting ldap entries\" do\n    expect(@manager).to respond_to(:delete)\n  end\n\n  it \"should have a :modify method for modifying ldap entries\" do\n    expect(@manager).to respond_to(:modify)\n  end\n\n  it \"should have a method for finding an entry by name in ldap\" do\n    expect(@manager).to respond_to(:find)\n  end\n\n  describe \"when converting ldap entries to hashes for providers\" do\n    before do\n      @manager.maps :uno => :one, :dos => :two\n\n      @result = @manager.entry2provider(\"dn\" => [\"cn=one,ou=people,dc=madstop\"], \"one\" => [\"two\"], \"three\" => %w{four}, \"objectclass\" => %w{yay ness})\n    end\n\n    it \"should set the name to the short portion of the dn\" do\n      expect(@result[:name]).to eq(\"one\")\n    end\n\n    it \"should remove the objectclasses\" do\n      expect(@result[\"objectclass\"]).to be_nil\n    end\n\n    it \"should remove any attributes that are not mentioned in the map\" do\n      expect(@result[\"three\"]).to be_nil\n    end\n\n    it \"should rename convert to symbols all attributes to their puppet names\" do\n      expect(@result[:uno]).to eq(%w{two})\n    end\n\n    it \"should set the value of all unset puppet attributes as :absent\" do\n      expect(@result[:dos]).to eq(:absent)\n    end\n  end\n\n  describe \"when using an ldap connection\" do\n    before do\n      @ldapconn = double('ldapconn')\n      @conn = double('connection', :connection => @ldapconn, :start => nil, :close => nil)\n      allow(Puppet::Util::Ldap::Connection).to receive(:new).and_return(@conn)\n    end\n\n    it \"should fail unless a block is given\" do\n      expect { @manager.connect }.to raise_error(ArgumentError, /must pass a block/)\n    end\n\n    it \"should open the connection with its server set to :ldapserver\" do\n      Puppet[:ldapserver] = \"myserver\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(\"myserver\", anything, anything).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should open the connection with its port set to the :ldapport\" do\n      Puppet[:ldapport] = 28\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, 28, anything).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should open the connection with no user if :ldapuser is not set\" do\n      Puppet[:ldapuser] = \"\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_excluding(:user)).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should open the connection with its user set to the :ldapuser if it is set\" do\n      Puppet[:ldapuser] = \"mypass\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(user: \"mypass\")).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should open the connection with no password if :ldappassword is not set\" do\n      Puppet[:ldappassword] = \"\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_excluding(:password)).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should open the connection with its password set to the :ldappassword if it is set\" do\n      Puppet[:ldappassword] = \"mypass\"\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(password: \"mypass\")).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should set ssl to :tls if ldaptls is enabled\" do\n      Puppet[:ldaptls] = true\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(ssl: :tls)).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should set ssl to true if ldapssl is enabled\" do\n      Puppet[:ldapssl] = true\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(ssl: true)).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should set ssl to false if neither ldaptls nor ldapssl is enabled\" do\n      Puppet[:ldapssl] = false\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).with(anything, anything, hash_including(ssl: false)).and_return(@conn)\n\n      @manager.connect { |c| }\n    end\n\n    it \"should open, yield, and then close the connection\" do\n      expect(@conn).to receive(:start)\n      expect(@conn).to receive(:close)\n      expect(Puppet::Util::Ldap::Connection).to receive(:new).and_return(@conn)\n      expect(@ldapconn).to receive(:test)\n      @manager.connect { |c| c.test }\n    end\n\n    it \"should close the connection even if there's an exception in the passed block\" do\n      expect(@conn).to receive(:close)\n      expect { @manager.connect { |c| raise ArgumentError } }.to raise_error(ArgumentError)\n    end\n  end\n\n  describe \"when using ldap\" do\n    before do\n      @conn = double('connection')\n      allow(@manager).to receive(:connect).and_yield(@conn)\n      allow(@manager).to receive(:objectclasses).and_return([:oc1, :oc2])\n      @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro\n    end\n\n    describe \"to create entries\" do\n      it \"should convert the first argument to its :create method to a full dn and pass the resulting argument list to its connection\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:add).with(\"mydn\", anything)\n\n        @manager.create(\"myname\", {\"attr\" => \"myattrs\"})\n      end\n\n      it \"should add the objectclasses to the attributes\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:add) do |_, attrs|\n          expect(attrs[\"objectClass\"]).to include(\"oc1\", \"oc2\")\n        end\n\n        @manager.create(\"myname\", {:one => :testing})\n      end\n\n      it \"should add the rdn to the attributes\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:add).with(anything, hash_including(\"cn\" => %w{myname}))\n\n        @manager.create(\"myname\", {:one => :testing})\n      end\n\n      it \"should add 'top' to the objectclasses if it is not listed\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:add) do |_, attrs|\n          expect(attrs[\"objectClass\"]).to include(\"top\")\n        end\n\n        @manager.create(\"myname\", {:one => :testing})\n      end\n\n      it \"should add any generated values that are defined\" do\n        generator = double('generator', :source => :one, :name => \"myparam\")\n\n        expect(Puppet::Util::Ldap::Generator).to receive(:new).with(:myparam).and_return(generator)\n\n        @manager.generates(:myparam)\n\n        allow(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n\n        expect(generator).to receive(:generate).with(:testing).and_return([\"generated value\"])\n        expect(@conn).to receive(:add).with(anything, hash_including(\"myparam\" => [\"generated value\"]))\n\n        @manager.create(\"myname\", {:one => :testing})\n      end\n\n      it \"should convert any generated values to arrays of strings if necessary\" do\n        generator = double('generator', :source => :one, :name => \"myparam\")\n\n        expect(Puppet::Util::Ldap::Generator).to receive(:new).with(:myparam).and_return(generator)\n\n        @manager.generates(:myparam)\n\n        allow(@manager).to receive(:dn).and_return(\"mydn\")\n\n        expect(generator).to receive(:generate).and_return(:generated)\n        expect(@conn).to receive(:add).with(anything, hash_including(\"myparam\" => [\"generated\"]))\n\n        @manager.create(\"myname\", {:one => :testing})\n      end\n    end\n\n    describe \"do delete entries\" do\n      it \"should convert the first argument to its :delete method to a full dn and pass the resulting argument list to its connection\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:delete).with(\"mydn\")\n\n        @manager.delete(\"myname\")\n      end\n    end\n\n    describe \"to modify entries\" do\n      it \"should convert the first argument to its :modify method to a full dn and pass the resulting argument list to its connection\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:modify).with(\"mydn\", :mymods)\n\n        @manager.modify(\"myname\", :mymods)\n      end\n    end\n\n    describe \"to find a single entry\" do\n      it \"should use the dn of the provided name as the search base, a scope of 0, and 'objectclass=*' as the filter for a search2 call\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:search2).with(\"mydn\", 0, \"objectclass=*\")\n\n        @manager.find(\"myname\")\n      end\n\n      it \"should return nil if an exception is thrown because no result is found\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        expect(@conn).to receive(:search2).and_raise(LDAP::ResultError)\n\n        expect(@manager.find(\"myname\")).to be_nil\n      end\n\n      it \"should return a converted provider hash if the result is found\" do\n        expect(@manager).to receive(:dn).with(\"myname\").and_return(\"mydn\")\n        result = {\"one\" => \"two\"}\n        expect(@conn).to receive(:search2).and_yield(result)\n\n        expect(@manager).to receive(:entry2provider).with(result).and_return(\"myprovider\")\n\n        expect(@manager.find(\"myname\")).to eq(\"myprovider\")\n      end\n    end\n\n    describe \"to search for multiple entries\" do\n      before do\n        allow(@manager).to receive(:filter).and_return(\"myfilter\")\n      end\n\n      it \"should use the manager's search base as the dn of the provided name as the search base\" do\n        expect(@manager).to receive(:base).and_return(\"mybase\")\n        expect(@conn).to receive(:search2).with(\"mybase\", anything, anything)\n\n        @manager.search\n      end\n\n      it \"should use a scope of 1\" do\n        expect(@conn).to receive(:search2).with(anything, 1, anything)\n\n        @manager.search\n      end\n\n      it \"should use any specified search filter\" do\n        expect(@manager).not_to receive(:filter)\n        expect(@conn).to receive(:search2).with(anything, anything, \"boo\")\n\n        @manager.search(\"boo\")\n      end\n\n      it \"should turn its objectclass list into its search filter if one is not specified\" do\n        expect(@manager).to receive(:filter).and_return(\"yay\")\n        expect(@conn).to receive(:search2).with(anything, anything, \"yay\")\n\n        @manager.search\n      end\n\n      it \"should return nil if no result is found\" do\n        expect(@conn).to receive(:search2)\n\n        expect(@manager.search).to be_nil\n      end\n\n      it \"should return an array of the found results converted to provider hashes\" do\n        # LAK: AFAICT, it's impossible to yield multiple times in an expectation.\n        one = {\"dn\" => \"cn=one,dc=madstop,dc=com\", \"one\" => \"two\"}\n        expect(@conn).to receive(:search2).and_yield(one)\n\n        expect(@manager).to receive(:entry2provider).with(one).and_return(\"myprov\")\n\n        expect(@manager.search).to eq([\"myprov\"])\n      end\n    end\n  end\n\n  describe \"when an instance\" do\n    before do\n      @name = \"myname\"\n      @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro\n    end\n\n    describe \"is being updated\" do\n      it \"should get created if the current attribute list is empty and the desired attribute list has :ensure == :present\" do\n        expect(@manager).to receive(:create)\n        @manager.update(@name, {}, {:ensure => :present})\n      end\n\n      it \"should get created if the current attribute list has :ensure == :absent and the desired attribute list has :ensure == :present\" do\n        expect(@manager).to receive(:create)\n        @manager.update(@name, {:ensure => :absent}, {:ensure => :present})\n      end\n\n      it \"should get deleted if the current attribute list has :ensure == :present and the desired attribute list has :ensure == :absent\" do\n        expect(@manager).to receive(:delete)\n        @manager.update(@name, {:ensure => :present}, {:ensure => :absent})\n      end\n\n      it \"should get modified if both attribute lists have :ensure == :present\" do\n        expect(@manager).to receive(:modify)\n        @manager.update(@name, {:ensure => :present, :one => :two}, {:ensure => :present, :one => :three})\n      end\n    end\n\n    describe \"is being deleted\" do\n      it \"should call the :delete method with its name and manager\" do\n        expect(@manager).to receive(:delete).with(@name)\n\n        @manager.update(@name, {}, {:ensure => :absent})\n      end\n    end\n\n    describe \"is being created\" do\n      before do\n        @is = {}\n        @should = {:ensure => :present, :one => :yay, :two => :absent}\n      end\n\n      it \"should call the :create method with its name\" do\n        expect(@manager).to receive(:create).with(@name, anything)\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should call the :create method with its property hash converted to ldap attribute names\" do\n        expect(@manager).to receive(:create).with(anything, hash_including(\"uno\" => [\"yay\"]))\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should not include :ensure in the properties sent\" do\n        expect(@manager).to receive(:create).with(anything, hash_excluding(:ensure))\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should not include attributes set to :absent in the properties sent\" do\n        expect(@manager).to receive(:create).with(anything, hash_excluding(:dos))\n        @manager.update(@name, @is, @should)\n      end\n    end\n\n    describe \"is being modified\" do\n      it \"should call the :modify method with its name and an array of LDAP::Mod instances\" do\n        allow(LDAP::Mod).to receive(:new).and_return(\"whatever\")\n\n        @is = {:one => :yay}\n        @should = {:one => :yay, :two => :foo}\n\n        expect(@manager).to receive(:modify).with(@name, anything)\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should create the LDAP::Mod with the property name converted to the ldap name as a string\" do\n        @is = {:one => :yay}\n        @should = {:one => :yay, :two => :foo}\n        mod = double('module')\n        expect(LDAP::Mod).to receive(:new).with(anything, \"dos\", anything).and_return(mod)\n\n        allow(@manager).to receive(:modify)\n\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should create an LDAP::Mod instance of type LDAP_MOD_ADD for each attribute being added, with the attribute value converted to a string of arrays\" do\n        @is = {:one => :yay}\n        @should = {:one => :yay, :two => :foo}\n        mod = double('module')\n        expect(LDAP::Mod).to receive(:new).with(LDAP::LDAP_MOD_ADD, \"dos\", [\"foo\"]).and_return(mod)\n\n        allow(@manager).to receive(:modify)\n\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should create an LDAP::Mod instance of type LDAP_MOD_DELETE for each attribute being deleted\" do\n        @is = {:one => :yay, :two => :foo}\n        @should = {:one => :yay, :two => :absent}\n        mod = double('module')\n        expect(LDAP::Mod).to receive(:new).with(LDAP::LDAP_MOD_DELETE, \"dos\", []).and_return(mod)\n\n        allow(@manager).to receive(:modify)\n\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should create an LDAP::Mod instance of type LDAP_MOD_REPLACE for each attribute being modified, with the attribute converted to a string of arrays\" do\n        @is = {:one => :yay, :two => :four}\n        @should = {:one => :yay, :two => :five}\n        mod = double('module')\n        expect(LDAP::Mod).to receive(:new).with(LDAP::LDAP_MOD_REPLACE, \"dos\", [\"five\"]).and_return(mod)\n\n        allow(@manager).to receive(:modify)\n\n        @manager.update(@name, @is, @should)\n      end\n\n      it \"should pass all created Mod instances to the modify method\" do\n        @is = {:one => :yay, :two => :foo, :three => :absent}\n        @should = {:one => :yay, :two => :foe, :three => :fee, :four => :fie}\n        expect(LDAP::Mod).to receive(:new).exactly(3).times().and_return(\"mod1\", \"mod2\", \"mod3\")\n\n        expect(@manager).to receive(:modify).with(anything, contain_exactly(*%w{mod1 mod2 mod3}))\n\n        @manager.update(@name, @is, @should)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/lockfile_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/lockfile'\n\nmodule LockfileSpecHelper\n  def self.run_in_forks(count, &blk)\n    forks = {}\n    results = []\n    count.times do |i|\n      forks[i] = {}\n      forks[i][:read], forks[i][:write] = IO.pipe\n\n      forks[i][:pid] = fork do\n        forks[i][:read].close\n        res = yield\n        Marshal.dump(res, forks[i][:write])\n        exit!\n      end\n    end\n\n    count.times do |i|\n      forks[i][:write].close\n      result = forks[i][:read].read\n      forks[i][:read].close\n      Process.wait2(forks[i][:pid])\n      results << Marshal.load(result)\n    end\n    results\n  end\nend\n\ndescribe Puppet::Util::Lockfile do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  before(:each) do\n    @lockfile = tmpfile(\"lock\")\n    @lock = Puppet::Util::Lockfile.new(@lockfile)\n  end\n\n  describe \"#lock\" do\n    it \"should return true if it successfully locked\" do\n      expect(@lock.lock).to be_truthy\n    end\n\n    it \"should return false if already locked\" do\n      @lock.lock\n      expect(@lock.lock).to be_falsey\n    end\n\n    it \"should create a lock file\" do\n      @lock.lock\n\n      expect(Puppet::FileSystem.exist?(@lockfile)).to be_truthy\n    end\n\n    # We test simultaneous locks using fork which isn't supported on Windows.\n    it \"should not be acquired by another process\", :unless => Puppet::Util::Platform.windows? || RUBY_PLATFORM == 'java' do\n      30.times do\n        forks = 3\n        results = LockfileSpecHelper.run_in_forks(forks) do\n          @lock.lock(Process.pid)\n        end\n        @lock.unlock\n\n        # Confirm one fork returned true and everyone else false.\n        expect((results - [true]).size).to eq(forks - 1)\n        expect((results - [false]).size).to eq(1)\n      end\n    end\n\n    it \"should create a lock file containing a string\" do\n      data = \"foofoo barbar\"\n      @lock.lock(data)\n\n      expect(File.read(@lockfile)).to eq(data)\n    end\n  end\n\n  describe \"#unlock\" do\n    it \"should return true when unlocking\" do\n      @lock.lock\n      expect(@lock.unlock).to be_truthy\n    end\n\n    it \"should return false when not locked\" do\n      expect(@lock.unlock).to be_falsey\n    end\n\n    it \"should clear the lock file\" do\n      File.open(@lockfile, 'w') { |fd| fd.print(\"locked\") }\n      @lock.unlock\n      expect(Puppet::FileSystem.exist?(@lockfile)).to be_falsey\n    end\n  end\n\n  it \"should be locked when locked\" do\n    @lock.lock\n    expect(@lock).to be_locked\n  end\n\n  it \"should not be locked when not locked\" do\n    expect(@lock).not_to be_locked\n  end\n\n  it \"should not be locked when unlocked\" do\n    @lock.lock\n    @lock.unlock\n    expect(@lock).not_to be_locked\n  end\n\n  it \"should return the lock data\" do\n    data = \"foofoo barbar\"\n    @lock.lock(data)\n    expect(@lock.lock_data).to eq(data)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/log/destinations_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/json'\n\nrequire 'puppet/util/log'\n\ndescribe Puppet::Util::Log.desttypes[:report] do\n  before do\n    @dest = Puppet::Util::Log.desttypes[:report]\n  end\n\n  it \"should require a report at initialization\" do\n    expect(@dest.new(\"foo\").report).to eq(\"foo\")\n  end\n\n  it \"should send new messages to the report\" do\n    report = double('report')\n    dest = @dest.new(report)\n\n    expect(report).to receive(:<<).with(\"my log\")\n\n    dest.handle \"my log\"\n  end\nend\n\ndescribe Puppet::Util::Log.desttypes[:file] do\n  include PuppetSpec::Files\n\n  before do\n    @class = Puppet::Util::Log.desttypes[:file]\n  end\n\n  it \"should default to autoflush false\" do\n    expect(@class.new(tmpfile('log')).autoflush).to eq(true)\n  end\n\n  describe \"when matching\" do\n    shared_examples_for \"file destination\" do\n      it \"should match an absolute path\" do\n        expect(@class.match?(abspath)).to be_truthy\n      end\n\n      it \"should not match a relative path\" do\n        expect(@class.match?(relpath)).to be_falsey\n      end\n    end\n\n    describe \"on POSIX systems\", :unless => Puppet::Util::Platform.windows? do\n      describe \"with a normal file\" do\n        let (:parent) { Pathname.new('/tmp') }\n        let (:abspath) { '/tmp/log' }\n        let (:relpath) { 'log' }\n\n        it_behaves_like \"file destination\"\n      end\n\n      describe \"with a JSON file\" do\n        let (:abspath) { '/tmp/log.json' }\n        let (:relpath) { 'log.json' }\n\n        it_behaves_like \"file destination\"\n\n        it \"should log messages as JSON\" do\n          msg = Puppet::Util::Log.new(:level => :info, :message => \"don't panic\")\n          dest = @class.new(abspath)\n          dest.handle(msg)\n          expect(JSON.parse(File.read(abspath) + ']')).to include(a_hash_including({\"message\" => \"don't panic\"}))\n        end\n      end\n\n      describe \"with a JSON lines file\" do\n        let (:abspath) { '/tmp/log.jsonl' }\n        let (:relpath) { 'log.jsonl' }\n\n        it_behaves_like \"file destination\"\n\n        it \"should log messages as JSON lines\" do\n          msg1 = Puppet::Util::Log.new(:level => :info, :message => \"don't panic\")\n          msg2 = Puppet::Util::Log.new(:level => :err, :message => \"panic!\")\n          dest = @class.new(abspath)\n          dest.handle(msg1)\n          dest.handle(msg2)\n          lines = IO.readlines(abspath)\n          expect(JSON.parse(lines[-2])).to include(\"level\" => \"info\", \"message\" => \"don't panic\")\n          expect(JSON.parse(lines[-1])).to include(\"level\" => \"err\", \"message\" => \"panic!\")\n        end\n      end\n    end\n\n    describe \"on Windows systems\", :if => Puppet::Util::Platform.windows? do\n      let (:abspath) { 'C:\\\\temp\\\\log.txt' }\n      let (:relpath) { 'log.txt' }\n\n      it_behaves_like \"file destination\"\n    end\n  end\nend\n\ndescribe Puppet::Util::Log.desttypes[:syslog] do\n  let (:klass) { Puppet::Util::Log.desttypes[:syslog] }\n\n  # these tests can only be run when syslog is present, because\n  # we can't stub the top-level Syslog module\n  describe \"when syslog is available\", :if => Puppet.features.syslog? do\n    before :each do\n      allow(Syslog).to receive(:opened?).and_return(false)\n      allow(Syslog).to receive(:const_get).and_return(\"LOG_KERN\", 0)\n      allow(Syslog).to receive(:open)\n    end\n\n    it \"should open syslog\" do\n      expect(Syslog).to receive(:open)\n\n      klass.new\n    end\n\n    it \"should close syslog\" do\n      expect(Syslog).to receive(:close)\n\n      dest = klass.new\n      dest.close\n    end\n\n    it \"should send messages to syslog\" do\n      syslog = double('syslog')\n      expect(syslog).to receive(:info).with(\"don't panic\")\n      allow(Syslog).to receive(:open).and_return(syslog)\n\n      msg = Puppet::Util::Log.new(:level => :info, :message => \"don't panic\")\n      dest = klass.new\n      dest.handle(msg)\n    end\n  end\n\n  describe \"when syslog is unavailable\" do\n    it \"should not be a suitable log destination\" do\n      allow(Puppet.features).to receive(:syslog?).and_return(false)\n\n      expect(klass.suitable?(:syslog)).to be_falsey\n    end\n  end\nend\n\ndescribe Puppet::Util::Log.desttypes[:logstash_event] do\n  describe \"when using structured log format with logstash_event schema\" do\n    before :each do\n      @msg = Puppet::Util::Log.new(:level => :info, :message => \"So long, and thanks for all the fish.\", :source => \"a dolphin\")\n    end\n\n    it \"format should fix the hash to have the correct structure\" do\n      dest = described_class.new\n      result = dest.format(@msg)\n      expect(result[\"version\"]).to eq(1)\n      expect(result[\"level\"]).to   eq('info')\n      expect(result[\"message\"]).to eq(\"So long, and thanks for all the fish.\")\n      expect(result[\"source\"]).to  eq(\"a dolphin\")\n      # timestamp should be within 10 seconds\n      expect(Time.parse(result[\"@timestamp\"])).to be >= ( Time.now - 10 )\n    end\n\n    it \"format returns a structure that can be converted to json\" do\n      dest = described_class.new\n      hash = dest.format(@msg)\n      Puppet::Util::Json.load(hash.to_json)\n    end\n\n    it \"handle should send the output to stdout\" do\n      expect($stdout).to receive(:puts).once\n      dest = described_class.new\n      dest.handle(@msg)\n    end\n  end\nend\n\ndescribe Puppet::Util::Log.desttypes[:console] do\n  let (:klass) { Puppet::Util::Log.desttypes[:console] }\n\n  it \"should support color output\" do\n    Puppet[:color] = true\n    expect(subject.colorize(:red, 'version')).to eq(\"\\e[0;31mversion\\e[0m\")\n  end\n\n  it \"should withhold color output when not appropriate\" do\n    Puppet[:color] = false\n    expect(subject.colorize(:red, 'version')).to eq(\"version\")\n  end\n\n  it \"should handle multiple overlapping colors in a stack-like way\" do\n    Puppet[:color] = true\n    vstring = subject.colorize(:red, 'version')\n    expect(subject.colorize(:green, \"(#{vstring})\")).to eq(\"\\e[0;32m(\\e[0;31mversion\\e[0;32m)\\e[0m\")\n  end\n\n  it \"should handle resets in a stack-like way\" do\n    Puppet[:color] = true\n    vstring = subject.colorize(:reset, 'version')\n    expect(subject.colorize(:green, \"(#{vstring})\")).to eq(\"\\e[0;32m(\\e[mversion\\e[0;32m)\\e[0m\")\n  end\n\n  it \"should include the log message's source/context in the output when available\" do\n    Puppet[:color] = false\n    expect($stdout).to receive(:puts).with(\"Info: a hitchhiker: don't panic\")\n\n    msg = Puppet::Util::Log.new(:level => :info, :message => \"don't panic\", :source => \"a hitchhiker\")\n    dest = klass.new\n    dest.handle(msg)\n  end\nend\n\n\ndescribe \":eventlog\", :if => Puppet::Util::Platform.windows? do\n  let(:klass) { Puppet::Util::Log.desttypes[:eventlog] }\n\n  def expects_message_with_type(klass, level, eventlog_type, eventlog_id)\n    eventlog = double('eventlog')\n    expect(eventlog).to receive(:report_event).with(hash_including(:event_type => eventlog_type, :event_id => eventlog_id, :data => \"a hitchhiker: don't panic\"))\n    allow(Puppet::Util::Windows::EventLog).to receive(:open).and_return(eventlog)\n\n    msg = Puppet::Util::Log.new(:level => level, :message => \"don't panic\", :source => \"a hitchhiker\")\n    dest = klass.new\n    dest.handle(msg)\n  end\n\n  it \"supports the eventlog feature\" do\n    expect(Puppet.features.eventlog?).to be_truthy\n  end\n\n  it \"should truncate extremely long log messages\" do\n    long_msg = \"x\" * 32000\n    expected_truncated_msg = \"#{'x' * 31785}...Message exceeds character length limit, truncating.\"\n    expected_data = \"a vogon ship: \" + expected_truncated_msg\n\n    eventlog = double('eventlog')\n    expect(eventlog).to receive(:report_event).with(hash_including(:event_type => 2, :event_id => 2, :data => expected_data))\n    msg = Puppet::Util::Log.new(:level => :warning, :message => long_msg, :source => \"a vogon ship\")\n    allow(Puppet::Util::Windows::EventLog).to receive(:open).and_return(eventlog)\n\n    dest = klass.new\n    dest.handle(msg)\n  end\n  \n  it \"logs to the Puppet Application event log\" do\n    expect(Puppet::Util::Windows::EventLog).to receive(:open).with('Puppet').and_return(double('eventlog'))\n\n    klass.new\n  end\n\n  it \"logs :debug level as an information type event\" do\n    expects_message_with_type(klass, :debug, klass::EVENTLOG_INFORMATION_TYPE, 0x1)\n  end\n\n  it \"logs :warning level as an warning type event\" do\n    expects_message_with_type(klass, :warning, klass::EVENTLOG_WARNING_TYPE, 0x2)\n  end\n\n  it \"logs :err level as an error type event\" do\n    expects_message_with_type(klass, :err, klass::EVENTLOG_ERROR_TYPE, 0x3)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/log_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\n\nrequire 'puppet/util/log'\n\ndescribe Puppet::Util::Log do\n  include PuppetSpec::Files\n\n  def log_notice(message)\n    Puppet::Util::Log.new(:level => :notice, :message => message)\n  end\n\n  it \"should write a given message to the specified destination\" do\n    arraydest = []\n    Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(arraydest))\n    Puppet::Util::Log.new(:level => :notice, :message => \"foo\")\n    message = arraydest.last.message\n    expect(message).to eq(\"foo\")\n  end\n\n  context \"given a message with invalid encoding\" do\n    let(:logs) { [] }\n    let(:invalid_message) { \"\\xFD\\xFBfoo\".force_encoding(Encoding::Shift_JIS) }\n\n    before do\n      Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(logs))\n      Puppet::Util::Log.new(:level => :notice, :message => invalid_message)\n    end\n\n    it \"does not raise an error\" do\n      expect { Puppet::Util::Log.new(:level => :notice, :message => invalid_message) }.not_to raise_error\n    end\n\n    it \"includes a backtrace in the log\" do\n      expect(logs.last.message).to match(/Backtrace:\\n.*in `newmessage'\\n.*in `initialize'/ )\n    end\n\n    it \"warns that message included invalid encoding\" do\n      expect(logs.last.message).to match(/Received a Log attribute with invalid encoding/)\n    end\n\n    it \"includes the 'dump' of the invalid message\" do\n      expect(logs.last.message).to match(/\\\"\\\\xFD\\\\xFBfoo\\\"/)\n    end\n  end\n\n  # need a string that cannot be converted to US-ASCII or other encodings easily\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ܎\n\n  it \"converts a given non-UTF-8 message to UTF-8\" do\n    logs = []\n    Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(logs))\n    Puppet::Util::Log.newdestination(:console)\n\n    # HIRAGANA LETTER SO\n    # In Windows_31J: \\x82 \\xbb - 130 187\n    # In Unicode: \\u305d - \\xe3 \\x81 \\x9d - 227 129 157\n    win_31j_msg = [130, 187].pack('C*').force_encoding(Encoding::Windows_31J)\n    utf_8_msg = \"\\u305d\"\n\n    expect($stdout).to receive(:puts).with(\"\\e[mNotice: #{mixed_utf8}: #{utf_8_msg}\\e[0m\")\n\n    # most handlers do special things with a :source => 'Puppet', so use something else\n    Puppet::Util::Log.new(:level => :notice, :message => win_31j_msg, :source => mixed_utf8)\n    expect(logs.last.message).to eq(utf_8_msg)\n  end\n\n  it \"converts a given non-UTF-8 source to UTF-8\" do\n    logs = []\n    Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(logs))\n    Puppet::Util::Log.newdestination(:console)\n\n    # HIRAGANA LETTER SO\n    # In Windows_31J: \\x82 \\xbb - 130 187\n    # In Unicode: \\u305d - \\xe3 \\x81 \\x9d - 227 129 157\n    win_31j_msg = [130, 187].pack('C*').force_encoding(Encoding::Windows_31J)\n    utf_8_msg = \"\\u305d\"\n\n    expect($stdout).to receive(:puts).with(\"\\e[mNotice: #{utf_8_msg}: #{mixed_utf8}\\e[0m\")\n\n    Puppet::Util::Log.new(:level => :notice, :message => mixed_utf8, :source => win_31j_msg)\n    expect(logs.last.source).to eq(utf_8_msg)\n  end\n\n  require 'puppet/util/log/destinations'\n\n  it \"raises an error when it has no successful logging destinations\" do\n    # spec_helper.rb redirects log output away from the console,\n    # so we have to stop that here, or else the logic we are testing\n    # will not be reached.\n    allow(Puppet::Util::Log).to receive(:destinations).and_return({})\n\n    our_exception = Puppet::DevError.new(\"test exception\")\n    expect(Puppet::FileSystem).to receive(:dir).and_raise(our_exception)\n    bad_file = tmpfile(\"bad_file\")\n\n    expect { Puppet::Util::Log.newdestination(bad_file) }.to raise_error(Puppet::DevError)\n  end\n\n  describe \".setup_default\" do\n    it \"should default to :syslog\" do\n      allow(Puppet.features).to receive(:syslog?).and_return(true)\n      expect(Puppet::Util::Log).to receive(:newdestination).with(:syslog)\n\n      Puppet::Util::Log.setup_default\n    end\n\n    it \"should fall back to :eventlog\" do\n      without_partial_double_verification do\n        allow(Puppet.features).to receive(:syslog?).and_return(false)\n        allow(Puppet.features).to receive(:eventlog?).and_return(true)\n      end\n      expect(Puppet::Util::Log).to receive(:newdestination).with(:eventlog)\n\n      Puppet::Util::Log.setup_default\n    end\n\n    it \"should fall back to :file\" do\n      without_partial_double_verification do\n        allow(Puppet.features).to receive(:syslog?).and_return(false)\n        allow(Puppet.features).to receive(:eventlog?).and_return(false)\n      end\n      expect(Puppet::Util::Log).to receive(:newdestination).with(Puppet[:puppetdlog])\n\n      Puppet::Util::Log.setup_default\n    end\n  end\n\n  describe \"#with_destination\" do\n    it \"does nothing when nested\" do\n      logs = []\n      destination = Puppet::Test::LogCollector.new(logs)\n      Puppet::Util::Log.with_destination(destination) do\n        Puppet::Util::Log.with_destination(destination) do\n          log_notice(\"Inner block\")\n        end\n\n        log_notice(\"Outer block\")\n      end\n\n      log_notice(\"Outside\")\n\n      expect(logs.collect(&:message)).to include(\"Inner block\", \"Outer block\")\n      expect(logs.collect(&:message)).not_to include(\"Outside\")\n    end\n\n    it \"logs when called a second time\" do\n      logs = []\n      destination = Puppet::Test::LogCollector.new(logs)\n\n      Puppet::Util::Log.with_destination(destination) do\n        log_notice(\"First block\")\n      end\n\n      log_notice(\"Between blocks\")\n\n      Puppet::Util::Log.with_destination(destination) do\n        log_notice(\"Second block\")\n      end\n\n      expect(logs.collect(&:message)).to include(\"First block\", \"Second block\")\n      expect(logs.collect(&:message)).not_to include(\"Between blocks\")\n    end\n\n    it \"doesn't close the destination if already set manually\" do\n      logs = []\n      destination = Puppet::Test::LogCollector.new(logs)\n\n      Puppet::Util::Log.newdestination(destination)\n      Puppet::Util::Log.with_destination(destination) do\n        log_notice \"Inner block\"\n      end\n\n      log_notice \"Outer block\"\n      Puppet::Util::Log.close(destination)\n\n      expect(logs.collect(&:message)).to include(\"Inner block\", \"Outer block\")\n    end\n  end\n\n  describe Puppet::Util::Log::DestConsole do\n    before do\n      @console = Puppet::Util::Log::DestConsole.new\n    end\n\n    it \"should colorize if Puppet[:color] is :ansi\" do\n      Puppet[:color] = :ansi\n\n      expect(@console.colorize(:alert, \"abc\")).to eq(\"\\e[0;31mabc\\e[0m\")\n    end\n\n    it \"should colorize if Puppet[:color] is 'yes'\" do\n      Puppet[:color] = \"yes\"\n\n      expect(@console.colorize(:alert, \"abc\")).to eq(\"\\e[0;31mabc\\e[0m\")\n    end\n\n    it \"should htmlize if Puppet[:color] is :html\" do\n      Puppet[:color] = :html\n\n      expect(@console.colorize(:alert, \"abc\")).to eq(\"<span style=\\\"color: #FFA0A0\\\">abc</span>\")\n    end\n\n    it \"should do nothing if Puppet[:color] is false\" do\n      Puppet[:color] = false\n\n      expect(@console.colorize(:alert, \"abc\")).to eq(\"abc\")\n    end\n\n    it \"should do nothing if Puppet[:color] is invalid\" do\n      Puppet[:color] = \"invalid option\"\n\n      expect(@console.colorize(:alert, \"abc\")).to eq(\"abc\")\n    end\n  end\n\n  describe Puppet::Util::Log::DestSyslog do\n    before do\n      @syslog = Puppet::Util::Log::DestSyslog.new\n    end\n  end\n\n  describe Puppet::Util::Log::DestEventlog, :if => Puppet.features.eventlog? do\n    before :each do\n      allow(Puppet::Util::Windows::EventLog).to receive(:open).and_return(double('mylog', :close => nil))\n    end\n\n    it \"should restrict its suitability to Windows\" do\n      allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n\n      expect(Puppet::Util::Log::DestEventlog.suitable?('whatever')).to eq(false)\n    end\n\n    it \"should open the 'Puppet' event log\" do\n      expect(Puppet::Util::Windows::EventLog).to receive(:open).with('Puppet')\n\n      Puppet::Util::Log.newdestination(:eventlog)\n    end\n\n    it \"should close the event log\" do\n      log = double('myeventlog')\n      expect(log).to receive(:close)\n      expect(Puppet::Util::Windows::EventLog).to receive(:open).and_return(log)\n\n      Puppet::Util::Log.newdestination(:eventlog)\n      Puppet::Util::Log.close(:eventlog)\n    end\n\n    it \"should handle each puppet log level\" do\n      log = Puppet::Util::Log::DestEventlog.new\n\n      Puppet::Util::Log.eachlevel do |level|\n        expect(log.to_native(level)).to be_is_a(Array)\n      end\n    end\n  end\n\n  describe \"instances\" do\n    before do\n      allow(Puppet::Util::Log).to receive(:newmessage)\n    end\n\n    [:level, :message, :time, :remote].each do |attr|\n      it \"should have a #{attr} attribute\" do\n        log = Puppet::Util::Log.new :level => :notice, :message => \"A test message\"\n        expect(log).to respond_to(attr)\n        expect(log).to respond_to(attr.to_s + \"=\")\n      end\n    end\n\n    it \"should fail if created without a level\" do\n      expect { Puppet::Util::Log.new(:message => \"A test message\") }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if created without a message\" do\n      expect { Puppet::Util::Log.new(:level => :notice) }.to raise_error(ArgumentError)\n    end\n\n    it \"should make available the level passed in at initialization\" do\n      expect(Puppet::Util::Log.new(:level => :notice, :message => \"A test message\").level).to eq(:notice)\n    end\n\n    it \"should make available the message passed in at initialization\" do\n      expect(Puppet::Util::Log.new(:level => :notice, :message => \"A test message\").message).to eq(\"A test message\")\n    end\n\n    # LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code,\n    # at least at first.\n    it \"should always convert messages to strings\" do\n      expect(Puppet::Util::Log.new(:level => :notice, :message => :foo).message).to eq(\"foo\")\n    end\n\n    it \"should flush the log queue when the first destination is specified\" do\n      Puppet::Util::Log.close_all\n      expect(Puppet::Util::Log).to receive(:flushqueue)\n      Puppet::Util::Log.newdestination(:console)\n    end\n\n    it \"should convert the level to a symbol if it's passed in as a string\" do\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo).level).to eq(:notice)\n    end\n\n    it \"should fail if the level is not a symbol or string\" do\n      expect { Puppet::Util::Log.new(:level => 50, :message => :foo) }.to raise_error(ArgumentError)\n    end\n\n    it \"should fail if the provided level is not valid\" do\n      expect(Puppet::Util::Log).to receive(:validlevel?).with(:notice).and_return(false)\n      expect { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.to raise_error(ArgumentError)\n    end\n\n    it \"should set its time to the initialization time\" do\n      time = double('time')\n      expect(Time).to receive(:now).and_return(time)\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo).time).to equal(time)\n    end\n\n    it \"should make available any passed-in tags\" do\n      log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :tags => %w{foo bar})\n      expect(log.tags).to be_include(\"foo\")\n      expect(log.tags).to be_include(\"bar\")\n    end\n\n    it \"should use a passed-in source\" do\n      expect_any_instance_of(Puppet::Util::Log).to receive(:source=).with(\"foo\")\n      Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :source => \"foo\")\n    end\n\n    [:file, :line].each do |attr|\n      it \"should use #{attr} if provided\" do\n        expect_any_instance_of(Puppet::Util::Log).to receive(attr.to_s + \"=\").with(\"foo\")\n        Puppet::Util::Log.new(:level => \"notice\", :message => :foo, attr => \"foo\")\n      end\n    end\n\n    it \"should default to 'Puppet' as its source\" do\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo).source).to eq(\"Puppet\")\n    end\n\n    it \"should register itself with Log\" do\n      expect(Puppet::Util::Log).to receive(:newmessage)\n      Puppet::Util::Log.new(:level => \"notice\", :message => :foo)\n    end\n\n    it \"should update Log autoflush when Puppet[:autoflush] is set\" do\n      expect(Puppet::Util::Log).to receive(:autoflush=).once.with(true)\n      Puppet[:autoflush] = true\n    end\n\n    it \"should have a method for determining if a tag is present\" do\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo)).to respond_to(:tagged?)\n    end\n\n    it \"should match a tag if any of the tags are equivalent to the passed tag as a string\" do\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :tags => %w{one two})).to be_tagged(:one)\n    end\n\n    it \"should tag itself with its log level\" do\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo)).to be_tagged(:notice)\n    end\n\n    it \"should return its message when converted to a string\" do\n      expect(Puppet::Util::Log.new(:level => \"notice\", :message => :foo).to_s).to eq(\"foo\")\n    end\n\n    it \"should include its time, source, level, and message when prepared for reporting\" do\n      log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo)\n      report = log.to_report\n      expect(report).to be_include(\"notice\")\n      expect(report).to be_include(\"foo\")\n      expect(report).to be_include(log.source)\n      expect(report).to be_include(log.time.to_s)\n    end\n\n    it \"should not create unsuitable log destinations\" do\n      allow(Puppet.features).to receive(:syslog?).and_return(false)\n\n      expect(Puppet::Util::Log::DestSyslog).to receive(:suitable?)\n      expect(Puppet::Util::Log::DestSyslog).not_to receive(:new)\n\n      Puppet::Util::Log.newdestination(:syslog)\n    end\n\n    describe \"when setting the source as a RAL object\" do\n      let(:path) { File.expand_path('/foo/bar') }\n\n      it \"should tag itself with any tags the source has\" do\n        source = Puppet::Type.type(:file).new :path => path\n        log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :source => source)\n        source.tags.each do |tag|\n          expect(log.tags).to be_include(tag)\n        end\n      end\n\n      it \"should set the source to a type's 'path', when available\" do\n        source = Puppet::Type.type(:file).new :path => path\n        source.tags = [\"tag\", \"tag2\"]\n\n        log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo)\n        log.source = source\n\n        expect(log).to be_tagged('file')\n        expect(log).to be_tagged('tag')\n        expect(log).to be_tagged('tag2')\n\n        expect(log.source).to eq(\"/File[#{path}]\")\n      end\n\n      it \"should set the source to a provider's type's 'path', when available\" do\n        source = Puppet::Type.type(:file).new :path => path\n        source.tags = [\"tag\", \"tag2\"]\n\n        log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo)\n\n        log.source = source.provider\n\n        expect(log.source).to match Regexp.quote(\"File\\[#{path}\\]\\(provider=\")\n      end\n\n      it \"should copy over any file and line information\" do\n        source = Puppet::Type.type(:file).new :path => path\n        source.file = \"/my/file\"\n        source.line = 50\n        log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :source => source)\n        expect(log.line).to eq(50)\n        expect(log.file).to eq(\"/my/file\")\n      end\n    end\n\n    describe \"when setting the source as a non-RAL object\" do\n      it \"should not try to copy over file, version, line, or tag information\" do\n        source = double('source')\n        expect(source).not_to receive(:file)\n        Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :source => source)\n      end\n    end\n  end\n\n  describe \"to_yaml\" do\n    it \"should not include the @version attribute\" do\n      log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :version => 100)\n      expect(log.to_data_hash.keys).not_to include('version')\n    end\n\n    it \"should include attributes 'file', 'line', 'level', 'message', 'source', 'tags', and 'time'\" do\n      log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :version => 100)\n      expect(log.to_data_hash.keys).to match_array(%w(file line level message source tags time))\n    end\n\n    it \"should include attributes 'file' and 'line' if specified\" do\n      log = Puppet::Util::Log.new(:level => \"notice\", :message => :foo, :file => \"foo\", :line => 35)\n      expect(log.to_data_hash.keys).to include('file')\n      expect(log.to_data_hash.keys).to include('line')\n    end\n  end\n\n  let(:log) { Puppet::Util::Log.new(:level => 'notice', :message => 'hooray', :file => 'thefile', :line => 1729, :source => 'specs', :tags => ['a', 'b', 'c']) }\n\n  it \"should round trip through json\" do\n    tripped = Puppet::Util::Log.from_data_hash(JSON.parse(log.to_json))\n\n    expect(tripped.file).to eq(log.file)\n    expect(tripped.line).to eq(log.line)\n    expect(tripped.level).to eq(log.level)\n    expect(tripped.message).to eq(log.message)\n    expect(tripped.source).to eq(log.source)\n    expect(tripped.tags).to eq(log.tags)\n    expect(tripped.time).to eq(log.time)\n  end\n\n  it 'to_data_hash returns value that is instance of to Data' do\n    expect(Puppet::Pops::Types::TypeFactory.data.instance?(log.to_data_hash)).to be_truthy\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/logging_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/logging'\n\nPuppet::Type.newtype(:logging_test) do\n  newparam(:name, isnamevar: true)\n  newproperty(:path)\nend\nPuppet::Type.type(:logging_test).provide(:logging_test) do\nend\n\nclass LoggingTester\n  include Puppet::Util::Logging\nend\n\nclass PuppetStackCreator\n  def raise_error(exception_class)\n    case exception_class\n    when Puppet::ParseErrorWithIssue\n      raise exception_class.new('Oops', '/tmp/test.pp', 30, 15, nil, :SYNTAX_ERROR)\n    when Puppet::ParseError\n      raise exception_class.new('Oops', '/tmp/test.pp', 30, 15)\n    else\n      raise exception_class.new('Oops')\n    end\n  end\n\n  def call_raiser(exception_class)\n    Puppet::Pops::PuppetStack.stack('/tmp/test2.pp', 20, self, :raise_error, [exception_class])\n  end\n\n  def two_frames_and_a_raise(exception_class)\n    Puppet::Pops::PuppetStack.stack('/tmp/test3.pp', 15, self, :call_raiser, [exception_class])\n  end\n\n  def outer_rescue(exception_class)\n    begin\n      two_frames_and_a_raise(exception_class)\n    rescue Puppet::Error => e\n      Puppet.log_exception(e)\n    end\n  end\n\n  def run(exception_class)\n    Puppet::Pops::PuppetStack.stack('/tmp/test4.pp', 10, self, :outer_rescue, [exception_class])\n  end\nend\n\ndescribe Puppet::Util::Logging do\n  before do\n    @logger = LoggingTester.new\n  end\n\n  Puppet::Util::Log.eachlevel do |level|\n    it \"should have a method for sending '#{level}' logs\" do\n      expect(@logger).to respond_to(level)\n    end\n  end\n\n  it \"should have a method for sending a log with a specified log level\" do\n    expect(@logger).to receive(:to_s).and_return(\"I'm a string!\")\n    expect(Puppet::Util::Log).to receive(:create).with(hash_including(source: \"I'm a string!\", level: \"loglevel\", message: \"mymessage\"))\n\n    @logger.send_log \"loglevel\", \"mymessage\"\n  end\n\n  describe \"when sending a log\" do\n    it \"should use the Log's 'create' entrance method\" do\n      expect(Puppet::Util::Log).to receive(:create)\n\n      @logger.notice \"foo\"\n    end\n\n    it \"should send itself converted to a string as the log source\" do\n      expect(@logger).to receive(:to_s).and_return(\"I'm a string!\")\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(source: \"I'm a string!\"))\n\n      @logger.notice \"foo\"\n    end\n\n    it \"should queue logs sent without a specified destination\" do\n      Puppet::Util::Log.close_all\n      expect(Puppet::Util::Log).to receive(:queuemessage)\n\n      @logger.notice \"foo\"\n    end\n\n    it \"should use the path of any provided resource type\" do\n      resource = Puppet::Type.type(:logging_test).new :name => \"foo\"\n\n      expect(resource).to receive(:path).and_return(\"/path/to/host\".to_sym)\n\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(source: \"/path/to/host\"))\n\n      resource.notice \"foo\"\n    end\n\n    it \"should use the path of any provided resource parameter\" do\n      resource = Puppet::Type.type(:logging_test).new :name => \"foo\"\n\n      param = resource.parameter(:name)\n\n      expect(param).to receive(:path).and_return(\"/path/to/param\".to_sym)\n\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(source: \"/path/to/param\"))\n\n      param.notice \"foo\"\n    end\n\n    it \"should send the provided argument as the log message\" do\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(message: \"foo\"))\n\n      @logger.notice \"foo\"\n    end\n\n    it \"should join any provided arguments into a single string for the message\" do\n      expect(Puppet::Util::Log).to receive(:create).with(hash_including(message: \"foo bar baz\"))\n\n      @logger.notice [\"foo\", \"bar\", \"baz\"]\n    end\n\n    [:file, :line, :tags].each do |attr|\n      it \"should include #{attr} if available\" do\n        @logger.singleton_class.send(:attr_accessor, attr)\n\n        @logger.send(attr.to_s + \"=\", \"myval\")\n\n        expect(Puppet::Util::Log).to receive(:create).with(hash_including(attr => \"myval\"))\n        @logger.notice \"foo\"\n      end\n    end\n  end\n\n  describe \"log_exception\" do\n    context \"when requesting a debug level it is logged at debug\" do\n      it \"the exception is a ParseErrorWithIssue and message is :default\" do\n        expect(Puppet::Util::Log).to receive(:create) do |args|\n          expect(args[:message]).to eq(\"Test\")\n          expect(args[:level]).to eq(:debug)\n        end\n\n        begin\n          raise Puppet::ParseErrorWithIssue, \"Test\"\n        rescue Puppet::ParseErrorWithIssue => err\n          Puppet.log_exception(err, :default, level: :debug)\n        end\n      end\n\n      it \"the exception is something else\" do\n        expect(Puppet::Util::Log).to receive(:create) do |args|\n          expect(args[:message]).to eq(\"Test\")\n          expect(args[:level]).to eq(:debug)\n        end\n\n        begin\n          raise Puppet::Error, \"Test\"\n        rescue Puppet::Error => err\n          Puppet.log_exception(err, :default, level: :debug)\n        end\n      end\n    end\n\n    context \"no log level is requested it defaults to err\" do\n      it \"the exception is a ParseErrorWithIssue and message is :default\" do\n        expect(Puppet::Util::Log).to receive(:create) do |args|\n          expect(args[:message]).to eq(\"Test\")\n          expect(args[:level]).to eq(:err)\n        end\n\n        begin\n          raise Puppet::ParseErrorWithIssue, \"Test\"\n        rescue Puppet::ParseErrorWithIssue => err\n          Puppet.log_exception(err)\n        end\n      end\n\n      it \"the exception is something else\" do\n        expect(Puppet::Util::Log).to receive(:create) do |args|\n          expect(args[:message]).to eq(\"Test\")\n          expect(args[:level]).to eq(:err)\n        end\n\n        begin\n          raise Puppet::Error, \"Test\"\n        rescue Puppet::Error => err\n          Puppet.log_exception(err)\n        end\n      end\n    end\n  end\n\n  describe \"when sending a deprecation warning\" do\n    it \"does not log a message when deprecation warnings are disabled\" do\n      expect(Puppet).to receive(:[]).with(:disable_warnings).and_return(%w[deprecations])\n      expect(@logger).not_to receive(:warning)\n      @logger.deprecation_warning 'foo'\n    end\n\n    it \"logs the message with warn\" do\n      expect(@logger).to receive(:warning).with(/^foo\\n/)\n      @logger.deprecation_warning 'foo'\n    end\n\n    it \"only logs each offending line once\" do\n      expect(@logger).to receive(:warning).with(/^foo\\n/).once\n      5.times { @logger.deprecation_warning 'foo' }\n    end\n\n    it \"ensures that deprecations from same origin are logged if their keys differ\" do\n      expect(@logger).to receive(:warning).with(/deprecated foo/).exactly(5).times()\n      5.times { |i| @logger.deprecation_warning('deprecated foo', :key => \"foo#{i}\") }\n    end\n\n    it \"does not duplicate deprecations for a given key\" do\n      expect(@logger).to receive(:warning).with(/deprecated foo/).once\n      5.times { @logger.deprecation_warning('deprecated foo', :key => 'foo-msg') }\n    end\n\n    it \"only logs the first 100 messages\" do\n      (1..100).each { |i|\n        expect(@logger).to receive(:warning).with(/^#{i}\\n/).once\n        # since the deprecation warning will only log each offending line once, we have to do some tomfoolery\n        # here in order to make it think each of these calls is coming from a unique call stack; we're basically\n        # mocking the method that it would normally use to find the call stack.\n        expect(@logger).to receive(:get_deprecation_offender).and_return([\"deprecation log count test ##{i}\"])\n        @logger.deprecation_warning i\n      }\n      expect(@logger).not_to receive(:warning).with(101)\n      @logger.deprecation_warning 101\n    end\n  end\n\n  describe \"when sending a puppet_deprecation_warning\" do\n    it \"requires file and line or key options\" do\n      expect do\n        @logger.puppet_deprecation_warning(\"foo\")\n      end.to raise_error(Puppet::DevError, /Need either :file and :line, or :key/)\n      expect do\n        @logger.puppet_deprecation_warning(\"foo\", :file => 'bar')\n      end.to raise_error(Puppet::DevError, /Need either :file and :line, or :key/)\n      expect do\n        @logger.puppet_deprecation_warning(\"foo\", :key => 'akey')\n        @logger.puppet_deprecation_warning(\"foo\", :file => 'afile', :line => 1)\n      end.to_not raise_error\n    end\n\n    it \"warns with file and line\" do\n      expect(@logger).to receive(:warning).with(/deprecated foo.*\\(file: afile, line: 5\\)/m)\n      @logger.puppet_deprecation_warning(\"deprecated foo\", :file => 'afile', :line => 5)\n    end\n\n    it \"warns keyed from file and line\" do\n      expect(@logger).to receive(:warning).with(/deprecated foo.*\\(file: afile, line: 5\\)/m).once\n      5.times do\n        @logger.puppet_deprecation_warning(\"deprecated foo\", :file => 'afile', :line => 5)\n      end\n    end\n\n    it \"warns with separate key only once regardless of file and line\" do\n      expect(@logger).to receive(:warning).with(/deprecated foo.*\\(file: afile, line: 5\\)/m).once\n      @logger.puppet_deprecation_warning(\"deprecated foo\", :key => 'some_key', :file => 'afile', :line => 5)\n      @logger.puppet_deprecation_warning(\"deprecated foo\", :key => 'some_key', :file => 'bfile', :line => 3)\n    end\n\n    it \"warns with key but no file and line\" do\n      expect(@logger).to receive(:warning).with(/deprecated foo.*\\(file: unknown, line: unknown\\)/m)\n      @logger.puppet_deprecation_warning(\"deprecated foo\", :key => 'some_key')\n    end\n  end\n\n  describe \"when sending a warn_once\" do\n    before(:each) {\n      @logger.clear_deprecation_warnings\n    }\n\n    it \"warns with file when only file is given\" do\n      expect(@logger).to receive(:send_log).with(:warning, /wet paint.*\\(file: aFile\\)/m)\n      @logger.warn_once('kind', 'wp', \"wet paint\", 'aFile')\n    end\n\n    it \"warns with unknown file and line when only line is given\" do\n      expect(@logger).to receive(:send_log).with(:warning, /wet paint.*\\(line: 5\\)/m)\n      @logger.warn_once('kind', 'wp', \"wet paint\", nil, 5)\n    end\n\n    it \"warns with file and line when both are given\" do\n      expect(@logger).to receive(:send_log).with(:warning, /wet paint.*\\(file: aFile, line: 5\\)/m)\n      @logger.warn_once('kind', 'wp', \"wet paint\",'aFile', 5)\n    end\n\n    it \"warns once per key\" do\n      expect(@logger).to receive(:send_log).with(:warning, /wet paint.*/m).once\n      5.times do\n        @logger.warn_once('kind', 'wp', \"wet paint\")\n      end\n    end\n\n    Puppet::Util::Log.eachlevel do |level|\n      it \"can use log level #{level}\" do\n        expect(@logger).to receive(:send_log).with(level, /wet paint.*/m).once\n        5.times do\n          @logger.warn_once('kind', 'wp', \"wet paint\", nil, nil, level)\n        end\n      end\n    end\n  end\n\n  describe \"does not warn about undefined variables when disabled_warnings says so\" do\n    let(:logger) { LoggingTester.new }\n\n    before(:each) do\n      Puppet.settings.initialize_global_settings\n      logger.clear_deprecation_warnings\n      Puppet[:disable_warnings] = ['undefined_variables']\n    end\n\n    after(:each) do\n      Puppet[:disable_warnings] = []\n      allow(logger).to receive(:send_log).and_call_original()\n      allow(Facter).to receive(:respond_to?).and_call_original()\n      allow(Facter).to receive(:debugging).and_call_original()\n    end\n\n    it \"does not produce warning if kind is disabled\" do\n      expect(logger).not_to receive(:send_log)\n      logger.warn_once('undefined_variables', 'wp', \"wet paint\")\n    end\n  end\n\n  describe \"warns about undefined variables when deprecations are in disabled_warnings\" do\n    let(:logger) { LoggingTester.new }\n\n    before(:each) do\n      Puppet.settings.initialize_global_settings\n      logger.clear_deprecation_warnings\n      Puppet[:disable_warnings] = ['deprecations']\n    end\n\n    after(:each) do\n      Puppet[:disable_warnings] = []\n      allow(logger).to receive(:send_log).and_call_original()\n      allow(Facter).to receive(:respond_to?).and_call_original()\n      allow(Facter).to receive(:debugging).and_call_original()\n    end\n\n    it \"produces warning even if deprecation warnings are disabled \" do\n      expect(logger).to receive(:send_log).with(:warning, /wet paint/).once\n      logger.warn_once('undefined_variables', 'wp', \"wet paint\")\n    end\n  end\n\n  describe \"when formatting exceptions\" do\n    it \"should be able to format a chain of exceptions\" do\n      exc3 = Puppet::Error.new(\"original\")\n      exc3.set_backtrace([\"1.rb:4:in `a'\",\"2.rb:2:in `b'\",\"3.rb:1\"])\n      exc2 = Puppet::Error.new(\"second\", exc3)\n      exc2.set_backtrace([\"4.rb:8:in `c'\",\"5.rb:1:in `d'\",\"6.rb:3\"])\n      exc1 = Puppet::Error.new(\"third\", exc2)\n      exc1.set_backtrace([\"7.rb:31:in `e'\",\"8.rb:22:in `f'\",\"9.rb:9\"])\n      # whoa ugly\n      expect(@logger.format_exception(exc1)).to match(/third\n.*7\\.rb:31:in `e'\n.*8\\.rb:22:in `f'\n.*9\\.rb:9\nWrapped exception:\nsecond\n.*4\\.rb:8:in `c'\n.*5\\.rb:1:in `d'\n.*6\\.rb:3\nWrapped exception:\noriginal\n.*1\\.rb:4:in `a'\n.*2\\.rb:2:in `b'\n.*3\\.rb:1/)\n    end\n\n    describe \"when trace is disabled\" do\n      it 'excludes backtrace for RuntimeError in log message' do\n        begin\n          raise RuntimeError, 'Oops'\n        rescue RuntimeError => e\n          Puppet.log_exception(e)\n        end\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n        expect(log.message).to_not match('/logging_spec.rb')\n        expect(log.backtrace).to be_nil\n      end\n\n      it \"backtrace member is unset when logging ParseErrorWithIssue\" do\n        begin\n          raise Puppet::ParseErrorWithIssue.new('Oops', '/tmp/test.pp', 30, 15, nil, :SYNTAX_ERROR)\n        rescue RuntimeError => e\n          Puppet.log_exception(e)\n        end\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n        expect(log.message).to_not match('/logging_spec.rb')\n        expect(log.backtrace).to be_nil\n      end\n    end\n\n    describe \"when trace is enabled\" do\n      it 'includes backtrace for RuntimeError in log message when enabled globally' do\n        Puppet[:trace] = true\n        begin\n          raise RuntimeError, 'Oops'\n        rescue RuntimeError => e\n          Puppet.log_exception(e, :default)\n        end\n        Puppet[:trace] = false\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n        expect(log.message).to match('/logging_spec.rb')\n        expect(log.backtrace).to be_nil\n      end\n\n      it 'includes backtrace for RuntimeError in log message when enabled via option' do\n        begin\n          raise RuntimeError, 'Oops'\n        rescue RuntimeError => e\n          Puppet.log_exception(e, :default, :trace => true)\n        end\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n        expect(log.message).to match('/logging_spec.rb')\n        expect(log.backtrace).to be_nil\n      end\n\n\n      it \"backtrace member is set when logging ParseErrorWithIssue\" do\n        begin\n          raise Puppet::ParseErrorWithIssue.new('Oops', '/tmp/test.pp', 30, 15, nil, :SYNTAX_ERROR)\n        rescue RuntimeError => e\n          Puppet.log_exception(e, :default, :trace => true)\n        end\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n        expect(log.message).to_not match('/logging_spec.rb')\n        expect(log.backtrace).to be_a(Array)\n        expect(log.backtrace[0]).to match('/logging_spec.rb')\n      end\n      it \"backtrace has interleaved PuppetStack when logging ParseErrorWithIssue\" do\n        Puppet[:trace] = true\n        PuppetStackCreator.new.run(Puppet::ParseErrorWithIssue)\n        Puppet[:trace] = false\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n        expect(log.message).to_not match('/logging_spec.rb')\n        expect(log.backtrace[0]).to match('/logging_spec.rb')\n\n        expect(log.backtrace[1]).to match('/tmp/test2.pp:20')\n        puppetstack = log.backtrace.select { |l| l =~ /tmp\\/test\\d\\.pp/ }\n\n        expect(puppetstack.length).to equal 3\n      end\n\n      it \"message has interleaved PuppetStack when logging ParseError\" do\n        Puppet[:trace] = true\n        PuppetStackCreator.new.run(Puppet::ParseError)\n        Puppet[:trace] = false\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n\n        log_lines = log.message.split(\"\\n\")\n        expect(log_lines[1]).to match('/logging_spec.rb')\n        expect(log_lines[2]).to match('/tmp/test2.pp:20')\n        puppetstack = log_lines.select { |l| l =~ /tmp\\/test\\d\\.pp/ }\n\n        expect(puppetstack.length).to equal 3\n      end\n    end\n\n    describe \"when trace is disabled but puppet_trace is enabled\" do\n      it \"includes only PuppetStack as backtrace member with ParseErrorWithIssue\" do\n        Puppet[:trace] = false\n        Puppet[:puppet_trace] = true\n        PuppetStackCreator.new.run(Puppet::ParseErrorWithIssue)\n        Puppet[:trace] = false\n        Puppet[:puppet_trace] = false\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n\n        expect(log.backtrace[0]).to match('/tmp/test2.pp:20')\n        expect(log.backtrace.length).to equal 3\n      end\n\n      it \"includes only PuppetStack in message with ParseError\" do\n        Puppet[:trace] = false\n        Puppet[:puppet_trace] = true\n        PuppetStackCreator.new.run(Puppet::ParseError)\n        Puppet[:trace] = false\n        Puppet[:puppet_trace] = false\n\n        expect(@logs.size).to eq(1)\n        log = @logs[0]\n\n        log_lines = log.message.split(\"\\n\")\n        expect(log_lines[1]).to match('/tmp/test2.pp:20')\n        puppetstack = log_lines.select { |l| l =~ /tmp\\/test\\d\\.pp/ }\n\n        expect(puppetstack.length).to equal 3\n      end\n    end\n\n    it 'includes position details for ParseError in log message' do\n      begin\n        raise Puppet::ParseError.new('Oops', '/tmp/test.pp', 30, 15)\n      rescue RuntimeError => e\n        Puppet.log_exception(e)\n      end\n\n      expect(@logs.size).to eq(1)\n      log = @logs[0]\n      expect(log.message).to match(/ \\(file: \\/tmp\\/test\\.pp, line: 30, column: 15\\)/)\n      expect(log.message).to be(log.to_s)\n    end\n\n    it 'excludes position details for ParseErrorWithIssue from log message' do\n      begin\n        raise Puppet::ParseErrorWithIssue.new('Oops', '/tmp/test.pp', 30, 15, nil, :SYNTAX_ERROR)\n      rescue RuntimeError => e\n        Puppet.log_exception(e)\n      end\n\n      expect(@logs.size).to eq(1)\n      log = @logs[0]\n      expect(log.message).to_not match(/ \\(file: \\/tmp\\/test\\.pp, line: 30, column: 15\\)/)\n      expect(log.to_s).to match(/ \\(file: \\/tmp\\/test\\.pp, line: 30, column: 15\\)/)\n      expect(log.issue_code).to eq(:SYNTAX_ERROR)\n      expect(log.file).to eq('/tmp/test.pp')\n      expect(log.line).to eq(30)\n      expect(log.pos).to eq(15)\n    end\n  end\n\n  describe 'when Facter' do\n    after :each do\n      # Unstub these calls as there is global code run after\n      # each spec that may reset the log level to debug\n      allow(Facter).to receive(:respond_to?).and_call_original()\n      allow(Facter).to receive(:debugging).and_call_original()\n    end\n\n    describe 'does support debugging' do\n      before :each do\n        allow(Facter).to receive(:respond_to?).with(:on_message).and_return(true)\n        allow(Facter).to receive(:respond_to?).with(:debugging, any_args).and_return(true)\n      end\n\n      it 'enables Facter debugging when debug level' do\n        allow(Facter).to receive(:debugging).with(true)\n        Puppet::Util::Log.level = :debug\n      end\n\n      it 'disables Facter debugging when not debug level' do\n        allow(Facter).to receive(:debugging).with(false)\n        Puppet::Util::Log.level = :info\n      end\n    end\n\n    describe 'does support trace' do\n      before :each do\n        allow(Facter).to receive(:respond_to?).with(:on_message)\n        allow(Facter).to receive(:respond_to?).with(:trace, any_args).and_return(true)\n      end\n\n      it 'enables Facter trace when enabled' do\n        allow(Facter).to receive(:trace).with(true)\n        Puppet[:trace] = true\n      end\n\n      it 'disables Facter trace when disabled' do\n        allow(Facter).to receive(:trace).with(false)\n        Puppet[:trace] = false\n      end\n    end\n\n    describe 'does support on_message' do\n      before :each do\n        allow(Facter).to receive(:respond_to?).with(:on_message, any_args).and_return(true)\n      end\n\n      def setup(level, message)\n        allow(Facter).to receive(:on_message).and_yield(level, message)\n\n        # Transform from Facter level to Puppet level\n        case level\n        when :trace\n          level = :debug\n        when :warn\n          level = :warning\n        when :error\n          level = :err\n        when :fatal\n          level = :crit\n        end\n\n        allow(Puppet::Util::Log).to receive(:create).with(hash_including(level: level, message: message, source: 'Facter')).once\n      end\n\n      [:trace, :debug, :info, :warn, :error, :fatal].each do |level|\n        it \"calls Facter.on_message and handles #{level} messages\" do\n          setup(level, \"#{level} message\")\n          expect(Puppet::Util::Logging::setup_facter_logging!).to be_truthy\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/metric_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/metric'\n\ndescribe Puppet::Util::Metric do\n  before do\n    @metric = Puppet::Util::Metric.new(\"foo\")\n  end\n\n  [:type, :name, :value, :label].each do |name|\n    it \"should have a #{name} attribute\" do\n      expect(@metric).to respond_to(name)\n      expect(@metric).to respond_to(name.to_s + \"=\")\n    end\n  end\n\n  it \"should require a name at initialization\" do\n    expect { Puppet::Util::Metric.new }.to raise_error(ArgumentError)\n  end\n\n  it \"should always convert its name to a string\" do\n    expect(Puppet::Util::Metric.new(:foo).name).to eq(\"foo\")\n  end\n\n  it \"should support a label\" do\n    expect(Puppet::Util::Metric.new(\"foo\", \"mylabel\").label).to eq(\"mylabel\")\n  end\n\n  it \"should autogenerate a label if none is provided\" do\n    expect(Puppet::Util::Metric.new(\"foo_bar\").label).to eq(\"Foo bar\")\n  end\n\n  it \"should have a method for adding values\" do\n    expect(@metric).to respond_to(:newvalue)\n  end\n\n  it \"should have a method for returning values\" do\n    expect(@metric).to respond_to(:values)\n  end\n\n  it \"should require a name and value for its values\" do\n    expect { @metric.newvalue }.to raise_error(ArgumentError)\n  end\n\n  it \"should support a label for values\" do\n    @metric.newvalue(\"foo\", 10, \"label\")\n    expect(@metric.values[0][1]).to eq(\"label\")\n  end\n\n  it \"should autogenerate value labels if none is provided\" do\n    @metric.newvalue(\"foo_bar\", 10)\n    expect(@metric.values[0][1]).to eq(\"Foo bar\")\n  end\n\n  it \"should return its values sorted by label\" do\n    @metric.newvalue(\"foo\", 10, \"b\")\n    @metric.newvalue(\"bar\", 10, \"a\")\n\n    expect(@metric.values).to eq([[\"bar\", \"a\", 10], [\"foo\", \"b\", 10]])\n  end\n\n  it \"should use an array indexer method to retrieve individual values\" do\n    @metric.newvalue(\"foo\", 10)\n    expect(@metric[\"foo\"]).to eq(10)\n  end\n\n  it \"should return nil if the named value cannot be found\" do\n    expect(@metric[\"foo\"]).to eq(0)\n  end\n\n  let(:metric) do\n    metric = Puppet::Util::Metric.new(\"foo\", \"mylabel\")\n    metric.newvalue(\"v1\", 10.1, \"something\")\n    metric.newvalue(\"v2\", 20, \"something else\")\n    metric\n  end\n\n  it \"should round trip through json\" do\n    tripped = Puppet::Util::Metric.from_data_hash(JSON.parse(metric.to_json))\n\n    expect(tripped.name).to eq(metric.name)\n    expect(tripped.label).to eq(metric.label)\n    expect(tripped.values).to eq(metric.values)\n  end\n\n  it 'to_data_hash returns value that is instance of to Data' do\n    expect(Puppet::Pops::Types::TypeFactory.data.instance?(metric.to_data_hash)).to be_truthy\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/monkey_patches_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/monkey_patches'\n\ndescribe Dir do\n  describe '.exists?' do\n    it 'returns false if the directory does not exist' do\n      expect(Dir.exists?('/madeupdirectory')).to be false\n    end\n\n    it 'returns true if the directory exists' do\n      expect(Dir.exists?(__dir__)).to be true\n    end\n\n    if RUBY_VERSION >= '3.2'\n      it 'logs a warning message' do\n        expect(Dir).to receive(:warn).with(\"Dir.exists?('#{__dir__}') is deprecated, use Dir.exist? instead\")\n        with_verbose_enabled do\n          Dir.exists?(__dir__)\n        end\n      end\n    end\n  end\nend\n\ndescribe File do\n  describe '.exists?' do\n    it 'returns false if the directory does not exist' do\n      expect(File.exists?('spec/unit/util/made_up_file')).to be false\n    end\n\n    it 'returns true if the file exists' do\n      expect(File.exists?(__FILE__)).to be true\n    end\n\n    if RUBY_VERSION >= '3.2'\n      it 'logs a warning message' do\n        expect(File).to receive(:warn).with(\"File.exists?('#{__FILE__}') is deprecated, use File.exist? instead\")\n        with_verbose_enabled do\n          File.exists?(__FILE__)\n        end\n      end\n    end\n  end\nend\n\ndescribe Symbol do\n  after :all do\n    $unique_warnings.delete('symbol_comparison') if $unique_warnings\n  end\n\n  it 'should have an equal? that is not true for a string with same letters' do\n    symbol = :undef\n    expect(symbol).to_not equal('undef')\n  end\n\n  it \"should have an eql? that is not true for a string with same letters\" do\n    symbol = :undef\n    expect(symbol).to_not eql('undef')\n  end\n\n  it \"should have an == that is not true for a string with same letters\" do\n    symbol = :undef\n    expect(symbol == 'undef').to_not be(true)\n  end\n\n  it \"should return self from #intern\" do\n    symbol = :foo\n    expect(symbol).to equal symbol.intern\n  end\nend\n\ndescribe OpenSSL::SSL::SSLContext do\n  it 'disables SSLv3 via the SSLContext#options bitmask' do\n    expect(subject.options & OpenSSL::SSL::OP_NO_SSLv3).to eq(OpenSSL::SSL::OP_NO_SSLv3)\n  end\n\n  it 'does not exclude SSLv3 ciphers shared with TLSv1' do\n    cipher_str = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers]\n    if cipher_str\n      expect(cipher_str.split(':')).not_to include('!SSLv3')\n    end\n  end\n\n  it 'sets parameters on initialization' do\n    expect_any_instance_of(described_class).to receive(:set_params)\n    subject\n  end\nend\n\n\ndescribe OpenSSL::X509::Store, :if => Puppet::Util::Platform.windows? do\n  let(:store)    { described_class.new }\n  let(:cert)     { OpenSSL::X509::Certificate.new(File.read(my_fixture('x509.pem'))) }\n  let(:samecert) { cert.dup() }\n\n  def with_root_certs(certs)\n    expect(Puppet::Util::Windows::RootCerts).to receive(:instance).and_return(certs)\n  end\n\n  it \"adds a root cert to the store\" do\n    with_root_certs([cert])\n\n    store.set_default_paths\n  end\n\n  it \"doesn't warn when calling set_default_paths multiple times\" do\n    with_root_certs([cert])\n    expect(store).not_to receive(:warn)\n\n    store.set_default_paths\n    store.set_default_paths\n  end\n\n  it \"ignores duplicate root certs\" do\n    # prove that even though certs have identical contents, their hashes differ\n    expect(cert.hash).to_not eq(samecert.hash)\n    with_root_certs([cert, samecert])\n\n    expect(store).to receive(:add_cert).with(cert).once\n    expect(store).not_to receive(:add_cert).with(samecert)\n\n    store.set_default_paths\n  end\n\n  # openssl 1.1.1 ignores duplicate certs\n  # https://github.com/openssl/openssl/commit/c0452248ea1a59a41023a4765ef7d9825e80a62b\n  if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10101000\n    it \"warns when adding a certificate that already exists\" do\n      with_root_certs([cert])\n      store.add_cert(cert)\n\n      expect(store).to receive(:warn).with('Failed to add CN=Microsoft Root Certificate Authority,DC=microsoft,DC=com')\n\n      store.set_default_paths\n    end\n  else\n    it \"doesn't warn when adding a duplicate cert\" do\n      with_root_certs([cert])\n      store.add_cert(cert)\n\n      expect(store).not_to receive(:warn)\n\n      store.set_default_paths\n    end\n  end\n\n  it \"raises when adding an invalid certificate\" do\n    with_root_certs(['notacert'])\n\n    expect {\n      store.set_default_paths\n    }.to raise_error(TypeError)\n  end\nend\n\ndescribe SecureRandom do\n  it 'generates a properly formatted uuid' do\n    expect(SecureRandom.uuid).to match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/multi_match_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/multi_match'\n\ndescribe \"The Puppet::Util::MultiMatch\" do\n  let(:not_nil) { Puppet::Util::MultiMatch::NOT_NIL }\n  let(:mm) { Puppet::Util::MultiMatch }\n\n  it \"matches against not nil\" do\n    expect(not_nil === 3).to be(true)\n  end\n\n  it \"matches against multiple values\" do\n    expect(mm.new(not_nil, not_nil) === [3, 3]).to be(true)\n  end\n\n  it \"matches each value using ===\" do\n    expect(mm.new(3, 3.14) === [Integer, Float]).to be(true)\n  end\n\n  it \"matches are commutative\" do\n    expect(mm.new(3, 3.14) === mm.new(Integer, Float)).to be(true)\n    expect(mm.new(Integer, Float) === mm.new(3, 3.14)).to be(true)\n  end\n\n  it \"has TUPLE constant for match of array of two non nil values\" do\n    expect(mm::TUPLE === [3, 3]).to be(true)\n  end\n\n  it \"has TRIPLE constant for match of array of two non nil values\" do\n    expect(mm::TRIPLE === [3, 3, 3]).to be(true)\n  end\n\n  it \"considers length of array of values when matching\" do\n    expect(mm.new(not_nil, not_nil) === [6, 6, 6]).to be(false)\n    expect(mm.new(not_nil, not_nil) === [6]).to be(false)\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/util/network_device/config_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/network_device/config'\n\ndescribe Puppet::Util::NetworkDevice::Config do\n  include PuppetSpec::Files\n\n  before(:each) do\n    Puppet[:deviceconfig] = tmpfile('deviceconfig')\n  end\n\n  describe \"when parsing device\" do\n    let(:config) { Puppet::Util::NetworkDevice::Config.new }\n\n    def write_device_config(*lines)\n      File.open(Puppet[:deviceconfig], 'w') {|f| f.puts lines}\n    end\n\n    it \"should skip comments\" do\n      write_device_config('  # comment')\n\n      expect(config.devices).to be_empty\n    end\n\n    it \"should increment line number even on commented lines\" do\n      write_device_config('  # comment','[router.puppetlabs.com]')\n\n      expect(config.devices).to be_include('router.puppetlabs.com')\n    end\n\n    it \"should skip blank lines\" do\n      write_device_config('  ')\n\n      expect(config.devices).to be_empty\n    end\n\n    it \"should produce the correct line number\" do\n      write_device_config('  ', '[router.puppetlabs.com]')\n\n      expect(config.devices['router.puppetlabs.com'].line).to eq(2)\n    end\n\n    it \"should throw an error if the current device already exists\" do\n      write_device_config('[router.puppetlabs.com]', '[router.puppetlabs.com]')\n\n    end\n\n    it \"should accept device certname containing dashes\" do\n      write_device_config('[router-1.puppetlabs.com]')\n\n      expect(config.devices).to include('router-1.puppetlabs.com')\n    end\n\n    it \"should create a new device for each found device line\" do\n      write_device_config('[router.puppetlabs.com]', '[swith.puppetlabs.com]')\n\n      expect(config.devices.size).to eq(2)\n    end\n\n    it \"should parse the device type\" do\n      write_device_config('[router.puppetlabs.com]', 'type cisco')\n\n      expect(config.devices['router.puppetlabs.com'].provider).to eq('cisco')\n    end\n\n    it \"should parse the device url\" do\n      write_device_config('[router.puppetlabs.com]', 'type cisco', 'url ssh://test/')\n\n      expect(config.devices['router.puppetlabs.com'].url).to eq('ssh://test/')\n    end\n\n    it \"should error with a malformed device url\" do\n      write_device_config('[router.puppetlabs.com]', 'type cisco', 'url ssh://test node/')\n\n      expect { config.devices['router.puppetlabs.com'] }.to raise_error Puppet::Error\n    end\n\n    it \"should parse the debug mode\" do\n      write_device_config('[router.puppetlabs.com]', 'type cisco', 'url ssh://test/', 'debug')\n\n      expect(config.devices['router.puppetlabs.com'].options).to eq({ :debug => true })\n    end\n\n    it \"should set the debug mode to false by default\" do\n      write_device_config('[router.puppetlabs.com]', 'type cisco', 'url ssh://test/')\n\n      expect(config.devices['router.puppetlabs.com'].options).to eq({ :debug => false })\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/util/network_device/transport/base_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/network_device/transport/base'\n\ndescribe Puppet::Util::NetworkDevice::Transport::Base do\n  class TestTransport < Puppet::Util::NetworkDevice::Transport::Base\n  end\n\n  before(:each) do\n    @transport = TestTransport.new\n  end\n\n  describe \"when sending commands\" do\n    it \"should send the command to the telnet session\" do\n      expect(@transport).to receive(:send).with(\"line\")\n      @transport.command(\"line\")\n    end\n\n    it \"should expect an output matching the given prompt\" do\n      expect(@transport).to receive(:expect).with(/prompt/)\n      @transport.command(\"line\", :prompt => /prompt/)\n    end\n\n    it \"should expect an output matching the default prompt\" do\n      @transport.default_prompt = /defprompt/\n      expect(@transport).to receive(:expect).with(/defprompt/)\n      @transport.command(\"line\")\n    end\n\n    it \"should yield telnet output to the given block\" do\n      expect(@transport).to receive(:expect).and_yield(\"output\")\n      @transport.command(\"line\") { |out| expect(out).to eq(\"output\") }\n    end\n\n    it \"should return telnet output to the caller\" do\n      expect(@transport).to receive(:expect).and_return(\"output\")\n      expect(@transport.command(\"line\")).to eq(\"output\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/network_device_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'ostruct'\nrequire 'puppet/util/network_device'\n\ndescribe Puppet::Util::NetworkDevice do\n  before(:each) do\n    @device = OpenStruct.new(:name => \"name\", :provider => \"test\", :url => \"telnet://admin:password@127.0.0.1\", :options => { :debug => false })\n  end\n\n  after(:each) do\n    Puppet::Util::NetworkDevice.teardown\n  end\n\n  class Puppet::Util::NetworkDevice::Test\n    class Device\n      def initialize(device, options)\n      end\n    end\n  end\n\n  describe \"when initializing the remote network device singleton\" do\n    it \"should create a network device instance\" do\n      allow(Puppet::Util::NetworkDevice).to receive(:require)\n      expect(Puppet::Util::NetworkDevice::Test::Device).to receive(:new).with(\"telnet://admin:password@127.0.0.1\", {:debug => false})\n      Puppet::Util::NetworkDevice.init(@device)\n    end\n\n    it \"should raise an error if the remote device instance can't be created\" do\n      allow(Puppet::Util::NetworkDevice).to receive(:require).and_raise(\"error\")\n      expect { Puppet::Util::NetworkDevice.init(@device) }.to raise_error(RuntimeError, /Can't load test for name/)\n    end\n\n    it \"should let caller to access the singleton device\" do\n      device = double('device')\n      allow(Puppet::Util::NetworkDevice).to receive(:require)\n      expect(Puppet::Util::NetworkDevice::Test::Device).to receive(:new).and_return(device)\n      Puppet::Util::NetworkDevice.init(@device)\n\n      expect(Puppet::Util::NetworkDevice.current).to eq(device)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/package/version/debian_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/package/version/debian'\n\ndescribe Puppet::Util::Package::Version::Debian do\n  context \"when creating new version should fail\" do\n    it \"if is parsing symbols\" do\n      expect { described_class.parse(:absent) }.to raise_error(described_class::ValidationFailure)\n    end\n  end\n  context \"when creating new version\" do\n    it \"is parsing basic version\" do\n      v = described_class.parse('1:20191210.1-0ubuntu0.19.04.2')\n      expect(v.epoch).to eql(1)\n      expect(v.upstream_version).to eql('20191210.1')\n      expect(v.debian_revision).to eql('0ubuntu0.19.04.2')\n    end\n\n    it \"is parsing no epoch basic version\" do\n      v = described_class.parse('20191210.1-0ubuntu0.19.04.2')\n      expect(v.epoch).to eql(0)\n      expect(v.upstream_version).to eql('20191210.1')\n      expect(v.debian_revision).to eql('0ubuntu0.19.04.2')\n    end\n\n    it \"is parsing no debian revision basic version\" do\n      v = described_class.parse('2.42.1+19.04')\n      expect(v.epoch).to eql(0)\n      expect(v.upstream_version).to eql('2.42.1+19.04')\n      expect(v.debian_revision).to eql(nil)\n    end\n\n    it \"is parsing no epoch complex version\" do\n      v = described_class.parse('3.32.2+git20190711-2ubuntu1~19.04.1')\n      expect(v.epoch).to eql(0)\n      expect(v.upstream_version).to eql('3.32.2+git20190711')\n      expect(v.debian_revision).to eql('2ubuntu1~19.04.1')\n    end\n\n    it \"is parsing even more complex version\" do\n      v = described_class.parse('5:1.0.0+git-20190109.133f4c4-0ubuntu2')\n      expect(v.epoch).to eql(5)\n      expect(v.upstream_version).to eql('1.0.0+git-20190109.133f4c4')\n      expect(v.debian_revision).to eql('0ubuntu2')\n    end\n  end\n  context \"when comparing two versions\" do\n    it \"epoch has precedence\" do\n      first = described_class.parse('9:99-99')\n      second = described_class.parse('10:01-01')\n      expect(first < second).to eql(true)\n    end\n    it \"handles equals letters-only versions\" do\n      lower = described_class.parse('abd-def')\n      higher = described_class.parse('abd-def')\n      expect(lower == higher).to eql(true)\n    end\n    it \"shorter version is smaller\" do\n      lower = described_class.parse('abd-de')\n      higher = described_class.parse('abd-def')\n      expect(lower < higher).to eql(true)\n    end\n    it \"shorter version is smaller even with digits\" do\n      lower = described_class.parse('a1b2d-d3e')\n      higher = described_class.parse('a1b2d-d3ef')\n      expect(lower < higher).to eql(true)\n    end\n    it \"shorter version is smaller when number is less\" do\n      lower = described_class.parse('a1b2d-d9')\n      higher = described_class.parse('a1b2d-d13')\n      expect(lower < higher).to eql(true)\n    end\n    it \"handles ~ version\" do\n      lower = described_class.parse('a1b2d-d10~')\n      higher = described_class.parse('a1b2d-d10')\n      expect(lower < higher).to eql(true)\n    end\n    it \"handles letters versus -\" do\n      lower = described_class.parse('a1b2d-d1a')\n      higher = described_class.parse('a1b2d-d1-')\n      expect(lower < higher).to eql(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/package/version/pip_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/package/version/pip'\n\ndescribe Puppet::Util::Package::Version::Pip do\n  describe \"initialization\" do\n    shared_examples_for 'a valid version' do |input_version, output = input_version|\n      [input_version, input_version.swapcase].each do |input|\n        it \"transforms #{input} back to string(#{output}) succesfully\" do\n          version = described_class.parse(input)\n          expect(version.to_s).to eq(output)\n        end\n      end\n\n      describe \"comparison\" do\n        version = described_class.parse(input_version)\n\n        # rubocop:disable UselessComparison\n        it \"#{input_version} shouldn't be lesser than itself\" do\n          expect(version <  version).to eq(false)\n        end\n\n        it \"#{input_version} shouldn't be greater than itself\" do\n          expect(version >  version).to eq(false)\n        end\n\n        it \"#{input_version} shouldn't be equal with itself\" do\n          expect(version != version).to eq(false)\n        end\n\n        it \"#{input_version} should be equal to itself\" do\n          expect(version == version).to eq(true)\n        end\n      end\n    end\n\n    shared_examples_for 'an invalid version' do |invalid_input|\n      [invalid_input, invalid_input.swapcase].each do |input|\n        it \"should not be able to transform #{invalid_input} to string\" do\n          expect{ described_class.parse(input) }.to raise_error(described_class::ValidationFailure)\n        end\n      end\n\n      describe \"comparison\" do\n        valid_version = described_class.parse(\"1.0\")\n\n        it \"should raise error when checking if #{invalid_input} is lesser than a valid version\" do\n          expect{ valid_version <  invalid_input }.to raise_error(described_class::ValidationFailure)\n        end\n\n        it \"should raise error when checking if #{invalid_input} is greater than a valid version\" do\n          expect{ valid_version >  invalid_input }.to raise_error(described_class::ValidationFailure)\n        end\n\n        it \"should raise error when checking if #{invalid_input} is greater or equal than a valid version\" do\n          expect{ valid_version >= invalid_input }.to raise_error(described_class::ValidationFailure)\n        end\n\n        it \"should raise error when checking if #{invalid_input} is lesser or equal than a valid version\" do\n          expect{ valid_version <= invalid_input }.to raise_error(described_class::ValidationFailure)\n        end\n      end\n    end\n\n    describe \"when only release segment is present in provided version\" do\n      context \"should work with any number of integer elements\" do\n        context \"when it has 1 element\" do\n          it_should_behave_like 'a valid version', \"1\"\n        end\n        context \"when it has 2 elements\" do\n          it_should_behave_like 'a valid version', \"1.1\"\n        end\n\n        context \"when it has 3 elements\" do\n          it_should_behave_like 'a valid version', \"1.1.1\"\n        end\n\n        context \"when it has 4 elements\" do\n          it_should_behave_like 'a valid version', \"1.1.1.1\"\n        end\n\n        context \"when it has 10 elements\" do\n          it_should_behave_like 'a valid version', \"1.1.1.1.1.1.1.1.1.1\"\n        end\n      end\n\n      describe \"should work with elements which are zero\" do\n        context \"when it ends with 1 zero\" do\n          it_should_behave_like 'a valid version', \"1.0\"\n        end\n\n        context \"when it ends with 2 zeros\" do\n          it_should_behave_like 'a valid version', \"1.0.0\"\n        end\n\n        context \"when it ends with 3 zeros\" do\n          it_should_behave_like 'a valid version', \"1.0.0.0\"\n        end\n\n        context \"when it starts with 1 zero\" do\n          it_should_behave_like 'a valid version', \"0.1\"\n        end\n\n        context \"when it starts with 2 zeros\" do\n          it_should_behave_like 'a valid version', \"0.0.1\"\n        end\n\n        context \"when it starts with 3 zeros\" do\n          it_should_behave_like 'a valid version', \"0.0.0.1\"\n        end\n\n        context \"when it is just a zero\" do\n          it_should_behave_like 'a valid version', \"0\"\n        end\n\n        context \"when it is full of just zeros\" do\n          it_should_behave_like 'a valid version', \"0.0.0\"\n        end\n      end\n\n      describe \"should work with elements containing multiple digits\" do\n        context \"when it has two digit elements\" do\n          it_should_behave_like 'a valid version', \"1.10.1\"\n        end\n\n        context \"when it has three digit elements\" do\n          it_should_behave_like 'a valid version', \"1.101.1.11\"\n        end\n\n        context \"when it has four digit elements\" do\n          it_should_behave_like 'a valid version', \"2019.0.11\"\n        end\n\n        context \"when it has a numerical element starting with zero\" do\n          # the zero will dissapear\n          it_should_behave_like 'a valid version', \"1.09.10\", \"1.9.10\"\n        end\n\n        context \"when it starts with multiple zeros\" do\n          # the zeros will dissapear\n          it_should_behave_like 'a valid version', \"0010.0000.0011\", \"10.0.11\"\n        end\n      end\n\n      context \"should fail because of misplaced letters\" do\n        context \"when it starts with letters\" do\n          it_should_behave_like 'an invalid version', \"d.2\"\n          it_should_behave_like 'an invalid version', \"ee.2\"\n        end\n\n        context \"when it has only letters\" do\n          it_should_behave_like 'an invalid version', \"d.c\"\n          it_should_behave_like 'an invalid version', \"dd.c\"\n        end\n      end\n    end\n\n    describe \"when the epoch segment is present in provided version\" do\n      context \"should work when epoch is an integer\" do\n        context \"when epoch has 1 digit\" do\n          it_should_behave_like 'a valid version', \"1!1.0.0\"\n        end\n\n        context \"when epoch has 2 digits\" do\n          it_should_behave_like 'a valid version', \"10!1.0.0\"\n        end\n\n        context \"when epoch is zero\" do\n          # versions without epoch specified are considered to have epoch 0\n          # it is accepted as input but it should be ignored at output\n          it_should_behave_like 'a valid version', \"0!1.0.0\", \"1.0.0\"\n        end\n      end\n\n      context \"should fail when epoch contains letters\" do\n        context \"when epoch starts with a letter\" do\n          it_should_behave_like 'an invalid version', \"a9!1.0.0\"\n        end\n\n        context \"when epoch ends with a letter\" do\n          it_should_behave_like 'an invalid version', \"9a!1.0.0\"\n        end\n      end\n    end\n\n    describe \"when the pre-release segment is present in provided version\" do\n      context \"when pre-release contains the letter a\" do\n        it_should_behave_like 'a valid version', \"1.0a\", \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0a0\"\n      end\n\n      context \"when pre-release contains the letter b\" do\n        it_should_behave_like 'a valid version', \"1.0b\", \"1.0b0\"\n        it_should_behave_like 'a valid version', \"1.0b0\"\n      end\n\n      context \"when pre-release contains the letter c\" do\n        it_should_behave_like 'a valid version', \"1.0c\",  \"1.0rc0\"\n        it_should_behave_like 'a valid version', \"1.0c0\", \"1.0rc0\"\n      end\n\n      context \"when pre-release contains the string alpha\" do\n        it_should_behave_like 'a valid version', \"1.0alpha\",  \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0alpha0\", \"1.0a0\"\n      end\n\n      context \"when pre-release contains the string beta\" do\n        it_should_behave_like 'a valid version', \"1.0beta\",  \"1.0b0\"\n        it_should_behave_like 'a valid version', \"1.0beta0\", \"1.0b0\"\n      end\n\n      context \"when pre-release contains the string rc\" do\n        it_should_behave_like 'a valid version', \"1.0rc\",  \"1.0rc0\"\n        it_should_behave_like 'a valid version', \"1.0rc0\", \"1.0rc0\"\n      end\n\n      context \"when pre-release contains the string pre\" do\n        it_should_behave_like 'a valid version', \"1.0pre\",  \"1.0rc0\"\n        it_should_behave_like 'a valid version', \"1.0pre0\", \"1.0rc0\"\n      end\n\n      context \"when pre-release contains the string preview\" do\n        it_should_behave_like 'a valid version', \"1.0preview\",  \"1.0rc0\"\n        it_should_behave_like 'a valid version', \"1.0preview0\", \"1.0rc0\"\n      end\n\n      context \"when pre-release contains multiple zeros at the beginning\" do\n        it_should_behave_like 'a valid version', \"1.0.beta.00\",  \"1.0b0\"\n        it_should_behave_like 'a valid version', \"1.0.beta.002\", \"1.0b2\"\n      end\n\n      context \"when pre-release elements are separated by dots\" do\n        it_should_behave_like 'a valid version', \"1.0.alpha\",   \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0.alpha.0\", \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0.alpha.2\", \"1.0a2\"\n      end\n\n      context \"when pre-release elements are separated by dashes\" do\n        it_should_behave_like 'a valid version', \"1.0-alpha\",   \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0-alpha-0\", \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0-alpha-2\", \"1.0a2\"\n      end\n\n      context \"when pre-release elements are separated by underscores\" do\n        it_should_behave_like 'a valid version', \"1.0_alpha\",   \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0_alpha_0\", \"1.0a0\"\n        it_should_behave_like 'a valid version', \"1.0_alpha_2\", \"1.0a2\"\n      end\n\n      context \"when pre-release elements are separated by mixed symbols\" do\n        it_should_behave_like 'a valid version', \"1.0-alpha_5\", \"1.0a5\"\n        it_should_behave_like 'a valid version', \"1.0-alpha.5\", \"1.0a5\"\n        it_should_behave_like 'a valid version', \"1.0_alpha-5\", \"1.0a5\"\n        it_should_behave_like 'a valid version', \"1.0_alpha.5\", \"1.0a5\"\n        it_should_behave_like 'a valid version', \"1.0.alpha-5\", \"1.0a5\"\n        it_should_behave_like 'a valid version', \"1.0.alpha_5\", \"1.0a5\"\n      end\n    end\n\n    describe \"when the post-release segment is present in provided version\" do\n      context \"when post-release is just an integer\" do\n        it_should_behave_like 'a valid version', \"1.0-9\",  \"1.0.post9\"\n        it_should_behave_like 'a valid version', \"1.0-10\", \"1.0.post10\"\n      end\n\n      context \"when post-release is just an integer and starts with zero\" do\n        it_should_behave_like 'a valid version', \"1.0-09\",  \"1.0.post9\"\n        it_should_behave_like 'a valid version', \"1.0-009\", \"1.0.post9\"\n      end\n\n      context \"when post-release contains the string post\" do\n        it_should_behave_like 'a valid version', \"1.0post\",  \"1.0.post0\"\n        it_should_behave_like 'a valid version', \"1.0post0\", \"1.0.post0\"\n        it_should_behave_like 'a valid version', \"1.0post1\", \"1.0.post1\"\n        it_should_behave_like 'an invalid version', \"1.0-0.post1\"\n      end\n\n      context \"when post-release contains the string rev\" do\n        it_should_behave_like 'a valid version', \"1.0rev\",  \"1.0.post0\"\n        it_should_behave_like 'a valid version', \"1.0rev0\", \"1.0.post0\"\n        it_should_behave_like 'a valid version', \"1.0rev1\", \"1.0.post1\"\n        it_should_behave_like 'an invalid version', \"1.0-0.rev1\"\n      end\n\n      context \"when post-release contains the letter r\" do\n        it_should_behave_like 'a valid version', \"1.0r\",  \"1.0.post0\"\n        it_should_behave_like 'a valid version', \"1.0r0\", \"1.0.post0\"\n        it_should_behave_like 'a valid version', \"1.0r1\", \"1.0.post1\"\n        it_should_behave_like 'an invalid version', \"1.0-0.r1\"\n      end\n\n      context \"when post-release elements are separated by dashes\" do\n        it_should_behave_like 'a valid version', \"1.0-post-22\", \"1.0.post22\"\n        it_should_behave_like 'a valid version', \"1.0-rev-22\",  \"1.0.post22\"\n        it_should_behave_like 'a valid version', \"1.0-r-22\",    \"1.0.post22\"\n      end\n\n      context \"when post-release elements are separated by underscores\" do\n        it_should_behave_like 'a valid version', \"1.0_post_22\", \"1.0.post22\"\n        it_should_behave_like 'a valid version', \"1.0_rev_22\",  \"1.0.post22\"\n        it_should_behave_like 'a valid version', \"1.0_r_22\",    \"1.0.post22\"\n      end\n\n      context \"when post-release elements are separated by dots\" do\n        it_should_behave_like 'a valid version', \"1.0.post.22\", \"1.0.post22\"\n        it_should_behave_like 'a valid version', \"1.0.rev.22\",  \"1.0.post22\"\n        it_should_behave_like 'a valid version', \"1.0.r.22\",    \"1.0.post22\"\n      end\n\n      context \"when post-release elements are separated by mixed symbols\" do\n        it_should_behave_like 'a valid version', \"1.0-r_5\", \"1.0.post5\"\n        it_should_behave_like 'a valid version', \"1.0-r.5\", \"1.0.post5\"\n        it_should_behave_like 'a valid version', \"1.0_r-5\", \"1.0.post5\"\n        it_should_behave_like 'a valid version', \"1.0_r.5\", \"1.0.post5\"\n        it_should_behave_like 'a valid version', \"1.0.r-5\", \"1.0.post5\"\n        it_should_behave_like 'a valid version', \"1.0.r_5\", \"1.0.post5\"\n      end\n    end\n\n    describe \"when the dev release segment is present in provided version\" do\n      context \"when dev release is only the keyword dev\" do\n        it_should_behave_like 'a valid version', \"1.0dev\",  \"1.0.dev0\"\n        it_should_behave_like 'a valid version', \"1.0-dev\", \"1.0.dev0\"\n        it_should_behave_like 'a valid version', \"1.0_dev\", \"1.0.dev0\"\n        it_should_behave_like 'a valid version', \"1.0.dev\", \"1.0.dev0\"\n      end\n\n      context \"when dev release contains the keyword dev and a number\" do\n        it_should_behave_like 'a valid version', \"1.0dev2\",    \"1.0.dev2\"\n        it_should_behave_like 'a valid version', \"1.0-dev33\",  \"1.0.dev33\"\n        it_should_behave_like 'a valid version', \"1.0.dev11\",  \"1.0.dev11\"\n        it_should_behave_like 'a valid version', \"1.0_dev101\", \"1.0.dev101\"\n      end\n\n      context \"when dev release's number element starts with 0\" do\n        it_should_behave_like 'a valid version', \"1.0dev02\",     \"1.0.dev2\"\n        it_should_behave_like 'a valid version', \"1.0-dev033\",   \"1.0.dev33\"\n        it_should_behave_like 'a valid version', \"1.0_dev0101\",  \"1.0.dev101\"\n        it_should_behave_like 'a valid version', \"1.0.dev00011\", \"1.0.dev11\"\n      end\n\n      context \"when dev release elements are separated by dashes\" do\n        it_should_behave_like 'a valid version', \"1.0-dev\",    \"1.0.dev0\"\n        it_should_behave_like 'a valid version', \"1.0-dev-2\",  \"1.0.dev2\"\n        it_should_behave_like 'a valid version', \"1.0-dev-22\", \"1.0.dev22\"\n      end\n\n      context \"when dev release elements are separated by underscores\" do\n        it_should_behave_like 'a valid version', \"1.0_dev\",    \"1.0.dev0\"\n        it_should_behave_like 'a valid version', \"1.0_dev_2\",  \"1.0.dev2\"\n        it_should_behave_like 'a valid version', \"1.0_dev_22\", \"1.0.dev22\"\n      end\n\n      context \"when dev release elements are separated by dots\" do\n        it_should_behave_like 'a valid version', \"1.0.dev\",    \"1.0.dev0\"\n        it_should_behave_like 'a valid version', \"1.0.dev.2\",  \"1.0.dev2\"\n        it_should_behave_like 'a valid version', \"1.0.dev.22\", \"1.0.dev22\"\n      end\n\n      context \"when dev release elements are separated by mixed symbols\" do\n        it_should_behave_like 'a valid version', \"1.0-dev_5\", \"1.0.dev5\"\n        it_should_behave_like 'a valid version', \"1.0-dev.5\", \"1.0.dev5\"\n        it_should_behave_like 'a valid version', \"1.0_dev-5\", \"1.0.dev5\"\n        it_should_behave_like 'a valid version', \"1.0_dev.5\", \"1.0.dev5\"\n        it_should_behave_like 'a valid version', \"1.0.dev-5\", \"1.0.dev5\"\n        it_should_behave_like 'a valid version', \"1.0.dev_5\", \"1.0.dev5\"\n      end\n    end\n\n    describe \"when the local version segment is present in provided version\" do\n      it_should_behave_like 'an invalid version', \"1.0+\"\n\n      context \"when local version is just letters\" do\n        it_should_behave_like 'a valid version', \"1.0+local\"\n        it_should_behave_like 'a valid version', \"1.0+Local\", \"1.0+local\"\n      end\n\n      context \"when local version contains numbers\" do\n        it_should_behave_like 'a valid version', \"1.0+10\"\n        it_should_behave_like 'a valid version', \"1.0+01\",    \"1.0+1\"\n        it_should_behave_like 'a valid version', \"1.0+01L\",   \"1.0+01l\"\n        it_should_behave_like 'a valid version', \"1.0+L101L\", \"1.0+l101l\"\n      end\n\n      context \"when local version contains multiple elements\" do\n        it_should_behave_like 'a valid version', \"1.0+10.local\"\n        it_should_behave_like 'a valid version', \"1.0+abc.def.ghi\"\n        it_should_behave_like 'a valid version', \"1.0+01.abc\",            \"1.0+1.abc\"\n        it_should_behave_like 'a valid version', \"1.0+01L.0001\",          \"1.0+01l.1\"\n        it_should_behave_like 'a valid version', \"1.0+L101L.local\",       \"1.0+l101l.local\"\n        it_should_behave_like 'a valid version', \"1.0+dash-undrsc_dot.5\", \"1.0+dash.undrsc.dot.5\"\n      end\n    end\n  end\n\n  describe \"comparison of versions\" do\n    # This array must remain sorted (smallest to highest version).\n    versions = [\n      \"0.1\",\n      \"0.10\",\n      \"0.10.1\",\n      \"0.10.1.0.1\",\n      \"1.0.dev456\",\n      \"1.0a1\",\n      \"1.0a2.dev456\",\n      \"1.0a12.dev456\",\n      \"1.0a12\",\n      \"1.0b1.dev456\",\n      \"1.0b2\",\n      \"1.0b2.post345.dev456\",\n      \"1.0b2.post345\",\n      \"1.0b2-346\",\n      \"1.0c1.dev456\",\n      \"1.0c1\",\n      \"1.0rc2\",\n      \"1.0c3\",\n      \"1.0\",\n      \"1.0.post456.dev34\",\n      \"1.0.post456\",\n      \"1.1.dev1\",\n      \"1.2\",\n      \"1.2+123abc\",\n      \"1.2+123abc456\",\n      \"1.2+abc\",\n      \"1.2+abc123\",\n      \"1.2+abc123-def2\",\n      \"1.2+abc123-def2-0\",\n      \"1.2+abc123def\",\n      \"1.2+1234.abc\",\n      \"1.2+123456\",\n      \"1.2.r32+123456\",\n      \"1.2.rev33+123456\",\n      \"1!1.0b2.post345.dev456\",\n      \"1!1.0\",\n      \"1!1.0.post456.dev34\",\n      \"1!1.0.post456\",\n      \"1!1.2.rev33+123456\",\n      \"2!2.3.4.alpha5.rev6.dev7+abc89\"\n    ]\n\n    it \"should find versions list to be already sorted\" do\n      sorted_versions = versions.sort do |x,y|\n        described_class.compare(x, y)\n      end\n      expect(versions).to eq(sorted_versions)\n    end\n\n    versions.combination(2).to_a.each do |version_pair|\n      lower_version = described_class.parse(version_pair.first)\n      greater_version = described_class.parse(version_pair.last)\n      \n      it \"#{lower_version} should be equal to #{lower_version}\" do\n        expect(lower_version == lower_version).to eq(true)\n      end\n\n      it \"#{lower_version} should not be equal to #{greater_version}\" do\n        expect(lower_version != greater_version).to eq(true)\n      end\n\n      it \"#{lower_version} should be lower than #{greater_version}\" do\n        expect(lower_version < greater_version).to eq(true)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/package/version/range_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/package/version/range'\n\nclass IntegerVersion\n  class ValidationFailure < ArgumentError; end\n  include Comparable\n  REGEX_FULL    = '(\\d+)'.freeze\n  REGEX_FULL_RX = /\\A#{REGEX_FULL}\\Z/.freeze\n\n  def self.parse(ver)\n    match, version = *ver.match(REGEX_FULL_RX)\n    raise ValidationFailure, \"Unable to parse '#{ver}' as a version identifier\" unless match\n\n    new(version).freeze\n  end\n\n  attr_reader :version\n\n  def initialize(version)\n    @version = version.to_i\n  end\n\n  def <=>(other)\n    @version <=> other.version\n  end\nend\n\ndescribe Puppet::Util::Package::Version::Range do\n  context 'when creating new version range' do\n    it 'should raise unless String is passed' do\n      expect { Puppet::Util::Package::Version::Range.parse(:abc, IntegerVersion) }.to raise_error(Puppet::Util::Package::Version::Range::ValidationFailure)\n    end\n    it 'should raise if operator is not implemented' do\n      expect { Puppet::Util::Package::Version::Range.parse('=a', IntegerVersion) }.to raise_error(Puppet::Util::Package::Version::Range::ValidationFailure)\n    end\n    it 'should raise if operator cannot be parsed' do\n      expect { Puppet::Util::Package::Version::Range.parse('~=a', IntegerVersion) }.to raise_error(IntegerVersion::ValidationFailure)\n    end\n    it 'should raise if version cannot be parsed' do\n      expect { Puppet::Util::Package::Version::Range.parse('>=a', IntegerVersion) }.to raise_error(IntegerVersion::ValidationFailure)\n    end\n  end\n\n  context 'when creating new version range with regular version' do\n    it 'it does not include greater version' do\n      vr = Puppet::Util::Package::Version::Range.parse('3', IntegerVersion)\n      v = IntegerVersion.parse('4')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it includes specified version' do\n      vr = Puppet::Util::Package::Version::Range.parse('3', IntegerVersion)\n      v = IntegerVersion.parse('3')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it does not include lower version' do\n      vr = Puppet::Util::Package::Version::Range.parse('3', IntegerVersion)\n      v = IntegerVersion.parse('2')\n      expect(vr.include?(v)).to eql(false)\n    end\n  end\n\n  context 'when creating new version range with greater or equal operator' do\n    it 'it includes greater version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>=3', IntegerVersion)\n      v = IntegerVersion.parse('4')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it includes specified version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>=3', IntegerVersion)\n      v = IntegerVersion.parse('3')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it does not include lower version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>=3', IntegerVersion)\n      v = IntegerVersion.parse('2')\n      expect(vr.include?(v)).to eql(false)\n    end\n  end\n\n  context 'when creating new version range with greater operator' do\n    it 'it includes greater version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3', IntegerVersion)\n      v = IntegerVersion.parse('10')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it does not include specified version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3', IntegerVersion)\n      v = IntegerVersion.parse('3')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it does not include lower version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3', IntegerVersion)\n      v = IntegerVersion.parse('1')\n      expect(vr.include?(v)).to eql(false)\n    end\n  end\n\n  context 'when creating new version range with lower or equal operator' do\n    it 'it does not include greater version' do\n      vr = Puppet::Util::Package::Version::Range.parse('<=3', IntegerVersion)\n      v = IntegerVersion.parse('5')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it includes specified version' do\n      vr = Puppet::Util::Package::Version::Range.parse('<=3', IntegerVersion)\n      v = IntegerVersion.parse('3')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it includes lower version' do\n      vr = Puppet::Util::Package::Version::Range.parse('<=3', IntegerVersion)\n      v = IntegerVersion.parse('1')\n      expect(vr.include?(v)).to eql(true)\n    end\n  end\n\n  context 'when creating new version range with lower operator' do\n    it 'it does not include greater version' do\n      vr = Puppet::Util::Package::Version::Range.parse('<3', IntegerVersion)\n      v = IntegerVersion.parse('8')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it does not include specified version' do\n      vr = Puppet::Util::Package::Version::Range.parse('<3', IntegerVersion)\n      v = IntegerVersion.parse('3')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it includes lower version' do\n      vr = Puppet::Util::Package::Version::Range.parse('<3', IntegerVersion)\n      v = IntegerVersion.parse('2')\n      expect(vr.include?(v)).to eql(true)\n    end\n  end\n\n  context 'when creating new version range with interval' do\n    it 'it does not include greater version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3 <=5', IntegerVersion)\n      v = IntegerVersion.parse('7')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it includes specified max interval value' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3 <=5', IntegerVersion)\n      v = IntegerVersion.parse('5')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it includes in interval version' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3 <=5', IntegerVersion)\n      v = IntegerVersion.parse('4')\n      expect(vr.include?(v)).to eql(true)\n    end\n\n    it 'it does not include min interval value ' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3 <=5', IntegerVersion)\n      v = IntegerVersion.parse('3')\n      expect(vr.include?(v)).to eql(false)\n    end\n\n    it 'it does not include lower value ' do\n      vr = Puppet::Util::Package::Version::Range.parse('>3 <=5', IntegerVersion)\n      v = IntegerVersion.parse('2')\n      expect(vr.include?(v)).to eql(false)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/package/version/rpm_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/package/version/rpm'\n\ndescribe Puppet::Util::Package::Version::Rpm do\n\n  context \"when parsing an invalid version\" do\n    it \"raises ArgumentError\" do\n      expect { described_class.parse(:absent)}.to raise_error(ArgumentError)\n    end\n  end\n\n  context \"when creating new version\" do\n    it \"is parsing basic version\" do\n      v = described_class.parse('1:2.8.8-1.el6')\n      expect([v.epoch, v.version, v.release, v.arch ]).to eq(['1', '2.8.8', '1.el6' , nil])\n    end\n\n    it \"is parsing no epoch basic version\" do\n      v = described_class.parse('2.8.8-1.el6')\n      expect([v.epoch, v.version, v.release, v.arch ]).to eq([nil, '2.8.8', '1.el6', nil])\n    end\n\n    it \"is parsing no epoch basic short version\" do\n      v = described_class.parse('7.15-8.fc29')\n      expect([v.epoch, v.version, v.release, v.arch ]).to eq([nil, '7.15', '8.fc29', nil])\n    end\n\n    it \"is parsing no epoch and no release basic version\" do\n      v = described_class.parse('2.8.8')\n      expect([v.epoch, v.version, v.release, v.arch ]).to eq([nil, '2.8.8', nil, nil])\n    end\n\n    it \"is parsing no epoch complex version\" do\n      v = described_class.parse('1.4-0.24.20120830CVS.fc31')\n      expect([v.epoch, v.version, v.release, v.arch ]).to eq([nil, '1.4', '0.24.20120830CVS.fc31', nil])\n    end\n  end\n\n  context \"when comparing two versions\" do\n    context 'with invalid version' do\n      it 'raises ArgumentError' do\n        version = described_class.parse('0:1.5.3-3.el6')\n        invalid = 'invalid'\n        expect { version < invalid }.to \\\n          raise_error(ArgumentError, 'Cannot compare, as invalid is not a Rpm Version')\n      end\n    end\n\n    context 'with valid versions' do\n      it \"epoch has precedence\" do\n        lower = described_class.parse('0:1.5.3-3.el6')\n        higher = described_class.parse('1:1.7.0-15.fc29')\n        expect(lower).to be < higher\n      end\n\n      it 'handles no epoch as 0 epoch' do\n        lower = described_class.parse('1.5.3-3.el6')\n        higher = described_class.parse('1:1.7.0-15.fc29')\n        expect(lower).to be < higher\n      end\n\n      it \"handles equals letters-only versions\" do\n        first = described_class.parse('abd-def')\n        second = described_class.parse('abd-def')\n        expect(first).to eq(second)\n      end\n\n      it \"shorter version is smaller letters-only versions\" do\n        lower = described_class.parse('ab')\n        higher = described_class.parse('abd')\n        expect(lower).to be < higher\n      end\n\n      it \"shorter version is smaller even with digits\" do\n        lower = described_class.parse('1.7')\n        higher = described_class.parse('1.7.0')\n        expect(lower).to be < higher\n      end\n\n      it \"shorter version is smaller when number is less\" do\n        lower = described_class.parse('1.7.0')\n        higher = described_class.parse('1.7.1')\n        expect(lower).to be < higher\n      end\n\n      it \"shorter release is smaller \" do\n        lower = described_class.parse('1.7.0-11.fc26')\n        higher = described_class.parse('1.7.0-11.fc27')\n        expect(lower).to be < higher\n      end\n\n      it \"release letters are smaller letters-only\" do\n        lower = described_class.parse('1.7.0-abc')\n        higher = described_class.parse('1.7.0-abd')\n        expect(lower).to be < higher\n      end\n\n      it \"shorter release is smaller\" do\n        lower = described_class.parse('1.7.0-11.fc2')\n        higher = described_class.parse('1.7.0-11.fc17')\n        expect(lower).to be < higher\n      end\n\n      it \"handles equal release\" do\n        first = described_class.parse('1.7.0-11.fc27')\n        second = described_class.parse('1.7.0-11.fc27')\n        expect(first).to eq(second)\n      end\n    end\n\n    context 'when one has no epoch' do\n      it 'handles no epoch as zero' do\n        version1 = described_class.parse('1:1.2')\n        version2 = described_class.parse('1.4')\n\n        expect(version1).to be > version2\n        expect(version2).to be < version1\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/package_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/package'\n\ndescribe Puppet::Util::Package, \" versioncmp\" do\n\n  it \"should be able to be used as a module function\" do\n    expect(Puppet::Util::Package).to respond_to(:versioncmp)\n  end\n\n  it \"should be able to sort a long set of various unordered versions\" do\n    ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06}\n\n    newary = ary.sort { |a, b| Puppet::Util::Package.versioncmp(a,b) }\n\n    expect(newary).to eq([\"0002\", \"1\", \"1.06\", \"1.1-3\", \"1.1-4\", \"1.1-5\", \"1.1.6\", \"1.1.a\", \"1.1a\", \"1.2\", \"1.5\", \"2.3\", \"2.3.0\", \"2.3.1\", \"2.3a.1\", \"2.4\", \"2.4\", \"2.4b\", \"2.40.2\", \"3.0\", \"3.1\"])\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/util/pidlock_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/pidlock'\n\ndescribe Puppet::Util::Pidlock, if: !Puppet::Util::Platform.jruby? do\n  require 'puppet_spec/files'\n  include PuppetSpec::Files\n\n  before(:each) do\n    @lockfile = tmpfile(\"lock\")\n    @lock = Puppet::Util::Pidlock.new(@lockfile)\n    allow(Facter).to receive(:value).with(:kernel).and_return('Linux')\n  end\n\n  describe \"#ps pid argument on posix\", unless: Puppet::Util::Platform.windows? do\n    let(:other_pid) { Process.pid + 1 }\n\n    before do\n      # another process has locked the pidfile\n      File.write(@lockfile, other_pid)\n\n      # and it's still active\n      allow(Process).to receive(:kill).with(0, other_pid)\n    end\n\n    it \"should fallback to '-p' when ps execution fails with '-eq' on Linux\" do\n      allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-eq', other_pid, '-o', 'comm=']).and_raise(Puppet::ExecutionFailure, 'Execution of command returned 1: error')\n\n      expect(Puppet::Util::Execution).to receive(:execute).with(['ps', \"-p\", other_pid, '-o', 'comm=']).and_return('puppet')\n      expect(Puppet::Util::Execution).to receive(:execute).with(['ps', \"-p\", other_pid, '-o', 'args=']).and_return('puppet')\n\n      expect(@lock).to be_locked\n    end\n\n    shared_examples_for 'a valid ps argument was provided' do |desired_kernel, ps_argument|\n      it \"should be '#{ps_argument}' when current kernel is #{desired_kernel}\" do\n        allow(Facter).to receive(:value).with(:kernel).and_return(desired_kernel)\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', ps_argument, other_pid, '-o', 'comm=']).and_return('ruby')\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', ps_argument, other_pid, '-o', 'args=']).and_return('puppet')\n        expect(@lock).to be_locked\n      end\n    end\n\n    context \"when current kernel is Linux\" do\n      it_should_behave_like 'a valid ps argument was provided', \"Linux\", \"-eq\"\n    end\n\n    context \"when current kernel is AIX\" do\n      it_should_behave_like 'a valid ps argument was provided', \"AIX\", \"-T\"\n    end\n\n    context \"when current kernel is Darwin\" do\n      it_should_behave_like 'a valid ps argument was provided', \"Darwin\", \"-p\"\n    end\n  end\n\n  describe \"#lock\" do\n    it \"should not be locked at start\" do\n      expect(@lock).not_to be_locked\n    end\n\n    it \"should not be mine at start\" do\n      expect(@lock).not_to be_mine\n    end\n\n    it \"should become locked\" do\n      @lock.lock\n      expect(@lock).to be_locked\n    end\n\n    it \"should become mine\" do\n      @lock.lock\n      expect(@lock).to be_mine\n    end\n\n    it \"should be possible to lock multiple times\" do\n      @lock.lock\n      expect { @lock.lock }.not_to raise_error\n    end\n\n    it \"should return true when locking\" do\n      expect(@lock.lock).to be_truthy\n    end\n\n    it \"should return true if locked by me\" do\n      @lock.lock\n      expect(@lock.lock).to be_truthy\n    end\n\n    it \"should create a lock file\" do\n      @lock.lock\n      expect(Puppet::FileSystem.exist?(@lockfile)).to be_truthy\n    end\n\n    it 'should create an empty lock file even when pid is missing' do\n      allow(Process).to receive(:pid).and_return('')\n      @lock.lock\n      expect(Puppet::FileSystem.exist?(@lock.file_path)).to be_truthy\n      expect(Puppet::FileSystem.read(@lock.file_path)).to be_empty\n    end\n\n    it 'should replace an existing empty lockfile with a pid, given a subsequent lock call made against a valid pid' do\n      # empty pid results in empty lockfile\n      allow(Process).to receive(:pid).and_return('')\n      @lock.lock\n      expect(Puppet::FileSystem.exist?(@lock.file_path)).to be_truthy\n\n      # next lock call with valid pid kills existing empty lockfile\n      allow(Process).to receive(:pid).and_return(1234)\n      @lock.lock\n      expect(Puppet::FileSystem.exist?(@lock.file_path)).to be_truthy\n      expect(Puppet::FileSystem.read(@lock.file_path)).to eq('1234')\n    end\n\n    it \"should expose the lock file_path\" do\n      expect(@lock.file_path).to eq(@lockfile)\n    end\n  end\n\n  describe \"#unlock\" do\n    it \"should not be locked anymore\" do\n      @lock.lock\n      @lock.unlock\n      expect(@lock).not_to be_locked\n    end\n\n    it \"should return false if not locked\" do\n      expect(@lock.unlock).to be_falsey\n    end\n\n    it \"should return true if properly unlocked\" do\n      @lock.lock\n      expect(@lock.unlock).to be_truthy\n    end\n\n    it \"should get rid of the lock file\" do\n      @lock.lock\n      @lock.unlock\n      expect(Puppet::FileSystem.exist?(@lockfile)).to be_falsey\n    end\n  end\n\n  describe \"#locked?\" do\n    it \"should return true if locked\" do\n      @lock.lock\n      expect(@lock).to be_locked\n    end\n\n    it \"should remove the lockfile when pid is missing\" do\n      allow(Process).to receive(:pid).and_return('')\n      @lock.lock\n      expect(@lock.locked?).to be_falsey\n      expect(Puppet::FileSystem.exist?(@lock.file_path)).to be_falsey\n    end\n  end\n\n  describe '#lock_pid' do\n    it 'should return nil if the pid is empty' do\n      # fake pid to get empty lockfile\n      allow(Process).to receive(:pid).and_return('')\n      @lock.lock\n      expect(@lock.lock_pid).to eq(nil)\n    end\n  end\n\n  describe \"with a stale lock\" do\n    before(:each) do\n      # fake our pid to be 1234\n      allow(Process).to receive(:pid).and_return(1234)\n      # lock the file\n      @lock.lock\n      # fake our pid to be a different pid, to simulate someone else\n      #  holding the lock\n      allow(Process).to receive(:pid).and_return(6789)\n\n      allow(Process).to receive(:kill).with(0, 6789)\n      allow(Process).to receive(:kill).with(0, 1234).and_raise(Errno::ESRCH)\n    end\n\n    it \"should not be locked\" do\n      expect(@lock).not_to be_locked\n    end\n\n    describe \"#lock\" do\n      it \"should clear stale locks\" do\n        expect(@lock.locked?).to be_falsey\n        expect(Puppet::FileSystem.exist?(@lockfile)).to be_falsey\n      end\n\n      it \"should replace with new locks\" do\n        @lock.lock\n        expect(Puppet::FileSystem.exist?(@lockfile)).to be_truthy\n        expect(@lock.lock_pid).to eq(6789)\n        expect(@lock).to be_mine\n        expect(@lock).to be_locked\n      end\n    end\n\n    describe \"#unlock\" do\n      it \"should not be allowed\" do\n        expect(@lock.unlock).to be_falsey\n      end\n\n      it \"should not remove the lock file\" do\n        @lock.unlock\n        expect(Puppet::FileSystem.exist?(@lockfile)).to be_truthy\n      end\n    end\n  end\n\n  describe \"with no access to open the process on Windows\", :if => Puppet.features.microsoft_windows? do\n    before(:each) do\n      allow(Process).to receive(:pid).and_return(6789)\n      @lock.lock\n      allow(Process).to receive(:pid).and_return(1234)\n      exception = Puppet::Util::Windows::Error.new('Access Denied', 5) # ERROR_ACCESS_DENIED\n      allow(Puppet::Util::Windows::Process).to receive(:get_process_image_name_by_pid).with(6789).and_raise(exception)\n      allow(Process).to receive(:kill).with(0, 6789)\n      allow(Process).to receive(:kill).with(0, 1234)\n    end\n\n    it \"should be locked\" do\n      expect(@lock).to be_locked\n    end\n\n    describe \"#lock\" do\n      it \"should not be possible\" do\n        expect(@lock.lock).to be_falsey\n      end\n\n      it \"should not overwrite the lock\" do\n        @lock.lock\n        expect(@lock).not_to be_mine\n      end\n    end\n\n    describe \"#unlock\" do\n      it \"should not be possible\" do\n        expect(@lock.unlock).to be_falsey\n      end\n\n      it \"should not remove the lock file\" do\n        @lock.unlock\n        expect(Puppet::FileSystem.exist?(@lockfile)).to be_truthy\n      end\n\n      it \"should still not be our lock\" do\n        @lock.unlock\n        expect(@lock).not_to be_mine\n      end\n    end\n  end\n\n  describe \"with another process lock\" do\n    before(:each) do\n      # fake our pid to be 1234\n      allow(Process).to receive(:pid).and_return(1234)\n      if Puppet::Util::Platform.windows?\n        allow(Puppet::Util::Windows::Process).to receive(:get_process_image_name_by_pid).with(1234).and_return('C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin\\ruby.exe')\n      else\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-eq', 1234, '-o', 'comm=']).and_return('puppet')\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-eq', 1234, '-o', 'args=']).and_return('puppet')\n      end\n      # lock the file\n      @lock.lock\n      # fake our pid to be a different pid, to simulate someone else\n      #  holding the lock\n      allow(Process).to receive(:pid).and_return(6789)\n\n      allow(Process).to receive(:kill).with(0, 6789)\n      allow(Process).to receive(:kill).with(0, 1234)\n    end\n\n    it \"should be locked\" do\n      expect(@lock).to be_locked\n    end\n\n    it \"should not be mine\" do\n      expect(@lock).not_to be_mine\n    end\n\n    it \"should be locked if the other process is a puppet gem\" do\n      File.write(@lockfile, \"1234\")\n\n      if Puppet::Util::Platform.windows?\n        allow(Puppet::Util::Windows::Process).to receive(:get_process_image_name_by_pid).with(1234).and_return('C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin\\ruby.exe')\n      else\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-p', 1234, '-o', 'comm=']).and_return('ruby')\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-p', 1234, '-o', 'args=']).and_return('ruby /root/puppet/.bundle/ruby/2.3.0/bin/puppet agent --no-daemonize -v')\n      end\n      expect(@lock).to be_locked\n    end\n\n    it \"should not be mine if the other process is a puppet gem\" do\n      File.write(@lockfile, \"1234\")\n\n      if Puppet::Util::Platform.windows?\n        allow(Puppet::Util::Windows::Process).to receive(:get_process_image_name_by_pid).with(1234).and_return('C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin\\ruby.exe')\n      else\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-p', 1234, '-o', 'comm=']).and_return('ruby')\n        allow(Puppet::Util::Execution).to receive(:execute).with(['ps', '-p', 1234, '-o', 'args=']).and_return('ruby /root/puppet/.bundle/ruby/2.3.0/bin/puppet agent --no-daemonize -v')\n      end\n      expect(@lock).to_not be_mine\n    end\n\n    describe \"#lock\" do\n      it \"should not be possible\" do\n        expect(@lock.lock).to be_falsey\n      end\n\n      it \"should not overwrite the lock\" do\n        @lock.lock\n        expect(@lock).not_to be_mine\n      end\n    end\n\n    describe \"#unlock\" do\n      it \"should not be possible\" do\n        expect(@lock.unlock).to be_falsey\n      end\n\n      it \"should not remove the lock file\" do\n        @lock.unlock\n        expect(Puppet::FileSystem.exist?(@lockfile)).to be_truthy\n      end\n\n      it \"should still not be our lock\" do\n        @lock.unlock\n        expect(@lock).not_to be_mine\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/plist_spec.rb",
    "content": "# coding: utf-8\n\nrequire 'spec_helper'\nrequire 'puppet/util/plist'\nrequire 'puppet_spec/files'\n\ndescribe Puppet::Util::Plist, :if => Puppet.features.cfpropertylist? do\n  include PuppetSpec::Files\n\n  let(:valid_xml_plist) do\n    '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n     <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n     <plist version=\"1.0\">\n     <dict>\n       <key>LastUsedPrinters</key>\n       <array>\n         <dict>\n                 <key>Network</key>\n                 <string>10.85.132.1</string>\n                 <key>PrinterID</key>\n                 <string>baskerville_corp_puppetlabs_net</string>\n         </dict>\n         <dict>\n                 <key>Network</key>\n                 <string>10.14.96.1</string>\n                 <key>PrinterID</key>\n                 <string>Statler</string>\n         </dict>\n       </array>\n    </dict>\n    </plist>'\n  end\n  let(:invalid_xml_plist) do\n    '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n     <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n     <plist version=\"1.0\">\n     <dict>\n       <key>LastUsedPrinters</key>\n       <array>\n         <dict>\n                 <!-- this comment is --terrible -->\n                 <key>Network</key>\n                 <string>10.85.132.1</string>\n                 <key>PrinterID</key>\n                 <string>baskerville_corp_puppetlabs_net</string>\n         </dict>\n         <dict>\n                 <key>Network</key>\n                 <string>10.14.96.1</string>\n                 <key>PrinterID</key>\n                 <string>Statler</string>\n         </dict>\n       </array>\n    </dict>\n    </plist>'\n  end\n  let(:ascii_xml_plist) do\n    '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n     <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n     <plist version=\"1.0\">\n     <dict>\n       <key>RecordName</key>\n         <array>\n           <string>Timișoara</string>\n           <string>Tōkyō</string>\n         </array>\n     </dict>\n     </plist>'.force_encoding(Encoding::US_ASCII)\n  end\n  let(:non_plist_data) do\n    \"Take my love, take my land\n     Take me where I cannot stand\n     I don't care, I'm still free\n     You can't take the sky from me.\"\n  end\n  let(:binary_data) do\n    \"\\xCF\\xFA\\xED\\xFE\\a\\u0000\\u0000\\u0001\\u0003\\u0000\\u0000\\x80\\u0002\\u0000\\u0000\\u0000\\u0012\\u0000\\u0000\\u0000\\b\"\n  end\n  let(:valid_xml_plist_hash) { {\"LastUsedPrinters\"=>[{\"Network\"=>\"10.85.132.1\", \"PrinterID\"=>\"baskerville_corp_puppetlabs_net\"}, {\"Network\"=>\"10.14.96.1\", \"PrinterID\"=>\"Statler\"}]} }\n  let(:ascii_xml_plist_hash) { {\"RecordName\"=>[\"Timișoara\", \"Tōkyō\"]} }\n  let(:plist_path) { file_containing('sample.plist', valid_xml_plist) }\n  let(:binary_plist_magic_number) { 'bplist00' }\n  let(:bad_xml_doctype) { '<!DOCTYPE plist PUBLIC -//Apple Computer' }\n  let(:good_xml_doctype) { '<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">' }\n\n  describe \"#read_plist_file\" do\n    it \"calls #convert_cfpropertylist_to_native_types on a plist object when a valid binary plist is read\" do\n      allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return(binary_plist_magic_number)\n      allow(subject).to receive(:new_cfpropertylist).with({:file => plist_path}).and_return('plist_object')\n      expect(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object').and_return('plist_hash')\n      expect(subject.read_plist_file(plist_path)).to eq('plist_hash')\n    end\n\n    it \"returns a valid hash when a valid XML plist is read\" do\n      allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')\n      allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(valid_xml_plist)\n      expect(subject.read_plist_file(plist_path)).to eq(valid_xml_plist_hash)\n    end\n\n    it \"raises a debug message and replaces a bad XML plist doctype should one be encountered\" do\n      allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')\n      allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(bad_xml_doctype)\n      expect(subject).to receive(:new_cfpropertylist).with({:data => good_xml_doctype}).and_return('plist_object')\n      allow(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object').and_return('plist_hash')\n      expect(Puppet).to receive(:debug).with(\"Had to fix plist with incorrect DOCTYPE declaration: #{plist_path}\")\n      expect(subject.read_plist_file(plist_path)).to eq('plist_hash')\n    end\n\n    it \"attempts to read pure xml using plutil when reading an improperly formatted service plist\" do\n      allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')\n      allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(invalid_xml_plist)\n      expect(Puppet).to receive(:debug).with(/^Failed with CFFormatError/)\n      expect(Puppet).to receive(:debug).with(\"Plist #{plist_path} ill-formatted, converting with plutil\")\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', plist_path],\n              {:failonfail => true, :combine => true})\n        .and_return(Puppet::Util::Execution::ProcessOutput.new(valid_xml_plist, 0))\n      expect(subject.read_plist_file(plist_path)).to eq(valid_xml_plist_hash)\n    end\n\n    it \"returns nil when direct parsing and plutil conversion both fail\" do\n      allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')\n      allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(non_plist_data)\n      expect(Puppet).to receive(:debug).with(/^Failed with (CFFormatError|NoMethodError)/)\n      expect(Puppet).to receive(:debug).with(\"Plist #{plist_path} ill-formatted, converting with plutil\")\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', plist_path],\n              {:failonfail => true, :combine => true})\n        .and_raise(Puppet::ExecutionFailure, 'boom')\n      expect(subject.read_plist_file(plist_path)).to eq(nil)\n    end\n\n    it \"returns nil when file is a non-plist binary blob\" do\n      allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')\n      allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(binary_data)\n      expect(Puppet).to receive(:debug).with(/^Failed with (CFFormatError|ArgumentError)/)\n      expect(Puppet).to receive(:debug).with(\"Plist #{plist_path} ill-formatted, converting with plutil\")\n      expect(Puppet::Util::Execution).to receive(:execute)\n        .with(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', plist_path],\n              {:failonfail => true, :combine => true})\n        .and_raise(Puppet::ExecutionFailure, 'boom')\n      expect(subject.read_plist_file(plist_path)).to eq(nil)\n    end\n  end\n\n  describe \"#parse_plist\" do\n    it \"returns a valid hash when a valid XML plist is provided\" do\n      expect(subject.parse_plist(valid_xml_plist)).to eq(valid_xml_plist_hash)\n    end\n\n    it \"returns a valid hash when an ASCII XML plist is provided\" do\n      expect(subject.parse_plist(ascii_xml_plist)).to eq(ascii_xml_plist_hash)\n    end\n\n    it \"raises a debug message and replaces a bad XML plist doctype should one be encountered\" do\n      expect(subject).to receive(:new_cfpropertylist).with({:data => good_xml_doctype}).and_return('plist_object')\n      allow(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object')\n      expect(Puppet).to receive(:debug).with(\"Had to fix plist with incorrect DOCTYPE declaration: #{plist_path}\")\n      subject.parse_plist(bad_xml_doctype, plist_path)\n    end\n\n    it \"raises a debug message with malformed plist\" do\n      allow(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object')\n      expect(Puppet).to receive(:debug).with(/^Failed with CFFormatError/)\n      subject.parse_plist(\"<plist><dict><key>Foo</key>\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/posix_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/ffi/posix'\nrequire 'puppet/util/posix'\n\nclass PosixTest\n  include Puppet::Util::POSIX\nend\n\ndescribe Puppet::Util::POSIX do\n  before do\n    @posix = PosixTest.new\n  end\n\n  describe '.groups_of' do \n    let(:mock_user_data) { double(user, :gid => 1000) }\n\n    let(:ngroups_ptr) { double('FFI::MemoryPointer', :address => 0x0001, :size => 4) }\n    let(:groups_ptr) { double('FFI::MemoryPointer', :address => 0x0002, :size => Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS) }\n\n    let(:mock_groups) do\n      [\n        ['root', ['root'], 0],\n        ['nomembers', [], 5 ],\n        ['group1', ['user1', 'user2'], 1001],\n        ['group2', ['user2'], 2002],\n        ['group1', ['user1', 'user2'], 1001],\n        ['group3', ['user1'], 3003],\n        ['group4', ['user2'], 4004],\n        ['user1', [], 1111],\n        ['user2', [], 2222]\n      ].map do |(name, members, gid)|\n        group_struct = double(\"Group #{name}\")\n        allow(group_struct).to receive(:name).and_return(name)\n        allow(group_struct).to receive(:mem).and_return(members)\n        allow(group_struct).to receive(:gid).and_return(gid)\n\n        group_struct\n      end\n    end\n\n    def prepare_user_and_groups_env(user, groups)\n      groups_gids = []\n      groups_and_user = []\n      groups_and_user.replace(groups)\n      groups_and_user.push(user)\n\n      groups_and_user.each do |group|\n        mock_group = mock_groups.find { |m| m.name == group }\n        groups_gids.push(mock_group.gid)\n\n        allow(Puppet::Etc).to receive(:getgrgid).with(mock_group.gid).and_return(mock_group)\n      end\n\n      if groups_and_user.size > Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS\n        allow(ngroups_ptr).to receive(:read_int).and_return(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS, groups_and_user.size)\n      else\n        allow(ngroups_ptr).to receive(:read_int).and_return(groups_and_user.size)\n      end\n\n      allow(groups_ptr).to receive(:get_array_of_uint).with(0, groups_and_user.size).and_return(groups_gids)\n      allow(Puppet::Etc).to receive(:getpwnam).with(user).and_return(mock_user_data)\n    end\n\n    before(:each) do\n      allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n    end\n\n    describe 'when it uses FFI function getgrouplist' do\n      before(:each) do\n        allow(FFI::MemoryPointer).to receive(:new).with(:int).and_yield(ngroups_ptr)\n        allow(FFI::MemoryPointer).to receive(:new).with(:uint, Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_yield(groups_ptr)\n        allow(ngroups_ptr).to receive(:write_int).with(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_return(ngroups_ptr)\n      end\n\n      describe 'when there are groups' do\n        context 'for user1' do\n          let(:user) { 'user1' }\n          let(:expected_groups) { ['group1', 'group3'] }\n\n          before(:each) do\n            prepare_user_and_groups_env(user, expected_groups)\n            allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)\n          end\n\n          it \"should return the groups for given user\" do\n            expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n          end\n\n          it 'should not print any debug message about falling back to Puppet::Etc.group' do\n            expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n            Puppet::Util::POSIX.groups_of(user)\n          end\n        end\n\n        context 'for user2' do\n          let(:user) { 'user2' }\n          let(:expected_groups) { ['group1', 'group2', 'group4'] }\n\n          before(:each) do\n            prepare_user_and_groups_env(user, expected_groups)\n            allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n            allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)\n          end\n\n          it \"should return the groups for given user\" do\n            expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n          end\n\n          it 'should not print any debug message about falling back to Puppet::Etc.group' do\n            expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n            Puppet::Util::POSIX.groups_of(user)\n          end\n        end\n      end\n\n      describe 'when there are no groups' do\n        let(:user) { 'nomembers' }\n        let(:expected_groups) { [] }\n\n        before(:each) do\n          prepare_user_and_groups_env(user, expected_groups)\n          allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n          allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)\n        end\n\n        it \"should return no groups for given user\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n        end\n\n        it 'should not print any debug message about falling back to Puppet::Etc.group' do\n          expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n      describe 'when primary group explicitly contains user' do\n        let(:user) { 'root' }\n        let(:expected_groups) { ['root'] }\n\n        before(:each) do\n          prepare_user_and_groups_env(user, expected_groups)\n          allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n          allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)\n        end\n\n        it \"should return the groups, including primary group, for given user\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n        end\n\n        it 'should not print any debug message about falling back to Puppet::Etc.group' do\n          expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n      describe 'when primary group does not explicitly contain user' do\n        let(:user) { 'user1' }\n        let(:expected_groups) { ['group1', 'group3'] }\n\n        before(:each) do\n          prepare_user_and_groups_env(user, expected_groups)\n          allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n          allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)\n        end\n\n        it \"should not return primary group for given user\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).not_to include(user)\n        end\n\n        it 'should not print any debug message about falling back to Puppet::Etc.group' do\n          expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n      context 'number of groups' do\n        before(:each) do\n          stub_const(\"Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS\", 2)\n          prepare_user_and_groups_env(user, expected_groups)\n\n          allow(FFI::MemoryPointer).to receive(:new).with(:uint, Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_yield(groups_ptr)\n          allow(ngroups_ptr).to receive(:write_int).with(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_return(ngroups_ptr)\n        end\n\n        describe 'when there are less than maximum expected number of groups' do\n          let(:user) { 'root' }\n          let(:expected_groups) { ['root'] }\n\n          before(:each) do\n            allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n            allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)\n          end\n\n          it \"should return the groups for given user, after one 'getgrouplist' call\" do\n            expect(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).once\n            expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n          end\n\n          it 'should not print any debug message about falling back to Puppet::Etc.group' do\n            expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n            Puppet::Util::POSIX.groups_of(user)\n          end\n        end\n\n        describe 'when there are more than maximum expected number of groups' do\n          let(:user) { 'user1' }\n          let(:expected_groups) { ['group1', 'group3'] }\n\n          before(:each) do\n            allow(FFI::MemoryPointer).to receive(:new).with(:uint, Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS * 2).and_yield(groups_ptr)\n            allow(ngroups_ptr).to receive(:write_int).with(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS * 2).and_return(ngroups_ptr)\n\n            allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(true)\n            allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(-1, 1)\n          end\n\n          it \"should return the groups for given user, after two 'getgrouplist' calls\" do\n            expect(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).twice\n            expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n          end\n\n          it 'should not print any debug message about falling back to Puppet::Etc.group' do\n            expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)\n            Puppet::Util::POSIX.groups_of(user)\n          end\n        end\n      end\n    end\n\n    describe 'when it falls back to Puppet::Etc.group method' do\n      before(:each) do\n        etc_stub = receive(:group)\n        mock_groups.each do |mock_group|\n          etc_stub = etc_stub.and_yield(mock_group)\n        end\n        allow(Puppet::Etc).to etc_stub\n\n        allow(Puppet::Etc).to receive(:getpwnam).with(user).and_raise(ArgumentError, \"can't find user for #{user}\")\n        allow(Puppet).to receive(:debug)\n\n        allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist, any_args).and_return(false)\n      end\n\n      describe 'when there are groups' do\n        context 'for user1' do\n          let(:user) { 'user1' }\n          let(:expected_groups) { ['group1', 'group3'] }\n\n          it \"should return the groups for given user\" do\n            expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n          end\n\n          it 'logs a debug message' do\n            expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available\")\n            Puppet::Util::POSIX.groups_of(user)\n          end\n        end\n\n        context 'for user2' do\n          let(:user) { 'user2' }\n          let(:expected_groups) { ['group1', 'group2', 'group4'] }\n\n          it \"should return the groups for given user\" do\n            expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n          end\n\n          it 'logs a debug message' do\n            expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available\")\n            Puppet::Util::POSIX.groups_of(user)\n          end\n        end\n      end\n\n      describe 'when there are no groups' do\n        let(:user) { 'nomembers' }\n        let(:expected_groups) { [] }\n\n        it \"should return no groups for given user\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n        end\n\n        it 'logs a debug message' do\n          expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available\")\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n      describe 'when primary group explicitly contains user' do\n        let(:user) { 'root' }\n        let(:expected_groups) { ['root'] }\n\n        it \"should return the groups, including primary group, for given user\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n        end\n\n        it 'logs a debug message' do\n          expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available\")\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n      describe 'when primary group does not explicitly contain user' do\n        let(:user) { 'user1' }\n        let(:expected_groups) { ['group1', 'group3'] }\n\n        it \"should not return primary group for given user\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).not_to include(user)\n        end\n\n        it 'logs a debug message' do\n          expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available\")\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n      describe \"when the 'getgrouplist' method is not available\" do\n        let(:user) { 'user1' }\n        let(:expected_groups) { ['group1', 'group3'] }\n\n        before(:each) do\n          allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist).and_return(false)\n        end\n\n        it \"should return the groups\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n        end\n\n        it 'logs a debug message' do\n          expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available\")\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n\n\n      describe \"when ffi is not available on the machine\" do\n        let(:user) { 'user1' }\n        let(:expected_groups) { ['group1', 'group3'] }\n\n        before(:each) do\n          allow(Puppet::Util::POSIX).to receive(:require_relative).with('../../puppet/ffi/posix').and_raise(LoadError, 'cannot load such file -- ffi')\n        end\n\n        it \"should return the groups\" do\n          expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)\n        end\n\n        it 'logs a debug message' do\n          expect(Puppet).to receive(:debug).with(\"Falling back to Puppet::Etc.group: cannot load such file -- ffi\")\n          Puppet::Util::POSIX.groups_of(user)\n        end\n      end\n    end\n  end\n\n  [:group, :gr].each do |name|\n    it \"should return :gid as the field for #{name}\" do\n      expect(@posix.idfield(name)).to eq(:gid)\n    end\n\n    it \"should return :getgrgid as the id method for #{name}\" do\n      expect(@posix.methodbyid(name)).to eq(:getgrgid)\n    end\n\n    it \"should return :getgrnam as the name method for #{name}\" do\n      expect(@posix.methodbyname(name)).to eq(:getgrnam)\n    end\n  end\n\n  [:user, :pw, :passwd].each do |name|\n    it \"should return :uid as the field for #{name}\" do\n      expect(@posix.idfield(name)).to eq(:uid)\n    end\n\n    it \"should return :getpwuid as the id method for #{name}\" do\n      expect(@posix.methodbyid(name)).to eq(:getpwuid)\n    end\n\n    it \"should return :getpwnam as the name method for #{name}\" do\n      expect(@posix.methodbyname(name)).to eq(:getpwnam)\n    end\n  end\n\n  describe \"when retrieving a posix field\" do\n    before do\n      @thing = double('thing', :field => \"asdf\")\n    end\n\n    it \"should fail if no id was passed\" do\n      expect { @posix.get_posix_field(\"asdf\", \"bar\", nil) }.to raise_error(Puppet::DevError)\n    end\n\n    describe \"and the id is an integer\" do\n      it \"should log an error and return nil if the specified id is greater than the maximum allowed ID\" do\n        Puppet[:maximum_uid] = 100\n        expect(Puppet).to receive(:err)\n\n        expect(@posix.get_posix_field(\"asdf\", \"bar\", 200)).to be_nil\n      end\n\n      it \"should use the method return by :methodbyid and return the specified field\" do\n        expect(Etc).to receive(:getgrgid).and_return(@thing)\n\n        expect(@thing).to receive(:field).and_return(\"myval\")\n\n        expect(@posix.get_posix_field(:gr, :field, 200)).to eq(\"myval\")\n      end\n\n      it \"should return nil if the method throws an exception\" do\n        expect(Etc).to receive(:getgrgid).and_raise(ArgumentError)\n\n        expect(@thing).not_to receive(:field)\n\n        expect(@posix.get_posix_field(:gr, :field, 200)).to be_nil\n      end\n    end\n\n    describe \"and the id is not an integer\" do\n      it \"should use the method return by :methodbyid and return the specified field\" do\n        expect(Etc).to receive(:getgrnam).and_return(@thing)\n\n        expect(@thing).to receive(:field).and_return(\"myval\")\n\n        expect(@posix.get_posix_field(:gr, :field, \"asdf\")).to eq(\"myval\")\n      end\n\n      it \"should return nil if the method throws an exception\" do\n        expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)\n\n        expect(@thing).not_to receive(:field)\n\n        expect(@posix.get_posix_field(:gr, :field, \"asdf\")).to be_nil\n      end\n    end\n  end\n\n  describe \"when returning the gid\" do\n    before do\n      allow(@posix).to receive(:get_posix_field)\n    end\n\n    describe \"and the group is an integer\" do\n      it \"should convert integers specified as a string into an integer\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100)\n\n        @posix.gid(\"100\")\n      end\n\n      it \"should look up the name for the group\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100)\n\n        @posix.gid(100)\n      end\n\n      it \"should return nil if the group cannot be found\" do\n        expect(@posix).to receive(:get_posix_field).once.and_return(nil)\n        expect(@posix).not_to receive(:search_posix_field)\n\n        expect(@posix.gid(100)).to be_nil\n      end\n\n      it \"should use the found name to look up the id\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(\"asdf\")\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\").and_return(100)\n\n        expect(@posix.gid(100)).to eq(100)\n      end\n\n      # LAK: This is because some platforms have a broken Etc module that always return\n      # the same group.\n      it \"should use :search_posix_field if the discovered id does not match the passed-in id\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(\"asdf\")\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\").and_return(50)\n\n        expect(@posix).to receive(:search_posix_field).with(:group, :gid, 100).and_return(\"asdf\")\n\n        expect(@posix.gid(100)).to eq(\"asdf\")\n      end\n    end\n\n    describe \"and the group is a string\" do\n      it \"should look up the gid for the group\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\")\n\n        @posix.gid(\"asdf\")\n      end\n\n      it \"should return nil if the group cannot be found\" do\n        expect(@posix).to receive(:get_posix_field).once.and_return(nil)\n        expect(@posix).not_to receive(:search_posix_field)\n\n        expect(@posix.gid(\"asdf\")).to be_nil\n      end\n\n      it \"should use the found gid to look up the nam\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(\"asdf\")\n\n        expect(@posix.gid(\"asdf\")).to eq(100)\n      end\n\n      it \"returns the id without full groups query if multiple groups have the same id\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(\"boo\")\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"boo\").and_return(100)\n\n        expect(@posix).not_to receive(:search_posix_field)\n        expect(@posix.gid(\"asdf\")).to eq(100)\n      end\n\n      it \"returns the id with full groups query if name is nil\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(nil)\n        expect(@posix).not_to receive(:get_posix_field).with(:group, :gid, nil)\n\n\n        expect(@posix).to receive(:search_posix_field).with(:group, :gid, \"asdf\").and_return(100)\n        expect(@posix.gid(\"asdf\")).to eq(100)\n      end\n\n      it \"should use :search_posix_field if the discovered name does not match the passed-in name\" do\n        expect(@posix).to receive(:get_posix_field).with(:group, :gid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(\"boo\")\n\n        expect(@posix).to receive(:search_posix_field).with(:group, :gid, \"asdf\").and_return(\"asdf\")\n\n        expect(@posix.gid(\"asdf\")).to eq(\"asdf\")\n      end\n    end\n  end\n\n  describe \"when returning the uid\" do\n    before do\n      allow(@posix).to receive(:get_posix_field)\n    end\n\n    describe \"and the group is an integer\" do\n      it \"should convert integers specified as a string into an integer\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100)\n\n        @posix.uid(\"100\")\n      end\n\n      it \"should look up the name for the group\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100)\n\n        @posix.uid(100)\n      end\n\n      it \"should return nil if the group cannot be found\" do\n        expect(@posix).to receive(:get_posix_field).once.and_return(nil)\n        expect(@posix).not_to receive(:search_posix_field)\n\n        expect(@posix.uid(100)).to be_nil\n      end\n\n      it \"should use the found name to look up the id\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(\"asdf\")\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\").and_return(100)\n\n        expect(@posix.uid(100)).to eq(100)\n      end\n\n      # LAK: This is because some platforms have a broken Etc module that always return\n      # the same group.\n      it \"should use :search_posix_field if the discovered id does not match the passed-in id\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(\"asdf\")\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\").and_return(50)\n\n        expect(@posix).to receive(:search_posix_field).with(:passwd, :uid, 100).and_return(\"asdf\")\n\n        expect(@posix.uid(100)).to eq(\"asdf\")\n      end\n    end\n\n    describe \"and the group is a string\" do\n      it \"should look up the uid for the group\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\")\n\n        @posix.uid(\"asdf\")\n      end\n\n      it \"should return nil if the group cannot be found\" do\n        expect(@posix).to receive(:get_posix_field).once.and_return(nil)\n        expect(@posix).not_to receive(:search_posix_field)\n\n        expect(@posix.uid(\"asdf\")).to be_nil\n      end\n\n      it \"should use the found uid to look up the nam\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(\"asdf\")\n\n        expect(@posix.uid(\"asdf\")).to eq(100)\n      end\n\n      it \"returns the id without full users query if multiple users have the same id\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(\"boo\")\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"boo\").and_return(100)\n\n        expect(@posix).not_to receive(:search_posix_field)\n        expect(@posix.uid(\"asdf\")).to eq(100)\n      end\n\n      it \"returns the id with full users query if name is nil\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(nil)\n        expect(@posix).not_to receive(:get_posix_field).with(:passwd, :uid, nil)\n\n\n        expect(@posix).to receive(:search_posix_field).with(:passwd, :uid, \"asdf\").and_return(100)\n        expect(@posix.uid(\"asdf\")).to eq(100)\n      end\n\n      it \"should use :search_posix_field if the discovered name does not match the passed-in name\" do\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, \"asdf\").and_return(100)\n        expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(\"boo\")\n\n        expect(@posix).to receive(:search_posix_field).with(:passwd, :uid, \"asdf\").and_return(\"asdf\")\n\n        expect(@posix.uid(\"asdf\")).to eq(\"asdf\")\n      end\n    end\n  end\n\n  it \"should be able to iteratively search for posix values\" do\n    expect(@posix).to respond_to(:search_posix_field)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/profiler/aggregate_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/profiler'\nrequire 'puppet/util/profiler/around_profiler'\nrequire 'puppet/util/profiler/aggregate'\n\ndescribe Puppet::Util::Profiler::Aggregate do\n  let(:logger) { AggregateSimpleLog.new }\n  let(:profiler) { Puppet::Util::Profiler::Aggregate.new(logger, nil) }\n  let(:profiler_mgr) do\n    p = Puppet::Util::Profiler::AroundProfiler.new\n    p.add_profiler(profiler)\n    p\n  end\n\n  it \"tracks the aggregate counts and time for the hierarchy of metrics\" do\n    profiler_mgr.profile(\"Looking up hiera data in production environment\", [\"function\", \"hiera_lookup\", \"production\"]) { sleep 0.01 }\n    profiler_mgr.profile(\"Looking up hiera data in test environment\", [\"function\", \"hiera_lookup\", \"test\"]) {}\n    profiler_mgr.profile(\"looking up stuff for compilation\", [\"compiler\", \"lookup\"]) { sleep 0.01 }\n    profiler_mgr.profile(\"COMPILING ALL OF THE THINGS!\", [\"compiler\", \"compiling\"]) {}\n\n    expect(profiler.values[\"function\"].count).to eq(2)\n    expect(profiler.values[\"function\"].time).to be > 0\n    expect(profiler.values[\"function\"][\"hiera_lookup\"].count).to eq(2)\n    expect(profiler.values[\"function\"][\"hiera_lookup\"][\"production\"].count).to eq(1)\n    expect(profiler.values[\"function\"][\"hiera_lookup\"][\"test\"].count).to eq(1)\n    expect(profiler.values[\"function\"].time).to be >= profiler.values[\"function\"][\"hiera_lookup\"][\"test\"].time\n\n    expect(profiler.values[\"compiler\"].count).to eq(2)\n    expect(profiler.values[\"compiler\"].time).to be > 0\n    expect(profiler.values[\"compiler\"][\"lookup\"].count).to eq(1)\n    expect(profiler.values[\"compiler\"][\"compiling\"].count).to eq(1)\n    expect(profiler.values[\"compiler\"].time).to be >= profiler.values[\"compiler\"][\"lookup\"].time\n\n    profiler.shutdown\n\n    expect(logger.output).to match(/function -> hiera_lookup: .*\\(2 calls\\)\\nfunction -> hiera_lookup ->.*\\(1 calls\\)/)\n    expect(logger.output).to match(/compiler: .*\\(2 calls\\)\\ncompiler ->.*\\(1 calls\\)/)\n  end\n\n  it \"supports both symbols and strings as components of a metric id\" do\n    profiler_mgr.profile(\"yo\", [:foo, \"bar\"]) {}\n  end\n\n  class AggregateSimpleLog\n    attr_reader :output\n\n    def initialize\n      @output = \"\"\n    end\n\n    def call(msg)\n      @output << msg << \"\\n\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/profiler/around_profiler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/profiler'\n\ndescribe Puppet::Util::Profiler::AroundProfiler do\n  let(:child) { TestAroundProfiler.new() }\n  let(:profiler) { Puppet::Util::Profiler::AroundProfiler.new }\n\n  before :each do\n    profiler.add_profiler(child)\n  end\n\n  it \"returns the value of the profiled segment\" do\n    retval = profiler.profile(\"Testing\", [\"testing\"]) { \"the return value\" }\n\n    expect(retval).to eq(\"the return value\")\n  end\n\n  it \"propagates any errors raised in the profiled segment\" do\n    expect do\n      profiler.profile(\"Testing\", [\"testing\"]) { raise \"a problem\" }\n    end.to raise_error(\"a problem\")\n  end\n\n  it \"makes the description and the context available to the `start` and `finish` methods\" do\n    profiler.profile(\"Testing\", [\"testing\"]) { }\n\n    expect(child.context).to eq(\"Testing\")\n    expect(child.description).to eq(\"Testing\")\n  end\n\n  it \"calls finish even when an error is raised\" do\n    begin\n      profiler.profile(\"Testing\", [\"testing\"]) { raise \"a problem\" }\n    rescue\n      expect(child.context).to eq(\"Testing\")\n    end\n  end\n\n  it \"supports multiple profilers\" do\n    profiler2 = TestAroundProfiler.new\n    profiler.add_profiler(profiler2)\n    profiler.profile(\"Testing\", [\"testing\"]) {}\n\n    expect(child.context).to eq(\"Testing\")\n    expect(profiler2.context).to eq(\"Testing\")\n  end\n\n  class TestAroundProfiler\n    attr_accessor :context, :description\n\n    def start(description, metric_id)\n      description\n    end\n\n    def finish(context, description, metric_id)\n      @context = context\n      @description = description\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/util/profiler/logging_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/profiler'\n\ndescribe Puppet::Util::Profiler::Logging do\n  let(:logger) { SimpleLog.new }\n  let(:identifier) { \"Profiling ID\" }\n  let(:logging_profiler) { TestLoggingProfiler.new(logger, identifier) }\n  let(:profiler) do\n    p = Puppet::Util::Profiler::AroundProfiler.new\n    p.add_profiler(logging_profiler)\n    p\n  end\n\n  it \"logs the explanation of the profile results\" do\n    profiler.profile(\"Testing\", [\"test\"]) { }\n\n    expect(logger.messages.first).to match(/the explanation/)\n  end\n\n  it \"describes the profiled segment\" do\n    profiler.profile(\"Tested measurement\", [\"test\"]) { }\n\n    expect(logger.messages.first).to match(/PROFILE \\[#{identifier}\\] \\d Tested measurement/)\n  end\n\n  it \"indicates the order in which segments are profiled\" do\n    profiler.profile(\"Measurement\", [\"measurement\"]) { }\n    profiler.profile(\"Another measurement\", [\"measurement\"]) { }\n\n    expect(logger.messages[0]).to match(/1 Measurement/)\n    expect(logger.messages[1]).to match(/2 Another measurement/)\n  end\n\n  it \"indicates the nesting of profiled segments\" do\n    profiler.profile(\"Measurement\", [\"measurement1\"]) do\n      profiler.profile(\"Nested measurement\", [\"measurement2\"]) { }\n    end\n    profiler.profile(\"Another measurement\", [\"measurement1\"]) do\n      profiler.profile(\"Another nested measurement\", [\"measurement2\"]) { }\n    end\n\n    expect(logger.messages[0]).to match(/1.1 Nested measurement/)\n    expect(logger.messages[1]).to match(/1 Measurement/)\n    expect(logger.messages[2]).to match(/2.1 Another nested measurement/)\n    expect(logger.messages[3]).to match(/2 Another measurement/)\n  end\n\n  class TestLoggingProfiler < Puppet::Util::Profiler::Logging\n    def do_start(metric, description)\n      \"the start\"\n    end\n\n    def do_finish(context, metric, description)\n      {:msg => \"the explanation of #{context}\"}\n    end\n  end\n\n  class SimpleLog\n    attr_reader :messages\n\n    def initialize\n      @messages = []\n    end\n\n    def call(msg)\n      @messages << msg\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/util/profiler/object_counts_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/profiler'\n\ndescribe Puppet::Util::Profiler::ObjectCounts, unless: Puppet::Util::Platform.jruby? do\n  # ObjectSpace is not enabled by default on JRuby\n  it \"reports the changes in the system object counts\" do\n    profiler = Puppet::Util::Profiler::ObjectCounts.new(nil, nil)\n\n    message = profiler.finish(profiler.start)\n\n    expect(message).to match(/ T_STRING: \\d+, /)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/profiler/wall_clock_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/profiler'\n\ndescribe Puppet::Util::Profiler::WallClock do\n\n  it \"logs the number of seconds it took to execute the segment\" do\n    profiler = Puppet::Util::Profiler::WallClock.new(nil, nil)\n\n    message = profiler.do_finish(profiler.start([\"foo\", \"bar\"], \"Testing\"), [\"foo\", \"bar\"], \"Testing\")[:msg]\n\n    expect(message).to match(/took \\d\\.\\d{4} seconds/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/profiler_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/profiler'\n\ndescribe Puppet::Util::Profiler do\n  let(:profiler) { TestProfiler.new() }\n\n  it \"supports adding profilers\" do\n    subject.add_profiler(profiler)\n    expect(subject.current[0]).to eq(profiler)\n  end\n\n  it \"supports removing profilers\" do\n    subject.add_profiler(profiler)\n    subject.remove_profiler(profiler)\n    expect(subject.current.length).to eq(0)\n  end\n\n  it \"supports clearing profiler list\" do\n    subject.add_profiler(profiler)\n    subject.clear\n    expect(subject.current.length).to eq(0)\n  end\n\n  it \"supports profiling\" do\n    subject.add_profiler(profiler)\n    subject.profile(\"hi\", [\"mymetric\"]) {}\n    expect(profiler.context[:metric_id]).to eq([\"mymetric\"])\n    expect(profiler.context[:description]).to eq(\"hi\")\n    expect(profiler.description).to eq(\"hi\")\n  end\n\n  class TestProfiler\n    attr_accessor :context, :metric, :description\n\n    def start(description, metric_id)\n      {:metric_id => metric_id,\n       :description => description}\n    end\n\n    def finish(context, description, metric_id)\n      @context = context\n      @metric_id = metric_id\n      @description = description\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/unit/util/rdoc_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/rdoc'\nrequire 'rdoc/rdoc'\n\ndescribe Puppet::Util::RDoc do\n  describe \"when generating RDoc HTML documentation\" do\n    before :each do\n      @rdoc = double('rdoc')\n      allow(RDoc::RDoc).to receive(:new).and_return(@rdoc)\n    end\n\n    it \"should tell RDoc to generate documentation using the Puppet generator\" do\n      expect(@rdoc).to receive(:document).with(include(\"--fmt\").and(include(\"puppet\")))\n\n      Puppet::Util::RDoc.rdoc(\"output\", [])\n    end\n\n    it \"should tell RDoc to be quiet\" do\n      expect(@rdoc).to receive(:document).with(include(\"--quiet\"))\n\n      Puppet::Util::RDoc.rdoc(\"output\", [])\n    end\n\n    it \"should pass charset to RDoc\" do\n      expect(@rdoc).to receive(:document).with(include(\"--charset\").and(include(\"utf-8\")))\n\n      Puppet::Util::RDoc.rdoc(\"output\", [], \"utf-8\")\n    end\n\n    it \"should tell RDoc to use the given outputdir\" do\n      expect(@rdoc).to receive(:document).with(include(\"--op\").and(include(\"myoutputdir\")))\n\n      Puppet::Util::RDoc.rdoc(\"myoutputdir\", [])\n    end\n\n    it \"should tell RDoc to exclude all files under any modules/<mod>/files section\" do\n      expect(@rdoc).to receive(:document).with(include(\"--exclude\").and(include(\"/modules/[^/]*/files/.*$\")))\n\n      Puppet::Util::RDoc.rdoc(\"myoutputdir\", [])\n    end\n\n    it \"should tell RDoc to exclude all files under any modules/<mod>/templates section\" do\n      expect(@rdoc).to receive(:document).with(include(\"--exclude\").and(include(\"/modules/[^/]*/templates/.*$\")))\n\n      Puppet::Util::RDoc.rdoc(\"myoutputdir\", [])\n    end\n\n    it \"should give all the source directories to RDoc\" do\n      expect(@rdoc).to receive(:document).with(include(\"sourcedir\"))\n\n      Puppet::Util::RDoc.rdoc(\"output\", [\"sourcedir\"])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/reference_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/reference'\n\ndescribe Puppet::Util::Reference do\n  it \"should create valid Markdown extension definition lists\" do\n    my_fragment = nil\n    Puppet::Util::Reference.newreference :testreference, :doc => \"A peer of the type and configuration references, but with no useful information\" do\n      my_term = \"A term\"\n      my_definition = <<-EOT\n        The definition of this term, marked by a colon and a space.\n        We should be able to handle multi-line definitions. Each subsequent\n        line should left-align with the first word character after the colon\n        used as the definition marker.\n\n        We should be able to handle multi-paragraph definitions.\n\n        Leading indentation should be stripped from the definition, which allows\n        us to indent the source string for cosmetic purposes.\n      EOT\n      my_fragment = markdown_definitionlist(my_term, my_definition)\n    end\n    Puppet::Util::Reference.reference(:testreference).send(:to_markdown, true)\n    expect(my_fragment).to eq <<-EOT\nA term\n: The definition of this term, marked by a colon and a space.\n  We should be able to handle multi-line definitions. Each subsequent\n  line should left-align with the first word character after the colon\n  used as the definition marker.\n\n  We should be able to handle multi-paragraph definitions.\n\n  Leading indentation should be stripped from the definition, which allows\n  us to indent the source string for cosmetic purposes.\n\n    EOT\n  end\n\nend\n"
  },
  {
    "path": "spec/unit/util/resource_template_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/resource_template'\n\ndescribe Puppet::Util::ResourceTemplate do\n  describe \"when initializing\" do\n    it \"should fail if the template does not exist\" do\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/template\").and_return(false)\n      expect { Puppet::Util::ResourceTemplate.new(\"/my/template\", double('resource')) }.to raise_error(ArgumentError)\n    end\n\n    it \"should not create the ERB template\" do\n      expect(ERB).not_to receive(:new)\n      expect(Puppet::FileSystem).to receive(:exist?).with(\"/my/template\").and_return(true)\n      Puppet::Util::ResourceTemplate.new(\"/my/template\", double('resource'))\n    end\n  end\n\n  describe \"when evaluating\" do\n    before do\n      allow(Puppet::FileSystem).to receive(:exist?).and_return(true)\n      allow(Puppet::FileSystem).to receive(:read).and_return(\"eh\")\n\n      @template = double('template', :result => nil)\n      allow(ERB).to receive(:new).and_return(@template)\n\n      @resource = double('resource')\n      @wrapper = Puppet::Util::ResourceTemplate.new(\"/my/template\", @resource)\n    end\n\n    it \"should set all of the resource's parameters as instance variables\" do\n      expect(@resource).to receive(:to_hash).and_return(:one => \"uno\", :two => \"dos\")\n      expect(@template).to receive(:result) do |bind|\n        expect(eval(\"@one\", bind)).to eq(\"uno\")\n        expect(eval(\"@two\", bind)).to eq(\"dos\")\n      end\n      @wrapper.evaluate\n    end\n\n    it \"should create a template instance with the contents of the file\" do\n      expect(Puppet::FileSystem).to receive(:read).with(\"/my/template\", {:encoding => 'utf-8'}).and_return(\"yay\")\n      expect(Puppet::Util).to receive(:create_erb).with(\"yay\").and_return(@template)\n\n      allow(@wrapper).to receive(:set_resource_variables)\n\n      @wrapper.evaluate\n    end\n\n    it \"should return the result of the template\" do\n      allow(@wrapper).to receive(:set_resource_variables)\n\n      expect(@wrapper).to receive(:binding).and_return(\"mybinding\")\n      expect(@template).to receive(:result).with(\"mybinding\").and_return(\"myresult\")\n      expect(@wrapper.evaluate).to eq(\"myresult\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/retry_action_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/retry_action'\n\ndescribe Puppet::Util::RetryAction do\n  let (:exceptions) { [ Puppet::Error, NameError ] }\n\n  it \"doesn't retry SystemExit\" do\n    expect do\n      Puppet::Util::RetryAction.retry_action( :retries => 0 ) do\n        raise SystemExit\n      end\n    end.to exit_with(0)\n  end\n\n  it \"doesn't retry NoMemoryError\" do\n    expect do\n      Puppet::Util::RetryAction.retry_action( :retries => 0 ) do\n        raise NoMemoryError, \"OOM\"\n      end\n    end.to raise_error(NoMemoryError, /OOM/)\n  end\n\n  it 'should retry on any exception if no acceptable exceptions given' do\n    expect(Puppet::Util::RetryAction).to receive(:sleep).with( (((2 ** 1) -1) * 0.1) )\n    expect(Puppet::Util::RetryAction).to receive(:sleep).with( (((2 ** 2) -1) * 0.1) )\n\n    expect do\n      Puppet::Util::RetryAction.retry_action( :retries => 2 ) do\n        raise ArgumentError, 'Fake Failure'\n      end\n    end.to raise_exception(Puppet::Util::RetryAction::RetryException::RetriesExceeded)\n  end\n\n  it 'should retry on acceptable exceptions' do\n    expect(Puppet::Util::RetryAction).to receive(:sleep).with( (((2 ** 1) -1) * 0.1) )\n    expect(Puppet::Util::RetryAction).to receive(:sleep).with( (((2 ** 2) -1) * 0.1) )\n\n    expect do\n      Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do\n        raise Puppet::Error, 'Fake Failure'\n      end\n    end.to raise_exception(Puppet::Util::RetryAction::RetryException::RetriesExceeded)\n  end\n\n  it 'should not retry on unacceptable exceptions' do\n    expect(Puppet::Util::RetryAction).not_to receive(:sleep)\n\n    expect do\n      Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do\n        raise ArgumentError\n      end\n    end.to raise_exception(ArgumentError)\n  end\n\n  it 'should succeed if nothing is raised' do\n    expect(Puppet::Util::RetryAction).not_to receive(:sleep)\n\n    Puppet::Util::RetryAction.retry_action( :retries => 2) do\n      true\n    end\n  end\n\n  it 'should succeed if an expected exception is raised retried and succeeds' do\n    should_retry = nil\n    expect(Puppet::Util::RetryAction).to receive(:sleep).once\n\n    Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do\n      if should_retry\n        true\n      else\n        should_retry = true\n        raise Puppet::Error, 'Fake error'\n      end\n    end\n  end\n\n  it \"doesn't mutate caller's arguments\" do\n    options = { :retries => 1 }.freeze\n\n    Puppet::Util::RetryAction.retry_action(options) do\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/rpm_compare_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'puppet/util/rpm_compare'\n\ndescribe Puppet::Util::RpmCompare do\n  class RpmTest\n    extend Puppet::Util::RpmCompare\n  end\n\n  describe '.rpmvercmp' do\n    # test cases munged directly from rpm's own\n    # tests/rpmvercmp.at\n    it { expect(RpmTest.rpmvercmp('1.0', '1.0')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('1.0', '2.0')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('2.0', '1.0')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('2.0.1', '2.0.1')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('2.0', '2.0.1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('2.0.1', '2.0')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('2.0.1a', '2.0.1a')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('2.0.1a', '2.0.1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('2.0.1', '2.0.1a')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('5.5p1', '5.5p1')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('5.5p1', '5.5p2')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('5.5p2', '5.5p1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('5.5p10', '5.5p10')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('5.5p1', '5.5p10')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('5.5p10', '5.5p1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('10xyz', '10.1xyz')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('10.1xyz', '10xyz')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('xyz10', 'xyz10')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('xyz10', 'xyz10.1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('xyz10.1', 'xyz10')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('xyz.4', 'xyz.4')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('xyz.4', '8')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('8', 'xyz.4')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('xyz.4', '2')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('2', 'xyz.4')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('5.5p2', '5.6p1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('5.6p1', '5.5p2')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('5.6p1', '6.5p1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('6.5p1', '5.6p1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('6.0.rc1', '6.0')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('6.0', '6.0.rc1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('10b2', '10a1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('10a2', '10b2')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('1.0aa', '1.0aa')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('1.0a', '1.0aa')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('1.0aa', '1.0a')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('10.0001', '10.0001')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('10.0001', '10.1')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('10.1', '10.0001')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('10.0001', '10.0039')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('10.0039', '10.0001')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('4.999.9', '5.0')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('5.0', '4.999.9')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('20101121', '20101121')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('20101121', '20101122')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('20101122', '20101121')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('2_0', '2_0')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('2.0', '2_0')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('2_0', '2.0')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('a', 'a')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('a+', 'a+')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('a+', 'a_')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('a_', 'a+')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('+a', '+a')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('+a', '_a')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('_a', '+a')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('+_', '+_')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('_+', '+_')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('_+', '_+')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('+', '_')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('_', '+')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1', '1.0~rc1')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1', '1.0')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('1.0', '1.0~rc1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1', '1.0~rc2')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc2', '1.0~rc1')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1~git123', '1.0~rc1~git123')).to eq(0) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1~git123', '1.0~rc1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1', '1.0~rc1~git123')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('1.0~rc1', '1.0arc1')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('', '~')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('~', '~~')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('~', '~+~')).to eq(1) }\n    it { expect(RpmTest.rpmvercmp('~', '~a')).to eq(-1) }\n\n    # non-upstream test cases\n    it { expect(RpmTest.rpmvercmp('405', '406')).to eq(-1) }\n    it { expect(RpmTest.rpmvercmp('1', '0')).to eq(1) }\n  end\n\n  describe '.rpm_compare_evr' do\n    it 'evaluates identical version-release as equal' do\n      expect(RpmTest.rpm_compare_evr('1.2.3-1.el5', '1.2.3-1.el5')).to eq(0)\n    end\n\n    it 'evaluates identical version as equal' do\n      expect(RpmTest.rpm_compare_evr('1.2.3', '1.2.3')).to eq(0)\n    end\n\n    it 'evaluates identical version but older release as less' do\n      expect(RpmTest.rpm_compare_evr('1.2.3-1.el5', '1.2.3-2.el5')).to eq(-1)\n    end\n\n    it 'evaluates identical version but newer release as greater' do\n      expect(RpmTest.rpm_compare_evr('1.2.3-3.el5', '1.2.3-2.el5')).to eq(1)\n    end\n\n    it 'evaluates a newer epoch as greater' do\n      expect(RpmTest.rpm_compare_evr('1:1.2.3-4.5', '1.2.3-4.5')).to eq(1)\n    end\n\n    # these tests describe PUP-1244 logic yet to be implemented\n    it 'evaluates any version as equal to the same version followed by release' do\n      expect(RpmTest.rpm_compare_evr('1.2.3', '1.2.3-2.el5')).to eq(0)\n    end\n\n    # test cases for PUP-682\n    it 'evaluates same-length numeric revisions numerically' do\n      expect(RpmTest.rpm_compare_evr('2.2-405', '2.2-406')).to eq(-1)\n    end\n  end\n\n  describe '.rpm_parse_evr' do\n    it 'parses full simple evr' do\n      version = RpmTest.rpm_parse_evr('0:1.2.3-4.el5')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq(['0', '1.2.3', '4.el5'])\n    end\n\n    it 'parses version only' do\n      version = RpmTest.rpm_parse_evr('1.2.3')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '1.2.3', nil])\n    end\n\n    it 'parses version-release' do\n      version = RpmTest.rpm_parse_evr('1.2.3-4.5.el6')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '1.2.3', '4.5.el6'])\n    end\n\n    it 'parses release with git hash' do\n      version = RpmTest.rpm_parse_evr('1.2.3-4.1234aefd')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '1.2.3', '4.1234aefd'])\n    end\n\n    it 'parses single integer versions' do\n      version = RpmTest.rpm_parse_evr('12345')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '12345', nil])\n    end\n\n    it 'parses text in the epoch to 0' do\n      version = RpmTest.rpm_parse_evr('foo0:1.2.3-4')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '1.2.3', '4'])\n    end\n\n    it 'parses revisions with text' do\n      version = RpmTest.rpm_parse_evr('1.2.3-SNAPSHOT20140107')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '1.2.3', 'SNAPSHOT20140107'])\n    end\n\n    # test cases for PUP-682\n    it 'parses revisions with text and numbers' do\n      version = RpmTest.rpm_parse_evr('2.2-SNAPSHOT20121119105647')\n      expect([version[:epoch], version[:version], version[:release]]).to \\\n        eq([nil, '2.2', 'SNAPSHOT20121119105647'])\n    end\n\n    it 'parses .noarch' do\n      version = RpmTest.rpm_parse_evr('3.0.12-1.el5.centos.noarch')\n      expect(version[:arch]).to eq('noarch')\n    end\n  end\n\n  describe '.compare_values' do\n    it 'treats two nil values as equal' do\n      expect(RpmTest.compare_values(nil, nil)).to eq(0)\n    end\n\n    it 'treats a nil value as less than a non-nil value' do\n      expect(RpmTest.compare_values(nil, '0')).to eq(-1)\n    end\n\n    it 'treats a non-nil value as greater than a nil value' do\n      expect(RpmTest.compare_values('0', nil)).to eq(1)\n    end\n\n    it 'passes two non-nil values on to rpmvercmp' do\n      allow(RpmTest).to receive(:rpmvercmp).and_return(0)\n      expect(RpmTest).to receive(:rpmvercmp).with('s1', 's2')\n      RpmTest.compare_values('s1', 's2')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/rubygems_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/rubygems'\n\ndescribe Puppet::Util::RubyGems::Source do\n  let(:gem_path) { File.expand_path('/foo/gems') }\n  let(:gem_lib) { File.join(gem_path, 'lib') }\n  let(:fake_gem) { double(:full_gem_path => gem_path) }\n\n  describe \"::new\" do\n    it \"returns NoGemsSource if rubygems is not present\" do\n      expect(described_class).to receive(:has_rubygems?).and_return(false)\n      expect(described_class.new).to be_kind_of(Puppet::Util::RubyGems::NoGemsSource)\n    end\n\n    it \"returns Gems18Source if Gem::Specification responds to latest_specs\" do\n      expect(described_class).to receive(:has_rubygems?).and_return(true)\n      expect(described_class.new).to be_kind_of(Puppet::Util::RubyGems::Gems18Source)\n    end\n  end\n\n  describe '::NoGemsSource' do\n    before(:each) { allow(described_class).to receive(:source).and_return(Puppet::Util::RubyGems::NoGemsSource) }\n\n    it \"#directories returns an empty list\" do\n      expect(described_class.new.directories).to eq([])\n    end\n\n    it \"#clear_paths returns nil\" do\n      expect(described_class.new.clear_paths).to be_nil\n    end\n  end\n\n  describe '::Gems18Source' do\n    before(:each) { allow(described_class).to receive(:source).and_return(Puppet::Util::RubyGems::Gems18Source) }\n\n    it \"#directories returns the lib subdirs of Gem::Specification.stubs\" do\n      expect(Gem::Specification).to receive(:stubs).and_return([fake_gem])\n\n      expect(described_class.new.directories).to eq([gem_lib])\n    end\n\n    it \"#clear_paths calls Gem.clear_paths\" do\n      expect(Gem).to receive(:clear_paths)\n      described_class.new.clear_paths\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/run_mode_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::RunMode do\n  before do\n    @run_mode = Puppet::Util::RunMode.new('fake')\n  end\n\n  describe Puppet::Util::UnixRunMode, :unless => Puppet::Util::Platform.windows? do\n    before do\n      @run_mode = Puppet::Util::UnixRunMode.new('fake')\n    end\n\n    describe \"#conf_dir\" do\n      it \"has confdir /etc/puppetlabs/puppet when run as root\" do\n        as_root { expect(@run_mode.conf_dir).to eq(File.expand_path('/etc/puppetlabs/puppet')) }\n      end\n\n      it \"has confdir ~/.puppetlabs/etc/puppet when run as non-root\" do\n        as_non_root { expect(@run_mode.conf_dir).to eq(File.expand_path('~/.puppetlabs/etc/puppet')) }\n      end\n\n      context \"server run mode\" do\n        before do\n          @run_mode = Puppet::Util::UnixRunMode.new('server')\n        end\n\n        it \"has confdir ~/.puppetlabs/etc/puppet when run as non-root and server run mode\" do\n          as_non_root { expect(@run_mode.conf_dir).to eq(File.expand_path('~/.puppetlabs/etc/puppet')) }\n        end\n      end\n    end\n\n    describe \"#code_dir\" do\n      it \"has codedir /etc/puppetlabs/code when run as root\" do\n        as_root { expect(@run_mode.code_dir).to eq(File.expand_path('/etc/puppetlabs/code')) }\n      end\n\n      it \"has codedir ~/.puppetlabs/etc/code when run as non-root\" do\n        as_non_root { expect(@run_mode.code_dir).to eq(File.expand_path('~/.puppetlabs/etc/code')) }\n      end\n\n      context \"server run mode\" do\n        before do\n          @run_mode = Puppet::Util::UnixRunMode.new('server')\n        end\n\n        it \"has codedir ~/.puppetlabs/etc/code when run as non-root and server run mode\" do\n          as_non_root { expect(@run_mode.code_dir).to eq(File.expand_path('~/.puppetlabs/etc/code')) }\n        end\n      end\n    end\n\n    describe \"#var_dir\" do\n      it \"has vardir /opt/puppetlabs/puppet/cache when run as root\" do\n        as_root { expect(@run_mode.var_dir).to eq(File.expand_path('/opt/puppetlabs/puppet/cache')) }\n      end\n\n      it \"has vardir ~/.puppetlabs/opt/puppet/cache when run as non-root\" do\n        as_non_root { expect(@run_mode.var_dir).to eq(File.expand_path('~/.puppetlabs/opt/puppet/cache')) }\n      end\n    end\n\n    describe \"#public_dir\" do\n      it \"has publicdir /opt/puppetlabs/puppet/public when run as root\" do\n        as_root { expect(@run_mode.public_dir).to eq(File.expand_path('/opt/puppetlabs/puppet/public')) }\n      end\n\n      it \"has publicdir ~/.puppetlabs/opt/puppet/public when run as non-root\" do\n        as_non_root { expect(@run_mode.public_dir).to eq(File.expand_path('~/.puppetlabs/opt/puppet/public')) }\n      end\n    end\n\n    describe \"#log_dir\" do\n      describe \"when run as root\" do\n        it \"has logdir /var/log/puppetlabs/puppet\" do\n          as_root { expect(@run_mode.log_dir).to eq(File.expand_path('/var/log/puppetlabs/puppet')) }\n        end\n      end\n\n      describe \"when run as non-root\" do\n        it \"has default logdir ~/.puppetlabs/var/log\" do\n          as_non_root { expect(@run_mode.log_dir).to eq(File.expand_path('~/.puppetlabs/var/log')) }\n        end\n      end\n    end\n\n    describe \"#run_dir\" do\n      describe \"when run as root\" do\n        it \"has rundir /var/run/puppetlabs\" do\n          as_root { expect(@run_mode.run_dir).to eq(File.expand_path('/var/run/puppetlabs')) }\n        end\n      end\n\n      describe \"when run as non-root\" do\n        it \"has default rundir ~/.puppetlabs/var/run\" do\n          as_non_root { expect(@run_mode.run_dir).to eq(File.expand_path('~/.puppetlabs/var/run')) }\n        end\n      end\n    end\n\n    describe \"#pkg_config_path\" do\n      it { expect(@run_mode.pkg_config_path).to eq('/opt/puppetlabs/puppet/lib/pkgconfig') }\n    end\n\n    describe \"#gem_cmd\" do\n      it { expect(@run_mode.gem_cmd).to eq('/opt/puppetlabs/puppet/bin/gem') }\n    end\n\n    describe \"#common_module_dir\" do\n      it { expect(@run_mode.common_module_dir).to eq('/opt/puppetlabs/puppet/modules') }\n    end\n\n    describe \"#vendor_module_dir\" do\n      it { expect(@run_mode.vendor_module_dir).to eq('/opt/puppetlabs/puppet/vendor_modules') }\n    end\n  end\n\n  describe Puppet::Util::WindowsRunMode, :if => Puppet::Util::Platform.windows? do\n    before do\n      @run_mode = Puppet::Util::WindowsRunMode.new('fake')\n    end\n\n    describe \"#conf_dir\" do\n      it \"has confdir ending in Puppetlabs/puppet/etc when run as root\" do\n        as_root { expect(@run_mode.conf_dir).to eq(File.expand_path(File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"etc\"))) }\n      end\n\n      it \"has confdir in ~/.puppetlabs/etc/puppet when run as non-root\" do\n        as_non_root { expect(@run_mode.conf_dir).to eq(File.expand_path(\"~/.puppetlabs/etc/puppet\")) }\n      end\n    end\n\n    describe \"#code_dir\" do\n      it \"has codedir ending in PuppetLabs/code when run as root\" do\n        as_root { expect(@run_mode.code_dir).to eq(File.expand_path(File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"code\"))) }\n      end\n\n      it \"has codedir in ~/.puppetlabs/etc/code when run as non-root\" do\n        as_non_root { expect(@run_mode.code_dir).to eq(File.expand_path(\"~/.puppetlabs/etc/code\")) }\n      end\n    end\n\n    describe \"#var_dir\" do\n      it \"has vardir ending in PuppetLabs/puppet/cache when run as root\" do\n        as_root { expect(@run_mode.var_dir).to eq(File.expand_path(File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"cache\"))) }\n      end\n\n      it \"has vardir in ~/.puppetlabs/opt/puppet/cache when run as non-root\" do\n        as_non_root { expect(@run_mode.var_dir).to eq(File.expand_path(\"~/.puppetlabs/opt/puppet/cache\")) }\n      end\n    end\n\n    describe \"#public_dir\" do\n      it \"has publicdir ending in PuppetLabs/puppet/public when run as root\" do\n        as_root { expect(@run_mode.public_dir).to eq(File.expand_path(File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"public\"))) }\n      end\n\n      it \"has publicdir in ~/.puppetlabs/opt/puppet/public when run as non-root\" do\n        as_non_root { expect(@run_mode.public_dir).to eq(File.expand_path(\"~/.puppetlabs/opt/puppet/public\")) }\n      end\n    end\n\n    describe \"#log_dir\" do\n      describe \"when run as root\" do\n        it \"has logdir ending in PuppetLabs/puppet/var/log\" do\n          as_root { expect(@run_mode.log_dir).to eq(File.expand_path(File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"var\", \"log\"))) }\n        end\n      end\n\n      describe \"when run as non-root\" do\n        it \"has default logdir ~/.puppetlabs/var/log\" do\n          as_non_root { expect(@run_mode.log_dir).to eq(File.expand_path('~/.puppetlabs/var/log')) }\n        end\n      end\n    end\n\n    describe \"#run_dir\" do\n      describe \"when run as root\" do\n        it \"has rundir ending in PuppetLabs/puppet/var/run\" do\n          as_root { expect(@run_mode.run_dir).to eq(File.expand_path(File.join(ENV['ALLUSERSPROFILE'], \"PuppetLabs\", \"puppet\", \"var\", \"run\"))) }\n        end\n      end\n\n      describe \"when run as non-root\" do\n        it \"has default rundir ~/.puppetlabs/var/run\" do\n          as_non_root { expect(@run_mode.run_dir).to eq(File.expand_path('~/.puppetlabs/var/run')) }\n        end\n      end\n    end\n\n    describe '#gem_cmd' do\n      before do\n        allow(ENV).to receive(:fetch).and_call_original\n        allow(ENV).to receive(:fetch).with('PUPPET_DIR', nil).and_return(puppetdir)\n      end\n\n      context 'when PUPPET_DIR is not set' do\n        let(:puppetdir) { nil }\n\n        before do\n          allow(Gem).to receive(:default_bindir).and_return('default_gem_bin')\n        end\n\n        it 'uses Gem.default_bindir' do\n          expected_path = File.join('default_gem_bin', 'gem.bat')\n          expect(@run_mode.gem_cmd).to eql(expected_path)\n        end\n      end\n\n      context 'when PUPPET_DIR is set' do\n        let(:puppetdir) { 'puppet_dir' }\n\n        it 'uses Gem.default_bindir' do\n          expected_path = File.join('puppet_dir', 'bin', 'gem.bat')\n          expect(@run_mode.gem_cmd).to eql(expected_path)\n        end\n      end\n    end\n\n    describe '#common_module_dir' do\n      before do\n        allow(ENV).to receive(:fetch).and_call_original\n        allow(ENV).to receive(:fetch).with('FACTER_env_windows_installdir', nil).and_return(installdir)\n      end\n\n      context 'when installdir is not set' do\n        let(:installdir) { nil }\n\n        it 'returns nil' do\n          expect(@run_mode.common_module_dir).to be(nil)\n        end\n      end\n\n      context 'with installdir' do\n        let(:installdir) { 'C:\\Program Files\\Puppet Labs\\Puppet' }\n\n        it 'returns INSTALLDIR/puppet/modules' do\n          expect(@run_mode.common_module_dir).to eq('C:\\Program Files\\Puppet Labs\\Puppet/puppet/modules')\n        end\n      end\n    end\n\n    describe '#vendor_module_dir' do\n      before do\n        allow(ENV).to receive(:fetch).and_call_original\n        allow(ENV).to receive(:fetch).with('FACTER_env_windows_installdir', nil).and_return(installdir)\n      end\n\n      context 'when installdir is not set' do\n        let(:installdir) { nil }\n\n        it 'returns nil' do\n          expect(@run_mode.vendor_module_dir).to be(nil)\n        end\n      end\n\n      context 'with installdir' do\n        let(:installdir) { 'C:\\Program Files\\Puppet Labs\\Puppet' }\n\n        it 'returns INSTALLDIR\\puppet\\vendor_modules' do\n          expect(@run_mode.vendor_module_dir).to eq('C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\vendor_modules')\n        end\n      end\n    end\n  end\n\n  def as_root\n    allow(Puppet.features).to receive(:root?).and_return(true)\n    yield\n  end\n\n  def as_non_root\n    allow(Puppet.features).to receive(:root?).and_return(false)\n    yield\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/selinux_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'pathname'\nrequire 'puppet/util/selinux'\n\ndescribe Puppet::Util::SELinux do\n  include Puppet::Util::SELinux\n\n  let(:selinux) { double('selinux', is_selinux_enabled: 0) }\n\n  before :each do\n    stub_const('Selinux', selinux)\n  end\n\n  describe \"selinux_support?\" do\n    it \"should return true if this system has SELinux enabled\" do\n      expect(Selinux).to receive(:is_selinux_enabled).and_return(1)\n      expect(selinux_support?).to eq(true)\n    end\n\n    it \"should return false if this system has SELinux disabled\" do\n      expect(Selinux).to receive(:is_selinux_enabled).and_return(0)\n      expect(selinux_support?).to eq(false)\n    end\n\n    it \"should return false if this system lacks SELinux\" do\n      hide_const('Selinux')\n      expect(selinux_support?).to eq(false)\n    end\n\n    it \"should return nil if /proc/mounts does not exist\" do\n      allow(File).to receive(:new).with(\"/proc/mounts\").and_raise(\"No such file or directory - /proc/mounts\")\n      expect(read_mounts).to eq(nil)\n    end\n  end\n\n  describe \"read_mounts\" do\n    before :each do\n      fh = double('fh', :close => nil)\n      allow(File).to receive(:new).and_call_original()\n      allow(File).to receive(:new).with(\"/proc/mounts\").and_return(fh)\n      times_fh_called = 0\n      expect(fh).to receive(:read_nonblock) do\n        times_fh_called += 1\n        raise EOFError if times_fh_called > 1\n\n        \"rootfs / rootfs rw 0 0\\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\\n/dev /dev tmpfs rw,relatime,mode=755 0 0\\n/proc /proc proc rw,relatime 0 0\\n/sys /sys sysfs rw,relatime 0 0\\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\\n\"\n      end.twice()\n    end\n\n    it \"should parse the contents of /proc/mounts\" do\n      result = read_mounts\n      expect(result).to  eq({\n        '/' => 'ext3',\n        '/sys' => 'sysfs',\n        '/mnt/nfs' => 'nfs',\n        '/proc' => 'proc',\n        '/dev' => 'tmpfs' })\n    end\n  end\n\n  describe \"filesystem detection\" do\n    before :each do\n      allow(self).to receive(:read_mounts).and_return({\n        '/'        => 'ext3',\n        '/sys'     => 'sysfs',\n        '/mnt/nfs' => 'nfs',\n        '/mnt/zfs' => 'zfs',\n        '/proc'    => 'proc',\n        '/dev'     => 'tmpfs' })\n    end\n\n    it \"should match a path on / to ext3\" do\n      expect(find_fs('/etc/puppetlabs/puppet/testfile')).to eq(\"ext3\")\n    end\n\n    it \"should match a path on /mnt/nfs to nfs\" do\n      expect(find_fs('/mnt/nfs/testfile/foobar')).to eq(\"nfs\")\n    end\n\n    it \"should return true for a capable filesystem\" do\n      expect(selinux_label_support?('/etc/puppetlabs/puppet/testfile')).to be_truthy\n    end\n\n    it \"should return true if tmpfs\" do\n      expect(selinux_label_support?('/dev/shm/testfile')).to be_truthy\n    end\n\n    it \"should return true if zfs\" do\n      expect(selinux_label_support?('/mnt/zfs/testfile')).to be_truthy\n    end\n\n    it \"should return false for a noncapable filesystem\" do\n      expect(selinux_label_support?('/mnt/nfs/testfile')).to be_falsey\n    end\n\n    it \"(#8714) don't follow symlinks when determining file systems\", :unless => Puppet::Util::Platform.windows? do\n      scratch = Pathname(PuppetSpec::Files.tmpdir('selinux'))\n\n      allow(self).to receive(:read_mounts).and_return({\n        '/'             => 'ext3',\n        scratch + 'nfs' => 'nfs',\n      })\n\n      (scratch + 'foo').make_symlink('nfs/bar')\n      expect(selinux_label_support?(scratch + 'foo')).to be_truthy\n    end\n\n    it \"should handle files that don't exist\" do\n      scratch = Pathname(PuppetSpec::Files.tmpdir('selinux'))\n      expect(selinux_label_support?(scratch + 'nonesuch')).to be_truthy\n    end\n  end\n\n  describe \"get_selinux_current_context\" do\n    it \"should return nil if no SELinux support\" do\n      expect(self).to receive(:selinux_support?).and_return(false)\n      expect(get_selinux_current_context(\"/foo\")).to be_nil\n    end\n\n    it \"should return a context\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lgetfilecon).with(\"/foo\").and_return([0, \"user_u:role_r:type_t:s0\"])\n        expect(get_selinux_current_context(\"/foo\")).to eq(\"user_u:role_r:type_t:s0\")\n      end\n    end\n\n    it \"should return nil if lgetfilecon fails\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lgetfilecon).with(\"/foo\").and_return(-1)\n        expect(get_selinux_current_context(\"/foo\")).to be_nil\n      end\n    end\n  end\n\n  describe \"get_selinux_default_context\" do\n    it \"should return nil if no SELinux support\" do\n      expect(self).to receive(:selinux_support?).and_return(false)\n      expect(get_selinux_default_context(\"/foo\")).to be_nil\n    end\n\n    it \"should return a context if a default context exists\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        fstat = double('File::Stat', :mode => 0)\n        expect(Puppet::FileSystem).to receive(:lstat).with('/foo').and_return(fstat)\n        expect(self).to receive(:find_fs).with(\"/foo\").and_return(\"ext3\")\n        expect(Selinux).to receive(:matchpathcon).with(\"/foo\", 0).and_return([0, \"user_u:role_r:type_t:s0\"])\n\n        expect(get_selinux_default_context(\"/foo\")).to eq(\"user_u:role_r:type_t:s0\")\n      end\n    end\n\n    it \"handles permission denied errors by issuing a warning\" do\n      without_partial_double_verification do\n        allow(self).to receive(:selinux_support?).and_return(true)\n        allow(self).to receive(:selinux_label_support?).and_return(true)\n        allow(Selinux).to receive(:matchpathcon).with(\"/root/chuj\", 0).and_return(-1)\n        allow(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::EACCES, \"/root/chuj\")\n\n        expect(get_selinux_default_context(\"/root/chuj\")).to be_nil\n      end\n    end\n\n    it \"backward compatibly handles no such file or directory errors by issuing a warning when resource_ensure not set\" do\n      without_partial_double_verification do\n        allow(self).to receive(:selinux_support?).and_return(true)\n        allow(self).to receive(:selinux_label_support?).and_return(true)\n        allow(Selinux).to receive(:matchpathcon).with(\"/root/chuj\", 0).and_return(-1)\n        allow(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context(\"/root/chuj\")).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to file\" do\n      without_partial_double_verification do\n        allow(self).to receive(:selinux_support?).and_return(true)\n        allow(self).to receive(:selinux_label_support?).and_return(true)\n        allow(Selinux).to receive(:matchpathcon).with(\"/root/chuj\", 32768).and_return(-1)\n        allow(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context(\"/root/chuj\", :present)).to be_nil\n        expect(get_selinux_default_context(\"/root/chuj\", :file)).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to dir\" do\n      without_partial_double_verification do\n        allow(self).to receive(:selinux_support?).and_return(true)\n        allow(self).to receive(:selinux_label_support?).and_return(true)\n        allow(Selinux).to receive(:matchpathcon).with(\"/root/chuj\", 16384).and_return(-1)\n        allow(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context(\"/root/chuj\", :directory)).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to link\" do\n      without_partial_double_verification do\n        allow(self).to receive(:selinux_support?).and_return(true)\n        allow(self).to receive(:selinux_label_support?).and_return(true)\n        allow(Selinux).to receive(:matchpathcon).with(\"/root/chuj\", 40960).and_return(-1)\n        allow(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context(\"/root/chuj\", :link)).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to unknown\" do\n      without_partial_double_verification do\n        allow(self).to receive(:selinux_support?).and_return(true)\n        allow(self).to receive(:selinux_label_support?).and_return(true)\n        allow(Selinux).to receive(:matchpathcon).with(\"/root/chuj\", 0).and_return(-1)\n        allow(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context(\"/root/chuj\", \"unknown\")).to be_nil\n      end\n    end\n\n    it \"should return nil if matchpathcon returns failure\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        fstat = double('File::Stat', :mode => 0)\n        expect(Puppet::FileSystem).to receive(:lstat).with('/foo').and_return(fstat)\n        expect(self).to receive(:find_fs).with(\"/foo\").and_return(\"ext3\")\n        expect(Selinux).to receive(:matchpathcon).with(\"/foo\", 0).and_return(-1)\n\n        expect(get_selinux_default_context(\"/foo\")).to be_nil\n      end\n    end\n\n    it \"should return nil if selinux_label_support returns false\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:find_fs).with(\"/foo\").and_return(\"nfs\")\n        expect(get_selinux_default_context(\"/foo\")).to be_nil\n      end\n    end\n  end\n\n  describe \"get_selinux_default_context_with_handle\" do\n    it \"should return a context if a default context exists\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:find_fs).with(\"/foo\").and_return(\"ext3\")\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd, '/foo', 0).and_return([0, \"user_u:role_r:type_t:s0\"])\n        expect(get_selinux_default_context_with_handle(\"/foo\", hnd)).to eq(\"user_u:role_r:type_t:s0\")\n      end\n    end\n\n    it \"should return nil when permission denied errors are encountered\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:selinux_label_support?).and_return(true)\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd, \"/root/chuj\", 0).and_return(-1)\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::EACCES, \"/root/chuj\")\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd)).to be_nil\n      end\n    end\n\n    it \"should return nil when no such file or directory errors are encountered and resource_ensure is unset\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:selinux_label_support?).and_return(true)\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd, \"/root/chuj\", 0).and_return(-1)\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd)).to be_nil\n      end\n    end\n\n    it \"should pass through lstat mode when file exists\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true).twice\n        expect(self).to receive(:selinux_label_support?).and_return(true).twice\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        fstat = double(\"File::Stat\", :mode => 16384)\n        expect(Selinux).to receive(:selabel_lookup).with(hnd,  \"/root/chuj\", fstat.mode).and_return([0, \"user_u:role_r:type_t:s0\"]).twice\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_return(fstat).twice\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd)).to eq(\"user_u:role_r:type_t:s0\")\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd, :file)).to eq(\"user_u:role_r:type_t:s0\")\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to file\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true).twice\n        expect(self).to receive(:selinux_label_support?).and_return(true).twice\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd,  \"/root/chuj\", 32768).and_return(-1).twice\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\").twice\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd, :present)).to be_nil\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd, :file)).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to dir\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:selinux_label_support?).and_return(true)\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd, \"/root/chuj\", 16384).and_return(-1)\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd, :directory)).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to link\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:selinux_label_support?).and_return(true)\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd, \"/root/chuj\", 40960).and_return(-1)\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd, :link)).to be_nil\n      end\n    end\n\n    it \"should determine mode based on resource ensure when set to unknown\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(self).to receive(:selinux_label_support?).and_return(true)\n        hnd = double(\"SWIG::TYPE_p_selabel_handle\")\n        expect(Selinux).to receive(:selabel_lookup).with(hnd, \"/root/chuj\", 0).and_return(-1)\n        expect(self).to receive(:file_lstat).with(\"/root/chuj\").and_raise(Errno::ENOENT, \"/root/chuj\")\n\n        expect(get_selinux_default_context_with_handle(\"/root/chuj\", hnd, \"unknown\")).to be_nil\n      end\n    end\n\n    it \"should raise an ArgumentError when handle is nil\" do\n      allow(self).to receive(:selinux_support?).and_return(true)\n      allow(self).to receive(:selinux_label_support?).and_return(true)\n      expect{get_selinux_default_context_with_handle(\"/foo\", nil)}.to raise_error(ArgumentError, /Cannot get default context with nil handle/)\n    end\n\n    it \"should return nil if there is no SELinux support\" do\n      expect(self).to receive(:selinux_support?).and_return(false)\n      expect(get_selinux_default_context_with_handle(\"/foo\", nil)).to be_nil\n    end\n\n    it \"should return nil if selinux_label_support returns false\" do\n      expect(self).to receive(:selinux_support?).and_return(true)\n      expect(self).to receive(:find_fs).with(\"/foo\").and_return(\"nfs\")\n      expect(get_selinux_default_context_with_handle(\"/foo\", nil)).to be_nil\n    end\n  end\n\n  describe \"parse_selinux_context\" do\n    it \"should return nil if no context is passed\" do\n      expect(parse_selinux_context(:seluser, nil)).to be_nil\n    end\n\n    it \"should return nil if the context is 'unlabeled'\" do\n      expect(parse_selinux_context(:seluser, \"unlabeled\")).to be_nil\n    end\n\n    it \"should return the user type when called with :seluser\" do\n      expect(parse_selinux_context(:seluser, \"user_u:role_r:type_t:s0\")).to eq(\"user_u\")\n      expect(parse_selinux_context(:seluser, \"user-withdash_u:role_r:type_t:s0\")).to eq(\"user-withdash_u\")\n    end\n\n    it \"should return the role type when called with :selrole\" do\n      expect(parse_selinux_context(:selrole, \"user_u:role_r:type_t:s0\")).to eq(\"role_r\")\n      expect(parse_selinux_context(:selrole, \"user_u:role-withdash_r:type_t:s0\")).to eq(\"role-withdash_r\")\n    end\n\n    it \"should return the type type when called with :seltype\" do\n      expect(parse_selinux_context(:seltype, \"user_u:role_r:type_t:s0\")).to eq(\"type_t\")\n      expect(parse_selinux_context(:seltype, \"user_u:role_r:type-withdash_t:s0\")).to eq(\"type-withdash_t\")\n    end\n\n    describe \"with spaces in the components\" do\n      it \"should raise when user contains a space\" do\n        expect{parse_selinux_context(:seluser, \"user with space_u:role_r:type_t:s0\")}.to raise_error Puppet::Error\n      end\n\n      it \"should raise when role contains a space\" do\n        expect{parse_selinux_context(:selrole, \"user_u:role with space_r:type_t:s0\")}.to raise_error Puppet::Error\n      end\n\n      it \"should raise when type contains a space\" do\n        expect{parse_selinux_context(:seltype, \"user_u:role_r:type with space_t:s0\")}.to raise_error Puppet::Error\n      end\n\n      it \"should return the range when range contains a space\" do\n        expect(parse_selinux_context(:selrange, \"user_u:role_r:type_t:s0 s1\")).to eq(\"s0 s1\")\n      end\n    end\n\n    it \"should return nil for :selrange when no range is returned\" do\n      expect(parse_selinux_context(:selrange, \"user_u:role_r:type_t\")).to be_nil\n    end\n\n    it \"should return the range type when called with :selrange\" do\n      expect(parse_selinux_context(:selrange, \"user_u:role_r:type_t:s0\")).to eq(\"s0\")\n      expect(parse_selinux_context(:selrange, \"user_u:role_r:type-withdash_t:s0\")).to eq(\"s0\")\n    end\n\n    describe \"with a variety of SELinux range formats\" do\n      ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range|\n        it \"should parse range '#{range}'\" do\n          expect(parse_selinux_context(:selrange, \"user_u:role_r:type_t:#{range}\")).to eq(range)\n        end\n      end\n    end\n  end\n\n  describe \"set_selinux_context\" do\n    before :each do\n      fh = double('fh', :close => nil)\n      allow(File).to receive(:new).with(\"/proc/mounts\").and_return(fh)\n      times_fh_called = 0\n      allow(fh).to receive(:read_nonblock) do\n        times_fh_called += 1\n        raise EOFError if times_fh_called > 1\n\n        \"rootfs / rootfs rw 0 0\\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\\n\"+\n        \"/dev /dev tmpfs rw,relatime,mode=755 0 0\\n/proc /proc proc rw,relatime 0 0\\n\"+\n        \"/sys /sys sysfs rw,relatime 0 0\\n\"\n      end\n    end\n\n    it \"should return nil if there is no SELinux support\" do\n      expect(self).to receive(:selinux_support?).and_return(false)\n      expect(set_selinux_context(\"/foo\", \"user_u:role_r:type_t:s0\")).to be_nil\n    end\n\n    it \"should return nil if selinux_label_support returns false\" do\n      expect(self).to receive(:selinux_support?).and_return(true)\n      expect(self).to receive(:selinux_label_support?).with(\"/foo\").and_return(false)\n      expect(set_selinux_context(\"/foo\", \"user_u:role_r:type_t:s0\")).to be_nil\n    end\n\n    it \"should use lsetfilecon to set a context\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lsetfilecon).with(\"/foo\", \"user_u:role_r:type_t:s0\").and_return(0)\n        expect(set_selinux_context(\"/foo\", \"user_u:role_r:type_t:s0\")).to be_truthy\n      end\n    end\n\n    it \"should use lsetfilecon to set user_u user context\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lgetfilecon).with(\"/foo\").and_return([0, \"foo:role_r:type_t:s0\"])\n        expect(Selinux).to receive(:lsetfilecon).with(\"/foo\", \"user_u:role_r:type_t:s0\").and_return(0)\n        expect(set_selinux_context(\"/foo\", \"user_u\", :seluser)).to be_truthy\n      end\n    end\n\n    it \"should use lsetfilecon to set role_r role context\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lgetfilecon).with(\"/foo\").and_return([0, \"user_u:foo:type_t:s0\"])\n        expect(Selinux).to receive(:lsetfilecon).with(\"/foo\", \"user_u:role_r:type_t:s0\").and_return(0)\n        expect(set_selinux_context(\"/foo\", \"role_r\", :selrole)).to be_truthy\n      end\n    end\n\n    it \"should use lsetfilecon to set type_t type context\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lgetfilecon).with(\"/foo\").and_return([0, \"user_u:role_r:foo:s0\"])\n        expect(Selinux).to receive(:lsetfilecon).with(\"/foo\", \"user_u:role_r:type_t:s0\").and_return(0)\n        expect(set_selinux_context(\"/foo\", \"type_t\", :seltype)).to be_truthy\n      end\n    end\n\n    it \"should use lsetfilecon to set s0:c3,c5 range context\" do\n      without_partial_double_verification do\n        expect(self).to receive(:selinux_support?).and_return(true)\n        expect(Selinux).to receive(:lgetfilecon).with(\"/foo\").and_return([0, \"user_u:role_r:type_t:s0\"])\n        expect(Selinux).to receive(:lsetfilecon).with(\"/foo\", \"user_u:role_r:type_t:s0:c3,c5\").and_return(0)\n        expect(set_selinux_context(\"/foo\", \"s0:c3,c5\", :selrange)).to be_truthy\n      end\n    end\n  end\n\n  describe \"set_selinux_default_context\" do\n    it \"should return nil if there is no SELinux support\" do\n      expect(self).to receive(:selinux_support?).and_return(false)\n      expect(set_selinux_default_context(\"/foo\")).to be_nil\n    end\n\n    it \"should return nil if no default context exists\" do\n      expect(self).to receive(:get_selinux_default_context).with(\"/foo\", nil).and_return(nil)\n      expect(set_selinux_default_context(\"/foo\")).to be_nil\n    end\n\n    it \"should do nothing and return nil if the current context matches the default context\" do\n      expect(self).to receive(:get_selinux_default_context).with(\"/foo\", nil).and_return(\"user_u:role_r:type_t\")\n      expect(self).to receive(:get_selinux_current_context).with(\"/foo\").and_return(\"user_u:role_r:type_t\")\n      expect(set_selinux_default_context(\"/foo\")).to be_nil\n    end\n\n    it \"should set and return the default context if current and default do not match\" do\n      expect(self).to receive(:get_selinux_default_context).with(\"/foo\", nil).and_return(\"user_u:role_r:type_t\")\n      expect(self).to receive(:get_selinux_current_context).with(\"/foo\").and_return(\"olduser_u:role_r:type_t\")\n      expect(self).to receive(:set_selinux_context).with(\"/foo\", \"user_u:role_r:type_t\").and_return(true)\n      expect(set_selinux_default_context(\"/foo\")).to eq(\"user_u:role_r:type_t\")\n    end\n  end\n\n  describe \"get_create_mode\" do\n    it \"should return 0 if the resource is absent\" do\n      expect(get_create_mode(:absent)).to eq(0)\n    end\n\n    it \"should return mode with file type set to S_IFREG when resource is file\" do\n      expect(get_create_mode(:present)).to eq(32768)\n      expect(get_create_mode(:file)).to eq(32768)\n    end\n\n    it \"should return mode with file type set to S_IFDIR when resource is dir\" do\n      expect(get_create_mode(:directory)).to eq(16384)\n    end\n\n    it \"should return mode with file type set to S_IFLNK when resource is link\" do\n      expect(get_create_mode(:link)).to eq(40960)\n    end\n\n    it \"should return 0 for everything else\" do\n      expect(get_create_mode(\"unknown\")).to eq(0)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/skip_tags_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\n\nrequire 'puppet/util/skip_tags'\n\ndescribe Puppet::Util::SkipTags do\n  let(:tagger) { Puppet::Util::SkipTags.new([]) }\n\n  it \"should add qualified classes as single tags\" do\n    tagger.tag(\"one::two::three\")\n    expect(tagger.tags).to include(\"one::two::three\")\n    expect(tagger.tags).not_to include(\"one\", \"two\", \"three\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/splayer_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/splayer'\n\ndescribe Puppet::Util::Splayer do\n  include Puppet::Util::Splayer\n\n  let (:subject) { self }\n\n  before do\n    Puppet[:splay] = true\n    Puppet[:splaylimit] = \"10\"\n  end\n\n  it \"should do nothing if splay is disabled\" do\n    Puppet[:splay] = false\n    expect(subject).not_to receive(:sleep)\n    subject.splay\n  end\n\n  it \"should do nothing if it has already splayed\" do\n    expect(subject).to receive(:splayed?).and_return(true)\n    expect(subject).not_to receive(:sleep)\n    subject.splay\n  end\n\n  it \"should log that it is splaying\" do\n    allow(subject).to receive(:sleep)\n    expect(Puppet).to receive(:info)\n    subject.splay\n  end\n\n  it \"should sleep for a random portion of the splaylimit plus 1\" do\n    Puppet[:splaylimit] = \"50\"\n    expect(subject).to receive(:rand).with(51).and_return(10)\n    expect(subject).to receive(:sleep).with(10)\n    subject.splay\n  end\n\n  it \"should mark that it has splayed\" do\n    allow(subject).to receive(:sleep)\n    subject.splay\n    expect(subject).to be_splayed\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/storage_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'yaml'\nrequire 'fileutils'\nrequire 'puppet/util/storage'\n\ndescribe Puppet::Util::Storage do\n  include PuppetSpec::Files\n\n  before(:each) do\n    @basepath = File.expand_path(\"/somepath\")\n  end\n\n  describe \"when caching a symbol\" do\n    it \"should return an empty hash\" do\n      expect(Puppet::Util::Storage.cache(:yayness)).to eq({})\n      expect(Puppet::Util::Storage.cache(:more_yayness)).to eq({})\n    end\n\n    it \"should add the symbol to its internal state\" do\n      Puppet::Util::Storage.cache(:yayness)\n      expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n    end\n\n    it \"should not clobber existing state when caching additional objects\" do\n      Puppet::Util::Storage.cache(:yayness)\n      expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n      Puppet::Util::Storage.cache(:bubblyness)\n      expect(Puppet::Util::Storage.state).to eq({:yayness=>{},:bubblyness=>{}})\n    end\n  end\n\n  describe \"when caching a Puppet::Type\" do\n    before(:each) do\n      @file_test = Puppet::Type.type(:file).new(:name => @basepath+\"/yayness\", :audit => %w{checksum type})\n      @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+\"/bin/ls /yayness\")\n    end\n\n    it \"should return an empty hash\" do\n      expect(Puppet::Util::Storage.cache(@file_test)).to eq({})\n      expect(Puppet::Util::Storage.cache(@exec_test)).to eq({})\n    end\n\n    it \"should add the resource ref to its internal state\" do\n      expect(Puppet::Util::Storage.state).to eq({})\n      Puppet::Util::Storage.cache(@file_test)\n      expect(Puppet::Util::Storage.state).to eq({\"File[#{@basepath}/yayness]\"=>{}})\n      Puppet::Util::Storage.cache(@exec_test)\n      expect(Puppet::Util::Storage.state).to eq({\"File[#{@basepath}/yayness]\"=>{}, \"Exec[#{@basepath}/bin/ls /yayness]\"=>{}})\n    end\n  end\n\n  describe \"when caching something other than a resource or symbol\" do\n    it \"should cache by converting to a string\" do\n      data = Puppet::Util::Storage.cache(42)\n      data[:yay] = true\n      expect(Puppet::Util::Storage.cache(\"42\")[:yay]).to be_truthy\n    end\n  end\n\n  it \"should clear its internal state when clear() is called\" do\n    Puppet::Util::Storage.cache(:yayness)\n    expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n    Puppet::Util::Storage.clear\n    expect(Puppet::Util::Storage.state).to eq({})\n  end\n\n  describe \"when loading from the state file\" do\n    before do\n      allow(Puppet.settings).to receive(:use).and_return(true)\n    end\n\n    describe \"when the state file/directory does not exist\" do\n      before(:each) do\n        @path = tmpfile('storage_test')\n      end\n\n      it \"should not fail to load\" do\n        expect(Puppet::FileSystem.exist?(@path)).to be_falsey\n        Puppet[:statedir] = @path\n        Puppet::Util::Storage.load\n        Puppet[:statefile] = @path\n        Puppet::Util::Storage.load\n      end\n\n      it \"should not lose its internal state when load() is called\" do\n        expect(Puppet::FileSystem.exist?(@path)).to be_falsey\n\n        Puppet::Util::Storage.cache(:yayness)\n        expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n\n        Puppet[:statefile] = @path\n        Puppet::Util::Storage.load\n\n        expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n      end\n    end\n\n    describe \"when the state file/directory exists\" do\n      before(:each) do\n        @state_file = tmpfile('storage_test')\n        FileUtils.touch(@state_file)\n        Puppet[:statefile] = @state_file\n      end\n\n      def write_state_file(contents)\n        File.open(@state_file, 'w') { |f| f.write(contents) }\n      end\n\n      it \"should overwrite its internal state if load() is called\" do\n        # Should the state be overwritten even if Puppet[:statefile] is not valid YAML?\n        Puppet::Util::Storage.cache(:yayness)\n        expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n\n        Puppet::Util::Storage.load\n\n        expect(Puppet::Util::Storage.state).to eq({})\n      end\n\n      it \"should restore its internal state if the state file contains valid YAML\" do\n        test_yaml = {'File[\"/yayness\"]'=>{\"name\"=>{:a=>:b,:c=>:d}}}\n        write_state_file(test_yaml.to_yaml)\n\n        Puppet::Util::Storage.load\n\n        expect(Puppet::Util::Storage.state).to eq(test_yaml)\n      end\n\n      it \"should initialize with a clear internal state if the state file does not contain valid YAML\" do\n        write_state_file('{ invalid')\n\n        Puppet::Util::Storage.load\n\n        expect(Puppet::Util::Storage.state).to eq({})\n      end\n\n      it \"should initialize with a clear internal state if the state file does not contain a hash of data\" do\n        write_state_file(\"not_a_hash\")\n\n        Puppet::Util::Storage.load\n\n        expect(Puppet::Util::Storage.state).to eq({})\n      end\n\n      it \"should raise an error if the state file does not contain valid YAML and cannot be renamed\" do\n        allow(File).to receive(:rename).and_call_original\n\n        write_state_file('{ invalid')\n\n        expect(File).to receive(:rename).with(@state_file, \"#{@state_file}.bad\").and_raise(SystemCallError)\n\n        expect { Puppet::Util::Storage.load }.to raise_error(Puppet::Error, /Could not rename/)\n      end\n\n      it \"should attempt to rename the state file if the file is corrupted\" do\n        write_state_file('{ invalid')\n\n        expect(File).to receive(:rename).at_least(:once)\n\n        Puppet::Util::Storage.load\n      end\n\n      it \"should fail gracefully on load() if the state file is not a regular file\" do\n        FileUtils.rm_f(@state_file)\n        Dir.mkdir(@state_file)\n\n        Puppet::Util::Storage.load\n      end\n\n      it 'should load Time and Symbols' do\n        state = {\n          'File[/etc/puppetlabs/puppet]' =>\n          { :checked => Time.new(2018, 8, 8, 15, 28, 25, \"-07:00\") }\n        }\n        write_state_file(YAML.dump(state))\n\n        Puppet::Util::Storage.load\n\n        expect(Puppet::Util::Storage.state).to eq(state.dup)\n      end\n    end\n  end\n\n  describe \"when storing to the state file\" do\n    A_SMALL_AMOUNT_OF_TIME = 0.001 #Seconds\n\n    before(:each) do\n      @state_file = tmpfile('storage_test')\n      @saved_statefile = Puppet[:statefile]\n      Puppet[:statefile] = @state_file\n    end\n\n    it \"should create the state file if it does not exist\" do\n      expect(Puppet::FileSystem.exist?(Puppet[:statefile])).to be_falsey\n      Puppet::Util::Storage.cache(:yayness)\n\n      Puppet::Util::Storage.store\n\n      expect(Puppet::FileSystem.exist?(Puppet[:statefile])).to be_truthy\n    end\n\n    it \"should raise an exception if the state file is not a regular file\" do\n      Dir.mkdir(Puppet[:statefile])\n      Puppet::Util::Storage.cache(:yayness)\n\n      expect { Puppet::Util::Storage.store }.to raise_error(Errno::EISDIR, /Is a directory/)\n\n      Dir.rmdir(Puppet[:statefile])\n    end\n\n    it \"should load() the same information that it store()s\" do\n      Puppet::Util::Storage.cache(:yayness)\n      expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n\n      Puppet::Util::Storage.store\n      Puppet::Util::Storage.clear\n\n      expect(Puppet::Util::Storage.state).to eq({})\n\n      Puppet::Util::Storage.load\n\n      expect(Puppet::Util::Storage.state).to eq({:yayness=>{}})\n    end\n\n    it \"expires entries with a :checked older than statettl seconds ago\" do\n      Puppet[:statettl] = '1d'\n      recent_checked = Time.now.round\n      stale_checked = recent_checked - (Puppet[:statettl] + 10)\n      Puppet::Util::Storage.cache(:yayness)[:checked] = recent_checked\n      Puppet::Util::Storage.cache(:stale)[:checked] = stale_checked\n      expect(Puppet::Util::Storage.state).to match(\n        {\n          :yayness => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)\n          },\n          :stale => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(stale_checked)\n          }\n        }\n      )\n\n      Puppet::Util::Storage.store\n      Puppet::Util::Storage.clear\n\n      expect(Puppet::Util::Storage.state).to eq({})\n\n      Puppet::Util::Storage.load\n\n      expect(Puppet::Util::Storage.state).to match(\n        {\n          :yayness => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)\n          }\n        }\n      )\n    end\n\n    it \"does not expire entries when statettl is 0\" do\n      Puppet[:statettl] = '0'\n      recent_checked = Time.now.round\n      older_checked = recent_checked - 10_000_000\n      Puppet::Util::Storage.cache(:yayness)[:checked] = recent_checked\n      Puppet::Util::Storage.cache(:older)[:checked] = older_checked\n      expect(Puppet::Util::Storage.state).to match(\n        {\n          :yayness => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)\n          },\n          :older => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)\n          }\n        }\n      )\n\n      Puppet::Util::Storage.store\n      Puppet::Util::Storage.clear\n\n      expect(Puppet::Util::Storage.state).to eq({})\n\n      Puppet::Util::Storage.load\n\n      expect(Puppet::Util::Storage.state).to match(\n        {\n          :yayness => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)\n          },\n          :older => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)\n          }\n        }\n      )\n    end\n\n    it \"does not expire entries when statettl is 'unlimited'\" do\n      Puppet[:statettl] = 'unlimited'\n      recent_checked = Time.now\n      older_checked = Time.now - 10_000_000\n      Puppet::Util::Storage.cache(:yayness)[:checked] = recent_checked\n      Puppet::Util::Storage.cache(:older)[:checked] = older_checked\n      expect(Puppet::Util::Storage.state).to match(\n        {\n          :yayness => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)\n          },\n          :older => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)\n          }\n        }\n      )\n\n      Puppet::Util::Storage.store\n      Puppet::Util::Storage.clear\n\n      expect(Puppet::Util::Storage.state).to eq({})\n\n      Puppet::Util::Storage.load\n\n      expect(Puppet::Util::Storage.state).to match(\n        {\n          :yayness => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)\n          },\n          :older => {\n            :checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)\n          }\n        }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/suidmanager_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::SUIDManager do\n  let :user do\n    Puppet::Type.type(:user).new(:name => 'name', :uid => 42, :gid => 42)\n  end\n\n  let :xids do\n    Hash.new {|h,k| 0}\n  end\n\n  before :each do\n    allow(Puppet::Util::SUIDManager).to receive(:convert_xid).and_return(42)\n    pwent = double('pwent', :name => 'fred', :uid => 42, :gid => 42)\n    allow(Etc).to receive(:getpwuid).with(42).and_return(pwent)\n\n    unless Puppet::Util::Platform.windows?\n      [:euid, :egid, :uid, :gid, :groups].each do |id|\n        allow(Process).to receive(\"#{id}=\") {|value| xids[id] = value}\n      end\n    end\n  end\n\n  describe \"#initgroups\", unless: Puppet::Util::Platform.windows? do\n    it \"should use the primary group of the user as the 'basegid'\" do\n      expect(Process).to receive(:initgroups).with('fred', 42)\n      described_class.initgroups(42)\n    end\n  end\n\n  describe \"#uid\" do\n    it \"should allow setting euid/egid\", unless: Puppet::Util::Platform.windows? do\n      Puppet::Util::SUIDManager.egid = user[:gid]\n      Puppet::Util::SUIDManager.euid = user[:uid]\n\n      expect(xids[:egid]).to eq(user[:gid])\n      expect(xids[:euid]).to eq(user[:uid])\n    end\n  end\n\n  describe \"#asuser\" do\n    it \"should not get or set euid/egid when not root\", unless: Puppet::Util::Platform.windows? do\n      allow(Process).to receive(:uid).and_return(1)\n\n      allow(Process).to receive(:egid).and_return(51)\n      allow(Process).to receive(:euid).and_return(50)\n\n      Puppet::Util::SUIDManager.asuser(user[:uid], user[:gid]) {}\n\n      expect(xids).to be_empty\n    end\n\n    context \"when root and not Windows\" do\n      before :each do\n        allow(Process).to receive(:uid).and_return(0)\n      end\n\n      it \"should set euid/egid\", unless: Puppet::Util::Platform.windows? do\n        allow(Process).to receive(:egid).and_return(51, 51, user[:gid])\n        allow(Process).to receive(:euid).and_return(50, 50, user[:uid])\n\n        allow(Puppet::Util::SUIDManager).to receive(:convert_xid).with(:gid, 51).and_return(51)\n        allow(Puppet::Util::SUIDManager).to receive(:convert_xid).with(:uid, 50).and_return(50)\n        allow(Puppet::Util::SUIDManager).to receive(:initgroups).and_return([])\n\n        yielded = false\n        Puppet::Util::SUIDManager.asuser(user[:uid], user[:gid]) do\n          expect(xids[:egid]).to eq(user[:gid])\n          expect(xids[:euid]).to eq(user[:uid])\n          yielded = true\n        end\n\n        expect(xids[:egid]).to eq(51)\n        expect(xids[:euid]).to eq(50)\n\n        # It's possible asuser could simply not yield, so the assertions in the\n        # block wouldn't fail. So verify those actually got checked.\n        expect(yielded).to be_truthy\n      end\n\n      it \"should just yield if user and group are nil\" do\n        expect { |b| Puppet::Util::SUIDManager.asuser(nil, nil, &b) }.to yield_control\n        expect(xids).to eq({})\n      end\n\n      it \"should just change group if only group is given\", unless: Puppet::Util::Platform.windows? do\n        expect { |b| Puppet::Util::SUIDManager.asuser(nil, 42, &b) }.to yield_control\n        expect(xids).to eq({ :egid => 42 })\n      end\n\n      it \"should change gid to the primary group of uid by default\", unless: Puppet::Util::Platform.windows? do\n        allow(Process).to receive(:initgroups)\n\n        expect { |b| Puppet::Util::SUIDManager.asuser(42, nil, &b) }.to yield_control\n        expect(xids).to eq({ :euid => 42, :egid => 42 })\n      end\n\n      it \"should change both uid and gid if given\", unless: Puppet::Util::Platform.windows? do\n        # I don't like the sequence, but it is the only way to assert on the\n        # internal behaviour in a reliable fashion, given we need multiple\n        # sequenced calls to the same methods. --daniel 2012-02-05\n        expect(Puppet::Util::SUIDManager).to receive(:change_group).with(43, false).ordered()\n        expect(Puppet::Util::SUIDManager).to receive(:change_user).with(42, false).ordered()\n        expect(Puppet::Util::SUIDManager).to receive(:change_group).with(Puppet::Util::SUIDManager.egid, false).ordered()\n        expect(Puppet::Util::SUIDManager).to receive(:change_user).with(Puppet::Util::SUIDManager.euid, false).ordered()\n\n        expect { |b| Puppet::Util::SUIDManager.asuser(42, 43, &b) }.to yield_control\n      end\n    end\n\n    it \"should just yield on Windows\", if: Puppet::Util::Platform.windows? do\n      expect { |b| Puppet::Util::SUIDManager.asuser(1, 2, &b) }.to yield_control\n    end\n  end\n\n  describe \"#change_group\" do\n    it \"raises on Windows\", if: Puppet::Util::Platform.windows? do\n      expect {\n        Puppet::Util::SUIDManager.change_group(42, true)\n      }.to raise_error(NotImplementedError, /change_privilege\\(\\) function is unimplemented/)\n    end\n\n    describe \"when changing permanently\", unless: Puppet::Util::Platform.windows? do\n      it \"should change_privilege\" do\n        expect(Process::GID).to receive(:change_privilege) do |gid|\n          Process.gid = gid\n          Process.egid = gid\n        end\n\n        Puppet::Util::SUIDManager.change_group(42, true)\n\n        expect(xids[:egid]).to eq(42)\n        expect(xids[:gid]).to eq(42)\n      end\n\n      it \"should not change_privilege when gid already matches\" do\n        expect(Process::GID).to receive(:change_privilege) do |gid|\n          Process.gid = 42\n          Process.egid = 42\n        end\n\n        Puppet::Util::SUIDManager.change_group(42, true)\n\n        expect(xids[:egid]).to eq(42)\n        expect(xids[:gid]).to eq(42)\n      end\n    end\n\n    describe \"when changing temporarily\", unless: Puppet::Util::Platform.windows? do\n      it \"should change only egid\" do\n        Puppet::Util::SUIDManager.change_group(42, false)\n\n        expect(xids[:egid]).to eq(42)\n        expect(xids[:gid]).to eq(0)\n      end\n    end\n  end\n\n  describe \"#change_user\" do\n    it \"raises on Windows\", if: Puppet::Util::Platform.windows? do\n      expect {\n        Puppet::Util::SUIDManager.change_user(42, true)\n      }.to raise_error(NotImplementedError, /initgroups\\(\\) function is unimplemented/)\n    end\n\n    describe \"when changing permanently\", unless: Puppet::Util::Platform.windows? do\n      it \"should change_privilege\" do\n        expect(Process::UID).to receive(:change_privilege) do |uid|\n          Process.uid = uid\n          Process.euid = uid\n        end\n\n        expect(Puppet::Util::SUIDManager).to receive(:initgroups).with(42)\n\n        Puppet::Util::SUIDManager.change_user(42, true)\n\n        expect(xids[:euid]).to eq(42)\n        expect(xids[:uid]).to eq(42)\n      end\n\n      it \"should not change_privilege when uid already matches\" do\n        expect(Process::UID).to receive(:change_privilege) do |uid|\n          Process.uid = 42\n          Process.euid = 42\n        end\n\n        expect(Puppet::Util::SUIDManager).to receive(:initgroups).with(42)\n\n        Puppet::Util::SUIDManager.change_user(42, true)\n\n        expect(xids[:euid]).to eq(42)\n        expect(xids[:uid]).to eq(42)\n      end\n    end\n\n    describe \"when changing temporarily\", unless: Puppet::Util::Platform.windows? do\n      it \"should change only euid and groups\" do\n        allow(Puppet::Util::SUIDManager).to receive(:initgroups).and_return([])\n        Puppet::Util::SUIDManager.change_user(42, false)\n\n        expect(xids[:euid]).to eq(42)\n        expect(xids[:uid]).to eq(0)\n      end\n\n      it \"should set euid before groups if changing to root\" do\n        allow(Process).to receive(:euid).and_return(50)\n\n        expect(Process).to receive(:euid=).ordered()\n        expect(Puppet::Util::SUIDManager).to receive(:initgroups).ordered()\n\n        Puppet::Util::SUIDManager.change_user(0, false)\n      end\n\n      it \"should set groups before euid if changing from root\" do\n        allow(Process).to receive(:euid).and_return(0)\n\n        expect(Puppet::Util::SUIDManager).to receive(:initgroups).ordered()\n        expect(Process).to receive(:euid=).ordered()\n\n        Puppet::Util::SUIDManager.change_user(50, false)\n      end\n    end\n  end\n\n  describe \"#root?\" do\n    describe \"on POSIX systems\", unless: Puppet::Util::Platform.windows? do\n      it \"should be root if uid is 0\" do\n        allow(Process).to receive(:uid).and_return(0)\n\n        expect(Puppet::Util::SUIDManager).to be_root\n      end\n\n      it \"should not be root if uid is not 0\" do\n        allow(Process).to receive(:uid).and_return(1)\n\n        expect(Puppet::Util::SUIDManager).not_to be_root\n      end\n    end\n\n    describe \"on Windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should be root if user is privileged\" do\n        allow(Puppet::Util::Windows::User).to receive(:admin?).and_return(true)\n\n        expect(Puppet::Util::SUIDManager).to be_root\n      end\n\n      it \"should not be root if user is not privileged\" do\n        allow(Puppet::Util::Windows::User).to receive(:admin?).and_return(false)\n\n        expect(Puppet::Util::SUIDManager).not_to be_root\n      end\n    end\n  end\nend\n\ndescribe 'Puppet::Util::SUIDManager#groups=' do\n  subject do\n    Puppet::Util::SUIDManager\n  end\n\n  it \"raises on Windows\", if: Puppet::Util::Platform.windows? do\n    expect {\n      subject.groups = []\n    }.to raise_error(NotImplementedError, /groups=\\(\\) function is unimplemented/)\n  end\n\n  it \"(#3419) should rescue Errno::EINVAL on OS X\", unless: Puppet::Util::Platform.windows? do\n    expect(Process).to receive(:groups=).and_raise(Errno::EINVAL, 'blew up')\n    expect(subject).to receive(:osx_maj_ver).and_return('10.7').twice\n    subject.groups = ['list', 'of', 'groups']\n  end\n\n  it \"(#3419) should fail if an Errno::EINVAL is raised NOT on OS X\", unless: Puppet::Util::Platform.windows? do\n    expect(Process).to receive(:groups=).and_raise(Errno::EINVAL, 'blew up')\n    expect(subject).to receive(:osx_maj_ver).and_return(false)\n    expect { subject.groups = ['list', 'of', 'groups'] }.to raise_error(Errno::EINVAL)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/symbolic_file_mode_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/symbolic_file_mode'\n\ndescribe Puppet::Util::SymbolicFileMode do\n  include Puppet::Util::SymbolicFileMode\n\n  describe \"#valid_symbolic_mode?\" do\n    %w{\n         0  0000  1  1  7  11  77  111  777  11\n         0 00000 01 01 07 011 077 0111 0777 011\n         = - + u= g= o= a= u+ g+ o+ a+ u- g- o- a- ugo= ugoa= ugugug=\n         a=,u=,g= a=,g+\n         =rwx +rwx -rwx\n         644 go-w =rw,+X +X 755 u=rwx,go=rx u=rwx,go=u-w go= g=u-w\n         755 0755\n    }.each do |input|\n      it \"should treat #{input.inspect} as valid\" do\n        expect(valid_symbolic_mode?(input)).to be_truthy\n      end\n    end\n\n    [0000, 0111, 0640, 0755, 0777].each do |input|\n      it \"should treat the int #{input.to_s(8)} as value\" do\n        expect(valid_symbolic_mode?(input)).to be_truthy\n      end\n    end\n\n    %w{\n          -1  -8  8  9  18  19  91  81  000000  11111  77777\n         0-1 0-8 08 09 018 019 091 081 0000000 011111 077777\n         u g o a ug uo ua ag\n    }.each do |input|\n      it \"should treat #{input.inspect} as invalid\" do\n        expect(valid_symbolic_mode?(input)).to be_falsey\n      end\n    end\n  end\n\n  describe \"#normalize_symbolic_mode\" do\n    it \"should turn an int into a string\" do\n      expect(normalize_symbolic_mode(12)).to be_an_instance_of String\n    end\n\n    it \"should not add a leading zero to an int\" do\n      expect(normalize_symbolic_mode(12)).not_to match(/^0/)\n    end\n\n    it \"should not add a leading zero to a string with a number\" do\n      expect(normalize_symbolic_mode(\"12\")).not_to match(/^0/)\n    end\n\n    it \"should string a leading zero from a number\" do\n      expect(normalize_symbolic_mode(\"012\")).to eq('12')\n    end\n\n    it \"should pass through any other string\" do\n      expect(normalize_symbolic_mode(\"u=rwx\")).to eq('u=rwx')\n    end\n  end\n\n  describe \"#symbolic_mode_to_int\" do\n    {\n      \"0654\"            => 00654,\n      \"u+r\"             => 00400,\n      \"g+r\"             => 00040,\n      \"a+r\"             => 00444,\n      \"a+x\"             => 00111,\n      \"o+t\"             => 01000,\n      [\"o-t\", 07777]    => 06777,\n      [\"a-x\", 07777]    => 07666,\n      [\"a-rwx\", 07777]  => 07000,\n      [\"ug-rwx\", 07777] => 07007,\n      \"a+x,ug-rwx\"      => 00001,\n      # My experimentation on debian suggests that +g ignores the sgid flag\n      [\"a+g\", 02060]    => 02666,\n      # My experimentation on debian suggests that -g ignores the sgid flag\n      [\"a-g\", 02666]    => 02000,\n      \"g+x,a+g\"         => 00111,\n      # +X without exec set in the original should not set anything\n      \"u+x,g+X\"         => 00100,\n      \"g+X\"             => 00000,\n      # +X only refers to the original, *unmodified* file mode!\n      [\"u+x,a+X\", 0600] => 00700,\n      # Examples from the MacOS chmod(1) manpage\n      \"0644\"            => 00644,\n      [\"go-w\", 07777]   => 07755,\n      [\"=rw,+X\", 07777] => 07777,\n      [\"=rw,+X\", 07766] => 07777,\n      [\"=rw,+X\", 07676] => 07777,\n      [\"=rw,+X\", 07667] => 07777,\n      [\"=rw,+X\", 07666] => 07666,\n      \"0755\"            => 00755,\n      \"u=rwx,go=rx\"     => 00755,\n      \"u=rwx,go=u-w\"    => 00755,\n      [\"go=\", 07777]    => 07700,\n      [\"g=u-w\", 07777]  => 07757,\n      [\"g=u-w\", 00700]  => 00750,\n      [\"g=u-w\", 00600]  => 00640,\n      [\"g=u-w\", 00500]  => 00550,\n      [\"g=u-w\", 00400]  => 00440,\n      [\"g=u-w\", 00300]  => 00310,\n      [\"g=u-w\", 00200]  => 00200,\n      [\"g=u-w\", 00100]  => 00110,\n      [\"g=u-w\", 00000]  => 00000,\n      # Cruel, but legal, use of the action set.\n      [\"g=u+r-w\", 0300] => 00350,\n      # Empty assignments.\n      [\"u=\",  00000]    => 00000,\n      [\"u=\",  00600]    => 00000,\n      [\"ug=\", 00000]    => 00000,\n      [\"ug=\", 00600]    => 00000,\n      [\"ug=\", 00660]    => 00000,\n      [\"ug=\", 00666]    => 00006,\n      [\"=\",   00000]    => 00000,\n      [\"=\",   00666]    => 00000,\n      [\"+\",   00000]    => 00000,\n      [\"+\",   00124]    => 00124,\n      [\"-\",   00000]    => 00000,\n      [\"-\",   00124]    => 00124,\n    }.each do |input, result|\n      from = input.is_a?(Array) ? \"#{input[0]}, 0#{input[1].to_s(8)}\" : input\n      it \"should map #{from.inspect} to #{result.inspect}\" do\n        expect(symbolic_mode_to_int(*input)).to eq(result)\n      end\n    end\n\n    # Now, test some failure modes.\n    it \"should fail if no mode is given\" do\n      expect { symbolic_mode_to_int('') }.\n        to raise_error Puppet::Error, /empty mode string/\n    end\n\n    %w{u g o ug uo go ugo a uu u/x u!x u=r,,g=r}.each do |input|\n      it \"should fail if no (valid) action is given: #{input.inspect}\" do\n        expect { symbolic_mode_to_int(input) }.\n          to raise_error Puppet::Error, /Missing action/\n      end\n    end\n\n    %w{u+q u-rwF u+rw,g+rw,o+RW}.each do |input|\n      it \"should fail with unknown op #{input.inspect}\" do\n        expect { symbolic_mode_to_int(input) }.\n          to raise_error Puppet::Error, /Unknown operation/\n      end\n    end\n\n    it \"should refuse to subtract the conditional execute op\" do\n      expect { symbolic_mode_to_int(\"o-rwX\") }.\n        to raise_error Puppet::Error, /only works with/\n    end\n\n    it \"should refuse to set to the conditional execute op\" do\n      expect { symbolic_mode_to_int(\"o=rwX\") }.\n        to raise_error Puppet::Error, /only works with/\n    end\n\n    %w{8 08 9 09 118 119}.each do |input|\n      it \"should fail for decimal modes: #{input.inspect}\" do\n        expect { symbolic_mode_to_int(input) }.\n          to raise_error Puppet::Error, /octal/\n      end\n    end\n\n    it \"should set the execute bit on a directory, without exec in original\" do\n      expect(symbolic_mode_to_int(\"u+X\", 0444, true).to_s(8)).to eq(\"544\")\n      expect(symbolic_mode_to_int(\"g+X\", 0444, true).to_s(8)).to eq(\"454\")\n      expect(symbolic_mode_to_int(\"o+X\", 0444, true).to_s(8)).to eq(\"445\")\n      expect(symbolic_mode_to_int(\"+X\",  0444, true).to_s(8)).to eq(\"555\")\n    end\n\n    it \"should set the execute bit on a file with exec in the original\" do\n      expect(symbolic_mode_to_int(\"+X\", 0544).to_s(8)).to eq(\"555\")\n    end\n\n    it \"should not set the execute bit on a file without exec on the original even if set by earlier DSL\" do\n      expect(symbolic_mode_to_int(\"u+x,go+X\", 0444).to_s(8)).to eq(\"544\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/tag_set_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/tag_set'\n\nRSpec::Matchers.define :be_one_of do |*expected|\n  match do |actual|\n    expected.include? actual\n  end\n\n  failure_message do |actual|\n    \"expected #{actual.inspect} to be one of #{expected.map(&:inspect).join(' or ')}\"\n  end\nend\n\ndescribe Puppet::Util::TagSet do\n  let(:set) { Puppet::Util::TagSet.new }\n\n  it 'serializes to yaml as an array' do\n    array = ['a', :b, 1, 5.4]\n    set.merge(array)\n\n    expect(Set.new(Puppet::Util::Yaml.safe_load(set.to_yaml, [Symbol, Puppet::Util::TagSet]))).to eq(Set.new(array))\n  end\n\n  it 'deserializes from a yaml array' do\n    array = ['a', :b, 1, 5.4]\n\n    expect(Puppet::Util::TagSet.from_yaml(array.to_yaml)).to eq(Puppet::Util::TagSet.new(array))\n  end\n\n  it 'round trips through json' do\n    array = ['a', 'b', 1, 5.4]\n    set.merge(array)\n\n    tes = Puppet::Util::TagSet.from_data_hash(JSON.parse(set.to_json))\n    expect(tes).to eq(set)\n  end\n\n  it 'can join its elements with a string separator' do\n    array = ['a', 'b']\n    set.merge(array)\n\n    expect(set.join(', ')).to be_one_of('a, b', 'b, a')\n  end\n\n  it 'raises when deserializing unacceptable objects' do\n    yaml = [Object.new].to_yaml\n    expect {\n      Puppet::Util::TagSet.from_yaml(yaml)\n    }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /Tried to load unspecified class: Object/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/tagging_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\n\nrequire 'puppet/util/tagging'\n\ndescribe Puppet::Util::Tagging do\n  let(:tagger) { Object.new.extend(Puppet::Util::Tagging) }\n\n  it \"should add tags to the returned tag list\" do\n    tagger.tag(\"one\")\n    expect(tagger.tags).to include(\"one\")\n  end\n\n  it \"should add all provided tags to the tag list\" do\n    tagger.tag(\"one\", \"two\")\n    expect(tagger.tags).to include(\"one\")\n    expect(tagger.tags).to include(\"two\")\n  end\n\n  it \"should fail on tags containing '*' characters\" do\n    expect { tagger.tag(\"bad*tag\") }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should fail on tags starting with '-' characters\" do\n    expect { tagger.tag(\"-badtag\") }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should fail on tags containing ' ' characters\" do\n    expect { tagger.tag(\"bad tag\") }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should fail on tags containing newline characters\" do\n    expect { tagger.tag(\"bad\\ntag\") }.to raise_error(Puppet::ParseError)\n  end\n\n  it \"should allow alpha tags\" do\n    expect { tagger.tag(\"good_tag\") }.not_to raise_error\n  end\n\n  it \"should allow tags containing '.' characters\" do\n    expect { tagger.tag(\"good.tag\") }.to_not raise_error\n  end\n\n  # different UTF-8 widths\n  # 1-byte A\n  # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n  # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n  # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n  let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ܎\n\n  it \"should allow UTF-8 alphanumeric characters\" do\n    expect { tagger.tag(mixed_utf8) }.not_to raise_error\n  end\n\n  # completely non-exhaustive list of a few UTF-8 punctuation characters\n  # http://www.fileformat.info/info/unicode/block/general_punctuation/utf8test.htm\n  [\n    \"\\u2020\", # dagger †\n    \"\\u203B\", # reference mark ※\n    \"\\u204F\", # reverse semicolon ⁏\n    \"!\",\n    \"@\",\n    \"#\",\n    \"$\",\n    \"%\",\n    \"^\",\n    \"&\",\n    \"*\",\n    \"(\",\n    \")\",\n    \"-\",\n    \"+\",\n    \"=\",\n    \"{\",\n    \"}\",\n    \"[\",\n    \"]\",\n    \"|\",\n    \"\\\\\",\n    \"/\",\n    \"?\",\n    \"<\",\n    \">\",\n    \",\",\n    \".\",\n    \"~\",\n    \",\",\n    \":\",\n    \";\",\n    \"\\\"\",\n    \"'\",\n  ].each do |char|\n    it \"should not allow UTF-8 punctuation characters, e.g. #{char}\" do\n      expect { tagger.tag(char) }.to raise_error(Puppet::ParseError)\n    end\n  end\n\n  it \"should allow encodings that can be coerced to UTF-8\" do\n     chinese = \"標記你是它\".force_encoding(Encoding::UTF_8)\n     ascii   = \"tags--\".force_encoding(Encoding::ASCII_8BIT)\n     jose    = \"jos\\xE9\".force_encoding(Encoding::ISO_8859_1)\n\n     [chinese, ascii, jose].each do |tag|\n       expect(tagger.valid_tag?(tag)).to be_truthy\n     end\n  end\n\n  it \"should not allow strings that cannot be converted to UTF-8\" do\n    invalid = \"\\xA0\".force_encoding(Encoding::ASCII_8BIT)\n    expect(tagger.valid_tag?(invalid)).to be_falsey\n  end\n\n  it \"should add qualified classes as tags\" do\n    tagger.tag(\"one::two\")\n    expect(tagger.tags).to include(\"one::two\")\n  end\n\n  it \"should add each part of qualified classes as tags\" do\n    tagger.tag(\"one::two::three\")\n    expect(tagger.tags).to include('one')\n    expect(tagger.tags).to include(\"two\")\n    expect(tagger.tags).to include(\"three\")\n  end\n\n  it \"should indicate when the object is tagged with a provided tag\" do\n    tagger.tag(\"one\")\n    expect(tagger).to be_tagged(\"one\")\n  end\n\n  it \"should indicate when the object is not tagged with a provided tag\" do\n    expect(tagger).to_not be_tagged(\"one\")\n  end\n\n  it \"should indicate when the object is tagged with any tag in an array\" do\n    tagger.tag(\"one\")\n    expect(tagger).to be_tagged(\"one\",\"two\",\"three\")\n  end\n\n  it \"should indicate when the object is not tagged with any tag in an array\" do\n    tagger.tag(\"one\")\n    expect(tagger).to_not be_tagged(\"two\",\"three\")\n  end\n\n  context \"when tagging\" do\n    it \"converts symbols to strings\" do\n      tagger.tag(:hello)\n      expect(tagger.tags).to include('hello')\n    end\n\n    it \"downcases tags\" do\n      tagger.tag(:HEllO)\n      tagger.tag(\"GooDByE\")\n      expect(tagger).to be_tagged(\"hello\")\n      expect(tagger).to be_tagged(\"goodbye\")\n    end\n\n    it \"downcases tag arguments\" do\n      tagger.tag(\"hello\")\n      tagger.tag(\"goodbye\")\n      expect(tagger).to be_tagged(:HEllO)\n      expect(tagger).to be_tagged(\"GooDByE\")\n    end\n\n    it \"accepts hyphenated tags\" do\n      tagger.tag(\"my-tag\")\n      expect(tagger).to be_tagged(\"my-tag\")\n    end\n\n    it 'skips undef /nil tags' do\n      tagger.tag('before')\n      tagger.tag(nil)\n      tagger.tag('after')\n      expect(tagger).to_not be_tagged(nil)\n      expect(tagger).to_not be_tagged('')\n      expect(tagger).to_not be_tagged('undef')\n      expect(tagger).to be_tagged('before')\n      expect(tagger).to be_tagged('after')\n    end\n  end\n\n  context \"when querying if tagged\" do\n    it \"responds true if queried on the entire set\" do\n      tagger.tag(\"one\", \"two\")\n      expect(tagger).to be_tagged(\"one\", \"two\")\n    end\n\n    it \"responds true if queried on a subset\" do\n      tagger.tag(\"one\", \"two\", \"three\")\n      expect(tagger).to be_tagged(\"two\", \"one\")\n    end\n\n    it \"responds true if queried on an overlapping but not fully contained set\" do\n      tagger.tag(\"one\", \"two\")\n      expect(tagger).to be_tagged(\"zero\", \"one\")\n    end\n\n    it \"responds false if queried on a disjoint set\" do\n      tagger.tag(\"one\", \"two\", \"three\")\n      expect(tagger).to_not be_tagged(\"five\")\n    end\n\n    it \"responds false if queried on the empty set\" do\n      expect(tagger).to_not be_tagged\n    end\n  end\n\n  context \"when assigning tags\" do\n    it \"splits a string on ','\" do\n      tagger.tags = \"one, two, three\"\n      expect(tagger).to be_tagged(\"one\")\n      expect(tagger).to be_tagged(\"two\")\n      expect(tagger).to be_tagged(\"three\")\n    end\n\n    it \"protects against empty tags\" do\n      expect { tagger.tags = \"one,,two\"}.to raise_error(/Invalid tag ''/)\n    end\n\n    it \"takes an array of tags\" do\n      tagger.tags = [\"one\", \"two\"]\n\n      expect(tagger).to be_tagged(\"one\")\n      expect(tagger).to be_tagged(\"two\")\n    end\n\n    it \"removes any existing tags when reassigning\" do\n      tagger.tags = \"one, two\"\n\n      tagger.tags = \"three, four\"\n\n      expect(tagger).to_not be_tagged(\"one\")\n      expect(tagger).to_not be_tagged(\"two\")\n      expect(tagger).to be_tagged(\"three\")\n      expect(tagger).to be_tagged(\"four\")\n    end\n\n    it \"allows empty tags that are generated from :: separated tags\" do\n      tagger.tags = \"one::::two::three\"\n\n      expect(tagger).to be_tagged(\"one\")\n      expect(tagger).to be_tagged(\"\")\n      expect(tagger).to be_tagged(\"two\")\n      expect(tagger).to be_tagged(\"three\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/terminal_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/terminal'\n\ndescribe Puppet::Util::Terminal do\n  describe '.width' do\n    before { allow(Puppet.features).to receive(:posix?).and_return(true) }\n\n    it 'should invoke `stty` and return the width' do\n      height, width = 100, 200\n      expect(subject).to receive(:`).with('stty size 2>/dev/null').and_return(\"#{height} #{width}\\n\")\n      expect(subject.width).to eq(width)\n    end\n\n    it 'should use `tput` if `stty` is unavailable' do\n      width = 200\n      expect(subject).to receive(:`).with('stty size 2>/dev/null').and_return(\"\\n\")\n      expect(subject).to receive(:`).with('tput cols 2>/dev/null').and_return(\"#{width}\\n\")\n      expect(subject.width).to eq(width)\n    end\n\n    it 'should default to 80 columns if `tput` and `stty` are unavailable' do\n      width = 80\n      expect(subject).to receive(:`).with('stty size 2>/dev/null').and_return(\"\\n\")\n      expect(subject).to receive(:`).with('tput cols 2>/dev/null').and_return(\"\\n\")\n      expect(subject.width).to eq(width)\n    end\n\n    it 'should default to 80 columns if `tput` or `stty` raise exceptions' do\n      width = 80\n      expect(subject).to receive(:`).with('stty size 2>/dev/null').and_raise()\n      allow(subject).to receive(:`).with('tput cols 2>/dev/null').and_return(\"#{width + 1000}\\n\")\n      expect(subject.width).to eq(width)\n    end\n\n    it 'should default to 80 columns if not in a POSIX environment' do\n      width = 80\n      allow(Puppet.features).to receive(:posix?).and_return(false)\n      expect(subject.width).to eq(width)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/user_attr_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/user_attr'\n\ndescribe UserAttr do\n  before do\n    user_attr = [\"foo::::type=role\", \"bar::::type=normal;profile=foobar\"]\n    allow(File).to receive(:readlines).and_return(user_attr)\n  end\n\n  describe \"when getting attributes by name\" do\n    it \"should return nil if there is no entry for that name\" do\n      expect(UserAttr.get_attributes_by_name('baz')).to eq(nil)\n    end\n\n    it \"should return a hash if there is an entry in /etc/user_attr\" do\n      expect(UserAttr.get_attributes_by_name('foo').class).to eq(Hash)\n    end\n\n    it \"should return a hash with the name value from /etc/user_attr\" do\n      expect(UserAttr.get_attributes_by_name('foo')[:name]).to eq('foo')\n    end\n\n    #this test is contrived\n    #there are a bunch of possible parameters that could be in the hash\n    #the role/normal is just a the convention of the file\n    describe \"when the name is a role\" do\n      it \"should contain :type = role\" do\n        expect(UserAttr.get_attributes_by_name('foo')[:type]).to eq('role')\n      end\n    end\n\n    describe \"when the name is not a role\" do\n      it \"should contain :type = normal\" do\n        expect(UserAttr.get_attributes_by_name('bar')[:type]).to eq('normal')\n      end\n    end\n\n    describe \"when the name has more attributes\" do\n      it \"should contain all the attributes\" do\n        expect(UserAttr.get_attributes_by_name('bar')[:profile]).to eq('foobar')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/warnings_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util::Warnings do\n  before(:all) do\n    @msg1 = \"booness\"\n    @msg2 = \"more booness\"\n  end\n\n  before(:each) do\n    Puppet.debug = true\n  end\n\n  after (:each) do\n    Puppet.debug = false\n  end\n\n  {:notice => \"notice_once\", :warning => \"warnonce\", :debug => \"debug_once\"}.each do |log, method|\n    describe \"when registring '#{log}' messages\" do\n      it \"should always return nil\" do\n        expect(Puppet::Util::Warnings.send(method, @msg1)).to be(nil)\n      end\n\n      it \"should issue a warning\" do\n        expect(Puppet).to receive(log).with(@msg1)\n        Puppet::Util::Warnings.send(method, @msg1)\n      end\n\n      it \"should issue a warning exactly once per unique message\" do\n        expect(Puppet).to receive(log).with(@msg1).once\n        Puppet::Util::Warnings.send(method, @msg1)\n        Puppet::Util::Warnings.send(method, @msg1)\n      end\n\n      it \"should issue multiple warnings for multiple unique messages\" do\n        expect(Puppet).to receive(log).twice()\n        Puppet::Util::Warnings.send(method, @msg1)\n        Puppet::Util::Warnings.send(method, @msg2)\n      end\n    end\n  end\n\n  after(:each) do\n    Puppet::Util::Warnings.clear_warnings\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/watched_file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/watched_file'\nrequire 'puppet/util/watcher'\n\ndescribe Puppet::Util::WatchedFile do\n  let(:an_absurdly_long_timeout) { Puppet::Util::Watcher::Timer.new(100000) }\n  let(:an_immediate_timeout) { Puppet::Util::Watcher::Timer.new(0) }\n\n  it \"acts like a string so that it can be used as a filename\" do\n    watched = Puppet::Util::WatchedFile.new(\"foo\")\n\n    expect(watched.to_str).to eq(\"foo\")\n  end\n\n  it \"considers the file to be unchanged before the timeout expires\" do\n    watched = Puppet::Util::WatchedFile.new(a_file_that_doesnt_exist, an_absurdly_long_timeout)\n\n    expect(watched).to_not be_changed\n  end\n\n  it \"considers a file that is created to be changed\" do\n    watched_filename = a_file_that_doesnt_exist\n    watched = Puppet::Util::WatchedFile.new(watched_filename, an_immediate_timeout)\n\n    create_file(watched_filename)\n\n    expect(watched).to be_changed\n  end\n\n  it \"considers a missing file to remain unchanged\" do\n    watched = Puppet::Util::WatchedFile.new(a_file_that_doesnt_exist, an_immediate_timeout)\n\n    expect(watched).to_not be_changed\n  end\n\n  it \"considers a file that has changed but the timeout is not expired to still be unchanged\" do\n    watched_filename = a_file_that_doesnt_exist\n    watched = Puppet::Util::WatchedFile.new(watched_filename, an_absurdly_long_timeout)\n\n    create_file(watched_filename)\n\n    expect(watched).to_not be_changed\n  end\n\n  def create_file(name)\n    File.open(name, \"wb\") { |file| file.puts(\"contents\") }\n  end\n\n  def a_file_that_doesnt_exist\n    PuppetSpec::Files.tmpfile(\"watched_file\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/watcher/periodic_watcher_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/watcher'\n\ndescribe Puppet::Util::Watcher::PeriodicWatcher do\n  let(:enabled_timeout) { 1 }\n  let(:disabled_timeout) { -1 }\n  let(:a_value) { 15 }\n  let(:a_different_value) { 16 }\n\n  let(:unused_watcher) { double('unused watcher') }\n  let(:unchanged_watcher) { a_watcher_reporting(a_value) }\n  let(:changed_watcher) { a_watcher_reporting(a_value, a_different_value) }\n\n  it 'reads only the initial change state when the timeout has not yet expired' do\n    watcher = Puppet::Util::Watcher::PeriodicWatcher.new(unchanged_watcher, an_unexpired_timer(enabled_timeout))\n\n    expect(watcher).to_not be_changed\n  end\n\n  it 'reads enough values to determine change when the timeout has expired' do\n    watcher = Puppet::Util::Watcher::PeriodicWatcher.new(changed_watcher, an_expired_timer(enabled_timeout))\n\n    expect(watcher).to be_changed\n  end\n\n  it 'is always marked as changed when the timeout is disabled' do\n    watcher = Puppet::Util::Watcher::PeriodicWatcher.new(unused_watcher, an_expired_timer(disabled_timeout))\n\n    expect(watcher).to be_changed\n  end\n\n  def a_watcher_reporting(*observed_values)\n    Puppet::Util::Watcher::ChangeWatcher.watch(proc do\n      observed_values.shift or raise \"No more observed values to report!\"\n    end)\n  end\n\n  def an_expired_timer(timeout)\n    a_time_that_reports_expired_as(true, timeout)\n  end\n\n  def an_unexpired_timer(timeout)\n    a_time_that_reports_expired_as(false, timeout)\n  end\n\n  def a_time_that_reports_expired_as(expired, timeout)\n    timer = Puppet::Util::Watcher::Timer.new(timeout)\n    allow(timer).to receive(:expired?).and_return(expired)\n    timer\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/watcher_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/watcher'\n\ndescribe Puppet::Util::Watcher do\n  describe \"the common file ctime watcher\" do\n    FakeStat = Struct.new(:ctime)\n\n    def ctime(time)\n      FakeStat.new(time)\n    end\n\n    let(:filename) { \"fake\" }\n\n    it \"is initially unchanged\" do\n      expect(Puppet::FileSystem).to receive(:stat).with(filename).and_return(ctime(20)).at_least(:once)\n\n      watcher = Puppet::Util::Watcher::Common.file_ctime_change_watcher(filename)\n\n      expect(watcher).to_not be_changed\n    end\n\n    it \"has not changed if a section of the file path continues to not exist\" do\n      expect(Puppet::FileSystem).to receive(:stat).with(filename).and_raise(Errno::ENOTDIR).at_least(:once)\n\n      watcher = Puppet::Util::Watcher::Common.file_ctime_change_watcher(filename)\n      watcher = watcher.next_reading\n\n      expect(watcher).to_not be_changed\n    end\n\n    it \"has not changed if the file continues to not exist\" do\n      expect(Puppet::FileSystem).to receive(:stat).with(filename).and_raise(Errno::ENOENT).at_least(:once)\n\n      watcher = Puppet::Util::Watcher::Common.file_ctime_change_watcher(filename)\n      watcher = watcher.next_reading\n\n      expect(watcher).to_not be_changed\n    end\n\n    it \"has changed if the file is created\" do\n      times_stat_called = 0\n      expect(Puppet::FileSystem).to receive(:stat).with(filename) do\n        times_stat_called += 1\n        raise Errno::ENOENT if times_stat_called == 1\n        ctime(20)\n      end.at_least(:once)\n\n      watcher = Puppet::Util::Watcher::Common.file_ctime_change_watcher(filename)\n      watcher = watcher.next_reading\n\n      expect(watcher).to be_changed\n    end\n\n    it \"is marked as changed if the file is deleted\" do\n      times_stat_called = 0\n      expect(Puppet::FileSystem).to receive(:stat).with(filename) do\n        times_stat_called += 1\n        raise Errno::ENOENT if times_stat_called > 1\n        ctime(20)\n      end.at_least(:once)\n\n      watcher = Puppet::Util::Watcher::Common.file_ctime_change_watcher(filename)\n      watcher = watcher.next_reading\n\n      expect(watcher).to be_changed\n    end\n\n    it \"is marked as changed if the file modified\" do\n      times_stat_called = 0\n      expect(Puppet::FileSystem).to receive(:stat).with(filename) do\n        times_stat_called += 1\n        if times_stat_called == 1\n          ctime(20)\n        else\n          ctime(21)\n        end\n      end.at_least(:once)\n\n      watcher = Puppet::Util::Watcher::Common.file_ctime_change_watcher(filename)\n      watcher = watcher.next_reading\n\n      expect(watcher).to be_changed\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/access_control_entry_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe \"Puppet::Util::Windows::AccessControlEntry\", :if => Puppet::Util::Platform.windows? do\n  let(:klass) { Puppet::Util::Windows::AccessControlEntry }\n  let(:sid) { 'S-1-5-18' }\n  let(:mask) { Puppet::Util::Windows::File::FILE_ALL_ACCESS }\n\n  it \"creates an access allowed ace\" do\n    ace = klass.new(sid, mask)\n\n    expect(ace.type).to eq(klass::ACCESS_ALLOWED_ACE_TYPE)\n  end\n\n  it \"creates an access denied ace\" do\n    ace = klass.new(sid, mask, 0, klass::ACCESS_DENIED_ACE_TYPE)\n\n    expect(ace.type).to eq(klass::ACCESS_DENIED_ACE_TYPE)\n  end\n\n  it \"creates a non-inherited ace by default\" do\n    ace = klass.new(sid, mask)\n\n    expect(ace).not_to be_inherited\n  end\n\n  it \"creates an inherited ace\" do\n    ace = klass.new(sid, mask, klass::INHERITED_ACE)\n\n    expect(ace).to be_inherited\n  end\n\n  it \"creates a non-inherit-only ace by default\" do\n    ace = klass.new(sid, mask)\n\n    expect(ace).not_to be_inherit_only\n  end\n\n  it \"creates an inherit-only ace\" do\n    ace = klass.new(sid, mask, klass::INHERIT_ONLY_ACE)\n\n    expect(ace).to be_inherit_only\n  end\n\n  context \"when comparing aces\" do\n    let(:ace1) { klass.new(sid, mask, klass::INHERIT_ONLY_ACE, klass::ACCESS_DENIED_ACE_TYPE) }\n    let(:ace2) { klass.new(sid, mask, klass::INHERIT_ONLY_ACE, klass::ACCESS_DENIED_ACE_TYPE) }\n\n    it \"returns true if different objects have the same set of values\" do\n    expect(ace1).to eq(ace2)\n    end\n\n    it \"returns false if different objects have different sets of values\" do\n      ace = klass.new(sid, mask)\n      expect(ace).not_to eq(ace1)\n    end\n\n    it \"returns true when testing if two objects are eql?\" do\n      ace1.eql?(ace2)\n    end\n\n    it \"returns false when comparing object identity\" do\n      expect(ace1).not_to be_equal(ace2)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/access_control_list_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe \"Puppet::Util::Windows::AccessControlList\", :if => Puppet::Util::Platform.windows? do\n  let(:klass) { Puppet::Util::Windows::AccessControlList }\n  let(:system_sid) { 'S-1-5-18' }\n  let(:admins_sid) { 'S-1-5-544' }\n  let(:none_sid)   { 'S-1-0-0' }\n\n  let(:system_ace) do\n    Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1)\n  end\n  let(:admins_ace) do\n    Puppet::Util::Windows::AccessControlEntry.new(admins_sid, 0x2)\n  end\n  let(:none_ace) do\n    Puppet::Util::Windows::AccessControlEntry.new(none_sid, 0x3)\n  end\n\n  it \"constructs an empty list\" do\n    acl = klass.new\n\n    expect(acl.to_a).to be_empty\n  end\n\n  it \"supports copy constructor\" do\n    aces = klass.new([system_ace]).to_a\n\n    expect(aces.to_a).to eq([system_ace])\n  end\n\n  context \"appending\" do\n    it \"appends an allow ace\" do\n      acl = klass.new\n      acl.allow(system_sid, 0x1, 0x2)\n\n      expect(acl.first.type).to eq(klass::ACCESS_ALLOWED_ACE_TYPE)\n    end\n\n    it \"appends a deny ace\" do\n      acl = klass.new\n      acl.deny(system_sid, 0x1, 0x2)\n\n      expect(acl.first.type).to eq(klass::ACCESS_DENIED_ACE_TYPE)\n    end\n\n    it \"always appends, never overwrites an ACE\" do\n      acl = klass.new([system_ace])\n      acl.allow(admins_sid, admins_ace.mask, admins_ace.flags)\n\n      aces = acl.to_a\n      expect(aces.size).to eq(2)\n      expect(aces[0]).to eq(system_ace)\n      expect(aces[1].sid).to eq(admins_sid)\n      expect(aces[1].mask).to eq(admins_ace.mask)\n      expect(aces[1].flags).to eq(admins_ace.flags)\n    end\n  end\n\n  context \"reassigning\" do\n    it \"preserves the mask from the old sid when reassigning to the new sid\" do\n      dacl = klass.new([system_ace])\n\n      dacl.reassign!(system_ace.sid, admins_ace.sid)\n      # we removed system, so ignore prepended ace\n      ace = dacl.to_a[1]\n      expect(ace.sid).to eq(admins_sid)\n      expect(ace.mask).to eq(system_ace.mask)\n    end\n\n    it \"matches multiple sids\" do\n      dacl = klass.new([system_ace, system_ace])\n\n      dacl.reassign!(system_ace.sid, admins_ace.sid)\n      # we removed system, so ignore prepended ace\n      aces = dacl.to_a\n      expect(aces.size).to eq(3)\n      aces.to_a[1,2].each do |ace|\n        expect(ace.sid).to eq(admins_ace.sid)\n      end\n    end\n\n    it \"preserves aces for sids that don't match, in their original order\" do\n      dacl = klass.new([system_ace, admins_ace])\n\n      dacl.reassign!(system_sid, none_sid)\n      aces = dacl.to_a\n      aces[1].sid == admins_ace.sid\n    end\n\n    it \"preserves inherited aces, even if the sids match\" do\n      flags = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE\n      inherited_ace = Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1, flags)\n      dacl = klass.new([inherited_ace, system_ace])\n      dacl.reassign!(system_sid, none_sid)\n      aces = dacl.to_a\n\n      expect(aces[0].sid).to eq(system_sid)\n    end\n\n    it \"prepends an explicit ace for the new sid with the same mask and basic inheritance as the inherited ace\" do\n      expected_flags =\n        Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |\n        Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE |\n        Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE\n\n      flags = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE | expected_flags\n\n      inherited_ace = Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1, flags)\n      dacl = klass.new([inherited_ace])\n      dacl.reassign!(system_sid, none_sid)\n      aces = dacl.to_a\n\n      expect(aces.size).to eq(2)\n      expect(aces[0].sid).to eq(none_sid)\n      expect(aces[0]).not_to be_inherited\n      expect(aces[0].flags).to eq(expected_flags)\n\n      expect(aces[1].sid).to eq(system_sid)\n      expect(aces[1]).to be_inherited\n    end\n\n    it \"makes a copy of the ace prior to modifying it\" do\n      arr = [system_ace]\n\n      acl = klass.new(arr)\n      acl.reassign!(system_sid, none_sid)\n\n      expect(arr[0].sid).to eq(system_sid)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/adsi_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/windows'\n\ndescribe Puppet::Util::Windows::ADSI, :if => Puppet::Util::Platform.windows? do\n  let(:connection) { double('connection') }\n  let(:builtin_localized) { Puppet::Util::Windows::SID.sid_to_name('S-1-5-32') }\n  # SYSTEM is special as English can retrieve it via Windows API\n  # but will return localized names\n  let(:ntauthority_localized) { Puppet::Util::Windows::SID::Principal.lookup_account_name('SYSTEM').domain }\n\n  before(:each) do\n    Puppet::Util::Windows::ADSI.instance_variable_set(:@computer_name, 'testcomputername')\n    allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_return(connection)\n  end\n\n  after(:each) do\n    Puppet::Util::Windows::ADSI.instance_variable_set(:@computer_name, nil)\n  end\n\n  it \"should generate the correct URI for a resource\" do\n    expect(Puppet::Util::Windows::ADSI.uri('test', 'user')).to eq(\"WinNT://./test,user\")\n  end\n\n  it \"should be able to get the name of the computer\" do\n    expect(Puppet::Util::Windows::ADSI.computer_name).to eq('testcomputername')\n  end\n\n  it \"should be able to provide the correct WinNT base URI for the computer\" do\n    expect(Puppet::Util::Windows::ADSI.computer_uri).to eq(\"WinNT://.\")\n  end\n\n  it \"should generate a fully qualified WinNT URI\" do\n    expect(Puppet::Util::Windows::ADSI.computer_uri('testcomputername')).to eq(\"WinNT://testcomputername\")\n  end\n\n  describe \".computer_name\" do\n    it \"should return a non-empty ComputerName string\" do\n      Puppet::Util::Windows::ADSI.instance_variable_set(:@computer_name, nil)\n      expect(Puppet::Util::Windows::ADSI.computer_name).not_to be_empty\n    end\n  end\n\n  describe \".domain_role\" do\n    DOMAIN_ROLES = Puppet::Util::Platform.windows? ? Puppet::Util::Windows::ADSI::DOMAIN_ROLES : {}\n\n    DOMAIN_ROLES.each do |id, role|\n      it \"should be able to return #{role} as the domain role of the computer\" do\n        Puppet::Util::Windows::ADSI.instance_variable_set(:@domain_role, nil)\n        domain_role = [double('WMI', :DomainRole => id)]\n        allow(Puppet::Util::Windows::ADSI).to receive(:execquery).with('select DomainRole from Win32_ComputerSystem').and_return(domain_role)\n        expect(Puppet::Util::Windows::ADSI.domain_role).to eq(role)\n      end\n    end\n  end\n\n  describe \".sid_uri\" do\n    it \"should raise an error when the input is not a SID Principal\" do\n      [Object.new, {}, 1, :symbol, '', nil].each do |input|\n        expect {\n          Puppet::Util::Windows::ADSI.sid_uri(input)\n        }.to raise_error(Puppet::Error, /Must use a valid SID::Principal/)\n      end\n    end\n\n    it \"should return a SID uri for a well-known SID (SYSTEM)\" do\n      sid = Puppet::Util::Windows::SID::Principal.lookup_account_name('SYSTEM')\n      expect(Puppet::Util::Windows::ADSI.sid_uri(sid)).to eq('WinNT://S-1-5-18')\n    end\n  end\n\n  shared_examples 'a local only resource query' do |klass, account_type|\n    before(:each) do\n      allow(Puppet::Util::Windows::ADSI).to receive(:domain_role).and_return(:MEMBER_SERVER)\n    end\n\n    it \"should be able to check for a local resource\" do\n      local_domain = 'testcomputername'\n      principal = double('Principal', :account => resource_name, :domain => local_domain, :account_type => account_type)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(resource_name).and_return(principal)\n      expect(klass.exists?(resource_name)).to eq(true)\n    end\n\n    it \"should be case insensitive when comparing the domain with the computer name\" do\n      local_domain = 'TESTCOMPUTERNAME'\n      principal = double('Principal', :account => resource_name, :domain => local_domain, :account_type => account_type)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(resource_name).and_return(principal)\n      expect(klass.exists?(resource_name)).to eq(true)\n    end\n\n    it \"should return false if no local resource exists\" do\n      principal = double('Principal', :account => resource_name, :domain => 'AD_DOMAIN', :account_type => account_type)\n      allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(resource_name).and_return(principal)\n      expect(klass.exists?(resource_name)).to eq(false)\n    end\n  end\n\n  describe '.get_sids' do\n    it 'returns an array of SIDs given two an array of ADSI children' do\n      child1 = double('child1', name: 'Administrator', sid: 'S-1-5-21-3882680660-671291151-3888264257-500')\n      child2 = double('child2', name: 'Guest', sid: 'S-1-5-21-3882680660-671291151-3888264257-501')\n      allow(Puppet::Util::Windows::SID).to receive(:ads_to_principal).with(child1).and_return('Administrator')\n      allow(Puppet::Util::Windows::SID).to receive(:ads_to_principal).with(child2).and_return('Guest')\n      sids = Puppet::Util::Windows::ADSI::ADSIObject.get_sids([child1, child2])\n      expect(sids).to eq(['Administrator', 'Guest'])\n    end\n\n    it 'returns an array of SIDs given an ADSI child and ads_to_principal returning domain failure' do\n      child = double('child1', name: 'Administrator', sid: 'S-1-5-21-3882680660-671291151-3888264257-500')\n      allow(Puppet::Util::Windows::SID).to receive(:ads_to_principal).with(child).and_raise(Puppet::Util::Windows::Error.new('', Puppet::Util::Windows::SID::ERROR_TRUSTED_DOMAIN_FAILURE))\n      sids = Puppet::Util::Windows::ADSI::ADSIObject.get_sids([child])\n      expect(sids[0]).to eq(Puppet::Util::Windows::SID::Principal.new(child.name, child.sid, child.name, nil, :SidTypeUnknown))\n    end\n\n    it 'returns an array of SIDs given an ADSI child and ads_to_principal returning relationship failure' do\n      child = double('child1', name: 'Administrator', sid: 'S-1-5-21-3882680660-671291151-3888264257-500')\n      allow(Puppet::Util::Windows::SID).to receive(:ads_to_principal).with(child).and_raise(Puppet::Util::Windows::Error.new('', Puppet::Util::Windows::SID::ERROR_TRUSTED_RELATIONSHIP_FAILURE))\n      sids = Puppet::Util::Windows::ADSI::ADSIObject.get_sids([child])\n      expect(sids[0]).to eq(Puppet::Util::Windows::SID::Principal.new(child.name, child.sid, child.name, nil, :SidTypeUnknown))\n    end\n  end\n\n  describe Puppet::Util::Windows::ADSI::User do\n    let(:username)  { 'testuser' }\n    let(:domain)    { 'DOMAIN' }\n    let(:domain_username) { \"#{domain}\\\\#{username}\"}\n\n    it \"should generate the correct URI\" do\n      expect(Puppet::Util::Windows::ADSI::User.uri(username)).to eq(\"WinNT://./#{username},user\")\n    end\n\n    it \"should generate the correct URI for a user with a domain\" do\n      expect(Puppet::Util::Windows::ADSI::User.uri(username, domain)).to eq(\"WinNT://#{domain}/#{username},user\")\n    end\n\n    it \"should generate the correct URI for a BUILTIN user\" do\n      expect(Puppet::Util::Windows::ADSI::User.uri(username, builtin_localized)).to eq(\"WinNT://./#{username},user\")\n    end\n\n    it \"should generate the correct URI for a NT AUTHORITY user\" do\n      expect(Puppet::Util::Windows::ADSI::User.uri(username, ntauthority_localized)).to eq(\"WinNT://./#{username},user\")\n    end\n\n    it \"should be able to parse a username without a domain\" do\n      expect(Puppet::Util::Windows::ADSI::User.parse_name(username)).to eq([username, '.'])\n    end\n\n    it \"should be able to parse a username with a domain\" do\n      expect(Puppet::Util::Windows::ADSI::User.parse_name(domain_username)).to eq([username, domain])\n    end\n\n    it \"should raise an error with a username that contains a /\" do\n      expect {\n        Puppet::Util::Windows::ADSI::User.parse_name(\"#{domain}/#{username}\")\n      }.to raise_error(Puppet::Error, /Value must be in DOMAIN\\\\user style syntax/)\n    end\n\n    it \"should be able to create a user\" do\n      adsi_user = double('adsi')\n\n      expect(connection).to receive(:Create).with('user', username).and_return(adsi_user)\n      expect(Puppet::Util::Windows::ADSI::Group).to receive(:exists?).with(username).and_return(false)\n\n      user = Puppet::Util::Windows::ADSI::User.create(username)\n\n      expect(user).to be_a(Puppet::Util::Windows::ADSI::User)\n      expect(user.native_object).to eq(adsi_user)\n    end\n\n    context \"when domain-joined\" do\n      it_should_behave_like 'a local only resource query', Puppet::Util::Windows::ADSI::User, :SidTypeUser do\n        let(:resource_name) { username }\n      end\n    end\n\n    it \"should be able to check the existence of a user\" do\n      expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(username).and_return(nil)\n      expect(Puppet::Util::Windows::ADSI).to receive(:connect).with(\"WinNT://./#{username},user\").and_return(connection)\n      expect(connection).to receive(:Class).and_return('User')\n      expect(Puppet::Util::Windows::ADSI::User.exists?(username)).to be_truthy\n    end\n\n    it \"should be able to check the existence of a domain user\" do\n      expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(\"#{domain}\\\\#{username}\").and_return(nil)\n      expect(Puppet::Util::Windows::ADSI).to receive(:connect).with(\"WinNT://#{domain}/#{username},user\").and_return(connection)\n      expect(connection).to receive(:Class).and_return('User')\n      expect(Puppet::Util::Windows::ADSI::User.exists?(domain_username)).to be_truthy\n    end\n\n    it \"should be able to confirm the existence of a user with a well-known SID\" do\n      system_user = Puppet::Util::Windows::SID::LocalSystem\n      # ensure that the underlying OS is queried here\n      allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_call_original()\n      expect(Puppet::Util::Windows::ADSI::User.exists?(system_user)).to be_truthy\n    end\n\n    it \"should return false with a well-known Group SID\" do\n      group = Puppet::Util::Windows::SID::BuiltinAdministrators\n      # ensure that the underlying OS is queried here\n      allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_call_original()\n      expect(Puppet::Util::Windows::ADSI::User.exists?(group)).to be_falsey\n    end\n\n    it \"should return nil with an unknown SID\" do\n      bogus_sid = 'S-1-2-3-4'\n      # ensure that the underlying OS is queried here\n      allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_call_original()\n      expect(Puppet::Util::Windows::ADSI::User.exists?(bogus_sid)).to be_falsey\n    end\n\n    it \"should be able to delete a user\" do\n      expect(connection).to receive(:Delete).with('user', username)\n\n      Puppet::Util::Windows::ADSI::User.delete(username)\n    end\n\n    it \"should return an enumeration of IADsUser wrapped objects\" do\n      name = 'Administrator'\n      wmi_users = [double('WMI', :name => name)]\n      expect(Puppet::Util::Windows::ADSI).to receive(:execquery).with('select name from win32_useraccount where localaccount = \"TRUE\"').and_return(wmi_users)\n\n      native_object = double('IADsUser')\n      homedir = \"C:\\\\Users\\\\#{name}\"\n      expect(native_object).to receive(:Get).with('HomeDirectory').and_return(homedir)\n      expect(Puppet::Util::Windows::ADSI).to receive(:connect).with(\"WinNT://./#{name},user\").and_return(native_object)\n\n      users = Puppet::Util::Windows::ADSI::User.to_a\n      expect(users.length).to eq(1)\n      expect(users[0].name).to eq(name)\n      expect(users[0]['HomeDirectory']).to eq(homedir)\n    end\n\n    describe \"an instance\" do\n      let(:adsi_user) { double('user', :objectSID => []) }\n      let(:sid)       { double(:account => username, :domain => 'testcomputername') }\n      let(:user)      { Puppet::Util::Windows::ADSI::User.new(username, adsi_user) }\n\n      it \"should provide its groups as a list of names\" do\n        names = [\"group1\", \"group2\"]\n\n        groups = names.map { |name| double('group', :Name => name) }\n\n        expect(adsi_user).to receive(:Groups).and_return(groups)\n\n        expect(user.groups).to match(names)\n      end\n\n      it \"should be able to test whether a given password is correct\" do\n        expect(Puppet::Util::Windows::ADSI::User).to receive(:logon).with(username, 'pwdwrong').and_return(false)\n        expect(Puppet::Util::Windows::ADSI::User).to receive(:logon).with(username, 'pwdright').and_return(true)\n\n        expect(user.password_is?('pwdwrong')).to be_falsey\n        expect(user.password_is?('pwdright')).to be_truthy\n      end\n\n      it \"should be able to set a password\" do\n        expect(adsi_user).to receive(:SetPassword).with('pwd')\n        expect(adsi_user).to receive(:SetInfo).at_least(:once)\n\n        flagname = \"UserFlags\"\n        fADS_UF_DONT_EXPIRE_PASSWD = 0x10000\n\n        expect(adsi_user).to receive(:Get).with(flagname).and_return(0)\n        expect(adsi_user).to receive(:Put).with(flagname, fADS_UF_DONT_EXPIRE_PASSWD)\n\n        user.password = 'pwd'\n      end\n\n       it \"should be able manage a user without a password\" do\n        expect(adsi_user).not_to receive(:SetPassword).with('pwd')\n        expect(adsi_user).to receive(:SetInfo).at_least(:once)\n\n        flagname = \"UserFlags\"\n        fADS_UF_DONT_EXPIRE_PASSWD = 0x10000\n\n        expect(adsi_user).to receive(:Get).with(flagname).and_return(0)\n        expect(adsi_user).to receive(:Put).with(flagname, fADS_UF_DONT_EXPIRE_PASSWD)\n\n        user.password = nil\n      end\n\n      it \"should generate the correct URI\" do\n        allow(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).and_return(sid)\n        expect(user.uri).to eq(\"WinNT://testcomputername/#{username},user\")\n      end\n\n      describe \"when given a set of groups to which to add the user\" do\n        let(:existing_groups) { ['group2','group3'] }\n        let(:group_sids) { existing_groups.each_with_index.map{|n,i| double(:Name => n, :objectSID => double(:sid => i))} }\n\n        let(:groups_to_set) { 'group1,group2' }\n        let(:desired_sids) { groups_to_set.split(',').each_with_index.map{|n,i| double(:Name => n, :objectSID => double(:sid => i-1))} }\n\n        before(:each) do\n          expect(user).to receive(:group_sids).and_return(group_sids.map {|s| s.objectSID })\n        end\n\n        describe \"if membership is specified as inclusive\" do\n          it \"should add the user to those groups, and remove it from groups not in the list\" do\n            expect(Puppet::Util::Windows::ADSI::User).to receive(:name_sid_hash).and_return(Hash[ desired_sids.map { |s| [s.objectSID.sid, s.objectSID] }])\n            expect(user).to receive(:add_group_sids) { |value| expect(value.sid).to eq(-1) }\n            expect(user).to receive(:remove_group_sids) { |value| expect(value.sid).to eq(1) }\n\n            user.set_groups(groups_to_set, false)\n          end\n\n          it \"should remove all users from a group if desired is empty\" do\n            expect(Puppet::Util::Windows::ADSI::User).to receive(:name_sid_hash).and_return({})\n            expect(user).not_to receive(:add_group_sids)\n            expect(user).to receive(:remove_group_sids) do |user1, user2|\n              expect(user1.sid).to eq(0)\n              expect(user2.sid).to eq(1)\n            end\n\n            user.set_groups('', false)\n          end\n        end\n\n        describe \"if membership is specified as minimum\" do\n          it \"should add the user to the specified groups without affecting its other memberships\" do\n            expect(Puppet::Util::Windows::ADSI::User).to receive(:name_sid_hash).and_return(Hash[ desired_sids.map { |s| [s.objectSID.sid, s.objectSID] }])\n            expect(user).to receive(:add_group_sids) { |value| expect(value.sid).to eq(-1) }\n            expect(user).not_to receive(:remove_group_sids)\n\n            user.set_groups(groups_to_set, true)\n          end\n\n          it \"should do nothing if desired is empty\" do\n            expect(Puppet::Util::Windows::ADSI::User).to receive(:name_sid_hash).and_return({})\n            expect(user).not_to receive(:remove_group_sids)\n            expect(user).not_to receive(:add_group_sids)\n\n            user.set_groups('', true)\n          end\n        end\n      end\n\n      describe 'userflags' do\n        # Avoid having to type out the constant everytime we want to\n        # retrieve a userflag's value.\n        def ads_userflags(flag)\n          Puppet::Util::Windows::ADSI::User::ADS_USERFLAGS[flag]\n        end\n\n        before(:each) do\n          userflags = [\n            :ADS_UF_SCRIPT,\n            :ADS_UF_ACCOUNTDISABLE,\n            :ADS_UF_HOMEDIR_REQUIRED,\n            :ADS_UF_LOCKOUT\n          ].inject(0) do |flags, flag|\n            flags | ads_userflags(flag)\n          end\n\n          allow(user).to receive(:[]).with('UserFlags').and_return(userflags)\n        end\n\n        describe '#userflag_set?' do\n          it 'returns true if the specified userflag is set' do\n            expect(user.userflag_set?(:ADS_UF_SCRIPT)).to be true\n          end\n\n          it 'returns false if the specified userflag is not set' do\n            expect(user.userflag_set?(:ADS_UF_PASSWD_NOTREQD)).to be false\n          end\n\n          it 'returns false if the specified userflag is an unrecognized userflag' do\n            expect(user.userflag_set?(:ADS_UF_UNRECOGNIZED_FLAG)).to be false\n          end\n        end\n\n        shared_examples 'set/unset common tests' do |method|\n          it 'raises an ArgumentError for any unrecognized userflags' do\n            unrecognized_flags = [\n              :ADS_UF_UNRECOGNIZED_FLAG_ONE,\n              :ADS_UF_UNRECOGNIZED_FLAG_TWO\n            ]\n            input_flags = unrecognized_flags + [\n              :ADS_UF_PASSWORD_EXPIRED,\n              :ADS_UF_DONT_EXPIRE_PASSWD\n            ]\n\n            expect { user.send(method, *input_flags) }.to raise_error(\n              ArgumentError, /#{unrecognized_flags.join(', ')}/\n            )\n          end\n\n          it 'noops if no userflags are passed-in' do\n            expect(user).not_to receive(:[]=)\n            expect(user).not_to receive(:commit)\n\n            user.send(method)\n          end\n        end\n\n        describe '#set_userflags' do\n          include_examples 'set/unset common tests', :set_userflags\n\n          it 'should add the passed-in flags to the current set of userflags' do\n            input_flags = [\n              :ADS_UF_PASSWORD_EXPIRED,\n              :ADS_UF_DONT_EXPIRE_PASSWD\n            ]\n\n            userflags = user['UserFlags']\n            expected_userflags = userflags | ads_userflags(input_flags[0]) | ads_userflags(input_flags[1])\n\n            expect(user).to receive(:[]=).with('UserFlags', expected_userflags)\n\n            user.set_userflags(*input_flags)\n          end\n        end\n\n        describe '#unset_userflags' do\n          include_examples 'set/unset common tests', :unset_userflags\n\n          it 'should remove the passed-in flags from the current set of userflags' do\n            input_flags = [\n              :ADS_UF_SCRIPT,\n              :ADS_UF_ACCOUNTDISABLE\n            ]\n\n            # ADS_UF_HOMEDIR_REQUIRED and ADS_UF_LOCKOUT should be the only flags set.\n            expected_userflags = 0 | ads_userflags(:ADS_UF_HOMEDIR_REQUIRED) | ads_userflags(:ADS_UF_LOCKOUT)\n\n            expect(user).to receive(:[]=).with('UserFlags', expected_userflags)\n\n            user.unset_userflags(*input_flags)\n          end\n        end\n      end\n    end\n  end\n\n  describe Puppet::Util::Windows::ADSI::Group do\n    let(:groupname)  { 'testgroup' }\n\n    describe \"an instance\" do\n      let(:adsi_group) { double('group') }\n      let(:group)      { Puppet::Util::Windows::ADSI::Group.new(groupname, adsi_group) }\n      let(:someone_sid){ double(:account => 'someone', :domain => 'testcomputername')}\n\n      describe \"should be able to use SID objects\" do\n        let(:system)     { Puppet::Util::Windows::SID.name_to_principal('SYSTEM') }\n        let(:invalid)    { Puppet::Util::Windows::SID.name_to_principal('foobar') }\n\n        it \"to add a member\" do\n          expect(adsi_group).to receive(:Add).with(\"WinNT://S-1-5-18\")\n\n          group.add_member_sids(system)\n        end\n\n        it \"and raise when passed a non-SID object to add\" do\n          expect{ group.add_member_sids(invalid)}.to raise_error(Puppet::Error, /Must use a valid SID::Principal/)\n        end\n\n        it \"to remove a member\" do\n          expect(adsi_group).to receive(:Remove).with(\"WinNT://S-1-5-18\")\n\n          group.remove_member_sids(system)\n        end\n\n        it \"and raise when passed a non-SID object to remove\" do\n          expect{ group.remove_member_sids(invalid)}.to raise_error(Puppet::Error, /Must use a valid SID::Principal/)\n        end\n      end\n\n      it \"should provide its groups as a list of names\" do\n        names = ['user1', 'user2']\n\n        users = names.map { |name| double('user', :Name => name, :objectSID => name, :ole_respond_to? => true) }\n\n        expect(adsi_group).to receive(:Members).and_return(users)\n\n        expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with('user1').and_return(double(:domain_account => 'HOSTNAME\\user1'))\n        expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with('user2').and_return(double(:domain_account => 'HOSTNAME\\user2'))\n\n        expect(group.members.map(&:domain_account)).to match(['HOSTNAME\\user1', 'HOSTNAME\\user2'])\n      end\n\n      context \"calling .set_members\" do\n        it \"should set the members of a group to only desired_members when inclusive\" do\n          names = ['DOMAIN\\user1', 'user2']\n          sids = [\n            double(:account => 'user1', :domain => 'DOMAIN', :sid => 1),\n            double(:account => 'user2', :domain => 'testcomputername', :sid => 2),\n            double(:account => 'user3', :domain => 'DOMAIN2', :sid => 3),\n          ]\n\n          # use stubbed objectSid on member to return stubbed SID\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([0]).and_return(sids[0])\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([1]).and_return(sids[1])\n\n          expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user2', false).and_return(sids[1])\n          expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('DOMAIN2\\user3', false).and_return(sids[2])\n\n          expect(Puppet::Util::Windows::ADSI).to receive(:sid_uri).with(sids[0]).and_return(\"WinNT://DOMAIN/user1,user\")\n          expect(Puppet::Util::Windows::ADSI).to receive(:sid_uri).with(sids[2]).and_return(\"WinNT://DOMAIN2/user3,user\")\n\n          members = names.each_with_index.map{|n,i| double(:Name => n, :objectSID => [i], :ole_respond_to? => true)}\n          expect(adsi_group).to receive(:Members).and_return(members)\n\n          expect(adsi_group).to receive(:Remove).with('WinNT://DOMAIN/user1,user')\n          expect(adsi_group).to receive(:Add).with('WinNT://DOMAIN2/user3,user')\n\n          group.set_members(['user2', 'DOMAIN2\\user3'])\n        end\n\n        it \"should add the desired_members to an existing group when not inclusive\" do\n          names = ['DOMAIN\\user1', 'user2']\n          sids = [\n            double(:account => 'user1', :domain => 'DOMAIN', :sid => 1),\n            double(:account => 'user2', :domain => 'testcomputername', :sid => 2),\n            double(:account => 'user3', :domain => 'DOMAIN2', :sid => 3),\n          ]\n\n          # use stubbed objectSid on member to return stubbed SID\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([0]).and_return(sids[0])\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([1]).and_return(sids[1])\n\n          expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('user2', any_args).and_return(sids[1])\n          expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with('DOMAIN2\\user3', any_args).and_return(sids[2])\n\n          expect(Puppet::Util::Windows::ADSI).to receive(:sid_uri).with(sids[2]).and_return(\"WinNT://DOMAIN2/user3,user\")\n\n          members = names.each_with_index.map {|n,i| double(:Name => n, :objectSID => [i], :ole_respond_to? => true)}\n          expect(adsi_group).to receive(:Members).and_return(members)\n\n          expect(adsi_group).not_to receive(:Remove).with('WinNT://DOMAIN/user1,user')\n\n          expect(adsi_group).to receive(:Add).with('WinNT://DOMAIN2/user3,user')\n\n          group.set_members(['user2', 'DOMAIN2\\user3'],false)\n        end\n\n        it \"should return immediately when desired_members is nil\" do\n          expect(adsi_group).not_to receive(:Members)\n\n          expect(adsi_group).not_to receive(:Remove)\n          expect(adsi_group).not_to receive(:Add)\n\n          group.set_members(nil)\n        end\n\n        it \"should remove all members when desired_members is empty and inclusive\" do\n          names = ['DOMAIN\\user1', 'user2']\n          sids = [\n            double(:account => 'user1', :domain => 'DOMAIN', :sid => 1 ),\n            double(:account => 'user2', :domain => 'testcomputername', :sid => 2 ),\n          ]\n\n          # use stubbed objectSid on member to return stubbed SID\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([0]).and_return(sids[0])\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([1]).and_return(sids[1])\n\n          expect(Puppet::Util::Windows::ADSI).to receive(:sid_uri).with(sids[0]).and_return(\"WinNT://DOMAIN/user1,user\")\n          expect(Puppet::Util::Windows::ADSI).to receive(:sid_uri).with(sids[1]).and_return(\"WinNT://testcomputername/user2,user\")\n\n          members = names.each_with_index.map{|n,i| double(:Name => n, :objectSID => [i], :ole_respond_to? => true)}\n          expect(adsi_group).to receive(:Members).and_return(members)\n\n          expect(adsi_group).to receive(:Remove).with('WinNT://DOMAIN/user1,user')\n          expect(adsi_group).to receive(:Remove).with('WinNT://testcomputername/user2,user')\n\n          group.set_members([])\n        end\n\n        it \"should do nothing when desired_members is empty and not inclusive\" do\n          names = ['DOMAIN\\user1', 'user2']\n          sids = [\n            double(:account => 'user1', :domain => 'DOMAIN', :sid => 1 ),\n            double(:account => 'user2', :domain => 'testcomputername', :sid => 2 ),\n          ]\n          # use stubbed objectSid on member to return stubbed SID\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([0]).and_return(sids[0])\n          expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([1]).and_return(sids[1])\n\n          members = names.each_with_index.map{|n,i| double(:Name => n, :objectSID => [i], :ole_respond_to? => true)}\n          expect(adsi_group).to receive(:Members).and_return(members)\n\n          expect(adsi_group).not_to receive(:Remove)\n          expect(adsi_group).not_to receive(:Add)\n\n          group.set_members([],false)\n        end\n\n        it \"should raise an error when a username does not resolve to a SID\" do\n          expect {\n            expect(adsi_group).to receive(:Members).and_return([])\n            group.set_members(['foobar'])\n          }.to raise_error(Puppet::Error, /Could not resolve name: foobar/)\n        end\n      end\n\n      it \"should generate the correct URI\" do\n        expect(adsi_group).to receive(:objectSID).and_return([0])\n        expect(Socket).to receive(:gethostname).and_return('TESTcomputerNAME')\n        computer_sid = double(:account => groupname,:domain => 'testcomputername')\n        expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([0]).and_return(computer_sid)\n        expect(group.uri).to eq(\"WinNT://./#{groupname},group\")\n      end\n    end\n\n    it \"should generate the correct URI\" do\n      expect(Puppet::Util::Windows::ADSI::Group.uri(\"people\")).to eq(\"WinNT://./people,group\")\n    end\n\n    it \"should generate the correct URI for a BUILTIN group\" do\n      expect(Puppet::Util::Windows::ADSI::Group.uri(groupname, builtin_localized)).to eq(\"WinNT://./#{groupname},group\")\n    end\n\n    it \"should generate the correct URI for a NT AUTHORITY group\" do\n      expect(Puppet::Util::Windows::ADSI::Group.uri(groupname, ntauthority_localized)).to eq(\"WinNT://./#{groupname},group\")\n    end\n\n    context \"when domain-joined\" do\n      it_should_behave_like 'a local only resource query', Puppet::Util::Windows::ADSI::Group, :SidTypeGroup do\n        let(:resource_name) { groupname }\n      end\n    end\n\n    it \"should be able to create a group\" do\n      adsi_group = double(\"adsi\")\n\n      expect(connection).to receive(:Create).with('group', groupname).and_return(adsi_group)\n      expect(Puppet::Util::Windows::ADSI::User).to receive(:exists?).with(groupname).and_return(false)\n\n      group = Puppet::Util::Windows::ADSI::Group.create(groupname)\n\n      expect(group).to be_a(Puppet::Util::Windows::ADSI::Group)\n      expect(group.native_object).to eq(adsi_group)\n    end\n\n    it \"should be able to confirm the existence of a group\" do\n      expect(Puppet::Util::Windows::SID).to receive(:name_to_principal).with(groupname).and_return(nil)\n      expect(Puppet::Util::Windows::ADSI).to receive(:connect).with(\"WinNT://./#{groupname},group\").and_return(connection)\n      expect(connection).to receive(:Class).and_return('Group')\n\n      expect(Puppet::Util::Windows::ADSI::Group.exists?(groupname)).to be_truthy\n    end\n\n    it \"should be able to confirm the existence of a group with a well-known SID\" do\n      service_group = Puppet::Util::Windows::SID::Service\n      # ensure that the underlying OS is queried here\n      allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_call_original()\n      expect(Puppet::Util::Windows::ADSI::Group.exists?(service_group)).to be_truthy\n    end\n\n    it \"will return true with a well-known User SID, as there is no way to resolve it with a WinNT:// style moniker\" do\n      user = Puppet::Util::Windows::SID::NtLocal\n      # ensure that the underlying OS is queried here\n      allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_call_original()\n      expect(Puppet::Util::Windows::ADSI::Group.exists?(user)).to be_truthy\n    end\n\n    it \"should return nil with an unknown SID\" do\n      bogus_sid = 'S-1-2-3-4'\n      # ensure that the underlying OS is queried here\n      allow(Puppet::Util::Windows::ADSI).to receive(:connect).and_call_original()\n      expect(Puppet::Util::Windows::ADSI::Group.exists?(bogus_sid)).to be_falsey\n    end\n\n    it \"should be able to delete a group\" do\n      expect(connection).to receive(:Delete).with('group', groupname)\n\n      Puppet::Util::Windows::ADSI::Group.delete(groupname)\n    end\n\n    it \"should return an enumeration of IADsGroup wrapped objects\" do\n      name = 'Administrators'\n      wmi_groups = [double('WMI', :name => name)]\n      expect(Puppet::Util::Windows::ADSI).to receive(:execquery).with('select name from win32_group where localaccount = \"TRUE\"').and_return(wmi_groups)\n\n      native_object = double('IADsGroup')\n      expect(Puppet::Util::Windows::SID).to receive(:octet_string_to_principal).with([]).and_return(double(:domain_account => '.\\Administrator'))\n      expect(native_object).to receive(:Members).and_return([double(:Name => 'Administrator', :objectSID => [], :ole_respond_to? => true)])\n      expect(Puppet::Util::Windows::ADSI).to receive(:connect).with(\"WinNT://./#{name},group\").and_return(native_object)\n\n      groups = Puppet::Util::Windows::ADSI::Group.to_a\n      expect(groups.length).to eq(1)\n      expect(groups[0].name).to eq(name)\n      expect(groups[0].members.map(&:domain_account)).to eq(['.\\Administrator'])\n    end\n  end\n\n  describe Puppet::Util::Windows::ADSI::UserProfile do\n    it \"should be able to delete a user profile\" do\n      expect(connection).to receive(:Delete).with(\"Win32_UserProfile.SID='S-A-B-C'\")\n      Puppet::Util::Windows::ADSI::UserProfile.delete('S-A-B-C')\n    end\n\n    it \"should warn on 2003\" do\n      expect(connection).to receive(:Delete).and_raise(WIN32OLERuntimeError,\n \"Delete (WIN32OLERuntimeError)\n    OLE error code:80041010 in SWbemServicesEx\n      Invalid class\n    HRESULT error code:0x80020009\n      Exception occurred.\")\n\n      expect(Puppet).to receive(:warning).with(\"Cannot delete user profile for 'S-A-B-C' prior to Vista SP1\")\n      Puppet::Util::Windows::ADSI::UserProfile.delete('S-A-B-C')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/api_types_spec.rb",
    "content": "# encoding: UTF-8\n\nrequire 'spec_helper'\n\ndescribe \"FFI::MemoryPointer\", :if => Puppet::Util::Platform.windows? do\n  # use 2 bad bytes at end so we have even number of bytes / characters\n  let(:bad_string) { \"hello invalid world\".encode(Encoding::UTF_16LE) + \"\\xDD\\xDD\".force_encoding(Encoding::UTF_16LE) }\n  let(:bad_string_bytes) { bad_string.bytes.to_a }\n  let(:a_wide_bytes) { \"A\".encode(Encoding::UTF_16LE).bytes.to_a }\n  let(:b_wide_bytes) { \"B\".encode(Encoding::UTF_16LE).bytes.to_a }\n\n  context \"read_wide_string\" do\n    let (:string) { \"foo_bar\" }\n\n    it \"should properly roundtrip a given string\" do\n      FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr|\n        expect(ptr.read_wide_string(string.length)).to eq(string)\n      end\n    end\n\n    it \"should return a given string in UTF-8\" do\n      FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr|\n        read_string = ptr.read_wide_string(string.length)\n        expect(read_string.encoding).to eq(Encoding::UTF_8)\n      end\n    end\n\n    it \"should raise an error and emit a debug message when receiving a string containing invalid bytes in the destination encoding\" do\n      Puppet[:log_level] = 'debug'\n\n      expect {\n        FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr|\n          # uchar here is synonymous with byte\n          ptr.put_array_of_uchar(0, bad_string_bytes)\n\n          ptr.read_wide_string(bad_string.length)\n        end\n      }.to raise_error(Encoding::InvalidByteSequenceError)\n\n      expect(@logs.last.message).to eq(\"Unable to convert value #{bad_string.dump} to encoding UTF-8 due to #<Encoding::InvalidByteSequenceError: \\\"\\\\xDD\\\\xDD\\\" on UTF-16LE>\")\n    end\n\n    it \"should not raise an error when receiving a string containing invalid bytes in the destination encoding, when specifying :invalid => :replace\" do\n      FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr|\n        # uchar here is synonymous with byte\n        ptr.put_array_of_uchar(0, bad_string_bytes)\n\n        read_string = ptr.read_wide_string(bad_string.length, Encoding::UTF_8, false, :invalid => :replace)\n        expect(read_string).to eq(\"hello invalid world\\uFFFD\")\n      end\n    end\n\n    it \"raises an IndexError if asked to read more characters than there are bytes allocated\" do\n      expect {\n        FFI::MemoryPointer.new(:byte, 1) do |ptr|\n          ptr.read_wide_string(1) # 1 wchar = 2 bytes\n        end\n      }.to raise_error(IndexError, /out of bounds/)\n    end\n\n    it \"raises an IndexError if asked to read a negative number of characters\" do\n      expect {\n        FFI::MemoryPointer.new(:byte, 1) do |ptr|\n          ptr.read_wide_string(-1)\n        end\n      }.to raise_error(IndexError, /out of bounds/)\n    end\n\n    it \"returns an empty string if asked to read 0 characters\" do\n      FFI::MemoryPointer.new(:byte, 1) do |ptr|\n        expect(ptr.read_wide_string(0)).to eq(\"\")\n      end\n    end\n\n    it \"returns a substring if asked to read fewer characters than are in the byte array\" do\n      FFI::MemoryPointer.new(:byte, 4) do |ptr|\n        ptr.write_array_of_uint8(\"AB\".encode('UTF-16LE').bytes.to_a)\n        expect(ptr.read_wide_string(1)).to eq(\"A\")\n      end\n    end\n\n    it \"preserves wide null characters in the string\" do\n      FFI::MemoryPointer.new(:byte, 6) do |ptr|\n        ptr.write_array_of_uint8(a_wide_bytes + [0, 0] + b_wide_bytes)\n        expect(ptr.read_wide_string(3)).to eq(\"A\\x00B\")\n      end\n    end\n  end\n\n  context \"read_arbitrary_wide_string_up_to\" do\n    let (:string) { \"foo_bar\" }\n    let (:single_null_string) { string + \"\\x00\" }\n    let (:double_null_string) { string + \"\\x00\\x00\" }\n\n    it \"should read a short single null terminated string\" do\n      FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr|\n        expect(ptr.read_arbitrary_wide_string_up_to).to eq(string)\n      end\n    end\n\n    it \"should read a short double null terminated string\" do\n      FFI::MemoryPointer.from_string_to_wide_string(double_null_string) do |ptr|\n        expect(ptr.read_arbitrary_wide_string_up_to(512, :double_null)).to eq(string)\n      end\n    end\n\n    it \"detects trailing single null wchar\" do\n      FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr|\n        expect(ptr).to receive(:read_wide_string).with(string.length, anything, anything, anything).and_call_original\n\n        expect(ptr.read_arbitrary_wide_string_up_to).to eq(string)\n      end\n    end\n\n    it \"detects trailing double null wchar\" do\n      FFI::MemoryPointer.from_string_to_wide_string(double_null_string) do |ptr|\n        expect(ptr).to receive(:read_wide_string).with(string.length, anything, anything, anything).and_call_original\n\n        expect(ptr.read_arbitrary_wide_string_up_to(512, :double_null)).to eq(string)\n      end\n    end\n\n    it \"should raises an IndexError if max_length is negative\" do\n      FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr|\n        expect {\n          ptr.read_arbitrary_wide_string_up_to(-1)\n        }.to raise_error(IndexError, /out of bounds/)\n      end\n    end\n\n    it \"should return an empty string when the max_length is 0\" do\n      FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr|\n        expect(ptr.read_arbitrary_wide_string_up_to(0)).to eq(\"\")\n      end\n    end\n\n    it \"should return a string of max_length characters when specified\" do\n      FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr|\n        expect(ptr.read_arbitrary_wide_string_up_to(3)).to eq(string[0..2])\n      end\n    end\n\n    it \"should return wide strings in UTF-8\" do\n      FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr|\n        read_string = ptr.read_arbitrary_wide_string_up_to\n        expect(read_string.encoding).to eq(Encoding::UTF_8)\n      end\n    end\n\n    it \"should not raise an error when receiving a string containing invalid bytes in the destination encoding, when specifying :invalid => :replace\" do\n      FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr|\n        # uchar here is synonymous with byte\n        ptr.put_array_of_uchar(0, bad_string_bytes)\n\n        read_string = ptr.read_arbitrary_wide_string_up_to(ptr.size / 2, :single_null, :invalid => :replace)\n        expect(read_string).to eq(\"hello invalid world\\uFFFD\")\n      end\n    end\n\n    it \"should raise an IndexError if there isn't a null terminator\" do\n      # This only works when using a memory pointer with a known number of cells\n      # and size per cell, but not arbitrary Pointers\n      FFI::MemoryPointer.new(:wchar, 1) do |ptr|\n        ptr.write_array_of_uint8(a_wide_bytes)\n\n        expect {\n          ptr.read_arbitrary_wide_string_up_to(42)\n        }.to raise_error(IndexError, /out of bounds/)\n      end\n    end\n\n    it \"should raise an IndexError if there isn't a double null terminator\" do\n      # This only works when using a memory pointer with a known number of cells\n      # and size per cell, but not arbitrary Pointers\n      FFI::MemoryPointer.new(:wchar, 1) do |ptr|\n        ptr.write_array_of_uint8(a_wide_bytes)\n\n        expect {\n          ptr.read_arbitrary_wide_string_up_to(42, :double_null)\n        }.to raise_error(IndexError, /out of bounds/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/eventlog_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'puppet/util/windows'\n\ndescribe Puppet::Util::Windows::EventLog, :if => Puppet::Util::Platform.windows? do\n\n  before(:each) { @event_log = Puppet::Util::Windows::EventLog.new }\n  after(:each) { @event_log.close }\n\n  describe \"class constants\" do\n    it \"should define NULL_HANDLE as 0\" do\n      expect(Puppet::Util::Windows::EventLog::NULL_HANDLE).to eq(0)\n    end\n\n    it \"should define WIN32_FALSE as 0\" do\n      expect(Puppet::Util::Windows::EventLog::WIN32_FALSE).to eq(0)\n    end\n  end\n\n  describe \"self.open\" do\n    it \"sets a handle to the event log\" do\n      default_name = Puppet::Util::Windows::String.wide_string('Puppet')\n      # return nil explicitly just to reinforce that we're not leaking eventlog handle\n      expect_any_instance_of(Puppet::Util::Windows::EventLog).to receive(:RegisterEventSourceW).with(anything, default_name).and_return(nil)\n      Puppet::Util::Windows::EventLog.new\n    end\n\n    context \"when it fails to open the event log\" do\n      before do\n        # RegisterEventSourceW will return NULL on failure\n        # Stubbing prevents leaking eventlog handle\n        allow_any_instance_of(Puppet::Util::Windows::EventLog).to receive(:RegisterEventSourceW).and_return(Puppet::Util::Windows::EventLog::NULL_HANDLE)\n      end\n\n      it \"raises an exception warning that the event log failed to open\" do\n        expect { Puppet::Util::Windows::EventLog.open('foo') }.to raise_error(Puppet::Util::Windows::EventLog::EventLogError, /failed to open Windows eventlog/)\n      end\n\n      it \"passes the exit code to the exception constructor\" do\n        fake_error = Puppet::Util::Windows::EventLog::EventLogError.new('foo', 87)\n        allow(FFI).to receive(:errno).and_return(87)\n        # All we're testing here is that the constructor actually receives the exit code from FFI.errno (87)\n        # We do so because `expect to...raise_error` doesn't support multiple parameter match arguments\n        # We return fake_error just because `raise` expects an exception class\n        expect(Puppet::Util::Windows::EventLog::EventLogError).to receive(:new).with(/failed to open Windows eventlog/, 87).and_return(fake_error)\n        expect { Puppet::Util::Windows::EventLog.open('foo') }.to raise_error(Puppet::Util::Windows::EventLog::EventLogError)\n      end\n    end\n  end\n\n  describe \"#close\" do\n    it \"closes the handle to the event log\" do\n      @handle = \"12345\"\n      allow_any_instance_of(Puppet::Util::Windows::EventLog).to receive(:RegisterEventSourceW).and_return(@handle)\n      event_log = Puppet::Util::Windows::EventLog.new\n      expect(event_log).to receive(:DeregisterEventSource).with(@handle).and_return(1)\n      event_log.close\n    end\n  end\n\n  describe \"#report_event\" do\n    it \"raises an exception if the message passed is not a string\" do\n      expect { @event_log.report_event(:data => 123, :event_type => nil, :event_id => nil) }.to raise_error(ArgumentError, /data must be a string/)\n    end\n\n    context \"when an event report fails\" do\n      before do\n        # ReportEventW returns 0 on failure, which is mapped to WIN32_FALSE\n        allow(@event_log).to receive(:ReportEventW).and_return(Puppet::Util::Windows::EventLog::WIN32_FALSE)\n      end\n\n      it \"raises an exception warning that the event report failed\" do\n        expect { @event_log.report_event(:data => 'foo', :event_type => Puppet::Util::Windows::EventLog::EVENTLOG_ERROR_TYPE, :event_id => 0x03) }.to raise_error(Puppet::Util::Windows::EventLog::EventLogError, /failed to report event/)\n      end\n\n      it \"passes the exit code to the exception constructor\" do\n        fake_error = Puppet::Util::Windows::EventLog::EventLogError.new('foo', 5)\n        allow(FFI).to receive(:errno).and_return(5)\n        # All we're testing here is that the constructor actually receives the exit code from FFI.errno (5)\n        # We do so because `expect to...raise_error` doesn't support multiple parameter match arguments\n        # We return fake_error just because `raise` expects an exception class\n        expect(Puppet::Util::Windows::EventLog::EventLogError).to receive(:new).with(/failed to report event/, 5).and_return(fake_error)\n        expect { @event_log.report_event(:data => 'foo', :event_type => Puppet::Util::Windows::EventLog::EVENTLOG_ERROR_TYPE, :event_id => 0x03) }.to raise_error(Puppet::Util::Windows::EventLog::EventLogError)\n      end\n    end\n  end\n\n  describe \"self.to_native\" do\n    it \"raises an exception if the log level is not supported\" do\n      expect { Puppet::Util::Windows::EventLog.to_native(:foo) }.to raise_error(ArgumentError)\n    end\n\n    # This is effectively duplicating the data assigned to the constants in\n    # Puppet::Util::Windows::EventLog but since these are public constants we\n    # ensure their values don't change lightly.\n    log_levels_to_type_and_id = {\n      :debug    => [0x0004, 0x01],\n      :info     => [0x0004, 0x01],\n      :notice   => [0x0004, 0x01],\n      :warning  => [0x0002, 0x02],\n      :err      => [0x0001, 0x03],\n      :alert    => [0x0001, 0x03],\n      :emerg    => [0x0001, 0x03],\n      :crit     => [0x0001, 0x03],\n    }\n\n    shared_examples_for \"#to_native\" do |level|\n      it \"should return the correct INFORMATION_TYPE and ID\" do\n        result = Puppet::Util::Windows::EventLog.to_native(level)\n        expect(result).to eq(log_levels_to_type_and_id[level])\n      end\n    end\n\n    log_levels_to_type_and_id.each_key do |level|\n      describe \"logging at #{level}\" do\n        it_should_behave_like \"#to_native\", level\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/file_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe Puppet::Util::Windows::File, :if => Puppet::Util::Platform.windows? do\n  include PuppetSpec::Files\n\n  let(:nonexist_file) { 'C:\\foo.bar' }\n  let(:nonexist_path) { 'C:\\somefile\\that\\wont\\ever\\exist' }\n  let(:invalid_file_attributes) { 0xFFFFFFFF } #define INVALID_FILE_ATTRIBUTES (DWORD (-1))\n\n  describe \"get_attributes\" do\n    it \"should raise an error for files that do not exist by default\" do\n      expect {\n        described_class.get_attributes(nonexist_file)\n      }.to raise_error(Puppet::Error, /GetFileAttributes/)\n    end\n\n    it \"should raise an error for files that do not exist when specified\" do\n      expect {\n        described_class.get_attributes(nonexist_file, true)\n      }.to raise_error(Puppet::Error, /GetFileAttributes/)\n    end\n\n    it \"should not raise an error for files that do not exist when specified\" do\n      expect {\n        described_class.get_attributes(nonexist_file, false)\n      }.not_to raise_error\n    end\n\n    it \"should return INVALID_FILE_ATTRIBUTES for files that do not exist when specified\" do\n      expect(described_class.get_attributes(nonexist_file, false)).to eq(invalid_file_attributes)\n    end\n  end\n\n  describe \"get_long_pathname\" do\n    it \"should raise an ERROR_FILE_NOT_FOUND for a file that does not exist in a valid path\" do\n      expect {\n        described_class.get_long_pathname(nonexist_file)\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(Puppet::Util::Windows::File::ERROR_FILE_NOT_FOUND)\n      end\n    end\n\n    it \"should raise an ERROR_PATH_NOT_FOUND for a path that does not exist\" do\n      expect {\n        described_class.get_long_pathname(nonexist_path)\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(Puppet::Util::Windows::File::ERROR_PATH_NOT_FOUND)\n      end\n    end\n\n    it \"should return the fully expanded path 'Program Files' given 'Progra~1'\" do\n      # this test could be resolve some of these values at runtime rather than hard-coding\n      shortened = ENV['SystemDrive'] + '\\\\Progra~1'\n      expanded = ENV['SystemDrive'] + '\\\\Program Files'\n      expect(described_class.get_long_pathname(shortened)).to eq (expanded)\n    end\n  end\n\n  describe \"get_short_pathname\" do\n    it \"should raise an ERROR_FILE_NOT_FOUND for a file that does not exist in a valid path\" do\n      expect {\n        described_class.get_short_pathname(nonexist_file)\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(Puppet::Util::Windows::File::ERROR_FILE_NOT_FOUND)\n      end\n    end\n\n    it \"should raise an ERROR_PATH_NOT_FOUND for a path that does not exist\" do\n      expect {\n        described_class.get_short_pathname(nonexist_path)\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(Puppet::Util::Windows::File::ERROR_PATH_NOT_FOUND)\n      end\n    end\n\n    it \"should return the shortened 'PROGRA~1' given fully expanded path 'Program Files'\" do\n      # this test could be resolve some of these values at runtime rather than hard-coding\n      expanded = ENV['SystemDrive'] + '\\\\Program Files'\n      shortened = ENV['SystemDrive'] + '\\\\PROGRA~1'\n      expect(described_class.get_short_pathname(expanded)).to eq (shortened)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/root_certs_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe \"Puppet::Util::Windows::RootCerts\", :if => Puppet::Util::Platform.windows? do\n  let(:x509_store) { Puppet::Util::Windows::RootCerts.instance.to_a }\n\n  it \"should return at least one X509 certificate\" do\n    expect(x509_store.to_a.size).to be >= 1\n  end\n\n  it \"should return an X509 certificate with a subject\" do\n    x509 = x509_store.first\n\n    expect(x509.subject.to_utf8).to match(/CN=.*/)\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/security_descriptor_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe \"Puppet::Util::Windows::SecurityDescriptor\", :if => Puppet::Util::Platform.windows? do\n  let(:system_sid) { Puppet::Util::Windows::SID::LocalSystem }\n  let(:admins_sid) { Puppet::Util::Windows::SID::BuiltinAdministrators }\n  let(:group_sid) { Puppet::Util::Windows::SID::Nobody }\n  let(:new_sid)   { 'S-1-5-32-500-1-2-3' }\n\n  def empty_dacl\n    Puppet::Util::Windows::AccessControlList.new\n  end\n\n  def system_ace_dacl\n    dacl = Puppet::Util::Windows::AccessControlList.new\n    dacl.allow(system_sid, 0x1)\n    dacl\n  end\n\n  context \"owner\" do\n    it \"changes the owner\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, system_ace_dacl)\n      sd.owner = new_sid\n\n      expect(sd.owner).to eq(new_sid)\n    end\n\n    it \"performs a noop if the new owner is the same as the old one\" do\n      dacl = system_ace_dacl\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, dacl)\n      sd.owner = sd.owner\n\n      expect(sd.dacl.object_id).to eq(dacl.object_id)\n    end\n\n    it \"prepends SYSTEM when security descriptor owner is no longer SYSTEM\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, system_ace_dacl)\n      sd.owner = new_sid\n\n      aces = sd.dacl.to_a\n      expect(aces.size).to eq(2)\n      expect(aces[0].sid).to eq(system_sid)\n      expect(aces[1].sid).to eq(new_sid)\n    end\n\n    it \"does not prepend SYSTEM when DACL already contains inherited SYSTEM ace\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(admins_sid, system_sid, empty_dacl)\n      sd.dacl.allow(admins_sid, 0x1)\n      sd.dacl.allow(system_sid, 0x1, Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE)\n      sd.owner = new_sid\n\n      aces = sd.dacl.to_a\n      expect(aces.size).to eq(2)\n      expect(aces[0].sid).to eq(new_sid)\n    end\n\n    it \"does not prepend SYSTEM when security descriptor owner wasn't SYSTEM\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(group_sid, group_sid, empty_dacl)\n      sd.dacl.allow(group_sid, 0x1)\n      sd.owner = new_sid\n\n      aces = sd.dacl.to_a\n      expect(aces.size).to eq(1)\n      expect(aces[0].sid).to eq(new_sid)\n    end\n  end\n\n  context \"group\" do\n    it \"changes the group\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, system_ace_dacl)\n      sd.group = new_sid\n\n      expect(sd.group).to eq(new_sid)\n    end\n\n    it \"performs a noop if the new group is the same as the old one\" do\n      dacl = system_ace_dacl\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, dacl)\n      sd.group = sd.group\n\n      expect(sd.dacl.object_id).to eq(dacl.object_id)\n    end\n\n    it \"prepends SYSTEM when security descriptor group is no longer SYSTEM\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(new_sid, system_sid, system_ace_dacl)\n      sd.group = new_sid\n\n      aces = sd.dacl.to_a\n      expect(aces.size).to eq(2)\n      expect(aces[0].sid).to eq(system_sid)\n      expect(aces[1].sid).to eq(new_sid)\n    end\n\n    it \"does not prepend SYSTEM when DACL already contains inherited SYSTEM ace\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(admins_sid, admins_sid, empty_dacl)\n      sd.dacl.allow(admins_sid, 0x1)\n      sd.dacl.allow(system_sid, 0x1, Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE)\n      sd.group = new_sid\n\n      aces = sd.dacl.to_a\n      expect(aces.size).to eq(2)\n      expect(aces[0].sid).to eq(new_sid)\n    end\n\n    it \"does not prepend SYSTEM when security descriptor group wasn't SYSTEM\" do\n      sd = Puppet::Util::Windows::SecurityDescriptor.new(group_sid, group_sid, empty_dacl)\n      sd.dacl.allow(group_sid, 0x1)\n      sd.group = new_sid\n\n      aces = sd.dacl.to_a\n      expect(aces.size).to eq(1)\n      expect(aces[0].sid).to eq(new_sid)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/service_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"Puppet::Util::Windows::Service\", :if => Puppet.features.microsoft_windows? do\n  require 'puppet/util/windows'\n\n  before(:each) do\n    allow(Puppet::Util::Windows::Error).to receive(:format_error_code)\n      .with(anything)\n      .and_return(\"fake error!\")\n  end\n\n  def service_state_str(state)\n    Puppet::Util::Windows::Service::SERVICE_STATES[state].to_s\n  end\n\n  # The following should emulate a successful call to the private function\n  # query_status that returns the value of query_return. This should give\n  # us a way to mock changes in service status.\n  #\n  # Everything else is stubbed, the emulation of the successful call is really\n  # just an expectation of subject::SERVICE_STATUS_PROCESS.new in sequence that\n  # returns the value passed in as a param\n  def expect_successful_status_query_and_return(query_return)\n    expect(subject::SERVICE_STATUS_PROCESS).to receive(:new).and_return(query_return)\n  end\n\n  def expect_successful_status_queries_and_return(*query_returns)\n    query_returns.each do |query_return|\n      expect_successful_status_query_and_return(query_return)\n    end\n  end\n\n  # The following should emulate a successful call to the private function\n  # query_config that returns the value of query_return. This should give\n  # us a way to mock changes in service configuration.\n  #\n  # Everything else is stubbed, the emulation of the successful call is really\n  # just an expectation of subject::QUERY_SERVICE_CONFIGW.new in sequence that\n  # returns the value passed in as a param\n  def expect_successful_config_query_and_return(query_return)\n    expect(subject::QUERY_SERVICE_CONFIGW).to receive(:new).and_return(query_return)\n  end\n\n  def expect_successful_config_query2_and_return(param, query_return)\n    expect(param).to receive(:new).and_return(query_return)\n  end\n\n  let(:subject)      { Puppet::Util::Windows::Service }\n  let(:pointer) { double() }\n  let(:mock_service_name) { double() }\n  let(:service) { double() }\n  let(:scm) { double() }\n  let(:timeout) { 30 }\n\n  before do\n    allow(subject).to receive(:QueryServiceStatusEx).and_return(1)\n    allow(subject).to receive(:QueryServiceConfigW).and_return(1)\n    allow(subject).to receive(:QueryServiceConfig2W).and_return(1)\n    allow(subject).to receive(:ChangeServiceConfigW).and_return(1)\n    allow(subject).to receive(:ChangeServiceConfig2W).and_return(1)\n    allow(subject).to receive(:OpenSCManagerW).and_return(scm)\n    allow(subject).to receive(:OpenServiceW).and_return(service)\n    allow(subject).to receive(:CloseServiceHandle)\n    allow(subject).to receive(:EnumServicesStatusExW).and_return(1)\n    allow(subject).to receive(:wide_string)\n    allow(subject::SERVICE_STATUS_PROCESS).to receive(:new)\n    allow(subject::QUERY_SERVICE_CONFIGW).to receive(:new)\n    allow(subject::SERVICE_STATUS).to receive(:new).and_return({:dwCurrentState => subject::SERVICE_RUNNING})\n    allow(FFI).to receive(:errno).and_return(0)\n    allow(FFI::MemoryPointer).to receive(:new).and_yield(pointer)\n    allow(pointer).to receive(:read_dword)\n    allow(pointer).to receive(:write_dword)\n    allow(pointer).to receive(:size)\n    allow(subject).to receive(:sleep)\n  end\n\n  describe \"#exists?\" do\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.exists?(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n\n      it \"returns false if it fails to open because the service does not exist\" do\n        allow(FFI).to receive(:errno).and_return(Puppet::Util::Windows::Service::ERROR_SERVICE_DOES_NOT_EXIST)\n\n        expect(subject.exists?(mock_service_name)).to be false\n      end\n\n      it \"raises a puppet error if it fails to open for some other reason\" do\n        expect{ subject.exists?(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      it \"returns true\" do\n        expect(subject.exists?(mock_service_name)).to be true\n      end\n    end\n  end\n\n  # This shared example contains the unit tests for the wait_on_pending_state\n  # helper as used by service actions like #start and #stop. Before including\n  # this shared example, be sure to mock out any intermediate calls prior to\n  # the pending transition, and make sure that the post-condition _after_ those\n  # intermediate calls leaves the service in the pending state. Before including\n  # this example in your tests, be sure to define the following variables in a `let`\n  # context:\n  #     * action -- The service action\n  shared_examples \"a service action waiting on a pending transition\" do |pending_state|\n    pending_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[pending_state].to_s\n\n    final_state = Puppet::Util::Windows::Service::FINAL_STATES[pending_state]\n    final_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[final_state].to_s\n\n    it \"raises a Puppet::Error if the service query fails\" do\n      expect(subject).to receive(:QueryServiceStatusEx).and_return(FFI::WIN32_FALSE)\n\n      expect { subject.send(action, mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n    end\n\n    it \"raises a Puppet::Error if the service unexpectedly transitions to a state other than #{pending_state_str} or #{final_state_str}\" do\n      invalid_state = (subject::SERVICE_STATES.keys - [pending_state, final_state]).first\n\n      expect_successful_status_query_and_return(dwCurrentState: invalid_state)\n\n      expect { subject.send(action, mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n    end\n\n    it \"waits for at least 1 second if the wait_hint/10 is < 1 second\" do\n      expect_successful_status_queries_and_return(\n        { :dwCurrentState => pending_state, :dwWaitHint => 0, :dwCheckPoint => 1 },\n        { :dwCurrentState => final_state }\n      )\n\n      expect(subject).to receive(:sleep).with(1)\n\n      subject.send(action, mock_service_name, timeout: timeout)\n    end\n\n    it \"waits for at most 10 seconds if wait_hint/10 is > 10 seconds\" do\n      expect_successful_status_queries_and_return(\n        { :dwCurrentState => pending_state, :dwWaitHint => 1000000, :dwCheckPoint => 1 },\n        { :dwCurrentState => final_state }\n      )\n\n      expect(subject).to receive(:sleep).with(10)\n\n      subject.send(action, mock_service_name, timeout: timeout)\n    end\n\n    it \"does not raise an error if the service makes any progress while transitioning to #{final_state_str}\" do\n      expect_successful_status_queries_and_return(\n        # The three \"pending_state\" statuses simulate the scenario where the service\n        # makes some progress during the transition right when Puppet's about to\n        # time out.\n        { :dwCurrentState => pending_state, :dwWaitHint => 100000, :dwCheckPoint => 1 },\n        { :dwCurrentState => pending_state, :dwWaitHint => 100000, :dwCheckPoint => 1 },\n        { :dwCurrentState => pending_state, :dwWaitHint => 100000, :dwCheckPoint => 2 },\n\n        { :dwCurrentState => final_state }\n      )\n\n      expect { subject.send(action, mock_service_name, timeout: timeout) }.to_not raise_error\n    end\n\n    it \"raises a Puppet::Error if it times out while waiting for the transition to #{final_state_str}\" do\n      31.times do\n        expect_successful_status_query_and_return(\n          dwCurrentState: pending_state,\n          dwWaitHint: 10000,\n          dwCheckPoint: 1\n        )\n      end\n\n      expect { subject.send(action, mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n    end\n  end\n\n  # This shared example contains the unit tests for the transition_service_state\n  # helper, which is the helper that all of our service actions like #start, #stop\n  # delegate to. Including these tests under a shared example lets us include them in each of\n  # those service action's unit tests. Before including this example in your tests, be\n  # sure to define the following variables in a `let` context:\n  #     * initial_state         -- The initial state of the service prior to performing the state\n  #                                transition\n  #\n  #     * mock_state_transition -- A lambda that mocks the state transition. This should mock\n  #                                any code in the block that's passed to the\n  #                                transition_service_state helper\n  #\n  # See the unit tests for the #start method to see how this shared example's\n  # included.\n  #\n  shared_examples \"a service action that transitions the service state\" do |action, valid_initial_states, pending_state, final_state|\n    valid_initial_states_str = valid_initial_states.map do |state|\n      Puppet::Util::Windows::Service::SERVICE_STATES[state]\n    end.join(', ')\n    pending_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[pending_state].to_s\n    final_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[final_state].to_s\n\n    it \"noops if the service is already in the #{final_state} state\" do\n      expect_successful_status_query_and_return(dwCurrentState: final_state)\n\n      expect { subject.send(action, mock_service_name, timeout: timeout) }.to_not raise_error\n    end\n\n    # invalid_initial_states will be empty for the #stop action\n    invalid_initial_states = Puppet::Util::Windows::Service::SERVICE_STATES.keys - valid_initial_states - [final_state]\n    unless invalid_initial_states.empty?\n      it \"raises a Puppet::Error if the service's initial state is not one of #{valid_initial_states_str}\" do\n        invalid_initial_state = invalid_initial_states.first\n        expect_successful_status_query_and_return(dwCurrentState: invalid_initial_state)\n\n        expect{ subject.send(action, mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when there's a pending transition to the #{final_state} state\" do\n      before(:each) do\n        expect_successful_status_query_and_return(dwCurrentState: pending_state)\n      end\n\n      include_examples \"a service action waiting on a pending transition\", pending_state do\n        let(:action) { action }\n      end\n    end\n\n    # If the service action accepts an unsafe pending state as one of the service's\n    # initial states, then we need to test that the action waits for the service to\n    # transition from that unsafe pending state before doing anything else.\n    unsafe_pending_states = valid_initial_states & Puppet::Util::Windows::Service::UNSAFE_PENDING_STATES\n    unless unsafe_pending_states.empty?\n      unsafe_pending_state = unsafe_pending_states.first\n      unsafe_pending_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[unsafe_pending_state]\n\n      context \"waiting for a service with #{unsafe_pending_state_str} as its initial state\" do\n        before(:each) do\n          # This mocks the status query to return the 'final_state' by default. Otherwise,\n          # we will fail the tests in the latter parts of the code where we wait for the\n          # service to finish transitioning to the 'final_state'.\n          allow(subject::SERVICE_STATUS_PROCESS).to receive(:new).and_return(dwCurrentState: final_state)\n\n          # Set our service's initial state\n          expect_successful_status_query_and_return(dwCurrentState: unsafe_pending_state)\n\n          mock_state_transition.call\n        end\n\n        include_examples \"a service action waiting on a pending transition\", unsafe_pending_state do\n          let(:action) { action }\n        end\n      end\n    end\n\n    # reads e.g. \"waiting for the service to transition to the SERVICE_RUNNING state after executing the 'start' action\"\n    #\n    # NOTE: This is really unit testing the wait_on_state_transition helper\n    context \"waiting for the service to transition to the #{final_state_str} state after executing the '#{action}' action\" do\n      before(:each) do\n        # Set our service's initial state prior to performing the state transition\n        expect_successful_status_query_and_return(dwCurrentState: initial_state)\n\n        mock_state_transition.call\n      end\n\n      it \"raises a Puppet::Error if the service query fails\" do\n        expect(subject).to receive(:QueryServiceStatusEx).and_return(FFI::WIN32_FALSE)\n\n        expect { subject.send(action, mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n\n      it \"waits, then queries again until it transitions to #{final_state_str}\" do\n        expect_successful_status_queries_and_return(\n          { :dwCurrentState => initial_state },\n          { :dwCurrentState => initial_state },\n          { :dwCurrentState => final_state }\n        )\n\n        expect(subject).to receive(:sleep).with(1).twice\n\n        subject.send(action, mock_service_name, timeout: timeout)\n      end\n\n      context \"when it transitions to the #{pending_state_str} state\" do\n        before(:each) do\n          expect_successful_status_query_and_return(dwCurrentState: pending_state)\n        end\n\n        include_examples \"a service action waiting on a pending transition\", pending_state do\n          let(:action) { action }\n        end\n      end\n\n      it \"raises a Puppet::Error if it times out while waiting for the transition to #{final_state_str}\" do\n        31.times do\n          expect_successful_status_query_and_return(dwCurrentState: initial_state)\n        end\n\n        expect { subject.send(action, mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n  end\n\n  describe \"#start\" do\n    # rspec will still try to load the tests even though\n    # the :if => Puppet.features.microsoft_windows? filter\n    # is passed-in to the top-level describe block on\n    # non-Windows platforms; it just won't run them. However\n    # on these platforms, the loading will fail because this\n    # test uses a shared example that references variables\n    # from the Windows::Service module when building the unit\n    # tests, which is only available on Windows platforms.\n    # Thus, we add the next here to ensure that rspec does not\n    # attempt to load our test code. This is OK for us to do\n    # because we do not want to run these tests on non-Windows\n    # platforms.\n    next unless Puppet.features.microsoft_windows?\n\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      # Can't use rspec's subject here because that\n      # can only be referenced inside an 'it' block.\n      service = Puppet::Util::Windows::Service\n      valid_initial_states = [\n        service::SERVICE_STOP_PENDING,\n        service::SERVICE_STOPPED,\n        service::SERVICE_START_PENDING\n      ]\n      final_state = service::SERVICE_RUNNING\n\n      include_examples \"a service action that transitions the service state\", :start, valid_initial_states, service::SERVICE_START_PENDING, final_state do\n        let(:initial_state) { subject::SERVICE_STOPPED }\n        let(:mock_state_transition) do\n          lambda do\n            allow(subject).to receive(:StartServiceW).and_return(1)\n          end\n        end\n      end\n\n      it \"raises a Puppet::Error if StartServiceW returns false\" do\n        expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_STOPPED)\n\n        expect(subject).to receive(:StartServiceW).and_return(FFI::WIN32_FALSE)\n\n        expect { subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n\n      it \"starts the service\" do\n        expect_successful_status_queries_and_return(\n          { dwCurrentState: subject::SERVICE_STOPPED },\n          { dwCurrentState: subject::SERVICE_RUNNING }\n        )\n\n        expect(subject).to receive(:StartServiceW).and_return(1)\n\n        subject.start(mock_service_name, timeout: timeout)\n      end\n    end\n  end\n\n  describe \"#stop\" do\n    next unless Puppet.features.microsoft_windows?\n\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      service = Puppet::Util::Windows::Service\n      valid_initial_states = service::SERVICE_STATES.keys - [service::SERVICE_STOPPED]\n      final_state = service::SERVICE_STOPPED\n\n      include_examples \"a service action that transitions the service state\", :stop, valid_initial_states, service::SERVICE_STOP_PENDING, final_state do\n        let(:initial_state) { subject::SERVICE_RUNNING }\n        let(:mock_state_transition) do\n          lambda do\n            allow(subject).to receive(:ControlService).and_return(1)\n          end\n        end\n      end\n\n      it \"raises a Puppet::Error if ControlService returns false\" do\n        expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_RUNNING)\n\n        allow(subject).to receive(:ControlService).and_return(FFI::WIN32_FALSE)\n\n        expect { subject.stop(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n\n      it \"stops the service\" do\n        expect_successful_status_queries_and_return(\n          { dwCurrentState: subject::SERVICE_RUNNING },\n          { dwCurrentState: subject::SERVICE_STOPPED }\n        )\n\n        expect(subject).to receive(:ControlService).and_return(1)\n\n        subject.stop(mock_service_name, timeout: timeout)\n      end\n    end\n  end\n\n  describe \"#resume\" do\n    next unless Puppet.features.microsoft_windows?\n\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.start(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      service = Puppet::Util::Windows::Service\n      valid_initial_states = [\n        service::SERVICE_PAUSE_PENDING,\n        service::SERVICE_PAUSED,\n        service::SERVICE_CONTINUE_PENDING\n      ]\n      final_state = service::SERVICE_RUNNING\n\n      include_examples \"a service action that transitions the service state\", :resume, valid_initial_states, service::SERVICE_CONTINUE_PENDING, final_state do\n        let(:initial_state) { service::SERVICE_PAUSED }\n        let(:mock_state_transition) do\n          lambda do\n            # We need to mock the status query because in the block for #resume, we\n            # wait for the service to enter the SERVICE_PAUSED state prior to\n            # performing the transition (in case it is in SERVICE_PAUSE_PENDING).\n            expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSED)\n\n            allow(subject).to receive(:ControlService).and_return(1)\n          end\n        end\n      end\n\n      context \"waiting for the SERVICE_PAUSE_PENDING => SERVICE_PAUSED transition to finish before resuming it\" do\n        before(:each) do\n          # This mocks the status query to return the SERVICE_RUNNING state by default.\n          # Otherwise, we will fail the tests in the latter parts of the code where we\n          # wait for the service to finish transitioning to the 'SERVICE_RUNNING' state.\n          allow(subject::SERVICE_STATUS_PROCESS).to receive(:new).and_return(dwCurrentState: subject::SERVICE_RUNNING)\n\n          expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSE_PENDING)\n\n          allow(subject).to receive(:ControlService).and_return(1)\n        end\n\n        include_examples \"a service action waiting on a pending transition\", service::SERVICE_PAUSE_PENDING do\n          let(:action) { :resume }\n        end\n      end\n\n      it \"raises a Puppet::Error if ControlService returns false\" do\n        expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSED)\n        expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSED)\n\n        allow(subject).to receive(:ControlService).and_return(FFI::WIN32_FALSE)\n\n        expect { subject.resume(mock_service_name, timeout: timeout) }.to raise_error(Puppet::Error)\n      end\n\n      it \"resumes the service\" do\n        expect_successful_status_queries_and_return(\n          { dwCurrentState: subject::SERVICE_PAUSED },\n          { dwCurrentState: subject::SERVICE_PAUSED },\n          { dwCurrentState: subject::SERVICE_RUNNING }\n        )\n\n        expect(subject).to receive(:ControlService).and_return(1)\n\n        subject.resume(mock_service_name, timeout: timeout)\n      end\n    end\n  end\n\n  describe \"#service_state\" do\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.service_state(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.service_state(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      it \"raises Puppet::Error if the result of the query is empty\" do\n        expect_successful_status_query_and_return({})\n        expect{subject.service_state(mock_service_name)}.to raise_error(Puppet::Error)\n      end\n\n      it \"raises Puppet::Error if the result of the query is an unknown state\" do\n        expect_successful_status_query_and_return({:dwCurrentState => 999})\n        expect{subject.service_state(mock_service_name)}.to raise_error(Puppet::Error)\n      end\n\n      # We need to guard this section explicitly since rspec will always\n      # construct all examples, even if it isn't going to run them.\n      if Puppet.features.microsoft_windows?\n        {\n          :SERVICE_STOPPED => Puppet::Util::Windows::Service::SERVICE_STOPPED,\n          :SERVICE_PAUSED => Puppet::Util::Windows::Service::SERVICE_PAUSED,\n          :SERVICE_STOP_PENDING => Puppet::Util::Windows::Service::SERVICE_STOP_PENDING,\n          :SERVICE_PAUSE_PENDING => Puppet::Util::Windows::Service::SERVICE_PAUSE_PENDING,\n          :SERVICE_RUNNING => Puppet::Util::Windows::Service::SERVICE_RUNNING,\n          :SERVICE_CONTINUE_PENDING => Puppet::Util::Windows::Service::SERVICE_CONTINUE_PENDING,\n          :SERVICE_START_PENDING => Puppet::Util::Windows::Service::SERVICE_START_PENDING,\n        }.each do |state_name, state|\n          it \"queries the service and returns #{state_name}\" do\n            expect_successful_status_query_and_return({:dwCurrentState => state})\n            expect(subject.service_state(mock_service_name)).to eq(state_name)\n          end\n        end\n      end\n    end\n  end\n\n  describe \"#service_start_type\" do\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.service_start_type(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.service_start_type(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      # We need to guard this section explicitly since rspec will always\n      # construct all examples, even if it isn't going to run them.\n      if Puppet.features.microsoft_windows?\n        {\n          :SERVICE_AUTO_START => Puppet::Util::Windows::Service::SERVICE_AUTO_START,\n          :SERVICE_BOOT_START => Puppet::Util::Windows::Service::SERVICE_BOOT_START,\n          :SERVICE_SYSTEM_START => Puppet::Util::Windows::Service::SERVICE_SYSTEM_START,\n          :SERVICE_DEMAND_START => Puppet::Util::Windows::Service::SERVICE_DEMAND_START,\n          :SERVICE_DISABLED => Puppet::Util::Windows::Service::SERVICE_DISABLED,\n        }.each do |start_type_name, start_type|\n          it \"queries the service and returns the service start type #{start_type_name}\" do\n            expect_successful_config_query_and_return({:dwStartType => start_type})\n            if start_type_name == :SERVICE_AUTO_START\n              expect_successful_config_query2_and_return(subject::SERVICE_DELAYED_AUTO_START_INFO, {:fDelayedAutostart => 0})\n            end\n            expect(subject.service_start_type(mock_service_name)).to eq(start_type_name)\n          end\n        end\n      end\n\n      it \"raises a puppet error if the service query fails\" do\n        expect(subject).to receive(:QueryServiceConfigW)\n        expect(subject).to receive(:QueryServiceConfigW).and_return(FFI::WIN32_FALSE)\n        expect{ subject.service_start_type(mock_service_name) }.to raise_error(Puppet::Error)\n      end\n    end\n  end\n\n  describe \"#set_startup_configuration\" do\n    let(:status_checks) { sequence('status_checks') }\n\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.set_startup_configuration(mock_service_name, options: {startup_type: :SERVICE_DEMAND_START}) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service cannot be opened\" do\n      let(:service) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.set_startup_configuration(mock_service_name, options: {startup_type: :SERVICE_DEMAND_START}) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service can be opened\" do\n      it \"Raises an error on an unsuccessful change\" do\n        expect(subject).to receive(:ChangeServiceConfigW).and_return(FFI::WIN32_FALSE)\n        expect{ subject.set_startup_configuration(mock_service_name, options: {startup_type: :SERVICE_DEMAND_START}) }.to raise_error(Puppet::Error)\n      end\n    end\n  end\n\n  describe \"#services\" do\n    let(:pointer_sequence) { sequence('pointer_sequence') }\n\n    context \"when the service control manager cannot be opened\" do\n      let(:scm) { FFI::Pointer::NULL_HANDLE }\n      it \"raises a puppet error\" do\n        expect{ subject.services }.to raise_error(Puppet::Error)\n      end\n    end\n\n    context \"when the service control manager is open\" do\n      let(:cursor) { [ 'svc1', 'svc2', 'svc3' ] }\n      let(:svc1name_ptr) { double() }\n      let(:svc2name_ptr) { double() }\n      let(:svc3name_ptr) { double() }\n      let(:svc1displayname_ptr) { double() }\n      let(:svc2displayname_ptr) { double() }\n      let(:svc3displayname_ptr) { double() }\n      let(:svc1) { { :lpServiceName => svc1name_ptr, :lpDisplayName => svc1displayname_ptr, :ServiceStatusProcess => 'foo' } }\n      let(:svc2) { { :lpServiceName => svc2name_ptr, :lpDisplayName => svc2displayname_ptr, :ServiceStatusProcess => 'foo' } }\n      let(:svc3) { { :lpServiceName => svc3name_ptr, :lpDisplayName => svc3displayname_ptr, :ServiceStatusProcess => 'foo' } }\n\n      it \"Raises an error if EnumServicesStatusExW fails\" do\n        expect(subject).to receive(:EnumServicesStatusExW)\n        expect(subject).to receive(:EnumServicesStatusExW).and_return(FFI::WIN32_FALSE)\n        expect{ subject.services }.to raise_error(Puppet::Error)\n      end\n\n      it \"Reads the buffer using pointer arithmetic to create a hash of service entries\" do\n        # the first read_dword is for reading the bytes required, let that return 3 too.\n        # the second read_dword will actually read the number of services returned\n        expect(pointer).to receive(:read_dword).twice.and_return(3)\n        expect(FFI::Pointer).to receive(:new).with(subject::ENUM_SERVICE_STATUS_PROCESSW, pointer).and_return(cursor)\n        expect(subject::ENUM_SERVICE_STATUS_PROCESSW).to receive(:new).with('svc1').and_return(svc1)\n        expect(subject::ENUM_SERVICE_STATUS_PROCESSW).to receive(:new).with('svc2').and_return(svc2)\n        expect(subject::ENUM_SERVICE_STATUS_PROCESSW).to receive(:new).with('svc3').and_return(svc3)\n        expect(svc1name_ptr).to receive(:read_arbitrary_wide_string_up_to).and_return('svc1')\n        expect(svc2name_ptr).to receive(:read_arbitrary_wide_string_up_to).and_return('svc2')\n        expect(svc3name_ptr).to receive(:read_arbitrary_wide_string_up_to).and_return('svc3')\n        expect(svc1displayname_ptr).to receive(:read_arbitrary_wide_string_up_to).and_return('service 1')\n        expect(svc2displayname_ptr).to receive(:read_arbitrary_wide_string_up_to).and_return('service 2')\n        expect(svc3displayname_ptr).to receive(:read_arbitrary_wide_string_up_to).and_return('service 3')\n        expect(subject.services).to eq({\n          'svc1' => { :display_name => 'service 1', :service_status_process => 'foo' },\n          'svc2' => { :display_name => 'service 2', :service_status_process => 'foo' },\n          'svc3' => { :display_name => 'service 3', :service_status_process => 'foo' }\n        })\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/sid_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"Puppet::Util::Windows::SID\", :if => Puppet::Util::Platform.windows? do\n  if Puppet::Util::Platform.windows?\n    require 'puppet/util/windows'\n  end\n\n  let(:subject)      { Puppet::Util::Windows::SID }\n  let(:sid)          { Puppet::Util::Windows::SID::LocalSystem }\n  let(:invalid_sid)  { 'bogus' }\n  let(:unknown_sid)  { 'S-0-0-0' }\n  let(:null_sid)     { 'S-1-0-0' }\n  let(:unknown_name) { 'chewbacca' }\n\n  context \"#octet_string_to_principal\" do\n    it \"should properly convert an array of bytes for a well-known non-localized SID\" do\n      bytes = [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n      converted = subject.octet_string_to_principal(bytes)\n\n      expect(converted).to be_an_instance_of Puppet::Util::Windows::SID::Principal\n      expect(converted.sid_bytes).to eq(bytes)\n      expect(converted.sid).to eq(null_sid)\n\n      # carefully select a SID here that is not localized on international Windows\n      expect(converted.account).to eq('NULL SID')\n    end\n\n    it \"should raise an error for non-array input\" do\n      expect {\n        subject.octet_string_to_principal(invalid_sid)\n      }.to raise_error(Puppet::Error, /Octet string must be an array of bytes/)\n    end\n\n    it \"should raise an error for an empty byte array\" do\n      expect {\n        subject.octet_string_to_principal([])\n      }.to raise_error(Puppet::Error, /Octet string must be an array of bytes/)\n    end\n\n    it \"should raise an error for a valid byte array with no mapping to a user\" do\n      expect {\n        # S-1-1-1 which is not a valid account\n        valid_octet_invalid_user =[1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0]\n        subject.octet_string_to_principal(valid_octet_invalid_user)\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(1332) # ERROR_NONE_MAPPED\n      end\n    end\n\n    it \"should raise an error for a malformed byte array\" do\n      expect {\n        invalid_octet = [2]\n        subject.octet_string_to_principal(invalid_octet)\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(87) # ERROR_INVALID_PARAMETER\n      end\n    end\n  end\n\n  context \"#name_to_sid\" do\n    it \"should return nil if the account does not exist\" do\n      expect(subject.name_to_sid(unknown_name)).to be_nil\n    end\n\n    it \"should accept unqualified account name\" do\n      # NOTE: lookup by name works in localized environments only for a few instances\n      # this works in French Windows, even though the account is really Syst\\u00E8me\n      expect(subject.name_to_sid('SYSTEM')).to eq(sid)\n    end\n\n    it \"should return a SID for a passed user or group name\" do\n      expect(subject).to receive(:name_to_principal).with('testers').and_return(double(:sid => 'S-1-5-32-547'))\n      expect(subject.name_to_sid('testers')).to eq('S-1-5-32-547')\n    end\n\n    it \"should return a SID for a passed fully-qualified user or group name\" do\n      expect(subject).to receive(:name_to_principal).with('MACHINE\\testers').and_return(double(:sid => 'S-1-5-32-547'))\n      expect(subject.name_to_sid('MACHINE\\testers')).to eq('S-1-5-32-547')\n    end\n\n    it \"should be case-insensitive\" do\n      expect(subject.name_to_sid('SYSTEM')).to eq(subject.name_to_sid('system'))\n    end\n\n    it \"should be leading and trailing whitespace-insensitive\" do\n      expect(subject.name_to_sid('SYSTEM')).to eq(subject.name_to_sid(' SYSTEM '))\n    end\n\n    it \"should accept domain qualified account names\" do\n      # NOTE: lookup by name works in localized environments only for a few instances\n      # this works in French Windows, even though the account is really AUTORITE NT\\\\Syst\\u00E8me\n      expect(subject.name_to_sid('NT AUTHORITY\\SYSTEM')).to eq(sid)\n    end\n\n    it \"should be the identity function for any sid\" do\n      expect(subject.name_to_sid(sid)).to eq(sid)\n    end\n\n    describe \"with non-US languages\" do\n      UMLAUT = [195, 164].pack('c*').force_encoding(Encoding::UTF_8)\n      let(:username) { SecureRandom.uuid.to_s.gsub(/\\-/, '')[0..13] + UMLAUT }\n\n      after(:each) {\n        Puppet::Util::Windows::ADSI::User.delete(username)\n      }\n\n      it \"should properly resolve a username with an umlaut\" do\n        # Ruby seems to use the local codepage when making COM calls\n        # if this fails, might want to use Windows API directly instead to ensure bytes\n        user = Puppet::Util::Windows::ADSI.create(username, 'user')\n        user.SetPassword('PUPPET_RULeZ_123!')\n        user.SetInfo()\n\n        # compare the new SID to the name_to_sid result\n        sid_bytes = user.objectSID.to_a\n        sid_string = ''\n        FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_byte_ptr|\n          sid_byte_ptr.write_array_of_uchar(sid_bytes)\n          sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_byte_ptr)\n        end\n\n        expect(subject.name_to_sid(username)).to eq(sid_string)\n      end\n    end\n  end\n\n  context \"#name_to_principal\" do\n    it \"should return nil if the account does not exist\" do\n      expect(subject.name_to_principal(unknown_name)).to be_nil\n    end\n\n    it \"should print a debug message if the account does not exist\" do\n      expect(Puppet).to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal(unknown_name)\n    end\n\n    it \"should return a Puppet::Util::Windows::SID::Principal instance for any valid sid\" do\n      expect(subject.name_to_principal(sid)).to be_an_instance_of(Puppet::Util::Windows::SID::Principal)\n    end\n\n    it \"should not print debug messages for valid sid\" do\n      expect(Puppet).not_to receive(:debug).with(/Could not retrieve raw SID bytes from/)\n      expect(Puppet).not_to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal(sid)\n    end\n\n    it \"should print a debug message for invalid sid\" do\n      expect(Puppet).not_to receive(:debug).with(/Could not retrieve raw SID bytes from/)\n      expect(Puppet).to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal('S-1-5-21-INVALID-SID')\n    end\n\n    it \"should accept unqualified account name\" do\n      # NOTE: lookup by name works in localized environments only for a few instances\n      # this works in French Windows, even though the account is really Syst\\u00E8me\n      expect(subject.name_to_principal('SYSTEM').sid).to eq(sid)\n    end\n\n    it \"should not print debug messages for unqualified account name\" do\n      expect(Puppet).not_to receive(:debug).with(/Could not retrieve raw SID bytes from/)\n      expect(Puppet).not_to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal('SYSTEM')\n    end\n\n    it \"should be case-insensitive\" do\n      # NOTE: lookup by name works in localized environments only for a few instances\n      # this works in French Windows, even though the account is really Syst\\u00E8me\n      expect(subject.name_to_principal('SYSTEM')).to eq(subject.name_to_principal('system'))\n    end\n\n    it \"should not print debug messages for wrongly cased account name\" do\n      expect(Puppet).not_to receive(:debug).with(/Could not retrieve raw SID bytes from/)\n      expect(Puppet).not_to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal('system')\n    end\n\n    it \"should be leading and trailing whitespace-insensitive\" do\n      # NOTE: lookup by name works in localized environments only for a few instances\n      # this works in French Windows, even though the account is really Syst\\u00E8me\n      expect(subject.name_to_principal('SYSTEM')).to eq(subject.name_to_principal(' SYSTEM '))\n    end\n\n    it \"should not print debug messages for account name with leading and trailing whitespace\" do\n      expect(Puppet).not_to receive(:debug).with(/Could not retrieve raw SID bytes from/)\n      expect(Puppet).not_to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal(' SYSTEM ')\n    end\n\n    it \"should accept domain qualified account names\" do\n      # NOTE: lookup by name works in localized environments only for a few instances\n      # this works in French Windows, even though the account is really AUTORITE NT\\\\Syst\\u00E8me\n      expect(subject.name_to_principal('NT AUTHORITY\\SYSTEM').sid).to eq(sid)\n    end\n\n    it \"should not print debug messages for domain qualified account names\" do\n      expect(Puppet).not_to receive(:debug).with(/Could not retrieve raw SID bytes from/)\n      expect(Puppet).not_to receive(:debug).with(/No mapping between account names and security IDs was done/)\n      subject.name_to_principal('NT AUTHORITY\\SYSTEM')\n    end\n  end\n\n  context \"#ads_to_principal\" do\n    it \"should raise an error for non-WIN32OLE input\" do\n      expect {\n        subject.ads_to_principal(double('WIN32OLE', { :Name => 'foo' }))\n      }.to raise_error(Puppet::Error, /ads_object must be an IAdsUser or IAdsGroup instance/)\n    end\n\n    it \"should raise an error for an empty byte array in the objectSID property\" do\n      expect {\n        subject.ads_to_principal(double('WIN32OLE', { :objectSID => [], :Name => '', :ole_respond_to? => true }))\n      }.to raise_error(Puppet::Error, /Octet string must be an array of bytes/)\n    end\n\n    it \"should raise an error for a malformed byte array\" do\n      expect {\n        invalid_octet = [2]\n        subject.ads_to_principal(double('WIN32OLE', { :objectSID => invalid_octet, :Name => '', :ole_respond_to? => true }))\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Util::Windows::Error)\n        expect(error.code).to eq(87) # ERROR_INVALID_PARAMETER\n      end\n    end\n\n    it \"should raise an error when a valid byte array for SID is unresolvable and its Name does not match\" do\n      expect {\n        # S-1-1-1 is a valid SID that will not resolve\n        valid_octet_invalid_user = [1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0]\n        subject.ads_to_principal(double('WIN32OLE', { :objectSID => valid_octet_invalid_user, :Name => unknown_name, :ole_respond_to? => true }))\n      }.to raise_error do |error|\n        expect(error).to be_a(Puppet::Error)\n        expect(error.cause.code).to eq(1332) # ERROR_NONE_MAPPED\n      end\n    end\n\n    it \"should return a Principal object even when the SID is unresolvable, as long as the Name matches\" do\n      # S-1-1-1 is a valid SID that will not resolve\n      valid_octet_invalid_user = [1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0]\n      unresolvable_user = double('WIN32OLE', { :objectSID => valid_octet_invalid_user, :Name => 'S-1-1-1', :ole_respond_to? => true })\n      principal = subject.ads_to_principal(unresolvable_user)\n\n      expect(principal).to be_an_instance_of(Puppet::Util::Windows::SID::Principal)\n      expect(principal.account).to eq('S-1-1-1')\n      expect(principal.domain).to eq(nil)\n      expect(principal.domain_account).to eq('S-1-1-1')\n      expect(principal.sid).to eq('S-1-1-1')\n      expect(principal.sid_bytes).to eq(valid_octet_invalid_user)\n      expect(principal.account_type).to eq(:SidTypeUnknown)\n    end\n\n    it \"should return a Puppet::Util::Windows::SID::Principal instance for any valid sid\" do\n      system_bytes = [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0]\n      adsuser = double('WIN32OLE', { :objectSID => system_bytes, :Name => 'SYSTEM', :ole_respond_to? => true })\n      expect(subject.ads_to_principal(adsuser)).to be_an_instance_of(Puppet::Util::Windows::SID::Principal)\n    end\n\n    it \"should properly convert an array of bytes for a well-known non-localized SID, ignoring the Name from the WIN32OLE object\" do\n      bytes = [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n      adsuser = double('WIN32OLE', { :objectSID => bytes, :Name => unknown_name, :ole_respond_to? => true })\n      converted = subject.ads_to_principal(adsuser)\n\n      expect(converted).to be_an_instance_of Puppet::Util::Windows::SID::Principal\n      expect(converted.sid_bytes).to eq(bytes)\n      expect(converted.sid).to eq(null_sid)\n\n      # carefully select a SID here that is not localized on international Windows\n      expect(converted.account).to eq('NULL SID')\n      # garbage name supplied does not carry forward as SID is looked up again\n      expect(converted.account).to_not eq(adsuser.Name)\n    end\n  end\n\n  context \"#sid_to_name\" do\n    it \"should return nil if given a sid for an account that doesn't exist\" do\n      expect(subject.sid_to_name(unknown_sid)).to be_nil\n    end\n\n    it \"should accept a sid\" do\n      # choose a value that is not localized, for instance\n      # S-1-5-18 can be NT AUTHORITY\\\\SYSTEM or AUTORITE NT\\\\Syst\\u00E8me\n      # but NULL SID appears universal\n      expect(subject.sid_to_name(null_sid)).to eq('NULL SID')\n    end\n  end\n\n  context \"#sid_ptr_to_string\" do\n    it \"should raise if given an invalid sid\" do\n      expect {\n        subject.sid_ptr_to_string(nil)\n      }.to raise_error(Puppet::Error, /Invalid SID/)\n    end\n\n    it \"should yield a valid sid pointer\" do\n      string = nil\n      subject.string_to_sid_ptr(sid) do |ptr|\n        string = subject.sid_ptr_to_string(ptr)\n      end\n      expect(string).to eq(sid)\n    end\n  end\n\n  context \"#string_to_sid_ptr\" do\n    it \"should yield sid_ptr\" do\n      ptr = nil\n      subject.string_to_sid_ptr(sid) do |p|\n        ptr = p\n      end\n      expect(ptr).not_to be_nil\n    end\n\n    it \"should raise on an invalid sid\" do\n      expect {\n        subject.string_to_sid_ptr(invalid_sid)\n      }.to raise_error(Puppet::Error, /Failed to convert string SID/)\n    end\n  end\n\n  context \"#valid_sid?\" do\n    it \"should return true for a valid SID\" do\n      expect(subject.valid_sid?(sid)).to be_truthy\n    end\n\n    it \"should return false for an invalid SID\" do\n      expect(subject.valid_sid?(invalid_sid)).to be_falsey\n    end\n\n    it \"should raise if the conversion fails\" do\n      expect(subject).to receive(:string_to_sid_ptr).with(sid).\n        and_raise(Puppet::Util::Windows::Error.new(\"Failed to convert string SID: #{sid}\", Puppet::Util::Windows::Error::ERROR_ACCESS_DENIED))\n\n      expect {\n        subject.string_to_sid_ptr(sid) {|ptr| }\n      }.to raise_error(Puppet::Util::Windows::Error, /Failed to convert string SID: #{sid}/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows/string_spec.rb",
    "content": "# encoding: UTF-8\n\nrequire 'spec_helper'\nrequire 'puppet/util/windows'\n\ndescribe \"Puppet::Util::Windows::String\", :if => Puppet::Util::Platform.windows? do\n  def wide_string(str)\n    Puppet::Util::Windows::String.wide_string(str)\n  end\n\n  def converts_to_wide_string(string_value)\n    expected = string_value.encode(Encoding::UTF_16LE)\n    expected_bytes = expected.bytes.to_a\n\n    expect(wide_string(string_value).bytes.to_a).to eq(expected_bytes)\n  end\n\n  context \"wide_string\" do\n    it \"should return encoding of UTF-16LE\" do\n      expect(wide_string(\"bob\").encoding).to eq(Encoding::UTF_16LE)\n    end\n\n    it \"should return valid encoding\" do\n      expect(wide_string(\"bob\").valid_encoding?).to be_truthy\n    end\n\n    it \"should convert an ASCII string\" do\n      converts_to_wide_string(\"bob\".encode(Encoding::US_ASCII))\n    end\n\n    it \"should convert a UTF-8 string\" do\n      converts_to_wide_string(\"bob\".encode(Encoding::UTF_8))\n    end\n\n    it \"should convert a UTF-16LE string\" do\n      converts_to_wide_string(\"bob\\u00E8\".encode(Encoding::UTF_16LE))\n    end\n\n    it \"should convert a UTF-16BE string\" do\n      converts_to_wide_string(\"bob\\u00E8\".encode(Encoding::UTF_16BE))\n    end\n\n    it \"should convert an UTF-32LE string\" do\n      converts_to_wide_string(\"bob\\u00E8\".encode(Encoding::UTF_32LE))\n    end\n\n    it \"should convert an UTF-32BE string\" do\n      converts_to_wide_string(\"bob\\u00E8\".encode(Encoding::UTF_32BE))\n    end\n\n    it \"should return a nil when given a nil\" do\n      expect(wide_string(nil)).to eq(nil)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/windows_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Puppet::Util::Windows do\n  %w[\n    ADSI\n    ADSI::ADSIObject\n    ADSI::User\n    ADSI::UserProfile\n    ADSI::Group\n    EventLog\n    File\n    Process\n    Registry\n    Service\n    SID\n    ].each do |name|\n    it \"defines Puppet::Util::Windows::#{name}\" do\n      expect(described_class.const_get(name)).to be\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util/yaml_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/util/yaml'\n\ndescribe Puppet::Util::Yaml do\n  include PuppetSpec::Files\n\n  let(:filename) { tmpfile(\"yaml\") }\n\n  shared_examples_for 'yaml file loader' do |load_method|\n    it 'returns false when the file is empty' do\n      file_path = file_containing('input', '')\n\n      expect(load_method.call(file_path)).to eq(false)\n    end\n\n    it 'reads a YAML file from disk' do\n      file_path = file_containing('input', YAML.dump({ \"my\" => \"data\" }))\n\n      expect(load_method.call(file_path)).to eq({ \"my\" => \"data\" })\n    end\n\n    it 'reads YAML as UTF-8' do\n      file_path = file_containing('input', YAML.dump({ \"my\" => \"𠜎\" }))\n\n      expect(load_method.call(file_path)).to eq({ \"my\" => \"𠜎\" })\n    end\n  end\n\n  context \"#safe_load\" do\n    it 'raises an error if YAML is invalid' do\n      expect {\n        Puppet::Util::Yaml.safe_load('{ invalid')\n      }.to raise_error(Puppet::Util::Yaml::YamlLoadError, %r[\\(<unknown>\\): .* at line \\d+ column \\d+])\n    end\n\n    it 'raises if YAML contains classes not in the list' do\n      expect {\n        Puppet::Util::Yaml.safe_load(<<FACTS, [])\n--- !ruby/object:Puppet::Node::Facts\nname: localhost\nFACTS\n      }.to raise_error(Puppet::Util::Yaml::YamlLoadError, \"(<unknown>): Tried to load unspecified class: Puppet::Node::Facts\")\n    end\n\n    it 'includes the filename if YAML contains classes not in the list' do\n      expect {\n        Puppet::Util::Yaml.safe_load(<<FACTS, [], 'foo.yaml')\n--- !ruby/object:Puppet::Node::Facts\nname: localhost\nFACTS\n      }.to raise_error(Puppet::Util::Yaml::YamlLoadError, \"(foo.yaml): Tried to load unspecified class: Puppet::Node::Facts\")\n    end\n\n    it 'allows classes to be loaded' do\n      facts = Puppet::Util::Yaml.safe_load(<<FACTS, [Puppet::Node::Facts])\n--- !ruby/object:Puppet::Node::Facts\nname: localhost\nvalues:\n  puppetversion: 6.0.0\nFACTS\n      expect(facts.name).to eq('localhost')\n    end\n\n    it 'returns false if the content is empty' do\n      expect(Puppet::Util::Yaml.safe_load('')).to eq(false)\n    end\n\n    it 'loads true' do\n      expect(Puppet::Util::Yaml.safe_load('true')).to eq(true)\n    end\n\n    it 'loads false' do\n      expect(Puppet::Util::Yaml.safe_load('false')).to eq(false)\n    end\n\n    it 'loads nil' do\n      expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq('a' => nil)\n        ---\n        a: null\n      YAML\n    end\n\n    it 'loads a numeric' do\n      expect(Puppet::Util::Yaml.safe_load('42')).to eq(42)\n    end\n\n    it 'loads a string' do\n      expect(Puppet::Util::Yaml.safe_load('puppet')).to eq('puppet')\n    end\n\n    it 'loads an array' do\n      expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq([1, 2])\n        ---\n        - 1\n        - 2\n      YAML\n    end\n\n    it 'loads a hash' do\n      expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq('a' => 1, 'b' => 2)\n        ---\n        a: 1\n        b: 2\n      YAML\n    end\n\n    it 'loads an alias' do\n      expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq('a' => [], 'b' => [])\n        ---\n        a: &1 []\n        b: *1\n      YAML\n    end\n  end\n\n  context \"#safe_load_file\" do\n    it_should_behave_like 'yaml file loader', Puppet::Util::Yaml.method(:safe_load_file)\n\n    it 'raises an error when the file is invalid YAML' do\n      file_path = file_containing('input', '{ invalid')\n\n      expect {\n        Puppet::Util::Yaml.safe_load_file(file_path)\n      }.to raise_error(Puppet::Util::Yaml::YamlLoadError, %r[\\(#{file_path}\\): .* at line \\d+ column \\d+])\n    end\n\n    it 'raises an error when the filename is illegal' do\n      expect {\n        Puppet::Util::Yaml.safe_load_file(\"not\\0allowed\")\n      }.to raise_error(ArgumentError, /pathname contains null byte/)\n    end\n\n    it 'raises an error when the file does not exist' do\n      expect {\n        Puppet::Util::Yaml.safe_load_file('does/not/exist.yaml')\n      }.to raise_error(Errno::ENOENT, /No such file or directory/)\n    end\n  end\n\n  context \"#safe_load_file_if_valid\" do\n    before do\n      Puppet[:log_level] = 'debug'\n    end\n\n    it_should_behave_like 'yaml file loader', Puppet::Util::Yaml.method(:safe_load_file_if_valid)\n\n    it 'returns nil when the file is invalid YAML and debug logs about it' do\n      file_path = file_containing('input', '{ invalid')\n\n      expect(Puppet).to receive(:debug)\n        .with(/Could not retrieve YAML content .+ expected ',' or '}'/).and_call_original\n\n      expect(Puppet::Util::Yaml.safe_load_file_if_valid(file_path)).to eql(nil)\n    end\n\n    it 'returns nil when the filename is illegal and debug logs about it' do\n      expect(Puppet).to receive(:debug)\n        .with(/Could not retrieve YAML content .+: pathname contains null byte/).and_call_original\n\n      expect(Puppet::Util::Yaml.safe_load_file_if_valid(\"not\\0allowed\")).to eql(nil)\n    end\n\n    it 'returns nil when the file does not exist and debug logs about it' do\n      expect(Puppet).to receive(:debug)\n        .with(/Could not retrieve YAML content .+: No such file or directory/).and_call_original\n\n      expect(Puppet::Util::Yaml.safe_load_file_if_valid('does/not/exist.yaml')).to eql(nil)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/util_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Puppet::Util do\n  include PuppetSpec::Files\n\n  if Puppet::Util::Platform.windows?\n    def set_mode(mode, file)\n      Puppet::Util::Windows::Security.set_mode(mode, file)\n    end\n\n    def get_mode(file)\n      Puppet::Util::Windows::Security.get_mode(file) & 07777\n    end\n  else\n    def set_mode(mode, file)\n      File.chmod(mode, file)\n    end\n\n    def get_mode(file)\n      Puppet::FileSystem.lstat(file).mode & 07777\n    end\n  end\n\n  describe \"#withenv\" do\n    let(:mode) { Puppet::Util::Platform.windows? ? :windows : :posix }\n\n    before :each do\n      @original_path = ENV[\"PATH\"]\n      @new_env = {:PATH => \"/some/bogus/path\"}\n    end\n\n    it \"should change environment variables within the block then reset environment variables to their original values\" do\n      Puppet::Util.withenv @new_env, mode do\n        expect(ENV[\"PATH\"]).to eq(\"/some/bogus/path\")\n      end\n      expect(ENV[\"PATH\"]).to eq(@original_path)\n    end\n\n    it \"should reset environment variables to their original values even if the block fails\" do\n      begin\n        Puppet::Util.withenv @new_env, mode do\n          expect(ENV[\"PATH\"]).to eq(\"/some/bogus/path\")\n          raise \"This is a failure\"\n        end\n      rescue\n      end\n      expect(ENV[\"PATH\"]).to eq(@original_path)\n    end\n\n    it \"should reset environment variables even when they are set twice\" do\n      # Setting Path & Environment parameters in Exec type can cause weirdness\n      @new_env[\"PATH\"] = \"/someother/bogus/path\"\n      Puppet::Util.withenv @new_env, mode do\n        # When assigning duplicate keys, can't guarantee order of evaluation\n        expect(ENV[\"PATH\"]).to match(/\\/some.*\\/bogus\\/path/)\n      end\n      expect(ENV[\"PATH\"]).to eq(@original_path)\n    end\n\n    it \"should remove any new environment variables after the block ends\" do\n      @new_env[:FOO] = \"bar\"\n      ENV[\"FOO\"] = nil\n      Puppet::Util.withenv @new_env, mode do\n        expect(ENV[\"FOO\"]).to eq(\"bar\")\n      end\n      expect(ENV[\"FOO\"]).to eq(nil)\n    end\n\n    it \"accepts symbolic keys\" do\n      Puppet::Util.withenv(:FOO => \"bar\") do\n        expect(ENV[\"FOO\"]).to eq(\"bar\")\n      end\n    end\n\n    it \"coerces invalid keys to strings\" do\n      Puppet::Util.withenv(12345678 => \"bar\") do\n        expect(ENV[\"12345678\"]).to eq(\"bar\")\n      end\n    end\n\n    it \"rejects keys with leading equals\" do\n      expect {\n        Puppet::Util.withenv(\"=foo\" => \"bar\") {}\n      }.to raise_error(Errno::EINVAL, /Invalid argument/)\n    end\n\n    it \"includes keys with unicode replacement characters\" do\n      Puppet::Util.withenv(\"foo\\uFFFD\" => \"bar\") do\n        expect(ENV).to be_include(\"foo\\uFFFD\")\n      end\n    end\n\n    it \"accepts a unicode key\" do\n      key = \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\"\n\n      Puppet::Util.withenv(key => \"bar\") do\n        expect(ENV[key]).to eq(\"bar\")\n      end\n    end\n\n    it \"accepts a unicode value\" do\n      value = \"\\u16A0\\u16C7\\u16BB\\u16EB\\u16D2\\u16E6\\u16A6\\u16EB\\u16A0\\u16B1\\u16A9\\u16A0\\u16A2\\u16B1\\u16EB\\u16A0\\u16C1\\u16B1\\u16AA\\u16EB\\u16B7\\u16D6\\u16BB\\u16B9\\u16E6\\u16DA\\u16B3\\u16A2\\u16D7\"\n\n      Puppet::Util.withenv(\"runes\" => value) do\n        expect(ENV[\"runes\"]).to eq(value)\n      end\n    end\n\n    it \"rejects a non-string value\" do\n      expect {\n        Puppet::Util.withenv(\"reject\" => 123) {}\n      }.to raise_error(TypeError, /no implicit conversion of Integer into String/)\n    end\n\n    it \"accepts a nil value\" do\n      Puppet::Util.withenv(\"foo\" => nil) do\n        expect(ENV[\"foo\"]).to eq(nil)\n      end\n    end\n  end\n\n  describe \"#withenv on POSIX\", :unless => Puppet::Util::Platform.windows? do\n    it \"compares keys case sensitively\" do\n      # start with lower case key,\n      env_key = SecureRandom.uuid.downcase\n\n      begin\n        original_value = 'hello'\n        ENV[env_key] = original_value\n        new_value = 'goodbye'\n\n        Puppet::Util.withenv(env_key.upcase => new_value) do\n          expect(ENV[env_key]).to eq(original_value)\n          expect(ENV[env_key.upcase]).to eq(new_value)\n        end\n\n        expect(ENV[env_key]).to eq(original_value)\n        expect(ENV[env_key.upcase]).to be_nil\n      ensure\n        ENV.delete(env_key)\n      end\n    end\n  end\n\n  describe \"#withenv on Windows\", :if => Puppet::Util::Platform.windows? do\n    let(:process) { Puppet::Util::Windows::Process }\n\n    it \"compares keys case-insensitively\" do\n      # start with lower case key, ensuring string is not entirely numeric\n      env_key = SecureRandom.uuid.downcase + 'a'\n\n      begin\n        original_value = 'hello'\n        ENV[env_key] = original_value\n        new_value = 'goodbye'\n\n        Puppet::Util.withenv(env_key.upcase => new_value) do\n          expect(ENV[env_key]).to eq(new_value)\n          expect(ENV[env_key.upcase]).to eq(new_value)\n        end\n\n        expect(ENV[env_key]).to eq(original_value)\n        expect(ENV[env_key.upcase]).to eq(original_value)\n      ensure\n        ENV.delete(env_key)\n      end\n    end\n\n    def withenv_utf8(&block)\n      env_var_name = SecureRandom.uuid\n      utf_8_bytes = [225, 154, 160] # rune ᚠ\n\n      utf_8_key = env_var_name + utf_8_bytes.pack('c*').force_encoding(Encoding::UTF_8)\n      utf_8_value = utf_8_key + 'value'\n      codepage_key = utf_8_key.dup.force_encoding(Encoding.default_external)\n\n      Puppet::Util.withenv(utf_8_key => utf_8_value) do\n        # the true Windows environment APIs see the variables correctly\n        expect(process.get_environment_strings[utf_8_key]).to eq(utf_8_value)\n\n        # the string contain the same bytes, but have different Ruby metadata\n        expect(utf_8_key.bytes.to_a).to eq(codepage_key.bytes.to_a)\n\n        yield utf_8_key, utf_8_value, codepage_key\n      end\n\n      # real environment shouldn't have env var anymore\n      expect(process.get_environment_strings[utf_8_key]).to eq(nil)\n    end\n\n    it \"should preseve existing environment and should not corrupt UTF-8 environment variables\" do\n      env_var_name = SecureRandom.uuid\n      utf_8_bytes = [225, 154, 160] # rune ᚠ\n      utf_8_str = env_var_name + utf_8_bytes.pack('c*').force_encoding(Encoding::UTF_8)\n      env_var_name_utf_8 = utf_8_str\n\n      begin\n        # UTF-8 name and value\n        process.set_environment_variable(env_var_name_utf_8, utf_8_str)\n        # ASCII name / UTF-8 value\n        process.set_environment_variable(env_var_name, utf_8_str)\n\n        original_keys = process.get_environment_strings.keys.to_a\n        Puppet::Util.withenv({}) { }\n\n        env = process.get_environment_strings\n\n        expect(env[env_var_name]).to eq(utf_8_str)\n        expect(env[env_var_name_utf_8]).to eq(utf_8_str)\n        expect(env.keys.to_a).to eq(original_keys)\n      ensure\n        process.set_environment_variable(env_var_name_utf_8, nil)\n        process.set_environment_variable(env_var_name, nil)\n      end\n    end\n  end\n\n  describe \"#absolute_path?\" do\n    describe \"on posix systems\", :if => Puppet.features.posix? do\n      it \"should default to the platform of the local system\" do\n        expect(Puppet::Util).to be_absolute_path('/foo')\n        expect(Puppet::Util).not_to be_absolute_path('C:/foo')\n      end\n    end\n\n    describe \"on windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should default to the platform of the local system\" do\n        expect(Puppet::Util).to be_absolute_path('C:/foo')\n        expect(Puppet::Util).not_to be_absolute_path('/foo')\n      end\n    end\n\n    describe \"when using platform :posix\" do\n      %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\\Server/Foo /foo//bar/baz].each do |path|\n        it \"should return true for #{path}\" do\n          expect(Puppet::Util).to be_absolute_path(path, :posix)\n        end\n      end\n\n      %w[. ./foo \\foo C:/foo \\\\Server\\Foo\\Bar \\\\?\\C:\\foo\\bar \\/?/foo\\bar \\/Server/foo foo//bar/baz].each do |path|\n        it \"should return false for #{path}\" do\n          expect(Puppet::Util).not_to be_absolute_path(path, :posix)\n        end\n      end\n    end\n\n    describe \"when using platform :windows\" do\n      %w[C:/foo C:\\foo \\\\\\\\Server\\Foo\\Bar \\\\\\\\?\\C:\\foo\\bar //Server/Foo/Bar //?/C:/foo/bar /\\?\\C:/foo\\bar \\/Server\\Foo/Bar c:/foo//bar//baz].each do |path|\n        it \"should return true for #{path}\" do\n          expect(Puppet::Util).to be_absolute_path(path, :windows)\n        end\n      end\n\n      %w[/ . ./foo \\foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path|\n        it \"should return false for #{path}\" do\n          expect(Puppet::Util).not_to be_absolute_path(path, :windows)\n        end\n      end\n    end\n  end\n\n  describe \"#path_to_uri\" do\n    # different UTF-8 widths\n    # 1-byte A\n    # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n    # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n    # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n    let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ܎\n    let (:mixed_utf8_urlencoded) { \"A%DB%BF%E1%9A%A0%F0%A0%9C%8E\" }\n\n    %w[. .. foo foo/bar foo/../bar].each do |path|\n      it \"should reject relative path: #{path}\" do\n        expect { Puppet::Util.path_to_uri(path) }.to raise_error(Puppet::Error)\n      end\n    end\n\n    it \"should perform URI escaping\" do\n      expect(Puppet::Util.path_to_uri(\"/foo bar\").path).to eq(\"/foo%20bar\")\n    end\n\n    it \"should properly URI encode + and space in path\" do\n      expect(Puppet::Util.path_to_uri(\"/foo+foo bar\").path).to eq(\"/foo+foo%20bar\")\n    end\n\n    # reserved characters are different for each part\n    # https://web.archive.org/web/20151229061347/http://blog.lunatech.com/2009/02/03/what-every-web-developer-must-know-about-url-encoding#Thereservedcharactersaredifferentforeachpart\n    # \"?\" is allowed unescaped anywhere within a query part,\n    # \"/\" is allowed unescaped anywhere within a query part,\n    # \"=\" is allowed unescaped anywhere within a path parameter or query parameter value, and within a path segment,\n    # \":@-._~!$&'()*+,;=\" are allowed unescaped anywhere within a path segment part,\n    # \"/?:@-._~!$&'()*+,;=\" are allowed unescaped anywhere within a fragment part.\n    it \"should properly URI encode + and space in path and query\" do\n      path = \"/foo+foo bar?foo+foo bar\"\n      uri = Puppet::Util.path_to_uri(path)\n\n      expected_encoding = Encoding::UTF_8\n      expect(uri.to_s.encoding).to eq(expected_encoding)\n      expect(uri.path).to eq(\"/foo+foo%20bar\")\n      # either + or %20 is correct for an encoded space in query\n      # + is usually used for backward compatibility, but %20 is preferred for compat with Puppet::Util.uri_unescape\n      expect(uri.query).to eq(\"foo%2Bfoo%20bar\")\n      # complete roundtrip\n      expect(Puppet::Util.uri_unescape(uri.to_s).sub(%r{^file:(//)?}, '')).to eq(path)\n      expect(Puppet::Util.uri_unescape(uri.to_s).encoding).to eq(expected_encoding)\n    end\n\n    it \"should perform UTF-8 URI escaping\" do\n      uri = Puppet::Util.path_to_uri(\"/#{mixed_utf8}\")\n\n      expect(uri.path.encoding).to eq(Encoding::UTF_8)\n      expect(uri.path).to eq(\"/#{mixed_utf8_urlencoded}\")\n    end\n\n    describe \"when using platform :posix\" do\n      before :each do\n        allow(Puppet.features).to receive(:posix?).and_return(true)\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      end\n\n      %w[/ /foo /foo/../bar].each do |path|\n        it \"should convert #{path} to URI\" do\n          expect(Puppet::Util.path_to_uri(path).path).to eq(path)\n        end\n      end\n    end\n\n    describe \"when using platform :windows\" do\n      before :each do\n        allow(Puppet.features).to receive(:posix?).and_return(false)\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n      end\n\n      it \"should normalize backslashes\" do\n        expect(Puppet::Util.path_to_uri('c:\\\\foo\\\\bar\\\\baz').path).to eq('/' + 'c:/foo/bar/baz')\n      end\n\n      %w[C:/ C:/foo/bar].each do |path|\n        it \"should convert #{path} to absolute URI\" do\n          expect(Puppet::Util.path_to_uri(path).path).to eq('/' + path)\n        end\n      end\n\n      %w[share C$].each do |path|\n        it \"should convert UNC #{path} to absolute URI\" do\n          uri = Puppet::Util.path_to_uri(\"\\\\\\\\server\\\\#{path}\")\n          expect(uri.host).to eq('server')\n          expect(uri.path).to eq('/' + Puppet::Util.uri_encode(path))\n        end\n      end\n    end\n  end\n\n  describe \"#uri_query_encode\" do\n    # different UTF-8 widths\n    # 1-byte A\n    # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n    # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n    # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n    let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ𠜎\n    let (:mixed_utf8_urlencoded) { \"A%DB%BF%E1%9A%A0%F0%A0%9C%8E\" }\n\n    it \"should perform basic URI escaping that includes space and +\" do\n      expect(Puppet::Util.uri_query_encode(\"foo bar+foo\")).to eq(\"foo%20bar%2Bfoo\")\n    end\n\n    it \"should URI encode any special characters: = + <space> & * and #\" do\n      expect(Puppet::Util.uri_query_encode(\"foo=bar+foo baz&bar=baz qux&special= *&qux=not fragment#\")).to eq(\"foo%3Dbar%2Bfoo%20baz%26bar%3Dbaz%20qux%26special%3D%20%2A%26qux%3Dnot%20fragment%23\")\n    end\n\n    [\n      \"A\\u06FF\\u16A0\\u{2070E}\",\n      \"A\\u06FF\\u16A0\\u{2070E}\".force_encoding(Encoding::BINARY)\n    ].each do |uri_string|\n      it \"should perform UTF-8 URI escaping, even when input strings are not UTF-8\" do\n        uri = Puppet::Util.uri_query_encode(mixed_utf8)\n\n        expect(uri.encoding).to eq(Encoding::UTF_8)\n        expect(uri).to eq(mixed_utf8_urlencoded)\n      end\n    end\n\n    it \"should be usable by URI::parse\" do\n      uri = URI::parse(\"puppet://server/path?\" + Puppet::Util.uri_query_encode(mixed_utf8))\n\n      expect(uri.scheme).to eq('puppet')\n      expect(uri.host).to eq('server')\n      expect(uri.path).to eq('/path')\n      expect(uri.query).to eq(mixed_utf8_urlencoded)\n    end\n\n    it \"should be usable by URI::Generic.build\" do\n      params = {\n        :scheme => 'file',\n        :host => 'foobar',\n        :path => '/path/to',\n        :query => Puppet::Util.uri_query_encode(mixed_utf8)\n      }\n\n      uri = URI::Generic.build(params)\n\n      expect(uri.scheme).to eq('file')\n      expect(uri.host).to eq('foobar')\n      expect(uri.path).to eq(\"/path/to\")\n      expect(uri.query).to eq(mixed_utf8_urlencoded)\n    end\n  end\n\n  describe \"#uri_encode\" do\n    # different UTF-8 widths\n    # 1-byte A\n    # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n    # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n    # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n    let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ܎\n    let (:mixed_utf8_urlencoded) { \"A%DB%BF%E1%9A%A0%F0%A0%9C%8E\" }\n\n    it \"should perform URI escaping\" do\n      expect(Puppet::Util.uri_encode(\"/foo bar\")).to eq(\"/foo%20bar\")\n    end\n\n    [\n      \"A\\u06FF\\u16A0\\u{2070E}\",\n      \"A\\u06FF\\u16A0\\u{2070E}\".force_encoding(Encoding::BINARY)\n    ].each do |uri_string|\n      it \"should perform UTF-8 URI escaping, even when input strings are not UTF-8\" do\n        uri = Puppet::Util.uri_encode(mixed_utf8)\n\n        expect(uri.encoding).to eq(Encoding::UTF_8)\n        expect(uri).to eq(mixed_utf8_urlencoded)\n      end\n    end\n\n    it \"should treat & and = as delimiters in a query string, but URI encode other special characters: + <space> * and #\" do\n      input = \"http://foo.bar.com/path?foo=bar+foo baz&bar=baz qux&special= *&qux=not fragment#\"\n      expected_output = \"http://foo.bar.com/path?foo=bar%2Bfoo%20baz&bar=baz%20qux&special=%20%2A&qux=not%20fragment%23\"\n      expect(Puppet::Util.uri_encode(input)).to eq(expected_output)\n    end\n\n    it \"should be usable by URI::parse\" do\n      uri = URI::parse(Puppet::Util.uri_encode(\"puppet://server/path/to/#{mixed_utf8}\"))\n\n      expect(uri.scheme).to eq('puppet')\n      expect(uri.host).to eq('server')\n      expect(uri.path).to eq(\"/path/to/#{mixed_utf8_urlencoded}\")\n    end\n\n    it \"should be usable by URI::Generic.build\" do\n      params = {\n        :scheme => 'file',\n        :host => 'foobar',\n        :path => Puppet::Util.uri_encode(\"/path/to/#{mixed_utf8}\")\n      }\n\n      uri = URI::Generic.build(params)\n\n      expect(uri.scheme).to eq('file')\n      expect(uri.host).to eq('foobar')\n      expect(uri.path).to eq(\"/path/to/#{mixed_utf8_urlencoded}\")\n    end\n\n    describe \"when using platform :posix\" do\n      before :each do\n        allow(Puppet.features).to receive(:posix?).and_return(true)\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      end\n\n      %w[/ /foo /foo/../bar].each do |path|\n        it \"should not replace / in #{path} with %2F\" do\n          expect(Puppet::Util.uri_encode(path)).to eq(path)\n        end\n      end\n    end\n\n    describe \"with fragment support\" do\n      context \"disabled by default\" do\n        it \"should encode # as %23 in path\" do\n          encoded = Puppet::Util.uri_encode(\"/foo bar#fragment\")\n          expect(encoded).to eq(\"/foo%20bar%23fragment\")\n        end\n\n        it \"should encode # as %23 in query\" do\n          encoded = Puppet::Util.uri_encode(\"/foo bar?baz+qux#fragment\")\n          expect(encoded).to eq(\"/foo%20bar?baz%2Bqux%23fragment\")\n        end\n      end\n\n      context \"optionally enabled\" do\n        it \"should leave fragment delimiter # after encoded paths\" do\n          encoded = Puppet::Util.uri_encode(\"/foo bar#fragment\", { :allow_fragment => true })\n          expect(encoded).to eq(\"/foo%20bar#fragment\")\n        end\n\n        it \"should leave fragment delimiter # after encoded query\" do\n          encoded = Puppet::Util.uri_encode(\"/foo bar?baz+qux#fragment\", { :allow_fragment => true })\n          expect(encoded).to eq(\"/foo%20bar?baz%2Bqux#fragment\")\n        end\n      end\n    end\n\n    describe \"when using platform :windows\" do\n      before :each do\n        allow(Puppet.features).to receive(:posix?).and_return(false)\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n      end\n\n      it \"should url encode \\\\ as %5C, but not replace : as %3F\" do\n        expect(Puppet::Util.uri_encode('c:\\\\foo\\\\bar\\\\baz')).to eq('c:%5Cfoo%5Cbar%5Cbaz')\n      end\n\n      %w[C:/ C:/foo/bar].each do |path|\n        it \"should not replace / in #{path} with %2F\" do\n          expect(Puppet::Util.uri_encode(path)).to eq(path)\n        end\n      end\n    end\n  end\n\n  describe \".uri_to_path\" do\n    require 'uri'\n\n    # different UTF-8 widths\n    # 1-byte A\n    # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191\n    # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160\n    # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142\n    let (:mixed_utf8) { \"A\\u06FF\\u16A0\\u{2070E}\" } # Aۿᚠ𠜎\n\n    it \"should strip host component\" do\n      expect(Puppet::Util.uri_to_path(URI.parse('http://foo/bar'))).to eq('/bar')\n    end\n\n    it \"should accept puppet URLs\" do\n      expect(Puppet::Util.uri_to_path(URI.parse('puppet:///modules/foo'))).to eq('/modules/foo')\n    end\n\n    it \"should return unencoded path\" do\n      expect(Puppet::Util.uri_to_path(URI.parse('http://foo/bar%20baz'))).to eq('/bar baz')\n    end\n\n    [\n      \"http://foo/A%DB%BF%E1%9A%A0%F0%A0%9C%8E\",\n      \"http://foo/A%DB%BF%E1%9A%A0%F0%A0%9C%8E\".force_encoding(Encoding::ASCII)\n    ].each do |uri_string|\n      it \"should return paths as UTF-8\" do\n        path = Puppet::Util.uri_to_path(URI.parse(uri_string))\n\n        expect(path).to eq(\"/#{mixed_utf8}\")\n        expect(path.encoding).to eq(Encoding::UTF_8)\n      end\n    end\n\n    it \"should be nil-safe\" do\n      expect(Puppet::Util.uri_to_path(nil)).to be_nil\n    end\n\n    describe \"when using platform :posix\",:if => Puppet.features.posix? do\n      it \"should accept root\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file:/'))).to eq('/')\n      end\n\n      it \"should accept single slash\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file:/foo/bar'))).to eq('/foo/bar')\n      end\n\n      it \"should accept triple slashes\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file:///foo/bar'))).to eq('/foo/bar')\n      end\n    end\n\n    describe \"when using platform :windows\", :if => Puppet::Util::Platform.windows? do\n      it \"should accept root\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file:/C:/'))).to eq('C:/')\n      end\n\n      it \"should accept single slash\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file:/C:/foo/bar'))).to eq('C:/foo/bar')\n      end\n\n      it \"should accept triple slashes\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file:///C:/foo/bar'))).to eq('C:/foo/bar')\n      end\n\n      it \"should accept file scheme with double slashes as a UNC path\" do\n        expect(Puppet::Util.uri_to_path(URI.parse('file://host/share/file'))).to eq('//host/share/file')\n      end\n    end\n  end\n\n  describe \"safe_posix_fork on Windows and JRuby\", if: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n    it \"raises not implemented error\" do\n      expect {\n        Puppet::Util.safe_posix_fork\n      }.to raise_error(NotImplementedError, /fork/)\n    end\n  end\n\n  describe \"safe_posix_fork\", unless: Puppet::Util::Platform.windows? || Puppet::Util::Platform.jruby? do\n    let(:pid) { 5501 }\n\n    before :each do\n      # Most of the things this method does are bad to do during specs. :/\n      allow(Kernel).to receive(:fork).and_return(pid).and_yield\n\n      allow($stdin).to receive(:reopen)\n      allow($stdout).to receive(:reopen)\n      allow($stderr).to receive(:reopen)\n\n      # ensure that we don't really close anything!\n      allow(IO).to receive(:new)\n    end\n\n    it \"should close all open file descriptors except stdin/stdout/stderr when /proc/self/fd exists\" do\n      # This is ugly, but I can't really think of a better way to do it without\n      # letting it actually close fds, which seems risky\n      fds = [\".\", \"..\",\"0\",\"1\",\"2\",\"3\",\"5\",\"100\",\"1000\"]\n      fds.each do |fd|\n        if fd == '.' || fd == '..'\n          next\n        elsif ['0', '1', '2'].include? fd\n          expect(IO).not_to receive(:new).with(fd.to_i)\n        else\n          expect(IO).to receive(:new).with(fd.to_i).and_return(double('io', close: nil))\n        end\n      end\n\n      dir_expectation = receive(:foreach).with('/proc/self/fd')\n      fds.each do |fd|\n        dir_expectation = dir_expectation.and_yield(fd)\n      end\n      allow(Dir).to dir_expectation\n      Puppet::Util.safe_posix_fork\n    end\n\n    it \"should close all open file descriptors except stdin/stdout/stderr when /proc/self/fd doesn't exist\" do\n      # This is ugly, but I can't really think of a better way to do it without\n      # letting it actually close fds, which seems risky\n      (0..2).each {|n| expect(IO).not_to receive(:new).with(n)}\n      (3..256).each {|n| expect(IO).to receive(:new).with(n).and_return(double('io', close: nil))  }\n      allow(Dir).to receive(:foreach).with('/proc/self/fd').and_raise(Errno::ENOENT)\n\n      Puppet::Util.safe_posix_fork\n    end\n\n    it \"should close all open file descriptors except stdin/stdout/stderr when /proc/self is not a directory\" do\n      # This is ugly, but I can't really think of a better way to do it without\n      # letting it actually close fds, which seems risky\n      (0..2).each {|n| expect(IO).not_to receive(:new).with(n)}\n      (3..256).each {|n| expect(IO).to receive(:new).with(n).and_return(double('io', close: nil))  }\n      allow(Dir).to receive(:foreach).with('/proc/self/fd').and_raise(Errno::ENOTDIR)\n\n      Puppet::Util.safe_posix_fork\n    end\n\n    it \"should fork a child process to execute the block\" do\n      expect(Kernel).to receive(:fork).and_return(pid).and_yield\n\n      Puppet::Util.safe_posix_fork do\n        \"Fork this!\"\n      end\n    end\n\n    it \"should return the pid of the child process\" do\n      expect(Puppet::Util.safe_posix_fork).to eq(pid)\n    end\n  end\n\n  describe \"#which\" do\n    let(:base) { File.expand_path('/bin') }\n    let(:path) { File.join(base, 'foo') }\n\n    before :each do\n      allow(FileTest).to receive(:file?).and_return(false)\n      allow(FileTest).to receive(:file?).with(path).and_return(true)\n\n      allow(FileTest).to receive(:executable?).and_return(false)\n      allow(FileTest).to receive(:executable?).with(path).and_return(true)\n    end\n\n    it \"should accept absolute paths\" do\n      expect(Puppet::Util.which(path)).to eq(path)\n    end\n\n    it \"should return nil if no executable found\" do\n      expect(Puppet::Util.which('doesnotexist')).to be_nil\n    end\n\n    it \"should reject directories\" do\n      expect(Puppet::Util.which(base)).to be_nil\n    end\n\n    it \"should ignore ~user directories if the user doesn't exist\" do\n      # Windows treats *any* user as a \"user that doesn't exist\", which means\n      # that this will work correctly across all our platforms, and should\n      # behave consistently.  If they ever implement it correctly (eg: to do\n      # the lookup for real) it should just work transparently.\n      baduser = 'if_this_user_exists_I_will_eat_my_hat'\n      Puppet::Util.withenv(\"PATH\" => \"~#{baduser}#{File::PATH_SEPARATOR}#{base}\") do\n        expect(Puppet::Util.which('foo')).to eq(path)\n      end\n    end\n\n    describe \"on POSIX systems\" do\n      before :each do\n        allow(Puppet.features).to receive(:posix?).and_return(true)\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(false)\n      end\n\n      it \"should walk the search PATH returning the first executable\" do\n        allow(ENV).to receive(:fetch).with('PATH').and_return(File.expand_path('/bin'))\n        allow(ENV).to receive(:fetch).with('PATHEXT', anything).and_return(nil)\n\n        expect(Puppet::Util.which('foo')).to eq(path)\n      end\n    end\n\n    describe \"on Windows systems\" do\n      let(:path) { File.expand_path(File.join(base, 'foo.CMD')) }\n\n      before :each do\n        allow(Puppet.features).to receive(:posix?).and_return(false)\n        allow(Puppet::Util::Platform).to receive(:windows?).and_return(true)\n      end\n\n      describe \"when a file extension is specified\" do\n        it \"should walk each directory in PATH ignoring PATHEXT\" do\n          allow(ENV).to receive(:fetch).with('PATH').and_return(%w[/bar /bin].map{|dir| File.expand_path(dir)}.join(File::PATH_SEPARATOR))\n          allow(ENV).to receive(:fetch).with('PATHEXT', anything).and_return('.FOOBAR')\n\n          expect(FileTest).to receive(:file?).with(File.join(File.expand_path('/bar'), 'foo.CMD')).and_return(false)\n\n          expect(Puppet::Util.which('foo.CMD')).to eq(path)\n        end\n      end\n\n      describe \"when a file extension is not specified\" do\n        it \"should walk each extension in PATHEXT until an executable is found\" do\n          bar = File.expand_path('/bar')\n          allow(ENV).to receive(:fetch).with('PATH').and_return(\"#{bar}#{File::PATH_SEPARATOR}#{base}\")\n          allow(ENV).to receive(:fetch).with('PATHEXT', anything).and_return(\".EXE#{File::PATH_SEPARATOR}.CMD\")\n\n          expect(FileTest).to receive(:file?).ordered().with(File.join(bar, 'foo.EXE')).and_return(false)\n          expect(FileTest).to receive(:file?).ordered().with(File.join(bar, 'foo.CMD')).and_return(false)\n          expect(FileTest).to receive(:file?).ordered().with(File.join(base, 'foo.EXE')).and_return(false)\n          expect(FileTest).to receive(:file?).ordered().with(path).and_return(true)\n\n          expect(Puppet::Util.which('foo')).to eq(path)\n        end\n\n        it \"should walk the default extension path if the environment variable is not defined\" do\n          allow(ENV).to receive(:fetch).with('PATH').and_return(base)\n          allow(ENV).to receive(:fetch).with('PATHEXT', anything).and_return(nil)\n\n          %w[.COM .EXE .BAT].each do |ext|\n            expect(FileTest).to receive(:file?).ordered().with(File.join(base, \"foo#{ext}\")).and_return(false)\n          end\n          expect(FileTest).to receive(:file?).ordered().with(path).and_return(true)\n\n          expect(Puppet::Util.which('foo')).to eq(path)\n        end\n\n        it \"should fall back if no extension matches\" do\n          allow(ENV).to receive(:fetch).with('PATH').and_return(base)\n          allow(ENV).to receive(:fetch).with('PATHEXT', anything).and_return(\".EXE\")\n\n          allow(FileTest).to receive(:file?).with(File.join(base, 'foo.EXE')).and_return(false)\n          allow(FileTest).to receive(:file?).with(File.join(base, 'foo')).and_return(true)\n          allow(FileTest).to receive(:executable?).with(File.join(base, 'foo')).and_return(true)\n\n          expect(Puppet::Util.which('foo')).to eq(File.join(base, 'foo'))\n        end\n      end\n    end\n  end\n\n  describe \"hash symbolizing functions\" do\n    let (:myhash) { { \"foo\" => \"bar\", :baz => \"bam\" } }\n    let (:resulthash) { { :foo => \"bar\", :baz => \"bam\" } }\n\n    describe \"#symbolizehash\" do\n      it \"should return a symbolized hash\" do\n        newhash = Puppet::Util.symbolizehash(myhash)\n        expect(newhash).to eq(resulthash)\n      end\n    end\n  end\n\n  context \"#replace_file\" do\n    subject { Puppet::Util }\n\n    it { is_expected.to respond_to :replace_file }\n\n    let :target do\n      target = Tempfile.new(\"puppet-util-replace-file\")\n      target.puts(\"hello, world\")\n      target.flush              # make sure content is on disk.\n      target.fsync rescue nil\n      target.close\n      target\n    end\n\n    it \"should fail if no block is given\" do\n      expect { subject.replace_file(target.path, 0600) }.to raise_error(/block/)\n    end\n\n    it \"should replace a file when invoked\" do\n      # Check that our file has the expected content.\n      expect(File.read(target.path)).to eq(\"hello, world\\n\")\n\n      # Replace the file.\n      subject.replace_file(target.path, 0600) do |fh|\n        fh.puts \"I am the passenger...\"\n      end\n\n      # ...and check the replacement was complete.\n      expect(File.read(target.path)).to eq(\"I am the passenger...\\n\")\n    end\n\n    # When running with the same user and group sid, which is the default,\n    # Windows collapses the owner and group modes into a single ACE, resulting\n    # in set(0600) => get(0660) and so forth. --daniel 2012-03-30\n    modes = [0555, 0660, 0770]\n    modes += [0600, 0700] unless Puppet::Util::Platform.windows?\n    modes.each do |mode|\n      it \"should copy 0#{mode.to_s(8)} permissions from the target file by default\" do\n        set_mode(mode, target.path)\n\n        expect(get_mode(target.path)).to eq(mode)\n\n        subject.replace_file(target.path, 0000) {|fh| fh.puts \"bazam\" }\n\n        expect(get_mode(target.path)).to eq(mode)\n        expect(File.read(target.path)).to eq(\"bazam\\n\")\n      end\n    end\n\n    it \"should copy the permissions of the source file after yielding on Unix\", :if => !Puppet::Util::Platform.windows? do\n      set_mode(0555, target.path)\n      inode = Puppet::FileSystem.stat(target.path).ino\n\n      yielded = false\n      subject.replace_file(target.path, 0660) do |fh|\n        expect(get_mode(fh.path)).to eq(0600)\n        yielded = true\n      end\n      expect(yielded).to be_truthy\n\n      expect(Puppet::FileSystem.stat(target.path).ino).not_to eq(inode)\n      expect(get_mode(target.path)).to eq(0555)\n    end\n\n    it \"should be able to create a new file with read-only permissions when it doesn't already exist\" do\n      temp_file = Tempfile.new('puppet-util-replace-file')\n      temp_path = temp_file.path\n      temp_file.close\n      temp_file.unlink\n\n      subject.replace_file(temp_path, 0440) do |fh|\n        fh.puts('some text in there')\n      end\n\n      expect(File.read(temp_path)).to eq(\"some text in there\\n\")\n      expect(get_mode(temp_path)).to eq(0440)\n    end\n\n    it \"should use the default permissions if the source file doesn't exist\" do\n      new_target = target.path + '.foo'\n      expect(Puppet::FileSystem.exist?(new_target)).to be_falsey\n\n      begin\n        subject.replace_file(new_target, 0555) {|fh| fh.puts \"foo\" }\n        expect(get_mode(new_target)).to eq(0555)\n      ensure\n        Puppet::FileSystem.unlink(new_target) if Puppet::FileSystem.exist?(new_target)\n      end\n    end\n\n    it \"should use a temporary staging location if provided\" do\n      new_target = File.join(tmpdir('new_file'), 'new_file.baz')\n      staging_target = tmpdir('staging_file')\n\n      subject.replace_file(new_target, 0555, staging_location: staging_target) do |fh|\n        expect(File.dirname(fh.path)).to eq(staging_target)\n          fh.puts \"foo\"\n      end\n    end\n\n    it \"should not replace the file if an exception is thrown in the block\" do\n      yielded = false\n      threw   = false\n\n      begin\n        subject.replace_file(target.path, 0600) do |fh|\n          yielded = true\n          fh.puts \"different content written, then...\"\n          raise \"...throw some random failure\"\n        end\n      rescue Exception => e\n        if e.to_s =~ /some random failure/\n          threw = true\n        else\n          raise\n        end\n      end\n\n      expect(yielded).to be_truthy\n      expect(threw).to be_truthy\n\n      # ...and check the replacement was complete.\n      expect(File.read(target.path)).to eq(\"hello, world\\n\")\n    end\n\n    {:string => '664', :number => 0664, :symbolic => \"ug=rw-,o=r--\" }.each do |label,mode|\n      it \"should support #{label} format permissions\" do\n        new_target = target.path + \"#{mode}.foo\"\n        expect(Puppet::FileSystem.exist?(new_target)).to be_falsey\n\n        begin\n          subject.replace_file(new_target, mode) {|fh| fh.puts \"this is an interesting content\" }\n\n          expect(get_mode(new_target)).to eq(0664)\n        ensure\n          Puppet::FileSystem.unlink(new_target) if Puppet::FileSystem.exist?(new_target)\n        end\n      end\n    end\n\n  end\n\n  describe \"#pretty_backtrace\" do\n    it \"should include lines that don't match the standard backtrace pattern\" do\n      line = \"non-standard line\\n\"\n      trace = caller[0..2] + [line] + caller[3..-1]\n      expect(Puppet::Util.pretty_backtrace(trace)).to match(/#{line}/)\n    end\n\n    it \"should include function names\" do\n      expect(Puppet::Util.pretty_backtrace).to match(/:in `\\w+'/)\n    end\n\n    it \"should work with Windows paths\" do\n      expect(Puppet::Util.pretty_backtrace([\"C:/work/puppet/c.rb:12:in `foo'\\n\"])).\n        to eq(\"C:/work/puppet/c.rb:12:in `foo'\")\n    end\n  end\n\n  describe \"#deterministic_rand\" do\n    it \"should not fiddle with future rand calls\" do\n      Puppet::Util.deterministic_rand(123,20)\n      rand_one = rand()\n      Puppet::Util.deterministic_rand(123,20)\n      expect(rand()).not_to eql(rand_one)\n    end\n\n    it \"should not fiddle with the global seed\" do\n      srand(1234)\n      Puppet::Util.deterministic_rand(123,20)\n      expect(srand()).to eql(1234)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/version_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"puppet/version\"\nrequire 'pathname'\n\ndescribe \"Puppet.version Public API\" do\n  before :each do\n    @current_ver = Puppet.version\n    Puppet.instance_eval do\n      if @puppet_version\n        @puppet_version = nil\n      end\n    end\n  end\n\n  after :each do\n    Puppet.version = @current_ver\n  end\n\n  context \"without a VERSION file\" do\n    before :each do\n      allow(Puppet).to receive(:read_version_file).and_return(nil)\n    end\n\n    it \"is Puppet::PUPPETVERSION\" do\n      expect(Puppet.version).to eq(Puppet::PUPPETVERSION)\n    end\n\n    it \"respects the version= setter\" do\n      Puppet.version = '1.2.3'\n      expect(Puppet.version).to eq('1.2.3')\n      expect(Puppet.minor_version).to eq('1.2')\n    end\n  end\n\n  context \"with a VERSION file\" do\n    it \"is the content of the file\" do\n      expect(Puppet).to receive(:read_version_file) do |path|\n        pathname = Pathname.new(path)\n        pathname.basename.to_s == \"VERSION\"\n      end.and_return('3.0.1-260-g9ca4e54')\n\n      expect(Puppet.version).to eq('3.0.1-260-g9ca4e54')\n      expect(Puppet.minor_version).to eq('3.0')\n    end\n\n    it \"respects the version= setter\" do\n      Puppet.version = '1.2.3'\n      expect(Puppet.version).to eq('1.2.3')\n      expect(Puppet.minor_version).to eq('1.2')\n    end\n  end\n\n  context \"Using version setter\" do\n    it \"does not read VERSION file if using set version\" do\n      expect(Puppet).not_to receive(:read_version_file)\n      Puppet.version = '1.2.3'\n      expect(Puppet.version).to eq('1.2.3')\n      expect(Puppet.minor_version).to eq('1.2')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/x509/cert_provider_spec.rb",
    "content": "require 'spec_helper'\nrequire 'puppet/x509'\n\ndescribe Puppet::X509::CertProvider do\n  include PuppetSpec::Files\n\n  def create_provider(options)\n    described_class.new(**options)\n  end\n\n  def expects_public_file(path)\n    if Puppet::Util::Platform.windows?\n      current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n      sd = Puppet::Util::Windows::Security.get_security_descriptor(path)\n      expect(sd.dacl).to contain_exactly(\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),\n        an_object_having_attributes(sid: current_sid, mask: 0x1f01ff),\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinUsers, mask: 0x120089)\n      )\n    else\n      expect(File.stat(path).mode & 07777).to eq(0644)\n    end\n  end\n\n  def expects_private_file(path)\n    if Puppet::Util::Platform.windows?\n      current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n      sd = Puppet::Util::Windows::Security.get_security_descriptor(path)\n      expect(sd.dacl).to contain_exactly(\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),\n        an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),\n        an_object_having_attributes(sid: current_sid, mask: 0x1f01ff)\n      )\n    else\n      expect(File.stat(path).mode & 07777).to eq(0640)\n    end\n  end\n\n  let(:fixture_dir) { File.join(PuppetSpec::FIXTURE_DIR, 'ssl') }\n\n  context 'when loading' do\n    context 'cacerts' do\n      it 'returns nil if it does not exist' do\n        provider = create_provider(capath: '/does/not/exist')\n\n        expect(provider.load_cacerts).to be_nil\n      end\n\n      it 'raises if cacerts are required' do\n        provider = create_provider(capath: '/does/not/exist')\n\n        expect {\n          provider.load_cacerts(required: true)\n        }.to raise_error(Puppet::Error, %r{The CA certificates are missing from '/does/not/exist'})\n      end\n\n      it 'returns an array of certificates' do\n        subject = OpenSSL::X509::Name.new([['CN', 'Test CA']])\n        certs = create_provider(capath: File.join(fixture_dir, 'ca.pem')).load_cacerts\n        expect(certs).to contain_exactly(an_object_having_attributes(subject: subject))\n      end\n\n      context 'and input is invalid' do\n        it 'raises when invalid input is inside BEGIN-END block' do\n          ca_path = file_containing('invalid_ca', <<~END)\n            -----BEGIN CERTIFICATE-----\n            whoops\n            -----END CERTIFICATE-----\n          END\n\n          expect {\n            create_provider(capath: ca_path).load_cacerts\n          }.to raise_error(OpenSSL::X509::CertificateError)\n        end\n\n        it 'raises if the input is empty' do\n          expect {\n            create_provider(capath: file_containing('empty_ca', '')).load_cacerts\n          }.to raise_error(OpenSSL::X509::CertificateError)\n        end\n\n        it 'raises if the input is malformed' do\n          ca_path = file_containing('malformed_ca', <<~END)\n            -----BEGIN CERTIFICATE-----\n            MIIBpDCCAQ2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0\n          END\n\n          expect {\n            create_provider(capath: ca_path).load_cacerts\n          }.to raise_error(OpenSSL::X509::CertificateError)\n        end\n      end\n\n      it 'raises if the cacerts are unreadable' do\n        capath = File.join(fixture_dir, 'ca.pem')\n        provider = create_provider(capath: capath)\n        allow(provider).to receive(:load_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.load_cacerts\n        }.to raise_error(Puppet::Error, \"Failed to load CA certificates from '#{capath}'\")\n      end\n    end\n\n    context 'crls' do\n      it 'returns nil if it does not exist' do\n        provider = create_provider(crlpath: '/does/not/exist')\n        expect(provider.load_crls).to be_nil\n      end\n\n      it 'raises if CRLs are required' do\n        provider = create_provider(crlpath: '/does/not/exist')\n\n        expect {\n          provider.load_crls(required: true)\n        }.to raise_error(Puppet::Error, %r{The CRL is missing from '/does/not/exist'})\n      end\n\n      it 'returns an array of CRLs' do\n        issuer = OpenSSL::X509::Name.new([['CN', 'Test CA']])\n        crls = create_provider(crlpath: File.join(fixture_dir, 'crl.pem')).load_crls\n        expect(crls).to contain_exactly(an_object_having_attributes(issuer: issuer))\n      end\n\n      context 'and input is invalid' do\n        it 'raises when invalid input is inside BEGIN-END block' do\n          pending('jruby bug: https://github.com/jruby/jruby/issues/5619') if Puppet::Util::Platform.jruby?\n\n          crl_path = file_containing('invalid_crls', <<~END)\n            -----BEGIN X509 CRL-----\n            whoops\n            -----END X509 CRL-----\n          END\n\n          expect {\n            create_provider(crlpath: crl_path).load_crls\n          }.to raise_error(OpenSSL::X509::CRLError, /(PEM_read_bio_X509_CRL: bad base64 decode|nested asn1 error)/)\n        end\n\n        it 'raises if the input is empty' do\n          expect {\n            create_provider(crlpath: file_containing('empty_crl', '')).load_crls\n          }.to raise_error(OpenSSL::X509::CRLError, 'Failed to parse CRLs as PEM')\n        end\n\n        it 'raises if the input is malformed' do\n          crl_path = file_containing('malformed_crl', <<~END)\n            -----BEGIN X509 CRL-----\n            MIIBCjB1AgEBMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EXDTcw\n          END\n\n          expect {\n            create_provider(crlpath: crl_path).load_crls\n          }.to raise_error(OpenSSL::X509::CRLError, 'Failed to parse CRLs as PEM')\n        end\n      end\n\n      it 'raises if the CRLs are unreadable' do\n        crlpath = File.join(fixture_dir, 'crl.pem')\n        provider = create_provider(crlpath: crlpath)\n        allow(provider).to receive(:load_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.load_crls\n        }.to raise_error(Puppet::Error, \"Failed to load CRLs from '#{crlpath}'\")\n      end\n    end\n  end\n\n  context 'when saving' do\n    context 'cacerts' do\n      let(:ca_path) { tmpfile('pem_cacerts') }\n      let(:ca_cert) { cert_fixture('ca.pem') }\n\n      it 'writes PEM encoded certs' do\n        create_provider(capath: ca_path).save_cacerts([ca_cert])\n\n        expect(File.read(ca_path)).to match(/\\A-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----\\Z/m)\n      end\n\n      it 'sets mode to 644' do\n        create_provider(capath: ca_path).save_cacerts([ca_cert])\n\n        expects_public_file(ca_path)\n      end\n\n      it 'raises if the CA certs are unwritable' do\n        provider = create_provider(capath: ca_path)\n        allow(provider).to receive(:save_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.save_cacerts([ca_cert])\n        }.to raise_error(Puppet::Error, \"Failed to save CA certificates to '#{ca_path}'\")\n      end\n    end\n\n    context 'crls' do\n      let(:crl_path) { tmpfile('pem_crls') }\n      let(:ca_crl) { crl_fixture('crl.pem') }\n\n      it 'writes PEM encoded CRLs' do\n        create_provider(crlpath: crl_path).save_crls([ca_crl])\n\n        expect(File.read(crl_path)).to match(/\\A-----BEGIN X509 CRL-----.*?-----END X509 CRL-----\\Z/m)\n      end\n\n      it 'sets mode to 644' do\n        create_provider(crlpath: crl_path).save_crls([ca_crl])\n\n        expects_public_file(crl_path)\n      end\n\n      it 'raises if the CRLs are unwritable' do\n        provider = create_provider(crlpath: crl_path)\n        allow(provider).to receive(:save_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.save_crls([ca_crl])\n        }.to raise_error(Puppet::Error, \"Failed to save CRLs to '#{crl_path}'\")\n      end\n    end\n  end\n\n  context 'when loading' do\n    context 'private keys', unless: RUBY_PLATFORM == 'java' do\n      let(:provider) { create_provider(privatekeydir: fixture_dir) }\n      let(:password) { '74695716c8b6' }\n\n      it 'returns nil if it does not exist' do\n        provider = create_provider(privatekeydir: '/does/not/exist')\n\n        expect(provider.load_private_key('whatever')).to be_nil\n      end\n\n      it 'raises if it is required' do\n        provider = create_provider(privatekeydir: '/does/not/exist')\n\n        expect {\n          provider.load_private_key('whatever', required: true)\n        }.to raise_error(Puppet::Error, %r{The private key is missing from '/does/not/exist/whatever.pem'})\n      end\n\n      it 'downcases name' do\n        expect(provider.load_private_key('SIGNED-KEY')).to be_a(OpenSSL::PKey::RSA)\n      end\n\n      it 'raises if name is invalid' do\n        expect {\n          provider.load_private_key('signed/../key')\n        }.to raise_error(RuntimeError, 'Certname \"signed/../key\" must not contain unprintable or non-ASCII characters')\n      end\n\n      it 'prefers `hostprivkey` if set' do\n        Puppet[:certname] = 'foo'\n        Puppet[:hostprivkey] = File.join(fixture_dir, \"signed-key.pem\")\n\n        expect(provider.load_private_key('foo')).to be_a(OpenSSL::PKey::RSA)\n      end\n\n      it 'raises if the private key is unreadable' do\n        allow(provider).to receive(:load_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.load_private_key('signed')\n        }.to raise_error(Puppet::Error, \"Failed to load private key for 'signed'\")\n      end\n\n      context 'using RSA' do\n        it 'returns an RSA key' do\n          expect(provider.load_private_key('signed-key')).to be_a(OpenSSL::PKey::RSA)\n        end\n\n        it 'decrypts an RSA key using the password' do\n          rsa = provider.load_private_key('encrypted-key', password: password)\n          expect(rsa).to be_a(OpenSSL::PKey::RSA)\n        end\n\n        it 'raises without a password' do\n          # password is 74695716c8b6\n          expect {\n            provider.load_private_key('encrypted-key')\n          }.to raise_error(OpenSSL::PKey::PKeyError, /Could not parse PKey/)\n        end\n\n        it 'decrypts an RSA key previously saved using 3DES' do\n          key = key_fixture('signed-key.pem')\n          cipher = OpenSSL::Cipher::DES.new(:EDE3, :CBC)\n          privatekeydir = dir_containing('private_keys', {'oldkey.pem' => key.export(cipher, password)})\n          provider = create_provider(privatekeydir: privatekeydir)\n\n          expect(provider.load_private_key('oldkey', password: password).to_der).to eq(key.to_der)\n        end\n      end\n\n      context 'using EC' do\n        it 'returns an EC key' do\n          expect(provider.load_private_key('ec-key')).to be_a(OpenSSL::PKey::EC)\n        end\n\n        it 'returns an EC key from PKCS#8 format' do\n          expect(provider.load_private_key('ec-key-pk8')).to be_a(OpenSSL::PKey::EC)\n        end\n\n        it 'returns an EC key from openssl format' do\n          expect(provider.load_private_key('ec-key-openssl')).to be_a(OpenSSL::PKey::EC)\n        end\n\n        it 'decrypts an EC key using the password' do\n          ec = provider.load_private_key('encrypted-ec-key', password: password)\n          expect(ec).to be_a(OpenSSL::PKey::EC)\n        end\n\n        it 'raises without a password' do\n          # password is 74695716c8b6\n          expect {\n            provider.load_private_key('encrypted-ec-key')\n          }.to raise_error(OpenSSL::PKey::PKeyError, /(unknown|invalid) curve name|Could not parse PKey/)\n        end\n      end\n    end\n\n    context 'certs' do\n      let(:provider) { create_provider(certdir: fixture_dir) }\n\n      it 'returns nil if it does not exist' do\n        provider = create_provider(certdir: '/does/not/exist')\n\n        expect(provider.load_client_cert('nonexistent')).to be_nil\n      end\n\n      it 'raises if it is required' do\n        provider = create_provider(certdir: '/does/not/exist')\n\n        expect {\n          provider.load_client_cert('nonexistent', required: true)\n        }.to raise_error(Puppet::Error, %r{The client certificate is missing from '/does/not/exist/nonexistent.pem'})\n      end\n\n      it 'returns a certificate' do\n        cert = provider.load_client_cert('signed')\n        expect(cert.subject.to_utf8).to eq('CN=signed')\n      end\n\n      it 'downcases name' do\n        cert = provider.load_client_cert('SIGNED')\n        expect(cert.subject.to_utf8).to eq('CN=signed')\n      end\n\n      it 'raises if name is invalid' do\n        expect {\n          provider.load_client_cert('tom/../key')\n        }.to raise_error(RuntimeError, 'Certname \"tom/../key\" must not contain unprintable or non-ASCII characters')\n      end\n\n      it 'prefers `hostcert` if set' do\n        Puppet[:certname] = 'foo'\n        Puppet[:hostcert] = File.join(fixture_dir, \"signed.pem\")\n\n        expect(provider.load_client_cert('foo')).to be_a(OpenSSL::X509::Certificate)\n      end\n\n      it 'raises if the certificate is unreadable' do\n        allow(provider).to receive(:load_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.load_client_cert('signed')\n        }.to raise_error(Puppet::Error, \"Failed to load client certificate for 'signed'\")\n      end\n    end\n\n    context 'requests' do\n      let(:request) { request_fixture('request.pem') }\n      let(:provider) { create_provider(requestdir: fixture_dir) }\n\n      it 'returns nil if it does not exist' do\n        expect(provider.load_request('whatever')).to be_nil\n      end\n\n      it 'returns a request' do\n        expect(provider.load_request('request')).to be_a(OpenSSL::X509::Request)\n      end\n\n      it 'downcases name' do\n        csr = provider.load_request('REQUEST')\n        expect(csr.subject.to_utf8).to eq('CN=pending')\n      end\n\n      it 'raises if name is invalid' do\n        expect {\n          provider.load_request('tom/../key')\n        }.to raise_error(RuntimeError, 'Certname \"tom/../key\" must not contain unprintable or non-ASCII characters')\n      end\n\n      it 'ignores `hostcsr`' do\n        Puppet[:hostcsr] = File.join(fixture_dir, \"doesnotexist.pem\")\n\n        expect(provider.load_request('request')).to be_a(OpenSSL::X509::Request)\n      end\n\n      it 'raises if the certificate is unreadable' do\n        allow(provider).to receive(:load_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.load_request('pending')\n        }.to raise_error(Puppet::Error, \"Failed to load certificate request for 'pending'\")\n      end\n    end\n  end\n\n  context 'when saving' do\n    let(:name) { 'tom' }\n\n    context 'private keys' do\n      let(:privatekeydir) { tmpdir('privatekeydir') }\n      let(:private_key) { key_fixture('signed-key.pem') }\n      let(:path) { File.join(privatekeydir, 'tom.pem') }\n      let(:provider) { create_provider(privatekeydir: privatekeydir) }\n\n      it 'writes PEM encoded private key' do\n        provider.save_private_key(name, private_key)\n\n        expect(File.read(path)).to match(/\\A-----BEGIN RSA PRIVATE KEY-----.*?-----END RSA PRIVATE KEY-----\\Z/m)\n      end\n\n      it 'encrypts the private key using AES128-CBC' do\n        provider.save_private_key(name, private_key, password: Random.new.bytes(8))\n\n        expect(File.read(path)).to match(/Proc-Type: 4,ENCRYPTED.*DEK-Info: AES-128-CBC/m)\n      end\n\n      it 'sets mode to 640' do\n        provider.save_private_key(name, private_key)\n\n        expects_private_file(path)\n      end\n\n      it 'downcases name' do\n        provider.save_private_key('TOM', private_key)\n\n        expect(File).to be_exist(path)\n      end\n\n      it 'raises if name is invalid' do\n        expect {\n          provider.save_private_key('tom/../key', private_key)\n        }.to raise_error(RuntimeError, 'Certname \"tom/../key\" must not contain unprintable or non-ASCII characters')\n      end\n\n      it 'raises if the private key is unwritable' do\n        allow(provider).to receive(:save_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.save_private_key(name, private_key)\n        }.to raise_error(Puppet::Error, \"Failed to save private key for '#{name}'\")\n      end\n\n      it 'prefers `hostprivkey` if set' do\n        overridden_path = tmpfile('hostprivkey')\n        Puppet[:hostprivkey] = overridden_path\n\n        provider.save_private_key(name, private_key)\n\n        expect(File).to_not exist(path)\n        expect(File).to exist(overridden_path)\n      end\n    end\n\n    context 'certs' do\n      let(:certdir) { tmpdir('certdir') }\n      let(:client_cert) { cert_fixture('signed.pem') }\n      let(:path) { File.join(certdir, 'tom.pem') }\n      let(:provider) { create_provider(certdir: certdir) }\n\n      it 'writes PEM encoded cert' do\n        provider.save_client_cert(name, client_cert)\n\n        expect(File.read(path)).to match(/\\A-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----\\Z/m)\n      end\n\n      it 'sets mode to 644' do\n        provider.save_client_cert(name, client_cert)\n\n        expects_public_file(path)\n      end\n\n      it 'downcases name' do\n        provider.save_client_cert('TOM', client_cert)\n\n        expect(File).to be_exist(path)\n      end\n\n      it 'raises if name is invalid' do\n        expect {\n          provider.save_client_cert('tom/../key', client_cert)\n        }.to raise_error(RuntimeError, 'Certname \"tom/../key\" must not contain unprintable or non-ASCII characters')\n      end\n\n      it 'raises if the cert is unwritable' do\n        allow(provider).to receive(:save_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.save_client_cert(name, client_cert)\n        }.to raise_error(Puppet::Error, \"Failed to save client certificate for '#{name}'\")\n      end\n\n      it 'prefers `hostcert` if set' do\n        overridden_path = tmpfile('hostcert')\n        Puppet[:hostcert] = overridden_path\n\n        provider.save_client_cert(name, client_cert)\n\n        expect(File).to_not exist(path)\n        expect(File).to exist(overridden_path)\n      end\n    end\n\n    context 'requests' do\n      let(:requestdir) { tmpdir('requestdir') }\n      let(:csr) { request_fixture('request.pem') }\n      let(:path) { File.join(requestdir, 'tom.pem') }\n      let(:provider) { create_provider(requestdir: requestdir) }\n\n      it 'writes PEM encoded request' do\n        provider.save_request(name, csr)\n\n        expect(File.read(path)).to match(/\\A-----BEGIN CERTIFICATE REQUEST-----.*?-----END CERTIFICATE REQUEST-----\\Z/m)\n      end\n\n      it 'sets mode to 644' do\n        provider.save_request(name, csr)\n\n        expects_public_file(path)\n      end\n\n      it 'downcases name' do\n        provider.save_request('TOM', csr)\n\n        expect(File).to be_exist(path)\n      end\n\n      it 'raises if name is invalid' do\n        expect {\n          provider.save_request('tom/../key', csr)\n        }.to raise_error(RuntimeError, 'Certname \"tom/../key\" must not contain unprintable or non-ASCII characters')\n      end\n\n      it 'raises if the request is unwritable' do\n        allow(provider).to receive(:save_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.save_request(name, csr)\n        }.to raise_error(Puppet::Error, \"Failed to save certificate request for '#{name}'\")\n      end\n    end\n  end\n\n  context 'when deleting' do\n    context 'requests' do\n      let(:name) { 'jerry' }\n      let(:requestdir) { tmpdir('cert_provider') }\n      let(:provider) { create_provider(requestdir: requestdir) }\n\n      it 'returns true if request was deleted' do\n        path = File.join(requestdir, \"#{name}.pem\")\n        File.write(path, \"PEM\")\n\n        expect(provider.delete_request(name)).to eq(true)\n        expect(File).not_to be_exist(path)\n      end\n\n      it 'returns false if the request is non-existent' do\n        path = File.join(requestdir, \"#{name}.pem\")\n\n        expect(provider.delete_request(name)).to eq(false)\n        expect(File).to_not be_exist(path)\n      end\n\n      it 'raises if the file is undeletable' do\n        allow(provider).to receive(:delete_pem).and_raise(Errno::EACCES, 'Permission denied')\n\n        expect {\n          provider.delete_request(name)\n        }.to raise_error(Puppet::Error, \"Failed to delete certificate request for '#{name}'\")\n      end\n    end\n  end\n\n  context 'when creating', :unless => RUBY_PLATFORM == 'java' do\n    context 'requests' do\n      let(:name) { 'tom' }\n      let(:requestdir) { tmpdir('cert_provider') }\n      let(:provider) { create_provider(requestdir: requestdir) }\n      let(:key) { OpenSSL::PKey::RSA.new(Puppet[:keylength]) }\n\n      it 'has the auto-renew attribute by default for agents that support automatic renewal' do\n        csr = provider.create_request(name, key)\n        # need to create CertificateRequest instance from csr in order to view CSR attributes\n        wrapped_csr = Puppet::SSL::CertificateRequest.from_instance csr\n        expect(wrapped_csr.custom_attributes).to include('oid' => 'pp_auth_auto_renew', 'value' => 'true')\n      end\n\n      it 'does not have the auto-renew attribute for agents that do not support automatic renewal' do\n        Puppet[:hostcert_renewal_interval] = 0\n        csr = provider.create_request(name, key)\n        wrapped_csr = Puppet::SSL::CertificateRequest.from_instance csr\n        expect(wrapped_csr.custom_attributes.length).to eq(0)\n      end\n    end\n  end\n\n  context 'CA last update time' do\n    let(:ca_path) { tmpfile('pem_ca') }\n\n    it 'returns nil if the CA does not exist' do\n      provider = create_provider(capath: '/does/not/exist')\n\n      expect(provider.ca_last_update).to be_nil\n    end\n\n    it 'returns the last update time' do\n      time = Time.now - 30\n      Puppet::FileSystem.touch(ca_path, mtime: time)\n      provider = create_provider(capath: ca_path)\n\n      expect(provider.ca_last_update).to be_within(1).of(time)\n    end\n\n    it 'sets the last update time' do\n      time = Time.now - 30\n      provider = create_provider(capath: ca_path)\n      provider.ca_last_update = time\n\n      expect(Puppet::FileSystem.stat(ca_path).mtime).to be_within(1).of(time)\n    end\n  end\n\n  context 'CRL last update time' do\n    let(:crl_path) { tmpfile('pem_crls') }\n\n    it 'returns nil if the CRL does not exist' do\n      provider = create_provider(crlpath: '/does/not/exist')\n\n      expect(provider.crl_last_update).to be_nil\n    end\n\n    it 'returns the last update time' do\n      time = Time.now - 30\n      Puppet::FileSystem.touch(crl_path, mtime: time)\n      provider = create_provider(crlpath: crl_path)\n\n      expect(provider.crl_last_update).to be_within(1).of(time)\n    end\n\n    it 'sets the last update time' do\n      time = Time.now - 30\n      provider = create_provider(crlpath: crl_path)\n      provider.crl_last_update = time\n\n      expect(Puppet::FileSystem.stat(crl_path).mtime).to be_within(1).of(time)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/x509/pem_store_spec.rb",
    "content": "# coding: utf-8\nrequire 'spec_helper'\nrequire 'puppet/x509'\n\nclass Puppet::X509::TestPemStore\n  include Puppet::X509::PemStore\nend\n\ndescribe Puppet::X509::PemStore do\n  include PuppetSpec::Files\n\n  let(:subject) { Puppet::X509::TestPemStore.new }\n\n  def with_unreadable_file\n    path = tmpfile('pem_store')\n    Puppet::FileSystem.touch(path)\n    Puppet::FileSystem.chmod(0, path)\n    yield path\n  ensure\n    Puppet::FileSystem.chmod(0600, path)\n  end\n\n  def with_unwritable_file(&block)\n    if Puppet::Util::Platform.windows?\n      with_unwritable_file_win32(&block)\n    else\n      with_unwritable_file_posix(&block)\n    end\n  end\n\n  def with_unwritable_file_win32\n    dir = tmpdir('pem_store')\n    path = File.join(dir, 'unwritable')\n\n    # if file handle is open, then file can't be written by other processes\n    File.open(path, 'w') do |f|\n      yield path\n    end\n  end\n\n  def with_unwritable_file_posix\n    dir = tmpdir('pem_store')\n    path = File.join(dir, 'unwritable')\n    # if directory is not executable/traverseable, then file can't be written to\n    Puppet::FileSystem.chmod(0, dir)\n    begin\n      yield path\n    ensure\n      Puppet::FileSystem.chmod(0700, dir)\n    end\n  end\n\n  let(:cert_path) { File.join(PuppetSpec::FIXTURE_DIR, 'ssl', 'netlock-arany-utf8.pem') }\n\n  context 'loading' do\n    it 'returns nil if it does not exist' do\n      expect(subject.load_pem('/does/not/exist')).to be_nil\n    end\n\n    it 'returns the file content as UTF-8' do\n      expect(\n        subject.load_pem(cert_path)\n      ).to match(/\\ANetLock Arany \\(Class Gold\\) Főtanúsítvány/)\n    end\n\n    it 'raises EACCES if the file is unreadable' do\n      with_unreadable_file do |path|\n        expect {\n          subject.load_pem(path)\n        }.to raise_error(Errno::EACCES, /Permission denied/)\n      end\n    end\n  end\n\n  context 'saving' do\n    let(:path) { tmpfile('pem_store') }\n\n    it 'writes the file content as UTF-8' do\n      # read the file directly to preserve the comments\n      utf8 = File.read(cert_path, encoding: 'UTF-8')\n\n      subject.save_pem(utf8, path)\n\n      expect(\n        File.read(path, :encoding => 'UTF-8')\n      ).to match(/\\ANetLock Arany \\(Class Gold\\) Főtanúsítvány/)\n    end\n\n    it 'never changes the owner and group on Windows', if: Puppet::Util::Platform.windows? do\n      expect(FileUtils).not_to receive(:chown)\n\n      subject.save_pem('PEM', path, owner: 'Administrator', group: 'None')\n    end\n\n    it 'changes the owner and group when running as root', unless: Puppet::Util::Platform.windows? do\n      allow(Puppet.features).to receive(:root?).and_return(true)\n      expect(FileUtils).to receive(:chown).with('root', 'root', path)\n\n      subject.save_pem('PEM', path, owner: 'root', group: 'root')\n    end\n\n    it 'does not change owner and group when running not as roo', unless: Puppet::Util::Platform.windows? do\n      allow(Puppet.features).to receive(:root?).and_return(false)\n      expect(FileUtils).not_to receive(:chown)\n\n      subject.save_pem('PEM', path, owner: 'root', group: 'root')\n    end\n\n    it 'allows a mode of 0600 to be specified', unless: Puppet::Util::Platform.windows? do\n      subject.save_pem('PEM', path, mode: 0600)\n\n      expect(File.stat(path).mode & 0777).to eq(0600)\n    end\n\n    it 'defaults the mode to 0644' do\n      subject.save_pem('PEM', path)\n\n      expect(File.stat(path).mode & 0777).to eq(0644)\n    end\n\n    it 'raises EACCES if the file is unwritable' do\n      with_unwritable_file do |path|\n        expect {\n          subject.save_pem('', path)\n        }.to raise_error(Errno::EACCES, /Permission denied/)\n      end\n    end\n\n    it 'raises if the directory does not exist' do\n      dir = tmpdir('pem_store')\n      Dir.unlink(dir)\n\n      expect {\n        subject.save_pem('', File.join(dir, 'something'))\n      }.to raise_error(Errno::ENOENT, /No such file or directory/)\n    end\n  end\n\n  context 'deleting' do\n    it 'returns false if the file does not exist' do\n      expect(subject.delete_pem('/does/not/exist')).to eq(false)\n    end\n\n    it 'returns true if the file exists' do\n      path = tmpfile('pem_store')\n      FileUtils.touch(path)\n\n      expect(subject.delete_pem(path)).to eq(true)\n      expect(File).to_not be_exist(path)\n    end\n\n    it 'raises EACCES if the file is undeletable' do\n      with_unwritable_file do |path|\n        expect {\n          subject.delete_pem(path)\n        }.to raise_error(Errno::EACCES, /Permission denied/)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "util/README_UTIL.md",
    "content": "Development Utilities\n=====================\n\nThe scripts in this directory are utility scripts useful during development.\n\nbinary_search_specs.rb\n----------------------\n\nThis script, written by Nick Lewis, is useful if you encounter a spec failure which only occurs when run in some sequence with other specs.  If you have a spec which passes by itself, but fails when run with the full spec suite, this script will help track it down.\n\nThe puppet spec/spec_helper.rb checks for an environment variable LOG_SPEC_ORDER.  If this is present, it will save the current order of the spec files to './spec_order.txt'.\n\nThis file is then used by binary_search_specs.rb so that:\n\n    $ ./util/binary_search_specs.rb spec/unit/foo_spec.rb\n\nwill begin bisecting runs before and after this spec until it narrows down to a candidate which seems to be effecting foo_spec.rb and causing it to fail.\n\n### with parallel-spec\n\nTo get the groups that the parallel task is running, run: be util/rspec_grouper 1000. Then run each spit out file with \"be util/rspec_runner <groupfile>\". If it fails, rename it to spec_order.txt and run the binary script.\n\n### Curl\n\nFor simple cases of testing REST API via curl:\n\n* edit ~/tests/master/:confdir/auth.conf and add `\"allow *\"` to `\"path /\"`\n\nNow you should be able to:\n\n```bash\njpartlow@percival:~/work/puppet$ curl -k -H 'Accept: text/pson' https://puppetmaster:8140/main/resource/user/nobody\n{\"type\":\"User\",\"title\":\"nobody\",\"tags\":[\"user\",\"nobody\"],\"exported\":false,\"parameters\":{\"ensure\":\"present\",\"home\":\"/nonexistent\",\"uid\":65534,\"gid\":65534,\"comment\":\"nobody\",\"shell\":\"/bin/sh\",\"groups\":[],\"expiry\":\"absent\",\"provider\":\"useradd\",\"membership\":\"minimum\",\"role_membership\":\"minimum\",\"auth_membership\":\"minimum\",\"profile_membership\":\"minimum\",\"key_membership\":\"minimum\",\"attribute_membership\":\"minimum\",\"loglevel\":\"notice\"}}\n```\n\nFor more complex authorization cases you will need to reference the agents keys:\n\n```bash\njpartlow@percival:~/work/puppet$ curl -H 'Accept: text/pson' --cert `puppet agent --configprint hostcert` --key `be puppet agent --configprint hostprivkey` --cacert `be puppet agent --configprint localcacert` https://puppetmaster:8140/foo/node/percival.corp.puppetlabs.net\n```\n"
  },
  {
    "path": "util/binary_search_specs.rb",
    "content": "#!/usr/bin/env ruby\n# Author: Nick Lewis\n\nspecs_in_order = File.read('spec_order.txt').split\n\nfailing_spec = ARGV.first\n\nspecs = specs_in_order[0...specs_in_order.index(failing_spec)]\n\nsuspects = specs\n\nwhile suspects.length > 1 do\n  count = suspects.length\n  specs_to_run = suspects[0...(count/2)]\n  puts \"Trying #{specs_to_run.join(' ')}\"\n  start = Time.now\n  system(\"bundle exec rspec #{specs_to_run.join(' ')} #{failing_spec}\")\n  puts \"Finished in #{Time.now - start} seconds\"\n  if $? == 0\n    puts \"This group is innocent. The culprit is in the other half.\"\n    suspects = suspects[(count/2)..-1]\n  else\n    puts \"One of these is guilty.\"\n    suspects = specs_to_run\n  end\nend\n\nputs \"The culprit is #{suspects.first}\"\n"
  },
  {
    "path": "util/rspec_grouper",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire 'rubygems'\nrequire 'rspec'\n\n# Disable ruby verbosity\n# We need control over output so that the parallel task can parse it correctly\n$VERBOSE = nil\n\nmodule Parallel\n  module RSpec\n    #\n    # Responsible for grouping rspec examples into groups of a given size.\n    #\n    class Grouper\n      attr_reader :groups\n      attr_reader :files\n      attr_reader :total_examples\n\n      def initialize(group_size)\n        config = ::RSpec::Core::Configuration.new\n        options = ::RSpec::Core::ConfigurationOptions.new((ENV.fetch('TEST', nil) || ENV.fetch('TESTS', nil) || 'spec').split(';'))\n        options.configure config\n\n        # This will scan and load all spec examples\n        config.load_spec_files\n\n        @total_examples = 0\n\n        # Populate a map of spec file => example count, sorted ascending by count\n        # NOTE: this uses a private API of RSpec and is may break if the gem is updated\n        @files = ::RSpec.world.example_groups.each_with_object({}) do |group, files|\n          file = group.metadata[:example_group_block].source_location[0]\n          count = count_examples(group)\n          files[file] = (files[file] || 0) + count\n          @total_examples += count\n        end.sort_by { |_, v| v }\n\n        # Group the spec files\n        @groups = []\n        group = nil\n        example_count = 0\n        @files.each do |file, count|\n          group ||= []\n          group << file\n          next unless (example_count += count) > group_size\n\n          example_count = 0\n          @groups << group\n          group = nil\n        end\n        @groups << group if group\n      end\n\n      private\n\n      def count_examples(group)\n        return 0 unless group\n\n        # Each group can have examples as well as child groups, so recursively traverse\n        group.children.inject(group.examples.count) { |count, g| count + count_examples(g) }\n      end\n    end\n  end\nend\n\ndef print_usage\n  puts 'usage: rspec_grouper <group_size>'\nend\n\nif __FILE__ == $PROGRAM_NAME\n  if ARGV.length != 1\n    print_usage\n  else\n    group_size = ARGV[0].to_i\n    abort 'error: group count must be greater than zero.' if group_size < 1\n    grouper = Parallel::RSpec::Grouper.new(group_size)\n    abort 'error: no rspec examples were found.' if grouper.total_examples == 0\n    groups = grouper.groups\n    puts \"Grouped #{grouper.total_examples} rspec example(s) into #{groups.length} group(s) from #{grouper.files.count} file(s).\"\n    puts\n\n    paths = []\n\n    begin\n      # Create a temp directory and write out group files\n      tmpdir = Dir.mktmpdir\n      groups.each_with_index do |group, index|\n        path = File.join(tmpdir, \"group#{index + 1}\")\n        file = File.new(path, 'w')\n        paths << path\n        file.puts group\n        file.close\n        puts path\n      end\n    rescue Exception\n      # Delete all files on an exception\n      paths.each do |path|\n        File.delete path\n      rescue Exception\n      end\n      raise\n    end\n  end\nend\n"
  },
  {
    "path": "util/rspec_runner",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire 'rubygems'\nrequire 'rspec'\nrequire 'rspec/core/formatters/progress_formatter'\nrequire 'rspec/core/runner'\n\n# Disable ruby verbosity\n# We need control over output so that the parallel task can parse it correctly\n$VERBOSE = nil\n\nmodule Parallel\n  module RSpec\n    #\n    # Responsible for formatting output.\n    # This differs from the built-in progress formatter by not appending an index to failures.\n    #\n    class Formatter < ::RSpec::Core::Formatters::ProgressFormatter\n      ::RSpec::Core::Formatters.register self, :dump_failure\n      def dump_failure(example, _)\n        # Unlike the super class implementation, do not print the failure number\n        output.puts \"#{short_padding}#{example.full_description}\"\n        dump_failure_info(example)\n      end\n    end\n\n    #\n    # Responsible for running spec files given a spec file.\n    # Can supply an optional list of additional options (used when running in CI).\n    # We do it this way so that we can run very long spec file lists on Windows, since\n    # Windows has a limited argument length depending on method of invocation.\n    #\n    class Runner\n      def initialize(specs_file, options = [])\n        abort \"error: spec list file '#{specs_file}' does not exist.\" unless File.exist? specs_file\n        if options.empty?\n          @options = ['-fParallel::RSpec::Formatter']\n        else\n          @options = options\n        end\n        File.readlines(specs_file).each { |line| @options << line.chomp }\n      end\n\n      def run\n        @options = ::RSpec::Core::ConfigurationOptions.new(@options)\n        ::RSpec::Core::Runner.new(@options).run($stderr, $stdout)\n      end\n    end\n  end\nend\n\ndef print_usage\n  puts 'usage: rspec_runner <spec_list_file> [<rspec_options_string>]'\nend\n\nif __FILE__ == $PROGRAM_NAME\n  if ARGV.length < 1\n    print_usage\n  elsif ARGV.length == 1\n    exit Parallel::RSpec::Runner.new(ARGV[0]).run\n  else\n    spec_file, *options = ARGV\n    exit Parallel::RSpec::Runner.new(spec_file, options).run\n  end\nend\n"
  },
  {
    "path": "yardoc/templates/default/method_details/html/method_signature.erb",
    "content": "<h3 class=\"signature <%= 'first' if @index == 0 %>\" id=\"<%= anchor_for(object) %>\">\n  <% if object.tags(:overload).size == 1 %>\n    <%= signature(object.tag(:overload), false) + format_method_detail_extras(object) %>\n  <% elsif object.tags(:overload).size > 1 %>\n    <% object.tags(:overload).each do |overload| %>\n      <span class=\"overload\"><%= signature(overload, false) + format_method_detail_extras(object) %></span>\n    <% end %>\n  <% else %>\n    <%= signature(object, false) + format_method_detail_extras(object)%>\n  <% end %>\n\n  <% if object.aliases.size > 0 %>\n    <span class=\"aliases\">Also known as:\n    <span class=\"names\"><%= object.aliases.map {|o|\n      \"<span id='#{anchor_for(o)}'>\" + h(o.name.to_s) + \"</span>\" }.join(\", \") %></span>\n    </span>\n  <% end %>\n\n  <% if owner != object.namespace %>\n    <span class=\"not_defined_here\">\n      Originally defined in <%= object.namespace.type %>\n        <%= linkify object, owner.relative_path(object.namespace) %>\n    </span>\n  <% end %>\n</h3>"
  },
  {
    "path": "yardoc/templates/default/method_details/html/setup.rb",
    "content": "def init\n  super\nend\n\ndef format_method_detail_extras(object)\n  result = \"\"\n  if object\n    if object.respond_to?(:visibility)\n      if object.visibility != :public\n        result << \"<span class=\\\"note title #{object.visibility}\\\">#{object.visibility}</span>\"\n      end\n    end\n      if object.has_tag?(:abstract)\n        result << '<span class=\"abstract note title\">abstract</span>'\n      end\n      if object.has_tag?(:deprecated)\n        result << '<span class=\"deprecated note title\">deprecated</span>'\n      end\n    if object.respond_to?(:visibility)\n      if object.has_tag?(:api) && object.tag(:api).text == 'private' && object.visibility != :private\n        result << '<span class=\"private note title\">private</span>'\n      end\n    else  \n      if object.has_tag?(:api) && object.tag(:api).text == 'private'\n        result << '<span class=\"private note title\">private</span>'\n      end\n    end\n    if object.has_tag?(:dsl)\n      result << '<span class=\"note title readonly\">DSL</span>'\n    end\n  end\n  # separate the extras with one space\n  if result != \"\"\n    result = \"&nbsp;\" + result\n  end\n  result\nend\n"
  },
  {
    "path": "yardoc/templates/default/module/html/item_summary.erb",
    "content": "<% \n# This is a copy of the original erb file with the same name. It adds \"DSL\" marker.\n# It also removes the double output of \"private\" when being both api private and private\n%>\n<li class=\"<%= @item.visibility %> <%= @item.has_tag?(:deprecated) ? 'deprecated' : '' %>\">\n  <span class=\"summary_signature\">\n    <% if @item.tags(:overload).size == 1 %>\n      <%= signature(@item.tag(:overload), true, !@item.attr_info) %>\n    <% else %>\n      <%= signature(@item, true, false, !@item.attr_info) %>\n    <% end %>\n\n    <% if @item.aliases.size > 0 %>\n      (also: <%= @item.aliases.map {|o| h(o.name(true)) }.join(\", \") %>)\n    <% end %>\n  </span>\n  <% if object != @item.namespace %>\n    <span class=\"note title not_defined_here\">\n      <%= @item.namespace.type == :class ? 'inherited' : (@item.scope == :class ? 'extended' : 'included') %>\n      from <%= linkify @item, object.relative_path(@item.namespace) %>\n    </span>\n  <% end %>\n  <% if @item.constructor? %>\n    <span class=\"note title constructor\">constructor</span>\n  <% end %>\n  <% if @item.has_tag?(:dsl) %>\n    <span class=\"note title readonly\">DSL</span>\n  <% end %>\n  <% if rw = @item.attr_info %>\n    <% if !run_verifier([rw[:read]].compact).empty? && run_verifier([rw[:write]].compact).empty? %>\n      <span class=\"note title readonly\">readonly</span>\n    <% end %>\n    <% if !run_verifier([rw[:write]].compact).empty? && run_verifier([rw[:read]].compact).empty? %>\n      <span class=\"note title writeonly\">writeonly</span>\n    <% end %>\n  <% end %>\n  <% if @item.visibility != :public %><span class=\"note title <%= @item.visibility %>\"><%= @item.visibility %></span><% end %>\n  <% if @item.has_tag?(:abstract) %><span class=\"abstract note title\">abstract</span><% end %>\n  <% if @item.has_tag?(:deprecated) %><span class=\"deprecated note title\">deprecated</span><% end %>\n  <% if @item.has_tag?(:api) && @item.tag(:api).text == 'private'&& @item.visibility != :private  %><span class=\"private note title\">private</span><% end %>\n\n  <% if @item.has_tag?(:deprecated) %>\n    <span class=\"summary_desc\"><strong>Deprecated.</strong> <%= htmlify_line @item.tag(:deprecated).text %></span>\n  <% else %>\n    <span class=\"summary_desc\"><%= htmlify_line docstring_summary(@item) %></span>\n  <% end %>\n</li>\n"
  }
]